diff --git a/src/d3d9/d3d9_cursor.cpp b/src/d3d9/d3d9_cursor.cpp index 0224c776b4ee..5c6c95051367 100644 --- a/src/d3d9/d3d9_cursor.cpp +++ b/src/d3d9/d3d9_cursor.cpp @@ -6,6 +6,22 @@ namespace dxvk { #ifdef _WIN32 + void D3D9Cursor::ResetCursor() { + ShowCursor(FALSE); + + if (likely(m_hCursor != nullptr)) { + ::DestroyCursor(m_hCursor); + m_hCursor = nullptr; + } else { + m_sCursor.Bitmap.clear(); + m_sCursor.Width = 0; + m_sCursor.Height = 0; + m_sCursor.X = 0; + m_sCursor.Y = 0; + } + } + + void D3D9Cursor::UpdateCursor(int X, int Y) { POINT currentPos = { }; if (::GetCursorPos(¤tPos) && currentPos == POINT{ X, Y }) @@ -15,11 +31,18 @@ namespace dxvk { } + void D3D9Cursor::RefreshSoftwareCursorPosition() { + POINT currentPos = { }; + ::GetCursorPos(¤tPos); + + m_sCursor.X = static_cast(currentPos.x); + m_sCursor.Y = static_cast(currentPos.y); + } + + BOOL D3D9Cursor::ShowCursor(BOOL bShow) { if (likely(m_hCursor != nullptr)) ::SetCursor(bShow ? m_hCursor : nullptr); - else - Logger::debug("D3D9Cursor::ShowCursor: Software cursor not implemented."); return std::exchange(m_visible, bShow); } @@ -48,12 +71,38 @@ namespace dxvk { return D3D_OK; } + + + HRESULT D3D9Cursor::SetSoftwareCursor(UINT Width, UINT Height, UINT XHotSpot, UINT YHotSpot) { + // Make sure to hide the win32 cursor + ::SetCursor(nullptr); + + m_sCursor.Width = Width; + m_sCursor.Height = Height; + m_sCursor.X = XHotSpot; + m_sCursor.Y = YHotSpot; + + ShowCursor(m_visible); + + return D3D_OK; + } + #else + void D3D9Cursor::ResetCursor() { + Logger::warn("D3D9Cursor::ResetCursor: Not supported on current platform."); + } + + void D3D9Cursor::UpdateCursor(int X, int Y) { Logger::warn("D3D9Cursor::UpdateCursor: Not supported on current platform."); } + void D3D9Cursor::RefreshSoftwareCursorPosition() { + Logger::warn("D3D9Cursor::RefreshSoftwareCursorPosition: Not supported on current platform."); + } + + BOOL D3D9Cursor::ShowCursor(BOOL bShow) { Logger::warn("D3D9Cursor::ShowCursor: Not supported on current platform."); return std::exchange(m_visible, bShow); @@ -65,6 +114,12 @@ namespace dxvk { return D3D_OK; } + + HRESULT D3D9Cursor::SetSoftwareCursor(UINT XHotSpot, UINT YHotSpot, Com pCursorBitmap) { + Logger::warn("D3D9Cursor::SetSoftwareCursor: Not supported on current platform."); + + return D3D_OK; + } #endif } diff --git a/src/d3d9/d3d9_cursor.h b/src/d3d9/d3d9_cursor.h index b2ca5537d023..533fe09abf0e 100644 --- a/src/d3d9/d3d9_cursor.h +++ b/src/d3d9/d3d9_cursor.h @@ -4,15 +4,26 @@ namespace dxvk { - constexpr uint32_t HardwareCursorWidth = 32u; - constexpr uint32_t HardwareCursorHeight = 32u; + /** + * \brief D3D9 Software Cursor + */ + struct D3D9_SOFTWARE_CURSOR { + std::vector Bitmap; + UINT Width = 0; + UINT Height = 0; + UINT X = 0; + UINT Y = 0; + }; + + constexpr uint32_t HardwareCursorWidth = 32u; + constexpr uint32_t HardwareCursorHeight = 32u; constexpr uint32_t HardwareCursorFormatSize = 4u; constexpr uint32_t HardwareCursorPitch = HardwareCursorWidth * HardwareCursorFormatSize; // Format Size of 4 bytes (ARGB) using CursorBitmap = uint8_t[HardwareCursorHeight * HardwareCursorPitch]; // Monochrome mask (1 bit) - using CursorMask = uint8_t[HardwareCursorHeight * HardwareCursorWidth / 8]; + using CursorMask = uint8_t[HardwareCursorHeight * HardwareCursorWidth / 8]; class D3D9Cursor { @@ -25,18 +36,37 @@ namespace dxvk { } #endif + void ResetCursor(); + void UpdateCursor(int X, int Y); + void RefreshSoftwareCursorPosition(); + BOOL ShowCursor(BOOL bShow); HRESULT SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap); + HRESULT SetSoftwareCursor(UINT Width, UINT Height, UINT XHotSpot, UINT YHotSpot); + + D3D9_SOFTWARE_CURSOR* GetSoftwareCursor() { + return &m_sCursor; + } + + BOOL IsSoftwareCursor() const { + return m_sCursor.Bitmap.size() > 0; + } + + BOOL IsCursorVisible() const { + return m_visible; + } + private: - BOOL m_visible = FALSE; + BOOL m_visible = FALSE; + D3D9_SOFTWARE_CURSOR m_sCursor; #ifdef _WIN32 - HCURSOR m_hCursor = nullptr; + HCURSOR m_hCursor = nullptr; #endif }; diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index ebab7d20d7cf..d84b1715e829 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -354,14 +354,14 @@ namespace dxvk { hwCursor |= inputWidth <= HardwareCursorWidth || inputHeight <= HardwareCursorHeight; - if (hwCursor) { - D3DLOCKED_BOX lockedBox; - HRESULT hr = LockImage(cursorTex, 0, 0, &lockedBox, nullptr, D3DLOCK_READONLY); - if (FAILED(hr)) - return hr; + D3DLOCKED_BOX lockedBox; + HRESULT hr = LockImage(cursorTex, 0, 0, &lockedBox, nullptr, D3DLOCK_READONLY); + if (FAILED(hr)) + return hr; - const uint8_t* data = reinterpret_cast(lockedBox.pBits); + const uint8_t* data = reinterpret_cast(lockedBox.pBits); + if (hwCursor) { // Windows works with a stride of 128, lets respect that. // Copy data to the bitmap... CursorBitmap bitmap = { 0 }; @@ -376,10 +376,57 @@ namespace dxvk { // Set this as our cursor. return m_cursor.SetHardwareCursor(XHotSpot, YHotSpot, bitmap); + } else { + // The cursor bitmap passed by the application has the potential + // to not be clipped to the correct dimensions, so we need to + // discard any transparent edges and keep only a tight rectangle + // bounded by the cursor's visible edge pixels + uint32_t leftEdge = inputWidth * HardwareCursorFormatSize; + uint32_t topEdge = inputHeight; + uint32_t rightEdge = 0; + uint32_t bottomEdge = 0; + + uint32_t rowPitch = inputWidth * HardwareCursorFormatSize; + + for (uint32_t h = 0; h < inputHeight; h++) { + uint32_t rowOffset = h * rowPitch; + for (uint32_t w = 0; w < rowPitch; w += HardwareCursorFormatSize) { + // Examine only pixels with non-zero alpha + if (data[rowOffset + w + 3] != 0) { + if (leftEdge > w) leftEdge = w; + if (topEdge > h) topEdge = h; + if (rightEdge < w) rightEdge = w; + if (bottomEdge < h) bottomEdge = h; + } + } + } + leftEdge /= HardwareCursorFormatSize; + rightEdge /= HardwareCursorFormatSize; + + if (leftEdge > rightEdge || topEdge > bottomEdge) + return D3DERR_INVALIDCALL; + + // Calculate clipped bitmap dimensions + uint32_t clippedInputWidth = rightEdge + 1 - leftEdge + 1; + uint32_t clippedInputHeight = bottomEdge + 1 - topEdge + 1; + // Windows works with a stride of 128, lets respect that. + uint32_t clippedCopyPitch = clippedInputWidth * HardwareCursorFormatSize; + + D3D9_SOFTWARE_CURSOR* pSoftwareCursor = m_cursor.GetSoftwareCursor(); + pSoftwareCursor->Bitmap.resize(clippedInputHeight * clippedCopyPitch); + uint8_t* clippedBitmap = &pSoftwareCursor->Bitmap[0]; + + for (uint32_t h = 0; h < clippedInputHeight; h++) + std::memcpy(&clippedBitmap[h * clippedCopyPitch], + &data[(h + topEdge) * lockedBox.RowPitch + leftEdge * HardwareCursorFormatSize], clippedCopyPitch); + + UnlockImage(cursorTex, 0, 0); + + m_implicitSwapchain->SetCursorTexture(clippedInputWidth, clippedInputHeight, &clippedBitmap[0]); + + return m_cursor.SetSoftwareCursor(clippedInputWidth, clippedInputHeight, XHotSpot, YHotSpot); } - // Software Cursor... - Logger::warn("D3D9DeviceEx::SetCursorProperties: Software cursor not implemented."); return D3D_OK; } @@ -459,6 +506,7 @@ namespace dxvk { } m_flags.clr(D3D9DeviceFlag::InScene); + m_cursor.ResetCursor(); /* * Before calling the IDirect3DDevice9::Reset method for a device, @@ -3852,6 +3900,19 @@ namespace dxvk { HWND hDestWindowOverride, const RGNDATA* pDirtyRegion, DWORD dwFlags) { + + if (m_cursor.IsSoftwareCursor()) { + m_cursor.RefreshSoftwareCursorPosition(); + + D3D9_SOFTWARE_CURSOR* pSoftwareCursor = m_cursor.GetSoftwareCursor(); + + UINT cursorWidth = m_cursor.IsCursorVisible() ? pSoftwareCursor->Width : 0; + UINT cursorHeight = m_cursor.IsCursorVisible() ? pSoftwareCursor->Height : 0; + + m_implicitSwapchain->SetCursorPosition(pSoftwareCursor->X, pSoftwareCursor->Y, + cursorWidth, cursorHeight); + } + return m_implicitSwapchain->Present( pSourceRect, pDestRect, diff --git a/src/d3d9/d3d9_swapchain.cpp b/src/d3d9/d3d9_swapchain.cpp index 902e7f6f6321..be4a4564a26a 100644 --- a/src/d3d9/d3d9_swapchain.cpp +++ b/src/d3d9/d3d9_swapchain.cpp @@ -748,6 +748,39 @@ namespace dxvk { } + void D3D9SwapChainEx::SetCursorTexture(UINT Width, UINT Height, uint8_t* pCursorBitmap) { + VkExtent2D cursorSize = { uint32_t(Width), uint32_t(Height) }; + + m_parent->EmitCs([ + cBlitter = m_blitter, + cExtent = cursorSize, + cFormat = VK_FORMAT_B8G8R8A8_UNORM, + cData = pCursorBitmap + ] (DxvkContext* ctx) { + cBlitter->setCursorTexture( + cExtent, + cFormat, + (void *) cData); + }); + } + + + void D3D9SwapChainEx::SetCursorPosition(UINT X, UINT Y, UINT Width, UINT Height) { + VkOffset2D cursorPosition = { int32_t(X), int32_t(Y) }; + VkExtent2D cursorSize = { uint32_t(Width), uint32_t(Height) }; + + VkRect2D cursorRect = { cursorPosition, cursorSize }; + + m_parent->EmitCs([ + cBlitter = m_blitter, + cRect = cursorRect + ] (DxvkContext* ctx) { + cBlitter->setCursorPos( + cRect); + }); + } + + HRESULT D3D9SwapChainEx::SetDialogBoxMode(bool bEnableDialogs) { D3D9DeviceLock lock = m_parent->LockDevice(); diff --git a/src/d3d9/d3d9_swapchain.h b/src/d3d9/d3d9_swapchain.h index ac0b36ec94b9..b28f32325695 100644 --- a/src/d3d9/d3d9_swapchain.h +++ b/src/d3d9/d3d9_swapchain.h @@ -118,6 +118,10 @@ namespace dxvk { void Invalidate(HWND hWindow); + void SetCursorTexture(UINT Width, UINT Height, uint8_t* pCursorBitmap); + + void SetCursorPosition(UINT X, UINT Y, UINT Width, UINT Height); + HRESULT SetDialogBoxMode(bool bEnableDialogs); D3D9Surface* GetBackBuffer(UINT iBackBuffer);