DRW: Add double buffering of objects matrices, bounds, and infos

This allows easy delta calculation and access to last known position of
deleted objects.
This commit is contained in:
Clément Foucault 2023-01-18 14:30:47 +01:00
parent efe51f0220
commit 9c54f2655d
5 changed files with 87 additions and 46 deletions

View File

@ -11,6 +11,7 @@
#include "GPU_debug.h"
#include "draw_command.hh"
#include "draw_pass.hh"
#include "draw_shader.h"
#include "draw_view.hh"
@ -530,15 +531,20 @@ std::string StencilSet::serialize() const
/** \name Commands buffers binding / command / resource ID generation
* \{ */
void DrawCommandBuf::bind(RecordingState &state,
Vector<Header, 0> &headers,
Vector<Undetermined, 0> &commands)
void DrawCommandBuf::finalize_commands(Vector<Header, 0> &headers,
Vector<Undetermined, 0> &commands,
SubPassVector &sub_passes,
uint &resource_id_count,
ResourceIdBuf &resource_id_buf)
{
UNUSED_VARS(headers, commands);
resource_id_count_ = 0;
for (const Header &header : headers) {
if (header.type == Type::SubPass) {
/** WARNING: Recursive. */
auto &sub = sub_passes[int64_t(header.index)];
finalize_commands(
sub.headers_, sub.commands_, sub_passes, resource_id_count, resource_id_buf);
}
if (header.type != Type::Draw) {
continue;
}
@ -559,18 +565,28 @@ void DrawCommandBuf::bind(RecordingState &state,
if (cmd.handle.raw > 0) {
/* Save correct offset to start of resource_id buffer region for this draw. */
uint instance_first = resource_id_count_;
resource_id_count_ += cmd.instance_len;
uint instance_first = resource_id_count;
resource_id_count += cmd.instance_len;
/* Ensure the buffer is big enough. */
resource_id_buf_.get_or_resize(resource_id_count_ - 1);
resource_id_buf.get_or_resize(resource_id_count - 1);
/* Copy the resource id for all instances. */
uint index = cmd.handle.resource_index();
for (int i = instance_first; i < (instance_first + cmd.instance_len); i++) {
resource_id_buf_[i] = index;
resource_id_buf[i] = index;
}
}
}
}
void DrawCommandBuf::bind(RecordingState &state,
Vector<Header, 0> &headers,
Vector<Undetermined, 0> &commands,
SubPassVector &sub_passes)
{
resource_id_count_ = 0;
finalize_commands(headers, commands, sub_passes, resource_id_count_, resource_id_buf_);
resource_id_buf_.push_update();

View File

@ -21,6 +21,12 @@
#include "draw_state.h"
#include "draw_view.hh"
/* Forward declarations. */
namespace blender::draw::detail {
template<typename T, int64_t block_size> class SubPassVector;
template<typename DrawCommandBufType> class PassBase;
} // namespace blender::draw::detail
namespace blender::draw::command {
class DrawCommandBuf;
@ -411,6 +417,7 @@ class DrawCommandBuf {
private:
using ResourceIdBuf = StorageArrayBuffer<uint, 128, false>;
using SubPassVector = detail::SubPassVector<detail::PassBase<DrawCommandBuf>, 16>;
/** Array of resource id. One per instance. Generated on GPU and send to GPU. */
ResourceIdBuf resource_id_buf_;
@ -436,7 +443,17 @@ class DrawCommandBuf {
commands[index].draw = {batch, instance_len, vertex_len, vertex_first, handle};
}
void bind(RecordingState &state, Vector<Header, 0> &headers, Vector<Undetermined, 0> &commands);
void bind(RecordingState &state,
Vector<Header, 0> &headers,
Vector<Undetermined, 0> &commands,
SubPassVector &sub_passes);
private:
static void finalize_commands(Vector<Header, 0> &headers,
Vector<Undetermined, 0> &commands,
SubPassVector &sub_passes,
uint &resource_id_count,
ResourceIdBuf &resource_id_buf);
};
/** \} */

View File

@ -27,6 +27,10 @@ Manager::~Manager()
void Manager::begin_sync()
{
matrix_buf.swap();
bounds_buf.swap();
infos_buf.swap();
/* TODO: This means the reference is kept until further redraw or manager tear-down. Instead,
* they should be released after each draw loop. But for now, mimics old DRW behavior. */
for (GPUTexture *texture : acquired_textures) {
@ -39,9 +43,9 @@ void Manager::begin_sync()
#ifdef DEBUG
/* Detect uninitialized data. */
memset(matrix_buf.data(), 0xF0, resource_len_ * sizeof(*matrix_buf.data()));
memset(bounds_buf.data(), 0xF0, resource_len_ * sizeof(*bounds_buf.data()));
memset(infos_buf.data(), 0xF0, resource_len_ * sizeof(*infos_buf.data()));
memset(matrix_buf.current().data(), 0xF0, resource_len_ * sizeof(*matrix_buf.current().data()));
memset(bounds_buf.current().data(), 0xF0, resource_len_ * sizeof(*bounds_buf.current().data()));
memset(infos_buf.current().data(), 0xF0, resource_len_ * sizeof(*infos_buf.current().data()));
#endif
resource_len_ = 0;
attribute_len_ = 0;
@ -88,9 +92,9 @@ void Manager::end_sync()
sync_layer_attributes();
matrix_buf.push_update();
bounds_buf.push_update();
infos_buf.push_update();
matrix_buf.current().push_update();
bounds_buf.current().push_update();
infos_buf.current().push_update();
attributes_buf.push_update();
layer_attributes_buf.push_update();
attributes_buf_legacy.push_update();
@ -104,9 +108,9 @@ void Manager::end_sync()
GPUShader *shader = DRW_shader_draw_resource_finalize_get();
GPU_shader_bind(shader);
GPU_shader_uniform_1i(shader, "resource_len", resource_len_);
GPU_storagebuf_bind(matrix_buf, GPU_shader_get_ssbo(shader, "matrix_buf"));
GPU_storagebuf_bind(bounds_buf, GPU_shader_get_ssbo(shader, "bounds_buf"));
GPU_storagebuf_bind(infos_buf, GPU_shader_get_ssbo(shader, "infos_buf"));
GPU_storagebuf_bind(matrix_buf.current(), GPU_shader_get_ssbo(shader, "matrix_buf"));
GPU_storagebuf_bind(bounds_buf.current(), GPU_shader_get_ssbo(shader, "bounds_buf"));
GPU_storagebuf_bind(infos_buf.current(), GPU_shader_get_ssbo(shader, "infos_buf"));
GPU_compute_dispatch(shader, thread_groups, 1, 1);
GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
@ -130,8 +134,8 @@ void Manager::debug_bind()
void Manager::resource_bind()
{
GPU_storagebuf_bind(matrix_buf, DRW_OBJ_MAT_SLOT);
GPU_storagebuf_bind(infos_buf, DRW_OBJ_INFOS_SLOT);
GPU_storagebuf_bind(matrix_buf.current(), DRW_OBJ_MAT_SLOT);
GPU_storagebuf_bind(infos_buf.current(), DRW_OBJ_INFOS_SLOT);
GPU_storagebuf_bind(attributes_buf, DRW_OBJ_ATTR_SLOT);
GPU_uniformbuf_bind(layer_attributes_buf, DRW_LAYER_ATTR_UBO_SLOT);
/* 2 is the hardcoded location of the uniform attr UBO. */
@ -148,7 +152,7 @@ void Manager::submit(PassSimple &pass, View &view)
command::RecordingState state;
state.inverted_view = view.is_inverted();
pass.draw_commands_buf_.bind(state, pass.headers_, pass.commands_);
pass.draw_commands_buf_.bind(state, pass.headers_, pass.commands_, pass.sub_passes_);
resource_bind();
@ -166,7 +170,7 @@ void Manager::submit(PassMain &pass, View &view)
bool freeze_culling = (U.experimental.use_viewport_debug && DST.draw_ctx.v3d &&
(DST.draw_ctx.v3d->debug_flag & V3D_DEBUG_FREEZE_CULLING) != 0);
view.compute_visibility(bounds_buf, resource_len_, freeze_culling);
view.compute_visibility(bounds_buf.current(), resource_len_, freeze_culling);
command::RecordingState state;
state.inverted_view = view.is_inverted();
@ -198,7 +202,7 @@ void Manager::submit(PassSimple &pass)
command::RecordingState state;
pass.draw_commands_buf_.bind(state, pass.headers_, pass.commands_);
pass.draw_commands_buf_.bind(state, pass.headers_, pass.commands_, pass.sub_passes_);
resource_bind();
@ -239,14 +243,14 @@ Manager::SubmitDebugOutput Manager::submit_debug(PassMain &pass, View &view)
Manager::DataDebugOutput Manager::data_debug()
{
matrix_buf.read();
bounds_buf.read();
infos_buf.read();
matrix_buf.current().read();
bounds_buf.current().read();
infos_buf.current().read();
Manager::DataDebugOutput output;
output.matrices = {matrix_buf.data(), resource_len_};
output.bounds = {bounds_buf.data(), resource_len_};
output.infos = {infos_buf.data(), resource_len_};
output.matrices = {matrix_buf.current().data(), resource_len_};
output.bounds = {bounds_buf.current().data(), resource_len_};
output.infos = {infos_buf.current().data(), resource_len_};
return output;
}

View File

@ -72,9 +72,9 @@ class Manager {
* Buffers containing all object data. Referenced by resource index.
* Exposed as public members for shader access after sync.
*/
ObjectMatricesBuf matrix_buf;
ObjectBoundsBuf bounds_buf;
ObjectInfosBuf infos_buf;
SwapChain<ObjectMatricesBuf, 2> matrix_buf;
SwapChain<ObjectBoundsBuf, 2> bounds_buf;
SwapChain<ObjectInfosBuf, 2> infos_buf;
/**
* Object Attributes are reference by indirection data inside ObjectInfos.
@ -193,17 +193,17 @@ class Manager {
inline ResourceHandle Manager::resource_handle(const ObjectRef ref)
{
bool is_active_object = (ref.dupli_object ? ref.dupli_parent : ref.object) == object_active;
matrix_buf.get_or_resize(resource_len_).sync(*ref.object);
bounds_buf.get_or_resize(resource_len_).sync(*ref.object);
infos_buf.get_or_resize(resource_len_).sync(ref, is_active_object);
matrix_buf.current().get_or_resize(resource_len_).sync(*ref.object);
bounds_buf.current().get_or_resize(resource_len_).sync(*ref.object);
infos_buf.current().get_or_resize(resource_len_).sync(ref, is_active_object);
return ResourceHandle(resource_len_++, (ref.object->transflag & OB_NEG_SCALE) != 0);
}
inline ResourceHandle Manager::resource_handle(const float4x4 &model_matrix)
{
matrix_buf.get_or_resize(resource_len_).sync(model_matrix);
bounds_buf.get_or_resize(resource_len_).sync();
infos_buf.get_or_resize(resource_len_).sync();
matrix_buf.current().get_or_resize(resource_len_).sync(model_matrix);
bounds_buf.current().get_or_resize(resource_len_).sync();
infos_buf.current().get_or_resize(resource_len_).sync();
return ResourceHandle(resource_len_++, false);
}
@ -211,9 +211,9 @@ inline ResourceHandle Manager::resource_handle(const float4x4 &model_matrix,
const float3 &bounds_center,
const float3 &bounds_half_extent)
{
matrix_buf.get_or_resize(resource_len_).sync(model_matrix);
bounds_buf.get_or_resize(resource_len_).sync(bounds_center, bounds_half_extent);
infos_buf.get_or_resize(resource_len_).sync();
matrix_buf.current().get_or_resize(resource_len_).sync(model_matrix);
bounds_buf.current().get_or_resize(resource_len_).sync(bounds_center, bounds_half_extent);
infos_buf.current().get_or_resize(resource_len_).sync();
return ResourceHandle(resource_len_++, false);
}
@ -221,7 +221,7 @@ inline void Manager::extract_object_attributes(ResourceHandle handle,
const ObjectRef &ref,
Span<GPUMaterial *> materials)
{
ObjectInfos &infos = infos_buf.get_or_resize(handle.resource_index());
ObjectInfos &infos = infos_buf.current().get_or_resize(handle.resource_index());
infos.object_attrs_offset = attribute_len_;
/* Simple cache solution to avoid duplicates. */

View File

@ -55,12 +55,15 @@
#include "intern/gpu_codegen.h"
namespace blender::draw {
using namespace blender::draw;
using namespace blender::draw::command;
class Manager;
namespace command {
class DrawCommandBuf;
}
/* -------------------------------------------------------------------- */
/** \name Pass API
* \{ */
@ -112,6 +115,7 @@ template<
typename DrawCommandBufType>
class PassBase {
friend Manager;
friend DrawCommandBuf;
/** Will use texture own sampler state. */
static constexpr eGPUSamplerState sampler_auto = GPU_SAMPLER_MAX;