Draw: Custom IDs

This pull request adds a new tipe of resource handles (thin handles).
These are intended for cases where a resource buffer with more than one
entry for each object is needed (for example, one entry per material
slot).
While it's already possible to have multiple regular handles for the
same object, they have a non-trivial overhead in terms of uploaded
data (matrix, bounds, object info) and computation (visibility
culling).
Thin handles store an indirection buffer pointing to their "parent"
regular handle, therefore multiple thin handles can share the same
per-object data and visibility culling computation.

Thin handles can only be used in their own Pass type (PassMainThin),
so passes that don't need them don't have to pay the overhead.

This pull request also includes the update of the Workbench Next
pre-pass to use PassMainThin, which is the main reason for the
implementation of this feature.

The main change from the previous PR is that the thin handles are now
stored directly in the main resource_id_buf, to avoid wasting an extra
 bind slot.

Pull Request #105261
This commit is contained in:
Miguel Pozo
2023-03-01 21:42:25 +01:00
committed by Clément Foucault
parent 03f17c42d0
commit 59b9bb0849
14 changed files with 107 additions and 42 deletions

View File

@@ -40,8 +40,7 @@ GPU_SHADER_CREATE_INFO(workbench_next_mesh)
.vertex_in(2, Type::VEC4, "ac")
.vertex_in(3, Type::VEC2, "au")
.vertex_source("workbench_prepass_vert.glsl")
.additional_info("draw_modelmat_new")
.additional_info("draw_resource_handle_new");
.additional_info("draw_modelmat_new_with_custom_id", "draw_resource_handle_new");
GPU_SHADER_CREATE_INFO(workbench_next_curves)
/* TODO Adding workbench_next_mesh to avoid shader compilation errors */

View File

@@ -16,8 +16,12 @@ void main()
normal_interp = normalize(normal_object_to_view(nor));
object_id = int(uint(resource_id) & 0xFFFFu) + 1;
#ifdef WORKBENCH_NEXT
workbench_material_data_get(
int(drw_CustomID), ac.rgb, color_interp, alpha_interp, _roughness, metallic);
#else
workbench_material_data_get(
resource_handle, ac.rgb, color_interp, alpha_interp, _roughness, metallic);
object_id = int(uint(resource_handle) & 0xFFFFu) + 1;
#endif
}

View File

