From 711f08c29b48ab801a863b70799c069ca7b93f48 Mon Sep 17 00:00:00 2001 From: Marcus Wanners Date: Sat, 28 Mar 2009 21:07:16 +0000 Subject: [PATCH] Added support for dumping avi files (thanks baby.lueshi). Use Microsoft Video 1 codec for starters. Make sure to check the dump rendered frames box in the video plugin settings, or it won't work. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@2781 8ced0084-cf51-0410-be5f-012b33b47a6e --- Readme.txt | 2 +- Source/Core/Common/Src/CommonPaths.h | 4 +- Source/Core/Core/Src/HW/PixelEngine.cpp | 16 ++ Source/Core/VideoCommon/Src/AVIDump.cpp | 157 ++++++++++++++++++ Source/Core/VideoCommon/VideoCommon.vcproj | 8 + .../Plugin_VideoDX9/Plugin_VideoDX9.vcproj | 12 +- Source/Plugins/Plugin_VideoDX9/Src/Config.cpp | 2 + Source/Plugins/Plugin_VideoDX9/Src/Config.h | 1 + .../Plugin_VideoDX9/Src/DlgSettings.cpp | 2 + Source/Plugins/Plugin_VideoDX9/Src/Render.cpp | 74 +++++++++ Source/Plugins/Plugin_VideoDX9/Src/Render.h | 4 + Source/Plugins/Plugin_VideoDX9/Src/resource.h | 1 + .../Plugins/Plugin_VideoDX9/Src/resource.rc | 11 +- .../Plugin_VideoOGL/Plugin_VideoOGL.vcproj | 12 +- Source/Plugins/Plugin_VideoOGL/Src/Config.cpp | 2 + Source/Plugins/Plugin_VideoOGL/Src/Config.h | 1 + .../Plugin_VideoOGL/Src/GUI/ConfigDlg.cpp | 17 ++ .../Plugin_VideoOGL/Src/GUI/ConfigDlg.h | 2 + Source/Plugins/Plugin_VideoOGL/Src/Render.cpp | 118 +++++++++++-- Source/Plugins/Plugin_VideoOGL/Src/Render.h | 1 + 20 files changed, 418 insertions(+), 29 deletions(-) create mode 100644 Source/Core/VideoCommon/Src/AVIDump.cpp diff --git a/Readme.txt b/Readme.txt index c3b8b79af8..22e00f42b8 100644 --- a/Readme.txt +++ b/Readme.txt @@ -21,7 +21,7 @@ System Requirements: [Windows Version] -DolphinWx.exe: The main program +Dolphin.exe: The main program Arguments: -d Run Dolphin with the debugger tools -l Run Dolphin with the logmanager enabled diff --git a/Source/Core/Common/Src/CommonPaths.h b/Source/Core/Common/Src/CommonPaths.h index be5c41db6f..3c50eeb8da 100644 --- a/Source/Core/Common/Src/CommonPaths.h +++ b/Source/Core/Common/Src/CommonPaths.h @@ -69,6 +69,7 @@ #define SCREENSHOTS_DIR "ScreenShots" #define DUMP_DIR "Dump" #define DUMP_TEXTURES_DIR "Textures" +#define DUMP_FRAMES_DIR "Frames" #define LOGS_DIR "Logs" #define MAIL_LOGS_DIR "Mail" @@ -122,7 +123,8 @@ #define FULL_CONFIG_DIR FULL_USERDATA_DIR CONFIG_DIR DIR_SEP #define FULL_CACHE_DIR FULL_USERDATA_DIR CACHE_DIR DIR_SEP #define FULL_STATESAVES_DIR FULL_USERDATA_DIR STATESAVES_DIR DIR_SEP -#define FULL_SCREENSHOTS_DIR FULL_USERDATA_DIR SCREENSHOTS_DIR DIR_SEP +#define FULL_SCREENSHOTS_DIR FULL_USERDATA_DIR SCREENSHOTS_DIR DIR_SEP +#define FULL_FRAMES_DIR FULL_USERDATA_DIR DUMP_DIR DIR_SEP DUMP_FRAMES_DIR #define FULL_DUMP_DIR FULL_USERDATA_DIR DUMP_DIR DIR_SEP #define FULL_DUMP_TEXTURES_DIR FULL_USERDATA_DIR DUMP_DIR DIR_SEP DUMP_TEXTURES_DIR #define FULL_LOGS_DIR FULL_USERDATA_DIR LOGS_DIR DIR_SEP diff --git a/Source/Core/Core/Src/HW/PixelEngine.cpp b/Source/Core/Core/Src/HW/PixelEngine.cpp index 602ee5f002..8edebd70ac 100644 --- a/Source/Core/Core/Src/HW/PixelEngine.cpp +++ b/Source/Core/Core/Src/HW/PixelEngine.cpp @@ -142,6 +142,22 @@ void Read16(u16& _uReturnValue, const u32 _iAddress) _uReturnValue = AlphaReg; return; + case 0x010: + _uReturnValue = 0x80; + return; + + case 0x012: + _uReturnValue = 0xA0; + return; + + case 0x014: + _uReturnValue = 0x80; + return; + + case 0x016: + _uReturnValue = 0xA0; + return; + default: WARN_LOG(PIXELENGINE, "(r16): unknown @ %08x", _iAddress); break; diff --git a/Source/Core/VideoCommon/Src/AVIDump.cpp b/Source/Core/VideoCommon/Src/AVIDump.cpp new file mode 100644 index 0000000000..6c4e66499c --- /dev/null +++ b/Source/Core/VideoCommon/Src/AVIDump.cpp @@ -0,0 +1,157 @@ +// Copyright (C) 2003-2009 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "AVIDump.h" +#include "tchar.h" + +#include +#include +#include +#include + +#include "CommonPaths.h" +#include "Log.h" + +static HWND m_emuWnd; +static int m_width; +static int m_height; +static LONG m_byteBuffer; +static LONG m_frameCount; +static LONG m_totalBytes; +static PAVIFILE m_file; +static int m_fileCount; +static PAVISTREAM m_stream; +static PAVISTREAM m_streamCompressed; +static AVISTREAMINFO m_header; +static AVICOMPRESSOPTIONS m_options; +static AVICOMPRESSOPTIONS *m_arrayOptions[1]; +static BITMAPINFOHEADER m_bitmap; + +bool AVIDump::Start(HWND hWnd, int w, int h) +{ + m_emuWnd = hWnd; + m_fileCount = 0; + + m_width = w; + m_height = h; + + return CreateFile(); +} + +bool AVIDump::CreateFile() { + m_totalBytes = 0; + m_frameCount = 0; + char movie_file_name[255]; + sprintf(movie_file_name, "%s/framedump%d.avi", FULL_FRAMES_DIR, m_fileCount); + AVIFileInit(); + NOTICE_LOG(VIDEO, "Opening AVI file (%s) for dumping", movie_file_name); + // TODO: Make this work with AVIFileOpenW without it throwing REGDB_E_CLASSNOTREG + if (FAILED(AVIFileOpenA(&m_file, movie_file_name, OF_WRITE | OF_CREATE, NULL))) { + Stop(); + return false; + } + SetBitmapFormat(); + NOTICE_LOG(VIDEO, "Setting video format..."); + if (!SetVideoFormat()) { + Stop(); + return false; + } + if (!m_fileCount) { + if (!SetCompressionOptions()) { + Stop(); + return false; + } + } + if (FAILED(AVIMakeCompressedStream(&m_streamCompressed, m_stream, &m_options, NULL))) { + Stop(); + return false; + } + if (FAILED(AVIStreamSetFormat(m_streamCompressed, 0, &m_bitmap, m_bitmap.biSize))) { + Stop(); + return false; + } + + return true; +} + +void AVIDump::CloseFile() +{ + if (m_streamCompressed) { + AVIStreamClose(m_streamCompressed); + m_streamCompressed = NULL; + } + if (m_stream) { + AVIStreamClose(m_stream); + m_stream = NULL; + } + if (m_file) { + AVIFileRelease(m_file); + m_file = NULL; + } + AVIFileExit(); +} + +void AVIDump::Stop() +{ + CloseFile(); + + m_fileCount = 0; +} + +void AVIDump::AddFrame(char *data) +{ + AVIStreamWrite(m_streamCompressed, ++m_frameCount, 1, (LPVOID) data, m_bitmap.biSizeImage, AVIIF_KEYFRAME, NULL, &m_byteBuffer); + m_totalBytes += m_byteBuffer; + // Fun fact: VfW can't property save files over 2gb in size, but can keep + // writing to them up to 4gb. + if (m_totalBytes >= 2000000000) { + CloseFile(); + m_fileCount++; + CreateFile(); + } +} + +void AVIDump::SetBitmapFormat() +{ + memset(&m_bitmap, 0, sizeof(m_bitmap)); + m_bitmap.biSize = 0x28; + m_bitmap.biPlanes = 1; + m_bitmap.biBitCount = 24; + m_bitmap.biWidth = m_width; + m_bitmap.biHeight = m_height; + m_bitmap.biSizeImage = 3 * m_width * m_height; +} + +bool AVIDump::SetCompressionOptions() +{ + memset(&m_options, 0, sizeof(m_options)); + m_arrayOptions[0] = &m_options; + + return (AVISaveOptions(m_emuWnd, 0, 1, &m_stream, m_arrayOptions) != 0); +} + +bool AVIDump::SetVideoFormat() +{ + memset(&m_header, 0, sizeof(m_header)); + m_header.fccType = streamtypeVIDEO; + m_header.dwScale = 1; + // TODO: Decect FPS using NTSC/PAL + m_header.dwRate = 60; + m_header.dwSuggestedBufferSize = m_bitmap.biSizeImage; + + return SUCCEEDED(AVIFileCreateStream(m_file, &m_stream, &m_header)); +} diff --git a/Source/Core/VideoCommon/VideoCommon.vcproj b/Source/Core/VideoCommon/VideoCommon.vcproj index 2da792c34d..ce86407495 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcproj +++ b/Source/Core/VideoCommon/VideoCommon.vcproj @@ -484,6 +484,14 @@ + + + + diff --git a/Source/Plugins/Plugin_VideoDX9/Plugin_VideoDX9.vcproj b/Source/Plugins/Plugin_VideoDX9/Plugin_VideoDX9.vcproj index 9b61e38d04..b5eb15296e 100644 --- a/Source/Plugins/Plugin_VideoDX9/Plugin_VideoDX9.vcproj +++ b/Source/Plugins/Plugin_VideoDX9/Plugin_VideoDX9.vcproj @@ -92,7 +92,7 @@ #include @@ -52,6 +53,9 @@ float Renderer::m_height; float Renderer::xScale; float Renderer::yScale; +int Renderer::m_recordWidth; +int Renderer::m_recordHeight; + std::vector Renderer::m_Textures; DWORD Renderer::m_RenderStates[MaxRenderStates+46]; @@ -59,6 +63,9 @@ DWORD Renderer::m_TextureStageStates[MaxTextureStages][MaxTextureTypes]; DWORD Renderer::m_SamplerStates[MaxSamplerSize][MaxSamplerTypes]; DWORD Renderer::m_FVF; +bool Renderer::m_LastFrameDumped; +bool Renderer::m_AVIDumping; + #define NUMWNDRES 6 extern int g_Res[NUMWNDRES][2]; @@ -106,6 +113,9 @@ void Renderer::Init(SVideoInitialize &_VideoInitialize) xScale = width / (float)EFB_WIDTH; yScale = height / (float)EFB_HEIGHT; + m_LastFrameDumped = false; + m_AVIDumping = false; + // We're not using much fixed function. Let's just set the matrices to identity. D3DXMATRIX mtx; D3DXMatrixIdentity(&mtx); @@ -126,6 +136,10 @@ void Renderer::Shutdown() D3D::font.Shutdown(); D3D::EndFrame(); D3D::Close(); + + if(m_AVIDumping) { + AVIDump::Stop(); + } } void Renderer::Initialize() @@ -194,6 +208,19 @@ void dumpMatrix(D3DXMATRIX &mtx) } } +void formatBufferDump(char *in, char *out, int w, int h, int p) +{ + for(int i = 0; i < h; i++) { + char *line = in + (h - i - 1) * p; + + for (int j = 0; j < w; j++) { + memcpy(out, line, 3); + out += 3; + line += 4; + } + } +} + void Renderer::SwapBuffers() { // Center window again. @@ -208,6 +235,53 @@ void Renderer::SwapBuffers() ::MoveWindow(EmuWindow::GetWnd(), 0, 0, width, height, FALSE); } + // Frame dumping routine + if (g_Config.bDumpFrames) { + D3DDISPLAYMODE DisplayMode; + if (SUCCEEDED(D3D::dev->GetDisplayMode(0, &DisplayMode))) { + LPDIRECT3DSURFACE9 surf; + if (SUCCEEDED(D3D::dev->CreateOffscreenPlainSurface(DisplayMode.Width, DisplayMode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &surf, NULL))) { + if (!m_LastFrameDumped) { + RECT windowRect; + GetWindowRect(EmuWindow::GetWnd(), &windowRect); + m_recordWidth = windowRect.right - windowRect.left; + m_recordHeight = windowRect.bottom - windowRect.top; + m_AVIDumping = AVIDump::Start(EmuWindow::GetParentWnd(), m_recordWidth, m_recordHeight); + if (!m_AVIDumping) { + PanicAlert("Error dumping frames to AVI."); + } else { + char msg [255]; + sprintf(msg, "Dumping Frames to \"%s/framedump0.avi\" (%dx%d RGB24)", FULL_FRAMES_DIR, m_recordWidth, m_recordHeight); + AddMessage(msg, 2000); + } + } + if (m_AVIDumping) { + if (SUCCEEDED(D3D::dev->GetFrontBufferData(0, surf))) { + RECT windowRect; + GetWindowRect(EmuWindow::GetWnd(), &windowRect); + D3DLOCKED_RECT rect; + if (SUCCEEDED(surf->LockRect(&rect, &windowRect, D3DLOCK_NO_DIRTY_UPDATE | D3DLOCK_NOSYSLOCK | D3DLOCK_READONLY))) { + char *data = (char *) malloc(3 * m_recordWidth * m_recordHeight); + formatBufferDump((char *) rect.pBits, data, m_recordWidth, m_recordHeight, rect.Pitch); + AVIDump::AddFrame(data); + free(data); + surf->UnlockRect(); + } + } + } + m_LastFrameDumped = true; + surf->Release(); + } + } + } else { + if(m_LastFrameDumped && m_AVIDumping) { + AVIDump::Stop(); + m_AVIDumping = false; + } + + m_LastFrameDumped = false; + } + //Finish up the current frame, print some stats Postprocess::FinalizeFrame(); if (g_Config.bOverlayStats) diff --git a/Source/Plugins/Plugin_VideoDX9/Src/Render.h b/Source/Plugins/Plugin_VideoDX9/Src/Render.h index e46ab5702f..23671baf64 100644 --- a/Source/Plugins/Plugin_VideoDX9/Src/Render.h +++ b/Source/Plugins/Plugin_VideoDX9/Src/Render.h @@ -38,6 +38,10 @@ class Renderer static float m_height; static float xScale; static float yScale; + static bool m_LastFrameDumped; + static bool m_AVIDumping; + static int m_recordWidth; + static int m_recordHeight; const static int MaxTextureStages = 9; const static int MaxRenderStates = 210; const static DWORD MaxTextureTypes = 33; diff --git a/Source/Plugins/Plugin_VideoDX9/Src/resource.h b/Source/Plugins/Plugin_VideoDX9/Src/resource.h index a1ce32f583..68f1ef9693 100644 --- a/Source/Plugins/Plugin_VideoDX9/Src/resource.h +++ b/Source/Plugins/Plugin_VideoDX9/Src/resource.h @@ -57,6 +57,7 @@ #define IDC_TEXFMT_CENTER 1034 #define IDC_TEXFMT_OVERLAY 1035 #define IDC_OVERLAYPROJSTATS 1036 +#define IDC_DUMPFRAMES 1037 // Next default values for new objects // diff --git a/Source/Plugins/Plugin_VideoDX9/Src/resource.rc b/Source/Plugins/Plugin_VideoDX9/Src/resource.rc index ac2ed1cd38..5a05e6291e 100644 --- a/Source/Plugins/Plugin_VideoDX9/Src/resource.rc +++ b/Source/Plugins/Plugin_VideoDX9/Src/resource.rc @@ -50,7 +50,7 @@ BEGIN CONTROL "",IDC_REGISTERSELECT,"SysTabControl32",TCS_BUTTONS,80,52,145,13 END -IDD_ADVANCED DIALOGEX 0, 0, 206, 175 +IDD_ADVANCED DIALOGEX 0, 0, 206, 195 STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU FONT 8, "MS Shell Dlg", 0, 0, 0x0 BEGIN @@ -60,13 +60,14 @@ BEGIN CONTROL "Overlay &Projection Statistics",IDC_OVERLAYPROJSTATS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,42,118,9 CONTROL "Show s&hader compilation errors",IDC_SHOWSHADERERRORS, "Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,53,127,9 - GROUPBOX "&Data dumping",IDC_STATIC,7,91,192,44 + GROUPBOX "&Data dumping",IDC_STATIC,7,91,192,58 CONTROL "Dump &textures to:",IDC_TEXDUMP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,104,70,9 EDITTEXT IDC_TEXDUMPPATH,25,116,148,12,ES_AUTOHSCROLL PUSHBUTTON "...",IDC_BROWSETEXDUMPPATH,176,116,14,13 - GROUPBOX "Texture Format Overlay",IDC_STATIC,7,138,192,30 - CONTROL "Enable Overlay",IDC_TEXFMT_OVERLAY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,150,74,10 - CONTROL "Centered",IDC_TEXFMT_CENTER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,110,150,82,10 + CONTROL "Dump Frames to User/Dump/Frames",IDC_DUMPFRAMES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,132,138,9 + GROUPBOX "Texture Format Overlay",IDC_STATIC,7,156,192,30 + CONTROL "Enable Overlay",IDC_TEXFMT_OVERLAY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,168,74,10 + CONTROL "Centered",IDC_TEXFMT_CENTER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,110,168,82,10 END IDD_ENHANCEMENTS DIALOGEX 0, 0, 207, 175 diff --git a/Source/Plugins/Plugin_VideoOGL/Plugin_VideoOGL.vcproj b/Source/Plugins/Plugin_VideoOGL/Plugin_VideoOGL.vcproj index 48af623807..26710c1a51 100644 --- a/Source/Plugins/Plugin_VideoOGL/Plugin_VideoOGL.vcproj +++ b/Source/Plugins/Plugin_VideoOGL/Plugin_VideoOGL.vcproj @@ -90,7 +90,7 @@ /> SetValue(g_Config.bDumpTextures); m_DumpEFBTarget = new wxCheckBox(m_PageAdvanced, ID_DUMPEFBTARGET, wxT("Dump EFB Target"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); m_DumpEFBTarget->SetValue(g_Config.bDumpEFBTarget); + m_DumpFrames = new wxCheckBox(m_PageAdvanced, ID_DUMPFRAMES, wxT("Dump Rendered Frames"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); +#ifdef _WIN32 + m_DumpFrames->SetToolTip(wxT( + "When dumping begins, you will be prompted to choose a video codec to" + " encode the video in.")); +#else + m_DumpFrames->SetToolTip(wxT( + "!!WARNING!! This option dumps raw bitmaps of each frame, and will fill up" + " your hard drive very quickly. Only turn this on if you have a named pipe" + " set up for the dump or several gigabytes of space available.")); +#endif + m_DumpFrames->SetValue(g_Config.bDumpFrames); // Hacks controls m_SafeTextureCache = new wxCheckBox(m_PageAdvanced, ID_SAFETEXTURECACHE, wxT("Use Safe texture cache"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); @@ -394,6 +407,7 @@ void ConfigDialog::CreateGUIControls() sUtilities = new wxBoxSizer(wxHORIZONTAL); sUtilities->Add(m_DumpTextures, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); sUtilities->Add(m_DumpEFBTarget, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); + sUtilities->Add(m_DumpFrames, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); sbUtilities->Add(sUtilities, 1, wxEXPAND); // Sizers @@ -525,6 +539,9 @@ void ConfigDialog::AdvancedSettingsChanged(wxCommandEvent& event) case ID_DUMPEFBTARGET: g_Config.bDumpEFBTarget = m_DumpEFBTarget->IsChecked(); break; + case ID_DUMPFRAMES: + g_Config.bDumpFrames = m_DumpFrames->IsChecked(); + break; case ID_TEXTUREPATH: break; case ID_CHECKBOX_DISABLECOPYEFB: diff --git a/Source/Plugins/Plugin_VideoOGL/Src/GUI/ConfigDlg.h b/Source/Plugins/Plugin_VideoOGL/Src/GUI/ConfigDlg.h index 26084d84d4..edd7fe5fd3 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/GUI/ConfigDlg.h +++ b/Source/Plugins/Plugin_VideoOGL/Src/GUI/ConfigDlg.h @@ -106,6 +106,7 @@ class ConfigDialog : public wxDialog wxCheckBox *m_DstAlphaPass; wxCheckBox *m_DumpTextures; wxCheckBox *m_DumpEFBTarget; + wxCheckBox *m_DumpFrames; wxStaticBox * m_StaticBox_EFB; wxCheckBox *m_CheckBox_DisableCopyEFB; wxRadioButton *m_Radio_CopyEFBToRAM, *m_Radio_CopyEFBToGL; @@ -161,6 +162,7 @@ class ConfigDialog : public wxDialog ID_DUMPTEXTURES, ID_DUMPEFBTARGET, + ID_DUMPFRAMES, ID_TEXTUREPATH, ID_CHECKBOX_DISABLECOPYEFB, diff --git a/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp b/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp index 5042af03aa..397b1041bb 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp @@ -19,6 +19,7 @@ #include #include +#include #include "GLUtil.h" @@ -29,6 +30,7 @@ #include #endif +#include "CommonPaths.h" #include "Config.h" #include "Profiler.h" #include "Statistics.h" @@ -52,6 +54,7 @@ #include "main.h" // Local #ifdef _WIN32 #include "OS/Win32.h" +#include "AVIDump.h" #endif #if defined(HAVE_WX) && HAVE_WX @@ -71,6 +74,13 @@ RasterFont* s_pfont = NULL; static bool s_bFullscreen = false; +static bool s_bLastFrameDumped = false; +#ifdef _WIN32 +static bool s_bAVIDumping = false; +#else +static FILE* f_pFrameDump; +#endif + static int nZBufferRender = 0; // if > 0, then use zbuffer render, and count down. // 1 for no MSAA. Use s_MSAASamples > 1 to check for MSAA. @@ -484,6 +494,15 @@ void Renderer::Shutdown(void) glDeleteFramebuffersEXT(1, &s_uFramebuffer); s_uFramebuffer = 0; } +#ifdef _WIN32 + if(s_bAVIDumping) { + AVIDump::Stop(); + } +#else + if(f_pFrameDump != NULL) { + fclose(f_pFrameDump); + } +#endif } bool Renderer::InitializeGL() @@ -1092,6 +1111,81 @@ void Renderer::Swap(const TRectangle& rc) s_criticalScreenshot.Leave(); } + // Frame dumps are handled a little differently in Windows +#ifdef _WIN32 + if (g_Config.bDumpFrames) { + s_criticalScreenshot.Enter(); + int w = OpenGL_GetBackbufferWidth(); + int h = OpenGL_GetBackbufferHeight(); + u8 *data = (u8 *) malloc(3 * w * h); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glReadPixels(0, 0, w, h, GL_BGR, GL_UNSIGNED_BYTE, data); + if (glGetError() == GL_NO_ERROR) { + if (!s_bLastFrameDumped) { + s_bAVIDumping = AVIDump::Start(EmuWindow::GetChildParentWnd(), w, h); + if (!s_bAVIDumping) { + PanicAlert("Error dumping frames to AVI."); + } else { + char msg [255]; + sprintf(msg, "Dumping Frames to \"%s/framedump0.avi\" (%dx%d RGB24)", FULL_FRAMES_DIR, w, h); + OSD::AddMessage(msg, 2000); + } + } + if (s_bAVIDumping) { + AVIDump::AddFrame((char *) data); + } + s_bLastFrameDumped = true; + } + free(data); + s_criticalScreenshot.Leave(); + } else { + if(s_bLastFrameDumped && s_bAVIDumping) { + AVIDump::Stop(); + s_bAVIDumping = false; + } + + s_bLastFrameDumped = false; + } +#else + if (g_Config.bDumpFrames) { + s_criticalScreenshot.Enter(); + char movie_file_name[255]; + int w = OpenGL_GetBackbufferWidth(); + int h = OpenGL_GetBackbufferHeight(); + u8 *data = (u8 *) malloc(3 * w * h); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, data); + if (glGetError() == GL_NO_ERROR) { + if (!s_bLastFrameDumped) { + sprintf(movie_file_name, "%s/framedump.raw", FULL_FRAMES_DIR); + f_pFrameDump = fopen(movie_file_name, "wb"); + if (f_pFrameDump == NULL) { + PanicAlert("Error opening framedump.raw for writing."); + } else { + char msg [255]; + sprintf(msg, "Dumping Frames to \"%s\" (%dx%d RGB24)", movie_file_name, w, h); + OSD::AddMessage(msg, 2000); + } + } + if (f_pFrameDump != NULL) { + FlipImageData(data, w, h); + fwrite(data, w * 3, h, f_pFrameDump); + fflush(f_pFrameDump); + } + s_bLastFrameDumped = true; + } + free(data); + s_criticalScreenshot.Leave(); + } else { + if(s_bLastFrameDumped && f_pFrameDump != NULL) { + fclose(f_pFrameDump); + f_pFrameDump = NULL; + } + + s_bLastFrameDumped = false; + } +#endif + // Place messages on the picture, then copy it to the screen SwapBuffers(); s_bNativeResolution = g_Config.bNativeResolution; @@ -1336,6 +1430,20 @@ bool Renderer::SaveRenderTarget(const char *filename, int w, int h) glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, data); if (glGetError() != GL_NO_ERROR) return false; + FlipImageData(data, w, h); +#if defined(HAVE_WX) && HAVE_WX + wxImage a(w, h, data); + a.SaveFile(wxString::FromAscii(filename), wxBITMAP_TYPE_BMP); + bool result = true; +#else + bool result = SaveTGA(filename, w, h, data); + free(data); +#endif + return result; +} + +void Renderer::FlipImageData(u8 *data, int w, int h) +{ // Flip image upside down. Damn OpenGL. for (int y = 0; y < h / 2; y++) { @@ -1346,16 +1454,6 @@ bool Renderer::SaveRenderTarget(const char *filename, int w, int h) std::swap(data[(y * w + x) * 3 + 2], data[((h - 1 - y) * w + x) * 3 + 2]); } } - -#if defined(HAVE_WX) && HAVE_WX - wxImage a(w, h, data); - a.SaveFile(wxString::FromAscii(filename), wxBITMAP_TYPE_BMP); - bool result = true; -#else - bool result = SaveTGA(filename, w, h, data); - free(data); -#endif - return result; } ////////////////////////////////////////////////////////////////////////////////////// diff --git a/Source/Plugins/Plugin_VideoOGL/Src/Render.h b/Source/Plugins/Plugin_VideoOGL/Src/Render.h index 00a4ff2b35..6f1d8c390c 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/Render.h +++ b/Source/Plugins/Plugin_VideoOGL/Src/Render.h @@ -100,6 +100,7 @@ public: static void RenderText(const char* pstr, int left, int top, u32 color); static void DrawDebugText(); static void SetScreenshot(const char *filename); + static void FlipImageData(u8 *data, int w, int h); static bool SaveRenderTarget(const char *filename, int w, int h); // Finish up the current frame, print some stats