Metal: Re-enable workbench NEXT shadows

With the shift to GPU-driven rendering pipeline,
the SSBO vertex fetch paradigm used to
implement workbench shadows on Metal
instead of utilising the geometry shader
path no longer worked correctly.

This is because the draw submission
required vertex amplification up-front,
based on the expected output geometry
amount for a given input geometry.

This patch aims to resolve this
issue through addition of API to
enable the features within the
GPU driven pipeline.

Co-authored-by: Michael Parkin-White <mparkinwhite@apple.com>
Pull Request: https://projects.blender.org/blender/blender/pulls/113498
This commit is contained in:
Jason Fielder 2023-10-13 11:02:06 +02:00 committed by Jeroen Bakker
parent c7384ba6f3
commit 62219f8da9
13 changed files with 179 additions and 41 deletions

View File

@ -40,8 +40,7 @@ vec3 extrude_offset(vec3 ls_P)
float signed_distance = dot(pass_data.far_plane.xyz, ws_P) - pass_data.far_plane.w;
extrude_distance = -signed_distance / L_dot_FP;
}
vec3 ls_light_direction = normal_world_to_object(vec3(pass_data.light_direction_ws));
return ls_light_direction * extrude_distance;
return pass_data.light_direction_ws * extrude_distance;
}
void emit_cap(const bool front, bool reversed, int triangle_vertex_id)
@ -87,13 +86,16 @@ void main()
/* Calculate front/back Positions. */
vData[0].frontPosition = point_object_to_ndc(vData[0].pos);
vData[0].backPosition = point_object_to_ndc(vData[0].pos + extrude_offset(vData[0].pos));
vData[0].backPosition = point_world_to_ndc(point_object_to_world(vData[0].pos) +
extrude_offset(vData[0].pos));
vData[1].frontPosition = point_object_to_ndc(vData[1].pos);
vData[1].backPosition = point_object_to_ndc(vData[1].pos + extrude_offset(vData[1].pos));
vData[1].backPosition = point_world_to_ndc(point_object_to_world(vData[1].pos) +
extrude_offset(vData[1].pos));
vData[2].frontPosition = point_object_to_ndc(vData[2].pos);
vData[2].backPosition = point_object_to_ndc(vData[2].pos + extrude_offset(vData[2].pos));
vData[2].backPosition = point_world_to_ndc(point_object_to_world(vData[2].pos) +
extrude_offset(vData[2].pos));
/* Geometry shader equivalent calc. */
vec3 v10 = vData[0].pos - vData[1].pos;

View File

@ -71,8 +71,7 @@ vec3 extrude_offset(vec3 ls_P)
float signed_distance = dot(pass_data.far_plane.xyz, ws_P) - pass_data.far_plane.w;
extrude_distance = -signed_distance / L_dot_FP;
}
vec3 ls_light_direction = normal_world_to_object(vec3(pass_data.light_direction_ws));
return ls_light_direction * extrude_distance;
return pass_data.light_direction_ws * extrude_distance;
}
void main()
@ -100,16 +99,20 @@ void main()
/* Calculate front/back Positions. */
vData[0].frontPosition = point_object_to_ndc(vData[0].pos);
vData[0].backPosition = point_object_to_ndc(vData[0].pos + extrude_offset(vData[0].pos));
vData[0].backPosition = point_world_to_ndc(point_object_to_world(vData[0].pos) +
extrude_offset(vData[0].pos));
vData[1].frontPosition = point_object_to_ndc(vData[1].pos);
vData[1].backPosition = point_object_to_ndc(vData[1].pos + extrude_offset(vData[1].pos));
vData[1].backPosition = point_world_to_ndc(point_object_to_world(vData[1].pos) +
extrude_offset(vData[1].pos));
vData[2].frontPosition = point_object_to_ndc(vData[2].pos);
vData[2].backPosition = point_object_to_ndc(vData[2].pos + extrude_offset(vData[2].pos));
vData[2].backPosition = point_world_to_ndc(point_object_to_world(vData[2].pos) +
extrude_offset(vData[2].pos));
vData[3].frontPosition = point_object_to_ndc(vData[3].pos);
vData[3].backPosition = point_object_to_ndc(vData[3].pos + extrude_offset(vData[3].pos));
vData[3].backPosition = point_world_to_ndc(point_object_to_world(vData[3].pos) +
extrude_offset(vData[3].pos));
/* Geometry shader equivalent path. */
vec3 v10 = vData[0].pos - vData[1].pos;

