[impeller] Use the GLES3 shaders in the embedder if supported (#180072)
<!--
Thanks for filing a pull request!
Reviewers are typically assigned within a week of filing a request.
To learn more about code review, see our documentation on Tree Hygiene:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
-->
Adds the GLES3 shaders to the embedder (used by Linux and custom
embedders) if they are supported.
Fixes #179185
## Pre-launch Checklist
- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.
---------
Co-authored-by: Jason Simmons <jsimmons@google.com>
Co-authored-by: Jason Simmons <jason-simmons@users.noreply.github.com>
diff --git a/docs/engine/testing/Testing-the-engine.md b/docs/engine/testing/Testing-the-engine.md
index 74ac146..0cfa214 100644
--- a/docs/engine/testing/Testing-the-engine.md
+++ b/docs/engine/testing/Testing-the-engine.md
@@ -34,6 +34,17 @@
testing/run_tests.py --type=engine --variant=host_debug_unopt_arm64
```
+Some tests depend on the underlying Graphics Driver implementation which can result
+in test flakyness. Test reliability on Linux for OpenGLES can be improved by selecting the
+[Software OpenGLES renderer](https://docs.mesa3d.org/drivers/llvmpipe.html)
+with [libglvnd](https://gitlab.freedesktop.org/glvnd/libglvnd). For example:
+
+```sh
+__EGL_VENDOR_LIBRARY_FILENAMES=/usr/share/glvnd/egl_vendor.d/50_mesa.json
+testing/run_tests.py
+```
+Check your distribution's documentation for more details.
+
Behind the scenes, those tests in the same directory are built together as a
testonly executable when you build the engine variant. The `run_tests.py` script
executes them one by one.
diff --git a/engine/src/flutter/impeller/display_list/aiks_dl_runtime_effect_unittests.cc b/engine/src/flutter/impeller/display_list/aiks_dl_runtime_effect_unittests.cc
index c8a9e21..73a4cd5 100644
--- a/engine/src/flutter/impeller/display_list/aiks_dl_runtime_effect_unittests.cc
+++ b/engine/src/flutter/impeller/display_list/aiks_dl_runtime_effect_unittests.cc
@@ -35,8 +35,7 @@
return runtime_stages_result.status();
}
std::shared_ptr<RuntimeStage> runtime_stage =
- runtime_stages_result
- .value()[PlaygroundBackendToRuntimeStageBackend(test->GetBackend())];
+ runtime_stages_result.value()[test->GetRuntimeStageBackend()];
if (!runtime_stage) {
return absl::InternalError("Runtime stage not found for backend.");
}
@@ -105,8 +104,7 @@
OpenAssetAsRuntimeStage("runtime_stage_filter_example.frag.iplr");
ABSL_ASSERT_OK(runtime_stages_result);
std::shared_ptr<RuntimeStage> runtime_stage =
- runtime_stages_result
- .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ runtime_stages_result.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(runtime_stage);
ASSERT_TRUE(runtime_stage->IsDirty());
@@ -179,8 +177,7 @@
OpenAssetAsRuntimeStage("runtime_stage_filter_warp.frag.iplr");
ABSL_ASSERT_OK(runtime_stages_result);
std::shared_ptr<RuntimeStage> runtime_stage =
- runtime_stages_result
- .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ runtime_stages_result.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(runtime_stage);
ASSERT_TRUE(runtime_stage->IsDirty());
@@ -216,8 +213,7 @@
OpenAssetAsRuntimeStage("runtime_stage_filter_warp.frag.iplr");
ABSL_ASSERT_OK(runtime_stages_result);
std::shared_ptr<RuntimeStage> runtime_stage =
- runtime_stages_result
- .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ runtime_stages_result.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(runtime_stage);
ASSERT_TRUE(runtime_stage->IsDirty());
Scalar xoffset = 50;
@@ -304,8 +300,7 @@
OpenAssetAsRuntimeStage("runtime_stage_filter_circle.frag.iplr");
ABSL_ASSERT_OK(runtime_stages_result);
std::shared_ptr<RuntimeStage> runtime_stage =
- runtime_stages_result
- .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ runtime_stages_result.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(runtime_stage);
ASSERT_TRUE(runtime_stage->IsDirty());
Scalar sigma = 20.0;
@@ -369,8 +364,7 @@
OpenAssetAsRuntimeStage("runtime_stage_filter_circle.frag.iplr");
ABSL_ASSERT_OK(runtime_stages_result);
std::shared_ptr<RuntimeStage> runtime_stage =
- runtime_stages_result
- .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ runtime_stages_result.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(runtime_stage);
ASSERT_TRUE(runtime_stage->IsDirty());
Scalar sigma = 5.0;
@@ -433,8 +427,7 @@
OpenAssetAsRuntimeStage("runtime_stage_filter_circle.frag.iplr");
ABSL_ASSERT_OK(runtime_stages_result);
std::shared_ptr<RuntimeStage> runtime_stage =
- runtime_stages_result
- .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ runtime_stages_result.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(runtime_stage);
ASSERT_TRUE(runtime_stage->IsDirty());
Scalar sigma = 5.0;
@@ -516,8 +509,7 @@
OpenAssetAsRuntimeStage("runtime_stage_border.frag.iplr");
ABSL_ASSERT_OK(runtime_stages_result);
std::shared_ptr<RuntimeStage> runtime_stage =
- runtime_stages_result
- .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ runtime_stages_result.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(runtime_stage);
ASSERT_TRUE(runtime_stage->IsDirty());
@@ -577,8 +569,7 @@
auto runtime_stages_result = OpenAssetAsRuntimeStage("gradient.frag.iplr");
ABSL_ASSERT_OK(runtime_stages_result);
std::shared_ptr<RuntimeStage> runtime_stage =
- runtime_stages_result
- .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ runtime_stages_result.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(runtime_stage);
ASSERT_TRUE(runtime_stage->IsDirty());
diff --git a/engine/src/flutter/impeller/display_list/aiks_dl_vertices_unittests.cc b/engine/src/flutter/impeller/display_list/aiks_dl_vertices_unittests.cc
index 6f31b96..55d8473 100644
--- a/engine/src/flutter/impeller/display_list/aiks_dl_vertices_unittests.cc
+++ b/engine/src/flutter/impeller/display_list/aiks_dl_vertices_unittests.cc
@@ -482,8 +482,7 @@
OpenAssetAsRuntimeStage("runtime_stage_simple.frag.iplr");
ABSL_ASSERT_OK(runtime_stages_result);
std::shared_ptr<RuntimeStage> runtime_stage =
- runtime_stages_result
- .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ runtime_stages_result.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(runtime_stage);
auto runtime_effect = DlRuntimeEffectImpeller::Make(runtime_stage);
@@ -532,8 +531,7 @@
OpenAssetAsRuntimeStage("runtime_stage_position.frag.iplr");
ABSL_ASSERT_OK(runtime_stages_result);
std::shared_ptr<RuntimeStage> runtime_stage =
- runtime_stages_result
- .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ runtime_stages_result.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(runtime_stage);
auto runtime_effect = DlRuntimeEffectImpeller::Make(runtime_stage);
diff --git a/engine/src/flutter/impeller/display_list/canvas_unittests.cc b/engine/src/flutter/impeller/display_list/canvas_unittests.cc
index 7ca94c4..6bcac03 100644
--- a/engine/src/flutter/impeller/display_list/canvas_unittests.cc
+++ b/engine/src/flutter/impeller/display_list/canvas_unittests.cc
@@ -332,8 +332,7 @@
OpenAssetAsRuntimeStage("runtime_stage_simple.frag.iplr");
ABSL_ASSERT_OK(runtime_stages_result);
std::shared_ptr<RuntimeStage> runtime_stage =
- runtime_stages_result
- .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ runtime_stages_result.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(runtime_stage);
auto runtime_effect = flutter::DlRuntimeEffectImpeller::Make(runtime_stage);
diff --git a/engine/src/flutter/impeller/entity/entity_unittests.cc b/engine/src/flutter/impeller/entity/entity_unittests.cc
index aae5107..69ce491 100644
--- a/engine/src/flutter/impeller/entity/entity_unittests.cc
+++ b/engine/src/flutter/impeller/entity/entity_unittests.cc
@@ -1759,8 +1759,7 @@
OpenAssetAsRuntimeStage("runtime_stage_example.frag.iplr");
ABSL_ASSERT_OK(runtime_stages_result);
std::shared_ptr<RuntimeStage> runtime_stage =
- runtime_stages_result
- .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ runtime_stages_result.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(runtime_stage);
ASSERT_TRUE(runtime_stage->IsDirty());
@@ -1816,9 +1815,7 @@
auto runtime_stages_result =
OpenAssetAsRuntimeStage("runtime_stage_example.frag.iplr");
ABSL_ASSERT_OK(runtime_stages_result);
- runtime_stage =
- runtime_stages_result
- .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ runtime_stage = runtime_stages_result.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(runtime_stage->IsDirty());
expect_dirty = true;
@@ -1831,9 +1828,7 @@
auto runtime_stages_result =
OpenAssetAsRuntimeStage("runtime_stage_example.frag.iplr");
ABSL_ASSERT_OK(runtime_stages_result);
- auto runtime_stage =
- runtime_stages_result
- .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ auto runtime_stage = runtime_stages_result.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(runtime_stage);
ASSERT_TRUE(runtime_stage->IsDirty());
@@ -1881,8 +1876,7 @@
OpenAssetAsRuntimeStage("runtime_stage_example.frag.iplr");
ABSL_ASSERT_OK(runtime_stages_result);
std::shared_ptr<RuntimeStage> runtime_stage =
- runtime_stages_result
- .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ runtime_stages_result.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(runtime_stage);
ASSERT_TRUE(runtime_stage->IsDirty());
@@ -1900,9 +1894,7 @@
auto runtime_stages_result =
OpenAssetAsRuntimeStage("runtime_stage_example.frag.iplr");
ABSL_ASSERT_OK(runtime_stages_result);
- auto runtime_stage =
- runtime_stages_result
- .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ auto runtime_stage = runtime_stages_result.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(runtime_stage);
ASSERT_TRUE(runtime_stage->IsDirty());
diff --git a/engine/src/flutter/impeller/golden_tests/golden_playground_test.h b/engine/src/flutter/impeller/golden_tests/golden_playground_test.h
index 9158eb6..cc1def8 100644
--- a/engine/src/flutter/impeller/golden_tests/golden_playground_test.h
+++ b/engine/src/flutter/impeller/golden_tests/golden_playground_test.h
@@ -11,7 +11,7 @@
#include "flutter/display_list/image/dl_image.h"
#include "flutter/impeller/display_list/aiks_context.h"
#include "flutter/impeller/golden_tests/screenshot.h"
-#include "flutter/impeller/renderer/render_target.h"
+#include "flutter/impeller/runtime_stage/runtime_stage.h"
#include "flutter/testing/testing.h"
#include "impeller/playground/playground.h"
#include "impeller/typographer/typographer_context.h"
@@ -90,6 +90,8 @@
/// Returns true if `OpenPlaygroundHere` will actually render anything.
bool WillRenderSomething() const { return true; }
+ RuntimeStageBackend GetRuntimeStageBackend() const;
+
protected:
void SetWindowSize(ISize size);
diff --git a/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc b/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc
index 72e6637..d139104 100644
--- a/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc
+++ b/engine/src/flutter/impeller/golden_tests/golden_playground_test_mac.cc
@@ -344,4 +344,8 @@
renderer, DisplayListToTexture(list, physical_window_size, renderer));
}
+RuntimeStageBackend GoldenPlaygroundTest::GetRuntimeStageBackend() const {
+ return pimpl_->screenshotter->GetPlayground().GetRuntimeStageBackend();
+}
+
} // namespace impeller
diff --git a/engine/src/flutter/impeller/golden_tests/golden_playground_test_stub.cc b/engine/src/flutter/impeller/golden_tests/golden_playground_test_stub.cc
index 69e0017..e1ad152 100644
--- a/engine/src/flutter/impeller/golden_tests/golden_playground_test_stub.cc
+++ b/engine/src/flutter/impeller/golden_tests/golden_playground_test_stub.cc
@@ -82,4 +82,8 @@
return nullptr;
}
+RuntimeStageBackend GoldenPlaygroundTest::GetRuntimeStageBackend() const {
+ FML_UNREACHABLE();
+}
+
} // namespace impeller
diff --git a/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.cc b/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.cc
index a1a075b..32b00b9 100644
--- a/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.cc
+++ b/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.cc
@@ -17,9 +17,15 @@
#include "impeller/entity/gles/entity_shaders_gles.h"
#include "impeller/entity/gles/framebuffer_blend_shaders_gles.h"
#include "impeller/entity/gles/modern_shaders_gles.h"
+#include "impeller/entity/gles3/entity_shaders_gles.h"
+#include "impeller/entity/gles3/framebuffer_blend_shaders_gles.h"
+#include "impeller/entity/gles3/modern_shaders_gles.h"
#include "impeller/fixtures/gles/fixtures_shaders_gles.h"
#include "impeller/fixtures/gles/modern_fixtures_shaders_gles.h"
+#include "impeller/fixtures/gles3/fixtures_shaders_gles.h"
+#include "impeller/fixtures/gles3/modern_fixtures_shaders_gles.h"
#include "impeller/playground/imgui/gles/imgui_shaders_gles.h"
+#include "impeller/playground/imgui/gles3/imgui_shaders_gles.h"
#include "impeller/renderer/backend/gles/context_gles.h"
#include "impeller/renderer/backend/gles/surface_gles.h"
@@ -111,7 +117,29 @@
PlaygroundImplGLES::~PlaygroundImplGLES() = default;
static std::vector<std::shared_ptr<fml::Mapping>>
-ShaderLibraryMappingsForPlayground() {
+ShaderLibraryMappingsForPlayground(bool is_gles3) {
+ if (is_gles3) {
+ return {
+ std::make_shared<fml::NonOwnedMapping>(
+ impeller_entity_shaders_gles3_data,
+ impeller_entity_shaders_gles3_length),
+ std::make_shared<fml::NonOwnedMapping>(
+ impeller_modern_shaders_gles3_data,
+ impeller_modern_shaders_gles3_length),
+ std::make_shared<fml::NonOwnedMapping>(
+ impeller_framebuffer_blend_shaders_gles3_data,
+ impeller_framebuffer_blend_shaders_gles3_length),
+ std::make_shared<fml::NonOwnedMapping>(
+ impeller_fixtures_shaders_gles3_data,
+ impeller_fixtures_shaders_gles3_length),
+ std::make_shared<fml::NonOwnedMapping>(
+ impeller_modern_fixtures_shaders_gles3_data,
+ impeller_modern_fixtures_shaders_gles3_length),
+ std::make_shared<fml::NonOwnedMapping>(
+ impeller_imgui_shaders_gles3_data,
+ impeller_imgui_shaders_gles3_length),
+ };
+ }
return {
std::make_shared<fml::NonOwnedMapping>(
impeller_entity_shaders_gles_data,
@@ -160,9 +188,10 @@
gl->Enable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR);
#endif
}
+ bool is_gles3 = gl->GetDescription()->GetGlVersion().IsAtLeast(Version(3));
auto context =
ContextGLES::Create(switches_.flags, std::move(gl),
- ShaderLibraryMappingsForPlayground(), true);
+ ShaderLibraryMappingsForPlayground(is_gles3), true);
if (!context) {
FML_LOG(ERROR) << "Could not create context.";
return nullptr;
@@ -227,4 +256,16 @@
"PlaygroundImplGLES doesn't support setting the capabilities.");
}
+RuntimeStageBackend PlaygroundImplGLES::GetRuntimeStageBackend() const {
+ const auto gl =
+ std::make_unique<ProcTableGLES>(CreateGLProcAddressResolver());
+ if (!gl->IsValid()) {
+ FML_LOG(ERROR) << "Proc table was invalid. Assuming baseline OpenGL ES";
+ return RuntimeStageBackend::kOpenGLES;
+ }
+ bool is_gles3 = gl->GetDescription()->GetGlVersion().IsAtLeast(Version(3));
+ return is_gles3 ? RuntimeStageBackend::kOpenGLES3
+ : RuntimeStageBackend::kOpenGLES;
+}
+
} // namespace impeller
diff --git a/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.h b/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.h
index c1973e9..d54d1e7 100644
--- a/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.h
+++ b/engine/src/flutter/impeller/playground/backend/gles/playground_impl_gles.h
@@ -42,6 +42,8 @@
Playground::GLProcAddressResolver CreateGLProcAddressResolver()
const override;
+ RuntimeStageBackend GetRuntimeStageBackend() const override;
+
PlaygroundImplGLES(const PlaygroundImplGLES&) = delete;
PlaygroundImplGLES& operator=(const PlaygroundImplGLES&) = delete;
diff --git a/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.h b/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.h
index 2028a82..4ba9a08 100644
--- a/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.h
+++ b/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.h
@@ -26,6 +26,8 @@
fml::Status SetCapabilities(
const std::shared_ptr<Capabilities>& capabilities) override;
+ RuntimeStageBackend GetRuntimeStageBackend() const override;
+
private:
struct Data;
diff --git a/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.mm b/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.mm
index 218dbe5..12c596f 100644
--- a/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.mm
+++ b/engine/src/flutter/impeller/playground/backend/metal/playground_impl_mtl.mm
@@ -143,4 +143,8 @@
is_gpu_disabled_sync_switch_->SetSwitch(disabled);
}
+RuntimeStageBackend PlaygroundImplMTL::GetRuntimeStageBackend() const {
+ return RuntimeStageBackend::kMetal;
+}
+
} // namespace impeller
diff --git a/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.cc b/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.cc
index 3ea803d..ab95717 100644
--- a/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.cc
+++ b/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.cc
@@ -246,4 +246,8 @@
};
}
+RuntimeStageBackend PlaygroundImplVK::GetRuntimeStageBackend() const {
+ return RuntimeStageBackend::kVulkan;
+}
+
} // namespace impeller
diff --git a/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.h b/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.h
index 0f40124..9ea956f 100644
--- a/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.h
+++ b/engine/src/flutter/impeller/playground/backend/vulkan/playground_impl_vk.h
@@ -21,6 +21,8 @@
fml::Status SetCapabilities(
const std::shared_ptr<Capabilities>& capabilities) override;
+ RuntimeStageBackend GetRuntimeStageBackend() const override;
+
private:
std::shared_ptr<Context> context_;
diff --git a/engine/src/flutter/impeller/playground/playground.cc b/engine/src/flutter/impeller/playground/playground.cc
index 363c8ba..b105924 100644
--- a/engine/src/flutter/impeller/playground/playground.cc
+++ b/engine/src/flutter/impeller/playground/playground.cc
@@ -28,6 +28,7 @@
#include "impeller/playground/imgui/imgui_impl_impeller.h"
#include "impeller/playground/playground.h"
#include "impeller/playground/playground_impl.h"
+#include "impeller/renderer/backend/gles/context_gles.h"
#include "impeller/renderer/context.h"
#include "impeller/renderer/render_pass.h"
#include "third_party/imgui/backends/imgui_impl_glfw.h"
@@ -531,4 +532,8 @@
impl_->SetGPUDisabled(value);
}
+RuntimeStageBackend Playground::GetRuntimeStageBackend() const {
+ return impl_->GetRuntimeStageBackend();
+}
+
} // namespace impeller
diff --git a/engine/src/flutter/impeller/playground/playground.h b/engine/src/flutter/impeller/playground/playground.h
index 6878a59..e343be2 100644
--- a/engine/src/flutter/impeller/playground/playground.h
+++ b/engine/src/flutter/impeller/playground/playground.h
@@ -18,7 +18,6 @@
#include "impeller/playground/image/decompressed_image.h"
#include "impeller/playground/switches.h"
#include "impeller/renderer/render_pass.h"
-#include "impeller/runtime_stage/runtime_stage.h"
namespace impeller {
@@ -30,19 +29,6 @@
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 {
@@ -127,6 +113,8 @@
/// Only supported on the Metal backend.
void SetGPUDisabled(bool disabled) const;
+ RuntimeStageBackend GetRuntimeStageBackend() const;
+
protected:
const PlaygroundSwitches switches_;
diff --git a/engine/src/flutter/impeller/playground/playground_impl.h b/engine/src/flutter/impeller/playground/playground_impl.h
index ed2593f..1a26d38 100644
--- a/engine/src/flutter/impeller/playground/playground_impl.h
+++ b/engine/src/flutter/impeller/playground/playground_impl.h
@@ -42,6 +42,9 @@
virtual void SetGPUDisabled(bool disabled) const {}
+ [[nodiscard]]
+ virtual RuntimeStageBackend GetRuntimeStageBackend() const = 0;
+
protected:
const PlaygroundSwitches switches_;
diff --git a/engine/src/flutter/impeller/playground/playground_test.h b/engine/src/flutter/impeller/playground/playground_test.h
index 85a2d32..fae11be 100644
--- a/engine/src/flutter/impeller/playground/playground_test.h
+++ b/engine/src/flutter/impeller/playground/playground_test.h
@@ -11,6 +11,7 @@
#include "flutter/testing/testing.h"
#include "impeller/playground/playground.h"
#include "impeller/playground/switches.h"
+#include "impeller/runtime_stage/runtime_stage.h"
#include "third_party/abseil-cpp/absl/status/statusor.h"
#if FML_OS_MACOSX
diff --git a/engine/src/flutter/impeller/renderer/backend/gles/buffer_bindings_gles.cc b/engine/src/flutter/impeller/renderer/backend/gles/buffer_bindings_gles.cc
index 34d9843..f5aab37 100644
--- a/engine/src/flutter/impeller/renderer/backend/gles/buffer_bindings_gles.cc
+++ b/engine/src/flutter/impeller/renderer/backend/gles/buffer_bindings_gles.cc
@@ -334,6 +334,9 @@
absl::flat_hash_map<std::string, std::pair<GLint, GLuint>>::iterator it =
ubo_locations_.find(metadata->name);
if (it == ubo_locations_.end()) {
+ // This should only happen if we have GLESv3 but are using v2 shaders,
+ // as GLESv3 shaders compiled by impeller always have
+ // **named** uniform buffer blocks
return BindUniformBufferV2(gl, buffer, metadata, device_buffer_gles);
}
const auto& [block_index, binding_point] = it->second;
diff --git a/engine/src/flutter/impeller/runtime_stage/runtime_stage_unittests.cc b/engine/src/flutter/impeller/runtime_stage/runtime_stage_unittests.cc
index 456c564..66f4a68 100644
--- a/engine/src/flutter/impeller/runtime_stage/runtime_stage_unittests.cc
+++ b/engine/src/flutter/impeller/runtime_stage/runtime_stage_unittests.cc
@@ -37,8 +37,7 @@
ASSERT_GT(fixture->GetSize(), 0u);
auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
ABSL_ASSERT_OK(stages);
- auto stage =
- stages.value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ auto stage = stages.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(stage);
ASSERT_EQ(stage->GetShaderStage(), RuntimeShaderStage::kFragment);
}
@@ -80,8 +79,7 @@
ASSERT_GT(fixture->GetSize(), 0u);
auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
ABSL_ASSERT_OK(stages);
- auto stage =
- stages.value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ auto stage = stages.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(stage);
switch (GetBackend()) {
@@ -341,8 +339,7 @@
ASSERT_GT(fixture->GetSize(), 0u);
auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
ABSL_ASSERT_OK(stages);
- auto stage =
- stages.value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ auto stage = stages.value()[GetRuntimeStageBackend()];
EXPECT_EQ(stage->GetUniforms().size(), 2u);
auto uni = stage->GetUniform(RuntimeStage::kVulkanUBOName);
@@ -368,8 +365,7 @@
ASSERT_GT(fixture->GetSize(), 0u);
auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
ABSL_ASSERT_OK(stages);
- auto stage =
- stages.value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ auto stage = stages.value()[GetRuntimeStageBackend()];
EXPECT_EQ(stage->GetUniforms().size(), 2u);
auto uni = stage->GetUniform(RuntimeStage::kVulkanUBOName);
@@ -391,8 +387,7 @@
ASSERT_GT(fixture->GetSize(), 0u);
auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
ABSL_ASSERT_OK(stages);
- auto stage =
- stages.value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ auto stage = stages.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(stage);
std::promise<bool> registration;
auto future = registration.get_future();
@@ -424,9 +419,7 @@
TEST_P(RuntimeStageTest, CanCreatePipelineFromRuntimeStage) {
auto stages_result = OpenAssetAsRuntimeStage("ink_sparkle.frag.iplr");
ABSL_ASSERT_OK(stages_result);
- auto stage =
- stages_result
- .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
+ auto stage = stages_result.value()[GetRuntimeStageBackend()];
ASSERT_TRUE(stage);
ASSERT_NE(stage, nullptr);
diff --git a/engine/src/flutter/shell/platform/embedder/BUILD.gn b/engine/src/flutter/shell/platform/embedder/BUILD.gn
index 18564dc..61abe64 100644
--- a/engine/src/flutter/shell/platform/embedder/BUILD.gn
+++ b/engine/src/flutter/shell/platform/embedder/BUILD.gn
@@ -316,6 +316,8 @@
if (test_enable_gl) {
sources += [
+ "//flutter/impeller/renderer/backend/gles/test/mock_gles.cc",
+ "//flutter/impeller/renderer/backend/gles/test/mock_gles.h",
"tests/embedder_test_backingstore_producer_gl.cc",
"tests/embedder_test_backingstore_producer_gl.h",
"tests/embedder_test_compositor_gl.cc",
@@ -323,9 +325,11 @@
"tests/embedder_test_context_gl.cc",
"tests/embedder_test_context_gl.h",
"tests/embedder_test_gl.cc",
+ "tests/embedder_test_surface_gl_impeller.cc",
]
public_deps += [
+ "//flutter/impeller/renderer/backend/gles",
"//flutter/testing:opengl",
"//flutter/third_party/vulkan-deps/vulkan-headers/src:vulkan_headers",
]
diff --git a/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc b/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc
index 779c37b..d73ae89 100644
--- a/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc
+++ b/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.cc
@@ -10,11 +10,43 @@
#include "impeller/entity/gles/entity_shaders_gles.h"
#include "impeller/entity/gles/framebuffer_blend_shaders_gles.h"
#include "impeller/entity/gles/modern_shaders_gles.h"
+#include "impeller/entity/gles3/entity_shaders_gles.h"
+#include "impeller/entity/gles3/framebuffer_blend_shaders_gles.h"
+#include "impeller/entity/gles3/modern_shaders_gles.h"
#include "impeller/renderer/backend/gles/context_gles.h"
#include "impeller/renderer/backend/gles/proc_table_gles.h"
namespace flutter {
+namespace {
+std::vector<std::shared_ptr<fml::Mapping>> GetShaderMappings(bool is_gles3) {
+ if (is_gles3) {
+ return {
+ std::make_shared<fml::NonOwnedMapping>(
+ impeller_entity_shaders_gles3_data,
+ impeller_entity_shaders_gles3_length),
+ std::make_shared<fml::NonOwnedMapping>(
+ impeller_modern_shaders_gles3_data,
+ impeller_modern_shaders_gles3_length),
+ std::make_shared<fml::NonOwnedMapping>(
+ impeller_framebuffer_blend_shaders_gles3_data,
+ impeller_framebuffer_blend_shaders_gles3_length),
+ };
+ }
+ return {
+ std::make_shared<fml::NonOwnedMapping>(
+ impeller_entity_shaders_gles_data,
+ impeller_entity_shaders_gles_length),
+ std::make_shared<fml::NonOwnedMapping>(
+ impeller_modern_shaders_gles_data,
+ impeller_modern_shaders_gles_length),
+ std::make_shared<fml::NonOwnedMapping>(
+ impeller_framebuffer_blend_shaders_gles_data,
+ impeller_framebuffer_blend_shaders_gles_length),
+ };
+}
+} // namespace
+
class ReactorWorker final : public impeller::ReactorGLES::Worker {
public:
ReactorWorker() = default;
@@ -63,23 +95,16 @@
// state can be accessed.
gl_dispatch_table_.gl_make_current_callback();
- std::vector<std::shared_ptr<fml::Mapping>> shader_mappings = {
- std::make_shared<fml::NonOwnedMapping>(
- impeller_entity_shaders_gles_data,
- impeller_entity_shaders_gles_length),
- std::make_shared<fml::NonOwnedMapping>(
- impeller_modern_shaders_gles_data,
- impeller_modern_shaders_gles_length),
- std::make_shared<fml::NonOwnedMapping>(
- impeller_framebuffer_blend_shaders_gles_data,
- impeller_framebuffer_blend_shaders_gles_length),
- };
auto gl = std::make_unique<impeller::ProcTableGLES>(
gl_dispatch_table_.gl_proc_resolver);
if (!gl->IsValid()) {
return;
}
+ const auto is_gles3 =
+ gl->GetDescription()->GetGlVersion().IsAtLeast(impeller::Version(3));
+ const auto shader_mappings = GetShaderMappings(is_gles3);
+
impeller_context_ = impeller::ContextGLES::Create(
impeller::Flags{}, std::move(gl), shader_mappings,
/*enable_gpu_tracing=*/false);
diff --git a/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.h b/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.h
index 52febc2..274e1cc 100644
--- a/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.h
+++ b/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.h
@@ -16,6 +16,11 @@
} // namespace impeller
namespace flutter {
+namespace testing {
+FML_TEST_CLASS(EmbedderSurfaceGLImpellerTest, GLES3ContextHasGLES3Shaders);
+FML_TEST_CLASS(EmbedderSurfaceGLImpellerTest,
+ GLES2ContextDoesNotHaveGLES3Shaders);
+} // namespace testing
class ReactorWorker;
@@ -30,6 +35,10 @@
~EmbedderSurfaceGLImpeller() override;
private:
+ FML_FRIEND_TEST(testing::EmbedderSurfaceGLImpellerTest,
+ GLES3ContextHasGLES3Shaders);
+ FML_FRIEND_TEST(testing::EmbedderSurfaceGLImpellerTest,
+ GLES2ContextDoesNotHaveGLES3Shaders);
bool valid_ = false;
EmbedderSurfaceGLSkia::GLDispatchTable gl_dispatch_table_;
bool fbo_reset_after_present_;
diff --git a/engine/src/flutter/shell/platform/embedder/tests/embedder_test_surface_gl_impeller.cc b/engine/src/flutter/shell/platform/embedder/tests/embedder_test_surface_gl_impeller.cc
new file mode 100644
index 0000000..eda11d2
--- /dev/null
+++ b/engine/src/flutter/shell/platform/embedder/tests/embedder_test_surface_gl_impeller.cc
@@ -0,0 +1,81 @@
+// 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 "flatbuffers/stl_emulation.h"
+#include "flutter/impeller/renderer/backend/gles/test/mock_gles.h"
+#include "flutter/shell/platform/embedder/embedder_surface_gl_impeller.h"
+#include "impeller/renderer/backend/gles/shader_function_gles.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace flutter {
+namespace testing {
+
+using ::testing::Not;
+using ::testing::StartsWith;
+
+namespace {
+EmbedderSurfaceGLSkia::GLDispatchTable StubDispatchTable(
+ std::string_view version) {
+ impeller::testing::MockGLES::Init(flatbuffers::nullopt, version.data());
+ static constexpr auto dummy_always_true = [] { return true; };
+ return EmbedderSurfaceGLSkia::GLDispatchTable{
+ .gl_make_current_callback = dummy_always_true,
+ .gl_clear_current_callback = dummy_always_true,
+ .gl_present_callback = [](const auto) { return true; },
+ .gl_fbo_callback = [](const auto) { return 0; },
+ .gl_make_resource_current_callback = dummy_always_true,
+ .gl_surface_transformation_callback = [] { return DlMatrix{}; },
+ .gl_proc_resolver = impeller::testing::kMockResolverGLES,
+ .gl_populate_existing_damage = [](const auto) { return GLFBOInfo{}; },
+ };
+}
+} // namespace
+
+TEST(EmbedderSurfaceGLImpellerTest, GLES3ContextHasGLES3Shaders) {
+ const auto gl_dispatch_table =
+ StubDispatchTable(/* version */ "OpenGL ES 3.0");
+ const auto surface = EmbedderSurfaceGLImpeller(
+ gl_dispatch_table, /* fbo_reset_after_present */ false,
+ /* external_view_embedder */ nullptr);
+
+ const std::shared_ptr<impeller::Context> context =
+ surface.CreateImpellerContext();
+ const std::shared_ptr<impeller::ShaderLibrary> shaders =
+ context->GetShaderLibrary();
+ const std::shared_ptr<const impeller::ShaderFunction> func =
+ shaders->GetFunction("imp_line_fragment_main",
+ impeller::ShaderStage::kFragment);
+ const auto gles_func = impeller::ShaderFunctionGLES::Cast(func.get());
+ const std::shared_ptr<const fml::Mapping> source =
+ gles_func->GetSourceMapping();
+ const auto text =
+ std::string_view(reinterpret_cast<const char*>(source->GetMapping()));
+ EXPECT_THAT(text, StartsWith("#version 300 es"));
+}
+
+TEST(EmbedderSurfaceGLImpellerTest, GLES2ContextDoesNotHaveGLES3Shaders) {
+ const auto gl_dispatch_table =
+ StubDispatchTable(/* version */ "OpenGL ES 2.0");
+ const auto surface = EmbedderSurfaceGLImpeller(
+ gl_dispatch_table, /* fbo_reset_after_present */ false,
+ /* external_view_embedder */ nullptr);
+
+ const std::shared_ptr<impeller::Context> context =
+ surface.CreateImpellerContext();
+ const std::shared_ptr<impeller::ShaderLibrary> shaders =
+ context->GetShaderLibrary();
+ const std::shared_ptr<const impeller::ShaderFunction> func =
+ shaders->GetFunction("imp_line_fragment_main",
+ impeller::ShaderStage::kFragment);
+ const auto gles_func = impeller::ShaderFunctionGLES::Cast(func.get());
+ const std::shared_ptr<const fml::Mapping> source =
+ gles_func->GetSourceMapping();
+ const auto text =
+ std::string_view(reinterpret_cast<const char*>(source->GetMapping()));
+ EXPECT_THAT(text, StartsWith("#version 100"));
+}
+} // namespace testing
+} // namespace flutter