diff options
33 files changed, 392 insertions, 113 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/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 279e5a4e0..4832c03c5 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -162,14 +162,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; } @@ -1294,15 +1298,13 @@ void RasterizerOpenGL::BeginTransformFeedback(GraphicsPipeline* program, GLenum program->ConfigureTransformFeedback(); UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderType::TessellationInit) || - regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation) || - regs.IsShaderConfigEnabled(Maxwell::ShaderType::Geometry)); - UNIMPLEMENTED_IF(primitive_mode != GL_POINTS); + regs.IsShaderConfigEnabled(Maxwell::ShaderType::Tessellation)); // We may have to call BeginTransformFeedbackNV here since they seem to call different // implementations on Nvidia's driver (the pointer is different) but we are using // ARB_transform_feedback3 features with NV_transform_feedback interactions and the ARB // extension doesn't define BeginTransformFeedback (without NV) interactions. It just works. - glBeginTransformFeedback(GL_POINTS); + glBeginTransformFeedback(primitive_mode); } void RasterizerOpenGL::EndTransformFeedback() { 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 5add59e18..3c61799fa 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -574,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()); }); } } 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; |