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