Vulkan: Clearing Storage Buffers #105487
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "GPU_storage_buffer.h"
|
#include "GPU_storage_buffer.h"
|
||||||
|
|
||||||
|
#include "BLI_math_vector_types.hh"
|
||||||
Jeroen-Bakker marked this conversation as resolved
Outdated
|
|||||||
#include "BLI_vector.hh"
|
#include "BLI_vector.hh"
|
||||||
|
|
||||||
#include "gpu_testing.hh"
|
#include "gpu_testing.hh"
|
||||||
|
@ -22,7 +23,7 @@ static Vector<int32_t> test_data()
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_gpu_storage_buffer_create_update_read()
|
static void test_storage_buffer_create_update_read()
|
||||||
{
|
{
|
||||||
GPUStorageBuf *ssbo = GPU_storagebuf_create_ex(
|
GPUStorageBuf *ssbo = GPU_storagebuf_create_ex(
|
||||||
SIZE_IN_BYTES, nullptr, GPU_USAGE_STATIC, __func__);
|
SIZE_IN_BYTES, nullptr, GPU_USAGE_STATIC, __func__);
|
||||||
|
@ -45,6 +46,94 @@ static void test_gpu_storage_buffer_create_update_read()
|
||||||
GPU_storagebuf_free(ssbo);
|
GPU_storagebuf_free(ssbo);
|
||||||
}
|
}
|
||||||
|
|
||||||
GPU_TEST(gpu_storage_buffer_create_update_read);
|
GPU_TEST(storage_buffer_create_update_read);
|
||||||
|
|
||||||
|
static void test_storage_buffer_clear_zero()
|
||||||
|
{
|
||||||
|
GPUStorageBuf *ssbo = GPU_storagebuf_create_ex(
|
||||||
|
SIZE_IN_BYTES, nullptr, GPU_USAGE_STATIC, __func__);
|
||||||
|
EXPECT_NE(ssbo, nullptr);
|
||||||
|
|
||||||
|
/* Upload some dummy data. */
|
||||||
|
const Vector<int32_t> data = test_data();
|
||||||
|
GPU_storagebuf_update(ssbo, data.data());
|
||||||
|
GPU_storagebuf_clear_to_zero(ssbo);
|
||||||
|
|
||||||
|
/* Read back data from SSBO. */
|
||||||
|
Vector<int32_t> read_data;
|
||||||
|
read_data.resize(SIZE, 0);
|
||||||
|
GPU_storagebuf_read(ssbo, read_data.data());
|
||||||
|
|
||||||
|
/* Check if data is the same. */
|
||||||
|
for (int i : IndexRange(SIZE)) {
|
||||||
|
EXPECT_EQ(0, read_data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
GPU_storagebuf_free(ssbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
GPU_TEST(storage_buffer_clear_zero);
|
||||||
|
|
||||||
|
/* TODO: Make template.*/
|
||||||
|
template<eGPUTextureFormat TextureFormat> static void storage_buffer_clear_uniform()
|
||||||
|
{
|
||||||
|
GPUStorageBuf *ssbo = GPU_storagebuf_create_ex(
|
||||||
|
SIZE_IN_BYTES, nullptr, GPU_USAGE_STATIC, __func__);
|
||||||
|
EXPECT_NE(ssbo, nullptr);
|
||||||
|
|
||||||
|
/* Read back data from SSBO. */
|
||||||
|
int4 clear_data = {-1, -1, -1, -1};
|
||||||
|
GPU_storagebuf_clear(ssbo, TextureFormat, GPU_DATA_INT, &clear_data);
|
||||||
|
|
||||||
|
/* Check if data is the same. */
|
||||||
|
Vector<int32_t> read_data;
|
||||||
|
read_data.resize(SIZE, 0);
|
||||||
|
GPU_storagebuf_read(ssbo, read_data.data());
|
||||||
|
for (int i : IndexRange(SIZE)) {
|
||||||
|
EXPECT_EQ(clear_data[i % 4], read_data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
GPU_storagebuf_free(ssbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_storage_buffer_clear_RGBA32I_uniform()
|
||||||
|
{
|
||||||
|
storage_buffer_clear_uniform<GPU_RGBA32I>();
|
||||||
|
}
|
||||||
|
GPU_TEST(storage_buffer_clear_RGBA32I_uniform);
|
||||||
|
|
||||||
|
static void test_storage_buffer_clear_RG32I_uniform()
|
||||||
|
{
|
||||||
|
storage_buffer_clear_uniform<GPU_RG32I>();
|
||||||
|
}
|
||||||
|
GPU_TEST(storage_buffer_clear_RG32I_uniform);
|
||||||
|
|
||||||
|
static void test_storage_buffer_clear_RG32UI_uniform()
|
||||||
|
{
|
||||||
|
storage_buffer_clear_uniform<GPU_RG32UI>();
|
||||||
|
}
|
||||||
|
GPU_TEST(storage_buffer_clear_RG32UI_uniform);
|
||||||
|
|
||||||
|
static void test_storage_buffer_clear_RGBAI32()
|
||||||
|
{
|
||||||
|
GPUStorageBuf *ssbo = GPU_storagebuf_create_ex(
|
||||||
|
SIZE_IN_BYTES, nullptr, GPU_USAGE_STATIC, __func__);
|
||||||
|
EXPECT_NE(ssbo, nullptr);
|
||||||
|
|
||||||
|
/* Read back data from SSBO. */
|
||||||
|
int4 clear_data = {-1, -2, -3, -4};
|
||||||
|
GPU_storagebuf_clear(ssbo, GPU_RGBA32I, GPU_DATA_INT, &clear_data);
|
||||||
|
|
||||||
|
/* Check if data is the same. */
|
||||||
|
Vector<int32_t> read_data;
|
||||||
|
read_data.resize(SIZE, 0);
|
||||||
|
GPU_storagebuf_read(ssbo, read_data.data());
|
||||||
|
for (int i : IndexRange(SIZE)) {
|
||||||
|
EXPECT_EQ(clear_data[i % 4], read_data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
GPU_storagebuf_free(ssbo);
|
||||||
|
}
|
||||||
|
GPU_TEST(storage_buffer_clear_RGBAI32);
|
||||||
|
|
||||||
} // namespace blender::gpu::tests
|
} // namespace blender::gpu::tests
|
||||||
|
|
|
@ -86,6 +86,11 @@ void VKCommandBuffer::push_constants(const VKPushConstants &push_constants,
|
||||||
push_constants.data());
|
push_constants.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VKCommandBuffer::fill(VKBuffer &buffer, uint32_t clear_data)
|
||||||
|
{
|
||||||
|
vkCmdFillBuffer(vk_command_buffer_, buffer.vk_handle(), 0, buffer.size_in_bytes(), clear_data);
|
||||||
|
}
|
||||||
|
|
||||||
void VKCommandBuffer::copy(VKBuffer &dst_buffer,
|
void VKCommandBuffer::copy(VKBuffer &dst_buffer,
|
||||||
VKTexture &src_texture,
|
VKTexture &src_texture,
|
||||||
Span<VkBufferImageCopy> regions)
|
Span<VkBufferImageCopy> regions)
|
||||||
|
|
|
@ -51,6 +51,7 @@ class VKCommandBuffer : NonCopyable, NonMovable {
|
||||||
void pipeline_barrier(VkPipelineStageFlags source_stages,
|
void pipeline_barrier(VkPipelineStageFlags source_stages,
|
||||||
VkPipelineStageFlags destination_stages);
|
VkPipelineStageFlags destination_stages);
|
||||||
void pipeline_barrier(Span<VkImageMemoryBarrier> image_memory_barriers);
|
void pipeline_barrier(Span<VkImageMemoryBarrier> image_memory_barriers);
|
||||||
|
void fill(VKBuffer &buffer, uint32_t data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop recording commands, encode + send the recordings to Vulkan, wait for the until the
|
* Stop recording commands, encode + send the recordings to Vulkan, wait for the until the
|
||||||
|
|
|
@ -10,6 +10,8 @@
|
||||||
|
|
||||||
#include "vk_storage_buffer.hh"
|
#include "vk_storage_buffer.hh"
|
||||||
|
|
||||||
|
#include "BLI_span.hh"
|
||||||
|
|
||||||
namespace blender::gpu {
|
namespace blender::gpu {
|
||||||
|
|
||||||
void VKStorageBuffer::update(const void *data)
|
void VKStorageBuffer::update(const void *data)
|
||||||
|
@ -23,7 +25,11 @@ void VKStorageBuffer::update(const void *data)
|
||||||
|
|
||||||
void VKStorageBuffer::allocate(VKContext &context)
|
void VKStorageBuffer::allocate(VKContext &context)
|
||||||
{
|
{
|
||||||
buffer_.create(context, size_in_bytes_, usage_, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
|
buffer_.create(context,
|
||||||
|
size_in_bytes_,
|
||||||
|
usage_,
|
||||||
|
static_cast<VkBufferUsageFlagBits>(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
|
||||||
|
VK_BUFFER_USAGE_TRANSFER_DST_BIT));
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKStorageBuffer::bind(int slot)
|
void VKStorageBuffer::bind(int slot)
|
||||||
|
@ -43,11 +49,51 @@ void VKStorageBuffer::unbind()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void VKStorageBuffer::clear(eGPUTextureFormat /*internal_format*/,
|
static bool is_uniform(Span<uint32_t> data)
|
||||||
eGPUDataFormat /*data_format*/,
|
|
||||||
void * /*data*/)
|
|
||||||
{
|
{
|
||||||
|
BLI_assert(!data.is_empty());
|
||||||
|
uint32_t expected_value = data[0];
|
||||||
|
for (uint32_t value : data) {
|
||||||
|
if (value != expected_value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool can_use_fill_command(eGPUTextureFormat internal_format,
|
||||||
|
eGPUDataFormat /*data_format*/,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
int element_size = to_bytesize(internal_format);
|
||||||
|
int num_components = element_size / 8;
|
||||||
|
if (is_uniform(Span<uint32_t>(static_cast<uint32_t *>(data), num_components))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VKStorageBuffer::clear(eGPUTextureFormat internal_format,
|
||||||
|
eGPUDataFormat data_format,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
VKContext &context = *VKContext::get();
|
||||||
|
if (!buffer_.is_allocated()) {
|
||||||
|
allocate(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
VKCommandBuffer &command_buffer = context.command_buffer_get();
|
||||||
|
if (can_use_fill_command(internal_format, data_format, data)) {
|
||||||
|
/* When the data format is 4 bytes we can use vkCmdFillBuffer.
|
||||||
|
* Common case when using GPU_storagebuf_clear_to_zero. */
|
||||||
|
command_buffer.fill(buffer_, *static_cast<uint32_t *>(data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Use a compute shader to clear the buffer. */
|
||||||
|
BLI_assert_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
void VKStorageBuffer::copy_sub(VertBuf * /*src*/,
|
void VKStorageBuffer::copy_sub(VertBuf * /*src*/,
|
||||||
uint /*dst_offset*/,
|
uint /*dst_offset*/,
|
||||||
uint /*src_offset*/,
|
uint /*src_offset*/,
|
||||||
|
|
Loading…
Reference in New Issue
This include isn't needed.