Skip to content
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

chore(draw_buf): add draw buf struct definition #4833

Merged
merged 2 commits into from
Dec 11, 2023

Conversation

XuNeo
Copy link
Collaborator

@XuNeo XuNeo commented Nov 20, 2023

Description of the feature or fix

Initial proposal of adding draw_buf struct.

Checkpoints

Be sure the following conventions are followed:

  • Follow the Styling guide
  • Prefer enums instead of macros. If inevitable to use defines export them with LV_EXPORT_CONST_INT(defined_value) right after the define.
  • In function arguments prefer type name[] declaration for array parameters instead of type * name
  • Use typed pointers instead of void * pointers
  • Do not malloc into a static or global variables. Instead declare the variable in lv_global_t structure in lv_global.h and mark the variable with (LV_GLOBAL_DEFAULT()->variable) when it's used. See a detailed description here.
  • Widget constructor must follow the lv_<widget_name>_create(lv_obj_t * parent) pattern.
  • Widget members function must start with lv_<module_name> and should receive lv_obj_t * as first argument which is a pointer to widget object itself.
  • structs should be used via an API and not modified directly via their elements.
  • struct APIs should follow the widgets' conventions. That is to receive a pointer to the struct as the first argument, and the prefix of the struct name should be used as the prefix of the function name too (e.g. lv_disp_set_default(lv_disp_t * disp))
  • Functions and structs which are not part of the public API must begin with underscore in order to mark them as "private".
  • Arguments must be named in H files too.
  • To register and use callbacks one of the following needs to be followed (see a detailed description here):
    • For both the registration function and the callback pass a pointer to a struct as the first argument. The struct must contain void * user_data field.
    • The last argument of the registration function must be void * user_data and the same user_data needs to be passed as the last argument of the callback.
    • Callback types not following these conventions should end with xcb_t.

@XuNeo
Copy link
Collaborator Author

XuNeo commented Nov 20, 2023

CI depends on #4832. Let ignore the CI failure for now.

Copy link
Member

@kisvegabor kisvegabor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feels quite elegant, however I can't tell how complicated will it be to use lv_draw_buf_t on all places, instead of using some simpler functions directly.

Anyway, let's go with it. I definitely see the added value of storing the stride and other data with the buffer, assuming it won't introduce too much boiler plate.

src/draw/lv_draw_buf.h Show resolved Hide resolved
src/draw/lv_draw_buf.h Outdated Show resolved Hide resolved
@XuNeo
Copy link
Collaborator Author

XuNeo commented Nov 21, 2023

however I can't tell how complicated will it be to use lv_draw_buf_t on all places, instead of using some simpler functions directly.

I'm planning to update some modules with latest draw buf interface these days. Let's see if more helpers can save us.

@XuNeo
Copy link
Collaborator Author

XuNeo commented Dec 5, 2023

I have included bin_decoder as an use case of draw_buf. Please help to review it d57ec2e

Thank you :)

src/draw/lv_draw_buf.h Outdated Show resolved Hide resolved
@FASTSHIFT
Copy link
Collaborator

During the process of debugging freetype, I found that the image cache provided by freetype cannot guarantee that the output bitmap meets the width alignment and address alignment requirements. We may need to manage the cache ourselves and use draw buf to uniformly manage the bitmap output by freetype.
The advantage of using the draw buf structure for management is that the image information is created together with the image, and no information will be lost in the context transfer, making it easier to track problems such as misalignment.

@kisvegabor
Copy link
Member

@FASTSHIFT
The idea is that the get_bitmap should return an address and stride aligned bitmap. I think it's even better to use our cache for FreeType, because we have more control over it.

@FASTSHIFT
Copy link
Collaborator

@FASTSHIFT The idea is that the get_bitmap should return an address and stride aligned bitmap. I think it's even better to use our cache for FreeType, because we have more control over it.

If get_bitmap returns the draw_buf structure, what do you think is its shortcoming? Is it taking up more memory and introducing construction overhead?

@kisvegabor
Copy link
Member

Yes, what we loose is the extra memory and the construction overhead for sure.
What we win is unified stride and address align handling and unified cache usage.

Signed-off-by: Xu Xingliang <xuxingliang@xiaomi.com>
@XuNeo
Copy link
Collaborator Author

XuNeo commented Dec 6, 2023

Hi Gabor,
I have updated the draw_buf related APIs, please help to review it.
Thank you.

@kisvegabor
Copy link
Member

I'm thinking about the stride parameter in lv_draw_buf_create. I understand that images can have any stride and those stride settings might not be supported by a draw unit, but do we really need to allow manually crafting draw_bufs with any stride value?

What is the advantage of providing full control over stride, instead of setting the stride automatically from the width and color format.

@XuNeo
Copy link
Collaborator Author

XuNeo commented Dec 7, 2023

I understand that images can have any stride and those stride settings might not be supported by a draw unit, but do we really need to allow manually crafting draw_bufs with any stride value?

We do need to create draw_bufs with different stride value, see the usage in bin_decoder

https://github.com/lvgl/lvgl/pull/4934/files#diff-fb2fd60d4b02948954b996a1c52438a1c6a51c9a2b61ffd7063f6d9594ad0d7dR949-R950

There's no reason to assume binary file has same stride as GPU needed, it could be already 128 byte aligned which also meets GPU(64Byte assume).

For PNG decoders, e.g. 100x100 image, the draw buffer alloced for directly decoded data is 400Byte. If GPU requires 64byte aligned stride, then a 448Byte stride draw buffer is alloced and lv_draw_buf_copy can process possibly by DMA2D.

For font with bitmap, we may also want to use draw buf. For example PXP(from NXP) doesn't need aligned stride.

So the conclusion is keep stride as open can easy lots of processing without hacking into draw buffer details.

@kisvegabor
Copy link
Member

So

  • it can happen that draw_bufs (a.k.a images) are not stride aligned.
  • the buffers allocated in draw_bufs are always address aligned
  • the images stored in flash might not be address aligned

So GPUs need to check for both address and stride before taking a task.

However:

  • Let's say we have a draw task for "Hello world". VG-Lite takes it, but when it gets the bitmap of the first letter, it will see that it's not stride aligned because some font engine returns a not stride aligned bitmap. It's already too late, as the draw task is already being executed, but VG-Lite couldn't make a good decide in the evaluate_cb
  • Let's say VG-Lite takes a seemingly innocent image draw task, which is actually PNG file. During drawing it will be decoded to a not stride aligned buffer. What to do now? Convert it to stride aligned? Or would it have been a better idea to leave it to PXP? (Maybe this already resolved because in the info_cb the draw_unit can set the stride in the header)

@XuNeo
Copy link
Collaborator Author

XuNeo commented Dec 8, 2023

So

  • it can happen that draw_bufs (a.k.a images) are not stride aligned.
  • the buffers allocated in draw_bufs are always address aligned
  • the images stored in flash might not be address aligned

Yes.

So GPUs need to check for both address and stride before taking a task.

No. The decoded image will always fulfill a draw task' requirement like stride alignment. It relies on post_process_cb for now.

@kisvegabor
Copy link
Member

No. The decoded image will always fulfill a draw task' requirement like stride alignment. It relies on post_process_cb for now.

I think this the catch. The stride alignment of SW render, VG-Lite, PXP, other? That's I believe having a global stride is cleaner.

@XuNeo
Copy link
Collaborator Author

XuNeo commented Dec 9, 2023

The stride alignment of SW render, VG-Lite, PXP, other? That's I believe having a global stride is cleaner.

SW render, VG-Lite, PXP, other have different alignment requirement, so it's not correct having a global stride is cleaner?

Aside from that, the stride parameter is a basic para for draw buf just like w, h. That's why I mentioned the PNG decoder case.

For PNG decoders, e.g. 100x100 image, the draw buffer alloced for directly decoded data is 400Byte. If GPU requires 64byte aligned stride, then a 448Byte stride draw buffer is alloced and lv_draw_buf_copy can process possibly by DMA2D.

Signed-off-by: Xu Xingliang <xuxingliang@xiaomi.com>
@XuNeo
Copy link
Collaborator Author

XuNeo commented Dec 9, 2023

  • During drawing it will be decoded to a not stride aligned buffer.

I hope the code here can explain it better.

https://github.com/lvgl/lvgl/pull/4968/files#diff-e78cbe701293b586a1376e5917219514c197244669c224744ee13af8f7055e8fR144-R157

@kisvegabor
Copy link
Member

For PNG decoders, e.g. 100x100 image, the draw buffer alloced for directly decoded data is 400Byte. If GPU requires 64byte aligned stride, then a 448Byte stride draw buffer is alloced and lv_draw_buf_copy can process possibly by DMA2D.

I understand that the buffers can be copied with on-the-fly stride alignment, but

  1. it require an other buffer -> extra memory. If it was a wallpaper then it could mean MBs of RAM
  2. it requires time, especially important as usually the buffers are in external RAM

It would be nice to have solution by design, and keep the buffer copy as a fallback for very special situation.

In my head I still have the "no room for failure" policy. If we choose a common stride which works for all it can't be messed up.

There are 2 places where it really matter and not trivial:

  1. image decoders: it might be hard to make e.g. libpng return a stride aligned image. However decoder_info_cb can tell in advance what the stride will be so the draw_units can decide if it's ok for them or not. Maybe a draw_unit will accept a not stride aligned image too because it knows that will use a buffer_copy internally. But it shouldn't be the default assumption because of the above mention memory and time reasons.
  2. font bitmaps: we can either
    1. return the bitmap as it is and let the draw_unit make the stride alignment
    2. returned stride aligned bitmap

Regarding fonts, now we have ii. implemented, but if we have a simple buf_copy to align stride, i. would be better as we have smaller bitmaps here and some draw units (e.g. SW and PXP) can use the bitmaps without any alignment.

For all other things, like snapshot buffer, canvas buffer, layer buffers, etc, we can use a stride which works for all to not put any limitation on the compatible draw_units.

@kisvegabor
Copy link
Member

I merge it now as blocks a lot of other PR and we can improve it later.

@kisvegabor kisvegabor merged commit 39264bc into lvgl:master Dec 11, 2023
15 checks passed
@XuNeo XuNeo deleted the add-draw-buf branch December 17, 2023 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants