mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
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:
parent
5318452e76
commit
c89cf1cde7
4 changed files with 121 additions and 167 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
|
|
Loading…
Add table
Reference in a new issue