From 6864e88db6a1926cee98e737c9cd69092ccebb60 Mon Sep 17 00:00:00 2001 From: Redwarx008 Date: Tue, 11 Apr 2023 15:24:59 +0800 Subject: [PATCH] Add texture partial update method --- doc/classes/RenderingDevice.xml | 17 ++ doc/classes/RenderingServer.xml | 12 ++ drivers/gles3/storage/texture_storage.cpp | 68 +++++++ drivers/gles3/storage/texture_storage.h | 3 + drivers/vulkan/rendering_device_vulkan.cpp | 167 ++++++++++++++++++ drivers/vulkan/rendering_device_vulkan.h | 3 +- .../rendering/dummy/storage/texture_storage.h | 1 + .../storage_rd/texture_storage.cpp | 26 +++ .../renderer_rd/storage_rd/texture_storage.h | 1 + servers/rendering/rendering_device.cpp | 1 + servers/rendering/rendering_device.h | 1 + servers/rendering/rendering_server_default.h | 1 + servers/rendering/storage/texture_storage.h | 1 + servers/rendering_server.cpp | 1 + servers/rendering_server.h | 1 + 15 files changed, 303 insertions(+), 1 deletion(-) diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index e4e194adf0c5..b20e654fcd9d 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -825,6 +825,23 @@ [b]Note:[/b] The existing [param texture] requires the [constant TEXTURE_USAGE_CAN_UPDATE_BIT] to be updatable. + + + + + + + + + + + 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. + + diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 02c1286392cd..3bf78a120850 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -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. + + + + + + + + + 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. + + diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index a3f230f9e2aa..a9bbe91e4639 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -868,6 +868,74 @@ void TextureStorage::texture_2d_update(RID p_texture, const Ref &p_image, #endif } +void TextureStorage::texture_2d_update_partial(RID p_texture, const Ref &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 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 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); diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index bad2b31a3151..e11beddb5f54 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -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 &p_image, int p_layer = 0) override; + virtual void texture_2d_update_partial(RID p_texture, const Ref &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> &p_data) override{}; virtual void texture_proxy_update(RID p_proxy, RID p_base) override; @@ -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 &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; diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index d521f675fbc8..c4ab0a62eac3 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -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 &p_data, BitField 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 &p_data, Vector2i p_data_size, Vector2i p_dst, uint32_t p_dst_mipmap, uint32_t p_layer, BitField 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; @@ -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 &p_data, Vector2i p_data_size, Vector2i p_dst_pos, uint32_t p_dst_mipmap, uint32_t p_layer, BitField 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 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); diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index fd832312acb5..8a06b4df6b9c 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -164,7 +164,7 @@ class RenderingDeviceVulkan : public RenderingDevice { Vector _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 &p_data, BitField p_post_barrier, bool p_use_setup_queue); - + Error _texture_update_partial(RID p_texture, const Vector &p_data, Vector2i p_size, Vector2i p_dst, uint32_t p_mipmap, uint32_t p_layer, BitField p_post_barrier = BARRIER_MASK_ALL_BARRIERS, bool p_use_setup_queue = false); /*****************/ /**** SAMPLER ****/ /*****************/ @@ -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 &p_data, BitField p_post_barrier = BARRIER_MASK_ALL_BARRIERS); + virtual Error texture_update_partial(RID p_texture, const Vector &p_data, Vector2i p_size, Vector2i p_dst, uint32_t p_mipmap = 0, uint32_t p_layer = 0, BitField p_post_barrier = BARRIER_MASK_ALL_BARRIERS); virtual Vector texture_get_data(RID p_texture, uint32_t p_layer); virtual bool texture_is_format_supported_for_usage(DataFormat p_format, BitField p_usage) const; diff --git a/servers/rendering/dummy/storage/texture_storage.h b/servers/rendering/dummy/storage/texture_storage.h index 71a1801de968..7de74c1ce61f 100644 --- a/servers/rendering/dummy/storage/texture_storage.h +++ b/servers/rendering/dummy/storage/texture_storage.h @@ -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 &p_image, int p_layer = 0) override{}; + virtual void texture_2d_update_partial(RID p_texture, const Ref &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> &p_data) override{}; virtual void texture_proxy_update(RID p_proxy, RID p_base) override{}; diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index d84f6e685069..6a701d5d853b 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -1131,7 +1131,33 @@ void TextureStorage::_texture_2d_update(RID p_texture, const Ref &p_image void TextureStorage::texture_2d_update(RID p_texture, const Ref &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 &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 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> &p_data) { Texture *tex = texture_owner.get_or_null(p_texture); ERR_FAIL_COND(!tex); diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h index 6df6faa40abe..19bad10f5a42 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h @@ -491,6 +491,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 &p_image, int p_layer = 0) override; + virtual void texture_2d_update_partial(RID p_texture, const Ref &p_data, Vector2i p_dst_pos, int p_dst_mipmap = 0, int p_layer = 0) override; virtual void texture_3d_update(RID p_texture, const Vector> &p_data) override; virtual void texture_proxy_update(RID p_proxy, RID p_base) override; diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 1b0a3e9d0fcb..0c5b63c93294 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -717,6 +717,7 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("texture_create_shared_from_slice", "view", "with_texture", "layer", "mipmap", "mipmaps", "slice_type"), &RenderingDevice::_texture_create_shared_from_slice, DEFVAL(1), DEFVAL(TEXTURE_SLICE_2D)); ClassDB::bind_method(D_METHOD("texture_update", "texture", "layer", "data", "post_barrier"), &RenderingDevice::texture_update, DEFVAL(BARRIER_MASK_ALL_BARRIERS)); + ClassDB::bind_method(D_METHOD("texture_update_partial", "texture", "data", "data_size", "dst_pos", "dst_mipmap", "layer", "post_barrier"), &RenderingDevice::texture_update_partial, DEFVAL(0), DEFVAL(0), DEFVAL(BARRIER_MASK_ALL_BARRIERS)); ClassDB::bind_method(D_METHOD("texture_get_data", "texture", "layer"), &RenderingDevice::texture_get_data); ClassDB::bind_method(D_METHOD("texture_is_format_supported_for_usage", "format", "usage_flags"), &RenderingDevice::texture_is_format_supported_for_usage); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 58da3df57786..760434cbd60e 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -536,6 +536,7 @@ class RenderingDevice : public Object { 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) = 0; virtual Error texture_update(RID p_texture, uint32_t p_layer, const Vector &p_data, BitField p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0; + virtual Error texture_update_partial(RID p_texture, const Vector &p_data, Vector2i p_data_size, Vector2i p_dst_pos, uint32_t p_dst_mipmap = 0, uint32_t p_layer = 0, BitField p_post_barrier = BARRIER_MASK_ALL_BARRIERS) = 0; virtual Vector texture_get_data(RID p_texture, uint32_t p_layer) = 0; // CPU textures will return immediately, while GPU textures will most likely force a flush virtual bool texture_is_format_supported_for_usage(DataFormat p_format, BitField p_usage) const = 0; diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index e9b40eb08215..4f69d0ca1948 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -186,6 +186,7 @@ class RenderingServerDefault : public RenderingServer { //these go through command queue if they are in another thread FUNC3(texture_2d_update, RID, const Ref &, int) + FUNC5(texture_2d_update_partial, RID, const Ref &, Vector2i, int, int) FUNC2(texture_3d_update, RID, const Vector> &) FUNC2(texture_proxy_update, RID, RID) diff --git a/servers/rendering/storage/texture_storage.h b/servers/rendering/storage/texture_storage.h index c3a257595c2d..b41d649fdfef 100644 --- a/servers/rendering/storage/texture_storage.h +++ b/servers/rendering/storage/texture_storage.h @@ -72,6 +72,7 @@ class RendererTextureStorage { virtual void texture_proxy_initialize(RID p_texture, RID p_base) = 0; //all slices, then all the mipmaps, must be coherent virtual void texture_2d_update(RID p_texture, const Ref &p_image, int p_layer = 0) = 0; + virtual void texture_2d_update_partial(RID p_texture, const Ref &p_data, Vector2i p_dst_pos, int p_dst_mipmap = 0, int p_layer = 0) = 0; virtual void texture_3d_update(RID p_texture, const Vector> &p_data) = 0; virtual void texture_proxy_update(RID p_proxy, RID p_base) = 0; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 48b38cf2b78e..931b55c23c64 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1680,6 +1680,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("texture_proxy_create", "base"), &RenderingServer::texture_proxy_create); ClassDB::bind_method(D_METHOD("texture_2d_update", "texture", "image", "layer"), &RenderingServer::texture_2d_update); + ClassDB::bind_method(D_METHOD("texture_2d_update_partial", "texture", "data", "dst_pos", "dst_mipmap", "layer"), &RenderingServer::texture_2d_update_partial, DEFVAL(0), DEFVAL(0)); ClassDB::bind_method(D_METHOD("texture_3d_update", "texture", "data"), &RenderingServer::_texture_3d_update); ClassDB::bind_method(D_METHOD("texture_proxy_update", "texture", "proxy_to"), &RenderingServer::texture_proxy_update); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 618ceb3091ff..c179a80bb9a1 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -109,6 +109,7 @@ class RenderingServer : public Object { virtual RID texture_proxy_create(RID p_base) = 0; virtual void texture_2d_update(RID p_texture, const Ref &p_image, int p_layer = 0) = 0; + virtual void texture_2d_update_partial(RID p_texture, const Ref &p_data, Vector2i p_dst_pos, int p_dst_mipmap = 0, int p_layer = 0) = 0; virtual void texture_3d_update(RID p_texture, const Vector> &p_data) = 0; virtual void texture_proxy_update(RID p_texture, RID p_proxy_to) = 0;