diff --git a/dxvk.conf b/dxvk.conf index 44b1be67..35eb157e 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -625,6 +625,17 @@ # d3d9.seamlessCubes = False +# Software Cursor Emulation +# +# Will atempt to emulate a software cursor by using a hardware cursor. This is useful +# for applications that rely on d3d9 API calls to create the cursor and potentially use +# bitmap sizes in excess of 32 x 32. +# +# Supported values: +# - True/False + +# d3d9.softwareCursorEmulation = False + # Debug Utils # # Enables debug utils as this is off by default, this enables user annotations like BeginEvent()/EndEvent(). diff --git a/src/d3d9/d3d9_cursor.cpp b/src/d3d9/d3d9_cursor.cpp index 16ec41d6..fb1b2449 100644 --- a/src/d3d9/d3d9_cursor.cpp +++ b/src/d3d9/d3d9_cursor.cpp @@ -25,25 +25,53 @@ namespace dxvk { } - HRESULT D3D9Cursor::SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap) { - DWORD mask[32]; - std::memset(mask, ~0, sizeof(mask)); + HRESULT D3D9Cursor::SetHardwareCursor( + UINT XHotSpot, + UINT YHotSpot, + const std::vector& bitmap, + bool cursorEmulation, + UINT width, + UINT height, + HWND window) { + bool noPreviousHardwareCursor = false; + + uint32_t cursorWidth = cursorEmulation ? width : HardwareCursorWidth; + uint32_t cursorHeight = cursorEmulation ? height : HardwareCursorHeight; + + // For color icons, the hbmMask and hbmColor bitmaps + // are the same size, each of which is the size of the icon. + std::vector mask(cursorWidth * cursorHeight, ~0); ICONINFO info; info.fIcon = FALSE; info.xHotspot = XHotSpot; info.yHotspot = YHotSpot; - info.hbmMask = ::CreateBitmap(HardwareCursorWidth, HardwareCursorHeight, 1, 1, mask); - info.hbmColor = ::CreateBitmap(HardwareCursorWidth, HardwareCursorHeight, 1, 32, &bitmap[0]); + info.hbmMask = ::CreateBitmap(cursorWidth, cursorHeight, 1, 1, &mask[0]); + info.hbmColor = ::CreateBitmap(cursorWidth, cursorHeight, 1, 32, &bitmap[0]); if (m_hCursor != nullptr) ::DestroyCursor(m_hCursor); + else + noPreviousHardwareCursor = true; m_hCursor = ::CreateIconIndirect(&info); ::DeleteObject(info.hbmMask); ::DeleteObject(info.hbmColor); + if (cursorEmulation) { + ::SetClassLongPtr(window, GCLP_HCURSOR, reinterpret_cast(m_hCursor)); + + CURSORINFO ci; + while (::GetCursorInfo(&ci) && ci.flags == 0u) + ::ShowCursor(TRUE); + + // Castle Strike needs one extra initial increment + // to display the emulated cursor on its menu + if (noPreviousHardwareCursor) + ::ShowCursor(TRUE); + } + ShowCursor(m_visible); return D3D_OK; @@ -60,7 +88,14 @@ namespace dxvk { } - HRESULT D3D9Cursor::SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap) { + HRESULT D3D9Cursor::SetHardwareCursor( + UINT XHotSpot, + UINT YHotSpot, + const std::vector& bitmap, + bool cursorEmulation, + UINT width, + UINT height, + HWND window) { Logger::warn("D3D9Cursor::SetHardwareCursor: Not supported on current platform."); return D3D_OK; diff --git a/src/d3d9/d3d9_cursor.h b/src/d3d9/d3d9_cursor.h index d69e3974..6afe119c 100644 --- a/src/d3d9/d3d9_cursor.h +++ b/src/d3d9/d3d9_cursor.h @@ -9,9 +9,6 @@ namespace dxvk { constexpr uint32_t HardwareCursorFormatSize = 4u; constexpr uint32_t HardwareCursorPitch = HardwareCursorWidth * HardwareCursorFormatSize; - // Format Size of 4 bytes (ARGB) - using CursorBitmap = uint8_t[HardwareCursorHeight * HardwareCursorPitch]; - class D3D9Cursor { public: @@ -20,7 +17,14 @@ namespace dxvk { BOOL ShowCursor(BOOL bShow); - HRESULT SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap); + HRESULT SetHardwareCursor( + UINT XHotSpot, + UINT YHotSpot, + const std::vector& bitmap, + bool cursorEmulation, + UINT width, + UINT height, + HWND window); private: diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index e99ad383..84f8158f 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -345,7 +345,7 @@ namespace dxvk { // Always use a hardware cursor when windowed. D3DPRESENT_PARAMETERS params; m_implicitSwapchain->GetPresentParameters(¶ms); - bool hwCursor = params.Windowed; + bool hwCursor = params.Windowed || m_d3d9Options.softwareCursorEmulation; // Always use a hardware cursor w/h <= 32 px hwCursor |= inputWidth <= HardwareCursorWidth @@ -359,20 +359,32 @@ namespace dxvk { const uint8_t* data = reinterpret_cast(lockedBox.pBits); + uint32_t emulatedCursorPitch = inputWidth * HardwareCursorFormatSize; + + uint32_t cursorHeight = m_d3d9Options.softwareCursorEmulation ? inputHeight : HardwareCursorHeight; + uint32_t cursorPitch = m_d3d9Options.softwareCursorEmulation ? emulatedCursorPitch : HardwareCursorPitch; + // Windows works with a stride of 128, lets respect that. // Copy data to the bitmap... - CursorBitmap bitmap = { 0 }; + // Format Size of 4 bytes (ARGB) + std::vector bitmap(cursorHeight * cursorPitch, 0); size_t copyPitch = std::min( - HardwareCursorPitch, + cursorPitch, inputWidth * inputHeight * HardwareCursorFormatSize); - for (uint32_t h = 0; h < HardwareCursorHeight; h++) - std::memcpy(&bitmap[h * HardwareCursorPitch], &data[h * lockedBox.RowPitch], copyPitch); + for (uint32_t h = 0; h < cursorHeight; h++) + std::memcpy(&bitmap[h * cursorPitch], &data[h * lockedBox.RowPitch], copyPitch); UnlockImage(cursorTex, 0, 0); // Set this as our cursor. - return m_cursor.SetHardwareCursor(XHotSpot, YHotSpot, bitmap); + return m_cursor.SetHardwareCursor(XHotSpot, + YHotSpot, + bitmap, + m_d3d9Options.softwareCursorEmulation, + inputWidth, + inputHeight, + m_window); } // Software Cursor... diff --git a/src/d3d9/d3d9_options.cpp b/src/d3d9/d3d9_options.cpp index fc3d0b48..b364fe5e 100644 --- a/src/d3d9/d3d9_options.cpp +++ b/src/d3d9/d3d9_options.cpp @@ -77,6 +77,7 @@ namespace dxvk { this->samplerLodBias = config.getOption ("d3d9.samplerLodBias", 0.0f); this->clampNegativeLodBias = config.getOption ("d3d9.clampNegativeLodBias", false); this->countLosableResources = config.getOption ("d3d9.countLosableResources", true); + this->softwareCursorEmulation = config.getOption ("d3d9.softwareCursorEmulation", false); // Clamp LOD bias so that people don't abuse this in unintended ways this->samplerLodBias = dxvk::fclamp(this->samplerLodBias, -2.0f, 1.0f); diff --git a/src/d3d9/d3d9_options.h b/src/d3d9/d3d9_options.h index 034c85d3..a12cb38e 100644 --- a/src/d3d9/d3d9_options.h +++ b/src/d3d9/d3d9_options.h @@ -144,6 +144,9 @@ namespace dxvk { /// Clamps negative LOD bias bool clampNegativeLodBias; + /// Emulate a software cursor using a hardware cursor + bool softwareCursorEmulation; + /// How much virtual memory will be used for textures (in MB). int32_t textureMemory;