Browse Source

Merge branch 'master' into shaderpipeline

rdb 1 year ago
parent
commit
2baa591ea5

+ 1 - 1
README.md

@@ -168,7 +168,7 @@ Install the appropriate package for it (such as `python37` or `python38`) and
 run the makepanda script with your chosen Python version:
 
 ```bash
-python3.7 makepanda/makepanda.py --everything --installer --no-egl --no-gles --no-gles2
+python3.11 makepanda/makepanda.py --everything --installer --no-egl --no-gles --no-gles2
 ```
 
 If successful, this will produce a .pkg file in the root of the source

+ 4 - 5
panda/src/display/graphicsEngine.I

@@ -164,11 +164,10 @@ make_parasite(GraphicsOutput *host, const std::string &name,
 }
 
 /**
- * Deprecated variant of dispatch_compute() which takes only a ShaderAttrib
- * instead of a whole RenderState.
+ * Version of dispatch_compute that takes a ShaderAttrib instead of a full
+ * RenderState.
  */
-void GraphicsEngine::
+INLINE void GraphicsEngine::
 dispatch_compute(const LVecBase3i &work_groups, const ShaderAttrib *sattr, GraphicsStateGuardian *gsg) {
-  CPT(RenderState) state = RenderState::make(sattr);
-  dispatch_compute(work_groups, state, gsg);
+  dispatch_compute(work_groups, RenderState::make(sattr), gsg);
 }

+ 2 - 1
panda/src/display/graphicsEngine.cxx

@@ -1187,7 +1187,8 @@ extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg) {
 void GraphicsEngine::
 dispatch_compute(const LVecBase3i &work_groups, const RenderState *state, GraphicsStateGuardian *gsg) {
   const ShaderAttrib *sattr;
-  state->get_attrib_def(sattr);
+  DCAST_INTO_V(sattr, state->get_attrib(ShaderAttrib::get_class_slot()));
+
   const Shader *shader = sattr->get_shader();
   nassertv(shader != nullptr);
   nassertv(gsg != nullptr);

+ 1 - 0
panda/src/display/graphicsEngine.h

@@ -32,6 +32,7 @@
 #include "indirectLess.h"
 #include "loader.h"
 #include "referenceCount.h"
+#include "renderState.h"
 
 class Pipeline;
 class DisplayRegion;

+ 515 - 72
panda/src/display/graphicsStateGuardian.cxx

@@ -892,8 +892,10 @@ update_shader_matrix_cache(Shader *shader, LVecBase4 *cache, int altered) {
 /**
  * The gsg contains a large number of useful matrices:
  *
- * * the world transform, * the modelview matrix, * the cs_transform, * etc,
- * etc.
+ * - the world transform,
+ * - the modelview matrix,
+ * - the cs_transform,
+ * - etc, etc.
  *
  * A shader can request any of these values, and furthermore, it can request
  * that various compositions, inverses, and transposes be performed.  The
@@ -903,65 +905,419 @@ update_shader_matrix_cache(Shader *shader, LVecBase4 *cache, int altered) {
  *
  * Some values, like the following, aren't matrices:
  *
- * * window size * texture coordinates of card center
+ * - window size
+ * - texture coordinates of card center
  *
  * This routine can fetch these values as well, by shoehorning them into a
  * matrix.  In this way, we avoid the need for a separate routine to fetch
  * these values.
- *
- * The "altered" bits indicate what parts of the state_and_transform have
- * changed since the last time this particular ShaderMatSpec was evaluated.
- * This may allow data to be cached and not reevaluated.
- *
  */
-const LVecBase4 *GraphicsStateGuardian::
-fetch_specified_value(Shader::ShaderMatSpec &spec, const LVecBase4 *cache, int altered) {
+const void *GraphicsStateGuardian::
+fetch_specified_value(Shader::ShaderMatSpec &spec, const LVecBase4 *cache,
+                      LVecBase4 *scratch, bool pad_rows) {
   LVecBase3 v;
+  LMatrix4 m;
 
   const LVecBase4 *cache0 = cache + spec._cache_offset[0];
   const LVecBase4 *cache1 = cache + spec._cache_offset[1];
 
   switch (spec._func) {
   case Shader::SMF_first:
-    return cache0;
+#ifdef STDFLOAT_DOUBLE
+    if (spec._scalar_type == ShaderType::ST_double) {
+      if (pad_rows || spec._num_cols == 4) {
+        // No padding or conversion needed
+        return (const double *)cache0->get_data() + spec._offset;
+      } else {
+        double *data = (double *)scratch;
+        const double *from_data = cache0->get_data();
+        from_data += spec._offset;
+
+        for (int i = 0; i < spec._array_count * spec._num_rows; ++i) {
+          for (int c = 0; c < spec._num_cols; ++c) {
+            *data++ = from_data[c];
+          }
+          from_data += 4;
+        }
+      }
+    }
+    else if (spec._scalar_type == ShaderType::ST_float) {
+      float *data = (float *)scratch;
+      const double *from_data = cache0->get_data();
+      from_data += spec._offset;
+
+      if (pad_rows || spec._num_cols == 4) {
+        for (size_t i = 0; i < (size_t)spec._array_count * (size_t)spec._num_rows * 4; ++i) {
+          data[i] = (float)from_data[i];
+        }
+        return data;
+      } else {
+        for (int i = 0; i < spec._array_count * spec._num_rows; ++i) {
+          for (int c = 0; c < spec._num_cols; ++c) {
+            *data++ = (float)from_data[c];
+          }
+          from_data += 4;
+        }
+      }
+      return (float *)scratch;
+    }
+#else
+    if (spec._scalar_type == ShaderType::ST_double) {
+      double *data = (double *)scratch;
+      const float *from_data = cache0->get_data();
+      from_data += spec._offset;
+
+      if (pad_rows || spec._num_cols == 4) {
+        for (size_t i = 0; i < (size_t)spec._array_count * (size_t)spec._num_rows * 4; ++i) {
+          data[i] = (double)from_data[i];
+        }
+        return data;
+      } else {
+        for (int i = 0; i < spec._array_count * spec._num_rows; ++i) {
+          for (int c = 0; c < spec._num_cols; ++c) {
+            *data++ = (double)from_data[c];
+          }
+          from_data += 4;
+        }
+      }
+      return data;
+    }
+#endif
+    if (pad_rows || spec._num_cols == 4 || (spec._num_rows == 1 && spec._array_count == 1)) {
+      // No padding or conversion needed
+      return (const uint32_t *)cache0->get_data() + spec._offset;
+    } else {
+      uint32_t *data = (uint32_t *)scratch;
+      const uint32_t *from_data = (const uint32_t *)cache0->get_data();
+      from_data += spec._offset;
+
+      for (int i = 0; i < spec._array_count * spec._num_rows; ++i) {
+        for (int c = 0; c < spec._num_cols; ++c) {
+          *data++ = from_data[c];
+        }
+        from_data += 4;
+      }
+    }
+    break;
 
   case Shader::SMF_compose:
-    spec._value.multiply(*(LMatrix4 *)cache0, *(LMatrix4 *)cache1);
-    return (LVecBase4 *)&spec._value;
+    m.multiply(*(LMatrix4 *)cache0, *(LMatrix4 *)cache1);
+    break;
 
   case Shader::SMF_transform_dlight:
-    spec._value = *(LMatrix4 *)cache0;
+    m = *(LMatrix4 *)cache0;
     v = (*(LMatrix4 *)cache1).xform_vec(cache0[2].get_xyz());
     v.normalize();
-    spec._value.set_row(2, v);
+    m.set_row(2, v);
     v = (*(LMatrix4 *)cache1).xform_vec(cache0[3].get_xyz());
     v.normalize();
-    spec._value.set_row(3, v);
-    return (LVecBase4 *)&spec._value;
+    m.set_row(3, v);
+    break;
 
   case Shader::SMF_transform_plight:
     {
       // Careful not to touch the w component, which contains the near value.
-      spec._value = *(LMatrix4 *)cache0;
+      m = *(LMatrix4 *)cache0;
       LPoint3 point = (*(LMatrix4 *)cache1).xform_point(cache0[2].get_xyz());
-      spec._value(2, 0) = point[0];
-      spec._value(2, 1) = point[1];
-      spec._value(2, 2) = point[2];
-      return (LVecBase4 *)&spec._value;
+      m(2, 0) = point[0];
+      m(2, 1) = point[1];
+      m(2, 2) = point[2];
+      break;
     }
 
   case Shader::SMF_transform_slight:
-    spec._value = *(LMatrix4 *)cache0;
-    spec._value.set_row(2, (*(LMatrix4 *)cache1).xform_point(cache0[2].get_xyz()));
+    m = *(LMatrix4 *)cache0;
+    m.set_row(2, (*(LMatrix4 *)cache1).xform_point(cache0[2].get_xyz()));
     v = (*(LMatrix4 *)cache1).xform_vec(cache0[3].get_xyz());
     v.normalize();
-    spec._value.set_row(3, v);
-    return (LVecBase4 *)&spec._value;
+    m.set_row(3, v);
+    break;
+
+  case Shader::SMF_shader_input_ptr:
+    return fetch_ptr_parameter(spec, scratch, pad_rows);
+
+  default:
+    // Should never get here
+    m = LMatrix4::ident_mat();
+    break;
+  }
+
+  if (spec._scalar_type == ShaderType::ST_double) {
+    *(LMatrix4d *)scratch = LCAST(double, m);
+    return (const double *)scratch + spec._offset;
+  } else {
+    *(LMatrix4f *)scratch = LCAST(float, m);
+    return (const float *)scratch + spec._offset;
+  }
+}
+
+/**
+ * Fetches a numeric shader input, doing conversion as necessary using the
+ * given amount of scratch space.
+ */
+const void *GraphicsStateGuardian::
+fetch_ptr_parameter(Shader::ShaderMatSpec &spec, LVecBase4 *scratch, bool pad_rows) {
+  Shader::ShaderPtrData ptr_data;
+  if (!_target_shader->get_shader_input_ptr(spec._arg[0], ptr_data)) {
+    return nullptr;
+  }
+
+  int total_rows = std::min(spec._array_count * spec._num_rows, (int)ptr_data._size / spec._num_cols);
+  if (total_rows == 1) {
+    pad_rows = false;
+  }
+  switch (spec._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 || spec._num_cols == 4) {
+          for (int i = 0; i < total_rows * spec._num_cols; ++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 < spec._num_cols; ++c) {
+              data[i * 4 + c] = (float)*from_data++;
+            }
+          }
+        }
+        return data;
+
+      case ShaderType::ST_uint:
+        // Convert unsigned int data to float data.
+        if (!pad_rows || spec._num_cols == 4) {
+          for (int i = 0; i < total_rows * spec._num_cols; ++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 < spec._num_cols; ++c) {
+              data[i * 4 + c] = (float)*from_data++;
+            }
+          }
+        }
+        return data;
+
+      case ShaderType::ST_double:
+        // Downgrade double data to float data.
+        if (!pad_rows || spec._num_cols == 4) {
+          for (int i = 0; i < total_rows * spec._num_cols; ++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 < spec._num_cols; ++c) {
+              data[i * 4 + c] = (float)*from_data++;
+            }
+          }
+        }
+        return data;
+
+      case ShaderType::ST_float:
+        if (!pad_rows || spec._num_cols == 4) {
+          // No conversion needed.
+          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 < spec._num_cols; ++c) {
+              data[i * 4 + c] = (float)*from_data++;
+            }
+          }
+        }
+        return data;
+
+      default:
+#ifndef NDEBUG
+        display_cat.error()
+          << "Invalid ShaderPtrData type " << (int)ptr_data._type
+          << " for shader input '" << spec._id._name << "'\n";
+#endif
+        return nullptr;
+      }
+
+      return data;
+    }
+    break;
+
+  case ShaderType::ST_int:
+    if (ptr_data._type != ShaderType::ST_int &&
+        ptr_data._type != ShaderType::ST_uint) {
+      display_cat.error()
+        << "Cannot pass floating-point data to integer shader input '" << spec._id._name << "'\n";
+
+      // Deactivate it to make sure the user doesn't get flooded with this
+      // error.
+      spec._dep = 0;
+
+    } else {
+      return ptr_data._ptr;
+    }
+    break;
+
+  case ShaderType::ST_uint:
+    if (ptr_data._type != ShaderType::ST_uint &&
+        ptr_data._type != ShaderType::ST_int) {
+      display_cat.error()
+        << "Cannot pass floating-point data to integer shader input '" << spec._id._name << "'\n";
+
+      // Deactivate it to make sure the user doesn't get flooded with this
+      // error.
+      spec._dep = 0;
+      return nullptr;
+
+    } 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 || spec._num_cols == 4) {
+          for (int i = 0; i < total_rows * spec._num_cols; ++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 < spec._num_cols; ++c) {
+              data[i * 4 + c] = (double)*from_data++;
+            }
+          }
+        }
+        return data;
+
+      case ShaderType::ST_uint:
+        // Convert int data to double data.
+        if (!pad_rows || spec._num_cols == 4) {
+          for (int i = 0; i < total_rows * spec._num_cols; ++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 < spec._num_cols; ++c) {
+              data[i * 4 + c] = (double)*from_data++;
+            }
+          }
+        }
+        return data;
+
+      case ShaderType::ST_double:
+        if (!pad_rows || spec._num_cols == 4) {
+          // No conversion needed.
+          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 < spec._num_cols; ++c) {
+              data[i * 4 + c] = (double)*from_data++;
+            }
+          }
+        }
+        return data;
+
+      case ShaderType::ST_float:
+        // Upgrade float data to double data.
+        if (!pad_rows || spec._num_cols == 4) {
+          for (int i = 0; i < total_rows * spec._num_cols; ++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 < spec._num_cols; ++c) {
+              data[i * 4 + c] = (double)*from_data++;
+            }
+          }
+        }
+        return data;
+
+      default:
+  #ifndef NDEBUG
+        display_cat.error()
+          << "Invalid ShaderPtrData type " << (int)ptr_data._type
+          << " for shader input '" << spec._id._name << "'\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 || spec._num_cols == 4) {
+          // No conversion needed.
+          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 < spec._num_cols; ++c) {
+              data[i * 4 + c] = (*from_data++) != 0;
+            }
+          }
+        }
+        return data;
+
+      case ShaderType::ST_double:
+        if (!pad_rows || spec._num_cols == 4) {
+          for (int i = 0; i < total_rows * spec._num_cols; ++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 < spec._num_cols; ++c) {
+              data[i * 4 + c] = (*from_data++) != 0.0;
+            }
+          }
+        }
+        return data;
+
+      case ShaderType::ST_float:
+        if (!pad_rows || spec._num_cols == 4) {
+          for (int i = 0; i < total_rows * spec._num_cols; ++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 < spec._num_cols; ++c) {
+              data[i * 4 + c] = (*from_data++) != 0.0f;
+            }
+          }
+        }
+        return data;
+      }
+    }
+    break;
+
+  case ShaderType::ST_unknown:
+    break;
   }
 
