summaryrefslogtreecommitdiff
path: root/src/shader_recompiler
diff options
context:
space:
mode:
Diffstat (limited to 'src/shader_recompiler')
-rw-r--r--src/shader_recompiler/CMakeLists.txt1
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp13
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp35
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp81
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.h9
-rw-r--r--src/shader_recompiler/host_translate_info.h3
-rw-r--r--src/shader_recompiler/ir_opt/layer_pass.cpp68
-rw-r--r--src/shader_recompiler/ir_opt/passes.h1
-rw-r--r--src/shader_recompiler/shader_info.h3
10 files changed, 196 insertions, 20 deletions
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index 545d69c7e..8cd584154 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -221,6 +221,7 @@ add_library(shader_recompiler STATIC
221 ir_opt/dual_vertex_pass.cpp 221 ir_opt/dual_vertex_pass.cpp
222 ir_opt/global_memory_to_storage_buffer_pass.cpp 222 ir_opt/global_memory_to_storage_buffer_pass.cpp
223 ir_opt/identity_removal_pass.cpp 223 ir_opt/identity_removal_pass.cpp
224 ir_opt/layer_pass.cpp
224 ir_opt/lower_fp16_to_fp32.cpp 225 ir_opt/lower_fp16_to_fp32.cpp
225 ir_opt/lower_int64_to_int32.cpp 226 ir_opt/lower_int64_to_int32.cpp
226 ir_opt/passes.h 227 ir_opt/passes.h
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index 265ac9c85..0f86a8004 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -402,8 +402,10 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
402 ctx.AddCapability(spv::Capability::SparseResidency); 402 ctx.AddCapability(spv::Capability::SparseResidency);
403 } 403 }
404 if (info.uses_demote_to_helper_invocation && profile.support_demote_to_helper_invocation) { 404 if (info.uses_demote_to_helper_invocation && profile.support_demote_to_helper_invocation) {
405 ctx.AddExtension("SPV_EXT_demote_to_helper_invocation"); 405 if (profile.supported_spirv < 0x00010600) {
406 ctx.AddCapability(spv::Capability::DemoteToHelperInvocationEXT); 406 ctx.AddExtension("SPV_EXT_demote_to_helper_invocation");
407 }
408 ctx.AddCapability(spv::Capability::DemoteToHelperInvocation);
407 } 409 }
408 if (info.stores[IR::Attribute::ViewportIndex]) { 410 if (info.stores[IR::Attribute::ViewportIndex]) {
409 ctx.AddCapability(spv::Capability::MultiViewport); 411 ctx.AddCapability(spv::Capability::MultiViewport);
@@ -426,12 +428,11 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
426 if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id || 428 if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id ||
427 info.uses_subgroup_shuffles) && 429 info.uses_subgroup_shuffles) &&
428 profile.support_vote) { 430 profile.support_vote) {
429 ctx.AddExtension("SPV_KHR_shader_ballot"); 431 ctx.AddCapability(spv::Capability::GroupNonUniformBallot);
430 ctx.AddCapability(spv::Capability::SubgroupBallotKHR); 432 ctx.AddCapability(spv::Capability::GroupNonUniformShuffle);
431 if (!profile.warp_size_potentially_larger_than_guest) { 433 if (!profile.warp_size_potentially_larger_than_guest) {
432 // vote ops are only used when not taking the long path 434 // vote ops are only used when not taking the long path
433 ctx.AddExtension("SPV_KHR_subgroup_vote"); 435 ctx.AddCapability(spv::Capability::GroupNonUniformVote);
434 ctx.AddCapability(spv::Capability::SubgroupVoteKHR);
435 } 436 }
436 } 437 }
437 if (info.uses_int64_bit_atomics && profile.support_int64_atomics) { 438 if (info.uses_int64_bit_atomics && profile.support_int64_atomics) {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
index 7ad0b08ac..fb2c792c1 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_control_flow.cpp
@@ -12,7 +12,7 @@ void EmitJoin(EmitContext&) {
12 12
13void EmitDemoteToHelperInvocation(EmitContext& ctx) { 13void EmitDemoteToHelperInvocation(EmitContext& ctx) {
14 if (ctx.profile.support_demote_to_helper_invocation) { 14 if (ctx.profile.support_demote_to_helper_invocation) {
15 ctx.OpDemoteToHelperInvocationEXT(); 15 ctx.OpDemoteToHelperInvocation();
16 } else { 16 } else {
17 const Id kill_label{ctx.OpLabel()}; 17 const Id kill_label{ctx.OpLabel()};
18 const Id impossible_label{ctx.OpLabel()}; 18 const Id impossible_label{ctx.OpLabel()};
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
index 7cbbbfaa6..2c90f2368 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_warp.cpp
@@ -6,6 +6,10 @@
6 6
7namespace Shader::Backend::SPIRV { 7namespace Shader::Backend::SPIRV {
8namespace { 8namespace {
9Id SubgroupScope(EmitContext& ctx) {
10 return ctx.Const(static_cast<u32>(spv::Scope::Subgroup));
11}
12
9Id GetThreadId(EmitContext& ctx) { 13Id GetThreadId(EmitContext& ctx) {
10 return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id); 14 return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id);
11} 15}
@@ -49,8 +53,9 @@ Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask
49} 53}
50 54
51Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) { 55Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) {
52 return ctx.OpSelect(ctx.U32[1], in_range, 56 return ctx.OpSelect(
53 ctx.OpSubgroupReadInvocationKHR(ctx.U32[1], value, src_thread_id), value); 57 ctx.U32[1], in_range,
58 ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value);
54} 59}
55 60
56Id GetUpperClamp(EmitContext& ctx, Id invocation_id, Id clamp) { 61Id GetUpperClamp(EmitContext& ctx, Id invocation_id, Id clamp) {
@@ -71,40 +76,46 @@ Id EmitLaneId(EmitContext& ctx) {
71 76
72Id EmitVoteAll(EmitContext& ctx, Id pred) { 77Id EmitVoteAll(EmitContext& ctx, Id pred) {
73 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 78 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
74 return ctx.OpSubgroupAllKHR(ctx.U1, pred); 79 return ctx.OpGroupNonUniformAll(ctx.U1, SubgroupScope(ctx), pred);
75 } 80 }
76 const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; 81 const Id mask_ballot{
82 ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), ctx.true_value)};
77 const Id active_mask{WarpExtract(ctx, mask_ballot)}; 83 const Id active_mask{WarpExtract(ctx, mask_ballot)};
78 const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; 84 const Id ballot{
85 WarpExtract(ctx, ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred))};
79 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)}; 86 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)};
80 return ctx.OpIEqual(ctx.U1, lhs, active_mask); 87 return ctx.OpIEqual(ctx.U1, lhs, active_mask);
81} 88}
82 89
83Id EmitVoteAny(EmitContext& ctx, Id pred) { 90Id EmitVoteAny(EmitContext& ctx, Id pred) {
84 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 91 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
85 return ctx.OpSubgroupAnyKHR(ctx.U1, pred); 92 return ctx.OpGroupNonUniformAny(ctx.U1, SubgroupScope(ctx), pred);
86 } 93 }
87 const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; 94 const Id mask_ballot{
95 ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), ctx.true_value)};
88 const Id active_mask{WarpExtract(ctx, mask_ballot)}; 96 const Id active_mask{WarpExtract(ctx, mask_ballot)};
89 const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; 97 const Id ballot{
98 WarpExtract(ctx, ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred))};
90 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)}; 99 const Id lhs{ctx.OpBitwiseAnd(ctx.U32[1], ballot, active_mask)};
91 return ctx.OpINotEqual(ctx.U1, lhs, ctx.u32_zero_value); 100 return ctx.OpINotEqual(ctx.U1, lhs, ctx.u32_zero_value);
92} 101}
93 102
94Id EmitVoteEqual(EmitContext& ctx, Id pred) { 103Id EmitVoteEqual(EmitContext& ctx, Id pred) {
95 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 104 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
96 return ctx.OpSubgroupAllEqualKHR(ctx.U1, pred); 105 return ctx.OpGroupNonUniformAllEqual(ctx.U1, SubgroupScope(ctx), pred);
97 } 106 }
98 const Id mask_ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], ctx.true_value)}; 107 const Id mask_ballot{
108 ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), ctx.true_value)};
99 const Id active_mask{WarpExtract(ctx, mask_ballot)}; 109 const Id active_mask{WarpExtract(ctx, mask_ballot)};
100 const Id ballot{WarpExtract(ctx, ctx.OpSubgroupBallotKHR(ctx.U32[4], pred))}; 110 const Id ballot{
111 WarpExtract(ctx, ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred))};
101 const Id lhs{ctx.OpBitwiseXor(ctx.U32[1], ballot, active_mask)}; 112 const Id lhs{ctx.OpBitwiseXor(ctx.U32[1], ballot, active_mask)};
102 return ctx.OpLogicalOr(ctx.U1, ctx.OpIEqual(ctx.U1, lhs, ctx.u32_zero_value), 113 return ctx.OpLogicalOr(ctx.U1, ctx.OpIEqual(ctx.U1, lhs, ctx.u32_zero_value),
103 ctx.OpIEqual(ctx.U1, lhs, active_mask)); 114 ctx.OpIEqual(ctx.U1, lhs, active_mask));
104} 115}
105 116
106Id EmitSubgroupBallot(EmitContext& ctx, Id pred) { 117Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
107 const Id ballot{ctx.OpSubgroupBallotKHR(ctx.U32[4], pred)}; 118 const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred)};
108 if (!ctx.profile.warp_size_potentially_larger_than_guest) { 119 if (!ctx.profile.warp_size_potentially_larger_than_guest) {
109 return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U); 120 return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U);
110 } 121 }
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index 376aae0ea..3adbd2b16 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -9,6 +9,7 @@
9#include "common/settings.h" 9#include "common/settings.h"
10#include "shader_recompiler/exception.h" 10#include "shader_recompiler/exception.h"
11#include "shader_recompiler/frontend/ir/basic_block.h" 11#include "shader_recompiler/frontend/ir/basic_block.h"
12#include "shader_recompiler/frontend/ir/ir_emitter.h"
12#include "shader_recompiler/frontend/ir/post_order.h" 13#include "shader_recompiler/frontend/ir/post_order.h"
13#include "shader_recompiler/frontend/maxwell/structured_control_flow.h" 14#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
14#include "shader_recompiler/frontend/maxwell/translate/translate.h" 15#include "shader_recompiler/frontend/maxwell/translate/translate.h"
@@ -233,6 +234,8 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
233 Optimization::VerificationPass(program); 234 Optimization::VerificationPass(program);
234 } 235 }
235 Optimization::CollectShaderInfoPass(env, program); 236 Optimization::CollectShaderInfoPass(env, program);
237 Optimization::LayerPass(program, host_info);
238
236 CollectInterpolationInfo(env, program); 239 CollectInterpolationInfo(env, program);
237 AddNVNStorageBuffers(program); 240 AddNVNStorageBuffers(program);
238 return program; 241 return program;
@@ -331,4 +334,82 @@ void ConvertLegacyToGeneric(IR::Program& program, const Shader::RuntimeInfo& run
331 } 334 }
332} 335}
333 336
337IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
338 ObjectPool<IR::Block>& block_pool,
339 const HostTranslateInfo& host_info,
340 IR::Program& source_program,
341 Shader::OutputTopology output_topology) {
342 IR::Program program;
343 program.stage = Stage::Geometry;
344 program.output_topology = output_topology;
345 switch (output_topology) {
346 case OutputTopology::PointList:
347 program.output_vertices = 1;
348 break;
349 case OutputTopology::LineStrip:
350 program.output_vertices = 2;
351 break;
352 default:
353 program.output_vertices = 3;
354 break;
355 }
356
357 program.is_geometry_passthrough = false;
358 program.info.loads.mask = source_program.info.stores.mask;
359 program.info.stores.mask = source_program.info.stores.mask;
360 program.info.stores.Set(IR::Attribute::Layer, true);
361 program.info.stores.Set(source_program.info.emulated_layer, false);
362
363 IR::Block* current_block = block_pool.Create(inst_pool);
364 auto& node{program.syntax_list.emplace_back()};
365 node.type = IR::AbstractSyntaxNode::Type::Block;
366 node.data.block = current_block;
367
368 IR::IREmitter ir{*current_block};
369 for (u32 i = 0; i < program.output_vertices; i++) {
370 // Assign generics from input
371 for (u32 j = 0; j < 32; j++) {
372 if (!program.info.stores.Generic(j)) {
373 continue;
374 }
375
376 const IR::Attribute attr = IR::Attribute::Generic0X + (j * 4);
377 ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
378 ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
379 ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
380 ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
381 }
382
383 // Assign position from input
384 const IR::Attribute attr = IR::Attribute::PositionX;
385 ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
386 ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
387 ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
388 ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
389
390 // Assign layer
391 ir.SetAttribute(IR::Attribute::Layer, ir.GetAttribute(source_program.info.emulated_layer),
392 ir.Imm32(0));
393
394 // Emit vertex
395 ir.EmitVertex(ir.Imm32(0));
396 }
397 ir.EndPrimitive(ir.Imm32(0));
398
399 IR::Block* return_block{block_pool.Create(inst_pool)};
400 IR::IREmitter{*return_block}.Epilogue();
401 current_block->AddBranch(return_block);
402
403 auto& merge{program.syntax_list.emplace_back()};
404 merge.type = IR::AbstractSyntaxNode::Type::Block;
405 merge.data.block = return_block;
406 program.syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Return;
407
408 program.blocks = GenerateBlocks(program.syntax_list);
409 program.post_order_blocks = PostOrder(program.syntax_list.front());
410 Optimization::SsaRewritePass(program);
411
412 return program;
413}
414
334} // namespace Shader::Maxwell 415} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.h b/src/shader_recompiler/frontend/maxwell/translate_program.h
index 02ede8c9c..497afe7cb 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.h
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.h
@@ -25,4 +25,13 @@ namespace Shader::Maxwell {
25 25
26void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_info); 26void ConvertLegacyToGeneric(IR::Program& program, const RuntimeInfo& runtime_info);
27 27
28// Maxwell v1 and older Nvidia cards don't support setting gl_Layer from non-geometry stages.
29// This creates a workaround by setting the layer as a generic output and creating a
30// passthrough geometry shader that reads the generic and sets the layer.
31[[nodiscard]] IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
32 ObjectPool<IR::Block>& block_pool,
33 const HostTranslateInfo& host_info,
34 IR::Program& source_program,
35 Shader::OutputTopology output_topology);
36
28} // namespace Shader::Maxwell 37} // namespace Shader::Maxwell
diff --git a/src/shader_recompiler/host_translate_info.h b/src/shader_recompiler/host_translate_info.h
index cc1500690..d5d279554 100644
--- a/src/shader_recompiler/host_translate_info.h
+++ b/src/shader_recompiler/host_translate_info.h
@@ -13,7 +13,8 @@ struct HostTranslateInfo {
13 bool support_float16{}; ///< True when the device supports 16-bit floats 13 bool support_float16{}; ///< True when the device supports 16-bit floats
14 bool support_int64{}; ///< True when the device supports 64-bit integers 14 bool support_int64{}; ///< True when the device supports 64-bit integers
15 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered 15 bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
16 bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers 16 bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
17 bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS
17}; 18};
18 19
19} // namespace Shader 20} // namespace Shader
diff --git a/src/shader_recompiler/ir_opt/layer_pass.cpp b/src/shader_recompiler/ir_opt/layer_pass.cpp
new file mode 100644
index 000000000..4574f7cf2
--- /dev/null
+++ b/src/shader_recompiler/ir_opt/layer_pass.cpp
@@ -0,0 +1,68 @@
1// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <algorithm>
5#include <bit>
6#include <optional>
7
8#include <boost/container/small_vector.hpp>
9
10#include "shader_recompiler/environment.h"
11#include "shader_recompiler/frontend/ir/basic_block.h"
12#include "shader_recompiler/frontend/ir/breadth_first_search.h"
13#include "shader_recompiler/frontend/ir/ir_emitter.h"
14#include "shader_recompiler/host_translate_info.h"
15#include "shader_recompiler/ir_opt/passes.h"
16#include "shader_recompiler/shader_info.h"
17
18namespace Shader::Optimization {
19
20static IR::Attribute EmulatedLayerAttribute(VaryingState& stores) {
21 for (u32 i = 0; i < 32; i++) {
22 if (!stores.Generic(i)) {
23 return IR::Attribute::Generic0X + (i * 4);
24 }
25 }
26 return IR::Attribute::Layer;
27}
28
29static bool PermittedProgramStage(Stage stage) {
30 switch (stage) {
31 case Stage::VertexA:
32 case Stage::VertexB:
33 case Stage::TessellationControl:
34 case Stage::TessellationEval:
35 return true;
36 default:
37 return false;
38 }
39}
40
41void LayerPass(IR::Program& program, const HostTranslateInfo& host_info) {
42 if (host_info.support_viewport_index_layer || !PermittedProgramStage(program.stage)) {
43 return;
44 }
45
46 const auto end{program.post_order_blocks.end()};
47 const auto layer_attribute = EmulatedLayerAttribute(program.info.stores);
48 bool requires_layer_emulation = false;
49
50 for (auto block = program.post_order_blocks.begin(); block != end; ++block) {
51 for (IR::Inst& inst : (*block)->Instructions()) {
52 if (inst.GetOpcode() == IR::Opcode::SetAttribute &&
53 inst.Arg(0).Attribute() == IR::Attribute::Layer) {
54 requires_layer_emulation = true;
55 inst.SetArg(0, IR::Value{layer_attribute});
56 }
57 }
58 }
59
60 if (requires_layer_emulation) {
61 program.info.requires_layer_emulation = true;
62 program.info.emulated_layer = layer_attribute;
63 program.info.stores.Set(IR::Attribute::Layer, false);
64 program.info.stores.Set(layer_attribute, true);
65 }
66}
67
68} // namespace Shader::Optimization
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h
index 586a0668f..11bfe801a 100644
--- a/src/shader_recompiler/ir_opt/passes.h
+++ b/src/shader_recompiler/ir_opt/passes.h
@@ -23,6 +23,7 @@ void RescalingPass(IR::Program& program);
23void SsaRewritePass(IR::Program& program); 23void SsaRewritePass(IR::Program& program);
24void PositionPass(Environment& env, IR::Program& program); 24void PositionPass(Environment& env, IR::Program& program);
25void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info); 25void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo& host_info);
26void LayerPass(IR::Program& program, const HostTranslateInfo& host_info);
26void VerificationPass(const IR::Program& program); 27void VerificationPass(const IR::Program& program);
27 28
28// Dual Vertex 29// Dual Vertex
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index ee6252bb5..d9c6e92db 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -204,6 +204,9 @@ struct Info {
204 u32 nvn_buffer_base{}; 204 u32 nvn_buffer_base{};
205 std::bitset<16> nvn_buffer_used{}; 205 std::bitset<16> nvn_buffer_used{};
206 206
207 bool requires_layer_emulation{};
208 IR::Attribute emulated_layer{};
209
207 boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS> 210 boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS>
208 constant_buffer_descriptors; 211 constant_buffer_descriptors;
209 boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors; 212 boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors;