Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add texture partial update method #80164

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions doc/classes/RenderingDevice.xml
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,23 @@
[b]Note:[/b] The existing [param texture] requires the [constant TEXTURE_USAGE_CAN_UPDATE_BIT] to be updatable.
</description>
</method>
<method name="texture_update_partial">
<return type="int" enum="Error" />
<param index="0" name="texture" type="RID" />
<param index="1" name="data" type="PackedByteArray" />
<param index="2" name="data_size" type="Vector2i" />
<param index="3" name="dst_pos" type="Vector2i" />
<param index="4" name="dst_mipmap" type="int" default="0" />
<param index="5" name="layer" type="int" default="0" />
<param index="6" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" />
<description>
Updates the partial of the texture specified by the [param texture] [RID] with the data.
[b]Note:[/b] Compressed textures are not supported.
[b]Note:[/b] Updating textures is forbidden during creation of a draw or compute list.
[b]Note:[/b] The existing [param texture] can't be updated while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to [constant FINAL_ACTION_CONTINUE]) to update this texture.
[b]Note:[/b] The existing [param texture] requires the [constant TEXTURE_USAGE_CAN_UPDATE_BIT] to be updatable.
</description>
</method>
<method name="uniform_buffer_create">
<return type="RID" />
<param index="0" name="size_bytes" type="int" />
Expand Down
12 changes: 12 additions & 0 deletions doc/classes/RenderingServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3252,6 +3252,18 @@
[b]Note:[/b] The [param image] must have the same width, height and format as the current [param texture] data. Otherwise, an error will be printed and the original texture won't be modified. If you need to use different width, height or format, use [method texture_replace] instead.
</description>
</method>
<method name="texture_2d_update_partial">
<return type="void" />
<param index="0" name="texture" type="RID" />
<param index="1" name="data" type="Image" />
<param index="2" name="dst_pos" type="Vector2i" />
<param index="3" name="dst_mipmap" type="int" default="0" />
<param index="4" name="layer" type="int" default="0" />
<description>
Updates the partial of the texture specified by the [param texture] [RID] with the data.
[b]Note:[/b] The [param data] must have the same format as the current [param texture] data and compressed textures are not supported.
</description>
</method>
<method name="texture_3d_create">
<return type="RID" />
<param index="0" name="format" type="int" enum="Image.Format" />
Expand Down
68 changes: 68 additions & 0 deletions drivers/gles3/storage/texture_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,74 @@ void TextureStorage::texture_2d_update(RID p_texture, const Ref<Image> &p_image,
#endif
}

void TextureStorage::texture_2d_update_partial(RID p_texture, const Ref<Image> &p_data, Vector2i p_dst_pos, int p_dst_mipmap, int p_layer) {
ERR_FAIL_COND(p_data.is_null() || p_data->is_empty());

Texture *texture = texture_owner.get_or_null(p_texture);
ERR_FAIL_COND_MSG(p_data->is_compressed() || texture->compressed, "Compressed texture is not supported for partial texture updates. Please use an uncompressed image instead.");
ERR_FAIL_COND(!texture);
ERR_FAIL_COND(!texture->active);
ERR_FAIL_COND(texture->is_render_target);
ERR_FAIL_COND(p_dst_pos.x < 0 || p_dst_pos.y < 0);
ERR_FAIL_COND(p_data->get_format() != texture->format);
ERR_FAIL_COND(p_dst_mipmap < 0 || p_dst_mipmap >= texture->mipmaps);

uint32_t dst_mip_width = texture->width >> p_dst_mipmap;
uint32_t dst_mip_height = texture->height >> p_dst_mipmap;
ERR_FAIL_COND_MSG((uint32_t)p_dst_pos.x >= dst_mip_width || (uint32_t)p_dst_pos.y >= dst_mip_height, "dst_pos is out of bounds for this mipmap");

GLenum type;
GLenum format;
GLenum internal_format;
bool compressed = false;

uint32_t data_width = p_data->get_width();
uint32_t data_height = p_data->get_height();
ERR_FAIL_COND_MSG((uint32_t)p_dst_pos.x + data_width > dst_mip_width || (uint32_t)p_dst_pos.y + data_height > dst_mip_height, "The size of the data is out of bounds for this mipmap");

Image::Format real_format;
Ref<Image> img = _get_gl_image_and_format(p_data, p_data->get_format(), real_format, format, internal_format, type, compressed, false);

GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_layer] : texture->target;

