From 50d6b62adf63328720211459babd07f0dd2416ba Mon Sep 17 00:00:00 2001 From: baldurk Date: Mon, 1 Dec 2014 17:13:30 +0000 Subject: [PATCH] Proper SRGB handling in opengl. See gl-320-fbo-srgb * We create fake backbuffer as SRGB if the original default backbuffer was SRGB in the program. * All of our output backbuffers are also SRGB, and we enable GL_FRAMEBUFFER_SRGB when writing to them. * Add the 'fake' srgb curve applied to linear data to display it as if it were srgb, which tends to look intuitively correct even if it's not 100% accurate. See the behaviour existing in D3D11 already. --- renderdoc/data/glsl/debuguniforms.h | 1 + renderdoc/data/glsl/texdisplay.frag | 8 +-- renderdoc/driver/gl/gl_debug.cpp | 7 ++- renderdoc/driver/gl/gl_driver.cpp | 16 +++--- renderdoc/driver/gl/gl_driver.h | 3 +- renderdoc/driver/gl/gl_replay.cpp | 3 +- renderdoc/driver/gl/gl_replay_linux.cpp | 1 + renderdoc/driver/gl/gl_replay_win32.cpp | 72 ++++++++++++++++++++++--- renderdoc/driver/gl/gl_resources.cpp | 14 +++++ renderdoc/driver/gl/gl_resources.h | 1 + renderdoc/hooks/gl_linux_hooks.cpp | 2 + renderdoc/hooks/gl_win32_hooks.cpp | 13 +++++ 12 files changed, 122 insertions(+), 19 deletions(-) diff --git a/renderdoc/data/glsl/debuguniforms.h b/renderdoc/data/glsl/debuguniforms.h index d22405459..863631e9f 100644 --- a/renderdoc/data/glsl/debuguniforms.h +++ b/renderdoc/data/glsl/debuguniforms.h @@ -111,6 +111,7 @@ BINDING(0) uniform HistogramCBufferData #define TEXDISPLAY_DEPTH_TEX 0x20 #define TEXDISPLAY_NANS 0x40 #define TEXDISPLAY_CLIPPING 0x80 +#define TEXDISPLAY_GAMMA_CURVE 0x100 #ifndef FLT_EPSILON #define FLT_EPSILON 1.192092896e-07f diff --git a/renderdoc/data/glsl/texdisplay.frag b/renderdoc/data/glsl/texdisplay.frag index 8fba75ce1..c1bfbdebd 100644 --- a/renderdoc/data/glsl/texdisplay.frag +++ b/renderdoc/data/glsl/texdisplay.frag @@ -154,9 +154,11 @@ void main(void) col = vec4(dot(col.rgb, 1.0f.xxx).xxx, 1.0f); } } - - // TODO: Check OutputDisplayFormat for SRGB handling - // TODO: Figure out SRGB in opengl at all :) + + if((OutputDisplayFormat & TEXDISPLAY_GAMMA_CURVE) > 0) + { + col.rgb = pow(clamp(col.rgb, 0.0f.xxx, 1.0f.xxx), 2.2f.xxx); + } color_out = col; } \ No newline at end of file diff --git a/renderdoc/driver/gl/gl_debug.cpp b/renderdoc/driver/gl/gl_debug.cpp index ac51239a0..12bb876f8 100644 --- a/renderdoc/driver/gl/gl_debug.cpp +++ b/renderdoc/driver/gl/gl_debug.cpp @@ -759,6 +759,9 @@ bool GLReplay::RenderTexture(TextureDisplay cfg) if(cfg.overlay == eTexOverlay_Clipping) ubo->OutputDisplayFormat |= TEXDISPLAY_CLIPPING; + + if(!IsSRGBFormat(texDetails.internalFormat) && cfg.linearDisplayAsGamma) + ubo->OutputDisplayFormat |= TEXDISPLAY_GAMMA_CURVE; ubo->RawOutput = cfg.rawoutput ? 1 : 0; @@ -788,6 +791,8 @@ bool GLReplay::RenderTexture(TextureDisplay cfg) gl.glBlendFunc(eGL_SRC_ALPHA, eGL_ONE_MINUS_SRC_ALPHA); } + gl.glEnable(eGL_FRAMEBUFFER_SRGB); + gl.glBindVertexArray(DebugData.emptyVAO); gl.glDrawArrays(eGL_TRIANGLE_STRIP, 0, 4); @@ -933,7 +938,7 @@ ResourceId GLReplay::RenderOverlay(ResourceId texid, TextureDisplayOverlay overl DebugData.overlayTexWidth = texDetails.width; DebugData.overlayTexHeight = texDetails.height; - gl.glTexStorage2D(eGL_TEXTURE_2D, 1, eGL_RGBA8, texDetails.width, texDetails.height); + gl.glTexStorage2D(eGL_TEXTURE_2D, 1, eGL_SRGB8_ALPHA8, texDetails.width, texDetails.height); gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MIN_FILTER, eGL_NEAREST); gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MAG_FILTER, eGL_NEAREST); gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_WRAP_S, eGL_CLAMP_TO_EDGE); diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index b790f6f55..e93574452 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -294,6 +294,7 @@ GLInitParams::GLInitParams() colorBits = 32; depthBits = 32; stencilBits = 8; + isSRGB = 1; width = 32; height = 32; } @@ -308,11 +309,12 @@ ReplayCreateStatus GLInitParams::Serialise() return eReplayCreate_APIIncompatibleVersion; } - SERIALISE_ELEMENT(uint32_t, col, colorBits); colorBits = col; - SERIALISE_ELEMENT(uint32_t, dpth, depthBits); depthBits = dpth; - SERIALISE_ELEMENT(uint32_t, stenc, stencilBits); stencilBits = stenc; - SERIALISE_ELEMENT(uint32_t, w, width); width = w; - SERIALISE_ELEMENT(uint32_t, h, height); height = h; + m_pSerialiser->Serialise("Color bits", colorBits); + m_pSerialiser->Serialise("Depth bits", depthBits); + m_pSerialiser->Serialise("Stencil bits", stencilBits); + m_pSerialiser->Serialise("Is SRGB", isSRGB); + m_pSerialiser->Serialise("Width", width); + m_pSerialiser->Serialise("Height", height); return eReplayCreate_Success; } @@ -466,9 +468,9 @@ void WrappedOpenGL::Initialise(GLInitParams ¶ms) GLenum colfmt = eGL_RGBA8; if(params.colorBits == 32) - colfmt = eGL_RGBA8; + colfmt = params.isSRGB ? eGL_SRGB8_ALPHA8 : eGL_RGBA8; else if(params.colorBits == 24) - colfmt = eGL_RGB8; + colfmt = params.isSRGB ? eGL_SRGB8 : eGL_RGB8; else RDCERR("Unexpected # colour bits: %d", params.colorBits); diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index 118f53a46..dc6199a98 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -50,10 +50,11 @@ struct GLInitParams : public RDCInitParams uint32_t colorBits; uint32_t depthBits; uint32_t stencilBits; + uint32_t isSRGB; uint32_t width; uint32_t height; - static const uint32_t GL_SERIALISE_VERSION = 0x0000002; + static const uint32_t GL_SERIALISE_VERSION = 0x0000003; // version number internal to opengl stream uint32_t SerialiseVersion; diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index e30f67c20..2f7728aaf 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -151,7 +151,7 @@ void GLReplay::CreateOutputWindowBackbuffer(OutputWindow &outwin) gl.glGenTextures(1, &outwin.BlitData.backbuffer); gl.glBindTexture(eGL_TEXTURE_2D, outwin.BlitData.backbuffer); - gl.glTexStorage2D(eGL_TEXTURE_2D, 1, eGL_RGB8, outwin.width, outwin.height); + gl.glTexStorage2D(eGL_TEXTURE_2D, 1, eGL_SRGB8, outwin.width, outwin.height); gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MIN_FILTER, eGL_NEAREST); gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_MAG_FILTER, eGL_NEAREST); gl.glTexParameteri(eGL_TEXTURE_2D, eGL_TEXTURE_WRAP_S, eGL_CLAMP_TO_EDGE); @@ -264,6 +264,7 @@ void GLReplay::FlipOutputWindow(uint64_t id) gl.glActiveTexture(eGL_TEXTURE0); gl.glBindTexture(eGL_TEXTURE_2D, outw.BlitData.backbuffer); + gl.glEnable(eGL_FRAMEBUFFER_SRGB); gl.glBindVertexArray(outw.BlitData.emptyVAO); gl.glDrawArrays(eGL_TRIANGLE_STRIP, 0, 4); diff --git a/renderdoc/driver/gl/gl_replay_linux.cpp b/renderdoc/driver/gl/gl_replay_linux.cpp index f6aafa60a..dbff22047 100644 --- a/renderdoc/driver/gl/gl_replay_linux.cpp +++ b/renderdoc/driver/gl/gl_replay_linux.cpp @@ -98,6 +98,7 @@ uint64_t GLReplay::MakeOutputWindow(void *wn, bool depth) GLX_BLUE_SIZE, 8, GLX_ALPHA_SIZE, 8, GLX_DOUBLEBUFFER, True, + GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, True, 0 }; int numCfgs = 0; diff --git a/renderdoc/driver/gl/gl_replay_win32.cpp b/renderdoc/driver/gl/gl_replay_win32.cpp index d0a6173fc..b76dca879 100644 --- a/renderdoc/driver/gl/gl_replay_win32.cpp +++ b/renderdoc/driver/gl/gl_replay_win32.cpp @@ -27,8 +27,8 @@ #include "gl_driver.h" #include "gl_resources.h" -PIXELFORMATDESCRIPTOR pfd = { 0 }; PFNWGLCREATECONTEXTATTRIBSARBPROC createContextAttribs = NULL; +PFNWGLGETPIXELFORMATATTRIBIVARBPROC getPixelFormatAttrib = NULL; typedef PROC (WINAPI *WGLGETPROCADDRESSPROC)(const char*); typedef HGLRC (WINAPI *WGLCREATECONTEXTPROC)(HDC); @@ -77,8 +77,57 @@ uint64_t GLReplay::MakeOutputWindow(void *wn, bool depth) NULL, NULL, GetModuleHandle(NULL), NULL); HDC DC = GetDC(w); + + PIXELFORMATDESCRIPTOR pfd = { 0 }; + + int attrib = eWGL_NUMBER_PIXEL_FORMATS_ARB; + int value = 1; + + getPixelFormatAttrib(DC, 1, 0, 1, &attrib, &value); + + int pf = 0; + + int numpfs = value; + for(int i=1; i <= numpfs; i++) + { + // verify that we have the properties we want + attrib = eWGL_DRAW_TO_WINDOW_ARB; + getPixelFormatAttrib(DC, i, 0, 1, &attrib, &value); + if(value == 0) continue; + + attrib = eWGL_ACCELERATION_ARB; + getPixelFormatAttrib(DC, i, 0, 1, &attrib, &value); + if(value == eWGL_NO_ACCELERATION_ARB) continue; + + attrib = eWGL_SUPPORT_OPENGL_ARB; + getPixelFormatAttrib(DC, i, 0, 1, &attrib, &value); + if(value == 0) continue; + + attrib = eWGL_DOUBLE_BUFFER_ARB; + getPixelFormatAttrib(DC, i, 0, 1, &attrib, &value); + if(value == 0) continue; + + attrib = eWGL_PIXEL_TYPE_ARB; + getPixelFormatAttrib(DC, i, 0, 1, &attrib, &value); + if(value != eWGL_TYPE_RGBA_ARB) continue; + + // we have an opengl-capable accelerated RGBA context. + // we use internal framebuffers to do almost all rendering, so we just need + // RGB (color bits > 24) and SRGB buffer. + + attrib = eWGL_COLOR_BITS_ARB; + getPixelFormatAttrib(DC, i, 0, 1, &attrib, &value); + if(value < 24) continue; + + attrib = WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB; + getPixelFormatAttrib(DC, i, 0, 1, &attrib, &value); + if(value == 0) continue; + + // this one suits our needs, choose it + pf = i; + break; + } - int pf = ChoosePixelFormat(DC, &pfd); if(pf == 0) { ReleaseDC(w, DC); @@ -86,7 +135,15 @@ uint64_t GLReplay::MakeOutputWindow(void *wn, bool depth) return NULL; } - BOOL res = SetPixelFormat(DC, pf, &pfd); + BOOL res = DescribePixelFormat(DC, pf, sizeof(pfd), &pfd); + if(res == FALSE) + { + ReleaseDC(w, DC); + RDCERR("Couldn't describe pixel format"); + return NULL; + } + + res = SetPixelFormat(DC, pf, &pfd); if(res == FALSE) { ReleaseDC(w, DC); @@ -193,6 +250,8 @@ ReplayCreateStatus GL_CreateReplayDevice(const char *logfile, IReplayDriver **dr return eReplayCreate_APIIncompatibleVersion; } + PIXELFORMATDESCRIPTOR pfd = { 0 }; + if(wglGetProc == NULL) { wglGetProc = (WGLGETPROCADDRESSPROC)GetProcAddress(lib, "wglGetProcAddress"); @@ -267,11 +326,12 @@ ReplayCreateStatus GL_CreateReplayDevice(const char *logfile, IReplayDriver **dr return eReplayCreate_APIInitFailed; } - createContextAttribs = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProc( "wglCreateContextAttribsARB" ); + createContextAttribs = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProc("wglCreateContextAttribsARB"); + getPixelFormatAttrib = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)wglGetProc("wglGetPixelFormatAttribivARB"); - if(createContextAttribs == NULL) + if(createContextAttribs == NULL || getPixelFormatAttrib == NULL) { - RDCERR("RenderDoc requires WGL_ARB_create_context"); + RDCERR("RenderDoc requires WGL_ARB_create_context and WGL_ARB_pixel_format"); return eReplayCreate_APIHardwareUnsupported; } diff --git a/renderdoc/driver/gl/gl_resources.cpp b/renderdoc/driver/gl/gl_resources.cpp index 15da907b8..7eaba01d7 100644 --- a/renderdoc/driver/gl/gl_resources.cpp +++ b/renderdoc/driver/gl/gl_resources.cpp @@ -532,6 +532,20 @@ bool IsSIntFormat(GLenum internalFormat) return false; } +bool IsSRGBFormat(GLenum internalFormat) +{ + switch(internalFormat) + { + case eGL_SRGB8: + case eGL_SRGB8_ALPHA8: + return true; + default: + break; + } + + return false; +} + GLenum TextureBinding(GLenum target) { switch(target) diff --git a/renderdoc/driver/gl/gl_resources.h b/renderdoc/driver/gl/gl_resources.h index d4cc06bab..6d6cf535c 100644 --- a/renderdoc/driver/gl/gl_resources.h +++ b/renderdoc/driver/gl/gl_resources.h @@ -42,6 +42,7 @@ bool IsCompressedFormat(GLenum internalFormat); bool IsDepthStencilFormat(GLenum internalFormat); bool IsUIntFormat(GLenum internalFormat); bool IsSIntFormat(GLenum internalFormat); +bool IsSRGBFormat(GLenum internalFormat); GLenum TextureBinding(GLenum target); GLenum TextureTarget(GLenum target); diff --git a/renderdoc/hooks/gl_linux_hooks.cpp b/renderdoc/hooks/gl_linux_hooks.cpp index e96cdd88f..c349b1caf 100644 --- a/renderdoc/hooks/gl_linux_hooks.cpp +++ b/renderdoc/hooks/gl_linux_hooks.cpp @@ -344,6 +344,8 @@ GLXContext glXCreateContext(Display *dpy, XVisualInfo *vis, GLXContext shareList OpenGLHook::glhooks.glXGetConfig_real(dpy, vis, GLX_BUFFER_SIZE, &value); init.colorBits = value; OpenGLHook::glhooks.glXGetConfig_real(dpy, vis, GLX_DEPTH_SIZE, &value); init.depthBits = value; OpenGLHook::glhooks.glXGetConfig_real(dpy, vis, GLX_STENCIL_SIZE, &value); init.stencilBits = value; + value = 1; // default to srgb + OpenGLHook::glhooks.glXGetConfig_real(dpy, vis, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB, &value); init.isSRGB = value; OpenGLHook::glhooks.GetDriver()->CreateContext(NULL, ret, shareList, init); diff --git a/renderdoc/hooks/gl_win32_hooks.cpp b/renderdoc/hooks/gl_win32_hooks.cpp index c246366df..6ce96636d 100644 --- a/renderdoc/hooks/gl_win32_hooks.cpp +++ b/renderdoc/hooks/gl_win32_hooks.cpp @@ -298,6 +298,19 @@ class OpenGLHook : LibraryHook ret.width = (r.right-r.left); ret.height = (r.bottom-r.top); + ret.isSRGB = true; + + if(glhooks.wglGetPixelFormatAttribivARB_realfunc == NULL) + glhooks.wglGetProcAddress_hooked("wglGetPixelFormatAttribivARB"); + + if(glhooks.wglGetPixelFormatAttribivARB_realfunc) + { + int attrname = eWGL_FRAMEBUFFER_SRGB_CAPABLE_ARB; + int srgb = 1; + glhooks.wglGetPixelFormatAttribivARB_realfunc(dc, pf, 0, 1, &attrname, &srgb); + ret.isSRGB = srgb; + } + if(pfd.iPixelType != PFD_TYPE_RGBA) { RDCERR("Unsupported OpenGL pixel type");