Skip to content

Commit

Permalink
[d3d9] Implement a software cursor
Browse files Browse the repository at this point in the history
  • Loading branch information
WinterSnowfall committed Oct 5, 2024
1 parent 329d9a0 commit ea43bc8
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 15 deletions.
59 changes: 57 additions & 2 deletions src/d3d9/d3d9_cursor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(&currentPos) && currentPos == POINT{ X, Y })
Expand All @@ -15,11 +31,18 @@ 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);
}
Expand Down Expand Up @@ -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);
Expand All @@ -65,6 +114,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

}
40 changes: 35 additions & 5 deletions src/d3d9/d3d9_cursor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint8_t> 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 {

Expand All @@ -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

};
Expand Down
77 changes: 69 additions & 8 deletions src/d3d9/d3d9_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<const uint8_t*>(lockedBox.pBits);
const uint8_t* data = reinterpret_cast<const uint8_t*>(lockedBox.pBits);

if (hwCursor) {
// Windows works with a stride of 128, lets respect that.
// Copy data to the bitmap...
CursorBitmap bitmap = { 0 };
Expand All @@ -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;
}

Expand Down Expand Up @@ -459,6 +506,7 @@ namespace dxvk {
}

m_flags.clr(D3D9DeviceFlag::InScene);
m_cursor.ResetCursor();

/*
* Before calling the IDirect3DDevice9::Reset method for a device,
Expand Down Expand Up @@ -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,
Expand Down
33 changes: 33 additions & 0 deletions src/d3d9/d3d9_swapchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
4 changes: 4 additions & 0 deletions src/d3d9/d3d9_swapchain.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit ea43bc8

Please sign in to comment.