mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[Impeller] Use 32 bit for positional stuff in the rrect blur (flutter/engine#42797)
Resolves https://github.com/flutter/flutter/issues/128410. 16 bit floats aren't precise enough for positional computations, like computing SDFs in the case of the RRect blur.
This commit is contained in:
parent
4114b30b01
commit
072ed208cf
@ -52,7 +52,12 @@ f16vec2 IPVec2GaussianIntegral(f16vec2 x, float16_t sigma) {
|
||||
}
|
||||
|
||||
/// Simpler (but less accurate) approximation of the Gaussian integral.
|
||||
f16vec2 IPVec2FastGaussianIntegral(f16vec2 x, float16_t sigma) {
|
||||
vec2 IPVec2FastGaussianIntegral(vec2 x, float sigma) {
|
||||
return 1.0 / (1.0 + exp(-kSqrtThree / sigma * x));
|
||||
}
|
||||
|
||||
/// Simpler (but less accurate) approximation of the Gaussian integral.
|
||||
f16vec2 IPHalfVec2FastGaussianIntegral(f16vec2 x, float16_t sigma) {
|
||||
return 1.0hf / (1.0hf + exp(float16_t(-kSqrtThree) / sigma * x));
|
||||
}
|
||||
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
|
||||
uniform FragInfo {
|
||||
f16vec4 color;
|
||||
f16vec2 rect_size;
|
||||
float16_t blur_sigma;
|
||||
float16_t corner_radius;
|
||||
vec2 rect_size;
|
||||
float blur_sigma;
|
||||
float corner_radius;
|
||||
}
|
||||
frag_info;
|
||||
|
||||
@ -19,50 +19,47 @@ out f16vec4 frag_color;
|
||||
|
||||
const int kSampleCount = 4;
|
||||
|
||||
float16_t RRectDistance(f16vec2 sample_position, f16vec2 half_size) {
|
||||
f16vec2 space = abs(sample_position) - half_size + frag_info.corner_radius;
|
||||
return length(max(space, float16_t(0.0hf))) +
|
||||
min(max(space.x, space.y), float16_t(0.0hf)) - frag_info.corner_radius;
|
||||
float16_t RRectDistance(vec2 sample_position, vec2 half_size) {
|
||||
vec2 space = abs(sample_position) - half_size + frag_info.corner_radius;
|
||||
return float16_t(length(max(space, 0.0)) + min(max(space.x, space.y), 0.0) -
|
||||
frag_info.corner_radius);
|
||||
}
|
||||
|
||||
/// Closed form unidirectional rounded rect blur mask solution using the
|
||||
/// analytical Gaussian integral (with approximated erf).
|
||||
float16_t RRectBlurX(f16vec2 sample_position, f16vec2 half_size) {
|
||||
float RRectBlurX(vec2 sample_position, vec2 half_size) {
|
||||
// Compute the X direction distance field (not incorporating the Y distance)
|
||||
// for the rounded rect.
|
||||
float16_t space =
|
||||
min(float16_t(0.0hf),
|
||||
half_size.y - frag_info.corner_radius - abs(sample_position.y));
|
||||
float16_t rrect_distance =
|
||||
float space =
|
||||
min(0.0, half_size.y - frag_info.corner_radius - abs(sample_position.y));
|
||||
float rrect_distance =
|
||||
half_size.x - frag_info.corner_radius +
|
||||
sqrt(max(
|
||||
float16_t(0.0hf),
|
||||
frag_info.corner_radius * frag_info.corner_radius - space * space));
|
||||
sqrt(max(0.0, frag_info.corner_radius * frag_info.corner_radius -
|
||||
space * space));
|
||||
|
||||
// Map the linear distance field to the approximate Gaussian integral.
|
||||
f16vec2 integral = IPVec2FastGaussianIntegral(
|
||||
sample_position.x + f16vec2(-rrect_distance, rrect_distance),
|
||||
frag_info.blur_sigma);
|
||||
vec2 integral = IPVec2FastGaussianIntegral(
|
||||
float(sample_position.x) + vec2(-rrect_distance, rrect_distance),
|
||||
float(frag_info.blur_sigma));
|
||||
return integral.y - integral.x;
|
||||
}
|
||||
|
||||
float16_t RRectBlur(f16vec2 sample_position, f16vec2 half_size) {
|
||||
float RRectBlur(vec2 sample_position, vec2 half_size) {
|
||||
// Limit the sampling range to 3 standard deviations in the Y direction from
|
||||
// the kernel center to incorporate 99.7% of the color contribution.
|
||||
float16_t half_sampling_range = frag_info.blur_sigma * 3.0hf;
|
||||
float half_sampling_range = frag_info.blur_sigma * 3.0;
|
||||
|
||||
float16_t begin_y =
|
||||
max(-half_sampling_range, sample_position.y - half_size.y);
|
||||
float16_t end_y = min(half_sampling_range, sample_position.y + half_size.y);
|
||||
float16_t interval = (end_y - begin_y) / float16_t(kSampleCount);
|
||||
float begin_y = max(-half_sampling_range, sample_position.y - half_size.y);
|
||||
float end_y = min(half_sampling_range, sample_position.y + half_size.y);
|
||||
float interval = (end_y - begin_y) / kSampleCount;
|
||||
|
||||
// Sample the X blur kSampleCount times, weighted by the Gaussian function.
|
||||
float16_t result = 0.0hf;
|
||||
float result = 0.0;
|
||||
for (int sample_i = 0; sample_i < kSampleCount; sample_i++) {
|
||||
float16_t y = begin_y + interval * (float16_t(sample_i) + 0.5hf);
|
||||
result += RRectBlurX(f16vec2(sample_position.x, sample_position.y - y),
|
||||
half_size) *
|
||||
IPHalfGaussian(y, frag_info.blur_sigma) * interval;
|
||||
float y = begin_y + interval * (float(sample_i) + 0.5);
|
||||
result +=
|
||||
RRectBlurX(vec2(sample_position.x, sample_position.y - y), half_size) *
|
||||
IPGaussian(float(y), float(frag_info.blur_sigma)) * interval;
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -71,12 +68,12 @@ float16_t RRectBlur(f16vec2 sample_position, f16vec2 half_size) {
|
||||
void main() {
|
||||
frag_color = frag_info.color;
|
||||
|
||||
f16vec2 half_size = frag_info.rect_size * 0.5hf;
|
||||
f16vec2 sample_position = f16vec2(v_position) - half_size;
|
||||
vec2 half_size = frag_info.rect_size * 0.5;
|
||||
vec2 sample_position = v_position - half_size;
|
||||
|
||||
if (frag_info.blur_sigma > 0.0hf) {
|
||||
frag_color *= RRectBlur(sample_position, half_size);
|
||||
if (frag_info.blur_sigma > 0.0) {
|
||||
frag_color *= float16_t(RRectBlur(sample_position, half_size));
|
||||
} else {
|
||||
frag_color *= -RRectDistance(sample_position, half_size);
|
||||
frag_color *= float16_t(-RRectDistance(sample_position, half_size));
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user