-
Notifications
You must be signed in to change notification settings - Fork 6k
[Impeller] Fix GLES gaussian implementation. #55329
Changes from 4 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -325,7 +325,7 @@ DownsamplePassArgs CalculateDownsamplePassArgs( | |||||
| fml::StatusOr<RenderTarget> MakeDownsampleSubpass( | ||||||
| const ContentContext& renderer, | ||||||
| const std::shared_ptr<CommandBuffer>& command_buffer, | ||||||
| std::shared_ptr<Texture> input_texture, | ||||||
| const std::shared_ptr<Texture>& input_texture, | ||||||
| const SamplerDescriptor& sampler_descriptor, | ||||||
| const DownsamplePassArgs& pass_args, | ||||||
| Entity::TileMode tile_mode) { | ||||||
|
|
@@ -345,7 +345,8 @@ fml::StatusOr<RenderTarget> MakeDownsampleSubpass( | |||||
|
|
||||||
| TextureFillVertexShader::FrameInfo frame_info; | ||||||
| frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); | ||||||
| frame_info.texture_sampler_y_coord_scale = 1.0; | ||||||
| frame_info.texture_sampler_y_coord_scale = | ||||||
| input_texture->GetYCoordScale(); | ||||||
|
|
||||||
| TextureFillFragmentShader::FragInfo frag_info; | ||||||
| frag_info.alpha = 1.0; | ||||||
|
|
@@ -398,7 +399,8 @@ fml::StatusOr<RenderTarget> MakeDownsampleSubpass( | |||||
|
|
||||||
| TextureFillVertexShader::FrameInfo frame_info; | ||||||
| frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)); | ||||||
| frame_info.texture_sampler_y_coord_scale = 1.0; | ||||||
| frame_info.texture_sampler_y_coord_scale = | ||||||
| input_texture->GetYCoordScale(); | ||||||
|
|
||||||
| TextureDownsampleFragmentShader::FragInfo frag_info; | ||||||
| frag_info.edge = edge; | ||||||
|
|
@@ -447,16 +449,18 @@ fml::StatusOr<RenderTarget> MakeBlurSubpass( | |||||
| return input_pass; | ||||||
| } | ||||||
|
|
||||||
| std::shared_ptr<Texture> input_texture = input_pass.GetRenderTargetTexture(); | ||||||
| const std::shared_ptr<Texture>& input_texture = | ||||||
| input_pass.GetRenderTargetTexture(); | ||||||
|
|
||||||
| // TODO(gaaclarke): This blurs the whole image, but because we know the clip | ||||||
| // region we could focus on just blurring that. | ||||||
| ISize subpass_size = input_texture->GetSize(); | ||||||
| ContentContext::SubpassCallback subpass_callback = | ||||||
| [&](const ContentContext& renderer, RenderPass& pass) { | ||||||
| GaussianBlurVertexShader::FrameInfo frame_info{ | ||||||
| .mvp = Matrix::MakeOrthographic(ISize(1, 1)), | ||||||
| .texture_sampler_y_coord_scale = 1.0}; | ||||||
| GaussianBlurVertexShader::FrameInfo frame_info; | ||||||
| frame_info.mvp = Matrix::MakeOrthographic(ISize(1, 1)), | ||||||
| frame_info.texture_sampler_y_coord_scale = | ||||||
| input_texture->GetYCoordScale(); | ||||||
|
|
||||||
| HostBuffer& host_buffer = renderer.GetTransientsBuffer(); | ||||||
|
|
||||||
|
|
@@ -481,11 +485,9 @@ fml::StatusOr<RenderTarget> MakeBlurSubpass( | |||||
| linear_sampler_descriptor)); | ||||||
| GaussianBlurVertexShader::BindFrameInfo( | ||||||
| pass, host_buffer.EmplaceUniform(frame_info)); | ||||||
| GaussianBlurPipeline::FragmentShader::KernelSamples kernel_samples = | ||||||
| LerpHackKernelSamples(GenerateBlurInfo(blur_info)); | ||||||
| FML_CHECK(kernel_samples.sample_count <= kGaussianBlurMaxKernelSize); | ||||||
| GaussianBlurFragmentShader::BindKernelSamples( | ||||||
| pass, host_buffer.EmplaceUniform(kernel_samples)); | ||||||
| GaussianBlurFragmentShader::BindBlurInfo( | ||||||
| pass, host_buffer.EmplaceUniform( | ||||||
| LerpHackKernelSamples(GenerateBlurInfo(blur_info)))); | ||||||
| return pass.Draw().ok(); | ||||||
| }; | ||||||
| if (destination_target.has_value()) { | ||||||
|
|
@@ -898,7 +900,7 @@ KernelSamples GenerateBlurInfo(BlurParameters parameters) { | |||||
| Scalar tally = 0.0f; | ||||||
| for (int i = 0; i < result.sample_count; ++i) { | ||||||
| int x = x_offset + (i * parameters.step_size) - parameters.blur_radius; | ||||||
| result.samples[i] = GaussianBlurPipeline::FragmentShader::KernelSample{ | ||||||
| result.samples[i] = KernelSample{ | ||||||
| .uv_offset = parameters.blur_uv_offset * x, | ||||||
| .coefficient = expf(-0.5f * (x * x) / | ||||||
| (parameters.blur_sigma * parameters.blur_sigma)) / | ||||||
|
|
@@ -917,27 +919,31 @@ KernelSamples GenerateBlurInfo(BlurParameters parameters) { | |||||
|
|
||||||
| // This works by shrinking the kernel size by 2 and relying on lerp to read | ||||||
| // between the samples. | ||||||
| GaussianBlurPipeline::FragmentShader::KernelSamples LerpHackKernelSamples( | ||||||
| GaussianBlurPipeline::FragmentShader::BlurInfo LerpHackKernelSamples( | ||||||
| KernelSamples parameters) { | ||||||
| GaussianBlurPipeline::FragmentShader::KernelSamples result; | ||||||
| GaussianBlurPipeline::FragmentShader::BlurInfo result = {}; | ||||||
| result.sample_count = ((parameters.sample_count - 1) / 2) + 1; | ||||||
| int32_t middle = result.sample_count / 2; | ||||||
| int32_t j = 0; | ||||||
| FML_DCHECK(result.sample_count <= kGaussianBlurMaxKernelSize); | ||||||
|
|
||||||
| for (int i = 0; i < result.sample_count; i++) { | ||||||
| if (i == middle) { | ||||||
| result.samples[i] = parameters.samples[j++]; | ||||||
| result.sample_data[i].x = parameters.samples[j].uv_offset.x; | ||||||
| result.sample_data[i].y = parameters.samples[j].uv_offset.y; | ||||||
| result.sample_data[i].z = parameters.samples[j].coefficient; | ||||||
| j++; | ||||||
| } else { | ||||||
| GaussianBlurPipeline::FragmentShader::KernelSample left = | ||||||
| parameters.samples[j]; | ||||||
| GaussianBlurPipeline::FragmentShader::KernelSample right = | ||||||
| parameters.samples[j + 1]; | ||||||
| result.samples[i] = GaussianBlurPipeline::FragmentShader::KernelSample{ | ||||||
| .uv_offset = (left.uv_offset * left.coefficient + | ||||||
| right.uv_offset * right.coefficient) / | ||||||
| (left.coefficient + right.coefficient), | ||||||
| .coefficient = left.coefficient + right.coefficient, | ||||||
| }; | ||||||
| KernelSample left = parameters.samples[j]; | ||||||
| KernelSample right = parameters.samples[j + 1]; | ||||||
|
|
||||||
| result.sample_data[i].z = left.coefficient + right.coefficient; | ||||||
|
|
||||||
| auto uv = (left.uv_offset * left.coefficient + | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||
| right.uv_offset * right.coefficient) / | ||||||
| (left.coefficient + right.coefficient); | ||||||
| result.sample_data[i].x = uv.x; | ||||||
| result.sample_data[i].y = uv.y; | ||||||
| j += 2; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -508,27 +508,24 @@ TEST(GaussianBlurFilterContentsTest, LerpHackKernelSamplesSimple) { | |||||
| }, | ||||||
| }; | ||||||
|
|
||||||
| GaussianBlurPipeline::FragmentShader::KernelSamples fast_kernel_samples = | ||||||
| GaussianBlurPipeline::FragmentShader::BlurInfo blur_info = | ||||||
| LerpHackKernelSamples(kernel_samples); | ||||||
| EXPECT_EQ(fast_kernel_samples.sample_count, 3); | ||||||
| EXPECT_EQ(blur_info.sample_count, 3); | ||||||
|
|
||||||
| GaussianBlurPipeline::FragmentShader::KernelSample* samples = | ||||||
| kernel_samples.samples; | ||||||
| GaussianBlurPipeline::FragmentShader::KernelSample* fast_samples = | ||||||
| fast_kernel_samples.samples; | ||||||
| KernelSample* samples = kernel_samples.samples; | ||||||
|
|
||||||
| ////////////////////////////////////////////////////////////////////////////// | ||||||
| // Check output kernel. | ||||||
|
|
||||||
| EXPECT_FLOAT_EQ(fast_samples[0].uv_offset.x, -1.3333333); | ||||||
| EXPECT_FLOAT_EQ(fast_samples[0].uv_offset.y, 0); | ||||||
| EXPECT_FLOAT_EQ(fast_samples[0].coefficient, 0.3); | ||||||
| EXPECT_FLOAT_EQ(fast_samples[1].uv_offset.x, 0); | ||||||
| EXPECT_FLOAT_EQ(fast_samples[1].uv_offset.y, 0); | ||||||
| EXPECT_FLOAT_EQ(fast_samples[1].coefficient, 0.4); | ||||||
| EXPECT_FLOAT_EQ(fast_samples[2].uv_offset.x, 1.3333333); | ||||||
| EXPECT_FLOAT_EQ(fast_samples[2].uv_offset.y, 0); | ||||||
| EXPECT_FLOAT_EQ(fast_samples[2].coefficient, 0.3); | ||||||
| EXPECT_FLOAT_EQ(blur_info.sample_data[0].x, -1.3333333); | ||||||
| EXPECT_FLOAT_EQ(blur_info.sample_data[0].y, 0); | ||||||
| EXPECT_FLOAT_EQ(blur_info.sample_data[0].z, 0.3); | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I think we would be better off not losing the names of values. It's very confusing to have to remember that Same thing for
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||||||
| EXPECT_FLOAT_EQ(blur_info.sample_data[1].x, 0); | ||||||
| EXPECT_FLOAT_EQ(blur_info.sample_data[1].y, 0); | ||||||
| EXPECT_FLOAT_EQ(blur_info.sample_data[1].z, 0.4); | ||||||
| EXPECT_FLOAT_EQ(blur_info.sample_data[2].x, 1.3333333); | ||||||
| EXPECT_FLOAT_EQ(blur_info.sample_data[2].y, 0); | ||||||
| EXPECT_FLOAT_EQ(blur_info.sample_data[2].z, 0.3); | ||||||
|
|
||||||
| ////////////////////////////////////////////////////////////////////////////// | ||||||
| // Check output of fast kernel versus original kernel. | ||||||
|
|
@@ -549,11 +546,11 @@ TEST(GaussianBlurFilterContentsTest, LerpHackKernelSamplesSimple) { | |||||
| } | ||||||
| }; | ||||||
| Scalar fast_output = | ||||||
| /*1st*/ lerp(fast_samples[0].uv_offset, data[0], data[1]) * | ||||||
| fast_samples[0].coefficient + | ||||||
| /*2nd*/ data[2] * fast_samples[1].coefficient + | ||||||
| /*3rd*/ lerp(fast_samples[2].uv_offset, data[3], data[4]) * | ||||||
| fast_samples[2].coefficient; | ||||||
| /*1st*/ lerp(blur_info.sample_data[0].xy(), data[0], data[1]) * | ||||||
| blur_info.sample_data[0].z + | ||||||
| /*2nd*/ data[2] * blur_info.sample_data[1].z + | ||||||
| /*3rd*/ lerp(blur_info.sample_data[2].xy(), data[3], data[4]) * | ||||||
| blur_info.sample_data[2].z; | ||||||
|
|
||||||
| EXPECT_NEAR(original_output, fast_output, 0.01); | ||||||
| } | ||||||
|
|
@@ -568,7 +565,7 @@ TEST(GaussianBlurFilterContentsTest, LerpHackKernelSamplesComplex) { | |||||
| .step_size = 1}; | ||||||
| KernelSamples kernel_samples = GenerateBlurInfo(parameters); | ||||||
| EXPECT_EQ(kernel_samples.sample_count, 33); | ||||||
| GaussianBlurPipeline::FragmentShader::KernelSamples fast_kernel_samples = | ||||||
| GaussianBlurPipeline::FragmentShader::BlurInfo fast_kernel_samples = | ||||||
| LerpHackKernelSamples(kernel_samples); | ||||||
| EXPECT_EQ(fast_kernel_samples.sample_count, 17); | ||||||
| float data[33]; | ||||||
|
|
@@ -604,9 +601,9 @@ TEST(GaussianBlurFilterContentsTest, LerpHackKernelSamplesComplex) { | |||||
| } | ||||||
|
|
||||||
| Scalar fast_output = 0.0; | ||||||
| for (int i = 0; i < fast_kernel_samples.sample_count; ++i) { | ||||||
| auto sample = fast_kernel_samples.samples[i]; | ||||||
| fast_output += sample.coefficient * sampler(sample.uv_offset); | ||||||
| for (int i = 0; i < fast_kernel_samples.sample_count; i++) { | ||||||
| fast_output += fast_kernel_samples.sample_data[i].z * | ||||||
| sampler(fast_kernel_samples.sample_data[i].xy()); | ||||||
| } | ||||||
|
|
||||||
| EXPECT_NEAR(output, fast_output, 0.1); | ||||||
|
|
@@ -621,7 +618,7 @@ TEST(GaussianBlurFilterContentsTest, ChopHugeBlurs) { | |||||
| .blur_radius = blur_radius, | ||||||
| .step_size = 1}; | ||||||
| KernelSamples kernel_samples = GenerateBlurInfo(parameters); | ||||||
| GaussianBlurPipeline::FragmentShader::KernelSamples frag_kernel_samples = | ||||||
| GaussianBlurPipeline::FragmentShader::BlurInfo frag_kernel_samples = | ||||||
| LerpHackKernelSamples(kernel_samples); | ||||||
| EXPECT_TRUE(frag_kernel_samples.sample_count <= kGaussianBlurMaxKernelSize); | ||||||
| } | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -57,6 +57,7 @@ class BufferBindingsGLES { | |
| std::vector<VertexAttribPointer> vertex_attrib_arrays_; | ||
|
|
||
| std::unordered_map<std::string, GLint> uniform_locations_; | ||
| std::vector<uint8_t> array_element_buffer_; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why are we adding this as an instance variable? I'd rather keep it localized to where it is used to avoid stale data bugs.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was trying to avoid creating the empty vector, since I can't safely conditionally initialize it. But that is probably not a worthwhile change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed |
||
|
|
||
| using BindingMap = std::unordered_map<std::string, std::vector<GLint>>; | ||
| BindingMap binding_map_ = {}; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please bring back this check. It's important because we'll crash anyways if we violate this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is checked in the LerpHackFunction too, I don't think the count can change after that point?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay yea, in that case I'd rather keep this one here since it is the final point before we'd actually get the error. We can remove the lerp hack one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But the kernel samples UBO has a std::array of a fixed size. It cannot be greater than kGaussianBlurMaxKernelSize
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could static assert this though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yea, the
sample_countisn't even consequential here because emplace isn't looking at it. Lets keep the runtime DCHECK in the lerp hack and a static assert sounds good to me.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done