diff options
93 files changed, 3642 insertions, 1100 deletions
| diff --git a/.ci/templates/build-msvc.yml b/.ci/templates/build-msvc.yml index 52cebaee0..adcecf0ec 100644 --- a/.ci/templates/build-msvc.yml +++ b/.ci/templates/build-msvc.yml @@ -4,7 +4,7 @@ parameters:    version: ''  steps: -- script: mkdir build && cd build && cmake -G "Visual Studio 15 2017 Win64" --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd .. +- script: mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd ..    displayName: 'Configure CMake'  - task: MSBuild@1    displayName: 'Build' diff --git a/.ci/yuzu-mainline-step2.yml b/.ci/yuzu-mainline-step2.yml index 5f2dfb3d8..a90041d28 100644 --- a/.ci/yuzu-mainline-step2.yml +++ b/.ci/yuzu-mainline-step2.yml @@ -45,7 +45,7 @@ stages:    - job: build      displayName: 'msvc'      pool: -      vmImage: vs2017-win2016 +      vmImage: windows-2019      steps:      - template: ./templates/sync-source.yml        parameters: diff --git a/.ci/yuzu-patreon-step2.yml b/.ci/yuzu-patreon-step2.yml index 1b36f63e1..26e287257 100644 --- a/.ci/yuzu-patreon-step2.yml +++ b/.ci/yuzu-patreon-step2.yml @@ -22,7 +22,7 @@ stages:    - job: build      displayName: 'windows-msvc'      pool: -      vmImage: vs2017-win2016 +      vmImage: windows-2019      steps:      - template: ./templates/sync-source.yml        parameters: diff --git a/dist/license.md b/dist/license.md index b777ebb20..f1ff35c95 100644 --- a/dist/license.md +++ b/dist/license.md @@ -2,8 +2,8 @@ The icons in this folder and its subfolders have the following licenses:  Icon Name | License | Origin/Author  --- | --- | --- -qt_themes/default/icons/16x16/checked.png | Free for non-commercial use -qt_themes/default/icons/16x16/failed.png | Free for non-commercial use +qt_themes/default/icons/16x16/checked.png | CC BY-ND 3.0 | https://icons8.com +qt_themes/default/icons/16x16/failed.png | CC BY-ND 3.0 | https://icons8.com  qt_themes/default/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com  qt_themes/default/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com  qt_themes/default/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com @@ -11,8 +11,6 @@ qt_themes/default/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com  qt_themes/default/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com  qt_themes/default/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team  qt_themes/default/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com -qt_themes/qdarkstyle/icons/16x16/checked.png | Free for non-commercial use -qt_themes/qdarkstyle/icons/16x16/failed.png | Free for non-commercial use  qt_themes/qdarkstyle/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com  qt_themes/qdarkstyle/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com  qt_themes/qdarkstyle/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com diff --git a/dist/qt_themes/default/icons/16x16/checked.png b/dist/qt_themes/default/icons/16x16/checked.pngBinary files differ index c277e6b40..3e017b715 100644 --- a/dist/qt_themes/default/icons/16x16/checked.png +++ b/dist/qt_themes/default/icons/16x16/checked.png diff --git a/dist/qt_themes/default/icons/16x16/failed.png b/dist/qt_themes/default/icons/16x16/failed.pngBinary files differ index ac10f174a..7c4047dd0 100644 --- a/dist/qt_themes/default/icons/16x16/failed.png +++ b/dist/qt_themes/default/icons/16x16/failed.png diff --git a/license.txt b/license.txt index bf5aec0e6..86e7b3c1b 100644 --- a/license.txt +++ b/license.txt @@ -343,8 +343,8 @@ The icons used in this project have the following licenses:  Icon Name                                   | License       | Origin/Author  ---                                         | ---           | --- -checked.png                                 | Free for non-commercial use -failed.png                                  | Free for non-commercial use +checked.png                                 | CC BY-ND 3.0  | https://icons8.com +failed.png                                  | CC BY-ND 3.0  | https://icons8.com  lock.png                                    | CC BY-ND 3.0  | https://icons8.com  plus_folder.png (Default, Dark)             | CC BY-ND 3.0  | https://icons8.com  bad_folder.png (Default, Dark)              | CC BY-ND 3.0  | https://icons8.com 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/CMakeLists.txt b/src/core/CMakeLists.txt index b31a0328c..66497a386 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -131,8 +131,6 @@ add_library(core STATIC      frontend/framebuffer_layout.cpp      frontend/framebuffer_layout.h      frontend/input.h -    frontend/scope_acquire_context.cpp -    frontend/scope_acquire_context.h      gdbstub/gdbstub.cpp      gdbstub/gdbstub.h      hardware_interrupt_manager.cpp @@ -287,6 +285,18 @@ add_library(core STATIC      hle/service/btm/btm.h      hle/service/caps/caps.cpp      hle/service/caps/caps.h +    hle/service/caps/caps_a.cpp +    hle/service/caps/caps_a.h +    hle/service/caps/caps_c.cpp +    hle/service/caps/caps_c.h +    hle/service/caps/caps_u.cpp +    hle/service/caps/caps_u.h +    hle/service/caps/caps_sc.cpp +    hle/service/caps/caps_sc.h +    hle/service/caps/caps_ss.cpp +    hle/service/caps/caps_ss.h +    hle/service/caps/caps_su.cpp +    hle/service/caps/caps_su.h      hle/service/erpt/erpt.cpp      hle/service/erpt/erpt.h      hle/service/es/es.cpp diff --git a/src/core/core.cpp b/src/core/core.cpp index d1bc9340d..3bd90d79f 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -24,7 +24,6 @@  #include "core/file_sys/sdmc_factory.h"  #include "core/file_sys/vfs_concat.h"  #include "core/file_sys/vfs_real.h" -#include "core/frontend/scope_acquire_context.h"  #include "core/gdbstub/gdbstub.h"  #include "core/hardware_interrupt_manager.h"  #include "core/hle/kernel/client_port.h" @@ -168,13 +167,12 @@ struct System::Impl {          Service::Init(service_manager, system);          GDBStub::DeferStart(); -        renderer = VideoCore::CreateRenderer(emu_window, system); -        if (!renderer->Init()) { +        interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system); +        gpu_core = VideoCore::CreateGPU(emu_window, system); +        if (!gpu_core) {              return ResultStatus::ErrorVideoCore;          } -        interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system); -        gpu_core = VideoCore::CreateGPU(system); -        renderer->Rasterizer().SetupDirtyFlags(); +        gpu_core->Renderer().Rasterizer().SetupDirtyFlags();          is_powered_on = true;          exit_lock = false; @@ -186,8 +184,6 @@ struct System::Impl {      ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,                        const std::string& filepath) { -        Core::Frontend::ScopeAcquireContext acquire_context{emu_window}; -          app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));          if (!app_loader) {              LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); @@ -216,10 +212,6 @@ struct System::Impl {          AddGlueRegistrationForProcess(*app_loader, *main_process);          kernel.MakeCurrentProcess(main_process.get()); -        // Main process has been loaded and been made current. -        // Begin GPU and CPU execution. -        gpu_core->Start(); -          // Initialize cheat engine          if (cheat_engine) {              cheat_engine->Initialize(); @@ -277,7 +269,6 @@ struct System::Impl {          }          // Shutdown emulation session -        renderer.reset();          GDBStub::Shutdown();          Service::Shutdown();          service_manager.reset(); @@ -353,7 +344,6 @@ struct System::Impl {      Service::FileSystem::FileSystemController fs_controller;      /// AppLoader used to load the current executing application      std::unique_ptr<Loader::AppLoader> app_loader; -    std::unique_ptr<VideoCore::RendererBase> renderer;      std::unique_ptr<Tegra::GPU> gpu_core;      std::unique_ptr<Hardware::InterruptManager> interrupt_manager;      Memory::Memory memory; @@ -536,11 +526,11 @@ const Core::Hardware::InterruptManager& System::InterruptManager() const {  }  VideoCore::RendererBase& System::Renderer() { -    return *impl->renderer; +    return impl->gpu_core->Renderer();  }  const VideoCore::RendererBase& System::Renderer() const { -    return *impl->renderer; +    return impl->gpu_core->Renderer();  }  Kernel::KernelCore& System::Kernel() { diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp index c909d1ce4..120032134 100644 --- a/src/core/file_sys/romfs.cpp +++ b/src/core/file_sys/romfs.cpp @@ -5,6 +5,7 @@  #include <memory>  #include "common/common_types.h" +#include "common/string_util.h"  #include "common/swap.h"  #include "core/file_sys/fsmitm_romfsbuild.h"  #include "core/file_sys/romfs.h" @@ -126,7 +127,7 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {          return out->GetSubdirectories().front();      while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) { -        if (out->GetSubdirectories().front()->GetName() == "data" && +        if (Common::ToLower(out->GetSubdirectories().front()->GetName()) == "data" &&              type == RomFSExtractionType::Truncated)              break;          out = out->GetSubdirectories().front(); diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 5eb87fb63..72294d4d8 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -13,19 +13,39 @@  namespace Core::Frontend {  /** - * Represents a graphics context that can be used for background computation or drawing. If the - * graphics backend doesn't require the context, then the implementation of these methods can be - * stubs + * Represents a drawing context that supports graphics operations.   */  class GraphicsContext {  public:      virtual ~GraphicsContext(); +    /// Inform the driver to swap the front/back buffers and present the current image +    virtual void SwapBuffers() {} +      /// Makes the graphics context current for the caller thread -    virtual void MakeCurrent() = 0; +    virtual void MakeCurrent() {}      /// Releases (dunno if this is the "right" word) the context from the caller thread -    virtual void DoneCurrent() = 0; +    virtual void DoneCurrent() {} + +    class Scoped { +    public: +        explicit Scoped(GraphicsContext& context_) : context(context_) { +            context.MakeCurrent(); +        } +        ~Scoped() { +            context.DoneCurrent(); +        } + +    private: +        GraphicsContext& context; +    }; + +    /// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value +    /// ends +    Scoped Acquire() { +        return Scoped{*this}; +    }  };  /** @@ -46,7 +66,7 @@ public:   * - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please   *   re-read the upper points again and think about it if you don't see this.   */ -class EmuWindow : public GraphicsContext { +class EmuWindow {  public:      /// Data structure to store emuwindow configuration      struct WindowConfig { @@ -60,17 +80,9 @@ public:      virtual void PollEvents() = 0;      /** -     * Returns a GraphicsContext that the frontend provides that is shared with the emu window. This -     * context can be used from other threads for background graphics computation. If the frontend -     * is using a graphics backend that doesn't need anything specific to run on a different thread, -     * then it can use a stubbed implemenation for GraphicsContext. -     * -     * If the return value is null, then the core should assume that the frontend cannot provide a -     * Shared Context +     * Returns a GraphicsContext that the frontend provides to be used for rendering.       */ -    virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const { -        return nullptr; -    } +    virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const = 0;      /// Returns if window is shown (not minimized)      virtual bool IsShown() const = 0; diff --git a/src/core/frontend/scope_acquire_context.cpp b/src/core/frontend/scope_acquire_context.cpp deleted file mode 100644 index 878c3157c..000000000 --- a/src/core/frontend/scope_acquire_context.cpp +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/frontend/emu_window.h" -#include "core/frontend/scope_acquire_context.h" - -namespace Core::Frontend { - -ScopeAcquireContext::ScopeAcquireContext(Core::Frontend::GraphicsContext& context) -    : context{context} { -    context.MakeCurrent(); -} -ScopeAcquireContext::~ScopeAcquireContext() { -    context.DoneCurrent(); -} - -} // namespace Core::Frontend diff --git a/src/core/frontend/scope_acquire_context.h b/src/core/frontend/scope_acquire_context.h deleted file mode 100644 index 7a65c0623..000000000 --- a/src/core/frontend/scope_acquire_context.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019 yuzu Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "common/common_types.h" - -namespace Core::Frontend { - -class GraphicsContext; - -/// Helper class to acquire/release window context within a given scope -class ScopeAcquireContext : NonCopyable { -public: -    explicit ScopeAcquireContext(Core::Frontend::GraphicsContext& context); -    ~ScopeAcquireContext(); - -private: -    Core::Frontend::GraphicsContext& context; -}; - -} // namespace Core::Frontend 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/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp index 907f464ab..26c8a7081 100644 --- a/src/core/hle/service/caps/caps.cpp +++ b/src/core/hle/service/caps/caps.cpp @@ -2,168 +2,24 @@  // Licensed under GPLv2 or any later version  // Refer to the license.txt file included. -#include <memory> -  #include "core/hle/service/caps/caps.h" +#include "core/hle/service/caps/caps_a.h" +#include "core/hle/service/caps/caps_c.h" +#include "core/hle/service/caps/caps_sc.h" +#include "core/hle/service/caps/caps_ss.h" +#include "core/hle/service/caps/caps_su.h" +#include "core/hle/service/caps/caps_u.h"  #include "core/hle/service/service.h" -#include "core/hle/service/sm/sm.h"  namespace Service::Capture { -class CAPS_A final : public ServiceFramework<CAPS_A> { -public: -    explicit CAPS_A() : ServiceFramework{"caps:a"} { -        // clang-format off -        static const FunctionInfo functions[] = { -            {0, nullptr, "GetAlbumFileCount"}, -            {1, nullptr, "GetAlbumFileList"}, -            {2, nullptr, "LoadAlbumFile"}, -            {3, nullptr, "DeleteAlbumFile"}, -            {4, nullptr, "StorageCopyAlbumFile"}, -            {5, nullptr, "IsAlbumMounted"}, -            {6, nullptr, "GetAlbumUsage"}, -            {7, nullptr, "GetAlbumFileSize"}, -            {8, nullptr, "LoadAlbumFileThumbnail"}, -            {9, nullptr, "LoadAlbumScreenShotImage"}, -            {10, nullptr, "LoadAlbumScreenShotThumbnailImage"}, -            {11, nullptr, "GetAlbumEntryFromApplicationAlbumEntry"}, -            {12, nullptr, "Unknown12"}, -            {13, nullptr, "Unknown13"}, -            {14, nullptr, "Unknown14"}, -            {15, nullptr, "Unknown15"}, -            {16, nullptr, "Unknown16"}, -            {17, nullptr, "Unknown17"}, -            {18, nullptr, "Unknown18"}, -            {202, nullptr, "SaveEditedScreenShot"}, -            {301, nullptr, "GetLastThumbnail"}, -            {401, nullptr, "GetAutoSavingStorage"}, -            {501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"}, -            {1001, nullptr, "Unknown1001"}, -            {1002, nullptr, "Unknown1002"}, -            {1003, nullptr, "Unknown1003"}, -            {8001, nullptr, "ForceAlbumUnmounted"}, -            {8002, nullptr, "ResetAlbumMountStatus"}, -            {8011, nullptr, "RefreshAlbumCache"}, -            {8012, nullptr, "GetAlbumCache"}, -            {8013, nullptr, "Unknown8013"}, -            {8021, nullptr, "GetAlbumEntryFromApplicationAlbumEntryAruid"}, -            {10011, nullptr, "SetInternalErrorConversionEnabled"}, -            {50000, nullptr, "Unknown50000"}, -            {60002, nullptr, "Unknown60002"}, -        }; -        // clang-format on - -        RegisterHandlers(functions); -    } -}; - -class CAPS_C final : public ServiceFramework<CAPS_C> { -public: -    explicit CAPS_C() : ServiceFramework{"caps:c"} { -        // clang-format off -        static const FunctionInfo functions[] = { -            {33, nullptr, "Unknown33"}, -            {2001, nullptr, "Unknown2001"}, -            {2002, nullptr, "Unknown2002"}, -            {2011, nullptr, "Unknown2011"}, -            {2012, nullptr, "Unknown2012"}, -            {2013, nullptr, "Unknown2013"}, -            {2014, nullptr, "Unknown2014"}, -            {2101, nullptr, "Unknown2101"}, -            {2102, nullptr, "Unknown2102"}, -            {2201, nullptr, "Unknown2201"}, -            {2301, nullptr, "Unknown2301"}, -        }; -        // clang-format on - -        RegisterHandlers(functions); -    } -}; - -class CAPS_SC final : public ServiceFramework<CAPS_SC> { -public: -    explicit CAPS_SC() : ServiceFramework{"caps:sc"} { -        // clang-format off -        static const FunctionInfo functions[] = { -            {1, nullptr, "Unknown1"}, -            {2, nullptr, "Unknown2"}, -            {1001, nullptr, "Unknown3"}, -            {1002, nullptr, "Unknown4"}, -            {1003, nullptr, "Unknown5"}, -            {1011, nullptr, "Unknown6"}, -            {1012, nullptr, "Unknown7"}, -            {1201, nullptr, "Unknown8"}, -            {1202, nullptr, "Unknown9"}, -            {1203, nullptr, "Unknown10"}, -        }; -        // clang-format on - -        RegisterHandlers(functions); -    } -}; - -class CAPS_SS final : public ServiceFramework<CAPS_SS> { -public: -    explicit CAPS_SS() : ServiceFramework{"caps:ss"} { -        // clang-format off -        static const FunctionInfo functions[] = { -            {201, nullptr, "Unknown1"}, -            {202, nullptr, "Unknown2"}, -            {203, nullptr, "Unknown3"}, -            {204, nullptr, "Unknown4"}, -        }; -        // clang-format on - -        RegisterHandlers(functions); -    } -}; - -class CAPS_SU final : public ServiceFramework<CAPS_SU> { -public: -    explicit CAPS_SU() : ServiceFramework{"caps:su"} { -        // clang-format off -        static const FunctionInfo functions[] = { -            {201, nullptr, "SaveScreenShot"}, -            {203, nullptr, "SaveScreenShotEx0"}, -        }; -        // clang-format on - -        RegisterHandlers(functions); -    } -}; - -class CAPS_U final : public ServiceFramework<CAPS_U> { -public: -    explicit CAPS_U() : ServiceFramework{"caps:u"} { -        // clang-format off -        static const FunctionInfo functions[] = { -            {32, nullptr, "SetShimLibraryVersion"}, -            {102, nullptr, "GetAlbumFileListByAruid"}, -            {103, nullptr, "DeleteAlbumFileByAruid"}, -            {104, nullptr, "GetAlbumFileSizeByAruid"}, -            {105, nullptr, "DeleteAlbumFileByAruidForDebug"}, -            {110, nullptr, "LoadAlbumScreenShotImageByAruid"}, -            {120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"}, -            {130, nullptr, "PrecheckToCreateContentsByAruid"}, -            {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"}, -            {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"}, -            {142, nullptr, "GetAlbumFileList3AaeAruid"}, -            {143, nullptr, "GetAlbumFileList4AaeUidAruid"}, -            {60002, nullptr, "OpenAccessorSessionForApplication"}, -        }; -        // clang-format on - -        RegisterHandlers(functions); -    } -}; -  void InstallInterfaces(SM::ServiceManager& sm) {      std::make_shared<CAPS_A>()->InstallAsService(sm);      std::make_shared<CAPS_C>()->InstallAsService(sm); +    std::make_shared<CAPS_U>()->InstallAsService(sm);      std::make_shared<CAPS_SC>()->InstallAsService(sm);      std::make_shared<CAPS_SS>()->InstallAsService(sm);      std::make_shared<CAPS_SU>()->InstallAsService(sm); -    std::make_shared<CAPS_U>()->InstallAsService(sm);  }  } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h index 471185dfa..fc70a4c27 100644 --- a/src/core/hle/service/caps/caps.h +++ b/src/core/hle/service/caps/caps.h @@ -4,12 +4,83 @@  #pragma once +#include "core/hle/service/service.h" +  namespace Service::SM {  class ServiceManager;  }  namespace Service::Capture { +enum AlbumImageOrientation { +    Orientation0 = 0, +    Orientation1 = 1, +    Orientation2 = 2, +    Orientation3 = 3, +}; + +enum AlbumReportOption { +    Disable = 0, +    Enable = 1, +}; + +enum ContentType : u8 { +    Screenshot = 0, +    Movie = 1, +    ExtraMovie = 3, +}; + +enum AlbumStorage : u8 { +    NAND = 0, +    SD = 1, +}; + +struct AlbumFileDateTime { +    u16 year; +    u8 month; +    u8 day; +    u8 hour; +    u8 minute; +    u8 second; +    u8 uid; +}; + +struct AlbumEntry { +    u64 size; +    u64 application_id; +    AlbumFileDateTime datetime; +    AlbumStorage storage; +    ContentType content; +    u8 padding[6]; +}; + +struct AlbumFileEntry { +    u64 size; +    u64 hash; +    AlbumFileDateTime datetime; +    AlbumStorage storage; +    ContentType content; +    u8 padding[5]; +    u8 unknown; +}; + +struct ApplicationAlbumEntry { +    u64 size; +    u64 hash; +    AlbumFileDateTime datetime; +    AlbumStorage storage; +    ContentType content; +    u8 padding[5]; +    u8 unknown; +}; + +struct ApplicationAlbumFileEntry { +    ApplicationAlbumEntry entry; +    AlbumFileDateTime datetime; +    u64 unknown; +}; + +/// Registers all Capture services with the specified service manager.  void InstallInterfaces(SM::ServiceManager& sm);  } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp new file mode 100644 index 000000000..88a3fdc05 --- /dev/null +++ b/src/core/hle/service/caps/caps_a.cpp @@ -0,0 +1,78 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/caps/caps_a.h" + +namespace Service::Capture { + +class IAlbumAccessorSession final : public ServiceFramework<IAlbumAccessorSession> { +public: +    explicit IAlbumAccessorSession() : ServiceFramework{"IAlbumAccessorSession"} { +        // clang-format off +        static const FunctionInfo functions[] = { +            {2001, nullptr, "OpenAlbumMovieReadStream"}, +            {2002, nullptr, "CloseAlbumMovieReadStream"}, +            {2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"}, +            {2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"}, +            {2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"}, +            {2006, nullptr, "GetAlbumMovieReadStreamImageDataSize"}, +            {2007, nullptr, "ReadImageDataFromAlbumMovieReadStream"}, +            {2008, nullptr, "ReadFileAttributeFromAlbumMovieReadStream"}, +        }; +        // clang-format on + +        RegisterHandlers(functions); +    } +}; + +CAPS_A::CAPS_A() : ServiceFramework("caps:a") { +    // clang-format off +    static const FunctionInfo functions[] = { +        {0, nullptr, "GetAlbumFileCount"}, +        {1, nullptr, "GetAlbumFileList"}, +        {2, nullptr, "LoadAlbumFile"}, +        {3, nullptr, "DeleteAlbumFile"}, +        {4, nullptr, "StorageCopyAlbumFile"}, +        {5, nullptr, "IsAlbumMounted"}, +        {6, nullptr, "GetAlbumUsage"}, +        {7, nullptr, "GetAlbumFileSize"}, +        {8, nullptr, "LoadAlbumFileThumbnail"}, +        {9, nullptr, "LoadAlbumScreenShotImage"}, +        {10, nullptr, "LoadAlbumScreenShotThumbnailImage"}, +        {11, nullptr, "GetAlbumEntryFromApplicationAlbumEntry"}, +        {12, nullptr, "LoadAlbumScreenShotImageEx"}, +        {13, nullptr, "LoadAlbumScreenShotThumbnailImageEx"}, +        {14, nullptr, "LoadAlbumScreenShotImageEx0"}, +        {15, nullptr, "GetAlbumUsage3"}, +        {16, nullptr, "GetAlbumMountResult"}, +        {17, nullptr, "GetAlbumUsage16"}, +        {18, nullptr, "Unknown18"}, +        {100, nullptr, "GetAlbumFileCountEx0"}, +        {101, nullptr, "GetAlbumFileListEx0"}, +        {202, nullptr, "SaveEditedScreenShot"}, +        {301, nullptr, "GetLastThumbnail"}, +        {302, nullptr, "GetLastOverlayMovieThumbnail"}, +        {401, nullptr, "GetAutoSavingStorage"}, +        {501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"}, +        {1001, nullptr, "LoadAlbumScreenShotThumbnailImageEx0"}, +        {1002, nullptr, "LoadAlbumScreenShotImageEx1"}, +        {1003, nullptr, "LoadAlbumScreenShotThumbnailImageEx1"}, +        {8001, nullptr, "ForceAlbumUnmounted"}, +        {8002, nullptr, "ResetAlbumMountStatus"}, +        {8011, nullptr, "RefreshAlbumCache"}, +        {8012, nullptr, "GetAlbumCache"}, +        {8013, nullptr, "GetAlbumCacheEx"}, +        {8021, nullptr, "GetAlbumEntryFromApplicationAlbumEntryAruid"}, +        {10011, nullptr, "SetInternalErrorConversionEnabled"}, +        {50000, nullptr, "LoadMakerNoteInfoForDebug"}, +        {60002, nullptr, "OpenAccessorSession"}, +    }; +    // clang-format on + +    RegisterHandlers(functions); +} + +CAPS_A::~CAPS_A() = default; + +} // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h new file mode 100644 index 000000000..8de832491 --- /dev/null +++ b/src/core/hle/service/caps/caps_a.h @@ -0,0 +1,21 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Kernel { +class HLERequestContext; +} + +namespace Service::Capture { + +class CAPS_A final : public ServiceFramework<CAPS_A> { +public: +    explicit CAPS_A(); +    ~CAPS_A() override; +}; + +} // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp new file mode 100644 index 000000000..ea6452ffa --- /dev/null +++ b/src/core/hle/service/caps/caps_c.cpp @@ -0,0 +1,75 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/caps/caps_c.h" + +namespace Service::Capture { + +class IAlbumControlSession final : public ServiceFramework<IAlbumControlSession> { +public: +    explicit IAlbumControlSession() : ServiceFramework{"IAlbumControlSession"} { +        // clang-format off +        static const FunctionInfo functions[] = { +            {2001, nullptr, "OpenAlbumMovieReadStream"}, +            {2002, nullptr, "CloseAlbumMovieReadStream"}, +            {2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"}, +            {2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"}, +            {2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"}, +            {2006, nullptr, "GetAlbumMovieReadStreamImageDataSize"}, +            {2007, nullptr, "ReadImageDataFromAlbumMovieReadStream"}, +            {2008, nullptr, "ReadFileAttributeFromAlbumMovieReadStream"}, +            {2401, nullptr, "OpenAlbumMovieWriteStream"}, +            {2402, nullptr, "FinishAlbumMovieWriteStream"}, +            {2403, nullptr, "CommitAlbumMovieWriteStream"}, +            {2404, nullptr, "DiscardAlbumMovieWriteStream"}, +            {2405, nullptr, "DiscardAlbumMovieWriteStreamNoDelete"}, +            {2406, nullptr, "CommitAlbumMovieWriteStreamEx"}, +            {2411, nullptr, "StartAlbumMovieWriteStreamDataSection"}, +            {2412, nullptr, "EndAlbumMovieWriteStreamDataSection"}, +            {2413, nullptr, "StartAlbumMovieWriteStreamMetaSection"}, +            {2414, nullptr, "EndAlbumMovieWriteStreamMetaSection"}, +            {2421, nullptr, "ReadDataFromAlbumMovieWriteStream"}, +            {2422, nullptr, "WriteDataToAlbumMovieWriteStream"}, +            {2424, nullptr, "WriteMetaToAlbumMovieWriteStream"}, +            {2431, nullptr, "GetAlbumMovieWriteStreamBrokenReason"}, +            {2433, nullptr, "GetAlbumMovieWriteStreamDataSize"}, +            {2434, nullptr, "SetAlbumMovieWriteStreamDataSize"}, +        }; +        // clang-format on + +        RegisterHandlers(functions); +    } +}; + +CAPS_C::CAPS_C() : ServiceFramework("caps:c") { +    // clang-format off +    static const FunctionInfo functions[] = { +        {1, nullptr, "CaptureRawImage"}, +        {2, nullptr, "CaptureRawImageWithTimeout"}, +        {33, nullptr, "Unknown33"}, +        {1001, nullptr, "RequestTakingScreenShot"}, +        {1002, nullptr, "RequestTakingScreenShotWithTimeout"}, +        {1011, nullptr, "NotifyTakingScreenShotRefused"}, +        {2001, nullptr, "NotifyAlbumStorageIsAvailable"}, +        {2002, nullptr, "NotifyAlbumStorageIsUnavailable"}, +        {2011, nullptr, "RegisterAppletResourceUserId"}, +        {2012, nullptr, "UnregisterAppletResourceUserId"}, +        {2013, nullptr, "GetApplicationIdFromAruid"}, +        {2014, nullptr, "CheckApplicationIdRegistered"}, +        {2101, nullptr, "GenerateCurrentAlbumFileId"}, +        {2102, nullptr, "GenerateApplicationAlbumEntry"}, +        {2201, nullptr, "SaveAlbumScreenShotFile"}, +        {2202, nullptr, "SaveAlbumScreenShotFileEx"}, +        {2301, nullptr, "SetOverlayScreenShotThumbnailData"}, +        {2302, nullptr, "SetOverlayMovieThumbnailData"}, +        {60001, nullptr, "OpenControlSession"}, +    }; +    // clang-format on + +    RegisterHandlers(functions); +} + +CAPS_C::~CAPS_C() = default; + +} // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h new file mode 100644 index 000000000..d07cdb441 --- /dev/null +++ b/src/core/hle/service/caps/caps_c.h @@ -0,0 +1,21 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Kernel { +class HLERequestContext; +} + +namespace Service::Capture { + +class CAPS_C final : public ServiceFramework<CAPS_C> { +public: +    explicit CAPS_C(); +    ~CAPS_C() override; +}; + +} // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_sc.cpp b/src/core/hle/service/caps/caps_sc.cpp new file mode 100644 index 000000000..d01a8a58e --- /dev/null +++ b/src/core/hle/service/caps/caps_sc.cpp @@ -0,0 +1,40 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/caps/caps_sc.h" + +namespace Service::Capture { + +CAPS_SC::CAPS_SC() : ServiceFramework("caps:sc") { +    // clang-format off +    static const FunctionInfo functions[] = { +        {1, nullptr, "CaptureRawImage"}, +        {2, nullptr, "CaptureRawImageWithTimeout"}, +        {3, nullptr, "AttachSharedBuffer"}, +        {5, nullptr, "CaptureRawImageToAttachedSharedBuffer"}, +        {210, nullptr, "Unknown210"}, +        {1001, nullptr, "RequestTakingScreenShot"}, +        {1002, nullptr, "RequestTakingScreenShotWithTimeout"}, +        {1003, nullptr, "RequestTakingScreenShotEx"}, +        {1004, nullptr, "RequestTakingScreenShotEx1"}, +        {1009, nullptr, "CancelTakingScreenShot"}, +        {1010, nullptr, "SetTakingScreenShotCancelState"}, +        {1011, nullptr, "NotifyTakingScreenShotRefused"}, +        {1012, nullptr, "NotifyTakingScreenShotFailed"}, +        {1101, nullptr, "SetupOverlayMovieThumbnail"}, +        {1106, nullptr, "Unknown1106"}, +        {1107, nullptr, "Unknown1107"}, +        {1201, nullptr, "OpenRawScreenShotReadStream"}, +        {1202, nullptr, "CloseRawScreenShotReadStream"}, +        {1203, nullptr, "ReadRawScreenShotReadStream"}, +        {1204, nullptr, "Unknown1204"}, +    }; +    // clang-format on + +    RegisterHandlers(functions); +} + +CAPS_SC::~CAPS_SC() = default; + +} // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_sc.h b/src/core/hle/service/caps/caps_sc.h new file mode 100644 index 000000000..9ba372f7a --- /dev/null +++ b/src/core/hle/service/caps/caps_sc.h @@ -0,0 +1,21 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Kernel { +class HLERequestContext; +} + +namespace Service::Capture { + +class CAPS_SC final : public ServiceFramework<CAPS_SC> { +public: +    explicit CAPS_SC(); +    ~CAPS_SC() override; +}; + +} // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_ss.cpp b/src/core/hle/service/caps/caps_ss.cpp new file mode 100644 index 000000000..eaa3a7494 --- /dev/null +++ b/src/core/hle/service/caps/caps_ss.cpp @@ -0,0 +1,26 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/caps/caps_ss.h" + +namespace Service::Capture { + +CAPS_SS::CAPS_SS() : ServiceFramework("caps:ss") { +    // clang-format off +    static const FunctionInfo functions[] = { +        {201, nullptr, "SaveScreenShot"}, +        {202, nullptr, "SaveEditedScreenShot"}, +        {203, nullptr, "SaveScreenShotEx0"}, +        {204, nullptr, "SaveEditedScreenShotEx0"}, +        {206, nullptr, "Unknown206"}, +        {208, nullptr, "SaveScreenShotOfMovieEx1"}, +    }; +    // clang-format on + +    RegisterHandlers(functions); +} + +CAPS_SS::~CAPS_SS() = default; + +} // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_ss.h b/src/core/hle/service/caps/caps_ss.h new file mode 100644 index 000000000..e258a6925 --- /dev/null +++ b/src/core/hle/service/caps/caps_ss.h @@ -0,0 +1,21 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Kernel { +class HLERequestContext; +} + +namespace Service::Capture { + +class CAPS_SS final : public ServiceFramework<CAPS_SS> { +public: +    explicit CAPS_SS(); +    ~CAPS_SS() override; +}; + +} // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp new file mode 100644 index 000000000..2b4c2d808 --- /dev/null +++ b/src/core/hle/service/caps/caps_su.cpp @@ -0,0 +1,22 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/caps/caps_su.h" + +namespace Service::Capture { + +CAPS_SU::CAPS_SU() : ServiceFramework("caps:su") { +    // clang-format off +    static const FunctionInfo functions[] = { +        {201, nullptr, "SaveScreenShot"}, +        {203, nullptr, "SaveScreenShotEx0"}, +    }; +    // clang-format on + +    RegisterHandlers(functions); +} + +CAPS_SU::~CAPS_SU() = default; + +} // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_su.h b/src/core/hle/service/caps/caps_su.h new file mode 100644 index 000000000..cb11f7c9a --- /dev/null +++ b/src/core/hle/service/caps/caps_su.h @@ -0,0 +1,21 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Kernel { +class HLERequestContext; +} + +namespace Service::Capture { + +class CAPS_SU final : public ServiceFramework<CAPS_SU> { +public: +    explicit CAPS_SU(); +    ~CAPS_SU() override; +}; + +} // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp new file mode 100644 index 000000000..78bab6ed8 --- /dev/null +++ b/src/core/hle/service/caps/caps_u.cpp @@ -0,0 +1,76 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/service/caps/caps.h" +#include "core/hle/service/caps/caps_u.h" + +namespace Service::Capture { + +class IAlbumAccessorApplicationSession final +    : public ServiceFramework<IAlbumAccessorApplicationSession> { +public: +    explicit IAlbumAccessorApplicationSession() +        : ServiceFramework{"IAlbumAccessorApplicationSession"} { +        // clang-format off +        static const FunctionInfo functions[] = { +            {2001, nullptr, "OpenAlbumMovieReadStream"}, +            {2002, nullptr, "CloseAlbumMovieReadStream"}, +            {2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"}, +            {2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"}, +            {2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"}, +        }; +        // clang-format on + +        RegisterHandlers(functions); +    } +}; + +CAPS_U::CAPS_U() : ServiceFramework("caps:u") { +    // clang-format off +    static const FunctionInfo functions[] = { +        {31, nullptr, "GetShimLibraryVersion"}, +        {32, nullptr, "SetShimLibraryVersion"}, +        {102, &CAPS_U::GetAlbumContentsFileListForApplication, "GetAlbumContentsFileListForApplication"}, +        {103, nullptr, "DeleteAlbumContentsFileForApplication"}, +        {104, nullptr, "GetAlbumContentsFileSizeForApplication"}, +        {105, nullptr, "DeleteAlbumFileByAruidForDebug"}, +        {110, nullptr, "LoadAlbumContentsFileScreenShotImageForApplication"}, +        {120, nullptr, "LoadAlbumContentsFileThumbnailImageForApplication"}, +        {130, nullptr, "PrecheckToCreateContentsForApplication"}, +        {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"}, +        {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"}, +        {142, nullptr, "GetAlbumFileList3AaeAruid"}, +        {143, nullptr, "GetAlbumFileList4AaeUidAruid"}, +        {60002, nullptr, "OpenAccessorSessionForApplication"}, +    }; +    // clang-format on + +    RegisterHandlers(functions); +} + +CAPS_U::~CAPS_U() = default; + +void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx) { +    // Takes a type-0x6 output buffer containing an array of ApplicationAlbumFileEntry, a PID, an +    // u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total +    // output entries (which is copied to a s32 by official SW). +    IPC::RequestParser rp{ctx}; +    [[maybe_unused]] const auto application_album_file_entries = rp.PopRaw<std::array<u8, 0x30>>(); +    const auto pid = rp.Pop<s32>(); +    const auto content_type = rp.PopRaw<ContentType>(); +    [[maybe_unused]] const auto start_datetime = rp.PopRaw<AlbumFileDateTime>(); +    [[maybe_unused]] const auto end_datetime = rp.PopRaw<AlbumFileDateTime>(); +    const auto applet_resource_user_id = rp.Pop<u64>(); +    LOG_WARNING(Service_Capture, +                "(STUBBED) called. pid={}, content_type={}, applet_resource_user_id={}", pid, +                content_type, applet_resource_user_id); + +    IPC::ResponseBuilder rb{ctx, 3}; +    rb.Push(RESULT_SUCCESS); +    rb.Push<s32>(0); +} + +} // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h new file mode 100644 index 000000000..e6e0716ff --- /dev/null +++ b/src/core/hle/service/caps/caps_u.h @@ -0,0 +1,24 @@ +// Copyright 2020 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Kernel { +class HLERequestContext; +} + +namespace Service::Capture { + +class CAPS_U final : public ServiceFramework<CAPS_U> { +public: +    explicit CAPS_U(); +    ~CAPS_U() override; + +private: +    void GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx); +}; + +} // namespace Service::Capture 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/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 157aeec88..647943020 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -342,17 +342,27 @@ public:              return;          } -        ASSERT( -            vm_manager -                .MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode) -                .IsSuccess()); +        // Mark text and read-only region as ModuleCode +        ASSERT(vm_manager +                   .MirrorMemory(*map_address, nro_address, header.text_size + header.ro_size, +                                 Kernel::MemoryState::ModuleCode) +                   .IsSuccess()); +        // Mark read/write region as ModuleCodeData, which is necessary if this region is used for +        // TransferMemory (e.g. Final Fantasy VIII Remastered does this) +        ASSERT(vm_manager +                   .MirrorMemory(*map_address + header.rw_offset, nro_address + header.rw_offset, +                                 header.rw_size, Kernel::MemoryState::ModuleCodeData) +                   .IsSuccess()); +        // Revoke permissions from the old memory region          ASSERT(vm_manager.ReprotectRange(nro_address, nro_size, Kernel::VMAPermission::None)                     .IsSuccess());          if (bss_size > 0) { +            // Mark BSS region as ModuleCodeData, which is necessary if this region is used for +            // TransferMemory (e.g. Final Fantasy VIII Remastered does this)              ASSERT(vm_manager                         .MirrorMemory(*map_address + nro_size, bss_address, bss_size, -                                     Kernel::MemoryState::ModuleCode) +                                     Kernel::MemoryState::ModuleCodeData)                         .IsSuccess());              ASSERT(vm_manager.ReprotectRange(bss_address, bss_size, Kernel::VMAPermission::None)                         .IsSuccess()); 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/engines/shader_header.h b/src/video_core/engines/shader_header.h index bc80661d8..72e2a33d5 100644 --- a/src/video_core/engines/shader_header.h +++ b/src/video_core/engines/shader_header.h @@ -4,6 +4,9 @@  #pragma once +#include <array> +#include <optional> +  #include "common/bit_field.h"  #include "common/common_funcs.h"  #include "common/common_types.h" @@ -16,7 +19,7 @@ enum class OutputTopology : u32 {      TriangleStrip = 7,  }; -enum class AttributeUse : u8 { +enum class PixelImap : u8 {      Unused = 0,      Constant = 1,      Perspective = 2, @@ -24,7 +27,7 @@ enum class AttributeUse : u8 {  };  // Documentation in: -// http://download.nvidia.com/open-gpu-doc/Shader-Program-Header/1/Shader-Program-Header.html#ImapTexture +// http://download.nvidia.com/open-gpu-doc/Shader-Program-Header/1/Shader-Program-Header.html  struct Header {      union {          BitField<0, 5, u32> sph_type; @@ -59,8 +62,8 @@ struct Header {      union {          BitField<0, 12, u32> max_output_vertices;          BitField<12, 8, u32> store_req_start; // NOTE: not used by geometry shaders. -        BitField<24, 4, u32> reserved; -        BitField<12, 8, u32> store_req_end; // NOTE: not used by geometry shaders. +        BitField<20, 4, u32> reserved; +        BitField<24, 8, u32> store_req_end; // NOTE: not used by geometry shaders.      } common4{};      union { @@ -93,17 +96,20 @@ struct Header {          struct {              INSERT_UNION_PADDING_BYTES(3); // ImapSystemValuesA              INSERT_UNION_PADDING_BYTES(1); // ImapSystemValuesB +              union { -                BitField<0, 2, AttributeUse> x; -                BitField<2, 2, AttributeUse> y; -                BitField<4, 2, AttributeUse> w; -                BitField<6, 2, AttributeUse> z; +                BitField<0, 2, PixelImap> x; +                BitField<2, 2, PixelImap> y; +                BitField<4, 2, PixelImap> z; +                BitField<6, 2, PixelImap> w;                  u8 raw;              } imap_generic_vector[32]; +              INSERT_UNION_PADDING_BYTES(2);  // ImapColor              INSERT_UNION_PADDING_BYTES(2);  // ImapSystemValuesC              INSERT_UNION_PADDING_BYTES(10); // ImapFixedFncTexture[10]              INSERT_UNION_PADDING_BYTES(2);  // ImapReserved +              struct {                  u32 target;                  union { @@ -112,31 +118,30 @@ struct Header {                      BitField<2, 30, u32> reserved;                  };              } omap; +              bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const {                  const u32 bit = render_target * 4 + component;                  return omap.target & (1 << bit);              } -            AttributeUse GetAttributeIndexUse(u32 attribute, u32 index) const { -                return static_cast<AttributeUse>( -                    (imap_generic_vector[attribute].raw >> (index * 2)) & 0x03); -            } -            AttributeUse GetAttributeUse(u32 attribute) const { -                AttributeUse result = AttributeUse::Unused; -                for (u32 i = 0; i < 4; i++) { -                    const auto index = GetAttributeIndexUse(attribute, i); -                    if (index == AttributeUse::Unused) { -                        continue; -                    } -                    if (result == AttributeUse::Unused || result == index) { -                        result = index; + +            PixelImap GetPixelImap(u32 attribute) const { +                const auto get_index = [this, attribute](u32 index) { +                    return static_cast<PixelImap>( +                        (imap_generic_vector[attribute].raw >> (index * 2)) & 3); +                }; + +                std::optional<PixelImap> result; +                for (u32 component = 0; component < 4; ++component) { +                    const PixelImap index = get_index(component); +                    if (index == PixelImap::Unused) {                          continue;                      } -                    LOG_CRITICAL(HW_GPU, "Generic Attribute Conflict in Interpolation Mode"); -                    if (index == AttributeUse::Perspective) { -                        result = index; +                    if (result && result != index) { +                        LOG_CRITICAL(HW_GPU, "Generic attribute conflict in interpolation mode");                      } +                    result = index;                  } -                return result; +                return result.value_or(PixelImap::Unused);              }          } ps; diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index e8f763ce9..8acf2eda2 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -7,6 +7,7 @@  #include "core/core.h"  #include "core/core_timing.h"  #include "core/core_timing_util.h" +#include "core/frontend/emu_window.h"  #include "core/memory.h"  #include "video_core/engines/fermi_2d.h"  #include "video_core/engines/kepler_compute.h" @@ -16,14 +17,15 @@  #include "video_core/gpu.h"  #include "video_core/memory_manager.h"  #include "video_core/renderer_base.h" +#include "video_core/video_core.h"  namespace Tegra {  MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192)); -GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async) -    : system{system}, renderer{renderer}, is_async{is_async} { -    auto& rasterizer{renderer.Rasterizer()}; +GPU::GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_, bool is_async) +    : system{system}, renderer{std::move(renderer_)}, is_async{is_async} { +    auto& rasterizer{renderer->Rasterizer()};      memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer);      dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);      maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager); @@ -137,7 +139,7 @@ u64 GPU::GetTicks() const {  }  void GPU::FlushCommands() { -    renderer.Rasterizer().FlushCommands(); +    renderer->Rasterizer().FlushCommands();  }  // Note that, traditionally, methods are treated as 4-byte addressable locations, and hence diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 64acb17df..ced9d7e28 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -25,8 +25,11 @@ inline u8* FromCacheAddr(CacheAddr cache_addr) {  }  namespace Core { -class System; +namespace Frontend { +class EmuWindow;  } +class System; +} // namespace Core  namespace VideoCore {  class RendererBase; @@ -129,7 +132,8 @@ class MemoryManager;  class GPU {  public: -    explicit GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async); +    explicit GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, +                 bool is_async);      virtual ~GPU(); @@ -174,6 +178,14 @@ public:      /// Returns a reference to the GPU DMA pusher.      Tegra::DmaPusher& DmaPusher(); +    VideoCore::RendererBase& Renderer() { +        return *renderer; +    } + +    const VideoCore::RendererBase& Renderer() const { +        return *renderer; +    } +      // Waits for the GPU to finish working      virtual void WaitIdle() const = 0; @@ -287,7 +299,7 @@ private:  protected:      std::unique_ptr<Tegra::DmaPusher> dma_pusher;      Core::System& system; -    VideoCore::RendererBase& renderer; +    std::unique_ptr<VideoCore::RendererBase> renderer;  private:      std::unique_ptr<Tegra::MemoryManager> memory_manager; diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp index 04222d060..925be8d7b 100644 --- a/src/video_core/gpu_asynch.cpp +++ b/src/video_core/gpu_asynch.cpp @@ -10,13 +10,16 @@  namespace VideoCommon { -GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer) -    : GPU(system, renderer, true), gpu_thread{system} {} +GPUAsynch::GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_, +                     std::unique_ptr<Core::Frontend::GraphicsContext>&& context) +    : GPU(system, std::move(renderer_), true), gpu_thread{system}, gpu_context(std::move(context)), +      cpu_context(renderer->GetRenderWindow().CreateSharedContext()) {}  GPUAsynch::~GPUAsynch() = default;  void GPUAsynch::Start() { -    gpu_thread.StartThread(renderer, *dma_pusher); +    cpu_context->MakeCurrent(); +    gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher);  }  void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) { diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h index 1241ade1d..265c62758 100644 --- a/src/video_core/gpu_asynch.h +++ b/src/video_core/gpu_asynch.h @@ -7,6 +7,10 @@  #include "video_core/gpu.h"  #include "video_core/gpu_thread.h" +namespace Core::Frontend { +class GraphicsContext; +} +  namespace VideoCore {  class RendererBase;  } // namespace VideoCore @@ -16,7 +20,8 @@ namespace VideoCommon {  /// Implementation of GPU interface that runs the GPU asynchronously  class GPUAsynch final : public Tegra::GPU {  public: -    explicit GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer); +    explicit GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, +                       std::unique_ptr<Core::Frontend::GraphicsContext>&& context);      ~GPUAsynch() override;      void Start() override; @@ -32,6 +37,8 @@ protected:  private:      GPUThread::ThreadManager gpu_thread; +    std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context; +    std::unique_ptr<Core::Frontend::GraphicsContext> gpu_context;  };  } // namespace VideoCommon diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp index d48221077..bd5278a5c 100644 --- a/src/video_core/gpu_synch.cpp +++ b/src/video_core/gpu_synch.cpp @@ -7,12 +7,15 @@  namespace VideoCommon { -GPUSynch::GPUSynch(Core::System& system, VideoCore::RendererBase& renderer) -    : GPU(system, renderer, false) {} +GPUSynch::GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, +                   std::unique_ptr<Core::Frontend::GraphicsContext>&& context) +    : GPU(system, std::move(renderer), false), context{std::move(context)} {}  GPUSynch::~GPUSynch() = default; -void GPUSynch::Start() {} +void GPUSynch::Start() { +    context->MakeCurrent(); +}  void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {      dma_pusher->Push(std::move(entries)); @@ -20,19 +23,19 @@ void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {  }  void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { -    renderer.SwapBuffers(framebuffer); +    renderer->SwapBuffers(framebuffer);  }  void GPUSynch::FlushRegion(CacheAddr addr, u64 size) { -    renderer.Rasterizer().FlushRegion(addr, size); +    renderer->Rasterizer().FlushRegion(addr, size);  }  void GPUSynch::InvalidateRegion(CacheAddr addr, u64 size) { -    renderer.Rasterizer().InvalidateRegion(addr, size); +    renderer->Rasterizer().InvalidateRegion(addr, size);  }  void GPUSynch::FlushAndInvalidateRegion(CacheAddr addr, u64 size) { -    renderer.Rasterizer().FlushAndInvalidateRegion(addr, size); +    renderer->Rasterizer().FlushAndInvalidateRegion(addr, size);  }  } // namespace VideoCommon diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h index c71baee89..866a94c8c 100644 --- a/src/video_core/gpu_synch.h +++ b/src/video_core/gpu_synch.h @@ -6,6 +6,10 @@  #include "video_core/gpu.h" +namespace Core::Frontend { +class GraphicsContext; +} +  namespace VideoCore {  class RendererBase;  } // namespace VideoCore @@ -15,7 +19,8 @@ namespace VideoCommon {  /// Implementation of GPU interface that runs the GPU synchronously  class GPUSynch final : public Tegra::GPU {  public: -    explicit GPUSynch(Core::System& system, VideoCore::RendererBase& renderer); +    explicit GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer, +                      std::unique_ptr<Core::Frontend::GraphicsContext>&& context);      ~GPUSynch() override;      void Start() override; @@ -29,6 +34,9 @@ public:  protected:      void TriggerCpuInterrupt([[maybe_unused]] u32 syncpoint_id,                               [[maybe_unused]] u32 value) const override {} + +private: +    std::unique_ptr<Core::Frontend::GraphicsContext> context;  };  } // namespace VideoCommon diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index b1088af3d..270c7ae0d 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -5,7 +5,7 @@  #include "common/assert.h"  #include "common/microprofile.h"  #include "core/core.h" -#include "core/frontend/scope_acquire_context.h" +#include "core/frontend/emu_window.h"  #include "video_core/dma_pusher.h"  #include "video_core/gpu.h"  #include "video_core/gpu_thread.h" @@ -14,8 +14,8 @@  namespace VideoCommon::GPUThread {  /// Runs the GPU thread -static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher, -                      SynchState& state) { +static void RunThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, +                      Tegra::DmaPusher& dma_pusher, SynchState& state) {      MicroProfileOnThreadCreate("GpuThread");      // Wait for first GPU command before acquiring the window context @@ -27,7 +27,7 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p          return;      } -    Core::Frontend::ScopeAcquireContext acquire_context{renderer.GetRenderWindow()}; +    auto current_context = context.Acquire();      CommandDataContainer next;      while (state.is_running) { @@ -62,8 +62,11 @@ ThreadManager::~ThreadManager() {      thread.join();  } -void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) { -    thread = std::thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)}; +void ThreadManager::StartThread(VideoCore::RendererBase& renderer, +                                Core::Frontend::GraphicsContext& context, +                                Tegra::DmaPusher& dma_pusher) { +    thread = std::thread{RunThread, std::ref(renderer), std::ref(context), std::ref(dma_pusher), +                         std::ref(state)};  }  void ThreadManager::SubmitList(Tegra::CommandList&& entries) { diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 882e2d9c7..be36c580e 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -10,7 +10,6 @@  #include <optional>  #include <thread>  #include <variant> -  #include "common/threadsafe_queue.h"  #include "video_core/gpu.h" @@ -20,6 +19,9 @@ class DmaPusher;  } // namespace Tegra  namespace Core { +namespace Frontend { +class GraphicsContext; +}  class System;  } // namespace Core @@ -99,7 +101,8 @@ public:      ~ThreadManager();      /// Creates and starts the GPU thread. -    void StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher); +    void StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context, +                     Tegra::DmaPusher& dma_pusher);      /// Push GPU command entries to be processed      void SubmitList(Tegra::CommandList&& entries); diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 5ec99a126..1d85219b6 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -46,7 +46,8 @@ public:      /// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer      /// specific implementation) -    virtual void TryPresent(int timeout_ms) = 0; +    /// Returns true if a frame was drawn +    virtual bool TryPresent(int timeout_ms) = 0;      // Getter/setter functions:      // ------------------------ diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp index 1a2e2a9f7..c286502ba 100644 --- a/src/video_core/renderer_opengl/gl_device.cpp +++ b/src/video_core/renderer_opengl/gl_device.cpp @@ -131,6 +131,31 @@ std::array<Device::BaseBindings, Tegra::Engines::MaxShaderTypes> BuildBaseBindin      return bindings;  } +bool IsASTCSupported() { +    static constexpr std::array formats = { +        GL_COMPRESSED_RGBA_ASTC_4x4_KHR,           GL_COMPRESSED_RGBA_ASTC_5x4_KHR, +        GL_COMPRESSED_RGBA_ASTC_5x5_KHR,           GL_COMPRESSED_RGBA_ASTC_6x5_KHR, +        GL_COMPRESSED_RGBA_ASTC_6x6_KHR,           GL_COMPRESSED_RGBA_ASTC_8x5_KHR, +        GL_COMPRESSED_RGBA_ASTC_8x6_KHR,           GL_COMPRESSED_RGBA_ASTC_8x8_KHR, +        GL_COMPRESSED_RGBA_ASTC_10x5_KHR,          GL_COMPRESSED_RGBA_ASTC_10x6_KHR, +        GL_COMPRESSED_RGBA_ASTC_10x8_KHR,          GL_COMPRESSED_RGBA_ASTC_10x10_KHR, +        GL_COMPRESSED_RGBA_ASTC_12x10_KHR,         GL_COMPRESSED_RGBA_ASTC_12x12_KHR, +        GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR,   GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR, +        GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR,   GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR, +        GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR,   GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR, +        GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR,   GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, +        GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR,  GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR, +        GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR,  GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, +        GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, +    }; +    return std::find_if_not(formats.begin(), formats.end(), [](GLenum format) { +               GLint supported; +               glGetInternalformativ(GL_TEXTURE_2D, format, GL_INTERNALFORMAT_SUPPORTED, 1, +                                     &supported); +               return supported == GL_TRUE; +           }) == formats.end(); +} +  } // Anonymous namespace  Device::Device() : base_bindings{BuildBaseBindings()} { @@ -152,6 +177,7 @@ Device::Device() : base_bindings{BuildBaseBindings()} {      has_shader_ballot = GLAD_GL_ARB_shader_ballot;      has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array;      has_image_load_formatted = HasExtension(extensions, "GL_EXT_shader_image_load_formatted"); +    has_astc = IsASTCSupported();      has_variable_aoffi = TestVariableAoffi();      has_component_indexing_bug = is_amd;      has_precise_bug = TestPreciseBug(); diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h index d73b099d0..a55050cb5 100644 --- a/src/video_core/renderer_opengl/gl_device.h +++ b/src/video_core/renderer_opengl/gl_device.h @@ -64,6 +64,10 @@ public:          return has_image_load_formatted;      } +    bool HasASTC() const { +        return has_astc; +    } +      bool HasVariableAoffi() const {          return has_variable_aoffi;      } @@ -97,6 +101,7 @@ private:      bool has_shader_ballot{};      bool has_vertex_viewport_layer{};      bool has_image_load_formatted{}; +    bool has_astc{};      bool has_variable_aoffi{};      bool has_component_indexing_bug{};      bool has_precise_bug{}; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 826eee7df..346feeb2f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -386,11 +386,14 @@ void RasterizerOpenGL::ConfigureClearFramebuffer(bool using_color_fb, bool using      texture_cache.GuardRenderTargets(true);      View color_surface;      if (using_color_fb) { -        color_surface = texture_cache.GetColorBufferSurface(regs.clear_buffers.RT, false); +        const std::size_t index = regs.clear_buffers.RT; +        color_surface = texture_cache.GetColorBufferSurface(index, true); +        texture_cache.MarkColorBufferInUse(index);      }      View depth_surface;      if (using_depth_fb || using_stencil_fb) { -        depth_surface = texture_cache.GetDepthBufferSurface(false); +        depth_surface = texture_cache.GetDepthBufferSurface(true); +        texture_cache.MarkDepthBufferInUse();      }      texture_cache.GuardRenderTargets(false); @@ -444,6 +447,7 @@ void RasterizerOpenGL::Clear() {      }      SyncRasterizeEnable(); +    SyncStencilTestState();      if (regs.clear_flags.scissor) {          SyncScissorTest(); @@ -1052,12 +1056,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_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index e3d31c3eb..046ee55a5 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -327,8 +327,7 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,      const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin,                              std::size_t end) { -        context->MakeCurrent(); -        SCOPE_EXIT({ return context->DoneCurrent(); }); +        const auto scope = context->Acquire();          for (std::size_t i = begin; i < end; ++i) {              if (stop_loading) { diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 8aa4a7ac9..160ae4340 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp @@ -31,11 +31,11 @@ namespace {  using Tegra::Engines::ShaderType;  using Tegra::Shader::Attribute; -using Tegra::Shader::AttributeUse;  using Tegra::Shader::Header;  using Tegra::Shader::IpaInterpMode;  using Tegra::Shader::IpaMode;  using Tegra::Shader::IpaSampleMode; +using Tegra::Shader::PixelImap;  using Tegra::Shader::Register;  using VideoCommon::Shader::BuildTransformFeedback;  using VideoCommon::Shader::Registry; @@ -702,20 +702,19 @@ private:          code.AddNewLine();      } -    std::string GetInputFlags(AttributeUse attribute) { +    const char* GetInputFlags(PixelImap attribute) {          switch (attribute) { -        case AttributeUse::Perspective: -            // Default, Smooth -            return {}; -        case AttributeUse::Constant: -            return "flat "; -        case AttributeUse::ScreenLinear: -            return "noperspective "; -        default: -        case AttributeUse::Unused: -            UNIMPLEMENTED_MSG("Unknown attribute usage index={}", static_cast<u32>(attribute)); -            return {}; +        case PixelImap::Perspective: +            return "smooth"; +        case PixelImap::Constant: +            return "flat"; +        case PixelImap::ScreenLinear: +            return "noperspective"; +        case PixelImap::Unused: +            break;          } +        UNIMPLEMENTED_MSG("Unknown attribute usage index={}", static_cast<int>(attribute)); +        return {};      }      void DeclareInputAttributes() { @@ -749,8 +748,8 @@ private:          std::string suffix;          if (stage == ShaderType::Fragment) { -            const auto input_mode{header.ps.GetAttributeUse(location)}; -            if (skip_unused && input_mode == AttributeUse::Unused) { +            const auto input_mode{header.ps.GetPixelImap(location)}; +            if (input_mode == PixelImap::Unused) {                  return;              }              suffix = GetInputFlags(input_mode); @@ -927,7 +926,7 @@ private:                  const u32 address{generic_base + index * generic_stride + element * element_stride};                  const bool declared = stage != ShaderType::Fragment || -                                      header.ps.GetAttributeUse(index) != AttributeUse::Unused; +                                      header.ps.GetPixelImap(index) != PixelImap::Unused;                  const std::string value =                      declared ? ReadAttribute(attribute, element).AsFloat() : "0.0f";                  code.AddLine("case 0x{:X}U: return {};", address, value); @@ -1142,8 +1141,7 @@ private:                                      GetSwizzle(element)),                          Type::Float};              case ShaderType::Fragment: -                return {element == 3 ? "1.0f" : ("gl_FragCoord"s + GetSwizzle(element)), -                        Type::Float}; +                return {"gl_FragCoord"s + GetSwizzle(element), Type::Float};              default:                  UNREACHABLE();              } @@ -2114,6 +2112,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 +2309,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 +2461,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_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index f424e3000..36590a6d0 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -24,7 +24,6 @@ using Tegra::Texture::SwizzleSource;  using VideoCore::MortonSwizzleMode;  using VideoCore::Surface::PixelFormat; -using VideoCore::Surface::SurfaceCompression;  using VideoCore::Surface::SurfaceTarget;  using VideoCore::Surface::SurfaceType; @@ -37,102 +36,100 @@ namespace {  struct FormatTuple {      GLint internal_format; -    GLenum format; -    GLenum type; -    bool compressed; +    GLenum format = GL_NONE; +    GLenum type = GL_NONE;  };  constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format_tuples = {{ -    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, false},                        // ABGR8U -    {GL_RGBA8_SNORM, GL_RGBA, GL_BYTE, false},                                      // ABGR8S -    {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, false},                         // ABGR8UI -    {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, false},                        // B5G6R5U -    {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, false},                  // A2B10G10R10U -    {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, false},                    // A1B5G5R5U -    {GL_R8, GL_RED, GL_UNSIGNED_BYTE, false},                                       // R8U -    {GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, false},                             // R8UI -    {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false},                                    // RGBA16F -    {GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, false},                                 // RGBA16U -    {GL_RGBA16_SNORM, GL_RGBA, GL_SHORT, false},                                    // RGBA16S -    {GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, false},                       // RGBA16UI -    {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, false},            // R11FG11FB10F -    {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, false},                         // RGBA32UI -    {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true},     // DXT1 -    {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true},     // DXT23 -    {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true},     // DXT45 -    {GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, true},               // DXN1 -    {GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, true},                 // DXN2UNORM -    {GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_INT, true},                           // DXN2SNORM -    {GL_COMPRESSED_RGBA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true},        // BC7U -    {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true}, // BC6H_UF16 -    {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true},   // BC6H_SF16 -    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false},                                   // ASTC_2D_4X4 -    {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE, false},                                   // BGRA8 -    {GL_RGBA32F, GL_RGBA, GL_FLOAT, false},                                         // RGBA32F -    {GL_RG32F, GL_RG, GL_FLOAT, false},                                             // RG32F -    {GL_R32F, GL_RED, GL_FLOAT, false},                                             // R32F -    {GL_R16F, GL_RED, GL_HALF_FLOAT, false},                                        // R16F -    {GL_R16, GL_RED, GL_UNSIGNED_SHORT, false},                                     // R16U -    {GL_R16_SNORM, GL_RED, GL_SHORT, false},                                        // R16S -    {GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT, false},                           // R16UI -    {GL_R16I, GL_RED_INTEGER, GL_SHORT, false},                                     // R16I -    {GL_RG16, GL_RG, GL_UNSIGNED_SHORT, false},                                     // RG16 -    {GL_RG16F, GL_RG, GL_HALF_FLOAT, false},                                        // RG16F -    {GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT, false},                           // RG16UI -    {GL_RG16I, GL_RG_INTEGER, GL_SHORT, false},                                     // RG16I -    {GL_RG16_SNORM, GL_RG, GL_SHORT, false},                                        // RG16S -    {GL_RGB32F, GL_RGB, GL_FLOAT, false},                                           // RGB32F -    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, false},                 // RGBA8_SRGB -    {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, false},                                       // RG8U -    {GL_RG8_SNORM, GL_RG, GL_BYTE, false},                                          // RG8S -    {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, false},                             // RG32UI -    {GL_RGB16F, GL_RGBA, GL_HALF_FLOAT, false},                                     // RGBX16F -    {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, false},                             // R32UI -    {GL_R32I, GL_RED_INTEGER, GL_INT, false},                                       // R32I -    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false},                                   // ASTC_2D_8X8 -    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false},                                   // ASTC_2D_8X5 -    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false},                                   // ASTC_2D_5X4 -    {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE, false},                            // BGRA8 +    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV},             // ABGR8U +    {GL_RGBA8_SNORM, GL_RGBA, GL_BYTE},                           // ABGR8S +    {GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE},              // ABGR8UI +    {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV},             // B5G6R5U +    {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV},       // A2B10G10R10U +    {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV},         // A1B5G5R5U +    {GL_R8, GL_RED, GL_UNSIGNED_BYTE},                            // R8U +    {GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE},                  // R8UI +    {GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT},                         // RGBA16F +    {GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT},                      // RGBA16U +    {GL_RGBA16_SNORM, GL_RGBA, GL_SHORT},                         // RGBA16S +    {GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT},            // RGBA16UI +    {GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV}, // R11FG11FB10F +    {GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT},              // RGBA32UI +    {GL_COMPRESSED_RGBA_S3TC_DXT1_EXT},                           // DXT1 +    {GL_COMPRESSED_RGBA_S3TC_DXT3_EXT},                           // DXT23 +    {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT},                           // DXT45 +    {GL_COMPRESSED_RED_RGTC1},                                    // DXN1 +    {GL_COMPRESSED_RG_RGTC2},                                     // DXN2UNORM +    {GL_COMPRESSED_SIGNED_RG_RGTC2},                              // DXN2SNORM +    {GL_COMPRESSED_RGBA_BPTC_UNORM},                              // BC7U +    {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT},                      // BC6H_UF16 +    {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT},                        // BC6H_SF16 +    {GL_COMPRESSED_RGBA_ASTC_4x4_KHR},                            // ASTC_2D_4X4 +    {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE},                        // BGRA8 +    {GL_RGBA32F, GL_RGBA, GL_FLOAT},                              // RGBA32F +    {GL_RG32F, GL_RG, GL_FLOAT},                                  // RG32F +    {GL_R32F, GL_RED, GL_FLOAT},                                  // R32F +    {GL_R16F, GL_RED, GL_HALF_FLOAT},                             // R16F +    {GL_R16, GL_RED, GL_UNSIGNED_SHORT},                          // R16U +    {GL_R16_SNORM, GL_RED, GL_SHORT},                             // R16S +    {GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT},                // R16UI +    {GL_R16I, GL_RED_INTEGER, GL_SHORT},                          // R16I +    {GL_RG16, GL_RG, GL_UNSIGNED_SHORT},                          // RG16 +    {GL_RG16F, GL_RG, GL_HALF_FLOAT},                             // RG16F +    {GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT},                // RG16UI +    {GL_RG16I, GL_RG_INTEGER, GL_SHORT},                          // RG16I +    {GL_RG16_SNORM, GL_RG, GL_SHORT},                             // RG16S +    {GL_RGB32F, GL_RGB, GL_FLOAT},                                // RGB32F +    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV},      // RGBA8_SRGB +    {GL_RG8, GL_RG, GL_UNSIGNED_BYTE},                            // RG8U +    {GL_RG8_SNORM, GL_RG, GL_BYTE},                               // RG8S +    {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT},                  // RG32UI +    {GL_RGB16F, GL_RGBA, GL_HALF_FLOAT},                          // RGBX16F +    {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT},                  // R32UI +    {GL_R32I, GL_RED_INTEGER, GL_INT},                            // R32I +    {GL_COMPRESSED_RGBA_ASTC_8x8_KHR},                            // ASTC_2D_8X8 +    {GL_COMPRESSED_RGBA_ASTC_8x5_KHR},                            // ASTC_2D_8X5 +    {GL_COMPRESSED_RGBA_ASTC_5x4_KHR},                            // ASTC_2D_5X4 +    {GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE},                 // BGRA8      // Compressed sRGB formats -    {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT1_SRGB -    {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT23_SRGB -    {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT45_SRGB -    {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true},    // BC7U_SRGB -    {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV, false},                        // R4G4B4A4U -    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false},      // ASTC_2D_4X4_SRGB -    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false},      // ASTC_2D_8X8_SRGB -    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false},      // ASTC_2D_8X5_SRGB -    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false},      // ASTC_2D_5X4_SRGB -    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false},             // ASTC_2D_5X5 -    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false},      // ASTC_2D_5X5_SRGB -    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false},             // ASTC_2D_10X8 -    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false},      // ASTC_2D_10X8_SRGB -    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false},             // ASTC_2D_6X6 -    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false},      // ASTC_2D_6X6_SRGB -    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false},             // ASTC_2D_10X10 -    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false},      // ASTC_2D_10X10_SRGB -    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false},             // ASTC_2D_12X12 -    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false},      // ASTC_2D_12X12_SRGB -    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false},             // ASTC_2D_8X6 -    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false},      // ASTC_2D_8X6_SRGB -    {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false},             // ASTC_2D_6X5 -    {GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false},      // ASTC_2D_6X5_SRGB -    {GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV, false}, // E5B9G9R9F +    {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT},           // DXT1_SRGB +    {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT},           // DXT23_SRGB +    {GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT},           // DXT45_SRGB +    {GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM},              // BC7U_SRGB +    {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // R4G4B4A4U +    {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR},          // ASTC_2D_4X4_SRGB +    {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR},          // ASTC_2D_8X8_SRGB +    {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR},          // ASTC_2D_8X5_SRGB +    {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR},          // ASTC_2D_5X4_SRGB +    {GL_COMPRESSED_RGBA_ASTC_5x5_KHR},                  // ASTC_2D_5X5 +    {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR},          // ASTC_2D_5X5_SRGB +    {GL_COMPRESSED_RGBA_ASTC_10x8_KHR},                 // ASTC_2D_10X8 +    {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR},         // ASTC_2D_10X8_SRGB +    {GL_COMPRESSED_RGBA_ASTC_6x6_KHR},                  // ASTC_2D_6X6 +    {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR},          // ASTC_2D_6X6_SRGB +    {GL_COMPRESSED_RGBA_ASTC_10x10_KHR},                // ASTC_2D_10X10 +    {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR},        // ASTC_2D_10X10_SRGB +    {GL_COMPRESSED_RGBA_ASTC_12x12_KHR},                // ASTC_2D_12X12 +    {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR},        // ASTC_2D_12X12_SRGB +    {GL_COMPRESSED_RGBA_ASTC_8x6_KHR},                  // ASTC_2D_8X6 +    {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR},          // ASTC_2D_8X6_SRGB +    {GL_COMPRESSED_RGBA_ASTC_6x5_KHR},                  // ASTC_2D_6X5 +    {GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR},          // ASTC_2D_6X5_SRGB +    {GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV},  // E5B9G9R9F      // Depth formats -    {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, false},         // Z32F -    {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, false}, // Z16 +    {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT},         // Z32F +    {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // Z16      // DepthStencil formats -    {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, false},               // Z24S8 -    {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, false},               // S8Z24 -    {GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, false}, // Z32FS8 +    {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8},               // Z24S8 +    {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8},               // S8Z24 +    {GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV}, // Z32FS8  }};  const FormatTuple& GetFormatTuple(PixelFormat pixel_format) {      ASSERT(static_cast<std::size_t>(pixel_format) < tex_format_tuples.size()); -    const auto& format{tex_format_tuples[static_cast<std::size_t>(pixel_format)]}; -    return format; +    return tex_format_tuples[static_cast<std::size_t>(pixel_format)];  }  GLenum GetTextureTarget(const SurfaceTarget& target) { @@ -242,13 +239,20 @@ OGLTexture CreateTexture(const SurfaceParams& params, GLenum target, GLenum inte  } // Anonymous namespace -CachedSurface::CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& params) -    : VideoCommon::SurfaceBase<View>(gpu_addr, params) { -    const auto& tuple{GetFormatTuple(params.pixel_format)}; -    internal_format = tuple.internal_format; -    format = tuple.format; -    type = tuple.type; -    is_compressed = tuple.compressed; +CachedSurface::CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& params, +                             bool is_astc_supported) +    : VideoCommon::SurfaceBase<View>(gpu_addr, params, is_astc_supported) { +    if (is_converted) { +        internal_format = params.srgb_conversion ? GL_SRGB8_ALPHA8 : GL_RGBA8; +        format = GL_RGBA; +        type = GL_UNSIGNED_BYTE; +    } else { +        const auto& tuple{GetFormatTuple(params.pixel_format)}; +        internal_format = tuple.internal_format; +        format = tuple.format; +        type = tuple.type; +        is_compressed = params.IsCompressed(); +    }      target = GetTextureTarget(params.target);      texture = CreateTexture(params, target, internal_format, texture_buffer);      DecorateSurfaceName(); @@ -264,7 +268,7 @@ void CachedSurface::DownloadTexture(std::vector<u8>& staging_buffer) {      if (params.IsBuffer()) {          glGetNamedBufferSubData(texture_buffer.handle, 0, -                                static_cast<GLsizeiptr>(params.GetHostSizeInBytes()), +                                static_cast<GLsizeiptr>(params.GetHostSizeInBytes(false)),                                  staging_buffer.data());          return;      } @@ -272,9 +276,10 @@ void CachedSurface::DownloadTexture(std::vector<u8>& staging_buffer) {      SCOPE_EXIT({ glPixelStorei(GL_PACK_ROW_LENGTH, 0); });      for (u32 level = 0; level < params.emulated_levels; ++level) { -        glPixelStorei(GL_PACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level))); +        glPixelStorei(GL_PACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level, is_converted)));          glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level))); -        const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level); +        const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level, is_converted); +          u8* const mip_data = staging_buffer.data() + mip_offset;          const GLsizei size = static_cast<GLsizei>(params.GetHostMipmapSize(level));          if (is_compressed) { @@ -294,14 +299,10 @@ void CachedSurface::UploadTexture(const std::vector<u8>& staging_buffer) {  }  void CachedSurface::UploadTextureMipmap(u32 level, const std::vector<u8>& staging_buffer) { -    glPixelStorei(GL_UNPACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level))); +    glPixelStorei(GL_UNPACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level, is_converted)));      glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level))); -    auto compression_type = params.GetCompressionType(); - -    const std::size_t mip_offset = compression_type == SurfaceCompression::Converted -                                       ? params.GetConvertedMipmapOffset(level) -                                       : params.GetHostMipmapLevelOffset(level); +    const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level, is_converted);      const u8* buffer{staging_buffer.data() + mip_offset};      if (is_compressed) {          const auto image_size{static_cast<GLsizei>(params.GetHostMipmapSize(level))}; @@ -482,7 +483,7 @@ OGLTextureView CachedSurfaceView::CreateTextureView() const {  TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system,                                         VideoCore::RasterizerInterface& rasterizer,                                         const Device& device, StateTracker& state_tracker) -    : TextureCacheBase{system, rasterizer}, state_tracker{state_tracker} { +    : TextureCacheBase{system, rasterizer, device.HasASTC()}, state_tracker{state_tracker} {      src_framebuffer.Create();      dst_framebuffer.Create();  } @@ -490,7 +491,7 @@ TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system,  TextureCacheOpenGL::~TextureCacheOpenGL() = default;  Surface TextureCacheOpenGL::CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) { -    return std::make_shared<CachedSurface>(gpu_addr, params); +    return std::make_shared<CachedSurface>(gpu_addr, params, is_astc_supported);  }  void TextureCacheOpenGL::ImageCopy(Surface& src_surface, Surface& dst_surface, @@ -596,7 +597,7 @@ void TextureCacheOpenGL::BufferCopy(Surface& src_surface, Surface& dst_surface)      glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle); -    if (source_format.compressed) { +    if (src_surface->IsCompressed()) {          glGetCompressedTextureImage(src_surface->GetTexture(), 0, static_cast<GLsizei>(source_size),                                      nullptr);      } else { @@ -610,7 +611,7 @@ void TextureCacheOpenGL::BufferCopy(Surface& src_surface, Surface& dst_surface)      const GLsizei width = static_cast<GLsizei>(dst_params.width);      const GLsizei height = static_cast<GLsizei>(dst_params.height);      const GLsizei depth = static_cast<GLsizei>(dst_params.depth); -    if (dest_format.compressed) { +    if (dst_surface->IsCompressed()) {          LOG_CRITICAL(HW_GPU, "Compressed buffer copy is unimplemented!");          UNREACHABLE();      } else { diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 6658c6ffd..02d9981a1 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h @@ -37,7 +37,7 @@ class CachedSurface final : public VideoCommon::SurfaceBase<View> {      friend CachedSurfaceView;  public: -    explicit CachedSurface(GPUVAddr gpu_addr, const SurfaceParams& params); +    explicit CachedSurface(GPUVAddr gpu_addr, const SurfaceParams& params, bool is_astc_supported);      ~CachedSurface();      void UploadTexture(const std::vector<u8>& staging_buffer) override; @@ -51,6 +51,10 @@ public:          return texture.handle;      } +    bool IsCompressed() const { +        return is_compressed; +    } +  protected:      void DecorateSurfaceName() override; diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index fca5e3ec0..f1a28cc21 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -30,8 +30,6 @@ namespace OpenGL {  namespace { -// If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have -// to wait on available presentation frames.  constexpr std::size_t SWAP_CHAIN_SIZE = 3;  struct Frame { @@ -214,7 +212,7 @@ public:      std::deque<Frame*> present_queue;      Frame* previous_frame{}; -    FrameMailbox() : has_debug_tool{HasDebugTool()} { +    FrameMailbox() {          for (auto& frame : swap_chain) {              free_queue.push(&frame);          } @@ -285,13 +283,9 @@ public:          std::unique_lock lock{swap_chain_lock};          present_queue.push_front(frame);          present_cv.notify_one(); - -        DebugNotifyNextFrame();      }      Frame* TryGetPresentFrame(int timeout_ms) { -        DebugWaitForNextFrame(); -          std::unique_lock lock{swap_chain_lock};          // wait for new entries in the present_queue          present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms), @@ -317,38 +311,12 @@ public:          previous_frame = frame;          return frame;      } - -private: -    std::mutex debug_synch_mutex; -    std::condition_variable debug_synch_condition; -    std::atomic_int frame_for_debug{}; -    const bool has_debug_tool; // When true, using a GPU debugger, so keep frames in lock-step - -    /// Signal that a new frame is available (called from GPU thread) -    void DebugNotifyNextFrame() { -        if (!has_debug_tool) { -            return; -        } -        frame_for_debug++; -        std::lock_guard lock{debug_synch_mutex}; -        debug_synch_condition.notify_one(); -    } - -    /// Wait for a new frame to be available (called from presentation thread) -    void DebugWaitForNextFrame() { -        if (!has_debug_tool) { -            return; -        } -        const int last_frame = frame_for_debug; -        std::unique_lock lock{debug_synch_mutex}; -        debug_synch_condition.wait(lock, -                                   [this, last_frame] { return frame_for_debug > last_frame; }); -    }  }; -RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system) +RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system, +                               Core::Frontend::GraphicsContext& context)      : VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system}, -      frame_mailbox{std::make_unique<FrameMailbox>()} {} +      frame_mailbox{}, context{context}, has_debug_tool{HasDebugTool()} {}  RendererOpenGL::~RendererOpenGL() = default; @@ -356,8 +324,6 @@ MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 12  MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));  void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { -    render_window.PollEvents(); -      if (!framebuffer) {          return;      } @@ -413,6 +379,13 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {          m_current_frame++;          rasterizer->TickFrame();      } + +    render_window.PollEvents(); +    if (has_debug_tool) { +        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); +        Present(0); +        context.SwapBuffers(); +    }  }  void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) { @@ -480,6 +453,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color  }  void RendererOpenGL::InitOpenGLObjects() { +    frame_mailbox = std::make_unique<FrameMailbox>(); +      glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,                   0.0f); @@ -692,12 +667,21 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {      glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);  } -void RendererOpenGL::TryPresent(int timeout_ms) { +bool RendererOpenGL::TryPresent(int timeout_ms) { +    if (has_debug_tool) { +        LOG_DEBUG(Render_OpenGL, +                  "Skipping presentation because we are presenting on the main context"); +        return false; +    } +    return Present(timeout_ms); +} + +bool RendererOpenGL::Present(int timeout_ms) {      const auto& layout = render_window.GetFramebufferLayout();      auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms);      if (!frame) {          LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present"); -        return; +        return false;      }      // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a @@ -725,6 +709,7 @@ void RendererOpenGL::TryPresent(int timeout_ms) {      glFlush();      glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); +    return true;  }  void RendererOpenGL::RenderScreenshot() { diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 33073ce5b..50b647661 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -55,13 +55,14 @@ class FrameMailbox;  class RendererOpenGL final : public VideoCore::RendererBase {  public: -    explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system); +    explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system, +                            Core::Frontend::GraphicsContext& context);      ~RendererOpenGL() override;      bool Init() override;      void ShutDown() override;      void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; -    void TryPresent(int timeout_ms) override; +    bool TryPresent(int timeout_ms) override;  private:      /// Initializes the OpenGL state and creates persistent objects. @@ -89,8 +90,11 @@ private:      void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer); +    bool Present(int timeout_ms); +      Core::Frontend::EmuWindow& emu_window;      Core::System& system; +    Core::Frontend::GraphicsContext& context;      StateTracker state_tracker{system}; @@ -115,6 +119,8 @@ private:      /// Frame presentation mailbox      std::unique_ptr<FrameMailbox> frame_mailbox; + +    bool has_debug_tool = false;  };  } // namespace OpenGL diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 42bb01418..6953aaafe 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -141,8 +141,9 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {      render_window.PollEvents();  } -void RendererVulkan::TryPresent(int /*timeout_ms*/) { +bool RendererVulkan::TryPresent(int /*timeout_ms*/) {      // TODO (bunnei): ImplementMe +    return true;  }  bool RendererVulkan::Init() { diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 3da08d2e4..d14384e79 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -42,7 +42,7 @@ public:      bool Init() override;      void ShutDown() override;      void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; -    void TryPresent(int timeout_ms) override; +    bool TryPresent(int timeout_ms) override;  private:      std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback( diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp index 28d2fbc4f..7aafb5e59 100644 --- a/src/video_core/renderer_vulkan/vk_device.cpp +++ b/src/video_core/renderer_vulkan/vk_device.cpp @@ -237,18 +237,21 @@ void VKDevice::ReportLoss() const {  bool VKDevice::IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features,                                        const vk::DispatchLoaderDynamic& dldi) const { -    // Disable for now to avoid converting ASTC twice. -    return false;      static constexpr std::array astc_formats = { -        vk::Format::eAstc4x4SrgbBlock,    vk::Format::eAstc8x8SrgbBlock, -        vk::Format::eAstc8x5SrgbBlock,    vk::Format::eAstc5x4SrgbBlock, +        vk::Format::eAstc4x4UnormBlock,   vk::Format::eAstc4x4SrgbBlock, +        vk::Format::eAstc5x4UnormBlock,   vk::Format::eAstc5x4SrgbBlock,          vk::Format::eAstc5x5UnormBlock,   vk::Format::eAstc5x5SrgbBlock, -        vk::Format::eAstc10x8UnormBlock,  vk::Format::eAstc10x8SrgbBlock, +        vk::Format::eAstc6x5UnormBlock,   vk::Format::eAstc6x5SrgbBlock,          vk::Format::eAstc6x6UnormBlock,   vk::Format::eAstc6x6SrgbBlock, -        vk::Format::eAstc10x10UnormBlock, vk::Format::eAstc10x10SrgbBlock, -        vk::Format::eAstc12x12UnormBlock, vk::Format::eAstc12x12SrgbBlock, +        vk::Format::eAstc8x5UnormBlock,   vk::Format::eAstc8x5SrgbBlock,          vk::Format::eAstc8x6UnormBlock,   vk::Format::eAstc8x6SrgbBlock, -        vk::Format::eAstc6x5UnormBlock,   vk::Format::eAstc6x5SrgbBlock}; +        vk::Format::eAstc8x8UnormBlock,   vk::Format::eAstc8x8SrgbBlock, +        vk::Format::eAstc10x5UnormBlock,  vk::Format::eAstc10x5SrgbBlock, +        vk::Format::eAstc10x6UnormBlock,  vk::Format::eAstc10x6SrgbBlock, +        vk::Format::eAstc10x8UnormBlock,  vk::Format::eAstc10x8SrgbBlock, +        vk::Format::eAstc10x10UnormBlock, vk::Format::eAstc10x10SrgbBlock, +        vk::Format::eAstc12x10UnormBlock, vk::Format::eAstc12x10SrgbBlock, +        vk::Format::eAstc12x12UnormBlock, vk::Format::eAstc12x12SrgbBlock};      if (!features.textureCompressionASTC_LDR) {          return false;      } @@ -572,24 +575,34 @@ std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperti                                          vk::Format::eBc2SrgbBlock,                                          vk::Format::eBc3SrgbBlock,                                          vk::Format::eBc7SrgbBlock, +                                        vk::Format::eAstc4x4UnormBlock,                                          vk::Format::eAstc4x4SrgbBlock, -                                        vk::Format::eAstc8x8SrgbBlock, -                                        vk::Format::eAstc8x5SrgbBlock, +                                        vk::Format::eAstc5x4UnormBlock,                                          vk::Format::eAstc5x4SrgbBlock,                                          vk::Format::eAstc5x5UnormBlock,                                          vk::Format::eAstc5x5SrgbBlock, -                                        vk::Format::eAstc10x8UnormBlock, -                                        vk::Format::eAstc10x8SrgbBlock, +                                        vk::Format::eAstc6x5UnormBlock, +                                        vk::Format::eAstc6x5SrgbBlock,                                          vk::Format::eAstc6x6UnormBlock,                                          vk::Format::eAstc6x6SrgbBlock, +                                        vk::Format::eAstc8x5UnormBlock, +                                        vk::Format::eAstc8x5SrgbBlock, +                                        vk::Format::eAstc8x6UnormBlock, +                                        vk::Format::eAstc8x6SrgbBlock, +                                        vk::Format::eAstc8x8UnormBlock, +                                        vk::Format::eAstc8x8SrgbBlock, +                                        vk::Format::eAstc10x5UnormBlock, +                                        vk::Format::eAstc10x5SrgbBlock, +                                        vk::Format::eAstc10x6UnormBlock, +                                        vk::Format::eAstc10x6SrgbBlock, +                                        vk::Format::eAstc10x8UnormBlock, +                                        vk::Format::eAstc10x8SrgbBlock,                                          vk::Format::eAstc10x10UnormBlock,                                          vk::Format::eAstc10x10SrgbBlock, +                                        vk::Format::eAstc12x10UnormBlock, +                                        vk::Format::eAstc12x10SrgbBlock,                                          vk::Format::eAstc12x12UnormBlock,                                          vk::Format::eAstc12x12SrgbBlock, -                                        vk::Format::eAstc8x6UnormBlock, -                                        vk::Format::eAstc8x6SrgbBlock, -                                        vk::Format::eAstc6x5UnormBlock, -                                        vk::Format::eAstc6x5SrgbBlock,                                          vk::Format::eE5B9G9R9UfloatPack32};      std::unordered_map<vk::Format, vk::FormatProperties> format_properties;      for (const auto format : formats) { diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index 51ecb5567..b9f9e2714 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp @@ -35,7 +35,7 @@ namespace {  using Sirit::Id;  using Tegra::Engines::ShaderType;  using Tegra::Shader::Attribute; -using Tegra::Shader::AttributeUse; +using Tegra::Shader::PixelImap;  using Tegra::Shader::Register;  using namespace VideoCommon::Shader; @@ -752,16 +752,16 @@ private:              if (stage != ShaderType::Fragment) {                  continue;              } -            switch (header.ps.GetAttributeUse(location)) { -            case AttributeUse::Constant: +            switch (header.ps.GetPixelImap(location)) { +            case PixelImap::Constant:                  Decorate(id, spv::Decoration::Flat);                  break; -            case AttributeUse::ScreenLinear: -                Decorate(id, spv::Decoration::NoPerspective); -                break; -            case AttributeUse::Perspective: +            case PixelImap::Perspective:                  // Default                  break; +            case PixelImap::ScreenLinear: +                Decorate(id, spv::Decoration::NoPerspective); +                break;              default:                  UNREACHABLE_MSG("Unused attribute being fetched");              } @@ -1145,9 +1145,6 @@ private:              switch (attribute) {              case Attribute::Index::Position: {                  if (stage == ShaderType::Fragment) { -                    if (element == 3) { -                        return {Constant(t_float, 1.0f), Type::Float}; -                    }                      return {OpLoad(t_float, AccessElement(t_in_float, frag_coord, element)),                              Type::Float};                  } @@ -1941,7 +1938,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 +1950,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 +2547,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/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 26175921b..5b9b39670 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -35,7 +35,6 @@ using VideoCore::MortonSwizzleMode;  using Tegra::Texture::SwizzleSource;  using VideoCore::Surface::PixelFormat; -using VideoCore::Surface::SurfaceCompression;  using VideoCore::Surface::SurfaceTarget;  namespace { @@ -96,9 +95,10 @@ vk::ImageViewType GetImageViewType(SurfaceTarget target) {      return {};  } -UniqueBuffer CreateBuffer(const VKDevice& device, const SurfaceParams& params) { +UniqueBuffer CreateBuffer(const VKDevice& device, const SurfaceParams& params, +                          std::size_t host_memory_size) {      // TODO(Rodrigo): Move texture buffer creation to the buffer cache -    const vk::BufferCreateInfo buffer_ci({}, params.GetHostSizeInBytes(), +    const vk::BufferCreateInfo buffer_ci({}, host_memory_size,                                           vk::BufferUsageFlagBits::eUniformTexelBuffer |                                               vk::BufferUsageFlagBits::eTransferSrc |                                               vk::BufferUsageFlagBits::eTransferDst, @@ -110,12 +110,13 @@ UniqueBuffer CreateBuffer(const VKDevice& device, const SurfaceParams& params) {  vk::BufferViewCreateInfo GenerateBufferViewCreateInfo(const VKDevice& device,                                                        const SurfaceParams& params, -                                                      vk::Buffer buffer) { +                                                      vk::Buffer buffer, +                                                      std::size_t host_memory_size) {      ASSERT(params.IsBuffer());      const auto format =          MaxwellToVK::SurfaceFormat(device, FormatType::Buffer, params.pixel_format).format; -    return vk::BufferViewCreateInfo({}, buffer, format, 0, params.GetHostSizeInBytes()); +    return vk::BufferViewCreateInfo({}, buffer, format, 0, host_memory_size);  }  vk::ImageCreateInfo GenerateImageCreateInfo(const VKDevice& device, const SurfaceParams& params) { @@ -169,14 +170,15 @@ CachedSurface::CachedSurface(Core::System& system, const VKDevice& device,                               VKResourceManager& resource_manager, VKMemoryManager& memory_manager,                               VKScheduler& scheduler, VKStagingBufferPool& staging_pool,                               GPUVAddr gpu_addr, const SurfaceParams& params) -    : SurfaceBase<View>{gpu_addr, params}, system{system}, device{device}, -      resource_manager{resource_manager}, memory_manager{memory_manager}, scheduler{scheduler}, -      staging_pool{staging_pool} { +    : SurfaceBase<View>{gpu_addr, params, device.IsOptimalAstcSupported()}, system{system}, +      device{device}, resource_manager{resource_manager}, +      memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{staging_pool} {      if (params.IsBuffer()) { -        buffer = CreateBuffer(device, params); +        buffer = CreateBuffer(device, params, host_memory_size);          commit = memory_manager.Commit(*buffer, false); -        const auto buffer_view_ci = GenerateBufferViewCreateInfo(device, params, *buffer); +        const auto buffer_view_ci = +            GenerateBufferViewCreateInfo(device, params, *buffer, host_memory_size);          format = buffer_view_ci.format;          const auto dev = device.GetLogical(); @@ -255,7 +257,7 @@ void CachedSurface::UploadBuffer(const std::vector<u8>& staging_buffer) {      std::memcpy(src_buffer.commit->Map(host_memory_size), staging_buffer.data(), host_memory_size);      scheduler.Record([src_buffer = *src_buffer.handle, dst_buffer = *buffer, -                      size = params.GetHostSizeInBytes()](auto cmdbuf, auto& dld) { +                      size = host_memory_size](auto cmdbuf, auto& dld) {          const vk::BufferCopy copy(0, 0, size);          cmdbuf.copyBuffer(src_buffer, dst_buffer, {copy}, dld); @@ -299,10 +301,7 @@ void CachedSurface::UploadImage(const std::vector<u8>& staging_buffer) {  vk::BufferImageCopy CachedSurface::GetBufferImageCopy(u32 level) const {      const u32 vk_depth = params.target == SurfaceTarget::Texture3D ? params.GetMipDepth(level) : 1; -    const auto compression_type = params.GetCompressionType(); -    const std::size_t mip_offset = compression_type == SurfaceCompression::Converted -                                       ? params.GetConvertedMipmapOffset(level) -                                       : params.GetHostMipmapLevelOffset(level); +    const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level, is_converted);      return vk::BufferImageCopy(          mip_offset, 0, 0, @@ -390,8 +389,9 @@ VKTextureCache::VKTextureCache(Core::System& system, VideoCore::RasterizerInterf                                 const VKDevice& device, VKResourceManager& resource_manager,                                 VKMemoryManager& memory_manager, VKScheduler& scheduler,                                 VKStagingBufferPool& staging_pool) -    : TextureCache(system, rasterizer), device{device}, resource_manager{resource_manager}, -      memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{staging_pool} {} +    : TextureCache(system, rasterizer, device.IsOptimalAstcSupported()), device{device}, +      resource_manager{resource_manager}, memory_manager{memory_manager}, scheduler{scheduler}, +      staging_pool{staging_pool} {}  VKTextureCache::~VKTextureCache() = default; diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp new file mode 100644 index 000000000..9b94dfff1 --- /dev/null +++ b/src/video_core/renderer_vulkan/wrapper.cpp @@ -0,0 +1,750 @@ +// 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; +} + +Instance Instance::Create(Span<const char*> layers, Span<const char*> extensions, +                          InstanceDispatch& dld) noexcept { +    VkApplicationInfo application_info; +    application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; +    application_info.pNext = nullptr; +    application_info.pApplicationName = "yuzu Emulator"; +    application_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0); +    application_info.pEngineName = "yuzu Emulator"; +    application_info.engineVersion = VK_MAKE_VERSION(0, 1, 0); +    application_info.apiVersion = VK_API_VERSION_1_1; + +    VkInstanceCreateInfo ci; +    ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; +    ci.pNext = nullptr; +    ci.flags = 0; +    ci.pApplicationInfo = &application_info; +    ci.enabledLayerCount = layers.size(); +    ci.ppEnabledLayerNames = layers.data(); +    ci.enabledExtensionCount = extensions.size(); +    ci.ppEnabledExtensionNames = extensions.data(); + +    VkInstance instance; +    if (dld.vkCreateInstance(&ci, nullptr, &instance) != VK_SUCCESS) { +        // Failed to create the instance. +        return {}; +    } +    if (!Proc(dld.vkDestroyInstance, dld, "vkDestroyInstance", instance)) { +        // We successfully created an instance but the destroy function couldn't be loaded. +        // This is a good moment to panic. +        return {}; +    } + +    return Instance(instance, dld); +} + +std::optional<std::vector<VkPhysicalDevice>> Instance::EnumeratePhysicalDevices() { +    u32 num; +    if (dld->vkEnumeratePhysicalDevices(handle, &num, nullptr) != VK_SUCCESS) { +        return std::nullopt; +    } +    std::vector<VkPhysicalDevice> physical_devices(num); +    if (dld->vkEnumeratePhysicalDevices(handle, &num, physical_devices.data()) != VK_SUCCESS) { +        return std::nullopt; +    } +    return physical_devices; +} + +DebugCallback Instance::TryCreateDebugCallback( +    PFN_vkDebugUtilsMessengerCallbackEXT callback) noexcept { +    VkDebugUtilsMessengerCreateInfoEXT ci; +    ci.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; +    ci.pNext = nullptr; +    ci.flags = 0; +    ci.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | +                         VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | +                         VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | +                         VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; +    ci.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | +                     VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | +                     VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; +    ci.pfnUserCallback = callback; +    ci.pUserData = nullptr; + +    VkDebugUtilsMessengerEXT messenger; +    if (dld->vkCreateDebugUtilsMessengerEXT(handle, &ci, nullptr, &messenger) != VK_SUCCESS) { +        return {}; +    } +    return DebugCallback(messenger, handle, *dld); +} + +std::vector<VkCheckpointDataNV> Queue::GetCheckpointDataNV(const DeviceDispatch& dld) const { +    if (!dld.vkGetQueueCheckpointDataNV) { +        return {}; +    } +    u32 num; +    dld.vkGetQueueCheckpointDataNV(queue, &num, nullptr); +    std::vector<VkCheckpointDataNV> checkpoints(num); +    dld.vkGetQueueCheckpointDataNV(queue, &num, checkpoints.data()); +    return checkpoints; +} + +void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const { +    Check(dld->vkBindBufferMemory(owner, handle, memory, offset)); +} + +void Image::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const { +    Check(dld->vkBindImageMemory(owner, handle, memory, offset)); +} + +DescriptorSets DescriptorPool::Allocate(const VkDescriptorSetAllocateInfo& ai) const { +    const std::size_t num = ai.descriptorSetCount; +    std::unique_ptr sets = std::make_unique<VkDescriptorSet[]>(num); +    switch (const VkResult result = dld->vkAllocateDescriptorSets(owner, &ai, sets.get())) { +    case VK_SUCCESS: +        return DescriptorSets(std::move(sets), num, owner, handle, *dld); +    case VK_ERROR_OUT_OF_POOL_MEMORY: +        return {}; +    default: +        throw Exception(result); +    } +} + +CommandBuffers CommandPool::Allocate(std::size_t num_buffers, VkCommandBufferLevel level) const { +    VkCommandBufferAllocateInfo ai; +    ai.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; +    ai.pNext = nullptr; +    ai.commandPool = handle; +    ai.level = level; +    ai.commandBufferCount = static_cast<u32>(num_buffers); + +    std::unique_ptr buffers = std::make_unique<VkCommandBuffer[]>(num_buffers); +    switch (const VkResult result = dld->vkAllocateCommandBuffers(owner, &ai, buffers.get())) { +    case VK_SUCCESS: +        return CommandBuffers(std::move(buffers), num_buffers, owner, handle, *dld); +    case VK_ERROR_OUT_OF_POOL_MEMORY: +        return {}; +    default: +        throw Exception(result); +    } +} + +std::vector<VkImage> SwapchainKHR::GetImages() const { +    u32 num; +    Check(dld->vkGetSwapchainImagesKHR(owner, handle, &num, nullptr)); +    std::vector<VkImage> images(num); +    Check(dld->vkGetSwapchainImagesKHR(owner, handle, &num, images.data())); +    return images; +} + +Device Device::Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci, +                      Span<const char*> enabled_extensions, +                      const VkPhysicalDeviceFeatures2& enabled_features, +                      DeviceDispatch& dld) noexcept { +    VkDeviceCreateInfo ci; +    ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; +    ci.pNext = &enabled_features; +    ci.flags = 0; +    ci.queueCreateInfoCount = queues_ci.size(); +    ci.pQueueCreateInfos = queues_ci.data(); +    ci.enabledLayerCount = 0; +    ci.ppEnabledLayerNames = nullptr; +    ci.enabledExtensionCount = enabled_extensions.size(); +    ci.ppEnabledExtensionNames = enabled_extensions.data(); +    ci.pEnabledFeatures = nullptr; + +    VkDevice device; +    if (dld.vkCreateDevice(physical_device, &ci, nullptr, &device) != VK_SUCCESS) { +        return {}; +    } +    Load(device, dld); +    return Device(device, dld); +} + +Queue Device::GetQueue(u32 family_index) const noexcept { +    VkQueue queue; +    dld->vkGetDeviceQueue(handle, family_index, 0, &queue); +    return Queue(queue, *dld); +} + +Buffer Device::CreateBuffer(const VkBufferCreateInfo& ci) const { +    VkBuffer object; +    Check(dld->vkCreateBuffer(handle, &ci, nullptr, &object)); +    return Buffer(object, handle, *dld); +} + +BufferView Device::CreateBufferView(const VkBufferViewCreateInfo& ci) const { +    VkBufferView object; +    Check(dld->vkCreateBufferView(handle, &ci, nullptr, &object)); +    return BufferView(object, handle, *dld); +} + +Image Device::CreateImage(const VkImageCreateInfo& ci) const { +    VkImage object; +    Check(dld->vkCreateImage(handle, &ci, nullptr, &object)); +    return Image(object, handle, *dld); +} + +ImageView Device::CreateImageView(const VkImageViewCreateInfo& ci) const { +    VkImageView object; +    Check(dld->vkCreateImageView(handle, &ci, nullptr, &object)); +    return ImageView(object, handle, *dld); +} + +Semaphore Device::CreateSemaphore() const { +    VkSemaphoreCreateInfo ci; +    ci.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; +    ci.pNext = nullptr; +    ci.flags = 0; + +    VkSemaphore object; +    Check(dld->vkCreateSemaphore(handle, &ci, nullptr, &object)); +    return Semaphore(object, handle, *dld); +} + +Fence Device::CreateFence(const VkFenceCreateInfo& ci) const { +    VkFence object; +    Check(dld->vkCreateFence(handle, &ci, nullptr, &object)); +    return Fence(object, handle, *dld); +} + +DescriptorPool Device::CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const { +    VkDescriptorPool object; +    Check(dld->vkCreateDescriptorPool(handle, &ci, nullptr, &object)); +    return DescriptorPool(object, handle, *dld); +} + +RenderPass Device::CreateRenderPass(const VkRenderPassCreateInfo& ci) const { +    VkRenderPass object; +    Check(dld->vkCreateRenderPass(handle, &ci, nullptr, &object)); +    return RenderPass(object, handle, *dld); +} + +DescriptorSetLayout Device::CreateDescriptorSetLayout( +    const VkDescriptorSetLayoutCreateInfo& ci) const { +    VkDescriptorSetLayout object; +    Check(dld->vkCreateDescriptorSetLayout(handle, &ci, nullptr, &object)); +    return DescriptorSetLayout(object, handle, *dld); +} + +PipelineLayout Device::CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const { +    VkPipelineLayout object; +    Check(dld->vkCreatePipelineLayout(handle, &ci, nullptr, &object)); +    return PipelineLayout(object, handle, *dld); +} + +Pipeline Device::CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci) const { +    VkPipeline object; +    Check(dld->vkCreateGraphicsPipelines(handle, nullptr, 1, &ci, nullptr, &object)); +    return Pipeline(object, handle, *dld); +} + +Pipeline Device::CreateComputePipeline(const VkComputePipelineCreateInfo& ci) const { +    VkPipeline object; +    Check(dld->vkCreateComputePipelines(handle, nullptr, 1, &ci, nullptr, &object)); +    return Pipeline(object, handle, *dld); +} + +Sampler Device::CreateSampler(const VkSamplerCreateInfo& ci) const { +    VkSampler object; +    Check(dld->vkCreateSampler(handle, &ci, nullptr, &object)); +    return Sampler(object, handle, *dld); +} + +Framebuffer Device::CreateFramebuffer(const VkFramebufferCreateInfo& ci) const { +    VkFramebuffer object; +    Check(dld->vkCreateFramebuffer(handle, &ci, nullptr, &object)); +    return Framebuffer(object, handle, *dld); +} + +CommandPool Device::CreateCommandPool(const VkCommandPoolCreateInfo& ci) const { +    VkCommandPool object; +    Check(dld->vkCreateCommandPool(handle, &ci, nullptr, &object)); +    return CommandPool(object, handle, *dld); +} + +DescriptorUpdateTemplateKHR Device::CreateDescriptorUpdateTemplateKHR( +    const VkDescriptorUpdateTemplateCreateInfoKHR& ci) const { +    VkDescriptorUpdateTemplateKHR object; +    Check(dld->vkCreateDescriptorUpdateTemplateKHR(handle, &ci, nullptr, &object)); +    return DescriptorUpdateTemplateKHR(object, handle, *dld); +} + +QueryPool Device::CreateQueryPool(const VkQueryPoolCreateInfo& ci) const { +    VkQueryPool object; +    Check(dld->vkCreateQueryPool(handle, &ci, nullptr, &object)); +    return QueryPool(object, handle, *dld); +} + +ShaderModule Device::CreateShaderModule(const VkShaderModuleCreateInfo& ci) const { +    VkShaderModule object; +    Check(dld->vkCreateShaderModule(handle, &ci, nullptr, &object)); +    return ShaderModule(object, handle, *dld); +} + +SwapchainKHR Device::CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const { +    VkSwapchainKHR object; +    Check(dld->vkCreateSwapchainKHR(handle, &ci, nullptr, &object)); +    return SwapchainKHR(object, handle, *dld); +} + +DeviceMemory Device::TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept { +    VkDeviceMemory memory; +    if (dld->vkAllocateMemory(handle, &ai, nullptr, &memory) != VK_SUCCESS) { +        return {}; +    } +    return DeviceMemory(memory, handle, *dld); +} + +DeviceMemory Device::AllocateMemory(const VkMemoryAllocateInfo& ai) const { +    VkDeviceMemory memory; +    Check(dld->vkAllocateMemory(handle, &ai, nullptr, &memory)); +    return DeviceMemory(memory, handle, *dld); +} + +VkMemoryRequirements Device::GetBufferMemoryRequirements(VkBuffer buffer) const noexcept { +    VkMemoryRequirements requirements; +    dld->vkGetBufferMemoryRequirements(handle, buffer, &requirements); +    return requirements; +} + +VkMemoryRequirements Device::GetImageMemoryRequirements(VkImage image) const noexcept { +    VkMemoryRequirements requirements; +    dld->vkGetImageMemoryRequirements(handle, image, &requirements); +    return requirements; +} + +void Device::UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes, +                                  Span<VkCopyDescriptorSet> copies) const noexcept { +    dld->vkUpdateDescriptorSets(handle, writes.size(), writes.data(), copies.size(), copies.data()); +} + +VkPhysicalDeviceProperties PhysicalDevice::GetProperties() const noexcept { +    VkPhysicalDeviceProperties properties; +    dld->vkGetPhysicalDeviceProperties(physical_device, &properties); +    return properties; +} + +void PhysicalDevice::GetProperties2KHR(VkPhysicalDeviceProperties2KHR& properties) const noexcept { +    dld->vkGetPhysicalDeviceProperties2KHR(physical_device, &properties); +} + +VkPhysicalDeviceFeatures PhysicalDevice::GetFeatures() const noexcept { +    VkPhysicalDeviceFeatures2KHR features2; +    features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR; +    features2.pNext = nullptr; +    dld->vkGetPhysicalDeviceFeatures2KHR(physical_device, &features2); +    return features2.features; +} + +void PhysicalDevice::GetFeatures2KHR(VkPhysicalDeviceFeatures2KHR& features) const noexcept { +    dld->vkGetPhysicalDeviceFeatures2KHR(physical_device, &features); +} + +VkFormatProperties PhysicalDevice::GetFormatProperties(VkFormat format) const noexcept { +    VkFormatProperties properties; +    dld->vkGetPhysicalDeviceFormatProperties(physical_device, format, &properties); +    return properties; +} + +std::vector<VkExtensionProperties> PhysicalDevice::EnumerateDeviceExtensionProperties() const { +    u32 num; +    dld->vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &num, nullptr); +    std::vector<VkExtensionProperties> properties(num); +    dld->vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &num, properties.data()); +    return properties; +} + +std::vector<VkQueueFamilyProperties> PhysicalDevice::GetQueueFamilyProperties() const { +    u32 num; +    dld->vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &num, nullptr); +    std::vector<VkQueueFamilyProperties> properties(num); +    dld->vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &num, properties.data()); +    return properties; +} + +bool PhysicalDevice::GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR surface) const { +    VkBool32 supported; +    Check(dld->vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, queue_family_index, surface, +                                                    &supported)); +    return supported == VK_TRUE; +} + +VkSurfaceCapabilitiesKHR PhysicalDevice::GetSurfaceCapabilitiesKHR(VkSurfaceKHR surface) const +    noexcept { +    VkSurfaceCapabilitiesKHR capabilities; +    Check(dld->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &capabilities)); +    return capabilities; +} + +std::vector<VkSurfaceFormatKHR> PhysicalDevice::GetSurfaceFormatsKHR(VkSurfaceKHR surface) const { +    u32 num; +    Check(dld->vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &num, nullptr)); +    std::vector<VkSurfaceFormatKHR> formats(num); +    Check( +        dld->vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &num, formats.data())); +    return formats; +} + +std::vector<VkPresentModeKHR> PhysicalDevice::GetSurfacePresentModesKHR( +    VkSurfaceKHR surface) const { +    u32 num; +    Check(dld->vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &num, nullptr)); +    std::vector<VkPresentModeKHR> modes(num); +    Check(dld->vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &num, +                                                         modes.data())); +    return modes; +} + +VkPhysicalDeviceMemoryProperties PhysicalDevice::GetMemoryProperties() const noexcept { +    VkPhysicalDeviceMemoryProperties properties; +    dld->vkGetPhysicalDeviceMemoryProperties(physical_device, &properties); +    return properties; +} + +std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProperties( +    const InstanceDispatch& dld) { +    u32 num; +    if (dld.vkEnumerateInstanceExtensionProperties(nullptr, &num, nullptr) != VK_SUCCESS) { +        return std::nullopt; +    } +    std::vector<VkExtensionProperties> properties(num); +    if (dld.vkEnumerateInstanceExtensionProperties(nullptr, &num, properties.data()) != +        VK_SUCCESS) { +        return std::nullopt; +    } +    return properties; +} + +} // 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..fb3657819 --- /dev/null +++ b/src/video_core/renderer_vulkan/wrapper.h @@ -0,0 +1,987 @@ +// 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>; + +/// Vulkan instance owning handle. +class Instance : public Handle<VkInstance, NoOwner, InstanceDispatch> { +    using Handle<VkInstance, NoOwner, InstanceDispatch>::Handle; + +public: +    /// Creates a Vulkan instance. Use "operator bool" for error handling. +    static Instance Create(Span<const char*> layers, Span<const char*> extensions, +                           InstanceDispatch& dld) noexcept; + +    /// Enumerates physical devices. +    /// @return Physical devices and an empty handle on failure. +    std::optional<std::vector<VkPhysicalDevice>> EnumeratePhysicalDevices(); + +    /// Tries to create a debug callback messenger. Returns an empty handle on failure. +    DebugCallback TryCreateDebugCallback(PFN_vkDebugUtilsMessengerCallbackEXT callback) noexcept; +}; + +class Queue { +public: +    /// Construct an empty queue handle. +    constexpr Queue() noexcept = default; + +    /// Construct a queue handle. +    constexpr Queue(VkQueue queue, const DeviceDispatch& dld) noexcept : queue{queue}, dld{&dld} {} + +    /// Returns the checkpoint data. +    /// @note Returns an empty vector when the function pointer is not present. +    std::vector<VkCheckpointDataNV> GetCheckpointDataNV(const DeviceDispatch& dld) const; + +    void Submit(Span<VkSubmitInfo> submit_infos, VkFence fence) const { +        Check(dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence)); +    } + +    VkResult Present(const VkPresentInfoKHR& present_info) const noexcept { +        return dld->vkQueuePresentKHR(queue, &present_info); +    } + +private: +    VkQueue queue = nullptr; +    const DeviceDispatch* dld = nullptr; +}; + +class Buffer : public Handle<VkBuffer, VkDevice, DeviceDispatch> { +    using Handle<VkBuffer, VkDevice, DeviceDispatch>::Handle; + +public: +    /// Attaches a memory allocation. +    void BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const; +}; + +class Image : public Handle<VkImage, VkDevice, DeviceDispatch> { +    using Handle<VkImage, VkDevice, DeviceDispatch>::Handle; + +public: +    /// Attaches a memory allocation. +    void BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const; +}; + +class DeviceMemory : public Handle<VkDeviceMemory, VkDevice, DeviceDispatch> { +    using Handle<VkDeviceMemory, VkDevice, DeviceDispatch>::Handle; + +public: +    u8* Map(VkDeviceSize offset, VkDeviceSize size) const { +        void* data; +        Check(dld->vkMapMemory(owner, handle, offset, size, 0, &data)); +        return static_cast<u8*>(data); +    } + +    void Unmap() const noexcept { +        dld->vkUnmapMemory(owner, handle); +    } +}; + +class Fence : public Handle<VkFence, VkDevice, DeviceDispatch> { +    using Handle<VkFence, VkDevice, DeviceDispatch>::Handle; + +public: +    VkResult Wait(u64 timeout = std::numeric_limits<u64>::max()) const noexcept { +        return dld->vkWaitForFences(owner, 1, &handle, true, timeout); +    } + +    VkResult GetStatus() const noexcept { +        return dld->vkGetFenceStatus(owner, handle); +    } + +    void Reset() const { +        Check(dld->vkResetFences(owner, 1, &handle)); +    } +}; + +class DescriptorPool : public Handle<VkDescriptorPool, VkDevice, DeviceDispatch> { +    using Handle<VkDescriptorPool, VkDevice, DeviceDispatch>::Handle; + +public: +    DescriptorSets Allocate(const VkDescriptorSetAllocateInfo& ai) const; +}; + +class CommandPool : public Handle<VkCommandPool, VkDevice, DeviceDispatch> { +    using Handle<VkCommandPool, VkDevice, DeviceDispatch>::Handle; + +public: +    CommandBuffers Allocate(std::size_t num_buffers, +                            VkCommandBufferLevel level = VK_COMMAND_BUFFER_LEVEL_PRIMARY) const; +}; + +class SwapchainKHR : public Handle<VkSwapchainKHR, VkDevice, DeviceDispatch> { +    using Handle<VkSwapchainKHR, VkDevice, DeviceDispatch>::Handle; + +public: +    std::vector<VkImage> GetImages() const; +}; + +class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> { +    using Handle<VkDevice, NoOwner, DeviceDispatch>::Handle; + +public: +    static Device Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci, +                         Span<const char*> enabled_extensions, +                         const VkPhysicalDeviceFeatures2& enabled_features, +                         DeviceDispatch& dld) noexcept; + +    Queue GetQueue(u32 family_index) const noexcept; + +    Buffer CreateBuffer(const VkBufferCreateInfo& ci) const; + +    BufferView CreateBufferView(const VkBufferViewCreateInfo& ci) const; + +    Image CreateImage(const VkImageCreateInfo& ci) const; + +    ImageView CreateImageView(const VkImageViewCreateInfo& ci) const; + +    Semaphore CreateSemaphore() const; + +    Fence CreateFence(const VkFenceCreateInfo& ci) const; + +    DescriptorPool CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const; + +    RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const; + +    DescriptorSetLayout CreateDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo& ci) const; + +    PipelineLayout CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const; + +    Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci) const; + +    Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci) const; + +    Sampler CreateSampler(const VkSamplerCreateInfo& ci) const; + +    Framebuffer CreateFramebuffer(const VkFramebufferCreateInfo& ci) const; + +    CommandPool CreateCommandPool(const VkCommandPoolCreateInfo& ci) const; + +    DescriptorUpdateTemplateKHR CreateDescriptorUpdateTemplateKHR( +        const VkDescriptorUpdateTemplateCreateInfoKHR& ci) const; + +    QueryPool CreateQueryPool(const VkQueryPoolCreateInfo& ci) const; + +    ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const; + +    SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const; + +    DeviceMemory TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept; + +    DeviceMemory AllocateMemory(const VkMemoryAllocateInfo& ai) const; + +    VkMemoryRequirements GetBufferMemoryRequirements(VkBuffer buffer) const noexcept; + +    VkMemoryRequirements GetImageMemoryRequirements(VkImage image) const noexcept; + +    void UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes, +                              Span<VkCopyDescriptorSet> copies) const noexcept; + +    void UpdateDescriptorSet(VkDescriptorSet set, VkDescriptorUpdateTemplateKHR update_template, +                             const void* data) const noexcept { +        dld->vkUpdateDescriptorSetWithTemplateKHR(handle, set, update_template, data); +    } + +    VkResult AcquireNextImageKHR(VkSwapchainKHR swapchain, u64 timeout, VkSemaphore semaphore, +                                 VkFence fence, u32* image_index) const noexcept { +        return dld->vkAcquireNextImageKHR(handle, swapchain, timeout, semaphore, fence, +                                          image_index); +    } + +    VkResult WaitIdle() const noexcept { +        return dld->vkDeviceWaitIdle(handle); +    } + +    void ResetQueryPoolEXT(VkQueryPool query_pool, u32 first, u32 count) const noexcept { +        dld->vkResetQueryPoolEXT(handle, query_pool, first, count); +    } + +    void GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size, +                         void* data, VkDeviceSize stride, VkQueryResultFlags flags) const { +        Check(dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride, +                                         flags)); +    } + +    template <typename T> +    T GetQueryResult(VkQueryPool query_pool, u32 first, VkQueryResultFlags flags) const { +        static_assert(std::is_trivially_copyable_v<T>); +        T value; +        GetQueryResults(query_pool, first, 1, sizeof(T), &value, sizeof(T), flags); +        return value; +    } +}; + +class PhysicalDevice { +public: +    constexpr PhysicalDevice() noexcept = default; + +    constexpr PhysicalDevice(VkPhysicalDevice physical_device, const InstanceDispatch& dld) noexcept +        : physical_device{physical_device}, dld{&dld} {} + +    constexpr operator VkPhysicalDevice() const noexcept { +        return physical_device; +    } + +    VkPhysicalDeviceProperties GetProperties() const noexcept; + +    void GetProperties2KHR(VkPhysicalDeviceProperties2KHR&) const noexcept; + +    VkPhysicalDeviceFeatures GetFeatures() const noexcept; + +    void GetFeatures2KHR(VkPhysicalDeviceFeatures2KHR&) const noexcept; + +    VkFormatProperties GetFormatProperties(VkFormat) const noexcept; + +    std::vector<VkExtensionProperties> EnumerateDeviceExtensionProperties() const; + +    std::vector<VkQueueFamilyProperties> GetQueueFamilyProperties() const; + +    bool GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR) const; + +    VkSurfaceCapabilitiesKHR GetSurfaceCapabilitiesKHR(VkSurfaceKHR) const noexcept; + +    std::vector<VkSurfaceFormatKHR> GetSurfaceFormatsKHR(VkSurfaceKHR) const; + +    std::vector<VkPresentModeKHR> GetSurfacePresentModesKHR(VkSurfaceKHR) const; + +    VkPhysicalDeviceMemoryProperties GetMemoryProperties() const noexcept; + +private: +    VkPhysicalDevice physical_device = nullptr; +    const InstanceDispatch* dld = nullptr; +}; + +class CommandBuffer { +public: +    CommandBuffer() noexcept = default; + +    explicit CommandBuffer(VkCommandBuffer handle, const DeviceDispatch& dld) noexcept +        : handle{handle}, dld{&dld} {} + +    const VkCommandBuffer* address() const noexcept { +        return &handle; +    } + +    void Begin(const VkCommandBufferBeginInfo& begin_info) const { +        Check(dld->vkBeginCommandBuffer(handle, &begin_info)); +    } + +    void End() const { +        Check(dld->vkEndCommandBuffer(handle)); +    } + +    void BeginRenderPass(const VkRenderPassBeginInfo& renderpass_bi, +                         VkSubpassContents contents) const noexcept { +        dld->vkCmdBeginRenderPass(handle, &renderpass_bi, contents); +    } + +    void EndRenderPass() const noexcept { +        dld->vkCmdEndRenderPass(handle); +    } + +    void BeginQuery(VkQueryPool query_pool, u32 query, VkQueryControlFlags flags) const noexcept { +        dld->vkCmdBeginQuery(handle, query_pool, query, flags); +    } + +    void EndQuery(VkQueryPool query_pool, u32 query) const noexcept { +        dld->vkCmdEndQuery(handle, query_pool, query); +    } + +    void BindDescriptorSets(VkPipelineBindPoint bind_point, VkPipelineLayout layout, u32 first, +                            Span<VkDescriptorSet> sets, Span<u32> dynamic_offsets) const noexcept { +        dld->vkCmdBindDescriptorSets(handle, bind_point, layout, first, sets.size(), sets.data(), +                                     dynamic_offsets.size(), dynamic_offsets.data()); +    } + +    void BindPipeline(VkPipelineBindPoint bind_point, VkPipeline pipeline) const noexcept { +        dld->vkCmdBindPipeline(handle, bind_point, pipeline); +    } + +    void BindIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType index_type) const +        noexcept { +        dld->vkCmdBindIndexBuffer(handle, buffer, offset, index_type); +    } + +    void BindVertexBuffers(u32 first, u32 count, const VkBuffer* buffers, +                           const VkDeviceSize* offsets) const noexcept { +        dld->vkCmdBindVertexBuffers(handle, first, count, buffers, offsets); +    } + +    void BindVertexBuffer(u32 binding, VkBuffer buffer, VkDeviceSize offset) const noexcept { +        BindVertexBuffers(binding, 1, &buffer, &offset); +    } + +    void Draw(u32 vertex_count, u32 instance_count, u32 first_vertex, u32 first_instance) const +        noexcept { +        dld->vkCmdDraw(handle, vertex_count, instance_count, first_vertex, first_instance); +    } + +    void DrawIndexed(u32 index_count, u32 instance_count, u32 first_index, u32 vertex_offset, +                     u32 first_instance) const noexcept { +        dld->vkCmdDrawIndexed(handle, index_count, instance_count, first_index, vertex_offset, +                              first_instance); +    } + +    void ClearAttachments(Span<VkClearAttachment> attachments, Span<VkClearRect> rects) const +        noexcept { +        dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(), +                                   rects.data()); +    } + +    void BlitImage(VkImage src_image, VkImageLayout src_layout, VkImage dst_image, +                   VkImageLayout dst_layout, Span<VkImageBlit> regions, VkFilter filter) const +        noexcept { +        dld->vkCmdBlitImage(handle, src_image, src_layout, dst_image, dst_layout, regions.size(), +                            regions.data(), filter); +    } + +    void Dispatch(u32 x, u32 y, u32 z) const noexcept { +        dld->vkCmdDispatch(handle, x, y, z); +    } + +    void PipelineBarrier(VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, +                         VkDependencyFlags dependency_flags, Span<VkMemoryBarrier> memory_barriers, +                         Span<VkBufferMemoryBarrier> buffer_barriers, +                         Span<VkImageMemoryBarrier> image_barriers) const noexcept { +        dld->vkCmdPipelineBarrier(handle, src_stage_mask, dst_stage_mask, dependency_flags, +                                  memory_barriers.size(), memory_barriers.data(), +                                  buffer_barriers.size(), buffer_barriers.data(), +                                  image_barriers.size(), image_barriers.data()); +    } + +    void CopyBufferToImage(VkBuffer src_buffer, VkImage dst_image, VkImageLayout dst_image_layout, +                           Span<VkBufferImageCopy> regions) const noexcept { +        dld->vkCmdCopyBufferToImage(handle, src_buffer, dst_image, dst_image_layout, regions.size(), +                                    regions.data()); +    } + +    void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, Span<VkBufferCopy> regions) const +        noexcept { +        dld->vkCmdCopyBuffer(handle, src_buffer, dst_buffer, regions.size(), regions.data()); +    } + +    void CopyImage(VkImage src_image, VkImageLayout src_layout, VkImage dst_image, +                   VkImageLayout dst_layout, Span<VkImageCopy> regions) const noexcept { +        dld->vkCmdCopyImage(handle, src_image, src_layout, dst_image, dst_layout, regions.size(), +                            regions.data()); +    } + +    void CopyImageToBuffer(VkImage src_image, VkImageLayout src_layout, VkBuffer dst_buffer, +                           Span<VkBufferImageCopy> regions) const noexcept { +        dld->vkCmdCopyImageToBuffer(handle, src_image, src_layout, dst_buffer, regions.size(), +                                    regions.data()); +    } + +    void FillBuffer(VkBuffer dst_buffer, VkDeviceSize dst_offset, VkDeviceSize size, u32 data) const +        noexcept { +        dld->vkCmdFillBuffer(handle, dst_buffer, dst_offset, size, data); +    } + +    void PushConstants(VkPipelineLayout layout, VkShaderStageFlags flags, u32 offset, u32 size, +                       const void* values) const noexcept { +        dld->vkCmdPushConstants(handle, layout, flags, offset, size, values); +    } + +    void SetCheckpointNV(const void* checkpoint_marker) const noexcept { +        dld->vkCmdSetCheckpointNV(handle, checkpoint_marker); +    } + +    void SetViewport(u32 first, Span<VkViewport> viewports) const noexcept { +        dld->vkCmdSetViewport(handle, first, viewports.size(), viewports.data()); +    } + +    void SetScissor(u32 first, Span<VkRect2D> scissors) const noexcept { +        dld->vkCmdSetScissor(handle, first, scissors.size(), scissors.data()); +    } + +    void SetBlendConstants(const float blend_constants[4]) const noexcept { +        dld->vkCmdSetBlendConstants(handle, blend_constants); +    } + +    void SetStencilCompareMask(VkStencilFaceFlags face_mask, u32 compare_mask) const noexcept { +        dld->vkCmdSetStencilCompareMask(handle, face_mask, compare_mask); +    } + +    void SetStencilReference(VkStencilFaceFlags face_mask, u32 reference) const noexcept { +        dld->vkCmdSetStencilReference(handle, face_mask, reference); +    } + +    void SetStencilWriteMask(VkStencilFaceFlags face_mask, u32 write_mask) const noexcept { +        dld->vkCmdSetStencilWriteMask(handle, face_mask, write_mask); +    } + +    void SetDepthBias(float constant_factor, float clamp, float slope_factor) const noexcept { +        dld->vkCmdSetDepthBias(handle, constant_factor, clamp, slope_factor); +    } + +    void SetDepthBounds(float min_depth_bounds, float max_depth_bounds) const noexcept { +        dld->vkCmdSetDepthBounds(handle, min_depth_bounds, max_depth_bounds); +    } + +    void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers, +                                         const VkDeviceSize* offsets, +                                         const VkDeviceSize* sizes) const noexcept { +        dld->vkCmdBindTransformFeedbackBuffersEXT(handle, first, count, buffers, offsets, sizes); +    } + +    void BeginTransformFeedbackEXT(u32 first_counter_buffer, u32 counter_buffers_count, +                                   const VkBuffer* counter_buffers, +                                   const VkDeviceSize* counter_buffer_offsets) const noexcept { +        dld->vkCmdBeginTransformFeedbackEXT(handle, first_counter_buffer, counter_buffers_count, +                                            counter_buffers, counter_buffer_offsets); +    } + +    void EndTransformFeedbackEXT(u32 first_counter_buffer, u32 counter_buffers_count, +                                 const VkBuffer* counter_buffers, +                                 const VkDeviceSize* counter_buffer_offsets) const noexcept { +        dld->vkCmdEndTransformFeedbackEXT(handle, first_counter_buffer, counter_buffers_count, +                                          counter_buffers, counter_buffer_offsets); +    } + +private: +    VkCommandBuffer handle; +    const DeviceDispatch* dld; +}; + +std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProperties( +    const InstanceDispatch& dld); + +} // namespace Vulkan::vk diff --git a/src/video_core/shader/decode/arithmetic_integer.cpp b/src/video_core/shader/decode/arithmetic_integer.cpp index 2fe787d6f..0f4c3103a 100644 --- a/src/video_core/shader/decode/arithmetic_integer.cpp +++ b/src/video_core/shader/decode/arithmetic_integer.cpp @@ -235,34 +235,30 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) {      case OpCode::Id::LEA_IMM:      case OpCode::Id::LEA_RZ:      case OpCode::Id::LEA_HI: { -        const auto [op_a, op_b, op_c] = [&]() -> std::tuple<Node, Node, Node> { +        auto [op_a, op_b, op_c] = [&]() -> std::tuple<Node, Node, Node> {              switch (opcode->get().GetId()) {              case OpCode::Id::LEA_R2: {                  return {GetRegister(instr.gpr20), GetRegister(instr.gpr39),                          Immediate(static_cast<u32>(instr.lea.r2.entry_a))};              } -              case OpCode::Id::LEA_R1: {                  const bool neg = instr.lea.r1.neg != 0;                  return {GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true),                          GetRegister(instr.gpr20),                          Immediate(static_cast<u32>(instr.lea.r1.entry_a))};              } -              case OpCode::Id::LEA_IMM: {                  const bool neg = instr.lea.imm.neg != 0;                  return {Immediate(static_cast<u32>(instr.lea.imm.entry_a)),                          GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true),                          Immediate(static_cast<u32>(instr.lea.imm.entry_b))};              } -              case OpCode::Id::LEA_RZ: {                  const bool neg = instr.lea.rz.neg != 0;                  return {GetConstBuffer(instr.lea.rz.cb_index, instr.lea.rz.cb_offset),                          GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true),                          Immediate(static_cast<u32>(instr.lea.rz.entry_a))};              } -              case OpCode::Id::LEA_HI:              default:                  UNIMPLEMENTED_MSG("Unhandled LEA subinstruction: {}", opcode->get().GetName()); @@ -275,12 +271,9 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) {          UNIMPLEMENTED_IF_MSG(instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex),                               "Unhandled LEA Predicate"); -        const Node shifted_c = -            Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, Immediate(1), op_c); -        const Node mul_bc = Operation(OperationCode::IMul, NO_PRECISE, op_b, shifted_c); -        const Node value = Operation(OperationCode::IAdd, NO_PRECISE, op_a, mul_bc); - -        SetRegister(bb, instr.gpr0, value); +        Node value = Operation(OperationCode::ILogicalShiftLeft, std::move(op_a), std::move(op_c)); +        value = Operation(OperationCode::IAdd, std::move(op_b), std::move(value)); +        SetRegister(bb, instr.gpr0, std::move(value));          break;      } diff --git a/src/video_core/shader/decode/conversion.cpp b/src/video_core/shader/decode/conversion.cpp index 6ead42070..c72690b2b 100644 --- a/src/video_core/shader/decode/conversion.cpp +++ b/src/video_core/shader/decode/conversion.cpp @@ -138,18 +138,23 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {          value = GetOperandAbsNegFloat(value, instr.conversion.abs_a, instr.conversion.negate_a); -        value = [&]() { +        value = [&] { +            if (instr.conversion.src_size != instr.conversion.dst_size) { +                // Rounding operations only matter when the source and destination conversion size +                // is the same. +                return value; +            }              switch (instr.conversion.f2f.GetRoundingMode()) {              case Tegra::Shader::F2fRoundingOp::None:                  return value;              case Tegra::Shader::F2fRoundingOp::Round: -                return Operation(OperationCode::FRoundEven, PRECISE, value); +                return Operation(OperationCode::FRoundEven, value);              case Tegra::Shader::F2fRoundingOp::Floor: -                return Operation(OperationCode::FFloor, PRECISE, value); +                return Operation(OperationCode::FFloor, value);              case Tegra::Shader::F2fRoundingOp::Ceil: -                return Operation(OperationCode::FCeil, PRECISE, value); +                return Operation(OperationCode::FCeil, value);              case Tegra::Shader::F2fRoundingOp::Trunc: -                return Operation(OperationCode::FTrunc, PRECISE, value); +                return Operation(OperationCode::FTrunc, value);              default:                  UNIMPLEMENTED_MSG("Unimplemented F2F rounding mode {}",                                    static_cast<u32>(instr.conversion.f2f.rounding.Value())); diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp index d2fe4ec5d..0dd7a1196 100644 --- a/src/video_core/shader/decode/image.cpp +++ b/src/video_core/shader/decode/image.cpp @@ -13,13 +13,247 @@  #include "video_core/engines/shader_bytecode.h"  #include "video_core/shader/node_helper.h"  #include "video_core/shader/shader_ir.h" +#include "video_core/textures/texture.h"  namespace VideoCommon::Shader {  using Tegra::Shader::Instruction;  using Tegra::Shader::OpCode; +using Tegra::Shader::PredCondition; +using Tegra::Shader::StoreType; +using Tegra::Texture::ComponentType; +using Tegra::Texture::TextureFormat; +using Tegra::Texture::TICEntry;  namespace { + +ComponentType GetComponentType(Tegra::Engines::SamplerDescriptor descriptor, +                               std::size_t component) { +    const TextureFormat format{descriptor.format}; +    switch (format) { +    case TextureFormat::R16_G16_B16_A16: +    case TextureFormat::R32_G32_B32_A32: +    case TextureFormat::R32_G32_B32: +    case TextureFormat::R32_G32: +    case TextureFormat::R16_G16: +    case TextureFormat::R32: +    case TextureFormat::R16: +    case TextureFormat::R8: +    case TextureFormat::R1: +        if (component == 0) { +            return descriptor.r_type; +        } +        if (component == 1) { +            return descriptor.g_type; +        } +        if (component == 2) { +            return descriptor.b_type; +        } +        if (component == 3) { +            return descriptor.a_type; +        } +        break; +    case TextureFormat::A8R8G8B8: +        if (component == 0) { +            return descriptor.a_type; +        } +        if (component == 1) { +            return descriptor.r_type; +        } +        if (component == 2) { +            return descriptor.g_type; +        } +        if (component == 3) { +            return descriptor.b_type; +        } +        break; +    case TextureFormat::A2B10G10R10: +    case TextureFormat::A4B4G4R4: +    case TextureFormat::A5B5G5R1: +    case TextureFormat::A1B5G5R5: +        if (component == 0) { +            return descriptor.a_type; +        } +        if (component == 1) { +            return descriptor.b_type; +        } +        if (component == 2) { +            return descriptor.g_type; +        } +        if (component == 3) { +            return descriptor.r_type; +        } +        break; +    case TextureFormat::R32_B24G8: +        if (component == 0) { +            return descriptor.r_type; +        } +        if (component == 1) { +            return descriptor.b_type; +        } +        if (component == 2) { +            return descriptor.g_type; +        } +        break; +    case TextureFormat::B5G6R5: +    case TextureFormat::B6G5R5: +        if (component == 0) { +            return descriptor.b_type; +        } +        if (component == 1) { +            return descriptor.g_type; +        } +        if (component == 2) { +            return descriptor.r_type; +        } +        break; +    case TextureFormat::G8R24: +    case TextureFormat::G24R8: +    case TextureFormat::G8R8: +    case TextureFormat::G4R4: +        if (component == 0) { +            return descriptor.g_type; +        } +        if (component == 1) { +            return descriptor.r_type; +        } +        break; +    } +    UNIMPLEMENTED_MSG("texture format not implement={}", format); +    return ComponentType::FLOAT; +} + +bool IsComponentEnabled(std::size_t component_mask, std::size_t component) { +    constexpr u8 R = 0b0001; +    constexpr u8 G = 0b0010; +    constexpr u8 B = 0b0100; +    constexpr u8 A = 0b1000; +    constexpr std::array<u8, 16> mask = { +        0,   (R),     (G),     (R | G),     (B),     (R | B),     (G | B),     (R | G | B), +        (A), (R | A), (G | A), (R | G | A), (B | A), (R | B | A), (G | B | A), (R | G | B | A)}; +    return std::bitset<4>{mask.at(component_mask)}.test(component); +} + +u32 GetComponentSize(TextureFormat format, std::size_t component) { +    switch (format) { +    case TextureFormat::R32_G32_B32_A32: +        return 32; +    case TextureFormat::R16_G16_B16_A16: +        return 16; +    case TextureFormat::R32_G32_B32: +        return component <= 2 ? 32 : 0; +    case TextureFormat::R32_G32: +        return component <= 1 ? 32 : 0; +    case TextureFormat::R16_G16: +        return component <= 1 ? 16 : 0; +    case TextureFormat::R32: +        return component == 0 ? 32 : 0; +    case TextureFormat::R16: +        return component == 0 ? 16 : 0; +    case TextureFormat::R8: +        return component == 0 ? 8 : 0; +    case TextureFormat::R1: +        return component == 0 ? 1 : 0; +    case TextureFormat::A8R8G8B8: +        return 8; +    case TextureFormat::A2B10G10R10: +        return (component == 3 || component == 2 || component == 1) ? 10 : 2; +    case TextureFormat::A4B4G4R4: +        return 4; +    case TextureFormat::A5B5G5R1: +        return (component == 0 || component == 1 || component == 2) ? 5 : 1; +    case TextureFormat::A1B5G5R5: +        return (component == 1 || component == 2 || component == 3) ? 5 : 1; +    case TextureFormat::R32_B24G8: +        if (component == 0) { +            return 32; +        } +        if (component == 1) { +            return 24; +        } +        if (component == 2) { +            return 8; +        } +        return 0; +    case TextureFormat::B5G6R5: +        if (component == 0 || component == 2) { +            return 5; +        } +        if (component == 1) { +            return 6; +        } +        return 0; +    case TextureFormat::B6G5R5: +        if (component == 1 || component == 2) { +            return 5; +        } +        if (component == 0) { +            return 6; +        } +        return 0; +    case TextureFormat::G8R24: +        if (component == 0) { +            return 8; +        } +        if (component == 1) { +            return 24; +        } +        return 0; +    case TextureFormat::G24R8: +        if (component == 0) { +            return 8; +        } +        if (component == 1) { +            return 24; +        } +        return 0; +    case TextureFormat::G8R8: +        return (component == 0 || component == 1) ? 8 : 0; +    case TextureFormat::G4R4: +        return (component == 0 || component == 1) ? 4 : 0; +    default: +        UNIMPLEMENTED_MSG("texture format not implement={}", format); +        return 0; +    } +} + +std::size_t GetImageComponentMask(TextureFormat format) { +    constexpr u8 R = 0b0001; +    constexpr u8 G = 0b0010; +    constexpr u8 B = 0b0100; +    constexpr u8 A = 0b1000; +    switch (format) { +    case TextureFormat::R32_G32_B32_A32: +    case TextureFormat::R16_G16_B16_A16: +    case TextureFormat::A8R8G8B8: +    case TextureFormat::A2B10G10R10: +    case TextureFormat::A4B4G4R4: +    case TextureFormat::A5B5G5R1: +    case TextureFormat::A1B5G5R5: +        return std::size_t{R | G | B | A}; +    case TextureFormat::R32_G32_B32: +    case TextureFormat::R32_B24G8: +    case TextureFormat::B5G6R5: +    case TextureFormat::B6G5R5: +        return std::size_t{R | G | B}; +    case TextureFormat::R32_G32: +    case TextureFormat::R16_G16: +    case TextureFormat::G8R24: +    case TextureFormat::G24R8: +    case TextureFormat::G8R8: +    case TextureFormat::G4R4: +        return std::size_t{R | G}; +    case TextureFormat::R32: +    case TextureFormat::R16: +    case TextureFormat::R8: +    case TextureFormat::R1: +        return std::size_t{R}; +    default: +        UNIMPLEMENTED_MSG("texture format not implement={}", format); +        return std::size_t{R | G | B | A}; +    } +} +  std::size_t GetImageTypeNumCoordinates(Tegra::Shader::ImageType image_type) {      switch (image_type) {      case Tegra::Shader::ImageType::Texture1D: @@ -37,6 +271,39 @@ std::size_t GetImageTypeNumCoordinates(Tegra::Shader::ImageType image_type) {  }  } // Anonymous namespace +std::pair<Node, bool> ShaderIR::GetComponentValue(ComponentType component_type, u32 component_size, +                                                  Node original_value) { +    switch (component_type) { +    case ComponentType::SNORM: { +        // range [-1.0, 1.0] +        auto cnv_value = Operation(OperationCode::FMul, original_value, +                                   Immediate(static_cast<float>(1 << component_size) / 2.f - 1.f)); +        cnv_value = Operation(OperationCode::ICastFloat, std::move(cnv_value)); +        return {BitfieldExtract(std::move(cnv_value), 0, component_size), true}; +    } +    case ComponentType::SINT: +    case ComponentType::UNORM: { +        bool is_signed = component_type == ComponentType::SINT; +        // range [0.0, 1.0] +        auto cnv_value = Operation(OperationCode::FMul, original_value, +                                   Immediate(static_cast<float>(1 << component_size) - 1.f)); +        return {SignedOperation(OperationCode::ICastFloat, is_signed, std::move(cnv_value)), +                is_signed}; +    } +    case ComponentType::UINT: // range [0, (1 << component_size) - 1] +        return {std::move(original_value), false}; +    case ComponentType::FLOAT: +        if (component_size == 16) { +            return {Operation(OperationCode::HCastFloat, original_value), true}; +        } else { +            return {std::move(original_value), true}; +        } +    default: +        UNIMPLEMENTED_MSG("Unimplement component type={}", component_type); +        return {std::move(original_value), true}; +    } +} +  u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {      const Instruction instr = {program_code[pc]};      const auto opcode = OpCode::Decode(instr); @@ -53,7 +320,6 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {      switch (opcode->get().GetId()) {      case OpCode::Id::SULD: { -        UNIMPLEMENTED_IF(instr.suldst.mode != Tegra::Shader::SurfaceDataMode::P);          UNIMPLEMENTED_IF(instr.suldst.out_of_bounds_store !=                           Tegra::Shader::OutOfBoundsStore::Ignore); @@ -62,17 +328,89 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {                                                : GetBindlessImage(instr.gpr39, type)};          image.MarkRead(); -        u32 indexer = 0; -        for (u32 element = 0; element < 4; ++element) { -            if (!instr.suldst.IsComponentEnabled(element)) { -                continue; +        if (instr.suldst.mode == Tegra::Shader::SurfaceDataMode::P) { +            u32 indexer = 0; +            for (u32 element = 0; element < 4; ++element) { +                if (!instr.suldst.IsComponentEnabled(element)) { +                    continue; +                } +                MetaImage meta{image, {}, element}; +                Node value = Operation(OperationCode::ImageLoad, meta, GetCoordinates(type)); +                SetTemporary(bb, indexer++, std::move(value)); +            } +            for (u32 i = 0; i < indexer; ++i) { +                SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i)); +            } +        } else if (instr.suldst.mode == Tegra::Shader::SurfaceDataMode::D_BA) { +            UNIMPLEMENTED_IF(instr.suldst.GetStoreDataLayout() != StoreType::Bits32 && +                             instr.suldst.GetStoreDataLayout() != StoreType::Bits64); + +            auto descriptor = [this, instr] { +                std::optional<Tegra::Engines::SamplerDescriptor> descriptor; +                if (instr.suldst.is_immediate) { +                    descriptor = +                        registry.ObtainBoundSampler(static_cast<u32>(instr.image.index.Value())); +                } else { +                    const Node image_register = GetRegister(instr.gpr39); +                    const auto [base_image, buffer, offset] = TrackCbuf( +                        image_register, global_code, static_cast<s64>(global_code.size())); +                    descriptor = registry.ObtainBindlessSampler(buffer, offset); +                } +                if (!descriptor) { +                    UNREACHABLE_MSG("Failed to obtain image descriptor"); +                } +                return *descriptor; +            }(); + +            const auto comp_mask = GetImageComponentMask(descriptor.format); + +            switch (instr.suldst.GetStoreDataLayout()) { +            case StoreType::Bits32: +            case StoreType::Bits64: { +                u32 indexer = 0; +                u32 shifted_counter = 0; +                Node value = Immediate(0); +                for (u32 element = 0; element < 4; ++element) { +                    if (!IsComponentEnabled(comp_mask, element)) { +                        continue; +                    } +                    const auto component_type = GetComponentType(descriptor, element); +                    const auto component_size = GetComponentSize(descriptor.format, element); +                    MetaImage meta{image, {}, element}; + +                    auto [converted_value, is_signed] = GetComponentValue( +                        component_type, component_size, +                        Operation(OperationCode::ImageLoad, meta, GetCoordinates(type))); + +                    // shift element to correct position +                    const auto shifted = shifted_counter; +                    if (shifted > 0) { +                        converted_value = +                            SignedOperation(OperationCode::ILogicalShiftLeft, is_signed, +                                            std::move(converted_value), Immediate(shifted)); +                    } +                    shifted_counter += component_size; + +                    // add value into result +                    value = Operation(OperationCode::UBitwiseOr, value, std::move(converted_value)); + +                    // if we shifted enough for 1 byte -> we save it into temp +                    if (shifted_counter >= 32) { +                        SetTemporary(bb, indexer++, std::move(value)); +                        // reset counter and value to prepare pack next byte +                        value = Immediate(0); +                        shifted_counter = 0; +                    } +                } +                for (u32 i = 0; i < indexer; ++i) { +                    SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i)); +                } +                break; +            } +            default: +                UNREACHABLE(); +                break;              } -            MetaImage meta{image, {}, element}; -            Node value = Operation(OperationCode::ImageLoad, meta, GetCoordinates(type)); -            SetTemporary(bb, indexer++, std::move(value)); -        } -        for (u32 i = 0; i < indexer; ++i) { -            SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i));          }          break;      } diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp index b5fbc4d58..b8f63922f 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,31 @@ 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; +        default: +            UNIMPLEMENTED_MSG("op={}", static_cast<int>(op)); +            return OperationCode::AtomicIAdd; +        } +    }(); +    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 +387,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 +402,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/decode/other.cpp b/src/video_core/shader/decode/other.cpp index 4944e9d69..e6edec459 100644 --- a/src/video_core/shader/decode/other.cpp +++ b/src/video_core/shader/decode/other.cpp @@ -11,12 +11,17 @@  namespace VideoCommon::Shader { +using std::move;  using Tegra::Shader::ConditionCode;  using Tegra::Shader::Instruction; +using Tegra::Shader::IpaInterpMode;  using Tegra::Shader::OpCode; +using Tegra::Shader::PixelImap;  using Tegra::Shader::Register;  using Tegra::Shader::SystemVariable; +using Index = Tegra::Shader::Attribute::Index; +  u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {      const Instruction instr = {program_code[pc]};      const auto opcode = OpCode::Decode(instr); @@ -213,27 +218,28 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {      }      case OpCode::Id::IPA: {          const bool is_physical = instr.ipa.idx && instr.gpr8.Value() != 0xff; -          const auto attribute = instr.attribute.fmt28; -        const Tegra::Shader::IpaMode input_mode{instr.ipa.interp_mode.Value(), -                                                instr.ipa.sample_mode.Value()}; +        const Index index = attribute.index;          Node value = is_physical ? GetPhysicalInputAttribute(instr.gpr8) -                                 : GetInputAttribute(attribute.index, attribute.element); -        const Tegra::Shader::Attribute::Index index = attribute.index.Value(); -        const bool is_generic = index >= Tegra::Shader::Attribute::Index::Attribute_0 && -                                index <= Tegra::Shader::Attribute::Index::Attribute_31; -        if (is_generic || is_physical) { -            // TODO(Blinkhawk): There are cases where a perspective attribute use PASS. -            // In theory by setting them as perspective, OpenGL does the perspective correction. -            // A way must figured to reverse the last step of it. -            if (input_mode.interpolation_mode == Tegra::Shader::IpaInterpMode::Multiply) { -                value = Operation(OperationCode::FMul, PRECISE, value, GetRegister(instr.gpr20)); +                                 : GetInputAttribute(index, attribute.element); + +        // Code taken from Ryujinx. +        if (index >= Index::Attribute_0 && index <= Index::Attribute_31) { +            const u32 location = static_cast<u32>(index) - static_cast<u32>(Index::Attribute_0); +            if (header.ps.GetPixelImap(location) == PixelImap::Perspective) { +                Node position_w = GetInputAttribute(Index::Position, 3); +                value = Operation(OperationCode::FMul, move(value), move(position_w));              }          } -        value = GetSaturatedFloat(value, instr.ipa.saturate); -        SetRegister(bb, instr.gpr0, value); +        if (instr.ipa.interp_mode == IpaInterpMode::Multiply) { +            value = Operation(OperationCode::FMul, move(value), GetRegister(instr.gpr20)); +        } + +        value = GetSaturatedFloat(move(value), instr.ipa.saturate); + +        SetRegister(bb, instr.gpr0, move(value));          break;      }      case OpCode::Id::OUT_R: { 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/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h index 80fc9b82c..ca6c976c9 100644 --- a/src/video_core/shader/shader_ir.h +++ b/src/video_core/shader/shader_ir.h @@ -312,6 +312,10 @@ private:      /// Conditionally saturates a half float pair      Node GetSaturatedHalfFloat(Node value, bool saturate = true); +    /// Get image component value by type and size +    std::pair<Node, bool> GetComponentValue(Tegra::Texture::ComponentType component_type, +                                            u32 component_size, Node original_value); +      /// Returns a predicate comparing two floats      Node GetPredicateComparisonFloat(Tegra::Shader::PredCondition condition, Node op_a, Node op_b);      /// Returns a predicate comparing two integers diff --git a/src/video_core/surface.h b/src/video_core/surface.h index ae8817465..e0acd44d3 100644 --- a/src/video_core/surface.h +++ b/src/video_core/surface.h @@ -504,103 +504,6 @@ static constexpr u32 GetBytesPerPixel(PixelFormat pixel_format) {      return GetFormatBpp(pixel_format) / CHAR_BIT;  } -enum class SurfaceCompression { -    None,       // Not compressed -    Compressed, // Texture is compressed -    Converted,  // Texture is converted before upload or after download -    Rearranged, // Texture is swizzled before upload or after download -}; - -constexpr std::array<SurfaceCompression, MaxPixelFormat> compression_type_table = {{ -    SurfaceCompression::None,       // ABGR8U -    SurfaceCompression::None,       // ABGR8S -    SurfaceCompression::None,       // ABGR8UI -    SurfaceCompression::None,       // B5G6R5U -    SurfaceCompression::None,       // A2B10G10R10U -    SurfaceCompression::None,       // A1B5G5R5U -    SurfaceCompression::None,       // R8U -    SurfaceCompression::None,       // R8UI -    SurfaceCompression::None,       // RGBA16F -    SurfaceCompression::None,       // RGBA16U -    SurfaceCompression::None,       // RGBA16S -    SurfaceCompression::None,       // RGBA16UI -    SurfaceCompression::None,       // R11FG11FB10F -    SurfaceCompression::None,       // RGBA32UI -    SurfaceCompression::Compressed, // DXT1 -    SurfaceCompression::Compressed, // DXT23 -    SurfaceCompression::Compressed, // DXT45 -    SurfaceCompression::Compressed, // DXN1 -    SurfaceCompression::Compressed, // DXN2UNORM -    SurfaceCompression::Compressed, // DXN2SNORM -    SurfaceCompression::Compressed, // BC7U -    SurfaceCompression::Compressed, // BC6H_UF16 -    SurfaceCompression::Compressed, // BC6H_SF16 -    SurfaceCompression::Converted,  // ASTC_2D_4X4 -    SurfaceCompression::None,       // BGRA8 -    SurfaceCompression::None,       // RGBA32F -    SurfaceCompression::None,       // RG32F -    SurfaceCompression::None,       // R32F -    SurfaceCompression::None,       // R16F -    SurfaceCompression::None,       // R16U -    SurfaceCompression::None,       // R16S -    SurfaceCompression::None,       // R16UI -    SurfaceCompression::None,       // R16I -    SurfaceCompression::None,       // RG16 -    SurfaceCompression::None,       // RG16F -    SurfaceCompression::None,       // RG16UI -    SurfaceCompression::None,       // RG16I -    SurfaceCompression::None,       // RG16S -    SurfaceCompression::None,       // RGB32F -    SurfaceCompression::None,       // RGBA8_SRGB -    SurfaceCompression::None,       // RG8U -    SurfaceCompression::None,       // RG8S -    SurfaceCompression::None,       // RG32UI -    SurfaceCompression::None,       // RGBX16F -    SurfaceCompression::None,       // R32UI -    SurfaceCompression::None,       // R32I -    SurfaceCompression::Converted,  // ASTC_2D_8X8 -    SurfaceCompression::Converted,  // ASTC_2D_8X5 -    SurfaceCompression::Converted,  // ASTC_2D_5X4 -    SurfaceCompression::None,       // BGRA8_SRGB -    SurfaceCompression::Compressed, // DXT1_SRGB -    SurfaceCompression::Compressed, // DXT23_SRGB -    SurfaceCompression::Compressed, // DXT45_SRGB -    SurfaceCompression::Compressed, // BC7U_SRGB -    SurfaceCompression::None,       // R4G4B4A4U -    SurfaceCompression::Converted,  // ASTC_2D_4X4_SRGB -    SurfaceCompression::Converted,  // ASTC_2D_8X8_SRGB -    SurfaceCompression::Converted,  // ASTC_2D_8X5_SRGB -    SurfaceCompression::Converted,  // ASTC_2D_5X4_SRGB -    SurfaceCompression::Converted,  // ASTC_2D_5X5 -    SurfaceCompression::Converted,  // ASTC_2D_5X5_SRGB -    SurfaceCompression::Converted,  // ASTC_2D_10X8 -    SurfaceCompression::Converted,  // ASTC_2D_10X8_SRGB -    SurfaceCompression::Converted,  // ASTC_2D_6X6 -    SurfaceCompression::Converted,  // ASTC_2D_6X6_SRGB -    SurfaceCompression::Converted,  // ASTC_2D_10X10 -    SurfaceCompression::Converted,  // ASTC_2D_10X10_SRGB -    SurfaceCompression::Converted,  // ASTC_2D_12X12 -    SurfaceCompression::Converted,  // ASTC_2D_12X12_SRGB -    SurfaceCompression::Converted,  // ASTC_2D_8X6 -    SurfaceCompression::Converted,  // ASTC_2D_8X6_SRGB -    SurfaceCompression::Converted,  // ASTC_2D_6X5 -    SurfaceCompression::Converted,  // ASTC_2D_6X5_SRGB -    SurfaceCompression::None,       // E5B9G9R9F -    SurfaceCompression::None,       // Z32F -    SurfaceCompression::None,       // Z16 -    SurfaceCompression::None,       // Z24S8 -    SurfaceCompression::Rearranged, // S8Z24 -    SurfaceCompression::None,       // Z32FS8 -}}; - -constexpr SurfaceCompression GetFormatCompressionType(PixelFormat format) { -    if (format == PixelFormat::Invalid) { -        return SurfaceCompression::None; -    } -    DEBUG_ASSERT(static_cast<std::size_t>(format) < compression_type_table.size()); -    return compression_type_table[static_cast<std::size_t>(format)]; -} -  SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type);  bool SurfaceTargetIsLayered(SurfaceTarget target); diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp index 002df414f..6fe815135 100644 --- a/src/video_core/texture_cache/surface_base.cpp +++ b/src/video_core/texture_cache/surface_base.cpp @@ -18,15 +18,20 @@ MICROPROFILE_DEFINE(GPU_Flush_Texture, "GPU", "Texture Flush", MP_RGB(128, 192,  using Tegra::Texture::ConvertFromGuestToHost;  using VideoCore::MortonSwizzleMode; -using VideoCore::Surface::SurfaceCompression; +using VideoCore::Surface::IsPixelFormatASTC; +using VideoCore::Surface::PixelFormat;  StagingCache::StagingCache() = default;  StagingCache::~StagingCache() = default; -SurfaceBaseImpl::SurfaceBaseImpl(GPUVAddr gpu_addr, const SurfaceParams& params) -    : params{params}, host_memory_size{params.GetHostSizeInBytes()}, gpu_addr{gpu_addr}, -      mipmap_sizes(params.num_levels), mipmap_offsets(params.num_levels) { +SurfaceBaseImpl::SurfaceBaseImpl(GPUVAddr gpu_addr, const SurfaceParams& params, +                                 bool is_astc_supported) +    : params{params}, gpu_addr{gpu_addr}, mipmap_sizes(params.num_levels), +      mipmap_offsets(params.num_levels) { +    is_converted = IsPixelFormatASTC(params.pixel_format) && !is_astc_supported; +    host_memory_size = params.GetHostSizeInBytes(is_converted); +      std::size_t offset = 0;      for (u32 level = 0; level < params.num_levels; ++level) {          const std::size_t mipmap_size{params.GetGuestMipmapSize(level)}; @@ -164,7 +169,7 @@ void SurfaceBaseImpl::SwizzleFunc(MortonSwizzleMode mode, u8* memory, const Surf      std::size_t guest_offset{mipmap_offsets[level]};      if (params.is_layered) { -        std::size_t host_offset{0}; +        std::size_t host_offset = 0;          const std::size_t guest_stride = layer_size;          const std::size_t host_stride = params.GetHostLayerSize(level);          for (u32 layer = 0; layer < params.depth; ++layer) { @@ -206,7 +211,7 @@ void SurfaceBaseImpl::LoadBuffer(Tegra::MemoryManager& memory_manager,          ASSERT_MSG(params.block_width == 0, "Block width is defined as {} on texture target {}",                     params.block_width, static_cast<u32>(params.target));          for (u32 level = 0; level < params.num_levels; ++level) { -            const std::size_t host_offset{params.GetHostMipmapLevelOffset(level)}; +            const std::size_t host_offset{params.GetHostMipmapLevelOffset(level, false)};              SwizzleFunc(MortonSwizzleMode::MortonToLinear, host_ptr, params,                          staging_buffer.data() + host_offset, level);          } @@ -219,7 +224,7 @@ void SurfaceBaseImpl::LoadBuffer(Tegra::MemoryManager& memory_manager,          const u32 height{(params.height + block_height - 1) / block_height};          const u32 copy_size{width * bpp};          if (params.pitch == copy_size) { -            std::memcpy(staging_buffer.data(), host_ptr, params.GetHostSizeInBytes()); +            std::memcpy(staging_buffer.data(), host_ptr, params.GetHostSizeInBytes(false));          } else {              const u8* start{host_ptr};              u8* write_to{staging_buffer.data()}; @@ -231,19 +236,15 @@ void SurfaceBaseImpl::LoadBuffer(Tegra::MemoryManager& memory_manager,          }      } -    auto compression_type = params.GetCompressionType(); -    if (compression_type == SurfaceCompression::None || -        compression_type == SurfaceCompression::Compressed) +    if (!is_converted && params.pixel_format != PixelFormat::S8Z24) {          return; +    } -    for (u32 level_up = params.num_levels; level_up > 0; --level_up) { -        const u32 level = level_up - 1; -        const std::size_t in_host_offset{params.GetHostMipmapLevelOffset(level)}; -        const std::size_t out_host_offset = compression_type == SurfaceCompression::Rearranged -                                                ? in_host_offset -                                                : params.GetConvertedMipmapOffset(level); -        u8* in_buffer = staging_buffer.data() + in_host_offset; -        u8* out_buffer = staging_buffer.data() + out_host_offset; +    for (u32 level = params.num_levels; level--;) { +        const std::size_t in_host_offset{params.GetHostMipmapLevelOffset(level, false)}; +        const std::size_t out_host_offset{params.GetHostMipmapLevelOffset(level, is_converted)}; +        u8* const in_buffer = staging_buffer.data() + in_host_offset; +        u8* const out_buffer = staging_buffer.data() + out_host_offset;          ConvertFromGuestToHost(in_buffer, out_buffer, params.pixel_format,                                 params.GetMipWidth(level), params.GetMipHeight(level),                                 params.GetMipDepth(level), true, true); @@ -273,7 +274,7 @@ void SurfaceBaseImpl::FlushBuffer(Tegra::MemoryManager& memory_manager,      if (params.is_tiled) {          ASSERT_MSG(params.block_width == 0, "Block width is defined as {}", params.block_width);          for (u32 level = 0; level < params.num_levels; ++level) { -            const std::size_t host_offset{params.GetHostMipmapLevelOffset(level)}; +            const std::size_t host_offset{params.GetHostMipmapLevelOffset(level, false)};              SwizzleFunc(MortonSwizzleMode::LinearToMorton, host_ptr, params,                          staging_buffer.data() + host_offset, level);          } diff --git a/src/video_core/texture_cache/surface_base.h b/src/video_core/texture_cache/surface_base.h index 5f79bb0aa..d7882a031 100644 --- a/src/video_core/texture_cache/surface_base.h +++ b/src/video_core/texture_cache/surface_base.h @@ -131,6 +131,10 @@ public:          return !params.is_tiled;      } +    bool IsConverted() const { +        return is_converted; +    } +      bool MatchFormat(VideoCore::Surface::PixelFormat pixel_format) const {          return params.pixel_format == pixel_format;      } @@ -160,7 +164,8 @@ public:      }  protected: -    explicit SurfaceBaseImpl(GPUVAddr gpu_addr, const SurfaceParams& params); +    explicit SurfaceBaseImpl(GPUVAddr gpu_addr, const SurfaceParams& params, +                             bool is_astc_supported);      ~SurfaceBaseImpl() = default;      virtual void DecorateSurfaceName() = 0; @@ -168,12 +173,13 @@ protected:      const SurfaceParams params;      std::size_t layer_size;      std::size_t guest_memory_size; -    const std::size_t host_memory_size; +    std::size_t host_memory_size;      GPUVAddr gpu_addr{};      CacheAddr cache_addr{};      CacheAddr cache_addr_end{};      VAddr cpu_addr{};      bool is_continuous{}; +    bool is_converted{};      std::vector<std::size_t> mipmap_sizes;      std::vector<std::size_t> mipmap_offsets; @@ -288,8 +294,9 @@ public:      }  protected: -    explicit SurfaceBase(const GPUVAddr gpu_addr, const SurfaceParams& params) -        : SurfaceBaseImpl(gpu_addr, params) {} +    explicit SurfaceBase(const GPUVAddr gpu_addr, const SurfaceParams& params, +                         bool is_astc_supported) +        : SurfaceBaseImpl(gpu_addr, params, is_astc_supported) {}      ~SurfaceBase() = default; diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp index 9931c5ef7..6f3ef45be 100644 --- a/src/video_core/texture_cache/surface_params.cpp +++ b/src/video_core/texture_cache/surface_params.cpp @@ -113,10 +113,8 @@ SurfaceParams SurfaceParams::CreateForTexture(const FormatLookupTable& lookup_ta          params.height = tic.Height();          params.depth = tic.Depth();          params.pitch = params.is_tiled ? 0 : tic.Pitch(); -        if (params.target == SurfaceTarget::Texture2D && params.depth > 1) { -            params.depth = 1; -        } else if (params.target == SurfaceTarget::TextureCubemap || -                   params.target == SurfaceTarget::TextureCubeArray) { +        if (params.target == SurfaceTarget::TextureCubemap || +            params.target == SurfaceTarget::TextureCubeArray) {              params.depth *= 6;          }          params.num_levels = tic.max_mip_level + 1; @@ -309,28 +307,26 @@ std::size_t SurfaceParams::GetGuestMipmapLevelOffset(u32 level) const {      return offset;  } -std::size_t SurfaceParams::GetHostMipmapLevelOffset(u32 level) const { -    std::size_t offset = 0; -    for (u32 i = 0; i < level; i++) { -        offset += GetInnerMipmapMemorySize(i, true, false) * GetNumLayers(); -    } -    return offset; -} - -std::size_t SurfaceParams::GetConvertedMipmapOffset(u32 level) const { +std::size_t SurfaceParams::GetHostMipmapLevelOffset(u32 level, bool is_converted) const {      std::size_t offset = 0; -    for (u32 i = 0; i < level; i++) { -        offset += GetConvertedMipmapSize(i); +    if (is_converted) { +        for (u32 i = 0; i < level; ++i) { +            offset += GetConvertedMipmapSize(i) * GetNumLayers(); +        } +    } else { +        for (u32 i = 0; i < level; ++i) { +            offset += GetInnerMipmapMemorySize(i, true, false) * GetNumLayers(); +        }      }      return offset;  }  std::size_t SurfaceParams::GetConvertedMipmapSize(u32 level) const {      constexpr std::size_t rgba8_bpp = 4ULL; -    const std::size_t width_t = GetMipWidth(level); -    const std::size_t height_t = GetMipHeight(level); -    const std::size_t depth_t = is_layered ? depth : GetMipDepth(level); -    return width_t * height_t * depth_t * rgba8_bpp; +    const std::size_t mip_width = GetMipWidth(level); +    const std::size_t mip_height = GetMipHeight(level); +    const std::size_t mip_depth = is_layered ? 1 : GetMipDepth(level); +    return mip_width * mip_height * mip_depth * rgba8_bpp;  }  std::size_t SurfaceParams::GetLayerSize(bool as_host_size, bool uncompressed) const { diff --git a/src/video_core/texture_cache/surface_params.h b/src/video_core/texture_cache/surface_params.h index 995cc3818..24957df8d 100644 --- a/src/video_core/texture_cache/surface_params.h +++ b/src/video_core/texture_cache/surface_params.h @@ -20,8 +20,6 @@ namespace VideoCommon {  class FormatLookupTable; -using VideoCore::Surface::SurfaceCompression; -  class SurfaceParams {  public:      /// Creates SurfaceCachedParams from a texture configuration. @@ -67,16 +65,14 @@ public:          return GetInnerMemorySize(false, false, false);      } -    std::size_t GetHostSizeInBytes() const { -        std::size_t host_size_in_bytes; -        if (GetCompressionType() == SurfaceCompression::Converted) { -            // ASTC is uncompressed in software, in emulated as RGBA8 -            host_size_in_bytes = 0; -            for (u32 level = 0; level < num_levels; ++level) { -                host_size_in_bytes += GetConvertedMipmapSize(level); -            } -        } else { -            host_size_in_bytes = GetInnerMemorySize(true, false, false); +    std::size_t GetHostSizeInBytes(bool is_converted) const { +        if (!is_converted) { +            return GetInnerMemorySize(true, false, false); +        } +        // ASTC is uncompressed in software, in emulated as RGBA8 +        std::size_t host_size_in_bytes = 0; +        for (u32 level = 0; level < num_levels; ++level) { +            host_size_in_bytes += GetConvertedMipmapSize(level) * GetNumLayers();          }          return host_size_in_bytes;      } @@ -107,9 +103,8 @@ public:      u32 GetMipBlockDepth(u32 level) const;      /// Returns the best possible row/pitch alignment for the surface. -    u32 GetRowAlignment(u32 level) const { -        const u32 bpp = -            GetCompressionType() == SurfaceCompression::Converted ? 4 : GetBytesPerPixel(); +    u32 GetRowAlignment(u32 level, bool is_converted) const { +        const u32 bpp = is_converted ? 4 : GetBytesPerPixel();          return 1U << Common::CountTrailingZeroes32(GetMipWidth(level) * bpp);      } @@ -117,11 +112,7 @@ public:      std::size_t GetGuestMipmapLevelOffset(u32 level) const;      /// Returns the offset in bytes in host memory (linear) of a given mipmap level. -    std::size_t GetHostMipmapLevelOffset(u32 level) const; - -    /// Returns the offset in bytes in host memory (linear) of a given mipmap level -    /// for a texture that is converted in host gpu. -    std::size_t GetConvertedMipmapOffset(u32 level) const; +    std::size_t GetHostMipmapLevelOffset(u32 level, bool is_converted) const;      /// Returns the size in bytes in guest memory of a given mipmap level.      std::size_t GetGuestMipmapSize(u32 level) const { @@ -196,11 +187,6 @@ public:                 pixel_format < VideoCore::Surface::PixelFormat::MaxDepthStencilFormat;      } -    /// Returns how the compression should be handled for this texture. -    SurfaceCompression GetCompressionType() const { -        return VideoCore::Surface::GetFormatCompressionType(pixel_format); -    } -      /// Returns is the surface is a TextureBuffer type of surface.      bool IsBuffer() const {          return target == VideoCore::Surface::SurfaceTarget::TextureBuffer; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 6cdbe63d0..c8f8d659d 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -289,8 +289,9 @@ public:      }  protected: -    TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer) -        : system{system}, rasterizer{rasterizer} { +    explicit TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer, +                          bool is_astc_supported) +        : system{system}, is_astc_supported{is_astc_supported}, rasterizer{rasterizer} {          for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {              SetEmptyColorBuffer(i);          } @@ -381,6 +382,7 @@ protected:      }      Core::System& system; +    const bool is_astc_supported;  private:      enum class RecycleStrategy : u32 { diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index a5f81a8a0..f60bdc60a 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -15,13 +15,13 @@  #endif  #include "video_core/video_core.h" -namespace VideoCore { - -std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, -                                             Core::System& system) { +namespace { +std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, +                                                        Core::System& system, +                                                        Core::Frontend::GraphicsContext& context) {      switch (Settings::values.renderer_backend) {      case Settings::RendererBackend::OpenGL: -        return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system); +        return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context);  #ifdef HAS_VULKAN      case Settings::RendererBackend::Vulkan:          return std::make_unique<Vulkan::RendererVulkan>(emu_window, system); @@ -30,13 +30,23 @@ std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_wind          return nullptr;      }  } +} // Anonymous namespace -std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) { -    if (Settings::values.use_asynchronous_gpu_emulation) { -        return std::make_unique<VideoCommon::GPUAsynch>(system, system.Renderer()); +namespace VideoCore { + +std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) { +    auto context = emu_window.CreateSharedContext(); +    const auto scope = context->Acquire(); +    auto renderer = CreateRenderer(emu_window, system, *context); +    if (!renderer->Init()) { +        return nullptr;      } -    return std::make_unique<VideoCommon::GPUSynch>(system, system.Renderer()); +    if (Settings::values.use_asynchronous_gpu_emulation) { +        return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer), +                                                        std::move(context)); +    } +    return std::make_unique<VideoCommon::GPUSynch>(system, std::move(renderer), std::move(context));  }  u16 GetResolutionScaleFactor(const RendererBase& renderer) { diff --git a/src/video_core/video_core.h b/src/video_core/video_core.h index b8e0ac372..f5c27125d 100644 --- a/src/video_core/video_core.h +++ b/src/video_core/video_core.h @@ -22,17 +22,8 @@ namespace VideoCore {  class RendererBase; -/** - * Creates a renderer instance. - * - * @note The returned renderer instance is simply allocated. Its Init() - *       function still needs to be called to fully complete its setup. - */ -std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window, -                                             Core::System& system); -  /// Creates an emulated GPU instance using the given system context. -std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system); +std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system);  u16 GetResolutionScaleFactor(const RendererBase& renderer); diff --git a/src/yuzu/about_dialog.cpp b/src/yuzu/about_dialog.cpp index d39b3f07a..695b2ef5f 100644 --- a/src/yuzu/about_dialog.cpp +++ b/src/yuzu/about_dialog.cpp @@ -3,15 +3,22 @@  // Refer to the license.txt file included.  #include <QIcon> +#include <fmt/format.h>  #include "common/scm_rev.h"  #include "ui_aboutdialog.h"  #include "yuzu/about_dialog.h"  AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent), ui(new Ui::AboutDialog) { +    const auto build_id = std::string(Common::g_build_id); +    const auto fmt = std::string(Common::g_title_bar_format_idle); +    const auto yuzu_build_version = +        fmt::format(fmt.empty() ? "yuzu Development Build" : fmt, std::string{}, std::string{}, +                    std::string{}, std::string{}, std::string{}, build_id); +      ui->setupUi(this);      ui->labelLogo->setPixmap(QIcon::fromTheme(QStringLiteral("yuzu")).pixmap(200));      ui->labelBuildInfo->setText(ui->labelBuildInfo->text().arg( -        QString::fromUtf8(Common::g_build_fullname), QString::fromUtf8(Common::g_scm_branch), +        QString::fromStdString(yuzu_build_version), QString::fromUtf8(Common::g_scm_branch),          QString::fromUtf8(Common::g_scm_desc), QString::fromUtf8(Common::g_build_date).left(10)));  } diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index c3dbb1a88..eaded2640 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -10,9 +10,6 @@  #include <QMessageBox>  #include <QOffscreenSurface>  #include <QOpenGLContext> -#include <QOpenGLFunctions> -#include <QOpenGLFunctions_4_3_Core> -#include <QOpenGLWindow>  #include <QPainter>  #include <QScreen>  #include <QStringList> @@ -29,7 +26,6 @@  #include "common/scope_exit.h"  #include "core/core.h"  #include "core/frontend/framebuffer_layout.h" -#include "core/frontend/scope_acquire_context.h"  #include "core/settings.h"  #include "input_common/keyboard.h"  #include "input_common/main.h" @@ -39,26 +35,16 @@  #include "yuzu/bootmanager.h"  #include "yuzu/main.h" -EmuThread::EmuThread(GRenderWindow& window) -    : shared_context{window.CreateSharedContext()}, -      context{(Settings::values.use_asynchronous_gpu_emulation && shared_context) ? *shared_context -                                                                                  : window} {} +EmuThread::EmuThread() = default;  EmuThread::~EmuThread() = default; -static GMainWindow* GetMainWindow() { -    for (QWidget* w : qApp->topLevelWidgets()) { -        if (GMainWindow* main = qobject_cast<GMainWindow*>(w)) { -            return main; -        } -    } -    return nullptr; -} -  void EmuThread::run() {      MicroProfileOnThreadCreate("EmuThread"); -    Core::Frontend::ScopeAcquireContext acquire_context{context}; +    // Main process has been loaded. Make the context current to this thread and begin GPU and CPU +    // execution. +    Core::System::GetInstance().GPU().Start();      emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0); @@ -111,162 +97,156 @@ void EmuThread::run() {  #endif  } -class GGLContext : public Core::Frontend::GraphicsContext { +class OpenGLSharedContext : public Core::Frontend::GraphicsContext {  public: -    explicit GGLContext(QOpenGLContext* shared_context) -        : context(new QOpenGLContext(shared_context->parent())), -          surface(new QOffscreenSurface(nullptr)) { +    /// Create the original context that should be shared from +    explicit OpenGLSharedContext(QSurface* surface) : surface(surface) { +        QSurfaceFormat format; +        format.setVersion(4, 3); +        format.setProfile(QSurfaceFormat::CompatibilityProfile); +        format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); +        // TODO: expose a setting for buffer value (ie default/single/double/triple) +        format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); +        format.setSwapInterval(0); + +        context = std::make_unique<QOpenGLContext>(); +        context->setFormat(format); +        if (!context->create()) { +            LOG_ERROR(Frontend, "Unable to create main openGL context"); +        } +    } + +    /// Create the shared contexts for rendering and presentation +    explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface = nullptr) {          // disable vsync for any shared contexts -        auto format = shared_context->format(); -        format.setSwapInterval(0); +        auto format = share_context->format(); +        format.setSwapInterval(main_surface ? Settings::values.use_vsync : 0); -        context->setShareContext(shared_context); +        context = std::make_unique<QOpenGLContext>(); +        context->setShareContext(share_context);          context->setFormat(format); -        context->create(); -        surface->setParent(shared_context->parent()); -        surface->setFormat(format); -        surface->create(); +        if (!context->create()) { +            LOG_ERROR(Frontend, "Unable to create shared openGL context"); +        } + +        if (!main_surface) { +            offscreen_surface = std::make_unique<QOffscreenSurface>(nullptr); +            offscreen_surface->setFormat(format); +            offscreen_surface->create(); +            surface = offscreen_surface.get(); +        } else { +            surface = main_surface; +        } +    } + +    ~OpenGLSharedContext() { +        DoneCurrent(); +    } + +    void SwapBuffers() override { +        context->swapBuffers(surface);      }      void MakeCurrent() override { -        context->makeCurrent(surface); +        if (is_current) { +            return; +        } +        is_current = context->makeCurrent(surface);      }      void DoneCurrent() override { +        if (!is_current) { +            return; +        }          context->doneCurrent(); +        is_current = false;      } -private: -    QOpenGLContext* context; -    QOffscreenSurface* surface; -}; - -class ChildRenderWindow : public QWindow { -public: -    ChildRenderWindow(QWindow* parent, QWidget* event_handler) -        : QWindow{parent}, event_handler{event_handler} {} - -    virtual ~ChildRenderWindow() = default; - -    virtual void Present() = 0; - -protected: -    bool event(QEvent* event) override { -        switch (event->type()) { -        case QEvent::UpdateRequest: -            Present(); -            return true; -        case QEvent::MouseButtonPress: -        case QEvent::MouseButtonRelease: -        case QEvent::MouseButtonDblClick: -        case QEvent::MouseMove: -        case QEvent::KeyPress: -        case QEvent::KeyRelease: -        case QEvent::FocusIn: -        case QEvent::FocusOut: -        case QEvent::FocusAboutToChange: -        case QEvent::Enter: -        case QEvent::Leave: -        case QEvent::Wheel: -        case QEvent::TabletMove: -        case QEvent::TabletPress: -        case QEvent::TabletRelease: -        case QEvent::TabletEnterProximity: -        case QEvent::TabletLeaveProximity: -        case QEvent::TouchBegin: -        case QEvent::TouchUpdate: -        case QEvent::TouchEnd: -        case QEvent::InputMethodQuery: -        case QEvent::TouchCancel: -            return QCoreApplication::sendEvent(event_handler, event); -        case QEvent::Drop: -            GetMainWindow()->DropAction(static_cast<QDropEvent*>(event)); -            return true; -        case QEvent::DragResponse: -        case QEvent::DragEnter: -        case QEvent::DragLeave: -        case QEvent::DragMove: -            GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event)); -            return true; -        default: -            return QWindow::event(event); -        } +    QOpenGLContext* GetShareContext() { +        return context.get();      } -    void exposeEvent(QExposeEvent* event) override { -        QWindow::requestUpdate(); -        QWindow::exposeEvent(event); +    const QOpenGLContext* GetShareContext() const { +        return context.get();      }  private: -    QWidget* event_handler{}; +    // Avoid using Qt parent system here since we might move the QObjects to new threads +    // As a note, this means we should avoid using slots/signals with the objects too +    std::unique_ptr<QOpenGLContext> context; +    std::unique_ptr<QOffscreenSurface> offscreen_surface{}; +    QSurface* surface; +    bool is_current = false;  }; -class OpenGLWindow final : public ChildRenderWindow { +class DummyContext : public Core::Frontend::GraphicsContext {}; + +class RenderWidget : public QWidget {  public: -    OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context) -        : ChildRenderWindow{parent, event_handler}, -          context(new QOpenGLContext(shared_context->parent())) { +    explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) { +        setAttribute(Qt::WA_NativeWindow); +        setAttribute(Qt::WA_PaintOnScreen); +    } -        // disable vsync for any shared contexts -        auto format = shared_context->format(); -        format.setSwapInterval(Settings::values.use_vsync ? 1 : 0); -        this->setFormat(format); +    virtual ~RenderWidget() = default; -        context->setShareContext(shared_context); -        context->setScreen(this->screen()); -        context->setFormat(format); -        context->create(); +    /// Called on the UI thread when this Widget is ready to draw +    /// Dervied classes can override this to draw the latest frame. +    virtual void Present() {} -        setSurfaceType(QWindow::OpenGLSurface); +    void paintEvent(QPaintEvent* event) override { +        Present(); +        update(); +    } -        // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground, -        // WA_DontShowOnScreen, WA_DeleteOnClose +    QPaintEngine* paintEngine() const override { +        return nullptr;      } -    ~OpenGLWindow() override { -        context->doneCurrent(); +private: +    GRenderWindow* render_window; +}; + +class OpenGLRenderWidget : public RenderWidget { +public: +    explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) { +        windowHandle()->setSurfaceType(QWindow::OpenGLSurface); +    } + +    void SetContext(std::unique_ptr<Core::Frontend::GraphicsContext>&& context_) { +        context = std::move(context_);      }      void Present() override { -        if (!isExposed()) { +        if (!isVisible()) {              return;          } -        context->makeCurrent(this); -        Core::System::GetInstance().Renderer().TryPresent(100); -        context->swapBuffers(this); -        auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>(); -        f->glFinish(); -        QWindow::requestUpdate(); +        context->MakeCurrent(); +        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); +        if (Core::System::GetInstance().Renderer().TryPresent(100)) { +            context->SwapBuffers(); +            glFinish(); +        }      }  private: -    QOpenGLContext* context{}; +    std::unique_ptr<Core::Frontend::GraphicsContext> context{};  };  #ifdef HAS_VULKAN -class VulkanWindow final : public ChildRenderWindow { +class VulkanRenderWidget : public RenderWidget {  public: -    VulkanWindow(QWindow* parent, QWidget* event_handler, QVulkanInstance* instance) -        : ChildRenderWindow{parent, event_handler} { -        setSurfaceType(QSurface::SurfaceType::VulkanSurface); -        setVulkanInstance(instance); -    } - -    ~VulkanWindow() override = default; - -    void Present() override { -        // TODO(bunnei): ImplementMe +    explicit VulkanRenderWidget(GRenderWindow* parent, QVulkanInstance* instance) +        : RenderWidget(parent) { +        windowHandle()->setSurfaceType(QWindow::VulkanSurface); +        windowHandle()->setVulkanInstance(instance);      } - -private: -    QWidget* event_handler{};  };  #endif -GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread) +GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread)      : QWidget(parent_), emu_thread(emu_thread) {      setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")                         .arg(QString::fromUtf8(Common::g_build_name), @@ -278,26 +258,13 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)      setLayout(layout);      InputCommon::Init(); -    GMainWindow* parent = GetMainWindow(); -    connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete); +    connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete);  }  GRenderWindow::~GRenderWindow() {      InputCommon::Shutdown();  } -void GRenderWindow::MakeCurrent() { -    if (core_context) { -        core_context->MakeCurrent(); -    } -} - -void GRenderWindow::DoneCurrent() { -    if (core_context) { -        core_context->DoneCurrent(); -    } -} -  void GRenderWindow::PollEvents() {      if (!first_frame) {          first_frame = true; @@ -309,21 +276,6 @@ bool GRenderWindow::IsShown() const {      return !isMinimized();  } -void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, -                                           void* surface) const { -#ifdef HAS_VULKAN -    const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr"); -    const VkInstance instance_copy = vk_instance->vkInstance(); -    const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_window); - -    std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); -    std::memcpy(instance, &instance_copy, sizeof(instance_copy)); -    std::memcpy(surface, &surface_copy, sizeof(surface_copy)); -#else -    UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan"); -#endif -} -  // On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).  //  // Older versions get the window size (density independent pixels), @@ -367,7 +319,7 @@ qreal GRenderWindow::windowPixelRatio() const {      return devicePixelRatio();  } -std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const { +std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF& pos) const {      const qreal pixel_ratio = windowPixelRatio();      return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),              static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))}; @@ -387,8 +339,10 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {  }  void GRenderWindow::mousePressEvent(QMouseEvent* event) { -    if (event->source() == Qt::MouseEventSynthesizedBySystem) -        return; // touch input is handled in TouchBeginEvent +    // touch input is handled in TouchBeginEvent +    if (event->source() == Qt::MouseEventSynthesizedBySystem) { +        return; +    }      auto pos = event->pos();      if (event->button() == Qt::LeftButton) { @@ -400,8 +354,10 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {  }  void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { -    if (event->source() == Qt::MouseEventSynthesizedBySystem) -        return; // touch input is handled in TouchUpdateEvent +    // touch input is handled in TouchUpdateEvent +    if (event->source() == Qt::MouseEventSynthesizedBySystem) { +        return; +    }      auto pos = event->pos();      const auto [x, y] = ScaleTouch(pos); @@ -410,13 +366,16 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {  }  void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) { -    if (event->source() == Qt::MouseEventSynthesizedBySystem) -        return; // touch input is handled in TouchEndEvent +    // touch input is handled in TouchEndEvent +    if (event->source() == Qt::MouseEventSynthesizedBySystem) { +        return; +    } -    if (event->button() == Qt::LeftButton) +    if (event->button() == Qt::LeftButton) {          this->TouchReleased(); -    else if (event->button() == Qt::RightButton) +    } else if (event->button() == Qt::RightButton) {          InputCommon::GetMotionEmu()->EndTilt(); +    }  }  void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) { @@ -474,9 +433,13 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {  std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {      if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) { -        return std::make_unique<GGLContext>(QOpenGLContext::globalShareContext()); +        auto c = static_cast<OpenGLSharedContext*>(main_context.get()); +        // Bind the shared contexts to the main surface in case the backend wants to take over +        // presentation +        return std::make_unique<OpenGLSharedContext>(c->GetShareContext(), +                                                     child_widget->windowHandle());      } -    return {}; +    return std::make_unique<DummyContext>();  }  bool GRenderWindow::InitRenderTarget() { @@ -497,14 +460,11 @@ bool GRenderWindow::InitRenderTarget() {          break;      } +    child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); +    layout()->addWidget(child_widget);      // Reset minimum required size to avoid resizing issues on the main window after restarting.      setMinimumSize(1, 1); -    // Show causes the window to actually be created and the gl context as well, but we don't want -    // the widget to be shown yet, so immediately hide it. -    show(); -    hide(); -      resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);      OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size); @@ -523,9 +483,10 @@ bool GRenderWindow::InitRenderTarget() {  void GRenderWindow::ReleaseRenderTarget() {      if (child_widget) {          layout()->removeWidget(child_widget); -        delete child_widget; +        child_widget->deleteLater();          child_widget = nullptr;      } +    main_context.reset();  }  void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) { @@ -557,24 +518,13 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal  bool GRenderWindow::InitializeOpenGL() {      // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,      // WA_DontShowOnScreen, WA_DeleteOnClose -    QSurfaceFormat fmt; -    fmt.setVersion(4, 3); -    fmt.setProfile(QSurfaceFormat::CompatibilityProfile); -    fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions); -    // TODO: expose a setting for buffer value (ie default/single/double/triple) -    fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior); -    fmt.setSwapInterval(0); -    QSurfaceFormat::setDefaultFormat(fmt); - -    GMainWindow* parent = GetMainWindow(); -    QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; -    child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext()); -    child_window->create(); -    child_widget = createWindowContainer(child_window, this); -    child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); -    layout()->addWidget(child_widget); - -    core_context = CreateSharedContext(); +    auto child = new OpenGLRenderWidget(this); +    child_widget = child; +    child_widget->windowHandle()->create(); +    auto context = std::make_shared<OpenGLSharedContext>(child->windowHandle()); +    main_context = context; +    child->SetContext( +        std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->windowHandle()));      return true;  } @@ -604,13 +554,10 @@ bool GRenderWindow::InitializeVulkan() {          return false;      } -    GMainWindow* parent = GetMainWindow(); -    QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr; -    child_window = new VulkanWindow(parent_win_handle, this, vk_instance.get()); -    child_window->create(); -    child_widget = createWindowContainer(child_window, this); -    child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height); -    layout()->addWidget(child_widget); +    auto child = new VulkanRenderWidget(this, vk_instance.get()); +    child_widget = child; +    child_widget->windowHandle()->create(); +    main_context = std::make_unique<DummyContext>();      return true;  #else @@ -620,8 +567,24 @@ bool GRenderWindow::InitializeVulkan() {  #endif  } +void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, +                                           void* surface) const { +#ifdef HAS_VULKAN +    const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr"); +    const VkInstance instance_copy = vk_instance->vkInstance(); +    const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_widget->windowHandle()); + +    std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr)); +    std::memcpy(instance, &instance_copy, sizeof(instance_copy)); +    std::memcpy(surface, &surface_copy, sizeof(surface_copy)); +#else +    UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan"); +#endif +} +  bool GRenderWindow::LoadOpenGL() { -    Core::Frontend::ScopeAcquireContext acquire_context{*this}; +    auto context = CreateSharedContext(); +    auto scope = context->Acquire();      if (!gladLoadGL()) {          QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"),                                tr("Your GPU may not support OpenGL 4.3, or you do not have the " diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 79b030304..d69078df1 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -18,12 +18,10 @@  #include "core/frontend/emu_window.h"  class GRenderWindow; +class GMainWindow;  class QKeyEvent; -class QScreen;  class QTouchEvent;  class QStringList; -class QSurface; -class QOpenGLContext;  #ifdef HAS_VULKAN  class QVulkanInstance;  #endif @@ -36,7 +34,7 @@ class EmuThread final : public QThread {      Q_OBJECT  public: -    explicit EmuThread(GRenderWindow& window); +    explicit EmuThread();      ~EmuThread() override;      /** @@ -90,12 +88,6 @@ private:      std::mutex running_mutex;      std::condition_variable running_cv; -    /// Only used in asynchronous GPU mode -    std::unique_ptr<Core::Frontend::GraphicsContext> shared_context; - -    /// This is shared_context in asynchronous GPU mode, core_context in synchronous GPU mode -    Core::Frontend::GraphicsContext& context; -  signals:      /**       * Emitted when the CPU has halted execution @@ -124,12 +116,10 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {      Q_OBJECT  public: -    GRenderWindow(QWidget* parent, EmuThread* emu_thread); +    GRenderWindow(GMainWindow* parent, EmuThread* emu_thread);      ~GRenderWindow() override;      // EmuWindow implementation. -    void MakeCurrent() override; -    void DoneCurrent() override;      void PollEvents() override;      bool IsShown() const override;      void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance, @@ -165,6 +155,8 @@ public:      void CaptureScreenshot(u32 res_scale, const QString& screenshot_path); +    std::pair<u32, u32> ScaleTouch(const QPointF& pos) const; +  public slots:      void OnEmulationStarting(EmuThread* emu_thread);      void OnEmulationStopping(); @@ -176,7 +168,6 @@ signals:      void FirstFrameDisplayed();  private: -    std::pair<u32, u32> ScaleTouch(QPointF pos) const;      void TouchBeginEvent(const QTouchEvent* event);      void TouchUpdateEvent(const QTouchEvent* event);      void TouchEndEvent(); @@ -190,7 +181,10 @@ private:      EmuThread* emu_thread; -    std::unique_ptr<GraphicsContext> core_context; +    // Main context that will be shared with all other contexts that are requested. +    // If this is used in a shared context setting, then this should not be used directly, but +    // should instead be shared from +    std::shared_ptr<Core::Frontend::GraphicsContext> main_context;  #ifdef HAS_VULKAN      std::unique_ptr<QVulkanInstance> vk_instance; @@ -201,12 +195,6 @@ private:      QByteArray geometry; -    /// Native window handle that backs this presentation widget -    QWindow* child_window = nullptr; - -    /// In order to embed the window into GRenderWindow, you need to use createWindowContainer to -    /// put the child_window into a widget then add it to the layout. This child_widget can be -    /// parented to GRenderWindow and use Qt's lifetime system      QWidget* child_widget = nullptr;      bool first_frame = false; diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui index 1556481d0..c3a1b68f0 100644 --- a/src/yuzu/configuration/configure_input_player.ui +++ b/src/yuzu/configuration/configure_input_player.ui @@ -839,21 +839,21 @@          <bool>false</bool>         </property>         <layout class="QGridLayout" name="gridLayout_3"> -        <item row="3" column="0"> -         <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout"> +        <item row="0" column="0"> +         <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">            <item> -           <layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout"> +           <layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout">              <item> -             <widget class="QLabel" name="labelSL"> +             <widget class="QLabel" name="labelL">                <property name="text"> -               <string>SL:</string> +               <string>L:</string>                </property>               </widget>              </item>             </layout>            </item>            <item> -           <widget class="QPushButton" name="buttonSL"> +           <widget class="QPushButton" name="buttonL">              <property name="text">               <string/>              </property> @@ -861,21 +861,21 @@            </item>           </layout>          </item> -        <item row="2" column="1"> -         <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout"> +        <item row="0" column="1"> +         <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout">            <item> -           <layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout"> +           <layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout">              <item> -             <widget class="QLabel" name="labelZR"> +             <widget class="QLabel" name="labelR">                <property name="text"> -               <string>ZR:</string> +               <string>R:</string>                </property>               </widget>              </item>             </layout>            </item>            <item> -           <widget class="QPushButton" name="buttonZR"> +           <widget class="QPushButton" name="buttonR">              <property name="text">               <string/>              </property> @@ -883,21 +883,21 @@            </item>           </layout>          </item> -        <item row="3" column="1"> -         <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout"> +        <item row="1" column="0"> +         <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">            <item> -           <layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout"> +           <layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout">              <item> -             <widget class="QLabel" name="labelSR"> +             <widget class="QLabel" name="labelZL">                <property name="text"> -               <string>SR:</string> +               <string>ZL:</string>                </property>               </widget>              </item>             </layout>            </item>            <item> -           <widget class="QPushButton" name="buttonSR"> +           <widget class="QPushButton" name="buttonZL">              <property name="text">               <string/>              </property> @@ -905,21 +905,21 @@            </item>           </layout>          </item> -        <item row="0" column="1"> -         <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout"> +        <item row="1" column="1"> +         <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">            <item> -           <layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout"> +           <layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout">              <item> -             <widget class="QLabel" name="labelZL"> +             <widget class="QLabel" name="labelZR">                <property name="text"> -               <string>ZL:</string> +               <string>ZR:</string>                </property>               </widget>              </item>             </layout>            </item>            <item> -           <widget class="QPushButton" name="buttonZL"> +           <widget class="QPushButton" name="buttonZR">              <property name="text">               <string/>              </property> @@ -927,21 +927,21 @@            </item>           </layout>          </item> -        <item row="0" column="0"> -         <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout"> +        <item row="2" column="0"> +         <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">            <item> -           <layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout"> +           <layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout">              <item> -             <widget class="QLabel" name="labelL"> +             <widget class="QLabel" name="labelSL">                <property name="text"> -               <string>L:</string> +               <string>SL:</string>                </property>               </widget>              </item>             </layout>            </item>            <item> -           <widget class="QPushButton" name="buttonL"> +           <widget class="QPushButton" name="buttonSL">              <property name="text">               <string/>              </property> @@ -949,21 +949,21 @@            </item>           </layout>          </item> -        <item row="2" column="0"> -         <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout"> +        <item row="2" column="1"> +         <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">            <item> -           <layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout"> +           <layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout">              <item> -             <widget class="QLabel" name="labelR"> +             <widget class="QLabel" name="labelSR">                <property name="text"> -               <string>R:</string> +               <string>SR:</string>                </property>               </widget>              </item>             </layout>            </item>            <item> -           <widget class="QPushButton" name="buttonR"> +           <widget class="QPushButton" name="buttonSR">              <property name="text">               <string/>              </property> diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4769a612e..1717e06f9 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -205,7 +205,13 @@ GMainWindow::GMainWindow()      ConnectMenuEvents();      ConnectWidgetEvents(); -    LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch, +    const auto build_id = std::string(Common::g_build_id); +    const auto fmt = std::string(Common::g_title_bar_format_idle); +    const auto yuzu_build_version = +        fmt::format(fmt.empty() ? "yuzu Development Build" : fmt, std::string{}, std::string{}, +                    std::string{}, std::string{}, std::string{}, build_id); + +    LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", yuzu_build_version, Common::g_scm_branch,               Common::g_scm_desc);  #ifdef ARCHITECTURE_x86_64      LOG_INFO(Frontend, "Host CPU: {}", Common::GetCPUCaps().cpu_string); @@ -984,7 +990,7 @@ void GMainWindow::BootGame(const QString& filename) {          return;      // Create and start the emulation thread -    emu_thread = std::make_unique<EmuThread>(*render_window); +    emu_thread = std::make_unique<EmuThread>();      emit EmulationStarting(emu_thread.get());      emu_thread->start(); @@ -1034,6 +1040,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 +1812,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(); @@ -2370,7 +2384,6 @@ int main(int argc, char* argv[]) {      // Enables the core to make the qt created contexts current on std::threads      QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); -    QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);      QApplication app(argc, argv);      // Qt changes the locale and causes issues in float conversion using std::to_string() when diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp index c0d373477..3522dcf6d 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp @@ -37,16 +37,24 @@ public:      }      void MakeCurrent() override { -        SDL_GL_MakeCurrent(window, context); +        if (is_current) { +            return; +        } +        is_current = SDL_GL_MakeCurrent(window, context) == 0;      }      void DoneCurrent() override { +        if (!is_current) { +            return; +        }          SDL_GL_MakeCurrent(window, nullptr); +        is_current = false;      }  private:      SDL_Window* window;      SDL_GLContext context; +    bool is_current = false;  };  bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() { @@ -148,14 +156,6 @@ EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() {      SDL_GL_DeleteContext(window_context);  } -void EmuWindow_SDL2_GL::MakeCurrent() { -    core_context->MakeCurrent(); -} - -void EmuWindow_SDL2_GL::DoneCurrent() { -    core_context->DoneCurrent(); -} -  void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,                                                 void* surface) const {      // Should not have been called from OpenGL diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h index b80669ff0..e092021d7 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h @@ -13,8 +13,6 @@ public:      explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen);      ~EmuWindow_SDL2_GL(); -    void MakeCurrent() override; -    void DoneCurrent() override;      void Present() override;      /// Ignored in OpenGL diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp index abcc58165..46d053f04 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp @@ -111,14 +111,6 @@ EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() {      vkDestroyInstance(vk_instance, nullptr);  } -void EmuWindow_SDL2_VK::MakeCurrent() { -    // Unused on Vulkan -} - -void EmuWindow_SDL2_VK::DoneCurrent() { -    // Unused on Vulkan -} -  void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,                                                 void* surface) const {      const auto instance_proc_addr = vkGetInstanceProcAddr; diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h index 1eb8c0868..3dd1f3f61 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h @@ -13,8 +13,6 @@ public:      explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen);      ~EmuWindow_SDL2_VK(); -    void MakeCurrent() override; -    void DoneCurrent() override;      void Present() override;      void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,                                  void* surface) const override; diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index babf4c3a4..4d2ea7e9e 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp @@ -230,17 +230,10 @@ int main(int argc, char** argv) {      system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL"); -    system.Renderer().Rasterizer().LoadDiskResources(); +    // Core is loaded, start the GPU (makes the GPU contexts current to this thread) +    system.GPU().Start(); -    // Acquire render context for duration of the thread if this is the rendering thread -    if (!Settings::values.use_asynchronous_gpu_emulation) { -        emu_window->MakeCurrent(); -    } -    SCOPE_EXIT({ -        if (!Settings::values.use_asynchronous_gpu_emulation) { -            emu_window->DoneCurrent(); -        } -    }); +    system.Renderer().Rasterizer().LoadDiskResources();      std::thread render_thread([&emu_window] { emu_window->Present(); });      while (emu_window->IsOpen()) { diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp index a1bdb1a12..a837430cc 100644 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp @@ -102,8 +102,6 @@ EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() {      LOG_INFO(Frontend, "yuzu-tester Version: {} | {}-{}", Common::g_build_fullname,               Common::g_scm_branch, Common::g_scm_desc);      Settings::LogSettings(); - -    DoneCurrent();  }  EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() { @@ -114,14 +112,6 @@ EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {  void EmuWindow_SDL2_Hide::PollEvents() {} -void EmuWindow_SDL2_Hide::MakeCurrent() { -    SDL_GL_MakeCurrent(render_window, gl_context); -} - -void EmuWindow_SDL2_Hide::DoneCurrent() { -    SDL_GL_MakeCurrent(render_window, nullptr); -} -  bool EmuWindow_SDL2_Hide::IsShown() const {      return false;  } @@ -129,3 +119,35 @@ bool EmuWindow_SDL2_Hide::IsShown() const {  void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const {      UNREACHABLE();  } + +class SDLGLContext : public Core::Frontend::GraphicsContext { +public: +    explicit SDLGLContext() { +        // create a hidden window to make the shared context against +        window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0, +                                  SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); +        context = SDL_GL_CreateContext(window); +    } + +    ~SDLGLContext() { +        DoneCurrent(); +        SDL_GL_DeleteContext(context); +        SDL_DestroyWindow(window); +    } + +    void MakeCurrent() override { +        SDL_GL_MakeCurrent(window, context); +    } + +    void DoneCurrent() override { +        SDL_GL_MakeCurrent(window, nullptr); +    } + +private: +    SDL_Window* window; +    SDL_GLContext context; +}; + +std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_Hide::CreateSharedContext() const { +    return std::make_unique<SDLGLContext>(); +} diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h index b13e15309..9f5d04fca 100644 --- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h +++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h @@ -16,12 +16,6 @@ public:      /// Polls window events      void PollEvents() override; -    /// Makes the graphics context current for the caller thread -    void MakeCurrent() override; - -    /// Releases the GL context from the caller thread -    void DoneCurrent() override; -      /// Whether the screen is being shown or not.      bool IsShown() const override; @@ -29,8 +23,7 @@ public:      void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,                                  void* surface) const override; -    /// Whether the window is still open, and a close request hasn't yet been sent -    bool IsOpen() const; +    std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;  private:      /// Whether the GPU and driver supports the OpenGL extension required diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp index 94ad50cb3..676e70ebd 100644 --- a/src/yuzu_tester/yuzu.cpp +++ b/src/yuzu_tester/yuzu.cpp @@ -164,11 +164,6 @@ int main(int argc, char** argv) {      std::unique_ptr<EmuWindow_SDL2_Hide> emu_window{std::make_unique<EmuWindow_SDL2_Hide>()}; -    if (!Settings::values.use_multi_core) { -        // Single core mode must acquire OpenGL context for entire emulation session -        emu_window->MakeCurrent(); -    } -      bool finished = false;      int return_value = 0;      const auto callback = [&finished, @@ -257,6 +252,7 @@ int main(int argc, char** argv) {      system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester"); +    system.GPU().Start();      system.Renderer().Rasterizer().LoadDiskResources();      while (!finished) { | 
