Pārlūkot izejas kodu

shader: analyze shader for capability use, query driver shader caps

rdb 5 gadi atpakaļ
vecāks
revīzija
e9acf26f9c

+ 3 - 3
panda/src/display/graphicsStateGuardian.I

@@ -580,7 +580,7 @@ get_supports_basic_shaders() const {
  */
  */
 INLINE bool GraphicsStateGuardian::
 INLINE bool GraphicsStateGuardian::
 get_supports_geometry_shaders() const {
 get_supports_geometry_shaders() const {
-  return _supports_geometry_shaders;
+  return (_supported_shader_caps & ShaderModule::C_geometry_shader) != 0;
 }
 }
 
 
 /**
 /**
@@ -588,7 +588,7 @@ get_supports_geometry_shaders() const {
  */
  */
 INLINE bool GraphicsStateGuardian::
 INLINE bool GraphicsStateGuardian::
 get_supports_tessellation_shaders() const {
 get_supports_tessellation_shaders() const {
-  return _supports_tessellation_shaders;
+  return (_supported_shader_caps & ShaderModule::C_tessellation_shader) != 0;
 }
 }
 
 
 /**
 /**
@@ -596,7 +596,7 @@ get_supports_tessellation_shaders() const {
  */
  */
 INLINE bool GraphicsStateGuardian::
 INLINE bool GraphicsStateGuardian::
 get_supports_compute_shaders() const {
 get_supports_compute_shaders() const {
-  return _supports_compute_shaders;
+  return (_supported_shader_caps & ShaderModule::C_compute_shader) != 0;
 }
 }
 
 
 /**
 /**

+ 2 - 3
panda/src/display/graphicsStateGuardian.cxx

@@ -241,9 +241,6 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system,
   _supports_shadow_filter = false;
   _supports_shadow_filter = false;
   _supports_sampler_objects = false;
   _supports_sampler_objects = false;
   _supports_basic_shaders = false;
   _supports_basic_shaders = false;
-  _supports_geometry_shaders = false;
-  _supports_tessellation_shaders = false;
-  _supports_compute_shaders = false;
   _supports_glsl = false;
   _supports_glsl = false;
   _supports_hlsl = false;
   _supports_hlsl = false;
   _supports_spir_v = false;
   _supports_spir_v = false;
@@ -2638,6 +2635,8 @@ reset() {
   _tex_gen_modifies_mat = false;
   _tex_gen_modifies_mat = false;
   _last_max_stage_index = 0;
   _last_max_stage_index = 0;
 
 
+  _supported_shader_caps = 0;
+
   _is_valid = true;
   _is_valid = true;
 }
 }
 
 

+ 1 - 3
panda/src/display/graphicsStateGuardian.h

@@ -620,9 +620,6 @@ protected:
   bool _supports_shadow_filter;
   bool _supports_shadow_filter;
   bool _supports_sampler_objects;
   bool _supports_sampler_objects;
   bool _supports_basic_shaders;
   bool _supports_basic_shaders;
-  bool _supports_geometry_shaders;
-  bool _supports_tessellation_shaders;
-  bool _supports_compute_shaders;
   bool _supports_glsl;
   bool _supports_glsl;
   bool _supports_hlsl;
   bool _supports_hlsl;
   bool _supports_spir_v;
   bool _supports_spir_v;
@@ -647,6 +644,7 @@ protected:
 
 
   ShaderModel _auto_detect_shader_model;
   ShaderModel _auto_detect_shader_model;
   ShaderModel _shader_model;
   ShaderModel _shader_model;
+  int _supported_shader_caps;
 
 
   static PT(TextureStage) _alpha_scale_texture_stage;
   static PT(TextureStage) _alpha_scale_texture_stage;
 
 

+ 1 - 1
panda/src/glstuff/glGraphicsBuffer_src.cxx

@@ -427,7 +427,7 @@ rebuild_bitplanes() {
       RenderTexturePlane plane = rt._plane;
       RenderTexturePlane plane = rt._plane;
       Texture *tex = rt._texture;
       Texture *tex = rt._texture;
 
 
-      if (rtm_mode == RTM_bind_layered && glgsg->_supports_geometry_shaders) {
+      if (rtm_mode == RTM_bind_layered && glgsg->get_supports_geometry_shaders()) {
         if (tex->get_z_size() != _rb_size_z) {
         if (tex->get_z_size() != _rb_size_z) {
           GLCAT.warning()
           GLCAT.warning()
            << "All textures attached to layered FBO should have the same layer count!\n";
            << "All textures attached to layered FBO should have the same layer count!\n";

+ 189 - 182
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -159,7 +159,7 @@ null_glBlendColor(GLclampf, GLclampf, GLclampf, GLclampf) {
 // drawing GUIs and such.
 // drawing GUIs and such.
 static const string default_vshader =
 static const string default_vshader =
 #ifndef OPENGLES
 #ifndef OPENGLES
-  "#version 150\n"
+  "#version 330\n"
   "in vec4 p3d_Vertex;\n"
   "in vec4 p3d_Vertex;\n"
   "in vec4 p3d_Color;\n"
   "in vec4 p3d_Color;\n"
   "in vec2 p3d_MultiTexCoord0;\n"
   "in vec2 p3d_MultiTexCoord0;\n"
@@ -184,7 +184,7 @@ static const string default_vshader =
 #ifndef OPENGLES
 #ifndef OPENGLES
 // This version of the shader is used if vertices-float64 is enabled.
 // This version of the shader is used if vertices-float64 is enabled.
 static const string default_vshader_fp64 =
 static const string default_vshader_fp64 =
-  "#version 150\n"
+  "#version 330\n"
   "#extension GL_ARB_vertex_attrib_64bit : require\n"
   "#extension GL_ARB_vertex_attrib_64bit : require\n"
   "#extension GL_ARB_gpu_shader_fp64 : require\n"
   "#extension GL_ARB_gpu_shader_fp64 : require\n"
   "in dvec3 p3d_Vertex;\n"
   "in dvec3 p3d_Vertex;\n"
@@ -221,7 +221,7 @@ static const string default_vshader_fp64_gl41 =
 
 
 static const string default_fshader =
 static const string default_fshader =
 #ifndef OPENGLES
 #ifndef OPENGLES
-  "#version 150\n"
+  "#version 330\n"
   "in vec2 texcoord;\n"
   "in vec2 texcoord;\n"
   "in vec4 color;\n"
   "in vec4 color;\n"
   "out vec4 p3d_FragColor;\n"
   "out vec4 p3d_FragColor;\n"
@@ -468,8 +468,8 @@ string CLP(GraphicsStateGuardian)::get_driver_renderer() { return _gl_renderer;
 string CLP(GraphicsStateGuardian)::get_driver_version() { return _gl_version; }
 string CLP(GraphicsStateGuardian)::get_driver_version() { return _gl_version; }
 int CLP(GraphicsStateGuardian)::get_driver_version_major() { return _gl_version_major; }
 int CLP(GraphicsStateGuardian)::get_driver_version_major() { return _gl_version_major; }
 int CLP(GraphicsStateGuardian)::get_driver_version_minor() { return _gl_version_minor; }
 int CLP(GraphicsStateGuardian)::get_driver_version_minor() { return _gl_version_minor; }
-int CLP(GraphicsStateGuardian)::get_driver_shader_version_major() { return _gl_shadlang_ver_major; }
-int CLP(GraphicsStateGuardian)::get_driver_shader_version_minor() { return _gl_shadlang_ver_minor; }
+int CLP(GraphicsStateGuardian)::get_driver_shader_version_major() { return _glsl_version / 100; }
+int CLP(GraphicsStateGuardian)::get_driver_shader_version_minor() { return _glsl_version % 100; }
 
 
 /**
 /**
  *
  *
@@ -486,9 +486,6 @@ CLP(GraphicsStateGuardian)(GraphicsEngine *engine, GraphicsPipe *pipe) :
   _check_errors = gl_check_errors;
   _check_errors = gl_check_errors;
   _force_flush = gl_force_flush;
   _force_flush = gl_force_flush;
 
 
-  _gl_shadlang_ver_major = 0;
-  _gl_shadlang_ver_minor = 0;
-
   // Let's say we have a core profile, to be checked later (Otherwise, if we are
   // Let's say we have a core profile, to be checked later (Otherwise, if we are
   // wrong the user may ask for some non-available functions)
   // wrong the user may ask for some non-available functions)
 #ifndef OPENGLES
 #ifndef OPENGLES
@@ -808,12 +805,6 @@ reset() {
     Geom::GR_line_strip |
     Geom::GR_line_strip |
     Geom::GR_flat_last_vertex;
     Geom::GR_flat_last_vertex;
 
 
-#ifndef OPENGLES
-  if (_supports_geometry_shaders) {
-    _supported_geom_rendering |= Geom::GR_adjacency;
-  }
-#endif
-
   _supports_point_parameters = false;
   _supports_point_parameters = false;
 
 
 #ifdef OPENGLES_1
 #ifdef OPENGLES_1
@@ -1677,47 +1668,163 @@ reset() {
   }
   }
 
 
   // Check for GLSL support.
   // Check for GLSL support.
+  _supported_shader_caps = 0;
 #if defined(OPENGLES_1)
 #if defined(OPENGLES_1)
   _supports_glsl = false;
   _supports_glsl = false;
-  _supports_geometry_shaders = false;
-  _supports_tessellation_shaders = false;
+
 #elif defined(OPENGLES)
 #elif defined(OPENGLES)
   _supports_glsl = true;
   _supports_glsl = true;
-  _supports_geometry_shaders = false;
-  _supports_tessellation_shaders = false;
+
+  if (is_at_least_gles_version(3, 0)) {
+    _supported_shader_caps |=
+      ShaderModule::C_integer |
+      ShaderModule::C_texture_fetch |
+      ShaderModule::C_vertex_id |
+      ShaderModule::C_round_even |
+      ShaderModule::C_instance_id;
+  }
+
+  if (is_at_least_gles_version(3, 1)) {
+    _supported_shader_caps |=
+      ShaderModule::C_compute_shader |
+      ShaderModule::C_image_load_store |
+      ShaderModule::C_extended_arithmetic;
+  }
+
+  if (is_at_least_gles_version(3, 2)) {
+    _supported_shader_caps |=
+      ShaderModule::C_sample_variables |
+      ShaderModule::C_buffer_texture;
+
+    if (has_extension("GL_EXT_geometry_shader")) {
+      _supported_shader_caps |= ShaderModule::C_geometry_shader;
+    }
+    if (has_extension("GL_EXT_tessellation_shader")) {
+      _supported_shader_caps |= ShaderModule::C_tessellation_shader;
+    }
+  }
+  else {
+    if (has_extension("GL_OES_sample_variables")) {
+      _supported_shader_caps |= ShaderModule::C_sample_variables;
+    }
+    if (has_extension("GL_OES_texture_buffer")) {
+      _supported_shader_caps |= ShaderModule::C_buffer_texture;
+    }
+  }
+
 #else
 #else
-  _supports_glsl = (_gl_shadlang_ver_major >= 1);
-  _supports_tessellation_shaders = is_at_least_gl_version(4, 0) || has_extension("GL_ARB_tessellation_shader");
+  _supports_glsl = (_glsl_version >= 100);
+
+  // Test support for shader features.
+  if (_supports_glsl) {
+    // 2.0 guarantees support for 1.10
+    // 2.1 guarantees support for 1.20
+    // 3.0 and 3.1 guarantee support for 1.30
+    // 3.2 guarantees support for 1.40 and 1.50
+    // 3.3 guarantees support for 3.30
+    // 4.0 guarantees support for 4.00
+    // 4.1 guarantees support for 4.10
+    // 4.x guarantees support for 1.40-4.x0 (for x >= 2)
+
+    if (_glsl_version >= 130) { // OpenGL 3.0
+      _supported_shader_caps |=
+        ShaderModule::C_integer |
+        ShaderModule::C_texture_fetch |
+        ShaderModule::C_buffer_texture |
+        ShaderModule::C_vertex_id |
+        ShaderModule::C_round_even;
+    }
+
+    if (_glsl_version >= 140) { // OpenGL 3.1
+      _supported_shader_caps |= ShaderModule::C_instance_id;
+    }
+
+    if (_glsl_version >= 150) { // OpenGL 3.2
+      _supported_shader_caps |=
+        ShaderModule::C_geometry_shader |
+        ShaderModule::C_primitive_id;
+      _supported_geom_rendering |= Geom::GR_adjacency;
+    }
+    else {
+      if (has_extension("GL_ARB_draw_instanced")) {
+        _supported_shader_caps |= ShaderModule::C_instance_id;
+      }
+      if (has_extension("GL_ARB_geometry_shader4")) {
+        _supported_shader_caps |= ShaderModule::C_geometry_shader;
+        _supported_geom_rendering |= Geom::GR_adjacency;
+      }
+    }
+
+    if (_glsl_version >= 330 || has_extension("GL_ARB_shader_bit_encoding")) {
+      _supported_shader_caps |= ShaderModule::C_bit_encoding;
+    }
+
+    if (_glsl_version >= 400) {
+      _supported_shader_caps |=
+        ShaderModule::C_double |
+        ShaderModule::C_cube_map_array |
+        ShaderModule::C_tessellation_shader |
+        ShaderModule::C_sample_variables |
+        ShaderModule::C_extended_arithmetic |
+        ShaderModule::C_texture_query_lod;
+    }
+    else {
+      if (has_extension("GL_ARB_gpu_shader_fp64")) {
+        _supported_shader_caps |= ShaderModule::C_double;
+      }
+      if (has_extension("GL_ARB_tessellation_shader")) {
+        _supported_shader_caps |= ShaderModule::C_tessellation_shader;
+      }
+      if (has_extension("GL_ARB_texture_query_lod")) {
+        _supported_shader_caps |= ShaderModule::C_texture_query_lod;
+      }
+    }
+
+    if (_glsl_version >= 420 || has_extension("GL_ARB_shader_image_load_store")) {
+      _supported_shader_caps |= ShaderModule::C_image_load_store;
+    }
+
+    if (_glsl_version >= 430 || has_extension("GL_ARB_compute_shader")) {
+      _supported_shader_caps |= ShaderModule::C_compute_shader;
+    }
+
+    if (_glsl_version >= 430 || has_extension("GL_ARB_texture_query_levels")) {
+      _supported_shader_caps |= ShaderModule::C_texture_query_levels;
+    }
+
+    if (_glsl_version >= 440 || has_extension("GL_ARB_enhanced_layouts")) {
+      _supported_shader_caps |= ShaderModule::C_enhanced_layouts;
+    }
+
+    if (_glsl_version >= 450) {
+      _supported_shader_caps |=
+        ShaderModule::C_derivative_control |
+        ShaderModule::C_texture_query_samples;
+    }
+    else if (has_extension("GL_ARB_derivative_control")) {
+      _supported_shader_caps |= ShaderModule::C_derivative_control;
+    }
+  }
 
 
   if (is_at_least_gl_version(3, 2)) {
   if (is_at_least_gl_version(3, 2)) {
-    _supports_geometry_shaders = true;
     _glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREARBPROC)
     _glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREARBPROC)
       get_extension_func("glFramebufferTexture");
       get_extension_func("glFramebufferTexture");
-
-  } else if (has_extension("GL_ARB_geometry_shader4")) {
-    _supports_geometry_shaders = true;
+  }
+  else if (has_extension("GL_ARB_geometry_shader4")) {
     _glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREARBPROC)
     _glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREARBPROC)
       get_extension_func("glFramebufferTextureARB");
       get_extension_func("glFramebufferTextureARB");
     _glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)
     _glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)
       get_extension_func("glProgramParameteriARB");
       get_extension_func("glProgramParameteriARB");
-
-  } else if (has_extension("GL_EXT_geometry_shader4")) {
-    _supports_geometry_shaders = true;
-    _glFramebufferTexture = nullptr;
-    _glProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC)
-      get_extension_func("glProgramParameteriEXT");
-
-  } else {
-    _supports_geometry_shaders = false;
+  }
+  else {
     _glFramebufferTexture = nullptr;
     _glFramebufferTexture = nullptr;
   }
   }
 #endif
 #endif
-  _shader_caps._supports_glsl = _supports_glsl;
 
 
   // Check for SPIR-V support.
   // Check for SPIR-V support.
 #ifndef OPENGLES
 #ifndef OPENGLES
-  _supports_spir_v = has_extension("GL_ARB_gl_spirv");
-  if (_supports_spir_v) {
+  _supports_spir_v = false;
+  if (has_extension("GL_ARB_gl_spirv")) {
     _glShaderBinary = (PFNGLSHADERBINARYPROC)
     _glShaderBinary = (PFNGLSHADERBINARYPROC)
       get_extension_func("glShaderBinary");
       get_extension_func("glShaderBinary");
     _glSpecializeShader = (PFNGLSPECIALIZESHADERARBPROC)
     _glSpecializeShader = (PFNGLSPECIALIZESHADERARBPROC)
@@ -1731,89 +1838,30 @@ reset() {
                has_extension("GL_EXT_direct_state_access")) {
                has_extension("GL_EXT_direct_state_access")) {
       _glProgramUniform1i = (PFNGLPROGRAMUNIFORM1IPROC)
       _glProgramUniform1i = (PFNGLPROGRAMUNIFORM1IPROC)
         get_extension_func("glProgramUniform1iEXT");
         get_extension_func("glProgramUniform1iEXT");
+    }
 
 
-    } else {
-      _supports_spir_v = false;
+    if (gl_support_spirv && _glShaderBinary != nullptr &&
+        _glSpecializeShader != nullptr && _glProgramUniform1i != nullptr) {
+      _supports_spir_v = true;
     }
     }
   }
   }
 #endif
 #endif
 
 
-  // Check for support for other types of shaders that can be used by Cg.
-  _supports_basic_shaders = false;
-#if defined(HAVE_CG) && !defined(OPENGLES)
-  if (has_extension("GL_ARB_vertex_program") &&
-      has_extension("GL_ARB_fragment_program")) {
-    _supports_basic_shaders = true;
-    _shader_caps._active_vprofile = (int)CG_PROFILE_ARBVP1;
-    _shader_caps._active_fprofile = (int)CG_PROFILE_ARBFP1;
-    _shader_caps._active_gprofile = (int)CG_PROFILE_UNKNOWN; // No geometry shader if only using basic
-
-    if (basic_shaders_only) {
-      // We're happy with ARB programs, thanks.
-    } else if (has_extension("GL_NV_gpu_program5")) {
-      _shader_caps._active_vprofile = (int)CG_PROFILE_GP5VP;
-      _shader_caps._active_fprofile = (int)CG_PROFILE_GP5FP;
-      _shader_caps._active_gprofile = (int)CG_PROFILE_GP5GP;
-
-    } else if (has_extension("GL_NV_gpu_program4")) {
-      _shader_caps._active_vprofile = (int)CG_PROFILE_GP4VP;
-      _shader_caps._active_fprofile = (int)CG_PROFILE_GP4FP;
-      _shader_caps._active_gprofile = (int)CG_PROFILE_GP4GP;
-
-    } else if (has_extension("GL_NV_vertex_program3") &&
-               has_extension("GL_NV_fragment_program2")) {
-      _shader_caps._active_vprofile = (int)CG_PROFILE_VP40;
-      _shader_caps._active_fprofile = (int)CG_PROFILE_FP40;
-      _shader_caps._active_gprofile = (int)CG_PROFILE_UNKNOWN;
-
-    } else if (has_extension("GL_NV_vertex_program2") &&
-               has_extension("GL_NV_fragment_program")) {
-      _shader_caps._active_vprofile = (int)CG_PROFILE_VP30;
-      _shader_caps._active_fprofile = (int)CG_PROFILE_FP30;
-      _shader_caps._active_gprofile = (int)CG_PROFILE_UNKNOWN;
-
-    } else if (has_extension("GL_NV_vertex_program1_1") &&
-               has_extension("GL_NV_texture_shader2") &&
-               has_extension("GL_NV_register_combiners2")) {
-      _shader_caps._active_vprofile = (int)CG_PROFILE_VP20;
-      _shader_caps._active_fprofile = (int)CG_PROFILE_FP20;
-      _shader_caps._active_gprofile = (int)CG_PROFILE_UNKNOWN;
-
-    } else if (_supports_glsl) {
-      // This is what will be available to non-NVIDIA cards.  It is the last
-      // option since it is slower to compile GLSL than the other options.
-      _shader_caps._active_vprofile = (int)CG_PROFILE_GLSLV;
-      _shader_caps._active_fprofile = (int)CG_PROFILE_GLSLF;
-      if (_supports_geometry_shaders) {
-        _shader_caps._active_gprofile = (int)CG_PROFILE_GLSLG;
-      }
-    }
-    _shader_caps._ultimate_vprofile = (int)CG_PROFILE_GLSLV;
-    _shader_caps._ultimate_fprofile = (int)CG_PROFILE_GLSLF;
-    _shader_caps._ultimate_gprofile = (int)CG_PROFILE_GLSLG;
-
-    // Bug workaround for radeons.
-    if (_shader_caps._active_fprofile == CG_PROFILE_ARBFP1) {
-      if (has_extension("GL_ATI_draw_buffers")) {
-        _shader_caps._bug_list.insert(Shader::SBUG_ati_draw_buffers);
-      }
-    }
-
-    Shader::set_default_caps(_shader_caps);
+  _supports_basic_shaders = _supports_glsl;
 
 
-  } else if (_supports_glsl) {
-    // No, but we do support GLSL...
+#if defined(HAVE_CG) && !defined(OPENGLES)
+  if (_supports_glsl) {
     _shader_caps._active_vprofile = (int)CG_PROFILE_GLSLV;
     _shader_caps._active_vprofile = (int)CG_PROFILE_GLSLV;
     _shader_caps._active_fprofile = (int)CG_PROFILE_GLSLF;
     _shader_caps._active_fprofile = (int)CG_PROFILE_GLSLF;
-    if (_supports_geometry_shaders) {
+    if (get_supports_geometry_shaders()) {
       _shader_caps._active_gprofile = (int)CG_PROFILE_GLSLG;
       _shader_caps._active_gprofile = (int)CG_PROFILE_GLSLG;
-    } else {
-      _shader_caps._active_gprofile = (int)CG_PROFILE_UNKNOWN;
     }
     }
+    _shader_caps._ultimate_vprofile = (int)CG_PROFILE_GLSLV;
+    _shader_caps._ultimate_fprofile = (int)CG_PROFILE_GLSLF;
+    _shader_caps._ultimate_gprofile = (int)CG_PROFILE_GLSLG;
   }
   }
 #endif  // HAVE_CG
 #endif  // HAVE_CG
 
 
-  _supports_compute_shaders = false;
 #ifndef OPENGLES_1
 #ifndef OPENGLES_1
 #ifdef OPENGLES
 #ifdef OPENGLES
   if (is_at_least_gles_version(3, 1)) {
   if (is_at_least_gles_version(3, 1)) {
@@ -1822,10 +1870,6 @@ reset() {
 #endif
 #endif
     _glDispatchCompute = (PFNGLDISPATCHCOMPUTEPROC)
     _glDispatchCompute = (PFNGLDISPATCHCOMPUTEPROC)
       get_extension_func("glDispatchCompute");
       get_extension_func("glDispatchCompute");
-
-    if (_glDispatchCompute != nullptr) {
-      _supports_compute_shaders = true;
-    }
   }
   }
 #endif  // !OPENGLES_1
 #endif  // !OPENGLES_1
 
 
@@ -1946,7 +1990,7 @@ reset() {
       _glVertexAttribLPointer = nullptr;
       _glVertexAttribLPointer = nullptr;
     }
     }
 
 
-    if (_supports_tessellation_shaders) {
+    if (get_supports_tessellation_shaders()) {
       _glPatchParameteri = (PFNGLPATCHPARAMETERIPROC)
       _glPatchParameteri = (PFNGLPATCHPARAMETERIPROC)
          get_extension_func("glPatchParameteri");
          get_extension_func("glPatchParameteri");
     }
     }
@@ -3489,36 +3533,34 @@ reset() {
   _runtime_color_scale = !has_fixed_function_pipeline();
   _runtime_color_scale = !has_fixed_function_pipeline();
 
 
 #ifndef OPENGLES
 #ifndef OPENGLES
-  if (_gl_shadlang_ver_major >= 4 || has_extension("GL_NV_gpu_program5")) {
+  if (_glsl_version >= 400 || has_extension("GL_NV_gpu_program5")) {
     // gp5fp - OpenGL fragment profile for GeForce 400 Series and up
     // gp5fp - OpenGL fragment profile for GeForce 400 Series and up
     _shader_model = SM_50;
     _shader_model = SM_50;
-
-  } else if (_gl_shadlang_ver_major >= 3 ||
-             has_extension("GL_NV_gpu_program4")) {
+  }
+  else if (_glsl_version >= 300 || has_extension("GL_NV_gpu_program4")) {
     // gp4fp - OpenGL fragment profile for G8x (GeForce 8xxx and up)
     // gp4fp - OpenGL fragment profile for G8x (GeForce 8xxx and up)
     _shader_model = SM_40;
     _shader_model = SM_40;
-
-  } else if (has_extension("GL_NV_fragment_program2")) {
+  }
+  else if (has_extension("GL_NV_fragment_program2")) {
     // fp40 - OpenGL fragment profile for NV4x (GeForce 6xxx and 7xxx Series,
     // fp40 - OpenGL fragment profile for NV4x (GeForce 6xxx and 7xxx Series,
     // NV4x-based Quadro FX, etc.)
     // NV4x-based Quadro FX, etc.)
     _shader_model = SM_30;
     _shader_model = SM_30;
-
-  } else if (has_extension("GL_NV_fragment_program")) {
+  }
+  else if (has_extension("GL_NV_fragment_program")) {
     // fp30 - OpenGL fragment profile for NV3x (GeForce FX, Quadro FX, etc.)
     // fp30 - OpenGL fragment profile for NV3x (GeForce FX, Quadro FX, etc.)
     _shader_model = SM_2X;
     _shader_model = SM_2X;
-
-  } else if (_gl_shadlang_ver_major >= 1 ||
-             has_extension("GL_ARB_fragment_program")) {
+  }
+  else if (_glsl_version >= 100 || has_extension("GL_ARB_fragment_program")) {
     // This OpenGL profile corresponds to the per-fragment functionality
     // This OpenGL profile corresponds to the per-fragment functionality
     // introduced by GeForce FX and other DirectX 9 GPUs.
     // introduced by GeForce FX and other DirectX 9 GPUs.
     _shader_model = SM_20;
     _shader_model = SM_20;
-
-  } else if (has_extension("GL_NV_texture_shader2")) {
+  }
+  else if (has_extension("GL_NV_texture_shader2")) {
     // fp20 - OpenGL fragment profile for NV2x (GeForce3, GeForce4 Ti, Quadro
     // fp20 - OpenGL fragment profile for NV2x (GeForce3, GeForce4 Ti, Quadro
     // DCC, etc.)
     // DCC, etc.)
     _shader_model = SM_11;
     _shader_model = SM_11;
-
-  } else {
+  }
+  else {
     // No shader support
     // No shader support
     _shader_model = SM_00;
     _shader_model = SM_00;
   }
   }
@@ -3541,53 +3583,11 @@ reset() {
   _auto_detect_shader_model = _shader_model;
   _auto_detect_shader_model = _shader_model;
 
 
   if (GLCAT.is_debug()) {
   if (GLCAT.is_debug()) {
-#ifdef HAVE_CG
-#if CG_VERSION_NUM >= 2200
-    GLCAT.debug() << "Supported Cg profiles:\n";
-    int num_profiles = cgGetNumSupportedProfiles();
-    for (int i = 0; i < num_profiles; ++i) {
-      CGprofile profile = cgGetSupportedProfile(i);
-      if (cgGLIsProfileSupported(profile)) {
-        GLCAT.debug() << "  " << cgGetProfileString(profile) << "\n";
-      }
-    }
-#endif  // CG_VERSION_NUM >= 2200
-
-#if CG_VERSION_NUM >= 3100
-    GLCAT.debug() << "Cg GLSL version = "
-      << cgGLGetGLSLVersionString(cgGLDetectGLSLVersion()) << "\n";
-#endif
-
-    GLCAT.debug()
-      << "Cg latest vertex profile = "
-      << cgGetProfileString(cgGLGetLatestProfile(CG_GL_VERTEX)) << "\n";
-    GLCAT.debug()
-      << "Cg latest fragment profile = "
-      << cgGetProfileString(cgGLGetLatestProfile(CG_GL_FRAGMENT)) << "\n";
-#if CG_VERSION_NUM >= 2000
-    GLCAT.debug()
-      << "Cg latest geometry profile = "
-      << cgGetProfileString(cgGLGetLatestProfile(CG_GL_GEOMETRY)) << "\n";
-#endif
-    GLCAT.debug() << "basic-shaders-only " << basic_shaders_only << "\n";
-    GLCAT.debug()
-      << "Cg active vertex profile = "
-      << cgGetProfileString((CGprofile)_shader_caps._active_vprofile) << "\n";
-    GLCAT.debug()
-      << "Cg active fragment profile = "
-      << cgGetProfileString((CGprofile)_shader_caps._active_fprofile) << "\n";
-    GLCAT.debug()
-      << "Cg active geometry profile = "
-      << cgGetProfileString((CGprofile)_shader_caps._active_gprofile) << "\n";
-#endif  // HAVE_CG
-
     GLCAT.debug() << "shader model = " << _shader_model << "\n";
     GLCAT.debug() << "shader model = " << _shader_model << "\n";
   }
   }
-#endif  // !OPENGLES
 
 
   // OpenGL core profile requires a VAO to be bound.  It's a bit silly,
   // OpenGL core profile requires a VAO to be bound.  It's a bit silly,
   // because we can just bind a VAO and then forget about it.
   // because we can just bind a VAO and then forget about it.
-#if !defined(OPENGLES)
   if (core_profile) {
   if (core_profile) {
     if (_supports_vao) {
     if (_supports_vao) {
       _glGenVertexArrays(1, &_current_vao_index);
       _glGenVertexArrays(1, &_current_vao_index);
@@ -6267,6 +6267,16 @@ prepare_shader(Shader *se) {
 
 
 #ifndef OPENGLES_1
 #ifndef OPENGLES_1
   if (_supports_glsl) {
   if (_supports_glsl) {
+    int unsupported_caps = se->get_used_capabilities() & ~_supported_shader_caps;
+    if (unsupported_caps != 0) {
+      std::ostream &out = GLCAT.error()
+        << "Cannot load shader because the graphics back-end does not support "
+           "these capabilities: ";
+
+      ShaderModule::output_capabilities(out, unsupported_caps);
+      out << std::endl;
+    }
+
     push_group_marker(std::string("Prepare Shader ") + se->get_debug_name());
     push_group_marker(std::string("Prepare Shader ") + se->get_debug_name());
     ShaderContext *result = new CLP(ShaderContext)(this, se);
     ShaderContext *result = new CLP(ShaderContext)(this, se);
     pop_group_marker();
     pop_group_marker();
@@ -7068,7 +7078,7 @@ dispatch_compute(int num_groups_x, int num_groups_y, int num_groups_z) {
   maybe_gl_finish();
   maybe_gl_finish();
 
 
   PStatGPUTimer timer(this, _compute_dispatch_pcollector);
   PStatGPUTimer timer(this, _compute_dispatch_pcollector);
-  nassertv(_supports_compute_shaders);
+  nassertv(get_supports_compute_shaders());
   nassertv(_current_shader_context != nullptr);
   nassertv(_current_shader_context != nullptr);
   _glDispatchCompute(num_groups_x, num_groups_y, num_groups_z);
   _glDispatchCompute(num_groups_x, num_groups_y, num_groups_z);
 
 
@@ -8791,32 +8801,30 @@ query_gl_version() {
  */
  */
 void CLP(GraphicsStateGuardian)::
 void CLP(GraphicsStateGuardian)::
 query_glsl_version() {
 query_glsl_version() {
-  _gl_shadlang_ver_major = 0;
-  _gl_shadlang_ver_minor = 0;
+  _glsl_version = 0;
 #ifndef OPENGLES_1
 #ifndef OPENGLES_1
 
 
 #ifndef OPENGLES
 #ifndef OPENGLES
   // OpenGL 2.0 introduces GLSL in the core.  In 1.x, it is an extension.
   // OpenGL 2.0 introduces GLSL in the core.  In 1.x, it is an extension.
   if (_gl_version_major >= 2 || has_extension("GL_ARB_shading_language_100")) {
   if (_gl_version_major >= 2 || has_extension("GL_ARB_shading_language_100")) {
     string ver = show_gl_string("GL_SHADING_LANGUAGE_VERSION", GL_SHADING_LANGUAGE_VERSION);
     string ver = show_gl_string("GL_SHADING_LANGUAGE_VERSION", GL_SHADING_LANGUAGE_VERSION);
-    _gl_shadlang_ver_major = 1;
-    _gl_shadlang_ver_minor = (_gl_version_major >= 2) ? 1 : 0;
-    if (ver.empty() ||
-        sscanf(ver.c_str(), "%d.%d", &_gl_shadlang_ver_major,
-                                     &_gl_shadlang_ver_minor) != 2) {
+    int major = 1;
+    int minor = (_gl_version_major >= 2) ? 1 : 0;
+    if (ver.empty() || sscanf(ver.c_str(), "%d.%d", &major, &minor) != 2) {
       GLCAT.warning() << "Invalid GL_SHADING_LANGUAGE_VERSION format.\n";
       GLCAT.warning() << "Invalid GL_SHADING_LANGUAGE_VERSION format.\n";
     }
     }
+    _glsl_version = major * 100 + minor;
   }
   }
 #else
 #else
   // OpenGL ES 2.0 and above has shader support built-in.
   // OpenGL ES 2.0 and above has shader support built-in.
   string ver = show_gl_string("GL_SHADING_LANGUAGE_VERSION", GL_SHADING_LANGUAGE_VERSION);
   string ver = show_gl_string("GL_SHADING_LANGUAGE_VERSION", GL_SHADING_LANGUAGE_VERSION);
-  _gl_shadlang_ver_major = 1;
-  _gl_shadlang_ver_minor = 0;
+  int major = 1;
+  int minor = 0;
   if (ver.empty() ||
   if (ver.empty() ||
-      sscanf(ver.c_str(), "OpenGL ES GLSL ES %d.%d", &_gl_shadlang_ver_major,
-                                                     &_gl_shadlang_ver_minor) != 2) {
+      sscanf(ver.c_str(), "OpenGL ES GLSL ES %d.%d", &major, &minor) != 2) {
     GLCAT.warning() << "Invalid GL_SHADING_LANGUAGE_VERSION format.\n";
     GLCAT.warning() << "Invalid GL_SHADING_LANGUAGE_VERSION format.\n";
   }
   }
+  _glsl_version = major * 100 + minor;
 #endif
 #endif
 
 
   if (GLCAT.is_debug()) {
   if (GLCAT.is_debug()) {
@@ -8825,8 +8833,7 @@ query_glsl_version() {
 #ifdef OPENGLES
 #ifdef OPENGLES
          "ES "
          "ES "
 #endif
 #endif
-         "version: "
-      << _gl_shadlang_ver_major << "." << _gl_shadlang_ver_minor << "\n";
+         "version: " << _glsl_version << "\n";
   }
   }
 #endif  // !OPENGLES_1
 #endif  // !OPENGLES_1
 }
 }

+ 1 - 2
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -748,8 +748,7 @@ protected:
   std::string _gl_renderer;
   std::string _gl_renderer;
   std::string _gl_version;
   std::string _gl_version;
   int _gl_version_major, _gl_version_minor;
   int _gl_version_major, _gl_version_minor;
-  // #--- Zhao Nov2011
-  int _gl_shadlang_ver_major, _gl_shadlang_ver_minor;
+  int _glsl_version = 0;
 
 
   pset<std::string> _extensions;
   pset<std::string> _extensions;
 
 

+ 5 - 0
panda/src/glstuff/glmisc_src.cxx

@@ -28,6 +28,11 @@ ConfigVariableBool gl_support_fbo
             "EXT_framebuffer_object is broken.  The system might still be "
             "EXT_framebuffer_object is broken.  The system might still be "
             "able to create buffers using pbuffers or the like."));
             "able to create buffers using pbuffers or the like."));
 
 
+ConfigVariableBool gl_support_spirv
+  ("gl-support-spirv", true,
+   PRC_DESC("True to use the graphics driver's support for SPIR-V shaders if "
+            "available, false to transpile all SPIR-V shaders to GLSL first."));
+
 ConfigVariableBool gl_cheap_textures
 ConfigVariableBool gl_cheap_textures
   ("gl-cheap-textures", false,
   ("gl-cheap-textures", false,
    PRC_DESC("Configure this true to glHint the textures into the cheapest "
    PRC_DESC("Configure this true to glHint the textures into the cheapest "

+ 1 - 0
panda/src/glstuff/glmisc_src.h

@@ -43,6 +43,7 @@
 extern EXPCL_GL ConfigVariableInt gl_version;
 extern EXPCL_GL ConfigVariableInt gl_version;
 extern EXPCL_GL ConfigVariableBool gl_forward_compatible;
 extern EXPCL_GL ConfigVariableBool gl_forward_compatible;
 extern EXPCL_GL ConfigVariableBool gl_support_fbo;
 extern EXPCL_GL ConfigVariableBool gl_support_fbo;
+extern ConfigVariableBool gl_support_spirv;
 extern ConfigVariableBool gl_cheap_textures;
 extern ConfigVariableBool gl_cheap_textures;
 extern ConfigVariableBool gl_ignore_clamp;
 extern ConfigVariableBool gl_ignore_clamp;
 extern ConfigVariableBool gl_support_clamp_to_border;
 extern ConfigVariableBool gl_support_clamp_to_border;

+ 9 - 0
panda/src/gobj/shader.I

@@ -135,6 +135,15 @@ get_language() const {
   return _language;
   return _language;
 }
 }
 
 
+/**
+ * Returns a mask indicating which capabilities this shader needs to function
+ * optimally.
+ */
+INLINE int Shader::
+get_used_capabilities() const {
+  return _used_caps;
+}
+
 /**
 /**
  * Returns true if the fullpath has been set and is available.  See
  * Returns true if the fullpath has been set and is available.  See
  * set_fullpath().
  * set_fullpath().

+ 2 - 0
panda/src/gobj/shader.cxx

@@ -1688,8 +1688,10 @@ do_read_source(ShaderModule::Stage stage, std::istream &in,
     }
     }
   }
   }
 
 
+  int used_caps = module->get_used_capabilities();
   _modules.push_back(std::move(module));
   _modules.push_back(std::move(module));
   _module_mask |= (1u << (uint32_t)stage);
   _module_mask |= (1u << (uint32_t)stage);
+  _used_caps |= used_caps;
 
 
   return true;
   return true;
 }
 }

+ 2 - 0
panda/src/gobj/shader.h

@@ -113,6 +113,7 @@ PUBLISHED:
   INLINE const std::string &get_text(ShaderType type = ST_none) const;
   INLINE const std::string &get_text(ShaderType type = ST_none) const;
   INLINE bool get_error_flag() const;
   INLINE bool get_error_flag() const;
   INLINE ShaderLanguage get_language() const;
   INLINE ShaderLanguage get_language() const;
+  INLINE int get_used_capabilities() const;
 
 
   INLINE bool has_fullpath() const;
   INLINE bool has_fullpath() const;
   INLINE const Filename &get_fullpath() const;
   INLINE const Filename &get_fullpath() const;
@@ -541,6 +542,7 @@ public:
   typedef pvector<COWPT(ShaderModule)> Modules;
   typedef pvector<COWPT(ShaderModule)> Modules;
   Modules _modules;
   Modules _modules;
   uint32_t _module_mask = 0;
   uint32_t _module_mask = 0;
+  int _used_caps = 0;
 
 
 protected:
 protected:
   ShaderFile _filename;
   ShaderFile _filename;

+ 45 - 0
panda/src/gobj/shaderType.cxx

@@ -81,6 +81,14 @@ std::ostream &operator << (std::ostream &out, ShaderType::ScalarType scalar_type
 }
 }
 
 
 #ifndef CPPPARSER
 #ifndef CPPPARSER
+/**
+ * Returns true if this type contains the given scalar type.
+ */
+bool ShaderType::Scalar::
+contains_scalar_type(ScalarType type) const {
+  return _scalar_type == type;
+}
+
 /**
 /**
  * If this is an array, vector or matrix of a scalar type, extracts the
  * If this is an array, vector or matrix of a scalar type, extracts the
  * dimensions.
  * dimensions.
@@ -114,6 +122,14 @@ compare_to_impl(const ShaderType &other) const {
        - (_scalar_type < other_scalar._scalar_type);
        - (_scalar_type < other_scalar._scalar_type);
 }
 }
 
 
+/**
+ * Returns true if this type contains the given scalar type.
+ */
+bool ShaderType::Vector::
+contains_scalar_type(ScalarType type) const {
+  return _scalar_type == type;
+}
+
 /**
 /**
  * If this is an array, vector or matrix of a scalar type, extracts the
  * If this is an array, vector or matrix of a scalar type, extracts the
  * dimensions.
  * dimensions.
@@ -150,6 +166,14 @@ compare_to_impl(const ShaderType &other) const {
        - (_num_components < other_vector._num_components);
        - (_num_components < other_vector._num_components);
 }
 }
 
 
+/**
+ * Returns true if this type contains the given scalar type.
+ */
+bool ShaderType::Matrix::
+contains_scalar_type(ScalarType type) const {
+  return _scalar_type == type;
+}
+
 /**
 /**
  * If this is an array, vector or matrix of a scalar type, extracts the
  * If this is an array, vector or matrix of a scalar type, extracts the
  * dimensions.
  * dimensions.
@@ -189,6 +213,19 @@ compare_to_impl(const ShaderType &other) const {
        - (_num_columns < other_matrix._num_columns);
        - (_num_columns < other_matrix._num_columns);
 }
 }
 
 
+/**
+ * Returns true if this type contains the given scalar type.
+ */
+bool ShaderType::Struct::
+contains_scalar_type(ScalarType type) const {
+  for (const Member &member : _members) {
+    if (member.type != nullptr && member.type->contains_scalar_type(type)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 /**
 /**
  *
  *
  */
  */
@@ -243,6 +280,14 @@ get_num_parameter_locations() const {
   return total;
   return total;
 }
 }
 
 
+/**
+ * Returns true if this type contains the given scalar type.
+ */
+bool ShaderType::Array::
+contains_scalar_type(ScalarType type) const {
+  return _element_type != nullptr && _element_type->contains_scalar_type(type);
+}
+
 /**
 /**
  * If this is an array, vector or matrix of a scalar type, extracts the
  * If this is an array, vector or matrix of a scalar type, extracts the
  * dimensions.
  * dimensions.

+ 6 - 0
panda/src/gobj/shaderType.h

@@ -69,6 +69,7 @@ PUBLISHED:
 
 
 public:
 public:
   virtual bool is_aggregate_type() const { return false; }
   virtual bool is_aggregate_type() const { return false; }
+  virtual bool contains_scalar_type(ScalarType type) const { return false; }
   virtual bool as_scalar_type(ScalarType &type,
   virtual bool as_scalar_type(ScalarType &type,
                               uint32_t &num_elements,
                               uint32_t &num_elements,
                               uint32_t &num_rows,
                               uint32_t &num_rows,
@@ -115,6 +116,7 @@ public:
   INLINE Scalar(ScalarType scalar_type);
   INLINE Scalar(ScalarType scalar_type);
 
 
   INLINE ScalarType get_scalar_type() const;
   INLINE ScalarType get_scalar_type() const;
+  virtual bool contains_scalar_type(ScalarType type) const override;
   virtual bool as_scalar_type(ScalarType &type, uint32_t &num_elements,
   virtual bool as_scalar_type(ScalarType &type, uint32_t &num_elements,
                               uint32_t &num_rows, uint32_t &num_columns) const override;
                               uint32_t &num_rows, uint32_t &num_columns) const override;
 
 
@@ -152,6 +154,7 @@ public:
   INLINE ScalarType get_scalar_type() const;
   INLINE ScalarType get_scalar_type() const;
   INLINE uint32_t get_num_components() const;
   INLINE uint32_t get_num_components() const;
 
 
+  virtual bool contains_scalar_type(ScalarType type) const override;
   virtual bool as_scalar_type(ScalarType &type, uint32_t &num_elements,
   virtual bool as_scalar_type(ScalarType &type, uint32_t &num_elements,
                               uint32_t &num_rows, uint32_t &num_columns) const override;
                               uint32_t &num_rows, uint32_t &num_columns) const override;
 
 
@@ -190,6 +193,7 @@ public:
   INLINE uint32_t get_num_rows() const;
   INLINE uint32_t get_num_rows() const;
   INLINE uint32_t get_num_columns() const;
   INLINE uint32_t get_num_columns() const;
 
 
+  virtual bool contains_scalar_type(ScalarType type) const override;
   virtual bool as_scalar_type(ScalarType &type, uint32_t &num_elements,
   virtual bool as_scalar_type(ScalarType &type, uint32_t &num_elements,
                               uint32_t &num_rows, uint32_t &num_columns) const override;
                               uint32_t &num_rows, uint32_t &num_columns) const override;
 
 
@@ -235,6 +239,7 @@ public:
   virtual int get_num_parameter_locations() const override;
   virtual int get_num_parameter_locations() const override;
 
 
   bool is_aggregate_type() const override { return true; }
   bool is_aggregate_type() const override { return true; }
+  virtual bool contains_scalar_type(ScalarType type) const override;
   const Struct *as_struct() const override { return this; }
   const Struct *as_struct() const override { return this; }
 
 
 PUBLISHED:
 PUBLISHED:
@@ -272,6 +277,7 @@ public:
   INLINE const ShaderType *get_element_type() const;
   INLINE const ShaderType *get_element_type() const;
   INLINE uint32_t get_num_elements() const;
   INLINE uint32_t get_num_elements() const;
 
 
+  virtual bool contains_scalar_type(ScalarType type) const override;
   virtual bool as_scalar_type(ScalarType &type, uint32_t &num_elements,
   virtual bool as_scalar_type(ScalarType &type, uint32_t &num_elements,
                               uint32_t &num_rows, uint32_t &num_columns) const override;
                               uint32_t &num_rows, uint32_t &num_columns) const override;
 
 

+ 8 - 0
panda/src/shaderpipeline/shaderModule.I

@@ -20,6 +20,14 @@ get_stage() const {
   return _stage;
   return _stage;
 }
 }
 
 
+/**
+ * Returns the set of capabilities used by this shader.
+ */
+INLINE int ShaderModule::
+get_used_capabilities() const {
+  return _used_caps;
+}
+
 /**
 /**
  * Returns the original filename that the module was compiled from.  This may
  * Returns the original filename that the module was compiled from.  This may
  * be empty.
  * be empty.

+ 87 - 0
panda/src/shaderpipeline/shaderModule.cxx

@@ -20,6 +20,23 @@ TypeHandle ShaderModule::_type_handle;
  */
  */
 ShaderModule::
 ShaderModule::
 ShaderModule(Stage stage) : _stage(stage) {
 ShaderModule(Stage stage) : _stage(stage) {
+  switch (stage) {
+  case Stage::tess_control:
+  case Stage::tess_evaluation:
+    _used_caps |= C_tessellation_shader;
+    break;
+
+  case Stage::geometry:
+    _used_caps |= C_geometry_shader;
+    break;
+
+  case Stage::compute:
+    _used_caps |= C_compute_shader;
+    break;
+
+  default:
+    break;
+  }
 }
 }
 
 
 /**
 /**
@@ -100,3 +117,73 @@ format_stage(Stage stage) {
 
 
   return "**invalid**";
   return "**invalid**";
 }
 }
+
+/**
+ * Outputs the given capabilities mask.
+ */
+void ShaderModule::
+output_capabilities(std::ostream &out, int caps) {
+  if (caps & C_integer) {
+    out << "integer ";
+  }
+  if (caps & C_texture_fetch) {
+    out << "texture_fetch ";
+  }
+  if (caps & C_buffer_texture) {
+    out << "buffer_texture ";
+  }
+  if (caps & C_vertex_id) {
+    out << "vertex_id ";
+  }
+  if (caps & C_round_even) {
+    out << "round_even ";
+  }
+  if (caps & C_instance_id) {
+    out << "instance_id ";
+  }
+  if (caps & C_geometry_shader) {
+    out << "geometry_shader ";
+  }
+  if (caps & C_primitive_id) {
+    out << "primitive_id ";
+  }
+  if (caps & C_bit_encoding) {
+    out << "bit_encoding ";
+  }
+  if (caps & C_double) {
+    out << "double ";
+  }
+  if (caps & C_cube_map_array) {
+    out << "cube_map_array ";
+  }
+  if (caps & C_tessellation_shader) {
+    out << "tessellation_shader ";
+  }
+  if (caps & C_sample_variables) {
+    out << "sample_variables ";
+  }
+  if (caps & C_extended_arithmetic) {
+    out << "extended_arithmetic ";
+  }
+  if (caps & C_texture_query_lod) {
+    out << "texture_query_lod ";
+  }
+  if (caps & C_image_load_store) {
+    out << "image_load_store ";
+  }
+  if (caps & C_compute_shader) {
+    out << "compute_shader ";
+  }
+  if (caps & C_texture_query_levels) {
+    out << "texture_query_levels ";
+  }
+  if (caps & C_enhanced_layouts) {
+    out << "enhanced_layouts ";
+  }
+  if (caps & C_derivative_control) {
+    out << "derivative_control ";
+  }
+  if (caps & C_texture_query_samples) {
+    out << "texture_query_samples ";
+  }
+}

+ 49 - 0
panda/src/shaderpipeline/shaderModule.h

@@ -61,6 +61,7 @@ public:
   virtual ~ShaderModule();
   virtual ~ShaderModule();
 
 
   INLINE Stage get_stage() const;
   INLINE Stage get_stage() const;
+  INLINE int get_used_capabilities() const;
 
 
   INLINE const Filename &get_source_filename() const;
   INLINE const Filename &get_source_filename() const;
   INLINE void set_source_filename(const Filename &);
   INLINE void set_source_filename(const Filename &);
@@ -90,7 +91,54 @@ PUBLISHED:
   virtual std::string get_ir() const=0;
   virtual std::string get_ir() const=0;
 
 
 public:
 public:
+  /**
+   * Indicates which features are used by the shader, which can be used by the
+   * driver to check whether cross-compilation is possible, or whether certain
+   * transformation steps may need to be applied.
+   */
+  enum Capabilities {
+    // GLSL 1.30
+    C_integer = 1 << 0,
+    C_texture_fetch = 1 << 1, // texelFetch, textureSize, etc.
+    C_buffer_texture = 1 << 2,
+    C_vertex_id = 1 << 3,
+    C_round_even = 1 << 4,
+
+    // GLSL 1.40
+    C_instance_id = 1 << 5,
+
+    // GLSL 1.50
+    C_geometry_shader = 1 << 6,
+    C_primitive_id = 1 << 7,
+
+    // GLSL 3.30 / ARB_shader_bit_encoding
+    C_bit_encoding = 1 << 8,
+
+    // GLSL 4.00
+    C_double = 1 << 9,
+    C_cube_map_array = 1 << 10,
+    C_tessellation_shader = 1 << 11,
+    C_sample_variables = 1 << 12,
+    C_extended_arithmetic = 1 << 13,
+    C_texture_query_lod = 1 << 14,
+
+    // GLSL 4.20
+    C_image_load_store = 1 << 15,
+
+    // GLSL 4.30
+    C_compute_shader = 1 << 16,
+    C_texture_query_levels = 1 << 17,
+
+    // GLSL 4.40 / ARB_enhanced_layouts
+    C_enhanced_layouts = 1 << 18,
+
+    // GLSL 4.50
+    C_derivative_control = 1 << 19,
+    C_texture_query_samples = 1 << 20,
+  };
+
   static std::string format_stage(Stage stage);
   static std::string format_stage(Stage stage);
+  static void output_capabilities(std::ostream &out, int capabilities);
 
 
   virtual void output(std::ostream &out) const;
   virtual void output(std::ostream &out) const;
 
 
@@ -105,6 +153,7 @@ protected:
   //std::pvector<Filename> _source_files;
   //std::pvector<Filename> _source_files;
   Filename _source_filename;
   Filename _source_filename;
   //time_t _source_modified = 0;
   //time_t _source_modified = 0;
+  int _used_caps = 0;
 
 
   typedef pvector<Variable> Variables;
   typedef pvector<Variable> Variables;
   Variables _inputs;
   Variables _inputs;

+ 169 - 1
panda/src/shaderpipeline/shaderModuleSpirV.cxx

@@ -75,6 +75,37 @@ ShaderModuleSpirV(Stage stage, std::vector<uint32_t> words) :
       else if (def._storage_class == SpvStorageClassUniformConstant) {
       else if (def._storage_class == SpvStorageClassUniformConstant) {
         _parameters.push_back(std::move(var));
         _parameters.push_back(std::move(var));
       }
       }
+
+      if (def._type->contains_scalar_type(ShaderType::ST_int) ||
+          def._type->contains_scalar_type(ShaderType::ST_uint)) {
+        _used_caps |= C_integer;
+      }
+    }
+    else if (def._dtype == DT_variable && def._used &&
+             def._storage_class == SpvStorageClassInput) {
+      // Built-in input variable.
+      switch (def._builtin) {
+      case SpvBuiltInVertexId:
+        _used_caps |= C_vertex_id;
+        break;
+
+      case SpvBuiltInInstanceId:
+        _used_caps |= C_instance_id;
+        break;
+
+      case SpvBuiltInPrimitiveId:
+        _used_caps |= C_primitive_id;
+        break;
+
+      case SpvBuiltInSampleId:
+      case SpvBuiltInSampleMask:
+      case SpvBuiltInSamplePosition:
+        _used_caps |= C_sample_variables;
+        break;
+
+      default:
+        break;
+      }
     }
     }
   }
   }
 
 
@@ -196,6 +227,19 @@ validate_header() const {
 bool ShaderModuleSpirV::
 bool ShaderModuleSpirV::
 parse_instruction(Definitions &defs, SpvOp opcode, const uint32_t *args, size_t nargs) {
 parse_instruction(Definitions &defs, SpvOp opcode, const uint32_t *args, size_t nargs) {
   switch (opcode) {
   switch (opcode) {
+  case SpvOpExtInstImport:
+    defs[args[0]].set_ext_inst((const char *)&args[1]);
+    break;
+
+  case SpvOpExtInst:
+    nassertr(defs[args[2]]._dtype == DT_ext_inst, false);
+    if (defs[args[2]]._name == "GLSL.std.450" && args[3] == 2) {
+      // We mark the use of the GLSL roundEven() function, which is not
+      // supported by HLSL and requires GLSL 1.30.
+      _used_caps |= C_round_even;
+    }
+    break;
+
   case SpvOpMemoryModel:
   case SpvOpMemoryModel:
     if (args[0] != SpvAddressingModelLogical) {
     if (args[0] != SpvAddressingModelLogical) {
       shader_cat.error()
       shader_cat.error()
@@ -231,6 +275,17 @@ parse_instruction(Definitions &defs, SpvOp opcode, const uint32_t *args, size_t
     }*/
     }*/
     break;
     break;
 
 
+  case SpvOpCapability:
+    switch ((SpvCapability)args[0]) {
+    case SpvCapabilityFloat64:
+      _used_caps |= C_double;
+      break;
+
+    case SpvCapabilityImageCubeArray:
+      _used_caps |= C_cube_map_array;
+      break;
+    }
+
   case SpvOpName:
   case SpvOpName:
     defs[args[0]].set_name((const char *)&args[1]);
     defs[args[0]].set_name((const char *)&args[1]);
     break;
     break;
@@ -259,7 +314,11 @@ parse_instruction(Definitions &defs, SpvOp opcode, const uint32_t *args, size_t
 
 
   case SpvOpTypeFloat:
   case SpvOpTypeFloat:
     {
     {
-      defs[args[0]].set_type(ShaderType::float_type);
+      if (nargs >= 2 && args[1] >= 64) {
+        defs[args[0]].set_type(ShaderType::double_type);
+      } else {
+        defs[args[0]].set_type(ShaderType::float_type);
+      }
     }
     }
     break;
     break;
 
 
@@ -318,6 +377,7 @@ parse_instruction(Definitions &defs, SpvOp opcode, const uint32_t *args, size_t
           texture_type = Texture::TT_cube_map;
           texture_type = Texture::TT_cube_map;
         }
         }
         break;
         break;
+
       case SpvDimRect:
       case SpvDimRect:
         shader_cat.error()
         shader_cat.error()
           << "imageRect shader inputs are not supported.\n";
           << "imageRect shader inputs are not supported.\n";
@@ -402,6 +462,13 @@ parse_instruction(Definitions &defs, SpvOp opcode, const uint32_t *args, size_t
     defs[args[1]].set_constant(defs[args[0]]._type, args + 2, nargs - 2);
     defs[args[1]].set_constant(defs[args[0]]._type, args + 2, nargs - 2);
     break;
     break;
 
 
+  case SpvOpFunctionCall:
+    // Mark all arguments as used.
+    for (size_t i = 3; i < nargs; ++i) {
+      defs[args[i]].mark_used();
+    }
+    break;
+
   case SpvOpVariable:
   case SpvOpVariable:
     {
     {
       const Definition &ptr = defs[args[0]];
       const Definition &ptr = defs[args[0]];
@@ -414,6 +481,44 @@ parse_instruction(Definitions &defs, SpvOp opcode, const uint32_t *args, size_t
     }
     }
     break;
     break;
 
 
+  case SpvOpImageTexelPointer:
+  case SpvOpLoad:
+  case SpvOpAccessChain:
+  case SpvOpInBoundsAccessChain:
+  case SpvOpPtrAccessChain:
+  case SpvOpCopyObject:
+  case SpvOpAtomicLoad:
+  case SpvOpAtomicExchange:
+  case SpvOpAtomicCompareExchange:
+  case SpvOpAtomicCompareExchangeWeak:
+  case SpvOpAtomicIIncrement:
+  case SpvOpAtomicIDecrement:
+  case SpvOpAtomicIAdd:
+  case SpvOpAtomicISub:
+  case SpvOpAtomicSMin:
+  case SpvOpAtomicUMin:
+  case SpvOpAtomicSMax:
+  case SpvOpAtomicUMax:
+  case SpvOpAtomicAnd:
+  case SpvOpAtomicOr:
+  case SpvOpAtomicXor:
+  case SpvOpAtomicFlagTestAndSet:
+    defs[args[2]].mark_used();
+    if (defs[args[2]]._type != nullptr &&
+        defs[args[2]]._type->contains_scalar_type(ShaderType::ST_double)) {
+      _used_caps |= C_double;
+    }
+    break;
+
+  case SpvOpCopyMemory:
+  case SpvOpCopyMemorySized:
+    defs[args[1]].mark_used();
+    if (defs[args[1]]._type != nullptr &&
+        defs[args[1]]._type->contains_scalar_type(ShaderType::ST_double)) {
+      _used_caps |= C_double;
+    }
+    break;
+
   case SpvOpDecorate:
   case SpvOpDecorate:
     switch ((SpvDecoration)args[1]) {
     switch ((SpvDecoration)args[1]) {
     case SpvDecorationBuiltIn:
     case SpvDecorationBuiltIn:
@@ -430,6 +535,50 @@ parse_instruction(Definitions &defs, SpvOp opcode, const uint32_t *args, size_t
       vars[args[0]]._set = args[2];
       vars[args[0]]._set = args[2];
     }*/
     }*/
     break;
     break;
+
+  case SpvOpImageRead:
+  case SpvOpImageWrite:
+    _used_caps |= C_image_load_store;
+    break;
+
+  case SpvOpImageFetch:
+  case SpvOpImageQuerySizeLod:
+  case SpvOpImageQuerySize:
+    _used_caps |= C_texture_fetch;
+    break;
+
+  case SpvOpImageQueryLod:
+    _used_caps |= C_texture_query_lod;
+    break;
+
+  case SpvOpImageQueryLevels:
+    _used_caps |= C_texture_query_levels;
+    break;
+
+  case SpvOpImageQuerySamples:
+    _used_caps |= C_texture_query_samples;
+    break;
+
+  case SpvOpBitcast:
+    _used_caps |= C_bit_encoding;
+    break;
+
+
+  case SpvOpIAddCarry:
+  case SpvOpISubBorrow:
+  case SpvOpUMulExtended:
+  case SpvOpSMulExtended:
+    _used_caps |= C_extended_arithmetic;
+    break;
+
+  case SpvOpDPdxFine:
+  case SpvOpDPdyFine:
+  case SpvOpFwidthFine:
+  case SpvOpDPdxCoarse:
+  case SpvOpDPdyCoarse:
+  case SpvOpFwidthCoarse:
+    _used_caps |= C_derivative_control;
+    break;
   }
   }
 
 
   return true;
   return true;
@@ -698,6 +847,7 @@ flatten_struct(Definitions &defs, uint32_t type_id) {
 
 
     case SpvOpAccessChain:
     case SpvOpAccessChain:
     case SpvOpInBoundsAccessChain:
     case SpvOpInBoundsAccessChain:
+    case SpvOpPtrAccessChain:
       if (deleted_ids.count(op.args[2])) {
       if (deleted_ids.count(op.args[2])) {
         uint32_t index = defs[op.args[3]]._constant;
         uint32_t index = defs[op.args[3]]._constant;
         if (op.nargs > 4) {
         if (op.nargs > 4) {
@@ -724,6 +874,7 @@ flatten_struct(Definitions &defs, uint32_t type_id) {
       break;
       break;
 
 
     case SpvOpCopyMemory:
     case SpvOpCopyMemory:
+    case SpvOpCopyMemorySized:
       // Shouldn't be copying the struct directly.
       // Shouldn't be copying the struct directly.
       nassertv(!deleted_ids.count(op.args[1]));
       nassertv(!deleted_ids.count(op.args[1]));
 
 
@@ -926,6 +1077,23 @@ set_constant(const ShaderType *type, const uint32_t *words, uint32_t nwords) {
   }
   }
 }
 }
 
 
+/**
+ * Called when OpExtInstImport is encountered in the SPIR-V instruction stream.
+ */
+void ShaderModuleSpirV::Definition::
+set_ext_inst(const char *name) {
+  _dtype = DT_ext_inst;
+  _name.assign(name);
+}
+
+/**
+ * Marks the variable as having been loaded.
+ */
+void ShaderModuleSpirV::Definition::
+mark_used() {
+  _used = true;
+}
+
 /**
 /**
  * Clears this definition, in case it has just been removed.
  * Clears this definition, in case it has just been removed.
  */
  */

+ 5 - 0
panda/src/shaderpipeline/shaderModuleSpirV.h

@@ -110,6 +110,7 @@ protected:
     DT_type_pointer,
     DT_type_pointer,
     DT_variable,
     DT_variable,
     DT_constant,
     DT_constant,
+    DT_ext_inst,
   };
   };
 
 
   /**
   /**
@@ -124,6 +125,7 @@ protected:
     SpvBuiltIn _builtin = SpvBuiltInMax;
     SpvBuiltIn _builtin = SpvBuiltInMax;
     uint32_t _constant = 0;
     uint32_t _constant = 0;
     vector_string _member_names;
     vector_string _member_names;
+    bool _used = false;
 
 
     // Only defined for DT_variable.
     // Only defined for DT_variable.
     SpvStorageClass _storage_class;
     SpvStorageClass _storage_class;
@@ -135,6 +137,9 @@ protected:
     void set_type_pointer(SpvStorageClass storage_class, const ShaderType *type);
     void set_type_pointer(SpvStorageClass storage_class, const ShaderType *type);
     void set_variable(const ShaderType *type, SpvStorageClass storage_class);
     void set_variable(const ShaderType *type, SpvStorageClass storage_class);
     void set_constant(const ShaderType *type, const uint32_t *words, uint32_t nwords);
     void set_constant(const ShaderType *type, const uint32_t *words, uint32_t nwords);
+    void set_ext_inst(const char *name);
+
+    void mark_used();
 
 
     void clear();
     void clear();
   };
   };