Compare commits

...

66 Commits

Author SHA1 Message Date
ReinUsesLisp
4d993f1b72 Ignore asserts 2020-04-03 16:24:35 -03:00
bunnei
0d4ca5a8fc Merge pull request #3595 from ReinUsesLisp/c4715-silence
shader/memory: Silence no return value warning
2020-04-02 14:32:19 -04:00
ReinUsesLisp
e1bd89e1c2 shader/memory: Silence no return value warning
Silences a warning about control paths not all returning a value.
2020-04-02 03:34:27 -03:00
Rodrigo Locatti
825a6e2615 Merge pull request #3552 from jroweboy/single-context
Refactor Context management (Fixes renderdoc on opengl issues)
2020-04-02 01:38:25 -03:00
Rodrigo Locatti
baf91c920c Merge pull request #3591 from ReinUsesLisp/vk-wrapper-part2
renderer_vulkan/wrapper: Add a Vulkan wrapper (part 2 of 2)
2020-03-31 22:14:26 -03:00
ReinUsesLisp
f22f6b72c3 renderer_vulkan/wrapper: Add vkEnumerateInstanceExtensionProperties wrapper 2020-03-31 21:32:08 -03:00
ReinUsesLisp
27dd542c60 renderer_vulkan/wrapper: Add command buffer handle 2020-03-31 21:32:08 -03:00
ReinUsesLisp
5c90d060d8 renderer_vulkan/wrapper: Add physical device handle 2020-03-31 21:32:08 -03:00
ReinUsesLisp
0eb37de98f renderer_vulkan/wrapper: Add device handle 2020-03-31 21:32:08 -03:00
ReinUsesLisp
11774308d3 renderer_vulkan/wrapper: Add swapchain handle 2020-03-31 21:32:07 -03:00
ReinUsesLisp
7fe52ef77f renderer_vulkan/wrapper: Add fence handle 2020-03-31 21:32:07 -03:00
ReinUsesLisp
3a63ae0658 renderer_vulkan/wrapper: Add device memory handle 2020-03-31 21:32:07 -03:00
ReinUsesLisp
397f53dea1 renderer_vulkan/wrapper: Add pool handles 2020-03-31 21:32:07 -03:00
ReinUsesLisp
affee77b70 renderer_vulkan/wrapper: Add buffer and image handles 2020-03-31 21:32:07 -03:00
ReinUsesLisp
d85ca0ab33 renderer_vulkan/wrapper: Add queue handle 2020-03-31 21:32:07 -03:00
ReinUsesLisp
151ddcf419 renderer_vulkan/wrapper: Add instance handle 2020-03-31 21:32:07 -03:00
Morph
224a75d839 capsrv: Split Capture services into individual files and stub GetAlbumContentsFileListForApplication (#3571)
* Organize capture services into individual files

* Stub GetAlbumContentsFileListForApplication

* Address feedback
2020-03-31 19:16:36 -04:00
Fernando Sahmkow
b03c0536ce Merge pull request #3561 from ReinUsesLisp/f2f-conversion
shader/conversion: Fix F2F rounding operations with different sizes
2020-03-31 14:45:02 -04:00
Fernando Sahmkow
5b95a01463 Merge pull request #3577 from ReinUsesLisp/lea
shader/lea: Fix LEA implementation
2020-03-31 14:36:07 -04:00
Rodrigo Locatti
c19425ed69 Merge pull request #3506 from namkazt/patch-9
shader_decode: Implement partial ATOM/ATOMS instr
2020-03-31 00:56:28 -03:00
Nguyen Dac Nam
238c35b2c9 clang-format 2020-03-31 08:08:06 +07:00
Nguyen Dac Nam
defb9642da shader_decode: fix by suggestion 2020-03-31 08:02:44 +07:00
Rodrigo Locatti
69728e8ad5 Merge pull request #3566 from ReinUsesLisp/vk-wrapper-part1
renderer_vulkan/wrapper: Add a Vulkan wrapper (part 1 of 2)
2020-03-30 21:57:36 -03:00
bunnei
4c72190a06 Merge pull request #3560 from ReinUsesLisp/fix-stencil
gl_rasterizer: Synchronize stencil testing on clears
2020-03-30 17:03:07 -04:00
James Rowe
f1da3ec584 Frontend: Don't call DoneCurrent if the context isnt already current 2020-03-30 14:57:42 -06:00
namkazy
cb0a4151f8 clang-format 2020-03-30 20:46:21 +07:00
namkazy
c2665ec9c2 gl_decompiler: min/max op not implement yet 2020-03-30 18:48:22 +07:00
namkazy
4f7bea403a shader_decode: ATOM/ATOMS: add function to avoid code repetition 2020-03-30 18:47:50 +07:00
namkazy
c8f6d9effd shader_decode: merge GlobalAtomicOp to AtomicOp 2020-03-30 18:47:00 +07:00
Nguyen Dac Nam
972485ff18 shader_decode: implement ATOM operation for S32 and U32 2020-03-30 17:44:48 +07:00
namkazy
93cac0d294 clang-format 2020-03-30 17:44:48 +07:00
Nguyen Dac Nam
3dc09a6250 shader_decode: implement ATOMS instr partial. 2020-03-30 17:44:46 +07:00
Nguyen Dac Nam
a2cc80b605 vk_decompiler: add atomic op and handler function. 2020-03-30 17:44:45 +07:00
Nguyen Dac Nam
552f0ff267 gl_decompiler: add atomic op 2020-03-30 17:44:45 +07:00
Nguyen Dac Nam
2c780db5b9 shader: node - update correct comment 2020-03-30 17:44:44 +07:00
Nguyen Dac Nam
c119473c40 shader_decode: add Atomic op for common usage 2020-03-30 17:44:44 +07:00
bunnei
2c9879d2eb Merge pull request #3568 from bunnei/time-calcspan
services: time: Implement CalculateSpanBetween.
2020-03-29 00:03:55 -04:00
bunnei
5e2f8e30e7 Merge pull request #3545 from FearlessTobi/audren-rev
audio_core: Accept Audren REV8
2020-03-28 22:08:19 -04:00
bunnei
ffe1e2b5ec Merge pull request #3562 from perillamint/vrsvc
am: Implement VR related APIs
2020-03-28 13:32:52 -04:00
bunnei
997a802bd6 Merge pull request #3569 from bunnei/hid-stub
services: hid: Stub InitializeSevenSixAxisSensor.
2020-03-28 04:10:45 -04:00
ReinUsesLisp
b6c9fba81c renderer_vulkan/wrapper: Address feedback 2020-03-28 04:09:02 -03:00
ReinUsesLisp
5300a918c6 shader/lea: Simplify generated LEA code 2020-03-28 03:55:04 -03:00
bunnei
75795a9a63 Merge pull request #3573 from FearlessTobi/port-5089
Port citra-emu/citra#5089: "Set render window's focus policy to Qt::StrongFocus"
2020-03-28 01:48:46 -04:00
bunnei
9bd9980372 Merge pull request #3576 from FearlessTobi/port-4906
Port citra-emu/citra#4906: "citra_qt: fix the stuck in fullscreen mode"
2020-03-28 00:28:31 -04:00
bunnei
6286b8dedd Merge pull request #3574 from FearlessTobi/port-5093
Port citra-emu/citra#5093: "Replace non-commercial icons"
2020-03-27 21:43:30 -04:00
ReinUsesLisp
523a709bf1 shader/lea: Fix op_a and op_b usages
They were swapped.
2020-03-27 18:37:20 -03:00
ReinUsesLisp
796b3319e6 shader/lea: Remove const and use move when possible 2020-03-27 18:36:38 -03:00
Guo Yunhe
dd236c6c1d Replace non-commercial icons with free icons from icons8 (#5093) 2020-03-27 22:31:54 +01:00
Vitor K
10ba8d16be Set render window to also accept focus via tabbing (Qt::StrongFocus) (#5089) 2020-03-27 22:25:01 +01:00
bunnei
8a6a25e4b6 services: hid: Stub InitializeSevenSixAxisSensor.
- Used by Super Smash Bros. Ultimate v7.0.0.
2020-03-27 10:48:01 -04:00
bunnei
a60f34a850 services: time: Implement CalculateSpanBetween.
- Used by Super Smash Bros. Ultimate.
2020-03-27 10:42:13 -04:00
perillamint
a429644672 am: Implement VR related APIs
Implement (and stub) VR related APIs in AM sysmodule.

This fixes issue #2938
2020-03-27 18:22:28 +09:00
ReinUsesLisp
2694552b7f renderer_vulkan/wrapper: Add owning handles 2020-03-27 03:21:04 -03:00
ReinUsesLisp
7413b30923 renderer_vulkan/wrapper: Add pool allocations owning templated class 2020-03-27 03:21:04 -03:00
ReinUsesLisp
d8d392b39a renderer_vulkan/wrapper: Add owning handle templated class 2020-03-27 03:21:04 -03:00
ReinUsesLisp
60f351084a renderer_vulkan/wrapper: Add destroy and free overload set 2020-03-27 03:21:04 -03:00
ReinUsesLisp
a9e4528d10 renderer_vulkan/wrapper: Add dispatch table and loaders 2020-03-27 03:21:04 -03:00
ReinUsesLisp
3f0b7673f0 renderer_vulkan/wrapper: Add exception class 2020-03-27 03:21:04 -03:00
ReinUsesLisp
f5cee0e885 renderer_vulkan/wrapper: Add ToString function for VkResult 2020-03-27 03:21:03 -03:00
ReinUsesLisp
92c8d783b3 renderer_vulkan/wrapper: Add Vulakn wrapper and a span helper
The intention behind a Vulkan wrapper is to drop Vulkan-Hpp.

The issues with Vulkan-Hpp are:
- Regular breaks of the API.
- Copy constructors that do the same as the aggregates (fixed recently)
- External dynamic dispatch that is hard to remove
- Alias KHR handles with non-KHR handles making it impossible to use
smart handles on Vulkan 1.0 instances with extensions that were included
on Vulkan 1.1.
- Dynamic dispatchers silently change size depending on preprocessor
definitions. Different files will have different dispatch definitions,
generating all kinds of hard to debug memory issues.

In other words, Vulkan-Hpp is not "production ready" for our needs and
this wrapper aims to replace it without losing RAII and exception
safety.
2020-03-27 03:13:18 -03:00
James Rowe
cf9c94d401 Address review and fix broken yuzu-tester build 2020-03-25 23:32:42 -06:00
ReinUsesLisp
46791c464a shader/conversion: Fix F2F rounding operations with different sizes
Rounding operations only matter when the conversion size of source and
destination is the same, i.e. .F16.F16, .F32.F32 and .F64.F64.

When there is a mismatch (.F16.F32), these bits are used for IEEE
rounding, we don't emulate this because GLSL and SPIR-V don't support
configuring it per operation.
2020-03-26 01:58:49 -03:00
ReinUsesLisp
7617e88fb2 gl_rasterizer: Update stencil test regardless of it being disabled 2020-03-26 01:08:14 -03:00
ReinUsesLisp
c310cef615 gl_rasterizer: Synchronize stencil testing on clears 2020-03-26 00:51:47 -03:00
James Rowe
282adfc70b Frontend/GPU: Refactor context management
Changes the GraphicsContext to be managed by the GPU core. This
eliminates the need for the frontends to fool around with tricky
MakeCurrent/DoneCurrent calls that are dependent on the settings (such
as async gpu option).

This also refactors out the need to use QWidget::fromWindowContainer as
that caused issues with focus and input handling. Now we use a regular
QWidget and just access the native windowHandle() directly.

Another change is removing the debug tool setting in FrameMailbox.
Instead of trying to block the frontend until a new frame is ready, the
core will now take over presentation and draw directly to the window if
the renderer detects that its hooked by NSight or RenderDoc

Lastly, since it was in the way, I removed ScopeAcquireWindowContext and
replaced it with a simple subclass in GraphicsContext that achieves the
same result
2020-03-24 21:03:42 -06:00
FearlessTobi
5d3b228409 audio_core: Accept Audren REV8
According to Ryujinx, REV8 only added changes on Performance buffer and Wavebuffer DSP command generation.

As we don't support any of those, we can just increment the revision number for now.
2020-03-23 03:07:14 +01:00
71 changed files with 2894 additions and 680 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 | Free for non-commercial use
qt_themes/default/icons/16x16/failed.png | Free for non-commercial use
qt_themes/default/icons/16x16/checked.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/16x16/failed.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
@@ -11,8 +11,6 @@ qt_themes/default/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
qt_themes/default/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/16x16/checked.png | Free for non-commercial use
qt_themes/qdarkstyle/icons/16x16/failed.png | Free for non-commercial use
qt_themes/qdarkstyle/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com

Binary file not shown.

Before

Width:  |  Height:  |  Size: 451 B

After

Width:  |  Height:  |  Size: 657 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 428 B

After

Width:  |  Height:  |  Size: 524 B

View File

@@ -343,8 +343,8 @@ The icons used in this project have the following licenses:
Icon Name | License | Origin/Author
--- | --- | ---
checked.png | Free for non-commercial use
failed.png | Free for non-commercial use
checked.png | CC BY-ND 3.0 | https://icons8.com
failed.png | CC BY-ND 3.0 | https://icons8.com
lock.png | CC BY-ND 3.0 | https://icons8.com
plus_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
bad_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com

View File

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

View File

@@ -30,20 +30,19 @@ __declspec(noinline, noreturn)
#define ASSERT(_a_) \
do \
if (!(_a_)) { \
assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
LOG_CRITICAL(Debug, "Assertion Failed!"); \
} \
while (0)
#define ASSERT_MSG(_a_, ...) \
do \
if (!(_a_)) { \
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
LOG_CRITICAL(Debug, "Assertion Failed! " __VA_ARGS__); \
} \
while (0)
#define UNREACHABLE() assert_noinline_call([] { LOG_CRITICAL(Debug, "Unreachable code!"); })
#define UNREACHABLE_MSG(...) \
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Unreachable code!\n" __VA_ARGS__); })
#define UNREACHABLE() LOG_CRITICAL(Debug, "Unreachable code!");
#define UNREACHABLE_MSG(...) LOG_CRITICAL(Debug, "Unreachable code! " __VA_ARGS__);
#ifdef _DEBUG
#define DEBUG_ASSERT(_a_) ASSERT(_a_)

View File

@@ -131,8 +131,6 @@ add_library(core STATIC
frontend/framebuffer_layout.cpp
frontend/framebuffer_layout.h
frontend/input.h
frontend/scope_acquire_context.cpp
frontend/scope_acquire_context.h
gdbstub/gdbstub.cpp
gdbstub/gdbstub.h
hardware_interrupt_manager.cpp
@@ -287,6 +285,18 @@ add_library(core STATIC
hle/service/btm/btm.h
hle/service/caps/caps.cpp
hle/service/caps/caps.h
hle/service/caps/caps_a.cpp
hle/service/caps/caps_a.h
hle/service/caps/caps_c.cpp
hle/service/caps/caps_c.h
hle/service/caps/caps_u.cpp
hle/service/caps/caps_u.h
hle/service/caps/caps_sc.cpp
hle/service/caps/caps_sc.h
hle/service/caps/caps_ss.cpp
hle/service/caps/caps_ss.h
hle/service/caps/caps_su.cpp
hle/service/caps/caps_su.h
hle/service/erpt/erpt.cpp
hle/service/erpt/erpt.h
hle/service/es/es.cpp

View File

@@ -24,7 +24,6 @@
#include "core/file_sys/sdmc_factory.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
#include "core/frontend/scope_acquire_context.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hardware_interrupt_manager.h"
#include "core/hle/kernel/client_port.h"
@@ -168,13 +167,12 @@ struct System::Impl {
Service::Init(service_manager, system);
GDBStub::DeferStart();
renderer = VideoCore::CreateRenderer(emu_window, system);
if (!renderer->Init()) {
interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
gpu_core = VideoCore::CreateGPU(emu_window, system);
if (!gpu_core) {
return ResultStatus::ErrorVideoCore;
}
interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
gpu_core = VideoCore::CreateGPU(system);
renderer->Rasterizer().SetupDirtyFlags();
gpu_core->Renderer().Rasterizer().SetupDirtyFlags();
is_powered_on = true;
exit_lock = false;
@@ -186,8 +184,6 @@ struct System::Impl {
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
const std::string& filepath) {
Core::Frontend::ScopeAcquireContext acquire_context{emu_window};
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
@@ -216,10 +212,6 @@ struct System::Impl {
AddGlueRegistrationForProcess(*app_loader, *main_process);
kernel.MakeCurrentProcess(main_process.get());
// Main process has been loaded and been made current.
// Begin GPU and CPU execution.
gpu_core->Start();
// Initialize cheat engine
if (cheat_engine) {
cheat_engine->Initialize();
@@ -277,7 +269,6 @@ struct System::Impl {
}
// Shutdown emulation session
renderer.reset();
GDBStub::Shutdown();
Service::Shutdown();
service_manager.reset();
@@ -353,7 +344,6 @@ struct System::Impl {
Service::FileSystem::FileSystemController fs_controller;
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core;
std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
Memory::Memory memory;
@@ -536,11 +526,11 @@ const Core::Hardware::InterruptManager& System::InterruptManager() const {
}
VideoCore::RendererBase& System::Renderer() {
return *impl->renderer;
return impl->gpu_core->Renderer();
}
const VideoCore::RendererBase& System::Renderer() const {
return *impl->renderer;
return impl->gpu_core->Renderer();
}
Kernel::KernelCore& System::Kernel() {

View File

@@ -13,19 +13,39 @@
namespace Core::Frontend {
/**
* Represents a graphics context that can be used for background computation or drawing. If the
* graphics backend doesn't require the context, then the implementation of these methods can be
* stubs
* Represents a drawing context that supports graphics operations.
*/
class GraphicsContext {
public:
virtual ~GraphicsContext();
/// Inform the driver to swap the front/back buffers and present the current image
virtual void SwapBuffers() {}
/// Makes the graphics context current for the caller thread
virtual void MakeCurrent() = 0;
virtual void MakeCurrent() {}
/// Releases (dunno if this is the "right" word) the context from the caller thread
virtual void DoneCurrent() = 0;
virtual void DoneCurrent() {}
class Scoped {
public:
explicit Scoped(GraphicsContext& context_) : context(context_) {
context.MakeCurrent();
}
~Scoped() {
context.DoneCurrent();
}
private:
GraphicsContext& context;
};
/// Calls MakeCurrent on the context and calls DoneCurrent when the scope for the returned value
/// ends
Scoped Acquire() {
return Scoped{*this};
}
};
/**
@@ -46,7 +66,7 @@ public:
* - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
* re-read the upper points again and think about it if you don't see this.
*/
class EmuWindow : public GraphicsContext {
class EmuWindow {
public:
/// Data structure to store emuwindow configuration
struct WindowConfig {
@@ -60,17 +80,9 @@ public:
virtual void PollEvents() = 0;
/**
* Returns a GraphicsContext that the frontend provides that is shared with the emu window. This
* context can be used from other threads for background graphics computation. If the frontend
* is using a graphics backend that doesn't need anything specific to run on a different thread,
* then it can use a stubbed implemenation for GraphicsContext.
*
* If the return value is null, then the core should assume that the frontend cannot provide a
* Shared Context
* Returns a GraphicsContext that the frontend provides to be used for rendering.
*/
virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const {
return nullptr;
}
virtual std::unique_ptr<GraphicsContext> CreateSharedContext() const = 0;
/// Returns if window is shown (not minimized)
virtual bool IsShown() const = 0;

View File

@@ -1,18 +0,0 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/frontend/emu_window.h"
#include "core/frontend/scope_acquire_context.h"
namespace Core::Frontend {
ScopeAcquireContext::ScopeAcquireContext(Core::Frontend::GraphicsContext& context)
: context{context} {
context.MakeCurrent();
}
ScopeAcquireContext::~ScopeAcquireContext() {
context.DoneCurrent();
}
} // namespace Core::Frontend

View File

@@ -1,23 +0,0 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace Core::Frontend {
class GraphicsContext;
/// Helper class to acquire/release window context within a given scope
class ScopeAcquireContext : NonCopyable {
public:
explicit ScopeAcquireContext(Core::Frontend::GraphicsContext& context);
~ScopeAcquireContext();
private:
Core::Frontend::GraphicsContext& context;
};
} // namespace Core::Frontend

View File

@@ -52,6 +52,11 @@ enum class LaunchParameterKind : u32 {
AccountPreselectedUser = 2,
};
enum class VrMode : u8 {
Disabled = 0,
Enabled = 1,
};
constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA;
struct LaunchParameterAccountPreselectedUser {
@@ -605,11 +610,11 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system,
{30, nullptr, "GetHomeButtonReaderLockAccessor"},
{31, nullptr, "GetReaderLockAccessorEx"},
{40, nullptr, "GetCradleFwVersion"},
{50, nullptr, "IsVrModeEnabled"},
{51, nullptr, "SetVrModeEnabled"},
{50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"},
{51, &ICommonStateGetter::SetVrModeEnabled, "SetVrModeEnabled"},
{52, &ICommonStateGetter::SetLcdBacklighOffEnabled, "SetLcdBacklighOffEnabled"},
{53, nullptr, "BeginVrModeEx"},
{54, nullptr, "EndVrModeEx"},
{54, &ICommonStateGetter::EndVrModeEx, "EndVrModeEx"},
{55, nullptr, "IsInControllerFirmwareUpdateSection"},
{60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
{61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"},
@@ -672,6 +677,30 @@ void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
rb.Push(static_cast<u8>(FocusState::InFocus));
}
void ICommonStateGetter::IsVrModeEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushEnum(VrMode::Disabled);
}
void ICommonStateGetter::SetVrModeEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto is_vr_mode_enabled = rp.Pop<bool>();
LOG_WARNING(Service_AM, "(STUBBED) called. is_vr_mode_enabled={}", is_vr_mode_enabled);
IPC::ResponseBuilder rb{ctx, 2};
if (!is_vr_mode_enabled) {
rb.Push(RESULT_SUCCESS);
} else {
// TODO: Find better error code for this
UNIMPLEMENTED_MSG("is_vr_mode_enabled={}", is_vr_mode_enabled);
rb.Push(RESULT_UNKNOWN);
}
}
void ICommonStateGetter::SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto is_lcd_backlight_off_enabled = rp.Pop<bool>();
@@ -683,6 +712,13 @@ void ICommonStateGetter::SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx
rb.Push(RESULT_SUCCESS);
}
void ICommonStateGetter::EndVrModeEx(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");

View File

@@ -182,7 +182,10 @@ private:
void GetOperationMode(Kernel::HLERequestContext& ctx);
void GetPerformanceMode(Kernel::HLERequestContext& ctx);
void GetBootMode(Kernel::HLERequestContext& ctx);
void IsVrModeEnabled(Kernel::HLERequestContext& ctx);
void SetVrModeEnabled(Kernel::HLERequestContext& ctx);
void SetLcdBacklighOffEnabled(Kernel::HLERequestContext& ctx);
void EndVrModeEx(Kernel::HLERequestContext& ctx);
void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
void SetCpuBoostMode(Kernel::HLERequestContext& ctx);

View File

@@ -2,168 +2,24 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <memory>
#include "core/hle/service/caps/caps.h"
#include "core/hle/service/caps/caps_a.h"
#include "core/hle/service/caps/caps_c.h"
#include "core/hle/service/caps/caps_sc.h"
#include "core/hle/service/caps/caps_ss.h"
#include "core/hle/service/caps/caps_su.h"
#include "core/hle/service/caps/caps_u.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::Capture {
class CAPS_A final : public ServiceFramework<CAPS_A> {
public:
explicit CAPS_A() : ServiceFramework{"caps:a"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetAlbumFileCount"},
{1, nullptr, "GetAlbumFileList"},
{2, nullptr, "LoadAlbumFile"},
{3, nullptr, "DeleteAlbumFile"},
{4, nullptr, "StorageCopyAlbumFile"},
{5, nullptr, "IsAlbumMounted"},
{6, nullptr, "GetAlbumUsage"},
{7, nullptr, "GetAlbumFileSize"},
{8, nullptr, "LoadAlbumFileThumbnail"},
{9, nullptr, "LoadAlbumScreenShotImage"},
{10, nullptr, "LoadAlbumScreenShotThumbnailImage"},
{11, nullptr, "GetAlbumEntryFromApplicationAlbumEntry"},
{12, nullptr, "Unknown12"},
{13, nullptr, "Unknown13"},
{14, nullptr, "Unknown14"},
{15, nullptr, "Unknown15"},
{16, nullptr, "Unknown16"},
{17, nullptr, "Unknown17"},
{18, nullptr, "Unknown18"},
{202, nullptr, "SaveEditedScreenShot"},
{301, nullptr, "GetLastThumbnail"},
{401, nullptr, "GetAutoSavingStorage"},
{501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"},
{1001, nullptr, "Unknown1001"},
{1002, nullptr, "Unknown1002"},
{1003, nullptr, "Unknown1003"},
{8001, nullptr, "ForceAlbumUnmounted"},
{8002, nullptr, "ResetAlbumMountStatus"},
{8011, nullptr, "RefreshAlbumCache"},
{8012, nullptr, "GetAlbumCache"},
{8013, nullptr, "Unknown8013"},
{8021, nullptr, "GetAlbumEntryFromApplicationAlbumEntryAruid"},
{10011, nullptr, "SetInternalErrorConversionEnabled"},
{50000, nullptr, "Unknown50000"},
{60002, nullptr, "Unknown60002"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class CAPS_C final : public ServiceFramework<CAPS_C> {
public:
explicit CAPS_C() : ServiceFramework{"caps:c"} {
// clang-format off
static const FunctionInfo functions[] = {
{33, nullptr, "Unknown33"},
{2001, nullptr, "Unknown2001"},
{2002, nullptr, "Unknown2002"},
{2011, nullptr, "Unknown2011"},
{2012, nullptr, "Unknown2012"},
{2013, nullptr, "Unknown2013"},
{2014, nullptr, "Unknown2014"},
{2101, nullptr, "Unknown2101"},
{2102, nullptr, "Unknown2102"},
{2201, nullptr, "Unknown2201"},
{2301, nullptr, "Unknown2301"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class CAPS_SC final : public ServiceFramework<CAPS_SC> {
public:
explicit CAPS_SC() : ServiceFramework{"caps:sc"} {
// clang-format off
static const FunctionInfo functions[] = {
{1, nullptr, "Unknown1"},
{2, nullptr, "Unknown2"},
{1001, nullptr, "Unknown3"},
{1002, nullptr, "Unknown4"},
{1003, nullptr, "Unknown5"},
{1011, nullptr, "Unknown6"},
{1012, nullptr, "Unknown7"},
{1201, nullptr, "Unknown8"},
{1202, nullptr, "Unknown9"},
{1203, nullptr, "Unknown10"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class CAPS_SS final : public ServiceFramework<CAPS_SS> {
public:
explicit CAPS_SS() : ServiceFramework{"caps:ss"} {
// clang-format off
static const FunctionInfo functions[] = {
{201, nullptr, "Unknown1"},
{202, nullptr, "Unknown2"},
{203, nullptr, "Unknown3"},
{204, nullptr, "Unknown4"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class CAPS_SU final : public ServiceFramework<CAPS_SU> {
public:
explicit CAPS_SU() : ServiceFramework{"caps:su"} {
// clang-format off
static const FunctionInfo functions[] = {
{201, nullptr, "SaveScreenShot"},
{203, nullptr, "SaveScreenShotEx0"},
};
// clang-format on
RegisterHandlers(functions);
}
};
class CAPS_U final : public ServiceFramework<CAPS_U> {
public:
explicit CAPS_U() : ServiceFramework{"caps:u"} {
// clang-format off
static const FunctionInfo functions[] = {
{32, nullptr, "SetShimLibraryVersion"},
{102, nullptr, "GetAlbumFileListByAruid"},
{103, nullptr, "DeleteAlbumFileByAruid"},
{104, nullptr, "GetAlbumFileSizeByAruid"},
{105, nullptr, "DeleteAlbumFileByAruidForDebug"},
{110, nullptr, "LoadAlbumScreenShotImageByAruid"},
{120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"},
{130, nullptr, "PrecheckToCreateContentsByAruid"},
{140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"},
{141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"},
{142, nullptr, "GetAlbumFileList3AaeAruid"},
{143, nullptr, "GetAlbumFileList4AaeUidAruid"},
{60002, nullptr, "OpenAccessorSessionForApplication"},
};
// clang-format on
RegisterHandlers(functions);
}
};
void InstallInterfaces(SM::ServiceManager& sm) {
std::make_shared<CAPS_A>()->InstallAsService(sm);
std::make_shared<CAPS_C>()->InstallAsService(sm);
std::make_shared<CAPS_U>()->InstallAsService(sm);
std::make_shared<CAPS_SC>()->InstallAsService(sm);
std::make_shared<CAPS_SS>()->InstallAsService(sm);
std::make_shared<CAPS_SU>()->InstallAsService(sm);
std::make_shared<CAPS_U>()->InstallAsService(sm);
}
} // namespace Service::Capture

View File

@@ -4,12 +4,83 @@
#pragma once
#include "core/hle/service/service.h"
namespace Service::SM {
class ServiceManager;
}
namespace Service::Capture {
enum AlbumImageOrientation {
Orientation0 = 0,
Orientation1 = 1,
Orientation2 = 2,
Orientation3 = 3,
};
enum AlbumReportOption {
Disable = 0,
Enable = 1,
};
enum ContentType : u8 {
Screenshot = 0,
Movie = 1,
ExtraMovie = 3,
};
enum AlbumStorage : u8 {
NAND = 0,
SD = 1,
};
struct AlbumFileDateTime {
u16 year;
u8 month;
u8 day;
u8 hour;
u8 minute;
u8 second;
u8 uid;
};
struct AlbumEntry {
u64 size;
u64 application_id;
AlbumFileDateTime datetime;
AlbumStorage storage;
ContentType content;
u8 padding[6];
};
struct AlbumFileEntry {
u64 size;
u64 hash;
AlbumFileDateTime datetime;
AlbumStorage storage;
ContentType content;
u8 padding[5];
u8 unknown;
};
struct ApplicationAlbumEntry {
u64 size;
u64 hash;
AlbumFileDateTime datetime;
AlbumStorage storage;
ContentType content;
u8 padding[5];
u8 unknown;
};
struct ApplicationAlbumFileEntry {
ApplicationAlbumEntry entry;
AlbumFileDateTime datetime;
u64 unknown;
};
/// Registers all Capture services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& sm);
} // namespace Service::Capture

View File

@@ -0,0 +1,78 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/caps/caps_a.h"
namespace Service::Capture {
class IAlbumAccessorSession final : public ServiceFramework<IAlbumAccessorSession> {
public:
explicit IAlbumAccessorSession() : ServiceFramework{"IAlbumAccessorSession"} {
// clang-format off
static const FunctionInfo functions[] = {
{2001, nullptr, "OpenAlbumMovieReadStream"},
{2002, nullptr, "CloseAlbumMovieReadStream"},
{2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"},
{2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"},
{2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"},
{2006, nullptr, "GetAlbumMovieReadStreamImageDataSize"},
{2007, nullptr, "ReadImageDataFromAlbumMovieReadStream"},
{2008, nullptr, "ReadFileAttributeFromAlbumMovieReadStream"},
};
// clang-format on
RegisterHandlers(functions);
}
};
CAPS_A::CAPS_A() : ServiceFramework("caps:a") {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetAlbumFileCount"},
{1, nullptr, "GetAlbumFileList"},
{2, nullptr, "LoadAlbumFile"},
{3, nullptr, "DeleteAlbumFile"},
{4, nullptr, "StorageCopyAlbumFile"},
{5, nullptr, "IsAlbumMounted"},
{6, nullptr, "GetAlbumUsage"},
{7, nullptr, "GetAlbumFileSize"},
{8, nullptr, "LoadAlbumFileThumbnail"},
{9, nullptr, "LoadAlbumScreenShotImage"},
{10, nullptr, "LoadAlbumScreenShotThumbnailImage"},
{11, nullptr, "GetAlbumEntryFromApplicationAlbumEntry"},
{12, nullptr, "LoadAlbumScreenShotImageEx"},
{13, nullptr, "LoadAlbumScreenShotThumbnailImageEx"},
{14, nullptr, "LoadAlbumScreenShotImageEx0"},
{15, nullptr, "GetAlbumUsage3"},
{16, nullptr, "GetAlbumMountResult"},
{17, nullptr, "GetAlbumUsage16"},
{18, nullptr, "Unknown18"},
{100, nullptr, "GetAlbumFileCountEx0"},
{101, nullptr, "GetAlbumFileListEx0"},
{202, nullptr, "SaveEditedScreenShot"},
{301, nullptr, "GetLastThumbnail"},
{302, nullptr, "GetLastOverlayMovieThumbnail"},
{401, nullptr, "GetAutoSavingStorage"},
{501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"},
{1001, nullptr, "LoadAlbumScreenShotThumbnailImageEx0"},
{1002, nullptr, "LoadAlbumScreenShotImageEx1"},
{1003, nullptr, "LoadAlbumScreenShotThumbnailImageEx1"},
{8001, nullptr, "ForceAlbumUnmounted"},
{8002, nullptr, "ResetAlbumMountStatus"},
{8011, nullptr, "RefreshAlbumCache"},
{8012, nullptr, "GetAlbumCache"},
{8013, nullptr, "GetAlbumCacheEx"},
{8021, nullptr, "GetAlbumEntryFromApplicationAlbumEntryAruid"},
{10011, nullptr, "SetInternalErrorConversionEnabled"},
{50000, nullptr, "LoadMakerNoteInfoForDebug"},
{60002, nullptr, "OpenAccessorSession"},
};
// clang-format on
RegisterHandlers(functions);
}
CAPS_A::~CAPS_A() = default;
} // namespace Service::Capture

View File

@@ -0,0 +1,21 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Kernel {
class HLERequestContext;
}
namespace Service::Capture {
class CAPS_A final : public ServiceFramework<CAPS_A> {
public:
explicit CAPS_A();
~CAPS_A() override;
};
} // namespace Service::Capture

View File

@@ -0,0 +1,75 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/caps/caps_c.h"
namespace Service::Capture {
class IAlbumControlSession final : public ServiceFramework<IAlbumControlSession> {
public:
explicit IAlbumControlSession() : ServiceFramework{"IAlbumControlSession"} {
// clang-format off
static const FunctionInfo functions[] = {
{2001, nullptr, "OpenAlbumMovieReadStream"},
{2002, nullptr, "CloseAlbumMovieReadStream"},
{2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"},
{2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"},
{2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"},
{2006, nullptr, "GetAlbumMovieReadStreamImageDataSize"},
{2007, nullptr, "ReadImageDataFromAlbumMovieReadStream"},
{2008, nullptr, "ReadFileAttributeFromAlbumMovieReadStream"},
{2401, nullptr, "OpenAlbumMovieWriteStream"},
{2402, nullptr, "FinishAlbumMovieWriteStream"},
{2403, nullptr, "CommitAlbumMovieWriteStream"},
{2404, nullptr, "DiscardAlbumMovieWriteStream"},
{2405, nullptr, "DiscardAlbumMovieWriteStreamNoDelete"},
{2406, nullptr, "CommitAlbumMovieWriteStreamEx"},
{2411, nullptr, "StartAlbumMovieWriteStreamDataSection"},
{2412, nullptr, "EndAlbumMovieWriteStreamDataSection"},
{2413, nullptr, "StartAlbumMovieWriteStreamMetaSection"},
{2414, nullptr, "EndAlbumMovieWriteStreamMetaSection"},
{2421, nullptr, "ReadDataFromAlbumMovieWriteStream"},
{2422, nullptr, "WriteDataToAlbumMovieWriteStream"},
{2424, nullptr, "WriteMetaToAlbumMovieWriteStream"},
{2431, nullptr, "GetAlbumMovieWriteStreamBrokenReason"},
{2433, nullptr, "GetAlbumMovieWriteStreamDataSize"},
{2434, nullptr, "SetAlbumMovieWriteStreamDataSize"},
};
// clang-format on
RegisterHandlers(functions);
}
};
CAPS_C::CAPS_C() : ServiceFramework("caps:c") {
// clang-format off
static const FunctionInfo functions[] = {
{1, nullptr, "CaptureRawImage"},
{2, nullptr, "CaptureRawImageWithTimeout"},
{33, nullptr, "Unknown33"},
{1001, nullptr, "RequestTakingScreenShot"},
{1002, nullptr, "RequestTakingScreenShotWithTimeout"},
{1011, nullptr, "NotifyTakingScreenShotRefused"},
{2001, nullptr, "NotifyAlbumStorageIsAvailable"},
{2002, nullptr, "NotifyAlbumStorageIsUnavailable"},
{2011, nullptr, "RegisterAppletResourceUserId"},
{2012, nullptr, "UnregisterAppletResourceUserId"},
{2013, nullptr, "GetApplicationIdFromAruid"},
{2014, nullptr, "CheckApplicationIdRegistered"},
{2101, nullptr, "GenerateCurrentAlbumFileId"},
{2102, nullptr, "GenerateApplicationAlbumEntry"},
{2201, nullptr, "SaveAlbumScreenShotFile"},
{2202, nullptr, "SaveAlbumScreenShotFileEx"},
{2301, nullptr, "SetOverlayScreenShotThumbnailData"},
{2302, nullptr, "SetOverlayMovieThumbnailData"},
{60001, nullptr, "OpenControlSession"},
};
// clang-format on
RegisterHandlers(functions);
}
CAPS_C::~CAPS_C() = default;
} // namespace Service::Capture

View File

@@ -0,0 +1,21 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Kernel {
class HLERequestContext;
}
namespace Service::Capture {
class CAPS_C final : public ServiceFramework<CAPS_C> {
public:
explicit CAPS_C();
~CAPS_C() override;
};
} // namespace Service::Capture

View File

@@ -0,0 +1,40 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/caps/caps_sc.h"
namespace Service::Capture {
CAPS_SC::CAPS_SC() : ServiceFramework("caps:sc") {
// clang-format off
static const FunctionInfo functions[] = {
{1, nullptr, "CaptureRawImage"},
{2, nullptr, "CaptureRawImageWithTimeout"},
{3, nullptr, "AttachSharedBuffer"},
{5, nullptr, "CaptureRawImageToAttachedSharedBuffer"},
{210, nullptr, "Unknown210"},
{1001, nullptr, "RequestTakingScreenShot"},
{1002, nullptr, "RequestTakingScreenShotWithTimeout"},
{1003, nullptr, "RequestTakingScreenShotEx"},
{1004, nullptr, "RequestTakingScreenShotEx1"},
{1009, nullptr, "CancelTakingScreenShot"},
{1010, nullptr, "SetTakingScreenShotCancelState"},
{1011, nullptr, "NotifyTakingScreenShotRefused"},
{1012, nullptr, "NotifyTakingScreenShotFailed"},
{1101, nullptr, "SetupOverlayMovieThumbnail"},
{1106, nullptr, "Unknown1106"},
{1107, nullptr, "Unknown1107"},
{1201, nullptr, "OpenRawScreenShotReadStream"},
{1202, nullptr, "CloseRawScreenShotReadStream"},
{1203, nullptr, "ReadRawScreenShotReadStream"},
{1204, nullptr, "Unknown1204"},
};
// clang-format on
RegisterHandlers(functions);
}
CAPS_SC::~CAPS_SC() = default;
} // namespace Service::Capture

View File

@@ -0,0 +1,21 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Kernel {
class HLERequestContext;
}
namespace Service::Capture {
class CAPS_SC final : public ServiceFramework<CAPS_SC> {
public:
explicit CAPS_SC();
~CAPS_SC() override;
};
} // namespace Service::Capture

View File

@@ -0,0 +1,26 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/caps/caps_ss.h"
namespace Service::Capture {
CAPS_SS::CAPS_SS() : ServiceFramework("caps:ss") {
// clang-format off
static const FunctionInfo functions[] = {
{201, nullptr, "SaveScreenShot"},
{202, nullptr, "SaveEditedScreenShot"},
{203, nullptr, "SaveScreenShotEx0"},
{204, nullptr, "SaveEditedScreenShotEx0"},
{206, nullptr, "Unknown206"},
{208, nullptr, "SaveScreenShotOfMovieEx1"},
};
// clang-format on
RegisterHandlers(functions);
}
CAPS_SS::~CAPS_SS() = default;
} // namespace Service::Capture

View File

@@ -0,0 +1,21 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Kernel {
class HLERequestContext;
}
namespace Service::Capture {
class CAPS_SS final : public ServiceFramework<CAPS_SS> {
public:
explicit CAPS_SS();
~CAPS_SS() override;
};
} // namespace Service::Capture

View File

@@ -0,0 +1,22 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/caps/caps_su.h"
namespace Service::Capture {
CAPS_SU::CAPS_SU() : ServiceFramework("caps:su") {
// clang-format off
static const FunctionInfo functions[] = {
{201, nullptr, "SaveScreenShot"},
{203, nullptr, "SaveScreenShotEx0"},
};
// clang-format on
RegisterHandlers(functions);
}
CAPS_SU::~CAPS_SU() = default;
} // namespace Service::Capture

View File

@@ -0,0 +1,21 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Kernel {
class HLERequestContext;
}
namespace Service::Capture {
class CAPS_SU final : public ServiceFramework<CAPS_SU> {
public:
explicit CAPS_SU();
~CAPS_SU() override;
};
} // namespace Service::Capture

View File

@@ -0,0 +1,76 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/caps/caps.h"
#include "core/hle/service/caps/caps_u.h"
namespace Service::Capture {
class IAlbumAccessorApplicationSession final
: public ServiceFramework<IAlbumAccessorApplicationSession> {
public:
explicit IAlbumAccessorApplicationSession()
: ServiceFramework{"IAlbumAccessorApplicationSession"} {
// clang-format off
static const FunctionInfo functions[] = {
{2001, nullptr, "OpenAlbumMovieReadStream"},
{2002, nullptr, "CloseAlbumMovieReadStream"},
{2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"},
{2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"},
{2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"},
};
// clang-format on
RegisterHandlers(functions);
}
};
CAPS_U::CAPS_U() : ServiceFramework("caps:u") {
// clang-format off
static const FunctionInfo functions[] = {
{31, nullptr, "GetShimLibraryVersion"},
{32, nullptr, "SetShimLibraryVersion"},
{102, &CAPS_U::GetAlbumContentsFileListForApplication, "GetAlbumContentsFileListForApplication"},
{103, nullptr, "DeleteAlbumContentsFileForApplication"},
{104, nullptr, "GetAlbumContentsFileSizeForApplication"},
{105, nullptr, "DeleteAlbumFileByAruidForDebug"},
{110, nullptr, "LoadAlbumContentsFileScreenShotImageForApplication"},
{120, nullptr, "LoadAlbumContentsFileThumbnailImageForApplication"},
{130, nullptr, "PrecheckToCreateContentsForApplication"},
{140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"},
{141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"},
{142, nullptr, "GetAlbumFileList3AaeAruid"},
{143, nullptr, "GetAlbumFileList4AaeUidAruid"},
{60002, nullptr, "OpenAccessorSessionForApplication"},
};
// clang-format on
RegisterHandlers(functions);
}
CAPS_U::~CAPS_U() = default;
void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx) {
// Takes a type-0x6 output buffer containing an array of ApplicationAlbumFileEntry, a PID, an
// u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total
// output entries (which is copied to a s32 by official SW).
IPC::RequestParser rp{ctx};
[[maybe_unused]] const auto application_album_file_entries = rp.PopRaw<std::array<u8, 0x30>>();
const auto pid = rp.Pop<s32>();
const auto content_type = rp.PopRaw<ContentType>();
[[maybe_unused]] const auto start_datetime = rp.PopRaw<AlbumFileDateTime>();
[[maybe_unused]] const auto end_datetime = rp.PopRaw<AlbumFileDateTime>();
const auto applet_resource_user_id = rp.Pop<u64>();
LOG_WARNING(Service_Capture,
"(STUBBED) called. pid={}, content_type={}, applet_resource_user_id={}", pid,
content_type, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<s32>(0);
}
} // namespace Service::Capture

View File

@@ -0,0 +1,24 @@
// Copyright 2020 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Kernel {
class HLERequestContext;
}
namespace Service::Capture {
class CAPS_U final : public ServiceFramework<CAPS_U> {
public:
explicit CAPS_U();
~CAPS_U() override;
private:
void GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Capture

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, nullptr, "InitializeSevenSixAxisSensor"},
{306, &Hid::InitializeSevenSixAxisSensor, "InitializeSevenSixAxisSensor"},
{307, nullptr, "FinalizeSevenSixAxisSensor"},
{308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
{309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
@@ -853,6 +853,13 @@ void Hid::SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
void Hid::InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
class HidDbg final : public ServiceFramework<HidDbg> {
public:
explicit HidDbg() : ServiceFramework{"hid:dbg"} {

View File

@@ -128,6 +128,7 @@ private:
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
void InitializeSevenSixAxisSensor(Kernel::HLERequestContext& ctx);
std::shared_ptr<IAppletResource> applet_resource;
Core::System& system;

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

View File

@@ -308,6 +308,35 @@ void Module::Interface::GetClockSnapshotFromSystemClockContext(Kernel::HLEReques
ctx.WriteBuffer(&clock_snapshot, sizeof(Clock::ClockSnapshot));
}
void Module::Interface::CalculateSpanBetween(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::RequestParser rp{ctx};
const auto snapshot_a = rp.PopRaw<Clock::ClockSnapshot>();
const auto snapshot_b = rp.PopRaw<Clock::ClockSnapshot>();
Clock::TimeSpanType time_span_type{};
s64 span{};
if (const ResultCode result{snapshot_a.steady_clock_time_point.GetSpanBetween(
snapshot_b.steady_clock_time_point, span)};
result != RESULT_SUCCESS) {
if (snapshot_a.network_time && snapshot_b.network_time) {
time_span_type =
Clock::TimeSpanType::FromSeconds(snapshot_b.network_time - snapshot_a.network_time);
} else {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_TIME_NOT_FOUND);
return;
}
} else {
time_span_type = Clock::TimeSpanType::FromSeconds(span);
}
IPC::ResponseBuilder rb{ctx, (sizeof(s64) / 4) + 2};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(time_span_type.nanoseconds);
}
void Module::Interface::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};

View File

@@ -32,6 +32,7 @@ public:
void CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx);
void GetClockSnapshot(Kernel::HLERequestContext& ctx);
void GetClockSnapshotFromSystemClockContext(Kernel::HLERequestContext& ctx);
void CalculateSpanBetween(Kernel::HLERequestContext& ctx);
void GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx);
private:

View File

@@ -210,6 +210,8 @@ if (ENABLE_VULKAN)
renderer_vulkan/vk_texture_cache.h
renderer_vulkan/vk_update_descriptor.cpp
renderer_vulkan/vk_update_descriptor.h
renderer_vulkan/wrapper.cpp
renderer_vulkan/wrapper.h
)
target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)

View File

@@ -231,18 +231,6 @@ enum class AtomicOp : u64 {
Or = 6,
Xor = 7,
Exch = 8,
};
enum class GlobalAtomicOp : u64 {
Add = 0,
Min = 1,
Max = 2,
Inc = 3,
Dec = 4,
And = 5,
Or = 6,
Xor = 7,
Exch = 8,
SafeAdd = 10,
};
@@ -1001,7 +989,7 @@ union Instruction {
} stg;
union {
BitField<52, 4, GlobalAtomicOp> operation;
BitField<52, 4, AtomicOp> operation;
BitField<49, 3, GlobalAtomicType> type;
BitField<28, 20, s64> offset;
} atom;

View File

@@ -7,6 +7,7 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/frontend/emu_window.h"
#include "core/memory.h"
#include "video_core/engines/fermi_2d.h"
#include "video_core/engines/kepler_compute.h"
@@ -16,14 +17,15 @@
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
namespace Tegra {
MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
GPU::GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async)
: system{system}, renderer{renderer}, is_async{is_async} {
auto& rasterizer{renderer.Rasterizer()};
GPU::GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_, bool is_async)
: system{system}, renderer{std::move(renderer_)}, is_async{is_async} {
auto& rasterizer{renderer->Rasterizer()};
memory_manager = std::make_unique<Tegra::MemoryManager>(system, rasterizer);
dma_pusher = std::make_unique<Tegra::DmaPusher>(*this);
maxwell_3d = std::make_unique<Engines::Maxwell3D>(system, rasterizer, *memory_manager);
@@ -137,7 +139,7 @@ u64 GPU::GetTicks() const {
}
void GPU::FlushCommands() {
renderer.Rasterizer().FlushCommands();
renderer->Rasterizer().FlushCommands();
}
// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence

View File

@@ -25,8 +25,11 @@ inline u8* FromCacheAddr(CacheAddr cache_addr) {
}
namespace Core {
class System;
namespace Frontend {
class EmuWindow;
}
class System;
} // namespace Core
namespace VideoCore {
class RendererBase;
@@ -129,7 +132,8 @@ class MemoryManager;
class GPU {
public:
explicit GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async);
explicit GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
bool is_async);
virtual ~GPU();
@@ -174,6 +178,14 @@ public:
/// Returns a reference to the GPU DMA pusher.
Tegra::DmaPusher& DmaPusher();
VideoCore::RendererBase& Renderer() {
return *renderer;
}
const VideoCore::RendererBase& Renderer() const {
return *renderer;
}
// Waits for the GPU to finish working
virtual void WaitIdle() const = 0;
@@ -287,7 +299,7 @@ private:
protected:
std::unique_ptr<Tegra::DmaPusher> dma_pusher;
Core::System& system;
VideoCore::RendererBase& renderer;
std::unique_ptr<VideoCore::RendererBase> renderer;
private:
std::unique_ptr<Tegra::MemoryManager> memory_manager;

View File

@@ -10,13 +10,16 @@
namespace VideoCommon {
GPUAsynch::GPUAsynch(Core::System& system, VideoCore::RendererBase& renderer)
: GPU(system, renderer, true), gpu_thread{system} {}
GPUAsynch::GPUAsynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer_,
std::unique_ptr<Core::Frontend::GraphicsContext>&& context)
: GPU(system, std::move(renderer_), true), gpu_thread{system}, gpu_context(std::move(context)),
cpu_context(renderer->GetRenderWindow().CreateSharedContext()) {}
GPUAsynch::~GPUAsynch() = default;
void GPUAsynch::Start() {
gpu_thread.StartThread(renderer, *dma_pusher);
cpu_context->MakeCurrent();
gpu_thread.StartThread(*renderer, *gpu_context, *dma_pusher);
}
void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {

View File

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

View File

@@ -7,12 +7,15 @@
namespace VideoCommon {
GPUSynch::GPUSynch(Core::System& system, VideoCore::RendererBase& renderer)
: GPU(system, renderer, false) {}
GPUSynch::GPUSynch(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
std::unique_ptr<Core::Frontend::GraphicsContext>&& context)
: GPU(system, std::move(renderer), false), context{std::move(context)} {}
GPUSynch::~GPUSynch() = default;
void GPUSynch::Start() {}
void GPUSynch::Start() {
context->MakeCurrent();
}
void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
dma_pusher->Push(std::move(entries));
@@ -20,19 +23,19 @@ void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
}
void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
renderer.SwapBuffers(framebuffer);
renderer->SwapBuffers(framebuffer);
}
void GPUSynch::FlushRegion(CacheAddr addr, u64 size) {
renderer.Rasterizer().FlushRegion(addr, size);
renderer->Rasterizer().FlushRegion(addr, size);
}
void GPUSynch::InvalidateRegion(CacheAddr addr, u64 size) {
renderer.Rasterizer().InvalidateRegion(addr, size);
renderer->Rasterizer().InvalidateRegion(addr, size);
}
void GPUSynch::FlushAndInvalidateRegion(CacheAddr addr, u64 size) {
renderer.Rasterizer().FlushAndInvalidateRegion(addr, size);
renderer->Rasterizer().FlushAndInvalidateRegion(addr, size);
}
} // namespace VideoCommon

View File

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

View File

@@ -5,7 +5,7 @@
#include "common/assert.h"
#include "common/microprofile.h"
#include "core/core.h"
#include "core/frontend/scope_acquire_context.h"
#include "core/frontend/emu_window.h"
#include "video_core/dma_pusher.h"
#include "video_core/gpu.h"
#include "video_core/gpu_thread.h"
@@ -14,8 +14,8 @@
namespace VideoCommon::GPUThread {
/// Runs the GPU thread
static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher,
SynchState& state) {
static void RunThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
Tegra::DmaPusher& dma_pusher, SynchState& state) {
MicroProfileOnThreadCreate("GpuThread");
// Wait for first GPU command before acquiring the window context
@@ -27,7 +27,7 @@ static void RunThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_p
return;
}
Core::Frontend::ScopeAcquireContext acquire_context{renderer.GetRenderWindow()};
auto current_context = context.Acquire();
CommandDataContainer next;
while (state.is_running) {
@@ -62,8 +62,11 @@ ThreadManager::~ThreadManager() {
thread.join();
}
void ThreadManager::StartThread(VideoCore::RendererBase& renderer, Tegra::DmaPusher& dma_pusher) {
thread = std::thread{RunThread, std::ref(renderer), std::ref(dma_pusher), std::ref(state)};
void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
Core::Frontend::GraphicsContext& context,
Tegra::DmaPusher& dma_pusher) {
thread = std::thread{RunThread, std::ref(renderer), std::ref(context), std::ref(dma_pusher),
std::ref(state)};
}
void ThreadManager::SubmitList(Tegra::CommandList&& entries) {

View File

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

View File

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

View File

@@ -444,6 +444,7 @@ void RasterizerOpenGL::Clear() {
}
SyncRasterizeEnable();
SyncStencilTestState();
if (regs.clear_flags.scissor) {
SyncScissorTest();
@@ -1052,12 +1053,8 @@ void RasterizerOpenGL::SyncStencilTestState() {
flags[Dirty::StencilTest] = false;
const auto& regs = gpu.regs;
if (!regs.stencil_enable) {
glDisable(GL_STENCIL_TEST);
return;
}
oglEnable(GL_STENCIL_TEST, regs.stencil_enable);
glEnable(GL_STENCIL_TEST);
glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func),
regs.stencil_front_func_ref, regs.stencil_front_func_mask);
glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op_fail),

View File

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

View File

@@ -2114,6 +2114,10 @@ private:
template <const std::string_view& opname, Type type>
Expression Atomic(Operation operation) {
if ((opname == Func::Min || opname == Func::Max) && type == Type::Int) {
UNIMPLEMENTED_MSG("Unimplemented Min & Max for atomic operations");
return {};
}
return {fmt::format("atomic{}({}, {})", opname, Visit(operation[0]).GetCode(),
Visit(operation[1]).As(type)),
type};
@@ -2307,6 +2311,8 @@ private:
~Func() = delete;
static constexpr std::string_view Add = "Add";
static constexpr std::string_view Min = "Min";
static constexpr std::string_view Max = "Max";
static constexpr std::string_view And = "And";
static constexpr std::string_view Or = "Or";
static constexpr std::string_view Xor = "Xor";
@@ -2457,7 +2463,21 @@ private:
&GLSLDecompiler::AtomicImage<Func::Xor>,
&GLSLDecompiler::AtomicImage<Func::Exchange>,
&GLSLDecompiler::Atomic<Func::Exchange, Type::Uint>,
&GLSLDecompiler::Atomic<Func::Add, Type::Uint>,
&GLSLDecompiler::Atomic<Func::Min, Type::Uint>,
&GLSLDecompiler::Atomic<Func::Max, Type::Uint>,
&GLSLDecompiler::Atomic<Func::And, Type::Uint>,
&GLSLDecompiler::Atomic<Func::Or, Type::Uint>,
&GLSLDecompiler::Atomic<Func::Xor, Type::Uint>,
&GLSLDecompiler::Atomic<Func::Exchange, Type::Int>,
&GLSLDecompiler::Atomic<Func::Add, Type::Int>,
&GLSLDecompiler::Atomic<Func::Min, Type::Int>,
&GLSLDecompiler::Atomic<Func::Max, Type::Int>,
&GLSLDecompiler::Atomic<Func::And, Type::Int>,
&GLSLDecompiler::Atomic<Func::Or, Type::Int>,
&GLSLDecompiler::Atomic<Func::Xor, Type::Int>,
&GLSLDecompiler::Branch,
&GLSLDecompiler::BranchIndirect,

View File

@@ -30,8 +30,6 @@ namespace OpenGL {
namespace {
// If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have
// to wait on available presentation frames.
constexpr std::size_t SWAP_CHAIN_SIZE = 3;
struct Frame {
@@ -214,7 +212,7 @@ public:
std::deque<Frame*> present_queue;
Frame* previous_frame{};
FrameMailbox() : has_debug_tool{HasDebugTool()} {
FrameMailbox() {
for (auto& frame : swap_chain) {
free_queue.push(&frame);
}
@@ -285,13 +283,9 @@ public:
std::unique_lock lock{swap_chain_lock};
present_queue.push_front(frame);
present_cv.notify_one();
DebugNotifyNextFrame();
}
Frame* TryGetPresentFrame(int timeout_ms) {
DebugWaitForNextFrame();
std::unique_lock lock{swap_chain_lock};
// wait for new entries in the present_queue
present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
@@ -317,38 +311,12 @@ public:
previous_frame = frame;
return frame;
}
private:
std::mutex debug_synch_mutex;
std::condition_variable debug_synch_condition;
std::atomic_int frame_for_debug{};
const bool has_debug_tool; // When true, using a GPU debugger, so keep frames in lock-step
/// Signal that a new frame is available (called from GPU thread)
void DebugNotifyNextFrame() {
if (!has_debug_tool) {
return;
}
frame_for_debug++;
std::lock_guard lock{debug_synch_mutex};
debug_synch_condition.notify_one();
}
/// Wait for a new frame to be available (called from presentation thread)
void DebugWaitForNextFrame() {
if (!has_debug_tool) {
return;
}
const int last_frame = frame_for_debug;
std::unique_lock lock{debug_synch_mutex};
debug_synch_condition.wait(lock,
[this, last_frame] { return frame_for_debug > last_frame; });
}
};
RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system)
RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system,
Core::Frontend::GraphicsContext& context)
: VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system},
frame_mailbox{std::make_unique<FrameMailbox>()} {}
frame_mailbox{}, context{context}, has_debug_tool{HasDebugTool()} {}
RendererOpenGL::~RendererOpenGL() = default;
@@ -356,8 +324,6 @@ MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 12
MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
render_window.PollEvents();
if (!framebuffer) {
return;
}
@@ -413,6 +379,13 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
m_current_frame++;
rasterizer->TickFrame();
}
render_window.PollEvents();
if (has_debug_tool) {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
Present(0);
context.SwapBuffers();
}
}
void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
@@ -480,6 +453,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
}
void RendererOpenGL::InitOpenGLObjects() {
frame_mailbox = std::make_unique<FrameMailbox>();
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
0.0f);
@@ -692,12 +667,21 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
void RendererOpenGL::TryPresent(int timeout_ms) {
bool RendererOpenGL::TryPresent(int timeout_ms) {
if (has_debug_tool) {
LOG_DEBUG(Render_OpenGL,
"Skipping presentation because we are presenting on the main context");
return false;
}
return Present(timeout_ms);
}
bool RendererOpenGL::Present(int timeout_ms) {
const auto& layout = render_window.GetFramebufferLayout();
auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms);
if (!frame) {
LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present");
return;
return false;
}
// Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a
@@ -725,6 +709,7 @@ void RendererOpenGL::TryPresent(int timeout_ms) {
glFlush();
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
return true;
}
void RendererOpenGL::RenderScreenshot() {

View File

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

View File

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

View File

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

View File

@@ -1941,7 +1941,11 @@ private:
return {};
}
Expression AtomicAdd(Operation operation) {
template <Id (Module::*func)(Id, Id, Id, Id, Id), Type result_type,
Type value_type = result_type>
Expression Atomic(Operation operation) {
const Id type_def = GetTypeDefinition(result_type);
Id pointer;
if (const auto smem = std::get_if<SmemNode>(&*operation[0])) {
pointer = GetSharedMemoryPointer(*smem);
@@ -1949,14 +1953,15 @@ private:
pointer = GetGlobalMemoryPointer(*gmem);
} else {
UNREACHABLE();
return {Constant(t_uint, 0), Type::Uint};
return {Constant(type_def, 0), result_type};
}
const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device));
const Id semantics = Constant(t_uint, 0U);
const Id value = As(Visit(operation[1]), value_type);
const Id value = AsUint(Visit(operation[1]));
return {OpAtomicIAdd(t_uint, pointer, scope, semantics, value), Type::Uint};
const Id scope = Constant(t_uint, static_cast<u32>(spv::Scope::Device));
const Id semantics = Constant(type_def, 0);
return {(this->*func)(type_def, pointer, scope, semantics, value), result_type};
}
Expression Branch(Operation operation) {
@@ -2545,7 +2550,21 @@ private:
&SPIRVDecompiler::AtomicImageXor,
&SPIRVDecompiler::AtomicImageExchange,
&SPIRVDecompiler::AtomicAdd,
&SPIRVDecompiler::Atomic<&Module::OpAtomicExchange, Type::Uint>,
&SPIRVDecompiler::Atomic<&Module::OpAtomicIAdd, Type::Uint>,
&SPIRVDecompiler::Atomic<&Module::OpAtomicUMin, Type::Uint>,
&SPIRVDecompiler::Atomic<&Module::OpAtomicUMax, Type::Uint>,
&SPIRVDecompiler::Atomic<&Module::OpAtomicAnd, Type::Uint>,
&SPIRVDecompiler::Atomic<&Module::OpAtomicOr, Type::Uint>,
&SPIRVDecompiler::Atomic<&Module::OpAtomicXor, Type::Uint>,
&SPIRVDecompiler::Atomic<&Module::OpAtomicExchange, Type::Int>,
&SPIRVDecompiler::Atomic<&Module::OpAtomicIAdd, Type::Int>,
&SPIRVDecompiler::Atomic<&Module::OpAtomicSMin, Type::Int>,
&SPIRVDecompiler::Atomic<&Module::OpAtomicSMax, Type::Int>,
&SPIRVDecompiler::Atomic<&Module::OpAtomicAnd, Type::Int>,
&SPIRVDecompiler::Atomic<&Module::OpAtomicOr, Type::Int>,
&SPIRVDecompiler::Atomic<&Module::OpAtomicXor, Type::Int>,
&SPIRVDecompiler::Branch,
&SPIRVDecompiler::BranchIndirect,

View File

@@ -0,0 +1,750 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <exception>
#include <memory>
#include <optional>
#include <utility>
#include <vector>
#include "common/common_types.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Vulkan::vk {
namespace {
template <typename T>
bool Proc(T& result, const InstanceDispatch& dld, const char* proc_name,
VkInstance instance = nullptr) noexcept {
result = reinterpret_cast<T>(dld.vkGetInstanceProcAddr(instance, proc_name));
return result != nullptr;
}
template <typename T>
void Proc(T& result, const DeviceDispatch& dld, const char* proc_name, VkDevice device) noexcept {
result = reinterpret_cast<T>(dld.vkGetDeviceProcAddr(device, proc_name));
}
void Load(VkDevice device, DeviceDispatch& dld) noexcept {
#define X(name) Proc(dld.name, dld, #name, device)
X(vkAcquireNextImageKHR);
X(vkAllocateCommandBuffers);
X(vkAllocateDescriptorSets);
X(vkAllocateMemory);
X(vkBeginCommandBuffer);
X(vkBindBufferMemory);
X(vkBindImageMemory);
X(vkCmdBeginQuery);
X(vkCmdBeginRenderPass);
X(vkCmdBeginTransformFeedbackEXT);
X(vkCmdBindDescriptorSets);
X(vkCmdBindIndexBuffer);
X(vkCmdBindPipeline);
X(vkCmdBindTransformFeedbackBuffersEXT);
X(vkCmdBindVertexBuffers);
X(vkCmdBlitImage);
X(vkCmdClearAttachments);
X(vkCmdCopyBuffer);
X(vkCmdCopyBufferToImage);
X(vkCmdCopyImage);
X(vkCmdCopyImageToBuffer);
X(vkCmdDispatch);
X(vkCmdDraw);
X(vkCmdDrawIndexed);
X(vkCmdEndQuery);
X(vkCmdEndRenderPass);
X(vkCmdEndTransformFeedbackEXT);
X(vkCmdFillBuffer);
X(vkCmdPipelineBarrier);
X(vkCmdPushConstants);
X(vkCmdSetBlendConstants);
X(vkCmdSetCheckpointNV);
X(vkCmdSetDepthBias);
X(vkCmdSetDepthBounds);
X(vkCmdSetScissor);
X(vkCmdSetStencilCompareMask);
X(vkCmdSetStencilReference);
X(vkCmdSetStencilWriteMask);
X(vkCmdSetViewport);
X(vkCreateBuffer);
X(vkCreateBufferView);
X(vkCreateCommandPool);
X(vkCreateComputePipelines);
X(vkCreateDescriptorPool);
X(vkCreateDescriptorSetLayout);
X(vkCreateDescriptorUpdateTemplateKHR);
X(vkCreateFence);
X(vkCreateFramebuffer);
X(vkCreateGraphicsPipelines);
X(vkCreateImage);
X(vkCreateImageView);
X(vkCreatePipelineLayout);
X(vkCreateQueryPool);
X(vkCreateRenderPass);
X(vkCreateSampler);
X(vkCreateSemaphore);
X(vkCreateShaderModule);
X(vkCreateSwapchainKHR);
X(vkDestroyBuffer);
X(vkDestroyBufferView);
X(vkDestroyCommandPool);
X(vkDestroyDescriptorPool);
X(vkDestroyDescriptorSetLayout);
X(vkDestroyDescriptorUpdateTemplateKHR);
X(vkDestroyFence);
X(vkDestroyFramebuffer);
X(vkDestroyImage);
X(vkDestroyImageView);
X(vkDestroyPipeline);
X(vkDestroyPipelineLayout);
X(vkDestroyQueryPool);
X(vkDestroyRenderPass);
X(vkDestroySampler);
X(vkDestroySemaphore);
X(vkDestroyShaderModule);
X(vkDestroySwapchainKHR);
X(vkDeviceWaitIdle);
X(vkEndCommandBuffer);
X(vkFreeCommandBuffers);
X(vkFreeDescriptorSets);
X(vkFreeMemory);
X(vkGetBufferMemoryRequirements);
X(vkGetDeviceQueue);
X(vkGetFenceStatus);
X(vkGetImageMemoryRequirements);
X(vkGetQueryPoolResults);
X(vkGetQueueCheckpointDataNV);
X(vkMapMemory);
X(vkQueueSubmit);
X(vkResetFences);
X(vkResetQueryPoolEXT);
X(vkUnmapMemory);
X(vkUpdateDescriptorSetWithTemplateKHR);
X(vkUpdateDescriptorSets);
X(vkWaitForFences);
#undef X
}
} // Anonymous namespace
bool Load(InstanceDispatch& dld) noexcept {
#define X(name) Proc(dld.name, dld, #name)
return X(vkCreateInstance) && X(vkEnumerateInstanceExtensionProperties);
#undef X
}
bool Load(VkInstance instance, InstanceDispatch& dld) noexcept {
#define X(name) Proc(dld.name, dld, #name, instance)
// These functions may fail to load depending on the enabled extensions.
// Don't return a failure on these.
X(vkCreateDebugUtilsMessengerEXT);
X(vkDestroyDebugUtilsMessengerEXT);
X(vkDestroySurfaceKHR);
X(vkGetPhysicalDeviceFeatures2KHR);
X(vkGetPhysicalDeviceProperties2KHR);
X(vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
X(vkGetPhysicalDeviceSurfaceFormatsKHR);
X(vkGetPhysicalDeviceSurfacePresentModesKHR);
X(vkGetPhysicalDeviceSurfaceSupportKHR);
X(vkGetSwapchainImagesKHR);
X(vkQueuePresentKHR);
return X(vkCreateDevice) && X(vkDestroyDevice) && X(vkDestroyDevice) &&
X(vkEnumerateDeviceExtensionProperties) && X(vkEnumeratePhysicalDevices) &&
X(vkGetDeviceProcAddr) && X(vkGetPhysicalDeviceFormatProperties) &&
X(vkGetPhysicalDeviceMemoryProperties) && X(vkGetPhysicalDeviceProperties) &&
X(vkGetPhysicalDeviceQueueFamilyProperties);
#undef X
}
const char* Exception::what() const noexcept {
return ToString(result);
}
const char* ToString(VkResult result) noexcept {
switch (result) {
case VkResult::VK_SUCCESS:
return "VK_SUCCESS";
case VkResult::VK_NOT_READY:
return "VK_NOT_READY";
case VkResult::VK_TIMEOUT:
return "VK_TIMEOUT";
case VkResult::VK_EVENT_SET:
return "VK_EVENT_SET";
case VkResult::VK_EVENT_RESET:
return "VK_EVENT_RESET";
case VkResult::VK_INCOMPLETE:
return "VK_INCOMPLETE";
case VkResult::VK_ERROR_OUT_OF_HOST_MEMORY:
return "VK_ERROR_OUT_OF_HOST_MEMORY";
case VkResult::VK_ERROR_OUT_OF_DEVICE_MEMORY:
return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
case VkResult::VK_ERROR_INITIALIZATION_FAILED:
return "VK_ERROR_INITIALIZATION_FAILED";
case VkResult::VK_ERROR_DEVICE_LOST:
return "VK_ERROR_DEVICE_LOST";
case VkResult::VK_ERROR_MEMORY_MAP_FAILED:
return "VK_ERROR_MEMORY_MAP_FAILED";
case VkResult::VK_ERROR_LAYER_NOT_PRESENT:
return "VK_ERROR_LAYER_NOT_PRESENT";
case VkResult::VK_ERROR_EXTENSION_NOT_PRESENT:
return "VK_ERROR_EXTENSION_NOT_PRESENT";
case VkResult::VK_ERROR_FEATURE_NOT_PRESENT:
return "VK_ERROR_FEATURE_NOT_PRESENT";
case VkResult::VK_ERROR_INCOMPATIBLE_DRIVER:
return "VK_ERROR_INCOMPATIBLE_DRIVER";
case VkResult::VK_ERROR_TOO_MANY_OBJECTS:
return "VK_ERROR_TOO_MANY_OBJECTS";
case VkResult::VK_ERROR_FORMAT_NOT_SUPPORTED:
return "VK_ERROR_FORMAT_NOT_SUPPORTED";
case VkResult::VK_ERROR_FRAGMENTED_POOL:
return "VK_ERROR_FRAGMENTED_POOL";
case VkResult::VK_ERROR_OUT_OF_POOL_MEMORY:
return "VK_ERROR_OUT_OF_POOL_MEMORY";
case VkResult::VK_ERROR_INVALID_EXTERNAL_HANDLE:
return "VK_ERROR_INVALID_EXTERNAL_HANDLE";
case VkResult::VK_ERROR_SURFACE_LOST_KHR:
return "VK_ERROR_SURFACE_LOST_KHR";
case VkResult::VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
case VkResult::VK_SUBOPTIMAL_KHR:
return "VK_SUBOPTIMAL_KHR";
case VkResult::VK_ERROR_OUT_OF_DATE_KHR:
return "VK_ERROR_OUT_OF_DATE_KHR";
case VkResult::VK_ERROR_INCOMPATIBLE_DISPLAY_KHR:
return "VK_ERROR_INCOMPATIBLE_DISPLAY_KHR";
case VkResult::VK_ERROR_VALIDATION_FAILED_EXT:
return "VK_ERROR_VALIDATION_FAILED_EXT";
case VkResult::VK_ERROR_INVALID_SHADER_NV:
return "VK_ERROR_INVALID_SHADER_NV";
case VkResult::VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT:
return "VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT";
case VkResult::VK_ERROR_FRAGMENTATION_EXT:
return "VK_ERROR_FRAGMENTATION_EXT";
case VkResult::VK_ERROR_NOT_PERMITTED_EXT:
return "VK_ERROR_NOT_PERMITTED_EXT";
case VkResult::VK_ERROR_INVALID_DEVICE_ADDRESS_EXT:
return "VK_ERROR_INVALID_DEVICE_ADDRESS_EXT";
case VkResult::VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT";
}
return "Unknown";
}
void Destroy(VkInstance instance, const InstanceDispatch& dld) noexcept {
dld.vkDestroyInstance(instance, nullptr);
}
void Destroy(VkDevice device, const InstanceDispatch& dld) noexcept {
dld.vkDestroyDevice(device, nullptr);
}
void Destroy(VkDevice device, VkBuffer handle, const DeviceDispatch& dld) noexcept {
dld.vkDestroyBuffer(device, handle, nullptr);
}
void Destroy(VkDevice device, VkBufferView handle, const DeviceDispatch& dld) noexcept {
dld.vkDestroyBufferView(device, handle, nullptr);
}
void Destroy(VkDevice device, VkCommandPool handle, const DeviceDispatch& dld) noexcept {
dld.vkDestroyCommandPool(device, handle, nullptr);
}
void Destroy(VkDevice device, VkDescriptorPool handle, const DeviceDispatch& dld) noexcept {
dld.vkDestroyDescriptorPool(device, handle, nullptr);
}
void Destroy(VkDevice device, VkDescriptorSetLayout handle, const DeviceDispatch& dld) noexcept {
dld.vkDestroyDescriptorSetLayout(device, handle, nullptr);
}
void Destroy(VkDevice device, VkDescriptorUpdateTemplateKHR handle,
const DeviceDispatch& dld) noexcept {
dld.vkDestroyDescriptorUpdateTemplateKHR(device, handle, nullptr);
}
void Destroy(VkDevice device, VkDeviceMemory handle, const DeviceDispatch& dld) noexcept {
dld.vkFreeMemory(device, handle, nullptr);
}
void Destroy(VkDevice device, VkFence handle, const DeviceDispatch& dld) noexcept {
dld.vkDestroyFence(device, handle, nullptr);
}
void Destroy(VkDevice device, VkFramebuffer handle, const DeviceDispatch& dld) noexcept {
dld.vkDestroyFramebuffer(device, handle, nullptr);
}
void Destroy(VkDevice device, VkImage handle, const DeviceDispatch& dld) noexcept {
dld.vkDestroyImage(device, handle, nullptr);
}
void Destroy(VkDevice device, VkImageView handle, const DeviceDispatch& dld) noexcept {
dld.vkDestroyImageView(device, handle, nullptr);
}
void Destroy(VkDevice device, VkPipeline handle, const DeviceDispatch& dld) noexcept {
dld.vkDestroyPipeline(device, handle, nullptr);
}
void Destroy(VkDevice device, VkPipelineLayout handle, const DeviceDispatch& dld) noexcept {
dld.vkDestroyPipelineLayout(device, handle, nullptr);
}
void Destroy(VkDevice device, VkQueryPool handle, const DeviceDispatch& dld) noexcept {
dld.vkDestroyQueryPool(device, handle, nullptr);
}
void Destroy(VkDevice device, VkRenderPass handle, const DeviceDispatch& dld) noexcept {
dld.vkDestroyRenderPass(device, handle, nullptr);
}
void Destroy(VkDevice device, VkSampler handle, const DeviceDispatch& dld) noexcept {
dld.vkDestroySampler(device, handle, nullptr);
}
void Destroy(VkDevice device, VkSwapchainKHR handle, const DeviceDispatch& dld) noexcept {
dld.vkDestroySwapchainKHR(device, handle, nullptr);
}
void Destroy(VkDevice device, VkSemaphore handle, const DeviceDispatch& dld) noexcept {
dld.vkDestroySemaphore(device, handle, nullptr);
}
void Destroy(VkDevice device, VkShaderModule handle, const DeviceDispatch& dld) noexcept {
dld.vkDestroyShaderModule(device, handle, nullptr);
}
void Destroy(VkInstance instance, VkDebugUtilsMessengerEXT handle,
const InstanceDispatch& dld) noexcept {
dld.vkDestroyDebugUtilsMessengerEXT(instance, handle, nullptr);
}
void Destroy(VkInstance instance, VkSurfaceKHR handle, const InstanceDispatch& dld) noexcept {
dld.vkDestroySurfaceKHR(instance, handle, nullptr);
}
VkResult Free(VkDevice device, VkDescriptorPool handle, Span<VkDescriptorSet> sets,
const DeviceDispatch& dld) noexcept {
return dld.vkFreeDescriptorSets(device, handle, sets.size(), sets.data());
}
VkResult Free(VkDevice device, VkCommandPool handle, Span<VkCommandBuffer> buffers,
const DeviceDispatch& dld) noexcept {
dld.vkFreeCommandBuffers(device, handle, buffers.size(), buffers.data());
return VK_SUCCESS;
}
Instance Instance::Create(Span<const char*> layers, Span<const char*> extensions,
InstanceDispatch& dld) noexcept {
VkApplicationInfo application_info;
application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
application_info.pNext = nullptr;
application_info.pApplicationName = "yuzu Emulator";
application_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0);
application_info.pEngineName = "yuzu Emulator";
application_info.engineVersion = VK_MAKE_VERSION(0, 1, 0);
application_info.apiVersion = VK_API_VERSION_1_1;
VkInstanceCreateInfo ci;
ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
ci.pNext = nullptr;
ci.flags = 0;
ci.pApplicationInfo = &application_info;
ci.enabledLayerCount = layers.size();
ci.ppEnabledLayerNames = layers.data();
ci.enabledExtensionCount = extensions.size();
ci.ppEnabledExtensionNames = extensions.data();
VkInstance instance;
if (dld.vkCreateInstance(&ci, nullptr, &instance) != VK_SUCCESS) {
// Failed to create the instance.
return {};
}
if (!Proc(dld.vkDestroyInstance, dld, "vkDestroyInstance", instance)) {
// We successfully created an instance but the destroy function couldn't be loaded.
// This is a good moment to panic.
return {};
}
return Instance(instance, dld);
}
std::optional<std::vector<VkPhysicalDevice>> Instance::EnumeratePhysicalDevices() {
u32 num;
if (dld->vkEnumeratePhysicalDevices(handle, &num, nullptr) != VK_SUCCESS) {
return std::nullopt;
}
std::vector<VkPhysicalDevice> physical_devices(num);
if (dld->vkEnumeratePhysicalDevices(handle, &num, physical_devices.data()) != VK_SUCCESS) {
return std::nullopt;
}
return physical_devices;
}
DebugCallback Instance::TryCreateDebugCallback(
PFN_vkDebugUtilsMessengerCallbackEXT callback) noexcept {
VkDebugUtilsMessengerCreateInfoEXT ci;
ci.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
ci.pNext = nullptr;
ci.flags = 0;
ci.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
ci.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
ci.pfnUserCallback = callback;
ci.pUserData = nullptr;
VkDebugUtilsMessengerEXT messenger;
if (dld->vkCreateDebugUtilsMessengerEXT(handle, &ci, nullptr, &messenger) != VK_SUCCESS) {
return {};
}
return DebugCallback(messenger, handle, *dld);
}
std::vector<VkCheckpointDataNV> Queue::GetCheckpointDataNV(const DeviceDispatch& dld) const {
if (!dld.vkGetQueueCheckpointDataNV) {
return {};
}
u32 num;
dld.vkGetQueueCheckpointDataNV(queue, &num, nullptr);
std::vector<VkCheckpointDataNV> checkpoints(num);
dld.vkGetQueueCheckpointDataNV(queue, &num, checkpoints.data());
return checkpoints;
}
void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const {
Check(dld->vkBindBufferMemory(owner, handle, memory, offset));
}
void Image::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const {
Check(dld->vkBindImageMemory(owner, handle, memory, offset));
}
DescriptorSets DescriptorPool::Allocate(const VkDescriptorSetAllocateInfo& ai) const {
const std::size_t num = ai.descriptorSetCount;
std::unique_ptr sets = std::make_unique<VkDescriptorSet[]>(num);
switch (const VkResult result = dld->vkAllocateDescriptorSets(owner, &ai, sets.get())) {
case VK_SUCCESS:
return DescriptorSets(std::move(sets), num, owner, handle, *dld);
case VK_ERROR_OUT_OF_POOL_MEMORY:
return {};
default:
throw Exception(result);
}
}
CommandBuffers CommandPool::Allocate(std::size_t num_buffers, VkCommandBufferLevel level) const {
VkCommandBufferAllocateInfo ai;
ai.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
ai.pNext = nullptr;
ai.commandPool = handle;
ai.level = level;
ai.commandBufferCount = static_cast<u32>(num_buffers);
std::unique_ptr buffers = std::make_unique<VkCommandBuffer[]>(num_buffers);
switch (const VkResult result = dld->vkAllocateCommandBuffers(owner, &ai, buffers.get())) {
case VK_SUCCESS:
return CommandBuffers(std::move(buffers), num_buffers, owner, handle, *dld);
case VK_ERROR_OUT_OF_POOL_MEMORY:
return {};
default:
throw Exception(result);
}
}
std::vector<VkImage> SwapchainKHR::GetImages() const {
u32 num;
Check(dld->vkGetSwapchainImagesKHR(owner, handle, &num, nullptr));
std::vector<VkImage> images(num);
Check(dld->vkGetSwapchainImagesKHR(owner, handle, &num, images.data()));
return images;
}
Device Device::Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci,
Span<const char*> enabled_extensions,
const VkPhysicalDeviceFeatures2& enabled_features,
DeviceDispatch& dld) noexcept {
VkDeviceCreateInfo ci;
ci.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
ci.pNext = &enabled_features;
ci.flags = 0;
ci.queueCreateInfoCount = queues_ci.size();
ci.pQueueCreateInfos = queues_ci.data();
ci.enabledLayerCount = 0;
ci.ppEnabledLayerNames = nullptr;
ci.enabledExtensionCount = enabled_extensions.size();
ci.ppEnabledExtensionNames = enabled_extensions.data();
ci.pEnabledFeatures = nullptr;
VkDevice device;
if (dld.vkCreateDevice(physical_device, &ci, nullptr, &device) != VK_SUCCESS) {
return {};
}
Load(device, dld);
return Device(device, dld);
}
Queue Device::GetQueue(u32 family_index) const noexcept {
VkQueue queue;
dld->vkGetDeviceQueue(handle, family_index, 0, &queue);
return Queue(queue, *dld);
}
Buffer Device::CreateBuffer(const VkBufferCreateInfo& ci) const {
VkBuffer object;
Check(dld->vkCreateBuffer(handle, &ci, nullptr, &object));
return Buffer(object, handle, *dld);
}
BufferView Device::CreateBufferView(const VkBufferViewCreateInfo& ci) const {
VkBufferView object;
Check(dld->vkCreateBufferView(handle, &ci, nullptr, &object));
return BufferView(object, handle, *dld);
}
Image Device::CreateImage(const VkImageCreateInfo& ci) const {
VkImage object;
Check(dld->vkCreateImage(handle, &ci, nullptr, &object));
return Image(object, handle, *dld);
}
ImageView Device::CreateImageView(const VkImageViewCreateInfo& ci) const {
VkImageView object;
Check(dld->vkCreateImageView(handle, &ci, nullptr, &object));
return ImageView(object, handle, *dld);
}
Semaphore Device::CreateSemaphore() const {
VkSemaphoreCreateInfo ci;
ci.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
ci.pNext = nullptr;
ci.flags = 0;
VkSemaphore object;
Check(dld->vkCreateSemaphore(handle, &ci, nullptr, &object));
return Semaphore(object, handle, *dld);
}
Fence Device::CreateFence(const VkFenceCreateInfo& ci) const {
VkFence object;
Check(dld->vkCreateFence(handle, &ci, nullptr, &object));
return Fence(object, handle, *dld);
}
DescriptorPool Device::CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const {
VkDescriptorPool object;
Check(dld->vkCreateDescriptorPool(handle, &ci, nullptr, &object));
return DescriptorPool(object, handle, *dld);
}
RenderPass Device::CreateRenderPass(const VkRenderPassCreateInfo& ci) const {
VkRenderPass object;
Check(dld->vkCreateRenderPass(handle, &ci, nullptr, &object));
return RenderPass(object, handle, *dld);
}
DescriptorSetLayout Device::CreateDescriptorSetLayout(
const VkDescriptorSetLayoutCreateInfo& ci) const {
VkDescriptorSetLayout object;
Check(dld->vkCreateDescriptorSetLayout(handle, &ci, nullptr, &object));
return DescriptorSetLayout(object, handle, *dld);
}
PipelineLayout Device::CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const {
VkPipelineLayout object;
Check(dld->vkCreatePipelineLayout(handle, &ci, nullptr, &object));
return PipelineLayout(object, handle, *dld);
}
Pipeline Device::CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci) const {
VkPipeline object;
Check(dld->vkCreateGraphicsPipelines(handle, nullptr, 1, &ci, nullptr, &object));
return Pipeline(object, handle, *dld);
}
Pipeline Device::CreateComputePipeline(const VkComputePipelineCreateInfo& ci) const {
VkPipeline object;
Check(dld->vkCreateComputePipelines(handle, nullptr, 1, &ci, nullptr, &object));
return Pipeline(object, handle, *dld);
}
Sampler Device::CreateSampler(const VkSamplerCreateInfo& ci) const {
VkSampler object;
Check(dld->vkCreateSampler(handle, &ci, nullptr, &object));
return Sampler(object, handle, *dld);
}
Framebuffer Device::CreateFramebuffer(const VkFramebufferCreateInfo& ci) const {
VkFramebuffer object;
Check(dld->vkCreateFramebuffer(handle, &ci, nullptr, &object));
return Framebuffer(object, handle, *dld);
}
CommandPool Device::CreateCommandPool(const VkCommandPoolCreateInfo& ci) const {
VkCommandPool object;
Check(dld->vkCreateCommandPool(handle, &ci, nullptr, &object));
return CommandPool(object, handle, *dld);
}
DescriptorUpdateTemplateKHR Device::CreateDescriptorUpdateTemplateKHR(
const VkDescriptorUpdateTemplateCreateInfoKHR& ci) const {
VkDescriptorUpdateTemplateKHR object;
Check(dld->vkCreateDescriptorUpdateTemplateKHR(handle, &ci, nullptr, &object));
return DescriptorUpdateTemplateKHR(object, handle, *dld);
}
QueryPool Device::CreateQueryPool(const VkQueryPoolCreateInfo& ci) const {
VkQueryPool object;
Check(dld->vkCreateQueryPool(handle, &ci, nullptr, &object));
return QueryPool(object, handle, *dld);
}
ShaderModule Device::CreateShaderModule(const VkShaderModuleCreateInfo& ci) const {
VkShaderModule object;
Check(dld->vkCreateShaderModule(handle, &ci, nullptr, &object));
return ShaderModule(object, handle, *dld);
}
SwapchainKHR Device::CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const {
VkSwapchainKHR object;
Check(dld->vkCreateSwapchainKHR(handle, &ci, nullptr, &object));
return SwapchainKHR(object, handle, *dld);
}
DeviceMemory Device::TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept {
VkDeviceMemory memory;
if (dld->vkAllocateMemory(handle, &ai, nullptr, &memory) != VK_SUCCESS) {
return {};
}
return DeviceMemory(memory, handle, *dld);
}
DeviceMemory Device::AllocateMemory(const VkMemoryAllocateInfo& ai) const {
VkDeviceMemory memory;
Check(dld->vkAllocateMemory(handle, &ai, nullptr, &memory));
return DeviceMemory(memory, handle, *dld);
}
VkMemoryRequirements Device::GetBufferMemoryRequirements(VkBuffer buffer) const noexcept {
VkMemoryRequirements requirements;
dld->vkGetBufferMemoryRequirements(handle, buffer, &requirements);
return requirements;
}
VkMemoryRequirements Device::GetImageMemoryRequirements(VkImage image) const noexcept {
VkMemoryRequirements requirements;
dld->vkGetImageMemoryRequirements(handle, image, &requirements);
return requirements;
}
void Device::UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes,
Span<VkCopyDescriptorSet> copies) const noexcept {
dld->vkUpdateDescriptorSets(handle, writes.size(), writes.data(), copies.size(), copies.data());
}
VkPhysicalDeviceProperties PhysicalDevice::GetProperties() const noexcept {
VkPhysicalDeviceProperties properties;
dld->vkGetPhysicalDeviceProperties(physical_device, &properties);
return properties;
}
void PhysicalDevice::GetProperties2KHR(VkPhysicalDeviceProperties2KHR& properties) const noexcept {
dld->vkGetPhysicalDeviceProperties2KHR(physical_device, &properties);
}
VkPhysicalDeviceFeatures PhysicalDevice::GetFeatures() const noexcept {
VkPhysicalDeviceFeatures2KHR features2;
features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
features2.pNext = nullptr;
dld->vkGetPhysicalDeviceFeatures2KHR(physical_device, &features2);
return features2.features;
}
void PhysicalDevice::GetFeatures2KHR(VkPhysicalDeviceFeatures2KHR& features) const noexcept {
dld->vkGetPhysicalDeviceFeatures2KHR(physical_device, &features);
}
VkFormatProperties PhysicalDevice::GetFormatProperties(VkFormat format) const noexcept {
VkFormatProperties properties;
dld->vkGetPhysicalDeviceFormatProperties(physical_device, format, &properties);
return properties;
}
std::vector<VkExtensionProperties> PhysicalDevice::EnumerateDeviceExtensionProperties() const {
u32 num;
dld->vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &num, nullptr);
std::vector<VkExtensionProperties> properties(num);
dld->vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &num, properties.data());
return properties;
}
std::vector<VkQueueFamilyProperties> PhysicalDevice::GetQueueFamilyProperties() const {
u32 num;
dld->vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &num, nullptr);
std::vector<VkQueueFamilyProperties> properties(num);
dld->vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &num, properties.data());
return properties;
}
bool PhysicalDevice::GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR surface) const {
VkBool32 supported;
Check(dld->vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, queue_family_index, surface,
&supported));
return supported == VK_TRUE;
}
VkSurfaceCapabilitiesKHR PhysicalDevice::GetSurfaceCapabilitiesKHR(VkSurfaceKHR surface) const
noexcept {
VkSurfaceCapabilitiesKHR capabilities;
Check(dld->vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &capabilities));
return capabilities;
}
std::vector<VkSurfaceFormatKHR> PhysicalDevice::GetSurfaceFormatsKHR(VkSurfaceKHR surface) const {
u32 num;
Check(dld->vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &num, nullptr));
std::vector<VkSurfaceFormatKHR> formats(num);
Check(
dld->vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &num, formats.data()));
return formats;
}
std::vector<VkPresentModeKHR> PhysicalDevice::GetSurfacePresentModesKHR(
VkSurfaceKHR surface) const {
u32 num;
Check(dld->vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &num, nullptr));
std::vector<VkPresentModeKHR> modes(num);
Check(dld->vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &num,
modes.data()));
return modes;
}
VkPhysicalDeviceMemoryProperties PhysicalDevice::GetMemoryProperties() const noexcept {
VkPhysicalDeviceMemoryProperties properties;
dld->vkGetPhysicalDeviceMemoryProperties(physical_device, &properties);
return properties;
}
std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProperties(
const InstanceDispatch& dld) {
u32 num;
if (dld.vkEnumerateInstanceExtensionProperties(nullptr, &num, nullptr) != VK_SUCCESS) {
return std::nullopt;
}
std::vector<VkExtensionProperties> properties(num);
if (dld.vkEnumerateInstanceExtensionProperties(nullptr, &num, properties.data()) !=
VK_SUCCESS) {
return std::nullopt;
}
return properties;
}
} // namespace Vulkan::vk

View File

@@ -0,0 +1,987 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <exception>
#include <iterator>
#include <limits>
#include <memory>
#include <optional>
#include <type_traits>
#include <utility>
#include <vector>
#define VK_NO_PROTOTYPES
#include <vulkan/vulkan.h>
#include "common/common_types.h"
namespace Vulkan::vk {
/**
* Span for Vulkan arrays.
* Based on std::span but optimized for array access instead of iterators.
* Size returns uint32_t instead of size_t to ease interaction with Vulkan functions.
*/
template <typename T>
class Span {
public:
using value_type = T;
using size_type = u32;
using difference_type = std::ptrdiff_t;
using reference = const T&;
using const_reference = const T&;
using pointer = const T*;
using const_pointer = const T*;
using iterator = const T*;
using const_iterator = const T*;
/// Construct an empty span.
constexpr Span() noexcept = default;
/// Construct a span from a single element.
constexpr Span(const T& value) noexcept : ptr{&value}, num{1} {}
/// Construct a span from a range.
template <typename Range>
// requires std::data(const Range&)
// requires std::size(const Range&)
constexpr Span(const Range& range) : ptr{std::data(range)}, num{std::size(range)} {}
/// Construct a span from a pointer and a size.
/// This is inteded for subranges.
constexpr Span(const T* ptr, std::size_t num) noexcept : ptr{ptr}, num{num} {}
/// Returns the data pointer by the span.
constexpr const T* data() const noexcept {
return ptr;
}
/// Returns the number of elements in the span.
/// @note Returns a 32 bits integer because most Vulkan functions expect this type.
constexpr u32 size() const noexcept {
return static_cast<u32>(num);
}
/// Returns true when the span is empty.
constexpr bool empty() const noexcept {
return num == 0;
}
/// Returns a reference to the element in the passed index.
/// @pre: index < size()
constexpr const T& operator[](std::size_t index) const noexcept {
return ptr[index];
}
/// Returns an iterator to the beginning of the span.
constexpr const T* begin() const noexcept {
return ptr;
}
/// Returns an iterator to the end of the span.
constexpr const T* end() const noexcept {
return ptr + num;
}
/// Returns an iterator to the beginning of the span.
constexpr const T* cbegin() const noexcept {
return ptr;
}
/// Returns an iterator to the end of the span.
constexpr const T* cend() const noexcept {
return ptr + num;
}
private:
const T* ptr = nullptr;
std::size_t num = 0;
};
/// Vulkan exception generated from a VkResult.
class Exception final : public std::exception {
public:
/// Construct the exception with a result.
/// @pre result != VK_SUCCESS
explicit Exception(VkResult result_) : result{result_} {}
virtual ~Exception() = default;
const char* what() const noexcept override;
private:
VkResult result;
};
/// Converts a VkResult enum into a rodata string
const char* ToString(VkResult) noexcept;
/// Throws a Vulkan exception if result is not success.
inline void Check(VkResult result) {
if (result != VK_SUCCESS) {
throw Exception(result);
}
}
/// Throws a Vulkan exception if result is an error.
/// @return result
inline VkResult Filter(VkResult result) {
if (result < 0) {
throw Exception(result);
}
return result;
}
/// Table holding Vulkan instance function pointers.
struct InstanceDispatch {
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
PFN_vkCreateInstance vkCreateInstance;
PFN_vkDestroyInstance vkDestroyInstance;
PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties;
PFN_vkCreateDebugUtilsMessengerEXT vkCreateDebugUtilsMessengerEXT;
PFN_vkCreateDevice vkCreateDevice;
PFN_vkDestroyDebugUtilsMessengerEXT vkDestroyDebugUtilsMessengerEXT;
PFN_vkDestroyDevice vkDestroyDevice;
PFN_vkDestroySurfaceKHR vkDestroySurfaceKHR;
PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties;
PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices;
PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
PFN_vkGetPhysicalDeviceFeatures2KHR vkGetPhysicalDeviceFeatures2KHR;
PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties;
PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR;
PFN_vkGetPhysicalDeviceQueueFamilyProperties vkGetPhysicalDeviceQueueFamilyProperties;
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR;
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
PFN_vkGetPhysicalDeviceSurfacePresentModesKHR vkGetPhysicalDeviceSurfacePresentModesKHR;
PFN_vkGetPhysicalDeviceSurfaceSupportKHR vkGetPhysicalDeviceSurfaceSupportKHR;
PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR;
PFN_vkQueuePresentKHR vkQueuePresentKHR;
};
/// Table holding Vulkan device function pointers.
struct DeviceDispatch : public InstanceDispatch {
PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR;
PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers;
PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets;
PFN_vkAllocateMemory vkAllocateMemory;
PFN_vkBeginCommandBuffer vkBeginCommandBuffer;
PFN_vkBindBufferMemory vkBindBufferMemory;
PFN_vkBindImageMemory vkBindImageMemory;
PFN_vkCmdBeginQuery vkCmdBeginQuery;
PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass;
PFN_vkCmdBeginTransformFeedbackEXT vkCmdBeginTransformFeedbackEXT;
PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets;
PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer;
PFN_vkCmdBindPipeline vkCmdBindPipeline;
PFN_vkCmdBindTransformFeedbackBuffersEXT vkCmdBindTransformFeedbackBuffersEXT;
PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers;
PFN_vkCmdBlitImage vkCmdBlitImage;
PFN_vkCmdClearAttachments vkCmdClearAttachments;
PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage;
PFN_vkCmdCopyImage vkCmdCopyImage;
PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer;
PFN_vkCmdDispatch vkCmdDispatch;
PFN_vkCmdDraw vkCmdDraw;
PFN_vkCmdDrawIndexed vkCmdDrawIndexed;
PFN_vkCmdEndQuery vkCmdEndQuery;
PFN_vkCmdEndRenderPass vkCmdEndRenderPass;
PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT;
PFN_vkCmdFillBuffer vkCmdFillBuffer;
PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier;
PFN_vkCmdPushConstants vkCmdPushConstants;
PFN_vkCmdSetBlendConstants vkCmdSetBlendConstants;
PFN_vkCmdSetCheckpointNV vkCmdSetCheckpointNV;
PFN_vkCmdSetDepthBias vkCmdSetDepthBias;
PFN_vkCmdSetDepthBounds vkCmdSetDepthBounds;
PFN_vkCmdSetScissor vkCmdSetScissor;
PFN_vkCmdSetStencilCompareMask vkCmdSetStencilCompareMask;
PFN_vkCmdSetStencilReference vkCmdSetStencilReference;
PFN_vkCmdSetStencilWriteMask vkCmdSetStencilWriteMask;
PFN_vkCmdSetViewport vkCmdSetViewport;
PFN_vkCreateBuffer vkCreateBuffer;
PFN_vkCreateBufferView vkCreateBufferView;
PFN_vkCreateCommandPool vkCreateCommandPool;
PFN_vkCreateComputePipelines vkCreateComputePipelines;
PFN_vkCreateDescriptorPool vkCreateDescriptorPool;
PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout;
PFN_vkCreateDescriptorUpdateTemplateKHR vkCreateDescriptorUpdateTemplateKHR;
PFN_vkCreateFence vkCreateFence;
PFN_vkCreateFramebuffer vkCreateFramebuffer;
PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines;
PFN_vkCreateImage vkCreateImage;
PFN_vkCreateImageView vkCreateImageView;
PFN_vkCreatePipelineLayout vkCreatePipelineLayout;
PFN_vkCreateQueryPool vkCreateQueryPool;
PFN_vkCreateRenderPass vkCreateRenderPass;
PFN_vkCreateSampler vkCreateSampler;
PFN_vkCreateSemaphore vkCreateSemaphore;
PFN_vkCreateShaderModule vkCreateShaderModule;
PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR;
PFN_vkDestroyBuffer vkDestroyBuffer;
PFN_vkDestroyBufferView vkDestroyBufferView;
PFN_vkDestroyCommandPool vkDestroyCommandPool;
PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool;
PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout;
PFN_vkDestroyDescriptorUpdateTemplateKHR vkDestroyDescriptorUpdateTemplateKHR;
PFN_vkDestroyFence vkDestroyFence;
PFN_vkDestroyFramebuffer vkDestroyFramebuffer;
PFN_vkDestroyImage vkDestroyImage;
PFN_vkDestroyImageView vkDestroyImageView;
PFN_vkDestroyPipeline vkDestroyPipeline;
PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout;
PFN_vkDestroyQueryPool vkDestroyQueryPool;
PFN_vkDestroyRenderPass vkDestroyRenderPass;
PFN_vkDestroySampler vkDestroySampler;
PFN_vkDestroySemaphore vkDestroySemaphore;
PFN_vkDestroyShaderModule vkDestroyShaderModule;
PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR;
PFN_vkDeviceWaitIdle vkDeviceWaitIdle;
PFN_vkEndCommandBuffer vkEndCommandBuffer;
PFN_vkFreeCommandBuffers vkFreeCommandBuffers;
PFN_vkFreeDescriptorSets vkFreeDescriptorSets;
PFN_vkFreeMemory vkFreeMemory;
PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
PFN_vkGetDeviceQueue vkGetDeviceQueue;
PFN_vkGetFenceStatus vkGetFenceStatus;
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
PFN_vkGetQueryPoolResults vkGetQueryPoolResults;
PFN_vkGetQueueCheckpointDataNV vkGetQueueCheckpointDataNV;
PFN_vkMapMemory vkMapMemory;
PFN_vkQueueSubmit vkQueueSubmit;
PFN_vkResetFences vkResetFences;
PFN_vkResetQueryPoolEXT vkResetQueryPoolEXT;
PFN_vkUnmapMemory vkUnmapMemory;
PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR;
PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets;
PFN_vkWaitForFences vkWaitForFences;
};
/// Loads instance agnostic function pointers.
/// @return True on success, false on error.
bool Load(InstanceDispatch&) noexcept;
/// Loads instance function pointers.
/// @return True on success, false on error.
bool Load(VkInstance, InstanceDispatch&) noexcept;
void Destroy(VkInstance, const InstanceDispatch&) noexcept;
void Destroy(VkDevice, const InstanceDispatch&) noexcept;
void Destroy(VkDevice, VkBuffer, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkBufferView, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkCommandPool, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkDescriptorPool, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkDescriptorSetLayout, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkDescriptorUpdateTemplateKHR, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkDeviceMemory, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkFence, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkFramebuffer, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkImage, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkImageView, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkPipeline, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkPipelineLayout, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkQueryPool, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkRenderPass, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkSampler, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkSwapchainKHR, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkSemaphore, const DeviceDispatch&) noexcept;
void Destroy(VkDevice, VkShaderModule, const DeviceDispatch&) noexcept;
void Destroy(VkInstance, VkDebugUtilsMessengerEXT, const InstanceDispatch&) noexcept;
void Destroy(VkInstance, VkSurfaceKHR, const InstanceDispatch&) noexcept;
VkResult Free(VkDevice, VkDescriptorPool, Span<VkDescriptorSet>, const DeviceDispatch&) noexcept;
VkResult Free(VkDevice, VkCommandPool, Span<VkCommandBuffer>, const DeviceDispatch&) noexcept;
template <typename Type, typename OwnerType, typename Dispatch>
class Handle;
/// Handle with an owning type.
/// Analogue to std::unique_ptr.
template <typename Type, typename OwnerType, typename Dispatch>
class Handle {
public:
/// Construct a handle and hold it's ownership.
explicit Handle(Type handle_, OwnerType owner_, const Dispatch& dld_) noexcept
: handle{handle_}, owner{owner_}, dld{&dld_} {}
/// Construct an empty handle.
Handle() = default;
/// Copying Vulkan objects is not supported and will never be.
Handle(const Handle&) = delete;
Handle& operator=(const Handle&) = delete;
/// Construct a handle transfering the ownership from another handle.
Handle(Handle&& rhs) noexcept
: handle{std::exchange(rhs.handle, nullptr)}, owner{rhs.owner}, dld{rhs.dld} {}
/// Assign the current handle transfering the ownership from another handle.
/// Destroys any previously held object.
Handle& operator=(Handle&& rhs) noexcept {
Release();
handle = std::exchange(rhs.handle, nullptr);
owner = rhs.owner;
dld = rhs.dld;
return *this;
}
/// Destroys the current handle if it existed.
~Handle() noexcept {
Release();
}
/// Destroys any held object.
void reset() noexcept {
Release();
handle = nullptr;
}
/// Returns the address of the held object.
/// Intended for Vulkan structures that expect a pointer to an array.
const Type* address() const noexcept {
return std::addressof(handle);
}
/// Returns the held Vulkan handle.
Type operator*() const noexcept {
return handle;
}
/// Returns true when there's a held object.
explicit operator bool() const noexcept {
return handle != nullptr;
}
protected:
Type handle = nullptr;
OwnerType owner = nullptr;
const Dispatch* dld = nullptr;
private:
/// Destroys the held object if it exists.
void Release() noexcept {
if (handle) {
Destroy(owner, handle, *dld);
}
}
};
/// Dummy type used to specify a handle has no owner.
struct NoOwner {};
/// Handle without an owning type.
/// Analogue to std::unique_ptr
template <typename Type, typename Dispatch>
class Handle<Type, NoOwner, Dispatch> {
public:
/// Construct a handle and hold it's ownership.
explicit Handle(Type handle_, const Dispatch& dld_) noexcept : handle{handle_}, dld{&dld_} {}
/// Construct an empty handle.
Handle() noexcept = default;
/// Copying Vulkan objects is not supported and will never be.
Handle(const Handle&) = delete;
Handle& operator=(const Handle&) = delete;
/// Construct a handle transfering ownership from another handle.
Handle(Handle&& rhs) noexcept : handle{std::exchange(rhs.handle, nullptr)}, dld{rhs.dld} {}
/// Assign the current handle transfering the ownership from another handle.
/// Destroys any previously held object.
Handle& operator=(Handle&& rhs) noexcept {
Release();
handle = std::exchange(rhs.handle, nullptr);
dld = rhs.dld;
return *this;
}
/// Destroys the current handle if it existed.
~Handle() noexcept {
Release();
}
/// Destroys any held object.
void reset() noexcept {
Release();
handle = nullptr;
}
/// Returns the address of the held object.
/// Intended for Vulkan structures that expect a pointer to an array.
const Type* address() const noexcept {
return std::addressof(handle);
}
/// Returns the held Vulkan handle.
Type operator*() const noexcept {
return handle;
}
/// Returns true when there's a held object.
operator bool() const noexcept {
return handle != nullptr;
}
protected:
Type handle = nullptr;
const Dispatch* dld = nullptr;
private:
/// Destroys the held object if it exists.
void Release() noexcept {
if (handle) {
Destroy(handle, *dld);
}
}
};
/// Array of a pool allocation.
/// Analogue to std::vector
template <typename AllocationType, typename PoolType>
class PoolAllocations {
public:
/// Construct an empty allocation.
PoolAllocations() = default;
/// Construct an allocation. Errors are reported through IsOutOfPoolMemory().
explicit PoolAllocations(std::unique_ptr<AllocationType[]> allocations, std::size_t num,
VkDevice device, PoolType pool, const DeviceDispatch& dld) noexcept
: allocations{std::move(allocations)}, num{num}, device{device}, pool{pool}, dld{&dld} {}
/// Copying Vulkan allocations is not supported and will never be.
PoolAllocations(const PoolAllocations&) = delete;
PoolAllocations& operator=(const PoolAllocations&) = delete;
/// Construct an allocation transfering ownership from another allocation.
PoolAllocations(PoolAllocations&& rhs) noexcept
: allocations{std::move(rhs.allocations)}, num{rhs.num}, device{rhs.device}, pool{rhs.pool},
dld{rhs.dld} {}
/// Assign an allocation transfering ownership from another allocation.
/// Releases any previously held allocation.
PoolAllocations& operator=(PoolAllocations&& rhs) noexcept {
Release();
allocations = std::move(rhs.allocations);
num = rhs.num;
device = rhs.device;
pool = rhs.pool;
dld = rhs.dld;
return *this;
}
/// Destroys any held allocation.
~PoolAllocations() {
Release();
}
/// Returns the number of allocations.
std::size_t size() const noexcept {
return num;
}
/// Returns a pointer to the array of allocations.
AllocationType const* data() const noexcept {
return allocations.get();
}
/// Returns the allocation in the specified index.
/// @pre index < size()
AllocationType operator[](std::size_t index) const noexcept {
return allocations[index];
}
/// True when a pool fails to construct.
bool IsOutOfPoolMemory() const noexcept {
return !device;
}
private:
/// Destroys the held allocations if they exist.
void Release() noexcept {
if (!allocations) {
return;
}
const Span<AllocationType> span(allocations.get(), num);
const VkResult result = Free(device, pool, span, *dld);
// There's no way to report errors from a destructor.
if (result != VK_SUCCESS) {
std::terminate();
}
}
std::unique_ptr<AllocationType[]> allocations;
std::size_t num = 0;
VkDevice device = nullptr;
PoolType pool = nullptr;
const DeviceDispatch* dld = nullptr;
};
using BufferView = Handle<VkBufferView, VkDevice, DeviceDispatch>;
using DebugCallback = Handle<VkDebugUtilsMessengerEXT, VkInstance, InstanceDispatch>;
using DescriptorSetLayout = Handle<VkDescriptorSetLayout, VkDevice, DeviceDispatch>;
using DescriptorUpdateTemplateKHR = Handle<VkDescriptorUpdateTemplateKHR, VkDevice, DeviceDispatch>;
using Framebuffer = Handle<VkFramebuffer, VkDevice, DeviceDispatch>;
using ImageView = Handle<VkImageView, VkDevice, DeviceDispatch>;
using Pipeline = Handle<VkPipeline, VkDevice, DeviceDispatch>;
using PipelineLayout = Handle<VkPipelineLayout, VkDevice, DeviceDispatch>;
using QueryPool = Handle<VkQueryPool, VkDevice, DeviceDispatch>;
using RenderPass = Handle<VkRenderPass, VkDevice, DeviceDispatch>;
using Sampler = Handle<VkSampler, VkDevice, DeviceDispatch>;
using Semaphore = Handle<VkSemaphore, VkDevice, DeviceDispatch>;
using ShaderModule = Handle<VkShaderModule, VkDevice, DeviceDispatch>;
using SurfaceKHR = Handle<VkSurfaceKHR, VkInstance, InstanceDispatch>;
using DescriptorSets = PoolAllocations<VkDescriptorSet, VkDescriptorPool>;
using CommandBuffers = PoolAllocations<VkCommandBuffer, VkCommandPool>;
/// Vulkan instance owning handle.
class Instance : public Handle<VkInstance, NoOwner, InstanceDispatch> {
using Handle<VkInstance, NoOwner, InstanceDispatch>::Handle;
public:
/// Creates a Vulkan instance. Use "operator bool" for error handling.
static Instance Create(Span<const char*> layers, Span<const char*> extensions,
InstanceDispatch& dld) noexcept;
/// Enumerates physical devices.
/// @return Physical devices and an empty handle on failure.
std::optional<std::vector<VkPhysicalDevice>> EnumeratePhysicalDevices();
/// Tries to create a debug callback messenger. Returns an empty handle on failure.
DebugCallback TryCreateDebugCallback(PFN_vkDebugUtilsMessengerCallbackEXT callback) noexcept;
};
class Queue {
public:
/// Construct an empty queue handle.
constexpr Queue() noexcept = default;
/// Construct a queue handle.
constexpr Queue(VkQueue queue, const DeviceDispatch& dld) noexcept : queue{queue}, dld{&dld} {}
/// Returns the checkpoint data.
/// @note Returns an empty vector when the function pointer is not present.
std::vector<VkCheckpointDataNV> GetCheckpointDataNV(const DeviceDispatch& dld) const;
void Submit(Span<VkSubmitInfo> submit_infos, VkFence fence) const {
Check(dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence));
}
VkResult Present(const VkPresentInfoKHR& present_info) const noexcept {
return dld->vkQueuePresentKHR(queue, &present_info);
}
private:
VkQueue queue = nullptr;
const DeviceDispatch* dld = nullptr;
};
class Buffer : public Handle<VkBuffer, VkDevice, DeviceDispatch> {
using Handle<VkBuffer, VkDevice, DeviceDispatch>::Handle;
public:
/// Attaches a memory allocation.
void BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const;
};
class Image : public Handle<VkImage, VkDevice, DeviceDispatch> {
using Handle<VkImage, VkDevice, DeviceDispatch>::Handle;
public:
/// Attaches a memory allocation.
void BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const;
};
class DeviceMemory : public Handle<VkDeviceMemory, VkDevice, DeviceDispatch> {
using Handle<VkDeviceMemory, VkDevice, DeviceDispatch>::Handle;
public:
u8* Map(VkDeviceSize offset, VkDeviceSize size) const {
void* data;
Check(dld->vkMapMemory(owner, handle, offset, size, 0, &data));
return static_cast<u8*>(data);
}
void Unmap() const noexcept {
dld->vkUnmapMemory(owner, handle);
}
};
class Fence : public Handle<VkFence, VkDevice, DeviceDispatch> {
using Handle<VkFence, VkDevice, DeviceDispatch>::Handle;
public:
VkResult Wait(u64 timeout = std::numeric_limits<u64>::max()) const noexcept {
return dld->vkWaitForFences(owner, 1, &handle, true, timeout);
}
VkResult GetStatus() const noexcept {
return dld->vkGetFenceStatus(owner, handle);
}
void Reset() const {
Check(dld->vkResetFences(owner, 1, &handle));
}
};
class DescriptorPool : public Handle<VkDescriptorPool, VkDevice, DeviceDispatch> {
using Handle<VkDescriptorPool, VkDevice, DeviceDispatch>::Handle;
public:
DescriptorSets Allocate(const VkDescriptorSetAllocateInfo& ai) const;
};
class CommandPool : public Handle<VkCommandPool, VkDevice, DeviceDispatch> {
using Handle<VkCommandPool, VkDevice, DeviceDispatch>::Handle;
public:
CommandBuffers Allocate(std::size_t num_buffers,
VkCommandBufferLevel level = VK_COMMAND_BUFFER_LEVEL_PRIMARY) const;
};
class SwapchainKHR : public Handle<VkSwapchainKHR, VkDevice, DeviceDispatch> {
using Handle<VkSwapchainKHR, VkDevice, DeviceDispatch>::Handle;
public:
std::vector<VkImage> GetImages() const;
};
class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> {
using Handle<VkDevice, NoOwner, DeviceDispatch>::Handle;
public:
static Device Create(VkPhysicalDevice physical_device, Span<VkDeviceQueueCreateInfo> queues_ci,
Span<const char*> enabled_extensions,
const VkPhysicalDeviceFeatures2& enabled_features,
DeviceDispatch& dld) noexcept;
Queue GetQueue(u32 family_index) const noexcept;
Buffer CreateBuffer(const VkBufferCreateInfo& ci) const;
BufferView CreateBufferView(const VkBufferViewCreateInfo& ci) const;
Image CreateImage(const VkImageCreateInfo& ci) const;
ImageView CreateImageView(const VkImageViewCreateInfo& ci) const;
Semaphore CreateSemaphore() const;
Fence CreateFence(const VkFenceCreateInfo& ci) const;
DescriptorPool CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const;
RenderPass CreateRenderPass(const VkRenderPassCreateInfo& ci) const;
DescriptorSetLayout CreateDescriptorSetLayout(const VkDescriptorSetLayoutCreateInfo& ci) const;
PipelineLayout CreatePipelineLayout(const VkPipelineLayoutCreateInfo& ci) const;
Pipeline CreateGraphicsPipeline(const VkGraphicsPipelineCreateInfo& ci) const;
Pipeline CreateComputePipeline(const VkComputePipelineCreateInfo& ci) const;
Sampler CreateSampler(const VkSamplerCreateInfo& ci) const;
Framebuffer CreateFramebuffer(const VkFramebufferCreateInfo& ci) const;
CommandPool CreateCommandPool(const VkCommandPoolCreateInfo& ci) const;
DescriptorUpdateTemplateKHR CreateDescriptorUpdateTemplateKHR(
const VkDescriptorUpdateTemplateCreateInfoKHR& ci) const;
QueryPool CreateQueryPool(const VkQueryPoolCreateInfo& ci) const;
ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const;
SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const;
DeviceMemory TryAllocateMemory(const VkMemoryAllocateInfo& ai) const noexcept;
DeviceMemory AllocateMemory(const VkMemoryAllocateInfo& ai) const;
VkMemoryRequirements GetBufferMemoryRequirements(VkBuffer buffer) const noexcept;
VkMemoryRequirements GetImageMemoryRequirements(VkImage image) const noexcept;
void UpdateDescriptorSets(Span<VkWriteDescriptorSet> writes,
Span<VkCopyDescriptorSet> copies) const noexcept;
void UpdateDescriptorSet(VkDescriptorSet set, VkDescriptorUpdateTemplateKHR update_template,
const void* data) const noexcept {
dld->vkUpdateDescriptorSetWithTemplateKHR(handle, set, update_template, data);
}
VkResult AcquireNextImageKHR(VkSwapchainKHR swapchain, u64 timeout, VkSemaphore semaphore,
VkFence fence, u32* image_index) const noexcept {
return dld->vkAcquireNextImageKHR(handle, swapchain, timeout, semaphore, fence,
image_index);
}
VkResult WaitIdle() const noexcept {
return dld->vkDeviceWaitIdle(handle);
}
void ResetQueryPoolEXT(VkQueryPool query_pool, u32 first, u32 count) const noexcept {
dld->vkResetQueryPoolEXT(handle, query_pool, first, count);
}
void GetQueryResults(VkQueryPool query_pool, u32 first, u32 count, std::size_t data_size,
void* data, VkDeviceSize stride, VkQueryResultFlags flags) const {
Check(dld->vkGetQueryPoolResults(handle, query_pool, first, count, data_size, data, stride,
flags));
}
template <typename T>
T GetQueryResult(VkQueryPool query_pool, u32 first, VkQueryResultFlags flags) const {
static_assert(std::is_trivially_copyable_v<T>);
T value;
GetQueryResults(query_pool, first, 1, sizeof(T), &value, sizeof(T), flags);
return value;
}
};
class PhysicalDevice {
public:
constexpr PhysicalDevice() noexcept = default;
constexpr PhysicalDevice(VkPhysicalDevice physical_device, const InstanceDispatch& dld) noexcept
: physical_device{physical_device}, dld{&dld} {}
constexpr operator VkPhysicalDevice() const noexcept {
return physical_device;
}
VkPhysicalDeviceProperties GetProperties() const noexcept;
void GetProperties2KHR(VkPhysicalDeviceProperties2KHR&) const noexcept;
VkPhysicalDeviceFeatures GetFeatures() const noexcept;
void GetFeatures2KHR(VkPhysicalDeviceFeatures2KHR&) const noexcept;
VkFormatProperties GetFormatProperties(VkFormat) const noexcept;
std::vector<VkExtensionProperties> EnumerateDeviceExtensionProperties() const;
std::vector<VkQueueFamilyProperties> GetQueueFamilyProperties() const;
bool GetSurfaceSupportKHR(u32 queue_family_index, VkSurfaceKHR) const;
VkSurfaceCapabilitiesKHR GetSurfaceCapabilitiesKHR(VkSurfaceKHR) const noexcept;
std::vector<VkSurfaceFormatKHR> GetSurfaceFormatsKHR(VkSurfaceKHR) const;
std::vector<VkPresentModeKHR> GetSurfacePresentModesKHR(VkSurfaceKHR) const;
VkPhysicalDeviceMemoryProperties GetMemoryProperties() const noexcept;
private:
VkPhysicalDevice physical_device = nullptr;
const InstanceDispatch* dld = nullptr;
};
class CommandBuffer {
public:
CommandBuffer() noexcept = default;
explicit CommandBuffer(VkCommandBuffer handle, const DeviceDispatch& dld) noexcept
: handle{handle}, dld{&dld} {}
const VkCommandBuffer* address() const noexcept {
return &handle;
}
void Begin(const VkCommandBufferBeginInfo& begin_info) const {
Check(dld->vkBeginCommandBuffer(handle, &begin_info));
}
void End() const {
Check(dld->vkEndCommandBuffer(handle));
}
void BeginRenderPass(const VkRenderPassBeginInfo& renderpass_bi,
VkSubpassContents contents) const noexcept {
dld->vkCmdBeginRenderPass(handle, &renderpass_bi, contents);
}
void EndRenderPass() const noexcept {
dld->vkCmdEndRenderPass(handle);
}
void BeginQuery(VkQueryPool query_pool, u32 query, VkQueryControlFlags flags) const noexcept {
dld->vkCmdBeginQuery(handle, query_pool, query, flags);
}
void EndQuery(VkQueryPool query_pool, u32 query) const noexcept {
dld->vkCmdEndQuery(handle, query_pool, query);
}
void BindDescriptorSets(VkPipelineBindPoint bind_point, VkPipelineLayout layout, u32 first,
Span<VkDescriptorSet> sets, Span<u32> dynamic_offsets) const noexcept {
dld->vkCmdBindDescriptorSets(handle, bind_point, layout, first, sets.size(), sets.data(),
dynamic_offsets.size(), dynamic_offsets.data());
}
void BindPipeline(VkPipelineBindPoint bind_point, VkPipeline pipeline) const noexcept {
dld->vkCmdBindPipeline(handle, bind_point, pipeline);
}
void BindIndexBuffer(VkBuffer buffer, VkDeviceSize offset, VkIndexType index_type) const
noexcept {
dld->vkCmdBindIndexBuffer(handle, buffer, offset, index_type);
}
void BindVertexBuffers(u32 first, u32 count, const VkBuffer* buffers,
const VkDeviceSize* offsets) const noexcept {
dld->vkCmdBindVertexBuffers(handle, first, count, buffers, offsets);
}
void BindVertexBuffer(u32 binding, VkBuffer buffer, VkDeviceSize offset) const noexcept {
BindVertexBuffers(binding, 1, &buffer, &offset);
}
void Draw(u32 vertex_count, u32 instance_count, u32 first_vertex, u32 first_instance) const
noexcept {
dld->vkCmdDraw(handle, vertex_count, instance_count, first_vertex, first_instance);
}
void DrawIndexed(u32 index_count, u32 instance_count, u32 first_index, u32 vertex_offset,
u32 first_instance) const noexcept {
dld->vkCmdDrawIndexed(handle, index_count, instance_count, first_index, vertex_offset,
first_instance);
}
void ClearAttachments(Span<VkClearAttachment> attachments, Span<VkClearRect> rects) const
noexcept {
dld->vkCmdClearAttachments(handle, attachments.size(), attachments.data(), rects.size(),
rects.data());
}
void BlitImage(VkImage src_image, VkImageLayout src_layout, VkImage dst_image,
VkImageLayout dst_layout, Span<VkImageBlit> regions, VkFilter filter) const
noexcept {
dld->vkCmdBlitImage(handle, src_image, src_layout, dst_image, dst_layout, regions.size(),
regions.data(), filter);
}
void Dispatch(u32 x, u32 y, u32 z) const noexcept {
dld->vkCmdDispatch(handle, x, y, z);
}
void PipelineBarrier(VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask,
VkDependencyFlags dependency_flags, Span<VkMemoryBarrier> memory_barriers,
Span<VkBufferMemoryBarrier> buffer_barriers,
Span<VkImageMemoryBarrier> image_barriers) const noexcept {
dld->vkCmdPipelineBarrier(handle, src_stage_mask, dst_stage_mask, dependency_flags,
memory_barriers.size(), memory_barriers.data(),
buffer_barriers.size(), buffer_barriers.data(),
image_barriers.size(), image_barriers.data());
}
void CopyBufferToImage(VkBuffer src_buffer, VkImage dst_image, VkImageLayout dst_image_layout,
Span<VkBufferImageCopy> regions) const noexcept {
dld->vkCmdCopyBufferToImage(handle, src_buffer, dst_image, dst_image_layout, regions.size(),
regions.data());
}
void CopyBuffer(VkBuffer src_buffer, VkBuffer dst_buffer, Span<VkBufferCopy> regions) const
noexcept {
dld->vkCmdCopyBuffer(handle, src_buffer, dst_buffer, regions.size(), regions.data());
}
void CopyImage(VkImage src_image, VkImageLayout src_layout, VkImage dst_image,
VkImageLayout dst_layout, Span<VkImageCopy> regions) const noexcept {
dld->vkCmdCopyImage(handle, src_image, src_layout, dst_image, dst_layout, regions.size(),
regions.data());
}
void CopyImageToBuffer(VkImage src_image, VkImageLayout src_layout, VkBuffer dst_buffer,
Span<VkBufferImageCopy> regions) const noexcept {
dld->vkCmdCopyImageToBuffer(handle, src_image, src_layout, dst_buffer, regions.size(),
regions.data());
}
void FillBuffer(VkBuffer dst_buffer, VkDeviceSize dst_offset, VkDeviceSize size, u32 data) const
noexcept {
dld->vkCmdFillBuffer(handle, dst_buffer, dst_offset, size, data);
}
void PushConstants(VkPipelineLayout layout, VkShaderStageFlags flags, u32 offset, u32 size,
const void* values) const noexcept {
dld->vkCmdPushConstants(handle, layout, flags, offset, size, values);
}
void SetCheckpointNV(const void* checkpoint_marker) const noexcept {
dld->vkCmdSetCheckpointNV(handle, checkpoint_marker);
}
void SetViewport(u32 first, Span<VkViewport> viewports) const noexcept {
dld->vkCmdSetViewport(handle, first, viewports.size(), viewports.data());
}
void SetScissor(u32 first, Span<VkRect2D> scissors) const noexcept {
dld->vkCmdSetScissor(handle, first, scissors.size(), scissors.data());
}
void SetBlendConstants(const float blend_constants[4]) const noexcept {
dld->vkCmdSetBlendConstants(handle, blend_constants);
}
void SetStencilCompareMask(VkStencilFaceFlags face_mask, u32 compare_mask) const noexcept {
dld->vkCmdSetStencilCompareMask(handle, face_mask, compare_mask);
}
void SetStencilReference(VkStencilFaceFlags face_mask, u32 reference) const noexcept {
dld->vkCmdSetStencilReference(handle, face_mask, reference);
}
void SetStencilWriteMask(VkStencilFaceFlags face_mask, u32 write_mask) const noexcept {
dld->vkCmdSetStencilWriteMask(handle, face_mask, write_mask);
}
void SetDepthBias(float constant_factor, float clamp, float slope_factor) const noexcept {
dld->vkCmdSetDepthBias(handle, constant_factor, clamp, slope_factor);
}
void SetDepthBounds(float min_depth_bounds, float max_depth_bounds) const noexcept {
dld->vkCmdSetDepthBounds(handle, min_depth_bounds, max_depth_bounds);
}
void BindTransformFeedbackBuffersEXT(u32 first, u32 count, const VkBuffer* buffers,
const VkDeviceSize* offsets,
const VkDeviceSize* sizes) const noexcept {
dld->vkCmdBindTransformFeedbackBuffersEXT(handle, first, count, buffers, offsets, sizes);
}
void BeginTransformFeedbackEXT(u32 first_counter_buffer, u32 counter_buffers_count,
const VkBuffer* counter_buffers,
const VkDeviceSize* counter_buffer_offsets) const noexcept {
dld->vkCmdBeginTransformFeedbackEXT(handle, first_counter_buffer, counter_buffers_count,
counter_buffers, counter_buffer_offsets);
}
void EndTransformFeedbackEXT(u32 first_counter_buffer, u32 counter_buffers_count,
const VkBuffer* counter_buffers,
const VkDeviceSize* counter_buffer_offsets) const noexcept {
dld->vkCmdEndTransformFeedbackEXT(handle, first_counter_buffer, counter_buffers_count,
counter_buffers, counter_buffer_offsets);
}
private:
VkCommandBuffer handle;
const DeviceDispatch* dld;
};
std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProperties(
const InstanceDispatch& dld);
} // namespace Vulkan::vk

View File

@@ -235,34 +235,30 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) {
case OpCode::Id::LEA_IMM:
case OpCode::Id::LEA_RZ:
case OpCode::Id::LEA_HI: {
const auto [op_a, op_b, op_c] = [&]() -> std::tuple<Node, Node, Node> {
auto [op_a, op_b, op_c] = [&]() -> std::tuple<Node, Node, Node> {
switch (opcode->get().GetId()) {
case OpCode::Id::LEA_R2: {
return {GetRegister(instr.gpr20), GetRegister(instr.gpr39),
Immediate(static_cast<u32>(instr.lea.r2.entry_a))};
}
case OpCode::Id::LEA_R1: {
const bool neg = instr.lea.r1.neg != 0;
return {GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true),
GetRegister(instr.gpr20),
Immediate(static_cast<u32>(instr.lea.r1.entry_a))};
}
case OpCode::Id::LEA_IMM: {
const bool neg = instr.lea.imm.neg != 0;
return {Immediate(static_cast<u32>(instr.lea.imm.entry_a)),
GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true),
Immediate(static_cast<u32>(instr.lea.imm.entry_b))};
}
case OpCode::Id::LEA_RZ: {
const bool neg = instr.lea.rz.neg != 0;
return {GetConstBuffer(instr.lea.rz.cb_index, instr.lea.rz.cb_offset),
GetOperandAbsNegInteger(GetRegister(instr.gpr8), false, neg, true),
Immediate(static_cast<u32>(instr.lea.rz.entry_a))};
}
case OpCode::Id::LEA_HI:
default:
UNIMPLEMENTED_MSG("Unhandled LEA subinstruction: {}", opcode->get().GetName());
@@ -275,12 +271,9 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) {
UNIMPLEMENTED_IF_MSG(instr.lea.pred48 != static_cast<u64>(Pred::UnusedIndex),
"Unhandled LEA Predicate");
const Node shifted_c =
Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, Immediate(1), op_c);
const Node mul_bc = Operation(OperationCode::IMul, NO_PRECISE, op_b, shifted_c);
const Node value = Operation(OperationCode::IAdd, NO_PRECISE, op_a, mul_bc);
SetRegister(bb, instr.gpr0, value);
Node value = Operation(OperationCode::ILogicalShiftLeft, std::move(op_a), std::move(op_c));
value = Operation(OperationCode::IAdd, std::move(op_b), std::move(value));
SetRegister(bb, instr.gpr0, std::move(value));
break;
}

View File

@@ -138,18 +138,23 @@ u32 ShaderIR::DecodeConversion(NodeBlock& bb, u32 pc) {
value = GetOperandAbsNegFloat(value, instr.conversion.abs_a, instr.conversion.negate_a);
value = [&]() {
value = [&] {
if (instr.conversion.src_size != instr.conversion.dst_size) {
// Rounding operations only matter when the source and destination conversion size
// is the same.
return value;
}
switch (instr.conversion.f2f.GetRoundingMode()) {
case Tegra::Shader::F2fRoundingOp::None:
return value;
case Tegra::Shader::F2fRoundingOp::Round:
return Operation(OperationCode::FRoundEven, PRECISE, value);
return Operation(OperationCode::FRoundEven, value);
case Tegra::Shader::F2fRoundingOp::Floor:
return Operation(OperationCode::FFloor, PRECISE, value);
return Operation(OperationCode::FFloor, value);
case Tegra::Shader::F2fRoundingOp::Ceil:
return Operation(OperationCode::FCeil, PRECISE, value);
return Operation(OperationCode::FCeil, value);
case Tegra::Shader::F2fRoundingOp::Trunc:
return Operation(OperationCode::FTrunc, PRECISE, value);
return Operation(OperationCode::FTrunc, value);
default:
UNIMPLEMENTED_MSG("Unimplemented F2F rounding mode {}",
static_cast<u32>(instr.conversion.f2f.rounding.Value()));

View File

@@ -19,7 +19,6 @@ namespace VideoCommon::Shader {
using Tegra::Shader::AtomicOp;
using Tegra::Shader::AtomicType;
using Tegra::Shader::Attribute;
using Tegra::Shader::GlobalAtomicOp;
using Tegra::Shader::GlobalAtomicType;
using Tegra::Shader::Instruction;
using Tegra::Shader::OpCode;
@@ -28,6 +27,31 @@ using Tegra::Shader::StoreType;
namespace {
Node GetAtomOperation(AtomicOp op, bool is_signed, Node memory, Node data) {
const OperationCode operation_code = [op] {
switch (op) {
case AtomicOp::Add:
return OperationCode::AtomicIAdd;
case AtomicOp::Min:
return OperationCode::AtomicIMin;
case AtomicOp::Max:
return OperationCode::AtomicIMax;
case AtomicOp::And:
return OperationCode::AtomicIAnd;
case AtomicOp::Or:
return OperationCode::AtomicIOr;
case AtomicOp::Xor:
return OperationCode::AtomicIXor;
case AtomicOp::Exch:
return OperationCode::AtomicIExchange;
default:
UNIMPLEMENTED_MSG("op={}", static_cast<int>(op));
return OperationCode::AtomicIAdd;
}
}();
return SignedOperation(operation_code, is_signed, std::move(memory), std::move(data));
}
bool IsUnaligned(Tegra::Shader::UniformType uniform_type) {
return uniform_type == Tegra::Shader::UniformType::UnsignedByte ||
uniform_type == Tegra::Shader::UniformType::UnsignedShort;
@@ -363,10 +387,13 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
break;
}
case OpCode::Id::ATOM: {
UNIMPLEMENTED_IF_MSG(instr.atom.operation != GlobalAtomicOp::Add, "operation={}",
static_cast<int>(instr.atom.operation.Value()));
UNIMPLEMENTED_IF_MSG(instr.atom.type != GlobalAtomicType::S32, "type={}",
static_cast<int>(instr.atom.type.Value()));
UNIMPLEMENTED_IF_MSG(instr.atom.operation == AtomicOp::Inc ||
instr.atom.operation == AtomicOp::Dec ||
instr.atom.operation == AtomicOp::SafeAdd,
"operation={}", static_cast<int>(instr.atom.operation.Value()));
UNIMPLEMENTED_IF_MSG(instr.atom.type == GlobalAtomicType::S64 ||
instr.atom.type == GlobalAtomicType::U64,
"type={}", static_cast<int>(instr.atom.type.Value()));
const auto [real_address, base_address, descriptor] =
TrackGlobalMemory(bb, instr, true, true);
@@ -375,25 +402,29 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
break;
}
const bool is_signed =
instr.atoms.type == AtomicType::S32 || instr.atoms.type == AtomicType::S64;
Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor);
Node value = Operation(OperationCode::AtomicAdd, std::move(gmem), GetRegister(instr.gpr20));
Node value = GetAtomOperation(static_cast<AtomicOp>(instr.atom.operation), is_signed, gmem,
GetRegister(instr.gpr20));
SetRegister(bb, instr.gpr0, std::move(value));
break;
}
case OpCode::Id::ATOMS: {
UNIMPLEMENTED_IF_MSG(instr.atoms.operation != AtomicOp::Add, "operation={}",
static_cast<int>(instr.atoms.operation.Value()));
UNIMPLEMENTED_IF_MSG(instr.atoms.type != AtomicType::U32, "type={}",
static_cast<int>(instr.atoms.type.Value()));
UNIMPLEMENTED_IF_MSG(instr.atoms.operation == AtomicOp::Inc ||
instr.atoms.operation == AtomicOp::Dec,
"operation={}", static_cast<int>(instr.atoms.operation.Value()));
UNIMPLEMENTED_IF_MSG(instr.atoms.type == AtomicType::S64 ||
instr.atoms.type == AtomicType::U64,
"type={}", static_cast<int>(instr.atoms.type.Value()));
const bool is_signed =
instr.atoms.type == AtomicType::S32 || instr.atoms.type == AtomicType::S64;
const s32 offset = instr.atoms.GetImmediateOffset();
Node address = GetRegister(instr.gpr8);
address = Operation(OperationCode::IAdd, std::move(address), Immediate(offset));
Node memory = GetSharedMemory(std::move(address));
Node data = GetRegister(instr.gpr20);
Node value = Operation(OperationCode::AtomicAdd, std::move(memory), std::move(data));
Node value =
GetAtomOperation(static_cast<AtomicOp>(instr.atoms.operation), is_signed,
GetSharedMemory(std::move(address)), GetRegister(instr.gpr20));
SetRegister(bb, instr.gpr0, std::move(value));
break;
}

View File

@@ -162,7 +162,21 @@ enum class OperationCode {
AtomicImageXor, /// (MetaImage, int[N] coords) -> void
AtomicImageExchange, /// (MetaImage, int[N] coords) -> void
AtomicAdd, /// (memory, {u}int) -> {u}int
AtomicUExchange, /// (memory, uint) -> uint
AtomicUAdd, /// (memory, uint) -> uint
AtomicUMin, /// (memory, uint) -> uint
AtomicUMax, /// (memory, uint) -> uint
AtomicUAnd, /// (memory, uint) -> uint
AtomicUOr, /// (memory, uint) -> uint
AtomicUXor, /// (memory, uint) -> uint
AtomicIExchange, /// (memory, int) -> int
AtomicIAdd, /// (memory, int) -> int
AtomicIMin, /// (memory, int) -> int
AtomicIMax, /// (memory, int) -> int
AtomicIAnd, /// (memory, int) -> int
AtomicIOr, /// (memory, int) -> int
AtomicIXor, /// (memory, int) -> int
Branch, /// (uint branch_target) -> void
BranchIndirect, /// (uint branch_target) -> void

View File

@@ -86,6 +86,20 @@ OperationCode SignedToUnsignedCode(OperationCode operation_code, bool is_signed)
return OperationCode::LogicalUNotEqual;
case OperationCode::LogicalIGreaterEqual:
return OperationCode::LogicalUGreaterEqual;
case OperationCode::AtomicIExchange:
return OperationCode::AtomicUExchange;
case OperationCode::AtomicIAdd:
return OperationCode::AtomicUAdd;
case OperationCode::AtomicIMin:
return OperationCode::AtomicUMin;
case OperationCode::AtomicIMax:
return OperationCode::AtomicUMax;
case OperationCode::AtomicIAnd:
return OperationCode::AtomicUAnd;
case OperationCode::AtomicIOr:
return OperationCode::AtomicUOr;
case OperationCode::AtomicIXor:
return OperationCode::AtomicUXor;
case OperationCode::INegate:
UNREACHABLE_MSG("Can't negate an unsigned integer");
return {};

View File

@@ -15,13 +15,13 @@
#endif
#include "video_core/video_core.h"
namespace VideoCore {
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
Core::System& system) {
namespace {
std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
Core::System& system,
Core::Frontend::GraphicsContext& context) {
switch (Settings::values.renderer_backend) {
case Settings::RendererBackend::OpenGL:
return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system);
return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context);
#ifdef HAS_VULKAN
case Settings::RendererBackend::Vulkan:
return std::make_unique<Vulkan::RendererVulkan>(emu_window, system);
@@ -30,13 +30,23 @@ std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_wind
return nullptr;
}
}
} // Anonymous namespace
std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) {
if (Settings::values.use_asynchronous_gpu_emulation) {
return std::make_unique<VideoCommon::GPUAsynch>(system, system.Renderer());
namespace VideoCore {
std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) {
auto context = emu_window.CreateSharedContext();
const auto scope = context->Acquire();
auto renderer = CreateRenderer(emu_window, system, *context);
if (!renderer->Init()) {
return nullptr;
}
return std::make_unique<VideoCommon::GPUSynch>(system, system.Renderer());
if (Settings::values.use_asynchronous_gpu_emulation) {
return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer),
std::move(context));
}
return std::make_unique<VideoCommon::GPUSynch>(system, std::move(renderer), std::move(context));
}
u16 GetResolutionScaleFactor(const RendererBase& renderer) {

View File

@@ -22,17 +22,8 @@ namespace VideoCore {
class RendererBase;
/**
* Creates a renderer instance.
*
* @note The returned renderer instance is simply allocated. Its Init()
* function still needs to be called to fully complete its setup.
*/
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
Core::System& system);
/// Creates an emulated GPU instance using the given system context.
std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system);
std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system);
u16 GetResolutionScaleFactor(const RendererBase& renderer);

View File

@@ -10,9 +10,6 @@
#include <QMessageBox>
#include <QOffscreenSurface>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLFunctions_4_3_Core>
#include <QOpenGLWindow>
#include <QPainter>
#include <QScreen>
#include <QStringList>
@@ -29,7 +26,6 @@
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/frontend/framebuffer_layout.h"
#include "core/frontend/scope_acquire_context.h"
#include "core/settings.h"
#include "input_common/keyboard.h"
#include "input_common/main.h"
@@ -39,26 +35,16 @@
#include "yuzu/bootmanager.h"
#include "yuzu/main.h"
EmuThread::EmuThread(GRenderWindow& window)
: shared_context{window.CreateSharedContext()},
context{(Settings::values.use_asynchronous_gpu_emulation && shared_context) ? *shared_context
: window} {}
EmuThread::EmuThread() = default;
EmuThread::~EmuThread() = default;
static GMainWindow* GetMainWindow() {
for (QWidget* w : qApp->topLevelWidgets()) {
if (GMainWindow* main = qobject_cast<GMainWindow*>(w)) {
return main;
}
}
return nullptr;
}
void EmuThread::run() {
MicroProfileOnThreadCreate("EmuThread");
Core::Frontend::ScopeAcquireContext acquire_context{context};
// Main process has been loaded. Make the context current to this thread and begin GPU and CPU
// execution.
Core::System::GetInstance().GPU().Start();
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
@@ -111,162 +97,156 @@ void EmuThread::run() {
#endif
}
class GGLContext : public Core::Frontend::GraphicsContext {
class OpenGLSharedContext : public Core::Frontend::GraphicsContext {
public:
explicit GGLContext(QOpenGLContext* shared_context)
: context(new QOpenGLContext(shared_context->parent())),
surface(new QOffscreenSurface(nullptr)) {
// disable vsync for any shared contexts
auto format = shared_context->format();
/// 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->setShareContext(shared_context);
context = std::make_unique<QOpenGLContext>();
context->setFormat(format);
context->create();
surface->setParent(shared_context->parent());
surface->setFormat(format);
surface->create();
if (!context->create()) {
LOG_ERROR(Frontend, "Unable to create main openGL context");
}
}
/// Create the shared contexts for rendering and presentation
explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface = nullptr) {
// disable vsync for any shared contexts
auto format = share_context->format();
format.setSwapInterval(main_surface ? Settings::values.use_vsync : 0);
context = std::make_unique<QOpenGLContext>();
context->setShareContext(share_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);
}
void MakeCurrent() override {
context->makeCurrent(surface);
if (is_current) {
return;
}
is_current = context->makeCurrent(surface);
}
void DoneCurrent() override {
context->doneCurrent();
}
private:
QOpenGLContext* context;
QOffscreenSurface* surface;
};
class ChildRenderWindow : public QWindow {
public:
ChildRenderWindow(QWindow* parent, QWidget* event_handler)
: QWindow{parent}, event_handler{event_handler} {}
virtual ~ChildRenderWindow() = default;
virtual void Present() = 0;
protected:
bool event(QEvent* event) override {
switch (event->type()) {
case QEvent::UpdateRequest:
Present();
return true;
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::FocusIn:
case QEvent::FocusOut:
case QEvent::FocusAboutToChange:
case QEvent::Enter:
case QEvent::Leave:
case QEvent::Wheel:
case QEvent::TabletMove:
case QEvent::TabletPress:
case QEvent::TabletRelease:
case QEvent::TabletEnterProximity:
case QEvent::TabletLeaveProximity:
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
case QEvent::InputMethodQuery:
case QEvent::TouchCancel:
return QCoreApplication::sendEvent(event_handler, event);
case QEvent::Drop:
GetMainWindow()->DropAction(static_cast<QDropEvent*>(event));
return true;
case QEvent::DragResponse:
case QEvent::DragEnter:
case QEvent::DragLeave:
case QEvent::DragMove:
GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event));
return true;
default:
return QWindow::event(event);
if (!is_current) {
return;
}
context->doneCurrent();
is_current = false;
}
void exposeEvent(QExposeEvent* event) override {
QWindow::requestUpdate();
QWindow::exposeEvent(event);
QOpenGLContext* GetShareContext() {
return context.get();
}
const QOpenGLContext* GetShareContext() const {
return context.get();
}
private:
QWidget* event_handler{};
// Avoid using Qt parent system here since we might move the QObjects to new threads
// As a note, this means we should avoid using slots/signals with the objects too
std::unique_ptr<QOpenGLContext> context;
std::unique_ptr<QOffscreenSurface> offscreen_surface{};
QSurface* surface;
bool is_current = false;
};
class OpenGLWindow final : public ChildRenderWindow {
class DummyContext : public Core::Frontend::GraphicsContext {};
class RenderWidget : public QWidget {
public:
OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context)
: ChildRenderWindow{parent, event_handler},
context(new QOpenGLContext(shared_context->parent())) {
// 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
explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) {
setAttribute(Qt::WA_NativeWindow);
setAttribute(Qt::WA_PaintOnScreen);
}
~OpenGLWindow() override {
context->doneCurrent();
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;
}
private:
GRenderWindow* render_window;
};
class OpenGLRenderWidget : public RenderWidget {
public:
explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {
windowHandle()->setSurfaceType(QWindow::OpenGLSurface);
}
void SetContext(std::unique_ptr<Core::Frontend::GraphicsContext>&& context_) {
context = std::move(context_);
}
void Present() override {
if (!isExposed()) {
if (!isVisible()) {
return;
}
context->makeCurrent(this);
Core::System::GetInstance().Renderer().TryPresent(100);
context->swapBuffers(this);
auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>();
f->glFinish();
QWindow::requestUpdate();
context->MakeCurrent();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
if (Core::System::GetInstance().Renderer().TryPresent(100)) {
context->SwapBuffers();
glFinish();
}
}
private:
QOpenGLContext* context{};
std::unique_ptr<Core::Frontend::GraphicsContext> context{};
};
#ifdef HAS_VULKAN
class VulkanWindow final : public ChildRenderWindow {
class VulkanRenderWidget : public RenderWidget {
public:
VulkanWindow(QWindow* parent, QWidget* event_handler, QVulkanInstance* instance)
: ChildRenderWindow{parent, event_handler} {
setSurfaceType(QSurface::SurfaceType::VulkanSurface);
setVulkanInstance(instance);
explicit VulkanRenderWidget(GRenderWindow* parent, QVulkanInstance* instance)
: RenderWidget(parent) {
windowHandle()->setSurfaceType(QWindow::VulkanSurface);
windowHandle()->setVulkanInstance(instance);
}
~VulkanWindow() override = default;
void Present() override {
// TODO(bunnei): ImplementMe
}
private:
QWidget* event_handler{};
};
#endif
GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)
GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread)
: QWidget(parent_), emu_thread(emu_thread) {
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
.arg(QString::fromUtf8(Common::g_build_name),
@@ -278,26 +258,13 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)
setLayout(layout);
InputCommon::Init();
GMainWindow* parent = GetMainWindow();
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete);
}
GRenderWindow::~GRenderWindow() {
InputCommon::Shutdown();
}
void GRenderWindow::MakeCurrent() {
if (core_context) {
core_context->MakeCurrent();
}
}
void GRenderWindow::DoneCurrent() {
if (core_context) {
core_context->DoneCurrent();
}
}
void GRenderWindow::PollEvents() {
if (!first_frame) {
first_frame = true;
@@ -309,21 +276,6 @@ bool GRenderWindow::IsShown() const {
return !isMinimized();
}
void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const {
#ifdef HAS_VULKAN
const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr");
const VkInstance instance_copy = vk_instance->vkInstance();
const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_window);
std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
std::memcpy(instance, &instance_copy, sizeof(instance_copy));
std::memcpy(surface, &surface_copy, sizeof(surface_copy));
#else
UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan");
#endif
}
// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
//
// Older versions get the window size (density independent pixels),
@@ -367,7 +319,7 @@ qreal GRenderWindow::windowPixelRatio() const {
return devicePixelRatio();
}
std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const {
std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF& pos) const {
const qreal pixel_ratio = windowPixelRatio();
return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
@@ -387,8 +339,10 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
}
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
if (event->source() == Qt::MouseEventSynthesizedBySystem)
return; // touch input is handled in TouchBeginEvent
// touch input is handled in TouchBeginEvent
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
return;
}
auto pos = event->pos();
if (event->button() == Qt::LeftButton) {
@@ -400,8 +354,10 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
}
void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
if (event->source() == Qt::MouseEventSynthesizedBySystem)
return; // touch input is handled in TouchUpdateEvent
// touch input is handled in TouchUpdateEvent
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
return;
}
auto pos = event->pos();
const auto [x, y] = ScaleTouch(pos);
@@ -410,13 +366,16 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
}
void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
if (event->source() == Qt::MouseEventSynthesizedBySystem)
return; // touch input is handled in TouchEndEvent
// touch input is handled in TouchEndEvent
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
return;
}
if (event->button() == Qt::LeftButton)
if (event->button() == Qt::LeftButton) {
this->TouchReleased();
else if (event->button() == Qt::RightButton)
} else if (event->button() == Qt::RightButton) {
InputCommon::GetMotionEmu()->EndTilt();
}
}
void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
@@ -474,9 +433,13 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {
std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
return std::make_unique<GGLContext>(QOpenGLContext::globalShareContext());
auto c = static_cast<OpenGLSharedContext*>(main_context.get());
// Bind the shared contexts to the main surface in case the backend wants to take over
// presentation
return std::make_unique<OpenGLSharedContext>(c->GetShareContext(),
child_widget->windowHandle());
}
return {};
return std::make_unique<DummyContext>();
}
bool GRenderWindow::InitRenderTarget() {
@@ -497,14 +460,11 @@ bool GRenderWindow::InitRenderTarget() {
break;
}
child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
layout()->addWidget(child_widget);
// Reset minimum required size to avoid resizing issues on the main window after restarting.
setMinimumSize(1, 1);
// Show causes the window to actually be created and the gl context as well, but we don't want
// the widget to be shown yet, so immediately hide it.
show();
hide();
resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
@@ -523,9 +483,10 @@ bool GRenderWindow::InitRenderTarget() {
void GRenderWindow::ReleaseRenderTarget() {
if (child_widget) {
layout()->removeWidget(child_widget);
delete child_widget;
child_widget->deleteLater();
child_widget = nullptr;
}
main_context.reset();
}
void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
@@ -557,24 +518,13 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal
bool GRenderWindow::InitializeOpenGL() {
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
// WA_DontShowOnScreen, WA_DeleteOnClose
QSurfaceFormat fmt;
fmt.setVersion(4, 3);
fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
// TODO: expose a setting for buffer value (ie default/single/double/triple)
fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
fmt.setSwapInterval(0);
QSurfaceFormat::setDefaultFormat(fmt);
GMainWindow* parent = GetMainWindow();
QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr;
child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext());
child_window->create();
child_widget = createWindowContainer(child_window, this);
child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
layout()->addWidget(child_widget);
core_context = CreateSharedContext();
auto child = new OpenGLRenderWidget(this);
child_widget = child;
child_widget->windowHandle()->create();
auto context = std::make_shared<OpenGLSharedContext>(child->windowHandle());
main_context = context;
child->SetContext(
std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->windowHandle()));
return true;
}
@@ -604,13 +554,10 @@ bool GRenderWindow::InitializeVulkan() {
return false;
}
GMainWindow* parent = GetMainWindow();
QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr;
child_window = new VulkanWindow(parent_win_handle, this, vk_instance.get());
child_window->create();
child_widget = createWindowContainer(child_window, this);
child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
layout()->addWidget(child_widget);
auto child = new VulkanRenderWidget(this, vk_instance.get());
child_widget = child;
child_widget->windowHandle()->create();
main_context = std::make_unique<DummyContext>();
return true;
#else
@@ -620,8 +567,24 @@ bool GRenderWindow::InitializeVulkan() {
#endif
}
void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const {
#ifdef HAS_VULKAN
const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr");
const VkInstance instance_copy = vk_instance->vkInstance();
const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_widget->windowHandle());
std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
std::memcpy(instance, &instance_copy, sizeof(instance_copy));
std::memcpy(surface, &surface_copy, sizeof(surface_copy));
#else
UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan");
#endif
}
bool GRenderWindow::LoadOpenGL() {
Core::Frontend::ScopeAcquireContext acquire_context{*this};
auto context = CreateSharedContext();
auto scope = context->Acquire();
if (!gladLoadGL()) {
QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"),
tr("Your GPU may not support OpenGL 4.3, or you do not have the "

View File

@@ -18,12 +18,10 @@
#include "core/frontend/emu_window.h"
class GRenderWindow;
class GMainWindow;
class QKeyEvent;
class QScreen;
class QTouchEvent;
class QStringList;
class QSurface;
class QOpenGLContext;
#ifdef HAS_VULKAN
class QVulkanInstance;
#endif
@@ -36,7 +34,7 @@ class EmuThread final : public QThread {
Q_OBJECT
public:
explicit EmuThread(GRenderWindow& window);
explicit EmuThread();
~EmuThread() override;
/**
@@ -90,12 +88,6 @@ private:
std::mutex running_mutex;
std::condition_variable running_cv;
/// Only used in asynchronous GPU mode
std::unique_ptr<Core::Frontend::GraphicsContext> shared_context;
/// This is shared_context in asynchronous GPU mode, core_context in synchronous GPU mode
Core::Frontend::GraphicsContext& context;
signals:
/**
* Emitted when the CPU has halted execution
@@ -124,12 +116,10 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
Q_OBJECT
public:
GRenderWindow(QWidget* parent, EmuThread* emu_thread);
GRenderWindow(GMainWindow* parent, EmuThread* emu_thread);
~GRenderWindow() override;
// EmuWindow implementation.
void MakeCurrent() override;
void DoneCurrent() override;
void PollEvents() override;
bool IsShown() const override;
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
@@ -165,6 +155,8 @@ public:
void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);
std::pair<u32, u32> ScaleTouch(const QPointF& pos) const;
public slots:
void OnEmulationStarting(EmuThread* emu_thread);
void OnEmulationStopping();
@@ -176,7 +168,6 @@ signals:
void FirstFrameDisplayed();
private:
std::pair<u32, u32> ScaleTouch(QPointF pos) const;
void TouchBeginEvent(const QTouchEvent* event);
void TouchUpdateEvent(const QTouchEvent* event);
void TouchEndEvent();
@@ -190,7 +181,10 @@ private:
EmuThread* emu_thread;
std::unique_ptr<GraphicsContext> core_context;
// Main context that will be shared with all other contexts that are requested.
// If this is used in a shared context setting, then this should not be used directly, but
// should instead be shared from
std::shared_ptr<Core::Frontend::GraphicsContext> main_context;
#ifdef HAS_VULKAN
std::unique_ptr<QVulkanInstance> vk_instance;
@@ -201,12 +195,6 @@ private:
QByteArray geometry;
/// Native window handle that backs this presentation widget
QWindow* child_window = nullptr;
/// In order to embed the window into GRenderWindow, you need to use createWindowContainer to
/// put the child_window into a widget then add it to the layout. This child_widget can be
/// parented to GRenderWindow and use Qt's lifetime system
QWidget* child_widget = nullptr;
bool first_frame = false;

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>(*render_window);
emu_thread = std::make_unique<EmuThread>();
emit EmulationStarting(emu_thread.get());
emu_thread->start();
@@ -1806,7 +1806,7 @@ void GMainWindow::ToggleWindowMode() {
// Render in the main window...
render_window->BackupGeometry();
ui.horizontalLayout->addWidget(render_window);
render_window->setFocusPolicy(Qt::ClickFocus);
render_window->setFocusPolicy(Qt::StrongFocus);
if (emulation_running) {
render_window->setVisible(true);
render_window->setFocus();
@@ -2378,7 +2378,6 @@ int main(int argc, char* argv[]) {
// Enables the core to make the qt created contexts current on std::threads
QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
QApplication app(argc, argv);
// Qt changes the locale and causes issues in float conversion using std::to_string() when

View File

@@ -37,16 +37,24 @@ public:
}
void MakeCurrent() override {
SDL_GL_MakeCurrent(window, context);
if (is_current) {
return;
}
is_current = SDL_GL_MakeCurrent(window, context) == 0;
}
void DoneCurrent() override {
if (!is_current) {
return;
}
SDL_GL_MakeCurrent(window, nullptr);
is_current = false;
}
private:
SDL_Window* window;
SDL_GLContext context;
bool is_current = false;
};
bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
@@ -148,14 +156,6 @@ EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() {
SDL_GL_DeleteContext(window_context);
}
void EmuWindow_SDL2_GL::MakeCurrent() {
core_context->MakeCurrent();
}
void EmuWindow_SDL2_GL::DoneCurrent() {
core_context->DoneCurrent();
}
void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const {
// Should not have been called from OpenGL

View File

@@ -13,8 +13,6 @@ public:
explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen);
~EmuWindow_SDL2_GL();
void MakeCurrent() override;
void DoneCurrent() override;
void Present() override;
/// Ignored in OpenGL

View File

@@ -111,14 +111,6 @@ EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() {
vkDestroyInstance(vk_instance, nullptr);
}
void EmuWindow_SDL2_VK::MakeCurrent() {
// Unused on Vulkan
}
void EmuWindow_SDL2_VK::DoneCurrent() {
// Unused on Vulkan
}
void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const {
const auto instance_proc_addr = vkGetInstanceProcAddr;

View File

@@ -13,8 +13,6 @@ public:
explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen);
~EmuWindow_SDL2_VK();
void MakeCurrent() override;
void DoneCurrent() override;
void Present() override;
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const override;

View File

@@ -230,17 +230,10 @@ int main(int argc, char** argv) {
system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL");
system.Renderer().Rasterizer().LoadDiskResources();
// Core is loaded, start the GPU (makes the GPU contexts current to this thread)
system.GPU().Start();
// Acquire render context for duration of the thread if this is the rendering thread
if (!Settings::values.use_asynchronous_gpu_emulation) {
emu_window->MakeCurrent();
}
SCOPE_EXIT({
if (!Settings::values.use_asynchronous_gpu_emulation) {
emu_window->DoneCurrent();
}
});
system.Renderer().Rasterizer().LoadDiskResources();
std::thread render_thread([&emu_window] { emu_window->Present(); });
while (emu_window->IsOpen()) {

View File

@@ -102,8 +102,6 @@ EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() {
LOG_INFO(Frontend, "yuzu-tester Version: {} | {}-{}", Common::g_build_fullname,
Common::g_scm_branch, Common::g_scm_desc);
Settings::LogSettings();
DoneCurrent();
}
EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
@@ -114,14 +112,6 @@ EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
void EmuWindow_SDL2_Hide::PollEvents() {}
void EmuWindow_SDL2_Hide::MakeCurrent() {
SDL_GL_MakeCurrent(render_window, gl_context);
}
void EmuWindow_SDL2_Hide::DoneCurrent() {
SDL_GL_MakeCurrent(render_window, nullptr);
}
bool EmuWindow_SDL2_Hide::IsShown() const {
return false;
}
@@ -129,3 +119,35 @@ bool EmuWindow_SDL2_Hide::IsShown() const {
void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const {
UNREACHABLE();
}
class SDLGLContext : public Core::Frontend::GraphicsContext {
public:
explicit SDLGLContext() {
// create a hidden window to make the shared context against
window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0,
SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
context = SDL_GL_CreateContext(window);
}
~SDLGLContext() {
DoneCurrent();
SDL_GL_DeleteContext(context);
SDL_DestroyWindow(window);
}
void MakeCurrent() override {
SDL_GL_MakeCurrent(window, context);
}
void DoneCurrent() override {
SDL_GL_MakeCurrent(window, nullptr);
}
private:
SDL_Window* window;
SDL_GLContext context;
};
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_Hide::CreateSharedContext() const {
return std::make_unique<SDLGLContext>();
}

View File

@@ -16,12 +16,6 @@ public:
/// Polls window events
void PollEvents() override;
/// Makes the graphics context current for the caller thread
void MakeCurrent() override;
/// Releases the GL context from the caller thread
void DoneCurrent() override;
/// Whether the screen is being shown or not.
bool IsShown() const override;
@@ -29,8 +23,7 @@ public:
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
void* surface) const override;
/// Whether the window is still open, and a close request hasn't yet been sent
bool IsOpen() const;
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
private:
/// Whether the GPU and driver supports the OpenGL extension required

View File

@@ -164,11 +164,6 @@ int main(int argc, char** argv) {
std::unique_ptr<EmuWindow_SDL2_Hide> emu_window{std::make_unique<EmuWindow_SDL2_Hide>()};
if (!Settings::values.use_multi_core) {
// Single core mode must acquire OpenGL context for entire emulation session
emu_window->MakeCurrent();
}
bool finished = false;
int return_value = 0;
const auto callback = [&finished,
@@ -257,6 +252,7 @@ int main(int argc, char** argv) {
system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester");
system.GPU().Start();
system.Renderer().Rasterizer().LoadDiskResources();
while (!finished) {