diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 0b73cac64..cb08dbac9 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -31,6 +31,10 @@ jobs: echo "CCACHE_DIR=/tmp/ccache" >> $GITHUB_ENV if: matrix.config.os == 'ubuntu-latest' + - name: Set up build environment (windows-latest) + run: echo "DXSDK_DIR=${Env:HOMEDRIVE}${Env:HOMEPATH}\ccache\" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append + if: matrix.config.os == 'windows-latest' && matrix.config.name != 'x86_64-w64-mingw32' + - uses: actions/cache@v2 with: path: /tmp/ccache @@ -38,6 +42,25 @@ jobs: restore-keys: ccache-${{ matrix.config.os }}- if: matrix.config.os != 'windows-latest' + - uses: actions/cache@v2 + id: cache + with: + path: $HOME/ccache + key: ccache-${{ matrix.config.os }}-${{ github.sha }} + restore-keys: ccache-${{ matrix.config.os }}- + if: matrix.config.os == 'windows-latest' + + - name: Download DX2010 + if: steps.cache.outputs.cache-hit != 'true' && matrix.config.os == 'windows-latest' && matrix.config.name != 'x86_64-w64-mingw32' + run: | + curl -L https://download.microsoft.com/download/a/e/7/ae743f1f-632b-4809-87a9-aa1bb3458e31/DXSDK_Jun10.exe -o _DX2010_.exe + 7z x _DX2010_.exe DXSDK/Include -o_DX2010_ + 7z x _DX2010_.exe DXSDK/Lib/x86 -o_DX2010_ + 7z x _DX2010_.exe DXSDK/Lib/x64 -o_DX2010_ + mv _DX2010_/DXSDK $HOME/ccache + rm -fR _DX*_ _DX*_.exe + shell: bash + - uses: actions/checkout@v2 with: fetch-depth: 0 diff --git a/CMakeLists.txt b/CMakeLists.txt index f89e8d802..563d4a230 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,9 +123,7 @@ if(ALSA_FOUND AND NOT ANDROID) endif() if(WIN32) - # Patch and build SDL - execute_process(COMMAND git apply -p1 ${CMAKE_CURRENT_SOURCE_DIR}/core/deps/patches/SDL.patch - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/core/deps/SDL) + # Build SDL file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/core/deps/SDL") if(CMAKE_GENERATOR MATCHES "Visual Studio") set(SDL_CMAKE_ARCH_ARG -A ${CMAKE_GENERATOR_PLATFORM}) @@ -576,7 +574,6 @@ target_sources(${PROJECT_NAME} PRIVATE core/input/gamepad.h core/input/gamepad_device.cpp core/input/gamepad_device.h - core/input/keyboard_device.cpp core/input/keyboard_device.h core/input/mapping.cpp core/input/mapping.h) @@ -790,6 +787,32 @@ if(USE_VULKAN AND NOT APPLE) core/rend/vulkan/vulkan_renderer.cpp) endif() +if(WIN32) + target_sources(${PROJECT_NAME} PRIVATE + core/rend/dx9/comptr.h + core/rend/dx9/d3d_overlay.h + core/rend/dx9/d3d_overlay.cpp + core/rend/dx9/d3d_renderer.h + core/rend/dx9/d3d_renderer.cpp + core/rend/dx9/d3d_shaders.h + core/rend/dx9/d3d_shaders.cpp + core/rend/dx9/d3d_texture.h + core/rend/dx9/d3d_texture.cpp + core/rend/dx9/dxcontext.h + core/rend/dx9/dxcontext.cpp + core/rend/dx9/imgui_impl_dx9.h + core/rend/dx9/imgui_impl_dx9.cpp) + if(NOT MINGW) + target_include_directories(${PROJECT_NAME} PRIVATE "$ENV{DXSDK_DIR}/Include") + if (CMAKE_SIZEOF_VOID_P EQUAL 8) + target_link_directories(${PROJECT_NAME} PRIVATE "$ENV{DXSDK_DIR}/Lib/x64") + else() + target_link_directories(${PROJECT_NAME} PRIVATE "$ENV{DXSDK_DIR}/Lib/x86") + endif() + endif() + target_link_libraries(${PROJECT_NAME} PRIVATE d3d9 d3dx9) +endif() + if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm.*|ARM.*)") target_include_directories(${PROJECT_NAME} PRIVATE core/deps/vixl) target_sources(${PROJECT_NAME} PRIVATE @@ -943,6 +966,8 @@ elseif(UNIX) elseif(WIN32) if(NOT BUILD_TESTING) target_sources(${PROJECT_NAME} PRIVATE + core/windows/rawinput.cpp + core/windows/rawinput.h core/windows/win_keyboard.h core/windows/winmain.cpp core/windows/xinput_gamepad.h) diff --git a/core/cfg/option.cpp b/core/cfg/option.cpp index 69b6f625c..31399801a 100644 --- a/core/cfg/option.cpp +++ b/core/cfg/option.cpp @@ -77,7 +77,7 @@ Option Fog("rend.Fog", true); Option FloatVMUs("rend.FloatVMUs"); Option Rotate90("rend.Rotate90"); Option PerStripSorting("rend.PerStripSorting"); -Option DelayFrameSwapping("rend.DelayFrameSwapping"); +Option DelayFrameSwapping("rend.DelayFrameSwapping", true); Option WidescreenGameHacks("rend.WidescreenGameHacks"); std::array, 4> CrosshairColor { Option("rend.CrossHairColor1"), @@ -143,5 +143,8 @@ std::array, 2>, 4> MapleExpansionDevices { Option("device4.1", MDT_None, "input"), Option("device4.2", MDT_None, "input"), }; +#ifdef _WIN32 +Option UseRawInput("RawInput", false, "input"); +#endif } // namespace config diff --git a/core/cfg/option.h b/core/cfg/option.h index 7ee49e735..844fd876e 100644 --- a/core/cfg/option.h +++ b/core/cfg/option.h @@ -277,12 +277,6 @@ protected: using OptionString = Option; -template -class ConstOption { -public: - operator T() const { return value; } -}; - // Dynarec extern Option DynarecEnabled; @@ -303,7 +297,7 @@ extern Option SavestateSlot; // Sound -constexpr ConstOption LimitFPS; +constexpr bool LimitFPS = true; extern Option DSPEnabled; extern Option DisableSound; extern Option AudioBufferSize; //In samples ,*4 for bytes @@ -316,11 +310,22 @@ extern OptionString AudioBackend; class RendererOption : public Option { public: RendererOption() +#ifdef _WIN32 + : Option("pvr.rend", RenderType::DirectX9) {} +#else : Option("pvr.rend", RenderType::OpenGL) {} +#endif bool isOpenGL() const { return value == RenderType::OpenGL || value == RenderType::OpenGL_OIT; } + bool isVulkan() const { + return value == RenderType::Vulkan || value == RenderType::Vulkan_OIT; + } + bool isDirectX() const { + return value == RenderType::DirectX9; + } + void set(RenderType v) { newValue = v; @@ -357,7 +362,7 @@ extern Option ShowFPS; extern Option RenderToTextureBuffer; extern Option TranslucentPolygonDepthMask; extern Option ModifierVolumes; -constexpr ConstOption Clipping; +constexpr bool Clipping = true; extern Option TextureUpscale; extern Option MaxFilteredTextureSize; extern Option ExtraDepthScale; @@ -411,6 +416,11 @@ extern Option MouseSensitivity; extern Option VirtualGamepadVibration; extern std::array, 4> MapleMainDevices; extern std::array, 2>, 4> MapleExpansionDevices; +#ifdef _WIN32 +extern Option UseRawInput; +#else +constexpr bool UseRawInput = false; +#endif } // namespace config diff --git a/core/core.mk b/core/core.mk index abf60375e..2471bd0ca 100755 --- a/core/core.mk +++ b/core/core.mk @@ -68,10 +68,11 @@ ifdef FOR_LINUX endif ifdef FOR_WINDOWS + RZDCY_MODULES += rend/dx9/ ifndef UNIT_TESTS RZDCY_FILES += $(RZDCY_SRC_DIR)/windows/winmain.cpp endif - RZDCY_FILES += $(RZDCY_SRC_DIR)/windows/win_vmem.cpp + RZDCY_FILES += $(RZDCY_SRC_DIR)/windows/win_vmem.cpp $(RZDCY_SRC_DIR)/windows/rawinput.cpp RZDCY_CFLAGS += -I$(RZDCY_SRC_DIR)/deps/dirent endif diff --git a/core/deps/patches/SDL.patch b/core/deps/patches/SDL.patch deleted file mode 100644 index ede65aa80..000000000 --- a/core/deps/patches/SDL.patch +++ /dev/null @@ -1,100 +0,0 @@ -diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c -index 2557d8628..f3d214b1d 100644 ---- a/src/video/windows/SDL_windowsevents.c -+++ b/src/video/windows/SDL_windowsevents.c -@@ -294,31 +294,31 @@ WIN_CheckWParamMouseButtons(WPARAM wParam, SDL_WindowData *data, SDL_MouseID mou - } - - static void --WIN_CheckRawMouseButtons(ULONG rawButtons, SDL_WindowData *data) -+WIN_CheckRawMouseButtons(ULONG rawButtons, SDL_WindowData *data, SDL_MouseID mouseID) - { - if (rawButtons != data->mouse_button_flags) { - Uint32 mouseFlags = SDL_GetMouseState(NULL, NULL); - SDL_bool swapButtons = GetSystemMetrics(SM_SWAPBUTTON) != 0; - if ((rawButtons & RI_MOUSE_BUTTON_1_DOWN)) -- WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_1_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_LEFT, 0); -+ WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_1_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_LEFT, mouseID); - if ((rawButtons & RI_MOUSE_BUTTON_1_UP)) -- WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_1_UP), mouseFlags, swapButtons, data, SDL_BUTTON_LEFT, 0); -+ WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_1_UP), mouseFlags, swapButtons, data, SDL_BUTTON_LEFT, mouseID); - if ((rawButtons & RI_MOUSE_BUTTON_2_DOWN)) -- WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_2_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_RIGHT, 0); -+ WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_2_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_RIGHT, mouseID); - if ((rawButtons & RI_MOUSE_BUTTON_2_UP)) -- WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_2_UP), mouseFlags, swapButtons, data, SDL_BUTTON_RIGHT, 0); -+ WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_2_UP), mouseFlags, swapButtons, data, SDL_BUTTON_RIGHT, mouseID); - if ((rawButtons & RI_MOUSE_BUTTON_3_DOWN)) -- WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_3_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_MIDDLE, 0); -+ WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_3_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_MIDDLE, mouseID); - if ((rawButtons & RI_MOUSE_BUTTON_3_UP)) -- WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_3_UP), mouseFlags, swapButtons, data, SDL_BUTTON_MIDDLE, 0); -+ WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_3_UP), mouseFlags, swapButtons, data, SDL_BUTTON_MIDDLE, mouseID); - if ((rawButtons & RI_MOUSE_BUTTON_4_DOWN)) -- WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_4_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_X1, 0); -+ WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_4_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_X1, mouseID); - if ((rawButtons & RI_MOUSE_BUTTON_4_UP)) -- WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_4_UP), mouseFlags, swapButtons, data, SDL_BUTTON_X1, 0); -+ WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_4_UP), mouseFlags, swapButtons, data, SDL_BUTTON_X1, mouseID); - if ((rawButtons & RI_MOUSE_BUTTON_5_DOWN)) -- WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_5_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_X2, 0); -+ WIN_CheckWParamMouseButton((rawButtons & RI_MOUSE_BUTTON_5_DOWN), mouseFlags, swapButtons, data, SDL_BUTTON_X2, mouseID); - if ((rawButtons & RI_MOUSE_BUTTON_5_UP)) -- WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_5_UP), mouseFlags, swapButtons, data, SDL_BUTTON_X2, 0); -+ WIN_CheckWParamMouseButton(!(rawButtons & RI_MOUSE_BUTTON_5_UP), mouseFlags, swapButtons, data, SDL_BUTTON_X2, mouseID); - data->mouse_button_flags = rawButtons; - } - } -@@ -631,15 +631,17 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) - - /* Mouse data (ignoring synthetic mouse events generated for touchscreens) */ - if (inp.header.dwType == RIM_TYPEMOUSE) { -+ SDL_MouseID mouseID; - if (GetMouseMessageSource() == SDL_MOUSE_EVENT_SOURCE_TOUCH || - (GetMessageExtraInfo() & 0x82) == 0x82) { - break; - } -+ mouseID = (Uint32)(uintptr_t)inp.header.hDevice; - if (isRelative) { - RAWMOUSE* rawmouse = &inp.data.mouse; - - if ((rawmouse->usFlags & 0x01) == MOUSE_MOVE_RELATIVE) { -- SDL_SendMouseMotion(data->window, 0, 1, (int)rawmouse->lLastX, (int)rawmouse->lLastY); -+ SDL_SendMouseMotion(data->window, mouseID, 1, (int)rawmouse->lLastX, (int)rawmouse->lLastY); - } else if (rawmouse->lLastX || rawmouse->lLastY) { - /* synthesize relative moves from the abs position */ - static SDL_Point lastMousePoint; -@@ -654,12 +656,12 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) - lastMousePoint.y = y; - } - -- SDL_SendMouseMotion(data->window, 0, 1, (int)(x-lastMousePoint.x), (int)(y-lastMousePoint.y)); -+ SDL_SendMouseMotion(data->window, mouseID, 1, (int)(x-lastMousePoint.x), (int)(y-lastMousePoint.y)); - - lastMousePoint.x = x; - lastMousePoint.y = y; - } -- WIN_CheckRawMouseButtons(rawmouse->usButtonFlags, data); -+ WIN_CheckRawMouseButtons(rawmouse->usButtonFlags, data, mouseID); - } else if (isCapture) { - /* we check for where Windows thinks the system cursor lives in this case, so we don't really lose mouse accel, etc. */ - POINT pt; -@@ -675,12 +677,12 @@ WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) - if(currentHnd != hwnd || pt.x < 0 || pt.y < 0 || pt.x > hwndRect.right || pt.y > hwndRect.right) { - SDL_bool swapButtons = GetSystemMetrics(SM_SWAPBUTTON) != 0; - -- SDL_SendMouseMotion(data->window, 0, 0, (int)pt.x, (int)pt.y); -- SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_LBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, !swapButtons ? SDL_BUTTON_LEFT : SDL_BUTTON_RIGHT); -- SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_RBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, !swapButtons ? SDL_BUTTON_RIGHT : SDL_BUTTON_LEFT); -- SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_MIDDLE); -- SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X1); -- SDL_SendMouseButton(data->window, 0, GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X2); -+ SDL_SendMouseMotion(data->window, mouseID, 0, (int)pt.x, (int)pt.y); -+ SDL_SendMouseButton(data->window, mouseID, GetAsyncKeyState(VK_LBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, !swapButtons ? SDL_BUTTON_LEFT : SDL_BUTTON_RIGHT); -+ SDL_SendMouseButton(data->window, mouseID, GetAsyncKeyState(VK_RBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, !swapButtons ? SDL_BUTTON_RIGHT : SDL_BUTTON_LEFT); -+ SDL_SendMouseButton(data->window, mouseID, GetAsyncKeyState(VK_MBUTTON) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_MIDDLE); -+ SDL_SendMouseButton(data->window, mouseID, GetAsyncKeyState(VK_XBUTTON1) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X1); -+ SDL_SendMouseButton(data->window, mouseID, GetAsyncKeyState(VK_XBUTTON2) & 0x8000 ? SDL_PRESSED : SDL_RELEASED, SDL_BUTTON_X2); - } - } else { - SDL_assert(0 && "Shouldn't happen"); diff --git a/core/hw/maple/maple_devs.cpp b/core/hw/maple/maple_devs.cpp index c29db0a73..4d856d675 100755 --- a/core/hw/maple/maple_devs.cpp +++ b/core/hw/maple/maple_devs.cpp @@ -1011,9 +1011,9 @@ struct maple_sega_purupuru : maple_base } }; -u8 kb_shift; // shift keys pressed (bitmask) -u8 kb_led; // leds currently lit -u8 kb_key[6]={0}; // normal keys pressed +u8 kb_shift[MAPLE_PORTS]; // shift keys pressed (bitmask) +u8 kb_led[MAPLE_PORTS]; // leds currently lit +u8 kb_key[MAPLE_PORTS][6]; // normal keys pressed struct maple_keyboard : maple_base { @@ -1074,14 +1074,12 @@ struct maple_keyboard : maple_base w32(MFID_6_Keyboard); //struct data //int8 shift ; shift keys pressed (bitmask) //1 - w8(kb_shift); + w8(kb_shift[bus_id]); //int8 led ; leds currently lit //1 - w8(kb_led); + w8(kb_led[bus_id]); //int8 key[6] ; normal keys pressed //6 for (int i = 0; i < 6; i++) - { - w8(kb_key[i]); - } + w8(kb_key[bus_id][i]); return MDRS_DataTransfer; @@ -1103,7 +1101,7 @@ struct maple_keyboard : maple_base // bit 1: Right button (B) // bit 2: Left button (A) // bit 3: Wheel button -u32 mo_buttons[4] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; +u8 mo_buttons[4] = { 0xFF, 0xFF, 0xFF, 0xFF }; // Relative mouse coordinates [-512:511] f32 mo_x_delta[4]; f32 mo_y_delta[4]; @@ -1116,9 +1114,6 @@ s32 mo_y_abs[4]; // previous mouse coordinates for relative motion s32 mo_x_prev[4] = { -1, -1, -1, -1 }; s32 mo_y_prev[4] = { -1, -1, -1, -1 }; -// physical mouse coordinates (relative to window/screen) -s32 mo_x_phy; -s32 mo_y_phy; // last known screen/window size static s32 mo_width; static s32 mo_height; @@ -1175,14 +1170,13 @@ struct maple_mouse : maple_base config->GetMouseInput(buttons, x, y, wheel); w32(MFID_9_Mouse); - //struct data - //int8 buttons ; buttons (RLDUSABC, where A is left btn, B is right, and S is middle/scrollwheel) + // buttons (RLDUSABC, where A is left btn, B is right, and S is middle/scrollwheel) w8(buttons); - //int8 options + // options w8(0); - //int8 axes overflow + // axes overflow w8(0); - //int8 reserved + // reserved w8(0); //int16 axis1 ; horizontal movement (0-$3FF) (little endian) w16(mo_cvt(x)); @@ -1404,11 +1398,8 @@ static void screenToNative(int& x, int& y, int width, int height) void SetMousePosition(int x, int y, int width, int height, u32 mouseId) { - if (mouseId == 0) - { - mo_x_phy = x; - mo_y_phy = y; - } + if (mouseId >= MAPLE_PORTS) + return; mo_width = width; mo_height = height; @@ -1434,6 +1425,8 @@ void SetMousePosition(int x, int y, int width, int height, u32 mouseId) void SetRelativeMousePosition(int xrel, int yrel, u32 mouseId) { + if (mouseId >= MAPLE_PORTS) + return; int width = mo_width; int height = mo_height; if (config::Rotate90) diff --git a/core/hw/maple/maple_devs.h b/core/hw/maple/maple_devs.h index efddb91de..12ab0e63b 100755 --- a/core/hw/maple/maple_devs.h +++ b/core/hw/maple/maple_devs.h @@ -165,16 +165,13 @@ extern u8 EEPROM[0x100]; void load_naomi_eeprom(); // Mouse position and buttons -extern u32 mo_buttons[4]; +extern u8 mo_buttons[4]; extern s32 mo_x_abs[4]; extern s32 mo_y_abs[4]; extern f32 mo_x_delta[4]; extern f32 mo_y_delta[4]; extern f32 mo_wheel_delta[4]; -extern s32 mo_x_phy; -extern s32 mo_y_phy; - extern s32 mo_x_prev[4]; extern s32 mo_y_prev[4]; diff --git a/core/hw/pvr/Renderer_if.cpp b/core/hw/pvr/Renderer_if.cpp index ec7d12f48..b08367c03 100644 --- a/core/hw/pvr/Renderer_if.cpp +++ b/core/hw/pvr/Renderer_if.cpp @@ -247,6 +247,7 @@ Renderer* rend_GL4(); Renderer* rend_norend(); Renderer* rend_Vulkan(); Renderer* rend_OITVulkan(); +Renderer* rend_DirectX9(); static void rend_create_renderer() { @@ -272,6 +273,11 @@ static void rend_create_renderer() case RenderType::Vulkan_OIT: renderer = rend_OITVulkan(); break; +#endif +#ifdef _WIN32 + case RenderType::DirectX9: + renderer = rend_DirectX9(); + break; #endif } #endif diff --git a/core/hw/pvr/Renderer_if.h b/core/hw/pvr/Renderer_if.h index 4318e9bfb..86007f5e3 100644 --- a/core/hw/pvr/Renderer_if.h +++ b/core/hw/pvr/Renderer_if.h @@ -38,7 +38,7 @@ struct Renderer virtual void DrawOSD(bool clear_screen) { } - virtual u64 GetTexture(TSP tsp, TCW tcw) { return 0; } + virtual BaseTextureCacheData *GetTexture(TSP tsp, TCW tcw) { return nullptr; } }; extern Renderer* renderer; diff --git a/core/hw/pvr/ta.h b/core/hw/pvr/ta.h index 853265fbb..62007764f 100644 --- a/core/hw/pvr/ta.h +++ b/core/hw/pvr/ta.h @@ -12,7 +12,7 @@ void ta_vtx_SoftReset(); void DYNACALL ta_vtx_data32(const SQBuffer *data); void ta_vtx_data(const SQBuffer *data, u32 size); -bool ta_parse_vdrc(TA_context* ctx); +bool ta_parse_vdrc(TA_context *ctx, bool bgraColors = false); class TaTypeLut { diff --git a/core/hw/pvr/ta_ctx.h b/core/hw/pvr/ta_ctx.h index c69c12387..c28593232 100644 --- a/core/hw/pvr/ta_ctx.h +++ b/core/hw/pvr/ta_ctx.h @@ -13,6 +13,8 @@ void* OS_aligned_malloc(size_t align, size_t size); // helper for 32 byte aligned memory de-allocation void OS_aligned_free(void *ptr); +class BaseTextureCacheData; + //Vertex storage types struct Vertex { @@ -35,7 +37,10 @@ struct PolyParam u32 first; //entry index , holds vertex/pos data u32 count; - u64 texid; + BaseTextureCacheData *texture; +#if !defined(HOST_64BIT_CPU) + u32 _pad0; +#endif TSP tsp; TCW tcw; @@ -46,7 +51,10 @@ struct PolyParam //float zMin,zMax; TSP tsp1; TCW tcw1; - u64 texid1; + BaseTextureCacheData *texture1; +#if !defined(HOST_64BIT_CPU) + u32 _pad1; +#endif }; struct ModifierVolumeParam diff --git a/core/hw/pvr/ta_vtx.cpp b/core/hw/pvr/ta_vtx.cpp index 2bee9069d..640955b46 100644 --- a/core/hw/pvr/ta_vtx.cpp +++ b/core/hw/pvr/ta_vtx.cpp @@ -86,8 +86,7 @@ static f32 f16(u16 v) #define vdrc vd_rc -//Splitter function (normally ta_dma_main , modified for split dma's) - +template class FifoSplitter { static const u32 *ta_type_lut; @@ -706,23 +705,23 @@ private: d_pp->pcw = pp->pcw; d_pp->tileclip = tileclip_val; - d_pp->texid = -1; - if (d_pp->pcw.Texture) - d_pp->texid = renderer->GetTexture(d_pp->tsp,d_pp->tcw); + d_pp->texture = renderer->GetTexture(d_pp->tsp, d_pp->tcw); + else + d_pp->texture = nullptr; d_pp->tsp1.full = -1; d_pp->tcw1.full = -1; - d_pp->texid1 = -1; + d_pp->texture1 = nullptr; } #define glob_param_bdc(pp) glob_param_bdc_( (TA_PolyParam0*)pp) #define poly_float_color_(to,a,r,g,b) \ - to[0] = float_to_satu8(r); \ - to[1] = float_to_satu8(g); \ - to[2] = float_to_satu8(b); \ - to[3] = float_to_satu8(a); + to[Red] = float_to_satu8(r); \ + to[Green] = float_to_satu8(g); \ + to[Blue] = float_to_satu8(b); \ + to[Alpha] = float_to_satu8(a); #define poly_float_color(to,src) \ @@ -778,7 +777,7 @@ private: CurrentPP->tsp1.full = pp->tsp1.full; CurrentPP->tcw1.full = pp->tcw1.full; if (pp->pcw.Texture) - CurrentPP->texid1 = renderer->GetTexture(pp->tsp1, pp->tcw1); + CurrentPP->texture1 = renderer->GetTexture(pp->tsp1, pp->tcw1); } // Intensity, with Two Volumes @@ -792,7 +791,7 @@ private: CurrentPP->tsp1.full = pp->tsp1.full; CurrentPP->tcw1.full = pp->tcw1.full; if (pp->pcw.Texture) - CurrentPP->texid1 = renderer->GetTexture(pp->tsp1, pp->tcw1); + CurrentPP->texture1 = renderer->GetTexture(pp->tsp1, pp->tcw1); } __forceinline @@ -869,17 +868,17 @@ private: #define vert_packed_color_(to,src) \ { \ u32 t=src; \ - to[2] = (u8)(t);t>>=8;\ - to[1] = (u8)(t);t>>=8;\ - to[0] = (u8)(t);t>>=8;\ - to[3] = (u8)(t); \ + to[Blue] = (u8)(t);t>>=8;\ + to[Green] = (u8)(t);t>>=8;\ + to[Red] = (u8)(t);t>>=8;\ + to[Alpha] = (u8)(t); \ } #define vert_float_color_(to,a,r,g,b) \ - to[0] = float_to_satu8(r); \ - to[1] = float_to_satu8(g); \ - to[2] = float_to_satu8(b); \ - to[3] = float_to_satu8(a); + to[Red] = float_to_satu8(r); \ + to[Green] = float_to_satu8(g); \ + to[Blue] = float_to_satu8(b); \ + to[Alpha] = float_to_satu8(a); //Macros to make thins easier ;) #define vert_packed_color(to,src) \ @@ -895,34 +894,32 @@ private: //Intensity is clamped before the mul, as well as on face color to work the same as the hardware. [Fixes red dog] #define vert_face_base_color(baseint) \ - { u32 satint=float_to_satu8(vtx->baseint); \ - cv->col[0] = FaceBaseColor[0]*satint/256; \ - cv->col[1] = FaceBaseColor[1]*satint/256; \ - cv->col[2] = FaceBaseColor[2]*satint/256; \ - cv->col[3] = FaceBaseColor[3]; } + { u32 satint = float_to_satu8(vtx->baseint); \ + cv->col[Red] = FaceBaseColor[Red] * satint / 256; \ + cv->col[Green] = FaceBaseColor[Green] * satint / 256; \ + cv->col[Blue] = FaceBaseColor[Blue] * satint / 256; \ + cv->col[Alpha] = FaceBaseColor[Alpha]; } #define vert_face_offs_color(offsint) \ - { u32 satint=float_to_satu8(vtx->offsint); \ - cv->spc[0] = FaceOffsColor[0]*satint/256; \ - cv->spc[1] = FaceOffsColor[1]*satint/256; \ - cv->spc[2] = FaceOffsColor[2]*satint/256; \ - cv->spc[3] = FaceOffsColor[3]; } + { u32 satint = float_to_satu8(vtx->offsint); \ + cv->spc[Red] = FaceOffsColor[Red] * satint / 256; \ + cv->spc[Green] = FaceOffsColor[Green] * satint / 256; \ + cv->spc[Blue] = FaceOffsColor[Blue] * satint / 256; \ + cv->spc[Alpha] = FaceOffsColor[Alpha]; } #define vert_face_base_color1(baseint) \ - { u32 satint=float_to_satu8(vtx->baseint); \ - cv->col1[0] = FaceBaseColor1[0]*satint/256; \ - cv->col1[1] = FaceBaseColor1[1]*satint/256; \ - cv->col1[2] = FaceBaseColor1[2]*satint/256; \ - cv->col1[3] = FaceBaseColor1[3]; } + { u32 satint = float_to_satu8(vtx->baseint); \ + cv->col1[Red] = FaceBaseColor1[Red] * satint / 256; \ + cv->col1[Green] = FaceBaseColor1[Green] * satint / 256; \ + cv->col1[Blue] = FaceBaseColor1[Blue] * satint / 256; \ + cv->col1[Alpha] = FaceBaseColor1[Alpha]; } #define vert_face_offs_color1(offsint) \ - { u32 satint=float_to_satu8(vtx->offsint); \ - cv->spc1[0] = FaceOffsColor1[0]*satint/256; \ - cv->spc1[1] = FaceOffsColor1[1]*satint/256; \ - cv->spc1[2] = FaceOffsColor1[2]*satint/256; \ - cv->spc1[3] = FaceOffsColor1[3]; } - - //vert_float_color_(cv->spc,FaceOffsColor[3],FaceOffsColor[0]*satint/256,FaceOffsColor[1]*satint/256,FaceOffsColor[2]*satint/256); } + { u32 satint = float_to_satu8(vtx->offsint); \ + cv->spc1[Red] = FaceOffsColor1[Red] * satint / 256; \ + cv->spc1[Green] = FaceOffsColor1[Green] * satint / 256; \ + cv->spc1[Blue] = FaceOffsColor1[Blue] * satint / 256; \ + cv->spc1[Alpha] = FaceOffsColor1[Alpha]; } //(Non-Textured, Packed Color) @@ -1170,14 +1167,14 @@ private: d_pp->pcw=spr->pcw; d_pp->tileclip=tileclip_val; - d_pp->texid = -1; - - if (d_pp->pcw.Texture) { - d_pp->texid = renderer->GetTexture(d_pp->tsp,d_pp->tcw); - } + if (d_pp->pcw.Texture) + d_pp->texture = renderer->GetTexture(d_pp->tsp, d_pp->tcw); + else + d_pp->texture = nullptr; + d_pp->tcw1.full = -1; d_pp->tsp1.full = -1; - d_pp->texid1 = -1; + d_pp->texture1 = nullptr; SFaceBaseColor=spr->BaseCol; SFaceOffsColor=spr->OffsCol; @@ -1374,7 +1371,8 @@ private: } }; -const u32 *FifoSplitter::ta_type_lut; +template +const u32 *FifoSplitter::ta_type_lut; TaTypeLut::TaTypeLut() { @@ -1382,8 +1380,8 @@ TaTypeLut::TaTypeLut() { PCW pcw; pcw.obj_ctrl = i; - u32 rv = FifoSplitter::poly_data_type_id(pcw); - u32 type = FifoSplitter::poly_header_type_size(pcw); + u32 rv = FifoSplitter<>::poly_data_type_id(pcw); + u32 type = FifoSplitter<>::poly_header_type_size(pcw); if (type & 0x80) rv |= SZ64 << 30; @@ -1399,7 +1397,8 @@ TaTypeLut::TaTypeLut() static bool ClearZBeforePass(int pass_number); static void getRegionTileClipping(u32& xmin, u32& xmax, u32& ymin, u32& ymax); -FifoSplitter TAFifo0; +FifoSplitter<> TAParser; +FifoSplitter<2, 1, 0, 3> TAParserDX; // // Check if a vertex has huge x,y,z values or negative z @@ -1540,7 +1539,7 @@ static void fix_texture_bleeding(const List *list) } } -bool ta_parse_vdrc(TA_context* ctx) +bool ta_parse_vdrc(TA_context* ctx, bool bgraColors) { ctx->rend_inuse.lock(); bool rv=false; @@ -1548,7 +1547,10 @@ bool ta_parse_vdrc(TA_context* ctx) vd_ctx = ctx; vd_rc = vd_ctx->rend; - TAFifo0.vdec_init(); + if (bgraColors) + TAParserDX.vdec_init(); + else + TAParser.vdec_init(); bool empty_context = true; int op_poly_count = 0; @@ -1558,7 +1560,7 @@ bool ta_parse_vdrc(TA_context* ctx) PolyParam *bgpp = vd_rc.global_param_op.head(); if (bgpp->pcw.Texture) { - bgpp->texid = renderer->GetTexture(bgpp->tsp, bgpp->tcw); + bgpp->texture = renderer->GetTexture(bgpp->tsp, bgpp->tcw); empty_context = false; } @@ -1636,7 +1638,8 @@ bool ta_parse_vdrc(TA_context* ctx) //decode a vertex in the native pvr format //used for bg poly -static void decode_pvr_vertex(u32 base,u32 ptr,Vertex* cv) +template +void decode_pvr_vertex(u32 base, u32 ptr, Vertex* cv) { //ISP //TSP @@ -1749,14 +1752,14 @@ void FillBGP(TA_context* ctx) u32 vertex_ptr=strip_vert_num*strip_vs+strip_base +3*4; //now , all the info is ready :p - bgpp->texid = -1; + bgpp->texture = nullptr; bgpp->isp.full = pvr_read32p(strip_base); bgpp->tsp.full = pvr_read32p(strip_base + 4); bgpp->tcw.full = pvr_read32p(strip_base + 8); bgpp->tcw1.full = -1; bgpp->tsp1.full = -1; - bgpp->texid1 = -1; + bgpp->texture1 = nullptr; bgpp->count=4; bgpp->first=0; bgpp->tileclip=0;//disabled ! HA ~ @@ -1773,7 +1776,10 @@ void FillBGP(TA_context* ctx) float scale_x= (SCALER_CTL.hscale) ? 2.f:1.f; //if AA hack the hacked pos value hacks for (int i=0;i<3;i++) { - decode_pvr_vertex(strip_base,vertex_ptr,&cv[i]); + if (config::RendererType.isDirectX()) + decode_pvr_vertex<2, 1, 0, 3>(strip_base,vertex_ptr,&cv[i]); + else + decode_pvr_vertex<0, 1, 2, 3>(strip_base,vertex_ptr,&cv[i]); vertex_ptr+=strip_vs; } diff --git a/core/input/gamepad_device.cpp b/core/input/gamepad_device.cpp index 4cbc4b54c..ba3a1bf95 100644 --- a/core/input/gamepad_device.cpp +++ b/core/input/gamepad_device.cpp @@ -22,6 +22,7 @@ #include "oslib/oslib.h" #include "rend/gui.h" #include "emulator.h" +#include "hw/maple/maple_devs.h" #include "stdclass.h" #include @@ -535,6 +536,54 @@ void GamepadDevice::SaveMaplePorts() } } +void Mouse::setAbsPos(int x, int y, int width, int height) { + SetMousePosition(x, y, width, height, maple_port()); +} + +void Mouse::setRelPos(int deltax, int deltay) { + SetRelativeMousePosition(deltax, deltay, maple_port()); +} + +void Mouse::setWheel(int delta) { + if (maple_port() >= 0 && maple_port() < ARRAY_SIZE(mo_wheel_delta)) + mo_wheel_delta[maple_port()] += delta; +} + +void Mouse::setButton(Button button, bool pressed) +{ + if (maple_port() >= 0 && maple_port() < ARRAY_SIZE(mo_buttons)) + { + if (pressed) + mo_buttons[maple_port()] &= ~(1 << (int)button); + else + mo_buttons[maple_port()] |= 1 << (int)button; + } + if (gui_is_open() && !is_detecting_input()) + // Don't register mouse clicks as gamepad presses when gui is open + // This makes the gamepad presses to be handled first and the mouse position to be ignored + return; + gamepad_btn_input(button, pressed); +} + + +void SystemMouse::setAbsPos(int x, int y, int width, int height) { + gui_set_mouse_position(x, y); + Mouse::setAbsPos(x, y, width, height); +} + +void SystemMouse::setButton(Button button, bool pressed) { + int uiBtn = (int)button - 1; + if (uiBtn < 2) + uiBtn ^= 1; + gui_set_mouse_button(uiBtn, pressed); + Mouse::setButton(button, pressed); +} + +void SystemMouse::setWheel(int delta) { + gui_set_mouse_wheel(delta * 35); + Mouse::setWheel(delta); +} + #ifdef TEST_AUTOMATION #include "cfg/option.h" static bool replay_inited; diff --git a/core/input/gamepad_device.h b/core/input/gamepad_device.h index e993ac52d..00bc65add 100644 --- a/core/input/gamepad_device.h +++ b/core/input/gamepad_device.h @@ -76,7 +76,15 @@ protected: : _api_name(api_name), _maple_port(maple_port), _input_detected(nullptr), _remappable(remappable) { } + bool find_mapping(const char *custom_mapping = nullptr); + void loadMapping() { + if (!find_mapping()) + input_mapper = getDefaultMapping(); + } + virtual std::shared_ptr getDefaultMapping() { + return std::make_shared(); + } virtual void load_axis_min_max(u32 axis) {} bool is_detecting_input() { return _input_detected != nullptr; } @@ -115,3 +123,73 @@ extern s8 joyx[4], joyy[4]; extern s8 joyrx[4], joyry[4]; void UpdateVibration(u32 port, float power, float inclination, u32 duration_ms); + +class MouseInputMapping : public InputMapping +{ +public: + MouseInputMapping() + { + name = "Mouse"; + set_button(DC_BTN_A, 2); // Left + set_button(DC_BTN_B, 1); // Right + set_button(DC_BTN_START, 3); // Middle + + dirty = false; + } +}; + +class Mouse : public GamepadDevice +{ +protected: + Mouse(const char *apiName, int maplePort = 0) : GamepadDevice(maplePort, apiName) { + this->_name = "Mouse"; + } + + virtual std::shared_ptr getDefaultMapping() override { + return std::make_shared(); + } + +public: + enum Button { + LEFT_BUTTON = 2, + RIGHT_BUTTON = 1, + MIDDLE_BUTTON = 3, + BUTTON_4 = 4, + BUTTON_5 = 5 + }; + + virtual const char *get_button_name(u32 code) override + { + switch((Button)code) + { + case LEFT_BUTTON: + return "Left Button"; + case RIGHT_BUTTON: + return "Right Button"; + case MIDDLE_BUTTON: + return "Middle Button"; + case BUTTON_4: + return "Button 4"; + case BUTTON_5: + return "Button 5"; + default: + return nullptr; + } + } + + void setAbsPos(int x, int y, int width, int height); + void setRelPos(int deltax, int deltay); + void setButton(Button button, bool pressed); + void setWheel(int delta); +}; + +class SystemMouse : public Mouse +{ +protected: + SystemMouse(const char *apiName, int maplePort = 0) : Mouse(apiName, maplePort) {} + +public: + void setAbsPos(int x, int y, int width, int height); + void setButton(Button button, bool pressed); + void setWheel(int delta); +}; diff --git a/core/input/keyboard_device.cpp b/core/input/keyboard_device.cpp deleted file mode 100644 index 83ef7a65a..000000000 --- a/core/input/keyboard_device.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "keyboard_device.h" - -KeyboardDevice *KeyboardDevice::_instance; diff --git a/core/input/keyboard_device.h b/core/input/keyboard_device.h index c6a2a5294..0e647ab91 100644 --- a/core/input/keyboard_device.h +++ b/core/input/keyboard_device.h @@ -1,45 +1,360 @@ /* - Copyright 2019 flyinghead + Copyright 2021 flyinghead - This file is part of reicast. + This file is part of Flycast. - reicast is free software: you can redistribute it and/or modify + Flycast is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. - reicast is distributed in the hope that it will be useful, + Flycast is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with reicast. If not, see . - */ - + along with Flycast. If not, see . +*/ #pragma once #include "types.h" - +#include "gamepad_device.h" +#include "rend/gui.h" #include -class KeyboardDevice +class KeyboardInputMapping : public InputMapping { public: - virtual const char* name() = 0; - int maple_port() const { return _maple_port; } - void keyboard_character(char c); - std::string get_character_input(); - virtual ~KeyboardDevice() = default; + KeyboardInputMapping() + { + name = "Keyboard"; + set_button(DC_BTN_A, 27); // X + set_button(DC_BTN_B, 6); // C + set_button(DC_BTN_X, 22); // S + set_button(DC_BTN_Y, 7); // D + set_button(DC_DPAD_UP, 82); + set_button(DC_DPAD_DOWN, 81); + set_button(DC_DPAD_LEFT, 80); + set_button(DC_DPAD_RIGHT, 79); + set_button(DC_BTN_START, 40); // Return + set_button(EMU_BTN_TRIGGER_LEFT, 9); // F + set_button(EMU_BTN_TRIGGER_RIGHT, 25); // V + set_button(EMU_BTN_MENU, 43); // TAB + set_button(EMU_BTN_FFORWARD, 44); // Space - static KeyboardDevice *GetInstance() { return _instance; } + dirty = false; + } +}; +class KeyboardDevice : public GamepadDevice +{ protected: - KeyboardDevice(int maple_port) : _maple_port(maple_port) { _instance = this; } + KeyboardDevice(int maple_port, const char* apiName, bool remappable = true) + : GamepadDevice(maple_port, apiName, remappable) { + _name = "Keyboard"; + } -private: - int _maple_port; - std::string char_input; - static KeyboardDevice *_instance; + std::shared_ptr getDefaultMapping() override { + return std::make_shared(); + } + +public: + const char *get_button_name(u32 code) override + { + switch (code) + { + case 0x04: + return "A"; + case 0x05: + return "B"; + case 0x06: + return "C"; + case 0x07: + return "D"; + case 0x08: + return "E"; + case 0x09: + return "F"; + case 0x0A: + return "G"; + case 0x0B: + return "H"; + case 0x0C: + return "I"; + case 0x0D: + return "J"; + case 0x0E: + return "K"; + case 0x0F: + return "L"; + case 0x10: + return "M"; + case 0x11: + return "N"; + case 0x12: + return "O"; + case 0x13: + return "P"; + case 0x14: + return "Q"; + case 0x15: + return "R"; + case 0x16: + return "S"; + case 0x17: + return "T"; + case 0x18: + return "U"; + case 0x19: + return "V"; + case 0x1A: + return "W"; + case 0x1B: + return "X"; + case 0x1C: + return "Y"; + case 0x1D: + return "Z"; + + case 0x1E: + return "1"; + case 0x1F: + return "2"; + case 0x20: + return "3"; + case 0x21: + return "4"; + case 0x22: + return "5"; + case 0x23: + return "6"; + case 0x24: + return "7"; + case 0x25: + return "8"; + case 0x26: + return "9"; + case 0x27: + return "0"; + + case 0x28: + return "Return"; + case 0x29: + return "Escape"; + case 0x2A: + return "Backspace"; + case 0x2B: + return "Tab"; + case 0x2C: + return "Space"; + + case 0x2D: + return "-"; + case 0x2E: + return "="; + case 0x2F: + return "["; + case 0x30: + return "]"; + case 0x31: + return "\\"; + case 0x32: + return "#"; // non-US + case 0x33: + return ";"; + case 0x34: + return "'"; + case 0x35: + return "`"; + case 0x36: + return ","; + case 0x37: + return "."; + case 0x38: + return "/"; + case 0x39: + return "CapsLock"; + + case 0x3A: + return "F1"; + case 0x3B: + return "F2"; + case 0x3C: + return "F3"; + case 0x3D: + return "F4"; + case 0x3E: + return "F5"; + case 0x3F: + return "F6"; + case 0x40: + return "F7"; + case 0x41: + return "F8"; + case 0x42: + return "F9"; + case 0x43: + return "F10"; + case 0x44: + return "F11"; + case 0x45: + return "F12"; + + case 0x46: + return "PrintScreen"; + case 0x47: + return "ScrollLock"; + case 0x48: + return "Pause"; + case 0x49: + return "Insert"; + case 0x4A: + return "Home"; + case 0x4B: + return "Page Up"; + case 0x4C: + return "Delete"; + case 0x4D: + return "End"; + case 0x4E: + return "Page Down"; + case 0x4F: + return "Right"; + case 0x50: + return "Left"; + case 0x51: + return "Down"; + case 0x52: + return "Up"; + + case 0x53: + return "NumLock"; + case 0x54: + return "Num /"; + case 0x55: + return "Num *"; + case 0x56: + return "Num -"; + case 0x57: + return "Num +"; + case 0x58: + return "Num Enter"; + case 0x59: + return "Num 1"; + case 0x5A: + return "Num 2"; + case 0x5B: + return "Num 3"; + case 0x5C: + return "Num 4"; + case 0x5D: + return "Num 5"; + case 0x5E: + return "Num 6"; + case 0x5F: + return "Num 7"; + case 0x60: + return "Num 8"; + case 0x61: + return "Num 9"; + case 0x62: + return "Num 0"; + case 0x63: + return "Num ."; + + case 0x64: + return "\\"; // non-US + case 0x65: + return "Application"; + case 0x66: + return "Power"; + case 0x67: + return "Num ="; + + case 0x68: + return "F13"; + case 0x69: + return "F14"; + case 0x6A: + return "F15"; + case 0x6B: + return "F16"; + case 0x6C: + return "F17"; + case 0x6D: + return "F18"; + case 0x6E: + return "F19"; + case 0x6F: + return "F20"; + case 0x70: + return "F21"; + case 0x71: + return "F22"; + case 0x72: + return "F23"; + case 0x73: + return "F24"; + + case 0x87: + return "Int1"; + case 0x88: + return "Int2"; + case 0x89: + return "Yen"; + case 0x8A: + return "Int4"; + case 0x8B: + return "Int5"; + case 0x8C: + return "Int6"; + case 0x8D: + return "Int7"; + case 0x8E: + return "Int8"; + case 0x8F: + return "Int9"; + + case 0x90: + return "Hangul"; + case 0x91: + return "Hanja"; + case 0x92: + return "Katakana"; + case 0x93: + return "Hiragana"; + case 0x94: + return "Zenkaku/Hankaku"; + case 0x95: + return "Lang6"; + case 0x96: + return "Lang7"; + case 0x97: + return "Lang8"; + case 0x98: + return "Lang9"; + + case 0xE0: + return "Left Ctrl"; + case 0xE1: + return "Left Shift"; + case 0xE2: + return "Left Alt"; + case 0xE3: + return "Left Meta"; + case 0xE4: + return "Right Ctrl"; + case 0xE5: + return "Right Shift"; + case 0xE6: + return "Right Alt"; + case 0xE7: + return "Right Meta"; + + default: + return nullptr; + } + } }; template @@ -47,9 +362,11 @@ class KeyboardDeviceTemplate : public KeyboardDevice { public: virtual void keyboard_input(Keycode keycode, bool pressed, int modifier_keys = 0); + virtual ~KeyboardDeviceTemplate() = default; protected: - KeyboardDeviceTemplate(int maple_port) : KeyboardDevice(maple_port), _modifier_keys(0), _kb_used(0) {} + KeyboardDeviceTemplate(int maple_port, const char *apiName, bool remappable = true) + : KeyboardDevice(maple_port, apiName, remappable), _modifier_keys(0), _kb_used(0) {} virtual u8 convert_keycode(Keycode keycode) = 0; private: @@ -68,8 +385,8 @@ enum DCKeyboardModifiers { DC_KBMOD_S2 = 0x80, }; -extern u8 kb_key[6]; // normal keys pressed -extern u8 kb_shift; // modifier keys pressed (bitmask) +extern u8 kb_key[4][6]; // normal keys pressed +extern u8 kb_shift[4]; // modifier keys pressed (bitmask) static inline void setFlag(int& v, u32 bitmask, bool set) { @@ -82,74 +399,77 @@ static inline void setFlag(int& v, u32 bitmask, bool set) template void KeyboardDeviceTemplate::keyboard_input(Keycode keycode, bool pressed, int modifier_keys) { + const int port = maple_port(); + if (port < 0 || port > (int)ARRAY_SIZE(kb_key)) + return; + u8 dc_keycode = convert_keycode(keycode); - // Some OSes (Mac OS) don't distinguish left and right modifier keys to we set them both. - // But not for Alt since Right Alt is used as a special modifier keys on some international - // keyboards. - switch (dc_keycode) + if (port < (int)ARRAY_SIZE(kb_key)) { - case 0xE1: // Left Shift - case 0xE5: // Right Shift - setFlag(_modifier_keys, DC_KBMOD_LEFTSHIFT | DC_KBMOD_RIGHTSHIFT, pressed); - break; - case 0xE0: // Left Ctrl - case 0xE4: // Right Ctrl - setFlag(_modifier_keys, DC_KBMOD_LEFTCTRL | DC_KBMOD_RIGHTCTRL, pressed); - break; - case 0xE2: // Left Alt - setFlag(_modifier_keys, DC_KBMOD_LEFTALT, pressed); - break; - case 0xE6: // Right Alt - setFlag(_modifier_keys, DC_KBMOD_RIGHTALT, pressed); - break; - case 0xE7: // S2 special key - setFlag(_modifier_keys, DC_KBMOD_S2, pressed); - break; - default: - break; - } - kb_shift = _modifier_keys; - - if (dc_keycode != 0 && dc_keycode < 0xE0) - { - if (pressed) + // Some OSes (Mac OS) don't distinguish left and right modifier keys to we set them both. + // But not for Alt since Right Alt is used as a special modifier keys on some international + // keyboards. + switch (dc_keycode) { - if (_kb_used < ARRAY_SIZE(kb_key)) - { - bool found = false; - for (u32 i = 0; !found && i < _kb_used; i++) - { - if (kb_key[i] == dc_keycode) - found = true; - } - if (!found) - kb_key[_kb_used++] = dc_keycode; - } + case 0xE1: // Left Shift + case 0xE5: // Right Shift + setFlag(_modifier_keys, DC_KBMOD_LEFTSHIFT | DC_KBMOD_RIGHTSHIFT, pressed); + break; + case 0xE0: // Left Ctrl + case 0xE4: // Right Ctrl + setFlag(_modifier_keys, DC_KBMOD_LEFTCTRL | DC_KBMOD_RIGHTCTRL, pressed); + break; + case 0xE2: // Left Alt + setFlag(_modifier_keys, DC_KBMOD_LEFTALT, pressed); + break; + case 0xE6: // Right Alt + setFlag(_modifier_keys, DC_KBMOD_RIGHTALT, pressed); + break; + case 0xE7: // S2 special key + setFlag(_modifier_keys, DC_KBMOD_S2, pressed); + break; + default: + break; } - else + kb_shift[port] = _modifier_keys; + + if (dc_keycode != 0 && dc_keycode < 0xE0) { - for (u32 i = 0; i < _kb_used; i++) + if (pressed) { - if (kb_key[i] == dc_keycode) + if (_kb_used < ARRAY_SIZE(kb_key[port])) { - _kb_used--; - for (u32 j = i; j < ARRAY_SIZE(kb_key) - 1; j++) - kb_key[j] = kb_key[j + 1]; - kb_key[ARRAY_SIZE(kb_key) - 1] = 0; - break; + bool found = false; + for (u32 i = 0; !found && i < _kb_used; i++) + { + if (kb_key[port][i] == dc_keycode) + found = true; + } + if (!found) + kb_key[port][_kb_used++] = dc_keycode; } } + else + { + for (u32 i = 0; i < _kb_used; i++) + { + if (kb_key[port][i] == dc_keycode) + { + _kb_used--; + for (u32 j = i; j < ARRAY_SIZE(kb_key[port]) - 1; j++) + kb_key[port][j] = kb_key[port][j + 1]; + kb_key[port][ARRAY_SIZE(kb_key[port]) - 1] = 0; + break; + } + } + } + kb_shift[port] |= modifier_keys; } - kb_shift |= modifier_keys; } -} - -inline void KeyboardDevice::keyboard_character(char c) { - char_input.push_back(c); -} - -inline std::string KeyboardDevice::get_character_input() { - std::string input = char_input; - char_input.clear(); - return input; + // Do not map keyboard keys to gamepad buttons unless the GUI is open + // or the corresponding maple device (if any) isn't a keyboard + if (gui_is_open() + || port == (int)ARRAY_SIZE(kb_key) + || config::MapleMainDevices[port] != MDT_Keyboard) + gamepad_btn_input(dc_keycode, pressed); } diff --git a/core/input/mapping.cpp b/core/input/mapping.cpp index 4bf525bb1..83110abc4 100644 --- a/core/input/mapping.cpp +++ b/core/input/mapping.cpp @@ -172,6 +172,7 @@ void InputMapping::load(FILE* fp) int dz = mf.get_int("emulator", "dead_zone", 10); dz = std::min(dz, 100); dz = std::max(dz, 0); + version = mf.get_int("emulator", "version", 1); this->dead_zone = (float)dz / 100.f; @@ -276,6 +277,7 @@ bool InputMapping::save(const char *name) mf.set("emulator", "mapping_name", this->name); mf.set_int("emulator", "dead_zone", (int)std::round(this->dead_zone * 100.f)); + mf.set_int("emulator", "version", version); for (int port = 0; port < 4; port++) { diff --git a/core/input/mapping.h b/core/input/mapping.h index 550dde070..514c5e252 100644 --- a/core/input/mapping.h +++ b/core/input/mapping.h @@ -41,6 +41,7 @@ public: std::string name; float dead_zone = 0.1f; + int version = 2; DreamcastKey get_button_id(u32 port, u32 code) { diff --git a/core/linux-dist/evdev_gamepad.h b/core/linux-dist/evdev_gamepad.h index 1f5cf8336..3bccfa679 100644 --- a/core/linux-dist/evdev_gamepad.h +++ b/core/linux-dist/evdev_gamepad.h @@ -83,7 +83,7 @@ public: input_mapper = std::make_shared(*input_mapper); } else - input_mapper = std::make_shared(); + input_mapper = getDefaultMapping(); input_mapper->name = _name + " mapping"; save_mapping(); } @@ -96,6 +96,10 @@ public: close(_fd); } + std::shared_ptr getDefaultMapping() override { + return std::make_shared(); + } + void rumble(float power, float inclination, u32 duration_ms) override { vib_inclination = inclination * power; diff --git a/core/linux-dist/x11.cpp b/core/linux-dist/x11.cpp index 4e09d7181..497c2ccca 100644 --- a/core/linux-dist/x11.cpp +++ b/core/linux-dist/x11.cpp @@ -27,74 +27,18 @@ static Window x11_win; Display *x11_disp; -class MouseInputMapping : public InputMapping +class X11Mouse : public SystemMouse { public: - MouseInputMapping() + X11Mouse() : SystemMouse("X11") { - name = "X11 Mouse"; - set_button(DC_BTN_A, Button1); - set_button(DC_BTN_B, Button3); - set_button(DC_BTN_START, Button2); - - dirty = false; - } -}; - -class X11MouseGamepadDevice : public GamepadDevice -{ -public: - X11MouseGamepadDevice(int maple_port) : GamepadDevice(maple_port, "X11") - { - _name = "Mouse"; _unique_id = "x11_mouse"; - if (!find_mapping()) - input_mapper = std::make_shared(); - } - - bool gamepad_btn_input(u32 code, bool pressed) override - { - if (gui_is_open() && !is_detecting_input()) - // Don't register mouse clicks as gamepad presses when gui is open - // This makes the gamepad presses to be handled first and the mouse position to be ignored - // TODO Make this generic - return false; - else - return GamepadDevice::gamepad_btn_input(code, pressed); - } - - const char *get_button_name(u32 code) override - { - switch (code) - { - case Button1: - return "Left Button"; - case Button2: - return "Middle Button"; - case Button3: - return "Right Button"; - case Button4: - return "Scroll Up"; - case Button5: - return "Scroll Down"; - case 6: - return "Scroll Left"; - case 7: - return "Scroll Right"; - case 8: - return "Button 4"; - case 9: - return "Button 5"; - default: - return nullptr; - } + loadMapping(); } }; -static int x11_keyboard_input = 0; -static std::shared_ptr x11_keyboard; -static std::shared_ptr kb_gamepad; -static std::shared_ptr mouse_gamepad; +static std::shared_ptr x11Keyboard; +static std::shared_ptr x11Mouse; int x11_width; int x11_height; @@ -217,7 +161,14 @@ void input_x11_handle() KeySym keysym_return; int len = XLookupString(&e.xkey, buf, 1, &keysym_return, NULL); if (len > 0) - x11_keyboard->keyboard_character(buf[0]); + { + // Cheap ISO Latin-1 to UTF-8 conversion + u16 b = (u8)buf[0]; + if (b < 0x80) + gui_keyboard_input(b); + else + gui_keyboard_input((u16)((0xc2 + (b > 0xbf)) | ((b & 0x3f) + 0x80) << 8)); + } } /* no break */ case KeyRelease: @@ -232,10 +183,7 @@ void input_x11_handle() // Key wasn’t actually released: auto repeat continue; } - // Dreamcast keyboard emulation - x11_keyboard->keyboard_input(e.xkey.keycode, e.type == KeyPress); - // keyboard-based emulated gamepad - kb_gamepad->gamepad_btn_input(e.xkey.keycode, e.type == KeyPress); + x11Keyboard->keyboard_input(e.xkey.keycode, e.type == KeyPress); // Start/stop mouse capture with Left Ctrl + Left Alt if (e.type == KeyPress @@ -249,72 +197,55 @@ void input_x11_handle() x11_uncapture_mouse(); } // TODO Move this to bindable keys or in the gui menu - if (x11_keyboard_input) +#if 0 + if (e.xkey.keycode == KEY_F10) { - if (e.xkey.keycode == KEY_F10) - { - // Dump the next frame into a file - dump_frame_switch = e.type == KeyPress; - } - else if (e.type == KeyPress && e.xkey.keycode == KEY_F11) - { - x11_fullscreen = !x11_fullscreen; - x11_window_set_fullscreen(x11_fullscreen); - } + // Dump the next frame into a file + dump_frame_switch = e.type == KeyPress; + } + else +#endif + if (e.type == KeyPress && e.xkey.keycode == KEY_F11) + { + x11_fullscreen = !x11_fullscreen; + x11_window_set_fullscreen(x11_fullscreen); } } break; case FocusOut: - { - if (capturing_mouse) - x11_uncapture_mouse(); - capturing_mouse = false; - } + if (capturing_mouse) + x11_uncapture_mouse(); + capturing_mouse = false; break; case ButtonPress: case ButtonRelease: - mouse_gamepad->gamepad_btn_input(e.xbutton.button, e.type == ButtonPress); + switch (e.xbutton.button) { - u32 button_mask = 0; - switch (e.xbutton.button) - { - case Button1: // Left button - button_mask = 1 << 2; - break; - case Button2: // Middle button - button_mask = 1 << 3; - break; - case Button3: // Right button - button_mask = 1 << 1; - break; - case Button4: // Mouse wheel up - mo_wheel_delta[0] -= 16; - break; - case Button5: // Mouse wheel down - mo_wheel_delta[0] += 16; - break; - default: - break; - } - - if (button_mask) - { - if (e.type == ButtonPress) - mo_buttons[0] &= ~button_mask; - else - mo_buttons[0] |= button_mask; - } + case Button1: // Left button + x11Mouse->setButton(Mouse::LEFT_BUTTON, e.type == ButtonPress); + break; + case Button2: // Middle button + x11Mouse->setButton(Mouse::MIDDLE_BUTTON, e.type == ButtonPress); + break; + case Button3: // Right button + x11Mouse->setButton(Mouse::RIGHT_BUTTON, e.type == ButtonPress); + break; + case Button4: // Mouse wheel up + x11Mouse->setWheel(-1); + break; + case Button5: // Mouse wheel down + x11Mouse->setWheel(1); + break; + default: + break; } /* no break */ case MotionNotify: - // For Light gun - SetMousePosition(e.xmotion.x, e.xmotion.y, x11_width, x11_height); - // For mouse + x11Mouse->setAbsPos(e.xmotion.x, e.xmotion.y, x11_width, x11_height); mouse_moved = true; - break; } } @@ -335,15 +266,10 @@ void input_x11_handle() void input_x11_init() { - x11_keyboard = std::make_shared(0); - kb_gamepad = std::make_shared(0); - GamepadDevice::Register(kb_gamepad); - mouse_gamepad = std::make_shared(0); - GamepadDevice::Register(mouse_gamepad); - - x11_keyboard_input = (cfgLoadInt("input", "enable_x11_keyboard", 1) >= 1); - if (!x11_keyboard_input) - INFO_LOG(INPUT, "X11 Keyboard input disabled by config."); + x11Keyboard = std::make_shared(0); + GamepadDevice::Register(x11Keyboard); + x11Mouse = std::make_shared(); + GamepadDevice::Register(x11Mouse); } void x11_window_create() diff --git a/core/linux-dist/x11_keyboard.h b/core/linux-dist/x11_keyboard.h index ee069a4be..c1c388456 100644 --- a/core/linux-dist/x11_keyboard.h +++ b/core/linux-dist/x11_keyboard.h @@ -3,10 +3,10 @@ #include "input/keyboard_device.h" #include "x11.h" -class X11KeyboardDevice : public KeyboardDeviceTemplate +class X11Keyboard : public KeyboardDeviceTemplate { public: - X11KeyboardDevice(int maple_port) : KeyboardDeviceTemplate(maple_port) + X11Keyboard(int maple_port) : KeyboardDeviceTemplate(maple_port, "X11") { //04-1D Letter keys A-Z (in alphabetic order) kb_map[KEY_A] = 0x04; @@ -125,275 +125,35 @@ public: kb_map[90] = 0x62; //63 "." (Numeric keypad) kb_map[91] = 0x63; - //64 #| (non-US) - //kb_map[94] = 0x64; //65 S3 key //66-A4 Not used //A5-DF Reserved kb_map[KEY_LCTRL] = 0xE0; // Left Control kb_map[KEY_LSHIFT] = 0xE1; // Left Shift - //E2 Left Alt + kb_map[KEY_LALT] = 0xE2; // Left Alt //E3 Left S1 kb_map[KEY_RCTRL] = 0xE4; // Right Control kb_map[KEY_RSHIFT] = 0xE5; // Right Shift - //E6 Right Alt + // AltGr + kb_map[108] = 0xE6; // Right Alt + kb_map[135] = 0x65; // Menu / S3 //E7 Right S3 //E8-FF Reserved + + _unique_id = "x11_keyboard"; + loadMapping(); } - const char* name() override { return "X11 Keyboard"; } protected: u8 convert_keycode(int keycode) override { + if (kb_map.find(keycode) == kb_map.end()) { + DEBUG_LOG(INPUT, "Unknown key %x", keycode); + return 0; + } return kb_map[keycode]; } private: std::map kb_map; }; - -class KbInputMapping : public InputMapping -{ -public: - KbInputMapping() - { - name = "X11 Keyboard"; - set_button(DC_BTN_A, KEY_X); - set_button(DC_BTN_B, KEY_C); - set_button(DC_BTN_X, KEY_S); - set_button(DC_BTN_Y, KEY_D); - set_button(DC_DPAD_UP, KEY_UP); - set_button(DC_DPAD_DOWN, KEY_DOWN); - set_button(DC_DPAD_LEFT, KEY_LEFT); - set_button(DC_DPAD_RIGHT, KEY_RIGHT); - set_button(DC_BTN_START, KEY_RETURN); - set_button(EMU_BTN_TRIGGER_LEFT, KEY_F); - set_button(EMU_BTN_TRIGGER_RIGHT, KEY_V); - set_button(EMU_BTN_MENU, KEY_TAB); - set_button(EMU_BTN_FFORWARD, KEY_SPACE); - - dirty = false; - } -}; - -class X11KbGamepadDevice : public GamepadDevice -{ -public: - X11KbGamepadDevice(int maple_port) : GamepadDevice(maple_port, "X11") - { - _name = "Keyboard"; - _unique_id = "x11_keyboard"; - if (!find_mapping()) - input_mapper = std::make_shared(); - } - - const char *get_button_name(u32 code) override - { - switch (code) - { - case KEY_A: - return "A"; - case KEY_B: - return "B"; - case KEY_C: - return "C"; - case KEY_D: - return "D"; - case KEY_E: - return "E"; - case KEY_F: - return "F"; - case KEY_G: - return "G"; - case KEY_H: - return "H"; - case KEY_I: - return "I"; - case KEY_J: - return "J"; - case KEY_K: - return "K"; - case KEY_L: - return "L"; - case KEY_M: - return "M"; - case KEY_N: - return "N"; - case KEY_O: - return "O"; - case KEY_P: - return "P"; - case KEY_Q: - return "Q"; - case KEY_R: - return "R"; - case KEY_S: - return "S"; - case KEY_T: - return "T"; - case KEY_U: - return "U"; - case KEY_V: - return "V"; - case KEY_W: - return "W"; - case KEY_X: - return "X"; - case KEY_Y: - return "Y"; - case KEY_Z: - return "Z"; - - case KEY_0: - return "0"; - case KEY_1: - return "1"; - case KEY_2: - return "2"; - case KEY_3: - return "3"; - case KEY_4: - return "4"; - case KEY_5: - return "5"; - case KEY_6: - return "6"; - case KEY_7: - return "7"; - case KEY_8: - return "8"; - case KEY_9: - return "9"; - - case KEY_BACKSPACE: - return "Backspace"; - case KEY_DEL: - return "Delete"; - case KEY_DOWN: - return "Down"; - case KEY_UP: - return "Up"; - case KEY_LEFT: - return "Left"; - case KEY_RIGHT: - return "Right"; - case KEY_END: - return "End"; - case KEY_ESC: - return "Escape"; - - case KEY_F1: - return "F1"; - case KEY_F2: - return "F2"; - case KEY_F3: - return "F3"; - case KEY_F4: - return "F4"; - case KEY_F5: - return "F5"; - case KEY_F6: - return "F6"; - case KEY_F7: - return "F7"; - case KEY_F8: - return "F8"; - case KEY_F9: - return "F9"; - case KEY_F10: - return "F10"; - case KEY_F11: - return "F11"; - case KEY_F12: - return "F12"; - - case KEY_HOME: - return "Home"; - case KEY_INS: - return "Insert"; - case KEY_RETURN: - return "Return"; - case KEY_LALT: - return "Left ALT"; - case KEY_LCTRL: - return "Left CTRL"; - case KEY_LSHIFT: - return "Left SHIFT"; - case KEY_RCTRL: - return "Right CTRL"; - case KEY_RSHIFT: - return "Right Shift"; - case 108: - return "Right ALT"; - case KEY_PGDOWN: - return "Page Down"; - case KEY_PGUP: - return "Page Up"; - case KEY_SPACE: - return "Space"; - case KEY_TAB: - return "Tab"; - - case 87: - return "Num 1"; - case 88: - return "Num 2"; - case 89: - return "Num 3"; - case 83: - return "Num 4"; - case 84: - return "Num 5"; - case 85: - return "Num 6"; - case 79: - return "Num 7"; - case 80: - return "Num 8"; - case 81: - return "Num 9"; - case 90: - return "Num 0"; - case 91: - return "Num ."; - case 106: - return "Num /"; - case 63: - return "Num *"; - case 82: - return "Num -"; - case 86: - return "Num +"; - case 104: - return "Num Enter"; - - case 20: - return "-"; - case 21: - return "="; - case 34: - return "["; - case 35: - return "]"; - case 47: - return ";"; - case 48: - return "'"; - case 49: - return "`"; - case 51: - return "\\"; - case 59: - return ","; - case 60: - return "."; - case 61: - return "/"; - case 94: - return "102nd"; - - default: - return nullptr; - } - } -}; diff --git a/core/oslib/audiobackend_directsound.cpp b/core/oslib/audiobackend_directsound.cpp index 5e0262d92..a525e5c2a 100644 --- a/core/oslib/audiobackend_directsound.cpp +++ b/core/oslib/audiobackend_directsound.cpp @@ -2,14 +2,12 @@ #include "audiostream.h" #include #include -#ifdef USE_SDL -#include "sdl/sdl.h" -#endif #include #include #include #include "stdclass.h" +HWND getNativeHwnd(); #define verifyc(x) verify(!FAILED(x)) static IDirectSound8* dsound; @@ -65,12 +63,8 @@ static void audioThreadMain() static void directsound_init() { verifyc(DirectSoundCreate8(NULL, &dsound, NULL)); + verifyc(dsound->SetCooperativeLevel(getNativeHwnd(), DSSCL_PRIORITY)); -#ifdef USE_SDL - verifyc(dsound->SetCooperativeLevel(sdl_get_native_hwnd(), DSSCL_PRIORITY)); -#else - verifyc(dsound->SetCooperativeLevel((HWND)libPvr_GetRenderTarget(), DSSCL_PRIORITY)); -#endif // Set up WAV format structure. WAVEFORMATEX wfx; memset(&wfx, 0, sizeof(WAVEFORMATEX)); diff --git a/core/rend/CustomTexture.cpp b/core/rend/CustomTexture.cpp index f3dcfa322..3be3d5f8c 100644 --- a/core/rend/CustomTexture.cpp +++ b/core/rend/CustomTexture.cpp @@ -182,50 +182,90 @@ void CustomTexture::DumpTexture(u32 hash, int w, int h, TextureType textype, voi for (int y = 0; y < h; y++) { - switch (textype) + if (!config::RendererType.isDirectX()) { - case TextureType::_4444: - for (int x = 0; x < w; x++) + switch (textype) { - *dst++ = ((*src >> 12) & 0xF) << 4; - *dst++ = ((*src >> 8) & 0xF) << 4; - *dst++ = ((*src >> 4) & 0xF) << 4; - *dst++ = (*src & 0xF) << 4; - src++; + case TextureType::_4444: + for (int x = 0; x < w; x++) + { + *dst++ = ((*src >> 12) & 0xF) << 4; + *dst++ = ((*src >> 8) & 0xF) << 4; + *dst++ = ((*src >> 4) & 0xF) << 4; + *dst++ = (*src & 0xF) << 4; + src++; + } + break; + case TextureType::_565: + for (int x = 0; x < w; x++) + { + *(u32 *)dst = Unpacker565_32::unpack(*src); + dst += 4; + src++; + } + break; + case TextureType::_5551: + for (int x = 0; x < w; x++) + { + *dst++ = ((*src >> 11) & 0x1F) << 3; + *dst++ = ((*src >> 6) & 0x1F) << 3; + *dst++ = ((*src >> 1) & 0x1F) << 3; + *dst++ = (*src & 1) ? 255 : 0; + src++; + } + break; + case TextureType::_8888: + memcpy(dst, src, w * 4); + dst += w * 4; + src += w * 2; + break; + default: + WARN_LOG(RENDERER, "dumpTexture: unsupported picture format %x", (u32)textype); + free(dst_buffer); + return; } - break; - case TextureType::_565: - for (int x = 0; x < w; x++) + } + else + { + switch (textype) { - *dst++ = ((*src >> 11) & 0x1F) << 3; - *dst++ = ((*src >> 5) & 0x3F) << 2; - *dst++ = (*src & 0x1F) << 3; - *dst++ = 255; - src++; + case TextureType::_4444: + for (int x = 0; x < w; x++) + { + *(u32 *)dst = Unpacker4444_32::unpack(*src); + dst += 4; + src++; + } + break; + case TextureType::_565: + for (int x = 0; x < w; x++) + { + *(u32 *)dst = Unpacker565_32::unpack(*src); + dst += 4; + src++; + } + break; + case TextureType::_5551: + for (int x = 0; x < w; x++) + { + *(u32 *)dst = Unpacker1555_32::unpack(*src); + dst += 4; + src++; + } + break; + case TextureType::_8888: + for (int x = 0; x < w; x++) + { + *(u32 *)dst = Unpacker8888::unpack(*(u32 *)src); + dst += 4; + src += 2; + } + break; + default: + WARN_LOG(RENDERER, "dumpTexture: unsupported picture format %x", (u32)textype); + free(dst_buffer); + return; } - break; - case TextureType::_5551: - for (int x = 0; x < w; x++) - { - *dst++ = ((*src >> 11) & 0x1F) << 3; - *dst++ = ((*src >> 6) & 0x1F) << 3; - *dst++ = ((*src >> 1) & 0x1F) << 3; - *dst++ = (*src & 1) ? 255 : 0; - src++; - } - break; - case TextureType::_8888: - for (int x = 0; x < w; x++) - { - *(u32 *)dst = *(u32 *)src; - dst += 4; - src += 2; - } - break; - default: - WARN_LOG(RENDERER, "dumpTexture: unsupported picture format %x", (u32)textype); - free(dst_buffer); - return; } } diff --git a/core/rend/TexCache.cpp b/core/rend/TexCache.cpp index 08237c0a6..ebfd87b4a 100644 --- a/core/rend/TexCache.cpp +++ b/core/rend/TexCache.cpp @@ -91,39 +91,74 @@ void palette_update() pal_needs_update = false; palette_updated = true; - switch(PAL_RAM_CTRL&3) + if (!config::RendererType.isDirectX()) { - case 0: - for (int i=0;i<1024;i++) + switch(PAL_RAM_CTRL&3) { - palette16_ram[i] = ARGB1555(PALETTE_RAM[i]); - palette32_ram[i] = ARGB1555_32(PALETTE_RAM[i]); - } - break; + case 0: + for (int i=0;i<1024;i++) + { + palette16_ram[i] = Unpacker1555::unpack(PALETTE_RAM[i]); + palette32_ram[i] = Unpacker1555_32::unpack(PALETTE_RAM[i]); + } + break; - case 1: - for (int i=0;i<1024;i++) - { - palette16_ram[i] = ARGB565(PALETTE_RAM[i]); - palette32_ram[i] = ARGB565_32(PALETTE_RAM[i]); - } - break; + case 1: + for (int i=0;i<1024;i++) + { + palette16_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); + palette32_ram[i] = Unpacker565_32::unpack(PALETTE_RAM[i]); + } + break; - case 2: - for (int i=0;i<1024;i++) - { - palette16_ram[i] = ARGB4444(PALETTE_RAM[i]); - palette32_ram[i] = ARGB4444_32(PALETTE_RAM[i]); - } - break; + case 2: + for (int i=0;i<1024;i++) + { + palette16_ram[i] = Unpacker4444::unpack(PALETTE_RAM[i]); + palette32_ram[i] = Unpacker4444_32::unpack(PALETTE_RAM[i]); + } + break; - case 3: - for (int i=0;i<1024;i++) - { - palette16_ram[i] = ARGB8888(PALETTE_RAM[i]); - palette32_ram[i] = ARGB8888_32(PALETTE_RAM[i]); + case 3: + for (int i=0;i<1024;i++) + palette32_ram[i] = Unpacker8888::unpack(PALETTE_RAM[i]); + break; + } + } + else + { + switch(PAL_RAM_CTRL&3) + { + + case 0: + for (int i=0;i<1024;i++) + { + palette16_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); + palette32_ram[i] = Unpacker1555_32::unpack(PALETTE_RAM[i]); + } + break; + + case 1: + for (int i=0;i<1024;i++) + { + palette16_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); + palette32_ram[i] = Unpacker565_32::unpack(PALETTE_RAM[i]); + } + break; + + case 2: + for (int i=0;i<1024;i++) + { + palette16_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); + palette32_ram[i] = Unpacker4444_32::unpack(PALETTE_RAM[i]); + } + break; + + case 3: + for (int i=0;i<1024;i++) + palette32_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); + break; } - break; } for (int i = 0; i < 64; i++) pal_hash_16[i] = XXH32(&PALETTE_RAM[i << 4], 16 * 4, 7); @@ -258,12 +293,6 @@ static void libCore_vramlock_Unlock_block_wb(vram_block* block) free(block); } -void libCore_vramlock_Unlock_block(vram_block* block) -{ - std::lock_guard lock(vramlist_lock); - libCore_vramlock_Unlock_block_wb(block); -} - #ifndef TARGET_NO_OPENMP static inline int getThreadCount() { @@ -309,28 +338,38 @@ struct PvrTexInfo int bpp; //4/8 for pal. 16 for yuv, rgb, argb TextureType type; // Conversion to 16 bpp - TexConvFP *PL; - TexConvFP *TW; - TexConvFP *VQ; + TexConvFP PL; + TexConvFP TW; + TexConvFP VQ; // Conversion to 32 bpp - TexConvFP32 *PL32; - TexConvFP32 *TW32; - TexConvFP32 *VQ32; + TexConvFP32 PL32; + TexConvFP32 TW32; + TexConvFP32 VQ32; // Conversion to 8 bpp (palette) - TexConvFP8 *TW8; + TexConvFP8 TW8; }; -static const PvrTexInfo format[8] = -{ // name bpp Final format Planar Twiddled VQ Planar(32b) Twiddled(32b) VQ (32b) Palette (8b) - {"1555", 16, TextureType::_5551, tex1555_PL, tex1555_TW, tex1555_VQ, tex1555_PL32, tex1555_TW32, tex1555_VQ32, nullptr }, //1555 - {"565", 16, TextureType::_565, tex565_PL, tex565_TW, tex565_VQ, tex565_PL32, tex565_TW32, tex565_VQ32, nullptr }, //565 - {"4444", 16, TextureType::_4444, tex4444_PL, tex4444_TW, tex4444_VQ, tex4444_PL32, tex4444_TW32, tex4444_VQ32, nullptr }, //4444 - {"yuv", 16, TextureType::_8888, nullptr, nullptr, nullptr, texYUV422_PL, texYUV422_TW, texYUV422_VQ, nullptr }, //yuv - {"bumpmap", 16, TextureType::_4444, texBMP_PL, texBMP_TW, texBMP_VQ, tex4444_PL32, tex4444_TW32, tex4444_VQ32, nullptr }, //bump map - {"pal4", 4, TextureType::_5551, nullptr, texPAL4_TW, texPAL4_VQ, nullptr, texPAL4_TW32, texPAL4_VQ32, texPAL4PT_TW }, //pal4 - {"pal8", 8, TextureType::_5551, nullptr, texPAL8_TW, texPAL8_VQ, nullptr, texPAL8_TW32, texPAL8_VQ32, texPAL8PT_TW }, //pal8 - {"ns/1555", 0}, // Not supported (1555) -}; +#define TEX_CONV_TABLE \ +const PvrTexInfo pvrTexInfo[8] = \ +{ /* name bpp Final format Planar Twiddled VQ Planar(32b) Twiddled(32b) VQ (32b) Palette (8b) */ \ + {"1555", 16, TextureType::_5551, tex1555_PL, tex1555_TW, tex1555_VQ, tex1555_PL32, tex1555_TW32, tex1555_VQ32, nullptr }, \ + {"565", 16, TextureType::_565, tex565_PL, tex565_TW, tex565_VQ, tex565_PL32, tex565_TW32, tex565_VQ32, nullptr }, \ + {"4444", 16, TextureType::_4444, tex4444_PL, tex4444_TW, tex4444_VQ, tex4444_PL32, tex4444_TW32, tex4444_VQ32, nullptr }, \ + {"yuv", 16, TextureType::_8888, nullptr, nullptr, nullptr, texYUV422_PL, texYUV422_TW, texYUV422_VQ, nullptr }, \ + {"bumpmap", 16, TextureType::_4444, texBMP_PL, texBMP_TW, texBMP_VQ, tex4444_PL32, tex4444_TW32, tex4444_VQ32, nullptr }, \ + {"pal4", 4, TextureType::_5551, nullptr, texPAL4_TW, texPAL4_VQ, nullptr, texPAL4_TW32, texPAL4_VQ32, texPAL4PT_TW }, \ + {"pal8", 8, TextureType::_5551, nullptr, texPAL8_TW, texPAL8_VQ, nullptr, texPAL8_TW32, texPAL8_VQ32, texPAL8PT_TW }, \ + {"ns/1555", 0}, \ +} + +namespace opengl { + TEX_CONV_TABLE; +} +namespace directx { + TEX_CONV_TABLE; +} +#undef TEX_CONV_TABLE +static const PvrTexInfo *pvrTexInfo = opengl::pvrTexInfo; static const u32 VQMipPoint[11] = { @@ -427,20 +466,15 @@ void BaseTextureCacheData::Create() lock_block = nullptr; custom_image_data = nullptr; custom_load_in_progress = 0; + gpuPalette = false; //decode info from tsp/tcw into the texture struct - tex = &format[tcw.PixelFmt == PixelReserved ? Pixel1555 : tcw.PixelFmt]; //texture format table entry + tex = &pvrTexInfo[tcw.PixelFmt == PixelReserved ? Pixel1555 : tcw.PixelFmt]; //texture format table entry sa_tex = (tcw.TexAddr << 3) & VRAM_MASK; //texture start address sa = sa_tex; //data texture start address (modified for MIPs, as needed) - w = 8 << tsp.TexU; //tex width - h = 8 << tsp.TexV; //tex height - - //PAL texture - if (tex->bpp == 4) - palette_index = tcw.PalSelect << 4; - else if (tex->bpp == 8) - palette_index = (tcw.PalSelect >> 4) << 8; + width = 8 << tsp.TexU; //tex width + height = 8 << tsp.TexV; //tex height texconv8 = nullptr; @@ -460,7 +494,7 @@ void BaseTextureCacheData::Create() } //Planar textures support stride selection, mostly used for non power of 2 textures (videos) - int stride = w; + int stride = width; if (tcw.StrideSel) stride = (TEXT_CONTROL & 31) * 32; @@ -468,26 +502,28 @@ void BaseTextureCacheData::Create() texconv = tex->PL; texconv32 = tex->PL32; //calculate the size, in bytes, for the locking - size = stride * h * tex->bpp / 8; + size = stride * height * tex->bpp / 8; } else { - tcw.ScanOrder = 0; - tcw.StrideSel = 0; + if (!IsPaletted()) + { + tcw.ScanOrder = 0; + tcw.StrideSel = 0; + } // Quake 3 Arena uses one if (tcw.MipMapped) // Mipmapped texture must be square and TexV is ignored - h = w; + height = width; if (tcw.VQ_Comp) { verify(tex->VQ != NULL || tex->VQ32 != NULL); - vq_codebook = sa; if (tcw.MipMapped) sa += VQMipPoint[tsp.TexU + 3]; texconv = tex->VQ; texconv32 = tex->VQ32; - size = w * h / 8; + size = width * height / 8 + 256 * 8; } else { @@ -496,7 +532,7 @@ void BaseTextureCacheData::Create() sa += OtherMipPoint[tsp.TexU + 3] * tex->bpp / 8; texconv = tex->TW; texconv32 = tex->TW32; - size = w * h * tex->bpp / 8; + size = width * height * tex->bpp / 8; texconv8 = tex->TW8; } } @@ -504,26 +540,39 @@ void BaseTextureCacheData::Create() void BaseTextureCacheData::ComputeHash() { - texture_hash = XXH32(&vram[sa], size, 7); + u32 hashSize = size; + if (tcw.VQ_Comp) + { + // The size for VQ textures wasn't correctly calculated. + // We use the old size to compute the hash for backward-compatibility + // with existing custom texture packs. + hashSize = size - 256 * 8; + } + texture_hash = XXH32(&vram[sa], hashSize, 7); if (IsPaletted()) texture_hash ^= palette_hash; old_texture_hash = texture_hash; - texture_hash ^= tcw.full & 0xFC000000; // everything but texaddr, reserved and stride + // Include everything but texaddr, reserved and stride. Palette textures don't have ScanOrder + const u32 tcwMask = IsPaletted() ? 0xF8000000 : 0xFC000000; + texture_hash ^= tcw.full & tcwMask; } void BaseTextureCacheData::Update() { //texture state tracking stuff Updates++; - dirty=0; - + dirty = 0; + gpuPalette = false; tex_type = tex->type; bool has_alpha = false; if (IsPaletted()) { if (IsGpuHandledPaletted(tsp, tcw)) + { tex_type = TextureType::_8; + gpuPalette = true; + } else { tex_type = PAL_TYPE[PAL_RAM_CTRL&3]; @@ -532,30 +581,37 @@ void BaseTextureCacheData::Update() } // Get the palette hash to check for future updates + // TODO get rid of ::palette_index and ::vq_codebook if (tcw.PixelFmt == PixelPal4) + { palette_hash = pal_hash_16[tcw.PalSelect]; + ::palette_index = tcw.PalSelect << 4; + } else + { palette_hash = pal_hash_256[tcw.PalSelect >> 4]; + ::palette_index = (tcw.PalSelect >> 4) << 8; + } } - ::palette_index = this->palette_index; // might be used if pal. tex - ::vq_codebook = &vram[vq_codebook]; // might be used if VQ tex + if (tcw.VQ_Comp) + ::vq_codebook = &vram[sa_tex]; // might be used if VQ tex //texture conversion work - u32 stride = w; + u32 stride = width; if (tcw.StrideSel && tcw.ScanOrder && (tex->PL || tex->PL32)) stride = (TEXT_CONTROL & 31) * 32; - u32 original_h = h; + u32 original_h = height; if (sa_tex > VRAM_SIZE || size == 0 || sa + size > VRAM_SIZE) { if (sa < VRAM_SIZE && sa + size > VRAM_SIZE && tcw.ScanOrder && stride > 0) { // Shenmue Space Harrier mini-arcade loads a texture that goes beyond the end of VRAM // but only uses the top portion of it - h = (VRAM_SIZE - sa) * 8 / stride / tex->bpp; - size = stride * h * tex->bpp/8; + height = (VRAM_SIZE - sa) * 8 / stride / tex->bpp; + size = stride * height * tex->bpp/8; } else { @@ -567,8 +623,8 @@ void BaseTextureCacheData::Update() custom_texture.LoadCustomTextureAsync(this); void *temp_tex_buffer = NULL; - u32 upscaled_w = w; - u32 upscaled_h = h; + u32 upscaled_w = width; + u32 upscaled_h = height; PixelBuffer pb16; PixelBuffer pb32; @@ -577,7 +633,7 @@ void BaseTextureCacheData::Update() // Figure out if we really need to use a 32-bit pixel buffer bool textureUpscaling = config::TextureUpscale > 1 // Don't process textures that are too big - && (int)(w * h) <= config::MaxFilteredTextureSize * config::MaxFilteredTextureSize + && (int)(width * height) <= config::MaxFilteredTextureSize * config::MaxFilteredTextureSize // Don't process YUV textures && tcw.PixelFmt != PixelYUV; bool need_32bit_buffer = true; @@ -600,7 +656,7 @@ void BaseTextureCacheData::Update() if (mipmapped) { - pb32.init(w, h, true); + pb32.init(width, height, true); for (u32 i = 0; i <= tsp.TexU + 3u; i++) { pb32.set_mipmap(i); @@ -621,7 +677,7 @@ void BaseTextureCacheData::Update() vram_addr = sa_tex + OtherMipPoint[i] * tex->bpp / 8; if (tcw.PixelFmt == PixelYUV && i == 0) // Special case for YUV at 1x1 LoD - format[Pixel565].TW32(&pb32, &vram[vram_addr], 1, 1); + pvrTexInfo[Pixel565].TW32(&pb32, &vram[vram_addr], 1, 1); else texconv32(&pb32, &vram[vram_addr], 1 << i, 1 << i); } @@ -629,19 +685,19 @@ void BaseTextureCacheData::Update() } else { - pb32.init(w, h); - texconv32(&pb32, (u8*)&vram[sa], stride, h); + pb32.init(width, height); + texconv32(&pb32, (u8*)&vram[sa], stride, height); // xBRZ scaling if (textureUpscaling) { PixelBuffer tmp_buf; - tmp_buf.init(w * config::TextureUpscale, h * config::TextureUpscale); + tmp_buf.init(width * config::TextureUpscale, height * config::TextureUpscale); if (tcw.PixelFmt == Pixel1555 || tcw.PixelFmt == Pixel4444) // Alpha channel formats. Palettes with alpha are already handled has_alpha = true; - UpscalexBRZ(config::TextureUpscale, pb32.data(), tmp_buf.data(), w, h, has_alpha); + UpscalexBRZ(config::TextureUpscale, pb32.data(), tmp_buf.data(), width, height, has_alpha); pb32.steal_data(tmp_buf); upscaled_w *= config::TextureUpscale; upscaled_h *= config::TextureUpscale; @@ -653,7 +709,8 @@ void BaseTextureCacheData::Update() { if (mipmapped) { - pb8.init(w, h, true); + // This shouldn't happen since mipmapped palette textures are converted to rgba + pb8.init(width, height, true); for (u32 i = 0; i <= tsp.TexU + 3u; i++) { pb8.set_mipmap(i); @@ -664,8 +721,8 @@ void BaseTextureCacheData::Update() } else { - pb8.init(w, h); - texconv8(&pb8, &vram[sa], stride, h); + pb8.init(width, height); + texconv8(&pb8, &vram[sa], stride, height); } temp_tex_buffer = pb8.data(); } @@ -673,7 +730,7 @@ void BaseTextureCacheData::Update() { if (mipmapped) { - pb16.init(w, h, true); + pb16.init(width, height, true); for (u32 i = 0; i <= tsp.TexU + 3u; i++) { pb16.set_mipmap(i); @@ -698,8 +755,8 @@ void BaseTextureCacheData::Update() } else { - pb16.init(w, h); - texconv(&pb16,(u8*)&vram[sa],stride,h); + pb16.init(width, height); + texconv(&pb16,(u8*)&vram[sa],stride,height); } temp_tex_buffer = pb16.data(); } @@ -707,13 +764,13 @@ void BaseTextureCacheData::Update() { //fill it in with a temp color WARN_LOG(RENDERER, "UNHANDLED TEXTURE"); - pb16.init(w, h); - memset(pb16.data(), 0x80, w * h * 2); + pb16.init(width, height); + memset(pb16.data(), 0x80, width * height * 2); temp_tex_buffer = pb16.data(); mipmapped = false; } // Restore the original texture height if it was constrained to VRAM limits above - h = original_h; + height = original_h; //lock the texture to detect changes in it libCore_vramlock_Lock(sa_tex, sa + size - 1, this); @@ -733,12 +790,18 @@ void BaseTextureCacheData::CheckCustomTexture() if (IsCustomTextureAvailable()) { tex_type = TextureType::_8888; + gpuPalette = false; UploadToGPU(custom_width, custom_height, custom_image_data, IsMipmapped(), false); free(custom_image_data); - custom_image_data = NULL; + custom_image_data = nullptr; } } +void BaseTextureCacheData::SetDirectXColorOrder(bool enabled) { + pvrTexInfo = enabled ? directx::pvrTexInfo : opengl::pvrTexInfo; +} + +template void ReadFramebuffer(PixelBuffer& pb, int& width, int& height) { width = (FB_R_SIZE.fb_x_size + 1) << 1; // in 16-bit words @@ -784,7 +847,7 @@ void ReadFramebuffer(PixelBuffer& pb, int& width, int& height) } pb.init(width, height); - u8 *dst = (u8*)pb.data(); + u32 *dst = (u32 *)pb.data(); switch (FB_R_CTRL.fb_depth) { @@ -794,10 +857,11 @@ void ReadFramebuffer(PixelBuffer& pb, int& width, int& height) for (int i = 0; i < width; i++) { u16 src = pvr_read32p(addr); - *dst++ = (((src >> 10) & 0x1F) << 3) + FB_R_CTRL.fb_concat; - *dst++ = (((src >> 5) & 0x1F) << 3) + FB_R_CTRL.fb_concat; - *dst++ = (((src >> 0) & 0x1F) << 3) + FB_R_CTRL.fb_concat; - *dst++ = 0xFF; + *dst++ = Packer::pack( + (((src >> 10) & 0x1F) << 3) + FB_R_CTRL.fb_concat, + (((src >> 5) & 0x1F) << 3) + FB_R_CTRL.fb_concat, + (((src >> 0) & 0x1F) << 3) + FB_R_CTRL.fb_concat, + 0xff); addr += bpp; } addr += modulus * bpp; @@ -810,10 +874,11 @@ void ReadFramebuffer(PixelBuffer& pb, int& width, int& height) for (int i = 0; i < width; i++) { u16 src = pvr_read32p(addr); - *dst++ = (((src >> 11) & 0x1F) << 3) + FB_R_CTRL.fb_concat; - *dst++ = (((src >> 5) & 0x3F) << 2) + (FB_R_CTRL.fb_concat & 3); - *dst++ = (((src >> 0) & 0x1F) << 3) + FB_R_CTRL.fb_concat; - *dst++ = 0xFF; + *dst++ = Packer::pack( + (((src >> 11) & 0x1F) << 3) + FB_R_CTRL.fb_concat, + (((src >> 5) & 0x3F) << 2) + (FB_R_CTRL.fb_concat & 3), + (((src >> 0) & 0x1F) << 3) + FB_R_CTRL.fb_concat, + 0xFF); addr += bpp; } addr += modulus * bpp; @@ -825,33 +890,21 @@ void ReadFramebuffer(PixelBuffer& pb, int& width, int& height) for (int i = 0; i < width; i += 4) { u32 src = pvr_read32p(addr); - *dst++ = src >> 16; - *dst++ = src >> 8; - *dst++ = src; - *dst++ = 0xFF; + *dst++ = Packer::pack(src >> 16, src >> 8, src, 0xff); addr += 4; if (i + 1 >= width) break; u32 src2 = pvr_read32p(addr); - *dst++ = src2 >> 8; - *dst++ = src2; - *dst++ = src >> 24; - *dst++ = 0xFF; + *dst++ = Packer::pack(src2 >> 8, src2, src >> 24, 0xff); addr += 4; if (i + 2 >= width) break; u32 src3 = pvr_read32p(addr); - *dst++ = src3; - *dst++ = src2 >> 24; - *dst++ = src2 >> 16; - *dst++ = 0xFF; + *dst++ = Packer::pack(src3, src2 >> 24, src2 >> 16, 0xff); addr += 4; if (i + 3 >= width) break; - *dst++ = src3 >> 24; - *dst++ = src3 >> 16; - *dst++ = src3 >> 8; - *dst++ = 0xFF; + *dst++ = Packer::pack(src3 >> 24, src3 >> 16, src3 >> 8, 0xff); } addr += modulus * bpp; } @@ -862,10 +915,7 @@ void ReadFramebuffer(PixelBuffer& pb, int& width, int& height) for (int i = 0; i < width; i++) { u32 src = pvr_read32p(addr); - *dst++ = src >> 16; - *dst++ = src >> 8; - *dst++ = src; - *dst++ = 0xFF; + *dst++ = Packer::pack(src >> 16, src >> 8, src, 0xff); addr += bpp; } addr += modulus * bpp; @@ -873,7 +923,10 @@ void ReadFramebuffer(PixelBuffer& pb, int& width, int& height) break; } } +template void ReadFramebuffer(PixelBuffer& pb, int& width, int& height); +template void ReadFramebuffer(PixelBuffer& pb, int& width, int& height); +template void WriteTextureToVRam(u32 width, u32 height, u8 *data, u16 *dst, u32 fb_w_ctrl_in, u32 linestride) { FB_W_CTRL_type fb_w_ctrl; @@ -895,25 +948,25 @@ void WriteTextureToVRam(u32 width, u32 height, u8 *data, u16 *dst, u32 fb_w_ctrl { case 0: //0x0 0555 KRGB 16 bit (default) Bit 15 is the value of fb_kval[7]. for (u32 c = 0; c < width; c++) { - *dst++ = (((p[0] >> 3) & 0x1F) << 10) | (((p[1] >> 3) & 0x1F) << 5) | ((p[2] >> 3) & 0x1F) | kval_bit; + *dst++ = (((p[Red] >> 3) & 0x1F) << 10) | (((p[Green] >> 3) & 0x1F) << 5) | ((p[Blue] >> 3) & 0x1F) | kval_bit; p += 4; } break; case 1: //0x1 565 RGB 16 bit for (u32 c = 0; c < width; c++) { - *dst++ = (((p[0] >> 3) & 0x1F) << 11) | (((p[1] >> 2) & 0x3F) << 5) | ((p[2] >> 3) & 0x1F); + *dst++ = (((p[Red] >> 3) & 0x1F) << 11) | (((p[Green] >> 2) & 0x3F) << 5) | ((p[Blue] >> 3) & 0x1F); p += 4; } break; case 2: //0x2 4444 ARGB 16 bit for (u32 c = 0; c < width; c++) { - *dst++ = (((p[0] >> 4) & 0xF) << 8) | (((p[1] >> 4) & 0xF) << 4) | ((p[2] >> 4) & 0xF) | (((p[3] >> 4) & 0xF) << 12); + *dst++ = (((p[Red] >> 4) & 0xF) << 8) | (((p[Green] >> 4) & 0xF) << 4) | ((p[Blue] >> 4) & 0xF) | (((p[Alpha] >> 4) & 0xF) << 12); p += 4; } break; case 3://0x3 1555 ARGB 16 bit The alpha value is determined by comparison with the value of fb_alpha_threshold. for (u32 c = 0; c < width; c++) { - *dst++ = (((p[0] >> 3) & 0x1F) << 10) | (((p[1] >> 3) & 0x1F) << 5) | ((p[2] >> 3) & 0x1F) | (p[3] > fb_alpha_threshold ? 0x8000 : 0); + *dst++ = (((p[Red] >> 3) & 0x1F) << 10) | (((p[Green] >> 3) & 0x1F) << 5) | ((p[Blue] >> 3) & 0x1F) | (p[Alpha] > fb_alpha_threshold ? 0x8000 : 0); p += 4; } break; @@ -921,6 +974,8 @@ void WriteTextureToVRam(u32 width, u32 height, u8 *data, u16 *dst, u32 fb_w_ctrl dst += padding; } } +template void WriteTextureToVRam<0, 1, 2, 3>(u32 width, u32 height, u8 *data, u16 *dst, u32 fb_w_ctrl_in, u32 linestride); +template void WriteTextureToVRam<2, 1, 0, 3>(u32 width, u32 height, u8 *data, u16 *dst, u32 fb_w_ctrl_in, u32 linestride); static void rend_text_invl(vram_block* bl) { diff --git a/core/rend/TexCache.h b/core/rend/TexCache.h index dc40a91ff..b2e98bb3b 100644 --- a/core/rend/TexCache.h +++ b/core/rend/TexCache.h @@ -21,6 +21,8 @@ extern bool palette_updated; extern u32 detwiddle[2][11][1024]; +void palette_update(); + template class PixelBuffer { @@ -120,407 +122,276 @@ public: } }; -void palette_update(); - #define clamp(minv, maxv, x) ((x) < (minv) ? (minv) : (x) > (maxv) ? (maxv) : (x)) -// Unpack to 16-bit word +// Open GL +struct RGBAPacker { + static u32 pack(u8 r, u8 g, u8 b, u8 a) { + return r | (g << 8) | (b << 16) | (a << 24); + } +}; +// DirectX +struct BGRAPacker { + static u32 pack(u8 r, u8 g, u8 b, u8 a) { + return b | (g << 8) | (r << 16) | (a << 24); + } +}; -#define ARGB1555( word ) ( ((word>>15)&1) | (((word>>10) & 0x1F)<<11) | (((word>>5) & 0x1F)<<6) | (((word>>0) & 0x1F)<<1) ) - -#define ARGB565( word ) ( (((word>>0)&0x1F)<<0) | (((word>>5)&0x3F)<<5) | (((word>>11)&0x1F)<<11) ) - -#define ARGB4444( word ) ( (((word>>0)&0xF)<<4) | (((word>>4)&0xF)<<8) | (((word>>8)&0xF)<<12) | (((word>>12)&0xF)<<0) ) - -#define ARGB8888( word ) ( (((word>>4)&0xF)<<4) | (((word>>12)&0xF)<<8) | (((word>>20)&0xF)<<12) | (((word>>28)&0xF)<<0) ) - -// Unpack to 32-bit word - -#define ARGB1555_32( word ) ( ((word & 0x8000) ? 0xFF000000 : 0) | \ - (((word >> 10) & 0x1F) << 3) | (((word >> 12) & 0x7) << 0) | \ - (((word >> 5) & 0x1F) << 11) | (((word >> 7) & 0x7) << 8) | \ - (((word >> 0) & 0x1F) << 19) | (((word >> 2) & 0x7) << 16) ) - -#define ARGB565_32( word ) ( (((word >> 11) & 0x1F) << 3) | (((word >> 13) & 0x7) << 0) | \ - (((word >> 5) & 0x3F) << 10) | (((word >> 9) & 0x3) << 8) | \ - (((word >> 0) & 0x1F) << 19) | (((word >> 2) & 0x7) << 16) | \ - 0xFF000000 ) - -#define ARGB4444_32( word ) ( (((word >> 12) & 0xF) << 28) | (((word >> 12) & 0xF) << 24) | \ - (((word >> 8) & 0xF) << 4) | (((word >> 8) & 0xF) << 0) | \ - (((word >> 4) & 0xF) << 12) | (((word >> 4) & 0xF) << 8) | \ - (((word >> 0) & 0xF) << 20) | (((word >> 0) & 0xF) << 16) ) - -#define ARGB8888_32( word ) ( ((word >> 0) & 0xFF000000) | (((word >> 16) & 0xFF) << 0) | (((word >> 8) & 0xFF) << 8) | ((word & 0xFF) << 16) ) - -inline static u32 YUV422(s32 Y,s32 Yu,s32 Yv) +template +inline static u32 YUV422(s32 Y, s32 Yu, s32 Yv) { - Yu-=128; - Yv-=128; + Yu -= 128; + Yv -= 128; - s32 R = Y + Yv*11/8; // Y + (Yv-128) * (11/8) ? - s32 G = Y - (Yu*11 + Yv*22)/32; // Y - (Yu-128) * (11/8) * 0.25 - (Yv-128) * (11/8) * 0.5 ? - s32 B = Y + Yu*110/64; // Y + (Yu-128) * (11/8) * 1.25 ? + s32 R = Y + Yv * 11 / 8; // Y + (Yv-128) * (11/8) ? + s32 G = Y - (Yu * 11 + Yv * 22) / 32; // Y - (Yu-128) * (11/8) * 0.25 - (Yv-128) * (11/8) * 0.5 ? + s32 B = Y + Yu * 110 / 64; // Y + (Yu-128) * (11/8) * 1.25 ? - return clamp(0, 255, R) | (clamp(0, 255, G) << 8) | (clamp(0, 255, B) << 16) | 0xFF000000; + return Packer::pack(clamp(0, 255, R), clamp(0, 255, G), clamp(0, 255, B), 0xFF); } #define twop(x,y,bcx,bcy) (detwiddle[0][bcy][x]+detwiddle[1][bcx][y]) -//pixel convertors ! -#define pixelcvt_start_base(name,x,y,type) \ - struct name \ - { \ - static const u32 xpp=x;\ - static const u32 ypp=y; \ - __forceinline static void Convert(PixelBuffer* pb,u8* data) \ - { +template +struct UnpackerNop { + using unpacked_type = Pixel; + static Pixel unpack(Pixel word) { + return word; + } +}; +// ARGB1555 to RGBA5551 +struct Unpacker1555 { + using unpacked_type = u16; + static u16 unpack(u16 word) { + return ((word >> 15) & 1) | (((word >> 10) & 0x1F) << 11) | (((word >> 5) & 0x1F) << 6) | (((word >> 0) & 0x1F) << 1); + } +}; +// ARGB4444 to RGBA4444 +struct Unpacker4444 { + using unpacked_type = u16; + static u16 unpack(u16 word) { + return (((word >> 0) & 0xF) << 4) | (((word >> 4) & 0xF) << 8) | (((word >> 8) & 0xF) << 12) | (((word >> 12) & 0xF) << 0); + } +}; -#define pixelcvt_start(name,x,y) pixelcvt_start_base(name, x, y, u16) -#define pixelcvt32_start(name,x,y) pixelcvt_start_base(name, x, y, u32) +template +struct Unpacker1555_32 { + using unpacked_type = u32; + static u32 unpack(u16 word) { + return Packer::pack( + (((word >> 10) & 0x1F) << 3) | ((word >> 12) & 7), + (((word >> 5) & 0x1F) << 3) | ((word >> 7) & 7), + (((word >> 0) & 0x1F) << 3) | ((word >> 2) & 7), + (word & 0x8000) ? 0xFF : 0); + } +}; +template +struct Unpacker565_32 { + using unpacked_type = u32; + static u32 unpack(u16 word) { + return Packer::pack( + (((word >> 11) & 0x1F) << 3) | ((word >> 13) & 7), + (((word >> 5) & 0x3F) << 2) | ((word >> 9) & 3), + (((word >> 0) & 0x1F) << 3) | ((word >> 2) & 7), + 0xFF); + } +}; +template +struct Unpacker4444_32 { + using unpacked_type = u32; + static u32 unpack(u16 word) { + return Packer::pack( + (((word >> 8) & 0xF) << 4) | ((word >> 8) & 0xF), + (((word >> 4) & 0xF) << 4) | ((word >> 4) & 0xF), + (((word >> 0) & 0xF) << 4) | ((word >> 0) & 0xF), + (((word >> 12) & 0xF) << 4) | ((word >> 12) & 0xF)); + } +}; +// ARGB8888 to whatever +template +struct Unpacker8888 { + using unpacked_type = u32; + static u32 unpack(u32 word) { + return Packer::pack( + (word >> 16) & 0xFF, + (word >> 8) & 0xFF, + (word >> 0) & 0xFF, + (word >> 24) & 0xFF); + } +}; -#define pixelcvt_size_start(name, x, y) template \ -struct name \ -{ \ - static const u32 xpp=x;\ - static const u32 ypp=y; \ - __forceinline static void Convert(PixelBuffer* pb,u8* data) \ +template +struct ConvertPlanar { + using unpacked_type = typename Unpacker::unpacked_type; + static constexpr u32 xpp = 4; + static constexpr u32 ypp = 1; + static void Convert(PixelBuffer *pb, u8 *data) + { + u16 *p_in = (u16 *)data; + pb->prel(0, Unpacker::unpack(p_in[0])); + pb->prel(1, Unpacker::unpack(p_in[1])); + pb->prel(2, Unpacker::unpack(p_in[2])); + pb->prel(3, Unpacker::unpack(p_in[3])); + } +}; -#define pixelcvt_end } } -#define pixelcvt_next(name,x,y) pixelcvt_end; pixelcvt_start(name,x,y) -// -//Non twiddled -// -// 16-bit pixel buffer -pixelcvt_start(conv565_PL,4,1) +template +struct ConvertPlanarYUV { - //convert 4x1 - u16* p_in=(u16*)data; - //0,0 - pb->prel(0,ARGB565(p_in[0])); - //1,0 - pb->prel(1,ARGB565(p_in[1])); - //2,0 - pb->prel(2,ARGB565(p_in[2])); - //3,0 - pb->prel(3,ARGB565(p_in[3])); -} -pixelcvt_next(conv1555_PL,4,1) + using unpacked_type = u32; + static constexpr u32 xpp = 4; + static constexpr u32 ypp = 1; + static void Convert(PixelBuffer *pb, u8 *data) + { + //convert 4x1 4444 to 4x1 8888 + u32 *p_in = (u32 *)data; + + + s32 Y0 = (p_in[0] >> 8) & 255; // + s32 Yu = (p_in[0] >> 0) & 255; //p_in[0] + s32 Y1 = (p_in[0] >> 24) & 255; //p_in[3] + s32 Yv = (p_in[0] >> 16) & 255; //p_in[2] + + //0,0 + pb->prel(0, YUV422(Y0, Yu, Yv)); + //1,0 + pb->prel(1, YUV422(Y1, Yu, Yv)); + + //next 4 bytes + p_in += 1; + + Y0 = (p_in[0] >> 8) & 255; // + Yu = (p_in[0] >> 0) & 255; //p_in[0] + Y1 = (p_in[0] >> 24) & 255; //p_in[3] + Yv = (p_in[0] >> 16) & 255; //p_in[2] + + //0,0 + pb->prel(2, YUV422(Y0, Yu, Yv)); + //1,0 + pb->prel(3, YUV422(Y1, Yu, Yv)); + } +}; + +template +struct ConvertTwiddle { - //convert 4x1 - u16* p_in=(u16*)data; - //0,0 - pb->prel(0,ARGB1555(p_in[0])); - //1,0 - pb->prel(1,ARGB1555(p_in[1])); - //2,0 - pb->prel(2,ARGB1555(p_in[2])); - //3,0 - pb->prel(3,ARGB1555(p_in[3])); -} -pixelcvt_next(conv4444_PL,4,1) + using unpacked_type = typename Unpacker::unpacked_type; + static constexpr u32 xpp = 2; + static constexpr u32 ypp = 2; + static void Convert(PixelBuffer *pb, u8 *data) + { + u16 *p_in = (u16 *)data; + pb->prel(0, 0, Unpacker::unpack(p_in[0])); + pb->prel(0, 1, Unpacker::unpack(p_in[1])); + pb->prel(1, 0, Unpacker::unpack(p_in[2])); + pb->prel(1, 1, Unpacker::unpack(p_in[3])); + } +}; + +template +struct ConvertTwiddleYUV { - //convert 4x1 - u16* p_in=(u16*)data; - //0,0 - pb->prel(0,ARGB4444(p_in[0])); - //1,0 - pb->prel(1,ARGB4444(p_in[1])); - //2,0 - pb->prel(2,ARGB4444(p_in[2])); - //3,0 - pb->prel(3,ARGB4444(p_in[3])); -} -pixelcvt_end; + using unpacked_type = u32; + static constexpr u32 xpp = 2; + static constexpr u32 ypp = 2; + static void Convert(PixelBuffer *pb, u8 *data) + { + //convert 4x1 4444 to 4x1 8888 + u16* p_in = (u16 *)data; -// 32-bit pixel buffer -pixelcvt32_start(conv565_PL32,4,1) + s32 Y0 = (p_in[0] >> 8) & 255; // + s32 Yu = (p_in[0] >> 0) & 255; //p_in[0] + s32 Y1 = (p_in[2] >> 8) & 255; //p_in[3] + s32 Yv = (p_in[2] >> 0) & 255; //p_in[2] + + //0,0 + pb->prel(0, 0, YUV422(Y0, Yu, Yv)); + //1,0 + pb->prel(1, 0, YUV422(Y1, Yu, Yv)); + + //next 4 bytes + //p_in+=2; + + Y0 = (p_in[1] >> 8) & 255; // + Yu = (p_in[1] >> 0) & 255; //p_in[0] + Y1 = (p_in[3] >> 8) & 255; //p_in[3] + Yv = (p_in[3] >> 0) & 255; //p_in[2] + + //0,1 + pb->prel(0, 1, YUV422(Y0, Yu, Yv)); + //1,1 + pb->prel(1, 1, YUV422(Y1, Yu, Yv)); + } +}; + +template +struct UnpackerPalToRgb { + using unpacked_type = Pixel; + static Pixel unpack(u8 col) + { + u32 *pal = sizeof(Pixel) == 2 ? &palette16_ram[palette_index] : &palette32_ram[palette_index]; + return pal[col]; + } +}; + +template +struct ConvertTwiddlePal4 { - //convert 4x1 - u16* p_in=(u16*)data; - //0,0 - pb->prel(0,ARGB565_32(p_in[0])); - //1,0 - pb->prel(1,ARGB565_32(p_in[1])); - //2,0 - pb->prel(2,ARGB565_32(p_in[2])); - //3,0 - pb->prel(3,ARGB565_32(p_in[3])); -} -pixelcvt_end; -pixelcvt32_start(conv1555_PL32,4,1) + using unpacked_type = typename Unpacker::unpacked_type; + static constexpr u32 xpp = 4; + static constexpr u32 ypp = 4; + static void Convert(PixelBuffer *pb, u8 *data) + { + u8 *p_in = (u8 *)data; + + pb->prel(0, 0, Unpacker::unpack(p_in[0] & 0xF)); + pb->prel(0, 1, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; + pb->prel(1, 0, Unpacker::unpack(p_in[0] & 0xF)); + pb->prel(1, 1, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; + + pb->prel(0, 2, Unpacker::unpack(p_in[0] & 0xF)); + pb->prel(0, 3, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; + pb->prel(1, 2, Unpacker::unpack(p_in[0] & 0xF)); + pb->prel(1, 3, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; + + pb->prel(2, 0, Unpacker::unpack(p_in[0] & 0xF)); + pb->prel(2, 1, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; + pb->prel(3, 0, Unpacker::unpack(p_in[0] & 0xF)); + pb->prel(3, 1, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; + + pb->prel(2, 2, Unpacker::unpack(p_in[0] & 0xF)); + pb->prel(2, 3, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; + pb->prel(3, 2, Unpacker::unpack(p_in[0] & 0xF)); + pb->prel(3, 3, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; + } +}; + +template +struct ConvertTwiddlePal8 { - //convert 4x1 - u16* p_in=(u16*)data; - //0,0 - pb->prel(0,ARGB1555_32(p_in[0])); - //1,0 - pb->prel(1,ARGB1555_32(p_in[1])); - //2,0 - pb->prel(2,ARGB1555_32(p_in[2])); - //3,0 - pb->prel(3,ARGB1555_32(p_in[3])); -} -pixelcvt_end; -pixelcvt32_start(conv4444_PL32,4,1) -{ - //convert 4x1 - u16* p_in=(u16*)data; - //0,0 - pb->prel(0,ARGB4444_32(p_in[0])); - //1,0 - pb->prel(1,ARGB4444_32(p_in[1])); - //2,0 - pb->prel(2,ARGB4444_32(p_in[2])); - //3,0 - pb->prel(3,ARGB4444_32(p_in[3])); -} -pixelcvt_end; -pixelcvt32_start(convYUV_PL,4,1) -{ - //convert 4x1 4444 to 4x1 8888 - u32* p_in=(u32*)data; + using unpacked_type = typename Unpacker::unpacked_type; + static constexpr u32 xpp = 2; + static constexpr u32 ypp = 4; + static void Convert(PixelBuffer *pb, u8 *data) + { + u8* p_in = (u8 *)data; + pb->prel(0, 0, Unpacker::unpack(p_in[0])); p_in++; + pb->prel(0, 1, Unpacker::unpack(p_in[0])); p_in++; + pb->prel(1, 0, Unpacker::unpack(p_in[0])); p_in++; + pb->prel(1, 1, Unpacker::unpack(p_in[0])); p_in++; - s32 Y0 = (p_in[0]>>8) &255; // - s32 Yu = (p_in[0]>>0) &255; //p_in[0] - s32 Y1 = (p_in[0]>>24) &255; //p_in[3] - s32 Yv = (p_in[0]>>16) &255; //p_in[2] - - //0,0 - pb->prel(0,YUV422(Y0,Yu,Yv)); - //1,0 - pb->prel(1,YUV422(Y1,Yu,Yv)); - - //next 4 bytes - p_in+=1; - - Y0 = (p_in[0]>>8) &255; // - Yu = (p_in[0]>>0) &255; //p_in[0] - Y1 = (p_in[0]>>24) &255; //p_in[3] - Yv = (p_in[0]>>16) &255; //p_in[2] - - //0,0 - pb->prel(2,YUV422(Y0,Yu,Yv)); - //1,0 - pb->prel(3,YUV422(Y1,Yu,Yv)); -} -pixelcvt_end; - -// -//twiddled -// -// 16-bit pixel buffer -pixelcvt_start(conv565_TW,2,2) -{ - //convert 4x1 565 to 4x1 8888 - u16* p_in=(u16*)data; - //0,0 - pb->prel(0,0,ARGB565(p_in[0])); - //0,1 - pb->prel(0,1,ARGB565(p_in[1])); - //1,0 - pb->prel(1,0,ARGB565(p_in[2])); - //1,1 - pb->prel(1,1,ARGB565(p_in[3])); -} -pixelcvt_next(conv1555_TW,2,2) -{ - //convert 4x1 565 to 4x1 8888 - u16* p_in=(u16*)data; - //0,0 - pb->prel(0,0,ARGB1555(p_in[0])); - //0,1 - pb->prel(0,1,ARGB1555(p_in[1])); - //1,0 - pb->prel(1,0,ARGB1555(p_in[2])); - //1,1 - pb->prel(1,1,ARGB1555(p_in[3])); -} -pixelcvt_next(conv4444_TW,2,2) -{ - //convert 4x1 565 to 4x1 8888 - u16* p_in=(u16*)data; - //0,0 - pb->prel(0,0,ARGB4444(p_in[0])); - //0,1 - pb->prel(0,1,ARGB4444(p_in[1])); - //1,0 - pb->prel(1,0,ARGB4444(p_in[2])); - //1,1 - pb->prel(1,1,ARGB4444(p_in[3])); -} -pixelcvt_end; - -// 32-bit pixel buffer -pixelcvt32_start(conv565_TW32,2,2) -{ - //convert 4x1 565 to 4x1 8888 - u16* p_in=(u16*)data; - //0,0 - pb->prel(0,0,ARGB565_32(p_in[0])); - //0,1 - pb->prel(0,1,ARGB565_32(p_in[1])); - //1,0 - pb->prel(1,0,ARGB565_32(p_in[2])); - //1,1 - pb->prel(1,1,ARGB565_32(p_in[3])); -} -pixelcvt_end; -pixelcvt32_start(conv1555_TW32,2,2) -{ - //convert 4x1 565 to 4x1 8888 - u16* p_in=(u16*)data; - //0,0 - pb->prel(0,0,ARGB1555_32(p_in[0])); - //0,1 - pb->prel(0,1,ARGB1555_32(p_in[1])); - //1,0 - pb->prel(1,0,ARGB1555_32(p_in[2])); - //1,1 - pb->prel(1,1,ARGB1555_32(p_in[3])); -} -pixelcvt_end; -pixelcvt32_start(conv4444_TW32,2,2) -{ - //convert 4x1 565 to 4x1 8888 - u16* p_in=(u16*)data; - //0,0 - pb->prel(0,0,ARGB4444_32(p_in[0])); - //0,1 - pb->prel(0,1,ARGB4444_32(p_in[1])); - //1,0 - pb->prel(1,0,ARGB4444_32(p_in[2])); - //1,1 - pb->prel(1,1,ARGB4444_32(p_in[3])); -} -pixelcvt_end; - -pixelcvt32_start(convYUV_TW,2,2) -{ - //convert 4x1 4444 to 4x1 8888 - u16* p_in=(u16*)data; - - - s32 Y0 = (p_in[0]>>8) &255; // - s32 Yu = (p_in[0]>>0) &255; //p_in[0] - s32 Y1 = (p_in[2]>>8) &255; //p_in[3] - s32 Yv = (p_in[2]>>0) &255; //p_in[2] - - //0,0 - pb->prel(0,0,YUV422(Y0,Yu,Yv)); - //1,0 - pb->prel(1,0,YUV422(Y1,Yu,Yv)); - - //next 4 bytes - //p_in+=2; - - Y0 = (p_in[1]>>8) &255; // - Yu = (p_in[1]>>0) &255; //p_in[0] - Y1 = (p_in[3]>>8) &255; //p_in[3] - Yv = (p_in[3]>>0) &255; //p_in[2] - - //0,1 - pb->prel(0,1,YUV422(Y0,Yu,Yv)); - //1,1 - pb->prel(1,1,YUV422(Y1,Yu,Yv)); -} -pixelcvt_end; - -// 16-bit && 32-bit pixel buffers -pixelcvt_size_start(convPAL4_TW,4,4) -{ - u8* p_in=(u8*)data; - u32* pal= sizeof(pixel_size) == 2 ? &palette16_ram[palette_index] : &palette32_ram[palette_index]; - - pb->prel(0,0,pal[p_in[0]&0xF]); - pb->prel(0,1,pal[(p_in[0]>>4)&0xF]);p_in++; - pb->prel(1,0,pal[p_in[0]&0xF]); - pb->prel(1,1,pal[(p_in[0]>>4)&0xF]);p_in++; - - pb->prel(0,2,pal[p_in[0]&0xF]); - pb->prel(0,3,pal[(p_in[0]>>4)&0xF]);p_in++; - pb->prel(1,2,pal[p_in[0]&0xF]); - pb->prel(1,3,pal[(p_in[0]>>4)&0xF]);p_in++; - - pb->prel(2,0,pal[p_in[0]&0xF]); - pb->prel(2,1,pal[(p_in[0]>>4)&0xF]);p_in++; - pb->prel(3,0,pal[p_in[0]&0xF]); - pb->prel(3,1,pal[(p_in[0]>>4)&0xF]);p_in++; - - pb->prel(2,2,pal[p_in[0]&0xF]); - pb->prel(2,3,pal[(p_in[0]>>4)&0xF]);p_in++; - pb->prel(3,2,pal[p_in[0]&0xF]); - pb->prel(3,3,pal[(p_in[0]>>4)&0xF]);p_in++; -} -pixelcvt_end; - -// Palette 4bpp -> 8bpp -pixelcvt_size_start(convPAL4PT_TW, 4, 4) -{ - u8* p_in = (u8 *)data; - - pb->prel(0, 0, p_in[0] & 0xF); - pb->prel(0, 1, (p_in[0] >> 4) & 0xF); p_in++; - pb->prel(1, 0, p_in[0] & 0xF); - pb->prel(1, 1, (p_in[0] >> 4) & 0xF); p_in++; - - pb->prel(0, 2, p_in[0] & 0xF); - pb->prel(0, 3, (p_in[0] >> 4) & 0xF); p_in++; - pb->prel(1, 2, p_in[0] & 0xF); - pb->prel(1, 3, (p_in[0] >> 4) & 0xF); p_in++; - - pb->prel(2, 0, p_in[0] & 0xF); - pb->prel(2, 1, (p_in[0] >> 4) & 0xF); p_in++; - pb->prel(3, 0, p_in[0] & 0xF); - pb->prel(3, 1, (p_in[0] >> 4) & 0xF); p_in++; - - pb->prel(2, 2, p_in[0] & 0xF); - pb->prel(2, 3, (p_in[0] >> 4) & 0xF); p_in++; - pb->prel(3, 2, p_in[0] & 0xF); - pb->prel(3, 3, (p_in[0] >> 4) & 0xF); p_in++; -} -pixelcvt_end; - -pixelcvt_size_start(convPAL8_TW,2,4) -{ - u8* p_in=(u8*)data; - u32* pal= sizeof(pixel_size) == 2 ? &palette16_ram[palette_index] : &palette32_ram[palette_index]; - - pb->prel(0,0,pal[p_in[0]]);p_in++; - pb->prel(0,1,pal[p_in[0]]);p_in++; - pb->prel(1,0,pal[p_in[0]]);p_in++; - pb->prel(1,1,pal[p_in[0]]);p_in++; - - pb->prel(0,2,pal[p_in[0]]);p_in++; - pb->prel(0,3,pal[p_in[0]]);p_in++; - pb->prel(1,2,pal[p_in[0]]);p_in++; - pb->prel(1,3,pal[p_in[0]]);p_in++; -} -pixelcvt_end; - -// Palette 8bpp -> 8bpp (untwiddle only) -pixelcvt_size_start(convPAL8PT_TW, 2, 4) -{ - u8* p_in = (u8 *)data; - - pb->prel(0, 0, p_in[0]); p_in++; - pb->prel(0, 1, p_in[0]); p_in++; - pb->prel(1, 0, p_in[0]); p_in++; - pb->prel(1, 1, p_in[0]); p_in++; - - pb->prel(0, 2, p_in[0]); p_in++; - pb->prel(0, 3, p_in[0]); p_in++; - pb->prel(1, 2, p_in[0]); p_in++; - pb->prel(1, 3, p_in[0]); p_in++; -} -pixelcvt_end; - + pb->prel(0, 2, Unpacker::unpack(p_in[0])); p_in++; + pb->prel(0, 3, Unpacker::unpack(p_in[0])); p_in++; + pb->prel(1, 2, Unpacker::unpack(p_in[0])); p_in++; + pb->prel(1, 3, Unpacker::unpack(p_in[0])); p_in++; + } +}; //handler functions -template -void texture_PL(PixelBuffer* pb,u8* p_in,u32 Width,u32 Height) +template +void texture_PL(PixelBuffer* pb,u8* p_in,u32 Width,u32 Height) { pb->amove(0,0); @@ -541,8 +412,8 @@ void texture_PL(PixelBuffer* pb,u8* p_in,u32 Width,u32 Height) } } -template -void texture_TW(PixelBuffer* pb,u8* p_in,u32 Width,u32 Height) +template +void texture_TW(PixelBuffer* pb,u8* p_in,u32 Width,u32 Height) { pb->amove(0, 0); @@ -564,8 +435,8 @@ void texture_TW(PixelBuffer* pb,u8* p_in,u32 Width,u32 Height) } } -template -void texture_VQ(PixelBuffer* pb,u8* p_in,u32 Width,u32 Height) +template +void texture_VQ(PixelBuffer* pb,u8* p_in,u32 Width,u32 Height) { p_in += 256 * 4 * 2; // Skip VQ codebook pb->amove(0, 0); @@ -587,51 +458,98 @@ void texture_VQ(PixelBuffer* pb,u8* p_in,u32 Width,u32 Height) } } +typedef void (*TexConvFP)(PixelBuffer *pb, u8 *p_in, u32 width, u32 height); +typedef void (*TexConvFP8)(PixelBuffer *pb, u8 *p_in, u32 width, u32 height); +typedef void (*TexConvFP32)(PixelBuffer *pb, u8 *p_in, u32 width, u32 height); + //Planar -#define tex565_PL texture_PL -#define tex1555_PL texture_PL -#define tex4444_PL texture_PL -#define texYUV422_PL texture_PL -#define texBMP_PL tex4444_PL - -#define tex565_PL32 texture_PL -#define tex1555_PL32 texture_PL -#define tex4444_PL32 texture_PL - +constexpr TexConvFP tex565_PL = texture_PL>>; //Twiddle -#define tex565_TW texture_TW -#define tex1555_TW texture_TW -#define tex4444_TW texture_TW -#define texYUV422_TW texture_TW -#define texBMP_TW tex4444_TW -#define texPAL4_TW texture_TW, u16> -#define texPAL8_TW texture_TW, u16> -#define texPAL4_TW32 texture_TW, u32> -#define texPAL8_TW32 texture_TW, u32> -#define texPAL4PT_TW texture_TW, u8> -#define texPAL8PT_TW texture_TW, u8> - -#define tex565_TW32 texture_TW -#define tex1555_TW32 texture_TW -#define tex4444_TW32 texture_TW - +constexpr TexConvFP tex565_TW = texture_TW>>; +// Palette +constexpr TexConvFP texPAL4_TW = texture_TW>>; +constexpr TexConvFP texPAL8_TW = texture_TW>>; +constexpr TexConvFP32 texPAL4_TW32 = texture_TW>>; +constexpr TexConvFP32 texPAL8_TW32 = texture_TW>>; +constexpr TexConvFP8 texPAL4PT_TW = texture_TW>>; +constexpr TexConvFP8 texPAL8PT_TW = texture_TW>>; //VQ -#define tex565_VQ texture_VQ -#define tex1555_VQ texture_VQ -#define tex4444_VQ texture_VQ -#define texYUV422_VQ texture_VQ -#define texBMP_VQ tex4444_VQ +constexpr TexConvFP tex565_VQ = texture_VQ>>; // According to the documentation, a texture cannot be compressed and use // a palette at the same time. However the hardware displays them // just fine. -#define texPAL4_VQ texture_VQ, u16> -#define texPAL8_VQ texture_VQ, u16> +constexpr TexConvFP texPAL4_VQ = texture_VQ>>; +constexpr TexConvFP texPAL8_VQ = texture_VQ>>; +constexpr TexConvFP32 texPAL4_VQ32 = texture_VQ>>; +constexpr TexConvFP32 texPAL8_VQ32 = texture_VQ>>; -#define tex565_VQ32 texture_VQ -#define tex1555_VQ32 texture_VQ -#define tex4444_VQ32 texture_VQ -#define texPAL4_VQ32 texture_VQ, u32> -#define texPAL8_VQ32 texture_VQ, u32> +namespace opengl { +// Open GL + +//Planar +constexpr TexConvFP tex1555_PL = texture_PL>; +constexpr TexConvFP tex4444_PL = texture_PL>; +constexpr TexConvFP texBMP_PL = tex4444_PL; +constexpr TexConvFP32 texYUV422_PL = texture_PL>; + +constexpr TexConvFP32 tex565_PL32 = texture_PL>>; +constexpr TexConvFP32 tex1555_PL32 = texture_PL>>; +constexpr TexConvFP32 tex4444_PL32 = texture_PL>>; + +//Twiddle +constexpr TexConvFP tex1555_TW = texture_TW>; +constexpr TexConvFP tex4444_TW = texture_TW>; +constexpr TexConvFP texBMP_TW = tex4444_TW; +constexpr TexConvFP32 texYUV422_TW = texture_TW>; + +constexpr TexConvFP32 tex565_TW32 = texture_TW>>; +constexpr TexConvFP32 tex1555_TW32 = texture_TW>>; +constexpr TexConvFP32 tex4444_TW32 = texture_TW>>; + +//VQ +constexpr TexConvFP tex1555_VQ = texture_VQ>; +constexpr TexConvFP tex4444_VQ = texture_VQ>; +constexpr TexConvFP texBMP_VQ = tex4444_VQ; +constexpr TexConvFP32 texYUV422_VQ = texture_VQ>; + +constexpr TexConvFP32 tex565_VQ32 = texture_VQ>>; +constexpr TexConvFP32 tex1555_VQ32 = texture_VQ>>; +constexpr TexConvFP32 tex4444_VQ32 = texture_VQ>>; +} + +namespace directx { +// DirectX + +//Planar +constexpr TexConvFP tex1555_PL = texture_PL>>; +constexpr TexConvFP tex4444_PL = texture_PL>>; +constexpr TexConvFP texBMP_PL = tex4444_PL; +constexpr TexConvFP32 texYUV422_PL = texture_PL>; + +constexpr TexConvFP32 tex565_PL32 = texture_PL>>; +constexpr TexConvFP32 tex1555_PL32 = texture_PL>>; +constexpr TexConvFP32 tex4444_PL32 = texture_PL>>; + +//Twiddle +constexpr TexConvFP tex1555_TW = texture_TW>>; +constexpr TexConvFP tex4444_TW = texture_TW>>; +constexpr TexConvFP texBMP_TW = tex4444_TW; +constexpr TexConvFP32 texYUV422_TW = texture_TW>; + +constexpr TexConvFP32 tex565_TW32 = texture_TW>>; +constexpr TexConvFP32 tex1555_TW32 = texture_TW>>; +constexpr TexConvFP32 tex4444_TW32 = texture_TW>>; + +//VQ +constexpr TexConvFP tex1555_VQ = texture_VQ>>; +constexpr TexConvFP tex4444_VQ = texture_VQ>>; +constexpr TexConvFP texBMP_VQ = tex4444_VQ; +constexpr TexConvFP32 texYUV422_VQ = texture_VQ>; + +constexpr TexConvFP32 tex565_VQ32 = texture_VQ>>; +constexpr TexConvFP32 tex1555_VQ32 = texture_VQ>>; +constexpr TexConvFP32 tex4444_VQ32 = texture_VQ>>; +} struct vram_block { @@ -646,52 +564,46 @@ struct vram_block class BaseTextureCacheData; bool VramLockedWriteOffset(size_t offset); -void libCore_vramlock_Unlock_block(vram_block *block); void libCore_vramlock_Lock(u32 start_offset, u32 end_offset, BaseTextureCacheData *texture); void UpscalexBRZ(int factor, u32* source, u32* dest, int width, int height, bool has_alpha); struct PvrTexInfo; -template class PixelBuffer; -typedef void TexConvFP(PixelBuffer* pb,u8* p_in,u32 Width,u32 Height); -typedef void TexConvFP8(PixelBuffer* pb, u8* p_in, u32 Width, u32 Height); -typedef void TexConvFP32(PixelBuffer* pb,u8* p_in,u32 Width,u32 Height); enum class TextureType { _565, _5551, _4444, _8888, _8 }; class BaseTextureCacheData { public: - TSP tsp; //dreamcast texture parameters + TSP tsp; //dreamcast texture parameters TCW tcw; // Decoded/filtered texture format TextureType tex_type; + u32 sa_tex; // texture data start address in vram - u32 sa; //pixel data start address in vram (might be offset for mipmaps/etc) - u32 sa_tex; //texture data start address in vram - u32 w,h; //width & height of the texture - u32 size; //size, in bytes, in vram + u32 dirty; // frame number at which texture was overwritten + vram_block* lock_block; + + u32 sa; // pixel data start address of max level mipmap + u16 width, height; // width & height of the texture + u32 size; // size in bytes of max level mipmap in vram const PvrTexInfo* tex; - TexConvFP *texconv; - TexConvFP32 *texconv32; - TexConvFP8 *texconv8; - - u32 dirty; - vram_block* lock_block; + TexConvFP texconv; + TexConvFP32 texconv32; + TexConvFP8 texconv8; u32 Updates; - u32 palette_index; //used for palette updates u32 palette_hash; // Palette hash at time of last update - u32 vq_codebook; // VQ quantizers table for compressed textures u32 texture_hash; // xxhash of texture data, used for custom textures u32 old_texture_hash; // legacy hash u8* custom_image_data; // loaded custom image data u32 custom_width; u32 custom_height; std::atomic_int custom_load_in_progress; + bool gpuPalette; void PrintTextureName(); virtual std::string GetId() = 0; @@ -748,6 +660,7 @@ public: && !tcw.MipMapped && !tcw.VQ_Comp; } + static void SetDirectXColorOrder(bool enabled); }; template @@ -829,7 +742,9 @@ protected: const TCW TCWTextureCacheMask = { { 0x1FFFFF, 0, 0, 1, 7, 1, 1 } }; }; +template void ReadFramebuffer(PixelBuffer& pb, int& width, int& height); +template void WriteTextureToVRam(u32 width, u32 height, u8 *data, u16 *dst, u32 fb_w_ctrl = -1, u32 linestride = -1); static inline void MakeFogTexture(u8 *tex_data) diff --git a/core/rend/dx9/comptr.h b/core/rend/dx9/comptr.h new file mode 100644 index 000000000..aa59262e6 --- /dev/null +++ b/core/rend/dx9/comptr.h @@ -0,0 +1,71 @@ +/* + Copyright 2021 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#pragma once +#include + +template +class ComPtr +{ +public: + ComPtr() = default; + ComPtr(const ComPtr& other) : ptr(other.ptr) { + if (ptr != nullptr) + ptr->AddRef(); + } + ComPtr(ComPtr&& other) noexcept { + std::swap(ptr, other.ptr); + } + ~ComPtr() { + if (ptr != nullptr) + ptr->Release(); + } + + ComPtr& operator=(const ComPtr& other) { + if (this != &other) + *this = ComPtr(other); + return *this; + } + ComPtr& operator=(ComPtr&& other) noexcept { + std::swap(ptr, other.ptr); + return *this; + } + + T* operator->() const noexcept { + return ptr; + } + explicit operator bool() const noexcept { + return ptr != nullptr; + } + operator T*() const noexcept { + return ptr; + } + T*& get() noexcept { + return ptr; + } + void reset(T *ptr = nullptr) { + if (ptr == this->ptr) + return; + std::swap(this->ptr, ptr); + if (ptr != nullptr) + ptr->Release(); + } + +private: + T *ptr = nullptr; +}; diff --git a/core/rend/dx9/d3d_overlay.cpp b/core/rend/dx9/d3d_overlay.cpp new file mode 100644 index 000000000..71a912b54 --- /dev/null +++ b/core/rend/dx9/d3d_overlay.cpp @@ -0,0 +1,162 @@ +/* + Copyright 2021 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#include "d3d_overlay.h" +#include "rend/gui.h" +#include +#include + +void D3DOverlay::drawQuad(const RECT& rect, D3DCOLOR color) +{ + device->SetTextureStageState(0, D3DTSS_CONSTANT, color); + Vertex quad[] { + { (float)(rect.left), (float)(rect.top), 0.5f, 0.f, 0.f }, + { (float)(rect.left), (float)(rect.bottom), 0.5f, 0.f, 1.f }, + { (float)(rect.right), (float)(rect.top), 0.5f, 1.f, 0.f }, + { (float)(rect.right), (float)(rect.bottom), 0.5f, 1.f, 1.f } + }; + device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, quad, sizeof(Vertex)); +} + +void D3DOverlay::draw(u32 width, u32 height, bool vmu, bool crosshair) +{ + setupRenderState(width, height); + if (vmu) + { + float vmu_padding = 8.f * scaling; + float vmu_height = 70.f * scaling; + float vmu_width = 48.f / 32.f * vmu_height; + + for (size_t i = 0; i < vmuTextures.size(); i++) + { + ComPtr < IDirect3DTexture9 > &texture = vmuTextures[i]; + if (!vmu_lcd_status[i]) + { + texture.reset(); + continue; + } + if (texture == nullptr || vmu_lcd_changed[i]) + { + device->CreateTexture(48, 32, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture.get(), 0); + D3DLOCKED_RECT rect; + if (SUCCEEDED(texture->LockRect(0, &rect, nullptr, 0))) + { + u8 *dst = (u8 *) rect.pBits; + for (int y = 0; y < 32; y++) + memcpy(dst + y * rect.Pitch, vmu_lcd_data[i] + (31 - y) * 48, 48 * 4); + texture->UnlockRect(0); + } + vmu_lcd_changed[i] = false; + } + float x; + if (i & 2) + x = width - vmu_padding - vmu_width; + else + x = vmu_padding; + float y; + if (i & 4) + { + y = height - vmu_padding - vmu_height; + if (i & 1) + y -= vmu_padding + vmu_height; + } + else + { + y = vmu_padding; + if (i & 1) + y += vmu_padding + vmu_height; + } + device->SetTexture(0, texture); + RECT rect { (long)x, (long)y, (long)(x + vmu_width), (long)(y + vmu_height) }; + drawQuad(rect, D3DCOLOR_ARGB(192, 255, 255, 255)); + } + } + if (crosshair) + { + if (!xhairTexture) + { + const u32* texData = getCrosshairTextureData(); + device->CreateTexture(16, 16, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &xhairTexture.get(), 0); + D3DLOCKED_RECT rect; + if (SUCCEEDED(xhairTexture->LockRect(0, &rect, nullptr, 0))) + { + if (rect.Pitch == 16 * sizeof(u32)) + memcpy(rect.pBits, texData, 16 * 16 * sizeof(u32)); + else + { + u8 *dst = (u8 *) rect.pBits; + for (int y = 0; y < 16; y++) + memcpy(dst + y * rect.Pitch, texData + y * 16, 16 * sizeof(u32)); + } + xhairTexture->UnlockRect(0); + } + } + device->SetTexture(0, xhairTexture); + for (u32 i = 0; i < config::CrosshairColor.size(); i++) + { + if (config::CrosshairColor[i] == 0) + continue; + if (settings.platform.system == DC_PLATFORM_DREAMCAST + && config::MapleMainDevices[i] != MDT_LightGun) + continue; + + float x, y; + std::tie(x, y) = getCrosshairPosition(i); + float halfWidth = XHAIR_WIDTH / 2.f; + RECT rect { (long) (x - halfWidth), (long) (y - halfWidth), (long) (x + halfWidth), (long) (y + halfWidth) }; + D3DCOLOR color = (config::CrosshairColor[i] & 0xFF00FF00) + | ((config::CrosshairColor[i] >> 16) & 0xFF) + | ((config::CrosshairColor[i] & 0xFF) << 16); + drawQuad(rect, color); + } + } +} + +void D3DOverlay::setupRenderState(u32 displayWidth, u32 displayHeight) +{ + device->SetPixelShader(NULL); + device->SetVertexShader(NULL); + device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + device->SetRenderState(D3DRS_ZENABLE, FALSE); + device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); + device->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); + device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + device->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, FALSE); + device->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); + device->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); + device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + device->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CONSTANT); + device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_CONSTANT); + device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); + device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); + + glm::mat4 identity = glm::identity(); + glm::mat4 projection = glm::translate(glm::vec3(-1.f - 1.f / displayWidth, 1.f + 1.f / displayHeight, 0)) + * glm::scale(glm::vec3(2.f / displayWidth, -2.f / displayHeight, 1.f)); + + device->SetTransform(D3DTS_WORLD, (const D3DMATRIX *)&identity[0][0]); + device->SetTransform(D3DTS_VIEW, (const D3DMATRIX *)&identity[0][0]); + device->SetTransform(D3DTS_PROJECTION, (const D3DMATRIX *)&projection[0][0]); + + device->SetFVF(D3DFVF_XYZ | D3DFVF_TEX1); +} diff --git a/core/rend/dx9/d3d_overlay.h b/core/rend/dx9/d3d_overlay.h new file mode 100644 index 000000000..5f4a6323c --- /dev/null +++ b/core/rend/dx9/d3d_overlay.h @@ -0,0 +1,55 @@ +/* + Copyright 2021 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#pragma once +#include "types.h" +#include +#include +#include +#include "comptr.h" + +class D3DOverlay +{ +public: + void init(const ComPtr& device) { + this->device = device; + } + + void term() { + device.reset(); + xhairTexture.reset(); + for (auto& vmu : vmuTextures) + vmu.reset(); + } + + void draw(u32 width, u32 height, bool vmu, bool crosshair); + +private: + void setupRenderState(u32 displayWidth, u32 displayHeight); + void drawQuad(const RECT& rect, D3DCOLOR color); + + struct Vertex + { + float pos[3]; + float uv[2]; + }; + + ComPtr device; + ComPtr xhairTexture; + std::array, 8> vmuTextures; +}; diff --git a/core/rend/dx9/d3d_renderer.cpp b/core/rend/dx9/d3d_renderer.cpp new file mode 100644 index 000000000..c6ec03fdf --- /dev/null +++ b/core/rend/dx9/d3d_renderer.cpp @@ -0,0 +1,1287 @@ +/* + Copyright 2021 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#include "d3d_renderer.h" +#include "hw/pvr/ta.h" +#include "hw/pvr/pvr_mem.h" +#include "rend/tileclip.h" +#include "rend/gui.h" + +#define verifyWin(x) verify(SUCCEEDED(x)) + +const u32 DstBlendGL[] +{ + D3DBLEND_ZERO, + D3DBLEND_ONE, + D3DBLEND_SRCCOLOR, + D3DBLEND_INVSRCCOLOR, + D3DBLEND_SRCALPHA, + D3DBLEND_INVSRCALPHA, + D3DBLEND_DESTALPHA, + D3DBLEND_INVDESTALPHA +}; + +const u32 SrcBlendGL[] +{ + D3DBLEND_ZERO, + D3DBLEND_ONE, + D3DBLEND_DESTCOLOR, + D3DBLEND_INVDESTCOLOR, + D3DBLEND_SRCALPHA, + D3DBLEND_INVSRCALPHA, + D3DBLEND_DESTALPHA, + D3DBLEND_INVDESTALPHA +}; + +const u32 CullMode[] +{ + D3DCULL_NONE, //0 No culling no culling + D3DCULL_NONE, //1 Cull if Small Cull if ( |det| < fpu_cull_val ) + + D3DCULL_CCW, //2 Cull if Negative Cull if ( |det| < 0 ) or + //( |det| < fpu_cull_val ) + D3DCULL_CW, //3 Cull if Positive Cull if ( |det| > 0 ) or + //( |det| < fpu_cull_val ) +}; + +const u32 Zfunction[] +{ + D3DCMP_NEVER, //0 Never + D3DCMP_LESS, //1 Less + D3DCMP_EQUAL, //2 Equal + D3DCMP_LESSEQUAL, //3 Less Or Equal + D3DCMP_GREATER, //4 Greater + D3DCMP_NOTEQUAL, //5 Not Equal + D3DCMP_GREATEREQUAL, //6 Greater Or Equal + D3DCMP_ALWAYS, //7 Always +}; + +const D3DVERTEXELEMENT9 MainVtxElement[] +{ + { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, + { 0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 }, //Base color + { 0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 1 }, //Specular color + { 0, 20, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 }, //u,v + D3DDECL_END() +}; +const D3DVERTEXELEMENT9 ModVolVtxElement[] +{ + { 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, + D3DDECL_END() +}; + +bool D3DRenderer::ensureVertexBufferSize(ComPtr& buffer, u32& currentSize, u32 minSize) +{ + if (minSize <= currentSize && buffer) + return true; + if (currentSize == 0) + currentSize = minSize; + else + while (currentSize < minSize) + currentSize *= 2; + buffer.reset(); + return SUCCEEDED(device->CreateVertexBuffer(currentSize, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &buffer.get(), 0)); +} + +bool D3DRenderer::ensureIndexBufferSize(ComPtr& buffer, u32& currentSize, u32 minSize) +{ + if (minSize <= currentSize && buffer) + return true; + if (currentSize == 0) + currentSize = minSize; + else + while (currentSize < minSize) + currentSize *= 2; + buffer.reset(); + return SUCCEEDED(device->CreateIndexBuffer(currentSize, D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFMT_INDEX32, D3DPOOL_DEFAULT, &buffer.get(), 0)); +} + +bool D3DRenderer::Init() +{ + ComPtr d3d9 = theDXContext.getD3D(); + D3DCAPS9 caps; + d3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps); + if (caps.VertexShaderVersion < D3DVS_VERSION(1, 0)) + { + WARN_LOG(RENDERER, "Vertex shader version %x", caps.VertexShaderVersion); + return false; + } + if (caps.PixelShaderVersion < D3DPS_VERSION(2, 0)) + { + WARN_LOG(RENDERER, "Pixel shader version %x", caps.PixelShaderVersion); + return false; + } + + device = theDXContext.getDevice(); + devCache.setDevice(device); + + bool success = ensureVertexBufferSize(vertexBuffer, vertexBufferSize, 4 * 1024 * 1024); + success &= ensureIndexBufferSize(indexBuffer, indexBufferSize, 120 * 1024 * 4); + + success &= SUCCEEDED(device->CreateVertexDeclaration(MainVtxElement, &mainVtxDecl.get())); + success &= SUCCEEDED(device->CreateVertexDeclaration(ModVolVtxElement, &modVolVtxDecl.get())); + + shaders.init(device); + success &= (bool)shaders.getVertexShader(true); + success &= SUCCEEDED(device->CreateTexture(32, 32, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &paletteTexture.get(), 0)); + success &= SUCCEEDED(device->CreateTexture(128, 2, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8, D3DPOOL_DEFAULT, &fogTexture.get(), 0)); + fog_needs_update = true; + palette_updated = true; + + if (!success) + { + WARN_LOG(RENDERER, "DirectX9 renderer initialization failed"); + Term(); + } + + return success; +} + +void D3DRenderer::preReset() +{ + texCache.Clear(); + backbuffer.reset(); + depthSurface.reset(); + rttSurface.reset(); + rttTexture.reset(); + dcfbSurface.reset(); + dcfbTexture.reset(); + fogTexture.reset(); + paletteTexture.reset(); + modVolVtxDecl.reset(); + mainVtxDecl.reset(); + modvolBuffer.reset(); + modvolBufferSize = 0; + sortedTriIndexBuffer.reset(); + sortedTriIndexBufferSize = 0; + indexBuffer.reset(); + indexBufferSize = 0; + vertexBuffer.reset(); + vertexBufferSize = 0; + framebufferSurface.reset(); + framebufferTexture.reset(); + resetting = true; +} + +void D3DRenderer::postReset() +{ + resetting = false; + devCache.reset(); + u32 w = width; // FIXME + u32 h = height; + width = 0; + height = 0; + Resize(w, h); + verify(ensureVertexBufferSize(vertexBuffer, vertexBufferSize, 4 * 1024 * 1024)); + verify(ensureIndexBufferSize(indexBuffer, indexBufferSize, 120 * 1024 * 4)); + verifyWin(device->CreateVertexDeclaration(MainVtxElement, &mainVtxDecl.get())); + verifyWin(device->CreateVertexDeclaration(ModVolVtxElement, &modVolVtxDecl.get())); + verifyWin(device->CreateTexture(32, 32, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &paletteTexture.get(), 0)); + verifyWin(device->CreateTexture(128, 2, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8, D3DPOOL_DEFAULT, &fogTexture.get(), 0)); + fog_needs_update = true; + palette_updated = true; + frameRendered = false; +} + +void D3DRenderer::Term() +{ + preReset(); + resetting = false; + shaders.term(); + device.reset(); +} + +BaseTextureCacheData *D3DRenderer::GetTexture(TSP tsp, TCW tcw) +{ + if (resetting) + return nullptr; + //lookup texture + D3DTexture* tf = texCache.getTextureCacheData(tsp, tcw); + + if (tf->texture == nullptr) + tf->Create(); + + //update if needed + if (tf->NeedsUpdate()) + tf->Update(); + else + { + if (tf->IsCustomTextureAvailable()) + { + texCache.DeleteLater(tf->texture); + tf->texture.reset(); + tf->loadCustomTexture(); + } + } + return tf; +} + +void D3DRenderer::readDCFramebuffer() +{ + if (FB_R_SIZE.fb_x_size == 0 || FB_R_SIZE.fb_y_size == 0) + return; + + PixelBuffer pb; + int width; + int height; + ReadFramebuffer(pb, width, height); + + if (!dcfbTexture) + { + // FIXME dimension can change + device->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &dcfbTexture.get(), 0); + dcfbTexture->GetSurfaceLevel(0, &dcfbSurface.get()); + } + + D3DLOCKED_RECT rect; + dcfbTexture->LockRect(0, &rect, nullptr, 0); + if ((u32)rect.Pitch == width * sizeof(u32)) + memcpy(rect.pBits, pb.data(), width * height * sizeof(u32)); + else + { + u8 *dst = (u8 *)rect.pBits; + for (int y = 0; y < height; y++) + memcpy(dst + y * rect.Pitch, pb.data() + y * width, width * sizeof(u32)); + } + dcfbTexture->UnlockRect(0); +} + +void D3DRenderer::renderDCFramebuffer() +{ + device->ColorFill(framebufferSurface, 0, D3DCOLOR_ARGB(255, VO_BORDER_COL.Red, VO_BORDER_COL.Green, VO_BORDER_COL.Blue)); + u32 bar = (width - height * 640 / 480) / 2; + RECT rd{ (LONG)bar, 0, (LONG)(width - bar), (LONG)height }; + device->StretchRect(dcfbSurface, nullptr, framebufferSurface, &rd, D3DTEXF_LINEAR); +} + +bool D3DRenderer::Process(TA_context* ctx) +{ + if (resetting) + return false; + + if (KillTex) + texCache.Clear(); + texCache.Cleanup(); + + if (ctx->rend.isRenderFramebuffer) + { + readDCFramebuffer(); + } + else + { + if (!ta_parse_vdrc(ctx, true)) + return false; + } + + return true; +} + +inline void D3DRenderer::setTexMode(D3DSAMPLERSTATETYPE state, u32 clamp, u32 mirror) +{ + if (clamp) + devCache.SetSamplerState(0, state, D3DTADDRESS_CLAMP); + else + { + if (mirror) + devCache.SetSamplerState(0, state, D3DTADDRESS_MIRROR); + else + devCache.SetSamplerState(0, state, D3DTADDRESS_WRAP); + } +} + +template +void D3DRenderer::setGPState(const PolyParam *gp) +{ + float trilinear_alpha; + if (gp->pcw.Texture && gp->tsp.FilterMode > 1 && Type != ListType_Punch_Through && gp->tcw.MipMapped == 1) + { + trilinear_alpha = 0.25f * (gp->tsp.MipMapD & 0x3); + if (gp->tsp.FilterMode == 2) + // Trilinear pass A + trilinear_alpha = 1.f - trilinear_alpha; + } + else + trilinear_alpha = 1.f; + + bool color_clamp = gp->tsp.ColorClamp && (pvrrc.fog_clamp_min != 0 || pvrrc.fog_clamp_max != 0xffffffff); + int fog_ctrl = config::Fog ? gp->tsp.FogCtrl : 2; + + int clip_rect[4] = {}; + TileClipping clipmode = GetTileClip(gp->tileclip, matrices.GetViewportMatrix(), clip_rect); + D3DTexture *texture = (D3DTexture *)gp->texture; + bool gpuPalette = texture != nullptr ? texture->gpuPalette : false; + + devCache.SetPixelShader(shaders.getShader( + gp->pcw.Texture, + gp->tsp.UseAlpha, + gp->tsp.IgnoreTexA, + gp->tsp.ShadInstr, + gp->pcw.Offset, + fog_ctrl, + gp->tcw.PixelFmt == PixelBumpMap, + color_clamp, + trilinear_alpha != 1.f, + gpuPalette, + gp->pcw.Gouraud)); + + if (trilinear_alpha != 1.f) + { + float f[4] { trilinear_alpha, 0, 0, 0 }; + device->SetPixelShaderConstantF(5, f, 1); + } + if (gpuPalette) + { + float paletteIndex[4]; + if (gp->tcw.PixelFmt == PixelPal4) + paletteIndex[0] = (float)(gp->tcw.PalSelect << 4); + else + paletteIndex[0] = (float)((gp->tcw.PalSelect >> 4) << 8); + device->SetPixelShaderConstantF(0, paletteIndex, 1); + } + devCache.SetVertexShader(shaders.getVertexShader(gp->pcw.Gouraud)); + devCache.SetRenderState(D3DRS_SHADEMODE, gp->pcw.Gouraud == 1 ? D3DSHADE_GOURAUD : D3DSHADE_FLAT); + + /* TODO + if (clipmode == TileClipping::Inside) + { + float f[] = { clip_rect[0], clip_rect[1], clip_rect[0] + clip_rect[2], clip_rect[1] + clip_rect[3] }; + device->SetPixelShaderConstantF(n, f, 1); + } + else */ + if (clipmode == TileClipping::Outside) + { + devCache.SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); + RECT rect { clip_rect[0], clip_rect[1], clip_rect[0] + clip_rect[2], clip_rect[1] + clip_rect[3] }; + // TODO cache + device->SetScissorRect(&rect); + } + else + { + devCache.SetRenderState(D3DRS_SCISSORTESTENABLE, scissorEnable); + if (scissorEnable) + device->SetScissorRect(&scissorRect); + } + + const u32 stencil = (gp->pcw.Shadow != 0) ? 0x80 : 0; + if (config::ModifierVolumes) + devCache.SetRenderState(D3DRS_STENCILREF, stencil); + + if (texture != nullptr) + { + devCache.SetTexture(0, texture->texture); + setTexMode(D3DSAMP_ADDRESSU, gp->tsp.ClampU, gp->tsp.FlipU); + setTexMode(D3DSAMP_ADDRESSV, gp->tsp.ClampV, gp->tsp.FlipV); + + //set texture filter mode + if (gp->tsp.FilterMode == 0 || gpuPalette) + { + //disable filtering, mipmaps + devCache.SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); + devCache.SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); + devCache.SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT); + } + else + { + //bilinear filtering + devCache.SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + devCache.SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + devCache.SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); // LINEAR for Trilinear filtering + } + } + + // Apparently punch-through polys support blending, or at least some combinations + if (Type == ListType_Translucent || Type == ListType_Punch_Through) + { + devCache.SetRenderState(D3DRS_SRCBLEND, SrcBlendGL[gp->tsp.SrcInstr]); + devCache.SetRenderState(D3DRS_DESTBLEND, DstBlendGL[gp->tsp.DstInstr]); + } + + devCache.SetRenderState(D3DRS_CULLMODE, CullMode[gp->isp.CullMode]); + + //set Z mode, only if required + if (Type == ListType_Punch_Through || (Type == ListType_Translucent && SortingEnabled)) + { + devCache.SetRenderState(D3DRS_ZFUNC, Zfunction[6]); // GEQ + } + else + { + devCache.SetRenderState(D3DRS_ZFUNC, Zfunction[gp->isp.DepthMode]); + } + + if (SortingEnabled && !config::PerStripSorting) + devCache.SetRenderState(D3DRS_ZWRITEENABLE, FALSE); + else + { + // Z Write Disable seems to be ignored for punch-through. + // Fixes Worms World Party, Bust-a-Move 4 and Re-Volt + if (Type == ListType_Punch_Through) + devCache.SetRenderState(D3DRS_ZWRITEENABLE, TRUE); + else + devCache.SetRenderState(D3DRS_ZWRITEENABLE, !gp->isp.ZWriteDis); + } +} + +template +void D3DRenderer::drawList(const List& gply, int first, int count) +{ + PolyParam* params = &gply.head()[first]; + + while (count-- > 0) + { + if (params->count > 2) + { + if ((Type == ListType_Opaque || (Type == ListType_Translucent && !SortingEnabled)) && params->isp.DepthMode == 0) + { + // depthFunc = never + params++; + continue; + } + setGPState(params); + device->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, params->count, params->first, params->count - 2); + } + + params++; + } +} + +void D3DRenderer::sortTriangles(int first, int count) +{ + std::vector vidx_sort; + GenSorted(first, count, pidx_sort, vidx_sort); + + //Upload to GPU if needed + if (pidx_sort.empty()) + return; + + const u32 bufSize = vidx_sort.size() * sizeof(u32); + // Upload sorted index buffer + ensureIndexBufferSize(sortedTriIndexBuffer, sortedTriIndexBufferSize, bufSize); + void *ptr; + sortedTriIndexBuffer->Lock(0, bufSize, &ptr, D3DLOCK_DISCARD); + memcpy(ptr, &vidx_sort[0], bufSize); + sortedTriIndexBuffer->Unlock(); + device->SetIndices(sortedTriIndexBuffer); +} + +void D3DRenderer::drawSorted(bool multipass) +{ + if (pidx_sort.empty()) + return; + + u32 count = pidx_sort.size(); + + for (u32 p = 0; p < count; p++) + { + const PolyParam* params = pidx_sort[p].ppid; + if (pidx_sort[p].count > 2) + { + setGPState(params); + device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, pidx_sort[p].count, pidx_sort[p].first, pidx_sort[p].count / 3); + } + } + if (multipass && config::TranslucentPolygonDepthMask) + { + // Write to the depth buffer now. The next render pass might need it. (Cosmic Smash) + devCache.SetRenderState(D3DRS_COLORWRITEENABLE, 0); + devCache.SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + + // We use the modifier volumes shader because it's fast. We don't need textures, etc. + devCache.SetPixelShader(shaders.getModVolShader()); + + devCache.SetRenderState(D3DRS_ZFUNC, D3DCMP_GREATEREQUAL); + devCache.SetRenderState(D3DRS_ZWRITEENABLE, TRUE); + devCache.SetRenderState(D3DRS_SCISSORTESTENABLE, scissorEnable); + if (scissorEnable) + device->SetScissorRect(&scissorRect); + + for (u32 p = 0; p < count; p++) + { + const PolyParam* params = pidx_sort[p].ppid; + if (pidx_sort[p].count > 2 && !params->isp.ZWriteDis) { + // FIXME no clipping in modvol shader + //SetTileClip(gp->tileclip,true); + + devCache.SetRenderState(D3DRS_CULLMODE, CullMode[params->isp.CullMode]); + device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, pidx_sort[p].count, pidx_sort[p].first, pidx_sort[p].count / 3); + } + } + devCache.SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE); + } + device->SetIndices(indexBuffer); +} + +//All pixels are in area 0 by default. +//If inside an 'in' volume, they are in area 1 +//if inside an 'out' volume, they are in area 0 +/* + Stencil bits: + bit 7: mv affected (must be preserved) + bit 1: current volume state + but 0: summary result (starts off as 0) + + Lower 2 bits: + + IN volume (logical OR): + 00 -> 00 + 01 -> 01 + 10 -> 01 + 11 -> 01 + + Out volume (logical AND): + 00 -> 00 + 01 -> 00 + 10 -> 00 + 11 -> 01 +*/ +void D3DRenderer::setMVS_Mode(ModifierVolumeMode mv_mode, ISP_Modvol ispc) +{ + if (mv_mode == Xor) + { + // set states + devCache.SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE); + // write only bit 1 + devCache.SetRenderState(D3DRS_STENCILWRITEMASK, 2); + // no stencil testing + devCache.SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS); + devCache.SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_INVERT); // flip bit 1 + devCache.SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP); // else keep it + + // Cull mode needs to be set + devCache.SetRenderState(D3DRS_CULLMODE, CullMode[ispc.CullMode]); + + } + else if (mv_mode == Or) + { + // set states + devCache.SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE); + // write only bit 1 + devCache.SetRenderState(D3DRS_STENCILWRITEMASK, 2); + // no stencil testing + devCache.SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS); + // Or'ing of all triangles + devCache.SetRenderState(D3DRS_STENCILREF, 2); + devCache.SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE); // set bit 1 + devCache.SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP); // else keep it + + // Cull mode needs to be set + devCache.SetRenderState(D3DRS_CULLMODE, CullMode[ispc.CullMode]); + } + else + { + // Inclusion or Exclusion volume + + // no depth test + devCache.SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); + // write bits 1:0 + devCache.SetRenderState(D3DRS_STENCILWRITEMASK, 3); + // read bits 1:0 + devCache.SetRenderState(D3DRS_STENCILMASK, 3); + + if (mv_mode == Inclusion) + { + // Inclusion volume + //res : old : final + //0 : 0 : 00 + //0 : 1 : 01 + //1 : 0 : 01 + //1 : 1 : 01 + + // if (1<=st) st=1; else st=0; + devCache.SetRenderState(D3DRS_STENCILFUNC, D3DCMP_LESSEQUAL); + devCache.SetRenderState(D3DRS_STENCILREF, 1); + devCache.SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE); // set bit 0, clear bit 1 + devCache.SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_ZERO); + } + else + { + // Exclusion volume + /* + I've only seen a single game use it, so i guess it doesn't matter ? (Zombie revenge) + (actually, i think there was also another, racing game) + */ + // The initial value for exclusion volumes is 1 so we need to invert the result before and'ing. + //res : old : final + //0 : 0 : 00 + //0 : 1 : 01 + //1 : 0 : 00 + //1 : 1 : 00 + + // if (1 == st) st = 1; else st = 0; + devCache.SetRenderState(D3DRS_STENCILFUNC, D3DCMP_LESSEQUAL); + devCache.SetRenderState(D3DRS_STENCILREF, 1); + devCache.SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_KEEP); + devCache.SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_ZERO); + } + } +} + +void D3DRenderer::drawModVols(int first, int count) +{ + if (count == 0 || pvrrc.modtrig.used() == 0 || !config::ModifierVolumes) + return; + + device->SetVertexDeclaration(modVolVtxDecl); + device->SetStreamSource(0, modvolBuffer, 0, 3 * sizeof(float)); + + devCache.SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + devCache.SetRenderState(D3DRS_STENCILENABLE, TRUE); + devCache.SetRenderState(D3DRS_ZWRITEENABLE, D3DZB_FALSE); + devCache.SetRenderState(D3DRS_SCISSORTESTENABLE, scissorEnable); + if (scissorEnable) + device->SetScissorRect(&scissorRect); + + devCache.SetPixelShader(shaders.getModVolShader()); + + ModifierVolumeParam* params = &pvrrc.global_param_mvo.head()[first]; + + devCache.SetRenderState(D3DRS_COLORWRITEENABLE, 0); + + int mod_base = -1; + + for (int cmv = 0; cmv < count; cmv++) + { + ModifierVolumeParam& param = params[cmv]; + + if (param.count == 0) + continue; + + u32 mv_mode = param.isp.DepthMode; + + if (mod_base == -1) + mod_base = param.first; + + if (!param.isp.VolumeLast && mv_mode > 0) + setMVS_Mode(Or, param.isp); // OR'ing (open volume or quad) + else + setMVS_Mode(Xor, param.isp); // XOR'ing (closed volume) + + device->DrawPrimitive(D3DPT_TRIANGLELIST, param.first * 3, param.count); + + if (mv_mode == 1 || mv_mode == 2) + { + // Sum the area + setMVS_Mode(mv_mode == 1 ? Inclusion : Exclusion, param.isp); + device->DrawPrimitive(D3DPT_TRIANGLELIST, mod_base * 3, param.first + param.count - mod_base); + mod_base = -1; + } + } + //disable culling + devCache.SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + //enable color writes + devCache.SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_ALPHA | D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE); + + //black out any stencil with '1' + devCache.SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + devCache.SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + devCache.SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + + //only pixels that are Modvol enabled, and in area 1 + devCache.SetRenderState(D3DRS_STENCILFUNC, D3DCMP_EQUAL); + devCache.SetRenderState(D3DRS_STENCILREF, 0x81); + devCache.SetRenderState(D3DRS_STENCILMASK, 0x81); + + //clear the stencil result bits + devCache.SetRenderState(D3DRS_STENCILWRITEMASK, 3); + devCache.SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_ZERO); + devCache.SetRenderState(D3DRS_STENCILFAIL, D3DSTENCILOP_ZERO); + + //don't do depth testing + devCache.SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE); + + device->SetVertexDeclaration(mainVtxDecl); + device->SetStreamSource(0, vertexBuffer, 0, sizeof(Vertex)); + device->SetIndices(indexBuffer); + + device->DrawIndexedPrimitive(D3DPT_TRIANGLESTRIP, 0, 0, 4, 0, 2); + + //restore states + devCache.SetRenderState(D3DRS_STENCILENABLE, FALSE); + devCache.SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE); +} + +// Direct3D uses the color values of the first vertex for flat shaded triangle strips. +// On Dreamcast the last vertex is the provoking one so we must copy it onto the first. +// TODO refactor with Vk +void D3DRenderer::setProvokingVertices() +{ + auto setProvokingVertex = [](const List& list) { + u32 *idx_base = pvrrc.idx.head(); + Vertex *vtx_base = pvrrc.verts.head(); + const PolyParam *pp_end = list.LastPtr(0); + for (const PolyParam *pp = list.head(); pp != pp_end; pp++) + { + if (!pp->pcw.Gouraud && pp->count > 2) + { + for (u32 i = 0; i < pp->count - 2; i++) + { + Vertex *vertex = &vtx_base[idx_base[pp->first + i]]; + Vertex *lastVertex = &vtx_base[idx_base[pp->first + i + 2]]; + memcpy(vertex->col, lastVertex->col, 4); + memcpy(vertex->spc, lastVertex->spc, 4); + //memcpy(vertex->col1, lastVertex->col1, 4); + //memcpy(vertex->spc1, lastVertex->spc1, 4); + } + } + } + }; + setProvokingVertex(pvrrc.global_param_op); + setProvokingVertex(pvrrc.global_param_pt); + setProvokingVertex(pvrrc.global_param_tr); +} + +void D3DRenderer::drawStrips() +{ + RenderPass previous_pass {}; + for (int render_pass = 0; render_pass < pvrrc.render_passes.used(); render_pass++) + { + const RenderPass& current_pass = pvrrc.render_passes.head()[render_pass]; + u32 op_count = current_pass.op_count - previous_pass.op_count; + u32 pt_count = current_pass.pt_count - previous_pass.pt_count; + u32 tr_count = current_pass.tr_count - previous_pass.tr_count; + u32 mvo_count = current_pass.mvo_count - previous_pass.mvo_count; + DEBUG_LOG(RENDERER, "Render pass %d OP %d PT %d TR %d MV %d", render_pass + 1, + op_count, pt_count, tr_count, mvo_count); + + if (config::ModifierVolumes) + { + devCache.SetRenderState(D3DRS_STENCILENABLE, TRUE); + devCache.SetRenderState(D3DRS_STENCILWRITEMASK, 0xFF); + devCache.SetRenderState(D3DRS_STENCILFUNC, D3DCMP_ALWAYS); + devCache.SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_REPLACE); + devCache.SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP); + } + else + { + devCache.SetRenderState(D3DRS_STENCILENABLE, FALSE); + } + devCache.SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + + drawList(pvrrc.global_param_op, previous_pass.op_count, op_count); + + devCache.SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + devCache.SetRenderState(D3DRS_ALPHATESTENABLE, TRUE); + devCache.SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL); + devCache.SetRenderState(D3DRS_ALPHAREF, PT_ALPHA_REF & 0xFF); + + drawList(pvrrc.global_param_pt, previous_pass.pt_count, pt_count); + + devCache.SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); + + drawModVols(previous_pass.mvo_count, mvo_count); + + devCache.SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + devCache.SetRenderState(D3DRS_STENCILENABLE, FALSE); + + if (current_pass.autosort) + { + if (!config::PerStripSorting) + { + sortTriangles(previous_pass.tr_count, tr_count); + drawSorted(render_pass < pvrrc.render_passes.used() - 1); + } + else + { + SortPParams(previous_pass.tr_count, tr_count); + drawList(pvrrc.global_param_tr, previous_pass.tr_count, tr_count); + } + } + else + { + drawList(pvrrc.global_param_tr, previous_pass.tr_count, tr_count); + } + previous_pass = current_pass; + } +} + +void D3DRenderer::setBaseScissor() +{ + bool wide_screen_on = !pvrrc.isRTT && config::Widescreen && !matrices.IsClipped() && !config::Rotate90; + if (!wide_screen_on) + { + float fWidth; + float fHeight; + float min_x; + float min_y; + if (!pvrrc.isRTT) + { + glm::vec4 clip_min(pvrrc.fb_X_CLIP.min, pvrrc.fb_Y_CLIP.min, 0, 1); + glm::vec4 clip_dim(pvrrc.fb_X_CLIP.max - pvrrc.fb_X_CLIP.min + 1, + pvrrc.fb_Y_CLIP.max - pvrrc.fb_Y_CLIP.min + 1, 0, 0); + clip_min = matrices.GetScissorMatrix() * clip_min; + clip_dim = matrices.GetScissorMatrix() * clip_dim; + + min_x = clip_min[0]; + min_y = clip_min[1]; + fWidth = clip_dim[0]; + fHeight = clip_dim[1]; + if (fWidth < 0) + { + min_x += fWidth; + fWidth = -fWidth; + } + if (fHeight < 0) + { + min_y += fHeight; + fHeight = -fHeight; + } + if (matrices.GetSidebarWidth() > 0) + { + float scaled_offs_x = matrices.GetSidebarWidth(); + + D3DCOLOR borderColor = D3DCOLOR_ARGB(255, VO_BORDER_COL.Red, VO_BORDER_COL.Green, VO_BORDER_COL.Blue); + devCache.SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); + D3DRECT rects[] { + { 0, 0, lroundf(scaled_offs_x), (long)height }, + { (long)(width - scaled_offs_x), 0, (long)(width + 1), (long)height }, + }; + device->Clear(2, rects, D3DCLEAR_TARGET, borderColor, 0.f, 0); + } + } + else + { + fWidth = (float)(pvrrc.fb_X_CLIP.max - pvrrc.fb_X_CLIP.min + 1); + fHeight = (float)(pvrrc.fb_Y_CLIP.max - pvrrc.fb_Y_CLIP.min + 1); + min_x = (float)pvrrc.fb_X_CLIP.min; + min_y = (float)pvrrc.fb_Y_CLIP.min; + if (config::RenderResolution > 480 && !config::RenderToTextureBuffer) + { + min_x *= config::RenderResolution / 480.f; + min_y *= config::RenderResolution / 480.f; + fWidth *= config::RenderResolution / 480.f; + fHeight *= config::RenderResolution / 480.f; + } + } + scissorEnable = true; + scissorRect.left = lroundf(min_x); + scissorRect.top = lroundf(min_y); + scissorRect.right = scissorRect.left + lroundf(fWidth); + scissorRect.bottom = scissorRect.top + lroundf(fHeight); + device->SetScissorRect(&scissorRect); + devCache.SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); + } + else + { + devCache.SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); + scissorEnable = false; + } +} + +void D3DRenderer::prepareRttRenderTarget(u32 texAddress) +{ + u32 fbw = pvrrc.fb_X_CLIP.max + 1; + u32 fbh = pvrrc.fb_Y_CLIP.max + 1; + DEBUG_LOG(RENDERER, "RTT packmode=%d stride=%d - %d x %d @ %06x", + FB_W_CTRL.fb_packmode, FB_W_LINESTRIDE.stride * 8, fbw, fbh, texAddress); + // Find the smallest power of two texture that fits the viewport + u32 fbh2 = 2; + while (fbh2 < fbh) + fbh2 *= 2; + u32 fbw2 = 2; + while (fbw2 < fbw) + fbw2 *= 2; + if (!config::RenderToTextureBuffer) + { + fbw *= config::RenderResolution / 480.f; + fbh *= config::RenderResolution / 480.f; + fbw2 *= config::RenderResolution / 480.f; + fbh2 *= config::RenderResolution / 480.f; + } + rttTexture.reset(); + device->CreateTexture(fbw2, fbh2, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &rttTexture.get(), NULL); + + rttSurface.reset(); + rttTexture->GetSurfaceLevel(0, &rttSurface.get()); + device->SetRenderTarget(0, rttSurface); + + D3DVIEWPORT9 viewport; + viewport.X = viewport.Y = 0; + viewport.Width = fbw; + viewport.Height = fbh; + viewport.MinZ = 0; + viewport.MaxZ = 1; + device->SetViewport(&viewport); +} + +void D3DRenderer::readRttRenderTarget(u32 texAddress) +{ + u32 w = pvrrc.fb_X_CLIP.max + 1; + u32 h = pvrrc.fb_Y_CLIP.max + 1; + const u8 fb_packmode = FB_W_CTRL.fb_packmode; + if (config::RenderToTextureBuffer) + { + D3DSURFACE_DESC rttDesc; + rttSurface->GetDesc(&rttDesc); + ComPtr offscreenSurface; + verifyWin(device->CreateOffscreenPlainSurface(rttDesc.Width, rttDesc.Height, rttDesc.Format, D3DPOOL_SYSTEMMEM, &offscreenSurface.get(), nullptr)); + verifyWin(device->GetRenderTargetData(rttSurface, offscreenSurface)); + + PixelBuffer tmp_buf; + tmp_buf.init(w, h); + + u8 *p = (u8 *)tmp_buf.data(); + D3DLOCKED_RECT rect; + RECT lockRect { 0, 0, (long)w, (long)h }; + verifyWin(offscreenSurface->LockRect(&rect, &lockRect, D3DLOCK_READONLY)); + if ((u32)rect.Pitch == w * sizeof(u32)) + memcpy(p, rect.pBits, w * h * sizeof(u32)); + else + { + u8 *src = (u8 *)rect.pBits; + for (u32 y = 0; y < h; y++) + { + memcpy(p, src, w * sizeof(u32)); + src += rect.Pitch; + p += w * sizeof(u32); + } + } + verifyWin(offscreenSurface->UnlockRect()); + + u16 *dst = (u16 *)&vram[texAddress]; + WriteTextureToVRam<2, 1, 0, 3>(w, h, p, dst); + } + else + { + //memset(&vram[gl.rtt.texAddress], 0, size); + if (w <= 1024 && h <= 1024) + { + // TexAddr : (address), Reserved : 0, StrideSel : 0, ScanOrder : 1 + TCW tcw = { { texAddress >> 3, 0, 0, 1 } }; + switch (fb_packmode) { + case 0: + case 3: + tcw.PixelFmt = Pixel1555; + break; + case 1: + tcw.PixelFmt = Pixel565; + break; + case 2: + tcw.PixelFmt = Pixel4444; + break; + } + TSP tsp = { 0 }; + for (tsp.TexU = 0; tsp.TexU <= 7 && (8u << tsp.TexU) < w; tsp.TexU++) + ; + + for (tsp.TexV = 0; tsp.TexV <= 7 && (8u << tsp.TexV) < h; tsp.TexV++) + ; + + D3DTexture* texture = texCache.getTextureCacheData(tsp, tcw); + if (!texture->texture) + texture->Create(); + + texture->texture = rttTexture; + texture->dirty = 0; + libCore_vramlock_Lock(texture->sa_tex, texture->sa + texture->size - 1, texture); + } + } +} + +bool D3DRenderer::Render() +{ + if (resetting) + return false; + bool is_rtt = pvrrc.isRTT; + + backbuffer.reset(); + verifyWin(device->GetRenderTarget(0, &backbuffer.get())); + u32 texAddress = FB_W_SOF1 & VRAM_MASK; + if (is_rtt) + { + prepareRttRenderTarget(texAddress); + } + else + { + verifyWin(device->SetRenderTarget(0, framebufferSurface)); + D3DVIEWPORT9 viewport; + viewport.X = viewport.Y = 0; + viewport.Width = width; + viewport.Height = height; + viewport.MinZ = 0; + viewport.MaxZ = 1; + verifyWin(device->SetViewport(&viewport)); + } + verifyWin(device->SetDepthStencilSurface(depthSurface)); + matrices.CalcMatrices(&pvrrc, width, height); + // infamous DX9 half-pixel viewport shift + // https://docs.microsoft.com/en-us/windows/win32/direct3d9/directly-mapping-texels-to-pixels + glm::mat4 normalMat = glm::translate(glm::vec3(-1.f / width, 1.f / height, 0)) * matrices.GetNormalMatrix(); + verifyWin(device->SetVertexShaderConstantF(0, &normalMat[0][0], 4)); + + devCache.reset(); + devCache.SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); + device->Clear(0, NULL, D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER, 0, 0.0f, 0); + + if (!pvrrc.isRenderFramebuffer) + { + setProvokingVertices(); + + verify(ensureVertexBufferSize(vertexBuffer, vertexBufferSize, pvrrc.verts.bytes())); + void *ptr; + verifyWin(vertexBuffer->Lock(0, pvrrc.verts.bytes(), &ptr, D3DLOCK_DISCARD)); + memcpy(ptr, pvrrc.verts.head(), pvrrc.verts.bytes()); + vertexBuffer->Unlock(); + verify(ensureIndexBufferSize(indexBuffer, indexBufferSize, pvrrc.idx.bytes())); + verifyWin(indexBuffer->Lock(0, pvrrc.idx.bytes(), &ptr, D3DLOCK_DISCARD)); + memcpy(ptr, pvrrc.idx.head(), pvrrc.idx.bytes()); + indexBuffer->Unlock(); + + if (config::ModifierVolumes && pvrrc.modtrig.used()) + { + verify(ensureVertexBufferSize(modvolBuffer, modvolBufferSize, pvrrc.modtrig.bytes())); + verifyWin(modvolBuffer->Lock(0, pvrrc.modtrig.bytes(), &ptr, D3DLOCK_DISCARD)); + memcpy(ptr, pvrrc.modtrig.head(), pvrrc.modtrig.bytes()); + modvolBuffer->Unlock(); + } + + updateFogTexture(); + updatePaletteTexture(); + + devCache.SetVertexShader(shaders.getVertexShader(true)); + + // VERT and RAM fog color constants + u8* fog_colvert_bgra = (u8*)&FOG_COL_VERT; + u8* fog_colram_bgra = (u8*)&FOG_COL_RAM; + float ps_FOG_COL_VERT[4] = { fog_colvert_bgra[2] / 255.0f, fog_colvert_bgra[1] / 255.0f, fog_colvert_bgra[0] / 255.0f, 1 }; + float ps_FOG_COL_RAM[4] = { fog_colram_bgra[2] / 255.0f, fog_colram_bgra[1] / 255.0f, fog_colram_bgra[0] / 255.0f, 1 }; + device->SetPixelShaderConstantF(1, ps_FOG_COL_VERT, 1); + device->SetPixelShaderConstantF(2, ps_FOG_COL_RAM, 1); + + // Fog density and scale constants + u8* fog_density = (u8*)&FOG_DENSITY; + float fog_den_mant = fog_density[1] / 128.0f; //bit 7 -> x. bit, so [6:0] -> fraction -> /128 + s32 fog_den_exp = (s8)fog_density[0]; + float fog_den_float = fog_den_mant * powf(2.0f, (float)fog_den_exp) * config::ExtraDepthScale; + float fogDensityAndScale[4]= { fog_den_float, 1.f - FPU_SHAD_SCALE.scale_factor / 256.f, 0, 1 }; + device->SetPixelShaderConstantF(3, fogDensityAndScale, 1); + + // Color clamping + float fog_clamp_min[] { + ((pvrrc.fog_clamp_min >> 16) & 0xFF) / 255.0f, + ((pvrrc.fog_clamp_min >> 8) & 0xFF) / 255.0f, + ((pvrrc.fog_clamp_min >> 0) & 0xFF) / 255.0f, + ((pvrrc.fog_clamp_min >> 24) & 0xFF) / 255.0f + }; + device->SetPixelShaderConstantF(6, fog_clamp_min, 1); + float fog_clamp_max[] { + ((pvrrc.fog_clamp_max >> 16) & 0xFF) / 255.0f, + ((pvrrc.fog_clamp_max >> 8) & 0xFF) / 255.0f, + ((pvrrc.fog_clamp_max >> 0) & 0xFF) / 255.0f, + ((pvrrc.fog_clamp_max >> 24) & 0xFF) / 255.0f + }; + device->SetPixelShaderConstantF(7, fog_clamp_max, 1); + + devCache.SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE); + + device->SetVertexDeclaration(mainVtxDecl); + device->SetStreamSource(0, vertexBuffer, 0, sizeof(Vertex)); + device->SetIndices(indexBuffer); + + devCache.SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + devCache.SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + + devCache.SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + devCache.SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); + devCache.SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID); + devCache.SetRenderState(D3DRS_CLIPPLANEENABLE, 0); + + setBaseScissor(); + + if (!SUCCEEDED(device->BeginScene())) + { + WARN_LOG(RENDERER, "Render: BeginScene failed!"); + return false; + } + drawStrips(); + device->EndScene(); + } + else + { + renderDCFramebuffer(); + } + + verifyWin(device->SetRenderTarget(0, backbuffer)); + + if (is_rtt) + { + readRttRenderTarget(texAddress); + } + else + { + renderFramebuffer(); + DrawOSD(false); + frameRendered = true; + } + + return !is_rtt; +} + +void D3DRenderer::Resize(int w, int h) +{ + if (width == (u32)w && height == (u32)h) + return; + width = w; + height = h; + framebufferTexture.reset(); + framebufferSurface.reset(); + verifyWin(device->CreateTexture(width, height, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &framebufferTexture.get(), NULL)); + verifyWin(framebufferTexture->GetSurfaceLevel(0, &framebufferSurface.get())); + depthSurface.reset(); + verifyWin(device->CreateDepthStencilSurface(width, height, D3DFMT_D24S8, D3DMULTISAMPLE_NONE, 0, TRUE, &depthSurface.get(), nullptr)); +} + +void D3DRenderer::renderFramebuffer() +{ + devCache.SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); + device->ColorFill(backbuffer, 0, D3DCOLOR_ARGB(255, VO_BORDER_COL.Red, VO_BORDER_COL.Green, VO_BORDER_COL.Blue)); + int fx = 0; + int sx = 0; + float screenAR = (float)screen_width / screen_height; + int fbwidth = width; + int fbheight = height; + if (config::Rotate90) + std::swap(fbwidth, fbheight); + float renderAR = (float)fbwidth / fbheight; + if (renderAR > screenAR) + fx = (int)roundf((fbwidth - screenAR * fbheight) / 2.f); + else + sx = (int)roundf((screen_width - renderAR * screen_height) / 2.f); + + if (!config::Rotate90) + { + RECT rs { 0, 0, (long)width, (long)height }; + RECT rd { 0, 0, screen_width, screen_height }; + if (sx != 0) + { + rd.left = sx; + rd.right = screen_width - sx; + } + else + { + rs.left = fx; + rs.right = width - fx; + } + device->StretchRect(framebufferSurface, &rs, backbuffer, &rd, D3DTEXF_LINEAR); // This can fail if window is minimized + } + else + { + device->SetPixelShader(NULL); + device->SetVertexShader(NULL); + device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + device->SetRenderState(D3DRS_ZENABLE, FALSE); + device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); + device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + + glm::mat4 identity = glm::identity(); + glm::mat4 projection = glm::translate(glm::vec3(-1.f / screen_width, 1.f / screen_height, 0)) + * glm::rotate((float)M_PI_2, glm::vec3(0, 0, 1)); + + device->SetTransform(D3DTS_WORLD, (const D3DMATRIX *)&identity[0][0]); + device->SetTransform(D3DTS_VIEW, (const D3DMATRIX *)&identity[0][0]); + device->SetTransform(D3DTS_PROJECTION, (const D3DMATRIX *)&projection[0][0]); + + device->SetFVF(D3DFVF_XYZ | D3DFVF_TEX1); + D3DVIEWPORT9 viewport; + viewport.X = sx; + viewport.Y = fx * screen_width / height; + viewport.Width = screen_width - sx * 2; + viewport.Height = screen_height - 2 * fx * screen_width / height; + viewport.MinZ = 0; + viewport.MaxZ = 1; + verifyWin(device->SetViewport(&viewport)); + float coords[] { + -1, 1, 0.5f, 0, 0, + -1, -1, 0.5f, 0, 1, + 1, 1, 0.5f, 1, 0, + 1, -1, 0.5f, 1, 1, + }; + device->SetTexture(0, framebufferTexture); + device->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, coords, sizeof(float) * 5); + } +} + +bool D3DRenderer::RenderLastFrame() +{ + if (resetting || !frameRendered) + return false; + backbuffer.reset(); + verifyWin(device->GetRenderTarget(0, &backbuffer.get())); + renderFramebuffer(); + + return true; +} + +void D3DRenderer::updatePaletteTexture() +{ + if (!palette_updated) + return; + palette_updated = false; + + D3DLOCKED_RECT rect; + verifyWin(paletteTexture->LockRect(0, &rect, nullptr, 0)); + if (rect.Pitch == 32 * sizeof(u32)) + memcpy(rect.pBits, palette32_ram, 32 * 32 * sizeof(u32)); + else + { + u8 *dst = (u8 *)rect.pBits; + for (int y = 0; y < 32; y++) + memcpy(dst + y * rect.Pitch, palette32_ram + y * 32, 32 * sizeof(u32)); + } + + paletteTexture->UnlockRect(0); + device->SetTexture(1, paletteTexture); + device->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_POINT); + device->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_POINT); +} + +void D3DRenderer::updateFogTexture() +{ + if (!fog_needs_update || !config::Fog) + return; + fog_needs_update = false; + u8 temp_tex_buffer[256]; + MakeFogTexture(temp_tex_buffer); + + D3DLOCKED_RECT rect; + verifyWin(fogTexture->LockRect(0, &rect, nullptr, 0)); + if (rect.Pitch == 128) + memcpy(rect.pBits, temp_tex_buffer, 128 * 2 * 1); + else + { + u8 *dst = (u8 *)rect.pBits; + for (int y = 0; y < 2; y++) + memcpy(dst + y * rect.Pitch, temp_tex_buffer + y * 128, 128); + } + fogTexture->UnlockRect(0); + device->SetTexture(2, fogTexture); + device->SetSamplerState(2, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + device->SetSamplerState(2, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); +} + +void D3DRenderer::DrawOSD(bool clear_screen) +{ + theDXContext.setOverlay(!clear_screen); + gui_display_osd(); + theDXContext.setOverlay(false); +} + +Renderer* rend_DirectX9() +{ + return new D3DRenderer(); +} diff --git a/core/rend/dx9/d3d_renderer.h b/core/rend/dx9/d3d_renderer.h new file mode 100644 index 000000000..df6be7e03 --- /dev/null +++ b/core/rend/dx9/d3d_renderer.h @@ -0,0 +1,180 @@ +/* + Copyright 2021 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#pragma once +#include "types.h" +#include +#include "hw/pvr/Renderer_if.h" +#include +#include "dxcontext.h" +#include "rend/transform_matrix.h" +#include "d3d_texture.h" +#include "d3d_shaders.h" +#include "rend/sorter.h" + +class RenderStateCache +{ + IDirect3DDevice9 *device = nullptr; + std::array renderState; + std::array sampler0State; + IDirect3DVertexShader9 *vertexShader = nullptr; + IDirect3DPixelShader9 *pixelShader = nullptr; + IDirect3DBaseTexture9 *texture = nullptr; + +public: + void setDevice(IDirect3DDevice9 *device) { + this->device = device; + reset(); + } + + void reset() { + renderState.fill(0xfefefefe); + sampler0State.fill(0xfefefefe); + vertexShader = nullptr; + pixelShader = nullptr; + texture = nullptr; + } + + HRESULT SetRenderState(D3DRENDERSTATETYPE state, DWORD value) + { + if ((u32)state < renderState.size()) + { + if (renderState[state] == value) + return S_OK; + renderState[state] = value; + } + return device->SetRenderState(state, value); + } + HRESULT SetSamplerState(DWORD sampler, D3DSAMPLERSTATETYPE type, DWORD value) + { + if (sampler == 0 && (u32)type < sampler0State.size()) + { + if (sampler0State[type] == value) + return S_OK; + sampler0State[type] = value; + } + return device->SetSamplerState(sampler, type, value); + } + HRESULT SetVertexShader(IDirect3DVertexShader9 *pShader) + { + if (pShader == vertexShader) + return S_OK; + vertexShader = pShader; + return device->SetVertexShader(pShader); + } + HRESULT SetPixelShader(IDirect3DPixelShader9 *pShader) + { + if (pShader == pixelShader) + return S_OK; + pixelShader = pShader; + return device->SetPixelShader(pShader); + } + HRESULT SetTexture(DWORD stage, IDirect3DBaseTexture9 *pTexture) + { + if (stage == 0) + { + if (pTexture == texture) + return S_OK; + texture = pTexture; + } + return device->SetTexture(stage, pTexture); + } +}; + +struct D3DRenderer : public Renderer +{ + bool Init() override; + void Resize(int w, int h) override; + void Term() override; + bool Process(TA_context* ctx) override; + bool Render() override; + bool RenderLastFrame() override; + bool Present() override + { + if (!frameRendered) + return false; + frameRendered = false; + return true; + } + void DrawOSD(bool clear_screen) override; + BaseTextureCacheData *GetTexture(TSP tsp, TCW tcw) override; + void preReset(); + void postReset(); + +private: + enum ModifierVolumeMode { Xor, Or, Inclusion, Exclusion, ModeCount }; + + void drawStrips(); + template + void drawList(const List& gply, int first, int count); + template + void setGPState(const PolyParam *gp); + bool ensureVertexBufferSize(ComPtr& buffer, u32& currentSize, u32 minSize); + bool ensureIndexBufferSize(ComPtr& buffer, u32& currentSize, u32 minSize); + void updatePaletteTexture(); + void updateFogTexture(); + void renderFramebuffer(); + void readDCFramebuffer(); + void renderDCFramebuffer(); + void sortTriangles(int first, int count); + void drawSorted(bool multipass); + void setMVS_Mode(ModifierVolumeMode mv_mode, ISP_Modvol ispc); + void drawModVols(int first, int count); + void setProvokingVertices(); + void setTexMode(D3DSAMPLERSTATETYPE state, u32 clamp, u32 mirror); + void setBaseScissor(); + void prepareRttRenderTarget(u32 texAddress); + + void readRttRenderTarget(u32 texAddress); + + RenderStateCache devCache; + ComPtr device; + ComPtr vertexBuffer; + u32 vertexBufferSize = 0; + ComPtr modvolBuffer; + u32 modvolBufferSize = 0; + ComPtr indexBuffer; + u32 indexBufferSize = 0; + ComPtr sortedTriIndexBuffer; + u32 sortedTriIndexBufferSize = 0; + ComPtr mainVtxDecl; + ComPtr modVolVtxDecl; + + ComPtr framebufferTexture; + ComPtr framebufferSurface; + ComPtr backbuffer; + ComPtr paletteTexture; + ComPtr fogTexture; + ComPtr dcfbTexture; + ComPtr dcfbSurface; + ComPtr rttTexture; + ComPtr rttSurface; + ComPtr depthSurface; + + u32 width = 0; + u32 height = 0; + TransformMatrix matrices; + D3DTextureCache texCache; + std::vector pidx_sort; + D3DShaders shaders; + RECT scissorRect{}; + bool scissorEnable = false; + bool resetting = false; + bool frameRendered = false; +}; + diff --git a/core/rend/dx9/d3d_shaders.cpp b/core/rend/dx9/d3d_shaders.cpp new file mode 100644 index 000000000..e21bde07f --- /dev/null +++ b/core/rend/dx9/d3d_shaders.cpp @@ -0,0 +1,352 @@ +/* + Copyright 2021 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#include "d3d_shaders.h" + +#define SHADER_DEBUG 0 // D3DXSHADER_DEBUG|D3DXSHADER_SKIPOPTIMIZATION + +const char *VertexShader = R"( +struct vertex_in +{ + float4 pos : POSITION; + float4 col : COLOR0; + float4 spc : COLOR1; + float2 uv : TEXCOORD0; +}; + +struct vertex_out +{ + float4 pos : POSITION; + float4 uv : TEXCOORD0; + float4 col : COLOR0; + float4 spc : COLOR1; +}; + +float4x4 normal_matrix : register(c0); + +vertex_out main(in vertex_in vin) +{ + vertex_out vo; + vo.pos = mul(normal_matrix, vin.pos); +#if pp_Gouraud == 1 + vo.col = vin.col * vo.pos.z; + vo.spc = vin.spc * vo.pos.z; +#else + // flat shading: no interpolation + vo.col = vin.col; + vo.spc = vin.spc; +#endif + vo.uv = float4(vin.uv * vo.pos.z, 0, vo.pos.z); + + vo.pos.w = 1.0f; + vo.pos.z = 0; + + return vo; +} + +)"; + +const char *PixelShader = R"( + +#define PI 3.1415926f + +struct pixel +{ + float4 uv : TEXCOORD0; + float4 col : COLOR0; +#if pp_Texture == 1 && (pp_BumpMap == 1 || pp_Offset == 1) + float4 offs : COLOR1; +#endif + +}; + +sampler2D samplr : register(s0); +sampler2D tex_pal : register(s1); +sampler2D fog_table : register(s2); + +float4 palette_index : register(c0); +float4 FOG_COL_VERT : register(c1); +float4 FOG_COL_RAM : register(c2); +float4 FOG_DENSITY_SCALE : register(c3); +float4 ClipTest : register(c4); +float4 trilinear_alpha : register(c5); +float4 fog_clamp_min : register(c6); +float4 fog_clamp_max : register(c7); + +float fog_mode2(float w) +{ + float z = clamp(w * FOG_DENSITY_SCALE.x, 1.0f, 255.9999f); + float exp = floor(log2(z)); + float m = z * 16.0f / pow(2.0, exp) - 16.0f; + float idx = floor(m) + exp * 16.0f + 0.5f; + float4 fog_coef = tex2D(fog_table, float2(idx / 128.0f, 0.75f - (m - floor(m)) / 2.0f)); + return fog_coef.a; +} + +float4 fog_clamp(float4 col) +{ +#if FogClamping == 1 + return clamp(col, fog_clamp_min, fog_clamp_max); +#else + return col; +#endif +} + +#if pp_Palette == 1 + +float4 palettePixel(float4 coords) +{ + int color_idx = int(floor(tex2Dproj(samplr, coords).a * 255.0f + 0.5f) + palette_index.x); + float2 c = float2((fmod(float(color_idx), 32.0f) * 2.0f + 1.0f) / 64.0f, (float(color_idx / 32) * 2.0f + 1.0f) / 64.0f); + return tex2D(tex_pal, c); +} + +#endif + +struct PSO +{ + float4 col : COLOR0; + float z : DEPTH; +}; + +PSO main(in pixel inpix) +{ + // Clip inside the box +/* TODO + #if pp_ClipInside == 1 + if (VPOS.x >= pp_ClipTest.x && VPOS.x <= pp_ClipTest.z + && VPOS.y >= pp_ClipTest.y && VPOS.y <= pp_ClipTest.w) + discard; + #endif +*/ + +#if pp_Gouraud == 1 + float4 color = inpix.col / inpix.uv.w; + #if pp_Texture == 1 && (pp_BumpMap == 1 || pp_Offset == 1) + float4 offset = inpix.offs / inpix.uv.w; + #endif +#else + float4 color = inpix.col; + #if pp_Texture == 1 && (pp_BumpMap == 1 || pp_Offset == 1) + float4 offset = inpix.offs; + #endif +#endif + #if pp_UseAlpha == 0 + color.a = 1.0f; + #endif + #if pp_FogCtrl == 3 + color = float4(FOG_COL_RAM.rgb, fog_mode2(inpix.uv.w)); + #endif + #if pp_Texture == 1 + { + #if pp_Palette == 0 + float4 texcol = tex2Dproj(samplr, inpix.uv); + #else + float4 texcol = palettePixel(inpix.uv); + #endif + + #if pp_BumpMap == 1 + float s = PI / 2.0f * (texcol.a * 15.0f * 16.0f + texcol.r * 15.0f) / 255.0f; + float r = 2.0f * PI * (texcol.g * 15.0f * 16.0f + texcol.b * 15.0f) / 255.0f; + texcol[3] = clamp(offset.a + offset.r * sin(s) + offset.g * cos(s) * cos(r - 2.0f * PI * offset.b), 0.0f, 1.0f); + texcol.rgb = float3(1.0f, 1.0f, 1.0f); + #else + #if pp_IgnoreTexA == 1 + texcol.a = 1.0f; + #endif + #endif + #if pp_ShadInstr == 0 + color = texcol; + #endif + #if pp_ShadInstr == 1 + color.rgb *= texcol.rgb; + color.a = texcol.a; + #endif + #if pp_ShadInstr == 2 + color.rgb = lerp(color.rgb, texcol.rgb, texcol.a); + #endif + #if pp_ShadInstr == 3 + color *= texcol; + #endif + + #if pp_Offset == 1 && pp_BumpMap == 0 + color.rgb += offset.rgb; + #endif + } + #endif + + color = fog_clamp(color); + + #if pp_FogCtrl == 0 + color.rgb = lerp(color.rgb, FOG_COL_RAM.rgb, fog_mode2(inpix.uv.w)); + #endif + #if pp_FogCtrl == 1 && pp_Offset == 1 && pp_BumpMap == 0 + color.rgb = lerp(color.rgb, FOG_COL_VERT.rgb, offset.a); + #endif + + #if pp_TriLinear == 1 + color *= trilinear_alpha; + #endif + + //color.rgb = float3(inpix.uv.w * FOG_DENSITY_SCALE.x / 128.0f); + PSO pso; + float w = inpix.uv.w * 100000.0f; + pso.z = log2(1.0f + w) / 34.0f; + pso.col = color; + + return pso; +} + +PSO modifierVolume(float4 uv : TEXCOORD0) +{ + PSO pso; + float w = uv.w * 100000.0f; + pso.z = log2(1.0f + w) / 34.0f; + pso.col = float4(0, 0, 0, FOG_DENSITY_SCALE.y); + + return pso; +} +)"; + +const char *MacroValues[] { "0", "1", "2", "3" }; + +static D3DXMACRO VertexMacros[] +{ + { "pp_Gouraud", "1" }, + { 0, 0 } +}; + +constexpr u32 MacroTexture = 0; +constexpr u32 MacroOffset = 1; +constexpr u32 MacroShadInstr = 2; +constexpr u32 MacroIgnoreTexA = 3; +constexpr u32 MacroUseAlpha = 4; +constexpr u32 MacroFogCtrl = 5; +constexpr u32 MacroFogClamping = 6; +constexpr u32 MacroPalette = 7; +constexpr u32 MacroBumpMap = 8; +constexpr u32 MacroTriLinear = 9; +constexpr u32 MacroGouraud = 10; + +static D3DXMACRO PixelMacros[] +{ + { "pp_Texture", "0" }, + { "pp_Offset", "0" }, + { "pp_ShadInstr", "0" }, + { "pp_IgnoreTexA", "0" }, + { "pp_UseAlpha", "0" }, + { "pp_FogCtrl", "0" }, + { "FogClamping", "0" }, + { "pp_Palette", "0" }, + { "pp_BumpMap", "0" }, + { "pp_TriLinear", "0" }, + { "pp_Gouraud", "1" }, + {0, 0} + //{ "pp_ClipInside", "0" }, // TODO +}; + +const ComPtr& D3DShaders::getShader(bool pp_Texture, bool pp_UseAlpha, bool pp_IgnoreTexA, u32 pp_ShadInstr, + bool pp_Offset, u32 pp_FogCtrl, bool pp_BumpMap, bool fog_clamping, + bool trilinear, bool palette, bool gouraud) +{ + u32 hash = (int)pp_Texture + | (pp_UseAlpha << 1) + | (pp_IgnoreTexA << 2) + | (pp_ShadInstr << 3) + | (pp_Offset << 5) + | (pp_FogCtrl << 6) + | (pp_BumpMap << 8) + | (fog_clamping << 9) + | (trilinear << 10) + | (palette << 11) + | (gouraud << 12); + auto it = shaders.find(hash); + if (it == shaders.end()) + { + verify(pp_ShadInstr < ARRAY_SIZE(MacroValues)); + verify(pp_FogCtrl < ARRAY_SIZE(MacroValues)); + PixelMacros[MacroTexture].Definition = MacroValues[pp_Texture]; + PixelMacros[MacroUseAlpha].Definition = MacroValues[pp_UseAlpha]; + PixelMacros[MacroIgnoreTexA].Definition = MacroValues[pp_IgnoreTexA]; + PixelMacros[MacroShadInstr].Definition = MacroValues[pp_ShadInstr]; + PixelMacros[MacroOffset].Definition = MacroValues[pp_Offset]; + PixelMacros[MacroFogCtrl].Definition = MacroValues[pp_FogCtrl]; + PixelMacros[MacroBumpMap].Definition = MacroValues[pp_BumpMap]; + PixelMacros[MacroFogClamping].Definition = MacroValues[fog_clamping]; + PixelMacros[MacroTriLinear].Definition = MacroValues[trilinear]; + PixelMacros[MacroPalette].Definition = MacroValues[palette]; + PixelMacros[MacroGouraud].Definition = MacroValues[gouraud]; + ComPtr shader = compilePS(PixelShader, "main", PixelMacros); + verify((bool )shader); + it = shaders.insert(std::make_pair(hash, shader)).first; + } + return it->second; +} + +const ComPtr& D3DShaders::getVertexShader(bool gouraud) +{ + ComPtr& vertexShader = gouraud ? gouraudVertexShader : flatVertexShader; + if (!vertexShader) + { + VertexMacros[0].Definition = MacroValues[gouraud]; + vertexShader = compileVS(VertexShader, "main", VertexMacros); + } + + return vertexShader; +} + +const ComPtr& D3DShaders::getModVolShader() +{ + if (!modVolShader) + modVolShader = compilePS(PixelShader, "modifierVolume", PixelMacros); + + return modVolShader; +} + +ComPtr D3DShaders::compileShader(const char* source, const char* function, const char* profile, const D3DXMACRO* pDefines) +{ + ComPtr errors; + ComPtr shader; + ComPtr constants; + D3DXCompileShader(source, strlen(source), pDefines, NULL, function, profile, SHADER_DEBUG, &shader.get(), &errors.get(), &constants.get()); + if (errors) { + char *text = (char *) errors->GetBufferPointer(); + WARN_LOG(RENDERER, "%s", text); + } + return shader; +} + +ComPtr D3DShaders::compileVS(const char* source, const char* function, const D3DXMACRO* pDefines) +{ + ComPtr buffer = compileShader(source, function, D3DXGetVertexShaderProfile(device), pDefines); + ComPtr shader; + if (buffer) + device->CreateVertexShader((DWORD *)buffer->GetBufferPointer(), &shader.get()); + + return shader; +} + +ComPtr D3DShaders::compilePS(const char* source, const char* function, const D3DXMACRO* pDefines) +{ + ComPtr buffer = compileShader(source, function, D3DXGetPixelShaderProfile(device), pDefines); + ComPtr shader; + if (buffer) + device->CreatePixelShader((DWORD *)buffer->GetBufferPointer(), &shader.get()); + + return shader; +} diff --git a/core/rend/dx9/d3d_shaders.h b/core/rend/dx9/d3d_shaders.h new file mode 100644 index 000000000..5219d52cb --- /dev/null +++ b/core/rend/dx9/d3d_shaders.h @@ -0,0 +1,54 @@ +/* + Copyright 2021 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#pragma once +#include +#include "dxcontext.h" +#include + +class D3DShaders +{ +public: + void init(const ComPtr& device) + { + this->device = device; + } + + const ComPtr& getShader(bool pp_Texture, bool pp_UseAlpha, bool pp_IgnoreTexA, u32 pp_ShadInstr, + bool pp_Offset, u32 pp_FogCtrl, bool pp_BumpMap, bool fog_clamping, bool trilinear, bool palette, bool gouraud); + const ComPtr& getVertexShader(bool gouraud); + const ComPtr& getModVolShader(); + void term() { + shaders.clear(); + gouraudVertexShader.reset(); + flatVertexShader.reset(); + modVolShader.reset(); + device.reset(); + } + +private: + ComPtr compileShader(const char* source, const char* function, const char* profile, const D3DXMACRO* pDefines); + ComPtr compileVS(const char* source, const char* function, const D3DXMACRO* pDefines); + ComPtr compilePS(const char* source, const char* function, const D3DXMACRO* pDefines); + + ComPtr device; + std::unordered_map> shaders; + ComPtr gouraudVertexShader; + ComPtr flatVertexShader; + ComPtr modVolShader; +}; diff --git a/core/rend/dx9/d3d_texture.cpp b/core/rend/dx9/d3d_texture.cpp new file mode 100644 index 000000000..02fabeea4 --- /dev/null +++ b/core/rend/dx9/d3d_texture.cpp @@ -0,0 +1,129 @@ +/* + Copyright 2021 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#include "d3d_texture.h" + +void D3DTexture::UploadToGPU(int width, int height, u8* temp_tex_buffer, bool mipmapped, bool mipmapsIncluded) +{ + D3DFORMAT d3dFormat; + u32 bpp = 2; + switch (tex_type) + { + case TextureType::_5551: + d3dFormat = D3DFMT_A1R5G5B5; + break; + case TextureType::_4444: + d3dFormat = D3DFMT_A4R4G4B4; + break; + case TextureType::_565: + d3dFormat = D3DFMT_R5G6B5; + break; + case TextureType::_8888: + bpp = 4; + d3dFormat = D3DFMT_A8R8G8B8; + break; + case TextureType::_8: + bpp = 1; + d3dFormat = D3DFMT_A8; + break; + default: + return; + } + int mipmapLevels = 1; + if (mipmapsIncluded) + { + mipmapLevels = 0; + int dim = width; + while (dim != 0) + { + mipmapLevels++; + dim >>= 1; + } + } + + D3DLOCKED_RECT rect; + while (true) + { + if (texture == nullptr) + { + u32 levels = mipmapLevels; + u32 usage = 0; + if (mipmapped && !mipmapsIncluded) + { + levels = 0; + usage = D3DUSAGE_AUTOGENMIPMAP; + } + theDXContext.getDevice()->CreateTexture(width, height, levels, usage, d3dFormat, D3DPOOL_MANAGED, &texture.get(), 0); // TODO the managed pool persists between device resets + verify(texture != nullptr); + } + if (SUCCEEDED(texture->LockRect(mipmapLevels - 1, &rect, nullptr, 0))) + break; + D3DSURFACE_DESC desc; + texture->GetLevelDesc(0, &desc); + if (desc.Pool != D3DPOOL_DEFAULT) + // it should be lockable so error out + return; + // RTT targets are created in the default pool and aren't lockable, so delete it and recreate it in the managed pool + texture.reset(); + } + for (int i = 0; i < mipmapLevels; i++) + { + u32 w = mipmapLevels == 1 ? width : 1 << i; + u32 h = mipmapLevels == 1 ? height : 1 << i; + if (w * bpp == (u32)rect.Pitch) + memcpy(rect.pBits, temp_tex_buffer, w * bpp * h); + else + { + u8 *dst = (u8 *)rect.pBits; + u8 *src = temp_tex_buffer; + for (u32 l = 0; l < h; l++) + { + memcpy(dst, src, w * bpp); + dst += rect.Pitch; + src += w * bpp; + } + } + texture->UnlockRect(mipmapLevels - i - 1); + temp_tex_buffer += (1 << (2 * i)) * bpp; + if (i < mipmapLevels - 1) + if (FAILED(texture->LockRect(mipmapLevels - i - 2, &rect, nullptr, 0))) + break; + } +} + +bool D3DTexture::Delete() +{ + if (!BaseTextureCacheData::Delete()) + return false; + + texture.reset(); + return true; +} + +void D3DTexture::loadCustomTexture() +{ + u32 size = custom_width * custom_height; + u8 *p = custom_image_data; + while (size--) + { + // RGBA -> BGRA + std::swap(p[0], p[2]); + p += 4; + } + CheckCustomTexture(); +} diff --git a/core/rend/dx9/d3d_texture.h b/core/rend/dx9/d3d_texture.h new file mode 100644 index 000000000..38d36bc8a --- /dev/null +++ b/core/rend/dx9/d3d_texture.h @@ -0,0 +1,52 @@ +/* + Copyright 2021 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#include "rend/TexCache.h" +#include +#include "dxcontext.h" + +class D3DTexture final : public BaseTextureCacheData +{ +public: + ComPtr texture; + std::string GetId() override { return std::to_string((uintptr_t)texture.get()); } + void UploadToGPU(int width, int height, u8* temp_tex_buffer, bool mipmapped, + bool mipmapsIncluded = false) override; + bool Delete() override; + void loadCustomTexture(); +}; + +class D3DTextureCache final : public BaseTextureCache +{ +public: + D3DTextureCache() { + D3DTexture::SetDirectXColorOrder(true); + } + ~D3DTextureCache() { + Clear(); + } + void Cleanup() + { + texturesToDelete.clear(); + CollectCleanup(); + } + void DeleteLater(ComPtr tex) { texturesToDelete.push_back(tex); } + +private: + std::vector> texturesToDelete; +}; diff --git a/core/rend/dx9/dxcontext.cpp b/core/rend/dx9/dxcontext.cpp new file mode 100644 index 000000000..b9659fa03 --- /dev/null +++ b/core/rend/dx9/dxcontext.cpp @@ -0,0 +1,139 @@ +/* + Copyright 2021 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#include "dxcontext.h" +#include "d3d_renderer.h" +#include "rend/gui.h" +#ifdef USE_SDL +#include "sdl/sdl.h" +#endif +#include "hw/pvr/Renderer_if.h" + +DXContext theDXContext; +extern int screen_width, screen_height; // FIXME + +bool DXContext::Init() +{ +#ifdef USE_SDL + if (!sdl_recreate_window(0)) + return false; +#endif + + pD3D.reset(Direct3DCreate9(D3D_SDK_VERSION)); + if (!pD3D) + return false; + memset(&d3dpp, 0, sizeof(d3dpp)); + d3dpp.hDeviceWindow = hWnd; + d3dpp.Windowed = true; + d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; + d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; + d3dpp.EnableAutoDepthStencil = FALSE; // No need for depth/stencil buffer for the backbuffer +#ifndef TEST_AUTOMATION + d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE; // Present with vsync +#else + d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; // Present without vsync, maximum unthrottled framerate +#endif + if (FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, + D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &pDevice.get()))) + return false; + gui_init(); + overlay.init(pDevice); + return ImGui_ImplDX9_Init(pDevice.get()); +} + +void DXContext::Term() +{ + overlay.term(); + ImGui_ImplDX9_Shutdown(); + pDevice.reset(); + pD3D.reset(); +} + +void DXContext::Present() +{ + HRESULT result = pDevice->Present(NULL, NULL, NULL, NULL); + // Handle loss of D3D9 device + if (result == D3DERR_DEVICELOST) + { + result = pDevice->TestCooperativeLevel(); + if (result == D3DERR_DEVICENOTRESET) + resetDevice(); + } + else if (FAILED(result)) + WARN_LOG(RENDERER, "Present failed %x", result); +} + +void DXContext::EndImGuiFrame() +{ + verify((bool)pDevice); + pDevice->SetRenderState(D3DRS_ZENABLE, FALSE); + pDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + pDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); + if (!overlayOnly) + { + pDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_RGBA(0, 0, 0, 255), 1.0f, 0); + if (renderer != nullptr) + renderer->RenderLastFrame(); + } + if (SUCCEEDED(pDevice->BeginScene())) + { + if (overlayOnly) + { + if (crosshairsNeeded() || config::FloatVMUs) + overlay.draw(screen_width, screen_height, config::FloatVMUs, true); + } + else + { + overlay.draw(screen_width, screen_height, true, false); + } + ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData()); + pDevice->EndScene(); + } +} + +void DXContext::resize() +{ + if (!pDevice) + return; + RECT rect; + GetClientRect(hWnd, &rect); + d3dpp.BackBufferWidth = screen_width = rect.right; + d3dpp.BackBufferHeight = screen_height = rect.bottom; + if (screen_width == 0 || screen_height == 0) + // window minimized + return; + resetDevice(); +} + +void DXContext::resetDevice() +{ + if (renderer != nullptr) + ((D3DRenderer *)renderer)->preReset(); + overlay.term(); + ImGui_ImplDX9_InvalidateDeviceObjects(); + HRESULT hr = pDevice->Reset(&d3dpp); + if (hr == D3DERR_INVALIDCALL) + { + ERROR_LOG(RENDERER, "DX9 device reset failed"); + return; + } + ImGui_ImplDX9_CreateDeviceObjects(); + overlay.init(pDevice); + if (renderer != nullptr) + ((D3DRenderer *)renderer)->postReset(); +} diff --git a/core/rend/dx9/dxcontext.h b/core/rend/dx9/dxcontext.h new file mode 100644 index 000000000..d2854b44c --- /dev/null +++ b/core/rend/dx9/dxcontext.h @@ -0,0 +1,65 @@ +/* + Copyright 2021 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#pragma once +#ifdef _WIN32 +#include "types.h" +#include +#include +#include "imgui_impl_dx9.h" +#include "comptr.h" +#include "d3d_overlay.h" + +class DXContext +{ +public: + bool Init(); + void Term(); + void EndImGuiFrame(); + void Present(); + const ComPtr& getD3D() const { return pD3D; } + const ComPtr& getDevice() const { return pDevice; } + void resize(); + void setOverlay(bool overlayOnly) { this->overlayOnly = overlayOnly; } + std::string getDriverName() const { + D3DADAPTER_IDENTIFIER9 id; + pD3D->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &id); + return std::string(id.Description); + } + std::string getDriverVersion() const { + D3DADAPTER_IDENTIFIER9 id; + pD3D->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &id); + return std::to_string(id.DriverVersion.HighPart >> 16) + "." + std::to_string((u16)id.DriverVersion.HighPart) + + "." + std::to_string(id.DriverVersion.LowPart >> 16) + "." + std::to_string((u16)id.DriverVersion.LowPart); + } + void setNativeWindow(HWND hWnd) { + this->hWnd = hWnd; + } + +private: + void resetDevice(); + + ComPtr pD3D; + ComPtr pDevice; + D3DPRESENT_PARAMETERS d3dpp{}; + bool overlayOnly = false; + HWND hWnd = nullptr; + D3DOverlay overlay; +}; +extern DXContext theDXContext; +#endif diff --git a/core/rend/dx9/imgui_impl_dx9.cpp b/core/rend/dx9/imgui_impl_dx9.cpp new file mode 100644 index 000000000..0e952fddf --- /dev/null +++ b/core/rend/dx9/imgui_impl_dx9.cpp @@ -0,0 +1,270 @@ +// dear imgui: Renderer Backend for DirectX9 +// This needs to be used along with a Platform Backend (e.g. Win32) + +// Implemented features: +// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID! +// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. + +// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. +// Read online: https://github.com/ocornut/imgui/tree/master/docs + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2021-03-03: DirectX9: Added support for IMGUI_USE_BGRA_PACKED_COLOR in user's imconfig file. +// 2021-02-18: DirectX9: Change blending equation to preserve alpha in output buffer. +// 2019-05-29: DirectX9: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. +// 2019-04-30: DirectX9: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. +// 2019-03-29: Misc: Fixed erroneous assert in ImGui_ImplDX9_InvalidateDeviceObjects(). +// 2019-01-16: Misc: Disabled fog before drawing UI's. Fixes issue #2288. +// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. +// 2018-06-08: Misc: Extracted imgui_impl_dx9.cpp/.h away from the old combined DX9+Win32 example. +// 2018-06-08: DirectX9: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. +// 2018-05-07: Render: Saving/restoring Transform because they don't seem to be included in the StateBlock. Setting shading mode to Gouraud. +// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplDX9_RenderDrawData() in the .h file so you can call it yourself. +// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. + +#include "imgui/imgui.h" +#include "imgui_impl_dx9.h" + +// DirectX +#include + +// DirectX data +static LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; +static LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; +static LPDIRECT3DINDEXBUFFER9 g_pIB = NULL; +static LPDIRECT3DTEXTURE9 g_FontTexture = NULL; +static int g_VertexBufferSize = 5000, g_IndexBufferSize = 10000; + +struct CUSTOMVERTEX +{ + float pos[3]; + D3DCOLOR col; + float uv[2]; +}; +#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1) + +static void ImGui_ImplDX9_SetupRenderState(ImDrawData* draw_data) +{ + // Setup viewport + D3DVIEWPORT9 vp; + vp.X = vp.Y = 0; + vp.Width = (DWORD)draw_data->DisplaySize.x; + vp.Height = (DWORD)draw_data->DisplaySize.y; + vp.MinZ = 0.0f; + vp.MaxZ = 1.0f; + g_pd3dDevice->SetViewport(&vp); + + // Setup render state: fixed-pipeline, alpha-blending, no face culling, no depth testing, shade mode (for gradient) + g_pd3dDevice->SetPixelShader(NULL); + g_pd3dDevice->SetVertexShader(NULL); + g_pd3dDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + g_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE); + g_pd3dDevice->SetRenderState(D3DRS_ZENABLE, FALSE); + g_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + g_pd3dDevice->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE); + g_pd3dDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); + g_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + g_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + g_pd3dDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE); + g_pd3dDevice->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE); + g_pd3dDevice->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA); + g_pd3dDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); + g_pd3dDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD); + g_pd3dDevice->SetRenderState(D3DRS_FOGENABLE, FALSE); + g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE); + g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + g_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE); + g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); + g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + g_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + + // Setup orthographic projection matrix + // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. + // Being agnostic of whether or can be used, we aren't relying on D3DXMatrixIdentity()/D3DXMatrixOrthoOffCenterLH() or DirectX::XMMatrixIdentity()/DirectX::XMMatrixOrthographicOffCenterLH() + { + float L = draw_data->DisplayPos.x + 0.5f; + float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x + 0.5f; + float T = draw_data->DisplayPos.y + 0.5f; + float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y + 0.5f; + D3DMATRIX mat_identity = { { { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f } } }; + D3DMATRIX mat_projection = + { { { + 2.0f/(R-L), 0.0f, 0.0f, 0.0f, + 0.0f, 2.0f/(T-B), 0.0f, 0.0f, + 0.0f, 0.0f, 0.5f, 0.0f, + (L+R)/(L-R), (T+B)/(B-T), 0.5f, 1.0f + } } }; + g_pd3dDevice->SetTransform(D3DTS_WORLD, &mat_identity); + g_pd3dDevice->SetTransform(D3DTS_VIEW, &mat_identity); + g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &mat_projection); + } +} + +// Render function. +void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data) +{ + // Avoid rendering when minimized + if (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f) + return; + + // Create and grow buffers if needed + if (!g_pVB || g_VertexBufferSize < draw_data->TotalVtxCount) + { + if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } + g_VertexBufferSize = draw_data->TotalVtxCount + 5000; + if (g_pd3dDevice->CreateVertexBuffer(g_VertexBufferSize * sizeof(CUSTOMVERTEX), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &g_pVB, NULL) < 0) + return; + } + if (!g_pIB || g_IndexBufferSize < draw_data->TotalIdxCount) + { + if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } + g_IndexBufferSize = draw_data->TotalIdxCount + 10000; + if (g_pd3dDevice->CreateIndexBuffer(g_IndexBufferSize * sizeof(ImDrawIdx), D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, sizeof(ImDrawIdx) == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32, D3DPOOL_DEFAULT, &g_pIB, NULL) < 0) + return; + } + + // Copy and convert all vertices into a single contiguous buffer, convert colors to DX9 default format. + // FIXME-OPT: This is a minor waste of resource, the ideal is to use imconfig.h and + // 1) to avoid repacking colors: #define IMGUI_USE_BGRA_PACKED_COLOR + // 2) to avoid repacking vertices: #define IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT struct ImDrawVert { ImVec2 pos; float z; ImU32 col; ImVec2 uv; } + CUSTOMVERTEX* vtx_dst; + ImDrawIdx* idx_dst; + if (g_pVB->Lock(0, (UINT)(draw_data->TotalVtxCount * sizeof(CUSTOMVERTEX)), (void**)&vtx_dst, D3DLOCK_DISCARD) < 0) + return; + if (g_pIB->Lock(0, (UINT)(draw_data->TotalIdxCount * sizeof(ImDrawIdx)), (void**)&idx_dst, D3DLOCK_DISCARD) < 0) + return; + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + const ImDrawVert* vtx_src = cmd_list->VtxBuffer.Data; + for (int i = 0; i < cmd_list->VtxBuffer.Size; i++) + { + vtx_dst->pos[0] = vtx_src->pos.x; + vtx_dst->pos[1] = vtx_src->pos.y; + vtx_dst->pos[2] = 0.0f; +#ifdef IMGUI_USE_BGRA_PACKED_COLOR + vtx_dst->col = vtx_src->col; +#else + vtx_dst->col = (vtx_src->col & 0xFF00FF00) | ((vtx_src->col & 0xFF0000) >> 16) | ((vtx_src->col & 0xFF) << 16); // RGBA --> ARGB for DirectX9 +#endif + vtx_dst->uv[0] = vtx_src->uv.x; + vtx_dst->uv[1] = vtx_src->uv.y; + vtx_dst++; + vtx_src++; + } + memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); + idx_dst += cmd_list->IdxBuffer.Size; + } + g_pVB->Unlock(); + g_pIB->Unlock(); + g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(CUSTOMVERTEX)); + g_pd3dDevice->SetIndices(g_pIB); + g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX); + + // Setup desired DX state + ImGui_ImplDX9_SetupRenderState(draw_data); + + // Render command lists + // (Because we merged all buffers into a single one, we maintain our own offset into them) + int global_vtx_offset = 0; + int global_idx_offset = 0; + ImVec2 clip_off = draw_data->DisplayPos; + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback != NULL) + { + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + ImGui_ImplDX9_SetupRenderState(draw_data); + else + pcmd->UserCallback(cmd_list, pcmd); + } + else + { + const RECT r = { (LONG)(pcmd->ClipRect.x - clip_off.x), (LONG)(pcmd->ClipRect.y - clip_off.y), (LONG)(pcmd->ClipRect.z - clip_off.x), (LONG)(pcmd->ClipRect.w - clip_off.y) }; + const LPDIRECT3DTEXTURE9 texture = (LPDIRECT3DTEXTURE9)pcmd->TextureId; + g_pd3dDevice->SetTexture(0, texture); + g_pd3dDevice->SetScissorRect(&r); + g_pd3dDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, pcmd->VtxOffset + global_vtx_offset, 0, (UINT)cmd_list->VtxBuffer.Size, pcmd->IdxOffset + global_idx_offset, pcmd->ElemCount / 3); + } + } + global_idx_offset += cmd_list->IdxBuffer.Size; + global_vtx_offset += cmd_list->VtxBuffer.Size; + } +} + +bool ImGui_ImplDX9_Init(IDirect3DDevice9* device) +{ + // Setup backend capabilities flags + ImGuiIO& io = ImGui::GetIO(); + io.BackendRendererName = "imgui_impl_dx9"; + io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + + g_pd3dDevice = device; + g_pd3dDevice->AddRef(); + return true; +} + +void ImGui_ImplDX9_Shutdown() +{ + ImGui_ImplDX9_InvalidateDeviceObjects(); + if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; } +} + +static bool ImGui_ImplDX9_CreateFontsTexture() +{ + // Build texture atlas + ImGuiIO& io = ImGui::GetIO(); + unsigned char* pixels; + int width, height, bytes_per_pixel; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height, &bytes_per_pixel); + + // Upload texture to graphics system + g_FontTexture = NULL; + if (g_pd3dDevice->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &g_FontTexture, NULL) < 0) + return false; + D3DLOCKED_RECT tex_locked_rect; + if (g_FontTexture->LockRect(0, &tex_locked_rect, NULL, 0) != D3D_OK) + return false; + for (int y = 0; y < height; y++) + memcpy((unsigned char*)tex_locked_rect.pBits + tex_locked_rect.Pitch * y, pixels + (width * bytes_per_pixel) * y, (width * bytes_per_pixel)); + g_FontTexture->UnlockRect(0); + + // Store our identifier + io.Fonts->SetTexID((ImTextureID)g_FontTexture); + + return true; +} + +bool ImGui_ImplDX9_CreateDeviceObjects() +{ + if (!g_pd3dDevice) + return false; + if (!ImGui_ImplDX9_CreateFontsTexture()) + return false; + return true; +} + +void ImGui_ImplDX9_InvalidateDeviceObjects() +{ + if (!g_pd3dDevice) + return; + if (g_pVB) { g_pVB->Release(); g_pVB = NULL; } + if (g_pIB) { g_pIB->Release(); g_pIB = NULL; } + if (g_FontTexture) { g_FontTexture->Release(); g_FontTexture = NULL; ImGui::GetIO().Fonts->SetTexID(NULL); } // We copied g_pFontTextureView to io.Fonts->TexID so let's clear that as well. +} + +void ImGui_ImplDX9_NewFrame() +{ + if (!g_FontTexture) + ImGui_ImplDX9_CreateDeviceObjects(); +} diff --git a/core/rend/dx9/imgui_impl_dx9.h b/core/rend/dx9/imgui_impl_dx9.h new file mode 100644 index 000000000..05e23a8b8 --- /dev/null +++ b/core/rend/dx9/imgui_impl_dx9.h @@ -0,0 +1,24 @@ +// dear imgui: Renderer Backend for DirectX9 +// This needs to be used along with a Platform Backend (e.g. Win32) + +// Implemented features: +// [X] Renderer: User texture binding. Use 'LPDIRECT3DTEXTURE9' as ImTextureID. Read the FAQ about ImTextureID! +// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. + +// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. +// Read online: https://github.com/ocornut/imgui/tree/master/docs + +#pragma once +#include "imgui/imgui.h" // IMGUI_IMPL_API + +struct IDirect3DDevice9; + +IMGUI_IMPL_API bool ImGui_ImplDX9_Init(IDirect3DDevice9* device); +IMGUI_IMPL_API void ImGui_ImplDX9_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplDX9_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplDX9_RenderDrawData(ImDrawData* draw_data); + +// Use if you want to reset your rendering device without losing Dear ImGui state. +IMGUI_IMPL_API bool ImGui_ImplDX9_CreateDeviceObjects(); +IMGUI_IMPL_API void ImGui_ImplDX9_InvalidateDeviceObjects(); diff --git a/core/rend/gl4/gldraw.cpp b/core/rend/gl4/gldraw.cpp index bdca7ecd8..cb750ad7e 100644 --- a/core/rend/gl4/gldraw.cpp +++ b/core/rend/gl4/gldraw.cpp @@ -37,19 +37,19 @@ static gl4PipelineShader *gl4GetProgram(bool cp_AlphaTest, bool pp_InsideClippin { u32 rv=0; - rv |= pp_InsideClipping; - rv <<= 1; rv |= cp_AlphaTest; - rv <<= 1; rv |= pp_Texture; - rv <<= 1; rv |= pp_UseAlpha; - rv <<= 1; rv |= pp_IgnoreTexA; + rv |= (int)pp_InsideClipping; + rv <<= 1; rv |= (int)cp_AlphaTest; + rv <<= 1; rv |= (int)pp_Texture; + rv <<= 1; rv |= (int)pp_UseAlpha; + rv <<= 1; rv |= (int)pp_IgnoreTexA; rv <<= 2; rv |= pp_ShadInstr; - rv <<= 1; rv |= pp_Offset; + rv <<= 1; rv |= (int)pp_Offset; rv <<= 2; rv |= pp_FogCtrl; - rv <<= 1; rv |= pp_TwoVolumes; - rv <<= 1; rv |= pp_Gouraud; - rv <<= 1; rv |= pp_BumpMap; - rv <<= 1; rv |= fog_clamping; - rv <<= 1; rv |= palette; + rv <<= 1; rv |= (int)pp_TwoVolumes; + rv <<= 1; rv |= (int)pp_Gouraud; + rv <<= 1; rv |= (int)pp_BumpMap; + rv <<= 1; rv |= (int)fog_clamping; + rv <<= 1; rv |= (int)palette; rv <<= 2; rv |= (int)pass; gl4PipelineShader *shader = &gl4.shaders[rv]; @@ -100,17 +100,17 @@ static void SetGPState(const PolyParam* gp) // Trilinear filtering. Ignore if texture isn't mipmapped (shenmue snowflakes) if (gp->pcw.Texture && gp->tsp.FilterMode > 1 && Type != ListType_Punch_Through && gp->tcw.MipMapped == 1) { - gl4ShaderUniforms.trilinear_alpha = 0.25 * (gp->tsp.MipMapD & 0x3); + gl4ShaderUniforms.trilinear_alpha = 0.25f * (gp->tsp.MipMapD & 0x3); if (gp->tsp.FilterMode == 2) // Trilinear pass A - gl4ShaderUniforms.trilinear_alpha = 1.0 - gl4ShaderUniforms.trilinear_alpha; + gl4ShaderUniforms.trilinear_alpha = 1.0f - gl4ShaderUniforms.trilinear_alpha; } else gl4ShaderUniforms.trilinear_alpha = 1.0; int clip_rect[4] = {}; TileClipping clipmode = GetTileClip(gp->tileclip, ViewportMatrix, clip_rect); - bool palette = false; + bool gpuPalette = false; if (pass == Pass::Depth) { @@ -136,7 +136,7 @@ static void SetGPState(const PolyParam* gp) bool color_clamp = gp->tsp.ColorClamp && (pvrrc.fog_clamp_min != 0 || pvrrc.fog_clamp_max != 0xffffffff); int fog_ctrl = config::Fog ? gp->tsp.FogCtrl : 2; - palette = BaseTextureCacheData::IsGpuHandledPaletted(gp->tsp, gp->tcw); + gpuPalette = gp->texture != nullptr ? gp->texture->gpuPalette : false; CurrentShader = gl4GetProgram(Type == ListType_Punch_Through ? true : false, clipmode == TileClipping::Inside, @@ -150,12 +150,12 @@ static void SetGPState(const PolyParam* gp) gp->pcw.Gouraud, gp->tcw.PixelFmt == PixelBumpMap, color_clamp, - palette, + gpuPalette, pass); } glcache.UseProgram(CurrentShader->program); - if (palette) + if (gpuPalette) { if (gp->tcw.PixelFmt == PixelPal4) gl4ShaderUniforms.palette_index = gp->tcw.PalSelect << 4; @@ -178,7 +178,8 @@ static void SetGPState(const PolyParam* gp) glcache.Disable(GL_BLEND); if (clipmode == TileClipping::Inside) - glUniform4f(CurrentShader->pp_ClipTest, clip_rect[0], clip_rect[1], clip_rect[0] + clip_rect[2], clip_rect[1] + clip_rect[3]); + glUniform4f(CurrentShader->pp_ClipTest, (float)clip_rect[0], (float)clip_rect[1], + (float)(clip_rect[0] + clip_rect[2]), (float)(clip_rect[1] + clip_rect[3])); if (clipmode == TileClipping::Outside) { glcache.Enable(GL_SCISSOR_TEST); @@ -197,11 +198,11 @@ static void SetGPState(const PolyParam* gp) for (int i = 0; i < 2; i++) { glActiveTexture(GL_TEXTURE0 + i); - GLuint texid = (GLuint)(i == 0 ? gp->texid : gp->texid1); + TextureCacheData *texture = (TextureCacheData *)(i == 0 ? gp->texture : gp->texture1); - glBindTexture(GL_TEXTURE_2D, texid == (GLuint)-1 ? 0 : texid); + glBindTexture(GL_TEXTURE_2D, texture == nullptr ? 0 : texture->texID); - if (texid != (GLuint)-1) + if (texture != nullptr) { TSP tsp = i == 0 ? gp->tsp : gp->tsp1; diff --git a/core/rend/gl4/gles.cpp b/core/rend/gl4/gles.cpp index bdf7bcc88..5bf80e107 100644 --- a/core/rend/gl4/gles.cpp +++ b/core/rend/gl4/gles.cpp @@ -588,6 +588,7 @@ static bool gl4_init() } fog_needs_update = true; palette_updated = true; + TextureCacheData::SetDirectXColorOrder(false); return true; } @@ -633,7 +634,7 @@ static bool RenderFrame(int width, int height) const bool is_rtt = pvrrc.isRTT; - TransformMatrix matrices(pvrrc, width, height); + TransformMatrix matrices(pvrrc, width, height); gl4ShaderUniforms.normal_mat = matrices.GetNormalMatrix(); const glm::mat4& scissor_mat = matrices.GetScissorMatrix(); ViewportMatrix = matrices.GetViewportMatrix(); diff --git a/core/rend/gles/gldraw.cpp b/core/rend/gles/gldraw.cpp index ec82356b3..0e411908b 100644 --- a/core/rend/gles/gldraw.cpp +++ b/core/rend/gles/gldraw.cpp @@ -107,10 +107,10 @@ __forceinline { if (gp->pcw.Texture && gp->tsp.FilterMode > 1 && Type != ListType_Punch_Through && gp->tcw.MipMapped == 1) { - ShaderUniforms.trilinear_alpha = 0.25 * (gp->tsp.MipMapD & 0x3); + ShaderUniforms.trilinear_alpha = 0.25f * (gp->tsp.MipMapD & 0x3); if (gp->tsp.FilterMode == 2) // Trilinear pass A - ShaderUniforms.trilinear_alpha = 1.0 - ShaderUniforms.trilinear_alpha; + ShaderUniforms.trilinear_alpha = 1.f - ShaderUniforms.trilinear_alpha; } else ShaderUniforms.trilinear_alpha = 1.f; @@ -120,7 +120,8 @@ __forceinline int clip_rect[4] = {}; TileClipping clipmode = GetTileClip(gp->tileclip, ViewportMatrix, clip_rect); - bool palette = BaseTextureCacheData::IsGpuHandledPaletted(gp->tsp, gp->tcw); + TextureCacheData *texture = (TextureCacheData *)gp->texture; + bool gpuPalette = texture != nullptr ? texture->gpuPalette : false; CurrentShader = GetProgram(Type == ListType_Punch_Through ? true : false, clipmode == TileClipping::Inside, @@ -134,12 +135,12 @@ __forceinline gp->tcw.PixelFmt == PixelBumpMap, color_clamp, ShaderUniforms.trilinear_alpha != 1.f, - palette); + gpuPalette); glcache.UseProgram(CurrentShader->program); if (CurrentShader->trilinear_alpha != -1) glUniform1f(CurrentShader->trilinear_alpha, ShaderUniforms.trilinear_alpha); - if (palette) + if (gpuPalette) { if (gp->tcw.PixelFmt == PixelPal4) ShaderUniforms.palette_index = gp->tcw.PalSelect << 4; @@ -149,7 +150,8 @@ __forceinline } if (clipmode == TileClipping::Inside) - glUniform4f(CurrentShader->pp_ClipTest, clip_rect[0], clip_rect[1], clip_rect[0] + clip_rect[2], clip_rect[1] + clip_rect[3]); + glUniform4f(CurrentShader->pp_ClipTest, (float)clip_rect[0], (float)clip_rect[1], + (float)(clip_rect[0] + clip_rect[2]), (float)(clip_rect[1] + clip_rect[3])); if (clipmode == TileClipping::Outside) { glcache.Enable(GL_SCISSOR_TEST); @@ -158,35 +160,40 @@ __forceinline else SetBaseClipping(); - //This bit control which pixels are affected - //by modvols - const u32 stencil=(gp->pcw.Shadow!=0)?0x80:0x0; - - glcache.StencilFunc(GL_ALWAYS,stencil,stencil); - - glcache.BindTexture(GL_TEXTURE_2D, gp->texid == (u64)-1 ? 0 : (GLuint)gp->texid); - - SetTextureRepeatMode(GL_TEXTURE_WRAP_S, gp->tsp.ClampU, gp->tsp.FlipU); - SetTextureRepeatMode(GL_TEXTURE_WRAP_T, gp->tsp.ClampV, gp->tsp.FlipV); - - //set texture filter mode - if (gp->tsp.FilterMode == 0 || palette) + if (config::ModifierVolumes) { - //disable filtering, mipmaps - glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + //This bit control which pixels are affected + //by modvols + const u32 stencil = gp->pcw.Shadow != 0 ? 0x80 : 0; + glcache.StencilFunc(GL_ALWAYS, stencil, stencil); } - else + + if (texture != nullptr) { - //bilinear filtering - //PowerVR supports also trilinear via two passes, but we ignore that for now - bool mipmapped = gp->tcw.MipMapped != 0 && gp->tcw.ScanOrder == 0 && config::UseMipmaps; - glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mipmapped ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR); - glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glcache.BindTexture(GL_TEXTURE_2D, texture->texID); + + SetTextureRepeatMode(GL_TEXTURE_WRAP_S, gp->tsp.ClampU, gp->tsp.FlipU); + SetTextureRepeatMode(GL_TEXTURE_WRAP_T, gp->tsp.ClampV, gp->tsp.FlipV); + + //set texture filter mode + if (gp->tsp.FilterMode == 0 || gpuPalette) + { + //disable filtering, mipmaps + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + else + { + //bilinear filtering + //PowerVR supports also trilinear via two passes, but we ignore that for now + bool mipmapped = gp->tcw.MipMapped != 0 && gp->tcw.ScanOrder == 0 && config::UseMipmaps; + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mipmapped ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR); + glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); #ifdef GL_TEXTURE_LOD_BIAS - if (!gl.is_gles && gl.gl_major >= 3 && mipmapped) - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, D_Adjust_LoD_Bias[gp->tsp.MipMapD]); + if (!gl.is_gles && gl.gl_major >= 3 && mipmapped) + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, D_Adjust_LoD_Bias[gp->tsp.MipMapD]); #endif + } } // Apparently punch-through polys support blending, or at least some combinations diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index 06d83ee5e..d25f68991 100644 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -852,6 +852,7 @@ bool gles_init() } fog_needs_update = true; palette_updated = true; + TextureCacheData::SetDirectXColorOrder(false); return true; } @@ -1025,7 +1026,7 @@ bool RenderFrame(int width, int height) vtx_min_fZ *= 0.98f; vtx_max_fZ *= 1.001f; - TransformMatrix matrices(pvrrc, width, height); + TransformMatrix matrices(pvrrc, width, height); ShaderUniforms.normal_mat = matrices.GetNormalMatrix(); const glm::mat4& scissor_mat = matrices.GetScissorMatrix(); ViewportMatrix = matrices.GetViewportMatrix(); diff --git a/core/rend/gles/gles.h b/core/rend/gles/gles.h index 43f9218f2..97b58ea81 100755 --- a/core/rend/gles/gles.h +++ b/core/rend/gles/gles.h @@ -130,13 +130,8 @@ struct gl_ctx extern gl_ctx gl; extern GLuint fbTextureId; -u64 gl_GetTexture(TSP tsp,TCW tcw); -struct text_info { - u16* pdata; - u32 width; - u32 height; - u32 textype; // 0 565, 1 1555, 2 4444 -}; +BaseTextureCacheData *gl_GetTexture(TSP tsp, TCW tcw); + enum ModifierVolumeMode { Xor, Or, Inclusion, Exclusion, ModeCount }; void gl_load_osd_resources(); @@ -151,7 +146,6 @@ void SetupMatrices(float dc_width, float dc_height, float scale_x, float scale_y, float scissoring_scale_x, float scissoring_scale_y, float &ds2s_offs_x, glm::mat4& normal_mat, glm::mat4& scissor_mat); -text_info raw_GetTexture(TSP tsp, TCW tcw); void SetCull(u32 CullMode); s32 SetTileClip(u32 val, GLint uniform); void SetMVS_Mode(ModifierVolumeMode mv_mode, ISP_Modvol ispc); @@ -270,7 +264,7 @@ struct OpenGLRenderer : Renderer void DrawOSD(bool clear_screen) override { OSD_DRAW(clear_screen); } - u64 GetTexture(TSP tsp, TCW tcw) override + BaseTextureCacheData *GetTexture(TSP tsp, TCW tcw) override { return gl_GetTexture(tsp, tcw); } diff --git a/core/rend/gles/gltex.cpp b/core/rend/gles/gltex.cpp index 190a95433..38969712b 100644 --- a/core/rend/gles/gltex.cpp +++ b/core/rend/gles/gltex.cpp @@ -392,7 +392,7 @@ static int TexCacheLookups; static int TexCacheHits; //static float LastTexCacheStats; -u64 gl_GetTexture(TSP tsp, TCW tcw) +BaseTextureCacheData *gl_GetTexture(TSP tsp, TCW tcw) { TexCacheLookups++; @@ -429,7 +429,7 @@ u64 gl_GetTexture(TSP tsp, TCW tcw) // } //return gl texture - return tf->texID; + return tf; } GLuint fbTextureId; diff --git a/core/rend/gui.cpp b/core/rend/gui.cpp index dcd62efe3..33b6e7910 100644 --- a/core/rend/gui.cpp +++ b/core/rend/gui.cpp @@ -30,7 +30,6 @@ #include "network/naomi_network.h" #include "wsi/context.h" #include "input/gamepad_device.h" -#include "input/keyboard_device.h" #include "gui_util.h" #include "gui_android.h" #include "game_scanner.h" @@ -45,8 +44,8 @@ extern void UpdateInputState(); static bool game_started; -extern u8 kb_shift; // shift keys pressed (bitmask) -extern u8 kb_key[6]; // normal keys pressed +extern u8 kb_shift[MAPLE_PORTS]; // shift keys pressed (bitmask) +extern u8 kb_key[MAPLE_PORTS][6]; // normal keys pressed int screen_dpi = 96; int insetLeft, insetRight, insetTop, insetBottom; @@ -58,6 +57,9 @@ static bool commandLineStart; #ifdef __ANDROID__ static bool touch_up; #endif +static u32 mouseButtons; +static int mouseX, mouseY; +static float mouseWheel; static std::string error_msg; static std::string osd_message; static double osd_message_end; @@ -262,10 +264,47 @@ void gui_init() EventManager::listen(Event::Terminate, emuEventCallback); } +void gui_keyboard_input(u16 wc) +{ + ImGuiIO& io = ImGui::GetIO(); + if (io.WantCaptureKeyboard) + io.AddInputCharacter(wc); +} + +void gui_keyboard_inputUTF8(const std::string& s) +{ + ImGuiIO& io = ImGui::GetIO(); + if (io.WantCaptureKeyboard) + io.AddInputCharactersUTF8(s.c_str()); +} + +void gui_set_mouse_position(int x, int y) +{ + mouseX = x; + mouseY = y; +} + +void gui_set_mouse_button(int button, bool pressed) +{ + if (pressed) + mouseButtons |= 1 << button; + else + mouseButtons &= ~(1 << button); +} + +void gui_set_mouse_wheel(float delta) +{ + mouseWheel += delta; +} + static void ImGui_Impl_NewFrame() { if (config::RendererType.isOpenGL()) ImGui_ImplOpenGL3_NewFrame(); +#ifdef _WIN32 + else if (config::RendererType.isDirectX()) + ImGui_ImplDX9_NewFrame(); +#endif ImGui::GetIO().DisplaySize.x = screen_width; ImGui::GetIO().DisplaySize.y = screen_height; @@ -274,43 +313,40 @@ static void ImGui_Impl_NewFrame() UpdateInputState(); // Read keyboard modifiers inputs - io.KeyCtrl = (kb_shift & (0x01 | 0x10)) != 0; - io.KeyShift = (kb_shift & (0x02 | 0x20)) != 0; + io.KeyCtrl = (kb_shift[0] & (0x01 | 0x10)) != 0; + io.KeyShift = (kb_shift[0] & (0x02 | 0x20)) != 0; io.KeyAlt = false; io.KeySuper = false; memset(&io.KeysDown[0], 0, sizeof(io.KeysDown)); - for (int i = 0; i < IM_ARRAYSIZE(kb_key); i++) - if (kb_key[i] != 0) - io.KeysDown[kb_key[i]] = true; + for (int i = 0; i < IM_ARRAYSIZE(kb_key[0]); i++) + if (kb_key[0][i] != 0) + io.KeysDown[kb_key[0][i]] = true; else break; - if (mo_x_phy < 0 || mo_x_phy >= screen_width || mo_y_phy < 0 || mo_y_phy >= screen_height) + if (mouseX < 0 || mouseX >= screen_width || mouseY < 0 || mouseY >= screen_height) io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); else - io.MousePos = ImVec2(mo_x_phy, mo_y_phy); + io.MousePos = ImVec2(mouseX, mouseY); static bool delayTouch; #ifdef __ANDROID__ // Delay touch by one frame to allow widgets to be hovered before click // This is required for widgets using ImGuiButtonFlags_AllowItemOverlap such as TabItem's - if (!delayTouch && (mo_buttons[0] & (1 << 2)) == 0 && !io.MouseDown[ImGuiMouseButton_Left]) + if (!delayTouch && (mouseButtons & (1 << 0)) != 0 && !io.MouseDown[ImGuiMouseButton_Left]) delayTouch = true; else delayTouch = false; #endif if (io.WantCaptureMouse) { - io.MouseWheel = -mo_wheel_delta[0] / 16; - // Reset all relative mouse positions - mo_x_delta[0] = 0; - mo_y_delta[0] = 0; - mo_wheel_delta[0] = 0; + io.MouseWheel = -mouseWheel / 16; + mouseWheel = 0; } if (!delayTouch) - io.MouseDown[ImGuiMouseButton_Left] = (mo_buttons[0] & (1 << 2)) == 0; - io.MouseDown[ImGuiMouseButton_Right] = (mo_buttons[0] & (1 << 1)) == 0; - io.MouseDown[ImGuiMouseButton_Middle] = (mo_buttons[0] & (1 << 3)) == 0; - io.MouseDown[3] = (mo_buttons[0] & (1 << 0)) == 0; + io.MouseDown[ImGuiMouseButton_Left] = (mouseButtons & (1 << 0)) != 0; + io.MouseDown[ImGuiMouseButton_Right] = (mouseButtons & (1 << 1)) != 0; + io.MouseDown[ImGuiMouseButton_Middle] = (mouseButtons & (1 << 2)) != 0; + io.MouseDown[3] = (mouseButtons & (1 << 3)) != 0; io.NavInputs[ImGuiNavInput_Activate] = (kcode[0] & DC_BTN_A) == 0; io.NavInputs[ImGuiNavInput_Cancel] = (kcode[0] & DC_BTN_B) == 0; @@ -332,19 +368,6 @@ static void ImGui_Impl_NewFrame() if (io.NavInputs[ImGuiNavInput_LStickDown] < 0.1f) io.NavInputs[ImGuiNavInput_LStickDown] = 0.f; - if (KeyboardDevice::GetInstance() != NULL) - { - const std::string input_text = KeyboardDevice::GetInstance()->get_character_input(); - if (io.WantCaptureKeyboard) - { - for (const u8 b : input_text) - // Cheap ISO Latin-1 to UTF-8 conversion - if (b < 0x80) - io.AddInputCharacter(b); - else - io.AddInputCharacter((0xc2 + (b > 0xbf)) | ((b & 0x3f) + 0x80) << 8); - } - } ImGui::GetStyle().Colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.06f, 0.06f, 0.06f, 0.94f); } @@ -966,9 +989,6 @@ static void gui_display_settings() { static bool maple_devices_changed; - RenderType pvr_rend = config::RendererType; - bool vulkan = !config::RendererType.isOpenGL(); - fullScreenWindow(false); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0); @@ -1210,6 +1230,9 @@ static void gui_display_settings() ImGui::Spacing(); OptionSlider("Mouse sensitivity", config::MouseSensitivity, 1, 500); +#ifdef _WIN32 + OptionCheckbox("Use Raw Input", config::UseRawInput, "Supports multiple pointing devices (mice, light guns) and keyboards"); +#endif ImGui::Spacing(); header("Dreamcast Devices"); @@ -1303,10 +1326,37 @@ static void gui_display_settings() } if (ImGui::BeginTabItem("Video")) { + int renderApi; + bool perPixel; + switch (config::RendererType) + { + default: + case RenderType::OpenGL: + renderApi = 0; + perPixel = false; + break; + case RenderType::OpenGL_OIT: + renderApi = 0; + perPixel = true; + break; + case RenderType::Vulkan: + renderApi = 1; + perPixel = false; + break; + case RenderType::Vulkan_OIT: + renderApi = 1; + perPixel = true; + break; + case RenderType::DirectX9: + renderApi = 2; + perPixel = false; + break; + } + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, normal_padding); #if !defined(__APPLE__) bool has_per_pixel = false; - if (!vulkan) + if (renderApi == 0) has_per_pixel = !theGLContext.IsGLES() && theGLContext.GetMajorVersion() >= 4; #ifdef USE_VULKAN else @@ -1317,7 +1367,7 @@ static void gui_display_settings() #endif header("Transparent Sorting"); { - int renderer = (pvr_rend == RenderType::OpenGL_OIT || pvr_rend == RenderType::Vulkan_OIT) ? 2 : config::PerStripSorting ? 1 : 0; + int renderer = perPixel ? 2 : config::PerStripSorting ? 1 : 0; ImGui::Columns(has_per_pixel ? 3 : 2, "renderers", false); ImGui::RadioButton("Per Triangle", &renderer, 0); ImGui::SameLine(); @@ -1337,24 +1387,15 @@ static void gui_display_settings() switch (renderer) { case 0: - if (!vulkan) - pvr_rend = RenderType::OpenGL; // regular Open GL - else - pvr_rend = RenderType::Vulkan; // regular Vulkan + perPixel = false; config::PerStripSorting.set(false); break; case 1: - if (!vulkan) - pvr_rend = RenderType::OpenGL; - else - pvr_rend = RenderType::Vulkan; + perPixel = false; config::PerStripSorting.set(true); break; case 2: - if (!vulkan) - pvr_rend = RenderType::OpenGL_OIT; - else - pvr_rend = RenderType::Vulkan_OIT; + perPixel = true; break; } } @@ -1399,11 +1440,27 @@ static void gui_display_settings() OptionCheckbox("Rotate Screen 90°", config::Rotate90, "Rotate the screen 90° counterclockwise"); OptionCheckbox("Delay Frame Swapping", config::DelayFrameSwapping, "Useful to avoid flashing screen or glitchy videos. Not recommended on slow platforms"); -#ifdef USE_VULKAN - ImGui::Checkbox("Use Vulkan Renderer", &vulkan); - ImGui::SameLine(); - ShowHelpMarker("Use Vulkan instead of Open GL/GLES"); +#if defined(USE_VULKAN) || defined(_WIN32) + ImGui::Text("Graphics API:"); +#if defined(USE_VULKAN) && defined(_WIN32) + constexpr u32 columns = 3; +#else + constexpr u32 columns = 2; #endif + ImGui::Columns(columns, "renderApi", false); + ImGui::RadioButton("Open GL", &renderApi, 0); + ImGui::NextColumn(); +#ifdef USE_VULKAN + ImGui::RadioButton("Vulkan", &renderApi, 1); + ImGui::NextColumn(); +#endif +#ifdef _WIN32 + ImGui::RadioButton("DirectX", &renderApi, 2); + ImGui::NextColumn(); +#endif + ImGui::Columns(1, NULL, false); +#endif + const std::array scalings{ 0.5f, 1.f, 1.5f, 2.f, 2.5f, 3.f, 4.f, 4.5f, 5.f }; const std::array scalingsText{ "Half", "Native", "x1.5", "x2", "x2.5", "x3", "x4", "x4.5", "x5" }; std::array vres; @@ -1483,6 +1540,19 @@ static void gui_display_settings() } ImGui::PopStyleVar(); ImGui::EndTabItem(); + + switch (renderApi) + { + case 0: + config::RendererType = perPixel ? RenderType::OpenGL_OIT : RenderType::OpenGL; + break; + case 1: + config::RendererType = perPixel ? RenderType::Vulkan_OIT : RenderType::Vulkan; + break; + case 2: + config::RendererType = RenderType::DirectX9; + break; + } } if (ImGui::BeginTabItem("Audio")) { @@ -1719,7 +1789,7 @@ static void gui_display_settings() ImGui::Text("Version: %s", (const char *)glGetString(GL_VERSION)); } #ifdef USE_VULKAN - else + else if (config::RendererType.isVulkan()) { header("Vulkan"); std::string name = VulkanContext::Instance()->GetDriverName(); @@ -1728,6 +1798,18 @@ static void gui_display_settings() ImGui::Text("Version: %s", version.c_str()); } #endif +#ifdef _WIN32 + else if (config::RendererType.isDirectX()) + { + if (ImGui::CollapsingHeader("DirectX", ImGuiTreeNodeFlags_DefaultOpen)) + { + std::string name = theDXContext.getDriverName(); + ImGui::Text("Driver Name: %s", name.c_str()); + std::string version = theDXContext.getDriverVersion(); + ImGui::Text("Version: %s", version.c_str()); + } + } +#endif #ifdef __ANDROID__ ImGui::Separator(); @@ -1747,11 +1829,6 @@ static void gui_display_settings() windowDragScroll(); ImGui::End(); ImGui::PopStyleVar(); - - if (vulkan != !config::RendererType.isOpenGL()) - pvr_rend = !vulkan ? RenderType::OpenGL - : config::RendererType == RenderType::OpenGL_OIT ? RenderType::Vulkan_OIT : RenderType::Vulkan; - config::RendererType = pvr_rend; } void gui_display_notification(const char *msg, int duration) @@ -1789,11 +1866,10 @@ static void gui_display_content() ImGui::Unindent(10 * scaling); static ImGuiTextFilter filter; - if (KeyboardDevice::GetInstance() != NULL) - { - ImGui::SameLine(0, 32 * scaling); - filter.Draw("Filter"); - } +#ifndef __ANDROID__ + ImGui::SameLine(0, 32 * scaling); + filter.Draw("Filter"); +#endif if (gui_state != GuiState::SelectDisk) { ImGui::SameLine(ImGui::GetContentRegionMax().x - ImGui::CalcTextSize("Settings").x - ImGui::GetStyle().FramePadding.x * 2.0f); diff --git a/core/rend/gui.h b/core/rend/gui.h index bbaf7c741..df6537408 100644 --- a/core/rend/gui.h +++ b/core/rend/gui.h @@ -29,6 +29,12 @@ void gui_open_onboarding(); void gui_term(); void gui_refresh_files(); void gui_cheats(); +void gui_keyboard_input(u16 wc); +void gui_keyboard_inputUTF8(const std::string& s); +void gui_set_mouse_position(int x, int y); +// 0: left, 1: right, 2: middle/wheel, 3: button 4 +void gui_set_mouse_button(int button, bool pressed); +void gui_set_mouse_wheel(float delta); void gui_set_insets(int left, int right, int top, int bottom); extern int screen_dpi; diff --git a/core/rend/gui_util.cpp b/core/rend/gui_util.cpp index 46fb152db..b6301b5df 100644 --- a/core/rend/gui_util.cpp +++ b/core/rend/gui_util.cpp @@ -810,8 +810,7 @@ void windowDragScroll() window->DragScrolling = false; // FIXME we should really move the mouse off-screen after a touch up and this wouldn't be necessary // the only problem is tool tips - mo_x_phy = -1; - mo_y_phy = -1; + gui_set_mouse_position(-1, -1); } } else diff --git a/core/rend/gui_util.h b/core/rend/gui_util.h index d186ee932..cfd44e8fa 100644 --- a/core/rend/gui_util.h +++ b/core/rend/gui_util.h @@ -25,6 +25,7 @@ #include "imgui/imgui_internal.h" #include "gles/imgui_impl_opengl3.h" #include "vulkan/vulkan_context.h" +#include "dx9/dxcontext.h" #include "gui.h" extern int screen_width, screen_height; @@ -37,9 +38,14 @@ void select_file_popup(const char *prompt, StringCallback callback, static inline void ImGui_impl_RenderDrawData(ImDrawData *draw_data) { #ifdef USE_VULKAN - if (!config::RendererType.isOpenGL()) + if (config::RendererType.isVulkan()) ImGui_ImplVulkan_RenderDrawData(draw_data); else +#endif +#ifdef _WIN32 + if (config::RendererType.isDirectX()) + theDXContext.EndImGuiFrame(); + else #endif ImGui_ImplOpenGL3_RenderDrawData(draw_data); } diff --git a/core/rend/mainui.cpp b/core/rend/mainui.cpp index b890dcbf8..f93763ac7 100644 --- a/core/rend/mainui.cpp +++ b/core/rend/mainui.cpp @@ -80,18 +80,23 @@ void mainui_loop() if (config::RendererType.isOpenGL()) theGLContext.Swap(); #ifdef USE_VULKAN - else + else if (config::RendererType.isVulkan()) VulkanContext::Instance()->Present(); +#endif +#ifdef _WIN32 + else if (config::RendererType.isDirectX()) + theDXContext.Present(); #endif } if (config::RendererType.pendingChange() || forceReinit) { - bool openGl = config::RendererType.isOpenGL(); + int api = config::RendererType.isOpenGL() ? 0 : config::RendererType.isVulkan() ? 1 : 2; mainui_term(); config::RendererType.commit(); - if (openGl != config::RendererType.isOpenGL() || forceReinit) - // Switch between vulkan and opengl (or full reinit) + int newApi = config::RendererType.isOpenGL() ? 0 : config::RendererType.isVulkan() ? 1 : 2; + if (newApi != api || forceReinit) + // Switch between vulkan/opengl/directx (or full reinit) SwitchRenderApi(); mainui_init(); forceReinit = false; diff --git a/core/rend/transform_matrix.h b/core/rend/transform_matrix.h index 676403d15..debee942f 100644 --- a/core/rend/transform_matrix.h +++ b/core/rend/transform_matrix.h @@ -28,7 +28,17 @@ extern int screen_width, screen_height; -template +// Dreamcast: +// +Y is down +// Open GL: +// +Y is up in clip, NDC and framebuffer coordinates +// Vulkan: +// +Y is down in clip, NDC and framebuffer coordinates +// DirectX9: +// +Y is up in clip and NDC coordinates, but down in framebuffer coordinates +// Y must also be flipped for render-to-texture so that the top of the texture comes first +enum CoordSystem { COORD_OPENGL, COORD_VULKAN, COORD_DIRECTX }; +template class TransformMatrix { public: @@ -68,6 +78,10 @@ public: void CalcMatrices(const rend_context *renderingContext, int width = 0, int height = 0) { + constexpr int screenFlipY = System == COORD_OPENGL || System == COORD_DIRECTX ? -1 : 1; + constexpr int rttFlipY = System == COORD_DIRECTX ? -1 : 1; + constexpr int framebufferFlipY = System == COORD_DIRECTX ? -1 : 1; + renderViewport = { width == 0 ? screen_width : width, height == 0 ? screen_height : height }; this->renderingContext = renderingContext; @@ -77,8 +91,8 @@ public: { dcViewport.x = renderingContext->fb_X_CLIP.max - renderingContext->fb_X_CLIP.min + 1; dcViewport.y = renderingContext->fb_Y_CLIP.max - renderingContext->fb_Y_CLIP.min + 1; - normalMatrix = glm::translate(glm::vec3(-1, -1, 0)) - * glm::scale(glm::vec3(2.0f / dcViewport.x, 2.0f / dcViewport.y, 1.f)); + normalMatrix = glm::translate(glm::vec3(-1, -rttFlipY, 0)) + * glm::scale(glm::vec3(2.0f / dcViewport.x, 2.0f / dcViewport.y * rttFlipY, 1.f)); scissorMatrix = normalMatrix; sidebarWidth = 0; } @@ -142,7 +156,7 @@ public: float dc2s_scale_h = renderViewport.x / 640.0f; sidebarWidth = 0; - y_coef = 2.0f / (renderViewport.y / dc2s_scale_h * scale_y) * screen_stretching * (invertY ? -1 : 1); + y_coef = 2.0f / (renderViewport.y / dc2s_scale_h * scale_y) * screen_stretching * screenFlipY; x_coef = 2.0f / dcViewport.x; } else @@ -151,9 +165,9 @@ public: sidebarWidth = (renderViewport.x - dc2s_scale_h * 640.0f * screen_stretching) / 2; x_coef = 2.0f / (renderViewport.x / dc2s_scale_h * scale_x) * screen_stretching; - y_coef = 2.0f / dcViewport.y * (invertY ? -1 : 1); + y_coef = 2.0f / dcViewport.y * screenFlipY; } - trans_rot = glm::translate(glm::vec3(-1 + 2 * sidebarWidth / renderViewport.x, invertY ? 1 : -1, 0)); + trans_rot = glm::translate(glm::vec3(-1 + 2 * sidebarWidth / renderViewport.x, -screenFlipY, 0)); normalMatrix = trans_rot * glm::scale(glm::vec3(x_coef, y_coef, 1.f)) @@ -165,15 +179,15 @@ public: normalMatrix = glm::scale(glm::vec3(1, 1, 1 / config::ExtraDepthScale)) * normalMatrix; - glm::mat4 vp_trans = glm::translate(glm::vec3(1, 1, 0)); + glm::mat4 vp_trans = glm::translate(glm::vec3(1, framebufferFlipY, 0)); if (renderingContext->isRTT) { - vp_trans = glm::scale(glm::vec3(dcViewport.x / 2, dcViewport.y / 2, 1.f)) + vp_trans = glm::scale(glm::vec3(dcViewport.x / 2, dcViewport.y / 2 * framebufferFlipY, 1.f)) * vp_trans; } else { - vp_trans = glm::scale(glm::vec3(renderViewport.x / 2, renderViewport.y / 2, 1.f)) + vp_trans = glm::scale(glm::vec3(renderViewport.x / 2, renderViewport.y / 2 * framebufferFlipY, 1.f)) * vp_trans; } viewportMatrix = vp_trans * normalMatrix; diff --git a/core/rend/vulkan/drawer.cpp b/core/rend/vulkan/drawer.cpp index 5fad3a28e..f5fd63dfd 100644 --- a/core/rend/vulkan/drawer.cpp +++ b/core/rend/vulkan/drawer.cpp @@ -144,14 +144,14 @@ void Drawer::DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool sor float trilinearAlpha = 1.f; if (poly.tsp.FilterMode > 1 && poly.pcw.Texture && listType != ListType_Punch_Through && poly.tcw.MipMapped == 1) { - trilinearAlpha = 0.25 * (poly.tsp.MipMapD & 0x3); + trilinearAlpha = 0.25f * (poly.tsp.MipMapD & 0x3); if (poly.tsp.FilterMode == 2) // Trilinear pass A - trilinearAlpha = 1.0 - trilinearAlpha; + trilinearAlpha = 1.f - trilinearAlpha; } - bool palette = BaseTextureCacheData::IsGpuHandledPaletted(poly.tsp, poly.tcw); + bool gpuPalette = poly.texture != nullptr ? poly.texture->gpuPalette : false; float palette_index = 0.f; - if (palette) + if (gpuPalette) { if (poly.tcw.PixelFmt == PixelPal4) palette_index = float(poly.tcw.PalSelect << 4) / 1023.f; @@ -159,7 +159,7 @@ void Drawer::DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool sor palette_index = float((poly.tcw.PalSelect >> 4) << 8) / 1023.f; } - if (tileClip == TileClipping::Inside || trilinearAlpha != 1.f || palette) + if (tileClip == TileClipping::Inside || trilinearAlpha != 1.f || gpuPalette) { std::array pushConstants = { (float)scissorRect.offset.x, @@ -173,12 +173,12 @@ void Drawer::DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool sor } if (poly.pcw.Texture) - GetCurrentDescSet().SetTexture(poly.texid, poly.tsp); + GetCurrentDescSet().SetTexture((Texture *)poly.texture, poly.tsp); - vk::Pipeline pipeline = pipelineManager->GetPipeline(listType, sortTriangles, poly); + vk::Pipeline pipeline = pipelineManager->GetPipeline(listType, sortTriangles, poly, gpuPalette); cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); if (poly.pcw.Texture) - GetCurrentDescSet().BindPerPolyDescriptorSets(cmdBuffer, poly.texid, poly.tsp); + GetCurrentDescSet().BindPerPolyDescriptorSets(cmdBuffer, (Texture *)poly.texture, poly.tsp); cmdBuffer.drawIndexed(count, 1, first, 0, 0); } @@ -300,7 +300,7 @@ void Drawer::UploadMainBuffer(const VertexShaderUniforms& vertexUniforms, const chunks.push_back(&fragmentUniforms); chunkSizes.push_back(sizeof(fragmentUniforms)); - u32 totalSize = offsets.fragmentUniformOffset + sizeof(FragmentShaderUniforms); + u32 totalSize = (u32)(offsets.fragmentUniformOffset + sizeof(FragmentShaderUniforms)); BufferData *buffer = GetMainBuffer(totalSize); buffer->upload(chunks.size(), &chunkSizes[0], &chunks[0]); @@ -324,7 +324,7 @@ bool Drawer::Draw(const Texture *fogTexture, const Texture *paletteTexture) UploadMainBuffer(vtxUniforms, fragUniforms); // Update per-frame descriptor set and bind it - GetCurrentDescSet().UpdateUniforms(GetMainBuffer(0)->buffer.get(), offsets.vertexUniformOffset, offsets.fragmentUniformOffset, + GetCurrentDescSet().UpdateUniforms(GetMainBuffer(0)->buffer.get(), (u32)offsets.vertexUniformOffset, (u32)offsets.fragmentUniformOffset, fogTexture->GetImageView(), paletteTexture->GetImageView()); GetCurrentDescSet().BindPerFrameDescriptorSets(cmdBuffer); @@ -683,7 +683,7 @@ vk::CommandBuffer ScreenDrawer::BeginRenderPass() const vk::ClearValue clear_colors[] = { vk::ClearColorValue(std::array { 0.f, 0.f, 0.f, 1.f }), vk::ClearDepthStencilValue { 0.f, 0 } }; commandBuffer.beginRenderPass(vk::RenderPassBeginInfo(renderPass, *framebuffers[GetCurrentImage()], vk::Rect2D( { 0, 0 }, viewport), 2, clear_colors), vk::SubpassContents::eInline); - commandBuffer.setViewport(0, vk::Viewport(0.0f, 0.0f, viewport.width, viewport.height, 1.0f, 0.0f)); + commandBuffer.setViewport(0, vk::Viewport(0.0f, 0.0f, (float)viewport.width, (float)viewport.height, 1.0f, 0.0f)); matrices.CalcMatrices(&pvrrc, viewport.width, viewport.height); diff --git a/core/rend/vulkan/drawer.h b/core/rend/vulkan/drawer.h index f2e841ed4..a09f3ba06 100644 --- a/core/rend/vulkan/drawer.h +++ b/core/rend/vulkan/drawer.h @@ -96,7 +96,7 @@ protected: vk::Rect2D baseScissor; vk::Rect2D currentScissor; - TransformMatrix matrices; + TransformMatrix matrices; CommandPool *commandPool = nullptr; }; diff --git a/core/rend/vulkan/oit/oit_drawer.cpp b/core/rend/vulkan/oit/oit_drawer.cpp index 015dd11d8..63f0626d2 100644 --- a/core/rend/vulkan/oit/oit_drawer.cpp +++ b/core/rend/vulkan/oit/oit_drawer.cpp @@ -36,23 +36,21 @@ void OITDrawer::DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool float trilinearAlpha = 1.f; if (poly.tsp.FilterMode > 1 && poly.pcw.Texture && listType != ListType_Punch_Through && poly.tcw.MipMapped == 1) { - trilinearAlpha = 0.25 * (poly.tsp.MipMapD & 0x3); + trilinearAlpha = 0.25f * (poly.tsp.MipMapD & 0x3); if (poly.tsp.FilterMode == 2) // Trilinear pass A - trilinearAlpha = 1.0 - trilinearAlpha; + trilinearAlpha = 1.f - trilinearAlpha; } bool twoVolumes = poly.tsp1.full != (u32)-1 || poly.tcw1.full != (u32)-1; - bool palette = BaseTextureCacheData::IsGpuHandledPaletted(poly.tsp, poly.tcw); + bool gpuPalette = poly.texture != nullptr ? poly.texture->gpuPalette : false; + float palette_index = 0.f; - if (palette) - { - if (poly.tcw.PixelFmt == PixelPal4) - palette_index = float(poly.tcw.PalSelect << 4) / 1023.f; - else - palette_index = float((poly.tcw.PalSelect >> 4) << 8) / 1023.f; - } + if (poly.tcw.PixelFmt == PixelPal4) + palette_index = float(poly.tcw.PalSelect << 4) / 1023.f; + else + palette_index = float((poly.tcw.PalSelect >> 4) << 8) / 1023.f; OITDescriptorSets::PushConstants pushConstants = { { @@ -82,12 +80,12 @@ void OITDrawer::DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool bool needTexture = poly.pcw.Texture; if (needTexture) - GetCurrentDescSet().SetTexture(poly.texid, poly.tsp, poly.texid1, poly.tsp1); + GetCurrentDescSet().SetTexture((Texture *)poly.texture, poly.tsp, (Texture *)poly.texture1, poly.tsp1); - vk::Pipeline pipeline = pipelineManager->GetPipeline(listType, autosort, poly, pass); + vk::Pipeline pipeline = pipelineManager->GetPipeline(listType, autosort, poly, pass, gpuPalette); cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline); if (needTexture) - GetCurrentDescSet().BindPerPolyDescriptorSets(cmdBuffer, poly.texid, poly.tsp, poly.texid1, poly.tsp1); + GetCurrentDescSet().BindPerPolyDescriptorSets(cmdBuffer, (Texture *)poly.texture, poly.tsp, (Texture *)poly.texture1, poly.tsp1); cmdBuffer.drawIndexed(count, 1, first, 0, 0); } @@ -240,8 +238,8 @@ void OITDrawer::UploadMainBuffer(const OITDescriptorSets::VertexShaderUniforms& } offsets.polyParamsSize = trPolyParams.size() * 4; chunks.push_back(trPolyParams.data()); - chunkSizes.push_back(offsets.polyParamsSize); - u32 totalSize = offsets.polyParamsOffset + offsets.polyParamsSize; + chunkSizes.push_back((u32)offsets.polyParamsSize); + u32 totalSize = (u32)(offsets.polyParamsOffset + offsets.polyParamsSize); BufferData *buffer = GetMainBuffer(totalSize); buffer->upload(chunks.size(), &chunkSizes[0], &chunks[0]); @@ -278,9 +276,9 @@ bool OITDrawer::Draw(const Texture *fogTexture, const Texture *paletteTexture) // Update per-frame descriptor set and bind it const vk::Buffer mainBuffer = GetMainBuffer(0)->buffer.get(); - GetCurrentDescSet().UpdateUniforms(mainBuffer, offsets.vertexUniformOffset, offsets.fragmentUniformOffset, - fogTexture->GetImageView(), offsets.polyParamsOffset, - offsets.polyParamsSize, depthAttachments[0]->GetStencilView(), + GetCurrentDescSet().UpdateUniforms(mainBuffer, (u32)offsets.vertexUniformOffset, (u32)offsets.fragmentUniformOffset, + fogTexture->GetImageView(), (u32)offsets.polyParamsOffset, + (u32)offsets.polyParamsSize, depthAttachments[0]->GetStencilView(), depthAttachments[0]->GetImageView(), paletteTexture->GetImageView()); GetCurrentDescSet().BindPerFrameDescriptorSets(cmdBuffer); GetCurrentDescSet().UpdateColorInputDescSet(0, colorAttachments[0]->GetImageView()); @@ -691,7 +689,7 @@ vk::CommandBuffer OITScreenDrawer::NewFrame() SetBaseScissor(viewport.extent); commandBuffer.setScissor(0, baseScissor); - commandBuffer.setViewport(0, vk::Viewport(viewport.offset.x, viewport.offset.y, viewport.extent.width, viewport.extent.height, 1.0f, 0.0f)); + commandBuffer.setViewport(0, vk::Viewport((float)viewport.offset.x, (float)viewport.offset.y, (float)viewport.extent.width, (float)viewport.extent.height, 1.0f, 0.0f)); currentCommandBuffer = commandBuffer; return commandBuffer; diff --git a/core/rend/vulkan/oit/oit_pipeline.cpp b/core/rend/vulkan/oit/oit_pipeline.cpp index f64f537c3..dce444355 100644 --- a/core/rend/vulkan/oit/oit_pipeline.cpp +++ b/core/rend/vulkan/oit/oit_pipeline.cpp @@ -21,7 +21,7 @@ #include "oit_pipeline.h" #include "../quad.h" -void OITPipelineManager::CreatePipeline(u32 listType, bool autosort, const PolyParam& pp, Pass pass) +void OITPipelineManager::CreatePipeline(u32 listType, bool autosort, const PolyParam& pp, Pass pass, bool gpuPalette) { vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo(); @@ -154,7 +154,7 @@ void OITPipelineManager::CreatePipeline(u32 listType, bool autosort, const PolyP params.useAlpha = pp.tsp.UseAlpha; params.pass = pass; params.twoVolume = pp.tsp1.full != (u32)-1 || pp.tcw1.full != (u32)-1; - params.palette = BaseTextureCacheData::IsGpuHandledPaletted(pp.tsp, pp.tcw); + params.palette = gpuPalette; vk::ShaderModule fragment_module = shaderManager->GetFragmentShader(params); vk::PipelineShaderStageCreateInfo stages[] = { @@ -180,7 +180,7 @@ void OITPipelineManager::CreatePipeline(u32 listType, bool autosort, const PolyP pass == Pass::Depth ? (listType == ListType_Translucent ? 2 : 0) : 1 // subpass ); - pipelines[hash(listType, autosort, &pp, pass)] = GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(), + pipelines[hash(listType, autosort, &pp, pass, gpuPalette)] = GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(), graphicsPipelineCreateInfo); } diff --git a/core/rend/vulkan/oit/oit_pipeline.h b/core/rend/vulkan/oit/oit_pipeline.h index 7296fc75d..92b607ef8 100644 --- a/core/rend/vulkan/oit/oit_pipeline.h +++ b/core/rend/vulkan/oit/oit_pipeline.h @@ -155,10 +155,10 @@ public: GetContext()->GetDevice().updateDescriptorSets(1, &writeDescriptorSet, 0, nullptr); } - void SetTexture(u64 textureId0, TSP tsp0, u64 textureId1, TSP tsp1) + void SetTexture(Texture *texture0, TSP tsp0, Texture *texture1, TSP tsp1) { - auto index = std::make_tuple(textureId0, tsp0.full & SamplerManager::TSP_Mask, - textureId1, tsp1.full & SamplerManager::TSP_Mask); + auto index = std::make_tuple(texture0, tsp0.full & SamplerManager::TSP_Mask, + texture1, tsp1.full & SamplerManager::TSP_Mask); if (perPolyDescSetsInFlight.find(index) != perPolyDescSetsInFlight.end()) return; @@ -168,15 +168,13 @@ public: perPolyDescSets = GetContext()->GetDevice().allocateDescriptorSetsUnique( vk::DescriptorSetAllocateInfo(GetContext()->GetDescriptorPool(), layouts.size(), &layouts[0])); } - Texture *texture = reinterpret_cast(textureId0); - vk::DescriptorImageInfo imageInfo0(samplerManager->GetSampler(tsp0), texture->GetReadOnlyImageView(), vk::ImageLayout::eShaderReadOnlyOptimal); + vk::DescriptorImageInfo imageInfo0(samplerManager->GetSampler(tsp0), texture0->GetReadOnlyImageView(), vk::ImageLayout::eShaderReadOnlyOptimal); std::vector writeDescriptorSets; writeDescriptorSets.emplace_back(*perPolyDescSets.back(), 0, 0, 1, vk::DescriptorType::eCombinedImageSampler, &imageInfo0, nullptr, nullptr); - if (textureId1 != (u64)-1) + if (texture1 != nullptr) { - Texture *texture1 = reinterpret_cast(textureId1); vk::DescriptorImageInfo imageInfo1(samplerManager->GetSampler(tsp1), texture1->GetReadOnlyImageView(), vk::ImageLayout::eShaderReadOnlyOptimal); writeDescriptorSets.emplace_back(*perPolyDescSets.back(), 1, 0, 1, vk::DescriptorType::eCombinedImageSampler, &imageInfo1, nullptr, nullptr); @@ -196,9 +194,9 @@ public: cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout, 2, 1, &colorInputDescSets[index].get(), 0, nullptr); } - void BindPerPolyDescriptorSets(vk::CommandBuffer cmdBuffer, u64 textureId0, TSP tsp0, u64 textureId1, TSP tsp1) + void BindPerPolyDescriptorSets(vk::CommandBuffer cmdBuffer, Texture *texture0, TSP tsp0, Texture *texture1, TSP tsp1) { - auto index = std::make_tuple(textureId0, tsp0.full & SamplerManager::TSP_Mask, textureId1, tsp1.full & SamplerManager::TSP_Mask); + auto index = std::make_tuple(texture0, tsp0.full & SamplerManager::TSP_Mask, texture1, tsp1.full & SamplerManager::TSP_Mask); cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout, 1, 1, &perPolyDescSetsInFlight[index].get(), 0, nullptr); } @@ -225,7 +223,7 @@ private: std::vector perFrameDescSetsInFlight; std::array colorInputDescSets; std::vector perPolyDescSets; - std::map, vk::UniqueDescriptorSet> perPolyDescSetsInFlight; + std::map, vk::UniqueDescriptorSet> perPolyDescSetsInFlight; SamplerManager* samplerManager; }; @@ -278,14 +276,14 @@ public: modVolPipelines.clear(); } - vk::Pipeline GetPipeline(u32 listType, bool autosort, const PolyParam& pp, Pass pass) + vk::Pipeline GetPipeline(u32 listType, bool autosort, const PolyParam& pp, Pass pass, bool gpuPalette) { - u32 pipehash = hash(listType, autosort, &pp, pass); + u32 pipehash = hash(listType, autosort, &pp, pass, gpuPalette); const auto &pipeline = pipelines.find(pipehash); if (pipeline != pipelines.end()) return pipeline->second.get(); - CreatePipeline(listType, autosort, pp, pass); + CreatePipeline(listType, autosort, pp, pass, gpuPalette); return *pipelines[pipehash]; } @@ -333,7 +331,7 @@ private: void CreateModVolPipeline(ModVolMode mode, int cullMode); void CreateTrModVolPipeline(ModVolMode mode, int cullMode); - u32 hash(u32 listType, bool autosort, const PolyParam *pp, Pass pass) const + u32 hash(u32 listType, bool autosort, const PolyParam *pp, Pass pass, bool gpuPalette) const { u32 hash = pp->pcw.Gouraud | (pp->pcw.Offset << 1) | (pp->pcw.Texture << 2) | (pp->pcw.Shadow << 3) | (((pp->tileclip >> 28) == 3) << 4); @@ -351,7 +349,7 @@ private: | (pp->tsp.SrcInstr << 14) | (pp->tsp.DstInstr << 17); } hash |= (pp->isp.ZWriteDis << 20) | (pp->isp.CullMode << 21) | ((autosort ? 6 : pp->isp.DepthMode) << 23); - hash |= ((u32)BaseTextureCacheData::IsGpuHandledPaletted(pp->tsp, pp->tcw) << 26) | ((u32)pass << 27); + hash |= ((u32)gpuPalette << 26) | ((u32)pass << 27); return hash; } @@ -389,7 +387,7 @@ private: full ? vertexInputAttributeDescriptions : vertexInputLightAttributeDescriptions); } - void CreatePipeline(u32 listType, bool autosort, const PolyParam& pp, Pass pass); + void CreatePipeline(u32 listType, bool autosort, const PolyParam& pp, Pass pass, bool gpuPalette); void CreateFinalPipeline(); void CreateClearPipeline(); diff --git a/core/rend/vulkan/pipeline.cpp b/core/rend/vulkan/pipeline.cpp index e61914eec..3dcd4f1de 100644 --- a/core/rend/vulkan/pipeline.cpp +++ b/core/rend/vulkan/pipeline.cpp @@ -168,7 +168,7 @@ void PipelineManager::CreateModVolPipeline(ModVolMode mode, int cullMode) graphicsPipelineCreateInfo); } -void PipelineManager::CreatePipeline(u32 listType, bool sortTriangles, const PolyParam& pp) +void PipelineManager::CreatePipeline(u32 listType, bool sortTriangles, const PolyParam& pp, bool gpuPalette) { vk::PipelineVertexInputStateCreateInfo pipelineVertexInputStateCreateInfo = GetMainVertexInputStateCreateInfo(); @@ -303,7 +303,7 @@ void PipelineManager::CreatePipeline(u32 listType, bool sortTriangles, const Pol params.texture = pp.pcw.Texture; params.trilinear = pp.pcw.Texture && pp.tsp.FilterMode > 1 && listType != ListType_Punch_Through && pp.tcw.MipMapped == 1; params.useAlpha = pp.tsp.UseAlpha; - params.palette = BaseTextureCacheData::IsGpuHandledPaletted(pp.tsp, pp.tcw); + params.palette = gpuPalette; vk::ShaderModule fragment_module = shaderManager->GetFragmentShader(params); vk::PipelineShaderStageCreateInfo stages[] = { @@ -328,7 +328,7 @@ void PipelineManager::CreatePipeline(u32 listType, bool sortTriangles, const Pol renderPass // renderPass ); - pipelines[hash(listType, sortTriangles, &pp)] = GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(), + pipelines[hash(listType, sortTriangles, &pp, gpuPalette)] = GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(), graphicsPipelineCreateInfo); } diff --git a/core/rend/vulkan/pipeline.h b/core/rend/vulkan/pipeline.h index 96e9914ab..0b64dae87 100644 --- a/core/rend/vulkan/pipeline.h +++ b/core/rend/vulkan/pipeline.h @@ -85,10 +85,10 @@ public: GetContext()->GetDevice().updateDescriptorSets(writeDescriptorSets, nullptr); } - void SetTexture(u64 textureId, TSP tsp) + void SetTexture(Texture *texture, TSP tsp) { auto& inFlight = perPolyDescSetsInFlight; - std::pair index = std::make_pair(textureId, tsp.full & SamplerManager::TSP_Mask); + std::pair index = std::make_pair(texture, tsp.full & SamplerManager::TSP_Mask); if (inFlight.find(index) != inFlight.end()) return; @@ -98,7 +98,6 @@ public: perPolyDescSets = GetContext()->GetDevice().allocateDescriptorSetsUnique( vk::DescriptorSetAllocateInfo(GetContext()->GetDescriptorPool(), layouts.size(), &layouts[0])); } - Texture *texture = reinterpret_cast(textureId); vk::DescriptorImageInfo imageInfo(samplerManager->GetSampler(tsp), texture->GetReadOnlyImageView(), vk::ImageLayout::eShaderReadOnlyOptimal); std::vector writeDescriptorSets; @@ -114,10 +113,10 @@ public: cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout, 0, 1, &perFrameDescSetsInFlight.back().get(), 0, nullptr); } - void BindPerPolyDescriptorSets(vk::CommandBuffer cmdBuffer, u64 textureId, TSP tsp) + void BindPerPolyDescriptorSets(vk::CommandBuffer cmdBuffer, Texture *texture, TSP tsp) { cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout, 1, 1, - &perPolyDescSetsInFlight[std::make_pair(textureId, tsp.full & SamplerManager::TSP_Mask)].get(), 0, nullptr); + &perPolyDescSetsInFlight[std::make_pair(texture, tsp.full & SamplerManager::TSP_Mask)].get(), 0, nullptr); } void Reset() @@ -140,7 +139,7 @@ private: std::vector perFrameDescSets; std::vector perFrameDescSetsInFlight; std::vector perPolyDescSets; - std::map, vk::UniqueDescriptorSet> perPolyDescSetsInFlight; + std::map, vk::UniqueDescriptorSet> perPolyDescSetsInFlight; SamplerManager* samplerManager = nullptr; }; @@ -183,14 +182,14 @@ public: } } - vk::Pipeline GetPipeline(u32 listType, bool sortTriangles, const PolyParam& pp) + vk::Pipeline GetPipeline(u32 listType, bool sortTriangles, const PolyParam& pp, bool gpuPalette) { - u32 pipehash = hash(listType, sortTriangles, &pp); + u32 pipehash = hash(listType, sortTriangles, &pp, gpuPalette); const auto &pipeline = pipelines.find(pipehash); if (pipeline != pipelines.end()) return pipeline->second.get(); - CreatePipeline(listType, sortTriangles, pp); + CreatePipeline(listType, sortTriangles, pp, gpuPalette); return *pipelines[pipehash]; } @@ -220,7 +219,7 @@ public: private: void CreateModVolPipeline(ModVolMode mode, int cullMode); - u32 hash(u32 listType, bool sortTriangles, const PolyParam *pp) const + u32 hash(u32 listType, bool sortTriangles, const PolyParam *pp, bool gpuPalette) const { u32 hash = pp->pcw.Gouraud | (pp->pcw.Offset << 1) | (pp->pcw.Texture << 2) | (pp->pcw.Shadow << 3) | (((pp->tileclip >> 28) == 3) << 4); @@ -230,7 +229,7 @@ private: | (pp->tsp.ColorClamp << 11) | ((config::Fog ? pp->tsp.FogCtrl : 2) << 12) | (pp->tsp.SrcInstr << 14) | (pp->tsp.DstInstr << 17); hash |= (pp->isp.ZWriteDis << 20) | (pp->isp.CullMode << 21) | (pp->isp.DepthMode << 23); - hash |= ((u32)sortTriangles << 26) | ((u32)BaseTextureCacheData::IsGpuHandledPaletted(pp->tsp, pp->tcw) << 27); + hash |= ((u32)sortTriangles << 26) | ((u32)gpuPalette << 27); return hash; } @@ -265,7 +264,7 @@ private: full ? vertexInputAttributeDescriptions : vertexInputLightAttributeDescriptions); } - void CreatePipeline(u32 listType, bool sortTriangles, const PolyParam& pp); + void CreatePipeline(u32 listType, bool sortTriangles, const PolyParam& pp, bool gpuPalette); std::map pipelines; std::map modVolPipelines; diff --git a/core/rend/vulkan/texture.h b/core/rend/vulkan/texture.h index 8fc875baa..45c18772f 100644 --- a/core/rend/vulkan/texture.h +++ b/core/rend/vulkan/texture.h @@ -137,6 +137,9 @@ private: class TextureCache final : public BaseTextureCache { public: + TextureCache() { + Texture::SetDirectXColorOrder(false); + } void SetCurrentIndex(int index) { if (currentIndex < inFlightTextures.size()) std::for_each(inFlightTextures[currentIndex].begin(), inFlightTextures[currentIndex].end(), diff --git a/core/rend/vulkan/vulkan_context.cpp b/core/rend/vulkan/vulkan_context.cpp index 2b7f948cb..a4c7c041b 100644 --- a/core/rend/vulkan/vulkan_context.cpp +++ b/core/rend/vulkan/vulkan_context.cpp @@ -982,10 +982,6 @@ void VulkanContext::Term() #endif #endif instance.reset(); -#ifdef _WIN32 - extern void DestroyMainWindow(); - DestroyMainWindow(); -#endif } void VulkanContext::DoSwapAutomation() diff --git a/core/rend/vulkan/vulkan_renderer.h b/core/rend/vulkan/vulkan_renderer.h index 7cc3891ad..684a0e542 100644 --- a/core/rend/vulkan/vulkan_renderer.h +++ b/core/rend/vulkan/vulkan_renderer.h @@ -78,7 +78,7 @@ public: framebufferTextures.clear(); } - u64 GetTexture(TSP tsp, TCW tcw) override + BaseTextureCacheData *GetTexture(TSP tsp, TCW tcw) override { Texture* tf = textureCache.getTextureCacheData(tsp, tcw); @@ -107,7 +107,7 @@ public: tf->SetCommandBuffer(nullptr); textureCache.SetInFlight(tf); - return tf->GetIntId(); + return tf; } bool Process(TA_context* ctx) override @@ -200,7 +200,7 @@ public: cmdBuffer.draw(4, 1, i, 0); if (clear_screen) GetContext()->EndFrame(); - } catch (const InvalidVulkanContext& err) { + } catch (const InvalidVulkanContext&) { } } @@ -283,8 +283,8 @@ protected: pp->tsp.SrcInstr = 1; pp->tsp1.full = (u32)-1; - pp->texid = (u64)reinterpret_cast(curTexture.get()); - pp->texid1 = (u64)-1; + pp->texture = curTexture.get(); + pp->texture1 = nullptr; pp->tileclip = 0; RenderPass *pass = ctx->rend.render_passes.Append(1); diff --git a/core/sdl/sdl.cpp b/core/sdl/sdl.cpp index e119be36e..8c5180aaa 100644 --- a/core/sdl/sdl.cpp +++ b/core/sdl/sdl.cpp @@ -15,6 +15,9 @@ #if !defined(_WIN32) && !defined(__APPLE__) #include "linux-dist/icon.h" #endif +#ifdef _WIN32 +#include "windows/rawinput.h" +#endif static SDL_Window* window = NULL; @@ -25,15 +28,14 @@ static SDL_Window* window = NULL; #endif #define WINDOW_HEIGHT 480 -static std::shared_ptr sdl_kb_gamepad; -static SDLKeyboardDevice* sdl_keyboard = NULL; +static std::shared_ptr sdl_mouse; +static std::shared_ptr sdl_keyboard; static bool window_fullscreen; static bool window_maximized; static int window_width = WINDOW_WIDTH; static int window_height = WINDOW_HEIGHT; static bool gameRunning; static bool mouseCaptured; -static std::map> mice; static void sdl_open_joystick(int index) { @@ -61,14 +63,23 @@ static void captureMouse(bool capture) return; if (!capture) { - SDL_SetRelativeMouseMode(SDL_FALSE); + if (!config::UseRawInput) + SDL_SetRelativeMouseMode(SDL_FALSE); + else + SDL_ShowCursor(SDL_ENABLE); SDL_SetWindowTitle(window, "Flycast"); mouseCaptured = false; } - else if (SDL_SetRelativeMouseMode(SDL_TRUE) == 0) + else { - SDL_SetWindowTitle(window, "Flycast - mouse capture"); - mouseCaptured = true; + if (config::UseRawInput + || SDL_SetRelativeMouseMode(SDL_TRUE) == 0) + { + if (config::UseRawInput) + SDL_ShowCursor(SDL_DISABLE); + SDL_SetWindowTitle(window, "Flycast - mouse capture"); + mouseCaptured = true; + } } } @@ -78,90 +89,58 @@ static void emuEventCallback(Event event) { case Event::Pause: gameRunning = false; - SDL_SetRelativeMouseMode(SDL_FALSE); + if (!config::UseRawInput) + SDL_SetRelativeMouseMode(SDL_FALSE); + SDL_ShowCursor(SDL_ENABLE); SDL_SetWindowTitle(window, "Flycast"); break; case Event::Resume: gameRunning = true; captureMouse(mouseCaptured); + if (window_fullscreen && !mouseCaptured) + SDL_ShowCursor(SDL_DISABLE); + break; default: break; } } -static void clearMice() +static void checkRawInput() { - for (const auto& pair : mice) - GamepadDevice::Unregister(pair.second); - mice.clear(); -} - -static void discoverMice() -{ - clearMice(); - - auto defaultMouse = std::make_shared(); - mice[0] = defaultMouse; - GamepadDevice::Register(defaultMouse); - #ifdef _WIN32 - u32 numDevices; - GetRawInputDeviceList(NULL, &numDevices, sizeof(RAWINPUTDEVICELIST)); - if (numDevices > 0) + if ((bool)config::UseRawInput != (bool)sdl_mouse) + return; + if (config::UseRawInput) { - RAWINPUTDEVICELIST *deviceList; - deviceList = new RAWINPUTDEVICELIST[numDevices]; - if (deviceList != nullptr) - { - GetRawInputDeviceList(deviceList, &numDevices, sizeof(RAWINPUTDEVICELIST)); - for (u32 i = 0; i < numDevices; ++i) - { - RAWINPUTDEVICELIST& device = deviceList[i]; - if (device.dwType == RIM_TYPEMOUSE) - { - // Get the device name - std::string name; - std::string uniqueId; - u32 size; - GetRawInputDeviceInfo(device.hDevice, RIDI_DEVICENAME, nullptr, &size); - if (size > 0) - { - std::vector deviceNameData(size); - u32 res = GetRawInputDeviceInfo(device.hDevice, RIDI_DEVICENAME, &deviceNameData[0], &size); - if (res != (u32)-1) - { - std::string deviceName(&deviceNameData[0], std::strlen(&deviceNameData[0])); - name = "Mouse " + deviceName; - uniqueId = "sdl_mouse_" + deviceName; - } - } - u32 handle = (u32)(uintptr_t)device.hDevice; - if (name.empty()) - name = "Mouse " + std::to_string(handle); - if (uniqueId.empty()) - uniqueId = "sdl_mouse_" + std::to_string(handle); - - auto ptr = std::make_shared(mice.size() >= 4 ? 3 : mice.size(), name, uniqueId, handle); - mice[handle] = ptr; - GamepadDevice::Register(ptr); - } - } - delete [] deviceList; - } + GamepadDevice::Unregister(sdl_keyboard); + sdl_keyboard = nullptr; + GamepadDevice::Unregister(sdl_mouse); + sdl_mouse = nullptr; + rawinput::init(); + } + else + { + rawinput::term(); + sdl_keyboard = std::make_shared(0); + GamepadDevice::Register(sdl_keyboard); + sdl_mouse = std::make_shared(); + GamepadDevice::Register(sdl_mouse); + } +#else + if (!sdl_keyboard) + { + sdl_keyboard = std::make_shared(0); + GamepadDevice::Register(sdl_keyboard); + } + if (!sdl_mouse) + { + sdl_mouse = std::make_shared(); + GamepadDevice::Register(sdl_mouse); } #endif } -static std::shared_ptr getMouse(u32 handle) -{ - auto it = mice.find(handle); - if (it != mice.end()) - return it->second; - else - return nullptr; -} - void input_sdl_init() { if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) @@ -195,70 +174,18 @@ void input_sdl_init() #if !defined(__APPLE__) SDL_SetRelativeMouseMode(SDL_FALSE); - sdl_keyboard = new SDLKeyboardDevice(0); - sdl_kb_gamepad = std::make_shared(0); - GamepadDevice::Register(sdl_kb_gamepad); - discoverMice(); - EventManager::listen(Event::Pause, emuEventCallback); EventManager::listen(Event::Resume, emuEventCallback); + + checkRawInput(); #endif } -inline void SDLMouse::detect_btn_input(input_detected_cb button_pressed) -{ - GamepadDevice::detect_btn_input(button_pressed); - if (rawHandle != 0) - { - auto defaultMouse = getMouse(0); - defaultMouse->detectedRawMouse = getMouse(rawHandle); - } -} - -inline void SDLMouse::cancel_detect_input() -{ - GamepadDevice::cancel_detect_input(); - if (rawHandle != 0) - { - auto defaultMouse = getMouse(0); - defaultMouse->detectedRawMouse = nullptr; - } -} - -inline void SDLMouse::setMouseAbsPos(int x, int y) { - if (maple_port() < 0) - return; - +inline void SDLMouse::setAbsPos(int x, int y) { int width, height; SDL_GetWindowSize(window, &width, &height); if (width != 0 && height != 0) - SetMousePosition(x, y, width, height, maple_port()); -} - -inline void SDLMouse::setMouseRelPos(int deltax, int deltay) { - if (maple_port() < 0) - return; - SetRelativeMousePosition(deltax, deltay, maple_port()); -} - -#define SET_FLAG(field, mask, expr) (field) = ((expr) ? ((field) & ~(mask)) : ((field) | (mask))) - -inline void SDLMouse::setMouseButton(u32 button, bool pressed) { - if (maple_port() < 0) - return; - - switch (button) - { - case SDL_BUTTON_LEFT: - SET_FLAG(mo_buttons[maple_port()], 1 << 2, pressed); - break; - case SDL_BUTTON_RIGHT: - SET_FLAG(mo_buttons[maple_port()], 1 << 1, pressed); - break; - case SDL_BUTTON_MIDDLE: - SET_FLAG(mo_buttons[maple_port()], 1 << 3, pressed); - break; - } + Mouse::setAbsPos(x, y, width, height); } void input_sdl_handle() @@ -277,39 +204,59 @@ void input_sdl_handle() case SDL_KEYDOWN: case SDL_KEYUP: + checkRawInput(); if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_RETURN && (event.key.keysym.mod & KMOD_ALT)) { if (window_fullscreen) + { SDL_SetWindowFullscreen(window, 0); + if (!gameRunning || !mouseCaptured) + SDL_ShowCursor(SDL_ENABLE); + } else + { SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP); + if (gameRunning) + SDL_ShowCursor(SDL_DISABLE); + } window_fullscreen = !window_fullscreen; } else if (event.type == SDL_KEYDOWN && (event.key.keysym.mod & KMOD_LALT) && (event.key.keysym.mod & KMOD_LCTRL)) { captureMouse(!mouseCaptured); } - else + else if (!config::UseRawInput) { - sdl_kb_gamepad->gamepad_btn_input(event.key.keysym.sym, event.type == SDL_KEYDOWN); sdl_keyboard->keyboard_input(event.key.keysym.scancode, event.type == SDL_KEYDOWN); } break; case SDL_TEXTINPUT: - for (int i = 0; event.text.text[i] != '\0'; i++) - sdl_keyboard->keyboard_character(event.text.text[i]); + gui_keyboard_inputUTF8(event.text.text); break; -#ifdef USE_VULKAN case SDL_WINDOWEVENT: if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED || event.window.event == SDL_WINDOWEVENT_RESTORED || event.window.event == SDL_WINDOWEVENT_MINIMIZED || event.window.event == SDL_WINDOWEVENT_MAXIMIZED) { +#ifdef USE_VULKAN theVulkanContext.SetResized(); +#endif +#ifdef _WIN32 + theDXContext.resize(); +#endif + } + else if (event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) + { + if (window_fullscreen && gameRunning) + SDL_ShowCursor(SDL_DISABLE); + } + else if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) + { + if (window_fullscreen) + SDL_ShowCursor(SDL_ENABLE); } break; -#endif #endif case SDL_JOYBUTTONDOWN: case SDL_JOYBUTTONUP: @@ -368,37 +315,66 @@ void input_sdl_handle() #if !defined(__APPLE__) case SDL_MOUSEMOTION: + gui_set_mouse_position(event.motion.x, event.motion.y); + checkRawInput(); + if (!config::UseRawInput) { - std::shared_ptr mouse = getMouse(event.motion.which); - if (mouse != nullptr) - { - if (mouseCaptured && gameRunning) - mouse->setMouseRelPos(event.motion.xrel, event.motion.yrel); - else - mouse->setMouseAbsPos(event.motion.x, event.motion.y); - mouse->setMouseButton(SDL_BUTTON_LEFT, event.motion.state & SDL_BUTTON_LMASK); - mouse->setMouseButton(SDL_BUTTON_RIGHT, event.motion.state & SDL_BUTTON_RMASK); - mouse->setMouseButton(SDL_BUTTON_MIDDLE, event.motion.state & SDL_BUTTON_MMASK); - } + if (mouseCaptured && gameRunning) + sdl_mouse->setRelPos(event.motion.xrel, event.motion.yrel); + else + sdl_mouse->setAbsPos(event.motion.x, event.motion.y); + sdl_mouse->setButton(Mouse::LEFT_BUTTON, event.motion.state & SDL_BUTTON_LMASK); + sdl_mouse->setButton(Mouse::RIGHT_BUTTON, event.motion.state & SDL_BUTTON_RMASK); + sdl_mouse->setButton(Mouse::MIDDLE_BUTTON, event.motion.state & SDL_BUTTON_MMASK); + sdl_mouse->setButton(Mouse::BUTTON_4, event.motion.state & SDL_BUTTON_X1MASK); + sdl_mouse->setButton(Mouse::BUTTON_5, event.motion.state & SDL_BUTTON_X2MASK); + } + else if (mouseCaptured && gameRunning) + { + int x, y; + SDL_GetWindowSize(window, &x, &y); + x /= 2; + y /= 2; + if (std::abs(x - event.motion.x) > 10 || std::abs(y - event.motion.y) > 10 ) + SDL_WarpMouseInWindow(window, x, y); } break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: + gui_set_mouse_position(event.button.x, event.button.y); + gui_set_mouse_button(event.button.button - 1, event.button.state == SDL_PRESSED); + checkRawInput(); + if (!config::UseRawInput) { - std::shared_ptr mouse = getMouse(event.button.which); - if (mouse != nullptr) - { - if (!mouseCaptured || !gameRunning) - mouse->setMouseAbsPos(event.button.x, event.button.y); - mouse->setMouseButton(event.button.button, event.button.state == SDL_PRESSED); - mouse->gamepad_btn_input(event.button.button, event.button.state == SDL_PRESSED); + if (!mouseCaptured || !gameRunning) + sdl_mouse->setAbsPos(event.button.x, event.button.y); + bool pressed = event.button.state == SDL_PRESSED; + switch (event.button.button) { + case SDL_BUTTON_LEFT: + sdl_mouse->setButton(Mouse::LEFT_BUTTON, pressed); + break; + case SDL_BUTTON_RIGHT: + sdl_mouse->setButton(Mouse::RIGHT_BUTTON, pressed); + break; + case SDL_BUTTON_MIDDLE: + sdl_mouse->setButton(Mouse::MIDDLE_BUTTON, pressed); + break; + case SDL_BUTTON_X1: + sdl_mouse->setButton(Mouse::BUTTON_4, pressed); + break; + case SDL_BUTTON_X2: + sdl_mouse->setButton(Mouse::BUTTON_5, pressed); + break; } } break; case SDL_MOUSEWHEEL: - mo_wheel_delta[0] -= event.wheel.y * 35; + gui_set_mouse_wheel(-event.wheel.y * 35); + checkRawInput(); + if (!config::UseRawInput) + sdl_mouse->setWheel(-event.wheel.y); break; #endif case SDL_JOYDEVICEADDED: @@ -432,6 +408,18 @@ static void get_window_state() } +#ifdef _WIN32 +#include + +HWND getNativeHwnd() +{ + SDL_SysWMinfo wmInfo; + SDL_VERSION(&wmInfo.version); + SDL_GetWindowWMInfo(window, &wmInfo); + return wmInfo.info.win.window; +} +#endif + bool sdl_recreate_window(u32 flags) { #ifdef _WIN32 @@ -470,7 +458,6 @@ bool sdl_recreate_window(u32 flags) get_window_state(); SDL_DestroyWindow(window); } - flags |= SDL_SWSURFACE; #if !defined(GLES) flags |= SDL_WINDOW_RESIZABLE; if (window_fullscreen) @@ -487,6 +474,8 @@ bool sdl_recreate_window(u32 flags) ERROR_LOG(COMMON, "Window creation failed: %s", SDL_GetError()); return false; } + screen_width = window_width * scaling; + screen_height = window_height * scaling; #if !defined(GLES) && !defined(_WIN32) // Set the window icon @@ -507,6 +496,9 @@ bool sdl_recreate_window(u32 flags) theVulkanContext.SetWindow(window, nullptr); #endif theGLContext.SetWindow(window); +#ifdef _WIN32 + theDXContext.setNativeWindow(getNativeHwnd()); +#endif return true; } @@ -534,16 +526,4 @@ void sdl_window_destroy() SDL_DestroyWindow(window); } -#ifdef _WIN32 -#include - -HWND sdl_get_native_hwnd() -{ - SDL_SysWMinfo wmInfo; - SDL_VERSION(&wmInfo.version); - SDL_GetWindowWMInfo(window, &wmInfo); - return wmInfo.info.win.window; -} -#endif - #endif // !defined(__APPLE__) diff --git a/core/sdl/sdl.h b/core/sdl/sdl.h index 0f1572f7a..852b34bcd 100644 --- a/core/sdl/sdl.h +++ b/core/sdl/sdl.h @@ -8,8 +8,3 @@ void sdl_window_create(); void sdl_window_set_text(const char* text); void sdl_window_destroy(); bool sdl_recreate_window(u32 flags); - -#ifdef _WIN32 -#include -HWND sdl_get_native_hwnd(); -#endif diff --git a/core/sdl/sdl_gamepad.h b/core/sdl/sdl_gamepad.h index 8d6befe3c..1ca8d26e6 100644 --- a/core/sdl/sdl_gamepad.h +++ b/core/sdl/sdl_gamepad.h @@ -110,10 +110,7 @@ public: _unique_id = "sdl_joystick_" + std::to_string(sdl_joystick_instance); INFO_LOG(INPUT, "SDL: Opened joystick %d on port %d: '%s' unique_id=%s", sdl_joystick_instance, maple_port, _name.c_str(), _unique_id.c_str()); - if (!find_mapping()) - input_mapper = std::make_shared(joystick_idx); - else - INFO_LOG(INPUT, "using custom mapping '%s'", input_mapper->name.c_str()); + loadMapping(); sdl_haptic = SDL_HapticOpenFromJoystick(sdl_joystick); if (SDL_HapticRumbleInit(sdl_haptic) != 0) { @@ -193,132 +190,16 @@ private: std::map> SDLGamepad::sdl_gamepads; -class KbInputMapping : public InputMapping +class SDLMouse : public Mouse { public: - KbInputMapping() - { - name = "SDL Keyboard"; - set_button(DC_BTN_A, SDLK_x); - set_button(DC_BTN_B, SDLK_c); - set_button(DC_BTN_X, SDLK_s); - set_button(DC_BTN_Y, SDLK_d); - set_button(DC_DPAD_UP, SDLK_UP); - set_button(DC_DPAD_DOWN, SDLK_DOWN); - set_button(DC_DPAD_LEFT, SDLK_LEFT); - set_button(DC_DPAD_RIGHT, SDLK_RIGHT); - set_button(DC_BTN_START, SDLK_RETURN); - set_button(EMU_BTN_TRIGGER_LEFT, SDLK_f); - set_button(EMU_BTN_TRIGGER_RIGHT, SDLK_v); - set_button(EMU_BTN_MENU, SDLK_TAB); - set_button(EMU_BTN_FFORWARD, SDLK_SPACE); - - dirty = false; - } -}; - -class SDLKbGamepadDevice : public GamepadDevice -{ -public: - SDLKbGamepadDevice(int maple_port) : GamepadDevice(maple_port, "SDL") - { - _name = "Keyboard"; - _unique_id = "sdl_keyboard"; - if (!find_mapping()) - input_mapper = std::make_shared(); - } - - const char *get_button_name(u32 code) override - { - const char *name = SDL_GetKeyName((SDL_Keycode)code); - if (name[0] == 0) - return nullptr; - return name; - } -}; - -class MouseInputMapping : public InputMapping -{ -public: - MouseInputMapping() - { - name = "SDL Mouse"; - set_button(DC_BTN_A, SDL_BUTTON_LEFT); - set_button(DC_BTN_B, SDL_BUTTON_RIGHT); - set_button(DC_BTN_START, SDL_BUTTON_MIDDLE); - - dirty = false; - } -}; - -class SDLMouse : public GamepadDevice -{ -public: - SDLMouse() : GamepadDevice(0, "SDL") + SDLMouse() : Mouse("SDL") { this->_name = "Default Mouse"; this->_unique_id = "sdl_mouse"; - if (!find_mapping()) - input_mapper = std::make_shared(); - } - SDLMouse(int maple_port, const std::string& name, const std::string& uniqueId, u32 handle) - : GamepadDevice(maple_port, "RAW") - { - this->_name = name; - this->_unique_id = uniqueId; - std::replace(this->_unique_id.begin(), this->_unique_id.end(), '=', '_'); - std::replace(this->_unique_id.begin(), this->_unique_id.end(), '[', '_'); - std::replace(this->_unique_id.begin(), this->_unique_id.end(), ']', '_'); - - this->rawHandle = handle; - if (!find_mapping()) - input_mapper = std::make_shared(); + loadMapping(); } - bool gamepad_btn_input(u32 code, bool pressed) override - { - if (!is_detecting_input() && detectedRawMouse != nullptr) - { - bool handled = detectedRawMouse->gamepad_btn_input(code, pressed); - if (!detectedRawMouse->is_detecting_input()) - detectedRawMouse = nullptr; - return handled; - } - if (gui_is_open() && !is_detecting_input()) - // Don't register mouse clicks as gamepad presses when gui is open - // This makes the gamepad presses to be handled first and the mouse position to be ignored - // TODO Make this generic - return false; - else - return GamepadDevice::gamepad_btn_input(code, pressed); - } - - const char *get_button_name(u32 code) override - { - switch(code) - { - case SDL_BUTTON_LEFT: - return "Left Button"; - case SDL_BUTTON_RIGHT: - return "Right Button"; - case SDL_BUTTON_MIDDLE: - return "Middle Button"; - case SDL_BUTTON_X1: - return "X1 Button"; - case SDL_BUTTON_X2: - return "X2 Button"; - default: - return nullptr; - } - } - - void setMouseAbsPos(int x, int y); - void setMouseRelPos(int deltax, int deltay); - void setMouseButton(u32 button, bool pressed); - void detect_btn_input(input_detected_cb button_pressed) override; - void cancel_detect_input() override; - -private: - u32 rawHandle = 0; - std::shared_ptr detectedRawMouse; + void setAbsPos(int x, int y); }; + diff --git a/core/sdl/sdl_keyboard.h b/core/sdl/sdl_keyboard.h index f63e270dc..ff38ebdfa 100644 --- a/core/sdl/sdl_keyboard.h +++ b/core/sdl/sdl_keyboard.h @@ -5,9 +5,46 @@ class SDLKeyboardDevice : public KeyboardDeviceTemplate { public: - SDLKeyboardDevice(int maple_port) : KeyboardDeviceTemplate(maple_port) {} - ~SDLKeyboardDevice() override = default; - const char* name() override { return "SDL Keyboard"; } + SDLKeyboardDevice(int maple_port) : KeyboardDeviceTemplate(maple_port, "SDL") { + _unique_id = "sdl_keyboard"; + if (find_mapping()) + { + if (input_mapper->version == 1) + { + // Convert keycodes to scancode + SDL_Scancode scancodes[4][26] {}; + for (int i = 0; i < 26; i++) + { + DreamcastKey key = (DreamcastKey)(1 << i); + for (int port = 0; port < 4; port++) + { + SDL_Keycode keycode = (SDL_Keycode)input_mapper->get_button_code(port, key); + if ((int)keycode != -1) + scancodes[port][i] = SDL_GetScancodeFromKey(keycode); + } + } + for (int i = 0; i < 26; i++) + { + DreamcastKey key = (DreamcastKey)(1 << i); + for (int port = 0; port < 4; port++) + if (scancodes[port][i] != 0) + input_mapper->set_button(port, key, (u32)scancodes[port][i]); + } + input_mapper->version = 2; + save_mapping(); + } + } + else + input_mapper = getDefaultMapping(); + } + + const char *get_button_name(u32 code) override + { + const char *name = SDL_GetKeyName(SDL_GetKeyFromScancode((SDL_Scancode)code)); + if (name[0] == 0) + return nullptr; + return name; + } protected: u8 convert_keycode(SDL_Scancode scancode) override diff --git a/core/types.h b/core/types.h index 3cf9ccedc..d61698796 100644 --- a/core/types.h +++ b/core/types.h @@ -280,7 +280,8 @@ enum class RenderType { OpenGL = 0, OpenGL_OIT = 3, Vulkan = 4, - Vulkan_OIT = 5 + Vulkan_OIT = 5, + DirectX9 = 1, }; enum class KeyboardLayout { @@ -360,8 +361,6 @@ s32 libPvr_Init(); void libPvr_Reset(bool hard); void libPvr_Term(); -void* libPvr_GetRenderTarget(); - // 0x00600000 - 0x006007FF [NAOMI] (modem area for dreamcast) u32 libExtDevice_ReadMem_A0_006(u32 addr,u32 size); void libExtDevice_WriteMem_A0_006(u32 addr,u32 data,u32 size); diff --git a/core/windows/rawinput.cpp b/core/windows/rawinput.cpp new file mode 100644 index 000000000..27a7653fb --- /dev/null +++ b/core/windows/rawinput.cpp @@ -0,0 +1,425 @@ +/* + Copyright 2021 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#include "rawinput.h" +#include +#include +#include "hw/maple/maple_devs.h" + +#ifndef CALLBACK +#define CALLBACK +#endif + +extern int screen_width, screen_height; +HWND getNativeHwnd(); + +namespace rawinput { + +static std::map> mice; +static std::map> keyboards; +static HWND hWnd; + +const u8 Ps2toUsb[0x80] { + // 00 + 0xff, + 0x29, // Esc + 0x1e, // 1 + 0x1f, // 2 + 0x20, // 3 + 0x21, // 4 + 0x22, // 5 + 0x23, // 6 + 0x24, // 7 + 0x25, // 8 + 0x26, // 9 + 0x27, // 0 + 0x2d, // - _ + 0x2e, // = + + 0x2a, // Backspace + 0x2b, // Tab + // 10 + 0x14, // Q + 0x1a, // W + 0x08, // E + 0x15, // R + 0x17, // T + 0x1c, // Y + 0x18, // U + 0x0c, // I + 0x12, // O + 0x13, // P + 0x2f, // [ { + 0x30, // ] } + 0x28, // Return + 0xe0, // Left Control + 0x04, // A + 0x16, // S + // 20 + 0x07, // D + 0x09, // F + 0x0a, // G + 0x0b, // H + 0x0d, // J + 0x0e, // K + 0x0f, // L + 0x33, // ; : + 0x34, // ' " + 0x35, // ` ~ + 0xe1, // Left Shift + 0x31, // \ | (US) + 0x1d, // Z + 0x1b, // X + 0x06, // C + 0x19, // V + // 30 + 0x05, // B + 0x11, // N + 0x10, // M + 0x36, // , < + 0x37, // . > + 0x38, // / ? + 0xe5, // Right Shift + 0x55, // Keypad * + 0xe2, // Left Alt + 0x2c, // Space + 0x39, // Caps Lock + 0x3a, // F1 + 0x3b, // F2 + 0x3c, // F3 + 0x3d, // F4 + 0x3e, // F5 + // 40 + 0x3f, // F6 + 0x40, // F7 + 0x41, // F8 + 0x42, // F9 + 0x43, // F10 + 0x53, // Num Lock + 0x47, // Scroll Lock + 0x5f, // Keypad 7 + 0x60, // Keypad 8 + 0x61, // Keypad 9 + 0x56, // Keypad - + 0x5c, // Keypad 4 + 0x5d, // Keypad 5 + 0x5e, // Keypad 6 + 0x57, // Keypad + + 0x59, // Keypad 1 + // 50 + 0x5a, // Keypad 2 + 0x5b, // Keypad 3 + 0x62, // Keypad 0 + 0x63, // Keypad . + 0xff, + 0xff, + 0x64, // (Europe2) + 0x44, // F11 + 0x45, // F12 + 0x67, // Keypad = + 0xff, + 0xff, + 0x8c, // Int'l 6 + 0xff, + 0xff, + 0xff, + // 60 + 0xff, + 0xff, + 0xff, + 0xff, + 0x68, // F13 + 0x69, // F14 + 0x6a, // F15 + 0x6b, // F16 + 0x6c, // F17 + 0x6d, // F18 + 0x6e, // F19 + 0x6f, // F20 + 0x70, // F21 + 0x71, // F22 + 0x72, // F23 + 0xff, + // 70 + 0x88, // Int'l 2 (Katakana/Hiragana) + 0xff, + 0xff, + 0x87, // Int'l 1 (Ro) + 0xff, + 0xff, + 0x73, // F24 + 0x93, // Lang 4 Hiragana + 0x92, // Lang 3 Katakana + 0x8a, // Int'l 4 (Henkan) + 0xff, + 0x8b, // Int'l 5 (Muhenkan) + 0xff, + 0x89, // Int'l 3 (Yen) + 0x85, // Keypad , + 0xff, +}; + +const u8 Ps2toUsbE0[][2] { + { 0x1c, 0x58 }, // Keypad Enter + { 0x1d, 0xe4 }, // Right Control + { 0x35, 0x54 }, // Keypad / + { 0x37, 0x46 }, // Print Screen + { 0x38, 0xe6 }, // Right Alt + { 0x46, 0x48 }, // Break + { 0x47, 0x4a }, // Home + { 0x48, 0x52 }, // Up + { 0x49, 0x4b }, // Page Up + { 0x4b, 0x50 }, // Left + { 0x4d, 0x4f }, // Right + { 0x4f, 0x4d }, // End + { 0x50, 0x51 }, // Down + { 0x51, 0x4e }, // Page Down + { 0x52, 0x49 }, // Insert + { 0x53, 0x4c }, // Delete + { 0x5b, 0xe3 }, // Left GUI + { 0x5c, 0xe7 }, // Right GUI + { 0x5d, 0x65 }, // App + +}; + +RawMouse::RawMouse(int maple_port, const std::string& name, const std::string& uniqueId, HANDLE handle) : + Mouse("RAW", maple_port), handle(handle) +{ + this->_name = name; + this->_unique_id = uniqueId; + std::replace(this->_unique_id.begin(), this->_unique_id.end(), '=', '_'); + std::replace(this->_unique_id.begin(), this->_unique_id.end(), '[', '_'); + std::replace(this->_unique_id.begin(), this->_unique_id.end(), ']', '_'); + loadMapping(); + + setAbsPos(screen_width / 2, screen_height / 2, screen_width, screen_height); +} + +void RawMouse::buttonInput(Button button, u16 flags, u16 downFlag, u16 upFlag) +{ + if (flags & (downFlag | upFlag)) + setButton(button, flags & downFlag); +} + +void RawMouse::updateState(RAWMOUSE* state) +{ + if (state->usFlags & MOUSE_MOVE_ABSOLUTE) + { + bool isVirtualDesktop = (state->usFlags & MOUSE_VIRTUAL_DESKTOP) == MOUSE_VIRTUAL_DESKTOP; + int width = GetSystemMetrics(isVirtualDesktop ? SM_CXVIRTUALSCREEN : SM_CXSCREEN); + int height = GetSystemMetrics(isVirtualDesktop ? SM_CYVIRTUALSCREEN : SM_CYSCREEN); + + POINT pt { long(state->lLastX / 65535.0f * width), long(state->lLastY / 65535.0f * height) }; + ScreenToClient(getNativeHwnd(), &pt); + setAbsPos(pt.x, pt.y, screen_width, screen_height); + } + else if (state->lLastX != 0 || state->lLastY != 0) + setRelPos(state->lLastX, state->lLastY); + buttonInput(LEFT_BUTTON, state->usButtonFlags, RI_MOUSE_LEFT_BUTTON_DOWN, RI_MOUSE_LEFT_BUTTON_UP); + buttonInput(MIDDLE_BUTTON, state->usButtonFlags, RI_MOUSE_MIDDLE_BUTTON_DOWN, RI_MOUSE_MIDDLE_BUTTON_UP); + buttonInput(RIGHT_BUTTON, state->usButtonFlags, RI_MOUSE_RIGHT_BUTTON_DOWN, RI_MOUSE_RIGHT_BUTTON_UP); + buttonInput(BUTTON_4, state->usButtonFlags, RI_MOUSE_BUTTON_4_DOWN, RI_MOUSE_BUTTON_4_UP); + buttonInput(BUTTON_5, state->usButtonFlags, RI_MOUSE_BUTTON_5_DOWN, RI_MOUSE_BUTTON_5_UP); + if ((state->usButtonFlags & RI_MOUSE_WHEEL)) + setWheel(-(short)state->usButtonData / WHEEL_DELTA); +} + +static LRESULT CALLBACK rawWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg != WM_INPUT) + return DefWindowProcA(hWnd, msg, wParam, lParam); + + RAWINPUT ri; + UINT size = sizeof(ri); + if (GET_RAWINPUT_CODE_WPARAM(wParam) != RIM_INPUT // app isn't in the foreground + || GetRawInputData((HRAWINPUT)lParam, RID_INPUT, &ri, &size, sizeof(RAWINPUTHEADER)) == (UINT)-1) + { + DefWindowProcA(hWnd, msg, wParam, lParam); + return 0; + } + + switch (ri.header.dwType) + { + case RIM_TYPEKEYBOARD: + { + if ((ri.data.keyboard.Flags & RI_KEY_E1) != 0) + break; + auto it = keyboards.find(ri.header.hDevice); + if (it == keyboards.end()) + break; + + u16 scancode = ri.data.keyboard.MakeCode; + bool pressed = (ri.data.keyboard.Flags & RI_KEY_BREAK) == 0; + u8 keycode = 0xff; + if ((ri.data.keyboard.Flags & RI_KEY_E0) != 0) + { + for (u32 i = 0; i < ARRAY_SIZE(Ps2toUsbE0); i++) + if (Ps2toUsbE0[i][0] == scancode) + { + keycode = Ps2toUsbE0[i][1]; + DEBUG_LOG(INPUT, "[%d] E0 key %x -> %x", it->second->maple_port(), scancode, keycode); + break; + } + } + else + { + keycode = Ps2toUsb[scancode]; + DEBUG_LOG(INPUT, "[%d] key %x -> %x", it->second->maple_port(), scancode, keycode); + } + if (keycode != 0xff) + it->second->keyboard_input(keycode, pressed); + } + break; + + case RIM_TYPEMOUSE: + { + auto it = mice.find(ri.header.hDevice); + if (it != mice.end()) + it->second->updateState(&ri.data.mouse); + } + break; + } + + DefWindowProcA(hWnd, msg, wParam, lParam); + return 0; +} + +static void createWindow() +{ + WNDCLASSA wndClass {}; + wndClass.hInstance = GetModuleHandleA(nullptr); + if (!wndClass.hInstance) + return; + wndClass.lpfnWndProc = rawWindowProc; + wndClass.lpszClassName = "flycastRawInput"; + if (RegisterClassA(&wndClass) == 0 && GetLastError() != ERROR_CLASS_ALREADY_EXISTS) + return; + + hWnd = CreateWindowExA(0, wndClass.lpszClassName, nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, nullptr, nullptr); + if (hWnd == nullptr) + UnregisterClassA(wndClass.lpszClassName, nullptr); +} + +static void destroyWindow() +{ + if (hWnd == nullptr) + return; + + DestroyWindow(hWnd); + hWnd = nullptr; + UnregisterClassA("flycastRawInput", nullptr); +} + +static void findDevices() +{ + u32 numDevices; + GetRawInputDeviceList(NULL, &numDevices, sizeof(RAWINPUTDEVICELIST)); + if (numDevices == 0) + return; + + RAWINPUTDEVICELIST *deviceList = new RAWINPUTDEVICELIST[numDevices]; + GetRawInputDeviceList(deviceList, &numDevices, sizeof(RAWINPUTDEVICELIST)); + for (u32 i = 0; i < numDevices; ++i) + { + RAWINPUTDEVICELIST& device = deviceList[i]; + if (device.dwType == RIM_TYPEMOUSE || device.dwType == RIM_TYPEKEYBOARD) + { + // Get the device name + std::string name; + std::string uniqueId; + u32 size; + GetRawInputDeviceInfo(device.hDevice, RIDI_DEVICENAME, nullptr, &size); + if (size > 0) + { + std::vector deviceNameData(size); + u32 res = GetRawInputDeviceInfo(device.hDevice, RIDI_DEVICENAME, &deviceNameData[0], &size); + if (res != (u32)-1) + { + std::string deviceName(&deviceNameData[0], std::strlen(&deviceNameData[0])); + if (deviceName.substr(0, 8) == "\\\\?\\HID#") + deviceName = deviceName.substr(8); + uniqueId = (device.dwType == RIM_TYPEMOUSE ? "raw_mouse_" : "raw_keyboard_") + deviceName; + if (deviceName.length() > 17 && deviceName.substr(0, 4) == "VID_" && deviceName.substr(8, 5) == "&PID_") + deviceName = deviceName.substr(0, 17); + name = (device.dwType == RIM_TYPEMOUSE ? "Mouse " : "Keyboard ") + deviceName; + } + } + uintptr_t handle = (uintptr_t)device.hDevice; + if (name.empty()) + name = (device.dwType == RIM_TYPEMOUSE ? "Mouse " : "Keyboard ") + std::to_string(handle); + if (uniqueId.empty()) + uniqueId = (device.dwType == RIM_TYPEMOUSE ? "raw_mouse_" : "raw_keyboard_") + std::to_string(handle); + NOTICE_LOG(INPUT, "Found RawInput %s name \"%s\" id %s", device.dwType == RIM_TYPEMOUSE ? "mouse" : "keyboard", name.c_str(), uniqueId.c_str()); + + if (device.dwType == RIM_TYPEMOUSE) + { + auto ptr = std::make_shared(mice.size() >= 4 ? 3 : mice.size(), name, uniqueId, device.hDevice); + mice[device.hDevice] = ptr; + GamepadDevice::Register(ptr); + } + else + { + auto ptr = std::make_shared(keyboards.size() >= 4 ? 3 : keyboards.size(), name, uniqueId, device.hDevice); + keyboards[device.hDevice] = ptr; + GamepadDevice::Register(ptr); + } + } + } + delete [] deviceList; +} + +void init() +{ + createWindow(); + verify(hWnd != NULL); + findDevices(); + + RAWINPUTDEVICE rid[2]; + rid[0].dwFlags = 0; + rid[0].hwndTarget = hWnd; + rid[0].usUsagePage = HID_USAGE_PAGE_GENERIC; + rid[0].usUsage = HID_USAGE_GENERIC_MOUSE; + rid[1].dwFlags = 0; + rid[1].hwndTarget = hWnd; + rid[1].usUsagePage = HID_USAGE_PAGE_GENERIC; + rid[1].usUsage = HID_USAGE_GENERIC_KEYBOARD; + RegisterRawInputDevices(rid, 2, sizeof(RAWINPUTDEVICE)); +} + +void term() +{ + RAWINPUTDEVICE rid[2]; + rid[0].dwFlags = RIDEV_REMOVE; + rid[0].hwndTarget = nullptr; + rid[0].usUsagePage = HID_USAGE_PAGE_GENERIC; + rid[0].usUsage = HID_USAGE_GENERIC_MOUSE; + rid[1].dwFlags = RIDEV_REMOVE; + rid[1].hwndTarget = nullptr; + rid[1].usUsagePage = HID_USAGE_PAGE_GENERIC; + rid[1].usUsage = HID_USAGE_GENERIC_KEYBOARD; + RegisterRawInputDevices(rid, 2, sizeof(RAWINPUTDEVICE)); + + destroyWindow(); + for (auto& mouse : mice) + GamepadDevice::Unregister(mouse.second); + mice.clear(); + for (auto& keyboard : keyboards) + GamepadDevice::Unregister(keyboard.second); + keyboards.clear(); +} + +} diff --git a/core/windows/rawinput.h b/core/windows/rawinput.h new file mode 100644 index 000000000..276481c4e --- /dev/null +++ b/core/windows/rawinput.h @@ -0,0 +1,68 @@ +/* + Copyright 2021 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#include "input/gamepad_device.h" +#include "input/keyboard_device.h" +#include "rend/gui.h" +#include + +namespace rawinput { + +class RawMouse : public Mouse +{ +public: + RawMouse(int maple_port, const std::string& name, const std::string& uniqueId, HANDLE handle); + + void updateState(RAWMOUSE* state); + +private: + void buttonInput(Button button, u16 flags, u16 downFlag, u16 upFlag); + + HANDLE handle = NULL; +}; + +class RawKeyboard : public KeyboardDeviceTemplate +{ +public: + RawKeyboard(int maple_port, const std::string& name, const std::string& uniqueId, HANDLE handle) + : KeyboardDeviceTemplate(maple_port, "RAW"), handle(handle) + { + this->_name = name; + this->_unique_id = uniqueId; + std::replace(this->_unique_id.begin(), this->_unique_id.end(), '=', '_'); + std::replace(this->_unique_id.begin(), this->_unique_id.end(), '[', '_'); + std::replace(this->_unique_id.begin(), this->_unique_id.end(), ']', '_'); + loadMapping(); + } + +protected: + virtual u8 convert_keycode(u8 scancode) override + { + if (settings.input.keyboardLangId != KeyboardLayout::US && scancode == 0x31) // US: backslash and pipe + return (u8)0x32; // non-US: hash and tilde + else + return (u8)scancode; + } +private: + HANDLE handle = NULL; +}; + +void init(); +void term(); + +} diff --git a/core/windows/win_keyboard.h b/core/windows/win_keyboard.h index 515e643d5..c2ea0ec00 100644 --- a/core/windows/win_keyboard.h +++ b/core/windows/win_keyboard.h @@ -1,154 +1,153 @@ -#pragma once -#include "input/keyboard_device.h" - -#include - -// Used to differentiate between main enter key and num keypad one -#define VK_NUMPAD_RETURN 0x0E - -class Win32KeyboardDevice : public KeyboardDeviceTemplate -{ -public: - Win32KeyboardDevice(int maple_port) : KeyboardDeviceTemplate(maple_port) - { - kb_map['A'] = 0x04; - kb_map['B'] = 0x05; - kb_map['C'] = 0x06; - kb_map['D'] = 0x07; - kb_map['E'] = 0x08; - kb_map['F'] = 0x09; - kb_map['G'] = 0x0A; - kb_map['H'] = 0x0B; - kb_map['I'] = 0x0C; - kb_map['J'] = 0x0D; - kb_map['K'] = 0x0E; - kb_map['L'] = 0x0F; - kb_map['M'] = 0x10; - kb_map['N'] = 0x11; - kb_map['O'] = 0x12; - kb_map['P'] = 0x13; - kb_map['Q'] = 0x14; - kb_map['R'] = 0x15; - kb_map['S'] = 0x16; - kb_map['T'] = 0x17; - kb_map['U'] = 0x18; - kb_map['V'] = 0x19; - kb_map['W'] = 0x1A; - kb_map['X'] = 0x1B; - kb_map['Y'] = 0x1C; - kb_map['Z'] = 0x1D; - - //1E-27 Number keys 1-0 - kb_map['1'] = 0x1E; - kb_map['2'] = 0x1F; - kb_map['3'] = 0x20; - kb_map['4'] = 0x21; - kb_map['5'] = 0x22; - kb_map['6'] = 0x23; - kb_map['7'] = 0x24; - kb_map['8'] = 0x25; - kb_map['9'] = 0x26; - kb_map['0'] = 0x27; - - kb_map[VK_RETURN] = 0x28; - kb_map[VK_ESCAPE] = 0x29; - kb_map[VK_BACK] = 0x2A; - kb_map[VK_TAB] = 0x2B; - kb_map[VK_SPACE] = 0x2C; - - kb_map[VK_OEM_MINUS] = 0x2D; // - - kb_map[VK_OEM_PLUS] = 0x2E; // = - kb_map[VK_OEM_4] = 0x2F; // [ - kb_map[VK_OEM_6] = 0x30; // ] - - kb_map[VK_OEM_5] = 0x31; // \ (US) unsure of keycode - - //32-34 "]", ";" and ":" (the 3 keys right of L) - kb_map[VK_OEM_8] = 0x32; // ~ (non-US) *,µ in FR layout - kb_map[VK_OEM_1] = 0x33; // ; - kb_map[VK_OEM_7] = 0x34; // ' - - //35 hankaku/zenkaku / kanji (top left) - kb_map[VK_OEM_3] = 0x35; // `~ (US) - - //36-38 ",", "." and "/" (the 3 keys right of M) - kb_map[VK_OEM_COMMA] = 0x36; - kb_map[VK_OEM_PERIOD] = 0x37; - kb_map[VK_OEM_2] = 0x38; - - // CAPSLOCK - kb_map[VK_CAPITAL] = 0x39; - - //3A-45 Function keys F1-F12 - for (int i = 0;i < 12; i++) - kb_map[VK_F1 + i] = 0x3A + i; - - //46-4E Control keys above cursor keys - kb_map[VK_SNAPSHOT] = 0x46; // Print Screen - kb_map[VK_SCROLL] = 0x47; // Scroll Lock - kb_map[VK_PAUSE] = 0x48; // Pause - kb_map[VK_INSERT] = 0x49; - kb_map[VK_HOME] = 0x4A; - kb_map[VK_PRIOR] = 0x4B; - kb_map[VK_DELETE] = 0x4C; - kb_map[VK_END] = 0x4D; - kb_map[VK_NEXT] = 0x4E; - - //4F-52 Cursor keys - kb_map[VK_RIGHT] = 0x4F; - kb_map[VK_LEFT] = 0x50; - kb_map[VK_DOWN] = 0x51; - kb_map[VK_UP] = 0x52; - - //53 Num Lock (Numeric keypad) - kb_map[VK_NUMLOCK] = 0x53; - //54 "/" (Numeric keypad) - kb_map[VK_DIVIDE] = 0x54; - //55 "*" (Numeric keypad) - kb_map[VK_MULTIPLY] = 0x55; - //56 "-" (Numeric keypad) - kb_map[VK_SUBTRACT] = 0x56; - //57 "+" (Numeric keypad) - kb_map[VK_ADD] = 0x57; - //58 Enter (Numeric keypad) - kb_map[VK_NUMPAD_RETURN] = 0x58; - //59-62 Number keys 1-0 (Numeric keypad) - kb_map[VK_NUMPAD1] = 0x59; - kb_map[VK_NUMPAD2] = 0x5A; - kb_map[VK_NUMPAD3] = 0x5B; - kb_map[VK_NUMPAD4] = 0x5C; - kb_map[VK_NUMPAD5] = 0x5D; - kb_map[VK_NUMPAD6] = 0x5E; - kb_map[VK_NUMPAD7] = 0x5F; - kb_map[VK_NUMPAD8] = 0x60; - kb_map[VK_NUMPAD9] = 0x61; - kb_map[VK_NUMPAD0] = 0x62; - //63 "." (Numeric keypad) - kb_map[VK_DECIMAL] = 0x63; - //64 #| (non-US) - //kb_map[94] = 0x64; - //65 S3 key - //66-A4 Not used - //A5-DF Reserved - kb_map[VK_CONTROL] = 0xE0; // Left Control - kb_map[VK_SHIFT] = 0xE1; // Left Shift - //E2 Left Alt - //E3 Left S1 - //E4 Right Control - //E5 Right Shift - //E6 Right Alt - //E7 Right S3 - //E8-FF Reserved - - } - const char* name() override { return "Windows Keyboard"; } - -protected: - u8 convert_keycode(u8 keycode) override - { - return kb_map[keycode]; - } - -private: - std::map kb_map; -}; +#pragma once +#include "input/keyboard_device.h" + +#include + +// Used to differentiate between main enter key and num keypad one +#define VK_NUMPAD_RETURN 0x0E + +class Win32KeyboardDevice : public KeyboardDeviceTemplate +{ +public: + Win32KeyboardDevice(int maple_port) : KeyboardDeviceTemplate(maple_port, "win32") + { + kb_map['A'] = 0x04; + kb_map['B'] = 0x05; + kb_map['C'] = 0x06; + kb_map['D'] = 0x07; + kb_map['E'] = 0x08; + kb_map['F'] = 0x09; + kb_map['G'] = 0x0A; + kb_map['H'] = 0x0B; + kb_map['I'] = 0x0C; + kb_map['J'] = 0x0D; + kb_map['K'] = 0x0E; + kb_map['L'] = 0x0F; + kb_map['M'] = 0x10; + kb_map['N'] = 0x11; + kb_map['O'] = 0x12; + kb_map['P'] = 0x13; + kb_map['Q'] = 0x14; + kb_map['R'] = 0x15; + kb_map['S'] = 0x16; + kb_map['T'] = 0x17; + kb_map['U'] = 0x18; + kb_map['V'] = 0x19; + kb_map['W'] = 0x1A; + kb_map['X'] = 0x1B; + kb_map['Y'] = 0x1C; + kb_map['Z'] = 0x1D; + + //1E-27 Number keys 1-0 + kb_map['1'] = 0x1E; + kb_map['2'] = 0x1F; + kb_map['3'] = 0x20; + kb_map['4'] = 0x21; + kb_map['5'] = 0x22; + kb_map['6'] = 0x23; + kb_map['7'] = 0x24; + kb_map['8'] = 0x25; + kb_map['9'] = 0x26; + kb_map['0'] = 0x27; + + kb_map[VK_RETURN] = 0x28; + kb_map[VK_ESCAPE] = 0x29; + kb_map[VK_BACK] = 0x2A; + kb_map[VK_TAB] = 0x2B; + kb_map[VK_SPACE] = 0x2C; + + kb_map[VK_OEM_MINUS] = 0x2D; // - + kb_map[VK_OEM_PLUS] = 0x2E; // = + kb_map[VK_OEM_4] = 0x2F; // [ + kb_map[VK_OEM_6] = 0x30; // ] + + kb_map[VK_OEM_5] = 0x31; // \ (US) unsure of keycode + + //32-34 "]", ";" and ":" (the 3 keys right of L) + kb_map[VK_OEM_8] = 0x32; // ~ (non-US) *,µ in FR layout + kb_map[VK_OEM_1] = 0x33; // ; + kb_map[VK_OEM_7] = 0x34; // ' + + //35 hankaku/zenkaku / kanji (top left) + kb_map[VK_OEM_3] = 0x35; // `~ (US) + + //36-38 ",", "." and "/" (the 3 keys right of M) + kb_map[VK_OEM_COMMA] = 0x36; + kb_map[VK_OEM_PERIOD] = 0x37; + kb_map[VK_OEM_2] = 0x38; + + // CAPSLOCK + kb_map[VK_CAPITAL] = 0x39; + + //3A-45 Function keys F1-F12 + for (int i = 0;i < 12; i++) + kb_map[VK_F1 + i] = 0x3A + i; + + //46-4E Control keys above cursor keys + kb_map[VK_SNAPSHOT] = 0x46; // Print Screen + kb_map[VK_SCROLL] = 0x47; // Scroll Lock + kb_map[VK_PAUSE] = 0x48; // Pause + kb_map[VK_INSERT] = 0x49; + kb_map[VK_HOME] = 0x4A; + kb_map[VK_PRIOR] = 0x4B; + kb_map[VK_DELETE] = 0x4C; + kb_map[VK_END] = 0x4D; + kb_map[VK_NEXT] = 0x4E; + + //4F-52 Cursor keys + kb_map[VK_RIGHT] = 0x4F; + kb_map[VK_LEFT] = 0x50; + kb_map[VK_DOWN] = 0x51; + kb_map[VK_UP] = 0x52; + + //53 Num Lock (Numeric keypad) + kb_map[VK_NUMLOCK] = 0x53; + //54 "/" (Numeric keypad) + kb_map[VK_DIVIDE] = 0x54; + //55 "*" (Numeric keypad) + kb_map[VK_MULTIPLY] = 0x55; + //56 "-" (Numeric keypad) + kb_map[VK_SUBTRACT] = 0x56; + //57 "+" (Numeric keypad) + kb_map[VK_ADD] = 0x57; + //58 Enter (Numeric keypad) + kb_map[VK_NUMPAD_RETURN] = 0x58; + //59-62 Number keys 1-0 (Numeric keypad) + kb_map[VK_NUMPAD1] = 0x59; + kb_map[VK_NUMPAD2] = 0x5A; + kb_map[VK_NUMPAD3] = 0x5B; + kb_map[VK_NUMPAD4] = 0x5C; + kb_map[VK_NUMPAD5] = 0x5D; + kb_map[VK_NUMPAD6] = 0x5E; + kb_map[VK_NUMPAD7] = 0x5F; + kb_map[VK_NUMPAD8] = 0x60; + kb_map[VK_NUMPAD9] = 0x61; + kb_map[VK_NUMPAD0] = 0x62; + //63 "." (Numeric keypad) + kb_map[VK_DECIMAL] = 0x63; + //64 #| (non-US) + //kb_map[94] = 0x64; + //65 S3 key + //66-A4 Not used + //A5-DF Reserved + kb_map[VK_CONTROL] = 0xE0; // Left Control + kb_map[VK_SHIFT] = 0xE1; // Left Shift + //E2 Left Alt + //E3 Left S1 + //E4 Right Control + //E5 Right Shift + //E6 Right Alt + //E7 Right S3 + //E8-FF Reserved + + } + +protected: + u8 convert_keycode(u8 keycode) override + { + return kb_map[keycode]; + } + +private: + std::map kb_map; +}; diff --git a/core/windows/winmain.cpp b/core/windows/winmain.cpp index d1f6f1621..9d0bad075 100644 --- a/core/windows/winmain.cpp +++ b/core/windows/winmain.cpp @@ -3,13 +3,14 @@ #include "imgread/common.h" #include "stdclass.h" #include "cfg/cfg.h" -#include "xinput_gamepad.h" #include "win_keyboard.h" #include "hw/sh4/dyna/blockmanager.h" #include "log/LogManager.h" #include "wsi/context.h" #if defined(USE_SDL) #include "sdl/sdl.h" +#else +#include "xinput_gamepad.h" #endif #include "hw/maple/maple_devs.h" #include "emulator.h" @@ -17,12 +18,13 @@ #include "hw/sh4/dyna/ngen.h" #include "oslib/host_context.h" #include "../shell/windows/resource.h" +#include "rawinput.h" #include #include -PCHAR* - CommandLineToArgvA( +static PCHAR* + commandLineToArgvA( PCHAR CmdLine, int* _argc ) @@ -118,8 +120,57 @@ PCHAR* bool VramLockedWrite(u8* address); bool BM_LockedWrite(u8* address); -static std::shared_ptr kb_gamepad; -static std::shared_ptr mouse_gamepad; +#ifndef USE_SDL + +static std::shared_ptr mouse; +static std::shared_ptr keyboard; +static bool mouseCaptured; +static POINT savedMousePos; +static bool gameRunning; + +static void captureMouse(bool); + +static void emuEventCallback(Event event) +{ + static bool captureOn; + switch (event) + { + case Event::Pause: + captureOn = mouseCaptured; + captureMouse(false); + gameRunning = false; + break; + case Event::Resume: + gameRunning = true; + captureMouse(captureOn); + break; + default: + break; + } +} + +static void checkRawInput() +{ + if ((bool)config::UseRawInput != (bool)keyboard) + return; + if (config::UseRawInput) + { + GamepadDevice::Unregister(keyboard); + keyboard = nullptr;; + GamepadDevice::Unregister(mouse); + mouse = nullptr; + rawinput::init(); + } + else + { + rawinput::term(); + keyboard = std::make_shared(0); + GamepadDevice::Register(keyboard); + mouse = std::make_shared(); + GamepadDevice::Register(mouse); + } +} +#endif void os_SetupInput() { @@ -127,11 +178,12 @@ void os_SetupInput() input_sdl_init(); #else XInputGamepadDevice::CreateDevices(); - kb_gamepad = std::make_shared(0); - GamepadDevice::Register(kb_gamepad); - mouse_gamepad = std::make_shared(0); - GamepadDevice::Register(mouse_gamepad); + EventManager::listen(Event::Pause, emuEventCallback); + EventManager::listen(Event::Resume, emuEventCallback); + checkRawInput(); #endif + if (config::UseRawInput) + rawinput::init(); } static void readContext(const EXCEPTION_POINTERS *ep, host_context_t &context) @@ -163,7 +215,7 @@ static void writeContext(EXCEPTION_POINTERS *ep, const host_context_t &context) ep->ContextRecord->Rcx = context.rcx; #endif } -LONG ExeptionHandler(EXCEPTION_POINTERS *ep) +static LONG exceptionHandler(EXCEPTION_POINTERS *ep) { u32 dwCode = ep->ExceptionRecord->ExceptionCode; @@ -203,7 +255,7 @@ LONG ExeptionHandler(EXCEPTION_POINTERS *ep) } -void SetupPath() +static void setupPath() { wchar_t fname[512]; GetModuleFileNameW(0, fname, ARRAY_SIZE(fname)); @@ -227,11 +279,6 @@ void SetupPath() CreateDirectory(data_path.c_str(), NULL); } -static Win32KeyboardDevice keyboard(0); - -void ToggleFullscreen(); - - void UpdateInputState() { #if defined(USE_SDL) @@ -246,8 +293,12 @@ void UpdateInputState() #endif } +static HWND hWnd; + +#ifndef USE_SDL // Windows class name to register #define WINDOW_CLASS "nilDC" +static int window_x, window_y; // Width and height of the window #define DEFAULT_WINDOW_WIDTH 1280 @@ -255,7 +306,42 @@ void UpdateInputState() extern int screen_width, screen_height; static bool window_maximized = false; -LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +static void centerMouse() +{ + RECT rect; + GetWindowRect(hWnd, &rect); + SetCursorPos((rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2); +} + +static void captureMouse(bool capture) +{ + if (hWnd == nullptr || !gameRunning) + return; + if (capture == mouseCaptured) + return; + + if (!capture) + { + os_SetWindowText(VER_EMUNAME); + mouseCaptured = false; + SetCursorPos(savedMousePos.x, savedMousePos.y); + while (ShowCursor(true) < 0) + ; + } + else + { + os_SetWindowText("Flycast - mouse capture"); + mouseCaptured = true; + GetCursorPos(&savedMousePos); + while (ShowCursor(false) >= 0) + ; + centerMouse(); + } +} + +static void toggleFullscreen(); + +static LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { @@ -282,6 +368,10 @@ LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) screen_width = LOWORD(lParam); screen_height = HIWORD(lParam); window_maximized = (wParam & SIZE_MAXIMIZED) != 0; +#ifdef USE_VULKAN + theVulkanContext.SetResized(); +#endif + theDXContext.resize(); return 0; case WM_LBUTTONDOWN: @@ -290,74 +380,102 @@ LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case WM_MBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: + gui_set_mouse_position(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + checkRawInput(); switch (message) { case WM_LBUTTONDOWN: - mouse_gamepad->gamepad_btn_input(0, true); - break; case WM_LBUTTONUP: - mouse_gamepad->gamepad_btn_input(0, false); + if (!mouseCaptured && !config::UseRawInput) + mouse->setButton(Mouse::LEFT_BUTTON, message == WM_LBUTTONDOWN); + gui_set_mouse_button(0, message == WM_LBUTTONDOWN); break; + case WM_MBUTTONDOWN: - mouse_gamepad->gamepad_btn_input(1, true); - break; case WM_MBUTTONUP: - mouse_gamepad->gamepad_btn_input(1, false); + if (!mouseCaptured && !config::UseRawInput) + mouse->setButton(Mouse::MIDDLE_BUTTON, message == WM_MBUTTONDOWN); + gui_set_mouse_button(2, message == WM_MBUTTONDOWN); break; case WM_RBUTTONDOWN: - mouse_gamepad->gamepad_btn_input(2, true); - break; case WM_RBUTTONUP: - mouse_gamepad->gamepad_btn_input(2, false); + if (!mouseCaptured && !config::UseRawInput) + mouse->setButton(Mouse::RIGHT_BUTTON, message == WM_RBUTTONDOWN); + gui_set_mouse_button(1, message == WM_RBUTTONDOWN); break; } + if (mouseCaptured) + break; /* no break */ case WM_MOUSEMOVE: + gui_set_mouse_position(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + checkRawInput(); + if (mouseCaptured) + // TODO relative mouse move if !rawinput + centerMouse(); + else if (!config::UseRawInput) { int xPos = GET_X_LPARAM(lParam); int yPos = GET_Y_LPARAM(lParam); - SetMousePosition(xPos, yPos, screen_width, screen_height); + mouse->setAbsPos(xPos, yPos, screen_width, screen_height); - mo_buttons[0] = 0xffffffff; if (wParam & MK_LBUTTON) - mo_buttons[0] &= ~(1 << 2); + mouse->setButton(Button::LEFT_BUTTON, true); if (wParam & MK_MBUTTON) - mo_buttons[0] &= ~(1 << 3); + mouse->setButton(Button::MIDDLE_BUTTON, true); if (wParam & MK_RBUTTON) - mo_buttons[0] &= ~(1 << 1); + mouse->setButton(Button::RIGHT_BUTTON, true); } if (message != WM_MOUSEMOVE) return 0; break; case WM_MOUSEWHEEL: - mo_wheel_delta[0] -= (float)GET_WHEEL_DELTA_WPARAM(wParam)/(float)WHEEL_DELTA * 16; + gui_set_mouse_wheel(-(float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA * 16); + checkRawInput(); + if (!config::UseRawInput) + mouse->setWheel(-GET_WHEEL_DELTA_WPARAM(wParam) / WHEEL_DELTA); break; case WM_KEYDOWN: case WM_KEYUP: { - u8 keycode; - // bit 24 indicates whether the key is an extended key, such as the right-hand ALT and CTRL keys that appear on an enhanced 101- or 102-key keyboard. - // (It also distinguishes between the main Return key and the numeric keypad Enter key) - // The value is 1 if it is an extended key; otherwise, it is 0. - if (wParam == VK_RETURN && ((lParam & (1 << 24)) != 0)) - keycode = VK_NUMPAD_RETURN; - else - keycode = wParam & 0xff; - kb_gamepad->gamepad_btn_input(keycode, message == WM_KEYDOWN); - keyboard.keyboard_input(keycode, message == WM_KEYDOWN); + if (message == WM_KEYDOWN + && ((wParam == VK_CONTROL && GetAsyncKeyState(VK_LMENU) < 0) + || (wParam == VK_MENU && GetAsyncKeyState(VK_LCONTROL) < 0))) + { + captureMouse(!mouseCaptured); + break; + } + checkRawInput(); + if (!config::UseRawInput) + { + u8 keycode; + // bit 24 indicates whether the key is an extended key, such as the right-hand ALT and CTRL keys that appear on an enhanced 101- or 102-key keyboard. + // (It also distinguishes between the main Return key and the numeric keypad Enter key) + // The value is 1 if it is an extended key; otherwise, it is 0. + if (wParam == VK_RETURN && ((lParam & (1 << 24)) != 0)) + keycode = VK_NUMPAD_RETURN; + else + keycode = wParam & 0xff; + keyboard->keyboard_input(keycode, message == WM_KEYDOWN); + } } break; case WM_SYSKEYDOWN: if (wParam == VK_RETURN) + { if ((HIWORD(lParam) & KF_ALTDOWN)) - ToggleFullscreen(); - + toggleFullscreen(); + } + else if (wParam == VK_CONTROL && (lParam & (1 << 24)) == 0 && GetAsyncKeyState(VK_LMENU) < 0) + { + captureMouse(!mouseCaptured); + } break; case WM_CHAR: - keyboard.keyboard_character((char)wParam); + gui_keyboard_input((u16)wParam); return 0; default: @@ -368,10 +486,6 @@ LRESULT CALLBACK WndProc2(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) return DefWindowProc(hWnd, message, wParam, lParam); } -static HWND hWnd; -static int window_x, window_y; - -#if !defined(USE_SDL) static bool windowClassRegistered; void CreateMainWindow() @@ -407,13 +521,14 @@ void CreateMainWindow() SetRect(&sRect, 0, 0, screen_width, screen_height); AdjustWindowRectEx(&sRect, WS_OVERLAPPEDWINDOW, false, 0); - hWnd = CreateWindow(WINDOW_CLASS, VER_FULLNAME, WS_VISIBLE | WS_OVERLAPPEDWINDOW | (window_maximized ? WS_MAXIMIZE : 0), + hWnd = CreateWindow(WINDOW_CLASS, VER_EMUNAME, WS_VISIBLE | WS_OVERLAPPEDWINDOW | (window_maximized ? WS_MAXIMIZE : 0), window_x, window_y, sRect.right - sRect.left, sRect.bottom - sRect.top, NULL, NULL, hInstance, NULL); #ifdef USE_VULKAN theVulkanContext.SetWindow((void *)hWnd, (void *)GetDC((HWND)hWnd)); #endif theGLContext.SetWindow(hWnd); theGLContext.SetDeviceContext(GetDC(hWnd)); + theDXContext.setNativeWindow(hWnd); } #endif @@ -427,7 +542,8 @@ void os_CreateWindow() #endif // !USE_SDL } -void DestroyMainWindow() +#ifndef USE_SDL +static void destroyMainWindow() { if (hWnd) { @@ -442,12 +558,7 @@ void DestroyMainWindow() } } -void* libPvr_GetRenderTarget() -{ - return (void*)hWnd; -} - -void ToggleFullscreen() +static void toggleFullscreen() { static RECT rSaved; static bool fullscreen=false; @@ -483,26 +594,11 @@ void ToggleFullscreen() } - -BOOL CtrlHandler( DWORD fdwCtrlType ) +HWND getNativeHwnd() { - switch( fdwCtrlType ) - { - case CTRL_SHUTDOWN_EVENT: - case CTRL_LOGOFF_EVENT: - // Pass other signals to the next handler. - case CTRL_BREAK_EVENT: - // CTRL-CLOSE: confirm that the user wants to exit. - case CTRL_CLOSE_EVENT: - // Handle the CTRL-C signal. - case CTRL_C_EVENT: - SendMessageA(hWnd, WM_CLOSE, 0, 0); //FIXEM - return( TRUE ); - default: - return FALSE; - } + return hWnd; } - +#endif void os_SetWindowText(const char* text) { @@ -516,7 +612,7 @@ void os_SetWindowText(const char* text) #endif } -void ReserveBottomMemory() +static void reserveBottomMemory() { #if defined(_WIN64) && defined(_DEBUG) static bool s_initialized = false; @@ -641,19 +737,6 @@ typedef struct _UNWIND_INFO { static RUNTIME_FUNCTION Table[1]; static _UNWIND_INFO unwind_info[1]; -EXCEPTION_DISPOSITION -__gnat_SEH_error_handler(struct _EXCEPTION_RECORD* ExceptionRecord, -void *EstablisherFrame, -struct _CONTEXT* ContextRecord, - void *DispatcherContext) -{ - EXCEPTION_POINTERS ep; - ep.ContextRecord = ContextRecord; - ep.ExceptionRecord = ExceptionRecord; - - return (EXCEPTION_DISPOSITION)ExeptionHandler(&ep); -} - PRUNTIME_FUNCTION seh_callback( _In_ DWORD64 ControlPc, @@ -678,8 +761,8 @@ _In_opt_ PVOID Context //for (;;); return Table; } -void setup_seh() { -#if 1 +static void setup_seh() +{ /* Get the base of the module. */ //u8* __ImageBase = (u8*)GetModuleHandle(NULL); /* Current version is always 1 and we are registering an @@ -705,7 +788,6 @@ void setup_seh() { Table[0].UnwindData = (DWORD)((u8 *)unwind_info - CodeCache); /* Register the unwind information. */ RtlAddFunctionTable(Table, 1, (DWORD64)CodeCache); -#endif //verify(RtlInstallFunctionTableCallback((unat)CodeCache | 0x3, (DWORD64)CodeCache, CODE_SIZE + TEMP_CODE_SIZE, seh_callback, 0, 0)); } @@ -758,7 +840,7 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi { int argc = 0; char* cmd_line = GetCommandLineA(); - char** argv = CommandLineToArgvA(cmd_line, &argc); + char** argv = commandLineToArgvA(cmd_line, &argc); #endif @@ -767,13 +849,13 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi #endif LogManager::Init(); - ReserveBottomMemory(); - SetupPath(); + reserveBottomMemory(); + setupPath(); findKeyboardLayout(); #ifdef _WIN64 - AddVectoredExceptionHandler(1, ExeptionHandler); + AddVectoredExceptionHandler(1, exceptionHandler); #else - SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)&ExeptionHandler); + SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)&exceptionHandler); #endif if (reicast_init(argc, argv) != 0) die("Flycast initialization failed"); @@ -790,6 +872,8 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi #ifdef USE_SDL sdl_window_destroy(); #else + TermRenderApi(); + destroyMainWindow(); cfgSaveBool("window", "maximized", window_maximized); if (!window_maximized && screen_width != 0 && screen_height != 0) { diff --git a/core/windows/xinput_gamepad.h b/core/windows/xinput_gamepad.h index 6c5a8e771..f1d0444a5 100644 --- a/core/windows/xinput_gamepad.h +++ b/core/windows/xinput_gamepad.h @@ -1,298 +1,215 @@ -#include "input/gamepad_device.h" -#include "rend/gui.h" - -#include -#include - -class XInputMapping : public InputMapping -{ -public: - XInputMapping() - { - name = "XInput"; - set_button(DC_BTN_A, XINPUT_GAMEPAD_A); - set_button(DC_BTN_B, XINPUT_GAMEPAD_B); - set_button(DC_BTN_X, XINPUT_GAMEPAD_X); - set_button(DC_BTN_Y, XINPUT_GAMEPAD_Y); - set_button(DC_DPAD_UP, XINPUT_GAMEPAD_DPAD_UP); - set_button(DC_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_DOWN); - set_button(DC_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_LEFT); - set_button(DC_DPAD_RIGHT, XINPUT_GAMEPAD_DPAD_RIGHT); - set_button(DC_BTN_START, XINPUT_GAMEPAD_START); - set_button(EMU_BTN_TRIGGER_LEFT, XINPUT_GAMEPAD_LEFT_SHOULDER); - set_button(EMU_BTN_TRIGGER_RIGHT, XINPUT_GAMEPAD_RIGHT_SHOULDER); - set_button(EMU_BTN_MENU, XINPUT_GAMEPAD_BACK); - set_axis(DC_AXIS_LT, 0, false); - set_axis(DC_AXIS_RT, 1, false); - set_axis(DC_AXIS_X, 2, false); - set_axis(DC_AXIS_Y, 3, true); - set_axis(DC_AXIS_X2, 4, false); - set_axis(DC_AXIS_Y2, 5, true); - dirty = false; - } -}; - -class XInputGamepadDevice : public GamepadDevice -{ -public: - XInputGamepadDevice(int maple_port, int xinput_port) - : GamepadDevice(maple_port, "xinput"), _xinput_port(xinput_port) - { - char buf[32]; - sprintf(buf, "xinput-%d", xinput_port + 1); - _unique_id = buf; - } - - void ReadInput() - { - update_rumble(); - - XINPUT_STATE state; - - if (XInputGetState(_xinput_port, &state) == 0) - { - if (!input_mapper) - Open(); - u32 xbutton = state.Gamepad.wButtons; - u32 changes = xbutton ^ last_buttons_state; - - for (int i = 0; i < 16; i++) - if ((changes & (1 << i)) != 0) - gamepad_btn_input(1 << i, (xbutton & (1 << i)) != 0); - last_buttons_state = xbutton; - - if (state.Gamepad.bLeftTrigger != last_left_trigger) - { - gamepad_axis_input(0, state.Gamepad.bLeftTrigger); - last_left_trigger = state.Gamepad.bLeftTrigger; - } - if (state.Gamepad.bRightTrigger != last_right_trigger) - { - gamepad_axis_input(1, state.Gamepad.bRightTrigger); - last_right_trigger = state.Gamepad.bRightTrigger; - } - if (state.Gamepad.sThumbLX != last_left_thumb_x) - { - gamepad_axis_input(2, state.Gamepad.sThumbLX); - last_left_thumb_x = state.Gamepad.sThumbLX; - } - if (state.Gamepad.sThumbLY != last_left_thumb_y) - { - gamepad_axis_input(3, state.Gamepad.sThumbLY); - last_left_thumb_y = state.Gamepad.sThumbLY; - } - if (state.Gamepad.sThumbRX != last_right_thumb_x) - { - gamepad_axis_input(4, state.Gamepad.sThumbRX); - last_right_thumb_x = state.Gamepad.sThumbRX; - } - if (state.Gamepad.sThumbRY != last_right_thumb_y) - { - gamepad_axis_input(5, state.Gamepad.sThumbRY); - last_right_thumb_y = state.Gamepad.sThumbRY; - } - } - else if (input_mapper) - { - INFO_LOG(INPUT, "xinput: Controller '%s' on port %d disconnected", _name.c_str(), _xinput_port); - GamepadDevice::Unregister(xinput_gamepads[_xinput_port]); - input_mapper.reset(); - last_buttons_state = 0; - last_left_trigger = 0; - last_right_trigger = 0; - last_left_thumb_x = 0; - last_left_thumb_y = 0; - last_right_thumb_x = 0; - last_right_thumb_y = 0; - } - } - void rumble(float power, float inclination, u32 duration_ms) override - { - vib_inclination = inclination * power; - vib_stop_time = os_GetSeconds() + duration_ms / 1000.0; - - do_rumble(power); - } - void update_rumble() override - { - if (vib_stop_time > 0) - { - int rem_time = (vib_stop_time - os_GetSeconds()) * 1000; - if (rem_time <= 0) - { - vib_stop_time = 0; - do_rumble(0); - } - else if (vib_inclination > 0) - do_rumble(vib_inclination * rem_time); - } - } - - void Open() - { - JOYCAPS joycaps; - int rc = joyGetDevCaps(_xinput_port, &joycaps, sizeof(joycaps)); - if (rc != 0) - _name = "xinput" + std::to_string(_xinput_port); - else - _name = joycaps.szPname; - INFO_LOG(INPUT, "xinput: Opened controller '%s' on port %d", _name.c_str(), _xinput_port); - if (!find_mapping()) - { - input_mapper = std::make_shared(); - input_mapper->name = _name + " mapping"; - save_mapping(); - INFO_LOG(INPUT, "using default mapping"); - } - else - INFO_LOG(INPUT, "using custom mapping '%s'n", input_mapper->name.c_str()); - GamepadDevice::Register(xinput_gamepads[_xinput_port]); - } - - static void CreateDevices() - { - for (int port = 0; port < XUSER_MAX_COUNT; port++) - xinput_gamepads[port] = std::make_shared(port, port); - } - static void CloseDevices() - { - for (int port = 0; port < XUSER_MAX_COUNT; port++) - GamepadDevice::Unregister(xinput_gamepads[port]); - } - - static std::shared_ptr GetXInputDevice(int port) - { - return xinput_gamepads[port]; - } - -protected: - void load_axis_min_max(u32 axis) override - { - if (axis == 0 || axis == 1) - { - axis_ranges[axis] = 255; - axis_min_values[axis] = 0; - } - else - { - axis_ranges[axis] = 65535; - axis_min_values[axis] = -32768; - } - } - -private: - void do_rumble(float power) - { - XINPUT_VIBRATION vib; - - vib.wLeftMotorSpeed = (u16)(65535 * power); - vib.wRightMotorSpeed = (u16)(65535 * power); - - XInputSetState(_xinput_port, &vib); - } - - const int _xinput_port; - u32 last_buttons_state = 0; - u8 last_left_trigger = 0; - u8 last_right_trigger = 0; - s16 last_left_thumb_x = 0; - s16 last_left_thumb_y = 0; - s16 last_right_thumb_x = 0; - s16 last_right_thumb_y = 0; - double vib_stop_time; - float vib_inclination; - static std::vector> xinput_gamepads; -}; - -std::vector> XInputGamepadDevice::xinput_gamepads(XUSER_MAX_COUNT); - -class KbInputMapping : public InputMapping -{ -public: - KbInputMapping() - { - name = "Windows Keyboard"; - set_button(DC_BTN_A, 'X'); - set_button(DC_BTN_B, 'C'); - set_button(DC_BTN_X, 'S'); - set_button(DC_BTN_Y, 'D'); - set_button(DC_DPAD_UP, VK_UP); - set_button(DC_DPAD_DOWN, VK_DOWN); - set_button(DC_DPAD_LEFT, VK_LEFT); - set_button(DC_DPAD_RIGHT, VK_RIGHT); - set_button(DC_BTN_START, VK_RETURN); - set_button(EMU_BTN_TRIGGER_LEFT, 'F'); - set_button(EMU_BTN_TRIGGER_RIGHT, 'V'); - set_button(EMU_BTN_MENU, VK_TAB); - set_button(EMU_BTN_FFORWARD, VK_SPACE); - - dirty = false; - } -}; - -class WinKbGamepadDevice : public GamepadDevice -{ -public: - WinKbGamepadDevice(int maple_port) : GamepadDevice(maple_port, "win32") - { - _name = "Keyboard"; - _unique_id = "win_keyboard"; - if (!find_mapping()) - input_mapper = std::make_shared(); - } - ~WinKbGamepadDevice() override = default; -}; - -class MouseInputMapping : public InputMapping -{ -public: - MouseInputMapping() - { - name = "Mouse"; - set_button(DC_BTN_A, 0); // Left - set_button(DC_BTN_B, 2); // Right - set_button(DC_BTN_START, 1);// Middle - - dirty = false; - } -}; - -class WinMouseGamepadDevice : public GamepadDevice -{ -public: - WinMouseGamepadDevice(int maple_port) : GamepadDevice(maple_port, "win32") - { - _name = "Mouse"; - _unique_id = "win_mouse"; - if (!find_mapping()) - input_mapper = std::make_shared(); - } - ~WinMouseGamepadDevice() override = default; - - bool gamepad_btn_input(u32 code, bool pressed) override - { - if (gui_is_open() && !is_detecting_input()) - // Don't register mouse clicks as gamepad presses when gui is open - // This makes the gamepad presses to be handled first and the mouse position to be ignored - // TODO Make this generic - return false; - else - return GamepadDevice::gamepad_btn_input(code, pressed); - } - - const char *get_button_name(u32 code) override - { - switch (code) - { - case 0: - return "Left Button"; - case 2: - return "Right Button"; - case 1: - return "Middle Button"; - default: - return nullptr; - } - } -}; - +#include "input/gamepad_device.h" +#include "rend/gui.h" + +#include +#include + +class XInputMapping : public InputMapping +{ +public: + XInputMapping() + { + name = "XInput"; + set_button(DC_BTN_A, XINPUT_GAMEPAD_A); + set_button(DC_BTN_B, XINPUT_GAMEPAD_B); + set_button(DC_BTN_X, XINPUT_GAMEPAD_X); + set_button(DC_BTN_Y, XINPUT_GAMEPAD_Y); + set_button(DC_DPAD_UP, XINPUT_GAMEPAD_DPAD_UP); + set_button(DC_DPAD_DOWN, XINPUT_GAMEPAD_DPAD_DOWN); + set_button(DC_DPAD_LEFT, XINPUT_GAMEPAD_DPAD_LEFT); + set_button(DC_DPAD_RIGHT, XINPUT_GAMEPAD_DPAD_RIGHT); + set_button(DC_BTN_START, XINPUT_GAMEPAD_START); + set_button(EMU_BTN_TRIGGER_LEFT, XINPUT_GAMEPAD_LEFT_SHOULDER); + set_button(EMU_BTN_TRIGGER_RIGHT, XINPUT_GAMEPAD_RIGHT_SHOULDER); + set_button(EMU_BTN_MENU, XINPUT_GAMEPAD_BACK); + set_axis(DC_AXIS_LT, 0, false); + set_axis(DC_AXIS_RT, 1, false); + set_axis(DC_AXIS_X, 2, false); + set_axis(DC_AXIS_Y, 3, true); + set_axis(DC_AXIS_X2, 4, false); + set_axis(DC_AXIS_Y2, 5, true); + dirty = false; + } +}; + +class XInputGamepadDevice : public GamepadDevice +{ +public: + XInputGamepadDevice(int maple_port, int xinput_port) + : GamepadDevice(maple_port, "xinput"), _xinput_port(xinput_port) + { + char buf[32]; + sprintf(buf, "xinput-%d", xinput_port + 1); + _unique_id = buf; + } + + virtual std::shared_ptr getDefaultMapping() override { + return std::make_shared(); + } + + void ReadInput() + { + update_rumble(); + + XINPUT_STATE state; + + if (XInputGetState(_xinput_port, &state) == 0) + { + if (!input_mapper) + Open(); + u32 xbutton = state.Gamepad.wButtons; + u32 changes = xbutton ^ last_buttons_state; + + for (int i = 0; i < 16; i++) + if ((changes & (1 << i)) != 0) + gamepad_btn_input(1 << i, (xbutton & (1 << i)) != 0); + last_buttons_state = xbutton; + + if (state.Gamepad.bLeftTrigger != last_left_trigger) + { + gamepad_axis_input(0, state.Gamepad.bLeftTrigger); + last_left_trigger = state.Gamepad.bLeftTrigger; + } + if (state.Gamepad.bRightTrigger != last_right_trigger) + { + gamepad_axis_input(1, state.Gamepad.bRightTrigger); + last_right_trigger = state.Gamepad.bRightTrigger; + } + if (state.Gamepad.sThumbLX != last_left_thumb_x) + { + gamepad_axis_input(2, state.Gamepad.sThumbLX); + last_left_thumb_x = state.Gamepad.sThumbLX; + } + if (state.Gamepad.sThumbLY != last_left_thumb_y) + { + gamepad_axis_input(3, state.Gamepad.sThumbLY); + last_left_thumb_y = state.Gamepad.sThumbLY; + } + if (state.Gamepad.sThumbRX != last_right_thumb_x) + { + gamepad_axis_input(4, state.Gamepad.sThumbRX); + last_right_thumb_x = state.Gamepad.sThumbRX; + } + if (state.Gamepad.sThumbRY != last_right_thumb_y) + { + gamepad_axis_input(5, state.Gamepad.sThumbRY); + last_right_thumb_y = state.Gamepad.sThumbRY; + } + } + else if (input_mapper) + { + INFO_LOG(INPUT, "xinput: Controller '%s' on port %d disconnected", _name.c_str(), _xinput_port); + GamepadDevice::Unregister(xinput_gamepads[_xinput_port]); + input_mapper.reset(); + last_buttons_state = 0; + last_left_trigger = 0; + last_right_trigger = 0; + last_left_thumb_x = 0; + last_left_thumb_y = 0; + last_right_thumb_x = 0; + last_right_thumb_y = 0; + } + } + void rumble(float power, float inclination, u32 duration_ms) override + { + vib_inclination = inclination * power; + vib_stop_time = os_GetSeconds() + duration_ms / 1000.0; + + do_rumble(power); + } + void update_rumble() override + { + if (vib_stop_time > 0) + { + int rem_time = (int)((vib_stop_time - os_GetSeconds()) * 1000.0); + if (rem_time <= 0) + { + vib_stop_time = 0; + do_rumble(0); + } + else if (vib_inclination > 0) + do_rumble(vib_inclination * rem_time); + } + } + + void Open() + { + JOYCAPS joycaps; + int rc = joyGetDevCaps(_xinput_port, &joycaps, sizeof(joycaps)); + if (rc != 0) + _name = "xinput" + std::to_string(_xinput_port); + else + _name = joycaps.szPname; + INFO_LOG(INPUT, "xinput: Opened controller '%s' on port %d", _name.c_str(), _xinput_port); + loadMapping(); + + GamepadDevice::Register(xinput_gamepads[_xinput_port]); + } + + static void CreateDevices() + { + for (int port = 0; port < XUSER_MAX_COUNT; port++) + xinput_gamepads[port] = std::make_shared(port, port); + } + static void CloseDevices() + { + for (int port = 0; port < XUSER_MAX_COUNT; port++) + GamepadDevice::Unregister(xinput_gamepads[port]); + } + + static std::shared_ptr GetXInputDevice(int port) + { + return xinput_gamepads[port]; + } + +protected: + void load_axis_min_max(u32 axis) override + { + if (axis == 0 || axis == 1) + { + axis_ranges[axis] = 255; + axis_min_values[axis] = 0; + } + else + { + axis_ranges[axis] = 65535; + axis_min_values[axis] = -32768; + } + } + +private: + void do_rumble(float power) + { + XINPUT_VIBRATION vib; + + vib.wLeftMotorSpeed = (u16)(65535 * power); + vib.wRightMotorSpeed = (u16)(65535 * power); + + XInputSetState(_xinput_port, &vib); + } + + const int _xinput_port; + u32 last_buttons_state = 0; + u8 last_left_trigger = 0; + u8 last_right_trigger = 0; + s16 last_left_thumb_x = 0; + s16 last_left_thumb_y = 0; + s16 last_right_thumb_x = 0; + s16 last_right_thumb_y = 0; + double vib_stop_time; + float vib_inclination; + static std::vector> xinput_gamepads; +}; + +std::vector> XInputGamepadDevice::xinput_gamepads(XUSER_MAX_COUNT); + +class WinMouse : public Mouse +{ +public: + WinMouse() : Mouse("win32") + { + _unique_id = "win_mouse"; + loadMapping(); + } +}; + diff --git a/core/wsi/context.h b/core/wsi/context.h index e5d20bf23..7b46276a8 100644 --- a/core/wsi/context.h +++ b/core/wsi/context.h @@ -20,6 +20,7 @@ */ #pragma once #include "gl_context.h" +#include "rend/dx9/dxcontext.h" #ifdef USE_VULKAN #include "rend/vulkan/vulkan_context.h" diff --git a/core/wsi/switcher.cpp b/core/wsi/switcher.cpp index 2cfb5178b..8f72ce68e 100644 --- a/core/wsi/switcher.cpp +++ b/core/wsi/switcher.cpp @@ -29,7 +29,7 @@ VulkanContext theVulkanContext; void InitRenderApi() { #ifdef USE_VULKAN - if (!config::RendererType.isOpenGL()) + if (config::RendererType.isVulkan()) { if (theVulkanContext.Init()) return; @@ -38,6 +38,17 @@ void InitRenderApi() config::RendererType = RenderType::OpenGL; config::RendererType.commit(); } +#endif +#ifdef _WIN32 + if (config::RendererType.isDirectX()) + { + if (theDXContext.Init()) + return; + // Fall back to Open GL + WARN_LOG(RENDERER, "DirectX init failed. Falling back to Open GL."); + config::RendererType = RenderType::OpenGL; + config::RendererType.commit(); + } #endif if (!theGLContext.Init()) exit(1); @@ -47,6 +58,9 @@ void TermRenderApi() { #ifdef USE_VULKAN theVulkanContext.Term(); +#endif +#ifdef _WIN32 + theDXContext.Term(); #endif theGLContext.Term(); } diff --git a/core/wsi/wgl.cpp b/core/wsi/wgl.cpp index 5679614d0..4f9bc00df 100644 --- a/core/wsi/wgl.cpp +++ b/core/wsi/wgl.cpp @@ -23,7 +23,6 @@ #if defined(_WIN32) && !defined(USE_SDL) void CreateMainWindow(); -void DestroyMainWindow(); WGLGraphicsContext theGLContext; @@ -137,7 +136,6 @@ void WGLGraphicsContext::Term() wglMakeCurrent(ourWindowHandleToDeviceContext, NULL); wglDeleteContext(ourOpenGLRenderingContext); ourOpenGLRenderingContext = NULL; - DestroyMainWindow(); } } diff --git a/core/wsi/wgl.h b/core/wsi/wgl.h index 76a768e0b..e901290f1 100644 --- a/core/wsi/wgl.h +++ b/core/wsi/wgl.h @@ -58,8 +58,6 @@ typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC) (int interval); class WGLGraphicsContext : public GLGraphicsContext { public: - ~WGLGraphicsContext() { Term(); } - bool Init(); void Term(); void Swap(); diff --git a/shell/android-studio/flycast/src/main/jni/src/Android.cpp b/shell/android-studio/flycast/src/main/jni/src/Android.cpp index 91897aa2c..bdf19d116 100644 --- a/shell/android-studio/flycast/src/main/jni/src/Android.cpp +++ b/shell/android-studio/flycast/src/main/jni/src/Android.cpp @@ -129,6 +129,8 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_emu_JNIdc_screenDpi(JNIEnv *env extern int screen_width,screen_height; +std::shared_ptr mouse; + float vjoy_pos[15][8]; static bool game_started; @@ -536,6 +538,9 @@ JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_init( { input_device_manager = env->NewGlobalRef(obj); input_device_manager_rumble = env->GetMethodID(env->GetObjectClass(obj), "rumble", "(IFFI)Z"); + // FIXME Don't connect it by default or any screen touch will register as button A press + mouse = std::make_shared(-1); + GamepadDevice::Register(mouse); } JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_joystickAdded(JNIEnv *env, jobject obj, jint id, jstring name, jint maple_port, jstring junique_id) @@ -589,17 +594,10 @@ JNIEXPORT jboolean JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_j JNIEXPORT void JNICALL Java_com_reicast_emulator_periph_InputDeviceManager_mouseEvent(JNIEnv *env, jobject obj, jint xpos, jint ypos, jint buttons) { - SetMousePosition(xpos, ypos, screen_width, screen_height); - mo_buttons[0] = 0xFFFF; - if (buttons & 1) // Left - mo_buttons[0] &= ~4; - if (buttons & 2) // Right - mo_buttons[0] &= ~2; - if (buttons & 4) // Middle - mo_buttons[0] &= ~8; - mouse_gamepad.gamepad_btn_input(1, (buttons & 1) != 0); - mouse_gamepad.gamepad_btn_input(2, (buttons & 2) != 0); - mouse_gamepad.gamepad_btn_input(4, (buttons & 4) != 0); + mouse->setAbsPos(xpos, ypos, screen_width, screen_height); + mouse->setButton(Mouse::LEFT_BUTTON, (buttons & 1) != 0); + mouse->setButton(Mouse::RIGHT_BUTTON, (buttons & 2) != 0); + mouse->setButton(Mouse::MIDDLE_BUTTON, (buttons & 4) != 0); } static jobject g_activity; diff --git a/shell/android-studio/flycast/src/main/jni/src/android_gamepad.h b/shell/android-studio/flycast/src/main/jni/src/android_gamepad.h index 090e07b96..c649d02c3 100644 --- a/shell/android-studio/flycast/src/main/jni/src/android_gamepad.h +++ b/shell/android-studio/flycast/src/main/jni/src/android_gamepad.h @@ -128,23 +128,24 @@ public: axis_min_values[DC_AXIS_RT] = 0; axis_ranges[DC_AXIS_RT] = 255; } - else if (!find_mapping()) - { - if (_name == "SHIELD Remote") - input_mapper = std::make_shared(); - else - input_mapper = std::make_shared(); - save_mapping(); - INFO_LOG(INPUT, "using default mapping"); - } else - INFO_LOG(INPUT, "using custom mapping '%s'", input_mapper->name.c_str()); + { + loadMapping(); + save_mapping(); + } } virtual ~AndroidGamepadDevice() override { INFO_LOG(INPUT, "Android: Joystick '%s' on port %d disconnected", _name.c_str(), maple_port()); } + virtual std::shared_ptr getDefaultMapping() override { + if (_name == "SHIELD Remote") + return std::make_shared(); + else + return std::make_shared(); + } + virtual const char *get_button_name(u32 code) override { switch(code) @@ -307,65 +308,13 @@ private: std::map> AndroidGamepadDevice::android_gamepads; -class MouseInputMapping : public InputMapping +class AndroidMouse : public SystemMouse { public: - MouseInputMapping() + AndroidMouse(int maple_port) : SystemMouse("Android", maple_port) { - name = "Android Mouse"; - set_button(DC_BTN_A, 1); - set_button(DC_BTN_B, 2); - set_button(DC_BTN_START, 4); - - dirty = false; - } -}; - -class AndroidMouseGamepadDevice : public GamepadDevice -{ -public: - AndroidMouseGamepadDevice(int maple_port) : GamepadDevice(maple_port, "Android") - { - _name = "Mouse"; _unique_id = "android_mouse"; - if (!find_mapping()) - input_mapper = std::make_shared(); - } - - bool gamepad_btn_input(u32 code, bool pressed) override - { - if (gui_is_open() && !is_detecting_input()) - // Don't register mouse clicks as gamepad presses when gui is open - // This makes the gamepad presses to be handled first and the mouse position to be ignored - // TODO Make this generic - return false; - else - return GamepadDevice::gamepad_btn_input(code, pressed); - } - - virtual const char *get_button_name(u32 code) override - { - switch (code) - { - case 1: - return "Left Button"; - case 2: - return "Right Button"; - case 4: - return "Middle Button"; - case 8: - return "Back Button"; - case 16: - return "Forward Button"; - case 32: - return "Stylus Primary"; - case 64: - return "Stylus Second"; - default: - return nullptr; - } + loadMapping(); } }; -// FIXME Don't connect it by default or any screen touch will register as button A press -AndroidMouseGamepadDevice mouse_gamepad(-1); diff --git a/shell/apple/emulator-osx/emulator-osx/EmuGLView.swift b/shell/apple/emulator-osx/emulator-osx/EmuGLView.swift index 6e11bbbef..dee89d238 100644 --- a/shell/apple/emulator-osx/emulator-osx/EmuGLView.swift +++ b/shell/apple/emulator-osx/emulator-osx/EmuGLView.swift @@ -97,22 +97,18 @@ class EmuGLView: NSOpenGLView, NSWindowDelegate { } override func mouseDown(with event: NSEvent) { emu_mouse_buttons(1, true) - pmo_buttons[0] &= ~(1 << 2) setMousePos(event) } override func mouseUp(with event: NSEvent) { emu_mouse_buttons(1, false) - pmo_buttons[0] |= 1 << 2; setMousePos(event) } override func rightMouseDown(with event: NSEvent) { emu_mouse_buttons(2, true) - pmo_buttons[0] &= ~(1 << 1) setMousePos(event) } override func rightMouseUp(with event: NSEvent) { emu_mouse_buttons(2, false) - pmo_buttons[0] |= 1 << 1 setMousePos(event) } // Not dispatched by default. Need to set Window.acceptsMouseMovedEvents to true @@ -120,30 +116,26 @@ class EmuGLView: NSOpenGLView, NSWindowDelegate { setMousePos(event) } override func mouseDragged(with event: NSEvent) { - pmo_buttons[0] &= ~(1 << 2) + emu_mouse_buttons(1, true) setMousePos(event) } override func rightMouseDragged(with event: NSEvent) { - pmo_buttons[0] &= ~(1 << 1) + emu_mouse_buttons(2, true) setMousePos(event) } override func otherMouseDown(with event: NSEvent) { emu_mouse_buttons(3, true) - pmo_buttons[0] &= ~(1 << 2) setMousePos(event) } override func otherMouseUp(with event: NSEvent) { emu_mouse_buttons(3, false) - pmo_buttons[0] |= 1 << 2 setMousePos(event) } override func scrollWheel(with event: NSEvent) { if (event.hasPreciseScrollingDeltas) { - // 1 per "line" - pmo_wheel_delta[0] -= Float(event.scrollingDeltaY) * 3.2 + emu_mouse_wheel(-Float(event.scrollingDeltaY) / 5) } else { - // 0.1 per wheel notch - pmo_wheel_delta[0] -= Float(event.scrollingDeltaY) * 160 + emu_mouse_wheel(-Float(event.scrollingDeltaY) * 10) } } diff --git a/shell/apple/emulator-osx/emulator-osx/emulator-osx-Bridging-Header.h b/shell/apple/emulator-osx/emulator-osx/emulator-osx-Bridging-Header.h index fbf2c2550..3e7820695 100644 --- a/shell/apple/emulator-osx/emulator-osx/emulator-osx-Bridging-Header.h +++ b/shell/apple/emulator-osx/emulator-osx/emulator-osx-Bridging-Header.h @@ -25,10 +25,9 @@ void emu_key_input(UInt16 keyCode, bool pressed, UInt32 modifierFlags); void emu_character_input(const char *characters); void emu_mouse_buttons(int button, bool pressed); void emu_set_mouse_position(int x, int y, int width, int height); +void emu_mouse_wheel(float v); bool emu_frame_pending(); -extern unsigned int *pmo_buttons; -extern float *pmo_wheel_delta; #ifdef __cplusplus } diff --git a/shell/apple/emulator-osx/emulator-osx/osx-main.mm b/shell/apple/emulator-osx/emulator-osx/osx-main.mm index 4fddd7cf8..b636295d6 100644 --- a/shell/apple/emulator-osx/emulator-osx/osx-main.mm +++ b/shell/apple/emulator-osx/emulator-osx/osx-main.mm @@ -30,11 +30,8 @@ #include "hw/pvr/Renderer_if.h" #include "rend/mainui.h" -OSXKeyboardDevice keyboard(0); -static std::shared_ptr kb_gamepad(0); -static std::shared_ptr mouse_gamepad(0); -unsigned int *pmo_buttons; -float *pmo_wheel_delta; +static std::shared_ptr keyboard(0); +static std::shared_ptr mouse; static UInt32 keyboardModifiers; int darw_printf(const char* text, ...) @@ -85,10 +82,10 @@ void os_SetupInput() input_sdl_init(); #endif - kb_gamepad = std::make_shared(0); - GamepadDevice::Register(kb_gamepad); - mouse_gamepad = std::make_shared(0); - GamepadDevice::Register(mouse_gamepad); + keyboard = std::make_shared(0); + GamepadDevice::Register(keyboard); + mouse = std::make_shared(); + GamepadDevice::Register(mouse); } void common_linux_setup(); @@ -141,10 +138,6 @@ int emu_single_frame(int w, int h) void emu_gles_init(int width, int height) { - // work around https://bugs.swift.org/browse/SR-12263 - pmo_buttons = mo_buttons; - pmo_wheel_delta = mo_wheel_delta; - char *home = getenv("HOME"); if (home != NULL) { @@ -261,37 +254,53 @@ int emu_reicast_init() void emu_key_input(UInt16 keyCode, bool pressed, UInt modifierFlags) { if (keyCode != 0xFF) - keyboard.keyboard_input(keyCode, pressed, 0); + keyboard->keyboard_input(keyCode, pressed, 0); else { // Modifier keys UInt32 changes = keyboardModifiers ^ modifierFlags; if (changes & NSEventModifierFlagShift) - keyboard.keyboard_input(kVK_Shift, modifierFlags & NSEventModifierFlagShift, 0); + keyboard->keyboard_input(kVK_Shift, modifierFlags & NSEventModifierFlagShift, 0); if (changes & NSEventModifierFlagControl) - keyboard.keyboard_input(kVK_Control, modifierFlags & NSEventModifierFlagControl, 0); + keyboard->keyboard_input(kVK_Control, modifierFlags & NSEventModifierFlagControl, 0); if (changes & NSEventModifierFlagOption) - keyboard.keyboard_input(kVK_Option, modifierFlags & NSEventModifierFlagOption, 0); + keyboard->keyboard_input(kVK_Option, modifierFlags & NSEventModifierFlagOption, 0); keyboardModifiers = modifierFlags; } - if ((modifierFlags - & (NSEventModifierFlagShift | NSEventModifierFlagControl | NSEventModifierFlagOption | NSEventModifierFlagCommand)) == 0) - kb_gamepad->gamepad_btn_input(keyCode, pressed); } void emu_character_input(const char *characters) { if (characters != NULL) - while (*characters != '\0') - keyboard.keyboard_character(*characters++); + gui_keyboard_inputUTF8(characters); } void emu_mouse_buttons(int button, bool pressed) { - mouse_gamepad->gamepad_btn_input(button, pressed); + Mouse::Button dcButton; + switch (button) { + case 1: + dcButton = Mouse::LEFT_BUTTON; + break; + case 2: + dcButton = Mouse::RIGHT_BUTTON; + break; + case 3: + dcButton = Mouse::MIDDLE_BUTTON; + break; + default: + dcButton = Mouse::BUTTON_4; + break; + } + mouse->setButton(dcButton, pressed); +} + +void emu_mouse_wheel(float v) +{ + mouse->setWheel((int)v); } void emu_set_mouse_position(int x, int y, int width, int height) { - SetMousePosition(x, y, width, height); + mouse->setAbsPos(x, y, width, height); } std::string os_Locale(){ diff --git a/shell/apple/emulator-osx/emulator-osx/osx_gamepad.h b/shell/apple/emulator-osx/emulator-osx/osx_gamepad.h index 18b9b57ef..6c7fe13e9 100644 --- a/shell/apple/emulator-osx/emulator-osx/osx_gamepad.h +++ b/shell/apple/emulator-osx/emulator-osx/osx_gamepad.h @@ -7,290 +7,13 @@ // #include "input/gamepad_device.h" -class KbInputMapping : public InputMapping +class OSXMouse : public SystemMouse { public: - KbInputMapping() + OSXMouse() : SystemMouse("OSX") { - name = "OSX Keyboard"; - set_button(DC_BTN_A, kVK_ANSI_X); - set_button(DC_BTN_B, kVK_ANSI_C); - set_button(DC_BTN_X, kVK_ANSI_S); - set_button(DC_BTN_Y, kVK_ANSI_D); - set_button(DC_DPAD_UP, kVK_UpArrow); - set_button(DC_DPAD_DOWN, kVK_DownArrow); - set_button(DC_DPAD_LEFT, kVK_LeftArrow); - set_button(DC_DPAD_RIGHT, kVK_RightArrow); - set_button(DC_BTN_START, kVK_Return); - set_button(EMU_BTN_TRIGGER_LEFT, kVK_ANSI_F); - set_button(EMU_BTN_TRIGGER_RIGHT, kVK_ANSI_V); - set_button(EMU_BTN_MENU, kVK_Tab); - set_button(EMU_BTN_FFORWARD, kVK_Space); - - dirty = false; - } -}; - -class OSXKbGamepadDevice : public GamepadDevice -{ -public: - OSXKbGamepadDevice(int maple_port) : GamepadDevice(maple_port, "OSX") - { - _name = "Keyboard"; - _unique_id = "osx_keyboard"; - if (!find_mapping()) - input_mapper = std::make_shared(); - } - - virtual const char *get_button_name(u32 code) override - { - switch(code) - { - case kVK_ANSI_A: - return "A"; - case kVK_ANSI_B: - return "B"; - case kVK_ANSI_C: - return "C"; - case kVK_ANSI_D: - return "D"; - case kVK_ANSI_E: - return "E"; - case kVK_ANSI_F: - return "F"; - case kVK_ANSI_G: - return "G"; - case kVK_ANSI_H: - return "H"; - case kVK_ANSI_I: - return "I"; - case kVK_ANSI_J: - return "J"; - case kVK_ANSI_K: - return "K"; - case kVK_ANSI_L: - return "L"; - case kVK_ANSI_M: - return "M"; - case kVK_ANSI_N: - return "N"; - case kVK_ANSI_O: - return "O"; - case kVK_ANSI_P: - return "P"; - case kVK_ANSI_Q: - return "Q"; - case kVK_ANSI_R: - return "R"; - case kVK_ANSI_S: - return "S"; - case kVK_ANSI_T: - return "T"; - case kVK_ANSI_U: - return "U"; - case kVK_ANSI_V: - return "V"; - case kVK_ANSI_W: - return "W"; - case kVK_ANSI_X: - return "X"; - case kVK_ANSI_Y: - return "Y"; - case kVK_ANSI_Z: - return "Z"; - - case kVK_UpArrow: - return "Up"; - case kVK_DownArrow: - return "Down"; - case kVK_LeftArrow: - return "Left"; - case kVK_RightArrow: - return "Right"; - case kVK_Return: - return "Return"; - case kVK_Tab: - return "Tab"; - case kVK_Space: - return "Space"; - case kVK_Delete: - return "Delete"; - case kVK_Escape: - return "Escape"; - case kVK_Help: - return "Help"; - case kVK_Home: - return "Home"; - case kVK_PageUp: - return "Page Up"; - case kVK_PageDown: - return "Page Down"; - case kVK_ForwardDelete: - return "Fwd Delete"; - case kVK_End: - return "End"; - - case kVK_ANSI_1: - return "1"; - case kVK_ANSI_2: - return "2"; - case kVK_ANSI_3: - return "3"; - case kVK_ANSI_4: - return "4"; - case kVK_ANSI_5: - return "5"; - case kVK_ANSI_6: - return "6"; - case kVK_ANSI_7: - return "7"; - case kVK_ANSI_8: - return "8"; - case kVK_ANSI_9: - return "9"; - case kVK_ANSI_0: - return "0"; - - case kVK_ANSI_Equal: - return "="; - case kVK_ANSI_Minus: - return "-"; - case kVK_ANSI_RightBracket: - return "]"; - case kVK_ANSI_LeftBracket: - return "["; - case kVK_ANSI_Quote: - return "'"; - case kVK_ANSI_Semicolon: - return ";"; - case kVK_ANSI_Backslash: - return "\\"; - case kVK_ANSI_Comma: - return ","; - case kVK_ANSI_Slash: - return "/"; - case kVK_ANSI_Period: - return "."; - case kVK_ANSI_Grave: - return "`"; - - case kVK_ANSI_KeypadDecimal: - return "Keypad ."; - case kVK_ANSI_KeypadMultiply: - return "Keypad *"; - case kVK_ANSI_KeypadPlus: - return "Keypad +"; - case kVK_ANSI_KeypadClear: - return "Keypad Clear"; - case kVK_ANSI_KeypadDivide: - return "Keypad /"; - case kVK_ANSI_KeypadEnter: - return "Keypad Enter"; - case kVK_ANSI_KeypadMinus: - return "Keypad -"; - case kVK_ANSI_KeypadEquals: - return "Keypad ="; - case kVK_ANSI_Keypad0: - return "Keypad 0"; - case kVK_ANSI_Keypad1: - return "Keypad 1"; - case kVK_ANSI_Keypad2: - return "Keypad 2"; - case kVK_ANSI_Keypad3: - return "Keypad 3"; - case kVK_ANSI_Keypad4: - return "Keypad 4"; - case kVK_ANSI_Keypad5: - return "Keypad 5"; - case kVK_ANSI_Keypad6: - return "Keypad 6"; - case kVK_ANSI_Keypad7: - return "Keypad 7"; - case kVK_ANSI_Keypad8: - return "Keypad 8"; - case kVK_ANSI_Keypad9: - return "Keypad 9"; - - case kVK_F1: - return "F1"; - case kVK_F2: - return "F2"; - case kVK_F3: - return "F3"; - case kVK_F4: - return "F4"; - case kVK_F5: - return "F5"; - case kVK_F6: - return "F6"; - case kVK_F7: - return "F7"; - case kVK_F8: - return "F8"; - case kVK_F9: - return "F9"; - case kVK_F10: - return "F10"; - case kVK_F11: - return "F11"; - case kVK_F12: - return "F12"; - - default: - return nullptr; - } - } -}; - -class MouseInputMapping : public InputMapping -{ -public: - MouseInputMapping() - { - name = "OSX Mouse"; - set_button(DC_BTN_A, 1); // Left button - set_button(DC_BTN_B, 2); // Right button - set_button(DC_BTN_START, 3); // Other button - - dirty = false; - } -}; - -class OSXMouseGamepadDevice : public GamepadDevice -{ -public: - OSXMouseGamepadDevice(int maple_port) : GamepadDevice(maple_port, "OSX") - { - _name = "Mouse"; _unique_id = "osx_mouse"; - if (!find_mapping()) - input_mapper = std::make_shared(); - } - - bool gamepad_btn_input(u32 code, bool pressed) override - { - if (gui_is_open() && !is_detecting_input()) - // Don't register mouse clicks as gamepad presses when gui is open - // This makes the gamepad presses to be handled first and the mouse position to be ignored - // TODO Make this generic - return false; - else - return GamepadDevice::gamepad_btn_input(code, pressed); - } - - virtual const char *get_button_name(u32 code) override - { - switch (code) - { - case 1: - return "Left Button"; - case 2: - return "Right Button"; - case 3: - return "Other Button"; - default: - return nullptr; - } + loadMapping(); } }; diff --git a/shell/apple/emulator-osx/emulator-osx/osx_keyboard.h b/shell/apple/emulator-osx/emulator-osx/osx_keyboard.h index 18581c05a..3737546c3 100644 --- a/shell/apple/emulator-osx/emulator-osx/osx_keyboard.h +++ b/shell/apple/emulator-osx/emulator-osx/osx_keyboard.h @@ -8,11 +8,15 @@ #pragma once #include "input/keyboard_device.h" -class OSXKeyboardDevice : public KeyboardDeviceTemplate +class OSXKeyboard : public KeyboardDeviceTemplate { public: - OSXKeyboardDevice(int maple_port) : KeyboardDeviceTemplate(maple_port) + OSXKeyboard(int maple_port) : KeyboardDeviceTemplate(maple_port, "OSX") { + _name = "Keyboard"; + _unique_id = "osx_keyboard"; + loadMapping(); + //04-1D Letter keys A-Z (in alphabetic order) kb_map[kVK_ANSI_A] = 0x04; kb_map[kVK_ANSI_B] = 0x05; @@ -160,10 +164,8 @@ public: kb_map[kVK_JIS_Yen] = 0x89; // I18n keyboard 3 } - virtual const char* name() override { return "OSX Keyboard"; } - protected: - virtual u8 convert_keycode(UInt16 keycode) override + u8 convert_keycode(UInt16 keycode) override { return kb_map[keycode]; } diff --git a/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj b/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj index 36de35cc1..ae65a5076 100644 --- a/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj +++ b/shell/apple/emulator-osx/reicast-osx.xcodeproj/project.pbxproj @@ -2207,7 +2207,6 @@ AEE6278122131BB500EC7E89 /* gamepad.h */, AEE6278222131BB500EC7E89 /* gamepad_device.cpp */, AEE6278322131BB500EC7E89 /* gamepad_device.h */, - AEE6279522247C2B00EC7E89 /* keyboard_device.cpp */, AEE6278422131BB500EC7E89 /* keyboard_device.h */, AEE6278522131BB500EC7E89 /* mapping.cpp */, AEE6278622131BB500EC7E89 /* mapping.h */, @@ -2668,7 +2667,6 @@ AE82C6B225B64AE200C79BC2 /* zip_get_num_files.c in Sources */, F219065A265C247D00AA2ACA /* inflate.c in Sources */, AE82C6C425B64AE200C79BC2 /* zip_stat.c in Sources */, - AEE6279622247C2B00EC7E89 /* keyboard_device.cpp in Sources */, AE82C69225B64AE200C79BC2 /* zip_source_tell.c in Sources */, AE82C6D125B64AE200C79BC2 /* zip_source_write.c in Sources */, AEFF7276265901BF003E8022 /* libchdr_flac.c in Sources */, diff --git a/shell/linux/Makefile b/shell/linux/Makefile index 9c1ee6ca1..addc7d4b8 100644 --- a/shell/linux/Makefile +++ b/shell/linux/Makefile @@ -272,7 +272,7 @@ else ifneq (,$(findstring win32,$(platform))) NOT_ARM := 1 CFLAGS += -fno-builtin-sqrtf -funroll-loops -I /mingw64/include LDFLAGS += -static-libgcc -static-libstdc++ -Wl,-subsystem,windows - LIBS := -lopengl32 -lwinmm -lgdi32 -lwsock32 -lws2_32 -ldsound -lcomctl32 -lcomdlg32 -lxinput -liphlpapi -Wl,-Bstatic -lgomp + LIBS := -lopengl32 -lwinmm -lgdi32 -lwsock32 -lws2_32 -ldsound -lcomctl32 -lcomdlg32 -lxinput -liphlpapi -ld3d9 -ld3dx9 -Wl,-Bstatic -lgomp PLATFORM_EXT := exe CC = gcc CXX = g++ @@ -502,7 +502,6 @@ ifdef USE_SDL $(OBJECTS): SDL/lib/libSDL2.a SDL/lib/libSDL2.a: - -patch -p1 --directory=$(RZDCY_SRC_DIR)/deps/SDL --forward < $(RZDCY_SRC_DIR)/deps/patches/SDL.patch mkdir -p SDL && \ cd SDL && \ cmake $(CMAKE_FLAVOR) -DCMAKE_INSTALL_PREFIX=. -DBUILD_SHARED_LIBS=OFF -DCMAKE_SH=SH-NOTFOUND $(RZDCY_SRC_DIR)/deps/SDL && \ @@ -511,7 +510,7 @@ SDL/lib/libSDL2.a: OBJECTS += SDL/lib/libSDL2.a LIBS += -ldinput8 -ldxguid -ldxerr8 -LIBS += -luser32 -lgdi32 -lwinmm -limm32 -lole32 -loleaut32 -lshell32 -lsetupapi -lversion -luuid +LIBS += -luser32 -limm32 -lole32 -loleaut32 -lshell32 -lsetupapi -lversion -luuid endif endif diff --git a/tests/src/test_stubs.cpp b/tests/src/test_stubs.cpp index 0f9243d72..8a35d1a0d 100644 --- a/tests/src/test_stubs.cpp +++ b/tests/src/test_stubs.cpp @@ -12,10 +12,12 @@ void os_DebugBreak() #endif } -void* libPvr_GetRenderTarget() +#ifdef _WIN32 +HWND getNativeHwnd() { - return nullptr; + return (HWND)NULL; } +#endif void os_SetupInput() { @@ -49,7 +51,4 @@ double os_GetSeconds() static LARGE_INTEGER time_now_base = time_now; return (time_now.QuadPart - time_now_base.QuadPart)*qpfd; } -void DestroyMainWindow() -{ -} #endif