@@ -68,6 +68,7 @@ class Instance {
resolution,
GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_ATTACHMENT |
GPU_TEXTURE_USAGE_MIP_SWIZZLE_VIEW);
resources.material_buf.clear();
opaque_ps.sync(scene_state, resources);
transparent_ps.sync(scene_state, resources);
@@ -202,12 +203,8 @@ class Instance {
if (batches[i] == nullptr) {
continue;
}
/* TODO(fclem): This create a cull-able instance for each sub-object. This is done
* for simplicity to reduce complexity. But this increase the overhead per object.
* Instead, we should use an indirection buffer to the material buffer. */
ResourceHandle _handle = i == 0 ? handle : manager.resource_handle(ob_ref);
Material &mat = resources.material_buf.get_or_resize(_handle.resource_index());
Material mat;
if (::Material *_mat = BKE_object_material_get_eval(ob_ref.object, i + 1)) {
mat = Material(*_mat);
@@ -225,7 +222,7 @@ class Instance {
get_material_image(ob_ref.object, i + 1, image, iuser, sampler_state);
}
draw_mesh(ob_ref, mat, batches[i], _handle, image, sampler_state, iuser);
draw_mesh(ob_ref, mat, batches[i], handle, image, sampler_state, iuser);
}
}
}
@@ -247,7 +244,7 @@ class Instance {
}
if (batch) {
Material &mat = resources.material_buf.get_or_resize(handle.resource_index());
Material mat;
if (object_state.color_type == V3D_SHADING_OBJECT_COLOR) {
mat = Material(*ob_ref.object);
@@ -291,9 +288,11 @@ class Instance {
ImageUser *iuser = nullptr)
{
const bool in_front = (ob_ref.object->dtx & OB_DRAW_IN_FRONT) != 0;
resources.material_buf.append(material);
int material_index = resources.material_buf.size() - 1;
auto draw = [&](MeshPass &pass) {
pass.draw(ob_ref, batch, handle, image, sampler_state, iuser);
pass.draw(ob_ref, batch, handle, material_index, image, sampler_state, iuser);
};
if (scene_state.xray_mode || material.is_transparent()) {

View File

@@ -18,6 +18,7 @@ bool MeshPass::is_empty() const
void MeshPass::init_pass(SceneResources &resources, DRWState state, int clip_planes)
{
use_custom_ids = true;
is_empty_ = true;
PassMain::init();
state_set(state, clip_planes);
@@ -57,6 +58,7 @@ void MeshPass::init_subpasses(ePipelineType pipeline,
void MeshPass::draw(ObjectRef &ref,
GPUBatch *batch,
ResourceHandle handle,
uint material_index,
::Image *image /* = nullptr */,
eGPUSamplerState sampler_state /* = GPU_SAMPLER_DEFAULT */,
ImageUser *iuser /* = nullptr */)
@@ -95,11 +97,11 @@ void MeshPass::draw(ObjectRef &ref,
};
texture_subpass_map_.lookup_or_add_cb(TextureSubPassKey(texture, geometry_type), add_cb)
->draw(batch, handle);
->draw(batch, handle, material_index);
return;
}
}
passes_[int(geometry_type)][int(eShaderType::MATERIAL)]->draw(batch, handle);
passes_[int(geometry_type)][int(eShaderType::MATERIAL)]->draw(batch, handle, material_index);
}
/** \} */

View File

@@ -184,6 +184,7 @@ class MeshPass : public PassMain {
void draw(ObjectRef &ref,
GPUBatch *batch,
ResourceHandle handle,
uint material_index,
::Image *image = nullptr,
eGPUSamplerState sampler_state = eGPUSamplerState::GPU_SAMPLER_DEFAULT,
ImageUser *iuser = nullptr);

View File

@@ -600,14 +600,13 @@ void DrawCommandBuf::bind(RecordingState &state,
}
void DrawMultiBuf::bind(RecordingState &state,
Vector<Header, 0> &headers,
Vector<Undetermined, 0> &commands,
Vector<Header, 0> & /*headers*/,
Vector<Undetermined, 0> & /*commands*/,
VisibilityBuf &visibility_buf,
int visibility_word_per_draw,
int view_len)
int view_len,
bool use_custom_ids)
{
UNUSED_VARS(headers, commands);
GPU_debug_group_begin("DrawMultiBuf.bind");
resource_id_count_ = 0u;
@@ -636,7 +635,7 @@ void DrawMultiBuf::bind(RecordingState &state,
group_buf_.push_update();
prototype_buf_.push_update();
/* Allocate enough for the expansion pass. */
resource_id_buf_.get_or_resize(resource_id_count_);
resource_id_buf_.get_or_resize(resource_id_count_ * (use_custom_ids ? 2 : 1));
/* Two command per group. */
command_buf_.get_or_resize(group_count_ * 2);
@@ -646,6 +645,7 @@ void DrawMultiBuf::bind(RecordingState &state,
GPU_shader_uniform_1i(shader, "prototype_len", prototype_count_);
GPU_shader_uniform_1i(shader, "visibility_word_per_draw", visibility_word_per_draw);
GPU_shader_uniform_1i(shader, "view_shift", log2_ceil_u(view_len));
GPU_shader_uniform_1b(shader, "use_custom_ids", use_custom_ids);
GPU_storagebuf_bind(group_buf_, GPU_shader_get_ssbo_binding(shader, "group_buf"));
GPU_storagebuf_bind(visibility_buf, GPU_shader_get_ssbo_binding(shader, "visibility_buf"));
GPU_storagebuf_bind(prototype_buf_, GPU_shader_get_ssbo_binding(shader, "prototype_buf"));

View File

@@ -433,7 +433,8 @@ class DrawCommandBuf {
uint instance_len,
uint vertex_len,
uint vertex_first,
ResourceHandle handle)
ResourceHandle handle,
uint /*custom_id*/)
{
vertex_first = vertex_first != -1 ? vertex_first : 0;
instance_len = instance_len != -1 ? instance_len : 1;
@@ -533,7 +534,8 @@ class DrawMultiBuf {
uint instance_len,
uint vertex_len,
uint vertex_first,
ResourceHandle handle)
ResourceHandle handle,
uint custom_id)
{
/* Custom draw-calls cannot be batched and will produce one group per draw. */
const bool custom_group = ((vertex_first != 0 && vertex_first != -1) || vertex_len != -1);
@@ -555,6 +557,7 @@ class DrawMultiBuf {
DrawPrototype &draw = prototype_buf_.get_or_resize(prototype_count_++);
draw.resource_handle = handle.raw;
draw.custom_id = custom_id;
draw.instance_len = instance_len;
draw.group_id = group_id;
@@ -594,7 +597,8 @@ class DrawMultiBuf {
Vector<Undetermined, 0> &commands,
VisibilityBuf &visibility_buf,
int visibility_word_per_draw,
int view_len);
int view_len,
bool use_custom_ids);
};
/** \} */

View File

@@ -72,9 +72,10 @@ struct DrawPrototype {
uint group_id;
/* Resource handle associated with this call. Also reference visibility. */
uint resource_handle;
/* Custom extra value to be used by the engines. */
uint custom_id;
/* Number of instances. */
uint instance_len;
uint _pad0;
};
BLI_STATIC_ASSERT_ALIGN(DrawPrototype, 16)

View File

@@ -186,7 +186,8 @@ void Manager::submit(PassMain &pass, View &view)
pass.commands_,
view.get_visibility_buffer(),
view.visibility_word_per_draw(),
view.view_len_);
view.view_len_,
pass.use_custom_ids);
resource_bind();

View File

@@ -135,6 +135,8 @@ class PassBase {
public:
const char *debug_name;
bool use_custom_ids;
PassBase(const char *name,
DrawCommandBufType &draw_command_buf,
SubPassVector<PassBase<DrawCommandBufType>> &sub_passes,
@@ -142,7 +144,8 @@ class PassBase {
: draw_commands_buf_(draw_command_buf),
sub_passes_(sub_passes),
shader_(shader),
debug_name(name){};
debug_name(name),
use_custom_ids(false){};
/**
* Reset the pass command pool.
@@ -224,13 +227,14 @@ class PassBase {
uint instance_len = -1,
uint vertex_len = -1,
uint vertex_first = -1,
ResourceHandle handle = {0});
ResourceHandle handle = {0},
uint custom_id = 0);
/**
* Shorter version for the common case.
* \note Implemented in derived class. Not a virtual function to avoid indirection.
*/
void draw(GPUBatch *batch, ResourceHandle handle);
void draw(GPUBatch *batch, ResourceHandle handle, uint custom_id = 0);
/**
* Record a procedural draw call. Geometry is **NOT** source from a GPUBatch.
@@ -240,7 +244,8 @@ class PassBase {
uint instance_len,
uint vertex_len,
uint vertex_first = -1,
ResourceHandle handle = {0});
ResourceHandle handle = {0},
uint custom_id = 0);
/**
* Indirect variants.
@@ -654,20 +659,25 @@ template<class T> std::string PassBase<T>::serialize(std::string line_prefix) co
* \{ */
template<class T>
inline void PassBase<T>::draw(
GPUBatch *batch, uint instance_len, uint vertex_len, uint vertex_first, ResourceHandle handle)
inline void PassBase<T>::draw(GPUBatch *batch,
uint instance_len,
uint vertex_len,
uint vertex_first,
ResourceHandle handle,
uint custom_id)
{
if (instance_len == 0 || vertex_len == 0) {
return;
}
BLI_assert(shader_);
draw_commands_buf_.append_draw(
headers_, commands_, batch, instance_len, vertex_len, vertex_first, handle);
headers_, commands_, batch, instance_len, vertex_len, vertex_first, handle, custom_id);
}
template<class T> inline void PassBase<T>::draw(GPUBatch *batch, ResourceHandle handle)
template<class T>
inline void PassBase<T>::draw(GPUBatch *batch, ResourceHandle handle, uint custom_id)
{
this->draw(batch, -1, -1, -1, handle);
this->draw(batch, -1, -1, -1, handle, custom_id);
}
template<class T>
@@ -675,9 +685,11 @@ inline void PassBase<T>::draw_procedural(GPUPrimType primitive,
uint instance_len,
uint vertex_len,
uint vertex_first,
ResourceHandle handle)
ResourceHandle handle,
uint custom_id)
{
this->draw(procedural_batch_get(primitive), instance_len, vertex_len, vertex_first, handle);
this->draw(
procedural_batch_get(primitive), instance_len, vertex_len, vertex_first, handle, custom_id);
}
/** \} */

View File

@@ -108,7 +108,14 @@ void main()
uint view_index = i * 32u;
while (word != 0u) {
if ((word & 1u) != 0u) {
resource_id_buf[dst_index++] = view_index | (resource_index << view_shift);
if (use_custom_ids) {
resource_id_buf[dst_index * 2] = view_index | (resource_index << view_shift);
resource_id_buf[dst_index * 2 + 1] = proto.custom_id;
}
else {
resource_id_buf[dst_index] = view_index | (resource_index << view_shift);
}
dst_index++;
}
view_index++;
word >>= 1u;
@@ -117,7 +124,13 @@ void main()
}
else {
for (uint i = dst_index; i < dst_index + visible_instance_len; i++) {
resource_id_buf[i] = resource_index;
if (use_custom_ids) {
resource_id_buf[i * 2] = resource_index;
resource_id_buf[i * 2 + 1] = proto.custom_id;
}
else {
resource_id_buf[i] = resource_index;
}
}
}
}

View File

@@ -189,6 +189,7 @@ GPU_SHADER_CREATE_INFO(draw_command_generate)
.push_constant(Type::INT, "prototype_len")
.push_constant(Type::INT, "visibility_word_per_draw")
.push_constant(Type::INT, "view_shift")
.push_constant(Type::BOOL, "use_custom_ids")
.compute_source("draw_command_generate_comp.glsl");
/** \} */
@@ -200,9 +201,18 @@ GPU_SHADER_CREATE_INFO(draw_command_generate)
GPU_SHADER_CREATE_INFO(draw_resource_id_new)
.define("UNIFORM_RESOURCE_ID_NEW")
/* TODO (Miguel Pozo): This is an int for compatibility.
* It should become uint once the "Next" ports are complete. */
.storage_buf(DRW_RESOURCE_ID_SLOT, Qualifier::READ, "int", "resource_id_buf[]")
.define("drw_ResourceID", "resource_id_buf[gpu_BaseInstance + gl_InstanceID]");
GPU_SHADER_CREATE_INFO(draw_resource_with_custom_id_new)
.define("UNIFORM_RESOURCE_ID_NEW")
.define("WITH_CUSTOM_IDS")
.storage_buf(DRW_RESOURCE_ID_SLOT, Qualifier::READ, "int2", "resource_id_buf[]")
.define("drw_ResourceID", "resource_id_buf[gpu_BaseInstance + gl_InstanceID].x")
.define("drw_CustomID", "resource_id_buf[gpu_BaseInstance + gl_InstanceID].y");
/**
* Workaround the lack of gl_BaseInstance by binding the resource_id_buf as vertex buf.
*/
@@ -210,6 +220,13 @@ GPU_SHADER_CREATE_INFO(draw_resource_id_fallback)
.define("UNIFORM_RESOURCE_ID_NEW")
.vertex_in(15, Type::INT, "drw_ResourceID");
GPU_SHADER_CREATE_INFO(draw_resource_with_custom_id_fallback)
.define("UNIFORM_RESOURCE_ID_NEW")
.define("WITH_CUSTOM_IDS")
.vertex_in(15, Type::IVEC2, "vertex_in_drw_ResourceID")
.define("drw_ResourceID", "vertex_in_drw_ResourceID.x")
.define("drw_CustomID", "vertex_in_drw_ResourceID.y");
/** TODO mask view id bits. */
GPU_SHADER_CREATE_INFO(draw_resource_handle_new).define("resource_handle", "drw_ResourceID");
@@ -219,14 +236,19 @@ GPU_SHADER_CREATE_INFO(draw_resource_handle_new).define("resource_handle", "drw_
/** \name Draw Object Resources
* \{ */
GPU_SHADER_CREATE_INFO(draw_modelmat_new)
GPU_SHADER_CREATE_INFO(draw_modelmat_new_common)
.typedef_source("draw_shader_shared.h")
.storage_buf(DRW_OBJ_MAT_SLOT, Qualifier::READ, "ObjectMatrices", "drw_matrix_buf[]")
.define("drw_ModelMatrixInverse", "drw_matrix_buf[resource_id].model_inverse")
.define("drw_ModelMatrix", "drw_matrix_buf[resource_id].model")
/* TODO For compatibility with old shaders. To be removed. */
.define("ModelMatrixInverse", "drw_ModelMatrixInverse")
.define("ModelMatrix", "drw_ModelMatrix")
.additional_info("draw_resource_id_new");
.define("ModelMatrix", "drw_ModelMatrix");
GPU_SHADER_CREATE_INFO(draw_modelmat_new)
.additional_info("draw_modelmat_new_common", "draw_resource_id_new");
GPU_SHADER_CREATE_INFO(draw_modelmat_new_with_custom_id)
.additional_info("draw_modelmat_new_common", "draw_resource_with_custom_id_new");
/** \} */

View File

@@ -317,6 +317,7 @@ void gpu_shader_create_info_init()
/* WORKAROUND: Replace the use of gpu_BaseInstance by an instance attribute. */
if (GPU_shader_draw_parameters_support() == false) {
draw_resource_id_new = draw_resource_id_fallback;
draw_resource_with_custom_id_new = draw_resource_with_custom_id_fallback;
}
#ifdef WITH_METAL_BACKEND

View File

@@ -121,12 +121,18 @@ void GLVertArray::update_bindings(const GLuint vao,
if (batch->resource_id_buf) {
const ShaderInput *input = interface->attr_get("drw_ResourceID");
int component_len = 1;
if (input == nullptr) {
/* Uses Custom IDs */
input = interface->attr_get("vertex_in_drw_ResourceID_");
component_len = 2;
}
if (input) {
dynamic_cast<GLStorageBuf *>(unwrap(batch->resource_id_buf))->bind_as(GL_ARRAY_BUFFER);
glEnableVertexAttribArray(input->location);
glVertexAttribDivisor(input->location, 1);
glVertexAttribIPointer(
input->location, 1, to_gl(GPU_COMP_I32), sizeof(uint32_t), (GLvoid *)nullptr);
input->location, component_len, to_gl(GPU_COMP_I32), 0, (GLvoid *)nullptr);
attr_mask &= ~(1 << input->location);
}
}