GPU: Refactor API for Clearing Storage Buffers

The previous API for clearing storage buffers was following the OpenGL
api. OpenGL has many options to support for data conversions, striding
and sizzling. Metal and Vulkan don't have these features and we have to
deal it ourselves.

Blender internally only uses a tiny subset for what is possible in
OpenGL. Making the current API to difficult to implement on our future
platforms as we had to implement all cases, most even not used at all.

By changing the API we make future development easier as we only need
to implement what we are actually using.

**New API**

`GPU_storagebuf_clear(GPUStorageBuf* ssbo, uint32_t clear_value)`

Related issue: #105492

Pull Request: blender/blender#105521
This commit is contained in:
2023-03-09 18:46:28 +01:00
committed by Clément Foucault
parent ffcd8e6b02
commit af5a115f65
10 changed files with 29 additions and 41 deletions

View File

@@ -836,8 +836,7 @@ void ShadowModule::end_sync()
tilemap_pool.tiles_data.clear_to_zero(); tilemap_pool.tiles_data.clear_to_zero();
/* Clear cached page buffer. */ /* Clear cached page buffer. */
int2 data = {-1, -1}; GPU_storagebuf_clear(pages_cached_data_, -1);
GPU_storagebuf_clear(pages_cached_data_, GPU_RG32I, GPU_DATA_INT, &data);
/* Reset info to match new state. */ /* Reset info to match new state. */
pages_infos_data_.page_free_count = shadow_page_len_; pages_infos_data_.page_free_count = shadow_page_len_;

View File

@@ -220,14 +220,14 @@ void ShadowPass::ShadowView::compute_visibility(ObjectBoundsBuf &bounds,
uint words_len = (view_len_ == 1) ? divide_ceil_u(resource_len, 32) : uint words_len = (view_len_ == 1) ? divide_ceil_u(resource_len, 32) :
resource_len * word_per_draw; resource_len * word_per_draw;
words_len = ceil_to_multiple_u(max_ii(1, words_len), 4); words_len = ceil_to_multiple_u(max_ii(1, words_len), 4);
uint32_t data = 0xFFFFFFFFu; const uint32_t data = 0xFFFFFFFFu;
if (current_pass_type_ == ShadowPass::PASS) { if (current_pass_type_ == ShadowPass::PASS) {
/* TODO(fclem): Resize to nearest pow2 to reduce fragmentation. */ /* TODO(fclem): Resize to nearest pow2 to reduce fragmentation. */
pass_visibility_buf_.resize(words_len); pass_visibility_buf_.resize(words_len);
GPU_storagebuf_clear(pass_visibility_buf_, GPU_R32UI, GPU_DATA_UINT, &data); GPU_storagebuf_clear(pass_visibility_buf_, data);
fail_visibility_buf_.resize(words_len); fail_visibility_buf_.resize(words_len);
GPU_storagebuf_clear(fail_visibility_buf_, GPU_R32UI, GPU_DATA_UINT, &data); GPU_storagebuf_clear(fail_visibility_buf_, data);
} }
else if (current_pass_type_ == ShadowPass::FAIL) { else if (current_pass_type_ == ShadowPass::FAIL) {
/* Already computed in the ShadowPass::PASS */ /* Already computed in the ShadowPass::PASS */
@@ -236,7 +236,7 @@ void ShadowPass::ShadowView::compute_visibility(ObjectBoundsBuf &bounds,
} }
else { else {
visibility_buf_.resize(words_len); visibility_buf_.resize(words_len);
GPU_storagebuf_clear(visibility_buf_, GPU_R32UI, GPU_DATA_UINT, &data); GPU_storagebuf_clear(visibility_buf_, data);
} }
if (do_visibility_) { if (do_visibility_) {

View File

@@ -280,8 +280,8 @@ void View::compute_visibility(ObjectBoundsBuf &bounds, uint resource_len, bool d
/* TODO(fclem): Resize to nearest pow2 to reduce fragmentation. */ /* TODO(fclem): Resize to nearest pow2 to reduce fragmentation. */
visibility_buf_.resize(words_len); visibility_buf_.resize(words_len);
uint32_t data = 0xFFFFFFFFu; const uint32_t data = 0xFFFFFFFFu;
GPU_storagebuf_clear(visibility_buf_, GPU_R32UI, GPU_DATA_UINT, &data); GPU_storagebuf_clear(visibility_buf_, data);
if (do_visibility_) { if (do_visibility_) {
GPUShader *shader = DRW_shader_draw_visibility_compute_get(); GPUShader *shader = DRW_shader_draw_visibility_compute_get();

View File

@@ -39,12 +39,14 @@ void GPU_storagebuf_bind(GPUStorageBuf *ssbo, int slot);
void GPU_storagebuf_unbind(GPUStorageBuf *ssbo); void GPU_storagebuf_unbind(GPUStorageBuf *ssbo);
void GPU_storagebuf_unbind_all(void); void GPU_storagebuf_unbind_all(void);
void GPU_storagebuf_clear(GPUStorageBuf *ssbo,
eGPUTextureFormat internal_format,
eGPUDataFormat data_format,
void *data);
void GPU_storagebuf_clear_to_zero(GPUStorageBuf *ssbo); void GPU_storagebuf_clear_to_zero(GPUStorageBuf *ssbo);
/**
* Clear the content of the buffer using the given #clear_value. #clear_value will be used as a
* repeatable pattern of 32bits.
*/
void GPU_storagebuf_clear(GPUStorageBuf *ssbo, uint32_t clear_value);
/** /**
* Read back content of the buffer to CPU for inspection. * Read back content of the buffer to CPU for inspection.
* Slow! Only use for inspection / debugging. * Slow! Only use for inspection / debugging.

View File

@@ -89,18 +89,14 @@ void GPU_storagebuf_unbind_all()
/* FIXME */ /* FIXME */
} }
void GPU_storagebuf_clear(GPUStorageBuf *ssbo,
eGPUTextureFormat internal_format,
eGPUDataFormat data_format,
void *data)
{
unwrap(ssbo)->clear(internal_format, data_format, data);
}
void GPU_storagebuf_clear_to_zero(GPUStorageBuf *ssbo) void GPU_storagebuf_clear_to_zero(GPUStorageBuf *ssbo)
{ {
uint32_t data = 0u; GPU_storagebuf_clear(ssbo, 0);
GPU_storagebuf_clear(ssbo, GPU_R32UI, GPU_DATA_UINT, &data); }
void GPU_storagebuf_clear(GPUStorageBuf *ssbo, uint32_t clear_value)
{
unwrap(ssbo)->clear(clear_value);
} }
void GPU_storagebuf_copy_sub_from_vertbuf( void GPU_storagebuf_copy_sub_from_vertbuf(

View File

@@ -7,6 +7,7 @@
#pragma once #pragma once
#include "BLI_span.hh"
#include "BLI_sys_types.h" #include "BLI_sys_types.h"
struct GPUStorageBuf; struct GPUStorageBuf;
@@ -42,9 +43,7 @@ class StorageBuf {
virtual void update(const void *data) = 0; virtual void update(const void *data) = 0;
virtual void bind(int slot) = 0; virtual void bind(int slot) = 0;
virtual void unbind() = 0; virtual void unbind() = 0;
virtual void clear(eGPUTextureFormat internal_format, virtual void clear(uint32_t clear_value) = 0;
eGPUDataFormat data_format,
void *data) = 0;
virtual void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) = 0; virtual void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) = 0;
virtual void read(void *data) = 0; virtual void read(void *data) = 0;
}; };

View File

@@ -117,27 +117,20 @@ void GLStorageBuf::unbind()
slot_ = 0; slot_ = 0;
} }
void GLStorageBuf::clear(eGPUTextureFormat internal_format, eGPUDataFormat data_format, void *data) void GLStorageBuf::clear(uint32_t clear_value)
{ {
if (ssbo_id_ == 0) { if (ssbo_id_ == 0) {
this->init(); this->init();
} }
if (GLContext::direct_state_access_support) { if (GLContext::direct_state_access_support) {
glClearNamedBufferData(ssbo_id_, glClearNamedBufferData(ssbo_id_, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, &clear_value);
to_gl_internal_format(internal_format),
to_gl_data_format(internal_format),
to_gl(data_format),
data);
} }
else { else {
/* WATCH(@fclem): This should be ok since we only use clear outside of drawing functions. */ /* WATCH(@fclem): This should be ok since we only use clear outside of drawing functions. */
glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo_id_); glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo_id_);
glClearBufferData(GL_SHADER_STORAGE_BUFFER, glClearBufferData(
to_gl_internal_format(internal_format), GL_SHADER_STORAGE_BUFFER, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, &clear_value);
to_gl_data_format(internal_format),
to_gl(data_format),
data);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
} }
} }

