ParallelRDP integration

This commit is contained in:
CocoSimone
2022-06-29 23:06:58 +02:00
parent 0ea5b8f2fc
commit b2beeaf2a5
21 changed files with 479 additions and 61 deletions

20
external/shader.frag vendored Normal file
View File

@@ -0,0 +1,20 @@
#version 450
layout(location = 0) in vec2 vUV;
layout(location = 0) out vec4 FragColor;
layout(set = 0, binding = 0) uniform sampler2D uImage;
layout(push_constant) uniform Screen {
vec2 size;
vec2 offset;
} uScreen;
layout(constant_id = 0) const float Scale = 1.0;
void main() {
vec2 uv = (vUV - uScreen.offset) / uScreen.size;
if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {
FragColor = vec4(0, 0, 0, 1);
} else {
FragColor = Scale * textureLod(uImage, uv, 0.0);
}
}

8
external/shader.vert vendored Normal file
View File

@@ -0,0 +1,8 @@
#version 450
layout(location = 0) in vec2 Position;
layout(location = 0) out highp vec2 vUV;
void main() {
gl_Position = vec4(Position, 0.0, 1.0);
vUV = 0.5 * Position + 0.5;
}

View File

@@ -24,3 +24,6 @@ using m128 = __m128i;
#define KiB * 1024
#define MiB (KiB * 1024)
#define GiB (MiB * 1024)
#define N64_CPU_FREQ 93750000
#define N64_CYCLES_PER_FRAME (N64_CPU_FREQ / 60)

View File

@@ -20,12 +20,19 @@ find_package(fmt REQUIRED)
add_subdirectory(core)
set(PARALLELRDP_INCLUDES
../../../external/parallel-rdp-standalone/vulkan
../../../external/parallel-rdp-standalone/util
../../../external/parallel-rdp-standalone/parallel-rdp
../../../external/parallel-rdp-standalone/volk
../../../external/parallel-rdp-standalone/vulkan-headers)
add_library(n64
Core.hpp
Core.cpp
memory_regions.hpp
../BaseCore.cpp
../BaseCore.hpp)
target_include_directories(n64 PRIVATE . ..)
target_include_directories(n64 PRIVATE . .. ../../frontend/sdl ${PARALLELRDP_INCLUDES})
target_include_directories(n64 PUBLIC ${mio_SOURCE_DIR}/include ${toml11_SOURCE_DIR}/include)
target_link_libraries(n64 PRIVATE mio::mio toml11::toml11 fmt::fmt n64-core)

View File