View File

@ -564,6 +564,16 @@ void DrawCommandBuf::finalize_commands(Vector<Header, 0> &headers,
cmd.vertex_len = batch_vert_len;
}
#ifdef WITH_METAL_BACKEND
/* For SSBO vertex fetch, mutate output vertex count by ssbo vertex fetch expansion factor. */
if (cmd.shader) {
int num_input_primitives = gpu_get_prim_count_from_type(cmd.vertex_len,
cmd.batch->prim_type);
cmd.vertex_len = num_input_primitives *
GPU_shader_get_ssbo_vertex_fetch_num_verts_per_prim(cmd.shader);
}
#endif
if (cmd.handle.raw > 0) {
/* Save correct offset to start of resource_id buffer region for this draw. */
uint instance_first = resource_id_count;
@ -624,6 +634,20 @@ void DrawMultiBuf::bind(RecordingState &state,
group.vertex_first = group.vertex_first == -1 ? batch_vert_first : group.vertex_first;
group.base_index = batch_base_index;
#ifdef WITH_METAL_BACKEND
/* For SSBO vertex fetch, mutate output vertex count by ssbo vertex fetch expansion factor. */
if (group.gpu_shader) {
int num_input_primitives = gpu_get_prim_count_from_type(group.vertex_len,
group.gpu_batch->prim_type);
group.vertex_len = num_input_primitives *
GPU_shader_get_ssbo_vertex_fetch_num_verts_per_prim(group.gpu_shader);
/* Override base index to -1, as all SSBO calls are submitted as non-indexed, with the
* index buffer indirection handled within the implemnetation. This is to ensure
* command generation can correctly assigns baseInstance in the non-indexed formatting. */
group.base_index = -1;
}
#endif
/* Instancing attributes are not supported using the new pipeline since we use the base
* instance to set the correct resource_id. Workaround is a storage_buf + gl_InstanceID. */
BLI_assert(batch_inst_len == 1);

View File

@ -291,6 +291,11 @@ struct Draw {
uint vertex_len;
uint vertex_first;
ResourceHandle handle;
#ifdef WITH_METAL_BACKEND
/* Shader is required for extracting SSBO vertex fetch expansion parameters during draw command
* generation. */
GPUShader *shader;
#endif
void execute(RecordingState &state) const;
std::string serialize() const;
@ -399,7 +404,7 @@ union Undetermined {
};
/** Try to keep the command size as low as possible for performance. */
BLI_STATIC_ASSERT(sizeof(Undetermined) <= 24, "One of the command type is too large.")
BLI_STATIC_ASSERT(sizeof(Undetermined) <= /*24*/ 32, "One of the command type is too large.")
/** \} */
@ -435,14 +440,28 @@ class DrawCommandBuf {
uint vertex_len,
uint vertex_first,
ResourceHandle handle,
uint /*custom_id*/)
uint /*custom_id*/
#ifdef WITH_METAL_BACKEND
,
GPUShader *shader = nullptr
#endif
)
{
vertex_first = vertex_first != -1 ? vertex_first : 0;
instance_len = instance_len != -1 ? instance_len : 1;
int64_t index = commands.append_and_get_index({});
headers.append({Type::Draw, uint(index)});
commands[index].draw = {batch, instance_len, vertex_len, vertex_first, handle};
commands[index].draw = {batch,
instance_len,
vertex_len,
vertex_first,
handle
#ifdef WITH_METAL_BACKEND
,
shader
#endif
};
}
void bind(RecordingState &state,
@ -536,7 +555,12 @@ class DrawMultiBuf {
uint vertex_len,
uint vertex_first,
ResourceHandle handle,
uint custom_id)
uint custom_id
#ifdef WITH_METAL_BACKEND
,
GPUShader *shader
#endif
)
{
/* 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);
@ -575,6 +599,11 @@ class DrawMultiBuf {
group.back_proto_len = 0;
group.vertex_len = vertex_len;
group.vertex_first = vertex_first;
#ifdef WITH_METAL_BACKEND
/* If SSBO vertex fetch is used, shader must be known to extract vertex expansion parameters.
*/
group.gpu_shader = shader;
#endif
/* Custom group are not to be registered in the group_ids_. */
if (!custom_group) {
group_id = new_group_id;
@ -588,6 +617,11 @@ class DrawMultiBuf {
DrawGroup &group = group_buf_[group_id];
group.len += instance_len;
group.front_facing_len += inverted ? 0 : instance_len;
#ifdef WITH_METAL_BACKEND
/* If SSBO vertex fetch is used, shader must be known to extract vertex expansion parameters.
*/
group.gpu_shader = shader;
#endif
/* For serialization only. */
(inverted ? group.back_proto_len : group.front_proto_len)++;
}

View File

@ -42,6 +42,7 @@ struct DrawGroup {
uint total_counter;
#ifndef GPU_SHADER
/* NOTE: Union just to make sure the struct has always the same size on all platform. */
union {
struct {
@ -50,12 +51,18 @@ struct DrawGroup {
uint back_proto_len;
/** Needed to create the correct draw call. */
GPUBatch *gpu_batch;
# ifdef WITH_METAL_BACKEND
GPUShader *gpu_shader;
# endif
};
struct {
#endif
uint front_facing_counter;
uint back_facing_counter;
uint _pad0, _pad1;
#if defined(WITH_METAL_BACKEND) || defined(GPU_METAL)
uint _pad2, _pad3, _pad4, _pad5;
#endif
#ifndef GPU_SHADER
};
};

View File

@ -673,8 +673,24 @@ inline void PassBase<T>::draw(GPUBatch *batch,
return;
}
BLI_assert(shader_);
draw_commands_buf_.append_draw(
headers_, commands_, batch, instance_len, vertex_len, vertex_first, handle, custom_id);
#ifdef WITH_METAL_BACKEND
/* TEMP: Note, shader_ is passed as part of the draw as vertex-expansion properties for SSBO
* vertex fetch need extracting at command generation time. */
GPUShader *draw_shader = GPU_shader_uses_ssbo_vertex_fetch(shader_) ? shader_ : nullptr;
#endif
draw_commands_buf_.append_draw(headers_,
commands_,
batch,
instance_len,
vertex_len,
vertex_first,
handle,
custom_id
#ifdef WITH_METAL_BACKEND
,
draw_shader
#endif
);
}
template<class T>

View File

@ -218,6 +218,14 @@ GPUShader *GPU_shader_create_ex(const char *vertcode,
bool GPU_shader_transform_feedback_enable(GPUShader *shader, struct GPUVertBuf *vertbuf);
void GPU_shader_transform_feedback_disable(GPUShader *shader);
/**
* SSBO Vertex-fetch is used as an alternative path to geometry shaders wherein the vertex count is
* expanded up-front. This function fetches the number of specified output vertices per input
* primitive.
*/
int GPU_shader_get_ssbo_vertex_fetch_num_verts_per_prim(GPUShader *shader);
bool GPU_shader_uses_ssbo_vertex_fetch(GPUShader *shader);
/**
* Shader cache warming.
* For each shader, rendering APIs perform a two-step compilation:

View File

@ -636,6 +636,16 @@ int GPU_shader_get_program(GPUShader *shader)
return unwrap(shader)->program_handle_get();
}
int GPU_shader_get_ssbo_vertex_fetch_num_verts_per_prim(GPUShader *shader)
{
return unwrap(shader)->get_ssbo_vertex_fetch_output_num_verts();
}
bool GPU_shader_uses_ssbo_vertex_fetch(GPUShader *shader)
{
return unwrap(shader)->get_uses_ssbo_vertex_fetch();
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -79,6 +79,10 @@ class Shader {
/* DEPRECATED: Kept only because of BGL API. */
virtual int program_handle_get() const = 0;
/* Only used by SSBO Vertex fetch. */
virtual bool get_uses_ssbo_vertex_fetch() const = 0;
virtual int get_ssbo_vertex_fetch_output_num_verts() const = 0;
inline const char *const name_get() const
{
return name;

View File

@ -903,10 +903,30 @@ void MTLBatch::draw_advanced_indirect(GPUStorageBuf *indirect_buf, intptr_t offs
return;
}
/* Render using SSBO Vertex Fetch not supported by Draw Indirect.
* NOTE: Add support? */
if (active_shader_->get_uses_ssbo_vertex_fetch()) {
printf("Draw indirect for SSBO vertex fetch disabled\n");
/* Fetch indirect buffer Metal handle. */
MTLStorageBuf *mtlssbo = static_cast<MTLStorageBuf *>(unwrap(indirect_buf));
id<MTLBuffer> mtl_indirect_buf = mtlssbo->get_metal_buffer();
BLI_assert(mtl_indirect_buf != nil);
if (mtl_indirect_buf == nil) {
MTL_LOG_WARNING("Metal Indirect Draw Storage Buffer is nil.");
/* End of draw. */
this->unbind(rec);
return;
}
/* Indirect SSBO vertex fetch calls require the draw command in the buffer to be mutated at
* command encoding time. This takes place within the draw manager when a shader supporting
* SSBO Vertex-Fetch is used. */
if (active_shader_->get_uses_ssbo_vertex_fetch())
{ /* Set depth stencil state (requires knowledge of primitive type). */
ctx->ensure_depth_stencil_state(active_shader_->get_ssbo_vertex_fetch_output_prim_type());
/* Issue draw call. */
[rec drawPrimitives:active_shader_->get_ssbo_vertex_fetch_output_prim_type()
indirectBuffer:mtl_indirect_buf
indirectBufferOffset:offset];
ctx->main_command_buffer.register_draw_counters(1);
/* End of draw. */
this->unbind(rec);
@ -929,18 +949,6 @@ void MTLBatch::draw_advanced_indirect(GPUStorageBuf *indirect_buf, intptr_t offs
return;
}
/* Fetch indirect buffer Metal handle. */
MTLStorageBuf *mtlssbo = static_cast<MTLStorageBuf *>(unwrap(indirect_buf));
id<MTLBuffer> mtl_indirect_buf = mtlssbo->get_metal_buffer();
BLI_assert(mtl_indirect_buf != nil);
if (mtl_indirect_buf == nil) {
MTL_LOG_WARNING("Metal Indirect Draw Storage Buffer is nil.");
/* End of draw. */
this->unbind(rec);
return;
}
if (mtl_elem == NULL) {
/* Set depth stencil state (requires knowledge of primitive type). */
ctx->ensure_depth_stencil_state(mtl_prim_type);

View File

@ -307,24 +307,26 @@ class MTLShader : public Shader {
bool get_push_constant_is_dirty();
void push_constant_bindstate_mark_dirty(bool is_dirty);
/* SSBO vertex fetch draw parameters. */
bool get_uses_ssbo_vertex_fetch() const override
{
return use_ssbo_vertex_fetch_mode_;
}
int get_ssbo_vertex_fetch_output_num_verts() const override
{
return ssbo_vertex_fetch_output_num_verts_;
}
/* DEPRECATED: Kept only because of BGL API. (Returning -1 in METAL). */
int program_handle_get() const override
{
return -1;
}
bool get_uses_ssbo_vertex_fetch()
{
return use_ssbo_vertex_fetch_mode_;
}
MTLPrimitiveType get_ssbo_vertex_fetch_output_prim_type()
{
return ssbo_vertex_fetch_output_prim_type_;
}
uint32_t get_ssbo_vertex_fetch_output_num_verts()
{
return ssbo_vertex_fetch_output_num_verts_;
}
static int ssbo_vertex_type_to_attr_type(MTLVertexFormat attribute_type);
void prepare_ssbo_vertex_fetch_metadata();

View File

@ -69,6 +69,16 @@ class GLShader : public Shader {
void uniform_float(int location, int comp_len, int array_size, const float *data) override;
void uniform_int(int location, int comp_len, int array_size, const int *data) override;
/* Unusued: SSBO vertex fetch draw parameters. */
bool get_uses_ssbo_vertex_fetch() const override
{
return false;
}
int get_ssbo_vertex_fetch_output_num_verts() const override
{
return 0;
}
/** DEPRECATED: Kept only because of BGL API. */
int program_handle_get() const override;

View File

@ -60,6 +60,16 @@ class VKShader : public Shader {
std::string geometry_layout_declare(const shader::ShaderCreateInfo &info) const override;
std::string compute_layout_declare(const shader::ShaderCreateInfo &info) const override;
/* Unusued: SSBO vertex fetch draw parameters. */
bool get_uses_ssbo_vertex_fetch() const override
{
return false;
}
int get_ssbo_vertex_fetch_output_num_verts() const override
{
return 0;
}
/* DEPRECATED: Kept only because of BGL API. */
int program_handle_get() const override;