Vector<uint8_t> read = img->get_data();

glActiveTexture(GL_TEXTURE0);
glBindTexture(texture->target, texture->tex_id);

#ifndef WEB_ENABLED
switch (texture->format) {
#ifdef GLES_OVER_GL
case Image::FORMAT_L8: {
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED);
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_RED);
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_RED);
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ONE);
} break;
case Image::FORMAT_LA8: {
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED);
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_RED);
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_RED);
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_GREEN);
} break;
#endif // GLES3_OVER_GL
default: {
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED);
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_GREEN);
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_BLUE);
glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ALPHA);
} break;
}
#endif // WEB_ENABLED

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
if (texture->target == GL_TEXTURE_2D_ARRAY) {
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, p_dst_mipmap, p_dst_pos.x, p_dst_pos.y, p_layer, data_width, data_height, 1, format, type, &read[0]);
} else {
glTexSubImage2D(blit_target, p_dst_mipmap, p_dst_pos.x, p_dst_pos.y, data_width, data_height, format, type, &read[0]);
}
}

void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) {
Texture *tex = texture_owner.get_or_null(p_texture);
ERR_FAIL_COND(!tex);
Expand Down
3 changes: 3 additions & 0 deletions drivers/gles3/storage/texture_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,7 @@ class TextureStorage : public RendererTextureStorage {
RID texture_create_external(Texture::Type p_type, Image::Format p_format, unsigned int p_image, int p_width, int p_height, int p_depth, int p_layers, RS::TextureLayeredType p_layered_type = RS::TEXTURE_LAYERED_2D_ARRAY);

virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override;
virtual void texture_2d_update_partial(RID p_texture, const Ref<Image> &p_data, Vector2i p_dst, int p_dst_mipmap = 0, int p_layer = 0) override;
virtual void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) override{};
virtual void texture_proxy_update(RID p_proxy, RID p_base) override;

Expand Down Expand Up @@ -550,7 +551,9 @@ class TextureStorage : public RendererTextureStorage {
virtual uint64_t texture_get_native_handle(RID p_texture, bool p_srgb = false) const override;

void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer = 0);

virtual Image::Format texture_get_format(RID p_texture) const override;

uint32_t texture_get_texid(RID p_texture) const;
uint32_t texture_get_width(RID p_texture) const;
uint32_t texture_get_height(RID p_texture) const;
Expand Down
167 changes: 167 additions & 0 deletions drivers/vulkan/rendering_device_vulkan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2445,6 +2445,9 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p
Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier) {
return _texture_update(p_texture, p_layer, p_data, p_post_barrier, false);
}
Error RenderingDeviceVulkan::texture_update_partial(RID p_texture, const Vector<uint8_t> &p_data, Vector2i p_data_size, Vector2i p_dst, uint32_t p_dst_mipmap, uint32_t p_layer, BitField<BarrierMask> p_post_barrier) {
return _texture_update_partial(p_texture, p_data, p_data_size, p_dst, p_dst_mipmap, p_layer, p_post_barrier, false);
}

