From 896346b99f7b497f346ac34ba7c7a21f6af765eb Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 19 Dec 2022 16:22:33 +0100 Subject: [PATCH] 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