Compare commits
183 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aa6214feb7 | ||
|
|
fb8afee077 | ||
|
|
59e75f4372 | ||
|
|
e6f02d5725 | ||
|
|
9d8886b1a4 | ||
|
|
0d4ca5a8fc | ||
|
|
e1bd89e1c2 | ||
|
|
825a6e2615 | ||
|
|
baf91c920c | ||
|
|
f22f6b72c3 | ||
|
|
27dd542c60 | ||
|
|
5c90d060d8 | ||
|
|
0eb37de98f | ||
|
|
11774308d3 | ||
|
|
7fe52ef77f | ||
|
|
3a63ae0658 | ||
|
|
397f53dea1 | ||
|
|
affee77b70 | ||
|
|
d85ca0ab33 | ||
|
|
151ddcf419 | ||
|
|
224a75d839 | ||
|
|
b03c0536ce | ||
|
|
5b95a01463 | ||
|
|
c19425ed69 | ||
|
|
238c35b2c9 | ||
|
|
defb9642da | ||
|
|
69728e8ad5 | ||
|
|
4c72190a06 | ||
|
|
f1da3ec584 | ||
|
|
cb0a4151f8 | ||
|
|
c2665ec9c2 | ||
|
|
4f7bea403a | ||
|
|
c8f6d9effd | ||
|
|
972485ff18 | ||
|
|
93cac0d294 | ||
|
|
3dc09a6250 | ||
|
|
a2cc80b605 | ||
|
|
552f0ff267 | ||
|
|
2c780db5b9 | ||
|
|
c119473c40 | ||
|
|
a8f3a13a1f | ||
|
|
2c9879d2eb | ||
|
|
5e2f8e30e7 | ||
|
|
ffe1e2b5ec | ||
|
|
997a802bd6 | ||
|
|
b6c9fba81c | ||
|
|
5300a918c6 | ||
|
|
75795a9a63 | ||
|
|
9bd9980372 | ||
|
|
6286b8dedd | ||
|
|
8ba06aa4e1 | ||
|
|
523a709bf1 | ||
|
|
796b3319e6 | ||
|
|
dd236c6c1d | ||
|
|
10ba8d16be | ||
|
|
7a2f60df26 | ||
|
|
8a6a25e4b6 | ||
|
|
a60f34a850 | ||
|
|
a429644672 | ||
|
|
2694552b7f | ||
|
|
7413b30923 | ||
|
|
d8d392b39a | ||
|
|
60f351084a | ||
|
|
a9e4528d10 | ||
|
|
3f0b7673f0 | ||
|
|
f5cee0e885 | ||
|
|
92c8d783b3 | ||
|
|
cedbe925cd | ||
|
|
e84b760016 | ||
|
|
744b207d92 | ||
|
|
950b6dbc80 | ||
|
|
5228bd0bb9 | ||
|
|
cf9c94d401 | ||
|
|
46791c464a | ||
|
|
3194f14aca | ||
|
|
8244536f7a | ||
|
|
7617e88fb2 | ||
|
|
c310cef615 | ||
|
|
23c7dda710 | ||
|
|
e6aff11057 | ||
|
|
282adfc70b | ||
|
|
aa41fcc04e | ||
|
|
ac4154bfde | ||
|
|
f8382c9d9d | ||
|
|
6ca8637d4c | ||
|
|
497f593525 | ||
|
|
7981910746 | ||
|
|
dc4415811c | ||
|
|
4afebf26b6 | ||
|
|
5d3b228409 | ||
|
|
5a5c6d4ed8 | ||
|
|
e731c4b991 | ||
|
|
fc37672f26 | ||
|
|
977418c65b | ||
|
|
f66743cd0c | ||
|
|
d4e93cf38c | ||
|
|
bdcedc8506 | ||
|
|
22f4268c2f | ||
|
|
7051dc1902 | ||
|
|
01af036c1f | ||
|
|
63c2635e6f | ||
|
|
dbfbe352e0 | ||
|
|
e5bb5d13c4 | ||
|
|
e70451d967 | ||
|
|
81fa492825 | ||
|
|
bdddbe2daa | ||
|
|
06dea163fa | ||
|
|
bc681dc555 | ||
|
|
9418b983bd | ||
|
|
76d6178e4a | ||
|
|
38c1e77f01 | ||
|
|
b6b2e31e5e | ||
|
|
fc51ece7bf | ||
|
|
98d85cdc20 | ||
|
|
dab450ec46 | ||
|
|
351816ac38 | ||
|
|
acf328a71f | ||
|
|
9f46066bda | ||
|
|
ba9674862d | ||
|
|
ac7ee21331 | ||
|
|
56ea0f8acb | ||
|
|
716d6aee30 | ||
|
|
664fa4ea06 | ||
|
|
f5658a9fda | ||
|
|
edb9cccb36 | ||
|
|
f54d2d3114 | ||
|
|
d787856621 | ||
|
|
9fdfd58f9f | ||
|
|
cdeadd448b | ||
|
|
1c45c8086e | ||
|
|
2fd3b328ae | ||
|
|
230ac6a4e8 | ||
|
|
eae2ed6b07 | ||
|
|
38036eb1c8 | ||
|
|
e8ded20d24 | ||
|
|
53d673a7d3 | ||
|
|
311d2fc768 | ||
|
|
b16c8e0e8d | ||
|
|
7cc46a6faa | ||
|
|
ddafc99776 | ||
|
|
d64edf21bb | ||
|
|
5afc397d52 | ||
|
|
6442e02c5d | ||
|
|
8e6e55d6f8 | ||
|
|
464bd5fad7 | ||
|
|
86b1f15d9a | ||
|
|
52acb7f9a0 | ||
|
|
d91a880f11 | ||
|
|
71cc772988 | ||
|
|
a7131af7d6 | ||
|
|
8baf98e439 | ||
|
|
c5afe93dcc | ||
|
|
4373fa8042 | ||
|
|
380fc8d2e1 | ||
|
|
42cb8f1124 | ||
|
|
9b8fb3c756 | ||
|
|
d71d7d917e | ||
|
|
134f3ff9b4 | ||
|
|
3287b1247d | ||
|
|
240d45830d | ||
|
|
3377b78ea7 | ||
|
|
801fd04f75 | ||
|
|
e183820956 | ||
|
|
70a31eda62 | ||
|
|
5ed377b989 | ||
|
|
e7d97605e8 | ||
|
|
835a3d09c6 | ||
|
|
731a9a322e | ||
|
|
d3dc4e399c | ||
|
|
829f424618 | ||
|
|
a166217480 | ||
|
|
753bc2026f | ||
|
|
54681909be | ||
|
|
00607fe1e0 | ||
|
|
325977c0c6 | ||
|
|
70ff82f72d | ||
|
|
96a4abe12d | ||
|
|
93547cac68 | ||
|
|
911c56ccef | ||
|
|
465ba30d08 | ||
|
|
4dcca90ef4 | ||
|
|
38fe070d78 | ||
|
|
7cbe6748c3 |
@@ -4,7 +4,7 @@ parameters:
|
||||
version: ''
|
||||
|
||||
steps:
|
||||
- script: mkdir build && cd build && cmake -G "Visual Studio 15 2017 Win64" --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd ..
|
||||
- script: mkdir build && cd build && cmake -G "Visual Studio 16 2019" -A x64 --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd ..
|
||||
displayName: 'Configure CMake'
|
||||
- task: MSBuild@1
|
||||
displayName: 'Build'
|
||||
|
||||
@@ -45,7 +45,7 @@ stages:
|
||||
- job: build
|
||||
displayName: 'msvc'
|
||||
pool:
|
||||
vmImage: vs2017-win2016
|
||||
vmImage: windows-2019
|
||||
steps:
|
||||
- template: ./templates/sync-source.yml
|
||||
parameters:
|
||||
|
||||
@@ -22,7 +22,7 @@ stages:
|
||||
- job: build
|
||||
displayName: 'windows-msvc'
|
||||
pool:
|
||||
vmImage: vs2017-win2016
|
||||
vmImage: windows-2019
|
||||
steps:
|
||||
- template: ./templates/sync-source.yml
|
||||
parameters:
|
||||
|
||||
@@ -6,9 +6,9 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||
set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/")
|
||||
set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/")
|
||||
set(Qt5_RESOURCES_DIR "${Qt5_DIR}/../../../resources/")
|
||||
set(PLATFORMS ${DLL_DEST}platforms/)
|
||||
set(STYLES ${DLL_DEST}styles/)
|
||||
set(IMAGEFORMATS ${DLL_DEST}imageformats/)
|
||||
set(PLATFORMS ${DLL_DEST}plugins/platforms/)
|
||||
set(STYLES ${DLL_DEST}plugins/styles/)
|
||||
set(IMAGEFORMATS ${DLL_DEST}plugins/imageformats/)
|
||||
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
|
||||
icudt*.dll
|
||||
icuin*.dll
|
||||
@@ -42,11 +42,15 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||
icudtl.dat
|
||||
)
|
||||
endif ()
|
||||
|
||||
windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
|
||||
windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*)
|
||||
windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS}
|
||||
qjpeg$<$<CONFIG:Debug>:d>.*
|
||||
qgif$<$<CONFIG:Debug>:d>.*
|
||||
)
|
||||
# Create an empty qt.conf file. Qt will detect that this file exists, and use the folder that its in as the root folder.
|
||||
# This way it'll look for plugins in the root/plugins/ folder
|
||||
add_custom_command(TARGET yuzu POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E touch ${DLL_DEST}qt.conf
|
||||
)
|
||||
endfunction(copy_yuzu_Qt5_deps)
|
||||
|
||||
@@ -2,6 +2,7 @@ yuzu emulator
|
||||
=============
|
||||
[](https://travis-ci.com/yuzu-emu/yuzu)
|
||||
[](https://dev.azure.com/yuzu-emu/yuzu/)
|
||||
[](https://discord.gg/XQV6dn9)
|
||||
|
||||
yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
|
||||
|
||||
@@ -21,7 +22,7 @@ For development discussion, please join us on [Discord](https://discord.gg/XQV6d
|
||||
|
||||
Most of the development happens on GitHub. It's also where [our central repository](https://github.com/yuzu-emu/yuzu) is hosted.
|
||||
|
||||
If you want to contribute please take a look at the [Contributor's Guide](CONTRIBUTING.md) and [Developer Information](https://github.com/yuzu-emu/yuzu/wiki/Developer-Information). You should also contact any of the developers on Discord in order to know about the current state of the emulator.
|
||||
If you want to contribute please take a look at the [Contributor's Guide](https://github.com/yuzu-emu/yuzu/wiki/Contributing) and [Developer Information](https://github.com/yuzu-emu/yuzu/wiki/Developer-Information). You should also contact any of the developers on Discord in order to know about the current state of the emulator.
|
||||
|
||||
### Building
|
||||
|
||||
|
||||
6
dist/license.md
vendored
6
dist/license.md
vendored
@@ -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
|
||||
|
||||
BIN
dist/qt_themes/default/icons/16x16/checked.png
vendored
BIN
dist/qt_themes/default/icons/16x16/checked.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 451 B After Width: | Height: | Size: 657 B |
BIN
dist/qt_themes/default/icons/16x16/failed.png
vendored
BIN
dist/qt_themes/default/icons/16x16/failed.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 428 B After Width: | Height: | Size: 524 B |
6
externals/microprofile/microprofile.h
vendored
6
externals/microprofile/microprofile.h
vendored
@@ -828,7 +828,7 @@ inline MicroProfileLogEntry MicroProfileMakeLogIndex(uint64_t nBegin, MicroProfi
|
||||
MicroProfileLogEntry Entry = (nBegin<<62) | ((0x3fff&nToken)<<48) | (MP_LOG_TICK_MASK&nTick);
|
||||
int t = MicroProfileLogType(Entry);
|
||||
uint64_t nTimerIndex = MicroProfileLogTimerIndex(Entry);
|
||||
MP_ASSERT(t == nBegin);
|
||||
MP_ASSERT((uint64_t)t == nBegin);
|
||||
MP_ASSERT(nTimerIndex == (nToken&0x3fff));
|
||||
return Entry;
|
||||
|
||||
@@ -1556,10 +1556,10 @@ void MicroProfileFlip()
|
||||
|
||||
pFramePut->nFrameStartCpu = MP_TICK();
|
||||
pFramePut->nFrameStartGpu = (uint32_t)MicroProfileGpuInsertTimeStamp();
|
||||
if(pFrameNext->nFrameStartGpu != (uint64_t)-1)
|
||||
if(pFrameNext->nFrameStartGpu != -1)
|
||||
pFrameNext->nFrameStartGpu = MicroProfileGpuGetTimeStamp((uint32_t)pFrameNext->nFrameStartGpu);
|
||||
|
||||
if(pFrameCurrent->nFrameStartGpu == (uint64_t)-1)
|
||||
if(pFrameCurrent->nFrameStartGpu == -1)
|
||||
pFrameCurrent->nFrameStartGpu = pFrameNext->nFrameStartGpu + 1;
|
||||
|
||||
uint64_t nFrameStartCpu = pFrameCurrent->nFrameStartCpu;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
@@ -166,15 +165,14 @@ struct System::Impl {
|
||||
service_manager = std::make_shared<Service::SM::ServiceManager>();
|
||||
|
||||
Service::Init(service_manager, system);
|
||||
GDBStub::Init();
|
||||
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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -141,6 +141,7 @@ constexpr char target_xml[] =
|
||||
)";
|
||||
|
||||
int gdbserver_socket = -1;
|
||||
bool defer_start = false;
|
||||
|
||||
u8 command_buffer[GDB_BUFFER_SIZE];
|
||||
u32 command_length;
|
||||
@@ -1166,6 +1167,9 @@ static void RemoveBreakpoint() {
|
||||
|
||||
void HandlePacket() {
|
||||
if (!IsConnected()) {
|
||||
if (defer_start) {
|
||||
ToggleServer(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1256,6 +1260,10 @@ void ToggleServer(bool status) {
|
||||
}
|
||||
}
|
||||
|
||||
void DeferStart() {
|
||||
defer_start = true;
|
||||
}
|
||||
|
||||
static void Init(u16 port) {
|
||||
if (!server_enabled) {
|
||||
// Set the halt loop to false in case the user enabled the gdbstub mid-execution.
|
||||
@@ -1341,6 +1349,7 @@ void Shutdown() {
|
||||
if (!server_enabled) {
|
||||
return;
|
||||
}
|
||||
defer_start = false;
|
||||
|
||||
LOG_INFO(Debug_GDBStub, "Stopping GDB ...");
|
||||
if (gdbserver_socket != -1) {
|
||||
|
||||
@@ -43,6 +43,13 @@ void ToggleServer(bool status);
|
||||
/// Start the gdbstub server.
|
||||
void Init();
|
||||
|
||||
/**
|
||||
* Defer initialization of the gdbstub to the first packet processing functions.
|
||||
* This avoids a case where the gdbstub thread is frozen after initialization
|
||||
* and fails to respond in time to packets.
|
||||
*/
|
||||
void DeferStart();
|
||||
|
||||
/// Stop gdbstub server.
|
||||
void Shutdown();
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -254,6 +254,12 @@ void WebBrowser::Execute() {
|
||||
|
||||
if (status != RESULT_SUCCESS) {
|
||||
complete = true;
|
||||
|
||||
// This is a workaround in order not to softlock yuzu when an error happens during the
|
||||
// webapplet init. In order to avoid an svcBreak, the status is set to RESULT_SUCCESS
|
||||
Finalize();
|
||||
status = RESULT_SUCCESS;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
78
src/core/hle/service/caps/caps_a.cpp
Normal file
78
src/core/hle/service/caps/caps_a.cpp
Normal 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
|
||||
21
src/core/hle/service/caps/caps_a.h
Normal file
21
src/core/hle/service/caps/caps_a.h
Normal 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
|
||||
75
src/core/hle/service/caps/caps_c.cpp
Normal file
75
src/core/hle/service/caps/caps_c.cpp
Normal 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
|
||||
21
src/core/hle/service/caps/caps_c.h
Normal file
21
src/core/hle/service/caps/caps_c.h
Normal 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
|
||||
40
src/core/hle/service/caps/caps_sc.cpp
Normal file
40
src/core/hle/service/caps/caps_sc.cpp
Normal 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
|
||||
21
src/core/hle/service/caps/caps_sc.h
Normal file
21
src/core/hle/service/caps/caps_sc.h
Normal 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
|
||||
26
src/core/hle/service/caps/caps_ss.cpp
Normal file
26
src/core/hle/service/caps/caps_ss.cpp
Normal 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
|
||||
21
src/core/hle/service/caps/caps_ss.h
Normal file
21
src/core/hle/service/caps/caps_ss.h
Normal 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
|
||||
22
src/core/hle/service/caps/caps_su.cpp
Normal file
22
src/core/hle/service/caps/caps_su.cpp
Normal 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
|
||||
21
src/core/hle/service/caps/caps_su.h
Normal file
21
src/core/hle/service/caps/caps_su.h
Normal 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
|
||||
76
src/core/hle/service/caps/caps_u.cpp
Normal file
76
src/core/hle/service/caps/caps_u.cpp
Normal 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
|
||||
24
src/core/hle/service/caps/caps_u.h
Normal file
24
src/core/hle/service/caps/caps_u.h
Normal 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
|
||||
@@ -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"} {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -342,17 +342,27 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT(
|
||||
vm_manager
|
||||
.MirrorMemory(*map_address, nro_address, nro_size, Kernel::MemoryState::ModuleCode)
|
||||
.IsSuccess());
|
||||
// Mark text and read-only region as ModuleCode
|
||||
ASSERT(vm_manager
|
||||
.MirrorMemory(*map_address, nro_address, header.text_size + header.ro_size,
|
||||
Kernel::MemoryState::ModuleCode)
|
||||
.IsSuccess());
|
||||
// Mark read/write region as ModuleCodeData, which is necessary if this region is used for
|
||||
// TransferMemory (e.g. Final Fantasy VIII Remastered does this)
|
||||
ASSERT(vm_manager
|
||||
.MirrorMemory(*map_address + header.rw_offset, nro_address + header.rw_offset,
|
||||
header.rw_size, Kernel::MemoryState::ModuleCodeData)
|
||||
.IsSuccess());
|
||||
// Revoke permissions from the old memory region
|
||||
ASSERT(vm_manager.ReprotectRange(nro_address, nro_size, Kernel::VMAPermission::None)
|
||||
.IsSuccess());
|
||||
|
||||
if (bss_size > 0) {
|
||||
// Mark BSS region as ModuleCodeData, which is necessary if this region is used for
|
||||
// TransferMemory (e.g. Final Fantasy VIII Remastered does this)
|
||||
ASSERT(vm_manager
|
||||
.MirrorMemory(*map_address + nro_size, bss_address, bss_size,
|
||||
Kernel::MemoryState::ModuleCode)
|
||||
Kernel::MemoryState::ModuleCodeData)
|
||||
.IsSuccess());
|
||||
ASSERT(vm_manager.ReprotectRange(bss_address, bss_size, Kernel::VMAPermission::None)
|
||||
.IsSuccess());
|
||||
|
||||
@@ -111,6 +111,14 @@ void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
|
||||
rb.PushEnum(available_language_codes[Settings::values.language_index]);
|
||||
}
|
||||
|
||||
void SET::GetRegionCode(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_SET, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(Settings::values.region_index);
|
||||
}
|
||||
|
||||
SET::SET() : ServiceFramework("set") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
@@ -118,7 +126,7 @@ SET::SET() : ServiceFramework("set") {
|
||||
{1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"},
|
||||
{2, &SET::MakeLanguageCode, "MakeLanguageCode"},
|
||||
{3, &SET::GetAvailableLanguageCodeCount, "GetAvailableLanguageCodeCount"},
|
||||
{4, nullptr, "GetRegionCode"},
|
||||
{4, &SET::GetRegionCode, "GetRegionCode"},
|
||||
{5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"},
|
||||
{6, &SET::GetAvailableLanguageCodeCount2, "GetAvailableLanguageCodeCount2"},
|
||||
{7, nullptr, "GetKeyCodeMap"},
|
||||
|
||||
@@ -43,6 +43,7 @@ private:
|
||||
void GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx);
|
||||
void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx);
|
||||
void GetQuestFlag(Kernel::HLERequestContext& ctx);
|
||||
void GetRegionCode(Kernel::HLERequestContext& ctx);
|
||||
};
|
||||
|
||||
} // namespace Service::Set
|
||||
|
||||
@@ -44,7 +44,7 @@ void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push<u16>(0x500);
|
||||
rb.Push<u16>(0x1000);
|
||||
}
|
||||
|
||||
Controller::Controller() : ServiceFramework("IpcController") {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -53,7 +53,7 @@ static std::vector<std::string> BuildLocationNameCache(Core::System& system) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<char> raw_data(binary_list->GetSize());
|
||||
std::vector<char> raw_data(binary_list->GetSize() + 1);
|
||||
binary_list->ReadBytes<char>(raw_data.data(), binary_list->GetSize());
|
||||
|
||||
std::stringstream data_stream{raw_data.data()};
|
||||
|
||||
@@ -86,6 +86,7 @@ void LogSettings() {
|
||||
LogSetting("System_RngSeed", Settings::values.rng_seed.value_or(0));
|
||||
LogSetting("System_CurrentUser", Settings::values.current_user);
|
||||
LogSetting("System_LanguageIndex", Settings::values.language_index);
|
||||
LogSetting("System_RegionIndex", Settings::values.region_index);
|
||||
LogSetting("Core_UseMultiCore", Settings::values.use_multi_core);
|
||||
LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
|
||||
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
|
||||
|
||||
@@ -387,6 +387,8 @@ struct Values {
|
||||
|
||||
s32 current_user;
|
||||
s32 language_index;
|
||||
s32 region_index;
|
||||
s32 sound_index;
|
||||
|
||||
// Controls
|
||||
std::array<PlayerInput, 10> players;
|
||||
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
pad_index(pad_index) {
|
||||
boost::system::error_code ec{};
|
||||
auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
|
||||
if (ec.failed()) {
|
||||
if (ec.value() != boost::system::errc::success) {
|
||||
LOG_ERROR(Input, "Invalid IPv4 address \"{}\" provided to socket", host);
|
||||
ipv4 = boost::asio::ip::address_v4{};
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/param_package.h"
|
||||
@@ -44,7 +45,7 @@ public:
|
||||
std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override {
|
||||
{
|
||||
std::lock_guard guard(status->update_mutex);
|
||||
status->touch_calibration.emplace();
|
||||
status->touch_calibration = DeviceStatus::CalibrationData{};
|
||||
// These default values work well for DS4 but probably not other touch inputs
|
||||
status->touch_calibration->min_x = params.Get("min_x", 100);
|
||||
status->touch_calibration->min_y = params.Get("min_y", 50);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -15,14 +15,6 @@ namespace VideoCommon::Dirty {
|
||||
|
||||
using Tegra::Engines::Maxwell3D;
|
||||
|
||||
void SetupCommonOnWriteStores(Tegra::Engines::Maxwell3D::DirtyState::Flags& store) {
|
||||
store[RenderTargets] = true;
|
||||
store[ZetaBuffer] = true;
|
||||
for (std::size_t i = 0; i < Maxwell3D::Regs::NumRenderTargets; ++i) {
|
||||
store[ColorBuffer0 + i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SetupDirtyRenderTargets(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables) {
|
||||
static constexpr std::size_t num_per_rt = NUM(rt[0]);
|
||||
static constexpr std::size_t begin = OFF(rt);
|
||||
|
||||
@@ -44,8 +44,6 @@ void FillBlock(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables, std::size_
|
||||
FillBlock(tables[1], begin, num, index_b);
|
||||
}
|
||||
|
||||
void SetupCommonOnWriteStores(Tegra::Engines::Maxwell3D::DirtyState::Flags& store);
|
||||
|
||||
void SetupDirtyRenderTargets(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables);
|
||||
|
||||
} // namespace VideoCommon::Dirty
|
||||
|
||||
@@ -18,10 +18,14 @@ struct SamplerDescriptor {
|
||||
union {
|
||||
u32 raw = 0;
|
||||
BitField<0, 2, Tegra::Shader::TextureType> texture_type;
|
||||
BitField<2, 3, Tegra::Texture::ComponentType> component_type;
|
||||
BitField<2, 3, Tegra::Texture::ComponentType> r_type;
|
||||
BitField<5, 1, u32> is_array;
|
||||
BitField<6, 1, u32> is_buffer;
|
||||
BitField<7, 1, u32> is_shadow;
|
||||
BitField<8, 3, Tegra::Texture::ComponentType> g_type;
|
||||
BitField<11, 3, Tegra::Texture::ComponentType> b_type;
|
||||
BitField<14, 3, Tegra::Texture::ComponentType> a_type;
|
||||
BitField<17, 7, Tegra::Texture::TextureFormat> format;
|
||||
};
|
||||
|
||||
bool operator==(const SamplerDescriptor& rhs) const noexcept {
|
||||
@@ -36,9 +40,11 @@ struct SamplerDescriptor {
|
||||
using Tegra::Shader::TextureType;
|
||||
SamplerDescriptor result;
|
||||
|
||||
// This is going to be used to determine the shading language type.
|
||||
// Because of that we don't care about all component types on color textures.
|
||||
result.component_type.Assign(tic.r_type.Value());
|
||||
result.format.Assign(tic.format.Value());
|
||||
result.r_type.Assign(tic.r_type.Value());
|
||||
result.g_type.Assign(tic.g_type.Value());
|
||||
result.b_type.Assign(tic.b_type.Value());
|
||||
result.a_type.Assign(tic.a_type.Value());
|
||||
|
||||
switch (tic.texture_type.Value()) {
|
||||
case Tegra::Texture::TextureType::Texture1D:
|
||||
|
||||
@@ -119,14 +119,6 @@ Texture::TICEntry KeplerCompute::GetTICEntry(u32 tic_index) const {
|
||||
Texture::TICEntry tic_entry;
|
||||
memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry));
|
||||
|
||||
const auto r_type{tic_entry.r_type.Value()};
|
||||
const auto g_type{tic_entry.g_type.Value()};
|
||||
const auto b_type{tic_entry.b_type.Value()};
|
||||
const auto a_type{tic_entry.a_type.Value()};
|
||||
|
||||
// TODO(Subv): Different data types for separate components are not supported
|
||||
DEBUG_ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
|
||||
|
||||
return tic_entry;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,8 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
regs.framebuffer_srgb = 1;
|
||||
regs.front_face = Maxwell3D::Regs::FrontFace::ClockWise;
|
||||
|
||||
shadow_state = regs;
|
||||
|
||||
mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_end_gl)] = true;
|
||||
mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)] = true;
|
||||
mme_inline[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true;
|
||||
@@ -160,8 +162,17 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
ASSERT_MSG(method < Regs::NUM_REGS,
|
||||
"Invalid Maxwell3D register, increase the size of the Regs structure");
|
||||
|
||||
if (regs.reg_array[method] != method_call.argument) {
|
||||
regs.reg_array[method] = method_call.argument;
|
||||
u32 arg = method_call.argument;
|
||||
// Keep track of the register value in shadow_state when requested.
|
||||
if (shadow_state.shadow_ram_control == Regs::ShadowRamControl::Track ||
|
||||
shadow_state.shadow_ram_control == Regs::ShadowRamControl::TrackWithFilter) {
|
||||
shadow_state.reg_array[method] = arg;
|
||||
} else if (shadow_state.shadow_ram_control == Regs::ShadowRamControl::Replay) {
|
||||
arg = shadow_state.reg_array[method];
|
||||
}
|
||||
|
||||
if (regs.reg_array[method] != arg) {
|
||||
regs.reg_array[method] = arg;
|
||||
|
||||
for (const auto& table : dirty.tables) {
|
||||
dirty.flags[table[method]] = true;
|
||||
@@ -169,12 +180,16 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
}
|
||||
|
||||
switch (method) {
|
||||
case MAXWELL3D_REG_INDEX(shadow_ram_control): {
|
||||
shadow_state.shadow_ram_control = static_cast<Regs::ShadowRamControl>(method_call.argument);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(macros.data): {
|
||||
ProcessMacroUpload(method_call.argument);
|
||||
ProcessMacroUpload(arg);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(macros.bind): {
|
||||
ProcessMacroBind(method_call.argument);
|
||||
ProcessMacroBind(arg);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(firmware[4]): {
|
||||
@@ -250,7 +265,7 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(data_upload): {
|
||||
const bool is_last_call = method_call.IsLastCall();
|
||||
upload_state.ProcessData(method_call.argument, is_last_call);
|
||||
upload_state.ProcessData(arg, is_last_call);
|
||||
if (is_last_call) {
|
||||
OnMemoryWrite();
|
||||
}
|
||||
|
||||
@@ -531,6 +531,17 @@ public:
|
||||
Fill = 0x1b02,
|
||||
};
|
||||
|
||||
enum class ShadowRamControl : u32 {
|
||||
// write value to shadow ram
|
||||
Track = 0,
|
||||
// write value to shadow ram ( with validation ??? )
|
||||
TrackWithFilter = 1,
|
||||
// only write to real hw register
|
||||
Passthrough = 2,
|
||||
// write value from shadow ram to real hw register
|
||||
Replay = 3,
|
||||
};
|
||||
|
||||
struct RenderTargetConfig {
|
||||
u32 address_high;
|
||||
u32 address_low;
|
||||
@@ -674,7 +685,9 @@ public:
|
||||
u32 bind;
|
||||
} macros;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x17);
|
||||
ShadowRamControl shadow_ram_control;
|
||||
|
||||
INSERT_UNION_PADDING_WORDS(0x16);
|
||||
|
||||
Upload::Registers upload;
|
||||
struct {
|
||||
@@ -1263,7 +1276,12 @@ public:
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
} regs{};
|
||||
};
|
||||
|
||||
Regs regs{};
|
||||
|
||||
/// Store temporary hw register values, used by some calls to restore state after a operation
|
||||
Regs shadow_state;
|
||||
|
||||
static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Maxwell3D Regs has wrong size");
|
||||
static_assert(std::is_trivially_copyable_v<Regs>, "Maxwell3D Regs must be trivially copyable");
|
||||
@@ -1458,6 +1476,7 @@ private:
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_REG_POSITION(macros, 0x45);
|
||||
ASSERT_REG_POSITION(shadow_ram_control, 0x49);
|
||||
ASSERT_REG_POSITION(upload, 0x60);
|
||||
ASSERT_REG_POSITION(exec_upload, 0x6C);
|
||||
ASSERT_REG_POSITION(data_upload, 0x6D);
|
||||
|
||||
@@ -82,6 +82,10 @@ union Attribute {
|
||||
Position = 7,
|
||||
Attribute_0 = 8,
|
||||
Attribute_31 = 39,
|
||||
FrontColor = 40,
|
||||
FrontSecondaryColor = 41,
|
||||
BackColor = 42,
|
||||
BackSecondaryColor = 43,
|
||||
ClipDistances0123 = 44,
|
||||
ClipDistances4567 = 45,
|
||||
PointCoord = 46,
|
||||
@@ -89,6 +93,8 @@ union Attribute {
|
||||
// shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval
|
||||
// shader.
|
||||
TessCoordInstanceIDVertexID = 47,
|
||||
TexCoord_0 = 48,
|
||||
TexCoord_7 = 55,
|
||||
// This attribute contains a tuple of (Unk, Unk, Unk, gl_FrontFacing) when inside a fragment
|
||||
// shader. It is unknown what the other values contain.
|
||||
FrontFacing = 63,
|
||||
@@ -225,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,
|
||||
};
|
||||
|
||||
@@ -911,14 +905,9 @@ union Instruction {
|
||||
} fadd32i;
|
||||
|
||||
union {
|
||||
BitField<20, 8, u64> shift_position;
|
||||
BitField<28, 8, u64> shift_length;
|
||||
BitField<48, 1, u64> negate_b;
|
||||
BitField<49, 1, u64> negate_a;
|
||||
|
||||
u64 GetLeftShiftValue() const {
|
||||
return 32 - (shift_position + shift_length);
|
||||
}
|
||||
BitField<40, 1, u64> brev;
|
||||
BitField<47, 1, u64> rd_cc;
|
||||
BitField<48, 1, u64> is_signed;
|
||||
} bfe;
|
||||
|
||||
union {
|
||||
@@ -1000,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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -39,6 +42,7 @@ enum class RenderTargetFormat : u32 {
|
||||
RGBA32_FLOAT = 0xC0,
|
||||
RGBA32_UINT = 0xC2,
|
||||
RGBA16_UNORM = 0xC6,
|
||||
RGBA16_SNORM = 0xC7,
|
||||
RGBA16_UINT = 0xC9,
|
||||
RGBA16_FLOAT = 0xCA,
|
||||
RG32_FLOAT = 0xCB,
|
||||
@@ -128,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();
|
||||
|
||||
@@ -173,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;
|
||||
|
||||
@@ -286,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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -51,6 +51,7 @@ static constexpr ConversionArray morton_to_linear_fns = {
|
||||
MortonCopy<true, PixelFormat::R8UI>,
|
||||
MortonCopy<true, PixelFormat::RGBA16F>,
|
||||
MortonCopy<true, PixelFormat::RGBA16U>,
|
||||
MortonCopy<true, PixelFormat::RGBA16S>,
|
||||
MortonCopy<true, PixelFormat::RGBA16UI>,
|
||||
MortonCopy<true, PixelFormat::R11FG11FB10F>,
|
||||
MortonCopy<true, PixelFormat::RGBA32UI>,
|
||||
@@ -131,6 +132,7 @@ static constexpr ConversionArray linear_to_morton_fns = {
|
||||
MortonCopy<false, PixelFormat::R8U>,
|
||||
MortonCopy<false, PixelFormat::R8UI>,
|
||||
MortonCopy<false, PixelFormat::RGBA16F>,
|
||||
MortonCopy<false, PixelFormat::RGBA16S>,
|
||||
MortonCopy<false, PixelFormat::RGBA16U>,
|
||||
MortonCopy<false, PixelFormat::RGBA16UI>,
|
||||
MortonCopy<false, PixelFormat::R11FG11FB10F>,
|
||||
|
||||
@@ -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:
|
||||
// ------------------------
|
||||
|
||||
@@ -93,10 +93,6 @@ void oglEnable(GLenum cap, bool state) {
|
||||
(state ? glEnable : glDisable)(cap);
|
||||
}
|
||||
|
||||
void oglEnablei(GLenum cap, bool state, GLuint index) {
|
||||
(state ? glEnablei : glDisablei)(cap, index);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
|
||||
@@ -448,6 +444,7 @@ void RasterizerOpenGL::Clear() {
|
||||
}
|
||||
|
||||
SyncRasterizeEnable();
|
||||
SyncStencilTestState();
|
||||
|
||||
if (regs.clear_flags.scissor) {
|
||||
SyncScissorTest();
|
||||
@@ -478,7 +475,6 @@ void RasterizerOpenGL::Clear() {
|
||||
void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Drawing);
|
||||
auto& gpu = system.GPU().Maxwell3D();
|
||||
const auto& regs = gpu.regs;
|
||||
|
||||
query_cache.UpdateCounters();
|
||||
|
||||
@@ -529,7 +525,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
|
||||
// Upload vertex and index data.
|
||||
SetupVertexBuffer();
|
||||
SetupVertexInstances();
|
||||
GLintptr index_buffer_offset;
|
||||
GLintptr index_buffer_offset = 0;
|
||||
if (is_indexed) {
|
||||
index_buffer_offset = SetupIndexBuffer();
|
||||
}
|
||||
@@ -555,7 +551,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
|
||||
ConfigureFramebuffers();
|
||||
|
||||
// Signal the buffer cache that we are not going to upload more things.
|
||||
const bool invalidate = buffer_cache.Unmap();
|
||||
buffer_cache.Unmap();
|
||||
|
||||
// Now that we are no longer uploading data, we can safely bind the buffers to OpenGL.
|
||||
vertex_array_pushbuffer.Bind();
|
||||
@@ -938,13 +934,15 @@ void RasterizerOpenGL::SyncViewport() {
|
||||
}
|
||||
flags[Dirty::Viewport0 + i] = false;
|
||||
|
||||
const Common::Rectangle<f32> rect{regs.viewport_transform[i].GetRect()};
|
||||
const auto& src = regs.viewport_transform[i];
|
||||
const Common::Rectangle<f32> rect{src.GetRect()};
|
||||
glViewportIndexedf(static_cast<GLuint>(i), rect.left, rect.bottom, rect.GetWidth(),
|
||||
rect.GetHeight());
|
||||
|
||||
const auto& src = regs.viewports[i];
|
||||
glDepthRangeIndexed(static_cast<GLuint>(i), static_cast<GLdouble>(src.depth_range_near),
|
||||
static_cast<GLdouble>(src.depth_range_far));
|
||||
const GLdouble reduce_z = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne;
|
||||
const GLdouble near_depth = src.translate_z - src.scale_z * reduce_z;
|
||||
const GLdouble far_depth = src.translate_z + src.scale_z;
|
||||
glDepthRangeIndexed(static_cast<GLuint>(i), near_depth, far_depth);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1055,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),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -366,10 +366,19 @@ constexpr bool IsGenericAttribute(Attribute::Index index) {
|
||||
return index >= Attribute::Index::Attribute_0 && index <= Attribute::Index::Attribute_31;
|
||||
}
|
||||
|
||||
constexpr bool IsLegacyTexCoord(Attribute::Index index) {
|
||||
return static_cast<int>(index) >= static_cast<int>(Attribute::Index::TexCoord_0) &&
|
||||
static_cast<int>(index) <= static_cast<int>(Attribute::Index::TexCoord_7);
|
||||
}
|
||||
|
||||
constexpr Attribute::Index ToGenericAttribute(u64 value) {
|
||||
return static_cast<Attribute::Index>(value + static_cast<u64>(Attribute::Index::Attribute_0));
|
||||
}
|
||||
|
||||
constexpr int GetLegacyTexCoordIndex(Attribute::Index index) {
|
||||
return static_cast<int>(index) - static_cast<int>(Attribute::Index::TexCoord_0);
|
||||
}
|
||||
|
||||
u32 GetGenericAttributeIndex(Attribute::Index index) {
|
||||
ASSERT(IsGenericAttribute(index));
|
||||
return static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0);
|
||||
@@ -393,10 +402,6 @@ std::string FlowStackTopName(MetaStackClass stack) {
|
||||
return fmt::format("{}_flow_stack_top", GetFlowStackPrefix(stack));
|
||||
}
|
||||
|
||||
[[deprecated]] constexpr bool IsVertexShader(ShaderType stage) {
|
||||
return stage == ShaderType::Vertex;
|
||||
}
|
||||
|
||||
struct GenericVaryingDescription {
|
||||
std::string name;
|
||||
u8 first_element = 0;
|
||||
@@ -502,7 +507,7 @@ private:
|
||||
if (!identifier.empty()) {
|
||||
code.AddLine("// {}", identifier);
|
||||
}
|
||||
code.AddLine("#version 440 core");
|
||||
code.AddLine("#version 440 {}", ir.UsesLegacyVaryings() ? "compatibility" : "core");
|
||||
code.AddLine("#extension GL_ARB_separate_shader_objects : enable");
|
||||
if (device.HasShaderBallot()) {
|
||||
code.AddLine("#extension GL_ARB_shader_ballot : require");
|
||||
@@ -529,8 +534,9 @@ private:
|
||||
}
|
||||
|
||||
void DeclareVertex() {
|
||||
if (!IsVertexShader(stage))
|
||||
if (stage != ShaderType::Vertex) {
|
||||
return;
|
||||
}
|
||||
|
||||
DeclareVertexRedeclarations();
|
||||
}
|
||||
@@ -564,6 +570,16 @@ private:
|
||||
if (stage != ShaderType::Fragment) {
|
||||
return;
|
||||
}
|
||||
if (ir.UsesLegacyVaryings()) {
|
||||
code.AddLine("in gl_PerFragment {{");
|
||||
++code.scope;
|
||||
code.AddLine("vec4 gl_TexCoord[8];");
|
||||
code.AddLine("vec4 gl_Color;");
|
||||
code.AddLine("vec4 gl_SecondaryColor;");
|
||||
--code.scope;
|
||||
code.AddLine("}};");
|
||||
}
|
||||
|
||||
for (u32 rt = 0; rt < Maxwell::NumRenderTargets; ++rt) {
|
||||
code.AddLine("layout (location = {}) out vec4 frag_color{};", rt, rt);
|
||||
}
|
||||
@@ -602,14 +618,14 @@ private:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!IsVertexShader(stage) || device.HasVertexViewportLayer()) {
|
||||
if (stage != ShaderType::Vertex || device.HasVertexViewportLayer()) {
|
||||
if (ir.UsesLayer()) {
|
||||
code.AddLine("int gl_Layer;");
|
||||
}
|
||||
if (ir.UsesViewportIndex()) {
|
||||
code.AddLine("int gl_ViewportIndex;");
|
||||
}
|
||||
} else if ((ir.UsesLayer() || ir.UsesViewportIndex()) && IsVertexShader(stage) &&
|
||||
} else if ((ir.UsesLayer() || ir.UsesViewportIndex()) && stage == ShaderType::Vertex &&
|
||||
!device.HasVertexViewportLayer()) {
|
||||
LOG_ERROR(
|
||||
Render_OpenGL,
|
||||
@@ -620,12 +636,12 @@ private:
|
||||
code.AddLine("float gl_PointSize;");
|
||||
}
|
||||
|
||||
if (ir.UsesInstanceId()) {
|
||||
code.AddLine("int gl_InstanceID;");
|
||||
}
|
||||
|
||||
if (ir.UsesVertexId()) {
|
||||
code.AddLine("int gl_VertexID;");
|
||||
if (ir.UsesLegacyVaryings()) {
|
||||
code.AddLine("vec4 gl_TexCoord[8];");
|
||||
code.AddLine("vec4 gl_FrontColor;");
|
||||
code.AddLine("vec4 gl_FrontSecondaryColor;");
|
||||
code.AddLine("vec4 gl_BackColor;");
|
||||
code.AddLine("vec4 gl_BackSecondaryColor;");
|
||||
}
|
||||
|
||||
--code.scope;
|
||||
@@ -1131,6 +1147,10 @@ private:
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
case Attribute::Index::FrontColor:
|
||||
return {"gl_Color"s + GetSwizzle(element), Type::Float};
|
||||
case Attribute::Index::FrontSecondaryColor:
|
||||
return {"gl_SecondaryColor"s + GetSwizzle(element), Type::Float};
|
||||
case Attribute::Index::PointCoord:
|
||||
switch (element) {
|
||||
case 0:
|
||||
@@ -1147,7 +1167,7 @@ private:
|
||||
// TODO(Subv): Find out what the values are for the first two elements when inside a
|
||||
// vertex shader, and what's the value of the fourth element when inside a Tess Eval
|
||||
// shader.
|
||||
ASSERT(IsVertexShader(stage));
|
||||
ASSERT(stage == ShaderType::Vertex);
|
||||
switch (element) {
|
||||
case 2:
|
||||
// Config pack's first value is instance_id.
|
||||
@@ -1171,6 +1191,12 @@ private:
|
||||
return {GeometryPass(GetGenericInputAttribute(attribute)) + GetSwizzle(element),
|
||||
Type::Float};
|
||||
}
|
||||
if (IsLegacyTexCoord(attribute)) {
|
||||
UNIMPLEMENTED_IF(stage == ShaderType::Geometry);
|
||||
return {fmt::format("gl_TexCoord[{}]{}", GetLegacyTexCoordIndex(attribute),
|
||||
GetSwizzle(element)),
|
||||
Type::Float};
|
||||
}
|
||||
break;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
|
||||
@@ -1209,21 +1235,22 @@ private:
|
||||
}
|
||||
|
||||
std::optional<Expression> GetOutputAttribute(const AbufNode* abuf) {
|
||||
const u32 element = abuf->GetElement();
|
||||
switch (const auto attribute = abuf->GetIndex()) {
|
||||
case Attribute::Index::Position:
|
||||
return {{"gl_Position"s + GetSwizzle(abuf->GetElement()), Type::Float}};
|
||||
return {{"gl_Position"s + GetSwizzle(element), Type::Float}};
|
||||
case Attribute::Index::LayerViewportPointSize:
|
||||
switch (abuf->GetElement()) {
|
||||
switch (element) {
|
||||
case 0:
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
case 1:
|
||||
if (IsVertexShader(stage) && !device.HasVertexViewportLayer()) {
|
||||
if (stage == ShaderType::Vertex && !device.HasVertexViewportLayer()) {
|
||||
return {};
|
||||
}
|
||||
return {{"gl_Layer", Type::Int}};
|
||||
case 2:
|
||||
if (IsVertexShader(stage) && !device.HasVertexViewportLayer()) {
|
||||
if (stage == ShaderType::Vertex && !device.HasVertexViewportLayer()) {
|
||||
return {};
|
||||
}
|
||||
return {{"gl_ViewportIndex", Type::Int}};
|
||||
@@ -1231,13 +1258,26 @@ private:
|
||||
return {{"gl_PointSize", Type::Float}};
|
||||
}
|
||||
return {};
|
||||
case Attribute::Index::FrontColor:
|
||||
return {{"gl_FrontColor"s + GetSwizzle(element), Type::Float}};
|
||||
case Attribute::Index::FrontSecondaryColor:
|
||||
return {{"gl_FrontSecondaryColor"s + GetSwizzle(element), Type::Float}};
|
||||
case Attribute::Index::BackColor:
|
||||
return {{"gl_BackColor"s + GetSwizzle(element), Type::Float}};
|
||||
case Attribute::Index::BackSecondaryColor:
|
||||
return {{"gl_BackSecondaryColor"s + GetSwizzle(element), Type::Float}};
|
||||
case Attribute::Index::ClipDistances0123:
|
||||
return {{fmt::format("gl_ClipDistance[{}]", abuf->GetElement()), Type::Float}};
|
||||
return {{fmt::format("gl_ClipDistance[{}]", element), Type::Float}};
|
||||
case Attribute::Index::ClipDistances4567:
|
||||
return {{fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4), Type::Float}};
|
||||
return {{fmt::format("gl_ClipDistance[{}]", element + 4), Type::Float}};
|
||||
default:
|
||||
if (IsGenericAttribute(attribute)) {
|
||||
return {{GetGenericOutputAttribute(attribute, abuf->GetElement()), Type::Float}};
|
||||
return {{GetGenericOutputAttribute(attribute, element), Type::Float}};
|
||||
}
|
||||
if (IsLegacyTexCoord(attribute)) {
|
||||
return {{fmt::format("gl_TexCoord[{}]{}", GetLegacyTexCoordIndex(attribute),
|
||||
GetSwizzle(element)),
|
||||
Type::Float}};
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unhandled output attribute: {}", static_cast<u32>(attribute));
|
||||
return {};
|
||||
@@ -2009,16 +2049,19 @@ private:
|
||||
expr += GetSampler(meta->sampler);
|
||||
expr += ", ";
|
||||
|
||||
expr += constructors.at(operation.GetOperandsCount() - 1);
|
||||
expr += constructors.at(operation.GetOperandsCount() + (meta->array ? 1 : 0) - 1);
|
||||
expr += '(';
|
||||
for (std::size_t i = 0; i < count; ++i) {
|
||||
expr += VisitOperand(operation, i).AsInt();
|
||||
const std::size_t next = i + 1;
|
||||
if (next == count)
|
||||
expr += ')';
|
||||
else if (next < count)
|
||||
if (i > 0) {
|
||||
expr += ", ";
|
||||
}
|
||||
expr += VisitOperand(operation, i).AsInt();
|
||||
}
|
||||
if (meta->array) {
|
||||
expr += ", ";
|
||||
expr += Visit(meta->array).AsInt();
|
||||
}
|
||||
expr += ')';
|
||||
|
||||
if (meta->lod && !meta->sampler.IsBuffer()) {
|
||||
expr += ", ";
|
||||
@@ -2071,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};
|
||||
@@ -2264,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";
|
||||
@@ -2414,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,
|
||||
@@ -2529,7 +2592,7 @@ private:
|
||||
}
|
||||
|
||||
u32 GetNumPhysicalInputAttributes() const {
|
||||
return IsVertexShader(stage) ? GetNumPhysicalAttributes() : GetNumPhysicalVaryings();
|
||||
return stage == ShaderType::Vertex ? GetNumPhysicalAttributes() : GetNumPhysicalVaryings();
|
||||
}
|
||||
|
||||
u32 GetNumPhysicalAttributes() const {
|
||||
|
||||
@@ -238,7 +238,6 @@ void StateTracker::Initialize() {
|
||||
SetupDirtyMisc(tables);
|
||||
|
||||
auto& store = dirty.on_write_stores;
|
||||
SetupCommonOnWriteStores(store);
|
||||
store[VertexBuffers] = true;
|
||||
for (std::size_t i = 0; i < Regs::NumVertexArrays; ++i) {
|
||||
store[VertexBuffer0 + i] = true;
|
||||
|
||||
@@ -53,6 +53,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format
|
||||
{GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, false}, // R8UI
|
||||
{GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false}, // RGBA16F
|
||||
{GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, false}, // RGBA16U
|
||||
{GL_RGBA16_SNORM, GL_RGBA, GL_SHORT, false}, // RGBA16S
|
||||
{GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, false}, // RGBA16UI
|
||||
{GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, false}, // R11FG11FB10F
|
||||
{GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, false}, // RGBA32UI
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
@@ -25,8 +28,8 @@
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
// 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.
|
||||
namespace {
|
||||
|
||||
constexpr std::size_t SWAP_CHAIN_SIZE = 3;
|
||||
|
||||
struct Frame {
|
||||
@@ -41,6 +44,159 @@ struct Frame {
|
||||
bool is_srgb{}; /// Framebuffer is sRGB or RGB
|
||||
};
|
||||
|
||||
constexpr char VERTEX_SHADER[] = R"(
|
||||
#version 430 core
|
||||
|
||||
out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
layout (location = 0) in vec2 vert_position;
|
||||
layout (location = 1) in vec2 vert_tex_coord;
|
||||
layout (location = 0) out vec2 frag_tex_coord;
|
||||
|
||||
// This is a truncated 3x3 matrix for 2D transformations:
|
||||
// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
|
||||
// The third column performs translation.
|
||||
// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
|
||||
// implicitly be [0, 0, 1]
|
||||
layout (location = 0) uniform mat3x2 modelview_matrix;
|
||||
|
||||
void main() {
|
||||
// Multiply input position by the rotscale part of the matrix and then manually translate by
|
||||
// the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
|
||||
// to `vec3(vert_position.xy, 1.0)`
|
||||
gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
|
||||
frag_tex_coord = vert_tex_coord;
|
||||
}
|
||||
)";
|
||||
|
||||
constexpr char FRAGMENT_SHADER[] = R"(
|
||||
#version 430 core
|
||||
|
||||
layout (location = 0) in vec2 frag_tex_coord;
|
||||
layout (location = 0) out vec4 color;
|
||||
|
||||
layout (binding = 0) uniform sampler2D color_texture;
|
||||
|
||||
void main() {
|
||||
color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
|
||||
}
|
||||
)";
|
||||
|
||||
constexpr GLint PositionLocation = 0;
|
||||
constexpr GLint TexCoordLocation = 1;
|
||||
constexpr GLint ModelViewMatrixLocation = 0;
|
||||
|
||||
struct ScreenRectVertex {
|
||||
constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v)
|
||||
: position{{static_cast<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {}
|
||||
|
||||
std::array<GLfloat, 2> position;
|
||||
std::array<GLfloat, 2> tex_coord;
|
||||
};
|
||||
|
||||
/// Returns true if any debug tool is attached
|
||||
bool HasDebugTool() {
|
||||
const bool nsight = std::getenv("NVTX_INJECTION64_PATH") || std::getenv("NSIGHT_LAUNCHED");
|
||||
if (nsight) {
|
||||
return true;
|
||||
}
|
||||
|
||||
GLint num_extensions;
|
||||
glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
|
||||
for (GLuint index = 0; index < static_cast<GLuint>(num_extensions); ++index) {
|
||||
const auto name = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, index));
|
||||
if (!std::strcmp(name, "GL_EXT_debug_tool")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left
|
||||
* corner and (width, height) on the lower-bottom.
|
||||
*
|
||||
* The projection part of the matrix is trivial, hence these operations are represented
|
||||
* by a 3x2 matrix.
|
||||
*/
|
||||
std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(float width, float height) {
|
||||
std::array<GLfloat, 3 * 2> matrix; // Laid out in column-major order
|
||||
|
||||
// clang-format off
|
||||
matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f;
|
||||
matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f;
|
||||
// Last matrix row is implicitly assumed to be [0, 0, 1].
|
||||
// clang-format on
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
const char* GetSource(GLenum source) {
|
||||
switch (source) {
|
||||
case GL_DEBUG_SOURCE_API:
|
||||
return "API";
|
||||
case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
|
||||
return "WINDOW_SYSTEM";
|
||||
case GL_DEBUG_SOURCE_SHADER_COMPILER:
|
||||
return "SHADER_COMPILER";
|
||||
case GL_DEBUG_SOURCE_THIRD_PARTY:
|
||||
return "THIRD_PARTY";
|
||||
case GL_DEBUG_SOURCE_APPLICATION:
|
||||
return "APPLICATION";
|
||||
case GL_DEBUG_SOURCE_OTHER:
|
||||
return "OTHER";
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return "Unknown source";
|
||||
}
|
||||
}
|
||||
|
||||
const char* GetType(GLenum type) {
|
||||
switch (type) {
|
||||
case GL_DEBUG_TYPE_ERROR:
|
||||
return "ERROR";
|
||||
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
|
||||
return "DEPRECATED_BEHAVIOR";
|
||||
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
|
||||
return "UNDEFINED_BEHAVIOR";
|
||||
case GL_DEBUG_TYPE_PORTABILITY:
|
||||
return "PORTABILITY";
|
||||
case GL_DEBUG_TYPE_PERFORMANCE:
|
||||
return "PERFORMANCE";
|
||||
case GL_DEBUG_TYPE_OTHER:
|
||||
return "OTHER";
|
||||
case GL_DEBUG_TYPE_MARKER:
|
||||
return "MARKER";
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return "Unknown type";
|
||||
}
|
||||
}
|
||||
|
||||
void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
|
||||
const GLchar* message, const void* user_param) {
|
||||
const char format[] = "{} {} {}: {}";
|
||||
const char* const str_source = GetSource(source);
|
||||
const char* const str_type = GetType(type);
|
||||
|
||||
switch (severity) {
|
||||
case GL_DEBUG_SEVERITY_HIGH:
|
||||
LOG_CRITICAL(Render_OpenGL, format, str_source, str_type, id, message);
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_MEDIUM:
|
||||
LOG_WARNING(Render_OpenGL, format, str_source, str_type, id, message);
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
||||
case GL_DEBUG_SEVERITY_LOW:
|
||||
LOG_DEBUG(Render_OpenGL, format, str_source, str_type, id, message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
/**
|
||||
* For smooth Vsync rendering, we want to always present the latest frame that the core generates,
|
||||
* but also make sure that rendering happens at the pace that the frontend dictates. This is a
|
||||
@@ -157,146 +313,10 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char VERTEX_SHADER[] = R"(
|
||||
#version 430 core
|
||||
|
||||
out gl_PerVertex {
|
||||
vec4 gl_Position;
|
||||
};
|
||||
|
||||
layout (location = 0) in vec2 vert_position;
|
||||
layout (location = 1) in vec2 vert_tex_coord;
|
||||
layout (location = 0) out vec2 frag_tex_coord;
|
||||
|
||||
// This is a truncated 3x3 matrix for 2D transformations:
|
||||
// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
|
||||
// The third column performs translation.
|
||||
// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
|
||||
// implicitly be [0, 0, 1]
|
||||
layout (location = 0) uniform mat3x2 modelview_matrix;
|
||||
|
||||
void main() {
|
||||
// Multiply input position by the rotscale part of the matrix and then manually translate by
|
||||
// the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
|
||||
// to `vec3(vert_position.xy, 1.0)`
|
||||
gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
|
||||
frag_tex_coord = vert_tex_coord;
|
||||
}
|
||||
)";
|
||||
|
||||
constexpr char FRAGMENT_SHADER[] = R"(
|
||||
#version 430 core
|
||||
|
||||
layout (location = 0) in vec2 frag_tex_coord;
|
||||
layout (location = 0) out vec4 color;
|
||||
|
||||
layout (binding = 0) uniform sampler2D color_texture;
|
||||
|
||||
void main() {
|
||||
color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
|
||||
}
|
||||
)";
|
||||
|
||||
constexpr GLint PositionLocation = 0;
|
||||
constexpr GLint TexCoordLocation = 1;
|
||||
constexpr GLint ModelViewMatrixLocation = 0;
|
||||
|
||||
struct ScreenRectVertex {
|
||||
constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v)
|
||||
: position{{static_cast<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {}
|
||||
|
||||
std::array<GLfloat, 2> position;
|
||||
std::array<GLfloat, 2> tex_coord;
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left
|
||||
* corner and (width, height) on the lower-bottom.
|
||||
*
|
||||
* The projection part of the matrix is trivial, hence these operations are represented
|
||||
* by a 3x2 matrix.
|
||||
*/
|
||||
std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(float width, float height) {
|
||||
std::array<GLfloat, 3 * 2> matrix; // Laid out in column-major order
|
||||
|
||||
// clang-format off
|
||||
matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f;
|
||||
matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f;
|
||||
// Last matrix row is implicitly assumed to be [0, 0, 1].
|
||||
// clang-format on
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
const char* GetSource(GLenum source) {
|
||||
switch (source) {
|
||||
case GL_DEBUG_SOURCE_API:
|
||||
return "API";
|
||||
case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
|
||||
return "WINDOW_SYSTEM";
|
||||
case GL_DEBUG_SOURCE_SHADER_COMPILER:
|
||||
return "SHADER_COMPILER";
|
||||
case GL_DEBUG_SOURCE_THIRD_PARTY:
|
||||
return "THIRD_PARTY";
|
||||
case GL_DEBUG_SOURCE_APPLICATION:
|
||||
return "APPLICATION";
|
||||
case GL_DEBUG_SOURCE_OTHER:
|
||||
return "OTHER";
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return "Unknown source";
|
||||
}
|
||||
}
|
||||
|
||||
const char* GetType(GLenum type) {
|
||||
switch (type) {
|
||||
case GL_DEBUG_TYPE_ERROR:
|
||||
return "ERROR";
|
||||
case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
|
||||
return "DEPRECATED_BEHAVIOR";
|
||||
case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
|
||||
return "UNDEFINED_BEHAVIOR";
|
||||
case GL_DEBUG_TYPE_PORTABILITY:
|
||||
return "PORTABILITY";
|
||||
case GL_DEBUG_TYPE_PERFORMANCE:
|
||||
return "PERFORMANCE";
|
||||
case GL_DEBUG_TYPE_OTHER:
|
||||
return "OTHER";
|
||||
case GL_DEBUG_TYPE_MARKER:
|
||||
return "MARKER";
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return "Unknown type";
|
||||
}
|
||||
}
|
||||
|
||||
void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
|
||||
const GLchar* message, const void* user_param) {
|
||||
const char format[] = "{} {} {}: {}";
|
||||
const char* const str_source = GetSource(source);
|
||||
const char* const str_type = GetType(type);
|
||||
|
||||
switch (severity) {
|
||||
case GL_DEBUG_SEVERITY_HIGH:
|
||||
LOG_CRITICAL(Render_OpenGL, format, str_source, str_type, id, message);
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_MEDIUM:
|
||||
LOG_WARNING(Render_OpenGL, format, str_source, str_type, id, message);
|
||||
break;
|
||||
case GL_DEBUG_SEVERITY_NOTIFICATION:
|
||||
case GL_DEBUG_SEVERITY_LOW:
|
||||
LOG_DEBUG(Render_OpenGL, format, str_source, str_type, id, message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
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;
|
||||
|
||||
@@ -304,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;
|
||||
}
|
||||
@@ -361,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) {
|
||||
@@ -428,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);
|
||||
|
||||
@@ -640,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
|
||||
@@ -673,6 +709,7 @@ void RendererOpenGL::TryPresent(int timeout_ms) {
|
||||
glFlush();
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RendererOpenGL::RenderScreenshot() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -125,6 +125,7 @@ struct FormatTuple {
|
||||
{vk::Format::eR8Uint, Attachable | Storage}, // R8UI
|
||||
{vk::Format::eR16G16B16A16Sfloat, Attachable | Storage}, // RGBA16F
|
||||
{vk::Format::eR16G16B16A16Unorm, Attachable | Storage}, // RGBA16U
|
||||
{vk::Format::eR16G16B16A16Snorm, Attachable | Storage}, // RGBA16S
|
||||
{vk::Format::eR16G16B16A16Uint, Attachable | Storage}, // RGBA16UI
|
||||
{vk::Format::eB10G11R11UfloatPack32, Attachable | Storage}, // R11FG11FB10F
|
||||
{vk::Format::eR32G32B32A32Uint, Attachable | Storage}, // RGBA32UI
|
||||
@@ -256,6 +257,8 @@ vk::ShaderStageFlagBits ShaderStage(Tegra::Engines::ShaderType stage) {
|
||||
return vk::ShaderStageFlagBits::eGeometry;
|
||||
case Tegra::Engines::ShaderType::Fragment:
|
||||
return vk::ShaderStageFlagBits::eFragment;
|
||||
case Tegra::Engines::ShaderType::Compute:
|
||||
return vk::ShaderStageFlagBits::eCompute;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented shader stage={}", static_cast<u32>(stage));
|
||||
return {};
|
||||
@@ -331,6 +334,8 @@ vk::Format VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttr
|
||||
return vk::Format::eR16G16B16Unorm;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
return vk::Format::eR16G16B16A16Unorm;
|
||||
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
|
||||
return vk::Format::eA2B10G10R10UnormPack32;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -364,6 +369,10 @@ vk::Format VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttr
|
||||
return vk::Format::eR8G8B8A8Uint;
|
||||
case Maxwell::VertexAttribute::Size::Size_32:
|
||||
return vk::Format::eR32Uint;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32:
|
||||
return vk::Format::eR32G32Uint;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32:
|
||||
return vk::Format::eR32G32B32Uint;
|
||||
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
|
||||
return vk::Format::eR32G32B32A32Uint;
|
||||
default:
|
||||
@@ -392,6 +401,26 @@ vk::Format VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttr
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::SignedScaled:
|
||||
switch (size) {
|
||||
case Maxwell::VertexAttribute::Size::Size_8:
|
||||
return vk::Format::eR8Sscaled;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8:
|
||||
return vk::Format::eR8G8Sscaled;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8:
|
||||
return vk::Format::eR8G8B8Sscaled;
|
||||
case Maxwell::VertexAttribute::Size::Size_8_8_8_8:
|
||||
return vk::Format::eR8G8B8A8Sscaled;
|
||||
case Maxwell::VertexAttribute::Size::Size_16:
|
||||
return vk::Format::eR16Sscaled;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16:
|
||||
return vk::Format::eR16G16Sscaled;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16:
|
||||
return vk::Format::eR16G16B16Sscaled;
|
||||
case Maxwell::VertexAttribute::Size::Size_16_16_16_16:
|
||||
return vk::Format::eR16G16B16A16Sscaled;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Maxwell::VertexAttribute::Type::Float:
|
||||
switch (size) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -535,6 +535,7 @@ std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperti
|
||||
vk::Format::eR32G32Sfloat,
|
||||
vk::Format::eR32G32Uint,
|
||||
vk::Format::eR16G16B16A16Uint,
|
||||
vk::Format::eR16G16B16A16Snorm,
|
||||
vk::Format::eR16G16B16A16Unorm,
|
||||
vk::Format::eR16G16Unorm,
|
||||
vk::Format::eR16G16Snorm,
|
||||
|
||||
@@ -179,10 +179,11 @@ Tegra::Engines::ConstBufferEngineInterface& CachedShader::GetEngine(
|
||||
VKPipelineCache::VKPipelineCache(Core::System& system, RasterizerVulkan& rasterizer,
|
||||
const VKDevice& device, VKScheduler& scheduler,
|
||||
VKDescriptorPool& descriptor_pool,
|
||||
VKUpdateDescriptorQueue& update_descriptor_queue)
|
||||
VKUpdateDescriptorQueue& update_descriptor_queue,
|
||||
VKRenderPassCache& renderpass_cache)
|
||||
: RasterizerCache{rasterizer}, system{system}, device{device}, scheduler{scheduler},
|
||||
descriptor_pool{descriptor_pool}, update_descriptor_queue{update_descriptor_queue},
|
||||
renderpass_cache(device) {}
|
||||
renderpass_cache{renderpass_cache} {}
|
||||
|
||||
VKPipelineCache::~VKPipelineCache() = default;
|
||||
|
||||
@@ -191,7 +192,6 @@ std::array<Shader, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() {
|
||||
|
||||
std::array<Shader, Maxwell::MaxShaderProgram> shaders;
|
||||
for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
|
||||
const auto& shader_config = gpu.regs.shader_config[index];
|
||||
const auto program{static_cast<Maxwell::ShaderProgram>(index)};
|
||||
|
||||
// Skip stages that are not enabled
|
||||
|
||||
@@ -161,7 +161,8 @@ public:
|
||||
explicit VKPipelineCache(Core::System& system, RasterizerVulkan& rasterizer,
|
||||
const VKDevice& device, VKScheduler& scheduler,
|
||||
VKDescriptorPool& descriptor_pool,
|
||||
VKUpdateDescriptorQueue& update_descriptor_queue);
|
||||
VKUpdateDescriptorQueue& update_descriptor_queue,
|
||||
VKRenderPassCache& renderpass_cache);
|
||||
~VKPipelineCache();
|
||||
|
||||
std::array<Shader, Maxwell::MaxShaderProgram> GetShaders();
|
||||
@@ -184,8 +185,7 @@ private:
|
||||
VKScheduler& scheduler;
|
||||
VKDescriptorPool& descriptor_pool;
|
||||
VKUpdateDescriptorQueue& update_descriptor_queue;
|
||||
|
||||
VKRenderPassCache renderpass_cache;
|
||||
VKRenderPassCache& renderpass_cache;
|
||||
|
||||
std::array<Shader, Maxwell::MaxShaderProgram> last_shaders;
|
||||
|
||||
|
||||
@@ -287,12 +287,13 @@ RasterizerVulkan::RasterizerVulkan(Core::System& system, Core::Frontend::EmuWind
|
||||
screen_info{screen_info}, device{device}, resource_manager{resource_manager},
|
||||
memory_manager{memory_manager}, state_tracker{state_tracker}, scheduler{scheduler},
|
||||
staging_pool(device, memory_manager, scheduler), descriptor_pool(device),
|
||||
update_descriptor_queue(device, scheduler),
|
||||
update_descriptor_queue(device, scheduler), renderpass_cache(device),
|
||||
quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
|
||||
uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
|
||||
texture_cache(system, *this, device, resource_manager, memory_manager, scheduler,
|
||||
staging_pool),
|
||||
pipeline_cache(system, *this, device, scheduler, descriptor_pool, update_descriptor_queue),
|
||||
pipeline_cache(system, *this, device, scheduler, descriptor_pool, update_descriptor_queue,
|
||||
renderpass_cache),
|
||||
buffer_cache(*this, system, device, memory_manager, scheduler, staging_pool),
|
||||
sampler_cache(device), query_cache(system, *this, device, scheduler) {
|
||||
scheduler.SetQueryCache(query_cache);
|
||||
@@ -365,13 +366,16 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
|
||||
void RasterizerVulkan::Clear() {
|
||||
MICROPROFILE_SCOPE(Vulkan_Clearing);
|
||||
|
||||
query_cache.UpdateCounters();
|
||||
|
||||
const auto& gpu = system.GPU().Maxwell3D();
|
||||
if (!system.GPU().Maxwell3D().ShouldExecute()) {
|
||||
return;
|
||||
}
|
||||
|
||||
sampled_views.clear();
|
||||
image_views.clear();
|
||||
|
||||
query_cache.UpdateCounters();
|
||||
|
||||
const auto& regs = gpu.regs;
|
||||
const bool use_color = regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B ||
|
||||
regs.clear_buffers.A;
|
||||
@@ -380,52 +384,54 @@ void RasterizerVulkan::Clear() {
|
||||
if (!use_color && !use_depth && !use_stencil) {
|
||||
return;
|
||||
}
|
||||
// Clearing images requires to be out of a renderpass
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
|
||||
// TODO(Rodrigo): Implement clears rendering a quad or using beginning a renderpass.
|
||||
[[maybe_unused]] const auto texceptions = UpdateAttachments();
|
||||
DEBUG_ASSERT(texceptions.none());
|
||||
SetupImageTransitions(0, color_attachments, zeta_attachment);
|
||||
|
||||
const vk::RenderPass renderpass = renderpass_cache.GetRenderPass(GetRenderPassParams(0));
|
||||
const auto [framebuffer, render_area] = ConfigureFramebuffers(renderpass);
|
||||
scheduler.RequestRenderpass({renderpass, framebuffer, {{0, 0}, render_area}, 0, nullptr});
|
||||
|
||||
const auto& scissor = regs.scissor_test[0];
|
||||
const vk::Offset2D scissor_offset(scissor.min_x, scissor.min_y);
|
||||
vk::Extent2D scissor_extent{scissor.max_x - scissor.min_x, scissor.max_y - scissor.min_y};
|
||||
scissor_extent.width = std::min(scissor_extent.width, render_area.width);
|
||||
scissor_extent.height = std::min(scissor_extent.height, render_area.height);
|
||||
|
||||
const u32 layer = regs.clear_buffers.layer;
|
||||
const vk::ClearRect clear_rect({scissor_offset, scissor_extent}, layer, 1);
|
||||
|
||||
if (use_color) {
|
||||
View color_view;
|
||||
{
|
||||
MICROPROFILE_SCOPE(Vulkan_RenderTargets);
|
||||
color_view = texture_cache.GetColorBufferSurface(regs.clear_buffers.RT.Value(), false);
|
||||
}
|
||||
|
||||
color_view->Transition(vk::ImageLayout::eTransferDstOptimal,
|
||||
vk::PipelineStageFlagBits::eTransfer,
|
||||
vk::AccessFlagBits::eTransferWrite);
|
||||
|
||||
const std::array clear_color = {regs.clear_color[0], regs.clear_color[1],
|
||||
regs.clear_color[2], regs.clear_color[3]};
|
||||
const vk::ClearColorValue clear(clear_color);
|
||||
scheduler.Record([image = color_view->GetImage(),
|
||||
subresource = color_view->GetImageSubresourceRange(),
|
||||
clear](auto cmdbuf, auto& dld) {
|
||||
cmdbuf.clearColorImage(image, vk::ImageLayout::eTransferDstOptimal, clear, subresource,
|
||||
dld);
|
||||
const vk::ClearValue clear_value{clear_color};
|
||||
const u32 color_attachment = regs.clear_buffers.RT;
|
||||
scheduler.Record([color_attachment, clear_value, clear_rect](auto cmdbuf, auto& dld) {
|
||||
const vk::ClearAttachment attachment(vk::ImageAspectFlagBits::eColor, color_attachment,
|
||||
clear_value);
|
||||
cmdbuf.clearAttachments(1, &attachment, 1, &clear_rect, dld);
|
||||
});
|
||||
}
|
||||
if (use_depth || use_stencil) {
|
||||
View zeta_surface;
|
||||
{
|
||||
MICROPROFILE_SCOPE(Vulkan_RenderTargets);
|
||||
zeta_surface = texture_cache.GetDepthBufferSurface(false);
|
||||
}
|
||||
|
||||
zeta_surface->Transition(vk::ImageLayout::eTransferDstOptimal,
|
||||
vk::PipelineStageFlagBits::eTransfer,
|
||||
vk::AccessFlagBits::eTransferWrite);
|
||||
|
||||
const vk::ClearDepthStencilValue clear(regs.clear_depth,
|
||||
static_cast<u32>(regs.clear_stencil));
|
||||
scheduler.Record([image = zeta_surface->GetImage(),
|
||||
subresource = zeta_surface->GetImageSubresourceRange(),
|
||||
clear](auto cmdbuf, auto& dld) {
|
||||
cmdbuf.clearDepthStencilImage(image, vk::ImageLayout::eTransferDstOptimal, clear,
|
||||
subresource, dld);
|
||||
});
|
||||
if (!use_depth && !use_stencil) {
|
||||
return;
|
||||
}
|
||||
vk::ImageAspectFlags aspect_flags;
|
||||
if (use_depth) {
|
||||
aspect_flags |= vk::ImageAspectFlagBits::eDepth;
|
||||
}
|
||||
if (use_stencil) {
|
||||
aspect_flags |= vk::ImageAspectFlagBits::eStencil;
|
||||
}
|
||||
|
||||
scheduler.Record([clear_depth = regs.clear_depth, clear_stencil = regs.clear_stencil,
|
||||
clear_rect, aspect_flags](auto cmdbuf, auto& dld) {
|
||||
const vk::ClearDepthStencilValue clear_zeta(clear_depth, clear_stencil);
|
||||
const vk::ClearValue clear_value{clear_zeta};
|
||||
const vk::ClearAttachment attachment(aspect_flags, 0, clear_value);
|
||||
cmdbuf.clearAttachments(1, &attachment, 1, &clear_rect, dld);
|
||||
});
|
||||
}
|
||||
|
||||
void RasterizerVulkan::DispatchCompute(GPUVAddr code_addr) {
|
||||
@@ -542,8 +548,6 @@ bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config,
|
||||
|
||||
// Verify that the cached surface is the same size and format as the requested framebuffer
|
||||
const auto& params{surface->GetSurfaceParams()};
|
||||
const auto& pixel_format{
|
||||
VideoCore::Surface::PixelFormatFromGPUPixelFormat(config.pixel_format)};
|
||||
ASSERT_MSG(params.width == config.width, "Framebuffer width is different");
|
||||
ASSERT_MSG(params.height == config.height, "Framebuffer height is different");
|
||||
|
||||
@@ -1151,7 +1155,7 @@ std::size_t RasterizerVulkan::CalculateVertexArraysSize() const {
|
||||
// This implementation assumes that all attributes are used in the shader.
|
||||
const GPUVAddr start{regs.vertex_array[index].StartAddress()};
|
||||
const GPUVAddr end{regs.vertex_array_limit[index].LimitAddress()};
|
||||
DEBUG_ASSERT(end > start);
|
||||
DEBUG_ASSERT(end >= start);
|
||||
|
||||
size += (end - start + 1) * regs.vertex_array[index].enable;
|
||||
}
|
||||
|
||||
@@ -253,6 +253,7 @@ private:
|
||||
VKStagingBufferPool staging_pool;
|
||||
VKDescriptorPool descriptor_pool;
|
||||
VKUpdateDescriptorQueue update_descriptor_queue;
|
||||
VKRenderPassCache renderpass_cache;
|
||||
QuadArrayPass quad_array_pass;
|
||||
Uint8Pass uint8_pass;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -100,7 +100,6 @@ void VKStagingBufferPool::ReleaseCache(bool host_visible) {
|
||||
}
|
||||
|
||||
u64 VKStagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, std::size_t log2) {
|
||||
static constexpr u64 epochs_to_destroy = 180;
|
||||
static constexpr std::size_t deletions_per_tick = 16;
|
||||
|
||||
auto& staging = cache[log2];
|
||||
@@ -108,6 +107,7 @@ u64 VKStagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, std::size_t lo
|
||||
const std::size_t old_size = entries.size();
|
||||
|
||||
const auto is_deleteable = [this](const auto& entry) {
|
||||
static constexpr u64 epochs_to_destroy = 180;
|
||||
return entry.last_epoch + epochs_to_destroy < epoch && !entry.watch.IsUsed();
|
||||
};
|
||||
const std::size_t begin_offset = staging.delete_index;
|
||||
|
||||
@@ -90,8 +90,6 @@ void StateTracker::Initialize() {
|
||||
SetupDirtyBlendConstants(tables);
|
||||
SetupDirtyDepthBounds(tables);
|
||||
SetupDirtyStencilProperties(tables);
|
||||
|
||||
SetupCommonOnWriteStores(dirty.on_write_stores);
|
||||
}
|
||||
|
||||
void StateTracker::InvalidateCommandBufferState() {
|
||||
|
||||
@@ -52,6 +52,9 @@ vk::ImageType SurfaceTargetToImage(SurfaceTarget target) {
|
||||
return vk::ImageType::e2D;
|
||||
case SurfaceTarget::Texture3D:
|
||||
return vk::ImageType::e3D;
|
||||
case SurfaceTarget::TextureBuffer:
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
UNREACHABLE_MSG("Unknown texture target={}", static_cast<u32>(target));
|
||||
return {};
|
||||
@@ -273,7 +276,6 @@ void CachedSurface::UploadImage(const std::vector<u8>& staging_buffer) {
|
||||
|
||||
for (u32 level = 0; level < params.num_levels; ++level) {
|
||||
vk::BufferImageCopy copy = GetBufferImageCopy(level);
|
||||
const auto& dld = device.GetDispatchLoader();
|
||||
if (image->GetAspectMask() ==
|
||||
(vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil)) {
|
||||
vk::BufferImageCopy depth = copy;
|
||||
@@ -422,7 +424,6 @@ void VKTextureCache::ImageCopy(Surface& src_surface, Surface& dst_surface,
|
||||
dst_base_layer, num_layers, copy_params.dest_level, 1, vk::PipelineStageFlagBits::eTransfer,
|
||||
vk::AccessFlagBits::eTransferWrite, vk::ImageLayout::eTransferDstOptimal);
|
||||
|
||||
const auto& dld{device.GetDispatchLoader()};
|
||||
const vk::ImageSubresourceLayers src_subresource(
|
||||
src_surface->GetAspectMask(), copy_params.source_level, copy_params.source_z, num_layers);
|
||||
const vk::ImageSubresourceLayers dst_subresource(
|
||||
@@ -458,7 +459,6 @@ void VKTextureCache::ImageBlit(View& src_view, View& dst_view,
|
||||
dst_view->GetImageSubresourceLayers(), {dst_top_left, dst_bot_right});
|
||||
const bool is_linear = copy_config.filter == Tegra::Engines::Fermi2D::Filter::Linear;
|
||||
|
||||
const auto& dld{device.GetDispatchLoader()};
|
||||
scheduler.Record([src_image = src_view->GetImage(), dst_image = dst_view->GetImage(), blit,
|
||||
is_linear](auto cmdbuf, auto& dld) {
|
||||
cmdbuf.blitImage(src_image, vk::ImageLayout::eTransferSrcOptimal, dst_image,
|
||||
|
||||
750
src/video_core/renderer_vulkan/wrapper.cpp
Normal file
750
src/video_core/renderer_vulkan/wrapper.cpp
Normal 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
|
||||
987
src/video_core/renderer_vulkan/wrapper.h
Normal file
987
src/video_core/renderer_vulkan/wrapper.h
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -17,33 +17,60 @@ u32 ShaderIR::DecodeBfe(NodeBlock& bb, u32 pc) {
|
||||
const Instruction instr = {program_code[pc]};
|
||||
const auto opcode = OpCode::Decode(instr);
|
||||
|
||||
UNIMPLEMENTED_IF(instr.bfe.negate_b);
|
||||
|
||||
Node op_a = GetRegister(instr.gpr8);
|
||||
op_a = GetOperandAbsNegInteger(op_a, false, instr.bfe.negate_a, false);
|
||||
Node op_b = [&] {
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::BFE_R:
|
||||
return GetRegister(instr.gpr20);
|
||||
case OpCode::Id::BFE_C:
|
||||
return GetConstBuffer(instr.cbuf34.index, instr.cbuf34.GetOffset());
|
||||
case OpCode::Id::BFE_IMM:
|
||||
return Immediate(instr.alu.GetSignedImm20_20());
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return Immediate(0);
|
||||
}
|
||||
}();
|
||||
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::BFE_IMM: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.generates_cc,
|
||||
"Condition codes generation in BFE is not implemented");
|
||||
UNIMPLEMENTED_IF_MSG(instr.bfe.rd_cc, "Condition codes in BFE is not implemented");
|
||||
|
||||
const Node inner_shift_imm = Immediate(static_cast<u32>(instr.bfe.GetLeftShiftValue()));
|
||||
const Node outer_shift_imm =
|
||||
Immediate(static_cast<u32>(instr.bfe.GetLeftShiftValue() + instr.bfe.shift_position));
|
||||
const bool is_signed = instr.bfe.is_signed;
|
||||
|
||||
const Node inner_shift =
|
||||
Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, op_a, inner_shift_imm);
|
||||
const Node outer_shift =
|
||||
Operation(OperationCode::ILogicalShiftRight, NO_PRECISE, inner_shift, outer_shift_imm);
|
||||
|
||||
SetInternalFlagsFromInteger(bb, outer_shift, instr.generates_cc);
|
||||
SetRegister(bb, instr.gpr0, outer_shift);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unhandled BFE instruction: {}", opcode->get().GetName());
|
||||
// using reverse parallel method in
|
||||
// https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel
|
||||
// note for later if possible to implement faster method.
|
||||
if (instr.bfe.brev) {
|
||||
const auto swap = [&](u32 s, u32 mask) {
|
||||
Node v1 =
|
||||
SignedOperation(OperationCode::ILogicalShiftRight, is_signed, op_a, Immediate(s));
|
||||
if (mask != 0) {
|
||||
v1 = SignedOperation(OperationCode::IBitwiseAnd, is_signed, std::move(v1),
|
||||
Immediate(mask));
|
||||
}
|
||||
Node v2 = op_a;
|
||||
if (mask != 0) {
|
||||
v2 = SignedOperation(OperationCode::IBitwiseAnd, is_signed, std::move(v2),
|
||||
Immediate(mask));
|
||||
}
|
||||
v2 = SignedOperation(OperationCode::ILogicalShiftLeft, is_signed, std::move(v2),
|
||||
Immediate(s));
|
||||
return SignedOperation(OperationCode::IBitwiseOr, is_signed, std::move(v1),
|
||||
std::move(v2));
|
||||
};
|
||||
op_a = swap(1, 0x55555555U);
|
||||
op_a = swap(2, 0x33333333U);
|
||||
op_a = swap(4, 0x0F0F0F0FU);
|
||||
op_a = swap(8, 0x00FF00FFU);
|
||||
op_a = swap(16, 0);
|
||||
}
|
||||
|
||||
const auto offset = SignedOperation(OperationCode::IBitfieldExtract, is_signed, op_b,
|
||||
Immediate(0), Immediate(8));
|
||||
const auto bits = SignedOperation(OperationCode::IBitfieldExtract, is_signed, op_b,
|
||||
Immediate(8), Immediate(8));
|
||||
auto result = SignedOperation(OperationCode::IBitfieldExtract, is_signed, op_a, offset, bits);
|
||||
SetRegister(bb, instr.gpr0, std::move(result));
|
||||
|
||||
return pc;
|
||||
}
|
||||
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace VideoCommon::Shader {
|
||||
|
||||
using Tegra::Shader::Instruction;
|
||||
using Tegra::Shader::OpCode;
|
||||
using Tegra::Shader::PredCondition;
|
||||
|
||||
u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) {
|
||||
const Instruction instr = {program_code[pc]};
|
||||
@@ -30,7 +31,7 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) {
|
||||
const bool is_signed_b = instr.xmad.sign_b == 1;
|
||||
const bool is_signed_c = is_signed_a;
|
||||
|
||||
auto [is_merge, is_psl, is_high_b, mode, op_b,
|
||||
auto [is_merge, is_psl, is_high_b, mode, op_b_binding,
|
||||
op_c] = [&]() -> std::tuple<bool, bool, bool, Tegra::Shader::XmadMode, Node, Node> {
|
||||
switch (opcode->get().GetId()) {
|
||||
case OpCode::Id::XMAD_CR:
|
||||
@@ -63,15 +64,19 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) {
|
||||
}
|
||||
}();
|
||||
|
||||
op_a = BitfieldExtract(op_a, instr.xmad.high_a ? 16 : 0, 16);
|
||||
op_a = SignedOperation(OperationCode::IBitfieldExtract, is_signed_a, std::move(op_a),
|
||||
instr.xmad.high_a ? Immediate(16) : Immediate(0), Immediate(16));
|
||||
|
||||
const Node original_b = op_b;
|
||||
op_b = BitfieldExtract(op_b, is_high_b ? 16 : 0, 16);
|
||||
const Node original_b = op_b_binding;
|
||||
const Node op_b =
|
||||
SignedOperation(OperationCode::IBitfieldExtract, is_signed_b, std::move(op_b_binding),
|
||||
is_high_b ? Immediate(16) : Immediate(0), Immediate(16));
|
||||
|
||||
// TODO(Rodrigo): Use an appropiate sign for this operation
|
||||
Node product = Operation(OperationCode::IMul, NO_PRECISE, op_a, op_b);
|
||||
// we already check sign_a and sign_b is difference or not before so just use one in here.
|
||||
Node product = SignedOperation(OperationCode::IMul, is_signed_a, op_a, op_b);
|
||||
if (is_psl) {
|
||||
product = Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, product, Immediate(16));
|
||||
product =
|
||||
SignedOperation(OperationCode::ILogicalShiftLeft, is_signed_a, product, Immediate(16));
|
||||
}
|
||||
SetTemporary(bb, 0, product);
|
||||
product = GetTemporary(0);
|
||||
@@ -88,12 +93,40 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) {
|
||||
return BitfieldExtract(original_c, 16, 16);
|
||||
case Tegra::Shader::XmadMode::CBcc: {
|
||||
const Node shifted_b = SignedOperation(OperationCode::ILogicalShiftLeft, is_signed_b,
|
||||
NO_PRECISE, original_b, Immediate(16));
|
||||
return SignedOperation(OperationCode::IAdd, is_signed_c, NO_PRECISE, original_c,
|
||||
shifted_b);
|
||||
original_b, Immediate(16));
|
||||
return SignedOperation(OperationCode::IAdd, is_signed_c, original_c, shifted_b);
|
||||
}
|
||||
case Tegra::Shader::XmadMode::CSfu: {
|
||||
const Node comp_a = GetPredicateComparisonInteger(PredCondition::Equal, is_signed_a,
|
||||
op_a, Immediate(0));
|
||||
const Node comp_b = GetPredicateComparisonInteger(PredCondition::Equal, is_signed_b,
|
||||
op_b, Immediate(0));
|
||||
const Node comp = Operation(OperationCode::LogicalOr, comp_a, comp_b);
|
||||
|
||||
const Node comp_minus_a = GetPredicateComparisonInteger(
|
||||
PredCondition::NotEqual, is_signed_a,
|
||||
SignedOperation(OperationCode::IBitwiseAnd, is_signed_a, op_a,
|
||||
Immediate(0x80000000)),
|
||||
Immediate(0));
|
||||
const Node comp_minus_b = GetPredicateComparisonInteger(
|
||||
PredCondition::NotEqual, is_signed_b,
|
||||
SignedOperation(OperationCode::IBitwiseAnd, is_signed_b, op_b,
|
||||
Immediate(0x80000000)),
|
||||
Immediate(0));
|
||||
|
||||
Node new_c = Operation(
|
||||
OperationCode::Select, comp_minus_a,
|
||||
SignedOperation(OperationCode::IAdd, is_signed_c, original_c, Immediate(-65536)),
|
||||
original_c);
|
||||
new_c = Operation(
|
||||
OperationCode::Select, comp_minus_b,
|
||||
SignedOperation(OperationCode::IAdd, is_signed_c, new_c, Immediate(-65536)),
|
||||
std::move(new_c));
|
||||
|
||||
return Operation(OperationCode::Select, comp, original_c, std::move(new_c));
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unhandled XMAD mode: {}", static_cast<u32>(instr.xmad.mode.Value()));
|
||||
UNREACHABLE();
|
||||
return Immediate(0);
|
||||
}
|
||||
}();
|
||||
@@ -102,18 +135,19 @@ u32 ShaderIR::DecodeXmad(NodeBlock& bb, u32 pc) {
|
||||
op_c = GetTemporary(1);
|
||||
|
||||
// TODO(Rodrigo): Use an appropiate sign for this operation
|
||||
Node sum = Operation(OperationCode::IAdd, product, op_c);
|
||||
Node sum = SignedOperation(OperationCode::IAdd, is_signed_a, product, std::move(op_c));
|
||||
SetTemporary(bb, 2, sum);
|
||||
sum = GetTemporary(2);
|
||||
if (is_merge) {
|
||||
const Node a = BitfieldExtract(sum, 0, 16);
|
||||
const Node b =
|
||||
Operation(OperationCode::ILogicalShiftLeft, NO_PRECISE, original_b, Immediate(16));
|
||||
sum = Operation(OperationCode::IBitwiseOr, NO_PRECISE, a, b);
|
||||
const Node a = SignedOperation(OperationCode::IBitfieldExtract, is_signed_a, std::move(sum),
|
||||
Immediate(0), Immediate(16));
|
||||
const Node b = SignedOperation(OperationCode::ILogicalShiftLeft, is_signed_b, original_b,
|
||||
Immediate(16));
|
||||
sum = SignedOperation(OperationCode::IBitwiseOr, is_signed_a, a, b);
|
||||
}
|
||||
|
||||
SetInternalFlagsFromInteger(bb, sum, instr.generates_cc);
|
||||
SetRegister(bb, instr.gpr0, sum);
|
||||
SetRegister(bb, instr.gpr0, std::move(sum));
|
||||
|
||||
return pc;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -68,6 +68,8 @@ OperationCode SignedToUnsignedCode(OperationCode operation_code, bool is_signed)
|
||||
return OperationCode::UBitwiseXor;
|
||||
case OperationCode::IBitwiseNot:
|
||||
return OperationCode::UBitwiseNot;
|
||||
case OperationCode::IBitfieldExtract:
|
||||
return OperationCode::UBitfieldExtract;
|
||||
case OperationCode::IBitfieldInsert:
|
||||
return OperationCode::UBitfieldInsert;
|
||||
case OperationCode::IBitCount:
|
||||
@@ -84,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 {};
|
||||
|
||||
@@ -96,6 +96,7 @@ Node ShaderIR::GetPredicate(bool immediate) {
|
||||
}
|
||||
|
||||
Node ShaderIR::GetInputAttribute(Attribute::Index index, u64 element, Node buffer) {
|
||||
MarkAttributeUsage(index, element);
|
||||
used_input_attributes.emplace(index);
|
||||
return MakeNode<AbufNode>(index, static_cast<u32>(element), std::move(buffer));
|
||||
}
|
||||
@@ -106,42 +107,8 @@ Node ShaderIR::GetPhysicalInputAttribute(Tegra::Shader::Register physical_addres
|
||||
}
|
||||
|
||||
Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buffer) {
|
||||
if (index == Attribute::Index::LayerViewportPointSize) {
|
||||
switch (element) {
|
||||
case 0:
|
||||
UNIMPLEMENTED();
|
||||
break;
|
||||
case 1:
|
||||
uses_layer = true;
|
||||
break;
|
||||
case 2:
|
||||
uses_viewport_index = true;
|
||||
break;
|
||||
case 3:
|
||||
uses_point_size = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index == Attribute::Index::TessCoordInstanceIDVertexID) {
|
||||
switch (element) {
|
||||
case 2:
|
||||
uses_instance_id = true;
|
||||
break;
|
||||
case 3:
|
||||
uses_vertex_id = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index == Attribute::Index::ClipDistances0123 ||
|
||||
index == Attribute::Index::ClipDistances4567) {
|
||||
const auto clip_index =
|
||||
static_cast<u32>((index == Attribute::Index::ClipDistances4567 ? 1 : 0) + element);
|
||||
used_clip_distances.at(clip_index) = true;
|
||||
}
|
||||
MarkAttributeUsage(index, element);
|
||||
used_output_attributes.insert(index);
|
||||
|
||||
return MakeNode<AbufNode>(index, static_cast<u32>(element), std::move(buffer));
|
||||
}
|
||||
|
||||
@@ -452,6 +419,54 @@ Node ShaderIR::BitfieldInsert(Node base, Node insert, u32 offset, u32 bits) {
|
||||
Immediate(bits));
|
||||
}
|
||||
|
||||
void ShaderIR::MarkAttributeUsage(Attribute::Index index, u64 element) {
|
||||
switch (index) {
|
||||
case Attribute::Index::LayerViewportPointSize:
|
||||
switch (element) {
|
||||
case 0:
|
||||
UNIMPLEMENTED();
|
||||
break;
|
||||
case 1:
|
||||
uses_layer = true;
|
||||
break;
|
||||
case 2:
|
||||
uses_viewport_index = true;
|
||||
break;
|
||||
case 3:
|
||||
uses_point_size = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Attribute::Index::TessCoordInstanceIDVertexID:
|
||||
switch (element) {
|
||||
case 2:
|
||||
uses_instance_id = true;
|
||||
break;
|
||||
case 3:
|
||||
uses_vertex_id = true;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Attribute::Index::ClipDistances0123:
|
||||
case Attribute::Index::ClipDistances4567: {
|
||||
const u64 clip_index = (index == Attribute::Index::ClipDistances4567 ? 4 : 0) + element;
|
||||
used_clip_distances.at(clip_index) = true;
|
||||
break;
|
||||
}
|
||||
case Attribute::Index::FrontColor:
|
||||
case Attribute::Index::FrontSecondaryColor:
|
||||
case Attribute::Index::BackColor:
|
||||
case Attribute::Index::BackSecondaryColor:
|
||||
uses_legacy_varyings = true;
|
||||
break;
|
||||
default:
|
||||
if (index >= Attribute::Index::TexCoord_0 && index <= Attribute::Index::TexCoord_7) {
|
||||
uses_legacy_varyings = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t ShaderIR::DeclareAmend(Node new_amend) {
|
||||
const std::size_t id = amend_code.size();
|
||||
amend_code.push_back(new_amend);
|
||||
|
||||
@@ -137,6 +137,10 @@ public:
|
||||
return uses_vertex_id;
|
||||
}
|
||||
|
||||
bool UsesLegacyVaryings() const {
|
||||
return uses_legacy_varyings;
|
||||
}
|
||||
|
||||
bool UsesWarps() const {
|
||||
return uses_warps;
|
||||
}
|
||||
@@ -343,6 +347,9 @@ private:
|
||||
/// Inserts a sequence of bits from a node
|
||||
Node BitfieldInsert(Node base, Node insert, u32 offset, u32 bits);
|
||||
|
||||
/// Marks the usage of a input or output attribute.
|
||||
void MarkAttributeUsage(Tegra::Shader::Attribute::Index index, u64 element);
|
||||
|
||||
void WriteTexInstructionFloat(NodeBlock& bb, Tegra::Shader::Instruction instr,
|
||||
const Node4& components);
|
||||
|
||||
@@ -443,6 +450,7 @@ private:
|
||||
bool uses_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes
|
||||
bool uses_instance_id{};
|
||||
bool uses_vertex_id{};
|
||||
bool uses_legacy_varyings{};
|
||||
bool uses_warps{};
|
||||
bool uses_indexed_samplers{};
|
||||
|
||||
|
||||
@@ -111,6 +111,8 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format)
|
||||
return PixelFormat::RGBA16F;
|
||||
case Tegra::RenderTargetFormat::RGBA16_UNORM:
|
||||
return PixelFormat::RGBA16U;
|
||||
case Tegra::RenderTargetFormat::RGBA16_SNORM:
|
||||
return PixelFormat::RGBA16S;
|
||||
case Tegra::RenderTargetFormat::RGBA16_UINT:
|
||||
return PixelFormat::RGBA16UI;
|
||||
case Tegra::RenderTargetFormat::RGBA32_FLOAT:
|
||||
|
||||
@@ -25,82 +25,83 @@ enum class PixelFormat {
|
||||
R8UI = 7,
|
||||
RGBA16F = 8,
|
||||
RGBA16U = 9,
|
||||
RGBA16UI = 10,
|
||||
R11FG11FB10F = 11,
|
||||
RGBA32UI = 12,
|
||||
DXT1 = 13,
|
||||
DXT23 = 14,
|
||||
DXT45 = 15,
|
||||
DXN1 = 16, // This is also known as BC4
|
||||
DXN2UNORM = 17,
|
||||
DXN2SNORM = 18,
|
||||
BC7U = 19,
|
||||
BC6H_UF16 = 20,
|
||||
BC6H_SF16 = 21,
|
||||
ASTC_2D_4X4 = 22,
|
||||
BGRA8 = 23,
|
||||
RGBA32F = 24,
|
||||
RG32F = 25,
|
||||
R32F = 26,
|
||||
R16F = 27,
|
||||
R16U = 28,
|
||||
R16S = 29,
|
||||
R16UI = 30,
|
||||
R16I = 31,
|
||||
RG16 = 32,
|
||||
RG16F = 33,
|
||||
RG16UI = 34,
|
||||
RG16I = 35,
|
||||
RG16S = 36,
|
||||
RGB32F = 37,
|
||||
RGBA8_SRGB = 38,
|
||||
RG8U = 39,
|
||||
RG8S = 40,
|
||||
RG32UI = 41,
|
||||
RGBX16F = 42,
|
||||
R32UI = 43,
|
||||
R32I = 44,
|
||||
ASTC_2D_8X8 = 45,
|
||||
ASTC_2D_8X5 = 46,
|
||||
ASTC_2D_5X4 = 47,
|
||||
BGRA8_SRGB = 48,
|
||||
DXT1_SRGB = 49,
|
||||
DXT23_SRGB = 50,
|
||||
DXT45_SRGB = 51,
|
||||
BC7U_SRGB = 52,
|
||||
R4G4B4A4U = 53,
|
||||
ASTC_2D_4X4_SRGB = 54,
|
||||
ASTC_2D_8X8_SRGB = 55,
|
||||
ASTC_2D_8X5_SRGB = 56,
|
||||
ASTC_2D_5X4_SRGB = 57,
|
||||
ASTC_2D_5X5 = 58,
|
||||
ASTC_2D_5X5_SRGB = 59,
|
||||
ASTC_2D_10X8 = 60,
|
||||
ASTC_2D_10X8_SRGB = 61,
|
||||
ASTC_2D_6X6 = 62,
|
||||
ASTC_2D_6X6_SRGB = 63,
|
||||
ASTC_2D_10X10 = 64,
|
||||
ASTC_2D_10X10_SRGB = 65,
|
||||
ASTC_2D_12X12 = 66,
|
||||
ASTC_2D_12X12_SRGB = 67,
|
||||
ASTC_2D_8X6 = 68,
|
||||
ASTC_2D_8X6_SRGB = 69,
|
||||
ASTC_2D_6X5 = 70,
|
||||
ASTC_2D_6X5_SRGB = 71,
|
||||
E5B9G9R9F = 72,
|
||||
RGBA16S = 10,
|
||||
RGBA16UI = 11,
|
||||
R11FG11FB10F = 12,
|
||||
RGBA32UI = 13,
|
||||
DXT1 = 14,
|
||||
DXT23 = 15,
|
||||
DXT45 = 16,
|
||||
DXN1 = 17, // This is also known as BC4
|
||||
DXN2UNORM = 18,
|
||||
DXN2SNORM = 19,
|
||||
BC7U = 20,
|
||||
BC6H_UF16 = 21,
|
||||
BC6H_SF16 = 22,
|
||||
ASTC_2D_4X4 = 23,
|
||||
BGRA8 = 24,
|
||||
RGBA32F = 25,
|
||||
RG32F = 26,
|
||||
R32F = 27,
|
||||
R16F = 28,
|
||||
R16U = 29,
|
||||
R16S = 30,
|
||||
R16UI = 31,
|
||||
R16I = 32,
|
||||
RG16 = 33,
|
||||
RG16F = 34,
|
||||
RG16UI = 35,
|
||||
RG16I = 36,
|
||||
RG16S = 37,
|
||||
RGB32F = 38,
|
||||
RGBA8_SRGB = 39,
|
||||
RG8U = 40,
|
||||
RG8S = 41,
|
||||
RG32UI = 42,
|
||||
RGBX16F = 43,
|
||||
R32UI = 44,
|
||||
R32I = 45,
|
||||
ASTC_2D_8X8 = 46,
|
||||
ASTC_2D_8X5 = 47,
|
||||
ASTC_2D_5X4 = 48,
|
||||
BGRA8_SRGB = 49,
|
||||
DXT1_SRGB = 50,
|
||||
DXT23_SRGB = 51,
|
||||
DXT45_SRGB = 52,
|
||||
BC7U_SRGB = 53,
|
||||
R4G4B4A4U = 54,
|
||||
ASTC_2D_4X4_SRGB = 55,
|
||||
ASTC_2D_8X8_SRGB = 56,
|
||||
ASTC_2D_8X5_SRGB = 57,
|
||||
ASTC_2D_5X4_SRGB = 58,
|
||||
ASTC_2D_5X5 = 59,
|
||||
ASTC_2D_5X5_SRGB = 60,
|
||||
ASTC_2D_10X8 = 61,
|
||||
ASTC_2D_10X8_SRGB = 62,
|
||||
ASTC_2D_6X6 = 63,
|
||||
ASTC_2D_6X6_SRGB = 64,
|
||||
ASTC_2D_10X10 = 65,
|
||||
ASTC_2D_10X10_SRGB = 66,
|
||||
ASTC_2D_12X12 = 67,
|
||||
ASTC_2D_12X12_SRGB = 68,
|
||||
ASTC_2D_8X6 = 69,
|
||||
ASTC_2D_8X6_SRGB = 70,
|
||||
ASTC_2D_6X5 = 71,
|
||||
ASTC_2D_6X5_SRGB = 72,
|
||||
E5B9G9R9F = 73,
|
||||
|
||||
MaxColorFormat,
|
||||
|
||||
// Depth formats
|
||||
Z32F = 73,
|
||||
Z16 = 74,
|
||||
Z32F = 74,
|
||||
Z16 = 75,
|
||||
|
||||
MaxDepthFormat,
|
||||
|
||||
// DepthStencil formats
|
||||
Z24S8 = 75,
|
||||
S8Z24 = 76,
|
||||
Z32FS8 = 77,
|
||||
Z24S8 = 76,
|
||||
S8Z24 = 77,
|
||||
Z32FS8 = 78,
|
||||
|
||||
MaxDepthStencilFormat,
|
||||
|
||||
@@ -138,6 +139,7 @@ constexpr std::array<u32, MaxPixelFormat> compression_factor_shift_table = {{
|
||||
0, // R8UI
|
||||
0, // RGBA16F
|
||||
0, // RGBA16U
|
||||
0, // RGBA16S
|
||||
0, // RGBA16UI
|
||||
0, // R11FG11FB10F
|
||||
0, // RGBA32UI
|
||||
@@ -235,6 +237,7 @@ constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
|
||||
1, // R8UI
|
||||
1, // RGBA16F
|
||||
1, // RGBA16U
|
||||
1, // RGBA16S
|
||||
1, // RGBA16UI
|
||||
1, // R11FG11FB10F
|
||||
1, // RGBA32UI
|
||||
@@ -324,6 +327,7 @@ constexpr std::array<u32, MaxPixelFormat> block_height_table = {{
|
||||
1, // R8UI
|
||||
1, // RGBA16F
|
||||
1, // RGBA16U
|
||||
1, // RGBA16S
|
||||
1, // RGBA16UI
|
||||
1, // R11FG11FB10F
|
||||
1, // RGBA32UI
|
||||
@@ -413,6 +417,7 @@ constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
|
||||
8, // R8UI
|
||||
64, // RGBA16F
|
||||
64, // RGBA16U
|
||||
64, // RGBA16S
|
||||
64, // RGBA16UI
|
||||
32, // R11FG11FB10F
|
||||
128, // RGBA32UI
|
||||
@@ -517,6 +522,7 @@ constexpr std::array<SurfaceCompression, MaxPixelFormat> compression_type_table
|
||||
SurfaceCompression::None, // R8UI
|
||||
SurfaceCompression::None, // RGBA16F
|
||||
SurfaceCompression::None, // RGBA16U
|
||||
SurfaceCompression::None, // RGBA16S
|
||||
SurfaceCompression::None, // RGBA16UI
|
||||
SurfaceCompression::None, // R11FG11FB10F
|
||||
SurfaceCompression::None, // RGBA32UI
|
||||
|
||||
@@ -41,7 +41,7 @@ struct Table {
|
||||
ComponentType alpha_component;
|
||||
bool is_srgb;
|
||||
};
|
||||
constexpr std::array<Table, 75> DefinitionTable = {{
|
||||
constexpr std::array<Table, 76> DefinitionTable = {{
|
||||
{TextureFormat::A8R8G8B8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ABGR8U},
|
||||
{TextureFormat::A8R8G8B8, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::ABGR8S},
|
||||
{TextureFormat::A8R8G8B8, C, UINT, UINT, UINT, UINT, PixelFormat::ABGR8UI},
|
||||
@@ -61,6 +61,7 @@ constexpr std::array<Table, 75> DefinitionTable = {{
|
||||
{TextureFormat::G8R8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::RG8U},
|
||||
{TextureFormat::G8R8, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::RG8S},
|
||||
|
||||
{TextureFormat::R16_G16_B16_A16, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::RGBA16S},
|
||||
{TextureFormat::R16_G16_B16_A16, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::RGBA16U},
|
||||
{TextureFormat::R16_G16_B16_A16, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::RGBA16F},
|
||||
{TextureFormat::R16_G16_B16_A16, C, UINT, UINT, UINT, UINT, PixelFormat::RGBA16UI},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user