@@ -1,9 +1,11 @@
#include <Core.hpp>
#include <SDL2/SDL_events.h>
#include <ParallelRDPWrapper.hpp>
namespace natsukashii::n64::core {
Core::Core(const std::string& rom) {
mem.LoadROM(rom);
LoadParallelRDP(mem.GetRDRAM());
}
void Core::Run() {

View File

@@ -1,7 +1,7 @@
#pragma once
#include <BaseCore.hpp>
#include "n64/core/Cpu.hpp"
#include "n64/core/Mem.hpp"
#include <n64/core/Cpu.hpp>
#include <n64/core/Mem.hpp>
#include <string>
namespace natsukashii::n64::core {

View File

@@ -7,7 +7,11 @@ add_library(n64-core
Cpu.hpp
Cpu.cpp
Mem.cpp
Mem.hpp RDP.cpp RDP.hpp)
Mem.hpp
RDP.cpp
RDP.hpp
mmio/VI.cpp
mmio/VI.hpp mmio/Interrupt.hpp mmio/MI.cpp mmio/MI.hpp mmio/Interrupt.cpp)
target_include_directories(n64-core PRIVATE . .. ../../)
target_include_directories(n64-core PRIVATE . .. ../../ mmio)
target_link_libraries(n64-core PUBLIC n64-cpu)

View File

@@ -1 +1 @@
#include "Cpu.hpp"
#include <Cpu.hpp>

View File

@@ -1,5 +1,5 @@
#pragma once
#include "n64/core/cpu/Registers.hpp"
#include <n64/core/cpu/Registers.hpp>
namespace natsukashii::n64::core {
struct Cpu {

View File

@@ -8,6 +8,9 @@ struct Mem {
~Mem() = default;
Mem();
void LoadROM(const std::string&);
[[nodiscard]] auto GetRDRAM() const -> const u8* {
return rdram.data();
}
private:
std::vector<u8> cart, rdram, sram;
u8 dmem[DMEM_SIZE]{}, imem[IMEM_SIZE]{}, pifRam[PIF_RAM_SIZE]{};

View File

@@ -0,0 +1,58 @@
#include <Interrupt.hpp>
#include <MI.hpp>
#include <n64/core/cpu/Registers.hpp>
namespace natsukashii::n64::core {
void InterruptRaise(MI &mi, Registers &regs, InterruptType intr) {
switch(intr) {
case InterruptType::VI:
mi.miIntr.vi = true;
break;
case InterruptType::SI:
mi.miIntr.si = true;
break;
case InterruptType::PI:
mi.miIntr.pi = true;
break;
case InterruptType::AI:
mi.miIntr.ai = true;
break;
case InterruptType::DP:
mi.miIntr.dp = true;
break;
case InterruptType::SP:
mi.miIntr.sp = true;
break;
}
UpdateInterrupt(mi, regs);
}
void InterruptLower(MI &mi, Registers &regs, InterruptType intr) {
switch(intr) {
case InterruptType::VI:
mi.miIntr.vi = false;
break;
case InterruptType::SI:
mi.miIntr.si = false;
break;
case InterruptType::PI:
mi.miIntr.pi = false;
break;
case InterruptType::AI:
mi.miIntr.ai = false;
break;
case InterruptType::DP:
mi.miIntr.dp = false;
break;
case InterruptType::SP:
mi.miIntr.sp = false;
break;
}
UpdateInterrupt(mi, regs);
}
void UpdateInterrupt(MI &mi, Registers &regs) {
bool interrupt = mi.miIntr.raw & mi.miIntrMask.raw;
regs.cop0.cause.ip2 = interrupt;
}
}

View File

@@ -0,0 +1,16 @@
#pragma once
#include <common.hpp>
#include <MI.hpp>
namespace natsukashii::n64::core {
struct Registers;
enum class InterruptType : u8 {
VI, SI, PI, AI, DP, SP
};
void InterruptRaise(MI &mi, Registers &regs, InterruptType intr);
void InterruptLower(MI &mi, Registers &regs, InterruptType intr);
void UpdateInterrupt(MI &mi, Registers &regs);
}

View File

@@ -0,0 +1,79 @@
#include <MI.hpp>
#include <n64/core/cpu/Registers.hpp>
#include <util.hpp>
#include <Interrupt.hpp>
#define MI_VERSION_REG 0x02020102
namespace natsukashii::n64::core {
MI::MI() {
miIntrMask.raw = 0;
miIntr.raw = 0;
miMode = 0;
}
u32 MI::Read(u32 paddr) {
switch(paddr & 0xF) {
case 0x0: return miMode & 0x3FF;
case 0x4: return MI_VERSION_REG;
case 0x8: return miIntr.raw & 0x3F;
case 0xC: return miIntrMask.raw & 0x3F;
default: util::panic("Unhandled MI[%08X] read\n", paddr);
}
}
void MI::Write(Registers& regs, u32 paddr, u32 val) {
switch(paddr & 0xF) {
case 0x0:
miMode &= 0xFFFFFF80;
miMode |= val & 0x7F;
if (val & (1 << 7)) {
miMode &= ~(1 << 7);
}
if (val & (1 << 8)) {
miMode |= 1 << 7;
}
if (val & (1 << 9)) {
miMode &= ~(1 << 8);
}
if (val & (1 << 10)) {
miMode |= 1 << 8;
}
if (val & (1 << 11)) {
InterruptLower(*this, regs, InterruptType::DP);
}
if (val & (1 << 12)) {
miMode &= ~(1 << 9);
}
if (val & (1 << 13)) {
miMode |= 1 << 9;
}
break;
case 0x4: break;
case 0xC:
for (int bit = 0; bit < 6; bit++) {
int clearbit = bit << 1;
int setbit = (bit << 1) + 1;
if (val & (1 << clearbit)) {
miIntrMask.raw &= ~(1 << bit);
}
if (val & (1 << setbit)) {
miIntrMask.raw |= 1 << bit;
}
}
UpdateInterrupt(*this, regs);
break;
default:
util::panic("Unhandled MI[%08X] write (%08X)\n", val, paddr);
}
}
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include <common.hpp>
namespace natsukashii::n64::core {
union MIIntr {
struct {
unsigned sp: 1;
unsigned si: 1;
unsigned ai: 1;
unsigned vi: 1;
unsigned pi: 1;
unsigned dp: 1;
unsigned: 26;
};
u32 raw;
};
struct Registers;
struct MI {
MI();
u32 Read(u32);
void Write(Registers& regs, u32, u32);
u32 miMode;
MIIntr miIntr{}, miIntrMask{};
};
}

View File

@@ -0,0 +1,83 @@
#include <VI.hpp>
#include <util.hpp>
#include <n64/core/cpu/Registers.hpp>
#include <MI.hpp>
#include <n64/core/mmio/Interrupt.hpp>
namespace natsukashii::n64::core {
VI::VI () {
status.raw = 0xF;
intr = 256;
origin = 0;
width = 320;
current = 0;
vsync = 0;
hsync = 0;
numHalflines = 262;
numFields = 1;
cyclesPerHalfline = 1000;
}
u32 VI::Read(u32 paddr) {
switch(paddr) {
case 0x04400000: return status.raw;
case 0x04400004: return origin;
case 0x04400008: return width;
case 0x0440000C: return intr;
case 0x04400010: return current << 1;
case 0x04400014: return burst.raw;
case 0x04400018: return vsync;
case 0x0440001C: return hsync;
case 0x04400020: return hsyncLeap.raw;
case 0x04400024: return hvideo.raw;
case 0x04400028: return vvideo.raw;
case 0x0440002C: return vburst.raw;
case 0x04400030: return xscale.raw;
case 0x04400034: return yscale.raw;
default:
util::panic("Unimplemented VI[%08X] read\n", paddr);
}
}
void VI::Write(MI& mi, Registers& regs, u32 paddr, u32 val) {
switch(paddr) {
case 0x04400000:
status.raw = val;
numFields = status.serrate ? 2 : 1;
break;
case 0x04400004: {
u32 masked = val & 0xFFFFFF;
if(origin != masked) {
swaps++;
}
origin = masked;
} break;
case 0x04400008: {
width = val & 0x7FF;
} break;
case 0x0440000C: {
intr = val & 0x3FF;
} break;
case 0x04400010:
InterruptLower(mi, regs, InterruptType::VI);
break;
case 0x04400014: burst.raw = val; break;
case 0x04400018: {
vsync = val & 0x3FF;
numHalflines = vsync >> 1;
cyclesPerHalfline = N64_CYCLES_PER_FRAME / numHalflines;
} break;
case 0x0440001C: {
hsync = val & 0x3FF;
} break;
case 0x04400020: hsyncLeap.raw = val; break;
case 0x04400024: hvideo.raw = val; break;
case 0x04400028: vvideo.raw = val; break;
case 0x0440002C: vburst.raw = val; break;
case 0x04400030: xscale.raw = val; break;
case 0x04400034: yscale.raw = val; break;
default:
util::panic("Unimplemented VI[%08X] write (%08X)\n", paddr, val);
}
}
}

View File

@@ -0,0 +1,83 @@
#pragma once
#include <common.hpp>
namespace natsukashii::n64::core {
union VIBurst {
struct {
unsigned hsyncW:8;
unsigned burstW:8;
unsigned vsyncW:4;
unsigned burstStart:10;
unsigned:2;
};
u32 raw;
};
union VIHsyncLeap {
struct {
unsigned leapB:10;
unsigned:6;
unsigned leapA:10;
unsigned:6;
};
u32 raw;
} ;
union VIVideo {
struct {
unsigned end:10;
unsigned:6;
unsigned start:10;
unsigned:6;
};
u32 raw;
};
union VIScale {
struct {
unsigned scale:12;
unsigned:4;
unsigned offset:12;
unsigned:4;
};
u32 raw;
};
enum VIFormat {
blank = 0,
reserved = 1,
f5553 = 2,
f8888 = 3
};
union VIStatus {
struct {
u8 format:2;
unsigned serrate:1;
unsigned:29;
};
u32 raw;
};
struct MI;
struct Registers;
struct VI {
VI();
u32 Read(u32);
void Write(MI&, Registers&, u32, u32);
VIScale xscale{}, yscale{};
VIVideo hvideo{}, vvideo{};
VIHsyncLeap hsyncLeap{};
VIStatus status{};
VIBurst burst{}, vburst{};
u32 origin, width, current;
u32 vsync, hsync, intr;
u32 hstart{}, vstart{};
int swaps{};
int numHalflines;
int numFields;
int cyclesPerHalfline;
};
} // natsukashii::n64::core

View File

@@ -3,6 +3,7 @@
#include <type_traits>
#include <cassert>
#include <portable_endian_bswap.h>
#include <fstream>
namespace natsukashii::util {
enum MessageType : u8 {
@@ -133,4 +134,21 @@ inline size_t NextPow2(size_t num) {
num |= num >> 16;
return num + 1;
}
inline void ReadFileBinary(const std::string& path, void* buf) {
std::ifstream file("external/vert.spv", std::ios::binary);
file.unsetf(std::ios::skipws);
if(!file.is_open()) {
util::panic("Could not load file!\n");
}
file.seekg(std::ios::end);
auto size = file.tellg();
file.seekg(std::ios::beg);
if(buf)
free(buf);
buf = calloc(size, 1);
file.read((char*)buf, size);
file.close();
}
}

View File

@@ -2,6 +2,7 @@
#include <gb/Core.hpp>
#include <n64/Core.hpp>
#include <volk.h>
#include <ParallelRDPWrapper.hpp>
namespace natsukashii::frontend {
using namespace natsukashii;

View File

@@ -15,7 +15,6 @@ struct App {
App(const std::string&, const std::string&);
void Run();
private:
SDL_Window *window = nullptr;
SDL_Renderer *renderer = nullptr;
u32 id;
std::unique_ptr<BaseCore> core;

View File

@@ -6,12 +6,11 @@
#include <SDL2/SDL_vulkan.h>
#include <util.hpp>
using namespace Vulkan;
using namespace natsukashii;
using namespace natsukashii::n64;
using namespace Vulkan;
using namespace RDP;
using std::unique_ptr;
using RDP::CommandProcessor;
using RDP::CommandProcessorFlags;
using RDP::VIRegister;
static CommandProcessor* command_processor;
static WSI* wsi;
@@ -19,7 +18,7 @@ static WSI* wsi;
std::vector<Semaphore> acquire_semaphore;
VkQueue GetGraphicsQueue() {
return wsi->get_context().get_graphics_queue();
return wsi->get_context().get_queue_info().queues[QUEUE_INDEX_GRAPHICS];
}
VkInstance GetVkInstance() {
@@ -35,7 +34,7 @@ VkDevice GetVkDevice() {
}
uint32_t GetVkGraphicsQueueFamily() {
return wsi->get_context().get_graphics_queue_family();
return wsi->get_context().get_queue_info().family_indices[QUEUE_INDEX_GRAPHICS];
}
VkFormat GetVkFormat() {
@@ -94,11 +93,11 @@ public:
}
uint32_t get_surface_width() override {
return N64_SCREEN_X * SCREEN_SCALE;
return 800;
}
uint32_t get_surface_height() override {
return N64_SCREEN_Y * SCREEN_SCALE;
return 600;
}
bool alive(Vulkan::WSI &wsi) override {
@@ -106,11 +105,14 @@ public:
}
void poll_input() override {
n64_poll_input();
SDL_Event e;
while(SDL_PollEvent(&e)) {
}
}
void event_frame_tick(double frame, double elapsed) override {
n64_render_screen();
//n64_render_screen();
}
};
@@ -125,22 +127,26 @@ void LoadParallelRDP(const u8* rdram) {
util::panic("Failed to initialize WSI!");
}
ResourceLayout vert_layout;
ResourceLayout frag_layout;
ResourceLayout vertLayout;
ResourceLayout fragLayout;
vert_layout.input_mask = 1;
vert_layout.output_mask = 1;
vertLayout.input_mask = 1;
vertLayout.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);
fragLayout.input_mask = 1;
fragLayout.output_mask = 1;
fragLayout.spec_constant_mask = 1;
fragLayout.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;
fragLayout.sets[0].sampled_image_mask = 1;
fragLayout.sets[0].fp_mask = 1;
fragLayout.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);
u32* fullscreenQuadVert, *fullscreenQuadFrag;
util::ReadFileBinary("external/vert.spv", fullscreenQuadVert);
util::ReadFileBinary("external/frag.spv", fullscreenQuadFrag);
fullscreen_quad_program = wsi->get_device().request_program(fullscreenQuadVert, sizeof(fullscreenQuadVert), fullscreenQuadFrag, sizeof(fullscreenQuadFrag), &vertLayout, &fragLayout);
auto aligned_rdram = reinterpret_cast<uintptr_t>(rdram);
uintptr_t offset = 0;
@@ -162,7 +168,7 @@ void LoadParallelRDP(const u8* rdram) {
}
}
void draw_fullscreen_textured_quad(Util::IntrusivePtr<Image> image, Util::IntrusivePtr<CommandBuffer> cmd) {
void DrawFullscreenTexturedQuad(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();
@@ -200,11 +206,11 @@ void draw_fullscreen_textured_quad(Util::IntrusivePtr<Image> image, Util::Intrus
cmd->draw(3, 1);
}
void update_screen(Util::IntrusivePtr<Image> image) {
void UpdateScreen(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);
auto info = Vulkan::ImageCreateInfo::immutable_2d_image(800, 600, 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;
@@ -228,32 +234,28 @@ void update_screen(Util::IntrusivePtr<Image> image) {
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());
DrawFullscreenTexturedQuad(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);
void UpdateScreenParallelRdp(const n64::core::VI& vi) {
command_processor->set_vi_register(VIRegister::Control, vi.status.raw);
command_processor->set_vi_register(VIRegister::Origin, vi.origin);
command_processor->set_vi_register(VIRegister::Width, vi.width);
command_processor->set_vi_register(VIRegister::Intr, vi.intr);
command_processor->set_vi_register(VIRegister::VCurrentLine, vi.current);
command_processor->set_vi_register(VIRegister::Timing, vi.burst.raw);
command_processor->set_vi_register(VIRegister::VSync, vi.vsync);
command_processor->set_vi_register(VIRegister::HSync, vi.hsync);
command_processor->set_vi_register(VIRegister::Leap, vi.hsyncLeap.raw);
command_processor->set_vi_register(VIRegister::HStart, vi.hstart);
command_processor->set_vi_register(VIRegister::VStart, vi.vstart);
command_processor->set_vi_register(VIRegister::VBurst, vi.burst.raw);
command_processor->set_vi_register(VIRegister::XScale, vi.xscale.raw);
command_processor->set_vi_register(VIRegister::YScale, vi.yscale.raw);
RDP::ScanoutOptions opts;
opts.persist_frame_on_invalid_input = true;
@@ -265,18 +267,18 @@ void update_screen_parallel_rdp() {
opts.downscale_steps = true;
opts.crop_overscan_pixels = true;
Util::IntrusivePtr<Image> image = command_processor->scanout(opts);
update_screen(image);
UpdateScreen(image);
command_processor->begin_frame_context();
}
void update_screen_parallel_rdp_no_game() {
update_screen(static_cast<Util::IntrusivePtr<Image>>(nullptr));
void UpdateScreenParallelRdpNoGame() {
UpdateScreen(static_cast<Util::IntrusivePtr<Image>>(nullptr));
}
void parallel_rdp_enqueue_command(int command_length, word* buffer) {
void ParallelRdpEnqueueCommand(int command_length, u32* buffer) {
command_processor->enqueue_command(command_length, buffer);
}
void parallel_rdp_on_full_sync() {
void ParallelRdpOnFullSync() {
command_processor->wait_for_timeline(command_processor->signal_timeline());
}

View File

@@ -1,7 +1,10 @@
#pragma once
#include <n64/Core.hpp>
#include <wsi.hpp>
#include <SDL2/SDL.h>
#include <n64/core/mmio/VI.hpp>
static SDL_Window* window;
VkQueue GetGraphicsQueue();
VkInstance GetVkInstance();
VkPhysicalDevice GetVkPhysicalDevice();
@@ -10,8 +13,8 @@ uint32_t GetVkGraphicsQueueFamily();
VkFormat GetVkFormat();
VkCommandBuffer GetVkCommandBuffer();
void SubmitRequestedVkCommandBuffer();
void LoadParallelRdp();
void UpdateScreenParallelRdp();
void LoadParallelRDP(const u8* rdram);
void UpdateScreenParallelRdp(natsukashii::n64::core::VI& vi);
void ParallelRdpEnqueueCommand(int command_length, u32* buffer);
void ParallelRdpOnFullSync();
void UpdateScreenParallelRdpNoGame();