Vulkan: Texture Data Conversions #105762

Merged
Jeroen Bakker merged 23 commits from Jeroen-Bakker/blender:vulkan-textures into main 2023-03-24 08:09:33 +01:00
3 changed files with 142 additions and 102 deletions
Showing only changes of commit 12a6b1914d - Show all commits

View File

@ -8,6 +8,48 @@
#include "vk_data_conversion.hh"
namespace blender::gpu {
/* -------------------------------------------------------------------- */
/** \name Conversion types
* \{ */
enum class ConversionType {
/** No conversion needed, result can be directly read back to host memory. */
PASS_THROUGH,
FLOAT_TO_UNORM8,
UNORM8_TO_FLOAT,
FLOAT_TO_SNORM8,
SNORM8_TO_FLOAT,
FLOAT_TO_UNORM16,
UNORM16_TO_FLOAT,
FLOAT_TO_SNORM16,
SNORM16_TO_FLOAT,
UI32_TO_UI16,
UI16_TO_UI32,
UI32_TO_UI8,
UI8_TO_UI32,
I32_TO_I16,
I16_TO_I32,
I32_TO_I8,
I8_TO_I32,
/** Convert device 16F to floats. */
HALF_TO_FLOAT,
FLOAT_TO_HALF,
/**
* The requested conversion isn't supported.
*/
UNSUPPORTED,
};
static ConversionType type_of_conversion_float(eGPUTextureFormat device_format)
{

Perhaps we can still use texture data format to find out which conversion should be used to remove these large switch statements.

Perhaps we can still use texture data format to find out which conversion should be used to remove these large switch statements.
switch (device_format) {
@ -401,8 +443,7 @@ static ConversionType type_of_conversion_ubyte(eGPUTextureFormat device_format)
return ConversionType::UNSUPPORTED;
}
ConversionType conversion_type_for_update(eGPUDataFormat host_format,
eGPUTextureFormat device_format)
static ConversionType host_to_device(eGPUDataFormat host_format, eGPUTextureFormat device_format)
{
BLI_assert(validate_data_format(device_format, host_format));
@ -427,7 +468,7 @@ ConversionType conversion_type_for_update(eGPUDataFormat host_format,
return ConversionType::UNSUPPORTED;
}
static ConversionType invert(ConversionType type)
static ConversionType reversed(ConversionType type)
{
#define CASE_SINGLE(a, b) \
case ConversionType::a##_TO_##b: \
@ -461,11 +502,7 @@ static ConversionType invert(ConversionType type)
return ConversionType::UNSUPPORTED;
}
ConversionType conversion_type_for_read(eGPUDataFormat host_format,
eGPUTextureFormat device_format)
{
return invert(conversion_type_for_update(host_format, device_format));
}
/* \} */
/* Copy the contents of src to dst with out performing any actual conversion. */
template<typename DestinationType, typename SourceType>
@ -481,9 +518,9 @@ template<typename DestinationType, typename SourceType>
void copy_unchecked(void *dst_memory,
const void *src_memory,
eGPUTextureFormat device_format,
size_t sample_len)
size_t buffer_size)
{
size_t total_components = to_component_len(device_format) * sample_len;
size_t total_components = to_component_len(device_format) * buffer_size;
Span<SourceType> src = Span<SourceType>(static_cast<const SourceType *>(src_memory),
total_components);
MutableSpan<DestinationType> dst = MutableSpan<DestinationType>(
@ -539,9 +576,9 @@ template<typename DestinationType, typename SourceType>
void float_to_unorm(void *dst_memory,
const void *src_memory,
eGPUTextureFormat device_format,
size_t sample_len)
size_t buffer_size)
{
size_t total_components = to_component_len(device_format) * sample_len;
size_t total_components = to_component_len(device_format) * buffer_size;
Span<SourceType> src = Span<SourceType>(static_cast<const SourceType *>(src_memory),
total_components);
MutableSpan<DestinationType> dst = MutableSpan<DestinationType>(
@ -562,9 +599,9 @@ template<typename DestinationType, typename SourceType>
void unorm_to_float(void *dst_memory,
const void *src_memory,
eGPUTextureFormat device_format,
size_t sample_len)
size_t buffer_size)
{
size_t total_components = to_component_len(device_format) * sample_len;
size_t total_components = to_component_len(device_format) * buffer_size;
Span<SourceType> src = Span<SourceType>(static_cast<const SourceType *>(src_memory),
total_components);
MutableSpan<DestinationType> dst = MutableSpan<DestinationType>(
@ -607,9 +644,9 @@ template<typename DestinationType, typename SourceType>
void float_to_snorm(void *dst_memory,
const void *src_memory,
eGPUTextureFormat device_format,
size_t sample_len)
size_t buffer_size)
{
size_t total_components = to_component_len(device_format) * sample_len;
size_t total_components = to_component_len(device_format) * buffer_size;
Span<SourceType> src = Span<SourceType>(static_cast<const SourceType *>(src_memory),
total_components);
MutableSpan<DestinationType> dst = MutableSpan<DestinationType>(
@ -632,9 +669,9 @@ template<typename DestinationType, typename SourceType>
void snorm_to_float(void *dst_memory,
const void *src_memory,
eGPUTextureFormat device_format,
size_t sample_len)
size_t buffer_size)
{
size_t total_components = to_component_len(device_format) * sample_len;
size_t total_components = to_component_len(device_format) * buffer_size;
Span<SourceType> src = Span<SourceType>(static_cast<const SourceType *>(src_memory),
total_components);
MutableSpan<DestinationType> dst = MutableSpan<DestinationType>(
@ -642,78 +679,78 @@ void snorm_to_float(void *dst_memory,
snorm_to_float<DestinationType, SourceType>(dst, src);
}
void convert(ConversionType type,
eGPUTextureFormat device_format,
size_t sample_len,
void *dst_memory,
const void *src_memory)
static void convert_buffer(void *dst_memory,
const void *src_memory,
size_t buffer_size,
eGPUTextureFormat device_format,
ConversionType type)
{
switch (type) {
case ConversionType::UNSUPPORTED:
return;
case ConversionType::PASS_THROUGH:
memcpy(dst_memory, src_memory, sample_len * to_bytesize(device_format));
memcpy(dst_memory, src_memory, buffer_size * to_bytesize(device_format));
return;
case ConversionType::UI32_TO_UI16:
copy_unchecked<uint16_t, uint32_t>(dst_memory, src_memory, device_format, sample_len);
copy_unchecked<uint16_t, uint32_t>(dst_memory, src_memory, device_format, buffer_size);
break;
case ConversionType::UI16_TO_UI32:
copy_unchecked<uint32_t, uint16_t>(dst_memory, src_memory, device_format, sample_len);
copy_unchecked<uint32_t, uint16_t>(dst_memory, src_memory, device_format, buffer_size);
break;
case ConversionType::UI32_TO_UI8:
copy_unchecked<uint8_t, uint32_t>(dst_memory, src_memory, device_format, sample_len);
copy_unchecked<uint8_t, uint32_t>(dst_memory, src_memory, device_format, buffer_size);
break;
case ConversionType::UI8_TO_UI32:
copy_unchecked<uint32_t, uint8_t>(dst_memory, src_memory, device_format, sample_len);
copy_unchecked<uint32_t, uint8_t>(dst_memory, src_memory, device_format, buffer_size);
break;
case ConversionType::I32_TO_I16:
copy_unchecked<int16_t, int32_t>(dst_memory, src_memory, device_format, sample_len);
copy_unchecked<int16_t, int32_t>(dst_memory, src_memory, device_format, buffer_size);
break;
case ConversionType::I16_TO_I32:
copy_unchecked<int32_t, int16_t>(dst_memory, src_memory, device_format, sample_len);
copy_unchecked<int32_t, int16_t>(dst_memory, src_memory, device_format, buffer_size);
break;
case ConversionType::I32_TO_I8:
copy_unchecked<int8_t, int32_t>(dst_memory, src_memory, device_format, sample_len);
copy_unchecked<int8_t, int32_t>(dst_memory, src_memory, device_format, buffer_size);
break;
case ConversionType::I8_TO_I32:
copy_unchecked<int32_t, int8_t>(dst_memory, src_memory, device_format, sample_len);
copy_unchecked<int32_t, int8_t>(dst_memory, src_memory, device_format, buffer_size);
break;
case ConversionType::FLOAT_TO_UNORM8:
float_to_unorm<uint8_t, float>(dst_memory, src_memory, device_format, sample_len);
float_to_unorm<uint8_t, float>(dst_memory, src_memory, device_format, buffer_size);
break;
case ConversionType::UNORM8_TO_FLOAT:
unorm_to_float<float, uint8_t>(dst_memory, src_memory, device_format, sample_len);
unorm_to_float<float, uint8_t>(dst_memory, src_memory, device_format, buffer_size);
break;
case ConversionType::FLOAT_TO_SNORM8:
float_to_snorm<uint8_t, float>(dst_memory, src_memory, device_format, sample_len);
float_to_snorm<uint8_t, float>(dst_memory, src_memory, device_format, buffer_size);
break;
case ConversionType::SNORM8_TO_FLOAT:
snorm_to_float<float, uint8_t>(dst_memory, src_memory, device_format, sample_len);
snorm_to_float<float, uint8_t>(dst_memory, src_memory, device_format, buffer_size);
break;
case ConversionType::FLOAT_TO_UNORM16:
float_to_unorm<uint16_t, float>(dst_memory, src_memory, device_format, sample_len);
float_to_unorm<uint16_t, float>(dst_memory, src_memory, device_format, buffer_size);
break;
case ConversionType::UNORM16_TO_FLOAT:
unorm_to_float<float, uint16_t>(dst_memory, src_memory, device_format, sample_len);
unorm_to_float<float, uint16_t>(dst_memory, src_memory, device_format, buffer_size);
break;
case ConversionType::FLOAT_TO_SNORM16:
float_to_snorm<uint16_t, float>(dst_memory, src_memory, device_format, sample_len);
float_to_snorm<uint16_t, float>(dst_memory, src_memory, device_format, buffer_size);
break;
case ConversionType::SNORM16_TO_FLOAT:
snorm_to_float<float, uint16_t>(dst_memory, src_memory, device_format, sample_len);
snorm_to_float<float, uint16_t>(dst_memory, src_memory, device_format, buffer_size);
break;
case ConversionType::FLOAT_TO_HALF:
@ -723,4 +760,32 @@ void convert(ConversionType type,
}
}
/* -------------------------------------------------------------------- */
/** \name API
* \{ */
void convert_host_to_device(void *dst_buffer,
const void *src_buffer,
size_t buffer_size,
eGPUDataFormat host_format,
eGPUTextureFormat device_format)
{
ConversionType conversion_type = host_to_device(host_format, device_format);
BLI_assert(conversion_type != ConversionType::UNSUPPORTED);
convert_buffer(dst_buffer, src_buffer, buffer_size, device_format, conversion_type);
}
void convert_device_to_host(void *dst_buffer,
const void *src_buffer,
size_t buffer_size,
eGPUDataFormat host_format,
eGPUTextureFormat device_format)
{
ConversionType conversion_type = reversed(host_to_device(host_format, device_format));
BLI_assert(conversion_type != ConversionType::UNSUPPORTED);
convert_buffer(dst_buffer, src_buffer, buffer_size, device_format, conversion_type);
}
/* \} */
} // namespace blender::gpu

View File

@ -12,57 +12,42 @@
namespace blender::gpu {
enum class ConversionType {
/** No conversion needed, result can be directly read back to host memory. */
PASS_THROUGH,
FLOAT_TO_UNORM8,
UNORM8_TO_FLOAT,
FLOAT_TO_SNORM8,
SNORM8_TO_FLOAT,
FLOAT_TO_UNORM16,
UNORM16_TO_FLOAT,
FLOAT_TO_SNORM16,
SNORM16_TO_FLOAT,
UI32_TO_UI16,
UI16_TO_UI32,
UI32_TO_UI8,
UI8_TO_UI32,
I32_TO_I16,
I16_TO_I32,
I32_TO_I8,
I8_TO_I32,
/** Convert device 16F to floats. */
HALF_TO_FLOAT,
FLOAT_TO_HALF,
/**
* The requested conversion isn't supported.
*/
UNSUPPORTED,
};
/**
* Convert host buffer to device buffer.
*
* \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 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,
size_t buffer_size,
eGPUDataFormat host_format,
eGPUTextureFormat device_format);
/**
* Determine the type of conversion that is needed to read back data from GPU device to host
* memory.
* Convert device buffer to host buffer.
*
* \param dst_buffer: host buffer
* \param src_buffer: device 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 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.
*/
ConversionType conversion_type_for_read(eGPUDataFormat host_format,
eGPUTextureFormat device_format);
ConversionType conversion_type_for_update(eGPUDataFormat host_format,
eGPUTextureFormat device_format);
void convert_device_to_host(void *dst_buffer,
const void *src_buffer,
size_t buffer_size,
eGPUDataFormat host_format,
eGPUTextureFormat device_format);
void convert(ConversionType type,
eGPUTextureFormat device_format,
size_t sample_len,
void *dst_memory,
const void *src_memory);
}; // namespace blender::gpu
}; // namespace blender::gpu

View File

@ -138,13 +138,7 @@ void *VKTexture::read(int mip, eGPUDataFormat format)
command_buffer.submit();
void *data = MEM_mallocN(host_memory_size, __func__);
/* Convert data from device to host memory. */
ConversionType conversion_type = conversion_type_for_read(format, format_);
BLI_assert_msg(conversion_type != ConversionType::UNSUPPORTED,
"Memory data conversions not implemented yet");
convert(conversion_type, format_, sample_len, data, staging_buffer.mapped_memory_get());
convert_device_to_host(data, staging_buffer.mapped_memory_get(), sample_len, format, format_);
return data;
}
@ -163,11 +157,7 @@ void VKTexture::update_sub(
staging_buffer.create(
context, device_memory_size, GPU_USAGE_DEVICE_ONLY, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
ConversionType conversion_type = conversion_type_for_update(format, format_);
BLI_assert_msg(conversion_type != ConversionType::UNSUPPORTED,
"Memory data conversions not implemented yet");
convert(conversion_type, format_, sample_len, staging_buffer.mapped_memory_get(), data);
convert_host_to_device(staging_buffer.mapped_memory_get(), data, sample_len, format, format_);
VkBufferImageCopy region = {};
region.imageExtent.width = extent[0];