Skip to content
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
On second thought, what matters is the range of the *format*, not the…
… gamut
  • Loading branch information
kainino0x committed Mar 19, 2024
commit d706a2085dfc441c2bad8c72847d3595b5bc9b49
60 changes: 30 additions & 30 deletions spec/index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -2148,34 +2148,36 @@ premultiplication) in which the WebGPU numeric values are to be interpreted.
WebGPU allows all of the color spaces in the {{PredefinedColorSpace}} enum.
Note, each color space is defined over an extended range, as defined by the referenced CSS definitions,
to represent color values outside of its space (in both chrominance and luminance).
Any numeric value in any color space is a well-defined "color" according to its extended color
space definition, regardless of whether the point represents a real or imaginary color
(WebGPU makes no distinction between real and imaginary colors).
That "color" can be represented in other color spaces as well, where it may be in-gamut.

<h4 data-dfn-type=dfn>Out-of-Standard-Gamut Color Representation
<h4 data-dfn-type=dfn>Out-of-Range Premultiplied RGBA Value
<span id=out-of-gamut-premultiplied-rgba-value><!-- historical permalink --></span>
</h4>

An [=out-of-standard-gamut color representation=] is one where any of the unpremultiplied
red/green/blue channel numeric values are outside of the 0-to-1 range.
In a premultiplied representation, the R/G/B value is outside the 0-to-`alpha` range.

Any numeric value in any gamut is a well-defined "color" according to that gamut's extended
color space (regardless of whether the point represents a real or imaginary color).
That "color" can be represented in other gamuts as well, where it may be in-gamut.
An [=out-of-range premultiplied RGBA value=] is one for which the unpremultiplied representation
of the same color would be outside of the range of the format.

<div class=example>
For example, the unpremultiplied sRGB RGBA value `[1.04, 0, 0, 0.5]` and the
premultiplied sRGB RGBA value `[0.52, 0, 0, 0.5]` both represent the
CSS color <code><a funcdef>color</a>(srgb 1.04 0 0 / 0.5)</code>, which is equivalent to the
in-gamut Display P3 color <code><a funcdef>color</a>(display-p3 0.95 0.21 0.15 / 0.5)</code>.

When represented in a {{PredefinedColorSpace/"srgb"}} {{GPUTextureFormat/"rgba8unorm"}} canvas,
both values are out-of-range for the purposes of this definition,
even though the premultiplied value is representable.
</div>

When a canvas containing such a value is
[$get a copy of the image contents of a context|used as an image source$],
the returned image must represent the original color values without clamping,
regardless of the canvas's {{GPUCanvasConfiguration/format}}.

However, when a canvas containing such a value is displayed *on screen*, **if and only if** the
canvas has a range-limited format (like `unorm` formats, which have a range of 0&ndash;1),
the visible result for the entire canvas is undefined.
However, when a ({{GPUCanvasAlphaMode/"premultiplied"}}) canvas containing such a value is
displayed *on screen*, the visible result for the entire canvas is undefined.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Checking that I understand.

Consider a canvas that is premultiplied, rgba16float, srgb being displayed on a P3 monitor.

If the value [0.52, 0, 0, 0.5] is written to the canvas, and the canvas is displayed, then the result is undefined even though it's within the gamut of the display, because of various machinations that might happen throughout the display pipeline.

This seems reasonable to me. Would the idea be to make this valid when in extended mode?


<div class=example>
For example, on a display with a color space of exactly Display P3,
Expand All @@ -2187,33 +2189,29 @@ the visible result for the entire canvas is undefined.
<tbody>
<tr><td>{{GPUCanvasAlphaMode/"opaque"}},<br>{{GPUTextureFormat/"rgba16float"}}
<td rowspan=2>`[1.04, 0, 0, 1.0]`
<td rowspan=2><code><a funcdef>color</a>(display-p3 0.95 0.21 0.15 / 1.0)</code>,
<br>because the format is not range-limited.
<td rowspan=2><code><a funcdef>color</a>(display-p3 0.95 0.21 0.15 / 1.0)</code>.
<tr><td>{{GPUCanvasAlphaMode/"premultiplied"}},<br>{{GPUTextureFormat/"rgba16float"}}
<tr><td>{{GPUCanvasAlphaMode/"premultiplied"}},<br>{{GPUTextureFormat/"rgba16float"}}
<td>`[0.52, 0, 0, 0.5]`
<td><code><a funcdef>color</a>(display-p3 0.95 0.21 0.15 / 0.5)</code>,
<br>because the format is not range-limited.
<td><code><a funcdef>color</a>(display-p3 0.95 0.21 0.15 / 0.5)</code>.
<tr><td>{{GPUCanvasAlphaMode/"premultiplied"}},<br>{{GPUTextureFormat/"rgba8unorm"}}
<td>`[0.50, 0, 0, 0.5]`
<td><code><a funcdef>color</a>(display-p3 0.92 0.20 0.14 / 0.5)</code>,
<br>because the color is in-gamut for {{PredefinedColorSpace/"srgb"}}.
<td><code><a funcdef>color</a>(display-p3 0.92 0.20 0.14 / 0.5)</code>.
<tr><td>{{GPUCanvasAlphaMode/"premultiplied"}},<br>{{GPUTextureFormat/"rgba8unorm"}}
<td>`[0.52, 0, 0, 0.5]`
<td>(undefined result),
<br>because the format is range-limited
<br>and the color is out-of-standard-gamut for {{PredefinedColorSpace/"srgb"}}.
<!-- POSTV1(#4108): add example for XR format -->
<br>because the unpremultiplied color cannot be represented in this format.
<!-- POSTV1(#4108): add example for XR formats, which *probably* handle this -->
</table>

The last result is undefined because the color could be:
Visible results may be undefined because the value could be lost at different steps of display:
Copy link
Copy Markdown
Contributor Author

@kainino0x kainino0x Mar 19, 2024

Choose a reason for hiding this comment

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

I wanted to write out an example of the actual blending math, but that ends up demonstrating a completely different problem, which is that you get very different results doing nonlinear blending in display color space (e.g. in display-p3), vs in srgb.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yes, it gets complicated.


- blended directly to the display buffer as
- Value blended directly to the display buffer as
<code><a funcdef>color</a>(display-p3 0.95 0.21 0.15 / 0.5)</code>.
- blended through an intermediate sRGB buffer as `[0.5, 0, 0, 0.5]`,
- Value blended through an intermediate sRGB buffer as `[0.5, 0, 0, 0.5]`,
then sent to the display buffer resulting in the appearance of
<code><a funcdef>color</a>(display-p3 0.92 0.2 0.14 / 0.5)</code>.
- displayed by some other process.
- Value displayed by some other process.
</div>

Note:
Expand All @@ -2234,9 +2232,11 @@ are ignored.
Note:
Grayscale images generally represent RGB values `(V, V, V)`, or RGBA values `(V, V, V, A)` in their color space.

Colors must not be lossily clamped during conversion: converting from one color space to another
may result in an [=out-of-standard-gamut color representation=] in the destination gamut,
which must be preserved. Refer to that section for details.
Colors are not lossily clamped during conversion: converting from one color space to another
will result in values outside the range [0, 1] if the source color values were outside the range
of the destination color space's gamut. For an sRGB destination, for example, this can occur if the
source is rgba16float, in a wider color space like Display-P3, or is premultiplied and contains
[=out-of-range premultiplied RGBA values=].

Similarly, precision must be preserved through color space conversion, with intermediate
computations being at least as precise as the less-precise of the source and destination
Expand Down Expand Up @@ -13816,9 +13816,9 @@ is being composited into (e.g. an HTML page rendering, or a 2D canvas).
When alpha is 0, no color is represented, regardless of the RGB values.
(Attempting to compute the color results in division by zero.)

In this mode, the canvas texture may hold [=out-of-standard-gamut color representations=].
If the canvas has a range-limited format and is displayed on screen, out-of-standard-gamut
values have undefined display results. Refer to that section for details.
In this mode, with some texture formats, the canvas texture can hold
[=out-of-range premultiplied RGBA values=], which have undefined display results.
Refer to that section for details.
</dl>

# Errors &amp; Debugging # {#errors-and-debugging}
Expand Down