static _ALWAYS_INLINE_ void _copy_region(uint8_t const *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_x, uint32_t p_src_y, uint32_t p_src_w, uint32_t p_src_h, uint32_t p_src_full_w, uint32_t p_unit_size) {
uint32_t src_offset = (p_src_y * p_src_full_w + p_src_x) * p_unit_size;
Expand Down Expand Up @@ -2686,6 +2689,170 @@ Error RenderingDeviceVulkan::_texture_update(RID p_texture, uint32_t p_layer, co
return OK;
}

Error RenderingDeviceVulkan::_texture_update_partial(RID p_texture, const Vector<uint8_t> &p_data, Vector2i p_data_size, Vector2i p_dst_pos, uint32_t p_dst_mipmap, uint32_t p_layer, BitField<BarrierMask> p_post_barrier, bool p_use_setup_queue) {
_THREAD_SAFE_METHOD_

ERR_FAIL_COND_V_MSG((draw_list || compute_list), ERR_INVALID_PARAMETER,
"Updating textures is forbidden during creation of a draw or compute list");

Texture *texture = texture_owner.get_or_null(p_texture);
ERR_FAIL_COND_V(!texture, ERR_INVALID_PARAMETER);

ERR_FAIL_COND_V_MSG(texture->bound, ERR_CANT_ACQUIRE_RESOURCE,
"Texture can't be updated while a render pass that uses it is being created. Ensure render pass is finalized (and that it was created with RENDER_PASS_CONTENTS_FINISH) to unbind this texture.");

ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_CAN_UPDATE_BIT), ERR_INVALID_PARAMETER,
"Texture requires the TEXTURE_USAGE_CAN_UPDATE_BIT in order to be updatable.");

uint32_t layer_count = texture->layers;
if (texture->type == TEXTURE_TYPE_CUBE || texture->type == TEXTURE_TYPE_CUBE_ARRAY) {
layer_count *= 6;
}
ERR_FAIL_COND_V(p_layer >= layer_count, ERR_INVALID_PARAMETER);

uint32_t region_size = texture_upload_region_size_px;
uint32_t pixel_size = get_image_format_pixel_size(texture->format);

const uint8_t *r = p_data.ptr();

VkCommandBuffer command_buffer = p_use_setup_queue ? frames[frame].setup_command_buffer : frames[frame].draw_command_buffer;

// Barrier to transfer.
{
VkImageMemoryBarrier image_memory_barrier;
image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
image_memory_barrier.pNext = nullptr;
image_memory_barrier.srcAccessMask = 0;
image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
image_memory_barrier.oldLayout = texture->layout;
image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;

image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_memory_barrier.image = texture->image;
image_memory_barrier.subresourceRange.aspectMask = texture->barrier_aspect_mask;
image_memory_barrier.subresourceRange.baseMipLevel = p_dst_mipmap;
image_memory_barrier.subresourceRange.levelCount = 1;
image_memory_barrier.subresourceRange.baseArrayLayer = p_layer;
image_memory_barrier.subresourceRange.layerCount = 1;

vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
}

uint32_t data_width = p_data_size.x;
uint32_t data_height = p_data_size.y;

uint32_t width;
uint32_t height;
uint32_t depth;
get_image_format_required_size(texture->format, texture->width, texture->height, texture->depth, p_dst_mipmap + 1, &width, &height, &depth);

ERR_FAIL_COND_V_MSG((uint32_t)(p_dst_pos.x + data_width) > width || (uint32_t)(p_dst_pos.y + data_height) > height, ERR_INVALID_PARAMETER, "Parameters are out of bound for this mipmap.");

