mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-26 23:10:38 +00:00
dx9: Take screenshots (fixes #7197.)
This commit is contained in:
parent
8d68c055cc
commit
962d8c5224
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
101
UI/NativeApp.cpp
101
UI/NativeApp.cpp
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user