diff options
Diffstat (limited to 'Source/Plugins/Plugin_VideoOGL/Src/Render.cpp')
| -rw-r--r-- | Source/Plugins/Plugin_VideoOGL/Src/Render.cpp | 756 |
1 files changed, 387 insertions, 369 deletions
diff --git a/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp b/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp index 3ff8b834ce..97015db667 100644 --- a/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp +++ b/Source/Plugins/Plugin_VideoOGL/Src/Render.cpp @@ -24,6 +24,9 @@ #include <cstdio> #include "GLUtil.h" +#if defined(HAVE_WX) && HAVE_WX +#include "WxUtils.h" +#endif #include "FileUtil.h" @@ -43,9 +46,8 @@ #include "RasterFont.h" #include "VertexShaderGen.h" #include "DLCache.h" -#include "PixelShaderCache.h" #include "PixelShaderManager.h" -#include "VertexShaderCache.h" +#include "ProgramShaderCache.h" #include "VertexShaderManager.h" #include "VertexLoaderManager.h" #include "VertexLoader.h" @@ -63,6 +65,9 @@ #include "BPFunctions.h" #include "FPSCounter.h" #include "ConfigManager.h" +#include "VertexManager.h" +#include "SamplerCache.h" +#include "StreamBuffer.h" #include "main.h" // Local #ifdef _WIN32 @@ -94,31 +99,41 @@ typedef struct } ScrStrct; #endif -#if defined HAVE_CG && HAVE_CG -CGcontext g_cgcontext; -CGprofile g_cgvProf; -CGprofile g_cgfProf; -#endif int OSDInternalW, OSDInternalH; namespace OGL { +enum MultisampleMode { + MULTISAMPLE_OFF, + MULTISAMPLE_2X, + MULTISAMPLE_4X, + MULTISAMPLE_8X, + MULTISAMPLE_CSAA_8X, + MULTISAMPLE_CSAA_8XQ, + MULTISAMPLE_CSAA_16X, + MULTISAMPLE_CSAA_16XQ, + MULTISAMPLE_SSAA_4X, +}; + + +VideoConfig g_ogl_config; + // Declarations and definitions // ---------------------------- -int s_fps=0; - +static int s_fps = 0; +static GLuint s_ShowEFBCopyRegions_VBO = 0; +static GLuint s_ShowEFBCopyRegions_VAO = 0; +static SHADER s_ShowEFBCopyRegions; -RasterFont* s_pfont = NULL; +static RasterFont* s_pfont = NULL; // 1 for no MSAA. Use s_MSAASamples > 1 to check for MSAA. static int s_MSAASamples = 1; static int s_MSAACoverageSamples = 0; static int s_LastMultisampleMode = 0; -bool s_bHaveFramebufferBlit = false; // export to FramebufferManager.cpp -static bool s_bHaveCoverageMSAA = false; static u32 s_blendMode; #if defined(HAVE_WX) && HAVE_WX @@ -126,69 +141,82 @@ static std::thread scrshotThread; #endif // EFB cache related -const u32 EFB_CACHE_RECT_SIZE = 64; // Cache 64x64 blocks. -const u32 EFB_CACHE_WIDTH = (EFB_WIDTH + EFB_CACHE_RECT_SIZE - 1) / EFB_CACHE_RECT_SIZE; // round up -const u32 EFB_CACHE_HEIGHT = (EFB_HEIGHT + EFB_CACHE_RECT_SIZE - 1) / EFB_CACHE_RECT_SIZE; +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 +static const u32 EFB_CACHE_HEIGHT = (EFB_HEIGHT + EFB_CACHE_RECT_SIZE - 1) / EFB_CACHE_RECT_SIZE; static bool s_efbCacheValid[2][EFB_CACHE_WIDTH * EFB_CACHE_HEIGHT]; static std::vector<u32> s_efbCache[2][EFB_CACHE_WIDTH * EFB_CACHE_HEIGHT]; // 2 for PEEK_Z and PEEK_COLOR - -#if defined HAVE_CG && HAVE_CG -void HandleCgError(CGcontext ctx, CGerror err, void* appdata) -{ - DEBUG_LOG(VIDEO, "Cg error: %s", cgGetErrorString(err)); - const char* listing = cgGetLastListing(g_cgcontext); - if (listing != NULL) - DEBUG_LOG(VIDEO, " last listing: %s", listing); -} -#endif - int GetNumMSAASamples(int MSAAMode) { - // required for MSAA - if (!s_bHaveFramebufferBlit) - return 1; - + int samples; switch (MSAAMode) { case MULTISAMPLE_OFF: - return 1; + samples = 1; + break; case MULTISAMPLE_2X: - return 2; + samples = 2; + break; case MULTISAMPLE_4X: case MULTISAMPLE_CSAA_8X: case MULTISAMPLE_CSAA_16X: - return 4; + case MULTISAMPLE_SSAA_4X: + samples = 4; + break; case MULTISAMPLE_8X: case MULTISAMPLE_CSAA_8XQ: case MULTISAMPLE_CSAA_16XQ: - return 8; + samples = 8; + break; default: - return 1; + samples = 1; } + + if(samples <= g_ogl_config.max_samples) return samples; + + ERROR_LOG(VIDEO, "MSAA Bug: %d samples selected, but only %d supported by gpu.", samples, g_ogl_config.max_samples); + return g_ogl_config.max_samples; } int GetNumMSAACoverageSamples(int MSAAMode) { - if (!s_bHaveCoverageMSAA) - return 0; - + int samples; switch (g_ActiveConfig.iMultisampleMode) { case MULTISAMPLE_CSAA_8X: case MULTISAMPLE_CSAA_8XQ: - return 8; + samples = 8; + break; case MULTISAMPLE_CSAA_16X: case MULTISAMPLE_CSAA_16XQ: - return 16; + samples = 16; + break; default: - return 0; + samples = 0; + } + if(g_ogl_config.bSupportCoverageMSAA || samples == 0) return samples; + + ERROR_LOG(VIDEO, "MSAA Bug: CSAA selected, but not supported by gpu."); + return 0; +} + +void ApplySSAASettings() { + if(g_ActiveConfig.iMultisampleMode == MULTISAMPLE_SSAA_4X) { + if(g_ogl_config.bSupportSampleShading) { + glEnable(GL_SAMPLE_SHADING_ARB); + glMinSampleShadingARB(s_MSAASamples); + } else { + ERROR_LOG(VIDEO, "MSAA Bug: SSAA selected, but not supported by gpu."); + } + } else if(g_ogl_config.bSupportSampleShading) { + glDisable(GL_SAMPLE_SHADING_ARB); } } @@ -199,39 +227,14 @@ Renderer::Renderer() OSDInternalH = 0; s_fps=0; + s_ShowEFBCopyRegions_VBO = 0; s_blendMode = 0; - InitFPSCounter(); -#if defined HAVE_CG && HAVE_CG - g_cgcontext = cgCreateContext(); - cgGetError(); - cgSetErrorHandler(HandleCgError, NULL); -#endif - - // Look for required extensions. - const char *ptoken = (const char*)glGetString(GL_EXTENSIONS); - if (!ptoken) - { - PanicAlert("Your OpenGL Driver seems to be not working.\n" - "Please make sure your drivers are up-to-date and\n" - "that your video hardware is OpenGL 2.x compatible."); - return; // TODO: fail - } - - INFO_LOG(VIDEO, "Supported OpenGL Extensions:"); - INFO_LOG(VIDEO, "%s", ptoken); // write to the log file - INFO_LOG(VIDEO, "\n"); - - OSD::AddMessage(StringFromFormat("Video Info: %s, %s, %s", - glGetString(GL_VENDOR), - glGetString(GL_RENDERER), - glGetString(GL_VERSION)).c_str(), 5000); - bool bSuccess = true; GLint numvertexattribs = 0; glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &numvertexattribs); - if (numvertexattribs < 11) + if (numvertexattribs < 16) { ERROR_LOG(VIDEO, "GPU: OGL ERROR: Number of attributes %d not enough.\n" "GPU: Does your video card support OpenGL 2.x?", @@ -240,42 +243,113 @@ Renderer::Renderer() } // Init extension support. +#ifdef __APPLE__ + glewExperimental = 1; +#endif if (glewInit() != GLEW_OK) { ERROR_LOG(VIDEO, "glewInit() failed! Does your video card support OpenGL 2.x?"); return; // TODO: fail } - if (!GLEW_EXT_framebuffer_object) + if (!GLEW_EXT_secondary_color) { - ERROR_LOG(VIDEO, "GPU: ERROR: Need GL_EXT_framebufer_object for multiple render targets.\n" + ERROR_LOG(VIDEO, "GPU: OGL ERROR: Need GL_EXT_secondary_color.\n" "GPU: Does your video card support OpenGL 2.x?"); bSuccess = false; } - if (!GLEW_EXT_secondary_color) + if (!GLEW_ARB_framebuffer_object) { - ERROR_LOG(VIDEO, "GPU: OGL ERROR: Need GL_EXT_secondary_color.\n" - "GPU: Does your video card support OpenGL 2.x?"); + ERROR_LOG(VIDEO, "GPU: ERROR: Need GL_ARB_framebufer_object for multiple render targets.\n" + "GPU: Does your video card support OpenGL 3.0?"); bSuccess = false; } - s_bHaveFramebufferBlit = strstr(ptoken, "GL_EXT_framebuffer_blit") != NULL; - s_bHaveCoverageMSAA = strstr(ptoken, "GL_NV_framebuffer_multisample_coverage") != NULL; + if (!GLEW_ARB_vertex_array_object) + { + ERROR_LOG(VIDEO, "GPU: OGL ERROR: Need GL_ARB_vertex_array_object.\n" + "GPU: Does your video card support OpenGL 3.0?"); + bSuccess = false; + } + + if (!GLEW_ARB_map_buffer_range) + { + ERROR_LOG(VIDEO, "GPU: OGL ERROR: Need GL_ARB_map_buffer_range.\n" + "GPU: Does your video card support OpenGL 3.0?"); + bSuccess = false; + } - s_LastMultisampleMode = g_ActiveConfig.iMultisampleMode; - s_MSAASamples = GetNumMSAASamples(s_LastMultisampleMode); - s_MSAACoverageSamples = GetNumMSAACoverageSamples(s_LastMultisampleMode); + if (!GLEW_ARB_sampler_objects && bSuccess) + { + ERROR_LOG(VIDEO, "GPU: OGL ERROR: Need GL_ARB_sampler_objects." + "GPU: Does your video card support OpenGL 3.2?" + "Please report this issue, then there will be a workaround"); + bSuccess = false; + } if (!bSuccess) return; // TODO: fail + + g_Config.backend_info.bSupportsDualSourceBlend = GLEW_ARB_blend_func_extended; + g_Config.backend_info.bSupportsGLSLUBO = GLEW_ARB_uniform_buffer_object; + + g_ogl_config.bSupportsGLSLCache = GLEW_ARB_get_program_binary; + g_ogl_config.bSupportsGLPinnedMemory = GLEW_AMD_pinned_memory; + g_ogl_config.bSupportsGLSync = GLEW_ARB_sync; + g_ogl_config.bSupportsGLBaseVertex = GLEW_ARB_draw_elements_base_vertex; + g_ogl_config.bSupportCoverageMSAA = GLEW_NV_framebuffer_multisample_coverage; + g_ogl_config.bSupportSampleShading = GLEW_ARB_sample_shading; + + g_ogl_config.gl_vendor = (const char*)glGetString(GL_VENDOR); + g_ogl_config.gl_renderer = (const char*)glGetString(GL_RENDERER); + g_ogl_config.gl_version = (const char*)glGetString(GL_VERSION); + + glGetIntegerv(GL_MAX_SAMPLES, &g_ogl_config.max_samples); + + if(g_Config.backend_info.bSupportsGLSLUBO && ( + // hd3000 get corruption, hd4000 also and a big slowdown + !strcmp(g_ogl_config.gl_vendor, "Intel Open Source Technology Center") && ( + !strcmp(g_ogl_config.gl_version, "3.0 Mesa 9.0.0") || + !strcmp(g_ogl_config.gl_version, "3.0 Mesa 9.0.1") || + !strcmp(g_ogl_config.gl_version, "3.0 Mesa 9.0.2") || + !strcmp(g_ogl_config.gl_version, "3.0 Mesa 9.0.3") || + !strcmp(g_ogl_config.gl_version, "3.0 Mesa 9.1.0") || + !strcmp(g_ogl_config.gl_version, "3.0 Mesa 9.1.1") ) + )) { + g_Config.backend_info.bSupportsGLSLUBO = false; + ERROR_LOG(VIDEO, "buggy driver detected. Disable UBO"); + } + + UpdateActiveConfig(); + OSD::AddMessage(StringFromFormat("Video Info: %s, %s, %s", + g_ogl_config.gl_vendor, + g_ogl_config.gl_renderer, + g_ogl_config.gl_version).c_str(), 5000); + + OSD::AddMessage(StringFromFormat("Missing Extensions: %s%s%s%s%s%s%s%s", + g_ActiveConfig.backend_info.bSupportsDualSourceBlend ? "" : "DualSourceBlend ", + g_ActiveConfig.backend_info.bSupportsGLSLUBO ? "" : "UniformBuffer ", + g_ogl_config.bSupportsGLPinnedMemory ? "" : "PinnedMemory ", + g_ogl_config.bSupportsGLSLCache ? "" : "ShaderCache ", + g_ogl_config.bSupportsGLBaseVertex ? "" : "BaseVertex ", + g_ogl_config.bSupportsGLSync ? "" : "Sync ", + g_ogl_config.bSupportCoverageMSAA ? "" : "CSAA ", + g_ogl_config.bSupportSampleShading ? "" : "SSAA " + ).c_str(), 5000); + + s_LastMultisampleMode = g_ActiveConfig.iMultisampleMode; + s_MSAASamples = GetNumMSAASamples(s_LastMultisampleMode); + s_MSAACoverageSamples = GetNumMSAACoverageSamples(s_LastMultisampleMode); + ApplySSAASettings(); + // Decide frambuffer size s_backbuffer_width = (int)GLInterface->GetBackBufferWidth(); s_backbuffer_height = (int)GLInterface->GetBackBufferHeight(); // Handle VSync on/off - int swapInterval = g_ActiveConfig.bVSync ? 1 : 0; + int swapInterval = g_ActiveConfig.IsVSync() ? 1 : 0; GLInterface->SwapInterval(swapInterval); // check the max texture width and height @@ -288,9 +362,6 @@ Renderer::Renderer() if (GL_REPORT_ERROR() != GL_NO_ERROR) bSuccess = false; - if (glDrawBuffers == NULL && !GLEW_ARB_draw_buffers) - glDrawBuffers = glDrawBuffersARB; - if (!GLEW_ARB_texture_non_power_of_two) WARN_LOG(VIDEO, "ARB_texture_non_power_of_two not supported."); @@ -314,90 +385,17 @@ Renderer::Renderer() g_framebuffer_manager = new FramebufferManager(s_target_width, s_target_height, s_MSAASamples, s_MSAACoverageSamples); - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); - if (GL_REPORT_ERROR() != GL_NO_ERROR) bSuccess = false; - - s_pfont = new RasterFont(); - -#if defined HAVE_CG && HAVE_CG - // load the effect, find the best profiles (if any) - if (cgGLIsProfileSupported(CG_PROFILE_ARBVP1) != CG_TRUE) - { - ERROR_LOG(VIDEO, "arbvp1 not supported"); - return; // TODO: fail - } - - if (cgGLIsProfileSupported(CG_PROFILE_ARBFP1) != CG_TRUE) - { - ERROR_LOG(VIDEO, "arbfp1 not supported"); - return; // TODO: fail - } - - g_cgvProf = cgGLGetLatestProfile(CG_GL_VERTEX); - g_cgfProf = cgGLGetLatestProfile(CG_GL_FRAGMENT); - if (strstr((const char*)glGetString(GL_VENDOR), "Humper") == NULL) - { -#if CG_VERSION_NUM == 2100 - // A bug was introduced in Cg2.1's handling of very large profile option values - // so this will not work on ATI. ATI returns MAXINT = 2147483647 (0x7fffffff) - // which is correct in OpenGL but Cg fails to handle it properly. As a result - // -1 is used by Cg resulting (signedness incorrect) and compilation fails. - if (strstr((const char*)glGetString(GL_VENDOR), "ATI") == NULL) -#endif - { - cgGLSetOptimalOptions(g_cgvProf); - cgGLSetOptimalOptions(g_cgfProf); - } - } -#endif // HAVE_CG - - int nenvvertparams, nenvfragparams, naddrregisters[2]; - glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, - GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, - (GLint *)&nenvvertparams); - glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, - GL_MAX_PROGRAM_ENV_PARAMETERS_ARB, - (GLint *)&nenvfragparams); - glGetProgramivARB(GL_VERTEX_PROGRAM_ARB, - GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB, - (GLint *)&naddrregisters[0]); - glGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, - GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB, - (GLint *)&naddrregisters[1]); - DEBUG_LOG(VIDEO, "Max program env parameters: vert=%d, frag=%d", - nenvvertparams, nenvfragparams); - DEBUG_LOG(VIDEO, "Max program address register parameters: vert=%d, frag=%d", - naddrregisters[0], naddrregisters[1]); - - if (nenvvertparams < 238) - ERROR_LOG(VIDEO, "Not enough vertex shader environment constants!!"); - -#if defined HAVE_CG && HAVE_CG - INFO_LOG(VIDEO, "Max buffer sizes: %d %d", - cgGetProgramBufferMaxSize(g_cgvProf), - cgGetProgramBufferMaxSize(g_cgfProf)); -#ifndef _DEBUG - cgGLSetDebugMode(GL_FALSE); -#endif -#endif - + glStencilFunc(GL_ALWAYS, 0, 0); glBlendFunc(GL_ONE, GL_ONE); glViewport(0, 0, GetTargetWidth(), GetTargetHeight()); // Reset The Current Viewport - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - glShadeModel(GL_SMOOTH); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClearDepth(1.0f); glEnable(GL_DEPTH_TEST); - glDisable(GL_LIGHTING); glDepthFunc(GL_LEQUAL); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // 4-byte pixel alignment @@ -406,39 +404,14 @@ Renderer::Renderer() glEnable(GL_SCISSOR_TEST); glScissor(0, 0, GetTargetWidth(), GetTargetHeight()); - glBlendColorEXT(0, 0, 0, 0.5f); + glBlendColor(0, 0, 0, 0.5f); glClearDepth(1.0f); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - // legacy multitexturing: select texture channel only. - glActiveTexture(GL_TEXTURE0); - glClientActiveTexture(GL_TEXTURE0); - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - UpdateActiveConfig(); - - //return GL_REPORT_ERROR() == GL_NO_ERROR && bSuccess; - return; } Renderer::~Renderer() { - g_Config.bRunning = false; - UpdateActiveConfig(); - delete s_pfont; - s_pfont = 0; - -#if defined HAVE_CG && HAVE_CG - if (g_cgcontext) - { - cgDestroyContext(g_cgcontext); - g_cgcontext = 0; - } -#endif #if defined(HAVE_WX) && HAVE_WX if (scrshotThread.joinable()) @@ -448,6 +421,49 @@ Renderer::~Renderer() delete g_framebuffer_manager; } +void Renderer::Shutdown() +{ + g_Config.bRunning = false; + UpdateActiveConfig(); + + glDeleteBuffers(1, &s_ShowEFBCopyRegions_VBO); + glDeleteVertexArrays(1, &s_ShowEFBCopyRegions_VAO); + s_ShowEFBCopyRegions_VBO = 0; + + delete s_pfont; + s_pfont = 0; + s_ShowEFBCopyRegions.Destroy(); +} + +void Renderer::Init() +{ + s_pfont = new RasterFont(); + + ProgramShaderCache::CompileShader(s_ShowEFBCopyRegions, + "in vec2 rawpos;\n" + "in vec3 color0;\n" + "out vec4 c;\n" + "void main(void) {\n" + " gl_Position = vec4(rawpos,0,1);\n" + " c = vec4(color0, 1.0);\n" + "}\n", + "in vec4 c;\n" + "out vec4 ocol0;\n" + "void main(void) {\n" + " ocol0 = c;\n" + "}\n"); + + // creating buffers + glGenBuffers(1, &s_ShowEFBCopyRegions_VBO); + glGenVertexArrays(1, &s_ShowEFBCopyRegions_VAO); + glBindBuffer(GL_ARRAY_BUFFER, s_ShowEFBCopyRegions_VBO); + glBindVertexArray( s_ShowEFBCopyRegions_VAO ); + glEnableVertexAttribArray(SHADER_POSITION_ATTRIB); + glVertexAttribPointer(SHADER_POSITION_ATTRIB, 2, GL_FLOAT, 0, sizeof(GLfloat)*5, NULL); + glEnableVertexAttribArray(SHADER_COLOR0_ATTRIB); + glVertexAttribPointer(SHADER_COLOR0_ATTRIB, 3, GL_FLOAT, 0, sizeof(GLfloat)*5, (GLfloat*)NULL+2); +} + // Create On-Screen-Messages void Renderer::DrawDebugInfo() { @@ -469,16 +485,18 @@ void Renderer::DrawDebugInfo() if (g_ActiveConfig.bShowEFBCopyRegions) { - // Store Line Size - GLfloat lSize; - glGetFloatv(GL_LINE_WIDTH, &lSize); - // Set Line Size glLineWidth(3.0f); - glBegin(GL_LINES); + // 2*Coords + 3*Color + glBindBuffer(GL_ARRAY_BUFFER, s_ShowEFBCopyRegions_VBO); + glBufferData(GL_ARRAY_BUFFER, stats.efb_regions.size() * sizeof(GLfloat) * (2+3)*2*6, NULL, GL_STREAM_DRAW); + GLfloat *Vertices = (GLfloat*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); // Draw EFB copy regions rectangles + int a = 0; + GLfloat color[3] = {0.0f, 1.0f, 1.0f}; + for (std::vector<EFBRectangle>::const_iterator it = stats.efb_regions.begin(); it != stats.efb_regions.end(); ++it) { @@ -489,25 +507,97 @@ void Renderer::DrawDebugInfo() GLfloat x2 = (GLfloat) -1.0f + ((GLfloat)it->right / halfWidth); GLfloat y2 = (GLfloat) 1.0f - ((GLfloat)it->bottom / halfHeight); - // Draw shadow of rect - glColor3f(0.0f, 0.0f, 0.0f); - glVertex2f(x, y - 0.01); glVertex2f(x2, y - 0.01); - glVertex2f(x, y2 - 0.01); glVertex2f(x2, y2 - 0.01); - glVertex2f(x + 0.005, y); glVertex2f(x + 0.005, y2); - glVertex2f(x2 + 0.005, y); glVertex2f(x2 + 0.005, y2); - - // Draw rect - glColor3f(0.0f, 1.0f, 1.0f); - glVertex2f(x, y); glVertex2f(x2, y); - glVertex2f(x, y2); glVertex2f(x2, y2); - glVertex2f(x, y); glVertex2f(x, y2); - glVertex2f(x2, y); glVertex2f(x2, y2); + Vertices[a++] = x; + Vertices[a++] = y; + Vertices[a++] = color[0]; + Vertices[a++] = color[1]; + Vertices[a++] = color[2]; + + Vertices[a++] = x2; + Vertices[a++] = y; + Vertices[a++] = color[0]; + Vertices[a++] = color[1]; + Vertices[a++] = color[2]; + + + Vertices[a++] = x2; + Vertices[a++] = y; + Vertices[a++] = color[0]; + Vertices[a++] = color[1]; + Vertices[a++] = color[2]; + + Vertices[a++] = x2; + Vertices[a++] = y2; + Vertices[a++] = color[0]; + Vertices[a++] = color[1]; + Vertices[a++] = color[2]; + + + Vertices[a++] = x2; + Vertices[a++] = y2; + Vertices[a++] = color[0]; + Vertices[a++] = color[1]; + Vertices[a++] = color[2]; + + Vertices[a++] = x; + Vertices[a++] = y2; + Vertices[a++] = color[0]; + Vertices[a++] = color[1]; + Vertices[a++] = color[2]; + + + Vertices[a++] = x; + Vertices[a++] = y2; + Vertices[a++] = color[0]; + Vertices[a++] = color[1]; + Vertices[a++] = color[2]; + + Vertices[a++] = x; + Vertices[a++] = y; + Vertices[a++] = color[0]; + Vertices[a++] = color[1]; + Vertices[a++] = color[2]; + + + Vertices[a++] = x; + Vertices[a++] = y; + Vertices[a++] = color[0]; + Vertices[a++] = color[1]; + Vertices[a++] = color[2]; + + Vertices[a++] = x2; + Vertices[a++] = y2; + Vertices[a++] = color[0]; + Vertices[a++] = color[1]; + Vertices[a++] = color[2]; + + + Vertices[a++] = x2; + Vertices[a++] = y; + Vertices[a++] = color[0]; + Vertices[a++] = color[1]; + Vertices[a++] = color[2]; + + Vertices[a++] = x; + Vertices[a++] = y2; + Vertices[a++] = color[0]; + Vertices[a++] = color[1]; + Vertices[a++] = color[2]; + + // TO DO: build something nicer here + GLfloat temp = color[0]; + color[0] = color[1]; + color[1] = color[2]; + color[2] = temp; } - - glEnd(); + glUnmapBuffer(GL_ARRAY_BUFFER); + + s_ShowEFBCopyRegions.Bind(); + glBindVertexArray( s_ShowEFBCopyRegions_VAO ); + glDrawArrays(GL_LINES, 0, stats.efb_regions.size() * 2*6); // Restore Line Size - glLineWidth(lSize); + SetLineWidth(); // Clear stored regions stats.efb_regions.clear(); @@ -532,20 +622,12 @@ void Renderer::RenderText(const char *text, int left, int top, u32 color) const int nBackbufferWidth = (int)GLInterface->GetBackBufferWidth(); const int nBackbufferHeight = (int)GLInterface->GetBackBufferHeight(); - glColor4f(((color>>16) & 0xff)/255.0f, ((color>> 8) & 0xff)/255.0f, - ((color>> 0) & 0xff)/255.0f, ((color>>24) & 0xFF)/255.0f); - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - s_pfont->printMultilineText(text, left * 2.0f / (float)nBackbufferWidth - 1, 1 - top * 2.0f / (float)nBackbufferHeight, - 0, nBackbufferWidth, nBackbufferHeight); + 0, nBackbufferWidth, nBackbufferHeight, color); GL_REPORT_ERRORD(); - - glDisable(GL_BLEND); } TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc) @@ -671,7 +753,7 @@ u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) { // Resolve our rectangle. FramebufferManager::GetEFBDepthTexture(efbPixelRc); - glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, FramebufferManager::GetResolvedFramebuffer()); + glBindFramebuffer(GL_READ_FRAMEBUFFER, FramebufferManager::GetResolvedFramebuffer()); } u32* depthMap = new u32[targetPixelRcWidth * targetPixelRcHeight]; @@ -720,7 +802,7 @@ u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data) { // Resolve our rectangle. FramebufferManager::GetEFBColorTexture(efbPixelRc); - glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, FramebufferManager::GetResolvedFramebuffer()); + glBindFramebuffer(GL_READ_FRAMEBUFFER, FramebufferManager::GetResolvedFramebuffer()); } u32* colorMap = new u32[targetPixelRcWidth * targetPixelRcHeight]; @@ -858,7 +940,11 @@ void Renderer::SetBlendMode(bool forceUpdate) { // Our render target always uses an alpha channel, so we need to override the blend functions to assume a destination alpha of 1 if the render target isn't supposed to have an alpha channel // Example: D3DBLEND_DESTALPHA needs to be D3DBLEND_ONE since the result without an alpha channel is assumed to always be 1. - bool target_has_alpha = bpmem.zcontrol.pixel_format == PIXELFMT_RGBA6_Z24; + bool target_has_alpha = bpmem.zcontrol.pixel_format == PIXELFMT_RGBA6_Z24; + + bool useDstAlpha = !g_ActiveConfig.bDstAlphaPass && bpmem.dstalpha.enable && bpmem.blendmode.alphaupdate && target_has_alpha; + bool useDualSource = useDstAlpha && g_ActiveConfig.backend_info.bSupportsDualSourceBlend; + const GLenum glSrcFactors[8] = { GL_ZERO, @@ -884,11 +970,13 @@ void Renderer::SetBlendMode(bool forceUpdate) // blend mode bit mask // 0 - blend enable + // 1 - dst alpha enabled // 2 - reverse subtract enable (else add) // 3-5 - srcRGB function // 6-8 - dstRGB function - u32 newval = bpmem.blendmode.subtract << 2; + u32 newval = useDualSource << 1; + newval |= bpmem.blendmode.subtract << 2; if (bpmem.blendmode.subtract) newval |= 0x0049; // enable blending src 1 dst 1 @@ -901,33 +989,23 @@ void Renderer::SetBlendMode(bool forceUpdate) u32 changes = forceUpdate ? 0xFFFFFFFF : newval ^ s_blendMode; -#ifdef USE_DUAL_SOURCE_BLEND - bool useDstAlpha = !g_ActiveConfig.bDstAlphaPass && bpmem.dstalpha.enable && bpmem.blendmode.alphaupdate - && bpmem.zcontrol.pixel_format == PIXELFMT_RGBA6_Z24; - bool useDualSource = useDstAlpha && GLEW_ARB_blend_func_extended; -#endif - if (changes & 1) // blend enable change (newval & 1) ? glEnable(GL_BLEND) : glDisable(GL_BLEND); if (changes & 4) { -#ifdef USE_DUAL_SOURCE_BLEND // subtract enable change GLenum equation = newval & 4 ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD; GLenum equationAlpha = useDualSource ? GL_FUNC_ADD : equation; + glBlendEquationSeparate(equation, equationAlpha); -#else - glBlendEquation(newval & 4 ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD); -#endif } - if (changes & 0x1F8) + if (changes & 0x1FA) { GLenum srcFactor = glSrcFactors[(newval >> 3) & 7]; GLenum dstFactor = glDestFactors[(newval >> 6) & 7]; -#ifdef USE_DUAL_SOURCE_BLEND GLenum srcFactorAlpha = srcFactor; GLenum dstFactorAlpha = dstFactor; if (useDualSource) @@ -945,31 +1023,31 @@ void Renderer::SetBlendMode(bool forceUpdate) else if (dstFactor == GL_ONE_MINUS_SRC_ALPHA) dstFactor = GL_ONE_MINUS_SRC1_ALPHA; } - + // blend RGB change glBlendFuncSeparate(srcFactor, dstFactor, srcFactorAlpha, dstFactorAlpha); -#else - glBlendFunc(srcFactor, dstFactor); -#endif } s_blendMode = newval; } +void DumpFrame(const std::vector<u8>& data, int w, int h) +{ +#if defined(HAVE_LIBAV) || defined(_WIN32) + if (g_ActiveConfig.bDumpFrames && !data.empty()) + { + AVIDump::AddFrame(&data[0], w, h); + } +#endif +} + // This function has the final picture. We adjust the aspect ratio here. void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,const EFBRectangle& rc,float Gamma) { static int w = 0, h = 0; if (g_bSkipCurrentFrame || (!XFBWrited && !g_ActiveConfig.RealXFBEnabled()) || !fbWidth || !fbHeight) { - if (g_ActiveConfig.bDumpFrames && frame_data) - { -#ifdef _WIN32 - AVIDump::AddFrame(frame_data); -#elif defined HAVE_LIBAV - AVIDump::AddFrame((u8*)frame_data, w, h); -#endif - } + DumpFrame(frame_data, w, h); Core::Callback_VideoCopiedToXFB(false); return; } @@ -979,20 +1057,14 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons const XFBSourceBase* const* xfbSourceList = FramebufferManager::GetXFBSource(xfbAddr, fbWidth, fbHeight, xfbCount); if (g_ActiveConfig.VirtualXFBEnabled() && (!xfbSourceList || xfbCount == 0)) { - if (g_ActiveConfig.bDumpFrames && frame_data) - { -#ifdef _WIN32 - AVIDump::AddFrame(frame_data); -#elif defined HAVE_LIBAV - AVIDump::AddFrame((u8*)frame_data, w, h); -#endif - } + DumpFrame(frame_data, w, h); Core::Callback_VideoCopiedToXFB(false); return; } ResetAPIState(); + PostProcessing::Update(s_backbuffer_width, s_backbuffer_height); UpdateDrawRectangle(s_backbuffer_width, s_backbuffer_height); TargetRectangle flipped_trc = GetTargetRectangle(); @@ -1000,38 +1072,20 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons int tmp = flipped_trc.top; flipped_trc.top = flipped_trc.bottom; flipped_trc.bottom = tmp; - - // Textured triangles are necessary because of post-processing shaders - - // Disable all other stages - for (int i = 1; i < 8; ++i) - OGL::TextureCache::DisableStage(i); - - // Update GLViewPort - glViewport(flipped_trc.left, flipped_trc.bottom, flipped_trc.GetWidth(), flipped_trc.GetHeight()); - + GL_REPORT_ERRORD(); // Copy the framebuffer to screen. - // Texture map s_xfbTexture onto the main buffer - glActiveTexture(GL_TEXTURE0); - glEnable(GL_TEXTURE_RECTANGLE_ARB); - // Use linear filtering. - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - // We must call ApplyShader here even if no post proc is selected - it takes - // care of disabling it in that case. It returns false in case of no post processing. - bool applyShader = PostProcessing::ApplyShader(); - const XFBSourceBase* xfbSource = NULL; if(g_ActiveConfig.bUseXFB) { + // Render to the real/postprocessing buffer now. + PostProcessing::BindTargetFramebuffer(); + // draw each xfb source - // Render to the real buffer now. - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the window backbuffer + glBindFramebuffer(GL_READ_FRAMEBUFFER, FramebufferManager::GetXFBFramebuffer()); for (u32 i = 0; i < xfbCount; ++i) { @@ -1041,10 +1095,10 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons if (g_ActiveConfig.bUseRealXFB) { - drawRc.top = 1; - drawRc.bottom = -1; - drawRc.left = -1; - drawRc.right = 1; + drawRc.top = flipped_trc.top; + drawRc.bottom = flipped_trc.bottom; + drawRc.left = flipped_trc.left; + drawRc.right = flipped_trc.right; } else { @@ -1052,12 +1106,12 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons int xfbHeight = xfbSource->srcHeight; int xfbWidth = xfbSource->srcWidth; int hOffset = ((s32)xfbSource->srcAddr - (s32)xfbAddr) / ((s32)fbWidth * 2); - - drawRc.top = 1.0f - (2.0f * (hOffset) / (float)fbHeight); - drawRc.bottom = 1.0f - (2.0f * (hOffset + xfbHeight) / (float)fbHeight); - drawRc.left = -(xfbWidth / (float)fbWidth); - drawRc.right = (xfbWidth / (float)fbWidth); - + + drawRc.top = flipped_trc.top - hOffset * flipped_trc.GetHeight() / fbHeight; + drawRc.bottom = flipped_trc.top - (hOffset + xfbHeight) * flipped_trc.GetHeight() / fbHeight; + drawRc.left = flipped_trc.left + (flipped_trc.GetWidth() - xfbWidth * flipped_trc.GetWidth() / fbWidth)/2; + drawRc.right = flipped_trc.left + (flipped_trc.GetWidth() + xfbWidth * flipped_trc.GetWidth() / fbWidth)/2; + // The following code disables auto stretch. Kept for reference. // scale draw area for a 1 to 1 pixel mapping with the draw target //float vScale = (float)fbHeight / (float)flipped_trc.GetHeight(); @@ -1077,62 +1131,30 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons sourceRc.bottom = xfbSource->sourceRc.bottom; xfbSource->Draw(sourceRc, drawRc, 0, 0); - - // We must call ApplyShader here even if no post proc is selected. - // It takes care of disabling it in that case. It returns false in - // case of no post processing. - if (applyShader) - PixelShaderCache::DisableShader(); } } else { TargetRectangle targetRc = ConvertEFBRectangle(rc); - GLuint read_texture = FramebufferManager::ResolveAndGetRenderTarget(rc); - // Render to the real buffer now. - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // switch to the window backbuffer - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, read_texture); - if (applyShader) - { - glBegin(GL_QUADS); - glTexCoord2f(targetRc.left, targetRc.bottom); - glMultiTexCoord2fARB(GL_TEXTURE1, 0, 0); - glVertex2f(-1, -1); - - glTexCoord2f(targetRc.left, targetRc.top); - glMultiTexCoord2fARB(GL_TEXTURE1, 0, 1); - glVertex2f(-1, 1); - - glTexCoord2f(targetRc.right, targetRc.top); - glMultiTexCoord2fARB(GL_TEXTURE1, 1, 1); - glVertex2f( 1, 1); - - glTexCoord2f(targetRc.right, targetRc.bottom); - glMultiTexCoord2fARB(GL_TEXTURE1, 1, 0); - glVertex2f( 1, -1); - glEnd(); - PixelShaderCache::DisableShader(); - } - else - { - glBegin(GL_QUADS); - glTexCoord2f(targetRc.left, targetRc.bottom); - glVertex2f(-1, -1); - - glTexCoord2f(targetRc.left, targetRc.top); - glVertex2f(-1, 1); - - glTexCoord2f(targetRc.right, targetRc.top); - glVertex2f( 1, 1); - - glTexCoord2f(targetRc.right, targetRc.bottom); - glVertex2f( 1, -1); - glEnd(); - } + + // for msaa mode, we must resolve the efb content to non-msaa + FramebufferManager::ResolveAndGetRenderTarget(rc); + + // Render to the real/postprocessing buffer now. (resolve have changed this in msaa mode) + PostProcessing::BindTargetFramebuffer(); + + // always the non-msaa fbo + GLuint fb = s_MSAASamples>1?FramebufferManager::GetResolvedFramebuffer():FramebufferManager::GetEFBFramebuffer(); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, fb); + glBlitFramebuffer(targetRc.left, targetRc.bottom, targetRc.right, targetRc.top, + flipped_trc.left, flipped_trc.bottom, flipped_trc.right, flipped_trc.top, + GL_COLOR_BUFFER_BIT, GL_LINEAR); } + + PostProcessing::BlitToScreen(); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); - OGL::TextureCache::DisableStage(0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); // Save screenshot if (s_bScreenshot) @@ -1149,16 +1171,15 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons if (g_ActiveConfig.bDumpFrames) { std::lock_guard<std::mutex> lk(s_criticalScreenshot); - if (!frame_data || w != flipped_trc.GetWidth() || + if (frame_data.empty() || w != flipped_trc.GetWidth() || h != flipped_trc.GetHeight()) { - if (frame_data) delete[] frame_data; w = flipped_trc.GetWidth(); h = flipped_trc.GetHeight(); - frame_data = new char[3 * w * h]; + frame_data.resize(3 * w * h); } glPixelStorei(GL_PACK_ALIGNMENT, 1); - glReadPixels(flipped_trc.left, flipped_trc.bottom, w, h, GL_BGR, GL_UNSIGNED_BYTE, frame_data); + glReadPixels(flipped_trc.left, flipped_trc.bottom, w, h, GL_BGR, GL_UNSIGNED_BYTE, &frame_data[0]); if (GL_REPORT_ERROR() == GL_NO_ERROR && w > 0 && h > 0) { if (!bLastFrameDumped) @@ -1179,12 +1200,11 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons } if (bAVIDumping) { - #ifdef _WIN32 - AVIDump::AddFrame(frame_data); - #else - FlipImageData((u8*)frame_data, w, h); - AVIDump::AddFrame((u8*)frame_data, w, h); + #ifndef _WIN32 + FlipImageData(&frame_data[0], w, h); #endif + + AVIDump::AddFrame(&frame_data[0], w, h); } bLastFrameDumped = true; @@ -1196,12 +1216,8 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons { if (bLastFrameDumped && bAVIDumping) { - if (frame_data) - { - delete[] frame_data; - frame_data = NULL; - w = h = 0; - } + std::vector<u8>().swap(frame_data); + w = h = 0; AVIDump::Stop(); bAVIDumping = false; OSD::AddMessage("Stop dumping frames", 2000); @@ -1215,9 +1231,9 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons std::string movie_file_name; w = GetTargetRectangle().GetWidth(); h = GetTargetRectangle().GetHeight(); - frame_data = new char[3 * w * h]; + frame_data.resize(3 * w * h); glPixelStorei(GL_PACK_ALIGNMENT, 1); - glReadPixels(GetTargetRectangle().left, GetTargetRectangle().bottom, w, h, GL_BGR, GL_UNSIGNED_BYTE, frame_data); + glReadPixels(GetTargetRectangle().left, GetTargetRectangle().bottom, w, h, GL_BGR, GL_UNSIGNED_BYTE, &frame_data[0]); if (GL_REPORT_ERROR() == GL_NO_ERROR) { if (!bLastFrameDumped) @@ -1228,21 +1244,17 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons OSD::AddMessage("Error opening framedump.raw for writing.", 2000); else { - char msg [255]; - sprintf(msg, "Dumping Frames to \"%s\" (%dx%d RGB24)", movie_file_name.c_str(), w, h); - OSD::AddMessage(msg, 2000); + OSD::AddMessage(StringFromFormat("Dumping Frames to \"%s\" (%dx%d RGB24)", movie_file_name.c_str(), w, h).c_str(), 2000); } } if (pFrameDump) { - FlipImageData((u8*)frame_data, w, h); - pFrameDump.WriteBytes(frame_data, w * 3 * h); + FlipImageData(&frame_data[0], w, h); + pFrameDump.WriteBytes(&frame_data[0], w * 3 * h); pFrameDump.Flush(); } bLastFrameDumped = true; } - - delete[] frame_data; } else { @@ -1289,11 +1301,11 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons s_LastMultisampleMode = g_ActiveConfig.iMultisampleMode; s_MSAASamples = GetNumMSAASamples(s_LastMultisampleMode); s_MSAACoverageSamples = GetNumMSAACoverageSamples(s_LastMultisampleMode); - + ApplySSAASettings(); + delete g_framebuffer_manager; g_framebuffer_manager = new FramebufferManager(s_target_width, s_target_height, s_MSAASamples, s_MSAACoverageSamples); - glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT); } } @@ -1301,18 +1313,16 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons s_fps = UpdateFPSCounter(); // --------------------------------------------------------------------- GL_REPORT_ERRORD(); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - DrawDebugText(); DrawDebugInfo(); + DrawDebugText(); GL_REPORT_ERRORD(); - // Get the status of the Blend mode - GLboolean blend_enabled = glIsEnabled(GL_BLEND); - glDisable(GL_BLEND); OSD::DrawMessages(); - if (blend_enabled) - glEnable(GL_BLEND); GL_REPORT_ERRORD(); // Copy the rendered frame to the real window @@ -1329,6 +1339,8 @@ void Renderer::Swap(u32 xfbAddr, FieldType field, u32 fbWidth, u32 fbHeight,cons GL_REPORT_ERRORD(); + GLInterface->SwapInterval(g_ActiveConfig.IsVSync() ? 1 : 0); + // Clean out old stuff from caches. It's not worth it to clean out the shader caches. DLCache::ProgressiveCleanup(); TextureCache::Cleanup(); @@ -1371,8 +1383,6 @@ void Renderer::ResetAPIState() { // Gets us to a reasonably sane state where it's possible to do things like // image copies with textured quads, etc. - VertexShaderCache::DisableShader(); - PixelShaderCache::DisableShader(); glDisable(GL_SCISSOR_TEST); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); @@ -1393,9 +1403,12 @@ void Renderer::RestoreAPIState() VertexShaderManager::SetViewportChanged(); glPolygonMode(GL_FRONT_AND_BACK, g_ActiveConfig.bWireFrame ? GL_LINE : GL_FILL); - - VertexShaderCache::SetCurrentShader(0); - PixelShaderCache::SetCurrentShader(0); + + VertexManager *vm = (OGL::VertexManager*)g_vertex_manager; + glBindBuffer(GL_ARRAY_BUFFER, vm->m_vertex_buffers); + vm->m_last_vao = 0; + + TextureCache::SetStage(); } void Renderer::SetGenerationMode() @@ -1433,6 +1446,7 @@ void Renderer::SetDepthMode() else { // if the test is disabled write is disabled too + // TODO: When PE performance metrics are being emulated via occlusion queries, we should (probably?) enable depth test with depth function ALWAYS here glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); } @@ -1492,7 +1506,11 @@ void Renderer::SetLineWidth() void Renderer::SetSamplerState(int stage, int texindex) { - // TODO + auto const& tex = bpmem.tex[texindex]; + auto const& tm0 = tex.texMode0[stage]; + auto const& tm1 = tex.texMode1[stage]; + + g_sampler_cache->SetSamplerState((texindex * 4) + stage, tm0, tm1); } void Renderer::SetInterlacingMode() @@ -1552,7 +1570,7 @@ void TakeScreenshot(ScrStrct* threadStruct) // 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(wxString::FromAscii(threadStruct->filename.c_str()), + threadStruct->img->SaveFile(StrToWxStr(threadStruct->filename), wxBITMAP_TYPE_PNG); threadStruct->img->Destroy(); |
