mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-09-24 22:55:41 +00:00
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.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user