View File

@@ -33,7 +33,7 @@ class GLStorageBuf : public StorageBuf {
void update(const void *data) override; void update(const void *data) override;
void bind(int slot) override; void bind(int slot) override;
void unbind() override; void unbind() override;
void clear(eGPUTextureFormat internal_format, eGPUDataFormat data_format, void *data) override; void clear(uint32_t clear_value) override;
void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) override; void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) override;
void read(void *data) override; void read(void *data) override;

View File

@@ -43,11 +43,10 @@ void VKStorageBuffer::unbind()
{ {
} }
void VKStorageBuffer::clear(eGPUTextureFormat /*internal_format*/, void VKStorageBuffer::clear(uint32_t /*clear_value*/)
eGPUDataFormat /*data_format*/,
void * /*data*/)
{ {
} }
void VKStorageBuffer::copy_sub(VertBuf * /*src*/, void VKStorageBuffer::copy_sub(VertBuf * /*src*/,
uint /*dst_offset*/, uint /*dst_offset*/,
uint /*src_offset*/, uint /*src_offset*/,

View File

@@ -30,7 +30,7 @@ class VKStorageBuffer : public StorageBuf {
void update(const void *data) override; void update(const void *data) override;
void bind(int slot) override; void bind(int slot) override;
void unbind() override; void unbind() override;
void clear(eGPUTextureFormat internal_format, eGPUDataFormat data_format, void *data) override; void clear(uint32_t clear_value) override;
void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) override; void copy_sub(VertBuf *src, uint dst_offset, uint src_offset, uint copy_size) override;
void read(void *data) override; void read(void *data) override;