Start wiring up fragment program for OpenGLES (flutter/engine#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.
This commit is contained in:
Dan Field
2024-01-02 14:11:05 -08:00
committed by GitHub
parent 50c148a227
commit 1255dfd26a
10 changed files with 123 additions and 36 deletions

View File

@@ -3033,13 +3033,15 @@ TEST_P(AiksTest, CanRenderForegroundAdvancedBlendWithMaskBlur) {
// 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, CanRenderClippedRuntimeEffects) {
}
TEST_P(AiksTest, DrawPaintTransformsBounds) {
if (GetParam() != PlaygroundBackend::kMetal) {
if (!BackendSupportsFragmentProgram()) {
GTEST_SKIP_("This backend doesn't support runtime effects.");
}

View File

@@ -70,6 +70,7 @@ struct ShaderStructMemberMetadata {
};
struct ShaderMetadata {
// This must match the uniform name in the shader program.
std::string name;
std::vector<ShaderStructMemberMetadata> members;
};

View File

@@ -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 @@ bool RuntimeEffectContents::CanInheritOpacity(const Entity& entity) const {
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 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
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 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
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 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
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 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
return restore.Render(renderer, entity, pass);
}
return true;
#endif // FML_OS_ANDROID
}
} // namespace impeller

View File

@@ -2132,13 +2132,14 @@ TEST_P(EntityTest, YUVToRGBFilter) {
}
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());

View File

@@ -60,7 +60,14 @@ impellerc("runtime_stages") {
"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
}

View File

@@ -36,6 +36,12 @@ class GoldenPlaygroundTest
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);

View File

@@ -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 @@ enum class PlaygroundBackend {
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 {

View File

@@ -42,6 +42,12 @@ class PlaygroundTest : public Playground,
// |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;

View File

@@ -65,8 +65,10 @@ static std::string CreateUniformMemberKey(const std::string& struct_name,
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 @@ bool BufferBindingsGLES::BindUniformBuffer(const ProcTableGLES& gl,
);
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:

View File

@@ -23,18 +23,26 @@ namespace testing {
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 @@ TEST(RuntimeStageTest, CanRejectInvalidBlob) {
::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(RuntimeStageTest, CanReadUniforms) {
}
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, CanRegisterStage) {
}
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);