diff --git a/Source/Core/VideoBackends/D3D/Src/Render.cpp b/Source/Core/VideoBackends/D3D/Src/Render.cpp index 9790f2b52e..380ab7fa83 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) } } -void Renderer::TakeScreenshot(const TargetRectangle &rc, std::string filename) +bool Renderer::SaveScreenshot(const std::string &filename, const TargetRectangle& rc) { if (!s_screenshot_texture) CreateScreenshotTexture(rc); @@ -689,26 +689,34 @@ void Renderer::TakeScreenshot(const TargetRectangle &rc, std::string filename) 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); - u8* __restrict dest = (u8*) malloc(rc.GetWidth() * rc.GetHeight() * 3); - + // D3DX11SaveTextureToFileA doesn't allow us to ignore the alpha channel, so we need to strip it out ourselves D3D11_MAPPED_SUBRESOURCE map; D3D::context->Map(s_screenshot_texture, 0, D3D11_MAP_READ_WRITE, 0, &map); - u8* src = (u8*) map.pData; - for (int y = 0; y < rc.GetHeight(); ++y) + for (unsigned int y = 0; y < rc.GetHeight(); ++y) { - u8* __restrict row = src; - for (int x = 0; x < rc.GetWidth(); ++x) + u8* ptr = (u8*)map.pData + y * map.RowPitch + 3; + for (unsigned int x = 0; x < rc.GetWidth(); ++x) { - *dest++ = *row++; - *dest++ = *row++; - *dest++ = *row++; - row++; + *ptr = 0xFF; + ptr += 4; } - src += map.RowPitch; } D3D::context->Unmap(s_screenshot_texture, 0); - SaveScreenshot(dest, rc.GetWidth(), rc.GetHeight(), filename); + // 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); } void formatBufferDump(const u8* in, u8* out, int w, int h, int p) @@ -846,7 +854,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) { - TakeScreenshot(GetTargetRectangle(), s_sScreenshotName); + SaveScreenshot(s_sScreenshotName, GetTargetRectangle()); s_bScreenshot = false; } diff --git a/Source/Core/VideoBackends/D3D/Src/Render.h b/Source/Core/VideoBackends/D3D/Src/Render.h index e899484111..12e5b595d3 100644 --- a/Source/Core/VideoBackends/D3D/Src/Render.h +++ b/Source/Core/VideoBackends/D3D/Src/Render.h @@ -48,10 +48,9 @@ public: void UpdateViewport(); - static void TakeScreenshot(const TargetRectangle &rc, std::string filename); + bool SaveScreenshot(const std::string &filename, 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 4583c6772c..d03d3cedd3 100644 --- a/Source/Core/VideoBackends/OGL/Src/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Src/Render.cpp @@ -11,6 +11,9 @@ #include #include "GLUtil.h" +#if defined(HAVE_WX) && HAVE_WX +#include "WxUtils.h" +#endif #include "FileUtil.h" @@ -22,6 +25,7 @@ #include "DriverDetails.h" #include "VideoConfig.h" #include "Statistics.h" +#include "ImageWrite.h" #include "PixelEngine.h" #include "Render.h" #include "BPStructs.h" @@ -58,6 +62,10 @@ #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 @@ -70,6 +78,17 @@ 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 @@ -108,6 +127,10 @@ 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 @@ -616,6 +639,11 @@ Renderer::Renderer() Renderer::~Renderer() { + +#if defined(HAVE_WX) && HAVE_WX + if (scrshotThread.joinable()) + scrshotThread.join(); +#endif } void Renderer::Shutdown() @@ -1391,9 +1419,11 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbHeight,const EFBRectangle& r // Save screenshot if (s_bScreenshot) { - TakeScreenshot(flipped_trc, s_sScreenshotName); - s_bScreenshot = false; + std::lock_guard lk(s_criticalScreenshot); + SaveScreenshot(s_sScreenshotName, flipped_trc); // Reset settings + s_sScreenshotName.clear(); + s_bScreenshot = false; } // Frame dumps are handled a little differently in Windows @@ -1771,22 +1801,71 @@ void Renderer::SetInterlacingMode() void Renderer::FlipImageData(u8 *data, int w, int h) { - // XXX make this faster - u8* __restrict top = data; - u8* bot = data + w * h * 3; + // Flip image upside down. Damn OpenGL. for (int y = 0; y < h / 2; y++) { - size_t stride = w * 3; - bot -= stride; - u8* __restrict brow = bot; - for(size_t x = 0; x < stride; x++) + for(int x = 0; x < w; x++) { - std::swap(*top++, *brow++); + 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]); } } } -void Renderer::TakeScreenshot(const TargetRectangle &back_rc, std::string filename) +} + +// 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) { u32 W = back_rc.GetWidth(); u32 H = back_rc.GetHeight(); @@ -1800,13 +1879,38 @@ void Renderer::TakeScreenshot(const TargetRectangle &back_rc, std::string filena { free(data); OSD::AddMessage("Error capturing or saving screenshot.", 2000); - return; + return false; } // Turn image upside down FlipImageData(data, W, H); - SaveScreenshot(data, W, H, filename); +#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; } } diff --git a/Source/Core/VideoBackends/OGL/Src/Render.h b/Source/Core/VideoBackends/OGL/Src/Render.h index f38c9b4c58..9b4838ee25 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) override; void DrawDebugInfo(); - static void FlipImageData(u8 *data, int w, int h); + void FlipImageData(u8 *data, int w, int h); u32 AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) override; @@ -80,7 +80,7 @@ public: void UpdateViewport() override; - static void TakeScreenshot(const TargetRectangle &rc, std::string filename); + bool SaveScreenshot(const std::string &filename, const TargetRectangle &rc); private: void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc, const TargetRectangle& targetPixelRc, const u32* data); diff --git a/Source/Core/VideoBackends/OGL/Src/VertexManager.cpp b/Source/Core/VideoBackends/OGL/Src/VertexManager.cpp index 4fb355d897..44d8ddeaa4 100644 --- a/Source/Core/VideoBackends/OGL/Src/VertexManager.cpp +++ b/Source/Core/VideoBackends/OGL/Src/VertexManager.cpp @@ -341,7 +341,7 @@ void VertexManager::vFlush() tr.right = Renderer::GetTargetWidth(); tr.top = 0; tr.bottom = Renderer::GetTargetHeight(); - Renderer::TakeScreenshot(tr, str); + g_renderer->SaveScreenshot(str, tr); } #endif g_Config.iSaveTargetId++; diff --git a/Source/Core/VideoCommon/Src/RenderBase.cpp b/Source/Core/VideoCommon/Src/RenderBase.cpp index bb3dad8a73..e509ccb980 100644 --- a/Source/Core/VideoCommon/Src/RenderBase.cpp +++ b/Source/Core/VideoCommon/Src/RenderBase.cpp @@ -30,12 +30,6 @@ #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 "ImageWrite.h" #include #include @@ -73,8 +67,6 @@ 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() @@ -250,72 +242,6 @@ 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::string filename) -{ - std::lock_guard lk(s_criticalScreenshot); -#if defined(HAVE_WX) && HAVE_WX - // Create wxImage - - std::thread thread(SaveScreenshotOnThread, ptr, width, height, filename); -#ifdef _WIN32 - SetThreadPriority(thread.native_handle(), THREAD_PRIORITY_BELOW_NORMAL); -#endif - thread.detach(); - - OSD::AddMessage("Saving Screenshot... ", 2000); - -#else - SaveTGA(filename.c_str(), width, height, ptr); - free(ptr); -#endif - - s_bScreenshot = false; -} - // Create On-Screen-Messages void Renderer::DrawDebugText() { @@ -421,6 +347,9 @@ 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 7a153fb5cc..22d31cde23 100644 --- a/Source/Core/VideoCommon/Src/RenderBase.h +++ b/Source/Core/VideoCommon/Src/RenderBase.h @@ -110,6 +110,8 @@ 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; } @@ -121,11 +123,6 @@ 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, std::string filename); - static volatile bool s_bScreenshot; static std::mutex s_criticalScreenshot; static std::string s_sScreenshotName;