diff options
| author | 2021-05-21 02:12:32 -0300 | |
|---|---|---|
| committer | 2021-07-22 21:51:33 -0400 | |
| commit | 9e7b6622c25aa858b96bf0f1c7f94223a2f449a2 (patch) | |
| tree | 48c62889aeb79d6f0f01a467ba0d1ac01dec512b | |
| parent | emit_glasm_context_get_and_set.cpp: Add missing semicolons (diff) | |
| download | yuzu-9e7b6622c25aa858b96bf0f1c7f94223a2f449a2.tar.gz yuzu-9e7b6622c25aa858b96bf0f1c7f94223a2f449a2.tar.xz yuzu-9e7b6622c25aa858b96bf0f1c7f94223a2f449a2.zip | |
shader: Split profile and runtime information in separate structs
14 files changed, 300 insertions, 308 deletions
diff --git a/src/shader_recompiler/backend/glasm/emit_context.cpp b/src/shader_recompiler/backend/glasm/emit_context.cpp index e42f186c1..659ff6d17 100644 --- a/src/shader_recompiler/backend/glasm/emit_context.cpp +++ b/src/shader_recompiler/backend/glasm/emit_context.cpp | |||
| @@ -23,23 +23,25 @@ std::string_view InterpDecorator(Interpolation interp) { | |||
| 23 | } | 23 | } |
| 24 | } // Anonymous namespace | 24 | } // Anonymous namespace |
| 25 | 25 | ||
| 26 | EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_) | 26 | EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_, |
| 27 | : info{program.info}, profile{profile_} { | 27 | const RuntimeInfo& runtime_info_) |
| 28 | : profile{profile_}, runtime_info{runtime_info_} { | ||
| 28 | // FIXME: Temporary partial implementation | 29 | // FIXME: Temporary partial implementation |
| 30 | const auto& info{program.info}; | ||
| 29 | u32 cbuf_index{}; | 31 | u32 cbuf_index{}; |
| 30 | for (const auto& desc : program.info.constant_buffer_descriptors) { | 32 | for (const auto& desc : info.constant_buffer_descriptors) { |
| 31 | if (desc.count != 1) { | 33 | if (desc.count != 1) { |
| 32 | throw NotImplementedException("Constant buffer descriptor array"); | 34 | throw NotImplementedException("Constant buffer descriptor array"); |
| 33 | } | 35 | } |
| 34 | Add("CBUFFER c{}[]={{program.buffer[{}]}};", desc.index, cbuf_index); | 36 | Add("CBUFFER c{}[]={{program.buffer[{}]}};", desc.index, cbuf_index); |
| 35 | ++cbuf_index; | 37 | ++cbuf_index; |
| 36 | } | 38 | } |
| 37 | for (const auto& desc : program.info.storage_buffers_descriptors) { | 39 | for (const auto& desc : info.storage_buffers_descriptors) { |
| 38 | if (desc.count != 1) { | 40 | if (desc.count != 1) { |
| 39 | throw NotImplementedException("Storage buffer descriptor array"); | 41 | throw NotImplementedException("Storage buffer descriptor array"); |
| 40 | } | 42 | } |
| 41 | } | 43 | } |
| 42 | if (const size_t num = program.info.storage_buffers_descriptors.size(); num > 0) { | 44 | if (const size_t num = info.storage_buffers_descriptors.size(); num > 0) { |
| 43 | Add("PARAM c[{}]={{program.local[0..{}]}};", num, num - 1); | 45 | Add("PARAM c[{}]={{program.local[0..{}]}};", num, num - 1); |
| 44 | } | 46 | } |
| 45 | stage = program.stage; | 47 | stage = program.stage; |
| @@ -67,8 +69,8 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile | |||
| 67 | break; | 69 | break; |
| 68 | } | 70 | } |
| 69 | const std::string_view attr_stage{stage == Stage::Fragment ? "fragment" : "vertex"}; | 71 | const std::string_view attr_stage{stage == Stage::Fragment ? "fragment" : "vertex"}; |
| 70 | for (size_t index = 0; index < program.info.input_generics.size(); ++index) { | 72 | for (size_t index = 0; index < info.input_generics.size(); ++index) { |
| 71 | const auto& generic{program.info.input_generics[index]}; | 73 | const auto& generic{info.input_generics[index]}; |
| 72 | if (generic.used) { | 74 | if (generic.used) { |
| 73 | Add("{}ATTRIB in_attr{}[]={{{}.attrib[{}..{}]}};", | 75 | Add("{}ATTRIB in_attr{}[]={{{}.attrib[{}..{}]}};", |
| 74 | InterpDecorator(generic.interpolation), index, attr_stage, index, index); | 76 | InterpDecorator(generic.interpolation), index, attr_stage, index, index); |
| @@ -101,8 +103,8 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile | |||
| 101 | index, index); | 103 | index, index); |
| 102 | } | 104 | } |
| 103 | } | 105 | } |
| 104 | for (size_t index = 0; index < program.info.stores_frag_color.size(); ++index) { | 106 | for (size_t index = 0; index < info.stores_frag_color.size(); ++index) { |
| 105 | if (!program.info.stores_frag_color[index]) { | 107 | if (!info.stores_frag_color[index]) { |
| 106 | continue; | 108 | continue; |
| 107 | } | 109 | } |
| 108 | if (index == 0) { | 110 | if (index == 0) { |
| @@ -111,28 +113,28 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile | |||
| 111 | Add("OUTPUT frag_color{}=result.color[{}];", index, index); | 113 | Add("OUTPUT frag_color{}=result.color[{}];", index, index); |
| 112 | } | 114 | } |
| 113 | } | 115 | } |
| 114 | for (size_t index = 0; index < program.info.stores_generics.size(); ++index) { | 116 | for (size_t index = 0; index < info.stores_generics.size(); ++index) { |
| 115 | if (program.info.stores_generics[index]) { | 117 | if (info.stores_generics[index]) { |
| 116 | Add("OUTPUT out_attr{}[]={{result.attrib[{}..{}]}};", index, index, index); | 118 | Add("OUTPUT out_attr{}[]={{result.attrib[{}..{}]}};", index, index, index); |
| 117 | } | 119 | } |
| 118 | } | 120 | } |
| 119 | image_buffer_bindings.reserve(program.info.image_buffer_descriptors.size()); | 121 | image_buffer_bindings.reserve(info.image_buffer_descriptors.size()); |
| 120 | for (const auto& desc : program.info.image_buffer_descriptors) { | 122 | for (const auto& desc : info.image_buffer_descriptors) { |
| 121 | image_buffer_bindings.push_back(bindings.image); | 123 | image_buffer_bindings.push_back(bindings.image); |
| 122 | bindings.image += desc.count; | 124 | bindings.image += desc.count; |
| 123 | } | 125 | } |
| 124 | image_bindings.reserve(program.info.image_descriptors.size()); | 126 | image_bindings.reserve(info.image_descriptors.size()); |
| 125 | for (const auto& desc : program.info.image_descriptors) { | 127 | for (const auto& desc : info.image_descriptors) { |
| 126 | image_bindings.push_back(bindings.image); | 128 | image_bindings.push_back(bindings.image); |
| 127 | bindings.image += desc.count; | 129 | bindings.image += desc.count; |
| 128 | } | 130 | } |
| 129 | texture_buffer_bindings.reserve(program.info.texture_buffer_descriptors.size()); | 131 | texture_buffer_bindings.reserve(info.texture_buffer_descriptors.size()); |
| 130 | for (const auto& desc : program.info.texture_buffer_descriptors) { | 132 | for (const auto& desc : info.texture_buffer_descriptors) { |
| 131 | texture_buffer_bindings.push_back(bindings.texture); | 133 | texture_buffer_bindings.push_back(bindings.texture); |
| 132 | bindings.texture += desc.count; | 134 | bindings.texture += desc.count; |
| 133 | } | 135 | } |
| 134 | texture_bindings.reserve(program.info.texture_descriptors.size()); | 136 | texture_bindings.reserve(info.texture_descriptors.size()); |
| 135 | for (const auto& desc : program.info.texture_descriptors) { | 137 | for (const auto& desc : info.texture_descriptors) { |
| 136 | texture_bindings.push_back(bindings.texture); | 138 | texture_bindings.push_back(bindings.texture); |
| 137 | bindings.texture += desc.count; | 139 | bindings.texture += desc.count; |
| 138 | } | 140 | } |
diff --git a/src/shader_recompiler/backend/glasm/emit_context.h b/src/shader_recompiler/backend/glasm/emit_context.h index e76ed1d7c..1f057fdd5 100644 --- a/src/shader_recompiler/backend/glasm/emit_context.h +++ b/src/shader_recompiler/backend/glasm/emit_context.h | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | namespace Shader { | 16 | namespace Shader { |
| 17 | struct Info; | 17 | struct Info; |
| 18 | struct Profile; | 18 | struct Profile; |
| 19 | struct RuntimeInfo; | ||
| 19 | } // namespace Shader | 20 | } // namespace Shader |
| 20 | 21 | ||
| 21 | namespace Shader::Backend { | 22 | namespace Shader::Backend { |
| @@ -31,7 +32,8 @@ namespace Shader::Backend::GLASM { | |||
| 31 | 32 | ||
| 32 | class EmitContext { | 33 | class EmitContext { |
| 33 | public: | 34 | public: |
| 34 | explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_); | 35 | explicit EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_, |
| 36 | const RuntimeInfo& runtime_info_); | ||
| 35 | 37 | ||
| 36 | template <typename... Args> | 38 | template <typename... Args> |
| 37 | void Add(const char* format_str, IR::Inst& inst, Args&&... args) { | 39 | void Add(const char* format_str, IR::Inst& inst, Args&&... args) { |
| @@ -56,8 +58,8 @@ public: | |||
| 56 | 58 | ||
| 57 | std::string code; | 59 | std::string code; |
| 58 | RegAlloc reg_alloc{*this}; | 60 | RegAlloc reg_alloc{*this}; |
| 59 | const Info& info; | ||
| 60 | const Profile& profile; | 61 | const Profile& profile; |
| 62 | const RuntimeInfo& runtime_info; | ||
| 61 | 63 | ||
| 62 | std::vector<u32> texture_buffer_bindings; | 64 | std::vector<u32> texture_buffer_bindings; |
| 63 | std::vector<u32> image_buffer_bindings; | 65 | std::vector<u32> image_buffer_bindings; |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp index f110fd7f8..edff04a44 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp +++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp | |||
| @@ -374,8 +374,9 @@ std::string_view GetTessSpacing(TessSpacing spacing) { | |||
| 374 | } | 374 | } |
| 375 | } // Anonymous namespace | 375 | } // Anonymous namespace |
| 376 | 376 | ||
| 377 | std::string EmitGLASM(const Profile& profile, IR::Program& program, Bindings& bindings) { | 377 | std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program, |
| 378 | EmitContext ctx{program, bindings, profile}; | 378 | Bindings& bindings) { |
| 379 | EmitContext ctx{program, bindings, profile, runtime_info}; | ||
| 379 | Precolor(ctx, program); | 380 | Precolor(ctx, program); |
| 380 | EmitCode(ctx, program); | 381 | EmitCode(ctx, program); |
| 381 | std::string header{StageHeader(program.stage)}; | 382 | std::string header{StageHeader(program.stage)}; |
| @@ -385,18 +386,18 @@ std::string EmitGLASM(const Profile& profile, IR::Program& program, Bindings& bi | |||
| 385 | header += fmt::format("VERTICES_OUT {};", program.invocations); | 386 | header += fmt::format("VERTICES_OUT {};", program.invocations); |
| 386 | break; | 387 | break; |
| 387 | case Stage::TessellationEval: | 388 | case Stage::TessellationEval: |
| 388 | header += | 389 | header += fmt::format("TESS_MODE {};" |
| 389 | fmt::format("TESS_MODE {};" | 390 | "TESS_SPACING {};" |
| 390 | "TESS_SPACING {};" | 391 | "TESS_VERTEX_ORDER {};", |
| 391 | "TESS_VERTEX_ORDER {};", | 392 | GetTessMode(runtime_info.tess_primitive), |
| 392 | GetTessMode(profile.tess_primitive), GetTessSpacing(profile.tess_spacing), | 393 | GetTessSpacing(runtime_info.tess_spacing), |
| 393 | profile.tess_clockwise ? "CW" : "CCW"); | 394 | runtime_info.tess_clockwise ? "CW" : "CCW"); |
| 394 | break; | 395 | break; |
| 395 | case Stage::Geometry: | 396 | case Stage::Geometry: |
| 396 | header += fmt::format("PRIMITIVE_IN {};" | 397 | header += fmt::format("PRIMITIVE_IN {};" |
| 397 | "PRIMITIVE_OUT {};" | 398 | "PRIMITIVE_OUT {};" |
| 398 | "VERTICES_OUT {};", | 399 | "VERTICES_OUT {};", |
| 399 | InputPrimitive(profile.input_topology), | 400 | InputPrimitive(runtime_info.input_topology), |
| 400 | OutputPrimitive(program.output_topology), program.output_vertices); | 401 | OutputPrimitive(program.output_topology), program.output_vertices); |
| 401 | break; | 402 | break; |
| 402 | case Stage::Compute: | 403 | case Stage::Compute: |
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.h b/src/shader_recompiler/backend/glasm/emit_glasm.h index a0dfdd818..3d02d873e 100644 --- a/src/shader_recompiler/backend/glasm/emit_glasm.h +++ b/src/shader_recompiler/backend/glasm/emit_glasm.h | |||
| @@ -12,12 +12,12 @@ | |||
| 12 | 12 | ||
| 13 | namespace Shader::Backend::GLASM { | 13 | namespace Shader::Backend::GLASM { |
| 14 | 14 | ||
| 15 | [[nodiscard]] std::string EmitGLASM(const Profile& profile, IR::Program& program, | 15 | [[nodiscard]] std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, |
| 16 | Bindings& binding); | 16 | IR::Program& program, Bindings& bindings); |
| 17 | 17 | ||
| 18 | [[nodiscard]] inline std::string EmitGLASM(const Profile& profile, IR::Program& program) { | 18 | [[nodiscard]] inline std::string EmitGLASM(const Profile& profile, IR::Program& program) { |
| 19 | Bindings binding; | 19 | Bindings binding; |
| 20 | return EmitGLASM(profile, program, binding); | 20 | return EmitGLASM(profile, {}, program, binding); |
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | } // namespace Shader::Backend::GLASM | 23 | } // namespace Shader::Backend::GLASM |
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp index a98e08392..3e8899f53 100644 --- a/src/shader_recompiler/backend/spirv/emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/emit_context.cpp | |||
| @@ -136,7 +136,7 @@ Id DefineInput(EmitContext& ctx, Id type, bool per_invocation, | |||
| 136 | break; | 136 | break; |
| 137 | case Stage::Geometry: | 137 | case Stage::Geometry: |
| 138 | if (per_invocation) { | 138 | if (per_invocation) { |
| 139 | const u32 num_vertices{NumVertices(ctx.profile.input_topology)}; | 139 | const u32 num_vertices{NumVertices(ctx.runtime_info.input_topology)}; |
| 140 | type = ctx.TypeArray(type, ctx.Const(num_vertices)); | 140 | type = ctx.TypeArray(type, ctx.Const(num_vertices)); |
| 141 | } | 141 | } |
| 142 | break; | 142 | break; |
| @@ -161,8 +161,8 @@ void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional<u32> invo | |||
| 161 | while (element < 4) { | 161 | while (element < 4) { |
| 162 | const u32 remainder{4 - element}; | 162 | const u32 remainder{4 - element}; |
| 163 | const TransformFeedbackVarying* xfb_varying{}; | 163 | const TransformFeedbackVarying* xfb_varying{}; |
| 164 | if (!ctx.profile.xfb_varyings.empty()) { | 164 | if (!ctx.runtime_info.xfb_varyings.empty()) { |
| 165 | xfb_varying = &ctx.profile.xfb_varyings[base_attr_index + element]; | 165 | xfb_varying = &ctx.runtime_info.xfb_varyings[base_attr_index + element]; |
| 166 | xfb_varying = xfb_varying && xfb_varying->components > 0 ? xfb_varying : nullptr; | 166 | xfb_varying = xfb_varying && xfb_varying->components > 0 ? xfb_varying : nullptr; |
| 167 | } | 167 | } |
| 168 | const u32 num_components{xfb_varying ? xfb_varying->components : remainder}; | 168 | const u32 num_components{xfb_varying ? xfb_varying->components : remainder}; |
| @@ -208,7 +208,7 @@ Id GetAttributeType(EmitContext& ctx, AttributeType type) { | |||
| 208 | } | 208 | } |
| 209 | 209 | ||
| 210 | std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) { | 210 | std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) { |
| 211 | const AttributeType type{ctx.profile.generic_input_types.at(index)}; | 211 | const AttributeType type{ctx.runtime_info.generic_input_types.at(index)}; |
| 212 | switch (type) { | 212 | switch (type) { |
| 213 | case AttributeType::Float: | 213 | case AttributeType::Float: |
| 214 | return AttrInfo{ctx.input_f32, ctx.F32[1], false}; | 214 | return AttrInfo{ctx.input_f32, ctx.F32[1], false}; |
| @@ -441,13 +441,15 @@ void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_vie | |||
| 441 | } | 441 | } |
| 442 | } | 442 | } |
| 443 | 443 | ||
| 444 | EmitContext::EmitContext(const Profile& profile_, IR::Program& program, Bindings& binding) | 444 | EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_info_, |
| 445 | : Sirit::Module(profile_.supported_spirv), profile{profile_}, stage{program.stage} { | 445 | IR::Program& program, Bindings& bindings) |
| 446 | : Sirit::Module(profile_.supported_spirv), profile{profile_}, | ||
| 447 | runtime_info{runtime_info_}, stage{program.stage} { | ||
| 446 | const bool is_unified{profile.unified_descriptor_binding}; | 448 | const bool is_unified{profile.unified_descriptor_binding}; |
| 447 | u32& uniform_binding{is_unified ? binding.unified : binding.uniform_buffer}; | 449 | u32& uniform_binding{is_unified ? bindings.unified : bindings.uniform_buffer}; |
| 448 | u32& storage_binding{is_unified ? binding.unified : binding.storage_buffer}; | 450 | u32& storage_binding{is_unified ? bindings.unified : bindings.storage_buffer}; |
| 449 | u32& texture_binding{is_unified ? binding.unified : binding.texture}; | 451 | u32& texture_binding{is_unified ? bindings.unified : bindings.texture}; |
| 450 | u32& image_binding{is_unified ? binding.unified : binding.image}; | 452 | u32& image_binding{is_unified ? bindings.unified : bindings.image}; |
| 451 | AddCapability(spv::Capability::Shader); | 453 | AddCapability(spv::Capability::Shader); |
| 452 | DefineCommonTypes(program.info); | 454 | DefineCommonTypes(program.info); |
| 453 | DefineCommonConstants(); | 455 | DefineCommonConstants(); |
| @@ -1211,7 +1213,7 @@ void EmitContext::DefineInputs(const Info& info) { | |||
| 1211 | if (!generic.used) { | 1213 | if (!generic.used) { |
| 1212 | continue; | 1214 | continue; |
| 1213 | } | 1215 | } |
| 1214 | const AttributeType input_type{profile.generic_input_types[index]}; | 1216 | const AttributeType input_type{runtime_info.generic_input_types[index]}; |
| 1215 | if (input_type == AttributeType::Disabled) { | 1217 | if (input_type == AttributeType::Disabled) { |
| 1216 | continue; | 1218 | continue; |
| 1217 | } | 1219 | } |
| @@ -1256,7 +1258,7 @@ void EmitContext::DefineOutputs(const IR::Program& program) { | |||
| 1256 | if (info.stores_position || stage == Stage::VertexB) { | 1258 | if (info.stores_position || stage == Stage::VertexB) { |
| 1257 | output_position = DefineOutput(*this, F32[4], invocations, spv::BuiltIn::Position); | 1259 | output_position = DefineOutput(*this, F32[4], invocations, spv::BuiltIn::Position); |
| 1258 | } | 1260 | } |
| 1259 | if (info.stores_point_size || profile.fixed_state_point_size) { | 1261 | if (info.stores_point_size || runtime_info.fixed_state_point_size) { |
| 1260 | if (stage == Stage::Fragment) { | 1262 | if (stage == Stage::Fragment) { |
| 1261 | throw NotImplementedException("Storing PointSize in fragment stage"); | 1263 | throw NotImplementedException("Storing PointSize in fragment stage"); |
| 1262 | } | 1264 | } |
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h index d2b79f6c1..961c9180c 100644 --- a/src/shader_recompiler/backend/spirv/emit_context.h +++ b/src/shader_recompiler/backend/spirv/emit_context.h | |||
| @@ -103,7 +103,8 @@ struct GenericElementInfo { | |||
| 103 | 103 | ||
| 104 | class EmitContext final : public Sirit::Module { | 104 | class EmitContext final : public Sirit::Module { |
| 105 | public: | 105 | public: |
| 106 | explicit EmitContext(const Profile& profile, IR::Program& program, Bindings& binding); | 106 | explicit EmitContext(const Profile& profile, const RuntimeInfo& runtime_info, |
| 107 | IR::Program& program, Bindings& binding); | ||
| 107 | ~EmitContext(); | 108 | ~EmitContext(); |
| 108 | 109 | ||
| 109 | [[nodiscard]] Id Def(const IR::Value& value); | 110 | [[nodiscard]] Id Def(const IR::Value& value); |
| @@ -150,6 +151,7 @@ public: | |||
| 150 | } | 151 | } |
| 151 | 152 | ||
| 152 | const Profile& profile; | 153 | const Profile& profile; |
| 154 | const RuntimeInfo& runtime_info; | ||
| 153 | Stage stage{}; | 155 | Stage stage{}; |
| 154 | 156 | ||
| 155 | Id void_id{}; | 157 | Id void_id{}; |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index 3e20ac3b9..cba420cda 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp | |||
| @@ -226,16 +226,17 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { | |||
| 226 | case Stage::TessellationEval: | 226 | case Stage::TessellationEval: |
| 227 | execution_model = spv::ExecutionModel::TessellationEvaluation; | 227 | execution_model = spv::ExecutionModel::TessellationEvaluation; |
| 228 | ctx.AddCapability(spv::Capability::Tessellation); | 228 | ctx.AddCapability(spv::Capability::Tessellation); |
| 229 | ctx.AddExecutionMode(main, ExecutionMode(ctx.profile.tess_primitive)); | 229 | ctx.AddExecutionMode(main, ExecutionMode(ctx.runtime_info.tess_primitive)); |
| 230 | ctx.AddExecutionMode(main, ExecutionMode(ctx.profile.tess_spacing)); | 230 | ctx.AddExecutionMode(main, ExecutionMode(ctx.runtime_info.tess_spacing)); |
| 231 | ctx.AddExecutionMode(main, ctx.profile.tess_clockwise ? spv::ExecutionMode::VertexOrderCw | 231 | ctx.AddExecutionMode(main, ctx.runtime_info.tess_clockwise |
| 232 | : spv::ExecutionMode::VertexOrderCcw); | 232 | ? spv::ExecutionMode::VertexOrderCw |
| 233 | : spv::ExecutionMode::VertexOrderCcw); | ||
| 233 | break; | 234 | break; |
| 234 | case Stage::Geometry: | 235 | case Stage::Geometry: |
| 235 | execution_model = spv::ExecutionModel::Geometry; | 236 | execution_model = spv::ExecutionModel::Geometry; |
| 236 | ctx.AddCapability(spv::Capability::Geometry); | 237 | ctx.AddCapability(spv::Capability::Geometry); |
| 237 | ctx.AddCapability(spv::Capability::GeometryStreams); | 238 | ctx.AddCapability(spv::Capability::GeometryStreams); |
| 238 | switch (ctx.profile.input_topology) { | 239 | switch (ctx.runtime_info.input_topology) { |
| 239 | case InputTopology::Points: | 240 | case InputTopology::Points: |
| 240 | ctx.AddExecutionMode(main, spv::ExecutionMode::InputPoints); | 241 | ctx.AddExecutionMode(main, spv::ExecutionMode::InputPoints); |
| 241 | break; | 242 | break; |
| @@ -279,7 +280,7 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) { | |||
| 279 | if (program.info.stores_frag_depth) { | 280 | if (program.info.stores_frag_depth) { |
| 280 | ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing); | 281 | ctx.AddExecutionMode(main, spv::ExecutionMode::DepthReplacing); |
| 281 | } | 282 | } |
| 282 | if (ctx.profile.force_early_z) { | 283 | if (ctx.runtime_info.force_early_z) { |
| 283 | ctx.AddExecutionMode(main, spv::ExecutionMode::EarlyFragmentTests); | 284 | ctx.AddExecutionMode(main, spv::ExecutionMode::EarlyFragmentTests); |
| 284 | } | 285 | } |
| 285 | break; | 286 | break; |
| @@ -402,7 +403,7 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct | |||
| 402 | if (info.uses_sample_id) { | 403 | if (info.uses_sample_id) { |
| 403 | ctx.AddCapability(spv::Capability::SampleRateShading); | 404 | ctx.AddCapability(spv::Capability::SampleRateShading); |
| 404 | } | 405 | } |
| 405 | if (!ctx.profile.xfb_varyings.empty()) { | 406 | if (!ctx.runtime_info.xfb_varyings.empty()) { |
| 406 | ctx.AddCapability(spv::Capability::TransformFeedback); | 407 | ctx.AddCapability(spv::Capability::TransformFeedback); |
| 407 | } | 408 | } |
| 408 | if (info.uses_derivatives) { | 409 | if (info.uses_derivatives) { |
| @@ -433,8 +434,9 @@ void PatchPhiNodes(IR::Program& program, EmitContext& ctx) { | |||
| 433 | } | 434 | } |
| 434 | } // Anonymous namespace | 435 | } // Anonymous namespace |
| 435 | 436 | ||
| 436 | std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program, Bindings& binding) { | 437 | std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, |
| 437 | EmitContext ctx{profile, program, binding}; | 438 | IR::Program& program, Bindings& bindings) { |
| 439 | EmitContext ctx{profile, runtime_info, program, bindings}; | ||
| 438 | const Id main{DefineMain(ctx, program)}; | 440 | const Id main{DefineMain(ctx, program)}; |
| 439 | DefineEntryPoint(program, ctx, main); | 441 | DefineEntryPoint(program, ctx, main); |
| 440 | if (profile.support_float_controls) { | 442 | if (profile.support_float_controls) { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h index d8ab2d8ed..db0c935fe 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv.h | |||
| @@ -16,12 +16,12 @@ | |||
| 16 | 16 | ||
| 17 | namespace Shader::Backend::SPIRV { | 17 | namespace Shader::Backend::SPIRV { |
| 18 | 18 | ||
| 19 | [[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program, | 19 | [[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, |
| 20 | Bindings& binding); | 20 | IR::Program& program, Bindings& bindings); |
| 21 | 21 | ||
| 22 | [[nodiscard]] inline std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program) { | 22 | [[nodiscard]] inline std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program) { |
| 23 | Bindings binding; | 23 | Bindings binding; |
| 24 | return EmitSPIRV(profile, program, binding); | 24 | return EmitSPIRV(profile, {}, program, binding); |
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | } // namespace Shader::Backend::SPIRV | 27 | } // namespace Shader::Backend::SPIRV |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp index 8e57ff070..c1b69c234 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp | |||
| @@ -17,7 +17,7 @@ struct AttrInfo { | |||
| 17 | }; | 17 | }; |
| 18 | 18 | ||
| 19 | std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) { | 19 | std::optional<AttrInfo> AttrTypes(EmitContext& ctx, u32 index) { |
| 20 | const AttributeType type{ctx.profile.generic_input_types.at(index)}; | 20 | const AttributeType type{ctx.runtime_info.generic_input_types.at(index)}; |
| 21 | switch (type) { | 21 | switch (type) { |
| 22 | case AttributeType::Float: | 22 | case AttributeType::Float: |
| 23 | return AttrInfo{ctx.input_f32, ctx.F32[1], false}; | 23 | return AttrInfo{ctx.input_f32, ctx.F32[1], false}; |
| @@ -468,7 +468,7 @@ Id EmitIsHelperInvocation(EmitContext& ctx) { | |||
| 468 | } | 468 | } |
| 469 | 469 | ||
| 470 | Id EmitYDirection(EmitContext& ctx) { | 470 | Id EmitYDirection(EmitContext& ctx) { |
| 471 | return ctx.Const(ctx.profile.y_negate ? -1.0f : 1.0f); | 471 | return ctx.Const(ctx.runtime_info.y_negate ? -1.0f : 1.0f); |
| 472 | } | 472 | } |
| 473 | 473 | ||
| 474 | Id EmitLoadLocal(EmitContext& ctx, Id word_offset) { | 474 | Id EmitLoadLocal(EmitContext& ctx, Id word_offset) { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp index ba948f3c9..072a3b1bd 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp | |||
| @@ -18,8 +18,8 @@ void ConvertDepthMode(EmitContext& ctx) { | |||
| 18 | } | 18 | } |
| 19 | 19 | ||
| 20 | void SetFixedPipelinePointSize(EmitContext& ctx) { | 20 | void SetFixedPipelinePointSize(EmitContext& ctx) { |
| 21 | if (ctx.profile.fixed_state_point_size) { | 21 | if (ctx.runtime_info.fixed_state_point_size) { |
| 22 | const float point_size{*ctx.profile.fixed_state_point_size}; | 22 | const float point_size{*ctx.runtime_info.fixed_state_point_size}; |
| 23 | ctx.OpStore(ctx.output_point_size, ctx.Const(point_size)); | 23 | ctx.OpStore(ctx.output_point_size, ctx.Const(point_size)); |
| 24 | } | 24 | } |
| 25 | } | 25 | } |
| @@ -62,7 +62,10 @@ Id ComparisonFunction(EmitContext& ctx, CompareFunction comparison, Id operand_1 | |||
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | void AlphaTest(EmitContext& ctx) { | 64 | void AlphaTest(EmitContext& ctx) { |
| 65 | const auto comparison{*ctx.profile.alpha_test_func}; | 65 | if (!ctx.runtime_info.alpha_test_func) { |
| 66 | return; | ||
| 67 | } | ||
| 68 | const auto comparison{*ctx.runtime_info.alpha_test_func}; | ||
| 66 | if (comparison == CompareFunction::Always) { | 69 | if (comparison == CompareFunction::Always) { |
| 67 | return; | 70 | return; |
| 68 | } | 71 | } |
| @@ -76,7 +79,7 @@ void AlphaTest(EmitContext& ctx) { | |||
| 76 | 79 | ||
| 77 | const Id true_label{ctx.OpLabel()}; | 80 | const Id true_label{ctx.OpLabel()}; |
| 78 | const Id discard_label{ctx.OpLabel()}; | 81 | const Id discard_label{ctx.OpLabel()}; |
| 79 | const Id alpha_reference{ctx.Const(ctx.profile.alpha_test_reference)}; | 82 | const Id alpha_reference{ctx.Const(ctx.runtime_info.alpha_test_reference)}; |
| 80 | const Id condition{ComparisonFunction(ctx, comparison, alpha, alpha_reference)}; | 83 | const Id condition{ComparisonFunction(ctx, comparison, alpha, alpha_reference)}; |
| 81 | 84 | ||
| 82 | ctx.OpSelectionMerge(true_label, spv::SelectionControlMask::MaskNone); | 85 | ctx.OpSelectionMerge(true_label, spv::SelectionControlMask::MaskNone); |
| @@ -113,7 +116,7 @@ void EmitPrologue(EmitContext& ctx) { | |||
| 113 | } | 116 | } |
| 114 | 117 | ||
| 115 | void EmitEpilogue(EmitContext& ctx) { | 118 | void EmitEpilogue(EmitContext& ctx) { |
| 116 | if (ctx.stage == Stage::VertexB && ctx.profile.convert_depth_mode) { | 119 | if (ctx.stage == Stage::VertexB && ctx.runtime_info.convert_depth_mode) { |
| 117 | ConvertDepthMode(ctx); | 120 | ConvertDepthMode(ctx); |
| 118 | } | 121 | } |
| 119 | if (ctx.stage == Stage::Fragment) { | 122 | if (ctx.stage == Stage::Fragment) { |
| @@ -122,7 +125,7 @@ void EmitEpilogue(EmitContext& ctx) { | |||
| 122 | } | 125 | } |
| 123 | 126 | ||
| 124 | void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) { | 127 | void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) { |
| 125 | if (ctx.profile.convert_depth_mode) { | 128 | if (ctx.runtime_info.convert_depth_mode) { |
| 126 | ConvertDepthMode(ctx); | 129 | ConvertDepthMode(ctx); |
| 127 | } | 130 | } |
| 128 | if (stream.IsImmediate()) { | 131 | if (stream.IsImmediate()) { |
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index 12699511a..c46452c3d 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h | |||
| @@ -81,19 +81,22 @@ struct Profile { | |||
| 81 | bool support_viewport_mask{}; | 81 | bool support_viewport_mask{}; |
| 82 | bool support_typeless_image_loads{}; | 82 | bool support_typeless_image_loads{}; |
| 83 | bool support_demote_to_helper_invocation{}; | 83 | bool support_demote_to_helper_invocation{}; |
| 84 | bool warp_size_potentially_larger_than_guest{}; | ||
| 85 | bool support_int64_atomics{}; | 84 | bool support_int64_atomics{}; |
| 85 | |||
| 86 | bool warp_size_potentially_larger_than_guest{}; | ||
| 86 | bool lower_left_origin_mode{}; | 87 | bool lower_left_origin_mode{}; |
| 87 | 88 | ||
| 88 | // FClamp is broken and OpFMax + OpFMin should be used instead | 89 | /// OpFClamp is broken and OpFMax + OpFMin should be used instead |
| 89 | bool has_broken_spirv_clamp{}; | 90 | bool has_broken_spirv_clamp{}; |
| 90 | // Offset image operands with an unsigned type do not work | 91 | /// Offset image operands with an unsigned type do not work |
| 91 | bool has_broken_unsigned_image_offsets{}; | 92 | bool has_broken_unsigned_image_offsets{}; |
| 92 | // Signed instructions with unsigned data types are misinterpreted | 93 | /// Signed instructions with unsigned data types are misinterpreted |
| 93 | bool has_broken_signed_operations{}; | 94 | bool has_broken_signed_operations{}; |
| 94 | // Ignores SPIR-V ordered vs unordered using GLSL semantics | 95 | /// Ignores SPIR-V ordered vs unordered using GLSL semantics |
| 95 | bool ignore_nan_fp_comparisons{}; | 96 | bool ignore_nan_fp_comparisons{}; |
| 97 | }; | ||
| 96 | 98 | ||
| 99 | struct RuntimeInfo { | ||
| 97 | std::array<AttributeType, 32> generic_input_types{}; | 100 | std::array<AttributeType, 32> generic_input_types{}; |
| 98 | bool convert_depth_mode{}; | 101 | bool convert_depth_mode{}; |
| 99 | bool force_early_z{}; | 102 | bool force_early_z{}; |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index b84b36b9d..d7efbdd01 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -61,33 +61,15 @@ const Shader::Profile profile{ | |||
| 61 | .support_viewport_mask = true, | 61 | .support_viewport_mask = true, |
| 62 | .support_typeless_image_loads = true, | 62 | .support_typeless_image_loads = true, |
| 63 | .support_demote_to_helper_invocation = false, | 63 | .support_demote_to_helper_invocation = false, |
| 64 | .warp_size_potentially_larger_than_guest = true, | ||
| 65 | .support_int64_atomics = false, | 64 | .support_int64_atomics = false, |
| 65 | |||
| 66 | .warp_size_potentially_larger_than_guest = true, | ||
| 66 | .lower_left_origin_mode = true, | 67 | .lower_left_origin_mode = true, |
| 67 | 68 | ||
| 68 | .has_broken_spirv_clamp = true, | 69 | .has_broken_spirv_clamp = true, |
| 69 | .has_broken_unsigned_image_offsets = true, | 70 | .has_broken_unsigned_image_offsets = true, |
| 70 | .has_broken_signed_operations = true, | 71 | .has_broken_signed_operations = true, |
| 71 | .ignore_nan_fp_comparisons = true, | 72 | .ignore_nan_fp_comparisons = true, |
| 72 | |||
| 73 | .generic_input_types = {}, | ||
| 74 | .convert_depth_mode = false, | ||
| 75 | .force_early_z = false, | ||
| 76 | |||
| 77 | .tess_primitive = {}, | ||
| 78 | .tess_spacing = {}, | ||
| 79 | .tess_clockwise = false, | ||
| 80 | |||
| 81 | .input_topology = Shader::InputTopology::Triangles, | ||
| 82 | |||
| 83 | .fixed_state_point_size = std::nullopt, | ||
| 84 | |||
| 85 | .alpha_test_func = Shader::CompareFunction::Always, | ||
| 86 | .alpha_test_reference = 0.0f, | ||
| 87 | |||
| 88 | .y_negate = false, | ||
| 89 | |||
| 90 | .xfb_varyings = {}, | ||
| 91 | }; | 73 | }; |
| 92 | 74 | ||
| 93 | using Shader::Backend::GLASM::EmitGLASM; | 75 | using Shader::Backend::GLASM::EmitGLASM; |
| @@ -302,10 +284,10 @@ std::unique_ptr<GraphicsProgram> ShaderCache::CreateGraphicsProgram( | |||
| 302 | const size_t stage_index{index - 1}; | 284 | const size_t stage_index{index - 1}; |
| 303 | infos[stage_index] = &program.info; | 285 | infos[stage_index] = &program.info; |
| 304 | if (device.UseAssemblyShaders()) { | 286 | if (device.UseAssemblyShaders()) { |
| 305 | const std::string code{EmitGLASM(profile, program, binding)}; | 287 | const std::string code{EmitGLASM(profile, {}, program, binding)}; |
| 306 | assembly_programs[stage_index] = CompileProgram(code, AssemblyStage(stage_index)); | 288 | assembly_programs[stage_index] = CompileProgram(code, AssemblyStage(stage_index)); |
| 307 | } else { | 289 | } else { |
| 308 | const std::vector<u32> code{EmitSPIRV(profile, program, binding)}; | 290 | const std::vector<u32> code{EmitSPIRV(profile, {}, program, binding)}; |
| 309 | AddShader(Stage(stage_index), source_program.handle, code); | 291 | AddShader(Stage(stage_index), source_program.handle, code); |
| 310 | } | 292 | } |
| 311 | } | 293 | } |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 7830c0194..88db10b75 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -89,6 +89,208 @@ Shader::CompareFunction MaxwellToCompareFunction(Maxwell::ComparisonOp compariso | |||
| 89 | UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison); | 89 | UNIMPLEMENTED_MSG("Unimplemented comparison op={}", comparison); |
| 90 | return {}; | 90 | return {}; |
| 91 | } | 91 | } |
| 92 | |||
| 93 | static Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexAttribute& attr) { | ||
| 94 | if (attr.enabled == 0) { | ||
| 95 | return Shader::AttributeType::Disabled; | ||
| 96 | } | ||
| 97 | switch (attr.Type()) { | ||
| 98 | case Maxwell::VertexAttribute::Type::SignedNorm: | ||
| 99 | case Maxwell::VertexAttribute::Type::UnsignedNorm: | ||
| 100 | case Maxwell::VertexAttribute::Type::UnsignedScaled: | ||
| 101 | case Maxwell::VertexAttribute::Type::SignedScaled: | ||
| 102 | case Maxwell::VertexAttribute::Type::Float: | ||
| 103 | return Shader::AttributeType::Float; | ||
| 104 | case Maxwell::VertexAttribute::Type::SignedInt: | ||
| 105 | return Shader::AttributeType::SignedInt; | ||
| 106 | case Maxwell::VertexAttribute::Type::UnsignedInt: | ||
| 107 | return Shader::AttributeType::UnsignedInt; | ||
| 108 | } | ||
| 109 | return Shader::AttributeType::Float; | ||
| 110 | } | ||
| 111 | |||
| 112 | std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings( | ||
| 113 | const GraphicsPipelineCacheKey& key) { | ||
| 114 | static constexpr std::array VECTORS{ | ||
| 115 | 28, // gl_Position | ||
| 116 | 32, // Generic 0 | ||
| 117 | 36, // Generic 1 | ||
| 118 | 40, // Generic 2 | ||
| 119 | 44, // Generic 3 | ||
| 120 | 48, // Generic 4 | ||
| 121 | 52, // Generic 5 | ||
| 122 | 56, // Generic 6 | ||
| 123 | 60, // Generic 7 | ||
| 124 | 64, // Generic 8 | ||
| 125 | 68, // Generic 9 | ||
| 126 | 72, // Generic 10 | ||
| 127 | 76, // Generic 11 | ||
| 128 | 80, // Generic 12 | ||
| 129 | 84, // Generic 13 | ||
| 130 | 88, // Generic 14 | ||
| 131 | 92, // Generic 15 | ||
| 132 | 96, // Generic 16 | ||
| 133 | 100, // Generic 17 | ||
| 134 | 104, // Generic 18 | ||
| 135 | 108, // Generic 19 | ||
| 136 | 112, // Generic 20 | ||
| 137 | 116, // Generic 21 | ||
| 138 | 120, // Generic 22 | ||
| 139 | 124, // Generic 23 | ||
| 140 | 128, // Generic 24 | ||
| 141 | 132, // Generic 25 | ||
| 142 | 136, // Generic 26 | ||
| 143 | 140, // Generic 27 | ||
| 144 | 144, // Generic 28 | ||
| 145 | 148, // Generic 29 | ||
| 146 | 152, // Generic 30 | ||
| 147 | 156, // Generic 31 | ||
| 148 | 160, // gl_FrontColor | ||
| 149 | 164, // gl_FrontSecondaryColor | ||
| 150 | 160, // gl_BackColor | ||
| 151 | 164, // gl_BackSecondaryColor | ||
| 152 | 192, // gl_TexCoord[0] | ||
| 153 | 196, // gl_TexCoord[1] | ||
| 154 | 200, // gl_TexCoord[2] | ||
| 155 | 204, // gl_TexCoord[3] | ||
| 156 | 208, // gl_TexCoord[4] | ||
| 157 | 212, // gl_TexCoord[5] | ||
| 158 | 216, // gl_TexCoord[6] | ||
| 159 | 220, // gl_TexCoord[7] | ||
| 160 | }; | ||
| 161 | std::vector<Shader::TransformFeedbackVarying> xfb(256); | ||
| 162 | for (size_t buffer = 0; buffer < Maxwell::NumTransformFeedbackBuffers; ++buffer) { | ||
| 163 | const auto& locations = key.state.xfb_state.varyings[buffer]; | ||
| 164 | const auto& layout = key.state.xfb_state.layouts[buffer]; | ||
| 165 | const u32 varying_count = layout.varying_count; | ||
| 166 | u32 highest = 0; | ||
| 167 | for (u32 offset = 0; offset < varying_count; ++offset) { | ||
| 168 | const u32 base_offset = offset; | ||
| 169 | const u8 location = locations[offset]; | ||
| 170 | |||
| 171 | Shader::TransformFeedbackVarying varying; | ||
| 172 | varying.buffer = layout.stream; | ||
| 173 | varying.stride = layout.stride; | ||
| 174 | varying.offset = offset * 4; | ||
| 175 | varying.components = 1; | ||
| 176 | |||
| 177 | if (std::ranges::find(VECTORS, Common::AlignDown(location, 4)) != VECTORS.end()) { | ||
| 178 | UNIMPLEMENTED_IF_MSG(location % 4 != 0, "Unaligned TFB"); | ||
| 179 | |||
| 180 | const u8 base_index = location / 4; | ||
| 181 | while (offset + 1 < varying_count && base_index == locations[offset + 1] / 4) { | ||
| 182 | ++offset; | ||
| 183 | ++varying.components; | ||
| 184 | } | ||
| 185 | } | ||
| 186 | xfb[location] = varying; | ||
| 187 | highest = std::max(highest, (base_offset + varying.components) * 4); | ||
| 188 | } | ||
| 189 | UNIMPLEMENTED_IF(highest != layout.stride); | ||
| 190 | } | ||
| 191 | return xfb; | ||
| 192 | } | ||
| 193 | |||
| 194 | Shader::RuntimeInfo MakeRuntimeInfo(const GraphicsPipelineCacheKey& key, | ||
| 195 | const Shader::IR::Program& program) { | ||
| 196 | Shader::RuntimeInfo info; | ||
| 197 | |||
| 198 | const Shader::Stage stage{program.stage}; | ||
| 199 | const bool has_geometry{key.unique_hashes[4] != 0}; | ||
| 200 | const bool gl_ndc{key.state.ndc_minus_one_to_one != 0}; | ||
| 201 | const float point_size{Common::BitCast<float>(key.state.point_size)}; | ||
| 202 | switch (stage) { | ||
| 203 | case Shader::Stage::VertexB: | ||
| 204 | if (!has_geometry) { | ||
| 205 | if (key.state.topology == Maxwell::PrimitiveTopology::Points) { | ||
| 206 | info.fixed_state_point_size = point_size; | ||
| 207 | } | ||
| 208 | if (key.state.xfb_enabled != 0) { | ||
| 209 | info.xfb_varyings = MakeTransformFeedbackVaryings(key); | ||
| 210 | } | ||
| 211 | info.convert_depth_mode = gl_ndc; | ||
| 212 | } | ||
| 213 | std::ranges::transform(key.state.attributes, info.generic_input_types.begin(), | ||
| 214 | &CastAttributeType); | ||
| 215 | break; | ||
| 216 | case Shader::Stage::TessellationEval: | ||
| 217 | // We have to flip tessellation clockwise for some reason... | ||
| 218 | info.tess_clockwise = key.state.tessellation_clockwise == 0; | ||
| 219 | info.tess_primitive = [&key] { | ||
| 220 | const u32 raw{key.state.tessellation_primitive.Value()}; | ||
| 221 | switch (static_cast<Maxwell::TessellationPrimitive>(raw)) { | ||
| 222 | case Maxwell::TessellationPrimitive::Isolines: | ||
| 223 | return Shader::TessPrimitive::Isolines; | ||
| 224 | case Maxwell::TessellationPrimitive::Triangles: | ||
| 225 | return Shader::TessPrimitive::Triangles; | ||
| 226 | case Maxwell::TessellationPrimitive::Quads: | ||
| 227 | return Shader::TessPrimitive::Quads; | ||
| 228 | } | ||
| 229 | UNREACHABLE(); | ||
| 230 | return Shader::TessPrimitive::Triangles; | ||
| 231 | }(); | ||
| 232 | info.tess_spacing = [&] { | ||
| 233 | const u32 raw{key.state.tessellation_spacing}; | ||
| 234 | switch (static_cast<Maxwell::TessellationSpacing>(raw)) { | ||
| 235 | case Maxwell::TessellationSpacing::Equal: | ||
| 236 | return Shader::TessSpacing::Equal; | ||
| 237 | case Maxwell::TessellationSpacing::FractionalOdd: | ||
| 238 | return Shader::TessSpacing::FractionalOdd; | ||
| 239 | case Maxwell::TessellationSpacing::FractionalEven: | ||
| 240 | return Shader::TessSpacing::FractionalEven; | ||
| 241 | } | ||
| 242 | UNREACHABLE(); | ||
| 243 | return Shader::TessSpacing::Equal; | ||
| 244 | }(); | ||
| 245 | break; | ||
| 246 | case Shader::Stage::Geometry: | ||
| 247 | if (program.output_topology == Shader::OutputTopology::PointList) { | ||
| 248 | info.fixed_state_point_size = point_size; | ||
| 249 | } | ||
| 250 | if (key.state.xfb_enabled != 0) { | ||
| 251 | info.xfb_varyings = MakeTransformFeedbackVaryings(key); | ||
| 252 | } | ||
| 253 | info.convert_depth_mode = gl_ndc; | ||
| 254 | break; | ||
| 255 | case Shader::Stage::Fragment: | ||
| 256 | info.alpha_test_func = MaxwellToCompareFunction( | ||
| 257 | key.state.UnpackComparisonOp(key.state.alpha_test_func.Value())); | ||
| 258 | info.alpha_test_reference = Common::BitCast<float>(key.state.alpha_test_ref); | ||
| 259 | break; | ||
| 260 | default: | ||
| 261 | break; | ||
| 262 | } | ||
| 263 | switch (key.state.topology) { | ||
| 264 | case Maxwell::PrimitiveTopology::Points: | ||
| 265 | info.input_topology = Shader::InputTopology::Points; | ||
| 266 | break; | ||
| 267 | case Maxwell::PrimitiveTopology::Lines: | ||
| 268 | case Maxwell::PrimitiveTopology::LineLoop: | ||
| 269 | case Maxwell::PrimitiveTopology::LineStrip: | ||
| 270 | info.input_topology = Shader::InputTopology::Lines; | ||
| 271 | break; | ||
| 272 | case Maxwell::PrimitiveTopology::Triangles: | ||
| 273 | case Maxwell::PrimitiveTopology::TriangleStrip: | ||
| 274 | case Maxwell::PrimitiveTopology::TriangleFan: | ||
| 275 | case Maxwell::PrimitiveTopology::Quads: | ||
| 276 | case Maxwell::PrimitiveTopology::QuadStrip: | ||
| 277 | case Maxwell::PrimitiveTopology::Polygon: | ||
| 278 | case Maxwell::PrimitiveTopology::Patches: | ||
| 279 | info.input_topology = Shader::InputTopology::Triangles; | ||
| 280 | break; | ||
| 281 | case Maxwell::PrimitiveTopology::LinesAdjacency: | ||
| 282 | case Maxwell::PrimitiveTopology::LineStripAdjacency: | ||
| 283 | info.input_topology = Shader::InputTopology::LinesAdjacency; | ||
| 284 | break; | ||
| 285 | case Maxwell::PrimitiveTopology::TrianglesAdjacency: | ||
| 286 | case Maxwell::PrimitiveTopology::TriangleStripAdjacency: | ||
| 287 | info.input_topology = Shader::InputTopology::TrianglesAdjacency; | ||
| 288 | break; | ||
| 289 | } | ||
| 290 | info.force_early_z = key.state.early_z != 0; | ||
| 291 | info.y_negate = key.state.y_negate != 0; | ||
| 292 | return info; | ||
| 293 | } | ||
| 92 | } // Anonymous namespace | 294 | } // Anonymous namespace |
| 93 | 295 | ||
| 94 | size_t ComputePipelineCacheKey::Hash() const noexcept { | 296 | size_t ComputePipelineCacheKey::Hash() const noexcept { |
| @@ -124,7 +326,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxw | |||
| 124 | serialization_thread(1, "yuzu:PipelineSerialization") { | 326 | serialization_thread(1, "yuzu:PipelineSerialization") { |
| 125 | const auto& float_control{device.FloatControlProperties()}; | 327 | const auto& float_control{device.FloatControlProperties()}; |
| 126 | const VkDriverIdKHR driver_id{device.GetDriverID()}; | 328 | const VkDriverIdKHR driver_id{device.GetDriverID()}; |
| 127 | base_profile = Shader::Profile{ | 329 | profile = Shader::Profile{ |
| 128 | .supported_spirv = device.IsKhrSpirv1_4Supported() ? 0x00010400U : 0x00010000U, | 330 | .supported_spirv = device.IsKhrSpirv1_4Supported() ? 0x00010400U : 0x00010000U, |
| 129 | .unified_descriptor_binding = true, | 331 | .unified_descriptor_binding = true, |
| 130 | .support_descriptor_aliasing = true, | 332 | .support_descriptor_aliasing = true, |
| @@ -153,14 +355,10 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, Tegra::Engines::Maxw | |||
| 153 | .support_viewport_mask = device.IsNvViewportArray2Supported(), | 355 | .support_viewport_mask = device.IsNvViewportArray2Supported(), |
| 154 | .support_typeless_image_loads = device.IsFormatlessImageLoadSupported(), | 356 | .support_typeless_image_loads = device.IsFormatlessImageLoadSupported(), |
| 155 | .support_demote_to_helper_invocation = true, | 357 | .support_demote_to_helper_invocation = true, |
| 156 | .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), | ||
| 157 | .support_int64_atomics = device.IsExtShaderAtomicInt64Supported(), | 358 | .support_int64_atomics = device.IsExtShaderAtomicInt64Supported(), |
| 359 | .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), | ||
| 158 | .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR, | 360 | .has_broken_spirv_clamp = driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR, |
| 159 | .has_broken_unsigned_image_offsets = false, | 361 | .has_broken_unsigned_image_offsets = false, |
| 160 | .generic_input_types{}, | ||
| 161 | .fixed_state_point_size{}, | ||
| 162 | .alpha_test_func{}, | ||
| 163 | .xfb_varyings{}, | ||
| 164 | }; | 362 | }; |
| 165 | } | 363 | } |
| 166 | 364 | ||
| @@ -329,8 +527,8 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( | |||
| 329 | const size_t stage_index{index - 1}; | 527 | const size_t stage_index{index - 1}; |
| 330 | infos[stage_index] = &program.info; | 528 | infos[stage_index] = &program.info; |
| 331 | 529 | ||
| 332 | const Shader::Profile profile{MakeProfile(key, program)}; | 530 | const Shader::RuntimeInfo runtime_info{MakeRuntimeInfo(key, program)}; |
| 333 | const std::vector<u32> code{EmitSPIRV(profile, program, binding)}; | 531 | const std::vector<u32> code{EmitSPIRV(profile, runtime_info, program, binding)}; |
| 334 | device.SaveShader(code); | 532 | device.SaveShader(code); |
| 335 | modules[stage_index] = BuildShader(device, code); | 533 | modules[stage_index] = BuildShader(device, code); |
| 336 | if (device.HasDebuggingToolAttached()) { | 534 | if (device.HasDebuggingToolAttached()) { |
| @@ -391,7 +589,7 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline( | |||
| 391 | 589 | ||
| 392 | Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; | 590 | Shader::Maxwell::Flow::CFG cfg{env, pools.flow_block, env.StartAddress()}; |
| 393 | Shader::IR::Program program{TranslateProgram(pools.inst, pools.block, env, cfg)}; | 591 | Shader::IR::Program program{TranslateProgram(pools.inst, pools.block, env, cfg)}; |
| 394 | const std::vector<u32> code{EmitSPIRV(base_profile, program)}; | 592 | const std::vector<u32> code{EmitSPIRV(profile, program)}; |
| 395 | device.SaveShader(code); | 593 | device.SaveShader(code); |
| 396 | vk::ShaderModule spv_module{BuildShader(device, code)}; | 594 | vk::ShaderModule spv_module{BuildShader(device, code)}; |
| 397 | if (device.HasDebuggingToolAttached()) { | 595 | if (device.HasDebuggingToolAttached()) { |
| @@ -403,206 +601,4 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline( | |||
| 403 | thread_worker, program.info, std::move(spv_module)); | 601 | thread_worker, program.info, std::move(spv_module)); |
| 404 | } | 602 | } |
| 405 | 603 | ||
| 406 | static Shader::AttributeType CastAttributeType(const FixedPipelineState::VertexAttribute& attr) { | ||
| 407 | if (attr.enabled == 0) { | ||
| 408 | return Shader::AttributeType::Disabled; | ||
| 409 | } | ||
| 410 | switch (attr.Type()) { | ||
| 411 | case Maxwell::VertexAttribute::Type::SignedNorm: | ||
| 412 | case Maxwell::VertexAttribute::Type::UnsignedNorm: | ||
| 413 | case Maxwell::VertexAttribute::Type::UnsignedScaled: | ||
| 414 | case Maxwell::VertexAttribute::Type::SignedScaled: | ||
| 415 | case Maxwell::VertexAttribute::Type::Float: | ||
| 416 | return Shader::AttributeType::Float; | ||
| 417 | case Maxwell::VertexAttribute::Type::SignedInt: | ||
| 418 | return Shader::AttributeType::SignedInt; | ||
| 419 | case Maxwell::VertexAttribute::Type::UnsignedInt: | ||
| 420 | return Shader::AttributeType::UnsignedInt; | ||
| 421 | } | ||
| 422 | return Shader::AttributeType::Float; | ||
| 423 | } | ||
| 424 | |||
| 425 | static std::vector<Shader::TransformFeedbackVarying> MakeTransformFeedbackVaryings( | ||
| 426 | const GraphicsPipelineCacheKey& key) { | ||
| 427 | static constexpr std::array VECTORS{ | ||
| 428 | 28, // gl_Position | ||
| 429 | 32, // Generic 0 | ||
| 430 | 36, // Generic 1 | ||
| 431 | 40, // Generic 2 | ||
| 432 | 44, // Generic 3 | ||
| 433 | 48, // Generic 4 | ||
| 434 | 52, // Generic 5 | ||
| 435 | 56, // Generic 6 | ||
| 436 | 60, // Generic 7 | ||
| 437 | 64, // Generic 8 | ||
| 438 | 68, // Generic 9 | ||
| 439 | 72, // Generic 10 | ||
| 440 | 76, // Generic 11 | ||
| 441 | 80, // Generic 12 | ||
| 442 | 84, // Generic 13 | ||
| 443 | 88, // Generic 14 | ||
| 444 | 92, // Generic 15 | ||
| 445 | 96, // Generic 16 | ||
| 446 | 100, // Generic 17 | ||
| 447 | 104, // Generic 18 | ||
| 448 | 108, // Generic 19 | ||
| 449 | 112, // Generic 20 | ||
| 450 | 116, // Generic 21 | ||
| 451 | 120, // Generic 22 | ||
| 452 | 124, // Generic 23 | ||
| 453 | 128, // Generic 24 | ||
| 454 | 132, // Generic 25 | ||
| 455 | 136, // Generic 26 | ||
| 456 | 140, // Generic 27 | ||
| 457 | 144, // Generic 28 | ||
| 458 | 148, // Generic 29 | ||
| 459 | 152, // Generic 30 | ||
| 460 | 156, // Generic 31 | ||
| 461 | 160, // gl_FrontColor | ||
| 462 | 164, // gl_FrontSecondaryColor | ||
| 463 | 160, // gl_BackColor | ||
| 464 | 164, // gl_BackSecondaryColor | ||
| 465 | 192, // gl_TexCoord[0] | ||
| 466 | 196, // gl_TexCoord[1] | ||
| 467 | 200, // gl_TexCoord[2] | ||
| 468 | 204, // gl_TexCoord[3] | ||
| 469 | 208, // gl_TexCoord[4] | ||
| 470 | 212, // gl_TexCoord[5] | ||
| 471 | 216, // gl_TexCoord[6] | ||
| 472 | 220, // gl_TexCoord[7] | ||
| 473 | }; | ||
| 474 | std::vector<Shader::TransformFeedbackVarying> xfb(256); | ||
| 475 | for (size_t buffer = 0; buffer < Maxwell::NumTransformFeedbackBuffers; ++buffer) { | ||
| 476 | const auto& locations = key.state.xfb_state.varyings[buffer]; | ||
| 477 | const auto& layout = key.state.xfb_state.layouts[buffer]; | ||
| 478 | const u32 varying_count = layout.varying_count; | ||
| 479 | u32 highest = 0; | ||
| 480 | for (u32 offset = 0; offset < varying_count; ++offset) { | ||
| 481 | const u32 base_offset = offset; | ||
| 482 | const u8 location = locations[offset]; | ||
| 483 | |||
| 484 | Shader::TransformFeedbackVarying varying; | ||
| 485 | varying.buffer = layout.stream; | ||
| 486 | varying.stride = layout.stride; | ||
| 487 | varying.offset = offset * 4; | ||
| 488 | varying.components = 1; | ||
| 489 | |||
| 490 | if (std::ranges::find(VECTORS, Common::AlignDown(location, 4)) != VECTORS.end()) { | ||
| 491 | UNIMPLEMENTED_IF_MSG(location % 4 != 0, "Unaligned TFB"); | ||
| 492 | |||
| 493 | const u8 base_index = location / 4; | ||
| 494 | while (offset + 1 < varying_count && base_index == locations[offset + 1] / 4) { | ||
| 495 | ++offset; | ||
| 496 | ++varying.components; | ||
| 497 | } | ||
| 498 | } | ||
| 499 | xfb[location] = varying; | ||
| 500 | highest = std::max(highest, (base_offset + varying.components) * 4); | ||
| 501 | } | ||
| 502 | UNIMPLEMENTED_IF(highest != layout.stride); | ||
| 503 | } | ||
| 504 | return xfb; | ||
| 505 | } | ||
| 506 | |||
| 507 | Shader::Profile PipelineCache::MakeProfile(const GraphicsPipelineCacheKey& key, | ||
| 508 | const Shader::IR::Program& program) { | ||
| 509 | Shader::Profile profile{base_profile}; | ||
| 510 | |||
| 511 | const Shader::Stage stage{program.stage}; | ||
| 512 | const bool has_geometry{key.unique_hashes[4] != 0}; | ||
| 513 | const bool gl_ndc{key.state.ndc_minus_one_to_one != 0}; | ||
| 514 | const float point_size{Common::BitCast<float>(key.state.point_size)}; | ||
| 515 | switch (stage) { | ||
| 516 | case Shader::Stage::VertexB: | ||
| 517 | if (!has_geometry) { | ||
| 518 | if (key.state.topology == Maxwell::PrimitiveTopology::Points) { | ||
| 519 | profile.fixed_state_point_size = point_size; | ||
| 520 | } | ||
| 521 | if (key.state.xfb_enabled != 0) { | ||
| 522 | profile.xfb_varyings = MakeTransformFeedbackVaryings(key); | ||
| 523 | } | ||
| 524 | profile.convert_depth_mode = gl_ndc; | ||
| 525 | } | ||
| 526 | std::ranges::transform(key.state.attributes, profile.generic_input_types.begin(), | ||
| 527 | &CastAttributeType); | ||
| 528 | break; | ||
| 529 | case Shader::Stage::TessellationEval: | ||
| 530 | // We have to flip tessellation clockwise for some reason... | ||
| 531 | profile.tess_clockwise = key.state.tessellation_clockwise == 0; | ||
| 532 | profile.tess_primitive = [&key] { | ||
| 533 | const u32 raw{key.state.tessellation_primitive.Value()}; | ||
| 534 | switch (static_cast<Maxwell::TessellationPrimitive>(raw)) { | ||
| 535 | case Maxwell::TessellationPrimitive::Isolines: | ||
| 536 | return Shader::TessPrimitive::Isolines; | ||
| 537 | case Maxwell::TessellationPrimitive::Triangles: | ||
| 538 | return Shader::TessPrimitive::Triangles; | ||
| 539 | case Maxwell::TessellationPrimitive::Quads: | ||
| 540 | return Shader::TessPrimitive::Quads; | ||
| 541 | } | ||
| 542 | UNREACHABLE(); | ||
| 543 | return Shader::TessPrimitive::Triangles; | ||
| 544 | }(); | ||
| 545 | profile.tess_spacing = [&] { | ||
| 546 | const u32 raw{key.state.tessellation_spacing}; | ||
| 547 | switch (static_cast<Maxwell::TessellationSpacing>(raw)) { | ||
| 548 | case Maxwell::TessellationSpacing::Equal: | ||
| 549 | return Shader::TessSpacing::Equal; | ||
| 550 | case Maxwell::TessellationSpacing::FractionalOdd: | ||
| 551 | return Shader::TessSpacing::FractionalOdd; | ||
| 552 | case Maxwell::TessellationSpacing::FractionalEven: | ||
| 553 | return Shader::TessSpacing::FractionalEven; | ||
| 554 | } | ||
| 555 | UNREACHABLE(); | ||
| 556 | return Shader::TessSpacing::Equal; | ||
| 557 | }(); | ||
| 558 | break; | ||
| 559 | case Shader::Stage::Geometry: | ||
| 560 | if (program.output_topology == Shader::OutputTopology::PointList) { | ||
| 561 | profile.fixed_state_point_size = point_size; | ||
| 562 | } | ||
| 563 | if (key.state.xfb_enabled != 0) { | ||
| 564 | profile.xfb_varyings = MakeTransformFeedbackVaryings(key); | ||
| 565 | } | ||
| 566 | profile.convert_depth_mode = gl_ndc; | ||
| 567 | break; | ||
| 568 | case Shader::Stage::Fragment: | ||
| 569 | profile.alpha_test_func = MaxwellToCompareFunction( | ||
| 570 | key.state.UnpackComparisonOp(key.state.alpha_test_func.Value())); | ||
| 571 | profile.alpha_test_reference = Common::BitCast<float>(key.state.alpha_test_ref); | ||
| 572 | break; | ||
| 573 | default: | ||
| 574 | break; | ||
| 575 | } | ||
| 576 | switch (key.state.topology) { | ||
| 577 | case Maxwell::PrimitiveTopology::Points: | ||
| 578 | profile.input_topology = Shader::InputTopology::Points; | ||
| 579 | break; | ||
| 580 | case Maxwell::PrimitiveTopology::Lines: | ||
| 581 | case Maxwell::PrimitiveTopology::LineLoop: | ||
| 582 | case Maxwell::PrimitiveTopology::LineStrip: | ||
| 583 | profile.input_topology = Shader::InputTopology::Lines; | ||
| 584 | break; | ||
| 585 | case Maxwell::PrimitiveTopology::Triangles: | ||
| 586 | case Maxwell::PrimitiveTopology::TriangleStrip: | ||
| 587 | case Maxwell::PrimitiveTopology::TriangleFan: | ||
| 588 | case Maxwell::PrimitiveTopology::Quads: | ||
| 589 | case Maxwell::PrimitiveTopology::QuadStrip: | ||
| 590 | case Maxwell::PrimitiveTopology::Polygon: | ||
| 591 | case Maxwell::PrimitiveTopology::Patches: | ||
| 592 | profile.input_topology = Shader::InputTopology::Triangles; | ||
| 593 | break; | ||
| 594 | case Maxwell::PrimitiveTopology::LinesAdjacency: | ||
| 595 | case Maxwell::PrimitiveTopology::LineStripAdjacency: | ||
| 596 | profile.input_topology = Shader::InputTopology::LinesAdjacency; | ||
| 597 | break; | ||
| 598 | case Maxwell::PrimitiveTopology::TrianglesAdjacency: | ||
| 599 | case Maxwell::PrimitiveTopology::TriangleStripAdjacency: | ||
| 600 | profile.input_topology = Shader::InputTopology::TrianglesAdjacency; | ||
| 601 | break; | ||
| 602 | } | ||
| 603 | profile.force_early_z = key.state.early_z != 0; | ||
| 604 | profile.y_negate = key.state.y_negate != 0; | ||
| 605 | return profile; | ||
| 606 | } | ||
| 607 | |||
| 608 | } // namespace Vulkan | 604 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index 4e48b4956..4116cc73f 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h | |||
| @@ -129,9 +129,6 @@ private: | |||
| 129 | Shader::Environment& env, | 129 | Shader::Environment& env, |
| 130 | bool build_in_parallel); | 130 | bool build_in_parallel); |
| 131 | 131 | ||
| 132 | Shader::Profile MakeProfile(const GraphicsPipelineCacheKey& key, | ||
| 133 | const Shader::IR::Program& program); | ||
| 134 | |||
| 135 | const Device& device; | 132 | const Device& device; |
| 136 | VKScheduler& scheduler; | 133 | VKScheduler& scheduler; |
| 137 | DescriptorPool& descriptor_pool; | 134 | DescriptorPool& descriptor_pool; |
| @@ -148,7 +145,7 @@ private: | |||
| 148 | 145 | ||
| 149 | ShaderPools main_pools; | 146 | ShaderPools main_pools; |
| 150 | 147 | ||
| 151 | Shader::Profile base_profile; | 148 | Shader::Profile profile; |
| 152 | std::filesystem::path pipeline_cache_filename; | 149 | std::filesystem::path pipeline_cache_filename; |
| 153 | 150 | ||
| 154 | Common::ThreadWorker workers; | 151 | Common::ThreadWorker workers; |