mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-10-08 11:33:28 +00:00
Merge pull request #10008 from hrydgard/thin3d-readpixels
Start unification of framebuffer readback code
This commit is contained in:
commit
424af60086
@ -1835,3 +1835,46 @@ void FramebufferManagerCommon::ShowScreenResolution() {
|
||||
host->NotifyUserMessage(messageStream.str(), 2.0f, 0xFFFFFF, "resize");
|
||||
INFO_LOG(SYSTEM, "%s", messageStream.str().c_str());
|
||||
}
|
||||
|
||||
bool FramebufferManagerCommon::GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) {
|
||||
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::GetPointer(fb_address | 0x04000000), fb_stride, 512, format);
|
||||
return true;
|
||||
}
|
||||
|
||||
int w = vfb->renderWidth, h = vfb->renderHeight;
|
||||
|
||||
Draw::Framebuffer *bound = nullptr;
|
||||
|
||||
if (vfb->fbo) {
|
||||
if (maxRes > 0 && vfb->renderWidth > vfb->width * maxRes) {
|
||||
w = vfb->width * maxRes;
|
||||
h = vfb->height * maxRes;
|
||||
|
||||
Draw::Framebuffer *tempFBO = GetTempFBO(w, h);
|
||||
VirtualFramebuffer tempVfb = *vfb;
|
||||
tempVfb.fbo = tempFBO;
|
||||
tempVfb.bufferWidth = vfb->width;
|
||||
tempVfb.bufferHeight = vfb->height;
|
||||
tempVfb.renderWidth = w;
|
||||
tempVfb.renderHeight = h;
|
||||
BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0);
|
||||
|
||||
bound = tempFBO;
|
||||
} else {
|
||||
bound = vfb->fbo;
|
||||
}
|
||||
}
|
||||
|
||||
buffer.Allocate(w, h, GE_FORMAT_8888, !useBufferedRendering_, true);
|
||||
bool retval = draw_->CopyFramebufferToMemorySync(bound, Draw::FB_COLOR_BIT, 0, 0, w, h, Draw::DataFormat::R8G8B8A8_UNORM, buffer.GetData(), w);
|
||||
// We may have blitted to a temp FBO.
|
||||
RebindFramebuffer();
|
||||
return retval;
|
||||
}
|
||||
|
@ -278,7 +278,7 @@ public:
|
||||
Draw::Framebuffer *GetTempFBO(u16 w, u16 h, Draw::FBColorDepth colorDepth = Draw::FBO_8888);
|
||||
|
||||
// Debug features
|
||||
virtual bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) = 0;
|
||||
virtual bool GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes);
|
||||
virtual bool GetDepthbuffer(u32 fb_address, int fb_stride, u32 z_address, int z_stride, GPUDebugBuffer &buffer) = 0;
|
||||
virtual bool GetStencilbuffer(u32 fb_address, int fb_stride, GPUDebugBuffer &buffer) = 0;
|
||||
virtual bool GetOutputFramebuffer(GPUDebugBuffer &buffer) = 0;
|
||||
|
@ -133,22 +133,9 @@ FramebufferManagerD3D11::FramebufferManagerD3D11(Draw::DrawContext *draw)
|
||||
ShaderTranslationInit();
|
||||
|
||||
CompilePostShader();
|
||||
|
||||
D3D11_TEXTURE2D_DESC packDesc{};
|
||||
packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
packDesc.BindFlags = 0;
|
||||
packDesc.Width = 512; // 512x512 is the maximum size of a framebuffer on the PSP.
|
||||
packDesc.Height = 512;
|
||||
packDesc.ArraySize = 1;
|
||||
packDesc.MipLevels = 1;
|
||||
packDesc.Usage = D3D11_USAGE_STAGING;
|
||||
packDesc.SampleDesc.Count = 1;
|
||||
packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
ASSERT_SUCCESS(device_->CreateTexture2D(&packDesc, nullptr, &packTexture_));
|
||||
}
|
||||
|
||||
FramebufferManagerD3D11::~FramebufferManagerD3D11() {
|
||||
packTexture_->Release();
|
||||
ShaderTranslationShutdown();
|
||||
|
||||
// Drawing cleanup
|
||||
@ -599,12 +586,12 @@ void FramebufferManagerD3D11::ReadFramebufferToMemory(VirtualFramebuffer *vfb, b
|
||||
OptimizeDownloadRange(vfb, x, y, w, h);
|
||||
if (vfb->renderWidth == vfb->width && vfb->renderHeight == vfb->height) {
|
||||
// No need to blit
|
||||
PackFramebufferD3D11_(vfb, x, y, w, h);
|
||||
PackFramebufferSync_(vfb, x, y, w, h);
|
||||
}
|
||||
else {
|
||||
VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb);
|
||||
BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0);
|
||||
PackFramebufferD3D11_(nvfb, x, y, w, h);
|
||||
PackFramebufferSync_(nvfb, x, y, w, h);
|
||||
}
|
||||
|
||||
textureCacheD3D11_->ForgetLastTexture();
|
||||
@ -639,7 +626,7 @@ void FramebufferManagerD3D11::DownloadFramebufferForClut(u32 fb_address, u32 loa
|
||||
VirtualFramebuffer *nvfb = FindDownloadTempBuffer(vfb);
|
||||
BlitFramebuffer(nvfb, x, y, vfb, x, y, w, h, 0);
|
||||
|
||||
PackFramebufferD3D11_(nvfb, x, y, w, h);
|
||||
PackFramebufferSync_(nvfb, x, y, w, h);
|
||||
|
||||
textureCacheD3D11_->ForgetLastTexture();
|
||||
RebindFramebuffer();
|
||||
@ -762,93 +749,43 @@ void FramebufferManagerD3D11::BlitFramebuffer(VirtualFramebuffer *dst, int dstX,
|
||||
false);
|
||||
}
|
||||
|
||||
// TODO: SSE/NEON
|
||||
// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :)
|
||||
void ConvertFromRGBA8888(u8 *dst, u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, GEBufferFormat format) {
|
||||
// Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP.
|
||||
const u32 *src32 = (const u32 *)src;
|
||||
|
||||
if (format == GE_FORMAT_8888) {
|
||||
u32 *dst32 = (u32 *)dst;
|
||||
if (src == dst) {
|
||||
return;
|
||||
} else {
|
||||
for (u32 y = 0; y < height; ++y) {
|
||||
memcpy(dst32, src32, width * 4);
|
||||
src32 += srcStride;
|
||||
dst32 += dstStride;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// But here it shouldn't matter if they do intersect
|
||||
u16 *dst16 = (u16 *)dst;
|
||||
switch (format) {
|
||||
case GE_FORMAT_565: // BGR 565
|
||||
for (u32 y = 0; y < height; ++y) {
|
||||
ConvertRGBA8888ToRGB565(dst16, src32, width);
|
||||
src32 += srcStride;
|
||||
dst16 += dstStride;
|
||||
}
|
||||
break;
|
||||
case GE_FORMAT_5551: // ABGR 1555
|
||||
for (u32 y = 0; y < height; ++y) {
|
||||
ConvertRGBA8888ToRGBA5551(dst16, src32, width);
|
||||
src32 += srcStride;
|
||||
dst16 += dstStride;
|
||||
}
|
||||
break;
|
||||
case GE_FORMAT_4444: // ABGR 4444
|
||||
for (u32 y = 0; y < height; ++y) {
|
||||
ConvertRGBA8888ToRGBA4444(dst16, src32, width);
|
||||
src32 += srcStride;
|
||||
dst16 += dstStride;
|
||||
}
|
||||
break;
|
||||
case GE_FORMAT_8888:
|
||||
case GE_FORMAT_INVALID:
|
||||
// Not possible.
|
||||
break;
|
||||
}
|
||||
static Draw::DataFormat GEFormatToThin3D(int geFormat) {
|
||||
switch (geFormat) {
|
||||
case GE_FORMAT_4444:
|
||||
return Draw::DataFormat::A4R4G4B4_UNORM_PACK16;
|
||||
case GE_FORMAT_5551:
|
||||
return Draw::DataFormat::A1R5G5B5_UNORM_PACK16;
|
||||
case GE_FORMAT_565:
|
||||
return Draw::DataFormat::R5G6B5_UNORM_PACK16;
|
||||
case GE_FORMAT_8888:
|
||||
return Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
default:
|
||||
return Draw::DataFormat::UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
// This function takes an already correctly-sized framebuffer and packs it into RAM.
|
||||
// Does not need to account for scaling.
|
||||
// Color conversion is currently done on CPU but should be done on GPU.
|
||||
void FramebufferManagerD3D11::PackFramebufferD3D11_(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
|
||||
void FramebufferManagerD3D11::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
|
||||
if (!vfb->fbo) {
|
||||
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferD3D11_: vfb->fbo == 0");
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 fb_address = (0x04000000) | vfb->fb_address;
|
||||
const int dstBpp = vfb->format == GE_FORMAT_8888 ? 4 : 2;
|
||||
|
||||
Draw::DataFormat destFormat = GEFormatToThin3D(vfb->format);
|
||||
const int dstBpp = (int)DataFormatSizeInBytes(destFormat);
|
||||
|
||||
const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp;
|
||||
u8 *destPtr = Memory::GetPointer(fb_address + dstByteOffset);
|
||||
|
||||
// We always need to convert from the framebuffer native format.
|
||||
// Right now that's always 8888.
|
||||
DEBUG_LOG(G3D, "Reading framebuffer to mem, fb_address = %08x", fb_address);
|
||||
ID3D11Texture2D *colorTex = (ID3D11Texture2D *)draw_->GetFramebufferAPITexture(vfb->fbo, Draw::FB_COLOR_BIT, 0);
|
||||
|
||||
// TODO: Only copy the necessary rectangle.
|
||||
D3D11_BOX srcBox{ (UINT)x, (UINT)y, 0, (UINT)(x+w), (UINT)(y+h), 1 };
|
||||
context_->CopySubresourceRegion(packTexture_, 0, x, y, 0, colorTex, 0, &srcBox);
|
||||
|
||||
// Ideally, we'd round robin between two packTexture_, and simply use the other one. Though if the game
|
||||
// does a once-off copy, that won't work at all.
|
||||
|
||||
// BIG GPU STALL
|
||||
D3D11_MAPPED_SUBRESOURCE map;
|
||||
HRESULT result = context_->Map(packTexture_, 0, D3D11_MAP_READ, 0, &map);
|
||||
if (FAILED(result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Handle the other formats? We don't currently create them, I think.
|
||||
const int srcByteOffset = y * map.RowPitch + x * 4;
|
||||
const int dstByteOffset = (y * vfb->fb_stride + x) * dstBpp;
|
||||
// Pixel size always 4 here because we always request BGRA8888.
|
||||
ConvertFromRGBA8888(Memory::GetPointer(fb_address + dstByteOffset), (u8 *)map.pData + srcByteOffset, vfb->fb_stride, map.RowPitch/4, w, h, vfb->format);
|
||||
context_->Unmap(packTexture_, 0);
|
||||
draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, x, y, w, h, destFormat, destPtr, vfb->fb_stride);
|
||||
}
|
||||
|
||||
// Nobody calls this yet.
|
||||
@ -937,75 +874,6 @@ void FramebufferManagerD3D11::Resized() {
|
||||
CompilePostShader();
|
||||
}
|
||||
|
||||
// Lots of this code could be shared (like the downsampling).
|
||||
bool FramebufferManagerD3D11::GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) {
|
||||
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::GetPointer(fb_address | 0x04000000), fb_stride, 512, format);
|
||||
return true;
|
||||
}
|
||||
|
||||
int w = vfb->renderWidth, h = vfb->renderHeight;
|
||||
Draw::Framebuffer *fboForRead = nullptr;
|
||||
if (vfb->fbo) {
|
||||
if (maxRes > 0 && vfb->renderWidth > vfb->width * maxRes) {
|
||||
w = vfb->width * maxRes;
|
||||
h = vfb->height * maxRes;
|
||||
|
||||
Draw::Framebuffer *tempFBO = GetTempFBO(w, h);
|
||||
VirtualFramebuffer tempVfb = *vfb;
|
||||
tempVfb.fbo = tempFBO;
|
||||
tempVfb.bufferWidth = vfb->width;
|
||||
tempVfb.bufferHeight = vfb->height;
|
||||
tempVfb.renderWidth = w;
|
||||
tempVfb.renderHeight = h;
|
||||
BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0);
|
||||
|
||||
fboForRead = tempFBO;
|
||||
} else {
|
||||
fboForRead = vfb->fbo;
|
||||
}
|
||||
}
|
||||
if (!fboForRead)
|
||||
return false;
|
||||
|
||||
buffer.Allocate(w, h, GE_FORMAT_8888, !useBufferedRendering_, true);
|
||||
|
||||
ID3D11Texture2D *packTex;
|
||||
D3D11_TEXTURE2D_DESC packDesc{};
|
||||
packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
packDesc.BindFlags = 0;
|
||||
packDesc.Width = w;
|
||||
packDesc.Height = h;
|
||||
packDesc.ArraySize = 1;
|
||||
packDesc.MipLevels = 1;
|
||||
packDesc.Usage = D3D11_USAGE_STAGING;
|
||||
packDesc.SampleDesc.Count = 1;
|
||||
packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
ASSERT_SUCCESS(device_->CreateTexture2D(&packDesc, nullptr, &packTex));
|
||||
|
||||
ID3D11Texture2D *nativeTex = (ID3D11Texture2D *)draw_->GetFramebufferAPITexture(fboForRead, Draw::FB_COLOR_BIT, 0);
|
||||
context_->CopyResource(packTex, nativeTex);
|
||||
|
||||
D3D11_MAPPED_SUBRESOURCE map;
|
||||
context_->Map(packTex, 0, D3D11_MAP_READ, 0, &map);
|
||||
|
||||
for (int y = 0; y < h; y++) {
|
||||
uint8_t *dest = (uint8_t *)buffer.GetData() + y * w * 4;
|
||||
const uint8_t *src = ((const uint8_t *)map.pData) + map.RowPitch * y;
|
||||
memcpy(dest, src, 4 * w);
|
||||
}
|
||||
|
||||
context_->Unmap(packTex, 0);
|
||||
packTex->Release();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FramebufferManagerD3D11::GetDepthStencilBuffer(VirtualFramebuffer *vfb, GPUDebugBuffer &buffer, bool stencil) {
|
||||
int w = vfb->renderWidth, h = vfb->renderHeight;
|
||||
Draw::Framebuffer *fboForRead = nullptr;
|
||||
|
@ -67,7 +67,6 @@ public:
|
||||
|
||||
virtual bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) 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;
|
||||
@ -94,7 +93,7 @@ private:
|
||||
void BindPostShader(const PostShaderUniforms &uniforms) override;
|
||||
void Bind2DShader() override;
|
||||
void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override;
|
||||
void PackFramebufferD3D11_(VirtualFramebuffer *vfb, int x, int y, int w, int h);
|
||||
void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h);
|
||||
void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h);
|
||||
void SimpleBlit(
|
||||
Draw::Framebuffer *dest, float destX1, float destY1, float destX2, float destY2,
|
||||
@ -135,10 +134,6 @@ private:
|
||||
ShaderManagerD3D11 *shaderManagerD3D11_;
|
||||
DrawEngineD3D11 *drawEngine_;
|
||||
|
||||
// Permanent 1:1 readback texture, 512x512 fixed
|
||||
// For larger debug readbacks, we create/destroy textures on the fly.
|
||||
ID3D11Texture2D *packTexture_;
|
||||
|
||||
// Used by post-processing shader
|
||||
// Postprocessing
|
||||
ID3D11VertexShader *postVertexShader_ = nullptr;
|
||||
|
@ -46,8 +46,6 @@
|
||||
#include "GPU/GLES/DrawEngineGLES.h"
|
||||
#include "GPU/GLES/ShaderManagerGLES.h"
|
||||
|
||||
// #define DEBUG_READ_PIXELS 1
|
||||
|
||||
static const char tex_fs[] =
|
||||
"#if __VERSION__ >= 130\n"
|
||||
"#define varying in\n"
|
||||
@ -807,60 +805,6 @@ void ConvertFromRGBA8888(u8 *dst, const u8 *src, u32 dstStride, u32 srcStride, u
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_READ_PIXELS
|
||||
// TODO: Make more generic.
|
||||
static void LogReadPixelsError(GLenum error) {
|
||||
switch (error) {
|
||||
case GL_NO_ERROR:
|
||||
break;
|
||||
case GL_INVALID_ENUM:
|
||||
ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_ENUM");
|
||||
break;
|
||||
case GL_INVALID_VALUE:
|
||||
ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_VALUE");
|
||||
break;
|
||||
case GL_INVALID_OPERATION:
|
||||
ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_OPERATION");
|
||||
break;
|
||||
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
||||
ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_FRAMEBUFFER_OPERATION");
|
||||
break;
|
||||
case GL_OUT_OF_MEMORY:
|
||||
ERROR_LOG(FRAMEBUF, "glReadPixels: GL_OUT_OF_MEMORY");
|
||||
break;
|
||||
#ifndef USING_GLES2
|
||||
case GL_STACK_UNDERFLOW:
|
||||
ERROR_LOG(FRAMEBUF, "glReadPixels: GL_STACK_UNDERFLOW");
|
||||
break;
|
||||
case GL_STACK_OVERFLOW:
|
||||
ERROR_LOG(FRAMEBUF, "glReadPixels: GL_STACK_OVERFLOW");
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ERROR_LOG(FRAMEBUF, "glReadPixels: %08x", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void SafeGLReadPixels(GLint x, GLint y, GLsizei w, GLsizei h, GLenum fmt, GLenum type, void *pixels) {
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
if (!gl_extensions.IsGLES || (gl_extensions.GLES3 && gl_extensions.gpuVendor != GPU_VENDOR_NVIDIA)) {
|
||||
// Some drivers seem to require we specify this. See #8254.
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, w);
|
||||
}
|
||||
|
||||
glReadPixels(x, y, w, h, fmt, type, pixels);
|
||||
#ifdef DEBUG_READ_PIXELS
|
||||
LogReadPixelsError(glGetError());
|
||||
#endif
|
||||
|
||||
if (!gl_extensions.IsGLES || gl_extensions.GLES3) {
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) {
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
const int MAX_PBO = 2;
|
||||
@ -920,59 +864,35 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) {
|
||||
|
||||
// Order packing/readback of the framebuffer
|
||||
if (vfb) {
|
||||
int pixelType, pixelSize, pixelFormat, align;
|
||||
|
||||
bool reverseOrder = gstate_c.Supports(GPU_PREFER_REVERSE_COLOR_ORDER);
|
||||
Draw::DataFormat dataFmt = Draw::DataFormat::UNDEFINED;
|
||||
switch (vfb->format) {
|
||||
// GL_UNSIGNED_INT_8_8_8_8 returns A B G R (little-endian, tested in Nvidia card/x86 PC)
|
||||
// GL_UNSIGNED_BYTE returns R G B A in consecutive bytes ("big-endian"/not treated as 32-bit value)
|
||||
// We want R G B A, so we use *_REV for 16-bit formats and GL_UNSIGNED_BYTE for 32-bit
|
||||
case GE_FORMAT_4444: // 16 bit RGBA
|
||||
#ifdef USING_GLES2
|
||||
pixelType = GL_UNSIGNED_SHORT_4_4_4_4;
|
||||
#else
|
||||
pixelType = (reverseOrder ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_SHORT_4_4_4_4);
|
||||
#endif
|
||||
pixelFormat = GL_RGBA;
|
||||
pixelSize = 2;
|
||||
align = 2;
|
||||
break;
|
||||
case GE_FORMAT_5551: // 16 bit RGBA
|
||||
#ifdef USING_GLES2
|
||||
pixelType = GL_UNSIGNED_SHORT_5_5_5_1;
|
||||
#else
|
||||
pixelType = (reverseOrder ? GL_UNSIGNED_SHORT_1_5_5_5_REV : GL_UNSIGNED_SHORT_5_5_5_1);
|
||||
#endif
|
||||
pixelFormat = GL_RGBA;
|
||||
pixelSize = 2;
|
||||
align = 2;
|
||||
break;
|
||||
case GE_FORMAT_565: // 16 bit RGB
|
||||
#ifdef USING_GLES2
|
||||
pixelType = GL_UNSIGNED_SHORT_5_6_5;
|
||||
#else
|
||||
pixelType = (reverseOrder ? GL_UNSIGNED_SHORT_5_6_5_REV : GL_UNSIGNED_SHORT_5_6_5);
|
||||
#endif
|
||||
pixelFormat = GL_RGB;
|
||||
pixelSize = 2;
|
||||
align = 2;
|
||||
break;
|
||||
case GE_FORMAT_8888: // 32 bit RGBA
|
||||
default:
|
||||
pixelType = GL_UNSIGNED_BYTE;
|
||||
pixelFormat = GL_RGBA;
|
||||
pixelSize = 4;
|
||||
align = 4;
|
||||
break;
|
||||
case GE_FORMAT_4444:
|
||||
dataFmt = (reverseOrder ? Draw::DataFormat::A4R4G4B4_UNORM_PACK16 : Draw::DataFormat::B4G4R4A4_UNORM_PACK16);
|
||||
break;
|
||||
case GE_FORMAT_5551:
|
||||
dataFmt = (reverseOrder ? Draw::DataFormat::A1R5G5B5_UNORM_PACK16 : Draw::DataFormat::B5G5R5A1_UNORM_PACK16);
|
||||
break;
|
||||
case GE_FORMAT_565:
|
||||
dataFmt = (reverseOrder ? Draw::DataFormat::R5G6B5_UNORM_PACK16 : Draw::DataFormat::B5G6R5_UNORM_PACK16);
|
||||
break;
|
||||
case GE_FORMAT_8888:
|
||||
dataFmt = Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
break;
|
||||
};
|
||||
|
||||
if (useCPU) {
|
||||
dataFmt = Draw::DataFormat::R8G8B8A8_UNORM;
|
||||
}
|
||||
|
||||
int pixelSize = (int)DataFormatSizeInBytes(dataFmt);
|
||||
int align = pixelSize;
|
||||
|
||||
// If using the CPU, we need 4 bytes per pixel always.
|
||||
u32 bufSize = vfb->fb_stride * vfb->height * (useCPU ? 4 : pixelSize);
|
||||
u32 bufSize = vfb->fb_stride * vfb->height * pixelSize;
|
||||
u32 fb_address = (0x04000000) | vfb->fb_address;
|
||||
|
||||
if (vfb->fbo) {
|
||||
draw_->BindFramebufferForRead(vfb->fbo);
|
||||
} else {
|
||||
if (!vfb->fbo) {
|
||||
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferAsync_: vfb->fbo == 0");
|
||||
return;
|
||||
}
|
||||
@ -985,15 +905,8 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) {
|
||||
pixelBufObj_[currentPBO_].maxSize = bufSize;
|
||||
}
|
||||
|
||||
if (useCPU) {
|
||||
// If converting pixel formats on the CPU we'll always request RGBA8888
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||
SafeGLReadPixels(0, 0, vfb->fb_stride, vfb->height, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
} else {
|
||||
// Otherwise we'll directly request the format we need and let the GPU sort it out
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, align);
|
||||
SafeGLReadPixels(0, 0, vfb->fb_stride, vfb->height, pixelFormat, pixelType, 0);
|
||||
}
|
||||
// TODO: This is a hack since PBOs have not been implemented in Thin3D yet (and maybe shouldn't? maybe should do this internally?)
|
||||
draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, 0, vfb->fb_stride, vfb->height, dataFmt, nullptr, vfb->fb_stride);
|
||||
|
||||
unbind = true;
|
||||
|
||||
@ -1014,9 +927,7 @@ void FramebufferManagerGLES::PackFramebufferAsync_(VirtualFramebuffer *vfb) {
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
|
||||
if (vfb->fbo) {
|
||||
draw_->BindFramebufferForRead(vfb->fbo);
|
||||
} else {
|
||||
if (!vfb->fbo) {
|
||||
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackFramebufferSync_: vfb->fbo == 0");
|
||||
return;
|
||||
}
|
||||
@ -1052,18 +963,14 @@ void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x
|
||||
|
||||
if (packed) {
|
||||
DEBUG_LOG(FRAMEBUF, "Reading framebuffer to mem, bufSize = %u, fb_address = %08x", bufSize, fb_address);
|
||||
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||
GLenum glfmt = GL_RGBA;
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
|
||||
SafeGLReadPixels(0, y, h == 1 ? packWidth : vfb->fb_stride, h, glfmt, GL_UNSIGNED_BYTE, packed);
|
||||
|
||||
int packW = h == 1 ? packWidth : vfb->fb_stride; // TODO: What's this about?
|
||||
draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_COLOR_BIT, 0, y, packW, h, Draw::DataFormat::R8G8B8A8_UNORM, packed, packW);
|
||||
if (convert) {
|
||||
ConvertFromRGBA8888(dst, packed, vfb->fb_stride, vfb->fb_stride, packWidth, h, vfb->format);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Move this into Thin3d.
|
||||
if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) {
|
||||
#ifdef USING_GLES2
|
||||
// GLES3 doesn't support using GL_READ_FRAMEBUFFER here.
|
||||
@ -1079,9 +986,7 @@ void FramebufferManagerGLES::PackFramebufferSync_(VirtualFramebuffer *vfb, int x
|
||||
}
|
||||
|
||||
void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h) {
|
||||
if (vfb->fbo) {
|
||||
draw_->BindFramebufferForRead(vfb->fbo);
|
||||
} else {
|
||||
if (!vfb->fbo) {
|
||||
ERROR_LOG_REPORT_ONCE(vfbfbozero, SCEGE, "PackDepthbuffer: vfb->fbo == 0");
|
||||
return;
|
||||
}
|
||||
@ -1099,8 +1004,8 @@ void FramebufferManagerGLES::PackDepthbuffer(VirtualFramebuffer *vfb, int x, int
|
||||
|
||||
DEBUG_LOG(FRAMEBUF, "Reading depthbuffer to mem at %08x for vfb=%08x", z_address, vfb->fb_address);
|
||||
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||
SafeGLReadPixels(0, y, h == 1 ? packWidth : vfb->z_stride, h, GL_DEPTH_COMPONENT, GL_FLOAT, convBuf_);
|
||||
int packW = h == 1 ? packWidth : vfb->z_stride;
|
||||
draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_DEPTH_BIT, 0, y, packW, h, Draw::DataFormat::D32F, convBuf_, packW);
|
||||
|
||||
int dstByteOffset = y * vfb->fb_stride * sizeof(u16);
|
||||
u16 *depth = (u16 *)Memory::GetPointer(z_address + dstByteOffset);
|
||||
@ -1224,60 +1129,13 @@ void FramebufferManagerGLES::Resized() {
|
||||
}
|
||||
}
|
||||
|
||||
bool FramebufferManagerGLES::GetFramebuffer(u32 fb_address, int fb_stride, GEBufferFormat format, GPUDebugBuffer &buffer, int maxRes) {
|
||||
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::GetPointer(fb_address | 0x04000000), fb_stride, 512, format);
|
||||
return true;
|
||||
}
|
||||
|
||||
int w = vfb->renderWidth, h = vfb->renderHeight;
|
||||
if (vfb->fbo) {
|
||||
if (maxRes > 0 && vfb->renderWidth > vfb->width * maxRes) {
|
||||
w = vfb->width * maxRes;
|
||||
h = vfb->height * maxRes;
|
||||
|
||||
Draw::Framebuffer *tempFBO = GetTempFBO(w, h);
|
||||
VirtualFramebuffer tempVfb = *vfb;
|
||||
tempVfb.fbo = tempFBO;
|
||||
tempVfb.bufferWidth = vfb->width;
|
||||
tempVfb.bufferHeight = vfb->height;
|
||||
tempVfb.renderWidth = w;
|
||||
tempVfb.renderHeight = h;
|
||||
BlitFramebuffer(&tempVfb, 0, 0, vfb, 0, 0, vfb->width, vfb->height, 0);
|
||||
|
||||
draw_->BindFramebufferForRead(tempFBO);
|
||||
} else {
|
||||
draw_->BindFramebufferForRead(vfb->fbo);
|
||||
}
|
||||
}
|
||||
|
||||
buffer.Allocate(w, h, GE_FORMAT_8888, !useBufferedRendering_, true);
|
||||
if (gl_extensions.GLES3 || !gl_extensions.IsGLES)
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||
SafeGLReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, buffer.GetData());
|
||||
|
||||
// We may have blitted to a temp FBO.
|
||||
RebindFramebuffer();
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FramebufferManagerGLES::GetOutputFramebuffer(GPUDebugBuffer &buffer) {
|
||||
int pw = PSP_CoreParameter().pixelWidth;
|
||||
int ph = PSP_CoreParameter().pixelHeight;
|
||||
|
||||
// The backbuffer is flipped.
|
||||
// The backbuffer is flipped (last bool)
|
||||
buffer.Allocate(pw, ph, GPU_DBG_FORMAT_888_RGB, true);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
SafeGLReadPixels(0, 0, pw, ph, GL_RGB, GL_UNSIGNED_BYTE, buffer.GetData());
|
||||
draw_->CopyFramebufferToMemorySync(nullptr, Draw::FB_COLOR_BIT, 0, 0, pw, ph, Draw::DataFormat::R8G8B8_UNORM, buffer.GetData(), pw);
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
return true;
|
||||
}
|
||||
@ -1303,12 +1161,7 @@ bool FramebufferManagerGLES::GetDepthbuffer(u32 fb_address, int fb_stride, u32 z
|
||||
} else {
|
||||
buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_FLOAT, !useBufferedRendering_);
|
||||
}
|
||||
if (vfb->fbo)
|
||||
draw_->BindFramebufferForRead(vfb->fbo);
|
||||
if (gl_extensions.GLES3 || !gl_extensions.IsGLES)
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 4);
|
||||
SafeGLReadPixels(0, 0, vfb->renderWidth, vfb->renderHeight, GL_DEPTH_COMPONENT, GL_FLOAT, buffer.GetData());
|
||||
draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_DEPTH_BIT, 0, 0, vfb->renderWidth, vfb->renderHeight, Draw::DataFormat::D32F, buffer.GetData(), vfb->renderWidth);
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
return true;
|
||||
}
|
||||
@ -1328,12 +1181,7 @@ bool FramebufferManagerGLES::GetStencilbuffer(u32 fb_address, int fb_stride, GPU
|
||||
|
||||
#ifndef USING_GLES2
|
||||
buffer.Allocate(vfb->renderWidth, vfb->renderHeight, GPU_DBG_FORMAT_8BIT, !useBufferedRendering_);
|
||||
if (vfb->fbo)
|
||||
draw_->BindFramebufferForRead(vfb->fbo);
|
||||
if (gl_extensions.GLES3 || !gl_extensions.IsGLES)
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 2);
|
||||
SafeGLReadPixels(0, 0, vfb->renderWidth, vfb->renderHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, buffer.GetData());
|
||||
draw_->CopyFramebufferToMemorySync(vfb->fbo, Draw::FB_STENCIL_BIT, 0, 0, vfb->renderWidth, vfb->renderHeight, Draw::DataFormat::S8, buffer.GetData(), vfb->renderWidth);
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
return true;
|
||||
#else
|
||||
|
@ -84,7 +84,6 @@ public:
|
||||
|
||||
bool NotifyStencilUpload(u32 addr, int size, bool skipZero = false) 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;
|
||||
|
@ -27,10 +27,6 @@ D3D11Context::~D3D11Context() {
|
||||
void D3D11Context::SwapBuffers() {
|
||||
swapChain_->Present(0, 0);
|
||||
draw_->HandleEvent(Draw::Event::PRESENTED, 0, 0, nullptr, nullptr);
|
||||
|
||||
// Might be a good idea.
|
||||
// context_->ClearState();
|
||||
//
|
||||
}
|
||||
|
||||
void D3D11Context::SwapInterval(int interval) {
|
||||
|
@ -41,6 +41,20 @@ size_t DataFormatSizeInBytes(DataFormat fmt) {
|
||||
}
|
||||
}
|
||||
|
||||
bool DataFormatIsDepthStencil(DataFormat fmt) {
|
||||
switch (fmt) {
|
||||
case DataFormat::D16:
|
||||
case DataFormat::D24_S8:
|
||||
case DataFormat::S8:
|
||||
case DataFormat::D32F:
|
||||
case DataFormat::D32F_S8:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool RefCountedObject::Release() {
|
||||
if (refcount_ > 0 && refcount_ < 10000) {
|
||||
refcount_--;
|
||||
|
@ -617,6 +617,9 @@ public:
|
||||
|
||||
virtual void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) = 0;
|
||||
virtual 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) = 0;
|
||||
virtual bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// These functions should be self explanatory.
|
||||
// Binding a zero render target means binding the backbuffer.
|
||||
@ -624,7 +627,6 @@ public:
|
||||
|
||||
// color must be 0, for now.
|
||||
virtual void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) = 0;
|
||||
virtual void BindFramebufferForRead(Framebuffer *fbo) = 0;
|
||||
|
||||
virtual uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) = 0;
|
||||
|
||||
@ -699,6 +701,10 @@ protected:
|
||||
};
|
||||
|
||||
size_t DataFormatSizeInBytes(DataFormat fmt);
|
||||
bool DataFormatIsDepthStencil(DataFormat fmt);
|
||||
inline bool DataFormatIsColor(DataFormat fmt) {
|
||||
return !DataFormatIsDepthStencil(fmt);
|
||||
}
|
||||
|
||||
DrawContext *T3DCreateGLContext();
|
||||
|
||||
|
@ -10,6 +10,9 @@
|
||||
#include "math/dataconv.h"
|
||||
#include "util/text/utf8.h"
|
||||
|
||||
#include "Common/ColorConv.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <D3DCommon.h>
|
||||
#include <d3d11.h>
|
||||
#include <d3d11_1.h>
|
||||
@ -27,7 +30,6 @@ class D3D11DepthStencilState;
|
||||
class D3D11SamplerState;
|
||||
class D3D11RasterState;
|
||||
|
||||
|
||||
class D3D11DrawContext : public DrawContext {
|
||||
public:
|
||||
D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *deviceContext, ID3D11Device1 *device1, ID3D11DeviceContext1 *deviceContext1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd);
|
||||
@ -56,12 +58,12 @@ public:
|
||||
|
||||
void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) override;
|
||||
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) override;
|
||||
bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride);
|
||||
|
||||
// These functions should be self explanatory.
|
||||
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) override;
|
||||
// color must be 0, for now.
|
||||
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override;
|
||||
void BindFramebufferForRead(Framebuffer *fbo) override;
|
||||
|
||||
uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) override;
|
||||
|
||||
@ -194,6 +196,9 @@ private:
|
||||
uint8_t stencilRef_;
|
||||
bool stencilRefDirty_;
|
||||
|
||||
// Temporaries
|
||||
ID3D11Texture2D *packTexture_ = nullptr;
|
||||
|
||||
// System info
|
||||
D3D_FEATURE_LEVEL featureLevel_;
|
||||
std::string adapterDesc_;
|
||||
@ -238,9 +243,26 @@ D3D11DrawContext::D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *de
|
||||
dxgiDevice->Release();
|
||||
}
|
||||
CreatePresets();
|
||||
|
||||
// Temp texture for read-back of small images. Custom textures are created on demand for larger ones.
|
||||
// TODO: Should really benchmark if this extra complexity has any benefit.
|
||||
D3D11_TEXTURE2D_DESC packDesc{};
|
||||
packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
packDesc.BindFlags = 0;
|
||||
packDesc.Width = 512;
|
||||
packDesc.Height = 512;
|
||||
packDesc.ArraySize = 1;
|
||||
packDesc.MipLevels = 1;
|
||||
packDesc.Usage = D3D11_USAGE_STAGING;
|
||||
packDesc.SampleDesc.Count = 1;
|
||||
packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
hr = device_->CreateTexture2D(&packDesc, nullptr, &packTexture_);
|
||||
assert(SUCCEEDED(hr));
|
||||
}
|
||||
|
||||
D3D11DrawContext::~D3D11DrawContext() {
|
||||
packTexture_->Release();
|
||||
|
||||
// Release references.
|
||||
ID3D11RenderTargetView *view = nullptr;
|
||||
context_->OMSetRenderTargets(1, &view, nullptr);
|
||||
@ -1286,6 +1308,149 @@ bool D3D11DrawContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1,
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: SSE/NEON
|
||||
// Could also make C fake-simd for 64-bit, two 8888 pixels fit in a register :)
|
||||
void ConvertFromRGBA8888(u8 *dst, u8 *src, u32 dstStride, u32 srcStride, u32 width, u32 height, Draw::DataFormat format) {
|
||||
// Must skip stride in the cases below. Some games pack data into the cracks, like MotoGP.
|
||||
const u32 *src32 = (const u32 *)src;
|
||||
|
||||
if (format == Draw::DataFormat::R8G8B8A8_UNORM) {
|
||||
u32 *dst32 = (u32 *)dst;
|
||||
if (src == dst) {
|
||||
return;
|
||||
} else {
|
||||
for (u32 y = 0; y < height; ++y) {
|
||||
memcpy(dst32, src32, width * 4);
|
||||
src32 += srcStride;
|
||||
dst32 += dstStride;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// But here it shouldn't matter if they do intersect
|
||||
u16 *dst16 = (u16 *)dst;
|
||||
switch (format) {
|
||||
case Draw::DataFormat::R5G6B5_UNORM_PACK16: // BGR 565
|
||||
for (u32 y = 0; y < height; ++y) {
|
||||
ConvertRGBA8888ToRGB565(dst16, src32, width);
|
||||
src32 += srcStride;
|
||||
dst16 += dstStride;
|
||||
}
|
||||
break;
|
||||
case Draw::DataFormat::A1R5G5B5_UNORM_PACK16: // ABGR 1555
|
||||
for (u32 y = 0; y < height; ++y) {
|
||||
ConvertRGBA8888ToRGBA5551(dst16, src32, width);
|
||||
src32 += srcStride;
|
||||
dst16 += dstStride;
|
||||
}
|
||||
break;
|
||||
case Draw::DataFormat::A4R4G4B4_UNORM_PACK16: // ABGR 4444
|
||||
for (u32 y = 0; y < height; ++y) {
|
||||
ConvertRGBA8888ToRGBA4444(dst16, src32, width);
|
||||
src32 += srcStride;
|
||||
dst16 += dstStride;
|
||||
}
|
||||
break;
|
||||
case Draw::DataFormat::R8G8B8A8_UNORM:
|
||||
case Draw::DataFormat::UNDEFINED:
|
||||
// Not possible.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int bx, int by, int bw, int bh, Draw::DataFormat format, void *pixels, int pixelStride) {
|
||||
D3D11Framebuffer *fb = (D3D11Framebuffer *)src;
|
||||
|
||||
assert(fb->colorFormat == DXGI_FORMAT_R8G8B8A8_UNORM);
|
||||
|
||||
bool useGlobalPacktex = (bx + bw <= 512 && by + bh <= 512) && channelBits == FB_COLOR_BIT;
|
||||
|
||||
ID3D11Texture2D *packTex;
|
||||
if (!useGlobalPacktex) {
|
||||
D3D11_TEXTURE2D_DESC packDesc{};
|
||||
packDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
packDesc.BindFlags = 0;
|
||||
packDesc.Width = bw;
|
||||
packDesc.Height = bh;
|
||||
packDesc.ArraySize = 1;
|
||||
packDesc.MipLevels = 1;
|
||||
packDesc.Usage = D3D11_USAGE_STAGING;
|
||||
packDesc.SampleDesc.Count = 1;
|
||||
D3D11_BOX srcBox{ (UINT)bx, (UINT)by, 0, (UINT)(bx + bw), (UINT)(by + bh), 1 };
|
||||
switch (channelBits) {
|
||||
case FB_COLOR_BIT:
|
||||
packDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // TODO: fb->colorFormat;
|
||||
break;
|
||||
case FB_DEPTH_BIT:
|
||||
case FB_STENCIL_BIT:
|
||||
packDesc.Format = fb->depthStencilFormat;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
device_->CreateTexture2D(&packDesc, nullptr, &packTex);
|
||||
} else {
|
||||
packTex = packTexture_;
|
||||
}
|
||||
|
||||
D3D11_BOX srcBox{ (UINT)bx, (UINT)by, 0, (UINT)(bx + bw), (UINT)(by + bh), 1 };
|
||||
switch (channelBits) {
|
||||
case FB_COLOR_BIT:
|
||||
context_->CopySubresourceRegion(packTex, 0, bx, by, 0, fb->colorTex, 0, &srcBox);
|
||||
break;
|
||||
case FB_DEPTH_BIT:
|
||||
case FB_STENCIL_BIT:
|
||||
// For depth/stencil buffers, we can't reliably copy subrectangles, so just copy the whole resource.
|
||||
context_->CopyResource(packTex, fb->colorTex);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// Ideally, we'd round robin between two packTexture_, and simply use the other one. Though if the game
|
||||
// does a once-off copy, that won't work at all.
|
||||
|
||||
// BIG GPU STALL
|
||||
D3D11_MAPPED_SUBRESOURCE map;
|
||||
HRESULT result = context_->Map(packTex, 0, D3D11_MAP_READ, 0, &map);
|
||||
if (FAILED(result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int srcByteOffset = by * map.RowPitch + bx * 4;
|
||||
switch (channelBits) {
|
||||
case FB_COLOR_BIT:
|
||||
// Pixel size always 4 here because we always request BGRA8888.
|
||||
ConvertFromRGBA8888((uint8_t *)pixels, (uint8_t *)map.pData + srcByteOffset, pixelStride, map.RowPitch / sizeof(uint32_t), bw, bh, format);
|
||||
break;
|
||||
case FB_DEPTH_BIT:
|
||||
for (int y = by; y < by + bh; y++) {
|
||||
float *dest = (float *)((uint8_t *)pixels + y * pixelStride * sizeof(float));
|
||||
const uint32_t *src = (const uint32_t *)((const uint8_t *)map.pData + map.RowPitch * y);
|
||||
for (int x = bx; x < bx + bw; x++) {
|
||||
dest[x] = (src[x] & 0xFFFFFF) / (256.f * 256.f * 256.f);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FB_STENCIL_BIT:
|
||||
for (int y = by; y < by + bh; y++) {
|
||||
uint8_t *destStencil = (uint8_t *)pixels + y * pixelStride;
|
||||
const uint32_t *src = (const uint32_t *)((const uint8_t *)map.pData + map.RowPitch * y);
|
||||
for (int x = bx; x < bx + bw; x++) {
|
||||
destStencil[x] = src[x] >> 24;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
context_->Unmap(packTex, 0);
|
||||
|
||||
if (!useGlobalPacktex) {
|
||||
packTex->Release();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void D3D11DrawContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) {
|
||||
// TODO: deviceContext1 can actually discard. Useful on Windows Mobile.
|
||||
if (fbo) {
|
||||
@ -1327,10 +1492,6 @@ void D3D11DrawContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, F
|
||||
context_->PSSetShaderResources(binding, 1, &fb->colorSRView);
|
||||
}
|
||||
|
||||
void D3D11DrawContext::BindFramebufferForRead(Framebuffer *fbo) {
|
||||
// This is meaningless in D3D11
|
||||
}
|
||||
|
||||
uintptr_t D3D11DrawContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) {
|
||||
D3D11Framebuffer *fb = (D3D11Framebuffer *)fbo;
|
||||
switch (channelBit) {
|
||||
|
@ -490,7 +490,6 @@ public:
|
||||
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) override;
|
||||
// color must be 0, for now.
|
||||
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override;
|
||||
void BindFramebufferForRead(Framebuffer *fbo) override {}
|
||||
|
||||
uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) override;
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <cassert>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "math/dataconv.h"
|
||||
@ -18,6 +19,8 @@
|
||||
extern void bindDefaultFBO();
|
||||
#endif
|
||||
|
||||
// #define DEBUG_READ_PIXELS 1
|
||||
|
||||
// Workaround for Retroarch. Simply declare
|
||||
// extern GLuint g_defaultFBO;
|
||||
// and set is as appropriate. Can adjust the variables in ext/native/base/display.h as
|
||||
@ -465,12 +468,12 @@ public:
|
||||
|
||||
void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) override;
|
||||
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) override;
|
||||
bool CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat format, void *pixels, int pixelStride) override;
|
||||
|
||||
// These functions should be self explanatory.
|
||||
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) override;
|
||||
// color must be 0, for now.
|
||||
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override;
|
||||
void BindFramebufferForRead(Framebuffer *fbo) override;
|
||||
|
||||
uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) override;
|
||||
|
||||
@ -729,11 +732,121 @@ void OpenGLTexture::AutoGenMipmaps() {
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) {
|
||||
int internalFormat;
|
||||
int format;
|
||||
int type;
|
||||
class OpenGLFramebuffer : public Framebuffer, public GfxResourceHolder {
|
||||
public:
|
||||
OpenGLFramebuffer() {
|
||||
register_gl_resource_holder(this, "framebuffer", 0);
|
||||
}
|
||||
~OpenGLFramebuffer();
|
||||
|
||||
void GLLost() override {
|
||||
handle = 0;
|
||||
color_texture = 0;
|
||||
z_stencil_buffer = 0;
|
||||
z_buffer = 0;
|
||||
stencil_buffer = 0;
|
||||
}
|
||||
|
||||
void GLRestore() override {
|
||||
ELOG("Restoring framebuffers not yet implemented");
|
||||
}
|
||||
|
||||
GLuint handle = 0;
|
||||
GLuint color_texture = 0;
|
||||
GLuint z_stencil_buffer = 0; // Either this is set, or the two below.
|
||||
GLuint z_buffer = 0;
|
||||
GLuint stencil_buffer = 0;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
FBColorDepth colorDepth;
|
||||
};
|
||||
|
||||
|
||||
// TODO: Also output storage format (GL_RGBA8 etc) for modern GL usage.
|
||||
static bool Thin3DFormatToFormatAndType(DataFormat fmt, GLuint &internalFormat, GLuint &format, GLuint &type, int &alignment) {
|
||||
alignment = 4;
|
||||
switch (fmt) {
|
||||
case DataFormat::R8G8B8A8_UNORM:
|
||||
internalFormat = GL_RGBA;
|
||||
format = GL_RGBA;
|
||||
type = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
|
||||
case DataFormat::D32F:
|
||||
internalFormat = GL_DEPTH_COMPONENT;
|
||||
format = GL_DEPTH_COMPONENT;
|
||||
type = GL_FLOAT;
|
||||
break;
|
||||
|
||||
#ifndef USING_GLES2
|
||||
case DataFormat::S8:
|
||||
internalFormat = GL_STENCIL_INDEX;
|
||||
format = GL_STENCIL_INDEX;
|
||||
type = GL_UNSIGNED_BYTE;
|
||||
alignment = 1;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case DataFormat::R8G8B8_UNORM:
|
||||
internalFormat = GL_RGB;
|
||||
format = GL_RGB;
|
||||
type = GL_UNSIGNED_BYTE;
|
||||
alignment = 1;
|
||||
break;
|
||||
|
||||
case DataFormat::B4G4R4A4_UNORM_PACK16:
|
||||
internalFormat = GL_RGBA;
|
||||
format = GL_RGBA;
|
||||
type = GL_UNSIGNED_SHORT_4_4_4_4;
|
||||
alignment = 2;
|
||||
break;
|
||||
|
||||
case DataFormat::B5G6R5_UNORM_PACK16:
|
||||
internalFormat = GL_RGB;
|
||||
format = GL_RGB;
|
||||
type = GL_UNSIGNED_SHORT_5_6_5;
|
||||
alignment = 2;
|
||||
break;
|
||||
|
||||
case DataFormat::B5G5R5A1_UNORM_PACK16:
|
||||
internalFormat = GL_RGBA;
|
||||
format = GL_RGBA;
|
||||
type = GL_UNSIGNED_SHORT_5_5_5_1;
|
||||
alignment = 2;
|
||||
break;
|
||||
|
||||
#ifndef USING_GLES2
|
||||
case DataFormat::A4R4G4B4_UNORM_PACK16:
|
||||
internalFormat = GL_RGBA;
|
||||
format = GL_RGBA;
|
||||
type = GL_UNSIGNED_SHORT_4_4_4_4_REV;
|
||||
alignment = 2;
|
||||
break;
|
||||
|
||||
case DataFormat::R5G6B5_UNORM_PACK16:
|
||||
internalFormat = GL_RGB;
|
||||
format = GL_RGB;
|
||||
type = GL_UNSIGNED_SHORT_5_6_5_REV;
|
||||
alignment = 2;
|
||||
break;
|
||||
|
||||
case DataFormat::A1R5G5B5_UNORM_PACK16:
|
||||
internalFormat = GL_RGBA;
|
||||
format = GL_RGBA;
|
||||
type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
|
||||
alignment = 2;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ELOG("Thin3d GL: Unsupported texture format %d", (int)fmt);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) {
|
||||
if (width != width_ || height != height_ || depth != depth_) {
|
||||
// When switching to texStorage we need to handle this correctly.
|
||||
width_ = width;
|
||||
@ -741,26 +854,11 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int
|
||||
depth_ = depth;
|
||||
}
|
||||
|
||||
switch (format_) {
|
||||
case DataFormat::R8G8B8A8_UNORM:
|
||||
internalFormat = GL_RGBA;
|
||||
format = GL_RGBA;
|
||||
type = GL_UNSIGNED_BYTE;
|
||||
break;
|
||||
case DataFormat::B4G4R4A4_UNORM_PACK16:
|
||||
internalFormat = GL_RGBA;
|
||||
format = GL_RGBA;
|
||||
type = GL_UNSIGNED_SHORT_4_4_4_4;
|
||||
break;
|
||||
#ifndef USING_GLES2
|
||||
case DataFormat::A4R4G4B4_UNORM_PACK16:
|
||||
internalFormat = GL_RGBA;
|
||||
format = GL_RGBA;
|
||||
type = GL_UNSIGNED_SHORT_4_4_4_4_REV;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ELOG("Thin3d GL: Unsupported texture format %d", (int)format_);
|
||||
GLuint internalFormat;
|
||||
GLuint format;
|
||||
GLuint type;
|
||||
int alignment;
|
||||
if (!Thin3DFormatToFormatAndType(format_, internalFormat, format, type, alignment)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -776,6 +874,80 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG_READ_PIXELS
|
||||
// TODO: Make more generic.
|
||||
static void LogReadPixelsError(GLenum error) {
|
||||
switch (error) {
|
||||
case GL_NO_ERROR:
|
||||
break;
|
||||
case GL_INVALID_ENUM:
|
||||
ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_ENUM");
|
||||
break;
|
||||
case GL_INVALID_VALUE:
|
||||
ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_VALUE");
|
||||
break;
|
||||
case GL_INVALID_OPERATION:
|
||||
ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_OPERATION");
|
||||
break;
|
||||
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
||||
ERROR_LOG(FRAMEBUF, "glReadPixels: GL_INVALID_FRAMEBUFFER_OPERATION");
|
||||
break;
|
||||
case GL_OUT_OF_MEMORY:
|
||||
ERROR_LOG(FRAMEBUF, "glReadPixels: GL_OUT_OF_MEMORY");
|
||||
break;
|
||||
#ifndef USING_GLES2
|
||||
case GL_STACK_UNDERFLOW:
|
||||
ERROR_LOG(FRAMEBUF, "glReadPixels: GL_STACK_UNDERFLOW");
|
||||
break;
|
||||
case GL_STACK_OVERFLOW:
|
||||
ERROR_LOG(FRAMEBUF, "glReadPixels: GL_STACK_OVERFLOW");
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ERROR_LOG(FRAMEBUF, "glReadPixels: %08x", error);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool OpenGLContext::CopyFramebufferToMemorySync(Framebuffer *src, int channelBits, int x, int y, int w, int h, Draw::DataFormat dataFormat, void *pixels, int pixelStride) {
|
||||
OpenGLFramebuffer *fb = (OpenGLFramebuffer *)src;
|
||||
fbo_bind_fb_target(true, fb ? fb->handle : 0);
|
||||
|
||||
// Reads from the "bound for read" framebuffer.
|
||||
if (gl_extensions.GLES3 || !gl_extensions.IsGLES)
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
|
||||
GLuint internalFormat;
|
||||
GLuint format;
|
||||
GLuint type;
|
||||
int alignment;
|
||||
if (!Thin3DFormatToFormatAndType(dataFormat, internalFormat, format, type, alignment)) {
|
||||
assert(false);
|
||||
}
|
||||
// Apply the correct alignment.
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, alignment);
|
||||
if (!gl_extensions.IsGLES || (gl_extensions.GLES3 && gl_extensions.gpuVendor != GPU_VENDOR_NVIDIA)) {
|
||||
// Some drivers seem to require we specify this. See #8254.
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, pixelStride);
|
||||
}
|
||||
|
||||
glReadPixels(x, y, w, h, format, type, pixels);
|
||||
#ifdef DEBUG_READ_PIXELS
|
||||
LogReadPixelsError(glGetError());
|
||||
#endif
|
||||
|
||||
if (!gl_extensions.IsGLES || gl_extensions.GLES3) {
|
||||
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
||||
}
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Texture *OpenGLContext::CreateTexture(const TextureDesc &desc) {
|
||||
return new OpenGLTexture(desc);
|
||||
}
|
||||
@ -1243,36 +1415,6 @@ void OpenGLInputLayout::Unapply() {
|
||||
}
|
||||
}
|
||||
|
||||
class OpenGLFramebuffer : public Framebuffer, public GfxResourceHolder {
|
||||
public:
|
||||
OpenGLFramebuffer() {
|
||||
register_gl_resource_holder(this, "framebuffer", 0);
|
||||
}
|
||||
~OpenGLFramebuffer();
|
||||
|
||||
void GLLost() override {
|
||||
handle = 0;
|
||||
color_texture = 0;
|
||||
z_stencil_buffer = 0;
|
||||
z_buffer = 0;
|
||||
stencil_buffer = 0;
|
||||
}
|
||||
|
||||
void GLRestore() override {
|
||||
ELOG("Restoring framebuffers not yet implemented");
|
||||
}
|
||||
|
||||
GLuint handle = 0;
|
||||
GLuint color_texture = 0;
|
||||
GLuint z_stencil_buffer = 0; // Either this is set, or the two below.
|
||||
GLuint z_buffer = 0;
|
||||
GLuint stencil_buffer = 0;
|
||||
|
||||
int width;
|
||||
int height;
|
||||
FBColorDepth colorDepth;
|
||||
};
|
||||
|
||||
// On PC, we always use GL_DEPTH24_STENCIL8.
|
||||
// On Android, we try to use what's available.
|
||||
|
||||
@ -1587,13 +1729,6 @@ void OpenGLContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const Render
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
// For GL_EXT_FRAMEBUFFER_BLIT and similar.
|
||||
void OpenGLContext::BindFramebufferForRead(Framebuffer *fbo) {
|
||||
OpenGLFramebuffer *fb = (OpenGLFramebuffer *)fbo;
|
||||
fbo_bind_fb_target(true, fb->handle);
|
||||
CHECK_GL_ERROR_IF_DEBUG();
|
||||
}
|
||||
|
||||
void OpenGLContext::CopyFramebufferImage(Framebuffer *fbsrc, int srcLevel, int srcX, int srcY, int srcZ, Framebuffer *fbdst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits) {
|
||||
OpenGLFramebuffer *src = (OpenGLFramebuffer *)fbsrc;
|
||||
OpenGLFramebuffer *dst = (OpenGLFramebuffer *)fbdst;
|
||||
|
@ -392,7 +392,6 @@ public:
|
||||
void BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPassInfo &rp) override;
|
||||
// color must be 0, for now.
|
||||
void BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int attachment) override;
|
||||
void BindFramebufferForRead(Framebuffer *fbo) override;
|
||||
|
||||
uintptr_t GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) override;
|
||||
|
||||
@ -2022,8 +2021,6 @@ void VKContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChanne
|
||||
fb->color.layout = barrier.newLayout;
|
||||
}
|
||||
|
||||
void VKContext::BindFramebufferForRead(Framebuffer *fbo) { /* noop */ }
|
||||
|
||||
uintptr_t VKContext::GetFramebufferAPITexture(Framebuffer *fbo, int channelBit, int attachment) {
|
||||
// TODO: Insert transition at the end of the previous command buffer, or the one that rendered to it last.
|
||||
VKFramebuffer *fb = (VKFramebuffer *)fbo;
|
||||
|
Loading…
Reference in New Issue
Block a user