Browse Source

Merge branch 'shaderpipeline' into vulkan

rdb 1 year ago
parent
commit
cc87cb4a6c
38 changed files with 1255 additions and 858 deletions
  1. 9 0
      panda/src/display/shaderInputBinding_impls.I
  2. 157 87
      panda/src/display/shaderInputBinding_impls.cxx
  3. 38 14
      panda/src/display/shaderInputBinding_impls.h
  4. 12 10
      panda/src/dxgsg9/dxShaderContext9.cxx
  5. 4 0
      panda/src/gles2gsg/gles2gsg.h
  6. 12 2
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  7. 2 1
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  8. 368 150
      panda/src/glstuff/glShaderContext_src.cxx
  9. 17 14
      panda/src/glstuff/glShaderContext_src.h
  10. 1 1
      panda/src/gobj/shader.I
  11. 35 31
      panda/src/gobj/shader.cxx
  12. 4 4
      panda/src/gobj/shader.h
  13. 0 6
      panda/src/gobj/shaderBuffer.I
  14. 12 12
      panda/src/gobj/shaderEnums.cxx
  15. 6 6
      panda/src/gobj/shaderEnums.h
  16. 2 2
      panda/src/gobj/shaderInputBinding.I
  17. 12 1
      panda/src/gobj/shaderInputBinding.cxx
  18. 5 1
      panda/src/gobj/shaderInputBinding.h
  19. 4 4
      panda/src/gobj/shaderModule.cxx
  20. 9 1
      panda/src/gobj/shaderType.I
  21. 124 57
      panda/src/gobj/shaderType.cxx
  22. 22 14
      panda/src/gobj/shaderType.h
  23. 1 342
      panda/src/pgraph/shaderAttrib.cxx
  24. 0 6
      panda/src/pgraph/shaderAttrib.h
  25. 9 9
      panda/src/shaderpipeline/shaderCompilerGlslang.cxx
  26. 43 9
      panda/src/shaderpipeline/shaderModuleSpirV.cxx
  27. 93 0
      panda/src/shaderpipeline/spirVReplaceVariableTypePass.cxx
  28. 3 1
      panda/src/shaderpipeline/spirVReplaceVariableTypePass.h
  29. 86 43
      panda/src/shaderpipeline/spirVResultDatabase.cxx
  30. 7 5
      panda/src/shaderpipeline/spirVResultDatabase.h
  31. 53 5
      panda/src/shaderpipeline/spirVTransformPass.cxx
  32. 2 0
      panda/src/shaderpipeline/spirVTransformPass.h
  33. 59 2
      panda/src/shaderpipeline/spirVTransformer.cxx
  34. 2 0
      panda/src/shaderpipeline/spirVTransformer.h
  35. 6 6
      panda/src/vulkandisplay/vulkanGraphicsStateGuardian.cxx
  36. 6 6
      panda/src/vulkandisplay/vulkanShaderContext.cxx
  37. 2 2
      panda/src/vulkandisplay/vulkanShaderContext.h
  38. 28 4
      tests/display/test_glsl_shader.py

+ 9 - 0
panda/src/display/shaderInputBinding_impls.I

@@ -82,6 +82,15 @@ ShaderTextureBinding(CPT(InternalName) input, Texture::TextureType desired_type)
   _desired_type(desired_type) {
   _desired_type(desired_type) {
 }
 }
 
 
+/**
+ *
+ */
+INLINE ShaderBufferBinding::
+ShaderBufferBinding(CPT(InternalName) input, size_t min_size) :
+  _input(std::move(input)),
+  _min_size(min_size) {
+}
+
 /**
 /**
  *
  *
  */
  */

+ 157 - 87
panda/src/display/shaderInputBinding_impls.cxx

@@ -275,7 +275,7 @@ check_light_struct_member(const string &name, const ShaderType *type) {
     return false;
     return false;
   }
   }
 
 
-  const ::ShaderType::Matrix *matrix = type->as_matrix();
+  const ShaderType::Matrix *matrix = type->as_matrix();
   if (matrix != nullptr) {
   if (matrix != nullptr) {
     if (matrix->get_num_rows() != num_rows ||
     if (matrix->get_num_rows() != num_rows ||
         matrix->get_num_columns() < min_cols ||
         matrix->get_num_columns() < min_cols ||
@@ -288,7 +288,7 @@ check_light_struct_member(const string &name, const ShaderType *type) {
   }
   }
   else {
   else {
     uint32_t num_components = 1;
     uint32_t num_components = 1;
-    if (const ::ShaderType::Vector *vector = type->as_vector()) {
+    if (const ShaderType::Vector *vector = type->as_vector()) {
       num_components = vector->get_num_components();
       num_components = vector->get_num_components();
     }
     }
     else if (type->as_scalar() == nullptr) {
     else if (type->as_scalar() == nullptr) {
@@ -315,6 +315,13 @@ make_shader_input(const ShaderType *type, CPT_InternalName name) {
     Texture::TextureType desired_type = image->get_texture_type();
     Texture::TextureType desired_type = image->get_texture_type();
     return new ShaderTextureBinding(std::move(name), desired_type);
     return new ShaderTextureBinding(std::move(name), desired_type);
   }
   }
+  else if (const ShaderType::StorageBuffer *buffer = type->as_storage_buffer()) {
+    size_t min_size = 0;
+    if (const ShaderType *contained_type = buffer->get_contained_type()) {
+      min_size = contained_type->get_size_bytes();
+    }
+    return new ShaderBufferBinding(std::move(name), min_size);
+  }
   else if (const ShaderType::Matrix *matrix = type->as_matrix()) {
   else if (const ShaderType::Matrix *matrix = type->as_matrix()) {
     // For historical reasons, we handle non-arrayed matrices differently,
     // For historical reasons, we handle non-arrayed matrices differently,
     // which has the additional feature that they can be passed in as a
     // which has the additional feature that they can be passed in as a
@@ -324,23 +331,23 @@ make_shader_input(const ShaderType *type, CPT_InternalName name) {
       uint32_t num_rows = matrix->get_num_rows();
       uint32_t num_rows = matrix->get_num_rows();
       if (num_rows == 4) {
       if (num_rows == 4) {
         if (matrix->get_scalar_type() == ShaderType::ST_double) {
         if (matrix->get_scalar_type() == ShaderType::ST_double) {
-          return ShaderInputBinding::make_data(dep, [=](const State &state, void *into, bool pad_rows) {
+          return ShaderInputBinding::make_data(dep, [=](const State &state, void *into, bool packed) {
             state.gsg->get_target_shader_attrib()->get_shader_input_matrix(name, *(LMatrix4d *)into);
             state.gsg->get_target_shader_attrib()->get_shader_input_matrix(name, *(LMatrix4d *)into);
           });
           });
         } else {
         } else {
-          return ShaderInputBinding::make_data(dep, [=](const State &state, void *into, bool pad_rows) {
+          return ShaderInputBinding::make_data(dep, [=](const State &state, void *into, bool packed) {
             state.gsg->get_target_shader_attrib()->get_shader_input_matrix(name, *(LMatrix4f *)into);
             state.gsg->get_target_shader_attrib()->get_shader_input_matrix(name, *(LMatrix4f *)into);
           });
           });
         }
         }
       } else {
       } else {
         if (matrix->get_scalar_type() == ShaderType::ST_double) {
         if (matrix->get_scalar_type() == ShaderType::ST_double) {
-          return ShaderInputBinding::make_data(dep, [=](const State &state, void *into, bool pad_rows) {
+          return ShaderInputBinding::make_data(dep, [=](const State &state, void *into, bool packed) {
             LMatrix4d tmp;
             LMatrix4d tmp;
             state.gsg->get_target_shader_attrib()->get_shader_input_matrix(name, tmp);
             state.gsg->get_target_shader_attrib()->get_shader_input_matrix(name, tmp);
             memcpy(into, tmp.get_data(), num_rows * sizeof(double) * 4);
             memcpy(into, tmp.get_data(), num_rows * sizeof(double) * 4);
           });
           });
         } else {
         } else {
-          return ShaderInputBinding::make_data(dep, [=](const State &state, void *into, bool pad_rows) {
+          return ShaderInputBinding::make_data(dep, [=](const State &state, void *into, bool packed) {
             LMatrix4f tmp;
             LMatrix4f tmp;
             state.gsg->get_target_shader_attrib()->get_shader_input_matrix(name, tmp);
             state.gsg->get_target_shader_attrib()->get_shader_input_matrix(name, tmp);
             memcpy(into, tmp.get_data(), num_rows * sizeof(float) * 4);
             memcpy(into, tmp.get_data(), num_rows * sizeof(float) * 4);
@@ -353,20 +360,20 @@ make_shader_input(const ShaderType *type, CPT_InternalName name) {
       if (num_rows == 3) {
       if (num_rows == 3) {
         // Short-cut for most common case
         // Short-cut for most common case
         if (matrix->get_scalar_type() == ShaderType::ST_double) {
         if (matrix->get_scalar_type() == ShaderType::ST_double) {
-          return ShaderInputBinding::make_data(dep, [=](const State &state, void *into, bool pad_rows) {
+          return ShaderInputBinding::make_data(dep, [=](const State &state, void *into, bool packed) {
             LMatrix4d tmp;
             LMatrix4d tmp;
             state.gsg->get_target_shader_attrib()->get_shader_input_matrix(name, tmp);
             state.gsg->get_target_shader_attrib()->get_shader_input_matrix(name, tmp);
-            if (pad_rows) {
+            if (!packed) {
               memcpy(into, tmp.get_data(), sizeof(double) * 4 * 3);
               memcpy(into, tmp.get_data(), sizeof(double) * 4 * 3);
             } else {
             } else {
               *((LMatrix3d *)into) = tmp.get_upper_3();
               *((LMatrix3d *)into) = tmp.get_upper_3();
             }
             }
           });
           });
         } else {
         } else {
-          return ShaderInputBinding::make_data(dep, [=](const State &state, void *into, bool pad_rows) {
+          return ShaderInputBinding::make_data(dep, [=](const State &state, void *into, bool packed) {
             LMatrix4f tmp;
             LMatrix4f tmp;
             state.gsg->get_target_shader_attrib()->get_shader_input_matrix(name, tmp);
             state.gsg->get_target_shader_attrib()->get_shader_input_matrix(name, tmp);
-            if (pad_rows) {
+            if (!packed) {
               memcpy(into, tmp.get_data(), sizeof(float) * 4 * 3);
               memcpy(into, tmp.get_data(), sizeof(float) * 4 * 3);
             } else {
             } else {
               *((LMatrix3f *)into) = tmp.get_upper_3();
               *((LMatrix3f *)into) = tmp.get_upper_3();
@@ -375,10 +382,10 @@ make_shader_input(const ShaderType *type, CPT_InternalName name) {
         }
         }
       } else {
       } else {
         if (matrix->get_scalar_type() == ShaderType::ST_double) {
         if (matrix->get_scalar_type() == ShaderType::ST_double) {
-          return ShaderInputBinding::make_data(dep, [=](const State &state, void *into, bool pad_rows) {
+          return ShaderInputBinding::make_data(dep, [=](const State &state, void *into, bool packed) {
             LMatrix4d tmp;
             LMatrix4d tmp;
             state.gsg->get_target_shader_attrib()->get_shader_input_matrix(name, tmp);
             state.gsg->get_target_shader_attrib()->get_shader_input_matrix(name, tmp);
-            if (pad_rows) {
+            if (!packed) {
               memcpy(into, tmp.get_data(), num_rows * sizeof(double) * 4);
               memcpy(into, tmp.get_data(), num_rows * sizeof(double) * 4);
             } else {
             } else {
               for (uint32_t i = 0; i < num_rows; ++i) {
               for (uint32_t i = 0; i < num_rows; ++i) {
@@ -387,10 +394,10 @@ make_shader_input(const ShaderType *type, CPT_InternalName name) {
             }
             }
           });
           });
         } else {
         } else {
-          return ShaderInputBinding::make_data(dep, [=](const State &state, void *into, bool pad_rows) {
+          return ShaderInputBinding::make_data(dep, [=](const State &state, void *into, bool packed) {
             LMatrix4f tmp;
             LMatrix4f tmp;
             state.gsg->get_target_shader_attrib()->get_shader_input_matrix(name, tmp);
             state.gsg->get_target_shader_attrib()->get_shader_input_matrix(name, tmp);
-            if (pad_rows) {
+            if (!packed) {
               memcpy(into, tmp.get_data(), num_rows * sizeof(float) * 4);
               memcpy(into, tmp.get_data(), num_rows * sizeof(float) * 4);
             } else {
             } else {
               for (uint32_t i = 0; i < num_rows; ++i) {
               for (uint32_t i = 0; i < num_rows; ++i) {
@@ -537,7 +544,7 @@ make_transform_table(const ShaderType *type, bool transpose) {
 
 
   if (num_rows == 4) {
   if (num_rows == 4) {
     return ShaderInputBinding::make_data(Shader::D_vertex_data,
     return ShaderInputBinding::make_data(Shader::D_vertex_data,
-                                         [=](const State &state, void *into, bool pad_rows) {
+                                         [=](const State &state, void *into, bool packed) {
 
 
       const TransformTable *table = state.gsg->get_data_reader()->get_transform_table();
       const TransformTable *table = state.gsg->get_data_reader()->get_transform_table();
       LMatrix4f *matrices = (LMatrix4f *)into;
       LMatrix4f *matrices = (LMatrix4f *)into;
@@ -564,7 +571,7 @@ make_transform_table(const ShaderType *type, bool transpose) {
     nassertr(transpose, nullptr);
     nassertr(transpose, nullptr);
 
 
     return ShaderInputBinding::make_data(Shader::D_vertex_data,
     return ShaderInputBinding::make_data(Shader::D_vertex_data,
-                                         [=](const State &state, void *into, bool pad_rows) {
+                                         [=](const State &state, void *into, bool packed) {
 
 
       const TransformTable *table = state.gsg->get_data_reader()->get_transform_table();
       const TransformTable *table = state.gsg->get_data_reader()->get_transform_table();
       LVecBase4f *vectors = (LVecBase4f *)into;
       LVecBase4f *vectors = (LVecBase4f *)into;
@@ -608,7 +615,7 @@ make_slider_table(const ShaderType *type) {
   nassertr(element_type == ShaderType::float_type, nullptr);
   nassertr(element_type == ShaderType::float_type, nullptr);
 
 
   return ShaderInputBinding::make_data(Shader::D_vertex_data,
   return ShaderInputBinding::make_data(Shader::D_vertex_data,
-                                       [=](const State &state, void *into, bool pad_rows) {
+                                       [=](const State &state, void *into, bool packed) {
 
 
     const SliderTable *table = state.gsg->get_data_reader()->get_slider_table();
     const SliderTable *table = state.gsg->get_data_reader()->get_slider_table();
     float *sliders = (float *)into;
     float *sliders = (float *)into;
@@ -630,13 +637,13 @@ static ShaderInputBinding *
 make_frame_time(const ShaderType *type) {
 make_frame_time(const ShaderType *type) {
   if (type == ShaderType::float_type) {
   if (type == ShaderType::float_type) {
     return ShaderInputBinding::make_data(Shader::D_frame,
     return ShaderInputBinding::make_data(Shader::D_frame,
-                                         [](const State &state, void *into, bool pad_rows) {
+                                         [](const State &state, void *into, bool packed) {
       *(float *)into = ClockObject::get_global_clock()->get_frame_time();
       *(float *)into = ClockObject::get_global_clock()->get_frame_time();
     });
     });
   }
   }
   if (type == ShaderType::double_type) {
   if (type == ShaderType::double_type) {
     return ShaderInputBinding::make_data(Shader::D_frame,
     return ShaderInputBinding::make_data(Shader::D_frame,
-                                         [](const State &state, void *into, bool pad_rows) {
+                                         [](const State &state, void *into, bool packed) {
       *(double *)into = ClockObject::get_global_clock()->get_frame_time();
       *(double *)into = ClockObject::get_global_clock()->get_frame_time();
     });
     });
   }
   }
@@ -649,7 +656,7 @@ make_frame_time(const ShaderType *type) {
 static ShaderInputBinding *
 static ShaderInputBinding *
 make_color(const ShaderType *type) {
 make_color(const ShaderType *type) {
   return ShaderInputBinding::make_data(Shader::D_color,
   return ShaderInputBinding::make_data(Shader::D_color,
-                                       [](const State &state, void *into, bool pad_rows) {
+                                       [](const State &state, void *into, bool packed) {
 
 
     const ColorAttrib *target_color = (const ColorAttrib *)
     const ColorAttrib *target_color = (const ColorAttrib *)
       state.gsg->get_target_state()->get_attrib_def(ColorAttrib::get_class_slot());
       state.gsg->get_target_state()->get_attrib_def(ColorAttrib::get_class_slot());
@@ -672,7 +679,7 @@ make_color_scale(const ShaderType *type) {
       return Shader::D_colorscale;
       return Shader::D_colorscale;
     }
     }
 
 
-    virtual void fetch_data(const State &state, void *into, bool pad_rows) const override final {
+    virtual void fetch_data(const State &state, void *into, bool packed) const override final {
       const ColorScaleAttrib *target_color_scale = (const ColorScaleAttrib *)
       const ColorScaleAttrib *target_color_scale = (const ColorScaleAttrib *)
         state.gsg->get_target_state()->get_attrib_def(ColorScaleAttrib::get_class_slot());
         state.gsg->get_target_state()->get_attrib_def(ColorScaleAttrib::get_class_slot());
       if (!target_color_scale->is_identity()) {
       if (!target_color_scale->is_identity()) {
@@ -743,7 +750,7 @@ make_texture_matrix(const ShaderType *type, size_t index, bool inverse, bool tra
   }
   }
 
 
   return ShaderInputBinding::make_data(Shader::D_tex_matrix,
   return ShaderInputBinding::make_data(Shader::D_tex_matrix,
-                                       [=](const State &state, void *into, bool pad_rows) {
+                                       [=](const State &state, void *into, bool packed) {
 
 
     const TexMatrixAttrib *tma;
     const TexMatrixAttrib *tma;
     const TextureAttrib *ta;
     const TextureAttrib *ta;
@@ -822,7 +829,7 @@ make_fog(const ShaderType *type) {
   }
   }
 
 
   return ShaderInputBinding::make_data(Shader::D_fog | Shader::D_frame,
   return ShaderInputBinding::make_data(Shader::D_fog | Shader::D_frame,
-                                       [=](const State &state, void *into, bool pad_rows) {
+                                       [=](const State &state, void *into, bool packed) {
 
 
     LVecBase4f color(1, 1, 1, 1);
     LVecBase4f color(1, 1, 1, 1);
     PN_stdfloat density = 0, start = 1, end = 1, scale = 1;
     PN_stdfloat density = 0, start = 1, end = 1, scale = 1;
@@ -922,7 +929,7 @@ make_material(const ShaderType *type) {
   }
   }
 
 
   return ShaderInputBinding::make_data(Shader::D_material | Shader::D_frame,
   return ShaderInputBinding::make_data(Shader::D_material | Shader::D_frame,
-                                       [=](const State &state, void *into, bool pad_rows) {
+                                       [=](const State &state, void *into, bool packed) {
 
 
     LVecBase4f base_color(0, 0, 0, 0);
     LVecBase4f base_color(0, 0, 0, 0);
     LVecBase4f ambient(1, 1, 1, 1);
     LVecBase4f ambient(1, 1, 1, 1);
@@ -967,7 +974,7 @@ make_material(const ShaderType *type) {
 static ShaderInputBinding *
 static ShaderInputBinding *
 make_light_ambient(const ShaderType *type) {
 make_light_ambient(const ShaderType *type) {
   return ShaderInputBinding::make_data(Shader::D_frame | Shader::D_light,
   return ShaderInputBinding::make_data(Shader::D_frame | Shader::D_light,
-                                       [](const State &state, void *into, bool pad_rows) {
+                                       [](const State &state, void *into, bool packed) {
     const LightAttrib *target_light;
     const LightAttrib *target_light;
     if (state.gsg->get_target_state()->get_attrib(target_light) && target_light->has_any_on_light()) {
     if (state.gsg->get_target_state()->get_attrib(target_light) && target_light->has_any_on_light()) {
       *(LVecBase4f *)into = LCAST(float, target_light->get_ambient_contribution());
       *(LVecBase4f *)into = LCAST(float, target_light->get_ambient_contribution());
@@ -1001,17 +1008,17 @@ setup(Shader *shader) {
  * Fetches the part of the shader input that is plain numeric data.
  * Fetches the part of the shader input that is plain numeric data.
  */
  */
 void ShaderMatrixBinding::
 void ShaderMatrixBinding::
-fetch_data(const State &state, void *into, bool pad_rows) const {
+fetch_data(const State &state, void *into, bool packed) const {
   LMatrix4f m = LCAST(float, state.matrix_cache[_cache_index]);
   LMatrix4f m = LCAST(float, state.matrix_cache[_cache_index]);
   if (_transpose) {
   if (_transpose) {
     m.transpose_in_place();
     m.transpose_in_place();
   }
   }
-  if (pad_rows || _num_cols == 4) {
-    memcpy(into, m.get_data(), _num_cols * 4 * sizeof(float));
-  } else {
+  if (packed && _num_cols != 4) {
     for (size_t i = 0; i < _num_rows; ++i) {
     for (size_t i = 0; i < _num_rows; ++i) {
       memcpy((float *)into + i * _num_cols, m.get_data() + i * 4, _num_cols * sizeof(float));
       memcpy((float *)into + i * _num_cols, m.get_data() + i * 4, _num_cols * sizeof(float));
     }
     }
+  } else {
+    memcpy(into, m.get_data(), _num_cols * 4 * sizeof(float));
   }
   }
 }
 }
 
 
@@ -1039,19 +1046,19 @@ setup(Shader *shader) {
  * Fetches the part of the shader input that is plain numeric data.
  * Fetches the part of the shader input that is plain numeric data.
  */
  */
 void ShaderMatrixComposeBinding::
 void ShaderMatrixComposeBinding::
-fetch_data(const State &state, void *into, bool pad_rows) const {
+fetch_data(const State &state, void *into, bool packed) const {
   LMatrix4f m;
   LMatrix4f m;
   m.multiply(LCAST(float, state.matrix_cache[_cache_index0]),
   m.multiply(LCAST(float, state.matrix_cache[_cache_index0]),
              LCAST(float, state.matrix_cache[_cache_index1]));
              LCAST(float, state.matrix_cache[_cache_index1]));
   if (_transpose) {
   if (_transpose) {
     m.transpose_in_place();
     m.transpose_in_place();
   }
   }
-  if (pad_rows || _num_cols == 4) {
-    memcpy(into, m.get_data(), _num_rows * 4 * sizeof(float));
-  } else {
+  if (packed && _num_cols != 4) {
     for (size_t i = 0; i < _num_rows; ++i) {
     for (size_t i = 0; i < _num_rows; ++i) {
       memcpy((float *)into + i * _num_cols, m.get_data() + i * 4, _num_cols * sizeof(float));
       memcpy((float *)into + i * _num_cols, m.get_data() + i * 4, _num_cols * sizeof(float));
     }
     }
+  } else {
+    memcpy(into, m.get_data(), _num_rows * 4 * sizeof(float));
   }
   }
 }
 }
 
 
@@ -1091,7 +1098,7 @@ setup(Shader *shader) {
  * Fetches the part of the shader input that is plain numeric data.
  * Fetches the part of the shader input that is plain numeric data.
  */
  */
 void ShaderPointParamsBinding::
 void ShaderPointParamsBinding::
-fetch_data(const State &state, void *into, bool pad_rows) const {
+fetch_data(const State &state, void *into, bool packed) const {
   const RenderModeAttrib *target_render_mode;
   const RenderModeAttrib *target_render_mode;
   state.gsg->get_target_state()->get_attrib_def(target_render_mode);
   state.gsg->get_target_state()->get_attrib_def(target_render_mode);
 
 
@@ -1132,7 +1139,7 @@ setup(Shader *shader) {
  * Fetches the part of the shader input that is plain numeric data.
  * Fetches the part of the shader input that is plain numeric data.
  */
  */
 void ShaderPackedLightBinding::
 void ShaderPackedLightBinding::
-fetch_data(const State &state, void *into, bool pad_rows) const {
+fetch_data(const State &state, void *into, bool packed) const {
   // The light matrix contains COLOR, ATTENUATION, VIEWVECTOR, POSITION
   // The light matrix contains COLOR, ATTENUATION, VIEWVECTOR, POSITION
   LVecBase4f *data = (LVecBase4f *)into;
   LVecBase4f *data = (LVecBase4f *)into;
 
 
@@ -1220,7 +1227,7 @@ setup(Shader *shader) {
  * Fetches the part of the shader input that is plain numeric data.
  * Fetches the part of the shader input that is plain numeric data.
  */
  */
 void ShaderLegacyDirectionalLightBinding::
 void ShaderLegacyDirectionalLightBinding::
-fetch_data(const State &state, void *into, bool pad_rows) const {
+fetch_data(const State &state, void *into, bool packed) const {
   // The dlight matrix contains COLOR, SPECULAR, DIRECTION, PSEUDOHALFANGLE
   // The dlight matrix contains COLOR, SPECULAR, DIRECTION, PSEUDOHALFANGLE
   const NodePath &np = state.gsg->get_target_shader_attrib()->get_shader_input_nodepath(_input);
   const NodePath &np = state.gsg->get_target_shader_attrib()->get_shader_input_nodepath(_input);
   nassertv(!np.is_empty());
   nassertv(!np.is_empty());
@@ -1250,7 +1257,7 @@ fetch_data(const State &state, void *into, bool pad_rows) const {
  * Fetches the part of the shader input that is plain numeric data.
  * Fetches the part of the shader input that is plain numeric data.
  */
  */
 void ShaderLegacyPointLightBinding::
 void ShaderLegacyPointLightBinding::
-fetch_data(const State &state, void *into, bool pad_rows) const {
+fetch_data(const State &state, void *into, bool packed) const {
   // The plight matrix contains COLOR, SPECULAR, POINT, ATTENUATION
   // The plight matrix contains COLOR, SPECULAR, POINT, ATTENUATION
   const NodePath &np = state.gsg->get_target_shader_attrib()->get_shader_input_nodepath(_input);
   const NodePath &np = state.gsg->get_target_shader_attrib()->get_shader_input_nodepath(_input);
   nassertv(!np.is_empty());
   nassertv(!np.is_empty());
@@ -1277,7 +1284,7 @@ fetch_data(const State &state, void *into, bool pad_rows) const {
  * Fetches the part of the shader input that is plain numeric data.
  * Fetches the part of the shader input that is plain numeric data.
  */
  */
 void ShaderLegacySpotlightBinding::
 void ShaderLegacySpotlightBinding::
-fetch_data(const State &state, void *into, bool pad_rows) const {
+fetch_data(const State &state, void *into, bool packed) const {
   // The slight matrix contains COLOR, SPECULAR, POINT, DIRECTION
   // The slight matrix contains COLOR, SPECULAR, POINT, DIRECTION
   const NodePath &np = state.gsg->get_target_shader_attrib()->get_shader_input_nodepath(_input);
   const NodePath &np = state.gsg->get_target_shader_attrib()->get_shader_input_nodepath(_input);
   nassertv(!np.is_empty());
   nassertv(!np.is_empty());
@@ -1418,7 +1425,7 @@ setup(Shader *shader) {
  * Fetches the part of the shader input that is plain numeric data.
  * Fetches the part of the shader input that is plain numeric data.
  */
  */
 void ShaderLightStructBinding::
 void ShaderLightStructBinding::
-fetch_data(const State &state, void *into, bool pad_rows) const {
+fetch_data(const State &state, void *into, bool packed) const {
   if (_input != nullptr) {
   if (_input != nullptr) {
     // Fetch shader input.
     // Fetch shader input.
     if (state.gsg->get_target_shader_attrib()->has_shader_input(_input)) {
     if (state.gsg->get_target_shader_attrib()->has_shader_input(_input)) {
@@ -1807,6 +1814,45 @@ fetch_texture_image(const State &state, ResourceId resource_id, ShaderType::Acce
   return tex;
   return tex;
 }
 }
 
 
+/**
+ * Returns a mask indicating which state changes should cause the parameter to
+ * be respecified.
+ */
+int ShaderBufferBinding::
+get_state_dep() const {
+  // We don't specify D_frame, because we don't (yet) support updating shader
+  // buffers from the CPU.
+  return Shader::D_frame | Shader::D_shader_inputs;
+}
+
+/**
+ * Returns an opaque resource identifier that can later be used to fetch the
+ * nth resource, which is of the given type.
+ */
+ShaderInputBinding::ResourceId ShaderBufferBinding::
+get_resource_id(int index, const ShaderType *type) const {
+  return (ResourceId)_input.p();
+}
+
+/**
+ * Fetches the shader buffer associated with the given resource identifier,
+ * which was previously returned by get_resource_id.
+ */
+PT(ShaderBuffer) ShaderBufferBinding::
+fetch_shader_buffer(const State &state, ResourceId resource_id) const {
+  const InternalName *name = (const InternalName *)resource_id;
+  PT(ShaderBuffer) buffer = state.gsg->get_target_shader_attrib()->get_shader_input_buffer(name);
+#ifndef NDEBUG
+  if (!_shown_error && buffer->get_data_size_bytes() < _min_size) {
+    _shown_error = true;
+    shader_cat.error()
+      << *buffer << " is too small for shader input " << *name
+      << " (expected at least " << _min_size << " bytes)\n";
+  }
+#endif
+  return buffer;
+}
+
 /**
 /**
  * Returns a mask indicating which state changes should cause the parameter to
  * Returns a mask indicating which state changes should cause the parameter to
  * be respecified.
  * be respecified.
@@ -1823,15 +1869,15 @@ get_state_dep() const {
  * Fetches the part of the shader input that is plain numeric data.
  * Fetches the part of the shader input that is plain numeric data.
  */
  */
 void ShaderFloatBinding::
 void ShaderFloatBinding::
-fetch_data(const State &state, void *into, bool pad_rows) const {
+fetch_data(const State &state, void *into, bool packed) const {
   Shader::ShaderPtrData ptr_data;
   Shader::ShaderPtrData ptr_data;
   if (!state.gsg->get_target_shader_attrib()->get_shader_input_ptr(_input, ptr_data)) {
   if (!state.gsg->get_target_shader_attrib()->get_shader_input_ptr(_input, ptr_data)) {
     return;
     return;
   }
   }
 
 
   int total_rows = std::min(_num_elements * _num_rows, (int)ptr_data._size / _num_cols);
   int total_rows = std::min(_num_elements * _num_rows, (int)ptr_data._size / _num_cols);
-  if (total_rows == 1) {
-    pad_rows = false;
+  if (total_rows == 1 || _num_cols == 4) {
+    packed = true;
   }
   }
 
 
   float *data = (float *)into;
   float *data = (float *)into;
@@ -1839,7 +1885,7 @@ fetch_data(const State &state, void *into, bool pad_rows) const {
   switch (ptr_data._type) {
   switch (ptr_data._type) {
   case ShaderType::ST_int:
   case ShaderType::ST_int:
     // Convert int data to float data.
     // Convert int data to float data.
-    if (!pad_rows || _num_cols == 4) {
+    if (packed) {
       for (int i = 0; i < total_rows * _num_cols; ++i) {
       for (int i = 0; i < total_rows * _num_cols; ++i) {
         data[i] = (float)(((int *)ptr_data._ptr)[i]);
         data[i] = (float)(((int *)ptr_data._ptr)[i]);
       }
       }
@@ -1855,7 +1901,7 @@ fetch_data(const State &state, void *into, bool pad_rows) const {
 
 
   case ShaderType::ST_uint:
   case ShaderType::ST_uint:
     // Convert unsigned int data to float data.
     // Convert unsigned int data to float data.
-    if (!pad_rows || _num_cols == 4) {
+    if (packed) {
       for (int i = 0; i < total_rows * _num_cols; ++i) {
       for (int i = 0; i < total_rows * _num_cols; ++i) {
         data[i] = (float)(((unsigned int *)ptr_data._ptr)[i]);
         data[i] = (float)(((unsigned int *)ptr_data._ptr)[i]);
       }
       }
@@ -1871,7 +1917,7 @@ fetch_data(const State &state, void *into, bool pad_rows) const {
 
 
   case ShaderType::ST_double:
   case ShaderType::ST_double:
     // Downgrade double data to float data.
     // Downgrade double data to float data.
-    if (!pad_rows || _num_cols == 4) {
+    if (packed) {
       for (int i = 0; i < total_rows * _num_cols; ++i) {
       for (int i = 0; i < total_rows * _num_cols; ++i) {
         data[i] = (float)(((double *)ptr_data._ptr)[i]);
         data[i] = (float)(((double *)ptr_data._ptr)[i]);
       }
       }
@@ -1886,7 +1932,7 @@ fetch_data(const State &state, void *into, bool pad_rows) const {
     return;
     return;
 
 
   case ShaderType::ST_float:
   case ShaderType::ST_float:
-    if (!pad_rows || _num_cols == 4) {
+    if (packed) {
       // No conversion needed.
       // No conversion needed.
       memcpy(data, ptr_data._ptr, total_rows * _num_cols * sizeof(float));
       memcpy(data, ptr_data._ptr, total_rows * _num_cols * sizeof(float));
       return;
       return;
@@ -1919,15 +1965,15 @@ fetch_data(const State &state, void *into, bool pad_rows) const {
  * Fetches the part of the shader input that is plain numeric data.
  * Fetches the part of the shader input that is plain numeric data.
  */
  */
 void ShaderDoubleBinding::
 void ShaderDoubleBinding::
-fetch_data(const State &state, void *into, bool pad_rows) const {
+fetch_data(const State &state, void *into, bool packed) const {
   Shader::ShaderPtrData ptr_data;
   Shader::ShaderPtrData ptr_data;
   if (!state.gsg->get_target_shader_attrib()->get_shader_input_ptr(_input, ptr_data)) {
   if (!state.gsg->get_target_shader_attrib()->get_shader_input_ptr(_input, ptr_data)) {
     return;
     return;
   }
   }
 
 
   int total_rows = std::min(_num_elements * _num_rows, (int)ptr_data._size / _num_cols);
   int total_rows = std::min(_num_elements * _num_rows, (int)ptr_data._size / _num_cols);
-  if (total_rows == 1) {
-    pad_rows = false;
+  if (total_rows == 1 || _num_cols == 4) {
+    packed = true;
   }
   }
 
 
   double *data = (double *)into;
   double *data = (double *)into;
@@ -1935,7 +1981,7 @@ fetch_data(const State &state, void *into, bool pad_rows) const {
   switch (ptr_data._type) {
   switch (ptr_data._type) {
   case ShaderType::ST_int:
   case ShaderType::ST_int:
     // Convert int data to double data.
     // Convert int data to double data.
-    if (!pad_rows || _num_cols == 4) {
+    if (packed) {
       for (int i = 0; i < total_rows * _num_cols; ++i) {
       for (int i = 0; i < total_rows * _num_cols; ++i) {
         data[i] = (double)(((int *)ptr_data._ptr)[i]);
         data[i] = (double)(((int *)ptr_data._ptr)[i]);
       }
       }
@@ -1951,7 +1997,7 @@ fetch_data(const State &state, void *into, bool pad_rows) const {
 
 
   case ShaderType::ST_uint:
   case ShaderType::ST_uint:
     // Convert int data to double data.
     // Convert int data to double data.
-    if (!pad_rows || _num_cols == 4) {
+    if (packed) {
       for (int i = 0; i < total_rows * _num_cols; ++i) {
       for (int i = 0; i < total_rows * _num_cols; ++i) {
         data[i] = (double)(((unsigned int *)ptr_data._ptr)[i]);
         data[i] = (double)(((unsigned int *)ptr_data._ptr)[i]);
       }
       }
@@ -1966,7 +2012,7 @@ fetch_data(const State &state, void *into, bool pad_rows) const {
     return;
     return;
 
 
   case ShaderType::ST_double:
   case ShaderType::ST_double:
-    if (!pad_rows || _num_cols == 4) {
+    if (packed) {
       // No conversion needed.
       // No conversion needed.
       //if (always_copy) {
       //if (always_copy) {
         memcpy(data, ptr_data._ptr, total_rows * _num_cols * sizeof(double));
         memcpy(data, ptr_data._ptr, total_rows * _num_cols * sizeof(double));
@@ -1986,7 +2032,7 @@ fetch_data(const State &state, void *into, bool pad_rows) const {
 
 
   case ShaderType::ST_float:
   case ShaderType::ST_float:
     // Upgrade float data to double data.
     // Upgrade float data to double data.
-    if (!pad_rows || _num_cols == 4) {
+    if (packed) {
       for (int i = 0; i < total_rows * _num_cols; ++i) {
       for (int i = 0; i < total_rows * _num_cols; ++i) {
         data[i] = (double)(((float *)ptr_data._ptr)[i]);
         data[i] = (double)(((float *)ptr_data._ptr)[i]);
       }
       }
@@ -2013,7 +2059,7 @@ fetch_data(const State &state, void *into, bool pad_rows) const {
  * Fetches the part of the shader input that is plain numeric data.
  * Fetches the part of the shader input that is plain numeric data.
  */
  */
 void ShaderIntBinding::
 void ShaderIntBinding::
-fetch_data(const State &state, void *into, bool pad_rows) const {
+fetch_data(const State &state, void *into, bool packed) const {
   Shader::ShaderPtrData ptr_data;
   Shader::ShaderPtrData ptr_data;
   if (!state.gsg->get_target_shader_attrib()->get_shader_input_ptr(_input, ptr_data)) {
   if (!state.gsg->get_target_shader_attrib()->get_shader_input_ptr(_input, ptr_data)) {
     return;
     return;
@@ -2028,11 +2074,11 @@ fetch_data(const State &state, void *into, bool pad_rows) const {
   }
   }
 
 
   int total_rows = std::min(_num_elements * _num_rows, (int)ptr_data._size / _num_cols);
   int total_rows = std::min(_num_elements * _num_rows, (int)ptr_data._size / _num_cols);
-  if (total_rows == 1) {
-    pad_rows = false;
+  if (total_rows == 1 || _num_cols == 4) {
+    packed = true;
   }
   }
 
 
-  if (!pad_rows || _num_cols == 4) {
+  if (packed) {
     memcpy(into, ptr_data._ptr, total_rows * _num_cols * sizeof(int));
     memcpy(into, ptr_data._ptr, total_rows * _num_cols * sizeof(int));
   } else {
   } else {
     int *data = (int *)into;
     int *data = (int *)into;
@@ -2049,15 +2095,15 @@ fetch_data(const State &state, void *into, bool pad_rows) const {
  * Fetches the part of the shader input that is plain numeric data.
  * Fetches the part of the shader input that is plain numeric data.
  */
  */
 void ShaderBoolBinding::
 void ShaderBoolBinding::
-fetch_data(const State &state, void *into, bool pad_rows) const {
+fetch_data(const State &state, void *into, bool packed) const {
   Shader::ShaderPtrData ptr_data;
   Shader::ShaderPtrData ptr_data;
   if (!state.gsg->get_target_shader_attrib()->get_shader_input_ptr(_input, ptr_data)) {
   if (!state.gsg->get_target_shader_attrib()->get_shader_input_ptr(_input, ptr_data)) {
     return;
     return;
   }
   }
 
 
   int total_rows = std::min(_num_elements * _num_rows, (int)ptr_data._size / _num_cols);
   int total_rows = std::min(_num_elements * _num_rows, (int)ptr_data._size / _num_cols);
-  if (total_rows == 1) {
-    pad_rows = false;
+  if (total_rows == 1 || _num_cols == 4) {
+    packed = true;
   }
   }
 
 
   uint32_t *data = (uint32_t *)into;
   uint32_t *data = (uint32_t *)into;
@@ -2067,7 +2113,7 @@ fetch_data(const State &state, void *into, bool pad_rows) const {
   case ShaderType::ST_uint:
   case ShaderType::ST_uint:
   case ShaderType::ST_bool:
   case ShaderType::ST_bool:
     // Convert int data to bool data.
     // Convert int data to bool data.
-    if (!pad_rows || _num_cols == 4) {
+    if (packed) {
       for (int i = 0; i < total_rows * _num_cols; ++i) {
       for (int i = 0; i < total_rows * _num_cols; ++i) {
         data[i] = (uint32_t)(((unsigned int *)ptr_data._ptr)[i] != 0);
         data[i] = (uint32_t)(((unsigned int *)ptr_data._ptr)[i] != 0);
       }
       }
@@ -2083,7 +2129,7 @@ fetch_data(const State &state, void *into, bool pad_rows) const {
 
 
   case ShaderType::ST_double:
   case ShaderType::ST_double:
     // Convert double data to bool data.
     // Convert double data to bool data.
-    if (!pad_rows || _num_cols == 4) {
+    if (packed) {
       for (int i = 0; i < total_rows * _num_cols; ++i) {
       for (int i = 0; i < total_rows * _num_cols; ++i) {
         data[i] = (uint32_t)(((double *)ptr_data._ptr)[i] != 0.0);
         data[i] = (uint32_t)(((double *)ptr_data._ptr)[i] != 0.0);
       }
       }
@@ -2099,7 +2145,7 @@ fetch_data(const State &state, void *into, bool pad_rows) const {
 
 
   case ShaderType::ST_float:
   case ShaderType::ST_float:
     // Convert float data to bool data.
     // Convert float data to bool data.
-    if (!pad_rows || _num_cols == 4) {
+    if (packed) {
       for (int i = 0; i < total_rows * _num_cols; ++i) {
       for (int i = 0; i < total_rows * _num_cols; ++i) {
         data[i] = (uint32_t)(((float *)ptr_data._ptr)[i] != 0.0f);
         data[i] = (uint32_t)(((float *)ptr_data._ptr)[i] != 0.0f);
       }
       }
@@ -2122,13 +2168,25 @@ fetch_data(const State &state, void *into, bool pad_rows) const {
   }
   }
 }
 }
 
 
+/**
+ * Returns a mask indicating which state changes should cause the parameter to
+ * be respecified.
+ */
+int ShaderAggregateBinding::
+get_state_dep() const {
+  return Shader::D_frame | Shader::D_shader_inputs;
+}
+
 /**
 /**
  * Fetches the part of the shader input that is plain numeric data.
  * Fetches the part of the shader input that is plain numeric data.
  */
  */
 void ShaderAggregateBinding::
 void ShaderAggregateBinding::
-fetch_data(const State &state, void *into, bool pad_rows) const {
+fetch_data(const State &state, void *into, bool packed) const {
+  // Note that the offsets are calculated for a non-packed layout.  That means
+  // we have too much padding if we want packed data.  It's probably not worth
+  // engineering a solution for that.
   for (const DataMember &member : _data_members) {
   for (const DataMember &member : _data_members) {
-    member._binding->fetch_data(state, (unsigned char *)into + member._offset, pad_rows);
+    member._binding->fetch_data(state, (unsigned char *)into + member._offset, packed);
   }
   }
 }
 }
 
 
@@ -2160,6 +2218,18 @@ fetch_texture_image(const State &state, ResourceId resource_id, ShaderType::Acce
   return state.gsg->get_target_shader_attrib()->get_shader_input_texture_image(name, access, z, n);
   return state.gsg->get_target_shader_attrib()->get_shader_input_texture_image(name, access, z, n);
 }
 }
 
 
+/**
+ * Fetches the shader buffer associated with the given resource identifier,
+ * which was previously returned by get_resource_id.
+ */
+PT(ShaderBuffer) ShaderAggregateBinding::
+fetch_shader_buffer(const State &state, ResourceId resource_id) const {
+  // GLSL does not support SSBOs in structs, but they can be in arrays, and we
+  // might add support for another shader language that does support this.
+  const InternalName *name = (const InternalName *)resource_id;
+  return state.gsg->get_target_shader_attrib()->get_shader_input_buffer(name);
+}
+
 /**
 /**
  * Unwraps the aggregate type, storing the individual members.
  * Unwraps the aggregate type, storing the individual members.
  */
  */
@@ -2189,15 +2259,15 @@ r_collect_members(const InternalName *name, const ShaderType *type, size_t offse
 
 
     _data_members.push_back({binding, offset});
     _data_members.push_back({binding, offset});
   }
   }
-  else if (const ::ShaderType::Struct *struct_type = type->as_struct()) {
+  else if (const ShaderType::Struct *struct_type = type->as_struct()) {
     for (size_t i = 0; i < struct_type->get_num_members(); ++i) {
     for (size_t i = 0; i < struct_type->get_num_members(); ++i) {
-      const ::ShaderType::Struct::Member &member = struct_type->get_member(i);
+      const ShaderType::Struct::Member &member = struct_type->get_member(i);
 
 
       PT(InternalName) fqname = ((InternalName *)name)->append(member.name);
       PT(InternalName) fqname = ((InternalName *)name)->append(member.name);
       r_collect_members(fqname, member.type, offset + member.offset);
       r_collect_members(fqname, member.type, offset + member.offset);
     }
     }
   }
   }
-  else if (const ::ShaderType::Array *array_type = type->as_array()) {
+  else if (const ShaderType::Array *array_type = type->as_array()) {
     size_t basename_size = name->get_basename().size();
     size_t basename_size = name->get_basename().size();
     char *buffer = (char *)alloca(basename_size + 14);
     char *buffer = (char *)alloca(basename_size + 14);
     memcpy(buffer, name->get_basename().c_str(), basename_size);
     memcpy(buffer, name->get_basename().c_str(), basename_size);
@@ -2441,7 +2511,7 @@ make_binding_glsl(const InternalName *name, const ShaderType *type) {
       }
       }
 
 
       return ShaderInputBinding::make_data(Shader::D_clip_planes | Shader::D_view_transform,
       return ShaderInputBinding::make_data(Shader::D_clip_planes | Shader::D_view_transform,
-                                           [=](const State &state, void *into, bool pad_rows) {
+                                           [=](const State &state, void *into, bool packed) {
 
 
         LPlanef *planes = (LPlanef *)into;
         LPlanef *planes = (LPlanef *)into;
 
 
@@ -2479,7 +2549,7 @@ make_binding_glsl(const InternalName *name, const ShaderType *type) {
       type->unwrap_array(element_type, num_elements);
       type->unwrap_array(element_type, num_elements);
 
 
       return ShaderInputBinding::make_data(Shader::D_texture | Shader::D_frame,
       return ShaderInputBinding::make_data(Shader::D_texture | Shader::D_frame,
-                                           [=](const State &state, void *into, bool pad_rows) {
+                                           [=](const State &state, void *into, bool packed) {
 
 
         const TextureAttrib *ta;
         const TextureAttrib *ta;
 
 
@@ -2604,13 +2674,13 @@ make_binding_glsl(const InternalName *name, const ShaderType *type) {
     else if (pieces[1] == "DeltaFrameTime") {
     else if (pieces[1] == "DeltaFrameTime") {
       if (type == ShaderType::float_type) {
       if (type == ShaderType::float_type) {
         return ShaderInputBinding::make_data(Shader::D_frame,
         return ShaderInputBinding::make_data(Shader::D_frame,
-                                             [](const State &state, void *into, bool pad_rows) {
+                                             [](const State &state, void *into, bool packed) {
           *(float *)into = ClockObject::get_global_clock()->get_dt();
           *(float *)into = ClockObject::get_global_clock()->get_dt();
         });
         });
       }
       }
       else if (type == ShaderType::double_type) {
       else if (type == ShaderType::double_type) {
         return ShaderInputBinding::make_data(Shader::D_frame,
         return ShaderInputBinding::make_data(Shader::D_frame,
-                                             [](const State &state, void *into, bool pad_rows) {
+                                             [](const State &state, void *into, bool packed) {
           *(double *)into = ClockObject::get_global_clock()->get_dt();
           *(double *)into = ClockObject::get_global_clock()->get_dt();
         });
         });
       }
       }
@@ -2621,7 +2691,7 @@ make_binding_glsl(const InternalName *name, const ShaderType *type) {
     else if (pieces[1] == "FrameNumber") {
     else if (pieces[1] == "FrameNumber") {
       if (type == ShaderType::int_type) {
       if (type == ShaderType::int_type) {
         return ShaderInputBinding::make_data(Shader::D_frame,
         return ShaderInputBinding::make_data(Shader::D_frame,
-                                             [](const State &state, void *into, bool pad_rows) {
+                                             [](const State &state, void *into, bool packed) {
           *(int *)into = ClockObject::get_global_clock()->get_frame_count();
           *(int *)into = ClockObject::get_global_clock()->get_frame_count();
         });
         });
       } else {
       } else {
@@ -2926,7 +2996,7 @@ make_binding_cg(const InternalName *name, const ShaderType *type) {
         return nullptr;
         return nullptr;
       }
       }
       return ShaderInputBinding::make_data(Shader::D_material | Shader::D_frame,
       return ShaderInputBinding::make_data(Shader::D_material | Shader::D_frame,
-                                           [=](const State &state, void *into, bool pad_rows) {
+                                           [=](const State &state, void *into, bool packed) {
 
 
         LVecBase4f &ambient = ((LVecBase4f *)into)[0];
         LVecBase4f &ambient = ((LVecBase4f *)into)[0];
         LVecBase4f &diffuse = ((LVecBase4f *)into)[1];
         LVecBase4f &diffuse = ((LVecBase4f *)into)[1];
@@ -2966,7 +3036,7 @@ make_binding_cg(const InternalName *name, const ShaderType *type) {
         return nullptr;
         return nullptr;
       }
       }
       return ShaderInputBinding::make_data(Shader::D_fog | Shader::D_frame,
       return ShaderInputBinding::make_data(Shader::D_fog | Shader::D_frame,
-                                           [](const State &state, void *into, bool pad_rows) {
+                                           [](const State &state, void *into, bool packed) {
 
 
         LVecBase4f &params = *(LVecBase4f *)into;
         LVecBase4f &params = *(LVecBase4f *)into;
 
 
@@ -2986,7 +3056,7 @@ make_binding_cg(const InternalName *name, const ShaderType *type) {
         return nullptr;
         return nullptr;
       }
       }
       return ShaderInputBinding::make_data(Shader::D_fog | Shader::D_frame,
       return ShaderInputBinding::make_data(Shader::D_fog | Shader::D_frame,
-                                           [](const State &state, void *into, bool pad_rows) {
+                                           [](const State &state, void *into, bool packed) {
 
 
         LVecBase4f &color = *(LVecBase4f *)into;
         LVecBase4f &color = *(LVecBase4f *)into;
 
 
@@ -3018,7 +3088,7 @@ make_binding_cg(const InternalName *name, const ShaderType *type) {
         return nullptr;
         return nullptr;
       }
       }
       return ShaderInputBinding::make_data(Shader::D_light | Shader::D_frame,
       return ShaderInputBinding::make_data(Shader::D_light | Shader::D_frame,
-                                           [=](const State &state, void *into, bool pad_rows) {
+                                           [=](const State &state, void *into, bool packed) {
 
 
         // We don't count ambient lights, which would be pretty silly to handle
         // We don't count ambient lights, which would be pretty silly to handle
         // via this mechanism.
         // via this mechanism.
@@ -3057,7 +3127,7 @@ make_binding_cg(const InternalName *name, const ShaderType *type) {
 
 
     CPT(InternalName) input = InternalName::make(pieces[1]);
     CPT(InternalName) input = InternalName::make(pieces[1]);
     return ShaderInputBinding::make_data(Shader::D_shader_inputs | Shader::D_frame,
     return ShaderInputBinding::make_data(Shader::D_shader_inputs | Shader::D_frame,
-                                         [=](const State &state, void *into, bool pad_rows) {
+                                         [=](const State &state, void *into, bool packed) {
       const NodePath &np = state.gsg->get_target_shader_attrib()->get_shader_input_nodepath(input);
       const NodePath &np = state.gsg->get_target_shader_attrib()->get_shader_input_nodepath(input);
       nassertv(!np.is_empty());
       nassertv(!np.is_empty());
       Light *light = np.node()->as_light();
       Light *light = np.node()->as_light();
@@ -3074,7 +3144,7 @@ make_binding_cg(const InternalName *name, const ShaderType *type) {
 
 
     CPT(InternalName) input = InternalName::make(pieces[1]);
     CPT(InternalName) input = InternalName::make(pieces[1]);
     return ShaderInputBinding::make_data(Shader::D_shader_inputs | Shader::D_frame,
     return ShaderInputBinding::make_data(Shader::D_shader_inputs | Shader::D_frame,
-                                         [=](const State &state, void *into, bool pad_rows) {
+                                         [=](const State &state, void *into, bool packed) {
       const NodePath &np = state.gsg->get_target_shader_attrib()->get_shader_input_nodepath(input);
       const NodePath &np = state.gsg->get_target_shader_attrib()->get_shader_input_nodepath(input);
       nassertv(!np.is_empty());
       nassertv(!np.is_empty());
       Light *light = np.node()->as_light();
       Light *light = np.node()->as_light();
@@ -3136,7 +3206,7 @@ make_binding_cg(const InternalName *name, const ShaderType *type) {
 
 
     int stage = atoi(pieces[1].c_str());
     int stage = atoi(pieces[1].c_str());
     return ShaderInputBinding::make_data(Shader::D_texture | Shader::D_tex_matrix,
     return ShaderInputBinding::make_data(Shader::D_texture | Shader::D_tex_matrix,
-                                         [=](const State &state, void *into, bool pad_rows) {
+                                         [=](const State &state, void *into, bool packed) {
 
 
       const TextureAttrib *ta;
       const TextureAttrib *ta;
       const TexMatrixAttrib *tma;
       const TexMatrixAttrib *tma;
@@ -3159,7 +3229,7 @@ make_binding_cg(const InternalName *name, const ShaderType *type) {
     // state change
     // state change
     int stage = atoi(pieces[1].c_str());
     int stage = atoi(pieces[1].c_str());
     return ShaderInputBinding::make_data(Shader::D_texture | Shader::D_frame,
     return ShaderInputBinding::make_data(Shader::D_texture | Shader::D_frame,
-                                         [=](const State &state, void *into, bool pad_rows) {
+                                         [=](const State &state, void *into, bool packed) {
 
 
       const TextureAttrib *ta;
       const TextureAttrib *ta;
       if (state.gsg->get_target_state()->get_attrib(ta) && stage < ta->get_num_on_stages()) {
       if (state.gsg->get_target_state()->get_attrib(ta) && stage < ta->get_num_on_stages()) {
@@ -3181,7 +3251,7 @@ make_binding_cg(const InternalName *name, const ShaderType *type) {
     // a state change
     // a state change
     int stage = atoi(pieces[1].c_str());
     int stage = atoi(pieces[1].c_str());
     return ShaderInputBinding::make_data(Shader::D_texture | Shader::D_tex_gen | Shader::D_frame,
     return ShaderInputBinding::make_data(Shader::D_texture | Shader::D_tex_gen | Shader::D_frame,
-                                         [=](const State &state, void *into, bool pad_rows) {
+                                         [=](const State &state, void *into, bool packed) {
 
 
       const TextureAttrib *ta;
       const TextureAttrib *ta;
       const TexGenAttrib *tga;
       const TexGenAttrib *tga;
@@ -3204,7 +3274,7 @@ make_binding_cg(const InternalName *name, const ShaderType *type) {
     // state change
     // state change
     CPT(InternalName) input = InternalName::make(pieces[1]);
     CPT(InternalName) input = InternalName::make(pieces[1]);
     return ShaderInputBinding::make_data(Shader::D_frame | Shader::D_shader_inputs,
     return ShaderInputBinding::make_data(Shader::D_frame | Shader::D_shader_inputs,
-                                         [=](const State &state, void *into, bool pad_rows) {
+                                         [=](const State &state, void *into, bool packed) {
       const NodePath &np = state.gsg->get_target_shader_attrib()->get_shader_input_nodepath(name);
       const NodePath &np = state.gsg->get_target_shader_attrib()->get_shader_input_nodepath(name);
       nassertv(!np.is_empty());
       nassertv(!np.is_empty());
       const PlaneNode *plane_node;
       const PlaneNode *plane_node;
@@ -3223,7 +3293,7 @@ make_binding_cg(const InternalName *name, const ShaderType *type) {
     // state change
     // state change
     int index = atoi(pieces[1].c_str());
     int index = atoi(pieces[1].c_str());
     return ShaderInputBinding::make_data(Shader::D_clip_planes | Shader::D_frame,
     return ShaderInputBinding::make_data(Shader::D_clip_planes | Shader::D_frame,
-                                         [=](const State &state, void *into, bool pad_rows) {
+                                         [=](const State &state, void *into, bool packed) {
       const ClipPlaneAttrib *cpa;
       const ClipPlaneAttrib *cpa;
       state.gsg->get_target_state()->get_attrib_def(cpa);
       state.gsg->get_target_state()->get_attrib_def(cpa);
       if (index >= cpa->get_num_on_planes()) {
       if (index >= cpa->get_num_on_planes()) {
@@ -3255,7 +3325,7 @@ make_binding_cg(const InternalName *name, const ShaderType *type) {
         return nullptr;
         return nullptr;
       }
       }
       return ShaderInputBinding::make_data(Shader::D_scene,
       return ShaderInputBinding::make_data(Shader::D_scene,
-                                           [=](const State &state, void *into, bool pad_rows) {
+                                           [=](const State &state, void *into, bool packed) {
         const DisplayRegion *region = state.gsg->get_current_display_region();
         const DisplayRegion *region = state.gsg->get_current_display_region();
         *(LVecBase2f *)into = LCAST(float, region->get_pixel_size());
         *(LVecBase2f *)into = LCAST(float, region->get_pixel_size());
       });
       });
@@ -3324,7 +3394,7 @@ make_binding_cg(const InternalName *name, const ShaderType *type) {
     }
     }
     CPT(InternalName) input = InternalName::make(pieces[1]);
     CPT(InternalName) input = InternalName::make(pieces[1]);
     return ShaderInputBinding::make_data(Shader::D_frame | Shader::D_shader_inputs,
     return ShaderInputBinding::make_data(Shader::D_frame | Shader::D_shader_inputs,
-                                         [=](const State &state, void *into, bool pad_rows) {
+                                         [=](const State &state, void *into, bool packed) {
       Texture *tex = state.gsg->get_target_shader_attrib()->get_shader_input_texture(input);
       Texture *tex = state.gsg->get_target_shader_attrib()->get_shader_input_texture(input);
       nassertv(tex != nullptr);
       nassertv(tex != nullptr);
       int sx = tex->get_x_size() - tex->get_pad_x_size();
       int sx = tex->get_x_size() - tex->get_pad_x_size();
@@ -3344,7 +3414,7 @@ make_binding_cg(const InternalName *name, const ShaderType *type) {
     }
     }
     CPT(InternalName) input = InternalName::make(pieces[1]);
     CPT(InternalName) input = InternalName::make(pieces[1]);
     return ShaderInputBinding::make_data(Shader::D_frame | Shader::D_shader_inputs,
     return ShaderInputBinding::make_data(Shader::D_frame | Shader::D_shader_inputs,
-                                         [=](const State &state, void *into, bool pad_rows) {
+                                         [=](const State &state, void *into, bool packed) {
       Texture *tex = state.gsg->get_target_shader_attrib()->get_shader_input_texture(input);
       Texture *tex = state.gsg->get_target_shader_attrib()->get_shader_input_texture(input);
       nassertv(tex != nullptr);
       nassertv(tex != nullptr);
       double px = 1.0 / tex->get_x_size();
       double px = 1.0 / tex->get_x_size();

+ 38 - 14
panda/src/display/shaderInputBinding_impls.h

@@ -31,7 +31,7 @@ public:
   virtual int get_state_dep() const override;
   virtual int get_state_dep() const override;
   virtual void setup(Shader *shader) override;
   virtual void setup(Shader *shader) override;
 
 
-  virtual void fetch_data(const State &state, void *into, bool pad_rows) const override;
+  virtual void fetch_data(const State &state, void *into, bool packed) const override;
 
 
 protected:
 protected:
   size_t _cache_index = 0;
   size_t _cache_index = 0;
@@ -57,7 +57,7 @@ public:
   virtual int get_state_dep() const override;
   virtual int get_state_dep() const override;
   virtual void setup(Shader *shader) override;
   virtual void setup(Shader *shader) override;
 
 
-  virtual void fetch_data(const State &state, void *into, bool pad_rows) const override;
+  virtual void fetch_data(const State &state, void *into, bool packed) const override;
 
 
   virtual bool is_model_to_apiclip_matrix() const override;
   virtual bool is_model_to_apiclip_matrix() const override;
 
 
@@ -82,7 +82,7 @@ public:
   virtual int get_state_dep() const override;
   virtual int get_state_dep() const override;
   virtual void setup(Shader *shader) override;
   virtual void setup(Shader *shader) override;
 
 
-  virtual void fetch_data(const State &state, void *into, bool pad_rows) const override;
+  virtual void fetch_data(const State &state, void *into, bool packed) const override;
 
 
 protected:
 protected:
   size_t _cache_index = 0;
   size_t _cache_index = 0;
@@ -98,7 +98,7 @@ public:
   virtual int get_state_dep() const override;
   virtual int get_state_dep() const override;
   virtual void setup(Shader *shader) override;
   virtual void setup(Shader *shader) override;
 
 
-  virtual void fetch_data(const State &state, void *into, bool pad_rows) const override;
+  virtual void fetch_data(const State &state, void *into, bool packed) const override;
 
 
 protected:
 protected:
   size_t _index;
   size_t _index;
@@ -131,7 +131,7 @@ class EXPCL_PANDA_DISPLAY ShaderLegacyDirectionalLightBinding : public ShaderLeg
 public:
 public:
   using ShaderLegacyLightBinding::ShaderLegacyLightBinding;
   using ShaderLegacyLightBinding::ShaderLegacyLightBinding;
 
 
-  virtual void fetch_data(const State &state, void *into, bool pad_rows) const override;
+  virtual void fetch_data(const State &state, void *into, bool packed) const override;
 };
 };
 
 
 /**
 /**
@@ -141,7 +141,7 @@ class EXPCL_PANDA_DISPLAY ShaderLegacyPointLightBinding : public ShaderLegacyLig
 public:
 public:
   using ShaderLegacyLightBinding::ShaderLegacyLightBinding;
   using ShaderLegacyLightBinding::ShaderLegacyLightBinding;
 
 
-  virtual void fetch_data(const State &state, void *into, bool pad_rows) const override;
+  virtual void fetch_data(const State &state, void *into, bool packed) const override;
 };
 };
 
 
 /**
 /**
@@ -151,7 +151,7 @@ class EXPCL_PANDA_DISPLAY ShaderLegacySpotlightBinding : public ShaderLegacyLigh
 public:
 public:
   using ShaderLegacyLightBinding::ShaderLegacyLightBinding;
   using ShaderLegacyLightBinding::ShaderLegacyLightBinding;
 
 
-  virtual void fetch_data(const State &state, void *into, bool pad_rows) const override;
+  virtual void fetch_data(const State &state, void *into, bool packed) const override;
 };
 };
 
 
 /**
 /**
@@ -165,7 +165,7 @@ public:
   virtual int get_state_dep() const override;
   virtual int get_state_dep() const override;
   virtual void setup(Shader *shader) override;
   virtual void setup(Shader *shader) override;
 
 
-  virtual void fetch_data(const State &state, void *into, bool pad_rows) const override;
+  virtual void fetch_data(const State &state, void *into, bool packed) const override;
 
 
   virtual ResourceId get_resource_id(int index, const ShaderType *type) const;
   virtual ResourceId get_resource_id(int index, const ShaderType *type) const;
   virtual PT(Texture) fetch_texture(const State &state,
   virtual PT(Texture) fetch_texture(const State &state,
@@ -250,6 +250,25 @@ protected:
   mutable bool _shown_error = false;
   mutable bool _shown_error = false;
 };
 };
 
 
+/**
+ * Binds a parameter to a generic storage buffer shader input.
+ */
+class EXPCL_PANDA_DISPLAY ShaderBufferBinding : public ShaderInputBinding {
+public:
+  INLINE ShaderBufferBinding(CPT(InternalName) input, size_t min_size = 0);
+
+  virtual int get_state_dep() const override;
+
+  virtual ResourceId get_resource_id(int index, const ShaderType *type) const;
+  virtual PT(ShaderBuffer) fetch_shader_buffer(const State &state,
+                                               ResourceId resource_id) const;
+
+protected:
+  CPT(InternalName) const _input;
+  size_t const _min_size = 0;
+  mutable bool _shown_error = false;
+};
+
 /**
 /**
  * This binds a parameter to a generic numeric data shader input.
  * This binds a parameter to a generic numeric data shader input.
  */
  */
@@ -260,7 +279,7 @@ public:
 
 
   virtual int get_state_dep() const override;
   virtual int get_state_dep() const override;
 
 
-  virtual void fetch_data(const State &state, void *into, bool pad_rows) const override=0;
+  virtual void fetch_data(const State &state, void *into, bool packed) const override=0;
 
 
 protected:
 protected:
   CPT_InternalName _input;
   CPT_InternalName _input;
@@ -276,7 +295,7 @@ class EXPCL_PANDA_DISPLAY ShaderFloatBinding : public ShaderDataBinding {
 public:
 public:
   using ShaderDataBinding::ShaderDataBinding;
   using ShaderDataBinding::ShaderDataBinding;
 
 
-  virtual void fetch_data(const State &state, void *into, bool pad_rows) const override;
+  virtual void fetch_data(const State &state, void *into, bool packed) const override;
 };
 };
 
 
 /**
 /**
@@ -286,7 +305,7 @@ class EXPCL_PANDA_DISPLAY ShaderDoubleBinding : public ShaderDataBinding {
 public:
 public:
   using ShaderDataBinding::ShaderDataBinding;
   using ShaderDataBinding::ShaderDataBinding;
 
 
-  virtual void fetch_data(const State &state, void *into, bool pad_rows) const override;
+  virtual void fetch_data(const State &state, void *into, bool packed) const override;
 };
 };
 
 
 /**
 /**
@@ -296,7 +315,7 @@ class EXPCL_PANDA_DISPLAY ShaderIntBinding : public ShaderDataBinding {
 public:
 public:
   using ShaderDataBinding::ShaderDataBinding;
   using ShaderDataBinding::ShaderDataBinding;
 
 
-  virtual void fetch_data(const State &state, void *into, bool pad_rows) const override;
+  virtual void fetch_data(const State &state, void *into, bool packed) const override;
 };
 };
 
 
 /**
 /**
@@ -306,7 +325,7 @@ class EXPCL_PANDA_DISPLAY ShaderBoolBinding : public ShaderDataBinding {
 public:
 public:
   using ShaderDataBinding::ShaderDataBinding;
   using ShaderDataBinding::ShaderDataBinding;
 
 
-  virtual void fetch_data(const State &state, void *into, bool pad_rows) const override;
+  virtual void fetch_data(const State &state, void *into, bool packed) const override;
 };
 };
 
 
 /**
 /**
@@ -317,7 +336,9 @@ class EXPCL_PANDA_DISPLAY ShaderAggregateBinding : public ShaderInputBinding {
 public:
 public:
   INLINE ShaderAggregateBinding(CPT_InternalName input, const ShaderType *type);
   INLINE ShaderAggregateBinding(CPT_InternalName input, const ShaderType *type);
 
 
-  virtual void fetch_data(const State &state, void *into, bool pad_rows) const override;
+  virtual int get_state_dep() const override;
+
+  virtual void fetch_data(const State &state, void *into, bool packed) const override;
 
 
   virtual ResourceId get_resource_id(int index, const ShaderType *type) const;
   virtual ResourceId get_resource_id(int index, const ShaderType *type) const;
   virtual PT(Texture) fetch_texture(const State &state,
   virtual PT(Texture) fetch_texture(const State &state,
@@ -327,6 +348,9 @@ public:
                                           ResourceId index,
                                           ResourceId index,
                                           ShaderType::Access &access,
                                           ShaderType::Access &access,
                                           int &z, int &n) const;
                                           int &z, int &n) const;
+  virtual PT(ShaderBuffer) fetch_shader_buffer(const State &state,
+                                               ResourceId resource_id) const;
+
 private:
 private:
   void r_collect_members(const InternalName *name, const ShaderType *type, size_t offset = 0);
   void r_collect_members(const InternalName *name, const ShaderType *type, size_t offset = 0);
 
 

+ 12 - 10
panda/src/dxgsg9/dxShaderContext9.cxx

@@ -49,7 +49,7 @@ TypeHandle DXShaderContext9::_type_handle;
 DXShaderContext9::
 DXShaderContext9::
 DXShaderContext9(Shader *s, GSG *gsg) : ShaderContext(s) {
 DXShaderContext9(Shader *s, GSG *gsg) : ShaderContext(s) {
   DWORD *vs_data;
   DWORD *vs_data;
-  CPT(ShaderModule) vertex_module = s->get_module(Shader::Stage::vertex);
+  CPT(ShaderModule) vertex_module = s->get_module(Shader::Stage::VERTEX);
   if (compile_module(vertex_module, vs_data)) {
   if (compile_module(vertex_module, vs_data)) {
     HRESULT result = gsg->_d3d_device->CreateVertexShader(vs_data, &_vertex_shader);
     HRESULT result = gsg->_d3d_device->CreateVertexShader(vs_data, &_vertex_shader);
     if (FAILED(result)) {
     if (FAILED(result)) {
@@ -59,7 +59,7 @@ DXShaderContext9(Shader *s, GSG *gsg) : ShaderContext(s) {
   }
   }
 
 
   DWORD *ps_data;
   DWORD *ps_data;
-  CPT(ShaderModule) fragment_module = s->get_module(Shader::Stage::fragment);
+  CPT(ShaderModule) fragment_module = s->get_module(Shader::Stage::FRAGMENT);
   if (compile_module(fragment_module, ps_data)) {
   if (compile_module(fragment_module, ps_data)) {
     HRESULT result = gsg->_d3d_device->CreatePixelShader(ps_data, &_pixel_shader);
     HRESULT result = gsg->_d3d_device->CreatePixelShader(ps_data, &_pixel_shader);
     if (FAILED(result)) {
     if (FAILED(result)) {
@@ -219,11 +219,11 @@ compile_module(const ShaderModule *module, DWORD *&data) {
 
 
   const char *profile;
   const char *profile;
   switch (spv->get_stage()) {
   switch (spv->get_stage()) {
-  case Shader::Stage::vertex:
+  case Shader::Stage::VERTEX:
     profile = "vs_3_0";
     profile = "vs_3_0";
     break;
     break;
 
 
-  case Shader::Stage::fragment:
+  case Shader::Stage::FRAGMENT:
     profile = "ps_3_0";
     profile = "ps_3_0";
     break;
     break;
 
 
@@ -305,7 +305,7 @@ query_constants(const ShaderModule *module, DWORD *data) {
     // parameter index.
     // parameter index.
     const char *name = (const char *)(table_data + constant.Name);
     const char *name = (const char *)(table_data + constant.Name);
     if (name[0] != 'p') {
     if (name[0] != 'p') {
-      if (stage == Shader::Stage::vertex && strcmp(name, "gl_HalfPixel") == 0) {
+      if (stage == Shader::Stage::VERTEX && strcmp(name, "gl_HalfPixel") == 0) {
         // This is a special input generated by spirv-cross.
         // This is a special input generated by spirv-cross.
         _half_pixel_register = constant.RegisterIndex;
         _half_pixel_register = constant.RegisterIndex;
         continue;
         continue;
@@ -364,8 +364,10 @@ query_constants(const ShaderModule *module, DWORD *data) {
           _constant_deps |= binding._dep;
           _constant_deps |= binding._dep;
           _data_bindings.push_back(std::move(binding));
           _data_bindings.push_back(std::move(binding));
 
 
-          // Pad space to 16-byte boundary
-          uint32_t size = param._type->get_size_bytes(true);
+          // Pad space to 16-byte boundary, since DX9 wants everything as a
+          // vec4, and otherwise we may end up copying out-of-bounds data if
+          // the last field is smaller than a vec4.
+          uint32_t size = param._type->get_size_bytes();
           size = (size + 15) & ~15;
           size = (size + 15) & ~15;
           _scratch_space_size += size;
           _scratch_space_size += size;
         }
         }
@@ -457,10 +459,10 @@ r_query_constants(Shader::Stage stage, const Shader::Parameter &param,
     // register, so we need to do an extra conversion step.
     // register, so we need to do an extra conversion step.
     reg.convert = (reg.set == D3DXRS_FLOAT4 && typeinfo.Type != D3DXPT_FLOAT);
     reg.convert = (reg.set == D3DXRS_FLOAT4 && typeinfo.Type != D3DXPT_FLOAT);
 
 
-    if (stage == Shader::Stage::vertex) {
+    if (stage == Shader::Stage::VERTEX) {
       _vertex_constants.push_back(std::move(reg));
       _vertex_constants.push_back(std::move(reg));
     }
     }
-    if (stage == Shader::Stage::fragment) {
+    if (stage == Shader::Stage::FRAGMENT) {
       _pixel_constants.push_back(std::move(reg));
       _pixel_constants.push_back(std::move(reg));
     }
     }
 
 
@@ -620,7 +622,7 @@ issue_parameters(GSG *gsg, int altered) {
 
 
     for (const Binding &binding : _data_bindings) {
     for (const Binding &binding : _data_bindings) {
       if (altered & binding._dep) {
       if (altered & binding._dep) {
-        binding._binding->fetch_data(state, scratch + binding._offset, true);
+        binding._binding->fetch_data(state, scratch + binding._offset, false);
       }
       }
     }
     }
 
 

+ 4 - 0
panda/src/gles2gsg/gles2gsg.h

@@ -272,6 +272,10 @@ typedef char GLchar;
 #define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067
 #define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067
 #define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069
 #define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069
 #define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A
 #define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A
+#define GL_SHADER_STORAGE_BUFFER 0x90D2
+#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3
+#define GL_SHADER_STORAGE_BUFFER_START 0x90D4
+#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5
 #define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
 #define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
 #define GL_UNSIGNALED 0x9118
 #define GL_UNSIGNALED 0x9118
 #define GL_SIGNALED 0x9119
 #define GL_SIGNALED 0x9119

+ 12 - 2
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -1810,6 +1810,7 @@ reset() {
       Shader::C_atomic_counters |
       Shader::C_atomic_counters |
       Shader::C_image_load_store |
       Shader::C_image_load_store |
       Shader::C_image_query_size |
       Shader::C_image_query_size |
+      Shader::C_storage_buffer |
       Shader::C_compute_shader;
       Shader::C_compute_shader;
 
 
     if (has_extension("GL_OES_geometry_shader")) {
     if (has_extension("GL_OES_geometry_shader")) {
@@ -2070,12 +2071,15 @@ reset() {
 
 
     _glGetProgramInterfaceiv = (PFNGLGETPROGRAMINTERFACEIVPROC)
     _glGetProgramInterfaceiv = (PFNGLGETPROGRAMINTERFACEIVPROC)
        get_extension_func("glGetProgramInterfaceiv");
        get_extension_func("glGetProgramInterfaceiv");
+    _glGetProgramResourceIndex = (PFNGLGETPROGRAMRESOURCEINDEXPROC)
+       get_extension_func("glGetProgramResourceIndex");
     _glGetProgramResourceName = (PFNGLGETPROGRAMRESOURCENAMEPROC)
     _glGetProgramResourceName = (PFNGLGETPROGRAMRESOURCENAMEPROC)
        get_extension_func("glGetProgramResourceName");
        get_extension_func("glGetProgramResourceName");
     _glGetProgramResourceiv = (PFNGLGETPROGRAMRESOURCEIVPROC)
     _glGetProgramResourceiv = (PFNGLGETPROGRAMRESOURCEIVPROC)
        get_extension_func("glGetProgramResourceiv");
        get_extension_func("glGetProgramResourceiv");
 
 
     if (_glGetProgramInterfaceiv != nullptr &&
     if (_glGetProgramInterfaceiv != nullptr &&
+        _glGetProgramResourceIndex != nullptr &&
         _glGetProgramResourceName != nullptr &&
         _glGetProgramResourceName != nullptr &&
         _glGetProgramResourceiv != nullptr) {
         _glGetProgramResourceiv != nullptr) {
       _supports_program_interface_query = true;
       _supports_program_interface_query = true;
@@ -2431,6 +2435,10 @@ reset() {
     _glShaderStorageBlockBinding = (PFNGLSHADERSTORAGEBLOCKBINDINGPROC)
     _glShaderStorageBlockBinding = (PFNGLSHADERSTORAGEBLOCKBINDINGPROC)
        get_extension_func("glShaderStorageBlockBinding");
        get_extension_func("glShaderStorageBlockBinding");
   } else
   } else
+#else
+  if (is_at_least_gles_version(3, 1)) {
+    _supports_shader_buffers = _supports_program_interface_query;
+  } else
 #endif
 #endif
   {
   {
     _supports_shader_buffers = false;
     _supports_shader_buffers = false;
@@ -7500,7 +7508,7 @@ setup_primitive(const unsigned char *&client_pointer,
   return true;
   return true;
 }
 }
 
 
-#ifndef OPENGLES
+#ifndef OPENGLES_1
 /**
 /**
  * Creates a new retained-mode representation of the given data, and returns a
  * Creates a new retained-mode representation of the given data, and returns a
  * newly-allocated BufferContext pointer to reference it.  It is the
  * newly-allocated BufferContext pointer to reference it.  It is the
@@ -7531,7 +7539,9 @@ prepare_shader_buffer(ShaderBuffer *data) {
     }
     }
 
 
     // Some drivers require the buffer to be padded to 16 byte boundary.
     // Some drivers require the buffer to be padded to 16 byte boundary.
-    uint64_t num_bytes = (data->get_data_size_bytes() + 15u) & ~15u;
+    //XXX rdb: actually, this breaks runtime-sized arrays.
+    //uint64_t num_bytes = (data->get_data_size_bytes() + 15u) & ~15u;
+    uint64_t num_bytes = data->get_data_size_bytes();
     if (_supports_buffer_storage) {
     if (_supports_buffer_storage) {
       _glBufferStorage(GL_SHADER_STORAGE_BUFFER, num_bytes, data->get_initial_data(), 0);
       _glBufferStorage(GL_SHADER_STORAGE_BUFFER, num_bytes, data->get_initial_data(), 0);
     } else {
     } else {

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

@@ -407,7 +407,7 @@ public:
                        const GeomPrimitivePipelineReader *reader,
                        const GeomPrimitivePipelineReader *reader,
                        bool force);
                        bool force);
 
 
-#ifndef OPENGLES
+#ifndef OPENGLES_1
   virtual BufferContext *prepare_shader_buffer(ShaderBuffer *data);
   virtual BufferContext *prepare_shader_buffer(ShaderBuffer *data);
   void apply_shader_buffer(GLuint base, ShaderBuffer *buffer);
   void apply_shader_buffer(GLuint base, ShaderBuffer *buffer);
   virtual void release_shader_buffer(BufferContext *bc);
   virtual void release_shader_buffer(BufferContext *bc);
@@ -1137,6 +1137,7 @@ public:
 
 
 #ifndef OPENGLES_1
 #ifndef OPENGLES_1
   PFNGLGETPROGRAMINTERFACEIVPROC _glGetProgramInterfaceiv;
   PFNGLGETPROGRAMINTERFACEIVPROC _glGetProgramInterfaceiv;
+  PFNGLGETPROGRAMRESOURCEINDEXPROC _glGetProgramResourceIndex;
   PFNGLGETPROGRAMRESOURCENAMEPROC _glGetProgramResourceName;
   PFNGLGETPROGRAMRESOURCENAMEPROC _glGetProgramResourceName;
   PFNGLGETPROGRAMRESOURCEIVPROC _glGetProgramResourceiv;
   PFNGLGETPROGRAMRESOURCEIVPROC _glGetProgramResourceiv;
 
 

+ 368 - 150
panda/src/glstuff/glShaderContext_src.cxx

@@ -58,16 +58,28 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
   // Ignoring any locations that may have already been set, we assign a new
   // Ignoring any locations that may have already been set, we assign a new
   // unique location to each parameter.
   // unique location to each parameter.
   LocationMap locations;
   LocationMap locations;
+  LocationMap ssbo_bindings;
   GLint next_location = 0;
   GLint next_location = 0;
+  GLint next_ssbo_binding = 0;
   for (const Shader::Parameter &param : _shader->_parameters) {
   for (const Shader::Parameter &param : _shader->_parameters) {
-    locations[param._name] = next_location;
-    next_location += param._type->get_num_parameter_locations();
+    GLint num_locations = 0;
+    GLint num_ssbo_bindings = 0;
+    r_count_locations_bindings(param._type, num_locations, num_ssbo_bindings);
+
+    if (num_locations > 0) {
+      locations[param._name] = next_location;
+      next_location += num_locations;
+    }
+    if (num_ssbo_bindings > 0) {
+      ssbo_bindings[param._name] = next_ssbo_binding;
+      next_ssbo_binding += num_ssbo_bindings;
+    }
   }
   }
 
 
   // We compile and analyze the shader here, instead of in shader.cxx, to
   // We compile and analyze the shader here, instead of in shader.cxx, to
   // avoid gobj getting a dependency on GL stuff.
   // avoid gobj getting a dependency on GL stuff.
-  bool needs_query_locations = false;
-  if (!compile_and_link(locations, needs_query_locations)) {
+  bool remap_locations = false;
+  if (!compile_and_link(locations, remap_locations, ssbo_bindings)) {
     release_resources();
     release_resources();
     s->_error_flag = true;
     s->_error_flag = true;
     return;
     return;
@@ -80,10 +92,12 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
   // yet, so we need to perform reflection on the shader.
   // yet, so we need to perform reflection on the shader.
   SparseArray active_locations;
   SparseArray active_locations;
   if (_is_legacy) {
   if (_is_legacy) {
-    reflect_program();
-    needs_query_locations = true;
+    locations.clear();
+    ssbo_bindings.clear();
+
+    reflect_program(active_locations, locations, ssbo_bindings);
   }
   }
-  else if (!needs_query_locations) {
+  else if (!remap_locations) {
     // We still need to query which uniform locations are actually in use,
     // We still need to query which uniform locations are actually in use,
     // because the GL driver may have optimized some out.
     // because the GL driver may have optimized some out.
     GLint num_active_uniforms = 0;
     GLint num_active_uniforms = 0;
@@ -114,8 +128,27 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
     block._dep = param._binding->get_state_dep();
     block._dep = param._binding->get_state_dep();
     block._bindings.push_back({param._binding, 0});
     block._bindings.push_back({param._binding, 0});
 
 
-    int chosen_location = locations[param._name];
-    int actual_location = needs_query_locations ? -1 : chosen_location;
+    // We chose a location earlier, but if remap_locations was set to true,
+    // we have to map this to the actual location chosen by the driver.
+    int chosen_location = -1;
+    int actual_location = -1;
+    {
+      auto it = locations.find(param._name);
+      if (it != locations.end()) {
+        chosen_location = it->second;
+        if (!remap_locations) {
+          actual_location = it->second;
+        }
+      }
+    }
+
+    int ssbo_binding = -1;
+    {
+      auto it = ssbo_bindings.find(param._name);
+      if (it != ssbo_bindings.end()) {
+        ssbo_binding = it->second;
+      }
+    }
 
 
     // Though the code is written to take advantage of UBOs, we're not using
     // Though the code is written to take advantage of UBOs, we're not using
     // UBOs yet, so we instead have the parameters copied to a scratch space.
     // UBOs yet, so we instead have the parameters copied to a scratch space.
@@ -123,16 +156,34 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
     int resource_index = 0;
     int resource_index = 0;
     std::string name = param._name->get_name();
     std::string name = param._name->get_name();
     char sym_buffer[16];
     char sym_buffer[16];
-    sprintf(sym_buffer, "p%d", chosen_location);
+    sym_buffer[0] = 0;
+    if (remap_locations && chosen_location >= 0) {
+      sprintf(sym_buffer, "p%d", chosen_location);
+    }
     r_collect_uniforms(param, block, param._type, name.c_str(), sym_buffer,
     r_collect_uniforms(param, block, param._type, name.c_str(), sym_buffer,
-                       actual_location, active_locations, resource_index);
+                       actual_location, active_locations, resource_index,
+                       ssbo_binding);
 
 
     if (!block._matrices.empty() || !block._vectors.empty()) {
     if (!block._matrices.empty() || !block._vectors.empty()) {
       _uniform_data_deps |= block._dep;
       _uniform_data_deps |= block._dep;
       _uniform_blocks.push_back(std::move(block));
       _uniform_blocks.push_back(std::move(block));
 
 
-      // Pad space to 16-byte boundary
-      uint32_t size = param._type->get_size_bytes();
+      // We ideally want the tightly packed size, since we are not using UBOs
+      // and the regular glUniform calls use tight packing.
+      uint32_t size;
+      ShaderType::ScalarType scalar_type;
+      uint32_t num_elements;
+      uint32_t num_rows;
+      uint32_t num_cols;
+      if (param._type->as_scalar_type(scalar_type, num_elements, num_rows, num_cols)) {
+        size = num_elements * num_rows * num_cols * ShaderType::get_scalar_size_bytes(scalar_type);
+      } else {
+        // If it's a struct, we just use the regular size.  It's too much, but
+        // since we're using the original offsets from the struct, I can't be
+        // bothered right now to write code to repack the entire struct.
+        size = param._type->get_size_bytes();
+      }
+
       size = (size + 15) & ~15;
       size = (size + 15) & ~15;
       _scratch_space_size = std::max(_scratch_space_size, (size_t)size);
       _scratch_space_size = std::max(_scratch_space_size, (size_t)size);
     }
     }
@@ -161,6 +212,52 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
   }
   }
 }
 }
 
 
+/**
+ * Counts the number of uniform locations and shader storage buffer bindings,
+ * which are separate because OpenGL doesn't use locations for SSBOs (unlike
+ * textures).
+ */
+void CLP(ShaderContext)::
+r_count_locations_bindings(const ShaderType *type,
+                           GLint &num_locations, GLint &num_ssbo_bindings) {
+
+  if (const ShaderType::Array *array_type = type->as_array()) {
+    GLint element_locs = 0, element_binds = 0;
+    r_count_locations_bindings(array_type->get_element_type(), element_locs, element_binds);
+    num_locations += element_locs * array_type->get_num_elements();
+    num_ssbo_bindings += element_binds * array_type->get_num_elements();
+    return;
+  }
+  if (const ShaderType::Struct *struct_type = type->as_struct()) {
+    for (uint32_t i = 0; i < struct_type->get_num_members(); ++i) {
+      const ShaderType::Struct::Member &member = struct_type->get_member(i);
+
+      r_count_locations_bindings(member.type, num_locations, num_ssbo_bindings);
+    }
+    return;
+  }
+
+  ShaderType::ScalarType scalar_type;
+  uint32_t num_elements;
+  uint32_t num_rows;
+  uint32_t num_cols;
+  if (type->as_scalar_type(scalar_type, num_elements, num_rows, num_cols)) {
+    num_locations += num_elements;
+    return;
+  }
+
+  if (type->as_sampled_image() != nullptr ||
+      type->as_image() != nullptr) {
+    ++num_locations;
+    return;
+  }
+
+  if (type->as_storage_buffer()) {
+    ++num_ssbo_bindings;
+    return;
+  }
+}
+
 /**
 /**
  * For UBO emulation, expand the individual uniforms within aggregate types and
  * For UBO emulation, expand the individual uniforms within aggregate types and
  * make a list of individual glUniform calls, also querying their location from
  * make a list of individual glUniform calls, also querying their location from
@@ -170,21 +267,25 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
 void CLP(ShaderContext)::
 void CLP(ShaderContext)::
 r_collect_uniforms(const Shader::Parameter &param, UniformBlock &block,
 r_collect_uniforms(const Shader::Parameter &param, UniformBlock &block,
                    const ShaderType *type, const char *name, const char *sym,
                    const ShaderType *type, const char *name, const char *sym,
-                   int location, const SparseArray &active_locations,
-                   int &resource_index, size_t offset) {
+                   int &cur_location, const SparseArray &active_locations,
+                   int &resource_index, int &ssbo_binding, size_t offset) {
 
 
   ShaderType::ScalarType scalar_type;
   ShaderType::ScalarType scalar_type;
   uint32_t num_elements;
   uint32_t num_elements;
   uint32_t num_rows;
   uint32_t num_rows;
   uint32_t num_cols;
   uint32_t num_cols;
   if (type->as_scalar_type(scalar_type, num_elements, num_rows, num_cols)) {
   if (type->as_scalar_type(scalar_type, num_elements, num_rows, num_cols)) {
+    int location = cur_location;
     if (location < 0) {
     if (location < 0) {
       location = _glgsg->_glGetUniformLocation(_glsl_program, _is_legacy ? name : sym);
       location = _glgsg->_glGetUniformLocation(_glsl_program, _is_legacy ? name : sym);
       if (location < 0) {
       if (location < 0) {
         return;
         return;
       }
       }
-    } else if (!active_locations.get_bit(location)) {
-      return;
+    } else {
+      cur_location += num_elements;
+      if (!active_locations.get_bit(location)) {
+        return;
+      }
     }
     }
     if (GLCAT.is_debug()) {
     if (GLCAT.is_debug()) {
       GLCAT.debug()
       GLCAT.debug()
@@ -285,16 +386,14 @@ r_collect_uniforms(const Shader::Parameter &param, UniformBlock &block,
     char *name_buffer = (char *)alloca(strlen(name) + 14);
     char *name_buffer = (char *)alloca(strlen(name) + 14);
     char *sym_buffer = (char *)alloca(strlen(sym) + 14);
     char *sym_buffer = (char *)alloca(strlen(sym) + 14);
     const ShaderType *element_type = array_type->get_element_type();
     const ShaderType *element_type = array_type->get_element_type();
-    int num_locations = element_type->get_num_parameter_locations();
     size_t stride = (size_t)array_type->get_stride_bytes();
     size_t stride = (size_t)array_type->get_stride_bytes();
 
 
     for (uint32_t i = 0; i < array_type->get_num_elements(); ++i) {
     for (uint32_t i = 0; i < array_type->get_num_elements(); ++i) {
       sprintf(name_buffer, "%s[%u]", name, i);
       sprintf(name_buffer, "%s[%u]", name, i);
       sprintf(sym_buffer, "%s[%u]", sym, i);
       sprintf(sym_buffer, "%s[%u]", sym, i);
-      r_collect_uniforms(param, block, element_type, name_buffer, sym_buffer, location, active_locations, resource_index, offset);
-      if (location >= 0) {
-        location += num_locations;
-      }
+      r_collect_uniforms(param, block, element_type, name_buffer, sym_buffer,
+                         cur_location, active_locations, resource_index, ssbo_binding,
+                         offset);
       offset += stride;
       offset += stride;
     }
     }
     return;
     return;
@@ -310,30 +409,59 @@ r_collect_uniforms(const Shader::Parameter &param, UniformBlock &block,
 
 
       // SPIRV-Cross names struct members _m0, _m1, etc. in declaration order.
       // SPIRV-Cross names struct members _m0, _m1, etc. in declaration order.
       sprintf(sym_buffer, "%s._m%u", sym, i);
       sprintf(sym_buffer, "%s._m%u", sym, i);
-      r_collect_uniforms(param, block, member.type, qualname.c_str(), sym_buffer, location, active_locations, resource_index, offset + member.offset);
+      r_collect_uniforms(param, block, member.type, qualname.c_str(), sym_buffer,
+                         cur_location, active_locations, resource_index, ssbo_binding,
+                         offset + member.offset);
+    }
+    return;
+  }
 
 
-      if (location >= 0) {
-        location += member.type->get_num_parameter_locations();
-      }
+  if (type->as_storage_buffer() != nullptr) {
+    // These are an exception, they do not have locations but bindings.
+    GLint binding = ssbo_binding;
+    if (binding < 0) {
+      // We have to look this one up.
+      GLuint index = _glgsg->_glGetProgramResourceIndex(_glsl_program, GL_SHADER_STORAGE_BLOCK, name);
+      const GLenum props[] = {GL_BUFFER_BINDING};
+      _glgsg->_glGetProgramResourceiv(_glsl_program, GL_SHADER_STORAGE_BLOCK, index, 1, props, 1, nullptr, &binding);
+    } else {
+      ++ssbo_binding;
     }
     }
+
+    if (GLCAT.is_debug()) {
+      GLCAT.debug()
+        << "Storage block " << name << " with type " << *type
+        << " is bound at binding " << binding << "\n";
+    }
+
+    StorageBlock block;
+    block._binding = param._binding;
+    block._resource_id = param._binding->get_resource_id(resource_index++, type);
+    block._binding_index = binding++;
+    _storage_blocks.push_back(std::move(block));
     return;
     return;
   }
   }
 
 
+  int location = cur_location;
   if (location < 0) {
   if (location < 0) {
     location = _glgsg->_glGetUniformLocation(_glsl_program, _is_legacy ? name : sym);
     location = _glgsg->_glGetUniformLocation(_glsl_program, _is_legacy ? name : sym);
     if (location < 0) {
     if (location < 0) {
       return;
       return;
     }
     }
-  } else if (!active_locations.get_bit(location)) {
-    return;
+  } else {
+    ++cur_location;
+    if (!active_locations.get_bit(location)) {
+      return;
+    }
   }
   }
+
   if (GLCAT.is_debug()) {
   if (GLCAT.is_debug()) {
     GLCAT.debug()
     GLCAT.debug()
       << "Active uniform " << name << " with type " << *type
       << "Active uniform " << name << " with type " << *type
       << " is bound to location " << location << "\n";
       << " is bound to location " << location << "\n";
   }
   }
 
 
-  if (const ::ShaderType::SampledImage *sampler = type->as_sampled_image()) {
+  if (const ShaderType::SampledImage *sampler = type->as_sampled_image()) {
     TextureUnit unit;
     TextureUnit unit;
     unit._binding = param._binding;
     unit._binding = param._binding;
     unit._resource_id = param._binding->get_resource_id(resource_index++, type);
     unit._resource_id = param._binding->get_resource_id(resource_index++, type);
@@ -342,7 +470,7 @@ r_collect_uniforms(const Shader::Parameter &param, UniformBlock &block,
     _glgsg->_glUniform1i(location, (GLint)_texture_units.size());
     _glgsg->_glUniform1i(location, (GLint)_texture_units.size());
     _texture_units.push_back(std::move(unit));
     _texture_units.push_back(std::move(unit));
   }
   }
-  else if (const ::ShaderType::Image *image = type->as_image()) {
+  else if (const ShaderType::Image *image = type->as_image()) {
     ImageUnit unit;
     ImageUnit unit;
     unit._binding = param._binding;
     unit._binding = param._binding;
     unit._resource_id = param._binding->get_resource_id(resource_index++, type);
     unit._resource_id = param._binding->get_resource_id(resource_index++, type);
@@ -359,10 +487,13 @@ r_collect_uniforms(const Shader::Parameter &param, UniformBlock &block,
 
 
 /**
 /**
  * Analyzes the uniforms, attributes, etc. of a shader that was not already
  * Analyzes the uniforms, attributes, etc. of a shader that was not already
- * reflected.
+ * reflected.  Also sets active_locations to all ranges of uniforms that are
+ * active, and anything that is known about top-level uniform names (that are
+ * not in a struct or array) is also already filled into the maps.  The rest
+ * will have to be queried later.
  */
  */
 void CLP(ShaderContext)::
 void CLP(ShaderContext)::
-reflect_program() {
+reflect_program(SparseArray &active_locations, LocationMap &locations, LocationMap &ssbo_bindings) {
   // Process the vertex attributes first.
   // Process the vertex attributes first.
   GLint param_count = 0;
   GLint param_count = 0;
   GLint name_buflen = 0;
   GLint name_buflen = 0;
@@ -412,7 +543,9 @@ reflect_program() {
     }
     }
   }
   }
 
 
-#ifndef OPENGLES
+  _shader->_parameters.clear();
+
+#ifndef OPENGLES_1
   // Get the used shader storage blocks.
   // Get the used shader storage blocks.
   if (_glgsg->_supports_shader_buffers) {
   if (_glgsg->_supports_shader_buffers) {
     GLint block_count = 0, block_maxlength = 0;
     GLint block_count = 0, block_maxlength = 0;
@@ -423,7 +556,17 @@ reflect_program() {
     block_maxlength = max(64, block_maxlength);
     block_maxlength = max(64, block_maxlength);
     char *block_name_cstr = (char *)alloca(block_maxlength);
     char *block_name_cstr = (char *)alloca(block_maxlength);
 
 
+#ifndef OPENGLES
     BitArray bindings;
     BitArray bindings;
+#endif
+
+    // OpenGL exposes SSBO arrays as individual members named name[0][1],
+    // name[0][2], etc. with potential gaps for unused SSBOs.  We have to
+    // untangle this.
+    struct SSBO {
+      pvector<uint32_t> _array_sizes;
+    };
+    pmap<CPT_InternalName, SSBO> ssbos;
 
 
     for (int i = 0; i < block_count; ++i) {
     for (int i = 0; i < block_count; ++i) {
       block_name_cstr[0] = 0;
       block_name_cstr[0] = 0;
@@ -433,12 +576,14 @@ reflect_program() {
       GLint values[2];
       GLint values[2];
       _glgsg->_glGetProgramResourceiv(_glsl_program, GL_SHADER_STORAGE_BLOCK, i, 2, props, 2, nullptr, values);
       _glgsg->_glGetProgramResourceiv(_glsl_program, GL_SHADER_STORAGE_BLOCK, i, 2, props, 2, nullptr, values);
 
 
+#ifndef OPENGLES
       if (bindings.get_bit(values[0])) {
       if (bindings.get_bit(values[0])) {
         // Binding index already in use, assign a different one.
         // Binding index already in use, assign a different one.
         values[0] = bindings.get_lowest_off_bit();
         values[0] = bindings.get_lowest_off_bit();
         _glgsg->_glShaderStorageBlockBinding(_glsl_program, i, values[0]);
         _glgsg->_glShaderStorageBlockBinding(_glsl_program, i, values[0]);
       }
       }
       bindings.set_bit(values[0]);
       bindings.set_bit(values[0]);
+#endif
 
 
       if (GLCAT.is_debug()) {
       if (GLCAT.is_debug()) {
         GLCAT.debug()
         GLCAT.debug()
@@ -447,11 +592,49 @@ reflect_program() {
           << values[0] << "\n";
           << values[0] << "\n";
       }
       }
 
 
-      StorageBlock block;
-      block._name = InternalName::make(block_name_cstr);
-      block._binding_index = values[0];
-      block._min_size = (GLuint)values[1];
-      _storage_blocks.push_back(block);
+      // Parse the array elements off the end of the name.
+      char *p = strchr(block_name_cstr, '[');
+      if (p != nullptr) {
+        // It's an array, this is a bit annoying.
+        CPT_InternalName name = InternalName::make(std::string(block_name_cstr, p - block_name_cstr));
+        SSBO &ssbo = ssbos[name];
+        size_t i = 0;
+        do {
+          ++p;
+          uint32_t count = strtoul(p, &p, 10) + 1;
+          if (*p == ']') {
+            if (i >= ssbo._array_sizes.size()) {
+              ssbo._array_sizes.resize(i + 1, 0);
+            }
+            if (count > ssbo._array_sizes[i]) {
+              ssbo._array_sizes[i] = count;
+            }
+            ++i;
+            ++p;
+          }
+        } while (*p == '[');
+      } else {
+        // Simple case.  We can already jot down the binding, so we don't have
+        // to query it later.
+        CPT(InternalName) name = InternalName::make(std::string(block_name_cstr));
+        ssbo_bindings[name] = values[0];
+        ssbos[std::move(name)];
+      }
+    }
+
+    for (auto &item : ssbos) {
+      const InternalName *name = item.first.p();
+      SSBO &ssbo = item.second;
+
+      //TODO: write code to actually query the block variables.
+      const ShaderType *struct_type = ShaderType::register_type(ShaderType::Struct());
+      const ShaderType *type = ShaderType::register_type(ShaderType::StorageBuffer(struct_type, ShaderType::Access::READ_WRITE));
+
+      std::reverse(ssbo._array_sizes.begin(), ssbo._array_sizes.end());
+      for (uint32_t count : ssbo._array_sizes) {
+        type = ShaderType::register_type(ShaderType::Array(type, count));
+      }
+      _shader->add_parameter(name, type);
     }
     }
   }
   }
 #endif
 #endif
@@ -461,8 +644,6 @@ reflect_program() {
   param_count = 0;
   param_count = 0;
   _glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_UNIFORMS, &param_count);
   _glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_UNIFORMS, &param_count);
 
 
-  _shader->_parameters.clear();
-
   // First gather the uniforms and sort them by location.
   // First gather the uniforms and sort them by location.
   struct Uniform {
   struct Uniform {
     std::string name;
     std::string name;
@@ -504,6 +685,8 @@ reflect_program() {
       continue;
       continue;
     }
     }
 
 
+    active_locations.set_range(loc, param_size);
+
     uniforms.insert({loc, {std::string(name_buffer), param_size, param_type}});
     uniforms.insert({loc, {std::string(name_buffer), param_size, param_type}});
   }
   }
 
 
@@ -561,7 +744,9 @@ reflect_program() {
 
 
       if (struct_stack.empty()) {
       if (struct_stack.empty()) {
         // Add struct as top-level
         // Add struct as top-level
-        _shader->add_parameter(InternalName::make(item.name), type, item.loc);
+        CPT(InternalName) name = InternalName::make(item.name);
+        locations[name] = item.loc;
+        _shader->add_parameter(std::move(name), type, item.loc);
       }
       }
       else if (struct_stack.back().num_elements <= 1) {
       else if (struct_stack.back().num_elements <= 1) {
         // Add as nested struct member
         // Add as nested struct member
@@ -582,9 +767,11 @@ reflect_program() {
     }
     }
 
 
     if (struct_stack.empty()) {
     if (struct_stack.empty()) {
-      // Add as top-level
+      // Add as top-level.
       assert(parts.size() == 1);
       assert(parts.size() == 1);
-      _shader->add_parameter(InternalName::make(parts[0]), type, loc);
+      CPT(InternalName) name = InternalName::make(parts[0]);
+      locations[name] = loc;
+      _shader->add_parameter(std::move(name), type, loc);
     }
     }
     else if (struct_stack.back().num_elements <= 1) {
     else if (struct_stack.back().num_elements <= 1) {
       // Add as struct member
       // Add as struct member
@@ -607,7 +794,9 @@ reflect_program() {
 
 
     if (struct_stack.empty()) {
     if (struct_stack.empty()) {
       // Add struct as top-level
       // Add struct as top-level
-      _shader->add_parameter(InternalName::make(item.name), type, item.loc);
+      CPT(InternalName) name = InternalName::make(item.name);
+      locations[name] = item.loc;
+      _shader->add_parameter(std::move(name), type, item.loc);
     }
     }
     else if (struct_stack.back().num_elements <= 1) {
     else if (struct_stack.back().num_elements <= 1) {
       // Add as nested struct member
       // Add as nested struct member
@@ -1030,77 +1219,77 @@ get_param_type(GLenum param_type) {
 
 
 #ifndef OPENGLES
 #ifndef OPENGLES
   case GL_IMAGE_1D:
   case GL_IMAGE_1D:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_1d_texture, ShaderType::ST_float, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_1d_texture, ShaderType::ST_float, ShaderType::Access::READ_WRITE));
 
 
   case GL_INT_IMAGE_1D:
   case GL_INT_IMAGE_1D:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_1d_texture, ShaderType::ST_int, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_1d_texture, ShaderType::ST_int, ShaderType::Access::READ_WRITE));
 
 
   case GL_UNSIGNED_INT_IMAGE_1D:
   case GL_UNSIGNED_INT_IMAGE_1D:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_1d_texture, ShaderType::ST_uint, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_1d_texture, ShaderType::ST_uint, ShaderType::Access::READ_WRITE));
 
 
   case GL_IMAGE_1D_ARRAY:
   case GL_IMAGE_1D_ARRAY:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_1d_texture_array, ShaderType::ST_float, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_1d_texture_array, ShaderType::ST_float, ShaderType::Access::READ_WRITE));
 
 
   case GL_INT_IMAGE_1D_ARRAY:
   case GL_INT_IMAGE_1D_ARRAY:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_1d_texture_array, ShaderType::ST_int, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_1d_texture_array, ShaderType::ST_int, ShaderType::Access::READ_WRITE));
 
 
   case GL_UNSIGNED_INT_IMAGE_1D_ARRAY:
   case GL_UNSIGNED_INT_IMAGE_1D_ARRAY:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_1d_texture_array, ShaderType::ST_uint, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_1d_texture_array, ShaderType::ST_uint, ShaderType::Access::READ_WRITE));
 #endif
 #endif
 
 
   case GL_IMAGE_2D:
   case GL_IMAGE_2D:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_2d_texture, ShaderType::ST_float, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_2d_texture, ShaderType::ST_float, ShaderType::Access::READ_WRITE));
 
 
   case GL_INT_IMAGE_2D:
   case GL_INT_IMAGE_2D:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_2d_texture, ShaderType::ST_int, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_2d_texture, ShaderType::ST_int, ShaderType::Access::READ_WRITE));
 
 
   case GL_UNSIGNED_INT_IMAGE_2D:
   case GL_UNSIGNED_INT_IMAGE_2D:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_2d_texture, ShaderType::ST_uint, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_2d_texture, ShaderType::ST_uint, ShaderType::Access::READ_WRITE));
 
 
   case GL_IMAGE_2D_ARRAY:
   case GL_IMAGE_2D_ARRAY:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_2d_texture_array, ShaderType::ST_float, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_2d_texture_array, ShaderType::ST_float, ShaderType::Access::READ_WRITE));
 
 
   case GL_INT_IMAGE_2D_ARRAY:
   case GL_INT_IMAGE_2D_ARRAY:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_2d_texture_array, ShaderType::ST_int, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_2d_texture_array, ShaderType::ST_int, ShaderType::Access::READ_WRITE));
 
 
   case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
   case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_2d_texture_array, ShaderType::ST_uint, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_2d_texture_array, ShaderType::ST_uint, ShaderType::Access::READ_WRITE));
 
 
   case GL_IMAGE_3D:
   case GL_IMAGE_3D:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_3d_texture, ShaderType::ST_float, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_3d_texture, ShaderType::ST_float, ShaderType::Access::READ_WRITE));
 
 
   case GL_INT_IMAGE_3D:
   case GL_INT_IMAGE_3D:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_3d_texture, ShaderType::ST_int, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_3d_texture, ShaderType::ST_int, ShaderType::Access::READ_WRITE));
 
 
   case GL_UNSIGNED_INT_IMAGE_3D:
   case GL_UNSIGNED_INT_IMAGE_3D:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_3d_texture, ShaderType::ST_uint, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_3d_texture, ShaderType::ST_uint, ShaderType::Access::READ_WRITE));
 
 
   case GL_IMAGE_CUBE:
   case GL_IMAGE_CUBE:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_cube_map, ShaderType::ST_float, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_cube_map, ShaderType::ST_float, ShaderType::Access::READ_WRITE));
 
 
   case GL_INT_IMAGE_CUBE:
   case GL_INT_IMAGE_CUBE:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_cube_map, ShaderType::ST_int, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_cube_map, ShaderType::ST_int, ShaderType::Access::READ_WRITE));
 
 
   case GL_UNSIGNED_INT_IMAGE_CUBE:
   case GL_UNSIGNED_INT_IMAGE_CUBE:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_cube_map, ShaderType::ST_uint, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_cube_map, ShaderType::ST_uint, ShaderType::Access::READ_WRITE));
 
 
   case GL_IMAGE_CUBE_MAP_ARRAY:
   case GL_IMAGE_CUBE_MAP_ARRAY:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_cube_map_array, ShaderType::ST_float, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_cube_map_array, ShaderType::ST_float, ShaderType::Access::READ_WRITE));
 
 
   case GL_INT_IMAGE_CUBE_MAP_ARRAY:
   case GL_INT_IMAGE_CUBE_MAP_ARRAY:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_cube_map_array, ShaderType::ST_int, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_cube_map_array, ShaderType::ST_int, ShaderType::Access::READ_WRITE));
 
 
   case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY:
   case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_cube_map_array, ShaderType::ST_uint, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_cube_map_array, ShaderType::ST_uint, ShaderType::Access::READ_WRITE));
 
 
   case GL_IMAGE_BUFFER:
   case GL_IMAGE_BUFFER:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_buffer_texture, ShaderType::ST_float, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_buffer_texture, ShaderType::ST_float, ShaderType::Access::READ_WRITE));
 
 
   case GL_INT_IMAGE_BUFFER:
   case GL_INT_IMAGE_BUFFER:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_buffer_texture, ShaderType::ST_int, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_buffer_texture, ShaderType::ST_int, ShaderType::Access::READ_WRITE));
 
 
   case GL_UNSIGNED_INT_IMAGE_BUFFER:
   case GL_UNSIGNED_INT_IMAGE_BUFFER:
-    return ShaderType::register_type(ShaderType::Image(Texture::TT_buffer_texture, ShaderType::ST_uint, ShaderType::Access::read_write));
+    return ShaderType::register_type(ShaderType::Image(Texture::TT_buffer_texture, ShaderType::ST_uint, ShaderType::Access::READ_WRITE));
   }
   }
 
 
   GLCAT.error()
   GLCAT.error()
@@ -1457,7 +1646,7 @@ issue_parameters(int altered) {
       }
       }
 
 
       for (const UniformBlock::Binding &binding : block._bindings) {
       for (const UniformBlock::Binding &binding : block._bindings) {
-        binding._binding->fetch_data(state, scratch + binding._offset, false);
+        binding._binding->fetch_data(state, scratch + binding._offset, true);
       }
       }
 
 
       for (const UniformBlock::Call &call : block._matrices) {
       for (const UniformBlock::Call &call : block._matrices) {
@@ -1782,7 +1971,7 @@ update_shader_texture_bindings(ShaderContext *prev) {
     for (int i = 0; i < num_image_units; ++i) {
     for (int i = 0; i < num_image_units; ++i) {
       ImageUnit &unit = _image_units[i];
       ImageUnit &unit = _image_units[i];
 
 
-      ShaderType::Access access = ShaderType::Access::read_write;
+      ShaderType::Access access = ShaderType::Access::READ_WRITE;
       int z = -1;
       int z = -1;
       int n = 0;
       int n = 0;
       PT(Texture) tex = unit._binding->fetch_texture_image(state, unit._resource_id, access, z, n);
       PT(Texture) tex = unit._binding->fetch_texture_image(state, unit._resource_id, access, z, n);
@@ -1836,7 +2025,7 @@ update_shader_texture_bindings(ShaderContext *prev) {
         }
         }
 
 
         if (gl_force_image_bindings_writeonly) {
         if (gl_force_image_bindings_writeonly) {
-          access = access & ShaderType::Access::write_only;
+          access = access & ShaderType::Access::WRITE_ONLY;
         }
         }
 
 
         GLenum gl_access = GL_READ_WRITE;
         GLenum gl_access = GL_READ_WRITE;
@@ -1845,18 +2034,18 @@ update_shader_texture_bindings(ShaderContext *prev) {
         GLboolean layered = z < 0;
         GLboolean layered = z < 0;
 
 
         switch (access) {
         switch (access) {
-        case ShaderType::Access::none:
+        case ShaderType::Access::NONE:
           gl_tex = 0;
           gl_tex = 0;
-        case ShaderType::Access::read_only:
+        case ShaderType::Access::READ_ONLY:
           gl_access = GL_READ_ONLY;
           gl_access = GL_READ_ONLY;
           break;
           break;
 
 
-        case ShaderType::Access::write_only:
+        case ShaderType::Access::WRITE_ONLY:
           gl_access = GL_WRITE_ONLY;
           gl_access = GL_WRITE_ONLY;
           unit._written = true;
           unit._written = true;
           break;
           break;
 
 
-        case ShaderType::Access::read_write:
+        case ShaderType::Access::READ_WRITE:
           gl_access = GL_READ_WRITE;
           gl_access = GL_READ_WRITE;
           unit._written = true;
           unit._written = true;
           break;
           break;
@@ -1979,24 +2168,15 @@ update_shader_texture_bindings(ShaderContext *prev) {
  */
  */
 void CLP(ShaderContext)::
 void CLP(ShaderContext)::
 update_shader_buffer_bindings(ShaderContext *prev) {
 update_shader_buffer_bindings(ShaderContext *prev) {
-#ifndef OPENGLES
   // Update the shader storage buffer bindings.
   // Update the shader storage buffer bindings.
-  const ShaderAttrib *attrib = _glgsg->_target_shader;
-
-  for (size_t i = 0; i < _storage_blocks.size(); ++i) {
-    StorageBlock &block = _storage_blocks[i];
+  ShaderInputBinding::State state;
+  state.gsg = _glgsg;
+  state.matrix_cache = &_matrix_cache[0];
 
 
-    ShaderBuffer *buffer = attrib->get_shader_input_buffer(block._name);
-#ifndef NDEBUG
-    if (buffer->get_data_size_bytes() < block._min_size) {
-      GLCAT.error()
-        << "cannot bind " << *buffer << " to shader because it is too small"
-           " (expected at least " << block._min_size << " bytes)\n";
-    }
-#endif
+  for (const StorageBlock &block : _storage_blocks) {
+    PT(ShaderBuffer) buffer = block._binding->fetch_shader_buffer(state, block._resource_id);
     _glgsg->apply_shader_buffer(block._binding_index, buffer);
     _glgsg->apply_shader_buffer(block._binding_index, buffer);
   }
   }
-#endif
 }
 }
 
 
 /**
 /**
@@ -2140,35 +2320,36 @@ report_program_errors(GLuint program, bool fatal) {
  */
  */
 bool CLP(ShaderContext)::
 bool CLP(ShaderContext)::
 attach_shader(const ShaderModule *module, Shader::ModuleSpecConstants &consts,
 attach_shader(const ShaderModule *module, Shader::ModuleSpecConstants &consts,
-              const LocationMap &locations, bool &needs_query_locations) {
+              const LocationMap &locations, bool &remap_locations,
+              const LocationMap &ssbo_bindings) {
   ShaderModule::Stage stage = module->get_stage();
   ShaderModule::Stage stage = module->get_stage();
 
 
   GLuint handle = 0;
   GLuint handle = 0;
   switch (stage) {
   switch (stage) {
-  case ShaderModule::Stage::vertex:
+  case ShaderModule::Stage::VERTEX:
     handle = _glgsg->_glCreateShader(GL_VERTEX_SHADER);
     handle = _glgsg->_glCreateShader(GL_VERTEX_SHADER);
     break;
     break;
-  case ShaderModule::Stage::fragment:
+  case ShaderModule::Stage::FRAGMENT:
     handle = _glgsg->_glCreateShader(GL_FRAGMENT_SHADER);
     handle = _glgsg->_glCreateShader(GL_FRAGMENT_SHADER);
     break;
     break;
 #ifndef OPENGLES
 #ifndef OPENGLES
-  case ShaderModule::Stage::geometry:
+  case ShaderModule::Stage::GEOMETRY:
     if (_glgsg->get_supports_geometry_shaders()) {
     if (_glgsg->get_supports_geometry_shaders()) {
       handle = _glgsg->_glCreateShader(GL_GEOMETRY_SHADER);
       handle = _glgsg->_glCreateShader(GL_GEOMETRY_SHADER);
     }
     }
     break;
     break;
-  case ShaderModule::Stage::tess_control:
+  case ShaderModule::Stage::TESS_CONTROL:
     if (_glgsg->get_supports_tessellation_shaders()) {
     if (_glgsg->get_supports_tessellation_shaders()) {
       handle = _glgsg->_glCreateShader(GL_TESS_CONTROL_SHADER);
       handle = _glgsg->_glCreateShader(GL_TESS_CONTROL_SHADER);
     }
     }
     break;
     break;
-  case ShaderModule::Stage::tess_evaluation:
+  case ShaderModule::Stage::TESS_EVALUATION:
     if (_glgsg->get_supports_tessellation_shaders()) {
     if (_glgsg->get_supports_tessellation_shaders()) {
       handle = _glgsg->_glCreateShader(GL_TESS_EVALUATION_SHADER);
       handle = _glgsg->_glCreateShader(GL_TESS_EVALUATION_SHADER);
     }
     }
     break;
     break;
 #endif
 #endif
-  case ShaderModule::Stage::compute:
+  case ShaderModule::Stage::COMPUTE:
     if (_glgsg->get_supports_compute_shaders()) {
     if (_glgsg->get_supports_compute_shaders()) {
       handle = _glgsg->_glCreateShader(GL_COMPUTE_SHADER);
       handle = _glgsg->_glCreateShader(GL_COMPUTE_SHADER);
     }
     }
@@ -2193,11 +2374,23 @@ attach_shader(const ShaderModule *module, Shader::ModuleSpecConstants &consts,
     ShaderModuleSpirV *spv = (ShaderModuleSpirV *)module;
     ShaderModuleSpirV *spv = (ShaderModuleSpirV *)module;
 
 
     pmap<uint32_t, int> id_to_location;
     pmap<uint32_t, int> id_to_location;
-    for (size_t pi = 0; pi < spv->get_num_parameters(); ++pi) {
-      const ShaderModule::Variable &var = spv->get_parameter(pi);
-      auto it = locations.find(var.name);
-      if (it != locations.end()) {
-        id_to_location[var.id] = it->second;
+    pvector<uint32_t> ssbo_binding_ids;
+    for (size_t pi = 0; pi < module->get_num_parameters(); ++pi) {
+      const ShaderModule::Variable &var = module->get_parameter(pi);
+      {
+        auto it = locations.find(var.name);
+        if (it != locations.end()) {
+          id_to_location[var.id] = it->second;
+        }
+      }
+      {
+        auto it = ssbo_bindings.find(var.name);
+        if (it != ssbo_bindings.end()) {
+          if (it->second >= (int)ssbo_binding_ids.size()) {
+            ssbo_binding_ids.resize(it->second + 1, 0);
+          }
+          ssbo_binding_ids[it->second] = var.id;
+        }
       }
       }
     }
     }
 
 
@@ -2216,33 +2409,21 @@ attach_shader(const ShaderModule *module, Shader::ModuleSpecConstants &consts,
 
 
       if (!id_to_location.empty()) {
       if (!id_to_location.empty()) {
         transformer.assign_locations(id_to_location);
         transformer.assign_locations(id_to_location);
-      }
 
 
-      ShaderModuleSpirV::InstructionStream stream = transformer.get_result();
-
-      if (_glgsg->_gl_vendor == "NVIDIA Corporation" && !id_to_location.empty()) {
-        // Sigh... NVIDIA driver gives an error if the SPIR-V ID doesn't match
-        // for variables with overlapping locations if the OpName is stripped.
-        // We'll have to just insert OpNames for every parameter.
-        // https://forums.developer.nvidia.com/t/gl-arb-gl-spirv-bug-duplicate-location-link-error-if-opname-is-stripped-from-spir-v-shader/128491
-        // Bug was found with 446.14 drivers on Windows 10 64-bit.
-        ShaderModuleSpirV::InstructionIterator it = stream.begin_annotations();
-        for (ShaderModuleSpirV::Instruction op : spv->_instructions) {
-          if (op.opcode == spv::OpVariable &&
-              (spv::StorageClass)op.args[2] == spv::StorageClassUniformConstant) {
-            uint32_t var_id = op.args[1];
-            auto lit = id_to_location.find(var_id);
-            if (lit != id_to_location.end()) {
-              uint32_t args[4] = {var_id, 0, 0, 0};
-              int len = sprintf((char *)(args + 1), "p%d", lit->second);
-              nassertr(len > 0 && len < 12, false);
-              it = stream.insert(it, spv::OpName, args, len / 4 + 2);
-              ++it;
-            }
-          }
+        if (_glgsg->_gl_vendor == "NVIDIA Corporation") {
+          // Sigh... NVIDIA driver gives an error if the SPIR-V ID doesn't match
+          // for variables with overlapping locations if the OpName is stripped.
+          // We'll have to just insert OpNames for every parameter.
+          // https://forums.developer.nvidia.com/t/gl-arb-gl-spirv-bug-duplicate-location-link-error-if-opname-is-stripped-from-spir-v-shader/128491
+          // Bug was found with 446.14 drivers on Windows 10 64-bit.
+          transformer.assign_procedural_names("p", id_to_location);
         }
         }
       }
       }
+      if (!ssbo_binding_ids.empty()) {
+        transformer.bind_descriptor_set(0, ssbo_binding_ids);
+      }
 
 
+      ShaderModuleSpirV::InstructionStream stream = transformer.get_result();
       _glgsg->_glShaderBinary(1, &handle, GL_SHADER_BINARY_FORMAT_SPIR_V_ARB,
       _glgsg->_glShaderBinary(1, &handle, GL_SHADER_BINARY_FORMAT_SPIR_V_ARB,
                               (const char *)stream.get_data(),
                               (const char *)stream.get_data(),
                               stream.get_data_size() * sizeof(uint32_t));
                               stream.get_data_size() * sizeof(uint32_t));
@@ -2260,9 +2441,8 @@ attach_shader(const ShaderModule *module, Shader::ModuleSpecConstants &consts,
           << "Transpiling SPIR-V " << stage << " shader "
           << "Transpiling SPIR-V " << stage << " shader "
           << module->get_source_filename() << "\n";
           << module->get_source_filename() << "\n";
       }
       }
-      spirv_cross::CompilerGLSL compiler(std::vector<uint32_t>(spv->get_data(), spv->get_data() + spv->get_data_size()));
-      spirv_cross::CompilerGLSL::Options options;
 
 
+      spirv_cross::CompilerGLSL::Options options;
       options.version = _glgsg->_glsl_version;
       options.version = _glgsg->_glsl_version;
 #ifdef OPENGLES
 #ifdef OPENGLES
       options.es = true;
       options.es = true;
@@ -2274,12 +2454,42 @@ attach_shader(const ShaderModule *module, Shader::ModuleSpecConstants &consts,
 #endif
 #endif
       options.vertex.support_nonzero_base_instance = false;
       options.vertex.support_nonzero_base_instance = false;
       options.enable_420pack_extension = false;
       options.enable_420pack_extension = false;
-      compiler.set_common_options(options);
+
+      // We want to use explicit bindings for SSBOs, which requires 420 or
+      // the 420pack extension.  Presumably we have it if we support SSBOs.
+      if (!ssbo_binding_ids.empty() && !options.es && options.version < 420) {
+        options.enable_420pack_extension = true;
+      }
 
 
       if (options.version < 130) {
       if (options.version < 130) {
         _emulate_float_attribs = true;
         _emulate_float_attribs = true;
       }
       }
 
 
+      ShaderModuleSpirV::InstructionStream stream = spv->_instructions;
+
+      // It's really important that we don't have any member name decorations
+      // if we're going to end up remapping the locations, because we rely on
+      // spirv-cross generating the standard _m0, _m2, etc. names.
+      if ((!options.es && options.version < 430) ||
+          (options.es && options.version < 310)) {
+        ShaderModuleSpirV::InstructionIterator it = stream.begin();
+        while (it != stream.end()) {
+          ShaderModuleSpirV::Instruction op = *it;
+          if (op.opcode == spv::OpMemberName) {
+            it = stream.erase(it);
+            continue;
+          }
+          else if (op.opcode == spv::OpFunction || op.is_annotation()) {
+            // There are no more debug instructions after this point.
+            break;
+          }
+          ++it;
+        }
+      }
+
+      spirv_cross::CompilerGLSL compiler(std::move(stream._words));
+      compiler.set_common_options(options);
+
       // At this time, SPIRV-Cross doesn't always add these automatically.
       // At this time, SPIRV-Cross doesn't always add these automatically.
       uint64_t used_caps = module->get_used_capabilities();
       uint64_t used_caps = module->get_used_capabilities();
 #ifndef OPENGLES
 #ifndef OPENGLES
@@ -2297,6 +2507,9 @@ attach_shader(const ShaderModule *module, Shader::ModuleSpecConstants &consts,
         if (options.version < 400 && (used_caps & Shader::C_dynamic_indexing) != 0) {
         if (options.version < 400 && (used_caps & Shader::C_dynamic_indexing) != 0) {
           compiler.require_extension("GL_ARB_gpu_shader5");
           compiler.require_extension("GL_ARB_gpu_shader5");
         }
         }
+        if (options.version < 430 && (used_caps & Shader::C_storage_buffer) != 0) {
+          compiler.require_extension("GL_ARB_shader_storage_buffer_object");
+        }
       }
       }
       else
       else
 #endif
 #endif
@@ -2322,29 +2535,22 @@ attach_shader(const ShaderModule *module, Shader::ModuleSpecConstants &consts,
 
 
         char buf[1024];
         char buf[1024];
         if (sc == spv::StorageClassUniformConstant) {
         if (sc == spv::StorageClassUniformConstant) {
-          CPT(InternalName) name;
-          for (size_t i = 0; i < spv->get_num_parameters(); ++i) {
-            const ShaderModule::Variable &var = spv->get_parameter(i);
-            if (var.id == id) {
-              auto it = locations.find(var.name);
-              if (it != locations.end()) {
-                sprintf(buf, "p%u", it->second);
-                compiler.set_name(id, buf);
-                compiler.set_decoration(id, spv::DecorationLocation, it->second);
-              }
-              break;
+          auto it = id_to_location.find(id);
+          if (it != id_to_location.end()) {
+            sprintf(buf, "p%u", it->second);
+            compiler.set_name(id, buf);
+            compiler.set_decoration(id, spv::DecorationLocation, it->second);
+
+            // Older versions of OpenGL (ES) do not support explicit uniform
+            // locations, and we need to query the locations later.
+            if ((!options.es && options.version < 430) ||
+                (options.es && options.version < 310)) {
+              remap_locations = true;
             }
             }
           }
           }
-
-          // Older versions of OpenGL (ES) do not support explicit uniform
-          // locations, and we need to query the locations later.
-          if ((!options.es && options.version < 430) ||
-              (options.es && options.version < 310)) {
-            needs_query_locations = true;
-          }
         }
         }
         else if (sc == spv::StorageClassInput) {
         else if (sc == spv::StorageClassInput) {
-          if (stage == ShaderModule::Stage::vertex) {
+          if (stage == ShaderModule::Stage::VERTEX) {
             // Explicit attrib locations were added in GLSL 3.30, but we can
             // Explicit attrib locations were added in GLSL 3.30, but we can
             // override the binding in older versions using the API.
             // override the binding in older versions using the API.
             sprintf(buf, "a%u", loc);
             sprintf(buf, "a%u", loc);
@@ -2360,7 +2566,7 @@ attach_shader(const ShaderModule *module, Shader::ModuleSpecConstants &consts,
           compiler.set_name(id, buf);
           compiler.set_name(id, buf);
         }
         }
         else if (sc == spv::StorageClassOutput) {
         else if (sc == spv::StorageClassOutput) {
-          if (stage == ShaderModule::Stage::fragment) {
+          if (stage == ShaderModule::Stage::FRAGMENT) {
             // Output of the last stage, same story as above.
             // Output of the last stage, same story as above.
             sprintf(buf, "o%u", loc);
             sprintf(buf, "o%u", loc);
             if (options.version < 330) {
             if (options.version < 330) {
@@ -2374,6 +2580,14 @@ attach_shader(const ShaderModule *module, Shader::ModuleSpecConstants &consts,
         }
         }
       }
       }
 
 
+      // Add bindings for the shader storage buffers.
+      for (size_t binding = 0; binding < ssbo_binding_ids.size(); ++binding) {
+        uint32_t id = ssbo_binding_ids[binding];
+        if (id > 0) {
+          compiler.set_decoration(id, spv::DecorationBinding, binding);
+        }
+      }
+
       // Optimize out unused variables.
       // Optimize out unused variables.
       compiler.set_enabled_interface_variables(compiler.get_active_interface_variables());
       compiler.set_enabled_interface_variables(compiler.get_active_interface_variables());
 
 
@@ -2422,10 +2636,14 @@ attach_shader(const ShaderModule *module, Shader::ModuleSpecConstants &consts,
 }
 }
 
 
 /**
 /**
- * This subroutine compiles a GLSL shader.
+ * This subroutine compiles a GLSL shader.  The given locations and SSBO
+ * bindings will be assigned.  If that is not possible, it will instead
+ * generate names for the uniforms based on the given locations, and
+ * remap_locations will be set to true.
  */
  */
 bool CLP(ShaderContext)::
 bool CLP(ShaderContext)::
-compile_and_link(const LocationMap &locations, bool &needs_query_locations) {
+compile_and_link(const LocationMap &locations, bool &remap_locations,
+                 const LocationMap &ssbo_bindings) {
   _modules.clear();
   _modules.clear();
   _glsl_program = _glgsg->_glCreateProgram();
   _glsl_program = _glgsg->_glCreateProgram();
   if (!_glsl_program) {
   if (!_glsl_program) {
@@ -2464,7 +2682,7 @@ compile_and_link(const LocationMap &locations, bool &needs_query_locations) {
   bool valid = true;
   bool valid = true;
   for (Shader::LinkedModule &linked_module : _shader->_modules) {
   for (Shader::LinkedModule &linked_module : _shader->_modules) {
     valid &= attach_shader(linked_module._module.get_read_pointer(), linked_module._consts,
     valid &= attach_shader(linked_module._module.get_read_pointer(), linked_module._consts,
-                           locations, needs_query_locations);
+                           locations, remap_locations, ssbo_bindings);
   }
   }
 
 
   if (!valid) {
   if (!valid) {

+ 17 - 14
panda/src/glstuff/glShaderContext_src.h

@@ -31,6 +31,7 @@ class CLP(GraphicsStateGuardian);
 class EXPCL_GL CLP(ShaderContext) final : public ShaderContext {
 class EXPCL_GL CLP(ShaderContext) final : public ShaderContext {
 private:
 private:
   struct UniformBlock;
   struct UniformBlock;
+  typedef pmap<const InternalName *, GLint> LocationMap;
 
 
 public:
 public:
   friend class CLP(GraphicsStateGuardian);
   friend class CLP(GraphicsStateGuardian);
@@ -39,13 +40,18 @@ public:
   ~CLP(ShaderContext)();
   ~CLP(ShaderContext)();
   ALLOC_DELETED_CHAIN(CLP(ShaderContext));
   ALLOC_DELETED_CHAIN(CLP(ShaderContext));
 
 
+  static void r_count_locations_bindings(const ShaderType *type,
+                                         GLint &num_locations,
+                                         GLint &num_ssbo_bindings);
+
   void r_collect_uniforms(const Shader::Parameter &param, UniformBlock &block,
   void r_collect_uniforms(const Shader::Parameter &param, UniformBlock &block,
                           const ShaderType *type, const char *name,
                           const ShaderType *type, const char *name,
-                          const char *sym, int location,
+                          const char *sym, int &location,
                           const SparseArray &active_locations,
                           const SparseArray &active_locations,
-                          int &resource_index, size_t offset = 0);
+                          int &resource_index, int &ssbo_binding,
+                          size_t offset = 0);
 
 
-  void reflect_program();
+  void reflect_program(SparseArray &active_locations, LocationMap &locations, LocationMap &ssbo_bindings);
   void reflect_attribute(int i, char *name_buf, GLsizei name_buflen);
   void reflect_attribute(int i, char *name_buf, GLsizei name_buflen);
   void reflect_uniform_block(int i, const char *block_name,
   void reflect_uniform_block(int i, const char *block_name,
                              char *name_buffer, GLsizei name_buflen);
                              char *name_buffer, GLsizei name_buflen);
@@ -135,7 +141,7 @@ private:
   TextureUnits _texture_units;
   TextureUnits _texture_units;
 
 
   struct ImageUnit {
   struct ImageUnit {
-    ShaderInputBinding *_binding;
+    PT(ShaderInputBinding) _binding;
     ShaderInputBinding::ResourceId _resource_id;
     ShaderInputBinding::ResourceId _resource_id;
     CLP(TextureContext) *_gtc = nullptr;
     CLP(TextureContext) *_gtc = nullptr;
     ShaderType::Access _access;
     ShaderType::Access _access;
@@ -147,28 +153,25 @@ private:
   BitMask32 _enabled_attribs;
   BitMask32 _enabled_attribs;
   GLint _color_attrib_index;
   GLint _color_attrib_index;
 
 
-#ifndef OPENGLES
   struct StorageBlock {
   struct StorageBlock {
-    CPT(InternalName) _name;
-    GLuint _binding_index;
-    GLuint _min_size;
+    PT(ShaderInputBinding) _binding;
+    ShaderInputBinding::ResourceId _resource_id;
+    GLint _binding_index;
   };
   };
   typedef pvector<StorageBlock> StorageBlocks;
   typedef pvector<StorageBlock> StorageBlocks;
   StorageBlocks _storage_blocks;
   StorageBlocks _storage_blocks;
-  BitArray _used_storage_bindings;
-#endif
 
 
   CLP(GraphicsStateGuardian) *_glgsg;
   CLP(GraphicsStateGuardian) *_glgsg;
 
 
   bool _uses_standard_vertex_arrays;
   bool _uses_standard_vertex_arrays;
 
 
-  typedef pmap<const InternalName *, GLint> LocationMap;
-
   void report_shader_errors(const Module &module, bool fatal);
   void report_shader_errors(const Module &module, bool fatal);
   void report_program_errors(GLuint program, bool fatal);
   void report_program_errors(GLuint program, bool fatal);
   bool attach_shader(const ShaderModule *module, Shader::ModuleSpecConstants &spec_consts,
   bool attach_shader(const ShaderModule *module, Shader::ModuleSpecConstants &spec_consts,
-                     const LocationMap &locations, bool &needs_query_locations);
-  bool compile_and_link(const LocationMap &locations, bool &needs_query_locations);
+                     const LocationMap &locations, bool &remap_locations,
+                     const LocationMap &ssbo_bindings);
+  bool compile_and_link(const LocationMap &locations, bool &remap_locations,
+                        const LocationMap &ssbo_bindings);
   void release_resources();
   void release_resources();
 
 
 public:
 public:

+ 1 - 1
panda/src/gobj/shader.I

@@ -730,7 +730,7 @@ write_datagram(Datagram &dg) const {
  */
  */
 INLINE void Shader::ShaderPtrData::
 INLINE void Shader::ShaderPtrData::
 read_datagram(DatagramIterator &scan) {
 read_datagram(DatagramIterator &scan) {
-  _type = (::ShaderType::ScalarType) scan.get_uint8();
+  _type = (ShaderType::ScalarType) scan.get_uint8();
   _size = scan.get_uint32();
   _size = scan.get_uint32();
 
 
   if (_type == ScalarType::ST_double) {
   if (_type == ScalarType::ST_double) {

+ 35 - 31
panda/src/gobj/shader.cxx

@@ -78,7 +78,7 @@ Shader::
  * parameter.  Always returns false.
  * parameter.  Always returns false.
  */
  */
 bool Shader::
 bool Shader::
-report_parameter_error(const InternalName *name, const ::ShaderType *type, const char *msg) {
+report_parameter_error(const InternalName *name, const ShaderType *type, const char *msg) {
   Filename fn = get_filename();
   Filename fn = get_filename();
   shader_cat.error()
   shader_cat.error()
     << fn << ": " << *type << ' ' << *name << ": " << msg << "\n";
     << fn << ": " << *type << ' ' << *name << ": " << msg << "\n";
@@ -178,27 +178,27 @@ read(const ShaderFile &sfile, BamCacheRecord *record) {
 
 
     // Read the various stages in order.
     // Read the various stages in order.
     if (!sfile._vertex.empty() &&
     if (!sfile._vertex.empty() &&
-        !do_read_source(Stage::vertex, sfile._vertex, record)) {
+        !do_read_source(Stage::VERTEX, sfile._vertex, record)) {
       return false;
       return false;
     }
     }
     if (!sfile._tess_control.empty() &&
     if (!sfile._tess_control.empty() &&
-        !do_read_source(Stage::tess_control, sfile._tess_control, record)) {
+        !do_read_source(Stage::TESS_CONTROL, sfile._tess_control, record)) {
       return false;
       return false;
     }
     }
     if (!sfile._tess_evaluation.empty() &&
     if (!sfile._tess_evaluation.empty() &&
-        !do_read_source(Stage::tess_evaluation, sfile._tess_evaluation, record)) {
+        !do_read_source(Stage::TESS_EVALUATION, sfile._tess_evaluation, record)) {
       return false;
       return false;
     }
     }
     if (!sfile._geometry.empty() &&
     if (!sfile._geometry.empty() &&
-        !do_read_source(Stage::geometry, sfile._geometry, record)) {
+        !do_read_source(Stage::GEOMETRY, sfile._geometry, record)) {
       return false;
       return false;
     }
     }
     if (!sfile._fragment.empty() &&
     if (!sfile._fragment.empty() &&
-        !do_read_source(Stage::fragment, sfile._fragment, record)) {
+        !do_read_source(Stage::FRAGMENT, sfile._fragment, record)) {
       return false;
       return false;
     }
     }
     if (!sfile._compute.empty() &&
     if (!sfile._compute.empty() &&
-        !do_read_source(Stage::compute, sfile._compute, record)) {
+        !do_read_source(Stage::COMPUTE, sfile._compute, record)) {
       return false;
       return false;
     }
     }
     _filename = sfile;
     _filename = sfile;
@@ -258,21 +258,21 @@ read(const ShaderFile &sfile, BamCacheRecord *record) {
 
 
     std::istringstream in(source);
     std::istringstream in(source);
 
 
-    PT(ShaderModule) vertex = compiler->compile_now(Stage::vertex, in, fullpath, record);
+    PT(ShaderModule) vertex = compiler->compile_now(Stage::VERTEX, in, fullpath, record);
     if (vertex == nullptr || !add_module(std::move(vertex))) {
     if (vertex == nullptr || !add_module(std::move(vertex))) {
       return false;
       return false;
     }
     }
     if (source.find("gshader") != string::npos) {
     if (source.find("gshader") != string::npos) {
       in.clear();
       in.clear();
       in.seekg(0);
       in.seekg(0);
-      PT(ShaderModule) geometry = compiler->compile_now(Stage::geometry, in, fullpath, record);
+      PT(ShaderModule) geometry = compiler->compile_now(Stage::GEOMETRY, in, fullpath, record);
       if (geometry == nullptr || !add_module(std::move(geometry))) {
       if (geometry == nullptr || !add_module(std::move(geometry))) {
         return false;
         return false;
       }
       }
     }
     }
     in.clear();
     in.clear();
     in.seekg(0);
     in.seekg(0);
-    PT(ShaderModule) fragment = compiler->compile_now(Stage::fragment, in, fullpath, record);
+    PT(ShaderModule) fragment = compiler->compile_now(Stage::FRAGMENT, in, fullpath, record);
     if (fragment == nullptr || !add_module(std::move(fragment))) {
     if (fragment == nullptr || !add_module(std::move(fragment))) {
       return false;
       return false;
     }
     }
@@ -320,27 +320,27 @@ load(const ShaderFile &sbody, BamCacheRecord *record) {
     }
     }
 
 
     if (!sbody._vertex.empty() &&
     if (!sbody._vertex.empty() &&
-        !do_load_source(Stage::vertex, sbody._vertex, record)) {
+        !do_load_source(Stage::VERTEX, sbody._vertex, record)) {
       return false;
       return false;
     }
     }
     if (!sbody._tess_control.empty() &&
     if (!sbody._tess_control.empty() &&
-        !do_load_source(Stage::tess_control, sbody._tess_control, record)) {
+        !do_load_source(Stage::TESS_CONTROL, sbody._tess_control, record)) {
       return false;
       return false;
     }
     }
     if (!sbody._tess_evaluation.empty() &&
     if (!sbody._tess_evaluation.empty() &&
-        !do_load_source(Stage::tess_evaluation, sbody._tess_evaluation, record)) {
+        !do_load_source(Stage::TESS_EVALUATION, sbody._tess_evaluation, record)) {
       return false;
       return false;
     }
     }
     if (!sbody._geometry.empty() &&
     if (!sbody._geometry.empty() &&
-        !do_load_source(Stage::geometry, sbody._geometry, record)) {
+        !do_load_source(Stage::GEOMETRY, sbody._geometry, record)) {
       return false;
       return false;
     }
     }
     if (!sbody._fragment.empty() &&
     if (!sbody._fragment.empty() &&
-        !do_load_source(Stage::fragment, sbody._fragment, record)) {
+        !do_load_source(Stage::FRAGMENT, sbody._fragment, record)) {
       return false;
       return false;
     }
     }
     if (!sbody._compute.empty() &&
     if (!sbody._compute.empty() &&
-        !do_load_source(Stage::compute, sbody._compute, record)) {
+        !do_load_source(Stage::COMPUTE, sbody._compute, record)) {
       return false;
       return false;
     }
     }
 
 
@@ -352,14 +352,14 @@ load(const ShaderFile &sbody, BamCacheRecord *record) {
     }
     }
     _language = SL_Cg;
     _language = SL_Cg;
 
 
-    if (!do_load_source(Stage::vertex, sbody._shared, record)) {
+    if (!do_load_source(Stage::VERTEX, sbody._shared, record)) {
       return false;
       return false;
     }
     }
-    if (!do_load_source(Stage::fragment, sbody._shared, record)) {
+    if (!do_load_source(Stage::FRAGMENT, sbody._shared, record)) {
       return false;
       return false;
     }
     }
     if (sbody._shared.find("gshader") != string::npos &&
     if (sbody._shared.find("gshader") != string::npos &&
-        !do_load_source(Stage::geometry, sbody._shared, record)) {
+        !do_load_source(Stage::GEOMETRY, sbody._shared, record)) {
       return false;
       return false;
     }
     }
 
 
@@ -455,7 +455,7 @@ link() {
   pmap<CPT_InternalName, Parameter> parameters_by_name;
   pmap<CPT_InternalName, Parameter> parameters_by_name;
   pvector<Parameter *> parameters;
   pvector<Parameter *> parameters;
 
 
-  pmap<CPT_InternalName, const ::ShaderType *> spec_const_types;
+  pmap<CPT_InternalName, const ShaderType *> spec_const_types;
 
 
   for (LinkedModule &linked_module : _modules) {
   for (LinkedModule &linked_module : _modules) {
     const ShaderModule *module = linked_module._module.get_read_pointer();
     const ShaderModule *module = linked_module._module.get_read_pointer();
@@ -499,7 +499,7 @@ link() {
       if (!result.second) {
       if (!result.second) {
         // Another module has already defined a spec constant with this name.
         // Another module has already defined a spec constant with this name.
         // Make sure they have the same type.
         // Make sure they have the same type.
-        const ::ShaderType *other_type = it->second;
+        const ShaderType *other_type = it->second;
         if (spec_const.type != other_type) {
         if (spec_const.type != other_type) {
           shader_cat.error()
           shader_cat.error()
             << "Specialization constant " << *spec_const.name << " in module "
             << "Specialization constant " << *spec_const.name << " in module "
@@ -530,13 +530,17 @@ link() {
 }
 }
 
 
 void Shader::
 void Shader::
-add_parameter(const InternalName *name, const ::ShaderType *type, int location) {
-  Shader::Parameter param;
-  param._name = name;
-  param._type = type;
-  param._binding = ShaderInputBinding::make(_language, name, type);
-  param._location = location;
-  _parameters.push_back(std::move(param));
+add_parameter(const InternalName *name, const ShaderType *type, int location) {
+  {
+    Shader::Parameter param;
+    param._name = name;
+    param._type = type;
+    param._binding = ShaderInputBinding::make(_language, name, type);
+    param._location = location;
+    _parameters.push_back(std::move(param));
+  }
+
+  Shader::Parameter &param = _parameters.back();
   if (param._binding != nullptr) {
   if (param._binding != nullptr) {
     param._binding->setup(this);
     param._binding->setup(this);
   } else {
   } else {
@@ -550,7 +554,7 @@ add_parameter(const InternalName *name, const ::ShaderType *type, int location)
  * Binds a vertex input parameter with the given type.
  * Binds a vertex input parameter with the given type.
  */
  */
 bool Shader::
 bool Shader::
-bind_vertex_input(const InternalName *name, const ::ShaderType *type, int location) {
+bind_vertex_input(const InternalName *name, const ShaderType *type, int location) {
   std::string name_str = name->get_name();
   std::string name_str = name->get_name();
 
 
   Shader::ShaderVarSpec bind;
   Shader::ShaderVarSpec bind;
@@ -797,7 +801,7 @@ load_compute(ShaderLanguage lang, const Filename &fn) {
   if (record != nullptr) {
   if (record != nullptr) {
     if (record->has_data()) {
     if (record->has_data()) {
       PT(Shader) shader = DCAST(Shader, record->get_data());
       PT(Shader) shader = DCAST(Shader, record->get_data());
-      if (shader->_module_mask == (1 << (int)Stage::compute)) {
+      if (shader->_module_mask == (1 << (int)Stage::COMPUTE)) {
         shader_cat.info()
         shader_cat.info()
           << "Compute shader " << fn << " was found in disk cache.\n";
           << "Compute shader " << fn << " was found in disk cache.\n";
         return shader;
         return shader;
@@ -1024,7 +1028,7 @@ add_module(PT(ShaderModule) module) {
       module->remap_input_locations(location_remap);
       module->remap_input_locations(location_remap);
     }
     }
   }
   }
-  else if (stage == Stage::vertex) {
+  else if (stage == Stage::VERTEX) {
     // Bind vertex inputs right away.
     // Bind vertex inputs right away.
     CPT(ShaderModule) module = cow_module.get_read_pointer();
     CPT(ShaderModule) module = cow_module.get_read_pointer();
     bool success = true;
     bool success = true;

+ 4 - 4
panda/src/gobj/shader.h

@@ -135,7 +135,7 @@ public:
 
 
   struct Parameter {
   struct Parameter {
     CPT_InternalName _name;
     CPT_InternalName _name;
-    const ::ShaderType *_type = nullptr;
+    const ShaderType *_type = nullptr;
     PT(ShaderInputBinding) _binding = nullptr;
     PT(ShaderInputBinding) _binding = nullptr;
     int _location = -1;
     int _location = -1;
     int _stage_mask = 0;
     int _stage_mask = 0;
@@ -259,7 +259,7 @@ public:
   };
   };
 
 
 protected:
 protected:
-  bool report_parameter_error(const InternalName *name, const ::ShaderType *type, const char *msg);
+  bool report_parameter_error(const InternalName *name, const ShaderType *type, const char *msg);
 
 
 public:
 public:
   size_t add_matrix_cache_item(StateMatrix input, const InternalName *arg, int dep);
   size_t add_matrix_cache_item(StateMatrix input, const InternalName *arg, int dep);
@@ -334,8 +334,8 @@ private:
 
 
 public:
 public:
   bool link();
   bool link();
-  void add_parameter(const InternalName *name, const ::ShaderType *type, int location = -1);
-  bool bind_vertex_input(const InternalName *name, const ::ShaderType *type, int location);
+  void add_parameter(const InternalName *name, const ShaderType *type, int location = -1);
+  bool bind_vertex_input(const InternalName *name, const ShaderType *type, int location);
 
 
   bool check_modified() const;
   bool check_modified() const;
   ShaderCompiler *get_compiler(ShaderLanguage lang) const;
   ShaderCompiler *get_compiler(ShaderLanguage lang) const;

+ 0 - 6
panda/src/gobj/shaderBuffer.I

@@ -32,12 +32,6 @@ ShaderBuffer(const std::string &name, vector_uchar initial_data, UsageHint usage
   _data_size_bytes(initial_data.size()),
   _data_size_bytes(initial_data.size()),
   _usage_hint(usage_hint),
   _usage_hint(usage_hint),
   _initial_data(std::move(initial_data)) {
   _initial_data(std::move(initial_data)) {
-
-  // Make sure it is padded to 16 bytes.  Some drivers like that.
-  if ((_initial_data.size() & 15u) != 0) {
-    _initial_data.resize((_initial_data.size() + 15u) & ~15u, 0);
-    _data_size_bytes = _initial_data.size();
-  }
 }
 }
 
 
 /**
 /**

+ 12 - 12
panda/src/gobj/shaderEnums.cxx

@@ -19,18 +19,18 @@
 std::string ShaderEnums::
 std::string ShaderEnums::
 format_stage(Stage stage) {
 format_stage(Stage stage) {
   switch (stage) {
   switch (stage) {
-  case Stage::vertex:
-    return "vertex";
-  case Stage::tess_control:
-    return "tess_control";
-  case Stage::tess_evaluation:
-    return "tess_evaluation";
-  case Stage::geometry:
-    return "geometry";
-  case Stage::fragment:
-    return "fragment";
-  case Stage::compute:
-    return "compute";
+  case Stage::VERTEX:
+    return "VERTEX";
+  case Stage::TESS_CONTROL:
+    return "TESS_CONTROL";
+  case Stage::TESS_EVALUATION:
+    return "TESS_EVALUATION";
+  case Stage::GEOMETRY:
+    return "GEOMETRY";
+  case Stage::FRAGMENT:
+    return "FRAGMENT";
+  case Stage::COMPUTE:
+    return "COMPUTE";
   }
   }
 
 
   return "**invalid**";
   return "**invalid**";

+ 6 - 6
panda/src/gobj/shaderEnums.h

@@ -26,12 +26,12 @@
 class EXPCL_PANDA_GOBJ ShaderEnums {
 class EXPCL_PANDA_GOBJ ShaderEnums {
 PUBLISHED:
 PUBLISHED:
   enum class Stage {
   enum class Stage {
-    vertex,
-    tess_control,
-    tess_evaluation,
-    geometry,
-    fragment,
-    compute,
+    VERTEX,
+    TESS_CONTROL,
+    TESS_EVALUATION,
+    GEOMETRY,
+    FRAGMENT,
+    COMPUTE,
   };
   };
 
 
   enum ShaderLanguage {
   enum ShaderLanguage {

+ 2 - 2
panda/src/gobj/shaderInputBinding.I

@@ -29,8 +29,8 @@ make_data(int dep, Callable callable) {
       return _dep;
       return _dep;
     }
     }
 
 
-    virtual void fetch_data(const State &state, void *scratch, bool pad_rows) const override final {
-      _callable(state, scratch, pad_rows);
+    virtual void fetch_data(const State &state, void *into, bool packed) const override final {
+      _callable(state, into, packed);
     }
     }
 
 
   private:
   private:

+ 12 - 1
panda/src/gobj/shaderInputBinding.cxx

@@ -54,9 +54,11 @@ setup(Shader *shader) {
 
 
 /**
 /**
  * Fetches the part of the shader input that is plain numeric data.
  * Fetches the part of the shader input that is plain numeric data.
+ * If packed is true, the data is tightly packed, even if the type originally
+ * contained padding.
  */
  */
 void ShaderInputBinding::
 void ShaderInputBinding::
-fetch_data(const State &state, void *into, bool pad_rows) const {
+fetch_data(const State &state, void *into, bool packed) const {
 }
 }
 
 
 /**
 /**
@@ -84,6 +86,15 @@ fetch_texture_image(const State &state, ResourceId resource_id, ShaderType::Acce
   return nullptr;
   return nullptr;
 }
 }
 
 
+/**
+ * Fetches the shader buffer associated with the given resource identifier,
+ * which was previously returned by get_resource_id.
+ */
+PT(ShaderBuffer) ShaderInputBinding::
+fetch_shader_buffer(const State &state, ResourceId resource_id) const {
+  return nullptr;
+}
+
 /**
 /**
  * Registers a factory function to create a binding.
  * Registers a factory function to create a binding.
  */
  */

+ 5 - 1
panda/src/gobj/shaderInputBinding.h

@@ -19,6 +19,8 @@
 #include "internalName.h"
 #include "internalName.h"
 #include "shaderEnums.h"
 #include "shaderEnums.h"
 #include "small_vector.h"
 #include "small_vector.h"
+#include "texture.h"
+#include "shaderBuffer.h"
 
 
 class GraphicsStateGuardian;
 class GraphicsStateGuardian;
 class LMatrix4f;
 class LMatrix4f;
@@ -52,7 +54,7 @@ public:
   };
   };
 
 
   virtual void fetch_data(const State &state, void *into,
   virtual void fetch_data(const State &state, void *into,
-                          bool pad_rows = false) const;
+                          bool packed = false) const;
 
 
   typedef uintptr_t ResourceId;
   typedef uintptr_t ResourceId;
   virtual ResourceId get_resource_id(int index, const ShaderType *type) const;
   virtual ResourceId get_resource_id(int index, const ShaderType *type) const;
@@ -63,6 +65,8 @@ public:
                                           ResourceId resource_id,
                                           ResourceId resource_id,
                                           ShaderType::Access &access,
                                           ShaderType::Access &access,
                                           int &z, int &n) const;
                                           int &z, int &n) const;
+  virtual PT(ShaderBuffer) fetch_shader_buffer(const State &state,
+                                               ResourceId resource_id) const;
 
 
   // All the binders are defined in display, we provide this mechanism so that
   // All the binders are defined in display, we provide this mechanism so that
   // we don't get a dependency on display here.
   // we don't get a dependency on display here.

+ 4 - 4
panda/src/gobj/shaderModule.cxx

@@ -21,16 +21,16 @@ TypeHandle ShaderModule::_type_handle;
 ShaderModule::
 ShaderModule::
 ShaderModule(Stage stage) : _stage(stage), _used_caps(C_basic_shader) {
 ShaderModule(Stage stage) : _stage(stage), _used_caps(C_basic_shader) {
   switch (stage) {
   switch (stage) {
-  case Stage::tess_control:
-  case Stage::tess_evaluation:
+  case Stage::TESS_CONTROL:
+  case Stage::TESS_EVALUATION:
     _used_caps |= C_tessellation_shader;
     _used_caps |= C_tessellation_shader;
     break;
     break;
 
 
-  case Stage::geometry:
+  case Stage::GEOMETRY:
     _used_caps |= C_geometry_shader;
     _used_caps |= C_geometry_shader;
     break;
     break;
 
 
-  case Stage::compute:
+  case Stage::COMPUTE:
     _used_caps |= C_compute_shader;
     _used_caps |= C_compute_shader;
     break;
     break;
 
 

+ 9 - 1
panda/src/gobj/shaderType.I

@@ -52,6 +52,14 @@ compare_to(const ShaderType &other) const {
   return (this_type > other_type) - (this_type < other_type);
   return (this_type > other_type) - (this_type < other_type);
 }
 }
 
 
+/**
+ * Returns the size of the given scalar type, in bytes.
+ */
+INLINE constexpr uint32_t ShaderType::
+get_scalar_size_bytes(ScalarType scalar_type) {
+  return (scalar_type == ST_double) ? 8 : 4;
+}
+
 /**
 /**
  * Constructs a scalar type.
  * Constructs a scalar type.
  */
  */
@@ -210,7 +218,7 @@ get_access() const {
  */
  */
 INLINE bool ShaderType::Image::
 INLINE bool ShaderType::Image::
 is_writable() const {
 is_writable() const {
-  return ((int)_access & (int)Access::write_only) != 0;
+  return ((int)_access & (int)Access::WRITE_ONLY) != 0;
 }
 }
 
 
 /**
 /**

+ 124 - 57
panda/src/gobj/shaderType.cxx

@@ -51,6 +51,15 @@ unwrap_array(const ShaderType *&element_type, uint32_t &num_elements) const {
   return false;
   return false;
 }
 }
 
 
+/**
+ * Returns a new type with all occurrences of the given type recursively
+ * replaced with the second type.
+ */
+const ShaderType *ShaderType::
+replace_type(const ShaderType *a, const ShaderType *b) const {
+  return (this == a) ? b : this;
+}
+
 /**
 /**
  *
  *
  */
  */
@@ -151,29 +160,6 @@ std::ostream &operator << (std::ostream &out, ShaderType::ScalarType scalar_type
 }
 }
 
 
 #ifndef CPPPARSER
 #ifndef CPPPARSER
-/**
- * Returns the size in bytes of this type in memory, if applicable.  Opaque
- * types will return 0.
- */
-int ShaderType::
-get_size_bytes(bool pad_rows) const {
-  ScalarType type;
-  uint32_t dim[3];
-  if (as_scalar_type(type, dim[0], dim[1], dim[2])) {
-    if (pad_rows) {
-      // std140 array element padding rules, also used in DX9.
-      dim[2] = (dim[2] + 3) & ~3;
-    }
-    if (type == ST_double) {
-      return 8 * dim[0] * dim[1] * dim[2];
-    } else {
-      return 4 * dim[0] * dim[1] * dim[2];
-    }
-  } else {
-    return 0;
-  }
-}
-
 /**
 /**
  *
  *
  */
  */
@@ -257,9 +243,18 @@ compare_to_impl(const ShaderType &other) const {
 /**
 /**
  * Returns the alignment in bytes of this type in memory, if applicable.
  * Returns the alignment in bytes of this type in memory, if applicable.
  */
  */
-int ShaderType::Scalar::
+uint32_t ShaderType::Scalar::
 get_align_bytes() const {
 get_align_bytes() const {
-  return (_scalar_type == ST_double) ? 8 : 4;
+  return get_scalar_size_bytes(_scalar_type);
+}
+
+/**
+ * Returns the size in bytes of this type in memory, if applicable.  Opaque
+ * types will return 0.
+ */
+uint32_t ShaderType::Scalar::
+get_size_bytes() const {
+  return get_scalar_size_bytes(_scalar_type);
 }
 }
 
 
 /**
 /**
@@ -327,11 +322,7 @@ replace_scalar_type(ScalarType a, ScalarType b) const {
  */
  */
 int ShaderType::Vector::
 int ShaderType::Vector::
 get_num_interface_locations() const {
 get_num_interface_locations() const {
-  if (_scalar_type == ST_double && _num_components > 2) {
-    return 2;
-  } else {
-    return 1;
-  }
+  return (get_scalar_size_bytes(_scalar_type) * _num_components + 15) / 16;
 }
 }
 
 
 /**
 /**
@@ -359,10 +350,20 @@ compare_to_impl(const ShaderType &other) const {
 /**
 /**
  * Returns the alignment in bytes of this type in memory, if applicable.
  * Returns the alignment in bytes of this type in memory, if applicable.
  */
  */
-int ShaderType::Vector::
+uint32_t ShaderType::Vector::
 get_align_bytes() const {
 get_align_bytes() const {
-  int component_align = (_scalar_type == ST_double) ? 8 : 4;
-  return component_align * ((_num_components == 3) ? 4 : _num_components);
+  return get_scalar_size_bytes(_scalar_type) * ((_num_components == 3) ? 4 : _num_components);
+}
+
+/**
+ * Returns the size in bytes of this type in memory, if applicable.  Opaque
+ * types will return 0.
+ */
+uint32_t ShaderType::Vector::
+get_size_bytes() const {
+  // Notably, a vec3 is vec4-aligned but not padded!  It is permissible for a
+  // scalar to directly follow a vec3 in a struct.
+  return get_scalar_size_bytes(_scalar_type) * _num_components;
 }
 }
 
 
 /**
 /**
@@ -454,11 +455,21 @@ compare_to_impl(const ShaderType &other) const {
 /**
 /**
  * Returns the alignment in bytes of this type in memory, if applicable.
  * Returns the alignment in bytes of this type in memory, if applicable.
  */
  */
-int ShaderType::Matrix::
+uint32_t ShaderType::Matrix::
 get_align_bytes() const {
 get_align_bytes() const {
-  //TODO: needs to be checked
-  int row_align = (_scalar_type == ST_double) ? 32 : 16;
-  return row_align * _num_rows;
+  return get_scalar_size_bytes(_scalar_type) * 4;
+}
+
+/**
+ * Returns the size in bytes of this type in memory, if applicable.  Opaque
+ * types will return 0.
+ */
+uint32_t ShaderType::Matrix::
+get_size_bytes() const {
+  // Pad rows to 16 bytes (std140 rules, but DX9 also expects that)
+  uint32_t row_size = _num_columns * get_scalar_size_bytes(_scalar_type);
+  row_size = (row_size + 15) & ~15;
+  return _num_rows * row_size;
 }
 }
 
 
 /**
 /**
@@ -508,7 +519,7 @@ add_member(const ShaderType *type, std::string name) {
   member.type = type;
   member.type = type;
   member.name = std::move(name);
   member.name = std::move(name);
   member.offset = _members.empty() ? 0 : _members.back().offset + _members.back().type->get_size_bytes();
   member.offset = _members.empty() ? 0 : _members.back().offset + _members.back().type->get_size_bytes();
-  int alignment = type->get_align_bytes();
+  uint32_t alignment = type->get_align_bytes();
   if (alignment > 0) {
   if (alignment > 0) {
     member.offset += alignment - ((member.offset + (alignment - 1)) % alignment) - 1;
     member.offset += alignment - ((member.offset + (alignment - 1)) % alignment) - 1;
   }
   }
@@ -563,11 +574,12 @@ contains_scalar_type(ScalarType type) const {
 const ShaderType *ShaderType::Struct::
 const ShaderType *ShaderType::Struct::
 replace_scalar_type(ScalarType a, ScalarType b) const {
 replace_scalar_type(ScalarType a, ScalarType b) const {
   if (contains_scalar_type(a)) {
   if (contains_scalar_type(a)) {
+    bool recompute_offsets = get_scalar_size_bytes(a) != get_scalar_size_bytes(b);
+
     ShaderType::Struct copy;
     ShaderType::Struct copy;
     for (const Member &member : _members) {
     for (const Member &member : _members) {
       const ShaderType *type = member.type->replace_scalar_type(a, b);
       const ShaderType *type = member.type->replace_scalar_type(a, b);
-      if ((a == ST_double) != (b == ST_double)) {
-        // Recompute offsets.
+      if (recompute_offsets) {
         copy.add_member(type, member.name);
         copy.add_member(type, member.name);
       } else {
       } else {
         copy.add_member(type, member.name, member.offset);
         copy.add_member(type, member.name, member.offset);
@@ -579,6 +591,36 @@ replace_scalar_type(ScalarType a, ScalarType b) const {
   }
   }
 }
 }
 
 
+/**
+ * Returns a new type with all occurrences of the given type recursively
+ * replaced with the second type.
+ */
+const ShaderType *ShaderType::Struct::
+replace_type(const ShaderType *a, const ShaderType *b) const {
+  if (this == a) {
+    return b;
+  }
+  bool any_changed = false;
+  bool recompute_offsets = a->get_size_bytes() != b->get_size_bytes();
+  ShaderType::Struct copy;
+  for (const Member &member : _members) {
+    const ShaderType *type = member.type->replace_type(a, b);
+    if (type != member.type) {
+      any_changed = true;
+    }
+    if (recompute_offsets) {
+      copy.add_member(type, member.name);
+    } else {
+      copy.add_member(type, member.name, member.offset);
+    }
+  }
+  if (any_changed) {
+    return ShaderType::register_type(std::move(copy));
+  } else {
+    return this;
+  }
+}
+
 /**
 /**
  *
  *
  */
  */
@@ -627,22 +669,24 @@ compare_to_impl(const ShaderType &other) const {
 /**
 /**
  * Returns the alignment in bytes of this type in memory, if applicable.
  * Returns the alignment in bytes of this type in memory, if applicable.
  */
  */
-int ShaderType::Struct::
+uint32_t ShaderType::Struct::
 get_align_bytes() const {
 get_align_bytes() const {
-  int align = 16;
+  uint32_t align = 16;
   for (const Member &member : _members) {
   for (const Member &member : _members) {
     align = std::max(align, member.type->get_align_bytes());
     align = std::max(align, member.type->get_align_bytes());
   }
   }
-  return align;
+  return (align + 15) & ~15;
 }
 }
 
 
 /**
 /**
  * Returns the size in bytes of this type in memory, if applicable.  Opaque
  * Returns the size in bytes of this type in memory, if applicable.  Opaque
  * types will return 0.
  * types will return 0.
  */
  */
-int ShaderType::Struct::
-get_size_bytes(bool pad_rows) const {
-  return _members.empty() ? 0 : _members.back().offset + _members.back().type->get_size_bytes();
+uint32_t ShaderType::Struct::
+get_size_bytes() const {
+  // Structs are padded to the base alignment of a vec4.
+  uint32_t size = _members.empty() ? 0 : _members.back().offset + _members.back().type->get_size_bytes();
+  return (size + 15) & ~15;
 }
 }
 
 
 /**
 /**
@@ -801,6 +845,23 @@ replace_scalar_type(ScalarType a, ScalarType b) const {
   }
   }
 }
 }
 
 
+/**
+ * Returns a new type with all occurrences of the given type recursively
+ * replaced with the second type.
+ */
+const ShaderType *ShaderType::Array::
+replace_type(const ShaderType *a, const ShaderType *b) const {
+  if (this == a) {
+    return b;
+  }
+  const ShaderType *element_type = _element_type->replace_type(a, b);
+  if (_element_type != element_type) {
+    return ShaderType::register_type(ShaderType::Array(element_type, _num_elements));
+  } else {
+    return this;
+  }
+}
+
 /**
 /**
  *
  *
  */
  */
@@ -830,26 +891,32 @@ compare_to_impl(const ShaderType &other) const {
 /**
 /**
  * Returns the array stride in bytes.
  * Returns the array stride in bytes.
  */
  */
-int ShaderType::Array::
+uint32_t ShaderType::Array::
 get_stride_bytes() const {
 get_stride_bytes() const {
-  int element_size = _element_type->get_size_bytes(true);
-  return (element_size + 15) & ~15;
+  // Array stride is always (at least) 16 bytes in std140 / DX9, even though
+  // this is (indeed) incredibly wasteful for arrays of scalars.
+  uint32_t size = _element_type->get_size_bytes();
+  return (size + 15) & ~15;
 }
 }
 
 
 /**
 /**
  * Returns the alignment in bytes of this type in memory, if applicable.
  * Returns the alignment in bytes of this type in memory, if applicable.
  */
  */
-int ShaderType::Array::
+uint32_t ShaderType::Array::
 get_align_bytes() const {
 get_align_bytes() const {
-  return get_stride_bytes();
+  uint32_t align = _element_type->get_align_bytes();
+  return (align + 15) & ~15;
 }
 }
 
 
 /**
 /**
  * Returns the size in bytes of this type in memory, if applicable.  Opaque
  * Returns the size in bytes of this type in memory, if applicable.  Opaque
  * types will return 0.
  * types will return 0.
  */
  */
-int ShaderType::Array::
-get_size_bytes(bool pad_rows) const {
+uint32_t ShaderType::Array::
+get_size_bytes() const {
+  // Arrays have padding at the end so that the next member is aligned to a
+  // 16-byte boundary.  This implies that a float may directly follow a vec3,
+  // but not a vec3[1]!  I didn't make up these rules.
   return get_stride_bytes() * _num_elements;
   return get_stride_bytes() * _num_elements;
 }
 }
 
 
@@ -926,10 +993,10 @@ make_from_bam(const FactoryParams &params) {
  */
  */
 void ShaderType::Image::
 void ShaderType::Image::
 output(std::ostream &out) const {
 output(std::ostream &out) const {
-  if ((_access & Access::write_only) == Access::none) {
+  if ((_access & Access::WRITE_ONLY) == Access::NONE) {
     out << "readonly ";
     out << "readonly ";
   }
   }
-  if ((_access & Access::read_only) == Access::none) {
+  if ((_access & Access::READ_ONLY) == Access::NONE) {
     out << "writeonly ";
     out << "writeonly ";
   }
   }
   if (_sampled_type == ST_int) {
   if (_sampled_type == ST_int) {
@@ -1102,10 +1169,10 @@ make_from_bam(const FactoryParams &params) {
  */
  */
 void ShaderType::StorageBuffer::
 void ShaderType::StorageBuffer::
 output(std::ostream &out) const {
 output(std::ostream &out) const {
-  if ((_access & Access::write_only) == Access::none) {
+  if ((_access & Access::WRITE_ONLY) == Access::NONE) {
     out << "readonly ";
     out << "readonly ";
   }
   }
-  if ((_access & Access::read_only) == Access::none) {
+  if ((_access & Access::READ_ONLY) == Access::NONE) {
     out << "writeonly ";
     out << "writeonly ";
   }
   }
   out << "buffer";
   out << "buffer";

+ 22 - 14
panda/src/gobj/shaderType.h

@@ -35,8 +35,8 @@ public:
 
 
   virtual void output(std::ostream &out) const=0;
   virtual void output(std::ostream &out) const=0;
 
 
-  virtual int get_align_bytes() const { return 1; }
-  virtual int get_size_bytes(bool pad_rows = false) const;
+  virtual uint32_t get_align_bytes() const { return 1; }
+  virtual uint32_t get_size_bytes() const { return 0; }
   virtual int get_num_interface_locations() const { return 1; }
   virtual int get_num_interface_locations() const { return 1; }
   virtual int get_num_parameter_locations() const { return 1; }
   virtual int get_num_parameter_locations() const { return 1; }
   virtual int get_num_resources() const { return 0; }
   virtual int get_num_resources() const { return 0; }
@@ -57,10 +57,10 @@ public:
   };
   };
 
 
   enum class Access {
   enum class Access {
-    none = 0,
-    read_only = 1,
-    write_only = 2,
-    read_write = 3,
+    NONE = 0,
+    READ_ONLY = 1,
+    WRITE_ONLY = 2,
+    READ_WRITE = 3,
   };
   };
 
 
 private:
 private:
@@ -99,6 +99,9 @@ public:
                               uint32_t &num_rows,
                               uint32_t &num_rows,
                               uint32_t &num_columns) const { return false; }
                               uint32_t &num_columns) const { return false; }
   virtual const ShaderType *replace_scalar_type(ScalarType a, ScalarType b) const { return this; }
   virtual const ShaderType *replace_scalar_type(ScalarType a, ScalarType b) const { return this; }
+  virtual const ShaderType *replace_type(const ShaderType *a, const ShaderType *b) const;
+
+  INLINE static constexpr uint32_t get_scalar_size_bytes(ScalarType scalar_type);
 
 
   virtual const Scalar *as_scalar() const { return nullptr; }
   virtual const Scalar *as_scalar() const { return nullptr; }
   virtual const Vector *as_vector() const { return nullptr; }
   virtual const Vector *as_vector() const { return nullptr; }
@@ -194,7 +197,8 @@ public:
 private:
 private:
   virtual int compare_to_impl(const ShaderType &other) const override;
   virtual int compare_to_impl(const ShaderType &other) const override;
 
 
-  virtual int get_align_bytes() const override;
+  virtual uint32_t get_align_bytes() const override;
+  virtual uint32_t get_size_bytes() const override;
 
 
   const ScalarType _scalar_type;
   const ScalarType _scalar_type;
 
 
@@ -241,7 +245,8 @@ public:
 private:
 private:
   virtual int compare_to_impl(const ShaderType &other) const override;
   virtual int compare_to_impl(const ShaderType &other) const override;
 
 
-  virtual int get_align_bytes() const override;
+  virtual uint32_t get_align_bytes() const override;
+  virtual uint32_t get_size_bytes() const override;
 
 
   const ScalarType _scalar_type;
   const ScalarType _scalar_type;
   const uint32_t _num_components;
   const uint32_t _num_components;
@@ -289,7 +294,8 @@ public:
 private:
 private:
   virtual int compare_to_impl(const ShaderType &other) const override;
   virtual int compare_to_impl(const ShaderType &other) const override;
 
 
-  virtual int get_align_bytes() const override;
+  virtual uint32_t get_align_bytes() const override;
+  virtual uint32_t get_size_bytes() const override;
 
 
   const ScalarType _scalar_type;
   const ScalarType _scalar_type;
   const uint32_t _num_rows;
   const uint32_t _num_rows;
@@ -328,8 +334,8 @@ public:
   virtual void output(std::ostream &out) const override;
   virtual void output(std::ostream &out) const override;
   virtual int compare_to_impl(const ShaderType &other) const override;
   virtual int compare_to_impl(const ShaderType &other) const override;
 
 
-  virtual int get_align_bytes() const override;
-  virtual int get_size_bytes(bool pad_rows = false) const override;
+  virtual uint32_t get_align_bytes() const override;
+  virtual uint32_t get_size_bytes() const override;
   virtual int get_num_interface_locations() const override;
   virtual int get_num_interface_locations() const override;
   virtual int get_num_parameter_locations() const override;
   virtual int get_num_parameter_locations() const override;
   virtual int get_num_resources() const override;
   virtual int get_num_resources() const override;
@@ -338,6 +344,7 @@ public:
   virtual bool contains_opaque_type() const override;
   virtual bool contains_opaque_type() const override;
   virtual bool contains_scalar_type(ScalarType type) const override;
   virtual bool contains_scalar_type(ScalarType type) const override;
   virtual const ShaderType *replace_scalar_type(ScalarType a, ScalarType b) const override;
   virtual const ShaderType *replace_scalar_type(ScalarType a, ScalarType b) const override;
+  virtual const ShaderType *replace_type(const ShaderType *a, const ShaderType *b) const override;
   const Struct *as_struct() const override { return this; }
   const Struct *as_struct() const override { return this; }
 
 
 PUBLISHED:
 PUBLISHED:
@@ -388,13 +395,14 @@ public:
   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;
   virtual const ShaderType *replace_scalar_type(ScalarType a, ScalarType b) const override;
   virtual const ShaderType *replace_scalar_type(ScalarType a, ScalarType b) const override;
+  virtual const ShaderType *replace_type(const ShaderType *a, const ShaderType *b) const override;
 
 
   virtual void output(std::ostream &out) const override;
   virtual void output(std::ostream &out) const override;
   virtual int compare_to_impl(const ShaderType &other) const override;
   virtual int compare_to_impl(const ShaderType &other) const override;
 
 
-  int get_stride_bytes() const;
-  virtual int get_align_bytes() const override;
-  virtual int get_size_bytes(bool pad_rows = false) const override;
+  uint32_t get_stride_bytes() const;
+  virtual uint32_t get_align_bytes() const override;
+  virtual uint32_t get_size_bytes() const override;
   virtual int get_num_interface_locations() const override;
   virtual int get_num_interface_locations() const override;
   virtual int get_num_parameter_locations() const override;
   virtual int get_num_parameter_locations() const override;
   virtual int get_num_resources() const override;
   virtual int get_num_resources() const override;

+ 1 - 342
panda/src/pgraph/shaderAttrib.cxx

@@ -480,347 +480,6 @@ get_shader_input_ptr(const InternalName *id, Shader::ShaderPtrData &data) const
   }
   }
 }
 }
 
 
-/**
- * Extracts the shader input data according to the given type expected by the
- * shader.  Returns the number of bytes written to "into".
- */
-size_t ShaderAttrib::
-get_shader_input_data(const InternalName *id, void *into,
-                      const ShaderType *type, bool pad_rows) const {
-  ShaderType::ScalarType scalar_type;
-  uint32_t num_elements;
-  uint32_t num_rows;
-  uint32_t num_columns;
-  if (type->as_scalar_type(scalar_type, num_elements, num_rows, num_columns)) {
-    Shader::ShaderPtrData data;
-    get_shader_input_data(id, into, scalar_type, num_elements, num_rows, num_columns, pad_rows, true);
-    return num_elements * num_rows * (pad_rows ? 16 : num_columns * 4);
-  }
-  else if (const ShaderType::Array *array_type = type->as_array()) {
-    size_t basename_size = id->get_basename().size();
-    char *buffer = (char *)alloca(basename_size + 14);
-    memcpy(buffer, id->get_basename().c_str(), basename_size);
-
-    size_t total_size = 0;
-    for (size_t i = 0; i < array_type->get_num_elements(); ++i) {
-      sprintf(buffer + basename_size, "[%d]", (int)i);
-
-      size_t size = get_shader_input_data(id->get_parent()->append(buffer), into, array_type->get_element_type(), pad_rows);
-      into = (char *)into + size;
-      total_size += size;
-    }
-    return total_size;
-  }
-  else if (const ShaderType::Struct *struct_type = type->as_struct()) {
-    size_t total_size = 0;
-    for (size_t i = 0; i < struct_type->get_num_members(); ++i) {
-      const ShaderType::Struct::Member &member = struct_type->get_member(i);
-
-      size_t size = get_shader_input_data(((InternalName *)id)->append(member.name), (char *)into + member.offset, member.type, pad_rows);
-      total_size += size;
-    }
-    return total_size;
-  }
-  else {
-    return 0;
-  }
-}
-
-/**
- * Extracts the shader input data, converting it as necessary.  The scratch
- * pointer must be large enough to contain the data, but may or may not be
- * filled by this function (depending on whether conversion is needed), unless
- * always_copy is true.
- */
-void *ShaderAttrib::
-get_shader_input_data(const InternalName *id, void *scratch,
-                      ShaderType::ScalarType scalar_type, int num_elements,
-                      int num_rows, int num_columns, bool pad_rows,
-                      bool always_copy) const {
-  Shader::ShaderPtrData ptr_data;
-  if (!get_shader_input_ptr(id, ptr_data)) {
-    return nullptr;
-  }
-
-  int total_rows = std::min(num_elements * num_rows, (int)ptr_data._size / num_columns);
-  if (total_rows == 1) {
-    pad_rows = false;
-  }
-  switch (scalar_type) {
-  case ShaderType::ST_float:
-    {
-      float *data = (float *)scratch;
-
-      switch (ptr_data._type) {
-      case ShaderType::ST_int:
-        // Convert int data to float data.
-        if (!pad_rows || num_columns == 4) {
-          for (int i = 0; i < total_rows * num_columns; ++i) {
-            data[i] = (float)(((int *)ptr_data._ptr)[i]);
-          }
-        } else {
-          const int *from_data = (const int *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < num_columns; ++c) {
-              data[i * 4 + c] = (float)*from_data++;
-            }
-          }
-        }
-        return data;
-
-      case ShaderType::ST_uint:
-        // Convert unsigned int data to float data.
-        if (!pad_rows || num_columns == 4) {
-          for (int i = 0; i < total_rows * num_columns; ++i) {
-            data[i] = (float)(((unsigned int *)ptr_data._ptr)[i]);
-          }
-        } else {
-          const unsigned int *from_data = (const unsigned int *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < num_columns; ++c) {
-              data[i * 4 + c] = (float)*from_data++;
-            }
-          }
-        }
-        return data;
-
-      case ShaderType::ST_double:
-        // Downgrade double data to float data.
-        if (!pad_rows || num_columns == 4) {
-          for (int i = 0; i < total_rows * num_columns; ++i) {
-            data[i] = (float)(((double *)ptr_data._ptr)[i]);
-          }
-        } else {
-          const double *from_data = (const double *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < num_columns; ++c) {
-              data[i * 4 + c] = (float)*from_data++;
-            }
-          }
-        }
-        return data;
-
-      case ShaderType::ST_float:
-        if (!pad_rows || num_columns == 4) {
-          // No conversion needed.
-          if (always_copy) {
-            memcpy(data, ptr_data._ptr, total_rows * num_columns * sizeof(float));
-            return data;
-          } else {
-            return (float *)ptr_data._ptr;
-          }
-        } else {
-          const float *from_data = (const float *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < num_columns; ++c) {
-              data[i * 4 + c] = (float)*from_data++;
-            }
-          }
-        }
-        return data;
-
-      default:
-#ifndef NDEBUG
-        pgraph_cat.error()
-          << "Invalid ShaderPtrData type " << (int)ptr_data._type
-          << " for shader input '" << *id << "'\n";
-#endif
-        return nullptr;
-      }
-
-      return data;
-    }
-    break;
-
-  case ShaderType::ST_int:
-    if (ptr_data._type != ShaderType::ST_int &&
-        ptr_data._type != ShaderType::ST_uint &&
-        ptr_data._type != ShaderType::ST_bool) {
-      pgraph_cat.error()
-        << "Cannot pass floating-point data to integer shader input '" << *id << "'\n";
-      return nullptr;
-    }
-    else if (always_copy) {
-      memcpy(scratch, ptr_data._ptr, total_rows * num_columns * sizeof(int));
-      return scratch;
-    }
-    else {
-      return ptr_data._ptr;
-    }
-    break;
-
-  case ShaderType::ST_uint:
-    if (ptr_data._type != ShaderType::ST_uint &&
-        ptr_data._type != ShaderType::ST_int &&
-        ptr_data._type != ShaderType::ST_bool) {
-      pgraph_cat.error()
-        << "Cannot pass floating-point data to integer shader input '" << *id << "'\n";
-      return nullptr;
-    }
-    else if (always_copy) {
-      memcpy(scratch, ptr_data._ptr, total_rows * num_columns * sizeof(unsigned int));
-      return scratch;
-    }
-    else {
-      return ptr_data._ptr;
-    }
-    break;
-
-  case ShaderType::ST_double:
-    {
-      double *data = (double *)scratch;
-
-      switch (ptr_data._type) {
-      case ShaderType::ST_int:
-        // Convert int data to double data.
-        if (!pad_rows || num_columns == 4) {
-          for (int i = 0; i < total_rows * num_columns; ++i) {
-            data[i] = (double)(((int *)ptr_data._ptr)[i]);
-          }
-        } else {
-          const int *from_data = (const int *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < num_columns; ++c) {
-              data[i * 4 + c] = (double)*from_data++;
-            }
-          }
-        }
-        return data;
-
-      case ShaderType::ST_uint:
-        // Convert int data to double data.
-        if (!pad_rows || num_columns == 4) {
-          for (int i = 0; i < total_rows * num_columns; ++i) {
-            data[i] = (double)(((unsigned int *)ptr_data._ptr)[i]);
-          }
-        } else {
-          const int *from_data = (const int *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < num_columns; ++c) {
-              data[i * 4 + c] = (double)*from_data++;
-            }
-          }
-        }
-        return data;
-
-      case ShaderType::ST_double:
-        if (!pad_rows || num_columns == 4) {
-          // No conversion needed.
-          if (always_copy) {
-            memcpy(data, ptr_data._ptr, total_rows * num_columns * sizeof(double));
-            return data;
-          } else {
-            return (double *)ptr_data._ptr;
-          }
-        } else {
-          const double *from_data = (const double *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < num_columns; ++c) {
-              data[i * 4 + c] = (double)*from_data++;
-            }
-          }
-        }
-        return data;
-
-      case ShaderType::ST_float:
-        // Upgrade float data to double data.
-        if (!pad_rows || num_columns == 4) {
-          for (int i = 0; i < total_rows * num_columns; ++i) {
-            data[i] = (double)(((float *)ptr_data._ptr)[i]);
-          }
-        } else {
-          const float *from_data = (const float *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < num_columns; ++c) {
-              data[i * 4 + c] = (double)*from_data++;
-            }
-          }
-        }
-        return data;
-
-      default:
-  #ifndef NDEBUG
-        pgraph_cat.error()
-          << "Invalid ShaderPtrData type " << (int)ptr_data._type
-          << " for shader input '" << *id << "'\n";
-  #endif
-        return nullptr;
-      }
-
-      return data;
-    }
-    break;
-
-  case ShaderType::ST_bool:
-    {
-      unsigned int *data = (unsigned int *)scratch;
-
-      switch (ptr_data._type) {
-      case ShaderType::ST_int:
-      case ShaderType::ST_uint:
-      case ShaderType::ST_bool:
-        if (!pad_rows || num_columns == 4) {
-          // No conversion needed.
-          if (always_copy) {
-            memcpy(data, ptr_data._ptr, total_rows * num_columns * sizeof(unsigned int));
-            return data;
-          } else {
-            return (unsigned int *)ptr_data._ptr;
-          }
-        } else {
-          // Pad out rows.
-          const unsigned int *from_data = (const unsigned int *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < num_columns; ++c) {
-              data[i * 4 + c] = (*from_data++) != 0;
-            }
-          }
-        }
-        return data;
-
-      case ShaderType::ST_double:
-        if (!pad_rows || num_columns == 4) {
-          for (int i = 0; i < total_rows * num_columns; ++i) {
-            data[i] = ((double *)ptr_data._ptr)[i] != 0.0;
-          }
-        } else {
-          const double *from_data = (const double *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < num_columns; ++c) {
-              data[i * 4 + c] = (*from_data++) != 0.0;
-            }
-          }
-        }
-        return data;
-
-      case ShaderType::ST_float:
-        if (!pad_rows || num_columns == 4) {
-          for (int i = 0; i < total_rows * num_columns; ++i) {
-            data[i] = ((float *)ptr_data._ptr)[i] != 0.0f;
-          }
-        } else {
-          const float *from_data = (const float *)ptr_data._ptr;
-          for (int i = 0; i < total_rows; ++i) {
-            for (int c = 0; c < num_columns; ++c) {
-              data[i * 4 + c] = (*from_data++) != 0.0f;
-            }
-          }
-        }
-        return data;
-
-      default:
-        break;
-      }
-    }
-    break;
-
-  case ShaderType::ST_unknown:
-    break;
-  }
-
-  return nullptr;
-}
-
 /**
 /**
  * Returns the ShaderInput as a texture.  Assertion fails if there is none, or
  * Returns the ShaderInput as a texture.  Assertion fails if there is none, or
  * if it is not a texture.
  * if it is not a texture.
@@ -891,7 +550,7 @@ get_shader_input_texture_image(const InternalName *id, ShaderType::Access &acces
       // People find it convenient to be able to pass a texture without
       // People find it convenient to be able to pass a texture without
       // further ado.
       // further ado.
       tex = p.get_texture();
       tex = p.get_texture();
-      access = ShaderType::Access::read_write;
+      access = ShaderType::Access::READ_WRITE;
       z = -1;
       z = -1;
       n = 0;
       n = 0;
       break;
       break;

+ 0 - 6
panda/src/pgraph/shaderAttrib.h

@@ -124,12 +124,6 @@ PUBLISHED:
   bool get_shader_input_ptr(const InternalName *id, Shader::ShaderPtrData &data) const;
   bool get_shader_input_ptr(const InternalName *id, Shader::ShaderPtrData &data) const;
   const LMatrix4f &get_shader_input_matrix(const InternalName *id, LMatrix4f &matrix) const;
   const LMatrix4f &get_shader_input_matrix(const InternalName *id, LMatrix4f &matrix) const;
   const LMatrix4d &get_shader_input_matrix(const InternalName *id, LMatrix4d &matrix) const;
   const LMatrix4d &get_shader_input_matrix(const InternalName *id, LMatrix4d &matrix) const;
-  size_t get_shader_input_data(const InternalName *id, void *into,
-                               const ShaderType *type, bool pad_rows) const;
-  void *get_shader_input_data(const InternalName *id, void *scratch,
-                              ShaderType::ScalarType scalar_type, int num_elements,
-                              int num_rows, int num_columns, bool pad_rows,
-                              bool always_copy=false) const;
   ShaderBuffer *get_shader_input_buffer(const InternalName *id) const;
   ShaderBuffer *get_shader_input_buffer(const InternalName *id) const;
 
 
 PUBLISHED:
 PUBLISHED:

+ 9 - 9
panda/src/shaderpipeline/shaderCompilerGlslang.cxx

@@ -231,22 +231,22 @@ compile_now(ShaderModule::Stage stage, std::istream &in,
   EShMessages messages = (EShMessages)(EShMsgDefault | EShMsgSpvRules);
   EShMessages messages = (EShMessages)(EShMsgDefault | EShMsgSpvRules);
   EShLanguage language;
   EShLanguage language;
   switch (stage) {
   switch (stage) {
-  case ShaderModule::Stage::vertex:
+  case ShaderModule::Stage::VERTEX:
     language = EShLangVertex;
     language = EShLangVertex;
     break;
     break;
-  case ShaderModule::Stage::tess_control:
+  case ShaderModule::Stage::TESS_CONTROL:
     language = EShLangTessControl;
     language = EShLangTessControl;
     break;
     break;
-  case ShaderModule::Stage::tess_evaluation:
+  case ShaderModule::Stage::TESS_EVALUATION:
     language = EShLangTessEvaluation;
     language = EShLangTessEvaluation;
     break;
     break;
-  case ShaderModule::Stage::geometry:
+  case ShaderModule::Stage::GEOMETRY:
     language = EShLangGeometry;
     language = EShLangGeometry;
     break;
     break;
-  case ShaderModule::Stage::fragment:
+  case ShaderModule::Stage::FRAGMENT:
     language = EShLangFragment;
     language = EShLangFragment;
     break;
     break;
-  case ShaderModule::Stage::compute:
+  case ShaderModule::Stage::COMPUTE:
     language = EShLangCompute;
     language = EShLangCompute;
     break;
     break;
   default:
   default:
@@ -269,13 +269,13 @@ compile_now(ShaderModule::Stage stage, std::istream &in,
 
 
     const char *source_entry_point;
     const char *source_entry_point;
     switch (stage) {
     switch (stage) {
-    case ShaderModule::Stage::vertex:
+    case ShaderModule::Stage::VERTEX:
       source_entry_point = "vshader";
       source_entry_point = "vshader";
       break;
       break;
-    case ShaderModule::Stage::geometry:
+    case ShaderModule::Stage::GEOMETRY:
       source_entry_point = "gshader";
       source_entry_point = "gshader";
       break;
       break;
-    case ShaderModule::Stage::fragment:
+    case ShaderModule::Stage::FRAGMENT:
       source_entry_point = "fshader";
       source_entry_point = "fshader";
       break;
       break;
     default:
     default:

+ 43 - 9
panda/src/shaderpipeline/shaderModuleSpirV.cxx

@@ -148,6 +148,11 @@ ShaderModuleSpirV(Stage stage, std::vector<uint32_t> words, BamCacheRecord *reco
   // Add in location decorations for any inputs that are missing it.
   // Add in location decorations for any inputs that are missing it.
   transformer.assign_locations(stage);
   transformer.assign_locations(stage);
 
 
+  // Get rid of uniform locations and bindings.  The numbering rules are
+  // different for each back-end, so we regenerate these later.
+  transformer.strip_uniform_locations();
+  transformer.strip_bindings();
+
   // Identify the inputs, outputs and uniform parameters.
   // Identify the inputs, outputs and uniform parameters.
   for (uint32_t id = 0; id < transformer.get_id_bound(); ++id) {
   for (uint32_t id = 0; id < transformer.get_id_bound(); ++id) {
     const Definition &def = db.get_definition(id);
     const Definition &def = db.get_definition(id);
@@ -182,7 +187,7 @@ ShaderModuleSpirV(Stage stage, std::vector<uint32_t> words, BamCacheRecord *reco
       if (def._storage_class == spv::StorageClassInput) {
       if (def._storage_class == spv::StorageClassInput) {
         _inputs.push_back(std::move(var));
         _inputs.push_back(std::move(var));
 
 
-        if (stage == Stage::fragment) {
+        if (stage == Stage::FRAGMENT) {
           // Integer varyings require shader model 4.
           // Integer varyings require shader model 4.
           if (def._type->contains_scalar_type(ShaderType::ST_uint) ||
           if (def._type->contains_scalar_type(ShaderType::ST_uint) ||
               def._type->contains_scalar_type(ShaderType::ST_int) ||
               def._type->contains_scalar_type(ShaderType::ST_int) ||
@@ -246,11 +251,16 @@ ShaderModuleSpirV(Stage stage, std::vector<uint32_t> words, BamCacheRecord *reco
         // name of the struct type.
         // name of the struct type.
         const Definition &type_pointer_def = db.get_definition(def._type_id);
         const Definition &type_pointer_def = db.get_definition(def._type_id);
         nassertd(type_pointer_def.is_pointer_type()) continue;
         nassertd(type_pointer_def.is_pointer_type()) continue;
-        const Definition &type_def = db.get_definition(type_pointer_def._type_id);
-        nassertd(type_def.is_type()) continue;
-        nassertd(!type_def._name.empty()) continue;
 
 
-        var.name = InternalName::make(type_def._name);
+        // This may be an array of structs.  Unwrap any array layers.
+        const Definition *type_def = &db.get_definition(type_pointer_def._type_id);
+        while (type_def->_type->as_array() != nullptr) {
+          type_def = &db.get_definition(type_def->_type_id);
+        }
+        nassertd(type_def->is_type()) continue;
+        nassertd(!type_def->_name.empty()) continue;
+
+        var.name = InternalName::make(type_def->_name);
         _parameters.push_back(std::move(var));
         _parameters.push_back(std::move(var));
 
 
         _used_caps |= C_storage_buffer;
         _used_caps |= C_storage_buffer;
@@ -302,6 +312,30 @@ ShaderModuleSpirV(Stage stage, std::vector<uint32_t> words, BamCacheRecord *reco
         _used_caps |= C_sample_variables;
         _used_caps |= C_sample_variables;
         break;
         break;
 
 
+      case spv::BuiltInFragCoord:
+        // glslang doesn't always check this properly, so we may need to convert
+        // this to a vec4 in order to get a valid module.
+        {
+          const ShaderType::Vector *vector_type = def._type->as_vector();
+          if (vector_type != nullptr || def._type->as_scalar() != nullptr) {
+            if (vector_type == nullptr ||
+                vector_type->get_scalar_type() != ShaderType::ST_float ||
+                vector_type->get_num_components() != 4) {
+              ShaderType::ScalarType scalar_type =
+                (vector_type != nullptr)
+                ? vector_type->get_scalar_type()
+                : def._type->as_scalar()->get_scalar_type();
+
+              const ShaderType *new_type = ShaderType::register_type(ShaderType::Vector(scalar_type, 4));
+              transformer.run(SpirVReplaceVariableTypePass(id, new_type, spv::StorageClassInput));
+            }
+          } else {
+            shader_cat.error()
+              << "FragCoord input must be a vector!\n";
+          }
+        }
+        break;
+
       default:
       default:
         break;
         break;
       }
       }
@@ -401,13 +435,13 @@ ShaderModuleSpirV(Stage stage, std::vector<uint32_t> words, BamCacheRecord *reco
 
 
     case spv::OpImageSampleExplicitLod:
     case spv::OpImageSampleExplicitLod:
     case spv::OpImageSampleProjExplicitLod:
     case spv::OpImageSampleProjExplicitLod:
-      if (stage != Stage::vertex) {
+      if (stage != Stage::VERTEX) {
         _used_caps |= C_texture_lod;
         _used_caps |= C_texture_lod;
       }
       }
       // fall through
       // fall through
     case spv::OpImageSampleImplicitLod:
     case spv::OpImageSampleImplicitLod:
     case spv::OpImageSampleProjImplicitLod:
     case spv::OpImageSampleProjImplicitLod:
-      if (stage == Stage::vertex) {
+      if (stage == Stage::VERTEX) {
         _used_caps |= C_vertex_texture;
         _used_caps |= C_vertex_texture;
       }
       }
       if (op.nargs >= 5 && (op.args[4] & spv::ImageOperandsGradMask) != 0) {
       if (op.nargs >= 5 && (op.args[4] & spv::ImageOperandsGradMask) != 0) {
@@ -417,7 +451,7 @@ ShaderModuleSpirV(Stage stage, std::vector<uint32_t> words, BamCacheRecord *reco
 
 
     case spv::OpImageSampleDrefExplicitLod:
     case spv::OpImageSampleDrefExplicitLod:
     case spv::OpImageSampleProjDrefExplicitLod:
     case spv::OpImageSampleProjDrefExplicitLod:
-      if (stage != Stage::vertex) {
+      if (stage != Stage::VERTEX) {
         _used_caps |= C_texture_lod;
         _used_caps |= C_texture_lod;
       }
       }
       // fall through
       // fall through
@@ -435,7 +469,7 @@ ShaderModuleSpirV(Stage stage, std::vector<uint32_t> words, BamCacheRecord *reco
           }
           }
         }
         }
       }
       }
-      if (stage == Stage::vertex) {
+      if (stage == Stage::VERTEX) {
         _used_caps |= C_vertex_texture;
         _used_caps |= C_vertex_texture;
       }
       }
       if (op.nargs >= 5 && (op.args[4] & spv::ImageOperandsGradMask) != 0) {
       if (op.nargs >= 5 && (op.args[4] & spv::ImageOperandsGradMask) != 0) {

+ 93 - 0
panda/src/shaderpipeline/spirVReplaceVariableTypePass.cxx

@@ -47,6 +47,7 @@ transform_definition_op(Instruction op) {
         (uint32_t)_new_storage_class,
         (uint32_t)_new_storage_class,
       });
       });
       def._type = _new_type;
       def._type = _new_type;
+      def._type_id = _pointer_type_id;
       if (def.is_used()) {
       if (def.is_used()) {
         _db.mark_used(_variable_id);
         _db.mark_used(_variable_id);
       }
       }
@@ -66,6 +67,98 @@ bool SpirVReplaceVariableTypePass::
 transform_function_op(Instruction op) {
 transform_function_op(Instruction op) {
   switch (op.opcode) {
   switch (op.opcode) {
   case spv::OpLoad:
   case spv::OpLoad:
+    if (_pointer_ids.count(op.args[2])) {
+      Definition &def = _db.modify_definition(op.args[1]);
+
+      // If both are vectors or scalars, we can try a conversion.
+      const ShaderType::Vector *old_vector = _new_type->as_vector();
+      const ShaderType::Scalar *old_scalar = _new_type->as_scalar();
+      const ShaderType::Vector *new_vector = def._type->as_vector();
+      const ShaderType::Scalar *new_scalar = def._type->as_scalar();
+      if ((old_vector != nullptr && new_vector != nullptr && old_vector != new_vector) ||
+          (old_scalar != nullptr && new_scalar != nullptr && old_scalar != new_scalar) ||
+          (old_vector != nullptr && new_scalar != nullptr) ||
+          (old_scalar != nullptr && new_vector != nullptr)) {
+        uint32_t temp = op_load(op.args[2]);
+        ShaderType::ScalarType old_scalar_type, new_scalar_type;
+        if (new_vector != nullptr && old_vector != nullptr) {
+          // Swizzle the vector.
+          old_scalar_type = old_vector->get_scalar_type();
+          new_scalar_type = new_vector->get_scalar_type();
+          if (new_vector->get_num_components() != old_vector->get_num_components()) {
+            pvector<uint32_t> components;
+            uint32_t i = 0;
+            while (i < new_vector->get_num_components() && i < old_vector->get_num_components()) {
+              components.push_back(i);
+              ++i;
+            }
+            // The remaining components are undefined.
+            while (i < new_vector->get_num_components()) {
+              components.push_back(0xffffffff);
+              ++i;
+            }
+            temp = op_vector_shuffle(temp, temp, components);
+          }
+        }
+        else if (new_vector != nullptr) {
+          // Convert scalar to vector.
+          old_scalar_type = old_scalar->get_scalar_type();
+          new_scalar_type = new_vector->get_scalar_type();
+          pvector<uint32_t> components(new_vector->get_num_components(), temp);
+          temp = op_composite_construct(new_scalar, components);
+        }
+        else if (new_scalar != nullptr) {
+          // Convert vector to scalar.
+          old_scalar_type = old_vector->get_scalar_type();
+          new_scalar_type = new_scalar->get_scalar_type();
+          temp = op_composite_extract(temp, {0});
+        }
+        else {
+          old_scalar_type = old_scalar->get_scalar_type();
+          new_scalar_type = new_scalar->get_scalar_type();
+        }
+
+        // Determine which conversion instruction to use.
+        spv::Op opcode;
+        if (old_scalar_type != new_scalar_type) {
+          bool old_float = old_scalar_type == ShaderType::ST_float
+                        || old_scalar_type == ShaderType::ST_double;
+          bool new_float = new_scalar_type == ShaderType::ST_float
+                        || new_scalar_type == ShaderType::ST_double;
+
+          if (old_float && new_float) {
+            opcode = spv::OpFConvert;
+          }
+          else if (old_float) {
+            bool new_signed = new_scalar_type == ShaderType::ST_int;
+            opcode = new_signed ? spv::OpConvertFToS : spv::OpConvertFToU;
+          }
+          else if (new_float) {
+            bool old_signed = old_scalar_type == ShaderType::ST_int;
+            opcode = old_signed ? spv::OpConvertSToF : spv::OpConvertUToF;
+          }
+          else {
+            // Assuming it's the same bit width, for now.
+            opcode = spv::OpBitcast;
+          }
+        } else {
+          // Redundant instruction, but keeps the logic here simple.
+          opcode = spv::OpCopyObject;
+        }
+        // Replace the original load with our conversion.
+        add_instruction(opcode, {op.args[0], op.args[1], temp});
+        return false;
+      }
+      else {
+        def._type = _new_type;
+        def._type_id = _type_id;
+
+        op.args[0] = _type_id;
+        _object_ids.insert(op.args[1]);
+      }
+    }
+    break;
+
   case spv::OpAtomicLoad:
   case spv::OpAtomicLoad:
   case spv::OpAtomicExchange:
   case spv::OpAtomicExchange:
   case spv::OpAtomicCompareExchange:
   case spv::OpAtomicCompareExchange:

+ 3 - 1
panda/src/shaderpipeline/spirVReplaceVariableTypePass.h

@@ -19,7 +19,9 @@
 /**
 /**
  * Changes the type of a given variable.  Does not check that the existing
  * Changes the type of a given variable.  Does not check that the existing
  * usage of the variable in the shader is valid with the new type - it only
  * usage of the variable in the shader is valid with the new type - it only
- * changes the types of loads and copies.
+ * changes the types of loads and copies.  An exception is when changing a
+ * scalar or vector to a different scalar type or number of components, where
+ * conversion is performed at the load point.
  */
  */
 class EXPCL_PANDA_SHADERPIPELINE SpirVReplaceVariableTypePass final : public SpirVTransformPass {
 class EXPCL_PANDA_SHADERPIPELINE SpirVReplaceVariableTypePass final : public SpirVTransformPass {
 public:
 public:

+ 86 - 43
panda/src/shaderpipeline/spirVResultDatabase.cxx

@@ -201,11 +201,13 @@ parse_instruction(spv::Op opcode, const uint32_t *args, uint32_t nargs, uint32_t
 
 
   case spv::OpTypeMatrix:
   case spv::OpTypeMatrix:
     {
     {
-      const ShaderType::Vector *column_type;
-      DCAST_INTO_V(column_type, _defs[args[1]]._type);
+      // SPIR-V uses GLSL parlance, in which a column is a row and a row is a
+      // column, compared to Panda conventions.  We flip it around here.
+      const ShaderType::Vector *row_type;
+      DCAST_INTO_V(row_type, _defs[args[1]]._type);
       uint32_t num_rows = args[2];
       uint32_t num_rows = args[2];
       record_type(args[0], ShaderType::register_type(
       record_type(args[0], ShaderType::register_type(
-        ShaderType::Matrix(column_type->get_scalar_type(), num_rows, column_type->get_num_components())));
+        ShaderType::Matrix(row_type->get_scalar_type(), num_rows, row_type->get_num_components())));
       _defs[args[0]]._type_id = args[1];
       _defs[args[0]]._type_id = args[1];
     }
     }
     break;
     break;
@@ -285,17 +287,17 @@ parse_instruction(spv::Op opcode, const uint32_t *args, uint32_t nargs, uint32_t
         return;
         return;
       }
       }
 
 
-      ShaderType::Access access = ShaderType::Access::read_write;
+      ShaderType::Access access = ShaderType::Access::READ_WRITE;
       if (nargs > 8) {
       if (nargs > 8) {
         switch ((spv::AccessQualifier)args[8]) {
         switch ((spv::AccessQualifier)args[8]) {
         case spv::AccessQualifierReadOnly:
         case spv::AccessQualifierReadOnly:
-          access = ShaderType::Access::read_only;
+          access = ShaderType::Access::READ_ONLY;
           break;
           break;
         case spv::AccessQualifierWriteOnly:
         case spv::AccessQualifierWriteOnly:
-          access = ShaderType::Access::write_only;
+          access = ShaderType::Access::WRITE_ONLY;
           break;
           break;
         case spv::AccessQualifierReadWrite:
         case spv::AccessQualifierReadWrite:
-          access = ShaderType::Access::read_write;
+          access = ShaderType::Access::READ_WRITE;
           break;
           break;
         default:
         default:
           shader_cat.error()
           shader_cat.error()
@@ -304,10 +306,10 @@ parse_instruction(spv::Op opcode, const uint32_t *args, uint32_t nargs, uint32_t
         }
         }
       }
       }
       if (_defs[args[0]]._flags & DF_non_writable) {
       if (_defs[args[0]]._flags & DF_non_writable) {
-        access = (access & ShaderType::Access::read_only);
+        access = (access & ShaderType::Access::READ_ONLY);
       }
       }
       if (_defs[args[0]]._flags & DF_non_readable) {
       if (_defs[args[0]]._flags & DF_non_readable) {
-        access = (access & ShaderType::Access::write_only);
+        access = (access & ShaderType::Access::WRITE_ONLY);
       }
       }
 
 
       record_type(args[0], ShaderType::register_type(
       record_type(args[0], ShaderType::register_type(
@@ -382,10 +384,10 @@ parse_instruction(spv::Op opcode, const uint32_t *args, uint32_t nargs, uint32_t
           if (const ShaderType::Image *image = member_type->as_image()) {
           if (const ShaderType::Image *image = member_type->as_image()) {
             ShaderType::Access access = image->get_access();
             ShaderType::Access access = image->get_access();
             if (member_def._flags & DF_non_writable) {
             if (member_def._flags & DF_non_writable) {
-              access = (access & ShaderType::Access::read_only);
+              access = (access & ShaderType::Access::READ_ONLY);
             }
             }
             if (member_def._flags & DF_non_readable) {
             if (member_def._flags & DF_non_readable) {
-              access = (access & ShaderType::Access::write_only);
+              access = (access & ShaderType::Access::WRITE_ONLY);
             }
             }
             if (access != image->get_access()) {
             if (access != image->get_access()) {
               member_type = ShaderType::register_type(ShaderType::Image(
               member_type = ShaderType::register_type(ShaderType::Image(
@@ -602,6 +604,10 @@ parse_instruction(spv::Op opcode, const uint32_t *args, uint32_t nargs, uint32_t
       _defs[args[0]]._flags |= DF_buffer_block;
       _defs[args[0]]._flags |= DF_buffer_block;
       break;
       break;
 
 
+    case spv::DecorationBlock:
+      _defs[args[0]]._flags |= DF_block;
+      break;
+
     case spv::DecorationBuiltIn:
     case spv::DecorationBuiltIn:
       _defs[args[0]]._builtin = (spv::BuiltIn)args[2];
       _defs[args[0]]._builtin = (spv::BuiltIn)args[2];
       break;
       break;
@@ -943,9 +949,43 @@ record_pointer_type(uint32_t id, spv::StorageClass storage_class, uint32_t type_
 
 
   const Definition &type_def = get_definition(type_id);
   const Definition &type_def = get_definition(type_id);
   nassertv(type_def._dtype == DT_type || type_def._dtype == DT_pointer_type);
   nassertv(type_def._dtype == DT_type || type_def._dtype == DT_pointer_type);
+  const ShaderType *type = type_def._type;
+
+  // Now that we know the access type, we can determine whether a struct is
+  // actually a uniform block or storage block.
+  if (storage_class == spv::StorageClassUniform ||
+      storage_class == spv::StorageClassStorageBuffer) {
+    // Unwrap arrays to get to the underlying block type.
+    uint32_t block_type_id = type_id;
+    while (_defs[block_type_id]._type->as_array() != nullptr) {
+      block_type_id = _defs[block_type_id]._type_id;
+    }
+
+    const Definition &block_type_def = get_definition(block_type_id);
+    if (block_type_def._type->as_struct() != nullptr) {
+      // Check if this was pointing to an SSBO.
+      if (block_type_def._flags & (DF_buffer_block | DF_block)) {
+        if (block_type_def._flags & DF_buffer_block) {
+          // Map old SPIR-V way of declaring SSBO to new way.
+          storage_class = spv::StorageClassStorageBuffer;
+        }
+        if (storage_class == spv::StorageClassStorageBuffer) {
+          ShaderType::Access access = ShaderType::Access::READ_WRITE;
+          if (block_type_def._flags & DF_non_writable) {
+            access = (access & ShaderType::Access::READ_ONLY);
+          }
+          if (block_type_def._flags & DF_non_readable) {
+            access = (access & ShaderType::Access::WRITE_ONLY);
+          }
+          const ShaderType *new_type = ShaderType::register_type(ShaderType::StorageBuffer(block_type_def._type, access));
+          type = type->replace_type(block_type_def._type, new_type);
+        }
+      }
+    }
+  }
 
 
   def._dtype = DT_pointer_type;
   def._dtype = DT_pointer_type;
-  def._type = type_def._type;
+  def._type = type;
   def._storage_class = storage_class;
   def._storage_class = storage_class;
   def._type_id = type_id;
   def._type_id = type_id;
 }
 }
@@ -973,64 +1013,67 @@ record_variable(uint32_t id, uint32_t pointer_type_id, spv::StorageClass storage
     return;
     return;
   }
   }
 
 
-  // In older versions of SPIR-V, an SSBO was defined using BufferBlock.
+  // If we've determined that the pointer was supposed to be StorageBuffer
+  // instead of Uniform, change that here as well.
   if (storage_class == spv::StorageClassUniform &&
   if (storage_class == spv::StorageClassUniform &&
-      (type_def._flags & DF_buffer_block) != 0) {
+      pointer_type_def._storage_class == spv::StorageClassStorageBuffer) {
     storage_class = spv::StorageClassStorageBuffer;
     storage_class = spv::StorageClassStorageBuffer;
   }
   }
 
 
   def._dtype = DT_variable;
   def._dtype = DT_variable;
-  def._type = type_def._type;
+  def._type = pointer_type_def._type;
   def._type_id = pointer_type_id;
   def._type_id = pointer_type_id;
   def._storage_class = storage_class;
   def._storage_class = storage_class;
   def._origin_id = id;
   def._origin_id = id;
   def._function_id = function_id;
   def._function_id = function_id;
 
 
-  if (storage_class == spv::StorageClassStorageBuffer) {
-    // Inherit readonly/writeonly from the variable but also from the struct.
-    int flags = def._flags | type_def._flags;
-    ShaderType::Access access = ShaderType::Access::read_write;
-    if (flags & DF_non_writable) {
-      access = (access & ShaderType::Access::read_only);
-    }
-    if (flags & DF_non_readable) {
-      access = (access & ShaderType::Access::write_only);
+  if (def._flags & (DF_non_writable | DF_non_readable)) {
+    // If an image variable, SSBO or array thereof has the readonly/writeonly
+    // qualifiers, then we'll inject those back into the type.
+    const ShaderType *unwrapped_type = def._type;
+    while (const ShaderType::Array *array_type = unwrapped_type->as_array()) {
+      unwrapped_type = array_type->get_element_type();
     }
     }
-    def._type = ShaderType::register_type(ShaderType::StorageBuffer(def._type, access));
 
 
-    if (shader_cat.is_debug()) {
-      std::ostream &out = shader_cat.debug()
-        << "Defined buffer " << id;
-      if (!def._name.empty()) {
-        out << ": " << def._name;
-      }
-      out << " with type " << *def._type << "\n";
-    }
-  }
-  else if (def._flags & (DF_non_writable | DF_non_readable)) {
-    // If an image variable has the readonly/writeonly qualifiers, then we'll
-    // inject those back into the type.
-    if (const ShaderType::Image *image = def._type->as_image()) {
+    const ShaderType *new_type = unwrapped_type;
+    if (const ShaderType::Image *image = unwrapped_type->as_image()) {
       ShaderType::Access access = image->get_access();
       ShaderType::Access access = image->get_access();
       if (def._flags & DF_non_writable) {
       if (def._flags & DF_non_writable) {
-        access = (access & ShaderType::Access::read_only);
+        access = (access & ShaderType::Access::READ_ONLY);
       }
       }
       if (def._flags & DF_non_readable) {
       if (def._flags & DF_non_readable) {
-        access = (access & ShaderType::Access::write_only);
+        access = (access & ShaderType::Access::WRITE_ONLY);
       }
       }
       if (access != image->get_access()) {
       if (access != image->get_access()) {
-        def._type = ShaderType::register_type(ShaderType::Image(
+        new_type = ShaderType::register_type(ShaderType::Image(
           image->get_texture_type(),
           image->get_texture_type(),
           image->get_sampled_type(),
           image->get_sampled_type(),
           access));
           access));
       }
       }
     }
     }
+    else if (const ShaderType::StorageBuffer *buffer = unwrapped_type->as_storage_buffer()) {
+      ShaderType::Access access = buffer->get_access();
+      if (def._flags & DF_non_writable) {
+        access = (access & ShaderType::Access::READ_ONLY);
+      }
+      if (def._flags & DF_non_readable) {
+        access = (access & ShaderType::Access::WRITE_ONLY);
+      }
+      if (access != buffer->get_access()) {
+        new_type = ShaderType::register_type(ShaderType::StorageBuffer(
+          buffer->get_contained_type(), access));
+      }
+    }
+
+    if (new_type != unwrapped_type) {
+      def._type = def._type->replace_type(unwrapped_type, new_type);
+    }
   }
   }
 
 
 #ifndef NDEBUG
 #ifndef NDEBUG
-  if (storage_class == spv::StorageClassUniformConstant && shader_cat.is_debug()) {
+  if (shader_cat.is_debug()) {
     std::ostream &out = shader_cat.debug()
     std::ostream &out = shader_cat.debug()
-      << "Defined uniform " << id;
+      << "Defined variable " << id;
 
 
     if (!def._name.empty()) {
     if (!def._name.empty()) {
       out << ": " << def._name;
       out << ": " << def._name;

+ 7 - 5
panda/src/shaderpipeline/spirVResultDatabase.h

@@ -38,16 +38,18 @@ private:
     // Set for arrays that are indexed with a non-const index.
     // Set for arrays that are indexed with a non-const index.
     DF_dynamically_indexed = 32,
     DF_dynamically_indexed = 32,
 
 
-    // Has the "buffer block" decoration (older versions of SPIR-V).
+    // Has the "buffer block" decoration (older versions of SPIR-V),
+    // or the "block" decoration on newer versions.
     DF_buffer_block = 64,
     DF_buffer_block = 64,
+    DF_block = 128,
 
 
     // If both of these are set, no access is permitted (size queries only)
     // If both of these are set, no access is permitted (size queries only)
-    DF_non_writable = 128, // readonly
-    DF_non_readable = 256, // writeonly
+    DF_non_writable = 256, // readonly
+    DF_non_readable = 512, // writeonly
 
 
-    DF_relaxed_precision = 512,
+    DF_relaxed_precision = 1024,
 
 
-    DF_null_constant = 1024,
+    DF_null_constant = 2048,
   };
   };
 
 
 public:
 public:

+ 53 - 5
panda/src/shaderpipeline/spirVTransformPass.cxx

@@ -785,16 +785,16 @@ define_type(const ShaderType *type) {
 
 
     uint32_t nargs = 8;
     uint32_t nargs = 8;
     switch (image_type->get_access()) {
     switch (image_type->get_access()) {
-    case ShaderType::Access::none:
-    case ShaderType::Access::read_only:
+    case ShaderType::Access::NONE:
+    case ShaderType::Access::READ_ONLY:
       args[8] = spv::AccessQualifierReadOnly;
       args[8] = spv::AccessQualifierReadOnly;
       ++nargs;
       ++nargs;
       break;
       break;
-    case ShaderType::Access::write_only:
+    case ShaderType::Access::WRITE_ONLY:
       args[8] = spv::AccessQualifierWriteOnly;
       args[8] = spv::AccessQualifierWriteOnly;
       ++nargs;
       ++nargs;
       break;
       break;
-    case ShaderType::Access::read_write:
+    case ShaderType::Access::READ_WRITE:
       args[8] = spv::AccessQualifierReadWrite;
       args[8] = spv::AccessQualifierReadWrite;
       ++nargs;
       ++nargs;
       break;
       break;
@@ -1001,8 +1001,9 @@ r_annotate_struct_layout(uint32_t type_id) {
 
 
     if (const ShaderType::Matrix *matrix_type = base_type->as_matrix()) {
     if (const ShaderType::Matrix *matrix_type = base_type->as_matrix()) {
       // Matrix types need to be explicitly laid out.
       // Matrix types need to be explicitly laid out.
+      uint32_t stride = ShaderType::get_scalar_size_bytes(matrix_type->get_scalar_type()) * 4;
       add_annotation(spv::OpMemberDecorate,
       add_annotation(spv::OpMemberDecorate,
-        {type_id, i, spv::DecorationMatrixStride, (matrix_type->get_scalar_type() == ShaderType::ST_double) ? 32 : 16});
+        {type_id, i, spv::DecorationMatrixStride, stride});
       add_annotation(spv::OpMemberDecorate,
       add_annotation(spv::OpMemberDecorate,
         {type_id, i, spv::DecorationColMajor});
         {type_id, i, spv::DecorationColMajor});
     } else {
     } else {
@@ -1129,6 +1130,52 @@ op_access_chain(uint32_t var_id, std::initializer_list<uint32_t> chain) {
   return id;
   return id;
 }
 }
 
 
+/**
+ * Inserts an OpVectorShuffle, like a swizzle but may source from two vectors
+ * at once, with the indices continuing to number into the second vector.
+ * For a regular swizzle, pass the same vector twice.
+ */
+uint32_t SpirVTransformPass::
+op_vector_shuffle(uint32_t vec1, uint32_t vec2, const pvector<uint32_t> &components) {
+  const ShaderType::Vector *vec1_type = resolve_type(get_type_id(vec1))->as_vector();
+  const ShaderType::Vector *vec2_type = resolve_type(get_type_id(vec2))->as_vector();
+  nassertr(vec1_type != nullptr && vec2_type != nullptr, 0);
+  nassertr(vec1_type->get_scalar_type() == vec2_type->get_scalar_type(), 0);
+
+  const ShaderType *result_type = ShaderType::register_type(ShaderType::Vector(vec1_type->get_scalar_type(), components.size()));
+  uint32_t type_id = define_type(result_type);
+
+  uint32_t id = allocate_id();
+  _new_functions.insert(_new_functions.end(), {((5 + (uint32_t)components.size()) << spv::WordCountShift) | spv::OpVectorShuffle, type_id, id, vec1, vec2});
+  _new_functions.insert(_new_functions.end(), components.begin(), components.end());
+
+  Definition &def = _db.modify_definition(id);
+  def._type_id = type_id;
+  def._type = result_type;
+
+  mark_defined(id);
+  return id;
+}
+
+/**
+ * Constructs a composite with the given type from the given constituents.
+ */
+uint32_t SpirVTransformPass::
+op_composite_construct(const ShaderType *type, const pvector<uint32_t> &constituents) {
+  uint32_t type_id = define_type(type);
+
+  uint32_t id = allocate_id();
+  _new_functions.insert(_new_functions.end(), {((3 + (uint32_t)constituents.size()) << spv::WordCountShift) | spv::OpCompositeConstruct, type_id, id});
+  _new_functions.insert(_new_functions.end(), constituents.begin(), constituents.end());
+
+  Definition &def = _db.modify_definition(id);
+  def._type_id = type_id;
+  def._type = type;
+
+  mark_defined(id);
+  return id;
+}
+
 /**
 /**
  * Inserts an OpCompositeExtract.
  * Inserts an OpCompositeExtract.
  */
  */
@@ -1157,6 +1204,7 @@ op_composite_extract(uint32_t obj_id, std::initializer_list<uint32_t> chain) {
 
 
   Definition &def = _db.modify_definition(id);
   Definition &def = _db.modify_definition(id);
   def._type_id = type_id;
   def._type_id = type_id;
+  def._type = resolve_type(type_id);
 
 
   mark_defined(id);
   mark_defined(id);
   return id;
   return id;

+ 2 - 0
panda/src/shaderpipeline/spirVTransformPass.h

@@ -125,6 +125,8 @@ protected:
   uint32_t op_load(uint32_t var_id, spv::MemoryAccessMask access = spv::MemoryAccessMaskNone);
   uint32_t op_load(uint32_t var_id, spv::MemoryAccessMask access = spv::MemoryAccessMaskNone);
   uint32_t op_select(uint32_t cond, uint32_t obj1, uint32_t obj2);
   uint32_t op_select(uint32_t cond, uint32_t obj1, uint32_t obj2);
   uint32_t op_access_chain(uint32_t var_id, std::initializer_list<uint32_t>);
   uint32_t op_access_chain(uint32_t var_id, std::initializer_list<uint32_t>);
+  uint32_t op_vector_shuffle(uint32_t vec1, uint32_t vec2, const pvector<uint32_t> &components);
+  uint32_t op_composite_construct(const ShaderType *type, const pvector<uint32_t> &constituents);
   uint32_t op_composite_extract(uint32_t obj_id, std::initializer_list<uint32_t>);
   uint32_t op_composite_extract(uint32_t obj_id, std::initializer_list<uint32_t>);
 
 
   // The module is split into sections to make it easier to add instructions
   // The module is split into sections to make it easier to add instructions

+ 59 - 2
panda/src/shaderpipeline/spirVTransformer.cxx

@@ -190,10 +190,10 @@ assign_locations(ShaderModule::Stage stage) {
           continue;
           continue;
         }
         }
 
 
-        if (stage == ShaderModule::Stage::vertex && !input_locations.get_bit(0) &&
+        if (stage == ShaderModule::Stage::VERTEX && !input_locations.get_bit(0) &&
             def._name != "vertex" && def._name != "p3d_Vertex" &&
             def._name != "vertex" && def._name != "p3d_Vertex" &&
             def._name != "vtx_position") {
             def._name != "vtx_position") {
-          // Leave location 0 open for the vertex attribute.
+          // Leave location 0 open for the vertex position attribute.
           location = input_locations.find_off_range(num_locations, 1);
           location = input_locations.find_off_range(num_locations, 1);
         } else {
         } else {
           location = input_locations.find_off_range(num_locations);
           location = input_locations.find_off_range(num_locations);
@@ -279,6 +279,39 @@ assign_locations(pmap<uint32_t, int> remap) {
   }
   }
 }
 }
 
 
+/**
+ * Assigns procedural names consisting of a prefix followed by an index.
+ */
+void SpirVTransformer::
+assign_procedural_names(const char *prefix, const pmap<uint32_t, int> &suffixes) {
+  // Remove existing names matching theses ids.
+  auto it = _preamble.begin() + 5;
+  while (it != _preamble.end()) {
+    spv::Op opcode = (spv::Op)(*it & spv::OpCodeMask);
+    uint32_t wcount = *it >> spv::WordCountShift;
+    nassertd(wcount > 0) break;
+
+    if (opcode == spv::OpName && wcount >= 2) {
+      if (suffixes.count(*(it + 1))) {
+        it = _preamble.erase(it, it + wcount);
+        continue;
+      }
+    }
+
+    std::advance(it, wcount);
+  }
+
+  // Insert names before the annotations block.
+  uint32_t *words = (uint32_t *)alloca(sizeof(uint32_t) + strlen(prefix) + 32);
+  for (auto it = suffixes.begin(); it != suffixes.end(); ++it) {
+    words[1] = it->first;
+    int len = sprintf((char *)(words + 2), "%s%d", prefix, it->second);
+    uint32_t num_words = len / 4 + 3;
+    words[0] = spv::OpName | (num_words << spv::WordCountShift);
+    _preamble.insert(_preamble.end(), words, words + num_words);
+  }
+}
+
 /**
 /**
  * Removes location decorations from uniforms.
  * Removes location decorations from uniforms.
  */
  */
@@ -303,6 +336,30 @@ strip_uniform_locations() {
   }
   }
 }
 }
 
 
+/**
+ * Removes all binding and descriptor set decorations.
+ */
+void SpirVTransformer::
+strip_bindings() {
+  auto it = _annotations.begin();
+  while (it != _annotations.end()) {
+    spv::Op opcode = (spv::Op)(*it & spv::OpCodeMask);
+    uint32_t wcount = *it >> spv::WordCountShift;
+    nassertd(wcount > 0) break;
+
+    if (opcode == spv::OpDecorate && wcount >= 3) {
+      spv::Decoration decoration = (spv::Decoration)*(it + 2);
+      if (decoration == spv::DecorationBinding ||
+          decoration == spv::DecorationDescriptorSet) {
+        it = _annotations.erase(it, it + wcount);
+        continue;
+      }
+    }
+
+    std::advance(it, wcount);
+  }
+}
+
 /**
 /**
  * Assign descriptor bindings for a descriptor set based on the given ids.
  * Assign descriptor bindings for a descriptor set based on the given ids.
  * To create gaps in the descriptor set, entries in ids may be 0.
  * To create gaps in the descriptor set, entries in ids may be 0.

+ 2 - 0
panda/src/shaderpipeline/spirVTransformer.h

@@ -44,7 +44,9 @@ public:
 
 
   void assign_locations(ShaderModule::Stage stage);
   void assign_locations(ShaderModule::Stage stage);
   void assign_locations(pmap<uint32_t, int> locations);
   void assign_locations(pmap<uint32_t, int> locations);
+  void assign_procedural_names(const char *prefix, const pmap<uint32_t, int> &suffixes);
   void strip_uniform_locations();
   void strip_uniform_locations();
+  void strip_bindings();
   void bind_descriptor_set(uint32_t set, const pvector<uint32_t> &ids);
   void bind_descriptor_set(uint32_t set, const pvector<uint32_t> &ids);
 
 
 private:
 private:

+ 6 - 6
panda/src/vulkandisplay/vulkanGraphicsStateGuardian.cxx

@@ -1965,7 +1965,7 @@ release_shader(ShaderContext *context) {
 
 
   // According to the Vulkan spec, it is safe to delete a shader module even
   // According to the Vulkan spec, it is safe to delete a shader module even
   // if pipelines using it are still in use, so let's do it now.
   // if pipelines using it are still in use, so let's do it now.
-  for (size_t i = 0; i <= (size_t)Shader::Stage::compute; ++i) {
+  for (size_t i = 0; i <= (size_t)Shader::Stage::COMPUTE; ++i) {
     if (sc->_modules[i] != VK_NULL_HANDLE) {
     if (sc->_modules[i] != VK_NULL_HANDLE) {
       vkDestroyShaderModule(_device, sc->_modules[i], nullptr);
       vkDestroyShaderModule(_device, sc->_modules[i], nullptr);
       sc->_modules[i] = VK_NULL_HANDLE;
       sc->_modules[i] = VK_NULL_HANDLE;
@@ -2347,7 +2347,7 @@ set_state_and_transform(const RenderState *state,
   // Note that this set may be recreated by update_dynamic_uniforms, above.
   // Note that this set may be recreated by update_dynamic_uniforms, above.
   descriptor_sets[DS_dynamic_uniforms] = sc->_uniform_descriptor_set;
   descriptor_sets[DS_dynamic_uniforms] = sc->_uniform_descriptor_set;
 
 
-  bool is_compute = (sc->_modules[(size_t)Shader::Stage::compute] != VK_NULL_HANDLE);
+  bool is_compute = (sc->_modules[(size_t)Shader::Stage::COMPUTE] != VK_NULL_HANDLE);
   vkCmdBindDescriptorSets(_frame_data->_cmd, is_compute ? VK_PIPELINE_BIND_POINT_COMPUTE : VK_PIPELINE_BIND_POINT_GRAPHICS,
   vkCmdBindDescriptorSets(_frame_data->_cmd, is_compute ? VK_PIPELINE_BIND_POINT_COMPUTE : VK_PIPELINE_BIND_POINT_GRAPHICS,
                           sc->_pipeline_layout, 0, DS_SET_COUNT, descriptor_sets,
                           sc->_pipeline_layout, 0, DS_SET_COUNT, descriptor_sets,
                           num_offsets, &offset);
                           num_offsets, &offset);
@@ -3557,8 +3557,8 @@ make_pipeline(VulkanShaderContext *sc,
 
 
   PStatTimer timer(_make_pipeline_pcollector);
   PStatTimer timer(_make_pipeline_pcollector);
 
 
-  VkPipelineShaderStageCreateInfo stages[(size_t)Shader::Stage::compute + 1];
-  const VkShaderStageFlagBits stage_flags[(size_t)Shader::Stage::compute + 1] = {
+  VkPipelineShaderStageCreateInfo stages[(size_t)Shader::Stage::COMPUTE + 1];
+  const VkShaderStageFlagBits stage_flags[(size_t)Shader::Stage::COMPUTE + 1] = {
     VK_SHADER_STAGE_VERTEX_BIT,
     VK_SHADER_STAGE_VERTEX_BIT,
     VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
     VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT,
     VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
     VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT,
@@ -3568,7 +3568,7 @@ make_pipeline(VulkanShaderContext *sc,
   };
   };
   uint32_t num_stages = 0;
   uint32_t num_stages = 0;
 
 
-  for (size_t i = 0; i <= (size_t)Shader::Stage::compute; ++i) {
+  for (size_t i = 0; i <= (size_t)Shader::Stage::COMPUTE; ++i) {
     if (sc->_modules[i] != VK_NULL_HANDLE) {
     if (sc->_modules[i] != VK_NULL_HANDLE) {
       VkPipelineShaderStageCreateInfo &stage = stages[num_stages++];
       VkPipelineShaderStageCreateInfo &stage = stages[num_stages++];
       stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
       stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
@@ -4055,7 +4055,7 @@ make_compute_pipeline(VulkanShaderContext *sc) {
   pipeline_info.stage.pNext = nullptr;
   pipeline_info.stage.pNext = nullptr;
   pipeline_info.stage.flags = 0;
   pipeline_info.stage.flags = 0;
   pipeline_info.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
   pipeline_info.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
-  pipeline_info.stage.module = sc->_modules[(size_t)Shader::Stage::compute];
+  pipeline_info.stage.module = sc->_modules[(size_t)Shader::Stage::COMPUTE];
   pipeline_info.stage.pName = "main";
   pipeline_info.stage.pName = "main";
   pipeline_info.stage.pSpecializationInfo = nullptr;
   pipeline_info.stage.pSpecializationInfo = nullptr;
   pipeline_info.layout = sc->_pipeline_layout;
   pipeline_info.layout = sc->_pipeline_layout;

+ 6 - 6
panda/src/vulkandisplay/vulkanShaderContext.cxx

@@ -301,7 +301,7 @@ create_modules(VkDevice device, const ShaderType::Struct *push_constant_block_ty
   }
   }
 
 
   if (!success) {
   if (!success) {
-    for (size_t i = 0; i <= (size_t)Shader::Stage::compute; ++i) {
+    for (size_t i = 0; i <= (size_t)Shader::Stage::COMPUTE; ++i) {
       if (_modules[i] != VK_NULL_HANDLE) {
       if (_modules[i] != VK_NULL_HANDLE) {
         vkDestroyShaderModule(device, _modules[i], nullptr);
         vkDestroyShaderModule(device, _modules[i], nullptr);
         _modules[i] = VK_NULL_HANDLE;
         _modules[i] = VK_NULL_HANDLE;
@@ -371,7 +371,7 @@ r_extract_resources(const Shader::Parameter &param, const AccessChain &chain,
         (sampler->get_texture_type() == Texture::TT_buffer_texture)
         (sampler->get_texture_type() == Texture::TT_buffer_texture)
           ? VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER
           ? VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER
           : VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
           : VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
-      desc._access = ShaderType::Access::read_only;
+      desc._access = ShaderType::Access::READ_ONLY;
     }
     }
     else if (const ShaderType::Image *image = type->as_image()) {
     else if (const ShaderType::Image *image = type->as_image()) {
       desc._type =
       desc._type =
@@ -591,17 +591,17 @@ fetch_descriptor(VulkanGraphicsStateGuardian *gsg, const Descriptor &desc,
     write.pImageInfo = image_infos;
     write.pImageInfo = image_infos;
 
 
     for (ResourceId id : desc._resource_ids) {
     for (ResourceId id : desc._resource_ids) {
-      ShaderType::Access access = ShaderType::Access::read_write;
+      ShaderType::Access access = ShaderType::Access::READ_WRITE;
       int z = -1;
       int z = -1;
       int n = 0;
       int n = 0;
       PT(Texture) texture = desc._binding->fetch_texture_image(state, id, access, z, n);
       PT(Texture) texture = desc._binding->fetch_texture_image(state, id, access, z, n);
       access = access & desc._access;
       access = access & desc._access;
 
 
       VkAccessFlags access_mask = 0;
       VkAccessFlags access_mask = 0;
-      if ((access & ShaderType::Access::read_only) != ShaderType::Access::none) {
+      if ((access & ShaderType::Access::READ_ONLY) != ShaderType::Access::NONE) {
         access_mask |= VK_ACCESS_SHADER_READ_BIT;
         access_mask |= VK_ACCESS_SHADER_READ_BIT;
       }
       }
-      if ((access & ShaderType::Access::write_only) != ShaderType::Access::none) {
+      if ((access & ShaderType::Access::WRITE_ONLY) != ShaderType::Access::NONE) {
         access_mask |= VK_ACCESS_SHADER_WRITE_BIT;
         access_mask |= VK_ACCESS_SHADER_WRITE_BIT;
       }
       }
 
 
@@ -837,7 +837,7 @@ get_compute_pipeline(VulkanGraphicsStateGuardian *gsg) {
     return _compute_pipeline;
     return _compute_pipeline;
   }
   }
 
 
-  nassertr(_modules[(size_t)Shader::Stage::compute] != VK_NULL_HANDLE, VK_NULL_HANDLE);
+  nassertr(_modules[(size_t)Shader::Stage::COMPUTE] != VK_NULL_HANDLE, VK_NULL_HANDLE);
 
 
   VkPipeline pipeline = gsg->make_compute_pipeline(this);
   VkPipeline pipeline = gsg->make_compute_pipeline(this);
   _compute_pipeline = pipeline;
   _compute_pipeline = pipeline;

+ 2 - 2
panda/src/vulkandisplay/vulkanShaderContext.h

@@ -95,7 +95,7 @@ public:
   };
   };
 
 
 private:
 private:
-  VkShaderModule _modules[(size_t)Shader::Stage::compute + 1];
+  VkShaderModule _modules[(size_t)Shader::Stage::COMPUTE + 1];
   VkDescriptorSetLayout _tattr_descriptor_set_layout = VK_NULL_HANDLE;
   VkDescriptorSetLayout _tattr_descriptor_set_layout = VK_NULL_HANDLE;
   VkDescriptorSetLayout _sattr_descriptor_set_layout = VK_NULL_HANDLE;
   VkDescriptorSetLayout _sattr_descriptor_set_layout = VK_NULL_HANDLE;
   VkDescriptorSetLayout _dynamic_uniform_descriptor_set_layout = VK_NULL_HANDLE;
   VkDescriptorSetLayout _dynamic_uniform_descriptor_set_layout = VK_NULL_HANDLE;
@@ -133,7 +133,7 @@ private:
     PT(ShaderInputBinding) _binding;
     PT(ShaderInputBinding) _binding;
     small_vector<ResourceId, 1> _resource_ids;
     small_vector<ResourceId, 1> _resource_ids;
     int _stage_mask = 0;
     int _stage_mask = 0;
-    ShaderType::Access _access = ShaderType::Access::read_write;
+    ShaderType::Access _access = ShaderType::Access::READ_WRITE;
   };
   };
   pvector<Descriptor> _tex_stage_descriptors;
   pvector<Descriptor> _tex_stage_descriptors;
   size_t _num_tex_stage_descriptor_elements = 0;
   size_t _num_tex_stage_descriptor_elements = 0;

+ 28 - 4
tests/display/test_glsl_shader.py

@@ -435,7 +435,6 @@ def test_glsl_uimage(gsg):
 
 
 
 
 def test_glsl_ssbo(gsg):
 def test_glsl_ssbo(gsg):
-    return
     from struct import pack
     from struct import pack
     num1 = pack('<i', 1234567)
     num1 = pack('<i', 1234567)
     num2 = pack('<i', -1234567)
     num2 = pack('<i', -1234567)
@@ -466,8 +465,33 @@ def test_glsl_ssbo(gsg):
                   version=430)
                   version=430)
 
 
 
 
+def test_glsl_ssbo_array(gsg):
+    from struct import pack
+    dummy = pack('<i', 999999)
+    num1 = pack('<i', 1234567)
+    num2 = pack('<i', -1234567)
+    unused = core.ShaderBuffer("unused", dummy, core.GeomEnums.UH_static)
+    buffer1 = core.ShaderBuffer("buffer1", num1, core.GeomEnums.UH_static)
+    buffer2 = core.ShaderBuffer("buffer2", num2, core.GeomEnums.UH_static)
+
+    preamble = """
+    struct InsideStruct {
+        int value;
+    };
+    layout(std430, binding=0) buffer test {
+        readonly InsideStruct inside[1];
+    } test_ns[3][1];
+    """
+    code = """
+    assert(test_ns[1][0].inside[0].value == 1234567);
+    assert(test_ns[2][0].inside[0].value == -1234567);
+    """
+    run_glsl_test(gsg, code, preamble,
+                  {'test[0][0]': unused, 'test[1][0]': buffer1, 'test[2][0]': buffer2},
+                  version=430)
+
+
 def test_glsl_ssbo_runtime_length(gsg):
 def test_glsl_ssbo_runtime_length(gsg):
-    return
     from struct import pack
     from struct import pack
     nums = pack('<ii', 1234, 5678)
     nums = pack('<ii', 1234, 5678)
     ssbo = core.ShaderBuffer("ssbo", nums, core.GeomEnums.UH_static)
     ssbo = core.ShaderBuffer("ssbo", nums, core.GeomEnums.UH_static)
@@ -1423,9 +1447,9 @@ def test_glsl_state_texture(gsg):
     assert(texture(p3d_TextureAdd[2], coord) == vec4(0.0, 0.0, 0.0, 1.0));
     assert(texture(p3d_TextureAdd[2], coord) == vec4(0.0, 0.0, 0.0, 1.0));
     assert(abs(texture(p3d_TextureNormal[0], coord).r - 4.0 / 255.0) < 0.001);
     assert(abs(texture(p3d_TextureNormal[0], coord).r - 4.0 / 255.0) < 0.001);
     assert(abs(texture(p3d_TextureNormal[1], coord).r - 6.0 / 255.0) < 0.001);
     assert(abs(texture(p3d_TextureNormal[1], coord).r - 6.0 / 255.0) < 0.001);
-    assert(texture(p3d_TextureNormal[2], coord) == vec4(127 / 255.0, 127 / 255.0, 1.0, 0.0));
+    assert(all(lessThan(abs(texture(p3d_TextureNormal[2], coord) - vec4(127 / 255.0, 127 / 255.0, 1.0, 0.0)), vec4(0.004))));
     assert(texture(p3d_TextureHeight[0], coord).r == 4.0 / 255.0);
     assert(texture(p3d_TextureHeight[0], coord).r == 4.0 / 255.0);
-    assert(texture(p3d_TextureHeight[1], coord) == vec4(127 / 255.0, 127 / 255.0, 1.0, 0.0));
+    assert(all(lessThan(abs(texture(p3d_TextureHeight[1], coord) - vec4(127 / 255.0, 127 / 255.0, 1.0, 0.0)), vec4(0.004))));
     """
     """
 
 
     run_glsl_test(gsg, code, preamble, state=np.get_state())
     run_glsl_test(gsg, code, preamble, state=np.get_state())