From e5b207105fce9c1410cc11befd89c16fd6cbbd06 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Thu, 4 Jan 2024 14:17:39 -0800 Subject: [PATCH 01/13] Vulkan runtime effects started --- impeller/aiks/aiks_unittests.cc | 8 - impeller/compiler/compiler.cc | 31 ++- impeller/compiler/compiler_backend.cc | 5 +- impeller/compiler/compiler_backend.h | 1 + impeller/compiler/impellerc_main.cc | 2 +- impeller/compiler/reflector.cc | 33 ++- impeller/compiler/spirv_compiler.cc | 2 + impeller/compiler/spirv_compiler.h | 2 + .../contents/runtime_effect_contents.cc | 261 ++++++++++-------- impeller/entity/entity_unittests.cc | 4 - impeller/fixtures/BUILD.gn | 1 + .../golden_tests/golden_playground_test.h | 6 - .../backend/vulkan/playground_impl_vk.cc | 4 +- impeller/playground/playground_test.h | 6 - .../backend/vulkan/binding_helpers_vk.cc | 1 - .../backend/vulkan/shader_library_vk.cc | 12 +- .../runtime_stage/runtime_stage_unittests.cc | 148 +++++----- impeller/tools/impeller.gni | 1 + lib/ui/fixtures/shaders/BUILD.gn | 1 + 19 files changed, 280 insertions(+), 249 deletions(-) diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index 61ddc200d3f83..21ce94ce1c21d 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -3033,10 +3033,6 @@ TEST_P(AiksTest, CanRenderForegroundAdvancedBlendWithMaskBlur) { // Regression test for https://github.com/flutter/flutter/issues/126701 . TEST_P(AiksTest, CanRenderClippedRuntimeEffects) { - if (!BackendSupportsFragmentProgram()) { - GTEST_SKIP_("This backend doesn't support runtime effects."); - } - auto runtime_stages = OpenAssetAsRuntimeStage("runtime_stage_example.frag.iplr"); @@ -3070,10 +3066,6 @@ TEST_P(AiksTest, CanRenderClippedRuntimeEffects) { } TEST_P(AiksTest, DrawPaintTransformsBounds) { - if (!BackendSupportsFragmentProgram()) { - GTEST_SKIP_("This backend doesn't support runtime effects."); - } - auto runtime_stages = OpenAssetAsRuntimeStage("gradient.frag.iplr"); auto runtime_stage = runtime_stages[RuntimeStageBackend::kMetal]; ASSERT_TRUE(runtime_stage); diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 261ab60a64360..6123ad3f15982 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -19,6 +19,7 @@ #include "impeller/compiler/includer.h" #include "impeller/compiler/logger.h" #include "impeller/compiler/spirv_compiler.h" +#include "impeller/compiler/types.h" #include "impeller/compiler/uniform_sorter.h" #include "impeller/compiler/utilities.h" @@ -122,15 +123,13 @@ static CompilerBackend CreateMSLCompiler( static CompilerBackend CreateVulkanCompiler( const spirv_cross::ParsedIR& ir, const SourceOptions& source_options) { - // TODO(dnfield): It seems like what we'd want is a CompilerGLSL with - // vulkan_semantics set to true, but that has regressed some things on GLES - // somehow. In the mean time, go back to using CompilerMSL, but set the Metal - // Language version to something really high so that we don't get weird - // complaints about using Metal features while trying to build Vulkan shaders. - // https://github.com/flutter/flutter/issues/123795 - return CreateMSLCompiler( - ir, source_options, - spirv_cross::CompilerMSL::Options::make_msl_version(3, 0, 0)); + auto gl_compiler = std::make_shared(ir); + spirv_cross::CompilerGLSL::Options sl_options; + sl_options.force_zero_initialized_variables = true; + sl_options.vertex.fixup_clipspace = true; + sl_options.vulkan_semantics = true; + gl_compiler->set_common_options(sl_options); + return CompilerBackend(gl_compiler); } static CompilerBackend CreateGLSLCompiler(const spirv_cross::ParsedIR& ir, @@ -302,18 +301,23 @@ Compiler::Compiler(const std::shared_ptr& source_mapping, } break; case TargetPlatform::kOpenGLES: case TargetPlatform::kOpenGLDesktop: - case TargetPlatform::kVulkan: { + case TargetPlatform::kVulkan: + case TargetPlatform::kRuntimeStageVulkan: { SPIRVCompilerTargetEnv target; target.env = shaderc_target_env::shaderc_target_env_vulkan; target.version = shaderc_env_version::shaderc_env_version_vulkan_1_1; target.spirv_version = shaderc_spirv_version::shaderc_spirv_version_1_3; + if (source_options.target_platform == + TargetPlatform::kRuntimeStageVulkan) { + spirv_options.macro_definitions.push_back("IMPELLER_GRAPHICS_BACKEND"); + spirv_options.relaxed_vulkan_rules = true; + } spirv_options.target = target; } break; case TargetPlatform::kRuntimeStageMetal: - case TargetPlatform::kRuntimeStageGLES: - case TargetPlatform::kRuntimeStageVulkan: { + case TargetPlatform::kRuntimeStageGLES: { SPIRVCompilerTargetEnv target; target.env = shaderc_target_env::shaderc_target_env_opengl; @@ -398,7 +402,8 @@ Compiler::Compiler(const std::shared_ptr& source_mapping, // If the target is Vulkan, our shading language is SPIRV which we already // have. We just need to strip it of debug information. If it isn't, we need // to invoke the appropriate compiler to compile the SPIRV to the target SL. - if (source_options.target_platform == TargetPlatform::kVulkan) { + if (source_options.target_platform == TargetPlatform::kVulkan || + source_options.target_platform == TargetPlatform::kRuntimeStageVulkan) { auto stripped_spirv_options = spirv_options; stripped_spirv_options.generate_debug_info = false; sl_mapping_ = spv_compiler.CompileToSPV( diff --git a/impeller/compiler/compiler_backend.cc b/impeller/compiler/compiler_backend.cc index d6111c9f232e5..58afead588383 100644 --- a/impeller/compiler/compiler_backend.cc +++ b/impeller/compiler/compiler_backend.cc @@ -13,7 +13,10 @@ CompilerBackend::CompilerBackend(MSLCompiler compiler) : CompilerBackend(Type::kMSL, compiler) {} CompilerBackend::CompilerBackend(GLSLCompiler compiler) - : CompilerBackend(Type::kGLSL, compiler) {} + : CompilerBackend(compiler->get_common_options().vulkan_semantics + ? Type::kGLSLVulkan + : Type::kGLSL, + compiler) {} CompilerBackend::CompilerBackend(SkSLCompiler compiler) : CompilerBackend(Type::kSkSL, compiler) {} diff --git a/impeller/compiler/compiler_backend.h b/impeller/compiler/compiler_backend.h index 4b2aabd0e00a3..d2918177fa983 100644 --- a/impeller/compiler/compiler_backend.h +++ b/impeller/compiler/compiler_backend.h @@ -27,6 +27,7 @@ struct CompilerBackend { enum class Type { kMSL, kGLSL, + kGLSLVulkan, kSkSL, }; diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index f9ecc73d6cdf5..7c0c88d2e341e 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -65,7 +65,7 @@ static bool OutputIPLR( if (!sksl_shader) { return false; } - stages.AddShader(sksl_shader); + // stages.AddShader(sksl_shader); } for (const auto& platform : switches.PlatformsToCompile()) { diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 4f0af09d56461..36549d6f11399 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -353,6 +353,7 @@ std::shared_ptr Reflector::GenerateRuntimeStageData() } auto data = std::make_unique(); data->entrypoint = options_.entry_point_name; + FML_LOG(ERROR) << "writing entrypoint as " << options_.entry_point_name; data->stage = entrypoints.front().execution_model; data->shader = shader_data_; data->backend = backend.value(); @@ -360,7 +361,6 @@ std::shared_ptr Reflector::GenerateRuntimeStageData() // Sort the IR so that the uniforms are in declaration order. std::vector uniforms = SortUniforms(ir_.get(), compiler_.GetCompiler()); - for (auto& sorted_id : uniforms) { auto var = ir_->ids[sorted_id].get(); const auto spir_type = compiler_->get_type(var.basetype); @@ -376,6 +376,35 @@ std::shared_ptr Reflector::GenerateRuntimeStageData() data->uniforms.emplace_back(std::move(uniform_description)); } + const auto ubos = compiler_->get_shader_resources().uniform_buffers; + if (data->backend == RuntimeStageBackend::kVulkan && !ubos.empty()) { + if (ubos.size() != 1 && + ubos[0].name != "_RESERVED_IDENTIFIER_FIXUP_gl_DefaultUniformBlock") { + VALIDATION_LOG << "Expected a single UBO resource named " + "'_RESERVED_IDENTIFIER_FIXUP_gl_DefaultUniformBlock' " + "for Vulkan runtime stage backend."; + return nullptr; + } + + const auto& ubo = ubos[0]; + + const auto& struct_type = compiler_->get_type(ubo.type_id); + FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct); + for (size_t i = 0; i < struct_type.member_types.size(); i += 1) { + const auto spir_type = compiler_->get_type(struct_type.member_types[i]); + UniformDescription uniform_description; + uniform_description.name = + compiler_->get_member_name(ubo.base_type_id, i); + uniform_description.location = i; + uniform_description.type = spir_type.basetype; + uniform_description.rows = spir_type.vecsize; + uniform_description.columns = spir_type.columns; + uniform_description.bit_width = spir_type.width; + uniform_description.array_elements = GetArrayElements(spir_type); + data->uniforms.emplace_back(std::move(uniform_description)); + } + } + // We only need to worry about storing vertex attributes. if (entrypoints.front().execution_model == spv::ExecutionModelVertex) { const auto inputs = compiler_->get_shader_resources().stage_inputs; @@ -425,6 +454,8 @@ static std::string ToString(CompilerBackend::Type type) { return "Metal Shading Language"; case CompilerBackend::Type::kGLSL: return "OpenGL Shading Language"; + case CompilerBackend::Type::kGLSLVulkan: + return "OpenGL Shading Language (Relaxed Vulkan Semantics)"; case CompilerBackend::Type::kSkSL: return "SkSL Shading Language"; } diff --git a/impeller/compiler/spirv_compiler.cc b/impeller/compiler/spirv_compiler.cc index a5bd9b66b8808..211073ab5ddca 100644 --- a/impeller/compiler/spirv_compiler.cc +++ b/impeller/compiler/spirv_compiler.cc @@ -310,6 +310,8 @@ shaderc::CompileOptions SPIRVCompilerOptions::BuildShadercOptions() const { options.SetIncluder(UniqueIncluder::Make(includer)); } + options.SetVulkanRulesRelaxed(relaxed_vulkan_rules); + return options; } diff --git a/impeller/compiler/spirv_compiler.h b/impeller/compiler/spirv_compiler.h index 9f6463d4de542..3798b6d53601d 100644 --- a/impeller/compiler/spirv_compiler.h +++ b/impeller/compiler/spirv_compiler.h @@ -49,6 +49,8 @@ struct SPIRVCompilerOptions { std::shared_ptr includer; + bool relaxed_vulkan_rules = false; + shaderc::CompileOptions BuildShadercOptions() const; }; diff --git a/impeller/entity/contents/runtime_effect_contents.cc b/impeller/entity/contents/runtime_effect_contents.cc index a2a5244b6b038..3890f83092574 100644 --- a/impeller/entity/contents/runtime_effect_contents.cc +++ b/impeller/entity/contents/runtime_effect_contents.cc @@ -42,53 +42,44 @@ 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 MakeShaderMetadata( - const RuntimeUniformDescription& uniform) { - auto metadata = std::make_shared(); - metadata->name = uniform.name; - metadata->members.emplace_back(ShaderStructMemberMetadata{ - .type = GetShaderType(uniform.type), - .size = uniform.GetSize(), - .byte_length = uniform.bit_width / 8, - }); - - return metadata; -} +// 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 MakeShaderMetadata( +// const RuntimeUniformDescription& uniform) { +// auto metadata = std::make_shared(); +// 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. - 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(); @@ -165,6 +156,13 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, auto vertex_descriptor = std::make_shared(); vertex_descriptor->SetStageInputs(VS::kAllShaderStageInputs, VS::kInterleavedBufferLayout); + vertex_descriptor->RegisterDescriptorSetLayouts(VS::kDescriptorSetLayouts); + vertex_descriptor->RegisterDescriptorSetLayouts( + std::array{DescriptorSetLayout{ + 64, + DescriptorType::kUniformBuffer, + ShaderStage::kFragment, + }}); desc.SetVertexDescriptor(std::move(vertex_descriptor)); desc.SetColorAttachmentDescriptor( 0u, {.format = color_attachment_format, .blending_enabled = true}); @@ -206,84 +204,111 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, /// Fragment stage uniforms. /// - size_t minimum_sampler_index = 100000000; - size_t buffer_index = 0; - size_t buffer_offset = 0; - for (const auto& uniform : runtime_stage_->GetUniforms()) { - std::shared_ptr metadata = MakeShaderMetadata(uniform); - - switch (uniform.type) { - case kSampledImage: { - // Sampler uniforms are ordered in the IPLR according to their - // declaration and the uniform location reflects the correct offset to - // be mapped to - except that it may include all proceeding float - // uniforms. For example, a float sampler that comes after 4 float - // uniforms may have a location of 4. To convert to the actual offset we - // need to find the largest location assigned to a float uniform and - // then subtract this from all uniform locations. This is more or less - // the same operation we previously performed in the shader compiler. - minimum_sampler_index = - std::min(minimum_sampler_index, uniform.location); - break; - } - case kFloat: { - size_t alignment = - std::max(uniform.bit_width / 8, DefaultUniformAlignment()); - auto buffer_view = pass.GetTransientsBuffer().Emplace( - uniform_data_->data() + buffer_offset, uniform.GetSize(), - alignment); - - ShaderUniformSlot uniform_slot; - uniform_slot.name = uniform.name.c_str(); - uniform_slot.ext_res_0 = uniform.location; - cmd.BindResource(ShaderStage::kFragment, uniform_slot, metadata, - buffer_view); - buffer_index++; - buffer_offset += uniform.GetSize(); - break; - } - 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 for " << uniform.name - << "."; - return true; - } - } + // size_t minimum_sampler_index = 100000000; + // size_t buffer_index = 0; + // size_t buffer_offset = 0; + + ShaderUniformSlot uniform_slot; + uniform_slot.name = "_RESERVED_IDENTIFIER_FIXUP_gl_DefaultUniformBlock"; + uniform_slot.ext_res_0 = 0; + uniform_slot.binding = 64; + ShaderMetadata metadata; + metadata.name = uniform_slot.name; + auto uniform_count = runtime_stage_->GetUniforms().size(); + metadata.members.emplace_back(ShaderStructMemberMetadata{ + .type = ShaderType::kStruct, + .size = uniform_count * 4, + .byte_length = uniform_count * 4 / 8, + }); + + size_t alignment = std::max(4ul * 3ul, DefaultUniformAlignment()); - size_t sampler_index = 0; + size_t buffer_offset = 0u; for (const auto& uniform : runtime_stage_->GetUniforms()) { - std::shared_ptr metadata = MakeShaderMetadata(uniform); - - switch (uniform.type) { - case kSampledImage: { - FML_DCHECK(sampler_index < texture_inputs_.size()); - auto& input = texture_inputs_[sampler_index]; - - auto sampler = - context->GetSamplerLibrary()->GetSampler(input.sampler_descriptor); - - 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, - input.texture, sampler); - - sampler_index++; - break; - } - default: - continue; - } + FML_LOG(ERROR) << "Uniform " << uniform.name << " has value " + << reinterpret_cast(uniform_data_->data() + + buffer_offset)[0] + << " And size " << uniform.GetSize(); + buffer_offset += uniform.GetSize(); } + auto buffer_view = pass.GetTransientsBuffer().Emplace( + reinterpret_cast(uniform_data_->data()), 3 * sizeof(float), + alignment); + cmd.BindResource(ShaderStage::kFragment, uniform_slot, metadata, buffer_view); + + // for (const auto& uniform : runtime_stage_->GetUniforms()) { + // std::shared_ptr metadata = MakeShaderMetadata(uniform); + + // switch (uniform.type) { + // case kSampledImage: { + // // Sampler uniforms are ordered in the IPLR according to their + // // declaration and the uniform location reflects the correct offset + // to + // // be mapped to - except that it may include all proceeding float + // // uniforms. For example, a float sampler that comes after 4 float + // // uniforms may have a location of 4. To convert to the actual offset + // we + // // need to find the largest location assigned to a float uniform and + // // then subtract this from all uniform locations. This is more or + // less + // // the same operation we previously performed in the shader compiler. + // minimum_sampler_index = + // std::min(minimum_sampler_index, uniform.location); + // break; + // } + // case kFloat: { + // size_t alignment = + // std::max(uniform.bit_width / 8, DefaultUniformAlignment()); + // auto buffer_view = pass.GetTransientsBuffer().Emplace( + // uniform_data_->data() + buffer_offset, uniform.GetSize(), + // alignment); + + // buffer_index++; + // buffer_offset += uniform.GetSize(); + // break; + // } + // 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 for " << uniform.name + // << "."; + // return true; + // } + // } + + // size_t sampler_index = 0; + // for (const auto& uniform : runtime_stage_->GetUniforms()) { + // std::shared_ptr metadata = MakeShaderMetadata(uniform); + + // switch (uniform.type) { + // case kSampledImage: { + // FML_DCHECK(sampler_index < texture_inputs_.size()); + // auto& input = texture_inputs_[sampler_index]; + + // auto sampler = + // context->GetSamplerLibrary()->GetSampler(input.sampler_descriptor); + + // 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, + // input.texture, sampler); + + // sampler_index++; + // break; + // } + // default: + // continue; + // } + // } pass.AddCommand(std::move(cmd)); diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 91d6b6a2735c8..79260171196f3 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -2132,10 +2132,6 @@ TEST_P(EntityTest, YUVToRGBFilter) { } TEST_P(EntityTest, RuntimeEffect) { - if (!BackendSupportsFragmentProgram()) { - GTEST_SKIP_("This backend doesn't support runtime effects."); - } - auto runtime_stages = OpenAssetAsRuntimeStage("runtime_stage_example.frag.iplr"); auto runtime_stage = diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index 473109e2f8208..4968f20abfaa9 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -64,6 +64,7 @@ impellerc("runtime_stages") { shader_target_flags = [ "--runtime-stage-metal", "--runtime-stage-gles", + "--runtime-stage-vulkan", ] iplr = true diff --git a/impeller/golden_tests/golden_playground_test.h b/impeller/golden_tests/golden_playground_test.h index e4fdde82fb5a6..f411615b51d8e 100644 --- a/impeller/golden_tests/golden_playground_test.h +++ b/impeller/golden_tests/golden_playground_test.h @@ -36,12 +36,6 @@ 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 typographer_context); diff --git a/impeller/playground/backend/vulkan/playground_impl_vk.cc b/impeller/playground/backend/vulkan/playground_impl_vk.cc index d05004f4eb43a..0ac2f050ef44c 100644 --- a/impeller/playground/backend/vulkan/playground_impl_vk.cc +++ b/impeller/playground/backend/vulkan/playground_impl_vk.cc @@ -73,7 +73,7 @@ PlaygroundImplVK::PlaygroundImplVK(PlaygroundSwitches switches) return; } - InitGlobalVulkanInstance(); + // InitGlobalVulkanInstance(); ::glfwDefaultWindowHints(); ::glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); @@ -101,6 +101,8 @@ PlaygroundImplVK::PlaygroundImplVK(PlaygroundSwitches switches) return; } + static std::shared_ptr gLeakedContext = context_vk; + // Without this, the playground will timeout waiting for the presentation. // It's better to have some Vulkan validation tests running on CI to catch // regressions, but for now this is a workaround. diff --git a/impeller/playground/playground_test.h b/impeller/playground/playground_test.h index 65ac08e67ecfd..e2051a296776b 100644 --- a/impeller/playground/playground_test.h +++ b/impeller/playground/playground_test.h @@ -42,12 +42,6 @@ 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; diff --git a/impeller/renderer/backend/vulkan/binding_helpers_vk.cc b/impeller/renderer/backend/vulkan/binding_helpers_vk.cc index 676980689ce65..5cf503061a7e9 100644 --- a/impeller/renderer/backend/vulkan/binding_helpers_vk.cc +++ b/impeller/renderer/backend/vulkan/binding_helpers_vk.cc @@ -173,7 +173,6 @@ fml::StatusOr> AllocateAndBindDescriptorSets( auto desc_set = command.pipeline->GetDescriptor() .GetVertexDescriptor() ->GetDescriptorSetLayouts(); - if (!BindBuffers(command.vertex_bindings, allocator, encoder, descriptor_sets[desc_index], desc_set, buffers, writes) || !BindBuffers(command.fragment_bindings, allocator, encoder, diff --git a/impeller/renderer/backend/vulkan/shader_library_vk.cc b/impeller/renderer/backend/vulkan/shader_library_vk.cc index bd85f2559b72f..cf722ae9259fb 100644 --- a/impeller/renderer/backend/vulkan/shader_library_vk.cc +++ b/impeller/renderer/backend/vulkan/shader_library_vk.cc @@ -59,7 +59,9 @@ ShaderLibraryVK::ShaderLibraryVK( const auto& name, // const auto& code // ) -> bool { - if (!RegisterFunction(name, ToShaderStage(type), code)) { + const auto stage = ToShaderStage(type); + if (!RegisterFunction(VKShaderNameToShaderKeyName(name, stage), stage, + code)) { success = false; return false; } @@ -157,17 +159,15 @@ bool ShaderLibraryVK::RegisterFunction( return false; } - const auto key_name = VKShaderNameToShaderKeyName(name, stage); - vk::UniqueShaderModule shader_module = std::move(module.value); ContextVK::SetDebugName(device_holder->GetDevice(), *shader_module, "Shader " + name); WriterLock lock(functions_mutex_); - functions_[ShaderKey{key_name, stage}] = std::shared_ptr( + functions_[ShaderKey{name, stage}] = std::shared_ptr( new ShaderFunctionVK(device_holder_, library_id_, // - key_name, // + name, // stage, // std::move(shader_module) // )); @@ -182,7 +182,7 @@ void ShaderLibraryVK::UnregisterFunction(std::string name, ShaderStage stage) { const auto key = ShaderKey{name, stage}; auto found = functions_.find(key); - if (found != functions_.end()) { + if (found == functions_.end()) { VALIDATION_LOG << "Library function named " << name << " was not found, so it couldn't be unregistered."; return; diff --git a/impeller/runtime_stage/runtime_stage_unittests.cc b/impeller/runtime_stage/runtime_stage_unittests.cc index 1d88146010889..0eaf46c1d529c 100644 --- a/impeller/runtime_stage/runtime_stage_unittests.cc +++ b/impeller/runtime_stage/runtime_stage_unittests.cc @@ -25,10 +25,6 @@ using RuntimeStageTest = RuntimeStagePlayground; INSTANTIATE_PLAYGROUND_SUITE(RuntimeStageTest); TEST_P(RuntimeStageTest, CanReadValidBlob) { - if (!BackendSupportsFragmentProgram()) { - GTEST_SKIP_("This backend doesn't support runtime effects."); - } - const std::shared_ptr fixture = flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr"); ASSERT_TRUE(fixture); @@ -40,10 +36,6 @@ TEST_P(RuntimeStageTest, CanReadValidBlob) { } TEST_P(RuntimeStageTest, CanRejectInvalidBlob) { - if (!BackendSupportsFragmentProgram()) { - GTEST_SKIP_("This backend doesn't support runtime effects."); - } - ScopedValidationDisable disable_validation; const std::shared_ptr fixture = flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr"); @@ -59,10 +51,6 @@ TEST_P(RuntimeStageTest, CanRejectInvalidBlob) { } TEST_P(RuntimeStageTest, CanReadUniforms) { - if (!BackendSupportsFragmentProgram()) { - GTEST_SKIP_("This backend doesn't support runtime effects."); - } - const std::shared_ptr fixture = flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr"); ASSERT_TRUE(fixture); @@ -75,139 +63,135 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { { auto uni = stage->GetUniform("u_color"); ASSERT_NE(uni, nullptr); - ASSERT_EQ(uni->dimensions.rows, 4u); - ASSERT_EQ(uni->dimensions.cols, 1u); - ASSERT_EQ(uni->location, 0u); - ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_EQ(uni->dimensions.rows, 4u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 0u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); } { auto uni = stage->GetUniform("u_alpha"); ASSERT_NE(uni, nullptr); - ASSERT_EQ(uni->dimensions.rows, 1u); - ASSERT_EQ(uni->dimensions.cols, 1u); - ASSERT_EQ(uni->location, 1u); - ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_EQ(uni->dimensions.rows, 1u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 1u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); } { auto uni = stage->GetUniform("u_sparkle_color"); ASSERT_NE(uni, nullptr); - ASSERT_EQ(uni->dimensions.rows, 4u); - ASSERT_EQ(uni->dimensions.cols, 1u); - ASSERT_EQ(uni->location, 2u); - ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_EQ(uni->dimensions.rows, 4u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 2u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); } { auto uni = stage->GetUniform("u_sparkle_alpha"); ASSERT_NE(uni, nullptr); - ASSERT_EQ(uni->dimensions.rows, 1u); - ASSERT_EQ(uni->dimensions.cols, 1u); - ASSERT_EQ(uni->location, 3u); - ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_EQ(uni->dimensions.rows, 1u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 3u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); } { auto uni = stage->GetUniform("u_blur"); ASSERT_NE(uni, nullptr); - ASSERT_EQ(uni->dimensions.rows, 1u); - ASSERT_EQ(uni->dimensions.cols, 1u); - ASSERT_EQ(uni->location, 4u); - ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_EQ(uni->dimensions.rows, 1u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 4u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); } { auto uni = stage->GetUniform("u_radius_scale"); ASSERT_NE(uni, nullptr); - ASSERT_EQ(uni->dimensions.rows, 1u); - ASSERT_EQ(uni->dimensions.cols, 1u); - ASSERT_EQ(uni->location, 6u); - ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_EQ(uni->dimensions.rows, 1u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 6u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); } { auto uni = stage->GetUniform("u_max_radius"); ASSERT_NE(uni, nullptr); - ASSERT_EQ(uni->dimensions.rows, 1u); - ASSERT_EQ(uni->dimensions.cols, 1u); - ASSERT_EQ(uni->location, 7u); - ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_EQ(uni->dimensions.rows, 1u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 7u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); } { auto uni = stage->GetUniform("u_resolution_scale"); ASSERT_NE(uni, nullptr); - ASSERT_EQ(uni->dimensions.rows, 2u); - ASSERT_EQ(uni->dimensions.cols, 1u); - ASSERT_EQ(uni->location, 8u); - ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_EQ(uni->dimensions.rows, 2u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 8u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); } { auto uni = stage->GetUniform("u_noise_scale"); ASSERT_NE(uni, nullptr); - ASSERT_EQ(uni->dimensions.rows, 2u); - ASSERT_EQ(uni->dimensions.cols, 1u); - ASSERT_EQ(uni->location, 9u); - ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_EQ(uni->dimensions.rows, 2u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 9u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); } { auto uni = stage->GetUniform("u_noise_phase"); ASSERT_NE(uni, nullptr); - ASSERT_EQ(uni->dimensions.rows, 1u); - ASSERT_EQ(uni->dimensions.cols, 1u); - ASSERT_EQ(uni->location, 10u); - ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_EQ(uni->dimensions.rows, 1u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 10u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); } { auto uni = stage->GetUniform("u_circle1"); ASSERT_NE(uni, nullptr); - ASSERT_EQ(uni->dimensions.rows, 2u); - ASSERT_EQ(uni->dimensions.cols, 1u); - ASSERT_EQ(uni->location, 11u); - ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_EQ(uni->dimensions.rows, 2u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 11u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); } { auto uni = stage->GetUniform("u_circle2"); ASSERT_NE(uni, nullptr); - ASSERT_EQ(uni->dimensions.rows, 2u); - ASSERT_EQ(uni->dimensions.cols, 1u); - ASSERT_EQ(uni->location, 12u); - ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_EQ(uni->dimensions.rows, 2u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 12u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); } { auto uni = stage->GetUniform("u_circle3"); ASSERT_NE(uni, nullptr); - ASSERT_EQ(uni->dimensions.rows, 2u); - ASSERT_EQ(uni->dimensions.cols, 1u); - ASSERT_EQ(uni->location, 13u); - ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_EQ(uni->dimensions.rows, 2u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 13u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); } { auto uni = stage->GetUniform("u_rotation1"); ASSERT_NE(uni, nullptr); - ASSERT_EQ(uni->dimensions.rows, 2u); - ASSERT_EQ(uni->dimensions.cols, 1u); - ASSERT_EQ(uni->location, 14u); - ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_EQ(uni->dimensions.rows, 2u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 14u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); } { auto uni = stage->GetUniform("u_rotation2"); ASSERT_NE(uni, nullptr); - ASSERT_EQ(uni->dimensions.rows, 2u); - ASSERT_EQ(uni->dimensions.cols, 1u); - ASSERT_EQ(uni->location, 15u); - ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_EQ(uni->dimensions.rows, 2u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 15u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); } { auto uni = stage->GetUniform("u_rotation3"); ASSERT_NE(uni, nullptr); - ASSERT_EQ(uni->dimensions.rows, 2u); - ASSERT_EQ(uni->dimensions.cols, 1u); - ASSERT_EQ(uni->location, 16u); - ASSERT_EQ(uni->type, RuntimeUniformType::kFloat); + EXPECT_EQ(uni->dimensions.rows, 2u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 16u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); } } TEST_P(RuntimeStageTest, CanRegisterStage) { - if (!BackendSupportsFragmentProgram()) { - GTEST_SKIP_("This backend doesn't support runtime effects."); - } - const std::shared_ptr fixture = flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr"); ASSERT_TRUE(fixture); @@ -243,9 +227,6 @@ TEST_P(RuntimeStageTest, CanRegisterStage) { } TEST_P(RuntimeStageTest, CanCreatePipelineFromRuntimeStage) { - if (!BackendSupportsFragmentProgram()) { - GTEST_SKIP_("This backend doesn't support runtime effects."); - } auto stages = OpenAssetAsRuntimeStage("ink_sparkle.frag.iplr"); auto stage = stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())]; @@ -263,6 +244,7 @@ TEST_P(RuntimeStageTest, CanCreatePipelineFromRuntimeStage) { auto vertex_descriptor = std::make_shared(); vertex_descriptor->SetStageInputs(VS::kAllShaderStageInputs, VS::kInterleavedBufferLayout); + vertex_descriptor->RegisterDescriptorSetLayouts(VS::kDescriptorSetLayouts); desc.SetVertexDescriptor(std::move(vertex_descriptor)); ColorAttachmentDescriptor color0; diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index 0fdcf260dcb27..e605d7820f154 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -289,6 +289,7 @@ template("_impellerc") { # --vulkan # --runtime-stage-metal # --runtime-stage-gles +# --runtime-stage-vulkan # Required: sl_file_extension The file extension to use for output files. # Not required for --shader_bundle mode. # Optional: iplr Causes --sl output to be in iplr/runtime diff --git a/lib/ui/fixtures/shaders/BUILD.gn b/lib/ui/fixtures/shaders/BUILD.gn index 1ea359df4cf64..ff3b6eb3cc896 100644 --- a/lib/ui/fixtures/shaders/BUILD.gn +++ b/lib/ui/fixtures/shaders/BUILD.gn @@ -41,6 +41,7 @@ if (enable_unittests) { shader_target_flags = [ "--runtime-stage-metal", "--runtime-stage-gles", + "--runtime-stage-vulkan", ] intermediates_subdir = "iplr-remap" sl_file_extension = "iplr" From 738056607380be93f6148ed419c0c9aba1913157 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Mon, 8 Jan 2024 14:13:47 -0800 Subject: [PATCH 02/13] ... --- impeller/compiler/impellerc_main.cc | 2 +- impeller/compiler/reflector.cc | 170 ++++++------ impeller/compiler/reflector.h | 102 ++++++- impeller/compiler/runtime_stage_data.cc | 53 ++-- impeller/compiler/runtime_stage_data.h | 4 + impeller/core/runtime_types.cc | 1 + impeller/core/runtime_types.h | 16 +- .../contents/runtime_effect_contents.cc | 251 ++++++++---------- impeller/runtime_stage/runtime_stage.cc | 25 +- .../runtime_stage/runtime_stage_types.fbs | 18 +- lib/ui/painting/fragment_program.cc | 4 + 11 files changed, 345 insertions(+), 301 deletions(-) diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index 7c0c88d2e341e..f9ecc73d6cdf5 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -65,7 +65,7 @@ static bool OutputIPLR( if (!sksl_shader) { return false; } - // stages.AddShader(sksl_shader); + stages.AddShader(sksl_shader); } for (const auto& platform : switches.PlatformsToCompile()) { diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 36549d6f11399..a65f87d89b9d6 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -12,9 +12,11 @@ #include #include "flutter/fml/logging.h" +#include "fml/backtrace.h" #include "impeller/base/strings.h" #include "impeller/base/validation.h" #include "impeller/compiler/code_gen_template.h" +#include "impeller/compiler/runtime_stage_data.h" #include "impeller/compiler/types.h" #include "impeller/compiler/uniform_sorter.h" #include "impeller/compiler/utilities.h" @@ -22,54 +24,11 @@ #include "impeller/geometry/half.h" #include "impeller/geometry/matrix.h" #include "impeller/geometry/scalar.h" +#include "spirv_common.hpp" namespace impeller { namespace compiler { -static std::string BaseTypeToString(spirv_cross::SPIRType::BaseType type) { - using Type = spirv_cross::SPIRType::BaseType; - switch (type) { - case Type::Void: - return "ShaderType::kVoid"; - case Type::Boolean: - return "ShaderType::kBoolean"; - case Type::SByte: - return "ShaderType::kSignedByte"; - case Type::UByte: - return "ShaderType::kUnsignedByte"; - case Type::Short: - return "ShaderType::kSignedShort"; - case Type::UShort: - return "ShaderType::kUnsignedShort"; - case Type::Int: - return "ShaderType::kSignedInt"; - case Type::UInt: - return "ShaderType::kUnsignedInt"; - case Type::Int64: - return "ShaderType::kSignedInt64"; - case Type::UInt64: - return "ShaderType::kUnsignedInt64"; - case Type::AtomicCounter: - return "ShaderType::kAtomicCounter"; - case Type::Half: - return "ShaderType::kHalfFloat"; - case Type::Float: - return "ShaderType::kFloat"; - case Type::Double: - return "ShaderType::kDouble"; - case Type::Struct: - return "ShaderType::kStruct"; - case Type::Image: - return "ShaderType::kImage"; - case Type::SampledImage: - return "ShaderType::kSampledImage"; - case Type::Sampler: - return "ShaderType::kSampler"; - default: - return "ShaderType::kUnknown"; - } -} - static std::string ExecutionModelToString(spv::ExecutionModel model) { switch (model) { case spv::ExecutionModel::ExecutionModelVertex: @@ -353,7 +312,6 @@ std::shared_ptr Reflector::GenerateRuntimeStageData() } auto data = std::make_unique(); data->entrypoint = options_.entry_point_name; - FML_LOG(ERROR) << "writing entrypoint as " << options_.entry_point_name; data->stage = entrypoints.front().execution_model; data->shader = shader_data_; data->backend = backend.value(); @@ -373,6 +331,11 @@ 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); + FML_CHECK(data->backend != RuntimeStageBackend::kVulkan || + spir_type.basetype == + spirv_cross::SPIRType::BaseType::SampledImage) + << "Vulkan runtime effect had unexpected uniforms outside of the " + "uniform buffer object."; data->uniforms.emplace_back(std::move(uniform_description)); } @@ -388,21 +351,41 @@ std::shared_ptr Reflector::GenerateRuntimeStageData() const auto& ubo = ubos[0]; - const auto& struct_type = compiler_->get_type(ubo.type_id); - FML_CHECK(struct_type.basetype == spirv_cross::SPIRType::BaseType::Struct); - for (size_t i = 0; i < struct_type.member_types.size(); i += 1) { - const auto spir_type = compiler_->get_type(struct_type.member_types[i]); - UniformDescription uniform_description; - uniform_description.name = - compiler_->get_member_name(ubo.base_type_id, i); - uniform_description.location = i; - uniform_description.type = spir_type.basetype; - uniform_description.rows = spir_type.vecsize; - uniform_description.columns = spir_type.columns; - uniform_description.bit_width = spir_type.width; - uniform_description.array_elements = GetArrayElements(spir_type); - data->uniforms.emplace_back(std::move(uniform_description)); + auto members = ReadStructMembers(ubo.type_id); + std::vector padding_locations; + size_t struct_byte_length = 0; + size_t float_count = 0; + for (size_t i = 0; i < members.size(); i += 1) { + const auto& member = members[i]; + struct_byte_length += member.byte_length; + std::vector bytes; + switch (member.underlying_type) { + case StructMember::UnderlyingType::kPadding: + size_t padding_count = + (member.size + sizeof(float) - 1) / sizeof(float); + // while (padding_count > 0) { + // padding_locations.push_back({i, member.size}); + // padding_count--; + // } + break; + case StructMember::UnderlyingType::kFloat: + float_count += member.byte_length / sizeof(float); + break; + case StructMember::UnderlyingType::kOther: + VALIDATION_LOG << "Non-floating-type struct member " << member.name + << " is not supported."; + return nullptr; + } } + data->uniforms.emplace_back(UniformDescription{ + .name = ubo.name, + .location = 64, // Magic constant that must match the descriptor set + // location for fragment programs. + .type = spirv_cross::SPIRType::Struct, + .padding_locations = std::move(padding_locations), + .struct_byte_length = struct_byte_length, + .struct_float_count = float_count, + }); } // We only need to worry about storing vertex attributes. @@ -551,7 +534,7 @@ std::optional Reflector::ReflectType( const auto type = compiler_->get_type(type_id); - result["type_name"] = BaseTypeToString(type.basetype); + result["type_name"] = StructMember::BaseTypeToString(type.basetype); result["bit_width"] = type.width; result["vec_size"] = type.vecsize; result["columns"] = type.columns; @@ -688,8 +671,8 @@ std::vector Reflector::ReadStructMembers( if (struct_member_offset > current_byte_offset) { const auto alignment_pad = struct_member_offset - current_byte_offset; result.emplace_back(StructMember{ - TypeNameWithPaddingOfSize(alignment_pad), // type - BaseTypeToString(spirv_cross::SPIRType::BaseType::Void), // basetype + TypeNameWithPaddingOfSize(alignment_pad), // type + spirv_cross::SPIRType::BaseType::Void, // basetype SPrintF("_PADDING_%s_", GetMemberNameAtIndex(struct_type, i).c_str()), // name current_byte_offset, // offset @@ -718,7 +701,7 @@ std::vector Reflector::ReadStructMembers( uint32_t element_padding = stride - size; result.emplace_back(StructMember{ compiler_->get_name(member.self), // type - BaseTypeToString(member.basetype), // basetype + member.basetype, // basetype GetMemberNameAtIndex(struct_type, i), // name struct_member_offset, // offset size, // size @@ -741,7 +724,7 @@ std::vector Reflector::ReadStructMembers( uint32_t element_padding = stride - sizeof(Matrix); result.emplace_back(StructMember{ "Matrix", // type - BaseTypeToString(member.basetype), // basetype + member.basetype, // basetype GetMemberNameAtIndex(struct_type, i), // name struct_member_offset, // offset sizeof(Matrix), // size @@ -764,7 +747,7 @@ std::vector Reflector::ReadStructMembers( uint32_t element_padding = stride - sizeof(UintPoint32); result.emplace_back(StructMember{ "UintPoint32", // type - BaseTypeToString(member.basetype), // basetype + member.basetype, // basetype GetMemberNameAtIndex(struct_type, i), // name struct_member_offset, // offset sizeof(UintPoint32), // size @@ -787,7 +770,7 @@ std::vector Reflector::ReadStructMembers( uint32_t element_padding = stride - sizeof(IPoint32); result.emplace_back(StructMember{ "IPoint32", // type - BaseTypeToString(member.basetype), // basetype + member.basetype, // basetype GetMemberNameAtIndex(struct_type, i), // name struct_member_offset, // offset sizeof(IPoint32), // size @@ -809,7 +792,7 @@ std::vector Reflector::ReadStructMembers( uint32_t element_padding = stride - sizeof(Point); result.emplace_back(StructMember{ "Point", // type - BaseTypeToString(member.basetype), // basetype + member.basetype, // basetype GetMemberNameAtIndex(struct_type, i), // name struct_member_offset, // offset sizeof(Point), // size @@ -831,7 +814,7 @@ std::vector Reflector::ReadStructMembers( uint32_t element_padding = stride - sizeof(Vector3); result.emplace_back(StructMember{ "Vector3", // type - BaseTypeToString(member.basetype), // basetype + member.basetype, // basetype GetMemberNameAtIndex(struct_type, i), // name struct_member_offset, // offset sizeof(Vector3), // size @@ -853,7 +836,7 @@ std::vector Reflector::ReadStructMembers( uint32_t element_padding = stride - sizeof(Vector4); result.emplace_back(StructMember{ "Vector4", // type - BaseTypeToString(member.basetype), // basetype + member.basetype, // basetype GetMemberNameAtIndex(struct_type, i), // name struct_member_offset, // offset sizeof(Vector4), // size @@ -876,7 +859,7 @@ std::vector Reflector::ReadStructMembers( uint32_t element_padding = stride - sizeof(HalfVector2); result.emplace_back(StructMember{ "HalfVector2", // type - BaseTypeToString(member.basetype), // basetype + member.basetype, // basetype GetMemberNameAtIndex(struct_type, i), // name struct_member_offset, // offset sizeof(HalfVector2), // size @@ -899,7 +882,7 @@ std::vector Reflector::ReadStructMembers( uint32_t element_padding = stride - sizeof(HalfVector3); result.emplace_back(StructMember{ "HalfVector3", // type - BaseTypeToString(member.basetype), // basetype + member.basetype, // basetype GetMemberNameAtIndex(struct_type, i), // name struct_member_offset, // offset sizeof(HalfVector3), // size @@ -922,7 +905,7 @@ std::vector Reflector::ReadStructMembers( uint32_t element_padding = stride - sizeof(HalfVector4); result.emplace_back(StructMember{ "HalfVector4", // type - BaseTypeToString(member.basetype), // basetype + member.basetype, // basetype GetMemberNameAtIndex(struct_type, i), // name struct_member_offset, // offset sizeof(HalfVector4), // size @@ -949,7 +932,7 @@ std::vector Reflector::ReadStructMembers( // Add the type directly. result.emplace_back(StructMember{ maybe_known_type.value().name, // type - BaseTypeToString(member.basetype), // basetype + member.basetype, // basetype GetMemberNameAtIndex(struct_type, i), // name struct_member_offset, // offset maybe_known_type.value().byte_size, // size @@ -973,7 +956,7 @@ std::vector Reflector::ReadStructMembers( auto element_padding = stride - size; result.emplace_back(StructMember{ TypeNameWithPaddingOfSize(size), // type - BaseTypeToString(member.basetype), // basetype + member.basetype, // basetype GetMemberNameAtIndex(struct_type, i), // name struct_member_offset, // offset size, // size @@ -993,15 +976,14 @@ std::vector Reflector::ReadStructMembers( if (excess != 0) { const auto padding = max_member_alignment - excess; result.emplace_back(StructMember{ - TypeNameWithPaddingOfSize(padding), // type - BaseTypeToString( - spirv_cross::SPIRType::BaseType::Void), // basetype - "_PADDING_", // name - current_byte_offset, // offset - padding, // size - padding, // byte_length - std::nullopt, // array_elements - 0, // element_padding + TypeNameWithPaddingOfSize(padding), // type + spirv_cross::SPIRType::BaseType::Void, // basetype + "_PADDING_", // name + current_byte_offset, // offset + padding, // size + padding, // byte_length + std::nullopt, // array_elements + 0, // element_padding }); } } @@ -1057,7 +1039,7 @@ nlohmann::json::object_t Reflector::EmitStructDefinition( struct VertexType { std::string type_name; - std::string base_type_name; + spirv_cross::SPIRType::BaseType base_type; std::string variable_name; size_t byte_length = 0u; }; @@ -1067,8 +1049,8 @@ static VertexType VertexTypeFromInputResource( const spirv_cross::Resource* resource) { VertexType result; result.variable_name = resource->name; - const auto type = compiler.get_type(resource->type_id); - result.base_type_name = BaseTypeToString(type.basetype); + const auto& type = compiler.get_type(resource->type_id); + result.base_type = type.basetype; const auto total_size = type.columns * type.vecsize * type.width / 8u; result.byte_length = total_size; @@ -1157,14 +1139,14 @@ Reflector::ReflectPerVertexStructDefinition( VertexTypeFromInputResource(*compiler_.GetCompiler(), resource); auto member = StructMember{ - vertex_type.type_name, // type - vertex_type.base_type_name, // base type - vertex_type.variable_name, // name - struc.byte_length, // offset - vertex_type.byte_length, // size - vertex_type.byte_length, // byte_length - std::nullopt, // array_elements - 0, // element_padding + vertex_type.type_name, // type + vertex_type.base_type, // base type + vertex_type.variable_name, // name + struc.byte_length, // offset + vertex_type.byte_length, // size + vertex_type.byte_length, // byte_length + std::nullopt, // array_elements + 0, // element_padding }; struc.byte_length += vertex_type.byte_length; struc.members.emplace_back(std::move(member)); diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index 37141e4c3809f..b4553e73019c7 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -11,9 +11,12 @@ #include "flutter/fml/macros.h" #include "flutter/fml/mapping.h" +#include "fml/logging.h" +#include "impeller/base/strings.h" #include "impeller/compiler/compiler_backend.h" #include "impeller/compiler/runtime_stage_data.h" #include "inja/inja.hpp" +#include "spirv_common.hpp" #include "spirv_msl.hpp" #include "spirv_parser.hpp" @@ -21,6 +24,14 @@ namespace impeller { namespace compiler { struct StructMember { + // Runtime stages on Vulkan use this information to validate that a struct + // only contains floats and encode where padding gets inserted. + enum class UnderlyingType { + kPadding, + kFloat, + kOther, + }; + std::string type; std::string base_type; std::string name; @@ -29,23 +40,106 @@ struct StructMember { size_t byte_length = 0u; std::optional array_elements = std::nullopt; size_t element_padding = 0u; + UnderlyingType underlying_type = UnderlyingType::kOther; + + static std::string BaseTypeToString(spirv_cross::SPIRType::BaseType type) { + using Type = spirv_cross::SPIRType::BaseType; + switch (type) { + case Type::Void: + return "ShaderType::kVoid"; + case Type::Boolean: + return "ShaderType::kBoolean"; + case Type::SByte: + return "ShaderType::kSignedByte"; + case Type::UByte: + return "ShaderType::kUnsignedByte"; + case Type::Short: + return "ShaderType::kSignedShort"; + case Type::UShort: + return "ShaderType::kUnsignedShort"; + case Type::Int: + return "ShaderType::kSignedInt"; + case Type::UInt: + return "ShaderType::kUnsignedInt"; + case Type::Int64: + return "ShaderType::kSignedInt64"; + case Type::UInt64: + return "ShaderType::kUnsignedInt64"; + case Type::AtomicCounter: + return "ShaderType::kAtomicCounter"; + case Type::Half: + return "ShaderType::kHalfFloat"; + case Type::Float: + return "ShaderType::kFloat"; + case Type::Double: + return "ShaderType::kDouble"; + case Type::Struct: + return "ShaderType::kStruct"; + case Type::Image: + return "ShaderType::kImage"; + case Type::SampledImage: + return "ShaderType::kSampledImage"; + case Type::Sampler: + return "ShaderType::kSampler"; + default: + return "ShaderType::kUnknown"; + } + FML_UNREACHABLE(); + } + + static UnderlyingType DetermineUnderlyingType( + spirv_cross::SPIRType::BaseType type) { + switch (type) { + case spirv_cross::SPIRType::Void: + return UnderlyingType::kPadding; + case spirv_cross::SPIRType::Float: + return UnderlyingType::kFloat; + case spirv_cross::SPIRType::Unknown: + case spirv_cross::SPIRType::Boolean: + case spirv_cross::SPIRType::SByte: + case spirv_cross::SPIRType::UByte: + case spirv_cross::SPIRType::Short: + case spirv_cross::SPIRType::UShort: + case spirv_cross::SPIRType::Int: + case spirv_cross::SPIRType::UInt: + case spirv_cross::SPIRType::Int64: + case spirv_cross::SPIRType::UInt64: + case spirv_cross::SPIRType::AtomicCounter: + case spirv_cross::SPIRType::Half: + case spirv_cross::SPIRType::Double: + case spirv_cross::SPIRType::Struct: + case spirv_cross::SPIRType::Image: + case spirv_cross::SPIRType::SampledImage: + case spirv_cross::SPIRType::Sampler: + case spirv_cross::SPIRType::AccelerationStructure: + case spirv_cross::SPIRType::RayQuery: + case spirv_cross::SPIRType::ControlPointArray: + case spirv_cross::SPIRType::Interpolant: + case spirv_cross::SPIRType::Char: + default: + return UnderlyingType::kOther; + } + FML_UNREACHABLE(); + } StructMember(std::string p_type, - std::string p_base_type, + spirv_cross::SPIRType::BaseType p_base_type, std::string p_name, size_t p_offset, size_t p_size, size_t p_byte_length, std::optional p_array_elements, - size_t p_element_padding) + size_t p_element_padding, + UnderlyingType p_underlying_type = UnderlyingType::kOther) : type(std::move(p_type)), - base_type(std::move(p_base_type)), + base_type(BaseTypeToString(p_base_type)), name(std::move(p_name)), offset(p_offset), size(p_size), byte_length(p_byte_length), array_elements(p_array_elements), - element_padding(p_element_padding) {} + element_padding(p_element_padding), + underlying_type(DetermineUnderlyingType(p_base_type)) {} }; class Reflector { diff --git a/impeller/compiler/runtime_stage_data.cc b/impeller/compiler/runtime_stage_data.cc index e6d8cdeead0f8..cb227d17dc979 100644 --- a/impeller/compiler/runtime_stage_data.cc +++ b/impeller/compiler/runtime_stage_data.cc @@ -61,32 +61,23 @@ static std::optional ToJsonStage(spv::ExecutionModel stage) { static std::optional ToUniformType( spirv_cross::SPIRType::BaseType type) { switch (type) { + case spirv_cross::SPIRType::Float: + return fb::UniformDataType::kFloat; + case spirv_cross::SPIRType::SampledImage: + return fb::UniformDataType::kSampledImage; + case spirv_cross::SPIRType::Struct: + return fb::UniformDataType::kStruct; case spirv_cross::SPIRType::Boolean: - return fb::UniformDataType::kBoolean; case spirv_cross::SPIRType::SByte: - return fb::UniformDataType::kSignedByte; case spirv_cross::SPIRType::UByte: - return fb::UniformDataType::kUnsignedByte; case spirv_cross::SPIRType::Short: - return fb::UniformDataType::kSignedShort; case spirv_cross::SPIRType::UShort: - return fb::UniformDataType::kUnsignedShort; case spirv_cross::SPIRType::Int: - return fb::UniformDataType::kSignedInt; case spirv_cross::SPIRType::UInt: - return fb::UniformDataType::kUnsignedInt; case spirv_cross::SPIRType::Int64: - return fb::UniformDataType::kSignedInt64; case spirv_cross::SPIRType::UInt64: - return fb::UniformDataType::kUnsignedInt64; case spirv_cross::SPIRType::Half: - return fb::UniformDataType::kHalfFloat; - case spirv_cross::SPIRType::Float: - return fb::UniformDataType::kFloat; case spirv_cross::SPIRType::Double: - return fb::UniformDataType::kDouble; - case spirv_cross::SPIRType::SampledImage: - return fb::UniformDataType::kSampledImage; case spirv_cross::SPIRType::AccelerationStructure: case spirv_cross::SPIRType::AtomicCounter: case spirv_cross::SPIRType::Char: @@ -95,7 +86,6 @@ static std::optional ToUniformType( case spirv_cross::SPIRType::Interpolant: case spirv_cross::SPIRType::RayQuery: case spirv_cross::SPIRType::Sampler: - case spirv_cross::SPIRType::Struct: case spirv_cross::SPIRType::Unknown: case spirv_cross::SPIRType::Void: return std::nullopt; @@ -174,6 +164,8 @@ static std::optional ToJsonType( return 11; // fb::UniformDataType::kDouble; case spirv_cross::SPIRType::SampledImage: return 12; // fb::UniformDataType::kSampledImage; + case spirv_cross::SPIRType::Struct: + return 13; case spirv_cross::SPIRType::AccelerationStructure: case spirv_cross::SPIRType::AtomicCounter: case spirv_cross::SPIRType::Char: @@ -182,7 +174,6 @@ static std::optional ToJsonType( case spirv_cross::SPIRType::Interpolant: case spirv_cross::SPIRType::RayQuery: case spirv_cross::SPIRType::Sampler: - case spirv_cross::SPIRType::Struct: case spirv_cross::SPIRType::Unknown: case spirv_cross::SPIRType::Void: return std::nullopt; @@ -202,6 +193,7 @@ static const char* kUniformRowsKey = "rows"; static const char* kUniformColumnsKey = "columns"; static const char* kUniformBitWidthKey = "bit_width"; static const char* kUniformArrayElementsKey = "array_elements"; +static const char* kUniformPaddingLocationsKey = "padding_locations"; static std::string RuntimeStageBackendToString(RuntimeStageBackend backend) { switch (backend) { @@ -273,13 +265,10 @@ std::shared_ptr RuntimeStageData::CreateJsonMapping() const { uniform_object[kUniformTypeKey] = uniform_type.value(); uniform_object[kUniformBitWidthKey] = uniform.bit_width; + uniform_object[kUniformArrayElementsKey] = + uniform.array_elements.value_or(0); + uniform_object[kUniformPaddingLocationsKey] = uniform.padding_locations; - if (uniform.array_elements.has_value()) { - uniform_object[kUniformArrayElementsKey] = - uniform.array_elements.value(); - } else { - uniform_object[kUniformArrayElementsKey] = 0; - } uniforms.push_back(uniform_object); } @@ -293,12 +282,25 @@ std::shared_ptr RuntimeStageData::CreateJsonMapping() const { json_string->size(), [json_string](auto, auto) {}); } +static std::vector> PaddingLocationsToLocations( + const UniformDescription::PaddingLocations& locations) { + std::vector> result; + for (const auto& location : locations) { + result.push_back(std::make_unique(fb::LocationT{ + .offset = location.first, + .size = location.second, + })); + } + return result; +} + std::unique_ptr RuntimeStageData::CreateFlatbuffer() const { // The high level object API is used here for writing to the buffer. This is // just a convenience. auto runtime_stages = std::make_unique(); for (const auto& kvp : data_) { + FML_DCHECK(kvp.first == kvp.second->backend); auto runtime_stage = std::make_unique(); runtime_stage->entrypoint = kvp.second->entrypoint; const auto stage = ToStage(kvp.second->stage); @@ -333,11 +335,16 @@ std::unique_ptr RuntimeStageData::CreateFlatbuffer() const { return nullptr; } desc->type = uniform_type.value(); + runtime_stage->struct_byte_length += uniform.struct_byte_length; + runtime_stage->float_count += uniform.struct_float_count; desc->bit_width = uniform.bit_width; if (uniform.array_elements.has_value()) { desc->array_elements = uniform.array_elements.value(); } + desc->padding_locations = + PaddingLocationsToLocations(uniform.padding_locations); + runtime_stage->uniforms.emplace_back(std::move(desc)); } diff --git a/impeller/compiler/runtime_stage_data.h b/impeller/compiler/runtime_stage_data.h index f09e3eeca3e38..5177e7a5a42e6 100644 --- a/impeller/compiler/runtime_stage_data.h +++ b/impeller/compiler/runtime_stage_data.h @@ -19,6 +19,7 @@ namespace impeller { namespace compiler { struct UniformDescription { + using PaddingLocations = std::vector>; std::string name; size_t location = 0u; spirv_cross::SPIRType::BaseType type = spirv_cross::SPIRType::BaseType::Float; @@ -26,6 +27,9 @@ struct UniformDescription { size_t columns = 0u; size_t bit_width = 0u; std::optional array_elements = std::nullopt; + PaddingLocations padding_locations = {}; + size_t struct_byte_length = 0u; + size_t struct_float_count = 0u; }; struct InputDescription { diff --git a/impeller/core/runtime_types.cc b/impeller/core/runtime_types.cc index 481ee9253f6ea..ad2b2ce229664 100644 --- a/impeller/core/runtime_types.cc +++ b/impeller/core/runtime_types.cc @@ -13,6 +13,7 @@ size_t RuntimeUniformDescription::GetSize() const { // NOLINTNEXTLINE(bugprone-unchecked-optional-access) size *= array_elements.value(); } + size += struct_byte_length; return size; } diff --git a/impeller/core/runtime_types.h b/impeller/core/runtime_types.h index c4f4b38738867..1d237d4fb9eec 100644 --- a/impeller/core/runtime_types.h +++ b/impeller/core/runtime_types.h @@ -8,6 +8,7 @@ #include #include #include +#include namespace impeller { @@ -19,19 +20,9 @@ enum class RuntimeStageBackend { }; enum RuntimeUniformType { - kBoolean, - kSignedByte, - kUnsignedByte, - kSignedShort, - kUnsignedShort, - kSignedInt, - kUnsignedInt, - kSignedInt64, - kUnsignedInt64, - kHalfFloat, kFloat, - kDouble, kSampledImage, + kStruct, }; enum class RuntimeShaderStage { @@ -52,6 +43,9 @@ struct RuntimeUniformDescription { RuntimeUniformDimensions dimensions; size_t bit_width; std::optional array_elements; + std::vector> struct_padding_locations = {}; + size_t struct_byte_length = 0u; + size_t struct_float_count = 0u; /// @brief Computes the total number of bytes that this uniform requires. size_t GetSize() const; diff --git a/impeller/entity/contents/runtime_effect_contents.cc b/impeller/entity/contents/runtime_effect_contents.cc index 3890f83092574..05921b5b1dfcd 100644 --- a/impeller/entity/contents/runtime_effect_contents.cc +++ b/impeller/entity/contents/runtime_effect_contents.cc @@ -42,40 +42,29 @@ 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 MakeShaderMetadata( -// const RuntimeUniformDescription& uniform) { -// auto metadata = std::make_shared(); -// metadata->name = uniform.name; -// metadata->members.emplace_back(ShaderStructMemberMetadata{ -// .type = GetShaderType(uniform.type), -// .size = uniform.GetSize(), -// .byte_length = uniform.bit_width / 8, -// }); - -// return metadata; -// } +static ShaderType GetShaderType(RuntimeUniformType type) { + switch (type) { + case kSampledImage: + return ShaderType::kSampledImage; + case kFloat: + return ShaderType::kFloat; + case kStruct: + return ShaderType::kStruct; + } +} + +static std::shared_ptr MakeShaderMetadata( + const RuntimeUniformDescription& uniform) { + auto metadata = std::make_shared(); + 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, @@ -204,111 +193,103 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, /// Fragment stage uniforms. /// - // size_t minimum_sampler_index = 100000000; - // size_t buffer_index = 0; - // size_t buffer_offset = 0; - - ShaderUniformSlot uniform_slot; - uniform_slot.name = "_RESERVED_IDENTIFIER_FIXUP_gl_DefaultUniformBlock"; - uniform_slot.ext_res_0 = 0; - uniform_slot.binding = 64; - ShaderMetadata metadata; - metadata.name = uniform_slot.name; - auto uniform_count = runtime_stage_->GetUniforms().size(); - metadata.members.emplace_back(ShaderStructMemberMetadata{ - .type = ShaderType::kStruct, - .size = uniform_count * 4, - .byte_length = uniform_count * 4 / 8, - }); + size_t minimum_sampler_index = 100000000; + size_t buffer_index = 0; + size_t buffer_offset = 0; - size_t alignment = std::max(4ul * 3ul, DefaultUniformAlignment()); + for (const auto& uniform : runtime_stage_->GetUniforms()) { + FML_LOG(ERROR) << "Uniform is " << uniform.name; + std::shared_ptr metadata = MakeShaderMetadata(uniform); + + switch (uniform.type) { + case kSampledImage: { + // Sampler uniforms are ordered in the IPLR according to their + // declaration and the uniform location reflects the correct offset to + // be mapped to - except that it may include all proceeding float + // uniforms. For example, a float sampler that comes after 4 float + // uniforms may have a location of 4. To convert to the actual offset we + // need to find the largest location assigned to a float uniform and + // then subtract this from all uniform locations. This is more or less + // the same operation we previously performed in the shader compiler. + minimum_sampler_index = + std::min(minimum_sampler_index, uniform.location); + break; + } + case kFloat: { + // FML_CHECK(renderer.GetContext()->GetBackendType() != + // Context::BackendType::kVulkan) + // << "Uniform " << uniform.name + // << " had unexpected type kFloat for Vulkan backend."; + size_t alignment = + std::max(uniform.bit_width / 8, DefaultUniformAlignment()); + auto buffer_view = pass.GetTransientsBuffer().Emplace( + uniform_data_->data() + buffer_offset, uniform.GetSize(), + alignment); + + ShaderUniformSlot uniform_slot; + uniform_slot.name = uniform.name.c_str(); + uniform_slot.ext_res_0 = uniform.location; + cmd.BindResource(ShaderStage::kFragment, uniform_slot, metadata, + buffer_view); + buffer_index++; + buffer_offset += uniform.GetSize(); + break; + } + case kStruct: { + FML_DCHECK(renderer.GetContext()->GetBackendType() == + Context::BackendType::kVulkan); + + ShaderUniformSlot uniform_slot; + uniform_slot.name = uniform.name.c_str(); + uniform_slot.binding = uniform.location; + + std::vector uniform_buffer; + FML_LOG(ERROR) << "u: " << uniform.name << " .. " << uniform.GetSize(); + // for (size_t i = 0; i < uniform.) + + // size_t alignment = std::max(4ul * 4ul, DefaultUniformAlignment()); + + // std::array uniform_buffer{ + // reinterpret_cast(uniform_data_->data())[0], + // 0.f, + // reinterpret_cast(uniform_data_->data())[1], + // reinterpret_cast(uniform_data_->data())[2], + // }; + // auto buffer_view = pass.GetTransientsBuffer().Emplace( + // reinterpret_cast(uniform_buffer.data()), + // 4 * sizeof(float), alignment); + // cmd.BindResource(ShaderStage::kFragment, uniform_slot, + // ShaderMetadata{}, + // buffer_view); + } + } + } - size_t buffer_offset = 0u; + size_t sampler_index = 0; for (const auto& uniform : runtime_stage_->GetUniforms()) { - FML_LOG(ERROR) << "Uniform " << uniform.name << " has value " - << reinterpret_cast(uniform_data_->data() + - buffer_offset)[0] - << " And size " << uniform.GetSize(); - buffer_offset += uniform.GetSize(); + std::shared_ptr metadata = MakeShaderMetadata(uniform); + + switch (uniform.type) { + case kSampledImage: { + FML_DCHECK(sampler_index < texture_inputs_.size()); + auto& input = texture_inputs_[sampler_index]; + + auto sampler = + context->GetSamplerLibrary()->GetSampler(input.sampler_descriptor); + + 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, + input.texture, sampler); + + sampler_index++; + break; + } + default: + continue; + } } - auto buffer_view = pass.GetTransientsBuffer().Emplace( - reinterpret_cast(uniform_data_->data()), 3 * sizeof(float), - alignment); - cmd.BindResource(ShaderStage::kFragment, uniform_slot, metadata, buffer_view); - - // for (const auto& uniform : runtime_stage_->GetUniforms()) { - // std::shared_ptr metadata = MakeShaderMetadata(uniform); - - // switch (uniform.type) { - // case kSampledImage: { - // // Sampler uniforms are ordered in the IPLR according to their - // // declaration and the uniform location reflects the correct offset - // to - // // be mapped to - except that it may include all proceeding float - // // uniforms. For example, a float sampler that comes after 4 float - // // uniforms may have a location of 4. To convert to the actual offset - // we - // // need to find the largest location assigned to a float uniform and - // // then subtract this from all uniform locations. This is more or - // less - // // the same operation we previously performed in the shader compiler. - // minimum_sampler_index = - // std::min(minimum_sampler_index, uniform.location); - // break; - // } - // case kFloat: { - // size_t alignment = - // std::max(uniform.bit_width / 8, DefaultUniformAlignment()); - // auto buffer_view = pass.GetTransientsBuffer().Emplace( - // uniform_data_->data() + buffer_offset, uniform.GetSize(), - // alignment); - - // buffer_index++; - // buffer_offset += uniform.GetSize(); - // break; - // } - // 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 for " << uniform.name - // << "."; - // return true; - // } - // } - - // size_t sampler_index = 0; - // for (const auto& uniform : runtime_stage_->GetUniforms()) { - // std::shared_ptr metadata = MakeShaderMetadata(uniform); - - // switch (uniform.type) { - // case kSampledImage: { - // FML_DCHECK(sampler_index < texture_inputs_.size()); - // auto& input = texture_inputs_[sampler_index]; - - // auto sampler = - // context->GetSamplerLibrary()->GetSampler(input.sampler_descriptor); - - // 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, - // input.texture, sampler); - - // sampler_index++; - // break; - // } - // default: - // continue; - // } - // } pass.AddCommand(std::move(cmd)); diff --git a/impeller/runtime_stage/runtime_stage.cc b/impeller/runtime_stage/runtime_stage.cc index 357f54313441c..1265312b4ce70 100644 --- a/impeller/runtime_stage/runtime_stage.cc +++ b/impeller/runtime_stage/runtime_stage.cc @@ -9,6 +9,7 @@ #include "fml/mapping.h" #include "impeller/base/validation.h" +#include "impeller/core/runtime_types.h" #include "impeller/runtime_stage/runtime_stage_flatbuffers.h" #include "runtime_stage_types_flatbuffers.h" @@ -16,32 +17,12 @@ namespace impeller { static RuntimeUniformType ToType(fb::UniformDataType type) { switch (type) { - case fb::UniformDataType::kBoolean: - return RuntimeUniformType::kBoolean; - case fb::UniformDataType::kSignedByte: - return RuntimeUniformType::kSignedByte; - case fb::UniformDataType::kUnsignedByte: - return RuntimeUniformType::kUnsignedByte; - case fb::UniformDataType::kSignedShort: - return RuntimeUniformType::kSignedShort; - case fb::UniformDataType::kUnsignedShort: - return RuntimeUniformType::kUnsignedShort; - case fb::UniformDataType::kSignedInt: - return RuntimeUniformType::kSignedInt; - case fb::UniformDataType::kUnsignedInt: - return RuntimeUniformType::kUnsignedInt; - case fb::UniformDataType::kSignedInt64: - return RuntimeUniformType::kSignedInt64; - case fb::UniformDataType::kUnsignedInt64: - return RuntimeUniformType::kUnsignedInt64; - case fb::UniformDataType::kHalfFloat: - return RuntimeUniformType::kHalfFloat; case fb::UniformDataType::kFloat: return RuntimeUniformType::kFloat; - case fb::UniformDataType::kDouble: - return RuntimeUniformType::kDouble; case fb::UniformDataType::kSampledImage: return RuntimeUniformType::kSampledImage; + case fb::UniformDataType::kStruct: + return RuntimeUniformType::kStruct; } FML_UNREACHABLE(); } diff --git a/impeller/runtime_stage/runtime_stage_types.fbs b/impeller/runtime_stage/runtime_stage_types.fbs index 21e6f802717a6..d830749856a43 100644 --- a/impeller/runtime_stage/runtime_stage_types.fbs +++ b/impeller/runtime_stage/runtime_stage_types.fbs @@ -11,20 +11,12 @@ enum Stage:byte { } // The subset of impeller::ShaderType that may be used for uniform bindings. +// kStruct is only supported for Impeller Vulkan. +// kFloat encompases multiple float-based types e.g. vec2. enum UniformDataType:uint32 { - kBoolean, - kSignedByte, - kUnsignedByte, - kSignedShort, - kUnsignedShort, - kSignedInt, - kUnsignedInt, - kSignedInt64, - kUnsignedInt64, - kHalfFloat, kFloat, - kDouble, kSampledImage, + kStruct, } table UniformDescription { @@ -35,6 +27,8 @@ table UniformDescription { rows: uint64; columns: uint64; array_elements: uint64; + padding_locations: [uint64]; + float_count: uint64; } // The subset of impeller::ShaderType that may be used for vertex attributes. @@ -72,6 +66,8 @@ table RuntimeStage { inputs: [StageInput]; uniforms: [UniformDescription]; shader: [ubyte]; + struct_byte_length: uint64; + float_count: uint64; } table RuntimeStages { diff --git a/lib/ui/painting/fragment_program.cc b/lib/ui/painting/fragment_program.cc index b74f33ab2e7ab..428ed92a6f362 100644 --- a/lib/ui/painting/fragment_program.cc +++ b/lib/ui/painting/fragment_program.cc @@ -63,6 +63,10 @@ std::string FragmentProgram::initFromAsset(const std::string& asset_name) { auto backend = UIDartState::Current()->GetRuntimeStageBackend(); auto runtime_stage = runtime_stages[backend]; + FML_LOG(ERROR) << "Runtime stage backend is " << static_cast(backend) + << " and got " << runtime_stage->GetUniforms().size() + << " uniforms" + << runtime_stage->GetCodeMapping()->GetMapping(); if (!runtime_stage) { std::ostringstream stream; stream << "Asset '" << asset_name From eead81f0583f204912d8e451253347f93e3dc92f Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 9 Jan 2024 12:16:27 -0800 Subject: [PATCH 03/13] It sort of works --- impeller/compiler/reflector.cc | 43 ++++++++++------- impeller/compiler/runtime_stage_data.cc | 7 ++- impeller/compiler/types.h | 2 + impeller/core/runtime_types.cc | 2 +- impeller/core/runtime_types.h | 7 ++- .../contents/runtime_effect_contents.cc | 47 ++++++++++--------- impeller/runtime_stage/runtime_stage.cc | 9 ++++ impeller/runtime_stage/runtime_stage.h | 2 + .../runtime_stage/runtime_stage_types.fbs | 12 ++++- lib/ui/painting/fragment_program.cc | 9 ++-- 10 files changed, 86 insertions(+), 54 deletions(-) diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 292d46a7ba682..e01755bccc38f 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -24,6 +24,7 @@ #include "impeller/geometry/half.h" #include "impeller/geometry/matrix.h" #include "impeller/geometry/scalar.h" +#include "impeller/runtime_stage/runtime_stage.h" #include "spirv_common.hpp" namespace impeller { @@ -350,10 +351,11 @@ std::shared_ptr Reflector::GenerateRuntimeStageData() const auto ubos = compiler_->get_shader_resources().uniform_buffers; if (data->backend == RuntimeStageBackend::kVulkan && !ubos.empty()) { - if (ubos.size() != 1 && - ubos[0].name != "_RESERVED_IDENTIFIER_FIXUP_gl_DefaultUniformBlock") { + if (ubos.size() != 1 && ubos[0].name != RuntimeStage::kVulkanUBOName) { VALIDATION_LOG << "Expected a single UBO resource named " - "'_RESERVED_IDENTIFIER_FIXUP_gl_DefaultUniformBlock' " + "'" + << RuntimeStage::kVulkanUBOName + << "' " "for Vulkan runtime stage backend."; return nullptr; } @@ -361,25 +363,31 @@ std::shared_ptr Reflector::GenerateRuntimeStageData() const auto& ubo = ubos[0]; auto members = ReadStructMembers(ubo.type_id); - std::vector padding_locations; - size_t struct_byte_length = 0; + std::vector struct_layout; size_t float_count = 0; + for (size_t i = 0; i < members.size(); i += 1) { const auto& member = members[i]; - struct_byte_length += member.byte_length; std::vector bytes; switch (member.underlying_type) { - case StructMember::UnderlyingType::kPadding: + case StructMember::UnderlyingType::kPadding: { size_t padding_count = (member.size + sizeof(float) - 1) / sizeof(float); - // while (padding_count > 0) { - // padding_locations.push_back({i, member.size}); - // padding_count--; - // } + while (padding_count > 0) { + struct_layout.push_back(0); + padding_count--; + } break; - case StructMember::UnderlyingType::kFloat: - float_count += member.byte_length / sizeof(float); + } + case StructMember::UnderlyingType::kFloat: { + 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); + member_float_count--; + } break; + } case StructMember::UnderlyingType::kOther: VALIDATION_LOG << "Non-floating-type struct member " << member.name << " is not supported."; @@ -391,8 +399,7 @@ std::shared_ptr Reflector::GenerateRuntimeStageData() .location = 64, // Magic constant that must match the descriptor set // location for fragment programs. .type = spirv_cross::SPIRType::Struct, - .padding_locations = std::move(padding_locations), - .struct_byte_length = struct_byte_length, + .struct_layout = std::move(struct_layout), .struct_float_count = float_count, }); } @@ -650,7 +657,8 @@ std::optional Reflector::ReflectType( auto member = nlohmann::json::object_t{}; member["name"] = struct_member.name; member["type"] = struct_member.type; - member["base_type"] = BaseTypeToString(struct_member.base_type); + member["base_type"] = + StructMember::BaseTypeToString(struct_member.base_type); member["offset"] = struct_member.offset; member["size"] = struct_member.size; member["byte_length"] = struct_member.byte_length; @@ -1130,7 +1138,8 @@ nlohmann::json::object_t Reflector::EmitStructDefinition( auto& member = members.emplace_back(nlohmann::json::object_t{}); member["name"] = struct_member.name; member["type"] = struct_member.type; - member["base_type"] = BaseTypeToString(struct_member.base_type); + member["base_type"] = + StructMember::BaseTypeToString(struct_member.base_type); member["offset"] = struct_member.offset; member["byte_length"] = struct_member.byte_length; if (struct_member.array_elements.has_value()) { diff --git a/impeller/compiler/runtime_stage_data.cc b/impeller/compiler/runtime_stage_data.cc index d33e36d5004d5..b45926305f55b 100644 --- a/impeller/compiler/runtime_stage_data.cc +++ b/impeller/compiler/runtime_stage_data.cc @@ -193,7 +193,6 @@ static const char* kUniformRowsKey = "rows"; static const char* kUniformColumnsKey = "columns"; static const char* kUniformBitWidthKey = "bit_width"; static const char* kUniformArrayElementsKey = "array_elements"; -static const char* kUniformPaddingLocationsKey = "padding_locations"; static std::string RuntimeStageBackendToString(RuntimeStageBackend backend) { switch (backend) { @@ -267,7 +266,6 @@ std::shared_ptr RuntimeStageData::CreateJsonMapping() const { uniform_object[kUniformBitWidthKey] = uniform.bit_width; uniform_object[kUniformArrayElementsKey] = uniform.array_elements.value_or(0); - uniform_object[kUniformPaddingLocationsKey] = uniform.padding_locations; uniforms.push_back(uniform_object); } @@ -328,6 +326,11 @@ 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)); + } + desc->struct_float_count = uniform.struct_float_count; + runtime_stage->uniforms.emplace_back(std::move(desc)); } diff --git a/impeller/compiler/types.h b/impeller/compiler/types.h index db108c9824477..c41423014071f 100644 --- a/impeller/compiler/types.h +++ b/impeller/compiler/types.h @@ -53,6 +53,8 @@ struct UniformDescription { size_t columns = 0u; size_t bit_width = 0u; std::optional array_elements = std::nullopt; + std::vector struct_layout = {}; + size_t struct_float_count = 0u; }; struct InputDescription { diff --git a/impeller/core/runtime_types.cc b/impeller/core/runtime_types.cc index ad2b2ce229664..08811275967ba 100644 --- a/impeller/core/runtime_types.cc +++ b/impeller/core/runtime_types.cc @@ -13,7 +13,7 @@ size_t RuntimeUniformDescription::GetSize() const { // NOLINTNEXTLINE(bugprone-unchecked-optional-access) size *= array_elements.value(); } - size += struct_byte_length; + size += sizeof(float) * struct_layout.size(); return size; } diff --git a/impeller/core/runtime_types.h b/impeller/core/runtime_types.h index 1d237d4fb9eec..48a8744e0e654 100644 --- a/impeller/core/runtime_types.h +++ b/impeller/core/runtime_types.h @@ -40,11 +40,10 @@ struct RuntimeUniformDescription { std::string name; size_t location = 0u; RuntimeUniformType type = RuntimeUniformType::kFloat; - RuntimeUniformDimensions dimensions; - size_t bit_width; + RuntimeUniformDimensions dimensions = {}; + size_t bit_width = 0u; std::optional array_elements; - std::vector> struct_padding_locations = {}; - size_t struct_byte_length = 0u; + std::vector struct_layout = {}; size_t struct_float_count = 0u; /// @brief Computes the total number of bytes that this uniform requires. diff --git a/impeller/entity/contents/runtime_effect_contents.cc b/impeller/entity/contents/runtime_effect_contents.cc index 0fa9257145f9c..5a49849c761b4 100644 --- a/impeller/entity/contents/runtime_effect_contents.cc +++ b/impeller/entity/contents/runtime_effect_contents.cc @@ -197,7 +197,6 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, size_t buffer_offset = 0; for (const auto& uniform : runtime_stage_->GetUniforms()) { - FML_LOG(ERROR) << "Uniform is " << uniform.name; std::shared_ptr metadata = MakeShaderMetadata(uniform); switch (uniform.type) { @@ -215,10 +214,10 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, break; } case kFloat: { - // FML_CHECK(renderer.GetContext()->GetBackendType() != - // Context::BackendType::kVulkan) - // << "Uniform " << uniform.name - // << " had unexpected type kFloat for Vulkan backend."; + FML_DCHECK(renderer.GetContext()->GetBackendType() != + Context::BackendType::kVulkan) + << "Uniform " << uniform.name + << " had unexpected type kFloat for Vulkan backend."; size_t alignment = std::max(uniform.bit_width / 8, DefaultUniformAlignment()); auto buffer_view = pass.GetTransientsBuffer().Emplace( @@ -243,23 +242,27 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, uniform_slot.binding = uniform.location; std::vector uniform_buffer; - FML_LOG(ERROR) << "u: " << uniform.name << " .. " << uniform.GetSize(); - // for (size_t i = 0; i < uniform.) - - // size_t alignment = std::max(4ul * 4ul, DefaultUniformAlignment()); - - // std::array uniform_buffer{ - // reinterpret_cast(uniform_data_->data())[0], - // 0.f, - // reinterpret_cast(uniform_data_->data())[1], - // reinterpret_cast(uniform_data_->data())[2], - // }; - // auto buffer_view = pass.GetTransientsBuffer().Emplace( - // reinterpret_cast(uniform_buffer.data()), - // 4 * sizeof(float), alignment); - // cmd.BindResource(ShaderStage::kFragment, uniform_slot, - // ShaderMetadata{}, - // buffer_view); + uniform_buffer.reserve(uniform.struct_layout.size()); + size_t uniform_byte_index = 0u; + for (const auto& byte_type : uniform.struct_layout) { + if (byte_type == 0) { + uniform_buffer.push_back(0.f); + } else if (byte_type == 1) { + uniform_buffer.push_back(reinterpret_cast( + uniform_data_->data())[uniform_byte_index++]); + } else { + FML_UNREACHABLE(); + } + } + + size_t alignment = std::max(sizeof(float) * uniform_buffer.size(), + DefaultUniformAlignment()); + + auto buffer_view = pass.GetTransientsBuffer().Emplace( + reinterpret_cast(uniform_buffer.data()), alignment, + alignment); + cmd.BindResource(ShaderStage::kFragment, uniform_slot, ShaderMetadata{}, + buffer_view); } } } diff --git a/impeller/runtime_stage/runtime_stage.cc b/impeller/runtime_stage/runtime_stage.cc index 1265312b4ce70..a8f81db79d91c 100644 --- a/impeller/runtime_stage/runtime_stage.cc +++ b/impeller/runtime_stage/runtime_stage.cc @@ -39,6 +39,9 @@ static RuntimeShaderStage ToShaderStage(fb::Stage stage) { FML_UNREACHABLE(); } +const char* RuntimeStage::kVulkanUBOName = + "_RESERVED_IDENTIFIER_FIXUP_gl_DefaultUniformBlock"; + std::unique_ptr RuntimeStage::RuntimeStageIfPresent( const fb::RuntimeStage* runtime_stage, const std::shared_ptr& payload) { @@ -91,6 +94,12 @@ RuntimeStage::RuntimeStage(const fb::RuntimeStage* runtime_stage, 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)); + } + } + desc.struct_float_count = i->struct_float_count(); uniforms_.emplace_back(std::move(desc)); } } diff --git a/impeller/runtime_stage/runtime_stage.h b/impeller/runtime_stage/runtime_stage.h index 1c433906004d6..a86344a89d4c5 100644 --- a/impeller/runtime_stage/runtime_stage.h +++ b/impeller/runtime_stage/runtime_stage.h @@ -18,6 +18,8 @@ namespace impeller { class RuntimeStage { public: + static const char* kVulkanUBOName; + using Map = std::map>; static Map DecodeRuntimeStages(const std::shared_ptr& payload); diff --git a/impeller/runtime_stage/runtime_stage_types.fbs b/impeller/runtime_stage/runtime_stage_types.fbs index d830749856a43..b6f88f1fa73b7 100644 --- a/impeller/runtime_stage/runtime_stage_types.fbs +++ b/impeller/runtime_stage/runtime_stage_types.fbs @@ -19,6 +19,14 @@ enum UniformDataType:uint32 { kStruct, } +// 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 { + kPadding = 0, + kFloat = 1, +} + table UniformDescription { name: string; location: uint64; @@ -27,8 +35,8 @@ table UniformDescription { rows: uint64; columns: uint64; array_elements: uint64; - padding_locations: [uint64]; - float_count: uint64; + struct_layout: [StructByteType]; + struct_float_count: uint64; } // The subset of impeller::ShaderType that may be used for vertex attributes. diff --git a/lib/ui/painting/fragment_program.cc b/lib/ui/painting/fragment_program.cc index 428ed92a6f362..5e1f91cac6ebd 100644 --- a/lib/ui/painting/fragment_program.cc +++ b/lib/ui/painting/fragment_program.cc @@ -63,10 +63,6 @@ std::string FragmentProgram::initFromAsset(const std::string& asset_name) { auto backend = UIDartState::Current()->GetRuntimeStageBackend(); auto runtime_stage = runtime_stages[backend]; - FML_LOG(ERROR) << "Runtime stage backend is " << static_cast(backend) - << " and got " << runtime_stage->GetUniforms().size() - << " uniforms" - << runtime_stage->GetCodeMapping()->GetMapping(); if (!runtime_stage) { std::ostringstream stream; stream << "Asset '" << asset_name @@ -117,16 +113,17 @@ std::string FragmentProgram::initFromAsset(const std::string& asset_name) { Dart_Handle result = Dart_SetField(ths, tonic::ToDart("_samplerCount"), Dart_NewInteger(sampled_image_count)); if (Dart_IsError(result)) { - Dart_PropagateError(result); + return "Failed to set sampler count for fragment program."; } size_t rounded_uniform_bytes = (other_uniforms_bytes + sizeof(float) - 1) & ~(sizeof(float) - 1); size_t float_count = rounded_uniform_bytes / sizeof(float); + result = Dart_SetField(ths, tonic::ToDart("_uniformFloatCount"), Dart_NewInteger(float_count)); if (Dart_IsError(result)) { - Dart_PropagateError(result); + return "Failed to set uniform float count for fragment program."; } return ""; From 7a2f981131caee26b202223bb19125731414d49d Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 9 Jan 2024 14:58:06 -0800 Subject: [PATCH 04/13] fix samplers --- .../contents/runtime_effect_contents.cc | 100 +++++++++++------- 1 file changed, 59 insertions(+), 41 deletions(-) diff --git a/impeller/entity/contents/runtime_effect_contents.cc b/impeller/entity/contents/runtime_effect_contents.cc index 5a49849c761b4..28e8e38e34dc2 100644 --- a/impeller/entity/contents/runtime_effect_contents.cc +++ b/impeller/entity/contents/runtime_effect_contents.cc @@ -127,7 +127,8 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, GetGeometry()->GetPositionBuffer(renderer, entity, pass); //-------------------------------------------------------------------------- - /// Get or create runtime stage pipeline. + /// Set up the command. Defer setting up the pipeline until the descriptor set + /// layouts are known from the uniforms. /// const auto& caps = context->GetCapabilities(); @@ -135,48 +136,9 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, const auto stencil_attachment_format = caps->GetDefaultStencilFormat(); using VS = RuntimeEffectVertexShader; - PipelineDescriptor desc; - desc.SetLabel("Runtime Stage"); - desc.AddStageEntrypoint( - library->GetFunction(VS::kEntrypointName, ShaderStage::kVertex)); - desc.AddStageEntrypoint(library->GetFunction(runtime_stage_->GetEntrypoint(), - ShaderStage::kFragment)); - auto vertex_descriptor = std::make_shared(); - vertex_descriptor->SetStageInputs(VS::kAllShaderStageInputs, - VS::kInterleavedBufferLayout); - vertex_descriptor->RegisterDescriptorSetLayouts(VS::kDescriptorSetLayouts); - vertex_descriptor->RegisterDescriptorSetLayouts( - std::array{DescriptorSetLayout{ - 64, - DescriptorType::kUniformBuffer, - ShaderStage::kFragment, - }}); - desc.SetVertexDescriptor(std::move(vertex_descriptor)); - desc.SetColorAttachmentDescriptor( - 0u, {.format = color_attachment_format, .blending_enabled = true}); - - StencilAttachmentDescriptor stencil0; - stencil0.stencil_compare = CompareFunction::kEqual; - desc.SetStencilAttachmentDescriptors(stencil0); - desc.SetStencilPixelFormat(stencil_attachment_format); - - auto options = OptionsFromPassAndEntity(pass, entity); - if (geometry_result.prevent_overdraw) { - options.stencil_compare = CompareFunction::kEqual; - options.stencil_operation = StencilOperation::kIncrementClamp; - } - options.primitive_type = geometry_result.type; - options.ApplyToPipelineDescriptor(desc); - - auto pipeline = context->GetPipelineLibrary()->GetPipeline(desc).Get(); - if (!pipeline) { - VALIDATION_LOG << "Failed to get or create runtime effect pipeline."; - return false; - } Command cmd; DEBUG_COMMAND_INFO(cmd, "RuntimeEffectContents"); - cmd.pipeline = pipeline; cmd.stencil_reference = entity.GetClipDepth(); cmd.BindVertices(std::move(geometry_result.vertex_buffer)); @@ -196,6 +158,8 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, size_t buffer_index = 0; size_t buffer_offset = 0; + std::vector descriptor_set_layouts; + for (const auto& uniform : runtime_stage_->GetUniforms()) { std::shared_ptr metadata = MakeShaderMetadata(uniform); @@ -236,7 +200,11 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, case kStruct: { FML_DCHECK(renderer.GetContext()->GetBackendType() == Context::BackendType::kVulkan); - + descriptor_set_layouts.emplace_back(DescriptorSetLayout{ + static_cast(uniform.location), + DescriptorType::kUniformBuffer, + ShaderStage::kFragment, + }); ShaderUniformSlot uniform_slot; uniform_slot.name = uniform.name.c_str(); uniform_slot.binding = uniform.location; @@ -281,6 +249,19 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, SampledImageSlot image_slot; image_slot.name = uniform.name.c_str(); + + uint32_t sampler_binding_location = 0u; + if (!descriptor_set_layouts.empty()) { + sampler_binding_location = descriptor_set_layouts.back().binding + 1; + } + + descriptor_set_layouts.emplace_back(DescriptorSetLayout{ + sampler_binding_location, + DescriptorType::kSampledImage, + ShaderStage::kFragment, + }); + + image_slot.binding = sampler_binding_location; image_slot.texture_index = uniform.location - minimum_sampler_index; cmd.BindResource(ShaderStage::kFragment, image_slot, *metadata, input.texture, sampler); @@ -293,6 +274,43 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, } } + /// Now that the descriptor set layouts are known, get the pipeline. + PipelineDescriptor desc; + desc.SetLabel("Runtime Stage"); + desc.AddStageEntrypoint( + library->GetFunction(VS::kEntrypointName, ShaderStage::kVertex)); + desc.AddStageEntrypoint(library->GetFunction(runtime_stage_->GetEntrypoint(), + ShaderStage::kFragment)); + auto vertex_descriptor = std::make_shared(); + vertex_descriptor->SetStageInputs(VS::kAllShaderStageInputs, + VS::kInterleavedBufferLayout); + vertex_descriptor->RegisterDescriptorSetLayouts(VS::kDescriptorSetLayouts); + vertex_descriptor->RegisterDescriptorSetLayouts( + descriptor_set_layouts.data(), descriptor_set_layouts.size()); + desc.SetVertexDescriptor(std::move(vertex_descriptor)); + desc.SetColorAttachmentDescriptor( + 0u, {.format = color_attachment_format, .blending_enabled = true}); + + StencilAttachmentDescriptor stencil0; + stencil0.stencil_compare = CompareFunction::kEqual; + desc.SetStencilAttachmentDescriptors(stencil0); + desc.SetStencilPixelFormat(stencil_attachment_format); + + auto options = OptionsFromPassAndEntity(pass, entity); + if (geometry_result.prevent_overdraw) { + options.stencil_compare = CompareFunction::kEqual; + options.stencil_operation = StencilOperation::kIncrementClamp; + } + options.primitive_type = geometry_result.type; + options.ApplyToPipelineDescriptor(desc); + + auto pipeline = context->GetPipelineLibrary()->GetPipeline(desc).Get(); + if (!pipeline) { + VALIDATION_LOG << "Failed to get or create runtime effect pipeline."; + return false; + } + cmd.pipeline = pipeline; + pass.AddCommand(std::move(cmd)); if (geometry_result.prevent_overdraw) { From c48be1047104bb17411e4cd554a82397c86788b4 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 9 Jan 2024 15:36:31 -0800 Subject: [PATCH 05/13] update test --- .../runtime_stage/runtime_stage_unittests.cc | 289 ++++++++++-------- 1 file changed, 161 insertions(+), 128 deletions(-) diff --git a/impeller/runtime_stage/runtime_stage_unittests.cc b/impeller/runtime_stage/runtime_stage_unittests.cc index 0eaf46c1d529c..477a9e24f84d2 100644 --- a/impeller/runtime_stage/runtime_stage_unittests.cc +++ b/impeller/runtime_stage/runtime_stage_unittests.cc @@ -7,6 +7,7 @@ #include "flutter/fml/make_copyable.h" #include "flutter/impeller/fixtures/simple.vert.h" #include "flutter/testing/testing.h" +#include "gmock/gmock.h" #include "impeller/base/allocation.h" #include "impeller/base/validation.h" #include "impeller/core/runtime_types.h" @@ -59,135 +60,167 @@ TEST_P(RuntimeStageTest, CanReadUniforms) { auto stage = stages[PlaygroundBackendToRuntimeStageBackend(GetBackend())]; ASSERT_TRUE(stage->IsValid()); - ASSERT_EQ(stage->GetUniforms().size(), 17u); - { - auto uni = stage->GetUniform("u_color"); - ASSERT_NE(uni, nullptr); - EXPECT_EQ(uni->dimensions.rows, 4u); - EXPECT_EQ(uni->dimensions.cols, 1u); - EXPECT_EQ(uni->location, 0u); - EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); - } - { - auto uni = stage->GetUniform("u_alpha"); - ASSERT_NE(uni, nullptr); - EXPECT_EQ(uni->dimensions.rows, 1u); - EXPECT_EQ(uni->dimensions.cols, 1u); - EXPECT_EQ(uni->location, 1u); - EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); - } - { - auto uni = stage->GetUniform("u_sparkle_color"); - ASSERT_NE(uni, nullptr); - EXPECT_EQ(uni->dimensions.rows, 4u); - EXPECT_EQ(uni->dimensions.cols, 1u); - EXPECT_EQ(uni->location, 2u); - EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); - } - { - auto uni = stage->GetUniform("u_sparkle_alpha"); - ASSERT_NE(uni, nullptr); - EXPECT_EQ(uni->dimensions.rows, 1u); - EXPECT_EQ(uni->dimensions.cols, 1u); - EXPECT_EQ(uni->location, 3u); - EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); - } - { - auto uni = stage->GetUniform("u_blur"); - ASSERT_NE(uni, nullptr); - EXPECT_EQ(uni->dimensions.rows, 1u); - EXPECT_EQ(uni->dimensions.cols, 1u); - EXPECT_EQ(uni->location, 4u); - EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); - } - { - auto uni = stage->GetUniform("u_radius_scale"); - ASSERT_NE(uni, nullptr); - EXPECT_EQ(uni->dimensions.rows, 1u); - EXPECT_EQ(uni->dimensions.cols, 1u); - EXPECT_EQ(uni->location, 6u); - EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); - } - { - auto uni = stage->GetUniform("u_max_radius"); - ASSERT_NE(uni, nullptr); - EXPECT_EQ(uni->dimensions.rows, 1u); - EXPECT_EQ(uni->dimensions.cols, 1u); - EXPECT_EQ(uni->location, 7u); - EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); - } - { - auto uni = stage->GetUniform("u_resolution_scale"); - ASSERT_NE(uni, nullptr); - EXPECT_EQ(uni->dimensions.rows, 2u); - EXPECT_EQ(uni->dimensions.cols, 1u); - EXPECT_EQ(uni->location, 8u); - EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); - } - { - auto uni = stage->GetUniform("u_noise_scale"); - ASSERT_NE(uni, nullptr); - EXPECT_EQ(uni->dimensions.rows, 2u); - EXPECT_EQ(uni->dimensions.cols, 1u); - EXPECT_EQ(uni->location, 9u); - EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); - } - { - auto uni = stage->GetUniform("u_noise_phase"); - ASSERT_NE(uni, nullptr); - EXPECT_EQ(uni->dimensions.rows, 1u); - EXPECT_EQ(uni->dimensions.cols, 1u); - EXPECT_EQ(uni->location, 10u); - EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); - } + switch (GetBackend()) { + case PlaygroundBackend::kMetal: + case PlaygroundBackend::kOpenGLES: { + ASSERT_EQ(stage->GetUniforms().size(), 17u); + { + auto uni = stage->GetUniform("u_color"); + ASSERT_NE(uni, nullptr); + EXPECT_EQ(uni->dimensions.rows, 4u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 0u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + } + { + auto uni = stage->GetUniform("u_alpha"); + ASSERT_NE(uni, nullptr); + EXPECT_EQ(uni->dimensions.rows, 1u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 1u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + } + { + auto uni = stage->GetUniform("u_sparkle_color"); + ASSERT_NE(uni, nullptr); + EXPECT_EQ(uni->dimensions.rows, 4u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 2u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + } + { + auto uni = stage->GetUniform("u_sparkle_alpha"); + ASSERT_NE(uni, nullptr); + EXPECT_EQ(uni->dimensions.rows, 1u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 3u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + } + { + auto uni = stage->GetUniform("u_blur"); + ASSERT_NE(uni, nullptr); + EXPECT_EQ(uni->dimensions.rows, 1u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 4u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + } + { + auto uni = stage->GetUniform("u_radius_scale"); + ASSERT_NE(uni, nullptr); + EXPECT_EQ(uni->dimensions.rows, 1u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 6u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + } + { + auto uni = stage->GetUniform("u_max_radius"); + ASSERT_NE(uni, nullptr); + EXPECT_EQ(uni->dimensions.rows, 1u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 7u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + } + { + auto uni = stage->GetUniform("u_resolution_scale"); + ASSERT_NE(uni, nullptr); + EXPECT_EQ(uni->dimensions.rows, 2u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 8u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + } + { + auto uni = stage->GetUniform("u_noise_scale"); + ASSERT_NE(uni, nullptr); + EXPECT_EQ(uni->dimensions.rows, 2u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 9u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + } + { + auto uni = stage->GetUniform("u_noise_phase"); + ASSERT_NE(uni, nullptr); + EXPECT_EQ(uni->dimensions.rows, 1u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 10u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + } - { - auto uni = stage->GetUniform("u_circle1"); - ASSERT_NE(uni, nullptr); - EXPECT_EQ(uni->dimensions.rows, 2u); - EXPECT_EQ(uni->dimensions.cols, 1u); - EXPECT_EQ(uni->location, 11u); - EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); - } - { - auto uni = stage->GetUniform("u_circle2"); - ASSERT_NE(uni, nullptr); - EXPECT_EQ(uni->dimensions.rows, 2u); - EXPECT_EQ(uni->dimensions.cols, 1u); - EXPECT_EQ(uni->location, 12u); - EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); - } - { - auto uni = stage->GetUniform("u_circle3"); - ASSERT_NE(uni, nullptr); - EXPECT_EQ(uni->dimensions.rows, 2u); - EXPECT_EQ(uni->dimensions.cols, 1u); - EXPECT_EQ(uni->location, 13u); - EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); - } - { - auto uni = stage->GetUniform("u_rotation1"); - ASSERT_NE(uni, nullptr); - EXPECT_EQ(uni->dimensions.rows, 2u); - EXPECT_EQ(uni->dimensions.cols, 1u); - EXPECT_EQ(uni->location, 14u); - EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); - } - { - auto uni = stage->GetUniform("u_rotation2"); - ASSERT_NE(uni, nullptr); - EXPECT_EQ(uni->dimensions.rows, 2u); - EXPECT_EQ(uni->dimensions.cols, 1u); - EXPECT_EQ(uni->location, 15u); - EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); - } - { - auto uni = stage->GetUniform("u_rotation3"); - ASSERT_NE(uni, nullptr); - EXPECT_EQ(uni->dimensions.rows, 2u); - EXPECT_EQ(uni->dimensions.cols, 1u); - EXPECT_EQ(uni->location, 16u); - EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + { + auto uni = stage->GetUniform("u_circle1"); + ASSERT_NE(uni, nullptr); + EXPECT_EQ(uni->dimensions.rows, 2u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 11u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + } + { + auto uni = stage->GetUniform("u_circle2"); + ASSERT_NE(uni, nullptr); + EXPECT_EQ(uni->dimensions.rows, 2u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 12u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + } + { + auto uni = stage->GetUniform("u_circle3"); + ASSERT_NE(uni, nullptr); + EXPECT_EQ(uni->dimensions.rows, 2u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 13u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + } + { + auto uni = stage->GetUniform("u_rotation1"); + ASSERT_NE(uni, nullptr); + EXPECT_EQ(uni->dimensions.rows, 2u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 14u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + } + { + auto uni = stage->GetUniform("u_rotation2"); + ASSERT_NE(uni, nullptr); + EXPECT_EQ(uni->dimensions.rows, 2u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 15u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + } + { + auto uni = stage->GetUniform("u_rotation3"); + ASSERT_NE(uni, nullptr); + EXPECT_EQ(uni->dimensions.rows, 2u); + EXPECT_EQ(uni->dimensions.cols, 1u); + EXPECT_EQ(uni->location, 16u); + EXPECT_EQ(uni->type, RuntimeUniformType::kFloat); + } + break; + } + case PlaygroundBackend::kVulkan: { + EXPECT_EQ(stage->GetUniforms().size(), 1u); + auto uni = stage->GetUniform(RuntimeStage::kVulkanUBOName); + ASSERT_TRUE(uni); + EXPECT_EQ(uni->type, RuntimeUniformType::kStruct); + EXPECT_EQ(uni->struct_float_count, 32u); + + // There are 36 4 byte chunks in the UBO: 32 for the 32 floats, and 4 for + // padding. Initialize a vector as if they'll all be floats, then manually + // set the few padding bytes. If the shader changes, the padding locations + // will change as well. For example, if `u_alpha` was moved to the end, + // three bytes of padding could potentially be dropped - or if some of the + // scalar floats were changed to vec2 or vec4s, or if any vec3s are + // 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_THAT(uni->struct_layout, ::testing::ElementsAreArray(layout)); + break; + } } } From a202d34e08fe803ec005442f775fea5e015e80d5 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 9 Jan 2024 15:40:52 -0800 Subject: [PATCH 06/13] another updated test --- impeller/renderer/backend/vulkan/context_vk_unittests.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/impeller/renderer/backend/vulkan/context_vk_unittests.cc b/impeller/renderer/backend/vulkan/context_vk_unittests.cc index 25128aa347966..53dd23b755488 100644 --- a/impeller/renderer/backend/vulkan/context_vk_unittests.cc +++ b/impeller/renderer/backend/vulkan/context_vk_unittests.cc @@ -85,7 +85,7 @@ TEST(ContextVKTest, DeleteShaderFunctionAfterContext) { pipeline_desc.SetVertexDescriptor(std::make_shared()); std::vector data = {0x03, 0x02, 0x23, 0x07}; context->GetShaderLibrary()->RegisterFunction( - "foobar", ShaderStage::kFragment, + "foobar_fragment_main", ShaderStage::kFragment, std::make_shared(data), [](bool) {}); shader_function = context->GetShaderLibrary()->GetFunction( "foobar_fragment_main", ShaderStage::kFragment); From db0ad498452a764071af21e0e8dd7b179b286c31 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 9 Jan 2024 15:59:39 -0800 Subject: [PATCH 07/13] unintended file change --- impeller/playground/backend/vulkan/playground_impl_vk.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/impeller/playground/backend/vulkan/playground_impl_vk.cc b/impeller/playground/backend/vulkan/playground_impl_vk.cc index 0ac2f050ef44c..d05004f4eb43a 100644 --- a/impeller/playground/backend/vulkan/playground_impl_vk.cc +++ b/impeller/playground/backend/vulkan/playground_impl_vk.cc @@ -73,7 +73,7 @@ PlaygroundImplVK::PlaygroundImplVK(PlaygroundSwitches switches) return; } - // InitGlobalVulkanInstance(); + InitGlobalVulkanInstance(); ::glfwDefaultWindowHints(); ::glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); @@ -101,8 +101,6 @@ PlaygroundImplVK::PlaygroundImplVK(PlaygroundSwitches switches) return; } - static std::shared_ptr gLeakedContext = context_vk; - // Without this, the playground will timeout waiting for the presentation. // It's better to have some Vulkan validation tests running on CI to catch // regressions, but for now this is a workaround. From 2b1d8c243f835a99dc3949ea922ca54ecedad180 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Tue, 9 Jan 2024 21:37:18 -0800 Subject: [PATCH 08/13] Cache the pipeline, resolve one more TODO --- impeller/entity/contents/content_context.cc | 14 +++++ impeller/entity/contents/content_context.h | 32 ++++++++++ .../contents/runtime_effect_contents.cc | 62 +++++++++++-------- impeller/entity/entity_unittests.cc | 9 ++- .../runtime_stage/runtime_stage_unittests.cc | 4 +- 5 files changed, 90 insertions(+), 31 deletions(-) diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index 606420b0ece98..b20f08c086efa 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -488,4 +488,18 @@ void ContentContext::SetWireframe(bool wireframe) { wireframe_ = wireframe; } +std::shared_ptr> +ContentContext::GetCachedRuntimeEffectPipeline( + const std::string& unique_entrypoint_name, + const ContentContextOptions& options, + const std::function>()>& + create_callback) const { + RuntimeEffectPipelineKey key{unique_entrypoint_name, options}; + auto it = runtime_effect_pipelines_.find(key); + if (it == runtime_effect_pipelines_.end()) { + it = runtime_effect_pipelines_.insert(it, {key, create_callback()}); + } + return it->second; +} + } // namespace impeller diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index d8217da6d5e35..0ceafabe2e5c9 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -729,10 +729,42 @@ class ContentContext { return render_target_cache_; } + std::shared_ptr> GetCachedRuntimeEffectPipeline( + const std::string& unique_entrypoint_name, + const ContentContextOptions& options, + const std::function>()>& + create_callback) const; + private: std::shared_ptr context_; std::shared_ptr lazy_glyph_atlas_; + struct RuntimeEffectPipelineKey { + std::string unique_entrypoint_name; + ContentContextOptions options; + + struct Hash { + std::size_t operator()(const RuntimeEffectPipelineKey& key) const { + return fml::HashCombine(key.unique_entrypoint_name, + ContentContextOptions::Hash{}(key.options)); + } + }; + + struct Equal { + constexpr bool operator()(const RuntimeEffectPipelineKey& lhs, + const RuntimeEffectPipelineKey& rhs) const { + return lhs.unique_entrypoint_name == rhs.unique_entrypoint_name && + ContentContextOptions::Equal{}(lhs.options, rhs.options); + } + }; + }; + + mutable std::unordered_map>, + RuntimeEffectPipelineKey::Hash, + RuntimeEffectPipelineKey::Equal> + runtime_effect_pipelines_; + template class Variants { public: diff --git a/impeller/entity/contents/runtime_effect_contents.cc b/impeller/entity/contents/runtime_effect_contents.cc index 28e8e38e34dc2..d0a41c410c9e5 100644 --- a/impeller/entity/contents/runtime_effect_contents.cc +++ b/impeller/entity/contents/runtime_effect_contents.cc @@ -275,26 +275,6 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, } /// Now that the descriptor set layouts are known, get the pipeline. - PipelineDescriptor desc; - desc.SetLabel("Runtime Stage"); - desc.AddStageEntrypoint( - library->GetFunction(VS::kEntrypointName, ShaderStage::kVertex)); - desc.AddStageEntrypoint(library->GetFunction(runtime_stage_->GetEntrypoint(), - ShaderStage::kFragment)); - auto vertex_descriptor = std::make_shared(); - vertex_descriptor->SetStageInputs(VS::kAllShaderStageInputs, - VS::kInterleavedBufferLayout); - vertex_descriptor->RegisterDescriptorSetLayouts(VS::kDescriptorSetLayouts); - vertex_descriptor->RegisterDescriptorSetLayouts( - descriptor_set_layouts.data(), descriptor_set_layouts.size()); - desc.SetVertexDescriptor(std::move(vertex_descriptor)); - desc.SetColorAttachmentDescriptor( - 0u, {.format = color_attachment_format, .blending_enabled = true}); - - StencilAttachmentDescriptor stencil0; - stencil0.stencil_compare = CompareFunction::kEqual; - desc.SetStencilAttachmentDescriptors(stencil0); - desc.SetStencilPixelFormat(stencil_attachment_format); auto options = OptionsFromPassAndEntity(pass, entity); if (geometry_result.prevent_overdraw) { @@ -302,14 +282,42 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer, options.stencil_operation = StencilOperation::kIncrementClamp; } options.primitive_type = geometry_result.type; - options.ApplyToPipelineDescriptor(desc); - auto pipeline = context->GetPipelineLibrary()->GetPipeline(desc).Get(); - if (!pipeline) { - VALIDATION_LOG << "Failed to get or create runtime effect pipeline."; - return false; - } - cmd.pipeline = pipeline; + auto create_callback = + [&]() -> std::shared_ptr> { + PipelineDescriptor desc; + desc.SetLabel("Runtime Stage"); + desc.AddStageEntrypoint( + library->GetFunction(VS::kEntrypointName, ShaderStage::kVertex)); + desc.AddStageEntrypoint(library->GetFunction( + runtime_stage_->GetEntrypoint(), ShaderStage::kFragment)); + auto vertex_descriptor = std::make_shared(); + vertex_descriptor->SetStageInputs(VS::kAllShaderStageInputs, + VS::kInterleavedBufferLayout); + vertex_descriptor->RegisterDescriptorSetLayouts(VS::kDescriptorSetLayouts); + vertex_descriptor->RegisterDescriptorSetLayouts( + descriptor_set_layouts.data(), descriptor_set_layouts.size()); + desc.SetVertexDescriptor(std::move(vertex_descriptor)); + desc.SetColorAttachmentDescriptor( + 0u, {.format = color_attachment_format, .blending_enabled = true}); + + StencilAttachmentDescriptor stencil0; + stencil0.stencil_compare = CompareFunction::kEqual; + desc.SetStencilAttachmentDescriptors(stencil0); + desc.SetStencilPixelFormat(stencil_attachment_format); + + options.ApplyToPipelineDescriptor(desc); + auto pipeline = context->GetPipelineLibrary()->GetPipeline(desc).Get(); + if (!pipeline) { + VALIDATION_LOG << "Failed to get or create runtime effect pipeline."; + return nullptr; + } + + return pipeline; + }; + + cmd.pipeline = renderer.GetCachedRuntimeEffectPipeline( + runtime_stage_->GetEntrypoint(), options, create_callback); pass.AddCommand(std::move(cmd)); diff --git a/impeller/entity/entity_unittests.cc b/impeller/entity/entity_unittests.cc index 757f42d832d65..51edc653305eb 100644 --- a/impeller/entity/entity_unittests.cc +++ b/impeller/entity/entity_unittests.cc @@ -2140,6 +2140,7 @@ TEST_P(EntityTest, RuntimeEffect) { ASSERT_TRUE(runtime_stage->IsDirty()); bool first_frame = true; + Pipeline* first_pipeline = nullptr; auto callback = [&](ContentContext& context, RenderPass& pass) -> bool { if (first_frame) { first_frame = false; @@ -2166,7 +2167,13 @@ TEST_P(EntityTest, RuntimeEffect) { Entity entity; entity.SetContents(contents); - return contents->Render(context, entity, pass); + bool result = contents->Render(context, entity, pass); + if (!first_pipeline) { + first_pipeline = pass.GetCommands().back().pipeline.get(); + } else { + EXPECT_EQ(pass.GetCommands().back().pipeline.get(), first_pipeline); + } + return result; }; ASSERT_TRUE(OpenPlaygroundHere(callback)); } diff --git a/impeller/runtime_stage/runtime_stage_unittests.cc b/impeller/runtime_stage/runtime_stage_unittests.cc index 477a9e24f84d2..b4d60f225697b 100644 --- a/impeller/runtime_stage/runtime_stage_unittests.cc +++ b/impeller/runtime_stage/runtime_stage_unittests.cc @@ -303,9 +303,7 @@ TEST_P(RuntimeStageTest, ContainsExpectedShaderTypes) { EXPECT_TRUE(stages[RuntimeStageBackend::kOpenGLES]); EXPECT_TRUE(stages[RuntimeStageBackend::kMetal]); - // TODO(dnfield): Flip this when - // https://github.com/flutter/flutter/issues/122823 is fixed. - EXPECT_FALSE(stages[RuntimeStageBackend::kVulkan]); + EXPECT_TRUE(stages[RuntimeStageBackend::kVulkan]); } } // namespace testing From 1f8b5342112b6c1f69e9ef9a0d7e76c154ee7a87 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 10 Jan 2024 00:02:56 -0800 Subject: [PATCH 09/13] Skip vulkan test on mac where sources dont match backend type? --- impeller/golden_tests/golden_playground_test_mac.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/impeller/golden_tests/golden_playground_test_mac.cc b/impeller/golden_tests/golden_playground_test_mac.cc index 80f5a8630dc08..81cec2c283a10 100644 --- a/impeller/golden_tests/golden_playground_test_mac.cc +++ b/impeller/golden_tests/golden_playground_test_mac.cc @@ -53,6 +53,8 @@ static const std::vector kSkipTests = { // Gold. "impeller_Play_AiksTest_TextRotated_Metal", "impeller_Play_AiksTest_TextRotated_Vulkan", + // Runtime stage based tests get confused with a Metal context. + "impeller_Play_AiksTest_CanRenderClippedRuntimeEffects_Vulkan", }; namespace { From a1bf361a7bb29d556bdf17506f8937b2a2a5f658 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 10 Jan 2024 00:20:49 -0800 Subject: [PATCH 10/13] GLES issues... --- impeller/renderer/backend/gles/shader_library_gles.cc | 2 +- impeller/runtime_stage/runtime_stage_unittests.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/impeller/renderer/backend/gles/shader_library_gles.cc b/impeller/renderer/backend/gles/shader_library_gles.cc index 5acdcdfd4dc93..48e52e4edc30e 100644 --- a/impeller/renderer/backend/gles/shader_library_gles.cc +++ b/impeller/renderer/backend/gles/shader_library_gles.cc @@ -141,7 +141,7 @@ void ShaderLibraryGLES::UnregisterFunction(std::string name, const auto key = ShaderKey{name, stage}; auto found = functions_.find(key); - if (found != functions_.end()) { + if (found == functions_.end()) { VALIDATION_LOG << "Library function named " << name << " was not found, so it couldn't be unregistered."; return; diff --git a/impeller/runtime_stage/runtime_stage_unittests.cc b/impeller/runtime_stage/runtime_stage_unittests.cc index b4d60f225697b..72eed52cced07 100644 --- a/impeller/runtime_stage/runtime_stage_unittests.cc +++ b/impeller/runtime_stage/runtime_stage_unittests.cc @@ -5,13 +5,13 @@ #include #include "flutter/fml/make_copyable.h" -#include "flutter/impeller/fixtures/simple.vert.h" #include "flutter/testing/testing.h" #include "gmock/gmock.h" #include "impeller/base/allocation.h" #include "impeller/base/validation.h" #include "impeller/core/runtime_types.h" #include "impeller/core/shader_types.h" +#include "impeller/entity/runtime_effect.vert.h" #include "impeller/playground/playground.h" #include "impeller/renderer/pipeline_descriptor.h" #include "impeller/renderer/pipeline_library.h" @@ -267,7 +267,7 @@ TEST_P(RuntimeStageTest, CanCreatePipelineFromRuntimeStage) { ASSERT_NE(stage, nullptr); ASSERT_TRUE(RegisterStage(*stage)); auto library = GetContext()->GetShaderLibrary(); - using VS = SimpleVertexShader; + using VS = RuntimeEffectVertexShader; PipelineDescriptor desc; desc.SetLabel("Runtime Stage InkSparkle"); desc.AddStageEntrypoint( From 5851a3eb2a7662bddb6254aa83cf2c92663ac137 Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 10 Jan 2024 12:17:52 -0800 Subject: [PATCH 11/13] NPE --- impeller/entity/contents/test/contents_test_helpers.h | 2 +- impeller/entity/contents/vertices_contents_unittests.cc | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/impeller/entity/contents/test/contents_test_helpers.h b/impeller/entity/contents/test/contents_test_helpers.h index afcef40c84075..9abbcb5ce22bd 100644 --- a/impeller/entity/contents/test/contents_test_helpers.h +++ b/impeller/entity/contents/test/contents_test_helpers.h @@ -32,7 +32,7 @@ typename T::FragInfo* GetFragInfo(const Command& command) { auto resource = std::find_if(command.fragment_bindings.buffers.begin(), command.fragment_bindings.buffers.end(), [](const BufferAndUniformSlot& data) { - return data.slot.ext_res_0 == 0u; + return data.slot.ext_res_0 == 0u || data.slot.binding == 64; }); if (resource == command.fragment_bindings.buffers.end()) { return nullptr; diff --git a/impeller/entity/contents/vertices_contents_unittests.cc b/impeller/entity/contents/vertices_contents_unittests.cc index 5d928e12f5159..173b13ea4bcfb 100644 --- a/impeller/entity/contents/vertices_contents_unittests.cc +++ b/impeller/entity/contents/vertices_contents_unittests.cc @@ -71,6 +71,7 @@ TEST_P(EntityTest, RendersDstPerColorWithAlpha) { const auto& cmd = render_pass->GetCommands()[0]; auto* frag_uniforms = GetFragInfo(cmd); + ASSERT_TRUE(frag_uniforms); ASSERT_EQ(frag_uniforms->alpha, 0.5); } From 6e59efc7013c178f83777d409057c3c40802c7dc Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 10 Jan 2024 12:21:39 -0800 Subject: [PATCH 12/13] Fix test failure --- impeller/entity/contents/test/contents_test_helpers.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/impeller/entity/contents/test/contents_test_helpers.h b/impeller/entity/contents/test/contents_test_helpers.h index 9abbcb5ce22bd..3e0a6d5aae845 100644 --- a/impeller/entity/contents/test/contents_test_helpers.h +++ b/impeller/entity/contents/test/contents_test_helpers.h @@ -32,7 +32,8 @@ typename T::FragInfo* GetFragInfo(const Command& command) { auto resource = std::find_if(command.fragment_bindings.buffers.begin(), command.fragment_bindings.buffers.end(), [](const BufferAndUniformSlot& data) { - return data.slot.ext_res_0 == 0u || data.slot.binding == 64; + return data.slot.ext_res_0 == 0u || + data.slot.binding == 64; }); if (resource == command.fragment_bindings.buffers.end()) { return nullptr; From 622289d69254503afd11644743917b9969f7cc3b Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 10 Jan 2024 12:35:31 -0800 Subject: [PATCH 13/13] docs --- impeller/entity/contents/content_context.h | 9 +++++++++ impeller/runtime_stage/runtime_stage.cc | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index 0ceafabe2e5c9..e6b0952e8e759 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -729,6 +729,15 @@ class ContentContext { return render_target_cache_; } + /// RuntimeEffect pipelines must be obtained via this method to avoid + /// re-creating them every frame. + /// + /// The unique_entrypoint_name comes from RuntimeEffect::GetEntrypoint. + /// Impellerc generates a unique entrypoint name for runtime effect shaders + /// based on the input file name and shader stage. + /// + /// The create_callback is synchronously invoked exactly once if a cached + /// pipeline is not found. std::shared_ptr> GetCachedRuntimeEffectPipeline( const std::string& unique_entrypoint_name, const ContentContextOptions& options, diff --git a/impeller/runtime_stage/runtime_stage.cc b/impeller/runtime_stage/runtime_stage.cc index a8f81db79d91c..4b5c6924fa386 100644 --- a/impeller/runtime_stage/runtime_stage.cc +++ b/impeller/runtime_stage/runtime_stage.cc @@ -39,6 +39,10 @@ static RuntimeShaderStage ToShaderStage(fb::Stage stage) { FML_UNREACHABLE(); } +/// The generated name from GLSLang/shaderc for the UBO containing non-opaque +/// uniforms specified in the user-written runtime effect shader. +/// +/// Vulkan does not allow non-opaque uniforms outside of a UBO. const char* RuntimeStage::kVulkanUBOName = "_RESERVED_IDENTIFIER_FIXUP_gl_DefaultUniformBlock";