Merge pull request #108422 from stuartcarnie/108115_metal_cpu_read_bit

Metal: Fix `texture_get_data` other linear formats
This commit is contained in:
Thaddeus Crews 2025-10-15 16:31:02 -05:00
commit 71031e4adb
No known key found for this signature in database
GPG key ID: 8C6E5FEB5FC03CCC
9 changed files with 253 additions and 191 deletions

View file

@ -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<uint8_t> 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<uint8_t> 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<uint8_t>());
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

View file

@ -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<uint8_t> 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<TextureUsageBits> texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) override final;

View file

@ -113,8 +113,7 @@ public:
private:
// Returns true if the texture is a valid linear format.
Result<bool> 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<uint8_t> 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<TextureUsageBits> texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) override final;

View file

@ -187,25 +187,14 @@ static const MTLTextureType TEXTURE_TYPE[RD::TEXTURE_TYPE_MAX] = {
MTLTextureTypeCubeArray,
};
RenderingDeviceDriverMetal::Result<bool> 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<bool> is_linear_or_err = is_valid_linear(p_format);
ERR_FAIL_COND_V(std::holds_alternative<Error>(is_linear_or_err), TextureID());
is_linear = std::get<bool>(is_linear_or_err);
}
id<MTLTexture> obj = nil;
if (is_linear) {
// Linear textures are restricted to 2D textures, a single mipmap level and a single array layer.
@ -525,49 +513,15 @@ 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<MTLTexture> 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<MTLTexture> 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);
MTLSize sz = MTLSizeMake(obj.width, obj.height, obj.depth);
if (p_subresource.mipmap > 0) {
r_layout->offset = get_image_format_required_size(format, sz.width, sz.height, sz.depth, p_subresource.mipmap);
}
@ -588,51 +542,78 @@ void RenderingDeviceDriverMetal::texture_get_copyable_layout(TextureID p_texture
array_length *= 6;
}
r_layout->layer_pitch = r_layout->size / array_length;
} else {
CRASH_NOW_MSG("need to calculate layout for shared texture");
}
Vector<uint8_t> RenderingDeviceDriverMetal::texture_get_data(TextureID p_texture, uint32_t p_layer) {
id<MTLTexture> obj = rid::get(p_texture);
ERR_FAIL_COND_V_MSG(obj.storageMode != MTLStorageModeShared, Vector<uint8_t>(), "Texture must be created with TEXTURE_USAGE_CPU_READ_BIT set.");
if (obj.buffer) {
ERR_FAIL_COND_V_MSG(p_layer > 0, Vector<uint8_t>(), "A linear texture has a single layer.");
ERR_FAIL_COND_V_MSG(obj.mipmapLevelCount > 1, Vector<uint8_t>(), "A linear texture has a single mipmap level.");
Vector<uint8_t> 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<uint8_t> 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<MTLTexture> 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) {

View file

@ -2236,6 +2236,78 @@ void RenderingDeviceDriverVulkan::texture_get_copyable_layout(TextureID p_textur
}
}
Vector<uint8_t> 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<uint8_t> 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<uint8_t>());
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;

View file

@ -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<uint8_t> 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<TextureUsageBits> texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) override final;

View file

@ -1903,71 +1903,6 @@ uint32_t RenderingDevice::_texture_vrs_method_to_usage_bits() const {
}
}
Vector<uint8_t> 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<uint8_t> 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<uint8_t>());
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<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_layer) {
ERR_RENDER_THREAD_GUARD_V(Vector<uint8_t>());
@ -1984,8 +1919,7 @@ Vector<uint8_t> 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<RDD::TextureCopyableLayout> mip_layouts;
uint32_t work_mip_alignment = driver->api_trait_get(RDD::API_TRAIT_TEXTURE_TRANSFER_ALIGNMENT);

View file

@ -347,7 +347,6 @@ public:
uint32_t texture_upload_region_size_px = 0;
uint32_t texture_download_region_size_px = 0;
Vector<uint8_t> _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<uint8_t> &p_data, RDD::TextureLayout p_dst_layout, bool p_immediate_flush);

View file

@ -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<uint8_t> 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<TextureUsageBits> texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) = 0;