diff --git a/docs/behind_scenes/opengl_support.rst b/docs/behind_scenes/opengl_support.rst index 6b76cfaad..77a311bd2 100644 --- a/docs/behind_scenes/opengl_support.rst +++ b/docs/behind_scenes/opengl_support.rst @@ -49,6 +49,8 @@ Android OpenGL ES capture and replay on Android is natively supported. For more information on how to capture with Android see :doc:`../how/how_android_capture`. +On many drivers debugging vertex shaders will not be possible due to lack of driver feature support. + OS X ---- diff --git a/renderdoc/data/glsl/debug_math.comp b/renderdoc/data/glsl/debug_math.comp index 04678651c..ca3f4628a 100644 --- a/renderdoc/data/glsl/debug_math.comp +++ b/renderdoc/data/glsl/debug_math.comp @@ -22,18 +22,20 @@ * THE SOFTWARE. ******************************************************************************/ +#if defined(OPENGL_CORE) #extension GL_ARB_compute_shader : require #extension GL_ARB_shader_storage_buffer_object : require // safe to assume this extension in compute shaders as it pre-dates compute shaders #extension GL_ARB_shading_language_420pack : require +#endif #include "glsl_ubos.h" uniform vec4 a; uniform vec4 b; uniform vec4 c; -uniform uint op; +uniform int op; layout(binding = 0, std430) buffer Output { diff --git a/renderdoc/data/glsl/debug_sample.frag b/renderdoc/data/glsl/debug_sample.frag index e2aabb5d0..bff7afb00 100644 --- a/renderdoc/data/glsl/debug_sample.frag +++ b/renderdoc/data/glsl/debug_sample.frag @@ -24,6 +24,10 @@ #define DEBUGSAMPLE_UBO +#ifdef OPENGL_ES +precision highp float; +#endif + #include "glsl_ubos.h" #if UINT_TEX @@ -31,16 +35,18 @@ #define RESULT uvec4 #define FLOAT_CONV(x) floatBitsToUint(x) -uniform usampler1D tex1D; -uniform usampler2D tex2D; -uniform usampler3D tex3D; -uniform usampler1DArray tex1DArray; -uniform usampler2DArray tex2DArray; -uniform usampler2DRect tex2DRect; -uniform usamplerBuffer texBuffer; +uniform PRECISION usampler2D tex2D; +uniform PRECISION usampler3D tex3D; +uniform PRECISION usampler2DArray tex2DArray; +uniform PRECISION usamplerBuffer texBuffer; #ifdef TEXSAMPLE_MULTISAMPLE -uniform usampler2DMS tex2DMS; -uniform usampler2DMSArray tex2DMSArray; +uniform PRECISION usampler2DMS tex2DMS; +uniform PRECISION usampler2DMSArray tex2DMSArray; +#endif +#ifdef OPENGL_CORE +uniform PRECISION usampler1D tex1D; +uniform PRECISION usampler1DArray tex1DArray; +uniform PRECISION usampler2DRect tex2DRect; #endif #elif SINT_TEX @@ -48,16 +54,18 @@ uniform usampler2DMSArray tex2DMSArray; #define RESULT ivec4 #define FLOAT_CONV(x) floatBitsToInt(x) -uniform isampler1D tex1D; -uniform isampler2D tex2D; -uniform isampler3D tex3D; -uniform isampler1DArray tex1DArray; -uniform isampler2DArray tex2DArray; -uniform isampler2DRect tex2DRect; -uniform isamplerBuffer texBuffer; +uniform PRECISION isampler2D tex2D; +uniform PRECISION isampler3D tex3D; +uniform PRECISION isampler2DArray tex2DArray; +uniform PRECISION isamplerBuffer texBuffer; #ifdef TEXSAMPLE_MULTISAMPLE -uniform isampler2DMS tex2DMS; -uniform isampler2DMSArray tex2DMSArray; +uniform PRECISION isampler2DMS tex2DMS; +uniform PRECISION isampler2DMSArray tex2DMSArray; +#endif +#ifdef OPENGL_CORE +uniform PRECISION isampler1D tex1D; +uniform PRECISION isampler1DArray tex1DArray; +uniform PRECISION isampler2DRect tex2DRect; #endif #else @@ -65,31 +73,35 @@ uniform isampler2DMSArray tex2DMSArray; #define RESULT vec4 #define FLOAT_CONV(x) x -uniform sampler1D tex1D; -uniform sampler2D tex2D; -uniform sampler3D tex3D; -uniform samplerCube texCube; -uniform sampler1DArray tex1DArray; -uniform sampler2DArray tex2DArray; +uniform PRECISION sampler2D tex2D; +uniform PRECISION sampler3D tex3D; +uniform PRECISION samplerCube texCube; +uniform PRECISION sampler2DArray tex2DArray; #ifdef TEXSAMPLE_CUBE_ARRAY -uniform samplerCubeArray texCubeArray; +uniform PRECISION samplerCubeArray texCubeArray; #endif -uniform sampler2DRect tex2DRect; -uniform samplerBuffer texBuffer; +uniform PRECISION samplerBuffer texBuffer; #ifdef TEXSAMPLE_MULTISAMPLE -uniform sampler2DMS tex2DMS; -uniform sampler2DMSArray tex2DMSArray; +uniform PRECISION sampler2DMS tex2DMS; +uniform PRECISION sampler2DMSArray tex2DMSArray; +#endif +#ifdef OPENGL_CORE +uniform PRECISION sampler1D tex1D; +uniform PRECISION sampler1DArray tex1DArray; +uniform PRECISION sampler2DRect tex2DRect; #endif -uniform sampler1DShadow tex1DShadow; -uniform sampler2DShadow tex2DShadow; -uniform samplerCubeShadow texCubeShadow; -uniform sampler1DArrayShadow tex1DArrayShadow; -uniform sampler2DArrayShadow tex2DArrayShadow; +uniform PRECISION sampler2DShadow tex2DShadow; +uniform PRECISION samplerCubeShadow texCubeShadow; +uniform PRECISION sampler2DArrayShadow tex2DArrayShadow; #ifdef TEXSAMPLE_CUBE_ARRAY -uniform samplerCubeArrayShadow texCubeArrayShadow; +uniform PRECISION samplerCubeArrayShadow texCubeArrayShadow; +#endif +#ifdef OPENGL_CORE +uniform PRECISION sampler1DShadow tex1DShadow; +uniform PRECISION sampler1DArrayShadow tex1DArrayShadow; +uniform PRECISION sampler2DRectShadow tex2DRectShadow; #endif -uniform sampler2DRectShadow tex2DRectShadow; #endif @@ -113,7 +125,11 @@ const ivec3 fetch_offset = ivec3(0, 0, 0); RESULT DoFetch1D() { +#ifdef OPENGL_CORE return texelFetchOffset(tex1D, debugsample.texel_uvw.x, debugsample.texel_lod, fetch_offset.x); +#else + return RESULT(0, 0, 0, 0); +#endif } RESULT DoFetch2D() @@ -134,8 +150,12 @@ RESULT DoFetchCube() RESULT DoFetch1DArray() { +#ifdef OPENGL_CORE return texelFetchOffset(tex1DArray, debugsample.texel_uvw.xy, debugsample.texel_lod, fetch_offset.x); +#else + return RESULT(0, 0, 0, 0); +#endif } RESULT DoFetch2DArray() @@ -152,7 +172,11 @@ RESULT DoFetchCubeArray() RESULT DoFetch2DRect() { +#ifdef OPENGL_CORE return texelFetchOffset(tex2DRect, debugsample.texel_uvw.xy, fetch_offset.xy); +#else + return RESULT(0, 0, 0, 0); +#endif } RESULT DoFetchBuffer() @@ -178,6 +202,7 @@ RESULT DoFetch2DMSArray() /////////////////////////////////// // OpImageQueryLod +#ifdef OPENGL_CORE RESULT DoQueryLod1D() { return RESULT(FLOAT_CONV(textureQueryLod(tex1D, input_uvwa.x)), 0, 0); @@ -224,13 +249,16 @@ RESULT DoQueryLodCubeArray() #endif } #endif +#endif /////////////////////////////////// // OpImageSampleExplicitLod (ImplicitLod is upgraded to this) RESULT DoSample1D() { -#if ENABLE_MINLOD && USE_GRAD +#ifdef OPENGL_ES + return RESULT(0, 0, 0, 0); +#elif ENABLE_MINLOD && USE_GRAD return textureGradOffsetClampARB(tex1D, debugsample.uvwa.x, debugsample.ddx_uvw.x, debugsample.ddy_uvw.x, fetch_offset.x, debugsample.minlod); #elif USE_GRAD @@ -290,7 +318,9 @@ RESULT DoSampleCube() RESULT DoSample1DArray() { -#if ENABLE_MINLOD && USE_GRAD +#ifdef OPENGL_ES + return RESULT(0, 0, 0, 0); +#elif ENABLE_MINLOD && USE_GRAD return textureGradOffsetClampARB(tex1DArray, debugsample.uvwa.xy, debugsample.ddx_uvw.x, debugsample.ddy_uvw.x, fetch_offset.x, debugsample.minlod); #elif USE_GRAD @@ -340,8 +370,12 @@ RESULT DoSampleCubeArray() RESULT DoSample2DRect() { +#ifdef OPENGL_ES + return RESULT(0, 0, 0, 0); +#else // fairly degenerate, no lod or grad in use. minlod is illegal return textureOffset(tex2DRect, debugsample.uvwa.xy, fetch_offset.xy); +#endif } /////////////////////////////////// @@ -351,7 +385,9 @@ RESULT DoSampleDref1D() { #if FLOAT_TEX -#if ENABLE_MINLOD && USE_GRAD +#ifdef OPENGL_ES + return RESULT(0, 0, 0, 0); +#elif ENABLE_MINLOD && USE_GRAD return vec4(textureGradOffsetClampARB( tex1DShadow, vec3(debugsample.uvwa.x, 0, debugsample.compare), debugsample.ddx_uvw.x, debugsample.ddy_uvw.x, fetch_offset.x, debugsample.minlod), @@ -429,7 +465,9 @@ RESULT DoSampleDref1DArray() { #if FLOAT_TEX -#if ENABLE_MINLOD && USE_GRAD +#ifdef OPENGL_ES + return RESULT(0, 0, 0, 0); +#elif ENABLE_MINLOD && USE_GRAD return vec4(textureGradOffsetClampARB( tex1DArrayShadow, vec3(debugsample.uvwa.xy, debugsample.compare), debugsample.ddx_uvw.x, debugsample.ddy_uvw.x, fetch_offset.x, debugsample.minlod), @@ -503,7 +541,9 @@ RESULT DoSampleDrefCubeArray() RESULT DoSampleDref2DRect() { -#if FLOAT_TEX +#ifdef OPENGL_ES + return RESULT(0, 0, 0, 0); +#elif FLOAT_TEX // fairly degenerate, no lod or grad in use. minlod is illegal return vec4(textureOffset(tex2DRectShadow, vec3(debugsample.uvwa.xy, debugsample.compare), fetch_offset.xy), @@ -590,7 +630,9 @@ RESULT DoGatherCubeArray() RESULT DoGather2DRect() { -#if GATHER_SUPPORT == 0 +#ifdef OPENGL_ES + return RESULT(0, 0, 0, 0); +#elif GATHER_SUPPORT == 0 // no gather support at all return RESULT(0, 0, 0, 0); #elif GATHER_SUPPORT == 1 @@ -692,7 +734,9 @@ RESULT DoGatherDrefCubeArray() RESULT DoGatherDref2DRect() { -#if !FLOAT_TEX +#ifdef OPENGL_ES + return RESULT(0, 0, 0, 0); +#elif !FLOAT_TEX // shadow samplers only for FLOAT_TEX return RESULT(0, 0, 0, 0); #elif GATHER_SUPPORT == 0 diff --git a/renderdoc/driver/gl/gl_debug.cpp b/renderdoc/driver/gl/gl_debug.cpp index c92a99d5d..eab48c3bf 100644 --- a/renderdoc/driver/gl/gl_debug.cpp +++ b/renderdoc/driver/gl/gl_debug.cpp @@ -1416,7 +1416,7 @@ GLuint GLReplay::MakeShaderDebugSampleProg(const SamplingProgramConfig &config) { glslVersion = 450; defines += "#extension GL_ARB_sparse_texture_clamp : require\n"; - defines += "#define USE_MINLOD 1\n"; + defines += "#define ENABLE_MINLOD 1\n"; } if(HasExt[ARB_gpu_shader5] && HasExt[ARB_texture_gather]) @@ -1435,6 +1435,22 @@ GLuint GLReplay::MakeShaderDebugSampleProg(const SamplingProgramConfig &config) defines += "#define GATHER_SUPPORT 0\n"; } } + else + { + defines += "#define ENABLE_MINLOD 0\n"; + if(HasExt[ARB_gpu_shader5] && HasExt[ARB_texture_gather]) + { + defines += "#define GATHER_SUPPORT 2\n"; + } + else if(HasExt[ARB_texture_gather]) + { + defines += "#define GATHER_SUPPORT 1\n"; + } + else + { + defines += "#define GATHER_SUPPORT 0\n"; + } + } rdcstr vs = GenerateGLSLShader(GetEmbeddedResource(glsl_shaderdebug_sample_vert), shaderType, glslCSVer); diff --git a/renderdoc/driver/gl/gl_shaderdebug.cpp b/renderdoc/driver/gl/gl_shaderdebug.cpp index fe9c0b2c0..df29c0308 100644 --- a/renderdoc/driver/gl/gl_shaderdebug.cpp +++ b/renderdoc/driver/gl/gl_shaderdebug.cpp @@ -1336,7 +1336,7 @@ public: } // push the operation afterwards - GL.glUniform1ui(GL.glGetUniformLocation(mathProg, "op"), (uint32_t)op); + GL.glUniform1i(GL.glGetUniformLocation(mathProg, "op"), (int32_t)op); GL.glDispatchCompute(1, 1, 1); @@ -2005,12 +2005,12 @@ static GLuint CreateInputFetcher(const WrappedOpenGL::ShaderData &shadDetails, source += "#define USE_SPIRV 1\n"; source += StringFormat::Fmt( - "#define VALID_MAGIC %u\n" + "#define VALID_MAGIC %uu\n" "#define STAGE_VS %u\n" "#define STAGE_PS %u\n" "#define STAGE_CS %u\n" "#define STAGE %u\n" - "#define MAXHIT %u\n" + "#define MAXHIT %uu\n" "#define STORAGE_BINDING %u\n" "#define NUMLANES %u\n" "#define USEPRIM %u\n" @@ -2069,17 +2069,26 @@ static GLuint CreateInputFetcher(const WrappedOpenGL::ShaderData &shadDetails, } source += R"EOSHADER( -#extension GL_ARB_shader_storage_buffer_object : require + +#ifdef OPENGL_ES +precision highp float; +#endif + +#ifdef OPENGL_CORE + #extension GL_ARB_shader_storage_buffer_object : require +#endif #if PROPER_DERIVS #extension GL_ARB_derivative_control : require #endif +#ifdef OPENGL_CORE #if HELPER && !USE_SPIRV // required for gl_HelperInvocation, but don't enable with glslang due to a bug - // we compile at a high enough core version to satisfy the requirement that way #extension GL_ARB_ES3_1_compatibility : require #endif +#endif #if SUBGROUP_BASIC #extension GL_KHR_shader_subgroup_basic : require @@ -2098,8 +2107,8 @@ static GLuint CreateInputFetcher(const WrappedOpenGL::ShaderData &shadDetails, #endif // bool signature elements get reflected as ints, make macros for their access to cast to int -#define gl_FrontFacing (gl_FrontFacing ? 1 : 0) -#define gl_HelperInvocation (gl_HelperInvocation ? 1 : 0) +#define gl_FrontFacing (gl_FrontFacing ? 1u : 0u) +#define gl_HelperInvocation (gl_HelperInvocation ? 1u : 0u) )EOSHADER"; @@ -2303,8 +2312,8 @@ void SetInputs(out Inputs inputs) {} #if STAGE == STAGE_VS struct VSLaneData { - uint inst; - uint vert; + int inst; + int vert; uint view; uint pad; }; @@ -2359,8 +2368,8 @@ struct ResultData { vec4 pos; - uint prim; - uint rd_sample; + int prim; + int rd_sample; uint view; uint valid; @@ -2382,7 +2391,7 @@ struct ResultData LaneData laneData[NUMLANES]; }; -#if USE_SPIRV +#if USE_SPIRV || defined(OPENGL_ES) layout(binding = STORAGE_BINDING) #endif layout(std140) buffer Output @@ -2430,13 +2439,13 @@ layout(std140) buffer Output void main() { vec4 debug_pixelPos = vec4(0,0,0,0); - uint primitive = 0; - uint rd_sample = 0; - uint isFrontFace = 0; + int primitive = 0; + int rd_sample = 0; + uint isFrontFace = 0u; #if STAGE == STAGE_VS - uint vert = gl_VertexID; - uint inst = gl_InstanceID; + int vert = gl_VertexID; + int inst = gl_InstanceID; #elif STAGE == STAGE_PS debug_pixelPos = gl_FragCoord; @@ -2454,27 +2463,27 @@ void main() #if STAGE == STAGE_VS VSLaneData vs; - vs.pad = 0; + vs.pad = 0u; #elif STAGE == STAGE_PS PSLaneData ps; - ps.pad = 0; + ps.pad = 0u; #else CSLaneData cs; #endif #if SUBGROUP_BASIC SubgroupLaneData sub; - sub.elect = subgroupElect() ? 1 : 0; - sub.rd_active = 1; + sub.elect = subgroupElect() ? 1u : 0u; + sub.rd_active = 1u; #endif - uint isHelper = 0; - uint quadLaneIndex = 0; - uint quadId = 0; - uint laneIndex = 0; - uvec4 globalBallot = uvec4(0,0,0,0); - uvec4 electBallot = uvec4(0,0,0,0); - uvec4 helperBallot = uvec4(0,0,0,0); + uint isHelper = 0u; + uint quadLaneIndex = 0u; + uint quadId = 0u; + uint laneIndex = 0u; + uvec4 globalBallot = uvec4(0u,0u,0u,0u); + uvec4 electBallot = uvec4(0u,0u,0u,0u); + uvec4 helperBallot = uvec4(0u,0u,0u,0u); float derivValid = 1.0f; quadLaneIndex = (2u * (uint(debug_pixelPos.y) & 1u)) + (uint(debug_pixelPos.x) & 1u); @@ -2515,7 +2524,7 @@ void main() // quadId is a single value that's unique for this quad and uniform across the quad. Degenerate // for the simple quad case - quadId = 1000+quadSwizzleHelper(laneIndex, quadLaneIndex, 0u); + quadId = 1000u+quadSwizzleHelper(laneIndex, quadLaneIndex, 0u); LaneData helper0data; LaneData helper1data; @@ -2545,7 +2554,7 @@ void main() for(size_t i = 0; i < floatInputs.size(); i++) { source += StringFormat::Fmt( - " helper%udata.inputs.%s = quadSwizzleHelper(%s, quadLaneIndex, %u);\n", q, + " helper%udata.inputs.%s = quadSwizzleHelper(%s, quadLaneIndex, %uu);\n", q, floatInputs[i].first.c_str(), floatInputs[i].second.c_str(), q); } if(!nonfloatInputs.empty()) @@ -2554,7 +2563,7 @@ void main() for(size_t i = 0; i < nonfloatInputs.size(); i++) { source += StringFormat::Fmt( - " helper%udata.inputs.%s = quadSwizzleHelper(%s, quadLaneIndex, %u);\n", q, + " helper%udata.inputs.%s = quadSwizzleHelper(%s, quadLaneIndex, %uu);\n", q, nonfloatInputs[i].first.c_str(), nonfloatInputs[i].second.c_str(), q); } source += "#else\n"; @@ -2595,7 +2604,7 @@ void main() if(activeSubgroup) { - if(isHelper == 0) + if(isHelper == 0u) { uint idx = MAXHIT; #if SUBGROUP_BALLOT @@ -2624,8 +2633,8 @@ void main() outbuffer.hits[idx].subgroupSize = gl_SubgroupSize; outbuffer.hits[idx].numSubgroups = gl_NumSubgroups; #else - outbuffer.hits[idx].subgroupSize = 0; - outbuffer.hits[idx].numSubgroups = 0; + outbuffer.hits[idx].subgroupSize = 0u; + outbuffer.hits[idx].numSubgroups = 0u; #endif outbuffer.hits[idx].globalBallot = globalBallot; outbuffer.hits[idx].electBallot = electBallot; @@ -2969,7 +2978,23 @@ ShaderDebugTrace *GLReplay::DebugVertex(uint32_t eventId, uint32_t vertid, uint3 GLuint replacementProgram = GL.glCreateProgram(); - // don't attach any other shaders since we don't declare outputs anyway and we don't need them + GLuint fragShader = 0; + + // on GLES we must have a pixel shader, it's not optional. + if(IsGLES) + { + ShaderType shaderType; + int glslVersion; + int glslBaseVer; + int glslCSVer; + GetGLSLVersions(shaderType, glslVersion, glslBaseVer, glslCSVer); + + rdcstr source = + GenerateGLSLShader(GetEmbeddedResource(glsl_fixedcol_frag), shaderType, glslVersion); + fragShader = CreateShader(eGL_FRAGMENT_SHADER, source); + GL.glAttachShader(replacementProgram, fragShader); + } + { GL.glAttachShader(replacementProgram, inputFetcher); @@ -2994,7 +3019,7 @@ ShaderDebugTrace *GLReplay::DebugVertex(uint32_t eventId, uint32_t vertid, uint3 GL.glUseProgram(replacementProgram); - if(shadDetails.spirvWords.empty()) + if(shadDetails.spirvWords.empty() && !IsGLES) { GLuint ssboIdx = GL.glGetProgramResourceIndex(replacementProgram, eGL_SHADER_STORAGE_BLOCK, "Output"); @@ -3006,6 +3031,8 @@ ShaderDebugTrace *GLReplay::DebugVertex(uint32_t eventId, uint32_t vertid, uint3 GL.glDeleteProgram(replacementProgram); GL.glDeleteShader(inputFetcher); + if(fragShader) + GL.glDeleteShader(fragShader); data.resize(feedbackStorageSize); GL.glGetBufferSubData(eGL_SHADER_STORAGE_BUFFER, 0, feedbackStorageSize, data.data()); @@ -3406,7 +3433,7 @@ ShaderDebugTrace *GLReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t y, GL.glUseProgram(replacementProgram); - if(inputShader) + if(inputShader && !IsGLES) { GLuint ssboIdx = GL.glGetProgramResourceIndex(replacementProgram, eGL_SHADER_STORAGE_BLOCK, "Output"); @@ -3862,7 +3889,7 @@ ShaderDebugTrace *GLReplay::DebugThread(uint32_t eventId, const rdcfixedarraydebugInfo.sourceDebugInformation) reflection->debugInfo.compileFlags.flags.push_back({"preferSourceDebug", "1"}); + if(HasExt[ARB_shader_storage_buffer_object]) + { + if(type == eGL_VERTEX_SHADER) + { + GLint numSSBOs = 0; + GL.glGetIntegerv(eGL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &numSSBOs); + + if(numSSBOs == 0) + { + reflection->debugInfo.debuggable = false; + reflection->debugInfo.debugStatus = + "Vertex shader debugging not supported on this driver."; + } + } + else if(type == eGL_FRAGMENT_SHADER) + { + GLint numSSBOs = 0; + GL.glGetIntegerv(eGL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &numSSBOs); + + if(numSSBOs == 0) + { + reflection->debugInfo.debuggable = false; + reflection->debugInfo.debugStatus = + "Fragment shader debugging not supported on this driver."; + } + } + } + // we must ensure the converted reflection & patch data matches the real reflection so // that ShaderBindIndex references are consistent. We could do a manual remapping during // debug to the right index, but ShaderBindIndex's are also baked into variable results.