summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--CMakeLists.txt2
m---------externals/Vulkan-Headers0
-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
36 files changed, 431 insertions, 118 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9dfc06ac3..7cd6b2108 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -305,7 +305,7 @@ find_package(ZLIB 1.2 REQUIRED)
find_package(zstd 1.5 REQUIRED)
if (NOT YUZU_USE_EXTERNAL_VULKAN_HEADERS)
- find_package(Vulkan 1.3.256 REQUIRED)
+ find_package(Vulkan 1.3.274 REQUIRED)
endif()
if (ENABLE_LIBUSB)
diff --git a/externals/Vulkan-Headers b/externals/Vulkan-Headers
-Subproject df60f0316899460eeaaefa06d2dd7e4e300c160
+Subproject 80207f9da86423ce33aff8328a792fd715f3c08
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() {
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
- gamesViewModel.isReloading.collect { binding.swipeRefresh.isRefreshing = it }
+ gamesViewModel.isReloading.collect {
+ binding.swipeRefresh.isRefreshing = it
+ if (gamesViewModel.games.value.isEmpty() && !it) {
+ binding.noticeText.visibility = View.VISIBLE
+ } else {
+ binding.noticeText.visibility = View.INVISIBLE
+ }
+ }
}
}
launch {
repeatOnLifecycle(Lifecycle.State.RESUMED) {
gamesViewModel.games.collectLatest {
(binding.gridGames.adapter as GameAdapter).submitList(it)
- if (it.isEmpty()) {
- binding.noticeText.visibility = View.VISIBLE
- } else {
- binding.noticeText.visibility = View.GONE
- }
}
}
}
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) {
}
using namespace Common::Literals;
-constexpr u32 StackSize = 32_KiB;
+constexpr u32 StackSize = 128_KiB;
} // namespace
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 @@
#include "common/bit_cast.h"
#include "core/arm/nce/interpreter_visitor.h"
-#include <dynarmic/frontend/A64/decoder/a64.h>
-
namespace Core {
template <u32 BitSize>
@@ -249,6 +247,7 @@ bool InterpreterVisitor::LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) {
return false;
}
+ // Size in bytes
const u64 size = 4 << opc.ZeroExtend();
const u64 offset = imm19.SignExtend<u64>() << 2;
const u64 address = this->GetPc() + offset;
@@ -530,7 +529,7 @@ bool InterpreterVisitor::SIMDImmediate(bool wback, bool postindex, size_t scale,
}
case MemOp::Load: {
u128 data{};
- m_memory.ReadBlock(address, &data, datasize);
+ m_memory.ReadBlock(address, &data, datasize / 8);
this->SetVec(Vt, data);
break;
}
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 @@
#pragma once
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+
#include <dynarmic/frontend/A64/a64_types.h>
+#include <dynarmic/frontend/A64/decoder/a64.h>
#include <dynarmic/frontend/imm.h>
+#pragma GCC diagnostic pop
+
namespace Core {
class 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:
IPC::RequestParser rp{ctx};
const auto vibration_device_handle{rp.PopRaw<Core::HID::VibrationDeviceHandle>()};
- if (resource_manager != nullptr) {
+ if (resource_manager != nullptr && resource_manager->GetNpad()) {
resource_manager->GetNpad()->InitializeVibrationDevice(vibration_device_handle);
}
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) {
case IR::Attribute::ClipDistance7: {
const u32 base{static_cast<u32>(IR::Attribute::ClipDistance0)};
const u32 index{static_cast<u32>(attr) - base};
+ if (index >= ctx.profile.max_user_clip_distances) {
+ LOG_WARNING(Shader, "Ignoring clip distance store {} >= {} supported", index,
+ ctx.profile.max_user_clip_distances);
+ return std::nullopt;
+ }
const Id clip_num{ctx.Const(index)};
return OutputAccessChain(ctx, ctx.output_f32, ctx.clip_distances, clip_num);
}
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
}
}
-Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
+std::pair<Id, bool> Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
if (!index.IsImmediate() || index.U32() != 0) {
throw NotImplementedException("Indirect image indexing");
}
if (info.type == TextureType::Buffer) {
const ImageBufferDefinition def{ctx.image_buffers.at(info.descriptor_index)};
- return ctx.OpLoad(def.image_type, def.id);
+ return {ctx.OpLoad(def.image_type, def.id), def.is_integer};
} else {
const ImageDefinition def{ctx.images.at(info.descriptor_index)};
- return ctx.OpLoad(def.image_type, def.id);
+ return {ctx.OpLoad(def.image_type, def.id), def.is_integer};
}
}
@@ -566,13 +566,23 @@ Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id co
LOG_WARNING(Shader_SPIRV, "Typeless image read not supported by host");
return ctx.ConstantNull(ctx.U32[4]);
}
- return Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst, ctx.U32[4],
- Image(ctx, index, info), coords, std::nullopt, std::span<const Id>{});
+ const auto [image, is_integer] = Image(ctx, index, info);
+ const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
+ Id color{Emit(&EmitContext::OpImageSparseRead, &EmitContext::OpImageRead, ctx, inst,
+ result_type, image, coords, std::nullopt, std::span<const Id>{})};
+ if (!is_integer) {
+ color = ctx.OpBitcast(ctx.U32[4], color);
+ }
+ return color;
}
void EmitImageWrite(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id color) {
const auto info{inst->Flags<IR::TextureInstInfo>()};
- ctx.OpImageWrite(Image(ctx, index, info), coords, color);
+ const auto [image, is_integer] = Image(ctx, index, info);
+ if (!is_integer) {
+ color = ctx.OpBitcast(ctx.F32[4], color);
+ }
+ ctx.OpImageWrite(image, coords, color);
}
Id 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) {
throw InvalidArgument("Invalid image format {}", format);
}
-Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) {
+Id ImageType(EmitContext& ctx, const ImageDescriptor& desc, Id sampled_type) {
const spv::ImageFormat format{GetImageFormat(desc.format)};
- const Id type{ctx.U32[1]};
switch (desc.type) {
case TextureType::Color1D:
- return ctx.TypeImage(type, spv::Dim::Dim1D, false, false, false, 2, format);
+ return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, false, false, 2, format);
case TextureType::ColorArray1D:
- return ctx.TypeImage(type, spv::Dim::Dim1D, false, true, false, 2, format);
+ return ctx.TypeImage(sampled_type, spv::Dim::Dim1D, false, true, false, 2, format);
case TextureType::Color2D:
- return ctx.TypeImage(type, spv::Dim::Dim2D, false, false, false, 2, format);
+ return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, false, false, 2, format);
case TextureType::ColorArray2D:
- return ctx.TypeImage(type, spv::Dim::Dim2D, false, true, false, 2, format);
+ return ctx.TypeImage(sampled_type, spv::Dim::Dim2D, false, true, false, 2, format);
case TextureType::Color3D:
- return ctx.TypeImage(type, spv::Dim::Dim3D, false, false, false, 2, format);
+ return ctx.TypeImage(sampled_type, spv::Dim::Dim3D, false, false, false, 2, format);
case TextureType::Buffer:
throw NotImplementedException("Image buffer");
default:
@@ -1273,7 +1272,9 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
throw NotImplementedException("Array of image buffers");
}
const spv::ImageFormat format{GetImageFormat(desc.format)};
- const Id image_type{TypeImage(U32[1], spv::Dim::Buffer, false, false, false, 2, format)};
+ const Id sampled_type{desc.is_integer ? U32[1] : F32[1]};
+ const Id image_type{
+ TypeImage(sampled_type, spv::Dim::Buffer, false, false, false, 2, format)};
const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
Decorate(id, spv::Decoration::Binding, binding);
@@ -1283,6 +1284,7 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
.id = id,
.image_type = image_type,
.count = desc.count,
+ .is_integer = desc.is_integer,
});
if (profile.supported_spirv >= 0x00010400) {
interfaces.push_back(id);
@@ -1327,7 +1329,8 @@ void EmitContext::DefineImages(const Info& info, u32& binding, u32& scaling_inde
if (desc.count != 1) {
throw NotImplementedException("Array of images");
}
- const Id image_type{ImageType(*this, desc)};
+ const Id sampled_type{desc.is_integer ? U32[1] : F32[1]};
+ const Id image_type{ImageType(*this, desc, sampled_type)};
const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
Decorate(id, spv::Decoration::Binding, binding);
@@ -1337,6 +1340,7 @@ void EmitContext::DefineImages(const Info& info, u32& binding, u32& scaling_inde
.id = id,
.image_type = image_type,
.count = desc.count,
+ .is_integer = desc.is_integer,
});
if (profile.supported_spirv >= 0x00010400) {
interfaces.push_back(id);
@@ -1528,7 +1532,8 @@ void EmitContext::DefineOutputs(const IR::Program& program) {
if (stage == Stage::Fragment) {
throw NotImplementedException("Storing ClipDistance in fragment stage");
}
- const Id type{TypeArray(F32[1], Const(8U))};
+ const Id type{TypeArray(
+ F32[1], Const(std::min(info.used_clip_distances, profile.max_user_clip_distances)))};
clip_distances = DefineOutput(*this, type, invocations, spv::BuiltIn::ClipDistance);
}
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 {
Id id;
Id image_type;
u32 count;
+ bool is_integer;
};
struct ImageDefinition {
Id id;
Id image_type;
u32 count;
+ bool is_integer;
};
struct 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:
[[nodiscard]] virtual TexturePixelFormat ReadTexturePixelFormat(u32 raw_handle) = 0;
+ [[nodiscard]] virtual bool IsTexturePixelFormatInteger(u32 raw_handle) = 0;
+
[[nodiscard]] virtual u32 ReadViewportTransformState() = 0;
[[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) {
}
for (size_t index = 0; index < 8; ++index) {
const u16 mask{header.vtg.omap_systemc.clip_distances};
- info.stores.Set(IR::Attribute::ClipDistance0 + index, ((mask >> index) & 1) != 0);
+ const bool used{((mask >> index) & 1) != 0};
+ info.stores.Set(IR::Attribute::ClipDistance0 + index, used);
+ if (used) {
+ info.used_clip_distances = static_cast<u32>(index) + 1;
+ }
}
info.stores.Set(IR::Attribute::PrimitiveId,
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
return true;
}
+void ConvertDerivatives(std::array<IR::Value, 3>& results, IR::IREmitter& ir) {
+ for (size_t i = 0; i < 3; i++) {
+ if (results[i].Type() == IR::Type::U32) {
+ results[i] = results[i].IsImmediate() ? ir.Imm32(Common::BitCast<f32>(results[i].U32()))
+ : ir.BitCast<IR::F32>(IR::U32(results[i]));
+ }
+ }
+}
+
void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
IR::TextureInstInfo info = inst.Flags<IR::TextureInstInfo>();
auto orig_opcode = inst.GetOpcode();
@@ -831,12 +840,14 @@ void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
if (!offset.IsImmediate()) {
return;
}
+ IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
IR::Inst* const inst2 = coords.InstRecursive();
std::array<std::array<IR::Value, 3>, 3> results_matrix;
for (size_t i = 0; i < 3; i++) {
if (!FindGradient3DDerivatives(results_matrix[i], inst2->Arg(i).Resolve())) {
return;
}
+ ConvertDerivatives(results_matrix[i], ir);
}
IR::F32 lod_clamp{};
if (info.has_lod_clamp != 0) {
@@ -846,7 +857,6 @@ void FoldImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
lod_clamp = IR::F32{bias_lc};
}
}
- IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
IR::Value new_coords =
ir.CompositeConstruct(results_matrix[0][0], results_matrix[1][0], results_matrix[2][0]);
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
return env.ReadTexturePixelFormat(GetTextureHandle(env, cbuf));
}
+bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf) {
+ return env.IsTexturePixelFormatInteger(GetTextureHandle(env, cbuf));
+}
+
class Descriptors {
public:
explicit Descriptors(TextureBufferDescriptors& texture_buffer_descriptors_,
@@ -403,6 +407,7 @@ public:
})};
image_buffer_descriptors[index].is_written |= desc.is_written;
image_buffer_descriptors[index].is_read |= desc.is_read;
+ image_buffer_descriptors[index].is_integer |= desc.is_integer;
return index;
}
@@ -432,6 +437,7 @@ public:
})};
image_descriptors[index].is_written |= desc.is_written;
image_descriptors[index].is_read |= desc.is_read;
+ image_descriptors[index].is_integer |= desc.is_integer;
return index;
}
@@ -469,6 +475,20 @@ void PatchImageSampleImplicitLod(IR::Block& block, IR::Inst& inst) {
ir.FPRecip(ir.ConvertUToF(32, 32, ir.CompositeExtract(texture_size, 1))))));
}
+bool IsPixelFormatSNorm(TexturePixelFormat pixel_format) {
+ switch (pixel_format) {
+ case TexturePixelFormat::A8B8G8R8_SNORM:
+ case TexturePixelFormat::R8G8_SNORM:
+ case TexturePixelFormat::R8_SNORM:
+ case TexturePixelFormat::R16G16B16A16_SNORM:
+ case TexturePixelFormat::R16G16_SNORM:
+ case TexturePixelFormat::R16_SNORM:
+ return true;
+ default:
+ return false;
+ }
+}
+
void PatchTexelFetch(IR::Block& block, IR::Inst& inst, TexturePixelFormat pixel_format) {
const auto it{IR::Block::InstructionList::s_iterator_to(inst)};
IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)};
@@ -587,11 +607,13 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
}
const bool is_written{inst->GetOpcode() != IR::Opcode::ImageRead};
const bool is_read{inst->GetOpcode() != IR::Opcode::ImageWrite};
+ const bool is_integer{IsTexturePixelFormatInteger(env, cbuf)};
if (flags.type == TextureType::Buffer) {
index = descriptors.Add(ImageBufferDescriptor{
.format = flags.image_format,
.is_written = is_written,
.is_read = is_read,
+ .is_integer = is_integer,
.cbuf_index = cbuf.index,
.cbuf_offset = cbuf.offset,
.count = cbuf.count,
@@ -603,6 +625,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
.format = flags.image_format,
.is_written = is_written,
.is_read = is_read,
+ .is_integer = is_integer,
.cbuf_index = cbuf.index,
.cbuf_offset = cbuf.offset,
.count = cbuf.count,
@@ -658,7 +681,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
if (!host_info.support_snorm_render_buffer && inst->GetOpcode() == IR::Opcode::ImageFetch &&
flags.type == TextureType::Buffer) {
const auto pixel_format = ReadTexturePixelFormat(env, cbuf);
- if (pixel_format != TexturePixelFormat::OTHER) {
+ if (IsPixelFormatSNorm(pixel_format)) {
PatchTexelFetch(*texture_inst.block, *texture_inst.inst, pixel_format);
}
}
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 {
bool has_broken_robust{};
u64 min_ssbo_alignment{};
+
+ u32 max_user_clip_distances{};
};
} // 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 {
};
constexpr u32 NUM_TEXTURE_TYPES = 9;
-enum class TexturePixelFormat : u32 {
+enum class TexturePixelFormat {
+ A8B8G8R8_UNORM,
A8B8G8R8_SNORM,
+ A8B8G8R8_SINT,
+ A8B8G8R8_UINT,
+ R5G6B5_UNORM,
+ B5G6R5_UNORM,
+ A1R5G5B5_UNORM,
+ A2B10G10R10_UNORM,
+ A2B10G10R10_UINT,
+ A2R10G10B10_UNORM,
+ A1B5G5R5_UNORM,
+ A5B5G5R1_UNORM,
+ R8_UNORM,
R8_SNORM,
- R8G8_SNORM,
+ R8_SINT,
+ R8_UINT,
+ R16G16B16A16_FLOAT,
+ R16G16B16A16_UNORM,
R16G16B16A16_SNORM,
- R16G16_SNORM,
+ R16G16B16A16_SINT,
+ R16G16B16A16_UINT,
+ B10G11R11_FLOAT,
+ R32G32B32A32_UINT,
+ BC1_RGBA_UNORM,
+ BC2_UNORM,
+ BC3_UNORM,
+ BC4_UNORM,
+ BC4_SNORM,
+ BC5_UNORM,
+ BC5_SNORM,
+ BC7_UNORM,
+ BC6H_UFLOAT,
+ BC6H_SFLOAT,
+ ASTC_2D_4X4_UNORM,
+ B8G8R8A8_UNORM,
+ R32G32B32A32_FLOAT,
+ R32G32B32A32_SINT,
+ R32G32_FLOAT,
+ R32G32_SINT,
+ R32_FLOAT,
+ R16_FLOAT,
+ R16_UNORM,
R16_SNORM,
- OTHER
+ R16_UINT,
+ R16_SINT,
+ R16G16_UNORM,
+ R16G16_FLOAT,
+ R16G16_UINT,
+ R16G16_SINT,
+ R16G16_SNORM,
+ R32G32B32_FLOAT,
+ A8B8G8R8_SRGB,
+ R8G8_UNORM,
+ R8G8_SNORM,
+ R8G8_SINT,
+ R8G8_UINT,
+ R32G32_UINT,
+ R16G16B16X16_FLOAT,
+ R32_UINT,
+ R32_SINT,
+ ASTC_2D_8X8_UNORM,
+ ASTC_2D_8X5_UNORM,
+ ASTC_2D_5X4_UNORM,
+ B8G8R8A8_SRGB,
+ BC1_RGBA_SRGB,
+ BC2_SRGB,
+ BC3_SRGB,
+ BC7_SRGB,
+ A4B4G4R4_UNORM,
+ G4R4_UNORM,
+ ASTC_2D_4X4_SRGB,
+ ASTC_2D_8X8_SRGB,
+ ASTC_2D_8X5_SRGB,
+ ASTC_2D_5X4_SRGB,
+ ASTC_2D_5X5_UNORM,
+ ASTC_2D_5X5_SRGB,
+ ASTC_2D_10X8_UNORM,
+ ASTC_2D_10X8_SRGB,
+ ASTC_2D_6X6_UNORM,
+ ASTC_2D_6X6_SRGB,
+ ASTC_2D_10X6_UNORM,
+ ASTC_2D_10X6_SRGB,
+ ASTC_2D_10X5_UNORM,
+ ASTC_2D_10X5_SRGB,
+ ASTC_2D_10X10_UNORM,
+ ASTC_2D_10X10_SRGB,
+ ASTC_2D_12X10_UNORM,
+ ASTC_2D_12X10_SRGB,
+ ASTC_2D_12X12_UNORM,
+ ASTC_2D_12X12_SRGB,
+ ASTC_2D_8X6_UNORM,
+ ASTC_2D_8X6_SRGB,
+ ASTC_2D_6X5_UNORM,
+ ASTC_2D_6X5_SRGB,
+ E5B9G9R9_FLOAT,
+ D32_FLOAT,
+ D16_UNORM,
+ X8_D24_UNORM,
+ S8_UINT,
+ D24_UNORM_S8_UINT,
+ S8_UINT_D24_UNORM,
+ D32_FLOAT_S8_UINT,
};
enum class ImageFormat : u32 {
@@ -97,6 +192,7 @@ struct ImageBufferDescriptor {
ImageFormat format;
bool is_written;
bool is_read;
+ bool is_integer;
u32 cbuf_index;
u32 cbuf_offset;
u32 count;
@@ -129,6 +225,7 @@ struct ImageDescriptor {
ImageFormat format;
bool is_written;
bool is_read;
+ bool is_integer;
u32 cbuf_index;
u32 cbuf_offset;
u32 count;
@@ -227,6 +324,8 @@ struct Info {
bool requires_layer_emulation{};
IR::Attribute emulated_layer{};
+ u32 used_clip_distances{};
+
boost::container::static_vector<ConstantBufferDescriptor, MAX_CBUFS>
constant_buffer_descriptors;
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
glObjectLabel(GL_BUFFER, buffer.handle, static_cast<GLsizei>(name.size()), name.data());
}
glNamedBufferData(buffer.handle, SizeBytes(), nullptr, GL_DYNAMIC_DRAW);
+ if (runtime.has_unified_vertex_buffers) {
+ glGetNamedBufferParameterui64vNV(buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, &address);
+ }
}
void Buffer::ImmediateUpload(size_t offset, std::span<const u8> data) noexcept {
@@ -109,6 +112,7 @@ BufferCacheRuntime::BufferCacheRuntime(const Device& device_,
: device{device_}, staging_buffer_pool{staging_buffer_pool_},
has_fast_buffer_sub_data{device.HasFastBufferSubData()},
use_assembly_shaders{device.UseAssemblyShaders()},
+ has_unified_vertex_buffers{device.HasVertexBufferUnifiedMemory()},
stream_buffer{has_fast_buffer_sub_data ? std::nullopt : std::make_optional<StreamBuffer>()} {
GLint gl_max_attributes;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &gl_max_attributes);
@@ -210,8 +214,14 @@ void BufferCacheRuntime::ClearBuffer(Buffer& dest_buffer, u32 offset, size_t siz
}
void BufferCacheRuntime::BindIndexBuffer(Buffer& buffer, u32 offset, u32 size) {
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.Handle());
- index_buffer_offset = offset;
+ if (has_unified_vertex_buffers) {
+ buffer.MakeResident(GL_READ_ONLY);
+ glBufferAddressRangeNV(GL_ELEMENT_ARRAY_ADDRESS_NV, 0, buffer.HostGpuAddr() + offset,
+ static_cast<GLsizeiptr>(Common::AlignUp(size, 4)));
+ } else {
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer.Handle());
+ index_buffer_offset = offset;
+ }
}
void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset, u32 size,
@@ -219,8 +229,15 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, Buffer& buffer, u32 offset,
if (index >= max_attributes) {
return;
}
- glBindVertexBuffer(index, buffer.Handle(), static_cast<GLintptr>(offset),
- static_cast<GLsizei>(stride));
+ if (has_unified_vertex_buffers) {
+ buffer.MakeResident(GL_READ_ONLY);
+ glBindVertexBuffer(index, 0, 0, static_cast<GLsizei>(stride));
+ glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, index,
+ buffer.HostGpuAddr() + offset, static_cast<GLsizeiptr>(size));
+ } else {
+ glBindVertexBuffer(index, buffer.Handle(), static_cast<GLintptr>(offset),
+ static_cast<GLsizei>(stride));
+ }
}
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
@@ -233,9 +250,23 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
[](u64 stride) { return static_cast<GLsizei>(stride); });
const u32 count =
std::min(static_cast<u32>(bindings.buffers.size()), max_attributes - bindings.min_index);
- glBindVertexBuffers(bindings.min_index, static_cast<GLsizei>(count), buffer_handles.data(),
- reinterpret_cast<const GLintptr*>(bindings.offsets.data()),
- buffer_strides.data());
+ if (has_unified_vertex_buffers) {
+ for (u32 index = 0; index < count; ++index) {
+ Buffer& buffer = *bindings.buffers[index];
+ buffer.MakeResident(GL_READ_ONLY);
+ glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, bindings.min_index + index,
+ buffer.HostGpuAddr() + bindings.offsets[index],
+ static_cast<GLsizeiptr>(bindings.sizes[index]));
+ }
+ static constexpr std::array<size_t, 32> ZEROS{};
+ glBindVertexBuffers(bindings.min_index, static_cast<GLsizei>(count),
+ reinterpret_cast<const GLuint*>(ZEROS.data()),
+ reinterpret_cast<const GLintptr*>(ZEROS.data()), buffer_strides.data());
+ } else {
+ glBindVertexBuffers(bindings.min_index, static_cast<GLsizei>(count), buffer_handles.data(),
+ reinterpret_cast<const GLintptr*>(bindings.offsets.data()),
+ buffer_strides.data());
+ }
}
void 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:
bool has_fast_buffer_sub_data = false;
bool use_assembly_shaders = false;
+ bool has_unified_vertex_buffers = false;
bool use_storage_buffers = false;
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) {
has_broken_texture_view_formats = is_amd || (!is_linux && is_intel);
has_nv_viewport_array2 = GLAD_GL_NV_viewport_array2;
has_derivative_control = GLAD_GL_ARB_derivative_control;
+ has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory;
has_debugging_tool_attached = IsDebugToolAttached(extensions);
has_depth_buffer_float = HasExtension(extensions, "GL_NV_depth_buffer_float");
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:
return has_texture_shadow_lod;
}
+ bool HasVertexBufferUnifiedMemory() const {
+ return has_vertex_buffer_unified_memory;
+ }
+
bool HasASTC() const {
return has_astc;
}
@@ -211,6 +215,7 @@ private:
bool has_vertex_viewport_layer{};
bool has_image_load_formatted{};
bool has_texture_shadow_lod{};
+ bool has_vertex_buffer_unified_memory{};
bool has_astc{};
bool has_variable_aoffi{};
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) {
SyncFramebufferSRGB();
}
if (regs.clear_surface.Z) {
- ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!");
+ if (regs.zeta_enable != 0) {
+ LOG_DEBUG(Render_OpenGL, "Tried to clear Z but buffer is not enabled!");
+ }
use_depth = true;
state_tracker.NotifyDepthMask();
glDepthMask(GL_TRUE);
}
if (regs.clear_surface.S) {
- ASSERT_MSG(regs.zeta_enable, "Tried to clear stencil but buffer is not enabled!");
+ if (regs.zeta_enable) {
+ LOG_DEBUG(Render_OpenGL, "Tried to clear stencil but buffer is not enabled!");
+ }
use_stencil = true;
}
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;
using VideoCommon::SerializePipeline;
using Context = ShaderContext::Context;
-constexpr u32 CACHE_VERSION = 9;
+constexpr u32 CACHE_VERSION = 10;
template <typename Container>
auto MakeSpan(Container& container) {
@@ -233,6 +233,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
.ignore_nan_fp_comparisons = true,
.gl_max_compute_smem_size = device.GetMaxComputeSharedMemorySize(),
.min_ssbo_alignment = device.GetShaderStorageBufferAlignment(),
+ .max_user_clip_distances = 8,
},
host_info{
.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_,
if (!GLAD_GL_ARB_seamless_cubemap_per_texture && !GLAD_GL_AMD_seamless_cubemap_per_texture) {
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
}
+ // Enable unified vertex attributes and query vertex buffer address when the driver supports it
+ if (device.HasVertexBufferUnifiedMemory()) {
+ glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV);
+ glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV);
+ glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY);
+ glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV,
+ &vertex_buffer_address);
+ }
}
RendererOpenGL::~RendererOpenGL() = default;
@@ -667,7 +675,13 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
offsetof(ScreenRectVertex, tex_coord));
glVertexAttribBinding(PositionLocation, 0);
glVertexAttribBinding(TexCoordLocation, 0);
- glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex));
+ if (device.HasVertexBufferUnifiedMemory()) {
+ glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex));
+ glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address,
+ sizeof(vertices));
+ } else {
+ glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex));
+ }
if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) {
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
}
} // Anonymous namespace
-Buffer::Buffer(BufferCacheRuntime&, VideoCommon::NullBufferParams null_params)
- : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params), tracker{4096} {}
+Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_params)
+ : VideoCommon::BufferBase<VideoCore::RasterizerInterface>(null_params), tracker{4096} {
+ if (runtime.device.HasNullDescriptor()) {
+ return;
+ }
+ device = &runtime.device;
+ buffer = runtime.CreateNullBuffer();
+ is_null = true;
+}
Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rasterizer_,
VAddr cpu_addr_, u64 size_bytes_)
@@ -93,8 +100,12 @@ Buffer::Buffer(BufferCacheRuntime& runtime, VideoCore::RasterizerInterface& rast
VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format) {
if (!device) {
- // Null buffer, return a null descriptor
+ // Null buffer supported, return a null descriptor
return VK_NULL_HANDLE;
+ } else if (is_null) {
+ // Null buffer not supported, adjust offset and size
+ offset = 0;
+ size = 0;
}
const auto it{std::ranges::find_if(views, [offset, size, format](const BufferView& view) {
return offset == view.offset && size == view.size && format == view.format;
@@ -563,22 +574,27 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
}
buffer_handles.push_back(handle);
}
+ const u32 device_max = device.GetMaxVertexInputBindings();
+ const u32 min_binding = std::min(bindings.min_index, device_max);
+ const u32 max_binding = std::min(bindings.max_index, device_max);
+ const u32 binding_count = max_binding - min_binding;
+ if (binding_count == 0) {
+ return;
+ }
if (device.IsExtExtendedDynamicStateSupported()) {
- scheduler.Record([this, bindings_ = std::move(bindings),
- buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
- cmdbuf.BindVertexBuffers2EXT(bindings_.min_index,
- std::min(bindings_.max_index - bindings_.min_index,
- device.GetMaxVertexInputBindings()),
- buffer_handles_.data(), bindings_.offsets.data(),
- bindings_.sizes.data(), bindings_.strides.data());
+ scheduler.Record([bindings_ = std::move(bindings),
+ buffer_handles_ = std::move(buffer_handles),
+ binding_count](vk::CommandBuffer cmdbuf) {
+ cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, binding_count, buffer_handles_.data(),
+ bindings_.offsets.data(), bindings_.sizes.data(),
+ bindings_.strides.data());
});
} else {
- scheduler.Record([this, bindings_ = std::move(bindings),
- buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) {
- cmdbuf.BindVertexBuffers(bindings_.min_index,
- std::min(bindings_.max_index - bindings_.min_index,
- device.GetMaxVertexInputBindings()),
- buffer_handles_.data(), bindings_.offsets.data());
+ scheduler.Record([bindings_ = std::move(bindings),
+ buffer_handles_ = std::move(buffer_handles),
+ binding_count](vk::CommandBuffer cmdbuf) {
+ cmdbuf.BindVertexBuffers(bindings_.min_index, binding_count, buffer_handles_.data(),
+ bindings_.offsets.data());
});
}
}
@@ -622,9 +638,12 @@ void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings<
}
void BufferCacheRuntime::ReserveNullBuffer() {
- if (null_buffer) {
- return;
+ if (!null_buffer) {
+ null_buffer = CreateNullBuffer();
}
+}
+
+vk::Buffer BufferCacheRuntime::CreateNullBuffer() {
VkBufferCreateInfo create_info{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
@@ -639,15 +658,17 @@ void BufferCacheRuntime::ReserveNullBuffer() {
if (device.IsExtTransformFeedbackSupported()) {
create_info.usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
}
- null_buffer = memory_allocator.CreateBuffer(create_info, MemoryUsage::DeviceLocal);
+ vk::Buffer ret = memory_allocator.CreateBuffer(create_info, MemoryUsage::DeviceLocal);
if (device.HasDebuggingToolAttached()) {
- null_buffer.SetObjectNameEXT("Null buffer");
+ ret.SetObjectNameEXT("Null buffer");
}
scheduler.RequestOutsideRenderPassOperationContext();
- scheduler.Record([buffer = *null_buffer](vk::CommandBuffer cmdbuf) {
+ scheduler.Record([buffer = *ret](vk::CommandBuffer cmdbuf) {
cmdbuf.FillBuffer(buffer, 0, VK_WHOLE_SIZE, 0);
});
+
+ return ret;
}
} // 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:
vk::Buffer buffer;
std::vector<BufferView> views;
VideoCommon::UsageTracker tracker;
+ bool is_null{};
};
class QuadArrayIndexBuffer;
@@ -151,6 +152,7 @@ private:
}
void ReserveNullBuffer();
+ vk::Buffer CreateNullBuffer();
const Device& device;
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;
using VideoCommon::GenericEnvironment;
using VideoCommon::GraphicsEnvironment;
-constexpr u32 CACHE_VERSION = 10;
+constexpr u32 CACHE_VERSION = 11;
constexpr std::array<char, 8> VULKAN_CACHE_MAGIC_NUMBER{'y', 'u', 'z', 'u', 'v', 'k', 'c', 'h'};
template <typename Container>
@@ -374,6 +374,7 @@ PipelineCache::PipelineCache(RasterizerVulkan& rasterizer_, const Device& device
.has_broken_robust =
device.IsNvidia() && device.GetNvidiaArch() <= NvidiaArchitecture::Arch_Pascal,
.min_ssbo_alignment = device.GetStorageBufferAlignment(),
+ .max_user_clip_distances = device.GetMaxUserClipDistances(),
};
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:
}
if (has_multi_queries) {
- size_t intermediary_buffer_index = ObtainBuffer<false>(num_slots_used);
+ const size_t min_accumulation_limit =
+ std::min(first_accumulation_checkpoint, num_slots_used);
+ const size_t max_accumulation_limit =
+ std::max(last_accumulation_checkpoint, num_slots_used);
+ const size_t intermediary_buffer_index = ObtainBuffer<false>(num_slots_used);
resolve_buffers.push_back(intermediary_buffer_index);
queries_prefix_scan_pass->Run(*accumulation_buffer, *buffers[intermediary_buffer_index],
*buffers[resolve_buffer_index], num_slots_used,
- std::min(first_accumulation_checkpoint, num_slots_used),
- last_accumulation_checkpoint);
+ min_accumulation_limit, max_accumulation_limit);
} else {
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
}
static Shader::TexturePixelFormat ConvertTexturePixelFormat(const Tegra::Texture::TICEntry& entry) {
- switch (PixelFormatFromTextureInfo(entry.format, entry.r_type, entry.g_type, entry.b_type,
- entry.a_type, entry.srgb_conversion)) {
- case VideoCore::Surface::PixelFormat::A8B8G8R8_SNORM:
- return Shader::TexturePixelFormat::A8B8G8R8_SNORM;
- case VideoCore::Surface::PixelFormat::R8_SNORM:
- return Shader::TexturePixelFormat::R8_SNORM;
- case VideoCore::Surface::PixelFormat::R8G8_SNORM:
- return Shader::TexturePixelFormat::R8G8_SNORM;
- case VideoCore::Surface::PixelFormat::R16G16B16A16_SNORM:
- return Shader::TexturePixelFormat::R16G16B16A16_SNORM;
- case VideoCore::Surface::PixelFormat::R16G16_SNORM:
- return Shader::TexturePixelFormat::R16G16_SNORM;
- case VideoCore::Surface::PixelFormat::R16_SNORM:
- return Shader::TexturePixelFormat::R16_SNORM;
- default:
- return Shader::TexturePixelFormat::OTHER;
- }
+ return static_cast<Shader::TexturePixelFormat>(
+ PixelFormatFromTextureInfo(entry.format, entry.r_type, entry.g_type, entry.b_type,
+ entry.a_type, entry.srgb_conversion));
}
static std::string_view StageToPrefix(Shader::Stage stage) {
@@ -398,6 +384,11 @@ Shader::TexturePixelFormat GraphicsEnvironment::ReadTexturePixelFormat(u32 handl
return result;
}
+bool GraphicsEnvironment::IsTexturePixelFormatInteger(u32 handle) {
+ return VideoCore::Surface::IsPixelFormatInteger(
+ static_cast<VideoCore::Surface::PixelFormat>(ReadTexturePixelFormat(handle)));
+}
+
u32 GraphicsEnvironment::ReadViewportTransformState() {
const auto& regs{maxwell3d->regs};
viewport_transform_state = regs.viewport_scale_offset_enabled;
@@ -448,6 +439,11 @@ Shader::TexturePixelFormat ComputeEnvironment::ReadTexturePixelFormat(u32 handle
return result;
}
+bool ComputeEnvironment::IsTexturePixelFormatInteger(u32 handle) {
+ return VideoCore::Surface::IsPixelFormatInteger(
+ static_cast<VideoCore::Surface::PixelFormat>(ReadTexturePixelFormat(handle)));
+}
+
u32 ComputeEnvironment::ReadViewportTransformState() {
return viewport_transform_state;
}
@@ -551,6 +547,11 @@ Shader::TexturePixelFormat FileEnvironment::ReadTexturePixelFormat(u32 handle) {
return it->second;
}
+bool FileEnvironment::IsTexturePixelFormatInteger(u32 handle) {
+ return VideoCore::Surface::IsPixelFormatInteger(
+ static_cast<VideoCore::Surface::PixelFormat>(ReadTexturePixelFormat(handle)));
+}
+
u32 FileEnvironment::ReadViewportTransformState() {
return viewport_transform_state;
}
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:
Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
+ bool IsTexturePixelFormatInteger(u32 handle) override;
+
u32 ReadViewportTransformState() override;
std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(u32 bank, u32 offset) override;
@@ -139,6 +141,8 @@ public:
Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
+ bool IsTexturePixelFormatInteger(u32 handle) override;
+
u32 ReadViewportTransformState() override;
std::optional<Shader::ReplaceConstant> GetReplaceConstBuffer(
@@ -171,6 +175,8 @@ public:
[[nodiscard]] Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;
+ [[nodiscard]] bool IsTexturePixelFormatInteger(u32 handle) override;
+
[[nodiscard]] u32 ReadViewportTransformState() override;
[[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
std::min(properties.properties.limits.maxVertexInputBindings, 16U);
}
+ if (is_turnip) {
+ LOG_WARNING(Render_Vulkan, "Turnip requires higher-than-reported binding limits");
+ properties.properties.limits.maxVertexInputBindings = 32;
+ }
+
if (!extensions.extended_dynamic_state && extensions.extended_dynamic_state2) {
LOG_INFO(Render_Vulkan,
"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:
return properties.properties.limits.maxViewports;
}
+ u32 GetMaxUserClipDistances() const {
+ return properties.properties.limits.maxClipDistances;
+ }
+
bool SupportsConditionalBarriers() const {
return supports_conditional_barriers;
}
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 {
return "VK_OPERATION_DEFERRED_KHR";
case VkResult::VK_OPERATION_NOT_DEFERRED_KHR:
return "VK_OPERATION_NOT_DEFERRED_KHR";
+ case VkResult::VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR:
+ return "VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR";
case VkResult::VK_PIPELINE_COMPILE_REQUIRED_EXT:
return "VK_PIPELINE_COMPILE_REQUIRED_EXT";
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(
Settings::values.bg_green.GetValue(),
Settings::values.bg_blue.GetValue()));
UpdateAPILayout();
- PopulateVSyncModeSelection(); //< must happen after UpdateAPILayout
+ PopulateVSyncModeSelection(false); //< must happen after UpdateAPILayout
// VSync setting needs to be determined after populating the VSync combobox
- if (Settings::IsConfiguringGlobal()) {
- const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue();
- const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting);
- int index{};
- for (const auto mode : vsync_mode_combobox_enum_map) {
- if (mode == vsync_mode) {
- break;
- }
- index++;
- }
- if (static_cast<unsigned long>(index) < vsync_mode_combobox_enum_map.size()) {
- vsync_mode_combobox->setCurrentIndex(index);
+ const auto vsync_mode_setting = Settings::values.vsync_mode.GetValue();
+ const auto vsync_mode = VSyncSettingToMode(vsync_mode_setting);
+ int index{};
+ for (const auto mode : vsync_mode_combobox_enum_map) {
+ if (mode == vsync_mode) {
+ break;
}
+ index++;
+ }
+ if (static_cast<unsigned long>(index) < vsync_mode_combobox_enum_map.size()) {
+ vsync_mode_combobox->setCurrentIndex(index);
}
connect(api_combobox, qOverload<int>(&QComboBox::activated), this, [this] {
UpdateAPILayout();
- PopulateVSyncModeSelection();
+ PopulateVSyncModeSelection(false);
});
connect(vulkan_device_combobox, qOverload<int>(&QComboBox::activated), this,
[this](int device) {
UpdateDeviceSelection(device);
- PopulateVSyncModeSelection();
+ PopulateVSyncModeSelection(false);
});
connect(shader_backend_combobox, qOverload<int>(&QComboBox::activated), this,
[this](int backend) { UpdateShaderBackendSelection(backend); });
@@ -147,8 +145,9 @@ ConfigureGraphics::ConfigureGraphics(
const auto& update_screenshot_info = [this, &builder]() {
const auto& combobox_enumerations = builder.ComboboxTranslations().at(
Settings::EnumMetadata<Settings::AspectRatio>::Index());
- const auto index = aspect_ratio_combobox->currentIndex();
- const auto ratio = static_cast<Settings::AspectRatio>(combobox_enumerations[index].first);
+ const auto ratio_index = aspect_ratio_combobox->currentIndex();
+ const auto ratio =
+ static_cast<Settings::AspectRatio>(combobox_enumerations[ratio_index].first);
const auto& combobox_enumerations_resolution = builder.ComboboxTranslations().at(
Settings::EnumMetadata<Settings::ResolutionSetup>::Index());
@@ -174,11 +173,7 @@ ConfigureGraphics::ConfigureGraphics(
}
}
-void ConfigureGraphics::PopulateVSyncModeSelection() {
- if (!Settings::IsConfiguringGlobal()) {
- return;
- }
-
+void ConfigureGraphics::PopulateVSyncModeSelection(bool use_setting) {
const Settings::RendererBackend backend{GetCurrentGraphicsBackend()};
if (backend == Settings::RendererBackend::Null) {
vsync_mode_combobox->setEnabled(false);
@@ -189,8 +184,9 @@ void ConfigureGraphics::PopulateVSyncModeSelection() {
const int current_index = //< current selected vsync mode from combobox
vsync_mode_combobox->currentIndex();
const auto current_mode = //< current selected vsync mode as a VkPresentModeKHR
- current_index == -1 ? VSyncSettingToMode(Settings::values.vsync_mode.GetValue())
- : vsync_mode_combobox_enum_map[current_index];
+ current_index == -1 || use_setting
+ ? VSyncSettingToMode(Settings::values.vsync_mode.GetValue())
+ : vsync_mode_combobox_enum_map[current_index];
int index{};
const int device{vulkan_device_combobox->currentIndex()}; //< current selected Vulkan device
@@ -214,6 +210,23 @@ void ConfigureGraphics::PopulateVSyncModeSelection() {
}
index++;
}
+
+ if (!Settings::IsConfiguringGlobal()) {
+ vsync_restore_global_button->setVisible(!Settings::values.vsync_mode.UsingGlobal());
+
+ const Settings::VSyncMode global_vsync_mode = Settings::values.vsync_mode.GetValue(true);
+ vsync_restore_global_button->setEnabled(
+ (backend == Settings::RendererBackend::OpenGL &&
+ (global_vsync_mode == Settings::VSyncMode::Immediate ||
+ global_vsync_mode == Settings::VSyncMode::Fifo)) ||
+ backend == Settings::RendererBackend::Vulkan);
+ }
+}
+
+void ConfigureGraphics::UpdateVsyncSetting() const {
+ const auto mode = vsync_mode_combobox_enum_map[vsync_mode_combobox->currentIndex()];
+ const auto vsync_mode = PresentModeToSetting(mode);
+ Settings::values.vsync_mode.SetValue(vsync_mode);
}
void ConfigureGraphics::UpdateDeviceSelection(int device) {
@@ -299,6 +312,33 @@ void ConfigureGraphics::Setup(const ConfigurationShared::Builder& builder) {
} else if (setting->Id() == Settings::values.vsync_mode.Id()) {
// Keep track of vsync_mode's combobox so we can populate it
vsync_mode_combobox = widget->combobox;
+
+ // Since vsync is populated at runtime, we have to manually set up the button for
+ // restoring the global setting.
+ if (!Settings::IsConfiguringGlobal()) {
+ QPushButton* restore_button =
+ ConfigurationShared::Widget::CreateRestoreGlobalButton(
+ Settings::values.vsync_mode.UsingGlobal(), widget);
+ restore_button->setEnabled(true);
+ widget->layout()->addWidget(restore_button);
+
+ QObject::connect(restore_button, &QAbstractButton::clicked,
+ [restore_button, this](bool) {
+ Settings::values.vsync_mode.SetGlobal(true);
+ PopulateVSyncModeSelection(true);
+
+ restore_button->setVisible(false);
+ });
+
+ std::function<void()> set_non_global = [restore_button, this]() {
+ Settings::values.vsync_mode.SetGlobal(false);
+ UpdateVsyncSetting();
+ restore_button->setVisible(true);
+ };
+ QObject::connect(widget->combobox, QOverload<int>::of(&QComboBox::activated),
+ [set_non_global]() { set_non_global(); });
+ vsync_restore_global_button = restore_button;
+ }
hold_graphics.emplace(setting->Id(), widget);
} else if (setting->Id() == Settings::values.aspect_ratio.Id()) {
// Keep track of the aspect ratio combobox to update other UI tabs that need it
@@ -400,11 +440,7 @@ void ConfigureGraphics::ApplyConfiguration() {
func(powered_on);
}
- if (Settings::IsConfiguringGlobal()) {
- const auto mode = vsync_mode_combobox_enum_map[vsync_mode_combobox->currentIndex()];
- const auto vsync_mode = PresentModeToSetting(mode);
- Settings::values.vsync_mode.SetValue(vsync_mode);
- }
+ UpdateVsyncSetting();
Settings::values.vulkan_device.SetGlobal(true);
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:
void Setup(const ConfigurationShared::Builder& builder);
- void PopulateVSyncModeSelection();
+ void PopulateVSyncModeSelection(bool use_setting);
+ void UpdateVsyncSetting() const;
void UpdateBackgroundColorButton(QColor color);
void UpdateAPILayout();
void UpdateDeviceSelection(int device);
@@ -104,6 +105,7 @@ private:
QComboBox* api_combobox;
QComboBox* shader_backend_combobox;
QComboBox* vsync_mode_combobox;
+ QPushButton* vsync_restore_global_button;
QWidget* vulkan_device_widget;
QWidget* api_widget;
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() {
QString::fromStdString(ReadStringSetting(std::string("recentFiles")))
.split(QStringLiteral(", "), Qt::SkipEmptyParts, Qt::CaseSensitive);
+ ReadCategory(Settings::Category::Paths);
+
EndGroup();
}
@@ -405,6 +407,8 @@ void QtConfig::SaveQtControlValues() {
void QtConfig::SavePathValues() {
BeginGroup(Settings::TranslateCategory(Settings::Category::Paths));
+ WriteCategory(Settings::Category::Paths);
+
WriteSetting(std::string("romsPath"), UISettings::values.roms_path);
BeginArray(std::string("gamedirs"));
for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {