瀏覽代碼

More code for shader matrix computation

Josh Yelon 20 年之前
父節點
當前提交
5fa80f8c40

+ 192 - 170
panda/src/display/graphicsStateGuardian.cxx

@@ -105,8 +105,6 @@ GraphicsStateGuardian(const FrameBufferProperties &properties,
   _current_lens = (Lens *)NULL;
   _projection_mat = TransformState::make_identity();
   _projection_mat_inv = TransformState::make_identity();
-  _clip_to_view = TransformState::make_identity();
-  _view_to_clip = TransformState::make_identity();
   
   _needs_reset = true;
   _is_valid = false;
@@ -373,10 +371,6 @@ set_scene(SceneSetup *scene_setup) {
     return false;
   }
   _projection_mat_inv = _projection_mat->get_inverse();
-  _view_to_clip = TransformState::make_mat(_current_lens->
-       get_projection_mat(_coordinate_system, _current_stereo_channel));
-  _clip_to_view = TransformState::make_mat(_current_lens->
-       get_projection_mat_inv(_coordinate_system, _current_stereo_channel));
   return prepare_lens();
 }
 
@@ -677,177 +671,205 @@ clear(DrawableRegion *clearable) {
 //               shoehorning them into a matrix.  In this way, we avoid
 //               the need for a separate routine to fetch these values.
 //
-//               This routine is actually a simple pcode-interpreter,
-//               and the ShaderMatSpec is actually a small pcode-array.
-//               This makes it possible to express requests for a
-//               large variety of values, and a large variety of
-//               possible conversions.
+//               If "altered" is false, that means you promise that
+//               this ShaderMatSpec has been evaluated before, and that
+//               since the last time this ShaderMatSpec was evaluated,
+//               that no part of the render state has changed except
+//               the external and internal transforms.
+//
 ////////////////////////////////////////////////////////////////////
-bool GraphicsStateGuardian::
-fetch_specified_value(const ShaderContext::ShaderMatSpec &spec, LMatrix4f &result) {
-  pvector<LMatrix4f> stack;
-  int pc = 0;
-  int ac = 0;
-  while (pc < (int)spec._opcodes.size()) {
-    int opcode = spec._opcodes[pc++];
-    switch(opcode) {
-    case ShaderContext::SMO_identity: {
-      stack.push_back(LMatrix4f::ident_mat());
-      break;
-    }
-    case ShaderContext::SMO_modelview: {
-      stack.push_back(_internal_transform->get_mat());
-      break;
-    }
-    case ShaderContext::SMO_projection: {
-      stack.push_back(_projection_mat->get_mat());
-      break;
-    }
-    case ShaderContext::SMO_modelproj: {
-      stack.push_back(_internal_transform->get_mat() * _projection_mat->get_mat());
-      break;
-    }
-    case ShaderContext::SMO_window_size: {
-      stack.push_back(LMatrix4f::translate_mat(_current_display_region->get_pixel_width(),
-                                               _current_display_region->get_pixel_height(),
-                                               0.0));
-      break;
-    }
-    case ShaderContext::SMO_pixel_size: {
-      stack.push_back(LMatrix4f::translate_mat(_current_display_region->get_pixel_width(),
-                                               _current_display_region->get_pixel_height(),
-                                               0.0));
-      break;
-    }
-    case ShaderContext::SMO_card_center: {
-      int px = _current_display_region->get_pixel_width();
-      int py = _current_display_region->get_pixel_height();
-      stack.push_back(LMatrix4f::translate_mat((px*0.5) / Texture::up_to_power_2(px),
-                                               (py*0.5) / Texture::up_to_power_2(py),
-                                               0.0));
-      break;
-    }
-    case ShaderContext::SMO_mat_constant_x: {
-      const ShaderInput *input = _target._shader->get_shader_input(spec._args[ac++]);
-      if (input->get_nodepath().is_empty()) {
-        return false;
-      }
-      stack.push_back(input->get_nodepath().node()->get_transform()->get_mat());
-      break;
-    }
-    case ShaderContext::SMO_vec_constant_x: {
-      const ShaderInput *input = _target._shader->get_shader_input(spec._args[ac++]);
-      const float *data = input->get_vector().get_data();
-      stack.push_back(LMatrix4f(data[0],data[1],data[2],data[3],
-                                data[0],data[1],data[2],data[3],
-                                data[0],data[1],data[2],data[3],
-                                data[0],data[1],data[2],data[3]));
-      break;
-    }
-    case ShaderContext::SMO_world_to_view: {
-      stack.push_back(get_scene()->get_world_transform()->get_mat());
-      break;
-    }
-    case ShaderContext::SMO_view_to_world_C: {
-      stack.back() *= get_scene()->get_camera_transform()->get_mat();
-      break;
-    }
-    case ShaderContext::SMO_model_to_view: {
-      stack.push_back(_external_transform->get_mat());
-      break;
-    }
-    case ShaderContext::SMO_view_to_model_C: {
-      stack.back() *= invert(_external_transform->get_mat());
-      break;
-    }
-    case ShaderContext::SMO_apiview_to_view: {
-      stack.push_back(_inv_cs_transform->get_mat());
-      break;
-    }
-    case ShaderContext::SMO_view_to_apiview_C: {
-      stack.back() *= _cs_transform->get_mat();
-      break;
-    }
-    case ShaderContext::SMO_clip_to_view: {
-      stack.push_back(_clip_to_view->get_mat());
-      break;
-    }
-    case ShaderContext::SMO_view_to_clip_C: {
-      stack.back() *= _view_to_clip->get_mat();
-      break;
-    }
-    case ShaderContext::SMO_apiclip_to_view: {
-      stack.push_back(_projection_mat_inv->get_mat() * _inv_cs_transform->get_mat());
-      break;
-    }
-    case ShaderContext::SMO_view_to_apiclip_C: {
-      stack.back() *= _cs_transform->get_mat() * _projection_mat->get_mat();
-      break;
-    }
-    case ShaderContext::SMO_view_x_to_view: {
-      const ShaderInput *input = _target._shader->get_shader_input(spec._args[ac++]);
-      nassertr(!input->get_nodepath().is_empty(), false);
-      stack.push_back(input->get_nodepath().get_net_transform()->get_mat() *
-                      get_scene()->get_world_transform()->get_mat());
-      break;
-    }
-    case ShaderContext::SMO_view_to_view_x_C: {
-      const ShaderInput *input = _target._shader->get_shader_input(spec._args[ac++]);
-      nassertr(!input->get_nodepath().is_empty(), false);
-      stack.back()*= (get_scene()->get_camera_transform()->get_mat() *
-                      invert(input->get_nodepath().get_net_transform()->get_mat()));
-      break;
-    }
-    case ShaderContext::SMO_apiview_x_to_view: {
-      const ShaderInput *input = _target._shader->get_shader_input(spec._args[ac++]);
-      nassertr(!input->get_nodepath().is_empty(), false);
-      stack.push_back(LMatrix4f::convert_mat(_internal_coordinate_system, _coordinate_system) *
-                      input->get_nodepath().get_net_transform()->get_mat() *
-                      get_scene()->get_world_transform()->get_mat());
-      break;
-    }
-    case ShaderContext::SMO_view_to_apiview_x_C: {
-      const ShaderInput *input = _target._shader->get_shader_input(spec._args[ac++]);
-      nassertr(!input->get_nodepath().is_empty(), false);
-      stack.back()*= (get_scene()->get_camera_transform()->get_mat() *
-                      invert(input->get_nodepath().get_net_transform()->get_mat()) *
-                      LMatrix4f::convert_mat(_coordinate_system, _internal_coordinate_system));
-      break;
-    }
-    case ShaderContext::SMO_clip_x_to_view: {
-      const ShaderInput *input = _target._shader->get_shader_input(spec._args[ac++]);
-      nassertr(!input->get_nodepath().is_empty(), false);
-      Lens *lens = DCAST(LensNode, input->get_nodepath().node())->get_lens();
-      stack.push_back(lens->get_projection_mat_inv(_coordinate_system, _current_stereo_channel) *
-                      input->get_nodepath().get_net_transform()->get_mat() *
-                      get_scene()->get_world_transform()->get_mat());
-      break;
+const LMatrix4f *GraphicsStateGuardian::
+fetch_specified_value(ShaderContext::ShaderMatSpec &spec, bool altered) {
+  static LMatrix4f acc;
+  const LMatrix4f *val1;
+  const LMatrix4f *val2;
+  static LMatrix4f t1;
+  static LMatrix4f t2;
+  
+  switch(spec._func) {
+  case ShaderContext::SMF_compose:
+    val1 = fetch_specified_part(spec._part[0], spec._arg[0], t1);
+    val2 = fetch_specified_part(spec._part[1], spec._arg[1], t2);
+    acc.multiply(*val1, *val2);
+    return &acc;
+  case ShaderContext::SMF_compose_cache_first:
+    if (altered) {
+      spec._cache = *fetch_specified_part(spec._part[0], spec._arg[0], t1);
     }
-    case ShaderContext::SMO_view_to_clip_x_C: {
-      const ShaderInput *input = _target._shader->get_shader_input(spec._args[ac++]);
-      nassertr(!input->get_nodepath().is_empty(), false);
-      Lens *lens = DCAST(LensNode, input->get_nodepath().node())->get_lens();
-      stack.back()*= (get_scene()->get_camera_transform()->get_mat() *
-                      invert(input->get_nodepath().get_net_transform()->get_mat()) *
-                      lens->get_projection_mat(_coordinate_system, _current_stereo_channel));
-      break;
+    val2 = fetch_specified_part(spec._part[1], spec._arg[1], t2);
+    acc.multiply(spec._cache, *val2);
+    return &acc;
+  case ShaderContext::SMF_compose_cache_second:
+    if (altered) {
+      spec._cache = *fetch_specified_part(spec._part[1], spec._arg[1], t2);
     }
-    case ShaderContext::SMO_apiclip_x_to_view: {
-      // NOT IMPLEMENTED
-      break;
+    val1 = fetch_specified_part(spec._part[0], spec._arg[0], t1);
+    acc.multiply(*val1, spec._cache);
+    return &acc;
+  case ShaderContext::SMF_first:
+    return fetch_specified_part(spec._part[0], spec._arg[0], t1);
+  default:
+    // should never get here
+    return &LMatrix4f::ident_mat();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::fetch_specified_part
+//       Access: Public
+//  Description: See fetch_specified_value
+////////////////////////////////////////////////////////////////////
+const LMatrix4f *GraphicsStateGuardian::
+fetch_specified_part(ShaderContext::ShaderMatInput part, InternalName *name, LMatrix4f &t) {
+  switch(part) {
+  case ShaderContext::SMO_identity: {
+    return &LMatrix4f::ident_mat();
+  }
+  case ShaderContext::SMO_window_size: {
+    t = LMatrix4f::translate_mat(_current_display_region->get_pixel_width(),
+                                 _current_display_region->get_pixel_height(),
+                                 0.0);
+    return &t;
+  }
+  case ShaderContext::SMO_pixel_size: {
+    t = LMatrix4f::translate_mat(_current_display_region->get_pixel_width(),
+                                 _current_display_region->get_pixel_height(),
+                                 0.0);
+    return &t;
+  }
+  case ShaderContext::SMO_card_center: {
+    int px = _current_display_region->get_pixel_width();
+    int py = _current_display_region->get_pixel_height();
+    t = LMatrix4f::translate_mat((px*0.5) / Texture::up_to_power_2(px),
+                                 (py*0.5) / Texture::up_to_power_2(py),
+                                 0.0);
+    return &t;
+  }
+  case ShaderContext::SMO_mat_constant_x: {
+    const ShaderInput *input = _target._shader->get_shader_input(name);
+    if (input->get_nodepath().is_empty()) {
+      return &LMatrix4f::ident_mat();
     }
-    case ShaderContext::SMO_view_to_apiclip_x_C: {
-      // NOT IMPLEMENTED
-      break;
+    return &(input->get_nodepath().node()->get_transform()->get_mat());
+  }
+  case ShaderContext::SMO_vec_constant_x: {
+    const ShaderInput *input = _target._shader->get_shader_input(name);
+    const float *data = input->get_vector().get_data();
+    t = LMatrix4f(data[0],data[1],data[2],data[3],
+                  data[0],data[1],data[2],data[3],
+                  data[0],data[1],data[2],data[3],
+                  data[0],data[1],data[2],data[3]);
+    return &t;
+  }
+  case ShaderContext::SMO_world_to_view: {
+    return &(get_scene()->get_world_transform()->get_mat());
+    break;
+  }
+  case ShaderContext::SMO_view_to_world: {
+    return &(get_scene()->get_camera_transform()->get_mat());
+  }
+  case ShaderContext::SMO_model_to_view: {
+    return &(_external_transform->get_mat());
+  }
+  case ShaderContext::SMO_view_to_model: {
+    // DANGER: SLOW AND NOT CACHEABLE!
+    t.invert_from(_external_transform->get_mat());
+    return &t;
+  }
+  case ShaderContext::SMO_apiview_to_view: {
+    return &(_inv_cs_transform->get_mat());
+  }
+  case ShaderContext::SMO_view_to_apiview: {
+    return &(_cs_transform->get_mat());
+  }
+  case ShaderContext::SMO_clip_to_view: {
+    if (_current_lens->get_coordinate_system() == _coordinate_system) {
+      return &(_current_lens->get_projection_mat_inv(_current_stereo_channel));
+    } else {
+      t = _current_lens->get_projection_mat_inv(_current_stereo_channel) *
+        LMatrix4f::convert_mat(_current_lens->get_coordinate_system(), _coordinate_system);
+      return &t;
     }
-
-    case ShaderContext::SMO_transpose:
-      stack.back().transpose_in_place();
-      break;
+  }
+  case ShaderContext::SMO_view_to_clip: {
+    if (_current_lens->get_coordinate_system() == _coordinate_system) {
+      return &(_current_lens->get_projection_mat(_current_stereo_channel));
+    } else {
+      t = LMatrix4f::convert_mat(_coordinate_system, _current_lens->get_coordinate_system()) *
+        _current_lens->get_projection_mat(_current_stereo_channel);
+      return &t;
     }
   }
-  result = stack.back();
-  return true;
+  case ShaderContext::SMO_apiclip_to_view: {
+    t = _projection_mat_inv->get_mat() * _inv_cs_transform->get_mat();
+    return &t;
+  }
+  case ShaderContext::SMO_view_to_apiclip: {
+    t = _cs_transform->get_mat() * _projection_mat->get_mat();
+    return &t;
+  }
+  case ShaderContext::SMO_view_x_to_view: {
+    const ShaderInput *input = _target._shader->get_shader_input(name);
+    nassertr(!input->get_nodepath().is_empty(), &LMatrix4f::ident_mat());
+    t = input->get_nodepath().get_net_transform()->get_mat() *
+      get_scene()->get_world_transform()->get_mat();
+    return &t;
+  }
+  case ShaderContext::SMO_view_to_view_x: {
+    const ShaderInput *input = _target._shader->get_shader_input(name);
+    nassertr(!input->get_nodepath().is_empty(),  &LMatrix4f::ident_mat());
+    t = get_scene()->get_camera_transform()->get_mat() *
+      invert(input->get_nodepath().get_net_transform()->get_mat());
+    return &t;
+  }
+  case ShaderContext::SMO_apiview_x_to_view: {
+    const ShaderInput *input = _target._shader->get_shader_input(name);
+    nassertr(!input->get_nodepath().is_empty(), &LMatrix4f::ident_mat());
+    t = LMatrix4f::convert_mat(_internal_coordinate_system, _coordinate_system) *
+      input->get_nodepath().get_net_transform()->get_mat() *
+      get_scene()->get_world_transform()->get_mat();
+    return &t;
+  }
+  case ShaderContext::SMO_view_to_apiview_x: {
+    const ShaderInput *input = _target._shader->get_shader_input(name);
+    nassertr(!input->get_nodepath().is_empty(), &LMatrix4f::ident_mat());
+    t = (get_scene()->get_camera_transform()->get_mat() *
+         invert(input->get_nodepath().get_net_transform()->get_mat()) *
+         LMatrix4f::convert_mat(_coordinate_system, _internal_coordinate_system));
+    return &t;
+  }
+  case ShaderContext::SMO_clip_x_to_view: {
+    const ShaderInput *input = _target._shader->get_shader_input(name);
+    nassertr(!input->get_nodepath().is_empty(), &LMatrix4f::ident_mat());
+    Lens *lens = DCAST(LensNode, input->get_nodepath().node())->get_lens();
+    t = lens->get_projection_mat_inv(_current_stereo_channel) *
+      LMatrix4f::convert_mat(lens->get_coordinate_system(), _coordinate_system) *
+      input->get_nodepath().get_net_transform()->get_mat() *
+      get_scene()->get_world_transform()->get_mat();
+    return &t;
+  }
+  case ShaderContext::SMO_view_to_clip_x: {
+    const ShaderInput *input = _target._shader->get_shader_input(name);
+    nassertr(!input->get_nodepath().is_empty(), &LMatrix4f::ident_mat());
+    Lens *lens = DCAST(LensNode, input->get_nodepath().node())->get_lens();
+    t = get_scene()->get_camera_transform()->get_mat() *
+      invert(input->get_nodepath().get_net_transform()->get_mat()) *
+      LMatrix4f::convert_mat(_coordinate_system, lens->get_coordinate_system()) *
+      lens->get_projection_mat(_current_stereo_channel);
+    return &t;
+  }
+  case ShaderContext::SMO_apiclip_x_to_view: {
+    // NOT IMPLEMENTED
+    return &LMatrix4f::ident_mat();
+  }
+  case ShaderContext::SMO_view_to_apiclip_x: {
+    // NOT IMPLEMENTED
+    return &LMatrix4f::ident_mat();
+  }
+  default:
+    // should never get here
+    return &LMatrix4f::ident_mat();
+  }
 }
 
 ////////////////////////////////////////////////////////////////////

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

@@ -159,7 +159,8 @@ public:
 
   void clear(DrawableRegion *clearable);
 
-  bool fetch_specified_value(const ShaderContext::ShaderMatSpec &spec, LMatrix4f &result);
+  const LMatrix4f *fetch_specified_value(ShaderContext::ShaderMatSpec &spec, bool altered);
+  const LMatrix4f *fetch_specified_part(ShaderContext::ShaderMatInput input, InternalName *name, LMatrix4f &t);
   
   virtual void prepare_display_region(DisplayRegion *dr,
                                       Lens::StereoChannel stereo_channel);
@@ -295,8 +296,6 @@ protected:
   CPT(Lens) _current_lens;
   CPT(TransformState) _projection_mat;
   CPT(TransformState) _projection_mat_inv;
-  CPT(TransformState) _view_to_clip;
-  CPT(TransformState) _clip_to_view;
   
   CoordinateSystem _coordinate_system;
   CoordinateSystem _internal_coordinate_system;

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

@@ -2763,8 +2763,9 @@ do_issue_transform() {
     do_auto_rescale_normal();
   }
 
-  if (_current_shader_context)
+  if (_current_shader_context) {
     _current_shader_context->issue_parameters(this, false);
+  }
 
   report_my_gl_errors();
 }

+ 13 - 9
panda/src/glstuff/glShaderContext_src.cxx

@@ -442,13 +442,17 @@ unbind()
 //  Description: This function gets called whenever the RenderState
 //               or TransformState has changed, but the ShaderExpansion
 //               itself has not changed.  It loads new values into the
-//               shader's parameters.  The flag "all" is false if the
-//               only thing that has changed is the modelview matrix.
-//               In this case, only the transform-dependent parameters
-//               are reloaded.
+//               shader's parameters.
+//
+//               If "altered" is false, that means you promise that
+//               the parameters for this shader context have already
+//               been issued once, and that since the last time the
+//               parameters were issued, no part of the render
+//               state has changed except the external and internal
+//               transforms.
 ////////////////////////////////////////////////////////////////////
 void CLP(ShaderContext)::
-issue_parameters(GSG *gsg, bool all)
+issue_parameters(GSG *gsg, bool altered)
 {
 #ifdef HAVE_CGGL
   if (_cg_context == 0) {
@@ -456,11 +460,11 @@ issue_parameters(GSG *gsg, bool all)
   }
   
   for (int i=0; i<(int)_mat_spec.size(); i++) {
-    if (all || _mat_spec[i]._trans_dependent) {
-      LMatrix4f result;
+    if (altered || _mat_spec[i]._trans_dependent) {
       CGparameter p = (CGparameter)(_mat_spec[i]._parameter);
-      if (gsg->fetch_specified_value(_mat_spec[i], result)) {
-        const float *data = result.get_data();
+      const LMatrix4f *val = gsg->fetch_specified_value(_mat_spec[i], altered);
+      if (val) {
+        const float *data = val->get_data();
         switch (_mat_spec[i]._piece) {
         case SMP_whole: cgGLSetMatrixParameterfc(p, data); break;
         case SMP_transpose: cgGLSetMatrixParameterfr(p, data); break;

+ 0 - 31
panda/src/gobj/lens.I

@@ -443,34 +443,3 @@ operator << (ostream &out, const Lens &lens) {
   return out;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: Lens::get_projection_mat
-//       Access: Published
-//  Description: Returns the complete transformation matrix from a 3-d
-//               point in space to a point on the film, if such a
-//               matrix exists, or the identity matrix if the lens is
-//               nonlinear, in the specified coordinate system.
-////////////////////////////////////////////////////////////////////
-const LMatrix4f Lens::
-get_projection_mat(CoordinateSystem cs, StereoChannel channel) const {
-  return
-    LMatrix4f::convert_mat(cs, _cs) *
-    get_projection_mat(channel) *
-    LMatrix4f::convert_mat(CS_yup_right, cs);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Lens::get_projection_mat_inv
-//       Access: Published
-//  Description: Returns the matrix that transforms from a 2-d point
-//               on the film to a 3-d vector in space, if such a
-//               matrix exists, in the specified coordinate system.
-////////////////////////////////////////////////////////////////////
-const LMatrix4f Lens::
-get_projection_mat_inv(CoordinateSystem cs, StereoChannel channel) const {
-  return
-    LMatrix4f::convert_mat(cs, CS_yup_right) *
-    get_projection_mat_inv(channel) *
-    LMatrix4f::convert_mat(_cs, cs);
-}
-

+ 0 - 3
panda/src/gobj/lens.h

@@ -155,9 +155,6 @@ PUBLISHED:
   const LMatrix4f &get_projection_mat(StereoChannel channel = SC_mono) const;
   const LMatrix4f &get_projection_mat_inv(StereoChannel channel = SC_mono) const;
 
-  INLINE const LMatrix4f get_projection_mat(CoordinateSystem cs, StereoChannel channel) const;
-  INLINE const LMatrix4f get_projection_mat_inv(CoordinateSystem cs, StereoChannel channel) const;
-  
   const LMatrix4f &get_film_mat() const;
   const LMatrix4f &get_film_mat_inv() const;
 

+ 109 - 50
panda/src/gobj/shaderContext.cxx

@@ -171,35 +171,88 @@ cp_errchk_parameter_sampler(ShaderArgInfo &p)
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GLShaderContext::cp_parse_trans_clause
+//     Function: ShaderContext::cp_parse_trans_clause
 //       Access: Public
 //  Description: Parses a single clause of a "trans" parameter.
 ////////////////////////////////////////////////////////////////////
 bool ShaderContext::
-cp_parse_trans_clause(ShaderArgInfo &p, ShaderMatSpec &spec, const vector_string &pieces,
-                      int &next, ShaderMatOp ofop, ShaderMatOp op) {
+cp_parse_trans_clause(ShaderArgInfo &p, ShaderMatSpec &spec, 
+                      int part, const vector_string &pieces,
+                      int &next, ShaderMatInput ofop, ShaderMatInput op) {
   if (pieces[next+1]=="of") {
     if (pieces[next+2]=="") {
       cp_report_error(p, "'of' should be followed by a name");
       return false;
     }
-    if (ofop != SMO_noop) {
-      spec._opcodes.push_back(ofop);
-      spec._args.push_back(InternalName::make(pieces[next+2]));
-    }
+    spec._part[part] = ofop;
+    spec._arg[part] = InternalName::make(pieces[next+2]);
     next += 3;
     return true;
   } else {
-    if (op != SMO_noop) {
-      spec._opcodes.push_back(op);
-    }
+    spec._part[part] = op;
+    spec._arg[part] = NULL;
     next += 1;
     return true;
   }
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GLShaderContext::compile_parameter
+//     Function: ShaderContext::cp_optimize_mat_spec
+//       Access: Public
+//  Description: Analyzes a ShaderMatSpec and decides what it should
+//               use its cache for.  It can cache the results of any
+//               one opcode, or, it can cache the entire result.  This
+//               routine needs to be smart enough to know which
+//               data items can be correctly cached, and which cannot.
+////////////////////////////////////////////////////////////////////
+void ShaderContext::
+cp_optimize_mat_spec(ShaderMatSpec &spec) {
+
+  // If we're composing with identity, simplify.
+  if (spec._func == SMF_compose) {
+    if (spec._part[1] == SMO_identity) {
+      spec._func = SMF_first;
+    }
+  }
+  if (spec._func == SMF_compose) {
+    if (spec._part[0] == SMO_identity) {
+      spec._func = SMF_first;
+      spec._part[0] = spec._part[1];
+      spec._arg[0] = spec._arg[1];
+    }
+  }
+
+  // See if either half can be cached.
+  bool can_cache_part0 = true;
+  bool can_cache_part1 = true;
+  if ((spec._part[0] == SMO_model_to_view)||
+      (spec._part[0] == SMO_view_to_model)) {
+    can_cache_part0 = false;
+  }
+  if ((spec._part[1] == SMO_model_to_view)||
+      (spec._part[1] == SMO_view_to_model)) {
+    can_cache_part1 = false;
+  }
+  
+  // See if we can use a compose-with-cache variant.
+  if (spec._func == SMF_compose) {
+    if (can_cache_part0) {
+      spec._func = SMF_compose_cache_first;
+    } else if (can_cache_part1) {
+      spec._func = SMF_compose_cache_second;
+    }
+  }
+  
+  // Determine transform-dependence.
+  if (can_cache_part0 && can_cache_part1) {
+    spec._trans_dependent = false;
+  } else {
+    spec._trans_dependent = true;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ShaderContext::compile_parameter
 //       Access: Public
 //  Description: Analyzes a parameter and decides how to
 //               bind the parameter to some part of panda's
@@ -377,14 +430,14 @@ compile_parameter(void *reference,
     
     ShaderMatSpec bind;
     bind._parameter = reference;
-    bind._trans_dependent = false;
+    bind._func = SMF_compose;
 
     int next = 1;
     pieces.push_back("");
 
     // Decide whether this is a matrix or vector.
     if      (pieces[0]=="trans")   bind._piece = SMP_whole;
-    else if (pieces[0]=="tpose")   bind._piece = SMP_whole;
+    else if (pieces[0]=="tpose")   bind._piece = SMP_transpose;
     else if (pieces[0]=="row0")    bind._piece = SMP_row0;
     else if (pieces[0]=="row1")    bind._piece = SMP_row1;
     else if (pieces[0]=="row2")    bind._piece = SMP_row2;
@@ -393,7 +446,7 @@ compile_parameter(void *reference,
     else if (pieces[0]=="col1")    bind._piece = SMP_col1;
     else if (pieces[0]=="col2")    bind._piece = SMP_col2;
     else if (pieces[0]=="col3")    bind._piece = SMP_col3;
-    if (bind._piece == SMP_whole) {
+    if ((bind._piece == SMP_whole)||(bind._piece == SMP_transpose)) {
       if (!cp_errchk_parameter_float(p, 16, 16)) return false;
     } else {
       if (!cp_errchk_parameter_float(p, 4, 4)) return false;
@@ -405,21 +458,22 @@ compile_parameter(void *reference,
       cp_report_error(p, "argument missing");
       return false;
     } else if (pieces[next] == "world") {
-      bind._opcodes.push_back(SMO_world_to_view);
+      bind._part[0] = SMO_world_to_view;
+      bind._arg[0] = NULL;
       next += 1;
     } else if (pieces[next] == "model") {
-      ok &= cp_parse_trans_clause(p, bind, pieces, next, SMO_view_x_to_view, SMO_model_to_view);
+      ok &= cp_parse_trans_clause(p, bind, 0, pieces, next, SMO_view_x_to_view, SMO_model_to_view);
     } else if (pieces[next] == "clip") {
-      ok &= cp_parse_trans_clause(p, bind, pieces, next, SMO_clip_x_to_view, SMO_clip_to_view);
+      ok &= cp_parse_trans_clause(p, bind, 0, pieces, next, SMO_clip_x_to_view, SMO_clip_to_view);
     } else if (pieces[next] == "view") {
-      ok &= cp_parse_trans_clause(p, bind, pieces, next, SMO_view_x_to_view, SMO_identity);
+      ok &= cp_parse_trans_clause(p, bind, 0, pieces, next, SMO_view_x_to_view, SMO_identity);
     } else if (pieces[next] == "apiview") {
-      ok &= cp_parse_trans_clause(p, bind, pieces, next, SMO_apiview_x_to_view, SMO_apiview_to_view);
+      ok &= cp_parse_trans_clause(p, bind, 0, pieces, next, SMO_apiview_x_to_view, SMO_apiview_to_view);
     } else if (pieces[next] == "apiclip") {
-      ok &= cp_parse_trans_clause(p, bind, pieces, next, SMO_apiclip_x_to_view, SMO_apiclip_to_view);
+      ok &= cp_parse_trans_clause(p, bind, 0, pieces, next, SMO_apiclip_x_to_view, SMO_apiclip_to_view);
     } else {
-      bind._opcodes.push_back(SMO_view_x_to_view);
-      bind._args.push_back(InternalName::make(pieces[next]));
+      bind._part[0] = SMO_view_x_to_view;
+      bind._arg[0] = InternalName::make(pieces[next]);
       next += 1;
     }
 
@@ -428,11 +482,6 @@ compile_parameter(void *reference,
       return false;
     }
 
-    // Check for transform-dependence.
-    if (bind._opcodes.back() == SMO_model_to_view) {
-      bind._trans_dependent = true;
-    }
-
     // Check for syntactic well-formed-ness.
     if (pieces[next] != "to") {
       cp_report_error(p, "keyword 'to' expected");
@@ -446,21 +495,22 @@ compile_parameter(void *reference,
       cp_report_error(p, "argument missing");
       return false;
     } else if (pieces[next] == "world") {
-      bind._opcodes.push_back(SMO_view_to_world_C);
+      bind._part[1] = SMO_view_to_world;
+      bind._arg[1] = NULL;
       next += 1;
     } else if (pieces[next] == "model") {
-      ok &= cp_parse_trans_clause(p, bind, pieces, next, SMO_view_to_view_x_C, SMO_view_to_model_C);
+      ok &= cp_parse_trans_clause(p, bind, 1, pieces, next, SMO_view_to_view_x, SMO_view_to_model);
     } else if (pieces[next] == "clip") {
-      ok &= cp_parse_trans_clause(p, bind, pieces, next, SMO_view_to_clip_x_C, SMO_view_to_clip_C);
+      ok &= cp_parse_trans_clause(p, bind, 1, pieces, next, SMO_view_to_clip_x, SMO_view_to_clip);
     } else if (pieces[next] == "view") {
-      ok &= cp_parse_trans_clause(p, bind, pieces, next, SMO_view_to_view_x_C, SMO_noop);
+      ok &= cp_parse_trans_clause(p, bind, 1, pieces, next, SMO_view_to_view_x, SMO_identity);
     } else if (pieces[next] == "apiview") {
-      ok &= cp_parse_trans_clause(p, bind, pieces, next, SMO_view_to_apiview_x_C, SMO_view_to_apiview_C);
+      ok &= cp_parse_trans_clause(p, bind, 1, pieces, next, SMO_view_to_apiview_x, SMO_view_to_apiview);
     } else if (pieces[next] == "apiclip") {
-      ok &= cp_parse_trans_clause(p, bind, pieces, next, SMO_view_to_apiclip_x_C, SMO_view_to_apiclip_C);
+      ok &= cp_parse_trans_clause(p, bind, 1, pieces, next, SMO_view_to_apiclip_x, SMO_view_to_apiclip);
     } else {
-      bind._opcodes.push_back(SMO_view_to_view_x_C);
-      bind._args.push_back(InternalName::make(pieces[next]));
+      bind._part[1] = SMO_view_to_view_x;
+      bind._arg[1] = InternalName::make(pieces[next]);
       next += 1;
     }
     
@@ -469,17 +519,13 @@ compile_parameter(void *reference,
       return false;
     }
 
-    // Check for transform-dependence.
-    if (bind._opcodes.back() == SMO_view_to_model_C) {
-      bind._trans_dependent = true;
-    }
-    
     // Check for syntactic well-formed-ness.
     if (pieces[next] != "") {
       cp_report_error(p, "end of line expected");
       return false;
     }
     
+    cp_optimize_mat_spec(bind);
     _mat_spec.push_back(bind);
     return true;
   }
@@ -494,27 +540,34 @@ compile_parameter(void *reference,
     }
     ShaderMatSpec bind;
     bind._parameter = reference;
-    bind._trans_dependent = false;
     bind._piece = SMP_row3;
+    bind._func = SMF_first;
+    bind._part[1] = SMO_identity;
+    bind._arg[1] = NULL;
     if (pieces[1] == "pixelsize") {
       if (!cp_errchk_parameter_float(p, 2, 2)) {
         return false;
       }
-      bind._opcodes.push_back(SMO_pixel_size);
+      bind._part[0] = SMO_pixel_size;
+      bind._arg[0] = NULL;
     } else if (pieces[1] == "windowsize") {
       if (!cp_errchk_parameter_float(p, 2, 2)) {
         return false;
       }
-      bind._opcodes.push_back(SMO_window_size);
+      bind._part[0] = SMO_window_size;
+      bind._arg[0] = NULL;
     } else if (pieces[1] == "cardcenter") {
       if (!cp_errchk_parameter_float(p, 2, 2)) {
         return false;
       }
-      bind._opcodes.push_back(SMO_card_center);
+      bind._part[0] = SMO_card_center;
+      bind._arg[0] = NULL;
     } else {
       cp_report_error(p,"unknown system parameter");
       return false;
     }
+    
+    cp_optimize_mat_spec(bind);
     _mat_spec.push_back(bind);
     return true;
   }
@@ -561,20 +614,26 @@ compile_parameter(void *reference,
     case SAT_float4: {
       ShaderMatSpec bind;
       bind._parameter = reference;
-      bind._trans_dependent = false;
       bind._piece = SMP_row3;
-      bind._opcodes.push_back(SMO_vec_constant_x);
-      bind._args.push_back(InternalName::make(pieces[1]));
+      bind._func = SMF_first;
+      bind._part[0] = SMO_vec_constant_x;
+      bind._arg[0] = InternalName::make(pieces[1]);
+      bind._part[1] = SMO_identity;
+      bind._arg[1] = NULL;
+      cp_optimize_mat_spec(bind);
       _mat_spec.push_back(bind);
       break;
     }
     case SAT_float4x4: {
       ShaderMatSpec bind;
       bind._parameter = reference;
-      bind._trans_dependent = false;
       bind._piece = SMP_whole;
-      bind._opcodes.push_back(SMO_mat_constant_x);
-      bind._args.push_back(InternalName::make(pieces[1]));
+      bind._func = SMF_first;
+      bind._part[0] = SMO_vec_constant_x;
+      bind._arg[0] = InternalName::make(pieces[1]);
+      bind._part[1] = SMO_identity;
+      bind._arg[1] = NULL;
+      cp_optimize_mat_spec(bind);
       _mat_spec.push_back(bind);
       break;
     }

+ 48 - 44
panda/src/gobj/shaderContext.h

@@ -49,33 +49,9 @@ public:
     SHADER_type_both=2,
   };
 
-  enum ShaderArgType {
-    SAT_float1,
-    SAT_float2,
-    SAT_float3,
-    SAT_float4,
-    SAT_float4x4,
-    SAT_sampler1d,
-    SAT_sampler2d,
-    SAT_sampler3d,
-    SAT_samplercube,
-    SAT_unknown,
-  };
-
-  enum ShaderArgDir {
-    SAD_in,
-    SAD_out,
-    SAD_inout,
-    SAD_unknown,
-  };
-
-  enum ShaderMatOp {
+  enum ShaderMatInput {
     SMO_identity,
 
-    SMO_modelview,
-    SMO_projection,
-    SMO_modelproj,
-    
     SMO_window_size,
     SMO_pixel_size,
     SMO_card_center,
@@ -84,34 +60,51 @@ public:
     SMO_vec_constant_x,
     
     SMO_world_to_view,
-    SMO_view_to_world_C,
+    SMO_view_to_world,
 
     SMO_model_to_view,
-    SMO_view_to_model_C,
+    SMO_view_to_model,
 
     SMO_apiview_to_view,
-    SMO_view_to_apiview_C,
+    SMO_view_to_apiview,
 
     SMO_clip_to_view,
-    SMO_view_to_clip_C,
+    SMO_view_to_clip,
 
     SMO_apiclip_to_view,
-    SMO_view_to_apiclip_C,
+    SMO_view_to_apiclip,
     
     SMO_view_x_to_view,
-    SMO_view_to_view_x_C,
+    SMO_view_to_view_x,
 
     SMO_apiview_x_to_view,
-    SMO_view_to_apiview_x_C,
+    SMO_view_to_apiview_x,
 
     SMO_clip_x_to_view,
-    SMO_view_to_clip_x_C,
+    SMO_view_to_clip_x,
 
     SMO_apiclip_x_to_view,
-    SMO_view_to_apiclip_x_C,
-    
-    SMO_transpose,
-    SMO_noop,
+    SMO_view_to_apiclip_x,
+  };
+
+  enum ShaderArgType {
+    SAT_float1,
+    SAT_float2,
+    SAT_float3,
+    SAT_float4,
+    SAT_float4x4,
+    SAT_sampler1d,
+    SAT_sampler2d,
+    SAT_sampler3d,
+    SAT_samplercube,
+    SAT_unknown,
+  };
+
+  enum ShaderArgDir {
+    SAD_in,
+    SAD_out,
+    SAD_inout,
+    SAD_unknown,
   };
 
   enum ShaderMatPiece {
@@ -127,12 +120,21 @@ public:
     SMP_col3,
   };
 
+  enum ShaderMatFunc {
+    SMF_compose,
+    SMF_compose_cache_first,
+    SMF_compose_cache_second,
+    SMF_first,
+  };
+
   struct ShaderMatSpec {
-    pvector<ShaderMatOp>      _opcodes;
-    pvector<PT(InternalName)> _args;
-    ShaderMatPiece            _piece;
-    bool                      _trans_dependent;
-    void                     *_parameter;
+    ShaderMatFunc    _func;
+    ShaderMatInput   _part[2];
+    PT(InternalName) _arg[2];
+    LMatrix4f        _cache;
+    ShaderMatPiece   _piece;
+    bool             _trans_dependent;
+    void            *_parameter;
   };
 
   struct ShaderTexSpec {
@@ -174,10 +176,12 @@ private:
   bool cp_errchk_parameter_sampler(ShaderArgInfo &arg);
   bool cp_parse_trans_clause(ShaderArgInfo &arg,
                              ShaderMatSpec &spec,
+                             int part,
                              const vector_string &pieces,
                              int &next,
-                             ShaderMatOp ofop,
-                             ShaderMatOp op);
+                             ShaderMatInput ofop,
+                             ShaderMatInput op);
+  void cp_optimize_mat_spec(ShaderMatSpec &spec);
 
 protected:
   bool compile_parameter(void *reference,