[d3d9] Add a software cursor emulation config option

This commit is contained in:
WinterSnowfall 2024-02-06 00:13:31 +02:00
parent 037d0fa1ad
commit 1299ad050f
No known key found for this signature in database
6 changed files with 82 additions and 16 deletions

View File

@ -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().

View File

@ -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<uint8_t>& 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<DWORD> 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<LONG_PTR>(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<uint8_t>& bitmap,
bool cursorEmulation,
UINT width,
UINT height,
HWND window) {
Logger::warn("D3D9Cursor::SetHardwareCursor: Not supported on current platform.");
return D3D_OK;

View File

@ -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<uint8_t>& bitmap,
bool cursorEmulation,
UINT width,
UINT height,
HWND window);
private:

View File

@ -345,7 +345,7 @@ namespace dxvk {
// Always use a hardware cursor when windowed.
D3DPRESENT_PARAMETERS params;
m_implicitSwapchain->GetPresentParameters(&params);
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<const uint8_t*>(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<uint8_t> bitmap(cursorHeight * cursorPitch, 0);
size_t copyPitch = std::min<size_t>(
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...

View File

@ -77,6 +77,7 @@ namespace dxvk {
this->samplerLodBias = config.getOption<float> ("d3d9.samplerLodBias", 0.0f);
this->clampNegativeLodBias = config.getOption<bool> ("d3d9.clampNegativeLodBias", false);
this->countLosableResources = config.getOption<bool> ("d3d9.countLosableResources", true);
this->softwareCursorEmulation = config.getOption<bool> ("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);

View File

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