diff --git a/src/d3d11/d3d11_gdi.cpp b/src/d3d11/d3d11_gdi.cpp new file mode 100644 index 00000000..30dbcf9c --- /dev/null +++ b/src/d3d11/d3d11_gdi.cpp @@ -0,0 +1,208 @@ +#include "d3d11_context.h" +#include "d3d11_device.h" +#include "d3d11_gdi.h" + +#include "../util/util_gdi.h" + +namespace dxvk { + + D3D11GDISurface::D3D11GDISurface( + ID3D11Resource* pResource, + UINT Subresource) + : m_resource (pResource), + m_subresource (Subresource), + m_readback (nullptr), + m_hdc (nullptr), + m_hbitmap (nullptr), + m_acquired (false) { + // Allocate memory for the bitmap + auto tex = GetCommonTexture(m_resource)->Desc(); + m_data.resize(tex->Width * tex->Height); + + // Create GDI DC + D3DKMT_CREATEDCFROMMEMORY desc; + desc.pMemory = m_data.data(); + desc.Format = D3DFMT_A8R8G8B8; + desc.Width = tex->Width; + desc.Height = tex->Height; + desc.Pitch = tex->Width * sizeof(uint32_t); + desc.hDeviceDc = CreateCompatibleDC(nullptr); + desc.pColorTable = nullptr; + desc.hDc = nullptr; + desc.hBitmap = nullptr; + + uint32_t a = D3DKMTCreateDCFromMemory(&desc); + + if (a) + Logger::err(str::format("D3D11: Failed to create GDI DC:", std::hex, a)); + + m_hdc = desc.hDc; + m_hbitmap = desc.hBitmap; + } + + + D3D11GDISurface::~D3D11GDISurface() { + if (m_readback) + m_readback->Release(); + + D3DKMT_DESTROYDCFROMMEMORY desc; + desc.hDC = m_hdc; + desc.hBitmap = m_hbitmap; + D3DKMTDestroyDCFromMemory(&desc); + } + + + HRESULT D3D11GDISurface::Acquire(BOOL Discard, HDC* phdc) { + if (!phdc) + return E_INVALIDARG; + + *phdc = nullptr; + + if (m_acquired) + return DXGI_ERROR_INVALID_CALL; + + if (!Discard) { + // Create a staging resource that we can map + if (!m_readback && FAILED(CreateReadbackResource())) { + Logger::err("D3D11: Failed to create GDI readback resource"); + return E_FAIL; + } + + // Copy subresource to staging image + Com device; + Com context; + + m_resource->GetDevice(&device); + device->GetImmediateContext(&context); + + context->CopySubresourceRegion(m_readback, 0, + 0, 0, 0, m_resource, m_subresource, nullptr); + + // Copy staging image to DC memory + auto tex = GetCommonTexture(m_resource)->Desc(); + auto rowData = reinterpret_cast(m_data.data()); + auto rowLength = sizeof(uint32_t) * tex->Width; + + D3D11_MAPPED_SUBRESOURCE sr; + context->Map(m_readback, 0, D3D11_MAP_READ, 0, &sr); + + for (uint32_t i = 0; i < tex->Height; i++) { + std::memcpy(rowData + rowLength * i, + reinterpret_cast(sr.pData) + sr.RowPitch * i, + rowLength); + } + + context->Unmap(m_readback, 0); + } + + m_acquired = true; + *phdc = m_hdc; + return S_OK; + } + + + HRESULT D3D11GDISurface::Release(const RECT* pDirtyRect) { + if (!m_acquired) + return DXGI_ERROR_INVALID_CALL; + + Com device; + Com context; + + m_resource->GetDevice(&device); + device->GetImmediateContext(&context); + + // Commit changes made to the DC + auto tex = GetCommonTexture(m_resource)->Desc(); + + RECT rect; + + if (pDirtyRect) { + rect.left = std::max(pDirtyRect->left, 0); + rect.top = std::max(pDirtyRect->top, 0); + rect.right = std::min(pDirtyRect->right, tex->Width); + rect.bottom = std::min(pDirtyRect->bottom, tex->Height); + } else { + rect.left = 0; + rect.top = 0; + rect.right = tex->Width; + rect.bottom = tex->Height; + } + + if (rect.left < rect.right && rect.top < rect.bottom) { + D3D11_BOX box; + box.left = rect.left; + box.top = rect.top; + box.front = 0; + box.right = rect.right; + box.bottom = rect.bottom; + box.back = 1; + + context->UpdateSubresource(m_resource, m_subresource, + &box, m_data.data() + rect.left, + sizeof(uint32_t) * tex->Width, + sizeof(uint32_t) * tex->Width * tex->Height); + } + + m_acquired = false; + return S_OK; + } + + + HRESULT D3D11GDISurface::CreateReadbackResource() { + auto tex = GetCommonTexture(m_resource); + + Com device; + Com context; + + m_resource->GetDevice(&device); + device->GetImmediateContext(&context); + + D3D11_RESOURCE_DIMENSION dim = { }; + m_resource->GetType(&dim); + + VkImageSubresource sr = tex->GetSubresourceFromIndex( + VK_IMAGE_ASPECT_COLOR_BIT, m_subresource); + + switch (dim) { + case D3D11_RESOURCE_DIMENSION_TEXTURE1D: { + D3D11_TEXTURE1D_DESC desc; + desc.Width = std::max(tex->Desc()->Width >> sr.mipLevel, 1); + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = tex->Desc()->Format; + desc.Usage = D3D11_USAGE_STAGING; + desc.BindFlags = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + desc.MiscFlags = 0; + + ID3D11Texture1D* tex1D = nullptr; + HRESULT hr = device->CreateTexture1D(&desc, nullptr, &tex1D); + m_readback = tex1D; + return hr; + } break; + + case D3D11_RESOURCE_DIMENSION_TEXTURE2D: { + D3D11_TEXTURE2D_DESC desc; + desc.Width = std::max(tex->Desc()->Width >> sr.mipLevel, 1); + desc.Height = std::max(tex->Desc()->Height >> sr.mipLevel, 1); + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.Format = tex->Desc()->Format; + desc.SampleDesc= { 1, 0 }; + desc.Usage = D3D11_USAGE_STAGING; + desc.BindFlags = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + desc.MiscFlags = 0; + + ID3D11Texture2D* tex2D = nullptr; + HRESULT hr = device->CreateTexture2D(&desc, nullptr, &tex2D); + m_readback = tex2D; + return hr; + } break; + + default: + return E_INVALIDARG; + } + } + +} diff --git a/src/d3d11/d3d11_gdi.h b/src/d3d11/d3d11_gdi.h new file mode 100644 index 00000000..a2656c0a --- /dev/null +++ b/src/d3d11/d3d11_gdi.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +#include "d3d11_include.h" + +namespace dxvk { + + class D3D11GDISurface { + + public: + + D3D11GDISurface( + ID3D11Resource* pResource, + UINT Subresource); + + ~D3D11GDISurface(); + + HRESULT Acquire( + BOOL Discard, + HDC* phdc); + + HRESULT Release( + const RECT* pDirtyRect); + + private: + + ID3D11Resource* m_resource; + uint32_t m_subresource; + ID3D11Resource* m_readback; + HDC m_hdc; + HANDLE m_hbitmap; + bool m_acquired; + + std::vector m_data; + + HRESULT CreateReadbackResource(); + + }; + +} diff --git a/src/d3d11/meson.build b/src/d3d11/meson.build index 994df7e9..45974976 100644 --- a/src/d3d11/meson.build +++ b/src/d3d11/meson.build @@ -42,6 +42,7 @@ d3d11_src = [ 'd3d11_depth_stencil.cpp', 'd3d11_device.cpp', 'd3d11_enums.cpp', + 'd3d11_gdi.cpp', 'd3d11_initializer.cpp', 'd3d11_input_layout.cpp', 'd3d11_interop.cpp',