Procházet zdrojové kódy

Add gsg::update_texture()

David Rose před 17 roky
rodič
revize
8c2e657f15

+ 21 - 0
panda/src/display/graphicsStateGuardian.cxx

@@ -414,6 +414,27 @@ prepare_texture(Texture *) {
   return (TextureContext *)NULL;
   return (TextureContext *)NULL;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::update_texture
+//       Access: Public, Virtual
+//  Description: Ensures that the current Texture data is refreshed
+//               onto the GSG.  This means updating the texture
+//               properties and/or re-uploading the texture image, if
+//               necessary.  This should only be called within the
+//               draw thread.
+//
+//               If force is true, this function will not return until
+//               the texture has been fully uploaded.  If force is
+//               false, the function may choose to upload a simple
+//               version of the texture instead, if the texture is not
+//               fully resident (and if get_incomplete_render() is
+//               true).
+////////////////////////////////////////////////////////////////////
+bool GraphicsStateGuardian::
+update_texture(TextureContext *, bool) {
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::release_texture
 //     Function: GraphicsStateGuardian::release_texture
 //       Access: Public, Virtual
 //       Access: Public, Virtual

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

@@ -170,6 +170,7 @@ public:
   virtual SceneSetup *get_scene() const;
   virtual SceneSetup *get_scene() const;
 
 
   virtual TextureContext *prepare_texture(Texture *tex);
   virtual TextureContext *prepare_texture(Texture *tex);
+  virtual bool update_texture(TextureContext *tc, bool force);
   virtual void release_texture(TextureContext *tc);
   virtual void release_texture(TextureContext *tc);
   virtual bool extract_texture_data(Texture *tex);
   virtual bool extract_texture_data(Texture *tex);
 
 

+ 87 - 28
panda/src/glstuff/glGraphicsStateGuardian_src.cxx

@@ -2637,6 +2637,50 @@ prepare_texture(Texture *tex) {
   return gtc;
   return gtc;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::update_texture
+//       Access: Public, Virtual
+//  Description: Ensures that the current Texture data is refreshed
+//               onto the GSG.  This means updating the texture
+//               properties and/or re-uploading the texture image, if
+//               necessary.  This should only be called within the
+//               draw thread.
+//
+//               If force is true, this function will not return until
+//               the texture has been fully uploaded.  If force is
+//               false, the function may choose to upload a simple
+//               version of the texture instead, if the texture is not
+//               fully resident (and if get_incomplete_render() is
+//               true).
+////////////////////////////////////////////////////////////////////
+bool CLP(GraphicsStateGuardian)::
+update_texture(TextureContext *tc, bool force) {
+  apply_texture(tc);
+
+  CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
+
+  if (gtc->was_image_modified()) {
+    // If the texture image was modified, reload the texture.  This
+    // means we also re-specify the properties for good measure.
+    specify_texture(gtc->get_texture());
+    bool okflag = upload_texture(gtc, force);
+    if (!okflag) {
+      GLCAT.error()
+        << "Could not load " << *gtc->get_texture() << "\n";
+      return false;
+    }
+
+  } else if (gtc->was_properties_modified()) {
+    // If only the properties have been modified, we don't necessarily
+    // need to reload the texture.
+    specify_texture(gtc->get_texture());
+    gtc->mark_loaded();
+  }
+
+  report_my_gl_errors();
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::release_texture
 //     Function: GLGraphicsStateGuardian::release_texture
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -3275,18 +3319,44 @@ framebuffer_copy_to_texture(Texture *tex, int z, const DisplayRegion *dr,
 
 
   TextureContext *tc = tex->prepare_now(get_prepared_objects(), this);
   TextureContext *tc = tex->prepare_now(get_prepared_objects(), this);
   nassertr(tc != (TextureContext *)NULL, false);
   nassertr(tc != (TextureContext *)NULL, false);
-  apply_texture(tc);
+  CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
+
+  apply_texture(gtc);
+  specify_texture(tex);
 
 
+  GLenum target = get_texture_target(tex->get_texture_type());
+  GLint internal_format = get_internal_image_format(tex);
+  bool uses_mipmaps = tex->uses_mipmaps() && !CLP(ignore_mipmaps);
+  if (uses_mipmaps) {
+    if (_supports_generate_mipmap) {
+      GLP(TexParameteri)(target, GL_GENERATE_MIPMAP, true);
+    } else {
+      // If we can't auto-generate mipmaps, do without mipmaps.
+      GLP(TexParameteri)(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+      uses_mipmaps = false;
+    }
+  }
+
+  bool new_image = gtc->was_image_modified();
   if (z >= 0) {
   if (z >= 0) {
-    // Copy to a cube map face.  This doesn't seem to work too well
-    // with CopyTexSubImage2D, so we always use CopyTexImage2D.
-    GLP(CopyTexImage2D)(GL_TEXTURE_CUBE_MAP_POSITIVE_X + z, 0,
-                        get_internal_image_format(tex),
-                        xo, yo, w, h, 0);
+    // Copy to a cube map face.
+    target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + z;
+
+    // Cube map faces seem to have trouble with CopyTexSubImage, so we
+    // always reload the image.
+    new_image = true;
+  }
+
+  if (new_image) {
+    // We have to create a new image.
+    GLP(CopyTexImage2D)(target, 0, internal_format, xo, yo, w, h, 0);
   } else {
   } else {
-    GLP(CopyTexSubImage2D)(GL_TEXTURE_2D, 0, 0, 0, xo, yo, w, h);
+    // We can overlay the existing image.
+    GLP(CopyTexSubImage2D)(target, 0, 0, 0, xo, yo, w, h);
   }
   }
 
 
+  gtc->mark_loaded();
+
   report_my_gl_errors();
   report_my_gl_errors();
 
 
   // Force reload of texture state, since we've just monkeyed with it.
   // Force reload of texture state, since we've just monkeyed with it.
@@ -6186,7 +6256,7 @@ update_standard_texture_bindings() {
     }
     }
     GLP(Enable)(target);
     GLP(Enable)(target);
     
     
-    if (!apply_texture(tc)) {
+    if (!update_texture(tc, false)) {
       GLP(Disable)(target);
       GLP(Disable)(target);
       break;
       break;
     }
     }
@@ -6755,24 +6825,6 @@ apply_texture(TextureContext *tc) {
   }
   }
   GLP(BindTexture)(target, gtc->_index);
   GLP(BindTexture)(target, gtc->_index);
 
 
-  if (gtc->was_image_modified()) {
-    // If the texture image was modified, reload the texture.  This
-    // means we also re-specify the properties for good measure.
-    specify_texture(gtc->get_texture());
-    bool okflag = upload_texture(gtc);
-    if (!okflag) {
-      GLCAT.error()
-        << "Could not load " << *gtc->get_texture() << "\n";
-      return false;
-    }
-
-  } else if (gtc->was_properties_modified()) {
-    // If only the properties have been modified, we don't necessarily
-    // need to reload the texture.
-    specify_texture(gtc->get_texture());
-    gtc->mark_loaded();
-  }
-
   report_my_gl_errors();
   report_my_gl_errors();
   return true;
   return true;
 }
 }
@@ -6787,10 +6839,10 @@ apply_texture(TextureContext *tc) {
 //               the texture has no image.
 //               the texture has no image.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool CLP(GraphicsStateGuardian)::
 bool CLP(GraphicsStateGuardian)::
-upload_texture(CLP(TextureContext) *gtc) {
+upload_texture(CLP(TextureContext) *gtc, bool force) {
   Texture *tex = gtc->get_texture();
   Texture *tex = gtc->get_texture();
 
 
-  if (_incomplete_render) {
+  if (_incomplete_render && !force) {
     bool has_image = _supports_compressed_texture ? tex->has_ram_image() : tex->has_uncompressed_ram_image();
     bool has_image = _supports_compressed_texture ? tex->has_ram_image() : tex->has_uncompressed_ram_image();
     if (!has_image && tex->might_have_ram_image() &&
     if (!has_image && tex->might_have_ram_image() &&
         tex->has_simple_ram_image() &&
         tex->has_simple_ram_image() &&
@@ -6827,6 +6879,13 @@ upload_texture(CLP(TextureContext) *gtc) {
     image_compression = Texture::CM_off;
     image_compression = Texture::CM_off;
   }    
   }    
 
 
+  /*
+  if (image.is_null()) {
+    // If we don't have an image, we can't upload.
+    return false;
+  }
+  */
+
   int mipmap_bias = 0;
   int mipmap_bias = 0;
 
 
   int width = tex->get_x_size();
   int width = tex->get_x_size();

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

@@ -143,6 +143,7 @@ public:
   INLINE bool draw_display_list(GeomContext *gc);
   INLINE bool draw_display_list(GeomContext *gc);
 
 
   virtual TextureContext *prepare_texture(Texture *tex);
   virtual TextureContext *prepare_texture(Texture *tex);
+  virtual bool update_texture(TextureContext *tc, bool force);
   virtual void release_texture(TextureContext *tc);
   virtual void release_texture(TextureContext *tc);
   virtual bool extract_texture_data(Texture *tex);
   virtual bool extract_texture_data(Texture *tex);
 
 
@@ -324,7 +325,7 @@ protected:
   void do_auto_rescale_normal();
   void do_auto_rescale_normal();
   void specify_texture(Texture *tex);
   void specify_texture(Texture *tex);
   bool apply_texture(TextureContext *tc);
   bool apply_texture(TextureContext *tc);
-  bool upload_texture(CLP(TextureContext) *gtc);
+  bool upload_texture(CLP(TextureContext) *gtc, bool force);
   bool upload_texture_image(CLP(TextureContext) *gtc,
   bool upload_texture_image(CLP(TextureContext) *gtc,
                             bool uses_mipmaps, int mipmap_bias,
                             bool uses_mipmaps, int mipmap_bias,
                             GLenum texture_target, GLenum page_target, 
                             GLenum texture_target, GLenum page_target, 

+ 2 - 1
panda/src/gobj/preparedGraphicsObjects.cxx

@@ -1214,7 +1214,8 @@ begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread) {
        qti != _enqueued_textures.end();
        qti != _enqueued_textures.end();
        ++qti) {
        ++qti) {
     Texture *tex = (*qti);
     Texture *tex = (*qti);
-    tex->prepare_now(this, gsg);
+    TextureContext *tc = tex->prepare_now(this, gsg);
+    gsg->update_texture(tc, true);
   }
   }
 
 
   _enqueued_textures.clear();
   _enqueued_textures.clear();

+ 1 - 0
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -133,6 +133,7 @@ public:
 #endif
 #endif
 
 
   virtual TextureContext *prepare_texture(Texture *tex)=0;
   virtual TextureContext *prepare_texture(Texture *tex)=0;
+  virtual bool update_texture(TextureContext *tc, bool force)=0;
   virtual void release_texture(TextureContext *tc)=0;
   virtual void release_texture(TextureContext *tc)=0;
   virtual bool extract_texture_data(Texture *tex)=0;
   virtual bool extract_texture_data(Texture *tex)=0;
 
 

+ 29 - 0
panda/src/pgraph/geomNode.cxx

@@ -352,6 +352,35 @@ safe_to_combine() const {
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::r_prepare_scene
+//       Access: Protected, Virtual
+//  Description: The recursive implementation of prepare_scene().
+//               Don't call this directly; call
+//               PandaNode::prepare_scene() or
+//               NodePath::prepare_scene() instead.
+////////////////////////////////////////////////////////////////////
+void GeomNode::
+r_prepare_scene(const RenderState *state,
+                PreparedGraphicsObjects *prepared_objects) {
+  int num_geoms = get_num_geoms();
+  for (int i = 0; i < num_geoms; i++) {
+    CPT(RenderState) geom_state = state->compose(get_geom_state(i));
+    const RenderAttrib *attrib = 
+      geom_state->get_attrib(TextureAttrib::get_class_type());
+    if (attrib != (const RenderAttrib *)NULL) {
+      const TextureAttrib *ta;
+      DCAST_INTO_V(ta, attrib);
+      Texture *texture = ta->get_texture();
+      if (texture != (Texture *)NULL) {
+        texture->prepare(prepared_objects);
+      }
+    }
+  }
+  
+  PandaNode::r_prepare_scene(state, prepared_objects);
+}
+
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomNode::combine_with
 //     Function: GeomNode::combine_with

+ 3 - 0
panda/src/pgraph/geomNode.h

@@ -59,6 +59,9 @@ public:
   virtual bool safe_to_flatten() const;
   virtual bool safe_to_flatten() const;
   virtual bool safe_to_combine() const;
   virtual bool safe_to_combine() const;
 
 
+  virtual void r_prepare_scene(const RenderState *state,
+                               PreparedGraphicsObjects *prepared_objects);
+
 PUBLISHED:
 PUBLISHED:
   INLINE void set_preserved(bool value);
   INLINE void set_preserved(bool value);
   INLINE bool get_preserved() const;
   INLINE bool get_preserved() const;

+ 1 - 46
panda/src/pgraph/nodePath.cxx

@@ -5752,10 +5752,8 @@ void NodePath::
 prepare_scene(GraphicsStateGuardianBase *gsg) {
 prepare_scene(GraphicsStateGuardianBase *gsg) {
   nassertv_always(!is_empty());
   nassertv_always(!is_empty());
 
 
-  PreparedGraphicsObjects *prepared_objects = gsg->get_prepared_objects();
-
   CPT(RenderState) net_state = get_net_state();
   CPT(RenderState) net_state = get_net_state();
-  r_prepare_scene(node(), net_state, prepared_objects);
+  node()->prepare_scene(gsg, net_state);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -7009,46 +7007,3 @@ r_find_all_materials(PandaNode *node, const RenderState *state,
     r_find_all_materials(child, next_state, materials);
     r_find_all_materials(child, next_state, materials);
   }
   }
 }
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: NodePath::r_prepare_scene
-//       Access: Private
-//  Description: The recursive implementation of prepare_scene.
-////////////////////////////////////////////////////////////////////
-void NodePath::
-r_prepare_scene(PandaNode *node, const RenderState *state,
-                PreparedGraphicsObjects *prepared_objects) {
-  if (node->is_geom_node()) {
-    GeomNode *gnode;
-    DCAST_INTO_V(gnode, node);
-
-    /* 
-       Not implemented yet in pgraph.  Maybe we don't need this anyway.
-    if (retained_mode) {
-      gnode->prepare(gsg);
-    }
-    */
-
-    int num_geoms = gnode->get_num_geoms();
-    for (int i = 0; i < num_geoms; i++) {
-      CPT(RenderState) geom_state = state->compose(gnode->get_geom_state(i));
-      const RenderAttrib *attrib = 
-        geom_state->get_attrib(TextureAttrib::get_class_type());
-      if (attrib != (const RenderAttrib *)NULL) {
-        const TextureAttrib *ta;
-        DCAST_INTO_V(ta, attrib);
-        Texture *texture = ta->get_texture();
-        if (texture != (Texture *)NULL) {
-          texture->prepare(prepared_objects);
-        }
-      }
-    }
-  }
-
-  int num_children = node->get_num_children();
-  for (int i = 0; i < num_children; i++) {
-    PandaNode *child = node->get_child(i);
-    CPT(RenderState) child_state = state->compose(child->get_state());
-    r_prepare_scene(child, child_state, prepared_objects);
-  }
-}

+ 0 - 3
panda/src/pgraph/nodePath.h

@@ -913,9 +913,6 @@ private:
   void r_find_all_materials(PandaNode *node, const RenderState *state,
   void r_find_all_materials(PandaNode *node, const RenderState *state,
                            Materials &materials) const;
                            Materials &materials) const;
 
 
-  void r_prepare_scene(PandaNode *node, const RenderState *state,
-                       PreparedGraphicsObjects *prepared_objects);
-
   PT(NodePathComponent) _head;
   PT(NodePathComponent) _head;
   int _backup_key;
   int _backup_key;
   ErrorType _error_type;
   ErrorType _error_type;

+ 41 - 0
panda/src/pgraph/pandaNode.cxx

@@ -27,6 +27,7 @@
 #include "pStatTimer.h"
 #include "pStatTimer.h"
 #include "config_mathutil.h"
 #include "config_mathutil.h"
 #include "reMutexHolder.h"
 #include "reMutexHolder.h"
+#include "graphicsStateGuardianBase.h"
 
 
 // This category is just temporary for debugging convenience.
 // This category is just temporary for debugging convenience.
 NotifyCategoryDecl(drawmask, EXPCL_PANDA_PGRAPH, EXPTP_PANDA_PGRAPH);
 NotifyCategoryDecl(drawmask, EXPCL_PANDA_PGRAPH, EXPTP_PANDA_PGRAPH);
@@ -2105,6 +2106,27 @@ get_off_clip_planes(Thread *current_thread) const {
   return cdata->_off_clip_planes;
   return cdata->_off_clip_planes;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::prepare_scene
+//       Access: Published
+//  Description: Walks through the scene graph beginning at this node,
+//               and does whatever initialization is required to
+//               render the scene properly with the indicated GSG.  It
+//               is not strictly necessary to call this, since the GSG
+//               will initialize itself when the scene is rendered,
+//               but this may take some of the overhead away from that
+//               process.
+//
+//               In particular, this will ensure that textures within
+//               the scene are loaded in texture memory, and display
+//               lists are built up from static geometry.
+////////////////////////////////////////////////////////////////////
+void PandaNode::
+prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *net_state) {
+  PreparedGraphicsObjects *prepared_objects = gsg->get_prepared_objects();
+  r_prepare_scene(net_state, prepared_objects);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::output
 //     Function: PandaNode::output
 //       Access: Published, Virtual
 //       Access: Published, Virtual
@@ -2661,6 +2683,25 @@ r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map,
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::r_prepare_scene
+//       Access: Public, Virtual
+//  Description: The recursive implementation of prepare_scene().
+//               Don't call this directly; call
+//               PandaNode::prepare_scene() or
+//               NodePath::prepare_scene() instead.
+////////////////////////////////////////////////////////////////////
+void PandaNode::
+r_prepare_scene(const RenderState *state,
+                PreparedGraphicsObjects *prepared_objects) {
+  int num_children = get_num_children();
+  for (int i = 0; i < num_children; i++) {
+    PandaNode *child = get_child(i);
+    CPT(RenderState) child_state = state->compose(child->get_state());
+    child->r_prepare_scene(child_state, prepared_objects);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::set_cull_callback
 //     Function: PandaNode::set_cull_callback
 //       Access: Protected
 //       Access: Protected

+ 7 - 0
panda/src/pgraph/pandaNode.h

@@ -60,6 +60,7 @@ class Light;
 class FactoryParams;
 class FactoryParams;
 class AccumulatedAttribs;
 class AccumulatedAttribs;
 class GeomTransformer;
 class GeomTransformer;
+class GraphicsStateGuardianBase;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : PandaNode
 //       Class : PandaNode
@@ -239,6 +240,8 @@ PUBLISHED:
   CollideMask get_net_collide_mask(Thread *current_thread = Thread::get_current_thread()) const;
   CollideMask get_net_collide_mask(Thread *current_thread = Thread::get_current_thread()) const;
   CPT(RenderAttrib) get_off_clip_planes(Thread *current_thread = Thread::get_current_thread()) const;
   CPT(RenderAttrib) get_off_clip_planes(Thread *current_thread = Thread::get_current_thread()) const;
 
 
+  void prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *net_state);
+
   virtual void output(ostream &out) const;
   virtual void output(ostream &out) const;
   virtual void write(ostream &out, int indent_level) const;
   virtual void write(ostream &out, int indent_level) const;
 
 
@@ -314,6 +317,10 @@ protected:
 
 
   void set_cull_callback();
   void set_cull_callback();
 
 
+public:
+  virtual void r_prepare_scene(const RenderState *state,
+                               PreparedGraphicsObjects *prepared_objects);
+
 protected:
 protected:
   // This is a base class of CData, defined below.  It contains just
   // This is a base class of CData, defined below.  It contains just
   // the protected (not private) part of CData that will be needed by
   // the protected (not private) part of CData that will be needed by

+ 24 - 0
panda/src/pgui/pgItem.cxx

@@ -335,6 +335,30 @@ compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage,
   bdata->_internal_bounds_stale = false;
   bdata->_internal_bounds_stale = false;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PGItem::r_prepare_scene
+//       Access: Protected, Virtual
+//  Description: The recursive implementation of prepare_scene().
+//               Don't call this directly; call
+//               PandaNode::prepare_scene() or
+//               NodePath::prepare_scene() instead.
+////////////////////////////////////////////////////////////////////
+void PGItem::
+r_prepare_scene(const RenderState *state,
+                PreparedGraphicsObjects *prepared_objects) {
+  StateDefs::iterator di;
+  for (di = _state_defs.begin(); di != _state_defs.end(); ++di) {
+    NodePath &root = (*di)._root;
+    if (!root.is_empty()) {
+      PandaNode *child = root.node();
+      CPT(RenderState) child_state = state->compose(child->get_state());
+      child->r_prepare_scene(child_state, prepared_objects);
+    }
+  }
+  
+  PandaNode::r_prepare_scene(state, prepared_objects);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PGItem::xform
 //     Function: PGItem::xform
 //       Access: Public, Virtual
 //       Access: Public, Virtual

+ 3 - 0
panda/src/pgui/pgItem.h

@@ -68,6 +68,9 @@ protected:
   virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
   virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
                                        Thread *current_thread) const;
                                        Thread *current_thread) const;
 
 
+  virtual void r_prepare_scene(const RenderState *state,
+                               PreparedGraphicsObjects *prepared_objects);
+
 public:
 public:
   virtual void xform(const LMatrix4f &mat);
   virtual void xform(const LMatrix4f &mat);
   bool activate_region(const LMatrix4f &transform, int sort,
   bool activate_region(const LMatrix4f &transform, int sort,

+ 22 - 0
panda/src/text/textNode.cxx

@@ -649,6 +649,28 @@ compute_internal_bounds(PandaNode::BoundsData *bdata, int pipeline_stage,
   bdata->_internal_bounds_stale = false;
   bdata->_internal_bounds_stale = false;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TextNode::r_prepare_scene
+//       Access: Protected, Virtual
+//  Description: The recursive implementation of prepare_scene().
+//               Don't call this directly; call
+//               PandaNode::prepare_scene() or
+//               NodePath::prepare_scene() instead.
+////////////////////////////////////////////////////////////////////
+void TextNode::
+r_prepare_scene(const RenderState *state,
+                PreparedGraphicsObjects *prepared_objects) {
+  check_rebuild();
+
+  PandaNode *child = _internal_geom;
+  if (child != (PandaNode *)NULL) {
+    CPT(RenderState) child_state = state->compose(child->get_state());
+    child->r_prepare_scene(child_state, prepared_objects);
+  }
+  
+  PandaNode::r_prepare_scene(state, prepared_objects);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TextNode::do_rebuild
 //     Function: TextNode::do_rebuild
 //       Access: Private
 //       Access: Private

+ 3 - 0
panda/src/text/textNode.h

@@ -242,6 +242,9 @@ public:
   virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
   virtual void compute_internal_bounds(BoundsData *bdata, int pipeline_stage,
                                        Thread *current_thread) const;
                                        Thread *current_thread) const;
 
 
+  virtual void r_prepare_scene(const RenderState *state,
+                               PreparedGraphicsObjects *prepared_objects);
+
 private:
 private:
   INLINE void invalidate_no_measure();
   INLINE void invalidate_no_measure();
   INLINE void invalidate_with_measure();
   INLINE void invalidate_with_measure();

+ 42 - 17
panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx

@@ -1468,6 +1468,47 @@ prepare_texture(Texture *tex) {
   return gtc;
   return gtc;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TinyGraphicsStateGuardian::update_texture
+//       Access: Public, Virtual
+//  Description: Ensures that the current Texture data is refreshed
+//               onto the GSG.  This means updating the texture
+//               properties and/or re-uploading the texture image, if
+//               necessary.  This should only be called within the
+//               draw thread.
+//
+//               If force is true, this function will not return until
+//               the texture has been fully uploaded.  If force is
+//               false, the function may choose to upload a simple
+//               version of the texture instead, if the texture is not
+//               fully resident (and if get_incomplete_render() is
+//               true).
+////////////////////////////////////////////////////////////////////
+bool TinyGraphicsStateGuardian::
+update_texture(TextureContext *tc, bool force) {
+  apply_texture(tc);
+
+  TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
+
+  GLTexture *gltex = &gtc->_gltex;
+
+  if (gtc->was_image_modified() || gltex->num_levels == 0) {
+    // If the texture image was modified, reload the texture.
+    bool okflag = upload_texture(gtc);
+    if (!okflag) {
+      tinydisplay_cat.error()
+        << "Could not load " << *gtc->get_texture() << "\n";
+      return false;
+    }
+  }
+  gtc->enqueue_lru(&_textures_lru);
+
+  _c->current_texture = gltex;
+  _c->zb->current_texture = gltex->levels;
+
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TinyGraphicsStateGuardian::release_texture
 //     Function: TinyGraphicsStateGuardian::release_texture
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -1874,7 +1915,7 @@ do_issue_texture() {
   }
   }
     
     
   // Then, turn on the current texture mode.
   // Then, turn on the current texture mode.
-  if (!apply_texture(tc)) {
+  if (!update_texture(tc, false)) {
     return;
     return;
   }
   }
 
 
@@ -1981,22 +2022,6 @@ apply_texture(TextureContext *tc) {
   TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
   TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
 
 
   gtc->set_active(true);
   gtc->set_active(true);
-
-  GLTexture *gltex = &gtc->_gltex;
-
-  if (gtc->was_image_modified() || gltex->num_levels == 0) {
-    // If the texture image was modified, reload the texture.
-    bool okflag = upload_texture(gtc);
-    if (!okflag) {
-      tinydisplay_cat.error()
-        << "Could not load " << *gtc->get_texture() << "\n";
-      return false;
-    }
-  }
-  gtc->enqueue_lru(&_textures_lru);
-
-  _c->current_texture = gltex;
-  _c->zb->current_texture = gltex->levels;
   return true;
   return true;
 }
 }
 
 

+ 1 - 0
panda/src/tinydisplay/tinyGraphicsStateGuardian.h

@@ -85,6 +85,7 @@ public:
                                        const TransformState *transform);
                                        const TransformState *transform);
 
 
   virtual TextureContext *prepare_texture(Texture *tex);
   virtual TextureContext *prepare_texture(Texture *tex);
+  virtual bool update_texture(TextureContext *tc, bool force);
   virtual void release_texture(TextureContext *tc);
   virtual void release_texture(TextureContext *tc);
 
 
   virtual void do_issue_light();
   virtual void do_issue_light();