Metal: Add support for Workbench Shadows.

Implementing non-geometry-shader path for rendering stencil shadows,
used by the workbench engine.
Patch also contains a few small modifications to Create-info to ensure
usage of gl_FragDepth is explicitly specified.
This is required for testing of the patch.

Authored by Apple: Michael Parkin-White

Ref T96261

Reviewed By: fclem
Differential Revision: https://developer.blender.org/D16436
This commit is contained in:
Jason Fielder 2022-12-08 23:02:52 +01:00 committed by Clément Foucault
parent a83f2834c7
commit 9ec20f2ede
Notes: blender-bot 2023-02-14 11:21:40 +01:00
Referenced by issue #96261, Metal Viewport
10 changed files with 330 additions and 35 deletions

View File

@ -477,9 +477,11 @@ set(GLSL_SRC
engines/workbench/shaders/workbench_prepass_pointcloud_vert.glsl
engines/workbench/shaders/workbench_prepass_vert.glsl
engines/workbench/shaders/workbench_shadow_caps_geom.glsl
engines/workbench/shaders/workbench_shadow_caps_vert_no_geom.glsl
engines/workbench/shaders/workbench_shadow_debug_frag.glsl
engines/workbench/shaders/workbench_shadow_geom.glsl
engines/workbench/shaders/workbench_shadow_vert.glsl
engines/workbench/shaders/workbench_shadow_vert_no_geom.glsl
engines/workbench/shaders/workbench_transparent_accum_frag.glsl
engines/workbench/shaders/workbench_transparent_resolve_frag.glsl
engines/workbench/shaders/workbench_volume_frag.glsl

View File

@ -13,12 +13,17 @@ GPU_SHADER_INTERFACE_INFO(workbench_shadow_iface, "vData")
GPU_SHADER_CREATE_INFO(workbench_shadow_common)
.vertex_in(0, Type::VEC3, "pos")
.vertex_out(workbench_shadow_iface)
.push_constant(Type::FLOAT, "lightDistance")
.push_constant(Type::VEC3, "lightDirection")
.vertex_source("workbench_shadow_vert.glsl")
.additional_info("draw_mesh");
/* `workbench_shadow_vert.glsl` only used by geometry shader path.
* Vertex output iface not needed by non-geometry shader variants,
* as only gl_Position is returned. */
GPU_SHADER_CREATE_INFO(workbench_shadow_common_geom)
.vertex_out(workbench_shadow_iface)
.vertex_source("workbench_shadow_vert.glsl");
/** \} */
/* -------------------------------------------------------------------- */
@ -26,13 +31,25 @@ GPU_SHADER_CREATE_INFO(workbench_shadow_common)
* \{ */
GPU_SHADER_CREATE_INFO(workbench_shadow_manifold)
.additional_info("workbench_shadow_common_geom")
.geometry_layout(PrimitiveIn::LINES_ADJACENCY, PrimitiveOut::TRIANGLE_STRIP, 4, 1)
.geometry_source("workbench_shadow_geom.glsl");
GPU_SHADER_CREATE_INFO(workbench_shadow_no_manifold)
.additional_info("workbench_shadow_common_geom")
.geometry_layout(PrimitiveIn::LINES_ADJACENCY, PrimitiveOut::TRIANGLE_STRIP, 4, 2)
.geometry_source("workbench_shadow_geom.glsl");
GPU_SHADER_CREATE_INFO(workbench_shadow_manifold_no_geom)
.vertex_source("workbench_shadow_vert_no_geom.glsl")
/* Inject SSBO vertex fetch declaration using 2 output triangles. */
.define("VAR_MANIFOLD", "\n#pragma USE_SSBO_VERTEX_FETCH(TriangleList, 6)");
GPU_SHADER_CREATE_INFO(workbench_shadow_no_manifold_no_geom)
.vertex_source("workbench_shadow_vert_no_geom.glsl")
/* Inject SSBO vertex fetch declaration using 4 output triangles. */
.define("VAR_NO_MANIFOLD", "\n#pragma USE_SSBO_VERTEX_FETCH(TriangleList, 12)");
/** \} */
/* -------------------------------------------------------------------- */
@ -40,9 +57,13 @@ GPU_SHADER_CREATE_INFO(workbench_shadow_no_manifold)
* \{ */
GPU_SHADER_CREATE_INFO(workbench_shadow_caps)
.additional_info("workbench_shadow_common_geom")
.geometry_layout(PrimitiveIn::TRIANGLES, PrimitiveOut::TRIANGLE_STRIP, 3, 2)
.geometry_source("workbench_shadow_caps_geom.glsl");
GPU_SHADER_CREATE_INFO(workbench_shadow_caps_no_geom)
.vertex_source("workbench_shadow_caps_vert_no_geom.glsl");
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -0,0 +1,105 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma USE_SSBO_VERTEX_FETCH(TriangleList, 6)
#ifdef DOUBLE_MANIFOLD
# define vert_len 6 /* Triangle Strip with 6 verts = 4 triangles = 12 verts*/
# define emit_triangle_count 2
#else
# define vert_len 6 /* Triangle Strip with 6 verts = 4 triangles = 12 verts*/
# define emit_triangle_count 2
#endif
struct VertexData {
vec3 pos; /* local position */
vec4 frontPosition; /* final ndc position */
vec4 backPosition;
};
/* Input geometry triangle list. */
VertexData vData[3] = {};
#define DISCARD_VERTEX \
gl_Position = vec4(0.0); \
return;
vec4 get_pos(int v, bool backface)
{
return (backface) ? vData[v].backPosition : vData[v].frontPosition;
}
void emit_cap(const bool front, bool reversed, int triangle_vertex_id)
{
/* Inverse. */
ivec2 idx = (reversed) ? ivec2(2, 1) : ivec2(1, 2);
/* Output position depending on vertex ID. */
switch (triangle_vertex_id) {
case 0: {
gl_Position = (front) ? vData[0].frontPosition : vData[0].backPosition;
} break;
case 1: {
gl_Position = (front) ? vData[idx.x].frontPosition : vData[idx.y].backPosition;
} break;
case 2: {
gl_Position = (front) ? vData[idx.y].frontPosition : vData[idx.x].backPosition;
} break;
}
/* Apply depth bias. Prevents Z-fighting artifacts when fast-math is enabled. */
gl_Position.z += 0.00005;
}
void main()
{
/* Output Data indexing. */
int input_prim_index = int(gl_VertexID / 6);
int output_vertex_id = gl_VertexID % 6;
int output_triangle_id = output_vertex_id / 3;
/* Source primitive data location derived from output primitive. */
int input_base_vertex_id = input_prim_index * 3;
/* In data is triangles - Should be guaranteed. */
/* Read input position data. */
vData[0].pos = vertex_fetch_attribute(input_base_vertex_id + 0, pos, vec3);
vData[1].pos = vertex_fetch_attribute(input_base_vertex_id + 1, pos, vec3);
vData[2].pos = vertex_fetch_attribute(input_base_vertex_id + 2, pos, vec3);
/* Calculate front/back Positions. */
vData[0].frontPosition = point_object_to_ndc(vData[0].pos);
vData[0].backPosition = point_object_to_ndc(vData[0].pos + lightDirection * lightDistance);
vData[1].frontPosition = point_object_to_ndc(vData[1].pos);
vData[1].backPosition = point_object_to_ndc(vData[1].pos + lightDirection * lightDistance);
vData[2].frontPosition = point_object_to_ndc(vData[2].pos);
vData[2].backPosition = point_object_to_ndc(vData[2].pos + lightDirection * lightDistance);
/* Geometry shader equivalent calc. */
vec3 v10 = vData[0].pos - vData[1].pos;
vec3 v12 = vData[2].pos - vData[1].pos;
vec3 n = cross(v12, v10);
float facing = dot(n, lightDirection);
bool backface = facing > 0.0;
#ifdef DOUBLE_MANIFOLD
/* In case of non manifold geom, we only increase/decrease
* the stencil buffer by one but do every faces as they were facing the light. */
bool invert = backface;
const bool is_manifold = false;
#else
const bool invert = false;
const bool is_manifold = true;
#endif
if (!is_manifold || !backface) {
bool do_front = (output_triangle_id==0)?true:false;
emit_cap(do_front, invert, output_vertex_id%3);
} else {
DISCARD_VERTEX
}
}

View File

@ -0,0 +1,160 @@
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
/* Two variants, split pass, generates either 2 triangles or 6 triangles depending on input
* geometry manifold type */
#ifdef DOUBLE_MANIFOLD
# define vert_len 12 /* Triangle Strip with 6 verts = 4 triangles = 12 verts*/
# define emit_triangle_count 4
#else
# define vert_len 6 /* Triang;e Strip with 4 verts = 2 triangles = 6 verts*/
# define emit_triangle_count 2
#endif
#define DEGENERATE_TRIS_WORKAROUND
#define DEGENERATE_TRIS_AREA_THRESHOLD 4e-15
#define len_sqr(a) dot(a, a)
struct VertexData {
vec3 pos; /* local position */
vec4 frontPosition; /* final ndc position */
vec4 backPosition;
};
#define DISCARD_VERTEX \
gl_Position = vec4(0.0); \
return;
/* Extra positions for primary triangle */
VertexData vData[4];
void extrude_edge(bool invert, int output_vertex_id)
{
/* Reverse order if backfacing the light. */
ivec2 idx = (invert) ? ivec2(1, 2) : ivec2(2, 1);
/* Either outputs first or second quad, depending on double manifold status. */
int triangle_vertex_id = output_vertex_id % 6;
switch (triangle_vertex_id) {
case 0:
gl_Position = vData[idx.x].frontPosition;
break;
case 1:
case 4:
gl_Position = vData[idx.y].frontPosition;
break;
case 2:
case 3:
gl_Position = vData[idx.x].backPosition;
break;
case 5:
gl_Position = vData[idx.y].backPosition;
break;
}
/* Apply depth bias. Prevents Z-fighting artifacts when fast-math is enabled. */
gl_Position.z += 0.00005;
}
void main()
{
/* Output Data indexing. */
#ifdef DOUBLE_MANIFOLD
int input_prim_index = int(gl_VertexID / 12);
int output_vertex_id = gl_VertexID % 12;
int output_quad_id = (output_vertex_id >= 6) ? 1 : 0;
#else
int input_prim_index = int(gl_VertexID / 6);
int output_vertex_id = gl_VertexID % 6;
int output_quad_id = 0;
#endif
/* Source primitive data location derived from output primitive. */
int input_base_vertex_id = (input_prim_index * 4);
/* IN DATA is lines_adjacency - Should be guaranteed. */
/* Read input position data. */
vData[0].pos = vertex_fetch_attribute(input_base_vertex_id + 0, pos, vec3);
vData[1].pos = vertex_fetch_attribute(input_base_vertex_id + 1, pos, vec3);
vData[2].pos = vertex_fetch_attribute(input_base_vertex_id + 2, pos, vec3);
vData[3].pos = vertex_fetch_attribute(input_base_vertex_id + 3, pos, vec3);
/* Calculate front/back Positions. */
vData[0].frontPosition = point_object_to_ndc(vData[0].pos);
vData[0].backPosition = point_object_to_ndc(vData[0].pos + lightDirection * lightDistance);
vData[1].frontPosition = point_object_to_ndc(vData[1].pos);
vData[1].backPosition = point_object_to_ndc(vData[1].pos + lightDirection * lightDistance);
vData[2].frontPosition = point_object_to_ndc(vData[2].pos);
vData[2].backPosition = point_object_to_ndc(vData[2].pos + lightDirection * lightDistance);
vData[3].frontPosition = point_object_to_ndc(vData[3].pos);
vData[3].backPosition = point_object_to_ndc(vData[3].pos + lightDirection * lightDistance);
/* Geometry shader equivalent path. */
vec3 v10 = vData[0].pos - vData[1].pos;
vec3 v12 = vData[2].pos - vData[1].pos;
vec3 v13 = vData[3].pos - vData[1].pos;
vec3 n1 = cross(v12, v10);
vec3 n2 = cross(v13, v12);
#ifdef DEGENERATE_TRIS_WORKAROUND
/* Check if area is null. */
vec2 faces_area = vec2(len_sqr(n1), len_sqr(n2));
bvec2 degen_faces = lessThan(abs(faces_area), vec2(DEGENERATE_TRIS_AREA_THRESHOLD));
/* Both triangles are degenerate, abort. */
if (all(degen_faces)) {
DISCARD_VERTEX
}
#endif
vec2 facing = vec2(dot(n1, lightDirection), dot(n2, lightDirection));
/* WATCH: maybe unpredictable in some cases. */
bool is_manifold = any(notEqual(vData[0].pos, vData[3].pos));
bvec2 backface = greaterThan(facing, vec2(0.0));
#ifdef DEGENERATE_TRIS_WORKAROUND
# ifndef DOUBLE_MANIFOLD
/* If the mesh is known to be manifold and we don't use double count,
* only create an quad if the we encounter a facing geom. */
if ((degen_faces.x && backface.y) || (degen_faces.y && backface.x)) {
DISCARD_VERTEX
}
# endif
/* If one of the 2 triangles is degenerate, replace edge by a non-manifold one. */
backface.x = (degen_faces.x) ? !backface.y : backface.x;
backface.y = (degen_faces.y) ? !backface.x : backface.y;
is_manifold = (any(degen_faces)) ? false : is_manifold;
#endif
/* If both faces face the same direction it's not an outline edge. */
if (backface.x == backface.y) {
DISCARD_VERTEX
}
/* Output correct output vertex depending on */
if (output_vertex_id < 6) {
/* Always output if first 6 vertices. */
extrude_edge(backface.x, output_vertex_id);
}
else {
#ifdef DOUBLE_MANIFOLD
/* If part of double manifold, only output this vertex if correctly contributing */
if (is_manifold) {
extrude_edge(backface.x, output_vertex_id);
}
else {
DISCARD_VERTEX
}
#else
DISCARD_VERTEX
#endif
}
}

View File

@ -75,9 +75,9 @@ void ShaderCreateInfo::finalize()
if (info.early_fragment_test_) {
early_fragment_test_ = true;
}
/* Override depth-write with additional info if this specifies a writing mode
* other than the default. */
if (info.depth_write_ != DepthWrite::NONE) {
/* Modify depth write if has been changed from default.
* `UNCHANGED` implies gl_FragDepth is not used at all. */
if (info.depth_write_ != DepthWrite::UNCHANGED) {
depth_write_ = info.depth_write_;
}
@ -343,6 +343,13 @@ void gpu_shader_create_info_init()
overlay_motion_path_line = overlay_motion_path_line_no_geom;
overlay_motion_path_line_clipped = overlay_motion_path_line_clipped_no_geom;
/* Workbench shadows.
* Note: Updates additional-info used by workbench shadow permutations.
* Must be prepared prior to permutation preparation. */
workbench_shadow_manifold = workbench_shadow_manifold_no_geom;
workbench_shadow_no_manifold = workbench_shadow_no_manifold_no_geom;
workbench_shadow_caps = workbench_shadow_caps_no_geom;
/* Conservative rasterization. */
basic_depth_mesh_conservative = basic_depth_mesh_conservative_no_geom;
basic_depth_mesh_conservative_clipped = basic_depth_mesh_conservative_no_geom_clipped;

View File

@ -189,8 +189,8 @@ ENUM_OPERATORS(BuiltinBits, BuiltinBits::USE_DEBUG_PRINT);
* https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_conservative_depth.txt
*/
enum class DepthWrite {
/* NONE specified as default to indicate gl_FragDepth is not used. */
NONE = 0,
/* UNCHANGED specified as default to indicate gl_FragDepth is not used. */
UNCHANGED = 0,
ANY,
GREATER,
LESS,
@ -344,7 +344,7 @@ struct ShaderCreateInfo {
/** If true, force the use of the GL shader introspection for resource location. */
bool legacy_resource_location_ = false;
/** Allow optimization when fragment shader writes to `gl_FragDepth`. */
DepthWrite depth_write_ = DepthWrite::NONE;
DepthWrite depth_write_ = DepthWrite::UNCHANGED;
/** GPU Backend compatibility flag. Temporary requirement until Metal enablement is fully
* complete. */
bool metal_backend_only_ = false;

View File

@ -24,19 +24,19 @@ GPU_SHADER_CREATE_INFO(depth_2d_update_float)
.metal_backend_only(true)
.fragment_source("depth_2d_update_float_frag.glsl")
.additional_info("depth_2d_update_info_base")
// .do_static_compilation(true)
.do_static_compilation(true)
.depth_write(DepthWrite::ANY);
GPU_SHADER_CREATE_INFO(depth_2d_update_int24)
.metal_backend_only(true)
.fragment_source("depth_2d_update_int24_frag.glsl")
.additional_info("depth_2d_update_info_base")
// .do_static_compilation(true)
.do_static_compilation(true)
.depth_write(DepthWrite::ANY);
GPU_SHADER_CREATE_INFO(depth_2d_update_int32)
.metal_backend_only(true)
.fragment_source("depth_2d_update_int32_frag.glsl")
.additional_info("depth_2d_update_info_base")
// .do_static_compilation(true)
.do_static_compilation(true)
.depth_write(DepthWrite::ANY);

View File

@ -472,21 +472,6 @@ id<MTLRenderCommandEncoder> MTLBatch::bind(uint v_first, uint v_count, uint i_fi
this->elem ? @"(indexed)" : @"",
active_shader_->get_interface()->get_name()]];
}
/* Ensure Context Render Pipeline State is fully setup and ready to execute the draw. */
MTLPrimitiveType mtl_prim_type = gpu_prim_type_to_metal(this->prim_type);
if (!ctx->ensure_render_pipeline_state(mtl_prim_type)) {
printf("FAILED TO ENSURE RENDER PIPELINE STATE");
BLI_assert(false);
if (G.debug & G_DEBUG_GPU) {
[rec popDebugGroup];
}
return nil;
}
/*** Bind Vertex Buffers and Index Buffers **/
/* SSBO Vertex Fetch Buffer bindings. */
if (uses_ssbo_fetch) {
@ -514,7 +499,7 @@ id<MTLRenderCommandEncoder> MTLBatch::bind(uint v_first, uint v_count, uint i_fi
}
rps.bind_vertex_buffer(idx_buffer, 0, MTL_SSBO_VERTEX_FETCH_IBO_INDEX);
/* Ensure all attributes are set */
/* Ensure all attributes are set. */
active_shader_->ssbo_vertex_fetch_bind_attributes_end(rec);
/* Bind NULL Buffers for unused vertex data slots. */
@ -529,7 +514,7 @@ id<MTLRenderCommandEncoder> MTLBatch::bind(uint v_first, uint v_count, uint i_fi
/* Flag whether Indexed rendering is used or not. */
int &uniform_ssbo_use_indexed = active_shader_->uni_ssbo_uses_indexed_rendering;
BLI_assert(uniform_ssbo_use_indexed != -1);
int uses_indexed_rendering = (mtl_elem != NULL) ? 1 : 0;
int uses_indexed_rendering = (mtl_elem != nullptr) ? 1 : 0;
active_shader_->uniform_int(uniform_ssbo_use_indexed, 1, 1, &uses_indexed_rendering);
/* Set SSBO-fetch-mode status uniforms. */
@ -547,6 +532,19 @@ id<MTLRenderCommandEncoder> MTLBatch::bind(uint v_first, uint v_count, uint i_fi
(const int *)(&v_count));
}
/* Ensure Context Render Pipeline State is fully setup and ready to execute the draw.
* This should happen after all other final rendeirng setup is complete. */
MTLPrimitiveType mtl_prim_type = gpu_prim_type_to_metal(this->prim_type);
if (!ctx->ensure_render_pipeline_state(mtl_prim_type)) {
printf("FAILED TO ENSURE RENDER PIPELINE STATE");
BLI_assert(false);
if (G.debug & G_DEBUG_GPU) {
[rec popDebugGroup];
}
return nil;
}
/* Bind Vertex Buffers. */
for (int i = 0; i < num_buffers; i++) {
MTLVertBuf *buf_at_index = buffers[i];
@ -755,6 +753,10 @@ void MTLBatch::draw_advanced(int v_first, int v_count, int i_first, int i_count)
mtl_vertex_count_fits_primitive_type(
output_num_verts, active_shader_->get_ssbo_vertex_fetch_output_prim_type()),
"Output Vertex count is not compatible with the requested output vertex primitive type");
/* Set depth stencil state (requires knowledge of primitive type). */
ctx->ensure_depth_stencil_state(active_shader_->get_ssbo_vertex_fetch_output_prim_type());
[rec drawPrimitives:active_shader_->get_ssbo_vertex_fetch_output_prim_type()
vertexStart:0
vertexCount:output_num_verts

View File

@ -675,7 +675,7 @@ bool MTLShader::generate_msl_from_glsl(const shader::ShaderCreateInfo *info)
/* NOTE(Metal): FragDepth output mode specified in create-info 'DepthWrite depth_write_'.
* If parsing without create-info, manual extraction will be required. */
msl_iface.uses_gl_FragDepth = (info->depth_write_ != DepthWrite::NONE) &&
msl_iface.uses_gl_FragDepth = (info->depth_write_ != DepthWrite::UNCHANGED) &&
shd_builder_->glsl_fragment_source_.find("gl_FragDepth") !=
std::string::npos;
msl_iface.depth_write = info->depth_write_;

View File

@ -282,27 +282,25 @@ void MTLStateManager::set_stencil_test(const eGPUStencilTest test, const eGPUSte
context_, MTLStencilOperationKeep, MTLStencilOperationKeep, MTLStencilOperationReplace);
break;
case GPU_STENCIL_OP_COUNT_DEPTH_PASS:
/* Winding inversed due to flipped Y coordinate system in Metal. */
mtl_stencil_set_op_separate(context_,
GPU_CULL_FRONT,
GPU_CULL_BACK,
MTLStencilOperationKeep,
MTLStencilOperationKeep,
MTLStencilOperationIncrementWrap);
mtl_stencil_set_op_separate(context_,
GPU_CULL_BACK,
GPU_CULL_FRONT,
MTLStencilOperationKeep,
MTLStencilOperationKeep,
MTLStencilOperationDecrementWrap);
break;
case GPU_STENCIL_OP_COUNT_DEPTH_FAIL:
/* Winding inversed due to flipped Y coordinate system in Metal. */
mtl_stencil_set_op_separate(context_,
GPU_CULL_FRONT,
GPU_CULL_BACK,
MTLStencilOperationKeep,
MTLStencilOperationDecrementWrap,
MTLStencilOperationKeep);
mtl_stencil_set_op_separate(context_,
GPU_CULL_BACK,
GPU_CULL_FRONT,
MTLStencilOperationKeep,
MTLStencilOperationIncrementWrap,
MTLStencilOperationKeep);