Vulkan: Texture Unpacking #107360

Merged
Jeroen Bakker merged 2 commits from Jeroen-Bakker/blender:vulkan-data-conversion-with-row-length into main 2023-04-26 09:24:03 +02:00
7 changed files with 110 additions and 11 deletions

View File

@ -126,6 +126,17 @@ void VKContext::finish()
void VKContext::memory_statistics_get(int * /*total_mem*/, int * /*free_mem*/) {}
/* -------------------------------------------------------------------- */
/** \name State manager
* \{ */
const VKStateManager &VKContext::state_manager_get() const
{
return *static_cast<const VKStateManager *>(state_manager);
}
/** \} */
void VKContext::activate_framebuffer(VKFrameBuffer &framebuffer)
{
if (has_active_framebuffer()) {

View File

@ -15,8 +15,9 @@
namespace blender::gpu {
class VKFrameBuffer;
class VKStateManager;
class VKContext : public Context {
class VKContext : public Context, NonCopyable {
private:
/** Copies of the handles owned by the GHOST context. */
VkInstance vk_instance_ = VK_NULL_HANDLE;
@ -108,6 +109,8 @@ class VKContext : public Context {
return descriptor_pools_;
}
const VKStateManager &state_manager_get() const;
VmaAllocator mem_allocator_get() const
{
return mem_allocator_;

View File

@ -764,6 +764,28 @@ void convert_host_to_device(void *dst_buffer,
convert_buffer(dst_buffer, src_buffer, buffer_size, device_format, conversion_type);
}
void convert_host_to_device(void *dst_buffer,
const void *src_buffer,
uint2 src_size,
uint src_row_length,
eGPUDataFormat host_format,
eGPUTextureFormat device_format)
{
const uint8_t *src = static_cast<const uint8_t *>(src_buffer);
uint8_t *dst = static_cast<uint8_t *>(dst_buffer);
ConversionType conversion_type = host_to_device(host_format, device_format);
size_t src_row_len = src_row_length * to_bytesize(device_format, host_format);
size_t dst_row_len = src_size.x * to_bytesize(device_format);
for (uint row : IndexRange(src_size.y)) {
convert_buffer(&dst[dst_row_len * row],
&src[src_row_len * row],
src_size.x,
device_format,
conversion_type);
}
}
void convert_device_to_host(void *dst_buffer,
const void *src_buffer,
size_t buffer_size,

View File

@ -7,6 +7,8 @@
#pragma once
#include "BLI_math_vector_types.hh"
#include "gpu_texture_private.hh"
namespace blender::gpu {
@ -17,7 +19,7 @@ namespace blender::gpu {
* \param dst_buffer: device buffer.
* \param src_buffer: host buffer.
* \param buffer_size: number of pixels to convert from the start of the given buffer.
* \param host_format: format of the host buffer
* \param host_format: format of the host buffer.
* \param device_format: format of the device buffer.
*
* \note Will assert when the host_format/device_format combination isn't valid
@ -30,6 +32,27 @@ void convert_host_to_device(void *dst_buffer,
eGPUDataFormat host_format,
eGPUTextureFormat device_format);
/**
* Convert host buffer to device buffer with row length.
*
* \param dst_buffer: device buffer.
* \param src_buffer: host buffer.
* \param src_size: size of the host buffer.
* \param src_row_length: Length of a single row of the buffer (in pixels).
* \param host_format: format of the host buffer.
* \param device_format: format of the device buffer.
*
* \note Will assert when the host_format/device_format combination isn't valid
* (#validate_data_format) or supported. Some combinations aren't supported in Vulkan due to
* platform incompatibility.
*/
void convert_host_to_device(void *dst_buffer,
const void *src_buffer,
uint2 src_size,
uint src_row_length,
eGPUDataFormat host_format,
eGPUTextureFormat device_format);
/**
* Convert device buffer to host buffer.
*

View File

@ -38,6 +38,14 @@ void VKStateManager::image_unbind(Texture * /*tex*/) {}
void VKStateManager::image_unbind_all() {}
void VKStateManager::texture_unpack_row_length_set(uint /*len*/) {}
void VKStateManager::texture_unpack_row_length_set(uint len)
{
texture_unpack_row_length_ = len;
}
uint VKStateManager::texture_unpack_row_length_get() const
{
return texture_unpack_row_length_;
}
} // namespace blender::gpu

View File

@ -11,6 +11,8 @@
namespace blender::gpu {
class VKStateManager : public StateManager {
uint texture_unpack_row_length_;
public:
void apply_state() override;
void force_state() override;
@ -26,5 +28,12 @@ class VKStateManager : public StateManager {
void image_unbind_all() override;
void texture_unpack_row_length_set(uint len) override;
/**
* Row length for unpacking host data when uploading texture data.
*
* When set to zero (0) host data can be assumed to be stored sequential.
*/
uint texture_unpack_row_length_get() const;
};
} // namespace blender::gpu

View File

@ -13,6 +13,7 @@
#include "vk_memory.hh"
#include "vk_shader.hh"
#include "vk_shader_interface.hh"
#include "vk_state_manager.hh"
#include "BLI_math_vector.hh"
@ -94,8 +95,12 @@ void *VKTexture::read(int mip, eGPUDataFormat format)
}
void VKTexture::update_sub(
int mip, int offset[3], int extent[3], eGPUDataFormat format, const void *data)
int mip, int offset[3], int extent_[3], eGPUDataFormat format, const void *data)
{
if (mip != 0) {
/* TODO: not implemented yet. */
return;
}
if (!is_allocated()) {
allocate();
}
@ -103,17 +108,31 @@ void VKTexture::update_sub(
/* Vulkan images cannot be directly mapped to host memory and requires a staging buffer. */
VKContext &context = *VKContext::get();
VKBuffer staging_buffer;
size_t sample_len = extent[0] * extent[1] * extent[2];
int3 extent = int3(extent_[0], max_ii(extent_[1], 1), max_ii(extent_[2], 1));
size_t sample_len = extent.x * extent.y * extent.z;
size_t device_memory_size = sample_len * to_bytesize(format_);
staging_buffer.create(
context, device_memory_size, GPU_USAGE_DEVICE_ONLY, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
convert_host_to_device(staging_buffer.mapped_memory_get(), data, sample_len, format, format_);
uint buffer_row_length = context.state_manager_get().texture_unpack_row_length_get();
if (buffer_row_length) {
/* Use custom row length #GPU_texture_unpack_row_length */
convert_host_to_device(staging_buffer.mapped_memory_get(),
data,
uint2(extent),
buffer_row_length,
format,
format_);
}
else {
convert_host_to_device(staging_buffer.mapped_memory_get(), data, sample_len, format, format_);
}
VkBufferImageCopy region = {};
region.imageExtent.width = extent[0];
region.imageExtent.height = extent[1];
region.imageExtent.depth = extent[2];
region.imageExtent.width = extent.x;
region.imageExtent.height = extent.y;
region.imageExtent.depth = extent.z;
region.imageOffset.x = offset[0];
region.imageOffset.y = offset[1];
region.imageOffset.z = offset[2];
@ -175,7 +194,8 @@ bool VKTexture::is_allocated() const
static VkImageUsageFlagBits to_vk_image_usage(const eGPUTextureUsage usage,
const eGPUTextureFormatFlag format_flag)
{
VkImageUsageFlagBits result = static_cast<VkImageUsageFlagBits>(VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VkImageUsageFlagBits result = static_cast<VkImageUsageFlagBits>(VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT);
if (usage & GPU_TEXTURE_USAGE_SHADER_READ) {
result = static_cast<VkImageUsageFlagBits>(result | VK_IMAGE_USAGE_STORAGE_BIT);
@ -184,7 +204,7 @@ static VkImageUsageFlagBits to_vk_image_usage(const eGPUTextureUsage usage,
result = static_cast<VkImageUsageFlagBits>(result | VK_IMAGE_USAGE_STORAGE_BIT);
}
if (usage & GPU_TEXTURE_USAGE_ATTACHMENT) {
if (format_flag & (GPU_FORMAT_NORMALIZED_INTEGER | GPU_FORMAT_COMPRESSED)) {
if (format_flag & GPU_FORMAT_COMPRESSED) {
/* These formats aren't supported as an attachment. When using GPU_TEXTURE_USAGE_DEFAULT they
* are still being evaluated to be attachable. So we need to skip them. */
}
@ -207,6 +227,7 @@ static VkImageUsageFlagBits to_vk_image_usage(const eGPUTextureUsage usage,
bool VKTexture::allocate()
{
BLI_assert(vk_image_ == VK_NULL_HANDLE);
BLI_assert(!is_allocated());
int extent[3] = {1, 1, 1};
@ -260,6 +281,7 @@ bool VKTexture::allocate()
if (result != VK_SUCCESS) {
return false;
}
debug::object_label(&context, vk_image_, name_);
/* Promote image to the correct layout. */
layout_ensure(context, VK_IMAGE_LAYOUT_GENERAL);
@ -277,6 +299,7 @@ bool VKTexture::allocate()
result = vkCreateImageView(
context.device_get(), &image_view_info, vk_allocation_callbacks, &vk_image_view_);
debug::object_label(&context, vk_image_view_, name_);
return result == VK_SUCCESS;
}