diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index de0914ad3189b..c9a7e6ac2a084 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2331,6 +2331,11 @@ ORIGIN: ../../../flutter/impeller/scene/shaders/unlit.frag + ../../../flutter/LI ORIGIN: ../../../flutter/impeller/scene/shaders/unskinned.vert + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/scene/skin.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/scene/skin.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/shader_archive/multi_arch_shader_archive.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/shader_archive/multi_arch_shader_archive.fbs + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/shader_archive/multi_arch_shader_archive.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/shader_archive/multi_arch_shader_archive_writer.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/shader_archive/multi_arch_shader_archive_writer.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/shader_archive/shader_archive.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/shader_archive/shader_archive.fbs + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/shader_archive/shader_archive.h + ../../../flutter/LICENSE @@ -5104,6 +5109,11 @@ FILE: ../../../flutter/impeller/scene/shaders/unlit.frag FILE: ../../../flutter/impeller/scene/shaders/unskinned.vert FILE: ../../../flutter/impeller/scene/skin.cc FILE: ../../../flutter/impeller/scene/skin.h +FILE: ../../../flutter/impeller/shader_archive/multi_arch_shader_archive.cc +FILE: ../../../flutter/impeller/shader_archive/multi_arch_shader_archive.fbs +FILE: ../../../flutter/impeller/shader_archive/multi_arch_shader_archive.h +FILE: ../../../flutter/impeller/shader_archive/multi_arch_shader_archive_writer.cc +FILE: ../../../flutter/impeller/shader_archive/multi_arch_shader_archive_writer.h FILE: ../../../flutter/impeller/shader_archive/shader_archive.cc FILE: ../../../flutter/impeller/shader_archive/shader_archive.fbs FILE: ../../../flutter/impeller/shader_archive/shader_archive.h diff --git a/impeller/renderer/backend/gles/shader_library_gles.cc b/impeller/renderer/backend/gles/shader_library_gles.cc index c26c62bc8f183..d56dc55316e3a 100644 --- a/impeller/renderer/backend/gles/shader_library_gles.cc +++ b/impeller/renderer/backend/gles/shader_library_gles.cc @@ -10,6 +10,7 @@ #include "impeller/base/config.h" #include "impeller/base/validation.h" #include "impeller/renderer/backend/gles/shader_function_gles.h" +#include "impeller/shader_archive/multi_arch_shader_archive.h" #include "impeller/shader_archive/shader_archive.h" namespace impeller { @@ -74,12 +75,13 @@ ShaderLibraryGLES::ShaderLibraryGLES( return true; }; for (auto library : shader_libraries) { - auto blob_library = ShaderArchive{std::move(library)}; - if (!blob_library.IsValid()) { - VALIDATION_LOG << "Could not construct blob library for shaders."; + auto gles_archive = MultiArchShaderArchive::CreateArchiveFromMapping( + std::move(library), ArchiveRenderingBackend::kOpenGLES); + if (!gles_archive || !gles_archive->IsValid()) { + VALIDATION_LOG << "Could not construct shader library."; return; } - blob_library.IterateAllShaders(iterator); + gles_archive->IterateAllShaders(iterator); } functions_ = functions; diff --git a/impeller/renderer/backend/vulkan/shader_library_vk.cc b/impeller/renderer/backend/vulkan/shader_library_vk.cc index cf25348d26dc1..8e825beb1fa70 100644 --- a/impeller/renderer/backend/vulkan/shader_library_vk.cc +++ b/impeller/renderer/backend/vulkan/shader_library_vk.cc @@ -8,6 +8,7 @@ #include "flutter/fml/trace_event.h" #include "impeller/renderer/backend/vulkan/context_vk.h" #include "impeller/renderer/backend/vulkan/shader_function_vk.h" +#include "impeller/shader_archive/multi_arch_shader_archive.h" #include "impeller/shader_archive/shader_archive.h" namespace impeller { @@ -69,12 +70,13 @@ ShaderLibraryVK::ShaderLibraryVK( return true; }; for (const auto& library_data : shader_libraries_data) { - auto blob_library = ShaderArchive{library_data}; - if (!blob_library.IsValid()) { - VALIDATION_LOG << "Could not construct shader blob library."; + auto vulkan_library = MultiArchShaderArchive::CreateArchiveFromMapping( + library_data, ArchiveRenderingBackend::kVulkan); + if (!vulkan_library || !vulkan_library->IsValid()) { + VALIDATION_LOG << "Could not construct Vulkan shader library archive."; return; } - blob_library.IterateAllShaders(iterator); + vulkan_library->IterateAllShaders(iterator); } if (!success) { diff --git a/impeller/shader_archive/BUILD.gn b/impeller/shader_archive/BUILD.gn index 62bc99912f9d4..5f52244a65652 100644 --- a/impeller/shader_archive/BUILD.gn +++ b/impeller/shader_archive/BUILD.gn @@ -11,13 +11,20 @@ config("shader_archive_config") { } flatbuffers("shader_archive_flatbuffers") { - flatbuffers = [ "shader_archive.fbs" ] + flatbuffers = [ + "shader_archive.fbs", + "multi_arch_shader_archive.fbs", + ] public_configs = [ ":shader_archive_config" ] public_deps = [ "//third_party/flatbuffers" ] } impeller_component("shader_archive") { sources = [ + "multi_arch_shader_archive.cc", + "multi_arch_shader_archive.h", + "multi_arch_shader_archive_writer.cc", + "multi_arch_shader_archive_writer.h", "shader_archive.cc", "shader_archive.h", "shader_archive_types.h", diff --git a/impeller/shader_archive/multi_arch_shader_archive.cc b/impeller/shader_archive/multi_arch_shader_archive.cc new file mode 100644 index 0000000000000..922d6eb463146 --- /dev/null +++ b/impeller/shader_archive/multi_arch_shader_archive.cc @@ -0,0 +1,104 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/shader_archive/multi_arch_shader_archive.h" + +#include "impeller/shader_archive/multi_arch_shader_archive_flatbuffers.h" + +namespace impeller { + +constexpr ArchiveRenderingBackend ToArchiveRenderingBackend( + fb::RenderingBackend backend) { + switch (backend) { + case fb::RenderingBackend::kOpenGLES: + return ArchiveRenderingBackend::kOpenGLES; + case fb::RenderingBackend::kVulkan: + return ArchiveRenderingBackend::kVulkan; + case fb::RenderingBackend::kMetal: + return ArchiveRenderingBackend::kMetal; + } + FML_UNREACHABLE(); +} + +std::shared_ptr MultiArchShaderArchive::CreateArchiveFromMapping( + const std::shared_ptr& mapping, + ArchiveRenderingBackend backend) { + { + auto multi_archive = std::make_shared(mapping); + if (multi_archive->IsValid()) { + return multi_archive->GetShaderArchive(backend); + } + } + { + auto single_archive = + std::shared_ptr(new ShaderArchive(mapping)); + if (single_archive->IsValid()) { + return single_archive; + } + } + return nullptr; +} + +MultiArchShaderArchive::MultiArchShaderArchive( + const std::shared_ptr& mapping) { + if (!mapping) { + return; + } + + if (!fb::MultiArchShaderArchiveBufferHasIdentifier(mapping->GetMapping())) { + return; + } + + const auto* multi_arch = fb::GetMultiArchShaderArchive(mapping->GetMapping()); + + if (!multi_arch) { + return; + } + + if (auto archives = multi_arch->items()) { + for (auto i = archives->begin(), end = archives->end(); i != end; i++) { + // This implementation is unable to handle multiple archives for the same + // backend. + backend_mappings_[ToArchiveRenderingBackend(i->rendering_backend())] = + std::make_shared(i->mapping()->Data(), + i->mapping()->size(), + [mapping](auto, auto) { + // Just hold the mapping. + }); + } + } + + is_valid_ = true; +} + +MultiArchShaderArchive::~MultiArchShaderArchive() = default; + +bool MultiArchShaderArchive::IsValid() const { + return is_valid_; +} + +std::shared_ptr MultiArchShaderArchive::GetArchive( + ArchiveRenderingBackend backend) const { + auto found = backend_mappings_.find(backend); + if (found == backend_mappings_.end()) { + return nullptr; + } + return found->second; +} + +std::shared_ptr MultiArchShaderArchive::GetShaderArchive( + ArchiveRenderingBackend backend) const { + auto archive = GetArchive(backend); + if (!archive) { + return nullptr; + } + auto shader_archive = + std::shared_ptr(new ShaderArchive(std::move(archive))); + if (!shader_archive->IsValid()) { + return nullptr; + } + return shader_archive; +} + +} // namespace impeller diff --git a/impeller/shader_archive/multi_arch_shader_archive.fbs b/impeller/shader_archive/multi_arch_shader_archive.fbs new file mode 100644 index 0000000000000..22760c6d6e002 --- /dev/null +++ b/impeller/shader_archive/multi_arch_shader_archive.fbs @@ -0,0 +1,25 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +namespace impeller.fb; + +enum RenderingBackend:byte { + kOpenGLES, + kVulkan, + kMetal, +} + +table ShaderArchiveBlob { + rendering_backend: RenderingBackend; + mapping: [ubyte]; +} + +table MultiArchShaderArchive { + // We could have just as easily used the existing `ShaderArchive` table here. + // However, those tables aren't used by Metal. + items: [ShaderArchiveBlob]; +} + +root_type MultiArchShaderArchive; +file_identifier "MARC"; diff --git a/impeller/shader_archive/multi_arch_shader_archive.h b/impeller/shader_archive/multi_arch_shader_archive.h new file mode 100644 index 0000000000000..1ab328670b6a2 --- /dev/null +++ b/impeller/shader_archive/multi_arch_shader_archive.h @@ -0,0 +1,44 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include + +#include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" +#include "impeller/shader_archive/shader_archive.h" +#include "impeller/shader_archive/shader_archive_types.h" + +namespace impeller { + +class MultiArchShaderArchive { + public: + static std::shared_ptr CreateArchiveFromMapping( + const std::shared_ptr& mapping, + ArchiveRenderingBackend backend); + + explicit MultiArchShaderArchive( + const std::shared_ptr& mapping); + + ~MultiArchShaderArchive(); + + std::shared_ptr GetArchive( + ArchiveRenderingBackend backend) const; + + std::shared_ptr GetShaderArchive( + ArchiveRenderingBackend backend) const; + + bool IsValid() const; + + private: + std::map> + backend_mappings_; + bool is_valid_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(MultiArchShaderArchive); +}; + +} // namespace impeller diff --git a/impeller/shader_archive/multi_arch_shader_archive_writer.cc b/impeller/shader_archive/multi_arch_shader_archive_writer.cc new file mode 100644 index 0000000000000..98a6ada2ef47e --- /dev/null +++ b/impeller/shader_archive/multi_arch_shader_archive_writer.cc @@ -0,0 +1,64 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/shader_archive/multi_arch_shader_archive_writer.h" + +#include "impeller/base/validation.h" +#include "impeller/shader_archive/multi_arch_shader_archive_flatbuffers.h" + +namespace impeller { + +MultiArchShaderArchiveWriter::MultiArchShaderArchiveWriter() = default; + +MultiArchShaderArchiveWriter::~MultiArchShaderArchiveWriter() = default; + +bool MultiArchShaderArchiveWriter::RegisterShaderArchive( + ArchiveRenderingBackend backend, + std::shared_ptr mapping) { + if (!mapping || mapping->GetMapping() == nullptr) { + return false; + } + if (archives_.find(backend) != archives_.end()) { + VALIDATION_LOG << "Multi-archive already has a shader library registered " + "for that backend."; + return false; + } + archives_[backend] = std::move(mapping); + return true; +} + +constexpr fb::RenderingBackend ToRenderingBackend( + ArchiveRenderingBackend backend) { + switch (backend) { + case ArchiveRenderingBackend::kMetal: + return fb::RenderingBackend::kMetal; + case ArchiveRenderingBackend::kVulkan: + return fb::RenderingBackend::kVulkan; + case ArchiveRenderingBackend::kOpenGLES: + return fb::RenderingBackend::kOpenGLES; + } + FML_UNREACHABLE(); +} + +std::shared_ptr MultiArchShaderArchiveWriter::CreateMapping() + const { + fb::MultiArchShaderArchiveT multi_archive; + for (const auto& archive : archives_) { + auto archive_blob = std::make_unique(); + archive_blob->rendering_backend = ToRenderingBackend(archive.first); + archive_blob->mapping = { + archive.second->GetMapping(), + archive.second->GetMapping() + archive.second->GetSize()}; + multi_archive.items.emplace_back(std::move(archive_blob)); + } + auto builder = std::make_shared(); + builder->Finish( + fb::MultiArchShaderArchive::Pack(*builder.get(), &multi_archive), + fb::MultiArchShaderArchiveIdentifier()); + return std::make_shared(builder->GetBufferPointer(), + builder->GetSize(), + [builder](auto, auto) {}); +} + +} // namespace impeller diff --git a/impeller/shader_archive/multi_arch_shader_archive_writer.h b/impeller/shader_archive/multi_arch_shader_archive_writer.h new file mode 100644 index 0000000000000..4e1b079b734c9 --- /dev/null +++ b/impeller/shader_archive/multi_arch_shader_archive_writer.h @@ -0,0 +1,34 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" +#include "impeller/shader_archive/shader_archive_types.h" + +namespace impeller { + +class MultiArchShaderArchiveWriter { + public: + MultiArchShaderArchiveWriter(); + + ~MultiArchShaderArchiveWriter(); + + [[nodiscard]] bool RegisterShaderArchive( + ArchiveRenderingBackend backend, + std::shared_ptr mapping); + + std::shared_ptr CreateMapping() const; + + private: + std::map> + archives_; + + FML_DISALLOW_COPY_AND_ASSIGN(MultiArchShaderArchiveWriter); +}; + +} // namespace impeller diff --git a/impeller/shader_archive/shader_archive.cc b/impeller/shader_archive/shader_archive.cc index 4cf2d3e1b564f..00ac140140ccd 100644 --- a/impeller/shader_archive/shader_archive.cc +++ b/impeller/shader_archive/shader_archive.cc @@ -25,7 +25,7 @@ constexpr ArchiveShaderType ToShaderType(fb::Stage stage) { FML_UNREACHABLE(); } -ShaderArchive::ShaderArchive(std::shared_ptr payload) +ShaderArchive::ShaderArchive(std::shared_ptr payload) : payload_(std::move(payload)) { if (!payload_ || payload_->GetMapping() == nullptr) { VALIDATION_LOG << "Shader mapping was absent."; @@ -33,7 +33,7 @@ ShaderArchive::ShaderArchive(std::shared_ptr payload) } if (!fb::ShaderArchiveBufferHasIdentifier(payload_->GetMapping())) { - VALIDATION_LOG << "Invalid shader magic."; + VALIDATION_LOG << "Invalid shader archive magic."; return; } diff --git a/impeller/shader_archive/shader_archive.h b/impeller/shader_archive/shader_archive.h index 4a56b2412347d..3e1182f0191d4 100644 --- a/impeller/shader_archive/shader_archive.h +++ b/impeller/shader_archive/shader_archive.h @@ -15,10 +15,10 @@ namespace impeller { +class MultiArchShaderArchive; + class ShaderArchive { public: - explicit ShaderArchive(std::shared_ptr payload); - ShaderArchive(ShaderArchive&&); ~ShaderArchive(); @@ -37,6 +37,8 @@ class ShaderArchive { const; private: + friend MultiArchShaderArchive; + struct ShaderKey { ArchiveShaderType type = ArchiveShaderType::kFragment; std::string name; @@ -61,10 +63,12 @@ class ShaderArchive { ShaderKey::Hash, ShaderKey::Equal>; - std::shared_ptr payload_; + std::shared_ptr payload_; Shaders shaders_; bool is_valid_ = false; + explicit ShaderArchive(std::shared_ptr payload); + FML_DISALLOW_COPY_AND_ASSIGN(ShaderArchive); }; diff --git a/impeller/shader_archive/shader_archive_types.h b/impeller/shader_archive/shader_archive_types.h index 1c3e4b6f60de8..a8007f3b2ffd0 100644 --- a/impeller/shader_archive/shader_archive_types.h +++ b/impeller/shader_archive/shader_archive_types.h @@ -12,4 +12,10 @@ enum class ArchiveShaderType { kCompute, }; +enum class ArchiveRenderingBackend { + kMetal, + kVulkan, + kOpenGLES, +}; + } // namespace impeller diff --git a/impeller/shader_archive/shader_archive_unittests.cc b/impeller/shader_archive/shader_archive_unittests.cc index ba4a3072c91b7..b8c00121a573f 100644 --- a/impeller/shader_archive/shader_archive_unittests.cc +++ b/impeller/shader_archive/shader_archive_unittests.cc @@ -6,7 +6,12 @@ #include "flutter/fml/mapping.h" #include "flutter/testing/testing.h" +#include "impeller/base/validation.h" +#include "impeller/shader_archive/multi_arch_shader_archive.h" +#include "impeller/shader_archive/multi_arch_shader_archive_flatbuffers.h" +#include "impeller/shader_archive/multi_arch_shader_archive_writer.h" #include "impeller/shader_archive/shader_archive.h" +#include "impeller/shader_archive/shader_archive_flatbuffers.h" #include "impeller/shader_archive/shader_archive_writer.h" namespace impeller { @@ -41,17 +46,51 @@ TEST(ShaderArchiveTest, CanReadAndWriteBlobs) { auto mapping = writer.CreateMapping(); ASSERT_NE(mapping, nullptr); - ShaderArchive library(mapping); - ASSERT_TRUE(library.IsValid()); - ASSERT_EQ(library.GetShaderCount(), 5u); + MultiArchShaderArchiveWriter multi_writer; + + ASSERT_TRUE(multi_writer.RegisterShaderArchive( + ArchiveRenderingBackend::kOpenGLES, mapping)); + + { + ScopedValidationDisable no_val; + // Can't add the same backend again. + ASSERT_FALSE(multi_writer.RegisterShaderArchive( + ArchiveRenderingBackend::kOpenGLES, mapping)); + } + + auto multi_mapping = multi_writer.CreateMapping(); + ASSERT_TRUE(multi_mapping); + + { + ScopedValidationDisable no_val; + auto no_library = MultiArchShaderArchive::CreateArchiveFromMapping( + multi_mapping, ArchiveRenderingBackend::kVulkan); + ASSERT_EQ(no_library, nullptr); + } + + auto library = MultiArchShaderArchive::CreateArchiveFromMapping( + multi_mapping, ArchiveRenderingBackend::kOpenGLES); + ASSERT_EQ(library->GetShaderCount(), 5u); // Wrong type. - ASSERT_EQ(library.GetMapping(ArchiveShaderType::kFragment, "Hello"), nullptr); + ASSERT_EQ(library->GetMapping(ArchiveShaderType::kFragment, "Hello"), + nullptr); - auto hello_vtx = library.GetMapping(ArchiveShaderType::kVertex, "Hello"); + auto hello_vtx = library->GetMapping(ArchiveShaderType::kVertex, "Hello"); ASSERT_NE(hello_vtx, nullptr); ASSERT_EQ(CreateStringFromMapping(*hello_vtx), "World"); } +TEST(ShaderArchiveTest, ArchiveAndMultiArchiveHaveDifferentIdentifiers) { + // The unarchiving process depends on these identifiers to check to see if its + // a standalone archive or a multi-archive. Things will get nutty if these are + // ever the same. + auto archive_id = fb::ShaderArchiveIdentifier(); + auto multi_archive_id = fb::MultiArchShaderArchiveIdentifier(); + ASSERT_EQ(std::strlen(archive_id), std::strlen(multi_archive_id)); + ASSERT_NE(std::strncmp(archive_id, multi_archive_id, std::strlen(archive_id)), + 0); +} + } // namespace testing } // namespace impeller