From 5f72542e0637799092ad1006d16ebad1961020e0 Mon Sep 17 00:00:00 2001 From: comex Date: Sat, 2 Nov 2013 22:17:56 -0400 Subject: [PATCH] Handle screenshot saving in RenderBase. Removes dependency on D3DX11 for screenshots (texture dumping is still broken). --- Source/Core/VideoBackends/D3D/Src/Render.cpp | 37 ++---- Source/Core/VideoBackends/D3D/Src/Render.h | 3 +- Source/Core/VideoBackends/OGL/Src/Render.cpp | 128 ++----------------- Source/Core/VideoBackends/OGL/Src/Render.h | 4 +- Source/Core/VideoCommon/Src/RenderBase.cpp | 77 ++++++++++- Source/Core/VideoCommon/Src/RenderBase.h | 7 +- 6 files changed, 109 insertions(+), 147 deletions(-) diff --git a/Source/Core/VideoBackends/D3D/Src/Render.cpp b/Source/Core/VideoBackends/D3D/Src/Render.cpp index e73b488bc9..e0f9aa7230 100644 --- a/Source/Core/VideoBackends/D3D/Src/Render.cpp +++ b/Source/Core/VideoBackends/D3D/Src/Render.cpp @@ -680,7 +680,7 @@ void Renderer::SetBlendMode(bool forceUpdate) } } -bool Renderer::SaveScreenshot(const std::string &filename, const TargetRectangle& rc) +void Renderer::TakeScreenshot(const TargetRectangle &rc) { if (!s_screenshot_texture) CreateScreenshotTexture(rc); @@ -689,34 +689,26 @@ bool Renderer::SaveScreenshot(const std::string &filename, const TargetRectangle D3D11_BOX box = CD3D11_BOX(rc.left, rc.top, 0, rc.right, rc.bottom, 1); D3D::context->CopySubresourceRegion(s_screenshot_texture, 0, 0, 0, 0, (ID3D11Resource*)D3D::GetBackBuffer()->GetTex(), 0, &box); - // D3DX11SaveTextureToFileA doesn't allow us to ignore the alpha channel, so we need to strip it out ourselves + u8* __restrict dest = (u8*) malloc(rc.GetWidth() * rc.GetHeight() * 3); + D3D11_MAPPED_SUBRESOURCE map; D3D::context->Map(s_screenshot_texture, 0, D3D11_MAP_READ_WRITE, 0, &map); - for (unsigned int y = 0; y < rc.GetHeight(); ++y) + u8* src = (u8*) map.pData; + for (int y = 0; y < rc.GetHeight(); ++y) { - u8* ptr = (u8*)map.pData + y * map.RowPitch + 3; - for (unsigned int x = 0; x < rc.GetWidth(); ++x) + u8* __restrict row = src; + for (int x = 0; x < rc.GetWidth(); ++x) { - *ptr = 0xFF; - ptr += 4; + *dest++ = *row++; + *dest++ = *row++; + *dest++ = *row++; + row++; } + src += map.RowPitch; } D3D::context->Unmap(s_screenshot_texture, 0); - // ready to be saved - //HRESULT hr = PD3DX11SaveTextureToFileA(D3D::context, s_screenshot_texture, D3DX11_IFF_PNG, filename.c_str()); - HRESULT hr = 0; - if (SUCCEEDED(hr)) - { - OSD::AddMessage(StringFromFormat("Saved %i x %i %s", rc.GetWidth(), - rc.GetHeight(), filename.c_str())); - } - else - { - OSD::AddMessage(StringFromFormat("Error saving %s", filename.c_str())); - } - - return SUCCEEDED(hr); + SaveScreenshot(dest, rc.GetWidth(), rc.GetHeight()); } void formatBufferDump(const u8* in, u8* out, int w, int h, int p) @@ -854,8 +846,7 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbHeight,const EFBRectangle& r // done with drawing the game stuff, good moment to save a screenshot if (s_bScreenshot) { - SaveScreenshot(s_sScreenshotName, GetTargetRectangle()); - s_bScreenshot = false; + TakeScreenshot(GetTargetRectangle()); } // Dump frames diff --git a/Source/Core/VideoBackends/D3D/Src/Render.h b/Source/Core/VideoBackends/D3D/Src/Render.h index 12e5b595d3..0d5a569eaf 100644 --- a/Source/Core/VideoBackends/D3D/Src/Render.h +++ b/Source/Core/VideoBackends/D3D/Src/Render.h @@ -48,9 +48,10 @@ public: void UpdateViewport(); - bool SaveScreenshot(const std::string &filename, const TargetRectangle &rc); + static void TakeScreenshot(const TargetRectangle &rc); static bool CheckForResize(); + }; } diff --git a/Source/Core/VideoBackends/OGL/Src/Render.cpp b/Source/Core/VideoBackends/OGL/Src/Render.cpp index 5c560dc3cd..a9ab506c80 100644 --- a/Source/Core/VideoBackends/OGL/Src/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Src/Render.cpp @@ -11,9 +11,6 @@ #include #include "GLUtil.h" -#if defined(HAVE_WX) && HAVE_WX -#include "WxUtils.h" -#endif #include "FileUtil.h" @@ -62,10 +59,6 @@ #include "AVIDump.h" #endif -#if defined(HAVE_WX) && HAVE_WX -#include -#endif - // glew1.8 doesn't define KHR_debug #ifndef GL_DEBUG_OUTPUT #define GL_DEBUG_OUTPUT 0x92E0 @@ -78,17 +71,6 @@ void VideoConfig::UpdateProjectionHack() } -#if defined(HAVE_WX) && HAVE_WX -// Screenshot thread struct -typedef struct -{ - int W, H; - std::string filename; - wxImage *img; -} ScrStrct; -#endif - - int OSDInternalW, OSDInternalH; namespace OGL @@ -127,10 +109,6 @@ static u32 s_blendMode; static bool s_vsync; -#if defined(HAVE_WX) && HAVE_WX -static std::thread scrshotThread; -#endif - // EFB cache related static const u32 EFB_CACHE_RECT_SIZE = 64; // Cache 64x64 blocks. static const u32 EFB_CACHE_WIDTH = (EFB_WIDTH + EFB_CACHE_RECT_SIZE - 1) / EFB_CACHE_RECT_SIZE; // round up @@ -626,11 +604,6 @@ Renderer::Renderer() Renderer::~Renderer() { - -#if defined(HAVE_WX) && HAVE_WX - if (scrshotThread.joinable()) - scrshotThread.join(); -#endif } void Renderer::Shutdown() @@ -1406,11 +1379,8 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbHeight,const EFBRectangle& r // Save screenshot if (s_bScreenshot) { - std::lock_guard lk(s_criticalScreenshot); - SaveScreenshot(s_sScreenshotName, flipped_trc); + TakeScreenshot(flipped_trc); // Reset settings - s_sScreenshotName.clear(); - s_bScreenshot = false; } // Frame dumps are handled a little differently in Windows @@ -1788,71 +1758,22 @@ void Renderer::SetInterlacingMode() void Renderer::FlipImageData(u8 *data, int w, int h) { - // Flip image upside down. Damn OpenGL. + // XXX make this faster + u8* __restrict top = data; + u8* bot = data + w * h * 3; for (int y = 0; y < h / 2; y++) { - for(int x = 0; x < w; x++) + size_t stride = w * 3; + bot -= stride; + u8* __restrict brow = bot; + for(size_t x = 0; x < stride; x++) { - std::swap(data[(y * w + x) * 3], data[((h - 1 - y) * w + x) * 3]); - std::swap(data[(y * w + x) * 3 + 1], data[((h - 1 - y) * w + x) * 3 + 1]); - std::swap(data[(y * w + x) * 3 + 2], data[((h - 1 - y) * w + x) * 3 + 2]); + std::swap(*top++, *brow++); } } } -} - -// TODO: remove -extern bool g_aspect_wide; - -#if defined(HAVE_WX) && HAVE_WX -void TakeScreenshot(ScrStrct* threadStruct) -{ - // These will contain the final image size - float FloatW = (float)threadStruct->W; - float FloatH = (float)threadStruct->H; - - // Handle aspect ratio for the final ScrStrct to look exactly like what's on screen. - if (g_ActiveConfig.iAspectRatio != ASPECT_STRETCH) - { - bool use16_9 = g_aspect_wide; - - // Check for force-settings and override. - if (g_ActiveConfig.iAspectRatio == ASPECT_FORCE_16_9) - use16_9 = true; - else if (g_ActiveConfig.iAspectRatio == ASPECT_FORCE_4_3) - use16_9 = false; - - float Ratio = (FloatW / FloatH) / (!use16_9 ? (4.0f / 3.0f) : (16.0f / 9.0f)); - - // If ratio > 1 the picture is too wide and we have to limit the width. - if (Ratio > 1) - FloatW /= Ratio; - // ratio == 1 or the image is too high, we have to limit the height. - else - FloatH *= Ratio; - - // This is a bit expensive on high resolutions - threadStruct->img->Rescale((int)FloatW, (int)FloatH, wxIMAGE_QUALITY_HIGH); - } - - // Save the screenshot and finally kill the wxImage object - // This is really expensive when saving to PNG, but not at all when using BMP - threadStruct->img->SaveFile(StrToWxStr(threadStruct->filename), - wxBITMAP_TYPE_PNG); - threadStruct->img->Destroy(); - - // Show success messages - OSD::AddMessage(StringFromFormat("Saved %i x %i %s", (int)FloatW, (int)FloatH, - threadStruct->filename.c_str()), 2000); - delete threadStruct; -} -#endif - -namespace OGL -{ - -bool Renderer::SaveScreenshot(const std::string &filename, const TargetRectangle &back_rc) +void Renderer::TakeScreenshot(const TargetRectangle &back_rc) { u32 W = back_rc.GetWidth(); u32 H = back_rc.GetHeight(); @@ -1866,38 +1787,13 @@ bool Renderer::SaveScreenshot(const std::string &filename, const TargetRectangle { free(data); OSD::AddMessage("Error capturing or saving screenshot.", 2000); - return false; + return; } // Turn image upside down FlipImageData(data, W, H); -#if defined(HAVE_WX) && HAVE_WX - // Create wxImage - wxImage *a = new wxImage(W, H, data); - - if (scrshotThread.joinable()) - scrshotThread.join(); - - ScrStrct *threadStruct = new ScrStrct; - threadStruct->filename = filename; - threadStruct->img = a; - threadStruct->H = H; threadStruct->W = W; - - scrshotThread = std::thread(TakeScreenshot, threadStruct); -#ifdef _WIN32 - SetThreadPriority(scrshotThread.native_handle(), THREAD_PRIORITY_BELOW_NORMAL); -#endif - bool result = true; - - OSD::AddMessage("Saving Screenshot... ", 2000); - -#else - bool result = SaveTGA(filename.c_str(), W, H, data); - free(data); -#endif - - return result; + SaveScreenshot(data, W, H); } } diff --git a/Source/Core/VideoBackends/OGL/Src/Render.h b/Source/Core/VideoBackends/OGL/Src/Render.h index e30380d627..f0ddbb8862 100644 --- a/Source/Core/VideoBackends/OGL/Src/Render.h +++ b/Source/Core/VideoBackends/OGL/Src/Render.h @@ -63,7 +63,7 @@ public: void RenderText(const char* pstr, int left, int top, u32 color); void DrawDebugInfo(); - void FlipImageData(u8 *data, int w, int h); + static void FlipImageData(u8 *data, int w, int h); u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data); @@ -80,7 +80,7 @@ public: void UpdateViewport(); - bool SaveScreenshot(const std::string &filename, const TargetRectangle &rc); + static void TakeScreenshot(const TargetRectangle &rc); private: void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc, const TargetRectangle& targetPixelRc, const u32* data); diff --git a/Source/Core/VideoCommon/Src/RenderBase.cpp b/Source/Core/VideoCommon/Src/RenderBase.cpp index e509ccb980..2171cc6a95 100644 --- a/Source/Core/VideoCommon/Src/RenderBase.cpp +++ b/Source/Core/VideoCommon/Src/RenderBase.cpp @@ -30,6 +30,11 @@ #include "XFMemory.h" #include "FifoPlayer/FifoRecorder.h" #include "AVIDump.h" +#include "OnScreenDisplay.h" +#if defined(HAVE_WX) && HAVE_WX +#include "WxUtils.h" +#include +#endif #include #include @@ -67,6 +72,8 @@ unsigned int Renderer::efb_scale_numeratorY = 1; unsigned int Renderer::efb_scale_denominatorX = 1; unsigned int Renderer::efb_scale_denominatorY = 1; +// TODO: remove +extern bool g_aspect_wide; Renderer::Renderer() : frame_data() @@ -242,6 +249,73 @@ void Renderer::SetScreenshot(const char *filename) s_bScreenshot = true; } +#if defined(HAVE_WX) && HAVE_WX +void Renderer::SaveScreenshotOnThread(u8* data, size_t width, size_t height, std::string filename) +{ + wxImage *img = new wxImage(width, height, data); + + // These will contain the final image size + float FloatW = (float)width; + float FloatH = (float)height; + + // Handle aspect ratio for the final ScrStrct to look exactly like what's on screen. + if (g_ActiveConfig.iAspectRatio != ASPECT_STRETCH) + { + bool use16_9 = g_aspect_wide; + + // Check for force-settings and override. + if (g_ActiveConfig.iAspectRatio == ASPECT_FORCE_16_9) + use16_9 = true; + else if (g_ActiveConfig.iAspectRatio == ASPECT_FORCE_4_3) + use16_9 = false; + + float Ratio = (FloatW / FloatH) / (!use16_9 ? (4.0f / 3.0f) : (16.0f / 9.0f)); + + // If ratio > 1 the picture is too wide and we have to limit the width. + if (Ratio > 1) + FloatW /= Ratio; + // ratio == 1 or the image is too high, we have to limit the height. + else + FloatH *= Ratio; + + // This is a bit expensive on high resolutions + img->Rescale((int)FloatW, (int)FloatH, wxIMAGE_QUALITY_HIGH); + } + + // Save the screenshot and finally kill the wxImage object + // This is really expensive when saving to PNG, but not at all when using BMP + img->SaveFile(StrToWxStr(filename), wxBITMAP_TYPE_PNG); + img->Destroy(); + + // Show success messages + OSD::AddMessage(StringFromFormat("Saved %i x %i %s", (int)FloatW, (int)FloatH, + filename.c_str()), 2000); +} +#endif + +void Renderer::SaveScreenshot(u8* ptr, size_t width, size_t height) +{ + std::lock_guard lk(s_criticalScreenshot); +#if defined(HAVE_WX) && HAVE_WX + // Create wxImage + + std::thread thread(SaveScreenshotOnThread, ptr, width, height, s_sScreenshotName); +#ifdef _WIN32 + SetThreadPriority(thread.native_handle(), THREAD_PRIORITY_BELOW_NORMAL); +#endif + thread.detach(); + + OSD::AddMessage("Saving Screenshot... ", 2000); + +#else + SaveTGA(filename.c_str(), W, H, data); + free(data); +#endif + + s_sScreenshotName.clear(); + s_bScreenshot = false; +} + // Create On-Screen-Messages void Renderer::DrawDebugText() { @@ -347,9 +421,6 @@ void Renderer::DrawDebugText() g_renderer->RenderText(final_yellow.c_str(), 20, 20, 0xFFFFFF00); } -// TODO: remove -extern bool g_aspect_wide; - void Renderer::UpdateDrawRectangle(int backbuffer_width, int backbuffer_height) { float FloatGLWidth = (float)backbuffer_width; diff --git a/Source/Core/VideoCommon/Src/RenderBase.h b/Source/Core/VideoCommon/Src/RenderBase.h index 22d31cde23..22f5df0cd7 100644 --- a/Source/Core/VideoCommon/Src/RenderBase.h +++ b/Source/Core/VideoCommon/Src/RenderBase.h @@ -110,8 +110,6 @@ public: virtual void UpdateViewport() = 0; - virtual bool SaveScreenshot(const std::string &filename, const TargetRectangle &rc) = 0; - static unsigned int GetPrevPixelFormat() { return prev_efb_format; } static void StorePixelFormat(unsigned int new_format) { prev_efb_format = new_format; } @@ -123,6 +121,11 @@ protected: static void CheckFifoRecording(); static void RecordVideoMemory(); + #if defined(HAVE_WX) && HAVE_WX + static void SaveScreenshotOnThread(u8* data, size_t width, size_t height, std::string filename); + #endif + static void SaveScreenshot(u8* ptr, size_t width, size_t height); + static volatile bool s_bScreenshot; static std::mutex s_criticalScreenshot; static std::string s_sScreenshotName;