summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.cpp79
-rw-r--r--src/shader_recompiler/backend/spirv/emit_context.h11
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp58
-rw-r--r--src/shader_recompiler/frontend/ir/modifiers.h17
-rw-r--r--src/shader_recompiler/frontend/ir/opcodes.inc24
-rw-r--r--src/shader_recompiler/ir_opt/texture_pass.cpp89
-rw-r--r--src/shader_recompiler/shader_info.h4
-rw-r--r--src/video_core/renderer_vulkan/pipeline_helper.h50
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp46
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp63
10 files changed, 284 insertions, 157 deletions
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp
index 7f16cb0dc..8e625f8fb 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_context.cpp
@@ -380,6 +380,24 @@ Id CasLoop(EmitContext& ctx, Operation operation, Id array_pointer, Id element_p
380 ctx.OpFunctionEnd(); 380 ctx.OpFunctionEnd();
381 return func; 381 return func;
382} 382}
383
384template <typename Desc>
385std::string NameOf(const Desc& desc, std::string_view prefix) {
386 if (desc.count > 1) {
387 return fmt::format("{}{}_{:02x}x{}", prefix, desc.cbuf_index, desc.cbuf_offset, desc.count);
388 } else {
389 return fmt::format("{}{}_{:02x}", prefix, desc.cbuf_index, desc.cbuf_offset);
390 }
391}
392
393Id DescType(EmitContext& ctx, Id sampled_type, Id pointer_type, u32 count) {
394 if (count > 1) {
395 const Id array_type{ctx.TypeArray(sampled_type, ctx.Const(count))};
396 return ctx.TypePointer(spv::StorageClass::UniformConstant, array_type);
397 } else {
398 return pointer_type;
399 }
400}
383} // Anonymous namespace 401} // Anonymous namespace
384 402
385void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) { 403void VectorTypes::Define(Sirit::Module& sirit_ctx, Id base_type, std::string_view name) {
@@ -971,12 +989,15 @@ void EmitContext::DefineTextureBuffers(const Info& info, u32& binding) {
971 const Id id{AddGlobalVariable(type, spv::StorageClass::UniformConstant)}; 989 const Id id{AddGlobalVariable(type, spv::StorageClass::UniformConstant)};
972 Decorate(id, spv::Decoration::Binding, binding); 990 Decorate(id, spv::Decoration::Binding, binding);
973 Decorate(id, spv::Decoration::DescriptorSet, 0U); 991 Decorate(id, spv::Decoration::DescriptorSet, 0U);
974 Name(id, fmt::format("texbuf{}_{:02x}", desc.cbuf_index, desc.cbuf_offset)); 992 Name(id, NameOf(desc, "texbuf"));
975 texture_buffers.insert(texture_buffers.end(), desc.count, id); 993 texture_buffers.push_back({
994 .id = id,
995 .count = desc.count,
996 });
976 if (profile.supported_spirv >= 0x00010400) { 997 if (profile.supported_spirv >= 0x00010400) {
977 interfaces.push_back(id); 998 interfaces.push_back(id);
978 } 999 }
979 binding += desc.count; 1000 ++binding;
980 } 1001 }
981} 1002}
982 1003
@@ -992,44 +1013,41 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
992 const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; 1013 const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
993 Decorate(id, spv::Decoration::Binding, binding); 1014 Decorate(id, spv::Decoration::Binding, binding);
994 Decorate(id, spv::Decoration::DescriptorSet, 0U); 1015 Decorate(id, spv::Decoration::DescriptorSet, 0U);
995 Name(id, fmt::format("imgbuf{}_{:02x}", desc.cbuf_index, desc.cbuf_offset)); 1016 Name(id, NameOf(desc, "imgbuf"));
996 const ImageBufferDefinition def{ 1017 image_buffers.push_back({
997 .id = id, 1018 .id = id,
998 .image_type = image_type, 1019 .image_type = image_type,
999 }; 1020 .count = desc.count,
1000 image_buffers.insert(image_buffers.end(), desc.count, def); 1021 });
1001 if (profile.supported_spirv >= 0x00010400) { 1022 if (profile.supported_spirv >= 0x00010400) {
1002 interfaces.push_back(id); 1023 interfaces.push_back(id);
1003 } 1024 }
1004 binding += desc.count; 1025 ++binding;
1005 } 1026 }
1006} 1027}
1007 1028
1008void EmitContext::DefineTextures(const Info& info, u32& binding) { 1029void EmitContext::DefineTextures(const Info& info, u32& binding) {
1009 textures.reserve(info.texture_descriptors.size()); 1030 textures.reserve(info.texture_descriptors.size());
1010 for (const TextureDescriptor& desc : info.texture_descriptors) { 1031 for (const TextureDescriptor& desc : info.texture_descriptors) {
1011 if (desc.count != 1) {
1012 throw NotImplementedException("Array of textures");
1013 }
1014 const Id image_type{ImageType(*this, desc)}; 1032 const Id image_type{ImageType(*this, desc)};
1015 const Id sampled_type{TypeSampledImage(image_type)}; 1033 const Id sampled_type{TypeSampledImage(image_type)};
1016 const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, sampled_type)}; 1034 const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, sampled_type)};
1017 const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; 1035 const Id desc_type{DescType(*this, sampled_type, pointer_type, desc.count)};
1036 const Id id{AddGlobalVariable(desc_type, spv::StorageClass::UniformConstant)};
1018 Decorate(id, spv::Decoration::Binding, binding); 1037 Decorate(id, spv::Decoration::Binding, binding);
1019 Decorate(id, spv::Decoration::DescriptorSet, 0U); 1038 Decorate(id, spv::Decoration::DescriptorSet, 0U);
1020 Name(id, fmt::format("tex{}_{:02x}", desc.cbuf_index, desc.cbuf_offset)); 1039 Name(id, NameOf(desc, "tex"));
1021 for (u32 index = 0; index < desc.count; ++index) { 1040 textures.push_back({
1022 // TODO: Pass count info 1041 .id = id,
1023 textures.push_back(TextureDefinition{ 1042 .sampled_type = sampled_type,
1024 .id{id}, 1043 .pointer_type = pointer_type,
1025 .sampled_type{sampled_type}, 1044 .image_type = image_type,
1026 .image_type{image_type}, 1045 .count = desc.count,
1027 }); 1046 });
1028 }
1029 if (profile.supported_spirv >= 0x00010400) { 1047 if (profile.supported_spirv >= 0x00010400) {
1030 interfaces.push_back(id); 1048 interfaces.push_back(id);
1031 } 1049 }
1032 binding += desc.count; 1050 ++binding;
1033 } 1051 }
1034} 1052}
1035 1053
@@ -1037,24 +1055,23 @@ void EmitContext::DefineImages(const Info& info, u32& binding) {
1037 images.reserve(info.image_descriptors.size()); 1055 images.reserve(info.image_descriptors.size());
1038 for (const ImageDescriptor& desc : info.image_descriptors) { 1056 for (const ImageDescriptor& desc : info.image_descriptors) {
1039 if (desc.count != 1) { 1057 if (desc.count != 1) {
1040 throw NotImplementedException("Array of textures"); 1058 throw NotImplementedException("Array of images");
1041 } 1059 }
1042 const Id image_type{ImageType(*this, desc)}; 1060 const Id image_type{ImageType(*this, desc)};
1043 const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; 1061 const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
1044 const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; 1062 const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
1045 Decorate(id, spv::Decoration::Binding, binding); 1063 Decorate(id, spv::Decoration::Binding, binding);
1046 Decorate(id, spv::Decoration::DescriptorSet, 0U); 1064 Decorate(id, spv::Decoration::DescriptorSet, 0U);
1047 Name(id, fmt::format("img{}_{:02x}", desc.cbuf_index, desc.cbuf_offset)); 1065 Name(id, NameOf(desc, "img"));
1048 for (u32 index = 0; index < desc.count; ++index) { 1066 images.push_back({
1049 images.push_back(ImageDefinition{ 1067 .id = id,
1050 .id{id}, 1068 .image_type = image_type,
1051 .image_type{image_type}, 1069 .count = desc.count,
1052 }); 1070 });
1053 }
1054 if (profile.supported_spirv >= 0x00010400) { 1071 if (profile.supported_spirv >= 0x00010400) {
1055 interfaces.push_back(id); 1072 interfaces.push_back(id);
1056 } 1073 }
1057 binding += desc.count; 1074 ++binding;
1058 } 1075 }
1059} 1076}
1060 1077
diff --git a/src/shader_recompiler/backend/spirv/emit_context.h b/src/shader_recompiler/backend/spirv/emit_context.h
index a4503c7ab..c52544fb7 100644
--- a/src/shader_recompiler/backend/spirv/emit_context.h
+++ b/src/shader_recompiler/backend/spirv/emit_context.h
@@ -32,17 +32,26 @@ private:
32struct TextureDefinition { 32struct TextureDefinition {
33 Id id; 33 Id id;
34 Id sampled_type; 34 Id sampled_type;
35 Id pointer_type;
35 Id image_type; 36 Id image_type;
37 u32 count;
38};
39
40struct TextureBufferDefinition {
41 Id id;
42 u32 count;
36}; 43};
37 44
38struct ImageBufferDefinition { 45struct ImageBufferDefinition {
39 Id id; 46 Id id;
40 Id image_type; 47 Id image_type;
48 u32 count;
41}; 49};
42 50
43struct ImageDefinition { 51struct ImageDefinition {
44 Id id; 52 Id id;
45 Id image_type; 53 Id image_type;
54 u32 count;
46}; 55};
47 56
48struct UniformDefinitions { 57struct UniformDefinitions {
@@ -162,7 +171,7 @@ public:
162 171
163 std::array<UniformDefinitions, Info::MAX_CBUFS> cbufs{}; 172 std::array<UniformDefinitions, Info::MAX_CBUFS> cbufs{};
164 std::array<StorageDefinitions, Info::MAX_SSBOS> ssbos{}; 173 std::array<StorageDefinitions, Info::MAX_SSBOS> ssbos{};
165 std::vector<Id> texture_buffers; 174 std::vector<TextureBufferDefinition> texture_buffers;
166 std::vector<ImageBufferDefinition> image_buffers; 175 std::vector<ImageBufferDefinition> image_buffers;
167 std::vector<TextureDefinition> textures; 176 std::vector<TextureDefinition> textures;
168 std::vector<ImageDefinition> images; 177 std::vector<ImageDefinition> images;
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 90817f161..6008980af 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -147,24 +147,31 @@ private:
147 spv::ImageOperandsMask mask{}; 147 spv::ImageOperandsMask mask{};
148}; 148};
149 149
150Id Texture(EmitContext& ctx, const IR::Value& index) { 150Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR::Value& index) {
151 if (index.IsImmediate()) { 151 const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
152 const TextureDefinition def{ctx.textures.at(index.U32())}; 152 if (def.count > 1) {
153 const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, ctx.Def(index))};
154 return ctx.OpLoad(def.sampled_type, pointer);
155 } else {
153 return ctx.OpLoad(def.sampled_type, def.id); 156 return ctx.OpLoad(def.sampled_type, def.id);
154 } 157 }
155 throw NotImplementedException("Indirect texture sample");
156} 158}
157 159
158Id TextureImage(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) { 160Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info,
159 if (!index.IsImmediate()) { 161 [[maybe_unused]] const IR::Value& index) {
160 throw NotImplementedException("Indirect texture sample");
161 }
162 if (info.type == TextureType::Buffer) { 162 if (info.type == TextureType::Buffer) {
163 const Id sampler_id{ctx.texture_buffers.at(index.U32())}; 163 const TextureBufferDefinition& def{ctx.texture_buffers.at(info.descriptor_index)};
164 if (def.count > 1) {
165 throw NotImplementedException("Indirect texture sample");
166 }
167 const Id sampler_id{def.id};
164 const Id id{ctx.OpLoad(ctx.sampled_texture_buffer_type, sampler_id)}; 168 const Id id{ctx.OpLoad(ctx.sampled_texture_buffer_type, sampler_id)};
165 return ctx.OpImage(ctx.image_buffer_type, id); 169 return ctx.OpImage(ctx.image_buffer_type, id);
166 } else { 170 } else {
167 const TextureDefinition def{ctx.textures.at(index.U32())}; 171 const TextureDefinition& def{ctx.textures.at(info.descriptor_index)};
172 if (def.count > 1) {
173 throw NotImplementedException("Indirect texture sample");
174 }
168 return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, def.id)); 175 return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, def.id));
169 } 176 }
170} 177}
@@ -311,7 +318,7 @@ Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value&
311 bias_lc, offset); 318 bias_lc, offset);
312 return Emit(&EmitContext::OpImageSparseSampleImplicitLod, 319 return Emit(&EmitContext::OpImageSparseSampleImplicitLod,
313 &EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4], 320 &EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4],
314 Texture(ctx, index), coords, operands.Mask(), operands.Span()); 321 Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
315 } else { 322 } else {
316 // We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as 323 // We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as
317 // if the lod was explicitly zero. This may change on Turing with implicit compute 324 // if the lod was explicitly zero. This may change on Turing with implicit compute
@@ -320,7 +327,7 @@ Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value&
320 const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset); 327 const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset);
321 return Emit(&EmitContext::OpImageSparseSampleExplicitLod, 328 return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
322 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], 329 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
323 Texture(ctx, index), coords, operands.Mask(), operands.Span()); 330 Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
324 } 331 }
325} 332}
326 333
@@ -329,8 +336,8 @@ Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value&
329 const auto info{inst->Flags<IR::TextureInstInfo>()}; 336 const auto info{inst->Flags<IR::TextureInstInfo>()};
330 const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod_lc, offset); 337 const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod_lc, offset);
331 return Emit(&EmitContext::OpImageSparseSampleExplicitLod, 338 return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
332 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], Texture(ctx, index), 339 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
333 coords, operands.Mask(), operands.Span()); 340 Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
334} 341}
335 342
336Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, 343Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
@@ -340,7 +347,7 @@ Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
340 offset); 347 offset);
341 return Emit(&EmitContext::OpImageSparseSampleDrefImplicitLod, 348 return Emit(&EmitContext::OpImageSparseSampleDrefImplicitLod,
342 &EmitContext::OpImageSampleDrefImplicitLod, ctx, inst, ctx.F32[1], 349 &EmitContext::OpImageSampleDrefImplicitLod, ctx, inst, ctx.F32[1],
343 Texture(ctx, index), coords, dref, operands.Mask(), operands.Span()); 350 Texture(ctx, info, index), coords, dref, operands.Mask(), operands.Span());
344} 351}
345 352
346Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, 353Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
@@ -349,7 +356,7 @@ Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
349 const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod_lc, offset); 356 const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod_lc, offset);
350 return Emit(&EmitContext::OpImageSparseSampleDrefExplicitLod, 357 return Emit(&EmitContext::OpImageSparseSampleDrefExplicitLod,
351 &EmitContext::OpImageSampleDrefExplicitLod, ctx, inst, ctx.F32[1], 358 &EmitContext::OpImageSampleDrefExplicitLod, ctx, inst, ctx.F32[1],
352 Texture(ctx, index), coords, dref, operands.Mask(), operands.Span()); 359 Texture(ctx, info, index), coords, dref, operands.Mask(), operands.Span());
353} 360}
354 361
355Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, 362Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
@@ -357,15 +364,17 @@ Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id
357 const auto info{inst->Flags<IR::TextureInstInfo>()}; 364 const auto info{inst->Flags<IR::TextureInstInfo>()};
358 const ImageOperands operands(ctx, offset, offset2); 365 const ImageOperands operands(ctx, offset, offset2);
359 return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst, 366 return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst,
360 ctx.F32[4], Texture(ctx, index), coords, ctx.Const(info.gather_component), 367 ctx.F32[4], Texture(ctx, info, index), coords, ctx.Const(info.gather_component),
361 operands.Mask(), operands.Span()); 368 operands.Mask(), operands.Span());
362} 369}
363 370
364Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, 371Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
365 const IR::Value& offset, const IR::Value& offset2, Id dref) { 372 const IR::Value& offset, const IR::Value& offset2, Id dref) {
373 const auto info{inst->Flags<IR::TextureInstInfo>()};
366 const ImageOperands operands(ctx, offset, offset2); 374 const ImageOperands operands(ctx, offset, offset2);
367 return Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, ctx, inst, 375 return Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, ctx, inst,
368 ctx.F32[4], Texture(ctx, index), coords, dref, operands.Mask(), operands.Span()); 376 ctx.F32[4], Texture(ctx, info, index), coords, dref, operands.Mask(),
377 operands.Span());
369} 378}
370 379
371Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, 380Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
@@ -376,12 +385,12 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
376 } 385 }
377 const ImageOperands operands(offset, lod, ms); 386 const ImageOperands operands(offset, lod, ms);
378 return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4], 387 return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
379 TextureImage(ctx, index, info), coords, operands.Mask(), operands.Span()); 388 TextureImage(ctx, info, index), coords, operands.Mask(), operands.Span());
380} 389}
381 390
382Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod) { 391Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod) {
383 const auto info{inst->Flags<IR::TextureInstInfo>()}; 392 const auto info{inst->Flags<IR::TextureInstInfo>()};
384 const Id image{TextureImage(ctx, index, info)}; 393 const Id image{TextureImage(ctx, info, index)};
385 const Id zero{ctx.u32_zero_value}; 394 const Id zero{ctx.u32_zero_value};
386 const auto mips{[&] { return ctx.OpImageQueryLevels(ctx.U32[1], image); }}; 395 const auto mips{[&] { return ctx.OpImageQueryLevels(ctx.U32[1], image); }};
387 switch (info.type) { 396 switch (info.type) {
@@ -405,9 +414,10 @@ Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& i
405 throw LogicError("Unspecified image type {}", info.type.Value()); 414 throw LogicError("Unspecified image type {}", info.type.Value());
406} 415}
407 416
408Id EmitImageQueryLod(EmitContext& ctx, IR::Inst*, const IR::Value& index, Id coords) { 417Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
418 const auto info{inst->Flags<IR::TextureInstInfo>()};
409 const Id zero{ctx.f32_zero_value}; 419 const Id zero{ctx.f32_zero_value};
410 const Id sampler{Texture(ctx, index)}; 420 const Id sampler{Texture(ctx, info, index)};
411 return ctx.OpCompositeConstruct(ctx.F32[4], ctx.OpImageQueryLod(ctx.F32[2], sampler, coords), 421 return ctx.OpCompositeConstruct(ctx.F32[4], ctx.OpImageQueryLod(ctx.F32[2], sampler, coords),
412 zero, zero); 422 zero, zero);
413} 423}
@@ -418,8 +428,8 @@ Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
418 const ImageOperands operands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates, 428 const ImageOperands operands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates,
419 offset, lod_clamp); 429 offset, lod_clamp);
420 return Emit(&EmitContext::OpImageSparseSampleExplicitLod, 430 return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
421 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], Texture(ctx, index), 431 &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
422 coords, operands.Mask(), operands.Span()); 432 Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
423} 433}
424 434
425Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) { 435Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
diff --git a/src/shader_recompiler/frontend/ir/modifiers.h b/src/shader_recompiler/frontend/ir/modifiers.h
index 5d7efa14c..77cda1f8a 100644
--- a/src/shader_recompiler/frontend/ir/modifiers.h
+++ b/src/shader_recompiler/frontend/ir/modifiers.h
@@ -34,14 +34,15 @@ static_assert(sizeof(FpControl) <= sizeof(u32));
34 34
35union TextureInstInfo { 35union TextureInstInfo {
36 u32 raw; 36 u32 raw;
37 BitField<0, 8, TextureType> type; 37 BitField<0, 16, u32> descriptor_index;
38 BitField<8, 1, u32> is_depth; 38 BitField<16, 3, TextureType> type;
39 BitField<9, 1, u32> has_bias; 39 BitField<19, 1, u32> is_depth;
40 BitField<10, 1, u32> has_lod_clamp; 40 BitField<20, 1, u32> has_bias;
41 BitField<11, 1, u32> relaxed_precision; 41 BitField<21, 1, u32> has_lod_clamp;
42 BitField<12, 2, u32> gather_component; 42 BitField<22, 1, u32> relaxed_precision;
43 BitField<14, 2, u32> num_derivates; 43 BitField<23, 2, u32> gather_component;
44 BitField<16, 3, ImageFormat> image_format; 44 BitField<25, 2, u32> num_derivates;
45 BitField<27, 3, ImageFormat> image_format;
45}; 46};
46static_assert(sizeof(TextureInstInfo) <= sizeof(u32)); 47static_assert(sizeof(TextureInstInfo) <= sizeof(u32));
47 48
diff --git a/src/shader_recompiler/frontend/ir/opcodes.inc b/src/shader_recompiler/frontend/ir/opcodes.inc
index b6869d4e4..8f32c9e74 100644
--- a/src/shader_recompiler/frontend/ir/opcodes.inc
+++ b/src/shader_recompiler/frontend/ir/opcodes.inc
@@ -482,18 +482,18 @@ OPCODE(BoundImageGradient, F32x4, U32,
482OPCODE(BoundImageRead, U32x4, U32, Opaque, ) 482OPCODE(BoundImageRead, U32x4, U32, Opaque, )
483OPCODE(BoundImageWrite, Void, U32, Opaque, U32x4, ) 483OPCODE(BoundImageWrite, Void, U32, Opaque, U32x4, )
484 484
485OPCODE(ImageSampleImplicitLod, F32x4, U32, Opaque, Opaque, Opaque, ) 485OPCODE(ImageSampleImplicitLod, F32x4, Opaque, Opaque, Opaque, Opaque, )
486OPCODE(ImageSampleExplicitLod, F32x4, U32, Opaque, Opaque, Opaque, ) 486OPCODE(ImageSampleExplicitLod, F32x4, Opaque, Opaque, Opaque, Opaque, )
487OPCODE(ImageSampleDrefImplicitLod, F32, U32, Opaque, F32, Opaque, Opaque, ) 487OPCODE(ImageSampleDrefImplicitLod, F32, Opaque, Opaque, F32, Opaque, Opaque, )
488OPCODE(ImageSampleDrefExplicitLod, F32, U32, Opaque, F32, Opaque, Opaque, ) 488OPCODE(ImageSampleDrefExplicitLod, F32, Opaque, Opaque, F32, Opaque, Opaque, )
489OPCODE(ImageGather, F32x4, U32, Opaque, Opaque, Opaque, ) 489OPCODE(ImageGather, F32x4, Opaque, Opaque, Opaque, Opaque, )
490OPCODE(ImageGatherDref, F32x4, U32, Opaque, Opaque, Opaque, F32, ) 490OPCODE(ImageGatherDref, F32x4, Opaque, Opaque, Opaque, Opaque, F32, )
491OPCODE(ImageFetch, F32x4, U32, Opaque, Opaque, U32, Opaque, ) 491OPCODE(ImageFetch, F32x4, Opaque, Opaque, Opaque, U32, Opaque, )
492OPCODE(ImageQueryDimensions, U32x4, U32, U32, ) 492OPCODE(ImageQueryDimensions, U32x4, Opaque, U32, )
493OPCODE(ImageQueryLod, F32x4, U32, Opaque, ) 493OPCODE(ImageQueryLod, F32x4, Opaque, Opaque, )
494OPCODE(ImageGradient, F32x4, U32, Opaque, Opaque, Opaque, Opaque, ) 494OPCODE(ImageGradient, F32x4, Opaque, Opaque, Opaque, Opaque, Opaque, )
495OPCODE(ImageRead, U32x4, U32, Opaque, ) 495OPCODE(ImageRead, U32x4, Opaque, Opaque, )
496OPCODE(ImageWrite, Void, U32, Opaque, U32x4, ) 496OPCODE(ImageWrite, Void, Opaque, Opaque, U32x4, )
497 497
498// Warp operations 498// Warp operations
499OPCODE(LaneId, U32, ) 499OPCODE(LaneId, U32, )
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index 5ac485522..cfa6b34b9 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <bit>
6#include <optional> 7#include <optional>
7 8
8#include <boost/container/small_vector.hpp> 9#include <boost/container/small_vector.hpp>
@@ -21,6 +22,8 @@ struct ConstBufferAddr {
21 u32 offset; 22 u32 offset;
22 u32 secondary_index; 23 u32 secondary_index;
23 u32 secondary_offset; 24 u32 secondary_offset;
25 IR::U32 dynamic_offset;
26 u32 count;
24 bool has_secondary; 27 bool has_secondary;
25}; 28};
26 29
@@ -32,6 +35,9 @@ struct TextureInst {
32 35
33using TextureInstVector = boost::container::small_vector<TextureInst, 24>; 36using TextureInstVector = boost::container::small_vector<TextureInst, 24>;
34 37
38constexpr u32 DESCRIPTOR_SIZE = 8;
39constexpr u32 DESCRIPTOR_SIZE_SHIFT = static_cast<u32>(std::countr_zero(DESCRIPTOR_SIZE));
40
35IR::Opcode IndexedInstruction(const IR::Inst& inst) { 41IR::Opcode IndexedInstruction(const IR::Inst& inst) {
36 switch (inst.GetOpcode()) { 42 switch (inst.GetOpcode()) {
37 case IR::Opcode::BindlessImageSampleImplicitLod: 43 case IR::Opcode::BindlessImageSampleImplicitLod:
@@ -131,6 +137,9 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) {
131 if (lhs->has_secondary || rhs->has_secondary) { 137 if (lhs->has_secondary || rhs->has_secondary) {
132 return std::nullopt; 138 return std::nullopt;
133 } 139 }
140 if (lhs->count > 1 || rhs->count > 1) {
141 return std::nullopt;
142 }
134 if (lhs->index > rhs->index || lhs->offset > rhs->offset) { 143 if (lhs->index > rhs->index || lhs->offset > rhs->offset) {
135 std::swap(lhs, rhs); 144 std::swap(lhs, rhs);
136 } 145 }
@@ -139,9 +148,12 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) {
139 .offset = lhs->offset, 148 .offset = lhs->offset,
140 .secondary_index = rhs->index, 149 .secondary_index = rhs->index,
141 .secondary_offset = rhs->offset, 150 .secondary_offset = rhs->offset,
151 .dynamic_offset = {},
152 .count = 1,
142 .has_secondary = true, 153 .has_secondary = true,
143 }; 154 };
144 } 155 }
156 case IR::Opcode::GetCbufU32x2:
145 case IR::Opcode::GetCbufU32: 157 case IR::Opcode::GetCbufU32:
146 break; 158 break;
147 } 159 }
@@ -152,15 +164,39 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst) {
152 // but not supported here at the moment 164 // but not supported here at the moment
153 return std::nullopt; 165 return std::nullopt;
154 } 166 }
155 if (!offset.IsImmediate()) { 167 if (offset.IsImmediate()) {
156 // TODO: Support arrays of textures 168 return ConstBufferAddr{
169 .index = index.U32(),
170 .offset = offset.U32(),
171 .secondary_index = 0,
172 .secondary_offset = 0,
173 .dynamic_offset = {},
174 .count = 1,
175 .has_secondary = false,
176 };
177 }
178 IR::Inst* const offset_inst{offset.InstRecursive()};
179 if (offset_inst->GetOpcode() != IR::Opcode::IAdd32) {
180 return std::nullopt;
181 }
182 u32 base_offset{};
183 IR::U32 dynamic_offset;
184 if (offset_inst->Arg(0).IsImmediate()) {
185 base_offset = offset_inst->Arg(0).U32();
186 dynamic_offset = IR::U32{offset_inst->Arg(1)};
187 } else if (offset_inst->Arg(1).IsImmediate()) {
188 base_offset = offset_inst->Arg(1).U32();
189 dynamic_offset = IR::U32{offset_inst->Arg(0)};
190 } else {
157 return std::nullopt; 191 return std::nullopt;
158 } 192 }
159 return ConstBufferAddr{ 193 return ConstBufferAddr{
160 .index{index.U32()}, 194 .index = index.U32(),
161 .offset{offset.U32()}, 195 .offset = base_offset,
162 .secondary_index = 0, 196 .secondary_index = 0,
163 .secondary_offset = 0, 197 .secondary_offset = 0,
198 .dynamic_offset = dynamic_offset,
199 .count = 8,
164 .has_secondary = false, 200 .has_secondary = false,
165 }; 201 };
166} 202}
@@ -179,11 +215,13 @@ TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
179 .offset = inst.Arg(0).U32(), 215 .offset = inst.Arg(0).U32(),
180 .secondary_index = 0, 216 .secondary_index = 0,
181 .secondary_offset = 0, 217 .secondary_offset = 0,
218 .dynamic_offset = {},
219 .count = 1,
182 .has_secondary = false, 220 .has_secondary = false,
183 }; 221 };
184 } 222 }
185 return TextureInst{ 223 return TextureInst{
186 .cbuf{addr}, 224 .cbuf = addr,
187 .inst = &inst, 225 .inst = &inst,
188 .block = block, 226 .block = block,
189 }; 227 };
@@ -209,18 +247,20 @@ public:
209 247
210 u32 Add(const TextureBufferDescriptor& desc) { 248 u32 Add(const TextureBufferDescriptor& desc) {
211 return Add(texture_buffer_descriptors, desc, [&desc](const auto& existing) { 249 return Add(texture_buffer_descriptors, desc, [&desc](const auto& existing) {
212 return desc.has_secondary == existing.has_secondary && 250 return desc.cbuf_index == existing.cbuf_index &&
213 desc.cbuf_index == existing.cbuf_index &&
214 desc.cbuf_offset == existing.cbuf_offset && 251 desc.cbuf_offset == existing.cbuf_offset &&
215 desc.secondary_cbuf_index == existing.secondary_cbuf_index && 252 desc.secondary_cbuf_index == existing.secondary_cbuf_index &&
216 desc.secondary_cbuf_offset == existing.secondary_cbuf_offset; 253 desc.secondary_cbuf_offset == existing.secondary_cbuf_offset &&
254 desc.count == existing.count && desc.size_shift == existing.size_shift &&
255 desc.has_secondary == existing.has_secondary;
217 }); 256 });
218 } 257 }
219 258
220 u32 Add(const ImageBufferDescriptor& desc) { 259 u32 Add(const ImageBufferDescriptor& desc) {
221 return Add(image_buffer_descriptors, desc, [&desc](const auto& existing) { 260 return Add(image_buffer_descriptors, desc, [&desc](const auto& existing) {
222 return desc.format == existing.format && desc.cbuf_index == existing.cbuf_index && 261 return desc.format == existing.format && desc.cbuf_index == existing.cbuf_index &&
223 desc.cbuf_offset == existing.cbuf_offset; 262 desc.cbuf_offset == existing.cbuf_offset && desc.count == existing.count &&
263 desc.size_shift == existing.size_shift;
224 }); 264 });
225 } 265 }
226 266
@@ -231,7 +271,8 @@ public:
231 desc.cbuf_index == existing.cbuf_index && 271 desc.cbuf_index == existing.cbuf_index &&
232 desc.cbuf_offset == existing.cbuf_offset && 272 desc.cbuf_offset == existing.cbuf_offset &&
233 desc.secondary_cbuf_index == existing.secondary_cbuf_index && 273 desc.secondary_cbuf_index == existing.secondary_cbuf_index &&
234 desc.secondary_cbuf_offset == existing.secondary_cbuf_offset; 274 desc.secondary_cbuf_offset == existing.secondary_cbuf_offset &&
275 desc.count == existing.count && desc.size_shift == existing.size_shift;
235 }); 276 });
236 } 277 }
237 278
@@ -239,7 +280,8 @@ public:
239 const u32 index{Add(image_descriptors, desc, [&desc](const auto& existing) { 280 const u32 index{Add(image_descriptors, desc, [&desc](const auto& existing) {
240 return desc.type == existing.type && desc.format == existing.format && 281 return desc.type == existing.type && desc.format == existing.format &&
241 desc.cbuf_index == existing.cbuf_index && 282 desc.cbuf_index == existing.cbuf_index &&
242 desc.cbuf_offset == existing.cbuf_offset; 283 desc.cbuf_offset == existing.cbuf_offset && desc.count == existing.count &&
284 desc.size_shift == existing.size_shift;
243 })}; 285 })};
244 image_descriptors[index].is_written |= desc.is_written; 286 image_descriptors[index].is_written |= desc.is_written;
245 return index; 287 return index;
@@ -310,7 +352,6 @@ void TexturePass(Environment& env, IR::Program& program) {
310 // This happens on Fire Emblem: Three Houses 352 // This happens on Fire Emblem: Three Houses
311 flags.type.Assign(TextureType::Buffer); 353 flags.type.Assign(TextureType::Buffer);
312 } 354 }
313 inst->SetFlags(flags);
314 break; 355 break;
315 default: 356 default:
316 break; 357 break;
@@ -329,7 +370,8 @@ void TexturePass(Environment& env, IR::Program& program) {
329 .is_written = is_written, 370 .is_written = is_written,
330 .cbuf_index = cbuf.index, 371 .cbuf_index = cbuf.index,
331 .cbuf_offset = cbuf.offset, 372 .cbuf_offset = cbuf.offset,
332 .count = 1, 373 .count = cbuf.count,
374 .size_shift = DESCRIPTOR_SIZE_SHIFT,
333 }); 375 });
334 } else { 376 } else {
335 index = descriptors.Add(ImageDescriptor{ 377 index = descriptors.Add(ImageDescriptor{
@@ -338,7 +380,8 @@ void TexturePass(Environment& env, IR::Program& program) {
338 .is_written = is_written, 380 .is_written = is_written,
339 .cbuf_index = cbuf.index, 381 .cbuf_index = cbuf.index,
340 .cbuf_offset = cbuf.offset, 382 .cbuf_offset = cbuf.offset,
341 .count = 1, 383 .count = cbuf.count,
384 .size_shift = DESCRIPTOR_SIZE_SHIFT,
342 }); 385 });
343 } 386 }
344 break; 387 break;
@@ -351,7 +394,8 @@ void TexturePass(Environment& env, IR::Program& program) {
351 .cbuf_offset = cbuf.offset, 394 .cbuf_offset = cbuf.offset,
352 .secondary_cbuf_index = cbuf.secondary_index, 395 .secondary_cbuf_index = cbuf.secondary_index,
353 .secondary_cbuf_offset = cbuf.secondary_offset, 396 .secondary_cbuf_offset = cbuf.secondary_offset,
354 .count = 1, 397 .count = cbuf.count,
398 .size_shift = DESCRIPTOR_SIZE_SHIFT,
355 }); 399 });
356 } else { 400 } else {
357 index = descriptors.Add(TextureDescriptor{ 401 index = descriptors.Add(TextureDescriptor{
@@ -362,12 +406,23 @@ void TexturePass(Environment& env, IR::Program& program) {
362 .cbuf_offset = cbuf.offset, 406 .cbuf_offset = cbuf.offset,
363 .secondary_cbuf_index = cbuf.secondary_index, 407 .secondary_cbuf_index = cbuf.secondary_index,
364 .secondary_cbuf_offset = cbuf.secondary_offset, 408 .secondary_cbuf_offset = cbuf.secondary_offset,
365 .count = 1, 409 .count = cbuf.count,
410 .size_shift = DESCRIPTOR_SIZE_SHIFT,
366 }); 411 });
367 } 412 }
368 break; 413 break;
369 } 414 }
370 inst->SetArg(0, IR::Value{index}); 415 flags.descriptor_index.Assign(index);
416 inst->SetFlags(flags);
417
418 if (cbuf.count > 1) {
419 const auto insert_point{IR::Block::InstructionList::s_iterator_to(*inst)};
420 IR::IREmitter ir{*texture_inst.block, insert_point};
421 const IR::U32 shift{ir.Imm32(std::countr_zero(DESCRIPTOR_SIZE))};
422 inst->SetArg(0, ir.ShiftRightArithmetic(cbuf.dynamic_offset, shift));
423 } else {
424 inst->SetArg(0, IR::Value{});
425 }
371 } 426 }
372} 427}
373 428
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index 0f45bdfb6..0f28ae07b 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -67,6 +67,7 @@ struct TextureBufferDescriptor {
67 u32 secondary_cbuf_index; 67 u32 secondary_cbuf_index;
68 u32 secondary_cbuf_offset; 68 u32 secondary_cbuf_offset;
69 u32 count; 69 u32 count;
70 u32 size_shift;
70}; 71};
71using TextureBufferDescriptors = boost::container::small_vector<TextureBufferDescriptor, 6>; 72using TextureBufferDescriptors = boost::container::small_vector<TextureBufferDescriptor, 6>;
72 73
@@ -76,6 +77,7 @@ struct ImageBufferDescriptor {
76 u32 cbuf_index; 77 u32 cbuf_index;
77 u32 cbuf_offset; 78 u32 cbuf_offset;
78 u32 count; 79 u32 count;
80 u32 size_shift;
79}; 81};
80using ImageBufferDescriptors = boost::container::small_vector<ImageBufferDescriptor, 2>; 82using ImageBufferDescriptors = boost::container::small_vector<ImageBufferDescriptor, 2>;
81 83
@@ -88,6 +90,7 @@ struct TextureDescriptor {
88 u32 secondary_cbuf_index; 90 u32 secondary_cbuf_index;
89 u32 secondary_cbuf_offset; 91 u32 secondary_cbuf_offset;
90 u32 count; 92 u32 count;
93 u32 size_shift;
91}; 94};
92using TextureDescriptors = boost::container::small_vector<TextureDescriptor, 12>; 95using TextureDescriptors = boost::container::small_vector<TextureDescriptor, 12>;
93 96
@@ -98,6 +101,7 @@ struct ImageDescriptor {
98 u32 cbuf_index; 101 u32 cbuf_index;
99 u32 cbuf_offset; 102 u32 cbuf_offset;
100 u32 count; 103 u32 count;
104 u32 size_shift;
101}; 105};
102using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>; 106using ImageDescriptors = boost::container::small_vector<ImageDescriptor, 4>;
103 107
diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h
index aaf9a735e..dd7d2cc0c 100644
--- a/src/video_core/renderer_vulkan/pipeline_helper.h
+++ b/src/video_core/renderer_vulkan/pipeline_helper.h
@@ -85,28 +85,30 @@ public:
85 } 85 }
86 86
87 void Add(const Shader::Info& info, VkShaderStageFlags stage) { 87 void Add(const Shader::Info& info, VkShaderStageFlags stage) {
88 Add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, stage, info.constant_buffer_descriptors.size()); 88 Add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, stage, info.constant_buffer_descriptors);
89 Add(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, stage, info.storage_buffers_descriptors.size()); 89 Add(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, stage, info.storage_buffers_descriptors);
90 Add(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, stage, info.texture_buffer_descriptors.size()); 90 Add(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, stage, info.texture_buffer_descriptors);
91 Add(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, stage, info.image_buffer_descriptors.size()); 91 Add(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, stage, info.image_buffer_descriptors);
92 Add(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, stage, info.texture_descriptors.size()); 92 Add(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, stage, info.texture_descriptors);
93 Add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, stage, info.image_descriptors.size()); 93 Add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, stage, info.image_descriptors);
94 } 94 }
95 95
96private: 96private:
97 void Add(VkDescriptorType type, VkShaderStageFlags stage, size_t num) { 97 template <typename Descriptors>
98 void Add(VkDescriptorType type, VkShaderStageFlags stage, const Descriptors& descriptors) {
99 const size_t num{descriptors.size()};
98 for (size_t i = 0; i < num; ++i) { 100 for (size_t i = 0; i < num; ++i) {
99 bindings.push_back({ 101 bindings.push_back({
100 .binding = binding, 102 .binding = binding,
101 .descriptorType = type, 103 .descriptorType = type,
102 .descriptorCount = 1, 104 .descriptorCount = descriptors[i].count,
103 .stageFlags = stage, 105 .stageFlags = stage,
104 .pImmutableSamplers = nullptr, 106 .pImmutableSamplers = nullptr,
105 }); 107 });
106 entries.push_back({ 108 entries.push_back({
107 .dstBinding = binding, 109 .dstBinding = binding,
108 .dstArrayElement = 0, 110 .dstArrayElement = 0,
109 .descriptorCount = 1, 111 .descriptorCount = descriptors[i].count,
110 .descriptorType = type, 112 .descriptorType = type,
111 .offset = offset, 113 .offset = offset,
112 .stride = sizeof(DescriptorUpdateEntry), 114 .stride = sizeof(DescriptorUpdateEntry),
@@ -126,21 +128,29 @@ private:
126inline void PushImageDescriptors(const Shader::Info& info, const VkSampler*& samplers, 128inline void PushImageDescriptors(const Shader::Info& info, const VkSampler*& samplers,
127 const ImageId*& image_view_ids, TextureCache& texture_cache, 129 const ImageId*& image_view_ids, TextureCache& texture_cache,
128 VKUpdateDescriptorQueue& update_descriptor_queue) { 130 VKUpdateDescriptorQueue& update_descriptor_queue) {
129 image_view_ids += info.texture_buffer_descriptors.size(); 131 for (const auto& desc : info.texture_buffer_descriptors) {
130 image_view_ids += info.image_buffer_descriptors.size(); 132 image_view_ids += desc.count;
133 }
134 for (const auto& desc : info.image_buffer_descriptors) {
135 image_view_ids += desc.count;
136 }
131 for (const auto& desc : info.texture_descriptors) { 137 for (const auto& desc : info.texture_descriptors) {
132 const VkSampler sampler{*(samplers++)}; 138 for (u32 index = 0; index < desc.count; ++index) {
133 ImageView& image_view{texture_cache.GetImageView(*(image_view_ids++))}; 139 const VkSampler sampler{*(samplers++)};
134 const VkImageView vk_image_view{image_view.Handle(desc.type)}; 140 ImageView& image_view{texture_cache.GetImageView(*(image_view_ids++))};
135 update_descriptor_queue.AddSampledImage(vk_image_view, sampler); 141 const VkImageView vk_image_view{image_view.Handle(desc.type)};
142 update_descriptor_queue.AddSampledImage(vk_image_view, sampler);
143 }
136 } 144 }
137 for (const auto& desc : info.image_descriptors) { 145 for (const auto& desc : info.image_descriptors) {
138 ImageView& image_view{texture_cache.GetImageView(*(image_view_ids++))}; 146 for (u32 index = 0; index < desc.count; ++index) {
139 if (desc.is_written) { 147 ImageView& image_view{texture_cache.GetImageView(*(image_view_ids++))};
140 texture_cache.MarkModification(image_view.image_id); 148 if (desc.is_written) {
149 texture_cache.MarkModification(image_view.image_id);
150 }
151 const VkImageView vk_image_view{image_view.StorageView(desc.type, desc.format)};
152 update_descriptor_queue.AddImage(vk_image_view);
141 } 153 }
142 const VkImageView vk_image_view{image_view.StorageView(desc.type, desc.format)};
143 update_descriptor_queue.AddImage(vk_image_view);
144 } 154 }
145} 155}
146 156
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 45d837ca4..6e9f66262 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -91,35 +91,41 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
91 const auto& qmd{kepler_compute.launch_description}; 91 const auto& qmd{kepler_compute.launch_description};
92 const auto& cbufs{qmd.const_buffer_config}; 92 const auto& cbufs{qmd.const_buffer_config};
93 const bool via_header_index{qmd.linked_tsc != 0}; 93 const bool via_header_index{qmd.linked_tsc != 0};
94 const auto read_handle{[&](const auto& desc) { 94 const auto read_handle{[&](const auto& desc, u32 index) {
95 ASSERT(((qmd.const_buffer_enable_mask >> desc.cbuf_index) & 1) != 0); 95 ASSERT(((qmd.const_buffer_enable_mask >> desc.cbuf_index) & 1) != 0);
96 const u32 index_offset{index << desc.size_shift};
97 const u32 offset{desc.cbuf_offset + index_offset};
96 const GPUVAddr addr{cbufs[desc.cbuf_index].Address() + desc.cbuf_offset}; 98 const GPUVAddr addr{cbufs[desc.cbuf_index].Address() + desc.cbuf_offset};
97 if constexpr (std::is_same_v<decltype(desc), const Shader::TextureDescriptor&> || 99 if constexpr (std::is_same_v<decltype(desc), const Shader::TextureDescriptor&> ||
98 std::is_same_v<decltype(desc), const Shader::TextureBufferDescriptor&>) { 100 std::is_same_v<decltype(desc), const Shader::TextureBufferDescriptor&>) {
99 if (desc.has_secondary) { 101 if (desc.has_secondary) {
100 ASSERT(((qmd.const_buffer_enable_mask >> desc.secondary_cbuf_index) & 1) != 0); 102 ASSERT(((qmd.const_buffer_enable_mask >> desc.secondary_cbuf_index) & 1) != 0);
103 const u32 secondary_offset{desc.secondary_cbuf_offset + index_offset};
101 const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].Address() + 104 const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].Address() +
102 desc.secondary_cbuf_offset}; 105 secondary_offset};
103 const u32 lhs_raw{gpu_memory.Read<u32>(addr)}; 106 const u32 lhs_raw{gpu_memory.Read<u32>(addr)};
104 const u32 rhs_raw{gpu_memory.Read<u32>(separate_addr)}; 107 const u32 rhs_raw{gpu_memory.Read<u32>(separate_addr)};
105 const u32 raw{lhs_raw | rhs_raw}; 108 return TextureHandle{lhs_raw | rhs_raw, via_header_index};
106 return TextureHandle{raw, via_header_index};
107 } 109 }
108 } 110 }
109 return TextureHandle{gpu_memory.Read<u32>(addr), via_header_index}; 111 return TextureHandle{gpu_memory.Read<u32>(addr), via_header_index};
110 }}; 112 }};
111 const auto add_image{[&](const auto& desc) { 113 const auto add_image{[&](const auto& desc) {
112 const TextureHandle handle{read_handle(desc)}; 114 for (u32 index = 0; index < desc.count; ++index) {
113 image_view_indices.push_back(handle.image); 115 const TextureHandle handle{read_handle(desc, index)};
116 image_view_indices.push_back(handle.image);
117 }
114 }}; 118 }};
115 std::ranges::for_each(info.texture_buffer_descriptors, add_image); 119 std::ranges::for_each(info.texture_buffer_descriptors, add_image);
116 std::ranges::for_each(info.image_buffer_descriptors, add_image); 120 std::ranges::for_each(info.image_buffer_descriptors, add_image);
117 for (const auto& desc : info.texture_descriptors) { 121 for (const auto& desc : info.texture_descriptors) {
118 const TextureHandle handle{read_handle(desc)}; 122 for (u32 index = 0; index < desc.count; ++index) {
119 image_view_indices.push_back(handle.image); 123 const TextureHandle handle{read_handle(desc, index)};
124 image_view_indices.push_back(handle.image);
120 125
121 Sampler* const sampler = texture_cache.GetComputeSampler(handle.sampler); 126 Sampler* const sampler = texture_cache.GetComputeSampler(handle.sampler);
122 samplers.push_back(sampler->Handle()); 127 samplers.push_back(sampler->Handle());
128 }
123 } 129 }
124 std::ranges::for_each(info.image_descriptors, add_image); 130 std::ranges::for_each(info.image_descriptors, add_image);
125 131
@@ -130,16 +136,18 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
130 ImageId* texture_buffer_ids{image_view_ids.data()}; 136 ImageId* texture_buffer_ids{image_view_ids.data()};
131 size_t index{}; 137 size_t index{};
132 const auto add_buffer{[&](const auto& desc) { 138 const auto add_buffer{[&](const auto& desc) {
133 ASSERT(desc.count == 1); 139 for (u32 index = 0; index < desc.count; ++index) {
134 bool is_written{false}; 140 bool is_written{false};
135 if constexpr (std::is_same_v<decltype(desc), const Shader::ImageBufferDescriptor&>) { 141 if constexpr (std::is_same_v<decltype(desc), const Shader::ImageBufferDescriptor&>) {
136 is_written = desc.is_written; 142 is_written = desc.is_written;
143 }
144 ImageView& image_view = texture_cache.GetImageView(*texture_buffer_ids);
145 buffer_cache.BindComputeTextureBuffer(index, image_view.GpuAddr(),
146 image_view.BufferSize(), image_view.format,
147 is_written);
148 ++texture_buffer_ids;
149 ++index;
137 } 150 }
138 ImageView& image_view = texture_cache.GetImageView(*texture_buffer_ids);
139 buffer_cache.BindComputeTextureBuffer(index, image_view.GpuAddr(), image_view.BufferSize(),
140 image_view.format, is_written);
141 ++texture_buffer_ids;
142 ++index;
143 }}; 151 }};
144 std::ranges::for_each(info.texture_buffer_descriptors, add_buffer); 152 std::ranges::for_each(info.texture_buffer_descriptors, add_buffer);
145 std::ranges::for_each(info.image_buffer_descriptors, add_buffer); 153 std::ranges::for_each(info.image_buffer_descriptors, add_buffer);
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 08f00b9ce..b7688aef9 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -161,23 +161,26 @@ void GraphicsPipeline::Configure(bool is_indexed) {
161 const Shader::Info& info{stage_infos[stage]}; 161 const Shader::Info& info{stage_infos[stage]};
162 buffer_cache.SetEnabledUniformBuffers(stage, info.constant_buffer_mask); 162 buffer_cache.SetEnabledUniformBuffers(stage, info.constant_buffer_mask);
163 buffer_cache.UnbindGraphicsStorageBuffers(stage); 163 buffer_cache.UnbindGraphicsStorageBuffers(stage);
164 size_t index{}; 164 size_t ssbo_index{};
165 for (const auto& desc : info.storage_buffers_descriptors) { 165 for (const auto& desc : info.storage_buffers_descriptors) {
166 ASSERT(desc.count == 1); 166 ASSERT(desc.count == 1);
167 buffer_cache.BindGraphicsStorageBuffer(stage, index, desc.cbuf_index, desc.cbuf_offset, 167 buffer_cache.BindGraphicsStorageBuffer(stage, ssbo_index, desc.cbuf_index,
168 desc.is_written); 168 desc.cbuf_offset, desc.is_written);
169 ++index; 169 ++ssbo_index;
170 } 170 }
171 const auto& cbufs{maxwell3d.state.shader_stages[stage].const_buffers}; 171 const auto& cbufs{maxwell3d.state.shader_stages[stage].const_buffers};
172 const auto read_handle{[&](const auto& desc) { 172 const auto read_handle{[&](const auto& desc, u32 index) {
173 ASSERT(cbufs[desc.cbuf_index].enabled); 173 ASSERT(cbufs[desc.cbuf_index].enabled);
174 const GPUVAddr addr{cbufs[desc.cbuf_index].address + desc.cbuf_offset}; 174 const u32 index_offset{index << desc.size_shift};
175 const u32 offset{desc.cbuf_offset + index_offset};
176 const GPUVAddr addr{cbufs[desc.cbuf_index].address + offset};
175 if constexpr (std::is_same_v<decltype(desc), const Shader::TextureDescriptor&> || 177 if constexpr (std::is_same_v<decltype(desc), const Shader::TextureDescriptor&> ||
176 std::is_same_v<decltype(desc), const Shader::TextureBufferDescriptor&>) { 178 std::is_same_v<decltype(desc), const Shader::TextureBufferDescriptor&>) {
177 if (desc.has_secondary) { 179 if (desc.has_secondary) {
178 ASSERT(cbufs[desc.secondary_cbuf_index].enabled); 180 ASSERT(cbufs[desc.secondary_cbuf_index].enabled);
181 const u32 second_offset{desc.secondary_cbuf_offset + index_offset};
179 const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].address + 182 const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].address +
180 desc.secondary_cbuf_offset}; 183 second_offset};
181 const u32 lhs_raw{gpu_memory.Read<u32>(addr)}; 184 const u32 lhs_raw{gpu_memory.Read<u32>(addr)};
182 const u32 rhs_raw{gpu_memory.Read<u32>(separate_addr)}; 185 const u32 rhs_raw{gpu_memory.Read<u32>(separate_addr)};
183 const u32 raw{lhs_raw | rhs_raw}; 186 const u32 raw{lhs_raw | rhs_raw};
@@ -187,17 +190,21 @@ void GraphicsPipeline::Configure(bool is_indexed) {
187 return TextureHandle{gpu_memory.Read<u32>(addr), via_header_index}; 190 return TextureHandle{gpu_memory.Read<u32>(addr), via_header_index};
188 }}; 191 }};
189 const auto add_image{[&](const auto& desc) { 192 const auto add_image{[&](const auto& desc) {
190 const TextureHandle handle{read_handle(desc)}; 193 for (u32 index = 0; index < desc.count; ++index) {
191 image_view_indices.push_back(handle.image); 194 const TextureHandle handle{read_handle(desc, index)};
195 image_view_indices.push_back(handle.image);
196 }
192 }}; 197 }};
193 std::ranges::for_each(info.texture_buffer_descriptors, add_image); 198 std::ranges::for_each(info.texture_buffer_descriptors, add_image);
194 std::ranges::for_each(info.image_buffer_descriptors, add_image); 199 std::ranges::for_each(info.image_buffer_descriptors, add_image);
195 for (const auto& desc : info.texture_descriptors) { 200 for (const auto& desc : info.texture_descriptors) {
196 const TextureHandle handle{read_handle(desc)}; 201 for (u32 index = 0; index < desc.count; ++index) {
197 image_view_indices.push_back(handle.image); 202 const TextureHandle handle{read_handle(desc, index)};
203 image_view_indices.push_back(handle.image);
198 204
199 Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.sampler)}; 205 Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.sampler)};
200 samplers.push_back(sampler->Handle()); 206 samplers.push_back(sampler->Handle());
207 }
201 } 208 }
202 std::ranges::for_each(info.image_descriptors, add_image); 209 std::ranges::for_each(info.image_descriptors, add_image);
203 } 210 }
@@ -208,24 +215,30 @@ void GraphicsPipeline::Configure(bool is_indexed) {
208 for (size_t stage = 0; stage < Maxwell::MaxShaderStage; ++stage) { 215 for (size_t stage = 0; stage < Maxwell::MaxShaderStage; ++stage) {
209 size_t index{}; 216 size_t index{};
210 const auto add_buffer{[&](const auto& desc) { 217 const auto add_buffer{[&](const auto& desc) {
211 ASSERT(desc.count == 1); 218 for (u32 index = 0; index < desc.count; ++index) {
212 bool is_written{false}; 219 bool is_written{false};
213 if constexpr (std::is_same_v<decltype(desc), const Shader::ImageBufferDescriptor&>) { 220 if constexpr (std::is_same_v<decltype(desc),
214 is_written = desc.is_written; 221 const Shader::ImageBufferDescriptor&>) {
222 is_written = desc.is_written;
223 }
224 ImageView& image_view{texture_cache.GetImageView(*texture_buffer_index)};
225 buffer_cache.BindGraphicsTextureBuffer(stage, index, image_view.GpuAddr(),
226 image_view.BufferSize(), image_view.format,
227 is_written);
228 ++index;
229 ++texture_buffer_index;
215 } 230 }
216 ImageView& image_view{texture_cache.GetImageView(*texture_buffer_index)};
217 buffer_cache.BindGraphicsTextureBuffer(stage, index, image_view.GpuAddr(),
218 image_view.BufferSize(), image_view.format,
219 is_written);
220 ++index;
221 ++texture_buffer_index;
222 }}; 231 }};
223 const Shader::Info& info{stage_infos[stage]}; 232 const Shader::Info& info{stage_infos[stage]};
224 buffer_cache.UnbindGraphicsTextureBuffers(stage); 233 buffer_cache.UnbindGraphicsTextureBuffers(stage);
225 std::ranges::for_each(info.texture_buffer_descriptors, add_buffer); 234 std::ranges::for_each(info.texture_buffer_descriptors, add_buffer);
226 std::ranges::for_each(info.image_buffer_descriptors, add_buffer); 235 std::ranges::for_each(info.image_buffer_descriptors, add_buffer);
227 texture_buffer_index += info.texture_descriptors.size(); 236 for (const auto& desc : info.texture_descriptors) {
228 texture_buffer_index += info.image_descriptors.size(); 237 texture_buffer_index += desc.count;
238 }
239 for (const auto& desc : info.image_descriptors) {
240 texture_buffer_index += desc.count;
241 }
229 } 242 }
230 buffer_cache.UpdateGraphicsBuffers(is_indexed); 243 buffer_cache.UpdateGraphicsBuffers(is_indexed);
231 244