Qt6 frontend #1

Merged
iris merged 5 commits from qt6 into dev 2026-06-09 17:14:08 +02:00
21 changed files with 433 additions and 709 deletions
Showing only changes of commit 74d9831d68 - Show all commits
+1
View File
@@ -6,6 +6,7 @@ saves/
.vscode/
.zed/
out/
.qtcreator/
*.toml
*.ini
*.z64
-6
View File
@@ -49,7 +49,6 @@ else()
message(FATAL_ERROR "Git not found, please define KAIZEN_GIT_COMMIT_HASH manually.")
endif()
endif()
configure_file(${CMAKE_CURRENT_LIST_DIR}/cmake/version.hpp.in ${CMAKE_CURRENT_LIST_DIR}/resources/version.hpp)
include_directories(
@@ -171,7 +170,6 @@ qt_add_executable(kaizen
src/utils/Options.cpp
src/utils/File.cpp)
if (WIN32)
set(MIO_LIB mio::mio_full_winapi)
else()
@@ -179,10 +177,6 @@ else()
endif()
target_link_libraries(kaizen PUBLIC Qt6::Core Qt6::Gui Qt6::Widgets SDL3::SDL3 SDL3::SDL3-static cflags::cflags ${MIO_LIB} parallel-rdp capstone backend)
target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED)
set_target_properties(kaizen PROPERTIES
WIN32_EXECUTABLE ON
MACOSX_BUNDLE ON)
if (SANITIZERS)
message("UBSAN AND ASAN: ON")
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright 2023 Simone Coco
Copyright 2026 Iris Coco
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+11 -6
View File
@@ -4,7 +4,6 @@
#include <rdp_device.hpp>
#include <resources/vert.spv.h>
#include <resources/frag.spv.h>
#include <KaizenGui.hpp>
using namespace Vulkan;
using namespace RDP;
@@ -23,7 +22,8 @@ Program *fullscreen_quad_program;
// Copied and modified from WSI::init_context_from_platform
Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, const unsigned num_thread_indices,
const Context::SystemHandles &system_handles) {
const Context::SystemHandles &system_handles,
Vulkan::InstanceFactory *instanceFactory) {
VK_ASSERT(platform);
const auto instance_ext = platform->get_instance_extensions();
const auto device_ext = platform->get_device_extensions();
@@ -32,6 +32,9 @@ Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, const unsig
new_context->set_application_info(platform->get_application_info());
new_context->set_num_thread_indices(num_thread_indices);
new_context->set_system_handles(system_handles);
if (instanceFactory) {
new_context->set_instance_factory(instanceFactory);
}
if (!new_context->init_instance(instance_ext.data(), instance_ext.size(),
CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT)) {
@@ -55,14 +58,15 @@ Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, const unsig
}
void ParallelRDP::LoadWSIPlatform(const std::shared_ptr<WSIPlatform> &wsi_platform,
const std::shared_ptr<WindowInfo> &newWindowInfo) {
const std::shared_ptr<WindowInfo> &newWindowInfo,
Vulkan::InstanceFactory *instanceFactory) {
wsi = std::make_shared<WSI>();
wsi->set_backbuffer_srgb(false);
wsi->set_platform(wsi_platform.get());
wsi->set_present_mode(PresentMode::SyncToVBlank);
if (constexpr Context::SystemHandles handles;
!wsi->init_from_existing_context(InitVulkanContext(wsi_platform.get(), 1, handles))) {
!wsi->init_from_existing_context(InitVulkanContext(wsi_platform.get(), 1, handles, instanceFactory))) {
panic("Failed to initialize WSI: init_from_existing_context() failed");
}
@@ -78,8 +82,9 @@ void ParallelRDP::LoadWSIPlatform(const std::shared_ptr<WSIPlatform> &wsi_platfo
}
void ParallelRDP::Init(const std::shared_ptr<WSIPlatform> &wsiPlatform,
const std::shared_ptr<WindowInfo> &newWindowInfo, const u8 *rdram) {
LoadWSIPlatform(wsiPlatform, newWindowInfo);
const std::shared_ptr<WindowInfo> &newWindowInfo, Vulkan::InstanceFactory *instanceFactory,
const u8 *rdram) {
LoadWSIPlatform(wsiPlatform, newWindowInfo, instanceFactory);
ResourceLayout vertLayout;
ResourceLayout fragLayout;
+4 -2
View File
@@ -15,7 +15,8 @@ class ParallelRDP {
virtual ~WindowInfo() = default;
};
void Init(const std::shared_ptr<Vulkan::WSIPlatform> &, const std::shared_ptr<WindowInfo> &, const u8 *);
void Init(const std::shared_ptr<Vulkan::WSIPlatform> &, const std::shared_ptr<WindowInfo> &,
Vulkan::InstanceFactory *, const u8 *);
template <bool>
void UpdateScreen() const;
@@ -29,7 +30,8 @@ class ParallelRDP {
std::shared_ptr<WindowInfo> windowInfo;
private:
void LoadWSIPlatform(const std::shared_ptr<Vulkan::WSIPlatform> &, const std::shared_ptr<WindowInfo> &);
void LoadWSIPlatform(const std::shared_ptr<Vulkan::WSIPlatform> &, const std::shared_ptr<WindowInfo> &,
Vulkan::InstanceFactory *);
void DrawFullscreenTexturedQuad(Util::IntrusivePtr<Vulkan::Image>, Util::IntrusivePtr<Vulkan::CommandBuffer>) const;
void UpdateScreen(Util::IntrusivePtr<Vulkan::Image>) const;
};
+3 -3
View File
@@ -5,7 +5,7 @@
namespace n64 {
Core::Core() : interpreter(*mem, regs) {
const auto selectedCpu = Options::GetInstance().GetValue<std::string>("cpu", "type");
const auto selectedCpu = Options::GetCpuType();
if (selectedCpu == "interpreter") {
cpuType = PlainInterpreter;
} else if (selectedCpu == "cached_interpreter") {
@@ -88,7 +88,7 @@ void Core::StepRSP(const u32 cpuCycles) {
}
}
void Core::Run(const float volumeL, const float volumeR) {
void Core::Run() {
MMIO &mmio = mem->mmio;
bool broken = false;
@@ -116,7 +116,7 @@ void Core::Run(const float volumeL, const float volumeR) {
mmio.mi.InterruptRaise(MI::Interrupt::VI);
}
mmio.ai.Step(frameCycles, volumeL, volumeR);
mmio.ai.Step(frameCycles);
Scheduler::GetInstance().Tick(frameCycles);
}
}
+1 -1
View File
@@ -32,7 +32,7 @@ struct Core {
void Reset();
void LoadROM(const std::string &);
void LoadTAS(const fs::path &) const;
void Run(float volumeL, float volumeR);
void Run();
void TogglePause() { pause = !pause; }
inline void ToggleBreakpoint(s64 addr) {
if (breakpoints.contains(addr)) {
+1 -1
View File
@@ -31,7 +31,7 @@ void Mem::Reset() {
void Mem::LoadSRAM(SaveType save_type, fs::path path) {
if (save_type == SAVE_SRAM_256k) {
std::error_code err;
std::string savePath = Options::GetInstance().GetValue<std::string>("general", "savePath");
std::string savePath = Options::GetSavesPath();
if (!savePath.empty()) {
path = savePath / path.filename();
}
+138 -138
View File
@@ -8,190 +8,190 @@ constexpr auto FLASH_SIZE = 1_mb;
Flash::Flash(mio::mmap_sink &saveData) : saveData(saveData) {}
void Flash::Reset() {
state = FlashState::Idle;
writeOffs = {};
state = {};
status = {};
eraseOffs = {};
writeBuf = {};
state = FlashState::Idle;
writeOffs = {};
state = {};
status = {};
eraseOffs = {};
writeBuf = {};
}
void Flash::Load(SaveType saveType, const std::string &path) {
if (saveType == SAVE_FLASH_1m) {
fs::path flashPath_ = path;
std::string savePath = Options::GetInstance().GetValue<std::string>("general", "savePath");
if (!savePath.empty()) {
flashPath_ = savePath / flashPath_.filename();
}
flashPath = flashPath_.replace_extension(".flash").string();
std::error_code error;
if (saveData.is_mapped()) {
saveData.sync(error);
if (error) {
panic("Could not sync {}", flashPath);
}
saveData.unmap();
}
if (saveType == SAVE_FLASH_1m) {
fs::path flashPath_ = path;
std::string savePath = Options::GetSavesPath();
if (!savePath.empty()) {
flashPath_ = savePath / flashPath_.filename();
}
flashPath = flashPath_.replace_extension(".flash").string();
std::error_code error;
if (saveData.is_mapped()) {
saveData.sync(error);
if (error) {
panic("Could not sync {}", flashPath);
}
saveData.unmap();
}
auto flashVec = ircolib::ReadFileBinary(flashPath);
if (flashVec.empty()) {
std::vector<u8> dummy{};
dummy.resize(FLASH_SIZE);
ircolib::WriteFileBinary(dummy, flashPath);
flashVec = ircolib::ReadFileBinary(flashPath);
}
auto flashVec = ircolib::ReadFileBinary(flashPath);
if (flashVec.empty()) {
std::vector<u8> dummy{};
dummy.resize(FLASH_SIZE);
ircolib::WriteFileBinary(dummy, flashPath);
flashVec = ircolib::ReadFileBinary(flashPath);
}
if (flashVec.size() != FLASH_SIZE) {
panic("Corrupt SRAM!");
}
if (flashVec.size() != FLASH_SIZE) {
panic("Corrupt SRAM!");
}
saveData = mio::make_mmap_sink(flashPath, error);
if (error) {
panic("Could not make mmap {}", flashPath);
saveData = mio::make_mmap_sink(flashPath, error);
if (error) {
panic("Could not make mmap {}", flashPath);
}
}
}
}
void Flash::CommandExecute() const {
trace("Flash::CommandExecute");
switch (state) {
case FlashState::Idle:
break;
case FlashState::Erase:
if (saveData.is_mapped()) {
for (int i = 0; i < 128; i++) {
saveData[eraseOffs + i] = 0xFF;
}
} else {
panic("Accessing flash when not mapped!");
trace("Flash::CommandExecute");
switch (state) {
case FlashState::Idle:
break;
case FlashState::Erase:
if (saveData.is_mapped()) {
for (int i = 0; i < 128; i++) {
saveData[eraseOffs + i] = 0xFF;
}
} else {
panic("Accessing flash when not mapped!");
}
break;
case FlashState::Write:
if (saveData.is_mapped()) {
for (int i = 0; i < 128; i++) {
saveData[writeOffs + i] = writeBuf[i];
}
} else {
panic("Accessing flash when not mapped!");
}
break;
case FlashState::Read:
panic("Execute command when flash in read state");
break;
case FlashState::Status:
break;
}
break;
case FlashState::Write:
if (saveData.is_mapped()) {
for (int i = 0; i < 128; i++) {
saveData[writeOffs + i] = writeBuf[i];
}
} else {
panic("Accessing flash when not mapped!");
}
break;
case FlashState::Read:
panic("Execute command when flash in read state");
break;
case FlashState::Status:
break;
}
}
void Flash::CommandStatus() {
state = FlashState::Status;
status = 0x1111800100C20000;
state = FlashState::Status;
status = 0x1111800100C20000;
}
void Flash::CommandSetEraseOffs(u32 val) { eraseOffs = (val & 0xffff) << 7; }
void Flash::CommandErase() {
state = FlashState::Erase;
status = 0x1111800800C20000LL;
state = FlashState::Erase;
status = 0x1111800800C20000LL;
}
void Flash::CommandSetWriteOffs(u32 val) {
writeOffs = (val & 0xffff) << 7;
status = 0x1111800400C20000LL;
writeOffs = (val & 0xffff) << 7;
status = 0x1111800400C20000LL;
}
void Flash::CommandWrite() { state = FlashState::Write; }
void Flash::CommandRead() {
state = FlashState::Read;
status = 0x11118004F0000000;
state = FlashState::Read;
status = 0x11118004F0000000;
}
template <>
void Flash::Write<u32>(u32 index, u32 val) {
if (index > 0) {
u8 cmd = val >> 24;
switch (cmd) {
case FLASH_COMMAND_EXECUTE:
CommandExecute();
break;
case FLASH_COMMAND_STATUS:
CommandStatus();
break;
case FLASH_COMMAND_SET_ERASE_OFFSET:
CommandSetEraseOffs(val);
break;
case FLASH_COMMAND_ERASE:
CommandErase();
break;
case FLASH_COMMAND_SET_WRITE_OFFSET:
CommandSetWriteOffs(val);
break;
case FLASH_COMMAND_WRITE:
CommandWrite();
break;
case FLASH_COMMAND_READ:
CommandRead();
break;
default:
warn("Invalid flash command: {:02X}", cmd);
if (index > 0) {
u8 cmd = val >> 24;
switch (cmd) {
case FLASH_COMMAND_EXECUTE:
CommandExecute();
break;
case FLASH_COMMAND_STATUS:
CommandStatus();
break;
case FLASH_COMMAND_SET_ERASE_OFFSET:
CommandSetEraseOffs(val);
break;
case FLASH_COMMAND_ERASE:
CommandErase();
break;
case FLASH_COMMAND_SET_WRITE_OFFSET:
CommandSetWriteOffs(val);
break;
case FLASH_COMMAND_WRITE:
CommandWrite();
break;
case FLASH_COMMAND_READ:
CommandRead();
break;
default:
warn("Invalid flash command: {:02X}", cmd);
}
} else {
warn("Flash Write of {:08X} @ {:08X}", val, index);
}
} else {
warn("Flash Write of {:08X} @ {:08X}", val, index);
}
}
template <>
void Flash::Write<u8>(u32 index, u8 val) {
switch (state) {
case FlashState::Idle:
panic("Invalid FlashState::Idle with Write<u8>");
case FlashState::Status:
panic("Invalid FlashState::Status with Write<u8>");
case FlashState::Erase:
panic("Invalid FlashState::Erase with Write<u8>");
case FlashState::Read:
panic("Invalid FlashState::Read with Write<u8>");
case FlashState::Write:
assert(index <= 0x7F && "Out of range flash Write8");
writeBuf[index] = val;
break;
default:
warn("Invalid flash state on Write<u8>: {:02X}", static_cast<u8>(state));
}
switch (state) {
case FlashState::Idle:
panic("Invalid FlashState::Idle with Write<u8>");
case FlashState::Status:
panic("Invalid FlashState::Status with Write<u8>");
case FlashState::Erase:
panic("Invalid FlashState::Erase with Write<u8>");
case FlashState::Read:
panic("Invalid FlashState::Read with Write<u8>");
case FlashState::Write:
assert(index <= 0x7F && "Out of range flash Write8");
writeBuf[index] = val;
break;
default:
warn("Invalid flash state on Write<u8>: {:02X}", static_cast<u8>(state));
}
}
template <>
u8 Flash::Read<u8>(const u32 index) const {
switch (state) {
case FlashState::Idle:
panic("Flash read byte while in state FLASH_STATE_IDLE");
case FlashState::Write:
panic("Flash read byte while in state FLASH_STATE_WRITE");
case FlashState::Read:
if (saveData.is_mapped()) {
const u8 value = saveData[index];
trace("Flash read byte in state read: index {:08X} = {:02X}", index, value);
return value;
}
switch (state) {
case FlashState::Idle:
panic("Flash read byte while in state FLASH_STATE_IDLE");
case FlashState::Write:
panic("Flash read byte while in state FLASH_STATE_WRITE");
case FlashState::Read:
if (saveData.is_mapped()) {
const u8 value = saveData[index];
trace("Flash read byte in state read: index {:08X} = {:02X}", index, value);
return value;
}
panic("Accessing flash when not mapped!");
panic("Accessing flash when not mapped!");
case FlashState::Status:
{
const u32 offset = (7 - (index % 8)) * 8;
const u8 value = (status >> offset) & 0xFF;
trace("Flash read byte in state status: index {:08X} = {:02X}", index, value);
return value;
case FlashState::Status:
{
const u32 offset = (7 - (index % 8)) * 8;
const u8 value = (status >> offset) & 0xFF;
trace("Flash read byte in state status: index {:08X} = {:02X}", index, value);
return value;
}
default:
panic("Flash read byte while in unknown state");
return 0;
}
default:
panic("Flash read byte while in unknown state");
return 0;
}
}
template <>
u32 Flash::Read<u32>(u32) const {
return status >> 32;
return status >> 32;
}
} // namespace n64
+99 -96
View File
@@ -1,119 +1,122 @@
#include <Core.hpp>
#include <log.hpp>
#include <Options.hpp>
namespace n64 {
AI::AI() { Reset(); }
void AI::Reset() {
dmaEnable = false;
dacRate = 0;
bitrate = 0;
dmaCount = 0;
dmaAddrCarry = false;
cycles = 0;
dmaLen = {};
dmaAddr = {};
dac = {44100, N64_CPU_FREQ / dac.freq, 16};
device.Reset();
dmaEnable = false;
dacRate = 0;
bitrate = 0;
dmaCount = 0;
dmaAddrCarry = false;
cycles = 0;
dmaLen = {};
dmaAddr = {};
dac = {44100, N64_CPU_FREQ / dac.freq, 16};
device.Reset();
}
// https://github.com/ares-emulator/ares/blob/master/ares/n64/ai/io.cpp
// https://github.com/ares-emulator/ares/blob/master/LICENSE
auto AI::Read(const u32 addr) const -> u32 {
if (addr == 0x0450000C) {
u32 val = 0;
val |= (dmaCount > 1);
val |= 1 << 20;
val |= 1 << 24;
val |= (dmaEnable << 25);
val |= (dmaCount > 0) << 30;
val |= (dmaCount > 1) << 31;
return val;
}
if (addr == 0x0450000C) {
u32 val = 0;
val |= (dmaCount > 1);
val |= 1 << 20;
val |= 1 << 24;
val |= (dmaEnable << 25);
val |= (dmaCount > 0) << 30;
val |= (dmaCount > 1) << 31;
return val;
}
return dmaLen[0];
return dmaLen[0];
}
void AI::Write(const u32 addr, const u32 val) {
n64::Mem& mem = n64::Core::GetMem();
switch (addr) {
case 0x04500000:
if (dmaCount < 2) {
dmaAddr[dmaCount] = val & 0xFFFFFF & ~7;
n64::Mem &mem = n64::Core::GetMem();
switch (addr) {
case 0x04500000:
if (dmaCount < 2) {
dmaAddr[dmaCount] = val & 0xFFFFFF & ~7;
}
break;
case 0x04500004:
{
const u32 len = (val & 0x3FFFF) & ~7;
if (dmaCount < 2) {
if (dmaCount == 0)
mem.mmio.mi.InterruptRaise(MI::Interrupt::AI);
dmaLen[dmaCount] = len;
dmaCount++;
}
}
break;
case 0x04500008:
dmaEnable = val & 1;
break;
case 0x0450000C:
mem.mmio.mi.InterruptLower(MI::Interrupt::AI);
break;
case 0x04500010:
{
const u32 oldDacFreq = dac.freq;
dacRate = val & 0x3FFF;
dac.freq = std::max(1.f, (float)GetVideoFrequency(mem.IsROMPAL()) / (dacRate + 1)) * 1.037;
dac.period = GetVideoFrequency(mem.IsROMPAL()) / dac.freq;
if (oldDacFreq != dac.freq) {
device.AdjustSampleRate(dac.freq);
}
}
break;
case 0x04500014:
bitrate = val & 0xF;
dac.precision = bitrate + 1;
break;
default:
panic("Unhandled AI write at addr {:08X} with val {:08X}", addr, val);
}
break;
case 0x04500004:
{
const u32 len = (val & 0x3FFFF) & ~7;
if (dmaCount < 2) {
if (dmaCount == 0)
mem.mmio.mi.InterruptRaise(MI::Interrupt::AI);
dmaLen[dmaCount] = len;
dmaCount++;
}
}
break;
case 0x04500008:
dmaEnable = val & 1;
break;
case 0x0450000C:
mem.mmio.mi.InterruptLower(MI::Interrupt::AI);
break;
case 0x04500010:
{
const u32 oldDacFreq = dac.freq;
dacRate = val & 0x3FFF;
dac.freq = std::max(1.f, (float)GetVideoFrequency(mem.IsROMPAL()) / (dacRate + 1)) * 1.037;
dac.period = GetVideoFrequency(mem.IsROMPAL()) / dac.freq;
if (oldDacFreq != dac.freq) {
device.AdjustSampleRate(dac.freq);
}
}
break;
case 0x04500014:
bitrate = val & 0xF;
dac.precision = bitrate + 1;
break;
default:
panic("Unhandled AI write at addr {:08X} with val {:08X}", addr, val);
}
}
void AI::Step(const u32 cpuCycles, const float volumeL, const float volumeR) {
n64::Mem& mem = n64::Core::GetMem();
cycles += cpuCycles;
while (cycles > dac.period) {
if (dmaCount == 0) {
return;
void AI::Step(const u32 cpuCycles) {
n64::Mem &mem = n64::Core::GetMem();
cycles += cpuCycles;
while (cycles > dac.period) {
if (dmaCount == 0) {
return;
}
if (dmaLen[0] && dmaEnable) {
const u32 addrHi = (dmaAddr[0] >> 13) + dmaAddrCarry & 0x7FF;
dmaAddr[0] = addrHi << 13 | dmaAddr[0] & 0x1FFF;
const u32 data = mem.mmio.rdp.ReadRDRAM<u32>(dmaAddr[0]);
const s16 l = s16(data >> 16);
const s16 r = s16(data);
auto volume = Options::GetVolume();
if (volume > 0) {
device.PushSample((float)l / std::numeric_limits<s16>::max(), volume,
(float)r / std::numeric_limits<s16>::max(), volume);
}
const u32 addrLo = dmaAddr[0] + 4 & 0x1FFF;
dmaAddr[0] = dmaAddr[0] & ~0x1FFF | addrLo;
dmaAddrCarry = addrLo == 0;
dmaLen[0] -= 4;
}
if (!dmaLen[0]) {
if (--dmaCount > 0) {
mem.mmio.mi.InterruptRaise(MI::Interrupt::AI);
dmaAddr[0] = dmaAddr[1];
dmaLen[0] = dmaLen[1];
}
}
cycles -= dac.period;
}
if (dmaLen[0] && dmaEnable) {
const u32 addrHi = (dmaAddr[0] >> 13) + dmaAddrCarry & 0x7FF;
dmaAddr[0] = addrHi << 13 | dmaAddr[0] & 0x1FFF;
const u32 data = mem.mmio.rdp.ReadRDRAM<u32>(dmaAddr[0]);
const s16 l = s16(data >> 16);
const s16 r = s16(data);
if (volumeR > 0 && volumeL > 0) {
device.PushSample((float)l / std::numeric_limits<s16>::max(), volumeL, (float)r / std::numeric_limits<s16>::max(), volumeR);
}
const u32 addrLo = dmaAddr[0] + 4 & 0x1FFF;
dmaAddr[0] = dmaAddr[0] & ~0x1FFF | addrLo;
dmaAddrCarry = addrLo == 0;
dmaLen[0] -= 4;
}
if (!dmaLen[0]) {
if (--dmaCount > 0) {
mem.mmio.mi.InterruptRaise(MI::Interrupt::AI);
dmaAddr[0] = dmaAddr[1];
dmaLen[0] = dmaLen[1];
}
}
cycles -= dac.period;
}
}
} // namespace n64
+20 -19
View File
@@ -1,29 +1,30 @@
#pragma once
#include <common.hpp>
#include <core/mmio/Audio.hpp>
#include <array>
namespace n64 {
struct AI {
AI();
void Reset();
auto Read(u32) const -> u32;
void Write(u32, u32);
void Step(u32, float, float);
bool dmaEnable{};
bool dmaAddrCarry{};
u8 bitrate{};
u16 dacRate{};
int dmaCount{};
u32 cycles{};
std::array<u32, 2> dmaLen{};
std::array<u32, 2> dmaAddr{};
AI();
void Reset();
auto Read(u32) const -> u32;
void Write(u32, u32);
void Step(u32);
bool dmaEnable{};
bool dmaAddrCarry{};
u8 bitrate{};
u16 dacRate{};
int dmaCount{};
u32 cycles{};
std::array<u32, 2> dmaLen{};
std::array<u32, 2> dmaAddr{};
struct {
u32 freq{44100};
u32 period{N64_CPU_FREQ / freq};
u32 precision{16};
} dac;
struct {
u32 freq{44100};
u32 period{N64_CPU_FREQ / freq};
u32 precision{16};
} dac;
AudioDevice device;
AudioDevice device;
};
} // namespace n64
+2 -2
View File
@@ -36,7 +36,7 @@ void PIF::Reset() {
void PIF::MaybeLoadMempak() {
if (!mempakOpen) {
fs::path mempakPath_ = mempakPath;
fs::path savePath = Options::GetInstance().GetValue<std::string>("general", "savePath");
fs::path savePath = Options::GetSavesPath();
if (!savePath.empty()) {
if (!fs::exists(savePath))
fs::create_directory(savePath);
@@ -92,7 +92,7 @@ FORCE_INLINE size_t GetSaveSize(SaveType saveType) {
void PIF::LoadEeprom(const SaveType saveType, const std::string &path) {
if (saveType == SAVE_EEPROM_16k || saveType == SAVE_EEPROM_4k) {
fs::path eepromPath_ = path;
std::string savePath = Options::GetInstance().GetValue<std::string>("general", "savePath");
std::string savePath = Options::GetSavesPath();
if (!savePath.empty()) {
eepromPath_ = savePath / eepromPath_.filename();
}
+12 -40
View File
@@ -1,53 +1,25 @@
#include <Core.hpp>
#include <EmuThread.hpp>
#include <KaizenGui.hpp>
#include <Core.hpp>
EmuThread::EmuThread(double &fps, SettingsWindow &settings) noexcept : settings(settings), fps(fps) {}
EmuThread::EmuThread() noexcept {}
void EmuThread::run() const noexcept {
n64::Core& core = n64::Core::GetInstance();
if(!core.romLoaded) return;
n64::Core &core = n64::Core::GetInstance();
if (!core.romLoaded)
return;
auto lastSample = std::chrono::high_resolution_clock::now();
auto avgFps = 16.667;
auto sampledFps = 0;
static bool oneSecondPassed = false;
fps = 1000.0 / avgFps;
const auto startFrameTime = std::chrono::high_resolution_clock::now();
if (!core.pause) {
core.Run(settings.getVolumeL(), settings.getVolumeR());
}
const auto endFrameTime = std::chrono::high_resolution_clock::now();
using namespace std::chrono_literals;
const auto frameTimeMs = std::chrono::duration<double>(endFrameTime - startFrameTime) / 1ms;
avgFps += frameTimeMs;
sampledFps++;
if (const auto elapsedSinceLastSample = std::chrono::duration<double>(endFrameTime - lastSample) / 1s;
elapsedSinceLastSample >= 1.0) {
if (!oneSecondPassed) {
oneSecondPassed = true;
return;
if (!core.pause) {
core.Run();
}
avgFps /= sampledFps;
fps = 1000.0 / avgFps;
}
}
void EmuThread::TogglePause() const noexcept {
n64::Core::GetInstance().TogglePause();
}
void EmuThread::TogglePause() const noexcept { n64::Core::GetInstance().TogglePause(); }
void EmuThread::Reset() const noexcept {
n64::Core::GetInstance().Reset();
}
void EmuThread::Reset() const noexcept { n64::Core::GetInstance().Reset(); }
void EmuThread::Stop() const noexcept {
n64::Core& core = n64::Core::GetInstance();
core.Stop();
core.rom = {};
n64::Core &core = n64::Core::GetInstance();
core.Stop();
core.rom = {};
}
+31 -338
View File
@@ -2,360 +2,53 @@
#include <backend/Core.hpp>
#include <QMenuBar>
#include <QMenu>
#include <QMessageBox>
#include <resources/gamecontrollerdb.h>
KaizenGui::KaizenGui() noexcept : vulkanWidget(windowHandle()) {
KaizenGui::KaizenGui() noexcept {
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
hide();
vulkanWidget = new RenderWidget();
setWindowTitle("Kaizen " KAIZEN_VERSION_STR);
setMinimumSize(640, 480);
setCentralWidget(vulkanWidget);
auto fileMenu = menuBar()->addMenu("File");
auto open = fileMenu->addMenu("Open");
auto exit = fileMenu->addMenu("Exit");
auto open = fileMenu->addAction("Open");
auto exit = fileMenu->addAction("Exit");
auto emulationMenu = menuBar()->addMenu("Emulation");
auto settingsMenu = emulationMenu->addMenu("Settings");
connect(settingsMenu, &QMenu::triggered, this, [&] { settingsWindow.show(); });
auto settingsMenu = emulationMenu->addAction("Settings");
settingsWindow = new SettingsWindow();
connect(settingsMenu, &QAction::triggered, settingsWindow, &SettingsWindow::show);
emulationMenu->addSeparator();
auto pause = emulationMenu->addMenu("Pause");
auto reset = emulationMenu->addMenu("Reset");
auto stop = emulationMenu->addMenu("Stop");
auto pause = emulationMenu->addAction("Pause");
auto reset = emulationMenu->addAction("Reset");
auto stop = emulationMenu->addAction("Stop");
auto helpMenu = menuBar()->addMenu("Help");
auto about = helpMenu->addAction("About");
connect(about, &QAction::triggered, this, [&] {
auto text = std::format("<p>Kaizen is a Nintendo 64 emulator that strives<br>"
"to offer a friendly user experience and compatibility.<br>"
"Kaizen is licensed under the BSD 3-clause license.<br>"
"Nintendo 64 is a registered trademark of Nintendo Co., Ltd.</p><hr>"
"Kaizen {}{}",
KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR);
QMessageBox::about(this, "About", text.c_str());
});
show();
}
KaizenGui::~KaizenGui() { SDL_Quit(); }
void KaizenGui::QueryDevices(const SDL_Event &event) {
switch (event.type) {
case SDL_EVENT_GAMEPAD_ADDED:
if (!gamepad) {
const auto index = event.gdevice.which;
gamepad = SDL_OpenGamepad(index);
info("Found controller!");
info("Name: {}", SDL_GetGamepadName(gamepad));
info("Vendor: {}", SDL_GetGamepadVendor(gamepad));
}
break;
case SDL_EVENT_GAMEPAD_REMOVED:
if (gamepad)
SDL_CloseGamepad(gamepad);
break;
default:
break;
}
}
void KaizenGui::HandleInput(const SDL_Event &event) {
const n64::Core &core = n64::Core::GetInstance();
n64::PIF &pif = n64::Core::GetMem().mmio.si.pif;
switch (event.type) {
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
if (!gamepad)
break;
{
pif.UpdateButton(0, n64::Controller::Key::Z,
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFT_TRIGGER) == SDL_JOYSTICK_AXIS_MAX);
pif.UpdateButton(0, n64::Controller::Key::CUp,
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) <= -127);
pif.UpdateButton(0, n64::Controller::Key::CDown,
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTY) >= 127);
pif.UpdateButton(0, n64::Controller::Key::CLeft,
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) <= -127);
pif.UpdateButton(0, n64::Controller::Key::CRight,
SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_RIGHTX) >= 127);
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
if (xclamped < 0) {
xclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MAX));
} else {
xclamped /= SDL_JOYSTICK_AXIS_MAX;
}
xclamped *= 86;
float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
if (yclamped < 0) {
yclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MIN));
} else {
yclamped /= SDL_JOYSTICK_AXIS_MAX;
}
yclamped *= 86;
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>(xclamped));
}
break;
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
case SDL_EVENT_GAMEPAD_BUTTON_UP:
if (!gamepad)
break;
pif.UpdateButton(0, n64::Controller::Key::A, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_SOUTH));
pif.UpdateButton(0, n64::Controller::Key::B, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_WEST));
pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_START));
pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_UP));
pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_DOWN));
pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_LEFT));
pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_DPAD_RIGHT));
pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER));
pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GetGamepadButton(gamepad, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER));
break;
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP:
{
const auto keys = SDL_GetKeyboardState(nullptr);
if ((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) {
fileDialogOpen = true;
}
fastForward = keys[SDL_SCANCODE_SPACE];
if (!unlockFramerate)
core.parallel.SetFramerateUnlocked(fastForward);
if (core.romLoaded) {
if (keys[SDL_SCANCODE_P]) {
emuThread.TogglePause();
}
if (keys[SDL_SCANCODE_R]) {
emuThread.Reset();
}
if (keys[SDL_SCANCODE_Q]) {
emuThread.Stop();
}
}
if (gamepad)
break;
pif.UpdateButton(0, n64::Controller::Key::Z, keys[SDL_SCANCODE_Z]);
pif.UpdateButton(0, n64::Controller::Key::CUp, keys[SDL_SCANCODE_HOME]);
pif.UpdateButton(0, n64::Controller::Key::CDown, keys[SDL_SCANCODE_END]);
pif.UpdateButton(0, n64::Controller::Key::CLeft, keys[SDL_SCANCODE_DELETE]);
pif.UpdateButton(0, n64::Controller::Key::CRight, keys[SDL_SCANCODE_PAGEDOWN]);
pif.UpdateButton(0, n64::Controller::Key::A, keys[SDL_SCANCODE_X]);
pif.UpdateButton(0, n64::Controller::Key::B, keys[SDL_SCANCODE_C]);
pif.UpdateButton(0, n64::Controller::Key::Start, keys[SDL_SCANCODE_RETURN]);
pif.UpdateButton(0, n64::Controller::Key::DUp, keys[SDL_SCANCODE_I]);
pif.UpdateButton(0, n64::Controller::Key::DDown, keys[SDL_SCANCODE_K]);
pif.UpdateButton(0, n64::Controller::Key::DLeft, keys[SDL_SCANCODE_J]);
pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]);
pif.UpdateButton(0, n64::Controller::Key::LT, keys[SDL_SCANCODE_A]);
pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]);
float x = 0, y = 0;
if (keys[SDL_SCANCODE_UP])
y = 86;
if (keys[SDL_SCANCODE_DOWN])
y = -86;
if (keys[SDL_SCANCODE_LEFT])
x = -86;
if (keys[SDL_SCANCODE_RIGHT])
x = 86;
pif.UpdateAxis(0, n64::Controller::Axis::X, x);
pif.UpdateAxis(0, n64::Controller::Axis::Y, y);
}
break;
default:
break;
}
}
void KaizenGui::RenderUI() {
n64::Core &core = n64::Core::GetInstance();
gui::StartFrame();
if (ImGui::BeginMainMenuBar()) {
if (ImGui::BeginMenu("File")) {
if (ImGui::MenuItem("Open", "Ctrl-O")) {
fileDialogOpen = true;
}
if (ImGui::MenuItem("Exit")) {
quit = true;
emuThread.Stop();
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Emulation")) {
ImGui::BeginDisabled(!core.romLoaded);
if (ImGui::MenuItem(core.pause ? "Resume" : "Pause", "P")) {
emuThread.TogglePause();
}
if (ImGui::MenuItem("Reset", "R")) {
emuThread.Reset();
}
if (ImGui::MenuItem("Stop", "Q")) {
emuThread.Stop();
core.romLoaded = false;
}
if (ImGui::Checkbox("Unlock framerate", &unlockFramerate)) {
core.parallel.SetFramerateUnlocked(unlockFramerate);
}
if (ImGui::MenuItem("Open Debugger")) {
debugger.Open();
}
ImGui::EndDisabled();
if (ImGui::MenuItem("Options")) {
settingsWindow.isOpen = true;
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Help")) {
if (ImGui::MenuItem("About")) {
aboutOpen = true;
}
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
if (settingsWindow.isOpen) {
ImGui::OpenPopup("Settings", ImGuiPopupFlags_None);
}
if (aboutOpen) {
ImGui::OpenPopup("About Kaizen");
}
settingsWindow.render();
debugger.render();
const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("Kaizen is a Nintendo 64 emulator that strives");
ImGui::Text("to offer a friendly user experience and compatibility.");
ImGui::Text("Kaizen is licensed under the BSD 3-clause license.");
ImGui::Text("Nintendo 64 is a registered trademark of Nintendo Co., Ltd.");
ImGui::Separator();
ImGui::Text("Kaizen %s%s", KAIZEN_USE_HASH ? "dev build " : "", KAIZEN_VERSION_STR);
ImGui::Separator();
if (ImGui::Button("OK")) {
aboutOpen = false;
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
if (ImGui::BeginMainStatusBar()) {
ImGui::Text("FPS: %.2f", ImGui::GetIO().Framerate);
ImGui::EndMainStatusBar();
}
if (shouldDisplaySpinner) {
ImGui::SetNextWindowPos({static_cast<float>(width) * 0.5f, static_cast<float>(height) * 0.5f}, 0,
ImVec2(0.5f, 0.5f));
ImGui::PushStyleColor(ImGuiCol_WindowBg, IM_COL32_BLACK_TRANS);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::Begin("##spinnerContainer", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration);
ImGui::Spinner("##spinner", 10.f, 4.f, ImGui::GetColorU32(ImGui::GetStyle().Colors[ImGuiCol_TitleBgActive]));
ImGui::SameLine();
ImGui::PushFont(nullptr, ImGui::GetStyle().FontSizeBase * 2.f);
ImGui::Text("Loading \"%s\"...", fs::path(fileToLoad).filename().string().c_str());
ImGui::PopFont();
ImGui::End();
ImGui::PopStyleVar();
ImGui::PopStyleColor();
}
ImGui::Render();
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
}
if (fileDialogOpen) {
fileDialogOpen = false;
constexpr SDL_DialogFileFilter filters[] = {{"All files", "*"},
{"Nintendo 64 executable", "n64;z64;v64"},
{"Nintendo 64 executable archive", "rar;tar;zip;7z"}};
SDL_ShowOpenFileDialog(
[](void *userdata, const char *const *filelist, int) {
auto kaizen = static_cast<KaizenGui *>(userdata);
if (!filelist) {
panic("An error occured: {}", SDL_GetError());
}
if (!*filelist) {
warn("The user did not select any file.");
warn("Most likely, the dialog was canceled.");
return;
}
kaizen->fileToLoad = *filelist;
kaizen->shouldDisplaySpinner = true;
std::thread fileWorker(&KaizenGui::FileWorker, kaizen);
fileWorker.detach();
},
this, window.getHandle(), filters, 3, nullptr, false);
}
if (minimized)
return;
if (core.romLoaded) {
core.parallel.UpdateScreen<true>();
return;
}
core.parallel.UpdateScreen<false>();
}
void KaizenGui::LoadROM(const std::string &path) noexcept {
n64::Core &core = n64::Core::GetInstance();
core.LoadROM(path);
const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB;
SDL_SetWindowTitle(window.getHandle(), ("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str());
}
void KaizenGui::run() {
while (!quit) {
SDL_Event e;
while (SDL_PollEvent(&e)) {
ImGui_ImplSDL3_ProcessEvent(&e);
switch (e.type) {
case SDL_EVENT_QUIT:
quit = true;
emuThread.Stop();
break;
case SDL_EVENT_WINDOW_MINIMIZED:
minimized = true;
break;
case SDL_EVENT_WINDOW_RESTORED:
minimized = false;
break;
default:
break;
}
QueryDevices(e);
HandleInput(e);
}
SDL_GetWindowSize(window.getHandle(), &width, &height);
emuThread.run();
RenderUI();
}
setWindowTitle(("Kaizen " KAIZEN_VERSION_STR " - " + gameNameDB).c_str());
}
void KaizenGui::LoadTAS(const std::string &path) noexcept { n64::Core::GetInstance().LoadTAS(fs::path(path)); }
+3 -8
View File
@@ -1,26 +1,24 @@
#pragma once
#include <QMainWindow>
#include <RenderWidget.hpp>
#include <EmuThread.hpp>
#include <SDL3/SDL_gamepad.h>
#include <QMainWindow>
class KaizenGui final : QMainWindow {
public:
explicit KaizenGui() noexcept;
~KaizenGui();
double fpsCounter = -1.0;
bool fastForward = false;
bool unlockFramerate = false;
bool minimized = false;
SettingsWindow settingsWindow;
RenderWidget vulkanWidget;
SettingsWindow *settingsWindow;
RenderWidget *vulkanWidget;
EmuThread emuThread;
SDL_Gamepad *gamepad = nullptr;
void run();
static void LoadTAS(const std::string &path) noexcept;
void LoadROM(const std::string &path) noexcept;
@@ -31,9 +29,6 @@ class KaizenGui final : QMainWindow {
bool quit = false;
bool shouldDisplaySpinner = false;
std::string fileToLoad = "";
void RenderUI();
void HandleInput(const SDL_Event &event);
void QueryDevices(const SDL_Event &event);
void FileWorker() {
if (fileToLoad.empty())
+44 -5
View File
@@ -1,10 +1,49 @@
#include <Core.hpp>
#include <KaizenGui.hpp>
#include <RenderWidget.hpp>
#include <Core.hpp>
#include <QGuiApplication>
RenderWidget::RenderWidget(QWindow *window) {
wsiPlatform = std::make_shared<QtWSIPlatform>(window);
windowInfo = std::make_shared<QtPrdpWindowInfo>(window);
enum class CompositorCategory { Windows, MacOS, XCB, Wayland };
static CompositorCategory GetOSCompositorCategory() {
const QString platform_name = QGuiApplication::platformName();
if (platform_name == QStringLiteral("windows"))
return CompositorCategory::Windows;
if (platform_name == QStringLiteral("xcb"))
return CompositorCategory::XCB;
if (platform_name == QStringLiteral("wayland") || platform_name == QStringLiteral("wayland-egl"))
return CompositorCategory::Wayland;
if (platform_name == QStringLiteral("cocoa") || platform_name == QStringLiteral("ios"))
return CompositorCategory::MacOS;
warn("Unknown Qt platform!");
return CompositorCategory::Windows;
}
RenderWidget::RenderWidget() {
setAttribute(Qt::WA_NativeWindow);
setAttribute(Qt::WA_PaintOnScreen);
if (GetOSCompositorCategory() == CompositorCategory::Wayland) {
setAttribute(Qt::WA_DontCreateNativeAncestors);
}
if (GetOSCompositorCategory() == CompositorCategory::MacOS) {
windowHandle()->setSurfaceType(QWindow::MetalSurface);
} else {
windowHandle()->setSurfaceType(QWindow::VulkanSurface);
}
if (!Vulkan::Context::init_loader(nullptr)) {
panic("Could not initialize Vulkan ICD");
}
qtVkInstanceFactory = std::make_unique<QtInstanceFactory>();
windowHandle()->setVulkanInstance(&qtVkInstanceFactory->handle);
windowHandle()->create();
wsiPlatform = std::make_shared<QtWSIPlatform>(windowHandle());
windowInfo = std::make_shared<QtPrdpWindowInfo>(windowHandle());
n64::Core &core = n64::Core::GetInstance();
core.parallel.Init(wsiPlatform, windowInfo, core.GetMem().GetRDRAMPtr());
core.parallel.Init(wsiPlatform, windowInfo, qtVkInstanceFactory.get(), core.GetMem().GetRDRAMPtr());
}
+39 -13
View File
@@ -1,4 +1,5 @@
#pragma once
#undef signals
#include <ParallelRDPWrapper.hpp>
#include <QVulkanWindow>
#include <QMainWindow>
@@ -8,12 +9,47 @@ struct InputSettings;
namespace n64 {
struct Core;
}
class QtPrdpWindowInfo;
class QtWSIPlatform;
struct QtInstanceFactory : Vulkan::InstanceFactory {
VkInstance create_instance(const VkInstanceCreateInfo *info) override {
handle.setApiVersion({1, 3, 0});
QByteArrayList exts;
for (int i = 0; i < info->enabledExtensionCount; i++) {
exts.push_back(QByteArray::fromStdString(info->ppEnabledExtensionNames[i]));
}
QByteArrayList layers;
for (int i = 0; i < info->enabledLayerCount; i++) {
layers.push_back(QByteArray::fromStdString(info->ppEnabledLayerNames[i]));
}
handle.setExtensions(exts);
handle.setLayers(layers);
handle.create();
return handle.vkInstance();
}
QVulkanInstance handle;
};
class RenderWidget final : public QWidget {
QVulkanInstance inst;
public:
explicit RenderWidget();
std::unique_ptr<QtInstanceFactory> qtVkInstanceFactory;
std::shared_ptr<ParallelRDP::WindowInfo> windowInfo;
std::shared_ptr<QtWSIPlatform> wsiPlatform;
};
class QtPrdpWindowInfo final : public ParallelRDP::WindowInfo {
public:
explicit QtPrdpWindowInfo(QWindow *window) : window(window) {}
CoordinatePair get_window_size() override {
return CoordinatePair{static_cast<float>(window->size().width()), static_cast<float>(window->size().height())};
return CoordinatePair{static_cast<float>(window->width()), static_cast<float>(window->height())};
}
private:
@@ -27,12 +63,11 @@ class QtWSIPlatform final : public Vulkan::WSIPlatform {
std::vector<const char *> get_instance_extensions() override {
auto vec = std::vector<const char *>();
u32 extCount;
auto extensions = window->vulkanInstance()->extensions();
vec.resize(extensions.size());
for (u32 i = 0; i < extCount; i++) {
for (u32 i = 0; i < extensions.size(); i++) {
vec[i] = extensions[i];
}
@@ -40,7 +75,7 @@ class QtWSIPlatform final : public Vulkan::WSIPlatform {
}
VkSurfaceKHR create_surface(VkInstance instance, VkPhysicalDevice pDevice) override {
return (surface = QVulkanInstance::surfaceForWindow(window));
return QVulkanInstance::surfaceForWindow(window);
}
void destroy_surface(VkInstance instance, VkSurfaceKHR surface) override {}
@@ -61,16 +96,7 @@ class QtWSIPlatform final : public Vulkan::WSIPlatform {
VkApplicationInfo appInfo{.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .apiVersion = VK_API_VERSION_1_3};
QWindow *window{};
VkSurfaceKHR surface;
private:
bool gamepadConnected = false;
};
class RenderWidget final : QVulkanWindow {
public:
explicit RenderWidget(QWindow *);
std::shared_ptr<ParallelRDP::WindowInfo> windowInfo;
std::shared_ptr<QtWSIPlatform> wsiPlatform;
};
+3 -3
View File
@@ -1,9 +1,9 @@
#include <GeneralSettings.hpp>
#include <Options.hpp>
#include <log.hpp>
#include <QSettings>
#include <QFileDialog>
#include <QCoreApplication>
#include <log.hpp>
GeneralSettings::GeneralSettings() {
QSettings settings;
@@ -18,10 +18,10 @@ GeneralSettings::GeneralSettings() {
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
selectedFolderLabel->setText(dir);
Options::SetSavesPath(dir.toStdString());
settings.setValue("general/saves_path", dir);
settings.setValue("general/savesPath", dir);
});
h = new QHBoxLayout(this);
h = new QHBoxLayout();
h->addWidget(description);
h->addWidget(selectedFolderLabel);
+7 -4
View File
@@ -4,11 +4,14 @@
SettingsWindow::SettingsWindow() {
hide();
QSettings settings;
general = new GeneralSettings();
cpu = new CPUSettings();
audio = new AudioSettings();
categories = new QTabWidget(this);
categories->addTab(&general, "General");
categories->addTab(&cpu, "MIPS VR4300i");
categories->addTab(&audio, "Audio");
categories->addTab(general, "General");
categories->addTab(cpu, "MIPS VR4300i");
categories->addTab(audio, "Audio");
v = new QVBoxLayout(this);
v->addWidget(categories);
+4 -5
View File
@@ -7,14 +7,13 @@
#include <AudioSettings.hpp>
#include <CPUSettings.hpp>
class SettingsWindow final : QWidget {
class SettingsWindow final : public QWidget {
QTabWidget *categories;
GeneralSettings general;
AudioSettings audio;
CPUSettings cpu;
GeneralSettings *general;
AudioSettings *audio;
CPUSettings *cpu;
QVBoxLayout *v;
public:
SettingsWindow();
void show() { QWidget::show(); }
};
+5 -14
View File
@@ -1,23 +1,14 @@
#include <QCoreApplication>
#include <KaizenGui.hpp>
#include <cflags.hpp>
#include <QCoreApplication>
#include <QApplication>
int main(const int argc, char **argv) {
int main(int argc, char **argv) {
QCoreApplication::setOrganizationName("kaizen");
QCoreApplication::setOrganizationDomain("irco.sh");
QCoreApplication::setApplicationName("Kaizen");
QApplication app(argc, argv);
KaizenGui kaizenGui;
cflags::cflags flags;
flags.add_string_callback(
'\0', "rom", [&kaizenGui](const std::string &v) { kaizenGui.LoadROM(v); }, "Rom to launch from command-line");
flags.add_string_callback(
'\0', "movie", [](const std::string &v) { KaizenGui::LoadTAS(v); }, "Mupen Movie to replay");
if (!flags.parse(argc, argv))
return -1;
kaizenGui.run();
return 0;
return app.exec();
}