From d071adea9ae1ebbef9f642e0174bb0ced684762e Mon Sep 17 00:00:00 2001 From: flyinghead Date: Sun, 28 Nov 2021 16:19:03 +0100 Subject: [PATCH] dx11: fix modifier volumes, rotation. sdl: on-screen keyboard support dx11: Back face stencil ops need to be set dx11: Rotate CCW instead of CW dx11: Present with NO_WAIT when fast forwarding sdl: support for on-screen keyboard. Delay keys up by one frame to allow quick key presses. ui: better fix to wait for future on UWP --- core/rend/dx11/dx11_renderstate.h | 4 +- core/rend/dx11/dx11_shaders.cpp | 2 +- core/rend/dx11/dx11context.cpp | 4 +- core/rend/gui.cpp | 87 +++++++++++++++++++------------ core/rend/gui.h | 1 + core/rend/gui_util.h | 54 +++++++++++-------- core/sdl/sdl.cpp | 18 +++++++ 7 files changed, 110 insertions(+), 60 deletions(-) diff --git a/core/rend/dx11/dx11_renderstate.h b/core/rend/dx11/dx11_renderstate.h index f77f5b8a2..6ef1d2308 100644 --- a/core/rend/dx11/dx11_renderstate.h +++ b/core/rend/dx11/dx11_renderstate.h @@ -72,11 +72,11 @@ public: desc.DepthWriteMask = depthWrite ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; desc.DepthFunc = Zfunction[depthFunc]; desc.StencilEnable = stencil; - desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; + desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP; + desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE; desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; desc.BackFace = desc.FrontFace; desc.StencilWriteMask = 0xFF; - desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_REPLACE; theDX11Context.getDevice()->CreateDepthStencilState(&desc, &state.get()); } return state; diff --git a/core/rend/dx11/dx11_shaders.cpp b/core/rend/dx11/dx11_shaders.cpp index 30e0a7a8c..350faec70 100644 --- a/core/rend/dx11/dx11_shaders.cpp +++ b/core/rend/dx11/dx11_shaders.cpp @@ -287,7 +287,7 @@ VertexOut main(in VertexIn vin) #if ROTATE == 0 vo.pos = float4(vin.pos, 0.f, 1.f); #else - vo.pos = float4(vin.pos.y, -vin.pos.x, 0.f, 1.f); + vo.pos = float4(-vin.pos.y, vin.pos.x, 0.f, 1.f); #endif vo.uv = vin.uv; diff --git a/core/rend/dx11/dx11context.cpp b/core/rend/dx11/dx11context.cpp index acd9e4d54..9e9124e70 100644 --- a/core/rend/dx11/dx11context.cpp +++ b/core/rend/dx11/dx11context.cpp @@ -179,13 +179,13 @@ void DX11Context::Present() return; frameRendered = false; bool swapOnVSync = !settings.input.fastForwardMode && config::VSync; - HRESULT hr = swapchain->Present(swapOnVSync ? 1 : 0, 0); + HRESULT hr = swapchain->Present(swapOnVSync ? 1 : 0, !swapOnVSync ? DXGI_PRESENT_DO_NOT_WAIT : 0); if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { WARN_LOG(RENDERER, "Present failed: device removed/reset"); handleDeviceLost(); } - else if (FAILED(hr)) + else if (hr != DXGI_ERROR_WAS_STILL_DRAWING && FAILED(hr)) WARN_LOG(RENDERER, "Present failed %x", hr); } diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index 7f877d3f4..6c295dcac 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -63,6 +63,8 @@ static bool error_msg_shown; static std::string osd_message; static double osd_message_end; static std::mutex osd_message_mutex; +static void (*showOnScreenKeyboard)(bool show); +static bool keysUpNextFrame[512]; static int map_system = 0; static void reset_vmus(); @@ -278,6 +280,11 @@ void gui_keyboard_key(u8 keyCode, bool pressed, u8 modifiers) if (!inited) return; ImGuiIO& io = ImGui::GetIO(); + if (!pressed && io.KeysDown[keyCode]) + { + keysUpNextFrame[keyCode] = true; + return; + } io.KeyCtrl = (modifiers & (0x01 | 0x10)) != 0; io.KeyShift = (modifiers & (0x02 | 0x20)) != 0; io.KeysDown[keyCode] = pressed; @@ -314,7 +321,7 @@ void gui_set_mouse_wheel(float delta) mouseWheel += delta; } -static void ImGui_Impl_NewFrame() +static void gui_newFrame() { imguiDriver->newFrame(); ImGui::GetIO().DisplaySize.x = settings.display.width; @@ -367,6 +374,30 @@ static void ImGui_Impl_NewFrame() io.NavInputs[ImGuiNavInput_LStickDown] = 0.f; ImGui::GetStyle().Colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.06f, 0.06f, 0.06f, 0.94f); + + if (showOnScreenKeyboard != nullptr) + showOnScreenKeyboard(io.WantTextInput); +} + +static void delayedKeysUp() +{ + ImGuiIO& io = ImGui::GetIO(); + for (int i = 0; i < ARRAY_SIZE(keysUpNextFrame); i++) + if (keysUpNextFrame[i]) + io.KeysDown[i] = false; + memset(keysUpNextFrame, 0, sizeof(keysUpNextFrame)); +} + +static void gui_endFrame() +{ + ImGui::Render(); + imguiDriver->renderDrawData(ImGui::GetDrawData()); + delayedKeysUp(); +} + +void gui_setOnScreenKeyboardCallback(void (*callback)(bool show)) +{ + showOnScreenKeyboard = callback; } void gui_set_insets(int left, int right, int top, int bottom) @@ -2275,23 +2306,25 @@ static void gui_network_start() if (networkStatus.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) { - try { - if (networkStatus.get()) - { - gui_state = GuiState::Closed; - ImGui::Text("Starting..."); - } - else - { - gui_state = GuiState::Main; + ImGui::Text("Starting..."); + gui_deAsync([] { + try { + if (networkStatus.get()) + { + gui_state = GuiState::Closed; + } + else + { + emu.unloadGame(); + gui_state = GuiState::Main; + } + } catch (const FlycastException& e) { + NetworkHandshake::instance->stop(); emu.unloadGame(); + gui_error(e.what()); + gui_state = GuiState::Main; } - } catch (const FlycastException& e) { - NetworkHandshake::instance->stop(); - gui_state = GuiState::Main; - emu.unloadGame(); - gui_error(e.what()); - } + }); } else { @@ -2307,9 +2340,7 @@ static void gui_network_start() if (ImGui::Button("Cancel", ImVec2(100.f * scaling, 0.f))) { NetworkHandshake::instance->stop(); -#ifdef TARGET_UWP - static std::future f; - f = std::async(std::launch::async, [] { + gui_deAsync([] { try { networkStatus.get(); } @@ -2318,14 +2349,6 @@ static void gui_network_start() emu.unloadGame(); gui_state = GuiState::Main; }); -#else - try { - networkStatus.get(); - } catch (const FlycastException& e) { - } - gui_state = GuiState::Main; - emu.unloadGame(); -#endif } ImGui::PopStyleVar(); @@ -2405,7 +2428,7 @@ void gui_display_ui() } } - ImGui_Impl_NewFrame(); + gui_newFrame(); ImGui::NewFrame(); error_msg_shown = false; @@ -2450,8 +2473,7 @@ void gui_display_ui() break; } error_popup(); - ImGui::Render(); - imguiDriver->renderDrawData(ImGui::GetDrawData()); + gui_endFrame(); if (gui_state == GuiState::Closed) emu.start(); @@ -2491,7 +2513,7 @@ void gui_display_osd() // if (!message.empty() || config::FloatVMUs || crosshairsNeeded() || (ggpo::active() && config::NetworkStats)) { - ImGui_Impl_NewFrame(); + gui_newFrame(); ImGui::NewFrame(); if (!message.empty()) @@ -2518,8 +2540,7 @@ void gui_display_osd() } lua::overlay(); - ImGui::Render(); - imguiDriver->renderDrawData(ImGui::GetDrawData()); + gui_endFrame(); } } diff --git a/core/rend/gui.h b/core/rend/gui.h index 62787553e..4a91ba48c 100644 --- a/core/rend/gui.h +++ b/core/rend/gui.h @@ -43,6 +43,7 @@ void gui_set_insets(int left, int right, int top, int bottom); void gui_stop_game(const std::string& message = ""); void gui_start_game(const std::string& path); void gui_error(const std::string& what); +void gui_setOnScreenKeyboardCallback(void (*callback)(bool show)); extern int screen_dpi; extern float scaling; diff --git a/core/rend/gui_util.h b/core/rend/gui_util.h index 294ffa75f..24c581609 100644 --- a/core/rend/gui_util.h +++ b/core/rend/gui_util.h @@ -25,6 +25,7 @@ #include "imgui/imgui_internal.h" #include "gui.h" #include "emulator.h" +#include "stdclass.h" typedef bool (*StringCallback)(bool cancelled, std::string selection); @@ -66,6 +67,30 @@ static inline bool operator!=(const ImVec2& l, const ImVec2& r) void fullScreenWindow(bool modal); void windowDragScroll(); +// UWP doesn't allow the UI thread to wait on a future +template +void gui_deAsync(F f) +{ +#ifdef TARGET_UWP + static cResetEvent event; + static std::future future; + + event.Reset(); + future = std::async(std::launch::async, [&f] { + try { + f(); + event.Set(); + } catch (...) { + event.Set(); + throw; + } + }); + event.Wait(); +#else + f(); +#endif +} + class BackgroundGameLoader { public: @@ -80,30 +105,13 @@ public: void cancel() { progress.cancelled = true; -#ifdef TARGET_UWP - if (future.valid()) - { - if (progress.cancelled) - return; - static std::future f; - f = std::async(std::launch::async, [this] { + gui_deAsync([this] { + if (future.valid()) try { future.get(); + } catch (const FlycastException& e) { } - catch (const FlycastException& e) { - } - emu.unloadGame(); - gui_state = GuiState::Main; - }); - return; - } -#else - if (future.valid()) - try { - future.get(); - } catch (const FlycastException& e) { - } -#endif + }); emu.unloadGame(); gui_state = GuiState::Main; } @@ -114,7 +122,9 @@ public: return true; if (future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - future.get(); + gui_deAsync([this] { + future.get(); + }); return true; } return false; diff --git a/core/sdl/sdl.cpp b/core/sdl/sdl.cpp index edc61473c..7b4f5a9c2 100644 --- a/core/sdl/sdl.cpp +++ b/core/sdl/sdl.cpp @@ -195,6 +195,22 @@ void input_sdl_init() for (int joy = 0; joy < 4; joy++) sdl_open_joystick(joy); #endif + if (SDL_HasScreenKeyboardSupport()) + { + NOTICE_LOG(INPUT, "On-screen keyboard supported"); + gui_setOnScreenKeyboardCallback([](bool show) { + // We should be able to use SDL_IsScreenKeyboardShown() but it doesn't seem to work on Xbox + static bool visible; + if (window != nullptr && visible != show) + { + visible = show; + if (show) + SDL_StartTextInput(); + else + SDL_StopTextInput(); + } + }); + } } inline void SDLMouse::setAbsPos(int x, int y) { @@ -248,9 +264,11 @@ void input_sdl_handle() } } break; + case SDL_TEXTINPUT: gui_keyboard_inputUTF8(event.text.text); break; + case SDL_WINDOWEVENT: if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED || event.window.event == SDL_WINDOWEVENT_RESTORED