-  // Should never get here
-  spec._value = LMatrix4::ident_mat();
-  return (LVecBase4 *)&spec._value;
+  return nullptr;
 }
 
 /**
@@ -1026,32 +1382,23 @@ fetch_specified_part(Shader::ShaderMatInput part, const InternalName *name,
       _target_rs->get_attrib_def(MaterialAttrib::get_class_slot());
     // Material matrix contains AMBIENT, DIFFUSE, EMISSION, SPECULAR+SHININESS
     if (target_material->is_off()) {
-      into[0].set(1, 1, 1, 1);
-      into[1].set(1, 1, 1, 1);
-      into[2].set(0, 0, 0, 0);
-      into[3].set(0, 0, 0, 0);
+      into[Shader::MA_ambient].set(1, 1, 1, 1);
+      into[Shader::MA_diffuse].set(1, 1, 1, 1);
+      into[Shader::MA_emission].set(0, 0, 0, 0);
+      into[Shader::MA_specular].set(0, 0, 0, 0);
+      into[Shader::MA_base_color].set(0, 0, 0, 0);
+      into[Shader::MA_metallic_ior_roughness].set(0, 0, 0, 1);
       return;
     }
     Material *m = target_material->get_material();
     LVecBase4 spc = m->get_specular();
     spc[3] = m->get_shininess();
-    into[0] = m->get_ambient();
-    into[1] = m->get_diffuse();
-    into[2] = m->get_emission();
-    into[3] = spc;
-    return;
-  }
-  case Shader::SMO_attr_material2: {
-    const MaterialAttrib *target_material = (const MaterialAttrib *)
-      _target_rs->get_attrib_def(MaterialAttrib::get_class_slot());
-    if (target_material->is_off()) {
-      into[0].set(0, 0, 0, 0);
-      into[1].set(0, 0, 0, 1);
-      return;
-    }
-    Material *m = target_material->get_material();
-    into[0] = m->get_base_color();
-    into[1].set(m->get_metallic(), m->get_refractive_index(), 0, m->get_roughness());
+    into[Shader::MA_ambient] = m->get_ambient();
+    into[Shader::MA_diffuse] = m->get_diffuse();
+    into[Shader::MA_emission] = m->get_emission();
+    into[Shader::MA_specular] = spc;
+    into[Shader::MA_base_color] = m->get_base_color();
+    into[Shader::MA_metallic_ior_roughness].set(m->get_metallic(), m->get_refractive_index(), 0, m->get_roughness());
     return;
   }
   case Shader::SMO_attr_color: {
@@ -1080,22 +1427,13 @@ fetch_specified_part(Shader::ShaderMatInput part, const InternalName *name,
     Fog *fog = target_fog->get_fog();
     if (fog == nullptr) {
       into[0].set(0, 1, 1, 1);
+      into[1].set(1, 1, 1, 1);
       return;
     }
     PN_stdfloat start, end;
     fog->get_linear_range(start, end);
     into[0].set(fog->get_exp_density(), start, end, 1.0f / (end - start));
-    return;
-  }
-  case Shader::SMO_attr_fogcolor: {
-    const FogAttrib *target_fog = (const FogAttrib *)
-      _target_rs->get_attrib_def(FogAttrib::get_class_slot());
-    Fog *fog = target_fog->get_fog();
-    if (fog == nullptr) {
-      into[0].set(1, 1, 1, 1);
-      return;
-    }
-    into[0] = fog->get_color();
+    into[1] = fog->get_color();
     return;
   }
   case Shader::SMO_alight_x: {
@@ -1570,7 +1908,7 @@ fetch_specified_part(Shader::ShaderMatInput part, const InternalName *name,
     fetch_specified_member(np, name->get_basename(), into[0]);
     return;
   }
-  case Shader::SMO_light_source_i_vec_attrib: {
+  case Shader::SMO_light_source_i: {
     const LightAttrib *target_light;
     _target_rs->get_attrib_def(target_light);
 
@@ -1582,17 +1920,17 @@ fetch_specified_part(Shader::ShaderMatInput part, const InternalName *name,
     for (i = 0; i < num_lights; ++i) {
       NodePath light = target_light->get_on_light(i);
       nassertv(!light.is_empty());
-      fetch_specified_member(light, name, into[i]);
+      fetch_specified_light(light, into);
+      into += Shader::LA_COUNT;
     }
     // Apply the default OpenGL lights otherwise.
     // Special exception for light 0, which defaults to white.
-    if (i == 0) {
-      //FIXME: only the color attribute
-      into[0].set(1, 1, 1, 1);
-      ++i;
-    }
     for (; i < (size_t)count; ++i) {
-      fetch_specified_member(NodePath(), name, into[i]);
+      fetch_specified_light(NodePath(), into);
+      if (i == 0) {
+        into[Shader::LA_color].set(1, 1, 1, 1);
+      }
+      into += Shader::LA_COUNT;
     }
     return;
   }
@@ -1631,7 +1969,7 @@ fetch_specified_part(Shader::ShaderMatInput part, const InternalName *name,
     return;
   }
   case Shader::SMO_light_source_i_packed: {
-    // The light matrix contains COLOR, ATTENUATION, POSITION, VIEWVECTOR
+    // The light matrix contains COLOR, ATTENUATION, VIEWVECTOR, POSITION
     const LightAttrib *target_light;
     _target_rs->get_attrib_def(target_light);
 
@@ -1682,7 +2020,7 @@ fetch_specified_part(Shader::ShaderMatInput part, const InternalName *name,
         }
       }
 
-      into += i * 4;
+      into += 4;
     }
     // Apply the default OpenGL lights otherwise.
     // Special exception for light 0, which defaults to white.
@@ -1698,7 +2036,7 @@ fetch_specified_part(Shader::ShaderMatInput part, const InternalName *name,
       into[1].set(1, 0, 0, 0);
       into[2].set(0, 0, 0, 0);
       into[3].set(0, 0, 0, 0);
-      into += i * 4;
+      into += 4;
     }
     return;
   }
@@ -1990,6 +2328,111 @@ fetch_specified_member(const NodePath &np, CPT_InternalName attrib, LVecBase4 &v
   }
 }
 
+/**
+ * Given a NodePath passed into a shader input that is a structure, fetches
+ * the value for the given member.
+ */
+void GraphicsStateGuardian::
+fetch_specified_light(const NodePath &np, LVecBase4 *into) {
+  PandaNode *node = nullptr;
+  if (!np.is_empty()) {
+    node = np.node();
+  }
+
+  if (node == nullptr) {
+    into[Shader::LA_color].set(0, 0, 0, 1);
+    into[Shader::LA_specular].set(0, 0, 0, 1);
+    into[Shader::LA_ambient].set(0, 0, 0, 1);
+    into[Shader::LA_diffuse].set(0, 0, 0, 1);
+    into[Shader::LA_position].set(0, 0, 1, 0);
+    into[Shader::LA_half_vector].set(0, 0, 1, 0);
+    into[Shader::LA_spot_direction].set(0, 0, -1, 0);
+    into[Shader::LA_spot_params].set(-1, 180, 0, 0);
+    into[Shader::LA_attenuation].set(1, 0, 0, 0);
+    *(LMatrix4 *)&into[Shader::LA_shadow_view_matrix] = shadow_bias_mat;
+  } else {
+    Light *light = node->as_light();
+    nassertv(light != nullptr);
+
+    LVecBase4 color = light->get_color();
+    into[Shader::LA_color] = color;
+    into[Shader::LA_specular] = light->get_specular_color();
+
+    if (node->is_ambient_light()) {
+      into[Shader::LA_ambient] = color;
+      into[Shader::LA_diffuse].set(0, 0, 0, 1);
+      into[Shader::LA_position].set(0, 0, 0, 0);
+      into[Shader::LA_half_vector].set(0, 0, 0, 0);
+      into[Shader::LA_spot_direction].set(0, 0, 0, 0);
+      into[Shader::LA_spot_params].set(-1, 180, 0, 0);
+    } else {
+      into[Shader::LA_ambient].set(0, 0, 0, 1);
+      into[Shader::LA_diffuse] = color;
+
+      CPT(TransformState) net_transform =
+        np.get_transform(_scene_setup->get_scene_root().get_parent());
+      CPT(TransformState) transform =
+        _scene_setup->get_cs_world_transform()->compose(net_transform);
+      const LMatrix4 &light_mat = transform->get_mat();
+
+      LightLensNode *light;
+      DCAST_INTO_V(light, node);
+      Lens *lens = light->get_lens();
+      nassertv(lens != nullptr);
+
+      if (node->is_of_type(DirectionalLight::get_class_type())) {
+        DirectionalLight *light;
+        DCAST_INTO_V(light, node);
+
+        LVector3 dir = -(light->get_direction() * light_mat);
+        into[Shader::LA_position].set(dir[0], dir[1], dir[2], 0);
+
+        dir.normalize();
+        dir += LVector3(0, 0, 1);
+        dir.normalize();
+        into[Shader::LA_half_vector].set(dir[0], dir[1], dir[2], 1);
+      }
+      else {
+        LPoint3 pos = lens->get_nodal_point() * light_mat;
+        into[Shader::LA_position].set(pos[0], pos[1], pos[2], 1);
+
+        pos.normalize();
+        pos += LVector3(0, 0, 1);
+        pos.normalize();
+        into[Shader::LA_half_vector].set(pos[0], pos[1], pos[2], 1);
+      }
+
+      if (node->is_of_type(Spotlight::get_class_type())) {
+        float cutoff = lens->get_hfov() * 0.5f;
+        into[Shader::LA_spot_params].set(ccos(deg_2_rad(cutoff)), cutoff, light->get_exponent(), 0);
+      } else {
+        // spotCosCutoff, spotCutoff, spotExponent
+        into[Shader::LA_spot_params].set(-1, 180, light->get_exponent(), 0);
+      }
+
+      LVector3 dir = lens->get_view_vector() * light_mat;
+      into[Shader::LA_spot_direction].set(dir[0], dir[1], dir[2], 0);
+
+      LMatrix4 t = _inv_cs_transform->get_mat() *
+        _scene_setup->get_camera_transform()->get_mat() *
+        net_transform->get_inverse()->get_mat() *
+        LMatrix4::convert_mat(_coordinate_system, lens->get_coordinate_system());
+
+      if (!node->is_of_type(PointLight::get_class_type())) {
+        t *= lens->get_projection_mat() * shadow_bias_mat;
+      }
+      *(LMatrix4 *)&into[Shader::LA_shadow_view_matrix] = t;
+    }
+
+    LVecBase3 atten = light->get_attenuation();
+    PN_stdfloat radius = 0;
+    if (node->is_of_type(SphereLight::get_class_type())) {
+      radius = ((const SphereLight *)node)->get_radius();
+    }
+    into[Shader::LA_attenuation].set(atten[0], atten[1], atten[2], radius);
+  }
+}
+
 /**
  * Like fetch_specified_value, but for texture inputs.
  */

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

@@ -339,11 +339,14 @@ public:
   virtual void clear(DrawableRegion *clearable);
 
   void update_shader_matrix_cache(Shader *shader, LVecBase4 *cache, int altered);
-  const LVecBase4 *fetch_specified_value(Shader::ShaderMatSpec &spec, const LVecBase4 *cache, int altered);
+  const void *fetch_specified_value(Shader::ShaderMatSpec &spec, const LVecBase4 *cache,
+                                    LVecBase4 *scratch, bool pad_rows);
+  const void *fetch_ptr_parameter(Shader::ShaderMatSpec &spec, LVecBase4 *scratch, bool pad_rows);
   void fetch_specified_part(Shader::ShaderMatInput input, const InternalName *name,
                             LVecBase4 *into, int count = 1);
   void fetch_specified_member(const NodePath &np, CPT_InternalName member,
                               LVecBase4 &v);
+  void fetch_specified_light(const NodePath &np, LVecBase4 *into);
   PT(Texture) fetch_specified_texture(Shader::ShaderTexSpec &spec,
                                       SamplerState &sampler, int &view);
   const Shader::ShaderPtrData *fetch_ptr_parameter(const Shader::ShaderPtrSpec& spec);

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

@@ -55,47 +55,7 @@ DXShaderContext9(Shader *s, GSG *gsg) : ShaderContext(s) {
   }
 
   _mat_part_cache = new LVecBase4[s->cp_get_mat_cache_size()];
-
-  // Determine the size of the scratch space to allocate on the stack inside
-  // issue_parameters().
-  for (const Shader::ShaderPtrSpec &spec : _shader->_ptr_spec) {
-    if (spec._id._location < 0 || (size_t)spec._id._location >= _register_map.size()) {
-      continue;
-    }
-
-    ConstantRegister &reg = _register_map[spec._id._location];
-    if (reg.count == 0) {
-      continue;
-    }
-
-    size_t num_cols = spec._dim[2];
-    if (num_cols == 0) {
-      continue;
-    }
-    size_t size = (size_t)spec._dim[0] * (size_t)spec._dim[1];
-    size *= 16;
-    if (size > _scratch_space_size) {
-      _scratch_space_size = size;
-    }
-  }
-
-  for (const Shader::ShaderMatSpec &spec : _shader->_mat_spec) {
-    if (spec._id._location < 0 || (size_t)spec._id._location >= _register_map.size()) {
-      continue;
-    }
-
-    ConstantRegister &reg = _register_map[spec._id._location];
-    if (reg.count == 0) {
-      continue;
-    }
-    if (reg.set == D3DXRS_FLOAT4) {
-      size_t size = spec._array_count * std::max(spec._size, 4);
-      size *= 4;
-      if (size > _scratch_space_size) {
-        _scratch_space_size = size;
-      }
-    }
-  }
+  _mat_scratch_space = new LVecBase4[_shader->cp_get_mat_scratch_size(true)];
 }
 
 /**
@@ -106,6 +66,7 @@ DXShaderContext9::
   release_resources();
 
   delete[] _mat_part_cache;
+  delete[] _mat_scratch_space;
 }
 
 /**
@@ -493,287 +454,12 @@ issue_parameters(GSG *gsg, int altered) {
     return;
   }
 
-  float *scratch = (float *)alloca(_scratch_space_size);
-
   LPDIRECT3DDEVICE9 device = gsg->_d3d_device;
 
-  // We have no way to track modifications to PTAs, so we assume that they are
-  // modified every frame and when we switch ShaderAttribs.
-  if (altered & (Shader::SSD_shaderinputs | Shader::SSD_frame)) {
-    // Iterate through _ptr parameters
-    for (const Shader::ShaderPtrSpec &spec : _shader->_ptr_spec) {
-      Shader::ShaderPtrData ptr_data;
-      if (!gsg->fetch_ptr_parameter(spec, ptr_data)) { //the input is not contained in ShaderPtrData
-        release_resources();
-        return;
-      }
-      if (spec._id._location < 0 || (size_t)spec._id._location >= _register_map.size()) {
-        continue;
-      }
-
-      ConstantRegister &reg = _register_map[spec._id._location];
-      if (reg.count == 0) {
-        continue;
-      }
-
-      // Calculate how many elements to transfer; no more than it expects,
-      // but certainly no more than we have.
-      size_t num_cols = spec._dim[2];
-      if (num_cols == 0) {
-        continue;
-      }
-      size_t num_rows = std::min((size_t)spec._dim[0] * (size_t)spec._dim[1],
-                                 (size_t)(ptr_data._size / num_cols));
-
-      switch (reg.set) {
-      case D3DXRS_BOOL:
-        {
-          BOOL *data = (BOOL *)scratch;
-          memset(data, 0, sizeof(BOOL) * 4 * num_rows);
-          switch (ptr_data._type) {
-          case ShaderType::ST_int:
-          case ShaderType::ST_uint:
-          case ShaderType::ST_float:
-            // All have the same 0-representation.
-            if (num_cols == 1) {
-              for (size_t i = 0; i < num_rows; ++i) {
-                data[i * 4] = ((int *)ptr_data._ptr)[i] != 0;
-              }
-            } else if (num_cols == 2) {
-              for (size_t i = 0; i < num_rows; ++i) {
-                data[i * 4 + 0] = ((int *)ptr_data._ptr)[i * 2] != 0;
-                data[i * 4 + 1] = ((int *)ptr_data._ptr)[i * 2 + 1] != 0;
-              }
-            } else if (num_cols == 3) {
-              for (size_t i = 0; i < num_rows; ++i) {
-                data[i * 4 + 0] = ((int *)ptr_data._ptr)[i * 3] != 0;
-                data[i * 4 + 1] = ((int *)ptr_data._ptr)[i * 3 + 1] != 0;
-                data[i * 4 + 2] = ((int *)ptr_data._ptr)[i * 3 + 2] != 0;
-              }
-            } else {
-              for (size_t i = 0; i < num_rows * 4; ++i) {
-                data[i] = ((int *)ptr_data._ptr)[i] != 0;
-              }
-            }
-            break;
-
-          case ShaderType::ST_double:
-            if (num_cols == 1) {
-              for (size_t i = 0; i < num_rows; ++i) {
-                data[i * 4] = ((double *)ptr_data._ptr)[i] != 0;
-              }
-            } else if (num_cols == 2) {
-              for (size_t i = 0; i < num_rows; ++i) {
-                data[i * 4 + 0] = ((double *)ptr_data._ptr)[i * 2] != 0;
-                data[i * 4 + 1] = ((double *)ptr_data._ptr)[i * 2 + 1] != 0;
-              }
-            } else if (num_cols == 3) {
-              for (size_t i = 0; i < num_rows; ++i) {
-                data[i * 4 + 0] = ((double *)ptr_data._ptr)[i * 3] != 0;
-                data[i * 4 + 1] = ((double *)ptr_data._ptr)[i * 3 + 1] != 0;
-                data[i * 4 + 2] = ((double *)ptr_data._ptr)[i * 3 + 2] != 0;
-              }
-            } else {
-              for (size_t i = 0; i < num_rows * 4; ++i) {
-                data[i] = ((double *)ptr_data._ptr)[i] != 0;
-              }
-            }
-            break;
-          }
-          if (reg.vreg >= 0) {
-            device->SetVertexShaderConstantB(reg.vreg, data, num_rows);
-          }
-          if (reg.freg >= 0) {
-            device->SetPixelShaderConstantB(reg.freg, data, num_rows);
-          }
-        }
-        break;
-
-      case D3DXRS_INT4:
-        if (ptr_data._type != ShaderType::ST_int &&
-            ptr_data._type != ShaderType::ST_uint) {
-          dxgsg9_cat.error()
-            << "Cannot pass floating-point data to integer shader input '" << spec._id._name << "'\n";
-
-          // Deactivate it to make sure the user doesn't get flooded with this
-          // error.
-          reg.count = -1;
-        }
-        else if (num_cols == 4) {
-          // Straight passthrough, hooray!
-          void *data = ptr_data._ptr;
-          if (reg.vreg >= 0) {
-            device->SetVertexShaderConstantI(reg.vreg, (int *)data, num_rows);
-          }
-          if (reg.freg >= 0) {
-            device->SetPixelShaderConstantI(reg.freg, (int *)data, num_rows);
-          }
-        }
-        else {
-          // Need to pad out the rows.
-          LVecBase4i *data = (LVecBase4i *)scratch;
-          memset(data, 0, sizeof(LVecBase4i) * num_rows);
-          if (num_cols == 1) {
-            for (size_t i = 0; i < num_rows; ++i) {
-              data[i].set(((int *)ptr_data._ptr)[i], 0, 0, 0);
-            }
-          } else if (num_cols == 2) {
-            for (size_t i = 0; i < num_rows; ++i) {
-              data[i].set(((int *)ptr_data._ptr)[i * 2],
-                          ((int *)ptr_data._ptr)[i * 2 + 1],
-                          0, 0);
-            }
-          } else if (num_cols == 3) {
-            for (size_t i = 0; i < num_rows; ++i) {
-              data[i].set(((int *)ptr_data._ptr)[i * 3],
-                          ((int *)ptr_data._ptr)[i * 3 + 1],
-                          ((int *)ptr_data._ptr)[i * 3 + 2],
-                          0);
-            }
-          }
-          if (reg.vreg >= 0) {
-            device->SetVertexShaderConstantI(reg.vreg, (int *)data, num_rows);
-          }
-          if (reg.freg >= 0) {
-            device->SetPixelShaderConstantI(reg.freg, (int *)data, num_rows);
-          }
-        }
-        break;
-
-      case D3DXRS_FLOAT4:
-        if (ptr_data._type == ShaderType::ST_float && num_cols == 4) {
-          // Straight passthrough, hooray!
-          void *data = ptr_data._ptr;
-          if (reg.vreg >= 0) {
-            device->SetVertexShaderConstantF(reg.vreg, (float *)data, num_rows);
-          }
-          if (reg.freg >= 0) {
-            device->SetPixelShaderConstantF(reg.freg, (float *)data, num_rows);
-          }
-        }
-        else {
-          // Need to pad out the rows.
-          LVecBase4f *data = (LVecBase4f *)scratch;
-          memset(data, 0, sizeof(LVecBase4f) * num_rows);
-          switch (ptr_data._type) {
-          case ShaderType::ST_int:
-            if (num_cols == 1) {
-              for (size_t i = 0; i < num_rows; ++i) {
-                data[i].set((float)((int *)ptr_data._ptr)[i], 0, 0, 0);
-              }
-            } else if (num_cols == 2) {
-              for (size_t i = 0; i < num_rows; ++i) {
-                data[i].set((float)((int *)ptr_data._ptr)[i * 2],
-                            (float)((int *)ptr_data._ptr)[i * 2 + 1],
-                            0, 0);
-              }
-            } else if (num_cols == 3) {
-              for (size_t i = 0; i < num_rows; ++i) {
-                data[i].set((float)((int *)ptr_data._ptr)[i * 3],
-                            (float)((int *)ptr_data._ptr)[i * 3 + 1],
-                            (float)((int *)ptr_data._ptr)[i * 3 + 2],
-                            0);
-              }
-            } else {
-              for (size_t i = 0; i < num_rows * 4; ++i) {
-                ((float *)data)[i] = (float)((int *)ptr_data._ptr)[i];
-              }
-            }
-            break;
-
-          case ShaderType::ST_uint:
-            if (num_cols == 1) {
-              for (size_t i = 0; i < num_rows; ++i) {
-                data[i].set((float)((unsigned int *)ptr_data._ptr)[i], 0, 0, 0);
-              }
-            } else if (num_cols == 2) {
-              for (size_t i = 0; i < num_rows; ++i) {
-                data[i].set((float)((unsigned int *)ptr_data._ptr)[i * 2],
-                            (float)((unsigned int *)ptr_data._ptr)[i * 2 + 1],
-                            0, 0);
-              }
-            } else if (num_cols == 3) {
-              for (size_t i = 0; i < num_rows; ++i) {
-                data[i].set((float)((unsigned int *)ptr_data._ptr)[i * 3],
-                            (float)((unsigned int *)ptr_data._ptr)[i * 3 + 1],
-                            (float)((unsigned int *)ptr_data._ptr)[i * 3 + 2],
-                            0);
-              }
-            } else {
-              for (size_t i = 0; i < num_rows * 4; ++i) {
-                ((float *)data)[i] = (float)((unsigned int *)ptr_data._ptr)[i];
-              }
-            }
-            break;
-
-          case ShaderType::ST_float:
-            if (num_cols == 1) {
-              for (size_t i = 0; i < num_rows; ++i) {
-                data[i].set(((float *)ptr_data._ptr)[i], 0, 0, 0);
-              }
-            } else if (num_cols == 2) {
-              for (size_t i = 0; i < num_rows; ++i) {
-                data[i].set(((float *)ptr_data._ptr)[i * 2],
-                            ((float *)ptr_data._ptr)[i * 2 + 1],
-                            0, 0);
-              }
-            } else if (num_cols == 3) {
-              for (size_t i = 0; i < num_rows; ++i) {
-                data[i].set(((float *)ptr_data._ptr)[i * 3],
-                            ((float *)ptr_data._ptr)[i * 3 + 1],
-                            ((float *)ptr_data._ptr)[i * 3 + 2],
-                            0);
-              }
-            } else {
-              for (size_t i = 0; i < num_rows * 4; ++i) {
-                ((float *)data)[i] = ((float *)ptr_data._ptr)[i];
-              }
-            }
-            break;
-
-          case ShaderType::ST_double:
-            if (num_cols == 1) {
-              for (size_t i = 0; i < num_rows; ++i) {
-                data[i].set((float)((double *)ptr_data._ptr)[i], 0, 0, 0);
-              }
-            } else if (num_cols == 2) {
-              for (size_t i = 0; i < num_rows; ++i) {
-                data[i].set((float)((double *)ptr_data._ptr)[i * 2],
-                            (float)((double *)ptr_data._ptr)[i * 2 + 1],
-                            0, 0);
-              }
-            } else if (num_cols == 3) {
-              for (size_t i = 0; i < num_rows; ++i) {
-                data[i].set((float)((double *)ptr_data._ptr)[i * 3],
-                            (float)((double *)ptr_data._ptr)[i * 3 + 1],
-                            (float)((double *)ptr_data._ptr)[i * 3 + 2],
-                            0);
-              }
-            } else {
-              for (size_t i = 0; i < num_rows * 4; ++i) {
-                ((float *)data)[i] = (float)((double *)ptr_data._ptr)[i];
-              }
-            }
-            break;
-          }
-
-          if (reg.vreg >= 0) {
-            device->SetVertexShaderConstantF(reg.vreg, (float *)data, num_rows);
-          }
-          if (reg.freg >= 0) {
-            device->SetPixelShaderConstantF(reg.freg, (float *)data, num_rows);
-          }
-        }
-        break;
-
-      default:
-        continue;
-      }
-    }
-  }
-
   if (altered & _shader->_mat_deps) {
-    gsg->update_shader_matrix_cache(_shader, _mat_part_cache, altered);
+    if (altered & _shader->_mat_cache_deps) {
+      gsg->update_shader_matrix_cache(_shader, _mat_part_cache, altered);
+    }
 
     for (Shader::ShaderMatSpec &spec : _shader->_mat_spec) {
       if ((altered & spec._dep) == 0) {
@@ -788,23 +474,14 @@ issue_parameters(GSG *gsg, int altered) {
         continue;
       }
 
-      const VecBase4 *val = gsg->fetch_specified_value(spec, _mat_part_cache, altered);
+      const void *val = gsg->fetch_specified_value(spec, _mat_part_cache, _mat_scratch_space, true);
       if (!val) continue;
 
       switch (reg.set) {
       case D3DXRS_FLOAT4:
         {
-#ifdef STDFLOAT_DOUBLE
-          float *data = (float *)scratch;
-          const double *from_data = val->get_data();
-          from_data += spec._offset;
-          for (size_t i = 0; i < spec._size; ++i) {
-            data[i] = (float)from_data[i];
-          }
-#else
-          const float *data = val->get_data();
-          data += spec._offset;
-#endif
+          const float *data = (const float *)val;
+          float scratch[16];
 
           switch (spec._piece) {
           default:
@@ -854,8 +531,7 @@ issue_parameters(GSG *gsg, int altered) {
 
       case D3DXRS_INT4:
         {
-          const int *data = (const int *)val->get_data();
-          data += spec._offset;
+          const int *data = (const int *)val;
 
           if (reg.vreg >= 0) {
             device->SetVertexShaderConstantI(reg.vreg, data, reg.count);
@@ -868,8 +544,7 @@ issue_parameters(GSG *gsg, int altered) {
 
       case D3DXRS_BOOL:
         {
-          const BOOL *data = (const BOOL *)val->get_data();
-          data += spec._offset;
+          const BOOL *data = (const BOOL *)val;
 
           if (reg.vreg >= 0) {
             device->SetVertexShaderConstantB(reg.vreg, data, reg.count);

+ 1 - 1
panda/src/dxgsg9/dxShaderContext9.h

@@ -72,7 +72,7 @@ private:
 
   int _frame_number = -1;
   LVecBase4 *_mat_part_cache = nullptr;
-  size_t _scratch_space_size = 0;
+  LVecBase4 *_mat_scratch_space = nullptr;
 
 private:
   void release_resources(void);

+ 285 - 356
panda/src/glstuff/glShaderContext_src.cxx

@@ -53,7 +53,6 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
   _color_attrib_index = -1;
   _transform_table_index = -1;
   _slider_table_index = -1;
-  _frame_number_loc = -1;
   _frame_number = -1;
   _validated = !gl_validate_shaders;
 
@@ -180,32 +179,6 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
       ++it;
     }
 
-    for (auto it = s->_ptr_spec.begin(); it != s->_ptr_spec.end();) {
-      const Shader::ShaderPtrSpec &spec = *it;
-
-      GLint location = get_uniform_location(spec._id._location);
-      if (location < 0) {
-        // Not used.  Optimize it out.
-        if (GLCAT.is_debug()) {
-          GLCAT.debug()
-            << "Uniform " << *spec._id._name << " is unused, unbinding\n";
-        }
-        it = s->_ptr_spec.erase(it);
-        continue;
-      }
-
-      if (GLCAT.is_debug()) {
-        GLCAT.debug()
-          << "Uniform " << *spec._id._name << " is bound to location "
-          << location << "\n";
-      }
-      ++it;
-    }
-
-    if (s->_frame_number_loc >= 0) {
-      _frame_number_loc = get_uniform_location(s->_frame_number_loc);
-    }
-
     // Do we have a p3d_Color attribute?
     for (auto it = s->_var_spec.begin(); it != s->_var_spec.end(); ++it) {
       Shader::ShaderVarSpec &spec = *it;
@@ -240,32 +213,7 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
   }
 
   _mat_part_cache = new LVecBase4[_shader->cp_get_mat_cache_size()];
-
-  // Determine the size of the scratch space to allocate on the stack inside
-  // issue_parameters().
-  for (const Shader::ShaderPtrSpec &spec : _shader->_ptr_spec) {
-    size_t size = spec._dim[0] * spec._dim[1] * spec._dim[2];
-    if (spec._type == ShaderType::ST_double) {
-      size *= 8;
-    } else {
-      size *= 4;
-    }
-    if (size > _scratch_space_size) {
-      _scratch_space_size = size;
-    }
-  }
-
-  for (const Shader::ShaderMatSpec &spec : _shader->_mat_spec) {
-    size_t size = spec._array_count * spec._size;
-    if (spec._scalar_type == ShaderType::ST_double) {
-      size *= 8;
-    } else {
-      size *= 4;
-    }
-    if (size > _scratch_space_size) {
-      _scratch_space_size = size;
-    }
-  }
+  _mat_scratch_space = new LVecBase4[_shader->cp_get_mat_scratch_size(false)];
 }
 
 /**
@@ -371,7 +319,6 @@ reflect_program() {
   param_count = 0;
   _glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_UNIFORMS, &param_count);
 
-  _shader->_ptr_spec.clear();
   _shader->_mat_spec.clear();
   _shader->_tex_spec.clear();
   for (int i = 0; i < param_count; ++i) {
@@ -853,10 +800,11 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
           }
 
           bind._func = Shader::SMF_first;
-          bind._part[0] = Shader::SMO_apiview_to_apiclip_light_source_i;
+          bind._part[0] = Shader::SMO_light_source_i;
           bind._arg[0] = nullptr;
           bind._part[1] = Shader::SMO_identity;
           bind._arg[1] = nullptr;
+          bind._offset = 4 * Shader::LA_shadow_view_matrix;
         }
         else if (strncmp(name_buffer, "shadowMatrix", 127) == 0) {
           // Only supported for backward compatibility: includes the model
@@ -967,7 +915,7 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
           GLCAT.error()
             << "p3d_Material.baseColor should be vec4\n";
         }
-        bind._part[0] = Shader::SMO_attr_material2;
+        bind._offset = 4 * Shader::MA_base_color;
         bind._piece = Shader::SMP_vec4;
         _shader->cp_add_mat_spec(bind);
         return;
@@ -977,6 +925,7 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
           GLCAT.error()
             << "p3d_Material.ambient should be vec4\n";
         }
+        bind._offset = 4 * Shader::MA_ambient;
         bind._piece = Shader::SMP_vec4;
         _shader->cp_add_mat_spec(bind);
         return;
@@ -986,8 +935,8 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
           GLCAT.error()
             << "p3d_Material.diffuse should be vec4\n";
         }
+        bind._offset = 4 * Shader::MA_diffuse;
         bind._piece = Shader::SMP_vec4;
-        bind._offset = 4;
         _shader->cp_add_mat_spec(bind);
         return;
       }
@@ -996,8 +945,8 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
           GLCAT.error()
             << "p3d_Material.emission should be vec4\n";
         }
+        bind._offset = 4 * Shader::MA_emission;
         bind._piece = Shader::SMP_vec4;
-        bind._offset = 8;
         _shader->cp_add_mat_spec(bind);
         return;
       }
@@ -1006,8 +955,8 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
           GLCAT.error()
             << "p3d_Material.specular should be vec3\n";
         }
+        bind._offset = 4 * Shader::MA_specular;
         bind._piece = Shader::SMP_vec3;
-        bind._offset = 12;
         _shader->cp_add_mat_spec(bind);
         return;
       }
@@ -1016,8 +965,8 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
           GLCAT.error()
             << "p3d_Material.shininess should be float\n";
         }
+        bind._offset = 4 * Shader::MA_specular + 3;
         bind._piece = Shader::SMP_scalar;
-        bind._offset = 15;
         _shader->cp_add_mat_spec(bind);
         return;
       }
@@ -1026,9 +975,8 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
           GLCAT.error()
             << "p3d_Material.roughness should be float\n";
         }
-        bind._part[0] = Shader::SMO_attr_material2;
+        bind._offset = 4 * Shader::MA_metallic_ior_roughness + 3;
         bind._piece = Shader::SMP_scalar;
-        bind._offset = 7;
         _shader->cp_add_mat_spec(bind);
         return;
       }
@@ -1037,9 +985,8 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
           GLCAT.error()
             << "p3d_Material.metallic should be bool or float\n";
         }
-        bind._part[0] = Shader::SMO_attr_material2;
+        bind._offset = 4 * Shader::MA_metallic_ior_roughness;
         bind._piece = Shader::SMP_scalar;
-        bind._offset = 4;
         _shader->cp_add_mat_spec(bind);
         return;
       }
@@ -1048,9 +995,8 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
           GLCAT.error()
             << "p3d_Material.refractiveIndex should be float\n";
         }
-        bind._part[0] = Shader::SMO_attr_material2;
+        bind._offset = 4 * Shader::MA_metallic_ior_roughness + 1;
         bind._piece = Shader::SMP_scalar;
-        bind._offset = 5;
         _shader->cp_add_mat_spec(bind);
         return;
       }
@@ -1124,7 +1070,8 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
       bind._arg[1] = nullptr;
 
       if (noprefix == "Fog.color") {
-        bind._part[0] = Shader::SMO_attr_fogcolor;
+        bind._part[0] = Shader::SMO_attr_fog;
+        bind._offset = 4 * Shader::FA_color;
 
         if (param_type == GL_FLOAT_VEC3) {
           bind._piece = Shader::SMP_vec3;
@@ -1138,6 +1085,7 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
       }
       else if (noprefix == "Fog.density") {
         bind._part[0] = Shader::SMO_attr_fog;
+        bind._offset = 4 * Shader::FA_params;
 
         if (param_type == GL_FLOAT) {
           bind._piece = Shader::SMP_scalar;
@@ -1149,10 +1097,10 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
       }
       else if (noprefix == "Fog.start") {
         bind._part[0] = Shader::SMO_attr_fog;
+        bind._offset = 4 * Shader::FA_params + 1;
 
         if (param_type == GL_FLOAT) {
           bind._piece = Shader::SMP_scalar;
-          bind._offset = 1;
         } else {
           GLCAT.error()
             << "p3d_Fog.start should be float\n";
@@ -1161,10 +1109,10 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
       }
       else if (noprefix == "Fog.end") {
         bind._part[0] = Shader::SMO_attr_fog;
+        bind._offset = 4 * Shader::FA_params + 2;
 
         if (param_type == GL_FLOAT) {
           bind._piece = Shader::SMP_scalar;
-          bind._offset = 2;
         } else {
           GLCAT.error()
             << "p3d_Fog.end should be float\n";
@@ -1173,10 +1121,10 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
       }
       else if (noprefix == "Fog.scale") {
         bind._part[0] = Shader::SMO_attr_fog;
+        bind._offset = 4 * Shader::FA_params + 3;
 
         if (param_type == GL_FLOAT) {
           bind._piece = Shader::SMP_scalar;
-          bind._offset = 3;
         } else {
           GLCAT.error()
             << "p3d_Fog.scale should be float\n";
@@ -1243,11 +1191,78 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
           bind._id = param;
           bind._func = Shader::SMF_first;
           bind._index = index;
-          bind._part[0] = Shader::SMO_light_source_i_vec_attrib;
-          bind._arg[0] = InternalName::make(member_name);
+          bind._part[0] = Shader::SMO_light_source_i;
+          bind._arg[0] = nullptr;
           bind._part[1] = Shader::SMO_identity;
           bind._arg[1] = nullptr;
 
+          GLenum expected = GL_FLOAT_VEC4;
+          if (member_name == "color") {
+            bind._offset = 4 * Shader::LA_color;
+          }
+          else if (member_name == "specular") {
+            bind._offset = 4 * Shader::LA_specular;
+          }
+          else if (member_name == "ambient") {
+            bind._offset = 4 * Shader::LA_ambient;
+          }
+          else if (member_name == "diffuse") {
+            bind._offset = 4 * Shader::LA_diffuse;
+          }
+          else if (member_name == "position") {
+            bind._offset = 4 * Shader::LA_position;
+          }
+          else if (member_name == "halfVector") {
+            bind._offset = 4 * Shader::LA_half_vector;
+          }
+          else if (member_name == "spotDirection") {
+            bind._offset = 4 * Shader::LA_spot_direction;
+          }
+          else if (member_name == "spotCosCutoff") {
+            bind._offset = 4 * Shader::LA_spot_params;
+            expected = GL_FLOAT;
+          }
+          else if (member_name == "spotCutoff") {
+            bind._offset = 4 * Shader::LA_spot_params + 1;
+            expected = GL_FLOAT;
+          }
+          else if (member_name == "spotExponent") {
+            bind._offset = 4 * Shader::LA_spot_params + 2;
+            expected = GL_FLOAT;
+          }
+          else if (member_name == "attenuation") {
+            bind._offset = 4 * Shader::LA_attenuation;
+            expected = GL_FLOAT_VEC3;
+          }
+          else if (member_name == "constantAttenuation") {
+            bind._offset = 4 * Shader::LA_attenuation;
+            expected = GL_FLOAT;
+          }
+          else if (member_name == "linearAttenuation") {
+            bind._offset = 4 * Shader::LA_attenuation + 1;
+            expected = GL_FLOAT;
+          }
+          else if (member_name == "quadraticAttenuation") {
+            bind._offset = 4 * Shader::LA_attenuation + 2;
+            expected = GL_FLOAT;
+          }
+          else if (member_name == "radius") {
+            bind._offset = 4 * Shader::LA_attenuation + 3;
+            expected = GL_FLOAT;
+          }
+          else {
+            GLCAT.error()
+              << "Invalid light struct member " << member_name << "\n";
+            return;
+          }
+
+          // It's okay to declare as vec3 if we allow vec4.
+          if (param_type != expected && (expected != GL_FLOAT_VEC4 || param_type != GL_FLOAT_VEC3)) {
+            GLCAT.error()
+              << "p3d_LightSource[]." << member_name << " has unexpected type\n";
+            return;
+          }
+
           switch (param_type) {
           case GL_FLOAT:
             bind._piece = Shader::SMP_scalar;
@@ -1266,8 +1281,7 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
             break;
 
           default:
-            GLCAT.error()
-              << "p3d_LightSource[]." << member_name << " should be float or vec\n";
+            assert(false);
             return;
           }
           _shader->cp_add_mat_spec(bind);
@@ -1354,12 +1368,15 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
       return;
     }
     else if (noprefix == "FrameNumber") {
-      // We don't currently support ints with this mechanism, so we special-
-      // case this one.
+      bind._piece = Shader::SMP_scalar;
+      bind._func = Shader::SMF_first;
+      bind._part[0] = Shader::SMO_frame_number;
+      bind._part[1] = Shader::SMO_identity;
+      bind._scalar_type = ShaderType::ST_int;
       if (param_type != GL_INT) {
         GLCAT.error() << "osg_FrameNumber should be uniform int\n";
       } else {
-        _frame_number_loc = p;
+        _shader->cp_add_mat_spec(bind);
       }
       return;
     }
@@ -1519,64 +1536,115 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
     case GL_UNSIGNED_INT_VEC2:
     case GL_UNSIGNED_INT_VEC3:
     case GL_UNSIGNED_INT_VEC4: {
-      Shader::ShaderPtrSpec bind;
+      Shader::ShaderMatSpec bind;
       bind._id = param;
-      bind._dim[0] = 1;
-      bind._dim[1] = 1;
+      bind._func = Shader::SMF_shader_input_ptr;
       switch (param_type) {
       case GL_BOOL:
-      case GL_INT:
-      case GL_UNSIGNED_INT:
-      case GL_FLOAT:      bind._dim[2] = 1; break;
-      case GL_BOOL_VEC2:
-      case GL_INT_VEC2:
-      case GL_UNSIGNED_INT_VEC2:
-      case GL_FLOAT_VEC2: bind._dim[2] = 2; break;
-      case GL_BOOL_VEC3:
-      case GL_INT_VEC3:
-      case GL_UNSIGNED_INT_VEC3:
-      case GL_FLOAT_VEC3: bind._dim[2] = 3; break;
-      case GL_BOOL_VEC4:
-      case GL_INT_VEC4:
-      case GL_UNSIGNED_INT_VEC4:
-      case GL_FLOAT_VEC4: bind._dim[2] = 4; break;
-      case GL_FLOAT_MAT3:
-        bind._dim[1] = 3;
-        bind._dim[2] = 3;
-        break;
-      case GL_FLOAT_MAT4:
-        bind._dim[1] = 4;
-        bind._dim[2] = 4;
+        bind._piece = Shader::SMP_scalar;
+        bind._scalar_type = ShaderType::ST_bool;
         break;
-      }
-      switch (param_type) {
-      case GL_BOOL:
       case GL_BOOL_VEC2:
+        bind._piece = Shader::SMP_vec2;
+        bind._scalar_type = ShaderType::ST_bool;
+        break;
       case GL_BOOL_VEC3:
+        bind._piece = Shader::SMP_vec3;
+        bind._scalar_type = ShaderType::ST_bool;
+        break;
       case GL_BOOL_VEC4:
+        bind._piece = Shader::SMP_vec4;
+        bind._scalar_type = ShaderType::ST_bool;
+        break;
       case GL_UNSIGNED_INT:
+        bind._piece = Shader::SMP_scalar;
+        bind._scalar_type = ShaderType::ST_uint;
+        break;
       case GL_UNSIGNED_INT_VEC2:
+        bind._piece = Shader::SMP_vec2;
+        bind._scalar_type = ShaderType::ST_uint;
+        break;
       case GL_UNSIGNED_INT_VEC3:
+        bind._piece = Shader::SMP_vec3;
+        bind._scalar_type = ShaderType::ST_uint;
+        break;
       case GL_UNSIGNED_INT_VEC4:
-        bind._type = ShaderType::ST_uint;
+        bind._piece = Shader::SMP_vec4;
+        bind._scalar_type = ShaderType::ST_uint;
         break;
       case GL_INT:
+        bind._piece = Shader::SMP_scalar;
+        bind._scalar_type = ShaderType::ST_int;
+        break;
       case GL_INT_VEC2:
+        bind._piece = Shader::SMP_vec2;
+        bind._scalar_type = ShaderType::ST_int;
+        break;
       case GL_INT_VEC3:
+        bind._piece = Shader::SMP_vec3;
+        bind._scalar_type = ShaderType::ST_int;
+        break;
       case GL_INT_VEC4:
-        bind._type = ShaderType::ST_int;
+        bind._piece = Shader::SMP_vec4;
+        bind._scalar_type = ShaderType::ST_int;
         break;
       case GL_FLOAT:
+        bind._piece = Shader::SMP_scalar;
+        bind._scalar_type = ShaderType::ST_float;
+        break;
       case GL_FLOAT_VEC2:
+        bind._piece = Shader::SMP_vec2;
+        bind._scalar_type = ShaderType::ST_float;
+        break;
       case GL_FLOAT_VEC3:
+        bind._piece = Shader::SMP_vec3;
+        bind._scalar_type = ShaderType::ST_float;
+        break;
       case GL_FLOAT_VEC4:
+        bind._piece = Shader::SMP_vec4;
+        bind._scalar_type = ShaderType::ST_float;
+        break;
       case GL_FLOAT_MAT3:
+        bind._piece = Shader::SMP_mat3_whole;
+        bind._scalar_type = ShaderType::ST_float;
+        break;
       case GL_FLOAT_MAT4:
-        bind._type = ShaderType::ST_float;
+        bind._piece = Shader::SMP_mat4_whole;
+        bind._scalar_type = ShaderType::ST_float;
         break;
+#ifndef OPENGLES
+      case GL_DOUBLE:
+        bind._piece = Shader::SMP_scalar;
+        bind._scalar_type = ShaderType::ST_double;
+        break;
+      case GL_DOUBLE_VEC2:
+        bind._piece = Shader::SMP_vec2;
+        bind._scalar_type = ShaderType::ST_double;
+        break;
+      case GL_DOUBLE_VEC3:
+        bind._piece = Shader::SMP_vec3;
+        bind._scalar_type = ShaderType::ST_double;
+        break;
+      case GL_DOUBLE_VEC4:
+        bind._piece = Shader::SMP_vec4;
+        bind._scalar_type = ShaderType::ST_double;
+        break;
+      case GL_DOUBLE_MAT3:
+        bind._piece = Shader::SMP_mat3_whole;
+        bind._scalar_type = ShaderType::ST_double;
+        break;
+      case GL_DOUBLE_MAT4:
+        bind._piece = Shader::SMP_mat4_whole;
+        bind._scalar_type = ShaderType::ST_double;
+        break;
+#endif
       }
-      bind._arg = param._name;
-      _shader->_ptr_spec.push_back(bind);
+      bind._part[0] = Shader::SMO_INVALID;
+      bind._part[1] = Shader::SMO_INVALID;
+      bind._arg[0] = param._name;
+      bind._arg[1] = nullptr;
+      bind._array_count = 1;
+      _shader->cp_add_mat_spec(bind);
       return;
     }
 #ifndef OPENGLES
@@ -1668,64 +1736,89 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
     case GL_FLOAT_VEC4:
     case GL_FLOAT_MAT3:
     case GL_FLOAT_MAT4: {
-      Shader::ShaderPtrSpec bind;
+      Shader::ShaderMatSpec bind;
       bind._id = param;
-      bind._dim[0] = param_size;
-      bind._dim[1] = 1;
-      switch (param_type) {
-        case GL_BOOL:
-        case GL_INT:
-        case GL_UNSIGNED_INT:
-        case GL_FLOAT:      bind._dim[2] = 1; break;
-        case GL_BOOL_VEC2:
-        case GL_INT_VEC2:
-        case GL_UNSIGNED_INT_VEC2:
-        case GL_FLOAT_VEC2: bind._dim[2] = 2; break;
-        case GL_BOOL_VEC3:
-        case GL_INT_VEC3:
-        case GL_UNSIGNED_INT_VEC3:
-        case GL_FLOAT_VEC3: bind._dim[2] = 3; break;
-        case GL_BOOL_VEC4:
-        case GL_INT_VEC4:
-        case GL_UNSIGNED_INT_VEC4:
-        case GL_FLOAT_VEC4: bind._dim[2] = 4; break;
-        case GL_FLOAT_MAT3:
-          bind._dim[1] = 3;
-          bind._dim[2] = 3;
-          break;
-        case GL_FLOAT_MAT4:
-          bind._dim[1] = 4;
-          bind._dim[2] = 4;
-          break;
-      }
+      bind._func = Shader::SMF_shader_input_ptr;
       switch (param_type) {
       case GL_BOOL:
+        bind._piece = Shader::SMP_scalar_array;
+        bind._scalar_type = ShaderType::ST_bool;
+        break;
       case GL_BOOL_VEC2:
+        bind._piece = Shader::SMP_vec2_array;
+        bind._scalar_type = ShaderType::ST_bool;
+        break;
       case GL_BOOL_VEC3:
+        bind._piece = Shader::SMP_vec3_array;
+        bind._scalar_type = ShaderType::ST_bool;
+        break;
       case GL_BOOL_VEC4:
+        bind._piece = Shader::SMP_vec4_array;
+        bind._scalar_type = ShaderType::ST_bool;
+        break;
       case GL_UNSIGNED_INT:
+        bind._piece = Shader::SMP_scalar_array;
+        bind._scalar_type = ShaderType::ST_uint;
+        break;
       case GL_UNSIGNED_INT_VEC2:
+        bind._piece = Shader::SMP_vec2_array;
+        bind._scalar_type = ShaderType::ST_uint;
+        break;
       case GL_UNSIGNED_INT_VEC3:
+        bind._piece = Shader::SMP_vec3_array;
+        bind._scalar_type = ShaderType::ST_uint;
+        break;
       case GL_UNSIGNED_INT_VEC4:
-        bind._type = ShaderType::ST_uint;
+        bind._piece = Shader::SMP_vec4_array;
+        bind._scalar_type = ShaderType::ST_uint;
         break;
       case GL_INT:
+        bind._piece = Shader::SMP_scalar_array;
+        bind._scalar_type = ShaderType::ST_int;
+        break;
       case GL_INT_VEC2:
+        bind._piece = Shader::SMP_vec2_array;
+        bind._scalar_type = ShaderType::ST_int;
+        break;
       case GL_INT_VEC3:
+        bind._piece = Shader::SMP_vec3_array;
+        bind._scalar_type = ShaderType::ST_int;
+        break;
       case GL_INT_VEC4:
-        bind._type = ShaderType::ST_int;
+        bind._piece = Shader::SMP_vec4_array;
+        bind._scalar_type = ShaderType::ST_int;
         break;
       case GL_FLOAT:
+        bind._piece = Shader::SMP_scalar_array;
+        bind._scalar_type = ShaderType::ST_float;
+        break;
       case GL_FLOAT_VEC2:
+        bind._piece = Shader::SMP_vec2_array;
+        bind._scalar_type = ShaderType::ST_float;
+        break;
       case GL_FLOAT_VEC3:
+        bind._piece = Shader::SMP_vec3_array;
+        bind._scalar_type = ShaderType::ST_float;
+        break;
       case GL_FLOAT_VEC4:
+        bind._piece = Shader::SMP_vec4_array;
+        bind._scalar_type = ShaderType::ST_float;
+        break;
       case GL_FLOAT_MAT3:
+        bind._piece = Shader::SMP_mat3_array;
+        bind._scalar_type = ShaderType::ST_float;
+        break;
       case GL_FLOAT_MAT4:
-        bind._type = ShaderType::ST_float;
+        bind._piece = Shader::SMP_mat4_array;
+        bind._scalar_type = ShaderType::ST_float;
         break;
       }
-      bind._arg = param._name;
-      _shader->_ptr_spec.push_back(bind);
+      bind._part[0] = Shader::SMO_INVALID;
+      bind._part[1] = Shader::SMO_INVALID;
+      bind._arg[0] = param._name;
+      bind._arg[1] = nullptr;
+      bind._array_count = param_size;
+      _shader->cp_add_mat_spec(bind);
       return;
     }
     default:
@@ -2079,6 +2172,7 @@ CLP(ShaderContext)::
 ~CLP(ShaderContext)() {
   // Don't call release_resources; we may not have an active context.
   delete[] _mat_part_cache;
+  delete[] _mat_scratch_space;
 }
 
 /**
@@ -2270,201 +2364,17 @@ issue_parameters(int altered) {
       << " (altered 0x" << hex << altered << dec << ")\n";
   }
 
-  void *scratch = alloca(_scratch_space_size);
-
-  // We have no way to track modifications to PTAs, so we assume that they are
-  // modified every frame and when we switch ShaderAttribs.
-  if (altered & (Shader::SSD_shaderinputs | Shader::SSD_frame)) {
-
-    // If we have an osg_FrameNumber input, set it now.
-    if ((altered & Shader::SSD_frame) != 0 && _frame_number_loc >= 0) {
-      _glgsg->_glUniform1i(_frame_number_loc, _frame_number);
-    }
-
-    // Iterate through _ptr parameters
-    for (int i = 0; i < (int)_shader->_ptr_spec.size(); ++i) {
-      Shader::ShaderPtrSpec &spec = _shader->_ptr_spec[i];
-
-      Shader::ShaderPtrData ptr_data;
-      if (!_glgsg->fetch_ptr_parameter(spec, ptr_data)) { //the input is not contained in ShaderPtrData
-        release_resources();
-        return;
-      }
-
-      nassertd(spec._dim[1] > 0) continue;
-
-      uint32_t dim = spec._dim[1] * spec._dim[2];
-      GLint p = get_uniform_location(spec._id._location);
-      if (p < 0) {
-        continue;
-      }
-
-      int array_size = min(spec._dim[0], (uint32_t)(ptr_data._size / dim));
-      switch (spec._type) {
-      case ShaderType::ST_bool:
-      case ShaderType::ST_float:
-        {
-          float *data = (float *)scratch;
-
-          switch (ptr_data._type) {
-          case ShaderType::ST_int:
-            // Convert int data to float data.
-            for (int i = 0; i < (array_size * dim); ++i) {
-              data[i] = (float)(((int*)ptr_data._ptr)[i]);
-            }
-            break;
-
-          case ShaderType::ST_uint:
-            // Convert unsigned int data to float data.
-            for (int i = 0; i < (array_size * dim); ++i) {
-              data[i] = (float)(((unsigned int*)ptr_data._ptr)[i]);
-            }
-            break;
-
-          case ShaderType::ST_double:
-            // Downgrade double data to float data.
-            for (int i = 0; i < (array_size * dim); ++i) {
-              data[i] = (float)(((double*)ptr_data._ptr)[i]);
-            }
-            break;
-
-          case ShaderType::ST_float:
-            data = (float *)ptr_data._ptr;
-            break;
-
-          default:
-#ifndef NDEBUG
-            GLCAT.error()
-              << "Invalid ShaderPtrSpec type " << (int)ptr_data._type
-              << " for shader input '" << spec._id._name << "'\n";
-#endif
-            continue;
-          }
-
-          switch (dim) {
-          case 1: _glgsg->_glUniform1fv(p, array_size, (float*)data); continue;
-          case 2: _glgsg->_glUniform2fv(p, array_size, (float*)data); continue;
-          case 3: _glgsg->_glUniform3fv(p, array_size, (float*)data); continue;
-          case 4: _glgsg->_glUniform4fv(p, array_size, (float*)data); continue;
-          case 9: _glgsg->_glUniformMatrix3fv(p, array_size, GL_FALSE, (float*)data); continue;
-          case 16: _glgsg->_glUniformMatrix4fv(p, array_size, GL_FALSE, (float*)data); continue;
-          }
-          nassertd(false) continue;
-        }
-        break;
-
-      case ShaderType::ST_int:
-        if (ptr_data._type != ShaderType::ST_int &&
-            ptr_data._type != ShaderType::ST_uint) {
-          GLCAT.error()
-            << "Cannot pass floating-point data to integer shader input '" << spec._id._name << "'\n";
-
-          // Deactivate it to make sure the user doesn't get flooded with this
-          // error.
-          set_uniform_location(spec._id._location, -1);
-
-        } else {
-          switch (spec._dim[1] * spec._dim[2]) {
-          case 1: _glgsg->_glUniform1iv(p, array_size, (int*)ptr_data._ptr); continue;
-          case 2: _glgsg->_glUniform2iv(p, array_size, (int*)ptr_data._ptr); continue;
-          case 3: _glgsg->_glUniform3iv(p, array_size, (int*)ptr_data._ptr); continue;
-          case 4: _glgsg->_glUniform4iv(p, array_size, (int*)ptr_data._ptr); continue;
-          }
-          nassertd(false) continue;
-        }
-        break;
-
-      case ShaderType::ST_uint:
-        if (ptr_data._type != ShaderType::ST_uint &&
-            ptr_data._type != ShaderType::ST_int) {
-          GLCAT.error()
-            << "Cannot pass floating-point data to integer shader input '" << spec._id._name << "'\n";
-
-          // Deactivate it to make sure the user doesn't get flooded with this
-          // error.
-          set_uniform_location(spec._id._location, -1);
-
-        } else {
-          switch (spec._dim[1] * spec._dim[2]) {
-          case 1: _glgsg->_glUniform1uiv(p, array_size, (GLuint *)ptr_data._ptr); continue;
-          case 2: _glgsg->_glUniform2uiv(p, array_size, (GLuint *)ptr_data._ptr); continue;
-          case 3: _glgsg->_glUniform3uiv(p, array_size, (GLuint *)ptr_data._ptr); continue;
-          case 4: _glgsg->_glUniform4uiv(p, array_size, (GLuint *)ptr_data._ptr); continue;
-          }
-          nassertd(false) continue;
-        }
-        break;
-
-      case ShaderType::ST_double:
-#ifdef OPENGLES
-          GLCAT.error()
-            << "Passing double-precision shader inputs to shaders is not supported in OpenGL ES.\n";
-
-          // Deactivate it to make sure the user doesn't get flooded with this
-          // error.
-          set_uniform_location(spec._id._location, -1);
-#else
-        {
-          double *data = (double *)scratch;
-
-          switch (ptr_data._type) {
-          case ShaderType::ST_int:
-            // Convert int data to double data.
-            for (int i = 0; i < (array_size * dim); ++i) {
-              data[i] = (double)(((int*)ptr_data._ptr)[i]);
-            }
-            break;
-
-          case ShaderType::ST_uint:
-            // Convert unsigned int data to double data.
-            for (int i = 0; i < (array_size * dim); ++i) {
-              data[i] = (double)(((unsigned int*)ptr_data._ptr)[i]);
-            }
-            break;
-
-          case ShaderType::ST_double:
-            data = (double *)ptr_data._ptr;
-            break;
-
-          case ShaderType::ST_float:
-            // Upgrade float data to double data.
-            for (int i = 0; i < (array_size * dim); ++i) {
-              data[i] = (double)(((float*)ptr_data._ptr)[i]);
-            }
-            break;
-
-          default:
-            nassertd(false) continue;
-          }
-
-          switch (dim) {
-          case 1: _glgsg->_glUniform1dv(p, array_size, data); continue;
-          case 2: _glgsg->_glUniform2dv(p, array_size, data); continue;
-          case 3: _glgsg->_glUniform3dv(p, array_size, data); continue;
-          case 4: _glgsg->_glUniform4dv(p, array_size, data); continue;
-          case 9: _glgsg->_glUniformMatrix3dv(p, array_size, GL_FALSE, data); continue;
-          case 16: _glgsg->_glUniformMatrix4dv(p, array_size, GL_FALSE, data); continue;
-          }
-          nassertd(false) continue;
-        }
-#endif
-        break;
-
-      default:
-        continue;
-      }
-    }
-  }
-
   if (altered & _shader->_mat_deps) {
-    _glgsg->update_shader_matrix_cache(_shader, _mat_part_cache, altered);
+    if (altered & _shader->_mat_cache_deps) {
+      _glgsg->update_shader_matrix_cache(_shader, _mat_part_cache, altered);
+    }
 
     for (Shader::ShaderMatSpec &spec : _shader->_mat_spec) {
       if ((altered & spec._dep) == 0) {
         continue;
       }
 
-      const LVecBase4 *val = _glgsg->fetch_specified_value(spec, _mat_part_cache, altered);
+      const void *val = _glgsg->fetch_specified_value(spec, _mat_part_cache, _mat_scratch_space, false);
       if (!val) continue;
 
       GLint p = get_uniform_location(spec._id._location);
@@ -2473,24 +2383,19 @@ issue_parameters(int altered) {
       }
 
       if (spec._scalar_type == ShaderType::ST_float) {
-#ifdef STDFLOAT_DOUBLE
-        float *data = (float *)scratch;
-        const double *from_data = val->get_data();
-        from_data += spec._offset;
-        for (size_t i = 0; i < spec._size; ++i) {
-          data[i] = (float)from_data[i];
-        }
-#else
-        const float *data = val->get_data();
-        data += spec._offset;
-#endif
+        const float *data = (const float *)val;
 
         switch (spec._piece) {
         case Shader::SMP_scalar: _glgsg->_glUniform1fv(p, 1, data); continue;
         case Shader::SMP_vec2: _glgsg->_glUniform2fv(p, 1, data); continue;
         case Shader::SMP_vec3: _glgsg->_glUniform3fv(p, 1, data); continue;
         case Shader::SMP_vec4: _glgsg->_glUniform4fv(p, 1, data); continue;
+        case Shader::SMP_scalar_array: _glgsg->_glUniform1fv(p, spec._array_count, data); continue;
+        case Shader::SMP_vec2_array: _glgsg->_glUniform2fv(p, spec._array_count, data); continue;
+        case Shader::SMP_vec3_array: _glgsg->_glUniform3fv(p, spec._array_count, data); continue;
         case Shader::SMP_vec4_array: _glgsg->_glUniform4fv(p, spec._array_count, data); continue;
+        case Shader::SMP_mat3_whole: _glgsg->_glUniformMatrix3fv(p, 1, GL_FALSE, data); continue;
+        case Shader::SMP_mat3_array: _glgsg->_glUniformMatrix3fv(p, spec._array_count, GL_FALSE, data); continue;
         case Shader::SMP_mat4_whole: _glgsg->_glUniformMatrix4fv(p, 1, GL_FALSE, data); continue;
         case Shader::SMP_mat4_array: _glgsg->_glUniformMatrix4fv(p, spec._array_count, GL_FALSE, data); continue;
         case Shader::SMP_mat4_transpose: _glgsg->_glUniformMatrix4fv(p, 1, GL_TRUE, data); continue;
@@ -2528,24 +2433,27 @@ issue_parameters(int altered) {
         }
       }
       else if (spec._scalar_type == ShaderType::ST_double) {
-#ifdef STDFLOAT_DOUBLE
-        const double *data = val->get_data();
-        data += spec._offset;
-#else
-        double *data = (double *)scratch;
-        const float *from_data = val->get_data();
-        from_data += spec._offset;
-        for (size_t i = 0; i < spec._size; ++i) {
-          data[i] = (double)from_data[i];
-        }
-#endif
+        const double *data = (const double *)val;
 
+#ifdef OPENGLES
+        GLCAT.error()
+          << "Passing double-precision shader inputs to shaders is not supported in OpenGL ES.\n";
+
+        // Deactivate it to make sure the user doesn't get flooded with this
+        // error.
+        set_uniform_location(spec._id._location, -1);
+#else
         switch (spec._piece) {
         case Shader::SMP_scalar: _glgsg->_glUniform1dv(p, 1, data); continue;
         case Shader::SMP_vec2: _glgsg->_glUniform2dv(p, 1, data); continue;
         case Shader::SMP_vec3: _glgsg->_glUniform3dv(p, 1, data); continue;
         case Shader::SMP_vec4: _glgsg->_glUniform4dv(p, 1, data); continue;
+        case Shader::SMP_scalar_array: _glgsg->_glUniform1dv(p, spec._array_count, data); continue;
+        case Shader::SMP_vec2_array: _glgsg->_glUniform2dv(p, spec._array_count, data); continue;
+        case Shader::SMP_vec3_array: _glgsg->_glUniform3dv(p, spec._array_count, data); continue;
         case Shader::SMP_vec4_array: _glgsg->_glUniform4dv(p, spec._array_count, data); continue;
+        case Shader::SMP_mat3_whole: _glgsg->_glUniformMatrix3dv(p, 1, GL_FALSE, data); continue;
+        case Shader::SMP_mat3_array: _glgsg->_glUniformMatrix3dv(p, spec._array_count, GL_FALSE, data); continue;
         case Shader::SMP_mat4_whole: _glgsg->_glUniformMatrix4dv(p, 1, GL_FALSE, data); continue;
         case Shader::SMP_mat4_array: _glgsg->_glUniformMatrix4dv(p, spec._array_count, GL_FALSE, data); continue;
         case Shader::SMP_mat4_transpose: _glgsg->_glUniformMatrix4dv(p, 1, GL_TRUE, data); continue;
@@ -2581,20 +2489,41 @@ issue_parameters(int altered) {
           _glgsg->_glUniformMatrix4x3dv(p, 1, GL_TRUE, data);
           continue;
         }
+#endif
       }
       else if (spec._scalar_type == ShaderType::ST_int) {
-        const int *data = (const int *)val->get_data();
-        data += spec._offset;
+        const int *data = (const int *)val;
 
         switch (spec._piece) {
         case Shader::SMP_scalar: _glgsg->_glUniform1i(p, ((int *)data)[0]);; continue;
         case Shader::SMP_vec2: _glgsg->_glUniform2iv(p, 1, data); continue;
         case Shader::SMP_vec3: _glgsg->_glUniform3iv(p, 1, data); continue;
         case Shader::SMP_vec4: _glgsg->_glUniform4iv(p, 1, data); continue;
+        case Shader::SMP_scalar_array: _glgsg->_glUniform1iv(p, spec._array_count, data); continue;
+        case Shader::SMP_vec2_array: _glgsg->_glUniform2iv(p, spec._array_count, data); continue;
+        case Shader::SMP_vec3_array: _glgsg->_glUniform3iv(p, spec._array_count, data); continue;
         case Shader::SMP_vec4_array: _glgsg->_glUniform4iv(p, spec._array_count, data); continue;
         default: nassert_raise("Invalid ShaderMatSpec piece with scalar type int");
         }
       }
+      else if (spec._scalar_type == ShaderType::ST_uint || spec._scalar_type == ShaderType::ST_bool) {
+        const unsigned int *data = (const unsigned int *)val;
+
+        switch (spec._piece) {
+        case Shader::SMP_scalar: _glgsg->_glUniform1uiv(p, 1, data); continue;
+        case Shader::SMP_vec2: _glgsg->_glUniform2uiv(p, 1, data); continue;
+        case Shader::SMP_vec3: _glgsg->_glUniform3uiv(p, 1, data); continue;
+        case Shader::SMP_vec4: _glgsg->_glUniform4uiv(p, 1, data); continue;
+        case Shader::SMP_scalar_array: _glgsg->_glUniform1uiv(p, spec._array_count, data); continue;
+        case Shader::SMP_vec2_array: _glgsg->_glUniform2uiv(p, spec._array_count, data); continue;
+        case Shader::SMP_vec3_array: _glgsg->_glUniform3uiv(p, spec._array_count, data); continue;
+        case Shader::SMP_vec4_array: _glgsg->_glUniform4uiv(p, spec._array_count, data); continue;
+        default: nassert_raise("Invalid ShaderMatSpec piece with scalar type uint/bool");
+        }
+      }
+      else {
+        nassert_raise("numeric type not supported");
+      }
     }
   }
 

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

@@ -87,7 +87,6 @@ private:
   bool _needs_query_uniform_locations = false;
   bool _remap_uniform_locations = false;
   bool _emulate_float_attribs = false;
-  size_t _scratch_space_size = 0;
 
   WCPT(RenderState) _state_rs;
   CPT(TransformState) _modelview_transform;
@@ -109,7 +108,6 @@ private:
   GLint _slider_table_index;
   GLsizei _transform_table_size;
   GLsizei _slider_table_size;
-  GLint _frame_number_loc;
   GLint _frame_number;
 
 #ifndef OPENGLES
@@ -131,6 +129,7 @@ private:
   pvector<ImageInput> _glsl_img_inputs;
 
   LVecBase4 *_mat_part_cache = nullptr;
+  LVecBase4 *_mat_scratch_space = nullptr;
 
   CLP(GraphicsStateGuardian) *_glgsg;
 

+ 282 - 132
panda/src/gobj/shader.cxx

@@ -264,16 +264,16 @@ cp_dependency(ShaderMatInput inp) {
   if (inp == SMO_INVALID) {
     return SSD_NONE;
   }
-  if (inp == SMO_attr_material || inp == SMO_attr_material2) {
+  if (inp == SMO_attr_material) {
     dep |= SSD_material | SSD_frame;
   }
-  if (inp == SMO_attr_color || inp == SMO_attr_material2) {
+  if (inp == SMO_attr_color) {
     dep |= SSD_color;
   }
   if (inp == SMO_attr_colorscale) {
     dep |= SSD_colorscale;
   }
-  if (inp == SMO_attr_fog || inp == SMO_attr_fogcolor) {
+  if (inp == SMO_attr_fog) {
     dep |= SSD_fog | SSD_frame;
   }
   if ((inp == SMO_model_to_view) ||
@@ -342,12 +342,12 @@ cp_dependency(ShaderMatInput inp) {
     }
   }
   if ((inp == SMO_light_ambient) ||
-      (inp == SMO_light_source_i_vec_attrib) ||
+      (inp == SMO_light_source_i) ||
       (inp == SMO_apiview_to_apiclip_light_source_i) ||
       (inp == SMO_light_source_i_packed)) {
     dep |= SSD_light | SSD_frame;
   }
-  if (inp == SMO_light_source_i_vec_attrib ||
+  if (inp == SMO_light_source_i ||
       inp == SMO_apiview_to_apiclip_light_source_i ||
       inp == SMO_light_source_i_packed ||
       inp == SMO_mat_constant_x_attrib ||
@@ -395,13 +395,97 @@ cp_dependency(ShaderMatInput inp) {
   return dep;
 }
 
+/**
+ * Given ShaderMatInput, returns the size in the cache that this part requires.
+ */
+int Shader::
+cp_size(ShaderMatInput inp) {
+  switch (inp) {
+  case SMO_INVALID:
+    return 0;
+
+  case SMO_window_size:
+  case SMO_pixel_size:
+  case SMO_texpad_x:
+  case SMO_texpix_x:
+  case SMO_attr_color:
+  case SMO_attr_colorscale:
+  case SMO_satten_x:
+  case SMO_plane_x:
+  case SMO_clipplane_x:
+  case SMO_vec_constant_x:
+  case SMO_frame_number:
+  case SMO_frame_time:
+  case SMO_frame_delta:
+  case SMO_vec_constant_x_attrib:
+  case SMO_light_ambient:
+  case SMO_light_product_i_ambient:
+  case SMO_light_product_i_diffuse:
+  case SMO_light_product_i_specular:
+  case SMO_apiview_clipplane_i:
+  case SMO_tex_is_alpha_i:
+  case SMO_texscale_i:
+  case SMO_texcolor_i:
+  case SMO_texconst_i:
+  case SMO_attr_pointparams:
+    return 1;
+
+  case SMO_identity:
+  case SMO_alight_x:
+  case SMO_dlight_x:
+  case SMO_plight_x:
+  case SMO_slight_x:
+  case SMO_texmat_i:
+  case SMO_mat_constant_x:
+  case SMO_world_to_view:
+  case SMO_view_to_world:
+  case SMO_model_to_view:
+  case SMO_view_to_model:
+  case SMO_apiview_to_view:
+  case SMO_view_to_apiview:
+  case SMO_clip_to_view:
+  case SMO_view_to_clip:
+  case SMO_apiclip_to_view:
+  case SMO_view_to_apiclip:
+  case SMO_view_x_to_view:
+  case SMO_view_to_view_x:
+  case SMO_apiview_x_to_view:
+  case SMO_view_to_apiview_x:
+  case SMO_clip_x_to_view:
+  case SMO_view_to_clip_x:
+  case SMO_apiclip_x_to_view:
+  case SMO_view_to_apiclip_x:
+  case SMO_mat_constant_x_attrib:
+  case SMO_apiview_to_apiclip_light_source_i:
+  case SMO_model_to_apiview:
+  case SMO_apiview_to_model:
+  case SMO_apiview_to_apiclip:
+  case SMO_apiclip_to_apiview:
+  case SMO_inv_texmat_i:
+  case SMO_light_source_i_packed:
+    return 4;
+
+  case SMO_attr_material:
+    return MA_COUNT;
+
+  case SMO_light_source_i:
+    return LA_COUNT;
+
+  case SMO_attr_fog:
+    return FA_COUNT;
+  }
+
+  nassertr(false, 0);
+  return 0;
+}
+
 /**
  * Adds the given ShaderMatSpec to the shader's mat spec table.
  */
 void Shader::
 cp_add_mat_spec(ShaderMatSpec &spec) {
   // We currently expect each ShaderMatSpec to map to one location.
-  nassertv(spec._id._type->get_num_parameter_locations() == 1);
+  //nassertv(spec._id._type->get_num_parameter_locations() == 1);
 
   // If we're composing with identity, simplify.
 
@@ -483,7 +567,7 @@ cp_add_mat_spec(ShaderMatSpec &spec) {
     for (int i = 0; i < 2; ++i) {
       if (spec._part[i] == SMO_texmat_i ||
           spec._part[i] == SMO_inv_texmat_i ||
-          spec._part[i] == SMO_light_source_i_vec_attrib ||
+          spec._part[i] == SMO_light_source_i ||
           spec._part[i] == SMO_apiview_to_apiclip_light_source_i ||
           spec._part[i] == SMO_light_product_i_ambient ||
           spec._part[i] == SMO_light_product_i_diffuse ||
@@ -500,29 +584,37 @@ cp_add_mat_spec(ShaderMatSpec &spec) {
     nassertv(end[0] == 1 || end[1] == 1);
   }
 
-  // Determine the number of elements used from the cache per array item.
+  // Determine the number of elements that will be passed to the shader3.
   switch (spec._piece) {
-  case SMP_scalar: spec._size = 1; break;
-  case SMP_vec2: spec._size = 2; break;
-  case SMP_vec3: spec._size = 3; break;
-  case SMP_vec4: spec._size = 4; break;
-  case SMP_vec4_array: spec._size = 4; break;
-  case SMP_mat4_whole: spec._size = 16; break;
-  case SMP_mat4_array: spec._size = 16; break;
-  case SMP_mat4_transpose: spec._size = 16; break;
-  case SMP_mat4_column: spec._size = 13; break;
-  case SMP_mat4_upper3x3: spec._size = 11; break;
-  case SMP_mat4_transpose3x3: spec._size = 11; break;
-  case SMP_mat4_upper3x4: spec._size = 15; break;
-  case SMP_mat4_transpose3x4: spec._size = 15; break;
-  case SMP_mat4_upper4x3: spec._size = 15; break;
-  case SMP_mat4_transpose4x3: spec._size = 15; break;
+  case SMP_scalar: spec._num_rows = 1; spec._num_cols = 1; break;
+  case SMP_vec2: spec._num_rows = 1; spec._num_cols = 2; break;
+  case SMP_vec3: spec._num_rows = 1; spec._num_cols = 3; break;
+  case SMP_vec4: spec._num_rows = 1; spec._num_cols = 4; break;
+  case SMP_scalar_array: spec._num_rows = 1; spec._num_cols = 1; break;
+  case SMP_vec2_array: spec._num_rows = 1; spec._num_cols = 2; break;
+  case SMP_vec3_array: spec._num_rows = 1; spec._num_cols = 3; break;
+  case SMP_vec4_array: spec._num_rows = 1; spec._num_cols = 4; break;
+  case SMP_mat3_whole: spec._num_rows = 3; spec._num_cols = 3; break;
+  case SMP_mat3_array: spec._num_rows = 3; spec._num_cols = 3; break;
+  case SMP_mat4_whole: spec._num_rows = 4; spec._num_cols = 4;  break;
+  case SMP_mat4_array: spec._num_rows = 4; spec._num_cols = 4;  break;
+  case SMP_mat4_transpose: spec._num_rows = 4; spec._num_cols = 4;  break;
+  case SMP_mat4_column: spec._num_rows = 4; spec._num_cols = 4;  break;
+  case SMP_mat4_upper3x3: spec._num_rows = 4; spec._num_cols = 4;  break;
+  case SMP_mat4_transpose3x3: spec._num_rows = 4; spec._num_cols = 4;  break;
+  case SMP_mat4_upper3x4: spec._num_rows = 4; spec._num_cols = 4;  break;
+  case SMP_mat4_transpose3x4: spec._num_rows = 4; spec._num_cols = 4;  break;
+  case SMP_mat4_upper4x3: spec._num_rows = 4; spec._num_cols = 4;  break;
+  case SMP_mat4_transpose4x3: spec._num_rows = 4; spec._num_cols = 4;  break;
   }
 
   // Make sure that we have a place in the part cache for both parts.
   int num_parts = (spec._func != SMF_first) ? 2 : 1;
 
   for (int p = 0; p < num_parts; ++p) {
+    if (spec._part[p] == SMO_INVALID) {
+      continue;
+    }
     int dep = cp_dependency(spec._part[p]);
     spec._dep |= dep;
 
@@ -556,6 +648,7 @@ cp_add_mat_spec(ShaderMatSpec &spec) {
       }
       offset += part._count * part._size;
     }
+    int size = cp_size(spec._part[p]);
     if (i == _mat_parts.size()) {
       // Didn't find this part yet, create a new one.
       ShaderMatPart part;
@@ -563,91 +656,22 @@ cp_add_mat_spec(ShaderMatSpec &spec) {
       part._count = end[p];
       part._arg = spec._arg[p];
       part._dep = dep;
-
-      switch (part._part) {
-      case SMO_INVALID:
-        part._size = 0;
-        break;
-
-      case SMO_window_size:
-      case SMO_pixel_size:
-      case SMO_texpad_x:
-      case SMO_texpix_x:
-      case SMO_attr_color:
-      case SMO_attr_colorscale:
-      case SMO_satten_x:
-      case SMO_plane_x:
-      case SMO_clipplane_x:
-      case SMO_vec_constant_x:
-      case SMO_attr_fog:
-      case SMO_attr_fogcolor:
-      case SMO_frame_number:
-      case SMO_frame_time:
-      case SMO_frame_delta:
-      case SMO_vec_constant_x_attrib:
-      case SMO_light_ambient:
-      case SMO_light_source_i_vec_attrib:
-      case SMO_light_product_i_ambient:
-      case SMO_light_product_i_diffuse:
-      case SMO_light_product_i_specular:
-      case SMO_apiview_clipplane_i:
-      case SMO_tex_is_alpha_i:
-      case SMO_texscale_i:
-      case SMO_texcolor_i:
-      case SMO_texconst_i:
-      case SMO_attr_pointparams:
-        part._size = 1;
-        break;
-
-      case SMO_attr_material2:
-        part._size = 2;
-        break;
-
-      case SMO_identity:
-      case SMO_attr_material:
-      case SMO_alight_x:
-      case SMO_dlight_x:
-      case SMO_plight_x:
-      case SMO_slight_x:
-      case SMO_texmat_i:
-      case SMO_mat_constant_x:
-      case SMO_world_to_view:
-      case SMO_view_to_world:
-      case SMO_model_to_view:
-      case SMO_view_to_model:
-      case SMO_apiview_to_view:
-      case SMO_view_to_apiview:
-      case SMO_clip_to_view:
-      case SMO_view_to_clip:
-      case SMO_apiclip_to_view:
-      case SMO_view_to_apiclip:
-      case SMO_view_x_to_view:
-      case SMO_view_to_view_x:
-      case SMO_apiview_x_to_view:
-      case SMO_view_to_apiview_x:
-      case SMO_clip_x_to_view:
-      case SMO_view_to_clip_x:
-      case SMO_apiclip_x_to_view:
-      case SMO_view_to_apiclip_x:
-      case SMO_mat_constant_x_attrib:
-      case SMO_apiview_to_apiclip_light_source_i:
-      case SMO_model_to_apiview:
-      case SMO_apiview_to_model:
-      case SMO_apiview_to_apiclip:
-      case SMO_apiclip_to_apiview:
-      case SMO_inv_texmat_i:
-      case SMO_light_source_i_packed:
-        part._size = 4;
-        break;
-      }
+      part._size = size;
 
       if (spec._func != SMF_first) {
         assert(part._size == 4);
       }
 
+      _mat_cache_deps |= part._dep;
       _mat_parts.push_back(std::move(part));
     }
-    spec._cache_offset[p] = offset + begin[p];
+    spec._cache_offset[p] = offset + begin[p] * size;
+  }
+  if (spec._func == SMF_shader_input_ptr) {
+    // We specify SSD_frame because a PTA may be modified by the app from
+    // frame to frame, and we have no way to know.  So, we must respecify a
+    // PTA at least once every frame.
+    spec._dep |= SSD_general | SSD_shaderinputs | SSD_frame;
   }
 
   _mat_spec.push_back(spec);
@@ -667,6 +691,31 @@ cp_get_mat_cache_size() const {
   return size;
 }
 
+/**
+ * Returns the total amount of scratch space required to fetch the largest
+ * shader input of this shader, in units of 16-byte blocks.
+ */
+size_t Shader::
+cp_get_mat_scratch_size(bool pad_rows) const {
+  size_t max_size = 4;
+  for (const ShaderMatSpec &spec : _mat_spec) {
+    //if (spec._func == SMF_first && (!pad_rows || spec._num_cols == 4)) {
+    //  continue;
+    //}
+    size_t basic_size = 1 + (spec._scalar_type == ScalarType::ST_double);
+    size_t size;
+    if (pad_rows) {
+      size = basic_size * spec._num_rows * spec._array_count;
+    } else {
+      size = basic_size * (spec._num_rows * spec._num_cols * spec._array_count + 3) / 4;
+    }
+    if (size > max_size) {
+      max_size = size;
+    }
+  }
+  return max_size;
+}
+
 /**
  *
  */
@@ -1497,64 +1546,58 @@ bind_parameter(const Parameter &param) {
 
         if (member.name == "baseColor") {
           if (expect_float_vector(fqname, member.type, 4, 4)) {
-            bind._part[0] = SMO_attr_material2;
             bind._piece = SMP_vec4;
+            bind._offset = 4 * MA_base_color;
             cp_add_mat_spec(bind);
             continue;
           }
         } else if (member.name == "ambient") {
           if (expect_float_vector(fqname, member.type, 4, 4)) {
-            bind._part[0] = SMO_attr_material;
             bind._piece = SMP_vec4;
+            bind._offset = 4 * MA_ambient;
             cp_add_mat_spec(bind);
             continue;
           }
         } else if (member.name == "diffuse") {
           if (expect_float_vector(fqname, member.type, 4, 4)) {
-            bind._part[0] = SMO_attr_material;
             bind._piece = SMP_vec4;
-            bind._offset = 4;
+            bind._offset = 4 * MA_diffuse;
             cp_add_mat_spec(bind);
             continue;
           }
         } else if (member.name == "emission") {
           if (expect_float_vector(fqname, member.type, 4, 4)) {
-            bind._part[0] = SMO_attr_material;
             bind._piece = SMP_vec4;
-            bind._offset = 8;
+            bind._offset = 4 * MA_emission;
             cp_add_mat_spec(bind);
             continue;
           }
         } else if (member.name == "specular") {
           if (expect_float_vector(fqname, member.type, 3, 3)) {
-            bind._part[0] = SMO_attr_material;
             bind._piece = SMP_vec3;
-            bind._offset = 12;
+            bind._offset = 4 * MA_specular;
             cp_add_mat_spec(bind);
             continue;
           }
         } else if (member.name == "shininess") {
           if (expect_float_vector(fqname, member.type, 1, 1)) {
-            bind._part[0] = SMO_attr_material;
             bind._piece = SMP_scalar;
-            bind._offset = 15;
+            bind._offset = 4 * MA_specular + 3;
             cp_add_mat_spec(bind);
             continue;
           }
         } else if (member.name == "roughness") {
           if (expect_float_vector(fqname, member.type, 1, 1)) {
-            bind._part[0] = SMO_attr_material2;
             bind._piece = SMP_scalar;
-            bind._offset = 7;
+            bind._offset = 4 * MA_metallic_ior_roughness + 3;
             cp_add_mat_spec(bind);
             continue;
           }
         } else if (member.name == "metallic") {
           if (member.type == ::ShaderType::bool_type ||
               member.type == ::ShaderType::float_type) {
-            bind._part[0] = SMO_attr_material2;
             bind._piece = SMP_scalar;
-            bind._offset = 4;
+            bind._offset = 4 * MA_metallic_ior_roughness;
             cp_add_mat_spec(bind);
             continue;
           } else {
@@ -1562,9 +1605,8 @@ bind_parameter(const Parameter &param) {
           }
         } else if (member.name == "refractiveIndex") {
           if (expect_float_vector(fqname, member.type, 1, 1)) {
-            bind._part[0] = SMO_attr_material2;
             bind._piece = SMP_scalar;
-            bind._offset = 5;
+            bind._offset = 4 * MA_metallic_ior_roughness + 1;
             cp_add_mat_spec(bind);
             continue;
           }
@@ -1677,7 +1719,8 @@ bind_parameter(const Parameter &param) {
 
         if (member.name == "color") {
           if (expect_float_vector(fqname, member.type, 3, 4)) {
-            bind._part[0] = SMO_attr_fogcolor;
+            bind._part[0] = SMO_attr_fog;
+            bind._offset = 4 * FA_color;
             if (member.type->as_vector()->get_num_components() == 3) {
               bind._piece = Shader::SMP_vec3;
             } else {
@@ -1689,6 +1732,7 @@ bind_parameter(const Parameter &param) {
         } else if (member.name == "density") {
           if (expect_float_vector(fqname, member.type, 1, 1)) {
             bind._part[0] = SMO_attr_fog;
+            bind._offset = 4 * FA_params;
             bind._piece = SMP_scalar;
             cp_add_mat_spec(bind);
             continue;
@@ -1696,23 +1740,25 @@ bind_parameter(const Parameter &param) {
         } else if (member.name == "start") {
           if (expect_float_vector(fqname, member.type, 1, 1)) {
             bind._part[0] = SMO_attr_fog;
+            bind._offset = 4 * FA_params + 1;
             bind._piece = SMP_scalar;
-            bind._offset = 1;
             cp_add_mat_spec(bind);
             continue;
           }
         } else if (member.name == "end") {
           if (expect_float_vector(fqname, member.type, 1, 1)) {
             bind._part[0] = SMO_attr_fog;
+            bind._offset = 4 * FA_params + 2;
             bind._piece = SMP_scalar;
-            bind._offset = 2;
+            cp_add_mat_spec(bind);
             continue;
           }
         } else if (member.name == "scale") {
           if (expect_float_vector(fqname, member.type, 1, 1)) {
             bind._part[0] = SMO_attr_fog;
+            bind._offset = 4 * FA_params + 3;
             bind._piece = SMP_scalar;
-            bind._offset = 3;
+            cp_add_mat_spec(bind);
             continue;
           }
         } else {
@@ -1859,11 +1905,79 @@ bind_parameter(const Parameter &param) {
             else {
               bind._piece = SMP_vec4;
             }
-            bind._part[0] = SMO_light_source_i_vec_attrib;
-            bind._arg[0] = InternalName::make(member.name);
+            bind._part[0] = SMO_light_source_i;
+            bind._arg[0] = nullptr;
             bind._part[1] = SMO_identity;
             bind._arg[1] = nullptr;
             bind._scalar_type = ScalarType::ST_float;
+
+            ShaderMatPiece expected = SMP_vec4;
+            if (member.name == "color") {
+              bind._offset = 4 * Shader::LA_color;
+            }
+            else if (member.name == "specular") {
+              bind._offset = 4 * Shader::LA_specular;
+            }
+            else if (member.name == "ambient") {
+              bind._offset = 4 * Shader::LA_ambient;
+            }
+            else if (member.name == "diffuse") {
+              bind._offset = 4 * Shader::LA_diffuse;
+            }
+            else if (member.name == "position") {
+              bind._offset = 4 * Shader::LA_position;
+            }
+            else if (member.name == "halfVector") {
+              bind._offset = 4 * Shader::LA_half_vector;
+            }
+            else if (member.name == "spotDirection") {
+              bind._offset = 4 * Shader::LA_spot_direction;
+            }
+            else if (member.name == "spotCosCutoff") {
+              bind._offset = 4 * Shader::LA_spot_params;
+              expected = SMP_scalar;
+            }
+            else if (member.name == "spotCutoff") {
+              bind._offset = 4 * Shader::LA_spot_params + 1;
+              expected = SMP_scalar;
+            }
+            else if (member.name == "spotExponent") {
+              bind._offset = 4 * Shader::LA_spot_params + 2;
+              expected = SMP_scalar;
+            }
+            else if (member.name == "attenuation") {
+              bind._offset = 4 * Shader::LA_attenuation;
+              expected = SMP_vec3;
+            }
+            else if (member.name == "constantAttenuation") {
+              bind._offset = 4 * Shader::LA_attenuation;
+              expected = SMP_scalar;
+            }
+            else if (member.name == "linearAttenuation") {
+              bind._offset = 4 * Shader::LA_attenuation + 1;
+              expected = SMP_scalar;
+            }
+            else if (member.name == "quadraticAttenuation") {
+              bind._offset = 4 * Shader::LA_attenuation + 2;
+              expected = SMP_scalar;
+            }
+            else if (member.name == "radius") {
+              bind._offset = 4 * Shader::LA_attenuation + 3;
+              expected = SMP_scalar;
+            }
+            else {
+              shader_cat.error()
+                << "Invalid light struct member " << member.name << "\n";
+              return false;
+            }
+
+            // It's okay to declare as vec3 if we allow vec4.
+            if (bind._piece != expected && (expected != SMP_vec4 || bind._piece != SMP_vec3)) {
+              shader_cat.error()
+                << "p3d_LightSource[]." << member.name << " has unexpected type "
+                << bind._piece << ", expected " << expected << "\n";
+              return false;
+            }
           }
           for (bind._index = 0; bind._index < (int)array->get_num_elements(); ++bind._index) {
             cp_add_mat_spec(bind);
@@ -1946,8 +2060,11 @@ bind_parameter(const Parameter &param) {
     }
     else if (pieces[1] == "FrameNumber") {
       if (type == ::ShaderType::int_type) {
-        _frame_number_loc = param._location;
-        return true;
+        bind._piece = SMP_scalar;
+        bind._func = SMF_first;
+        bind._part[0] = SMO_frame_number;
+        bind._part[1] = SMO_identity;
+        bind._scalar_type = ShaderType::ST_int;
       } else {
         return report_parameter_error(name, type, "expected int");
       }
@@ -2202,6 +2319,7 @@ bind_parameter(const Parameter &param) {
         bind._arg[0] = nullptr;
         bind._part[1] = SMO_identity;
         bind._arg[1] = nullptr;
+        bind._offset = FA_params;
       }
       else if (pieces[1] == "fogcolor") {
         if (!expect_float_vector(name, type, 3, 4)) {
@@ -2209,10 +2327,11 @@ bind_parameter(const Parameter &param) {
         }
         bind._piece = SMP_vec4;
         bind._func = SMF_first;
-        bind._part[0] = SMO_attr_fogcolor;
+        bind._part[0] = SMO_attr_fog;
         bind._arg[0] = nullptr;
         bind._part[1] = SMO_identity;
         bind._arg[1] = nullptr;
+        bind._offset = FA_color;
       }
       else if (pieces[1] == "ambient") {
         if (!expect_float_vector(name, type, 3, 4)) {
@@ -2243,10 +2362,11 @@ bind_parameter(const Parameter &param) {
           return false;
         }
         bind._func = SMF_first;
-        bind._part[0] = SMO_light_source_i_vec_attrib;
-        bind._arg[0] = InternalName::make("specular");
+        bind._part[0] = SMO_light_source_i;
+        bind._arg[0] = nullptr;
         bind._part[1] = SMO_identity;
         bind._arg[1] = nullptr;
+        bind._offset = LA_specular;
         bind._index = atoi(pieces[1].c_str() + 5);
 
         if (type->as_vector()->get_num_components() == 3) {
@@ -2852,17 +2972,47 @@ bind_parameter(const Parameter &param) {
     }
   }
 
-  ShaderPtrSpec bind;
-  if (type->as_scalar_type(bind._type, bind._dim[0], bind._dim[1], bind._dim[2])) {
+  ShaderMatSpec bind;
+  uint32_t arg_dim[3];
+  if (type->as_scalar_type(bind._scalar_type, arg_dim[0], arg_dim[1], arg_dim[2])) {
     bind._id = param;
-    bind._arg = name;
+    bind._func = SMF_shader_input_ptr;
+    bind._part[0] = SMO_INVALID;
+    bind._part[1] = SMO_INVALID;
+    bind._arg[0] = name;
+    bind._arg[1] = nullptr;
+    bind._array_count = arg_dim[0];
+
+    if (arg_dim[0] > 1 && arg_dim[1] > 1 && arg_dim[1] != arg_dim[2]) {
+      shader_cat.error()
+        << "Non-square matrix arrays are not supported in custom shader inputs\n";
+      return false;
+    }
+
+    if (arg_dim[1] >= 4) {
+      if (arg_dim[2] == 4) {
+        bind._piece = type->as_array() ? SMP_mat4_array : SMP_mat4_whole;
+      } else {
+        bind._piece = SMP_mat4_upper4x3;
+      }
+    } else if (arg_dim[1] > 1) {
+      if (arg_dim[2] == 4) {
+        bind._piece = SMP_mat4_upper3x4;
+      } else {
+        bind._piece = type->as_array() ? SMP_mat3_array : SMP_mat3_whole;
+      }
+    } else if (type->as_array()) {
+      bind._piece = (ShaderMatPiece)(SMP_scalar_array + (arg_dim[2] - 1));
+    } else {
+      bind._piece = (ShaderMatPiece)(SMP_scalar + (arg_dim[2] - 1));
+    }
 
     //if (k_prefix) {
     //  // Backward compatibility, disables certain checks.
     //  bind._dim[0] = -1;
     //}
 
-    _ptr_spec.push_back(std::move(bind));
+    cp_add_mat_spec(bind);
     return true;
   }
 

+ 49 - 13
panda/src/gobj/shader.h

@@ -33,7 +33,7 @@
 #include "pta_LVecBase3.h"
 #include "pta_LVecBase2.h"
 #include "pStatCollector.h"
-#include "epvector.h"
+#include "pvector.h"
 #include "asyncFuture.h"
 #include "shaderModule.h"
 #include "copyOnWritePointer.h"
@@ -187,7 +187,6 @@ public:
     SMO_view_to_apiclip_x,
 
     SMO_attr_fog,
-    SMO_attr_fogcolor,
 
     SMO_frame_number,
     SMO_frame_time,
@@ -197,7 +196,8 @@ public:
     SMO_vec_constant_x_attrib,
 
     SMO_light_ambient,
-    SMO_light_source_i_vec_attrib,
+    SMO_light_source_i,
+    SMO_light_source_i_packed,
     SMO_apiview_to_apiclip_light_source_i,
 
     SMO_light_product_i_ambient,
@@ -214,14 +214,9 @@ public:
 
     SMO_inv_texmat_i,
 
-    // Additional properties for PBR materials
-    SMO_attr_material2,
-
     // Hack for text rendering.  Don't use in user shaders.
     SMO_tex_is_alpha_i,
 
-    SMO_light_source_i_packed,
-
     // Texture scale component of texture matrix.
     SMO_texscale_i,
 
@@ -261,7 +256,12 @@ public:
     SMP_vec2,
     SMP_vec3,
     SMP_vec4,
+    SMP_scalar_array,
+    SMP_vec2_array,
+    SMP_vec3_array,
     SMP_vec4_array,
+    SMP_mat3_whole,
+    SMP_mat3_array,
     SMP_mat4_whole,
     SMP_mat4_array,
     SMP_mat4_transpose,
@@ -304,6 +304,7 @@ public:
     SMF_transform_dlight,
     SMF_transform_plight,
     SMF_transform_slight,
+    SMF_shader_input_ptr,
   };
 
   struct Parameter {
@@ -318,9 +319,43 @@ public:
     SPT_double = ScalarType::ST_double,
     SPT_int = ScalarType::ST_int,
     SPT_uint = ScalarType::ST_uint,
+    SPT_bool = ScalarType::ST_bool,
     SPT_unknown = ScalarType::ST_unknown,
   };
 
+  // Attributes (vec4) of the material structure.
+  enum MaterialAttribute {
+    MA_ambient,
+    MA_diffuse,
+    MA_emission,
+    MA_specular, // shininess in w
+    MA_base_color,
+    MA_metallic_ior_roughness,
+    MA_COUNT,
+  };
+
+  // Attributes (vec4) of the light structure.
+  enum LightAttribute {
+    LA_color,
+    LA_specular,
+    LA_ambient,
+    LA_diffuse,
+    LA_position,
+    LA_half_vector,
+    LA_spot_direction,
+    LA_spot_params, // spotCosCutoff, spotCutoff, spotExponent
+    LA_attenuation, // and radius
+    LA_shadow_view_matrix, // mat4
+    LA_COUNT = LA_shadow_view_matrix + 4,
+  };
+
+  // Attributes (vec4) of the fog structure.
+  enum FogAttribute {
+    FA_params, // exp density, start, end, scale
+    FA_color,
+    FA_COUNT,
+  };
+
   // Container structure for data of parameters ShaderPtrSpec.
   struct ShaderPtrData {
   private:
@@ -393,13 +428,13 @@ public:
     ShaderMatFunc     _func;
     ShaderMatInput    _part[2];
     CPT(InternalName) _arg[2];
-    LMatrix4          _value;
     int               _dep = SSD_NONE;
     int               _index = 0;
     ShaderMatPiece    _piece;
     int               _offset = 0;
-    int               _size = 1;
     int               _array_count = 1;
+    int               _num_rows = 1;
+    int               _num_cols = 4;
     ScalarType        _scalar_type = ScalarType::ST_float;
   };
 
@@ -480,10 +515,12 @@ protected:
                                 vector_string &pieces, int &next,
                                 ShaderMatSpec &spec, bool fromflag);
   int cp_dependency(ShaderMatInput inp);
+  int cp_size(ShaderMatInput inp);
 
 public:
   void cp_add_mat_spec(ShaderMatSpec &spec);
   size_t cp_get_mat_cache_size() const;
+  size_t cp_get_mat_scratch_size(bool pad_rows) const;
 
   void clear_parameters();
 
@@ -494,14 +531,13 @@ public:
   INLINE const std::string &get_debug_name() const;
 
 public:
-  pvector<ShaderPtrSpec> _ptr_spec;
-  epvector<ShaderMatSpec> _mat_spec;
+  pvector<ShaderMatSpec> _mat_spec;
   pvector<ShaderTexSpec> _tex_spec;
   pvector<ShaderImgSpec> _img_spec;
   pvector<ShaderVarSpec> _var_spec;
   pvector<ShaderMatPart> _mat_parts;
+  int _mat_cache_deps = 0;
   int _mat_deps = 0;
-  int _mat_cache_size = 0;
 
   // These are here because we don't support passing these via ShaderMatSpec yet
   int _frame_number_loc = -1;

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

@@ -462,7 +462,7 @@ get_shader_input_ptr(const InternalName *id, Shader::ShaderPtrData &data) const
         else if (param->is_of_type(ParamVecBase4d::get_class_type())) {
           data._ptr = (void *)((const ParamVecBase4d *)param)->get_value().get_data();
           data._size = 4;
-          data._type = ShaderType::ST_float;
+          data._type = ShaderType::ST_double;
           return true;
         }
       }

+ 411 - 14
tests/display/test_glsl_shader.py

@@ -390,14 +390,16 @@ def test_glsl_bool(gsg):
 
 
 def test_glsl_mat3(gsg):
-    param1 = core.LMatrix4(core.LMatrix3(1, 2, 3, 4, 5, 6, 7, 8, 9))
+    param1 = core.LMatrix4f(core.LMatrix3f(1, 2, 3, 4, 5, 6, 7, 8, 9))
+    param2 = core.LMatrix4d(core.LMatrix3d(10, 11, 12, 13, 14, 15, 16, 17, 18))
 
-    param2 = core.NodePath("param2")
-    param2.set_mat(core.LMatrix3(10, 11, 12, 13, 14, 15, 16, 17, 18))
+    param3 = core.NodePath("param3")
+    param3.set_mat(core.LMatrix3(19, 20, 21, 22, 23, 24, 25, 26, 27))
 
     preamble = """
     uniform mat3 param1;
     uniform mat3 param2;
+    uniform mat3 param3;
     """
     code = """
     assert(param1[0] == vec3(1, 2, 3));
@@ -406,23 +408,29 @@ def test_glsl_mat3(gsg):
     assert(param2[0] == vec3(10, 11, 12));
     assert(param2[1] == vec3(13, 14, 15));
     assert(param2[2] == vec3(16, 17, 18));
+    assert(param3[0] == vec3(19, 20, 21));
+    assert(param3[1] == vec3(22, 23, 24));
+    assert(param3[2] == vec3(25, 26, 27));
     """
-    run_glsl_test(gsg, code, preamble, {'param1': param1, 'param2': param2})
+    run_glsl_test(gsg, code, preamble,
+        {'param1': param1, 'param2': param2, 'param3': param3})
 
 
 def test_glsl_mat4(gsg):
-    param1 = core.LMatrix4(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)
+    param1 = core.LMatrix4f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)
+    param2 = core.LMatrix4d(17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32)
 
-    param2 = core.NodePath("param2")
-    param2.set_mat(core.LMatrix4(
-        17, 18, 19, 20,
-        21, 22, 23, 24,
-        25, 26, 27, 28,
-        29, 30, 31, 32))
+    param3 = core.NodePath("param3")
+    param3.set_mat(core.LMatrix4(
+        33, 34, 35, 36,
+        37, 38, 39, 40,
+        41, 42, 43, 44,
+        45, 46, 47, 48))
 
     preamble = """
     uniform mat4 param1;
     uniform mat4 param2;
+    uniform mat4 param3;
     """
     code = """
     assert(param1[0] == vec4(1, 2, 3, 4));
@@ -433,8 +441,56 @@ def test_glsl_mat4(gsg):
     assert(param2[1] == vec4(21, 22, 23, 24));
     assert(param2[2] == vec4(25, 26, 27, 28));
     assert(param2[3] == vec4(29, 30, 31, 32));
+    assert(param3[0] == vec4(33, 34, 35, 36));
+    assert(param3[1] == vec4(37, 38, 39, 40));
+    assert(param3[2] == vec4(41, 42, 43, 44));
+    assert(param3[3] == vec4(45, 46, 47, 48));
     """
-    run_glsl_test(gsg, code, preamble, {'param1': param1, 'param2': param2})
+    run_glsl_test(gsg, code, preamble,
+        {'param1': param1, 'param2': param2, 'param3': param3})
+
+
+def test_glsl_mat3x4(gsg):
+    param1 = core.LMatrix4f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)
+    param2 = core.LMatrix4d(17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32)
+
+    preamble = """
+    uniform mat3x4 param1;
+    uniform mat3x4 param2;
+    """
+    code = """
+    assert(param1[0] == vec4(1, 2, 3, 4));
+    assert(param1[1] == vec4(5, 6, 7, 8));
+    assert(param1[2] == vec4(9, 10, 11, 12));
+    assert(param2[0] == vec4(17, 18, 19, 20));
+    assert(param2[1] == vec4(21, 22, 23, 24));
+    assert(param2[2] == vec4(25, 26, 27, 28));
+    """
+    run_glsl_test(gsg, code, preamble,
+        {'param1': param1, 'param2': param2})
+
+
+def test_glsl_mat4x3(gsg):
+    param1 = core.LMatrix4f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)
+    param2 = core.LMatrix4d(17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32)
+
+    preamble = """
+    uniform mat4x3 param1;
+    uniform mat4x3 param2;
+    uniform mat4x3 param3;
+    """
+    code = """
+    assert(param1[0] == vec3(1, 2, 3));
+    assert(param1[1] == vec3(5, 6, 7));
+    assert(param1[2] == vec3(9, 10, 11));
+    assert(param1[3] == vec3(13, 14, 15));
+    assert(param2[0] == vec3(17, 18, 19));
+    assert(param2[1] == vec3(21, 22, 23));
+    assert(param2[2] == vec3(25, 26, 27));
+    assert(param2[3] == vec3(29, 30, 31));
+    """
+    run_glsl_test(gsg, code, preamble,
+        {'param1': param1, 'param2': param2})
 
 
 def test_glsl_pta_int(gsg):
@@ -465,8 +521,106 @@ def test_glsl_pta_ivec4(gsg):
     run_glsl_test(gsg, code, preamble, {'pta': pta})
 
 
-def test_glsl_pta_mat4(gsg):
-    pta = core.PTA_LMatrix4f((
[email protected]("type", (core.PTA_LVecBase3f, core.PTA_LVecBase3d, core.PTA_LVecBase3i))
+def test_glsl_pta_vec3(gsg, type):
+    pta = type((
+        (0, 1, 2),
+        (3, 4, 5),
+        (6, 7, 8),
+    ))
+
+    preamble = """
+    uniform vec3 pta[3];
+    """
+    code = """
+    assert(pta[0] == vec3(0, 1, 2));
+    assert(pta[1] == vec3(3, 4, 5));
+    assert(pta[2] == vec3(6, 7, 8));
+    """
+    run_glsl_test(gsg, code, preamble, {'pta': pta})
+
+
[email protected]("type", (core.PTA_LVecBase3f, core.PTA_LVecBase3d, core.PTA_LVecBase3i))
+def test_glsl_pta_dvec3(gsg, type):
+    pta = type((
+        (0, 1, 2),
+        (3, 4, 5),
+        (6, 7, 8),
+    ))
+
+    preamble = """
+    uniform dvec3 pta[3];
+    """
+    code = """
+    assert(pta[0] == vec3(0, 1, 2));
+    assert(pta[1] == vec3(3, 4, 5));
+    assert(pta[2] == vec3(6, 7, 8));
+    """
+    run_glsl_test(gsg, code, preamble, {'pta': pta})
+
+
[email protected]("type", (core.PTA_LVecBase4f, core.PTA_LVecBase4d, core.PTA_LVecBase4i))
+def test_glsl_pta_vec4(gsg, type):
+    pta = type((
+        (0, 1, 2, 3),
+        (4, 5, 6, 7),
+        (8, 9, 10, 11),
+    ))
+
+    preamble = """
+    uniform vec4 pta[4];
+    """
+    code = """
+    assert(pta[0] == vec4(0, 1, 2, 3));
+    assert(pta[1] == vec4(4, 5, 6, 7));
+    assert(pta[2] == vec4(8, 9, 10, 11));
+    """
+    run_glsl_test(gsg, code, preamble, {'pta': pta})
+
+
[email protected]("type", (core.PTA_LVecBase4f, core.PTA_LVecBase4d, core.PTA_LVecBase4i))
+def test_glsl_pta_dvec4(gsg, type):
+    pta = type((
+        (0, 1, 2, 3),
+        (4, 5, 6, 7),
+        (8, 9, 10, 11),
+    ))
+
+    preamble = """
+    uniform dvec4 pta[4];
+    """
+    code = """
+    assert(pta[0] == dvec4(0, 1, 2, 3));
+    assert(pta[1] == dvec4(4, 5, 6, 7));
+    assert(pta[2] == dvec4(8, 9, 10, 11));
+    """
+    run_glsl_test(gsg, code, preamble, {'pta': pta})
+
+
[email protected]("type", (core.PTA_LMatrix3f, core.PTA_LMatrix3d))
+def test_glsl_pta_mat3(gsg, type):
+    pta = type((
+        (0, 1, 2, 3, 4, 5, 6, 7, 8),
+        (9, 10, 11, 12, 13, 14, 15, 16, 17),
+    ))
+
+    preamble = """
+    uniform mat3 pta[2];
+    """
+    code = """
+    assert(pta[0][0] == vec3(0, 1, 2));
+    assert(pta[0][1] == vec3(3, 4, 5));
+    assert(pta[0][2] == vec3(6, 7, 8));
+    assert(pta[1][0] == vec3(9, 10, 11));
+    assert(pta[1][1] == vec3(12, 13, 14));
+    assert(pta[1][2] == vec3(15, 16, 17));
+    """
+    run_glsl_test(gsg, code, preamble, {'pta': pta})
+
+
[email protected]("type", (core.PTA_LMatrix4f, core.PTA_LMatrix4d))
+def test_glsl_pta_mat4(gsg, type):
+    pta = type((
         (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15),
         (16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31),
     ))
@@ -671,6 +825,26 @@ def test_glsl_light(gsg):
     })
 
 
+def test_glsl_named_light_source(gsg):
+    spot = core.Spotlight("spot")
+    spot.get_lens().set_fov(90, 90)
+    spot.set_color((1, 2, 3, 4))
+    spot.set_specular_color((5, 6, 7, 8))
+
+    preamble = """
+    struct p3d_LightSourceParameters {
+      vec4 color;
+      vec4 specular;
+    };
+    uniform p3d_LightSourceParameters spot;
+    """
+    code = """
+    assert(spot.color == vec4(1, 2, 3, 4));
+    assert(spot.specular == vec4(5, 6, 7, 8));
+    """
+    run_glsl_test(gsg, code, preamble, {'spot': core.NodePath(spot)})
+
+
 def test_glsl_state_light(gsg):
     preamble = """
     uniform struct p3d_LightSourceParameters {
@@ -740,6 +914,229 @@ def test_glsl_state_light(gsg):
     run_glsl_test(gsg, code, preamble, state=state)
 
 
+def test_glsl_state_light_source(gsg):
+    spot = core.Spotlight("spot")
+    spot.priority = 3
+    spot.get_lens().set_fov(120, 120)
+    spot.set_color((1, 2, 3, 4))
+    spot.set_specular_color((5, 6, 7, 8))
+    spot.attenuation = (23, 24, 25)
+    spot.exponent = 26
+
+    dire = core.DirectionalLight("dire")
+    dire.priority = 2
+    dire.set_color((9, 10, 11, 12))
+    dire.set_specular_color((13, 14, 15, 16))
+    dire.direction = (17, 18, 19)
+
+    preamble = """
+    struct p3d_LightSourceParameters {
+      vec4 color;
+      vec4 specular;
+      vec4 ambient;
+      vec4 diffuse;
+      vec4 position;
+      vec3 attenuation;
+      float constantAttenuation;
+      float linearAttenuation;
+      float quadraticAttenuation;
+      float spotExponent;
+      float spotCosCutoff;
+      float spotCutoff;
+      mat4 shadowViewMatrix;
+    };
+    uniform p3d_LightSourceParameters p3d_LightSource[3];
+    """
+    code = """
+    assert(p3d_LightSource[0].color == vec4(1, 2, 3, 4));
+    assert(p3d_LightSource[0].specular == vec4(5, 6, 7, 8));
+    assert(p3d_LightSource[0].ambient == vec4(0, 0, 0, 1));
+    assert(p3d_LightSource[0].diffuse == vec4(1, 2, 3, 4));
+    assert(p3d_LightSource[0].position == vec4(20, 21, 22, 1));
+    assert(p3d_LightSource[0].attenuation == vec3(23, 24, 25));
+    assert(p3d_LightSource[0].constantAttenuation == 23);
+    assert(p3d_LightSource[0].linearAttenuation == 24);
+    assert(p3d_LightSource[0].quadraticAttenuation == 25);
+    assert(p3d_LightSource[0].spotExponent == 26);
+    assert(p3d_LightSource[0].spotCosCutoff > 0.499);
+    assert(p3d_LightSource[0].spotCosCutoff < 0.501);
+    assert(p3d_LightSource[0].spotCutoff == 60);
+    assert(p3d_LightSource[0].shadowViewMatrix[0][0] > 0.2886);
+    assert(p3d_LightSource[0].shadowViewMatrix[0][0] < 0.2887);
+    assert(p3d_LightSource[0].shadowViewMatrix[0][1] == 0);
+    assert(p3d_LightSource[0].shadowViewMatrix[0][2] == 0);
+    assert(p3d_LightSource[0].shadowViewMatrix[0][3] == 0);
+    assert(p3d_LightSource[0].shadowViewMatrix[1][0] == 0);
+    assert(p3d_LightSource[0].shadowViewMatrix[1][1] > 0.2886);
+    assert(p3d_LightSource[0].shadowViewMatrix[1][1] < 0.2887);
+    assert(p3d_LightSource[0].shadowViewMatrix[1][2] == 0);
+    assert(p3d_LightSource[0].shadowViewMatrix[1][3] == 0);
+    assert(p3d_LightSource[0].shadowViewMatrix[2][0] == -0.5);
+    assert(p3d_LightSource[0].shadowViewMatrix[2][1] == -0.5);
+    assert(p3d_LightSource[0].shadowViewMatrix[2][2] > -1.00002);
+    assert(p3d_LightSource[0].shadowViewMatrix[2][2] < -1.0);
+    assert(p3d_LightSource[0].shadowViewMatrix[2][3] == -1);
+    assert(p3d_LightSource[0].shadowViewMatrix[3][0] > -16.2736);
+    assert(p3d_LightSource[0].shadowViewMatrix[3][0] < -16.2734);
+    assert(p3d_LightSource[0].shadowViewMatrix[3][1] > -16.8510);
+    assert(p3d_LightSource[0].shadowViewMatrix[3][1] < -16.8508);
+    assert(p3d_LightSource[0].shadowViewMatrix[3][2] > -22.0003);
+    assert(p3d_LightSource[0].shadowViewMatrix[3][2] < -22.0001);
+    assert(p3d_LightSource[0].shadowViewMatrix[3][3] > -21.0001);
+    assert(p3d_LightSource[0].shadowViewMatrix[3][3] < -20.9999);
+    assert(p3d_LightSource[1].color == vec4(9, 10, 11, 12));
+    assert(p3d_LightSource[1].specular == vec4(13, 14, 15, 16));
+    assert(p3d_LightSource[1].diffuse == vec4(9, 10, 11, 12));
+    assert(p3d_LightSource[1].ambient == vec4(0, 0, 0, 1));
+    assert(p3d_LightSource[1].position == vec4(-17, -18, -19, 0));
+    assert(p3d_LightSource[1].attenuation == vec3(1, 0, 0));
+    assert(p3d_LightSource[1].constantAttenuation == 1);
+    assert(p3d_LightSource[1].linearAttenuation == 0);
+    assert(p3d_LightSource[1].quadraticAttenuation == 0);
+    assert(p3d_LightSource[1].spotExponent == 0);
+    assert(p3d_LightSource[1].spotCosCutoff == -1);
+    assert(p3d_LightSource[2].color == vec4(0, 0, 0, 1));
+    assert(p3d_LightSource[2].specular == vec4(0, 0, 0, 1));
+    assert(p3d_LightSource[2].diffuse == vec4(0, 0, 0, 1));
+    assert(p3d_LightSource[2].ambient == vec4(0, 0, 0, 1));
+    assert(p3d_LightSource[2].position == vec4(0, 0, 1, 0));
+    assert(p3d_LightSource[2].attenuation == vec3(1, 0, 0));
+    assert(p3d_LightSource[2].constantAttenuation == 1);
+    assert(p3d_LightSource[2].linearAttenuation == 0);
+    assert(p3d_LightSource[2].quadraticAttenuation == 0);
+    assert(p3d_LightSource[2].spotExponent == 0);
+    assert(p3d_LightSource[2].spotCosCutoff == -1);
+    """
+
+    node = core.NodePath("state")
+    spot_path = node.attach_new_node(spot)
+    spot_path.set_pos(20, 21, 22)
+    node.set_light(spot_path)
+
+    dire_path = node.attach_new_node(dire)
+    node.set_light(dire_path)
+
+    run_glsl_test(gsg, code, preamble, state=node.get_state())
+
+
+def test_glsl_state_material(gsg):
+    mat = core.Material("mat")
+    mat.ambient = (1, 2, 3, 4)
+    mat.diffuse = (5, 6, 7, 8)
+    mat.emission = (9, 10, 11, 12)
+    mat.specular = (13, 14, 15, 0)
+    mat.shininess = 16
+    mat.metallic = 0.5
+    mat.refractive_index = 21
+
+    preamble = """
+    struct p3d_MaterialParameters {
+      vec4 ambient;
+      vec4 diffuse;
+      vec4 emission;
+      vec3 specular;
+      float shininess;
+      float metallic;
+      float refractiveIndex;
+    };
+    uniform p3d_MaterialParameters p3d_Material;
+    """
+    code = """
+    assert(p3d_Material.ambient == vec4(1, 2, 3, 4));
+    assert(p3d_Material.diffuse == vec4(5, 6, 7, 8));
+    assert(p3d_Material.emission == vec4(9, 10, 11, 12));
+    assert(p3d_Material.specular == vec3(13, 14, 15));
+    assert(p3d_Material.shininess == 16);
+    assert(p3d_Material.metallic == 0.5);
+    assert(p3d_Material.refractiveIndex == 21);
+    """
+
+    node = core.NodePath("state")
+    node.set_material(mat)
+
+    run_glsl_test(gsg, code, preamble, state=node.get_state())
+
+
+def test_glsl_state_material_pbr(gsg):
+    mat = core.Material("mat")
+    mat.base_color = (1, 2, 3, 4)
+    mat.emission = (9, 10, 11, 12)
+    mat.roughness = 16
+    mat.metallic = 0.5
+    mat.refractive_index = 21
+
+    preamble = """
+    struct p3d_MaterialParameters {
+      vec4 baseColor;
+      vec4 emission;
+      float metallic;
+      float refractiveIndex;
+      float roughness;
+    };
+    uniform p3d_MaterialParameters p3d_Material;
+    """
+    code = """
+    assert(p3d_Material.baseColor == vec4(1, 2, 3, 4));
+    assert(p3d_Material.emission == vec4(9, 10, 11, 12));
+    assert(p3d_Material.roughness == 16);
+    assert(p3d_Material.metallic == 0.5);
+    assert(p3d_Material.refractiveIndex == 21);
+    """
+
+    node = core.NodePath("state")
+    node.set_material(mat)
+
+    run_glsl_test(gsg, code, preamble, state=node.get_state())
+
+
+def test_glsl_state_fog(gsg):
+    fog = core.Fog("fog")
+    fog.color = (1, 2, 3, 4)
+    fog.exp_density = 0.5
+    fog.set_linear_range(6, 10)
+
+    preamble = """
+    struct p3d_FogParameters {
+      vec4 color;
+      float density;
+      float start;
+      float end;
+      float scale;
+    };
+    uniform p3d_FogParameters p3d_Fog;
+    """
+    code = """
+    assert(p3d_Fog.color == vec4(1, 2, 3, 4));
+    assert(p3d_Fog.density == 0.5);
+    assert(p3d_Fog.start == 6);
+    assert(p3d_Fog.end == 10);
+    assert(p3d_Fog.scale == 0.25);
+    """
+
+    node = core.NodePath("state")
+    node.set_fog(fog)
+
+    run_glsl_test(gsg, code, preamble, state=node.get_state())
+
+
+def test_glsl_frame_number(gsg):
+    clock = core.ClockObject.get_global_clock()
+    old_frame_count = clock.get_frame_count()
+    try:
+        clock.set_frame_count(123)
+
+        preamble = """
+        uniform int osg_FrameNumber;
+        """
+        code = """
+        assert(osg_FrameNumber == 123);
+        """
+
+        run_glsl_test(gsg, code, preamble)
+    finally:
+        clock.set_frame_count(old_frame_count)
+
+
 def test_glsl_write_extract_image_buffer(gsg):
     # Tests that we can write to a buffer texture on the GPU, and then extract
     # the data on the CPU.  We test two textures since there was in the past a

+ 1 - 1
tests/tkwidgets/test_Dial.py

@@ -1,5 +1,5 @@
-import tkinter as tk
 import pytest
+tk = pytest.importorskip('tkinter')
 pytest.importorskip('Pmw')
 from direct.tkwidgets.Dial import Dial