OSR OnPaint() provides partially rendered buffer

Having problems with building or using CEF's C/C++ APIs? This forum is here to help. Please do not post bug reports or feature requests here.

OSR OnPaint() provides partially rendered buffer

Postby cfx » Mon Mar 07, 2022 4:32 am

Hi everyone,

Note: this post is a copy of this issue, but I couldn't post here due to an account creation issue.

I’ve derived the cef_simple example to implement offscreen rendering in a restapi.
The webpage I’m trying to capture renders asynchronously, through some JS code. It renders one or several canvas, with a few html elements overlaid on top.
The JS code can notify me when all the canvas/html elements have been drawn/updated, which allows me to trigger a JS callback, which in turn raises a flag to tell my RenderHandler that on the next OnPaint() it needs to copy the provided buffer, and finally calls Invalidate(PET_VIEW) in order to trigger a repaint.

OnPaint() is indeed triggered, the buffer and the one dirty region have the same expected size (usually 1024x1024).

The content of the buffer might however be incorrect: the canvas and/or the html elements might not have been rendered, or the buffer can be partially filled with random noise.
It happens approx. 15% of the time, it looks like its linked to my server’s load.

I’ve tried wrapping my JS callback in requestAnimationFrame, but that does not seems to have any effect (which might actually be expected, since requestAnimationFrame is there to ensure some code is executed before the next repaint, while I’m trying to notify CEF that everything has been painted).

The only solution I found is to delay my JS callback with setTimeout(), but even with a 250ms delay I can still get some incorrect results (and obviously I would like to avoid such delay).

Is there something I need to do to ensure the buffer is completely repainted?


CEF versions : 97.1.9, 98.1.0, 98.1.19, 98.2.0, 98.2.1, 99.2.7 (will try some older versions).
Running in a Docker container, on Linux, with no GPU available.
Last edited by cfx on Wed Apr 06, 2022 10:19 am, edited 1 time in total.
cfx
Newbie
 
Posts: 4
Joined: Thu Feb 24, 2022 4:25 am

Re: OSE OnPaint() provides partially rendered buffer

Postby magreenblatt » Mon Mar 07, 2022 12:34 pm

Just to make sure I understand. Are you saying that calling Invalidate(PET_VIEW) results in OnPaint() with the expected size (e.g. 1024x1024) but the pixels inside that specified region sometimes contain random noise?

Does this also occur for calls to OnPaint() that are not triggered via Invalidate()?
magreenblatt
Site Admin
 
Posts: 12382
Joined: Fri May 29, 2009 6:57 pm

Re: OSR OnPaint() provides partially rendered buffer

Postby cfx » Wed Mar 30, 2022 11:52 am

Yes, sometimes it contains noise, but I believe it might just be a side effect of another problem.

In my standard workflow, I have a few (512x512) or (1024x1024) canvas stacked on top of each others, e.g.:
+------------+ y=0
| canvas 0 |
+------------+ y=1024
| canvas 1 |
+------------+ y=2048
| canvas 2 |
+------------+ y=3072
Each canvas is rendered asynchronously, but my code is able to notify me when the code that update each canvas has been executed.
When I've received the notifications for every canvas, I trigger an Invalidate(PET_VIEW), which effectively triggers OnPaint().

In my experiments, every OnPaint() that I receive cover the expected region, but if I look at the dirty rectangle, the results varies from one run to the other.

I might get a rectangle that covers the whole requested region, and everything has been correctly painted. This is the perfect behavior, I've got everything I need, I can close my browser.

Or I might get a rectangle in the middle of the requested region (e.g. from y=1024 to y=2048).
Or a rectangle that covers the requested region, but some parts have not been painted, e.g.:
+------------------------------------+
| canvas 0 correctly rendered
+------------------------------------+
| background page color, or
| some of html overlay that
| I have on top of my canvas,
| or random noise
+------------------------------------+
| canvas 2 correctly rendered
+------------------------------------+
If I let CEF continue, I will get other OnPaint() that will fill the blanks.
I can accumulate the dirty rectangle and effectively compose the requested region, but I don't really know when all the OnPaint() will have occurred.

So, at the moment, my theory is that every update to my canvas will post some kind of "Paint" event the event loop.
But for some reason, Invalidate(PET_VIEW) does not guarantee that all the events will be processed before the next call to OnPaint().
That would explain why OnPaint() might give me a partially rendered buffer, and why subsequent OnPaint() will finalize everything.

The random noise part could just be chromium not clearing the overall buffer before painting in it.

I'm about to try to implement a custom event loop that will hopefully allow me to trigger Invalidate(PET_VIEW) only when I detect that there's no more event to process (aka all the "Paint" events have been processed).
cfx
Newbie
 
Posts: 4
Joined: Thu Feb 24, 2022 4:25 am

Re: OSR OnPaint() provides partially rendered buffer

Postby cfx » Wed Apr 06, 2022 10:26 am

Well, a custom event loop is probably a dead-end, I can be notified when there's work to do with OnScheduleMessagePumpWork(), I can trigger the processing of some events with CefDoMessageLoopWork(), but I'm still unable to know when there's no more event to process.
cfx
Newbie
 
Posts: 4
Joined: Thu Feb 24, 2022 4:25 am

Re: OSR OnPaint() provides partially rendered buffer

Postby AndrewVW » Thu Aug 11, 2022 7:32 pm

Did you have any luck with this issue?

I'm in the same boat, with a similar use case:

--- Use case:
I use CEF as a "rendering engine" - I load a page and some data to do animations. I communicate with JS to seek to frames, and once the JS code has performed all of the actions, it calls a custom CEF-defined JS function to notify that it has completed the frame. After the notification, I use C++ to invalidate and take the first OnPaint to create a bitmap for a video frame. This is done with software rendering on cloud servers.

Similar to you, I've had to put in setTimeout() calls in my JS before calling the completed() function. If I don't, many frames have artifacts or missing elements. With a 250ms setTimeout, it renders most videos correctly, but I've had to go as high as 1000ms for complex animations and content.
---

Like you said, I think this is tied to load, and I think it's an underlying "issue" in Chromium itself. This page seems to confirm it:
https://chromium.googlesource.com/chrom ... c_works.md -- Check the scheduling section, it mentions slow rasters. It also mentions that in the single-threaded mode, it doesn't use a pending tree for full commits. (Note: I don't understand all of this, so I'm sure some I'm looking at the wrong parts often haha)

My theory is similar to yours: Chromium can't complete the required work in a single draw, so it completes over multiple draws.

I've been digging around in cc and components/viz to try to understand the call stack and find out if there's a variable somewhere that can be exposed that shows additional work is needed. It's quite a bit over my head, but it would be so beneficial to know that all compositor work is completed. Using setTimeout makes my renders slow and wasteful, and it still doesn't guarantee perfect output!
AndrewVW
Newbie
 
Posts: 1
Joined: Thu Aug 11, 2022 6:56 pm

Re: OSR OnPaint() provides partially rendered buffer

Postby cfx » Thu Nov 17, 2022 8:32 am

Hello Andrew,

Sorry for the late answer (for some reason I don't receive notifications).

Unfortunately no, I haven't progressed on this subject, and haven't really touched it since.
But I'm still hoping to revive that next year.
cfx
Newbie
 
Posts: 4
Joined: Thu Feb 24, 2022 4:25 am


Return to Support Forum

Who is online

Users browsing this forum: Google [Bot] and 25 guests