1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2024-12-11 10:24:10 +01:00

[d3d9] Implement a software cursor

This commit is contained in:
WinterSnowfall 2024-09-28 15:53:02 +03:00 committed by Philip Rebohle
parent 87a7882812
commit 7ff5321910
5 changed files with 203 additions and 16 deletions

View File

@ -6,6 +6,21 @@
namespace dxvk {
#ifdef _WIN32
void D3D9Cursor::ResetCursor() {
ShowCursor(FALSE);
if (likely(m_hCursor != nullptr)) {
::DestroyCursor(m_hCursor);
m_hCursor = nullptr;
} else {
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(&currentPos) && currentPos == POINT{ X, Y })
@ -15,17 +30,31 @@ namespace dxvk {
}
void D3D9Cursor::RefreshSoftwareCursorPosition() {
POINT currentPos = { };
::GetCursorPos(&currentPos);
m_sCursor.X = static_cast<UINT>(currentPos.x);
m_sCursor.Y = static_cast<UINT>(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);
}
HRESULT D3D9Cursor::SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap) {
if (unlikely(IsSoftwareCursor())) {
m_sCursor.Width = 0;
m_sCursor.Height = 0;
m_sCursor.X = 0;
m_sCursor.Y = 0;
}
CursorMask mask;
std::memset(mask, ~0, sizeof(mask));
@ -48,12 +77,43 @@ 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);
if (unlikely(m_hCursor != nullptr)) {
::DestroyCursor(m_hCursor);
m_hCursor = 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 +125,12 @@ namespace dxvk {
return D3D_OK;
}
HRESULT D3D9Cursor::SetSoftwareCursor(UINT Width, UINT Height, UINT XHotSpot, UINT YHotSpot) {
Logger::warn("D3D9Cursor::SetSoftwareCursor: Not supported on current platform.");
return D3D_OK;
}
#endif
}

View File

@ -4,15 +4,25 @@
namespace dxvk {
constexpr uint32_t HardwareCursorWidth = 32u;
constexpr uint32_t HardwareCursorHeight = 32u;
/**
* \brief D3D9 Software Cursor
*/
struct D3D9_SOFTWARE_CURSOR {
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 +35,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.Width > 0 && m_sCursor.Height > 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
};

View File

@ -354,14 +354,14 @@ namespace dxvk {
hwCursor |= inputWidth <= HardwareCursorWidth
|| inputHeight <= HardwareCursorHeight;
D3DLOCKED_BOX lockedBox;
HRESULT hr = LockImage(cursorTex, 0, 0, &lockedBox, nullptr, D3DLOCK_READONLY);
if (FAILED(hr))
return hr;
const uint8_t* data = reinterpret_cast<const uint8_t*>(lockedBox.pBits);
if (hwCursor) {
D3DLOCKED_BOX lockedBox;
HRESULT hr = LockImage(cursorTex, 0, 0, &lockedBox, nullptr, D3DLOCK_READONLY);
if (FAILED(hr))
return hr;
const uint8_t* data = reinterpret_cast<const uint8_t*>(lockedBox.pBits);
// Windows works with a stride of 128, lets respect that.
// Copy data to the bitmap...
CursorBitmap bitmap = { 0 };
@ -376,10 +376,58 @@ 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) {
UnlockImage(cursorTex, 0, 0);
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;
std::vector<uint8_t> clippedBitmap(clippedInputHeight * clippedCopyPitch, 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 +507,7 @@ namespace dxvk {
}
m_flags.clr(D3D9DeviceFlag::InScene);
m_cursor.ResetCursor();
/*
* Before calling the IDirect3DDevice9::Reset method for a device,
@ -3852,6 +3901,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,

View File

@ -748,6 +748,32 @@ namespace dxvk {
}
void D3D9SwapChainEx::SetCursorTexture(UINT Width, UINT Height, uint8_t* pCursorBitmap) {
VkExtent2D cursorSize = { uint32_t(Width), uint32_t(Height) };
m_blitter->setCursorTexture(
cursorSize,
VK_FORMAT_B8G8R8A8_UNORM,
(void *) pCursorBitmap);
}
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();

View File

@ -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);