diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/audio_core/audio_renderer.h | 2 | ||||
-rw-r--r-- | src/core/hle/service/am/am.cpp | 42 | ||||
-rw-r--r-- | src/core/hle/service/am/am.h | 3 | ||||
-rw-r--r-- | src/core/hle/service/hid/hid.cpp | 9 | ||||
-rw-r--r-- | src/core/hle/service/hid/hid.h | 1 | ||||
-rw-r--r-- | src/core/hle/service/time/interface.cpp | 2 | ||||
-rw-r--r-- | src/core/hle/service/time/time.cpp | 29 | ||||
-rw-r--r-- | src/core/hle/service/time/time.h | 1 | ||||
-rw-r--r-- | src/video_core/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/video_core/engines/shader_bytecode.h | 14 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 7 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 20 | ||||
-rw-r--r-- | src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | 31 | ||||
-rw-r--r-- | src/video_core/renderer_vulkan/wrapper.cpp | 342 | ||||
-rw-r--r-- | src/video_core/renderer_vulkan/wrapper.h | 545 | ||||
-rw-r--r-- | src/video_core/shader/decode/memory.cpp | 60 | ||||
-rw-r--r-- | src/video_core/shader/node.h | 16 | ||||
-rw-r--r-- | src/video_core/shader/node_helper.cpp | 14 | ||||
-rw-r--r-- | src/yuzu/main.cpp | 10 |
19 files changed, 1102 insertions, 48 deletions
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index be1b019f1..c0fae669e 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h @@ -189,7 +189,7 @@ struct UpdateDataHeader { UpdateDataHeader() {} explicit UpdateDataHeader(const AudioRendererParameter& config) { - revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision + revision = Common::MakeMagic('R', 'E', 'V', '8'); // 9.2.0 Revision behavior_size = 0xb0; memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10; voices_size = config.voice_count * 0x10; diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index d1bf13c89..557608e76 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -52,6 +52,11 @@ enum class LaunchParameterKind : u32 { AccountPreselectedUser = 2, }; +enum class VrMode : u8 { + Disabled = 0, + Enabled = 1, +}; + constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA; struct LaunchParameterAccountPreselectedUser { @@ -605,11 +610,11 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system, {30, nullptr, "GetHomeButtonReaderLockAccessor"}, {31, nullptr, "GetReaderLockAccessorEx"}, {40, nullptr, "GetCradleFwVersion"}, - {50, nullptr, "IsVrModeEnabled"}, - {51, nullptr, "SetVrModeEnabled"}, + {50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"}, + {51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"}, {52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"}, {53, nullptr, "BeginVrModeEx"}, - {54, nullptr, "EndVrModeEx"}, + {54, &ICommonStateGetter::EndVrModeEx, "EndVrModeEx"}, {55, nullptr, "IsInControllerFirmwareUpdateSection"}, {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"}, @@ -672,6 +677,30 @@ void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) { rb.Push(static_cast<u8>(FocusState::InFocus)); } +void ICommonStateGetter::IsVrModeEnabled(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.PushEnum(VrMode::Disabled); +} + +void ICommonStateGetter::SetVrModeEnabled(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto is_vr_mode_enabled = rp.Pop<bool>(); + + LOG_WARNING(Service_AM, "(STUBBED) called. is_vr_mode_enabled={}", is_vr_mode_enabled); + + IPC::ResponseBuilder rb{ctx, 2}; + if (!is_vr_mode_enabled) { + rb.Push(RESULT_SUCCESS); + } else { + // TODO: Find better error code for this + UNIMPLEMENTED_MSG("is_vr_mode_enabled={}", is_vr_mode_enabled); + rb.Push(RESULT_UNKNOWN); + } +} + void ICommonStateGetter::SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const auto is_lcd_backlight_off_enabled = rp.Pop<bool>(); @@ -683,6 +712,13 @@ void ICommonStateGetter::SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx rb.Push(RESULT_SUCCESS); } +void ICommonStateGetter::EndVrModeEx(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_AM, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_AM, "called"); diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index 0843de781..53cfce10f 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h @@ -182,7 +182,10 @@ private: void GetOperationMode(Kernel::HLERequestContext& ctx); void GetPerformanceMode(Kernel::HLERequestContext& ctx); void GetBootMode(Kernel::HLERequestContext& ctx); + void IsVrModeEnabled(Kernel::HLERequestContext& ctx); + void SetVrModeEnabled(Kernel::HLERequestContext& ctx); void SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx); + void EndVrModeEx(Kernel::HLERequestContext& ctx); void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx); void SetCpuBoostMode(Kernel::HLERequestContext& ctx); diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index e6b56a9f9..d6ed5f304 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -235,7 +235,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) { {303, nullptr, "ActivateSevenSixAxisSensor"}, {304, nullptr, "StartSevenSixAxisSensor"}, {305, nullptr, "StopSevenSixAxisSensor"}, - {306, nullptr, "InitializeSevenSixAxisSensor"}, + {306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"}, {307, nullptr, "FinalizeSevenSixAxisSensor"}, {308, nullptr, "SetSevenSixAxisSensorFusionStrength"}, {309, nullptr, "GetSevenSixAxisSensorFusionStrength"}, @@ -853,6 +853,13 @@ void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) { rb.Push(RESULT_SUCCESS); } +void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_HID, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + class HidDbg final : public ServiceFramework<HidDbg> { public: explicit HidDbg() : ServiceFramework{"hid:dbg"} { diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index ad20f147c..039c38b58 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -128,6 +128,7 @@ private: void StopSixAxisSensor(Kernel::HLERequestContext& ctx); void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx); void SetPalmaBoostMode(Kernel::HLERequestContext& ctx); + void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx); std::shared_ptr<IAppletResource> applet_resource; Core::System& system; diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp index 1660bbdb8..f509653a3 100644 --- a/src/core/hle/service/time/interface.cpp +++ b/src/core/hle/service/time/interface.cpp @@ -30,7 +30,7 @@ Time::Time(std::shared_ptr<Module> module, Core::System& system, const char* nam {400, &Time::GetClockSnapshot, "GetClockSnapshot"}, {401, &Time::GetClockSnapshotFromSystemClockContext, "GetClockSnapshotFromSystemClockContext"}, {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"}, - {501, nullptr, "CalculateSpanBetween"}, + {501, &Time::CalculateSpanBetween, "CalculateSpanBetween"}, }; // clang-format on diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp index 749b7be70..ce859f18d 100644 --- a/src/core/hle/service/time/time.cpp +++ b/src/core/hle/service/time/time.cpp @@ -308,6 +308,35 @@ void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLEReques ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot)); } +void Module::Interface::CalculateSpanBetween(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_Time, "called"); + + IPC::RequestParser rp{ctx}; + const auto snapshot_a = rp.PopRaw<Clock::ClockSnapshot>(); + const auto snapshot_b = rp.PopRaw<Clock::ClockSnapshot>(); + + Clock::TimeSpanType time_span_type{}; + s64 span{}; + if (const ResultCode result{snapshot_a.steady_clock_time_point.GetSpanBetween( + snapshot_b.steady_clock_time_point, span)}; + result != RESULT_SUCCESS) { + if (snapshot_a.network_time && snapshot_b.network_time) { + time_span_type = + Clock::TimeSpanType::FromSeconds(snapshot_b.network_time - snapshot_a.network_time); + } else { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ERROR_TIME_NOT_FOUND); + return; + } + } else { + time_span_type = Clock::TimeSpanType::FromSeconds(span); + } + + IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2}; + rb.Push(RESULT_SUCCESS); + rb.PushRaw(time_span_type.nanoseconds); +} + void Module::Interface::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Time, "called"); IPC::ResponseBuilder rb{ctx, 2, 1}; diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h index aadc2df60..351988468 100644 --- a/src/core/hle/service/time/time.h +++ b/src/core/hle/service/time/time.h @@ -32,6 +32,7 @@ public: void CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx); void GetClockSnapshot(Kernel::HLERequestContext& ctx); void GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx); + void CalculateSpanBetween(Kernel::HLERequestContext& ctx); void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx); private: diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 91df062d7..effe76a63 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -210,6 +210,8 @@ if (ENABLE_VULKAN) renderer_vulkan/vk_texture_cache.h renderer_vulkan/vk_update_descriptor.cpp renderer_vulkan/vk_update_descriptor.h + renderer_vulkan/wrapper.cpp + renderer_vulkan/wrapper.h ) target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 49dc5abe0..930b605af 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h @@ -231,18 +231,6 @@ enum class AtomicOp : u64 { Or = 6, Xor = 7, Exch = 8, -}; - -enum class GlobalAtomicOp : u64 { - Add = 0, - Min = 1, - Max = 2, - Inc = 3, - Dec = 4, - And = 5, - Or = 6, - Xor = 7, - Exch = 8, SafeAdd = 10, }; @@ -1001,7 +989,7 @@ union Instruction { } stg; union { - BitField<52, 4, GlobalAtomicOp> operation; + BitField<52, 4, AtomicOp> operation; BitField<49, 3, GlobalAtomicType> type; BitField<28, 20, s64> offset; } atom; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 826eee7df..31add708f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -444,6 +444,7 @@ void RasterizerOpenGL::Clear() { } SyncRasterizeEnable(); + SyncStencilTestState(); if (regs.clear_flags.scissor) { SyncScissorTest(); @@ -1052,12 +1053,8 @@ void RasterizerOpenGL::SyncStencilTestState() { flags[Dirty::StencilTest] = false; const auto& regs = gpu.regs; - if (!regs.stencil_enable) { - glDisable(GL_STENCIL_TEST); - return; - } + oglEnable(GL_STENCIL_TEST, regs.stencil_enable); - glEnable(GL_STENCIL_TEST); glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func), regs.stencil_front_func_ref, regs.stencil_front_func_mask); glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op_fail), diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 8aa4a7ac9..c7d24cf14 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -2114,6 +2114,10 @@ private: template <const std::string_view& opname, Type type> Expression Atomic(Operation operation) { + if ((opname == Func::Min || opname == Func::Max) && type == Type::Int) { + UNIMPLEMENTED_MSG("Unimplemented Min & Max for atomic operations"); + return {}; + } return {fmt::format("atomic{}({}, {})", opname, Visit(operation[0]).GetCode(), Visit(operation[1]).As(type)), type}; @@ -2307,6 +2311,8 @@ private: ~Func() = delete; static constexpr std::string_view Add = "Add"; + static constexpr std::string_view Min = "Min"; + static constexpr std::string_view Max = "Max"; static constexpr std::string_view And = "And"; static constexpr std::string_view Or = "Or"; static constexpr std::string_view Xor = "Xor"; @@ -2457,7 +2463,21 @@ private: &GLSLDecompiler::AtomicImage<Func::Xor>, &GLSLDecompiler::AtomicImage<Func::Exchange>, + &GLSLDecompiler::Atomic<Func::Exchange, Type::Uint>, &GLSLDecompiler::Atomic<Func::Add, Type::Uint>, + &GLSLDecompiler::Atomic<Func::Min, Type::Uint>, + &GLSLDecompiler::Atomic<Func::Max, Type::Uint>, + &GLSLDecompiler::Atomic<Func::And, Type::Uint>, + &GLSLDecompiler::Atomic<Func::Or, Type::Uint>, + &GLSLDecompiler::Atomic<Func::Xor, Type::Uint>, + + &GLSLDecompiler::Atomic<Func::Exchange, Type::Int>, + &GLSLDecompiler::Atomic<Func::Add, Type::Int>, + &GLSLDecompiler::Atomic<Func::Min, Type::Int>, + &GLSLDecompiler::Atomic<Func::Max, Type::Int>, + &GLSLDecompiler::Atomic<Func::And, Type::Int>, + &GLSLDecompiler::Atomic<Func::Or, Type::Int>, + &GLSLDecompiler::Atomic<Func::Xor, Type::Int>, &GLSLDecompiler::Branch, &GLSLDecompiler::BranchIndirect, diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 51ecb5567..d67f08cf9 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -1941,7 +1941,11 @@ private: return {}; } - Expression AtomicAdd(Operation operation) { + template <Id (Module::*func)(Id, Id, Id, Id, Id), Type result_type, + Type value_type = result_type> + Expression Atomic(Operation operation) { + const Id type_def = GetTypeDefinition(result_type); + Id pointer; if (const auto smem = std::get_if<SmemNode>(&*operation[0])) { pointer = GetSharedMemoryPointer(*smem); @@ -1949,14 +1953,15 @@ private: pointer = GetGlobalMemoryPointer(*gmem); } else { UNREACHABLE(); - return {Constant(t_uint, 0), Type::Uint}; + return {Constant(type_def, 0), result_type}; } + const Id value = As(Visit(operation[1]), value_type); + const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device)); - const Id semantics = Constant(t_uint, 0U); + const Id semantics = Constant(type_def, 0); - const Id value = AsUint(Visit(operation[1])); - return {OpAtomicIAdd(t_uint, pointer, scope, semantics, value), Type::Uint}; + return {(this->*func)(type_def, pointer, scope, semantics, value), result_type}; } Expression Branch(Operation operation) { @@ -2545,7 +2550,21 @@ private: &SPIRVDecompiler::AtomicImageXor, &SPIRVDecompiler::AtomicImageExchange, - &SPIRVDecompiler::AtomicAdd, + &SPIRVDecompiler::Atomic<&Module::OpAtomicExchange, Type::Uint>, + &SPIRVDecompiler::Atomic<&Module::OpAtomicIAdd, Type::Uint>, + &SPIRVDecompiler::Atomic<&Module::OpAtomicUMin, Type::Uint>, + &SPIRVDecompiler::Atomic<&Module::OpAtomicUMax, Type::Uint>, + &SPIRVDecompiler::Atomic<&Module::OpAtomicAnd, Type::Uint>, + &SPIRVDecompiler::Atomic<&Module::OpAtomicOr, Type::Uint>, + &SPIRVDecompiler::Atomic<&Module::OpAtomicXor, Type::Uint>, + + &SPIRVDecompiler::Atomic<&Module::OpAtomicExchange, Type::Int>, + &SPIRVDecompiler::Atomic<&Module::OpAtomicIAdd, Type::Int>, + &SPIRVDecompiler::Atomic<&Module::OpAtomicSMin, Type::Int>, + &SPIRVDecompiler::Atomic<&Module::OpAtomicSMax, Type::Int>, + &SPIRVDecompiler::Atomic<&Module::OpAtomicAnd, Type::Int>, + &SPIRVDecompiler::Atomic<&Module::OpAtomicOr, Type::Int>, + &SPIRVDecompiler::Atomic<&Module::OpAtomicXor, Type::Int>, &SPIRVDecompiler::Branch, &SPIRVDecompiler::BranchIndirect, diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp new file mode 100644 index 000000000..c412b7f20 --- /dev/null +++ b/src/video_core/renderer_vulkan/wrapper.cpp @@ -0,0 +1,342 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <exception> +#include <memory> +#include <optional> +#include <utility> +#include <vector> + +#include "common/common_types.h" + +#include "video_core/renderer_vulkan/wrapper.h" + +namespace Vulkan::vk { + +namespace { + +template <typename T> +bool Proc(T& result, const InstanceDispatch& dld, const char* proc_name, + VkInstance instance = nullptr) noexcept { + result = reinterpret_cast<T>(dld.vkGetInstanceProcAddr(instance, proc_name)); + return result != nullptr; +} + +template <typename T> +void Proc(T& result, const DeviceDispatch& dld, const char* proc_name, VkDevice device) noexcept { + result = reinterpret_cast<T>(dld.vkGetDeviceProcAddr(device, proc_name)); +} + +void Load(VkDevice device, DeviceDispatch& dld) noexcept { +#define X(name) Proc(dld.name, dld, #name, device) + X(vkAcquireNextImageKHR); + X(vkAllocateCommandBuffers); + X(vkAllocateDescriptorSets); + X(vkAllocateMemory); + X(vkBeginCommandBuffer); + X(vkBindBufferMemory); + X(vkBindImageMemory); + X(vkCmdBeginQuery); + X(vkCmdBeginRenderPass); + X(vkCmdBeginTransformFeedbackEXT); + X(vkCmdBindDescriptorSets); + X(vkCmdBindIndexBuffer); + X(vkCmdBindPipeline); + X(vkCmdBindTransformFeedbackBuffersEXT); + X(vkCmdBindVertexBuffers); + X(vkCmdBlitImage); + X(vkCmdClearAttachments); + X(vkCmdCopyBuffer); + X(vkCmdCopyBufferToImage); + X(vkCmdCopyImage); + X(vkCmdCopyImageToBuffer); + X(vkCmdDispatch); + X(vkCmdDraw); + X(vkCmdDrawIndexed); + X(vkCmdEndQuery); + X(vkCmdEndRenderPass); + X(vkCmdEndTransformFeedbackEXT); + X(vkCmdFillBuffer); + X(vkCmdPipelineBarrier); + X(vkCmdPushConstants); + X(vkCmdSetBlendConstants); + X(vkCmdSetCheckpointNV); + X(vkCmdSetDepthBias); + X(vkCmdSetDepthBounds); + X(vkCmdSetScissor); + X(vkCmdSetStencilCompareMask); + X(vkCmdSetStencilReference); + X(vkCmdSetStencilWriteMask); + X(vkCmdSetViewport); + X(vkCreateBuffer); + X(vkCreateBufferView); + X(vkCreateCommandPool); + X(vkCreateComputePipelines); + X(vkCreateDescriptorPool); + X(vkCreateDescriptorSetLayout); + X(vkCreateDescriptorUpdateTemplateKHR); + X(vkCreateFence); + X(vkCreateFramebuffer); + X(vkCreateGraphicsPipelines); + X(vkCreateImage); + X(vkCreateImageView); + X(vkCreatePipelineLayout); + X(vkCreateQueryPool); + X(vkCreateRenderPass); + X(vkCreateSampler); + X(vkCreateSemaphore); + X(vkCreateShaderModule); + X(vkCreateSwapchainKHR); + X(vkDestroyBuffer); + X(vkDestroyBufferView); + X(vkDestroyCommandPool); + X(vkDestroyDescriptorPool); + X(vkDestroyDescriptorSetLayout); + X(vkDestroyDescriptorUpdateTemplateKHR); + X(vkDestroyFence); + X(vkDestroyFramebuffer); + X(vkDestroyImage); + X(vkDestroyImageView); + X(vkDestroyPipeline); + X(vkDestroyPipelineLayout); + X(vkDestroyQueryPool); + X(vkDestroyRenderPass); + X(vkDestroySampler); + X(vkDestroySemaphore); + X(vkDestroyShaderModule); + X(vkDestroySwapchainKHR); + X(vkDeviceWaitIdle); + X(vkEndCommandBuffer); + X(vkFreeCommandBuffers); + X(vkFreeDescriptorSets); + X(vkFreeMemory); + X(vkGetBufferMemoryRequirements); + X(vkGetDeviceQueue); + X(vkGetFenceStatus); + X(vkGetImageMemoryRequirements); + X(vkGetQueryPoolResults); + X(vkGetQueueCheckpointDataNV); + X(vkMapMemory); + X(vkQueueSubmit); + X(vkResetFences); + X(vkResetQueryPoolEXT); + X(vkUnmapMemory); + X(vkUpdateDescriptorSetWithTemplateKHR); + X(vkUpdateDescriptorSets); + X(vkWaitForFences); +#undef X +} + +} // Anonymous namespace + +bool Load(InstanceDispatch& dld) noexcept { +#define X(name) Proc(dld.name, dld, #name) + return X(vkCreateInstance) && X(vkEnumerateInstanceExtensionProperties); +#undef X +} + +bool Load(VkInstance instance, InstanceDispatch& dld) noexcept { +#define X(name) Proc(dld.name, dld, #name, instance) + // These functions may fail to load depending on the enabled extensions. + // Don't return a failure on these. + X(vkCreateDebugUtilsMessengerEXT); + X(vkDestroyDebugUtilsMessengerEXT); + X(vkDestroySurfaceKHR); + X(vkGetPhysicalDeviceFeatures2KHR); + X(vkGetPhysicalDeviceProperties2KHR); + X(vkGetPhysicalDeviceSurfaceCapabilitiesKHR); + X(vkGetPhysicalDeviceSurfaceFormatsKHR); + X(vkGetPhysicalDeviceSurfacePresentModesKHR); + X(vkGetPhysicalDeviceSurfaceSupportKHR); + X(vkGetSwapchainImagesKHR); + X(vkQueuePresentKHR); + + return X(vkCreateDevice) && X(vkDestroyDevice) && X(vkDestroyDevice) && + X(vkEnumerateDeviceExtensionProperties) && X(vkEnumeratePhysicalDevices) && + X(vkGetDeviceProcAddr) && X(vkGetPhysicalDeviceFormatProperties) && + X(vkGetPhysicalDeviceMemoryProperties) && X(vkGetPhysicalDeviceProperties) && + X(vkGetPhysicalDeviceQueueFamilyProperties); +#undef X +} + +const char* Exception::what() const noexcept { + return ToString(result); +} + +const char* ToString(VkResult result) noexcept { + switch (result) { + case VkResult::VK_SUCCESS: + return "VK_SUCCESS"; + case VkResult::VK_NOT_READY: + return "VK_NOT_READY"; + case VkResult::VK_TIMEOUT: + return "VK_TIMEOUT"; + case VkResult::VK_EVENT_SET: + return "VK_EVENT_SET"; + case VkResult::VK_EVENT_RESET: + return "VK_EVENT_RESET"; + case VkResult::VK_INCOMPLETE: + return "VK_INCOMPLETE"; + case VkResult::VK_ERROR_OUT_OF_HOST_MEMORY: + return "VK_ERROR_OUT_OF_HOST_MEMORY"; + case VkResult::VK_ERROR_OUT_OF_DEVICE_MEMORY: + return "VK_ERROR_OUT_OF_DEVICE_MEMORY"; + case VkResult::VK_ERROR_INITIALIZATION_FAILED: + return "VK_ERROR_INITIALIZATION_FAILED"; + case VkResult::VK_ERROR_DEVICE_LOST: + return "VK_ERROR_DEVICE_LOST"; + case VkResult::VK_ERROR_MEMORY_MAP_FAILED: + return "VK_ERROR_MEMORY_MAP_FAILED"; + case VkResult::VK_ERROR_LAYER_NOT_PRESENT: + return "VK_ERROR_LAYER_NOT_PRESENT"; + case VkResult::VK_ERROR_EXTENSION_NOT_PRESENT: + return "VK_ERROR_EXTENSION_NOT_PRESENT"; + case VkResult::VK_ERROR_FEATURE_NOT_PRESENT: + return "VK_ERROR_FEATURE_NOT_PRESENT"; + case VkResult::VK_ERROR_INCOMPATIBLE_DRIVER: + return "VK_ERROR_INCOMPATIBLE_DRIVER"; + case VkResult::VK_ERROR_TOO_MANY_OBJECTS: + return "VK_ERROR_TOO_MANY_OBJECTS"; + case VkResult::VK_ERROR_FORMAT_NOT_SUPPORTED: + return "VK_ERROR_FORMAT_NOT_SUPPORTED"; + case VkResult::VK_ERROR_FRAGMENTED_POOL: + return "VK_ERROR_FRAGMENTED_POOL"; + case VkResult::VK_ERROR_OUT_OF_POOL_MEMORY: + return "VK_ERROR_OUT_OF_POOL_MEMORY"; + case VkResult::VK_ERROR_INVALID_EXTERNAL_HANDLE: + return "VK_ERROR_INVALID_EXTERNAL_HANDLE"; + case VkResult::VK_ERROR_SURFACE_LOST_KHR: + return "VK_ERROR_SURFACE_LOST_KHR"; + case VkResult::VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: + return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR"; + case VkResult::VK_SUBOPTIMAL_KHR: + return "VK_SUBOPTIMAL_KHR"; + case VkResult::VK_ERROR_OUT_OF_DATE_KHR: + return "VK_ERROR_OUT_OF_DATE_KHR"; + case VkResult::VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: + return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR"; + case VkResult::VK_ERROR_VALIDATION_FAILED_EXT: + return "VK_ERROR_VALIDATION_FAILED_EXT"; + case VkResult::VK_ERROR_INVALID_SHADER_NV: + return "VK_ERROR_INVALID_SHADER_NV"; + case VkResult::VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT: + return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT"; + case VkResult::VK_ERROR_FRAGMENTATION_EXT: + return "VK_ERROR_FRAGMENTATION_EXT"; + case VkResult::VK_ERROR_NOT_PERMITTED_EXT: + return "VK_ERROR_NOT_PERMITTED_EXT"; + case VkResult::VK_ERROR_INVALID_DEVICE_ADDRESS_EXT: + return "VK_ERROR_INVALID_DEVICE_ADDRESS_EXT"; + case VkResult::VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT: + return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT"; + } + return "Unknown"; +} + +void Destroy(VkInstance instance, const InstanceDispatch& dld) noexcept { + dld.vkDestroyInstance(instance, nullptr); +} + +void Destroy(VkDevice device, const InstanceDispatch& dld) noexcept { + dld.vkDestroyDevice(device, nullptr); +} + +void Destroy(VkDevice device, VkBuffer handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyBuffer(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkBufferView handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyBufferView(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkCommandPool handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyCommandPool(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkDescriptorPool handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyDescriptorPool(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkDescriptorSetLayout handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyDescriptorSetLayout(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkDescriptorUpdateTemplateKHR handle, + const DeviceDispatch& dld) noexcept { + dld.vkDestroyDescriptorUpdateTemplateKHR(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkDeviceMemory handle, const DeviceDispatch& dld) noexcept { + dld.vkFreeMemory(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkFence handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyFence(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkFramebuffer handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyFramebuffer(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkImage handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyImage(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkImageView handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyImageView(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkPipeline handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyPipeline(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkPipelineLayout handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyPipelineLayout(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkQueryPool handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyQueryPool(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkRenderPass handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyRenderPass(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkSampler handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroySampler(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkSwapchainKHR handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroySwapchainKHR(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkSemaphore handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroySemaphore(device, handle, nullptr); +} + +void Destroy(VkDevice device, VkShaderModule handle, const DeviceDispatch& dld) noexcept { + dld.vkDestroyShaderModule(device, handle, nullptr); +} + +void Destroy(VkInstance instance, VkDebugUtilsMessengerEXT handle, + const InstanceDispatch& dld) noexcept { + dld.vkDestroyDebugUtilsMessengerEXT(instance, handle, nullptr); +} + +void Destroy(VkInstance instance, VkSurfaceKHR handle, const InstanceDispatch& dld) noexcept { + dld.vkDestroySurfaceKHR(instance, handle, nullptr); +} + +VkResult Free(VkDevice device, VkDescriptorPool handle, Span<VkDescriptorSet> sets, + const DeviceDispatch& dld) noexcept { + return dld.vkFreeDescriptorSets(device, handle, sets.size(), sets.data()); +} + +VkResult Free(VkDevice device, VkCommandPool handle, Span<VkCommandBuffer> buffers, + const DeviceDispatch& dld) noexcept { + dld.vkFreeCommandBuffers(device, handle, buffers.size(), buffers.data()); + return VK_SUCCESS; +} + +} // namespace Vulkan::vk diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h new file mode 100644 index 000000000..686c2b9a1 --- /dev/null +++ b/src/video_core/renderer_vulkan/wrapper.h @@ -0,0 +1,545 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <exception> +#include <iterator> +#include <limits> +#include <memory> +#include <optional> +#include <type_traits> +#include <utility> +#include <vector> + +#define VK_NO_PROTOTYPES +#include <vulkan/vulkan.h> + +#include "common/common_types.h" + +namespace Vulkan::vk { + +/** + * Span for Vulkan arrays. + * Based on std::span but optimized for array access instead of iterators. + * Size returns uint32_t instead of size_t to ease interaction with Vulkan functions. + */ +template <typename T> +class Span { +public: + using value_type = T; + using size_type = u32; + using difference_type = std::ptrdiff_t; + using reference = const T&; + using const_reference = const T&; + using pointer = const T*; + using const_pointer = const T*; + using iterator = const T*; + using const_iterator = const T*; + + /// Construct an empty span. + constexpr Span() noexcept = default; + + /// Construct a span from a single element. + constexpr Span(const T& value) noexcept : ptr{&value}, num{1} {} + + /// Construct a span from a range. + template <typename Range> + // requires std::data(const Range&) + // requires std::size(const Range&) + constexpr Span(const Range& range) : ptr{std::data(range)}, num{std::size(range)} {} + + /// Construct a span from a pointer and a size. + /// This is inteded for subranges. + constexpr Span(const T* ptr, std::size_t num) noexcept : ptr{ptr}, num{num} {} + + /// Returns the data pointer by the span. + constexpr const T* data() const noexcept { + return ptr; + } + + /// Returns the number of elements in the span. + /// @note Returns a 32 bits integer because most Vulkan functions expect this type. + constexpr u32 size() const noexcept { + return static_cast<u32>(num); + } + + /// Returns true when the span is empty. + constexpr bool empty() const noexcept { + return num == 0; + } + + /// Returns a reference to the element in the passed index. + /// @pre: index < size() + constexpr const T& operator[](std::size_t index) const noexcept { + return ptr[index]; + } + + /// Returns an iterator to the beginning of the span. + constexpr const T* begin() const noexcept { + return ptr; + } + + /// Returns an iterator to the end of the span. + constexpr const T* end() const noexcept { + return ptr + num; + } + + /// Returns an iterator to the beginning of the span. + constexpr const T* cbegin() const noexcept { + return ptr; + } + + /// Returns an iterator to the end of the span. + constexpr const T* cend() const noexcept { + return ptr + num; + } + +private: + const T* ptr = nullptr; + std::size_t num = 0; +}; + +/// Vulkan exception generated from a VkResult. +class Exception final : public std::exception { +public: + /// Construct the exception with a result. + /// @pre result != VK_SUCCESS + explicit Exception(VkResult result_) : result{result_} {} + virtual ~Exception() = default; + + const char* what() const noexcept override; + +private: + VkResult result; +}; + +/// Converts a VkResult enum into a rodata string +const char* ToString(VkResult) noexcept; + +/// Throws a Vulkan exception if result is not success. +inline void Check(VkResult result) { + if (result != VK_SUCCESS) { + throw Exception(result); + } +} + +/// Throws a Vulkan exception if result is an error. +/// @return result +inline VkResult Filter(VkResult result) { + if (result < 0) { + throw Exception(result); + } + return result; +} + +/// Table holding Vulkan instance function pointers. +struct InstanceDispatch { + PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; + + PFN_vkCreateInstance vkCreateInstance; + PFN_vkDestroyInstance vkDestroyInstance; + PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties; + + PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT; + PFN_vkCreateDevice vkCreateDevice; + PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT; + PFN_vkDestroyDevice vkDestroyDevice; + PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR; + PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties; + PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices; + PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; + PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR; + PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties; + PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties; + PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties; + PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR; + PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties; + PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR; + PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR; + PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR; + PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR; + PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR; + PFN_vkQueuePresentKHR vkQueuePresentKHR; +}; + +/// Table holding Vulkan device function pointers. +struct DeviceDispatch : public InstanceDispatch { + PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR; + PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers; + PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets; + PFN_vkAllocateMemory vkAllocateMemory; + PFN_vkBeginCommandBuffer vkBeginCommandBuffer; + PFN_vkBindBufferMemory vkBindBufferMemory; + PFN_vkBindImageMemory vkBindImageMemory; + PFN_vkCmdBeginQuery vkCmdBeginQuery; + PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass; + PFN_vkCmdBeginTransformFeedbackEXT vkCmdBeginTransformFeedbackEXT; + PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets; + PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer; + PFN_vkCmdBindPipeline vkCmdBindPipeline; + PFN_vkCmdBindTransformFeedbackBuffersEXT vkCmdBindTransformFeedbackBuffersEXT; + PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers; + PFN_vkCmdBlitImage vkCmdBlitImage; + PFN_vkCmdClearAttachments vkCmdClearAttachments; + PFN_vkCmdCopyBuffer vkCmdCopyBuffer; + PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage; + PFN_vkCmdCopyImage vkCmdCopyImage; + PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer; + PFN_vkCmdDispatch vkCmdDispatch; + PFN_vkCmdDraw vkCmdDraw; + PFN_vkCmdDrawIndexed vkCmdDrawIndexed; + PFN_vkCmdEndQuery vkCmdEndQuery; + PFN_vkCmdEndRenderPass vkCmdEndRenderPass; + PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT; + PFN_vkCmdFillBuffer vkCmdFillBuffer; + PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier; + PFN_vkCmdPushConstants vkCmdPushConstants; + PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants; + PFN_vkCmdSetCheckpointNV vkCmdSetCheckpointNV; + PFN_vkCmdSetDepthBias vkCmdSetDepthBias; + PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds; + PFN_vkCmdSetScissor vkCmdSetScissor; + PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask; + PFN_vkCmdSetStencilReference vkCmdSetStencilReference; + PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask; + PFN_vkCmdSetViewport vkCmdSetViewport; + PFN_vkCreateBuffer vkCreateBuffer; + PFN_vkCreateBufferView vkCreateBufferView; + PFN_vkCreateCommandPool vkCreateCommandPool; + PFN_vkCreateComputePipelines vkCreateComputePipelines; + PFN_vkCreateDescriptorPool vkCreateDescriptorPool; + PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout; + PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR; + PFN_vkCreateFence vkCreateFence; + PFN_vkCreateFramebuffer vkCreateFramebuffer; + PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines; + PFN_vkCreateImage vkCreateImage; + PFN_vkCreateImageView vkCreateImageView; + PFN_vkCreatePipelineLayout vkCreatePipelineLayout; + PFN_vkCreateQueryPool vkCreateQueryPool; + PFN_vkCreateRenderPass vkCreateRenderPass; + PFN_vkCreateSampler vkCreateSampler; + PFN_vkCreateSemaphore vkCreateSemaphore; + PFN_vkCreateShaderModule vkCreateShaderModule; + PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR; + PFN_vkDestroyBuffer vkDestroyBuffer; + PFN_vkDestroyBufferView vkDestroyBufferView; + PFN_vkDestroyCommandPool vkDestroyCommandPool; + PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool; + PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout; + PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR; + PFN_vkDestroyFence vkDestroyFence; + PFN_vkDestroyFramebuffer vkDestroyFramebuffer; + PFN_vkDestroyImage vkDestroyImage; + PFN_vkDestroyImageView vkDestroyImageView; + PFN_vkDestroyPipeline vkDestroyPipeline; + PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout; + PFN_vkDestroyQueryPool vkDestroyQueryPool; + PFN_vkDestroyRenderPass vkDestroyRenderPass; + PFN_vkDestroySampler vkDestroySampler; + PFN_vkDestroySemaphore vkDestroySemaphore; + PFN_vkDestroyShaderModule vkDestroyShaderModule; + PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR; + PFN_vkDeviceWaitIdle vkDeviceWaitIdle; + PFN_vkEndCommandBuffer vkEndCommandBuffer; + PFN_vkFreeCommandBuffers vkFreeCommandBuffers; + PFN_vkFreeDescriptorSets vkFreeDescriptorSets; + PFN_vkFreeMemory vkFreeMemory; + PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; + PFN_vkGetDeviceQueue vkGetDeviceQueue; + PFN_vkGetFenceStatus vkGetFenceStatus; + PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; + PFN_vkGetQueryPoolResults vkGetQueryPoolResults; + PFN_vkGetQueueCheckpointDataNV vkGetQueueCheckpointDataNV; + PFN_vkMapMemory vkMapMemory; + PFN_vkQueueSubmit vkQueueSubmit; + PFN_vkResetFences vkResetFences; + PFN_vkResetQueryPoolEXT vkResetQueryPoolEXT; + PFN_vkUnmapMemory vkUnmapMemory; + PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR; + PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets; + PFN_vkWaitForFences vkWaitForFences; +}; + +/// Loads instance agnostic function pointers. +/// @return True on success, false on error. +bool Load(InstanceDispatch&) noexcept; + +/// Loads instance function pointers. +/// @return True on success, false on error. +bool Load(VkInstance, InstanceDispatch&) noexcept; + +void Destroy(VkInstance, const InstanceDispatch&) noexcept; +void Destroy(VkDevice, const InstanceDispatch&) noexcept; + +void Destroy(VkDevice, VkBuffer, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkBufferView, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkCommandPool, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkDescriptorPool, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkDescriptorSetLayout, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkDescriptorUpdateTemplateKHR, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkDeviceMemory, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkFence, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkFramebuffer, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkImageView, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkPipeline, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkPipelineLayout, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkQueryPool, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkRenderPass, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkSampler, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkSwapchainKHR, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkSemaphore, const DeviceDispatch&) noexcept; +void Destroy(VkDevice, VkShaderModule, const DeviceDispatch&) noexcept; +void Destroy(VkInstance, VkDebugUtilsMessengerEXT, const InstanceDispatch&) noexcept; +void Destroy(VkInstance, VkSurfaceKHR, const InstanceDispatch&) noexcept; + +VkResult Free(VkDevice, VkDescriptorPool, Span<VkDescriptorSet>, const DeviceDispatch&) noexcept; +VkResult Free(VkDevice, VkCommandPool, Span<VkCommandBuffer>, const DeviceDispatch&) noexcept; + +template <typename Type, typename OwnerType, typename Dispatch> +class Handle; + +/// Handle with an owning type. +/// Analogue to std::unique_ptr. +template <typename Type, typename OwnerType, typename Dispatch> +class Handle { +public: + /// Construct a handle and hold it's ownership. + explicit Handle(Type handle_, OwnerType owner_, const Dispatch& dld_) noexcept + : handle{handle_}, owner{owner_}, dld{&dld_} {} + + /// Construct an empty handle. + Handle() = default; + + /// Copying Vulkan objects is not supported and will never be. + Handle(const Handle&) = delete; + Handle& operator=(const Handle&) = delete; + + /// Construct a handle transfering the ownership from another handle. + Handle(Handle&& rhs) noexcept + : handle{std::exchange(rhs.handle, nullptr)}, owner{rhs.owner}, dld{rhs.dld} {} + + /// Assign the current handle transfering the ownership from another handle. + /// Destroys any previously held object. + Handle& operator=(Handle&& rhs) noexcept { + Release(); + handle = std::exchange(rhs.handle, nullptr); + owner = rhs.owner; + dld = rhs.dld; + return *this; + } + + /// Destroys the current handle if it existed. + ~Handle() noexcept { + Release(); + } + + /// Destroys any held object. + void reset() noexcept { + Release(); + handle = nullptr; + } + + /// Returns the address of the held object. + /// Intended for Vulkan structures that expect a pointer to an array. + const Type* address() const noexcept { + return std::addressof(handle); + } + + /// Returns the held Vulkan handle. + Type operator*() const noexcept { + return handle; + } + + /// Returns true when there's a held object. + explicit operator bool() const noexcept { + return handle != nullptr; + } + +protected: + Type handle = nullptr; + OwnerType owner = nullptr; + const Dispatch* dld = nullptr; + +private: + /// Destroys the held object if it exists. + void Release() noexcept { + if (handle) { + Destroy(owner, handle, *dld); + } + } +}; + +/// Dummy type used to specify a handle has no owner. +struct NoOwner {}; + +/// Handle without an owning type. +/// Analogue to std::unique_ptr +template <typename Type, typename Dispatch> +class Handle<Type, NoOwner, Dispatch> { +public: + /// Construct a handle and hold it's ownership. + explicit Handle(Type handle_, const Dispatch& dld_) noexcept : handle{handle_}, dld{&dld_} {} + + /// Construct an empty handle. + Handle() noexcept = default; + + /// Copying Vulkan objects is not supported and will never be. + Handle(const Handle&) = delete; + Handle& operator=(const Handle&) = delete; + + /// Construct a handle transfering ownership from another handle. + Handle(Handle&& rhs) noexcept : handle{std::exchange(rhs.handle, nullptr)}, dld{rhs.dld} {} + + /// Assign the current handle transfering the ownership from another handle. + /// Destroys any previously held object. + Handle& operator=(Handle&& rhs) noexcept { + Release(); + handle = std::exchange(rhs.handle, nullptr); + dld = rhs.dld; + return *this; + } + + /// Destroys the current handle if it existed. + ~Handle() noexcept { + Release(); + } + + /// Destroys any held object. + void reset() noexcept { + Release(); + handle = nullptr; + } + + /// Returns the address of the held object. + /// Intended for Vulkan structures that expect a pointer to an array. + const Type* address() const noexcept { + return std::addressof(handle); + } + + /// Returns the held Vulkan handle. + Type operator*() const noexcept { + return handle; + } + + /// Returns true when there's a held object. + operator bool() const noexcept { + return handle != nullptr; + } + +protected: + Type handle = nullptr; + const Dispatch* dld = nullptr; + +private: + /// Destroys the held object if it exists. + void Release() noexcept { + if (handle) { + Destroy(handle, *dld); + } + } +}; + +/// Array of a pool allocation. +/// Analogue to std::vector +template <typename AllocationType, typename PoolType> +class PoolAllocations { +public: + /// Construct an empty allocation. + PoolAllocations() = default; + + /// Construct an allocation. Errors are reported through IsOutOfPoolMemory(). + explicit PoolAllocations(std::unique_ptr<AllocationType[]> allocations, std::size_t num, + VkDevice device, PoolType pool, const DeviceDispatch& dld) noexcept + : allocations{std::move(allocations)}, num{num}, device{device}, pool{pool}, dld{&dld} {} + + /// Copying Vulkan allocations is not supported and will never be. + PoolAllocations(const PoolAllocations&) = delete; + PoolAllocations& operator=(const PoolAllocations&) = delete; + + /// Construct an allocation transfering ownership from another allocation. + PoolAllocations(PoolAllocations&& rhs) noexcept + : allocations{std::move(rhs.allocations)}, num{rhs.num}, device{rhs.device}, pool{rhs.pool}, + dld{rhs.dld} {} + + /// Assign an allocation transfering ownership from another allocation. + /// Releases any previously held allocation. + PoolAllocations& operator=(PoolAllocations&& rhs) noexcept { + Release(); + allocations = std::move(rhs.allocations); + num = rhs.num; + device = rhs.device; + pool = rhs.pool; + dld = rhs.dld; + return *this; + } + + /// Destroys any held allocation. + ~PoolAllocations() { + Release(); + } + + /// Returns the number of allocations. + std::size_t size() const noexcept { + return num; + } + + /// Returns a pointer to the array of allocations. + AllocationType const* data() const noexcept { + return allocations.get(); + } + + /// Returns the allocation in the specified index. + /// @pre index < size() + AllocationType operator[](std::size_t index) const noexcept { + return allocations[index]; + } + + /// True when a pool fails to construct. + bool IsOutOfPoolMemory() const noexcept { + return !device; + } + +private: + /// Destroys the held allocations if they exist. + void Release() noexcept { + if (!allocations) { + return; + } + const Span<AllocationType> span(allocations.get(), num); + const VkResult result = Free(device, pool, span, *dld); + // There's no way to report errors from a destructor. + if (result != VK_SUCCESS) { + std::terminate(); + } + } + + std::unique_ptr<AllocationType[]> allocations; + std::size_t num = 0; + VkDevice device = nullptr; + PoolType pool = nullptr; + const DeviceDispatch* dld = nullptr; +}; + +using BufferView = Handle<VkBufferView, VkDevice, DeviceDispatch>; +using DebugCallback = Handle<VkDebugUtilsMessengerEXT, VkInstance, InstanceDispatch>; +using DescriptorSetLayout = Handle<VkDescriptorSetLayout, VkDevice, DeviceDispatch>; +using DescriptorUpdateTemplateKHR = Handle<VkDescriptorUpdateTemplateKHR, VkDevice, DeviceDispatch>; +using Framebuffer = Handle<VkFramebuffer, VkDevice, DeviceDispatch>; +using ImageView = Handle<VkImageView, VkDevice, DeviceDispatch>; +using Pipeline = Handle<VkPipeline, VkDevice, DeviceDispatch>; +using PipelineLayout = Handle<VkPipelineLayout, VkDevice, DeviceDispatch>; +using QueryPool = Handle<VkQueryPool, VkDevice, DeviceDispatch>; +using RenderPass = Handle<VkRenderPass, VkDevice, DeviceDispatch>; +using Sampler = Handle<VkSampler, VkDevice, DeviceDispatch>; +using Semaphore = Handle<VkSemaphore, VkDevice, DeviceDispatch>; +using ShaderModule = Handle<VkShaderModule, VkDevice, DeviceDispatch>; +using SurfaceKHR = Handle<VkSurfaceKHR, VkInstance, InstanceDispatch>; + +using DescriptorSets = PoolAllocations<VkDescriptorSet, VkDescriptorPool>; +using CommandBuffers = PoolAllocations<VkCommandBuffer, VkCommandPool>; + +} // namespace Vulkan::vk diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp index b5fbc4d58..28a49addd 100644 --- a/src/video_core/shader/decode/memory.cpp +++ b/src/video_core/shader/decode/memory.cpp @@ -19,7 +19,6 @@ namespace VideoCommon::Shader { using Tegra::Shader::AtomicOp; using Tegra::Shader::AtomicType; using Tegra::Shader::Attribute; -using Tegra::Shader::GlobalAtomicOp; using Tegra::Shader::GlobalAtomicType; using Tegra::Shader::Instruction; using Tegra::Shader::OpCode; @@ -28,6 +27,28 @@ using Tegra::Shader::StoreType; namespace { +Node GetAtomOperation(AtomicOp op, bool is_signed, Node memory, Node data) { + const OperationCode operation_code = [op] { + switch (op) { + case AtomicOp::Add: + return OperationCode::AtomicIAdd; + case AtomicOp::Min: + return OperationCode::AtomicIMin; + case AtomicOp::Max: + return OperationCode::AtomicIMax; + case AtomicOp::And: + return OperationCode::AtomicIAnd; + case AtomicOp::Or: + return OperationCode::AtomicIOr; + case AtomicOp::Xor: + return OperationCode::AtomicIXor; + case AtomicOp::Exch: + return OperationCode::AtomicIExchange; + } + }(); + return SignedOperation(operation_code, is_signed, std::move(memory), std::move(data)); +} + bool IsUnaligned(Tegra::Shader::UniformType uniform_type) { return uniform_type == Tegra::Shader::UniformType::UnsignedByte || uniform_type == Tegra::Shader::UniformType::UnsignedShort; @@ -363,10 +384,13 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { break; } case OpCode::Id::ATOM: { - UNIMPLEMENTED_IF_MSG(instr.atom.operation != GlobalAtomicOp::Add, "operation={}", - static_cast<int>(instr.atom.operation.Value())); - UNIMPLEMENTED_IF_MSG(instr.atom.type != GlobalAtomicType::S32, "type={}", - static_cast<int>(instr.atom.type.Value())); + UNIMPLEMENTED_IF_MSG(instr.atom.operation == AtomicOp::Inc || + instr.atom.operation == AtomicOp::Dec || + instr.atom.operation == AtomicOp::SafeAdd, + "operation={}", static_cast<int>(instr.atom.operation.Value())); + UNIMPLEMENTED_IF_MSG(instr.atom.type == GlobalAtomicType::S64 || + instr.atom.type == GlobalAtomicType::U64, + "type={}", static_cast<int>(instr.atom.type.Value())); const auto [real_address, base_address, descriptor] = TrackGlobalMemory(bb, instr, true, true); @@ -375,25 +399,29 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) { break; } + const bool is_signed = + instr.atoms.type == AtomicType::S32 || instr.atoms.type == AtomicType::S64; Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor); - Node value = Operation(OperationCode::AtomicAdd, std::move(gmem), GetRegister(instr.gpr20)); + Node value = GetAtomOperation(static_cast<AtomicOp>(instr.atom.operation), is_signed, gmem, + GetRegister(instr.gpr20)); SetRegister(bb, instr.gpr0, std::move(value)); break; } case OpCode::Id::ATOMS: { - UNIMPLEMENTED_IF_MSG(instr.atoms.operation != AtomicOp::Add, "operation={}", - static_cast<int>(instr.atoms.operation.Value())); - UNIMPLEMENTED_IF_MSG(instr.atoms.type != AtomicType::U32, "type={}", - static_cast<int>(instr.atoms.type.Value())); - + UNIMPLEMENTED_IF_MSG(instr.atoms.operation == AtomicOp::Inc || + instr.atoms.operation == AtomicOp::Dec, + "operation={}", static_cast<int>(instr.atoms.operation.Value())); + UNIMPLEMENTED_IF_MSG(instr.atoms.type == AtomicType::S64 || + instr.atoms.type == AtomicType::U64, + "type={}", static_cast<int>(instr.atoms.type.Value())); + const bool is_signed = + instr.atoms.type == AtomicType::S32 || instr.atoms.type == AtomicType::S64; const s32 offset = instr.atoms.GetImmediateOffset(); Node address = GetRegister(instr.gpr8); address = Operation(OperationCode::IAdd, std::move(address), Immediate(offset)); - - Node memory = GetSharedMemory(std::move(address)); - Node data = GetRegister(instr.gpr20); - - Node value = Operation(OperationCode::AtomicAdd, std::move(memory), std::move(data)); + Node value = + GetAtomOperation(static_cast<AtomicOp>(instr.atoms.operation), is_signed, + GetSharedMemory(std::move(address)), GetRegister(instr.gpr20)); SetRegister(bb, instr.gpr0, std::move(value)); break; } diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h index a1828546e..5fcc9da60 100644 --- a/src/video_core/shader/node.h +++ b/src/video_core/shader/node.h @@ -162,7 +162,21 @@ enum class OperationCode { AtomicImageXor, /// (MetaImage, int[N] coords) -> void AtomicImageExchange, /// (MetaImage, int[N] coords) -> void - AtomicAdd, /// (memory, {u}int) -> {u}int + AtomicUExchange, /// (memory, uint) -> uint + AtomicUAdd, /// (memory, uint) -> uint + AtomicUMin, /// (memory, uint) -> uint + AtomicUMax, /// (memory, uint) -> uint + AtomicUAnd, /// (memory, uint) -> uint + AtomicUOr, /// (memory, uint) -> uint + AtomicUXor, /// (memory, uint) -> uint + + AtomicIExchange, /// (memory, int) -> int + AtomicIAdd, /// (memory, int) -> int + AtomicIMin, /// (memory, int) -> int + AtomicIMax, /// (memory, int) -> int + AtomicIAnd, /// (memory, int) -> int + AtomicIOr, /// (memory, int) -> int + AtomicIXor, /// (memory, int) -> int Branch, /// (uint branch_target) -> void BranchIndirect, /// (uint branch_target) -> void diff --git a/src/video_core/shader/node_helper.cpp b/src/video_core/shader/node_helper.cpp index 76c56abb5..7bf4ff387 100644 --- a/src/video_core/shader/node_helper.cpp +++ b/src/video_core/shader/node_helper.cpp @@ -86,6 +86,20 @@ OperationCode SignedToUnsignedCode(OperationCode operation_code, bool is_signed) return OperationCode::LogicalUNotEqual; case OperationCode::LogicalIGreaterEqual: return OperationCode::LogicalUGreaterEqual; + case OperationCode::AtomicIExchange: + return OperationCode::AtomicUExchange; + case OperationCode::AtomicIAdd: + return OperationCode::AtomicUAdd; + case OperationCode::AtomicIMin: + return OperationCode::AtomicUMin; + case OperationCode::AtomicIMax: + return OperationCode::AtomicUMax; + case OperationCode::AtomicIAnd: + return OperationCode::AtomicUAnd; + case OperationCode::AtomicIOr: + return OperationCode::AtomicUOr; + case OperationCode::AtomicIXor: + return OperationCode::AtomicUXor; case OperationCode::INegate: UNREACHABLE_MSG("Can't negate an unsigned integer"); return {}; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4769a612e..d7e59d0cd 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1034,6 +1034,14 @@ void GMainWindow::BootGame(const QString& filename) { } void GMainWindow::ShutdownGame() { + if (!emulation_running) { + return; + } + + if (ui.action_Fullscreen->isChecked()) { + HideFullscreen(); + } + AllowOSSleep(); discord_rpc->Pause(); @@ -1798,7 +1806,7 @@ void GMainWindow::ToggleWindowMode() { // Render in the main window... render_window->BackupGeometry(); ui.horizontalLayout->addWidget(render_window); - render_window->setFocusPolicy(Qt::ClickFocus); + render_window->setFocusPolicy(Qt::StrongFocus); if (emulation_running) { render_window->setVisible(true); render_window->setFocus(); |