mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-06-09 12:31:40 +12:00
Merge pull request #120 from Wunkolo/renderer-null
Add a `null` rendering backend
This commit is contained in:
commit
5b4f6ef46c
13 changed files with 143 additions and 30 deletions
|
@ -95,7 +95,8 @@ endif()
|
||||||
|
|
||||||
set(SOURCE_FILES src/main.cpp src/emulator.cpp src/io_file.cpp src/config.cpp
|
set(SOURCE_FILES src/main.cpp src/emulator.cpp src/io_file.cpp src/config.cpp
|
||||||
src/core/CPU/cpu_dynarmic.cpp src/core/CPU/dynarmic_cycles.cpp
|
src/core/CPU/cpu_dynarmic.cpp src/core/CPU/dynarmic_cycles.cpp
|
||||||
src/core/memory.cpp src/renderer.cpp src/httpserver.cpp src/stb_image_write.c
|
src/core/memory.cpp src/renderer.cpp src/core/renderer_null/renderer_null.cpp
|
||||||
|
src/httpserver.cpp src/stb_image_write.c
|
||||||
)
|
)
|
||||||
set(CRYPTO_SOURCE_FILES src/core/crypto/aes_engine.cpp)
|
set(CRYPTO_SOURCE_FILES src/core/crypto/aes_engine.cpp)
|
||||||
set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp
|
set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp
|
||||||
|
@ -130,7 +131,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
|
||||||
include/dynarmic_cp15.hpp include/kernel/resource_limits.hpp include/kernel/kernel_types.hpp
|
include/dynarmic_cp15.hpp include/kernel/resource_limits.hpp include/kernel/kernel_types.hpp
|
||||||
include/kernel/config_mem.hpp include/services/service_manager.hpp include/services/apt.hpp
|
include/kernel/config_mem.hpp include/services/service_manager.hpp include/services/apt.hpp
|
||||||
include/kernel/handles.hpp include/services/hid.hpp include/services/fs.hpp
|
include/kernel/handles.hpp include/services/hid.hpp include/services/fs.hpp
|
||||||
include/services/gsp_gpu.hpp include/services/gsp_lcd.hpp include/arm_defs.hpp
|
include/services/gsp_gpu.hpp include/services/gsp_lcd.hpp include/arm_defs.hpp include/renderer_null/renderer_null.hpp
|
||||||
include/PICA/gpu.hpp include/PICA/regs.hpp include/services/ndm.hpp
|
include/PICA/gpu.hpp include/PICA/regs.hpp include/services/ndm.hpp
|
||||||
include/PICA/shader.hpp include/PICA/shader_unit.hpp include/PICA/float_types.hpp
|
include/PICA/shader.hpp include/PICA/shader_unit.hpp include/PICA/float_types.hpp
|
||||||
include/logger.hpp include/loader/ncch.hpp include/loader/ncsd.hpp include/io_file.hpp
|
include/logger.hpp include/loader/ncch.hpp include/loader/ncsd.hpp include/io_file.hpp
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include "renderer.hpp"
|
||||||
|
|
||||||
// Remember to initialize every field here to its default value otherwise bad things will happen
|
// Remember to initialize every field here to its default value otherwise bad things will happen
|
||||||
struct EmulatorConfig {
|
struct EmulatorConfig {
|
||||||
bool shaderJitEnabled = false;
|
bool shaderJitEnabled = false;
|
||||||
|
RendererType rendererType = RendererType::OpenGL;
|
||||||
|
|
||||||
|
EmulatorConfig(const std::filesystem::path& path);
|
||||||
void load(const std::filesystem::path& path);
|
void load(const std::filesystem::path& path);
|
||||||
void save(const std::filesystem::path& path);
|
void save(const std::filesystem::path& path);
|
||||||
};
|
};
|
|
@ -25,13 +25,13 @@ enum class ROMType {
|
||||||
};
|
};
|
||||||
|
|
||||||
class Emulator {
|
class Emulator {
|
||||||
|
EmulatorConfig config;
|
||||||
CPU cpu;
|
CPU cpu;
|
||||||
GPU gpu;
|
GPU gpu;
|
||||||
Memory memory;
|
Memory memory;
|
||||||
Kernel kernel;
|
Kernel kernel;
|
||||||
Crypto::AESEngine aesEngine;
|
Crypto::AESEngine aesEngine;
|
||||||
|
|
||||||
EmulatorConfig config;
|
|
||||||
SDL_Window* window;
|
SDL_Window* window;
|
||||||
|
|
||||||
#ifdef PANDA3DS_ENABLE_OPENGL
|
#ifdef PANDA3DS_ENABLE_OPENGL
|
||||||
|
@ -70,8 +70,8 @@ class Emulator {
|
||||||
public:
|
public:
|
||||||
// Decides whether to reload or not reload the ROM when resetting. We use enum class over a plain bool for clarity.
|
// Decides whether to reload or not reload the ROM when resetting. We use enum class over a plain bool for clarity.
|
||||||
// If NoReload is selected, the emulator will not reload its selected ROM. This is useful for things like booting up the emulator, or resetting to
|
// If NoReload is selected, the emulator will not reload its selected ROM. This is useful for things like booting up the emulator, or resetting to
|
||||||
// change ROMs. If Reload is selected, the emulator will reload its selected ROM. This is useful for eg a "reset" button that keeps the current ROM
|
// change ROMs. If Reload is selected, the emulator will reload its selected ROM. This is useful for eg a "reset" button that keeps the current
|
||||||
// and just resets the emu
|
// ROM and just resets the emu
|
||||||
enum class ReloadOption { NoReload, Reload };
|
enum class ReloadOption { NoReload, Reload };
|
||||||
|
|
||||||
Emulator();
|
Emulator();
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include "PICA/pica_vertex.hpp"
|
#include "PICA/pica_vertex.hpp"
|
||||||
#include "PICA/regs.hpp"
|
#include "PICA/regs.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
|
||||||
|
enum class RendererType : s8 {
|
||||||
|
// Todo: Auto = -1,
|
||||||
|
Null = 0,
|
||||||
|
OpenGL = 1,
|
||||||
|
Vulkan = 2,
|
||||||
|
};
|
||||||
|
|
||||||
class GPU;
|
class GPU;
|
||||||
|
|
||||||
class Renderer {
|
class Renderer {
|
||||||
|
@ -28,6 +36,8 @@ class Renderer {
|
||||||
virtual ~Renderer();
|
virtual ~Renderer();
|
||||||
|
|
||||||
static constexpr u32 vertexBufferSize = 0x10000;
|
static constexpr u32 vertexBufferSize = 0x10000;
|
||||||
|
static std::optional<RendererType> typeFromString(std::string inString);
|
||||||
|
static const char* typeToString(RendererType rendererType);
|
||||||
|
|
||||||
virtual void reset() = 0;
|
virtual void reset() = 0;
|
||||||
virtual void display() = 0; // Display the 3DS screen contents to the window
|
virtual void display() = 0; // Display the 3DS screen contents to the window
|
||||||
|
|
|
@ -68,6 +68,7 @@ class RendererGL final : public Renderer {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RendererGL(GPU& gpu, const std::array<u32, regNum>& internalRegs) : Renderer(gpu, internalRegs) {}
|
RendererGL(GPU& gpu, const std::array<u32, regNum>& internalRegs) : Renderer(gpu, internalRegs) {}
|
||||||
|
~RendererGL() override;
|
||||||
|
|
||||||
void reset() override;
|
void reset() override;
|
||||||
void display() override; // Display the 3DS screen contents to the window
|
void display() override; // Display the 3DS screen contents to the window
|
||||||
|
|
17
include/renderer_null/renderer_null.hpp
Normal file
17
include/renderer_null/renderer_null.hpp
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#include "renderer.hpp"
|
||||||
|
|
||||||
|
class GPU;
|
||||||
|
|
||||||
|
class RendererNull final : public Renderer {
|
||||||
|
public:
|
||||||
|
RendererNull(GPU& gpu, const std::array<u32, regNum>& internalRegs);
|
||||||
|
~RendererNull() override;
|
||||||
|
|
||||||
|
void reset() override;
|
||||||
|
void display() override;
|
||||||
|
void initGraphicsContext() override;
|
||||||
|
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) override;
|
||||||
|
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) override;
|
||||||
|
void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) override;
|
||||||
|
void screenshot(const std::string& name) override;
|
||||||
|
};
|
|
@ -1,6 +1,7 @@
|
||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "toml.hpp"
|
#include "toml.hpp"
|
||||||
|
@ -9,6 +10,8 @@
|
||||||
// We are legally allowed, as per the author's wish, to use the above code without any licensing restrictions
|
// We are legally allowed, as per the author's wish, to use the above code without any licensing restrictions
|
||||||
// However we still want to follow the license as closely as possible and offer the proper attributions.
|
// However we still want to follow the license as closely as possible and offer the proper attributions.
|
||||||
|
|
||||||
|
EmulatorConfig::EmulatorConfig(const std::filesystem::path& path) { load(path); }
|
||||||
|
|
||||||
void EmulatorConfig::load(const std::filesystem::path& path) {
|
void EmulatorConfig::load(const std::filesystem::path& path) {
|
||||||
// If the configuration file does not exist, create it and return
|
// If the configuration file does not exist, create it and return
|
||||||
std::error_code error;
|
std::error_code error;
|
||||||
|
@ -31,6 +34,17 @@ void EmulatorConfig::load(const std::filesystem::path& path) {
|
||||||
if (gpuResult.is_ok()) {
|
if (gpuResult.is_ok()) {
|
||||||
auto gpu = gpuResult.unwrap();
|
auto gpu = gpuResult.unwrap();
|
||||||
|
|
||||||
|
// Get renderer
|
||||||
|
auto rendererName = toml::find_or<std::string>(gpu, "Renderer", "OpenGL");
|
||||||
|
auto configRendererType = Renderer::typeFromString(rendererName);
|
||||||
|
|
||||||
|
if (configRendererType.has_value()) {
|
||||||
|
rendererType = configRendererType.value();
|
||||||
|
} else {
|
||||||
|
Helpers::warn("Invalid renderer specified: %s\n", rendererName.c_str());
|
||||||
|
rendererType = RendererType::OpenGL;
|
||||||
|
}
|
||||||
|
|
||||||
shaderJitEnabled = toml::find_or<toml::boolean>(gpu, "EnableShaderJIT", false);
|
shaderJitEnabled = toml::find_or<toml::boolean>(gpu, "EnableShaderJIT", false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +57,7 @@ void EmulatorConfig::save(const std::filesystem::path& path) {
|
||||||
if (std::filesystem::exists(path, error)) {
|
if (std::filesystem::exists(path, error)) {
|
||||||
try {
|
try {
|
||||||
data = toml::parse<toml::preserve_comments>(path);
|
data = toml::parse<toml::preserve_comments>(path);
|
||||||
} catch (std::exception& ex) {
|
} catch (const std::exception& ex) {
|
||||||
Helpers::warn("Exception trying to parse config file. Exception: %s\n", ex.what());
|
Helpers::warn("Exception trying to parse config file. Exception: %s\n", ex.what());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -55,6 +69,7 @@ void EmulatorConfig::save(const std::filesystem::path& path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
data["GPU"]["EnableShaderJIT"] = shaderJitEnabled;
|
data["GPU"]["EnableShaderJIT"] = shaderJitEnabled;
|
||||||
|
data["GPU"]["Renderer"] = std::string(Renderer::typeToString(rendererType));
|
||||||
|
|
||||||
std::ofstream file(path, std::ios::out);
|
std::ofstream file(path, std::ios::out);
|
||||||
file << data;
|
file << data;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#include "PICA/float_types.hpp"
|
#include "PICA/float_types.hpp"
|
||||||
#include "PICA/regs.hpp"
|
#include "PICA/regs.hpp"
|
||||||
|
#include "renderer_null/renderer_null.hpp"
|
||||||
#ifdef PANDA3DS_ENABLE_OPENGL
|
#ifdef PANDA3DS_ENABLE_OPENGL
|
||||||
#include "renderer_gl/renderer_gl.hpp"
|
#include "renderer_gl/renderer_gl.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
@ -20,10 +20,27 @@ GPU::GPU(Memory& mem, EmulatorConfig& config) : mem(mem), config(config) {
|
||||||
vram = new u8[vramSize];
|
vram = new u8[vramSize];
|
||||||
mem.setVRAM(vram); // Give the bus a pointer to our VRAM
|
mem.setVRAM(vram); // Give the bus a pointer to our VRAM
|
||||||
|
|
||||||
// TODO: Configurable backend
|
switch (config.rendererType) {
|
||||||
|
case RendererType::Null: {
|
||||||
|
renderer.reset(new RendererNull(*this, regs));
|
||||||
|
break;
|
||||||
|
}
|
||||||
#ifdef PANDA3DS_ENABLE_OPENGL
|
#ifdef PANDA3DS_ENABLE_OPENGL
|
||||||
|
case RendererType::OpenGL: {
|
||||||
renderer.reset(new RendererGL(*this, regs));
|
renderer.reset(new RendererGL(*this, regs));
|
||||||
|
break;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
case RendererType::Vulkan: {
|
||||||
|
Helpers::panic("Vulkan is not supported yet, please pick another renderer");
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
Helpers::panic("Rendering backend not supported: %s", Renderer::typeToString(config.rendererType));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU::reset() {
|
void GPU::reset() {
|
||||||
|
|
|
@ -14,6 +14,8 @@ using namespace Floats;
|
||||||
using namespace Helpers;
|
using namespace Helpers;
|
||||||
using namespace PICA;
|
using namespace PICA;
|
||||||
|
|
||||||
|
RendererGL::~RendererGL() {}
|
||||||
|
|
||||||
void RendererGL::reset() {
|
void RendererGL::reset() {
|
||||||
depthBufferCache.reset();
|
depthBufferCache.reset();
|
||||||
colourBufferCache.reset();
|
colourBufferCache.reset();
|
||||||
|
|
12
src/core/renderer_null/renderer_null.cpp
Normal file
12
src/core/renderer_null/renderer_null.cpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include "renderer_null/renderer_null.hpp"
|
||||||
|
|
||||||
|
RendererNull::RendererNull(GPU& gpu, const std::array<u32, regNum>& internalRegs) : Renderer(gpu, internalRegs) {}
|
||||||
|
RendererNull::~RendererNull() {}
|
||||||
|
|
||||||
|
void RendererNull::reset() {}
|
||||||
|
void RendererNull::display() {}
|
||||||
|
void RendererNull::initGraphicsContext() {}
|
||||||
|
void RendererNull::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {}
|
||||||
|
void RendererNull::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) {}
|
||||||
|
void RendererNull::drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) {}
|
||||||
|
void RendererNull::screenshot(const std::string& name) {}
|
|
@ -14,7 +14,9 @@ __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 1;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Emulator::Emulator() : kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory, config), memory(cpu.getTicksRef()) {
|
Emulator::Emulator()
|
||||||
|
: config(std::filesystem::current_path() / "config.toml"), kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory, config),
|
||||||
|
memory(cpu.getTicksRef()) {
|
||||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
|
||||||
Helpers::panic("Failed to initialize SDL2");
|
Helpers::panic("Failed to initialize SDL2");
|
||||||
}
|
}
|
||||||
|
@ -26,6 +28,7 @@ Emulator::Emulator() : kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef PANDA3DS_ENABLE_OPENGL
|
#ifdef PANDA3DS_ENABLE_OPENGL
|
||||||
|
if (config.rendererType == RendererType::OpenGL) {
|
||||||
// Request OpenGL 4.1 Core (Max available on MacOS)
|
// Request OpenGL 4.1 Core (Max available on MacOS)
|
||||||
// MacOS gets mad if we don't explicitly demand a core profile
|
// MacOS gets mad if we don't explicitly demand a core profile
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||||
|
@ -45,6 +48,7 @@ Emulator::Emulator() : kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory
|
||||||
if (!gladLoadGL(reinterpret_cast<GLADloadfunc>(SDL_GL_GetProcAddress))) {
|
if (!gladLoadGL(reinterpret_cast<GLADloadfunc>(SDL_GL_GetProcAddress))) {
|
||||||
Helpers::panic("OpenGL init failed: %s", SDL_GetError());
|
Helpers::panic("OpenGL init failed: %s", SDL_GetError());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (SDL_WasInit(SDL_INIT_GAMECONTROLLER)) {
|
if (SDL_WasInit(SDL_INIT_GAMECONTROLLER)) {
|
||||||
|
@ -56,7 +60,6 @@ Emulator::Emulator() : kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
config.load(std::filesystem::current_path() / "config.toml");
|
|
||||||
reset(ReloadOption::NoReload);
|
reset(ReloadOption::NoReload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,35 @@
|
||||||
#include "renderer.hpp"
|
#include "renderer.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
Renderer::Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs) : gpu(gpu), regs(internalRegs) {}
|
Renderer::Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs) : gpu(gpu), regs(internalRegs) {}
|
||||||
Renderer::~Renderer() {}
|
Renderer::~Renderer() {}
|
||||||
|
|
||||||
|
std::optional<RendererType> Renderer::typeFromString(std::string inString) {
|
||||||
|
// Transform to lower-case to make the setting case-insensitive
|
||||||
|
std::transform(inString.begin(), inString.end(), inString.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||||
|
|
||||||
|
// Huge table of possible names and misspellings
|
||||||
|
// Please stop misspelling Vulkan as Vulcan
|
||||||
|
static const std::unordered_map<std::string, RendererType> map = {
|
||||||
|
{"null", RendererType::Null}, {"nil", RendererType::Null}, {"none", RendererType::Null},
|
||||||
|
{"gl", RendererType::OpenGL}, {"ogl", RendererType::OpenGL}, {"opengl", RendererType::OpenGL},
|
||||||
|
{"vk", RendererType::Vulkan}, {"vulkan", RendererType::Vulkan}, {"vulcan", RendererType::Vulkan},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (auto search = map.find(inString); search != map.end()) {
|
||||||
|
return search->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Renderer::typeToString(RendererType rendererType) {
|
||||||
|
switch (rendererType) {
|
||||||
|
case RendererType::Null: return "null";
|
||||||
|
case RendererType::OpenGL: return "opengl";
|
||||||
|
case RendererType::Vulkan: return "vulkan";
|
||||||
|
default: return "Invalid";
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue