dx9: Take screenshots (fixes #7197.)

This commit is contained in:
Unknown W. Brackets 2014-12-20 08:31:56 -08:00
parent 8d68c055cc
commit 962d8c5224
10 changed files with 128 additions and 53 deletions

View File

@ -57,6 +57,9 @@ enum GPUDebugBufferFormat {
GPU_DBG_FORMAT_8BIT = 0x12,
GPU_DBG_FORMAT_24BIT_8X = 0x13,
GPU_DBG_FORMAT_24X_8BIT = 0x14,
// This is used for screenshots, mainly.
GPU_DBG_FORMAT_888_RGB = 0x20,
};
inline GPUDebugBufferFormat &operator |=(GPUDebugBufferFormat &lhs, const GPUDebugBufferFormat &rhs) {
@ -149,6 +152,10 @@ struct GPUDebugBuffer {
pixelSize = 4;
break;
case GPU_DBG_FORMAT_888_RGB:
pixelSize = 3;
break;
case GPU_DBG_FORMAT_8BIT:
pixelSize = 1;
break;

View File

@ -1256,7 +1256,6 @@ namespace DX9 {
resized_ = true;
}
bool FramebufferManagerDX9::GetCurrentFramebuffer(GPUDebugBuffer &buffer) {
u32 fb_address = gstate.getFrameBufRawAddress();
int fb_stride = gstate.FrameBufStride();
@ -1272,26 +1271,49 @@ namespace DX9 {
return true;
}
LPDIRECT3DSURFACE9 renderTarget = nullptr;
HRESULT hr;
hr = pD3Ddevice->GetRenderTarget(0, &renderTarget);
if (!renderTarget || !SUCCEEDED(hr))
return false;
LPDIRECT3DSURFACE9 renderTarget = vfb->fbo ? fbo_get_color_for_read(vfb->fbo) : nullptr;
bool success = false;
if (renderTarget) {
LPDIRECT3DSURFACE9 offscreen = GetOffscreenSurface(renderTarget);
if (offscreen) {
success = GetRenderTargetFramebuffer(renderTarget, offscreen, vfb->renderWidth, vfb->renderHeight, buffer);
}
}
return success;
}
bool FramebufferManagerDX9::GetDisplayFramebuffer(GPUDebugBuffer &buffer) {
fbo_unbind();
LPDIRECT3DSURFACE9 renderTarget = nullptr;
HRESULT hr = pD3Ddevice->GetRenderTarget(0, &renderTarget);
bool success = false;
if (renderTarget && SUCCEEDED(hr)) {
D3DSURFACE_DESC desc;
renderTarget->GetDesc(&desc);
LPDIRECT3DSURFACE9 offscreen = nullptr;
HRESULT hr = pD3Ddevice->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);
LPDIRECT3DSURFACE9 offscreen = GetOffscreenSurface(renderTarget);
if (!offscreen) {
renderTarget->Release();
return false;
}
bool success = false;
hr = pD3Ddevice->GetRenderTargetData(renderTarget, offscreen);
HRESULT hr = pD3Ddevice->GetRenderTargetData(renderTarget, offscreen);
if (SUCCEEDED(hr)) {
D3DLOCKED_RECT locked;
RECT rect = {0, 0, vfb->renderWidth, vfb->renderHeight};
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.
@ -1302,8 +1324,6 @@ namespace DX9 {
}
}
renderTarget->Release();
return success;
}

View File

@ -87,6 +87,7 @@ public:
bool GetCurrentFramebuffer(GPUDebugBuffer &buffer);
bool GetCurrentDepthbuffer(GPUDebugBuffer &buffer);
bool GetCurrentStencilbuffer(GPUDebugBuffer &buffer);
static bool GetDisplayFramebuffer(GPUDebugBuffer &buffer);
virtual void RebindFramebuffer() override;
@ -114,6 +115,7 @@ private:
void SetNumExtraFBOs(int num);
void PackFramebufferDirectx9_(VirtualFramebuffer *vfb, int x, int y, int w, int h);
static bool GetRenderTargetFramebuffer(LPDIRECT3DSURFACE9 renderTarget, LPDIRECT3DSURFACE9 offscreen, int w, int h, GPUDebugBuffer &buffer);
// Used by DrawPixels
LPDIRECT3DTEXTURE9 drawPixelsTex_;

View File

@ -2129,6 +2129,10 @@ bool DIRECTX9_GPU::GetCurrentTexture(GPUDebugBuffer &buffer, int level) {
return success;
}
bool DIRECTX9_GPU::GetDisplayFramebuffer(GPUDebugBuffer &buffer) {
return FramebufferManagerDX9::GetDisplayFramebuffer(buffer);
}
bool DIRECTX9_GPU::GetCurrentSimpleVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices) {
return transformDraw_.GetCurrentSimpleVertices(count, vertices, indices);
}

View File

@ -76,6 +76,7 @@ public:
bool GetCurrentDepthbuffer(GPUDebugBuffer &buffer);
bool GetCurrentStencilbuffer(GPUDebugBuffer &buffer);
bool GetCurrentTexture(GPUDebugBuffer &buffer, int level);
static bool GetDisplayFramebuffer(GPUDebugBuffer &buffer);
bool GetCurrentSimpleVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices);
typedef void (DIRECTX9_GPU::*CmdFunc)(u32 op, u32 diff);

View File

@ -1841,6 +1841,14 @@ bool FramebufferManager::GetCurrentFramebuffer(GPUDebugBuffer &buffer) {
return true;
}
bool FramebufferManager::GetDisplayFramebuffer(GPUDebugBuffer &buffer) {
fbo_unbind_read();
buffer.Allocate(PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight, GPU_DBG_FORMAT_888_RGB, true);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight, GL_RGB, GL_UNSIGNED_BYTE, buffer.GetData());
return true;
}
bool FramebufferManager::GetCurrentDepthbuffer(GPUDebugBuffer &buffer) {
u32 fb_address = gstate.getFrameBufRawAddress();
int fb_stride = gstate.FrameBufStride();

View File

@ -109,6 +109,7 @@ public:
bool GetCurrentFramebuffer(GPUDebugBuffer &buffer);
bool GetCurrentDepthbuffer(GPUDebugBuffer &buffer);
bool GetCurrentStencilbuffer(GPUDebugBuffer &buffer);
static bool GetDisplayFramebuffer(GPUDebugBuffer &buffer);
virtual void RebindFramebuffer() override;

View File

@ -2226,6 +2226,10 @@ bool GLES_GPU::GetCurrentTexture(GPUDebugBuffer &buffer, int level) {
#endif
}
bool GLES_GPU::GetDisplayFramebuffer(GPUDebugBuffer &buffer) {
return FramebufferManager::GetDisplayFramebuffer(buffer);
}
bool GLES_GPU::GetCurrentSimpleVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices) {
return transformDraw_.GetCurrentSimpleVertices(count, vertices, indices);
}

View File

@ -78,6 +78,7 @@ public:
bool GetCurrentDepthbuffer(GPUDebugBuffer &buffer);
bool GetCurrentStencilbuffer(GPUDebugBuffer &buffer);
bool GetCurrentTexture(GPUDebugBuffer &buffer, int level);
static bool GetDisplayFramebuffer(GPUDebugBuffer &buffer);
bool GetCurrentSimpleVertices(int count, std::vector<GPUDebugVertex> &vertices, std::vector<u16> &indices);
virtual bool DescribeCodePtr(const u8 *ptr, std::string &name);

View File

@ -70,6 +70,7 @@
#include "Common/CPUDetect.h"
#include "Common/FileUtil.h"
#include "Common/LogManager.h"
#include "Common/MemArena.h"
#include "Core/Config.h"
#include "Core/Core.h"
#include "Core/Host.h"
@ -78,7 +79,8 @@
#include "Core/System.h"
#include "Core/HLE/sceCtrl.h"
#include "Core/Util/GameManager.h"
#include "Common/MemArena.h"
#include "GPU/GLES/GLES_GPU.h"
#include "GPU/Directx9/GPU_DX9.h"
#include "ui_atlas.h"
#include "EmuScreen.h"
@ -598,11 +600,6 @@ void NativeShutdownGraphics() {
}
void TakeScreenshot() {
if (g_Config.iGPUBackend != GPU_BACKEND_OPENGL) {
// Not yet supported
return;
}
g_TakeScreenshot = false;
#if defined(_WIN32) || (defined(USING_QT_UI) && !defined(MOBILE_DEVICE))
@ -628,42 +625,72 @@ void TakeScreenshot() {
i++;
}
// Okay, allocate a buffer.
u8 *buffer = new u8[3 * pixel_xres * pixel_yres];
glReadPixels(0, 0, pixel_xres, pixel_yres, GL_RGB, GL_UNSIGNED_BYTE, buffer);
GPUDebugBuffer buf;
bool success = true;
#ifdef USING_QT_UI
QImage image(buffer, pixel_xres, pixel_yres, QImage::Format_RGB888);
image = image.mirrored();
success = image.save(filename, g_Config.bScreenshotsAsPNG ? "PNG" : "JPG");
#else
// Silly openGL reads upside down, we flip to another buffer for simplicity.
u8 *flipbuffer = new u8[3 * pixel_xres * pixel_yres];
for (int y = 0; y < pixel_yres; y++) {
memcpy(flipbuffer + y * pixel_xres * 3, buffer + (pixel_yres - y - 1) * pixel_xres * 3, pixel_xres * 3);
}
if (g_Config.bScreenshotsAsPNG) {
png_image png;
memset(&png, 0, sizeof(png));
png.version = PNG_IMAGE_VERSION;
png.format = PNG_FORMAT_RGB;
png.width = pixel_xres;
png.height = pixel_yres;
png_image_write_to_file(&png, filename, 0, flipbuffer, pixel_xres * 3, NULL);
png_image_free(&png);
success = png.warning_or_error >= 2;
if (g_Config.iGPUBackend == GPU_BACKEND_OPENGL) {
success = GLES_GPU::GetDisplayFramebuffer(buf);
} else if (g_Config.iGPUBackend == GPU_BACKEND_DIRECT3D9) {
success = DX9::DIRECTX9_GPU::GetDisplayFramebuffer(buf);
} else {
jpge::params params;
params.m_quality = 90;
success = compress_image_to_jpeg_file(filename, pixel_xres, pixel_yres, 3, flipbuffer, params);
success = false;
}
delete [] flipbuffer;
#endif
delete [] buffer;
#ifdef USING_QT_UI
if (success) {
// TODO: Handle other formats (e.g. Direct3D.) Would only happen on Qt/Windows.
const u8 *buffer = buf.GetData();
QImage image(buffer, buf.GetStride(), buf.GetHeight(), QImage::Format_RGB888);
image = image.mirrored();
success = image.save(filename, g_Config.bScreenshotsAsPNG ? "PNG" : "JPG");
}
#else
if (success) {
const u8 *buffer = buf.GetData();
u8 *flipbuffer = nullptr;
if (buf.GetFlipped()) {
// Silly OpenGL reads upside down, we flip to another buffer for simplicity.
_assert_msg_(G3D, buf.GetFormat() == GPU_DBG_FORMAT_888_RGB, "Expecting RGB for flipped buffer (OpenGL.)");
flipbuffer = new u8[3 * buf.GetStride() * buf.GetHeight()];
for (u32 y = 0; y < buf.GetHeight(); y++) {
memcpy(flipbuffer + y * buf.GetStride() * 3, buffer + (buf.GetHeight() - y - 1) * buf.GetStride() * 3, buf.GetStride() * 3);
}
buffer = flipbuffer;
}
if (buf.GetFormat() == GPU_DBG_FORMAT_8888_BGRA) {
// Yay, we need to swap AND remove alpha.
flipbuffer = new u8[3 * buf.GetStride() * buf.GetHeight()];
for (u32 y = 0; y < buf.GetHeight(); y++) {
for (u32 x = 0; x < buf.GetStride(); x++) {
u8 *dst = &flipbuffer[y * buf.GetStride() * 3 + x * 3];
const u8 *src = &buffer[y * buf.GetStride() * 4 + x * 4];
dst[0] = src[2];
dst[1] = src[1];
dst[2] = src[0];
}
}
buffer = flipbuffer;
}
if (g_Config.bScreenshotsAsPNG) {
png_image png;
memset(&png, 0, sizeof(png));
png.version = PNG_IMAGE_VERSION;
png.format = PNG_FORMAT_RGB;
png.width = buf.GetStride();
png.height = buf.GetHeight();
png_image_write_to_file(&png, filename, 0, flipbuffer, buf.GetStride() * 3, NULL);
png_image_free(&png);
success = png.warning_or_error >= 2;
} else {
jpge::params params;
params.m_quality = 90;
success = compress_image_to_jpeg_file(filename, buf.GetStride(), buf.GetHeight(), 3, flipbuffer, params);
}
delete [] flipbuffer;
}
#endif
if (success) {
osm.Show(filename);