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:
committed by
Clément Foucault
parent
03f17c42d0
commit
59b9bb0849
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user