Vulkan: Convert VertexBuffer to Contain Supported Attributes #107733

Merged
Jeroen Bakker merged 7 commits from Jeroen-Bakker/blender:vulkan-attribute-data-conversions into main 2023-05-11 12:23:29 +02:00
9 changed files with 490 additions and 30 deletions

View File

@ -645,6 +645,7 @@ template<typename T> struct AssertUnitEpsilon {
using char2 = blender::VecBase<int8_t, 2>;
using char3 = blender::VecBase<int8_t, 3>;
using char4 = blender::VecBase<int8_t, 4>;
using uchar3 = blender::VecBase<uint8_t, 3>;
using uchar4 = blender::VecBase<uint8_t, 4>;
@ -659,6 +660,7 @@ using uint4 = VecBase<uint32_t, 4>;
using short2 = blender::VecBase<int16_t, 2>;
using short3 = blender::VecBase<int16_t, 3>;
using short4 = blender::VecBase<int16_t, 4>;
using ushort2 = VecBase<uint16_t, 2>;
using ushort3 = blender::VecBase<uint16_t, 3>;

View File

@ -848,6 +848,7 @@ if(WITH_GTESTS)
tests/state_blend_test.cc
tests/storage_buffer_test.cc
tests/texture_test.cc
tests/vertex_buffer_test.cc
tests/gpu_testing.hh
)

View File

@ -0,0 +1,116 @@
#include "testing/testing.h"
#include "GPU_framebuffer.h"
#include "GPU_immediate.h"
#include "GPU_shader.h"
#include "GPU_vertex_buffer.h"
#include "GPU_vertex_format.h"
#include "BLI_index_range.hh"
#include "BLI_math_vector_types.hh"
#include "gpu_testing.hh"
namespace blender::gpu::tests {
static constexpr int Size = 256;
template<GPUVertCompType comp_type, GPUVertFetchMode fetch_mode, typename ColorType>
static void vertex_buffer_fetch_mode(ColorType color)
{
GPUOffScreen *offscreen = GPU_offscreen_create(Size,
Size,
false,
GPU_RGBA16F,
GPU_TEXTURE_USAGE_ATTACHMENT |
GPU_TEXTURE_USAGE_HOST_READ,
nullptr);
BLI_assert(offscreen != nullptr);
GPU_offscreen_bind(offscreen, false);
GPUTexture *color_texture = GPU_offscreen_color_texture(offscreen);
GPU_texture_clear(color_texture, GPU_DATA_FLOAT, float4(0.0f));
GPUVertFormat format = {0};
GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
GPU_vertformat_attr_add(&format, "color", comp_type, 4, fetch_mode);
GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
GPU_vertbuf_data_alloc(vbo, 4);
struct Vert {
float2 pos;
ColorType color;
};
Vert data[4] = {
{float2(-1.0, -1.0), color},
{float2(1.0, -1.0), color},
{float2(1.0, 1.0), color},
{float2(-1.0, 1.0), color},
};
for (int i : IndexRange(4)) {
GPU_vertbuf_vert_set(vbo, i, &data[i]);
}
GPUBatch *batch = GPU_batch_create(GPU_PRIM_TRI_FAN, vbo, NULL);
GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_FLAT_COLOR);
GPU_batch_draw(batch);
GPU_offscreen_unbind(offscreen, false);
GPU_flush();
/* Read back data and perform some basic tests. */
float read_data[4 * Size * Size];
GPU_offscreen_read_color(offscreen, GPU_DATA_FLOAT, &read_data);
for (int pixel_index = 0; pixel_index < Size * Size; pixel_index++) {
float4 read_color = float4(&read_data[pixel_index * 4]);
EXPECT_EQ(read_color, float4(color));
}
GPU_batch_discard(batch);
GPU_vertbuf_discard(vbo);
GPU_offscreen_free(offscreen);
}
static void test_vertex_buffer_fetch_mode__GPU_COMP_I8__GPU_FETCH_INT_TO_FLOAT()
{
vertex_buffer_fetch_mode<GPU_COMP_I8, GPU_FETCH_INT_TO_FLOAT, char4>(char4(4, 5, 6, 1));
}
GPU_TEST(vertex_buffer_fetch_mode__GPU_COMP_I8__GPU_FETCH_INT_TO_FLOAT);
static void test_vertex_buffer_fetch_mode__GPU_COMP_U8__GPU_FETCH_INT_TO_FLOAT()
{
vertex_buffer_fetch_mode<GPU_COMP_U8, GPU_FETCH_INT_TO_FLOAT, uchar4>(uchar4(4, 5, 6, 1));
}
GPU_TEST(vertex_buffer_fetch_mode__GPU_COMP_U8__GPU_FETCH_INT_TO_FLOAT);
static void test_vertex_buffer_fetch_mode__GPU_COMP_I16__GPU_FETCH_INT_TO_FLOAT()
{
vertex_buffer_fetch_mode<GPU_COMP_I16, GPU_FETCH_INT_TO_FLOAT, short4>(short4(4, 5, 6, 1));
}
GPU_TEST(vertex_buffer_fetch_mode__GPU_COMP_I16__GPU_FETCH_INT_TO_FLOAT);
static void test_vertex_buffer_fetch_mode__GPU_COMP_U16__GPU_FETCH_INT_TO_FLOAT()
{
vertex_buffer_fetch_mode<GPU_COMP_U16, GPU_FETCH_INT_TO_FLOAT, ushort4>(ushort4(4, 5, 6, 1));
}
GPU_TEST(vertex_buffer_fetch_mode__GPU_COMP_U16__GPU_FETCH_INT_TO_FLOAT);
static void test_vertex_buffer_fetch_mode__GPU_COMP_I32__GPU_FETCH_INT_TO_FLOAT()
{
vertex_buffer_fetch_mode<GPU_COMP_I32, GPU_FETCH_INT_TO_FLOAT, int4>(int4(4, 5, 6, 1));
}
GPU_TEST(vertex_buffer_fetch_mode__GPU_COMP_I32__GPU_FETCH_INT_TO_FLOAT);
static void test_vertex_buffer_fetch_mode__GPU_COMP_U32__GPU_FETCH_INT_TO_FLOAT()
{
vertex_buffer_fetch_mode<GPU_COMP_U32, GPU_FETCH_INT_TO_FLOAT, uint4>(uint4(4, 5, 6, 1));
}
GPU_TEST(vertex_buffer_fetch_mode__GPU_COMP_U32__GPU_FETCH_INT_TO_FLOAT);
static void test_vertex_buffer_fetch_mode__GPU_COMP_F32__GPU_FETCH_FLOAT()
{
vertex_buffer_fetch_mode<GPU_COMP_F32, GPU_FETCH_FLOAT, float4>(float4(4, 5, 6, 1));
}
GPU_TEST(vertex_buffer_fetch_mode__GPU_COMP_F32__GPU_FETCH_FLOAT);
} // namespace blender::gpu::tests

