Migration of shibumi

This commit is contained in:
CocoSimone
2022-06-27 00:17:04 +02:00
parent aeded59dab
commit 92897fe9ce
34 changed files with 853 additions and 52 deletions

6
.gitmodules vendored
View File

@@ -0,0 +1,6 @@
[submodule "external/volk"]
path = external/volk
url = https://github.com/zeux/volk
[submodule "external/parallel-rdp-standalone"]
path = external/parallel-rdp-standalone
url = https://github.com/Themaister/parallel-rdp-standalone

View File

@@ -1,3 +1,61 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
project(parallel-rdp)
include_directories(.) file(GLOB_RECURSE parallel-rdp-cpp parallel-rdp-standalone/parallel-rdp/*.cpp)
add_library(parallel-rdp
${parallel-rdp-cpp}
parallel-rdp-standalone/vulkan/buffer.cpp
parallel-rdp-standalone/vulkan/buffer_pool.cpp
parallel-rdp-standalone/vulkan/command_buffer.cpp
parallel-rdp-standalone/vulkan/command_pool.cpp
parallel-rdp-standalone/vulkan/context.cpp
parallel-rdp-standalone/vulkan/cookie.cpp
parallel-rdp-standalone/vulkan/descriptor_set.cpp
parallel-rdp-standalone/vulkan/device.cpp
parallel-rdp-standalone/vulkan/event_manager.cpp
parallel-rdp-standalone/vulkan/fence.cpp
parallel-rdp-standalone/vulkan/fence_manager.cpp
parallel-rdp-standalone/vulkan/image.cpp
parallel-rdp-standalone/vulkan/memory_allocator.cpp
parallel-rdp-standalone/vulkan/pipeline_event.cpp
parallel-rdp-standalone/vulkan/query_pool.cpp
parallel-rdp-standalone/vulkan/render_pass.cpp
parallel-rdp-standalone/vulkan/sampler.cpp
parallel-rdp-standalone/vulkan/semaphore.cpp
parallel-rdp-standalone/vulkan/semaphore_manager.cpp
parallel-rdp-standalone/vulkan/shader.cpp
parallel-rdp-standalone/vulkan/texture_format.cpp
parallel-rdp-standalone/vulkan/wsi.cpp
parallel-rdp-standalone/vulkan/wsi.hpp
parallel-rdp-standalone/vulkan/wsi_timing.cpp
parallel-rdp-standalone/vulkan/wsi_timing.hpp
parallel-rdp-standalone/util/aligned_alloc.cpp
parallel-rdp-standalone/util/timer.cpp
parallel-rdp-standalone/util/timeline_trace_file.cpp
parallel-rdp-standalone/util/timeline_trace_file.hpp
parallel-rdp-standalone/util/thread_name.cpp
parallel-rdp-standalone/util/thread_name.hpp
parallel-rdp-standalone/util/logging.cpp
parallel-rdp-standalone/util/logging.hpp
parallel-rdp-standalone/util/thread_id.cpp
parallel-rdp-standalone/util/thread_id.hpp
# C
parallel-rdp-standalone/volk/volk.c
)
target_compile_definitions(parallel-rdp PUBLIC GRANITE_VULKAN_MT)
target_include_directories(parallel-rdp PUBLIC
parallel-rdp-standalone/parallel-rdp
parallel-rdp-standalone/volk
parallel-rdp-standalone/spirv-cross
parallel-rdp-standalone/vulkan
parallel-rdp-standalone/vulkan-headers/include
parallel-rdp-standalone/util)
if(WIN32)
target_compile_definitions(parallel-rdp PUBLIC VK_USE_PLATFORM_WIN32_KHR)
else()
target_link_libraries(parallel-rdp dl)
endif()

View File

@@ -2,4 +2,5 @@
namespace natsukashii::core { namespace natsukashii::core {
void BaseCore::Run() {} void BaseCore::Run() {}
void BaseCore::PollInputs(u32 windowID) {}
} }

View File

@@ -1,8 +1,13 @@
#pragma once #pragma once
#include <common.hpp>
namespace natsukashii::core { namespace natsukashii::core {
struct BaseCore { struct BaseCore {
virtual ~BaseCore() {} virtual ~BaseCore() = default;
virtual void Run(); virtual void Run();
virtual void PollInputs(u32);
[[nodiscard]] bool& ShouldQuit() { return quit; }
private:
bool quit = false;
}; };
} }

View File

@@ -12,6 +12,5 @@ add_library(cores
Scheduler.hpp Scheduler.hpp
common.hpp common.hpp
util.hpp) util.hpp)
target_include_directories(cores PRIVATE .) target_include_directories(cores PUBLIC . ../../external)
target_include_directories(cores PUBLIC ../../external)
target_link_libraries(cores PUBLIC gb n64) target_link_libraries(cores PUBLIC gb n64)

View File

@@ -1,4 +1,5 @@
#include <Core.hpp> #include <Core.hpp>
#include <SDL2/SDL_events.h>
namespace natsukashii::gb::core { namespace natsukashii::gb::core {
Core::Core(const std::string& rom) { Core::Core(const std::string& rom) {
@@ -10,4 +11,10 @@ void Core::Run() {
cpu.Step(mem); cpu.Step(mem);
} }
} }
void Core::PollInputs(u32 windowID) {
SDL_Event event;
SDL_PollEvent(&event);
ShouldQuit() = event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == windowID;
}
} }

View File

@@ -10,6 +10,7 @@ struct Core : BaseCore {
~Core() override = default; ~Core() override = default;
explicit Core(const std::string&); explicit Core(const std::string&);
void Run() override; void Run() override;
void PollInputs(u32) override;
private: private:
Mem mem; Mem mem;
Cpu cpu; Cpu cpu;

View File

@@ -18,16 +18,14 @@ FetchContent_Declare(
FetchContent_MakeAvailable(mio toml11) FetchContent_MakeAvailable(mio toml11)
find_package(fmt REQUIRED) find_package(fmt REQUIRED)
add_subdirectory(core)
add_library(n64 add_library(n64
Core.hpp Core.hpp
Core.cpp Core.cpp
Cpu.hpp
Cpu.cpp
Mem.cpp
Mem.hpp
memory_regions.hpp memory_regions.hpp
../BaseCore.cpp ../BaseCore.cpp
../BaseCore.hpp) ../BaseCore.hpp)
target_include_directories(n64 PRIVATE . .. ../../../external) target_include_directories(n64 PRIVATE . ..)
target_include_directories(n64 PUBLIC ${mio_SOURCE_DIR}/include ${toml11_SOURCE_DIR}/include) target_include_directories(n64 PUBLIC ${mio_SOURCE_DIR}/include ${toml11_SOURCE_DIR}/include)
target_link_libraries(n64 PRIVATE mio::mio toml11::toml11 fmt::fmt) target_link_libraries(n64 PRIVATE mio::mio toml11::toml11 fmt::fmt n64-core)

View File

@@ -1,9 +1,18 @@
#include <Core.hpp> #include <Core.hpp>
#include <SDL2/SDL_events.h>
namespace natsukashii::n64::core { namespace natsukashii::n64::core {
Core::Core(const std::string& rom) {} Core::Core(const std::string& rom) {
mem.LoadROM(rom);
}
void Core::Run() { void Core::Run() {
} }
void Core::PollInputs(u32 windowID) {
SDL_Event event;
SDL_PollEvent(&event);
ShouldQuit() = event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == windowID;
}
} }

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <BaseCore.hpp> #include <BaseCore.hpp>
#include <Cpu.hpp> #include "n64/core/Cpu.hpp"
#include <Mem.hpp> #include "n64/core/Mem.hpp"
#include <string> #include <string>
namespace natsukashii::n64::core { namespace natsukashii::n64::core {
@@ -10,5 +10,9 @@ struct Core : BaseCore {
~Core() override = default; ~Core() override = default;
explicit Core(const std::string&); explicit Core(const std::string&);
void Run() override; void Run() override;
void PollInputs(u32) override;
private:
Mem mem;
Cpu cpu;
}; };
} }

View File

@@ -1 +0,0 @@
#include <Cpu.hpp>

View File

@@ -0,0 +1,13 @@
cmake_minimum_required(VERSION 3.20)
project(n64-core)
add_subdirectory(cpu)
add_library(n64-core
Cpu.hpp
Cpu.cpp
Mem.cpp
Mem.hpp RDP.cpp RDP.hpp)
target_include_directories(n64-core PRIVATE . .. ../../)
target_link_libraries(n64-core PUBLIC n64-cpu)

View File

@@ -0,0 +1 @@
#include "Cpu.hpp"

View File

@@ -0,0 +1,8 @@
#pragma once
#include "n64/core/cpu/Registers.hpp"
namespace natsukashii::n64::core {
struct Cpu {
Registers regs;
};
}

View File

@@ -18,13 +18,13 @@ void Mem::LoadROM(const std::string& filename) {
file.seekg(std::ios::end); file.seekg(std::ios::end);
auto size = file.tellg(); auto size = file.tellg();
auto size_adjusted = util::NextPow2(size);
romMask = size_adjusted - 1;
file.seekg(std::ios::beg); file.seekg(std::ios::beg);
std::vector<u8> rom; std::vector<u8> rom;
rom.reserve(size); rom.reserve(size_adjusted);
rom.insert(rom.begin(), file.read(reinterpret_cast<char*>(rom.data()), size);
std::istream_iterator<u8>(file),
std::istream_iterator<u8>());
file.close(); file.close();
util::SwapN64Rom(size, rom.data()); util::SwapN64Rom(size, rom.data());

View File

@@ -1,16 +1,17 @@
#pragma once #pragma once
#include <common.hpp> #include <common.hpp>
#include <memory_regions.hpp> #include <n64/memory_regions.hpp>
#include <vector> #include <vector>
namespace natsukashii::n64::core { namespace natsukashii::n64::core {
struct Mem { struct Mem {
~Mem() = default;
Mem(); Mem();
void LoadROM(const std::string&); void LoadROM(const std::string&);
private: private:
std::vector<u8> cart, rdram, sram; std::vector<u8> cart, rdram, sram;
u8 dmem[DMEM_SIZE]{}, imem[IMEM_SIZE]{}, pif_ram[PIF_RAM_SIZE]{}; u8 dmem[DMEM_SIZE]{}, imem[IMEM_SIZE]{}, pifRam[PIF_RAM_SIZE]{};
u8 pif_bootrom[PIF_BOOTROM_SIZE]{}; u8 pifBootrom[PIF_BOOTROM_SIZE]{};
size_t rom_mask; size_t romMask;
}; };
} }

View File

@@ -0,0 +1 @@
#include <RDP.hpp>

View File

@@ -1,7 +1,8 @@
#pragma once #pragma once
namespace natsukashii::n64::core { namespace natsukashii::n64::core {
struct Cpu {
struct RDP {
}; };
}
} // natsukashii

View File

@@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.20)
project(n64-cpu)
add_library(n64-cpu
Registers.cpp
Registers.hpp
registers/Cop0.cpp
registers/Cop0.hpp)
target_include_directories(n64-cpu PRIVATE . .. ../../ ../../../)
target_include_directories(n64-cpu PUBLIC registers ../../../../../external)

View File

@@ -0,0 +1 @@
#include <Registers.hpp>

View File

@@ -0,0 +1,9 @@
#pragma once
#include <Cop0.hpp>
namespace natsukashii::n64::core {
struct Registers {
s64 gpr[32];
Cop0 cop0;
};
}

View File

@@ -0,0 +1,17 @@
#include <Cop0.hpp>
namespace natsukashii::n64::core {
Cop0::Cop0() {
}
template<class T>
T Cop0::GetReg(u8 index) {
}
template<class T>
void Cop0::SetReg(u8 index, T val) {
}
}

View File

@@ -0,0 +1,168 @@
#pragma once
#include <common.hpp>
namespace natsukashii::n64::core {
#define STATUS_MASK 0xFF77FFFF
struct Cpu;
struct Mem;
union Cop0Cause {
u32 raw;
struct {
unsigned: 8;
unsigned interruptPending: 8;
unsigned: 16;
} __attribute__((__packed__));
struct {
unsigned: 2;
unsigned exceptionCode: 5;
unsigned: 1;
unsigned ip0: 1;
unsigned ip1: 1;
unsigned ip2: 1;
unsigned ip3: 1;
unsigned ip4: 1;
unsigned ip5: 1;
unsigned ip6: 1;
unsigned ip7: 1;
unsigned: 12;
unsigned copError: 2;
unsigned: 1;
unsigned branchDelay: 1;
} __attribute__((__packed__));
};
union Cop0Status {
struct {
unsigned ie: 1;
unsigned exl: 1;
unsigned erl: 1;
unsigned ksu: 2;
unsigned ux: 1;
unsigned sx: 1;
unsigned kx: 1;
unsigned im: 8;
unsigned ds: 9;
unsigned re: 1;
unsigned fr: 1;
unsigned rp: 1;
unsigned cu0: 1;
unsigned cu1: 1;
unsigned cu2: 1;
unsigned cu3: 1;
} __attribute__((__packed__));
struct {
unsigned: 16;
unsigned de: 1;
unsigned ce: 1;
unsigned ch: 1;
unsigned: 1;
unsigned sr: 1;
unsigned ts: 1;
unsigned bev: 1;
unsigned: 1;
unsigned its: 1;
unsigned: 7;
} __attribute__((__packed__));
u32 raw;
} __attribute__((__packed__));
union EntryLo {
u32 raw;
struct {
unsigned g: 1;
unsigned v: 1;
unsigned d: 1;
unsigned c: 3;
unsigned pfn: 20;
unsigned: 6;
};
};
union EntryHi {
u64 raw;
struct {
u64 asid: 8;
u64: 5;
u64 vpn2: 27;
u64 fill: 22;
u64 r: 2;
} __attribute__((__packed__));
};
union PageMask {
u32 raw;
struct {
unsigned: 13;
unsigned mask: 12;
unsigned: 7;
};
};
struct TLBEntry {
EntryLo entryLo0, entryLo1;
EntryHi entryHi;
PageMask pageMask;
};
enum TLBError : u8 {
NONE,
MISS,
INVALID,
MODIFICATION,
DISALLOWED_ADDRESS
};
enum TLBAccessType {
LOAD, STORE
};
union Context {
u64 raw;
struct {
u64: 4;
u64 badvpn2: 19;
u64 ptebase: 41;
};
};
union XContext {
u64 raw;
struct {
u64: 4;
u64 badvpn2: 27;
u64 r: 2;
u64 ptebase: 31;
} __attribute__((__packed__));
};
struct Cop0 {
Cop0();
template<class T>
T GetReg(u8 index);
template<class T>
void SetReg(u8 index, T val);
PageMask pageMask;
EntryHi entryHi;
EntryLo entryLo0, entryLo1;
u32 index, random;
Context context;
u32 wired, r7;
u64 badVaddr, count;
u32 compare;
Cop0Status status;
Cop0Cause cause;
u64 EPC;
u32 PRId, Config, LLAddr, WatchLo, WatchHi;
XContext xcontext;
u32 r21, r22, r23, r24, r25, ParityError, CacheError, TagLo, TagHi;
u64 ErrorEPC;
u32 r31;
TLBEntry tlb[32];
TLBError tlbError;
};
}

View File

@@ -59,7 +59,7 @@ inline T ReadAccess(u8* data, u32 index) {
static_assert(sizeof(T) != 2 && sizeof(T) != 4 && sizeof(T) != 8); static_assert(sizeof(T) != 2 && sizeof(T) != 4 && sizeof(T) != 8);
T result = 0; T result = 0;
memcpy(&result, &data[index], sizeof(T)); memcpy(&result, &data[index], sizeof(T));
return GetSwapFunc(result); return GetSwapFunc<T>(result);
} }
template <typename T> template <typename T>
@@ -95,4 +95,16 @@ inline void SwapN64Rom(size_t size, u8* data) {
panic("Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!\n"); panic("Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!\n");
} }
} }
inline size_t NextPow2(size_t num) {
// Taken from "Bit Twiddling Hacks" by Sean Anderson:
// https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
--num;
num |= num >> 1;
num |= num >> 2;
num |= num >> 4;
num |= num >> 8;
num |= num >> 16;
return num + 1;
}
} }

View File

@@ -11,5 +11,5 @@ find_package(Qt5 COMPONENTS Widgets REQUIRED)
add_executable(natsukashii-qt Frontend.hpp Frontend.cpp main.cpp) add_executable(natsukashii-qt Frontend.hpp Frontend.cpp main.cpp)
target_include_directories(natsukashii-qt PRIVATE . ../../core ../../core/gb ../../core/n64 ../../../external) target_include_directories(natsukashii-qt PRIVATE . ../../core ../../core/gb ../../core/n64)
target_link_libraries(natsukashii-qt PRIVATE cores Qt5::Widgets) target_link_libraries(natsukashii-qt PRIVATE cores Qt5::Widgets)

View File

@@ -1,7 +1,112 @@
#include <Frontend.hpp> #include <Frontend.hpp>
#include <QPainter>
namespace natsukashii::frontend { namespace natsukashii::frontend {
App::~App() {} QVulkanWindowRenderer *VulkanWindow::createRenderer() {
return new VulkanRenderer(this);
App::App() {} }
VulkanRenderer::VulkanRenderer(QVulkanWindow *w)
: m_window(w) { }
void VulkanRenderer::initResources() {
m_devFuncs = m_window->vulkanInstance()->deviceFunctions(m_window->device());
}
void VulkanRenderer::startNextFrame() {
VkClearColorValue clearColor = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
VkClearDepthStencilValue clearDS = { 1.0f, 0 };
VkClearValue clearValues[2];
memset(clearValues, 0, sizeof(clearValues));
clearValues[0].color = clearColor;
clearValues[1].depthStencil = clearDS;
VkRenderPassBeginInfo rpBeginInfo;
memset(&rpBeginInfo, 0, sizeof(rpBeginInfo));
rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
rpBeginInfo.renderPass = m_window->defaultRenderPass();
rpBeginInfo.framebuffer = m_window->currentFramebuffer();
const QSize sz = m_window->swapChainImageSize();
rpBeginInfo.renderArea.extent.width = sz.width();
rpBeginInfo.renderArea.extent.height = sz.height();
rpBeginInfo.clearValueCount = 2;
rpBeginInfo.pClearValues = clearValues;
VkCommandBuffer cmdBuf = m_window->currentCommandBuffer();
m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
// do thing
m_devFuncs->vkCmdEndRenderPass(cmdBuf);
m_window->frameReady();
m_window->requestUpdate(); // render continuously, throttled by the presentation rate
}
OpenGLWindow::OpenGLWindow(QWindow *parent) : QWindow(parent) {
setSurfaceType(QWindow::OpenGLSurface);
}
void OpenGLWindow::render(QPainter *painter) {
Q_UNUSED(painter);
}
void OpenGLWindow::initialize() {}
void OpenGLWindow::render() {
if (!m_device)
m_device = new QOpenGLPaintDevice;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
m_device->setSize(size() * devicePixelRatio());
m_device->setDevicePixelRatio(devicePixelRatio());
QPainter painter(m_device);
render(&painter);
}
bool OpenGLWindow::event(QEvent *event) {
switch (event->type()) {
case QEvent::UpdateRequest:
renderNow();
return true;
default:
return QWindow::event(event);
}
}
void OpenGLWindow::exposeEvent(QExposeEvent *event) {
Q_UNUSED(event);
if (isExposed())
renderNow();
}
void OpenGLWindow::renderNow() {
if (!isExposed())
return;
bool needsInitialize = false;
if (!m_context) {
m_context = new QOpenGLContext(this);
m_context->setFormat(requestedFormat());
m_context->create();
needsInitialize = true;
}
m_context->makeCurrent(this);
if (needsInitialize) {
initializeOpenGLFunctions();
initialize();
}
render();
m_context->swapBuffers(this);
}
} }

View File

@@ -1,8 +1,63 @@
#pragma once #pragma once
#include <QVulkanWindow>
#include <QVulkanWindowRenderer>
#include <QVulkanDeviceFunctions>
#include <QOpenGLWindow>
#include <QOpenGLFunctions>
#include <QOpenGLPaintDevice>
namespace natsukashii::frontend { namespace natsukashii::frontend {
struct App { class VulkanRenderer : public QVulkanWindowRenderer {
~App(); public:
App(); VulkanRenderer(QVulkanWindow *w);
void Run() {}
void initResources() override;
void initSwapChainResources() override;
void releaseSwapChainResources() override;
void releaseResources() override;
void startNextFrame() override;
private:
QVulkanWindow *m_window;
QVulkanDeviceFunctions *m_devFuncs;
};
class VulkanWindow : public QVulkanWindow {
public:
QVulkanWindowRenderer *createRenderer() override;
};
class OpenGLWindow : protected QOpenGLFunctions, public QWindow {
Q_OBJECT
public:
explicit OpenGLWindow(QWindow *parent = nullptr);
~OpenGLWindow();
void render(QPainter *painter);
void render();
void initialize();
void setAnimating(bool animating);
public slots:
void renderNow();
protected:
bool event(QEvent *event) override;
void exposeEvent(QExposeEvent *event) override;
private:
QOpenGLContext *m_context = nullptr;
QOpenGLPaintDevice *m_device = nullptr;
};
class Window : public QWindow {
public:
Window() {}
private:
VulkanWindow vulkanWindow;
OpenGLWindow openGlWindow;
}; };
} }

View File

@@ -1,8 +1,10 @@
#include <Frontend.hpp> #include <Frontend.hpp>
#include <util.hpp> #include <QGuiApplication>
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
natsukashii::frontend::App app; QGuiApplication app(argc, argv);
app.Run(); natsukashii::frontend::Window window;
return 0; window.show();
return app.exec();
} }

View File

@@ -5,7 +5,7 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(SDL2 REQUIRED) find_package(SDL2 REQUIRED)
add_executable(natsukashii-sdl Frontend.cpp Frontend.hpp main.cpp) add_executable(natsukashii-sdl Frontend.cpp Frontend.hpp main.cpp ParallelRDPWrapper.cpp ParallelRDPWrapper.hpp)
include(FetchContent) include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
@@ -15,5 +15,7 @@ FetchContent_Declare(
) )
FetchContent_MakeAvailable(argparse) FetchContent_MakeAvailable(argparse)
target_include_directories(natsukashii-sdl PRIVATE . ../../core ../../core/gb ../../core/n64 ../../../external) add_subdirectory(../../../external temp)
target_link_libraries(natsukashii-sdl PRIVATE cores argparse::argparse SDL2)
target_include_directories(natsukashii-sdl PRIVATE . ../../core ../../core/gb ../../core/n64 ../../core/n64/core ../../core/n64/core/cpu/registers)
target_link_libraries(natsukashii-sdl PRIVATE cores argparse::argparse SDL2 parallel-rdp)

View File

@@ -1,6 +1,7 @@
#include <Frontend.hpp> #include <Frontend.hpp>
#include <gb/Core.hpp> #include <gb/Core.hpp>
#include <n64/Core.hpp> #include <n64/Core.hpp>
#include <volk.h>
namespace natsukashii::frontend { namespace natsukashii::frontend {
using namespace natsukashii; using namespace natsukashii;
@@ -11,14 +12,20 @@ App::~App() {
} }
App::App(const std::string& rom, const std::string& selectedCore) { App::App(const std::string& rom, const std::string& selectedCore) {
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO); SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_VIDEO_VULKAN);
window = SDL_CreateWindow("natukashii", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_SHOWN);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
id = SDL_GetWindowID(window);
if(selectedCore == "gb") { if(selectedCore == "gb") {
window = SDL_CreateWindow("natukashii", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_RESIZABLE);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
SDL_RenderSetLogicalSize(renderer, 160, 144);
id = SDL_GetWindowID(window);
core = std::make_unique<gb::core::Core>(rom); core = std::make_unique<gb::core::Core>(rom);
} else if(selectedCore == "n64") { } else if(selectedCore == "n64") {
window = SDL_CreateWindow("natukashii", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_RESIZABLE | SDL_WINDOW_VULKAN);
id = SDL_GetWindowID(window);
if(volkInitialize() != VK_SUCCESS) {
util::panic("Failed to initialize Volk\n");
}
core = std::make_unique<n64::core::Core>(rom); core = std::make_unique<n64::core::Core>(rom);
} else { } else {
util::panic("Unimplemented core!"); util::panic("Unimplemented core!");
@@ -26,12 +33,9 @@ App::App(const std::string& rom, const std::string& selectedCore) {
} }
void App::Run() { void App::Run() {
while(!quit) { while(!core->ShouldQuit()) {
core->Run(); core->Run();
core->PollInputs(id);
SDL_Event event;
SDL_PollEvent(&event);
quit = event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == id;
} }
} }
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#define SDL_MAIN_HANDLED #define SDL_MAIN_HANDLED
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
#include <SDL2/SDL_vulkan.h>
#include <BaseCore.hpp> #include <BaseCore.hpp>
#include <string> #include <string>
#include <memory> #include <memory>
@@ -8,6 +9,7 @@
namespace natsukashii::frontend { namespace natsukashii::frontend {
using namespace natsukashii::core; using namespace natsukashii::core;
struct App { struct App {
~App(); ~App();
App(const std::string&, const std::string&); App(const std::string&, const std::string&);
@@ -15,8 +17,7 @@ struct App {
private: private:
SDL_Window *window = nullptr; SDL_Window *window = nullptr;
SDL_Renderer *renderer = nullptr; SDL_Renderer *renderer = nullptr;
Uint32 id; u32 id;
bool quit = false;
std::unique_ptr<BaseCore> core; std::unique_ptr<BaseCore> core;
}; };
} }

View File

@@ -0,0 +1,282 @@
#include <ParallelRDPWrapper.hpp>
#include <RDP.hpp>
#include <memory>
#include <rdp_device.hpp>
#include <SDL2/SDL_video.h>
#include <SDL2/SDL_vulkan.h>
#include <util.hpp>
using namespace Vulkan;
using namespace natsukashii;
using std::unique_ptr;
using RDP::CommandProcessor;
using RDP::CommandProcessorFlags;
using RDP::VIRegister;
static CommandProcessor* command_processor;
static WSI* wsi;
std::vector<Semaphore> acquire_semaphore;
VkQueue GetGraphicsQueue() {
return wsi->get_context().get_graphics_queue();
}
VkInstance GetVkInstance() {
return wsi->get_context().get_instance();
}
VkPhysicalDevice GetVkPhysicalDevice() {
return wsi->get_device().get_physical_device();
}
VkDevice GetVkDevice() {
return wsi->get_device().get_device();
}
uint32_t GetVkGraphicsQueueFamily() {
return wsi->get_context().get_graphics_queue_family();
}
VkFormat GetVkFormat() {
return wsi->get_device().get_swapchain_view().get_format();
}
CommandBufferHandle requested_command_buffer;
VkCommandBuffer GetVkCommandBuffer() {
requested_command_buffer = wsi->get_device().request_command_buffer();
return requested_command_buffer->get_command_buffer();
}
void SubmitRequestedVkCommandBuffer() {
wsi->get_device().submit(requested_command_buffer);
}
bool IsFramerateUnlocked() {
return wsi->get_present_mode() != PresentMode::SyncToVBlank;
}
void SetFramerateUnlocked(bool unlocked) {
if (unlocked) {
wsi->set_present_mode(PresentMode::UnlockedForceTearing);
} else {
wsi->set_present_mode(PresentMode::SyncToVBlank);
}
}
class SDLWSIPlatform : public Vulkan::WSIPlatform {
public:
SDLWSIPlatform() = default;
std::vector<const char *> get_instance_extensions() override {
const char* extensions[64];
unsigned int num_extensions = 64;
if (!SDL_Vulkan_GetInstanceExtensions(window, &num_extensions, extensions)) {
util::panic("SDL_Vulkan_GetInstanceExtensions failed: %s", SDL_GetError());
}
auto vec = std::vector<const char*>();
for (unsigned int i = 0; i < num_extensions; i++) {
vec.push_back(extensions[i]);
}
return vec;
}
VkSurfaceKHR create_surface(VkInstance instance, VkPhysicalDevice gpu) override {
VkSurfaceKHR vk_surface;
if (!SDL_Vulkan_CreateSurface(window, instance, &vk_surface)) {
util::panic("Failed to create Vulkan window surface: %s", SDL_GetError());
}
return vk_surface;
}
uint32_t get_surface_width() override {
return N64_SCREEN_X * SCREEN_SCALE;
}
uint32_t get_surface_height() override {
return N64_SCREEN_Y * SCREEN_SCALE;
}
bool alive(Vulkan::WSI &wsi) override {
return true;
}
void poll_input() override {
n64_poll_input();
}
void event_frame_tick(double frame, double elapsed) override {
n64_render_screen();
}
};
Program* fullscreen_quad_program;
void LoadParallelRDP(const u8* rdram) {
wsi = new WSI();
wsi->set_backbuffer_srgb(false);
wsi->set_platform(new SDLWSIPlatform());
Context::SystemHandles handles;
if (!wsi->init(1, handles)) {
util::panic("Failed to initialize WSI!");
}
ResourceLayout vert_layout;
ResourceLayout frag_layout;
vert_layout.input_mask = 1;
vert_layout.output_mask = 1;
frag_layout.input_mask = 1;
frag_layout.output_mask = 1;
frag_layout.spec_constant_mask = 1;
frag_layout.push_constant_size = 4 * sizeof(float);
frag_layout.sets[0].sampled_image_mask = 1;
frag_layout.sets[0].fp_mask = 1;
frag_layout.sets[0].array_size[0] = 1;
fullscreen_quad_program = wsi->get_device().request_program(fullscreen_quad_vert, sizeof(fullscreen_quad_vert), fullscreen_quad_frag, sizeof(fullscreen_quad_frag), &vert_layout, &frag_layout);
auto aligned_rdram = reinterpret_cast<uintptr_t>(rdram);
uintptr_t offset = 0;
if (wsi->get_device().get_device_features().supports_external_memory_host)
{
size_t align = wsi->get_device().get_device_features().host_memory_properties.minImportedHostPointerAlignment;
offset = aligned_rdram & (align - 1);
aligned_rdram -= offset;
}
CommandProcessorFlags flags = 1 << 1; // TODO configurable scaling
command_processor = new CommandProcessor(wsi->get_device(), reinterpret_cast<void *>(aligned_rdram),
offset, 8 * 1024 * 1024, 4 * 1024 * 1024, flags);
if (!command_processor->device_is_supported()) {
util::panic("This device probably does not support 8/16-bit storage. Make sure you're using up-to-date drivers!");
}
}
void draw_fullscreen_textured_quad(Util::IntrusivePtr<Image> image, Util::IntrusivePtr<CommandBuffer> cmd) {
cmd->set_texture(0, 0, image->get_view(), Vulkan::StockSampler::LinearClamp);
cmd->set_program(fullscreen_quad_program);
cmd->set_quad_state();
auto data = static_cast<float*>(cmd->allocate_vertex_data(0, 6 * sizeof(float), 2 * sizeof(float)));
*data++ = -1.0f;
*data++ = -3.0f;
*data++ = -1.0f;
*data++ = +1.0f;
*data++ = +3.0f;
*data++ = +1.0f;
int sdlWinWidth, sdlWinHeight;
SDL_GetWindowSize(window, &sdlWinWidth, &sdlWinHeight);
float zoom = std::min(
(float)sdlWinWidth / wsi->get_platform().get_surface_width(),
(float)sdlWinHeight / wsi->get_platform().get_surface_height());
float width = (wsi->get_platform().get_surface_width() / (float)sdlWinWidth) * zoom;
float height = (wsi->get_platform().get_surface_height() / (float)sdlWinHeight) * zoom;
float uniform_data[] = {
// Size
width, height,
// Offset
(1.0f - width) * 0.5f,
(1.0f - height) * 0.5f};
cmd->push_constants(uniform_data, 0, sizeof(uniform_data));
cmd->set_vertex_attrib(0, 0, VK_FORMAT_R32G32_SFLOAT, 0);
cmd->set_depth_test(false, false);
cmd->set_depth_compare(VK_COMPARE_OP_ALWAYS);
cmd->set_primitive_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
cmd->draw(3, 1);
}
void update_screen(Util::IntrusivePtr<Image> image) {
wsi->begin_frame();
if (!image) {
auto info = Vulkan::ImageCreateInfo::immutable_2d_image(N64_SCREEN_X * SCREEN_SCALE, N64_SCREEN_Y * SCREEN_SCALE, VK_FORMAT_R8G8B8A8_UNORM);
info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
VK_IMAGE_USAGE_TRANSFER_DST_BIT;
info.misc = IMAGE_MISC_MUTABLE_SRGB_BIT;
info.initial_layout = VK_IMAGE_LAYOUT_UNDEFINED;
image = wsi->get_device().create_image(info);
auto cmd = wsi->get_device().request_command_buffer();
cmd->image_barrier(*image,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT);
cmd->clear_image(*image, {});
cmd->image_barrier(*image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_ACCESS_SHADER_READ_BIT);
wsi->get_device().submit(cmd);
}
Util::IntrusivePtr<CommandBuffer> cmd = wsi->get_device().request_command_buffer();
cmd->begin_render_pass(wsi->get_device().get_swapchain_render_pass(SwapchainRenderPass::ColorOnly));
draw_fullscreen_textured_quad(image, cmd);
ImGui_ImplVulkan_RenderDrawData(imgui_frame(), cmd->get_command_buffer());
cmd->end_render_pass();
wsi->get_device().submit(cmd);
wsi->end_frame();
}
void update_screen_parallel_rdp() {
if (unlikely(!command_processor)) {
logfatal("Update screen without an initialized command processor");
}
command_processor->set_vi_register(VIRegister::Control, n64sys.vi.status.raw);
command_processor->set_vi_register(VIRegister::Origin, n64sys.vi.vi_origin);
command_processor->set_vi_register(VIRegister::Width, n64sys.vi.vi_width);
command_processor->set_vi_register(VIRegister::Intr, n64sys.vi.vi_v_intr);
command_processor->set_vi_register(VIRegister::VCurrentLine, n64sys.vi.v_current);
command_processor->set_vi_register(VIRegister::Timing, n64sys.vi.vi_burst.raw);
command_processor->set_vi_register(VIRegister::VSync, n64sys.vi.vsync);
command_processor->set_vi_register(VIRegister::HSync, n64sys.vi.hsync);
command_processor->set_vi_register(VIRegister::Leap, n64sys.vi.leap);
command_processor->set_vi_register(VIRegister::HStart, n64sys.vi.hstart.raw);
command_processor->set_vi_register(VIRegister::VStart, n64sys.vi.vstart.raw);
command_processor->set_vi_register(VIRegister::VBurst, n64sys.vi.vburst);
command_processor->set_vi_register(VIRegister::XScale, n64sys.vi.xscale.raw);
command_processor->set_vi_register(VIRegister::YScale, n64sys.vi.yscale.raw);
RDP::ScanoutOptions opts;
opts.persist_frame_on_invalid_input = true;
opts.vi.aa = true;
opts.vi.scale = true;
opts.vi.dither_filter = true;
opts.vi.divot_filter = true;
opts.vi.gamma_dither = true;
opts.downscale_steps = true;
opts.crop_overscan_pixels = true;
Util::IntrusivePtr<Image> image = command_processor->scanout(opts);
update_screen(image);
command_processor->begin_frame_context();
}
void update_screen_parallel_rdp_no_game() {
update_screen(static_cast<Util::IntrusivePtr<Image>>(nullptr));
}
void parallel_rdp_enqueue_command(int command_length, word* buffer) {
command_processor->enqueue_command(command_length, buffer);
}
void parallel_rdp_on_full_sync() {
command_processor->wait_for_timeline(command_processor->signal_timeline());
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include <n64/Core.hpp>
#include <wsi.hpp>
VkQueue GetGraphicsQueue();
VkInstance GetVkInstance();
VkPhysicalDevice GetVkPhysicalDevice();
VkDevice GetVkDevice();
uint32_t GetVkGraphicsQueueFamily();
VkFormat GetVkFormat();
VkCommandBuffer GetVkCommandBuffer();
void SubmitRequestedVkCommandBuffer();
void LoadParallelRdp();
void UpdateScreenParallelRdp();
void ParallelRdpEnqueueCommand(int command_length, u32* buffer);
void ParallelRdpOnFullSync();
void UpdateScreenParallelRdpNoGame();
bool IsFramerateUnlocked();
void SetFramerateUnlocked(bool unlocked);