WIP: Vulkan: Clearing Storage Buffers #105299

Closed
Jeroen Bakker wants to merge 73 commits from Jeroen-Bakker:gpu-storage-buffer-clear into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
3 changed files with 48 additions and 37 deletions
Showing only changes of commit 8957c56f62 - Show all commits

View File

@ -6,6 +6,7 @@
*/
#include "vk_buffer.hh"
#include "BLI_span.hh"
namespace blender::gpu {
@ -75,6 +76,7 @@ bool VKBuffer::create(VKContext &context,
bool VKBuffer::update(VKContext &context, const void *data)
{
/* TODO: When size <64Kb we should use vkCmdUpdateBuffer. */
void *mapped_memory;
bool result = map(context, &mapped_memory);
if (result) {
@ -84,6 +86,47 @@ bool VKBuffer::update(VKContext &context, const void *data)
return result;
}
static bool is_uniform(Span<uint32_t> 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)
{
/* TODO: See `validate_data_format` what we should support. */
BLI_assert(ELEM(data_format, GPU_DATA_INT, GPU_DATA_UINT, GPU_DATA_FLOAT));
UNUSED_VARS_NDEBUG(data_format);
const uint32_t element_size = to_bytesize(internal_format);
const uint32_t num_components = element_size / sizeof(uint32_t);
return is_uniform(Span<uint32_t>(static_cast<uint32_t *>(data), num_components));
}
void VKBuffer::clear(VKContext &context,
eGPUTextureFormat internal_format,
eGPUDataFormat data_format,
void *data)
{
VKCommandBuffer &command_buffer = context.command_buffer_get();
if (can_use_fill_command(internal_format, data_format, data)) {
command_buffer.fill(*this, *static_cast<uint32_t *>(data));
return;
}
/* TODO: Use a compute shader to clear the buffer. This is performance wise not recommended, and
* should be avoided. There are some cases where we don't have a choice. Especially when using
* compute shaders.*/
BLI_assert_unreachable();
}
bool VKBuffer::map(VKContext &context, void **r_mapped_memory) const
{
VmaAllocator allocator = context.mem_allocator_get();

View File

@ -34,6 +34,10 @@ class VKBuffer {
GPUUsageType usage,
VkBufferUsageFlagBits buffer_usage);
bool update(VKContext &context, const void *data);
void clear(VKContext &context,
eGPUTextureFormat internal_format,
eGPUDataFormat data_format,
void *data);
bool free(VKContext &context);
bool map(VKContext &context, void **r_mapped_memory) const;
void unmap(VKContext &context) const;

View File

@ -10,8 +10,6 @@
#include "vk_storage_buffer.hh"
#include "BLI_span.hh"
namespace blender::gpu {
void VKStorageBuffer::update(const void *data)
@ -49,30 +47,6 @@ void VKStorageBuffer::unbind()
{
}
static bool is_uniform(Span<uint32_t> 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 / 4;
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)
@ -81,17 +55,7 @@ void VKStorageBuffer::clear(eGPUTextureFormat internal_format,
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();
buffer_.clear(context, internal_format, data_format, data);
}
void VKStorageBuffer::copy_sub(VertBuf * /*src*/,