Desktop: Use dirty rects for UI texture uploads#4305
Conversation
|
@TrueDoctor Would likely self merge soon. Not sure if this needs your review. |
There was a problem hiding this comment.
Code Review
This pull request refactors the CEF rendering pipeline by introducing a new View struct to manage texture uploads and state, replacing the previous FrameBufferRef implementation. It also updates RenderState::bind_ui_texture to optimize redundant texture bindings. Feedback on the changes highlights a bug in the dirty rectangle clamping logic within View::upload_frame_buffer when dealing with negative coordinates, which could lead to incorrect pixel copies, and suggests a robust intersection-based calculation to resolve it.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| let x = (rect.x.max(0) as u32).min(width); | ||
| let y = (rect.y.max(0) as u32).min(height); | ||
| let rect_width = (rect.width.max(0) as u32).min(width - x); | ||
| let rect_height = (rect.height.max(0) as u32).min(height - y); | ||
| if rect_width == 0 || rect_height == 0 { | ||
| continue; | ||
| } |
There was a problem hiding this comment.
The current clamping logic does not correctly handle cases where the dirty rectangle starts off-screen (e.g., rect.x < 0). If rect.x is negative, x is clamped to 0, but rect_width is not adjusted to account for the shifted start. This causes the copy operation to upload pixels outside the actual dirty region, and can even result in copying pixels when the dirty rect is entirely off-screen (e.g., rect.x = -100, rect.width = 50).
Using proper rectangle intersection ensures that only the visible portion of the dirty rectangle is uploaded, avoiding unnecessary GPU writes.
let x1 = rect.x.max(0).min(width as i32);
let y1 = rect.y.max(0).min(height as i32);
let x2 = (rect.x + rect.width).max(0).min(width as i32);
let y2 = (rect.y + rect.height).max(0).min(height as i32);
let rect_width = (x2 - x1).max(0) as u32;
let rect_height = (y2 - y1).max(0) as u32;
if rect_width == 0 || rect_height == 0 {
continue;
}
let x = x1 as u32;
let y = y1 as u32;There was a problem hiding this comment.
1 issue found across 9 files
Confidence score: 2/5
- In
desktop/src/cef/internal/render_handler.rs, the accelerated paint callback currently doesinfo.unwrap(), so a missing texture info (None) will panic and can crash the app during rendering; this is a concrete runtime stability risk in a user-facing path—handleNonelocally (log and return early) before merging.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="desktop/src/cef/internal/render_handler.rs">
<violation number="1" location="desktop/src/cef/internal/render_handler.rs:54">
P1: Accelerated paint can crash the app when no texture info is provided, because `info.unwrap()` panics in a callback path. Handling `None` locally (log + return) keeps rendering resilient.
(Based on your team's feedback about avoiding panicking Option/Result handling in application code.) [FEEDBACK_USED]</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| self.view.import_shared_texture(info.unwrap()); | ||
| self.event_handler.draw(&self.view) |
There was a problem hiding this comment.
P1: Accelerated paint can crash the app when no texture info is provided, because info.unwrap() panics in a callback path. Handling None locally (log + return) keeps rendering resilient.
(Based on your team's feedback about avoiding panicking Option/Result handling in application code.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At desktop/src/cef/internal/render_handler.rs, line 54:
<comment>Accelerated paint can crash the app when no texture info is provided, because `info.unwrap()` panics in a callback path. Handling `None` locally (log + return) keeps rendering resilient.
(Based on your team's feedback about avoiding panicking Option/Result handling in application code.) </comment>
<file context>
@@ -31,29 +33,26 @@ impl<H: CefEventHandler> ImplRenderHandler for RenderHandlerImpl<H> {
- }
-
- self.event_handler.draw_gpu(shared_handle);
+ self.view.import_shared_texture(info.unwrap());
+ self.event_handler.draw(&self.view)
}
</file context>
| self.view.import_shared_texture(info.unwrap()); | |
| self.event_handler.draw(&self.view) | |
| if let Some(info) = info { | |
| self.view.import_shared_texture(info); | |
| self.event_handler.draw(&self.view) | |
| } else { | |
| tracing::error!("Missing accelerated paint info"); | |
| } |
Slightly improves latency for large windows. Most noticeable on low end hardware with a "slow" CPU to GPU bus. Almost no effect on faster systems or systems with unified memory.
Mostly useful for Linux, we have ui acceleration disabled by default currently.
Wanted to do this for quite some time now.
Not as effective as I hoped and dose nothing about the still not optimal resize latency.
Still consider it a good idea to merge this and if only for the cleanup.