mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-06 10:00:40 +00:00
Handle GLES stupid limitations of color-rendering properly in emulation
* GLES doesn't handle rendering to formats like a normal API - it has an odd list of exceptions and extensions to check. If we don't have support for blitting in our texture copy emulation we fall back to CPU read and upload. * We also can't use framebuffer blits for copying MSAA textures so we have to fall back to clearing to black. Emulating this would require a shader write with sample shading of some kind which is not worthwhile since this emulation path is only hit for initial contents where MSAA texture initial contents are very unlikely to be needed.
This commit is contained in:
@@ -1556,97 +1556,131 @@ GLuint GetBoundVertexBuffer(GLuint i)
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void SafeBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0,
|
||||
GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
|
||||
struct SafeClearBlitState
|
||||
{
|
||||
// only viewport 0's scissor should be used for blits, but Intel seems to require all scissors to
|
||||
// be disabled. Since it's only a bit more work to disable them all, we push/pop all of them
|
||||
SafeClearBlitState()
|
||||
{
|
||||
// only viewport 0's scissor should be used for blits, but Intel seems to require all scissors
|
||||
// to be disabled. Since it's only a bit more work to disable them all, we push/pop all of them
|
||||
GL.glGetIntegerv(eGL_MAX_VIEWPORTS, &maxViews);
|
||||
|
||||
// fetch current state
|
||||
{
|
||||
if(HasExt[ARB_viewport_array])
|
||||
{
|
||||
for(GLint v = 0; v < maxViews; v++)
|
||||
scissorEnabled[v] = GL.glIsEnabledi(eGL_SCISSOR_TEST, v) != 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
scissorEnabled[0] = GL.glIsEnabled(eGL_SCISSOR_TEST) != 0;
|
||||
}
|
||||
|
||||
if(HasExt[EXT_draw_buffers2] || HasExt[ARB_draw_buffers_blend])
|
||||
GL.glGetBooleani_v(eGL_COLOR_WRITEMASK, 0, ColorMask);
|
||||
else
|
||||
GL.glGetBooleanv(eGL_COLOR_WRITEMASK, ColorMask);
|
||||
|
||||
GL.glGetBooleanv(eGL_DEPTH_WRITEMASK, &DepthMask);
|
||||
|
||||
GL.glGetIntegerv(eGL_STENCIL_WRITEMASK, &StencilMask);
|
||||
GL.glGetIntegerv(eGL_STENCIL_BACK_WRITEMASK, &StencilBackMask);
|
||||
}
|
||||
|
||||
GL.glGetFloatv(eGL_COLOR_CLEAR_VALUE, ClearColor);
|
||||
GL.glGetFloatv(eGL_DEPTH_CLEAR_VALUE, &ClearDepth);
|
||||
GL.glGetIntegerv(eGL_STENCIL_CLEAR_VALUE, &ClearStencil);
|
||||
|
||||
// apply safe state
|
||||
{
|
||||
if(HasExt[ARB_viewport_array])
|
||||
{
|
||||
for(GLint v = 0; v < maxViews; v++)
|
||||
GL.glDisablei(eGL_SCISSOR_TEST, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL.glDisable(eGL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
if(HasExt[EXT_draw_buffers2] || HasExt[ARB_draw_buffers_blend])
|
||||
GL.glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
else
|
||||
GL.glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
|
||||
GL.glDepthMask(GL_TRUE);
|
||||
|
||||
GL.glStencilMaskSeparate(eGL_FRONT, 0xff);
|
||||
GL.glStencilMaskSeparate(eGL_BACK, 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
~SafeClearBlitState()
|
||||
{
|
||||
// restore original state
|
||||
{
|
||||
if(HasExt[ARB_viewport_array])
|
||||
{
|
||||
for(GLint v = 0; v < maxViews; v++)
|
||||
{
|
||||
if(scissorEnabled[v])
|
||||
GL.glEnablei(eGL_SCISSOR_TEST, v);
|
||||
else
|
||||
GL.glDisablei(eGL_SCISSOR_TEST, v);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(scissorEnabled[0])
|
||||
GL.glEnable(eGL_SCISSOR_TEST);
|
||||
else
|
||||
GL.glDisable(eGL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
if(HasExt[EXT_draw_buffers2] || HasExt[ARB_draw_buffers_blend])
|
||||
GL.glColorMaski(0, ColorMask[0], ColorMask[1], ColorMask[2], ColorMask[3]);
|
||||
else
|
||||
GL.glColorMask(ColorMask[0], ColorMask[1], ColorMask[2], ColorMask[3]);
|
||||
|
||||
GL.glDepthMask(DepthMask);
|
||||
|
||||
GL.glStencilMaskSeparate(eGL_FRONT, StencilMask);
|
||||
GL.glStencilMaskSeparate(eGL_BACK, StencilBackMask);
|
||||
}
|
||||
|
||||
GL.glClearColor(ClearColor[0], ClearColor[1], ClearColor[2], ClearColor[3]);
|
||||
GL.glClearDepthf(ClearDepth);
|
||||
GL.glClearStencil(ClearStencil);
|
||||
}
|
||||
|
||||
bool scissorEnabled[16] = {};
|
||||
GLboolean ColorMask[4] = {GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE};
|
||||
GLboolean DepthMask = GL_TRUE;
|
||||
GLint StencilMask = 0xff, StencilBackMask = 0xff;
|
||||
|
||||
GLfloat ClearColor[4] = {};
|
||||
GLfloat ClearDepth = 1.0f;
|
||||
GLint ClearStencil = 0;
|
||||
|
||||
GLint maxViews = 0;
|
||||
GL.glGetIntegerv(eGL_MAX_VIEWPORTS, &maxViews);
|
||||
};
|
||||
|
||||
// fetch current state
|
||||
{
|
||||
if(HasExt[ARB_viewport_array])
|
||||
{
|
||||
for(GLint v = 0; v < maxViews; v++)
|
||||
scissorEnabled[v] = GL.glIsEnabledi(eGL_SCISSOR_TEST, v) != 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
scissorEnabled[0] = GL.glIsEnabled(eGL_SCISSOR_TEST) != 0;
|
||||
}
|
||||
|
||||
if(HasExt[EXT_draw_buffers2] || HasExt[ARB_draw_buffers_blend])
|
||||
GL.glGetBooleani_v(eGL_COLOR_WRITEMASK, 0, ColorMask);
|
||||
else
|
||||
GL.glGetBooleanv(eGL_COLOR_WRITEMASK, ColorMask);
|
||||
|
||||
GL.glGetBooleanv(eGL_DEPTH_WRITEMASK, &DepthMask);
|
||||
|
||||
GL.glGetIntegerv(eGL_STENCIL_WRITEMASK, &StencilMask);
|
||||
GL.glGetIntegerv(eGL_STENCIL_BACK_WRITEMASK, &StencilBackMask);
|
||||
}
|
||||
|
||||
// apply safe state
|
||||
{
|
||||
if(HasExt[ARB_viewport_array])
|
||||
{
|
||||
for(GLint v = 0; v < maxViews; v++)
|
||||
GL.glDisablei(eGL_SCISSOR_TEST, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
GL.glDisable(eGL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
if(HasExt[EXT_draw_buffers2] || HasExt[ARB_draw_buffers_blend])
|
||||
GL.glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
else
|
||||
GL.glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
|
||||
GL.glDepthMask(GL_TRUE);
|
||||
|
||||
GL.glStencilMaskSeparate(eGL_FRONT, 0xff);
|
||||
GL.glStencilMaskSeparate(eGL_BACK, 0xff);
|
||||
}
|
||||
void SafeBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0,
|
||||
GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)
|
||||
{
|
||||
SafeClearBlitState safe;
|
||||
|
||||
GL.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
|
||||
}
|
||||
|
||||
// restore original state
|
||||
{
|
||||
if(HasExt[ARB_viewport_array])
|
||||
{
|
||||
for(GLint v = 0; v < maxViews; v++)
|
||||
{
|
||||
if(scissorEnabled[v])
|
||||
GL.glEnablei(eGL_SCISSOR_TEST, v);
|
||||
else
|
||||
GL.glDisablei(eGL_SCISSOR_TEST, v);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(scissorEnabled[0])
|
||||
GL.glEnable(eGL_SCISSOR_TEST);
|
||||
else
|
||||
GL.glDisable(eGL_SCISSOR_TEST);
|
||||
}
|
||||
void SafeClearFramebuffer(GLbitfield clearMask, GLfloat rgba[4], GLfloat depth, GLint stencil)
|
||||
{
|
||||
SafeClearBlitState safe;
|
||||
|
||||
if(HasExt[EXT_draw_buffers2] || HasExt[ARB_draw_buffers_blend])
|
||||
GL.glColorMaski(0, ColorMask[0], ColorMask[1], ColorMask[2], ColorMask[3]);
|
||||
else
|
||||
GL.glColorMask(ColorMask[0], ColorMask[1], ColorMask[2], ColorMask[3]);
|
||||
|
||||
GL.glDepthMask(DepthMask);
|
||||
|
||||
GL.glStencilMaskSeparate(eGL_FRONT, StencilMask);
|
||||
GL.glStencilMaskSeparate(eGL_BACK, StencilBackMask);
|
||||
}
|
||||
GL.glClearColor(rgba[0], rgba[1], rgba[2], rgba[3]);
|
||||
GL.glClearDepthf(0.0f);
|
||||
GL.glClearStencil(0);
|
||||
GL.glClear(clearMask);
|
||||
}
|
||||
|
||||
BufferCategory MakeBufferCategory(GLenum bufferTarget)
|
||||
|
||||
@@ -630,6 +630,7 @@ void GetCurrentBinding(GLuint curProg, ShaderReflection *refl, const ConstantBlo
|
||||
// pops state for only a single drawbuffer!
|
||||
void SafeBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0,
|
||||
GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
|
||||
void SafeClearFramebuffer(GLbitfield clearMask, GLfloat rgba[4], GLfloat depth, GLint stencil);
|
||||
|
||||
enum UniformType
|
||||
{
|
||||
@@ -790,6 +791,8 @@ extern bool IsGLES;
|
||||
EXT_TO_CHECK(99, 99, EXT_texture_buffer) \
|
||||
/* OpenGL ES extensions */ \
|
||||
EXT_TO_CHECK(99, 32, EXT_color_buffer_float) \
|
||||
EXT_TO_CHECK(99, 99, EXT_color_buffer_half_float) \
|
||||
EXT_TO_CHECK(99, 99, EXT_render_snorm) \
|
||||
EXT_TO_CHECK(99, 32, EXT_primitive_bounding_box) \
|
||||
EXT_TO_CHECK(99, 32, OES_primitive_bounding_box) \
|
||||
EXT_TO_CHECK(99, 32, OES_texture_border_color) \
|
||||
|
||||
@@ -1187,6 +1187,7 @@ void APIENTRY _glCopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLev
|
||||
GLenum attach = eGL_COLOR_ATTACHMENT0;
|
||||
|
||||
bool layered = false;
|
||||
bool msaa = false;
|
||||
bool cpuTransfer = false;
|
||||
|
||||
if(srcTarget == eGL_TEXTURE_CUBE_MAP || srcTarget == eGL_TEXTURE_CUBE_MAP_ARRAY ||
|
||||
@@ -1206,8 +1207,87 @@ void APIENTRY _glCopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLev
|
||||
GLenum fmt = eGL_NONE;
|
||||
GL.glGetTexLevelParameteriv(levelQueryType, 0, eGL_TEXTURE_INTERNAL_FORMAT, (GLint *)&fmt);
|
||||
|
||||
bool colorRenderable = true;
|
||||
|
||||
if(IsCompressedFormat(fmt))
|
||||
colorRenderable = false;
|
||||
|
||||
if(fmt == eGL_RGB9_E5)
|
||||
colorRenderable = false;
|
||||
|
||||
if(colorRenderable && IsGLES)
|
||||
{
|
||||
// before GLES 3 framebuffers basically weren't supported for the lack of formats you could
|
||||
// use. We can turn some on based on extensions below
|
||||
if(GLCoreVersion < 30)
|
||||
colorRenderable = false;
|
||||
|
||||
// GLES randomly excludes an inconsistent set of formats
|
||||
switch(fmt)
|
||||
{
|
||||
// case eGL_RGB8: // valid?!
|
||||
case eGL_SRGB8:
|
||||
case eGL_RGB8I:
|
||||
case eGL_RGB8UI:
|
||||
case eGL_RGB8_SNORM:
|
||||
|
||||
case eGL_RGB9_E5:
|
||||
|
||||
case eGL_RGB10:
|
||||
|
||||
case eGL_RGB16I:
|
||||
case eGL_RGB16UI:
|
||||
|
||||
case eGL_RGB32F:
|
||||
case eGL_RGB32I:
|
||||
case eGL_RGB32UI: colorRenderable = false; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
// SRGB 1- and 2- channel formats aren't renderable
|
||||
if(fmt == eGL_SR8_EXT || fmt == eGL_SRG8_EXT)
|
||||
colorRenderable = false;
|
||||
|
||||
// some extensions are required for other formats
|
||||
switch(fmt)
|
||||
{
|
||||
case eGL_R8_SNORM:
|
||||
case eGL_RG8_SNORM:
|
||||
case eGL_RGBA8_SNORM: colorRenderable = HasExt[EXT_render_snorm];
|
||||
|
||||
case eGL_R16_SNORM:
|
||||
case eGL_RG16_SNORM:
|
||||
case eGL_RGBA16_SNORM: colorRenderable = HasExt[EXT_render_snorm];
|
||||
|
||||
case eGL_R16F:
|
||||
case eGL_RG16F:
|
||||
case eGL_RGBA16F:
|
||||
colorRenderable = HasExt[EXT_color_buffer_half_float] || HasExt[EXT_color_buffer_float];
|
||||
|
||||
case eGL_RGB16F: colorRenderable = HasExt[EXT_color_buffer_half_float];
|
||||
|
||||
case eGL_R32F:
|
||||
case eGL_RG32F:
|
||||
case eGL_RGBA32F:
|
||||
case eGL_R11F_G11F_B10F: colorRenderable = HasExt[EXT_color_buffer_float];
|
||||
|
||||
case eGL_RGB10_A2:
|
||||
case eGL_RGB10_A2UI: colorRenderable = (GLCoreVersion >= 30);
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
// can't blit or CPU-read MSAA textures. we treat these as 'color-renderable' but we will just clear to black
|
||||
if(srcTarget == eGL_TEXTURE_2D_MULTISAMPLE || srcTarget == eGL_TEXTURE_2D_MULTISAMPLE_ARRAY)
|
||||
{
|
||||
colorRenderable = true;
|
||||
msaa = true;
|
||||
RDCDEBUG("Can't support image copy emulation on MSAA images, clearing to black");
|
||||
}
|
||||
|
||||
// non-color-renderable formats have to go through this path even though they are not compressed
|
||||
if(IsCompressedFormat(fmt) || fmt == eGL_RGB9_E5)
|
||||
if(!colorRenderable)
|
||||
{
|
||||
// have to do this via CPU readback, there's no alternative for GPU copies
|
||||
cpuTransfer = true;
|
||||
@@ -1332,6 +1412,8 @@ void APIENTRY _glCopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLev
|
||||
}
|
||||
}
|
||||
|
||||
GLfloat black[4] = {};
|
||||
|
||||
if(cpuTransfer)
|
||||
{
|
||||
// nothing to do!
|
||||
@@ -1348,8 +1430,16 @@ void APIENTRY _glCopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLev
|
||||
if(status != eGL_FRAMEBUFFER_COMPLETE)
|
||||
RDCERR("glCopyImageSubData emulation read FBO is %s", ToStr(status).c_str());
|
||||
|
||||
SafeBlitFramebuffer(srcX, srcY, srcX + srcWidth, srcY + srcHeight, dstX, dstY,
|
||||
dstX + srcWidth, dstY + srcHeight, mask, eGL_NEAREST);
|
||||
if(msaa)
|
||||
{
|
||||
SafeClearFramebuffer(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT,
|
||||
black, 0.0f, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SafeBlitFramebuffer(srcX, srcY, srcX + srcWidth, srcY + srcHeight, dstX, dstY,
|
||||
dstX + srcWidth, dstY + srcHeight, mask, eGL_NEAREST);
|
||||
}
|
||||
}
|
||||
else if(srcTarget == eGL_TEXTURE_CUBE_MAP)
|
||||
{
|
||||
@@ -1381,8 +1471,16 @@ void APIENTRY _glCopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLev
|
||||
RDCERR("glCopyImageSubData emulation read FBO is %s for slice 0", ToStr(status).c_str());
|
||||
}
|
||||
|
||||
SafeBlitFramebuffer(srcX, srcY, srcX + srcWidth, srcY + srcHeight, dstX, dstY,
|
||||
dstX + srcWidth, dstY + srcHeight, mask, eGL_NEAREST);
|
||||
if(msaa)
|
||||
{
|
||||
SafeClearFramebuffer(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT,
|
||||
black, 0.0f, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SafeBlitFramebuffer(srcX, srcY, srcX + srcWidth, srcY + srcHeight, dstX, dstY,
|
||||
dstX + srcWidth, dstY + srcHeight, mask, eGL_NEAREST);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1405,8 +1503,16 @@ void APIENTRY _glCopyImageSubData(GLuint srcName, GLenum srcTarget, GLint srcLev
|
||||
RDCERR("glCopyImageSubData emulation read FBO is %s for slice 0", ToStr(status).c_str());
|
||||
}
|
||||
|
||||
SafeBlitFramebuffer(srcX, srcY, srcX + srcWidth, srcY + srcHeight, dstX, dstY,
|
||||
dstX + srcWidth, dstY + srcHeight, mask, eGL_NEAREST);
|
||||
if(msaa)
|
||||
{
|
||||
SafeClearFramebuffer(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT,
|
||||
black, 0.0f, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SafeBlitFramebuffer(srcX, srcY, srcX + srcWidth, srcY + srcHeight, dstX, dstY,
|
||||
dstX + srcWidth, dstY + srcHeight, mask, eGL_NEAREST);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user