Compare commits

..

1 Commits

Author SHA1 Message Date
Vitor K
bd0c56c6e7 common: Port some changes from dolphin (#5127)
* IOFile: Make the move constructor and move assignment operator noexcept

Certain parts of the standard library try to determine whether or not a
transfer operation should either be a copy or a move. The prevalent notion
of move constructors/assignment operators is that they should not throw,
they simply move an already existing resource somewhere else.

This is typically done with 'std::move_if_noexcept'. Like the name says,
if a type's move constructor is noexcept, then the functions retrieves an
r-value reference (for move semantics), or an l-value (for copy semantics)
if it is not noexcept.

As IOFile deletes the copy constructor and copy assignment operators,
using IOFile with certain parts of the standard library can fail in
unexcepted ways (especially when used with various container
implementations). This prevents that.

* fix various instances of -1 being assigned to unsigned types

* do not assign in conditional statements

* File/IOFile: Check _tfopen_s properly

* common/file_util.cpp: address review comments

Co-authored-by: Lioncash <mathew1800@gmail.com>
Co-authored-by: Shawn Hoffman <godisgovernment@gmail.com>
Co-authored-by: Sepalani <sepalani@hotmail.fr>
2020-04-01 02:58:42 +02:00
75 changed files with 778 additions and 3011 deletions

6
dist/license.md vendored
View File

@@ -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 | 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/checked.png | Free for non-commercial use
qt_themes/default/icons/16x16/failed.png | Free for non-commercial use
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,6 +11,8 @@ 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 657 B

After

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 524 B

After

Width:  |  Height:  |  Size: 428 B

View File

@@ -343,8 +343,8 @@ The icons used in this project have the following licenses:
Icon Name | License | Origin/Author
--- | --- | ---
checked.png | CC BY-ND 3.0 | https://icons8.com
failed.png | CC BY-ND 3.0 | https://icons8.com
checked.png | Free for non-commercial use
failed.png | Free for non-commercial use
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

View File

@@ -189,7 +189,7 @@ struct UpdateDataHeader {
UpdateDataHeader() {}
explicit UpdateDataHeader(const AudioRendererParameter& config) {
revision = Common::MakeMagic('R', 'E', 'V', '8'); // 9.2.0 Revision
revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
behavior_size = 0xb0;
memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
voices_size = config.voice_count * 0x10;

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <array>
#include <limits>
#include <memory>
#include <sstream>
#include <unordered_map>
@@ -530,11 +531,11 @@ void CopyDir(const std::string& source_path, const std::string& dest_path) {
std::optional<std::string> GetCurrentDir() {
// Get the current working directory (getcwd uses malloc)
#ifdef _WIN32
wchar_t* dir;
if (!(dir = _wgetcwd(nullptr, 0))) {
wchar_t* dir = _wgetcwd(nullptr, 0);
if (!dir) {
#else
char* dir;
if (!(dir = getcwd(nullptr, 0))) {
char* dir = getcwd(nullptr, 0);
if (!dir) {
#endif
LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg());
return {};
@@ -918,19 +919,22 @@ void IOFile::Swap(IOFile& other) noexcept {
bool IOFile::Open(const std::string& filename, const char openmode[], int flags) {
Close();
bool m_good;
#ifdef _WIN32
if (flags != 0) {
m_file = _wfsopen(Common::UTF8ToUTF16W(filename).c_str(),
Common::UTF8ToUTF16W(openmode).c_str(), flags);
m_good = m_file != nullptr;
} else {
_wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(),
Common::UTF8ToUTF16W(openmode).c_str());
m_good = _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(),
Common::UTF8ToUTF16W(openmode).c_str()) == 0;
}
#else
m_file = fopen(filename.c_str(), openmode);
m_file = std::fopen(filename.c_str(), openmode);
m_good = m_file != nullptr;
#endif
return IsOpen();
return m_good;
}
bool IOFile::Close() {
@@ -956,7 +960,7 @@ u64 IOFile::Tell() const {
if (IsOpen())
return ftello(m_file);
return -1;
return std::numeric_limits<u64>::max();
}
bool IOFile::Flush() {

View File

@@ -28,11 +28,8 @@ namespace Common {
#ifdef _MSC_VER
// Sets the debugger-visible name of the current thread.
// Uses undocumented (actually, it is now documented) trick.
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp
// This is implemented much nicer in upcoming msvc++, see:
// http://msdn.microsoft.com/en-us/library/xcb2z8hs(VS.100).aspx
// Uses trick documented in:
// https://docs.microsoft.com/en-us/visualstudio/debugger/how-to-set-a-thread-name-in-native-code
void SetCurrentThreadName(const char* name) {
static const DWORD MS_VC_EXCEPTION = 0x406D1388;
@@ -47,7 +44,7 @@ void SetCurrentThreadName(const char* name) {
info.dwType = 0x1000;
info.szName = name;
info.dwThreadID = -1; // dwThreadID;
info.dwThreadID = std::numeric_limits<DWORD>::max();
info.dwFlags = 0;
__try {

View File

@@ -131,6 +131,8 @@ 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
@@ -285,18 +287,6 @@ 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

View File

@@ -24,6 +24,7 @@
#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"
@@ -167,12 +168,13 @@ struct System::Impl {
Service::Init(service_manager, system);
GDBStub::DeferStart();
interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
gpu_core = VideoCore::CreateGPU(emu_window, system);
if (!gpu_core) {
renderer = VideoCore::CreateRenderer(emu_window, system);
if (!renderer->Init()) {
return ResultStatus::ErrorVideoCore;
}
gpu_core->Renderer().Rasterizer().SetupDirtyFlags();
interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
gpu_core = VideoCore::CreateGPU(system);
renderer->Rasterizer().SetupDirtyFlags();
is_powered_on = true;
exit_lock = false;
@@ -184,6 +186,8 @@ 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);
@@ -212,6 +216,10 @@ 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();
@@ -269,6 +277,7 @@ struct System::Impl {
}
// Shutdown emulation session
renderer.reset();
GDBStub::Shutdown();
Service::Shutdown();
service_manager.reset();
@@ -344,6 +353,7 @@ 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;
@@ -526,11 +536,11 @@ const Core::Hardware::InterruptManager& System::InterruptManager() const {
}
VideoCore::RendererBase& System::Renderer() {
return impl->gpu_core->Renderer();
return *impl->renderer;
}
const VideoCore::RendererBase& System::Renderer() const {
return impl->gpu_core->Renderer();
return *impl->renderer;
}
Kernel::KernelCore& System::Kernel() {

View File

@@ -5,7 +5,6 @@
#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"
@@ -127,7 +126,7 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
return out->GetSubdirectories().front();
while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) {
if (Common::ToLower(out->GetSubdirectories().front()->GetName()) == "data" &&
if (out->GetSubdirectories().front()->GetName() == "data" &&
type == RomFSExtractionType::Truncated)
break;
out = out->GetSubdirectories().front();

View File

@@ -13,39 +13,19 @@
namespace Core::Frontend {
/**
* Represents a drawing context that supports graphics operations.
* 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
*/
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() {}
virtual void MakeCurrent() = 0;
/// Releases (dunno if this is the "right" word) the context from the caller thread
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};
}
virtual void DoneCurrent() = 0;
};
/**
@@ -66,7 +46,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 {
class EmuWindow : public GraphicsContext {
public:
/// Data structure to store emuwindow configuration
struct WindowConfig {
@@ -80,9 +60,17 @@ public:
virtual void PollEvents() = 0;
/**
* Returns a GraphicsContext that the frontend provides to be used for rendering.
* 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
*/
virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const = 0;
virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const {
return nullptr;
}
/// Returns if window is shown (not minimized)
virtual bool IsShown() const = 0;

View File

@@ -0,0 +1,18 @@
// 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

View File

@@ -0,0 +1,23 @@
// 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

View File

@@ -52,11 +52,6 @@ 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 {
@@ -610,11 +605,11 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system,
{30, nullptr, "GetHomeButtonReaderLockAccessor"},
{31, nullptr, "GetReaderLockAccessorEx"},
{40, nullptr, "GetCradleFwVersion"},
{50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"},
{51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"},
{50, nullptr, "IsVrModeEnabled"},
{51, nullptr, "SetVrModeEnabled"},
{52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"},
{53, nullptr, "BeginVrModeEx"},
{54, &ICommonStateGetter::EndVrModeEx, "EndVrModeEx"},
{54, nullptr, "EndVrModeEx"},
{55, nullptr, "IsInControllerFirmwareUpdateSection"},
{60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
{61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"},
@@ -677,30 +672,6 @@ 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>();
@@ -712,13 +683,6 @@ 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");

View File

@@ -182,10 +182,7 @@ 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);

View File

@@ -2,24 +2,168 @@
// 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

View File

@@ -4,83 +4,12 @@
#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

View File

@@ -1,78 +0,0 @@
// 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

View File

@@ -1,21 +0,0 @@
// 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

View File

@@ -1,75 +0,0 @@
// 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

View File

@@ -1,21 +0,0 @@
// 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

View File

@@ -1,40 +0,0 @@
// 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

View File

@@ -1,21 +0,0 @@
// 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

View File

@@ -1,26 +0,0 @@
// 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

View File

@@ -1,21 +0,0 @@
// 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

View File

@@ -1,22 +0,0 @@
// 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

View File

@@ -1,21 +0,0 @@
// 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

View File

@@ -1,76 +0,0 @@
// 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

View File

@@ -1,24 +0,0 @@
// 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

View File

@@ -235,7 +235,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{303, nullptr, "ActivateSevenSixAxisSensor"},
{304, nullptr, "StartSevenSixAxisSensor"},
{305, nullptr, "StopSevenSixAxisSensor"},
{306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"},
{306, nullptr, "InitializeSevenSixAxisSensor"},
{307, nullptr, "FinalizeSevenSixAxisSensor"},
{308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
{309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
@@ -853,13 +853,6 @@ 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"} {

View File

@@ -128,7 +128,6 @@ 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;

View File

@@ -342,27 +342,17 @@ public:
return;
}
// 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
.MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode)
.IsSuccess());
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::ModuleCodeData)
Kernel::MemoryState::ModuleCode)
.IsSuccess());
ASSERT(vm_manager.ReprotectRange(bss_address, bss_size, Kernel::VMAPermission::None)
.IsSuccess());

View File

@@ -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, &Time::CalculateSpanBetween, "CalculateSpanBetween"},
{501, nullptr, "CalculateSpanBetween"},
};
// clang-format on

View File

@@ -308,35 +308,6 @@ 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};

View File

@@ -32,7 +32,6 @@ 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:

View File

@@ -210,8 +210,6 @@ 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)

View File

@@ -231,6 +231,18 @@ 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,
};
@@ -989,7 +1001,7 @@ union Instruction {
} stg;
union {
BitField<52, 4, AtomicOp> operation;
BitField<52, 4, GlobalAtomicOp> operation;
BitField<49, 3, GlobalAtomicType> type;
BitField<28, 20, s64> offset;
} atom;

View File

@@ -7,7 +7,6 @@
#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"
@@ -17,15 +16,14 @@
#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, std::unique_ptr<VideoCore::RendererBase>&& renderer_, bool is_async)
: system{system}, renderer{std::move(renderer_)}, is_async{is_async} {
auto& rasterizer{renderer->Rasterizer()};
GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async)
: system{system}, renderer{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);
@@ -139,7 +137,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

View File

@@ -25,11 +25,8 @@ inline u8* FromCacheAddr(CacheAddr cache_addr) {
}
namespace Core {
namespace Frontend {
class EmuWindow;
}
class System;
} // namespace Core
}
namespace VideoCore {
class RendererBase;
@@ -132,8 +129,7 @@ class MemoryManager;
class GPU {
public:
explicit GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
bool is_async);
explicit GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async);
virtual ~GPU();
@@ -178,14 +174,6 @@ 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;
@@ -299,7 +287,7 @@ private:
protected:
std::unique_ptr<Tegra::DmaPusher> dma_pusher;
Core::System& system;
std::unique_ptr<VideoCore::RendererBase> renderer;
VideoCore::RendererBase& renderer;
private:
std::unique_ptr<Tegra::MemoryManager> memory_manager;

View File

@@ -10,16 +10,13 @@
namespace VideoCommon {
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(Core::System& system, VideoCore::RendererBase& renderer)
: GPU(system, renderer, true), gpu_thread{system} {}
GPUAsynch::~GPUAsynch() = default;
void GPUAsynch::Start() {
cpu_context->MakeCurrent();
gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher);
gpu_thread.StartThread(renderer, *dma_pusher);
}
void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {

View File

@@ -7,10 +7,6 @@
#include "video_core/gpu.h"
#include "video_core/gpu_thread.h"
namespace Core::Frontend {
class GraphicsContext;
}
namespace VideoCore {
class RendererBase;
} // namespace VideoCore
@@ -20,8 +16,7 @@ namespace VideoCommon {
/// Implementation of GPU interface that runs the GPU asynchronously
class GPUAsynch final : public Tegra::GPU {
public:
explicit GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
std::unique_ptr<Core::Frontend::GraphicsContext>&& context);
explicit GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer);
~GPUAsynch() override;
void Start() override;
@@ -37,8 +32,6 @@ protected:
private:
GPUThread::ThreadManager gpu_thread;
std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
std::unique_ptr<Core::Frontend::GraphicsContext> gpu_context;
};
} // namespace VideoCommon

View File

@@ -7,15 +7,12 @@
namespace VideoCommon {
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(Core::System& system, VideoCore::RendererBase& renderer)
: GPU(system, renderer, false) {}
GPUSynch::~GPUSynch() = default;
void GPUSynch::Start() {
context->MakeCurrent();
}
void GPUSynch::Start() {}
void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
dma_pusher->Push(std::move(entries));
@@ -23,19 +20,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

View File

@@ -6,10 +6,6 @@
#include "video_core/gpu.h"
namespace Core::Frontend {
class GraphicsContext;
}
namespace VideoCore {
class RendererBase;
} // namespace VideoCore
@@ -19,8 +15,7 @@ namespace VideoCommon {
/// Implementation of GPU interface that runs the GPU synchronously
class GPUSynch final : public Tegra::GPU {
public:
explicit GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
std::unique_ptr<Core::Frontend::GraphicsContext>&& context);
explicit GPUSynch(Core::System& system, VideoCore::RendererBase& renderer);
~GPUSynch() override;
void Start() override;
@@ -34,9 +29,6 @@ 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

View File

@@ -5,7 +5,7 @@
#include "common/assert.h"
#include "common/microprofile.h"
#include "core/core.h"
#include "core/frontend/emu_window.h"
#include "core/frontend/scope_acquire_context.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, Core::Frontend::GraphicsContext& context,
Tegra::DmaPusher& dma_pusher, SynchState& state) {
static void RunThread(VideoCore::RendererBase& renderer, 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, Core::Frontend::Graphic
return;
}
auto current_context = context.Acquire();
Core::Frontend::ScopeAcquireContext acquire_context{renderer.GetRenderWindow()};
CommandDataContainer next;
while (state.is_running) {
@@ -62,11 +62,8 @@ ThreadManager::~ThreadManager() {
thread.join();
}
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::StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) {
thread = std::thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)};
}
void ThreadManager::SubmitList(Tegra::CommandList&& entries) {

View File

@@ -10,6 +10,7 @@
#include <optional>
#include <thread>
#include <variant>
#include "common/threadsafe_queue.h"
#include "video_core/gpu.h"
@@ -19,9 +20,6 @@ class DmaPusher;
} // namespace Tegra
namespace Core {
namespace Frontend {
class GraphicsContext;
}
class System;
} // namespace Core
@@ -101,8 +99,7 @@ public:
~ThreadManager();
/// Creates and starts the GPU thread.
void StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
Tegra::DmaPusher& dma_pusher);
void StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher);
/// Push GPU command entries to be processed
void SubmitList(Tegra::CommandList&& entries);

View File

@@ -46,8 +46,7 @@ public:
/// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer
/// specific implementation)
/// Returns true if a frame was drawn
virtual bool TryPresent(int timeout_ms) = 0;
virtual void TryPresent(int timeout_ms) = 0;
// Getter/setter functions:
// ------------------------

View File

@@ -444,7 +444,6 @@ void RasterizerOpenGL::Clear() {
}
SyncRasterizeEnable();
SyncStencilTestState();
if (regs.clear_flags.scissor) {
SyncScissorTest();
@@ -1053,8 +1052,12 @@ void RasterizerOpenGL::SyncStencilTestState() {
flags[Dirty::StencilTest] = false;
const auto& regs = gpu.regs;
oglEnable(GL_STENCIL_TEST, regs.stencil_enable);
if (!regs.stencil_enable) {
glDisable(GL_STENCIL_TEST);
return;
}
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),

View File

@@ -327,7 +327,8 @@ void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
const auto worker = [&](Core::Frontend::GraphicsContext* context, std::size_t begin,
std::size_t end) {
const auto scope = context->Acquire();
context->MakeCurrent();
SCOPE_EXIT({ return context->DoneCurrent(); });
for (std::size_t i = begin; i < end; ++i) {
if (stop_loading) {

View File

@@ -2114,10 +2114,6 @@ 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};
@@ -2311,8 +2307,6 @@ 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";
@@ -2463,21 +2457,7 @@ 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,

View File

@@ -30,6 +30,8 @@ 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 {
@@ -212,7 +214,7 @@ public:
std::deque<Frame*> present_queue;
Frame* previous_frame{};
FrameMailbox() {
FrameMailbox() : has_debug_tool{HasDebugTool()} {
for (auto& frame : swap_chain) {
free_queue.push(&frame);
}
@@ -283,9 +285,13 @@ 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),
@@ -311,12 +317,38 @@ 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,
Core::Frontend::GraphicsContext& context)
RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system)
: VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system},
frame_mailbox{}, context{context}, has_debug_tool{HasDebugTool()} {}
frame_mailbox{std::make_unique<FrameMailbox>()} {}
RendererOpenGL::~RendererOpenGL() = default;
@@ -324,6 +356,8 @@ 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;
}
@@ -379,13 +413,6 @@ 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) {
@@ -453,8 +480,6 @@ 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);
@@ -667,21 +692,12 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
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) {
void RendererOpenGL::TryPresent(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 false;
return;
}
// Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a
@@ -709,7 +725,6 @@ bool RendererOpenGL::Present(int timeout_ms) {
glFlush();
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
return true;
}
void RendererOpenGL::RenderScreenshot() {

View File

@@ -55,14 +55,13 @@ class FrameMailbox;
class RendererOpenGL final : public VideoCore::RendererBase {
public:
explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system,
Core::Frontend::GraphicsContext& context);
explicit RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system);
~RendererOpenGL() override;
bool Init() override;
void ShutDown() override;
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
bool TryPresent(int timeout_ms) override;
void TryPresent(int timeout_ms) override;
private:
/// Initializes the OpenGL state and creates persistent objects.
@@ -90,11 +89,8 @@ 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};
@@ -119,8 +115,6 @@ private:
/// Frame presentation mailbox
std::unique_ptr<FrameMailbox> frame_mailbox;
bool has_debug_tool = false;
};
} // namespace OpenGL

View File

@@ -141,9 +141,8 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
render_window.PollEvents();
}
bool RendererVulkan::TryPresent(int /*timeout_ms*/) {
void RendererVulkan::TryPresent(int /*timeout_ms*/) {
// TODO (bunnei): ImplementMe
return true;
}
bool RendererVulkan::Init() {

View File

@@ -42,7 +42,7 @@ public:
bool Init() override;
void ShutDown() override;
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
bool TryPresent(int timeout_ms) override;
void TryPresent(int timeout_ms) override;
private:
std::optional<vk::DebugUtilsMessengerEXT> CreateDebugCallback(

View File

@@ -1941,11 +1941,7 @@ private:
return {};
}
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);
Expression AtomicAdd(Operation operation) {
Id pointer;
if (const auto smem = std::get_if<SmemNode>(&*operation[0])) {
pointer = GetSharedMemoryPointer(*smem);
@@ -1953,15 +1949,14 @@ private:
pointer = GetGlobalMemoryPointer(*gmem);
} else {
UNREACHABLE();
return {Constant(type_def, 0), result_type};
return {Constant(t_uint, 0), Type::Uint};
}
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(type_def, 0);
const Id semantics = Constant(t_uint, 0U);
return {(this->*func)(type_def, pointer, scope, semantics, value), result_type};
const Id value = AsUint(Visit(operation[1]));
return {OpAtomicIAdd(t_uint, pointer, scope, semantics, value), Type::Uint};
}
Expression Branch(Operation operation) {
@@ -2550,21 +2545,7 @@ private:
&SPIRVDecompiler::AtomicImageXor,
&SPIRVDecompiler::AtomicImageExchange,
&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::AtomicAdd,
&SPIRVDecompiler::Branch,
&SPIRVDecompiler::BranchIndirect,

View File

@@ -1,750 +0,0 @@
// 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

View File

@@ -1,987 +0,0 @@
// 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

View File

@@ -235,30 +235,34 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) {
case OpCode::Id::LEA_IMM:
case OpCode::Id::LEA_RZ:
case OpCode::Id::LEA_HI: {
auto [op_a, op_b, op_c] = [&]() -> std::tuple<Node, Node, Node> {
const 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());
@@ -271,9 +275,12 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) {
UNIMPLEMENTED_IF_MSG(instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex),
"Unhandled LEA Predicate");
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));
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);
break;
}

View File

@@ -138,23 +138,18 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
value = GetOperandAbsNegFloat(value, instr.conversion.abs_a, instr.conversion.negate_a);
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;
}
value = [&]() {
switch (instr.conversion.f2f.GetRoundingMode()) {
case Tegra::Shader::F2fRoundingOp::None:
return value;
case Tegra::Shader::F2fRoundingOp::Round:
return Operation(OperationCode::FRoundEven, value);
return Operation(OperationCode::FRoundEven, PRECISE, value);
case Tegra::Shader::F2fRoundingOp::Floor:
return Operation(OperationCode::FFloor, value);
return Operation(OperationCode::FFloor, PRECISE, value);
case Tegra::Shader::F2fRoundingOp::Ceil:
return Operation(OperationCode::FCeil, value);
return Operation(OperationCode::FCeil, PRECISE, value);
case Tegra::Shader::F2fRoundingOp::Trunc:
return Operation(OperationCode::FTrunc, value);
return Operation(OperationCode::FTrunc, PRECISE, value);
default:
UNIMPLEMENTED_MSG("Unimplemented F2F rounding mode {}",
static_cast<u32>(instr.conversion.f2f.rounding.Value()));

View File

@@ -19,6 +19,7 @@ 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;
@@ -27,31 +28,6 @@ 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;
@@ -387,13 +363,10 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
break;
}
case OpCode::Id::ATOM: {
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()));
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()));
const auto [real_address, base_address, descriptor] =
TrackGlobalMemory(bb, instr, true, true);
@@ -402,29 +375,25 @@ 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 = GetAtomOperation(static_cast<AtomicOp>(instr.atom.operation), is_signed, gmem,
GetRegister(instr.gpr20));
Node value = Operation(OperationCode::AtomicAdd, std::move(gmem), GetRegister(instr.gpr20));
SetRegister(bb, instr.gpr0, std::move(value));
break;
}
case OpCode::Id::ATOMS: {
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;
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()));
const s32 offset = instr.atoms.GetImmediateOffset();
Node address = GetRegister(instr.gpr8);
address = Operation(OperationCode::IAdd, std::move(address), Immediate(offset));
Node value =
GetAtomOperation(static_cast<AtomicOp>(instr.atoms.operation), is_signed,
GetSharedMemory(std::move(address)), GetRegister(instr.gpr20));
Node memory = GetSharedMemory(std::move(address));
Node data = GetRegister(instr.gpr20);
Node value = Operation(OperationCode::AtomicAdd, std::move(memory), std::move(data));
SetRegister(bb, instr.gpr0, std::move(value));
break;
}

