我正在尝试通过 Present 挂钩获取 DirectX 11 屏幕截图。 Backbuffer 是多重采样的,我使用 ResolveSubresource 但仍然出现黑屏。尝试了 ResolveSubresource 调用的变体,结果相同。尝试启用调试层,没有有关 CopyResource 或 ResolveSubresource 调用的消息。这是代码,感谢任何帮助。
BOOL
D3D11CreateTextureCopyWithCPUAccess(
__in ID3D11Device* dev,
__in ID3D11Texture2D* texFrom,
__out ID3D11Texture2D** cpuAccessTexOut
)
{
BOOL result = FALSE;
do {
if (!dev || !texFrom || !cpuAccessTexOut) break;
D3D11_TEXTURE2D_DESC texFromDesc{};
texFrom->GetDesc(&texFromDesc);
LOG_DEBUG(
L"Texture format %d, sample count %d, usage %d, array size %d, mips levels %d, bind flags %.8x, misc flags %.8x.",
texFromDesc.Format,
texFromDesc.SampleDesc.Count,
texFromDesc.Usage,
texFromDesc.ArraySize,
texFromDesc.MipLevels,
texFromDesc.BindFlags,
texFromDesc.MiscFlags
);
D3D11_TEXTURE2D_DESC cpuAccessTexDesc{};
cpuAccessTexDesc.Width = texFromDesc.Width;
cpuAccessTexDesc.Height = texFromDesc.Height;
cpuAccessTexDesc.Format = texFromDesc.Format;
cpuAccessTexDesc.ArraySize = 1;
cpuAccessTexDesc.BindFlags = 0;
cpuAccessTexDesc.MiscFlags = 0;
cpuAccessTexDesc.SampleDesc.Count = 1;
cpuAccessTexDesc.SampleDesc.Quality = 0;
cpuAccessTexDesc.MipLevels = 1;
cpuAccessTexDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
cpuAccessTexDesc.Usage = D3D11_USAGE_STAGING;
ID3D11Texture2D* cpuAccessTex = nullptr;
HRESULT hr = dev->CreateTexture2D(&cpuAccessTexDesc, nullptr, &cpuAccessTex);
if (FAILED(hr)) {
LOG_ERROR(L"CreateTexture2D failed, result %.8x.", hr);
break;
}
*cpuAccessTexOut = cpuAccessTex;
result = TRUE;
} while (FALSE);
return result;
}
BOOL
D3D11CopyMultisampledResource(
__in ID3D11Device* device,
__in ID3D11DeviceContext* context,
__in ID3D11Texture2D* texFrom,
__out ID3D11Texture2D** texToOut
)
{
BOOL result = FALSE;
do {
D3D11_TEXTURE2D_DESC desc{};
texFrom->GetDesc(&desc);
UINT support = NULL;
device->CheckFormatSupport(desc.Format, &support);
if (!(support & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RESOLVE)) {
LOG_ERROR(L"Multisample resolving is not supported.");
break;
}
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
ID3D11Texture2D* texTo = NULL;
HRESULT result = device->CreateTexture2D(&desc, NULL, &texTo);
if (FAILED(result)) {
LOG_ERROR(L"Failed to create temp texture, result %.8x.", result);
break;
}
for (ULONG item = 0; item < desc.ArraySize; ++item) {
for (ULONG level = 0; level < desc.MipLevels; ++level) {
ULONG index = D3D11CalcSubresource(level, item, desc.MipLevels);
context->ResolveSubresource(texTo, index, texFrom, index, desc.Format);
}
}
//context->ResolveSubresource(texTo, 0, texFrom, 0, desc.Format);
*texToOut = texTo;
result = TRUE;
} while (FALSE);
return result;
}
BOOL
D3D11TakeScreenshot(
IDXGISwapChain* swapChain,
CAPTURE_HEADER** screenshotOut
)
{
BOOL result = FALSE;
ID3D11Device* device = NULL;
ID3D11DeviceContext* context = NULL;
ID3D11Texture2D* backBuffer = NULL;
ID3D11Texture2D* tempTex = NULL;
ID3D11Texture2D* cpuAccessTex = NULL;
do {
if (!swapChain) break;
DXGI_SWAP_CHAIN_DESC swapChainDesc = {0};
swapChain->GetDesc(&swapChainDesc);
RECT rect = {0};
GetClientRect(swapChainDesc.OutputWindow, &rect);
UINT width = rect.right - rect.left;
UINT height = rect.bottom - rect.top;
HRESULT hr = swapChain->GetDevice(__uuidof(ID3D11Device), (void**)&device);
if (FAILED(hr)) {
LOG_ERROR(L"IDXGISwapChain::GetDevice failed, result %.8x.", hr);
break;
}
device->GetImmediateContext(&context);
hr = swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&backBuffer);
if (FAILED(hr)) {
LOG_ERROR(L"IDXGISwapChain::GetBuffer failed, result %.8x.", hr);
break;
}
if (!D3D11CreateTextureCopyWithCPUAccess(device, backBuffer, &cpuAccessTex)) {
LOG_ERROR(L"D3D11CreateTextureCopyWithCPUAccess failed.");
break;
}
D3D11_TEXTURE2D_DESC desc = {0};
backBuffer->GetDesc(&desc);
if (desc.SampleDesc.Count > 1) {
LOG_DEBUG(L"Multisampled source.");
if (D3D11CopyMultisampledResource(device, context, backBuffer, &tempTex)) {
context->CopyResource(cpuAccessTex, tempTex);
}
} else {
context->CopyResource(cpuAccessTex, backBuffer);
}
D3D11_MAPPED_SUBRESOURCE res{};
hr = context->Map(cpuAccessTex, 0, D3D11_MAP_READ, 0, &res);
if (FAILED(hr)) {
LOG_ERROR(L"Map failed, result %.8x", hr);
break;
}
VOID* buf = MemAllocVirtual(
NULL,
sizeof(CAPTURE_HEADER) + height * res.RowPitch,
MEM_COMMIT | MEM_RESERVE,
PAGE_READWRITE
);
if (!buf) {
LOG_ERROR(L"VirtualAlloc failed, error %.8x.", GetLastError());
break;
}
CAPTURE_HEADER* hdr = (CAPTURE_HEADER*)buf;
hdr->success = 1;
hdr->type = CaptureType_DirectX;
hdr->cx = width;
hdr->cy = height;
hdr->pitch = res.RowPitch;
PUCHAR p = (PUCHAR)buf + sizeof(CAPTURE_HEADER);
for (ULONG i = 0; i < height; i++) {
for (ULONG j = 0; j < res.RowPitch / sizeof(ULONG); j++) {
PUCHAR q = ((PUCHAR)res.pData + i * res.RowPitch + j * 4);
*p++ = q[2];
*p++ = q[1];
*p++ = q[0];
*p++ = q[3];
}
}
LOG_DEBUG(L"DX11 Screenshot taken %d:%d.", width, height);
*screenshotOut = hdr;
result = TRUE;
} while (FALSE);
if (tempTex) tempTex->Release();
if (cpuAccessTex) {
if (context) context->Unmap(cpuAccessTex, 0);
cpuAccessTex->Release();
}
if (backBuffer) {
backBuffer->Release();
}
if (context) {
context->Release();
}
if (device) {
device->Release();
}
return result;
}