diff --git a/include/recomp_ui.h b/include/recomp_ui.h new file mode 100644 index 0000000..b00daed --- /dev/null +++ b/include/recomp_ui.h @@ -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 diff --git a/portultra/events.cpp b/portultra/events.cpp index c1fb9b0..629ed32 100644 --- a/portultra/events.cpp +++ b/portultra/events.cpp @@ -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; diff --git a/portultra/multilibultra.hpp b/portultra/multilibultra.hpp index dc8b51e..487e836 100644 --- a/portultra/multilibultra.hpp +++ b/portultra/multilibultra.hpp @@ -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 lock; -}; +void start_game(int game); +bool is_game_started(); } // namespace Multilibultra diff --git a/portultra/scheduler.cpp b/portultra/scheduler.cpp index f4d7a87..3030683 100644 --- a/portultra/scheduler.cpp +++ b/portultra/scheduler.cpp @@ -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); diff --git a/src/main/main.cpp b/src/main/main.cpp index fd556a5..8611fd1 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -14,6 +14,8 @@ #include "SDL2/SDL_syswm.h" #endif +#include "recomp_ui.h" + #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include @@ -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()); diff --git a/src/recomp.cpp b/src/recomp.cpp index 958b97c..6d5a76b 100644 --- a/src/recomp.cpp +++ b/src/recomp.cpp @@ -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); } } } diff --git a/src/ui.cpp b/src/ui.cpp index 5ed983d..899db7f 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -5,6 +5,8 @@ #include #include +#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(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 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(image_width), static_cast(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 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;