summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt14
-rw-r--r--src/core/arm/nce/arm_nce.cpp2
-rw-r--r--src/core/arm/nce/interpreter_visitor.cpp5
-rw-r--r--src/core/arm/nce/visitor_base.h6
-rw-r--r--src/core/hle/service/hid/hid_server.cpp2
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp5
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_image.cpp22
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.cpp25
-rw-r--r--src/shader_recompiler/backend/spirv/spirv_emit_context.h2
-rw-r--r--src/shader_recompiler/environment.h2
-rw-r--r--src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp6
-rw-r--r--src/shader_recompiler/ir_opt/constant_propagation_pass.cpp12
-rw-r--r--src/shader_recompiler/ir_opt/texture_pass.cpp25
-rw-r--r--src/shader_recompiler/profile.h2
-rw-r--r--src/shader_recompiler/shader_info.h107
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp45
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h1
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_device.h5
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp3
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp16
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp63
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp3
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp9
-rw-r--r--src/video_core/shader_environment.cpp35
-rw-r--r--src/video_core/shader_environment.h6
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp5
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h4
-rw-r--r--src/video_core/vulkan_common/vulkan_wrapper.cpp2
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp94
-rw-r--r--src/yuzu/configuration/configure_graphics.h4
-rw-r--r--src/yuzu/configuration/qt_config.cpp4
34 files changed, 430 insertions, 117 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
index fc0eeb9ad..54380323e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
@@ -91,18 +91,20 @@ class GamesFragment : Fragment() {
91 viewLifecycleOwner.lifecycleScope.apply { 91 viewLifecycleOwner.lifecycleScope.apply {
92 launch { 92 launch {
93 repeatOnLifecycle(Lifecycle.State.RESUMED) { 93 repeatOnLifecycle(Lifecycle.State.RESUMED) {
94 gamesViewModel.isReloading.collect { binding.swipeRefresh.isRefreshing = it } 94 gamesViewModel.isReloading.collect {
95 binding.swipeRefresh.isRefreshing = it
96 if (gamesViewModel.games.value.isEmpty() && !it) {
97 binding.noticeText.visibility = View.VISIBLE
98 } else {
99 binding.noticeText.visibility = View.INVISIBLE
100 }
101 }
95 } 102 }
96 } 103 }
97 launch { 104 launch {
98 repeatOnLifecycle(Lifecycle.State.RESUMED) { 105 repeatOnLifecycle(Lifecycle.State.RESUMED) {
99 gamesViewModel.games.collectLatest { 106 gamesViewModel.games.collectLatest {
100 (binding.gridGames.adapter as GameAdapter).submitList(it) 107 (binding.gridGames.adapter as GameAdapter).submitList(it)
101 if (it.isEmpty()) {
102 binding.noticeText.visibility = View.VISIBLE
103 } else {
104 binding.noticeText.visibility = View.GONE
105 }
106 } 108 }
107 } 109 }
108 } 110 }
diff --git a/src/core/arm/nce/arm_nce.cpp b/src/core/arm/nce/arm_nce.cpp
index 1311e66a9..123b3da7e 100644
--- a/src/core/arm/nce/arm_nce.cpp
+++ b/src/core/arm/nce/arm_nce.cpp
@@ -39,7 +39,7 @@ fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
39} 39}
40 40
41using namespace Common::Literals; 41using namespace Common::Literals;
42constexpr u32 StackSize = 32_KiB; 42constexpr u32 StackSize = 128_KiB;
43 43
44} // namespace 44} // namespace
45 45
diff --git a/src/core/arm/nce/interpreter_visitor.cpp b/src/core/arm/nce/interpreter_visitor.cpp
index 8e81c66a5..def888d15 100644
--- a/src/core/arm/nce/interpreter_visitor.cpp
+++ b/src/core/arm/nce/interpreter_visitor.cpp
@@ -5,8 +5,6 @@
5#include "common/bit_cast.h" 5#include "common/bit_cast.h"
6#include "core/arm/nce/interpreter_visitor.h" 6#include "core/arm/nce/interpreter_visitor.h"
7 7
8#include <dynarmic/frontend/A64/decoder/a64.h>
9
10namespace Core { 8namespace Core {
11 9
12template <u32 BitSize> 10template <u32 BitSize>
@@ -249,6 +247,7 @@ bool InterpreterVisitor::LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) {
249 return false; 247 return false;
250 } 248 }
251 249
250 // Size in bytes
252 const u64 size = 4 << opc.ZeroExtend(); 251 const u64 size = 4 << opc.ZeroExtend();
253 const u64 offset = imm19.SignExtend<u64>() << 2; 252 const u64 offset = imm19.SignExtend<u64>() << 2;
254 const u64 address = this->GetPc() + offset; 253 const u64 address = this->GetPc() + offset;
@@ -530,7 +529,7 @@ bool InterpreterVisitor::SIMDImmediate(bool wback, bool postindex, size_t scale,
530 } 529 }
531 case MemOp::Load: { 530 case MemOp::Load: {
532 u128 data{}; 531 u128 data{};
533 m_memory.ReadBlock(address, &data, datasize); 532 m_memory.ReadBlock(address, &data, datasize / 8);
534 this->SetVec(Vt, data); 533 this->SetVec(Vt, data);
535 break; 534 break;
536 } 535 }
diff --git a/src/core/arm/nce/visitor_base.h b/src/core/arm/nce/visitor_base.h
index 8fb032912..6a2be3d9b 100644
--- a/src/core/arm/nce/visitor_base.h
+++ b/src/core/arm/nce/visitor_base.h
@@ -4,9 +4,15 @@
4 4
5#pragma once 5#pragma once
6 6
7#pragma GCC diagnostic push
8#pragma GCC diagnostic ignored "-Wshadow"
9
7#include <dynarmic/frontend/A64/a64_types.h> 10#include <dynarmic/frontend/A64/a64_types.h>
11#include <dynarmic/frontend/A64/decoder/a64.h>
8#include <dynarmic/frontend/imm.h> 12#include <dynarmic/frontend/imm.h>
9 13
14#pragma GCC diagnostic pop
15
10namespace Core { 16namespace Core {
11 17
12class VisitorBase { 18class VisitorBase {
diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp
index de24b0401..06a01c02c 100644
--- a/src/core/hle/service/hid/hid_server.cpp
+++ b/src/core/hle/service/hid/hid_server.cpp
@@ -51,7 +51,7 @@ private:
51 IPC::RequestParser rp{ctx}; 51 IPC::RequestParser rp{ctx};
52 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()}; 52 const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
53 53
54 if (resource_manager != nullptr) { 54 if (resource_manager != nullptr && resource_manager->GetNpad()) {
55 resource_manager->GetNpad()->InitializeVibrationDevice(vibration_device_handle); 55 resource_manager->GetNpad()->InitializeVibrationDevice(vibration_device_handle);
56 } 56 }
57 57
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 e5a78a914..feca5105f 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
@@ -74,6 +74,11 @@ std::optional<OutAttr> OutputAttrPointer(EmitContext& ctx, IR::Attribute attr) {
74 case IR::Attribute::ClipDistance7: { 74 case IR::Attribute::ClipDistance7: {
75 const u32 base{static_cast<u32>(IR::Attribute::ClipDistance0)}; 75 const u32 base{static_cast<u32>(IR::Attribute::ClipDistance0)};
76 const u32 index{static_cast<u32>(attr) - base}; 76 const u32 index{static_cast<u32>(attr) - base};
77 if (index >= ctx.profile.max_user_clip_distances) {
78 LOG_WARNING(Shader, "Ignoring clip distance store {} >= {} supported", index,
79 ctx.profile.max_user_clip_distances);
80 return std::nullopt;
81 }
77 const Id clip_num{ctx.Const(index)}; 82 const Id clip_num{ctx.Const(index)};
78 return OutputAccessChain(ctx, ctx.output_f32, ctx.clip_distances, clip_num); 83 return OutputAccessChain(ctx, ctx.output_f32, ctx.clip_distances, clip_num);
79 } 84 }
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
index 22ceca19c..800754554 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp
@@ -214,16 +214,16 @@ Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& ind
214 } 214 }
215} 215}
216 216
217Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) { 217std::pair<Id, bool> Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
218 if (!index.IsImmediate() || index.U32() != 0) { 218 if (!index.IsImmediate() || index.U32() != 0) {
219 throw NotImplementedException("Indirect image indexing"); 219 throw NotImplementedException("Indirect image indexing");
220 } 220 }
221 if (info.type == TextureType::Buffer) { 221 if (info.type == TextureType::Buffer) {
222 const ImageBufferDefinition def{ctx.image_buffers.at(info.descriptor_index)}; 222 const ImageBufferDefinition def{ctx.image_buffers.at(info.descriptor_index)};
223 return ctx.OpLoad(def.image_type, def.id); 223 return {ctx.OpLoad(def.image_type, def.id), def.is_integer};
224 } else { 224 } else {
225 const ImageDefinition def{ctx.images.at(info.descriptor_index)}; 225 const ImageDefinition def{ctx.images.at(info.descriptor_index)};
226 return ctx.OpLoad(def.image_type, def.id); 226 return {ctx.OpLoad(def.image_type, def.id), def.is_integer};
227 } 227 }
228} 228}
229 229
@@ -566,13 +566,23 @@ Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id co
566 LOG_WARNING(Shader_SPIRV, "Typeless image read not supported by host"); 566 LOG_WARNING(Shader_SPIRV, "Typeless image read not supported by host");
567 return ctx.ConstantNull(ctx.U32[4]); 567 return ctx.ConstantNull(ctx.U32[4]);
568 } 568 }
569 return Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst, ctx.U32[4], 569 const auto [image, is_integer] = Image(ctx, index, info);
570 Image(ctx, index, info), coords, std::nullopt, std::span<const Id>{}); 570 const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
571 Id color{Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst,
572 result_type, image, coords, std::nullopt, std::span<const Id>{})};
573 if (!is_integer) {
574 color = ctx.OpBitcast(ctx.U32[4], color);
575 }
576 return color;
571} 577}
572 578
573void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color) { 579void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color) {
574 const auto info{inst->Flags<IR::TextureInstInfo>()}; 580 const auto info{inst->Flags<IR::TextureInstInfo>()};
575 ctx.OpImageWrite(Image(ctx, index, info), coords, color); 581 const auto [image, is_integer] = Image(ctx, index, info);
582 if (!is_integer) {
583 color = ctx.OpBitcast(ctx.F32[4], color);
584 }
585 ctx.OpImageWrite(image, coords, color);
576} 586}
577 587
578Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index) { 588Id EmitIsTextureScaled(EmitContext& ctx, const IR::Value& index) {
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
index 3350f1f85..ed023fcfe 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp
@@ -74,20 +74,19 @@ spv::ImageFormat GetImageFormat(ImageFormat format) {
74 throw InvalidArgument("Invalid image format {}", format); 74 throw InvalidArgument("Invalid image format {}", format);
75} 75}
76 76
77Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) { 77Id ImageType(EmitContext& ctx, const ImageDescriptor& desc, Id sampled_type) {
78 const spv::ImageFormat format{GetImageFormat(desc.format)}; 78 const spv::ImageFormat format{GetImageFormat(desc.format)};
79 const Id type{ctx.U32[1]};
80 switch (desc.type) { 79 switch (desc.type) {
81 case TextureType::Color1D: 80 case TextureType::Color1D:
82 return ctx.TypeImage(type, spv::Dim::Dim1D, false, false, false, 2, format); 81 return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, false, false, 2, format);
83 case TextureType::ColorArray1D: 82 case TextureType::ColorArray1D:
84 return ctx.TypeImage(type, spv::Dim::Dim1D, false, true, false, 2, format); 83 return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, true, false, 2, format);
85 case TextureType::Color2D: 84 case TextureType::Color2D:
86 return ctx.TypeImage(type, spv::Dim::Dim2D, false, false, false, 2, format); 85 return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, false, false, 2, format);
87 case TextureType::ColorArray2D: 86 case TextureType::ColorArray2D:
88 return ctx.TypeImage(type, spv::Dim::Dim2D, false, true, false, 2, format); 87 return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, true, false, 2, format);
89 case TextureType::Color3D: 88 case TextureType::Color3D:
90 return ctx.TypeImage(type, spv::Dim::Dim3D, false, false, false, 2, format); 89 return ctx.TypeImage(sampled_type, spv::Dim::Dim3D, false, false, false, 2, format);
91 case TextureType::Buffer: 90 case TextureType::Buffer:
92 throw NotImplementedException("Image buffer"); 91 throw NotImplementedException("Image buffer");
93 default: 92 default:
@@ -1273,7 +1272,9 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
1273 throw NotImplementedException("Array of image buffers"); 1272 throw NotImplementedException("Array of image buffers");
1274 } 1273 }
1275 const spv::ImageFormat format{GetImageFormat(desc.format)}; 1274 const spv::ImageFormat format{GetImageFormat(desc.format)};
1276 const Id image_type{TypeImage(U32[1], spv::Dim::Buffer, false, false, false, 2, format)}; 1275 const Id sampled_type{desc.is_integer ? U32[1] : F32[1]};
1276 const Id image_type{
1277 TypeImage(sampled_type, spv::Dim::Buffer, false, false, false, 2, format)};
1277 const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; 1278 const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
1278 const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; 1279 const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
1279 Decorate(id, spv::Decoration::Binding, binding); 1280 Decorate(id, spv::Decoration::Binding, binding);
@@ -1283,6 +1284,7 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
1283 .id = id, 1284 .id = id,
1284 .image_type = image_type, 1285 .image_type = image_type,
1285 .count = desc.count, 1286 .count = desc.count,
1287 .is_integer = desc.is_integer,
1286 }); 1288 });
1287 if (profile.supported_spirv >= 0x00010400) { 1289 if (profile.supported_spirv >= 0x00010400) {
1288 interfaces.push_back(id); 1290 interfaces.push_back(id);
@@ -1327,7 +1329,8 @@ void EmitContext::DefineImages(const Info& info, u32& binding, u32& scaling_inde
1327 if (desc.count != 1) { 1329 if (desc.count != 1) {
1328 throw NotImplementedException("Array of images"); 1330 throw NotImplementedException("Array of images");
1329 } 1331 }
1330 const Id image_type{ImageType(*this, desc)}; 1332 const Id sampled_type{desc.is_integer ? U32[1] : F32[1]};
1333 const Id image_type{ImageType(*this, desc, sampled_type)};
1331 const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)}; 1334 const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
1332 const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)}; 1335 const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
1333 Decorate(id, spv::Decoration::Binding, binding); 1336 Decorate(id, spv::Decoration::Binding, binding);
@@ -1337,6 +1340,7 @@ void EmitContext::DefineImages(const Info& info, u32& binding, u32& scaling_inde
1337 .id = id, 1340 .id = id,
1338 .image_type = image_type, 1341 .image_type = image_type,
1339 .count = desc.count, 1342 .count = desc.count,
1343 .is_integer = desc.is_integer,
1340 }); 1344 });
1341 if (profile.supported_spirv >= 0x00010400) { 1345 if (profile.supported_spirv >= 0x00010400) {
1342 interfaces.push_back(id); 1346 interfaces.push_back(id);
@@ -1528,7 +1532,8 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
1528 if (stage == Stage::Fragment) { 1532 if (stage == Stage::Fragment) {
1529 throw NotImplementedException("Storing ClipDistance in fragment stage"); 1533 throw NotImplementedException("Storing ClipDistance in fragment stage");
1530 } 1534 }
1531 const Id type{TypeArray(F32[1], Const(8U))}; 1535 const Id type{TypeArray(
1536 F32[1], Const(std::min(info.used_clip_distances, profile.max_user_clip_distances)))};
1532 clip_distances = DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance); 1537 clip_distances = DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance);
1533 } 1538 }
1534 if (info.stores[IR::Attribute::Layer] && 1539 if (info.stores[IR::Attribute::Layer] &&
diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.h b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
index 1aa79863d..56019ad89 100644
--- a/src/shader_recompiler/backend/spirv/spirv_emit_context.h
+++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.h
@@ -47,12 +47,14 @@ struct ImageBufferDefinition {
47 Id id; 47 Id id;
48 Id image_type; 48 Id image_type;
49 u32 count; 49 u32 count;
50 bool is_integer;
50}; 51};
51 52
52struct ImageDefinition { 53struct ImageDefinition {
53 Id id; 54 Id id;
54 Id image_type; 55 Id image_type;
55 u32 count; 56 u32 count;
57 bool is_integer;
56}; 58};
57 59
58struct UniformDefinitions { 60struct UniformDefinitions {
diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h
index 15285ab0a..e30bf094a 100644
--- a/src/shader_recompiler/environment.h
+++ b/src/shader_recompiler/environment.h
@@ -24,6 +24,8 @@ public:
24 24
25 [[nodiscard]] virtual TexturePixelFormat ReadTexturePixelFormat(u32 raw_handle) = 0; 25 [[nodiscard]] virtual TexturePixelFormat ReadTexturePixelFormat(u32 raw_handle) = 0;
26 26
27 [[nodiscard]] virtual bool IsTexturePixelFormatInteger(u32 raw_handle) = 0;
28
27 [[nodiscard]] virtual u32 ReadViewportTransformState() = 0; 29 [[nodiscard]] virtual u32 ReadViewportTransformState() = 0;
28 30
29 [[nodiscard]] virtual u32 TextureBoundBuffer() const = 0; 31 [[nodiscard]] virtual u32 TextureBoundBuffer() const = 0;
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 70292686f..cb82a326c 100644
--- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
+++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp
@@ -913,7 +913,11 @@ void GatherInfoFromHeader(Environment& env, Info& info) {
913 } 913 }
914 for (size_t index = 0; index < 8; ++index) { 914 for (size_t index = 0; index < 8; ++index) {
915 const u16 mask{header.vtg.omap_systemc.clip_distances}; 915 const u16 mask{header.vtg.omap_systemc.clip_distances};
916 info.stores.Set(IR::Attribute::ClipDistance0 + index, ((mask >> index) & 1) != 0); 916 const bool used{((mask >> index) & 1) != 0};
917 info.stores.Set(IR::Attribute::ClipDistance0 + index, used);
918 if (used) {
919 info.used_clip_distances = static_cast<u32>(index) + 1;
920 }
917 } 921 }
918 info.stores.Set(IR::Attribute::PrimitiveId, 922 info.stores.Set(IR::Attribute::PrimitiveId,
919 header.vtg.omap_systemb.primitive_array_id != 0); 923 header.vtg.omap_systemb.primitive_array_id != 0);
diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
index ec12c843a..e4a73a360 100644
--- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
+++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp
@@ -815,6 +815,15 @@ bool FindGradient3DDerivatives(std::array<IR::Value, 3>& results, IR::Value coor
815 return true; 815 return true;
816} 816}
817 817
818void ConvertDerivatives(std::array<IR::Value, 3>& results, IR::IREmitter& ir) {
819 for (size_t i = 0; i < 3; i++) {
820 if (results[i].Type() == IR::Type::U32) {
821 results[i] = results[i].IsImmediate() ? ir.Imm32(Common::BitCast<f32>(results[i].U32()))
822 : ir.BitCast<IR::F32>(IR::U32(results[i]));
823 }
824 }
825}
826
818void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) { 827void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
819 IR::TextureInstInfo info = inst.Flags<IR::TextureInstInfo>(); 828 IR::TextureInstInfo info = inst.Flags<IR::TextureInstInfo>();
820 auto orig_opcode = inst.GetOpcode(); 829 auto orig_opcode = inst.GetOpcode();
@@ -831,12 +840,14 @@ void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
831 if (!offset.IsImmediate()) { 840 if (!offset.IsImmediate()) {
832 return; 841 return;
833 } 842 }
843 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
834 IR::Inst* const inst2 = coords.InstRecursive(); 844 IR::Inst* const inst2 = coords.InstRecursive();
835 std::array<std::array<IR::Value, 3>, 3> results_matrix; 845 std::array<std::array<IR::Value, 3>, 3> results_matrix;
836 for (size_t i = 0; i < 3; i++) { 846 for (size_t i = 0; i < 3; i++) {
837 if (!FindGradient3DDerivatives(results_matrix[i], inst2->Arg(i).Resolve())) { 847 if (!FindGradient3DDerivatives(results_matrix[i], inst2->Arg(i).Resolve())) {
838 return; 848 return;
839 } 849 }
850 ConvertDerivatives(results_matrix[i], ir);
840 } 851 }
841 IR::F32 lod_clamp{}; 852 IR::F32 lod_clamp{};
842 if (info.has_lod_clamp != 0) { 853 if (info.has_lod_clamp != 0) {
@@ -846,7 +857,6 @@ void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
846 lod_clamp = IR::F32{bias_lc}; 857 lod_clamp = IR::F32{bias_lc};
847 } 858 }
848 } 859 }
849 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
850 IR::Value new_coords = 860 IR::Value new_coords =
851 ir.CompositeConstruct(results_matrix[0][0], results_matrix[1][0], results_matrix[2][0]); 861 ir.CompositeConstruct(results_matrix[0][0], results_matrix[1][0], results_matrix[2][0]);
852 IR::Value derivatives_1 = ir.CompositeConstruct(results_matrix[0][1], results_matrix[0][2], 862 IR::Value derivatives_1 = ir.CompositeConstruct(results_matrix[0][1], results_matrix[0][2],
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp
index d374c976a..100437f0e 100644
--- a/src/shader_recompiler/ir_opt/texture_pass.cpp
+++ b/src/shader_recompiler/ir_opt/texture_pass.cpp
@@ -372,6 +372,10 @@ TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAdd
372 return env.ReadTexturePixelFormat(GetTextureHandle(env, cbuf)); 372 return env.ReadTexturePixelFormat(GetTextureHandle(env, cbuf));
373} 373}
374 374
375bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf) {
376 return env.IsTexturePixelFormatInteger(GetTextureHandle(env, cbuf));
377}
378
375class Descriptors { 379class Descriptors {
376public: 380public:
377 explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_, 381 explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_,
@@ -403,6 +407,7 @@ public:
403 })}; 407 })};
404 image_buffer_descriptors[index].is_written |= desc.is_written; 408 image_buffer_descriptors[index].is_written |= desc.is_written;
405 image_buffer_descriptors[index].is_read |= desc.is_read; 409 image_buffer_descriptors[index].is_read |= desc.is_read;
410 image_buffer_descriptors[index].is_integer |= desc.is_integer;
406 return index; 411 return index;
407 } 412 }
408 413
@@ -432,6 +437,7 @@ public:
432 })}; 437 })};
433 image_descriptors[index].is_written |= desc.is_written; 438 image_descriptors[index].is_written |= desc.is_written;
434 image_descriptors[index].is_read |= desc.is_read; 439 image_descriptors[index].is_read |= desc.is_read;
440 image_descriptors[index].is_integer |= desc.is_integer;
435 return index; 441 return index;
436 } 442 }
437 443
@@ -469,6 +475,20 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
469 ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1)))))); 475 ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1))))));
470} 476}
471 477
478bool IsPixelFormatSNorm(TexturePixelFormat pixel_format) {
479 switch (pixel_format) {
480 case TexturePixelFormat::A8B8G8R8_SNORM:
481 case TexturePixelFormat::R8G8_SNORM:
482 case TexturePixelFormat::R8_SNORM:
483 case TexturePixelFormat::R16G16B16A16_SNORM:
484 case TexturePixelFormat::R16G16_SNORM:
485 case TexturePixelFormat::R16_SNORM:
486 return true;
487 default:
488 return false;
489 }
490}
491
472void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) { 492void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) {
473 const auto it{IR::Block::InstructionList::s_iterator_to(inst)}; 493 const auto it{IR::Block::InstructionList::s_iterator_to(inst)};
474 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; 494 IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
@@ -587,11 +607,13 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
587 } 607 }
588 const bool is_written{inst->GetOpcode() != IR::Opcode::ImageRead}; 608 const bool is_written{inst->GetOpcode() != IR::Opcode::ImageRead};
589 const bool is_read{inst->GetOpcode() != IR::Opcode::ImageWrite}; 609 const bool is_read{inst->GetOpcode() != IR::Opcode::ImageWrite};
610 const bool is_integer{IsTexturePixelFormatInteger(env, cbuf)};
590 if (flags.type == TextureType::Buffer) { 611 if (flags.type == TextureType::Buffer) {
591 index = descriptors.Add(ImageBufferDescriptor{ 612 index = descriptors.Add(ImageBufferDescriptor{
592 .format = flags.image_format, 613 .format = flags.image_format,
593 .is_written = is_written, 614 .is_written = is_written,
594 .is_read = is_read, 615 .is_read = is_read,
616 .is_integer = is_integer,
595 .cbuf_index = cbuf.index, 617 .cbuf_index = cbuf.index,
596 .cbuf_offset = cbuf.offset, 618 .cbuf_offset = cbuf.offset,
597 .count = cbuf.count, 619 .count = cbuf.count,
@@ -603,6 +625,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
603 .format = flags.image_format, 625 .format = flags.image_format,
604 .is_written = is_written, 626 .is_written = is_written,
605 .is_read = is_read, 627 .is_read = is_read,
628 .is_integer = is_integer,
606 .cbuf_index = cbuf.index, 629 .cbuf_index = cbuf.index,
607 .cbuf_offset = cbuf.offset, 630 .cbuf_offset = cbuf.offset,
608 .count = cbuf.count, 631 .count = cbuf.count,
@@ -658,7 +681,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
658 if (!host_info.support_snorm_render_buffer && inst->GetOpcode() == IR::Opcode::ImageFetch && 681 if (!host_info.support_snorm_render_buffer && inst->GetOpcode() == IR::Opcode::ImageFetch &&
659 flags.type == TextureType::Buffer) { 682 flags.type == TextureType::Buffer) {
660 const auto pixel_format = ReadTexturePixelFormat(env, cbuf); 683 const auto pixel_format = ReadTexturePixelFormat(env, cbuf);
661 if (pixel_format != TexturePixelFormat::OTHER) { 684 if (IsPixelFormatSNorm(pixel_format)) {
662 PatchTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format); 685 PatchTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format);
663 } 686 }
664 } 687 }
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h
index 66901a965..7578d41cc 100644
--- a/src/shader_recompiler/profile.h
+++ b/src/shader_recompiler/profile.h
@@ -87,6 +87,8 @@ struct Profile {
87 bool has_broken_robust{}; 87 bool has_broken_robust{};
88 88
89 u64 min_ssbo_alignment{}; 89 u64 min_ssbo_alignment{};
90
91 u32 max_user_clip_distances{};
90}; 92};
91 93
92} // namespace Shader 94} // namespace Shader
diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h
index b4b4afd37..ed13e6820 100644
--- a/src/shader_recompiler/shader_info.h
+++ b/src/shader_recompiler/shader_info.h
@@ -35,14 +35,109 @@ enum class TextureType : u32 {
35}; 35};
36constexpr u32 NUM_TEXTURE_TYPES = 9; 36constexpr u32 NUM_TEXTURE_TYPES = 9;
37 37
38enum class TexturePixelFormat : u32 { 38enum class TexturePixelFormat {
39 A8B8G8R8_UNORM,
39 A8B8G8R8_SNORM, 40 A8B8G8R8_SNORM,
41 A8B8G8R8_SINT,
42 A8B8G8R8_UINT,
43 R5G6B5_UNORM,
44 B5G6R5_UNORM,
45 A1R5G5B5_UNORM,
46 A2B10G10R10_UNORM,
47 A2B10G10R10_UINT,
48 A2R10G10B10_UNORM,
49 A1B5G5R5_UNORM,
50 A5B5G5R1_UNORM,
51 R8_UNORM,
40 R8_SNORM, 52 R8_SNORM,
41 R8G8_SNORM, 53 R8_SINT,
54 R8_UINT,
55 R16G16B16A16_FLOAT,
56 R16G16B16A16_UNORM,
42 R16G16B16A16_SNORM, 57 R16G16B16A16_SNORM,
43 R16G16_SNORM, 58 R16G16B16A16_SINT,
59 R16G16B16A16_UINT,
60 B10G11R11_FLOAT,
61 R32G32B32A32_UINT,
62 BC1_RGBA_UNORM,
63 BC2_UNORM,
64 BC3_UNORM,
65 BC4_UNORM,
66 BC4_SNORM,
67 BC5_UNORM,
68 BC5_SNORM,
69 BC7_UNORM,
70 BC6H_UFLOAT,
71 BC6H_SFLOAT,
72 ASTC_2D_4X4_UNORM,
73 B8G8R8A8_UNORM,
74 R32G32B32A32_FLOAT,
75 R32G32B32A32_SINT,
76 R32G32_FLOAT,
77 R32G32_SINT,
78 R32_FLOAT,
79 R16_FLOAT,
80 R16_UNORM,
44 R16_SNORM, 81 R16_SNORM,
45 OTHER 82 R16_UINT,
83 R16_SINT,
84 R16G16_UNORM,
85 R16G16_FLOAT,
86 R16G16_UINT,
87 R16G16_SINT,
88 R16G16_SNORM,
89 R32G32B32_FLOAT,
90 A8B8G8R8_SRGB,
91 R8G8_UNORM,
92 R8G8_SNORM,
93 R8G8_SINT,
94 R8G8_UINT,
95 R32G32_UINT,
96 R16G16B16X16_FLOAT,
97 R32_UINT,
98 R32_SINT,
99 ASTC_2D_8X8_UNORM,
100 ASTC_2D_8X5_UNORM,
101 ASTC_2D_5X4_UNORM,
102 B8G8R8A8_SRGB,
103 BC1_RGBA_SRGB,
104 BC2_SRGB,
105 BC3_SRGB,
106 BC7_SRGB,
107 A4B4G4R4_UNORM,
108 G4R4_UNORM,
109 ASTC_2D_4X4_SRGB,
110 ASTC_2D_8X8_SRGB,
111 ASTC_2D_8X5_SRGB,
112 ASTC_2D_5X4_SRGB,
113 ASTC_2D_5X5_UNORM,
114 ASTC_2D_5X5_SRGB,
115 ASTC_2D_10X8_UNORM,
116 ASTC_2D_10X8_SRGB,
117 ASTC_2D_6X6_UNORM,
118 ASTC_2D_6X6_SRGB,
119 ASTC_2D_10X6_UNORM,
120 ASTC_2D_10X6_SRGB,
121 ASTC_2D_10X5_UNORM,
122 ASTC_2D_10X5_SRGB,
123 ASTC_2D_10X10_UNORM,
124 ASTC_2D_10X10_SRGB,
125 ASTC_2D_12X10_UNORM,
126 ASTC_2D_12X10_SRGB,
127 ASTC_2D_12X12_UNORM,
128 ASTC_2D_12X12_SRGB,
129 ASTC_2D_8X6_UNORM,
130 ASTC_2D_8X6_SRGB,
131 ASTC_2D_6X5_UNORM,
132 ASTC_2D_6X5_SRGB,
133 E5B9G9R9_FLOAT,
134 D32_FLOAT,
135 D16_UNORM,
136 X8_D24_UNORM,
137 S8_UINT,
138 D24_UNORM_S8_UINT,
139 S8_UINT_D24_UNORM,
140 D32_FLOAT_S8_UINT,
46}; 141};
47 142
48enum class ImageFormat : u32 { 143enum class ImageFormat : u32 {
@@ -97,6 +192,7 @@ struct ImageBufferDescriptor {
97 ImageFormat format; 192 ImageFormat format;
98 bool is_written; 193 bool is_written;
99 bool is_read; 194 bool is_read;
195 bool is_integer;
100 u32 cbuf_index; 196 u32 cbuf_index;
101 u32 cbuf_offset; 197 u32 cbuf_offset;
102 u32 count; 198 u32 count;
@@ -129,6 +225,7 @@ struct ImageDescriptor {
129 ImageFormat format; 225 ImageFormat format;
130 bool is_written; 226 bool is_written;
131 bool is_read; 227 bool is_read;
228 bool is_integer;
132 u32 cbuf_index; 229 u32 cbuf_index;
133 u32 cbuf_offset; 230 u32 cbuf_offset;
134 u32 count; 231 u32 count;
@@ -227,6 +324,8 @@ struct Info {
227 bool requires_layer_emulation{}; 324 bool requires_layer_emulation{};
228 IR::Attribute emulated_layer{}; 325 IR::Attribute emulated_layer{};
229 326
327 u32 used_clip_distances{};
328
230 boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS> 329 boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS>
231 constant_buffer_descriptors; 330 constant_buffer_descriptors;
232 boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors; 331 boost::container::static_vector<StorageBufferDescriptor, MAX_SSBOS> storage_buffers_descriptors;
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index a71866b75..b787b6994 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -58,6 +58,9 @@ Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rast
58 glObjectLabel(GL_BUFFER, buffer.handle, static_cast<GLsizei>(name.size()), name.data()); 58 glObjectLabel(GL_BUFFER, buffer.handle, static_cast<GLsizei>(name.size()), name.data());
59 } 59 }
60 glNamedBufferData(buffer.handle, SizeBytes(), nullptr, GL_DYNAMIC_DRAW); 60 glNamedBufferData(buffer.handle, SizeBytes(), nullptr, GL_DYNAMIC_DRAW);
61 if (runtime.has_unified_vertex_buffers) {
62 glGetNamedBufferParameterui64vNV(buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, &address);
63 }
61} 64}
62 65
63void Buffer::ImmediateUpload(size_t offset, std::span<const u8> data) noexcept { 66void Buffer::ImmediateUpload(size_t offset, std::span<const u8> data) noexcept {
@@ -109,6 +112,7 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_,
109 : device{device_}, staging_buffer_pool{staging_buffer_pool_}, 112 : device{device_}, staging_buffer_pool{staging_buffer_pool_},
110 has_fast_buffer_sub_data{device.HasFastBufferSubData()}, 113 has_fast_buffer_sub_data{device.HasFastBufferSubData()},
111 use_assembly_shaders{device.UseAssemblyShaders()}, 114 use_assembly_shaders{device.UseAssemblyShaders()},
115 has_unified_vertex_buffers{device.HasVertexBufferUnifiedMemory()},
112 stream_buffer{has_fast_buffer_sub_data ? std::nullopt : std::make_optional<StreamBuffer>()} { 116 stream_buffer{has_fast_buffer_sub_data ? std::nullopt : std::make_optional<StreamBuffer>()} {
113 GLint gl_max_attributes; 117 GLint gl_max_attributes;
114 glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &gl_max_attributes); 118 glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &gl_max_attributes);
@@ -210,8 +214,14 @@ void BufferCacheRuntime::ClearBuffer(Buffer& dest_buffer, u32 offset, size_t siz
210} 214}
211 215
212void BufferCacheRuntime::BindIndexBuffer(Buffer& buffer, u32 offset, u32 size) { 216void BufferCacheRuntime::BindIndexBuffer(Buffer& buffer, u32 offset, u32 size) {
213 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.Handle()); 217 if (has_unified_vertex_buffers) {
214 index_buffer_offset = offset; 218 buffer.MakeResident(GL_READ_ONLY);
219 glBufferAddressRangeNV(GL_ELEMENT_ARRAY_ADDRESS_NV, 0, buffer.HostGpuAddr() + offset,
220 static_cast<GLsizeiptr>(Common::AlignUp(size, 4)));
221 } else {
222 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.Handle());
223 index_buffer_offset = offset;
224 }
215} 225}
216 226
217void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size, 227void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size,
@@ -219,8 +229,15 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset,
219 if (index >= max_attributes) { 229 if (index >= max_attributes) {
220 return; 230 return;
221 } 231 }
222 glBindVertexBuffer(index, buffer.Handle(), static_cast<GLintptr>(offset), 232 if (has_unified_vertex_buffers) {
223 static_cast<GLsizei>(stride)); 233 buffer.MakeResident(GL_READ_ONLY);
234 glBindVertexBuffer(index, 0, 0, static_cast<GLsizei>(stride));
235 glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, index,
236 buffer.HostGpuAddr() + offset, static_cast<GLsizeiptr>(size));
237 } else {
238 glBindVertexBuffer(index, buffer.Handle(), static_cast<GLintptr>(offset),
239 static_cast<GLsizei>(stride));
240 }
224} 241}
225 242
226void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) { 243void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
@@ -233,9 +250,23 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
233 [](u64 stride) { return static_cast<GLsizei>(stride); }); 250 [](u64 stride) { return static_cast<GLsizei>(stride); });
234 const u32 count = 251 const u32 count =
235 std::min(static_cast<u32>(bindings.buffers.size()), max_attributes - bindings.min_index); 252 std::min(static_cast<u32>(bindings.buffers.size()), max_attributes - bindings.min_index);
236 glBindVertexBuffers(bindings.min_index, static_cast<GLsizei>(count), buffer_handles.data(), 253 if (has_unified_vertex_buffers) {
237 reinterpret_cast<const GLintptr*>(bindings.offsets.data()), 254 for (u32 index = 0; index < count; ++index) {
238 buffer_strides.data()); 255 Buffer& buffer = *bindings.buffers[index];
256 buffer.MakeResident(GL_READ_ONLY);
257 glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, bindings.min_index + index,
258 buffer.HostGpuAddr() + bindings.offsets[index],
259 static_cast<GLsizeiptr>(bindings.sizes[index]));
260 }
261 static constexpr std::array<size_t, 32> ZEROS{};
262 glBindVertexBuffers(bindings.min_index, static_cast<GLsizei>(count),
263 reinterpret_cast<const GLuint*>(ZEROS.data()),
264 reinterpret_cast<const GLintptr*>(ZEROS.data()), buffer_strides.data());
265 } else {
266 glBindVertexBuffers(bindings.min_index, static_cast<GLsizei>(count), buffer_handles.data(),
267 reinterpret_cast<const GLintptr*>(bindings.offsets.data()),
268 buffer_strides.data());
269 }
239} 270}
240 271
241void BufferCacheRuntime::BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer, 272void BufferCacheRuntime::BindUniformBuffer(size_t stage, u32 binding_index, Buffer& buffer,
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 71cd45d35..1e8708f59 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -209,6 +209,7 @@ private:
209 209
210 bool has_fast_buffer_sub_data = false; 210 bool has_fast_buffer_sub_data = false;
211 bool use_assembly_shaders = false; 211 bool use_assembly_shaders = false;
212 bool has_unified_vertex_buffers = false;
212 213
213 bool use_storage_buffers = false; 214 bool use_storage_buffers = false;
214 215
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index a6c93068f..993438a27 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -200,6 +200,7 @@ Device::Device(Core::Frontend::EmuWindow& emu_window) {
200 has_broken_texture_view_formats = is_amd || (!is_linux && is_intel); 200 has_broken_texture_view_formats = is_amd || (!is_linux && is_intel);
201 has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2; 201 has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2;
202 has_derivative_control = GLAD_GL_ARB_derivative_control; 202 has_derivative_control = GLAD_GL_ARB_derivative_control;
203 has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory;
203 has_debugging_tool_attached = IsDebugToolAttached(extensions); 204 has_debugging_tool_attached = IsDebugToolAttached(extensions);
204 has_depth_buffer_float = HasExtension(extensions, "GL_NV_depth_buffer_float"); 205 has_depth_buffer_float = HasExtension(extensions, "GL_NV_depth_buffer_float");
205 has_geometry_shader_passthrough = GLAD_GL_NV_geometry_shader_passthrough; 206 has_geometry_shader_passthrough = GLAD_GL_NV_geometry_shader_passthrough;
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index 96034ea4a..a5a6bbbba 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -72,6 +72,10 @@ public:
72 return has_texture_shadow_lod; 72 return has_texture_shadow_lod;
73 } 73 }
74 74
75 bool HasVertexBufferUnifiedMemory() const {
76 return has_vertex_buffer_unified_memory;
77 }
78
75 bool HasASTC() const { 79 bool HasASTC() const {
76 return has_astc; 80 return has_astc;
77 } 81 }
@@ -211,6 +215,7 @@ private:
211 bool has_vertex_viewport_layer{}; 215 bool has_vertex_viewport_layer{};
212 bool has_image_load_formatted{}; 216 bool has_image_load_formatted{};
213 bool has_texture_shadow_lod{}; 217 bool has_texture_shadow_lod{};
218 bool has_vertex_buffer_unified_memory{};
214 bool has_astc{}; 219 bool has_astc{};
215 bool has_variable_aoffi{}; 220 bool has_variable_aoffi{};
216 bool has_component_indexing_bug{}; 221 bool has_component_indexing_bug{};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 8111f77a8..339950d2e 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -178,14 +178,18 @@ void RasterizerOpenGL::Clear(u32 layer_count) {
178 SyncFramebufferSRGB(); 178 SyncFramebufferSRGB();
179 } 179 }
180 if (regs.clear_surface.Z) { 180 if (regs.clear_surface.Z) {
181 ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!"); 181 if (regs.zeta_enable != 0) {
182 LOG_DEBUG(Render_OpenGL, "Tried to clear Z but buffer is not enabled!");
183 }
182 use_depth = true; 184 use_depth = true;
183 185
184 state_tracker.NotifyDepthMask(); 186 state_tracker.NotifyDepthMask();
185 glDepthMask(GL_TRUE); 187 glDepthMask(GL_TRUE);
186 } 188 }
187 if (regs.clear_surface.S) { 189 if (regs.clear_surface.S) {
188 ASSERT_MSG(regs.zeta_enable, "Tried to clear stencil but buffer is not enabled!"); 190 if (regs.zeta_enable) {
191 LOG_DEBUG(Render_OpenGL, "Tried to clear stencil but buffer is not enabled!");
192 }
189 use_stencil = true; 193 use_stencil = true;
190 } 194 }
191 195
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 26f2d0ea7..30df41b7d 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -51,7 +51,7 @@ using VideoCommon::LoadPipelines;
51using VideoCommon::SerializePipeline; 51using VideoCommon::SerializePipeline;
52using Context = ShaderContext::Context; 52using Context = ShaderContext::Context;
53 53
54constexpr u32 CACHE_VERSION = 9; 54constexpr u32 CACHE_VERSION = 10;
55 55
56template <typename Container> 56template <typename Container>
57auto MakeSpan(Container& container) { 57auto MakeSpan(Container& container) {
@@ -233,6 +233,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
233 .ignore_nan_fp_comparisons = true, 233 .ignore_nan_fp_comparisons = true,
234 .gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(), 234 .gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
235 .min_ssbo_alignment = device.GetShaderStorageBufferAlignment(), 235 .min_ssbo_alignment = device.GetShaderStorageBufferAlignment(),
236 .max_user_clip_distances = 8,
236 }, 237 },
237 host_info{ 238 host_info{
238 .support_float64 = true, 239 .support_float64 = true,
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 7a4f0c5c1..2933718b6 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -168,6 +168,14 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
168 if (!GLAD_GL_ARB_seamless_cubemap_per_texture && !GLAD_GL_AMD_seamless_cubemap_per_texture) { 168 if (!GLAD_GL_ARB_seamless_cubemap_per_texture && !GLAD_GL_AMD_seamless_cubemap_per_texture) {
169 glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); 169 glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
170 } 170 }
171 // Enable unified vertex attributes and query vertex buffer address when the driver supports it
172 if (device.HasVertexBufferUnifiedMemory()) {
173 glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV);
174 glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV);
175 glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY);
176 glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV,
177 &vertex_buffer_address);
178 }
171} 179}
172 180
173RendererOpenGL::~RendererOpenGL() = default; 181RendererOpenGL::~RendererOpenGL() = default;
@@ -667,7 +675,13 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
667 offsetof(ScreenRectVertex, tex_coord)); 675 offsetof(ScreenRectVertex, tex_coord));
668 glVertexAttribBinding(PositionLocation, 0); 676 glVertexAttribBinding(PositionLocation, 0);
669 glVertexAttribBinding(TexCoordLocation, 0); 677 glVertexAttribBinding(TexCoordLocation, 0);
670 glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); 678 if (device.HasVertexBufferUnifiedMemory()) {
679 glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex));
680 glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address,
681 sizeof(vertices));
682 } else {
683 glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex));
684 }
671 685
672 if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) { 686 if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) {
673 glBindSampler(0, present_sampler.handle); 687 glBindSampler(0, present_sampler.handle);
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 5958f52f7..3c61799fa 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -78,8 +78,15 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo
78} 78}
79} // Anonymous namespace 79} // Anonymous namespace
80 80
81Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params) 81Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_params)
82 : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params), tracker{4096} {} 82 : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params), tracker{4096} {
83 if (runtime.device.HasNullDescriptor()) {
84 return;
85 }
86 device = &runtime.device;
87 buffer = runtime.CreateNullBuffer();
88 is_null = true;
89}
83 90
84Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_, 91Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_,
85 VAddr cpu_addr_, u64 size_bytes_) 92 VAddr cpu_addr_, u64 size_bytes_)
@@ -93,8 +100,12 @@ Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rast
93 100
94VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format) { 101VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format) {
95 if (!device) { 102 if (!device) {
96 // Null buffer, return a null descriptor 103 // Null buffer supported, return a null descriptor
97 return VK_NULL_HANDLE; 104 return VK_NULL_HANDLE;
105 } else if (is_null) {
106 // Null buffer not supported, adjust offset and size
107 offset = 0;
108 size = 0;
98 } 109 }
99 const auto it{std::ranges::find_if(views, [offset, size, format](const BufferView& view) { 110 const auto it{std::ranges::find_if(views, [offset, size, format](const BufferView& view) {
100 return offset == view.offset && size == view.size && format == view.format; 111 return offset == view.offset && size == view.size && format == view.format;
@@ -563,22 +574,27 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
563 } 574 }
564 buffer_handles.push_back(handle); 575 buffer_handles.push_back(handle);
565 } 576 }
577 const u32 device_max = device.GetMaxVertexInputBindings();
578 const u32 min_binding = std::min(bindings.min_index, device_max);
579 const u32 max_binding = std::min(bindings.max_index, device_max);
580 const u32 binding_count = max_binding - min_binding;
581 if (binding_count == 0) {
582 return;
583 }
566 if (device.IsExtExtendedDynamicStateSupported()) { 584 if (device.IsExtExtendedDynamicStateSupported()) {
567 scheduler.Record([this, bindings_ = std::move(bindings), 585 scheduler.Record([bindings_ = std::move(bindings),
568 buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { 586 buffer_handles_ = std::move(buffer_handles),
569 cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, 587 binding_count](vk::CommandBuffer cmdbuf) {
570 std::min(bindings_.max_index - bindings_.min_index, 588 cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, binding_count, buffer_handles_.data(),
571 device.GetMaxVertexInputBindings()), 589 bindings_.offsets.data(), bindings_.sizes.data(),
572 buffer_handles_.data(), bindings_.offsets.data(), 590 bindings_.strides.data());
573 bindings_.sizes.data(), bindings_.strides.data());
574 }); 591 });
575 } else { 592 } else {
576 scheduler.Record([this, bindings_ = std::move(bindings), 593 scheduler.Record([bindings_ = std::move(bindings),
577 buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { 594 buffer_handles_ = std::move(buffer_handles),
578 cmdbuf.BindVertexBuffers(bindings_.min_index, 595 binding_count](vk::CommandBuffer cmdbuf) {
579 std::min(bindings_.max_index - bindings_.min_index, 596 cmdbuf.BindVertexBuffers(bindings_.min_index, binding_count, buffer_handles_.data(),
580 device.GetMaxVertexInputBindings()), 597 bindings_.offsets.data());
581 buffer_handles_.data(), bindings_.offsets.data());
582 }); 598 });
583 } 599 }
584} 600}
@@ -622,9 +638,12 @@ void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings<
622} 638}
623 639
624void BufferCacheRuntime::ReserveNullBuffer() { 640void BufferCacheRuntime::ReserveNullBuffer() {
625 if (null_buffer) { 641 if (!null_buffer) {
626 return; 642 null_buffer = CreateNullBuffer();
627 } 643 }
644}
645
646vk::Buffer BufferCacheRuntime::CreateNullBuffer() {
628 VkBufferCreateInfo create_info{ 647 VkBufferCreateInfo create_info{
629 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, 648 .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
630 .pNext = nullptr, 649 .pNext = nullptr,
@@ -639,15 +658,17 @@ void BufferCacheRuntime::ReserveNullBuffer() {
639 if (device.IsExtTransformFeedbackSupported()) { 658 if (device.IsExtTransformFeedbackSupported()) {
640 create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT; 659 create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
641 } 660 }
642 null_buffer = memory_allocator.CreateBuffer(create_info, MemoryUsage::DeviceLocal); 661 vk::Buffer ret = memory_allocator.CreateBuffer(create_info, MemoryUsage::DeviceLocal);
643 if (device.HasDebuggingToolAttached()) { 662 if (device.HasDebuggingToolAttached()) {
644 null_buffer.SetObjectNameEXT("Null buffer"); 663 ret.SetObjectNameEXT("Null buffer");
645 } 664 }
646 665
647 scheduler.RequestOutsideRenderPassOperationContext(); 666 scheduler.RequestOutsideRenderPassOperationContext();
648 scheduler.Record([buffer = *null_buffer](vk::CommandBuffer cmdbuf) { 667 scheduler.Record([buffer = *ret](vk::CommandBuffer cmdbuf) {
649 cmdbuf.FillBuffer(buffer, 0, VK_WHOLE_SIZE, 0); 668 cmdbuf.FillBuffer(buffer, 0, VK_WHOLE_SIZE, 0);
650 }); 669 });
670
671 return ret;
651} 672}
652 673
653} // namespace Vulkan 674} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 0b3fbd6d0..dc300d7cb 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -63,6 +63,7 @@ private:
63 vk::Buffer buffer; 63 vk::Buffer buffer;
64 std::vector<BufferView> views; 64 std::vector<BufferView> views;
65 VideoCommon::UsageTracker tracker; 65 VideoCommon::UsageTracker tracker;
66 bool is_null{};
66}; 67};
67 68
68class QuadArrayIndexBuffer; 69class QuadArrayIndexBuffer;
@@ -151,6 +152,7 @@ private:
151 } 152 }
152 153
153 void ReserveNullBuffer(); 154 void ReserveNullBuffer();
155 vk::Buffer CreateNullBuffer();
154 156
155 const Device& device; 157 const Device& device;
156 MemoryAllocator& memory_allocator; 158 MemoryAllocator& memory_allocator;
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 2a13b2a72..d1841198d 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -54,7 +54,7 @@ using VideoCommon::FileEnvironment;
54using VideoCommon::GenericEnvironment; 54using VideoCommon::GenericEnvironment;
55using VideoCommon::GraphicsEnvironment; 55using VideoCommon::GraphicsEnvironment;
56 56
57constexpr u32 CACHE_VERSION = 10; 57constexpr u32 CACHE_VERSION = 11;
58constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'}; 58constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
59 59
60template <typename Container> 60template <typename Container>
@@ -374,6 +374,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
374 .has_broken_robust = 374 .has_broken_robust =
375 device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal, 375 device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
376 .min_ssbo_alignment = device.GetStorageBufferAlignment(), 376 .min_ssbo_alignment = device.GetStorageBufferAlignment(),
377 .max_user_clip_distances = device.GetMaxUserClipDistances(),
377 }; 378 };
378 379
379 host_info = Shader::HostTranslateInfo{ 380 host_info = Shader::HostTranslateInfo{
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 078777cdd..95954ade7 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -289,12 +289,15 @@ public:
289 } 289 }
290 290
291 if (has_multi_queries) { 291 if (has_multi_queries) {
292 size_t intermediary_buffer_index = ObtainBuffer<false>(num_slots_used); 292 const size_t min_accumulation_limit =
293 std::min(first_accumulation_checkpoint, num_slots_used);
294 const size_t max_accumulation_limit =
295 std::max(last_accumulation_checkpoint, num_slots_used);
296 const size_t intermediary_buffer_index = ObtainBuffer<false>(num_slots_used);
293 resolve_buffers.push_back(intermediary_buffer_index); 297 resolve_buffers.push_back(intermediary_buffer_index);
294 queries_prefix_scan_pass->Run(*accumulation_buffer, *buffers[intermediary_buffer_index], 298 queries_prefix_scan_pass->Run(*accumulation_buffer, *buffers[intermediary_buffer_index],
295 *buffers[resolve_buffer_index], num_slots_used, 299 *buffers[resolve_buffer_index], num_slots_used,
296 std::min(first_accumulation_checkpoint, num_slots_used), 300 min_accumulation_limit, max_accumulation_limit);
297 last_accumulation_checkpoint);
298 301
299 } else { 302 } else {
300 scheduler.RequestOutsideRenderPassOperationContext(); 303 scheduler.RequestOutsideRenderPassOperationContext();
diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp
index 4edbe5700..492440ac4 100644
--- a/src/video_core/shader_environment.cpp
+++ b/src/video_core/shader_environment.cpp
@@ -62,23 +62,9 @@ static Shader::TextureType ConvertTextureType(const Tegra::Texture::TICEntry& en
62} 62}
63 63
64static Shader::TexturePixelFormat ConvertTexturePixelFormat(const Tegra::Texture::TICEntry& entry) { 64static Shader::TexturePixelFormat ConvertTexturePixelFormat(const Tegra::Texture::TICEntry& entry) {
65 switch (PixelFormatFromTextureInfo(entry.format, entry.r_type, entry.g_type, entry.b_type, 65 return static_cast<Shader::TexturePixelFormat>(
66 entry.a_type, entry.srgb_conversion)) { 66 PixelFormatFromTextureInfo(entry.format, entry.r_type, entry.g_type, entry.b_type,
67 case VideoCore::Surface::PixelFormat::A8B8G8R8_SNORM: 67 entry.a_type, entry.srgb_conversion));
68 return Shader::TexturePixelFormat::A8B8G8R8_SNORM;
69 case VideoCore::Surface::PixelFormat::R8_SNORM:
70 return Shader::TexturePixelFormat::R8_SNORM;
71 case VideoCore::Surface::PixelFormat::R8G8_SNORM:
72 return Shader::TexturePixelFormat::R8G8_SNORM;
73 case VideoCore::Surface::PixelFormat::R16G16B16A16_SNORM:
74 return Shader::TexturePixelFormat::R16G16B16A16_SNORM;
75 case VideoCore::Surface::PixelFormat::R16G16_SNORM:
76 return Shader::TexturePixelFormat::R16G16_SNORM;
77 case VideoCore::Surface::PixelFormat::R16_SNORM:
78 return Shader::TexturePixelFormat::R16_SNORM;
79 default:
80 return Shader::TexturePixelFormat::OTHER;
81 }
82} 68}
83 69
84static std::string_view StageToPrefix(Shader::Stage stage) { 70static std::string_view StageToPrefix(Shader::Stage stage) {
@@ -398,6 +384,11 @@ Shader::TexturePixelFormat GraphicsEnvironment::ReadTexturePixelFormat(u32 handl
398 return result; 384 return result;
399} 385}
400 386
387bool GraphicsEnvironment::IsTexturePixelFormatInteger(u32 handle) {
388 return VideoCore::Surface::IsPixelFormatInteger(
389 static_cast<VideoCore::Surface::PixelFormat>(ReadTexturePixelFormat(handle)));
390}
391
401u32 GraphicsEnvironment::ReadViewportTransformState() { 392u32 GraphicsEnvironment::ReadViewportTransformState() {
402 const auto& regs{maxwell3d->regs}; 393 const auto& regs{maxwell3d->regs};
403 viewport_transform_state = regs.viewport_scale_offset_enabled; 394 viewport_transform_state = regs.viewport_scale_offset_enabled;
@@ -448,6 +439,11 @@ Shader::TexturePixelFormat ComputeEnvironment::ReadTexturePixelFormat(u32 handle
448 return result; 439 return result;
449} 440}
450 441
442bool ComputeEnvironment::IsTexturePixelFormatInteger(u32 handle) {
443 return VideoCore::Surface::IsPixelFormatInteger(
444 static_cast<VideoCore::Surface::PixelFormat>(ReadTexturePixelFormat(handle)));
445}
446
451u32 ComputeEnvironment::ReadViewportTransformState() { 447u32 ComputeEnvironment::ReadViewportTransformState() {
452 return viewport_transform_state; 448 return viewport_transform_state;
453} 449}
@@ -551,6 +547,11 @@ Shader::TexturePixelFormat FileEnvironment::ReadTexturePixelFormat(u32 handle) {
551 return it->second; 547 return it->second;
552} 548}
553 549
550bool FileEnvironment::IsTexturePixelFormatInteger(u32 handle) {
551 return VideoCore::Surface::IsPixelFormatInteger(
552 static_cast<VideoCore::Surface::PixelFormat>(ReadTexturePixelFormat(handle)));
553}
554
554u32 FileEnvironment::ReadViewportTransformState() { 555u32 FileEnvironment::ReadViewportTransformState() {
555 return viewport_transform_state; 556 return viewport_transform_state;
556} 557}
diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h
index b90f3d44e..6b372e336 100644
--- a/src/video_core/shader_environment.h
+++ b/src/video_core/shader_environment.h
@@ -115,6 +115,8 @@ public:
115 115
116 Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override; 116 Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
117 117
118 bool IsTexturePixelFormatInteger(u32 handle) override;
119
118 u32 ReadViewportTransformState() override; 120 u32 ReadViewportTransformState() override;
119 121
120 std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(u32 bank, u32 offset) override; 122 std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(u32 bank, u32 offset) override;
@@ -139,6 +141,8 @@ public:
139 141
140 Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override; 142 Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
141 143
144 bool IsTexturePixelFormatInteger(u32 handle) override;
145
142 u32 ReadViewportTransformState() override; 146 u32 ReadViewportTransformState() override;
143 147
144 std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer( 148 std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(
@@ -171,6 +175,8 @@ public:
171 175
172 [[nodiscard]] Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override; 176 [[nodiscard]] Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
173 177
178 [[nodiscard]] bool IsTexturePixelFormatInteger(u32 handle) override;
179
174 [[nodiscard]] u32 ReadViewportTransformState() override; 180 [[nodiscard]] u32 ReadViewportTransformState() override;
175 181
176 [[nodiscard]] u32 LocalMemorySize() const override; 182 [[nodiscard]] u32 LocalMemorySize() const override;
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index 1fda0042d..a6fbca69e 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -695,6 +695,11 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
695 std::min(properties.properties.limits.maxVertexInputBindings, 16U); 695 std::min(properties.properties.limits.maxVertexInputBindings, 16U);
696 } 696 }
697 697
698 if (is_turnip) {
699 LOG_WARNING(Render_Vulkan, "Turnip requires higher-than-reported binding limits");
700 properties.properties.limits.maxVertexInputBindings = 32;
701 }
702
698 if (!extensions.extended_dynamic_state && extensions.extended_dynamic_state2) { 703 if (!extensions.extended_dynamic_state && extensions.extended_dynamic_state2) {
699 LOG_INFO(Render_Vulkan, 704 LOG_INFO(Render_Vulkan,
700 "Removing extendedDynamicState2 due to missing extendedDynamicState"); 705 "Removing extendedDynamicState2 due to missing extendedDynamicState");
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index 4f3846345..701817086 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -665,6 +665,10 @@ public:
665 return properties.properties.limits.maxViewports; 665 return properties.properties.limits.maxViewports;
666 } 666 }
667 667
668 u32 GetMaxUserClipDistances() const {
669 return properties.properties.limits.maxClipDistances;
670 }
671
668 bool SupportsConditionalBarriers() const { 672 bool SupportsConditionalBarriers() const {
669 return supports_conditional_barriers; 673 return supports_conditional_barriers;
670 } 674 }
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp
index 70cf14afa..2f78b8af0 100644
--- a/src/video_core/vulkan_common/vulkan_wrapper.cpp
+++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp
@@ -377,6 +377,8 @@ const char* ToString(VkResult result) noexcept {
377 return "VK_OPERATION_DEFERRED_KHR"; 377 return "VK_OPERATION_DEFERRED_KHR";
378 case VkResult::VK_OPERATION_NOT_DEFERRED_KHR: 378 case VkResult::VK_OPERATION_NOT_DEFERRED_KHR:
379 return "VK_OPERATION_NOT_DEFERRED_KHR"; 379 return "VK_OPERATION_NOT_DEFERRED_KHR";
380 case VkResult::VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR:
381 return "VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR";
380 case VkResult::VK_PIPELINE_COMPILE_REQUIRED_EXT: 382 case VkResult::VK_PIPELINE_COMPILE_REQUIRED_EXT:
381 return "VK_PIPELINE_COMPILE_REQUIRED_EXT"; 383 return "VK_PIPELINE_COMPILE_REQUIRED_EXT";
382 case VkResult::VK_RESULT_MAX_ENUM: 384 case VkResult::VK_RESULT_MAX_ENUM:
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index fd6bebf0f..0836bcb7e 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -106,32 +106,30 @@ ConfigureGraphics::ConfigureGraphics(
106 Settings::values.bg_green.GetValue(), 106 Settings::values.bg_green.GetValue(),
107 Settings::values.bg_blue.GetValue())); 107 Settings::values.bg_blue.GetValue()));
108 UpdateAPILayout(); 108 UpdateAPILayout();
109 PopulateVSyncModeSelection(); //< must happen after UpdateAPILayout 109 PopulateVSyncModeSelection(false); //< must happen after UpdateAPILayout
110 110
111 // VSync setting needs to be determined after populating the VSync combobox 111 // VSync setting needs to be determined after populating the VSync combobox
112 if (Settings::IsConfiguringGlobal()) { 112 const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue();
113 const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue(); 113 const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting);
114 const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting); 114 int index{};
115 int index{}; 115 for (const auto mode : vsync_mode_combobox_enum_map) {
116 for (const auto mode : vsync_mode_combobox_enum_map) { 116 if (mode == vsync_mode) {
117 if (mode == vsync_mode) { 117 break;
118 break;
119 }
120 index++;
121 }
122 if (static_cast<unsigned long>(index) < vsync_mode_combobox_enum_map.size()) {
123 vsync_mode_combobox->setCurrentIndex(index);
124 } 118 }
119 index++;
120 }
121 if (static_cast<unsigned long>(index) < vsync_mode_combobox_enum_map.size()) {
122 vsync_mode_combobox->setCurrentIndex(index);
125 } 123 }
126 124
127 connect(api_combobox, qOverload<int>(&QComboBox::activated), this, [this] { 125 connect(api_combobox, qOverload<int>(&QComboBox::activated), this, [this] {
128 UpdateAPILayout(); 126 UpdateAPILayout();
129 PopulateVSyncModeSelection(); 127 PopulateVSyncModeSelection(false);
130 }); 128 });
131 connect(vulkan_device_combobox, qOverload<int>(&QComboBox::activated), this, 129 connect(vulkan_device_combobox, qOverload<int>(&QComboBox::activated), this,
132 [this](int device) { 130 [this](int device) {
133 UpdateDeviceSelection(device); 131 UpdateDeviceSelection(device);
134 PopulateVSyncModeSelection(); 132 PopulateVSyncModeSelection(false);
135 }); 133 });
136 connect(shader_backend_combobox, qOverload<int>(&QComboBox::activated), this, 134 connect(shader_backend_combobox, qOverload<int>(&QComboBox::activated), this,
137 [this](int backend) { UpdateShaderBackendSelection(backend); }); 135 [this](int backend) { UpdateShaderBackendSelection(backend); });
@@ -147,8 +145,9 @@ ConfigureGraphics::ConfigureGraphics(
147 const auto& update_screenshot_info = [this, &builder]() { 145 const auto& update_screenshot_info = [this, &builder]() {
148 const auto& combobox_enumerations = builder.ComboboxTranslations().at( 146 const auto& combobox_enumerations = builder.ComboboxTranslations().at(
149 Settings::EnumMetadata<Settings::AspectRatio>::Index()); 147 Settings::EnumMetadata<Settings::AspectRatio>::Index());
150 const auto index = aspect_ratio_combobox->currentIndex(); 148 const auto ratio_index = aspect_ratio_combobox->currentIndex();
151 const auto ratio = static_cast<Settings::AspectRatio>(combobox_enumerations[index].first); 149 const auto ratio =
150 static_cast<Settings::AspectRatio>(combobox_enumerations[ratio_index].first);
152 151
153 const auto& combobox_enumerations_resolution = builder.ComboboxTranslations().at( 152 const auto& combobox_enumerations_resolution = builder.ComboboxTranslations().at(
154 Settings::EnumMetadata<Settings::ResolutionSetup>::Index()); 153 Settings::EnumMetadata<Settings::ResolutionSetup>::Index());
@@ -174,11 +173,7 @@ ConfigureGraphics::ConfigureGraphics(
174 } 173 }
175} 174}
176 175
177void ConfigureGraphics::PopulateVSyncModeSelection() { 176void ConfigureGraphics::PopulateVSyncModeSelection(bool use_setting) {
178 if (!Settings::IsConfiguringGlobal()) {
179 return;
180 }
181
182 const Settings::RendererBackend backend{GetCurrentGraphicsBackend()}; 177 const Settings::RendererBackend backend{GetCurrentGraphicsBackend()};
183 if (backend == Settings::RendererBackend::Null) { 178 if (backend == Settings::RendererBackend::Null) {
184 vsync_mode_combobox->setEnabled(false); 179 vsync_mode_combobox->setEnabled(false);
@@ -189,8 +184,9 @@ void ConfigureGraphics::PopulateVSyncModeSelection() {
189 const int current_index = //< current selected vsync mode from combobox 184 const int current_index = //< current selected vsync mode from combobox
190 vsync_mode_combobox->currentIndex(); 185 vsync_mode_combobox->currentIndex();
191 const auto current_mode = //< current selected vsync mode as a VkPresentModeKHR 186 const auto current_mode = //< current selected vsync mode as a VkPresentModeKHR
192 current_index == -1 ? VSyncSettingToMode(Settings::values.vsync_mode.GetValue()) 187 current_index == -1 || use_setting
193 : vsync_mode_combobox_enum_map[current_index]; 188 ? VSyncSettingToMode(Settings::values.vsync_mode.GetValue())
189 : vsync_mode_combobox_enum_map[current_index];
194 int index{}; 190 int index{};
195 const int device{vulkan_device_combobox->currentIndex()}; //< current selected Vulkan device 191 const int device{vulkan_device_combobox->currentIndex()}; //< current selected Vulkan device
196 192
@@ -214,6 +210,23 @@ void ConfigureGraphics::PopulateVSyncModeSelection() {
214 } 210 }
215 index++; 211 index++;
216 } 212 }
213
214 if (!Settings::IsConfiguringGlobal()) {
215 vsync_restore_global_button->setVisible(!Settings::values.vsync_mode.UsingGlobal());
216
217 const Settings::VSyncMode global_vsync_mode = Settings::values.vsync_mode.GetValue(true);
218 vsync_restore_global_button->setEnabled(
219 (backend == Settings::RendererBackend::OpenGL &&
220 (global_vsync_mode == Settings::VSyncMode::Immediate ||
221 global_vsync_mode == Settings::VSyncMode::Fifo)) ||
222 backend == Settings::RendererBackend::Vulkan);
223 }
224}
225
226void ConfigureGraphics::UpdateVsyncSetting() const {
227 const auto mode = vsync_mode_combobox_enum_map[vsync_mode_combobox->currentIndex()];
228 const auto vsync_mode = PresentModeToSetting(mode);
229 Settings::values.vsync_mode.SetValue(vsync_mode);
217} 230}
218 231
219void ConfigureGraphics::UpdateDeviceSelection(int device) { 232void ConfigureGraphics::UpdateDeviceSelection(int device) {
@@ -299,6 +312,33 @@ void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) {
299 } else if (setting->Id() == Settings::values.vsync_mode.Id()) { 312 } else if (setting->Id() == Settings::values.vsync_mode.Id()) {
300 // Keep track of vsync_mode's combobox so we can populate it 313 // Keep track of vsync_mode's combobox so we can populate it
301 vsync_mode_combobox = widget->combobox; 314 vsync_mode_combobox = widget->combobox;
315
316 // Since vsync is populated at runtime, we have to manually set up the button for
317 // restoring the global setting.
318 if (!Settings::IsConfiguringGlobal()) {
319 QPushButton* restore_button =
320 ConfigurationShared::Widget::CreateRestoreGlobalButton(
321 Settings::values.vsync_mode.UsingGlobal(), widget);
322 restore_button->setEnabled(true);
323 widget->layout()->addWidget(restore_button);
324
325 QObject::connect(restore_button, &QAbstractButton::clicked,
326 [restore_button, this](bool) {
327 Settings::values.vsync_mode.SetGlobal(true);
328 PopulateVSyncModeSelection(true);
329
330 restore_button->setVisible(false);
331 });
332
333 std::function<void()> set_non_global = [restore_button, this]() {
334 Settings::values.vsync_mode.SetGlobal(false);
335 UpdateVsyncSetting();
336 restore_button->setVisible(true);
337 };
338 QObject::connect(widget->combobox, QOverload<int>::of(&QComboBox::activated),
339 [set_non_global]() { set_non_global(); });
340 vsync_restore_global_button = restore_button;
341 }
302 hold_graphics.emplace(setting->Id(), widget); 342 hold_graphics.emplace(setting->Id(), widget);
303 } else if (setting->Id() == Settings::values.aspect_ratio.Id()) { 343 } else if (setting->Id() == Settings::values.aspect_ratio.Id()) {
304 // Keep track of the aspect ratio combobox to update other UI tabs that need it 344 // Keep track of the aspect ratio combobox to update other UI tabs that need it
@@ -400,11 +440,7 @@ void ConfigureGraphics::ApplyConfiguration() {
400 func(powered_on); 440 func(powered_on);
401 } 441 }
402 442
403 if (Settings::IsConfiguringGlobal()) { 443 UpdateVsyncSetting();
404 const auto mode = vsync_mode_combobox_enum_map[vsync_mode_combobox->currentIndex()];
405 const auto vsync_mode = PresentModeToSetting(mode);
406 Settings::values.vsync_mode.SetValue(vsync_mode);
407 }
408 444
409 Settings::values.vulkan_device.SetGlobal(true); 445 Settings::values.vulkan_device.SetGlobal(true);
410 Settings::values.shader_backend.SetGlobal(true); 446 Settings::values.shader_backend.SetGlobal(true);
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index 9c24a56db..5c8286836 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -62,7 +62,8 @@ private:
62 62
63 void Setup(const ConfigurationShared::Builder& builder); 63 void Setup(const ConfigurationShared::Builder& builder);
64 64
65 void PopulateVSyncModeSelection(); 65 void PopulateVSyncModeSelection(bool use_setting);
66 void UpdateVsyncSetting() const;
66 void UpdateBackgroundColorButton(QColor color); 67 void UpdateBackgroundColorButton(QColor color);
67 void UpdateAPILayout(); 68 void UpdateAPILayout();
68 void UpdateDeviceSelection(int device); 69 void UpdateDeviceSelection(int device);
@@ -104,6 +105,7 @@ private:
104 QComboBox* api_combobox; 105 QComboBox* api_combobox;
105 QComboBox* shader_backend_combobox; 106 QComboBox* shader_backend_combobox;
106 QComboBox* vsync_mode_combobox; 107 QComboBox* vsync_mode_combobox;
108 QPushButton* vsync_restore_global_button;
107 QWidget* vulkan_device_widget; 109 QWidget* vulkan_device_widget;
108 QWidget* api_widget; 110 QWidget* api_widget;
109 QWidget* shader_backend_widget; 111 QWidget* shader_backend_widget;
diff --git a/src/yuzu/configuration/qt_config.cpp b/src/yuzu/configuration/qt_config.cpp
index 417a43ec5..a71000b72 100644
--- a/src/yuzu/configuration/qt_config.cpp
+++ b/src/yuzu/configuration/qt_config.cpp
@@ -225,6 +225,8 @@ void QtConfig::ReadPathValues() {
225 QString::fromStdString(ReadStringSetting(std::string("recentFiles"))) 225 QString::fromStdString(ReadStringSetting(std::string("recentFiles")))
226 .split(QStringLiteral(", "), Qt::SkipEmptyParts, Qt::CaseSensitive); 226 .split(QStringLiteral(", "), Qt::SkipEmptyParts, Qt::CaseSensitive);
227 227
228 ReadCategory(Settings::Category::Paths);
229
228 EndGroup(); 230 EndGroup();
229} 231}
230 232
@@ -405,6 +407,8 @@ void QtConfig::SaveQtControlValues() {
405void QtConfig::SavePathValues() { 407void QtConfig::SavePathValues() {
406 BeginGroup(Settings::TranslateCategory(Settings::Category::Paths)); 408 BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
407 409
410 WriteCategory(Settings::Category::Paths);
411
408 WriteSetting(std::string("romsPath"), UISettings::values.roms_path); 412 WriteSetting(std::string("romsPath"), UISettings::values.roms_path);
409 BeginArray(std::string("gamedirs")); 413 BeginArray(std::string("gamedirs"));
410 for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { 414 for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {