void GLReplay::InitDebugData()

in renderdoc/driver/gl/gl_debug.cpp [340:1198]


void GLReplay::InitDebugData()
{
  if(m_pDriver == NULL)
    return;

  // don't reflect any shaders or programs we make
  m_pDriver->PushInternalShader();

  m_HighlightCache.driver = m_pDriver->GetReplay();

  RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 0.0f);

  {
    WindowingData window = {WindowingSystem::Headless};
    uint64_t id = MakeOutputWindow(window, true);

    m_DebugCtx = NULL;

    if(id == 0)
      return;

    m_DebugID = id;
    m_DebugCtx = &m_OutputWindows[id];

    MakeCurrentReplayContext(m_DebugCtx);

    m_pDriver->RegisterDebugCallback();
  }

  WrappedOpenGL &drv = *m_pDriver;

  DebugData.outWidth = 0.0f;
  DebugData.outHeight = 0.0f;

  rdcstr vs;
  rdcstr fs;
  rdcstr gs;
  rdcstr cs;

  ShaderType shaderType;
  int glslVersion;
  int glslBaseVer;
  int glslCSVer;    // compute shader

  GetGLSLVersions(shaderType, glslVersion, glslBaseVer, glslCSVer);

  rdcstr texSampleDefines;

  if(IsGLES)
  {
    if(HasExt[OES_texture_cube_map_array] || HasExt[EXT_texture_cube_map_array] || GLCoreVersion >= 32)
      texSampleDefines += "#define TEXSAMPLE_CUBE_ARRAY 1\n";

    if(HasExt[OES_texture_cube_map_array])
      texSampleDefines += "#extension GL_OES_texture_cube_map_array : require\n";

    if(HasExt[EXT_texture_cube_map_array])
      texSampleDefines += "#extension GL_EXT_texture_cube_map_array : require\n";

    if(HasExt[EXT_texture_buffer])
      texSampleDefines +=
          "#define TEXSAMPLE_BUFFER 1\n"
          "#extension GL_EXT_texture_buffer : require\n";

    texSampleDefines += "#define HAS_BIT_CONVERSION 1\n";
  }
  else
  {
    if(HasExt[ARB_texture_cube_map_array])
      texSampleDefines +=
          "#define TEXSAMPLE_CUBE_ARRAY 1\n"
          "#extension GL_ARB_texture_cube_map_array : require\n";

    if(HasExt[ARB_texture_multisample])
      texSampleDefines +=
          "#define TEXSAMPLE_MULTISAMPLE 1\n"
          "#extension GL_ARB_texture_multisample : require\n";

    if(HasExt[ARB_gpu_shader5])
      texSampleDefines +=
          "#define HAS_BIT_CONVERSION 1\n"
          "#extension GL_ARB_gpu_shader5 : require\n";
    else if(HasExt[ARB_shader_bit_encoding])
      texSampleDefines +=
          "#define HAS_BIT_CONVERSION 1\n"
          "#extension GL_ARB_shader_bit_encoding : require\n";
  }

  rdcstr vsDefines = "#define FORCE_IO_LOCATION 1";

  if(!IsGLES)
  {
    vsDefines =
        "#extension GL_ARB_separate_shader_objects : require\n"
        "#extension GL_ARB_explicit_attrib_location : require\n" +
        vsDefines;
  }

  vs = GenerateGLSLShader(GetEmbeddedResource(glsl_blit_vert), shaderType, glslBaseVer, vsDefines);

  // used to combine with custom shaders.
  // this has to have explicit locations on the output even though we don't normally use that,
  // because GL doesn't have a fallback to match by name, and custom shaders are expected to have an
  // explicit location on the input
  DebugData.texDisplayVertexShader = CreateShader(eGL_VERTEX_SHADER, vs);

  vs = GenerateGLSLShader(GetEmbeddedResource(glsl_blit_vert), shaderType, glslBaseVer);
  fs = GenerateGLSLShader(GetEmbeddedResource(glsl_fixedcol_frag), shaderType, glslBaseVer);
  DebugData.fullScreenFixedColProg = CreateShaderProgram(vs, fs);

  fs = GenerateGLSLShader(GetEmbeddedResource(glsl_depth_copy_frag), shaderType, glslBaseVer);
  DebugData.fullScreenCopyDepth = CreateShaderProgram(vs, fs);

  fs = GenerateGLSLShader(GetEmbeddedResource(glsl_depth_copyms_frag), shaderType, glslBaseVer);
  DebugData.fullScreenCopyDepthMS = CreateShaderProgram(vs, fs);

  DebugData.fixedcolFragShaderSPIRV = DebugData.quadoverdrawFragShaderSPIRV = 0;

  // pre-compile SPIR-V shaders up front since this is more expensive
  if(HasExt[ARB_gl_spirv])
  {
    // SPIR-V shaders are always generated as desktop GL 430, for ease
    rdcstr source =
        GenerateGLSLShader(GetEmbeddedResource(glsl_fixedcol_frag), ShaderType::GLSPIRV, 430);
    DebugData.fixedcolFragShaderSPIRV = CreateSPIRVShader(eGL_FRAGMENT_SHADER, source);

    if(HasExt[ARB_gpu_shader5] && HasExt[ARB_shader_image_load_store])
    {
      rdcstr defines = "";

      if(!HasExt[ARB_derivative_control])
      {
        defines += "#define dFdxFine dFdx\n\n";
        defines += "#define dFdyFine dFdy\n\n";
      }

      source = GenerateGLSLShader(GetEmbeddedResource(glsl_quadwrite_frag), ShaderType::GLSPIRV,
                                  430, defines);
      DebugData.quadoverdrawFragShaderSPIRV = CreateSPIRVShader(eGL_FRAGMENT_SHADER, source);
    }
  }

  for(int i = 0; i < 3; i++)
  {
    rdcstr defines = rdcstr("#define SHADER_BASETYPE ") + ToStr(i) + "\n";

    fs = GenerateGLSLShader(GetEmbeddedResource(glsl_texdisplay_frag), shaderType, glslBaseVer,
                            defines + texSampleDefines);

    DebugData.texDisplayProg[i] = CreateShaderProgram(vs, fs);

    BindUBO(DebugData.texDisplayProg[i], "TexDisplayUBOData", 0);
    BindUBO(DebugData.texDisplayProg[i], "HeatmapData", 1);
    ConfigureTexDisplayProgramBindings(DebugData.texDisplayProg[i]);

    fs = GenerateGLSLShader(GetEmbeddedResource(glsl_texremap_frag), shaderType, glslBaseVer,
                            defines + texSampleDefines);

    DebugData.texRemapProg[i] = CreateShaderProgram(vs, fs);

    BindUBO(DebugData.texRemapProg[i], "TexDisplayUBOData", 0);
    ConfigureTexDisplayProgramBindings(DebugData.texRemapProg[i]);
  }

  RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 0.2f);

  if(GLCoreVersion >= 43 && !IsGLES)
  {
    GLint numsl = 0;
    drv.glGetIntegerv(eGL_NUM_SHADING_LANGUAGE_VERSIONS, &numsl);

    for(GLint i = 0; i < numsl; i++)
    {
      const char *sl = (const char *)drv.glGetStringi(eGL_SHADING_LANGUAGE_VERSION, (GLuint)i);

      CheckGLSLVersion(sl, glslVersion);
    }
  }
  else
  {
    const char *sl = (const char *)drv.glGetString(eGL_SHADING_LANGUAGE_VERSION);

    CheckGLSLVersion(sl, glslVersion);
  }

  DebugData.glslVersion = glslVersion;

  RDCLOG("GLSL version %d", glslVersion);

  vs = GenerateGLSLShader(GetEmbeddedResource(glsl_blit_vert), shaderType, glslBaseVer);

  DebugData.fixedcolFragShader = DebugData.quadoverdrawFragShader = 0;
  DebugData.quadoverdrawResolveProg = 0;

  if(IsGLES)
  {
    // quad overdraw not supported on GLES.
    // 1.
    //   dFdx doesn't support uints - potentially workaroundable with float casts, but highly
    //   doubtful GLES compilers will do that properly without exploding.
    // 2.
    //   quad overdraw write shader must be linked with user shaders in program, which requires
    //   matching ESSL version and features required for it aren't exposed as extensions to older
    //   versions but only in core versions.
  }
  else if(HasExt[ARB_shader_image_load_store] && HasExt[ARB_gpu_shader5])
  {
    fs = GenerateGLSLShader(GetEmbeddedResource(glsl_quadresolve_frag), shaderType, glslBaseVer);

    DebugData.quadoverdrawResolveProg = CreateShaderProgram(vs, fs);

    GL.glUseProgram(DebugData.quadoverdrawResolveProg);

    GL.glUniform1i(GL.glGetUniformLocation(DebugData.quadoverdrawResolveProg, "overdrawImage"), 0);
  }
  else
  {
    RDCWARN(
        "GL_ARB_shader_image_load_store/GL_ARB_gpu_shader5 not supported, disabling quad overdraw "
        "feature.");
    m_pDriver->AddDebugMessage(MessageCategory::Portability, MessageSeverity::Medium,
                               MessageSource::RuntimeWarning,
                               "GL_ARB_shader_image_load_store/GL_ARB_gpu_shader5 not supported, "
                               "disabling quad overdraw feature.");
  }

  fs = GenerateGLSLShader(GetEmbeddedResource(glsl_checkerboard_frag), shaderType, glslBaseVer);
  DebugData.checkerProg = CreateShaderProgram(vs, fs);

  BindUBO(DebugData.checkerProg, "CheckerboardUBOData", 0);

  for(size_t intIdx = 0; intIdx < 3; intIdx++)
  {
    for(size_t numViews = 0; numViews < ARRAY_COUNT(DebugData.discardProg[intIdx]); numViews++)
    {
      rdcstr defines;

      defines += rdcstr("#define SHADER_BASETYPE ") + ToStr(intIdx) + "\n";

      if(numViews > 0 && IsGLES && HasExt[OVR_multiview])
        defines = StringFormat::Fmt("#define NUM_VIEWS %zu\n", numViews + 1);

      rdcstr blitvs =
          GenerateGLSLShader(GetEmbeddedResource(glsl_blit_vert), shaderType, glslBaseVer, defines);

      fs = GenerateGLSLShader(GetEmbeddedResource(glsl_discard_frag), shaderType, glslBaseVer,
                              defines);
      DebugData.discardProg[intIdx][numViews] = CreateShaderProgram(blitvs, fs);

      BindUBO(DebugData.discardProg[intIdx][numViews], "DiscardUBOData", 0);

      if(!IsGLES)
      {
        rdcstr name = "col0";
        for(GLuint i = 0; i < 8; i++)
        {
          name[3] = char('0' + i);
          GL.glBindFragDataLocation(DebugData.discardProg[intIdx][numViews], i, name.c_str());
        }
      }
    }
  }

  {
    ResourceFormat fmt;
    fmt.type = ResourceFormatType::Regular;
    fmt.compType = CompType::Float;
    fmt.compByteWidth = 4;
    fmt.compCount = 1;
    bytebuf pattern = GetDiscardPattern(DiscardType::InvalidateCall, fmt, 1, true);
    fmt.compType = CompType::UInt;
    pattern.append(GetDiscardPattern(DiscardType::InvalidateCall, fmt, 1, true));
    drv.glGenBuffers(1, &DebugData.discardPatternBuffer);
    drv.glBindBuffer(eGL_UNIFORM_BUFFER, DebugData.discardPatternBuffer);
    drv.glNamedBufferDataEXT(DebugData.discardPatternBuffer, pattern.size(), pattern.data(),
                             eGL_STATIC_DRAW);
  }

  if(HasExt[ARB_geometry_shader4])
  {
    vs = GenerateGLSLShader(GetEmbeddedResource(glsl_mesh_vert), shaderType, glslBaseVer);
    fs = GenerateGLSLShader(GetEmbeddedResource(glsl_trisize_frag), shaderType, glslBaseVer);
    gs = GenerateGLSLShader(GetEmbeddedResource(glsl_trisize_geom), shaderType, glslBaseVer);

    // create the shaders
    GLuint vsShad = CreateShader(eGL_VERTEX_SHADER, vs);
    GLuint trifsShad = CreateShader(eGL_FRAGMENT_SHADER, fs);
    GLuint gsShad = CreateShader(eGL_GEOMETRY_SHADER, gs);

    DebugData.trisizeProg = CreateMeshProgram(vsShad, trifsShad, gsShad);

    // bind trisize-unique viewport size UBO
    BindUBO(DebugData.trisizeProg, "ViewportSizeUBO", 2);

    GL.glDeleteShader(trifsShad);
    GL.glDeleteShader(gsShad);

    // we have two fragment shaders, one that reads from the vs outputs and one that reads from the
    // gs outputs
    rdcstr vsfs = GenerateGLSLShader(GetEmbeddedResource(glsl_mesh_frag), shaderType, glslBaseVer,
                                     "#define SECONDARY_NAME vsout_secondary\n"
                                     "#define NORM_NAME vsout_norm\n");
    rdcstr gsfs = GenerateGLSLShader(GetEmbeddedResource(glsl_mesh_frag), shaderType, glslBaseVer,
                                     "#define SECONDARY_NAME gsout_secondary\n"
                                     "#define NORM_NAME gsout_norm\n");
    gs = GenerateGLSLShader(GetEmbeddedResource(glsl_mesh_geom), shaderType, glslBaseVer);

    // recreate the shaders
    GLuint vsfsShad = CreateShader(eGL_FRAGMENT_SHADER, vsfs);
    GLuint gsfsShad = CreateShader(eGL_FRAGMENT_SHADER, gsfs);
    gsShad = CreateShader(eGL_GEOMETRY_SHADER, gs);

    DebugData.meshProg[0] = CreateMeshProgram(vsShad, vsfsShad);
    DebugData.meshgsProg[0] = CreateMeshProgram(vsShad, gsfsShad, gsShad);

    if(HasExt[ARB_gpu_shader_fp64] && HasExt[ARB_vertex_attrib_64bit])
    {
      rdcstr extensions =
          "#extension GL_ARB_gpu_shader_fp64 : require\n"
          "#extension GL_ARB_vertex_attrib_64bit : require\n";

      // position only dvec4
      vs = GenerateGLSLShader(GetEmbeddedResource(glsl_mesh_vert), shaderType, glslBaseVer,
                              extensions + "#define POSITION_TYPE dvec4\n");

      // delete old shader and recreate with new source
      GL.glDeleteShader(vsShad);
      vsShad = CreateShader(eGL_VERTEX_SHADER, vs);

      DebugData.meshProg[1] = CreateMeshProgram(vsShad, vsfsShad);
      DebugData.meshgsProg[1] = CreateMeshProgram(vsShad, gsfsShad, gsShad);

      // secondary only dvec4
      vs = GenerateGLSLShader(GetEmbeddedResource(glsl_mesh_vert), shaderType, glslBaseVer,
                              extensions + "#define SECONDARY_TYPE dvec4\n");

      // delete old shader and recreate with new source
      GL.glDeleteShader(vsShad);
      vsShad = CreateShader(eGL_VERTEX_SHADER, vs);

      DebugData.meshProg[2] = CreateMeshProgram(vsShad, vsfsShad);
      DebugData.meshgsProg[2] = CreateMeshProgram(vsShad, gsfsShad, gsShad);

      // both dvec4
      vs = GenerateGLSLShader(GetEmbeddedResource(glsl_mesh_vert), shaderType, glslBaseVer,
                              extensions +
                                  "#define POSITION_TYPE dvec4\n"
                                  "#define SECONDARY_TYPE dvec4\n");

      // delete old shader and recreate with new source
      GL.glDeleteShader(vsShad);
      vsShad = CreateShader(eGL_VERTEX_SHADER, vs);

      DebugData.meshProg[3] = CreateMeshProgram(vsShad, vsfsShad);
      DebugData.meshgsProg[3] = CreateMeshProgram(vsShad, gsfsShad, gsShad);
    }
    else
    {
      // we don't warn about the lack of double support, assuming that if the driver doesn't support
      // it then it's highly unlikely that the capture uses it.
      DebugData.meshProg[1] = DebugData.meshProg[2] = DebugData.meshProg[3] = 0;
      DebugData.meshgsProg[1] = DebugData.meshgsProg[2] = DebugData.meshgsProg[3] = 0;
    }

    GL.glDeleteShader(vsShad);
    GL.glDeleteShader(vsfsShad);
    GL.glDeleteShader(gsfsShad);
    GL.glDeleteShader(gsShad);
  }
  else
  {
    vs = GenerateGLSLShader(GetEmbeddedResource(glsl_mesh_vert), shaderType, glslBaseVer);

    // without a geometry shader, the fragment shader always reads from vs outputs
    fs = GenerateGLSLShader(GetEmbeddedResource(glsl_mesh_frag), shaderType, glslBaseVer,
                            "#define SECONDARY_NAME vsout_secondary\n"
                            "#define NORM_NAME vsout_norm\n");

    // create the shaders
    GLuint vsShad = CreateShader(eGL_VERTEX_SHADER, vs);
    GLuint fsShad = CreateShader(eGL_FRAGMENT_SHADER, fs);

    DebugData.meshProg[0] = CreateMeshProgram(vsShad, fsShad);
    RDCEraseEl(DebugData.meshgsProg);
    DebugData.trisizeProg = 0;

    const char *warning_msg =
        "GL_ARB_geometry_shader4/GL_EXT_geometry_shader not supported, disabling triangle size and "
        "lit solid shading feature.";
    RDCWARN(warning_msg);
    m_pDriver->AddDebugMessage(MessageCategory::Portability, MessageSeverity::Medium,
                               MessageSource::RuntimeWarning, warning_msg);

    if(HasExt[ARB_gpu_shader_fp64] && HasExt[ARB_vertex_attrib_64bit])
    {
      rdcstr extensions =
          "#extension GL_ARB_gpu_shader_fp64 : require\n"
          "#extension GL_ARB_vertex_attrib_64bit : require\n";

      // position only dvec4
      vs = GenerateGLSLShader(GetEmbeddedResource(glsl_mesh_vert), shaderType, glslBaseVer,
                              extensions + "#define POSITION_TYPE dvec4");

      // delete old shader and recreate with new source
      GL.glDeleteShader(vsShad);
      vsShad = CreateShader(eGL_VERTEX_SHADER, vs);

      DebugData.meshProg[1] = CreateMeshProgram(vsShad, fsShad);

      // secondary only dvec4
      vs = GenerateGLSLShader(GetEmbeddedResource(glsl_mesh_vert), shaderType, glslBaseVer,
                              extensions + "#define SECONDARY_TYPE dvec4");

      // delete old shader and recreate with new source
      GL.glDeleteShader(vsShad);
      vsShad = CreateShader(eGL_VERTEX_SHADER, vs);

      DebugData.meshProg[2] = CreateMeshProgram(vsShad, fsShad);

      // both dvec4
      vs = GenerateGLSLShader(GetEmbeddedResource(glsl_mesh_vert), shaderType, glslBaseVer,
                              extensions +
                                  "#define POSITION_TYPE dvec4\n"
                                  "#define SECONDARY_TYPE dvec4");

      // delete old shader and recreate with new source
      GL.glDeleteShader(vsShad);
      vsShad = CreateShader(eGL_VERTEX_SHADER, vs);

      DebugData.meshProg[3] = CreateMeshProgram(vsShad, fsShad);
    }
    else
    {
      // we don't warn about the lack of double support, assuming that if the driver doesn't support
      // it then it's highly unlikely that the capture uses it.
      DebugData.meshProg[1] = DebugData.meshProg[2] = DebugData.meshProg[3] = 0;
    }

    GL.glDeleteShader(vsShad);
    GL.glDeleteShader(fsShad);
  }

  RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 0.4f);

  drv.glGenBuffers(ARRAY_COUNT(DebugData.UBOs), DebugData.UBOs);
  for(size_t i = 0; i < ARRAY_COUNT(DebugData.UBOs); i++)
  {
    drv.glBindBuffer(eGL_UNIFORM_BUFFER, DebugData.UBOs[i]);
    drv.glNamedBufferDataEXT(DebugData.UBOs[i], 2048, NULL, eGL_DYNAMIC_DRAW);
    RDCCOMPILE_ASSERT(sizeof(TexDisplayUBOData) <= 2048, "UBO too small");
    RDCCOMPILE_ASSERT(sizeof(FontUBOData) <= 2048, "UBO too small");
    RDCCOMPILE_ASSERT(sizeof(HistogramUBOData) <= 2048, "UBO too small");
  }

  DebugData.overlayTexWidth = DebugData.overlayTexHeight = DebugData.overlayTexSamples = 0;
  DebugData.overlayTex = DebugData.overlayFBO = 0;

  DebugData.overlayProg = 0;

  drv.glGenFramebuffers(1, &DebugData.customFBO);
  drv.glBindFramebuffer(eGL_FRAMEBUFFER, DebugData.customFBO);
  DebugData.customTex = 0;

  drv.glGenFramebuffers(1, &DebugData.pickPixelFBO);
  drv.glBindFramebuffer(eGL_FRAMEBUFFER, DebugData.pickPixelFBO);

  if(HasExt[ARB_texture_buffer_object])
  {
    drv.glGenBuffers(1, &DebugData.dummyTexBufferStore);
    drv.glBindBuffer(eGL_TEXTURE_BUFFER, DebugData.dummyTexBufferStore);
    drv.glNamedBufferDataEXT(DebugData.dummyTexBufferStore, 32, NULL, eGL_STATIC_DRAW);
    drv.glBindBuffer(eGL_TEXTURE_BUFFER, 0);

    drv.glGenTextures(1, &DebugData.dummyTexBuffer);
    drv.glBindTexture(eGL_TEXTURE_BUFFER, DebugData.dummyTexBuffer);
    drv.glTextureBufferEXT(DebugData.dummyTexBuffer, eGL_TEXTURE_BUFFER, eGL_RGBA32F,
                           DebugData.dummyTexBufferStore);
    drv.glBindTexture(eGL_TEXTURE_BUFFER, 0);
  }
  else
  {
    DebugData.dummyTexBuffer = DebugData.dummyTexBufferStore = 0;
  }

  drv.glGenTextures(1, &DebugData.pickPixelTex);
  drv.glBindTexture(eGL_TEXTURE_2D, DebugData.pickPixelTex);

  drv.glTextureImage2DEXT(DebugData.pickPixelTex, eGL_TEXTURE_2D, 0, eGL_RGBA32F, 1, 1, 0, eGL_RGBA,
                          eGL_FLOAT, NULL);
  drv.glTextureParameteriEXT(DebugData.pickPixelTex, eGL_TEXTURE_2D, eGL_TEXTURE_MAX_LEVEL, 0);
  drv.glTextureParameteriEXT(DebugData.pickPixelTex, eGL_TEXTURE_2D, eGL_TEXTURE_MIN_FILTER,
                             eGL_NEAREST);
  drv.glTextureParameteriEXT(DebugData.pickPixelTex, eGL_TEXTURE_2D, eGL_TEXTURE_MAG_FILTER,
                             eGL_NEAREST);
  drv.glTextureParameteriEXT(DebugData.pickPixelTex, eGL_TEXTURE_2D, eGL_TEXTURE_WRAP_S,
                             eGL_CLAMP_TO_EDGE);
  drv.glTextureParameteriEXT(DebugData.pickPixelTex, eGL_TEXTURE_2D, eGL_TEXTURE_WRAP_T,
                             eGL_CLAMP_TO_EDGE);
  drv.glFramebufferTexture2D(eGL_FRAMEBUFFER, eGL_COLOR_ATTACHMENT0, eGL_TEXTURE_2D,
                             DebugData.pickPixelTex, 0);

  drv.glGenVertexArrays(1, &DebugData.emptyVAO);
  drv.glBindVertexArray(DebugData.emptyVAO);

  RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 0.6f);

  // histogram/minmax data
  {
    RDCEraseEl(DebugData.minmaxTileProgram);
    RDCEraseEl(DebugData.histogramProgram);
    RDCEraseEl(DebugData.minmaxResultProgram);

    RDCCOMPILE_ASSERT(
        ARRAY_COUNT(DebugData.minmaxTileProgram) >= (TEXDISPLAY_SINT_TEX | TEXDISPLAY_TYPEMASK) + 1,
        "not enough programs");

    if(HasExt[ARB_compute_shader] && HasExt[ARB_shader_storage_buffer_object] &&
       HasExt[ARB_shading_language_420pack])
    {
      for(int t = 1; t <= RESTYPE_TEXTYPEMAX; t++)
      {
        // float, uint, sint
        for(int i = 0; i < 3; i++)
        {
          int idx = t;
          if(i == 1)
            idx |= TEXDISPLAY_UINT_TEX;
          if(i == 2)
            idx |= TEXDISPLAY_SINT_TEX;

          {
            rdcstr defines;
            defines += rdcstr("#define SHADER_RESTYPE ") + ToStr(t) + "\n";
            defines += rdcstr("#define SHADER_BASETYPE ") + ToStr(i) + "\n";
            defines += texSampleDefines;

            cs = GenerateGLSLShader(GetEmbeddedResource(glsl_minmaxtile_comp), shaderType,
                                    glslCSVer, defines);

            DebugData.minmaxTileProgram[idx] = CreateCShaderProgram(cs);

            BindUBO(DebugData.minmaxTileProgram[idx], "HistogramUBOData", 2);
            ConfigureTexDisplayProgramBindings(DebugData.minmaxTileProgram[idx]);
          }

          {
            rdcstr defines;
            defines += rdcstr("#define SHADER_RESTYPE ") + ToStr(t) + "\n";
            defines += rdcstr("#define SHADER_BASETYPE ") + ToStr(i) + "\n";
            defines += texSampleDefines;

            cs = GenerateGLSLShader(GetEmbeddedResource(glsl_histogram_comp), shaderType, glslCSVer,
                                    defines);

            DebugData.histogramProgram[idx] = CreateCShaderProgram(cs);

            BindUBO(DebugData.histogramProgram[idx], "HistogramUBOData", 2);
            ConfigureTexDisplayProgramBindings(DebugData.histogramProgram[idx]);
          }

          if(t == 1)
          {
            rdcstr defines;
            defines += rdcstr("#define SHADER_RESTYPE ") + ToStr(t) + "\n";
            defines += rdcstr("#define SHADER_BASETYPE ") + ToStr(i) + "\n";

            cs = GenerateGLSLShader(GetEmbeddedResource(glsl_minmaxresult_comp), shaderType,
                                    glslCSVer, defines);

            DebugData.minmaxResultProgram[i] = CreateCShaderProgram(cs);

            BindUBO(DebugData.minmaxResultProgram[i], "HistogramUBOData", 2);
            ConfigureTexDisplayProgramBindings(DebugData.minmaxResultProgram[i]);
          }
        }
      }
    }
    else
    {
      RDCWARN(
          "GL_ARB_compute_shader or ARB_shader_storage_buffer_object not supported, disabling "
          "min/max and histogram features.");
      m_pDriver->AddDebugMessage(MessageCategory::Portability, MessageSeverity::Medium,
                                 MessageSource::RuntimeWarning,
                                 "GL_ARB_compute_shader or ARB_shader_storage_buffer_object not "
                                 "supported, disabling min/max and histogram features.");
    }

    drv.glGenBuffers(1, &DebugData.minmaxTileResult);
    drv.glGenBuffers(1, &DebugData.minmaxResult);
    drv.glGenBuffers(1, &DebugData.histogramBuf);

    const uint32_t maxTexDim = 16384;
    const uint32_t blockPixSize = HGRAM_PIXELS_PER_TILE * HGRAM_TILES_PER_BLOCK;
    const uint32_t maxBlocksNeeded = (maxTexDim * maxTexDim) / (blockPixSize * blockPixSize);

    const size_t byteSize =
        2 * sizeof(Vec4f) * HGRAM_TILES_PER_BLOCK * HGRAM_TILES_PER_BLOCK * maxBlocksNeeded;

    drv.glNamedBufferDataEXT(DebugData.minmaxTileResult, byteSize, NULL, eGL_DYNAMIC_DRAW);
    drv.glNamedBufferDataEXT(DebugData.minmaxResult, sizeof(Vec4f) * 2, NULL, eGL_DYNAMIC_READ);
    drv.glNamedBufferDataEXT(DebugData.histogramBuf, sizeof(uint32_t) * HGRAM_NUM_BUCKETS, NULL,
                             eGL_DYNAMIC_READ);
  }

  if(HasExt[ARB_compute_shader] && HasExt[ARB_shading_language_420pack])
  {
    cs = GenerateGLSLShader(GetEmbeddedResource(glsl_mesh_comp), shaderType, glslCSVer);
    DebugData.meshPickProgram = CreateCShaderProgram(cs);

    BindUBO(DebugData.meshPickProgram, "MeshPickUBOData", 0);
  }
  else
  {
    DebugData.meshPickProgram = 0;
    RDCWARN("GL_ARB_compute_shader not supported, disabling mesh picking.");
    m_pDriver->AddDebugMessage(MessageCategory::Portability, MessageSeverity::Medium,
                               MessageSource::RuntimeWarning,
                               "GL_ARB_compute_shader not supported, disabling mesh picking.");
  }

  RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 0.8f);

  DebugData.pickResultBuf = 0;

  if(DebugData.meshPickProgram)
  {
    drv.glGenBuffers(1, &DebugData.pickResultBuf);
    drv.glBindBuffer(eGL_SHADER_STORAGE_BUFFER, DebugData.pickResultBuf);
    drv.glNamedBufferDataEXT(DebugData.pickResultBuf,
                             sizeof(Vec4f) * DebugRenderData::maxMeshPicks + sizeof(uint32_t) * 4,
                             NULL, eGL_DYNAMIC_READ);

    // sized/created on demand
    DebugData.pickVBBuf = DebugData.pickIBBuf = 0;
    DebugData.pickVBSize = DebugData.pickIBSize = 0;
  }

  drv.glGenVertexArrays(1, &DebugData.meshVAO);
  drv.glBindVertexArray(DebugData.meshVAO);

  drv.glGenBuffers(1, &DebugData.axisFrustumBuffer);
  drv.glBindBuffer(eGL_ARRAY_BUFFER, DebugData.axisFrustumBuffer);

  Vec3f TLN = Vec3f(-1.0f, 1.0f, 0.0f);    // TopLeftNear, etc...
  Vec3f TRN = Vec3f(1.0f, 1.0f, 0.0f);
  Vec3f BLN = Vec3f(-1.0f, -1.0f, 0.0f);
  Vec3f BRN = Vec3f(1.0f, -1.0f, 0.0f);

  Vec3f TLF = Vec3f(-1.0f, 1.0f, 1.0f);
  Vec3f TRF = Vec3f(1.0f, 1.0f, 1.0f);
  Vec3f BLF = Vec3f(-1.0f, -1.0f, 1.0f);
  Vec3f BRF = Vec3f(1.0f, -1.0f, 1.0f);

  Vec3f axisFrustum[] = {
      // axis marker vertices
      Vec3f(0.0f, 0.0f, 0.0f),
      Vec3f(1.0f, 0.0f, 0.0f),
      Vec3f(0.0f, 0.0f, 0.0f),
      Vec3f(0.0f, 1.0f, 0.0f),
      Vec3f(0.0f, 0.0f, 0.0f),
      Vec3f(0.0f, 0.0f, 1.0f),

      // frustum vertices
      TLN,
      TRN,
      TRN,
      BRN,
      BRN,
      BLN,
      BLN,
      TLN,

      TLN,
      TLF,
      TRN,
      TRF,
      BLN,
      BLF,
      BRN,
      BRF,

      TLF,
      TRF,
      TRF,
      BRF,
      BRF,
      BLF,
      BLF,
      TLF,
  };

  drv.glNamedBufferDataEXT(DebugData.axisFrustumBuffer, sizeof(axisFrustum), axisFrustum,
                           eGL_STATIC_DRAW);

  drv.glGenVertexArrays(1, &DebugData.axisVAO);
  drv.glBindVertexArray(DebugData.axisVAO);
  drv.glVertexAttribPointer(0, 3, eGL_FLOAT, GL_FALSE, sizeof(Vec3f), NULL);
  drv.glEnableVertexAttribArray(0);

  drv.glGenVertexArrays(1, &DebugData.frustumVAO);
  drv.glBindVertexArray(DebugData.frustumVAO);
  drv.glVertexAttribPointer(0, 3, eGL_FLOAT, GL_FALSE, sizeof(Vec3f),
                            (const void *)(sizeof(Vec3f) * 6));
  drv.glEnableVertexAttribArray(0);

  drv.glGenVertexArrays(1, &DebugData.triHighlightVAO);
  drv.glBindVertexArray(DebugData.triHighlightVAO);

  drv.glGenBuffers(1, &DebugData.triHighlightBuffer);
  drv.glBindBuffer(eGL_ARRAY_BUFFER, DebugData.triHighlightBuffer);

  drv.glNamedBufferDataEXT(DebugData.triHighlightBuffer, sizeof(Vec4f) * 24, NULL, eGL_DYNAMIC_DRAW);

  drv.glVertexAttribPointer(0, 4, eGL_FLOAT, GL_FALSE, sizeof(Vec4f), NULL);
  drv.glEnableVertexAttribArray(0);

  MakeCurrentReplayContext(&m_ReplayCtx);

  // try to identify the GPU we're running on.
  {
    RDCEraseEl(m_DriverInfo);

    const char *vendor = (const char *)drv.glGetString(eGL_VENDOR);
    const char *renderer = (const char *)drv.glGetString(eGL_RENDERER);
    const char *version = (const char *)drv.glGetString(eGL_VERSION);

    // we're just doing substring searches, so combine both for ease.
    rdcstr combined = (vendor ? vendor : "");
    combined += " ";
    combined += (renderer ? renderer : "");

    // make lowercase, for case-insensitive matching, and add preceding/trailing space for easier
    // 'word' matching
    combined = " " + strlower(combined) + " ";

    RDCDEBUG("Identifying vendor from '%s'", combined.c_str());

    struct pattern
    {
      const char *search;
      GPUVendor vendor;
    } patterns[] = {
        {" arm ", GPUVendor::ARM},
        {" mali ", GPUVendor::ARM},
        {" mali-", GPUVendor::ARM},
        {" amd ", GPUVendor::AMD},
        {"advanced micro devices", GPUVendor::AMD},
        {"ati technologies", GPUVendor::AMD},
        {"radeon", GPUVendor::AMD},
        {"broadcom", GPUVendor::Broadcom},
        {"imagination", GPUVendor::Imagination},
        {"powervr", GPUVendor::Imagination},
        {"intel", GPUVendor::Intel},
        {"geforce", GPUVendor::nVidia},
        {"quadro", GPUVendor::nVidia},
        {"nouveau", GPUVendor::nVidia},
        {"nvidia", GPUVendor::nVidia},
        {"adreno", GPUVendor::Qualcomm},
        {"qualcomm", GPUVendor::Qualcomm},
        {"vivante", GPUVendor::Verisilicon},
        {"llvmpipe", GPUVendor::Software},
        {"softpipe", GPUVendor::Software},
        {"bluestacks", GPUVendor::Software},
    };

    for(const pattern &p : patterns)
    {
      if(combined.contains(p.search))
      {
        if(m_DriverInfo.vendor == GPUVendor::Unknown)
        {
          m_DriverInfo.vendor = p.vendor;
        }
        else
        {
          // either we already found this with another pattern, or we've identified two patterns and
          // it's ambiguous. Keep the first one we found, arbitrarily, but print a warning.
          if(m_DriverInfo.vendor != p.vendor)
          {
            RDCWARN("Already identified '%s' as %s, but now identified as %s", combined.c_str(),
                    ToStr(m_DriverInfo.vendor).c_str(), ToStr(p.vendor).c_str());
          }
        }
      }
    }

    RDCDEBUG("Identified GPU vendor '%s'", ToStr(m_DriverInfo.vendor).c_str());

    rdcstr versionString = version;

    versionString += " / ";
    versionString += renderer ? renderer : "";
    versionString += " / ";
    versionString += vendor ? vendor : "";

    versionString.resize(RDCMIN(versionString.size(), ARRAY_COUNT(m_DriverInfo.version) - 1));
    memcpy(m_DriverInfo.version, versionString.c_str(), versionString.size());
  }

  // these below need to be made on the replay context, as they are context-specific (not shared)
  // and will be used on the replay context.

  if(HasExt[ARB_transform_feedback2])
    drv.glGenTransformFeedbacks(1, &DebugData.feedbackObj);
  drv.glGenBuffers(1, &DebugData.feedbackBuffer);
  DebugData.feedbackQueries.push_back(0);
  drv.glGenQueries(1, &DebugData.feedbackQueries[0]);

  if(HasExt[ARB_transform_feedback2])
    drv.glBindTransformFeedback(eGL_TRANSFORM_FEEDBACK, DebugData.feedbackObj);
  drv.glBindBuffer(eGL_TRANSFORM_FEEDBACK_BUFFER, DebugData.feedbackBuffer);
  drv.glNamedBufferDataEXT(DebugData.feedbackBuffer, (GLsizeiptr)DebugData.feedbackBufferSize, NULL,
                           eGL_DYNAMIC_READ);
  drv.glBindBufferBase(eGL_TRANSFORM_FEEDBACK_BUFFER, 0, DebugData.feedbackBuffer);
  if(HasExt[ARB_transform_feedback2])
    drv.glBindTransformFeedback(eGL_TRANSFORM_FEEDBACK, 0);

  RenderDoc::Inst().SetProgress(LoadProgress::DebugManagerInit, 1.0f);

  if(!HasExt[ARB_gpu_shader5])
  {
    RDCWARN(
        "ARB_gpu_shader5 not supported, pixel picking and saving of integer textures may be "
        "inaccurate.");
    m_pDriver->AddDebugMessage(MessageCategory::Portability, MessageSeverity::Medium,
                               MessageSource::RuntimeWarning,
                               "ARB_gpu_shader5 not supported, pixel picking and saving of integer "
                               "textures may be inaccurate.");

    m_Degraded = true;
  }

  if(!HasExt[ARB_stencil_texturing])
  {
    RDCWARN("ARB_stencil_texturing not supported, stencil values will not be displayed or picked.");
    m_pDriver->AddDebugMessage(
        MessageCategory::Portability, MessageSeverity::Medium, MessageSource::RuntimeWarning,
        "ARB_stencil_texturing not supported, stencil values will not be displayed or picked.");

    m_Degraded = true;
  }

  if(!HasExt[ARB_shader_image_load_store] || !HasExt[ARB_compute_shader] ||
     !HasExt[ARB_shading_language_420pack])
  {
    RDCWARN(
        "Don't have shader image load/store or compute shaders, functionality will be degraded.");
    m_Degraded = true;
  }

#if ENABLED(RDOC_APPLE)
  // temporary hack - just never consider apple degraded, since there's never going to be an
  // improvement so there's no point warning users.
  m_Degraded = false;
#endif

  m_pDriver->PopInternalShader();
}