Allow shader cache to switch to different titles at runtime

This commit is contained in:
PabloMK7 2025-04-24 17:27:08 +02:00
parent 975ad17442
commit c1bb85ba96
19 changed files with 205 additions and 67 deletions

View file

@ -215,7 +215,8 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
std::unique_ptr<Frontend::GraphicsContext> cpu_context;
system.GPU().Renderer().Rasterizer()->LoadDiskResources(stop_run, &LoadDiskCacheProgress);
system.GPU().Renderer().Rasterizer()->LoadDefaultDiskResources(stop_run,
&LoadDiskCacheProgress);
LoadDiskCacheProgress(VideoCore::LoadCallbackStage::Complete, 0, 0);

View file

@ -72,9 +72,14 @@ void EmuThread::run() {
std::size_t total) { emit LoadProgress(stage, value, total); });
}
system.GPU().Renderer().Rasterizer()->SetSwitchDiskResourcesCallback(
[this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
emit SwitchDiskResources(stage, value, total);
});
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
system.GPU().Renderer().Rasterizer()->LoadDiskResources(
system.GPU().Renderer().Rasterizer()->LoadDefaultDiskResources(
stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
emit LoadProgress(stage, value, total);
});

View file

@ -1,4 +1,4 @@
// Copyright 2014 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -110,6 +110,9 @@ signals:
void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total);
void SwitchDiskResources(VideoCore::LoadCallbackStage stage, std::size_t value,
std::size_t total);
void HideLoadingScreen();
};

View file

