Browse Source

text: Make TextNode pipeline-cycled

Fixes #1070

Note that text property changes are not pipeline-cycled at the moment.
rdb 5 years ago
parent
commit
ac4faf3d74
3 changed files with 639 additions and 552 deletions
  1. 204 252
      panda/src/text/textNode.I
  2. 368 260
      panda/src/text/textNode.cxx
  3. 67 40
      panda/src/text/textNode.h

File diff suppressed because it is too large
+ 204 - 252
panda/src/text/textNode.I


+ 368 - 260
panda/src/text/textNode.cxx

@@ -62,36 +62,9 @@ TextNode::
 TextNode(const string &name) : PandaNode(name) {
   set_cull_callback();
 
-  _flags = 0;
-  _max_rows = 0;
-  _usage_hint = GeomEnums::UH_static;
-  _flatten_flags = 0;
-  if (text_flatten) {
-    _flatten_flags |= FF_strong;
-  }
-  if (text_dynamic_merge) {
-    _flatten_flags |= FF_dynamic_merge;
-  }
-
   if (text_small_caps) {
     TextProperties::set_small_caps(true);
   }
-
-  _frame_color.set(1.0f, 1.0f, 1.0f, 1.0f);
-  _card_color.set(1.0f, 1.0f, 1.0f, 1.0f);
-
-  _frame_width = 1.0f;
-
-  _frame_ul.set(0.0f, 0.0f);
-  _frame_lr.set(0.0f, 0.0f);
-  _card_ul.set(0.0f, 0.0f);
-  _card_lr.set(0.0f, 0.0f);
-
-  _transform = LMatrix4::ident_mat();
-  _coordinate_system = CS_default;
-
-  _ul3d.set(0.0f, 0.0f, 0.0f);
-  _lr3d.set(0.0f, 0.0f, 0.0f);
 }
 
 /**
@@ -102,25 +75,6 @@ TextNode::
 TextNode(const string &name, const TextProperties &copy) :
   PandaNode(name), TextProperties(copy)
 {
-  _flags = 0;
-  _max_rows = 0;
-  _usage_hint = GeomEnums::UH_static;
-
-  _frame_color.set(1.0f, 1.0f, 1.0f, 1.0f);
-  _card_color.set(1.0f, 1.0f, 1.0f, 1.0f);
-
-  _frame_width = 1.0f;
-
-  _frame_ul.set(0.0f, 0.0f);
-  _frame_lr.set(0.0f, 0.0f);
-  _card_ul.set(0.0f, 0.0f);
-  _card_lr.set(0.0f, 0.0f);
-
-  _transform = LMatrix4::ident_mat();
-  _coordinate_system = CS_default;
-
-  _ul3d.set(0.0f, 0.0f, 0.0f);
-  _lr3d.set(0.0f, 0.0f, 0.0f);
 }
 
 /**
@@ -131,25 +85,9 @@ TextNode(const TextNode &copy) :
   PandaNode(copy),
   TextEncoder(copy),
   TextProperties(copy),
-  _card_texture(copy._card_texture),
-  _frame_color(copy._frame_color),
-  _card_color(copy._card_color),
-  _flags(copy._flags),
-  _max_rows(copy._max_rows),
-  _usage_hint(GeomEnums::UH_static),
-  _frame_width(copy._frame_width),
-  _card_border_size(copy._card_border_size),
-  _card_border_uv_portion(copy._card_border_uv_portion),
-  _frame_ul(copy._frame_ul),
-  _frame_lr(copy._frame_lr),
-  _card_ul(copy._card_ul),
-  _card_lr(copy._card_lr),
-  _transform(copy._transform),
-  _coordinate_system(copy._coordinate_system),
-  _ul3d(copy._ul3d),
-  _lr3d(copy._lr3d)
+  _cycler(copy._cycler)
 {
-  invalidate_with_measure();
+  mark_internal_bounds_stale();
 }
 
 /**
@@ -169,6 +107,82 @@ TextNode::
 ~TextNode() {
 }
 
+/**
+ * Returns the actual dimensions of the frame around the text.  If the frame
+ * was set via set_frame_as_margin(), the result returned by this function
+ * reflects the size of the current text; if the frame was set via
+ * set_frame_actual(), this returns the values actually set.
+ *
+ * If the text has no frame at all, this returns the dimensions of the text
+ * itself, as if the frame were set with a margin of 0, 0, 0, 0.
+ */
+LVecBase4 TextNode::
+get_frame_actual() const {
+  CDLockedReader cdata(_cycler);
+  if ((cdata->_flags & (F_has_frame | F_frame_as_margin)) == F_has_frame) {
+    return LVecBase4(cdata->_frame_ul[0],
+                     cdata->_frame_lr[0],
+                     cdata->_frame_lr[1],
+                     cdata->_frame_ul[1]);
+  }
+
+  if (do_needs_measure(cdata)) {
+    CDWriter cdataw(((TextNode *)this)->_cycler, cdata, false);
+    ((TextNode *)this)->do_measure(cdataw);
+
+    LVecBase4 frame(cdataw->_text_ul[0], cdataw->_text_lr[0], cdataw->_text_lr[1], cdataw->_text_ul[1]);
+    if (cdataw->_flags & F_has_frame) {
+      frame += LVecBase4(-cdataw->_frame_ul[0], cdataw->_frame_lr[0], -cdataw->_frame_lr[1], cdataw->_frame_ul[1]);
+    }
+    return frame;
+  }
+  else {
+    LVecBase4 frame(cdata->_text_ul[0], cdata->_text_lr[0], cdata->_text_lr[1], cdata->_text_ul[1]);
+    if (cdata->_flags & F_has_frame) {
+      frame += LVecBase4(-cdata->_frame_ul[0], cdata->_frame_lr[0], -cdata->_frame_lr[1], cdata->_frame_ul[1]);
+    }
+    return frame;
+  }
+}
+
+/**
+ * Returns the actual dimensions of the card around the text.  If the card was
+ * set via set_card_as_margin(), the result returned by this function reflects
+ * the size of the current text; if the card was set via set_card_actual(),
+ * this returns the values actually set.
+ *
+ * If the text has no card at all, this returns the dimensions of the text
+ * itself, as if the card were set with a margin of 0, 0, 0, 0.
+ */
+LVecBase4 TextNode::
+get_card_actual() const {
+  CDLockedReader cdata(_cycler);
+  if ((cdata->_flags & (F_has_card | F_card_as_margin)) == F_has_card) {
+    return LVecBase4(cdata->_card_ul[0],
+                     cdata->_card_lr[0],
+                     cdata->_card_lr[1],
+                     cdata->_card_ul[1]);
+  }
+
+  if (do_needs_measure(cdata)) {
+    CDWriter cdataw(((TextNode *)this)->_cycler, cdata, false);
+    ((TextNode *)this)->do_measure(cdataw);
+
+    LVecBase4 card(cdataw->_text_ul[0], cdataw->_text_lr[0], cdataw->_text_lr[1], cdataw->_text_ul[1]);
+    if (cdataw->_flags & F_has_card) {
+      card += LVecBase4(-cdataw->_card_ul[0], cdataw->_card_lr[0], -cdataw->_card_lr[1], cdataw->_card_ul[1]);
+    }
+    return card;
+  }
+  else {
+    LVecBase4 card(cdata->_text_ul[0], cdata->_text_lr[0], cdata->_text_lr[1], cdata->_text_ul[1]);
+    if (cdata->_flags & F_has_card) {
+      card += LVecBase4(-cdata->_card_ul[0], cdata->_card_lr[0], -cdata->_card_lr[1], cdata->_card_ul[1]);
+    }
+    return card;
+  }
+}
+
 /**
  * Returns the width of a single character of the font, or 0.0 if the
  * character is not known.  This may be a wide character (greater than 255).
@@ -292,14 +306,15 @@ output(std::ostream &out) const {
 void TextNode::
 write(std::ostream &out, int indent_level) const {
   PandaNode::write(out, indent_level);
-  MutexHolder holder(_lock);
+
+  CDReader cdata(_cycler);
   TextProperties::write(out, indent_level + 2);
   indent(out, indent_level + 2)
-    << "transform is: " << *TransformState::make_mat(_transform) << "\n";
+    << "transform is: " << *TransformState::make_mat(cdata->_transform) << "\n";
   indent(out, indent_level + 2)
-    << "in coordinate system " << _coordinate_system << "\n";
+    << "in coordinate system " << cdata->_coordinate_system << "\n";
   indent(out, indent_level + 2)
-    << "text is " << get_text() << "\n";
+    << "text is " << cdata->_text << "\n";
 }
 
 /**
@@ -324,8 +339,12 @@ get_internal_geom() const {
  */
 void TextNode::
 text_changed() {
-  MutexHolder holder(_lock);
-  invalidate_with_measure();
+  // Copy the text to this class, since TextEncoder doesn't store it in a
+  // pipeline-cycled manner.
+  CDWriter cdata(_cycler);
+  cdata->_text = TextEncoder::get_text();
+  cdata->_wtext = TextEncoder::get_wtext();
+  invalidate_with_measure(cdata);
 }
 
 /**
@@ -356,60 +375,65 @@ get_unsafe_to_apply_attribs() const {
 void TextNode::
 apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
                           GeomTransformer &transformer) {
-  MutexHolder holder(_lock);
-  if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
-    const LMatrix4 &mat = attribs._transform->get_mat();
-    _transform *= mat;
-
-    if ((_flags & F_needs_measure) == 0) {
-      // If we already have a measure, transform it too.  We don't need to
-      // invalidate the 2-d parts, since that's not affected by the transform
-      // anyway.
-      _ul3d = _ul3d * mat;
-      _lr3d = _lr3d * mat;
-    }
-  }
-  if ((attrib_types & SceneGraphReducer::TT_color) != 0) {
-    if (attribs._color != nullptr) {
-      const ColorAttrib *ca = DCAST(ColorAttrib, attribs._color);
-      if (ca->get_color_type() == ColorAttrib::T_flat) {
-        const LColor &c = ca->get_color();
-        TextProperties::set_text_color(c);
-        TextProperties::set_shadow_color(c);
-        _frame_color = c;
-        _card_color = c;
-        invalidate_no_measure();
-      }
-    }
-  }
-  if ((attrib_types & SceneGraphReducer::TT_color_scale) != 0) {
-    if (attribs._color_scale != nullptr) {
-      const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attribs._color_scale);
-      const LVecBase4 &s = csa->get_scale();
-      if (s != LVecBase4(1.0f, 1.0f, 1.0f, 1.0f)) {
-        LVecBase4 tc = get_text_color();
-        tc.componentwise_mult(s);
-        TextProperties::set_text_color(tc);
 
-        LVecBase4 sc = get_shadow_color();
-        sc.componentwise_mult(s);
-        TextProperties::set_shadow_color(sc);
+  Thread *current_thread = Thread::get_current_thread();
+  OPEN_ITERATE_CURRENT_AND_UPSTREAM(_cycler, current_thread) {
+    CDStageWriter cdata(_cycler, pipeline_stage, current_thread);
 
-        _frame_color.componentwise_mult(s);
-        _card_color.componentwise_mult(s);
+    if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
+      const LMatrix4 &mat = attribs._transform->get_mat();
+      cdata->_transform *= mat;
 
-        invalidate_no_measure();
+      if ((cdata->_flags & F_needs_measure) == 0) {
+        // If we already have a measure, transform it too.  We don't need to
+        // invalidate the 2-d parts, since that's not affected by the transform
+        // anyway.
+        cdata->_ul3d = cdata->_ul3d * mat;
+        cdata->_lr3d = cdata->_lr3d * mat;
+      }
+    }
+    if ((attrib_types & SceneGraphReducer::TT_color) != 0) {
+      if (attribs._color != nullptr) {
+        const ColorAttrib *ca = DCAST(ColorAttrib, attribs._color);
+        if (ca->get_color_type() == ColorAttrib::T_flat) {
+          const LColor &c = ca->get_color();
+          TextProperties::set_text_color(c);
+          TextProperties::set_shadow_color(c);
+          cdata->_frame_color = c;
+          cdata->_card_color = c;
+          invalidate_no_measure(cdata);
+        }
+      }
+    }
+    if ((attrib_types & SceneGraphReducer::TT_color_scale) != 0) {
+      if (attribs._color_scale != nullptr) {
+        const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attribs._color_scale);
+        const LVecBase4 &s = csa->get_scale();
+        if (s != LVecBase4(1.0f, 1.0f, 1.0f, 1.0f)) {
+          LVecBase4 tc = get_text_color();
+          tc.componentwise_mult(s);
+          TextProperties::set_text_color(tc);
+
+          LVecBase4 sc = get_shadow_color();
+          sc.componentwise_mult(s);
+          TextProperties::set_shadow_color(sc);
+
+          cdata->_frame_color.componentwise_mult(s);
+          cdata->_card_color.componentwise_mult(s);
+
+          invalidate_no_measure(cdata);
+        }
       }
     }
-  }
 
-  // Now propagate the attributes down to our already-generated geometry, if
-  // we have any.
-  if ((_flags & F_needs_rebuild) == 0 &&
-      _internal_geom != nullptr) {
-    SceneGraphReducer gr;
-    gr.apply_attribs(_internal_geom, attribs, attrib_types, transformer);
+    // Now propagate the attributes down to our already-generated geometry, if
+    // we have any.
+    if ((cdata->_flags & F_needs_rebuild) == 0 && cdata->_internal_geom != nullptr) {
+      SceneGraphReducer gr;
+      gr.apply_attribs(cdata->_internal_geom, attribs, attrib_types, transformer);
+    }
   }
+  CLOSE_ITERATE_CURRENT_AND_UPSTREAM(_cycler);
 }
 
 /**
@@ -501,17 +525,27 @@ compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
   // actually generating the text, if we have at least measured it.
   LPoint3 vertices[8];
   {
-    MutexHolder holder(_lock);
-    check_measure();
+    LPoint3 ul3d, lr3d;
+    CDLockedReader cdata(_cycler);
+    if (do_needs_measure(cdata)) {
+      CDWriter cdataw(((TextNode *)this)->_cycler, cdata, false);
+      ((TextNode *)this)->do_measure(cdataw);
+
+      ul3d = cdataw->_ul3d;
+      lr3d = cdataw->_lr3d;
+    } else {
+      ul3d = cdata->_ul3d;
+      lr3d = cdata->_lr3d;
+    }
 
-    vertices[0].set(_ul3d[0], _ul3d[1], _ul3d[2]);
-    vertices[1].set(_ul3d[0], _ul3d[1], _lr3d[2]);
-    vertices[2].set(_ul3d[0], _lr3d[1], _ul3d[2]);
-    vertices[3].set(_ul3d[0], _lr3d[1], _lr3d[2]);
-    vertices[4].set(_lr3d[0], _ul3d[1], _ul3d[2]);
-    vertices[5].set(_lr3d[0], _ul3d[1], _lr3d[2]);
-    vertices[6].set(_lr3d[0], _lr3d[1], _ul3d[2]);
-    vertices[7].set(_lr3d[0], _lr3d[1], _lr3d[2]);
+    vertices[0].set(ul3d[0], ul3d[1], ul3d[2]);
+    vertices[1].set(ul3d[0], ul3d[1], lr3d[2]);
+    vertices[2].set(ul3d[0], lr3d[1], ul3d[2]);
+    vertices[3].set(ul3d[0], lr3d[1], lr3d[2]);
+    vertices[4].set(lr3d[0], ul3d[1], ul3d[2]);
+    vertices[5].set(lr3d[0], ul3d[1], lr3d[2]);
+    vertices[6].set(lr3d[0], lr3d[1], ul3d[2]);
+    vertices[7].set(lr3d[0], lr3d[1], lr3d[2]);
   }
 
   gbv->around(vertices, vertices + 8);
@@ -542,21 +576,19 @@ r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state,
  * text instead.
  */
 void TextNode::
