From d44a00c8988207013842557e9725df2756397d80 Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 4 Apr 2019 11:49:30 +0100 Subject: [PATCH] When replaying specific GL extension functions, check availability * In theory we should check everywhere on every function otherwise we crash with a NULL pointer, but figuring out which functions to check is non-trivial in some cases and there's the risk of false positives with fallbacks available etc in cases where there are several possible alternatives for a given function. * These checks have been sprinkled around in places where it's safe because there's only one implementation of a function that never made it to core, as well as the most common problems - glFramebufferTexture2DMultisampleEXT and glFramebufferTextureMultiviewOVR/glFramebufferTextureMultisampleMultiviewOVR which comes from users mistakenly replaying a mobile capture on desktop. This is not expected to work but it shouldn't crash. --- renderdoc/driver/gl/gl_common.h | 10 ++++++++++ renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp | 16 ++++++++++++++++ .../driver/gl/wrappers/gl_framebuffer_funcs.cpp | 12 ++++++++++++ .../driver/gl/wrappers/gl_interop_funcs.cpp | 12 ++++++++++++ renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp | 8 ++++++++ renderdoc/driver/gl/wrappers/gl_state_funcs.cpp | 8 ++++++++ .../driver/gl/wrappers/gl_texture_funcs.cpp | 2 ++ 7 files changed, 68 insertions(+) diff --git a/renderdoc/driver/gl/gl_common.h b/renderdoc/driver/gl/gl_common.h index f2128f15b..3d9474ea8 100644 --- a/renderdoc/driver/gl/gl_common.h +++ b/renderdoc/driver/gl/gl_common.h @@ -428,6 +428,16 @@ T CheckConstParam(T t); // Writing is unambiguously during capture mode, so we don't have to check both in that case. #define IsReplayingAndReading() (ser.IsReading() && IsReplayMode(m_State)) +// on GL we don't have an easy way of checking which functions/extensions were used or which +// functions/extensions on replay could suffice. So we check at the last minute on replay and bail +// out if it's not present +#define CheckReplayFunctionPresent(func) \ + if(func == NULL) \ + { \ + m_FailedReplayStatus = ReplayStatus::APIHardwareUnsupported; \ + return false; \ + } + // no longer in glcorearb.h or glext.h const GLenum eGL_LIGHTING = (GLenum)0x0B50; const GLenum eGL_ALPHA_TEST = (GLenum)0x0BC0; diff --git a/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp index e1dbcdd14..55f19e22f 100644 --- a/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_draw_funcs.cpp @@ -308,6 +308,8 @@ bool WrappedOpenGL::Serialise_glDispatchComputeGroupSizeARB(SerialiserType &ser, if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glDispatchComputeGroupSizeARB); + GL.glDispatchComputeGroupSizeARB(num_groups_x, num_groups_y, num_groups_z, group_size_x, group_size_y, group_size_z); @@ -408,6 +410,8 @@ bool WrappedOpenGL::Serialise_glDispatchComputeIndirect(SerialiserType &ser, GLi if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glDispatchComputeIndirect); + GL.glDispatchComputeIndirect((GLintptr)offset); if(IsLoading(m_State)) @@ -1094,6 +1098,8 @@ bool WrappedOpenGL::Serialise_glDrawArraysIndirect(SerialiserType &ser, GLenum m if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glDrawArraysIndirect); + if(Check_SafeDraw(false)) GL.glDrawArraysIndirect(mode, (const void *)offset); @@ -1397,6 +1403,8 @@ bool WrappedOpenGL::Serialise_glDrawElementsIndirect(SerialiserType &ser, GLenum if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glDrawElementsIndirect); + if(Check_SafeDraw(true)) GL.glDrawElementsIndirect(mode, type, (const void *)offset); @@ -2529,6 +2537,8 @@ bool WrappedOpenGL::Serialise_glMultiDrawArraysIndirect(SerialiserType &ser, GLe { if(IsLoading(m_State)) { + CheckReplayFunctionPresent(GL.glMultiDrawArraysIndirect); + if(Check_SafeDraw(false)) GL.glMultiDrawArraysIndirect(mode, (const void *)offset, drawcount, stride); @@ -2743,6 +2753,8 @@ bool WrappedOpenGL::Serialise_glMultiDrawElementsIndirect(SerialiserType &ser, G { if(IsLoading(m_State)) { + CheckReplayFunctionPresent(GL.glMultiDrawElementsIndirect); + GLRenderState state; state.FetchState(this); @@ -2970,6 +2982,8 @@ bool WrappedOpenGL::Serialise_glMultiDrawArraysIndirectCount(SerialiserType &ser if(IsLoading(m_State)) { + CheckReplayFunctionPresent(GL.glMultiDrawArraysIndirectCount); + if(Check_SafeDraw(false)) GL.glMultiDrawArraysIndirectCount(mode, (const void *)offset, (GLintptr)drawcount, maxdrawcount, stride); @@ -3196,6 +3210,8 @@ bool WrappedOpenGL::Serialise_glMultiDrawElementsIndirectCount(SerialiserType &s if(IsLoading(m_State)) { + CheckReplayFunctionPresent(GL.glMultiDrawElementsIndirectCount); + if(Check_SafeDraw(true)) GL.glMultiDrawElementsIndirectCount(mode, type, (const void *)offset, (GLintptr)drawcount, maxdrawcount, stride); diff --git a/renderdoc/driver/gl/wrappers/gl_framebuffer_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_framebuffer_funcs.cpp index f279072bf..c09a99d6a 100644 --- a/renderdoc/driver/gl/wrappers/gl_framebuffer_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_framebuffer_funcs.cpp @@ -599,6 +599,8 @@ bool WrappedOpenGL::Serialise_glFramebufferTexture2DMultisampleEXT( if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glFramebufferTexture2DMultisampleEXT); + if(framebuffer.name == 0) framebuffer.name = m_CurrentDefaultFBO; @@ -1137,6 +1139,8 @@ bool WrappedOpenGL::Serialise_glFramebufferTextureMultiviewOVR(SerialiserType &s if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glFramebufferTextureMultiviewOVR); + GL.glFramebufferTextureMultiviewOVR(target, attachment, texture.name, level, baseViewIndex, numViews); @@ -1253,6 +1257,8 @@ bool WrappedOpenGL::Serialise_glFramebufferTextureMultisampleMultiviewOVR( if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glFramebufferTextureMultisampleMultiviewOVR); + GL.glFramebufferTextureMultisampleMultiviewOVR(target, attachment, texture.name, level, samples, baseViewIndex, numViews); @@ -2403,6 +2409,10 @@ bool WrappedOpenGL::Serialise_glNamedRenderbufferStorageMultisampleEXT(Serialise if(IsReplayingAndReading()) { + // the DSA function is emulated if not present, but we need to check the underlying function is + // present + CheckReplayFunctionPresent(GL.glRenderbufferStorageMultisample); + ResourceId liveId = GetResourceManager()->GetID(renderbuffer); TextureData &texDetails = m_Textures[liveId]; @@ -2596,6 +2606,8 @@ bool WrappedOpenGL::Serialise_glRenderbufferStorageMultisampleEXT(SerialiserType if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glRenderbufferStorageMultisampleEXT); + ResourceId liveId = GetResourceManager()->GetID(renderbuffer); TextureData &texDetails = m_Textures[liveId]; diff --git a/renderdoc/driver/gl/wrappers/gl_interop_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_interop_funcs.cpp index 341b3f7bf..658906cae 100644 --- a/renderdoc/driver/gl/wrappers/gl_interop_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_interop_funcs.cpp @@ -541,6 +541,8 @@ bool WrappedOpenGL::Serialise_glCreateMemoryObjectsEXT(SerialiserType &ser, GLsi if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glCreateMemoryObjectsEXT); + GLuint real = 0; GL.glCreateMemoryObjectsEXT(1, &real); @@ -621,6 +623,8 @@ bool WrappedOpenGL::Serialise_glMemoryObjectParameterivEXT(SerialiserType &ser, if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glMemoryObjectParameterivEXT); + GL.glMemoryObjectParameterivEXT(memoryObject.name, pname, params); AddResourceInitChunk(memoryObject); @@ -812,6 +816,8 @@ bool WrappedOpenGL::Serialise_glGenSemaphoresEXT(SerialiserType &ser, GLsizei n, if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glGenSemaphoresEXT); + GLuint real = 0; GL.glGenSemaphoresEXT(1, &real); @@ -892,6 +898,8 @@ bool WrappedOpenGL::Serialise_glSemaphoreParameterui64vEXT(SerialiserType &ser, if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glSemaphoreParameterui64vEXT); + GL.glSemaphoreParameterui64vEXT(semaphore.name, pname, params); AddResourceInitChunk(semaphore); @@ -1633,6 +1641,10 @@ bool WrappedOpenGL::Serialise_glTextureStorageMem2DMultisampleEXT( if(IsReplayingAndReading()) { + // the DSA function is emulated if not present, but we need to check the underlying function is + // present + CheckReplayFunctionPresent(GL.glTexStorage2DMultisample); + // Replay external texture storage backed by external memory as just a plain texture. ResourceId liveId = GetResourceManager()->GetID(texture); m_Textures[liveId].width = width; diff --git a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp index 9fe4b870d..ba13e79ce 100644 --- a/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_shader_funcs.cpp @@ -1755,6 +1755,8 @@ bool WrappedOpenGL::Serialise_glCompileShaderIncludeARB(SerialiserType &ser, GLu if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glCompileShaderIncludeARB); + ResourceId liveId = GetResourceManager()->GetID(shader); auto &shadDetails = m_Shaders[liveId]; @@ -1828,6 +1830,8 @@ bool WrappedOpenGL::Serialise_glNamedStringARB(SerialiserType &ser, GLenum type, if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glNamedStringARB); + GL.glNamedStringARB(type, (GLint)name.length(), name.c_str(), (GLint)value.length(), value.c_str()); } @@ -1865,6 +1869,8 @@ bool WrappedOpenGL::Serialise_glDeleteNamedStringARB(SerialiserType &ser, GLint if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glDeleteNamedStringARB); + GL.glDeleteNamedStringARB((GLint)name.length(), name.c_str()); } @@ -1913,6 +1919,8 @@ bool WrappedOpenGL::Serialise_glSpecializeShader(SerialiserType &ser, GLuint sha if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glSpecializeShader); + ResourceId liveId = GetResourceManager()->GetID(shader); GL.glSpecializeShader(shader.name, pEntryPoint, numSpecializationConstants, pConstantIndex, diff --git a/renderdoc/driver/gl/wrappers/gl_state_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_state_funcs.cpp index b13b0e852..985186af7 100644 --- a/renderdoc/driver/gl/wrappers/gl_state_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_state_funcs.cpp @@ -923,6 +923,8 @@ bool WrappedOpenGL::Serialise_glDepthBoundsEXT(SerialiserType &ser, GLclampd nea if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glDepthBoundsEXT); + GL.glDepthBoundsEXT(nearVal, farVal); } @@ -1418,6 +1420,8 @@ bool WrappedOpenGL::Serialise_glRasterSamplesEXT(SerialiserType &ser, GLuint sam if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glRasterSamplesEXT); + GL.glRasterSamplesEXT(samples, fixedsamplelocations ? GL_TRUE : GL_FALSE); } @@ -1915,6 +1919,8 @@ bool WrappedOpenGL::Serialise_glPolygonOffsetClamp(SerialiserType &ser, GLfloat if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glPolygonOffsetClamp); + GL.glPolygonOffsetClamp(factor, units, clamp); } @@ -1953,6 +1959,8 @@ bool WrappedOpenGL::Serialise_glPrimitiveBoundingBox(SerialiserType &ser, GLfloa if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glPrimitiveBoundingBox); + GL.glPrimitiveBoundingBox(minX, minY, minZ, minW, maxX, maxY, maxZ, maxW); } diff --git a/renderdoc/driver/gl/wrappers/gl_texture_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_texture_funcs.cpp index 4f0845a82..1f76d41dc 100644 --- a/renderdoc/driver/gl/wrappers/gl_texture_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_texture_funcs.cpp @@ -6352,6 +6352,8 @@ bool WrappedOpenGL::Serialise_glTextureFoveationParametersQCOM(SerialiserType &s if(IsReplayingAndReading()) { + CheckReplayFunctionPresent(GL.glTextureFoveationParametersQCOM); + GL.glTextureFoveationParametersQCOM(texture.name, layer, focalPoint, focalX, focalY, gainX, gainY, foveaArea);