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:
@@ -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.");
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user