Panda3D
geomVertexArrayData.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file geomVertexArrayData.cxx
10  * @author drose
11  * @date 2005-03-17
12  */
13 
14 #include "geomVertexArrayData.h"
15 #include "geom.h"
17 #include "reversedNumericData.h"
18 #include "bamReader.h"
19 #include "bamWriter.h"
20 #include "pset.h"
21 #include "config_gobj.h"
22 #include "pStatTimer.h"
23 #include "configVariableInt.h"
24 #include "simpleAllocator.h"
25 #include "vertexDataBuffer.h"
26 #include "texture.h"
27 
28 using std::max;
29 using std::min;
30 
31 ConfigVariableInt max_independent_vertex_data
32 ("max-independent-vertex-data", -1,
33  PRC_DESC("Specifies the maximum number of bytes of all vertex data "
34  "that is independent of the paging system. This is an "
35  "initial buffer before max-ram-vertex-data, specifically "
36  "designed for vertex datas that are dynamic in nature and "
37  "may change size or be created and destroyed frequently."));
38 
39 ConfigVariableInt vertex_data_page_size
40 ("vertex-data-page-size", 262144,
41  PRC_DESC("The number of bytes to allocate at a time for vertex data. "
42  "This also controls the page size that is compressed or written "
43  "to disk when vertex data pages are evicted from memory."));
44 
45 SimpleLru GeomVertexArrayData::_independent_lru("independent", max_independent_vertex_data);
46 SimpleLru GeomVertexArrayData::_small_lru("small", max_independent_vertex_data);
47 
48 VertexDataBook GeomVertexArrayData::_book(vertex_data_page_size);
49 
50 
51 TypeHandle GeomVertexArrayData::_type_handle;
52 TypeHandle GeomVertexArrayData::CData::_type_handle;
53 TypeHandle GeomVertexArrayDataHandle::_type_handle;
54 
55 ALLOC_DELETED_CHAIN_DEF(GeomVertexArrayDataHandle);
56 
57 /**
58  * Constructs an invalid object. This is only used when reading from the bam
59  * file.
60  */
61 GeomVertexArrayData::
62 GeomVertexArrayData() : SimpleLruPage(0) {
63  _contexts = nullptr;
64 
65  // Can't put it in the LRU until it has been read in and made valid.
66 }
67 
68 /**
69  * Required to implement CopyOnWriteObject.
70  */
71 PT(CopyOnWriteObject) GeomVertexArrayData::
72 make_cow_copy() {
73  return new GeomVertexArrayData(*this);
74 }
75 
76 /**
77  *
78  */
79 GeomVertexArrayData::
80 GeomVertexArrayData(const GeomVertexArrayFormat *array_format,
81  GeomVertexArrayData::UsageHint usage_hint) :
82  SimpleLruPage(0),
83  _array_format(array_format),
84  _cycler(CData(usage_hint)),
85  _contexts(nullptr)
86 {
87  set_lru_size(0);
88  nassertv(_array_format->is_registered());
89 }
90 
91 /**
92  *
93  */
94 GeomVertexArrayData::
95 GeomVertexArrayData(const GeomVertexArrayData &copy) :
96  CopyOnWriteObject(copy),
97  SimpleLruPage(copy),
98  _array_format(copy._array_format),
99  _cycler(copy._cycler),
100  _contexts(nullptr)
101 {
102  copy.mark_used_lru();
103 
104  set_lru_size(get_data_size_bytes());
105  nassertv(_array_format->is_registered());
106 }
107 
108 /**
109  * The copy assignment operator is not pipeline-safe. This will completely
110  * obliterate all stages of the pipeline, so don't do it for a
111  * GeomVertexArrayData that is actively being used for rendering.
112  */
115  CopyOnWriteObject::operator = (copy);
116  SimpleLruPage::operator = (copy);
117 
118  copy.mark_used_lru();
119 
120  _array_format = copy._array_format;
121  _cycler = copy._cycler;
122 
123  OPEN_ITERATE_ALL_STAGES(_cycler) {
124  CDStageWriter cdata(_cycler, pipeline_stage);
125  cdata->_modified = Geom::get_next_modified();
126  }
127  CLOSE_ITERATE_ALL_STAGES(_cycler);
128 
129  nassertv(_array_format->is_registered());
130 }
131 
132 /**
133  *
134  */
135 GeomVertexArrayData::
136 ~GeomVertexArrayData() {
137  release_all();
138 }
139 
140 /**
141  * Returns 0 if the two arrays are equivalent, even if they are not the same
142  * pointer.
143  */
145 compare_to(const GeomVertexArrayData &other) const {
146  Thread *current_thread = Thread::get_current_thread();
147 
148  CPT(GeomVertexArrayDataHandle) handle = get_handle(current_thread);
149  CPT(GeomVertexArrayDataHandle) other_handle = other.get_handle(current_thread);
150 
151  if (handle->get_usage_hint() != other_handle->get_usage_hint()) {
152  return (int)handle->get_usage_hint() - (int)other_handle->get_usage_hint();
153  }
154  if (handle->get_array_format() != other_handle->get_array_format()) {
155  return handle->get_array_format() < other_handle->get_array_format() ? -1 : 1;
156  }
157  if (handle->get_data_size_bytes() != other_handle->get_data_size_bytes()) {
158  return (int)handle->get_data_size_bytes() - (int)other_handle->get_data_size_bytes();
159  }
160  return memcmp(handle->get_read_pointer(true),
161  other_handle->get_read_pointer(true),
162  handle->get_data_size_bytes());
163 }
164 
165 /**
166  * Changes the UsageHint hint for this array. See get_usage_hint().
167  *
168  * Don't call this in a downstream thread unless you don't mind it blowing
169  * away other changes you might have recently made in an upstream thread.
170  */
172 set_usage_hint(GeomVertexArrayData::UsageHint usage_hint) {
173  CDWriter cdata(_cycler, true);
174  cdata->_usage_hint = usage_hint;
175  cdata->_modified = Geom::get_next_modified();
176 }
177 
178 /**
179  *
180  */
181 void GeomVertexArrayData::
182 output(std::ostream &out) const {
183  out << get_num_rows() << " rows: " << *get_array_format();
184 }
185 
186 /**
187  *
188  */
189 void GeomVertexArrayData::
190 write(std::ostream &out, int indent_level) const {
191  _array_format->write_with_data(out, indent_level, this);
192 }
193 
194 /**
195  * Indicates that the data should be enqueued to be prepared in the indicated
196  * prepared_objects at the beginning of the next frame. This will ensure the
197  * data is already loaded into the GSG if it is expected to be rendered soon.
198  *
199  * Use this function instead of prepare_now() to preload datas from a user
200  * interface standpoint.
201  */
203 prepare(PreparedGraphicsObjects *prepared_objects) {
204  prepared_objects->enqueue_vertex_buffer(this);
205 }
206 
207 /**
208  * Returns true if the data has already been prepared or enqueued for
209  * preparation on the indicated GSG, false otherwise.
210  */
212 is_prepared(PreparedGraphicsObjects *prepared_objects) const {
213  if (_contexts == nullptr) {
214  return false;
215  }
216  Contexts::const_iterator ci;
217  ci = _contexts->find(prepared_objects);
218  if (ci != _contexts->end()) {
219  return true;
220  }
221  return prepared_objects->is_vertex_buffer_queued(this);
222 }
223 
224 /**
225  * Creates a context for the data on the particular GSG, if it does not
226  * already exist. Returns the new (or old) VertexBufferContext. This assumes
227  * that the GraphicsStateGuardian is the currently active rendering context
228  * and that it is ready to accept new datas. If this is not necessarily the
229  * case, you should use prepare() instead.
230  *
231  * Normally, this is not called directly except by the GraphicsStateGuardian;
232  * a data does not need to be explicitly prepared by the user before it may be
233  * rendered.
234  */
238  if (_contexts == nullptr) {
239  _contexts = new Contexts;
240  }
241  Contexts::const_iterator ci;
242  ci = _contexts->find(prepared_objects);
243  if (ci != _contexts->end()) {
244  return (*ci).second;
245  }
246 
247  VertexBufferContext *vbc = prepared_objects->prepare_vertex_buffer_now(this, gsg);
248  if (vbc != nullptr) {
249  (*_contexts)[prepared_objects] = vbc;
250  }
251  return vbc;
252 }
253 
254 /**
255  * Frees the data context only on the indicated object, if it exists there.
256  * Returns true if it was released, false if it had not been prepared.
257  */
259 release(PreparedGraphicsObjects *prepared_objects) {
260  if (_contexts != nullptr) {
261  Contexts::iterator ci;
262  ci = _contexts->find(prepared_objects);
263  if (ci != _contexts->end()) {
264  VertexBufferContext *vbc = (*ci).second;
265  prepared_objects->release_vertex_buffer(vbc);
266  return true;
267  }
268  }
269 
270  // Maybe it wasn't prepared yet, but it's about to be.
271  return prepared_objects->dequeue_vertex_buffer(this);
272 }
273 
274 /**
275  * Frees the context allocated on all objects for which the data has been
276  * declared. Returns the number of contexts which have been freed.
277  */
280  int num_freed = 0;
281 
282  if (_contexts != nullptr) {
283  // We have to traverse a copy of the _contexts list, because the
284  // PreparedGraphicsObjects object will call clear_prepared() in response
285  // to each release_vertex_buffer(), and we don't want to be modifying the
286  // _contexts list while we're traversing it.
287  Contexts temp = *_contexts;
288  num_freed = (int)_contexts->size();
289 
290  Contexts::const_iterator ci;
291  for (ci = temp.begin(); ci != temp.end(); ++ci) {
292  PreparedGraphicsObjects *prepared_objects = (*ci).first;
293  VertexBufferContext *vbc = (*ci).second;
294  prepared_objects->release_vertex_buffer(vbc);
295  }
296 
297  // Now that we've called release_vertex_buffer() on every known context,
298  // the _contexts list should have completely emptied itself.
299  nassertr(_contexts == nullptr, num_freed);
300  }
301 
302  return num_freed;
303 }
304 
305 /**
306  * Marks that an epoch has passed in each LRU. Asks the LRU's to consider
307  * whether they should perform evictions.
308  */
311  _independent_lru.begin_epoch();
312  VertexDataPage::get_global_lru(VertexDataPage::RC_resident)->begin_epoch();
313  VertexDataPage::get_global_lru(VertexDataPage::RC_compressed)->begin_epoch();
314 }
315 
316 /**
317  * Evicts the page from the LRU. Called internally when the LRU determines
318  * that it is full. May also be called externally when necessary to
319  * explicitly evict the page.
320  *
321  * It is legal for this method to either evict the page as requested, do
322  * nothing (in which case the eviction will be requested again at the next
323  * epoch), or requeue itself on the tail of the queue (in which case the
324  * eviction will be requested again much later).
325  */
328  dequeue_lru();
329  CDWriter cdata(_cycler, true);
330  cdata->_buffer.page_out(_book);
331 }
332 
333 /**
334  * Removes the indicated PreparedGraphicsObjects table from the data array's
335  * table, without actually releasing the data array. This is intended to be
336  * called only from PreparedGraphicsObjects::release_vertex_buffer(); it
337  * should never be called by user code.
338  */
339 void GeomVertexArrayData::
340 clear_prepared(PreparedGraphicsObjects *prepared_objects) {
341  nassertv(_contexts != nullptr);
342 
343  Contexts::iterator ci;
344  ci = _contexts->find(prepared_objects);
345  if (ci != _contexts->end()) {
346  _contexts->erase(ci);
347  if (_contexts->empty()) {
348  delete _contexts;
349  _contexts = nullptr;
350  }
351  } else {
352  // If this assertion fails, clear_prepared() was given a prepared_objects
353  // which the data array didn't know about.
354  nassert_raise("unknown PreparedGraphicsObjects");
355  }
356 }
357 
358 /**
359  * Fills a new data array with all numeric values expressed in the indicated
360  * array reversed, byte-for-byte, to convert littleendian to bigendian and
361  * vice-versa.
362  */
363 void GeomVertexArrayData::
364 reverse_data_endianness(unsigned char *dest, const unsigned char *source,
365  size_t size) {
366  int num_columns = _array_format->get_num_columns();
367 
368  // Walk through each row of the data.
369  for (size_t pi = 0; pi < size; pi += _array_format->get_stride()) {
370  // For each row, visit all of the columns; and for each column, visit all
371  // of the components of that column.
372  for (int ci = 0; ci < num_columns; ++ci) {
373  const GeomVertexColumn *col = _array_format->get_column(ci);
374  int component_bytes = col->get_component_bytes();
375  if (component_bytes > 1) {
376  // Get the index of the beginning of the column.
377  size_t ci = pi + col->get_start();
378 
379  int num_components = col->get_num_components();
380  for (int cj = 0; cj < num_components; ++cj) {
381  // Reverse the bytes of each component.
382  ReversedNumericData nd(source + ci, component_bytes);
383  nd.store_value(dest + ci, component_bytes);
384  ci += component_bytes;
385  }
386  }
387  }
388  }
389 }
390 
391 /**
392  * Tells the BamReader how to create objects of type GeomVertexArrayData.
393  */
396  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
397 }
398 
399 /**
400  * Writes the contents of this object to the datagram for shipping out to a
401  * Bam file.
402  */
406 
407  manager->write_pointer(dg, _array_format);
408  manager->write_cdata(dg, _cycler, this);
409 }
410 
411 /**
412  * Called by CData::fillin to read the raw data of the array from the
413  * indicated datagram.
414  */
415 PTA_uchar GeomVertexArrayData::
417  size_t size = scan.get_uint32();
418  PTA_uchar data = PTA_uchar::empty_array(size, get_class_type());
419  const unsigned char *source_data =
420  (const unsigned char *)scan.get_datagram().get_data();
421  memcpy(data, source_data + scan.get_current_index(), size);
422  scan.skip_bytes(size);
423 
424  return data;
425 }
426 
427 /**
428  * Receives an array of pointers, one for each time manager->read_pointer()
429  * was called in fillin(). Returns the number of pointers processed.
430  */
433  int pi = CopyOnWriteObject::complete_pointers(p_list, manager);
434 
435  _array_format = DCAST(GeomVertexArrayFormat, p_list[pi++]);
436 
437  return pi;
438 }
439 
440 /**
441  * Called by the BamReader to perform any final actions needed for setting up
442  * the object after all objects have been read and all pointers have been
443  * completed.
444  */
446 finalize(BamReader *manager) {
447  // Now we need to register the format that we have read from the bam file
448  // (since it doesn't come out of the bam file automatically registered).
449  // This may change the format's pointer, which we should then update our own
450  // data to reflect. But since this may cause the unregistered object to
451  // destruct, we have to also tell the BamReader to return the new object
452  // from now on.
453 
454  CDWriter cdata(_cycler, true);
455 
456  CPT(GeomVertexArrayFormat) new_array_format =
457  GeomVertexArrayFormat::register_format(_array_format);
458 
459  manager->change_pointer(_array_format, new_array_format);
460  _array_format = new_array_format;
461 
462  PT(BamAuxData) aux_data = (BamAuxData *)manager->get_aux_data(this, "");
463  if (aux_data != nullptr) {
464  if (aux_data->_endian_reversed) {
465  // Now is the time to endian-reverse the data.
466  VertexDataBuffer new_buffer(cdata->_buffer.get_size());
467  reverse_data_endianness(new_buffer.get_write_pointer(), cdata->_buffer.get_read_pointer(true), cdata->_buffer.get_size());
468  cdata->_buffer.swap(new_buffer);
469  }
470  }
471 
472  set_lru_size(cdata->_buffer.get_size());
473 }
474 
475 /**
476  * This function is called by the BamReader's factory when a new object of
477  * type GeomVertexArrayData is encountered in the Bam file. It should create
478  * the GeomVertexArrayData and extract its information from the file.
479  */
480 TypedWritable *GeomVertexArrayData::
481 make_from_bam(const FactoryParams &params) {
483  DatagramIterator scan;
484  BamReader *manager;
485 
486  parse_params(params, scan, manager);
487  object->fillin(scan, manager);
488  manager->register_finalize(object);
489 
490  return object;
491 }
492 
493 /**
494  * This internal function is called by make_from_bam to read in all of the
495  * relevant data from the BamFile for the new GeomVertexArrayData.
496  */
497 void GeomVertexArrayData::
498 fillin(DatagramIterator &scan, BamReader *manager) {
499  CopyOnWriteObject::fillin(scan, manager);
500 
501  manager->read_pointer(scan);
502  manager->read_cdata(scan, _cycler, this);
503 }
504 
505 /**
506  *
507  */
508 GeomVertexArrayData::CData::
509 ~CData() {
510 }
511 
512 /**
513  *
514  */
515 CycleData *GeomVertexArrayData::CData::
516 make_copy() const {
517  return new CData(*this);
518 }
519 
520 /**
521  * Writes the contents of this object to the datagram for shipping out to a
522  * Bam file.
523  */
524 void GeomVertexArrayData::CData::
525 write_datagram(BamWriter *manager, Datagram &dg, void *extra_data) const {
526  GeomVertexArrayData *array_data = (GeomVertexArrayData *)extra_data;
527  dg.add_uint8(_usage_hint);
528 
529  dg.add_uint32(_buffer.get_size());
530 
531  if (manager->get_file_endian() == BamWriter::BE_native) {
532  // For native endianness, we only have to write the data directly.
533  dg.append_data(_buffer.get_read_pointer(true), _buffer.get_size());
534 
535  } else {
536  // Otherwise, we have to convert it.
537  unsigned char *new_data = (unsigned char *)alloca(_buffer.get_size());
538  array_data->reverse_data_endianness(new_data, _buffer.get_read_pointer(true), _buffer.get_size());
539  dg.append_data(new_data, _buffer.get_size());
540  }
541 }
542 
543 /**
544  * This internal function is called by make_from_bam to read in all of the
545  * relevant data from the BamFile for the new GeomVertexArrayData.
546  */
547 void GeomVertexArrayData::CData::
548 fillin(DatagramIterator &scan, BamReader *manager, void *extra_data) {
549  GeomVertexArrayData *array_data = (GeomVertexArrayData *)extra_data;
550  _usage_hint = (UsageHint)scan.get_uint8();
551 
552  if (manager->get_file_minor_ver() < 8) {
553  // Before bam version 6.8, the array data was a PTA_uchar.
554  PTA_uchar new_data;
555  READ_PTA(manager, scan, array_data->read_raw_data, new_data);
556  _buffer.unclean_realloc(new_data.size());
557  _buffer.set_size(new_data.size());
558  memcpy(_buffer.get_write_pointer(), &new_data[0], new_data.size());
559 
560  } else {
561  // Now, the array data is just stored directly.
562  size_t size = scan.get_uint32();
563  _buffer.unclean_realloc(size);
564  _buffer.set_size(size);
565 
566  const unsigned char *source_data =
567  (const unsigned char *)scan.get_datagram().get_data();
568  memcpy(_buffer.get_write_pointer(), source_data + scan.get_current_index(), size);
569  scan.skip_bytes(size);
570  }
571 
572  bool endian_reversed = false;
573 
574  if (manager->get_file_endian() != BamReader::BE_native) {
575  // For non-native endian files, we have to convert the data.
576 
577  if (array_data->_array_format == nullptr) {
578  // But we can't do that until we've completed the _array_format pointer,
579  // which tells us how to convert it.
580  endian_reversed = true;
581  } else {
582  // Since we have the _array_format pointer now, we can reverse it
583  // immediately (and we should, to support threaded CData updates).
584  VertexDataBuffer new_buffer(_buffer.get_size());
585  array_data->reverse_data_endianness(new_buffer.get_write_pointer(), _buffer.get_read_pointer(true), _buffer.get_size());
586  _buffer.swap(new_buffer);
587  }
588  }
589 
590  if (endian_reversed) {
591  PT(BamAuxData) aux_data = new BamAuxData;
592  aux_data->_endian_reversed = endian_reversed;
593  manager->set_aux_data(array_data, "", aux_data);
594  }
595 
596  array_data->set_lru_size(_buffer.get_size());
597 
598  _modified = Geom::get_next_modified();
599 }
600 
601 /**
602  * Returns a writable pointer to the beginning of the actual data stream.
603  */
604 unsigned char *GeomVertexArrayDataHandle::
606  nassertr(_writable, nullptr);
607  mark_used();
608  _cdata->_modified = Geom::get_next_modified();
609  return _cdata->_buffer.get_write_pointer();
610 }
611 
612 /**
613  *
614  */
615 bool GeomVertexArrayDataHandle::
616 set_num_rows(int n) {
617  nassertr(_writable, false);
618  mark_used();
619 
620  int stride = _object->_array_format->get_stride();
621  size_t new_size = n * stride;
622  size_t orig_size = _cdata->_buffer.get_size();
623 
624  if (gobj_cat.is_spam()) {
625  gobj_cat.spam()
626  << _object << ".set_num_rows(" << n << "), size = " << new_size << "\n";
627  }
628 
629  if (new_size != orig_size) {
630  size_t orig_reserved_size = _cdata->_buffer.get_reserved_size();
631  if (new_size > orig_reserved_size) {
632  // Add more rows. Go up to the next power of two bytes, mainly to
633  // reduce the number of allocs needed.
634  size_t new_reserved_size = (size_t)Texture::up_to_power_2((int)new_size);
635  nassertr(new_reserved_size >= new_size, false);
636 
637  _cdata->_buffer.clean_realloc(new_reserved_size);
638 
639  } else if (new_size == 0) {
640  // If we set the number of rows to 0, go ahead and clear the buffer
641  // altogether, and let the user build it up again from nothing, to try
642  // to reduce frivolous memory waste.
643  _cdata->_buffer.clear();
644  }
645 
646  _cdata->_buffer.set_size(new_size);
647 
648  // Now ensure that the newly-added rows are initialized to 0.
649  if (new_size > orig_size) {
650  memset(_cdata->_buffer.get_write_pointer() + orig_size, 0,
651  new_size - orig_size);
652  }
653 
654  _cdata->_modified = Geom::get_next_modified();
655 
656  if (get_current_thread()->get_pipeline_stage() == 0) {
657  _object->set_lru_size(_cdata->_buffer.get_size());
658  }
659 
660  nassertr(get_num_rows() == n, true);
661  return true;
662  }
663 
664  nassertr(get_num_rows() == n, false);
665  return false;
666 }
667 
668 /**
669  *
670  */
671 bool GeomVertexArrayDataHandle::
672 unclean_set_num_rows(int n) {
673  nassertr(_writable, false);
674  mark_used();
675 
676  int stride = _object->_array_format->get_stride();
677  size_t new_size = n * stride;
678  size_t orig_size = _cdata->_buffer.get_size();
679  size_t orig_reserved_size = _cdata->_buffer.get_reserved_size();
680 
681  if (new_size != orig_size || new_size != orig_reserved_size) {
682  // Since this is unclean_set_num_rows(), we won't be using it to
683  // incrementally increase the array; instead, it will generally be used
684  // only to create an array initially. So it makes sense to set the
685  // reserved size to precisely the same as the target size.
686 
687  _cdata->_buffer.unclean_realloc(new_size);
688  _cdata->_buffer.set_size(new_size);
689 
690  // No need to fill to zero or copy the old buffer, since this is
691  // unclean_set_num_rows().
692 
693  if (new_size != orig_size) {
694  _cdata->_modified = Geom::get_next_modified();
695 
696  if (get_current_thread()->get_pipeline_stage() == 0) {
697  _object->set_lru_size(_cdata->_buffer.get_size());
698  }
699  }
700  return true;
701  }
702 
703  return false;
704 }
705 
706 /**
707  *
708  */
709 bool GeomVertexArrayDataHandle::
710 reserve_num_rows(int n) {
711  nassertr(_writable, false);
712  mark_used();
713 
714  int stride = _object->_array_format->get_stride();
715  size_t new_reserved_size = n * stride;
716  new_reserved_size = max(_cdata->_buffer.get_size(), new_reserved_size);
717  size_t orig_reserved_size = _cdata->_buffer.get_reserved_size();
718 
719  if (gobj_cat.is_debug()) {
720  gobj_cat.debug()
721  << _object << ".reserve_num_rows(" << n << "), size = " << new_reserved_size << "\n";
722  }
723 
724  if (new_reserved_size != orig_reserved_size) {
725  // We allow the user to set the alloc point smaller with this call,
726  // assuming the user knows what he's doing. This allows the user to
727  // reduce wasted memory after completely filling up a buffer.
728  _cdata->_buffer.clean_realloc(new_reserved_size);
729  return true;
730  }
731 
732  return false;
733 }
734 
735 /**
736  * Copies the entire data array from the other object.
737  */
740  nassertv(_writable);
741  other->mark_used();
742 
743  size_t size = other->_cdata->_buffer.get_size();
744  const unsigned char *source = other->_cdata->_buffer.get_read_pointer(true);
745 
746  copy_data_from(source, size);
747 }
748 
749 /**
750  * Copies a portion of the data array from the other object into a portion of
751  * the data array of this object. If to_size != from_size, the size of this
752  * data array is adjusted accordingly.
753  */
755 copy_subdata_from(size_t to_start, size_t to_size,
756  const GeomVertexArrayDataHandle *other,
757  size_t from_start, size_t from_size) {
758  other->mark_used();
759 
760  const VertexDataBuffer &from_buffer = other->_cdata->_buffer;
761  size_t from_buffer_orig_size = from_buffer.get_size();
762  from_start = min(from_start, from_buffer_orig_size);
763  from_size = min(from_size, from_buffer_orig_size - from_start);
764 
765  copy_subdata_from(to_start, to_size,
766  other->get_read_pointer(true),
767  from_start, from_size);
768 }
769 
770 /**
771  * Copies the entire data array from the buffer.
772  */
774 copy_data_from(const unsigned char *source, size_t size) {
775  nassertv(_writable);
776  mark_used();
777 
778  _cdata->_buffer.unclean_realloc(size);
779  _cdata->_buffer.set_size(size);
780 
781  unsigned char *dest = _cdata->_buffer.get_write_pointer();
782  memcpy(dest, source, size);
783 
784  _cdata->_modified = Geom::get_next_modified();
785 
786  if (get_current_thread()->get_pipeline_stage() == 0) {
787  _object->set_lru_size(_cdata->_buffer.get_size());
788  }
789 }
790 
791 /**
792  * Copies a portion of the data array from the buffer into a portion of the
793  * data array of this object. If to_size != from_size, the size of this data
794  * array is adjusted accordingly.
795  */
797 copy_subdata_from(size_t to_start, size_t to_size,
798  const unsigned char *source,
799  size_t from_start, size_t from_size) {
800  nassertv(_writable);
801  mark_used();
802 
803  VertexDataBuffer &to_buffer = _cdata->_buffer;
804  size_t to_buffer_orig_size = to_buffer.get_size();
805  to_start = min(to_start, to_buffer_orig_size);
806  to_size = min(to_size, to_buffer_orig_size - to_start);
807 
808  if (from_size < to_size) {
809  // Reduce the array.
810  unsigned char *pointer = to_buffer.get_write_pointer();
811  memmove(pointer + to_start + to_size,
812  pointer + to_start + from_size,
813  to_buffer_orig_size - (to_start + to_size));
814  to_buffer.set_size(to_buffer_orig_size + from_size - to_size);
815 
816  } else if (to_size < from_size) {
817  // Expand the array.
818  size_t needed_size = to_buffer_orig_size + from_size - to_size;
819  size_t to_buffer_orig_reserved_size = to_buffer.get_reserved_size();
820  if (needed_size > to_buffer_orig_reserved_size) {
821  size_t new_reserved_size = (size_t)Texture::up_to_power_2((int)needed_size);
822  to_buffer.clean_realloc(new_reserved_size);
823  }
824  to_buffer.set_size(needed_size);
825 
826  unsigned char *pointer = to_buffer.get_write_pointer();
827  memmove(pointer + to_start + to_size,
828  pointer + to_start + from_size,
829  to_buffer_orig_size - (to_start + to_size));
830  }
831 
832  // Now copy the data.
833  memcpy(to_buffer.get_write_pointer() + to_start,
834  source + from_start, from_size);
835  _cdata->_modified = Geom::get_next_modified();
836 
837  if (get_current_thread()->get_pipeline_stage() == 0) {
838  _object->set_lru_size(_cdata->_buffer.get_size());
839  }
840 }
841 
842 /**
843  * Replaces the entire raw data array with the contents of the indicated
844  * string. This is primarily for the benefit of high-level languages like
845  * Python.
846  */
848 set_data(const vector_uchar &data) {
849  nassertv(_writable);
850  mark_used();
851 
852  _cdata->_buffer.unclean_realloc(data.size());
853  _cdata->_buffer.set_size(data.size());
854  memcpy(_cdata->_buffer.get_write_pointer(), data.data(), data.size());
855 
856  _cdata->_modified = Geom::get_next_modified();
857 
858  if (get_current_thread()->get_pipeline_stage() == 0) {
859  _object->set_lru_size(_cdata->_buffer.get_size());
860  }
861 }
862 
863 /**
864  * Replaces a portion of the data array from the indicated string. If size !=
865  * data.size(), the size of this data array is adjusted accordingly.
866  *
867  * This is primarily for the benefit of high-level languages like Python.
868  */
870 set_subdata(size_t start, size_t size, const vector_uchar &data) {
871  nassertv(_writable);
872  mark_used();
873 
874  VertexDataBuffer &to_buffer = _cdata->_buffer;
875  size_t to_buffer_orig_size = to_buffer.get_size();
876  start = min(start, to_buffer_orig_size);
877  size = min(size, to_buffer_orig_size - start);
878 
879  size_t from_size = data.size();
880 
881  if (from_size < size) {
882  // Reduce the array.
883  unsigned char *pointer = to_buffer.get_write_pointer();
884  memmove(pointer + start + from_size,
885  pointer + start + size,
886  to_buffer_orig_size - (start + size));
887  to_buffer.set_size(to_buffer_orig_size + from_size - size);
888 
889  } else if (size < from_size) {
890  // Expand the array.
891  size_t needed_size = to_buffer_orig_size + from_size - size;
892  size_t to_buffer_orig_reserved_size = to_buffer.get_reserved_size();
893  if (needed_size > to_buffer_orig_reserved_size) {
894  size_t new_reserved_size = (size_t)Texture::up_to_power_2((int)needed_size);
895  to_buffer.clean_realloc(new_reserved_size);
896  }
897  to_buffer.set_size(needed_size);
898 
899  unsigned char *pointer = to_buffer.get_write_pointer();
900  memmove(pointer + start + from_size,
901  pointer + start + size,
902  to_buffer_orig_size - (start + size));
903  }
904 
905  // Now copy the data.
906  memcpy(to_buffer.get_write_pointer() + start, data.data(), from_size);
907  _cdata->_modified = Geom::get_next_modified();
908 
909  if (get_current_thread()->get_pipeline_stage() == 0) {
910  _object->set_lru_size(_cdata->_buffer.get_size());
911  }
912 }
VertexBufferContext * prepare_now(PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg)
Creates a context for the data on the particular GSG, if it does not already exist.
unsigned char * get_write_pointer()
Returns a writable pointer to the raw data.
size_t get_size() const
Returns the number of bytes in the buffer.
An implementation of a very simple LRU algorithm.
Definition: simpleLru.h:28
int compare_to(const GeomVertexArrayData &other) const
Returns 0 if the two arrays are equivalent, even if they are not the same pointer.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
PTA_uchar read_raw_data(BamReader *manager, DatagramIterator &source)
Called by CData::fillin to read the raw data of the array from the indicated datagram.
AuxData * get_aux_data(TypedWritable *obj, const std::string &name) const
Returns the pointer previously associated with the bam reader by a previous call to set_aux_data(),...
Definition: bamReader.cxx:189
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void operator=(const GeomVertexArrayData &copy)
The copy assignment operator is not pipeline-safe.
void append_data(const void *data, size_t size)
Appends some more raw data to the end of the datagram.
Definition: datagram.cxx:129
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
set_usage_hint
Changes the UsageHint hint for this array.
void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler)
Reads in the indicated CycleData object.
Definition: bamReader.cxx:695
void mark_used_lru() const
To be called when the page is used; this will move it to the tail of the SimpleLru queue it is alread...
Definition: simpleLru.I:152
const Datagram & get_datagram() const
Return the datagram of this iterator.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class is similar to CycleDataWriter, except it allows writing to a particular stage of the pipel...
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:47
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
Definition: bamWriter.cxx:425
bool is_vertex_buffer_queued(const GeomVertexArrayData *data) const
Returns true if the vertex buffer has been queued on this GSG, false otherwise.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int release_all()
Frees the context allocated on all objects for which the data has been declared.
void release_vertex_buffer(VertexBufferContext *vbc)
Indicates that a data context, created by a previous call to prepare_vertex_buffer(),...
void dequeue_lru()
Removes the page from its SimpleLru.
Definition: simpleLru.I:134
void begin_epoch()
Marks the end of the previous epoch and the beginning of the next one.
Definition: simpleLru.I:77
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
VertexBufferContext * prepare_vertex_buffer_now(GeomVertexArrayData *data, GraphicsStateGuardianBase *gsg)
Immediately creates a new VertexBufferContext for the indicated data and returns it.
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static SimpleLru * get_global_lru(RamClass rclass)
Returns a pointer to the global LRU object that manages the VertexDataPage's with the indicated RamCl...
This data object is returned by GeomVertexArrayData::get_handle() or modify_handle().
A table of objects that are saved within the graphics context for reference by handle later.
static void lru_epoch()
Marks that an epoch has passed in each LRU.
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:83
virtual void fillin(DatagramIterator &scan, BamReader *manager)
This internal function is intended to be called by each class's make_from_bam() method to read in all...
This defines how a single column is interleaved within a vertex array stored within a Geom.
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:94
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void prepare(PreparedGraphicsObjects *prepared_objects)
Indicates that the data should be enqueued to be prepared in the indicated prepared_objects at the be...
void clean_realloc(size_t reserved_size)
Changes the "reserved" size of the buffer, preserving its data (except for any data beyond the new en...
virtual void evict_lru()
Evicts the page from the LRU.
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition: bamReader.I:275
size_t get_current_index() const
Returns the current position within the datagram of the next piece of data to extract.
get_array_format
Returns the format object that describes this array.
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
void skip_bytes(size_t size)
Skips over the indicated number of bytes in the datagram.
bool change_pointer(const TypedWritable *orig_pointer, const TypedWritable *new_pointer)
Indicates that an object recently read from the bam stream should be replaced with a new object.
Definition: bamReader.cxx:464
One atomic piece that may be managed by a SimpleLru chain.
Definition: simpleLru.h:65
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
bool dequeue_vertex_buffer(GeomVertexArrayData *data)
Removes a buffer from the queued list of data arrays to be prepared.
A block of bytes that stores the actual raw vertex data referenced by a GeomVertexArrayData object.
void set_data(const vector_uchar &data)
Replaces the entire raw data array with the contents of the indicated string.
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
size_t get_reserved_size() const
Returns the total number of bytes "reserved" in the buffer.
bool release(PreparedGraphicsObjects *prepared_objects)
Frees the data context only on the indicated object, if it exists there.
void enqueue_vertex_buffer(GeomVertexArrayData *data)
Indicates that a buffer would like to be put on the list to be prepared when the GSG is next ready to...
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:73
void set_size(size_t size)
Changes the size of the buffer.
void register_finalize(TypedWritable *whom)
Should be called by an object reading itself from the Bam file to indicate that this particular objec...
Definition: bamReader.cxx:808
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool is_prepared(PreparedGraphicsObjects *prepared_objects) const
Returns true if the data has already been prepared or enqueued for preparation on the indicated GSG,...
unsigned char * get_write_pointer()
Returns a writable pointer to the beginning of the actual data stream.
NativeNumericData and ReversedNumericData work together to provide a sneaky interface for automatical...
void copy_data_from(const GeomVertexArrayDataHandle *other)
Copies the entire data array from the other object.
A collection of VertexDataPages, which can be used to allocate new VertexDataBlock objects.
int get_start() const
Returns the byte within the array record at which this column starts.
void set_aux_data(TypedWritable *obj, const std::string &name, AuxData *data)
Associates an arbitrary block of data with the indicated object (or NULL), and the indicated name.
Definition: bamReader.cxx:167
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
const unsigned char * get_read_pointer(bool force) const
Returns a readable pointer to the beginning of the actual data stream, or NULL if the data is not cur...
int get_component_bytes() const
Returns the number of bytes used by each component (that is, by one element of the numeric type).
This base class provides basic reference counting, but also can be used with a CopyOnWritePointer to ...
get_file_endian
Returns the endian preference indicated by the Bam file currently being read.
Definition: bamReader.h:159
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
static int up_to_power_2(int value)
Returns the smallest power of 2 greater than or equal to value.
Definition: texture.cxx:1983
int get_num_rows() const
Returns the number of rows stored in the array, based on the number of bytes and the stride.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
void mark_used() const
Marks the array data recently-used.
A thread; that is, a lightweight process.
Definition: thread.h:46
void copy_subdata_from(size_t to_start, size_t to_size, const GeomVertexArrayDataHandle *other, size_t from_start, size_t from_size)
Copies a portion of the data array from the other object into a portion of the data array of this obj...
bool read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:610
void set_subdata(size_t start, size_t size, const vector_uchar &data)
Replaces a portion of the data array from the indicated string.
static UpdateSeq get_next_modified()
Returns a monotonically increasing sequence.
Definition: geom.cxx:1324
This is a special class object that holds all the information returned by a particular GSG to indicat...
virtual int complete_pointers(TypedWritable **plist, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
This is a convenience class to specialize ConfigVariable as an integer type.
This describes the structure of a single array within a Geom data.
get_usage_hint
Returns the usage hint that describes to the rendering backend how often the vertex data will be modi...
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:50
get_file_endian
Returns the endian preference indicated by the Bam file currently being written.
Definition: bamWriter.h:93
static void register_with_read_factory()
Tells the BamReader how to create objects of type GeomVertexArrayData.
A class to retrieve the individual data elements previously stored in a Datagram.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
int get_num_components() const
Returns the number of components of the column: the number of instances of the NumericType in each el...
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
PT(CopyOnWriteObject) GeomVertexArrayData
Required to implement CopyOnWriteObject.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the data for one array of a GeomVertexData structure.
virtual void finalize(BamReader *manager)
Called by the BamReader to perform any final actions needed for setting up the object after all objec...
const void * get_data() const
Returns a pointer to the beginning of the datagram's data.
Definition: datagram.I:327
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:317