-
Notifications
You must be signed in to change notification settings - Fork 6k
[Impeller] Implement RuntimeEffectContents #36866
Changes from 5 commits
730474c
4f633ee
99a9b58
d6ddae3
c7fbde9
08227c8
cba7506
ecf021a
da4b6bc
3310ce8
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 |
|---|---|---|
|
|
@@ -5,11 +5,14 @@ | |
| #include "impeller/display_list/display_list_dispatcher.h" | ||
|
|
||
| #include <algorithm> | ||
| #include <cstring> | ||
| #include <memory> | ||
| #include <optional> | ||
| #include <unordered_map> | ||
|
|
||
| #include "display_list/display_list_blend_mode.h" | ||
| #include "display_list/display_list_color_filter.h" | ||
| #include "display_list/display_list_color_source.h" | ||
| #include "display_list/display_list_path_effect.h" | ||
| #include "display_list/display_list_tile_mode.h" | ||
| #include "flutter/fml/logging.h" | ||
|
|
@@ -21,6 +24,7 @@ | |
| #include "impeller/entity/contents/filters/inputs/filter_input.h" | ||
| #include "impeller/entity/contents/linear_gradient_contents.h" | ||
| #include "impeller/entity/contents/radial_gradient_contents.h" | ||
| #include "impeller/entity/contents/runtime_effect_contents.h" | ||
| #include "impeller/entity/contents/solid_stroke_contents.h" | ||
| #include "impeller/entity/contents/sweep_gradient_contents.h" | ||
| #include "impeller/entity/contents/tiled_texture_contents.h" | ||
|
|
@@ -426,7 +430,26 @@ void DisplayListDispatcher::setColorSource( | |
| return; | ||
| } | ||
| case flutter::DlColorSourceType::kConicalGradient: | ||
| case flutter::DlColorSourceType::kRuntimeEffect: | ||
| case flutter::DlColorSourceType::kRuntimeEffect: { | ||
| const flutter::DlRuntimeEffectColorSource* runtime_effect_color_source = | ||
| source->asRuntimeEffect(); | ||
| auto runtime_stage = | ||
| runtime_effect_color_source->runtime_effect()->runtime_stage(); | ||
| auto uniform_data_sk = runtime_effect_color_source->uniform_data(); | ||
|
|
||
| paint_.color_source = [runtime_stage, uniform_data_sk]() { | ||
| // TODO(bdero): Get rid of the allocation + copy for uniform data. | ||
|
Contributor
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. Please file an issue for this TODO.
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: flutter/flutter#113714 |
||
| std::vector<uint8_t> uniform_data; | ||
| uniform_data.resize(uniform_data_sk->size()); | ||
| memcpy(uniform_data.data(), uniform_data_sk->bytes(), | ||
| uniform_data.size()); | ||
|
|
||
| auto contents = std::make_shared<RuntimeEffectContents>(); | ||
| contents->SetRuntimeStage(runtime_stage); | ||
| contents->SetUniformData(std::move(uniform_data)); | ||
| return contents; | ||
| }; | ||
| } | ||
| case flutter::DlColorSourceType::kUnknown: | ||
| UNIMPLEMENTED; | ||
| break; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| // Copyright 2013 The Flutter Authors. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|
|
||
| #include "impeller/entity/contents/runtime_effect_contents.h" | ||
|
|
||
| #include <future> | ||
| #include <memory> | ||
|
|
||
| #include "flutter/fml/logging.h" | ||
| #include "flutter/fml/make_copyable.h" | ||
| #include "impeller/base/validation.h" | ||
| #include "impeller/entity/contents/clip_contents.h" | ||
| #include "impeller/entity/contents/content_context.h" | ||
| #include "impeller/entity/position_no_color.vert.h" | ||
| #include "impeller/renderer/formats.h" | ||
| #include "impeller/renderer/pipeline_library.h" | ||
| #include "impeller/renderer/render_pass.h" | ||
| #include "impeller/renderer/shader_function.h" | ||
| #include "impeller/renderer/shader_types.h" | ||
|
|
||
| namespace impeller { | ||
|
|
||
| void RuntimeEffectContents::SetRuntimeStage( | ||
| std::shared_ptr<RuntimeStage> runtime_stage) { | ||
| runtime_stage_ = std::move(runtime_stage); | ||
| } | ||
|
|
||
| void RuntimeEffectContents::SetUniformData(std::vector<uint8_t> uniform_data) { | ||
| uniform_data_ = std::move(uniform_data); | ||
| } | ||
|
|
||
| bool RuntimeEffectContents::Render(const ContentContext& renderer, | ||
| const Entity& entity, | ||
| RenderPass& pass) const { | ||
| auto context = renderer.GetContext(); | ||
| auto library = context->GetShaderLibrary(); | ||
|
|
||
| //-------------------------------------------------------------------------- | ||
| /// Get or register shader. | ||
| /// | ||
|
|
||
| std::shared_ptr<const ShaderFunction> function = library->GetFunction( | ||
| runtime_stage_->GetEntrypoint(), ShaderStage::kFragment); | ||
|
|
||
| if (!function) { | ||
| std::promise<bool> promise; | ||
| auto future = promise.get_future(); | ||
|
|
||
| library->RegisterFunction( | ||
|
Contributor
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. Should we be doing this immediately after reading the shader in from the asset?
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. Yes, but I'd like to defer figuring out the threading and g3 dependency situation to a later patch. I assume we're already building all of Impeller though, so maybe g3 isn't as much a concern. |
||
| runtime_stage_->GetEntrypoint(), | ||
| ToShaderStage(runtime_stage_->GetShaderStage()), | ||
| runtime_stage_->GetCodeMapping(), | ||
| fml::MakeCopyable([promise = std::move(promise)](bool result) mutable { | ||
| promise.set_value(result); | ||
| })); | ||
|
|
||
| if (!future.get()) { | ||
| VALIDATION_LOG << "Failed to build runtime effect (entry point: " | ||
| << runtime_stage_->GetEntrypoint() << ")"; | ||
| return false; | ||
| } | ||
|
|
||
| function = library->GetFunction(runtime_stage_->GetEntrypoint(), | ||
| ShaderStage::kFragment); | ||
| if (!function) { | ||
| VALIDATION_LOG | ||
| << "Failed to fetch runtime effect function immediately after " | ||
| "registering it (entry point: " | ||
| << runtime_stage_->GetEntrypoint() << ")"; | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| //-------------------------------------------------------------------------- | ||
| /// Resolve geometry. | ||
| /// | ||
|
|
||
| auto geometry_result = GetGeometry()->GetPositionBuffer( | ||
| context->GetResourceAllocator(), pass.GetTransientsBuffer(), | ||
| renderer.GetTessellator(), pass.GetRenderTargetSize()); | ||
|
|
||
| //-------------------------------------------------------------------------- | ||
| /// Get or create runtime stage pipeline. | ||
| /// | ||
|
|
||
| using VS = PositionNoColorVertexShader; | ||
| PipelineDescriptor desc; | ||
| desc.SetLabel("Runtime Stage"); | ||
| desc.AddStageEntrypoint( | ||
| library->GetFunction(VS::kEntrypointName, ShaderStage::kVertex)); | ||
| desc.AddStageEntrypoint(library->GetFunction(runtime_stage_->GetEntrypoint(), | ||
| ShaderStage::kFragment)); | ||
| auto vertex_descriptor = std::make_shared<VertexDescriptor>(); | ||
| if (!vertex_descriptor->SetStageInputs(VS::kAllShaderStageInputs)) { | ||
| VALIDATION_LOG << "Failed to set stage inputs for runtime effect pipeline."; | ||
| } | ||
| desc.SetVertexDescriptor(std::move(vertex_descriptor)); | ||
| desc.SetColorAttachmentDescriptor(0u, {.format = PixelFormat::kDefaultColor}); | ||
| desc.SetStencilAttachmentDescriptors({}); | ||
| desc.SetStencilPixelFormat(PixelFormat::kDefaultStencil); | ||
|
|
||
| auto options = OptionsFromPassAndEntity(pass, entity); | ||
| if (geometry_result.prevent_overdraw) { | ||
| options.stencil_compare = CompareFunction::kEqual; | ||
| options.stencil_operation = StencilOperation::kIncrementClamp; | ||
| } | ||
| options.ApplyToPipelineDescriptor(desc); | ||
|
|
||
| auto pipeline = context->GetPipelineLibrary()->GetPipeline(desc).get(); | ||
| if (!pipeline) { | ||
| VALIDATION_LOG << "Failed to get or create runtime effect pipeline."; | ||
| return false; | ||
| } | ||
|
|
||
| Command cmd; | ||
| cmd.label = "RuntimeEffectContents"; | ||
| cmd.pipeline = pipeline; | ||
| cmd.stencil_reference = entity.GetStencilDepth(); | ||
| cmd.BindVertices(geometry_result.vertex_buffer); | ||
| cmd.primitive_type = geometry_result.type; | ||
|
|
||
| //-------------------------------------------------------------------------- | ||
| /// Vertex stage uniforms. | ||
| /// | ||
|
|
||
| VS::VertInfo frame_info; | ||
| frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * | ||
| entity.GetTransformation(); | ||
| VS::BindVertInfo(cmd, pass.GetTransientsBuffer().EmplaceUniform(frame_info)); | ||
|
|
||
| //-------------------------------------------------------------------------- | ||
| /// Fragment stage uniforms. | ||
| /// | ||
|
|
||
| size_t buffer_index = 0; | ||
| for (auto uniform : runtime_stage_->GetUniforms()) { | ||
| // TODO(bdero): Populate this metadata once GLES is able to handle | ||
|
Contributor
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. Please file (or update) an issue for this TODO
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: flutter/flutter#113715 |
||
| // non-struct uniform names. | ||
| ShaderMetadata metadata; | ||
|
|
||
| size_t alignment = | ||
| std::max(uniform.bit_width / 8, DefaultUniformAlignment()); | ||
| auto buffer_view = pass.GetTransientsBuffer().Emplace( | ||
| &uniform_data_[uniform.location * sizeof(float)], uniform.GetSize(), | ||
| alignment); | ||
|
|
||
| ShaderUniformSlot slot; | ||
| slot.name = uniform.name.c_str(); | ||
| slot.ext_res_0 = buffer_index; | ||
| cmd.BindResource(ShaderStage::kFragment, slot, metadata, buffer_view); | ||
|
|
||
| buffer_index++; | ||
| } | ||
|
|
||
| pass.AddCommand(std::move(cmd)); | ||
|
|
||
| if (!geometry_result.prevent_overdraw) { | ||
| return ClipRestoreContents().Render(renderer, entity, pass); | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| } // namespace impeller | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| // Copyright 2013 The Flutter Authors. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|
|
||
| #include <functional> | ||
| #include <memory> | ||
|
|
||
| #include "impeller/entity/contents/color_source_contents.h" | ||
| #include "impeller/runtime_stage/runtime_stage.h" | ||
|
|
||
| namespace impeller { | ||
|
|
||
| class RuntimeEffectContents final : public ColorSourceContents { | ||
| public: | ||
| void SetRuntimeStage(std::shared_ptr<RuntimeStage> runtime_stage); | ||
|
|
||
| void SetUniformData(std::vector<uint8_t> uniform_data); | ||
|
|
||
| // |Contents| | ||
| bool Render(const ContentContext& renderer, | ||
| const Entity& entity, | ||
| RenderPass& pass) const override; | ||
|
|
||
| private: | ||
| std::shared_ptr<RuntimeStage> runtime_stage_; | ||
| std::vector<uint8_t> uniform_data_; | ||
| }; | ||
|
|
||
| } // namespace impeller |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,13 +3,15 @@ | |
| // found in the LICENSE file. | ||
|
|
||
| #include <algorithm> | ||
| #include <cstring> | ||
| #include <memory> | ||
| #include <optional> | ||
| #include <unordered_map> | ||
| #include <vector> | ||
|
|
||
| #include "flutter/testing/testing.h" | ||
| #include "fml/logging.h" | ||
| #include "fml/time/time_point.h" | ||
| #include "gtest/gtest.h" | ||
| #include "impeller/entity/contents/atlas_contents.h" | ||
| #include "impeller/entity/contents/clip_contents.h" | ||
|
|
@@ -19,6 +21,7 @@ | |
| #include "impeller/entity/contents/filters/filter_contents.h" | ||
| #include "impeller/entity/contents/filters/inputs/filter_input.h" | ||
| #include "impeller/entity/contents/rrect_shadow_contents.h" | ||
| #include "impeller/entity/contents/runtime_effect_contents.h" | ||
| #include "impeller/entity/contents/solid_color_contents.h" | ||
| #include "impeller/entity/contents/solid_stroke_contents.h" | ||
| #include "impeller/entity/contents/text_contents.h" | ||
|
|
@@ -37,6 +40,7 @@ | |
| #include "impeller/playground/widgets.h" | ||
| #include "impeller/renderer/render_pass.h" | ||
| #include "impeller/renderer/vertex_buffer_builder.h" | ||
| #include "impeller/runtime_stage/runtime_stage.h" | ||
| #include "impeller/tessellator/tessellator.h" | ||
| #include "impeller/typographer/backends/skia/text_frame_skia.h" | ||
| #include "impeller/typographer/backends/skia/text_render_context_skia.h" | ||
|
|
@@ -2026,5 +2030,49 @@ TEST_P(EntityTest, SdfText) { | |
| ASSERT_TRUE(OpenPlaygroundHere(callback)); | ||
| } | ||
|
|
||
| TEST_P(EntityTest, RuntimeEffect) { | ||
| if (GetParam() != PlaygroundBackend::kMetal) { | ||
| GTEST_SKIP_("This test only has a Metal fixture at the moment."); | ||
| } | ||
|
|
||
| auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { | ||
| auto contents = std::make_shared<RuntimeEffectContents>(); | ||
| contents->SetGeometry(Geometry::MakeCover()); | ||
|
|
||
| // layout(location = 0) uniform float iTime; | ||
| // layout(location = 1) uniform vec2 iResolution; | ||
| // | ||
| // layout(location = 0) out vec4 fragColor; | ||
| // | ||
| // void main() { | ||
| // // Normalized pixel coordinates (from 0 to 1) | ||
| // vec2 uv = gl_FragCoord.xy/iResolution; | ||
| // float t = 4 * iTime; | ||
| // vec3 col = 0.5 + 0.5*cos(t + uv.xyx + vec3(0,1,4)); | ||
| // fragColor = vec4(col,1.0); | ||
| // } | ||
| auto runtime_stage = LoadFixtureRuntimeStage("example.frag.metal.iplr"); | ||
|
Contributor
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. This doesn't need to be checked in. It can be generated by the build. We do that for ink_sparkle here.
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. Good call, done. |
||
| contents->SetRuntimeStage(runtime_stage); | ||
|
|
||
| struct FragUniforms { | ||
| Scalar iTime; | ||
| Vector2 iResolution; | ||
| } frag_uniforms = { | ||
| .iTime = static_cast<Scalar>( | ||
| fml::TimePoint::Now().ToEpochDelta().ToSecondsF()), | ||
| .iResolution = Vector2(GetWindowSize().width, GetWindowSize().height), | ||
| }; | ||
| std::vector<uint8_t> uniform_data; | ||
| uniform_data.resize(sizeof(FragUniforms)); | ||
| memcpy(uniform_data.data(), &frag_uniforms, sizeof(FragUniforms)); | ||
| contents->SetUniformData(uniform_data); | ||
|
|
||
| Entity entity; | ||
| entity.SetContents(contents); | ||
| return contents->Render(context, entity, pass); | ||
| }; | ||
| ASSERT_TRUE(OpenPlaygroundHere(callback)); | ||
| } | ||
|
|
||
| } // namespace testing | ||
| } // namespace impeller | ||
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.
Should this still be
UNIMPLEMENTED?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.
Yes, fixed.