summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar FengChen2022-09-01 22:05:11 +0800
committerGravatar FengChen2022-09-20 11:55:43 +0800
commit9a95c7fa14bdfc14aacea92896c8ae8533918fe8 (patch)
tree3beb2289136a59134195d35e37d4b56b294a3081 /src
parentMerge pull request #8841 from zhaobot/tx-update-20220901035349 (diff)
downloadyuzu-9a95c7fa14bdfc14aacea92896c8ae8533918fe8.tar.gz
yuzu-9a95c7fa14bdfc14aacea92896c8ae8533918fe8.tar.xz
yuzu-9a95c7fa14bdfc14aacea92896c8ae8533918fe8.zip
video_core: Generate mipmap texture by drawing
Diffstat (limited to 'src')
-rw-r--r--src/shader_recompiler/CMakeLists.txt1
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm.cpp3
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp4
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_instructions.h1
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp4
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_instructions.h1
-rw-r--r--src/shader_recompiler/backend/glsl/glsl_emit_context.cpp3
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.h4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp12
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_instructions.h1
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp31
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h4
-rw-r--r--src/shader_recompiler/environment.h2
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.cpp8
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.h3
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.inc1
-rw-r--r--src/shader_recompiler/frontend/maxwell/translate_program.cpp2
-rw-r--r--src/shader_recompiler/ir_opt/passes.h1
-rw-r--r--src/shader_recompiler/ir_opt/position_pass.cpp77
-rw-r--r--src/shader_recompiler/shader_info.h1
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp11
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp2
-rw-r--r--src/video_core/renderer_vulkan/pipeline_helper.h10
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp24
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp16
-rw-r--r--src/video_core/shader_environment.cpp17
-rw-r--r--src/video_core/shader_environment.h9
29 files changed, 259 insertions, 8 deletions
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt
index af8e51fe8..189e5cb17 100644
--- a/src/shader_recompiler/CMakeLists.txt
+++ b/src/shader_recompiler/CMakeLists.txt
@@ -224,6 +224,7 @@ add_library(shader_recompiler STATIC
224 ir_opt/lower_fp16_to_fp32.cpp 224 ir_opt/lower_fp16_to_fp32.cpp
225 ir_opt/lower_int64_to_int32.cpp 225 ir_opt/lower_int64_to_int32.cpp
226 ir_opt/passes.h 226 ir_opt/passes.h
227 ir_opt/position_pass.cpp
227 ir_opt/rescaling_pass.cpp 228 ir_opt/rescaling_pass.cpp
228 ir_opt/ssa_rewrite_pass.cpp 229 ir_opt/ssa_rewrite_pass.cpp
229 ir_opt/texture_pass.cpp 230 ir_opt/texture_pass.cpp
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
index 97a6b383b..1419207e8 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
@@ -450,6 +450,9 @@ std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, I
450 if (program.info.uses_rescaling_uniform) { 450 if (program.info.uses_rescaling_uniform) {
451 header += "PARAM scaling[1]={program.local[0..0]};"; 451 header += "PARAM scaling[1]={program.local[0..0]};";
452 } 452 }
453 if (program.info.uses_render_area) {
454 header += "PARAM render_area[1]={program.local[1..1]};";
455 }
453 header += "TEMP "; 456 header += "TEMP ";
454 for (size_t index = 0; index < ctx.reg_alloc.NumUsedRegisters(); ++index) { 457 for (size_t index = 0; index < ctx.reg_alloc.NumUsedRegisters(); ++index) {
455 header += fmt::format("R{},", index); 458 header += fmt::format("R{},", index);
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
index b5c08d611..ee0b0f53d 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_context_get_set.cpp
@@ -379,6 +379,10 @@ void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
379 ctx.Add("MOV.F {}.x,scaling[0].z;", inst); 379 ctx.Add("MOV.F {}.x,scaling[0].z;", inst);
380} 380}
381 381
382void EmitRenderArea(EmitContext& ctx, IR::Inst& inst) {
383 ctx.Add("MOV.F {},render_area[0];", inst);
384}
385
382void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) { 386void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset) {
383 ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset); 387 ctx.Add("MOV.U {},lmem[{}].x;", inst, word_offset);
384} 388}
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
index 8b0ac3031..c08f48ed9 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
@@ -73,6 +73,7 @@ void EmitSampleId(EmitContext& ctx, IR::Inst& inst);
73void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); 73void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst);
74void EmitYDirection(EmitContext& ctx, IR::Inst& inst); 74void EmitYDirection(EmitContext& ctx, IR::Inst& inst);
75void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst); 75void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst);
76void EmitRenderArea(EmitContext& ctx, IR::Inst& inst);
76void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset); 77void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, ScalarU32 word_offset);
77void EmitWriteLocal(EmitContext& ctx, ScalarU32 word_offset, ScalarU32 value); 78void EmitWriteLocal(EmitContext& ctx, ScalarU32 word_offset, ScalarU32 value);
78void EmitUndefU1(EmitContext& ctx, IR::Inst& inst); 79void EmitUndefU1(EmitContext& ctx, IR::Inst& inst);
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
index fad8d1e30..d7c845469 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp
@@ -416,6 +416,10 @@ void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
416 ctx.AddF32("{}=scaling.z;", inst); 416 ctx.AddF32("{}=scaling.z;", inst);
417} 417}
418 418
419void EmitRenderArea(EmitContext& ctx, IR::Inst& inst) {
420 ctx.AddF32x4("{}=render_area;", inst);
421}
422
419void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) { 423void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) {
420 ctx.AddU32("{}=lmem[{}];", inst, word_offset); 424 ctx.AddU32("{}=lmem[{}];", inst, word_offset);
421} 425}
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
index 639691ba6..3c8bcb7e9 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
@@ -87,6 +87,7 @@ void EmitSampleId(EmitContext& ctx, IR::Inst& inst);
87void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst); 87void EmitIsHelperInvocation(EmitContext& ctx, IR::Inst& inst);
88void EmitYDirection(EmitContext& ctx, IR::Inst& inst); 88void EmitYDirection(EmitContext& ctx, IR::Inst& inst);
89void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst); 89void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst);
90void EmitRenderArea(EmitContext& ctx, IR::Inst& inst);
90void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset); 91void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset);
91void EmitWriteLocal(EmitContext& ctx, std::string_view word_offset, std::string_view value); 92void EmitWriteLocal(EmitContext& ctx, std::string_view word_offset, std::string_view value);
92void EmitUndefU1(EmitContext& ctx, IR::Inst& inst); 93void EmitUndefU1(EmitContext& ctx, IR::Inst& inst);
diff --git a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
index c767a9dc3..5d01ec0cd 100644
--- a/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/glsl_emit_context.cpp
@@ -358,6 +358,9 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
358 if (info.uses_rescaling_uniform) { 358 if (info.uses_rescaling_uniform) {
359 header += "layout(location=0) uniform vec4 scaling;"; 359 header += "layout(location=0) uniform vec4 scaling;";
360 } 360 }
361 if (info.uses_render_area) {
362 header += "layout(location=1) uniform vec4 render_area;";
363 }
361 DefineConstantBuffers(bindings); 364 DefineConstantBuffers(bindings);
362 DefineConstantBufferIndirect(); 365 DefineConstantBufferIndirect();
363 DefineStorageBuffers(bindings); 366 DefineStorageBuffers(bindings);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
index 7567b6fc9..937881484 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -23,8 +23,12 @@ struct RescalingLayout {
23 alignas(16) std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images; 23 alignas(16) std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images;
24 u32 down_factor; 24 u32 down_factor;
25}; 25};
26struct RenderAreaLayout {
27 std::array<f32, 4> render_area;
28};
26constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures); 29constexpr u32 RESCALING_LAYOUT_WORDS_OFFSET = offsetof(RescalingLayout, rescaling_textures);
27constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor); 30constexpr u32 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET = offsetof(RescalingLayout, down_factor);
31constexpr u32 RENDERAREA_LAYOUT_OFFSET = offsetof(RenderAreaLayout, render_area);
28 32
29[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, 33[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
30 IR::Program& program, Bindings& bindings); 34 IR::Program& program, Bindings& bindings);
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 2c68aba39..a4751b42d 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
@@ -353,7 +353,6 @@ Id EmitGetAttribute(EmitContext& ctx, IR::Attribute attr, Id vertex) {
353 case IR::Attribute::TessellationEvaluationPointV: 353 case IR::Attribute::TessellationEvaluationPointV:
354 return ctx.OpLoad(ctx.F32[1], 354 return ctx.OpLoad(ctx.F32[1],
355 ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.Const(1U))); 355 ctx.OpAccessChain(ctx.input_f32, ctx.tess_coord, ctx.Const(1U)));
356
357 default: 356 default:
358 throw NotImplementedException("Read attribute {}", attr); 357 throw NotImplementedException("Read attribute {}", attr);
359 } 358 }
@@ -537,6 +536,17 @@ Id EmitResolutionDownFactor(EmitContext& ctx) {
537 } 536 }
538} 537}
539 538
539Id EmitRenderArea(EmitContext& ctx) {
540 if (ctx.profile.unified_descriptor_binding) {
541 const Id pointer_type{ctx.TypePointer(spv::StorageClass::PushConstant, ctx.F32[4])};
542 const Id index{ctx.Const(ctx.render_are_member_index)};
543 const Id pointer{ctx.OpAccessChain(pointer_type, ctx.render_area_push_constant, index)};
544 return ctx.OpLoad(ctx.F32[4], pointer);
545 } else {
546 throw NotImplementedException("SPIR-V Instruction");
547 }
548}
549
540Id EmitLoadLocal(EmitContext& ctx, Id word_offset) { 550Id EmitLoadLocal(EmitContext& ctx, Id word_offset) {
541 const Id pointer{ctx.OpAccessChain(ctx.private_u32, ctx.local_memory, word_offset)}; 551 const Id pointer{ctx.OpAccessChain(ctx.private_u32, ctx.local_memory, word_offset)};
542 return ctx.OpLoad(ctx.U32[1], pointer); 552 return ctx.OpLoad(ctx.U32[1], pointer);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index 984d072b4..86dd3c4f3 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -76,6 +76,7 @@ Id EmitSampleId(EmitContext& ctx);
76Id EmitIsHelperInvocation(EmitContext& ctx); 76Id EmitIsHelperInvocation(EmitContext& ctx);
77Id EmitYDirection(EmitContext& ctx); 77Id EmitYDirection(EmitContext& ctx);
78Id EmitResolutionDownFactor(EmitContext& ctx); 78Id EmitResolutionDownFactor(EmitContext& ctx);
79Id EmitRenderArea(EmitContext& ctx);
79Id EmitLoadLocal(EmitContext& ctx, Id word_offset); 80Id EmitLoadLocal(EmitContext& ctx, Id word_offset);
80void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value); 81void EmitWriteLocal(EmitContext& ctx, Id word_offset, Id value);
81Id EmitUndefU1(EmitContext& ctx); 82Id EmitUndefU1(EmitContext& ctx);
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index aecc4c612..c26ad8f93 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -473,6 +473,7 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf
473 DefineAttributeMemAccess(program.info); 473 DefineAttributeMemAccess(program.info);
474 DefineGlobalMemoryFunctions(program.info); 474 DefineGlobalMemoryFunctions(program.info);
475 DefineRescalingInput(program.info); 475 DefineRescalingInput(program.info);
476 DefineRenderArea(program.info);
476} 477}
477 478
478EmitContext::~EmitContext() = default; 479EmitContext::~EmitContext() = default;
@@ -982,6 +983,36 @@ void EmitContext::DefineRescalingInputUniformConstant() {
982 } 983 }
983} 984}
984 985
986void EmitContext::DefineRenderArea(const Info& info) {
987 if (!info.uses_render_area) {
988 return;
989 }
990
991 if (profile.unified_descriptor_binding) {
992 boost::container::static_vector<Id, 1> members{};
993 u32 member_index{0};
994
995 members.push_back(F32[4]);
996 render_are_member_index = member_index++;
997
998 const Id push_constant_struct{TypeStruct(std::span(members.data(), members.size()))};
999 Decorate(push_constant_struct, spv::Decoration::Block);
1000 Name(push_constant_struct, "RenderAreaInfo");
1001
1002 MemberDecorate(push_constant_struct, render_are_member_index, spv::Decoration::Offset, 0);
1003 MemberName(push_constant_struct, render_are_member_index, "render_area");
1004
1005 const Id pointer_type{TypePointer(spv::StorageClass::PushConstant, push_constant_struct)};
1006 render_area_push_constant =
1007 AddGlobalVariable(pointer_type, spv::StorageClass::PushConstant);
1008 Name(render_area_push_constant, "render_area_push_constants");
1009
1010 if (profile.supported_spirv >= 0x00010400) {
1011 interfaces.push_back(render_area_push_constant);
1012 }
1013 }
1014}
1015
985void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) { 1016void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
986 if (info.constant_buffer_descriptors.empty()) { 1017 if (info.constant_buffer_descriptors.empty()) {
987 return; 1018 return;
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index bc25b8b84..c86e50911 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
@@ -243,6 +243,9 @@ public:
243 u32 texture_rescaling_index{}; 243 u32 texture_rescaling_index{};
244 u32 image_rescaling_index{}; 244 u32 image_rescaling_index{};
245 245
246 Id render_area_push_constant{};
247 u32 render_are_member_index{};
248
246 Id local_memory{}; 249 Id local_memory{};
247 250
248 Id shared_memory_u8{}; 251 Id shared_memory_u8{};
@@ -318,6 +321,7 @@ private:
318 void DefineRescalingInput(const Info& info); 321 void DefineRescalingInput(const Info& info);
319 void DefineRescalingInputPushConstant(); 322 void DefineRescalingInputPushConstant();
320 void DefineRescalingInputUniformConstant(); 323 void DefineRescalingInputUniformConstant();
324 void DefineRenderArea(const Info& info);
321 325
322 void DefineInputs(const IR::Program& program); 326 void DefineInputs(const IR::Program& program);
323 void DefineOutputs(const IR::Program& program); 327 void DefineOutputs(const IR::Program& program);
diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h
index 9729d48c6..980e0e54c 100644
--- a/src/shader_recompiler/environment.h
+++ b/src/shader_recompiler/environment.h
@@ -22,6 +22,8 @@ public:
22 22
23 [[nodiscard]] virtual TextureType ReadTextureType(u32 raw_handle) = 0; 23 [[nodiscard]] virtual TextureType ReadTextureType(u32 raw_handle) = 0;
24 24
25 [[nodiscard]] virtual u32 ReadViewportTransformState() = 0;
26
25 [[nodiscard]] virtual u32 TextureBoundBuffer() const = 0; 27 [[nodiscard]] virtual u32 TextureBoundBuffer() const = 0;
26 28
27 [[nodiscard]] virtual u32 LocalMemorySize() const = 0; 29 [[nodiscard]] virtual u32 LocalMemorySize() const = 0;
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index 11086ed8c..de1ce90a3 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -378,6 +378,14 @@ F32 IREmitter::ResolutionDownFactor() {
378 return Inst<F32>(Opcode::ResolutionDownFactor); 378 return Inst<F32>(Opcode::ResolutionDownFactor);
379} 379}
380 380
381F32 IREmitter::RenderAreaWidth() {
382 return F32(CompositeExtract(Inst<Value>(Opcode::RenderArea), 0));
383}
384
385F32 IREmitter::RenderAreaHeight() {
386 return F32(CompositeExtract(Inst<Value>(Opcode::RenderArea), 1));
387}
388
381U32 IREmitter::LaneId() { 389U32 IREmitter::LaneId() {
382 return Inst<U32>(Opcode::LaneId); 390 return Inst<U32>(Opcode::LaneId);
383} 391}
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index 25839a371..f163c18d9 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -103,6 +103,9 @@ public:
103 103
104 [[nodiscard]] F32 ResolutionDownFactor(); 104 [[nodiscard]] F32 ResolutionDownFactor();
105 105
106 [[nodiscard]] F32 RenderAreaWidth();
107 [[nodiscard]] F32 RenderAreaHeight();
108
106 [[nodiscard]] U32 LaneId(); 109 [[nodiscard]] U32 LaneId();
107 110
108 [[nodiscard]] U32 LoadGlobalU8(const U64& address); 111 [[nodiscard]] U32 LoadGlobalU8(const U64& address);
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index 86410ddfc..f3038f298 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -63,6 +63,7 @@ OPCODE(SampleId, U32,
63OPCODE(IsHelperInvocation, U1, ) 63OPCODE(IsHelperInvocation, U1, )
64OPCODE(YDirection, F32, ) 64OPCODE(YDirection, F32, )
65OPCODE(ResolutionDownFactor, F32, ) 65OPCODE(ResolutionDownFactor, F32, )
66OPCODE(RenderArea, F32x4, )
66 67
67// Undefined 68// Undefined
68OPCODE(UndefU1, U1, ) 69OPCODE(UndefU1, U1, )
diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index 77efb4f57..dafb9deee 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -213,6 +213,8 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
213 213
214 Optimization::ConstantPropagationPass(program); 214 Optimization::ConstantPropagationPass(program);
215 215
216 Optimization::PositionPass(env, program);
217
216 Optimization::GlobalMemoryToStorageBufferPass(program); 218 Optimization::GlobalMemoryToStorageBufferPass(program);
217 Optimization::TexturePass(env, program); 219 Optimization::TexturePass(env, program);
218 220
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h
index 6ff8e4266..24f609d69 100644
--- a/src/shader_recompiler/ir_opt/passes.h
+++ b/src/shader_recompiler/ir_opt/passes.h
@@ -17,6 +17,7 @@ void LowerFp16ToFp32(IR::Program& program);
17void LowerInt64ToInt32(IR::Program& program); 17void LowerInt64ToInt32(IR::Program& program);
18void RescalingPass(IR::Program& program); 18void RescalingPass(IR::Program& program);
19void SsaRewritePass(IR::Program& program); 19void SsaRewritePass(IR::Program& program);
20void PositionPass(Environment& env, IR::Program& program);
20void TexturePass(Environment& env, IR::Program& program); 21void TexturePass(Environment& env, IR::Program& program);
21void VerificationPass(const IR::Program& program); 22void VerificationPass(const IR::Program& program);
22 23
diff --git a/src/shader_recompiler/ir_opt/position_pass.cpp b/src/shader_recompiler/ir_opt/position_pass.cpp
new file mode 100644
index 000000000..3c20b7189
--- /dev/null
+++ b/src/shader_recompiler/ir_opt/position_pass.cpp
@@ -0,0 +1,77 @@
1// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <boost/container/small_vector.hpp>
5
6#include "shader_recompiler/frontend/ir/basic_block.h"
7#include "shader_recompiler/frontend/ir/ir_emitter.h"
8#include "shader_recompiler/frontend/ir/value.h"
9#include "shader_recompiler/ir_opt/passes.h"
10
11namespace Shader::Optimization {
12
13namespace {
14struct PositionInst {
15 IR::Inst* inst;
16 IR::Block* block;
17 IR::Attribute attr;
18};
19using PositionInstVector = boost::container::small_vector<PositionInst, 24>;
20} // Anonymous namespace
21
22void PositionPass(Environment& env, IR::Program& program) {
23 if (env.ShaderStage() != Stage::VertexB || env.ReadViewportTransformState()) {
24 return;
25 }
26
27 Info& info{program.info};
28 info.uses_render_area = true;
29
30 PositionInstVector to_replace;
31 for (IR::Block* const block : program.post_order_blocks) {
32 for (IR::Inst& inst : block->Instructions()) {
33 switch (inst.GetOpcode()) {
34 case IR::Opcode::SetAttribute: {
35 const IR::Attribute attr{inst.Arg(0).Attribute()};
36 switch (attr) {
37 case IR::Attribute::PositionX:
38 case IR::Attribute::PositionY: {
39 to_replace.push_back(PositionInst{.inst = &inst, .block = block, .attr = attr});
40 break;
41 }
42 default:
43 break;
44 }
45 break;
46 }
47 default:
48 break;
49 }
50 }
51 }
52
53 for (PositionInst& position_inst : to_replace) {
54 IR::IREmitter ir{*position_inst.block,
55 IR::Block::InstructionList::s_iterator_to(*position_inst.inst)};
56 const IR::F32 value(position_inst.inst->Arg(1));
57 const IR::F32F64 scale(ir.Imm32(2.f));
58 const IR::F32 negative_one{ir.Imm32(-1.f)};
59 switch (position_inst.attr) {
60 case IR::Attribute::PositionX: {
61 position_inst.inst->SetArg(
62 1,
63 ir.FPFma(value, ir.FPMul(ir.FPRecip(ir.RenderAreaWidth()), scale), negative_one));
64 break;
65 }
66 case IR::Attribute::PositionY: {
67 position_inst.inst->SetArg(
68 1,
69 ir.FPFma(value, ir.FPMul(ir.FPRecip(ir.RenderAreaHeight()), scale), negative_one));
70 break;
71 }
72 default:
73 break;
74 }
75 }
76}
77} // namespace Shader::Optimization
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index f5690805c..e4b5ba567 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -175,6 +175,7 @@ struct Info {
175 bool uses_shadow_lod{}; 175 bool uses_shadow_lod{};
176 bool uses_rescaling_uniform{}; 176 bool uses_rescaling_uniform{};
177 bool uses_cbuf_indirect{}; 177 bool uses_cbuf_indirect{};
178 bool uses_render_area{};
178 179
179 IR::Type used_constant_buffer_types{}; 180 IR::Type used_constant_buffer_types{};
180 IR::Type used_storage_buffer_types{}; 181 IR::Type used_storage_buffer_types{};
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index 67eae369d..c297f7121 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -502,6 +502,17 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
502 float_image_scaling_mask, down_factor, 0.0f); 502 float_image_scaling_mask, down_factor, 0.0f);
503 } 503 }
504 } 504 }
505 if (info.uses_render_area) {
506 const auto render_area_width(static_cast<GLfloat>(regs.render_area.width));
507 const auto render_area_height(static_cast<GLfloat>(regs.render_area.height));
508 if (use_assembly) {
509 glProgramLocalParameter4fARB(AssemblyStage(stage), 1, render_area_width,
510 render_area_height, 0.0f, 0.0f);
511 } else {
512 glProgramUniform4f(source_programs[stage].handle, 1, render_area_width,
513 render_area_height, 0.0f, 0.0f);
514 }
515 }
505 }}; 516 }};
506 if constexpr (Spec::enabled_stages[0]) { 517 if constexpr (Spec::enabled_stages[0]) {
507 prepare_stage(0); 518 prepare_stage(0);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index a0d048b0b..f018333af 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -622,6 +622,16 @@ void RasterizerOpenGL::SyncViewport() {
622 } 622 }
623 flags[Dirty::Viewport0 + index] = false; 623 flags[Dirty::Viewport0 + index] = false;
624 624
625 if (!regs.viewport_transform_enabled) {
626 const auto x = static_cast<GLfloat>(regs.render_area.x);
627 const auto y = static_cast<GLfloat>(regs.render_area.y);
628 const auto width = static_cast<GLfloat>(regs.render_area.width);
629 const auto height = static_cast<GLfloat>(regs.render_area.height);
630 glViewportIndexedf(static_cast<GLuint>(index), x, y, width != 0.0f ? width : 1.0f,
631 height != 0.0f ? height : 1.0f);
632 continue;
633 }
634
625 const auto& src = regs.viewport_transform[index]; 635 const auto& src = regs.viewport_transform[index];
626 GLfloat x = conv(src.translate_x - src.scale_x); 636 GLfloat x = conv(src.translate_x - src.scale_x);
627 GLfloat y = conv(src.translate_y - src.scale_y); 637 GLfloat y = conv(src.translate_y - src.scale_y);
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 1ad56d9e7..7e76f679f 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -49,7 +49,7 @@ using VideoCommon::LoadPipelines;
49using VideoCommon::SerializePipeline; 49using VideoCommon::SerializePipeline;
50using Context = ShaderContext::Context; 50using Context = ShaderContext::Context;
51 51
52constexpr u32 CACHE_VERSION = 5; 52constexpr u32 CACHE_VERSION = 7;
53 53
54template <typename Container> 54template <typename Container>
55auto MakeSpan(Container& container) { 55auto MakeSpan(Container& container) {
diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h
index b24f3424a..b7843e995 100644
--- a/src/video_core/renderer_vulkan/pipeline_helper.h
+++ b/src/video_core/renderer_vulkan/pipeline_helper.h
@@ -68,13 +68,15 @@ public:
68 } 68 }
69 69
70 vk::PipelineLayout CreatePipelineLayout(VkDescriptorSetLayout descriptor_set_layout) const { 70 vk::PipelineLayout CreatePipelineLayout(VkDescriptorSetLayout descriptor_set_layout) const {
71 using Shader::Backend::SPIRV::RenderAreaLayout;
71 using Shader::Backend::SPIRV::RescalingLayout; 72 using Shader::Backend::SPIRV::RescalingLayout;
72 const u32 size_offset = is_compute ? sizeof(RescalingLayout::down_factor) : 0u; 73 const u32 size_offset = is_compute ? sizeof(RescalingLayout::down_factor) : 0u;
73 const VkPushConstantRange range{ 74 const VkPushConstantRange range{
74 .stageFlags = static_cast<VkShaderStageFlags>( 75 .stageFlags = static_cast<VkShaderStageFlags>(
75 is_compute ? VK_SHADER_STAGE_COMPUTE_BIT : VK_SHADER_STAGE_ALL_GRAPHICS), 76 is_compute ? VK_SHADER_STAGE_COMPUTE_BIT : VK_SHADER_STAGE_ALL_GRAPHICS),
76 .offset = 0, 77 .offset = 0,
77 .size = static_cast<u32>(sizeof(RescalingLayout)) - size_offset, 78 .size = static_cast<u32>(sizeof(RescalingLayout)) - size_offset +
79 static_cast<u32>(sizeof(RenderAreaLayout)),
78 }; 80 };
79 return device->GetLogical().CreatePipelineLayout({ 81 return device->GetLogical().CreatePipelineLayout({
80 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, 82 .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
@@ -167,6 +169,12 @@ private:
167 u32 image_bit{1u}; 169 u32 image_bit{1u};
168}; 170};
169 171
172class RenderAreaPushConstant {
173public:
174 bool uses_render_area{};
175 std::array<f32, 4> words{};
176};
177
170inline void PushImageDescriptors(TextureCache& texture_cache, 178inline void PushImageDescriptors(TextureCache& texture_cache,
171 UpdateDescriptorQueue& update_descriptor_queue, 179 UpdateDescriptorQueue& update_descriptor_queue,
172 const Shader::Info& info, RescalingPushConstant& rescaling, 180 const Shader::Info& info, RescalingPushConstant& rescaling,
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 5aca8f038..7eb623a7c 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -31,6 +31,7 @@ namespace {
31using boost::container::small_vector; 31using boost::container::small_vector;
32using boost::container::static_vector; 32using boost::container::static_vector;
33using Shader::ImageBufferDescriptor; 33using Shader::ImageBufferDescriptor;
34using Shader::Backend::SPIRV::RENDERAREA_LAYOUT_OFFSET;
34using Shader::Backend::SPIRV::RESCALING_LAYOUT_DOWN_FACTOR_OFFSET; 35using Shader::Backend::SPIRV::RESCALING_LAYOUT_DOWN_FACTOR_OFFSET;
35using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET; 36using Shader::Backend::SPIRV::RESCALING_LAYOUT_WORDS_OFFSET;
36using Tegra::Texture::TexturePair; 37using Tegra::Texture::TexturePair;
@@ -433,12 +434,19 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
433 update_descriptor_queue.Acquire(); 434 update_descriptor_queue.Acquire();
434 435
435 RescalingPushConstant rescaling; 436 RescalingPushConstant rescaling;
437 RenderAreaPushConstant render_area;
436 const VkSampler* samplers_it{samplers.data()}; 438 const VkSampler* samplers_it{samplers.data()};
437 const VideoCommon::ImageViewInOut* views_it{views.data()}; 439 const VideoCommon::ImageViewInOut* views_it{views.data()};
438 const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE { 440 const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE {
439 buffer_cache.BindHostStageBuffers(stage); 441 buffer_cache.BindHostStageBuffers(stage);
440 PushImageDescriptors(texture_cache, update_descriptor_queue, stage_infos[stage], rescaling, 442 PushImageDescriptors(texture_cache, update_descriptor_queue, stage_infos[stage], rescaling,
441 samplers_it, views_it); 443 samplers_it, views_it);
444 const auto& info{stage_infos[0]};
445 if (info.uses_render_area) {
446 render_area.uses_render_area = true;
447 render_area.words = {static_cast<float>(regs.render_area.width),
448 static_cast<float>(regs.render_area.height)};
449 }
442 }}; 450 }};
443 if constexpr (Spec::enabled_stages[0]) { 451 if constexpr (Spec::enabled_stages[0]) {
444 prepare_stage(0); 452 prepare_stage(0);
@@ -455,10 +463,11 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
455 if constexpr (Spec::enabled_stages[4]) { 463 if constexpr (Spec::enabled_stages[4]) {
456 prepare_stage(4); 464 prepare_stage(4);
457 } 465 }
458 ConfigureDraw(rescaling); 466 ConfigureDraw(rescaling, render_area);
459} 467}
460 468
461void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) { 469void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling,
470 const RenderAreaPushConstant& render_are) {
462 texture_cache.UpdateRenderTargets(false); 471 texture_cache.UpdateRenderTargets(false);
463 scheduler.RequestRenderpass(texture_cache.GetFramebuffer()); 472 scheduler.RequestRenderpass(texture_cache.GetFramebuffer());
464 473
@@ -474,7 +483,9 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) {
474 const bool bind_pipeline{scheduler.UpdateGraphicsPipeline(this)}; 483 const bool bind_pipeline{scheduler.UpdateGraphicsPipeline(this)};
475 const void* const descriptor_data{update_descriptor_queue.UpdateData()}; 484 const void* const descriptor_data{update_descriptor_queue.UpdateData()};
476 scheduler.Record([this, descriptor_data, bind_pipeline, rescaling_data = rescaling.Data(), 485 scheduler.Record([this, descriptor_data, bind_pipeline, rescaling_data = rescaling.Data(),
477 is_rescaling, update_rescaling](vk::CommandBuffer cmdbuf) { 486 is_rescaling, update_rescaling,
487 uses_render_area = render_are.uses_render_area,
488 render_area_data = render_are.words](vk::CommandBuffer cmdbuf) {
478 if (bind_pipeline) { 489 if (bind_pipeline) {
479 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline); 490 cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline);
480 } 491 }
@@ -483,11 +494,16 @@ void GraphicsPipeline::ConfigureDraw(const RescalingPushConstant& rescaling) {
483 rescaling_data.data()); 494 rescaling_data.data());
484 if (update_rescaling) { 495 if (update_rescaling) {
485 const f32 config_down_factor{Settings::values.resolution_info.down_factor}; 496 const f32 config_down_factor{Settings::values.resolution_info.down_factor};
486 const f32 scale_down_factor{is_rescaling ? config_down_factor : 1.0f}; 497 const f32 scale_down_factor{is_rescaling ? config_down_factor : 2.0f};
487 cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_ALL_GRAPHICS, 498 cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_ALL_GRAPHICS,
488 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET, sizeof(scale_down_factor), 499 RESCALING_LAYOUT_DOWN_FACTOR_OFFSET, sizeof(scale_down_factor),
489 &scale_down_factor); 500 &scale_down_factor);
490 } 501 }
502 if (uses_render_area) {
503 cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_ALL_GRAPHICS,
504 RENDERAREA_LAYOUT_OFFSET, sizeof(render_area_data),
505 &render_area_data);
506 }
491 if (!descriptor_set_layout) { 507 if (!descriptor_set_layout) {
492 return; 508 return;
493 } 509 }
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
index e8949a9ab..8842f62d7 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.h
@@ -62,6 +62,7 @@ class Device;
62class PipelineStatistics; 62class PipelineStatistics;
63class RenderPassCache; 63class RenderPassCache;
64class RescalingPushConstant; 64class RescalingPushConstant;
65class RenderAreaPushConstant;
65class Scheduler; 66class Scheduler;
66class UpdateDescriptorQueue; 67class UpdateDescriptorQueue;
67 68
@@ -113,7 +114,8 @@ private:
113 template <typename Spec> 114 template <typename Spec>
114 void ConfigureImpl(bool is_indexed); 115 void ConfigureImpl(bool is_indexed);
115 116
116 void ConfigureDraw(const RescalingPushConstant& rescaling); 117 void ConfigureDraw(const RescalingPushConstant& rescaling,
118 const RenderAreaPushConstant& render_are);
117 119
118 void MakePipeline(VkRenderPass render_pass); 120 void MakePipeline(VkRenderPass render_pass);
119 121
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 7e40c2df1..bf9f825f4 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -682,6 +682,22 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg
682 if (!state_tracker.TouchViewports()) { 682 if (!state_tracker.TouchViewports()) {
683 return; 683 return;
684 } 684 }
685 if (!regs.viewport_transform_enabled) {
686 const auto x = static_cast<float>(regs.render_area.x);
687 const auto y = static_cast<float>(regs.render_area.y);
688 const auto width = static_cast<float>(regs.render_area.width);
689 const auto height = static_cast<float>(regs.render_area.height);
690 VkViewport viewport{
691 .x = x,
692 .y = y,
693 .width = width != 0.0f ? width : 1.0f,
694 .height = height != 0.0f ? height : 1.0f,
695 .minDepth = 0.0f,
696 .maxDepth = 1.0f,
697 };
698 scheduler.Record([viewport](vk::CommandBuffer cmdbuf) { cmdbuf.SetViewport(0, viewport); });
699 return;
700 }
685 const bool is_rescaling{texture_cache.IsRescaling()}; 701 const bool is_rescaling{texture_cache.IsRescaling()};
686 const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f; 702 const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f;
687 const std::array viewports{ 703 const std::array viewports{
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp
index 808d88eec..3ead06dd6 100644
--- a/src/video_core/shader_environment.cpp
+++ b/src/video_core/shader_environment.cpp
@@ -191,6 +191,8 @@ void GenericEnvironment::Serialize(std::ofstream& file) const {
191 .write(reinterpret_cast<const char*>(&start_address), sizeof(start_address)) 191 .write(reinterpret_cast<const char*>(&start_address), sizeof(start_address))
192 .write(reinterpret_cast<const char*>(&cached_lowest), sizeof(cached_lowest)) 192 .write(reinterpret_cast<const char*>(&cached_lowest), sizeof(cached_lowest))
193 .write(reinterpret_cast<const char*>(&cached_highest), sizeof(cached_highest)) 193 .write(reinterpret_cast<const char*>(&cached_highest), sizeof(cached_highest))
194 .write(reinterpret_cast<const char*>(&viewport_transform_state),
195 sizeof(viewport_transform_state))
194 .write(reinterpret_cast<const char*>(&stage), sizeof(stage)) 196 .write(reinterpret_cast<const char*>(&stage), sizeof(stage))
195 .write(reinterpret_cast<const char*>(code.data()), code_size); 197 .write(reinterpret_cast<const char*>(code.data()), code_size);
196 for (const auto& [key, type] : texture_types) { 198 for (const auto& [key, type] : texture_types) {
@@ -311,6 +313,12 @@ Shader::TextureType GraphicsEnvironment::ReadTextureType(u32 handle) {
311 return ReadTextureTypeImpl(regs.tic.Address(), regs.tic.limit, via_header_index, handle); 313 return ReadTextureTypeImpl(regs.tic.Address(), regs.tic.limit, via_header_index, handle);
312} 314}
313 315
316u32 GraphicsEnvironment::ReadViewportTransformState() {
317 const auto& regs{maxwell3d->regs};
318 viewport_transform_state = regs.viewport_transform_enabled;
319 return viewport_transform_state;
320}
321
314ComputeEnvironment::ComputeEnvironment(Tegra::Engines::KeplerCompute& kepler_compute_, 322ComputeEnvironment::ComputeEnvironment(Tegra::Engines::KeplerCompute& kepler_compute_,
315 Tegra::MemoryManager& gpu_memory_, GPUVAddr program_base_, 323 Tegra::MemoryManager& gpu_memory_, GPUVAddr program_base_,
316 u32 start_address_) 324 u32 start_address_)
@@ -342,6 +350,10 @@ Shader::TextureType ComputeEnvironment::ReadTextureType(u32 handle) {
342 return ReadTextureTypeImpl(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle); 350 return ReadTextureTypeImpl(regs.tic.Address(), regs.tic.limit, qmd.linked_tsc != 0, handle);
343} 351}
344 352
353u32 ComputeEnvironment::ReadViewportTransformState() {
354 return viewport_transform_state;
355}
356
345void FileEnvironment::Deserialize(std::ifstream& file) { 357void FileEnvironment::Deserialize(std::ifstream& file) {
346 u64 code_size{}; 358 u64 code_size{};
347 u64 num_texture_types{}; 359 u64 num_texture_types{};
@@ -354,6 +366,7 @@ void FileEnvironment::Deserialize(std::ifstream& file) {
354 .read(reinterpret_cast<char*>(&start_address), sizeof(start_address)) 366 .read(reinterpret_cast<char*>(&start_address), sizeof(start_address))
355 .read(reinterpret_cast<char*>(&read_lowest), sizeof(read_lowest)) 367 .read(reinterpret_cast<char*>(&read_lowest), sizeof(read_lowest))
356 .read(reinterpret_cast<char*>(&read_highest), sizeof(read_highest)) 368 .read(reinterpret_cast<char*>(&read_highest), sizeof(read_highest))
369 .read(reinterpret_cast<char*>(&viewport_transform_state), sizeof(viewport_transform_state))
357 .read(reinterpret_cast<char*>(&stage), sizeof(stage)); 370 .read(reinterpret_cast<char*>(&stage), sizeof(stage));
358 code = std::make_unique<u64[]>(Common::DivCeil(code_size, sizeof(u64))); 371 code = std::make_unique<u64[]>(Common::DivCeil(code_size, sizeof(u64)));
359 file.read(reinterpret_cast<char*>(code.get()), code_size); 372 file.read(reinterpret_cast<char*>(code.get()), code_size);
@@ -411,6 +424,10 @@ Shader::TextureType FileEnvironment::ReadTextureType(u32 handle) {
411 return it->second; 424 return it->second;
412} 425}
413 426
427u32 FileEnvironment::ReadViewportTransformState() {
428 return viewport_transform_state;
429}
430
414u32 FileEnvironment::LocalMemorySize() const { 431u32 FileEnvironment::LocalMemorySize() const {
415 return local_memory_size; 432 return local_memory_size;
416} 433}
diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h
index 5a145f33a..a0659fd7c 100644
--- a/src/video_core/shader_environment.h
+++ b/src/video_core/shader_environment.h
@@ -85,6 +85,8 @@ protected:
85 u32 cached_highest = 0; 85 u32 cached_highest = 0;
86 u32 initial_offset = 0; 86 u32 initial_offset = 0;
87 87
88 u32 viewport_transform_state = 1;
89
88 bool has_unbound_instructions = false; 90 bool has_unbound_instructions = false;
89}; 91};
90 92
@@ -102,6 +104,8 @@ public:
102 104
103 Shader::TextureType ReadTextureType(u32 handle) override; 105 Shader::TextureType ReadTextureType(u32 handle) override;
104 106
107 u32 ReadViewportTransformState() override;
108
105private: 109private:
106 Tegra::Engines::Maxwell3D* maxwell3d{}; 110 Tegra::Engines::Maxwell3D* maxwell3d{};
107 size_t stage_index{}; 111 size_t stage_index{};
@@ -120,6 +124,8 @@ public:
120 124
121 Shader::TextureType ReadTextureType(u32 handle) override; 125 Shader::TextureType ReadTextureType(u32 handle) override;
122 126
127 u32 ReadViewportTransformState() override;
128
123private: 129private:
124 Tegra::Engines::KeplerCompute* kepler_compute{}; 130 Tegra::Engines::KeplerCompute* kepler_compute{};
125}; 131};
@@ -143,6 +149,8 @@ public:
143 149
144 [[nodiscard]] Shader::TextureType ReadTextureType(u32 handle) override; 150 [[nodiscard]] Shader::TextureType ReadTextureType(u32 handle) override;
145 151
152 [[nodiscard]] u32 ReadViewportTransformState() override;
153
146 [[nodiscard]] u32 LocalMemorySize() const override; 154 [[nodiscard]] u32 LocalMemorySize() const override;
147 155
148 [[nodiscard]] u32 SharedMemorySize() const override; 156 [[nodiscard]] u32 SharedMemorySize() const override;
@@ -164,6 +172,7 @@ private:
164 u32 read_lowest{}; 172 u32 read_lowest{};
165 u32 read_highest{}; 173 u32 read_highest{};
166 u32 initial_offset{}; 174 u32 initial_offset{};
175 u32 viewport_transform_state = 1;
167}; 176};
168 177
169void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs, 178void SerializePipeline(std::span<const char> key, std::span<const GenericEnvironment* const> envs,