-
-
Notifications
You must be signed in to change notification settings - Fork 21.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add texture_2d_update_partial
method
#75892
Add texture_2d_update_partial
method
#75892
Conversation
servers/rendering_server.h
Outdated
@@ -107,6 +107,7 @@ class RenderingServer : public Object { | |||
virtual RID texture_proxy_create(RID p_base) = 0; | |||
|
|||
virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) = 0; | |||
virtual void texture_2d_update_partial(RID p_texture, const Ref<Image> &p_sub_image, int p_dst_x, int p_dst_y, int p_mipmap = 0, int p_layer = 0) = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Performance: for cases where only a part of the source image are uploaded, it would be preferable if the method took a sub-region of the source image, rather than forcing to first instantiate another and copy pixels from the source image just to be passed to this function.
Like it was in Godot 3: https://docs.godotengine.org/en/3.6/classes/class_visualserver.html#class-visualserver-method-texture-set-data-partial
Though in Godot 4 the signature could be shortened using Rect2i and Vector2i.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would be preferable if the method took a sub-region of the source image, rather than forcing to first instantiate and copy it from the source image just to be passed to this function.
I don't think there might be a big performance gap, and even if you do that, you're just calling Image.get_region() inside the method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But why is it necessary to even do that? Is it a requirement of Vulkan/OpenGL? I think OpenGL required that, but not sure about Vulkan.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure, but I think the current approach is more in line with function names.
Moreover, Image.get_data() must be called internally anyway. This method produces a complete copy operation, and calling this function may be expensive if the subimage is not obtained first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would tend to agree with Zylann here, I would add arguments for source coordinates. Godot 3.x had the same and the currently unimplemented stub also has signature like that:
godot/drivers/gles3/storage/texture_storage.cpp
Lines 1276 to 1278 in 578ca94
void TextureStorage::texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, int p_layer) { | |
ERR_PRINT("Not implemented yet, sorry :("); | |
} |
Implementing that can be slightly more difficult as you also have to calculate image stride and offsets, but it should be worth it in performance. My ultimate version (no need to implement) would probably take a PackedByteArray instead of Image, similar to high-performance mesh update methods like mesh_surface_update_vertex_region
.
Also note that Image.get_data()
returns a Vector, which is a copy-on-write container. It doesn't copy the contents until they are modified, which in this case is not needed.
Also, thanks for doing this! This will be a very useful method to many.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would tend to agree with Zylann here, I would add arguments for source coordinates. Godot 3.x had the same and the currently unimplemented stub also has signature like that:
godot/drivers/gles3/storage/texture_storage.cpp
Lines 1276 to 1278 in 578ca94
void TextureStorage::texture_set_data_partial(RID p_texture, const Ref<Image> &p_image, int src_x, int src_y, int src_w, int src_h, int dst_x, int dst_y, int p_dst_mip, int p_layer) { ERR_PRINT("Not implemented yet, sorry :("); } Implementing that can be slightly more difficult as you also have to calculate image stride and offsets, but it should be worth it in performance. My ultimate version (no need to implement) would probably take a PackedByteArray instead of Image, similar to high-performance mesh update methods like
mesh_surface_update_vertex_region
.Also note that
Image.get_data()
returns a Vector, which is a copy-on-write container. It doesn't copy the contents until they are modified, which in this case is not needed.Also, thanks for doing this! This will be a very useful method to many.
That makes sense, I've updated the api.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
virtual void texture_2d_update_partial(RID p_texture, const Ref<Image> &p_src_image, Rect2i p_region, Vector2i p_dst, int p_mipmap = 0, int p_layer = 0)
I think the region
should still be kept as small as possible. For example, rgb8
is rendered as rgba8
in vulkan, a Image.convert()
will be called inside, which is a very expensive operation.
a26e04b
to
75807bc
Compare
75807bc
to
28523cf
Compare
texture_2d_update_ partial
method
97aa269
to
2f54340
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall this looks great and is a much needed missing feature in Godot 4.0. I left a few comments on things that need to be fixed before merging though.
Please let me know if you have any questions or if anything doesn't make sense
@@ -812,6 +812,41 @@ void TextureStorage::texture_2d_update(RID p_texture, const Ref<Image> &p_image, | |||
#endif | |||
} | |||
|
|||
void TextureStorage::texture_2d_update_partial(RID p_texture, const Ref<Image> &p_src_image, Rect2i p_region, Vector2i p_dst, int p_mipmap, int p_layer) { | |||
ERR_FAIL_COND(p_src_image.is_null() || p_src_image->is_empty()); | |||
ERR_FAIL_COND_MSG(p_src_image->is_compressed(), "Compressed image is not supported"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ERR_FAIL_COND_MSG(p_src_image->is_compressed(), "Compressed image is not supported"); | |
ERR_FAIL_COND_MSG(p_src_image->is_compressed(), "Compressed image is not supported for partial texture updates. Please use an uncompressed image instead"); |
On this note, it looks like the check is missing for if p_texture
is compressed. We should also fail if p_texture
is a compressed format. Alternatively, we can add support for compressed formats as we have in 3.x
glBindTexture(texture->target, texture->tex_id); | ||
|
||
int pixel_size = img->get_format_pixel_size(img->get_format()); | ||
int ofs = (p_region.position.x + p_region.position.y * p_src_image->get_width()) * pixel_size; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't going to work if the src_image width is different from region width. You will end up getting the wrong data for all pixels except for the first row because openGL is going to pull from the data array without regard to the region size. in other words, it expects that you will have packed the data into an array already.
We have to give OpenGL a tightly packed array. Which means the size of the region given to glTexSubImage2D
must match the size of the image used to blit.
Accordingly, we need to handle the case where p_region size does not match p_src_image size. We can do that the same way we did in 3.x. I.e. if the region size is not the same as the src_image size, then we copy the partial rect out of the src image and use that. If the region size matches, then we can use it directly with no copy. Performance-minded uses can thus always provide the correctly-sized image to avoid the expensive copy.
if (src_format == Image::FORMAT_RGB8) { | ||
validated = p_src_image->get_region(p_region); | ||
validated->convert(Image::FORMAT_RGBA8); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you converting to an alpha format? If it is to enforce 4-byte alignment, see how _texture_update()
handles it. You should be able to copy that approach directly.
We should really avoid doing a full texture convert in such cases.
2f54340
to
f66dba1
Compare
Needs a rebase to pass CI Edit: I'm sorry it seems the CI was not fixed, waiting for a new fix |
f66dba1
to
0f35461
Compare
ecae02b
to
d441469
Compare
doc/classes/RenderingDevice.xml
Outdated
<param index="5" name="layer" type="int" default="0" /> | ||
<param index="6" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" default="7" /> | ||
<description> | ||
</description> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs description.
Same below.
Not sure what is the status of this PR, but I can confirm that the feature works correctly (even if implementation isn't perfect apparently?). |
texture_2d_update_ partial
methodtexture_2d_update_partial
method
d97c49b
to
cd1a6ec
Compare
cd1a6ec
to
ba3fb66
Compare
Why close? |
Because of some operational mistakes, I have resubmitted a pr.#80164 |
Add texture_2d_update_partial method for RenderingServer.
Compressed textures are not supported, they are usually not present in use cases for this function.
The gles3 backend does not have a corresponding implementation (texture_set_data_partial() is called inside but it has been not implemented).edit: Now gles3 backend is available, removed texture_set_data_partial() which was not implemented.
Bugsquad edit: This closes #65762.