mirror of
https://github.com/Mr-Wiseguy/Zelda64Recomp.git
synced 2024-11-25 06:06:58 +01:00
Delayed game start, made UI hot reload include all files, passed input
events to UI
This commit is contained in:
parent
346e298eb1
commit
91611c9c33
9
include/recomp_ui.h
Normal file
9
include/recomp_ui.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef __RECOMP_UI__
|
||||
#define __RECOMP_UI__
|
||||
|
||||
#include "SDL.h"
|
||||
|
||||
void queue_event(const SDL_Event& event);
|
||||
bool try_deque_event(SDL_Event& out);
|
||||
|
||||
#endif
|
@ -95,6 +95,8 @@ extern "C" void osViSetEvent(RDRAM_ARG PTR(OSMesgQueue) mq_, OSMesg msg, u32 ret
|
||||
|
||||
uint64_t total_vis = 0;
|
||||
|
||||
void set_dummy_vi();
|
||||
|
||||
void vi_thread_func() {
|
||||
Multilibultra::set_native_thread_name("VI Thread");
|
||||
// This thread should be prioritized over every other thread in the application, as it's what allows
|
||||
@ -131,11 +133,24 @@ void vi_thread_func() {
|
||||
if (remaining_retraces == 0) {
|
||||
remaining_retraces = events_context.vi.retrace_count;
|
||||
|
||||
if (events_context.vi.mq != NULLPTR) {
|
||||
if (osSendMesg(PASS_RDRAM events_context.vi.mq, events_context.vi.msg, OS_MESG_NOBLOCK) == -1) {
|
||||
//printf("Game skipped a VI frame!\n");
|
||||
if (Multilibultra::is_game_started()) {
|
||||
if (events_context.vi.mq != NULLPTR) {
|
||||
if (osSendMesg(PASS_RDRAM events_context.vi.mq, events_context.vi.msg, OS_MESG_NOBLOCK) == -1) {
|
||||
//printf("Game skipped a VI frame!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
set_dummy_vi();
|
||||
static bool swap = false;
|
||||
uint32_t vi_origin = 0x400 + 0x280; // Skip initial RDRAM contents and add the usual origin offset
|
||||
// Offset by one FB every other frame so RT64 continues drawing
|
||||
if (swap) {
|
||||
vi_origin += 0x25800;
|
||||
}
|
||||
osViSwapBuffer(rdram, vi_origin);
|
||||
swap = !swap;
|
||||
}
|
||||
}
|
||||
if (events_context.ai.mq != NULLPTR) {
|
||||
if (osSendMesg(PASS_RDRAM events_context.ai.mq, events_context.ai.msg, OS_MESG_NOBLOCK) == -1) {
|
||||
@ -238,7 +253,6 @@ void task_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_rea
|
||||
|
||||
void gfx_thread_func(uint8_t* rdram, uint8_t* rom, std::atomic_flag* thread_ready, Multilibultra::WindowHandle window_handle) {
|
||||
using namespace std::chrono_literals;
|
||||
Multilibultra::gfx_callbacks_t::gfx_data_t gfx_data{};
|
||||
|
||||
Multilibultra::set_native_thread_name("Gfx Thread");
|
||||
Multilibultra::set_native_thread_priority(Multilibultra::ThreadPriority::Normal);
|
||||
@ -291,6 +305,22 @@ uint32_t hstart = 0;
|
||||
uint32_t vi_origin_offset = 320 * sizeof(uint16_t);
|
||||
bool vi_black = false;
|
||||
|
||||
void set_dummy_vi() {
|
||||
VI_STATUS_REG = 0x311E;
|
||||
VI_WIDTH_REG = 0x140;
|
||||
VI_V_SYNC_REG = 0x20D;
|
||||
VI_H_SYNC_REG = 0xC15;
|
||||
VI_LEAP_REG = 0x0C150C15;
|
||||
hstart = 0x006C02EC;
|
||||
VI_X_SCALE_REG = 0x200;
|
||||
VI_V_CURRENT_LINE_REG = 0x0;
|
||||
vi_origin_offset = 0x280;
|
||||
VI_Y_SCALE_REG = 0x400;
|
||||
VI_V_START_REG = 0x2501FF;
|
||||
VI_V_BURST_REG = 0xE0204;
|
||||
VI_INTR_REG = 0x2;
|
||||
}
|
||||
|
||||
extern "C" void osViSwapBuffer(RDRAM_ARG PTR(void) frameBufPtr) {
|
||||
if (vi_black) {
|
||||
VI_H_START_REG = 0;
|
||||
|
@ -123,14 +123,8 @@ struct gfx_callbacks_t {
|
||||
update_gfx_t* update_gfx;
|
||||
};
|
||||
void start(WindowHandle window_handle, const audio_callbacks_t& audio_callbacks, const input_callbacks_t& input_callbacks, const gfx_callbacks_t& gfx_callbacks);
|
||||
|
||||
class preemption_guard {
|
||||
public:
|
||||
preemption_guard();
|
||||
~preemption_guard();
|
||||
private:
|
||||
std::lock_guard<std::mutex> lock;
|
||||
};
|
||||
void start_game(int game);
|
||||
bool is_game_started();
|
||||
|
||||
} // namespace Multilibultra
|
||||
|
||||
|
@ -258,16 +258,6 @@ void enable_preemption() {
|
||||
#pragma warning( pop )
|
||||
}
|
||||
|
||||
// lock's constructor is called first, so can_preempt is set after locking
|
||||
preemption_guard::preemption_guard() : lock{scheduler_context.premption_mutex} {
|
||||
scheduler_context.can_preempt = false;
|
||||
}
|
||||
|
||||
// lock's destructor is called last, so can_preempt is set before unlocking
|
||||
preemption_guard::~preemption_guard() {
|
||||
scheduler_context.can_preempt = true;
|
||||
}
|
||||
|
||||
void notify_scheduler() {
|
||||
std::lock_guard lock{scheduler_context.mutex};
|
||||
scheduler_context.notify_count.fetch_add(1);
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include "SDL2/SDL_syswm.h"
|
||||
#endif
|
||||
|
||||
#include "recomp_ui.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
@ -138,6 +140,9 @@ int sdl_event_filter(void* userdata, SDL_Event* event) {
|
||||
case SDL_EventType::SDL_QUIT:
|
||||
std::quick_exit(EXIT_SUCCESS);
|
||||
break;
|
||||
default:
|
||||
queue_event(*event);
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@ -153,7 +158,7 @@ Multilibultra::gfx_callbacks_t::gfx_data_t create_gfx() {
|
||||
SDL_Window* window;
|
||||
|
||||
Multilibultra::WindowHandle create_window(Multilibultra::gfx_callbacks_t::gfx_data_t) {
|
||||
window = SDL_CreateWindow("Majora's Mask", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_RESIZABLE);
|
||||
window = SDL_CreateWindow("Recomp", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_RESIZABLE);
|
||||
|
||||
if (window == nullptr) {
|
||||
exit_error("Failed to create window: %s\n", SDL_GetError());
|
||||
|
@ -183,11 +183,16 @@ EXPORT extern "C" void init() {
|
||||
MEM_W(osMemSize, 0) = 8 * 1024 * 1024; // 8MB
|
||||
}
|
||||
|
||||
// LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
||||
// return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
// }
|
||||
std::atomic_int game_started = -1;
|
||||
|
||||
static Multilibultra::gfx_callbacks_t gfx_callbacks;
|
||||
void Multilibultra::start_game(int game) {
|
||||
game_started.store(game);
|
||||
game_started.notify_all();
|
||||
}
|
||||
|
||||
bool Multilibultra::is_game_started() {
|
||||
return game_started.load() != -1;
|
||||
}
|
||||
|
||||
void set_audio_callbacks(const Multilibultra::audio_callbacks_t& callbacks);
|
||||
void set_input_callbacks(const Multilibultra::input_callbacks_t& callback);
|
||||
@ -220,7 +225,13 @@ void Multilibultra::start(WindowHandle window_handle, const audio_callbacks_t& a
|
||||
|
||||
Multilibultra::preinit(rdram_buffer.get(), rom.get(), window_handle);
|
||||
|
||||
recomp_entrypoint(rdram_buffer.get(), &context);
|
||||
game_started.wait(-1);
|
||||
|
||||
switch (game_started.load()) {
|
||||
case 0:
|
||||
recomp_entrypoint(rdram_buffer.get(), &context);
|
||||
break;
|
||||
}
|
||||
|
||||
debug_printf("[Recomp] Quitting\n");
|
||||
}, window_handle};
|
||||
@ -229,7 +240,7 @@ void Multilibultra::start(WindowHandle window_handle, const audio_callbacks_t& a
|
||||
using namespace std::chrono_literals;
|
||||
std::this_thread::sleep_for(1ms);
|
||||
if (gfx_callbacks.update_gfx != nullptr) {
|
||||
gfx_callbacks.update_gfx(nullptr);
|
||||
gfx_callbacks.update_gfx(gfx_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
78
src/ui.cpp
78
src/ui.cpp
@ -5,6 +5,8 @@
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
#include "concurrentqueue.h"
|
||||
|
||||
#include "rt64_layer.h"
|
||||
#include "rt64_render_hooks.h"
|
||||
#include "rt64_render_interface_builders.h"
|
||||
@ -116,6 +118,8 @@ class RmlRenderInterface_RT64 : public Rml::RenderInterface {
|
||||
int scissor_y_ = 0;
|
||||
int scissor_width_ = 0;
|
||||
int scissor_height_ = 0;
|
||||
int window_width_ = 0;
|
||||
int window_height_ = 0;
|
||||
Rml::Matrix4f projection_mtx_ = Rml::Matrix4f::Identity();
|
||||
Rml::Matrix4f transform_ = Rml::Matrix4f::Identity();
|
||||
Rml::Matrix4f mvp_ = Rml::Matrix4f::Identity();
|
||||
@ -274,11 +278,16 @@ public:
|
||||
uint32_t total_bytes = vert_size_bytes + index_size_bytes;
|
||||
uint32_t index_bytes_start = vert_size_bytes;
|
||||
|
||||
if (textures_.size() == 0) {
|
||||
// Create a 1x1 pixel white texture as the first handle
|
||||
Rml::byte white_pixel[] = { 255, 255, 255, 255 };
|
||||
Rml::TextureHandle dummy_handle;
|
||||
GenerateTexture(dummy_handle, white_pixel, Rml::Vector2i{ 1,1 });
|
||||
|
||||
if (!textures_.contains(texture)) {
|
||||
if (texture == 0) {
|
||||
// Create a 1x1 pixel white texture as the first handle
|
||||
Rml::byte white_pixel[] = { 255, 255, 255, 255 };
|
||||
create_texture(0, white_pixel, Rml::Vector2i{ 1,1 });
|
||||
}
|
||||
else {
|
||||
assert(false && "Rendered without texture!");
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t upload_buffer_offset = allocate_upload_data(total_bytes);
|
||||
@ -325,17 +334,19 @@ public:
|
||||
};
|
||||
list_->barriers(usage_barriers, uint32_t(std::size(usage_barriers)));
|
||||
|
||||
// TODO set scissor, viewport
|
||||
//if (scissor_enabled_) {
|
||||
list_->setViewports(RT64::RenderViewport{ 0, 0, float(scissor_width_), float(scissor_height_) });
|
||||
list_->setScissors(RT64::RenderRect{ 0, 0, scissor_width_, scissor_height_ });
|
||||
//}
|
||||
list_->setViewports(RT64::RenderViewport{ 0, 0, float(window_width_), float(window_height_) });
|
||||
if (scissor_enabled_) {
|
||||
list_->setScissors(RT64::RenderRect{ scissor_x_, scissor_y_, scissor_width_, scissor_height_ });
|
||||
}
|
||||
else {
|
||||
list_->setScissors(RT64::RenderRect{ 0, 0, window_width_, window_height_ });
|
||||
}
|
||||
|
||||
RT64::RenderIndexBufferView index_view{index_buffer_->at(0), index_size_bytes, RT64::RenderFormat::R32_UINT};
|
||||
list_->setIndexBuffer(&index_view);
|
||||
RT64::RenderVertexBufferView vertex_view{vertex_buffer_->at(0), vert_size_bytes};
|
||||
list_->setVertexBuffers(0, &vertex_view, 1, &vertex_slot_);
|
||||
list_->setGraphicsDescriptorHeap(textures_[texture].heap.get());
|
||||
list_->setGraphicsDescriptorHeap(textures_.at(texture).heap.get());
|
||||
|
||||
RmlPushConstants constants{
|
||||
.transform = mvp_,
|
||||
@ -420,6 +431,7 @@ public:
|
||||
texture_dimensions.x = size_x;
|
||||
texture_dimensions.y = size_y;
|
||||
|
||||
texture_handle = texture_count_++;
|
||||
create_texture(texture_handle, reinterpret_cast<const Rml::byte*>(file_data.data() + 18), texture_dimensions, true);
|
||||
|
||||
return true;
|
||||
@ -429,11 +441,11 @@ public:
|
||||
}
|
||||
|
||||
bool GenerateTexture(Rml::TextureHandle& texture_handle, const Rml::byte* source, const Rml::Vector2i& source_dimensions) override {
|
||||
texture_handle = texture_count_++;
|
||||
return create_texture(texture_handle, source, source_dimensions);
|
||||
}
|
||||
|
||||
bool create_texture(Rml::TextureHandle& texture_handle, const Rml::byte* source, const Rml::Vector2i& source_dimensions, bool flip_y = false) {
|
||||
texture_handle = texture_count_++;
|
||||
bool create_texture(Rml::TextureHandle texture_handle, const Rml::byte* source, const Rml::Vector2i& source_dimensions, bool flip_y = false) {
|
||||
std::unique_ptr<RT64::RenderTexture> texture =
|
||||
render_context_->device->createTexture(RT64::RenderTextureDesc::Texture2D(source_dimensions.x, source_dimensions.y, 1, RmlTextureFormat));
|
||||
|
||||
@ -524,6 +536,9 @@ public:
|
||||
list_->setPipeline(pipeline_.get());
|
||||
list_->setGraphicsPipelineLayout(layout_.get());
|
||||
|
||||
window_width_ = image_width;
|
||||
window_height_ = image_height;
|
||||
|
||||
projection_mtx_ = Rml::Matrix4f::ProjectOrtho(0.0f, static_cast<float>(image_width), static_cast<float>(image_height), 0.0f, -10000, 10000);
|
||||
recalculate_mvp();
|
||||
|
||||
@ -565,6 +580,10 @@ extern SDL_Window* window;
|
||||
|
||||
void load_document() {
|
||||
if (UIContext.render.document) {
|
||||
UIContext.render.document->ReloadStyleSheet();
|
||||
Rml::ReleaseTextures();
|
||||
Rml::ReleaseMemoryPools();
|
||||
UIContext.render.document->Hide();
|
||||
UIContext.render.document->Close();
|
||||
// Documents are owned by RmlUi, so we don't have anything to free here.
|
||||
UIContext.render.document = nullptr;
|
||||
@ -620,15 +639,17 @@ void init_hook(RT64::RenderInterface* interface, RT64::RenderDevice* device) {
|
||||
load_document();
|
||||
}
|
||||
|
||||
moodycamel::ConcurrentQueue<SDL_Event> ui_event_queue{};
|
||||
|
||||
void queue_event(const SDL_Event& event) {
|
||||
ui_event_queue.enqueue(event);
|
||||
}
|
||||
|
||||
bool try_deque_event(SDL_Event& out) {
|
||||
return ui_event_queue.try_dequeue(out);
|
||||
}
|
||||
|
||||
void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderTexture* swap_chain_texture) {
|
||||
command_list->barriers(RT64::RenderTextureBarrier::Transition(swap_chain_texture, RT64::RenderTextureState::RENDER_TARGET));
|
||||
// command_list->setGraphicsPipelineLayout(UIContext.render.layout.get());
|
||||
// command_list->setPipeline(UIContext.render.pipeline.get());
|
||||
// command_list->setIndexBuffer(&UIContext.render.index_buffer_view);
|
||||
// command_list->drawIndexedInstanced(6, 1, 0, 0, 0);
|
||||
|
||||
// TODO process SDL events
|
||||
|
||||
int num_keys;
|
||||
const Uint8* key_state = SDL_GetKeyboardState(&num_keys);
|
||||
|
||||
@ -637,13 +658,26 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderTexture* swap_
|
||||
bool reload_sheets = is_reload_held && !was_reload_held;
|
||||
was_reload_held = is_reload_held;
|
||||
|
||||
static bool menu_open = false;
|
||||
static bool menu_open = true;
|
||||
static bool was_toggle_menu_held = false;
|
||||
bool is_toggle_menu_held = key_state[SDL_SCANCODE_M] != 0;
|
||||
if (is_toggle_menu_held && !was_toggle_menu_held) {
|
||||
menu_open = !menu_open;
|
||||
}
|
||||
was_toggle_menu_held = is_toggle_menu_held;
|
||||
|
||||
static bool was_start_game_held = false;
|
||||
bool is_start_game_held = key_state[SDL_SCANCODE_SPACE] != 0;
|
||||
if (is_start_game_held && !was_start_game_held) {
|
||||
Multilibultra::start_game(0);
|
||||
}
|
||||
was_start_game_held = is_start_game_held;
|
||||
|
||||
SDL_Event cur_event{};
|
||||
|
||||
while (try_deque_event(cur_event)) {
|
||||
RmlSDL::InputEventHandler(UIContext.rml.context, cur_event);
|
||||
}
|
||||
|
||||
if (menu_open) {
|
||||
int width, height;
|
||||
|
Loading…
Reference in New Issue
Block a user