for (uint32_t z = 0; z < depth; z++) { // For 3D textures, depth may be > 1.

const uint8_t *read_ptr = r + (p_data.size() / depth) * z;

for (uint32_t y = 0; y < data_height; y += region_size) {
for (uint32_t x = 0; x < data_width; x += region_size) {
uint32_t region_w = MIN(region_size, data_width - x);
uint32_t region_h = MIN(region_size, data_height - y);

uint32_t to_allocate = region_w * region_h * pixel_size;

uint32_t alloc_offset, alloc_size;
Error err = _staging_buffer_allocate(to_allocate, pixel_size, alloc_offset, alloc_size, false);
ERR_FAIL_COND_V(err, ERR_CANT_CREATE);

uint8_t *write_ptr;

{ // Map.
void *data_ptr = nullptr;
VkResult vkerr = vmaMapMemory(allocator, staging_buffer_blocks[staging_buffer_current].allocation, &data_ptr);
ERR_FAIL_COND_V_MSG(vkerr, ERR_CANT_CREATE, "vmaMapMemory failed with error " + itos(vkerr) + ".");
write_ptr = (uint8_t *)data_ptr;
write_ptr += alloc_offset;
}

_copy_region(read_ptr, write_ptr, x, y, region_w, region_h, data_width, pixel_size);

{ // Unmap.
vmaUnmapMemory(allocator, staging_buffer_blocks[staging_buffer_current].allocation);
}

VkBufferImageCopy buffer_image_copy;
buffer_image_copy.bufferOffset = alloc_offset;
buffer_image_copy.bufferRowLength = 0; // Tightly packed.
buffer_image_copy.bufferImageHeight = 0; // Tightly packed.

buffer_image_copy.imageSubresource.aspectMask = texture->read_aspect_mask;
buffer_image_copy.imageSubresource.mipLevel = p_dst_mipmap;
buffer_image_copy.imageSubresource.baseArrayLayer = p_layer;
buffer_image_copy.imageSubresource.layerCount = 1;

buffer_image_copy.imageOffset.x = p_dst_pos.x + x;
buffer_image_copy.imageOffset.y = p_dst_pos.y + y;
buffer_image_copy.imageOffset.z = z;

buffer_image_copy.imageExtent.width = region_w;
buffer_image_copy.imageExtent.height = region_h;
buffer_image_copy.imageExtent.depth = 1;

vkCmdCopyBufferToImage(command_buffer, staging_buffer_blocks[staging_buffer_current].buffer, texture->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &buffer_image_copy);

staging_buffer_blocks.write[staging_buffer_current].fill_amount = alloc_offset + alloc_size;
}
}
}
// Barrier to restore layout.
{
uint32_t barrier_flags = 0;
uint32_t access_flags = 0;
if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) {
barrier_flags |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
}
if (p_post_barrier.has_flag(BARRIER_MASK_RASTER)) {
barrier_flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
access_flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
}
if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) {
barrier_flags |= VK_PIPELINE_STAGE_TRANSFER_BIT;
access_flags |= VK_ACCESS_TRANSFER_WRITE_BIT;
}

if (barrier_flags == 0) {
barrier_flags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
}

VkImageMemoryBarrier image_memory_barrier;
image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
image_memory_barrier.pNext = nullptr;
image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
image_memory_barrier.dstAccessMask = access_flags;
image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
image_memory_barrier.newLayout = texture->layout;
image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_memory_barrier.image = texture->image;
image_memory_barrier.subresourceRange.aspectMask = texture->barrier_aspect_mask;
image_memory_barrier.subresourceRange.baseMipLevel = p_dst_mipmap;
image_memory_barrier.subresourceRange.levelCount = 1;
image_memory_barrier.subresourceRange.baseArrayLayer = p_layer;
image_memory_barrier.subresourceRange.layerCount = 1;

vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, barrier_flags, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
}

if (texture->used_in_frame != frames_drawn) {
texture->used_in_raster = false;
texture->used_in_compute = false;
texture->used_in_frame = frames_drawn;
}
texture->used_in_transfer = true;

