tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit cad38fb97859273974da3ec9b2a9e393b5d73868
parent 49dbde2ae8f9985a4f049823542580bcc29398e9
Author: me <mr.alexander.n.hill@gmail.com>
Date:   Mon,  6 Oct 2025 17:58:46 +0000

Bug 627771 - Implement SWGL fast-path dithering for linear and radial gradients. r=lsalzman

Differential Revision: https://phabricator.services.mozilla.com/D265921

Diffstat:
Mgfx/wr/glsl-to-cxx/src/hir.rs | 24++++++++++++++++++++++++
Mgfx/wr/swgl/src/swgl_ext.h | 429++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mgfx/wr/webrender/res/brush_linear_gradient.glsl | 6+++++-
Mgfx/wr/webrender/res/cs_radial_gradient.glsl | 8++++++--
Mgfx/wr/webrender/res/ps_quad_gradient.glsl | 5+++++
Mgfx/wr/webrender/res/ps_quad_radial_gradient.glsl | 8++++++--
6 files changed, 395 insertions(+), 85 deletions(-)

diff --git a/gfx/wr/glsl-to-cxx/src/hir.rs b/gfx/wr/glsl-to-cxx/src/hir.rs @@ -3966,6 +3966,14 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio ); declare_function( state, + "swgl_commitDitheredLinearGradientRGBA8", + None, + Type::new(Void), + vec![Type::new(Sampler2D), Type::new(Int), Type::new(Float), Type::new(Bool), Type::new(Bool), + Type::new(Vec2), Type::new(Vec2), Type::new(Float), Type::new(Vec4)], + ); + declare_function( + state, "swgl_commitRadialGradientRGBA8", None, Type::new(Void), @@ -3974,6 +3982,14 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio ); declare_function( state, + "swgl_commitDitheredRadialGradientRGBA8", + None, + Type::new(Void), + vec![Type::new(Sampler2D), Type::new(Int), Type::new(Float), Type::new(Bool), Type::new(Vec2), + Type::new(Float), Type::new(Vec4)], + ); + declare_function( + state, "swgl_commitRadialGradientFromStopsRGBA8", None, Type::new(Void), @@ -3982,6 +3998,14 @@ pub fn ast_to_hir(state: &mut State, tu: &syntax::TranslationUnit) -> Translatio ); declare_function( state, + "swgl_commitDitheredRadialGradientFromStopsRGBA8", + None, + Type::new(Void), + vec![Type::new(Sampler2D), Type::new(Int), Type::new(Int), Type::new(Float), Type::new(Bool), Type::new(Vec2), + Type::new(Float), Type::new(Vec4)], + ); + declare_function( + state, "swgl_commitGradientRGBA8", None, Type::new(Void), diff --git a/gfx/wr/swgl/src/swgl_ext.h b/gfx/wr/swgl/src/swgl_ext.h @@ -1384,14 +1384,122 @@ static inline WideRGBA8 sampleGradient(sampler2D sampler, int address, swgl_commitChunk(RGBA8, applyColor(sampleGradient(sampler, address, entry), \ packColor(swgl_OutRGBA, color))) +// Precomputed noise for adding directly to four horizontally contiguous pixels +// TODO: These should be updated for parity with the shader dither +// implementation once something more final exists there. Right now, these are +// very close but slightly off. +static const VectorType<int16_t, 16> ditherNoise[64] = { + {2, 2, 2, 128, 194, 194, 194, 128, 50, 50, 50, 128, 242, 242, 242, 128}, + {194, 194, 194, 128, 50, 50, 50, 128, 242, 242, 242, 128, 14, 14, 14, 128}, + {50, 50, 50, 128, 242, 242, 242, 128, 14, 14, 14, 128, 206, 206, 206, 128}, + {242, 242, 242, 128, 14, 14, 14, 128, 206, 206, 206, 128, 62, 62, 62, 128}, + {14, 14, 14, 128, 206, 206, 206, 128, 62, 62, 62, 128, 254, 254, 254, 128}, + {206, 206, 206, 128, 62, 62, 62, 128, 254, 254, 254, 128, 130, 130, 130, + 128}, + {62, 62, 62, 128, 254, 254, 254, 128, 130, 130, 130, 128, 66, 66, 66, 128}, + {254, 254, 254, 128, 130, 130, 130, 128, 66, 66, 66, 128, 178, 178, 178, + 128}, + {130, 130, 130, 128, 66, 66, 66, 128, 178, 178, 178, 128, 114, 114, 114, + 128}, + {66, 66, 66, 128, 178, 178, 178, 128, 114, 114, 114, 128, 142, 142, 142, + 128}, + {178, 178, 178, 128, 114, 114, 114, 128, 142, 142, 142, 128, 78, 78, 78, + 128}, + {114, 114, 114, 128, 142, 142, 142, 128, 78, 78, 78, 128, 190, 190, 190, + 128}, + {142, 142, 142, 128, 78, 78, 78, 128, 190, 190, 190, 128, 126, 126, 126, + 128}, + {78, 78, 78, 128, 190, 190, 190, 128, 126, 126, 126, 128, 34, 34, 34, 128}, + {190, 190, 190, 128, 126, 126, 126, 128, 34, 34, 34, 128, 226, 226, 226, + 128}, + {126, 126, 126, 128, 34, 34, 34, 128, 226, 226, 226, 128, 18, 18, 18, 128}, + {34, 34, 34, 128, 226, 226, 226, 128, 18, 18, 18, 128, 210, 210, 210, 128}, + {226, 226, 226, 128, 18, 18, 18, 128, 210, 210, 210, 128, 46, 46, 46, 128}, + {18, 18, 18, 128, 210, 210, 210, 128, 46, 46, 46, 128, 238, 238, 238, 128}, + {210, 210, 210, 128, 46, 46, 46, 128, 238, 238, 238, 128, 30, 30, 30, 128}, + {46, 46, 46, 128, 238, 238, 238, 128, 30, 30, 30, 128, 222, 222, 222, 128}, + {238, 238, 238, 128, 30, 30, 30, 128, 222, 222, 222, 128, 162, 162, 162, + 128}, + {30, 30, 30, 128, 222, 222, 222, 128, 162, 162, 162, 128, 98, 98, 98, 128}, + {222, 222, 222, 128, 162, 162, 162, 128, 98, 98, 98, 128, 146, 146, 146, + 128}, + {162, 162, 162, 128, 98, 98, 98, 128, 146, 146, 146, 128, 82, 82, 82, 128}, + {98, 98, 98, 128, 146, 146, 146, 128, 82, 82, 82, 128, 174, 174, 174, 128}, + {146, 146, 146, 128, 82, 82, 82, 128, 174, 174, 174, 128, 110, 110, 110, + 128}, + {82, 82, 82, 128, 174, 174, 174, 128, 110, 110, 110, 128, 158, 158, 158, + 128}, + {174, 174, 174, 128, 110, 110, 110, 128, 158, 158, 158, 128, 94, 94, 94, + 128}, + {110, 110, 110, 128, 158, 158, 158, 128, 94, 94, 94, 128, 10, 10, 10, 128}, + {158, 158, 158, 128, 94, 94, 94, 128, 10, 10, 10, 128, 202, 202, 202, 128}, + {94, 94, 94, 128, 10, 10, 10, 128, 202, 202, 202, 128, 58, 58, 58, 128}, + {10, 10, 10, 128, 202, 202, 202, 128, 58, 58, 58, 128, 250, 250, 250, 128}, + {202, 202, 202, 128, 58, 58, 58, 128, 250, 250, 250, 128, 6, 6, 6, 128}, + {58, 58, 58, 128, 250, 250, 250, 128, 6, 6, 6, 128, 198, 198, 198, 128}, + {250, 250, 250, 128, 6, 6, 6, 128, 198, 198, 198, 128, 54, 54, 54, 128}, + {6, 6, 6, 128, 198, 198, 198, 128, 54, 54, 54, 128, 246, 246, 246, 128}, + {198, 198, 198, 128, 54, 54, 54, 128, 246, 246, 246, 128, 138, 138, 138, + 128}, + {54, 54, 54, 128, 246, 246, 246, 128, 138, 138, 138, 128, 74, 74, 74, 128}, + {246, 246, 246, 128, 138, 138, 138, 128, 74, 74, 74, 128, 186, 186, 186, + 128}, + {138, 138, 138, 128, 74, 74, 74, 128, 186, 186, 186, 128, 122, 122, 122, + 128}, + {74, 74, 74, 128, 186, 186, 186, 128, 122, 122, 122, 128, 134, 134, 134, + 128}, + {186, 186, 186, 128, 122, 122, 122, 128, 134, 134, 134, 128, 70, 70, 70, + 128}, + {122, 122, 122, 128, 134, 134, 134, 128, 70, 70, 70, 128, 182, 182, 182, + 128}, + {134, 134, 134, 128, 70, 70, 70, 128, 182, 182, 182, 128, 118, 118, 118, + 128}, + {70, 70, 70, 128, 182, 182, 182, 128, 118, 118, 118, 128, 42, 42, 42, 128}, + {182, 182, 182, 128, 118, 118, 118, 128, 42, 42, 42, 128, 234, 234, 234, + 128}, + {118, 118, 118, 128, 42, 42, 42, 128, 234, 234, 234, 128, 26, 26, 26, 128}, + {42, 42, 42, 128, 234, 234, 234, 128, 26, 26, 26, 128, 218, 218, 218, 128}, + {234, 234, 234, 128, 26, 26, 26, 128, 218, 218, 218, 128, 38, 38, 38, 128}, + {26, 26, 26, 128, 218, 218, 218, 128, 38, 38, 38, 128, 230, 230, 230, 128}, + {218, 218, 218, 128, 38, 38, 38, 128, 230, 230, 230, 128, 22, 22, 22, 128}, + {38, 38, 38, 128, 230, 230, 230, 128, 22, 22, 22, 128, 214, 214, 214, 128}, + {230, 230, 230, 128, 22, 22, 22, 128, 214, 214, 214, 128, 170, 170, 170, + 128}, + {22, 22, 22, 128, 214, 214, 214, 128, 170, 170, 170, 128, 106, 106, 106, + 128}, + {214, 214, 214, 128, 170, 170, 170, 128, 106, 106, 106, 128, 154, 154, 154, + 128}, + {170, 170, 170, 128, 106, 106, 106, 128, 154, 154, 154, 128, 90, 90, 90, + 128}, + {106, 106, 106, 128, 154, 154, 154, 128, 90, 90, 90, 128, 166, 166, 166, + 128}, + {154, 154, 154, 128, 90, 90, 90, 128, 166, 166, 166, 128, 102, 102, 102, + 128}, + {90, 90, 90, 128, 166, 166, 166, 128, 102, 102, 102, 128, 150, 150, 150, + 128}, + {166, 166, 166, 128, 102, 102, 102, 128, 150, 150, 150, 128, 86, 86, 86, + 128}, + {102, 102, 102, 128, 150, 150, 150, 128, 86, 86, 86, 128, 2, 2, 2, 128}, + {150, 150, 150, 128, 86, 86, 86, 128, 2, 2, 2, 128, 194, 194, 194, 128}, + {86, 86, 86, 128, 2, 2, 2, 128, 194, 194, 194, 128, 50, 50, 50, 128}}; + +// Values in color should be in the 0x00FF..0xFF00 range so that dithering has +// enough overhead to avoid overflow and underflow. +static inline VectorType<uint16_t, 4 * 4> dither( + VectorType<uint16_t, 4 * 4> color, ivec4_scalar fragCoord, + const VectorType<int16_t, 16>* ditherNoiseYIndexed) { + color += ditherNoiseYIndexed[fragCoord.x & 7]; + return color; +} + // Samples an entire span of a linear gradient by crawling the gradient table // and looking for consecutive stops that can be merged into a single larger // gradient, then interpolating between those larger gradients within the span. -template <bool BLEND> +template <bool BLEND, bool DITHER> static bool commitLinearGradient(sampler2D sampler, int address, float size, bool tileRepeat, bool gradientRepeat, vec2 pos, const vec2_scalar& scaleDir, float startOffset, - uint32_t* buf, int span) { + uint32_t* buf, int span, vec4 fragCoord) { assert(sampler->format == TextureFormat::RGBA32F); assert(address >= 0 && address < int(sampler->height * sampler->stride)); GradientStops* stops = (GradientStops*)&sampler->buf[address]; @@ -1403,6 +1511,16 @@ static bool commitLinearGradient(sampler2D sampler, int address, float size, if (!isfinite(delta)) { return false; } + + // Only incremented in the case of dithering + ivec4_scalar currentFragCoord = + ivec4_scalar(fragCoord.x.x, fragCoord.y.x, fragCoord.z.x, fragCoord.w.x); + + const VectorType<int16_t, 16>* ditherNoiseYIndexed; + if (DITHER) { + ditherNoiseYIndexed = &ditherNoise[((uint32_t)fragCoord.y.x & 7) * 8]; + } + // If we have a repeating brush, then the position will be modulo the [0,1) // interval. Compute coefficients that can be used to quickly evaluate the // distance to the interval boundary where the offset will wrap. @@ -1501,17 +1619,20 @@ static bool commitLinearGradient(sampler2D sampler, int address, float size, if (chunks >= 1.0f) { int inside = int(chunks); // Sample the start color from the min entry and the end color from the - // max entry of the merged gradient. These are scaled to a range of - // 0..0xFF00, as that is the largest shifted value that can fit in a U16. - // Since we are only doing addition with the step value, we can still - // represent negative step values without having to use an explicit sign - // bit, as the result will still come out the same, allowing us to gain an - // extra bit of precision. We will later shift these into 8 bit output - // range while committing the span, but stepping with higher precision to - // avoid banding. We convert from RGBA to BGRA here to avoid doing this in - // the inner loop. + // max entry of the merged gradient. For non-dithered, these are scaled to + // a range of 0..0xFF00, as that is the largest shifted value that can fit + // in a U16. For dithered, these are scaled to a range of 0x00FF-0xFF00 to + // allow room to avoid overflow and underflow when applying the dither + // pattern. Since we are only doing addition with the step value, we can + // still represent negative step values without having to use an explicit + // sign bit, as the result will still come out the same, allowing us to + // gain an extra bit of precision. We will later shift these into 8 bit + // output range while committing the span, but stepping with higher + // precision to avoid banding. We convert from RGBA to BGRA here to avoid + // doing this in the inner loop. auto minColorF = stops[minIndex].startColor.zyxw * float(0xFF00); auto maxColorF = stops[maxIndex].end_color().zyxw * float(0xFF00); + // Get the color range of the merged gradient, normalized to its size. auto colorRangeF = (maxColorF - minColorF) * (1.0f / (maxIndex + 1 - minIndex)); @@ -1537,7 +1658,15 @@ static bool commitLinearGradient(sampler2D sampler, int address, float size, // deltas. int segment = min(remaining, 256 / 4); for (auto* end = buf + segment * 4; buf < end; buf += 4) { - commit_blend_span<BLEND>(buf, bit_cast<WideRGBA8>(color >> 8)); + if (DITHER) { + commit_blend_span<BLEND>( + buf, + bit_cast<WideRGBA8>( + dither(color, currentFragCoord, ditherNoiseYIndexed) >> 8)); + currentFragCoord.x += 4; + } else { + commit_blend_span<BLEND>(buf, bit_cast<WideRGBA8>(color >> 8)); + } color += deltaColor; } remaining -= segment; @@ -1570,7 +1699,19 @@ static bool commitLinearGradient(sampler2D sampler, int address, float size, // will calculate a table entry for each sample, assuming the samples may // have different table entries. Float entry = clamp(offset * size + 1.0f, 0.0f, 1.0f + size); - commit_blend_span<BLEND>(buf, sampleGradient(sampler, address, entry)); + if (DITHER) { + auto gradientSample = static_cast<VectorType<uint16_t, 4 * 4>>( + sampleGradient(sampler, address, entry)) + << 8; + commit_blend_span<BLEND>( + buf, static_cast<WideRGBA8>(dither(gradientSample, currentFragCoord, + ditherNoiseYIndexed) >> + 8)); + currentFragCoord.x += 4; + } else { + commit_blend_span<BLEND>( + buf, static_cast<WideRGBA8>(sampleGradient(sampler, address, entry))); + } span -= 4; buf += 4; pos += posStep; @@ -1590,13 +1731,35 @@ static bool commitLinearGradient(sampler2D sampler, int address, float size, do { \ bool drawn = false; \ if (blend_key) { \ - drawn = commitLinearGradient<true>( \ + drawn = commitLinearGradient<true, false>( \ + sampler, address, size, tileRepeat, gradientRepeat, pos, scaleDir, \ + startOffset, swgl_OutRGBA8, swgl_SpanLength, \ + static_cast<vec4>(0x0)); \ + } else { \ + drawn = commitLinearGradient<false, false>( \ + sampler, address, size, tileRepeat, gradientRepeat, pos, scaleDir, \ + startOffset, swgl_OutRGBA8, swgl_SpanLength, \ + static_cast<vec4>(0x0)); \ + } \ + if (drawn) { \ + swgl_OutRGBA8 += swgl_SpanLength; \ + swgl_SpanLength = 0; \ + } \ + } while (0) + +#define swgl_commitDitheredLinearGradientRGBA8( \ + sampler, address, size, tileRepeat, gradientRepeat, pos, scaleDir, \ + startOffset, fragCoord) \ + do { \ + bool drawn = false; \ + if (blend_key) { \ + drawn = commitLinearGradient<true, true>( \ sampler, address, size, tileRepeat, gradientRepeat, pos, scaleDir, \ - startOffset, swgl_OutRGBA8, swgl_SpanLength); \ + startOffset, swgl_OutRGBA8, swgl_SpanLength, fragCoord); \ } else { \ - drawn = commitLinearGradient<false>( \ + drawn = commitLinearGradient<false, true>( \ sampler, address, size, tileRepeat, gradientRepeat, pos, scaleDir, \ - startOffset, swgl_OutRGBA8, swgl_SpanLength); \ + startOffset, swgl_OutRGBA8, swgl_SpanLength, fragCoord); \ } \ if (drawn) { \ swgl_OutRGBA8 += swgl_SpanLength; \ @@ -1626,10 +1789,10 @@ static ALWAYS_INLINE auto fastLength(V v) { // and looking for consecutive stops that can be merged into a single larger // gradient, then interpolating between those larger gradients within the span // based on the computed position relative to a radius. -template <bool BLEND> +template <bool BLEND, bool DITHER> static bool commitRadialGradient(sampler2D sampler, int address, float size, bool repeat, vec2 pos, float radius, - uint32_t* buf, int span) { + uint32_t* buf, int span, vec4 fragCoord) { assert(sampler->format == TextureFormat::RGBA32F); assert(address >= 0 && address < int(sampler->height * sampler->stride)); GradientStops* stops = (GradientStops*)&sampler->buf[address]; @@ -1659,6 +1822,16 @@ static bool commitRadialGradient(sampler2D sampler, int address, float size, if (!isfinite(deltaDelta) || !isfinite(radius)) { return false; } + + // Only incremented in the case of dithering + ivec4_scalar currentFragCoord = + ivec4_scalar(fragCoord.x.x, fragCoord.y.x, fragCoord.z.x, fragCoord.w.x); + + const VectorType<int16_t, 16>* ditherNoiseYIndexed; + if (DITHER) { + ditherNoiseYIndexed = &ditherNoise[((uint32_t)fragCoord.y.x & 7) * 8]; + } + float invDelta, middleT, middleB; if (deltaDelta > 0) { invDelta = 1.0f / deltaDelta; @@ -1779,9 +1952,13 @@ static bool commitRadialGradient(sampler2D sampler, int address, float size, // Figure out how many chunks are actually inside the merged gradient. if (t + 4.0f <= endT) { int inside = int(endT - t) & ~3; - // Convert start and end colors to BGRA and scale to 0..255 range later. - auto minColorF = stops[minIndex].startColor.zyxw * 255.0f; - auto maxColorF = stops[maxIndex].end_color().zyxw * 255.0f; + // Convert start and end colors to BGRA and scale to 0x00FF..0xFF00 range + // (for dithered) or 0..255 (for non-dithered) later. + auto minColorF = + stops[minIndex].startColor.zyxw * (DITHER ? float(0xFF00) : 255.0f); + auto maxColorF = + stops[maxIndex].end_color().zyxw * (DITHER ? float(0xFF00) : 255.0f); + // Compute the change in color per change in gradient offset. auto deltaColorF = (maxColorF - minColorF) * (size / (maxIndex + 1 - minIndex)); @@ -1790,18 +1967,30 @@ static bool commitRadialGradient(sampler2D sampler, int address, float size, Float colorF = minColorF - deltaColorF * (startRadius + (minIndex - 1) / size); // Finally, walk over the span accumulating the position dot product and - // getting its sqrt as an offset into the color ramp. Since we're already - // in BGRA format and scaled to 255, we just need to round to an integer - // and pack down to pixel format. + // getting its sqrt as an offset into the color ramp. At this point we + // just need to round to an integer and pack down to pixel format. for (auto* end = buf + inside; buf < end; buf += 4) { Float offsetG = fastSqrt<false>(dotPos); - commit_blend_span<BLEND>( - buf, - combine( - packRGBA8(round_pixel(colorF + deltaColorF * offsetG.x, 1), - round_pixel(colorF + deltaColorF * offsetG.y, 1)), - packRGBA8(round_pixel(colorF + deltaColorF * offsetG.z, 1), - round_pixel(colorF + deltaColorF * offsetG.w, 1)))); + if (DITHER) { + auto color = combine( + CONVERT(round_pixel(colorF + deltaColorF * offsetG.x, 1), U16), + CONVERT(round_pixel(colorF + deltaColorF * offsetG.y, 1), U16), + CONVERT(round_pixel(colorF + deltaColorF * offsetG.z, 1), U16), + CONVERT(round_pixel(colorF + deltaColorF * offsetG.w, 1), U16)); + commit_blend_span<BLEND>( + buf, + static_cast<WideRGBA8>( + dither(color, currentFragCoord, ditherNoiseYIndexed) >> 8)); + currentFragCoord.x += 4; + } else { + auto color = combine( + packRGBA8(round_pixel(colorF + deltaColorF * offsetG.x, 1), + round_pixel(colorF + deltaColorF * offsetG.y, 1)), + packRGBA8(round_pixel(colorF + deltaColorF * offsetG.z, 1), + round_pixel(colorF + deltaColorF * offsetG.w, 1))); + commit_blend_span<BLEND>(buf, color); + } + dotPos += dotPosDelta; dotPosDelta += deltaDelta2; } @@ -1908,11 +2097,12 @@ static int32_t findGradientStopPair(float offset, float* stops, } // Samples an entire span of a radial gradient. -template <bool BLEND> +template <bool BLEND, bool DITHER> static bool commitRadialGradientFromStops(sampler2D sampler, int offsetsAddress, int colorsAddress, float stopCount, - bool repeat, vec2 pos, float startRadius, - uint32_t* buf, int span) { + bool repeat, vec2 pos, + float startRadius, uint32_t* buf, + int span, vec4 fragCoord) { assert(sampler->format == TextureFormat::RGBA32F); // Stop offsets are expected to be stored just after the colors. assert(colorsAddress >= 0 && colorsAddress < offsetsAddress); @@ -1946,6 +2136,16 @@ static bool commitRadialGradientFromStops(sampler2D sampler, int offsetsAddress, if (!isfinite(deltaDelta) || !isfinite(startRadius)) { return false; } + + // Only incremented in the case of dithering + ivec4_scalar currentFragCoord = + ivec4_scalar(fragCoord.x.x, fragCoord.y.x, fragCoord.z.x, fragCoord.w.x); + + const VectorType<int16_t, 16>* ditherNoiseYIndexed; + if (DITHER) { + ditherNoiseYIndexed = &ditherNoise[((uint32_t)fragCoord.y.x & 7) * 8]; + } + float invDelta, middleT, middleB; if (deltaDelta > 0) { invDelta = 1.0f / deltaDelta; @@ -2054,9 +2254,13 @@ static bool commitRadialGradientFromStops(sampler2D sampler, int offsetsAddress, // Figure out how many pixels belonging to whole chunks are inside the gradient // stop pair. int inside = int(endT - t) & ~3; - // Convert start and end colors to BGRA and scale to 0..255 range. - auto minColorF = stopColors[stopIndex].zyxw * 255.0f; - auto maxColorF = stopColors[stopIndex + 1].zyxw * 255.0f; + // Convert start and end colors to BGRA and scale to 0x00FF..0xFF00 range + // (for dithered) and 0.255 range (for non-dithered). + auto minColorF = + stopColors[stopIndex].zyxw * (DITHER ? float(0xFF00) : 255.0f); + auto maxColorF = + stopColors[stopIndex + 1].zyxw * (DITHER ? float(0xFF00) : 255.0f); + // Compute the change in color per change in gradient offset. auto deltaOffset = nextOffset - prevOffset; Float deltaColorF = @@ -2073,17 +2277,29 @@ static bool commitRadialGradientFromStops(sampler2D sampler, int offsetsAddress, Float colorF = minColorF - deltaColorF * (adjustedStartRadius + prevOffset); // Finally, walk over the span accumulating the position dot product and - // getting its sqrt as an offset into the color ramp. Since we're already - // in BGRA format and scaled to 255, we just need to round to an integer - // and pack down to pixel format. + // getting its sqrt as an offset into the color ramp. At this point we just + // need to round to an integer and pack down to pixel format. for (auto* end = buf + inside; buf < end; buf += 4) { Float offsetG = fastSqrt<false>(dotPos); - commit_blend_span<BLEND>( - buf, - combine(packRGBA8(round_pixel(colorF + deltaColorF * offsetG.x, 1), - round_pixel(colorF + deltaColorF * offsetG.y, 1)), - packRGBA8(round_pixel(colorF + deltaColorF * offsetG.z, 1), - round_pixel(colorF + deltaColorF * offsetG.w, 1)))); + if (DITHER) { + auto color = combine( + CONVERT(round_pixel(colorF + deltaColorF * offsetG.x, 1), U16), + CONVERT(round_pixel(colorF + deltaColorF * offsetG.y, 1), U16), + CONVERT(round_pixel(colorF + deltaColorF * offsetG.z, 1), U16), + CONVERT(round_pixel(colorF + deltaColorF * offsetG.w, 1), U16)); + commit_blend_span<BLEND>( + buf, + static_cast<WideRGBA8>( + dither(color, currentFragCoord, ditherNoiseYIndexed) >> 8)); + currentFragCoord.x += 4; + } else { + auto color = combine( + packRGBA8(round_pixel(colorF + deltaColorF * offsetG.x, 1), + round_pixel(colorF + deltaColorF * offsetG.y, 1)), + packRGBA8(round_pixel(colorF + deltaColorF * offsetG.z, 1), + round_pixel(colorF + deltaColorF * offsetG.w, 1))); + commit_blend_span<BLEND>(buf, color); + } dotPos += dotPosDelta; dotPosDelta += deltaDelta2; } @@ -2102,14 +2318,26 @@ static bool commitRadialGradientFromStops(sampler2D sampler, int offsetsAddress, // The logic here is similar to the full chunks loop above, but we do a // partial write instead of a pushing a full chunk. Float offsetG = fastSqrt<false>(dotPos); - commit_blend_span<BLEND>( - buf, - combine(packRGBA8(round_pixel(colorF + deltaColorF * offsetG.x, 1), - round_pixel(colorF + deltaColorF * offsetG.y, 1)), - packRGBA8(round_pixel(colorF + deltaColorF * offsetG.z, 1), - round_pixel(colorF + deltaColorF * offsetG.w, 1))), - remainder); - + if (DITHER) { + auto color = combine( + CONVERT(round_pixel(colorF + deltaColorF * offsetG.x, 1), U16), + CONVERT(round_pixel(colorF + deltaColorF * offsetG.y, 1), U16), + CONVERT(round_pixel(colorF + deltaColorF * offsetG.z, 1), U16), + CONVERT(round_pixel(colorF + deltaColorF * offsetG.w, 1), U16)); + commit_blend_span<BLEND>( + buf, + static_cast<WideRGBA8>( + dither(color, currentFragCoord, ditherNoiseYIndexed) >> 8), + remainder); + currentFragCoord.x += 4; + } else { + auto color = combine( + packRGBA8(round_pixel(colorF + deltaColorF * offsetG.x, 1), + round_pixel(colorF + deltaColorF * offsetG.y, 1)), + packRGBA8(round_pixel(colorF + deltaColorF * offsetG.z, 1), + round_pixel(colorF + deltaColorF * offsetG.w, 1))); + commit_blend_span<BLEND>(buf, color, remainder); + } buf += remainder; t += remainder; @@ -2149,18 +2377,37 @@ static bool commitRadialGradientFromStops(sampler2D sampler, int offsetsAddress, // swglcommitLinearGradient, but given a varying 2D position scaled to // gradient-space and a radius at which the distance from the origin maps to the // start of the gradient table. -#define swgl_commitRadialGradientRGBA8(sampler, address, size, repeat, pos, \ - radius) \ +#define swgl_commitRadialGradientRGBA8(sampler, address, size, repeat, pos, \ + radius) \ + do { \ + bool drawn = false; \ + if (blend_key) { \ + drawn = commitRadialGradient<true, false>( \ + sampler, address, size, repeat, pos, radius, swgl_OutRGBA8, \ + swgl_SpanLength, static_cast<vec4>(0x0)); \ + } else { \ + drawn = commitRadialGradient<false, false>( \ + sampler, address, size, repeat, pos, radius, swgl_OutRGBA8, \ + swgl_SpanLength, static_cast<vec4>(0x0)); \ + } \ + if (drawn) { \ + swgl_OutRGBA8 += swgl_SpanLength; \ + swgl_SpanLength = 0; \ + } \ + } while (0) + +#define swgl_commitDitheredRadialGradientRGBA8(sampler, address, size, repeat, \ + pos, radius, fragCoord) \ do { \ bool drawn = false; \ if (blend_key) { \ - drawn = \ - commitRadialGradient<true>(sampler, address, size, repeat, pos, \ - radius, swgl_OutRGBA8, swgl_SpanLength); \ + drawn = commitRadialGradient<true, true>(sampler, address, size, repeat, \ + pos, radius, swgl_OutRGBA8, \ + swgl_SpanLength, fragCoord); \ } else { \ - drawn = \ - commitRadialGradient<false>(sampler, address, size, repeat, pos, \ - radius, swgl_OutRGBA8, swgl_SpanLength); \ + drawn = commitRadialGradient<false, true>( \ + sampler, address, size, repeat, pos, radius, swgl_OutRGBA8, \ + swgl_SpanLength, fragCoord); \ } \ if (drawn) { \ swgl_OutRGBA8 += swgl_SpanLength; \ @@ -2172,23 +2419,45 @@ static bool commitRadialGradientFromStops(sampler2D sampler, int offsetsAddress, // swglcommitLinearGradient, but given a varying 2D position scaled to // gradient-space and a radius at which the distance from the origin maps to the // start of the gradient table. -#define swgl_commitRadialGradientFromStopsRGBA8( \ - sampler, offsetsAddress, colorsAddress, size, repeat, pos, startRadius) \ - do { \ - bool drawn = false; \ - if (blend_key) { \ - drawn = commitRadialGradientFromStops<true>( \ - sampler, offsetsAddress, colorsAddress, size, repeat, pos, startRadius, \ - swgl_OutRGBA8, swgl_SpanLength); \ - } else { \ - drawn = commitRadialGradientFromStops<false>( \ - sampler, offsetsAddress, colorsAddress, size, repeat, pos, startRadius, \ - swgl_OutRGBA8, swgl_SpanLength); \ - } \ - if (drawn) { \ - swgl_OutRGBA8 += swgl_SpanLength; \ - swgl_SpanLength = 0; \ - } \ +#define swgl_commitRadialGradientFromStopsRGBA8( \ + sampler, offsetsAddress, colorsAddress, size, repeat, pos, startRadius) \ + do { \ + bool drawn = false; \ + if (blend_key) { \ + drawn = commitRadialGradientFromStops<true, false>( \ + sampler, offsetsAddress, colorsAddress, size, repeat, pos, \ + startRadius, swgl_OutRGBA8, swgl_SpanLength, \ + static_cast<vec4>(0x0)); \ + } else { \ + drawn = commitRadialGradientFromStops<false, false>( \ + sampler, offsetsAddress, colorsAddress, size, repeat, pos, \ + startRadius, swgl_OutRGBA8, swgl_SpanLength, \ + static_cast<vec4>(0x0)); \ + } \ + if (drawn) { \ + swgl_OutRGBA8 += swgl_SpanLength; \ + swgl_SpanLength = 0; \ + } \ + } while (0) + +#define swgl_commitDitheredRadialGradientFromStopsRGBA8( \ + sampler, offsetsAddress, colorsAddress, size, repeat, pos, startRadius, \ + fragCoord) \ + do { \ + bool drawn = false; \ + if (blend_key) { \ + drawn = commitRadialGradientFromStops<true, true>( \ + sampler, offsetsAddress, colorsAddress, size, repeat, pos, \ + startRadius, swgl_OutRGBA8, swgl_SpanLength, fragCoord); \ + } else { \ + drawn = commitRadialGradientFromStops<false, true>( \ + sampler, offsetsAddress, colorsAddress, size, repeat, pos, \ + startRadius, swgl_OutRGBA8, swgl_SpanLength, fragCoord); \ + } \ + if (drawn) { \ + swgl_OutRGBA8 += swgl_SpanLength; \ + swgl_SpanLength = 0; \ + } \ } while (0) // Extension to set a clip mask image to be sampled during blending. The offset diff --git a/gfx/wr/webrender/res/brush_linear_gradient.glsl b/gfx/wr/webrender/res/brush_linear_gradient.glsl @@ -86,9 +86,13 @@ void swgl_drawSpanRGBA8() { if (address < 0) { return; } - +#ifdef WR_FEATURE_DITHERING + swgl_commitDitheredLinearGradientRGBA8(sGpuBufferF, address, GRADIENT_ENTRIES, true, v_gradient_repeat.x != 0.0, + v_pos, v_scale_dir, v_start_offset.x, gl_FragCoord); +#else swgl_commitLinearGradientRGBA8(sGpuBufferF, address, GRADIENT_ENTRIES, true, v_gradient_repeat.x != 0.0, v_pos, v_scale_dir, v_start_offset.x); +#endif } #endif diff --git a/gfx/wr/webrender/res/cs_radial_gradient.glsl b/gfx/wr/webrender/res/cs_radial_gradient.glsl @@ -63,8 +63,12 @@ void swgl_drawSpanRGBA8() { if (address < 0) { return; } - swgl_commitRadialGradientRGBA8(sGpuBufferF, address, GRADIENT_ENTRIES, v_gradient_repeat.x != 0.0, - v_pos, v_start_radius.x); +#ifdef WR_FEATURE_DITHERING + swgl_commitDitheredRadialGradientRGBA8(sGpuBufferF, address, GRADIENT_ENTRIES, v_gradient_repeat.x != 0.0, + v_pos, v_start_radius.x, gl_FragCoord); +#else + swgl_commitRadialGradientRGBA8(sGpuBufferF, address, GRADIENT_ENTRIES, v_gradient_repeat.x != 0.0, v_pos, v_start_radius.x); +#endif } #endif diff --git a/gfx/wr/webrender/res/ps_quad_gradient.glsl b/gfx/wr/webrender/res/ps_quad_gradient.glsl @@ -383,8 +383,13 @@ void swgl_drawSpanRGBA8() { float start_radius = v_flat_data.x; bool repeat = v_gradient_header.z != 0.0; +#ifdef WR_FEATURE_DITHERING + swgl_commitDitheredRadialGradientFromStopsRGBA8(sGpuBufferF, offsets_addr, colors_addr, + stop_count, repeat, pos, start_radius, gl_FragCoord); +#else swgl_commitRadialGradientFromStopsRGBA8(sGpuBufferF, offsets_addr, colors_addr, stop_count, repeat, pos, start_radius); +#endif } #endif diff --git a/gfx/wr/webrender/res/ps_quad_radial_gradient.glsl b/gfx/wr/webrender/res/ps_quad_radial_gradient.glsl @@ -73,8 +73,12 @@ void swgl_drawSpanRGBA8() { if (address < 0) { return; } - swgl_commitRadialGradientRGBA8(sGpuBufferF, address, GRADIENT_ENTRIES, v_gradient_repeat.x != 0.0, - v_pos, v_start_radius.x); +#ifdef WR_FEATURE_DITHERING + swgl_commitDitheredRadialGradientRGBA8(sGpuBufferF, address, GRADIENT_ENTRIES, v_gradient_repeat.x != 0.0, + v_pos, v_start_radius.x, gl_FragCoord); +#else + swgl_commitRadialGradientRGBA8(sGpuBufferF, address, GRADIENT_ENTRIES, v_gradient_repeat.x != 0.0, v_pos, v_start_radius.x); +#endif } #endif