Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[d3d9] Implement GetFrontBufferData using GDI #3949

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/d3d9/d3d9_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ namespace dxvk {
m_activeRTsWhichAreTextures = 0;
m_alphaSwizzleRTs = 0;
m_lastHazardsRT = 0;

m_gamescopeWSI = dxvk::env::getEnvVar("ENABLE_GAMESCOPE_WSI") == "1";
}


Expand Down Expand Up @@ -1093,6 +1095,12 @@ namespace dxvk {
if (unlikely(iSwapChain != 0))
return D3DERR_INVALIDCALL;

#ifdef _WIN32
if (!IsGamescopeWSIEnabled()) {
return D3D9SwapChainEx::GetFrontBufferDataGDI(pDestSurface);
doitsujin marked this conversation as resolved.
Show resolved Hide resolved
}
#endif

D3D9DeviceLock lock = LockDevice();

// In windowed mode, GetFrontBufferData takes a screenshot of the entire screen.
Expand Down
6 changes: 6 additions & 0 deletions src/d3d9/d3d9_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,10 @@ namespace dxvk {
m_mostRecentlyUsedSwapchain = m_implicitSwapchain.ptr();
}

bool IsGamescopeWSIEnabled() const {
return m_gamescopeWSI;
}

Com<D3D9InterfaceEx> m_parent;
D3DDEVTYPE m_deviceType;
HWND m_window;
Expand Down Expand Up @@ -1439,6 +1443,8 @@ namespace dxvk {

D3D9SwapChainEx* m_mostRecentlyUsedSwapchain = nullptr;

bool m_gamescopeWSI;

#ifdef D3D9_ALLOW_UNMAPPING
lru_list<D3D9CommonTexture*> m_mappedTextures;
#endif
Expand Down
82 changes: 82 additions & 0 deletions src/d3d9/d3d9_swapchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,91 @@ namespace dxvk {

return S_OK;
}

HRESULT D3D9SwapChainEx::GetFrontBufferDataGDI(IDirect3DSurface9* pDestSurface) {
D3D9Surface* dst = static_cast<D3D9Surface*>(pDestSurface);

if (unlikely(dst == nullptr))
return D3DERR_INVALIDCALL;

D3D9CommonTexture* dstTexInfo = dst->GetCommonTexture();
VkExtent3D dstTexExtent = dstTexInfo->GetExtentMip(dst->GetMipLevel());

if (unlikely(dstTexInfo->Desc()->Format != D3D9Format::A8R8G8B8)) {
return D3DERR_INVALIDCALL;
}

if (unlikely(dstTexInfo->Desc()->Pool != D3DPOOL_SYSTEMMEM && dstTexInfo->Desc()->Pool != D3DPOOL_SCRATCH))
return D3DERR_INVALIDCALL;

const POINT ptZero = { 0, 0 };
HMONITOR primaryMonitor = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);

MONITORINFO monitorInfo = {};
monitorInfo.cbSize = sizeof(MONITORINFO);
if (!GetMonitorInfo(primaryMonitor, &monitorInfo)) {
Logger::err("D3D9SwapChainEx::GetFrontBufferData: Failed to query window size.");
return D3DERR_INVALIDCALL;
}
VkExtent2D monitorExtent = { uint32_t(monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left), uint32_t(monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top) };
VkExtent2D blitRegionExtent = { std::min(monitorExtent.width, dstTexExtent.width), std::min(monitorExtent.height, dstTexExtent.height) };

HDC srcDC = GetDC(nullptr);
HDC dstDC = CreateCompatibleDC(nullptr);
HBITMAP hbitmap = CreateCompatibleBitmap(srcDC, dstTexExtent.width, dstTexExtent.height);

if (unlikely(hbitmap == nullptr)) {
Logger::err("D3D9SwapChainEx::GetFrontBufferData: Failed to create bitmap.");
return D3DERR_INVALIDCALL;
}

HBITMAP oldBitmap = static_cast<HBITMAP>(SelectObject(dstDC, hbitmap));
if (unlikely(oldBitmap == nullptr)) {
Logger::err("D3D9SwapChainEx::GetFrontBufferData: Failed to select bitmap for DC.");
return D3DERR_INVALIDCALL;
}

// We always want the primary monitor which conveniently always sits at 0,0.
if (unlikely(!BitBlt(dstDC, 0, 0, blitRegionExtent.width, blitRegionExtent.height, srcDC, 0, 0, SRCCOPY))) {
Logger::err("D3D9SwapChainEx::GetFrontBufferData: Failed to create blit window.");
return D3DERR_INVALIDCALL;
}

D3DLOCKED_RECT lockedRect = {};
if (unlikely(pDestSurface->LockRect(&lockedRect, nullptr, D3DLOCK_DISCARD) != D3D_OK)) {
Logger::err("D3D9SwapChainEx::GetFrontBufferData: Failed to lock dst surface.");
return D3DERR_INVALIDCALL;
}

BITMAPINFO info = {
sizeof(BITMAPINFOHEADER), int32_t(dstTexExtent.width), int32_t(-blitRegionExtent.height), 1, 32, BI_RGB, 0, 0, 0, 0, 0,
0,0,0,0
};

// For uncompressed RGB formats, the minimum stride is always the image width in bytes, rounded up to the nearest DWORD
// Our pitch is always aligned to 4 and GetFrontBufferData only supports A8R8G8B8. So we can avoid another copy to repack the data.

uint32_t lines = uint32_t(blitRegionExtent.height);
int scanlinesCopied = GetDIBits(dstDC, hbitmap, 0, lines, lockedRect.pBits, &info, DIB_RGB_COLORS);
if (unlikely(scanlinesCopied != int32_t(lines))) {
Logger::err("D3D9SwapChainEx::GetFrontBufferData: Failed to read hbitmap data.");
return D3DERR_INVALIDCALL;
}

pDestSurface->UnlockRect();

SelectObject(dstDC, oldBitmap);
return D3D_OK;
}
#endif

HRESULT STDMETHODCALLTYPE D3D9SwapChainEx::GetFrontBufferData(IDirect3DSurface9* pDestSurface) {
#ifdef _WIN32
if (!m_parent->IsGamescopeWSIEnabled() && (m_presentParams.Windowed || !HasFrontBuffer())) {
return D3D9SwapChainEx::GetFrontBufferDataGDI(pDestSurface);
}
#endif

D3D9DeviceLock lock = m_parent->LockDevice();

// This function can do absolutely everything!
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 @@ -134,6 +134,10 @@ namespace dxvk {

void UpdateWindowCtx();

#ifdef _WIN32
static HRESULT GetFrontBufferDataGDI(IDirect3DSurface9* pDestSurface);
#endif

private:

enum BindingIds : uint32_t {
Expand Down
Loading