@ -500,6 +500,8 @@ void GMainWindow::InitializeWidgets() {
progress_bar->hide();
statusBar()->addPermanentWidget(progress_bar);
loading_shaders_label = new QLabel();
artic_traffic_label = new QLabel();
artic_traffic_label->setToolTip(
tr("Current Artic traffic speed. Higher values indicate bigger transfer loads."));
@ -515,8 +517,8 @@ void GMainWindow::InitializeWidgets() {
tr("Time taken to emulate a 3DS frame, not counting framelimiting or v-sync. For "
"full-speed emulation this should be at most 16.67 ms."));
for (auto& label :
{artic_traffic_label, emu_speed_label, game_fps_label, emu_frametime_label}) {
for (auto& label : {loading_shaders_label, artic_traffic_label, emu_speed_label, game_fps_label,
emu_frametime_label}) {
label->setVisible(false);
label->setFrameStyle(QFrame::NoFrame);
label->setContentsMargins(4, 0, 4, 0);
@ -1430,6 +1432,8 @@ void GMainWindow::BootGame(const QString& filename) {
connect(emu_thread.get(), &EmuThread::LoadProgress, loading_screen,
&LoadingScreen::OnLoadProgress, Qt::QueuedConnection);
connect(emu_thread.get(), &EmuThread::SwitchDiskResources, this,
&GMainWindow::OnSwitchDiskResources, Qt::QueuedConnection);
connect(emu_thread.get(), &EmuThread::HideLoadingScreen, loading_screen,
&LoadingScreen::OnLoadComplete);
@ -1529,6 +1533,7 @@ void GMainWindow::ShutdownGame() {
status_bar_update_timer.stop();
message_label_used_for_movie = false;
show_artic_label = false;
loading_shaders_label->setVisible(false);
artic_traffic_label->setVisible(false);
emu_speed_label->setVisible(false);
game_fps_label->setVisible(false);
@ -3711,6 +3716,18 @@ void GMainWindow::OnEmulatorUpdateAvailable() {
}
#endif
void GMainWindow::OnSwitchDiskResources(VideoCore::LoadCallbackStage stage, std::size_t value,
std::size_t total) {
if (stage == VideoCore::LoadCallbackStage::Prepare) {
loading_shaders_label->setText(QString());
loading_shaders_label->setVisible(true);
} else if (stage == VideoCore::LoadCallbackStage::Complete) {
loading_shaders_label->setVisible(false);
} else {
loading_shaders_label->setText(loading_screen->GetStageTranslation(stage, value, total));
}
}
void GMainWindow::UpdateWindowTitle() {
const QString full_name = QString::fromUtf8(Common::g_build_fullname);

View file

@ -24,6 +24,7 @@
#include "citra_qt/user_data_migration.h"
#include "core/core.h"
#include "core/savestate.h"
#include "video_core/rasterizer_interface.h"
// Needs to be included at the end due to https://bugreports.qt.io/browse/QTBUG-73263
#include <filesystem>
@ -299,6 +300,8 @@ private slots:
#ifdef ENABLE_QT_UPDATE_CHECKER
void OnEmulatorUpdateAvailable();
#endif
void OnSwitchDiskResources(VideoCore::LoadCallbackStage stage, std::size_t value,
std::size_t total);
private:
Q_INVOKABLE void OnMoviePlaybackCompleted();
@ -334,6 +337,7 @@ private:
QProgressBar* progress_bar = nullptr;
QLabel* message_label = nullptr;
bool show_artic_label = false;
QLabel* loading_shaders_label = nullptr;
QLabel* artic_traffic_label = nullptr;
QLabel* emu_speed_label = nullptr;
QLabel* game_fps_label = nullptr;

View file

@ -1,4 +1,4 @@
// Copyright 2020 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -184,14 +184,7 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size
}
// update labels and progress bar
const auto& stg = tr(stage_translations.at(stage));
if (stage == VideoCore::LoadCallbackStage::Decompile ||
stage == VideoCore::LoadCallbackStage::Build ||
stage == VideoCore::LoadCallbackStage::Preload) {
ui->stage->setText(stg.arg(value).arg(total));
} else {
ui->stage->setText(stg);
}
ui->stage->setText(GetStageTranslation(stage, value, total));
ui->value->setText(estimate);
ui->progress_bar->setValue(static_cast<int>(value));
previous_time = now;
@ -205,4 +198,16 @@ void LoadingScreen::paintEvent(QPaintEvent* event) {
QWidget::paintEvent(event);
}
QString LoadingScreen::GetStageTranslation(VideoCore::LoadCallbackStage stage, std::size_t value,
std::size_t total) {
const auto& stg = tr(stage_translations.at(stage));
if (stage == VideoCore::LoadCallbackStage::Decompile ||
stage == VideoCore::LoadCallbackStage::Build ||
stage == VideoCore::LoadCallbackStage::Preload) {
return stg.arg(value).arg(total);
} else {
return stg;
}
}
void LoadingScreen::Clear() {}

View file

@ -1,4 +1,4 @@
// Copyright 2020 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -49,6 +49,9 @@ public:
// See https://wiki.qt.io/How_to_Change_the_Background_Color_of_QWidget
void paintEvent(QPaintEvent* event) override;
QString GetStageTranslation(VideoCore::LoadCallbackStage stage, std::size_t value,
std::size_t total);
signals:
void LoadProgress(VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total);
/// Signals that this widget is completely hidden now and should be replaced with the other

View file

@ -509,9 +509,7 @@ Result CIAFile::WriteTitleMetadata(std::span<const u8> tmd_data, std::size_t off
return FileSys::ResultFileNotFound;
}
PrepareToImportContent(tmd);
return ResultSuccess;
return PrepareToImportContent(tmd);
}
ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length, const u8* buffer) {

View file

@ -21,6 +21,7 @@
#include "video_core/gpu.h"
#include "video_core/gpu_debugger.h"
#include "video_core/pica/regs_lcd.h"
#include "video_core/renderer_base.h"
#include "video_core/right_eye_disabler.h"
SERIALIZE_EXPORT_IMPL(Service::GSP::SessionData)
@ -613,6 +614,8 @@ Result GSP_GPU::AcquireGpuRight(const Kernel::HLERequestContext& ctx,
ErrorLevel::Success};
}
gpu.Renderer().Rasterizer()->SwitchDiskResources(process->codeset->program_id);
if (blocking) {
// TODO: The thread should be put to sleep until acquired.
ASSERT_MSG(active_thread_id == std::numeric_limits<u32>::max(),

View file

@ -9,6 +9,8 @@
namespace VideoCore {
DiskResourceLoadCallback RasterizerInterface::switch_disk_resources_callback{};
using Pica::f24;
static Common::Vec4f ColorRGBA8(const u32 color) {

View file

@ -1,4 +1,4 @@
// Copyright 2015 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -78,8 +78,15 @@ public:
return false;
}
virtual void LoadDiskResources([[maybe_unused]] const std::atomic_bool& stop_loading,
[[maybe_unused]] const DiskResourceLoadCallback& callback) {}
virtual void LoadDefaultDiskResources(
[[maybe_unused]] const std::atomic_bool& stop_loading,
[[maybe_unused]] const DiskResourceLoadCallback& callback) {}
virtual void SwitchDiskResources([[maybe_unused]] u64 title_id) {}
static void SetSwitchDiskResourcesCallback(const DiskResourceLoadCallback& callback) {
switch_disk_resources_callback = callback;
}
virtual void SyncEntireState() {}
@ -89,5 +96,9 @@ public:
protected:
bool accurate_mul = false;
// Rasterizer gets destroyed on reboot, so make the callback
// static until a better solution is found.
static DiskResourceLoadCallback switch_disk_resources_callback;
};
} // namespace VideoCore

View file

@ -8,6 +8,7 @@
#include "common/logging/log.h"
#include "common/math_util.h"
#include "common/microprofile.h"
#include "core/loader/loader.h"
#include "video_core/pica/pica_core.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/pica_to_gl.h"
@ -84,8 +85,8 @@ RasterizerOpenGL::RasterizerOpenGL(Memory::MemorySystem& memory, Pica::PicaCore&
VideoCore::CustomTexManager& custom_tex_manager,
VideoCore::RendererBase& renderer, Driver& driver_)
: VideoCore::RasterizerAccelerated{memory, pica}, driver{driver_},
shader_manager{renderer.GetRenderWindow(), driver, !driver.IsOpenGLES()},
runtime{driver, renderer}, res_cache{memory, custom_tex_manager, runtime, regs, renderer},
render_window{renderer.GetRenderWindow()}, runtime{driver, renderer},
res_cache{memory, custom_tex_manager, runtime, regs, renderer},
vertex_buffer{driver, GL_ARRAY_BUFFER, VERTEX_BUFFER_SIZE},
uniform_buffer{driver, GL_UNIFORM_BUFFER, UNIFORM_BUFFER_SIZE},
index_buffer{driver, GL_ELEMENT_ARRAY_BUFFER, INDEX_BUFFER_SIZE},
@ -173,9 +174,86 @@ void RasterizerOpenGL::TickFrame() {
res_cache.TickFrame();
}
void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback) {
shader_manager.LoadDiskCache(stop_loading, callback, accurate_mul);
void RasterizerOpenGL::LoadDefaultDiskResources(
const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback) {
// First element in vector is the default one and cannot be removed.
u64 program_id;
if (Core::System::GetInstance().GetAppLoader().ReadProgramId(program_id) !=
Loader::ResultStatus::Success) {
program_id = 0;
}
shader_managers.clear();
curr_shader_manager = shader_managers.emplace_back(std::make_shared<ShaderProgramManager>(
render_window, driver, program_id, !driver.IsOpenGLES()));
curr_shader_manager->LoadDiskCache(stop_loading, callback, accurate_mul);
}
void RasterizerOpenGL::SwitchDiskResources(u64 title_id) {
// NOTE: curr_shader_manager can be null if emulation restarted without calling
// LoadDefaultDiskResources
// Check if the current manager is for the specified TID.
if (curr_shader_manager && curr_shader_manager->GetProgramID() == title_id) {
return;
}
// Search for an existing manager
size_t new_pos = 0;
for (new_pos = 0; new_pos < shader_managers.size(); new_pos++) {
if (shader_managers[new_pos]->GetProgramID() == title_id) {
break;
}
}
// Manager does not exist, create it and append to the end
if (new_pos >= shader_managers.size()) {
new_pos = shader_managers.size();
auto& new_manager = shader_managers.emplace_back(std::make_shared<ShaderProgramManager>(
render_window, driver, title_id, !driver.IsOpenGLES()));
if (switch_disk_resources_callback) {
switch_disk_resources_callback(VideoCore::LoadCallbackStage::Prepare, 0, 0);
}
std::atomic_bool stop_loading;
new_manager->LoadDiskCache(stop_loading, switch_disk_resources_callback, accurate_mul);
if (switch_disk_resources_callback) {
switch_disk_resources_callback(VideoCore::LoadCallbackStage::Complete, 0, 0);
}
}
auto is_applet = [](u64 tid) {
constexpr u32 APPLET_TID_HIGH = 0x00040030;
return static_cast<u32>(tid >> 32) == APPLET_TID_HIGH;
};
bool prev_applet = curr_shader_manager ? is_applet(curr_shader_manager->GetProgramID()) : false;
bool new_applet = is_applet(shader_managers[new_pos]->GetProgramID());
curr_shader_manager = shader_managers[new_pos];
if (prev_applet) {
// If we came from an applet, clean up all other applets
for (auto it = shader_managers.begin(); it != shader_managers.end();) {
if (it == shader_managers.begin() || *it == curr_shader_manager ||
!is_applet((*it)->GetProgramID())) {
it++;
continue;
}
it = shader_managers.erase(it);
}
}
if (!new_applet) {
// If we are going into a non-applet, clean up everything
for (auto it = shader_managers.begin(); it != shader_managers.end();) {
if (it == shader_managers.begin() || *it == curr_shader_manager) {
it++;
continue;
}
it = shader_managers.erase(it);
}
}
}
void RasterizerOpenGL::SyncFixedState() {
@ -271,7 +349,7 @@ void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset,
bool RasterizerOpenGL::SetupVertexShader() {
MICROPROFILE_SCOPE(OpenGL_VS);
return shader_manager.UseProgrammableVertexShader(regs, pica.vs_setup, accurate_mul);
return curr_shader_manager->UseProgrammableVertexShader(regs, pica.vs_setup, accurate_mul);
}
bool RasterizerOpenGL::SetupGeometryShader() {
@ -286,9 +364,9 @@ bool RasterizerOpenGL::SetupGeometryShader() {
// lighting and care about proper quaternions. Otherwise just use standard vertex+fragment
// shaders
if (regs.lighting.disable) {
shader_manager.UseTrivialGeometryShader();
curr_shader_manager->UseTrivialGeometryShader();
} else {
shader_manager.UseFixedGeometryShader(regs);
curr_shader_manager->UseFixedGeometryShader(regs);
}
return true;
@ -333,7 +411,7 @@ bool RasterizerOpenGL::AccelerateDrawBatchInternal(bool is_indexed) {
SetupVertexArray(buffer_ptr, buffer_offset, vs_input_index_min, vs_input_index_max);
vertex_buffer.Unmap(vs_input_size);
shader_manager.ApplyTo(state, accurate_mul);
curr_shader_manager->ApplyTo(state, accurate_mul);
state.Apply();
if (is_indexed) {
@ -444,7 +522,7 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
// Sync and bind the shader
if (shader_dirty) {
shader_manager.UseFragmentShader(regs, user_config);
curr_shader_manager->UseFragmentShader(regs, user_config);
shader_dirty = false;
}
@ -462,9 +540,9 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
} else {
state.draw.vertex_array = sw_vao.handle;
state.draw.vertex_buffer = vertex_buffer.GetHandle();
shader_manager.UseTrivialVertexShader();
shader_manager.UseTrivialGeometryShader();
shader_manager.ApplyTo(state, accurate_mul);
curr_shader_manager->UseTrivialVertexShader();
curr_shader_manager->UseTrivialGeometryShader();
curr_shader_manager->ApplyTo(state, accurate_mul);
state.Apply();
std::size_t max_vertices = 3 * (VERTEX_BUFFER_SIZE / (3 * sizeof(HardwareVertex)));

View file

@ -1,4 +1,4 @@
// Copyright 2022 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -40,8 +40,9 @@ public:
~RasterizerOpenGL() override;
void TickFrame();
void LoadDiskResources(const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback) override;
void LoadDefaultDiskResources(const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback) override;
void SwitchDiskResources(u64 title_id) override;
void DrawTriangles() override;
void FlushAll() override;
@ -137,7 +138,9 @@ private:
private:
Driver& driver;
OpenGLState state;
ShaderProgramManager shader_manager;
Frontend::EmuWindow& render_window;
std::vector<std::shared_ptr<ShaderProgramManager>> shader_managers;
std::shared_ptr<ShaderProgramManager> curr_shader_manager{};
TextureRuntime runtime;
RasterizerCache res_cache;

View file

@ -1,3 +1,7 @@
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -12,8 +16,6 @@
#include "common/scm_rev.h"
#include "common/settings.h"
#include "common/zstd_compression.h"
#include "core/core.h"
#include "core/loader/loader.h"
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
namespace OpenGL {
@ -103,8 +105,8 @@ bool ShaderDiskCacheRaw::Save(FileUtil::IOFile& file) const {
return true;
}
ShaderDiskCache::ShaderDiskCache(bool separable)
: separable{separable}, transferable_file(AppendTransferableFile()),
ShaderDiskCache::ShaderDiskCache(u64 program_id, bool separable)
: separable{separable}, program_id{program_id}, transferable_file(AppendTransferableFile()),
// seperable shaders use the virtual precompile file, that already has a header.
precompiled_file(AppendPrecompiledFile(!separable)) {}
@ -568,15 +570,7 @@ std::string ShaderDiskCache::GetBaseDir() const {
return FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir) + DIR_SEP "opengl";
}
u64 ShaderDiskCache::GetProgramID() {
// Skip games without title id
if (program_id != 0) {
return program_id;
}
if (Core::System::GetInstance().GetAppLoader().ReadProgramId(program_id) !=
Loader::ResultStatus::Success) {
return 0;
}
u64 ShaderDiskCache::GetProgramID() const {
return program_id;
}

View file

@ -1,3 +1,7 @@
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -83,7 +87,7 @@ struct ShaderDiskCacheDump {
class ShaderDiskCache {
public:
explicit ShaderDiskCache(bool separable);
explicit ShaderDiskCache(u64 title_id, bool separable);
~ShaderDiskCache() = default;
/// Loads transferable cache. If file has a old version or on failure, it deletes the file.
@ -113,6 +117,9 @@ public:
/// Serializes virtual precompiled shader cache file to real file
void SaveVirtualPrecompiledFile();
/// Get current game's title id as u64
u64 GetProgramID() const;
private:
/// Loads the transferable cache. Returns empty on failure.
std::optional<std::pair<ShaderDecompiledMap, ShaderDumpsMap>> LoadPrecompiledFile(
@ -161,9 +168,6 @@ private:
/// Get user's shader directory path
std::string GetBaseDir() const;
/// Get current game's title id as u64
u64 GetProgramID();
/// Get current game's title id
std::string GetTitleID();

View file

@ -1,4 +1,4 @@
// Copyright 2022 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -259,10 +259,10 @@ using FragmentShaders = ShaderCache<FSConfig, &GLSL::GenerateFragmentShader, GL_
class ShaderProgramManager::Impl {
public:
explicit Impl(const Driver& driver, bool separable)
explicit Impl(const Driver& driver, u64 title_id, bool separable)
: separable(separable), programmable_vertex_shaders(separable),
trivial_vertex_shader(driver, separable), fixed_geometry_shaders(separable),
fragment_shaders(separable), disk_cache(separable) {
fragment_shaders(separable), disk_cache(title_id, separable) {
if (separable) {
pipeline.Create();
}
@ -329,10 +329,10 @@ public:
};
ShaderProgramManager::ShaderProgramManager(Frontend::EmuWindow& emu_window_, const Driver& driver_,
bool separable)
u64 title_id, bool separable)
: emu_window{emu_window_}, driver{driver_},
strict_context_required{emu_window.StrictContextRequired()},
impl{std::make_unique<Impl>(driver_, separable)} {}
impl{std::make_unique<Impl>(driver_, title_id, separable)} {}
ShaderProgramManager::~ShaderProgramManager() = default;
@ -425,6 +425,10 @@ void ShaderProgramManager::ApplyTo(OpenGLState& state, bool accurate_mul) {
}
}
u64 ShaderProgramManager::GetProgramID() const {
return impl->disk_cache.GetProgramID();
}
void ShaderProgramManager::LoadDiskCache(const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback,
bool accurate_mul) {

View file

@ -1,4 +1,4 @@
// Copyright 2022 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -34,7 +34,8 @@ enum UniformBindings {
/// A class that manage different shader stages and configures them with given config data.
class ShaderProgramManager {
public:
ShaderProgramManager(Frontend::EmuWindow& emu_window, const Driver& driver, bool separable);
ShaderProgramManager(Frontend::EmuWindow& emu_window, const Driver& driver, u64 title_id,
bool separable);
~ShaderProgramManager();
void LoadDiskCache(const std::atomic_bool& stop_loading,
@ -53,6 +54,8 @@ public:
void ApplyTo(OpenGLState& state, bool accurate_mul);
u64 GetProgramID() const;
private:
Frontend::EmuWindow& emu_window;
const Driver& driver;

View file

@ -143,8 +143,8 @@ void RasterizerVulkan::TickFrame() {
res_cache.TickFrame();
}
void RasterizerVulkan::LoadDiskResources(const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback) {
void RasterizerVulkan::LoadDefaultDiskResources(
const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback) {
pipeline_cache.LoadDiskCache();
}

View file

@ -1,4 +1,4 @@
// Copyright 2023 Citra Emulator Project
// Copyright Citra Emulator Project / Azahar Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
@ -45,8 +45,8 @@ public:
~RasterizerVulkan() override;
void TickFrame();
void LoadDiskResources(const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback) override;
void LoadDefaultDiskResources(const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback) override;
void DrawTriangles() override;
void FlushAll() override;