Open Bug 1889288 (wr-hdr) Opened 2 years ago Updated 21 days ago

[meta] [project] Implement WR functionality needed for HDR

Categories

(Core :: Graphics: WebRender, task)

task

Tracking

()

People

(Reporter: gw, Unassigned)

References

(Depends on 2 open bugs, Blocks 1 open bug)

Details

(Keywords: meta)

No description provided.
Depends on: 1716093
Rank: 3
Severity: -- → S3
Depends on: HDR

Suggestion: Priority Upgrade to P2

I think we need to upgrade the Gecko backbuffer HDR support to an S2 with the new situation:

  • This is a bedrock HDR item that enables everything else (AVIF HDR, JXL HDR, VIDEO HDR, Canvas2D HDR supported by Chrome/Edge, and WebGPU HDR supported by Chrome/Edge/Safari)
  • Boom of MiniLED and OLED desktops, laptops and mobiles
  • Late 2024 release of Chrome/Edge 129+ WebGPU HDR (enabled by default, no flags)
  • March 2025 release of Safari Tech Preview 215+ WebGPU HDR (not behind any feature flags)

For developer testing, stick to any HDR display that can peak brightly (1000-1500nit minimum) such as MacBook MiniLED displays or the boom of PC desktop gaming monitors with HDR (MiniLED or OLED). Avoid the boring 400-nit HDR shovelware junk hardware that doesn't make HDR look impressive.

IMPORTANT: HDR was crap for a long time, especially if you bought common edgelit LCDs (no local dimming). As a test suite, HDR usually looks crap unless your monitor is either MiniLED or OLED. Now with the boom of sub-500 models (there were 240Hz OLEDs for Boxing Day for only 499) and a few MiniLED desktop monitors hit near-budget-pricing.

HDR test suites include:

The first two doesn't need flags to work in Chrome(129+)/Edge(129+)/Brave(129+)/Safari(Tech215+)

Also, from what I checked, it looks like work can proceed on #1889288 without waiting for DependsOn #1716093

Depends on: 1716093
Depends on: HDR
Blocks: wr-projects

That's incorrect. On the Windows platform, this bugzilla item does not depend on those items.

It should be:

Blocks: 1716093
Blocks: HDR
Blocks: wr-projects

(It's the actual converse. Item HDR depends on this item, NOT vice versa, if I read Mozilla's glossary terms)

I spent a few hours untangling dependency trees.

My dependency-tree disentanglement commentary: https://bugzilla.mozilla.org/show_bug.cgi?id=1539685#c34

The most important conclusion is that everything seems to depend on this:

  • Basically, the entire Gecko engine (framebuffer of Gecko) needing to support HDR, before you can composite HDR stuff into it (VIDEO buffers, WebGPU HDR buffers, HDR AVIF images, HDR JXL images, and other HDR pixels). Basically whenever the screen is in HDR mode, Gecko using a 48-bit final-compositor HDR framebuffer instead of 24-bit RGB framebuffer. Children elements may be in various colorspaces (including RGB) but the final window rectangle (framebuffer of the viewport) is all HDR 48-bit pixels in Chrome(129+)/Edge(129+)/Brave(129+)/Safari(TechPreview215+) -- because that essentially handles things this way to do high-performance HDR compositing, even doing SDR graphics within the HDR framebuffer (e.g. the text parts of a HDR page).

Are there any tracking items about this other than #1889288?

If not, I declare #1889288 this very item. Implement this 48-bit compositing framebuffer in Gecko is the goal of gecko WR capability for HDR.

Question -- How does FireFox implement HDR in VIDEO? Is the whole browser window a 64-bit framebuffer (48-bit RGB + 16-bit alpha using rgba-float16 pixel format, using half-precision floats to represent HDR pixels), or is it a video overlay?

Yes, 64-bit per pixel for the entire application is absolutely bonkers

  • But that's what Chromium and WebKit codebases now do for HDR, even during compositing the RGB pixels.
  • But it unblocks a lot of issues in the giant 6-year-old HDR meta tracking entry
  • Make it MUCH easier to bugfix/debug a lot of glitches.

Kinda tantamount to a 32-bit to 64-bit port, but at the graphics framebuffer level, not the code level.

For the ones who asked about 10-12bit HDR displays; good question (I got asked offline)

....What I'm witnessing the operating systems do at the OS layer (Windows/MacOS/Wayland/iDevices) = 16bit on-GPU application framebuffer.

GPU Layer: Final tonemap / downconverts from 16bit to whatever bit depth the cable and display HDR supports needs, which may be as little as 10-12bits.

This allows faster compositing (SDR+HDR) with less compute and without color banding (rounding errors) and simpler power-of-2 maths. Many compositing layers (filters/transparencies) can create color banding, so the extra bits is good at the internal OS level, even though it's pushed out of the HDMI/DP port at a lower bitdepth. Kind of like the color dimension of supersampled AA.

