Skip to content

Commit

Permalink
Add texture partial update method
Browse files Browse the repository at this point in the history
  • Loading branch information
Redwarx008 committed Aug 22, 2023
1 parent dca12c2 commit 6864e88
Show file tree
Hide file tree
Showing 15 changed files with 303 additions and 1 deletion.
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

0 comments on commit 6864e88

Please sign in to comment.