Returning a PackedByteArray from a GDExtension class

Godot Version

4.3

Question

I am working on a GDExtension class that generates a custom texture that needs to be updated every frame. The texture is very large and I would like to minimize the amount of data copying that happens during the update. I have the texture internally stored in the private class member PackedByteArray pixel_data_ that is written to by the update logic and then updates the texture inside of C++ like this:

  void World::_process(double delta) {
    // Update visualization
    world_image_->set_data(Globals::kWorldWidth, Globals::kWorldHeight, false,
                           Image::FORMAT_R8, pixel_data_);
    world_texture_->update(world_image_);
  }

My understanding of Godot’s internal logic is that the call to Image::set_data() will only update the image’s internal data pointer to point to my new array without making a copy, and the call to ImageTexture::update() will only cause a single copy from the CPU to the GPU, which isn’t really avoidable.

In the interest of separating concerns, I am considering moving some of this update code to GDScript, but would like to do so in a way that doesn’t add extra overhead from copying data needlessly. If I instead turn this into a property:

PackedByteArray get_pixel_data() const { return pixel_data_; }

and do the update inside of GDScript:

func _process(_delta):
    update_visualization()

func update_visualization():
    world_image.set_data(Globals.kWorldWidth, Globals.kWorldHeight, false, Image.FORMAT_R8, world.get_pixel_data())
    world_texture.update(world_image)

will this still pass the data by reference, or will there be another copy made in here? Is there a way I can do this without making an extra copy?

I know the documentation for PackedByteArray has this to say:

Packed arrays are always passed by reference. To get a copy of an array that can be modified independently of the original array, use duplicate. This is not the case for built-in properties and methods. The returned packed array of these are a copies, and changing it will not affect the original value. To update a built-in property you need to modify the returned array, and then assign it to the property again.

This is a little confusing to me. It’s clear to me that PackedByteArrays passed within GDScript are passed by reference, but I’m not sure if an array returned from a GDExtension class would qualify as “built-in” under this definition.