From 42372e139cf620da227eec26ce9b6458d0ad32b2 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 16 Dec 2022 19:37:28 +0100 Subject: [PATCH 01/16] motiontrail: Update API documentation --- direct/src/motiontrail/MotionTrail.py | 36 ++++++++++++++++----------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/direct/src/motiontrail/MotionTrail.py b/direct/src/motiontrail/MotionTrail.py index 097f61ee09..2f33f48410 100644 --- a/direct/src/motiontrail/MotionTrail.py +++ b/direct/src/motiontrail/MotionTrail.py @@ -47,9 +47,9 @@ class MotionTrailFrame: class MotionTrail(NodePath, DirectObject): """Generates smooth geometry-based motion trails behind a moving object. - To use this class, first define the shape of the cross-section of the trail - by repeatedly calling `add_vertex()` and `set_vertex_color()`. - When this is done, `update_vertices()` must be called. + To use this class, first define the shape of the cross-section polygon that + is to be extruded along the motion trail by calling `add_vertex()` and + `set_vertex_color()`. When this is done, call `update_vertices()`. To generate the motion trail, either call `register_motion_trail()` to have Panda update it automatically, or periodically call the method @@ -80,6 +80,10 @@ class MotionTrail(NodePath, DirectObject): @classmethod def setGlobalEnable(cls, enable): + """Set this to False to have the task stop updating all motion trails. + This does not prevent updating them manually using the + `update_motion_trail()` method. + """ cls.global_enable = enable def __init__(self, name, parent_node_path): @@ -117,14 +121,14 @@ class MotionTrail(NodePath, DirectObject): self.continuous_motion_trail = True self.color_scale = 1.0 - ## How long the time window is for which the trail is computed. Can be - ## increased to obtain a longer trail, decreased for a shorter trail. + #: How long the time window is for which the trail is computed. Can be + #: increased to obtain a longer trail, decreased for a shorter trail. self.time_window = 1.0 - ## How often the trail updates, in seconds. The default is 0.0, which - ## has the trail updated every frame for the smoothest result. Higher - ## values will generate a choppier trail. The `use_nurbs` option can - ## compensate partially for this choppiness, however. + #: How often the trail updates, in seconds. The default is 0.0, which + #: has the trail updated every frame for the smoothest result. Higher + #: values will generate a choppier trail. The `use_nurbs` option can + #: compensate partially for this choppiness, however. self.sampling_time = 0.0 self.square_t = True @@ -135,9 +139,9 @@ class MotionTrail(NodePath, DirectObject): # node path states self.reparentTo(parent_node_path) - ## A `.GeomNode` object containing the generated geometry. By default - ## parented to the MotionTrail itself, but can be reparented elsewhere - ## if necessary. + #: A `.GeomNode` object containing the generated geometry. By default + #: parented to the MotionTrail itself, but can be reparented elsewhere + #: if necessary. self.geom_node = GeomNode("motion_trail") self.geom_node_path = self.attachNewNode(self.geom_node) node_path = self.geom_node_path @@ -167,9 +171,11 @@ class MotionTrail(NodePath, DirectObject): self.relative_to_render = False - ## Set this to True to use a NURBS curve to generate a smooth trail, - ## even if the underlying animation or movement is janky. + #: Set this to True to use a NURBS curve to generate a smooth trail, + #: even if the underlying animation or movement is janky. self.use_nurbs = False + + #: This can be changed to fine-tune the resolution of the NURBS curve. self.resolution_distance = 0.5 self.cmotion_trail = CMotionTrail() @@ -245,7 +251,7 @@ class MotionTrail(NodePath, DirectObject): return Task.cont def add_vertex(self, vertex_id, vertex_function=None, context=None): - """This must be called repeatedly to define the polygon that forms the + """This must be called initially to define the polygon that forms the cross-section of the generated motion trail geometry. The first argument is a user-defined vertex identifier, the second is a function that will be called with three parameters that should return the From 32297ecd025fa8c616d4f574be45d4ceb2cff096 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 16 Dec 2022 19:37:49 +0100 Subject: [PATCH 02/16] showbase: Fix some members not showing up in API documentation --- direct/src/showbase/ShowBase.py | 66 ++++++++++++++++----------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 1f286588d2..8e39f7b6ab 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -1160,7 +1160,7 @@ class ShowBase(DirectObject.DirectObject): Creates the render scene graph, the primary scene graph for rendering 3-d geometry. """ - ## This is the root of the 3-D scene graph. + #: This is the root of the 3-D scene graph. self.render = NodePath('render') self.render.setAttrib(RescaleNormalAttrib.makeDefault()) @@ -1179,7 +1179,7 @@ class ShowBase(DirectObject.DirectObject): # for the benefit of creating DirectGui elements before ShowBase. from . import ShowBaseGlobal - ## This is the root of the 2-D scene graph. + #: This is the root of the 2-D scene graph. self.render2d = ShowBaseGlobal.render2d # Set up some overrides to turn off certain properties which @@ -1200,12 +1200,12 @@ class ShowBase(DirectObject.DirectObject): self.render2d.setMaterialOff(1) self.render2d.setTwoSided(1) - ## The normal 2-d DisplayRegion has an aspect ratio that - ## matches the window, but its coordinate system is square. - ## This means anything we parent to render2d gets stretched. - ## For things where that makes a difference, we set up - ## aspect2d, which scales things back to the right aspect - ## ratio along the X axis (Z is still from -1 to 1) + #: The normal 2-d DisplayRegion has an aspect ratio that + #: matches the window, but its coordinate system is square. + #: This means anything we parent to render2d gets stretched. + #: For things where that makes a difference, we set up + #: aspect2d, which scales things back to the right aspect + #: ratio along the X axis (Z is still from -1 to 1) self.aspect2d = ShowBaseGlobal.aspect2d aspectRatio = self.getAspectRatio() @@ -1213,13 +1213,13 @@ class ShowBase(DirectObject.DirectObject): self.a2dBackground = self.aspect2d.attachNewNode("a2dBackground") - ## The Z position of the top border of the aspect2d screen. + #: The Z position of the top border of the aspect2d screen. self.a2dTop = 1.0 - ## The Z position of the bottom border of the aspect2d screen. + #: The Z position of the bottom border of the aspect2d screen. self.a2dBottom = -1.0 - ## The X position of the left border of the aspect2d screen. + #: The X position of the left border of the aspect2d screen. self.a2dLeft = -aspectRatio - ## The X position of the right border of the aspect2d screen. + #: The X position of the right border of the aspect2d screen. self.a2dRight = aspectRatio self.a2dTopCenter = self.aspect2d.attachNewNode("a2dTopCenter") @@ -1259,9 +1259,9 @@ class ShowBase(DirectObject.DirectObject): self.a2dBottomRight.setPos(self.a2dRight, 0, self.a2dBottom) self.a2dBottomRightNs.setPos(self.a2dRight, 0, self.a2dBottom) - ## This special root, pixel2d, uses units in pixels that are relative - ## to the window. The upperleft corner of the window is (0, 0), - ## the lowerleft corner is (xsize, -ysize), in this coordinate system. + #: This special root, pixel2d, uses units in pixels that are relative + #: to the window. The upperleft corner of the window is (0, 0), + #: the lowerleft corner is (xsize, -ysize), in this coordinate system. self.pixel2d = self.render2d.attachNewNode(PGTop("pixel2d")) self.pixel2d.setPos(-1, 0, 1) xsize, ysize = self.getSize() @@ -1291,25 +1291,25 @@ class ShowBase(DirectObject.DirectObject): self.render2dp.setMaterialOff(1) self.render2dp.setTwoSided(1) - ## The normal 2-d DisplayRegion has an aspect ratio that - ## matches the window, but its coordinate system is square. - ## This means anything we parent to render2dp gets stretched. - ## For things where that makes a difference, we set up - ## aspect2dp, which scales things back to the right aspect - ## ratio along the X axis (Z is still from -1 to 1) + #: The normal 2-d DisplayRegion has an aspect ratio that + #: matches the window, but its coordinate system is square. + #: This means anything we parent to render2dp gets stretched. + #: For things where that makes a difference, we set up + #: aspect2dp, which scales things back to the right aspect + #: ratio along the X axis (Z is still from -1 to 1) self.aspect2dp = self.render2dp.attachNewNode(PGTop("aspect2dp")) self.aspect2dp.node().setStartSort(16384) aspectRatio = self.getAspectRatio() self.aspect2dp.setScale(1.0 / aspectRatio, 1.0, 1.0) - ## The Z position of the top border of the aspect2dp screen. + #: The Z position of the top border of the aspect2dp screen. self.a2dpTop = 1.0 - ## The Z position of the bottom border of the aspect2dp screen. + #: The Z position of the bottom border of the aspect2dp screen. self.a2dpBottom = -1.0 - ## The X position of the left border of the aspect2dp screen. + #: The X position of the left border of the aspect2dp screen. self.a2dpLeft = -aspectRatio - ## The X position of the right border of the aspect2dp screen. + #: The X position of the right border of the aspect2dp screen. self.a2dpRight = aspectRatio self.a2dpTopCenter = self.aspect2dp.attachNewNode("a2dpTopCenter") @@ -1333,9 +1333,9 @@ class ShowBase(DirectObject.DirectObject): self.a2dpBottomLeft.setPos(self.a2dpLeft, 0, self.a2dpBottom) self.a2dpBottomRight.setPos(self.a2dpRight, 0, self.a2dpBottom) - ## This special root, pixel2d, uses units in pixels that are relative - ## to the window. The upperleft corner of the window is (0, 0), - ## the lowerleft corner is (xsize, -ysize), in this coordinate system. + #: This special root, pixel2dp, uses units in pixels that are relative + #: to the window. The upperleft corner of the window is (0, 0), + #: the lowerleft corner is (xsize, -ysize), in this coordinate system. self.pixel2dp = self.render2dp.attachNewNode(PGTop("pixel2dp")) self.pixel2dp.node().setStartSort(16384) self.pixel2dp.setPos(-1, 0, 1) @@ -1656,11 +1656,11 @@ class ShowBase(DirectObject.DirectObject): mw = self.buttonThrowers[0].getParent() - ## A special ButtonThrower to generate keyboard events and - ## include the time from the OS. This is separate only to - ## support legacy code that did not expect a time parameter; it - ## will eventually be folded into the normal ButtonThrower, - ## above. + #: A special ButtonThrower to generate keyboard events and + #: include the time from the OS. This is separate only to + #: support legacy code that did not expect a time parameter; it + #: will eventually be folded into the normal ButtonThrower, + #: above. self.timeButtonThrower = mw.attachNewNode(ButtonThrower('timeButtons')) self.timeButtonThrower.node().setPrefix('time-') self.timeButtonThrower.node().setTimeFlag(1) From 7f916eeb74c25652c4237fa3a3be7044a87677c1 Mon Sep 17 00:00:00 2001 From: rdb Date: Fri, 16 Dec 2022 19:38:33 +0100 Subject: [PATCH 03/16] gobj: Don't wastefully compute bounds for Geom with custom bounds This was done by `get_nested_vertices()`, which now won't call the expensive `compute_internal_bounds()` if there was a custom bounding volume set. --- doc/ReleaseNotes | 1 + panda/src/gobj/geom.I | 1 + panda/src/gobj/geom.cxx | 25 ++++++++++++++++++++++--- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/doc/ReleaseNotes b/doc/ReleaseNotes index eafbf4f3eb..4baeb47d6c 100644 --- a/doc/ReleaseNotes +++ b/doc/ReleaseNotes @@ -83,6 +83,7 @@ Miscellaneous * Fix interrogate syntax error with C++11-style attributes in declarators * Fix regression with BufferViewer in double-precision build (#1365) * Fix `PandaNode.nested_vertices` not updating properly +* Prevent Panda calculating bounding volume of Geom with custom bounding volume * Add `do_events()` and `process_event()` snake_case aliases in eventMgr * Support second arg of None in `replace_texture()` / `replace_material()` * Support `os.fspath()` for ConfigVariableFilename objects (#1406) diff --git a/panda/src/gobj/geom.I b/panda/src/gobj/geom.I index 3fa32ade36..0f6b5fc7e8 100644 --- a/panda/src/gobj/geom.I +++ b/panda/src/gobj/geom.I @@ -379,6 +379,7 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, */ INLINE void Geom:: mark_internal_bounds_stale(CData *cdata) { + cdata->_nested_vertices = 0; cdata->_internal_bounds_stale = true; } diff --git a/panda/src/gobj/geom.cxx b/panda/src/gobj/geom.cxx index 820c67875e..11c49f14c8 100644 --- a/panda/src/gobj/geom.cxx +++ b/panda/src/gobj/geom.cxx @@ -1119,9 +1119,28 @@ int Geom:: get_nested_vertices(Thread *current_thread) const { CDLockedReader cdata(_cycler, current_thread); if (cdata->_internal_bounds_stale) { - CDWriter cdataw(((Geom *)this)->_cycler, cdata, false); - compute_internal_bounds(cdataw, current_thread); - return cdataw->_nested_vertices; + if (cdata->_user_bounds != nullptr) { + // Don't do the expensive compute_internal_bounds call. + if (cdata->_nested_vertices == 0) { + CDWriter cdataw(((Geom *)this)->_cycler, cdata, false); + int num_vertices = 0; + + Primitives::const_iterator pi; + for (pi = cdataw->_primitives.begin(); + pi != cdataw->_primitives.end(); + ++pi) { + GeomPrimitivePipelineReader reader((*pi).get_read_pointer(current_thread), current_thread); + num_vertices += reader.get_num_vertices(); + } + + cdataw->_nested_vertices = num_vertices; + return num_vertices; + } + } else { + CDWriter cdataw(((Geom *)this)->_cycler, cdata, false); + compute_internal_bounds(cdataw, current_thread); + return cdataw->_nested_vertices; + } } return cdata->_nested_vertices; } From e5a8eb5b1daac9401ee30b1fb12e804d1f8ab71a Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 19 Dec 2022 12:55:28 +0100 Subject: [PATCH 04/16] cocoadisplay: Fix Command+Q in green-button-style fullscreen mode It seems macOS has an extra NSWindow without a delegate in this mode --- panda/src/cocoadisplay/cocoaPandaAppDelegate.mm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/panda/src/cocoadisplay/cocoaPandaAppDelegate.mm b/panda/src/cocoadisplay/cocoaPandaAppDelegate.mm index cbb84bc205..f64b2be56a 100644 --- a/panda/src/cocoadisplay/cocoaPandaAppDelegate.mm +++ b/panda/src/cocoadisplay/cocoaPandaAppDelegate.mm @@ -39,7 +39,8 @@ // Ask all the windows whether they are OK to be closed. bool should_close = true; for (NSWindow *window in [app windows]) { - if (![[window delegate] windowShouldClose:window]) { + id delegate = [window delegate]; + if (delegate != nil && ![delegate windowShouldClose:window]) { should_close = false; } } From eafcfe1d7080721b732c2e76b09ef242e8944a49 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 19 Dec 2022 14:11:50 +0100 Subject: [PATCH 05/16] motiontrail: Fix Python implementation error in double-prec mode --- direct/src/motiontrail/MotionTrail.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/direct/src/motiontrail/MotionTrail.py b/direct/src/motiontrail/MotionTrail.py index 2f33f48410..33412c99c7 100644 --- a/direct/src/motiontrail/MotionTrail.py +++ b/direct/src/motiontrail/MotionTrail.py @@ -390,21 +390,21 @@ class MotionTrail(NodePath, DirectObject): def add_geometry_quad(self, v0, v1, v2, v3, c0, c1, c2, c3, t0, t1, t2, t3): - self.vertex_writer.addData3f(v0 [0], v0 [1], v0 [2]) - self.vertex_writer.addData3f(v1 [0], v1 [1], v1 [2]) - self.vertex_writer.addData3f(v2 [0], v2 [1], v2 [2]) - self.vertex_writer.addData3f(v3 [0], v3 [1], v3 [2]) + self.vertex_writer.addData3(v0[0], v0[1], v0[2]) + self.vertex_writer.addData3(v1[0], v1[1], v1[2]) + self.vertex_writer.addData3(v2[0], v2[1], v2[2]) + self.vertex_writer.addData3(v3[0], v3[1], v3[2]) - self.color_writer.addData4f(c0) - self.color_writer.addData4f(c1) - self.color_writer.addData4f(c2) - self.color_writer.addData4f(c3) + self.color_writer.addData4(c0) + self.color_writer.addData4(c1) + self.color_writer.addData4(c2) + self.color_writer.addData4(c3) if self.texture is not None: - self.texture_writer.addData2f(t0) - self.texture_writer.addData2f(t1) - self.texture_writer.addData2f(t2) - self.texture_writer.addData2f(t3) + self.texture_writer.addData2(t0) + self.texture_writer.addData2(t1) + self.texture_writer.addData2(t2) + self.texture_writer.addData2(t3) vertex_index = self.vertex_index From 0ace26a938bf879d92c101880bd849efec3938e1 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 19 Dec 2022 15:17:37 +0100 Subject: [PATCH 06/16] gobj: Fix double-precision colors not being clamped in GeomVertexWriter --- doc/ReleaseNotes | 1 + panda/src/gobj/geomVertexColumn.cxx | 50 +++++++++++++++++++++++++++++ panda/src/gobj/geomVertexColumn.h | 4 +++ 3 files changed, 55 insertions(+) diff --git a/doc/ReleaseNotes b/doc/ReleaseNotes index 4baeb47d6c..2b8d78a58b 100644 --- a/doc/ReleaseNotes +++ b/doc/ReleaseNotes @@ -81,6 +81,7 @@ Miscellaneous * Add various useful functions to interrogatedb module * Fix Python 3 issues unpacking uint types in Python 3 (#1380) * Fix interrogate syntax error with C++11-style attributes in declarators +* Fix double-precision color values not being clamped by GeomVertexWriter * Fix regression with BufferViewer in double-precision build (#1365) * Fix `PandaNode.nested_vertices` not updating properly * Prevent Panda calculating bounding volume of Geom with custom bounding volume diff --git a/panda/src/gobj/geomVertexColumn.cxx b/panda/src/gobj/geomVertexColumn.cxx index 9e6c7bd188..2c1c4b4d6f 100644 --- a/panda/src/gobj/geomVertexColumn.cxx +++ b/panda/src/gobj/geomVertexColumn.cxx @@ -4475,6 +4475,20 @@ get_data4f(const unsigned char *pointer) { return _v4; } +/** + * + */ +const LVecBase4d &GeomVertexColumn::Packer_argb_packed:: +get_data4d(const unsigned char *pointer) { + uint32_t dword = *(const uint32_t *)pointer; + _v4d.set(GeomVertexData::unpack_abcd_b(dword), + GeomVertexData::unpack_abcd_c(dword), + GeomVertexData::unpack_abcd_d(dword), + GeomVertexData::unpack_abcd_a(dword)); + _v4d /= 255.0; + return _v4d; +} + /** * */ @@ -4489,6 +4503,20 @@ set_data4f(unsigned char *pointer, const LVecBase4f &data) { (unsigned int)(min(max(data[2], 0.0f), 1.0f) * 255.0f)); } +/** + * + */ +void GeomVertexColumn::Packer_argb_packed:: +set_data4d(unsigned char *pointer, const LVecBase4d &data) { + // when packing an argb, we want to make sure we cap the input values at 1 + // since going above one will cause the value to be truncated. + *(uint32_t *)pointer = GeomVertexData::pack_abcd + ((unsigned int)(min(max(data[3], 0.0), 1.0) * 255.0), + (unsigned int)(min(max(data[0], 0.0), 1.0) * 255.0), + (unsigned int)(min(max(data[1], 0.0), 1.0) * 255.0), + (unsigned int)(min(max(data[2], 0.0), 1.0) * 255.0)); +} + /** * */ @@ -4500,6 +4528,17 @@ get_data4f(const unsigned char *pointer) { return _v4; } +/** + * + */ +const LVecBase4d &GeomVertexColumn::Packer_rgba_uint8_4:: +get_data4d(const unsigned char *pointer) { + _v4d.set((double)pointer[0], (double)pointer[1], + (double)pointer[2], (double)pointer[3]); + _v4d /= 255.0; + return _v4d; +} + /** * */ @@ -4511,6 +4550,17 @@ set_data4f(unsigned char *pointer, const LVecBase4f &data) { pointer[3] = (unsigned int)(min(max(data[3], 0.0f), 1.0f) * 255.0f); } +/** + * + */ +void GeomVertexColumn::Packer_rgba_uint8_4:: +set_data4d(unsigned char *pointer, const LVecBase4d &data) { + pointer[0] = (unsigned int)(min(max(data[0], 0.0), 1.0) * 255.0); + pointer[1] = (unsigned int)(min(max(data[1], 0.0), 1.0) * 255.0); + pointer[2] = (unsigned int)(min(max(data[2], 0.0), 1.0) * 255.0); + pointer[3] = (unsigned int)(min(max(data[3], 0.0), 1.0) * 255.0); +} + /** * */ diff --git a/panda/src/gobj/geomVertexColumn.h b/panda/src/gobj/geomVertexColumn.h index 1ff36b3eda..e76107a2dd 100644 --- a/panda/src/gobj/geomVertexColumn.h +++ b/panda/src/gobj/geomVertexColumn.h @@ -381,7 +381,9 @@ private: class Packer_argb_packed final : public Packer_color { public: virtual const LVecBase4f &get_data4f(const unsigned char *pointer); + virtual const LVecBase4d &get_data4d(const unsigned char *pointer); virtual void set_data4f(unsigned char *pointer, const LVecBase4f &value); + virtual void set_data4d(unsigned char *pointer, const LVecBase4d &value); virtual const char *get_name() const { return "Packer_argb_packed"; @@ -391,7 +393,9 @@ private: class Packer_rgba_uint8_4 final : public Packer_color { public: virtual const LVecBase4f &get_data4f(const unsigned char *pointer); + virtual const LVecBase4d &get_data4d(const unsigned char *pointer); virtual void set_data4f(unsigned char *pointer, const LVecBase4f &value); + virtual void set_data4d(unsigned char *pointer, const LVecBase4d &value); virtual const char *get_name() const { return "Packer_rgba_uint8_4"; From e67cd74725700b236bb1d2346fc329ac4d04a07a Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 19 Dec 2022 16:19:46 +0100 Subject: [PATCH 07/16] express: Implement `copy.deepcopy()` for PointerToArray It actually makes a unique copy of the underlying array. --- doc/ReleaseNotes | 1 + panda/src/express/pointerToArray.h | 4 ++ panda/src/express/pointerToArray_ext.I | 28 ++++++++++++ panda/src/express/pointerToArray_ext.h | 4 ++ tests/express/test_pointertoarray.py | 60 ++++++++++++++++++++++++++ 5 files changed, 97 insertions(+) diff --git a/doc/ReleaseNotes b/doc/ReleaseNotes index 2b8d78a58b..946879c85a 100644 --- a/doc/ReleaseNotes +++ b/doc/ReleaseNotes @@ -71,6 +71,7 @@ Miscellaneous * Fix texture transforms sometimes not being flattened (#1392) * Fix support for `#pragma include ` in GLSL shaders * Fix `ShaderBuffer.prepare()` not doing anything +* Implement deepcopy for PointerToArray * Fix bf-cbc encryption no longer working when building with OpenSSL 3.0 * PandaNode bounds_type property was erroneously marked read-only * Fix warnings when copying OdeTriMeshGeom objects diff --git a/panda/src/express/pointerToArray.h b/panda/src/express/pointerToArray.h index 24a5322bc7..65974c28bc 100644 --- a/panda/src/express/pointerToArray.h +++ b/panda/src/express/pointerToArray.h @@ -121,6 +121,8 @@ PUBLISHED: EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags)); EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const); + + EXTENSION(PointerToArray __deepcopy__(PyObject *memo) const); #endif #else // CPPPARSER @@ -279,6 +281,8 @@ PUBLISHED: EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const); EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const); + + EXTENSION(ConstPointerToArray __deepcopy__(PyObject *memo) const); #endif #else // CPPPARSER diff --git a/panda/src/express/pointerToArray_ext.I b/panda/src/express/pointerToArray_ext.I index 425f4d87e3..a56368f36b 100644 --- a/panda/src/express/pointerToArray_ext.I +++ b/panda/src/express/pointerToArray_ext.I @@ -510,6 +510,20 @@ __releasebuffer__(PyObject *self, Py_buffer *view) const { #endif } +/** + * A special Python method that is invoked by copy.deepcopy(pta). This makes + * sure that there is truly a unique copy of the array. + */ +template +INLINE PointerToArray Extension >:: +__deepcopy__(PyObject *memo) const { + PointerToArray copy; + if (!_this->is_null()) { + copy.v() = _this->v(); + } + return copy; +} + /** * This is used to implement the buffer protocol, in order to allow efficient * access to the array data through a Python multiview object. @@ -702,3 +716,17 @@ __releasebuffer__(PyObject *self, Py_buffer *view) const { } #endif } + +/** + * A special Python method that is invoked by copy.deepcopy(pta). This makes + * sure that there is truly a unique copy of the array. + */ +template +INLINE ConstPointerToArray Extension >:: +__deepcopy__(PyObject *memo) const { + PointerToArray copy; + if (!_this->is_null()) { + copy.v() = _this->v(); + } + return copy; +} diff --git a/panda/src/express/pointerToArray_ext.h b/panda/src/express/pointerToArray_ext.h index ce6104791b..6059aec062 100644 --- a/panda/src/express/pointerToArray_ext.h +++ b/panda/src/express/pointerToArray_ext.h @@ -44,6 +44,8 @@ public: INLINE int __getbuffer__(PyObject *self, Py_buffer *view, int flags); INLINE void __releasebuffer__(PyObject *self, Py_buffer *view) const; + + INLINE PointerToArray __deepcopy__(PyObject *memo) const; }; template<> @@ -81,6 +83,8 @@ public: INLINE int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const; INLINE void __releasebuffer__(PyObject *self, Py_buffer *view) const; + + INLINE ConstPointerToArray __deepcopy__(PyObject *memo) const; }; template<> diff --git a/tests/express/test_pointertoarray.py b/tests/express/test_pointertoarray.py index e68fa2ca36..3c87373904 100644 --- a/tests/express/test_pointertoarray.py +++ b/tests/express/test_pointertoarray.py @@ -69,3 +69,63 @@ def test_cpta_float_pickle(): data_pta2 = loads(dumps(data_pta, proto)) assert tuple(data_pta2) == (1.0, 2.0, 3.0) assert data_pta2.get_data() == data_pta.get_data() + + +def test_pta_float_copy(): + from panda3d.core import PTA_float + from copy import copy + + null_pta = PTA_float() + assert copy(null_pta).is_null() + + empty_pta = PTA_float([]) + empty_pta_copy = copy(empty_pta) + assert not empty_pta_copy.is_null() + assert len(empty_pta_copy) == 0 + assert empty_pta_copy.get_ref_count() == 2 + + data_pta = PTA_float([1.0, 2.0, 3.0]) + data_pta_copy = copy(data_pta) + assert not data_pta_copy.is_null() + assert data_pta_copy.get_ref_count() == 2 + assert tuple(data_pta_copy) == (1.0, 2.0, 3.0) + + +def test_pta_float_deepcopy(): + from panda3d.core import PTA_float + from copy import deepcopy + + null_pta = PTA_float() + assert deepcopy(null_pta).is_null() + + empty_pta = PTA_float([]) + empty_pta_copy = deepcopy(empty_pta) + assert not empty_pta_copy.is_null() + assert len(empty_pta_copy) == 0 + assert empty_pta_copy.get_ref_count() == 1 + + data_pta = PTA_float([1.0, 2.0, 3.0]) + data_pta_copy = deepcopy(data_pta) + assert not data_pta_copy.is_null() + assert data_pta_copy.get_ref_count() == 1 + assert tuple(data_pta_copy) == (1.0, 2.0, 3.0) + + +def test_cpta_float_deepcopy(): + from panda3d.core import PTA_float, CPTA_float + from copy import deepcopy + + null_pta = CPTA_float(PTA_float()) + assert deepcopy(null_pta).is_null() + + empty_pta = CPTA_float([]) + empty_pta_copy = deepcopy(empty_pta) + assert not empty_pta_copy.is_null() + assert len(empty_pta_copy) == 0 + assert empty_pta_copy.get_ref_count() == 1 + + data_pta = CPTA_float([1.0, 2.0, 3.0]) + data_pta_copy = deepcopy(data_pta) + assert not data_pta_copy.is_null() + assert data_pta_copy.get_ref_count() == 1 + assert tuple(data_pta_copy) == (1.0, 2.0, 3.0) From 896346b99f7b497f346ac34ba7c7a21f6af765eb Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 19 Dec 2022 16:22:33 +0100 Subject: [PATCH 08/16] gobj: Implement `copy.deepcopy()` for Texture class Actually ensures that the underlying RAM images are really fully unique. --- doc/ReleaseNotes | 1 + panda/src/gobj/texture.h | 4 ++++ panda/src/gobj/texture_ext.cxx | 28 ++++++++++++++++++++++++++++ panda/src/gobj/texture_ext.h | 2 ++ tests/gobj/test_texture.py | 23 +++++++++++++++++++++++ 5 files changed, 58 insertions(+) diff --git a/doc/ReleaseNotes b/doc/ReleaseNotes index 946879c85a..2707d324d9 100644 --- a/doc/ReleaseNotes +++ b/doc/ReleaseNotes @@ -72,6 +72,7 @@ Miscellaneous * Fix support for `#pragma include ` in GLSL shaders * Fix `ShaderBuffer.prepare()` not doing anything * Implement deepcopy for PointerToArray +* Fix Texture deepcopy keeping a reference to the original RAM image * Fix bf-cbc encryption no longer working when building with OpenSSL 3.0 * PandaNode bounds_type property was erroneously marked read-only * Fix warnings when copying OdeTriMeshGeom objects diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index 86ca58fe02..c2ff9ad24d 100644 --- a/panda/src/gobj/texture.h +++ b/panda/src/gobj/texture.h @@ -46,6 +46,7 @@ #include "pnmImage.h" #include "pfmFile.h" #include "asyncFuture.h" +#include "extension.h" class TextureContext; class FactoryParams; @@ -472,6 +473,8 @@ PUBLISHED: MAKE_PROPERTY(keep_ram_image, get_keep_ram_image, set_keep_ram_image); MAKE_PROPERTY(cacheable, is_cacheable); + EXTENSION(PT(Texture) __deepcopy__(PyObject *memo) const); + BLOCKING INLINE bool compress_ram_image(CompressionMode compression = CM_on, QualityLevel quality_level = QL_default, GraphicsStateGuardianBase *gsg = nullptr); @@ -1110,6 +1113,7 @@ private: static TypeHandle _type_handle; + friend class Extension; friend class TextureContext; friend class PreparedGraphicsObjects; friend class TexturePool; diff --git a/panda/src/gobj/texture_ext.cxx b/panda/src/gobj/texture_ext.cxx index 9af792be08..78f1dad1b4 100644 --- a/panda/src/gobj/texture_ext.cxx +++ b/panda/src/gobj/texture_ext.cxx @@ -165,4 +165,32 @@ set_ram_image_as(PyObject *image, const std::string &provided_format) { Dtool_Raise_ArgTypeError(image, 0, "Texture.set_ram_image_as", "CPTA_uchar or buffer"); } +/** + * A special Python method that is invoked by copy.deepcopy(tex). This makes + * sure that the copy has a unique copy of the RAM image. + */ +PT(Texture) Extension:: +__deepcopy__(PyObject *memo) const { + PT(Texture) copy = _this->make_copy(); + { + Texture::CDWriter cdata(copy->_cycler, true); + for (Texture::RamImage &image : cdata->_ram_images) { + if (image._image.get_ref_count() > 1) { + PTA_uchar new_image; + new_image.v() = image._image.v(); + image._image = std::move(new_image); + } + } + { + Texture::RamImage &image = cdata->_simple_ram_image; + if (image._image.get_ref_count() > 1) { + PTA_uchar new_image; + new_image.v() = image._image.v(); + image._image = std::move(new_image); + } + } + } + return copy; +} + #endif // HAVE_PYTHON diff --git a/panda/src/gobj/texture_ext.h b/panda/src/gobj/texture_ext.h index b4f0559b08..bebe54557f 100644 --- a/panda/src/gobj/texture_ext.h +++ b/panda/src/gobj/texture_ext.h @@ -32,6 +32,8 @@ public: void set_ram_image(PyObject *image, Texture::CompressionMode compression = Texture::CM_off, size_t page_size = 0); void set_ram_image_as(PyObject *image, const std::string &provided_format); + + PT(Texture) __deepcopy__(PyObject *memo) const; }; #endif // HAVE_PYTHON diff --git a/tests/gobj/test_texture.py b/tests/gobj/test_texture.py index 4bf917a8c2..2cddddb855 100644 --- a/tests/gobj/test_texture.py +++ b/tests/gobj/test_texture.py @@ -134,3 +134,26 @@ def test_texture_clear_half(): assert col.y == -inf assert col.z == -inf assert math.isnan(col.w) + + +def test_texture_deepcopy(): + from copy import deepcopy + + empty_tex = Texture("empty-texture") + empty_tex.setup_2d_texture(16, 16, Texture.T_unsigned_byte, Texture.F_rgba) + assert not empty_tex.has_ram_image() + empty_tex2 = deepcopy(empty_tex) + assert empty_tex2.name == empty_tex.name + assert not empty_tex2.has_ram_image() + + tex = Texture("texture") + tex.setup_2d_texture(16, 16, Texture.T_unsigned_byte, Texture.F_rgba) + img = tex.make_ram_image() + assert tex.has_ram_image() + assert img.get_ref_count() == 2 + + tex2 = deepcopy(tex) + assert tex2.name == tex.name + assert tex2.has_ram_image() + img2 = tex2.get_ram_image() + assert img2.get_ref_count() == 2 From 0911040999d32cd76012ada940d9ad92bd694e2e Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 19 Dec 2022 16:38:19 +0100 Subject: [PATCH 09/16] express: Fix compile error with GCC --- panda/src/express/pointerToArray_ext.I | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/panda/src/express/pointerToArray_ext.I b/panda/src/express/pointerToArray_ext.I index a56368f36b..7ebb8afd4d 100644 --- a/panda/src/express/pointerToArray_ext.I +++ b/panda/src/express/pointerToArray_ext.I @@ -518,8 +518,8 @@ template INLINE PointerToArray Extension >:: __deepcopy__(PyObject *memo) const { PointerToArray copy; - if (!_this->is_null()) { - copy.v() = _this->v(); + if (!this->_this->is_null()) { + copy.v() = this->_this->v(); } return copy; } @@ -725,8 +725,8 @@ template INLINE ConstPointerToArray Extension >:: __deepcopy__(PyObject *memo) const { PointerToArray copy; - if (!_this->is_null()) { - copy.v() = _this->v(); + if (!this->_this->is_null()) { + copy.v() = this->_this->v(); } return copy; } From 3247903d7b25c1ae62d654e1c4b9fb75e82d0074 Mon Sep 17 00:00:00 2001 From: Cary Sandvig Date: Wed, 21 Dec 2022 16:01:04 -0500 Subject: [PATCH 10/16] add more HAVE_PYTHON Also add some more #endif comments. --- dtool/src/dtoolbase/typeHandle.h | 6 +++++- dtool/src/dtoolutil/filename.h | 10 +++++---- dtool/src/dtoolutil/textEncoder.h | 20 ++++++++--------- dtool/src/prc/configVariable.h | 4 +++- dtool/src/prc/streamReader.h | 5 +++-- dtool/src/prc/streamWriter.h | 4 +++- panda/src/display/frameBufferProperties.h | 4 +++- panda/src/display/graphicsPipeSelection.h | 4 +++- panda/src/display/graphicsStateGuardian.h | 14 ++++++------ panda/src/display/graphicsWindow.h | 6 ++++-- panda/src/display/windowProperties.h | 26 +++++++++++++---------- panda/src/event/asyncFuture.h | 11 ++++++++-- panda/src/express/datagram.h | 8 ++++--- panda/src/express/memoryUsagePointers.h | 6 +++--- panda/src/express/multifile.h | 10 +++++---- panda/src/express/pointerToArray.h | 22 ++++++++++++------- panda/src/express/pointerToArray_ext.h | 6 +++--- panda/src/express/ramfile.h | 7 ++++-- panda/src/express/stringStream.h | 10 ++++++--- panda/src/express/virtualFile.h | 6 +++++- panda/src/express/virtualFileSystem.h | 6 +++++- panda/src/gobj/geomVertexArrayData.h | 6 ++++-- panda/src/gobj/texture.h | 8 +++---- panda/src/gobj/texturePool.h | 4 +++- panda/src/linmath/lmatrix3_src.h | 2 ++ panda/src/linmath/lmatrix4_src.h | 2 ++ panda/src/linmath/lpoint2_src.h | 4 +++- panda/src/linmath/lpoint3_src.h | 4 +++- panda/src/linmath/lpoint4_src.h | 4 +++- panda/src/linmath/lvecBase2_src.h | 16 +++++++++----- panda/src/linmath/lvecBase3_src.h | 18 ++++++++++------ panda/src/linmath/lvecBase4_src.h | 16 +++++++++----- panda/src/linmath/lvector2_src.h | 4 +++- panda/src/linmath/lvector3_src.h | 4 +++- panda/src/linmath/lvector4_src.h | 4 +++- panda/src/pgraph/loaderFileTypeRegistry.h | 6 +++++- panda/src/pgraph/nodePath.h | 10 ++++++++- panda/src/pgraph/nodePathCollection.h | 6 ++++-- panda/src/pgraph/pandaNode.h | 8 +++++-- panda/src/pgraph/renderState.h | 6 +++++- panda/src/pgraph/shaderAttrib.h | 6 ++++-- panda/src/pgraph/shaderInput.h | 4 +++- panda/src/pgraph/transformState.h | 6 +++++- panda/src/pipeline/pmutex.h | 7 +++--- panda/src/pipeline/reMutex.h | 6 ++++-- panda/src/putil/bamReader.h | 8 ++++++- panda/src/putil/bitArray.h | 6 +++++- panda/src/putil/bitMask.h | 6 ++++-- panda/src/putil/callbackObject.h | 4 +++- panda/src/putil/doubleBitMask.h | 6 +++++- panda/src/putil/sparseArray.h | 4 +++- panda/src/putil/typedWritable.h | 4 +++- 52 files changed, 271 insertions(+), 123 deletions(-) diff --git a/dtool/src/dtoolbase/typeHandle.h b/dtool/src/dtoolbase/typeHandle.h index 6d26a1f793..10997ef67d 100644 --- a/dtool/src/dtoolbase/typeHandle.h +++ b/dtool/src/dtoolbase/typeHandle.h @@ -97,7 +97,9 @@ PUBLISHED: // its value, it might happen after the value had already been set // previously by another static initializer! +#ifdef HAVE_PYTHON EXTENSION(static TypeHandle make(PyTypeObject *classobj)); +#endif INLINE bool operator == (const TypeHandle &other) const; INLINE bool operator != (const TypeHandle &other) const; @@ -137,13 +139,15 @@ PUBLISHED: MAKE_SEQ_PROPERTY(parent_classes, get_num_parent_classes, get_parent_class); MAKE_SEQ_PROPERTY(child_classes, get_num_child_classes, get_child_class); +#ifdef HAVE_PYTHON EXTENSION(PyObject *__reduce__() const); EXTENSION(void __setstate__(PyObject *)); +#endif // HAVE_PYTHON public: #ifdef HAVE_PYTHON PyObject *get_python_type() const; -#endif +#endif // HAVE_PYTHON void *allocate_array(size_t size) RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT); void *reallocate_array(void *ptr, size_t size) RETURNS_ALIGNED(MEMORY_HOOK_ALIGNMENT); diff --git a/dtool/src/dtoolutil/filename.h b/dtool/src/dtoolutil/filename.h index 3e3812f711..29ea7fc262 100644 --- a/dtool/src/dtoolutil/filename.h +++ b/dtool/src/dtoolutil/filename.h @@ -70,7 +70,7 @@ PUBLISHED: EXTENSION(Filename(PyObject *path)); EXTENSION(PyObject *__reduce__(PyObject *self) const); -#endif +#endif // HAVE_PYTHON // Static constructors to explicitly create a filename that refers to a text // or binary file. This is in lieu of calling set_text() or set_binary() or @@ -114,8 +114,10 @@ PUBLISHED: INLINE size_t length() const; INLINE char operator [] (size_t n) const; +#ifdef HAVE_PYTHON EXTENSION(PyObject *__repr__() const); EXTENSION(PyObject *__fspath__() const); +#endif // HAVE_PYTHON INLINE std::string substr(size_t begin) const; INLINE std::string substr(size_t begin, size_t end) const; @@ -202,7 +204,7 @@ PUBLISHED: bool scan_directory(vector_string &contents) const; #ifdef HAVE_PYTHON EXTENSION(PyObject *scan_directory() const); -#endif +#endif // HAVE_PYTHON bool open_read(std::ifstream &stream) const; bool open_write(std::ofstream &stream, bool truncate = true) const; @@ -274,7 +276,7 @@ protected: #ifdef ANDROID public: static std::string _internal_data_dir; -#endif +#endif // ANDROID public: static TypeHandle get_class_type() { @@ -295,4 +297,4 @@ INLINE std::ostream &operator << (std::ostream &out, const Filename &n) { #include "filename.I" -#endif +#endif // !FILENAME_H diff --git a/dtool/src/dtoolutil/textEncoder.h b/dtool/src/dtoolutil/textEncoder.h index faa50715be..918192ef65 100644 --- a/dtool/src/dtoolutil/textEncoder.h +++ b/dtool/src/dtoolutil/textEncoder.h @@ -54,28 +54,28 @@ PUBLISHED: INLINE static Encoding get_default_encoding(); MAKE_PROPERTY(default_encoding, get_default_encoding, set_default_encoding); -#ifdef CPPPARSER +#if defined(CPPPARSER) && defined(HAVE_PYTHON) EXTEND void set_text(PyObject *text); EXTEND void set_text(PyObject *text, Encoding encoding); -#else +#else // CPPPARSER && HAVE_PYTHON INLINE void set_text(const std::string &text); INLINE void set_text(const std::string &text, Encoding encoding); -#endif +#endif // CPPPARSER && HAVE_PYTHON INLINE void clear_text(); INLINE bool has_text() const; void make_upper(); void make_lower(); -#ifdef CPPPARSER +#if defined(CPPPARSER) && defined(HAVE_PYTHON) EXTEND PyObject *get_text() const; EXTEND PyObject *get_text(Encoding encoding) const; EXTEND void append_text(PyObject *text); -#else +#else // CPPPARSER && HAVE_PYTHON INLINE std::string get_text() const; INLINE std::string get_text(Encoding encoding) const; INLINE void append_text(const std::string &text); -#endif +#endif // CPPPARSER && HAVE_PYTHON INLINE void append_unicode_char(char32_t character); INLINE size_t get_num_chars() const; INLINE int get_unicode_char(size_t index) const; @@ -108,19 +108,19 @@ PUBLISHED: std::wstring get_wtext_as_ascii() const; bool is_wtext() const; -#ifdef CPPPARSER +#if defined(CPPPARSER) && defined(HAVE_PYTHON) EXTEND static PyObject *encode_wchar(char32_t ch, Encoding encoding); EXTEND INLINE PyObject *encode_wtext(const std::wstring &wtext) const; EXTEND static PyObject *encode_wtext(const std::wstring &wtext, Encoding encoding); EXTEND INLINE PyObject *decode_text(PyObject *text) const; EXTEND static PyObject *decode_text(PyObject *text, Encoding encoding); -#else +#else // CPPPARSER && HAVE_PYTHON static std::string encode_wchar(char32_t ch, Encoding encoding); INLINE std::string encode_wtext(const std::wstring &wtext) const; static std::string encode_wtext(const std::wstring &wtext, Encoding encoding); INLINE std::wstring decode_text(const std::string &text) const; static std::wstring decode_text(const std::string &text, Encoding encoding); -#endif +#endif // CPPPARSER && HAVE_PYTHON MAKE_PROPERTY(text, get_text, set_text); @@ -156,4 +156,4 @@ INLINE std::ostream & operator << (std::ostream &out, const std::wstring &str); #include "textEncoder.I" -#endif +#endif // !TEXTENCODER_H diff --git a/dtool/src/prc/configVariable.h b/dtool/src/prc/configVariable.h index 5c242907d1..e9ae49aa74 100644 --- a/dtool/src/prc/configVariable.h +++ b/dtool/src/prc/configVariable.h @@ -44,7 +44,9 @@ PUBLISHED: INLINE size_t get_num_words() const; +#ifdef HAVE_PYTHON EXTENSION(PyObject *__reduce__(PyObject *self) const); +#endif // HAVE_PYTHON protected: INLINE const ConfigDeclaration *get_default_value() const; @@ -74,4 +76,4 @@ protected: #include "configVariable.I" -#endif +#endif // !CONFIGVARIABLE_H diff --git a/dtool/src/prc/streamReader.h b/dtool/src/prc/streamReader.h index e05e8ea0e2..522fef86af 100644 --- a/dtool/src/prc/streamReader.h +++ b/dtool/src/prc/streamReader.h @@ -68,11 +68,12 @@ PUBLISHED: BLOCKING void skip_bytes(size_t size); BLOCKING size_t extract_bytes(unsigned char *into, size_t size); +#ifdef HAVE_PYTHON EXTENSION(PyObject *extract_bytes(size_t size)); EXTENSION(PyObject *readline()); EXTENSION(PyObject *readlines()); - +#endif // HAVE_PYTHON public: BLOCKING vector_uchar extract_bytes(size_t size); BLOCKING std::string readline(); @@ -84,4 +85,4 @@ private: #include "streamReader.I" -#endif +#endif // !STREAMREADER_H diff --git a/dtool/src/prc/streamWriter.h b/dtool/src/prc/streamWriter.h index 522fa45050..01c72c887e 100644 --- a/dtool/src/prc/streamWriter.h +++ b/dtool/src/prc/streamWriter.h @@ -70,7 +70,9 @@ PUBLISHED: BLOCKING INLINE void add_fixed_string(const std::string &str, size_t size); BLOCKING void pad_bytes(size_t size); +#ifdef HAVE_PYTHON EXTENSION(void append_data(PyObject *data)); +#endif // HAVE_PYTHON BLOCKING INLINE void flush(); @@ -87,4 +89,4 @@ private: #include "streamWriter.I" -#endif +#endif // !STREAMWRITER_H diff --git a/panda/src/display/frameBufferProperties.h b/panda/src/display/frameBufferProperties.h index 69993a0d0d..dc3a814b21 100644 --- a/panda/src/display/frameBufferProperties.h +++ b/panda/src/display/frameBufferProperties.h @@ -144,8 +144,10 @@ PUBLISHED: MAKE_PROPERTY(float_color, get_float_color, set_float_color); MAKE_PROPERTY(float_depth, get_float_depth, set_float_depth); +#ifdef HAVE_PYTHON EXTENSION(PyObject *__getstate__() const); EXTENSION(void __setstate__(PyObject *self, PyObject *state)); +#endif // HAVE_PYTHON // Other. @@ -181,4 +183,4 @@ INLINE std::ostream &operator << (std::ostream &out, const FrameBufferProperties #include "frameBufferProperties.I" -#endif +#endif // !FRAMEBUFFERPROPERTIES_H diff --git a/panda/src/display/graphicsPipeSelection.h b/panda/src/display/graphicsPipeSelection.h index 74877a9c94..dfbc528373 100644 --- a/panda/src/display/graphicsPipeSelection.h +++ b/panda/src/display/graphicsPipeSelection.h @@ -52,7 +52,9 @@ PUBLISHED: INLINE static GraphicsPipeSelection *get_global_ptr(); +#ifdef HAVE_PYTHON EXTENSION(PyObject *__reduce__() const); +#endif // HAVE_PYTHON public: typedef PT(GraphicsPipe) PipeConstructorFunc(); @@ -94,4 +96,4 @@ private: #include "graphicsPipeSelection.I" -#endif +#endif // !GRAPHICSPIPESELECTION_H diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index 6d0b26b6e1..bc6e175f8e 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -254,7 +254,9 @@ PUBLISHED: MAKE_PROPERTY(texture_quality_override, get_texture_quality_override, set_texture_quality_override); +#ifdef HAVE_PYTHON EXTENSION(PyObject *get_prepared_textures() const); +#endif // HAVE_PYTHON typedef bool TextureCallback(TextureContext *tc, void *callback_arg); void traverse_prepared_textures(TextureCallback *func, void *callback_arg); @@ -263,7 +265,7 @@ PUBLISHED: void clear_flash_texture(); Texture *get_flash_texture() const; MAKE_PROPERTY(flash_texture, get_flash_texture, set_flash_texture); -#endif +#endif // !NDEBUG || !CPPPARSER PUBLISHED: virtual bool has_extension(const std::string &extension) const; @@ -444,7 +446,7 @@ public: #ifdef DO_PSTATS static void init_frame_pstats(); PStatThread get_pstats_thread(); -#endif +#endif // DO_PSTATS protected: virtual void reissue_transforms(); @@ -601,7 +603,7 @@ protected: #ifdef DO_PSTATS int _pstats_gpu_thread; bool _timer_queries_active; -#endif +#endif // DO_PSTATS bool _copy_texture_inverted; bool _supports_multisample; @@ -650,9 +652,9 @@ protected: #ifndef NDEBUG PT(Texture) _flash_texture; -#else +#else // !NDEBUG PT(Texture) _flash_texture_unused; -#endif +#endif // !NDEBUG public: // Statistics @@ -763,4 +765,4 @@ EXPCL_PANDA_DISPLAY std::ostream &operator << (std::ostream &out, GraphicsStateG #include "graphicsStateGuardian.I" -#endif +#endif // !GRAPHICSSTATEGUARDIAN_H diff --git a/panda/src/display/graphicsWindow.h b/panda/src/display/graphicsWindow.h index 45b1438906..c69e5f8062 100644 --- a/panda/src/display/graphicsWindow.h +++ b/panda/src/display/graphicsWindow.h @@ -56,7 +56,9 @@ PUBLISHED: void clear_rejected_properties(); WindowProperties get_rejected_properties() const; +#ifdef HAVE_PYTHON EXTENSION(void request_properties(PyObject *args, PyObject *kwds)); +#endif // HAVE_PYTHON INLINE bool is_closed() const; virtual bool is_active() const; @@ -169,7 +171,7 @@ private: #ifdef HAVE_PYTHON typedef pset PythonWinProcClasses; PythonWinProcClasses _python_window_proc_classes; -#endif +#endif // HAVE_PYTHON public: static TypeHandle get_class_type() { @@ -194,4 +196,4 @@ private: #include "graphicsWindow.I" -#endif /* GRAPHICSWINDOW_H */ +#endif // !GRAPHICSWINDOW_H diff --git a/panda/src/display/windowProperties.h b/panda/src/display/windowProperties.h index f998b87ae6..32e6b530eb 100644 --- a/panda/src/display/windowProperties.h +++ b/panda/src/display/windowProperties.h @@ -44,7 +44,9 @@ PUBLISHED: M_confined, }; +#ifdef HAVE_PYTHON EXTENSION(WindowProperties(PyObject *self, PyObject *args, PyObject *kwds)); +#endif // HAVE_PYTHON PUBLISHED: void operator = (const WindowProperties ©); @@ -70,9 +72,9 @@ PUBLISHED: INLINE void set_origin(int x_origin, int y_origin); #ifdef CPPPARSER INLINE LPoint2i get_origin() const; -#else +#else // CPPPARSER INLINE const LPoint2i &get_origin() const; -#endif +#endif // CPPPARSER INLINE int get_x_origin() const; INLINE int get_y_origin() const; INLINE bool has_origin() const; @@ -83,9 +85,9 @@ PUBLISHED: INLINE void set_size(int x_size, int y_size); #ifdef CPPPARSER INLINE LVector2i get_size() const; -#else +#else // CPPPARSER INLINE const LVector2i &get_size() const; -#endif +#endif // CPPPARSER INLINE int get_x_size() const; INLINE int get_y_size() const; INLINE bool has_size() const; @@ -102,9 +104,9 @@ PUBLISHED: INLINE void set_title(const std::string &title); #ifdef CPPPARSER INLINE std::string get_title() const; -#else +#else // CPPPARSER INLINE const std::string &get_title() const; -#endif +#endif // CPPPARSER INLINE bool has_title() const; INLINE void clear_title(); MAKE_PROPERTY2(title, has_title, get_title, set_title, clear_title); @@ -172,9 +174,9 @@ PUBLISHED: INLINE void set_icon_filename(const Filename &icon_filename); #ifdef CPPPARSER INLINE Filename get_icon_filename() const; -#else +#else // CPPPARSER INLINE const Filename &get_icon_filename() const; -#endif +#endif // CPPPARSER INLINE bool has_icon_filename() const; INLINE void clear_icon_filename(); MAKE_PROPERTY2(icon_filename, has_icon_filename, get_icon_filename, @@ -183,9 +185,9 @@ PUBLISHED: INLINE void set_cursor_filename(const Filename &cursor_filename); #ifdef CPPPARSER INLINE Filename get_cursor_filename() const; -#else +#else // CPPPARSER INLINE const Filename &get_cursor_filename() const; -#endif +#endif // CPPPARSER INLINE bool has_cursor_filename() const; INLINE void clear_cursor_filename(); MAKE_PROPERTY2(cursor_filename, has_cursor_filename, get_cursor_filename, @@ -205,8 +207,10 @@ PUBLISHED: MAKE_PROPERTY2(parent_window, has_parent_window, get_parent_window, set_parent_window, clear_parent_window); +#ifdef HAVE_PYTHON EXTENSION(PyObject *__getstate__(PyObject *self) const); EXTENSION(void __setstate__(PyObject *self, PyObject *state)); +#endif // HAVE_PYTHON void add_properties(const WindowProperties &other); @@ -278,4 +282,4 @@ INLINE std::ostream &operator << (std::ostream &out, const WindowProperties &pro #include "windowProperties.I" -#endif +#endif // !WINDOWPROPERTIES_H diff --git a/panda/src/event/asyncFuture.h b/panda/src/event/asyncFuture.h index 20c42ce4d4..d76baa1d91 100644 --- a/panda/src/event/asyncFuture.h +++ b/panda/src/event/asyncFuture.h @@ -62,12 +62,16 @@ PUBLISHED: INLINE AsyncFuture(); virtual ~AsyncFuture(); +#ifdef HAVE_PYTHON EXTENSION(static PyObject *__await__(PyObject *self)); EXTENSION(static PyObject *__iter__(PyObject *self)); +#endif // HAVE_PYTHON INLINE bool done() const; INLINE bool cancelled() const; +#ifdef HAVE_PYTHON EXTENSION(PyObject *result(PyObject *self, PyObject *timeout = Py_None) const); +#endif // HAVE_PYTHON virtual bool cancel(); @@ -75,9 +79,11 @@ PUBLISHED: INLINE const std::string &get_done_event() const; MAKE_PROPERTY(done_event, get_done_event, set_done_event); +#ifdef HAVE_PYTHON EXTENSION(PyObject *add_done_callback(PyObject *self, PyObject *fn)); EXTENSION(static PyObject *gather(PyObject *args)); +#endif // HAVE_PYTHON INLINE static PT(AsyncFuture) shield(PT(AsyncFuture) future); virtual void output(std::ostream &out) const; @@ -85,8 +91,9 @@ PUBLISHED: BLOCKING void wait(); BLOCKING void wait(double timeout); +#ifdef HAVE_PYTHON EXTENSION(void set_result(PyObject *)); - +#endif // HAVE_PYTHON public: INLINE void set_result(std::nullptr_t); INLINE void set_result(TypedReferenceCount *result); @@ -201,4 +208,4 @@ private: #include "asyncFuture.I" -#endif +#endif // !ASYNCFUTURE_H diff --git a/panda/src/express/datagram.h b/panda/src/express/datagram.h index 20821fc37e..a23efb51a8 100644 --- a/panda/src/express/datagram.h +++ b/panda/src/express/datagram.h @@ -95,9 +95,11 @@ public: INLINE const void *get_data() const; PUBLISHED: +#ifdef HAVE_PYTHON EXTENSION(INLINE PyObject *get_message() const); EXTENSION(INLINE PyObject *__bytes__() const); EXTENSION(PyObject *__reduce__() const); +#endif // HAVE_PYTHON INLINE size_t get_length() const; @@ -121,9 +123,9 @@ private: #ifdef STDFLOAT_DOUBLE bool _stdfloat_double = true; -#else +#else // STDFLOAT_DOUBLE bool _stdfloat_double = false; -#endif +#endif // STDFLOAT_DOUBLE public: @@ -168,4 +170,4 @@ generic_write_datagram(Datagram &dest, const vector_uchar &value); #include "datagram.I" -#endif +#endif // !DATAGRAM_H diff --git a/panda/src/express/memoryUsagePointers.h b/panda/src/express/memoryUsagePointers.h index 30063cf925..c7c8ecfa07 100644 --- a/panda/src/express/memoryUsagePointers.h +++ b/panda/src/express/memoryUsagePointers.h @@ -50,9 +50,9 @@ PUBLISHED: std::string get_type_name(size_t n) const; double get_age(size_t n) const; -#ifdef DO_MEMORY_USAGE +#if defined(DO_MEMORY_USAGE) && defined(HAVE_PYTHON) EXTENSION(PyObject *get_python_pointer(size_t n) const); -#endif +#endif // DO_MEMORY_USAGE && HAVE_PYTHON void clear(); @@ -93,4 +93,4 @@ INLINE std::ostream &operator << (std::ostream &out, const MemoryUsagePointers & #include "memoryUsagePointers.I" -#endif +#endif // !MEMORYUSAGEPOINTERS_H diff --git a/panda/src/express/multifile.h b/panda/src/express/multifile.h index b344eed9fd..e7e04f209c 100644 --- a/panda/src/express/multifile.h +++ b/panda/src/express/multifile.h @@ -29,7 +29,7 @@ #ifdef HAVE_OPENSSL typedef struct x509_st X509; typedef struct evp_pkey_st EVP_PKEY; -#endif +#endif // HAVE_OPENSSL /** * A file that contains a set of files. @@ -84,8 +84,10 @@ PUBLISHED: std::string update_subfile(const std::string &subfile_name, const Filename &filename, int compression_level); +#ifdef HAVE_PYTHON EXTENSION(INLINE PyObject *set_encryption_password(PyObject *encryption_password) const); EXTENSION(INLINE PyObject *get_encryption_password() const); +#endif // HAVE_PYTHON #ifdef HAVE_OPENSSL bool add_signature(const Filename &certificate, @@ -208,7 +210,7 @@ private: int _compression_level; // Not preserved on disk. #ifdef HAVE_OPENSSL EVP_PKEY *_pkey; // Not preserved on disk. -#endif +#endif // HAVE_OPENSSL }; INLINE std::streampos word_to_streampos(size_t word) const; @@ -238,7 +240,7 @@ private: #ifdef HAVE_OPENSSL typedef pvector Certificates; Certificates _signatures; -#endif +#endif // HAVE_OPENSSL std::streampos _offset; IStreamWrapper *_read; @@ -285,4 +287,4 @@ private: #include "multifile.I" -#endif +#endif // !MULTIFILE_H diff --git a/panda/src/express/pointerToArray.h b/panda/src/express/pointerToArray.h index 6e372be4f9..3678338985 100644 --- a/panda/src/express/pointerToArray.h +++ b/panda/src/express/pointerToArray.h @@ -65,7 +65,7 @@ // disable mysterious MSVC warning for static inline PTA::empty_array method // need to chk if vc 7.0 still has this problem, would like to keep it enabled #pragma warning (disable : 4506) -#endif +#endif // _MSC_VER && !__INTEL_COMPILER template class ConstPointerToArray; @@ -96,7 +96,9 @@ PUBLISHED: INLINE static PointerToArray empty_array(size_type n, TypeHandle type_handle = get_type_handle(Element)); INLINE PointerToArray(const PointerToArray ©); +#ifdef HAVE_PYTHON EXTENSION(PointerToArray(PyObject *self, PyObject *source)); +#endif // HAVE_PYTHON */ INLINE void clear(); @@ -107,9 +109,11 @@ PUBLISHED: INLINE void set_element(size_type n, const Element &value); EXTENSION(const Element &__getitem__(size_type n) const); EXTENSION(void __setitem__(size_type n, const Element &value)); +#ifdef HAVE_PYTHON EXTENSION(PyObject *get_data() const); EXTENSION(void set_data(PyObject *data)); EXTENSION(PyObject *get_subdata(size_type n, size_type count) const); +#endif // HAVE_PYTHON INLINE void set_subdata(size_type n, size_type count, const std::string &data); INLINE int get_ref_count() const; INLINE int get_node_ref_count() const; @@ -123,7 +127,7 @@ PUBLISHED: EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const); EXTENSION(PointerToArray __deepcopy__(PyObject *memo) const); -#endif +#endif // HAVE_PYTHON #else // CPPPARSER // This is the actual, complete interface. @@ -186,7 +190,7 @@ public: #ifndef _WIN32 INLINE reference operator [](size_type n) const; INLINE reference operator [](int n) const; -#endif +#endif // !_WIN32 INLINE void push_back(const Element &x); INLINE void pop_back(); @@ -269,8 +273,10 @@ PUBLISHED: INLINE size_type size() const; INLINE const Element &get_element(size_type n) const; EXTENSION(const Element &__getitem__(size_type n) const); +#ifdef HAVE_PYTHON EXTENSION(PyObject *get_data() const); EXTENSION(PyObject *get_subdata(size_type n, size_type count) const); +#endif // HAVE_PYTHON INLINE int get_ref_count() const; INLINE int get_node_ref_count() const; @@ -283,7 +289,7 @@ PUBLISHED: EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const); EXTENSION(ConstPointerToArray __deepcopy__(PyObject *memo) const); -#endif +#endif // HAVE_PYTHON #else // CPPPARSER // This is the actual, complete interface. @@ -296,9 +302,9 @@ PUBLISHED: #ifdef _MSC_VER // VC++ seems to break the const_reverse_iterator definition somehow. typedef typename pvector::reverse_iterator reverse_iterator; -#else +#else // _MSC_VER typedef typename pvector::const_reverse_iterator reverse_iterator; -#endif +#endif // _MSC_VER typedef typename pvector::const_reverse_iterator const_reverse_iterator; typedef typename pvector::difference_type difference_type; typedef typename pvector::size_type size_type; @@ -334,7 +340,7 @@ PUBLISHED: #ifndef _WIN32 INLINE reference operator [](size_type n) const; INLINE reference operator [](int n) const; -#endif +#endif // !_WIN32 INLINE operator const Element *() const; INLINE const Element *p() const; @@ -390,4 +396,4 @@ private: #include "pointerToArray.I" -#endif // HAVE_POINTERTOARRAY_H +#endif // !POINTERTOARRAY_H diff --git a/panda/src/express/pointerToArray_ext.h b/panda/src/express/pointerToArray_ext.h index 6059aec062..3446e1210d 100644 --- a/panda/src/express/pointerToArray_ext.h +++ b/panda/src/express/pointerToArray_ext.h @@ -14,7 +14,7 @@ #ifndef POINTERTOARRAY_EXT_H #define POINTERTOARRAY_EXT_H -#ifndef CPPPARSER +#if !defined(CPPPARSER) && defined(HAVE_PYTHON) #include "extension.h" #include "py_panda.h" @@ -166,6 +166,6 @@ define_format_code("4i", UnalignedLVecBase4i); #include "pointerToArray_ext.I" -#endif // CPPPARSER +#endif // !CPPPARSER && HAVE_PYTHON -#endif // HAVE_POINTERTOARRAY_EXT_H +#endif // !HAVE_POINTERTOARRAY_EXT_H diff --git a/panda/src/express/ramfile.h b/panda/src/express/ramfile.h index 61fe382a6b..e5385fd0a2 100644 --- a/panda/src/express/ramfile.h +++ b/panda/src/express/ramfile.h @@ -27,17 +27,20 @@ PUBLISHED: INLINE void seek(size_t pos); INLINE size_t tell() const; +#ifdef HAVE_PYTHON EXTENSION(PyObject *read(size_t length)); EXTENSION(PyObject *readline()); EXTENSION(PyObject *readlines()); EXTENSION(PyObject *get_data() const); +#endif // HAVE_PYTHON INLINE size_t get_data_size() const; INLINE void clear(); +#ifdef HAVE_PYTHON EXTENSION(PyObject *__getstate__() const); EXTENSION(void __setstate__(PyObject *state)); - +#endif // HAVE_PYTHON public: std::string read(size_t length); std::string readline(); @@ -51,4 +54,4 @@ public: #include "ramfile.I" -#endif +#endif // !RAMFILE_H diff --git a/panda/src/express/stringStream.h b/panda/src/express/stringStream.h index 79c10f38ed..de1dc7d0f1 100644 --- a/panda/src/express/stringStream.h +++ b/panda/src/express/stringStream.h @@ -30,27 +30,31 @@ public: INLINE StringStream(vector_uchar source); PUBLISHED: +#ifdef HAVE_PYTHON EXTENSION(StringStream(PyObject *source)); +#endif // HAVE_PYTHON INLINE StringStream(); #if _MSC_VER >= 1800 INLINE StringStream(const StringStream ©) = delete; -#endif +#endif // _MSC_VER >= 1800 INLINE void clear_data(); INLINE size_t get_data_size(); +#ifdef HAVE_PYTHON EXTENSION(PyObject *get_data()); EXTENSION(void set_data(PyObject *data)); MAKE_PROPERTY(data, get_data, set_data); +#endif // HAVE_PYTHON public: #ifndef CPPPARSER INLINE std::string get_data(); INLINE void set_data(const std::string &data); void set_data(const unsigned char *data, size_t size); -#endif +#endif // !CPPPARSER INLINE void swap_data(vector_uchar &data); @@ -62,4 +66,4 @@ private: #include "stringStream.I" -#endif +#endif // !STRINGSTREAM_H diff --git a/panda/src/express/virtualFile.h b/panda/src/express/virtualFile.h index 2198e2dde3..973a28253c 100644 --- a/panda/src/express/virtualFile.h +++ b/panda/src/express/virtualFile.h @@ -56,12 +56,16 @@ PUBLISHED: BLOCKING void ls(std::ostream &out = std::cout) const; BLOCKING void ls_all(std::ostream &out = std::cout) const; +#ifdef HAVE_PYTHON EXTENSION(PyObject *read_file(bool auto_unwrap) const); +#endif // HAVE_PYTHON BLOCKING virtual std::istream *open_read_file(bool auto_unwrap) const; BLOCKING virtual void close_read_file(std::istream *stream) const; virtual bool was_read_successful() const; +#ifdef HAVE_PYTHON EXTENSION(PyObject *write_file(PyObject *data, bool auto_wrap)); +#endif // HAVE_PYTHON BLOCKING virtual std::ostream *open_write_file(bool auto_wrap, bool truncate); BLOCKING virtual std::ostream *open_append_file(); BLOCKING virtual void close_write_file(std::ostream *stream); @@ -126,4 +130,4 @@ INLINE std::ostream &operator << (std::ostream &out, const VirtualFile &file); #include "virtualFile.I" -#endif +#endif // !VIRTUALFILE_H diff --git a/panda/src/express/virtualFileSystem.h b/panda/src/express/virtualFileSystem.h index 775f2ddd44..bca2d63055 100644 --- a/panda/src/express/virtualFileSystem.h +++ b/panda/src/express/virtualFileSystem.h @@ -98,11 +98,15 @@ PUBLISHED: static VirtualFileSystem *get_global_ptr(); +#ifdef HAVE_PYTHON EXTENSION(PyObject *read_file(const Filename &filename, bool auto_unwrap) const); +#endif // HAVE_PYTHON BLOCKING std::istream *open_read_file(const Filename &filename, bool auto_unwrap) const; BLOCKING static void close_read_file(std::istream *stream); +#ifdef HAVE_PYTHON EXTENSION(PyObject *write_file(const Filename &filename, PyObject *data, bool auto_wrap)); +#endif // HAVE_PYTHON BLOCKING std::ostream *open_write_file(const Filename &filename, bool auto_wrap, bool truncate); BLOCKING std::ostream *open_append_file(const Filename &filename); BLOCKING static void close_write_file(std::ostream *stream); @@ -172,4 +176,4 @@ private: #include "virtualFileSystem.I" -#endif +#endif // !VIRTUALFILESYSTEM_H diff --git a/panda/src/gobj/geomVertexArrayData.h b/panda/src/gobj/geomVertexArrayData.h index e7f1fa750d..7b407fddee 100644 --- a/panda/src/gobj/geomVertexArrayData.h +++ b/panda/src/gobj/geomVertexArrayData.h @@ -118,7 +118,7 @@ PUBLISHED: EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags)); EXTENSION(int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const); EXTENSION(void __releasebuffer__(PyObject *self, Py_buffer *view) const); -#endif +#endif // HAVE_PYTHON public: virtual void evict_lru(); @@ -310,12 +310,14 @@ PUBLISHED: const unsigned char *source, size_t from_start, size_t from_size); +#ifdef HAVE_PYTHON EXTENSION(void copy_data_from(PyObject *buffer)); EXTENSION(void copy_subdata_from(size_t to_start, size_t to_size, PyObject *buffer)); EXTENSION(void copy_subdata_from(size_t to_start, size_t to_size, PyObject *buffer, size_t from_start, size_t from_size)); +#endif // HAVE_PYTHON INLINE vector_uchar get_data() const; void set_data(const vector_uchar &data); @@ -355,4 +357,4 @@ INLINE std::ostream &operator << (std::ostream &out, const GeomVertexArrayData & #include "geomVertexArrayData.I" -#endif +#endif // !GEOMVERTEXARRAYDATA_H diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index a735c2a68a..6b0ef6211f 100644 --- a/panda/src/gobj/texture.h +++ b/panda/src/gobj/texture.h @@ -456,15 +456,15 @@ PUBLISHED: CPTA_uchar get_ram_image_as(const std::string &requested_format); INLINE PTA_uchar modify_ram_image(); INLINE PTA_uchar make_ram_image(); -#ifndef CPPPARSER +#if !defined(CPPPARSER) || !defined(HAVE_PYTHON) INLINE void set_ram_image(CPTA_uchar image, CompressionMode compression = CM_off, size_t page_size = 0); void set_ram_image_as(CPTA_uchar image, const std::string &provided_format); -#else +#else // !CPPPARSER || !HAVE_PYTHON EXTEND void set_ram_image(PyObject *image, CompressionMode compression = CM_off, size_t page_size = 0); EXTEND void set_ram_image_as(PyObject *image, const std::string &provided_format); -#endif +#endif // !CPPPARSER || !HAVE_PYTHON INLINE void clear_ram_image(); INLINE void set_keep_ram_image(bool keep_ram_image); virtual bool get_keep_ram_image() const; @@ -1144,4 +1144,4 @@ EXPCL_PANDA_GOBJ std::istream &operator >> (std::istream &in, Texture::QualityLe #include "texture.I" -#endif +#endif // !TEXTURE_H diff --git a/panda/src/gobj/texturePool.h b/panda/src/gobj/texturePool.h index 997017ee2e..3686ec52e9 100644 --- a/panda/src/gobj/texturePool.h +++ b/panda/src/gobj/texturePool.h @@ -98,9 +98,11 @@ PUBLISHED: TexturePoolFilter *get_filter(size_t i) const; MAKE_SEQ_PROPERTY(filters, get_num_filters, get_filter); +#ifdef HAVE_PYTHON EXTENSION(bool register_filter(PyObject *tex_filter)); EXTENSION(bool unregister_filter(PyObject *tex_filter)); EXTENSION(bool is_filter_registered(PyObject *tex_filter)); +#endif // HAVE_PYTHON static TexturePool *get_global_ptr(); @@ -216,4 +218,4 @@ private: #include "texturePool.I" -#endif +#endif // !TEXTUREPOOL_H diff --git a/panda/src/linmath/lmatrix3_src.h b/panda/src/linmath/lmatrix3_src.h index f46432b1d2..67c450db3a 100644 --- a/panda/src/linmath/lmatrix3_src.h +++ b/panda/src/linmath/lmatrix3_src.h @@ -68,7 +68,9 @@ PUBLISHED: const FLOATNAME(LVecBase3) &); ALLOC_DELETED_CHAIN(FLOATNAME(LMatrix3)); +#ifdef HAVE_PYTHON EXTENSION(INLINE_LINMATH PyObject *__reduce__(PyObject *self) const); +#endif // HAVE_PYTHON void fill(FLOATTYPE fill_value); INLINE_LINMATH void set( diff --git a/panda/src/linmath/lmatrix4_src.h b/panda/src/linmath/lmatrix4_src.h index 3609f54d8d..38e68355bf 100644 --- a/panda/src/linmath/lmatrix4_src.h +++ b/panda/src/linmath/lmatrix4_src.h @@ -72,7 +72,9 @@ PUBLISHED: const FLOATNAME(LVecBase4) &); ALLOC_DELETED_CHAIN(FLOATNAME(LMatrix4)); +#ifdef HAVE_PYTHON EXTENSION(INLINE_LINMATH PyObject *__reduce__(PyObject *self) const); +#endif // HAVE_PYTHON // Construct a 4x4 matrix given a 3x3 rotation matrix and an optional // translation component. diff --git a/panda/src/linmath/lpoint2_src.h b/panda/src/linmath/lpoint2_src.h index 8d8a97948c..390fbb89bc 100644 --- a/panda/src/linmath/lpoint2_src.h +++ b/panda/src/linmath/lpoint2_src.h @@ -22,8 +22,10 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LPoint2)(FLOATTYPE fill_value); INLINE_LINMATH FLOATNAME(LPoint2)(FLOATTYPE x, FLOATTYPE y); +#ifdef HAVE_PYTHON EXTENSION(INLINE_LINMATH PyObject *__getattr__(PyObject *self, const std::string &attr_name) const); EXTENSION(INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign)); +#endif // HAVE_PYTHON INLINE_LINMATH static const FLOATNAME(LPoint2) &zero(); INLINE_LINMATH static const FLOATNAME(LPoint2) &unit_x(); @@ -51,7 +53,7 @@ PUBLISHED: #ifndef FLOATTYPE_IS_INT INLINE_LINMATH FLOATNAME(LPoint2) normalized() const; INLINE_LINMATH FLOATNAME(LPoint2) project(const FLOATNAME(LVecBase2) &onto) const; -#endif +#endif // !FLOATTYPE_IS_INT EXTENSION(INLINE_LINMATH std::string __repr__() const); diff --git a/panda/src/linmath/lpoint3_src.h b/panda/src/linmath/lpoint3_src.h index 512bc5ef88..563ab419c8 100644 --- a/panda/src/linmath/lpoint3_src.h +++ b/panda/src/linmath/lpoint3_src.h @@ -26,8 +26,10 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LPoint3)(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z); INLINE_LINMATH FLOATNAME(LPoint3)(const FLOATNAME(LVecBase2) ©, FLOATTYPE z); +#ifdef HAVE_PYTHON EXTENSION(INLINE_LINMATH PyObject *__getattr__(PyObject *self, const std::string &attr_name) const); EXTENSION(INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign)); +#endif // HAVE_PYTHON INLINE_LINMATH static const FLOATNAME(LPoint3) &zero(); INLINE_LINMATH static const FLOATNAME(LPoint3) &unit_x(); @@ -61,7 +63,7 @@ PUBLISHED: #ifndef FLOATTYPE_IS_INT INLINE_LINMATH FLOATNAME(LPoint3) normalized() const; INLINE_LINMATH FLOATNAME(LPoint3) project(const FLOATNAME(LVecBase3) &onto) const; -#endif +#endif // !FLOATTYPE_IS_INT INLINE_LINMATH FLOATNAME(LPoint3) operator * (FLOATTYPE scalar) const; INLINE_LINMATH FLOATNAME(LPoint3) operator / (FLOATTYPE scalar) const; diff --git a/panda/src/linmath/lpoint4_src.h b/panda/src/linmath/lpoint4_src.h index b0d8eb8036..d58eea5642 100644 --- a/panda/src/linmath/lpoint4_src.h +++ b/panda/src/linmath/lpoint4_src.h @@ -22,8 +22,10 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LPoint4)(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z, FLOATTYPE w); INLINE_LINMATH FLOATNAME(LPoint4)(const FLOATNAME(LVecBase3) ©, FLOATTYPE w); +#ifdef HAVE_PYTHON EXTENSION(INLINE_LINMATH PyObject *__getattr__(PyObject *self, const std::string &attr_name) const); EXTENSION(INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign)); +#endif // HAVE_PYTHON INLINE_LINMATH static const FLOATNAME(LPoint4) &zero(); INLINE_LINMATH static const FLOATNAME(LPoint4) &unit_x(); @@ -59,7 +61,7 @@ PUBLISHED: #ifndef FLOATTYPE_IS_INT INLINE_LINMATH FLOATNAME(LPoint4) normalized() const; INLINE_LINMATH FLOATNAME(LPoint4) project(const FLOATNAME(LVecBase4) &onto) const; -#endif +#endif // !FLOATTYPE_IS_INT EXTENSION(INLINE_LINMATH std::string __repr__() const); diff --git a/panda/src/linmath/lvecBase2_src.h b/panda/src/linmath/lvecBase2_src.h index 7f451b67b3..2820a0c65e 100644 --- a/panda/src/linmath/lvecBase2_src.h +++ b/panda/src/linmath/lvecBase2_src.h @@ -25,9 +25,9 @@ PUBLISHED: #ifdef FLOATTYPE_IS_INT is_int = 1 -#else +#else // FLOATTYPE_IS_INT is_int = 0 -#endif +#endif // FLOATTYPE_IS_INT }; INLINE_LINMATH FLOATNAME(LVecBase2)() = default; @@ -38,15 +38,17 @@ PUBLISHED: #ifdef CPPPARSER FLOATNAME(LVecBase2) &operator = (const FLOATNAME(LVecBase2) ©) = default; FLOATNAME(LVecBase2) &operator = (FLOATTYPE fill_value) = default; -#endif +#endif // CPPPARSER INLINE_LINMATH static const FLOATNAME(LVecBase2) &zero(); INLINE_LINMATH static const FLOATNAME(LVecBase2) &unit_x(); INLINE_LINMATH static const FLOATNAME(LVecBase2) &unit_y(); +#ifdef HAVE_PYTHON EXTENSION(INLINE_LINMATH PyObject *__reduce__(PyObject *self) const); EXTENSION(INLINE_LINMATH PyObject *__getattr__(PyObject *self, const std::string &attr_name) const); EXTENSION(INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign)); +#endif // HAVE_PYTHON INLINE_LINMATH FLOATTYPE operator [](int i) const; INLINE_LINMATH FLOATTYPE &operator [](int i); @@ -95,7 +97,7 @@ PUBLISHED: INLINE_LINMATH bool normalize(); INLINE_LINMATH FLOATNAME(LVecBase2) normalized() const; INLINE_LINMATH FLOATNAME(LVecBase2) project(const FLOATNAME(LVecBase2) &onto) const; -#endif +#endif // !FLOATTYPE_IS_INT INLINE_LINMATH bool operator < (const FLOATNAME(LVecBase2) &other) const; INLINE_LINMATH bool operator == (const FLOATNAME(LVecBase2) &other) const; @@ -113,7 +115,7 @@ PUBLISHED: INLINE_LINMATH size_t add_hash(size_t hash, FLOATTYPE threshold) const; INLINE_LINMATH void generate_hash(ChecksumHashGenerator &hashgen, FLOATTYPE threshold) const; -#endif +#endif // !FLOATTYPE_IS_INT INLINE_LINMATH FLOATNAME(LVecBase2) operator - () const; @@ -133,6 +135,7 @@ PUBLISHED: INLINE_LINMATH void componentwise_mult(const FLOATNAME(LVecBase2) &other); +#ifdef HAVE_PYTHON EXTENSION(INLINE_LINMATH PyObject *__rmul__(PyObject *self, FLOATTYPE scalar) const); EXTENSION(INLINE_LINMATH PyObject *__floordiv__(PyObject *self, FLOATTYPE scalar) const); @@ -144,6 +147,7 @@ PUBLISHED: EXTENSION(INLINE_LINMATH PyObject *__round__(PyObject *self)); EXTENSION(INLINE_LINMATH PyObject *__floor__(PyObject *self)); EXTENSION(INLINE_LINMATH PyObject *__ceil__(PyObject *self)); +#endif // HAVE_PYTHON INLINE_LINMATH FLOATNAME(LVecBase2) fmax(const FLOATNAME(LVecBase2) &other) const; INLINE_LINMATH FLOATNAME(LVecBase2) fmin(const FLOATNAME(LVecBase2) &other) const; @@ -160,7 +164,9 @@ PUBLISHED: INLINE_LINMATH void write_datagram(Datagram &destination) const; INLINE_LINMATH void read_datagram(DatagramIterator &source); +#ifdef HAVE_PYTHON EXTENSION(INLINE_LINMATH int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const); +#endif // HAVE_PYTHON public: // The underlying implementation is via the Eigen library, if available. diff --git a/panda/src/linmath/lvecBase3_src.h b/panda/src/linmath/lvecBase3_src.h index 567adae9c9..efa419b723 100644 --- a/panda/src/linmath/lvecBase3_src.h +++ b/panda/src/linmath/lvecBase3_src.h @@ -25,9 +25,9 @@ PUBLISHED: #ifdef FLOATTYPE_IS_INT is_int = 1 -#else +#else // FLOATTYPE_IS_INT is_int = 0 -#endif +#endif // FLOATTYPE_IS_INT }; INLINE_LINMATH FLOATNAME(LVecBase3)() = default; @@ -39,16 +39,18 @@ PUBLISHED: #ifdef CPPPARSER FLOATNAME(LVecBase3) &operator = (const FLOATNAME(LVecBase3) ©) = default; FLOATNAME(LVecBase3) &operator = (FLOATTYPE fill_value) = default; -#endif +#endif // CPPPARSER INLINE_LINMATH static const FLOATNAME(LVecBase3) &zero(); INLINE_LINMATH static const FLOATNAME(LVecBase3) &unit_x(); INLINE_LINMATH static const FLOATNAME(LVecBase3) &unit_y(); INLINE_LINMATH static const FLOATNAME(LVecBase3) &unit_z(); +#ifdef HAVE_PYTHON EXTENSION(INLINE_LINMATH PyObject *__reduce__(PyObject *self) const); EXTENSION(INLINE_LINMATH PyObject *__getattr__(PyObject *self, const std::string &attr_name) const); EXTENSION(INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign)); +#endif // HAVE_PYTHON INLINE_LINMATH FLOATTYPE operator [](int i) const; INLINE_LINMATH FLOATTYPE &operator [](int i); @@ -108,7 +110,7 @@ PUBLISHED: INLINE_LINMATH bool normalize(); INLINE_LINMATH FLOATNAME(LVecBase3) normalized() const; INLINE_LINMATH FLOATNAME(LVecBase3) project(const FLOATNAME(LVecBase3) &onto) const; -#endif +#endif // !FLOATTYPE_IS_INT INLINE_LINMATH FLOATNAME(LVecBase3) cross(const FLOATNAME(LVecBase3) &other) const; @@ -118,7 +120,7 @@ PUBLISHED: #ifndef FLOATTYPE_IS_INT INLINE_LINMATH FLOATNAME(LVecBase3) get_standardized_hpr() const; -#endif +#endif // !FLOATTYPE_IS_INT INLINE_LINMATH int compare_to(const FLOATNAME(LVecBase3) &other) const; INLINE_LINMATH size_t get_hash() const; @@ -132,7 +134,7 @@ PUBLISHED: INLINE_LINMATH size_t add_hash(size_t hash, FLOATTYPE threshold) const; INLINE_LINMATH void generate_hash(ChecksumHashGenerator &hashgen, FLOATTYPE threshold) const; -#endif +#endif // !FLOATTYPE_IS_INT INLINE_LINMATH FLOATNAME(LVecBase3) operator - () const; @@ -152,6 +154,7 @@ PUBLISHED: INLINE_LINMATH void componentwise_mult(const FLOATNAME(LVecBase3) &other); +#ifdef HAVE_PYTHON EXTENSION(INLINE_LINMATH PyObject *__rmul__(PyObject *self, FLOATTYPE scalar) const); EXTENSION(INLINE_LINMATH PyObject *__floordiv__(PyObject *self, FLOATTYPE scalar) const); @@ -163,6 +166,7 @@ PUBLISHED: EXTENSION(INLINE_LINMATH PyObject *__round__(PyObject *self)); EXTENSION(INLINE_LINMATH PyObject *__floor__(PyObject *self)); EXTENSION(INLINE_LINMATH PyObject *__ceil__(PyObject *self)); +#endif // HAVE_PYTHON INLINE_LINMATH FLOATNAME(LVecBase3) fmax(const FLOATNAME(LVecBase3) &other) const; INLINE_LINMATH FLOATNAME(LVecBase3) fmin(const FLOATNAME(LVecBase3) &other) const; @@ -181,7 +185,9 @@ PUBLISHED: INLINE_LINMATH void write_datagram(Datagram &destination) const; INLINE_LINMATH void read_datagram(DatagramIterator &source); +#ifdef HAVE_PYTHON EXTENSION(INLINE_LINMATH int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const); +#endif // HAVE_PYTHON public: // The underlying implementation is via the Eigen library, if available. diff --git a/panda/src/linmath/lvecBase4_src.h b/panda/src/linmath/lvecBase4_src.h index aed77a9470..bb53708cae 100644 --- a/panda/src/linmath/lvecBase4_src.h +++ b/panda/src/linmath/lvecBase4_src.h @@ -31,9 +31,9 @@ PUBLISHED: #ifdef FLOATTYPE_IS_INT is_int = 1 -#else +#else // FLOATTYPE_IS_INT is_int = 0 -#endif +#endif // FLOATTYPE_IS_INT }; INLINE_LINMATH FLOATNAME(LVecBase4)() = default; @@ -48,7 +48,7 @@ PUBLISHED: #ifdef CPPPARSER FLOATNAME(LVecBase4) &operator = (const FLOATNAME(LVecBase4) ©) = default; FLOATNAME(LVecBase4) &operator = (FLOATTYPE fill_value) = default; -#endif +#endif // CPPPARSER INLINE_LINMATH static const FLOATNAME(LVecBase4) &zero(); INLINE_LINMATH static const FLOATNAME(LVecBase4) &unit_x(); @@ -56,9 +56,11 @@ PUBLISHED: INLINE_LINMATH static const FLOATNAME(LVecBase4) &unit_z(); INLINE_LINMATH static const FLOATNAME(LVecBase4) &unit_w(); +#ifdef HAVE_PYTHON EXTENSION(INLINE_LINMATH PyObject *__reduce__(PyObject *self) const); EXTENSION(INLINE_LINMATH PyObject *__getattr__(PyObject *self, const std::string &attr_name) const); EXTENSION(INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign)); +#endif // HAVE_PYTHON INLINE_LINMATH FLOATTYPE operator [](int i) const; INLINE_LINMATH FLOATTYPE &operator [](int i); @@ -122,7 +124,7 @@ PUBLISHED: INLINE_LINMATH bool normalize(); INLINE_LINMATH FLOATNAME(LVecBase4) normalized() const; INLINE_LINMATH FLOATNAME(LVecBase4) project(const FLOATNAME(LVecBase4) &onto) const; -#endif +#endif // !FLOATTYPE_IS_INT INLINE_LINMATH bool operator < (const FLOATNAME(LVecBase4) &other) const; INLINE_LINMATH bool operator == (const FLOATNAME(LVecBase4) &other) const; @@ -140,7 +142,7 @@ PUBLISHED: INLINE_LINMATH size_t add_hash(size_t hash, FLOATTYPE threshold) const; INLINE_LINMATH void generate_hash(ChecksumHashGenerator &hashgen, FLOATTYPE threshold) const; -#endif +#endif // !FLOATTYPE_IS_INT INLINE_LINMATH FLOATNAME(LVecBase4) operator - () const; @@ -160,6 +162,7 @@ PUBLISHED: INLINE_LINMATH void componentwise_mult(const FLOATNAME(LVecBase4) &other); +#ifdef HAVE_PYTHON EXTENSION(INLINE_LINMATH PyObject *__rmul__(PyObject *self, FLOATTYPE scalar) const); EXTENSION(INLINE_LINMATH PyObject *__floordiv__(PyObject *self, FLOATTYPE scalar) const); @@ -171,6 +174,7 @@ PUBLISHED: EXTENSION(INLINE_LINMATH PyObject *__round__(PyObject *self)); EXTENSION(INLINE_LINMATH PyObject *__floor__(PyObject *self)); EXTENSION(INLINE_LINMATH PyObject *__ceil__(PyObject *self)); +#endif // HAVE_PYTHON INLINE_LINMATH FLOATNAME(LVecBase4) fmax(const FLOATNAME(LVecBase4) &other) const; INLINE_LINMATH FLOATNAME(LVecBase4) fmin(const FLOATNAME(LVecBase4) &other) const; @@ -187,7 +191,9 @@ PUBLISHED: INLINE_LINMATH void write_datagram(Datagram &destination) const; INLINE_LINMATH void read_datagram(DatagramIterator &source); +#ifdef HAVE_PYTHON EXTENSION(INLINE_LINMATH int __getbuffer__(PyObject *self, Py_buffer *view, int flags) const); +#endif // HAVE_PYTHON public: // The underlying implementation is via the Eigen library, if available. diff --git a/panda/src/linmath/lvector2_src.h b/panda/src/linmath/lvector2_src.h index 2e0b68f0d3..f7bf8162da 100644 --- a/panda/src/linmath/lvector2_src.h +++ b/panda/src/linmath/lvector2_src.h @@ -22,8 +22,10 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LVector2)(FLOATTYPE fill_value); INLINE_LINMATH FLOATNAME(LVector2)(FLOATTYPE x, FLOATTYPE y); +#ifdef HAVE_PYTHON EXTENSION(INLINE_LINMATH PyObject *__getattr__(PyObject *self, const std::string &attr_name) const); EXTENSION(INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign)); +#endif // HAVE_PYTHON INLINE_LINMATH static const FLOATNAME(LVector2) &zero(); INLINE_LINMATH static const FLOATNAME(LVector2) &unit_x(); @@ -47,7 +49,7 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LVector2) project(const FLOATNAME(LVecBase2) &onto) const; INLINE_LINMATH FLOATTYPE signed_angle_rad(const FLOATNAME(LVector2) &other) const; INLINE_LINMATH FLOATTYPE signed_angle_deg(const FLOATNAME(LVector2) &other) const; -#endif +#endif // !FLOATTYPE_IS_INT EXTENSION(INLINE_LINMATH std::string __repr__() const); diff --git a/panda/src/linmath/lvector3_src.h b/panda/src/linmath/lvector3_src.h index 075dbe338f..db0ba4a8b3 100644 --- a/panda/src/linmath/lvector3_src.h +++ b/panda/src/linmath/lvector3_src.h @@ -26,8 +26,10 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LVector3)(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z); INLINE_LINMATH FLOATNAME(LVector3)(const FLOATNAME(LVecBase2) ©, FLOATTYPE z); +#ifdef HAVE_PYTHON EXTENSION(INLINE_LINMATH PyObject *__getattr__(PyObject *self, const std::string &attr_name) const); EXTENSION(INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign)); +#endif // HAVE_PYTHON INLINE_LINMATH static const FLOATNAME(LVector3) &zero(); INLINE_LINMATH static const FLOATNAME(LVector3) &unit_x(); @@ -66,7 +68,7 @@ PUBLISHED: INLINE_LINMATH FLOATTYPE relative_angle_rad(const FLOATNAME(LVector3) &other) const; INLINE_LINMATH FLOATTYPE relative_angle_deg(const FLOATNAME(LVector3) &other) const; -#endif +#endif // !FLOATTYPE_IS_INT INLINE_LINMATH FLOATNAME(LVector3) operator * (FLOATTYPE scalar) const; INLINE_LINMATH FLOATNAME(LVector3) operator / (FLOATTYPE scalar) const; diff --git a/panda/src/linmath/lvector4_src.h b/panda/src/linmath/lvector4_src.h index d8a00c7e9f..0dea78c0ca 100644 --- a/panda/src/linmath/lvector4_src.h +++ b/panda/src/linmath/lvector4_src.h @@ -22,8 +22,10 @@ PUBLISHED: INLINE_LINMATH FLOATNAME(LVector4)(FLOATTYPE x, FLOATTYPE y, FLOATTYPE z, FLOATTYPE w); INLINE_LINMATH FLOATNAME(LVector4)(const FLOATNAME(LVecBase3) ©, FLOATTYPE w); +#ifdef HAVE_PYTHON EXTENSION(INLINE_LINMATH PyObject *__getattr__(PyObject *self, const std::string &attr_name) const); EXTENSION(INLINE_LINMATH int __setattr__(PyObject *self, const std::string &attr_name, PyObject *assign)); +#endif // HAVE_PYTHON INLINE_LINMATH static const FLOATNAME(LVector4) &zero(); INLINE_LINMATH static const FLOATNAME(LVector4) &unit_x(); @@ -53,7 +55,7 @@ PUBLISHED: #ifndef FLOATTYPE_IS_INT INLINE_LINMATH FLOATNAME(LVector4) normalized() const; INLINE_LINMATH FLOATNAME(LVector4) project(const FLOATNAME(LVecBase4) &onto) const; -#endif +#endif // !FLOATTYPE_IS_INT EXTENSION(INLINE_LINMATH std::string __repr__() const); diff --git a/panda/src/pgraph/loaderFileTypeRegistry.h b/panda/src/pgraph/loaderFileTypeRegistry.h index 27f29664dd..fe1766929b 100644 --- a/panda/src/pgraph/loaderFileTypeRegistry.h +++ b/panda/src/pgraph/loaderFileTypeRegistry.h @@ -38,10 +38,12 @@ public: void unregister_type(LoaderFileType *type); PUBLISHED: +#ifdef HAVE_PYTHON EXTENSION(void register_type(PyObject *type)); EXTENSION(void register_deferred_type(PyObject *entry_point)); EXTENSION(void unregister_type(PyObject *type)); +#endif // HAVE_PYTHON int get_num_types() const; LoaderFileType *get_type(int n) const; @@ -53,7 +55,9 @@ PUBLISHED: static LoaderFileTypeRegistry *get_global_ptr(); +#ifdef HAVE_PYTHON EXTENSION(PyObject *__reduce__() const); +#endif // HAVE_PYTHON private: void record_extension(const std::string &extension, LoaderFileType *type); @@ -71,4 +75,4 @@ private: static LoaderFileTypeRegistry *_global_ptr; }; -#endif +#endif // !LOADERFILETYPEREGISTRY_H diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index d27a76c7ad..a22415d8f0 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -183,9 +183,11 @@ PUBLISHED: INLINE void clear(); EXTENSION(NodePath __copy__() const); +#ifdef HAVE_PYTHON EXTENSION(PyObject *__deepcopy__(PyObject *self, PyObject *memo) const); EXTENSION(PyObject *__reduce__(PyObject *self) const); EXTENSION(PyObject *__reduce_persist__(PyObject *self, PyObject *pickler) const); +#endif // HAVE_PYTHON INLINE static NodePath not_found(); INLINE static NodePath removed(); @@ -666,8 +668,10 @@ PUBLISHED: INLINE void set_shader_input(CPT_InternalName id, PN_stdfloat n1, PN_stdfloat n2, PN_stdfloat n3=0, PN_stdfloat n4=0, int priority=0); +#ifdef HAVE_PYTHON EXTENSION(void set_shader_input(CPT_InternalName, PyObject *, int priority=0)); EXTENSION(void set_shader_inputs(PyObject *args, PyObject *kwargs)); +#endif // HAVE_PYTHON void clear_shader_input(CPT_InternalName id); void set_instance_count(int instance_count); @@ -911,7 +915,9 @@ PUBLISHED: const NodePath &other = NodePath(), Thread *current_thread = Thread::get_current_thread()) const; +#ifdef HAVE_PYTHON EXTENSION(PyObject *get_tight_bounds(const NodePath &other = NodePath()) const); +#endif // HAVE_PYTHON // void analyze() const; @@ -932,6 +938,7 @@ PUBLISHED: MAKE_MAP_PROPERTY(net_tags, has_net_tag, get_net_tag); +#ifdef HAVE_PYTHON EXTENSION(INLINE PyObject *get_tags() const); EXTENSION(INLINE PyObject *get_tag_keys() const); MAKE_PROPERTY(tags, get_tags); @@ -948,6 +955,7 @@ PUBLISHED: MAKE_PROPERTY(python_tags, get_python_tags); EXTENSION(int __traverse__(visitproc visit, void *arg)); +#endif // HAVE_PYTHON INLINE void list_tags() const; @@ -1071,4 +1079,4 @@ INLINE std::ostream &operator << (std::ostream &out, const NodePath &node_path); #include "nodePath.I" -#endif +#endif // !NODEPATH_H diff --git a/panda/src/pgraph/nodePathCollection.h b/panda/src/pgraph/nodePathCollection.h index 6dd4e76b12..3e7919e7c5 100644 --- a/panda/src/pgraph/nodePathCollection.h +++ b/panda/src/pgraph/nodePathCollection.h @@ -30,7 +30,7 @@ PUBLISHED: #ifdef HAVE_PYTHON EXTENSION(NodePathCollection(PyObject *self, PyObject *sequence)); EXTENSION(PyObject *__reduce__(PyObject *self) const); -#endif +#endif // HAVE_PYTHON void add_path(const NodePath &node_path); bool remove_path(const NodePath &node_path); @@ -74,7 +74,9 @@ PUBLISHED: bool calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point) const; +#ifdef HAVE_PYTHON EXTENSION(PyObject *get_tight_bounds() const); +#endif // HAVE_PYTHON void set_texture(Texture *tex, int priority = 0); void set_texture(TextureStage *stage, Texture *tex, int priority = 0); @@ -113,4 +115,4 @@ INLINE std::ostream &operator << (std::ostream &out, const NodePathCollection &c #include "nodePathCollection.I" -#endif +#endif // !NODEPATHCOLLECTION_H diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h index d4d36c8f69..1eee01fc93 100644 --- a/panda/src/pgraph/pandaNode.h +++ b/panda/src/pgraph/pandaNode.h @@ -106,8 +106,10 @@ PUBLISHED: virtual PandaNode *make_copy() const; PT(PandaNode) copy_subgraph(Thread *current_thread = Thread::get_current_thread()) const; +#ifdef HAVE_PYTHON EXTENSION(PT(PandaNode) __copy__() const); EXTENSION(PyObject *__deepcopy__(PyObject *self, PyObject *memo) const); +#endif // HAVE_PYTHON INLINE int get_num_parents(Thread *current_thread = Thread::get_current_thread()) const; INLINE PandaNode *get_parent(int n, Thread *current_thread = Thread::get_current_thread()) const; @@ -204,6 +206,7 @@ PUBLISHED: MAKE_MAP_PROPERTY(tags, has_tag, get_tag, set_tag, clear_tag); MAKE_MAP_KEYS_SEQ(tags, get_num_tags, get_tag_key); +#ifdef HAVE_PYTHON EXTENSION(PyObject *get_tag_keys() const); EXTENSION(PyObject *get_python_tags()); @@ -215,6 +218,7 @@ PUBLISHED: MAKE_PROPERTY(python_tags, get_python_tags); EXTENSION(int __traverse__(visitproc visit, void *arg)); +#endif // HAVE_PYTHON INLINE bool has_tags() const; void copy_tags(PandaNode *other); @@ -843,7 +847,7 @@ private: #ifndef DO_PIPELINING friend class PandaNode::Children; friend class PandaNode::Stashed; -#endif +#endif // !DO_PIPELINING friend class NodePath; friend class NodePathComponent; friend class WorkingNodePath; @@ -943,4 +947,4 @@ INLINE std::ostream &operator << (std::ostream &out, const PandaNode &node) { #include "pandaNode.I" -#endif +#endif // !PANDANODE_H diff --git a/panda/src/pgraph/renderState.h b/panda/src/pgraph/renderState.h index 76a65877fb..5c4fe8e598 100644 --- a/panda/src/pgraph/renderState.h +++ b/panda/src/pgraph/renderState.h @@ -132,8 +132,10 @@ PUBLISHED: INLINE size_t get_invert_composition_cache_size() const; INLINE const RenderState *get_invert_composition_cache_source(size_t n) const; INLINE const RenderState *get_invert_composition_cache_result(size_t n) const; +#ifdef HAVE_PYTHON EXTENSION(PyObject *get_composition_cache() const); EXTENSION(PyObject *get_invert_composition_cache() const); +#endif // HAVE_PYTHON void output(std::ostream &out) const; void write(std::ostream &out, int indent_level) const; @@ -148,8 +150,10 @@ PUBLISHED: static void list_cycles(std::ostream &out); static void list_states(std::ostream &out); static bool validate_states(); +#ifdef HAVE_PYTHON EXTENSION(static PyObject *get_states()); EXTENSION(static PyObject *get_unused_states()); +#endif // HAVE_PYTHON PUBLISHED: // These methods are intended for use by low-level code, but they're also @@ -391,4 +395,4 @@ INLINE std::ostream &operator << (std::ostream &out, const RenderState &state) { #include "renderState.I" -#endif +#endif // !RENDERSTATE_H diff --git a/panda/src/pgraph/shaderAttrib.h b/panda/src/pgraph/shaderAttrib.h index 2c7f052ace..4e42bee47d 100644 --- a/panda/src/pgraph/shaderAttrib.h +++ b/panda/src/pgraph/shaderAttrib.h @@ -92,12 +92,14 @@ public: INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, const LMatrix3 &v, int priority=0) const; INLINE CPT(RenderAttrib) set_shader_input(CPT_InternalName id, double n1=0, double n2=0, double n3=0, double n4=1, int priority=0) const; - + CPT(RenderAttrib) set_shader_inputs(const pvector &inputs) const; PUBLISHED: +#ifdef HAVE_PYTHON EXTENSION(CPT(RenderAttrib) set_shader_input(CPT_InternalName, PyObject *, int priority=0) const); EXTENSION(CPT(RenderAttrib) set_shader_inputs(PyObject *args, PyObject *kwargs) const); +#endif // HAVE_PYTHON CPT(RenderAttrib) set_instance_count(int instance_count) const; @@ -194,4 +196,4 @@ private: #include "shaderAttrib.I" -#endif // SHADERATTRIB_H +#endif // !SHADERATTRIB_H diff --git a/panda/src/pgraph/shaderInput.h b/panda/src/pgraph/shaderInput.h index 2901ccb60d..ac17770498 100644 --- a/panda/src/pgraph/shaderInput.h +++ b/panda/src/pgraph/shaderInput.h @@ -49,7 +49,9 @@ PUBLISHED: static const ShaderInput &get_blank(); INLINE explicit ShaderInput(CPT_InternalName name, int priority=0); +#ifdef HAVE_PYTHON EXTENSION(explicit ShaderInput(CPT_InternalName name, PyObject *value, int priority=0)); +#endif // HAVE_PYTHON public: INLINE ShaderInput(CPT_InternalName name, Texture *tex, int priority=0); @@ -145,4 +147,4 @@ private: #include "shaderInput.I" -#endif // SHADERINPUT_H +#endif // !SHADERINPUT_H diff --git a/panda/src/pgraph/transformState.h b/panda/src/pgraph/transformState.h index 80bdcf5adf..6d59683ce6 100644 --- a/panda/src/pgraph/transformState.h +++ b/panda/src/pgraph/transformState.h @@ -194,8 +194,10 @@ PUBLISHED: INLINE const TransformState *get_invert_composition_cache_source(size_t n) const; INLINE const TransformState *get_invert_composition_cache_result(size_t n) const; bool validate_composition_cache() const; +#ifdef HAVE_PYTHON EXTENSION(PyObject *get_composition_cache() const); EXTENSION(PyObject *get_invert_composition_cache() const); +#endif // HAVE_PYTHON void output(std::ostream &out) const; void write(std::ostream &out, int indent_level) const; @@ -208,8 +210,10 @@ PUBLISHED: static void list_cycles(std::ostream &out); static void list_states(std::ostream &out); static bool validate_states(); +#ifdef HAVE_PYTHON EXTENSION(static PyObject *get_states()); EXTENSION(static PyObject *get_unused_states()); +#endif // HAVE_PYTHON public: static void init_states(); @@ -423,4 +427,4 @@ INLINE std::ostream &operator << (std::ostream &out, const TransformState &state #include "transformState.I" -#endif +#endif // !TRANSFORMSTATE_H diff --git a/panda/src/pipeline/pmutex.h b/panda/src/pipeline/pmutex.h index 51b588a0c9..75ad13d1f3 100644 --- a/panda/src/pipeline/pmutex.h +++ b/panda/src/pipeline/pmutex.h @@ -34,7 +34,7 @@ */ #ifdef DEBUG_THREADS class EXPCL_PANDA_PIPELINE Mutex : public MutexDebug -#else +#else // DEBUG_THREADS class EXPCL_PANDA_PIPELINE Mutex : public MutexDirect #endif // DEBUG_THREADS { @@ -49,10 +49,11 @@ PUBLISHED: void operator = (const Mutex ©) = delete; +#ifdef HAVE_PYTHON EXTENSION(bool acquire(bool blocking=true) const); EXTENSION(bool __enter__()); EXTENSION(void __exit__(PyObject *, PyObject *, PyObject *)); - +#endif // HAVE_PYTHON public: // This is a global mutex set aside for the purpose of protecting Notify // messages from being interleaved between threads. @@ -61,4 +62,4 @@ public: #include "pmutex.I" -#endif +#endif // !PMUTEX_H diff --git a/panda/src/pipeline/reMutex.h b/panda/src/pipeline/reMutex.h index bb87953949..35d2e164db 100644 --- a/panda/src/pipeline/reMutex.h +++ b/panda/src/pipeline/reMutex.h @@ -28,7 +28,7 @@ */ #ifdef DEBUG_THREADS class EXPCL_PANDA_PIPELINE ReMutex : public MutexDebug -#else +#else // DEBUG_THREADS class EXPCL_PANDA_PIPELINE ReMutex : public ReMutexDirect #endif // DEBUG_THREADS { @@ -43,11 +43,13 @@ PUBLISHED: void operator = (const ReMutex ©) = delete; +#ifdef HAVE_PYTHON EXTENSION(bool acquire(bool blocking=true) const); EXTENSION(bool __enter__()); EXTENSION(void __exit__(PyObject *, PyObject *, PyObject *)); +#endif // HAVE_PYTHON }; #include "reMutex.I" -#endif +#endif // !REMUTEX_H diff --git a/panda/src/putil/bamReader.h b/panda/src/putil/bamReader.h index 69c901f0fa..2d41c2260c 100644 --- a/panda/src/putil/bamReader.h +++ b/panda/src/putil/bamReader.h @@ -148,14 +148,18 @@ PUBLISHED: INLINE int get_current_major_ver() const; INLINE int get_current_minor_ver() const; +#ifdef HAVE_PYTHON EXTENSION(PyObject *get_file_version() const); +#endif // HAVE_PYTHON PUBLISHED: MAKE_PROPERTY(source, get_source, set_source); MAKE_PROPERTY(filename, get_filename); MAKE_PROPERTY(loader_options, get_loader_options, set_loader_options); +#ifdef HAVE_PYTHON MAKE_PROPERTY(file_version, get_file_version); +#endif // HAVE_PYTHON MAKE_PROPERTY(file_endian, get_file_endian); MAKE_PROPERTY(file_stdfloat_double, get_file_stdfloat_double); @@ -201,8 +205,10 @@ public: void *user_data = nullptr); INLINE static WritableFactory *get_factory(); +#ifdef HAVE_PYTHON PUBLISHED: EXTENSION(static void register_factory(TypeHandle handle, PyObject *func)); +#endif // HAVE_PYTHON private: INLINE static void create_factory(); @@ -350,4 +356,4 @@ parse_params(const FactoryParams ¶ms, #include "bamReader.I" -#endif +#endif // !__BAM_READER_ diff --git a/panda/src/putil/bitArray.h b/panda/src/putil/bitArray.h index f705f9b5de..7ccb50a359 100644 --- a/panda/src/putil/bitArray.h +++ b/panda/src/putil/bitArray.h @@ -49,7 +49,9 @@ PUBLISHED: INLINE BitArray(); BitArray(const SparseArray &from); +#ifdef HAVE_PYTHON EXTENSION(BitArray(PyObject *init_value)); +#endif // HAVE_PYTHON INLINE static BitArray all_on(); INLINE static BitArray all_off(); @@ -129,8 +131,10 @@ PUBLISHED: void operator >>= (int shift); EXTENSION(bool __bool__() const); +#ifdef HAVE_PYTHON EXTENSION(PyObject *__getstate__() const); EXTENSION(void __setstate__(PyObject *state)); +#endif // HAVE_PYTHON public: void generate_hash(ChecksumHashGenerator &hashgen) const; @@ -171,4 +175,4 @@ operator << (std::ostream &out, const BitArray &array) { return out; } -#endif +#endif // !BITARRAY_H diff --git a/panda/src/putil/bitMask.h b/panda/src/putil/bitMask.h index 26c19b3f3c..92935661d4 100644 --- a/panda/src/putil/bitMask.h +++ b/panda/src/putil/bitMask.h @@ -126,8 +126,10 @@ PUBLISHED: INLINE int get_key() const; EXTENSION(bool __bool__() const); +#ifdef HAVE_PYTHON EXTENSION(PyObject *__int__() const); EXTENSION(PyObject *__reduce__(PyObject *self) const); +#endif // HAVE_PYTHON public: INLINE void generate_hash(ChecksumHashGenerator &hashgen) const; @@ -170,8 +172,8 @@ typedef BitMask BitMask64; typedef BitMask32 BitMaskNative; #elif NATIVE_WORDSIZE == 64 typedef BitMask64 BitMaskNative; -#else +#else // NATIVE_WORDSIZE #error No definition for NATIVE_WORDSIZE--should be defined in dtoolbase.h. #endif // NATIVE_WORDSIZE -#endif +#endif // !BITMASK_H diff --git a/panda/src/putil/callbackObject.h b/panda/src/putil/callbackObject.h index 0702c704f5..db26d51fd9 100644 --- a/panda/src/putil/callbackObject.h +++ b/panda/src/putil/callbackObject.h @@ -34,7 +34,9 @@ public: PUBLISHED: virtual void output(std::ostream &out) const; +#ifdef HAVE_PYTHON EXTENSION(static PT(CallbackObject) make(PyObject *function)); +#endif // HAVE_PYTHON public: virtual void do_callback(CallbackData *cbdata); @@ -64,4 +66,4 @@ inline std::ostream &operator << (std::ostream &out, const CallbackObject &cbo) #include "callbackObject.I" -#endif +#endif // !CALLBACKOBJECT_H diff --git a/panda/src/putil/doubleBitMask.h b/panda/src/putil/doubleBitMask.h index da24e96814..930024dbe7 100644 --- a/panda/src/putil/doubleBitMask.h +++ b/panda/src/putil/doubleBitMask.h @@ -39,7 +39,9 @@ PUBLISHED: }; constexpr DoubleBitMask() = default; +#ifdef HAVE_PYTHON EXTENSION(DoubleBitMask(PyObject *init_value)); +#endif // HAVE_PYTHON INLINE static DoubleBitMask all_on(); INLINE static DoubleBitMask all_off(); @@ -113,8 +115,10 @@ PUBLISHED: INLINE void operator >>= (int shift); EXTENSION(bool __bool__() const); +#ifdef HAVE_PYTHON EXTENSION(PyObject *__int__() const); EXTENSION(PyObject *__reduce__(PyObject *self) const); +#endif // HAVE_PYTHON public: INLINE void generate_hash(ChecksumHashGenerator &hashgen) const; @@ -148,4 +152,4 @@ typedef DoubleBitMask DoubleBitMaskNative; EXPORT_TEMPLATE_CLASS(EXPCL_PANDA_PUTIL, EXPTP_PANDA_PUTIL, DoubleBitMask); typedef DoubleBitMask QuadBitMaskNative; -#endif +#endif // !DOUBLEBITMASK_H diff --git a/panda/src/putil/sparseArray.h b/panda/src/putil/sparseArray.h index 61ffc6b92a..7db104ff8d 100644 --- a/panda/src/putil/sparseArray.h +++ b/panda/src/putil/sparseArray.h @@ -118,8 +118,10 @@ PUBLISHED: INLINE int get_subrange_end(size_t n) const; EXTENSION(bool __bool__() const); +#ifdef HAVE_PYTHON EXTENSION(PyObject *__getstate__() const); EXTENSION(void __setstate__(PyObject *state)); +#endif // HAVE_PYTHON private: void do_add_range(int begin, int end); @@ -171,4 +173,4 @@ operator << (std::ostream &out, const SparseArray &array) { return out; } -#endif +#endif // !SPARSEARRAY_H diff --git a/panda/src/putil/typedWritable.h b/panda/src/putil/typedWritable.h index 2dbdc3d967..55a60aa223 100644 --- a/panda/src/putil/typedWritable.h +++ b/panda/src/putil/typedWritable.h @@ -60,8 +60,10 @@ PUBLISHED: INLINE void mark_bam_modified(); INLINE UpdateSeq get_bam_modified() const; +#ifdef HAVE_PYTHON EXTENSION(PyObject *__reduce__(PyObject *self) const); EXTENSION(PyObject *__reduce_persist__(PyObject *self, PyObject *pickler) const); +#endif // HAVE_PYTHON INLINE vector_uchar encode_to_bam_stream() const; bool encode_to_bam_stream(vector_uchar &data, BamWriter *writer = nullptr) const; @@ -111,4 +113,4 @@ private: #include "typedWritable.I" -#endif +#endif // !TYPEDWRITABLE_H From a2890c03de158bbfc973738dd0fa6a06712b50fc Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 26 Dec 2022 11:16:53 +0100 Subject: [PATCH 11/16] collide: Allow creating dummy CollisionHandler Useful for benchmarks or unit tests where the result does not matter. --- panda/src/collide/collisionHandler.cxx | 8 -------- panda/src/collide/collisionHandler.h | 11 +++++------ 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/panda/src/collide/collisionHandler.cxx b/panda/src/collide/collisionHandler.cxx index 39d1bf4269..6ac919b9b2 100644 --- a/panda/src/collide/collisionHandler.cxx +++ b/panda/src/collide/collisionHandler.cxx @@ -15,14 +15,6 @@ TypeHandle CollisionHandler::_type_handle; -/** - * - */ -CollisionHandler:: -CollisionHandler() { - _wants_all_potential_collidees = false; -} - /** * Will be called by the CollisionTraverser before a new traversal is begun. * It instructs the handler to reset itself in preparation for a number of diff --git a/panda/src/collide/collisionHandler.h b/panda/src/collide/collisionHandler.h index 8d41fe0df7..f0e7337d5e 100644 --- a/panda/src/collide/collisionHandler.h +++ b/panda/src/collide/collisionHandler.h @@ -28,9 +28,10 @@ class CollisionEntry; * dispatch detected collisions. */ class EXPCL_PANDA_COLLIDE CollisionHandler : public TypedReferenceCount { -public: - CollisionHandler(); +PUBLISHED: + CollisionHandler() = default; +public: virtual void begin_group(); virtual void add_entry(CollisionEntry *entry); virtual bool end_group(); @@ -38,12 +39,10 @@ public: INLINE bool wants_all_potential_collidees() const; INLINE void set_root(const NodePath &root); -PUBLISHED: +public: static TypeHandle get_class_type() { return _type_handle; } - -public: static void init_type() { TypedReferenceCount::init_type(); register_type(_type_handle, "CollisionHandler", @@ -55,7 +54,7 @@ public: virtual TypeHandle force_init_type() {init_type(); return get_class_type();} protected: - bool _wants_all_potential_collidees; + bool _wants_all_potential_collidees = false; const NodePath *_root; private: From 2aa2a35a9fab8aea2a44cf70b9e01755ec3484dd Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 26 Dec 2022 11:18:00 +0100 Subject: [PATCH 12/16] pstats: Fix regression: crash when opening session file --- panda/src/pstatclient/pStatFrameData.h | 2 +- pandatool/src/pstatserver/pStatClientData.cxx | 2 +- pandatool/src/pstatserver/pStatThreadData.cxx | 4 ++-- pandatool/src/pstatserver/pStatThreadData.h | 3 ++- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/panda/src/pstatclient/pStatFrameData.h b/panda/src/pstatclient/pStatFrameData.h index 3f506ea98e..2df430fbd6 100644 --- a/panda/src/pstatclient/pStatFrameData.h +++ b/panda/src/pstatclient/pStatFrameData.h @@ -58,7 +58,7 @@ public: INLINE double get_level(size_t n) const; bool write_datagram(Datagram &destination, PStatClient *client = nullptr) const; - void read_datagram(DatagramIterator &source, PStatClientVersion *version = nullptr); + void read_datagram(DatagramIterator &source, PStatClientVersion *version); private: class DataPoint { diff --git a/pandatool/src/pstatserver/pStatClientData.cxx b/pandatool/src/pstatserver/pStatClientData.cxx index 1f28caefcf..3c709d8a80 100644 --- a/pandatool/src/pstatserver/pStatClientData.cxx +++ b/pandatool/src/pstatserver/pStatClientData.cxx @@ -471,7 +471,7 @@ read_datagram(DatagramIterator &scan) { std::string name = scan.get_string(); define_thread(thread_index, name); - _threads[thread_index]._data->read_datagram(scan); + _threads[thread_index]._data->read_datagram(scan, this); } update_toplevel_collectors(); diff --git a/pandatool/src/pstatserver/pStatThreadData.cxx b/pandatool/src/pstatserver/pStatThreadData.cxx index a8b1861a88..7ef7fe79a9 100644 --- a/pandatool/src/pstatserver/pStatThreadData.cxx +++ b/pandatool/src/pstatserver/pStatThreadData.cxx @@ -328,11 +328,11 @@ write_datagram(Datagram &dg) const { * Restores the thread data from a datagram. */ void PStatThreadData:: -read_datagram(DatagramIterator &scan) { +read_datagram(DatagramIterator &scan, PStatClientVersion *version) { int frame_number; while ((frame_number = scan.get_int32()) != -1) { PStatFrameData *frame_data = new PStatFrameData; - frame_data->read_datagram(scan); + frame_data->read_datagram(scan, version); record_new_frame(frame_number, frame_data); } diff --git a/pandatool/src/pstatserver/pStatThreadData.h b/pandatool/src/pstatserver/pStatThreadData.h index f2dce4338d..53364266f0 100644 --- a/pandatool/src/pstatserver/pStatThreadData.h +++ b/pandatool/src/pstatserver/pStatThreadData.h @@ -24,6 +24,7 @@ class PStatCollectorDef; class PStatFrameData; class PStatClientData; +class PStatClientVersion; /** * A collection of FrameData structures for recently-received frames within a @@ -63,7 +64,7 @@ public: void record_new_frame(int frame_number, PStatFrameData *frame_data); void write_datagram(Datagram &dg) const; - void read_datagram(DatagramIterator &scan); + void read_datagram(DatagramIterator &scan, PStatClientVersion *version); private: void compute_elapsed_frames() const; From 0747f4605579ac9d17cf1b57d209a28933abbe8d Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 26 Dec 2022 19:10:01 +0100 Subject: [PATCH 13/16] event: Improve interface for adding tasks from C++ The order of arguments is now the same as in Python (with the callable first), sort and priority arguments have been added, and new tasks can be added directly to a chain --- panda/src/event/asyncTaskChain.I | 40 +++++++++++++++++++++++++ panda/src/event/asyncTaskChain.cxx | 28 +++++++++++++++-- panda/src/event/asyncTaskChain.h | 9 +++++- panda/src/event/asyncTaskManager.I | 21 +++---------- panda/src/event/asyncTaskManager.cxx | 15 ++++++++-- panda/src/event/asyncTaskManager.h | 8 +++-- panda/src/framework/windowFramework.cxx | 4 +-- panda/src/gobj/texture.cxx | 7 ++--- 8 files changed, 101 insertions(+), 31 deletions(-) diff --git a/panda/src/event/asyncTaskChain.I b/panda/src/event/asyncTaskChain.I index bd85a12c2c..bd72709817 100644 --- a/panda/src/event/asyncTaskChain.I +++ b/panda/src/event/asyncTaskChain.I @@ -21,6 +21,46 @@ is_started() const { return (_state == S_started); } +#ifndef CPPPARSER +/** + * Adds a new task to the task chain which calls the indicated callable. + * This method is defined as a more convenient alternative to subclassing + * AsyncTask. + * + * This given callable allowed to be any object defining a call operator that + * accepts an AsyncTask pointer and returns a DoneStatus. + * + * Returns the newly created AsyncTask object. + * + * @since 1.11.0 + */ +template +INLINE AsyncTask *AsyncTaskChain:: +add(Callable callable, const std::string &name, int sort, int priority) { + class InlineTask final : public AsyncTask { + public: + InlineTask(Callable callable, const std::string &name, int sort, int priority) : + AsyncTask(name), + _callable(std::move(callable)) { + _sort = sort; + _priority = priority; + } + + ALLOC_DELETED_CHAIN(InlineTask); + + private: + virtual DoneStatus do_task() override final { + return _callable(this); + } + + Callable _callable; + }; + AsyncTask *task = new InlineTask(std::move(callable), name, sort, priority); + add(task); + return task; +} +#endif + /** * Returns the time at which the next sleeping thread will awaken, or -1 if * there are no sleeping threads. Assumes the lock is already held. diff --git a/panda/src/event/asyncTaskChain.cxx b/panda/src/event/asyncTaskChain.cxx index 03a1bce508..a854596898 100644 --- a/panda/src/event/asyncTaskChain.cxx +++ b/panda/src/event/asyncTaskChain.cxx @@ -37,14 +37,15 @@ PStatCollector AsyncTaskChain::_wait_pcollector("Wait"); * */ AsyncTaskChain:: -AsyncTaskChain(AsyncTaskManager *manager, const string &name) : +AsyncTaskChain(AsyncTaskManager *manager, const string &name, int num_threads, + ThreadPriority thread_priority) : Namable(name), _manager(manager), _cvar(manager->_lock), _tick_clock(false), _timeslice_priority(false), - _num_threads(0), - _thread_priority(TP_normal), + _num_threads(num_threads), + _thread_priority(thread_priority), _frame_budget(-1.0), _frame_sync(false), _num_busy_threads(0), @@ -288,6 +289,27 @@ start_threads() { } } +/** + * Adds the indicated task to the active queue. The task must be inactive, and + * may not have been added to any queue (including the current one). + */ +void AsyncTaskChain:: +add(AsyncTask *task) { + nassertv(task->_manager == nullptr && task->_state == AsyncTask::S_inactive); + nassertv(task->is_runnable()); + + task->_chain_name = get_name(); + task->upon_birth(_manager); + + if (task_cat.is_debug()) { + task_cat.debug() + << "Adding " << *task << "\n"; + } + + MutexHolder holder(_manager->_lock); + do_add(task); +} + /** * Returns true if the indicated task has been added to this AsyncTaskChain, * false otherwise. diff --git a/panda/src/event/asyncTaskChain.h b/panda/src/event/asyncTaskChain.h index 3f288c9770..ed54cbf42c 100644 --- a/panda/src/event/asyncTaskChain.h +++ b/panda/src/event/asyncTaskChain.h @@ -49,7 +49,8 @@ class AsyncTaskManager; */ class EXPCL_PANDA_EVENT AsyncTaskChain : public TypedReferenceCount, public Namable { public: - AsyncTaskChain(AsyncTaskManager *manager, const std::string &name); + AsyncTaskChain(AsyncTaskManager *manager, const std::string &name, + int num_threads=0, ThreadPriority thread_priority=TP_normal); ~AsyncTaskChain(); PUBLISHED: @@ -76,6 +77,12 @@ PUBLISHED: void start_threads(); INLINE bool is_started() const; + void add(AsyncTask *task); +#ifndef CPPPARSER + template + INLINE AsyncTask *add(Callable callable, const std::string &name, + int sort = 0, int priority = 0); +#endif bool has_task(AsyncTask *task) const; BLOCKING void wait_for_tasks(); diff --git a/panda/src/event/asyncTaskManager.I b/panda/src/event/asyncTaskManager.I index 46f675a3e5..5c413bb70c 100644 --- a/panda/src/event/asyncTaskManager.I +++ b/panda/src/event/asyncTaskManager.I @@ -47,23 +47,10 @@ get_clock() { */ template INLINE AsyncTask *AsyncTaskManager:: -add(const std::string &name, Callable callable) { - class InlineTask final : public AsyncTask { - public: - InlineTask(Callable callable) : _callable(std::move(callable)) {} - - ALLOC_DELETED_CHAIN(InlineTask); - - private: - virtual DoneStatus do_task() override final { - return _callable(this); - } - - Callable _callable; - }; - AsyncTask *task = new InlineTask(std::move(callable)); - add(task); - return task; +add(Callable callable, const std::string &name, int sort, int priority) { + AsyncTaskChain *chain = make_task_chain("default"); + nassertr(chain != nullptr, nullptr); + return chain->add(std::move(callable), name, sort, priority); } #endif diff --git a/panda/src/event/asyncTaskManager.cxx b/panda/src/event/asyncTaskManager.cxx index 48d3ac5bfc..43dc285f04 100644 --- a/panda/src/event/asyncTaskManager.cxx +++ b/panda/src/event/asyncTaskManager.cxx @@ -131,6 +131,17 @@ make_task_chain(const string &name) { return do_make_task_chain(name); } +/** + * Creates a new threaded AsyncTaskChain of the indicated name and stores it + * within the AsyncTaskManager. If a task chain with this name already exists, + * returns it instead. + */ +AsyncTaskChain *AsyncTaskManager:: +make_task_chain(const string &name, int num_threads, ThreadPriority thread_priority) { + MutexHolder holder(_lock); + return do_make_task_chain(name, num_threads, thread_priority); +} + /** * Searches a new AsyncTaskChain of the indicated name and returns it if it * exists, or NULL otherwise. @@ -562,8 +573,8 @@ write(std::ostream &out, int indent_level) const { * Assumes the lock is held. */ AsyncTaskChain *AsyncTaskManager:: -do_make_task_chain(const string &name) { - PT(AsyncTaskChain) chain = new AsyncTaskChain(this, name); +do_make_task_chain(const string &name, int num_threads, ThreadPriority thread_priority) { + PT(AsyncTaskChain) chain = new AsyncTaskChain(this, name, num_threads, thread_priority); TaskChains::const_iterator tci = _task_chains.insert(chain).first; return (*tci); diff --git a/panda/src/event/asyncTaskManager.h b/panda/src/event/asyncTaskManager.h index 0028054ef7..2825895078 100644 --- a/panda/src/event/asyncTaskManager.h +++ b/panda/src/event/asyncTaskManager.h @@ -60,13 +60,16 @@ PUBLISHED: AsyncTaskChain *get_task_chain(int n) const; MAKE_SEQ(get_task_chains, get_num_task_chains, get_task_chain); AsyncTaskChain *make_task_chain(const std::string &name); + AsyncTaskChain *make_task_chain(const std::string &name, int num_threads, + ThreadPriority thread_priority); AsyncTaskChain *find_task_chain(const std::string &name); BLOCKING bool remove_task_chain(const std::string &name); void add(AsyncTask *task); #ifndef CPPPARSER template - INLINE AsyncTask *add(const std::string &name, Callable callable); + INLINE AsyncTask *add(Callable callable, const std::string &name, + int sort = 0, int priority = 0); #endif bool has_task(AsyncTask *task) const; @@ -100,7 +103,8 @@ PUBLISHED: INLINE static AsyncTaskManager *get_global_ptr(); protected: - AsyncTaskChain *do_make_task_chain(const std::string &name); + AsyncTaskChain *do_make_task_chain(const std::string &name, int num_threads=0, + ThreadPriority thread_priority=TP_normal); AsyncTaskChain *do_find_task_chain(const std::string &name); INLINE void add_task_by_name(AsyncTask *task); diff --git a/panda/src/framework/windowFramework.cxx b/panda/src/framework/windowFramework.cxx index c6eaa1d48a..c2fd0f6582 100644 --- a/panda/src/framework/windowFramework.cxx +++ b/panda/src/framework/windowFramework.cxx @@ -1437,10 +1437,10 @@ create_anim_controls() { setup_shuttle_button(":", 3, st_forward_button); AsyncTaskManager &task_mgr = _panda_framework->get_task_mgr(); - _update_anim_controls_task = task_mgr.add("controls", [this](AsyncTask *task) { + _update_anim_controls_task = task_mgr.add([this](AsyncTask *task) { update_anim_controls(); return AsyncTask::DS_cont; - }); + }, "controls"); } /** diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index a8562f317d..995549cb5c 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -1064,7 +1064,7 @@ async_ensure_ram_image(bool allow_compression, int priority) { double delay = async_load_delay; // This texture has not yet been queued to be reloaded. Queue it up now. - task = task_mgr->add(task_name, [=](AsyncTask *task) { + task = chain->add([=](AsyncTask *task) { if (delay != 0.0) { Thread::sleep(delay); } @@ -1080,9 +1080,8 @@ async_ensure_ram_image(bool allow_compression, int priority) { do_get_uncompressed_ram_image(cdata); } return AsyncTask::DS_done; - }); - task->set_priority(priority); - task->set_task_chain("texture_reload"); + }, task_name, 0, priority); + cdataw->_reload_task = task; return (AsyncFuture *)task; } From d5fed54a0c214967fc618f397b55174af54cc33a Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 26 Dec 2022 19:18:25 +0100 Subject: [PATCH 14/16] x11: Fix occasional crash on shutdown --- panda/src/x11display/x11GraphicsWindow.cxx | 6 +++--- panda/src/x11display/x11GraphicsWindow.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/panda/src/x11display/x11GraphicsWindow.cxx b/panda/src/x11display/x11GraphicsWindow.cxx index eae17632f0..b96c4c4f82 100644 --- a/panda/src/x11display/x11GraphicsWindow.cxx +++ b/panda/src/x11display/x11GraphicsWindow.cxx @@ -121,6 +121,7 @@ x11GraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, _override_redirect = False; _wm_delete_window = x11_pipe->_wm_delete_window; _net_wm_ping = x11_pipe->_net_wm_ping; + _net_wm_state = x11_pipe->_net_wm_state; PT(GraphicsWindowInputDevice) device = GraphicsWindowInputDevice::pointer_and_keyboard(this, "keyboard_mouse"); add_input_device(device); @@ -368,9 +369,7 @@ process_events() { case PropertyNotify: //std::cout << "PropertyNotify event: atom = " << event.xproperty.atom << std::endl; - x11GraphicsPipe *x11_pipe; - DCAST_INTO_V(x11_pipe, _pipe); - if (event.xproperty.atom == x11_pipe->_net_wm_state) { + if (event.xproperty.atom == _net_wm_state) { // currently we're only interested in the net_wm_state type of // changes and only need to gather property informations once at // the end after the while loop @@ -516,6 +515,7 @@ process_events() { } else if ((Atom)(event.xclient.data.l[0]) == _net_wm_ping && event.xclient.window == _xwindow) { + x11GraphicsPipe *x11_pipe; DCAST_INTO_V(x11_pipe, _pipe); event.xclient.window = x11_pipe->get_root(); XSendEvent(_display, x11_pipe->get_root(), False, SubstructureRedirectMask | SubstructureNotifyMask, &event); diff --git a/panda/src/x11display/x11GraphicsWindow.h b/panda/src/x11display/x11GraphicsWindow.h index 49b878a904..1bb20e181e 100644 --- a/panda/src/x11display/x11GraphicsWindow.h +++ b/panda/src/x11display/x11GraphicsWindow.h @@ -109,6 +109,7 @@ protected: Bool _override_redirect; Atom _wm_delete_window; Atom _net_wm_ping; + Atom _net_wm_state; x11GraphicsPipe::pfn_XRRGetScreenInfo _XRRGetScreenInfo; x11GraphicsPipe::pfn_XRRSetScreenConfig _XRRSetScreenConfig; From cfd18bb16f5dfbb8c9c6a447c40000355213dded Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 26 Dec 2022 19:25:53 +0100 Subject: [PATCH 15/16] display: Add support for asynchronous screenshot download --- direct/src/showbase/ShowBase.py | 15 +- panda/src/display/CMakeLists.txt | 2 + panda/src/display/config_display.cxx | 2 + panda/src/display/graphicsEngine.cxx | 5 + panda/src/display/graphicsOutput.cxx | 90 +++++- panda/src/display/graphicsOutput.h | 7 + panda/src/display/graphicsStateGuardian.cxx | 6 +- panda/src/display/graphicsStateGuardian.h | 3 +- panda/src/display/p3display_composite2.cxx | 1 + panda/src/display/screenshotRequest.I | 39 +++ panda/src/display/screenshotRequest.cxx | 104 +++++++ panda/src/display/screenshotRequest.h | 71 +++++ panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx | 13 +- panda/src/dxgsg9/dxGraphicsStateGuardian9.h | 3 +- panda/src/gles2gsg/gles2gsg.h | 7 + .../glstuff/glGraphicsStateGuardian_src.cxx | 293 +++++++++++++++++- .../src/glstuff/glGraphicsStateGuardian_src.h | 55 +++- panda/src/gsgbase/graphicsStateGuardianBase.h | 4 +- .../tinydisplay/tinyGraphicsStateGuardian.cxx | 7 +- .../tinydisplay/tinyGraphicsStateGuardian.h | 3 +- 20 files changed, 702 insertions(+), 28 deletions(-) create mode 100644 panda/src/display/screenshotRequest.I create mode 100644 panda/src/display/screenshotRequest.cxx create mode 100644 panda/src/display/screenshotRequest.h diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index d33e580ba7..ce02cfb78d 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -2713,7 +2713,7 @@ class ShowBase(DirectObject.DirectObject): def screenshot(self, namePrefix = 'screenshot', defaultFilename = 1, source = None, - imageComment=""): + imageComment="", blocking=True): """ Captures a screenshot from the main window or from the specified window or Texture and writes it to a filename in the current directory (or to a specified directory). @@ -2735,6 +2735,13 @@ class ShowBase(DirectObject.DirectObject): generated by makeCubeMap(), namePrefix should contain the hash mark ('#') character. + Normally, this call will block until the screenshot is fully + written. To write the screenshot in a background thread + instead, pass blocking = False. In this case, the return value + is a future that can be awaited. + + A "screenshot" event will be sent once the screenshot is saved. + :returns: The filename if successful, or None if there is a problem. """ @@ -2751,8 +2758,12 @@ class ShowBase(DirectObject.DirectObject): saved = source.write(filename, 0, 0, 1, 0) else: saved = source.write(filename) - else: + elif blocking: saved = source.saveScreenshot(filename, imageComment) + else: + request = source.saveAsyncScreenshot(filename, imageComment) + request.addDoneCallback(lambda fut, filename=filename: messenger.send('screenshot', [filename])) + return request if saved: # Announce to anybody that a screenshot has been taken diff --git a/panda/src/display/CMakeLists.txt b/panda/src/display/CMakeLists.txt index 56e9552d9b..e7662b9e98 100644 --- a/panda/src/display/CMakeLists.txt +++ b/panda/src/display/CMakeLists.txt @@ -27,6 +27,7 @@ set(P3DISPLAY_HEADERS windowHandle.I windowHandle.h windowProperties.I windowProperties.h renderBuffer.h + screenshotRequest.I screenshotRequest.h stereoDisplayRegion.I stereoDisplayRegion.h displaySearchParameters.h displayInformation.h @@ -61,6 +62,7 @@ set(P3DISPLAY_SOURCES parasiteBuffer.cxx windowHandle.cxx windowProperties.cxx + screenshotRequest.cxx stereoDisplayRegion.cxx subprocessWindow.cxx touchInfo.cxx diff --git a/panda/src/display/config_display.cxx b/panda/src/display/config_display.cxx index 1a5f975215..2c7ea8fd2e 100644 --- a/panda/src/display/config_display.cxx +++ b/panda/src/display/config_display.cxx @@ -29,6 +29,7 @@ #include "nativeWindowHandle.h" #include "parasiteBuffer.h" #include "pandaSystem.h" +#include "screenshotRequest.h" #include "stereoDisplayRegion.h" #include "subprocessWindow.h" #include "windowHandle.h" @@ -534,6 +535,7 @@ init_libdisplay() { MouseAndKeyboard::init_type(); NativeWindowHandle::init_type(); ParasiteBuffer::init_type(); + ScreenshotRequest::init_type(); StandardMunger::init_type(); StereoDisplayRegion::init_type(); #ifdef SUPPORT_SUBPROCESS_WINDOW diff --git a/panda/src/display/graphicsEngine.cxx b/panda/src/display/graphicsEngine.cxx index af95350f82..061375611d 100644 --- a/panda/src/display/graphicsEngine.cxx +++ b/panda/src/display/graphicsEngine.cxx @@ -1419,6 +1419,8 @@ cull_and_draw_together(GraphicsEngine::Windows wlist, } if (win->begin_frame(GraphicsOutput::FM_render, current_thread)) { + win->copy_async_screenshot(); + if (win->is_any_clear_active()) { GraphicsStateGuardian *gsg = win->get_gsg(); PStatGPUTimer timer(gsg, win->get_clear_window_pcollector(), current_thread); @@ -1720,6 +1722,9 @@ draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) { // a current context for PStatGPUTimer to work. { PStatGPUTimer timer(gsg, win->get_draw_window_pcollector(), current_thread); + + win->copy_async_screenshot(); + if (win->is_any_clear_active()) { PStatGPUTimer timer(gsg, win->get_clear_window_pcollector(), current_thread); win->get_gsg()->push_group_marker("Clear"); diff --git a/panda/src/display/graphicsOutput.cxx b/panda/src/display/graphicsOutput.cxx index 462a298cc3..6d4e19981e 100644 --- a/panda/src/display/graphicsOutput.cxx +++ b/panda/src/display/graphicsOutput.cxx @@ -976,6 +976,43 @@ make_cube_map(const string &name, int size, NodePath &camera_rig, return buffer; } +/** + * Like save_screenshot, but performs both the texture transfer and the saving + * to disk in the background. Returns a future that can be awaited. + * + * This captures the frame that was last submitted by the App stage to the + * render_frame() call. This may not be the latest frame shown on the screen + * if the multi-threaded pipeline is used, in which case the request may take + * several frames extra to complete. + */ +PT(ScreenshotRequest) GraphicsOutput:: +save_async_screenshot(const Filename &filename, const std::string &image_comment) { + PT(ScreenshotRequest) request = get_async_screenshot(); + request->add_output_file(filename, image_comment); + return request; +} + +/** + * Used to obtain a new Texture object containing the previously rendered frame. + * Unlike get_screenshot, this works asynchronously, meaning that the contents + * are transferred in the background. Returns a future that can be awaited. + * + * This captures the frame that was last submitted by the App stage to the + * render_frame() call. This may not be the latest frame shown on the screen + * if the multi-threaded pipeline is used, in which case the request may take + * several frames extra to complete. + */ +PT(ScreenshotRequest) GraphicsOutput:: +get_async_screenshot() { + Thread *current_thread = Thread::get_current_thread(); + CDWriter cdata(_cycler, current_thread); + if (cdata->_screenshot_request == nullptr) { + PT(Texture) texture = new Texture("screenshot of " + get_name()); + cdata->_screenshot_request = new ScreenshotRequest(texture); + } + return cdata->_screenshot_request; +} + /** * Returns a PandaNode containing a square polygon. The dimensions are * (-1,0,-1) to (1,0,1). The texture coordinates are such that the texture of @@ -1468,6 +1505,56 @@ copy_to_textures() { return okflag; } +/** + * Do the necessary copies for the get_async_screenshot request. + */ +void GraphicsOutput:: +copy_async_screenshot() { + Thread *current_thread = Thread::get_current_thread(); + PT(ScreenshotRequest) request; + { + CDWriter cdata(_cycler, current_thread); + if (cdata->_screenshot_request == nullptr) { + return; + } + request = std::move(cdata->_screenshot_request); + cdata->_screenshot_request.clear(); + } + + // Make sure it is cleared from upstream stages as well. + OPEN_ITERATE_UPSTREAM_ONLY(_cycler, current_thread) { + CDStageWriter cdata(_cycler, pipeline_stage, current_thread); + if (cdata->_screenshot_request == request) { + cdata->_screenshot_request.clear(); + } + } + CLOSE_ITERATE_UPSTREAM_ONLY(_cycler); + + PStatTimer timer(_copy_texture_pcollector); + + RenderBuffer buffer = _gsg->get_render_buffer(get_draw_buffer_type(), + get_fb_properties()); + DisplayRegion *dr = _overlay_display_region; + + Texture *texture = request->get_result(); + + if (_fb_properties.is_stereo()) { + // We've got two texture views to copy. + texture->set_num_views(2); + + RenderBuffer left(_gsg, buffer._buffer_type & ~RenderBuffer::T_right); + RenderBuffer right(_gsg, buffer._buffer_type & ~RenderBuffer::T_left); + + _gsg->framebuffer_copy_to_ram(texture, 0, _target_tex_page, + dr, left, request); + _gsg->framebuffer_copy_to_ram(texture, 1, _target_tex_page, + dr, right, request); + } else { + _gsg->framebuffer_copy_to_ram(texture, 0, _target_tex_page, + dr, buffer, request); + } +} + /** * Generates a GeomVertexData for a texture card. */ @@ -1653,7 +1740,8 @@ CData(const GraphicsOutput::CData ©) : _active(copy._active), _one_shot_frame(copy._one_shot_frame), _active_display_regions(copy._active_display_regions), - _active_display_regions_stale(copy._active_display_regions_stale) + _active_display_regions_stale(copy._active_display_regions_stale), + _screenshot_request(copy._screenshot_request) { } diff --git a/panda/src/display/graphicsOutput.h b/panda/src/display/graphicsOutput.h index 7a210cb188..87d094e9aa 100644 --- a/panda/src/display/graphicsOutput.h +++ b/panda/src/display/graphicsOutput.h @@ -41,6 +41,7 @@ #include "pipelineCycler.h" #include "updateSeq.h" #include "asyncFuture.h" +#include "screenshotRequest.h" class PNMImage; class GraphicsEngine; @@ -239,6 +240,9 @@ PUBLISHED: const Filename &filename, const std::string &image_comment = ""); INLINE bool get_screenshot(PNMImage &image); INLINE PT(Texture) get_screenshot(); + PT(ScreenshotRequest) save_async_screenshot(const Filename &filename, + const std::string &image_comment = ""); + PT(ScreenshotRequest) get_async_screenshot(); NodePath get_texture_card(); @@ -298,6 +302,7 @@ protected: void prepare_for_deletion(); void promote_to_copy_texture(); bool copy_to_textures(); + void copy_async_screenshot(); INLINE void begin_frame_spam(FrameMode mode); INLINE void end_frame_spam(FrameMode mode); @@ -392,6 +397,8 @@ protected: int _one_shot_frame; ActiveDisplayRegions _active_display_regions; bool _active_display_regions_stale; + + PT(ScreenshotRequest) _screenshot_request; }; PipelineCycler _cycler; typedef CycleDataLockedReader CDLockedReader; diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 6f23741a15..34e49c42de 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -3037,11 +3037,15 @@ framebuffer_copy_to_texture(Texture *, int, int, const DisplayRegion *, * into system memory, not texture memory. Returns true on success, false on * failure. * + * If a future is given, the operation may be scheduled to occur in the + * background, in which case the texture will be passed as the result of the + * future when the operation is complete. + * * This completely redefines the ram image of the indicated texture. */ bool GraphicsStateGuardian:: framebuffer_copy_to_ram(Texture *, int, int, const DisplayRegion *, - const RenderBuffer &) { + const RenderBuffer &, ScreenshotRequest *) { return false; } diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index bc6e175f8e..ee08075393 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -426,7 +426,8 @@ public: virtual bool framebuffer_copy_to_texture (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb); virtual bool framebuffer_copy_to_ram - (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb); + (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb, + ScreenshotRequest *request = nullptr); virtual void bind_light(PointLight *light_obj, const NodePath &light, int light_id); diff --git a/panda/src/display/p3display_composite2.cxx b/panda/src/display/p3display_composite2.cxx index ef13357435..8f49915f30 100644 --- a/panda/src/display/p3display_composite2.cxx +++ b/panda/src/display/p3display_composite2.cxx @@ -9,6 +9,7 @@ #include "parasiteBuffer.cxx" #include "standardMunger.cxx" #include "touchInfo.cxx" +#include "screenshotRequest.cxx" #include "stereoDisplayRegion.cxx" #include "subprocessWindow.cxx" #ifdef IS_OSX diff --git a/panda/src/display/screenshotRequest.I b/panda/src/display/screenshotRequest.I new file mode 100644 index 0000000000..27c8d0affe --- /dev/null +++ b/panda/src/display/screenshotRequest.I @@ -0,0 +1,39 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file screenshotRequest.I + * @author rdb + * @date 2022-12-26 + */ + +/** + * + */ +INLINE ScreenshotRequest:: +ScreenshotRequest(Texture *tex) : + _frame_number(ClockObject::get_global_clock()->get_frame_count()) { + _result = tex; + _result_ref = tex; +} + + +/** + * Returns the frame number in which the request originated. + */ +INLINE int ScreenshotRequest:: +get_frame_number() const { + return _frame_number; +} + +/** + * Returns the resulting texture. Can always be called. + */ +INLINE Texture *ScreenshotRequest:: +get_result() const { + return (Texture *)_result; +} diff --git a/panda/src/display/screenshotRequest.cxx b/panda/src/display/screenshotRequest.cxx new file mode 100644 index 0000000000..bb05b448c6 --- /dev/null +++ b/panda/src/display/screenshotRequest.cxx @@ -0,0 +1,104 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file screenshotRequest.cxx + * @author rdb + * @date 2022-12-26 + */ + +#include "screenshotRequest.h" +#include "lightMutexHolder.h" +#include "pnmImage.h" +#include "texture.h" + +TypeHandle ScreenshotRequest::_type_handle; + +/** + * + */ +void ScreenshotRequest:: +set_view_data(int view, const void *ptr) { + const int z = 0; + + Texture *tex = get_result(); + PTA_uchar new_image = tex->modify_ram_image(); + unsigned char *image_ptr = new_image.p(); + size_t image_size = tex->get_ram_image_size(); + if (z >= 0 || view > 0) { + image_size = tex->get_expected_ram_page_size(); + if (z >= 0) { + image_ptr += z * image_size; + } + if (view > 0) { + image_ptr += (view * tex->get_z_size()) * image_size; + nassertd(view < tex->get_num_views()) { + if (set_future_state(FS_cancelled)) { + notify_done(false); + } + return; + } + } + } + memcpy(image_ptr, ptr, image_size); +} + +/** + * + */ +void ScreenshotRequest:: +finish() { + Texture *tex = get_result(); + + ++_got_num_views; + if (_got_num_views < tex->get_num_views()) { + return; + } + + { + LightMutexHolder holder(_lock); + if (!_output_files.empty()) { + PNMImage image; + tex->store(image); + + for (const auto &item : _output_files) { + image.set_comment(item.second); + image.write(item.first); + } + } + + AsyncFuture::set_result(tex); + _output_files.clear(); + + if (!set_future_state(FS_finished)) { + return; + } + } + + notify_done(true); +} + +/** + * Adds a filename to write the screenshot to when it is available. If the + * request is already done, performs the write synchronously. + */ +void ScreenshotRequest:: +add_output_file(const Filename &filename, const std::string &image_comment) { + if (!done()) { + LightMutexHolder holder(_lock); + if (!done()) { + _output_files[filename] = image_comment; + return; + } + } + // Was already done, write it right away. + Texture *tex = get_result(); + PNMImage image; + tex->store(image); + image.set_comment(image_comment); + image.write(filename); +} diff --git a/panda/src/display/screenshotRequest.h b/panda/src/display/screenshotRequest.h new file mode 100644 index 0000000000..4d56cf73bf --- /dev/null +++ b/panda/src/display/screenshotRequest.h @@ -0,0 +1,71 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file screenshotRequest.h + * @author rdb + * @date 2022-12-26 + */ + +#ifndef SCREENSHOTREQUEST_H +#define SCREENSHOTREQUEST_H + +#include "pandabase.h" + +#include "asyncFuture.h" +#include "filename.h" +#include "lightMutex.h" +#include "pmap.h" + +/** + * A class representing an asynchronous request to save a screenshot. + */ +class EXPCL_PANDA_PGRAPH ScreenshotRequest : public AsyncFuture { +public: + INLINE ScreenshotRequest(Texture *tex); + + INLINE int get_frame_number() const; + INLINE Texture *get_result() const; + + void set_view_data(int view, const void *ptr); + void finish(); + +PUBLISHED: + void add_output_file(const Filename &filename, + const std::string &image_comment = ""); + +private: + // It's possible to call save_screenshot multiple times in the same frame, so + // rather than have to store a vector of request objects, we just allow + // storing multiple filenames to handle this corner case. + LightMutex _lock; + pmap _output_files; + int _got_num_views = 0; + + int _frame_number = 0; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + AsyncFuture::init_type(); + register_type(_type_handle, "ScreenshotRequest", + AsyncFuture::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + +private: + static TypeHandle _type_handle; +}; + +#include "screenshotRequest.I" + +#endif diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx index 1452c59252..be3cfb14d6 100644 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx @@ -1988,8 +1988,17 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z, */ bool DXGraphicsStateGuardian9:: framebuffer_copy_to_ram(Texture *tex, int view, int z, - const DisplayRegion *dr, const RenderBuffer &rb) { - return do_framebuffer_copy_to_ram(tex, view, z, dr, rb, false); + const DisplayRegion *dr, const RenderBuffer &rb, + ScreenshotRequest *request) { + bool success = do_framebuffer_copy_to_ram(tex, view, z, dr, rb, false); + if (request != nullptr) { + if (success) { + request->finish(); + } else { + request->cancel(); + } + } + return success; } /** diff --git a/panda/src/dxgsg9/dxGraphicsStateGuardian9.h b/panda/src/dxgsg9/dxGraphicsStateGuardian9.h index eda2a5c233..01fa8cf981 100644 --- a/panda/src/dxgsg9/dxGraphicsStateGuardian9.h +++ b/panda/src/dxgsg9/dxGraphicsStateGuardian9.h @@ -127,7 +127,8 @@ public: const RenderBuffer &rb); virtual bool framebuffer_copy_to_ram(Texture *tex, int view, int z, const DisplayRegion *dr, - const RenderBuffer &rb); + const RenderBuffer &rb, + ScreenshotRequest *request); bool do_framebuffer_copy_to_ram(Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb, diff --git a/panda/src/gles2gsg/gles2gsg.h b/panda/src/gles2gsg/gles2gsg.h index 044ac72991..d5c77bf6b9 100644 --- a/panda/src/gles2gsg/gles2gsg.h +++ b/panda/src/gles2gsg/gles2gsg.h @@ -177,6 +177,7 @@ typedef char GLchar; #define GL_READ_ONLY 0x88B8 #define GL_WRITE_ONLY 0x88B9 #define GL_READ_WRITE 0x88BA +#define GL_PIXEL_PACK_BUFFER 0x88EB #define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF #define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 #define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 @@ -253,6 +254,12 @@ typedef char GLchar; #define GL_UNSIGNED_INT_IMAGE_3D 0x9064 #define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 #define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 +#define GL_UNSIGNALED 0x9118 +#define GL_SIGNALED 0x9119 +#define GL_ALREADY_SIGNALED 0x911A +#define GL_TIMEOUT_EXPIRED 0x911B +#define GL_CONDITION_SATISFIED 0x911C #define GL_COMPUTE_SHADER 0x91B9 #define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 #define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index 95078a71e2..702da1ce2c 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -93,6 +93,8 @@ PStatCollector CLP(GraphicsStateGuardian)::_texture_update_pcollector("Draw:Upda PStatCollector CLP(GraphicsStateGuardian)::_fbo_bind_pcollector("Draw:Bind FBO"); PStatCollector CLP(GraphicsStateGuardian)::_check_error_pcollector("Draw:Check errors"); PStatCollector CLP(GraphicsStateGuardian)::_check_residency_pcollector("*:PStats:Check residency"); +PStatCollector CLP(GraphicsStateGuardian)::_wait_fence_pcollector("Wait:Fence"); +PStatCollector CLP(GraphicsStateGuardian)::_copy_texture_finish_pcollector("Draw:Copy texture:Finish"); #if defined(HAVE_CG) && !defined(OPENGLES) AtomicAdjust::Integer CLP(GraphicsStateGuardian)::_num_gsgs_with_cg_contexts = 0; @@ -164,6 +166,10 @@ null_glPolygonOffsetClamp(GLfloat factor, GLfloat units, GLfloat clamp) { } #endif +static void APIENTRY +null_glMemoryBarrier(GLbitfield barriers) { +} + #ifndef OPENGLES_1 // We have a default shader that will be applied when there isn't any shader // applied (e.g. if it failed to compile). We need this because OpenGL ES @@ -507,7 +513,9 @@ int CLP(GraphicsStateGuardian)::get_driver_shader_version_minor() { return _gl_s CLP(GraphicsStateGuardian):: CLP(GraphicsStateGuardian)(GraphicsEngine *engine, GraphicsPipe *pipe) : GraphicsStateGuardian(gl_coordinate_system, engine, pipe), - _renderbuffer_residency(get_prepared_objects()->get_name(), "renderbuffer") + _renderbuffer_residency(get_prepared_objects()->get_name(), "renderbuffer"), + _active_ppbuffer_memory_pcollector("Graphics memory:" + get_prepared_objects()->get_name() + ":Active:ppbuffer"), + _inactive_ppbuffer_memory_pcollector("Graphics memory:" + get_prepared_objects()->get_name() + ":Inactive:ppbuffer") { _error_count = 0; _last_error_check = -1.0; @@ -1685,13 +1693,18 @@ reset() { if (is_at_least_gles_version(3, 0)) { _glMapBufferRange = (PFNGLMAPBUFFERRANGEEXTPROC) get_extension_func("glMapBufferRange"); - - } else if (has_extension("GL_EXT_map_buffer_range")) { + _glUnmapBuffer = (PFNGLUNMAPBUFFERPROC) + get_extension_func("glUnmapBuffer"); + } + else if (has_extension("GL_EXT_map_buffer_range")) { _glMapBufferRange = (PFNGLMAPBUFFERRANGEEXTPROC) get_extension_func("glMapBufferRangeEXT"); - - } else { + _glUnmapBuffer = (PFNGLUNMAPBUFFERPROC) + get_extension_func("glUnmapBufferOES"); + } + else { _glMapBufferRange = nullptr; + _glUnmapBuffer = nullptr; } #else // Check for various advanced buffer management features. @@ -2891,6 +2904,28 @@ reset() { is_at_least_gl_version(3, 3) || has_extension("GL_ARB_blend_func_extended"); #endif +#ifndef OPENGLES + if (is_at_least_gl_version(3, 2) || has_extension("GL_ARB_sync")) { + _glFenceSync = (PFNGLFENCESYNCPROC)get_extension_func("glFenceSync"); + _glDeleteSync = (PFNGLDELETESYNCPROC)get_extension_func("glDeleteSync"); + _glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)get_extension_func("glClientWaitSync"); + _glGetSynciv = (PFNGLGETSYNCIVPROC)get_extension_func("glGetSynciv"); + } +#elif !defined(OPENGLES_1) + if (is_at_least_gles_version(3, 0)) { + _glFenceSync = (PFNGLFENCESYNCPROC)get_extension_func("glFenceSync"); + _glDeleteSync = (PFNGLDELETESYNCPROC)get_extension_func("glDeleteSync"); + _glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)get_extension_func("glClientWaitSync"); + _glGetSynciv = (PFNGLGETSYNCIVPROC)get_extension_func("glGetSynciv"); + } + else if (has_extension("GL_APPLE_sync")) { + _glFenceSync = (PFNGLFENCESYNCPROC)get_extension_func("glFenceSyncAPPLE"); + _glDeleteSync = (PFNGLDELETESYNCPROC)get_extension_func("glDeleteSyncAPPLE"); + _glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)get_extension_func("glClientWaitSyncAPPLE"); + _glGetSynciv = (PFNGLGETSYNCIVPROC)get_extension_func("glGetSyncivAPPLE"); + } +#endif + #ifdef OPENGLES _edge_clamp = GL_CLAMP_TO_EDGE; #else @@ -3137,7 +3172,7 @@ reset() { } else { _glBindImageTexture = nullptr; - _glMemoryBarrier = nullptr; + _glMemoryBarrier = null_glMemoryBarrier; } #endif // !OPENGLES_1 @@ -4162,6 +4197,10 @@ begin_frame(Thread *current_thread) { _primitive_batches_display_list_pcollector.clear_level(); #endif + if (!_async_ram_copies.empty()) { + finish_async_framebuffer_ram_copies(); + } + #if defined(DO_PSTATS) && !defined(OPENGLES) int frame_number = ClockObject::get_global_clock()->get_frame_count(current_thread); if (_current_frame_timing == nullptr || @@ -4350,6 +4389,38 @@ end_frame(Thread *current_thread) { } #endif // OPENGLES +#ifndef OPENGLES_1 + if (!_deleted_buffers.empty()) { + GLuint *indices = (GLuint *)alloca(sizeof(GLuint *) * _deleted_buffers.size()); + size_t num_indices = 0; + DeletedBuffers::iterator it = _deleted_buffers.begin(); + while (it != _deleted_buffers.end()) { + DeletedBuffer &buffer = *it; + if (!_supports_buffer_storage && buffer._mapped_pointer != nullptr) { + _glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer._index); + _glUnmapBuffer(GL_PIXEL_PACK_BUFFER); + buffer._mapped_pointer = nullptr; + } + if (++buffer._age > 2) { + indices[num_indices++] = buffer._index; + it = _deleted_buffers.erase(it); + _inactive_ppbuffer_memory_pcollector.sub_level(buffer._size); + } else { + ++it; + } + } + if (!_supports_buffer_storage) { + _glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + } + if (num_indices > 0) { + _glDeleteBuffers(num_indices, indices); + } + } + + _active_ppbuffer_memory_pcollector.flush_level(); + _inactive_ppbuffer_memory_pcollector.flush_level(); +#endif + #ifndef NDEBUG if (_check_errors || (_supports_debug && gl_debug)) { report_my_gl_errors(); @@ -6573,6 +6644,72 @@ record_deleted_display_list(GLuint index) { _deleted_display_lists.push_back(index); } +#ifndef OPENGLES_1 +/** + * Creates a new buffer for client access. It is bound when this returns. + * If persistent mapping is possible, mapped_ptr will be filled in with a + * pointer to the mapped data. + */ +void CLP(GraphicsStateGuardian):: +bind_new_client_buffer(GLuint &index, void *&mapped_ptr, GLenum target, size_t size) { + _active_ppbuffer_memory_pcollector.add_level(size); + + { + // Start at the end, because removing near the end is cheaper. + LightMutexHolder holder(_lock); + size_t i = _deleted_buffers.size(); + while (i > 1) { + --i; + DeletedBuffer &buffer = _deleted_buffers[i]; + if (buffer._size == size) { + index = buffer._index; + mapped_ptr = buffer._mapped_pointer; + _glBindBuffer(target, buffer._index); + if (!_supports_buffer_storage && mapped_ptr != nullptr) { + // Need to unmap it before we can use it. + _glUnmapBuffer(target); + mapped_ptr = nullptr; + } + _deleted_buffers.erase(_deleted_buffers.begin() + i); + _inactive_ppbuffer_memory_pcollector.sub_level(size); + return; + } + } + } + + _glGenBuffers(1, &index); + _glBindBuffer(target, index); +#ifndef OPENGLES + if (_supports_buffer_storage) { + // Map persistently, we already use fences to synchronize access anyway. + _glBufferStorage(target, size, nullptr, GL_MAP_READ_BIT | + GL_CLIENT_STORAGE_BIT | GL_MAP_PERSISTENT_BIT); + mapped_ptr = _glMapBufferRange(target, 0, size, + GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT); + } else +#endif + { + //XXX does it matter what usage hint we pass here? None seem to fit well. + _glBufferData(target, size, nullptr, GL_DYNAMIC_DRAW); + mapped_ptr = nullptr; + } +} + +/** + * Called when the given buffer, as returned by bind_new_client_buffer, is no + * longer needed. + */ +void CLP(GraphicsStateGuardian):: +release_client_buffer(GLuint index, void *mapped_ptr, size_t size) { + // This may be called from any thread, so we can't make OpenGL calls here + // (like unmapping the buffer). + LightMutexHolder holder(_lock); + _deleted_buffers.push_back({index, 0, mapped_ptr, size}); + _active_ppbuffer_memory_pcollector.sub_level(size); + _inactive_ppbuffer_memory_pcollector.add_level(size); +} +#endif // !OPENGLES_1 + /** * Creates a new retained-mode representation of the given data, and returns a * newly-allocated VertexBufferContext pointer to reference it. It is the @@ -7567,7 +7704,6 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z, return true; } - /** * Copy the pixels within the indicated display region from the framebuffer * into system memory, not texture memory. Returns true on success, false on @@ -7577,7 +7713,8 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z, */ bool CLP(GraphicsStateGuardian):: framebuffer_copy_to_ram(Texture *tex, int view, int z, - const DisplayRegion *dr, const RenderBuffer &rb) { + const DisplayRegion *dr, const RenderBuffer &rb, + ScreenshotRequest *request) { nassertr(tex != nullptr && dr != nullptr, false); set_read_buffer(rb._buffer_type); glPixelStorei(GL_PACK_ALIGNMENT, 1); @@ -7868,12 +8005,23 @@ framebuffer_copy_to_ram(Texture *tex, int view, int z, } #endif // NDEBUG - unsigned char *image_ptr = tex->modify_ram_image(); - size_t image_size = tex->get_ram_image_size(); - if (z >= 0 || view > 0) { - image_size = tex->get_expected_ram_page_size(); + size_t image_size = tex->get_expected_ram_page_size(); + unsigned char *image_ptr = nullptr; +#ifndef OPENGLES_1 + GLuint pbo = 0; + void *mapped_ptr = nullptr; + if (request != nullptr) { + nassertr(z <= 0, false); + image_size *= tex->get_z_size(); + bind_new_client_buffer(pbo, mapped_ptr, GL_PIXEL_PACK_BUFFER, image_size); + } else +#endif + { + image_ptr = tex->modify_ram_image(); if (z >= 0) { image_ptr += z * image_size; + } else { + image_size = tex->get_ram_image_size(); } if (view > 0) { image_ptr += (view * tex->get_z_size()) * image_size; @@ -7884,9 +8032,22 @@ framebuffer_copy_to_ram(Texture *tex, int view, int z, glReadPixels(xo, yo, w, h, external_format, get_component_type(component_type), image_ptr); - // We may have to reverse the byte ordering of the image if GL didn't do it - // for us. +#ifndef OPENGLES_1 + if (request != nullptr) { + _glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); +#ifndef OPENGLES + if (_supports_buffer_storage) { + _glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT); + } +#endif + GLsync fence = _glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + _async_ram_copies.push_back({request, pbo, fence, external_format, + view, mapped_ptr, image_size}); + } else +#endif if (external_format == GL_RGBA || external_format == GL_RGB) { + // We may have to reverse the byte ordering of the image if GL didn't do it + // for us. PTA_uchar new_image; const unsigned char *result = fix_component_ordering(new_image, image_ptr, image_size, @@ -7896,10 +8057,114 @@ framebuffer_copy_to_ram(Texture *tex, int view, int z, } } +#ifdef OPENGLES_1 + if (request != nullptr) { + request->finish(); + } +#endif + report_my_gl_errors(); return true; } +/** + * Finishes all asynchronous framebuffer-copy-to-ram operations. + */ +void CLP(GraphicsStateGuardian):: +finish_async_framebuffer_ram_copies(bool force) { +#ifndef OPENGLES_1 + if (_async_ram_copies.empty()) { + return; + } + + //XXX having a fixed number of threads is not a great idea. We ought to have + // a common thread pool that is sized based on the available number of CPUs. +#ifdef HAVE_THREADS + AsyncTaskManager *task_mgr = AsyncTaskManager::get_global_ptr(); + static AsyncTaskChain *chain = task_mgr->make_task_chain("texture_download", 2, TP_low); +#endif + + PStatTimer timer(_copy_texture_finish_pcollector); + + if (force) { + // Just wait for the last fence, the rest must be complete too then. + PStatTimer timer(_wait_fence_pcollector); + GLsync fence = _async_ram_copies.back()._fence; + _glClientWaitSync(fence, 0, (GLuint64)-1); + } + + while (!_async_ram_copies.empty()) { + AsyncRamCopy © = _async_ram_copies.front(); + if (!force) { + GLenum result = _glClientWaitSync(copy._fence, 0, 0); + if (result != GL_ALREADY_SIGNALED && result != GL_CONDITION_SATISFIED) { + // Not yet done. The rest must not yet be done then, either. + break; + } + } + _glDeleteSync(copy._fence); + + GLuint pbo = copy._pbo; + int view = copy._view; + PT(ScreenshotRequest) request = std::move(copy._request); + GLuint external_format = copy._external_format; + void *mapped_ptr = copy._mapped_pointer; + size_t size = copy._size; + + if (mapped_ptr == nullptr) { + _glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); +#ifdef OPENGLES + // There is neither glMapBuffer nor persistent mapping in OpenGL ES + mapped_ptr = _glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, size, GL_MAP_READ_BIT); +#else + // If we get here in desktop GL, we must not have persistent mapping + nassertv(!_supports_buffer_storage); + mapped_ptr = _glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY); +#endif + } + + // Do the memcpy in the background, since it can be slow. + auto func = [=](AsyncTask *task) { + const unsigned char *result = (unsigned char *)mapped_ptr; + PTA_uchar new_image; + if (external_format == GL_RGBA || external_format == GL_RGB) { + // We may have to reverse the byte ordering of the image if GL didn't do + // it for us. + result = fix_component_ordering(new_image, result, size, + external_format, request->get_result()); + } + request->set_view_data(view, result); + + // Finishing can take a long time, release the client buffer first so it + // can be reused for the next screenshot. + this->release_client_buffer(pbo, mapped_ptr, size); + request->finish(); + return AsyncTask::DS_done; + }; +#ifdef HAVE_THREADS + // We assign a sort value based on the originating frame number, so that + // earlier frames will be processed before subsequent frames, but we don't + // make it unique for every frame, which would kill concurrency. + int frame_number = request->get_frame_number(); + chain->add(std::move(func), "screenshot", frame_number >> 3, -(frame_number & ((1 << 3) - 1))); +#else + func(nullptr); +#endif + + _async_ram_copies.pop_front(); + + // If there is 1 remaining, save it for next frame. This helps prevent an + // inconsistent frame rate when the number of fetched frames alternates + // between 0 and 2, which can settle into a stable feedback loop. + if (!force && _async_ram_copies.size() == 1) { + break; + } + } + + _glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); +#endif +} + #ifdef SUPPORT_FIXED_FUNCTION /** * diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index 911b182c31..f12803b12c 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -142,6 +142,7 @@ typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *a typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target); #ifndef OPENGLES_1 // GLSL shader functions @@ -231,6 +232,13 @@ typedef void (APIENTRYP PFNGLBUFFERSTORAGEPROC) (GLenum target, GLsizeiptr size, typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC) (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); typedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); typedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); +typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC) (GLenum condition, GLbitfield flags); +typedef GLboolean (APIENTRYP PFNGLISSYNCPROC) (GLsync sync); +typedef void (APIENTRYP PFNGLDELETESYNCPROC) (GLsync sync); +typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLGETINTEGER64VPROC) (GLenum pname, GLint64 *data); +typedef void (APIENTRYP PFNGLGETSYNCIVPROC) (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); #endif // OPENGLES_1 #ifndef OPENGLES typedef void (APIENTRYP PFNGLBINDTEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); @@ -253,7 +261,6 @@ typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64PROC) (GLuint index, GLuint64EXT typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VPROC) (GLuint index, const GLuint64EXT *v); typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VPROC) (GLuint index, GLenum pname, GLuint64EXT *params); typedef void *(APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access); -typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target); typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, void *data); #endif // OPENGLES #endif // __EDG__ @@ -353,6 +360,11 @@ public: virtual ShaderContext *prepare_shader(Shader *shader); virtual void release_shader(ShaderContext *sc); +#ifndef OPENGLES_1 + void bind_new_client_buffer(GLuint &index, void *&mapped_ptr, GLenum target, size_t size); + void release_client_buffer(GLuint index, void *mapped_ptr, size_t size); +#endif + void record_deleted_display_list(GLuint index); virtual VertexBufferContext *prepare_vertex_buffer(GeomVertexArrayData *data); @@ -403,7 +415,9 @@ public: virtual bool framebuffer_copy_to_texture (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb); virtual bool framebuffer_copy_to_ram - (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb); + (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb, + ScreenshotRequest *request); + void finish_async_framebuffer_ram_copies(bool force = false); #ifdef SUPPORT_FIXED_FUNCTION void apply_fog(Fog *fog); @@ -880,6 +894,7 @@ public: #ifdef OPENGLES PFNGLMAPBUFFERRANGEEXTPROC _glMapBufferRange; + PFNGLUNMAPBUFFEROESPROC _glUnmapBuffer; #else PFNGLMAPBUFFERRANGEPROC _glMapBufferRange; #endif @@ -891,6 +906,8 @@ public: bool _supports_buffer_storage; PFNGLBUFFERSTORAGEPROC _glBufferStorage; +#else + static const bool _supports_buffer_storage = false; #endif bool _supports_blend_equation_separate; @@ -1088,6 +1105,13 @@ public: PFNGLSHADERSTORAGEBLOCKBINDINGPROC _glShaderStorageBlockBinding; #endif // !OPENGLES +#ifndef OPENGLES_1 + PFNGLFENCESYNCPROC _glFenceSync; + PFNGLDELETESYNCPROC _glDeleteSync; + PFNGLCLIENTWAITSYNCPROC _glClientWaitSync; + PFNGLGETSYNCIVPROC _glGetSynciv; +#endif + GLenum _edge_clamp; GLenum _border_clamp; GLenum _mirror_repeat; @@ -1109,6 +1133,17 @@ public: DeletedNames _deleted_display_lists; DeletedNames _deleted_queries; +#ifndef OPENGLES_1 + struct DeletedBuffer { + GLuint _index; + int _age; + void *_mapped_pointer; + size_t _size; + }; + typedef pvector DeletedBuffers; + DeletedBuffers _deleted_buffers; +#endif + #ifndef OPENGLES_1 // Stores textures for which memory bariers should be issued. typedef pset TextureSet; @@ -1165,8 +1200,22 @@ public: FrameTiming *_current_frame_timing = nullptr; #endif + struct AsyncRamCopy { + PT(ScreenshotRequest) _request; + GLuint _pbo; + GLsync _fence; + GLuint _external_format; + int _view; + void *_mapped_pointer; + size_t _size; + }; + pdeque _async_ram_copies; + BufferResidencyTracker _renderbuffer_residency; + PStatCollector _active_ppbuffer_memory_pcollector; + PStatCollector _inactive_ppbuffer_memory_pcollector; + static PStatCollector _load_display_list_pcollector; static PStatCollector _primitive_batches_display_list_pcollector; static PStatCollector _vertices_display_list_pcollector; @@ -1177,6 +1226,8 @@ public: static PStatCollector _fbo_bind_pcollector; static PStatCollector _check_error_pcollector; static PStatCollector _check_residency_pcollector; + static PStatCollector _wait_fence_pcollector; + static PStatCollector _copy_texture_finish_pcollector; public: virtual TypeHandle get_type() const { diff --git a/panda/src/gsgbase/graphicsStateGuardianBase.h b/panda/src/gsgbase/graphicsStateGuardianBase.h index 19c7140024..dd144cb859 100644 --- a/panda/src/gsgbase/graphicsStateGuardianBase.h +++ b/panda/src/gsgbase/graphicsStateGuardianBase.h @@ -28,6 +28,7 @@ class RenderBuffer; class GraphicsWindow; class NodePath; class GraphicsOutputBase; +class ScreenshotRequest; class VertexBufferContext; class IndexBufferContext; @@ -220,7 +221,8 @@ public: virtual bool framebuffer_copy_to_texture (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb)=0; virtual bool framebuffer_copy_to_ram - (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb)=0; + (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb, + ScreenshotRequest *request = nullptr)=0; virtual CoordinateSystem get_internal_coordinate_system() const=0; diff --git a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx index 0e2af03bdd..17b7ac24d4 100644 --- a/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx +++ b/panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx @@ -1393,8 +1393,8 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z, */ bool TinyGraphicsStateGuardian:: framebuffer_copy_to_ram(Texture *tex, int view, int z, - const DisplayRegion *dr, - const RenderBuffer &rb) { + const DisplayRegion *dr, const RenderBuffer &rb, + ScreenshotRequest *request) { nassertr(tex != nullptr && dr != nullptr, false); int xo, yo, w, h; @@ -1465,6 +1465,9 @@ framebuffer_copy_to_ram(Texture *tex, int view, int z, fo += _c->zb->linesize / PSZB; } + if (request != nullptr) { + request->finish(); + } return true; } diff --git a/panda/src/tinydisplay/tinyGraphicsStateGuardian.h b/panda/src/tinydisplay/tinyGraphicsStateGuardian.h index c4ee0348ad..5f569fc1c5 100644 --- a/panda/src/tinydisplay/tinyGraphicsStateGuardian.h +++ b/panda/src/tinydisplay/tinyGraphicsStateGuardian.h @@ -78,7 +78,8 @@ public: virtual bool framebuffer_copy_to_texture (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb); virtual bool framebuffer_copy_to_ram - (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb); + (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb, + ScreenshotRequest *request); virtual void set_state_and_transform(const RenderState *state, const TransformState *transform); From 4734bb9debf54ec03f02d6e91895f8d55b70e7ab Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 26 Dec 2022 21:24:16 +0100 Subject: [PATCH 16/16] display: Fix missing includes --- panda/src/display/screenshotRequest.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/panda/src/display/screenshotRequest.h b/panda/src/display/screenshotRequest.h index 4d56cf73bf..68e5d17ab4 100644 --- a/panda/src/display/screenshotRequest.h +++ b/panda/src/display/screenshotRequest.h @@ -17,9 +17,11 @@ #include "pandabase.h" #include "asyncFuture.h" +#include "clockObject.h" #include "filename.h" #include "lightMutex.h" #include "pmap.h" +#include "texture.h" /** * A class representing an asynchronous request to save a screenshot.