View File

@ -241,7 +241,7 @@ VkFormat to_vk_format(const eGPUTextureFormat format)
return VK_FORMAT_UNDEFINED;
}
VkFormat to_vk_format(const GPUVertCompType type, const uint32_t size)
static VkFormat to_vk_format_norm(const GPUVertCompType type, const uint32_t size)
{
switch (type) {
case GPU_COMP_I8:
@ -254,8 +254,11 @@ VkFormat to_vk_format(const GPUVertCompType type, const uint32_t size)
return VK_FORMAT_R8G8B8_SNORM;
case 4:
return VK_FORMAT_R8G8B8A8_SNORM;
case 16:
return VK_FORMAT_R8G8B8A8_SNORM;
default:
break;
BLI_assert_unreachable();
return VK_FORMAT_R8_SNORM;
}
break;
@ -269,8 +272,11 @@ VkFormat to_vk_format(const GPUVertCompType type, const uint32_t size)
return VK_FORMAT_R8G8B8_UNORM;
case 4:
return VK_FORMAT_R8G8B8A8_UNORM;
case 16:
return VK_FORMAT_R8G8B8A8_UNORM;
default:
break;
BLI_assert_unreachable();
return VK_FORMAT_R8_UNORM;
}
break;
@ -285,7 +291,8 @@ VkFormat to_vk_format(const GPUVertCompType type, const uint32_t size)
case 8:
return VK_FORMAT_R16G16B16A16_SNORM;
default:
break;
BLI_assert_unreachable();
return VK_FORMAT_R16_SNORM;
}
break;
@ -300,39 +307,102 @@ VkFormat to_vk_format(const GPUVertCompType type, const uint32_t size)
case 8:
return VK_FORMAT_R16G16B16A16_UNORM;
default:
break;
BLI_assert_unreachable();
return VK_FORMAT_R16_UNORM;
}
break;
case GPU_COMP_I10:
BLI_assert(size == 4);
return VK_FORMAT_A2B10G10R10_SNORM_PACK32;
case GPU_COMP_I32:
switch (size) {
case 4:
return VK_FORMAT_R32_SINT;
case 8:
return VK_FORMAT_R32G32_SINT;
case 12:
return VK_FORMAT_R32G32B32_SINT;
case 16:
return VK_FORMAT_R32G32B32A32_SINT;
default:
break;
}
break;
case GPU_COMP_U32:
case GPU_COMP_F32:
default:
break;
}
BLI_assert_unreachable();
return VK_FORMAT_R32_SFLOAT;
}
static VkFormat to_vk_format_float(const GPUVertCompType type, const uint32_t size)
{
switch (type) {
case GPU_COMP_I8:
switch (size) {
case 1:
return VK_FORMAT_R8_SSCALED;
case 2:
return VK_FORMAT_R8G8_SSCALED;
case 3:
return VK_FORMAT_R8G8B8_SSCALED;
case 4:
return VK_FORMAT_R8G8B8A8_SSCALED;
default:
BLI_assert_unreachable();
return VK_FORMAT_R8_SSCALED;
}
case GPU_COMP_U8:
switch (size) {
case 1:
return VK_FORMAT_R8_USCALED;
case 2:
return VK_FORMAT_R8G8_USCALED;
case 3:
return VK_FORMAT_R8G8B8_USCALED;
case 4:
return VK_FORMAT_R8G8B8A8_USCALED;
default:
BLI_assert_unreachable();
return VK_FORMAT_R8_USCALED;
}
case GPU_COMP_I16:
switch (size) {
case 2:
return VK_FORMAT_R16_SSCALED;
case 4:
return VK_FORMAT_R16G16_SSCALED;
case 6:
return VK_FORMAT_R16G16B16_SSCALED;
case 8:
return VK_FORMAT_R16G16B16A16_SSCALED;
default:
BLI_assert_unreachable();
return VK_FORMAT_R16_SSCALED;
}
case GPU_COMP_U16:
switch (size) {
case 2:
return VK_FORMAT_R16_USCALED;
case 4:
return VK_FORMAT_R16G16_USCALED;
case 6:
return VK_FORMAT_R16G16B16_USCALED;
case 8:
return VK_FORMAT_R16G16B16A16_USCALED;
default:
BLI_assert_unreachable();
return VK_FORMAT_R16_USCALED;
}
case GPU_COMP_I32:
case GPU_COMP_U32:
/* NOTE: GPU_COMP_I32/U32 using GPU_FETCH_INT_TO_FLOAT isn't natively supported. These are
* converted on host-side to signed floats. */
Jeroen-Bakker marked this conversation as resolved Outdated

spelling

spelling
switch (size) {
case 4:
return VK_FORMAT_R32_UINT;
return VK_FORMAT_R32_SFLOAT;
case 8:
return VK_FORMAT_R32G32_UINT;
return VK_FORMAT_R32G32_SFLOAT;
case 12:
return VK_FORMAT_R32G32B32_UINT;
return VK_FORMAT_R32G32B32_SFLOAT;
case 16:
return VK_FORMAT_R32G32B32A32_UINT;
return VK_FORMAT_R32G32B32A32_SFLOAT;
default:
break;
BLI_assert_unreachable();
return VK_FORMAT_R32_SFLOAT;
}
break;
case GPU_COMP_F32:
switch (size) {
@ -347,17 +417,165 @@ VkFormat to_vk_format(const GPUVertCompType type, const uint32_t size)
case 64:
return VK_FORMAT_R32G32B32A32_SFLOAT;
default:
break;
BLI_assert_unreachable();
return VK_FORMAT_R32_SFLOAT;
}
case GPU_COMP_I10:
BLI_assert(size == 4);
return VK_FORMAT_A2B10G10R10_SSCALED_PACK32;
default:
break;
}
BLI_assert_unreachable();
return VK_FORMAT_R32_SFLOAT;
}
static VkFormat to_vk_format_int(const GPUVertCompType type, const uint32_t size)
{
switch (type) {
case GPU_COMP_I8:
switch (size) {
case 1:
return VK_FORMAT_R8_SINT;
case 2:
return VK_FORMAT_R8G8_SINT;
case 3:
return VK_FORMAT_R8G8B8_SINT;
case 4:
return VK_FORMAT_R8G8B8A8_SINT;
default:
BLI_assert_unreachable();
return VK_FORMAT_R8_SINT;
}
break;
case GPU_COMP_U8:
switch (size) {
case 1:
return VK_FORMAT_R8_USCALED;
case 2:
return VK_FORMAT_R8G8_USCALED;
case 3:
return VK_FORMAT_R8G8B8_USCALED;
case 4:
return VK_FORMAT_R8G8B8A8_USCALED;
default:
BLI_assert_unreachable();
return VK_FORMAT_R8_USCALED;
}
break;
case GPU_COMP_I16:
switch (size) {
case 2:
return VK_FORMAT_R16_SINT;
case 4:
return VK_FORMAT_R16G16_SINT;
case 6:
return VK_FORMAT_R16G16B16_SINT;
case 8:
return VK_FORMAT_R16G16B16A16_SINT;
default:
BLI_assert_unreachable();
return VK_FORMAT_R16_SINT;
}
break;
case GPU_COMP_U16:
switch (size) {
case 2:
return VK_FORMAT_R16_USCALED;
case 4:
return VK_FORMAT_R16G16_USCALED;
case 6:
return VK_FORMAT_R16G16B16_USCALED;
case 8:
return VK_FORMAT_R16G16B16A16_USCALED;
default:
BLI_assert_unreachable();
return VK_FORMAT_R16_USCALED;
}
break;
case GPU_COMP_I32:
switch (size) {
case 4:
return VK_FORMAT_R32_SINT;
case 8:
return VK_FORMAT_R32G32_SINT;
case 12:
return VK_FORMAT_R32G32B32_SINT;
case 16:
return VK_FORMAT_R32G32B32A32_SINT;
default:
BLI_assert_unreachable();
return VK_FORMAT_R32_SINT;
}
break;
case GPU_COMP_U32:
switch (size) {
case 4:
return VK_FORMAT_R32_UINT;
case 8:
return VK_FORMAT_R32G32_UINT;
case 12:
return VK_FORMAT_R32G32B32_UINT;
case 16:
return VK_FORMAT_R32G32B32A32_UINT;
default:
BLI_assert_unreachable();
return VK_FORMAT_R32_UINT;
}
break;
case GPU_COMP_F32:
switch (size) {
case 4:
return VK_FORMAT_R32_SINT;
case 8:
return VK_FORMAT_R32G32_SINT;
case 12:
return VK_FORMAT_R32G32B32_SINT;
case 16:
return VK_FORMAT_R32G32B32A32_SINT;
default:
BLI_assert_unreachable();
return VK_FORMAT_R32_SINT;
}
break;
case GPU_COMP_I10:
BLI_assert(size == 4);
return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
return VK_FORMAT_A2B10G10R10_SINT_PACK32;
default:
break;
}
BLI_assert_unreachable();
return VK_FORMAT_R32_SFLOAT;
}
VkFormat to_vk_format(const GPUVertCompType type, const uint32_t size, GPUVertFetchMode fetch_mode)
{
switch (fetch_mode) {
case GPU_FETCH_FLOAT:
case GPU_FETCH_INT_TO_FLOAT:
return to_vk_format_float(type, size);
break;
case GPU_FETCH_INT:
return to_vk_format_int(type, size);
break;
case GPU_FETCH_INT_TO_FLOAT_UNIT:
return to_vk_format_norm(type, size);
break;
default:
break;
}
BLI_assert_unreachable();
return VK_FORMAT_R32_SFLOAT;
}