See Also: → 1972744

The current plan within the graphics: color management team (which I lead) is to promote surfaces reliably (this means they become native compositor surfaces), and specify colorspace on them (so that lets us honor the color space of the surface), and pixel format (which lets us send things like P010 video which is basically HDR10 Rec2100PQ in a specific YUV planar format, and also RGB10A2 Rec2100PQ which again is HDR10). This is the only way to avoid regressing performance on existing benchmarks, since compositing into RGBA16F render targets uses twice the video memory bandwidth.

After we get video and canvas on the screen as native compositor surfaces, we can look at upgrading all of WebRender and pieces of Gecko (primarily the pieces that translate colors to send to WebRender) to process HDR content using the scRGB colorspace (aka srgb-linear extended) and pixel format (RGBA16F).

There are several good options for WebRender's render targets to use:

  • Linear formats:
    • RGBA16F in scRGB - this format is also called things like srgb-linear-extended, it has a value range from roughly -0.5 to +50.0, as a practical this means it can only be represented in RGBA16F pixel format. Blending math is linear, so it changes how the web looks where transparent images are overlapping eachother, which is a meaningful change and something we have to consider carefully, but is likely unavoidable in the future. This format is supported by the Windows compositor, Mac compositor and all others.
    • RGBA16F in rec2100-linear - basically equivalent to scRGB, but with the Rec2020 primaries, which means all values are >= 0 for all known web content, no advantages over scRGB but this is more compatible with the following formats so it's worth thinking about. This format is supported by the Mac compositor.
    • RGBA16F in display-p3-linear-extended - basically we can do stuff with display P3 rather than Rec2020/Rec2100 if we want to. This format is supported by the Mac compositor.
    • RGB10A2 in rec2020-linear - basically the same as above but with limited dynamic range (0..1 values), this is a poor choice of format for handling transparency, so only really suitable for opaque image data.
    • RGBA10A2 in display-p3-linear - similar to rec2020-linear. This format is supported by the Mac compositor.
    • RGBA8 in rec2020-linear - somewhat degraded quality on gradients (less precision on dark colors than sRGB even), this is a useful fallback for cases where performance is more important than quality, and may be suitable for text.
    • RGBA8 in display-p3-linear - much the same as rec2100-linear. This format is supported by the Mac compositor.
  • sRGB transfer function formats:
    • RGBA8 in sRGB - this is the conventional format of the web when content does not otherwise indicate the intended colorspace, for example canvas elements default to srgb, SVG defaults to srgb, legacy color specifiers like #123456 are assumed to be in srgb unless they specify a colorspace such as p3 explicitly. Blending between images on the web has conventionally been assumed to be using this transfer function, so there are some visual artifacts when blending elements with certain blend functions and these artifacts are expected by convention (broadly speaking we already knew it was wrong to do it this way from the beginning of graphics on the web, the math should be done with linear color values, not sRGB transfer function). This format is supported by the Windows compositor and Mac compositor.
    • RGBA8 in display-p3 - this is the conventional format on Mac, the color primaries are a bit different than sRGB but all other details match. This format is supported by the Mac compositor.
    • RGBA8 in rec2020 - this is the same as sRGB in terms of transfer function but uses the rec2020 primaries, it has a slightly wider gamut than display-p3. This format is supported by the Windows compositor and Mac compositor.
  • PQ transfer function formats:
    • RGB10A2 in rec2100-pq - this is otherwise known as HDR10, it uses the PQ transfer function which maps the peak value to 10000 nits, with significant curvature (but not the same as sRGB piecewise gamma curve). Performing blending on colors is basically new territory, I don't know if anyone has done it, it might look different than blending sRGB colors.
    • RGBA8 in rec2100-pq - this would technically be 'HDR8' if such a standard existed, using the same PQ transfer function, we could technically use this as a rendering target format for high performance but lower quality images (e.g. maybe for compositing text).

The main advantage of picking one colorspace is that we don't have to apply a color matrix as often when compositing elements into a framebuffer, and similarly avoiding having transfer functions is very appealing, so certain combinations of these work well together (e.g. RGBA16F rec2100-linear with optimized cases for RGB10A2 rec2020-linear and RGBA8 rec2020-linear), if we use sRGB transfer function (rec2020, display-p3, srgb) it's consistent with how blending has been performed in the web in the past, but not technically correct, so there's a question of preserving intent.

There is some merit to experimenting with rendering the web entirely in rec2100-pq and seeing how that looks, since it would follow all of the existing expectations throughout all of the standards and at least the opaque elements would be properly HDR10 quality, but obviously blending would look different than sRGB or scRGB and that may be unavoidable.

