-
Notifications
You must be signed in to change notification settings - Fork 787
servo: android hardware rendering and refactor #10201
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
base: master
Are you sure you want to change the base?
Conversation
burhankhanzada
commented
Dec 4, 2025
- Extend vulkan implementation to support android
- Add cpu fallback non optimized path in vulkan for android emulator
- Refactor vulkan to its own module
- Introduce safegaurd for surface to auto unbind and rebind when drop
- Add docs
- Improve metal erros handling
…d update build dependencies.
…ew safe helper function.
…disable push constants" This reverts commit 57ac9c7.
| /// Flips an image buffer vertically in place. | ||
| /// | ||
| /// This is used to correct the orientation of textures read from OpenGL, which uses a | ||
| /// bottom-left origin, whereas other APIs (like WGPU/Metal/Vulkan) typically use top-left. | ||
| pub fn flip_image_vertically( | ||
| pixels: &mut [u8], | ||
| width: usize, | ||
| height: usize, | ||
| bytes_per_pixel: usize, | ||
| ) { | ||
| let stride = width * bytes_per_pixel; | ||
| let mut row_buffer = vec![0u8; stride]; | ||
| for y in 0..height / 2 { | ||
| let top_row_start = y * stride; | ||
| let bottom_row_start = (height - y - 1) * stride; | ||
|
|
||
| // Swap rows | ||
| row_buffer.copy_from_slice(&pixels[top_row_start..top_row_start + stride]); | ||
| pixels.copy_within(bottom_row_start..bottom_row_start + stride, top_row_start); | ||
| pixels[bottom_row_start..bottom_row_start + stride].copy_from_slice(&row_buffer); | ||
| } | ||
| } |
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 seems like something that should be happening on the gpu instead
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.
Hm yeah i agree but its duplicated code used in surfman_rendering_context and vulkcan cpu path thats why i exracted it.
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 is this fallback needed?
There should be three code paths:
- Rendering with
servo::SoftwareRenderingContext, which should not require flipping an image around (an OpenGL "feature"). - Rendering with OpenGL conversion to Vulkan texture, where the call to
blit_named_framebufferwill do the y-flip on the GPU. - Rendering with OpenGL and conversion to Metal Texture, where
create_flipped_texture_render()takes care of the flipping on the GPU.
| embedder_traits = { git = "https://github.com/servo/servo", rev = "158bf97e", features = ["baked-default-resources"] } | ||
| libservo = { git = "https://github.com/servo/servo", rev = "39d3fc6c" } | ||
| embedder_traits = { git = "https://github.com/servo/servo", rev = "39d3fc6c", features = ["baked-default-resources"] } | ||
| # not used in code but added for no need to set specific rev whenever run cargo update or freash install and without this its not building |
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 comment isn't very clear to me
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.
Ah its not related to vulkan or hardware rendering any way its just whenever i update servo revision to new version and run cargo update it update its internal dependencies to new version which conflict sometimes and making build to failed that why i added it here so all servo dependencies use this specific version.
| slint-build = { path = "../../api/rs/build" } | ||
|
|
||
| [target.'cfg(target_os = "linux")'.build-dependencies] | ||
| # need to add to for all host to generate gl_bindings for android |
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.
Same here
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.
Ah this need to build gl_binding so that android build have gl_bindings even on mac as host
|
|
||
| pub struct WPGPUTextureFromVulkan<'a> { | ||
| context: &'a GPURenderingContext, | ||
| } |
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's a pet peeve but I'm really not a fan of these (AI generated?) abstraction structs that don't really do a lot except obscure the code more than needed. It'd be great if you could refactor these into pure functions.
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.
Well its true mostly AI did this but i make did this intentionally to pass as less parametrs as possible to functions. But if you and @tronical think its bad approach then will happy to refactor as funntions. Also @expenses can you please review gl related code coz i try to understand but didn't get much its your expertize, i just modify to work it with android if your some suggestion there or way to optimize or reduce boilerplate.
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 agree with @expenses . I'll go one step further: This PR moves a lot of code around and it contains crucial changes to make certain scenarios work (including the emulator detection). The fact that it's all one big diff makes it very hard to review, and the individual commits are also mixing things.
For example, commit ebabf4f appears to contain two different changes that should be separate commits: (1) It switches the GL profile to GLES for Android (makes sense, I suppose this is what fixes the rendering with GL on the phone) and (2) adds emulator detection.
But I'm guessing here, as it's mixed - and it's better if the reviewers don't have to guess :)
So: It's very cool that you've gotten this to work. That's excellent, in fact. The next logical step is IMO to turn the individual things that you've done (fixing GLES profile, fixing emulator detection, moving code around to clean it up, adding insets) into either separate PRs or clean separate commits. As the PR stands, it has unrelated changes to the wgpu_texture example that are reverted later. That means the PR can only be squashed, which in turn destroys the ability to look at the changes individual and try to understand then when 9 months down the line we're looking at the code to debug something.
This is the next stage of development.
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.
Ah OK i understand will do this in new more small pull requestss so its easy to review and reason about
|
|
||
| pub struct WPGPUTextureFromVulkan<'a> { | ||
| context: &'a GPURenderingContext, | ||
| } |
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 agree with @expenses . I'll go one step further: This PR moves a lot of code around and it contains crucial changes to make certain scenarios work (including the emulator detection). The fact that it's all one big diff makes it very hard to review, and the individual commits are also mixing things.
For example, commit ebabf4f appears to contain two different changes that should be separate commits: (1) It switches the GL profile to GLES for Android (makes sense, I suppose this is what fixes the rendering with GL on the phone) and (2) adds emulator detection.
But I'm guessing here, as it's mixed - and it's better if the reviewers don't have to guess :)
So: It's very cool that you've gotten this to work. That's excellent, in fact. The next logical step is IMO to turn the individual things that you've done (fixing GLES profile, fixing emulator detection, moving code around to clean it up, adding insets) into either separate PRs or clean separate commits. As the PR stands, it has unrelated changes to the wgpu_texture example that are reverted later. That means the PR can only be squashed, which in turn destroys the ability to look at the changes individual and try to understand then when 9 months down the line we're looking at the code to debug something.
This is the next stage of development.
| /// Flips an image buffer vertically in place. | ||
| /// | ||
| /// This is used to correct the orientation of textures read from OpenGL, which uses a | ||
| /// bottom-left origin, whereas other APIs (like WGPU/Metal/Vulkan) typically use top-left. | ||
| pub fn flip_image_vertically( | ||
| pixels: &mut [u8], | ||
| width: usize, | ||
| height: usize, | ||
| bytes_per_pixel: usize, | ||
| ) { | ||
| let stride = width * bytes_per_pixel; | ||
| let mut row_buffer = vec![0u8; stride]; | ||
| for y in 0..height / 2 { | ||
| let top_row_start = y * stride; | ||
| let bottom_row_start = (height - y - 1) * stride; | ||
|
|
||
| // Swap rows | ||
| row_buffer.copy_from_slice(&pixels[top_row_start..top_row_start + stride]); | ||
| pixels.copy_within(bottom_row_start..bottom_row_start + stride, top_row_start); | ||
| pixels[bottom_row_start..bottom_row_start + stride].copy_from_slice(&row_buffer); | ||
| } | ||
| } |
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 is this fallback needed?
There should be three code paths:
- Rendering with
servo::SoftwareRenderingContext, which should not require flipping an image around (an OpenGL "feature"). - Rendering with OpenGL conversion to Vulkan texture, where the call to
blit_named_framebufferwill do the y-flip on the GPU. - Rendering with OpenGL and conversion to Metal Texture, where
create_flipped_texture_render()takes care of the flipping on the GPU.
|
|
||
| // Config copied from https://github.com/YaLTeR/bxt-rs/blob/9f621251b8ce5c2af00b67d2feab731e48d1dae9/build.rs. | ||
|
|
||
| let api = if target_os == "android" { Api::Gles2 } else { Api::Gl }; |
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.
In this code, the decision is "sort of" made at run-time by comparing strings. However, this can be done at compile-time, using either #[cfg(target_os = "android"] or using if cfg!(target_os = "android").
Given that either branch is compilable, I think this should be written like this:
let api = if cfg!(target_os = "android") { ... } else { ... };That said, I wonder if we can get away with just OpenGL ES 3.0 everywhere. On Android and Linux Embedded that'll be preferred. On Windows I thought servo is using angle, so that's also OpenGL ES.
@expenses I'm unsure, but is there perhaps a reason to use "Desktop GL" over GLES here?
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'm not too familiar with the different OpenGL versions. I believe that if we're only using the GLES feature set and nothing exclusive to Desktop GL then using GLES everywhere is fine.