-do_rebuild() {
-  nassertv(_lock.debug_is_locked());
-  _flags &= ~(F_needs_rebuild | F_needs_measure);
-  _internal_geom = do_generate();
+do_rebuild(CData *cdata) {
+  cdata->_flags &= ~(F_needs_rebuild | F_needs_measure);
+  cdata->_internal_geom = do_generate(cdata);
 }
 
-
 /**
  * Can be called in lieu of do_rebuild() to measure the text and set up the
  * bounding boxes properly without actually assembling it.
  */
 void TextNode::
-do_measure() {
+do_measure(CData *cdata) {
   // We no longer make this a special case.
-  do_rebuild();
+  do_rebuild(cdata);
 }
 
 /**
@@ -565,14 +597,12 @@ do_measure() {
  * represent it.
  */
 PT(PandaNode) TextNode::
-do_generate() {
-  nassertr(_lock.debug_is_locked(), nullptr);
-
+do_generate(CData *cdata) {
   PStatTimer timer(_text_generate_pcollector);
   if (text_cat.is_debug()) {
     text_cat.debug()
       << "Rebuilding " << get_type() << " " << get_name()
-      << " with '" << get_text() << "'\n";
+      << " with '" << cdata->_text << "'\n";
   }
 
   // The strategy here will be to assemble together a bunch of letters,
@@ -584,18 +614,18 @@ do_generate() {
   // row, that moves the row into the right place horizontally and vertically,
   // and for each row, there is another node for each character.
 
-  _ul3d.set(0.0f, 0.0f, 0.0f);
-  _lr3d.set(0.0f, 0.0f, 0.0f);
+  cdata->_ul3d.set(0.0f, 0.0f, 0.0f);
+  cdata->_lr3d.set(0.0f, 0.0f, 0.0f);
 
   // Now build a new sub-tree for all the text components.
-  string name = get_text();
+  string name = cdata->_text;
   size_t newline = name.find('\n');
   if (newline != string::npos) {
     name = name.substr(0, newline);
   }
   PT(PandaNode) root = new PandaNode(name);
 
-  if (!has_text()) {
+  if (cdata->_wtext.empty()) {
     return root;
   }
 
@@ -607,34 +637,32 @@ do_generate() {
   // Compute the overall text transform matrix.  We build the text in a Z-up
   // coordinate system and then convert it to whatever the user asked for.
   LMatrix4 mat =
-    LMatrix4::convert_mat(CS_zup_right, _coordinate_system) *
-    _transform;
+    LMatrix4::convert_mat(CS_zup_right, cdata->_coordinate_system) *
+    cdata->_transform;
 
   CPT(TransformState) transform = TransformState::make_mat(mat);
   root->set_transform(transform);
 
-  std::wstring wtext = get_wtext();
-
   // Assemble the text.
   TextAssembler assembler(this);
   assembler.set_properties(*this);
-  assembler.set_max_rows(_max_rows);
-  assembler.set_usage_hint(_usage_hint);
-  assembler.set_dynamic_merge((_flatten_flags & FF_dynamic_merge) != 0);
-  bool all_set = assembler.set_wtext(wtext);
+  assembler.set_max_rows(cdata->_max_rows);
+  assembler.set_usage_hint(cdata->_usage_hint);
+  assembler.set_dynamic_merge((cdata->_flatten_flags & FF_dynamic_merge) != 0);
+  bool all_set = assembler.set_wtext(cdata->_wtext);
   if (all_set) {
     // No overflow.
-    _flags &= ~F_has_overflow;
+    cdata->_flags &= ~F_has_overflow;
   } else {
     // Overflow.
-    _flags |= F_has_overflow;
+    cdata->_flags |= F_has_overflow;
   }
 
   PT(PandaNode) text_root = assembler.assemble_text();
-  _text_ul = assembler.get_ul();
-  _text_lr = assembler.get_lr();
-  _num_rows = assembler.get_num_rows();
-  _wordwrapped_wtext = assembler.get_wordwrapped_wtext();
+  cdata->_text_ul = assembler.get_ul();
+  cdata->_text_lr = assembler.get_lr();
+  cdata->_num_rows = assembler.get_num_rows();
+  cdata->_wordwrapped_wtext = assembler.get_wordwrapped_wtext();
 
   // Parent the text in.
   PT(PandaNode) text = new PandaNode("text");
@@ -645,43 +673,45 @@ do_generate() {
   // the user.
   const LVector2 &ul = assembler.get_ul();
   const LVector2 &lr = assembler.get_lr();
-  _ul3d.set(ul[0], 0.0f, ul[1]);
-  _lr3d.set(lr[0], 0.0f, lr[1]);
+  cdata->_ul3d.set(ul[0], 0.0f, ul[1]);
+  cdata->_lr3d.set(lr[0], 0.0f, lr[1]);
 
-  _ul3d = _ul3d * _transform;
-  _lr3d = _lr3d * _transform;
+  cdata->_ul3d = cdata->_ul3d * cdata->_transform;
+  cdata->_lr3d = cdata->_lr3d * cdata->_transform;
 
   // Incidentally, that means we don't need to measure the text now.
-  _flags &= ~F_needs_measure;
+  cdata->_flags &= ~F_needs_measure;
 
   // Now flatten our hierarchy to get rid of the transforms we put in,
   // applying them to the vertices.
 
   NodePath root_np(root);
-  if (_flatten_flags & FF_strong) {
+  if (cdata->_flatten_flags & FF_strong) {
     root_np.flatten_strong();
-  } else if (_flatten_flags & FF_medium) {
+  }
+  else if (cdata->_flatten_flags & FF_medium) {
     root_np.flatten_medium();
-  } else if (_flatten_flags & FF_light) {
+  }
+  else if (cdata->_flatten_flags & FF_light) {
     root_np.flatten_light();
   }
 
   // Now deal with the decorations.
 
-  if (_flags & F_has_card) {
+  if (cdata->_flags & F_has_card) {
     PT(PandaNode) card_root;
-    if (_flags & F_has_card_border) {
-      card_root = make_card_with_border();
+    if (cdata->_flags & F_has_card_border) {
+      card_root = do_make_card_with_border(cdata);
     } else {
-      card_root = make_card();
+      card_root = do_make_card(cdata);
     }
     card_root->set_transform(transform);
-    card_root->set_attrib(ColorAttrib::make_flat(_card_color));
-    if (_card_color[3] != 1.0f) {
+    card_root->set_attrib(ColorAttrib::make_flat(cdata->_card_color));
+    if (cdata->_card_color[3] != 1.0f) {
       card_root->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
     }
-    if (_flags & F_has_card_texture) {
-      card_root->set_attrib(TextureAttrib::make(_card_texture));
+    if (cdata->_flags & F_has_card_texture) {
+      card_root->set_attrib(TextureAttrib::make(cdata->_card_texture));
     }
 
     if (has_bin()) {
@@ -697,17 +727,17 @@ do_generate() {
     card_root->add_child(root);
     root = card_root;
 
-    if (_flags & F_card_decal) {
+    if (cdata->_flags & F_card_decal) {
       card_root->set_effect(DecalEffect::make());
     }
   }
 
-  if (_flags & F_has_frame) {
-    PT(PandaNode) frame_root = make_frame();
+  if (cdata->_flags & F_has_frame) {
+    PT(PandaNode) frame_root = do_make_frame(cdata);
     frame_root->set_transform(transform);
     root->add_child(frame_root, get_draw_order() + 1);
-    frame_root->set_attrib(ColorAttrib::make_flat(_frame_color));
-    if (_frame_color[3] != 1.0f) {
+    frame_root->set_attrib(ColorAttrib::make_flat(cdata->_frame_color));
+    if (cdata->_frame_color[3] != 1.0f) {
       frame_root->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
     }
 
@@ -728,38 +758,46 @@ do_generate() {
  */
 PT(PandaNode) TextNode::
 do_get_internal_geom() const {
-  MutexHolder holder(_lock);
-  check_rebuild();
-  return _internal_geom;
+  CDLockedReader cdata(_cycler);
+  if ((cdata->_flags & F_needs_rebuild) != 0) {
+    // Propagate the generated text upstream if the upstream stages have no
+    // changes to the text.
+    CDWriter cdataw(((TextNode *)this)->_cycler, cdata, false);
+    ((TextNode *)this)->do_rebuild(cdataw);
+
+    return cdataw->_internal_geom;
+  }
+  else {
+    return cdata->_internal_geom;
+  }
 }
 
 /**
  * Creates a frame around the text.
  */
 PT(PandaNode) TextNode::
-make_frame() {
-  nassertr(_lock.debug_is_locked(), nullptr);
-  nassertr((_flags & F_needs_measure) == 0, nullptr);
+do_make_frame(const CData *cdata) {
+  nassertr((cdata->_flags & F_needs_measure) == 0, nullptr);
 
   PT(GeomNode) frame_node = new GeomNode("frame");
 
-  PN_stdfloat left = _frame_ul[0];
-  PN_stdfloat right = _frame_lr[0];
-  PN_stdfloat bottom = _frame_lr[1];
-  PN_stdfloat top = _frame_ul[1];
+  PN_stdfloat left = cdata->_frame_ul[0];
+  PN_stdfloat right = cdata->_frame_lr[0];
+  PN_stdfloat bottom = cdata->_frame_lr[1];
+  PN_stdfloat top = cdata->_frame_ul[1];
 
-  if (_flags & F_frame_as_margin) {
-    left = _text_ul[0] - left;
-    right = _text_lr[0] + right;
-    bottom = _text_lr[1] - bottom;
-    top = _text_ul[1] + top;
+  if (cdata->_flags & F_frame_as_margin) {
+    left = cdata->_text_ul[0] - left;
+    right = cdata->_text_lr[0] + right;
+    bottom = cdata->_text_lr[1] - bottom;
+    top = cdata->_text_ul[1] + top;
   }
 
-  CPT(RenderAttrib) thick = RenderModeAttrib::make(RenderModeAttrib::M_unchanged, _frame_width);
+  CPT(RenderAttrib) thick = RenderModeAttrib::make(RenderModeAttrib::M_unchanged, cdata->_frame_width);
   CPT(RenderState) state = RenderState::make(thick);
 
   PT(GeomVertexData) vdata = new GeomVertexData
-    ("text", GeomVertexFormat::get_v3(), _usage_hint);
+    ("text", GeomVertexFormat::get_v3(), cdata->_usage_hint);
   vdata->unclean_set_num_rows(4);
   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
 
@@ -768,7 +806,7 @@ make_frame() {
   vertex.set_data3(right, 0.0f, bottom);
   vertex.set_data3(right, 0.0f, top);
 
-  PT(GeomLinestrips) frame = new GeomLinestrips(_usage_hint);
+  PT(GeomLinestrips) frame = new GeomLinestrips(cdata->_usage_hint);
   frame->add_consecutive_vertices(0, 4);
   frame->add_vertex(0);
   frame->close_primitive();
@@ -777,8 +815,8 @@ make_frame() {
   geom->add_primitive(frame);
   frame_node->add_geom(geom, state);
 
-  if (_flags & F_frame_corners) {
-    PT(GeomPoints) corners = new GeomPoints(_usage_hint);
+  if (cdata->_flags & F_frame_corners) {
+    PT(GeomPoints) corners = new GeomPoints(cdata->_usage_hint);
     corners->add_consecutive_vertices(0, 4);
     PT(Geom) geom2 = new Geom(vdata);
     geom2->add_primitive(corners);
@@ -792,26 +830,25 @@ make_frame() {
  * Creates a card behind the text.
  */
 PT(PandaNode) TextNode::
-make_card() {
-  nassertr(_lock.debug_is_locked(), nullptr);
-  nassertr((_flags & F_needs_measure) == 0, nullptr);
+do_make_card(const CData *cdata) {
+  nassertr((cdata->_flags & F_needs_measure) == 0, nullptr);
 
   PT(GeomNode) card_node = new GeomNode("card");
 
-  PN_stdfloat left = _card_ul[0];
-  PN_stdfloat right = _card_lr[0];
-  PN_stdfloat bottom = _card_lr[1];
-  PN_stdfloat top = _card_ul[1];
+  PN_stdfloat left = cdata->_card_ul[0];
+  PN_stdfloat right = cdata->_card_lr[0];
+  PN_stdfloat bottom = cdata->_card_lr[1];
+  PN_stdfloat top = cdata->_card_ul[1];
 
-  if (_flags & F_card_as_margin) {
-    left = _text_ul[0] - left;
-    right = _text_lr[0] + right;
-    bottom = _text_lr[1] - bottom;
-    top = _text_ul[1] + top;
+  if (cdata->_flags & F_card_as_margin) {
+    left = cdata->_text_ul[0] - left;
+    right = cdata->_text_lr[0] + right;
+    bottom = cdata->_text_lr[1] - bottom;
+    top = cdata->_text_ul[1] + top;
   }
 
   PT(GeomVertexData) vdata = new GeomVertexData
-    ("text", GeomVertexFormat::get_v3t2(), _usage_hint);
+    ("text", GeomVertexFormat::get_v3t2(), cdata->_usage_hint);
   vdata->unclean_set_num_rows(4);
   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
   GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
@@ -826,7 +863,7 @@ make_card() {
   texcoord.set_data2(1.0f, 1.0f);
   texcoord.set_data2(1.0f, 0.0f);
 
-  PT(GeomTristrips) card = new GeomTristrips(_usage_hint);
+  PT(GeomTristrips) card = new GeomTristrips(cdata->_usage_hint);
   card->add_consecutive_vertices(0, 4);
   card->close_primitive();
 
@@ -844,83 +881,88 @@ make_card() {
  * what have you.
  */
 PT(PandaNode) TextNode::
-make_card_with_border() {
-  nassertr(_lock.debug_is_locked(), nullptr);
-  nassertr((_flags & F_needs_measure) == 0, nullptr);
+do_make_card_with_border(const CData *cdata) {
+  nassertr((cdata->_flags & F_needs_measure) == 0, nullptr);
 
   PT(GeomNode) card_node = new GeomNode("card");
 
-  PN_stdfloat left = _card_ul[0];
-  PN_stdfloat right = _card_lr[0];
-  PN_stdfloat bottom = _card_lr[1];
-  PN_stdfloat top = _card_ul[1];
+  PN_stdfloat left = cdata->_card_ul[0];
+  PN_stdfloat right = cdata->_card_lr[0];
+  PN_stdfloat bottom = cdata->_card_lr[1];
+  PN_stdfloat top = cdata->_card_ul[1];
 
-  if (_flags & F_card_as_margin) {
-    left = _text_ul[0] - left;
-    right = _text_lr[0] + right;
-    bottom = _text_lr[1] - bottom;
-    top = _text_ul[1] + top;
+  if (cdata->_flags & F_card_as_margin) {
+    left = cdata->_text_ul[0] - left;
+    right = cdata->_text_lr[0] + right;
+    bottom = cdata->_text_lr[1] - bottom;
+    top = cdata->_text_ul[1] + top;
   }
 
-/*
- * we now create three tri-strips instead of one with vertices arranged as
- * follows: 1 3            5 7  - one 2 4            6 8    \ two 9 11
- * 13 15 \ 10 12          14 16 - three
- */
+  PN_stdfloat border_size = cdata->_card_border_size;
+  PN_stdfloat border_uv_portion = cdata->_card_border_uv_portion;
+
+  // we now create three tri-strips instead of one
+  // with vertices arranged as follows:
+  //
+  //  1 3            5 7  - one
+  //  2 4            6 8  /  \ two
+  //  9 11          13 15 \  /
+  // 10 12          14 16 - three
+  //
 
   PT(GeomVertexData) vdata = new GeomVertexData
-    ("text", GeomVertexFormat::get_v3t2(), _usage_hint);
+    ("text", GeomVertexFormat::get_v3t2(), cdata->_usage_hint);
   vdata->unclean_set_num_rows(16);
   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
   GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
 
   // verts 1,2,3,4
   vertex.set_data3(left, 0.02, top);
-  vertex.set_data3(left, 0.02, top - _card_border_size);
-  vertex.set_data3(left + _card_border_size, 0.02, top);
-  vertex.set_data3(left + _card_border_size, 0.02,
-                    top - _card_border_size);
+  vertex.set_data3(left, 0.02, top - border_size);
+  vertex.set_data3(left + border_size, 0.02, top);
+  vertex.set_data3(left + border_size, 0.02,
+                    top - border_size);
   // verts 5,6,7,8
-  vertex.set_data3(right - _card_border_size, 0.02, top);
-  vertex.set_data3(right - _card_border_size, 0.02,
-                    top - _card_border_size);
+  vertex.set_data3(right - border_size, 0.02, top);
+  vertex.set_data3(right - border_size, 0.02,
+                    top - border_size);
   vertex.set_data3(right, 0.02, top);
-  vertex.set_data3(right, 0.02, top - _card_border_size);
+  vertex.set_data3(right, 0.02, top - border_size);
   // verts 9,10,11,12
-  vertex.set_data3(left, 0.02, bottom + _card_border_size);
+  vertex.set_data3(left, 0.02, bottom + border_size);
   vertex.set_data3(left, 0.02, bottom);
-  vertex.set_data3(left + _card_border_size, 0.02,
-                    bottom + _card_border_size);
-  vertex.set_data3(left + _card_border_size, 0.02, bottom);
+  vertex.set_data3(left + border_size, 0.02,
+                    bottom + border_size);
+  vertex.set_data3(left + border_size, 0.02, bottom);
   // verts 13,14,15,16
-  vertex.set_data3(right - _card_border_size, 0.02,
-                    bottom + _card_border_size);
-  vertex.set_data3(right - _card_border_size, 0.02, bottom);
-  vertex.set_data3(right, 0.02, bottom + _card_border_size);
+  vertex.set_data3(right - border_size, 0.02,
+                    bottom + border_size);
+  vertex.set_data3(right - border_size, 0.02, bottom);
+  vertex.set_data3(right, 0.02, bottom + border_size);
   vertex.set_data3(right, 0.02, bottom);
 
   texcoord.set_data2(0.0f, 1.0f); //1
-  texcoord.set_data2(0.0f, 1.0f - _card_border_uv_portion); //2
-  texcoord.set_data2(0.0f + _card_border_uv_portion, 1.0f); //3
-  texcoord.set_data2(0.0f + _card_border_uv_portion,
-                      1.0f - _card_border_uv_portion); //4
-  texcoord.set_data2(1.0f -_card_border_uv_portion, 1.0f); //5
-  texcoord.set_data2(1.0f -_card_border_uv_portion,
-                      1.0f - _card_border_uv_portion); //6
+  texcoord.set_data2(0.0f, 1.0f - border_uv_portion); //2
+  texcoord.set_data2(0.0f + border_uv_portion, 1.0f); //3
+  texcoord.set_data2(0.0f + border_uv_portion,
+                      1.0f - border_uv_portion); //4
+  texcoord.set_data2(1.0f -border_uv_portion, 1.0f); //5
+  texcoord.set_data2(1.0f -border_uv_portion,
+                      1.0f - border_uv_portion); //6
   texcoord.set_data2(1.0f, 1.0f); //7
-  texcoord.set_data2(1.0f, 1.0f - _card_border_uv_portion); //8
+  texcoord.set_data2(1.0f, 1.0f - border_uv_portion); //8
 
-  texcoord.set_data2(0.0f, _card_border_uv_portion); //9
+  texcoord.set_data2(0.0f, border_uv_portion); //9
   texcoord.set_data2(0.0f, 0.0f); //10
-  texcoord.set_data2(_card_border_uv_portion, _card_border_uv_portion); //11
-  texcoord.set_data2(_card_border_uv_portion, 0.0f); //12
+  texcoord.set_data2(border_uv_portion, border_uv_portion); //11
+  texcoord.set_data2(border_uv_portion, 0.0f); //12
 
-  texcoord.set_data2(1.0f - _card_border_uv_portion, _card_border_uv_portion);//13
-  texcoord.set_data2(1.0f - _card_border_uv_portion, 0.0f);//14
-  texcoord.set_data2(1.0f, _card_border_uv_portion);//15
+  texcoord.set_data2(1.0f - border_uv_portion, border_uv_portion);//13
+  texcoord.set_data2(1.0f - border_uv_portion, 0.0f);//14
+  texcoord.set_data2(1.0f, border_uv_portion);//15
   texcoord.set_data2(1.0f, 0.0f);//16
 
-  PT(GeomTristrips) card = new GeomTristrips(_usage_hint);
+  PT(GeomTristrips) card = new GeomTristrips(cdata->_usage_hint);
   card->reserve_num_vertices(24);
 
   // tristrip #1
@@ -970,3 +1012,69 @@ count_geoms(PandaNode *node) {
 
   return num_geoms;
 }
+
+/**
+ *
+ */
+TextNode::CData::
+CData() {
+  _flags = 0;
+  _max_rows = 0;
+  _usage_hint = GeomEnums::UH_static;
+  _flatten_flags = 0;
+  if (text_flatten) {
+    _flatten_flags |= FF_strong;
+  }
+  if (text_dynamic_merge) {
+    _flatten_flags |= FF_dynamic_merge;
+  }
+
+  _frame_color.set(1.0f, 1.0f, 1.0f, 1.0f);
+  _card_color.set(1.0f, 1.0f, 1.0f, 1.0f);
+
+  _frame_width = 1.0f;
+
+  _frame_ul.set(0.0f, 0.0f);
+  _frame_lr.set(0.0f, 0.0f);
+  _card_ul.set(0.0f, 0.0f);
+  _card_lr.set(0.0f, 0.0f);
+
+  _transform = LMatrix4::ident_mat();
+  _coordinate_system = CS_default;
+
+  _ul3d.set(0.0f, 0.0f, 0.0f);
+  _lr3d.set(0.0f, 0.0f, 0.0f);
+}
+
+/**
+ *
+ */
+TextNode::CData::
+CData(const CData &copy) :
+  _card_texture(copy._card_texture),
+  _frame_color(copy._frame_color),
+  _card_color(copy._card_color),
+  _flags(copy._flags | F_needs_rebuild | F_needs_measure),
+  _max_rows(copy._max_rows),
+  _usage_hint(GeomEnums::UH_static),
+  _frame_width(copy._frame_width),
+  _card_border_size(copy._card_border_size),
+  _card_border_uv_portion(copy._card_border_uv_portion),
+  _frame_ul(copy._frame_ul),
+  _frame_lr(copy._frame_lr),
+  _card_ul(copy._card_ul),
+  _card_lr(copy._card_lr),
+  _transform(copy._transform),
+  _coordinate_system(copy._coordinate_system),
+  _ul3d(copy._ul3d),
+  _lr3d(copy._lr3d)
+{
+}
+
+/**
+ *
+ */
+CycleData *TextNode::CData::
+make_copy() const {
+  return new CData(*this);
+}

+ 67 - 40
panda/src/text/textNode.h

@@ -24,8 +24,12 @@
 #include "pandaNode.h"
 #include "luse.h"
 #include "geom.h"
-#include "pmutex.h"
-#include "mutexHolder.h"
+#include "cycleData.h"
+#include "cycleDataLockedReader.h"
+#include "cycleDataReader.h"
+#include "cycleDataWriter.h"
+#include "cycleDataStageReader.h"
+#include "cycleDataStageWriter.h"
 
 /**
  * The primary interface to this module.  This class does basic text assembly;
@@ -99,7 +103,7 @@ PUBLISHED:
   INLINE bool has_frame() const;
   INLINE bool is_frame_as_margin() const;
   INLINE LVecBase4 get_frame_as_set() const;
-  INLINE LVecBase4 get_frame_actual() const;
+  LVecBase4 get_frame_actual() const;
 
   INLINE void set_frame_line_width(PN_stdfloat line_width);
   INLINE PN_stdfloat get_frame_line_width() const;
@@ -116,7 +120,7 @@ PUBLISHED:
   INLINE bool get_card_decal() const;
   INLINE bool is_card_as_margin() const;
   INLINE LVecBase4 get_card_as_set() const;
-  INLINE LVecBase4 get_card_actual() const;
+  LVecBase4 get_card_actual() const;
   INLINE LVecBase4 get_card_transformed() const;
 
   INLINE void set_transform(const LMatrix4 &transform);
@@ -295,30 +299,25 @@ public:
                                Thread *current_thread);
 
 private:
-  INLINE void invalidate_no_measure();
-  INLINE void invalidate_with_measure();
-  INLINE void check_rebuild() const;
-  INLINE void check_measure() const;
+  class CData;
 
-  void do_rebuild();
-  void do_measure();
+  INLINE void invalidate_no_measure(CData *cdata);
+  INLINE void invalidate_with_measure(CData *cdata);
+  INLINE bool do_needs_rebuild(const CData *cdata) const;
+  INLINE bool do_needs_measure(const CData *cdata) const;
 
-  PT(PandaNode) do_generate();
+  void do_rebuild(CData *cdata);
+  void do_measure(CData *cdata);
+
+  PT(PandaNode) do_generate(CData *cdata);
   PT(PandaNode) do_get_internal_geom() const;
 
-  PT(PandaNode) make_frame();
-  PT(PandaNode) make_card();
-  PT(PandaNode) make_card_with_border();
+  PT(PandaNode) do_make_frame(const CData *cdata);
+  PT(PandaNode) do_make_card(const CData *cdata);
+  PT(PandaNode) do_make_card_with_border(const CData *cdata);
 
   static int count_geoms(PandaNode *node);
 
-  Mutex _lock;
-  PT(PandaNode) _internal_geom;
-
-  PT(Texture) _card_texture;
-  LColor _frame_color;
-  LColor _card_color;
-
   enum Flags {
     F_has_frame        =  0x0001,
     F_frame_as_margin  =  0x0002,
@@ -334,26 +333,54 @@ private:
     F_card_decal       =  0x0800,
   };
 
-  int _flags;
-  int _max_rows;
-  GeomEnums::UsageHint _usage_hint;
-  int _flatten_flags;
-  PN_stdfloat _frame_width;
-  PN_stdfloat _card_border_size;
-  PN_stdfloat _card_border_uv_portion;
-
-  LVector2 _frame_ul, _frame_lr;
-  LVector2 _card_ul, _card_lr;
-
-  LMatrix4 _transform;
-  CoordinateSystem _coordinate_system;
-
-  LPoint3 _ul3d, _lr3d;
+  // This is the data that must be cycled between pipeline stages.
+  class EXPCL_PANDA_TEXT CData : public CycleData {
+  public:
+    CData();
+    CData(const CData &copy);
+    virtual CycleData *make_copy() const;
+    virtual TypeHandle get_parent_type() const {
+      return TextNode::get_class_type();
+    }
+
+    // We copy these here because they aren't pipeline-cycled on TextEncoder.
+    std::string _text;
+    std::wstring _wtext;
+
+    PT(PandaNode) _internal_geom;
+
+    PT(Texture) _card_texture;
+    LColor _frame_color;
+    LColor _card_color;
+
+    int _flags;
+    int _max_rows;
+    GeomEnums::UsageHint _usage_hint;
+    int _flatten_flags;
+    PN_stdfloat _frame_width;
+    PN_stdfloat _card_border_size;
+    PN_stdfloat _card_border_uv_portion;
+
+    LVector2 _frame_ul, _frame_lr;
+    LVector2 _card_ul, _card_lr;
+
+    LMatrix4 _transform;
+    CoordinateSystem _coordinate_system;
+
+    LPoint3 _ul3d, _lr3d;
+
+    // Returned from TextAssembler:
+    LVector2 _text_ul, _text_lr;
+    int _num_rows;
+    std::wstring _wordwrapped_wtext;
+  };
 
-  // Returned from TextAssembler:
-  LVector2 _text_ul, _text_lr;
-  int _num_rows;
-  std::wstring _wordwrapped_wtext;
+  PipelineCycler<CData> _cycler;
+  typedef CycleDataLockedReader<CData> CDLockedReader;
+  typedef CycleDataReader<CData> CDReader;
+  typedef CycleDataWriter<CData> CDWriter;
+  typedef CycleDataStageReader<CData> CDStageReader;
+  typedef CycleDataStageWriter<CData> CDStageWriter;
 
   static PStatCollector _text_generate_pcollector;
 

Some files were not shown because too many files changed in this diff