diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..dc371319 --- /dev/null +++ b/.clang-format @@ -0,0 +1,14 @@ +BasedOnStyle: Google +IndentWidth: 4 +ColumnLimit: 150 +AccessModifierOffset: -2 +TabWidth: 4 +NamespaceIndentation: All +UseTab: ForContinuationAndIndentation +AllowShortEnumsOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: true +AllowShortIfStatementsOnASingleLine: true +Cpp11BracedListStyle: true +PackConstructorInitializers: BinPack +AlignAfterOpenBracket: BlockIndent diff --git a/.github/workflows/Windows_Build.yml b/.github/workflows/Windows_Build.yml new file mode 100644 index 00000000..0a4abe41 --- /dev/null +++ b/.github/workflows/Windows_Build.yml @@ -0,0 +1,39 @@ +name: Windows Build + +on: + push: + branches: + - master + pull_request: + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally + # well on Windows or Mac. You can convert this to a matrix build if you need + # cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + - name: Fetch submodules + run: git submodule update --init --recursive + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + + - name: Build + # Build your program with the given configuration + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + + - name: Upload executable + uses: actions/upload-artifact@v2 + with: + name: Windows executable + path: './build/Release/Alber.exe' diff --git a/include/PICA/regs.hpp b/include/PICA/regs.hpp index 8ef46e67..0b3246db 100644 --- a/include/PICA/regs.hpp +++ b/include/PICA/regs.hpp @@ -1,4 +1,5 @@ #pragma once +#include "helpers.hpp" namespace PICAInternalRegs { enum : u32 { @@ -71,7 +72,7 @@ namespace PICAInternalRegs { FixedAttribData0 = 0x233, FixedAttribData1 = 0x234, FixedAttribData2 = 0x235, - + // Command processor registers CmdBufSize0 = 0x238, CmdBufSize1 = 0x239, diff --git a/include/cpu_dynarmic.hpp b/include/cpu_dynarmic.hpp index d51a1f88..daa25d03 100644 --- a/include/cpu_dynarmic.hpp +++ b/include/cpu_dynarmic.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include "dynarmic/interface/A32/a32.h" #include "dynarmic/interface/A32/config.h" #include "dynarmic/interface/exclusive_monitor.h" @@ -132,17 +134,11 @@ public: return jit->Regs()[index]; } - std::array& regs() { - return jit->Regs(); - } + std::span regs() { return jit->Regs(); } - // Get reference to array of FPRs. This array consists of the FPRs as single precision values - // Hence why its base type is u32 - // Note: Dynarmic keeps 64 VFP registers as VFPv3 extends the VFP register set to 64 registers. - // However the 3DS ARM11 is an ARMv6k processor with VFPv2, so only the first 32 registers are actually used - std::array& fprs() { - return jit->ExtRegs(); - } + // Get reference to array of FPRs. This array consists of the FPRs as single precision values + // Hence why its base type is u32 + std::span fprs() { return std::span(jit->ExtRegs()).first<32>(); } void setCPSR(u32 value) { jit->SetCpsr(value); diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index b4790056..cc2f4a4c 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -2,10 +2,12 @@ #include #include #include +#include #include #include -#include "kernel_types.hpp" + #include "helpers.hpp" +#include "kernel_types.hpp" #include "logger.hpp" #include "memory.hpp" #include "resource_limits.hpp" @@ -14,7 +16,7 @@ class CPU; class Kernel { - std::array& regs; + std::span regs; CPU& cpu; Memory& mem; diff --git a/include/opengl.hpp b/include/opengl.hpp index 8cb1613e..f23be4d6 100644 --- a/include/opengl.hpp +++ b/include/opengl.hpp @@ -30,6 +30,19 @@ #include "gl3w.h" +// Check if we have C++20. If yes, we can add C++20 std::span support +#ifdef _MSVC_LANG // MSVC does not properly define __cplusplus without a compiler flag... +#if _MSVC_LANG >= 202002L +#define OPENGL_HAVE_CPP20 +#endif +#elif __cplusplus >= 202002L +#define OPENGL_HAVE_CPP20 +#endif // MSVC_LANG + +#ifdef OPENGL_HAVE_CPP20 +#include +#endif + // Uncomment the following define if you want GL objects to automatically free themselves when their lifetime ends // #define OPENGL_DESTRUCTORS @@ -389,17 +402,30 @@ namespace OpenGL { void free() { glDeleteBuffers(1, &m_handle); } // Reallocates the buffer on every call. Prefer the sub version if possible. - template - void bufferVerts(VertType* vertices, int vertCount, GLenum usage = GL_DYNAMIC_DRAW) { - glBufferData(GL_ARRAY_BUFFER, sizeof(VertType) * vertCount, vertices, usage); - } + template + void bufferVerts(VertType* vertices, int vertCount, GLenum usage = GL_DYNAMIC_DRAW) { + glBufferData(GL_ARRAY_BUFFER, sizeof(VertType) * vertCount, vertices, usage); + } - // Only use if you used createFixedSize - template - void bufferVertsSub(VertType* vertices, int vertCount, GLintptr offset = 0) { - glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(VertType) * vertCount, vertices); - } - }; + // Only use if you used createFixedSize + template + void bufferVertsSub(VertType* vertices, int vertCount, GLintptr offset = 0) { + glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(VertType) * vertCount, vertices); + } + + // If C++20 is available, add overloads that take std::span instead of raw pointers +#ifdef OPENGL_HAVE_CPP20 + template + void bufferVerts(std::span vertices, GLenum usage = GL_DYNAMIC_DRAW) { + glBufferData(GL_ARRAY_BUFFER, sizeof(VertType) * vertices.size(), vertices.data(), usage); + } + + template + void bufferVertsSub(std::span vertices, GLintptr offset = 0) { + glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(VertType) * vertices.size(), vertices.data()); + } +#endif + }; enum DepthFunc { Never = GL_NEVER, // Depth test never passes diff --git a/include/renderer_gl/renderer_gl.hpp b/include/renderer_gl/renderer_gl.hpp index 7f99d496..8ab8b953 100644 --- a/include/renderer_gl/renderer_gl.hpp +++ b/include/renderer_gl/renderer_gl.hpp @@ -1,5 +1,7 @@ #pragma once #include +#include + #include "helpers.hpp" #include "logger.hpp" #include "opengl.hpp" @@ -50,11 +52,11 @@ class Renderer { SurfaceCache colourBufferCache; SurfaceCache textureCache; - OpenGL::uvec2 fbSize; // The size of the framebuffer (ie both the colour and depth buffer)' + OpenGL::uvec2 fbSize; // The size of the framebuffer (ie both the colour and depth buffer)' + + u32 colourBufferLoc; // Location in 3DS VRAM for the colour buffer + ColourBuffer::Formats colourBufferFormat; // Format of the colours stored in the colour buffer - u32 colourBufferLoc; // Location in 3DS VRAM for the colour buffer - ColourBuffer::Formats colourBufferFormat; // Format of the colours stored in the colour buffer - // Same for the depth/stencil buffer u32 depthBufferLoc; DepthBuffer::Formats depthBufferFormat; @@ -63,7 +65,7 @@ class Renderer { OpenGL::VertexArray dummyVAO; OpenGL::VertexBuffer dummyVBO; - static constexpr u32 regNum = 0x300; // Number of internal PICA registers + static constexpr u32 regNum = 0x300; // Number of internal PICA registers const std::array& regs; OpenGL::Texture screenTexture; @@ -77,16 +79,16 @@ class Renderer { void bindDepthBuffer(); void setupTextureEnvState(); -public: + public: Renderer(GPU& gpu, const std::array& internalRegs) : gpu(gpu), regs(internalRegs) {} void reset(); - void display(); // Display the 3DS screen contents to the window - void initGraphicsContext(); // Initialize graphics context - void getGraphicsContext(); // Set up graphics context for rendering - void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control); // Clear a GPU buffer in VRAM - void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags); // Perform display transfer - void drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count); // Draw the given vertices + void display(); // Display the 3DS screen contents to the window + void initGraphicsContext(); // Initialize graphics context + void getGraphicsContext(); // Set up graphics context for rendering + void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control); // Clear a GPU buffer in VRAM + void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags); // Perform display transfer + void drawVertices(OpenGL::Primitives primType, std::span vertices); // Draw the given vertices void setFBSize(u32 width, u32 height) { fbSize.x() = width; diff --git a/include/services/service_manager.hpp b/include/services/service_manager.hpp index 300e8f2e..e09fd455 100644 --- a/include/services/service_manager.hpp +++ b/include/services/service_manager.hpp @@ -1,6 +1,8 @@ #pragma once #include #include +#include + #include "kernel_types.hpp" #include "logger.hpp" #include "memory.hpp" @@ -14,16 +16,16 @@ #include "services/cfg.hpp" #include "services/dlp_srvr.hpp" #include "services/dsp.hpp" -#include "services/hid.hpp" #include "services/frd.hpp" #include "services/fs.hpp" #include "services/gsp_gpu.hpp" #include "services/gsp_lcd.hpp" +#include "services/hid.hpp" #include "services/ldr_ro.hpp" #include "services/mic.hpp" +#include "services/ndm.hpp" #include "services/nfc.hpp" #include "services/nim.hpp" -#include "services/ndm.hpp" #include "services/ptm.hpp" #include "services/y2r.hpp" @@ -31,7 +33,7 @@ class Kernel; class ServiceManager { - std::array& regs; + std::span regs; Memory& mem; Kernel& kernel; @@ -69,8 +71,8 @@ class ServiceManager { void registerClient(u32 messagePointer); void subscribe(u32 messagePointer); -public: - ServiceManager(std::array& regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel); + public: + ServiceManager(std::span regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel); void reset(); void initializeFS() { fs.initializeFilesystem(); } void handleSyncRequest(u32 messagePointer); diff --git a/readme.md b/readme.md index 8631d18f..6047f19b 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,13 @@ # Panda3DS +[![Windows Build](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/Windows_Build.yml/badge.svg?branch=master)](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/Windows_Build.yml) [![MacOS Build](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/MacOS_Build.yml/badge.svg?branch=master)](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/MacOS_Build.yml) [![Linux Build](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/Linux_Build.yml/badge.svg?branch=master)](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/Linux_Build.yml) Panda3DS is an HLE, red-panda-themed Nintendo 3DS emulator written in C++ which started out as a fun project out of curiosity, but evolved into something that can sort of play games! +# Discussion +Join our Discord server by pressing on the banner below! + +[![Discord Banner 2](https://discord.com/api/guilds/1118695732958994532/widget.png?style=banner2)](https://discord.gg/ZYbugsEmsw) + ![screenshot1](docs/img/MK7_Car_Select.png) ![screenshot2](docs/img/OoT_Title.png) ![screenshot3](docs/img/Mayro.png) # Compatibility diff --git a/src/core/PICA/gpu.cpp b/src/core/PICA/gpu.cpp index 9cbd17a8..f0e832a2 100644 --- a/src/core/PICA/gpu.cpp +++ b/src/core/PICA/gpu.cpp @@ -1,7 +1,10 @@ #include "PICA/gpu.hpp" + +#include +#include + #include "PICA/float_types.hpp" #include "PICA/regs.hpp" -#include using namespace Floats; @@ -41,7 +44,7 @@ void GPU::drawArrays(bool indexed) { drawArrays(); } -Vertex* vertices = new Vertex[Renderer::vertexBufferSize]; +static std::array vertices; template void GPU::drawArrays() { @@ -205,7 +208,7 @@ void GPU::drawArrays() { OpenGL::Triangle, OpenGL::TriangleStrip, OpenGL::TriangleFan, OpenGL::Triangle }; const auto shape = primTypes[primType]; - renderer.drawVertices(shape, vertices, vertexCount); + renderer.drawVertices(shape, std::span(vertices).first(vertexCount)); } Vertex GPU::getImmediateModeVertex() { diff --git a/src/core/PICA/regs.cpp b/src/core/PICA/regs.cpp index a5e2c6b9..26feaf9d 100644 --- a/src/core/PICA/regs.cpp +++ b/src/core/PICA/regs.cpp @@ -157,7 +157,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) { // If we've reached 3 verts, issue a draw call // Handle rendering depending on the primitive type if (immediateModeVertIndex == 3) { - renderer.drawVertices(OpenGL::Triangle, &immediateModeVertices[0], 3); + renderer.drawVertices(OpenGL::Triangle, immediateModeVertices); switch (primType) { // Triangle or geometry primitive. Draw a triangle and discard all vertices diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 8a87edb7..f41de34b 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -1,8 +1,10 @@ #include #include -#include "kernel.hpp" + #include "arm_defs.hpp" -// This header needs to be included because I did stupid forward decl hack so the kernel and CPU can both access each other +#include "kernel.hpp" +// This header needs to be included because I did stupid forward decl hack so the kernel and CPU can both access each +// other #include "cpu.hpp" #include "resource_limits.hpp" @@ -20,17 +22,17 @@ void Kernel::switchThread(int newThreadIndex) { } // Backup context - std::memcpy(&oldThread.gprs[0], &cpu.regs()[0], 16 * sizeof(u32)); // Backup the 16 GPRs - std::memcpy(&oldThread.fprs[0], &cpu.fprs()[0], 32 * sizeof(u32)); // Backup the 32 FPRs - oldThread.cpsr = cpu.getCPSR(); // Backup CPSR - oldThread.fpscr = cpu.getFPSCR(); // Backup FPSCR + std::memcpy(oldThread.gprs.data(), cpu.regs().data(), cpu.regs().size_bytes()); // Backup the 16 GPRs + std::memcpy(oldThread.fprs.data(), cpu.fprs().data(), cpu.fprs().size_bytes()); // Backup the 32 FPRs + oldThread.cpsr = cpu.getCPSR(); // Backup CPSR + oldThread.fpscr = cpu.getFPSCR(); // Backup FPSCR // Load new context - std::memcpy(&cpu.regs()[0], &newThread.gprs[0], 16 * sizeof(u32)); // Load 16 GPRs - std::memcpy(&cpu.fprs()[0], &newThread.fprs[0], 32 * sizeof(u32)); // Load 32 FPRs - cpu.setCPSR(newThread.cpsr); // Load CPSR - cpu.setFPSCR(newThread.fpscr); // Load FPSCR - cpu.setTLSBase(newThread.tlsBase); // Load CP15 thread-local-storage pointer register + std::memcpy(cpu.regs().data(), newThread.gprs.data(), cpu.regs().size_bytes()); // Load 16 GPRs + std::memcpy(cpu.fprs().data(), newThread.fprs.data(), cpu.fprs().size_bytes()); // Load 32 FPRs + cpu.setCPSR(newThread.cpsr); // Load CPSR + cpu.setFPSCR(newThread.fpscr); // Load FPSCR + cpu.setTLSBase(newThread.tlsBase); // Load CP15 thread-local-storage pointer register currentThreadIndex = newThreadIndex; } diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp index 06e6d505..42cb07d9 100644 --- a/src/core/renderer_gl/renderer_gl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -475,7 +475,10 @@ void Renderer::setupTextureEnvState() { glUniform4f(textureEnvBufferColorLoc, r, g, b, a); } -void Renderer::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count) { +void Renderer::drawVertices(OpenGL::Primitives primType, std::span vertices) { + // TODO: We should implement a GL state tracker that tracks settings like scissor, blending, bound program, etc + // This way if we attempt to eg do multiple glEnable(GL_BLEND) calls in a row, it will say "Oh blending is already enabled" + // And not actually perform the very expensive driver call for it OpenGL::disableScissor(); vbo.bind(); @@ -570,8 +573,8 @@ void Renderer::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 c } } - vbo.bufferVertsSub(vertices, count); - OpenGL::draw(primType, count); + vbo.bufferVertsSub(vertices); + OpenGL::draw(primType, vertices.size()); } constexpr u32 topScreenBuffer = 0x1f000000; diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp index 27b39af9..2b9c5167 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -1,13 +1,15 @@ #include "services/service_manager.hpp" + #include + #include "ipc.hpp" #include "kernel.hpp" -ServiceManager::ServiceManager(std::array& regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel) +ServiceManager::ServiceManager(std::span regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel) : regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem), - cecd(mem, kernel), cfg(mem), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), frd(mem), fs(mem, kernel), - gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mic(mem), nfc(mem, kernel), nim(mem), ndm(mem), - ptm(mem), y2r(mem, kernel) {} + cecd(mem, kernel), cfg(mem), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), frd(mem), fs(mem, kernel), + gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mic(mem), nfc(mem, kernel), nim(mem), ndm(mem), + ptm(mem), y2r(mem, kernel) {} static constexpr int MAX_NOTIFICATION_COUNT = 16;