If the approach is composing individual surfaces and sending them to the Windows DWM for display (as opposed to one big surface composed in Gecko), there are some Windows composition drawbacks to be aware of:

  • Transparency is not supported on HDR surfaces. (As soon as you set AlphaMode.Premultiplied on a D2D swapchain, it clamps to SDR.) The compositor just won't blend them with underlying surfaces for you. Either would have to do it manually by sampling underlying surfaces and accounting for their geometry, or just present one big surface.
  • HDR surfaces require a minimum size and refresh rate. (274x274 pixels and 24Hz in my experimentation) or DWM will clamp the surface to SDR. Apparently a deliberate design decision for power consumption reasons, etc.
  • For performance, Microsoft doesn't recommend more than about 4 SwapChainPanels in any given WinUI 3 app; I'd give this guidance some thought even outside of a WinUI3 context.

(In reply to Nathan Bryant from comment #8)

If the approach is composing individual surfaces and sending them to the Windows DWM for display (as opposed to one big surface composed in Gecko), there are some Windows composition drawbacks to be aware of:

We already use around 10-30 surfaces per window, just turn on gfx.webrender.debug.picture-caching=true in about:config to see what I mean, it's particularly obvious with the sidebar enabled or when browsing websites like Twitch or the Element matrix client that have several scrolling layers; even Bugzilla has several.

  • Transparency is not supported on HDR surfaces. (As soon as you set AlphaMode.Premultiplied on a D2D swapchain, it clamps to SDR.) The compositor just won't blend them with underlying surfaces for you. Either would have to do it manually by sampling underlying surfaces and accounting for their geometry, or just present one big surface.

This seems strange and may need more experimentation to verify because other user agents are already using HDR windows with rounded corners, perhaps this depends on the pixel format (RGB10A2? RGBA16F?) and perhaps it works without premultiplied alpha? We don't use D2D for swapchains, it's all D3D11, so if this is a D2D specific limitation it isn't very relevant.

  • HDR surfaces require a minimum size and refresh rate. (274x274 pixels and 24Hz in my experimentation) or DWM will clamp the surface to SDR. Apparently a deliberate design decision for power consumption reasons, etc.

Do you mean that DWM automatically switches to HDR mode under some circumstances? I had assumed you have to toggle it on manually in display settings? Is this related to the laptop displays that support HDR video but not HDR desktop mode, where they just max out the backlight and Windows provides some manual calibration steps to carry out to get it to look right?

  • For performance, Microsoft doesn't recommend more than about 4 SwapChainPanels in any given WinUI 3 app; I'd give this guidance some thought even outside of a WinUI3 context.

With the layer compositor (gfx.webrender.layer-compositor=true) the expectation is that the whole browser window will only use a single swapchain for regular web content, plus up to 3 additional swapchains for videos and canvas. This somewhat runs counter to the promoted surfaces approach I mentioned above in comment #7, and deeper changes may be needed to make the layer compositor work with HDR pixel formats.

Flags: needinfo?(nbryant)

(In reply to Ashley Hale [:ahale] from comment #9)

other user agents are already using HDR windows with rounded corners

I might like to see an example to make sure we're talking about the same thing (e.g. not just title bars.) Rounded corners are standard Win11 design style (CornerRadius=4 is the MS standard to be specific). Title bars or not, this is typically done with an SDR layer that might be sitting on top of an HDR layer, which is not the same scenario as what I'm talking about.

My experimentation has been limited to R16G16B16A16_Float because D2D supports a much more limited set of formats than exposed by D3D.

Do you mean that DWM automatically switches to HDR mode under some circumstances?

No, I mean that Win11 (as of 24H2) DWM, when running in HDR mode, when you feed it a R16G16B16A16_Float swapchain that is either too small or not actively refreshed at least 24Hz (meaning not just .Present calls, you also need BeginDraw-->some actual calls -->EndDraw-->Present or DWM will classify the surface as "idle") will be clamped to SDR. The visual effect is that you get HDR for some few milliseconds and then the brightness clamps to the SDR whitepoint (typically 80-200 nits depending on the slider in Settings.) I am being told that both of these behaviors are design decisions related to power savings / they were opinionated enough to think that small surfaces weren't worth HDR. You can see this in action if you clone my repo (it's a small, toy C# project, not a lot of code) and comment out the body of CalibrationDisplay.StartRenderLoop, and/or disable my workaround logic that puts a hard minimum on the surface size (just go back to the commit from before that was introduced would be the easiest.)

With the layer compositor (gfx.webrender.layer-compositor=true) the expectation is that the whole browser window

It sounds like you're on a reasonably good path then, as long as this means that e.g. gain-mapped JPEGs will be in the "single swapchain." There will still be limitations will be on canvas size, and I don't know how your approach compares to Chromium's in that area. I think where you'll end up is that gfx.webrender.layer-compositor will need to be true for HDR on Windows, at least with support for full CSS alpha semantics etc.

Flags: needinfo?(nbryant)

Oops. CornerRadius=6!

Apologies for the half-baked info. Sure seemed fully baked; testing was consistently showing problems with HDR swapchains where SDR worked fine. But after a deep dive, the fundamental issue on Windows is that Present() is asynchronous, and so there's no foolproof way to know that your swapchain is ready to Commit() to the DComp visual tree. Workaround is to render e.g. 3x on a 2-buffer swapchain on initial setup. This has always been the case but it appears that HDR swapchains are much more vulnerable to the issue and will start to behave in confusing and misleading ways.

Apologies for the half-baked info. Sure seemed fully baked; testing was consistently showing problems with HDR swapchains where SDR worked fine. But after a deep dive, the fundamental issue on Windows is that Present() is asynchronous, and so there's no foolproof way to know that your swapchain is ready to Commit() to the DComp visual tree. Workaround is to render e.g. 3x on a 2-buffer swapchain on initial setup. This has always been the case but it appears that HDR swapchains are much more vulnerable to the issue and will start to behave in confusing and misleading ways.

Can you give citations?

  1. The temporal dimension problems (synchronousness) hasn't had anything to do with color dimension problems (HDR vs SDR). Are you telling me there's a framework that behave differently synchronously in SDR vs HDR? I have not encounted this.

  2. You're talking to a me -- a person who successfully achieved synchronous Present() on Microsoft Windows platforms through various beam racing (raster interrupt style) hacks on Radeon/GeForces/Intel GPUs at https://www.pouet.net/topic.php?which=11422&page=2
    I actually helped RTSS add Scanline Sync, and helped SpecialK app add Latent Sync, by simulating a low-latency VSYNC ON via tearingless VSYNC OFF (by raster-steering the tearline offscreen), and a few emulators do sub-refresh latency via precision synchronization methods.

Obviously, this is not needed for FireFox, but you can do back-ended frame presentation or front-ended frame presentation (end of blanking interval, versus beginning of blanking interval). I am also familiar with the latency tradeoff versus fluidity tradeoff, given that one full refresh cycle of jitter can be allowed in Chromium engines unlike FireFox, based on my old 2013 browser tests of 120Hz-browser-stutters at https://blurbusters.com/blur-busters-120hz-web-browser-tests/

You can even test Chromium versus FireFox in Microsoft Windows using https://beta.testufo.com/animation-time-graph#GLOBAL.easteregg=1 .... Adjusting delay will add a busyloop. Chrome keeps running smoothly during longer busyloops than FireFox does, according to stutters (in the graph scrolling + frameskipping indicator). Runs perfectly identically smoothly in Chrome in both SDR and HDR mode -- make sure you turn on Chrome HDR support at testufo.com/hdr test.

My experience is that SDR vs HDR is completely irrelevant to the Present() synchronousness on Windows.

Cheers,
Me, An Expert in Synchronous Present()

Part 2/2

The time-phase of when you Present() relative to the VBI, will determine how sensitive it is to stutters. Chrome does this completely perfectly (and I can even in javascript self-detect when the browser stutters, when running in WINDOWS + CHROME + single monitor --

As you can see TestUFO automatically detects stutters fairly accurately in javascript but only when running in Chromium Browsers on Windows platforms on the primary monitor (nothing running/animating on secondary monitor). While newer versions of windows 11 does different-Hz multimonitor much better, let's isolate to primary monitor for simplicity.

The way Chrome succeeds on successful pseudo-synchronous Present() by historical precedent, as proven by (A) My TestUFO's successful ability IN JAVASCRIPT to self-detect stutters in Chrome on Windows platforms, and (B) Chrome's ability to avoid stuttering until a busyloop consumes 90% of a refresh cycle -- as proven by adjusting the delay numbbers in my TestUFO https://beta.testufo.com/animation-time-graph#GLOBAL.easteregg=1

Also, most graphics drivers unblocks Present() at the beginning of VBI (aka after scanline #1080 of a 1080p is scanned out of a GPU port -- a 1920s TVs and a 2020s DisplayPort still raster scansout the same way. left-to-right, top-to-bottom pixel delivery -- if you are familiar with CRT tubes, you know what I am talking about).

Some software modifies this to create a software-based swap chain that overrides this behavior slightly, by doing things like my open-source refresh rate calculator: https://github.com/blurbusters/RefreshRateCalculator (I released this under Apache 2.0 license), and adjusting the phase of Present() -- aka a simulated raster poll -- relative to the unblock timings, to give you more precision control over when you present the frame, to give you things like a better/worse latency-vs-fluidity tradeoff.

Being that said, some vendors now use algorithms like this to produce a different time-offset frame presentation (e.g. a fixed timeoffset relative to VBI) for various purposes such as reducing latency without beamracing more than once per Hz (the scanline when to Present() the frame at). During VSYNC ON, no tearline appears, but you control a fixed latency between the microsecond you Present() and the microsecond the first pixel of the scanline at top of the screen begins outputting of the GPU port.

Fortunately browsers do not need to be beam raced (without the --disable-gpu-vsync option of Chromium anyway -- I did create a javascript clone of one tearline jedi demo successfully! But only works on Windows+Chrome).

The bottom line of my reply is simply just to clarify the synchronous thing:

  1. (Main point) Ideally this should be irrelevant to SDR vs HDR, given this is color dimenison and not time dimension (sync)
  2. If you're not stuck using a third party Present() framework, you CAN roll your own pseudo-synchronous Present()
  3. Chrome succeeded in doing a pseudo-synchronous Present() that is not guaranteed synchronous but usually is (especially in single monitor situations), which is why things stutters a lot less in Chrome than FireFox. I have 10 years experience in this matter, as I indicate, thanks to my 2013 browser-present-sync tests, that I wrote over ten years ago, wink wink...

Anyway, I don't know what Chrome exactly does, but they managed to roll a pseudo-synchronous Present() behind the scenes. Yes, a software-based layer on top of the hardware Present()

Nontheless, if you discovered a synchronousness problem during a HDR vs SDR situation -- please inform me so I am aware. This is news to me, as a person who did successful synchronous Present().

3/3 Errata

Conditions for successful synchronous Present(), that gracefully degrades:

Assumptions:

  • Assumes desktop PC (or a single-GPU laptop in Performance Mode, plugged in)
  • Assumes desktop monitor (or a single-GPU laptop in Performance Mode, plugged in)
  1. Single monitor, 60Hz
  2. Single monitor, any Hz
  3. Multiple monitor, browser running on primary monitor (may stutter if some graphics update occur on secondary screens)
  4. Multiple monitor, browser running on secondary monitor (all displays same Hz)
  5. Multiple monitor, browser running on secondary monitor (all displays different Hz)

For Chrome usually succeeds on item 1-3 for page rendertimes up to 90% of refresh cycle in 10 years of my testing, with minor fluidity degradation for item 4, and bad fluidity degradation for item 5. FireFox still fails all 1,2,3,4,5 during 90% refresh cycle rendertimes.

(In reply to mark from comment #14)

Mark, I'm pretty sure we're talking about different things. I'm not talking about vblank at all, I am talking about a very specific race condition: After initial swapchain creation, when can you be sure that it is fully initialized and ready to pass to Commit (or, similarly, any higher-level APIs such as SwapChainPanel that are layered on top of DComp or the WinUI 3 lifted compositor.)

So when I call it async, I mean: you call .Present() and your thread does not necessarily block unless queues are full. (It may sync to vblank, but that's not the point. If you call .Commit() at the wrong time you can get a black frame flicker.)

There was a second issue I was dealing with, which now seems to be separate from the present-to-commit race, as I'm not still sure what caused it, but if you were to check out some early revisions of my codebase and comment out a key workaround, you would see some very confusing behavior with HDR surfaces getting clamped to SDR.

So the point (which is frankly getting a bit off-topic here) is that workaround is not and should not be necessary in a codebase where all the other API calls are set up and sequenced correctly. (This is one of those things that got fixed in my codebase after a lot of attention to detail in various places and I'm not sure precisely what fixed it. Doesn't change the fact that the "HDR surfaces getting clamped to SDR after a few milliseconds" behavior was visible and misleading. Mon dieu, I burned a lot of time on that one.)

Bottom line, I'm not going to spend all day chasing ghosts now that things are working.

Now I understand. Yes, we were talking about different things. But I understand your issue too.

Mind if you tell me the pull requests / source code contribution link? I'd love to quickly look. I don't think I'll be of any quick advice here, but we have a situation where Chrome is perfectly stutterless/jankless from 0% through 80-90% refreshtime, while FireFox (on Windows) will stutter at certain CPU/GPU utilization less than 80% refreshtime. It almost feels like it's theoretically a simple fix, but I've seen situations where it is not. It's often ranked as a "Minor" when black flicker is like "Major"/"Blocker".

...Adding to "chasing ghosts". What are ghosts to one developer is old hat to someone experienced in jank engineering (seeing I work with display manufacturers). Weird stutter/nostutter situations have long afflicted a lot of software. For example, software package A may framepace perfectly from 0-20% refreshtime (e.g. 0-2ms rendertime at 100Hz), stutter badly at 20-55% refreshtime (e.g. 2ms-5.5ms rendertime at 100Hz), then framepace perfectly at 55-80% refreshtime (5.5ms-8ms rendertime at 100Hz).

This often creates a confusing debug situation because different developer computers are slower at rendering than others. It requires custom pages like https://beta.testufo.com/animation-time-graph#easteregg=1 with a busyloop inserter (to configure rendertimes: renders more/less dummy graphics).

Anyway, I know I'm getting offtopic here, but since this was brought up, and since it's a peripherally related graphics subsystem sphere, I'd love to post my "due diligence" to the correct tracking entry...

A few things I'll respond to at once here:

  • An update on color formats for HDR WebRender - when we get around to HDR WebRender experiments we're looking at trying a different internal rendering format of srgb-extended (sRGB transfer function but with HDR range, using RGBA16F), this would have identical (i.e. 'using gamma wrong') blending behavior for the web in general, technically has the same gamut and HDR range as scRGB (though the sRGB transfer function results in silly values like 50.0 HDR headroom becoming a value around 11500, which is within range for RGBA16F as the float16 range is +/-65504), wide gamut colors may blend strangely (negative coefficients with sRGB gamma applied is a weird concept), and the sRGB transfer function can simply be undone when preparing the final swapchain image to send to the OS compositor as proper scRGB. There's no ETA for HDR functionality beyond video and canvas within the year within the team, but contributors could potentially help with that, it's a fun project.
  • I have a compositor color test utility for Windows that I wrote where I've already prototyped all of these HDR colorspaces and formats.
    • I've seen no issues with infrequently updated swapchains that are small (128x24 pixels for instance) in scRGB (DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 with DXGI_FORMAT_R16G16B16A16_FLOAT) or HDR10 (DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 with DXGI_FORMAT_R10G10B10A2_UNORM). The behavior described sounds like it would be tied to a power saving profile aimed at laptops on battery, so I'll experiment with more test hardware to see what's up.
    • However I can't get the P010 HDR10 format to work (DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020 with DXGI_FORMAT_P010), it always acts as if I provided a Rec709 video format (identical to DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709 with DXGI_FORMAT_P010), so that may be a deadend even though it would be a nice way to get video on the screen efficiently if it worked. Might need to try this on more GPUs as it might be vendor specific.
    • I've not experimented with rounded window corners on a scRGB swapchain yet, I was only experimenting with putting swapchains as overlays in front of the main window surface, so not an exact match for how Firefox operates.
  • regarding vsync, this is an interesting topic for me personally and I've researched the vsync code in Firefox a bit (mostly in regards to https://www.vsynctester.com/ and https://bugzilla.mozilla.org/show_bug.cgi?id=1941916 and https://bugzilla.mozilla.org/show_bug.cgi?id=1941693 ) but I have not made changes (other than https://bugzilla.mozilla.org/show_bug.cgi?id=1959878 which I should land sometime), I'd like to revisit this when I have time.

regarding vsync, this is an interesting topic for me personally and I've researched the vsync code in Firefox a bit (mostly in regards to https://www.vsynctester.com/ and https://bugzilla.mozilla.org/show_bug.cgi?id=1941916 and https://bugzilla.mozilla.org/show_bug.cgi?id=1941693 ) but I have not made changes (other than https://bugzilla.mozilla.org/show_bug.cgi?id=1959878 which I should land sometime), I'd like to revisit this when I have time.

FYI, I worked with the author of vsynctester to release a Apache license open-source refresh rate detector at https://github.com/blurbusters/RefreshRateCalculator .... The same source code is used at both vsynctester and testufo, we collab'd on this.

If you would like to work together on debugging some improvements to vsync, contact me mark [at] blurbusters.com as I am considered an expert in this territory. Weird odd-% utilization jank is often traced to an offset between two vsync's (a real vsync and a virtualized vsync), like a phase delay between vsync and presentation.

Steering back to ontopic HDR discussion:

An update on color formats for HDR WebRender - when we get around to HDR WebRender experiments we're looking at trying a different internal rendering format of srgb-extended (sRGB transfer function but with HDR range, using RGBA16F), this would have identical (i.e. 'using gamma wrong') blending behavior for the web in general, technically has the same gamut and HDR range as scRGB (though the sRGB transfer function results in silly values like 50.0 HDR headroom becoming a value around 11500, which is within range for RGBA16F as the float16 range is +/-65504), wide gamut colors may blend strangely (negative coefficients with sRGB gamma applied is a weird concept), and the sRGB transfer function can simply be undone when preparing the final swapchain image to send to the OS compositor as proper scRGB. There's no ETA for HDR functionality beyond video and canvas within the year within the team, but contributors could potentially help with that, it's a fun project.

Wow, the 65504 nuance is new to me! (I need to study this more).

Being pulled kicking and screaming into HDR because my display contacts got me to add HDR to TestUFO (Disclaimer: LG Display Korea paid me to add HDR to TestUFO (credit screen: https://beta.testufo.com#GLOBAL.showabout=1 ), my HDR experience is only 3 years old -- much younger than the bug tracking entry.

I am still learning these nuances too, trying to color-match HDR and SDR modes at TestUFO. It was only a few weeks ago that I successfully got SDR colors looking the same in both SDR+HDR modes in Chromium, for both HDR Canvas and HDR WebGL (which Chrome unofficially supports via the same Experimental Web Platforms Features flag).

On HDR web rendering -- color matching the HDR+SDR has been a challenge, considering the need to convert between CSS4 color values and GPU uniform values, for the beta HDR WebGL version of TestUFO ( https://beta.testufo.com/#GLOBAL.engine=webgl&GLOBAL.hdr=1 ) and HDR Canvas2D version of TestUFO ( https://beta.testufo.com/#GLOBAL.engine=2d&GLOBAL.hdr=1 ) -- these are undocumented settings at TestUFO for your HDR beta testing needs ....

The nice thing is I've got WebGL/Canvas2D engine hotswitching working now, so I am able to compare back and fourth rapidly. Experimentation on swatch colors can be done in other tests such as https://beta.testufo.com/chase#GLOBAL.engine=2d&GLOBAL.hdr=1 for HDR debugging, if you need to compare SDR/HDR and 2D/WebGL colors (I support all 4 combinations now) -- I'm adding WebGPU SDR/HDR and will include hotswitching between 2D/WebGL/WebGPU, as TestUFO is essentially a javascript hardware abstraction layer now. (Be noted, it's not fully optimized; all text overlays are still Canvas2D's converted to textures).

Nontheless, this may provide an opportunity to more easily debug HDR web rendering in FireFox, at least if you're implementing canvas-only HDR support (in either Canvas2D, or WebGL, or (soon) WebGPU). Hope TestUFO eventually becomes a useful addition to your HDR debugging suite; I'm willing to add additional tests upon FireFox requests (as an additional selection at the 40 tests in the upper right corner). Not all tests are useful for testing browsers instead of displays, but some of them are.

I have a compositor color test utility for Windows that I wrote where I've already prototyped all of these HDR colorspaces and formats.
I've seen no issues with infrequently updated swapchains that are small (128x24 pixels for instance) in scRGB (DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 with DXGI_FORMAT_R16G16B16A16_FLOAT) or HDR10 (DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 with DXGI_FORMAT_R10G10B10A2_UNORM).

Is this open source? If so, where?

I'd love to play with all the colorspaces and formats, to help me with additional conversions since I need to add multiple framebuffer formats (display-p3, rec2100-hq, rec2100-hlg, rec2100-linear) and the appropriate color conversions from all the matrixes I've seen in the various specs (IEEE, SMPTE, etc). Long term, it will eventually need to become easier for web developers, but I'm still trailblazing ahead (no other web developer is currently doing WebGL HDR like I am).

The behavior described sounds like it would be tied to a power saving profile aimed at laptops on battery, so I'll experiment with more test hardware to see what's up.

I have seen power management automatically disable HDR before, while simultaneously lowering Hz (e.g. 30-60Hz). That's why I monitor HDR change events inside my engine, e.g. I automatically destroy/recreate the canvas everytime engine is switched (2D/WebGL) or colorspace is switched (srgb/rec2100-pq/display-p3), I even created my very own "onColorSpaceChanged" type of event inside TestUFO to allow me to re-sync colorspaces on the fly, since there's no official equivalent listener for onchange/oncontextlost/etc.

(Aside: In theory, a colorspace-changed event, someday in the future, should become part of the official web specification someday (a color space change event), alongside the existing events (like resizes etc). This would allow some webpages to readjust graphics on the fly whenever things like power management or a window move (between SDR + HDR on multimonitor) changes the colorspace of the browser window. (My makeshift color space changed event works for this now, at least for Chrome on Macs). Since few developers are pushing HDR limits yet in browsers, this is understandably not priority, but I add this in case one of you are participating in HDR standards discussions)

However I can't get the P010 HDR10 format to work (DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020 with DXGI_FORMAT_P010), it always acts as if I provided a Rec709 video format (identical to DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709 with DXGI_FORMAT_P010), so that may be a deadend even though it would be a nice way to get video on the screen efficiently if it worked. Might need to try this on more GPUs as it might be vendor specific.

Hmmm, interesting. I suspect it's vendor specific, but I haven't tested colorspaces in DXGI yet.

I realize HDR is not highly prioritized (yet), but the convergence of high Hz and HDR has lit a big fire at my side, so I'm currently quite attentive to HDR web rendering developments, due to the convergence of Hz and HDR, eliminating low-quality compromises of high Hz (like yesteryear TN gaming monitors that had poor color quality). The mainstreaming of 120Hz with HDR is well under way ("Pro" Apple devices, Android devices, consoles, televisions) and testufo is used for display testing, and this provides impetus for HDR web rendering.

(Errata: I see some typos, "rec2100-hq" should have been "rec2100-pq". I don't have edit priveleges here)

Just to clarify, I think I now understand the source of my "ghost": I was using ResizeBuffers to toggle format from SDR<->HDR on a surface that was already visible. That's the most likely culprit. Hindsight being 20/20, I'd advise against making the same mistakes I did--the compositor seems to get hella confused.

(In reply to mark from comment #21)

On HDR web rendering -- color matching the HDR+SDR has been a challenge, considering the need to convert between CSS4 color values and GPU uniform values, for the beta HDR WebGL version of TestUFO ( https://beta.testufo.com/#GLOBAL.engine=webgl&GLOBAL.hdr=1 ) and HDR Canvas2D version of TestUFO ( https://beta.testufo.com/#GLOBAL.engine=2d&GLOBAL.hdr=1 ) -- these are undocumented settings at TestUFO for your HDR beta testing needs ....

This seems useful, I'll take a look when we start working on HDR WebGL support again later this year, video is current priority for the common use-case of watching movies and other content on the web.

I have a compositor color test utility for Windows that I wrote where I've already prototyped all of these HDR colorspaces and formats.
I've seen no issues with infrequently updated swapchains that are small (128x24 pixels for instance) in scRGB (DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 with DXGI_FORMAT_R16G16B16A16_FLOAT) or HDR10 (DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 with DXGI_FORMAT_R10G10B10A2_UNORM).

Is this open source? If so, where?

I'd love to play with all the colorspaces and formats, to help me with additional conversions since I need to add multiple framebuffer formats (display-p3, rec2100-hq, rec2100-hlg, rec2100-linear) and the appropriate color conversions from all the matrixes I've seen in the various specs (IEEE, SMPTE, etc). Long term, it will eventually need to become easier for web developers, but I'm still trailblazing ahead (no other web developer is currently doing WebGL HDR like I am).

Definitely only research project quality but here's the code https://github.com/FirefoxGraphics/compositor_colortest/

(Aside: In theory, a colorspace-changed event, someday in the future, should become part of the official web specification someday (a color space change event), alongside the existing events (like resizes etc). This would allow some webpages to readjust graphics on the fly whenever things like power management or a window move (between SDR + HDR on multimonitor) changes the colorspace of the browser window. (My makeshift color space changed event works for this now, at least for Chrome on Macs). Since few developers are pushing HDR limits yet in browsers, this is understandably not priority, but I add this in case one of you are participating in HDR standards discussions)

Certainly an interesting idea, I'm sure major online streaming websites would want this as well, currently it seems they only query the media feature 'video-dynamic-range' on page load.

I realize HDR is not highly prioritized (yet), but the convergence of high Hz and HDR has lit a big fire at my side, so I'm currently quite attentive to HDR web rendering developments, due to the convergence of Hz and HDR, eliminating low-quality compromises of high Hz (like yesteryear TN gaming monitors that had poor color quality). The mainstreaming of 120Hz with HDR is well under way ("Pro" Apple devices, Android devices, consoles, televisions) and testufo is used for display testing, and this provides impetus for HDR web rendering.

I can say that HDR has been highly prioritized since the start of 2026 as part of a priority shuffle based on feedback.

Side note: Putting a needinfo on you just so you notice this message as I know it's been a while.

Flags: needinfo?(mark)

BTW, TestUFO supports <EMBED> so you can compare 3 framebuffer APIs + all colorspaces side by side

  • 2D as '2d'
  • WebGL as 'webgl'
  • WebGPU as 'webgpu'

Engines: '2d', 'webgl', 'webgpu'
Colorspaces: 'srgb', 'srgb-linear', 'display-p3' 'rec2100-pq', 'rec2100-hlg', 'rec2100-linear'

Examples, TestUFO will attempt to create the framebuffer format at these TestUFO hash parameters for your browser testing, and you can use global.showhdr to show colorspaces. TestUFO already internally converts CSS4/shader color framebuffer colors to xyz then reconvert back to the new colorspace.

global.engine=webgpu
global.colorspace=rec2100-pq
global.showhdr=1

So if you want TestUFO in WebGPU in PQ format:

And as an embed add /embed/ before the screen name ('hdr').

The list of TestUFO screens are www.blurbusters.com/testufo3 and for embed, add /embed/

You can add multiple TestUFO embeds to the same webpage, example:

  <p>
    <iframe src="/embed/chase#background=F06040&global.showhdr=1&global.showdebug=0&global.engine=2d" width="600" height="240"></iframe>
    <iframe src="/embed/chase#background=F06040&global.showhdr=1&global.showdebug=0&global.engine=webgl" width="600" height="240"></iframe>
    <iframe src="/embed/chase#background=F06040&global.showhdr=1&global.showdebug=0&global.engine=webgpu" width="600" height="240"></iframe>
  </p>
  <p>
    <iframe src="/embed/vrr#global.showhdr=1&global.showdebug=0&global.engine=2d" width="600" height="360"></iframe>
    <iframe src="/embed/vrr#global.showhdr=1&global.showdebug=0&global.engine=webgl" width="600" height="360"></iframe>
    <iframe src="/embed/vrr#global.showhdr=1&global.showdebug=0&global.engine=webgpu" width="600" height="360"></iframe>
  </p>

The 2d/webgl/webgpu looks identical in Chromium. This can help comparing color shades and graphics rendering across APIs.

Flags: needinfo?(mark)

Ooops, add absolute https://testufo.com/ to that example ... It was copypasta from one of my test suite pages (using relative URLs) that I use for color-swatch comparision across engines and colorspaces.

Duplicate of this bug: 1928793
You need to log in before you can comment on or make changes to this bug.