View File

@ -24,7 +24,9 @@ namespace blender::gpu {
VkImageAspectFlagBits to_vk_image_aspect_flag_bits(const eGPUTextureFormat format);
VkFormat to_vk_format(const eGPUTextureFormat format);
VkFormat to_vk_format(const GPUVertCompType type, const uint32_t size);
VkFormat to_vk_format(const GPUVertCompType type,
const uint32_t size,
const GPUVertFetchMode fetch_mode);
VkComponentMapping to_vk_component_mapping(const eGPUTextureFormat format);
VkImageViewType to_vk_image_view_type(const eGPUTextureType type);
VkImageType to_vk_image_type(const eGPUTextureType type);

View File

@ -882,4 +882,56 @@ void convert_device_to_host(void *dst_buffer,
/* \} */
/* -------------------------------------------------------------------- */
/** \name Vertex Attributes
* \{ */
static bool conversion_needed(const GPUVertAttr &vertex_attribute)
{
return (vertex_attribute.fetch_mode == GPU_FETCH_INT_TO_FLOAT &&
ELEM(vertex_attribute.comp_type, GPU_COMP_I32, GPU_COMP_U32));
}
bool conversion_needed(const GPUVertFormat &vertex_format)
{
for (int attr_index : IndexRange(vertex_format.attr_len)) {
const GPUVertAttr &vert_attr = vertex_format.attrs[attr_index];
if (conversion_needed(vert_attr)) {
return true;
}
}
return false;
}
void convert_in_place(void *data, const GPUVertFormat &vertex_format, const uint vertex_len)
{
BLI_assert(vertex_format.deinterleaved == false);
for (int attr_index : IndexRange(vertex_format.attr_len)) {
const GPUVertAttr &vert_attr = vertex_format.attrs[attr_index];
if (!conversion_needed(vert_attr)) {
continue;
}
void *row_data = static_cast<uint8_t *>(data) + vert_attr.offset;
for (int vert_index = 0; vert_index < vertex_len; vert_index++) {
if (vert_attr.comp_type == GPU_COMP_I32) {
for (int component : IndexRange(vert_attr.comp_len)) {
int32_t *component_in = static_cast<int32_t *>(row_data) + component;
float *component_out = static_cast<float *>(row_data) + component;
*component_out = float(*component_in);
}
}
else if (vert_attr.comp_type == GPU_COMP_U32) {
for (int component : IndexRange(vert_attr.comp_len)) {
uint32_t *component_in = static_cast<uint32_t *>(row_data) + component;
float *component_out = static_cast<float *>(row_data) + component;
*component_out = float(*component_in);
}
}
row_data = static_cast<uint8_t *>(row_data) + vertex_format.stride;
}
}
}
/* \} */
} // namespace blender::gpu

View File

@ -72,4 +72,28 @@ void convert_device_to_host(void *dst_buffer,
eGPUDataFormat host_format,
eGPUTextureFormat device_format);
/**
* Are all attributes of the given vertex format natively supported or does conversion needs to
* happen.
*
* \param vertex_format: the vertex format to check if an associated buffer requires conversion
* being done on the host.
*/
bool conversion_needed(const GPUVertFormat &vertex_format);
/**
* Convert the given `data` to contain Vulkan natively supported data formats.
*
* When for an vertex attribute the fetch mode is set to GPU_FETCH_INT_TO_FLOAT and the attribute
* is an int32_t or uint32_t the conversion will be done. Attributes of 16 or 8 bits are supported
* natively and will be done in Vulkan.
*
* \param data: Buffer to convert. Data will be converted in place.
* \param vertex_format: Vertex format of the given data. Attributes that aren't supported will be
* converted to a supported one.
* \param vertex_len: Number of vertices of the given data buffer;
* The number of vertices to convert.
*/
void convert_in_place(void *data, const GPUVertFormat &vertex_format, const uint vertex_len);
}; // namespace blender::gpu

