Add progress indicator when loading a file in case user reads from a server and not a local disk
This commit is contained in:
@@ -152,6 +152,7 @@ target_link_libraries(kaizen PUBLIC imgui SDL3::SDL3 SDL3::SDL3-static cflags::c
|
|||||||
target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED)
|
target_compile_definitions(kaizen PUBLIC SDL_MAIN_HANDLED)
|
||||||
|
|
||||||
if (SANITIZERS)
|
if (SANITIZERS)
|
||||||
|
message("UBSAN AND ASAN: ON")
|
||||||
target_compile_options(kaizen PUBLIC -fsanitize=undefined -fsanitize=address)
|
target_compile_options(kaizen PUBLIC -fsanitize=undefined -fsanitize=address)
|
||||||
target_link_options(kaizen PUBLIC -fsanitize=undefined -fsanitize=address)
|
target_link_options(kaizen PUBLIC -fsanitize=undefined -fsanitize=address)
|
||||||
endif ()
|
endif ()
|
||||||
|
|||||||
55
external/parallel-rdp/ParallelRDPWrapper.cpp
vendored
55
external/parallel-rdp/ParallelRDPWrapper.cpp
vendored
@@ -6,14 +6,13 @@
|
|||||||
#include <resources/frag.spv.h>
|
#include <resources/frag.spv.h>
|
||||||
#include <KaizenGui.hpp>
|
#include <KaizenGui.hpp>
|
||||||
#include <imgui_impl_vulkan.h>
|
#include <imgui_impl_vulkan.h>
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
using namespace Vulkan;
|
using namespace Vulkan;
|
||||||
using namespace RDP;
|
using namespace RDP;
|
||||||
|
|
||||||
bool ParallelRDP::IsFramerateUnlocked() const { return wsi->get_present_mode() != PresentMode::SyncToVBlank; }
|
bool ParallelRDP::IsFramerateUnlocked() const { return wsi->get_present_mode() != PresentMode::SyncToVBlank; }
|
||||||
|
|
||||||
void ParallelRDP::SetFramerateUnlocked(bool unlocked) const {
|
void ParallelRDP::SetFramerateUnlocked(const bool unlocked) const {
|
||||||
if (unlocked) {
|
if (unlocked) {
|
||||||
wsi->set_present_mode(PresentMode::UnlockedForceTearing);
|
wsi->set_present_mode(PresentMode::UnlockedForceTearing);
|
||||||
} else {
|
} else {
|
||||||
@@ -24,11 +23,11 @@ void ParallelRDP::SetFramerateUnlocked(bool unlocked) const {
|
|||||||
Program *fullscreen_quad_program;
|
Program *fullscreen_quad_program;
|
||||||
|
|
||||||
// Copied and modified from WSI::init_context_from_platform
|
// Copied and modified from WSI::init_context_from_platform
|
||||||
Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, unsigned num_thread_indices,
|
Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, const unsigned num_thread_indices,
|
||||||
const Context::SystemHandles &system_handles) {
|
const Context::SystemHandles &system_handles) {
|
||||||
VK_ASSERT(platform);
|
VK_ASSERT(platform);
|
||||||
auto instance_ext = platform->get_instance_extensions();
|
const auto instance_ext = platform->get_instance_extensions();
|
||||||
auto device_ext = platform->get_device_extensions();
|
const auto device_ext = platform->get_device_extensions();
|
||||||
auto new_context = Util::make_handle<Context>();
|
auto new_context = Util::make_handle<Context>();
|
||||||
|
|
||||||
new_context->set_application_info(platform->get_application_info());
|
new_context->set_application_info(platform->get_application_info());
|
||||||
@@ -39,9 +38,9 @@ Util::IntrusivePtr<Context> InitVulkanContext(WSIPlatform *platform, unsigned nu
|
|||||||
panic("Failed to create Vulkan instance.\n");
|
panic("Failed to create Vulkan instance.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
VkSurfaceKHR tmp_surface = platform->create_surface(new_context->get_instance(), VK_NULL_HANDLE);
|
const auto tmp_surface = platform->create_surface(new_context->get_instance(), VK_NULL_HANDLE);
|
||||||
|
|
||||||
bool ret = new_context->init_device(VK_NULL_HANDLE, tmp_surface, device_ext.data(), device_ext.size(),
|
const bool ret = new_context->init_device(VK_NULL_HANDLE, tmp_surface, device_ext.data(), device_ext.size(),
|
||||||
CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT);
|
CONTEXT_CREATION_ENABLE_ADVANCED_WSI_BIT);
|
||||||
|
|
||||||
if (tmp_surface) {
|
if (tmp_surface) {
|
||||||
@@ -61,9 +60,9 @@ void ParallelRDP::LoadWSIPlatform(const std::shared_ptr<WSIPlatform> &wsi_platfo
|
|||||||
wsi->set_backbuffer_srgb(false);
|
wsi->set_backbuffer_srgb(false);
|
||||||
wsi->set_platform(wsi_platform.get());
|
wsi->set_platform(wsi_platform.get());
|
||||||
wsi->set_present_mode(PresentMode::SyncToVBlank);
|
wsi->set_present_mode(PresentMode::SyncToVBlank);
|
||||||
Context::SystemHandles handles;
|
|
||||||
|
|
||||||
if (!wsi->init_from_existing_context(InitVulkanContext(wsi_platform.get(), 1, handles))) {
|
if (constexpr Context::SystemHandles handles;
|
||||||
|
!wsi->init_from_existing_context(InitVulkanContext(wsi_platform.get(), 1, handles))) {
|
||||||
panic("Failed to initialize WSI: init_from_existing_context() failed");
|
panic("Failed to initialize WSI: init_from_existing_context() failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,8 +96,8 @@ void ParallelRDP::Init(const std::shared_ptr<WSIPlatform> &wsiPlatform,
|
|||||||
fragLayout.sets[0].fp_mask = 1;
|
fragLayout.sets[0].fp_mask = 1;
|
||||||
fragLayout.sets[0].array_size[0] = 1;
|
fragLayout.sets[0].array_size[0] = 1;
|
||||||
|
|
||||||
auto sizeVert = sizeof(vertex_shader);
|
constexpr auto sizeVert = sizeof(vertex_shader);
|
||||||
auto sizeFrag = sizeof(fragment_shader);
|
constexpr auto sizeFrag = sizeof(fragment_shader);
|
||||||
|
|
||||||
fullscreen_quad_program = wsi->get_device().request_program(
|
fullscreen_quad_program = wsi->get_device().request_program(
|
||||||
reinterpret_cast<const u32 *>(vertex_shader), sizeVert, reinterpret_cast<const u32 *>(fragment_shader),
|
reinterpret_cast<const u32 *>(vertex_shader), sizeVert, reinterpret_cast<const u32 *>(fragment_shader),
|
||||||
@@ -108,8 +107,8 @@ void ParallelRDP::Init(const std::shared_ptr<WSIPlatform> &wsiPlatform,
|
|||||||
uintptr_t offset = 0;
|
uintptr_t offset = 0;
|
||||||
|
|
||||||
if (wsi->get_device().get_device_features().supports_external_memory_host) {
|
if (wsi->get_device().get_device_features().supports_external_memory_host) {
|
||||||
size_t align = wsi->get_device().get_device_features().host_memory_properties.minImportedHostPointerAlignment;
|
const size_t align = wsi->get_device().get_device_features().host_memory_properties.minImportedHostPointerAlignment;
|
||||||
offset = aligned_rdram & (align - 1);
|
offset = aligned_rdram & align - 1;
|
||||||
aligned_rdram -= offset;
|
aligned_rdram -= offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +127,7 @@ void ParallelRDP::DrawFullscreenTexturedQuad(Util::IntrusivePtr<Image> image,
|
|||||||
cmd->set_texture(0, 0, image->get_view(), StockSampler::LinearClamp);
|
cmd->set_texture(0, 0, image->get_view(), StockSampler::LinearClamp);
|
||||||
cmd->set_program(fullscreen_quad_program);
|
cmd->set_program(fullscreen_quad_program);
|
||||||
cmd->set_quad_state();
|
cmd->set_quad_state();
|
||||||
auto data = static_cast<float *>(cmd->allocate_vertex_data(0, 6 * sizeof(float), 2 * sizeof(float)));
|
const auto data = static_cast<float *>(cmd->allocate_vertex_data(0, 6 * sizeof(float), 2 * sizeof(float)));
|
||||||
data[0] = -1.0f;
|
data[0] = -1.0f;
|
||||||
data[1] = -3.0f;
|
data[1] = -3.0f;
|
||||||
data[2] = -1.0f;
|
data[2] = -1.0f;
|
||||||
@@ -136,15 +135,15 @@ void ParallelRDP::DrawFullscreenTexturedQuad(Util::IntrusivePtr<Image> image,
|
|||||||
data[4] = +3.0f;
|
data[4] = +3.0f;
|
||||||
data[5] = +1.0f;
|
data[5] = +1.0f;
|
||||||
|
|
||||||
auto windowSize = windowInfo->get_window_size();
|
const auto [x, y] = windowInfo->get_window_size();
|
||||||
|
|
||||||
float zoom = std::min(windowSize.x / wsi->get_platform().get_surface_width(),
|
const float zoom = std::min(x / static_cast<float>(wsi->get_platform().get_surface_width()),
|
||||||
windowSize.y / wsi->get_platform().get_surface_height());
|
y / static_cast<float>(wsi->get_platform().get_surface_height()));
|
||||||
|
|
||||||
float width = (wsi->get_platform().get_surface_width() / windowSize.x) * zoom;
|
const float width = static_cast<float>(wsi->get_platform().get_surface_width()) / x * zoom;
|
||||||
float height = (wsi->get_platform().get_surface_height() / windowSize.y) * zoom;
|
const float height = static_cast<float>(wsi->get_platform().get_surface_height()) / y * zoom;
|
||||||
|
|
||||||
float uniform_data[] = {// Size
|
const float uniform_data[] = {// Size
|
||||||
width, height,
|
width, height,
|
||||||
// Offset
|
// Offset
|
||||||
(1.0f - width) * 0.5f, (1.0f - height) * 0.5f};
|
(1.0f - width) * 0.5f, (1.0f - height) * 0.5f};
|
||||||
@@ -190,10 +189,9 @@ void ParallelRDP::UpdateScreen(Util::IntrusivePtr<Image> image) const {
|
|||||||
wsi->end_frame();
|
wsi->end_frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParallelRDP::UpdateScreen(bool playing) const {
|
template <>
|
||||||
if(playing) {
|
void ParallelRDP::UpdateScreen<true>() const {
|
||||||
n64::Core& core = n64::Core::GetInstance();
|
const n64::VI& vi = n64::Core::GetMem().mmio.vi;
|
||||||
n64::VI& vi = core.GetMem().mmio.vi;
|
|
||||||
command_processor->set_vi_register(VIRegister::Control, vi.status.raw);
|
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::Origin, vi.origin);
|
||||||
command_processor->set_vi_register(VIRegister::Width, vi.width);
|
command_processor->set_vi_register(VIRegister::Width, vi.width);
|
||||||
@@ -217,16 +215,17 @@ void ParallelRDP::UpdateScreen(bool playing) const {
|
|||||||
opts.vi.gamma_dither = true;
|
opts.vi.gamma_dither = true;
|
||||||
opts.downscale_steps = true;
|
opts.downscale_steps = true;
|
||||||
opts.crop_overscan_pixels = true;
|
opts.crop_overscan_pixels = true;
|
||||||
Util::IntrusivePtr<Image> image = command_processor->scanout(opts);
|
const Util::IntrusivePtr<Image> image = command_processor->scanout(opts);
|
||||||
UpdateScreen(image);
|
UpdateScreen(image);
|
||||||
command_processor->begin_frame_context();
|
command_processor->begin_frame_context();
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void ParallelRDP::UpdateScreen<false>() const {
|
||||||
UpdateScreen(static_cast<Util::IntrusivePtr<Image>>(nullptr));
|
UpdateScreen(static_cast<Util::IntrusivePtr<Image>>(nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ParallelRDP::EnqueueCommand(int command_length, const u32 *buffer) const {
|
void ParallelRDP::EnqueueCommand(const int command_length, const u32 *buffer) const {
|
||||||
command_processor->enqueue_command(command_length, buffer);
|
command_processor->enqueue_command(command_length, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
5
external/parallel-rdp/ParallelRDPWrapper.hpp
vendored
5
external/parallel-rdp/ParallelRDPWrapper.hpp
vendored
@@ -18,10 +18,11 @@ public:
|
|||||||
void Init(const std::shared_ptr<Vulkan::WSIPlatform> &,
|
void Init(const std::shared_ptr<Vulkan::WSIPlatform> &,
|
||||||
const std::shared_ptr<WindowInfo> &, const u8 *);
|
const std::shared_ptr<WindowInfo> &, const u8 *);
|
||||||
|
|
||||||
void UpdateScreen(bool = true) const;
|
template <bool>
|
||||||
|
void UpdateScreen() const;
|
||||||
void EnqueueCommand(int, const u32 *) const;
|
void EnqueueCommand(int, const u32 *) const;
|
||||||
void OnFullSync() const;
|
void OnFullSync() const;
|
||||||
bool IsFramerateUnlocked() const;
|
[[nodiscard]] bool IsFramerateUnlocked() const;
|
||||||
void SetFramerateUnlocked(bool) const;
|
void SetFramerateUnlocked(bool) const;
|
||||||
|
|
||||||
std::shared_ptr<Vulkan::WSI> wsi;
|
std::shared_ptr<Vulkan::WSI> wsi;
|
||||||
|
|||||||
43
src/frontend/ImGuiImpl/ProgressIndicators.hpp
Normal file
43
src/frontend/ImGuiImpl/ProgressIndicators.hpp
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <imgui_internal.h>
|
||||||
|
|
||||||
|
namespace ImGui {
|
||||||
|
inline bool Spinner(const char* label, const float radius, const int thickness, const ImU32& color) {
|
||||||
|
ImGuiWindow* window = GetCurrentWindow();
|
||||||
|
if (window->SkipItems)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const ImGuiContext & g = *GImGui;
|
||||||
|
const ImGuiStyle& style = g.Style;
|
||||||
|
const ImGuiID id = window->GetID(label);
|
||||||
|
|
||||||
|
const ImVec2 pos = window->DC.CursorPos;
|
||||||
|
const ImVec2 size(radius*2, (radius + style.FramePadding.y)*2);
|
||||||
|
|
||||||
|
const ImRect bb(pos, ImVec2(pos.x + size.x, pos.y + size.y));
|
||||||
|
ItemSize(bb, style.FramePadding.y);
|
||||||
|
if (!ItemAdd(bb, id))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Render
|
||||||
|
window->DrawList->PathClear();
|
||||||
|
|
||||||
|
constexpr int num_segments = 30;
|
||||||
|
const int start = abs(ImSin(g.Time*1.8f)*(num_segments-5));
|
||||||
|
|
||||||
|
const float a_min = IM_PI * 2.0f * static_cast<float>(start) / static_cast<float>(num_segments);
|
||||||
|
constexpr float a_max = IM_PI*2.0f * (static_cast<float>(num_segments) -3) / static_cast<float>(num_segments);
|
||||||
|
|
||||||
|
const auto centre = ImVec2(pos.x+radius, pos.y+radius+style.FramePadding.y);
|
||||||
|
|
||||||
|
for (int i = 0; i < num_segments; i++) {
|
||||||
|
const float a = a_min + static_cast<float>(i) / static_cast<float>(num_segments) * (a_max - a_min);
|
||||||
|
window->DrawList->PathLineTo(ImVec2(centre.x + ImCos(a+g.Time*8) * radius,
|
||||||
|
centre.y + ImSin(a+g.Time*8) * radius));
|
||||||
|
}
|
||||||
|
|
||||||
|
window->DrawList->PathStroke(color, false, thickness);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <imgui_internal.h>
|
#include <imgui_internal.h>
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
namespace ImGui {
|
namespace ImGui {
|
||||||
inline bool BeginMainStatusBar()
|
inline bool BeginMainStatusBar()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include <KaizenGui.hpp>
|
#include <KaizenGui.hpp>
|
||||||
#include <backend/Core.hpp>
|
#include <backend/Core.hpp>
|
||||||
#include <ImGuiImpl/GUI.hpp>
|
#include <ImGuiImpl/GUI.hpp>
|
||||||
|
#include <ImGuiImpl/ProgressIndicators.hpp>
|
||||||
#include <ImGuiImpl/StatusBar.hpp>
|
#include <ImGuiImpl/StatusBar.hpp>
|
||||||
#include <resources/gamecontrollerdb.h>
|
#include <resources/gamecontrollerdb.h>
|
||||||
|
|
||||||
@@ -9,6 +10,10 @@ KaizenGui::KaizenGui() noexcept : window("Kaizen", 800, 600), settingsWindow(win
|
|||||||
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
|
SDL_InitSubSystem(SDL_INIT_GAMEPAD);
|
||||||
|
|
||||||
SDL_AddGamepadMapping(gamecontrollerdb_str);
|
SDL_AddGamepadMapping(gamecontrollerdb_str);
|
||||||
|
|
||||||
|
std::thread fileWorker(&KaizenGui::FileWorker, this);
|
||||||
|
|
||||||
|
fileWorker.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
KaizenGui::~KaizenGui() {
|
KaizenGui::~KaizenGui() {
|
||||||
@@ -16,7 +21,7 @@ KaizenGui::~KaizenGui() {
|
|||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenGui::QueryDevices(SDL_Event event) {
|
void KaizenGui::QueryDevices(const SDL_Event &event) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case SDL_EVENT_GAMEPAD_ADDED:
|
case SDL_EVENT_GAMEPAD_ADDED:
|
||||||
if (!gamepad) {
|
if (!gamepad) {
|
||||||
@@ -36,8 +41,8 @@ void KaizenGui::QueryDevices(SDL_Event event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenGui::HandleInput(SDL_Event event) {
|
void KaizenGui::HandleInput(const SDL_Event &event) {
|
||||||
n64::Core& core = n64::Core::GetInstance();
|
const n64::Core& core = n64::Core::GetInstance();
|
||||||
n64::PIF &pif = n64::Core::GetMem().mmio.si.pif;
|
n64::PIF &pif = n64::Core::GetMem().mmio.si.pif;
|
||||||
switch(event.type) {
|
switch(event.type) {
|
||||||
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
case SDL_EVENT_GAMEPAD_AXIS_MOTION:
|
||||||
@@ -52,7 +57,7 @@ void KaizenGui::HandleInput(SDL_Event event) {
|
|||||||
|
|
||||||
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
|
float xclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTX);
|
||||||
if (xclamped < 0) {
|
if (xclamped < 0) {
|
||||||
xclamped /= float(std::abs(SDL_JOYSTICK_AXIS_MAX));
|
xclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MAX));
|
||||||
} else {
|
} else {
|
||||||
xclamped /= SDL_JOYSTICK_AXIS_MAX;
|
xclamped /= SDL_JOYSTICK_AXIS_MAX;
|
||||||
}
|
}
|
||||||
@@ -61,15 +66,15 @@ void KaizenGui::HandleInput(SDL_Event event) {
|
|||||||
|
|
||||||
float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
|
float yclamped = SDL_GetGamepadAxis(gamepad, SDL_GAMEPAD_AXIS_LEFTY);
|
||||||
if (yclamped < 0) {
|
if (yclamped < 0) {
|
||||||
yclamped /= float(std::abs(SDL_JOYSTICK_AXIS_MIN));
|
yclamped /= static_cast<float>(std::abs(SDL_JOYSTICK_AXIS_MIN));
|
||||||
} else {
|
} else {
|
||||||
yclamped /= SDL_JOYSTICK_AXIS_MAX;
|
yclamped /= SDL_JOYSTICK_AXIS_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
yclamped *= 86;
|
yclamped *= 86;
|
||||||
|
|
||||||
pif.UpdateAxis(0, n64::Controller::Axis::Y, -yclamped);
|
pif.UpdateAxis(0, n64::Controller::Axis::Y, static_cast<s8>(-yclamped));
|
||||||
pif.UpdateAxis(0, n64::Controller::Axis::X, xclamped);
|
pif.UpdateAxis(0, n64::Controller::Axis::X, static_cast<s8>( xclamped));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
|
||||||
@@ -90,7 +95,7 @@ void KaizenGui::HandleInput(SDL_Event event) {
|
|||||||
case SDL_EVENT_KEY_DOWN:
|
case SDL_EVENT_KEY_DOWN:
|
||||||
case SDL_EVENT_KEY_UP:
|
case SDL_EVENT_KEY_UP:
|
||||||
{
|
{
|
||||||
auto keys = SDL_GetKeyboardState(nullptr);
|
const auto keys = SDL_GetKeyboardState(nullptr);
|
||||||
if((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) {
|
if((keys[SDL_SCANCODE_LCTRL] || keys[SDL_SCANCODE_RCTRL]) && keys[SDL_SCANCODE_O]) {
|
||||||
fileDialogOpen = true;
|
fileDialogOpen = true;
|
||||||
}
|
}
|
||||||
@@ -130,15 +135,25 @@ void KaizenGui::HandleInput(SDL_Event event) {
|
|||||||
pif.UpdateButton(0, n64::Controller::Key::DRight, keys[SDL_SCANCODE_L]);
|
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::LT, keys[SDL_SCANCODE_A]);
|
||||||
pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]);
|
pif.UpdateButton(0, n64::Controller::Key::RT, keys[SDL_SCANCODE_S]);
|
||||||
pif.UpdateAxis(0, n64::Controller::Axis::Y, keys[SDL_SCANCODE_UP] ? 86 : keys[SDL_SCANCODE_DOWN] ? -86 : 0);
|
|
||||||
pif.UpdateAxis(0, n64::Controller::Axis::X, keys[SDL_SCANCODE_LEFT] ? -86 : keys[SDL_SCANCODE_RIGHT] ? 86 : 0);
|
if (keys[SDL_SCANCODE_UP]) pif.UpdateAxis(0, n64::Controller::Axis::Y, 86);
|
||||||
|
else pif.UpdateAxis(0, n64::Controller::Axis::Y, 0);
|
||||||
|
|
||||||
|
if (keys[SDL_SCANCODE_DOWN]) pif.UpdateAxis(0, n64::Controller::Axis::Y, -86);
|
||||||
|
else pif.UpdateAxis(0, n64::Controller::Axis::Y, 0);
|
||||||
|
|
||||||
|
if (keys[SDL_SCANCODE_LEFT]) pif.UpdateAxis(0, n64::Controller::Axis::X, -86);
|
||||||
|
else pif.UpdateAxis(0, n64::Controller::Axis::X, 0);
|
||||||
|
|
||||||
|
if (keys[SDL_SCANCODE_RIGHT]) pif.UpdateAxis(0, n64::Controller::Axis::X, 86);
|
||||||
|
else pif.UpdateAxis(0, n64::Controller::Axis::X, 0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<std::optional<u64>, std::optional<Util::Error::MemoryAccess>> RenderErrorMessageDetails() {
|
std::pair<std::optional<s64>, std::optional<Util::Error::MemoryAccess>> RenderErrorMessageDetails() {
|
||||||
auto lastPC = Util::Error::GetLastPC();
|
auto lastPC = Util::Error::GetLastPC();
|
||||||
if(lastPC.has_value()) {
|
if(lastPC.has_value()) {
|
||||||
ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str());
|
ImGui::Text("%s", std::format("Occurred @ PC = {:016X}", Util::Error::GetLastPC().value()).c_str());
|
||||||
@@ -146,10 +161,10 @@ std::tuple<std::optional<u64>, std::optional<Util::Error::MemoryAccess>> RenderE
|
|||||||
|
|
||||||
auto memoryAccess = Util::Error::GetMemoryAccess();
|
auto memoryAccess = Util::Error::GetMemoryAccess();
|
||||||
if(memoryAccess.has_value()) {
|
if(memoryAccess.has_value()) {
|
||||||
auto memoryAccessV = memoryAccess.value();
|
const auto [is_write, size, address, written_val] = memoryAccess.value();
|
||||||
ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", memoryAccessV.is_write ? "Writing" : "Reading",
|
ImGui::Text("%s", std::format("{} {}-bit value @ {:08X}{}", is_write ? "Writing" : "Reading",
|
||||||
(u8)memoryAccessV.size, memoryAccessV.address,
|
static_cast<u8>(size), address,
|
||||||
memoryAccessV.is_write ? std::format(" (value = 0x{:X})", memoryAccessV.written_val) : "")
|
is_write ? std::format(" (value = 0x{:X})", written_val) : "")
|
||||||
.c_str());
|
.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +242,7 @@ void KaizenGui::RenderUI() {
|
|||||||
settingsWindow.render();
|
settingsWindow.render();
|
||||||
debugger.render();
|
debugger.render();
|
||||||
|
|
||||||
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
const ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
||||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||||
|
|
||||||
if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) {
|
if (ImGui::BeginPopupModal("About Kaizen", &aboutOpen, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||||
@@ -258,9 +273,9 @@ void KaizenGui::RenderUI() {
|
|||||||
RenderErrorMessageDetails();
|
RenderErrorMessageDetails();
|
||||||
|
|
||||||
if(n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) {
|
if(n64::Core::GetInstance().romLoaded && !n64::Core::GetInstance().pause) {
|
||||||
bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine();
|
const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine();
|
||||||
bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine();
|
const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine();
|
||||||
bool chooseAnother = ImGui::Button("Choose another ROM");
|
const bool chooseAnother = ImGui::Button("Choose another ROM");
|
||||||
if(ignore || stop || chooseAnother) {
|
if(ignore || stop || chooseAnother) {
|
||||||
Util::Error::SetHandled();
|
Util::Error::SetHandled();
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
@@ -306,10 +321,10 @@ void KaizenGui::RenderUI() {
|
|||||||
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
ImGui::Text(R"(Error message: "%s")", Util::Error::GetError().c_str());
|
||||||
auto [lastPC, memoryAccess] = RenderErrorMessageDetails();
|
auto [lastPC, memoryAccess] = RenderErrorMessageDetails();
|
||||||
|
|
||||||
bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine();
|
const bool ignore = ImGui::Button("Try continuing"); ImGui::SameLine();
|
||||||
bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine();
|
const bool stop = ImGui::Button("Stop emulation"); ImGui::SameLine();
|
||||||
bool chooseAnother = ImGui::Button("Choose another ROM");
|
const bool chooseAnother = ImGui::Button("Choose another ROM");
|
||||||
bool openInDebugger = lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false;
|
const bool openInDebugger = lastPC.has_value() ? ImGui::Button("Add breakpoint at this PC and open the debugger") : false;
|
||||||
if(ignore || stop || chooseAnother || openInDebugger) {
|
if(ignore || stop || chooseAnother || openInDebugger) {
|
||||||
Util::Error::SetHandled();
|
Util::Error::SetHandled();
|
||||||
ImGui::CloseCurrentPopup();
|
ImGui::CloseCurrentPopup();
|
||||||
@@ -346,6 +361,26 @@ void KaizenGui::RenderUI() {
|
|||||||
ImGui::EndMainStatusBar();
|
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).stem().c_str());
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
|
if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
|
||||||
ImGui::UpdatePlatformWindows();
|
ImGui::UpdatePlatformWindows();
|
||||||
@@ -354,20 +389,22 @@ void KaizenGui::RenderUI() {
|
|||||||
|
|
||||||
if(fileDialogOpen) {
|
if(fileDialogOpen) {
|
||||||
fileDialogOpen = false;
|
fileDialogOpen = false;
|
||||||
const SDL_DialogFileFilter filters[] = {{"All files", "*"}, {"Nintendo 64 executable", "n64;z64;v64"}, {"Nintendo 64 executable archive", "rar;tar;zip;7z"}};
|
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 filter) {
|
SDL_ShowOpenFileDialog([](void *userdata, const char * const *filelist, int) {
|
||||||
auto kaizen = (KaizenGui*)userdata;
|
const auto kaizen = static_cast<KaizenGui*>(userdata);
|
||||||
|
|
||||||
if (!filelist) {
|
if (!filelist) {
|
||||||
panic("An error occured: {}", SDL_GetError());
|
panic("An error occured: {}", SDL_GetError());
|
||||||
return;
|
}
|
||||||
} else if (!*filelist) {
|
|
||||||
|
if (!*filelist) {
|
||||||
warn("The user did not select any file.");
|
warn("The user did not select any file.");
|
||||||
warn("Most likely, the dialog was canceled.");
|
warn("Most likely, the dialog was canceled.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
kaizen->LoadROM(*filelist);
|
kaizen->fileToLoad = *filelist;
|
||||||
|
kaizen->shouldDisplaySpinner = true;
|
||||||
}, this, window.getHandle(), filters, 3, nullptr, false);
|
}, this, window.getHandle(), filters, 3, nullptr, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,17 +412,17 @@ void KaizenGui::RenderUI() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if(core.romLoaded) {
|
if(core.romLoaded) {
|
||||||
core.parallel.UpdateScreen();
|
core.parallel.UpdateScreen<true>();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
core.parallel.UpdateScreen(false);
|
core.parallel.UpdateScreen<false>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void KaizenGui::LoadROM(const std::string &path) noexcept {
|
void KaizenGui::LoadROM(const std::string &path) noexcept {
|
||||||
n64::Core& core = n64::Core::GetInstance();
|
n64::Core& core = n64::Core::GetInstance();
|
||||||
core.LoadROM(path);
|
core.LoadROM(path);
|
||||||
const auto gameNameDB = core.GetMem().rom.gameNameDB;
|
const auto gameNameDB = n64::Core::GetMem().rom.gameNameDB;
|
||||||
SDL_SetWindowTitle(window.getHandle(), ("Kaizen - " + gameNameDB).c_str());
|
SDL_SetWindowTitle(window.getHandle(), ("Kaizen - " + gameNameDB).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -405,11 +442,14 @@ void KaizenGui::run() {
|
|||||||
case SDL_EVENT_WINDOW_RESTORED:
|
case SDL_EVENT_WINDOW_RESTORED:
|
||||||
minimized = false;
|
minimized = false;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
QueryDevices(e);
|
QueryDevices(e);
|
||||||
HandleInput(e);
|
HandleInput(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_GetWindowSize(window.getHandle(), &width, &height);
|
||||||
|
|
||||||
emuThread.run();
|
emuThread.run();
|
||||||
RenderUI();
|
RenderUI();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,10 +27,23 @@ public:
|
|||||||
static void LoadTAS(const std::string &path) noexcept;
|
static void LoadTAS(const std::string &path) noexcept;
|
||||||
void LoadROM(const std::string &path) noexcept;
|
void LoadROM(const std::string &path) noexcept;
|
||||||
private:
|
private:
|
||||||
|
int width{}, height{};
|
||||||
bool aboutOpen = false;
|
bool aboutOpen = false;
|
||||||
bool fileDialogOpen = false;
|
bool fileDialogOpen = false;
|
||||||
bool quit = false;
|
bool quit = false;
|
||||||
|
bool shouldDisplaySpinner = false;
|
||||||
|
std::string fileToLoad;
|
||||||
void RenderUI();
|
void RenderUI();
|
||||||
void HandleInput(SDL_Event event);
|
void HandleInput(const SDL_Event &event);
|
||||||
void QueryDevices(SDL_Event event);
|
void QueryDevices(const SDL_Event &event);
|
||||||
|
|
||||||
|
[[noreturn]] void FileWorker() {
|
||||||
|
while (true) {
|
||||||
|
if (!fileToLoad.empty()) {
|
||||||
|
LoadROM(fileToLoad);
|
||||||
|
shouldDisplaySpinner = false;
|
||||||
|
fileToLoad = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include <KaizenGui.hpp>
|
#include <KaizenGui.hpp>
|
||||||
#include <cflags.hpp>
|
#include <cflags.hpp>
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(const int argc, char **argv) {
|
||||||
KaizenGui kaizenGui;
|
KaizenGui kaizenGui;
|
||||||
cflags::cflags flags;
|
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', "rom", [&kaizenGui](const std::string& v) { kaizenGui.LoadROM(v); }, "Rom to launch from command-line");
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ struct Error {
|
|||||||
UNRECOVERABLE,
|
UNRECOVERABLE,
|
||||||
} as_enum;
|
} as_enum;
|
||||||
|
|
||||||
const char* as_c_str() {
|
[[nodiscard]] const char* as_c_str() const {
|
||||||
switch(as_enum) {
|
switch(as_enum) {
|
||||||
case NONE: return "";
|
case NONE: return "";
|
||||||
case WARN: return "Warning";
|
case WARN: return "Warning";
|
||||||
@@ -70,7 +70,7 @@ struct Error {
|
|||||||
} as_enum;
|
} as_enum;
|
||||||
|
|
||||||
|
|
||||||
const char* as_c_str() {
|
[[nodiscard]] const char* as_c_str() const {
|
||||||
switch(as_enum) {
|
switch(as_enum) {
|
||||||
case SCHEDULER_EOL: return "SCHEDULER_EOL";
|
case SCHEDULER_EOL: return "SCHEDULER_EOL";
|
||||||
case SCHEDULER_UNKNOWN: return "SCHEDULER_UNKNOWN";
|
case SCHEDULER_UNKNOWN: return "SCHEDULER_UNKNOWN";
|
||||||
@@ -108,10 +108,8 @@ struct Error {
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <class... Args>
|
template <class... Args>
|
||||||
void Throw (Severity severity,
|
void Throw (const Severity severity, const Type type, const std::optional<u64> lastPC,
|
||||||
Type type,
|
const std::optional<MemoryAccess> memoryAccess,
|
||||||
std::optional<u64> lastPC,
|
|
||||||
std::optional<MemoryAccess> memoryAccess,
|
|
||||||
const std::format_string<Args...> fmt, Args... args) {
|
const std::format_string<Args...> fmt, Args... args) {
|
||||||
this->severity = severity;
|
this->severity = severity;
|
||||||
this->lastPC = lastPC;
|
this->lastPC = lastPC;
|
||||||
@@ -128,7 +126,7 @@ struct Error {
|
|||||||
static std::string& GetError() { return GetInstance().err; }
|
static std::string& GetError() { return GetInstance().err; }
|
||||||
static Severity& GetSeverity() { return GetInstance().severity; }
|
static Severity& GetSeverity() { return GetInstance().severity; }
|
||||||
static Type& GetType() { return GetInstance().type; }
|
static Type& GetType() { return GetInstance().type; }
|
||||||
static std::optional<u64>& GetLastPC() { return GetInstance().lastPC; }
|
static std::optional<s64>& GetLastPC() { return GetInstance().lastPC; }
|
||||||
static std::optional<MemoryAccess>& GetMemoryAccess() { return GetInstance().memoryAccess; }
|
static std::optional<MemoryAccess>& GetMemoryAccess() { return GetInstance().memoryAccess; }
|
||||||
static bool IsHandled() {
|
static bool IsHandled() {
|
||||||
return GetSeverity().as_enum == Severity::NONE;
|
return GetSeverity().as_enum == Severity::NONE;
|
||||||
@@ -145,7 +143,7 @@ private:
|
|||||||
std::string err;
|
std::string err;
|
||||||
Severity severity = {};
|
Severity severity = {};
|
||||||
Type type = {};
|
Type type = {};
|
||||||
std::optional<u64> lastPC = {};
|
std::optional<s64> lastPC = {};
|
||||||
std::optional<MemoryAccess> memoryAccess = {};
|
std::optional<MemoryAccess> memoryAccess = {};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user