return OK;
}
Vector<uint8_t> RenderingDeviceVulkan::_texture_get_data_from_image(Texture *tex, VkImage p_image, VmaAllocation p_allocation, uint32_t p_layer, bool p_2d) {
uint32_t width, height, depth;
uint32_t image_size = get_image_format_required_size(tex->format, tex->width, tex->height, p_2d ? 1 : tex->depth, tex->mipmaps, &width, &height, &depth);
Expand Down
3 changes: 2 additions & 1 deletion drivers/vulkan/rendering_device_vulkan.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ class RenderingDeviceVulkan : public RenderingDevice {

Vector<uint8_t> _texture_get_data_from_image(Texture *tex, VkImage p_image, VmaAllocation p_allocation, uint32_t p_layer, bool p_2d = false);
Error _texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier, bool p_use_setup_queue);

Error _texture_update_partial(RID p_texture, const Vector<uint8_t> &p_data, Vector2i p_size, Vector2i p_dst, uint32_t p_mipmap, uint32_t p_layer, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS, bool p_use_setup_queue = false);
/*****************/
/**** SAMPLER ****/
/*****************/
Expand Down Expand Up @@ -1077,6 +1077,7 @@ class RenderingDeviceVulkan : public RenderingDevice {

virtual RID texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps = 1, TextureSliceType p_slice_type = TEXTURE_SLICE_2D, uint32_t p_layers = 0);
virtual Error texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS);
virtual Error texture_update_partial(RID p_texture, const Vector<uint8_t> &p_data, Vector2i p_size, Vector2i p_dst, uint32_t p_mipmap = 0, uint32_t p_layer = 0, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS);
virtual Vector<uint8_t> texture_get_data(RID p_texture, uint32_t p_layer);

virtual bool texture_is_format_supported_for_usage(DataFormat p_format, BitField<RenderingDevice::TextureUsageBits> p_usage) const;
Expand Down
1 change: 1 addition & 0 deletions servers/rendering/dummy/storage/texture_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ class TextureStorage : public RendererTextureStorage {
virtual void texture_proxy_initialize(RID p_texture, RID p_base) override{}; //all slices, then all the mipmaps, must be coherent

virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override{};
virtual void texture_2d_update_partial(RID p_texture, const Ref<Image> &p_data, Vector2i p_dst, int p_dst_mipmap = 0, int p_layer = 0) override{};
virtual void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) override{};
virtual void texture_proxy_update(RID p_proxy, RID p_base) override{};

Expand Down
26 changes: 26 additions & 0 deletions servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1131,7 +1131,33 @@ void TextureStorage::_texture_2d_update(RID p_texture, const Ref<Image> &p_image
void TextureStorage::texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer) {
_texture_2d_update(p_texture, p_image, p_layer, false);
}
void TextureStorage::texture_2d_update_partial(RID p_texture, const Ref<Image> &p_data, Vector2i p_dst_pos, int p_dst_mipmap, int p_layer) {
ERR_FAIL_COND(p_data.is_null() || p_data->is_empty());

Texture *texture = texture_owner.get_or_null(p_texture);
ERR_FAIL_COND_MSG(p_data->is_compressed() || texture->format > Image::FORMAT_RGBE9995, "Compressed texture is not supported for partial texture updates. Please use an uncompressed image instead.");
Image::Format src_format = p_data->get_format();
ERR_FAIL_COND(!texture);
ERR_FAIL_COND(texture->is_render_target);
ERR_FAIL_COND(p_dst_pos.x < 0 || p_dst_pos.y < 0);
ERR_FAIL_COND(src_format != texture->format);
ERR_FAIL_COND(p_dst_mipmap < 0 || p_dst_mipmap >= texture->mipmaps);

ERR_FAIL_COND_MSG(p_dst_pos.x >= texture->width >> p_dst_mipmap || p_dst_pos.y >= texture->height >> p_dst_mipmap, "dst_pos is out of bounds for this mipmap.");

if (texture->type == TextureStorage::TYPE_LAYERED) {
ERR_FAIL_INDEX(p_layer, texture->layers);
}

#ifdef TOOLS_ENABLED
texture->image_cache_2d.unref();
#endif

TextureToRDFormat f;
Ref<Image> validated = _validate_texture_format(p_data, f);

RD::get_singleton()->texture_update_partial(texture->rd_texture, validated->get_data(), validated->get_size(), p_dst_pos, p_dst_mipmap, p_layer);
}
void TextureStorage::texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) {
Texture *tex = texture_owner.get_or_null(p_texture);
ERR_FAIL_COND(!tex);
Expand Down
Loading