From a281e91c5a8a4790d087ece1e0666f887c4b9b78 Mon Sep 17 00:00:00 2001 From: Stuart Carnie Date: Wed, 9 Jul 2025 07:52:44 +1000 Subject: [PATCH] Metal: Fix `texture_get_data` other linear formats Introduce a specialised `texture_get_data` for `RenderDeviceDriver`, which can retrieve the texture data from the GPU driver for shared textures (`TEXTURE_USAGE_CPU_READ_BIT`). Closes #108115 --- .../d3d12/rendering_device_driver_d3d12.cpp | 72 ++++++ drivers/d3d12/rendering_device_driver_d3d12.h | 1 + drivers/metal/rendering_device_driver_metal.h | 4 +- .../metal/rendering_device_driver_metal.mm | 223 ++++++++---------- .../vulkan/rendering_device_driver_vulkan.cpp | 72 ++++++ .../vulkan/rendering_device_driver_vulkan.h | 1 + servers/rendering/rendering_device.cpp | 68 +----- servers/rendering/rendering_device.h | 1 - servers/rendering/rendering_device_driver.h | 2 + 9 files changed, 253 insertions(+), 191 deletions(-) diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp index de9b8522ad9..3828ec93cd8 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp @@ -1867,6 +1867,78 @@ void RenderingDeviceDriverD3D12::texture_get_copyable_layout(TextureID p_texture r_layout->layer_pitch = subresource_total_size / tex_info->desc.ArraySize(); } +Vector RenderingDeviceDriverD3D12::texture_get_data(TextureID p_texture, uint32_t p_layer) { + const TextureInfo *tex = (const TextureInfo *)p_texture.id; + + DataFormat tex_format = tex->format; + uint32_t tex_width = tex->desc.Width; + uint32_t tex_height = tex->desc.Height; + uint32_t tex_depth = tex->desc.DepthOrArraySize; + uint32_t tex_mipmaps = tex->mipmaps; + + uint32_t width, height, depth; + uint32_t tight_mip_size = get_image_format_required_size(tex_format, tex_width, tex_height, tex_depth, tex_mipmaps, &width, &height, &depth); + + Vector image_data; + image_data.resize(tight_mip_size); + + uint32_t blockw, blockh; + get_compressed_image_format_block_dimensions(tex_format, blockw, blockh); + uint32_t block_size = get_compressed_image_format_block_byte_size(tex_format); + uint32_t pixel_size = get_image_format_pixel_size(tex_format); + + { + uint8_t *w = image_data.ptrw(); + + uint32_t mipmap_offset = 0; + for (uint32_t mm_i = 0; mm_i < tex_mipmaps; mm_i++) { + uint32_t image_total = get_image_format_required_size(tex_format, tex_width, tex_height, tex_depth, mm_i + 1, &width, &height, &depth); + + uint8_t *write_ptr_mipmap = w + mipmap_offset; + tight_mip_size = image_total - mipmap_offset; + + RDD::TextureSubresource subres; + subres.aspect = RDD::TEXTURE_ASPECT_COLOR; + subres.layer = p_layer; + subres.mipmap = mm_i; + RDD::TextureCopyableLayout layout; + texture_get_copyable_layout(p_texture, subres, &layout); + + uint8_t *img_mem = texture_map(p_texture, subres); + ERR_FAIL_NULL_V(img_mem, Vector()); + + for (uint32_t z = 0; z < depth; z++) { + uint8_t *write_ptr = write_ptr_mipmap + z * tight_mip_size / depth; + const uint8_t *slice_read_ptr = img_mem + z * layout.depth_pitch; + + if (block_size > 1) { + // Compressed. + uint32_t line_width = (block_size * (width / blockw)); + for (uint32_t y = 0; y < height / blockh; y++) { + const uint8_t *rptr = slice_read_ptr + y * layout.row_pitch; + uint8_t *wptr = write_ptr + y * line_width; + + memcpy(wptr, rptr, line_width); + } + } else { + // Uncompressed. + for (uint32_t y = 0; y < height; y++) { + const uint8_t *rptr = slice_read_ptr + y * layout.row_pitch; + uint8_t *wptr = write_ptr + y * pixel_size * width; + memcpy(wptr, rptr, (uint64_t)pixel_size * width); + } + } + } + + texture_unmap(p_texture); + + mipmap_offset = image_total; + } + } + + return image_data; +} + uint8_t *RenderingDeviceDriverD3D12::texture_map(TextureID p_texture, const TextureSubresource &p_subresource) { TextureInfo *tex_info = (TextureInfo *)p_texture.id; #ifdef DEBUG_ENABLED diff --git a/drivers/d3d12/rendering_device_driver_d3d12.h b/drivers/d3d12/rendering_device_driver_d3d12.h index f24c1c49abe..5d77ceb1019 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.h +++ b/drivers/d3d12/rendering_device_driver_d3d12.h @@ -384,6 +384,7 @@ public: virtual void texture_free(TextureID p_texture) override final; virtual uint64_t texture_get_allocation_size(TextureID p_texture) override final; virtual void texture_get_copyable_layout(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) override final; + virtual Vector texture_get_data(TextureID p_texture, uint32_t p_layer) override final; virtual uint8_t *texture_map(TextureID p_texture, const TextureSubresource &p_subresource) override final; virtual void texture_unmap(TextureID p_texture) override final; virtual BitField texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) override final; diff --git a/drivers/metal/rendering_device_driver_metal.h b/drivers/metal/rendering_device_driver_metal.h index 43d7fb0f5c9..fcefae591ef 100644 --- a/drivers/metal/rendering_device_driver_metal.h +++ b/drivers/metal/rendering_device_driver_metal.h @@ -113,8 +113,7 @@ public: private: // Returns true if the texture is a valid linear format. - Result is_valid_linear(TextureFormat const &p_format) const; - void _get_sub_resource(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) const; + bool is_valid_linear(TextureFormat const &p_format) const; public: virtual TextureID texture_create(const TextureFormat &p_format, const TextureView &p_view) override final; @@ -124,6 +123,7 @@ public: virtual void texture_free(TextureID p_texture) override final; virtual uint64_t texture_get_allocation_size(TextureID p_texture) override final; virtual void texture_get_copyable_layout(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) override final; + virtual Vector texture_get_data(TextureID p_texture, uint32_t p_layer) override final; virtual uint8_t *texture_map(TextureID p_texture, const TextureSubresource &p_subresource) override final; virtual void texture_unmap(TextureID p_texture) override final; virtual BitField texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) override final; diff --git a/drivers/metal/rendering_device_driver_metal.mm b/drivers/metal/rendering_device_driver_metal.mm index 898785c3147..3502259e579 100644 --- a/drivers/metal/rendering_device_driver_metal.mm +++ b/drivers/metal/rendering_device_driver_metal.mm @@ -187,25 +187,14 @@ static const MTLTextureType TEXTURE_TYPE[RD::TEXTURE_TYPE_MAX] = { MTLTextureTypeCubeArray, }; -RenderingDeviceDriverMetal::Result RenderingDeviceDriverMetal::is_valid_linear(TextureFormat const &p_format) const { - if (!flags::any(p_format.usage_bits, TEXTURE_USAGE_CPU_READ_BIT)) { - return false; - } +bool RenderingDeviceDriverMetal::is_valid_linear(TextureFormat const &p_format) const { + MTLFormatType ft = pixel_formats->getFormatType(p_format.format); - PixelFormats &pf = *pixel_formats; - MTLFormatType ft = pf.getFormatType(p_format.format); - - // Requesting a linear format, which has further restrictions, similar to Vulkan - // when specifying VK_IMAGE_TILING_LINEAR. - - ERR_FAIL_COND_V_MSG(p_format.texture_type != TEXTURE_TYPE_2D, ERR_CANT_CREATE, "Linear (TEXTURE_USAGE_CPU_READ_BIT) textures must be 2D"); - ERR_FAIL_COND_V_MSG(ft != MTLFormatType::DepthStencil, ERR_CANT_CREATE, "Linear (TEXTURE_USAGE_CPU_READ_BIT) textures must not be a depth/stencil format"); - ERR_FAIL_COND_V_MSG(ft != MTLFormatType::Compressed, ERR_CANT_CREATE, "Linear (TEXTURE_USAGE_CPU_READ_BIT) textures must not be a compressed format"); - ERR_FAIL_COND_V_MSG(p_format.mipmaps != 1, ERR_CANT_CREATE, "Linear (TEXTURE_USAGE_CPU_READ_BIT) textures must have 1 mipmap level"); - ERR_FAIL_COND_V_MSG(p_format.array_layers != 1, ERR_CANT_CREATE, "Linear (TEXTURE_USAGE_CPU_READ_BIT) textures must have 1 array layer"); - ERR_FAIL_COND_V_MSG(p_format.samples != TEXTURE_SAMPLES_1, ERR_CANT_CREATE, "Linear (TEXTURE_USAGE_CPU_READ_BIT) textures must have 1 sample"); - - return true; + return p_format.texture_type == TEXTURE_TYPE_2D // Linear textures must be 2D textures. + && ft != MTLFormatType::DepthStencil && ft != MTLFormatType::Compressed // Linear textures must not be depth/stencil or compressed formats.) + && p_format.mipmaps == 1 // Linear textures must have 1 mipmap level. + && p_format.array_layers == 1 // Linear textures must have 1 array layer. + && p_format.samples == TEXTURE_SAMPLES_1; // Linear textures must have 1 sample. } RDD::TextureID RenderingDeviceDriverMetal::texture_create(const TextureFormat &p_format, const TextureView &p_view) { @@ -292,6 +281,7 @@ RDD::TextureID RenderingDeviceDriverMetal::texture_create(const TextureFormat &p // Usage. MTLResourceOptions options = 0; + bool is_linear = false; #if defined(VISIONOS_ENABLED) const bool supports_memoryless = true; #else @@ -304,6 +294,11 @@ RDD::TextureID RenderingDeviceDriverMetal::texture_create(const TextureFormat &p options = MTLResourceCPUCacheModeDefaultCache | MTLResourceHazardTrackingModeTracked; if (p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT) { options |= MTLResourceStorageModeShared; + // The user has indicated they want to read from the texture on the CPU, + // so we'll see if we can use a linear format. + // A linear format is a texture that is backed by a buffer, + // which allows for CPU access to the texture data via a pointer. + is_linear = is_valid_linear(p_format); } else { options |= MTLResourceStorageModePrivate; } @@ -358,13 +353,6 @@ RDD::TextureID RenderingDeviceDriverMetal::texture_create(const TextureFormat &p // Allocate memory. - bool is_linear; - { - Result is_linear_or_err = is_valid_linear(p_format); - ERR_FAIL_COND_V(std::holds_alternative(is_linear_or_err), TextureID()); - is_linear = std::get(is_linear_or_err); - } - id obj = nil; if (is_linear) { // Linear textures are restricted to 2D textures, a single mipmap level and a single array layer. @@ -525,114 +513,107 @@ uint64_t RenderingDeviceDriverMetal::texture_get_allocation_size(TextureID p_tex return obj.allocatedSize; } -void RenderingDeviceDriverMetal::_get_sub_resource(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) const { - id obj = rid::get(p_texture); - - *r_layout = {}; - - PixelFormats &pf = *pixel_formats; - - size_t row_alignment = get_texel_buffer_alignment_for_format(obj.pixelFormat); - size_t offset = 0; - size_t array_layers = obj.arrayLength; - MTLSize size = MTLSizeMake(obj.width, obj.height, obj.depth); - MTLPixelFormat pixel_format = obj.pixelFormat; - - // First skip over the mipmap levels. - for (uint32_t mipLvl = 0; mipLvl < p_subresource.mipmap; mipLvl++) { - MTLSize mip_size = mipmapLevelSizeFromSize(size, mipLvl); - size_t bytes_per_row = pf.getBytesPerRow(pixel_format, mip_size.width); - bytes_per_row = round_up_to_alignment(bytes_per_row, row_alignment); - size_t bytes_per_layer = pf.getBytesPerLayer(pixel_format, bytes_per_row, mip_size.height); - offset += bytes_per_layer * mip_size.depth * array_layers; - } - - // Get current mipmap. - MTLSize mip_size = mipmapLevelSizeFromSize(size, p_subresource.mipmap); - size_t bytes_per_row = pf.getBytesPerRow(pixel_format, mip_size.width); - bytes_per_row = round_up_to_alignment(bytes_per_row, row_alignment); - size_t bytes_per_layer = pf.getBytesPerLayer(pixel_format, bytes_per_row, mip_size.height); - r_layout->size = bytes_per_layer * mip_size.depth; - r_layout->offset = offset + (r_layout->size * p_subresource.layer - 1); - r_layout->depth_pitch = bytes_per_layer; - r_layout->row_pitch = bytes_per_row; - r_layout->layer_pitch = r_layout->size * array_layers; -} - void RenderingDeviceDriverMetal::texture_get_copyable_layout(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) { id obj = rid::get(p_texture); *r_layout = {}; - if ((obj.resourceOptions & MTLResourceStorageModePrivate) != 0) { - MTLSize sz = MTLSizeMake(obj.width, obj.height, obj.depth); + PixelFormats &pf = *pixel_formats; + DataFormat format = pf.getDataFormat(obj.pixelFormat); - PixelFormats &pf = *pixel_formats; - DataFormat format = pf.getDataFormat(obj.pixelFormat); - if (p_subresource.mipmap > 0) { - r_layout->offset = get_image_format_required_size(format, sz.width, sz.height, sz.depth, p_subresource.mipmap); - } + MTLSize sz = MTLSizeMake(obj.width, obj.height, obj.depth); - sz = mipmapLevelSizeFromSize(sz, p_subresource.mipmap); - - uint32_t bw = 0, bh = 0; - get_compressed_image_format_block_dimensions(format, bw, bh); - uint32_t sbw = 0, sbh = 0; - r_layout->size = get_image_format_required_size(format, sz.width, sz.height, sz.depth, 1, &sbw, &sbh); - r_layout->row_pitch = r_layout->size / ((sbh / bh) * sz.depth); - r_layout->depth_pitch = r_layout->size / sz.depth; - - uint32_t array_length = obj.arrayLength; - if (obj.textureType == MTLTextureTypeCube) { - array_length = 6; - } else if (obj.textureType == MTLTextureTypeCubeArray) { - array_length *= 6; - } - r_layout->layer_pitch = r_layout->size / array_length; - } else { - CRASH_NOW_MSG("need to calculate layout for shared texture"); + if (p_subresource.mipmap > 0) { + r_layout->offset = get_image_format_required_size(format, sz.width, sz.height, sz.depth, p_subresource.mipmap); } + + sz = mipmapLevelSizeFromSize(sz, p_subresource.mipmap); + + uint32_t bw = 0, bh = 0; + get_compressed_image_format_block_dimensions(format, bw, bh); + uint32_t sbw = 0, sbh = 0; + r_layout->size = get_image_format_required_size(format, sz.width, sz.height, sz.depth, 1, &sbw, &sbh); + r_layout->row_pitch = r_layout->size / ((sbh / bh) * sz.depth); + r_layout->depth_pitch = r_layout->size / sz.depth; + + uint32_t array_length = obj.arrayLength; + if (obj.textureType == MTLTextureTypeCube) { + array_length = 6; + } else if (obj.textureType == MTLTextureTypeCubeArray) { + array_length *= 6; + } + r_layout->layer_pitch = r_layout->size / array_length; +} + +Vector RenderingDeviceDriverMetal::texture_get_data(TextureID p_texture, uint32_t p_layer) { + id obj = rid::get(p_texture); + ERR_FAIL_COND_V_MSG(obj.storageMode != MTLStorageModeShared, Vector(), "Texture must be created with TEXTURE_USAGE_CPU_READ_BIT set."); + + if (obj.buffer) { + ERR_FAIL_COND_V_MSG(p_layer > 0, Vector(), "A linear texture has a single layer."); + ERR_FAIL_COND_V_MSG(obj.mipmapLevelCount > 1, Vector(), "A linear texture has a single mipmap level."); + Vector image_data; + image_data.resize_uninitialized(obj.buffer.length); + memcpy(image_data.ptrw(), obj.buffer.contents, obj.buffer.length); + return image_data; + } + + DataFormat tex_format = pixel_formats->getDataFormat(obj.pixelFormat); + uint32_t tex_w = obj.width; + uint32_t tex_h = obj.height; + uint32_t tex_d = obj.depth; + uint32_t tex_mipmaps = obj.mipmapLevelCount; + + // Must iteratively copy the texture data to a buffer. + + uint32_t tight_mip_size = get_image_format_required_size(tex_format, tex_w, tex_h, tex_d, tex_mipmaps); + + Vector image_data; + image_data.resize(tight_mip_size); + + uint32_t pixel_size = get_image_format_pixel_size(tex_format); + uint32_t pixel_rshift = get_compressed_image_format_pixel_rshift(tex_format); + uint32_t blockw = 0, blockh = 0; + get_compressed_image_format_block_dimensions(tex_format, blockw, blockh); + + uint8_t *dest_ptr = image_data.ptrw(); + + for (uint32_t mm_i = 0; mm_i < tex_mipmaps; mm_i++) { + uint32_t bw = STEPIFY(tex_w, blockw); + uint32_t bh = STEPIFY(tex_h, blockh); + + uint32_t bytes_per_row = (bw * pixel_size) >> pixel_rshift; + uint32_t bytes_per_img = bytes_per_row * bh; + uint32_t mip_size = bytes_per_img * tex_d; + + [obj getBytes:(void *)dest_ptr + bytesPerRow:bytes_per_row + bytesPerImage:bytes_per_img + fromRegion:MTLRegionMake3D(0, 0, 0, bw, bh, tex_d) + mipmapLevel:mm_i + slice:p_layer]; + + dest_ptr += mip_size; + + // Next mipmap level. + tex_w = MAX(blockw, tex_w >> 1); + tex_h = MAX(blockh, tex_h >> 1); + tex_d = MAX(1u, tex_d >> 1); + } + + // Ensure that the destination pointer is at the end of the image data. + DEV_ASSERT(dest_ptr - image_data.ptr() == image_data.size()); + + return image_data; } uint8_t *RenderingDeviceDriverMetal::texture_map(TextureID p_texture, const TextureSubresource &p_subresource) { id obj = rid::get(p_texture); - ERR_FAIL_NULL_V_MSG(obj.buffer, nullptr, "texture is not created from a buffer"); + ERR_FAIL_COND_V_MSG(obj.storageMode != MTLStorageModeShared, nullptr, "Texture must be created with TEXTURE_USAGE_CPU_READ_BIT set."); + ERR_FAIL_COND_V_MSG(obj.buffer, nullptr, "Texture mapping is not supported for non-linear textures in Metal."); + ERR_FAIL_COND_V_MSG(p_subresource.layer > 0, nullptr, "A linear texture should have a single layer."); + ERR_FAIL_COND_V_MSG(p_subresource.mipmap > 0, nullptr, "A linear texture should have a single mipmap."); - TextureCopyableLayout layout; - _get_sub_resource(p_texture, p_subresource, &layout); - return (uint8_t *)(obj.buffer.contents) + layout.offset; - PixelFormats &pf = *pixel_formats; - - size_t row_alignment = get_texel_buffer_alignment_for_format(obj.pixelFormat); - size_t offset = 0; - size_t array_layers = obj.arrayLength; - MTLSize size = MTLSizeMake(obj.width, obj.height, obj.depth); - MTLPixelFormat pixel_format = obj.pixelFormat; - - // First skip over the mipmap levels. - for (uint32_t mipLvl = 0; mipLvl < p_subresource.mipmap; mipLvl++) { - MTLSize mipExtent = mipmapLevelSizeFromSize(size, mipLvl); - size_t bytes_per_row = pf.getBytesPerRow(pixel_format, mipExtent.width); - bytes_per_row = round_up_to_alignment(bytes_per_row, row_alignment); - size_t bytes_per_layer = pf.getBytesPerLayer(pixel_format, bytes_per_row, mipExtent.height); - offset += bytes_per_layer * mipExtent.depth * array_layers; - } - - if (p_subresource.layer > 1) { - // Calculate offset to desired layer. - MTLSize mipExtent = mipmapLevelSizeFromSize(size, p_subresource.mipmap); - size_t bytes_per_row = pf.getBytesPerRow(pixel_format, mipExtent.width); - bytes_per_row = round_up_to_alignment(bytes_per_row, row_alignment); - size_t bytes_per_layer = pf.getBytesPerLayer(pixel_format, bytes_per_row, mipExtent.height); - offset += bytes_per_layer * mipExtent.depth * (p_subresource.layer - 1); - } - - // TODO: Confirm with rendering team that there is no other way Godot may attempt to map a texture with multiple mipmaps or array layers. - - // NOTE: It is not possible to create a buffer-backed texture with mipmaps or array layers, - // as noted in the is_valid_linear function, so the offset calculation SHOULD always be zero. - // Given that, this code should be simplified. - - return (uint8_t *)(obj.buffer.contents) + offset; + return (uint8_t *)obj.buffer.contents; } void RenderingDeviceDriverMetal::texture_unmap(TextureID p_texture) { diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp index cc1c9f41976..90291bd9447 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -2236,6 +2236,78 @@ void RenderingDeviceDriverVulkan::texture_get_copyable_layout(TextureID p_textur } } +Vector RenderingDeviceDriverVulkan::texture_get_data(TextureID p_texture, uint32_t p_layer) { + const TextureInfo *tex = (const TextureInfo *)p_texture.id; + + DataFormat tex_format = tex->rd_format; + uint32_t tex_width = tex->vk_create_info.extent.width; + uint32_t tex_height = tex->vk_create_info.extent.height; + uint32_t tex_depth = tex->vk_create_info.extent.depth; + uint32_t tex_mipmaps = tex->vk_create_info.mipLevels; + + uint32_t width, height, depth; + uint32_t tight_mip_size = get_image_format_required_size(tex_format, tex_width, tex_height, tex_depth, tex_mipmaps, &width, &height, &depth); + + Vector image_data; + image_data.resize(tight_mip_size); + + uint32_t blockw, blockh; + get_compressed_image_format_block_dimensions(tex_format, blockw, blockh); + uint32_t block_size = get_compressed_image_format_block_byte_size(tex_format); + uint32_t pixel_size = get_image_format_pixel_size(tex_format); + + { + uint8_t *w = image_data.ptrw(); + + uint32_t mipmap_offset = 0; + for (uint32_t mm_i = 0; mm_i < tex_mipmaps; mm_i++) { + uint32_t image_total = get_image_format_required_size(tex_format, tex_width, tex_height, tex_depth, mm_i + 1, &width, &height, &depth); + + uint8_t *write_ptr_mipmap = w + mipmap_offset; + tight_mip_size = image_total - mipmap_offset; + + RDD::TextureSubresource subres; + subres.aspect = RDD::TEXTURE_ASPECT_COLOR; + subres.layer = p_layer; + subres.mipmap = mm_i; + RDD::TextureCopyableLayout layout; + texture_get_copyable_layout(p_texture, subres, &layout); + + uint8_t *img_mem = texture_map(p_texture, subres); + ERR_FAIL_NULL_V(img_mem, Vector()); + + for (uint32_t z = 0; z < depth; z++) { + uint8_t *write_ptr = write_ptr_mipmap + z * tight_mip_size / depth; + const uint8_t *slice_read_ptr = img_mem + z * layout.depth_pitch; + + if (block_size > 1) { + // Compressed. + uint32_t line_width = (block_size * (width / blockw)); + for (uint32_t y = 0; y < height / blockh; y++) { + const uint8_t *rptr = slice_read_ptr + y * layout.row_pitch; + uint8_t *wptr = write_ptr + y * line_width; + + memcpy(wptr, rptr, line_width); + } + } else { + // Uncompressed. + for (uint32_t y = 0; y < height; y++) { + const uint8_t *rptr = slice_read_ptr + y * layout.row_pitch; + uint8_t *wptr = write_ptr + y * pixel_size * width; + memcpy(wptr, rptr, (uint64_t)pixel_size * width); + } + } + } + + texture_unmap(p_texture); + + mipmap_offset = image_total; + } + } + + return image_data; +} + uint8_t *RenderingDeviceDriverVulkan::texture_map(TextureID p_texture, const TextureSubresource &p_subresource) { const TextureInfo *tex_info = (const TextureInfo *)p_texture.id; diff --git a/drivers/vulkan/rendering_device_driver_vulkan.h b/drivers/vulkan/rendering_device_driver_vulkan.h index 873eb446a17..ff3fe7963fe 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.h +++ b/drivers/vulkan/rendering_device_driver_vulkan.h @@ -234,6 +234,7 @@ public: virtual void texture_free(TextureID p_texture) override final; virtual uint64_t texture_get_allocation_size(TextureID p_texture) override final; virtual void texture_get_copyable_layout(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) override final; + virtual Vector texture_get_data(TextureID p_texture, uint32_t p_layer) override final; virtual uint8_t *texture_map(TextureID p_texture, const TextureSubresource &p_subresource) override final; virtual void texture_unmap(TextureID p_texture) override final; virtual BitField texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) override final; diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 2e4fafff0a2..e4605a2d4e9 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -1903,71 +1903,6 @@ uint32_t RenderingDevice::_texture_vrs_method_to_usage_bits() const { } } -Vector RenderingDevice::_texture_get_data(Texture *tex, uint32_t p_layer, bool p_2d) { - uint32_t width, height, depth; - uint32_t tight_mip_size = get_image_format_required_size(tex->format, tex->width, tex->height, p_2d ? 1 : tex->depth, tex->mipmaps, &width, &height, &depth); - - Vector image_data; - image_data.resize(tight_mip_size); - - uint32_t blockw, blockh; - get_compressed_image_format_block_dimensions(tex->format, blockw, blockh); - uint32_t block_size = get_compressed_image_format_block_byte_size(tex->format); - uint32_t pixel_size = get_image_format_pixel_size(tex->format); - - { - uint8_t *w = image_data.ptrw(); - - uint32_t mipmap_offset = 0; - for (uint32_t mm_i = 0; mm_i < tex->mipmaps; mm_i++) { - uint32_t image_total = get_image_format_required_size(tex->format, tex->width, tex->height, p_2d ? 1 : tex->depth, mm_i + 1, &width, &height, &depth); - - uint8_t *write_ptr_mipmap = w + mipmap_offset; - tight_mip_size = image_total - mipmap_offset; - - RDD::TextureSubresource subres; - subres.aspect = RDD::TEXTURE_ASPECT_COLOR; - subres.layer = p_layer; - subres.mipmap = mm_i; - RDD::TextureCopyableLayout layout; - driver->texture_get_copyable_layout(tex->driver_id, subres, &layout); - - uint8_t *img_mem = driver->texture_map(tex->driver_id, subres); - ERR_FAIL_NULL_V(img_mem, Vector()); - - for (uint32_t z = 0; z < depth; z++) { - uint8_t *write_ptr = write_ptr_mipmap + z * tight_mip_size / depth; - const uint8_t *slice_read_ptr = img_mem + z * layout.depth_pitch; - - if (block_size > 1) { - // Compressed. - uint32_t line_width = (block_size * (width / blockw)); - for (uint32_t y = 0; y < height / blockh; y++) { - const uint8_t *rptr = slice_read_ptr + y * layout.row_pitch; - uint8_t *wptr = write_ptr + y * line_width; - - memcpy(wptr, rptr, line_width); - } - - } else { - // Uncompressed. - for (uint32_t y = 0; y < height; y++) { - const uint8_t *rptr = slice_read_ptr + y * layout.row_pitch; - uint8_t *wptr = write_ptr + y * pixel_size * width; - memcpy(wptr, rptr, (uint64_t)pixel_size * width); - } - } - } - - driver->texture_unmap(tex->driver_id); - - mipmap_offset = image_total; - } - } - - return image_data; -} - Vector RenderingDevice::texture_get_data(RID p_texture, uint32_t p_layer) { ERR_RENDER_THREAD_GUARD_V(Vector()); @@ -1984,8 +1919,7 @@ Vector RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye _check_transfer_worker_texture(tex); if (tex->usage_flags & TEXTURE_USAGE_CPU_READ_BIT) { - // Does not need anything fancy, map and read. - return _texture_get_data(tex, p_layer); + return driver->texture_get_data(tex->driver_id, p_layer); } else { LocalVector mip_layouts; uint32_t work_mip_alignment = driver->api_trait_get(RDD::API_TRAIT_TEXTURE_TRANSFER_ALIGNMENT); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 5cf76b2e758..21b5ee2a5ad 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -347,7 +347,6 @@ public: uint32_t texture_upload_region_size_px = 0; uint32_t texture_download_region_size_px = 0; - Vector _texture_get_data(Texture *tex, uint32_t p_layer, bool p_2d = false); uint32_t _texture_layer_count(Texture *p_texture) const; uint32_t _texture_alignment(Texture *p_texture) const; Error _texture_initialize(RID p_texture, uint32_t p_layer, const Vector &p_data, RDD::TextureLayout p_dst_layout, bool p_immediate_flush); diff --git a/servers/rendering/rendering_device_driver.h b/servers/rendering/rendering_device_driver.h index 54898b80145..2d208a5c8fd 100644 --- a/servers/rendering/rendering_device_driver.h +++ b/servers/rendering/rendering_device_driver.h @@ -271,6 +271,8 @@ public: virtual void texture_free(TextureID p_texture) = 0; virtual uint64_t texture_get_allocation_size(TextureID p_texture) = 0; virtual void texture_get_copyable_layout(TextureID p_texture, const TextureSubresource &p_subresource, TextureCopyableLayout *r_layout) = 0; + // Returns the data of a texture layer for a CPU texture that was created with TEXTURE_USAGE_CPU_READ_BIT. + virtual Vector texture_get_data(TextureID p_texture, uint32_t p_layer) = 0; virtual uint8_t *texture_map(TextureID p_texture, const TextureSubresource &p_subresource) = 0; virtual void texture_unmap(TextureID p_texture) = 0; virtual BitField texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) = 0;