diff --git a/engine/src/flutter/impeller/compiler/compiler_test.cc b/engine/src/flutter/impeller/compiler/compiler_test.cc index dd4603fd683..683e6bea578 100644 --- a/engine/src/flutter/impeller/compiler/compiler_test.cc +++ b/engine/src/flutter/impeller/compiler/compiler_test.cc @@ -106,6 +106,7 @@ bool CompilerTestBase::CanCompileAndReflect( entry_point_name); Reflector::Options reflector_options; + reflector_options.target_platform = GetParam(); reflector_options.header_file_name = ReflectionHeaderName(fixture_name); reflector_options.shader_name = "shader_name"; diff --git a/engine/src/flutter/impeller/compiler/reflector.cc b/engine/src/flutter/impeller/compiler/reflector.cc index d5f220a3710..c63c21a83ef 100644 --- a/engine/src/flutter/impeller/compiler/reflector.cc +++ b/engine/src/flutter/impeller/compiler/reflector.cc @@ -26,6 +26,7 @@ #include "impeller/geometry/matrix.h" #include "impeller/geometry/scalar.h" #include "impeller/runtime_stage/runtime_stage.h" +#include "runtime_stage_types_flatbuffers.h" #include "spirv_common.hpp" namespace impeller { @@ -372,6 +373,27 @@ std::shared_ptr Reflector::GenerateRuntimeStageData() uniform_description.columns = spir_type.columns; uniform_description.bit_width = spir_type.width; uniform_description.array_elements = GetArrayElements(spir_type); + + if (TargetPlatformIsMetal(options_.target_platform) && + uniform_description.type == spirv_cross::SPIRType::BaseType::Float) { + // Metal aligns float3 to 16 bytes. + // Metal aligns float3x3 COLUMNS to 16 bytes. + // For float3: Size 12. Padding 4. Stride 16. + // For float3x3: Size 36. Padding 12 (4 per col). Stride 48. + + if (spir_type.vecsize == 3 && + (spir_type.columns == 1 || spir_type.columns == 3)) { + for (size_t c = 0; c < spir_type.columns; c++) { + for (size_t v = 0; v < 3; v++) { + uniform_description.padding_layout.push_back( + fb::PaddingType::kFloat); + } + uniform_description.padding_layout.push_back( + fb::PaddingType::kPadding); + } + } + } + FML_CHECK(data->backend != RuntimeStageBackend::kVulkan || spir_type.basetype == spirv_cross::SPIRType::BaseType::SampledImage) @@ -396,7 +418,7 @@ std::shared_ptr Reflector::GenerateRuntimeStageData() size_t binding = compiler_->get_decoration(ubo.id, spv::Decoration::DecorationBinding); auto members = ReadStructMembers(ubo.type_id); - std::vector struct_layout; + std::vector padding_layout; size_t float_count = 0; for (size_t i = 0; i < members.size(); i += 1) { @@ -407,7 +429,7 @@ std::shared_ptr Reflector::GenerateRuntimeStageData() size_t padding_count = (member.size + sizeof(float) - 1) / sizeof(float); while (padding_count > 0) { - struct_layout.push_back(0); + padding_layout.push_back(fb::PaddingType::kPadding); padding_count--; } break; @@ -418,18 +440,18 @@ std::shared_ptr Reflector::GenerateRuntimeStageData() // and 0 layout property per byte of padding for (auto i = 0; i < member.array_elements; i++) { for (auto j = 0u; j < member.size / sizeof(float); j++) { - struct_layout.push_back(1); + padding_layout.push_back(fb::PaddingType::kFloat); } for (auto j = 0u; j < member.element_padding / sizeof(float); j++) { - struct_layout.push_back(0); + padding_layout.push_back(fb::PaddingType::kPadding); } } } else { size_t member_float_count = member.byte_length / sizeof(float); float_count += member_float_count; while (member_float_count > 0) { - struct_layout.push_back(1); + padding_layout.push_back(fb::PaddingType::kFloat); member_float_count--; } } @@ -446,7 +468,7 @@ std::shared_ptr Reflector::GenerateRuntimeStageData() .location = binding, .binding = binding, .type = spirv_cross::SPIRType::Struct, - .struct_layout = std::move(struct_layout), + .padding_layout = std::move(padding_layout), .struct_float_count = float_count, }); } diff --git a/engine/src/flutter/impeller/compiler/runtime_stage_data.cc b/engine/src/flutter/impeller/compiler/runtime_stage_data.cc index b745ec94673..ab184cc3182 100644 --- a/engine/src/flutter/impeller/compiler/runtime_stage_data.cc +++ b/engine/src/flutter/impeller/compiler/runtime_stage_data.cc @@ -332,8 +332,8 @@ std::unique_ptr RuntimeStageData::CreateStageFlatbuffer( desc->array_elements = uniform.array_elements.value(); } - for (const auto& byte_type : uniform.struct_layout) { - desc->struct_layout.push_back(static_cast(byte_type)); + for (const auto& byte_type : uniform.padding_layout) { + desc->padding_layout.push_back(static_cast(byte_type)); } desc->struct_float_count = uniform.struct_float_count; diff --git a/engine/src/flutter/impeller/compiler/types.h b/engine/src/flutter/impeller/compiler/types.h index 2776ef432b9..f683b3554e8 100644 --- a/engine/src/flutter/impeller/compiler/types.h +++ b/engine/src/flutter/impeller/compiler/types.h @@ -12,6 +12,7 @@ #include #include +#include "runtime_stage_types_flatbuffers.h" #include "shaderc/shaderc.hpp" #include "spirv_cross.hpp" #include "spirv_msl.hpp" @@ -55,7 +56,11 @@ struct UniformDescription { size_t columns = 0u; size_t bit_width = 0u; std::optional array_elements = std::nullopt; - std::vector struct_layout = {}; + /// The layout of padding bytes in the uniform buffer. + /// The format matches the values in the flatbuffer + /// UniformDescription::padding_layout. + /// \see RuntimeEffectContents::EmplaceUniform + std::vector padding_layout = {}; size_t struct_float_count = 0u; }; diff --git a/engine/src/flutter/impeller/core/runtime_types.cc b/engine/src/flutter/impeller/core/runtime_types.cc index 08811275967..34f3e42b0c0 100644 --- a/engine/src/flutter/impeller/core/runtime_types.cc +++ b/engine/src/flutter/impeller/core/runtime_types.cc @@ -6,14 +6,37 @@ namespace impeller { -size_t RuntimeUniformDescription::GetSize() const { - size_t size = dimensions.rows * dimensions.cols * bit_width / 8u; +size_t RuntimeUniformDescription::GetDartSize() const { + size_t size = 0; + if (!padding_layout.empty()) { + for (impeller::RuntimePaddingType byte_type : padding_layout) { + if (byte_type == RuntimePaddingType::kFloat) { + size += sizeof(float); + } + } + } else { + size = dimensions.rows * dimensions.cols * bit_width / 8u; + } + if (array_elements.value_or(0) > 0) { + // Covered by check on the line above. + // NOLINTNEXTLINE(bugprone-unchecked-optional-access) + size *= array_elements.value(); + } + return size; +} + +size_t RuntimeUniformDescription::GetGPUSize() const { + size_t size = 0; + if (padding_layout.empty()) { + size = dimensions.rows * dimensions.cols * bit_width / 8u; + } else { + size = sizeof(float) * padding_layout.size(); + } if (array_elements.value_or(0) > 0) { // Covered by check on the line above. // NOLINTNEXTLINE(bugprone-unchecked-optional-access) size *= array_elements.value(); } - size += sizeof(float) * struct_layout.size(); return size; } diff --git a/engine/src/flutter/impeller/core/runtime_types.h b/engine/src/flutter/impeller/core/runtime_types.h index 84eb052991a..163a434d444 100644 --- a/engine/src/flutter/impeller/core/runtime_types.h +++ b/engine/src/flutter/impeller/core/runtime_types.h @@ -38,6 +38,11 @@ struct RuntimeUniformDimensions { size_t cols = 0; }; +enum class RuntimePaddingType : uint8_t { + kPadding = 0, + kFloat = 1, +}; + struct RuntimeUniformDescription { std::string name; size_t location = 0u; @@ -47,11 +52,16 @@ struct RuntimeUniformDescription { RuntimeUniformDimensions dimensions = {}; size_t bit_width = 0u; std::optional array_elements; - std::vector struct_layout = {}; + std::vector padding_layout = {}; size_t struct_float_count = 0u; - /// @brief Computes the total number of bytes that this uniform requires. - size_t GetSize() const; + /// @brief Computes the total number of bytes that this uniform requires for + /// representation in the Dart float buffer. + size_t GetDartSize() const; + + /// @brief Computes the total number of bytes that this uniform requires for + /// representation in the GPU. + size_t GetGPUSize() const; }; } // namespace impeller diff --git a/engine/src/flutter/impeller/display_list/dl_runtime_effect_impeller.cc b/engine/src/flutter/impeller/display_list/dl_runtime_effect_impeller.cc index c85085fc67b..73e360f65b0 100644 --- a/engine/src/flutter/impeller/display_list/dl_runtime_effect_impeller.cc +++ b/engine/src/flutter/impeller/display_list/dl_runtime_effect_impeller.cc @@ -38,7 +38,7 @@ size_t DlRuntimeEffectImpeller::uniform_size() const { size_t total = 0; for (const auto& uniform : runtime_stage_->GetUniforms()) { - total += uniform.GetSize(); + total += uniform.GetGPUSize(); } return total; } diff --git a/engine/src/flutter/impeller/entity/contents/runtime_effect_contents.cc b/engine/src/flutter/impeller/entity/contents/runtime_effect_contents.cc index 54a891d1d62..2cce0a5c3eb 100644 --- a/engine/src/flutter/impeller/entity/contents/runtime_effect_contents.cc +++ b/engine/src/flutter/impeller/entity/contents/runtime_effect_contents.cc @@ -24,35 +24,48 @@ namespace impeller { -namespace { -constexpr char kPaddingType = 0; -constexpr char kFloatType = 1; -} // namespace - // static -BufferView RuntimeEffectContents::EmplaceVulkanUniform( - const std::shared_ptr>& input_data, +BufferView RuntimeEffectContents::EmplaceUniform( + const uint8_t* source_data, HostBuffer& data_host_buffer, - const RuntimeUniformDescription& uniform, - size_t minimum_uniform_alignment) { - // TODO(jonahwilliams): rewrite this to emplace directly into - // HostBuffer. - std::vector uniform_buffer; - uniform_buffer.reserve(uniform.struct_layout.size()); - size_t uniform_byte_index = 0u; - for (char byte_type : uniform.struct_layout) { - if (byte_type == kPaddingType) { - uniform_buffer.push_back(0.f); - } else { - FML_DCHECK(byte_type == kFloatType); - uniform_buffer.push_back(reinterpret_cast( - input_data->data())[uniform_byte_index++]); - } + const RuntimeUniformDescription& uniform) { + size_t minimum_uniform_alignment = + data_host_buffer.GetMinimumUniformAlignment(); + size_t alignment = std::max(uniform.bit_width / 8, minimum_uniform_alignment); + + if (uniform.padding_layout.empty()) { + return data_host_buffer.Emplace(source_data, uniform.GetGPUSize(), + alignment); } + // If the uniform has a padding layout, we need to repack the data. + // We can do this by using the EmplaceProc to write directly to the + // HostBuffer. return data_host_buffer.Emplace( - reinterpret_cast(uniform_buffer.data()), - sizeof(float) * uniform_buffer.size(), minimum_uniform_alignment); + uniform.GetGPUSize(), alignment, + [&uniform, source_data](uint8_t* destination) { + size_t count = uniform.array_elements.value_or(1); + if (count == 0) { + // Make sure to run at least once. + count = 1; + } + size_t uniform_byte_index = 0u; + size_t struct_float_index = 0u; + auto* float_destination = reinterpret_cast(destination); + auto* float_source = reinterpret_cast(source_data); + + for (size_t i = 0; i < count; i++) { + for (RuntimePaddingType byte_type : uniform.padding_layout) { + if (byte_type == RuntimePaddingType::kPadding) { + float_destination[struct_float_index++] = 0.f; + } else { + FML_DCHECK(byte_type == RuntimePaddingType::kFloat); + float_destination[struct_float_index++] = + float_source[uniform_byte_index++]; + } + } + } + }); } void RuntimeEffectContents::SetRuntimeStage( @@ -284,12 +297,8 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, << "Uniform " << uniform.name << " had unexpected type kFloat for Vulkan backend."; - size_t alignment = - std::max(uniform.bit_width / 8, - data_host_buffer.GetMinimumUniformAlignment()); - BufferView buffer_view = - data_host_buffer.Emplace(uniform_data_->data() + buffer_offset, - uniform.GetSize(), alignment); + BufferView buffer_view = EmplaceUniform( + uniform_data_->data() + buffer_offset, data_host_buffer, uniform); ShaderUniformSlot uniform_slot; uniform_slot.name = uniform.name.c_str(); @@ -298,7 +307,7 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, DescriptorType::kUniformBuffer, uniform_slot, std::move(metadata), std::move(buffer_view)); buffer_index++; - buffer_offset += uniform.GetSize(); + buffer_offset += uniform.GetDartSize(); buffer_location++; break; } @@ -309,12 +318,10 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, uniform_slot.binding = uniform.location; uniform_slot.name = uniform.name.c_str(); - pass.BindResource(ShaderStage::kFragment, - DescriptorType::kUniformBuffer, uniform_slot, - nullptr, - EmplaceVulkanUniform( - uniform_data_, data_host_buffer, uniform, - data_host_buffer.GetMinimumUniformAlignment())); + pass.BindResource( + ShaderStage::kFragment, DescriptorType::kUniformBuffer, + uniform_slot, nullptr, + EmplaceUniform(uniform_data_->data(), data_host_buffer, uniform)); } } } diff --git a/engine/src/flutter/impeller/entity/contents/runtime_effect_contents.h b/engine/src/flutter/impeller/entity/contents/runtime_effect_contents.h index 0084a3bc252..1985d31000f 100644 --- a/engine/src/flutter/impeller/entity/contents/runtime_effect_contents.h +++ b/engine/src/flutter/impeller/entity/contents/runtime_effect_contents.h @@ -37,11 +37,17 @@ class RuntimeEffectContents final : public ColorSourceContents { bool BootstrapShader(const ContentContext& renderer) const; // Visible for testing - static BufferView EmplaceVulkanUniform( - const std::shared_ptr>& input_data, - HostBuffer& host_buffer, - const RuntimeUniformDescription& uniform, - size_t minimum_uniform_alignment); + /// Copies the uniform data into the host buffer. + /// + /// If the `uniform` has a `padding_layout`, it is used to repack the data. + /// + /// @param source_data The pointer to the start of the uniform data in the + /// source. + /// @param host_buffer The host buffer to emplace the uniform data into. + /// @param uniform The description of the uniform being emplaced. + static BufferView EmplaceUniform(const uint8_t* source_data, + HostBuffer& host_buffer, + const RuntimeUniformDescription& uniform); private: bool RegisterShader(const ContentContext& renderer) const; diff --git a/engine/src/flutter/impeller/entity/entity_unittests.cc b/engine/src/flutter/impeller/entity/entity_unittests.cc index 86c39c5db8d..aae5107e8d9 100644 --- a/engine/src/flutter/impeller/entity/entity_unittests.cc +++ b/engine/src/flutter/impeller/entity/entity_unittests.cc @@ -1922,12 +1922,9 @@ TEST_P(EntityTest, RuntimeEffectSetsRightSizeWhenUniformIsStruct) { uniform_data->resize(sizeof(FragUniforms)); memcpy(uniform_data->data(), &frag_uniforms, sizeof(FragUniforms)); - auto buffer_view = RuntimeEffectContents::EmplaceVulkanUniform( - uniform_data, GetContentContext()->GetTransientsDataBuffer(), - runtime_stage->GetUniforms()[0], - GetContentContext() - ->GetTransientsDataBuffer() - .GetMinimumUniformAlignment()); + auto buffer_view = RuntimeEffectContents::EmplaceUniform( + uniform_data->data(), GetContentContext()->GetTransientsDataBuffer(), + runtime_stage->GetUniforms()[0]); // 16 bytes: // 8 bytes for iResolution diff --git a/engine/src/flutter/impeller/runtime_stage/runtime_stage.cc b/engine/src/flutter/impeller/runtime_stage/runtime_stage.cc index 6d0bfb31d49..732477d4cd6 100644 --- a/engine/src/flutter/impeller/runtime_stage/runtime_stage.cc +++ b/engine/src/flutter/impeller/runtime_stage/runtime_stage.cc @@ -81,9 +81,18 @@ absl::StatusOr RuntimeStage::Create( static_cast(i->rows()), static_cast(i->columns())}; desc.bit_width = i->bit_width(); desc.array_elements = i->array_elements(); - if (i->struct_layout()) { - for (const auto& byte_type : *i->struct_layout()) { - desc.struct_layout.push_back(static_cast(byte_type)); + if (i->padding_layout()) { + for (const auto& byte_type : *i->padding_layout()) { + impeller::RuntimePaddingType type; + switch (byte_type) { + case fb::PaddingType::kPadding: + type = impeller::RuntimePaddingType::kPadding; + break; + case fb::PaddingType::kFloat: + type = impeller::RuntimePaddingType::kFloat; + break; + } + desc.padding_layout.push_back(type); } } desc.struct_float_count = i->struct_float_count(); diff --git a/engine/src/flutter/impeller/runtime_stage/runtime_stage_types.fbs b/engine/src/flutter/impeller/runtime_stage/runtime_stage_types.fbs index 941184b7d6c..1df3941399b 100644 --- a/engine/src/flutter/impeller/runtime_stage/runtime_stage_types.fbs +++ b/engine/src/flutter/impeller/runtime_stage/runtime_stage_types.fbs @@ -8,7 +8,7 @@ namespace impeller.fb; // incremented any time there is a change to this file which changes the // resulting flat buffer format. enum RuntimeStagesFormatVersion:uint32 { - kVersion = 1 + kVersion = 2 } enum Stage:byte { @@ -29,7 +29,7 @@ enum UniformDataType:uint32 { // A struct is made up solely of 4 byte floats and 4-byte paddings between // them. // This enum describes whether a particular byte is a float or padding. -enum StructByteType:uint8 { +enum PaddingType:uint8 { kPadding = 0, kFloat = 1, } @@ -43,7 +43,7 @@ table UniformDescription { rows: uint64; columns: uint64; array_elements: uint64; - struct_layout: [StructByteType]; + padding_layout: [PaddingType]; struct_float_count: uint64; } 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 c66bb133bc5..40a67af01a7 100644 --- a/engine/src/flutter/impeller/runtime_stage/runtime_stage_unittests.cc +++ b/engine/src/flutter/impeller/runtime_stage/runtime_stage_unittests.cc @@ -94,6 +94,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { EXPECT_EQ(uni->dimensions.cols, 1u); EXPECT_EQ(uni->location, 0u); EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_TRUE(uni->padding_layout.empty()); } { auto uni = stage->GetUniform("u_alpha"); @@ -102,6 +103,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { EXPECT_EQ(uni->dimensions.cols, 1u); EXPECT_EQ(uni->location, 1u); EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_TRUE(uni->padding_layout.empty()); } { auto uni = stage->GetUniform("u_sparkle_color"); @@ -110,6 +112,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { EXPECT_EQ(uni->dimensions.cols, 1u); EXPECT_EQ(uni->location, 2u); EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_TRUE(uni->padding_layout.empty()); } { auto uni = stage->GetUniform("u_sparkle_alpha"); @@ -118,6 +121,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { EXPECT_EQ(uni->dimensions.cols, 1u); EXPECT_EQ(uni->location, 3u); EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_TRUE(uni->padding_layout.empty()); } { auto uni = stage->GetUniform("u_blur"); @@ -126,6 +130,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { EXPECT_EQ(uni->dimensions.cols, 1u); EXPECT_EQ(uni->location, 4u); EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_TRUE(uni->padding_layout.empty()); } { auto uni = stage->GetUniform("u_radius_scale"); @@ -134,6 +139,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { EXPECT_EQ(uni->dimensions.cols, 1u); EXPECT_EQ(uni->location, 6u); EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_TRUE(uni->padding_layout.empty()); } { auto uni = stage->GetUniform("u_max_radius"); @@ -142,6 +148,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { EXPECT_EQ(uni->dimensions.cols, 1u); EXPECT_EQ(uni->location, 7u); EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_TRUE(uni->padding_layout.empty()); } { auto uni = stage->GetUniform("u_resolution_scale"); @@ -150,6 +157,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { EXPECT_EQ(uni->dimensions.cols, 1u); EXPECT_EQ(uni->location, 8u); EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_TRUE(uni->padding_layout.empty()); } { auto uni = stage->GetUniform("u_noise_scale"); @@ -158,6 +166,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { EXPECT_EQ(uni->dimensions.cols, 1u); EXPECT_EQ(uni->location, 9u); EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_TRUE(uni->padding_layout.empty()); } { auto uni = stage->GetUniform("u_noise_phase"); @@ -166,6 +175,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { EXPECT_EQ(uni->dimensions.cols, 1u); EXPECT_EQ(uni->location, 10u); EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_TRUE(uni->padding_layout.empty()); } { @@ -175,6 +185,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { EXPECT_EQ(uni->dimensions.cols, 1u); EXPECT_EQ(uni->location, 11u); EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_TRUE(uni->padding_layout.empty()); } { auto uni = stage->GetUniform("u_circle2"); @@ -183,6 +194,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { EXPECT_EQ(uni->dimensions.cols, 1u); EXPECT_EQ(uni->location, 12u); EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_TRUE(uni->padding_layout.empty()); } { auto uni = stage->GetUniform("u_circle3"); @@ -191,6 +203,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { EXPECT_EQ(uni->dimensions.cols, 1u); EXPECT_EQ(uni->location, 13u); EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_TRUE(uni->padding_layout.empty()); } { auto uni = stage->GetUniform("u_rotation1"); @@ -199,6 +212,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { EXPECT_EQ(uni->dimensions.cols, 1u); EXPECT_EQ(uni->location, 14u); EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_TRUE(uni->padding_layout.empty()); } { auto uni = stage->GetUniform("u_rotation2"); @@ -207,6 +221,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { EXPECT_EQ(uni->dimensions.cols, 1u); EXPECT_EQ(uni->location, 15u); EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_TRUE(uni->padding_layout.empty()); } { auto uni = stage->GetUniform("u_rotation3"); @@ -215,6 +230,7 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { EXPECT_EQ(uni->dimensions.cols, 1u); EXPECT_EQ(uni->location, 16u); EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_TRUE(uni->padding_layout.empty()); } break; } @@ -234,14 +250,15 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { // introduced. // This means 36 * 4 = 144 bytes total. - EXPECT_EQ(uni->GetSize(), 144u); - std::vector layout(uni->GetSize() / sizeof(float), 1); - layout[5] = 0; - layout[6] = 0; - layout[7] = 0; - layout[23] = 0; + EXPECT_EQ(uni->GetGPUSize(), 144u); + std::vector layout(uni->GetGPUSize() / sizeof(float), + RuntimePaddingType::kFloat); + layout[5] = RuntimePaddingType::kPadding; + layout[6] = RuntimePaddingType::kPadding; + layout[7] = RuntimePaddingType::kPadding; + layout[23] = RuntimePaddingType::kPadding; - EXPECT_THAT(uni->struct_layout, ::testing::ElementsAreArray(layout)); + EXPECT_THAT(uni->padding_layout, ::testing::ElementsAreArray(layout)); break; } } diff --git a/engine/src/flutter/lib/ui/painting/fragment_program.cc b/engine/src/flutter/lib/ui/painting/fragment_program.cc index ae705901f8f..3877760ba59 100644 --- a/engine/src/flutter/lib/ui/painting/fragment_program.cc +++ b/engine/src/flutter/lib/ui/painting/fragment_program.cc @@ -82,7 +82,8 @@ Dart_Handle ConvertUniformDescriptionToMap( FML_DCHECK(!Dart_IsError(result)); } { // 2 - Dart_Handle size = Dart_NewIntegerFromUint64(uniform_description.GetSize()); + Dart_Handle size = + Dart_NewIntegerFromUint64(uniform_description.GetDartSize()); FML_DCHECK(!Dart_IsError(size)); [[maybe_unused]] Dart_Handle result = Dart_ListSetAt(keys, 2, Dart_NewStringFromCString("size")); @@ -161,7 +162,7 @@ std::string FragmentProgram::initFromAsset(const std::string& asset_name) { impeller::RuntimeUniformType::kSampledImage) { sampled_image_count++; } else { - other_uniforms_bytes += uniform_description.GetSize(); + other_uniforms_bytes += uniform_description.GetDartSize(); } } diff --git a/engine/src/flutter/testing/dart/fragment_shader_test.dart b/engine/src/flutter/testing/dart/fragment_shader_test.dart index 3a6fe2570b6..d573fe6e89b 100644 --- a/engine/src/flutter/testing/dart/fragment_shader_test.dart +++ b/engine/src/flutter/testing/dart/fragment_shader_test.dart @@ -90,7 +90,7 @@ void main() async { ); }); - _runSkiaTest('FragmentProgram getUniformFloat offset overflow', () async { + test('FragmentProgram getUniformFloat offset overflow', () async { expect( () => shader.getUniformFloat('iVec2Uniform', 2), throwsA( @@ -101,9 +101,9 @@ void main() async { ), ), ); - }); + }, skip: !_isMacMetal()); - _runSkiaTest('FragmentProgram getUniformFloat offset underflow', () async { + test('FragmentProgram getUniformFloat offset underflow', () async { expect( () => shader.getUniformFloat('iVec2Uniform', -1), throwsA( @@ -114,14 +114,14 @@ void main() async { ), ), ); - }); + }, skip: !_isMacMetal()); - _runSkiaTest('FragmentProgram getUniformVec2', () async { + test('FragmentProgram getUniformVec2', () async { final UniformVec2Slot slot = shader.getUniformVec2('iVec2Uniform'); slot.set(6.0, 7.0); - }); + }, skip: !_isMacMetal()); - _runSkiaTest('FragmentProgram getUniformVec2 wrong size', () async { + test('FragmentProgram getUniformVec2 wrong size', () async { expect( () => shader.getUniformVec2('iVec3Uniform'), throwsA( @@ -142,14 +142,14 @@ void main() async { ), ), ); - }); + }, skip: !_isMacMetal()); - _runSkiaTest('FragmentProgram getUniformVec3', () async { + test('FragmentProgram getUniformVec3', () async { final UniformVec3Slot slot = shader.getUniformVec3('iVec3Uniform'); slot.set(0.8, 0.1, 0.3); - }); + }, skip: !_isMacMetal()); - _runSkiaTest('FragmentProgram getUniformVec3 wrong size', () async { + test('FragmentProgram getUniformVec3 wrong size', () async { expect( () => shader.getUniformVec3('iVec2Uniform'), throwsA( @@ -170,14 +170,14 @@ void main() async { ), ), ); - }); + }, skip: !_isMacMetal()); - _runSkiaTest('FragmentProgram getUniformVec4', () async { + test('FragmentProgram getUniformVec4', () async { final UniformVec4Slot slot = shader.getUniformVec4('iVec4Uniform'); slot.set(11.0, 22.0, 19.0, 96.0); - }); + }, skip: !_isMacMetal()); - _runSkiaTest('FragmentProgram getUniformVec4 wrong size', () async { + test('FragmentProgram getUniformVec4 wrong size', () async { expect( () => shader.getUniformVec4('iVec3Uniform'), throwsA( @@ -188,18 +188,18 @@ void main() async { ), ), ); - }); + }, skip: !_isMacMetal()); - _runSkiaTest('FragmentProgram getUniformArray float', () async { + test('FragmentProgram getUniformArray float', () async { final UniformArray slots = shader.getUniformFloatArray( 'iFloatArrayUniform', ); expect(slots.length, 10); slots[0].set(1.0); slots[1].set(1.0); - }); + }, skip: !_isMacMetal()); - _runSkiaTest('FragmentProgram getUniformArray not found', () async { + test('FragmentProgram getUniformArray not found', () async { expect( () => shader.getUniformFloatArray('unknown'), throwsA( @@ -210,15 +210,15 @@ void main() async { ), ), ); - }); + }, skip: !_isMacMetal()); - _runSkiaTest('FragmentProgram getUniformArrayVec2', () async { + test('FragmentProgram getUniformArrayVec2', () async { final UniformArray slots = shader.getUniformVec2Array('iVec2ArrayUniform'); expect(slots.length, 3); slots[0].set(1.0, 1.0); - }); + }, skip: !_isMacMetal()); - _runSkiaTest('FragmentProgram getUniformArrayVec2 wrong type', () async { + test('FragmentProgram getUniformArrayVec2 wrong type', () async { expect( () => shader.getUniformVec2Array('iVec3ArrayUniform'), throwsA( @@ -229,15 +229,15 @@ void main() async { ), ), ); - }); + }, skip: !_isMacMetal()); - _runSkiaTest('FragmentProgram getUniformArrayVec3', () async { + test('FragmentProgram getUniformArrayVec3', () async { final UniformArray slots = shader.getUniformVec3Array('iVec3ArrayUniform'); expect(slots.length, 3); slots[0].set(1.0, 1.0, 1.0); - }); + }, skip: !_isMacMetal()); - _runSkiaTest('FragmentProgram getUniformArrayVec3 wrong type', () async { + test('FragmentProgram getUniformArrayVec3 wrong type', () async { expect( () => shader.getUniformVec3Array('iFloatArrayUniform'), throwsA( @@ -248,15 +248,15 @@ void main() async { ), ), ); - }); + }, skip: !_isMacMetal()); - _runSkiaTest('FragmentProgram getUniformArrayVec4', () async { + test('FragmentProgram getUniformArrayVec4', () async { final UniformArray slots = shader.getUniformVec4Array('iVec4ArrayUniform'); expect(slots.length, 3); slots[0].set(1.0, 1.0, 1.0, 1.0); - }); + }, skip: !_isMacMetal()); - _runSkiaTest('FragmentProgram getUniformArrayVec4 wrong type', () async { + test('FragmentProgram getUniformArrayVec4 wrong type', () async { expect( () => shader.getUniformVec4Array('iFloatArrayUniform'), throwsA( @@ -267,7 +267,7 @@ void main() async { ), ), ); - }); + }, skip: !_isMacMetal()); }); test('FragmentProgram getImageSampler', () async { @@ -695,39 +695,31 @@ void main() async { shader.dispose(); }); - test( - 'FragmentShader shader with array uniforms renders correctly', - () async { - final FragmentProgram program = await FragmentProgram.fromAsset('uniform_arrays.frag.iplr'); + test('FragmentShader shader with array uniforms renders correctly', () async { + final FragmentProgram program = await FragmentProgram.fromAsset('uniform_arrays.frag.iplr'); - final FragmentShader shader = program.fragmentShader(); - for (var i = 0; i < 20; i++) { - shader.setFloat(i, i.toDouble()); - } + final FragmentShader shader = program.fragmentShader(); + for (var i = 0; i < 20; i++) { + shader.setFloat(i, i.toDouble()); + } - await _expectShaderRendersGreen(shader); - shader.dispose(); - }, - skip: Platform.executableArguments.contains('--impeller-backend=metal'), - ); + await _expectShaderRendersGreen(shader); + shader.dispose(); + }); - test( - 'FragmentShader shader with mat2 uniform renders correctly', - () async { - final FragmentProgram program = await FragmentProgram.fromAsset('uniform_mat2.frag.iplr'); + test('FragmentShader shader with mat2 uniform renders correctly', () async { + final FragmentProgram program = await FragmentProgram.fromAsset('uniform_mat2.frag.iplr'); - final FragmentShader shader = program.fragmentShader(); + final FragmentShader shader = program.fragmentShader(); - shader.setFloat(0, 4.0); // m00 - shader.setFloat(1, 8.0); // m01 - shader.setFloat(2, 16.0); // m10 - shader.setFloat(3, 32.0); // m11 + shader.setFloat(0, 4.0); // m00 + shader.setFloat(1, 8.0); // m01 + shader.setFloat(2, 16.0); // m10 + shader.setFloat(3, 32.0); // m11 - await _expectShaderRendersGreen(shader); - shader.dispose(); - }, - skip: Platform.executableArguments.contains('--impeller-backend=metal'), - ); + await _expectShaderRendersGreen(shader); + shader.dispose(); + }); _runImpellerTest( 'ImageFilter.shader errors if shader does not have correct uniform layout', @@ -878,6 +870,11 @@ void _runImpellerTest(String name, Future Function() callback, {Object? sk }, skip: skip); } +// TODO(walley892): remove this function and associated test skips, https://github.com/flutter/flutter/issues/181562 +bool _isMacMetal() { + return Platform.isMacOS && Platform.executableArguments.contains('--impeller-backend=metal'); +} + // Expect that all of the shaders in this folder render green. // Keeping the outer loop of the test synchronous allows for easy printing // of the file name within the test case.