View File

@@ -162,21 +162,7 @@ enum class OperationCode {
AtomicImageXor, /// (MetaImage, int[N] coords) -> void
AtomicImageExchange, /// (MetaImage, int[N] coords) -> void
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
AtomicAdd, /// (memory, {u}int) -> {u}int
Branch, /// (uint branch_target) -> void
BranchIndirect, /// (uint branch_target) -> void

View File

@@ -86,20 +86,6 @@ 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 {};

View File

@@ -15,13 +15,13 @@
#endif
#include "video_core/video_core.h"
namespace {
std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
Core::System& system,
Core::Frontend::GraphicsContext& context) {
namespace VideoCore {
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
Core::System& system) {
switch (Settings::values.renderer_backend) {
case Settings::RendererBackend::OpenGL:
return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context);
return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system);
#ifdef HAS_VULKAN
case Settings::RendererBackend::Vulkan:
return std::make_unique<Vulkan::RendererVulkan>(emu_window, system);
@@ -30,23 +30,13 @@ std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindo
return nullptr;
}
}
} // Anonymous namespace
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;
}
std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) {
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::GPUAsynch>(system, system.Renderer());
}
return std::make_unique<VideoCommon::GPUSynch>(system, std::move(renderer), std::move(context));
return std::make_unique<VideoCommon::GPUSynch>(system, system.Renderer());
}
u16 GetResolutionScaleFactor(const RendererBase& renderer) {

View File

@@ -22,8 +22,17 @@ 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::Frontend::EmuWindow& emu_window, Core::System& system);
std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system);
u16 GetResolutionScaleFactor(const RendererBase& renderer);

