Im Grund funktioniert das wie jeder CPU-readback. Du erzeugst eine Textur (D3D11_USAGE_DEFAULT, D3D11_BIND_RENDER_TARGET) und setzt diese als Rendertarget. Außerdem brauchst du eine passende Stagingtextur fürs zurücklesen (D3D11_USAGE_STAGING, D3D11_CPU_ACCESS_READ). Dann kannst du mit CopyResource/CopySubresourceRegion den Inhalt zurück in den Systemspeicher kopieren. Die Stagingtextur kannst du mappen und auslesen.
Um die CPU nicht zu 'stallen' solltest du einen Ring von Stagingtexturen verwenden (z.B. 3 oder 4) und jeden Frame eine neue Textur als Ziel angeben. Beim zurücklesen kannst du entsprechend D3D11_MAP_FLAG_DO_NOT_WAIT angeben um zu testen ob die Textur schon zum auslesen bereit ist. Hier etwas Pseudocode um das Ganze zu verdeutlichen: (alles ungetestet ohne Gewähr auf Fehlerfreiheit!
)
|
C-/C++-Quelltext
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
// create readback ring
ID3D11Texture2D* m_ReadBackRing[MAX_READBACK_BUFFERS];
for (uint32 i = 0; i < MAX_READBACK_BUFFERS; ++i)
m_ReadBackRing[i] = CreateStagingTexture(width, height, bpp, ...);
// copy frame to texture
if (RingBufferIsFull()) // wenn der ringpuffer voll ist, also writeindex = readindex
{
context->Map(m_ReadBackRing[m_CurrentReadIndex], ..., 0, &mappedResource); // CPU stallen bis nächster slot frei wird
resultReady = true;
}
else
{
device->CopyResource(frameBuffer, m_ReadBackRing[m_CurrentWriteIndex]); // kopie vom framebuffer anstoßen
m_CurrentWriteIndex = (m_CurrentWriteIndex + 1) % MAX_READBACK_BUFFERS;
// readback
resultReady = context->Map(m_ReadBackRing[m_CurrentReadIndex], ..., D3D11_MAP_FLAG_DO_NOT_WAIT, &mappedResource) != DXGI_ERROR_WAS_STILL_DRAWING;
}
if (resultReady)
{
memcpy(dest, mappedResource.pData, size);
m_CurrentReadIndex = (m_CurrentReadIndex + 1) % MAX_READBACK_BUFFERS;
}
|
Links:
Copying and Accessing Resource Data