Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a null rendering backend #120

Merged
merged 11 commits into from
Jul 18, 2023
5 changes: 3 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ endif()

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/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(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp
Expand Down Expand Up @@ -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/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/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/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
Expand Down
4 changes: 4 additions & 0 deletions include/config.hpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
#pragma once
#include <filesystem>

#include "renderer.hpp"

// Remember to initialize every field here to its default value otherwise bad things will happen
struct EmulatorConfig {
bool shaderJitEnabled = false;
RendererType rendererType = RendererType::OpenGL;

EmulatorConfig(const std::filesystem::path& path);
void load(const std::filesystem::path& path);
void save(const std::filesystem::path& path);
};
6 changes: 3 additions & 3 deletions include/emulator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ enum class ROMType {
};

class Emulator {
EmulatorConfig config;
CPU cpu;
GPU gpu;
Memory memory;
Kernel kernel;
Crypto::AESEngine aesEngine;

EmulatorConfig config;
SDL_Window* window;

#ifdef PANDA3DS_ENABLE_OPENGL
Expand Down Expand Up @@ -70,8 +70,8 @@ class Emulator {
public:
// 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
// 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
// and just resets the emu
// 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 and just resets the emu
enum class ReloadOption { NoReload, Reload };

Emulator();
Expand Down
10 changes: 10 additions & 0 deletions include/renderer.hpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
#pragma once
#include <array>
#include <span>
#include <optional>

#include "PICA/pica_vertex.hpp"
#include "PICA/regs.hpp"
#include "helpers.hpp"

enum class RendererType : s8 {
// Todo: Auto = -1,
Null = 0,
OpenGL = 1,
Vulkan = 2,
};

class GPU;

class Renderer {
Expand All @@ -28,6 +36,8 @@ class Renderer {
virtual ~Renderer();

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 display() = 0; // Display the 3DS screen contents to the window
Expand Down
1 change: 1 addition & 0 deletions include/renderer_gl/renderer_gl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class RendererGL final : public Renderer {

public:
RendererGL(GPU& gpu, const std::array<u32, regNum>& internalRegs) : Renderer(gpu, internalRegs) {}
~RendererGL() override;

void reset() override;
void display() override; // Display the 3DS screen contents to the window
Expand Down
17 changes: 17 additions & 0 deletions include/renderer_null/renderer_null.hpp
Original file line number Diff line number Diff line change
@@ -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;
};
17 changes: 16 additions & 1 deletion src/config.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "config.hpp"

#include <fstream>
#include <string>

#include "helpers.hpp"
#include "toml.hpp"
Expand All @@ -9,6 +10,8 @@
// 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.

EmulatorConfig::EmulatorConfig(const std::filesystem::path& path) { load(path); }

void EmulatorConfig::load(const std::filesystem::path& path) {
// If the configuration file does not exist, create it and return
std::error_code error;
Expand All @@ -31,6 +34,17 @@ void EmulatorConfig::load(const std::filesystem::path& path) {
if (gpuResult.is_ok()) {
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);
}
}
Expand All @@ -43,7 +57,7 @@ void EmulatorConfig::save(const std::filesystem::path& path) {
if (std::filesystem::exists(path, error)) {
try {
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());
return;
}
Expand All @@ -55,6 +69,7 @@ void EmulatorConfig::save(const std::filesystem::path& path) {
}

data["GPU"]["EnableShaderJIT"] = shaderJitEnabled;
data["GPU"]["Renderer"] = std::string(Renderer::typeToString(rendererType));

std::ofstream file(path, std::ios::out);
file << data;
Expand Down
23 changes: 20 additions & 3 deletions src/core/PICA/gpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

#include "PICA/float_types.hpp"
#include "PICA/regs.hpp"

#include "renderer_null/renderer_null.hpp"
#ifdef PANDA3DS_ENABLE_OPENGL
#include "renderer_gl/renderer_gl.hpp"
#endif
Expand All @@ -20,10 +20,27 @@ GPU::GPU(Memory& mem, EmulatorConfig& config) : mem(mem), config(config) {
vram = new u8[vramSize];
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
renderer.reset(new RendererGL(*this, regs));
case RendererType::OpenGL: {
renderer.reset(new RendererGL(*this, regs));
break;
}
#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() {
Expand Down
2 changes: 2 additions & 0 deletions src/core/renderer_gl/renderer_gl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ using namespace Floats;
using namespace Helpers;
using namespace PICA;

RendererGL::~RendererGL() {}

void RendererGL::reset() {
depthBufferCache.reset();
colourBufferCache.reset();
Expand Down
12 changes: 12 additions & 0 deletions src/core/renderer_null/renderer_null.cpp
Original file line number Diff line number Diff line change
@@ -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) {}
39 changes: 21 additions & 18 deletions src/emulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 1;
}
#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) {
Helpers::panic("Failed to initialize SDL2");
}
Expand All @@ -26,24 +28,26 @@ Emulator::Emulator() : kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory
}

#ifdef PANDA3DS_ENABLE_OPENGL
// Request OpenGL 4.1 Core (Max available on MacOS)
// 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_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
window = SDL_CreateWindow("Alber", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL);

if (window == nullptr) {
Helpers::panic("Window creation failed: %s", SDL_GetError());
}
if (config.rendererType == RendererType::OpenGL) {
// Request OpenGL 4.1 Core (Max available on MacOS)
// 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_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
window = SDL_CreateWindow("Alber", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL);

if (window == nullptr) {
Helpers::panic("Window creation failed: %s", SDL_GetError());
}

glContext = SDL_GL_CreateContext(window);
if (glContext == nullptr) {
Helpers::panic("OpenGL context creation failed: %s", SDL_GetError());
}
glContext = SDL_GL_CreateContext(window);
if (glContext == nullptr) {
Helpers::panic("OpenGL context creation failed: %s", SDL_GetError());
}

if (!gladLoadGL(reinterpret_cast<GLADloadfunc>(SDL_GL_GetProcAddress))) {
Helpers::panic("OpenGL init failed: %s", SDL_GetError());
if (!gladLoadGL(reinterpret_cast<GLADloadfunc>(SDL_GL_GetProcAddress))) {
Helpers::panic("OpenGL init failed: %s", SDL_GetError());
}
}
#endif

Expand All @@ -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);
}

Expand Down
6 changes: 3 additions & 3 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#include "emulator.hpp"

int main (int argc, char *argv[]) {
Emulator emu;
int main(int argc, char *argv[]) {
Emulator emu;

emu.initGraphicsContext();
emu.initGraphicsContext();

if (argc > 1) {
auto romPath = std::filesystem::current_path() / argv[1];
Expand Down
33 changes: 32 additions & 1 deletion src/renderer.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,35 @@
#include "renderer.hpp"

#include <algorithm>
#include <unordered_map>

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