View File

@@ -10,6 +10,9 @@
#include <QMessageBox>
#include <QOffscreenSurface>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLFunctions_4_3_Core>
#include <QOpenGLWindow>
#include <QPainter>
#include <QScreen>
#include <QStringList>
@@ -26,6 +29,7 @@
#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"
@@ -35,16 +39,26 @@
#include "yuzu/bootmanager.h"
#include "yuzu/main.h"
EmuThread::EmuThread() = default;
EmuThread::EmuThread(GRenderWindow& window)
: shared_context{window.CreateSharedContext()},
context{(Settings::values.use_asynchronous_gpu_emulation && shared_context) ? *shared_context
: window} {}
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");
// Main process has been loaded. Make the context current to this thread and begin GPU and CPU
// execution.
Core::System::GetInstance().GPU().Start();
Core::Frontend::ScopeAcquireContext acquire_context{context};
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
@@ -97,156 +111,162 @@ void EmuThread::run() {
#endif
}
class OpenGLSharedContext : public Core::Frontend::GraphicsContext {
class GGLContext : public Core::Frontend::GraphicsContext {
public:
/// 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) {
explicit GGLContext(QOpenGLContext* shared_context)
: context(new QOpenGLContext(shared_context->parent())),
surface(new QOffscreenSurface(nullptr)) {
// disable vsync for any shared contexts
auto format = share_context->format();
format.setSwapInterval(main_surface ? Settings::values.use_vsync : 0);
auto format = shared_context->format();
format.setSwapInterval(0);
context = std::make_unique<QOpenGLContext>();
context->setShareContext(share_context);
context->setShareContext(shared_context);
context->setFormat(format);
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);
context->create();
surface->setParent(shared_context->parent());
surface->setFormat(format);
surface->create();
}
void MakeCurrent() override {
if (is_current) {
return;
}
is_current = context->makeCurrent(surface);
context->makeCurrent(surface);
}
void DoneCurrent() override {
if (!is_current) {
return;
}
context->doneCurrent();
is_current = false;
}
QOpenGLContext* GetShareContext() {
return context.get();
}
const QOpenGLContext* GetShareContext() const {
return context.get();
}
private:
// 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;
QOpenGLContext* context;
QOffscreenSurface* surface;
};
class DummyContext : public Core::Frontend::GraphicsContext {};
class RenderWidget : public QWidget {
class ChildRenderWindow : public QWindow {
public:
explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) {
setAttribute(Qt::WA_NativeWindow);
setAttribute(Qt::WA_PaintOnScreen);
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);
}
}
virtual ~RenderWidget() = default;
/// 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() {}
void paintEvent(QPaintEvent* event) override {
Present();
update();
}
QPaintEngine* paintEngine() const override {
return nullptr;
void exposeEvent(QExposeEvent* event) override {
QWindow::requestUpdate();
QWindow::exposeEvent(event);
}
private:
GRenderWindow* render_window;
QWidget* event_handler{};
};
class OpenGLRenderWidget : public RenderWidget {
class OpenGLWindow final : public ChildRenderWindow {
public:
explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {
windowHandle()->setSurfaceType(QWindow::OpenGLSurface);
OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context)
: ChildRenderWindow{parent, event_handler},
context(new QOpenGLContext(shared_context->parent())) {
// disable vsync for any shared contexts
auto format = shared_context->format();
format.setSwapInterval(Settings::values.use_vsync ? 1 : 0);
this->setFormat(format);
context->setShareContext(shared_context);
context->setScreen(this->screen());
context->setFormat(format);
context->create();
setSurfaceType(QWindow::OpenGLSurface);
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
// WA_DontShowOnScreen, WA_DeleteOnClose
}
void SetContext(std::unique_ptr<Core::Frontend::GraphicsContext>&& context_) {
context = std::move(context_);
~OpenGLWindow() override {
context->doneCurrent();
}
void Present() override {
if (!isVisible()) {
if (!isExposed()) {
return;
}
context->MakeCurrent();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
if (Core::System::GetInstance().Renderer().TryPresent(100)) {
context->SwapBuffers();
glFinish();
}
context->makeCurrent(this);
Core::System::GetInstance().Renderer().TryPresent(100);
context->swapBuffers(this);
auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>();
f->glFinish();
QWindow::requestUpdate();
}
private:
std::unique_ptr<Core::Frontend::GraphicsContext> context{};
QOpenGLContext* context{};
};
#ifdef HAS_VULKAN
class VulkanRenderWidget : public RenderWidget {
class VulkanWindow final : public ChildRenderWindow {
public:
explicit VulkanRenderWidget(GRenderWindow* parent, QVulkanInstance* instance)
: RenderWidget(parent) {
windowHandle()->setSurfaceType(QWindow::VulkanSurface);
windowHandle()->setVulkanInstance(instance);
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
}
private:
QWidget* event_handler{};
};
#endif
GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread)
GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)
: QWidget(parent_), emu_thread(emu_thread) {
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
.arg(QString::fromUtf8(Common::g_build_name),
@@ -258,13 +278,26 @@ GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread)
setLayout(layout);
InputCommon::Init();
connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete);
GMainWindow* parent = GetMainWindow();
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;
@@ -276,6 +309,21 @@ 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),
@@ -319,7 +367,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}))};
@@ -339,10 +387,8 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
}
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
// touch input is handled in TouchBeginEvent
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
return;
}
if (event->source() == Qt::MouseEventSynthesizedBySystem)
return; // touch input is handled in TouchBeginEvent
auto pos = event->pos();
if (event->button() == Qt::LeftButton) {
@@ -354,10 +400,8 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
}
void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
// touch input is handled in TouchUpdateEvent
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
return;
}
if (event->source() == Qt::MouseEventSynthesizedBySystem)
return; // touch input is handled in TouchUpdateEvent
auto pos = event->pos();
const auto [x, y] = ScaleTouch(pos);
@@ -366,16 +410,13 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
}
void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
// touch input is handled in TouchEndEvent
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
return;
}
if (event->source() == Qt::MouseEventSynthesizedBySystem)
return; // touch input is handled in TouchEndEvent
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) {
@@ -433,13 +474,9 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {
std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
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 std::make_unique<GGLContext>(QOpenGLContext::globalShareContext());
}
return std::make_unique<DummyContext>();
return {};
}
bool GRenderWindow::InitRenderTarget() {
@@ -460,11 +497,14 @@ 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);
@@ -483,10 +523,9 @@ bool GRenderWindow::InitRenderTarget() {
void GRenderWindow::ReleaseRenderTarget() {
if (child_widget) {
layout()->removeWidget(child_widget);
child_widget->deleteLater();
delete child_widget;
child_widget = nullptr;
}
main_context.reset();
}
void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
@@ -518,13 +557,24 @@ 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
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()));
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();
return true;
}
@@ -554,10 +604,13 @@ bool GRenderWindow::InitializeVulkan() {
return false;
}
auto child = new VulkanRenderWidget(this, vk_instance.get());
child_widget = child;
child_widget->windowHandle()->create();
main_context = std::make_unique<DummyContext>();
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);
return true;
#else
@@ -567,24 +620,8 @@ 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() {
auto context = CreateSharedContext();
auto scope = context->Acquire();
Core::Frontend::ScopeAcquireContext acquire_context{*this};
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 "

View File

@@ -18,10 +18,12 @@
#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
@@ -34,7 +36,7 @@ class EmuThread final : public QThread {
Q_OBJECT
public:
explicit EmuThread();
explicit EmuThread(GRenderWindow& window);
~EmuThread() override;
/**
@@ -88,6 +90,12 @@ 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
@@ -116,10 +124,12 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
Q_OBJECT
public:
GRenderWindow(GMainWindow* parent, EmuThread* emu_thread);
GRenderWindow(QWidget* 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,
@@ -155,8 +165,6 @@ 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();
@@ -168,6 +176,7 @@ signals:
void FirstFrameDisplayed();
private:
std::pair<u32, u32> ScaleTouch(QPointF pos) const;
void TouchBeginEvent(const QTouchEvent* event);
void TouchUpdateEvent(const QTouchEvent* event);
void TouchEndEvent();
@@ -181,10 +190,7 @@ private:
EmuThread* emu_thread;
// 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;
std::unique_ptr<GraphicsContext> core_context;
#ifdef HAS_VULKAN
std::unique_ptr<QVulkanInstance> vk_instance;
@@ -195,6 +201,12 @@ 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;

View File

@@ -839,95 +839,7 @@
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">
<item>
<layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout">
<item>
<widget class="QLabel" name="labelL">
<property name="text">
<string>L:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="buttonL">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout">
<item>
<layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout">
<item>
<widget class="QLabel" name="labelR">
<property name="text">
<string>R:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="buttonR">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
<item>
<layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout">
<item>
<widget class="QLabel" name="labelZL">
<property name="text">
<string>ZL:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="buttonZL">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
<item>
<layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout">
<item>
<widget class="QLabel" name="labelZR">
<property name="text">
<string>ZR:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="buttonZR">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<item row="3" column="0">
<layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
<item>
<layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout">
@@ -950,6 +862,28 @@
</layout>
</item>
<item row="2" column="1">
<layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
<item>
<layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout">
<item>
<widget class="QLabel" name="labelZR">
<property name="text">
<string>ZR:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="buttonZR">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="1">
<layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
<item>
<layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout">
@@ -971,6 +905,72 @@
</item>
</layout>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
<item>
<layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout">
<item>
<widget class="QLabel" name="labelZL">
<property name="text">
<string>ZL:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="buttonZL">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">
<item>
<layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout">
<item>
<widget class="QLabel" name="labelL">
<property name="text">
<string>L:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="buttonL">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout">
<item>
<layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout">
<item>
<widget class="QLabel" name="labelR">
<property name="text">
<string>R:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="buttonR">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>

View File

@@ -984,7 +984,7 @@ void GMainWindow::BootGame(const QString& filename) {
return;
// Create and start the emulation thread
emu_thread = std::make_unique<EmuThread>();
emu_thread = std::make_unique<EmuThread>(*render_window);
emit EmulationStarting(emu_thread.get());
emu_thread->start();
@@ -1034,14 +1034,6 @@ void GMainWindow::BootGame(const QString& filename) {
}
void GMainWindow::ShutdownGame() {
if (!emulation_running) {
return;
}
if (ui.action_Fullscreen->isChecked()) {
HideFullscreen();
}
AllowOSSleep();
discord_rpc->Pause();
@@ -1806,7 +1798,7 @@ void GMainWindow::ToggleWindowMode() {
// Render in the main window...
render_window->BackupGeometry();
ui.horizontalLayout->addWidget(render_window);
render_window->setFocusPolicy(Qt::StrongFocus);
render_window->setFocusPolicy(Qt::ClickFocus);
if (emulation_running) {
render_window->setVisible(true);
render_window->setFocus();
@@ -2378,6 +2370,7 @@ 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

View File

@@ -37,24 +37,16 @@ public:
}
void MakeCurrent() override {
if (is_current) {
return;
}
is_current = SDL_GL_MakeCurrent(window, context) == 0;
SDL_GL_MakeCurrent(window, context);
}
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() {
@@ -156,6 +148,14 @@ 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

View File

@@ -13,6 +13,8 @@ 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

View File

@@ -111,6 +111,14 @@ 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;

View File

@@ -13,6 +13,8 @@ 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;

View File

@@ -230,11 +230,18 @@ int main(int argc, char** argv) {
system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL");
// Core is loaded, start the GPU (makes the GPU contexts current to this thread)
system.GPU().Start();
system.Renderer().Rasterizer().LoadDiskResources();
// 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();
}
});
std::thread render_thread([&emu_window] { emu_window->Present(); });
while (emu_window->IsOpen()) {
system.RunLoop();

View File

@@ -102,6 +102,8 @@ 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() {
@@ -112,6 +114,14 @@ 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;
}
@@ -119,35 +129,3 @@ 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>();
}

View File

@@ -16,6 +16,12 @@ 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;
@@ -23,7 +29,8 @@ public:
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const override;
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
/// Whether the window is still open, and a close request hasn't yet been sent
bool IsOpen() const;
private:
/// Whether the GPU and driver supports the OpenGL extension required

View File

@@ -164,6 +164,11 @@ 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,
@@ -252,7 +257,6 @@ int main(int argc, char** argv) {
system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester");
system.GPU().Start();
system.Renderer().Rasterizer().LoadDiskResources();
while (!finished) {