View File

@ -7,6 +7,7 @@
#include "MEM_guardedalloc.h"
#include "vk_data_conversion.hh"
#include "vk_shader.hh"
#include "vk_shader_interface.hh"
#include "vk_vertex_buffer.hh"
@ -66,13 +67,51 @@ void VKVertexBuffer::release_data()
MEM_SAFE_FREE(data);
}
void VKVertexBuffer::upload_data() {}
static bool inplace_conversion_supported(const GPUUsageType &usage)
{
return ELEM(usage, GPU_USAGE_STATIC, GPU_USAGE_STREAM);
}
void *VKVertexBuffer::convert() const
{
void *out_data = data;
if (!inplace_conversion_supported(usage_)) {
out_data = MEM_dupallocN(out_data);
}
BLI_assert(format.deinterleaved);
convert_in_place(out_data, format, vertex_len);
return out_data;
}
void VKVertexBuffer::upload_data()
{
if (!buffer_.is_allocated()) {
allocate();
}
if (flag &= GPU_VERTBUF_DATA_DIRTY) {
void *data_to_upload = data;
if (conversion_needed(format)) {
data_to_upload = convert();
}
buffer_.update(data_to_upload);
if (data_to_upload != data) {
MEM_SAFE_FREE(data_to_upload);
}
if (usage_ == GPU_USAGE_STATIC) {
MEM_SAFE_FREE(data);
}
flag &= ~GPU_VERTBUF_DATA_DIRTY;
flag |= GPU_VERTBUF_DATA_UPLOADED;
}
}
void VKVertexBuffer::duplicate_data(VertBuf * /*dst*/) {}
void VKVertexBuffer::allocate()
{
buffer_.create(size_used_get(),
buffer_.create(size_alloc_get(),
usage_,
static_cast<VkBufferUsageFlagBits>(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT));

View File

@ -40,6 +40,12 @@ class VKVertexBuffer : public VertBuf {
private:
void allocate();
void *convert() const;
};
static inline VKVertexBuffer *unwrap(VertBuf *vertex_buffer)
{
return static_cast<VKVertexBuffer *>(vertex_buffer);
}
} // namespace blender::gpu