mirror of
https://github.com/godotengine/godot.git
synced 2025-10-19 07:53:26 +00:00
Merge pull request #108422 from stuartcarnie/108115_metal_cpu_read_bit
Metal: Fix `texture_get_data` other linear formats
This commit is contained in:
commit
71031e4adb
9 changed files with 253 additions and 191 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,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<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);
|
||||
|
||||
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<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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue