Start wiring up fragment program for OpenGLES (#49347)
Makes fragment programs work for OpenGLES backend.
Fixes https://github.com/flutter/flutter/issues/113715
Fixes https://github.com/flutter/flutter/issues/105538
I cannot find a dedicated issue for this, but there probably is someone somewhere and I don't want to file a new one for it.
diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc
index b7a7e0d..61ddc20 100644
--- a/impeller/aiks/aiks_unittests.cc
+++ b/impeller/aiks/aiks_unittests.cc
@@ -3033,13 +3033,15 @@
// Regression test for https://github.com/flutter/flutter/issues/126701 .
TEST_P(AiksTest, CanRenderClippedRuntimeEffects) {
- if (GetParam() != PlaygroundBackend::kMetal) {
+ if (!BackendSupportsFragmentProgram()) {
GTEST_SKIP_("This backend doesn't support runtime effects.");
}
auto runtime_stages =
OpenAssetAsRuntimeStage("runtime_stage_example.frag.iplr");
- auto runtime_stage = runtime_stages[RuntimeStageBackend::kMetal];
+
+ auto runtime_stage =
+ runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
ASSERT_TRUE(runtime_stage);
ASSERT_TRUE(runtime_stage->IsDirty());
@@ -3068,7 +3070,7 @@
}
TEST_P(AiksTest, DrawPaintTransformsBounds) {
- if (GetParam() != PlaygroundBackend::kMetal) {
+ if (!BackendSupportsFragmentProgram()) {
GTEST_SKIP_("This backend doesn't support runtime effects.");
}
diff --git a/impeller/core/shader_types.h b/impeller/core/shader_types.h
index 8b267db..25c405b 100644
--- a/impeller/core/shader_types.h
+++ b/impeller/core/shader_types.h
@@ -70,6 +70,7 @@
};
struct ShaderMetadata {
+ // This must match the uniform name in the shader program.
std::string name;
std::vector<ShaderStructMemberMetadata> members;
};
diff --git a/impeller/entity/contents/runtime_effect_contents.cc b/impeller/entity/contents/runtime_effect_contents.cc
index 82ad350..a2a5244 100644
--- a/impeller/entity/contents/runtime_effect_contents.cc
+++ b/impeller/entity/contents/runtime_effect_contents.cc
@@ -11,6 +11,7 @@
#include "flutter/fml/make_copyable.h"
#include "impeller/base/validation.h"
#include "impeller/core/formats.h"
+#include "impeller/core/runtime_types.h"
#include "impeller/core/shader_types.h"
#include "impeller/entity/contents/clip_contents.h"
#include "impeller/entity/contents/content_context.h"
@@ -41,16 +42,53 @@
return false;
}
+static ShaderType GetShaderType(RuntimeUniformType type) {
+ switch (type) {
+ case kSampledImage:
+ return ShaderType::kSampledImage;
+ case kFloat:
+ return ShaderType::kFloat;
+ case kBoolean:
+ case kSignedByte:
+ case kUnsignedByte:
+ case kSignedShort:
+ case kUnsignedShort:
+ case kSignedInt:
+ case kUnsignedInt:
+ case kSignedInt64:
+ case kUnsignedInt64:
+ case kHalfFloat:
+ case kDouble:
+ VALIDATION_LOG << "Unsupported uniform type.";
+ return ShaderType::kVoid;
+ }
+}
+
+static std::shared_ptr<ShaderMetadata> MakeShaderMetadata(
+ const RuntimeUniformDescription& uniform) {
+ auto metadata = std::make_shared<ShaderMetadata>();
+ metadata->name = uniform.name;
+ metadata->members.emplace_back(ShaderStructMemberMetadata{
+ .type = GetShaderType(uniform.type),
+ .size = uniform.GetSize(),
+ .byte_length = uniform.bit_width / 8,
+ });
+
+ return metadata;
+}
+
bool RuntimeEffectContents::Render(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
-// TODO(jonahwilliams): FragmentProgram API is not fully wired up on Android.
-// Disable until this is complete so that integration tests and benchmarks can
-// run m3 applications.
-#ifdef FML_OS_ANDROID
- return true;
-#else
-
+ // TODO(jonahwilliams): FragmentProgram API is not fully wired up on Android.
+ // Disable until this is complete so that integration tests and benchmarks can
+ // run m3 applications.
+ if (renderer.GetContext()->GetBackendType() ==
+ Context::BackendType::kVulkan) {
+ FML_DLOG(WARNING)
+ << "Fragment programs not supported on Vulkan. Content dropped.";
+ return true;
+ }
auto context = renderer.GetContext();
auto library = context->GetShaderLibrary();
@@ -172,10 +210,7 @@
size_t buffer_index = 0;
size_t buffer_offset = 0;
for (const auto& uniform : runtime_stage_->GetUniforms()) {
- // TODO(113715): Populate this metadata once GLES is able to handle
- // non-struct uniform names.
- std::shared_ptr<ShaderMetadata> metadata =
- std::make_shared<ShaderMetadata>();
+ std::shared_ptr<ShaderMetadata> metadata = MakeShaderMetadata(uniform);
switch (uniform.type) {
case kSampledImage: {
@@ -226,9 +261,7 @@
size_t sampler_index = 0;
for (const auto& uniform : runtime_stage_->GetUniforms()) {
- // TODO(113715): Populate this metadata once GLES is able to handle
- // non-struct uniform names.
- ShaderMetadata metadata;
+ std::shared_ptr<ShaderMetadata> metadata = MakeShaderMetadata(uniform);
switch (uniform.type) {
case kSampledImage: {
@@ -241,7 +274,7 @@
SampledImageSlot image_slot;
image_slot.name = uniform.name.c_str();
image_slot.texture_index = uniform.location - minimum_sampler_index;
- cmd.BindResource(ShaderStage::kFragment, image_slot, metadata,
+ cmd.BindResource(ShaderStage::kFragment, image_slot, *metadata,
input.texture, sampler);
sampler_index++;
@@ -260,7 +293,6 @@
return restore.Render(renderer, entity, pass);
}
return true;
-#endif // FML_OS_ANDROID
}
} // namespace impeller
diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc
index 4cb0c41..91d6b6a 100644
--- a/impeller/entity/entity_unittests.cc
+++ b/impeller/entity/entity_unittests.cc
@@ -2132,13 +2132,14 @@
}
TEST_P(EntityTest, RuntimeEffect) {
- if (GetParam() != PlaygroundBackend::kMetal) {
+ if (!BackendSupportsFragmentProgram()) {
GTEST_SKIP_("This backend doesn't support runtime effects.");
}
auto runtime_stages =
OpenAssetAsRuntimeStage("runtime_stage_example.frag.iplr");
- auto runtime_stage = runtime_stages[RuntimeStageBackend::kMetal];
+ auto runtime_stage =
+ runtime_stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
ASSERT_TRUE(runtime_stage);
ASSERT_TRUE(runtime_stage->IsDirty());
diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn
index 1867b65..bce07ac 100644
--- a/impeller/fixtures/BUILD.gn
+++ b/impeller/fixtures/BUILD.gn
@@ -60,7 +60,14 @@
"gradient.frag",
]
sl_file_extension = "iplr"
- shader_target_flag = "--runtime-stage-metal"
+
+ # TODO(dnfield): Make impellerc able to bundle multiple non-SkSL shaders.
+ # https://github.com/flutter/flutter/issues/140817
+ if (is_ios || is_mac) {
+ shader_target_flag = "--runtime-stage-metal"
+ } else {
+ shader_target_flag = "--runtime-stage-gles"
+ }
iplr = true
}
diff --git a/impeller/golden_tests/golden_playground_test.h b/impeller/golden_tests/golden_playground_test.h
index f411615..e4fdde8 100644
--- a/impeller/golden_tests/golden_playground_test.h
+++ b/impeller/golden_tests/golden_playground_test.h
@@ -36,6 +36,12 @@
PlaygroundBackend GetBackend() const;
+ // TODO(dnfield): Delete this once
+ // https://github.com/flutter/flutter/issues/122823 is fixed.
+ bool BackendSupportsFragmentProgram() const {
+ return GetBackend() != PlaygroundBackend::kVulkan;
+ }
+
void SetTypographerContext(
std::shared_ptr<TypographerContext> typographer_context);
diff --git a/impeller/playground/playground.h b/impeller/playground/playground.h
index 57e6b88..6f8f37f 100644
--- a/impeller/playground/playground.h
+++ b/impeller/playground/playground.h
@@ -12,6 +12,7 @@
#include "flutter/fml/macros.h"
#include "flutter/fml/status.h"
#include "flutter/fml/time/time_delta.h"
+#include "impeller/core/runtime_types.h"
#include "impeller/core/texture.h"
#include "impeller/geometry/point.h"
#include "impeller/image/compressed_image.h"
@@ -30,6 +31,19 @@
kVulkan,
};
+constexpr inline RuntimeStageBackend PlaygroundBackendToRuntimeStageBackend(
+ PlaygroundBackend backend) {
+ switch (backend) {
+ case PlaygroundBackend::kMetal:
+ return RuntimeStageBackend::kMetal;
+ case PlaygroundBackend::kOpenGLES:
+ return RuntimeStageBackend::kOpenGLES;
+ case PlaygroundBackend::kVulkan:
+ return RuntimeStageBackend::kVulkan;
+ }
+ FML_UNREACHABLE();
+}
+
std::string PlaygroundBackendToString(PlaygroundBackend backend);
class Playground {
diff --git a/impeller/playground/playground_test.h b/impeller/playground/playground_test.h
index e2051a2..65ac08e 100644
--- a/impeller/playground/playground_test.h
+++ b/impeller/playground/playground_test.h
@@ -42,6 +42,12 @@
// |Playground|
std::string GetWindowTitle() const override;
+ // TODO(dnfield): Delete this once
+ // https://github.com/flutter/flutter/issues/122823 is fixed.
+ bool BackendSupportsFragmentProgram() const {
+ return GetBackend() != PlaygroundBackend::kVulkan;
+ }
+
private:
// |Playground|
bool ShouldKeepRendering() const;
diff --git a/impeller/renderer/backend/gles/buffer_bindings_gles.cc b/impeller/renderer/backend/gles/buffer_bindings_gles.cc
index 696ab62..899cbbb 100644
--- a/impeller/renderer/backend/gles/buffer_bindings_gles.cc
+++ b/impeller/renderer/backend/gles/buffer_bindings_gles.cc
@@ -65,8 +65,10 @@
std::string result;
result.reserve(struct_name.length() + member.length() + (is_array ? 4 : 1));
result += struct_name;
- result += '.';
- result += member;
+ if (!member.empty()) {
+ result += '.';
+ result += member;
+ }
if (is_array) {
result += "[0]";
}
@@ -312,6 +314,9 @@
);
continue;
}
+ VALIDATION_LOG << "Size " << member.size
+ << " could not be mapped ShaderType::kFloat for key: "
+ << member.name;
case ShaderType::kBoolean:
case ShaderType::kSignedByte:
case ShaderType::kUnsignedByte:
diff --git a/impeller/runtime_stage/runtime_stage_unittests.cc b/impeller/runtime_stage/runtime_stage_unittests.cc
index f5b95aa..082b4a4 100644
--- a/impeller/runtime_stage/runtime_stage_unittests.cc
+++ b/impeller/runtime_stage/runtime_stage_unittests.cc
@@ -23,18 +23,26 @@
using RuntimeStageTest = RuntimeStagePlayground;
INSTANTIATE_PLAYGROUND_SUITE(RuntimeStageTest);
-TEST(RuntimeStageTest, CanReadValidBlob) {
+TEST_P(RuntimeStageTest, CanReadValidBlob) {
+ if (!BackendSupportsFragmentProgram()) {
+ GTEST_SKIP_("This backend doesn't support runtime effects.");
+ }
+
const std::shared_ptr<fml::Mapping> fixture =
flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
ASSERT_TRUE(fixture);
ASSERT_GT(fixture->GetSize(), 0u);
auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
- auto stage = stages[RuntimeStageBackend::kMetal];
+ auto stage = stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
ASSERT_TRUE(stage->IsValid());
ASSERT_EQ(stage->GetShaderStage(), RuntimeShaderStage::kFragment);
}
-TEST(RuntimeStageTest, CanRejectInvalidBlob) {
+TEST_P(RuntimeStageTest, CanRejectInvalidBlob) {
+ if (!BackendSupportsFragmentProgram()) {
+ GTEST_SKIP_("This backend doesn't support runtime effects.");
+ }
+
ScopedValidationDisable disable_validation;
const std::shared_ptr<fml::Mapping> fixture =
flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
@@ -46,16 +54,20 @@
::memset(junk_allocation->GetBuffer(), 127, junk_allocation->GetLength());
auto stages = RuntimeStage::DecodeRuntimeStages(
CreateMappingFromAllocation(junk_allocation));
- ASSERT_FALSE(stages[RuntimeStageBackend::kMetal]);
+ ASSERT_FALSE(stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())]);
}
-TEST(RuntimeStageTest, CanReadUniforms) {
+TEST_P(RuntimeStageTest, CanReadUniforms) {
+ if (!BackendSupportsFragmentProgram()) {
+ GTEST_SKIP_("This backend doesn't support runtime effects.");
+ }
+
const std::shared_ptr<fml::Mapping> fixture =
flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
ASSERT_TRUE(fixture);
ASSERT_GT(fixture->GetSize(), 0u);
auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
- auto stage = stages[RuntimeStageBackend::kMetal];
+ auto stage = stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
ASSERT_TRUE(stage->IsValid());
ASSERT_EQ(stage->GetUniforms().size(), 17u);
@@ -191,15 +203,16 @@
}
TEST_P(RuntimeStageTest, CanRegisterStage) {
- if (GetParam() != PlaygroundBackend::kMetal) {
- GTEST_SKIP_("Skipped: https://github.com/flutter/flutter/issues/105538");
+ if (!BackendSupportsFragmentProgram()) {
+ GTEST_SKIP_("This backend doesn't support runtime effects.");
}
+
const std::shared_ptr<fml::Mapping> fixture =
flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
ASSERT_TRUE(fixture);
ASSERT_GT(fixture->GetSize(), 0u);
auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
- auto stage = stages[RuntimeStageBackend::kMetal];
+ auto stage = stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
ASSERT_TRUE(stage->IsValid());
std::promise<bool> registration;
auto future = registration.get_future();
@@ -229,11 +242,11 @@
}
TEST_P(RuntimeStageTest, CanCreatePipelineFromRuntimeStage) {
- if (GetParam() != PlaygroundBackend::kMetal) {
- GTEST_SKIP_("Skipped: https://github.com/flutter/flutter/issues/105538");
+ if (!BackendSupportsFragmentProgram()) {
+ GTEST_SKIP_("This backend doesn't support runtime effects.");
}
auto stages = OpenAssetAsRuntimeStage("ink_sparkle.frag.iplr");
- auto stage = stages[RuntimeStageBackend::kMetal];
+ auto stage = stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
ASSERT_TRUE(stage);
ASSERT_NE(stage, nullptr);