D3D9: Implement CopyFramebufferToMemorySync().

This works like other backends, including D3D11.  This allows us to get
rid of the old implementation and reuse more code.
This commit is contained in:
Unknown W. Brackets 2022-10-10 21:28:14 -07:00
parent 5318452e76
commit c89cf1cde7
4 changed files with 121 additions and 167 deletions

View file

@ -1623,13 +1623,25 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel
}
break;
case FB_STENCIL_BIT:
_assert_(destFormat == DataFormat::S8);
for (int y = 0; y < bh; y++) {
uint8_t *destStencil = (uint8_t *)pixels + y * pixelStride;
const uint32_t *src = (const uint32_t *)(srcWithOffset + map.RowPitch * y);
for (int x = 0; x < bw; x++) {
destStencil[x] = src[x] >> 24;
if (srcFormat == destFormat) {
// Can just memcpy when it matches no matter the format!
uint8_t *dst = (uint8_t *)pixels;
const uint8_t *src = (const uint8_t *)srcWithOffset;
for (int y = 0; y < bh; ++y) {
memcpy(dst, src, bw * DataFormatSizeInBytes(srcFormat));
dst += pixelStride * DataFormatSizeInBytes(srcFormat);
src += map.RowPitch;
}
} else if (destFormat == DataFormat::S8) {
for (int y = 0; y < bh; y++) {
uint8_t *destStencil = (uint8_t *)pixels + y * pixelStride;
const uint32_t *src = (const uint32_t *)(srcWithOffset + map.RowPitch * y);
for (int x = 0; x < bw; x++) {
destStencil[x] = src[x] >> 24;
}
}
} else {
_assert_(false);
}
break;
}

View file

@ -528,6 +528,7 @@ public:
// Not implemented
}
bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) override;
bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride, const char *tag) override;
// These functions should be self explanatory.
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp, const char *tag) override;
@ -1400,6 +1401,108 @@ bool D3D9Context::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int
return SUCCEEDED(device_->StretchRect(srcSurf, &srcRect, dstSurf, &dstRect, (filter == FB_BLIT_LINEAR && channelBits == FB_COLOR_BIT) ? D3DTEXF_LINEAR : D3DTEXF_POINT));
}
bool D3D9Context::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int bx, int by, int bw, int bh, Draw::DataFormat destFormat, void *pixels, int pixelStride, const char *tag) {
D3D9Framebuffer *fb = (D3D9Framebuffer *)src;
if (fb) {
if (bx + bw > fb->Width()) {
bw -= (bx + bw) - fb->Width();
}
if (by + bh > fb->Height()) {
bh -= (by + bh) - fb->Height();
}
}
if (bh <= 0 || bw <= 0)
return true;
DataFormat srcFormat = Draw::DataFormat::R8G8B8A8_UNORM;
if (channelBits != FB_COLOR_BIT) {
srcFormat = Draw::DataFormat::D24_S8;
if (!supportsINTZ)
return false;
}
D3DSURFACE_DESC desc;
D3DLOCKED_RECT locked;
RECT rect = { (LONG)bx, (LONG)by, (LONG)bw, (LONG)bh };
LPDIRECT3DSURFACE9 offscreen = nullptr;
HRESULT hr = E_UNEXPECTED;
if (channelBits == FB_COLOR_BIT) {
fb->tex->GetLevelDesc(0, &desc);
hr = device_->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &offscreen, nullptr);
if (SUCCEEDED(hr)) {
hr = device_->GetRenderTargetData(fb->surf, offscreen);
if (SUCCEEDED(hr)) {
hr = offscreen->LockRect(&locked, &rect, D3DLOCK_READONLY);
}
}
} else {
fb->depthstenciltex->GetLevelDesc(0, &desc);
hr = fb->depthstenciltex->LockRect(0, &locked, &rect, D3DLOCK_READONLY);
}
if (SUCCEEDED(hr)) {
switch (channelBits) {
case FB_COLOR_BIT:
// Pixel size always 4 here because we always request BGRA8888.
ConvertFromBGRA8888((uint8_t *)pixels, (const uint8_t *)locked.pBits, pixelStride, locked.Pitch / sizeof(uint32_t), bw, bh, destFormat);
break;
case FB_DEPTH_BIT:
if (srcFormat == destFormat) {
// Can just memcpy when it matches no matter the format!
uint8_t *dst = (uint8_t *)pixels;
const uint8_t *src = (const uint8_t *)locked.pBits;
for (int y = 0; y < bh; ++y) {
memcpy(dst, src, bw * DataFormatSizeInBytes(srcFormat));
dst += pixelStride * DataFormatSizeInBytes(srcFormat);
src += locked.Pitch;
}
} else if (destFormat == DataFormat::D32F) {
ConvertToD32F((uint8_t *)pixels, (const uint8_t *)locked.pBits, pixelStride, locked.Pitch / sizeof(uint32_t), bw, bh, srcFormat);
} else if (destFormat == DataFormat::D16) {
ConvertToD16((uint8_t *)pixels, (const uint8_t *)locked.pBits, pixelStride, locked.Pitch / sizeof(uint32_t), bw, bh, srcFormat);
} else {
_assert_(false);
}
break;
case FB_STENCIL_BIT:
if (srcFormat == destFormat) {
uint8_t *dst = (uint8_t *)pixels;
const uint8_t *src = (const uint8_t *)locked.pBits;
for (int y = 0; y < bh; ++y) {
memcpy(dst, src, bw * DataFormatSizeInBytes(srcFormat));
dst += pixelStride * DataFormatSizeInBytes(srcFormat);
src += locked.Pitch;
}
} else if (destFormat == DataFormat::S8) {
for (int y = 0; y < bh; y++) {
uint8_t *destStencil = (uint8_t *)pixels + y * pixelStride;
const uint32_t *src = (const uint32_t *)((const uint8_t *)locked.pBits + locked.Pitch * y);
for (int x = 0; x < bw; x++) {
destStencil[x] = src[x] >> 24;
}
}
} else {
_assert_(false);
}
break;
}
}
if (channelBits != FB_COLOR_BIT) {
fb->depthstenciltex->UnlockRect(0);
}
if (offscreen) {
offscreen->UnlockRect();
offscreen->Release();
}
return SUCCEEDED(hr);
}
void D3D9Context::HandleEvent(Event ev, int width, int height, void *param1, void *param2) {
switch (ev) {
case Event::LOST_BACKBUFFER:

View file

@ -243,158 +243,3 @@
}
offscreenSurfaces_.clear();
}
bool FramebufferManagerDX9::GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat fb_format, GPUDebugBuffer &buffer, int maxRes) {
VirtualFramebuffer *vfb = currentRenderVfb_;
if (!vfb) {
vfb = GetVFBAt(fb_address);
}
if (!vfb) {
if (!Memory::IsValidAddress(fb_address))
return false;
// If there's no vfb and we're drawing there, must be memory?
buffer = GPUDebugBuffer(Memory::GetPointerWrite(fb_address), fb_stride, 512, fb_format);
return true;
}
LPDIRECT3DSURFACE9 renderTarget = vfb->fbo ? (LPDIRECT3DSURFACE9)draw_->GetFramebufferAPITexture(vfb->fbo, Draw::FB_COLOR_BIT | Draw::FB_SURFACE_BIT, 0) : nullptr;
bool success = false;
if (renderTarget) {
Draw::Framebuffer *tempFBO = nullptr;
int w = vfb->renderWidth, h = vfb->renderHeight;
if (maxRes > 0 && vfb->renderWidth > vfb->width * maxRes) {
// Let's resize. We must stretch to a render target first.
w = vfb->width * maxRes;
h = vfb->height * maxRes;
tempFBO = draw_->CreateFramebuffer({ w, h, 1, 1, false });
if (draw_->BlitFramebuffer(vfb->fbo, 0, 0, vfb->renderWidth, vfb->renderHeight, tempFBO, 0, 0, w, h, Draw::FB_COLOR_BIT, g_Config.iBufFilter == SCALE_LINEAR ? Draw::FB_BLIT_LINEAR : Draw::FB_BLIT_NEAREST, "GetFramebuffer")) {
renderTarget = (LPDIRECT3DSURFACE9)draw_->GetFramebufferAPITexture(tempFBO, Draw::FB_COLOR_BIT | Draw::FB_SURFACE_BIT, 0);
}
}
LPDIRECT3DSURFACE9 offscreen = GetOffscreenSurface(renderTarget, vfb);
if (offscreen) {
success = GetRenderTargetFramebuffer(renderTarget, offscreen, w, h, buffer);
}
}
return success;
}
bool FramebufferManagerDX9::GetOutputFramebuffer(GPUDebugBuffer &buffer) {
LPDIRECT3DSURFACE9 renderTarget = nullptr;
HRESULT hr = device_->GetRenderTarget(0, &renderTarget);
bool success = false;
if (renderTarget && SUCCEEDED(hr)) {
D3DSURFACE_DESC desc;
renderTarget->GetDesc(&desc);
LPDIRECT3DSURFACE9 offscreen = nullptr;
HRESULT hr = device_->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &offscreen, NULL);
if (offscreen && SUCCEEDED(hr)) {
success = GetRenderTargetFramebuffer(renderTarget, offscreen, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight, buffer);
offscreen->Release();
}
renderTarget->Release();
}
return success;
}
bool FramebufferManagerDX9::GetRenderTargetFramebuffer(LPDIRECT3DSURFACE9 renderTarget, LPDIRECT3DSURFACE9 offscreen, int w, int h, GPUDebugBuffer &buffer) {
D3DSURFACE_DESC desc;
renderTarget->GetDesc(&desc);
bool success = false;
HRESULT hr = device_->GetRenderTargetData(renderTarget, offscreen);
if (SUCCEEDED(hr)) {
D3DLOCKED_RECT locked;
RECT rect = {0, 0, w, h};
hr = offscreen->LockRect(&locked, &rect, D3DLOCK_READONLY);
if (SUCCEEDED(hr)) {
// TODO: Handle the other formats? We don't currently create them, I think.
buffer.Allocate(locked.Pitch / 4, desc.Height, GPU_DBG_FORMAT_8888_BGRA, false);
memcpy(buffer.GetData(), locked.pBits, locked.Pitch * desc.Height);
offscreen->UnlockRect();
success = true;
}
}
return success;
}
bool FramebufferManagerDX9::GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) {
VirtualFramebuffer *vfb = currentRenderVfb_;
if (!vfb) {
vfb = GetVFBAt(fb_address);
}
if (!vfb) {
// If there's no vfb and we're drawing there, must be memory?
buffer = GPUDebugBuffer(Memory::GetPointerWrite(z_address), z_stride, 512, GPU_DBG_FORMAT_16BIT);
return true;
}
bool success = false;
LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)draw_->GetFramebufferAPITexture(vfb->fbo, Draw::FB_DEPTH_BIT, 0);
if (tex) {
D3DSURFACE_DESC desc;
D3DLOCKED_RECT locked;
tex->GetLevelDesc(0, &desc);
RECT rect = {0, 0, (LONG)desc.Width, (LONG)desc.Height};
HRESULT hr = tex->LockRect(0, &locked, &rect, D3DLOCK_READONLY);
if (SUCCEEDED(hr)) {
GPUDebugBufferFormat fmt = GPU_DBG_FORMAT_24BIT_8X;
if (gstate_c.Supports(GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT)) {
fmt = GPU_DBG_FORMAT_24BIT_8X_DIV_256;
}
int pixelSize = 4;
buffer.Allocate(locked.Pitch / pixelSize, desc.Height, fmt, false);
memcpy(buffer.GetData(), locked.pBits, locked.Pitch * desc.Height);
success = true;
tex->UnlockRect(0);
}
}
return success;
}
bool FramebufferManagerDX9::GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) {
VirtualFramebuffer *vfb = currentRenderVfb_;
if (!vfb) {
vfb = GetVFBAt(fb_address);
}
if (!vfb) {
if (!Memory::IsValidAddress(fb_address))
return false;
// If there's no vfb and we're drawing there, must be memory?
// TODO: Actually get the stencil.
buffer = GPUDebugBuffer(Memory::GetPointerWrite(fb_address), fb_stride, 512, GPU_DBG_FORMAT_8888);
return true;
}
bool success = false;
LPDIRECT3DTEXTURE9 tex = (LPDIRECT3DTEXTURE9)draw_->GetFramebufferAPITexture(vfb->fbo, Draw::FB_DEPTH_BIT, 0);
if (tex) {
D3DSURFACE_DESC desc;
D3DLOCKED_RECT locked;
tex->GetLevelDesc(0, &desc);
RECT rect = {0, 0, (LONG)desc.Width, (LONG)desc.Height};
HRESULT hr = tex->LockRect(0, &locked, &rect, D3DLOCK_READONLY);
if (SUCCEEDED(hr)) {
GPUDebugBufferFormat fmt = GPU_DBG_FORMAT_24X_8BIT;
int pixelSize = 4;
buffer.Allocate(locked.Pitch / pixelSize, desc.Height, fmt, false);
memcpy(buffer.GetData(), locked.pBits, locked.Pitch * desc.Height);
success = true;
tex->UnlockRect(0);
}
}
return success;
}

View file

@ -39,11 +39,6 @@ public:
void DestroyAllFBOs() override;
bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) override;
bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) override;
bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) override;
bool GetOutputFramebuffer(GPUDebugBuffer &buffer) override;
LPDIRECT3DSURFACE9 GetOffscreenSurface(LPDIRECT3DSURFACE9 similarSurface, VirtualFramebuffer *vfb);
LPDIRECT3DSURFACE9 GetOffscreenSurface(D3DFORMAT fmt, u32 w, u32 h);
@ -52,7 +47,6 @@ protected:
void ReadbackFramebufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel) override;
private:
bool GetRenderTargetFramebuffer(LPDIRECT3DSURFACE9 renderTarget, LPDIRECT3DSURFACE9 offscreen, int w, int h, GPUDebugBuffer &buffer);
void ReadbackDepthbufferSync(VirtualFramebuffer *vfb, int x, int y, int w, int h);
LPDIRECT3DDEVICE9 device_;