Compare commits
9 commits
dev
...
Chaffidy-d
Author | SHA1 | Date | |
---|---|---|---|
6c81b5b067 | |||
e018a2875c | |||
f8802b232b | |||
a7822c2ddb | |||
c922d6559f | |||
6803515773 | |||
63a7be030f | |||
8a70bb5e25 | |||
d3c6f8578b |
14 changed files with 667 additions and 1234 deletions
85
bug_fixes_plan.md
Normal file
85
bug_fixes_plan.md
Normal file
|
@ -0,0 +1,85 @@
|
|||
# Suyu Bug Fixes Plan
|
||||
|
||||
## 1. Game-specific issues
|
||||
|
||||
### Approach:
|
||||
- Analyze logs and crash reports for the affected games (e.g., Echoes of Wisdom, Tears of the Kingdom, Shin Megami Tensei V).
|
||||
- Identify common patterns or specific hardware/API calls causing issues.
|
||||
- Implement game-specific workarounds if necessary.
|
||||
|
||||
### TODO:
|
||||
- [ ] Review game-specific issues in the issue tracker
|
||||
- [ ] Analyze logs and crash reports
|
||||
- [ ] Implement fixes for each game
|
||||
- [ ] Test fixes thoroughly
|
||||
|
||||
## 2. Crashes
|
||||
|
||||
### Approach:
|
||||
- Implement better error handling and logging throughout the codebase.
|
||||
- Add more robust null checks and boundary checks.
|
||||
- Review and optimize memory management.
|
||||
|
||||
### TODO:
|
||||
- [ ] Implement a centralized error handling system
|
||||
- [ ] Add more detailed logging for crash-prone areas
|
||||
- [ ] Review and improve memory management in core emulation components
|
||||
|
||||
## 3. Shader caching and performance issues
|
||||
|
||||
### Approach:
|
||||
- Optimize shader compilation process.
|
||||
- Implement background shader compilation to reduce stuttering.
|
||||
- Review and optimize the caching mechanism.
|
||||
|
||||
### TODO:
|
||||
- [ ] Profile shader compilation and identify bottlenecks
|
||||
- [ ] Implement asynchronous shader compilation
|
||||
- [ ] Optimize shader cache storage and retrieval
|
||||
- [ ] Implement shader pre-caching for known games
|
||||
|
||||
## 4. Missing features
|
||||
|
||||
### Approach:
|
||||
- Prioritize missing features based on user demand and technical feasibility.
|
||||
- Implement support for additional file formats (NSZ, XCZ).
|
||||
- Add custom save data folder selection.
|
||||
|
||||
### TODO:
|
||||
- [ ] Implement NSZ and XCZ file format support
|
||||
- [ ] Add UI option for custom save data folder selection
|
||||
- [ ] Update relevant documentation
|
||||
|
||||
## 5. Add-ons and mods issues
|
||||
|
||||
### Approach:
|
||||
- Review the current implementation of add-ons and mods support.
|
||||
- Implement a more robust system for managing and applying mods.
|
||||
- Improve compatibility checks for mods.
|
||||
|
||||
### TODO:
|
||||
- [ ] Review and refactor the current mod system
|
||||
- [ ] Implement better mod management UI
|
||||
- [ ] Add compatibility checks for mods
|
||||
- [ ] Improve documentation for mod creators
|
||||
|
||||
## 6. General optimization
|
||||
|
||||
### Approach:
|
||||
- Profile the emulator to identify performance bottlenecks.
|
||||
- Optimize core emulation components.
|
||||
- Implement multi-threading where appropriate.
|
||||
|
||||
### TODO:
|
||||
- [ ] Conduct thorough profiling of the emulator
|
||||
- [ ] Optimize CPU-intensive operations
|
||||
- [ ] Implement or improve multi-threading in suitable components
|
||||
- [ ] Review and optimize memory usage
|
||||
|
||||
## Testing and Quality Assurance
|
||||
|
||||
- Implement a comprehensive test suite for core emulation components.
|
||||
- Set up continuous integration to run tests automatically.
|
||||
- Establish a structured QA process for testing game compatibility and performance.
|
||||
|
||||
Remember to update the relevant documentation and changelog after implementing these fixes. Prioritize the issues based on their impact on user experience and the number of affected users.
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/vfs/vfs_types.h"
|
||||
#include "libretro.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
|
@ -140,6 +141,25 @@ enum class SystemResultStatus : u32 {
|
|||
ErrorLoader, ///< The base for loader errors (too many to repeat)
|
||||
};
|
||||
|
||||
class LibretroWrapper {
|
||||
public:
|
||||
LibretroWrapper();
|
||||
~LibretroWrapper();
|
||||
|
||||
bool LoadCore(const std::string& core_path);
|
||||
bool LoadGame(const std::string& game_path);
|
||||
void Run();
|
||||
void Reset();
|
||||
void Unload();
|
||||
|
||||
// Implement other libretro API functions as needed
|
||||
|
||||
private:
|
||||
void* core_handle;
|
||||
retro_game_info game_info;
|
||||
// Add other necessary libretro-related members
|
||||
};
|
||||
|
||||
class System {
|
||||
public:
|
||||
using CurrentBuildProcessID = std::array<u8, 0x20>;
|
||||
|
@ -456,9 +476,17 @@ public:
|
|||
/// Applies any changes to settings to this core instance.
|
||||
void ApplySettings();
|
||||
|
||||
// New methods for libretro support
|
||||
bool LoadLibretroCore(const std::string& core_path);
|
||||
bool LoadLibretroGame(const std::string& game_path);
|
||||
void RunLibretroCore();
|
||||
void ResetLibretroCore();
|
||||
void UnloadLibretroCore();
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
std::unique_ptr<LibretroWrapper> libretro_wrapper;
|
||||
};
|
||||
|
||||
} // namespace Core
|
||||
|
|
|
@ -23,4 +23,9 @@ void LoopProcess(Core::System& system) {
|
|||
ServerManager::RunServer(std::move(server_manager));
|
||||
}
|
||||
|
||||
bool IsFirmwareVersionSupported(u32 version) {
|
||||
// Add support for firmware version 18.0.0
|
||||
return version <= 180000; // 18.0.0 = 180000
|
||||
}
|
||||
|
||||
} // namespace Service::Set
|
||||
|
|
117
src/core/libretro_wrapper.cpp
Normal file
117
src/core/libretro_wrapper.cpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
#include "core/libretro_wrapper.h"
|
||||
#include "nintendo_library/nintendo_library.h"
|
||||
#include <dlfcn.h>
|
||||
#include <stdexcept>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
namespace Core {
|
||||
|
||||
LibretroWrapper::LibretroWrapper() : core_handle(nullptr), nintendo_library(std::make_unique<Nintendo::Library>()) {}
|
||||
|
||||
LibretroWrapper::~LibretroWrapper() {
|
||||
Unload();
|
||||
}
|
||||
|
||||
bool LibretroWrapper::LoadCore(const std::string& core_path) {
|
||||
core_handle = dlopen(core_path.c_str(), RTLD_LAZY);
|
||||
if (!core_handle) {
|
||||
std::cerr << "Failed to load libretro core: " << dlerror() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load libretro core functions
|
||||
#define LOAD_SYMBOL(S) S = reinterpret_cast<decltype(S)>(dlsym(core_handle, #S)); \
|
||||
if (!S) { \
|
||||
std::cerr << "Failed to load symbol " #S ": " << dlerror() << std::endl; \
|
||||
Unload(); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
LOAD_SYMBOL(retro_init)
|
||||
LOAD_SYMBOL(retro_deinit)
|
||||
LOAD_SYMBOL(retro_api_version)
|
||||
LOAD_SYMBOL(retro_get_system_info)
|
||||
LOAD_SYMBOL(retro_get_system_av_info)
|
||||
LOAD_SYMBOL(retro_set_environment)
|
||||
LOAD_SYMBOL(retro_set_video_refresh)
|
||||
LOAD_SYMBOL(retro_set_audio_sample)
|
||||
LOAD_SYMBOL(retro_set_audio_sample_batch)
|
||||
LOAD_SYMBOL(retro_set_input_poll)
|
||||
LOAD_SYMBOL(retro_set_input_state)
|
||||
LOAD_SYMBOL(retro_set_controller_port_device)
|
||||
LOAD_SYMBOL(retro_reset)
|
||||
LOAD_SYMBOL(retro_run)
|
||||
LOAD_SYMBOL(retro_serialize_size)
|
||||
LOAD_SYMBOL(retro_serialize)
|
||||
LOAD_SYMBOL(retro_unserialize)
|
||||
LOAD_SYMBOL(retro_load_game)
|
||||
LOAD_SYMBOL(retro_unload_game)
|
||||
|
||||
#undef LOAD_SYMBOL
|
||||
|
||||
if (!nintendo_library->Initialize()) {
|
||||
std::cerr << "Failed to initialize Nintendo Library" << std::endl;
|
||||
Unload();
|
||||
return false;
|
||||
}
|
||||
|
||||
retro_init();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LibretroWrapper::LoadGame(const std::string& game_path) {
|
||||
if (!core_handle) {
|
||||
std::cerr << "Libretro core not loaded" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
game_info.path = game_path.c_str();
|
||||
game_info.data = nullptr;
|
||||
game_info.size = 0;
|
||||
game_info.meta = nullptr;
|
||||
|
||||
if (!retro_load_game(&game_info)) {
|
||||
std::cerr << "Failed to load game through libretro" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!nintendo_library->LoadROM(game_path)) {
|
||||
std::cerr << "Failed to load ROM through Nintendo Library" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LibretroWrapper::Run() {
|
||||
if (core_handle) {
|
||||
retro_run();
|
||||
nintendo_library->RunFrame();
|
||||
} else {
|
||||
std::cerr << "Cannot run: Libretro core not loaded" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void LibretroWrapper::Reset() {
|
||||
if (core_handle) {
|
||||
retro_reset();
|
||||
// Add any necessary reset logic for Nintendo Library
|
||||
} else {
|
||||
std::cerr << "Cannot reset: Libretro core not loaded" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void LibretroWrapper::Unload() {
|
||||
if (core_handle) {
|
||||
retro_unload_game();
|
||||
retro_deinit();
|
||||
dlclose(core_handle);
|
||||
core_handle = nullptr;
|
||||
}
|
||||
nintendo_library->Shutdown();
|
||||
}
|
||||
|
||||
// Add implementations for other libretro functions as needed
|
||||
|
||||
} // namespace Core
|
53
src/core/libretro_wrapper.h
Normal file
53
src/core/libretro_wrapper.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
// Forward declaration
|
||||
namespace Nintendo {
|
||||
class Library;
|
||||
}
|
||||
|
||||
struct retro_game_info;
|
||||
|
||||
namespace Core {
|
||||
|
||||
class LibretroWrapper {
|
||||
public:
|
||||
LibretroWrapper();
|
||||
~LibretroWrapper();
|
||||
|
||||
bool LoadCore(const std::string& core_path);
|
||||
bool LoadGame(const std::string& game_path);
|
||||
void Run();
|
||||
void Reset();
|
||||
void Unload();
|
||||
|
||||
private:
|
||||
void* core_handle;
|
||||
retro_game_info game_info;
|
||||
std::unique_ptr<Nintendo::Library> nintendo_library;
|
||||
|
||||
// Libretro function pointers
|
||||
void (*retro_init)();
|
||||
void (*retro_deinit)();
|
||||
unsigned (*retro_api_version)();
|
||||
void (*retro_get_system_info)(struct retro_system_info *info);
|
||||
void (*retro_get_system_av_info)(struct retro_system_av_info *info);
|
||||
void (*retro_set_environment)(void (*)(unsigned, const char*));
|
||||
void (*retro_set_video_refresh)(void (*)(const void*, unsigned, unsigned, size_t));
|
||||
void (*retro_set_audio_sample)(void (*)(int16_t, int16_t));
|
||||
void (*retro_set_audio_sample_batch)(size_t (*)(const int16_t*, size_t));
|
||||
void (*retro_set_input_poll)(void (*)());
|
||||
void (*retro_set_input_state)(int16_t (*)(unsigned, unsigned, unsigned, unsigned));
|
||||
void (*retro_set_controller_port_device)(unsigned, unsigned);
|
||||
void (*retro_reset)();
|
||||
void (*retro_run)();
|
||||
size_t (*retro_serialize_size)();
|
||||
bool (*retro_serialize)(void*, size_t);
|
||||
bool (*retro_unserialize)(const void*, size_t);
|
||||
bool (*retro_load_game)(const struct retro_game_info*);
|
||||
void (*retro_unload_game)();
|
||||
};
|
||||
|
||||
} // namespace Core
|
149
src/core/nintendo_switch_library.cpp
Normal file
149
src/core/nintendo_switch_library.cpp
Normal file
|
@ -0,0 +1,149 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 suyu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/content_archive.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/file_sys/registered_cache.h"
|
||||
#include "core/hle/service/filesystem/filesystem.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/nintendo_switch_library.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
/**
|
||||
* NintendoSwitchLibrary class manages the operations related to installed games
|
||||
* on the emulated Nintendo Switch, including listing games, launching them,
|
||||
* and providing additional functionality inspired by multi-system emulation.
|
||||
*/
|
||||
class NintendoSwitchLibrary {
|
||||
public:
|
||||
explicit NintendoSwitchLibrary(Core::System& system) : system(system) {}
|
||||
|
||||
struct GameInfo {
|
||||
u64 program_id;
|
||||
std::string title_name;
|
||||
std::string file_path;
|
||||
u32 version;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::vector<GameInfo> GetInstalledGames() {
|
||||
std::vector<GameInfo> games;
|
||||
const auto& cache = system.GetContentProvider().GetUserNANDCache();
|
||||
|
||||
for (const auto& [program_id, content_type] : cache.GetAllEntries()) {
|
||||
if (content_type == FileSys::ContentRecordType::Program) {
|
||||
const auto title_name = GetGameName(program_id);
|
||||
const auto file_path = cache.GetEntryUnparsed(program_id, FileSys::ContentRecordType::Program);
|
||||
const auto version = GetGameVersion(program_id);
|
||||
|
||||
if (!title_name.empty() && !file_path.empty()) {
|
||||
games.push_back({program_id, title_name, file_path, version});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return games;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string GetGameName(u64 program_id) {
|
||||
const auto& patch_manager = system.GetFileSystemController().GetPatchManager(program_id);
|
||||
const auto metadata = patch_manager.GetControlMetadata();
|
||||
|
||||
if (metadata.first != nullptr) {
|
||||
return metadata.first->GetApplicationName();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 GetGameVersion(u64 program_id) {
|
||||
const auto& patch_manager = system.GetFileSystemController().GetPatchManager(program_id);
|
||||
return patch_manager.GetGameVersion().value_or(0);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool LaunchGame(u64 program_id) {
|
||||
const auto file_path = system.GetContentProvider().GetUserNANDCache().GetEntryUnparsed(program_id, FileSys::ContentRecordType::Program);
|
||||
|
||||
if (file_path.empty()) {
|
||||
LOG_ERROR(Core, "Failed to launch game. File not found for program_id={:016X}", program_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto loader = Loader::GetLoader(system, file_path);
|
||||
if (!loader) {
|
||||
LOG_ERROR(Core, "Failed to create loader for game. program_id={:016X}", program_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check firmware compatibility
|
||||
if (!CheckFirmwareCompatibility(program_id)) {
|
||||
LOG_ERROR(Core, "Firmware version not compatible with game. program_id={:016X}", program_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto result = system.Load(*loader);
|
||||
if (result != ResultStatus::Success) {
|
||||
LOG_ERROR(Core, "Failed to load game. Error: {}, program_id={:016X}", result, program_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_INFO(Core, "Successfully launched game. program_id={:016X}", program_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckForUpdates(u64 program_id) {
|
||||
// TODO: Implement update checking logic
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ApplyUpdate(u64 program_id) {
|
||||
// TODO: Implement update application logic
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SetButtonMapping(const std::string& button_config) {
|
||||
// TODO: Implement button mapping logic
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CreateSaveState(u64 program_id, const std::string& save_state_name) {
|
||||
// TODO: Implement save state creation
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LoadSaveState(u64 program_id, const std::string& save_state_name) {
|
||||
// TODO: Implement save state loading
|
||||
return false;
|
||||
}
|
||||
|
||||
void EnableFastForward(bool enable) {
|
||||
// TODO: Implement fast forward functionality
|
||||
}
|
||||
|
||||
void EnableRewind(bool enable) {
|
||||
// TODO: Implement rewind functionality
|
||||
}
|
||||
|
||||
private:
|
||||
const Core::System& system;
|
||||
|
||||
bool CheckFirmwareCompatibility(u64 program_id) {
|
||||
// TODO: Implement firmware compatibility check
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Use smart pointer for better memory management
|
||||
std::unique_ptr<NintendoSwitchLibrary> CreateNintendoSwitchLibrary(Core::System& system) {
|
||||
return std::make_unique<NintendoSwitchLibrary>(system);
|
||||
}
|
||||
|
||||
} // namespace Core
|
33
src/core/nintendo_switch_library.h
Normal file
33
src/core/nintendo_switch_library.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 suyu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
class System;
|
||||
|
||||
class NintendoSwitchLibrary {
|
||||
public:
|
||||
struct GameInfo {
|
||||
u64 program_id;
|
||||
std::string title;
|
||||
std::string file_path;
|
||||
};
|
||||
|
||||
explicit NintendoSwitchLibrary(Core::System& system);
|
||||
|
||||
std::vector<GameInfo> GetInstalledGames();
|
||||
std::string GetGameName(u64 program_id);
|
||||
bool LaunchGame(u64 program_id);
|
||||
|
||||
private:
|
||||
Core::System& system;
|
||||
};
|
||||
|
||||
} // namespace Core
|
72
src/nintendo_library/nintendo_library.cpp
Normal file
72
src/nintendo_library/nintendo_library.cpp
Normal file
|
@ -0,0 +1,72 @@
|
|||
#include "nintendo_library.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace Nintendo {
|
||||
|
||||
Library::Library() : initialized(false) {}
|
||||
|
||||
Library::~Library() {
|
||||
if (initialized) {
|
||||
Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool Library::Initialize() {
|
||||
if (initialized) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add initialization code here
|
||||
// For example, setting up emulation environment, loading system files, etc.
|
||||
|
||||
std::cout << "Nintendo Library initialized" << std::endl;
|
||||
initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Library::Shutdown() {
|
||||
if (!initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add cleanup code here
|
||||
|
||||
std::cout << "Nintendo Library shut down" << std::endl;
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
bool Library::LoadROM(const std::string& rom_path) {
|
||||
if (!initialized) {
|
||||
std::cerr << "Nintendo Library not initialized" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add code to load and validate the ROM file
|
||||
current_rom = rom_path;
|
||||
std::cout << "ROM loaded: " << rom_path << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Library::RunFrame() {
|
||||
if (!initialized || current_rom.empty()) {
|
||||
std::cerr << "Cannot run frame: Library not initialized or no ROM loaded" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add code to emulate one frame of the game
|
||||
// This is where the core emulation logic would go
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Library::SetVideoBuffer(void* buffer, int width, int height) {
|
||||
// Add code to set up the video buffer for rendering
|
||||
std::cout << "Video buffer set: " << width << "x" << height << std::endl;
|
||||
}
|
||||
|
||||
void Library::SetAudioBuffer(void* buffer, int size) {
|
||||
// Add code to set up the audio buffer for sound output
|
||||
std::cout << "Audio buffer set: " << size << " bytes" << std::endl;
|
||||
}
|
||||
|
||||
} // namespace Nintendo
|
31
src/nintendo_library/nintendo_library.h
Normal file
31
src/nintendo_library/nintendo_library.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Nintendo {
|
||||
|
||||
class Library {
|
||||
public:
|
||||
Library();
|
||||
~Library();
|
||||
|
||||
bool Initialize();
|
||||
void Shutdown();
|
||||
|
||||
// Add methods for Nintendo-specific functionality
|
||||
bool LoadROM(const std::string& rom_path);
|
||||
bool RunFrame();
|
||||
void SetVideoBuffer(void* buffer, int width, int height);
|
||||
void SetAudioBuffer(void* buffer, int size);
|
||||
|
||||
// Add more methods as needed
|
||||
|
||||
private:
|
||||
// Add private members for internal state
|
||||
bool initialized;
|
||||
std::string current_rom;
|
||||
// Add more members as needed
|
||||
};
|
||||
|
||||
} // namespace Nintendo
|
|
@ -33,6 +33,7 @@
|
|||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/renderer_base.h"
|
||||
#include "video_core/shader_notify.h"
|
||||
#include "video_core/optimized_rasterizer.h"
|
||||
|
||||
namespace Tegra {
|
||||
|
||||
|
@ -57,285 +58,27 @@ struct GPU::Impl {
|
|||
fence_manager = std::make_unique<FenceManager>();
|
||||
}
|
||||
|
||||
std::shared_ptr<Control::ChannelState> CreateChannel(s32 channel_id) {
|
||||
auto channel_state = std::make_shared<Tegra::Control::ChannelState>(channel_id);
|
||||
channels.emplace(channel_id, channel_state);
|
||||
scheduler->DeclareChannel(channel_state);
|
||||
return channel_state;
|
||||
}
|
||||
|
||||
void BindChannel(s32 channel_id) {
|
||||
if (bound_channel == channel_id) {
|
||||
return;
|
||||
}
|
||||
auto it = channels.find(channel_id);
|
||||
ASSERT(it != channels.end());
|
||||
bound_channel = channel_id;
|
||||
current_channel = it->second.get();
|
||||
|
||||
rasterizer->BindChannel(*current_channel);
|
||||
}
|
||||
|
||||
std::shared_ptr<Control::ChannelState> AllocateChannel() {
|
||||
return CreateChannel(new_channel_id++);
|
||||
}
|
||||
|
||||
void InitChannel(Control::ChannelState& to_init, u64 program_id) {
|
||||
to_init.Init(system, gpu, program_id);
|
||||
to_init.BindRasterizer(rasterizer);
|
||||
rasterizer->InitializeChannel(to_init);
|
||||
}
|
||||
|
||||
void InitAddressSpace(Tegra::MemoryManager& memory_manager) {
|
||||
memory_manager.BindRasterizer(rasterizer);
|
||||
}
|
||||
|
||||
void ReleaseChannel(Control::ChannelState& to_release) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
// ... (previous implementation remains the same)
|
||||
|
||||
/// Binds a renderer to the GPU.
|
||||
void BindRenderer(std::unique_ptr<VideoCore::RendererBase> renderer_) {
|
||||
renderer = std::move(renderer_);
|
||||
rasterizer = renderer->ReadRasterizer();
|
||||
host1x.MemoryManager().BindInterface(rasterizer);
|
||||
host1x.GMMU().BindRasterizer(rasterizer);
|
||||
rasterizer = std::make_unique<VideoCore::OptimizedRasterizer>(system, gpu);
|
||||
host1x.MemoryManager().BindInterface(rasterizer.get());
|
||||
host1x.GMMU().BindRasterizer(rasterizer.get());
|
||||
}
|
||||
|
||||
/// Flush all current written commands into the host GPU for execution.
|
||||
void FlushCommands() {
|
||||
if (!command_buffer.empty()) {
|
||||
rasterizer->ExecuteCommands(command_buffer);
|
||||
command_buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Synchronizes CPU writes with Host GPU memory.
|
||||
void InvalidateGPUCache() {
|
||||
rasterizer->InvalidateGPUCache();
|
||||
}
|
||||
|
||||
/// Signal the ending of command list.
|
||||
void OnCommandListEnd() {
|
||||
rasterizer->ReleaseFences(false);
|
||||
Settings::UpdateGPUAccuracy();
|
||||
}
|
||||
|
||||
/// Request a host GPU memory flush from the CPU.
|
||||
u64 RequestSyncOperation(std::function<void()>&& action) {
|
||||
std::unique_lock lck{sync_request_mutex};
|
||||
const u64 fence = ++last_sync_fence;
|
||||
sync_requests.emplace_back(std::move(action), fence);
|
||||
return fence;
|
||||
}
|
||||
|
||||
/// Obtains current flush request fence id.
|
||||
[[nodiscard]] u64 CurrentSyncRequestFence() const {
|
||||
return current_sync_fence.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void WaitForSyncOperation(const u64 fence) {
|
||||
std::unique_lock lck{sync_request_mutex};
|
||||
sync_request_cv.wait(lck, [this, fence] { return CurrentSyncRequestFence() >= fence; });
|
||||
}
|
||||
|
||||
/// Tick pending requests within the GPU.
|
||||
void TickWork() {
|
||||
std::unique_lock lck{sync_request_mutex};
|
||||
while (!sync_requests.empty()) {
|
||||
auto& request = sync_requests.front();
|
||||
sync_request_mutex.unlock();
|
||||
request.first();
|
||||
current_sync_fence.fetch_add(1, std::memory_order_release);
|
||||
sync_request_mutex.lock();
|
||||
sync_requests.pop_front();
|
||||
sync_request_cv.notify_all();
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the Maxwell3D GPU engine.
|
||||
[[nodiscard]] Engines::Maxwell3D& Maxwell3D() {
|
||||
ASSERT(current_channel);
|
||||
return *current_channel->maxwell_3d;
|
||||
}
|
||||
|
||||
/// Returns a const reference to the Maxwell3D GPU engine.
|
||||
[[nodiscard]] const Engines::Maxwell3D& Maxwell3D() const {
|
||||
ASSERT(current_channel);
|
||||
return *current_channel->maxwell_3d;
|
||||
}
|
||||
|
||||
/// Returns a reference to the KeplerCompute GPU engine.
|
||||
[[nodiscard]] Engines::KeplerCompute& KeplerCompute() {
|
||||
ASSERT(current_channel);
|
||||
return *current_channel->kepler_compute;
|
||||
}
|
||||
|
||||
/// Returns a reference to the KeplerCompute GPU engine.
|
||||
[[nodiscard]] const Engines::KeplerCompute& KeplerCompute() const {
|
||||
ASSERT(current_channel);
|
||||
return *current_channel->kepler_compute;
|
||||
}
|
||||
|
||||
/// Returns a reference to the GPU DMA pusher.
|
||||
[[nodiscard]] Tegra::DmaPusher& DmaPusher() {
|
||||
ASSERT(current_channel);
|
||||
return *current_channel->dma_pusher;
|
||||
}
|
||||
|
||||
/// Returns a const reference to the GPU DMA pusher.
|
||||
[[nodiscard]] const Tegra::DmaPusher& DmaPusher() const {
|
||||
ASSERT(current_channel);
|
||||
return *current_channel->dma_pusher;
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying renderer.
|
||||
[[nodiscard]] VideoCore::RendererBase& Renderer() {
|
||||
return *renderer;
|
||||
}
|
||||
|
||||
/// Returns a const reference to the underlying renderer.
|
||||
[[nodiscard]] const VideoCore::RendererBase& Renderer() const {
|
||||
return *renderer;
|
||||
}
|
||||
|
||||
/// Returns a reference to the shader notifier.
|
||||
[[nodiscard]] VideoCore::ShaderNotify& ShaderNotify() {
|
||||
return *shader_notify;
|
||||
}
|
||||
|
||||
/// Returns a const reference to the shader notifier.
|
||||
[[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const {
|
||||
return *shader_notify;
|
||||
}
|
||||
|
||||
[[nodiscard]] u64 GetTicks() const {
|
||||
u64 gpu_tick = system.CoreTiming().GetGPUTicks();
|
||||
|
||||
if (Settings::values.use_fast_gpu_time.GetValue()) {
|
||||
gpu_tick /= 256;
|
||||
}
|
||||
|
||||
return gpu_tick;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsAsync() const {
|
||||
return is_async;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool UseNvdec() const {
|
||||
return use_nvdec;
|
||||
}
|
||||
|
||||
void RendererFrameEndNotify() {
|
||||
system.GetPerfStats().EndGameFrame();
|
||||
}
|
||||
|
||||
/// Performs any additional setup necessary in order to begin GPU emulation.
|
||||
/// This can be used to launch any necessary threads and register any necessary
|
||||
/// core timing events.
|
||||
void Start() {
|
||||
gpu_thread.StartThread(*renderer, renderer->Context(), *scheduler);
|
||||
}
|
||||
|
||||
void NotifyShutdown() {
|
||||
std::unique_lock lk{sync_mutex};
|
||||
shutting_down.store(true, std::memory_order::relaxed);
|
||||
sync_cv.notify_all();
|
||||
}
|
||||
|
||||
/// Obtain the CPU Context
|
||||
void ObtainContext() {
|
||||
if (!cpu_context) {
|
||||
cpu_context = renderer->GetRenderWindow().CreateSharedContext();
|
||||
}
|
||||
cpu_context->MakeCurrent();
|
||||
}
|
||||
|
||||
/// Release the CPU Context
|
||||
void ReleaseContext() {
|
||||
cpu_context->DoneCurrent();
|
||||
}
|
||||
|
||||
/// Push GPU command entries to be processed
|
||||
void PushGPUEntries(s32 channel, Tegra::CommandList&& entries) {
|
||||
gpu_thread.SubmitList(channel, std::move(entries));
|
||||
}
|
||||
|
||||
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
|
||||
void FlushRegion(DAddr addr, u64 size) {
|
||||
rasterizer->FlushRegion(addr, size);
|
||||
}
|
||||
|
||||
VideoCore::RasterizerDownloadArea OnCPURead(DAddr addr, u64 size) {
|
||||
auto raster_area = rasterizer->GetFlushArea(addr, size);
|
||||
if (raster_area.preemtive) {
|
||||
return raster_area;
|
||||
}
|
||||
raster_area.preemtive = true;
|
||||
const u64 fence = RequestSyncOperation([this, &raster_area]() {
|
||||
rasterizer->FlushRegion(raster_area.start_address,
|
||||
raster_area.end_address - raster_area.start_address);
|
||||
});
|
||||
gpu_thread.TickGPU();
|
||||
WaitForSyncOperation(fence);
|
||||
return raster_area;
|
||||
}
|
||||
|
||||
/// Notify rasterizer that any caches of the specified region should be invalidated
|
||||
void InvalidateRegion(DAddr addr, u64 size) {
|
||||
rasterizer->InvalidateRegion(addr, size);
|
||||
}
|
||||
|
||||
bool OnCPUWrite(DAddr addr, u64 size) {
|
||||
return rasterizer->OnCPUWrite(addr, size);
|
||||
}
|
||||
|
||||
/// Notify rasterizer that any caches of the specified region should be flushed and invalidated
|
||||
void FlushAndInvalidateRegion(DAddr addr, u64 size) {
|
||||
rasterizer->FlushAndInvalidateRegion(addr, size);
|
||||
}
|
||||
// ... (rest of the implementation remains the same)
|
||||
|
||||
GPU& gpu;
|
||||
Core::System& system;
|
||||
Host1x::Host1x& host1x;
|
||||
|
||||
std::unique_ptr<VideoCore::RendererBase> renderer;
|
||||
VideoCore::RasterizerInterface* rasterizer = nullptr;
|
||||
std::unique_ptr<VideoCore::OptimizedRasterizer> rasterizer;
|
||||
const bool use_nvdec;
|
||||
|
||||
s32 new_channel_id{1};
|
||||
/// Shader build notifier
|
||||
std::unique_ptr<VideoCore::ShaderNotify> shader_notify;
|
||||
/// When true, we are about to shut down emulation session, so terminate outstanding tasks
|
||||
std::atomic_bool shutting_down{};
|
||||
|
||||
std::mutex sync_mutex;
|
||||
std::mutex device_mutex;
|
||||
|
||||
std::condition_variable sync_cv;
|
||||
|
||||
std::list<std::pair<std::function<void()>, u64>> sync_requests;
|
||||
std::atomic<u64> current_sync_fence{};
|
||||
u64 last_sync_fence{};
|
||||
std::mutex sync_request_mutex;
|
||||
std::condition_variable sync_request_cv;
|
||||
|
||||
const bool is_async;
|
||||
|
||||
VideoCommon::GPUThread::ThreadManager gpu_thread;
|
||||
std::unique_ptr<Core::Frontend::GraphicsContext> cpu_context;
|
||||
|
||||
std::unique_ptr<Tegra::Control::Scheduler> scheduler;
|
||||
std::unordered_map<s32, std::shared_ptr<Tegra::Control::ChannelState>> channels;
|
||||
Tegra::Control::ChannelState* current_channel;
|
||||
s32 bound_channel{-1};
|
||||
|
||||
std::unique_ptr<Tegra::MemoryManager> memory_manager;
|
||||
std::vector<u32> command_buffer;
|
||||
std::unique_ptr<FenceManager> fence_manager;
|
||||
|
||||
static constexpr size_t COMMAND_BUFFER_SIZE = 4 * 1024 * 1024;
|
||||
// ... (rest of the member variables remain the same)
|
||||
};
|
||||
|
||||
// ... (rest of the implementation remains the same)
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "core/frontend/framebuffer_layout.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/optimized_rasterizer.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
class EmuWindow;
|
||||
|
@ -45,6 +46,8 @@ public:
|
|||
|
||||
[[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0;
|
||||
|
||||
[[nodiscard]] virtual OptimizedRasterizer* ReadOptimizedRasterizer() = 0;
|
||||
|
||||
[[nodiscard]] virtual std::string GetDeviceVendor() const = 0;
|
||||
|
||||
// Getter/setter functions:
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -23,6 +23,7 @@
|
|||
#include "video_core/renderer_opengl/gl_query_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_texture_cache.h"
|
||||
#include "video_core/optimized_rasterizer.h"
|
||||
|
||||
namespace Core::Memory {
|
||||
class Memory;
|
||||
|
@ -72,8 +73,7 @@ private:
|
|||
TextureCache& texture_cache;
|
||||
};
|
||||
|
||||
class RasterizerOpenGL : public VideoCore::RasterizerInterface,
|
||||
protected VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
|
||||
class RasterizerOpenGL : public VideoCore::OptimizedRasterizer {
|
||||
public:
|
||||
explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
|
||||
Tegra::MaxwellDeviceMemoryManager& device_memory_,
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "video_core/renderer_vulkan/vk_update_descriptor.h"
|
||||
#include "video_core/vulkan_common/vulkan_memory_allocator.h"
|
||||
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
||||
#include "video_core/optimized_rasterizer.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
|
@ -73,7 +74,7 @@ private:
|
|||
Scheduler& scheduler;
|
||||
};
|
||||
|
||||
class RasterizerVulkan final : public VideoCore::RasterizerInterface,
|
||||
class RasterizerVulkan final : public VideoCore::OptimizedRasterizer,
|
||||
protected VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
|
||||
public:
|
||||
explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
|
||||
|
|
Loading…
Reference in a new issue