From: Evan Tang etang@codeweavers.com
--- libs/vkd3d-shader/spirv.c | 136 +++++++++++++++++++---- libs/vkd3d-shader/vkd3d_shader_private.h | 4 + 2 files changed, 119 insertions(+), 21 deletions(-)
diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index 7863b985d..07b00ee64 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -2337,7 +2337,6 @@ struct spirv_compiler size_t spec_constants_size; enum vkd3d_shader_compile_option_formatting_flags formatting; bool write_tess_geom_point_size; - bool wrap_main_in_interlock;
struct vkd3d_string_buffer_cache string_buffers;
@@ -4156,7 +4155,7 @@ static uint32_t spirv_compiler_emit_int_to_bool(struct spirv_compiler *compiler, uint32_t type_id; SpvOp op;
- assert(!(condition & ~(VKD3D_SHADER_CONDITIONAL_OP_NZ | VKD3D_SHADER_CONDITIONAL_OP_Z))); + assert(!(condition & ~(VKD3D_SHADER_CONDITIONAL_OP_NZ | VKD3D_SHADER_CONDITIONAL_OP_Z | VKD3DSI_ROV_MASK)));
type_id = vkd3d_spirv_get_type_id(builder, VKD3D_SHADER_COMPONENT_BOOL, component_count); op = condition & VKD3D_SHADER_CONDITIONAL_OP_Z ? SpvOpIEqual : SpvOpINotEqual; @@ -6009,7 +6008,6 @@ static void spirv_compiler_emit_resource_declaration(struct spirv_compiler *comp { /* Will be later converted to SampleInterlockEXT if the shader turns out to run per-sample */ vkd3d_spirv_enable_capability(builder, SpvCapabilityFragmentShaderPixelInterlockEXT); - compiler->wrap_main_in_interlock = true; } else { @@ -6522,9 +6520,6 @@ static void spirv_compiler_emit_shader_epilogue_invocation(struct spirv_compiler uint32_t arguments[MAX_REG_OUTPUT]; unsigned int i, count;
- if (compiler->wrap_main_in_interlock) - vkd3d_spirv_build_op(&builder->function_stream, SpvOpEndInvocationInterlockEXT); - if ((function_id = compiler->epilogue_function_id)) { void_id = vkd3d_spirv_get_op_type_void(builder); @@ -7335,6 +7330,9 @@ static void spirv_compiler_emit_return(struct spirv_compiler *compiler, { struct vkd3d_spirv_builder *builder = &compiler->spirv_builder;
+ if (instruction->flags & VKD3DSI_ROV_END) + vkd3d_spirv_build_op(&compiler->spirv_builder.function_stream, SpvOpEndInvocationInterlockEXT); + if (compiler->shader_type != VKD3D_SHADER_TYPE_GEOMETRY && (is_in_default_phase(compiler) || is_in_control_point_phase(compiler))) spirv_compiler_emit_shader_epilogue_invocation(compiler); @@ -9305,21 +9303,6 @@ static void spirv_compiler_emit_main_prolog(struct spirv_compiler *compiler) if (compiler->xfb_info && compiler->xfb_info->element_count && compiler->shader_type != VKD3D_SHADER_TYPE_GEOMETRY) spirv_compiler_emit_point_size(compiler); - - if (compiler->wrap_main_in_interlock) - { - if (compiler->shader_type == VKD3D_SHADER_TYPE_PIXEL) - { - assert(compiler->main_block_open); - /* Maybe in the future we can try to shrink the size of the interlocked section */ - vkd3d_spirv_build_op(&compiler->spirv_builder.function_stream, SpvOpBeginInvocationInterlockEXT); - } - else - { - ERR("ROV used in non-pixel shader"); - compiler->wrap_main_in_interlock = false; - } - } }
static bool is_dcl_instruction(enum vkd3d_shader_opcode handler_idx) @@ -9339,6 +9322,9 @@ static int spirv_compiler_handle_instruction(struct spirv_compiler *compiler, spirv_compiler_emit_main_prolog(compiler); }
+ if (instruction->flags & VKD3DSI_ROV_BEGIN) + vkd3d_spirv_build_op(&compiler->spirv_builder.function_stream, SpvOpBeginInvocationInterlockEXT); + switch (instruction->handler_idx) { case VKD3DSIH_DCL_GLOBAL_FLAGS: @@ -9667,6 +9653,10 @@ static int spirv_compiler_handle_instruction(struct spirv_compiler *compiler, FIXME("Unhandled instruction %#x.\n", instruction->handler_idx); }
+ /* RET and RETP have special handling since they need the end interlock *before* the ret */ + if (instruction->flags & VKD3DSI_ROV_END && instruction->handler_idx != VKD3DSIH_RET && instruction->handler_idx != VKD3DSIH_RETP) + vkd3d_spirv_build_op(&compiler->spirv_builder.function_stream, SpvOpEndInvocationInterlockEXT); + return ret; }
@@ -9736,6 +9726,107 @@ static void spirv_compiler_fix_up_fragment_shader_interlock(struct spirv_compile } }
+static bool spirv_compiler_is_rov_resource(struct spirv_compiler *compiler, const struct vkd3d_shader_register *reg) +{ + const struct vkd3d_symbol *rsrc; + const struct vkd3d_shader_descriptor_info1 *info; + if (reg->type != VKD3DSPR_UAV) + return false; + rsrc = spirv_compiler_find_resource(compiler, reg); + info = spirv_compiler_get_descriptor_info(compiler, VKD3D_SHADER_DESCRIPTOR_TYPE_UAV, &rsrc->info.resource.range); + return !!(info->flags & VKD3D_SHADER_DESCRIPTOR_INFO_FLAG_UAV_RASTERIZER_ORDERED); +} + +static void spirv_compiler_handle_rov(struct spirv_compiler *compiler, struct vkd3d_shader_instruction_array* instructions) +{ + struct vkd3d_shader_instruction *block_begin = NULL; + struct vkd3d_shader_instruction *rov_end = NULL; + unsigned int block_depth = 0; + unsigned int i; + bool rov_access_in_current_block = false; + bool is_in_rov = false; + + /* Begin and end need to be executed once for any run through the shader. + * It's possible to follow if/elses as long as we put one on both sides of the if/else, + * but we'd have to do fun things like make sure there actually is an else, so for now we'll + * force all control flow to happen outside of any loops or if/elses. */ + for (i = 0; i < instructions->count; i++) + { + struct vkd3d_shader_instruction *instruction = &instructions->elements[i]; + bool needs_begin = false; + switch (instruction->handler_idx) + { + case VKD3DSIH_IF: + case VKD3DSIH_LOOP: + case VKD3DSIH_SWITCH: + if (block_depth == 0) + block_begin = instruction; + block_depth++; + break; + + case VKD3DSIH_ENDIF: + case VKD3DSIH_ENDLOOP: + case VKD3DSIH_ENDSWITCH: + assert(block_depth > 0); + block_depth--; + if (block_depth == 0 && rov_access_in_current_block) + { + rov_end = instruction; + rov_access_in_current_block = false; + } + break; + + case VKD3DSIH_RET: + case VKD3DSIH_RETP: + /* Ret needs to end rov, and it can't do that if it hasn't started rov. + * We could begin and end right at the ret, but then we would need to verify that no later instruction + * places a begin before the ret after we process it (via block hoisting) */ + if (!is_in_rov) + needs_begin = true; + if (block_depth != 0) + instruction->flags |= VKD3DSI_ROV_END; + break; + + case VKD3DSIH_LD_UAV_TYPED: + case VKD3DSIH_LD_STRUCTURED: + case VKD3DSIH_LD_RAW: + needs_begin = spirv_compiler_is_rov_resource(compiler, &instruction->src[instruction->src_count - 1].reg); + break; + case VKD3DSIH_STORE_UAV_TYPED: + case VKD3DSIH_STORE_STRUCTURED: + case VKD3DSIH_STORE_RAW: + needs_begin = spirv_compiler_is_rov_resource(compiler, &instruction->dst[0].reg); + break; + + default: + break; + } + + if (needs_begin) + { + struct vkd3d_shader_instruction *rov_begin; + if (block_depth == 0) + { + rov_end = instruction; + rov_begin = instruction; + } + else + { + rov_access_in_current_block = true; + rov_begin = block_begin; + } + if (!is_in_rov) + { + is_in_rov = true; + rov_begin->flags |= VKD3DSI_ROV_BEGIN; + } + } + } + assert(block_depth == 0); + if (rov_end) + rov_end->flags |= VKD3DSI_ROV_END; +} + static int spirv_compiler_generate_spirv(struct spirv_compiler *compiler, const struct vkd3d_shader_compile_info *compile_info, struct vkd3d_shader_parser *parser, struct vkd3d_shader_code *spirv) @@ -9761,6 +9852,9 @@ static int spirv_compiler_generate_spirv(struct spirv_compiler *compiler, if ((result = vkd3d_shader_normalise(parser, compile_info)) < 0) return result;
+ if (vkd3d_spirv_capability_is_enabled(builder, SpvCapabilityFragmentShaderPixelInterlockEXT)) + spirv_compiler_handle_rov(compiler, &parser->instructions); + instructions = parser->instructions; memset(&parser->instructions, 0, sizeof(parser->instructions));
diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 603291808..207b241c4 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -714,6 +714,10 @@ enum vkd3d_tessellator_domain | VKD3DSI_PRECISE_Z | VKD3DSI_PRECISE_W) #define VKD3DSI_PRECISE_SHIFT 8
+#define VKD3DSI_ROV_BEGIN 0x1000 +#define VKD3DSI_ROV_END 0x2000 +#define VKD3DSI_ROV_MASK (VKD3DSI_ROV_BEGIN | VKD3DSI_ROV_END) + enum vkd3d_shader_rel_op { VKD3D_SHADER_REL_OP_GT = 1,