summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2021-08-01 18:57:45 -0300
committerGravatar Fernando Sahmkow2021-11-16 22:11:29 +0100
commite66d5b88a6f1c2d85c5cd8e351c6ed52c96a0ecf (patch)
tree0107548906df0b9d42e89451489be6a54ed71bf3
parentshader: Properly blacklist and scale image loads (diff)
downloadyuzu-e66d5b88a6f1c2d85c5cd8e351c6ed52c96a0ecf.tar.gz
yuzu-e66d5b88a6f1c2d85c5cd8e351c6ed52c96a0ecf.tar.xz
yuzu-e66d5b88a6f1c2d85c5cd8e351c6ed52c96a0ecf.zip
shader: Properly scale image reads and add GL SPIR-V support
Thanks for everything!
Diffstat (limited to '')
-rw-r--r--src/shader_recompiler/backend/bindings.h2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_context.cpp4
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm.h2
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_image.cpp9
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_instructions.h1
-rw-r--r--src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_context_get_set.cpp2
-rw-r--r--src/shader_recompiler/backend/glsl/emit_glsl_image.cpp8
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.cpp65
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.h11
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.h16
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp13
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp74
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_instructions.h1
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.cpp4
-rw-r--r--src/shader_recompiler/frontend/ir/ir_emitter.h1
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.inc1
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp1
-rw-r--r--src/shader_recompiler/ir_opt/rescaling_pass.cpp3
-rw-r--r--src/shader_recompiler/runtime_info.h2
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp13
-rw-r--r--src/video_core/renderer_opengl/gl_compute_pipeline.cpp21
-rw-r--r--src/video_core/renderer_opengl/gl_graphics_pipeline.cpp24
-rw-r--r--src/video_core/renderer_vulkan/pipeline_helper.h22
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp3
25 files changed, 228 insertions, 77 deletions
diff --git a/src/shader_recompiler/backend/bindings.h b/src/shader_recompiler/backend/bindings.h
index 35503000c..669702553 100644
--- a/src/shader_recompiler/backend/bindings.h
+++ b/src/shader_recompiler/backend/bindings.h
@@ -14,6 +14,8 @@ struct Bindings {
14 u32 storage_buffer{}; 14 u32 storage_buffer{};
15 u32 texture{}; 15 u32 texture{};
16 u32 image{}; 16 u32 image{};
17 u32 texture_scaling_index{};
18 u32 image_scaling_index{};
17}; 19};
18 20
19} // namespace Shader::Backend 21} // namespace Shader::Backend
diff --git a/src/shader_recompiler/backend/glasm/emit_context.cpp b/src/shader_recompiler/backend/glasm/emit_context.cpp
index 069c019ad..8fd459dfe 100644
--- a/src/shader_recompiler/backend/glasm/emit_context.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_context.cpp
@@ -6,6 +6,7 @@
6 6
7#include "shader_recompiler/backend/bindings.h" 7#include "shader_recompiler/backend/bindings.h"
8#include "shader_recompiler/backend/glasm/emit_context.h" 8#include "shader_recompiler/backend/glasm/emit_context.h"
9#include "shader_recompiler/backend/glasm/emit_glasm.h"
9#include "shader_recompiler/frontend/ir/program.h" 10#include "shader_recompiler/frontend/ir/program.h"
10#include "shader_recompiler/profile.h" 11#include "shader_recompiler/profile.h"
11#include "shader_recompiler/runtime_info.h" 12#include "shader_recompiler/runtime_info.h"
@@ -55,7 +56,8 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
55 } 56 }
56 if (!runtime_info.glasm_use_storage_buffers) { 57 if (!runtime_info.glasm_use_storage_buffers) {
57 if (const size_t num = info.storage_buffers_descriptors.size(); num > 0) { 58 if (const size_t num = info.storage_buffers_descriptors.size(); num > 0) {
58 Add("PARAM c[{}]={{program.local[0..{}]}};", num, num - 1); 59 const size_t index{num + PROGRAM_LOCAL_PARAMETER_STORAGE_BUFFER_BASE};
60 Add("PARAM c[{}]={{program.local[0..{}]}};", index, index - 1);
59 } 61 }
60 } 62 }
61 stage = program.stage; 63 stage = program.stage;
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.h b/src/shader_recompiler/backend/glasm/emit_glasm.h
index bcb55f062..292655acb 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm.h
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.h
@@ -13,6 +13,8 @@
13 13
14namespace Shader::Backend::GLASM { 14namespace Shader::Backend::GLASM {
15 15
16constexpr u32 PROGRAM_LOCAL_PARAMETER_STORAGE_BUFFER_BASE = 1;
17
16[[nodiscard]] std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, 18[[nodiscard]] std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info,
17 IR::Program& program, Bindings& bindings); 19 IR::Program& program, Bindings& bindings);
18 20
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
index 05e88cd97..d325d31c7 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_image.cpp
@@ -617,6 +617,15 @@ void EmitIsTextureScaled(EmitContext& ctx, IR::Inst& inst, const IR::Value& inde
617 1u << index.U32(), ctx.reg_alloc.Define(inst)); 617 1u << index.U32(), ctx.reg_alloc.Define(inst));
618} 618}
619 619
620void EmitIsImageScaled(EmitContext& ctx, IR::Inst& inst, const IR::Value& index) {
621 if (!index.IsImmediate()) {
622 throw NotImplementedException("Non-constant texture rescaling");
623 }
624 ctx.Add("AND.U RC.x,scaling[0].y,{};"
625 "SNE.S {},RC.x,0;",
626 1u << index.U32(), ctx.reg_alloc.Define(inst));
627}
628
620void EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord, 629void EmitImageAtomicIAdd32(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
621 ScalarU32 value) { 630 ScalarU32 value) {
622 ImageAtomic(ctx, inst, index, coord, value, "ADD.U32"); 631 ImageAtomic(ctx, inst, index, coord, value, "ADD.U32");
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
index e2b7d601d..1f343bff5 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
@@ -557,6 +557,7 @@ void EmitImageRead(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Reg
557void EmitImageWrite(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord, 557void EmitImageWrite(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, Register coord,
558 Register color); 558 Register color);
559void EmitIsTextureScaled(EmitContext& ctx, IR::Inst& inst, const IR::Value& index); 559void EmitIsTextureScaled(EmitContext& ctx, IR::Inst& inst, const IR::Value& index);
560void EmitIsImageScaled(EmitContext& ctx, IR::Inst& inst, const IR::Value& index);
560void EmitBindlessImageAtomicIAdd32(EmitContext&); 561void EmitBindlessImageAtomicIAdd32(EmitContext&);
561void EmitBindlessImageAtomicSMin32(EmitContext&); 562void EmitBindlessImageAtomicSMin32(EmitContext&);
562void EmitBindlessImageAtomicUMin32(EmitContext&); 563void EmitBindlessImageAtomicUMin32(EmitContext&);
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
index c0f8ddcad..681aeda8d 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
@@ -211,7 +211,7 @@ void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
211} 211}
212 212
213void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) { 213void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
214 ctx.Add("MOV.F {}.x,scaling[0].y;", inst); 214 ctx.Add("MOV.F {}.x,scaling[0].z;", inst);
215} 215}
216 216
217void EmitUndefU1(EmitContext& ctx, IR::Inst& inst) { 217void 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 542a79230..4c26f3829 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
@@ -446,7 +446,7 @@ void EmitYDirection(EmitContext& ctx, IR::Inst& inst) {
446} 446}
447 447
448void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) { 448void EmitResolutionDownFactor(EmitContext& ctx, IR::Inst& inst) {
449 ctx.AddF32("{}=scaling.y;", inst); 449 ctx.AddF32("{}=scaling.z;", inst);
450} 450}
451 451
452void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) { 452void EmitLoadLocal(EmitContext& ctx, IR::Inst& inst, std::string_view word_offset) {
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
index 82b6f0d77..2f78d0267 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp
@@ -620,6 +620,14 @@ void EmitIsTextureScaled(EmitContext& ctx, IR::Inst& inst, const IR::Value& inde
620 ctx.AddU1("{}=(ftou(scaling.x)&{})!=0;", inst, 1u << image_index); 620 ctx.AddU1("{}=(ftou(scaling.x)&{})!=0;", inst, 1u << image_index);
621} 621}
622 622
623void EmitIsImageScaled(EmitContext& ctx, IR::Inst& inst, const IR::Value& index) {
624 if (!index.IsImmediate()) {
625 throw NotImplementedException("Non-constant texture rescaling");
626 }
627 const u32 image_index{index.U32()};
628 ctx.AddU1("{}=(ftou(scaling.y)&{})!=0;", inst, 1u << image_index);
629}
630
623void EmitBindlessImageSampleImplicitLod(EmitContext&) { 631void EmitBindlessImageSampleImplicitLod(EmitContext&) {
624 NotImplemented(); 632 NotImplemented();
625} 633}
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp
index 222baa177..8646fe989 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_context.cpp
@@ -14,6 +14,7 @@
14#include "common/common_types.h" 14#include "common/common_types.h"
15#include "common/div_ceil.h" 15#include "common/div_ceil.h"
16#include "shader_recompiler/backend/spirv/emit_context.h" 16#include "shader_recompiler/backend/spirv/emit_context.h"
17#include "shader_recompiler/backend/spirv/emit_spirv.h"
17 18
18namespace Shader::Backend::SPIRV { 19namespace Shader::Backend::SPIRV {
19namespace { 20namespace {
@@ -476,8 +477,9 @@ void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_vie
476 477
477EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_info_, 478EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_info_,
478 IR::Program& program, Bindings& bindings) 479 IR::Program& program, Bindings& bindings)
479 : Sirit::Module(profile_.supported_spirv), profile{profile_}, 480 : Sirit::Module(profile_.supported_spirv), profile{profile_}, runtime_info{runtime_info_},
480 runtime_info{runtime_info_}, stage{program.stage} { 481 stage{program.stage}, texture_rescaling_index{bindings.texture_scaling_index},
482 image_rescaling_index{bindings.image_scaling_index} {
481 const bool is_unified{profile.unified_descriptor_binding}; 483 const bool is_unified{profile.unified_descriptor_binding};
482 u32& uniform_binding{is_unified ? bindings.unified : bindings.uniform_buffer}; 484 u32& uniform_binding{is_unified ? bindings.unified : bindings.uniform_buffer};
483 u32& storage_binding{is_unified ? bindings.unified : bindings.storage_buffer}; 485 u32& storage_binding{is_unified ? bindings.unified : bindings.storage_buffer};
@@ -494,8 +496,8 @@ EmitContext::EmitContext(const Profile& profile_, const RuntimeInfo& runtime_inf
494 DefineStorageBuffers(program.info, storage_binding); 496 DefineStorageBuffers(program.info, storage_binding);
495 DefineTextureBuffers(program.info, texture_binding); 497 DefineTextureBuffers(program.info, texture_binding);
496 DefineImageBuffers(program.info, image_binding); 498 DefineImageBuffers(program.info, image_binding);
497 DefineTextures(program.info, texture_binding); 499 DefineTextures(program.info, texture_binding, bindings.texture_scaling_index);
498 DefineImages(program.info, image_binding); 500 DefineImages(program.info, image_binding, bindings.image_scaling_index);
499 DefineAttributeMemAccess(program.info); 501 DefineAttributeMemAccess(program.info);
500 DefineGlobalMemoryFunctions(program.info); 502 DefineGlobalMemoryFunctions(program.info);
501 DefineRescalingInput(program.info); 503 DefineRescalingInput(program.info);
@@ -1003,25 +1005,49 @@ void EmitContext::DefineRescalingInput(const Info& info) {
1003 if (!info.uses_rescaling_uniform) { 1005 if (!info.uses_rescaling_uniform) {
1004 return; 1006 return;
1005 } 1007 }
1006 boost::container::static_vector<Id, 2> members{F32[1]}; 1008 if (profile.unified_descriptor_binding) {
1009 DefineRescalingInputPushConstant(info);
1010 } else {
1011 DefineRescalingInputUniformConstant();
1012 }
1013}
1014
1015void EmitContext::DefineRescalingInputPushConstant(const Info& info) {
1016 boost::container::static_vector<Id, 3> members{F32[1]};
1007 u32 member_index{0}; 1017 u32 member_index{0};
1008 const u32 num_texture_words{Common::DivCeil(runtime_info.num_textures, 32u)}; 1018 if (!info.texture_descriptors.empty()) {
1009 if (runtime_info.num_textures > 0) { 1019 rescaling_textures_type = TypeArray(U32[1], Const(4u));
1010 rescaling_textures_type = TypeArray(U32[1], Const(num_texture_words));
1011 Decorate(rescaling_textures_type, spv::Decoration::ArrayStride, 4u); 1020 Decorate(rescaling_textures_type, spv::Decoration::ArrayStride, 4u);
1012 members.push_back(rescaling_textures_type); 1021 members.push_back(rescaling_textures_type);
1013 rescaling_textures_member_index = ++member_index; 1022 rescaling_textures_member_index = ++member_index;
1014 } 1023 }
1024 if (!info.image_descriptors.empty()) {
1025 rescaling_images_type = TypeArray(U32[1], Const(NUM_IMAGE_SCALING_WORDS));
1026 if (rescaling_textures_type.value != rescaling_images_type.value) {
1027 Decorate(rescaling_images_type, spv::Decoration::ArrayStride, 4u);
1028 }
1029 members.push_back(rescaling_images_type);
1030 rescaling_images_member_index = ++member_index;
1031 }
1015 const Id push_constant_struct{TypeStruct(std::span(members.data(), members.size()))}; 1032 const Id push_constant_struct{TypeStruct(std::span(members.data(), members.size()))};
1016 Decorate(push_constant_struct, spv::Decoration::Block); 1033 Decorate(push_constant_struct, spv::Decoration::Block);
1017 Name(push_constant_struct, "ResolutionInfo"); 1034 Name(push_constant_struct, "ResolutionInfo");
1035
1018 MemberDecorate(push_constant_struct, 0u, spv::Decoration::Offset, 0u); 1036 MemberDecorate(push_constant_struct, 0u, spv::Decoration::Offset, 0u);
1019 MemberName(push_constant_struct, 0u, "down_factor"); 1037 MemberName(push_constant_struct, 0u, "down_factor");
1020 if (runtime_info.num_textures > 0) { 1038
1021 MemberDecorate(push_constant_struct, rescaling_textures_member_index, 1039 const u32 offset_bias = stage == Stage::Compute ? sizeof(u32) : 0;
1022 spv::Decoration::Offset, 4u); 1040 if (!info.texture_descriptors.empty()) {
1041 MemberDecorate(
1042 push_constant_struct, rescaling_textures_member_index, spv::Decoration::Offset,
1043 static_cast<u32>(offsetof(RescalingLayout, rescaling_textures) - offset_bias));
1023 MemberName(push_constant_struct, rescaling_textures_member_index, "rescaling_textures"); 1044 MemberName(push_constant_struct, rescaling_textures_member_index, "rescaling_textures");
1024 } 1045 }
1046 if (!info.image_descriptors.empty()) {
1047 MemberDecorate(push_constant_struct, rescaling_images_member_index, spv::Decoration::Offset,
1048 static_cast<u32>(offsetof(RescalingLayout, rescaling_images) - offset_bias));
1049 MemberName(push_constant_struct, rescaling_images_member_index, "rescaling_images");
1050 }
1025 const Id pointer_type{TypePointer(spv::StorageClass::PushConstant, push_constant_struct)}; 1051 const Id pointer_type{TypePointer(spv::StorageClass::PushConstant, push_constant_struct)};
1026 rescaling_push_constants = AddGlobalVariable(pointer_type, spv::StorageClass::PushConstant); 1052 rescaling_push_constants = AddGlobalVariable(pointer_type, spv::StorageClass::PushConstant);
1027 Name(rescaling_push_constants, "rescaling_push_constants"); 1053 Name(rescaling_push_constants, "rescaling_push_constants");
@@ -1031,6 +1057,17 @@ void EmitContext::DefineRescalingInput(const Info& info) {
1031 } 1057 }
1032} 1058}
1033 1059
1060void EmitContext::DefineRescalingInputUniformConstant() {
1061 const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, F32[4])};
1062 rescaling_uniform_constant =
1063 AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant);
1064 Decorate(rescaling_uniform_constant, spv::Decoration::Location, 0u);
1065
1066 if (profile.supported_spirv >= 0x00010400) {
1067 interfaces.push_back(rescaling_uniform_constant);
1068 }
1069}
1070
1034void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) { 1071void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
1035 if (info.constant_buffer_descriptors.empty()) { 1072 if (info.constant_buffer_descriptors.empty()) {
1036 return; 1073 return;
@@ -1219,7 +1256,7 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
1219 } 1256 }
1220} 1257}
1221 1258
1222void EmitContext::DefineTextures(const Info& info, u32& binding) { 1259void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_index) {
1223 textures.reserve(info.texture_descriptors.size()); 1260 textures.reserve(info.texture_descriptors.size());
1224 for (const TextureDescriptor& desc : info.texture_descriptors) { 1261 for (const TextureDescriptor& desc : info.texture_descriptors) {
1225 const Id image_type{ImageType(*this, desc)}; 1262 const Id image_type{ImageType(*this, desc)};
@@ -1241,13 +1278,14 @@ void EmitContext::DefineTextures(const Info& info, u32& binding) {
1241 interfaces.push_back(id); 1278 interfaces.push_back(id);
1242 } 1279 }
1243 ++binding; 1280 ++binding;
1281 ++scaling_index;
1244 } 1282 }
1245 if (info.uses_atomic_image_u32) { 1283 if (info.uses_atomic_image_u32) {
1246 image_u32 = TypePointer(spv::StorageClass::Image, U32[1]); 1284 image_u32 = TypePointer(spv::StorageClass::Image, U32[1]);
1247 } 1285 }
1248} 1286}
1249 1287
1250void EmitContext::DefineImages(const Info& info, u32& binding) { 1288void EmitContext::DefineImages(const Info& info, u32& binding, u32& scaling_index) {
1251 images.reserve(info.image_descriptors.size()); 1289 images.reserve(info.image_descriptors.size());
1252 for (const ImageDescriptor& desc : info.image_descriptors) { 1290 for (const ImageDescriptor& desc : info.image_descriptors) {
1253 if (desc.count != 1) { 1291 if (desc.count != 1) {
@@ -1268,6 +1306,7 @@ void EmitContext::DefineImages(const Info& info, u32& binding) {
1268 interfaces.push_back(id); 1306 interfaces.push_back(id);
1269 } 1307 }
1270 ++binding; 1308 ++binding;
1309 ++scaling_index;
1271 } 1310 }
1272} 1311}
1273 1312
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h
index a7917ac51..b67704baa 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.h
+++ b/src/shader_recompiler/backend/spirv/emit_context.h
@@ -238,9 +238,14 @@ public:
238 Id indexed_load_func{}; 238 Id indexed_load_func{};
239 Id indexed_store_func{}; 239 Id indexed_store_func{};
240 240
241 Id rescaling_uniform_constant{};
241 Id rescaling_push_constants{}; 242 Id rescaling_push_constants{};
242 Id rescaling_textures_type{}; 243 Id rescaling_textures_type{};
244 Id rescaling_images_type{};
243 u32 rescaling_textures_member_index{}; 245 u32 rescaling_textures_member_index{};
246 u32 rescaling_images_member_index{};
247 u32 texture_rescaling_index{};
248 u32 image_rescaling_index{};
244 249
245 Id local_memory{}; 250 Id local_memory{};
246 251
@@ -314,11 +319,13 @@ private:
314 void DefineStorageBuffers(const Info& info, u32& binding); 319 void DefineStorageBuffers(const Info& info, u32& binding);
315 void DefineTextureBuffers(const Info& info, u32& binding); 320 void DefineTextureBuffers(const Info& info, u32& binding);
316 void DefineImageBuffers(const Info& info, u32& binding); 321 void DefineImageBuffers(const Info& info, u32& binding);
317 void DefineTextures(const Info& info, u32& binding); 322 void DefineTextures(const Info& info, u32& binding, u32& scaling_index);
318 void DefineImages(const Info& info, u32& binding); 323 void DefineImages(const Info& info, u32& binding, u32& scaling_index);
319 void DefineAttributeMemAccess(const Info& info); 324 void DefineAttributeMemAccess(const Info& info);
320 void DefineGlobalMemoryFunctions(const Info& info); 325 void DefineGlobalMemoryFunctions(const Info& info);
321 void DefineRescalingInput(const Info& info); 326 void DefineRescalingInput(const Info& info);
327 void DefineRescalingInputPushConstant(const Info& info);
328 void DefineRescalingInputUniformConstant();
322 329
323 void DefineInputs(const IR::Program& program); 330 void DefineInputs(const IR::Program& program);
324 void DefineOutputs(const IR::Program& program); 331 void DefineOutputs(const IR::Program& program);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h
index 7b0d8d980..db0998ad6 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.h
@@ -16,15 +16,23 @@
16 16
17namespace Shader::Backend::SPIRV { 17namespace Shader::Backend::SPIRV {
18 18
19constexpr u32 NUM_TEXTURE_SCALING_WORDS = 4;
20constexpr u32 NUM_IMAGE_SCALING_WORDS = 2;
21constexpr u32 NUM_TEXTURE_AND_IMAGE_SCALING_WORDS =
22 NUM_TEXTURE_SCALING_WORDS + NUM_IMAGE_SCALING_WORDS;
23
24struct RescalingLayout {
25 u32 down_factor;
26 std::array<u32, NUM_TEXTURE_SCALING_WORDS> rescaling_textures;
27 std::array<u32, NUM_IMAGE_SCALING_WORDS> rescaling_images;
28};
29
19[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, 30[[nodiscard]] std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info,
20 IR::Program& program, Bindings& bindings); 31 IR::Program& program, Bindings& bindings);
21 32
22[[nodiscard]] inline std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program) { 33[[nodiscard]] inline std::vector<u32> EmitSPIRV(const Profile& profile, IR::Program& program) {
23 RuntimeInfo runtime_info{};
24 runtime_info.num_textures = Shader::NumDescriptors(program.info.texture_descriptors);
25
26 Bindings binding; 34 Bindings binding;
27 return EmitSPIRV(profile, runtime_info, program, binding); 35 return EmitSPIRV(profile, {}, program, binding);
28} 36}
29 37
30} // namespace Shader::Backend::SPIRV 38} // 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 6bb791b03..c0db7452f 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
@@ -527,10 +527,15 @@ Id EmitYDirection(EmitContext& ctx) {
527} 527}
528 528
529Id EmitResolutionDownFactor(EmitContext& ctx) { 529Id EmitResolutionDownFactor(EmitContext& ctx) {
530 const Id pointer_type{ctx.TypePointer(spv::StorageClass::PushConstant, ctx.F32[1])}; 530 if (ctx.profile.unified_descriptor_binding) {
531 const Id pointer{ 531 const Id pointer_type{ctx.TypePointer(spv::StorageClass::PushConstant, ctx.F32[1])};
532 ctx.OpAccessChain(pointer_type, ctx.rescaling_push_constants, ctx.u32_zero_value)}; 532 const Id pointer{
533 return ctx.OpLoad(ctx.F32[1], pointer); 533 ctx.OpAccessChain(pointer_type, ctx.rescaling_push_constants, ctx.u32_zero_value)};
534 return ctx.OpLoad(ctx.F32[1], pointer);
535 } else {
536 const Id composite{ctx.OpLoad(ctx.F32[4], ctx.rescaling_uniform_constant)};
537 return ctx.OpCompositeExtract(ctx.F32[1], composite, 2u);
538 }
534} 539}
535 540
536Id EmitLoadLocal(EmitContext& ctx, Id word_offset) { 541Id EmitLoadLocal(EmitContext& ctx, Id word_offset) {
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 7d7c0627e..519ce8b9b 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -224,6 +224,40 @@ Id Emit(MethodPtrType sparse_ptr, MethodPtrType non_sparse_ptr, EmitContext& ctx
224 Decorate(ctx, inst, sample); 224 Decorate(ctx, inst, sample);
225 return ctx.OpCompositeExtract(result_type, sample, 1U); 225 return ctx.OpCompositeExtract(result_type, sample, 1U);
226} 226}
227
228Id IsScaled(EmitContext& ctx, const IR::Value& index, Id member_index, u32 base_index) {
229 const Id push_constant_u32{ctx.TypePointer(spv::StorageClass::PushConstant, ctx.U32[1])};
230 Id bit{};
231 if (index.IsImmediate()) {
232 // Use BitwiseAnd instead of BitfieldExtract for better codegen on Nvidia OpenGL.
233 // LOP32I.NZ is used to set the predicate rather than BFE+ISETP.
234 const u32 index_value{index.U32() + base_index};
235 const Id word_index{ctx.Const(index_value / 32)};
236 const Id bit_index_mask{ctx.Const(1u << (index_value % 32))};
237 const Id pointer{ctx.OpAccessChain(push_constant_u32, ctx.rescaling_push_constants,
238 member_index, word_index)};
239 const Id word{ctx.OpLoad(ctx.U32[1], pointer)};
240 bit = ctx.OpBitwiseAnd(ctx.U32[1], word, bit_index_mask);
241 } else {
242 Id index_value{ctx.Def(index)};
243 if (base_index != 0) {
244 index_value = ctx.OpIAdd(ctx.U32[1], index_value, ctx.Const(base_index));
245 }
246 const Id word_index{ctx.OpShiftRightArithmetic(ctx.U32[1], index_value, ctx.Const(5u))};
247 const Id pointer{ctx.OpAccessChain(push_constant_u32, ctx.rescaling_push_constants,
248 member_index, word_index)};
249 const Id word{ctx.OpLoad(ctx.U32[1], pointer)};
250 const Id bit_index{ctx.OpBitwiseAnd(ctx.U32[1], index_value, ctx.Const(31u))};
251 bit = ctx.OpBitFieldUExtract(ctx.U32[1], index_value, bit_index, ctx.Const(1u));
252 }
253 return ctx.OpINotEqual(ctx.U1, bit, ctx.u32_zero_value);
254}
255
256Id BitTest(EmitContext& ctx, Id mask, Id bit) {
257 const Id shifted{ctx.OpShiftRightLogical(ctx.U32[1], mask, bit)};
258 const Id bit_value{ctx.OpBitwiseAnd(ctx.U32[1], shifted, ctx.Const(1u))};
259 return ctx.OpINotEqual(ctx.U1, bit_value, ctx.u32_zero_value);
260}
227} // Anonymous namespace 261} // Anonymous namespace
228 262
229Id EmitBindlessImageSampleImplicitLod(EmitContext&) { 263Id EmitBindlessImageSampleImplicitLod(EmitContext&) {
@@ -471,29 +505,27 @@ void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id
471} 505}
472 506
473Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index) { 507Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index) {
474 const Id push_constant_u32{ctx.TypePointer(spv::StorageClass::PushConstant, ctx.U32[1])}; 508 if (ctx.profile.unified_descriptor_binding) {
475 const Id member_index{ctx.Const(ctx.rescaling_textures_member_index)}; 509 const Id member_index{ctx.Const(ctx.rescaling_textures_member_index)};
476 Id bit{}; 510 return IsScaled(ctx, index, member_index, ctx.texture_rescaling_index);
477 if (index.IsImmediate()) {
478 // Use BitwiseAnd instead of BitfieldExtract for better codegen on Nvidia OpenGL.
479 // LOP32I.NZ is used to set the predicate rather than BFE+ISETP.
480 const u32 index_value{index.U32()};
481 const Id word_index{ctx.Const(index_value / 32)};
482 const Id bit_index_mask{ctx.Const(1u << (index_value % 32))};
483 const Id pointer{ctx.OpAccessChain(push_constant_u32, ctx.rescaling_push_constants,
484 member_index, word_index)};
485 const Id word{ctx.OpLoad(ctx.U32[1], pointer)};
486 bit = ctx.OpBitwiseAnd(ctx.U32[1], word, bit_index_mask);
487 } else { 511 } else {
488 const Id index_value{ctx.Def(index)}; 512 const Id composite{ctx.OpLoad(ctx.F32[4], ctx.rescaling_uniform_constant)};
489 const Id word_index{ctx.OpShiftRightArithmetic(ctx.U32[1], index_value, ctx.Const(5u))}; 513 const Id mask_f32{ctx.OpCompositeExtract(ctx.F32[1], composite, 0u)};
490 const Id pointer{ctx.OpAccessChain(push_constant_u32, ctx.rescaling_push_constants, 514 const Id mask{ctx.OpBitcast(ctx.U32[1], mask_f32)};
491 member_index, word_index)}; 515 return BitTest(ctx, mask, ctx.Def(index));
492 const Id word{ctx.OpLoad(ctx.U32[1], pointer)}; 516 }
493 const Id bit_index{ctx.OpBitwiseAnd(ctx.U32[1], index_value, ctx.Const(31u))}; 517}
494 bit = ctx.OpBitFieldUExtract(ctx.U32[1], index_value, bit_index, ctx.Const(1u)); 518
519Id EmitIsImageScaled(EmitContext& ctx, const IR::Value& index) {
520 if (ctx.profile.unified_descriptor_binding) {
521 const Id member_index{ctx.Const(ctx.rescaling_images_member_index)};
522 return IsScaled(ctx, index, member_index, ctx.image_rescaling_index);
523 } else {
524 const Id composite{ctx.OpLoad(ctx.F32[4], ctx.rescaling_uniform_constant)};
525 const Id mask_f32{ctx.OpCompositeExtract(ctx.F32[1], composite, 1u)};
526 const Id mask{ctx.OpBitcast(ctx.U32[1], mask_f32)};
527 return BitTest(ctx, mask, ctx.Def(index));
495 } 528 }
496 return ctx.OpINotEqual(ctx.U1, bit, ctx.u32_zero_value);
497} 529}
498 530
499} // namespace Shader::Backend::SPIRV 531} // namespace Shader::Backend::SPIRV
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index 69fc18f5f..6cd22dd3e 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -514,6 +514,7 @@ Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
514Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords); 514Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords);
515void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color); 515void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color);
516Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index); 516Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index);
517Id EmitIsImageScaled(EmitContext& ctx, const IR::Value& index);
517Id EmitBindlessImageAtomicIAdd32(EmitContext&); 518Id EmitBindlessImageAtomicIAdd32(EmitContext&);
518Id EmitBindlessImageAtomicSMin32(EmitContext&); 519Id EmitBindlessImageAtomicSMin32(EmitContext&);
519Id EmitBindlessImageAtomicUMin32(EmitContext&); 520Id EmitBindlessImageAtomicUMin32(EmitContext&);
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.cpp b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
index 3ccd91c10..356f889ac 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.cpp
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.cpp
@@ -1950,6 +1950,10 @@ U1 IREmitter::IsTextureScaled(const U32& index) {
1950 return Inst<U1>(Opcode::IsTextureScaled, index); 1950 return Inst<U1>(Opcode::IsTextureScaled, index);
1951} 1951}
1952 1952
1953U1 IREmitter::IsImageScaled(const U32& index) {
1954 return Inst<U1>(Opcode::IsImageScaled, index);
1955}
1956
1953U1 IREmitter::VoteAll(const U1& value) { 1957U1 IREmitter::VoteAll(const U1& value) {
1954 return Inst<U1>(Opcode::VoteAll, value); 1958 return Inst<U1>(Opcode::VoteAll, value);
1955} 1959}
diff --git a/src/shader_recompiler/frontend/ir/ir_emitter.h b/src/shader_recompiler/frontend/ir/ir_emitter.h
index a78628413..13eefa88b 100644
--- a/src/shader_recompiler/frontend/ir/ir_emitter.h
+++ b/src/shader_recompiler/frontend/ir/ir_emitter.h
@@ -361,6 +361,7 @@ public:
361 const Value& value, TextureInstInfo info); 361 const Value& value, TextureInstInfo info);
362 362
363 [[nodiscard]] U1 IsTextureScaled(const U32& index); 363 [[nodiscard]] U1 IsTextureScaled(const U32& index);
364 [[nodiscard]] U1 IsImageScaled(const U32& index);
364 365
365 [[nodiscard]] U1 VoteAll(const U1& value); 366 [[nodiscard]] U1 VoteAll(const U1& value);
366 [[nodiscard]] U1 VoteAny(const U1& value); 367 [[nodiscard]] U1 VoteAny(const U1& value);
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index ec629428a..6929919df 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -494,6 +494,7 @@ OPCODE(ImageRead, U32x4, Opaq
494OPCODE(ImageWrite, Void, Opaque, Opaque, U32x4, ) 494OPCODE(ImageWrite, Void, Opaque, Opaque, U32x4, )
495 495
496OPCODE(IsTextureScaled, U1, U32, ) 496OPCODE(IsTextureScaled, U1, U32, )
497OPCODE(IsImageScaled, U1, U32, )
497 498
498// Atomic Image operations 499// Atomic Image operations
499 500
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
index ed82fa2ac..1e476d83d 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -432,6 +432,7 @@ void VisitUsages(Info& info, IR::Inst& inst) {
432 break; 432 break;
433 case IR::Opcode::ResolutionDownFactor: 433 case IR::Opcode::ResolutionDownFactor:
434 case IR::Opcode::IsTextureScaled: 434 case IR::Opcode::IsTextureScaled:
435 case IR::Opcode::IsImageScaled:
435 info.uses_rescaling_uniform = true; 436 info.uses_rescaling_uniform = true;
436 break; 437 break;
437 case IR::Opcode::LaneId: 438 case IR::Opcode::LaneId:
diff --git a/src/shader_recompiler/ir_opt/rescaling_pass.cpp b/src/shader_recompiler/ir_opt/rescaling_pass.cpp
index 86c8f0c69..2af12fc07 100644
--- a/src/shader_recompiler/ir_opt/rescaling_pass.cpp
+++ b/src/shader_recompiler/ir_opt/rescaling_pass.cpp
@@ -129,8 +129,7 @@ void PatchImageFetch(IR::Block& block, IR::Inst& inst) {
129void PatchImageRead(IR::Block& block, IR::Inst& inst) { 129void PatchImageRead(IR::Block& block, IR::Inst& inst) {
130 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; 130 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
131 const auto info{inst.Flags<IR::TextureInstInfo>()}; 131 const auto info{inst.Flags<IR::TextureInstInfo>()};
132 // TODO: Scale conditionally 132 const IR::U1 is_scaled{ir.IsImageScaled(ir.Imm32(info.descriptor_index))};
133 const IR::U1 is_scaled{IR::Value{true}};
134 ScaleIntegerCoord(ir, inst, is_scaled); 133 ScaleIntegerCoord(ir, inst, is_scaled);
135} 134}
136 135
diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h
index dc89cb923..f3f83a258 100644
--- a/src/shader_recompiler/runtime_info.h
+++ b/src/shader_recompiler/runtime_info.h
@@ -63,8 +63,6 @@ struct RuntimeInfo {
63 std::array<AttributeType, 32> generic_input_types{}; 63 std::array<AttributeType, 32> generic_input_types{};
64 VaryingState previous_stage_stores; 64 VaryingState previous_stage_stores;
65 65
66 u32 num_textures{};
67
68 bool convert_depth_mode{}; 66 bool convert_depth_mode{};
69 bool force_early_z{}; 67 bool force_early_z{};
70 68
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index 187a28e4d..d4dd10bb6 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -5,6 +5,7 @@
5#include <algorithm> 5#include <algorithm>
6#include <span> 6#include <span>
7 7
8#include "shader_recompiler/backend/glasm/emit_glasm.h"
8#include "video_core/buffer_cache/buffer_cache.h" 9#include "video_core/buffer_cache/buffer_cache.h"
9#include "video_core/renderer_opengl/gl_buffer_cache.h" 10#include "video_core/renderer_opengl/gl_buffer_cache.h"
10#include "video_core/renderer_opengl/gl_device.h" 11#include "video_core/renderer_opengl/gl_device.h"
@@ -229,8 +230,10 @@ void BufferCacheRuntime::BindStorageBuffer(size_t stage, u32 binding_index, Buff
229 .padding = 0, 230 .padding = 0,
230 }; 231 };
231 buffer.MakeResident(is_written ? GL_READ_WRITE : GL_READ_ONLY); 232 buffer.MakeResident(is_written ? GL_READ_WRITE : GL_READ_ONLY);
232 glProgramLocalParametersI4uivNV(PROGRAM_LUT[stage], binding_index, 1, 233 glProgramLocalParametersI4uivNV(
233 reinterpret_cast<const GLuint*>(&ssbo)); 234 PROGRAM_LUT[stage],
235 Shader::Backend::GLASM::PROGRAM_LOCAL_PARAMETER_STORAGE_BUFFER_BASE + binding_index, 1,
236 reinterpret_cast<const GLuint*>(&ssbo));
234 } 237 }
235} 238}
236 239
@@ -250,8 +253,10 @@ void BufferCacheRuntime::BindComputeStorageBuffer(u32 binding_index, Buffer& buf
250 .padding = 0, 253 .padding = 0,
251 }; 254 };
252 buffer.MakeResident(is_written ? GL_READ_WRITE : GL_READ_ONLY); 255 buffer.MakeResident(is_written ? GL_READ_WRITE : GL_READ_ONLY);
253 glProgramLocalParametersI4uivNV(GL_COMPUTE_PROGRAM_NV, binding_index, 1, 256 glProgramLocalParametersI4uivNV(
254 reinterpret_cast<const GLuint*>(&ssbo)); 257 GL_COMPUTE_PROGRAM_NV,
258 Shader::Backend::GLASM::PROGRAM_LOCAL_PARAMETER_STORAGE_BUFFER_BASE + binding_index, 1,
259 reinterpret_cast<const GLuint*>(&ssbo));
255 } 260 }
256} 261}
257 262
diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
index 60c65047b..9af61c340 100644
--- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
@@ -181,33 +181,40 @@ void ComputePipeline::Configure() {
181 texture_binding += num_texture_buffers; 181 texture_binding += num_texture_buffers;
182 image_binding += num_image_buffers; 182 image_binding += num_image_buffers;
183 183
184 u32 scaling_mask{}; 184 u32 texture_scaling_mask{};
185 for (const auto& desc : info.texture_descriptors) { 185 for (const auto& desc : info.texture_descriptors) {
186 for (u32 index = 0; index < desc.count; ++index) { 186 for (u32 index = 0; index < desc.count; ++index) {
187 ImageView& image_view{texture_cache.GetImageView((views_it++)->id)}; 187 ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
188 textures[texture_binding] = image_view.Handle(desc.type); 188 textures[texture_binding] = image_view.Handle(desc.type);
189 if (texture_cache.IsRescaling(image_view)) { 189 if (texture_cache.IsRescaling(image_view)) {
190 scaling_mask |= 1u << texture_binding; 190 texture_scaling_mask |= 1u << texture_binding;
191 } 191 }
192 ++texture_binding; 192 ++texture_binding;
193 } 193 }
194 } 194 }
195 u32 image_scaling_mask{};
195 for (const auto& desc : info.image_descriptors) { 196 for (const auto& desc : info.image_descriptors) {
196 for (u32 index = 0; index < desc.count; ++index) { 197 for (u32 index = 0; index < desc.count; ++index) {
197 ImageView& image_view{texture_cache.GetImageView((views_it++)->id)}; 198 ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
198 if (desc.is_written) { 199 if (desc.is_written) {
199 texture_cache.MarkModification(image_view.image_id); 200 texture_cache.MarkModification(image_view.image_id);
200 } 201 }
201 images[image_binding++] = image_view.StorageView(desc.type, desc.format); 202 images[image_binding] = image_view.StorageView(desc.type, desc.format);
203 if (texture_cache.IsRescaling(image_view)) {
204 image_scaling_mask |= 1u << image_binding;
205 }
206 ++image_binding;
202 } 207 }
203 } 208 }
204 if (info.uses_rescaling_uniform) { 209 if (info.uses_rescaling_uniform) {
205 const f32 float_scaling_mask{Common::BitCast<f32>(scaling_mask)}; 210 const f32 float_texture_scaling_mask{Common::BitCast<f32>(texture_scaling_mask)};
211 const f32 float_image_scaling_mask{Common::BitCast<f32>(image_scaling_mask)};
206 if (assembly_program.handle != 0) { 212 if (assembly_program.handle != 0) {
207 glProgramLocalParameter4fARB(GL_COMPUTE_PROGRAM_NV, 0, float_scaling_mask, 0.0f, 0.0f, 213 glProgramLocalParameter4fARB(GL_COMPUTE_PROGRAM_NV, 0, float_texture_scaling_mask,
208 0.0f); 214 float_image_scaling_mask, 0.0f, 0.0f);
209 } else { 215 } else {
210 glProgramUniform4f(source_program.handle, 0, float_scaling_mask, 0.0f, 0.0f, 0.0f); 216 glProgramUniform4f(source_program.handle, 0, float_texture_scaling_mask,
217 float_image_scaling_mask, 0.0f, 0.0f);
211 } 218 }
212 } 219 }
213 if (texture_binding != 0) { 220 if (texture_binding != 0) {
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index 11559d6ce..f8495896c 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -464,8 +464,10 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
464 views_it += num_texture_buffers[stage]; 464 views_it += num_texture_buffers[stage];
465 views_it += num_image_buffers[stage]; 465 views_it += num_image_buffers[stage];
466 466
467 u32 scaling_mask{}; 467 u32 texture_scaling_mask{};
468 u32 image_scaling_mask{};
468 u32 stage_texture_binding{}; 469 u32 stage_texture_binding{};
470 u32 stage_image_binding{};
469 471
470 const auto& info{stage_infos[stage]}; 472 const auto& info{stage_infos[stage]};
471 for (const auto& desc : info.texture_descriptors) { 473 for (const auto& desc : info.texture_descriptors) {
@@ -473,7 +475,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
473 ImageView& image_view{texture_cache.GetImageView((views_it++)->id)}; 475 ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
474 textures[texture_binding] = image_view.Handle(desc.type); 476 textures[texture_binding] = image_view.Handle(desc.type);
475 if (texture_cache.IsRescaling(image_view)) { 477 if (texture_cache.IsRescaling(image_view)) {
476 scaling_mask |= 1u << stage_texture_binding; 478 texture_scaling_mask |= 1u << stage_texture_binding;
477 } 479 }
478 ++texture_binding; 480 ++texture_binding;
479 ++stage_texture_binding; 481 ++stage_texture_binding;
@@ -485,20 +487,26 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
485 if (desc.is_written) { 487 if (desc.is_written) {
486 texture_cache.MarkModification(image_view.image_id); 488 texture_cache.MarkModification(image_view.image_id);
487 } 489 }
488 images[image_binding++] = image_view.StorageView(desc.type, desc.format); 490 images[image_binding] = image_view.StorageView(desc.type, desc.format);
491 if (texture_cache.IsRescaling(image_view)) {
492 image_scaling_mask |= 1u << stage_image_binding;
493 }
494 ++image_binding;
495 ++stage_image_binding;
489 } 496 }
490 } 497 }
491 if (info.uses_rescaling_uniform) { 498 if (info.uses_rescaling_uniform) {
492 const f32 float_scaling_mask{Common::BitCast<f32>(scaling_mask)}; 499 const f32 float_texture_scaling_mask{Common::BitCast<f32>(texture_scaling_mask)};
500 const f32 float_image_scaling_mask{Common::BitCast<f32>(image_scaling_mask)};
493 const bool is_rescaling{texture_cache.IsRescaling()}; 501 const bool is_rescaling{texture_cache.IsRescaling()};
494 const f32 config_down_factor{Settings::values.resolution_info.down_factor}; 502 const f32 config_down_factor{Settings::values.resolution_info.down_factor};
495 const f32 down_factor{is_rescaling ? config_down_factor : 1.0f}; 503 const f32 down_factor{is_rescaling ? config_down_factor : 1.0f};
496 if (use_assembly) { 504 if (use_assembly) {
497 glProgramLocalParameter4fARB(AssemblyStage(stage), 0, float_scaling_mask, 505 glProgramLocalParameter4fARB(AssemblyStage(stage), 0, float_texture_scaling_mask,
498 down_factor, 0.0f, 0.0f); 506 float_image_scaling_mask, down_factor, 0.0f);
499 } else { 507 } else {
500 glProgramUniform4f(source_programs[stage].handle, 0, float_scaling_mask, 508 glProgramUniform4f(source_programs[stage].handle, 0, float_texture_scaling_mask,
501 down_factor, 0.0f, 0.0f); 509 float_image_scaling_mask, down_factor, 0.0f);
502 } 510 }
503 } 511 }
504 }}; 512 }};
diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h
index bce4220c6..85ae726d1 100644
--- a/src/video_core/renderer_vulkan/pipeline_helper.h
+++ b/src/video_core/renderer_vulkan/pipeline_helper.h
@@ -10,6 +10,7 @@
10 10
11#include "common/assert.h" 11#include "common/assert.h"
12#include "common/common_types.h" 12#include "common/common_types.h"
13#include "shader_recompiler/backend/spirv/emit_spirv.h"
13#include "shader_recompiler/shader_info.h" 14#include "shader_recompiler/shader_info.h"
14#include "video_core/renderer_vulkan/vk_texture_cache.h" 15#include "video_core/renderer_vulkan/vk_texture_cache.h"
15#include "video_core/renderer_vulkan/vk_update_descriptor.h" 16#include "video_core/renderer_vulkan/vk_update_descriptor.h"
@@ -20,7 +21,7 @@
20 21
21namespace Vulkan { 22namespace Vulkan {
22 23
23constexpr size_t MAX_RESCALING_WORDS = 4; 24using Shader::Backend::SPIRV::NUM_TEXTURE_AND_IMAGE_SCALING_WORDS;
24 25
25class DescriptorLayoutBuilder { 26class DescriptorLayoutBuilder {
26public: 27public:
@@ -74,7 +75,8 @@ public:
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 = (is_compute ? 0 : sizeof(f32)) + sizeof(std::array<u32, MAX_RESCALING_WORDS>), 78 .size = (is_compute ? 0 : sizeof(f32)) +
79 sizeof(std::array<u32, NUM_TEXTURE_AND_IMAGE_SCALING_WORDS>),
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,
@@ -146,14 +148,25 @@ public:
146 } 148 }
147 } 149 }
148 150
149 const std::array<u32, MAX_RESCALING_WORDS>& Data() const noexcept { 151 void PushImage(bool is_rescaled) noexcept {
152 *image_ptr |= is_rescaled ? image_bit : 0;
153 image_bit <<= 1;
154 if (image_bit == 0) {
155 image_bit = 1u;
156 ++image_ptr;
157 }
158 }
159
160 const std::array<u32, NUM_TEXTURE_AND_IMAGE_SCALING_WORDS>& Data() const noexcept {
150 return words; 161 return words;
151 } 162 }
152 163
153private: 164private:
154 std::array<u32, MAX_RESCALING_WORDS> words{}; 165 std::array<u32, NUM_TEXTURE_AND_IMAGE_SCALING_WORDS> words{};
155 u32* texture_ptr{words.data()}; 166 u32* texture_ptr{words.data()};
167 u32* image_ptr{words.data() + Shader::Backend::SPIRV::NUM_TEXTURE_SCALING_WORDS};
156 u32 texture_bit{1u}; 168 u32 texture_bit{1u};
169 u32 image_bit{1u};
157}; 170};
158 171
159inline void PushImageDescriptors(TextureCache& texture_cache, 172inline void PushImageDescriptors(TextureCache& texture_cache,
@@ -181,6 +194,7 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
181 } 194 }
182 const VkImageView vk_image_view{image_view.StorageView(desc.type, desc.format)}; 195 const VkImageView vk_image_view{image_view.StorageView(desc.type, desc.format)};
183 update_descriptor_queue.AddImage(vk_image_view); 196 update_descriptor_queue.AddImage(vk_image_view);
197 rescaling.PushImage(texture_cache.IsRescaling(image_view));
184 } 198 }
185 } 199 }
186} 200}
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 691ef0841..eb8b4e08b 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -139,9 +139,6 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
139 } else { 139 } else {
140 info.previous_stage_stores.mask.set(); 140 info.previous_stage_stores.mask.set();
141 } 141 }
142 for (const auto& stage : programs) {
143 info.num_textures += Shader::NumDescriptors(stage.info.texture_descriptors);
144 }
145 const Shader::Stage stage{program.stage}; 142 const Shader::Stage stage{program.stage};
146 const bool has_geometry{key.unique_hashes[4] != 0 && !programs[4].is_geometry_passthrough}; 143 const bool has_geometry{key.unique_hashes[4] != 0 && !programs[4].is_geometry_passthrough};
147 const bool gl_ndc{key.state.ndc_minus_one_to_one != 0}; 144 const bool gl_ndc{key.state.ndc_minus_one_to_one != 0};