From ab5cbe1c52ae0b00a8c006102f085077f81f1b2d Mon Sep 17 00:00:00 2001 From: ColdPaleLight Date: Fri, 16 Sep 2022 02:17:17 +0800 Subject: [PATCH] [Impeller] Make gradients work as expected (flutter/engine#36140) --- .../compiler/shader_lib/impeller/texture.glsl | 44 +++++++++++++++++++ .../contents/linear_gradient_contents.cc | 2 + .../contents/radial_gradient_contents.cc | 2 + .../contents/sweep_gradient_contents.cc | 2 + .../entity/shaders/linear_gradient_fill.frag | 5 ++- .../entity/shaders/radial_gradient_fill.frag | 5 ++- .../entity/shaders/sweep_gradient_fill.frag | 5 ++- 7 files changed, 59 insertions(+), 6 deletions(-) diff --git a/engine/src/flutter/impeller/compiler/shader_lib/impeller/texture.glsl b/engine/src/flutter/impeller/compiler/shader_lib/impeller/texture.glsl index 633af1cd281..447bedc2215 100644 --- a/engine/src/flutter/impeller/compiler/shader_lib/impeller/texture.glsl +++ b/engine/src/flutter/impeller/compiler/shader_lib/impeller/texture.glsl @@ -19,6 +19,18 @@ vec4 IPSample(sampler2D texture_sampler, vec2 coords, float y_coord_scale) { return texture(texture_sampler, coords); } +/// Sample from a texture. +/// +/// If `y_coord_scale` < 0.0, the Y coordinate is flipped. This is useful +/// for Impeller graphics backends that use a flipped framebuffer coordinate +/// space. +/// The range of `coods` will be mapped from [0, 1] to [half_texel, 1 - half_texel] +vec4 IPSampleLinear(sampler2D texture_sampler, vec2 coords, float y_coord_scale, vec2 half_texel) { + coords.x = mix(half_texel.x, 1 - half_texel.x, coords.x); + coords.y = mix(half_texel.y, 1 - half_texel.y, coords.y); + return IPSample(texture_sampler, coords, y_coord_scale); +} + // These values must correspond to the order of the items in the // 'Entity::TileMode' enum class. const float kTileModeClamp = 0; @@ -81,4 +93,36 @@ vec4 IPSampleWithTileMode(sampler2D tex, return IPSampleWithTileMode(tex, coords, y_coord_scale, tile_mode, tile_mode); } +/// Sample a texture, emulating a specific tile mode. +/// +/// This is useful for Impeller graphics backend that don't have native support +/// for Decal. +/// The range of `coods` will be mapped from [0, 1] to [half_texel, 1 - half_texel] +vec4 IPSampleLinearWithTileMode(sampler2D tex, + vec2 coords, + float y_coord_scale, + vec2 half_texel, + float x_tile_mode, + float y_tile_mode) { + if (x_tile_mode == kTileModeDecal && (coords.x < 0 || coords.x >= 1) || + y_tile_mode == kTileModeDecal && (coords.y < 0 || coords.y >= 1)) { + return vec4(0); + } + + return IPSampleLinear(tex, IPVec2Tile(coords, x_tile_mode, y_tile_mode), y_coord_scale, half_texel); +} + +/// Sample a texture, emulating a specific tile mode. +/// +/// This is useful for Impeller graphics backend that don't have native support +/// for Decal. +/// The range of `coods` will be mapped from [0, 1] to [half_texel, 1 - half_texel] +vec4 IPSampleLinearWithTileMode(sampler2D tex, + vec2 coords, + float y_coord_scale, + vec2 half_texel, + float tile_mode) { + return IPSampleLinearWithTileMode(tex, coords, y_coord_scale, half_texel, tile_mode, tile_mode); +} + #endif diff --git a/engine/src/flutter/impeller/entity/contents/linear_gradient_contents.cc b/engine/src/flutter/impeller/entity/contents/linear_gradient_contents.cc index 55fd5a37c87..18fdf1fd601 100644 --- a/engine/src/flutter/impeller/entity/contents/linear_gradient_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/linear_gradient_contents.cc @@ -64,6 +64,8 @@ bool LinearGradientContents::Render(const ContentContext& renderer, gradient_info.texture_sampler_y_coord_scale = gradient_texture->GetYCoordScale(); gradient_info.alpha = GetAlpha(); + gradient_info.half_texel = Vector2(0.5 / gradient_texture->GetSize().width, + 0.5 / gradient_texture->GetSize().height); VS::FrameInfo frame_info; frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * diff --git a/engine/src/flutter/impeller/entity/contents/radial_gradient_contents.cc b/engine/src/flutter/impeller/entity/contents/radial_gradient_contents.cc index 7d0141a763f..4a82933ff18 100644 --- a/engine/src/flutter/impeller/entity/contents/radial_gradient_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/radial_gradient_contents.cc @@ -63,6 +63,8 @@ bool RadialGradientContents::Render(const ContentContext& renderer, gradient_info.texture_sampler_y_coord_scale = gradient_texture->GetYCoordScale(); gradient_info.alpha = GetAlpha(); + gradient_info.half_texel = Vector2(0.5 / gradient_texture->GetSize().width, + 0.5 / gradient_texture->GetSize().height); VS::FrameInfo frame_info; frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * diff --git a/engine/src/flutter/impeller/entity/contents/sweep_gradient_contents.cc b/engine/src/flutter/impeller/entity/contents/sweep_gradient_contents.cc index 80dd6f08f69..dfeff8e9260 100644 --- a/engine/src/flutter/impeller/entity/contents/sweep_gradient_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/sweep_gradient_contents.cc @@ -70,6 +70,8 @@ bool SweepGradientContents::Render(const ContentContext& renderer, gradient_texture->GetYCoordScale(); gradient_info.tile_mode = static_cast(tile_mode_); gradient_info.alpha = GetAlpha(); + gradient_info.half_texel = Vector2(0.5 / gradient_texture->GetSize().width, + 0.5 / gradient_texture->GetSize().height); VS::FrameInfo frame_info; frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * diff --git a/engine/src/flutter/impeller/entity/shaders/linear_gradient_fill.frag b/engine/src/flutter/impeller/entity/shaders/linear_gradient_fill.frag index 8067182ab69..577dd7dbb47 100644 --- a/engine/src/flutter/impeller/entity/shaders/linear_gradient_fill.frag +++ b/engine/src/flutter/impeller/entity/shaders/linear_gradient_fill.frag @@ -12,6 +12,7 @@ uniform GradientInfo { float tile_mode; float texture_sampler_y_coord_scale; float alpha; + vec2 half_texel; } gradient_info; in vec2 v_position; @@ -25,11 +26,11 @@ void main() { gradient_info.end_point - gradient_info.start_point ); float t = dot / (len * len); - frag_color = IPSampleWithTileMode( + frag_color = IPSampleLinearWithTileMode( texture_sampler, vec2(t, 0.5), gradient_info.texture_sampler_y_coord_scale, - gradient_info.tile_mode, + gradient_info.half_texel, gradient_info.tile_mode); frag_color = vec4(frag_color.xyz * frag_color.a, frag_color.a) * gradient_info.alpha; } diff --git a/engine/src/flutter/impeller/entity/shaders/radial_gradient_fill.frag b/engine/src/flutter/impeller/entity/shaders/radial_gradient_fill.frag index 541b3a6b093..31d3ae24636 100644 --- a/engine/src/flutter/impeller/entity/shaders/radial_gradient_fill.frag +++ b/engine/src/flutter/impeller/entity/shaders/radial_gradient_fill.frag @@ -12,6 +12,7 @@ uniform GradientInfo { float tile_mode; float texture_sampler_y_coord_scale; float alpha; + vec2 half_texel; } gradient_info; in vec2 v_position; @@ -21,11 +22,11 @@ out vec4 frag_color; void main() { float len = length(v_position - gradient_info.center); float t = len / gradient_info.radius; - frag_color = IPSampleWithTileMode( + frag_color = IPSampleLinearWithTileMode( texture_sampler, vec2(t, 0.5), gradient_info.texture_sampler_y_coord_scale, - gradient_info.tile_mode, + gradient_info.half_texel, gradient_info.tile_mode); frag_color = vec4(frag_color.xyz * frag_color.a, frag_color.a) * gradient_info.alpha; } diff --git a/engine/src/flutter/impeller/entity/shaders/sweep_gradient_fill.frag b/engine/src/flutter/impeller/entity/shaders/sweep_gradient_fill.frag index c6d9ed38a37..2fe042dd2c8 100644 --- a/engine/src/flutter/impeller/entity/shaders/sweep_gradient_fill.frag +++ b/engine/src/flutter/impeller/entity/shaders/sweep_gradient_fill.frag @@ -14,6 +14,7 @@ uniform GradientInfo { float tile_mode; float texture_sampler_y_coord_scale; float alpha; + vec2 half_texel; } gradient_info; in vec2 v_position; @@ -25,11 +26,11 @@ void main() { float angle = atan(-coord.y, -coord.x); float t = (angle * k1Over2Pi + 0.5 + gradient_info.bias) * gradient_info.scale; - frag_color = IPSampleWithTileMode( + frag_color = IPSampleLinearWithTileMode( texture_sampler, vec2(t, 0.5), gradient_info.texture_sampler_y_coord_scale, - gradient_info.tile_mode, + gradient_info.half_texel, gradient_info.tile_mode); frag_color = vec4(frag_color.xyz * frag_color.a, frag_color.a) * gradient_info.alpha; }