Compare commits
44 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6ea0d010b | ||
|
|
27f122c48c | ||
|
|
588a20be3f | ||
|
|
69277de29d | ||
|
|
1633fbf99a | ||
|
|
59e75f4372 | ||
|
|
e6f02d5725 | ||
|
|
9d8886b1a4 | ||
|
|
0d4ca5a8fc | ||
|
|
e1bd89e1c2 | ||
|
|
825a6e2615 | ||
|
|
2339fe199f | ||
|
|
dd1232755b | ||
|
|
2f0da10dc3 | ||
|
|
b6571ca9f0 | ||
|
|
16270dcfe4 | ||
|
|
baf91c920c | ||
|
|
224a75d839 | ||
|
|
b03c0536ce | ||
|
|
5b95a01463 | ||
|
|
1c5e2b60a7 | ||
|
|
c19425ed69 | ||
|
|
238c35b2c9 | ||
|
|
defb9642da | ||
|
|
f1da3ec584 | ||
|
|
cb0a4151f8 | ||
|
|
c2665ec9c2 | ||
|
|
4f7bea403a | ||
|
|
c8f6d9effd | ||
|
|
972485ff18 | ||
|
|
93cac0d294 | ||
|
|
3dc09a6250 | ||
|
|
a2cc80b605 | ||
|
|
552f0ff267 | ||
|
|
2c780db5b9 | ||
|
|
c119473c40 | ||
|
|
a8f3a13a1f | ||
|
|
5300a918c6 | ||
|
|
523a709bf1 | ||
|
|
796b3319e6 | ||
|
|
5228bd0bb9 | ||
|
|
cf9c94d401 | ||
|
|
46791c464a | ||
|
|
282adfc70b |
@@ -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:
|
||||
|
||||
@@ -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"
|
||||
@@ -168,13 +167,12 @@ struct System::Impl {
|
||||
Service::Init(service_manager, system);
|
||||
GDBStub::DeferStart();
|
||||
|
||||
renderer = VideoCore::CreateRenderer(emu_window, system);
|
||||
if (!renderer->Init()) {
|
||||
interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
|
||||
gpu_core = VideoCore::CreateGPU(emu_window, system);
|
||||
if (!gpu_core) {
|
||||
return ResultStatus::ErrorVideoCore;
|
||||
}
|
||||
interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
|
||||
gpu_core = VideoCore::CreateGPU(system);
|
||||
renderer->Rasterizer().SetupDirtyFlags();
|
||||
gpu_core->Renderer().Rasterizer().SetupDirtyFlags();
|
||||
|
||||
is_powered_on = true;
|
||||
exit_lock = false;
|
||||
@@ -186,8 +184,6 @@ struct System::Impl {
|
||||
|
||||
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
|
||||
const std::string& filepath) {
|
||||
Core::Frontend::ScopeAcquireContext acquire_context{emu_window};
|
||||
|
||||
app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
|
||||
if (!app_loader) {
|
||||
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
|
||||
@@ -216,10 +212,6 @@ struct System::Impl {
|
||||
AddGlueRegistrationForProcess(*app_loader, *main_process);
|
||||
kernel.MakeCurrentProcess(main_process.get());
|
||||
|
||||
// Main process has been loaded and been made current.
|
||||
// Begin GPU and CPU execution.
|
||||
gpu_core->Start();
|
||||
|
||||
// Initialize cheat engine
|
||||
if (cheat_engine) {
|
||||
cheat_engine->Initialize();
|
||||
@@ -277,7 +269,6 @@ struct System::Impl {
|
||||
}
|
||||
|
||||
// Shutdown emulation session
|
||||
renderer.reset();
|
||||
GDBStub::Shutdown();
|
||||
Service::Shutdown();
|
||||
service_manager.reset();
|
||||
@@ -353,7 +344,6 @@ struct System::Impl {
|
||||
Service::FileSystem::FileSystemController fs_controller;
|
||||
/// AppLoader used to load the current executing application
|
||||
std::unique_ptr<Loader::AppLoader> app_loader;
|
||||
std::unique_ptr<VideoCore::RendererBase> renderer;
|
||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||
std::unique_ptr<Hardware::InterruptManager> interrupt_manager;
|
||||
Memory::Memory memory;
|
||||
@@ -536,11 +526,11 @@ const Core::Hardware::InterruptManager& System::InterruptManager() const {
|
||||
}
|
||||
|
||||
VideoCore::RendererBase& System::Renderer() {
|
||||
return *impl->renderer;
|
||||
return impl->gpu_core->Renderer();
|
||||
}
|
||||
|
||||
const VideoCore::RendererBase& System::Renderer() const {
|
||||
return *impl->renderer;
|
||||
return impl->gpu_core->Renderer();
|
||||
}
|
||||
|
||||
Kernel::KernelCore& System::Kernel() {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/fsmitm_romfsbuild.h"
|
||||
#include "core/file_sys/romfs.h"
|
||||
@@ -126,7 +127,7 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
|
||||
return out->GetSubdirectories().front();
|
||||
|
||||
while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) {
|
||||
if (out->GetSubdirectories().front()->GetName() == "data" &&
|
||||
if (Common::ToLower(out->GetSubdirectories().front()->GetName()) == "data" &&
|
||||
type == RomFSExtractionType::Truncated)
|
||||
break;
|
||||
out = out->GetSubdirectories().front();
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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());
|
||||
|
||||
@@ -231,18 +231,6 @@ enum class AtomicOp : u64 {
|
||||
Or = 6,
|
||||
Xor = 7,
|
||||
Exch = 8,
|
||||
};
|
||||
|
||||
enum class GlobalAtomicOp : u64 {
|
||||
Add = 0,
|
||||
Min = 1,
|
||||
Max = 2,
|
||||
Inc = 3,
|
||||
Dec = 4,
|
||||
And = 5,
|
||||
Or = 6,
|
||||
Xor = 7,
|
||||
Exch = 8,
|
||||
SafeAdd = 10,
|
||||
};
|
||||
|
||||
@@ -1001,7 +989,7 @@ union Instruction {
|
||||
} stg;
|
||||
|
||||
union {
|
||||
BitField<52, 4, GlobalAtomicOp> operation;
|
||||
BitField<52, 4, AtomicOp> operation;
|
||||
BitField<49, 3, GlobalAtomicType> type;
|
||||
BitField<28, 20, s64> offset;
|
||||
} atom;
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -16,7 +19,7 @@ enum class OutputTopology : u32 {
|
||||
TriangleStrip = 7,
|
||||
};
|
||||
|
||||
enum class AttributeUse : u8 {
|
||||
enum class PixelImap : u8 {
|
||||
Unused = 0,
|
||||
Constant = 1,
|
||||
Perspective = 2,
|
||||
@@ -24,7 +27,7 @@ enum class AttributeUse : u8 {
|
||||
};
|
||||
|
||||
// Documentation in:
|
||||
// http://download.nvidia.com/open-gpu-doc/Shader-Program-Header/1/Shader-Program-Header.html#ImapTexture
|
||||
// http://download.nvidia.com/open-gpu-doc/Shader-Program-Header/1/Shader-Program-Header.html
|
||||
struct Header {
|
||||
union {
|
||||
BitField<0, 5, u32> sph_type;
|
||||
@@ -59,8 +62,8 @@ struct Header {
|
||||
union {
|
||||
BitField<0, 12, u32> max_output_vertices;
|
||||
BitField<12, 8, u32> store_req_start; // NOTE: not used by geometry shaders.
|
||||
BitField<24, 4, u32> reserved;
|
||||
BitField<12, 8, u32> store_req_end; // NOTE: not used by geometry shaders.
|
||||
BitField<20, 4, u32> reserved;
|
||||
BitField<24, 8, u32> store_req_end; // NOTE: not used by geometry shaders.
|
||||
} common4{};
|
||||
|
||||
union {
|
||||
@@ -93,17 +96,20 @@ struct Header {
|
||||
struct {
|
||||
INSERT_UNION_PADDING_BYTES(3); // ImapSystemValuesA
|
||||
INSERT_UNION_PADDING_BYTES(1); // ImapSystemValuesB
|
||||
|
||||
union {
|
||||
BitField<0, 2, AttributeUse> x;
|
||||
BitField<2, 2, AttributeUse> y;
|
||||
BitField<4, 2, AttributeUse> w;
|
||||
BitField<6, 2, AttributeUse> z;
|
||||
BitField<0, 2, PixelImap> x;
|
||||
BitField<2, 2, PixelImap> y;
|
||||
BitField<4, 2, PixelImap> z;
|
||||
BitField<6, 2, PixelImap> w;
|
||||
u8 raw;
|
||||
} imap_generic_vector[32];
|
||||
|
||||
INSERT_UNION_PADDING_BYTES(2); // ImapColor
|
||||
INSERT_UNION_PADDING_BYTES(2); // ImapSystemValuesC
|
||||
INSERT_UNION_PADDING_BYTES(10); // ImapFixedFncTexture[10]
|
||||
INSERT_UNION_PADDING_BYTES(2); // ImapReserved
|
||||
|
||||
struct {
|
||||
u32 target;
|
||||
union {
|
||||
@@ -112,31 +118,30 @@ struct Header {
|
||||
BitField<2, 30, u32> reserved;
|
||||
};
|
||||
} omap;
|
||||
|
||||
bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const {
|
||||
const u32 bit = render_target * 4 + component;
|
||||
return omap.target & (1 << bit);
|
||||
}
|
||||
AttributeUse GetAttributeIndexUse(u32 attribute, u32 index) const {
|
||||
return static_cast<AttributeUse>(
|
||||
(imap_generic_vector[attribute].raw >> (index * 2)) & 0x03);
|
||||
}
|
||||
AttributeUse GetAttributeUse(u32 attribute) const {
|
||||
AttributeUse result = AttributeUse::Unused;
|
||||
for (u32 i = 0; i < 4; i++) {
|
||||
const auto index = GetAttributeIndexUse(attribute, i);
|
||||
if (index == AttributeUse::Unused) {
|
||||
|
||||
PixelImap GetPixelImap(u32 attribute) const {
|
||||
const auto get_index = [this, attribute](u32 index) {
|
||||
return static_cast<PixelImap>(
|
||||
(imap_generic_vector[attribute].raw >> (index * 2)) & 3);
|
||||
};
|
||||
|
||||
std::optional<PixelImap> result;
|
||||
for (u32 component = 0; component < 4; ++component) {
|
||||
const PixelImap index = get_index(component);
|
||||
if (index == PixelImap::Unused) {
|
||||
continue;
|
||||
}
|
||||
if (result == AttributeUse::Unused || result == index) {
|
||||
result = index;
|
||||
continue;
|
||||
}
|
||||
LOG_CRITICAL(HW_GPU, "Generic Attribute Conflict in Interpolation Mode");
|
||||
if (index == AttributeUse::Perspective) {
|
||||
result = index;
|
||||
if (result && result != index) {
|
||||
LOG_CRITICAL(HW_GPU, "Generic attribute conflict in interpolation mode");
|
||||
}
|
||||
result = index;
|
||||
}
|
||||
return result;
|
||||
return result.value_or(PixelImap::Unused);
|
||||
}
|
||||
} ps;
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -129,7 +132,8 @@ class MemoryManager;
|
||||
|
||||
class GPU {
|
||||
public:
|
||||
explicit GPU(Core::System& system, VideoCore::RendererBase& renderer, bool is_async);
|
||||
explicit GPU(Core::System& system, std::unique_ptr<VideoCore::RendererBase>&& renderer,
|
||||
bool is_async);
|
||||
|
||||
virtual ~GPU();
|
||||
|
||||
@@ -174,6 +178,14 @@ public:
|
||||
/// Returns a reference to the GPU DMA pusher.
|
||||
Tegra::DmaPusher& DmaPusher();
|
||||
|
||||
VideoCore::RendererBase& Renderer() {
|
||||
return *renderer;
|
||||
}
|
||||
|
||||
const VideoCore::RendererBase& Renderer() const {
|
||||
return *renderer;
|
||||
}
|
||||
|
||||
// Waits for the GPU to finish working
|
||||
virtual void WaitIdle() const = 0;
|
||||
|
||||
@@ -287,7 +299,7 @@ private:
|
||||
protected:
|
||||
std::unique_ptr<Tegra::DmaPusher> dma_pusher;
|
||||
Core::System& system;
|
||||
VideoCore::RendererBase& renderer;
|
||||
std::unique_ptr<VideoCore::RendererBase> renderer;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Tegra::MemoryManager> memory_manager;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
// ------------------------
|
||||
|
||||
@@ -131,6 +131,31 @@ std::array<Device::BaseBindings, Tegra::Engines::MaxShaderTypes> BuildBaseBindin
|
||||
return bindings;
|
||||
}
|
||||
|
||||
bool IsASTCSupported() {
|
||||
static constexpr std::array formats = {
|
||||
GL_COMPRESSED_RGBA_ASTC_4x4_KHR, GL_COMPRESSED_RGBA_ASTC_5x4_KHR,
|
||||
GL_COMPRESSED_RGBA_ASTC_5x5_KHR, GL_COMPRESSED_RGBA_ASTC_6x5_KHR,
|
||||
GL_COMPRESSED_RGBA_ASTC_6x6_KHR, GL_COMPRESSED_RGBA_ASTC_8x5_KHR,
|
||||
GL_COMPRESSED_RGBA_ASTC_8x6_KHR, GL_COMPRESSED_RGBA_ASTC_8x8_KHR,
|
||||
GL_COMPRESSED_RGBA_ASTC_10x5_KHR, GL_COMPRESSED_RGBA_ASTC_10x6_KHR,
|
||||
GL_COMPRESSED_RGBA_ASTC_10x8_KHR, GL_COMPRESSED_RGBA_ASTC_10x10_KHR,
|
||||
GL_COMPRESSED_RGBA_ASTC_12x10_KHR, GL_COMPRESSED_RGBA_ASTC_12x12_KHR,
|
||||
GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR,
|
||||
GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR,
|
||||
GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR,
|
||||
GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR,
|
||||
GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR,
|
||||
GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR,
|
||||
GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR,
|
||||
};
|
||||
return std::find_if_not(formats.begin(), formats.end(), [](GLenum format) {
|
||||
GLint supported;
|
||||
glGetInternalformativ(GL_TEXTURE_2D, format, GL_INTERNALFORMAT_SUPPORTED, 1,
|
||||
&supported);
|
||||
return supported == GL_TRUE;
|
||||
}) == formats.end();
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
Device::Device() : base_bindings{BuildBaseBindings()} {
|
||||
@@ -152,6 +177,7 @@ Device::Device() : base_bindings{BuildBaseBindings()} {
|
||||
has_shader_ballot = GLAD_GL_ARB_shader_ballot;
|
||||
has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array;
|
||||
has_image_load_formatted = HasExtension(extensions, "GL_EXT_shader_image_load_formatted");
|
||||
has_astc = IsASTCSupported();
|
||||
has_variable_aoffi = TestVariableAoffi();
|
||||
has_component_indexing_bug = is_amd;
|
||||
has_precise_bug = TestPreciseBug();
|
||||
|
||||
@@ -64,6 +64,10 @@ public:
|
||||
return has_image_load_formatted;
|
||||
}
|
||||
|
||||
bool HasASTC() const {
|
||||
return has_astc;
|
||||
}
|
||||
|
||||
bool HasVariableAoffi() const {
|
||||
return has_variable_aoffi;
|
||||
}
|
||||
@@ -97,6 +101,7 @@ private:
|
||||
bool has_shader_ballot{};
|
||||
bool has_vertex_viewport_layer{};
|
||||
bool has_image_load_formatted{};
|
||||
bool has_astc{};
|
||||
bool has_variable_aoffi{};
|
||||
bool has_component_indexing_bug{};
|
||||
bool has_precise_bug{};
|
||||
|
||||
@@ -386,11 +386,14 @@ void RasterizerOpenGL::ConfigureClearFramebuffer(bool using_color_fb, bool using
|
||||
texture_cache.GuardRenderTargets(true);
|
||||
View color_surface;
|
||||
if (using_color_fb) {
|
||||
color_surface = texture_cache.GetColorBufferSurface(regs.clear_buffers.RT, false);
|
||||
const std::size_t index = regs.clear_buffers.RT;
|
||||
color_surface = texture_cache.GetColorBufferSurface(index, true);
|
||||
texture_cache.MarkColorBufferInUse(index);
|
||||
}
|
||||
View depth_surface;
|
||||
if (using_depth_fb || using_stencil_fb) {
|
||||
depth_surface = texture_cache.GetDepthBufferSurface(false);
|
||||
depth_surface = texture_cache.GetDepthBufferSurface(true);
|
||||
texture_cache.MarkDepthBufferInUse();
|
||||
}
|
||||
texture_cache.GuardRenderTargets(false);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -31,11 +31,11 @@ namespace {
|
||||
|
||||
using Tegra::Engines::ShaderType;
|
||||
using Tegra::Shader::Attribute;
|
||||
using Tegra::Shader::AttributeUse;
|
||||
using Tegra::Shader::Header;
|
||||
using Tegra::Shader::IpaInterpMode;
|
||||
using Tegra::Shader::IpaMode;
|
||||
using Tegra::Shader::IpaSampleMode;
|
||||
using Tegra::Shader::PixelImap;
|
||||
using Tegra::Shader::Register;
|
||||
using VideoCommon::Shader::BuildTransformFeedback;
|
||||
using VideoCommon::Shader::Registry;
|
||||
@@ -702,20 +702,19 @@ private:
|
||||
code.AddNewLine();
|
||||
}
|
||||
|
||||
std::string GetInputFlags(AttributeUse attribute) {
|
||||
const char* GetInputFlags(PixelImap attribute) {
|
||||
switch (attribute) {
|
||||
case AttributeUse::Perspective:
|
||||
// Default, Smooth
|
||||
return {};
|
||||
case AttributeUse::Constant:
|
||||
return "flat ";
|
||||
case AttributeUse::ScreenLinear:
|
||||
return "noperspective ";
|
||||
default:
|
||||
case AttributeUse::Unused:
|
||||
UNIMPLEMENTED_MSG("Unknown attribute usage index={}", static_cast<u32>(attribute));
|
||||
return {};
|
||||
case PixelImap::Perspective:
|
||||
return "smooth";
|
||||
case PixelImap::Constant:
|
||||
return "flat";
|
||||
case PixelImap::ScreenLinear:
|
||||
return "noperspective";
|
||||
case PixelImap::Unused:
|
||||
break;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unknown attribute usage index={}", static_cast<int>(attribute));
|
||||
return {};
|
||||
}
|
||||
|
||||
void DeclareInputAttributes() {
|
||||
@@ -749,8 +748,8 @@ private:
|
||||
|
||||
std::string suffix;
|
||||
if (stage == ShaderType::Fragment) {
|
||||
const auto input_mode{header.ps.GetAttributeUse(location)};
|
||||
if (skip_unused && input_mode == AttributeUse::Unused) {
|
||||
const auto input_mode{header.ps.GetPixelImap(location)};
|
||||
if (input_mode == PixelImap::Unused) {
|
||||
return;
|
||||
}
|
||||
suffix = GetInputFlags(input_mode);
|
||||
@@ -927,7 +926,7 @@ private:
|
||||
const u32 address{generic_base + index * generic_stride + element * element_stride};
|
||||
|
||||
const bool declared = stage != ShaderType::Fragment ||
|
||||
header.ps.GetAttributeUse(index) != AttributeUse::Unused;
|
||||
header.ps.GetPixelImap(index) != PixelImap::Unused;
|
||||
const std::string value =
|
||||
declared ? ReadAttribute(attribute, element).AsFloat() : "0.0f";
|
||||
code.AddLine("case 0x{:X}U: return {};", address, value);
|
||||
@@ -1142,8 +1141,7 @@ private:
|
||||
GetSwizzle(element)),
|
||||
Type::Float};
|
||||
case ShaderType::Fragment:
|
||||
return {element == 3 ? "1.0f" : ("gl_FragCoord"s + GetSwizzle(element)),
|
||||
Type::Float};
|
||||
return {"gl_FragCoord"s + GetSwizzle(element), Type::Float};
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -2114,6 +2112,10 @@ private:
|
||||
|
||||
template <const std::string_view& opname, Type type>
|
||||
Expression Atomic(Operation operation) {
|
||||
if ((opname == Func::Min || opname == Func::Max) && type == Type::Int) {
|
||||
UNIMPLEMENTED_MSG("Unimplemented Min & Max for atomic operations");
|
||||
return {};
|
||||
}
|
||||
return {fmt::format("atomic{}({}, {})", opname, Visit(operation[0]).GetCode(),
|
||||
Visit(operation[1]).As(type)),
|
||||
type};
|
||||
@@ -2307,6 +2309,8 @@ private:
|
||||
~Func() = delete;
|
||||
|
||||
static constexpr std::string_view Add = "Add";
|
||||
static constexpr std::string_view Min = "Min";
|
||||
static constexpr std::string_view Max = "Max";
|
||||
static constexpr std::string_view And = "And";
|
||||
static constexpr std::string_view Or = "Or";
|
||||
static constexpr std::string_view Xor = "Xor";
|
||||
@@ -2457,7 +2461,21 @@ private:
|
||||
&GLSLDecompiler::AtomicImage<Func::Xor>,
|
||||
&GLSLDecompiler::AtomicImage<Func::Exchange>,
|
||||
|
||||
&GLSLDecompiler::Atomic<Func::Exchange, Type::Uint>,
|
||||
&GLSLDecompiler::Atomic<Func::Add, Type::Uint>,
|
||||
&GLSLDecompiler::Atomic<Func::Min, Type::Uint>,
|
||||
&GLSLDecompiler::Atomic<Func::Max, Type::Uint>,
|
||||
&GLSLDecompiler::Atomic<Func::And, Type::Uint>,
|
||||
&GLSLDecompiler::Atomic<Func::Or, Type::Uint>,
|
||||
&GLSLDecompiler::Atomic<Func::Xor, Type::Uint>,
|
||||
|
||||
&GLSLDecompiler::Atomic<Func::Exchange, Type::Int>,
|
||||
&GLSLDecompiler::Atomic<Func::Add, Type::Int>,
|
||||
&GLSLDecompiler::Atomic<Func::Min, Type::Int>,
|
||||
&GLSLDecompiler::Atomic<Func::Max, Type::Int>,
|
||||
&GLSLDecompiler::Atomic<Func::And, Type::Int>,
|
||||
&GLSLDecompiler::Atomic<Func::Or, Type::Int>,
|
||||
&GLSLDecompiler::Atomic<Func::Xor, Type::Int>,
|
||||
|
||||
&GLSLDecompiler::Branch,
|
||||
&GLSLDecompiler::BranchIndirect,
|
||||
|
||||
@@ -24,7 +24,6 @@ using Tegra::Texture::SwizzleSource;
|
||||
using VideoCore::MortonSwizzleMode;
|
||||
|
||||
using VideoCore::Surface::PixelFormat;
|
||||
using VideoCore::Surface::SurfaceCompression;
|
||||
using VideoCore::Surface::SurfaceTarget;
|
||||
using VideoCore::Surface::SurfaceType;
|
||||
|
||||
@@ -37,102 +36,100 @@ namespace {
|
||||
|
||||
struct FormatTuple {
|
||||
GLint internal_format;
|
||||
GLenum format;
|
||||
GLenum type;
|
||||
bool compressed;
|
||||
GLenum format = GL_NONE;
|
||||
GLenum type = GL_NONE;
|
||||
};
|
||||
|
||||
constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format_tuples = {{
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, false}, // ABGR8U
|
||||
{GL_RGBA8_SNORM, GL_RGBA, GL_BYTE, false}, // ABGR8S
|
||||
{GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, false}, // ABGR8UI
|
||||
{GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV, false}, // B5G6R5U
|
||||
{GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, false}, // A2B10G10R10U
|
||||
{GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, false}, // A1B5G5R5U
|
||||
{GL_R8, GL_RED, GL_UNSIGNED_BYTE, false}, // R8U
|
||||
{GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, false}, // R8UI
|
||||
{GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false}, // RGBA16F
|
||||
{GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, false}, // RGBA16U
|
||||
{GL_RGBA16_SNORM, GL_RGBA, GL_SHORT, false}, // RGBA16S
|
||||
{GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, false}, // RGBA16UI
|
||||
{GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, false}, // R11FG11FB10F
|
||||
{GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, false}, // RGBA32UI
|
||||
{GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT1
|
||||
{GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT23
|
||||
{GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT45
|
||||
{GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, true}, // DXN1
|
||||
{GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_INT_8_8_8_8, true}, // DXN2UNORM
|
||||
{GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_INT, true}, // DXN2SNORM
|
||||
{GL_COMPRESSED_RGBA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // BC7U
|
||||
{GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true}, // BC6H_UF16
|
||||
{GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true}, // BC6H_SF16
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_4X4
|
||||
{GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE, false}, // BGRA8
|
||||
{GL_RGBA32F, GL_RGBA, GL_FLOAT, false}, // RGBA32F
|
||||
{GL_RG32F, GL_RG, GL_FLOAT, false}, // RG32F
|
||||
{GL_R32F, GL_RED, GL_FLOAT, false}, // R32F
|
||||
{GL_R16F, GL_RED, GL_HALF_FLOAT, false}, // R16F
|
||||
{GL_R16, GL_RED, GL_UNSIGNED_SHORT, false}, // R16U
|
||||
{GL_R16_SNORM, GL_RED, GL_SHORT, false}, // R16S
|
||||
{GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT, false}, // R16UI
|
||||
{GL_R16I, GL_RED_INTEGER, GL_SHORT, false}, // R16I
|
||||
{GL_RG16, GL_RG, GL_UNSIGNED_SHORT, false}, // RG16
|
||||
{GL_RG16F, GL_RG, GL_HALF_FLOAT, false}, // RG16F
|
||||
{GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT, false}, // RG16UI
|
||||
{GL_RG16I, GL_RG_INTEGER, GL_SHORT, false}, // RG16I
|
||||
{GL_RG16_SNORM, GL_RG, GL_SHORT, false}, // RG16S
|
||||
{GL_RGB32F, GL_RGB, GL_FLOAT, false}, // RGB32F
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, false}, // RGBA8_SRGB
|
||||
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE, false}, // RG8U
|
||||
{GL_RG8_SNORM, GL_RG, GL_BYTE, false}, // RG8S
|
||||
{GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, false}, // RG32UI
|
||||
{GL_RGB16F, GL_RGBA, GL_HALF_FLOAT, false}, // RGBX16F
|
||||
{GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, false}, // R32UI
|
||||
{GL_R32I, GL_RED_INTEGER, GL_INT, false}, // R32I
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X8
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X5
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_5X4
|
||||
{GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE, false}, // BGRA8
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, // ABGR8U
|
||||
{GL_RGBA8_SNORM, GL_RGBA, GL_BYTE}, // ABGR8S
|
||||
{GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE}, // ABGR8UI
|
||||
{GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5_REV}, // B5G6R5U
|
||||
{GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}, // A2B10G10R10U
|
||||
{GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV}, // A1B5G5R5U
|
||||
{GL_R8, GL_RED, GL_UNSIGNED_BYTE}, // R8U
|
||||
{GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE}, // R8UI
|
||||
{GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT}, // RGBA16F
|
||||
{GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT}, // RGBA16U
|
||||
{GL_RGBA16_SNORM, GL_RGBA, GL_SHORT}, // RGBA16S
|
||||
{GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT}, // RGBA16UI
|
||||
{GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV}, // R11FG11FB10F
|
||||
{GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT}, // RGBA32UI
|
||||
{GL_COMPRESSED_RGBA_S3TC_DXT1_EXT}, // DXT1
|
||||
{GL_COMPRESSED_RGBA_S3TC_DXT3_EXT}, // DXT23
|
||||
{GL_COMPRESSED_RGBA_S3TC_DXT5_EXT}, // DXT45
|
||||
{GL_COMPRESSED_RED_RGTC1}, // DXN1
|
||||
{GL_COMPRESSED_RG_RGTC2}, // DXN2UNORM
|
||||
{GL_COMPRESSED_SIGNED_RG_RGTC2}, // DXN2SNORM
|
||||
{GL_COMPRESSED_RGBA_BPTC_UNORM}, // BC7U
|
||||
{GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT}, // BC6H_UF16
|
||||
{GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT}, // BC6H_SF16
|
||||
{GL_COMPRESSED_RGBA_ASTC_4x4_KHR}, // ASTC_2D_4X4
|
||||
{GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}, // BGRA8
|
||||
{GL_RGBA32F, GL_RGBA, GL_FLOAT}, // RGBA32F
|
||||
{GL_RG32F, GL_RG, GL_FLOAT}, // RG32F
|
||||
{GL_R32F, GL_RED, GL_FLOAT}, // R32F
|
||||
{GL_R16F, GL_RED, GL_HALF_FLOAT}, // R16F
|
||||
{GL_R16, GL_RED, GL_UNSIGNED_SHORT}, // R16U
|
||||
{GL_R16_SNORM, GL_RED, GL_SHORT}, // R16S
|
||||
{GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT}, // R16UI
|
||||
{GL_R16I, GL_RED_INTEGER, GL_SHORT}, // R16I
|
||||
{GL_RG16, GL_RG, GL_UNSIGNED_SHORT}, // RG16
|
||||
{GL_RG16F, GL_RG, GL_HALF_FLOAT}, // RG16F
|
||||
{GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT}, // RG16UI
|
||||
{GL_RG16I, GL_RG_INTEGER, GL_SHORT}, // RG16I
|
||||
{GL_RG16_SNORM, GL_RG, GL_SHORT}, // RG16S
|
||||
{GL_RGB32F, GL_RGB, GL_FLOAT}, // RGB32F
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV}, // RGBA8_SRGB
|
||||
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE}, // RG8U
|
||||
{GL_RG8_SNORM, GL_RG, GL_BYTE}, // RG8S
|
||||
{GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT}, // RG32UI
|
||||
{GL_RGB16F, GL_RGBA, GL_HALF_FLOAT}, // RGBX16F
|
||||
{GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT}, // R32UI
|
||||
{GL_R32I, GL_RED_INTEGER, GL_INT}, // R32I
|
||||
{GL_COMPRESSED_RGBA_ASTC_8x8_KHR}, // ASTC_2D_8X8
|
||||
{GL_COMPRESSED_RGBA_ASTC_8x5_KHR}, // ASTC_2D_8X5
|
||||
{GL_COMPRESSED_RGBA_ASTC_5x4_KHR}, // ASTC_2D_5X4
|
||||
{GL_SRGB8_ALPHA8, GL_BGRA, GL_UNSIGNED_BYTE}, // BGRA8
|
||||
// Compressed sRGB formats
|
||||
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT1_SRGB
|
||||
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT23_SRGB
|
||||
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT45_SRGB
|
||||
{GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // BC7U_SRGB
|
||||
{GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV, false}, // R4G4B4A4U
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_4X4_SRGB
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X8_SRGB
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X5_SRGB
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_5X4_SRGB
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_5X5
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_5X5_SRGB
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_10X8
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_10X8_SRGB
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_6X6
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_6X6_SRGB
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_10X10
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_10X10_SRGB
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_12X12
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_12X12_SRGB
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X6
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_8X6_SRGB
|
||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_6X5
|
||||
{GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_6X5_SRGB
|
||||
{GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV, false}, // E5B9G9R9F
|
||||
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT}, // DXT1_SRGB
|
||||
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT}, // DXT23_SRGB
|
||||
{GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}, // DXT45_SRGB
|
||||
{GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM}, // BC7U_SRGB
|
||||
{GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4_REV}, // R4G4B4A4U
|
||||
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR}, // ASTC_2D_4X4_SRGB
|
||||
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR}, // ASTC_2D_8X8_SRGB
|
||||
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR}, // ASTC_2D_8X5_SRGB
|
||||
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR}, // ASTC_2D_5X4_SRGB
|
||||
{GL_COMPRESSED_RGBA_ASTC_5x5_KHR}, // ASTC_2D_5X5
|
||||
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR}, // ASTC_2D_5X5_SRGB
|
||||
{GL_COMPRESSED_RGBA_ASTC_10x8_KHR}, // ASTC_2D_10X8
|
||||
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR}, // ASTC_2D_10X8_SRGB
|
||||
{GL_COMPRESSED_RGBA_ASTC_6x6_KHR}, // ASTC_2D_6X6
|
||||
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR}, // ASTC_2D_6X6_SRGB
|
||||
{GL_COMPRESSED_RGBA_ASTC_10x10_KHR}, // ASTC_2D_10X10
|
||||
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR}, // ASTC_2D_10X10_SRGB
|
||||
{GL_COMPRESSED_RGBA_ASTC_12x12_KHR}, // ASTC_2D_12X12
|
||||
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR}, // ASTC_2D_12X12_SRGB
|
||||
{GL_COMPRESSED_RGBA_ASTC_8x6_KHR}, // ASTC_2D_8X6
|
||||
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR}, // ASTC_2D_8X6_SRGB
|
||||
{GL_COMPRESSED_RGBA_ASTC_6x5_KHR}, // ASTC_2D_6X5
|
||||
{GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR}, // ASTC_2D_6X5_SRGB
|
||||
{GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV}, // E5B9G9R9F
|
||||
|
||||
// Depth formats
|
||||
{GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, false}, // Z32F
|
||||
{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, false}, // Z16
|
||||
{GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT}, // Z32F
|
||||
{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // Z16
|
||||
|
||||
// DepthStencil formats
|
||||
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, false}, // Z24S8
|
||||
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, false}, // S8Z24
|
||||
{GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, false}, // Z32FS8
|
||||
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // Z24S8
|
||||
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // S8Z24
|
||||
{GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV}, // Z32FS8
|
||||
}};
|
||||
|
||||
const FormatTuple& GetFormatTuple(PixelFormat pixel_format) {
|
||||
ASSERT(static_cast<std::size_t>(pixel_format) < tex_format_tuples.size());
|
||||
const auto& format{tex_format_tuples[static_cast<std::size_t>(pixel_format)]};
|
||||
return format;
|
||||
return tex_format_tuples[static_cast<std::size_t>(pixel_format)];
|
||||
}
|
||||
|
||||
GLenum GetTextureTarget(const SurfaceTarget& target) {
|
||||
@@ -242,13 +239,20 @@ OGLTexture CreateTexture(const SurfaceParams& params, GLenum target, GLenum inte
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
CachedSurface::CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& params)
|
||||
: VideoCommon::SurfaceBase<View>(gpu_addr, params) {
|
||||
const auto& tuple{GetFormatTuple(params.pixel_format)};
|
||||
internal_format = tuple.internal_format;
|
||||
format = tuple.format;
|
||||
type = tuple.type;
|
||||
is_compressed = tuple.compressed;
|
||||
CachedSurface::CachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& params,
|
||||
bool is_astc_supported)
|
||||
: VideoCommon::SurfaceBase<View>(gpu_addr, params, is_astc_supported) {
|
||||
if (is_converted) {
|
||||
internal_format = params.srgb_conversion ? GL_SRGB8_ALPHA8 : GL_RGBA8;
|
||||
format = GL_RGBA;
|
||||
type = GL_UNSIGNED_BYTE;
|
||||
} else {
|
||||
const auto& tuple{GetFormatTuple(params.pixel_format)};
|
||||
internal_format = tuple.internal_format;
|
||||
format = tuple.format;
|
||||
type = tuple.type;
|
||||
is_compressed = params.IsCompressed();
|
||||
}
|
||||
target = GetTextureTarget(params.target);
|
||||
texture = CreateTexture(params, target, internal_format, texture_buffer);
|
||||
DecorateSurfaceName();
|
||||
@@ -264,7 +268,7 @@ void CachedSurface::DownloadTexture(std::vector<u8>& staging_buffer) {
|
||||
|
||||
if (params.IsBuffer()) {
|
||||
glGetNamedBufferSubData(texture_buffer.handle, 0,
|
||||
static_cast<GLsizeiptr>(params.GetHostSizeInBytes()),
|
||||
static_cast<GLsizeiptr>(params.GetHostSizeInBytes(false)),
|
||||
staging_buffer.data());
|
||||
return;
|
||||
}
|
||||
@@ -272,9 +276,10 @@ void CachedSurface::DownloadTexture(std::vector<u8>& staging_buffer) {
|
||||
SCOPE_EXIT({ glPixelStorei(GL_PACK_ROW_LENGTH, 0); });
|
||||
|
||||
for (u32 level = 0; level < params.emulated_levels; ++level) {
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level)));
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level, is_converted)));
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level)));
|
||||
const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level);
|
||||
const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level, is_converted);
|
||||
|
||||
u8* const mip_data = staging_buffer.data() + mip_offset;
|
||||
const GLsizei size = static_cast<GLsizei>(params.GetHostMipmapSize(level));
|
||||
if (is_compressed) {
|
||||
@@ -294,14 +299,10 @@ void CachedSurface::UploadTexture(const std::vector<u8>& staging_buffer) {
|
||||
}
|
||||
|
||||
void CachedSurface::UploadTextureMipmap(u32 level, const std::vector<u8>& staging_buffer) {
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level)));
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level, is_converted)));
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level)));
|
||||
|
||||
auto compression_type = params.GetCompressionType();
|
||||
|
||||
const std::size_t mip_offset = compression_type == SurfaceCompression::Converted
|
||||
? params.GetConvertedMipmapOffset(level)
|
||||
: params.GetHostMipmapLevelOffset(level);
|
||||
const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level, is_converted);
|
||||
const u8* buffer{staging_buffer.data() + mip_offset};
|
||||
if (is_compressed) {
|
||||
const auto image_size{static_cast<GLsizei>(params.GetHostMipmapSize(level))};
|
||||
@@ -482,7 +483,7 @@ OGLTextureView CachedSurfaceView::CreateTextureView() const {
|
||||
TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system,
|
||||
VideoCore::RasterizerInterface& rasterizer,
|
||||
const Device& device, StateTracker& state_tracker)
|
||||
: TextureCacheBase{system, rasterizer}, state_tracker{state_tracker} {
|
||||
: TextureCacheBase{system, rasterizer, device.HasASTC()}, state_tracker{state_tracker} {
|
||||
src_framebuffer.Create();
|
||||
dst_framebuffer.Create();
|
||||
}
|
||||
@@ -490,7 +491,7 @@ TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system,
|
||||
TextureCacheOpenGL::~TextureCacheOpenGL() = default;
|
||||
|
||||
Surface TextureCacheOpenGL::CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) {
|
||||
return std::make_shared<CachedSurface>(gpu_addr, params);
|
||||
return std::make_shared<CachedSurface>(gpu_addr, params, is_astc_supported);
|
||||
}
|
||||
|
||||
void TextureCacheOpenGL::ImageCopy(Surface& src_surface, Surface& dst_surface,
|
||||
@@ -596,7 +597,7 @@ void TextureCacheOpenGL::BufferCopy(Surface& src_surface, Surface& dst_surface)
|
||||
|
||||
glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle);
|
||||
|
||||
if (source_format.compressed) {
|
||||
if (src_surface->IsCompressed()) {
|
||||
glGetCompressedTextureImage(src_surface->GetTexture(), 0, static_cast<GLsizei>(source_size),
|
||||
nullptr);
|
||||
} else {
|
||||
@@ -610,7 +611,7 @@ void TextureCacheOpenGL::BufferCopy(Surface& src_surface, Surface& dst_surface)
|
||||
const GLsizei width = static_cast<GLsizei>(dst_params.width);
|
||||
const GLsizei height = static_cast<GLsizei>(dst_params.height);
|
||||
const GLsizei depth = static_cast<GLsizei>(dst_params.depth);
|
||||
if (dest_format.compressed) {
|
||||
if (dst_surface->IsCompressed()) {
|
||||
LOG_CRITICAL(HW_GPU, "Compressed buffer copy is unimplemented!");
|
||||
UNREACHABLE();
|
||||
} else {
|
||||
|
||||
@@ -37,7 +37,7 @@ class CachedSurface final : public VideoCommon::SurfaceBase<View> {
|
||||
friend CachedSurfaceView;
|
||||
|
||||
public:
|
||||
explicit CachedSurface(GPUVAddr gpu_addr, const SurfaceParams& params);
|
||||
explicit CachedSurface(GPUVAddr gpu_addr, const SurfaceParams& params, bool is_astc_supported);
|
||||
~CachedSurface();
|
||||
|
||||
void UploadTexture(const std::vector<u8>& staging_buffer) override;
|
||||
@@ -51,6 +51,10 @@ public:
|
||||
return texture.handle;
|
||||
}
|
||||
|
||||
bool IsCompressed() const {
|
||||
return is_compressed;
|
||||
}
|
||||
|
||||
protected:
|
||||
void DecorateSurfaceName() override;
|
||||
|
||||
|
||||
@@ -30,8 +30,6 @@ namespace OpenGL {
|
||||
|
||||
namespace {
|
||||
|
||||
// If the size of this is too small, it ends up creating a soft cap on FPS as the renderer will have
|
||||
// to wait on available presentation frames.
|
||||
constexpr std::size_t SWAP_CHAIN_SIZE = 3;
|
||||
|
||||
struct Frame {
|
||||
@@ -214,7 +212,7 @@ public:
|
||||
std::deque<Frame*> present_queue;
|
||||
Frame* previous_frame{};
|
||||
|
||||
FrameMailbox() : has_debug_tool{HasDebugTool()} {
|
||||
FrameMailbox() {
|
||||
for (auto& frame : swap_chain) {
|
||||
free_queue.push(&frame);
|
||||
}
|
||||
@@ -285,13 +283,9 @@ public:
|
||||
std::unique_lock lock{swap_chain_lock};
|
||||
present_queue.push_front(frame);
|
||||
present_cv.notify_one();
|
||||
|
||||
DebugNotifyNextFrame();
|
||||
}
|
||||
|
||||
Frame* TryGetPresentFrame(int timeout_ms) {
|
||||
DebugWaitForNextFrame();
|
||||
|
||||
std::unique_lock lock{swap_chain_lock};
|
||||
// wait for new entries in the present_queue
|
||||
present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
|
||||
@@ -317,38 +311,12 @@ public:
|
||||
previous_frame = frame;
|
||||
return frame;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex debug_synch_mutex;
|
||||
std::condition_variable debug_synch_condition;
|
||||
std::atomic_int frame_for_debug{};
|
||||
const bool has_debug_tool; // When true, using a GPU debugger, so keep frames in lock-step
|
||||
|
||||
/// Signal that a new frame is available (called from GPU thread)
|
||||
void DebugNotifyNextFrame() {
|
||||
if (!has_debug_tool) {
|
||||
return;
|
||||
}
|
||||
frame_for_debug++;
|
||||
std::lock_guard lock{debug_synch_mutex};
|
||||
debug_synch_condition.notify_one();
|
||||
}
|
||||
|
||||
/// Wait for a new frame to be available (called from presentation thread)
|
||||
void DebugWaitForNextFrame() {
|
||||
if (!has_debug_tool) {
|
||||
return;
|
||||
}
|
||||
const int last_frame = frame_for_debug;
|
||||
std::unique_lock lock{debug_synch_mutex};
|
||||
debug_synch_condition.wait(lock,
|
||||
[this, last_frame] { return frame_for_debug > last_frame; });
|
||||
}
|
||||
};
|
||||
|
||||
RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system)
|
||||
RendererOpenGL::RendererOpenGL(Core::Frontend::EmuWindow& emu_window, Core::System& system,
|
||||
Core::Frontend::GraphicsContext& context)
|
||||
: VideoCore::RendererBase{emu_window}, emu_window{emu_window}, system{system},
|
||||
frame_mailbox{std::make_unique<FrameMailbox>()} {}
|
||||
frame_mailbox{}, context{context}, has_debug_tool{HasDebugTool()} {}
|
||||
|
||||
RendererOpenGL::~RendererOpenGL() = default;
|
||||
|
||||
@@ -356,8 +324,6 @@ MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 12
|
||||
MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
|
||||
|
||||
void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
render_window.PollEvents();
|
||||
|
||||
if (!framebuffer) {
|
||||
return;
|
||||
}
|
||||
@@ -413,6 +379,13 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
|
||||
m_current_frame++;
|
||||
rasterizer->TickFrame();
|
||||
}
|
||||
|
||||
render_window.PollEvents();
|
||||
if (has_debug_tool) {
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
Present(0);
|
||||
context.SwapBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
|
||||
@@ -480,6 +453,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
|
||||
}
|
||||
|
||||
void RendererOpenGL::InitOpenGLObjects() {
|
||||
frame_mailbox = std::make_unique<FrameMailbox>();
|
||||
|
||||
glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
|
||||
0.0f);
|
||||
|
||||
@@ -692,12 +667,21 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
}
|
||||
|
||||
void RendererOpenGL::TryPresent(int timeout_ms) {
|
||||
bool RendererOpenGL::TryPresent(int timeout_ms) {
|
||||
if (has_debug_tool) {
|
||||
LOG_DEBUG(Render_OpenGL,
|
||||
"Skipping presentation because we are presenting on the main context");
|
||||
return false;
|
||||
}
|
||||
return Present(timeout_ms);
|
||||
}
|
||||
|
||||
bool RendererOpenGL::Present(int timeout_ms) {
|
||||
const auto& layout = render_window.GetFramebufferLayout();
|
||||
auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms);
|
||||
if (!frame) {
|
||||
LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a
|
||||
@@ -725,6 +709,7 @@ void RendererOpenGL::TryPresent(int timeout_ms) {
|
||||
glFlush();
|
||||
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
void RendererOpenGL::RenderScreenshot() {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -237,18 +237,21 @@ void VKDevice::ReportLoss() const {
|
||||
|
||||
bool VKDevice::IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features,
|
||||
const vk::DispatchLoaderDynamic& dldi) const {
|
||||
// Disable for now to avoid converting ASTC twice.
|
||||
return false;
|
||||
static constexpr std::array astc_formats = {
|
||||
vk::Format::eAstc4x4SrgbBlock, vk::Format::eAstc8x8SrgbBlock,
|
||||
vk::Format::eAstc8x5SrgbBlock, vk::Format::eAstc5x4SrgbBlock,
|
||||
vk::Format::eAstc4x4UnormBlock, vk::Format::eAstc4x4SrgbBlock,
|
||||
vk::Format::eAstc5x4UnormBlock, vk::Format::eAstc5x4SrgbBlock,
|
||||
vk::Format::eAstc5x5UnormBlock, vk::Format::eAstc5x5SrgbBlock,
|
||||
vk::Format::eAstc10x8UnormBlock, vk::Format::eAstc10x8SrgbBlock,
|
||||
vk::Format::eAstc6x5UnormBlock, vk::Format::eAstc6x5SrgbBlock,
|
||||
vk::Format::eAstc6x6UnormBlock, vk::Format::eAstc6x6SrgbBlock,
|
||||
vk::Format::eAstc10x10UnormBlock, vk::Format::eAstc10x10SrgbBlock,
|
||||
vk::Format::eAstc12x12UnormBlock, vk::Format::eAstc12x12SrgbBlock,
|
||||
vk::Format::eAstc8x5UnormBlock, vk::Format::eAstc8x5SrgbBlock,
|
||||
vk::Format::eAstc8x6UnormBlock, vk::Format::eAstc8x6SrgbBlock,
|
||||
vk::Format::eAstc6x5UnormBlock, vk::Format::eAstc6x5SrgbBlock};
|
||||
vk::Format::eAstc8x8UnormBlock, vk::Format::eAstc8x8SrgbBlock,
|
||||
vk::Format::eAstc10x5UnormBlock, vk::Format::eAstc10x5SrgbBlock,
|
||||
vk::Format::eAstc10x6UnormBlock, vk::Format::eAstc10x6SrgbBlock,
|
||||
vk::Format::eAstc10x8UnormBlock, vk::Format::eAstc10x8SrgbBlock,
|
||||
vk::Format::eAstc10x10UnormBlock, vk::Format::eAstc10x10SrgbBlock,
|
||||
vk::Format::eAstc12x10UnormBlock, vk::Format::eAstc12x10SrgbBlock,
|
||||
vk::Format::eAstc12x12UnormBlock, vk::Format::eAstc12x12SrgbBlock};
|
||||
if (!features.textureCompressionASTC_LDR) {
|
||||
return false;
|
||||
}
|
||||
@@ -572,24 +575,34 @@ std::unordered_map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperti
|
||||
vk::Format::eBc2SrgbBlock,
|
||||
vk::Format::eBc3SrgbBlock,
|
||||
vk::Format::eBc7SrgbBlock,
|
||||
vk::Format::eAstc4x4UnormBlock,
|
||||
vk::Format::eAstc4x4SrgbBlock,
|
||||
vk::Format::eAstc8x8SrgbBlock,
|
||||
vk::Format::eAstc8x5SrgbBlock,
|
||||
vk::Format::eAstc5x4UnormBlock,
|
||||
vk::Format::eAstc5x4SrgbBlock,
|
||||
vk::Format::eAstc5x5UnormBlock,
|
||||
vk::Format::eAstc5x5SrgbBlock,
|
||||
vk::Format::eAstc10x8UnormBlock,
|
||||
vk::Format::eAstc10x8SrgbBlock,
|
||||
vk::Format::eAstc6x6UnormBlock,
|
||||
vk::Format::eAstc6x6SrgbBlock,
|
||||
vk::Format::eAstc10x10UnormBlock,
|
||||
vk::Format::eAstc10x10SrgbBlock,
|
||||
vk::Format::eAstc12x12UnormBlock,
|
||||
vk::Format::eAstc12x12SrgbBlock,
|
||||
vk::Format::eAstc8x6UnormBlock,
|
||||
vk::Format::eAstc8x6SrgbBlock,
|
||||
vk::Format::eAstc6x5UnormBlock,
|
||||
vk::Format::eAstc6x5SrgbBlock,
|
||||
vk::Format::eAstc6x6UnormBlock,
|
||||
vk::Format::eAstc6x6SrgbBlock,
|
||||
vk::Format::eAstc8x5UnormBlock,
|
||||
vk::Format::eAstc8x5SrgbBlock,
|
||||
vk::Format::eAstc8x6UnormBlock,
|
||||
vk::Format::eAstc8x6SrgbBlock,
|
||||
vk::Format::eAstc8x8UnormBlock,
|
||||
vk::Format::eAstc8x8SrgbBlock,
|
||||
vk::Format::eAstc10x5UnormBlock,
|
||||
vk::Format::eAstc10x5SrgbBlock,
|
||||
vk::Format::eAstc10x6UnormBlock,
|
||||
vk::Format::eAstc10x6SrgbBlock,
|
||||
vk::Format::eAstc10x8UnormBlock,
|
||||
vk::Format::eAstc10x8SrgbBlock,
|
||||
vk::Format::eAstc10x10UnormBlock,
|
||||
vk::Format::eAstc10x10SrgbBlock,
|
||||
vk::Format::eAstc12x10UnormBlock,
|
||||
vk::Format::eAstc12x10SrgbBlock,
|
||||
vk::Format::eAstc12x12UnormBlock,
|
||||
vk::Format::eAstc12x12SrgbBlock,
|
||||
vk::Format::eE5B9G9R9UfloatPack32};
|
||||
std::unordered_map<vk::Format, vk::FormatProperties> format_properties;
|
||||
for (const auto format : formats) {
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace {
|
||||
using Sirit::Id;
|
||||
using Tegra::Engines::ShaderType;
|
||||
using Tegra::Shader::Attribute;
|
||||
using Tegra::Shader::AttributeUse;
|
||||
using Tegra::Shader::PixelImap;
|
||||
using Tegra::Shader::Register;
|
||||
using namespace VideoCommon::Shader;
|
||||
|
||||
@@ -752,16 +752,16 @@ private:
|
||||
if (stage != ShaderType::Fragment) {
|
||||
continue;
|
||||
}
|
||||
switch (header.ps.GetAttributeUse(location)) {
|
||||
case AttributeUse::Constant:
|
||||
switch (header.ps.GetPixelImap(location)) {
|
||||
case PixelImap::Constant:
|
||||
Decorate(id, spv::Decoration::Flat);
|
||||
break;
|
||||
case AttributeUse::ScreenLinear:
|
||||
Decorate(id, spv::Decoration::NoPerspective);
|
||||
break;
|
||||
case AttributeUse::Perspective:
|
||||
case PixelImap::Perspective:
|
||||
// Default
|
||||
break;
|
||||
case PixelImap::ScreenLinear:
|
||||
Decorate(id, spv::Decoration::NoPerspective);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE_MSG("Unused attribute being fetched");
|
||||
}
|
||||
@@ -1145,9 +1145,6 @@ private:
|
||||
switch (attribute) {
|
||||
case Attribute::Index::Position: {
|
||||
if (stage == ShaderType::Fragment) {
|
||||
if (element == 3) {
|
||||
return {Constant(t_float, 1.0f), Type::Float};
|
||||
}
|
||||
return {OpLoad(t_float, AccessElement(t_in_float, frag_coord, element)),
|
||||
Type::Float};
|
||||
}
|
||||
@@ -1941,7 +1938,11 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
Expression AtomicAdd(Operation operation) {
|
||||
template <Id (Module::*func)(Id, Id, Id, Id, Id), Type result_type,
|
||||
Type value_type = result_type>
|
||||
Expression Atomic(Operation operation) {
|
||||
const Id type_def = GetTypeDefinition(result_type);
|
||||
|
||||
Id pointer;
|
||||
if (const auto smem = std::get_if<SmemNode>(&*operation[0])) {
|
||||
pointer = GetSharedMemoryPointer(*smem);
|
||||
@@ -1949,14 +1950,15 @@ private:
|
||||
pointer = GetGlobalMemoryPointer(*gmem);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
return {Constant(t_uint, 0), Type::Uint};
|
||||
return {Constant(type_def, 0), result_type};
|
||||
}
|
||||
|
||||
const Id 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 +2547,21 @@ private:
|
||||
&SPIRVDecompiler::AtomicImageXor,
|
||||
&SPIRVDecompiler::AtomicImageExchange,
|
||||
|
||||
&SPIRVDecompiler::AtomicAdd,
|
||||
&SPIRVDecompiler::Atomic<&Module::OpAtomicExchange, Type::Uint>,
|
||||
&SPIRVDecompiler::Atomic<&Module::OpAtomicIAdd, Type::Uint>,
|
||||
&SPIRVDecompiler::Atomic<&Module::OpAtomicUMin, Type::Uint>,
|
||||
&SPIRVDecompiler::Atomic<&Module::OpAtomicUMax, Type::Uint>,
|
||||
&SPIRVDecompiler::Atomic<&Module::OpAtomicAnd, Type::Uint>,
|
||||
&SPIRVDecompiler::Atomic<&Module::OpAtomicOr, Type::Uint>,
|
||||
&SPIRVDecompiler::Atomic<&Module::OpAtomicXor, Type::Uint>,
|
||||
|
||||
&SPIRVDecompiler::Atomic<&Module::OpAtomicExchange, Type::Int>,
|
||||
&SPIRVDecompiler::Atomic<&Module::OpAtomicIAdd, Type::Int>,
|
||||
&SPIRVDecompiler::Atomic<&Module::OpAtomicSMin, Type::Int>,
|
||||
&SPIRVDecompiler::Atomic<&Module::OpAtomicSMax, Type::Int>,
|
||||
&SPIRVDecompiler::Atomic<&Module::OpAtomicAnd, Type::Int>,
|
||||
&SPIRVDecompiler::Atomic<&Module::OpAtomicOr, Type::Int>,
|
||||
&SPIRVDecompiler::Atomic<&Module::OpAtomicXor, Type::Int>,
|
||||
|
||||
&SPIRVDecompiler::Branch,
|
||||
&SPIRVDecompiler::BranchIndirect,
|
||||
|
||||
@@ -35,7 +35,6 @@ using VideoCore::MortonSwizzleMode;
|
||||
|
||||
using Tegra::Texture::SwizzleSource;
|
||||
using VideoCore::Surface::PixelFormat;
|
||||
using VideoCore::Surface::SurfaceCompression;
|
||||
using VideoCore::Surface::SurfaceTarget;
|
||||
|
||||
namespace {
|
||||
@@ -96,9 +95,10 @@ vk::ImageViewType GetImageViewType(SurfaceTarget target) {
|
||||
return {};
|
||||
}
|
||||
|
||||
UniqueBuffer CreateBuffer(const VKDevice& device, const SurfaceParams& params) {
|
||||
UniqueBuffer CreateBuffer(const VKDevice& device, const SurfaceParams& params,
|
||||
std::size_t host_memory_size) {
|
||||
// TODO(Rodrigo): Move texture buffer creation to the buffer cache
|
||||
const vk::BufferCreateInfo buffer_ci({}, params.GetHostSizeInBytes(),
|
||||
const vk::BufferCreateInfo buffer_ci({}, host_memory_size,
|
||||
vk::BufferUsageFlagBits::eUniformTexelBuffer |
|
||||
vk::BufferUsageFlagBits::eTransferSrc |
|
||||
vk::BufferUsageFlagBits::eTransferDst,
|
||||
@@ -110,12 +110,13 @@ UniqueBuffer CreateBuffer(const VKDevice& device, const SurfaceParams& params) {
|
||||
|
||||
vk::BufferViewCreateInfo GenerateBufferViewCreateInfo(const VKDevice& device,
|
||||
const SurfaceParams& params,
|
||||
vk::Buffer buffer) {
|
||||
vk::Buffer buffer,
|
||||
std::size_t host_memory_size) {
|
||||
ASSERT(params.IsBuffer());
|
||||
|
||||
const auto format =
|
||||
MaxwellToVK::SurfaceFormat(device, FormatType::Buffer, params.pixel_format).format;
|
||||
return vk::BufferViewCreateInfo({}, buffer, format, 0, params.GetHostSizeInBytes());
|
||||
return vk::BufferViewCreateInfo({}, buffer, format, 0, host_memory_size);
|
||||
}
|
||||
|
||||
vk::ImageCreateInfo GenerateImageCreateInfo(const VKDevice& device, const SurfaceParams& params) {
|
||||
@@ -169,14 +170,15 @@ CachedSurface::CachedSurface(Core::System& system, const VKDevice& device,
|
||||
VKResourceManager& resource_manager, VKMemoryManager& memory_manager,
|
||||
VKScheduler& scheduler, VKStagingBufferPool& staging_pool,
|
||||
GPUVAddr gpu_addr, const SurfaceParams& params)
|
||||
: SurfaceBase<View>{gpu_addr, params}, system{system}, device{device},
|
||||
resource_manager{resource_manager}, memory_manager{memory_manager}, scheduler{scheduler},
|
||||
staging_pool{staging_pool} {
|
||||
: SurfaceBase<View>{gpu_addr, params, device.IsOptimalAstcSupported()}, system{system},
|
||||
device{device}, resource_manager{resource_manager},
|
||||
memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{staging_pool} {
|
||||
if (params.IsBuffer()) {
|
||||
buffer = CreateBuffer(device, params);
|
||||
buffer = CreateBuffer(device, params, host_memory_size);
|
||||
commit = memory_manager.Commit(*buffer, false);
|
||||
|
||||
const auto buffer_view_ci = GenerateBufferViewCreateInfo(device, params, *buffer);
|
||||
const auto buffer_view_ci =
|
||||
GenerateBufferViewCreateInfo(device, params, *buffer, host_memory_size);
|
||||
format = buffer_view_ci.format;
|
||||
|
||||
const auto dev = device.GetLogical();
|
||||
@@ -255,7 +257,7 @@ void CachedSurface::UploadBuffer(const std::vector<u8>& staging_buffer) {
|
||||
std::memcpy(src_buffer.commit->Map(host_memory_size), staging_buffer.data(), host_memory_size);
|
||||
|
||||
scheduler.Record([src_buffer = *src_buffer.handle, dst_buffer = *buffer,
|
||||
size = params.GetHostSizeInBytes()](auto cmdbuf, auto& dld) {
|
||||
size = host_memory_size](auto cmdbuf, auto& dld) {
|
||||
const vk::BufferCopy copy(0, 0, size);
|
||||
cmdbuf.copyBuffer(src_buffer, dst_buffer, {copy}, dld);
|
||||
|
||||
@@ -299,10 +301,7 @@ void CachedSurface::UploadImage(const std::vector<u8>& staging_buffer) {
|
||||
|
||||
vk::BufferImageCopy CachedSurface::GetBufferImageCopy(u32 level) const {
|
||||
const u32 vk_depth = params.target == SurfaceTarget::Texture3D ? params.GetMipDepth(level) : 1;
|
||||
const auto compression_type = params.GetCompressionType();
|
||||
const std::size_t mip_offset = compression_type == SurfaceCompression::Converted
|
||||
? params.GetConvertedMipmapOffset(level)
|
||||
: params.GetHostMipmapLevelOffset(level);
|
||||
const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level, is_converted);
|
||||
|
||||
return vk::BufferImageCopy(
|
||||
mip_offset, 0, 0,
|
||||
@@ -390,8 +389,9 @@ VKTextureCache::VKTextureCache(Core::System& system, VideoCore::RasterizerInterf
|
||||
const VKDevice& device, VKResourceManager& resource_manager,
|
||||
VKMemoryManager& memory_manager, VKScheduler& scheduler,
|
||||
VKStagingBufferPool& staging_pool)
|
||||
: TextureCache(system, rasterizer), device{device}, resource_manager{resource_manager},
|
||||
memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{staging_pool} {}
|
||||
: TextureCache(system, rasterizer, device.IsOptimalAstcSupported()), device{device},
|
||||
resource_manager{resource_manager}, memory_manager{memory_manager}, scheduler{scheduler},
|
||||
staging_pool{staging_pool} {}
|
||||
|
||||
VKTextureCache::~VKTextureCache() = default;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -11,12 +11,17 @@
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
using std::move;
|
||||
using Tegra::Shader::ConditionCode;
|
||||
using Tegra::Shader::Instruction;
|
||||
using Tegra::Shader::IpaInterpMode;
|
||||
using Tegra::Shader::OpCode;
|
||||
using Tegra::Shader::PixelImap;
|
||||
using Tegra::Shader::Register;
|
||||
using Tegra::Shader::SystemVariable;
|
||||
|
||||
using Index = Tegra::Shader::Attribute::Index;
|
||||
|
||||
u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
const Instruction instr = {program_code[pc]};
|
||||
const auto opcode = OpCode::Decode(instr);
|
||||
@@ -213,27 +218,28 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
}
|
||||
case OpCode::Id::IPA: {
|
||||
const bool is_physical = instr.ipa.idx && instr.gpr8.Value() != 0xff;
|
||||
|
||||
const auto attribute = instr.attribute.fmt28;
|
||||
const Tegra::Shader::IpaMode input_mode{instr.ipa.interp_mode.Value(),
|
||||
instr.ipa.sample_mode.Value()};
|
||||
const Index index = attribute.index;
|
||||
|
||||
Node value = is_physical ? GetPhysicalInputAttribute(instr.gpr8)
|
||||
: GetInputAttribute(attribute.index, attribute.element);
|
||||
const Tegra::Shader::Attribute::Index index = attribute.index.Value();
|
||||
const bool is_generic = index >= Tegra::Shader::Attribute::Index::Attribute_0 &&
|
||||
index <= Tegra::Shader::Attribute::Index::Attribute_31;
|
||||
if (is_generic || is_physical) {
|
||||
// TODO(Blinkhawk): There are cases where a perspective attribute use PASS.
|
||||
// In theory by setting them as perspective, OpenGL does the perspective correction.
|
||||
// A way must figured to reverse the last step of it.
|
||||
if (input_mode.interpolation_mode == Tegra::Shader::IpaInterpMode::Multiply) {
|
||||
value = Operation(OperationCode::FMul, PRECISE, value, GetRegister(instr.gpr20));
|
||||
: GetInputAttribute(index, attribute.element);
|
||||
|
||||
// Code taken from Ryujinx.
|
||||
if (index >= Index::Attribute_0 && index <= Index::Attribute_31) {
|
||||
const u32 location = static_cast<u32>(index) - static_cast<u32>(Index::Attribute_0);
|
||||
if (header.ps.GetPixelImap(location) == PixelImap::Perspective) {
|
||||
Node position_w = GetInputAttribute(Index::Position, 3);
|
||||
value = Operation(OperationCode::FMul, move(value), move(position_w));
|
||||
}
|
||||
}
|
||||
value = GetSaturatedFloat(value, instr.ipa.saturate);
|
||||
|
||||
SetRegister(bb, instr.gpr0, value);
|
||||
if (instr.ipa.interp_mode == IpaInterpMode::Multiply) {
|
||||
value = Operation(OperationCode::FMul, move(value), GetRegister(instr.gpr20));
|
||||
}
|
||||
|
||||
value = GetSaturatedFloat(move(value), instr.ipa.saturate);
|
||||
|
||||
SetRegister(bb, instr.gpr0, move(value));
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::OUT_R: {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -86,6 +86,20 @@ OperationCode SignedToUnsignedCode(OperationCode operation_code, bool is_signed)
|
||||
return OperationCode::LogicalUNotEqual;
|
||||
case OperationCode::LogicalIGreaterEqual:
|
||||
return OperationCode::LogicalUGreaterEqual;
|
||||
case OperationCode::AtomicIExchange:
|
||||
return OperationCode::AtomicUExchange;
|
||||
case OperationCode::AtomicIAdd:
|
||||
return OperationCode::AtomicUAdd;
|
||||
case OperationCode::AtomicIMin:
|
||||
return OperationCode::AtomicUMin;
|
||||
case OperationCode::AtomicIMax:
|
||||
return OperationCode::AtomicUMax;
|
||||
case OperationCode::AtomicIAnd:
|
||||
return OperationCode::AtomicUAnd;
|
||||
case OperationCode::AtomicIOr:
|
||||
return OperationCode::AtomicUOr;
|
||||
case OperationCode::AtomicIXor:
|
||||
return OperationCode::AtomicUXor;
|
||||
case OperationCode::INegate:
|
||||
UNREACHABLE_MSG("Can't negate an unsigned integer");
|
||||
return {};
|
||||
|
||||
@@ -504,103 +504,6 @@ static constexpr u32 GetBytesPerPixel(PixelFormat pixel_format) {
|
||||
return GetFormatBpp(pixel_format) / CHAR_BIT;
|
||||
}
|
||||
|
||||
enum class SurfaceCompression {
|
||||
None, // Not compressed
|
||||
Compressed, // Texture is compressed
|
||||
Converted, // Texture is converted before upload or after download
|
||||
Rearranged, // Texture is swizzled before upload or after download
|
||||
};
|
||||
|
||||
constexpr std::array<SurfaceCompression, MaxPixelFormat> compression_type_table = {{
|
||||
SurfaceCompression::None, // ABGR8U
|
||||
SurfaceCompression::None, // ABGR8S
|
||||
SurfaceCompression::None, // ABGR8UI
|
||||
SurfaceCompression::None, // B5G6R5U
|
||||
SurfaceCompression::None, // A2B10G10R10U
|
||||
SurfaceCompression::None, // A1B5G5R5U
|
||||
SurfaceCompression::None, // R8U
|
||||
SurfaceCompression::None, // R8UI
|
||||
SurfaceCompression::None, // RGBA16F
|
||||
SurfaceCompression::None, // RGBA16U
|
||||
SurfaceCompression::None, // RGBA16S
|
||||
SurfaceCompression::None, // RGBA16UI
|
||||
SurfaceCompression::None, // R11FG11FB10F
|
||||
SurfaceCompression::None, // RGBA32UI
|
||||
SurfaceCompression::Compressed, // DXT1
|
||||
SurfaceCompression::Compressed, // DXT23
|
||||
SurfaceCompression::Compressed, // DXT45
|
||||
SurfaceCompression::Compressed, // DXN1
|
||||
SurfaceCompression::Compressed, // DXN2UNORM
|
||||
SurfaceCompression::Compressed, // DXN2SNORM
|
||||
SurfaceCompression::Compressed, // BC7U
|
||||
SurfaceCompression::Compressed, // BC6H_UF16
|
||||
SurfaceCompression::Compressed, // BC6H_SF16
|
||||
SurfaceCompression::Converted, // ASTC_2D_4X4
|
||||
SurfaceCompression::None, // BGRA8
|
||||
SurfaceCompression::None, // RGBA32F
|
||||
SurfaceCompression::None, // RG32F
|
||||
SurfaceCompression::None, // R32F
|
||||
SurfaceCompression::None, // R16F
|
||||
SurfaceCompression::None, // R16U
|
||||
SurfaceCompression::None, // R16S
|
||||
SurfaceCompression::None, // R16UI
|
||||
SurfaceCompression::None, // R16I
|
||||
SurfaceCompression::None, // RG16
|
||||
SurfaceCompression::None, // RG16F
|
||||
SurfaceCompression::None, // RG16UI
|
||||
SurfaceCompression::None, // RG16I
|
||||
SurfaceCompression::None, // RG16S
|
||||
SurfaceCompression::None, // RGB32F
|
||||
SurfaceCompression::None, // RGBA8_SRGB
|
||||
SurfaceCompression::None, // RG8U
|
||||
SurfaceCompression::None, // RG8S
|
||||
SurfaceCompression::None, // RG32UI
|
||||
SurfaceCompression::None, // RGBX16F
|
||||
SurfaceCompression::None, // R32UI
|
||||
SurfaceCompression::None, // R32I
|
||||
SurfaceCompression::Converted, // ASTC_2D_8X8
|
||||
SurfaceCompression::Converted, // ASTC_2D_8X5
|
||||
SurfaceCompression::Converted, // ASTC_2D_5X4
|
||||
SurfaceCompression::None, // BGRA8_SRGB
|
||||
SurfaceCompression::Compressed, // DXT1_SRGB
|
||||
SurfaceCompression::Compressed, // DXT23_SRGB
|
||||
SurfaceCompression::Compressed, // DXT45_SRGB
|
||||
SurfaceCompression::Compressed, // BC7U_SRGB
|
||||
SurfaceCompression::None, // R4G4B4A4U
|
||||
SurfaceCompression::Converted, // ASTC_2D_4X4_SRGB
|
||||
SurfaceCompression::Converted, // ASTC_2D_8X8_SRGB
|
||||
SurfaceCompression::Converted, // ASTC_2D_8X5_SRGB
|
||||
SurfaceCompression::Converted, // ASTC_2D_5X4_SRGB
|
||||
SurfaceCompression::Converted, // ASTC_2D_5X5
|
||||
SurfaceCompression::Converted, // ASTC_2D_5X5_SRGB
|
||||
SurfaceCompression::Converted, // ASTC_2D_10X8
|
||||
SurfaceCompression::Converted, // ASTC_2D_10X8_SRGB
|
||||
SurfaceCompression::Converted, // ASTC_2D_6X6
|
||||
SurfaceCompression::Converted, // ASTC_2D_6X6_SRGB
|
||||
SurfaceCompression::Converted, // ASTC_2D_10X10
|
||||
SurfaceCompression::Converted, // ASTC_2D_10X10_SRGB
|
||||
SurfaceCompression::Converted, // ASTC_2D_12X12
|
||||
SurfaceCompression::Converted, // ASTC_2D_12X12_SRGB
|
||||
SurfaceCompression::Converted, // ASTC_2D_8X6
|
||||
SurfaceCompression::Converted, // ASTC_2D_8X6_SRGB
|
||||
SurfaceCompression::Converted, // ASTC_2D_6X5
|
||||
SurfaceCompression::Converted, // ASTC_2D_6X5_SRGB
|
||||
SurfaceCompression::None, // E5B9G9R9F
|
||||
SurfaceCompression::None, // Z32F
|
||||
SurfaceCompression::None, // Z16
|
||||
SurfaceCompression::None, // Z24S8
|
||||
SurfaceCompression::Rearranged, // S8Z24
|
||||
SurfaceCompression::None, // Z32FS8
|
||||
}};
|
||||
|
||||
constexpr SurfaceCompression GetFormatCompressionType(PixelFormat format) {
|
||||
if (format == PixelFormat::Invalid) {
|
||||
return SurfaceCompression::None;
|
||||
}
|
||||
DEBUG_ASSERT(static_cast<std::size_t>(format) < compression_type_table.size());
|
||||
return compression_type_table[static_cast<std::size_t>(format)];
|
||||
}
|
||||
|
||||
SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type);
|
||||
|
||||
bool SurfaceTargetIsLayered(SurfaceTarget target);
|
||||
|
||||
@@ -18,15 +18,20 @@ MICROPROFILE_DEFINE(GPU_Flush_Texture, "GPU", "Texture Flush", MP_RGB(128, 192,
|
||||
|
||||
using Tegra::Texture::ConvertFromGuestToHost;
|
||||
using VideoCore::MortonSwizzleMode;
|
||||
using VideoCore::Surface::SurfaceCompression;
|
||||
using VideoCore::Surface::IsPixelFormatASTC;
|
||||
using VideoCore::Surface::PixelFormat;
|
||||
|
||||
StagingCache::StagingCache() = default;
|
||||
|
||||
StagingCache::~StagingCache() = default;
|
||||
|
||||
SurfaceBaseImpl::SurfaceBaseImpl(GPUVAddr gpu_addr, const SurfaceParams& params)
|
||||
: params{params}, host_memory_size{params.GetHostSizeInBytes()}, gpu_addr{gpu_addr},
|
||||
mipmap_sizes(params.num_levels), mipmap_offsets(params.num_levels) {
|
||||
SurfaceBaseImpl::SurfaceBaseImpl(GPUVAddr gpu_addr, const SurfaceParams& params,
|
||||
bool is_astc_supported)
|
||||
: params{params}, gpu_addr{gpu_addr}, mipmap_sizes(params.num_levels),
|
||||
mipmap_offsets(params.num_levels) {
|
||||
is_converted = IsPixelFormatASTC(params.pixel_format) && !is_astc_supported;
|
||||
host_memory_size = params.GetHostSizeInBytes(is_converted);
|
||||
|
||||
std::size_t offset = 0;
|
||||
for (u32 level = 0; level < params.num_levels; ++level) {
|
||||
const std::size_t mipmap_size{params.GetGuestMipmapSize(level)};
|
||||
@@ -164,7 +169,7 @@ void SurfaceBaseImpl::SwizzleFunc(MortonSwizzleMode mode, u8* memory, const Surf
|
||||
|
||||
std::size_t guest_offset{mipmap_offsets[level]};
|
||||
if (params.is_layered) {
|
||||
std::size_t host_offset{0};
|
||||
std::size_t host_offset = 0;
|
||||
const std::size_t guest_stride = layer_size;
|
||||
const std::size_t host_stride = params.GetHostLayerSize(level);
|
||||
for (u32 layer = 0; layer < params.depth; ++layer) {
|
||||
@@ -206,7 +211,7 @@ void SurfaceBaseImpl::LoadBuffer(Tegra::MemoryManager& memory_manager,
|
||||
ASSERT_MSG(params.block_width == 0, "Block width is defined as {} on texture target {}",
|
||||
params.block_width, static_cast<u32>(params.target));
|
||||
for (u32 level = 0; level < params.num_levels; ++level) {
|
||||
const std::size_t host_offset{params.GetHostMipmapLevelOffset(level)};
|
||||
const std::size_t host_offset{params.GetHostMipmapLevelOffset(level, false)};
|
||||
SwizzleFunc(MortonSwizzleMode::MortonToLinear, host_ptr, params,
|
||||
staging_buffer.data() + host_offset, level);
|
||||
}
|
||||
@@ -219,7 +224,7 @@ void SurfaceBaseImpl::LoadBuffer(Tegra::MemoryManager& memory_manager,
|
||||
const u32 height{(params.height + block_height - 1) / block_height};
|
||||
const u32 copy_size{width * bpp};
|
||||
if (params.pitch == copy_size) {
|
||||
std::memcpy(staging_buffer.data(), host_ptr, params.GetHostSizeInBytes());
|
||||
std::memcpy(staging_buffer.data(), host_ptr, params.GetHostSizeInBytes(false));
|
||||
} else {
|
||||
const u8* start{host_ptr};
|
||||
u8* write_to{staging_buffer.data()};
|
||||
@@ -231,19 +236,15 @@ void SurfaceBaseImpl::LoadBuffer(Tegra::MemoryManager& memory_manager,
|
||||
}
|
||||
}
|
||||
|
||||
auto compression_type = params.GetCompressionType();
|
||||
if (compression_type == SurfaceCompression::None ||
|
||||
compression_type == SurfaceCompression::Compressed)
|
||||
if (!is_converted && params.pixel_format != PixelFormat::S8Z24) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (u32 level_up = params.num_levels; level_up > 0; --level_up) {
|
||||
const u32 level = level_up - 1;
|
||||
const std::size_t in_host_offset{params.GetHostMipmapLevelOffset(level)};
|
||||
const std::size_t out_host_offset = compression_type == SurfaceCompression::Rearranged
|
||||
? in_host_offset
|
||||
: params.GetConvertedMipmapOffset(level);
|
||||
u8* in_buffer = staging_buffer.data() + in_host_offset;
|
||||
u8* out_buffer = staging_buffer.data() + out_host_offset;
|
||||
for (u32 level = params.num_levels; level--;) {
|
||||
const std::size_t in_host_offset{params.GetHostMipmapLevelOffset(level, false)};
|
||||
const std::size_t out_host_offset{params.GetHostMipmapLevelOffset(level, is_converted)};
|
||||
u8* const in_buffer = staging_buffer.data() + in_host_offset;
|
||||
u8* const out_buffer = staging_buffer.data() + out_host_offset;
|
||||
ConvertFromGuestToHost(in_buffer, out_buffer, params.pixel_format,
|
||||
params.GetMipWidth(level), params.GetMipHeight(level),
|
||||
params.GetMipDepth(level), true, true);
|
||||
@@ -273,7 +274,7 @@ void SurfaceBaseImpl::FlushBuffer(Tegra::MemoryManager& memory_manager,
|
||||
if (params.is_tiled) {
|
||||
ASSERT_MSG(params.block_width == 0, "Block width is defined as {}", params.block_width);
|
||||
for (u32 level = 0; level < params.num_levels; ++level) {
|
||||
const std::size_t host_offset{params.GetHostMipmapLevelOffset(level)};
|
||||
const std::size_t host_offset{params.GetHostMipmapLevelOffset(level, false)};
|
||||
SwizzleFunc(MortonSwizzleMode::LinearToMorton, host_ptr, params,
|
||||
staging_buffer.data() + host_offset, level);
|
||||
}
|
||||
|
||||
@@ -131,6 +131,10 @@ public:
|
||||
return !params.is_tiled;
|
||||
}
|
||||
|
||||
bool IsConverted() const {
|
||||
return is_converted;
|
||||
}
|
||||
|
||||
bool MatchFormat(VideoCore::Surface::PixelFormat pixel_format) const {
|
||||
return params.pixel_format == pixel_format;
|
||||
}
|
||||
@@ -160,7 +164,8 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit SurfaceBaseImpl(GPUVAddr gpu_addr, const SurfaceParams& params);
|
||||
explicit SurfaceBaseImpl(GPUVAddr gpu_addr, const SurfaceParams& params,
|
||||
bool is_astc_supported);
|
||||
~SurfaceBaseImpl() = default;
|
||||
|
||||
virtual void DecorateSurfaceName() = 0;
|
||||
@@ -168,12 +173,13 @@ protected:
|
||||
const SurfaceParams params;
|
||||
std::size_t layer_size;
|
||||
std::size_t guest_memory_size;
|
||||
const std::size_t host_memory_size;
|
||||
std::size_t host_memory_size;
|
||||
GPUVAddr gpu_addr{};
|
||||
CacheAddr cache_addr{};
|
||||
CacheAddr cache_addr_end{};
|
||||
VAddr cpu_addr{};
|
||||
bool is_continuous{};
|
||||
bool is_converted{};
|
||||
|
||||
std::vector<std::size_t> mipmap_sizes;
|
||||
std::vector<std::size_t> mipmap_offsets;
|
||||
@@ -288,8 +294,9 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit SurfaceBase(const GPUVAddr gpu_addr, const SurfaceParams& params)
|
||||
: SurfaceBaseImpl(gpu_addr, params) {}
|
||||
explicit SurfaceBase(const GPUVAddr gpu_addr, const SurfaceParams& params,
|
||||
bool is_astc_supported)
|
||||
: SurfaceBaseImpl(gpu_addr, params, is_astc_supported) {}
|
||||
|
||||
~SurfaceBase() = default;
|
||||
|
||||
|
||||
@@ -309,28 +309,26 @@ std::size_t SurfaceParams::GetGuestMipmapLevelOffset(u32 level) const {
|
||||
return offset;
|
||||
}
|
||||
|
||||
std::size_t SurfaceParams::GetHostMipmapLevelOffset(u32 level) const {
|
||||
std::size_t SurfaceParams::GetHostMipmapLevelOffset(u32 level, bool is_converted) const {
|
||||
std::size_t offset = 0;
|
||||
for (u32 i = 0; i < level; i++) {
|
||||
offset += GetInnerMipmapMemorySize(i, true, false) * GetNumLayers();
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
std::size_t SurfaceParams::GetConvertedMipmapOffset(u32 level) const {
|
||||
std::size_t offset = 0;
|
||||
for (u32 i = 0; i < level; i++) {
|
||||
offset += GetConvertedMipmapSize(i);
|
||||
if (is_converted) {
|
||||
for (u32 i = 0; i < level; ++i) {
|
||||
offset += GetConvertedMipmapSize(i) * GetNumLayers();
|
||||
}
|
||||
} else {
|
||||
for (u32 i = 0; i < level; ++i) {
|
||||
offset += GetInnerMipmapMemorySize(i, true, false) * GetNumLayers();
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
std::size_t SurfaceParams::GetConvertedMipmapSize(u32 level) const {
|
||||
constexpr std::size_t rgba8_bpp = 4ULL;
|
||||
const std::size_t width_t = GetMipWidth(level);
|
||||
const std::size_t height_t = GetMipHeight(level);
|
||||
const std::size_t depth_t = is_layered ? depth : GetMipDepth(level);
|
||||
return width_t * height_t * depth_t * rgba8_bpp;
|
||||
const std::size_t mip_width = GetMipWidth(level);
|
||||
const std::size_t mip_height = GetMipHeight(level);
|
||||
const std::size_t mip_depth = is_layered ? 1 : GetMipDepth(level);
|
||||
return mip_width * mip_height * mip_depth * rgba8_bpp;
|
||||
}
|
||||
|
||||
std::size_t SurfaceParams::GetLayerSize(bool as_host_size, bool uncompressed) const {
|
||||
|
||||
@@ -20,8 +20,6 @@ namespace VideoCommon {
|
||||
|
||||
class FormatLookupTable;
|
||||
|
||||
using VideoCore::Surface::SurfaceCompression;
|
||||
|
||||
class SurfaceParams {
|
||||
public:
|
||||
/// Creates SurfaceCachedParams from a texture configuration.
|
||||
@@ -67,16 +65,14 @@ public:
|
||||
return GetInnerMemorySize(false, false, false);
|
||||
}
|
||||
|
||||
std::size_t GetHostSizeInBytes() const {
|
||||
std::size_t host_size_in_bytes;
|
||||
if (GetCompressionType() == SurfaceCompression::Converted) {
|
||||
// ASTC is uncompressed in software, in emulated as RGBA8
|
||||
host_size_in_bytes = 0;
|
||||
for (u32 level = 0; level < num_levels; ++level) {
|
||||
host_size_in_bytes += GetConvertedMipmapSize(level);
|
||||
}
|
||||
} else {
|
||||
host_size_in_bytes = GetInnerMemorySize(true, false, false);
|
||||
std::size_t GetHostSizeInBytes(bool is_converted) const {
|
||||
if (!is_converted) {
|
||||
return GetInnerMemorySize(true, false, false);
|
||||
}
|
||||
// ASTC is uncompressed in software, in emulated as RGBA8
|
||||
std::size_t host_size_in_bytes = 0;
|
||||
for (u32 level = 0; level < num_levels; ++level) {
|
||||
host_size_in_bytes += GetConvertedMipmapSize(level) * GetNumLayers();
|
||||
}
|
||||
return host_size_in_bytes;
|
||||
}
|
||||
@@ -107,9 +103,8 @@ public:
|
||||
u32 GetMipBlockDepth(u32 level) const;
|
||||
|
||||
/// Returns the best possible row/pitch alignment for the surface.
|
||||
u32 GetRowAlignment(u32 level) const {
|
||||
const u32 bpp =
|
||||
GetCompressionType() == SurfaceCompression::Converted ? 4 : GetBytesPerPixel();
|
||||
u32 GetRowAlignment(u32 level, bool is_converted) const {
|
||||
const u32 bpp = is_converted ? 4 : GetBytesPerPixel();
|
||||
return 1U << Common::CountTrailingZeroes32(GetMipWidth(level) * bpp);
|
||||
}
|
||||
|
||||
@@ -117,11 +112,7 @@ public:
|
||||
std::size_t GetGuestMipmapLevelOffset(u32 level) const;
|
||||
|
||||
/// Returns the offset in bytes in host memory (linear) of a given mipmap level.
|
||||
std::size_t GetHostMipmapLevelOffset(u32 level) const;
|
||||
|
||||
/// Returns the offset in bytes in host memory (linear) of a given mipmap level
|
||||
/// for a texture that is converted in host gpu.
|
||||
std::size_t GetConvertedMipmapOffset(u32 level) const;
|
||||
std::size_t GetHostMipmapLevelOffset(u32 level, bool is_converted) const;
|
||||
|
||||
/// Returns the size in bytes in guest memory of a given mipmap level.
|
||||
std::size_t GetGuestMipmapSize(u32 level) const {
|
||||
@@ -196,11 +187,6 @@ public:
|
||||
pixel_format < VideoCore::Surface::PixelFormat::MaxDepthStencilFormat;
|
||||
}
|
||||
|
||||
/// Returns how the compression should be handled for this texture.
|
||||
SurfaceCompression GetCompressionType() const {
|
||||
return VideoCore::Surface::GetFormatCompressionType(pixel_format);
|
||||
}
|
||||
|
||||
/// Returns is the surface is a TextureBuffer type of surface.
|
||||
bool IsBuffer() const {
|
||||
return target == VideoCore::Surface::SurfaceTarget::TextureBuffer;
|
||||
|
||||
@@ -289,8 +289,9 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
|
||||
: system{system}, rasterizer{rasterizer} {
|
||||
explicit TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
|
||||
bool is_astc_supported)
|
||||
: system{system}, is_astc_supported{is_astc_supported}, rasterizer{rasterizer} {
|
||||
for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
|
||||
SetEmptyColorBuffer(i);
|
||||
}
|
||||
@@ -381,6 +382,7 @@ protected:
|
||||
}
|
||||
|
||||
Core::System& system;
|
||||
const bool is_astc_supported;
|
||||
|
||||
private:
|
||||
enum class RecycleStrategy : u32 {
|
||||
|
||||
@@ -15,13 +15,13 @@
|
||||
#endif
|
||||
#include "video_core/video_core.h"
|
||||
|
||||
namespace VideoCore {
|
||||
|
||||
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
|
||||
Core::System& system) {
|
||||
namespace {
|
||||
std::unique_ptr<VideoCore::RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
|
||||
Core::System& system,
|
||||
Core::Frontend::GraphicsContext& context) {
|
||||
switch (Settings::values.renderer_backend) {
|
||||
case Settings::RendererBackend::OpenGL:
|
||||
return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system);
|
||||
return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system, context);
|
||||
#ifdef HAS_VULKAN
|
||||
case Settings::RendererBackend::Vulkan:
|
||||
return std::make_unique<Vulkan::RendererVulkan>(emu_window, system);
|
||||
@@ -30,13 +30,23 @@ std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_wind
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) {
|
||||
if (Settings::values.use_asynchronous_gpu_emulation) {
|
||||
return std::make_unique<VideoCommon::GPUAsynch>(system, system.Renderer());
|
||||
namespace VideoCore {
|
||||
|
||||
std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) {
|
||||
auto context = emu_window.CreateSharedContext();
|
||||
const auto scope = context->Acquire();
|
||||
auto renderer = CreateRenderer(emu_window, system, *context);
|
||||
if (!renderer->Init()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<VideoCommon::GPUSynch>(system, system.Renderer());
|
||||
if (Settings::values.use_asynchronous_gpu_emulation) {
|
||||
return std::make_unique<VideoCommon::GPUAsynch>(system, std::move(renderer),
|
||||
std::move(context));
|
||||
}
|
||||
return std::make_unique<VideoCommon::GPUSynch>(system, std::move(renderer), std::move(context));
|
||||
}
|
||||
|
||||
u16 GetResolutionScaleFactor(const RendererBase& renderer) {
|
||||
|
||||
@@ -22,17 +22,8 @@ namespace VideoCore {
|
||||
|
||||
class RendererBase;
|
||||
|
||||
/**
|
||||
* Creates a renderer instance.
|
||||
*
|
||||
* @note The returned renderer instance is simply allocated. Its Init()
|
||||
* function still needs to be called to fully complete its setup.
|
||||
*/
|
||||
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
|
||||
Core::System& system);
|
||||
|
||||
/// Creates an emulated GPU instance using the given system context.
|
||||
std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system);
|
||||
std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system);
|
||||
|
||||
u16 GetResolutionScaleFactor(const RendererBase& renderer);
|
||||
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
#include <QMessageBox>
|
||||
#include <QOffscreenSurface>
|
||||
#include <QOpenGLContext>
|
||||
#include <QOpenGLFunctions>
|
||||
#include <QOpenGLFunctions_4_3_Core>
|
||||
#include <QOpenGLWindow>
|
||||
#include <QPainter>
|
||||
#include <QScreen>
|
||||
#include <QStringList>
|
||||
@@ -29,7 +26,6 @@
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/core.h"
|
||||
#include "core/frontend/framebuffer_layout.h"
|
||||
#include "core/frontend/scope_acquire_context.h"
|
||||
#include "core/settings.h"
|
||||
#include "input_common/keyboard.h"
|
||||
#include "input_common/main.h"
|
||||
@@ -39,26 +35,16 @@
|
||||
#include "yuzu/bootmanager.h"
|
||||
#include "yuzu/main.h"
|
||||
|
||||
EmuThread::EmuThread(GRenderWindow& window)
|
||||
: shared_context{window.CreateSharedContext()},
|
||||
context{(Settings::values.use_asynchronous_gpu_emulation && shared_context) ? *shared_context
|
||||
: window} {}
|
||||
EmuThread::EmuThread() = default;
|
||||
|
||||
EmuThread::~EmuThread() = default;
|
||||
|
||||
static GMainWindow* GetMainWindow() {
|
||||
for (QWidget* w : qApp->topLevelWidgets()) {
|
||||
if (GMainWindow* main = qobject_cast<GMainWindow*>(w)) {
|
||||
return main;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void EmuThread::run() {
|
||||
MicroProfileOnThreadCreate("EmuThread");
|
||||
|
||||
Core::Frontend::ScopeAcquireContext acquire_context{context};
|
||||
// Main process has been loaded. Make the context current to this thread and begin GPU and CPU
|
||||
// execution.
|
||||
Core::System::GetInstance().GPU().Start();
|
||||
|
||||
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
|
||||
|
||||
@@ -111,162 +97,155 @@ void EmuThread::run() {
|
||||
#endif
|
||||
}
|
||||
|
||||
class GGLContext : public Core::Frontend::GraphicsContext {
|
||||
class OpenGLSharedContext : public Core::Frontend::GraphicsContext {
|
||||
public:
|
||||
explicit GGLContext(QOpenGLContext* shared_context)
|
||||
: context(new QOpenGLContext(shared_context->parent())),
|
||||
surface(new QOffscreenSurface(nullptr)) {
|
||||
|
||||
// disable vsync for any shared contexts
|
||||
auto format = shared_context->format();
|
||||
/// Create the original context that should be shared from
|
||||
explicit OpenGLSharedContext(QSurface* surface) : surface(surface) {
|
||||
QSurfaceFormat format;
|
||||
format.setVersion(4, 3);
|
||||
format.setProfile(QSurfaceFormat::CompatibilityProfile);
|
||||
format.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
|
||||
// TODO: expose a setting for buffer value (ie default/single/double/triple)
|
||||
format.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
|
||||
format.setSwapInterval(0);
|
||||
|
||||
context->setShareContext(shared_context);
|
||||
context = std::make_unique<QOpenGLContext>();
|
||||
context->setFormat(format);
|
||||
context->create();
|
||||
surface->setParent(shared_context->parent());
|
||||
surface->setFormat(format);
|
||||
surface->create();
|
||||
if (!context->create()) {
|
||||
LOG_ERROR(Frontend, "Unable to create main openGL context");
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the shared contexts for rendering and presentation
|
||||
explicit OpenGLSharedContext(QOpenGLContext* share_context, QSurface* main_surface = nullptr) {
|
||||
|
||||
// disable vsync for any shared contexts
|
||||
auto format = share_context->format();
|
||||
format.setSwapInterval(main_surface ? Settings::values.use_vsync : 0);
|
||||
|
||||
context = std::make_unique<QOpenGLContext>();
|
||||
context->setShareContext(share_context);
|
||||
context->setFormat(format);
|
||||
if (!context->create()) {
|
||||
LOG_ERROR(Frontend, "Unable to create shared openGL context");
|
||||
}
|
||||
|
||||
if (!main_surface) {
|
||||
offscreen_surface = std::make_unique<QOffscreenSurface>(nullptr);
|
||||
offscreen_surface->setFormat(format);
|
||||
offscreen_surface->create();
|
||||
surface = offscreen_surface.get();
|
||||
} else {
|
||||
surface = main_surface;
|
||||
}
|
||||
}
|
||||
|
||||
~OpenGLSharedContext() {
|
||||
DoneCurrent();
|
||||
}
|
||||
|
||||
void SwapBuffers() override {
|
||||
context->swapBuffers(surface);
|
||||
}
|
||||
|
||||
void MakeCurrent() override {
|
||||
context->makeCurrent(surface);
|
||||
if (is_current) {
|
||||
return;
|
||||
}
|
||||
is_current = context->makeCurrent(surface);
|
||||
}
|
||||
|
||||
void DoneCurrent() override {
|
||||
context->doneCurrent();
|
||||
}
|
||||
|
||||
private:
|
||||
QOpenGLContext* context;
|
||||
QOffscreenSurface* surface;
|
||||
};
|
||||
|
||||
class ChildRenderWindow : public QWindow {
|
||||
public:
|
||||
ChildRenderWindow(QWindow* parent, QWidget* event_handler)
|
||||
: QWindow{parent}, event_handler{event_handler} {}
|
||||
|
||||
virtual ~ChildRenderWindow() = default;
|
||||
|
||||
virtual void Present() = 0;
|
||||
|
||||
protected:
|
||||
bool event(QEvent* event) override {
|
||||
switch (event->type()) {
|
||||
case QEvent::UpdateRequest:
|
||||
Present();
|
||||
return true;
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::MouseButtonRelease:
|
||||
case QEvent::MouseButtonDblClick:
|
||||
case QEvent::MouseMove:
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::KeyRelease:
|
||||
case QEvent::FocusIn:
|
||||
case QEvent::FocusOut:
|
||||
case QEvent::FocusAboutToChange:
|
||||
case QEvent::Enter:
|
||||
case QEvent::Leave:
|
||||
case QEvent::Wheel:
|
||||
case QEvent::TabletMove:
|
||||
case QEvent::TabletPress:
|
||||
case QEvent::TabletRelease:
|
||||
case QEvent::TabletEnterProximity:
|
||||
case QEvent::TabletLeaveProximity:
|
||||
case QEvent::TouchBegin:
|
||||
case QEvent::TouchUpdate:
|
||||
case QEvent::TouchEnd:
|
||||
case QEvent::InputMethodQuery:
|
||||
case QEvent::TouchCancel:
|
||||
return QCoreApplication::sendEvent(event_handler, event);
|
||||
case QEvent::Drop:
|
||||
GetMainWindow()->DropAction(static_cast<QDropEvent*>(event));
|
||||
return true;
|
||||
case QEvent::DragResponse:
|
||||
case QEvent::DragEnter:
|
||||
case QEvent::DragLeave:
|
||||
case QEvent::DragMove:
|
||||
GetMainWindow()->AcceptDropEvent(static_cast<QDropEvent*>(event));
|
||||
return true;
|
||||
default:
|
||||
return QWindow::event(event);
|
||||
if (!is_current) {
|
||||
return;
|
||||
}
|
||||
context->doneCurrent();
|
||||
is_current = false;
|
||||
}
|
||||
|
||||
void exposeEvent(QExposeEvent* event) override {
|
||||
QWindow::requestUpdate();
|
||||
QWindow::exposeEvent(event);
|
||||
QOpenGLContext* GetShareContext() {
|
||||
return context.get();
|
||||
}
|
||||
|
||||
const QOpenGLContext* GetShareContext() const {
|
||||
return context.get();
|
||||
}
|
||||
|
||||
private:
|
||||
QWidget* event_handler{};
|
||||
// Avoid using Qt parent system here since we might move the QObjects to new threads
|
||||
// As a note, this means we should avoid using slots/signals with the objects too
|
||||
std::unique_ptr<QOpenGLContext> context;
|
||||
std::unique_ptr<QOffscreenSurface> offscreen_surface{};
|
||||
QSurface* surface;
|
||||
bool is_current = false;
|
||||
};
|
||||
|
||||
class OpenGLWindow final : public ChildRenderWindow {
|
||||
class DummyContext : public Core::Frontend::GraphicsContext {};
|
||||
|
||||
class RenderWidget : public QWidget {
|
||||
public:
|
||||
OpenGLWindow(QWindow* parent, QWidget* event_handler, QOpenGLContext* shared_context)
|
||||
: ChildRenderWindow{parent, event_handler},
|
||||
context(new QOpenGLContext(shared_context->parent())) {
|
||||
|
||||
// disable vsync for any shared contexts
|
||||
auto format = shared_context->format();
|
||||
format.setSwapInterval(Settings::values.use_vsync ? 1 : 0);
|
||||
this->setFormat(format);
|
||||
|
||||
context->setShareContext(shared_context);
|
||||
context->setScreen(this->screen());
|
||||
context->setFormat(format);
|
||||
context->create();
|
||||
|
||||
setSurfaceType(QWindow::OpenGLSurface);
|
||||
|
||||
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
||||
// WA_DontShowOnScreen, WA_DeleteOnClose
|
||||
explicit RenderWidget(GRenderWindow* parent) : QWidget(parent), render_window(parent) {
|
||||
setAttribute(Qt::WA_NativeWindow);
|
||||
setAttribute(Qt::WA_PaintOnScreen);
|
||||
}
|
||||
|
||||
~OpenGLWindow() override {
|
||||
context->doneCurrent();
|
||||
virtual ~RenderWidget() = default;
|
||||
|
||||
/// Called on the UI thread when this Widget is ready to draw
|
||||
/// Dervied classes can override this to draw the latest frame.
|
||||
virtual void Present() {}
|
||||
|
||||
void paintEvent(QPaintEvent* event) override {
|
||||
Present();
|
||||
update();
|
||||
}
|
||||
|
||||
QPaintEngine* paintEngine() const override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
GRenderWindow* render_window;
|
||||
};
|
||||
|
||||
class OpenGLRenderWidget : public RenderWidget {
|
||||
public:
|
||||
explicit OpenGLRenderWidget(GRenderWindow* parent) : RenderWidget(parent) {
|
||||
windowHandle()->setSurfaceType(QWindow::OpenGLSurface);
|
||||
}
|
||||
|
||||
void SetContext(std::unique_ptr<Core::Frontend::GraphicsContext>&& context_) {
|
||||
context = std::move(context_);
|
||||
}
|
||||
|
||||
void Present() override {
|
||||
if (!isExposed()) {
|
||||
if (!isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
context->makeCurrent(this);
|
||||
Core::System::GetInstance().Renderer().TryPresent(100);
|
||||
context->swapBuffers(this);
|
||||
auto f = context->versionFunctions<QOpenGLFunctions_4_3_Core>();
|
||||
f->glFinish();
|
||||
QWindow::requestUpdate();
|
||||
context->MakeCurrent();
|
||||
if (Core::System::GetInstance().Renderer().TryPresent(100)) {
|
||||
context->SwapBuffers();
|
||||
glFinish();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QOpenGLContext* context{};
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> context{};
|
||||
};
|
||||
|
||||
#ifdef HAS_VULKAN
|
||||
class VulkanWindow final : public ChildRenderWindow {
|
||||
class VulkanRenderWidget : public RenderWidget {
|
||||
public:
|
||||
VulkanWindow(QWindow* parent, QWidget* event_handler, QVulkanInstance* instance)
|
||||
: ChildRenderWindow{parent, event_handler} {
|
||||
setSurfaceType(QSurface::SurfaceType::VulkanSurface);
|
||||
setVulkanInstance(instance);
|
||||
explicit VulkanRenderWidget(GRenderWindow* parent, QVulkanInstance* instance)
|
||||
: RenderWidget(parent) {
|
||||
windowHandle()->setSurfaceType(QWindow::VulkanSurface);
|
||||
windowHandle()->setVulkanInstance(instance);
|
||||
}
|
||||
|
||||
~VulkanWindow() override = default;
|
||||
|
||||
void Present() override {
|
||||
// TODO(bunnei): ImplementMe
|
||||
}
|
||||
|
||||
private:
|
||||
QWidget* event_handler{};
|
||||
};
|
||||
#endif
|
||||
|
||||
GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)
|
||||
GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread)
|
||||
: QWidget(parent_), emu_thread(emu_thread) {
|
||||
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
|
||||
.arg(QString::fromUtf8(Common::g_build_name),
|
||||
@@ -278,26 +257,13 @@ GRenderWindow::GRenderWindow(QWidget* parent_, EmuThread* emu_thread)
|
||||
setLayout(layout);
|
||||
InputCommon::Init();
|
||||
|
||||
GMainWindow* parent = GetMainWindow();
|
||||
connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
|
||||
connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete);
|
||||
}
|
||||
|
||||
GRenderWindow::~GRenderWindow() {
|
||||
InputCommon::Shutdown();
|
||||
}
|
||||
|
||||
void GRenderWindow::MakeCurrent() {
|
||||
if (core_context) {
|
||||
core_context->MakeCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::DoneCurrent() {
|
||||
if (core_context) {
|
||||
core_context->DoneCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::PollEvents() {
|
||||
if (!first_frame) {
|
||||
first_frame = true;
|
||||
@@ -309,21 +275,6 @@ bool GRenderWindow::IsShown() const {
|
||||
return !isMinimized();
|
||||
}
|
||||
|
||||
void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||
void* surface) const {
|
||||
#ifdef HAS_VULKAN
|
||||
const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr");
|
||||
const VkInstance instance_copy = vk_instance->vkInstance();
|
||||
const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_window);
|
||||
|
||||
std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
|
||||
std::memcpy(instance, &instance_copy, sizeof(instance_copy));
|
||||
std::memcpy(surface, &surface_copy, sizeof(surface_copy));
|
||||
#else
|
||||
UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan");
|
||||
#endif
|
||||
}
|
||||
|
||||
// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
|
||||
//
|
||||
// Older versions get the window size (density independent pixels),
|
||||
@@ -367,7 +318,7 @@ qreal GRenderWindow::windowPixelRatio() const {
|
||||
return devicePixelRatio();
|
||||
}
|
||||
|
||||
std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const {
|
||||
std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF& pos) const {
|
||||
const qreal pixel_ratio = windowPixelRatio();
|
||||
return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
|
||||
static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
|
||||
@@ -387,8 +338,10 @@ void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
|
||||
}
|
||||
|
||||
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||
return; // touch input is handled in TouchBeginEvent
|
||||
// touch input is handled in TouchBeginEvent
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto pos = event->pos();
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
@@ -400,8 +353,10 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
|
||||
}
|
||||
|
||||
void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||
return; // touch input is handled in TouchUpdateEvent
|
||||
// touch input is handled in TouchUpdateEvent
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto pos = event->pos();
|
||||
const auto [x, y] = ScaleTouch(pos);
|
||||
@@ -410,13 +365,16 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
|
||||
}
|
||||
|
||||
void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem)
|
||||
return; // touch input is handled in TouchEndEvent
|
||||
// touch input is handled in TouchEndEvent
|
||||
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->button() == Qt::LeftButton)
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
this->TouchReleased();
|
||||
else if (event->button() == Qt::RightButton)
|
||||
} else if (event->button() == Qt::RightButton) {
|
||||
InputCommon::GetMotionEmu()->EndTilt();
|
||||
}
|
||||
}
|
||||
|
||||
void GRenderWindow::TouchBeginEvent(const QTouchEvent* event) {
|
||||
@@ -474,9 +432,13 @@ void GRenderWindow::resizeEvent(QResizeEvent* event) {
|
||||
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedContext() const {
|
||||
if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
|
||||
return std::make_unique<GGLContext>(QOpenGLContext::globalShareContext());
|
||||
auto c = static_cast<OpenGLSharedContext*>(main_context.get());
|
||||
// Bind the shared contexts to the main surface in case the backend wants to take over
|
||||
// presentation
|
||||
return std::make_unique<OpenGLSharedContext>(c->GetShareContext(),
|
||||
child_widget->windowHandle());
|
||||
}
|
||||
return {};
|
||||
return std::make_unique<DummyContext>();
|
||||
}
|
||||
|
||||
bool GRenderWindow::InitRenderTarget() {
|
||||
@@ -497,14 +459,11 @@ bool GRenderWindow::InitRenderTarget() {
|
||||
break;
|
||||
}
|
||||
|
||||
child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
|
||||
layout()->addWidget(child_widget);
|
||||
// Reset minimum required size to avoid resizing issues on the main window after restarting.
|
||||
setMinimumSize(1, 1);
|
||||
|
||||
// Show causes the window to actually be created and the gl context as well, but we don't want
|
||||
// the widget to be shown yet, so immediately hide it.
|
||||
show();
|
||||
hide();
|
||||
|
||||
resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
|
||||
|
||||
OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
|
||||
@@ -523,9 +482,10 @@ bool GRenderWindow::InitRenderTarget() {
|
||||
void GRenderWindow::ReleaseRenderTarget() {
|
||||
if (child_widget) {
|
||||
layout()->removeWidget(child_widget);
|
||||
delete child_widget;
|
||||
child_widget->deleteLater();
|
||||
child_widget = nullptr;
|
||||
}
|
||||
main_context.reset();
|
||||
}
|
||||
|
||||
void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
|
||||
@@ -557,24 +517,13 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal
|
||||
bool GRenderWindow::InitializeOpenGL() {
|
||||
// TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
|
||||
// WA_DontShowOnScreen, WA_DeleteOnClose
|
||||
QSurfaceFormat fmt;
|
||||
fmt.setVersion(4, 3);
|
||||
fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
|
||||
fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
|
||||
// TODO: expose a setting for buffer value (ie default/single/double/triple)
|
||||
fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
|
||||
fmt.setSwapInterval(0);
|
||||
QSurfaceFormat::setDefaultFormat(fmt);
|
||||
|
||||
GMainWindow* parent = GetMainWindow();
|
||||
QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr;
|
||||
child_window = new OpenGLWindow(parent_win_handle, this, QOpenGLContext::globalShareContext());
|
||||
child_window->create();
|
||||
child_widget = createWindowContainer(child_window, this);
|
||||
child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
|
||||
layout()->addWidget(child_widget);
|
||||
|
||||
core_context = CreateSharedContext();
|
||||
auto child = new OpenGLRenderWidget(this);
|
||||
child_widget = child;
|
||||
child_widget->windowHandle()->create();
|
||||
auto context = std::make_shared<OpenGLSharedContext>(child->windowHandle());
|
||||
main_context = context;
|
||||
child->SetContext(
|
||||
std::make_unique<OpenGLSharedContext>(context->GetShareContext(), child->windowHandle()));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -604,13 +553,10 @@ bool GRenderWindow::InitializeVulkan() {
|
||||
return false;
|
||||
}
|
||||
|
||||
GMainWindow* parent = GetMainWindow();
|
||||
QWindow* parent_win_handle = parent ? parent->windowHandle() : nullptr;
|
||||
child_window = new VulkanWindow(parent_win_handle, this, vk_instance.get());
|
||||
child_window->create();
|
||||
child_widget = createWindowContainer(child_window, this);
|
||||
child_widget->resize(Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height);
|
||||
layout()->addWidget(child_widget);
|
||||
auto child = new VulkanRenderWidget(this, vk_instance.get());
|
||||
child_widget = child;
|
||||
child_widget->windowHandle()->create();
|
||||
main_context = std::make_unique<DummyContext>();
|
||||
|
||||
return true;
|
||||
#else
|
||||
@@ -620,8 +566,24 @@ bool GRenderWindow::InitializeVulkan() {
|
||||
#endif
|
||||
}
|
||||
|
||||
void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||
void* surface) const {
|
||||
#ifdef HAS_VULKAN
|
||||
const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr");
|
||||
const VkInstance instance_copy = vk_instance->vkInstance();
|
||||
const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child_widget->windowHandle());
|
||||
|
||||
std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
|
||||
std::memcpy(instance, &instance_copy, sizeof(instance_copy));
|
||||
std::memcpy(surface, &surface_copy, sizeof(surface_copy));
|
||||
#else
|
||||
UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool GRenderWindow::LoadOpenGL() {
|
||||
Core::Frontend::ScopeAcquireContext acquire_context{*this};
|
||||
auto context = CreateSharedContext();
|
||||
auto scope = context->Acquire();
|
||||
if (!gladLoadGL()) {
|
||||
QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"),
|
||||
tr("Your GPU may not support OpenGL 4.3, or you do not have the "
|
||||
|
||||
@@ -18,12 +18,10 @@
|
||||
#include "core/frontend/emu_window.h"
|
||||
|
||||
class GRenderWindow;
|
||||
class GMainWindow;
|
||||
class QKeyEvent;
|
||||
class QScreen;
|
||||
class QTouchEvent;
|
||||
class QStringList;
|
||||
class QSurface;
|
||||
class QOpenGLContext;
|
||||
#ifdef HAS_VULKAN
|
||||
class QVulkanInstance;
|
||||
#endif
|
||||
@@ -36,7 +34,7 @@ class EmuThread final : public QThread {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit EmuThread(GRenderWindow& window);
|
||||
explicit EmuThread();
|
||||
~EmuThread() override;
|
||||
|
||||
/**
|
||||
@@ -90,12 +88,6 @@ private:
|
||||
std::mutex running_mutex;
|
||||
std::condition_variable running_cv;
|
||||
|
||||
/// Only used in asynchronous GPU mode
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> shared_context;
|
||||
|
||||
/// This is shared_context in asynchronous GPU mode, core_context in synchronous GPU mode
|
||||
Core::Frontend::GraphicsContext& context;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* Emitted when the CPU has halted execution
|
||||
@@ -124,12 +116,10 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
GRenderWindow(QWidget* parent, EmuThread* emu_thread);
|
||||
GRenderWindow(GMainWindow* parent, EmuThread* emu_thread);
|
||||
~GRenderWindow() override;
|
||||
|
||||
// EmuWindow implementation.
|
||||
void MakeCurrent() override;
|
||||
void DoneCurrent() override;
|
||||
void PollEvents() override;
|
||||
bool IsShown() const override;
|
||||
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||
@@ -165,6 +155,8 @@ public:
|
||||
|
||||
void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);
|
||||
|
||||
std::pair<u32, u32> ScaleTouch(const QPointF& pos) const;
|
||||
|
||||
public slots:
|
||||
void OnEmulationStarting(EmuThread* emu_thread);
|
||||
void OnEmulationStopping();
|
||||
@@ -176,7 +168,6 @@ signals:
|
||||
void FirstFrameDisplayed();
|
||||
|
||||
private:
|
||||
std::pair<u32, u32> ScaleTouch(QPointF pos) const;
|
||||
void TouchBeginEvent(const QTouchEvent* event);
|
||||
void TouchUpdateEvent(const QTouchEvent* event);
|
||||
void TouchEndEvent();
|
||||
@@ -190,7 +181,10 @@ private:
|
||||
|
||||
EmuThread* emu_thread;
|
||||
|
||||
std::unique_ptr<GraphicsContext> core_context;
|
||||
// Main context that will be shared with all other contexts that are requested.
|
||||
// If this is used in a shared context setting, then this should not be used directly, but
|
||||
// should instead be shared from
|
||||
std::shared_ptr<Core::Frontend::GraphicsContext> main_context;
|
||||
|
||||
#ifdef HAS_VULKAN
|
||||
std::unique_ptr<QVulkanInstance> vk_instance;
|
||||
@@ -201,12 +195,6 @@ private:
|
||||
|
||||
QByteArray geometry;
|
||||
|
||||
/// Native window handle that backs this presentation widget
|
||||
QWindow* child_window = nullptr;
|
||||
|
||||
/// In order to embed the window into GRenderWindow, you need to use createWindowContainer to
|
||||
/// put the child_window into a widget then add it to the layout. This child_widget can be
|
||||
/// parented to GRenderWindow and use Qt's lifetime system
|
||||
QWidget* child_widget = nullptr;
|
||||
|
||||
bool first_frame = false;
|
||||
|
||||
@@ -839,94 +839,6 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="3" column="0">
|
||||
<layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelSL">
|
||||
<property name="text">
|
||||
<string>SL:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonSL">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelZR">
|
||||
<property name="text">
|
||||
<string>ZR:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonZR">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelSR">
|
||||
<property name="text">
|
||||
<string>SR:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonSR">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelZL">
|
||||
<property name="text">
|
||||
<string>ZL:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonZL">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">
|
||||
<item>
|
||||
@@ -949,7 +861,7 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="0" column="1">
|
||||
<layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout">
|
||||
@@ -971,6 +883,94 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelZL">
|
||||
<property name="text">
|
||||
<string>ZL:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonZL">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelZR">
|
||||
<property name="text">
|
||||
<string>ZR:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonZR">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelSL">
|
||||
<property name="text">
|
||||
<string>SL:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonSL">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="labelSR">
|
||||
<property name="text">
|
||||
<string>SR:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="buttonSR">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@@ -984,7 +984,7 @@ void GMainWindow::BootGame(const QString& filename) {
|
||||
return;
|
||||
|
||||
// Create and start the emulation thread
|
||||
emu_thread = std::make_unique<EmuThread>(*render_window);
|
||||
emu_thread = std::make_unique<EmuThread>();
|
||||
emit EmulationStarting(emu_thread.get());
|
||||
emu_thread->start();
|
||||
|
||||
@@ -2378,7 +2378,6 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
// Enables the core to make the qt created contexts current on std::threads
|
||||
QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
|
||||
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
|
||||
QApplication app(argc, argv);
|
||||
|
||||
// Qt changes the locale and causes issues in float conversion using std::to_string() when
|
||||
|
||||
@@ -37,16 +37,24 @@ public:
|
||||
}
|
||||
|
||||
void MakeCurrent() override {
|
||||
SDL_GL_MakeCurrent(window, context);
|
||||
if (is_current) {
|
||||
return;
|
||||
}
|
||||
is_current = SDL_GL_MakeCurrent(window, context) == 0;
|
||||
}
|
||||
|
||||
void DoneCurrent() override {
|
||||
if (!is_current) {
|
||||
return;
|
||||
}
|
||||
SDL_GL_MakeCurrent(window, nullptr);
|
||||
is_current = false;
|
||||
}
|
||||
|
||||
private:
|
||||
SDL_Window* window;
|
||||
SDL_GLContext context;
|
||||
bool is_current = false;
|
||||
};
|
||||
|
||||
bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
|
||||
@@ -148,14 +156,6 @@ EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() {
|
||||
SDL_GL_DeleteContext(window_context);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_GL::MakeCurrent() {
|
||||
core_context->MakeCurrent();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_GL::DoneCurrent() {
|
||||
core_context->DoneCurrent();
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||
void* surface) const {
|
||||
// Should not have been called from OpenGL
|
||||
|
||||
@@ -13,8 +13,6 @@ public:
|
||||
explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen);
|
||||
~EmuWindow_SDL2_GL();
|
||||
|
||||
void MakeCurrent() override;
|
||||
void DoneCurrent() override;
|
||||
void Present() override;
|
||||
|
||||
/// Ignored in OpenGL
|
||||
|
||||
@@ -111,14 +111,6 @@ EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() {
|
||||
vkDestroyInstance(vk_instance, nullptr);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_VK::MakeCurrent() {
|
||||
// Unused on Vulkan
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_VK::DoneCurrent() {
|
||||
// Unused on Vulkan
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||
void* surface) const {
|
||||
const auto instance_proc_addr = vkGetInstanceProcAddr;
|
||||
|
||||
@@ -13,8 +13,6 @@ public:
|
||||
explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen);
|
||||
~EmuWindow_SDL2_VK();
|
||||
|
||||
void MakeCurrent() override;
|
||||
void DoneCurrent() override;
|
||||
void Present() override;
|
||||
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||
void* surface) const override;
|
||||
|
||||
@@ -230,17 +230,10 @@ int main(int argc, char** argv) {
|
||||
|
||||
system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDL");
|
||||
|
||||
system.Renderer().Rasterizer().LoadDiskResources();
|
||||
// Core is loaded, start the GPU (makes the GPU contexts current to this thread)
|
||||
system.GPU().Start();
|
||||
|
||||
// Acquire render context for duration of the thread if this is the rendering thread
|
||||
if (!Settings::values.use_asynchronous_gpu_emulation) {
|
||||
emu_window->MakeCurrent();
|
||||
}
|
||||
SCOPE_EXIT({
|
||||
if (!Settings::values.use_asynchronous_gpu_emulation) {
|
||||
emu_window->DoneCurrent();
|
||||
}
|
||||
});
|
||||
system.Renderer().Rasterizer().LoadDiskResources();
|
||||
|
||||
std::thread render_thread([&emu_window] { emu_window->Present(); });
|
||||
while (emu_window->IsOpen()) {
|
||||
|
||||
@@ -102,8 +102,6 @@ EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() {
|
||||
LOG_INFO(Frontend, "yuzu-tester Version: {} | {}-{}", Common::g_build_fullname,
|
||||
Common::g_scm_branch, Common::g_scm_desc);
|
||||
Settings::LogSettings();
|
||||
|
||||
DoneCurrent();
|
||||
}
|
||||
|
||||
EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
|
||||
@@ -114,14 +112,6 @@ EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
|
||||
|
||||
void EmuWindow_SDL2_Hide::PollEvents() {}
|
||||
|
||||
void EmuWindow_SDL2_Hide::MakeCurrent() {
|
||||
SDL_GL_MakeCurrent(render_window, gl_context);
|
||||
}
|
||||
|
||||
void EmuWindow_SDL2_Hide::DoneCurrent() {
|
||||
SDL_GL_MakeCurrent(render_window, nullptr);
|
||||
}
|
||||
|
||||
bool EmuWindow_SDL2_Hide::IsShown() const {
|
||||
return false;
|
||||
}
|
||||
@@ -129,3 +119,35 @@ bool EmuWindow_SDL2_Hide::IsShown() const {
|
||||
void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
class SDLGLContext : public Core::Frontend::GraphicsContext {
|
||||
public:
|
||||
explicit SDLGLContext() {
|
||||
// create a hidden window to make the shared context against
|
||||
window = SDL_CreateWindow(NULL, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0,
|
||||
SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
|
||||
context = SDL_GL_CreateContext(window);
|
||||
}
|
||||
|
||||
~SDLGLContext() {
|
||||
DoneCurrent();
|
||||
SDL_GL_DeleteContext(context);
|
||||
SDL_DestroyWindow(window);
|
||||
}
|
||||
|
||||
void MakeCurrent() override {
|
||||
SDL_GL_MakeCurrent(window, context);
|
||||
}
|
||||
|
||||
void DoneCurrent() override {
|
||||
SDL_GL_MakeCurrent(window, nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
SDL_Window* window;
|
||||
SDL_GLContext context;
|
||||
};
|
||||
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_Hide::CreateSharedContext() const {
|
||||
return std::make_unique<SDLGLContext>();
|
||||
}
|
||||
|
||||
@@ -16,12 +16,6 @@ public:
|
||||
/// Polls window events
|
||||
void PollEvents() override;
|
||||
|
||||
/// Makes the graphics context current for the caller thread
|
||||
void MakeCurrent() override;
|
||||
|
||||
/// Releases the GL context from the caller thread
|
||||
void DoneCurrent() override;
|
||||
|
||||
/// Whether the screen is being shown or not.
|
||||
bool IsShown() const override;
|
||||
|
||||
@@ -29,8 +23,7 @@ public:
|
||||
void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
|
||||
void* surface) const override;
|
||||
|
||||
/// Whether the window is still open, and a close request hasn't yet been sent
|
||||
bool IsOpen() const;
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
|
||||
|
||||
private:
|
||||
/// Whether the GPU and driver supports the OpenGL extension required
|
||||
|
||||
@@ -164,11 +164,6 @@ int main(int argc, char** argv) {
|
||||
|
||||
std::unique_ptr<EmuWindow_SDL2_Hide> emu_window{std::make_unique<EmuWindow_SDL2_Hide>()};
|
||||
|
||||
if (!Settings::values.use_multi_core) {
|
||||
// Single core mode must acquire OpenGL context for entire emulation session
|
||||
emu_window->MakeCurrent();
|
||||
}
|
||||
|
||||
bool finished = false;
|
||||
int return_value = 0;
|
||||
const auto callback = [&finished,
|
||||
@@ -257,6 +252,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
system.TelemetrySession().AddField(Telemetry::FieldType::App, "Frontend", "SDLHideTester");
|
||||
|
||||
system.GPU().Start();
|
||||
system.Renderer().Rasterizer().LoadDiskResources();
|
||||
|
||||
while (!finished) {
|
||||
|
||||
Reference in New Issue
Block a user