diff --git a/BUILD.gn b/BUILD.gn index 132ef4f940b93..f9157ff6914a8 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -172,11 +172,13 @@ group("unittests") { public_deps += [ "//flutter/shell/platform/fuchsia:tests" ] } + if (is_mac || is_linux) { + public_deps += [ "//flutter/impeller:impeller_unittests" ] + } + if (is_mac) { - public_deps += [ - "//flutter/impeller:impeller_unittests", - "//flutter/shell/platform/darwin:flutter_channels_unittests", - ] + public_deps += + [ "//flutter/shell/platform/darwin:flutter_channels_unittests" ] } if (!is_win && !is_fuchsia) { diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 77997513f80b8..2237c6311df24 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -611,13 +611,26 @@ FILE: ../../../flutter/impeller/renderer/allocator.cc FILE: ../../../flutter/impeller/renderer/allocator.h FILE: ../../../flutter/impeller/renderer/backend/gles/allocator_gles.cc FILE: ../../../flutter/impeller/renderer/backend/gles/allocator_gles.h +FILE: ../../../flutter/impeller/renderer/backend/gles/buffer_bindings_gles.cc +FILE: ../../../flutter/impeller/renderer/backend/gles/buffer_bindings_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/command_buffer_gles.cc FILE: ../../../flutter/impeller/renderer/backend/gles/command_buffer_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/context_gles.cc FILE: ../../../flutter/impeller/renderer/backend/gles/context_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/device_buffer_gles.cc +FILE: ../../../flutter/impeller/renderer/backend/gles/device_buffer_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/formats_gles.cc +FILE: ../../../flutter/impeller/renderer/backend/gles/formats_gles.h +FILE: ../../../flutter/impeller/renderer/backend/gles/gl_description.cc +FILE: ../../../flutter/impeller/renderer/backend/gles/gl_description.h +FILE: ../../../flutter/impeller/renderer/backend/gles/gles.h +FILE: ../../../flutter/impeller/renderer/backend/gles/gles_handle.cc +FILE: ../../../flutter/impeller/renderer/backend/gles/gles_handle.h +FILE: ../../../flutter/impeller/renderer/backend/gles/gles_test.cc +FILE: ../../../flutter/impeller/renderer/backend/gles/gles_test.h +FILE: ../../../flutter/impeller/renderer/backend/gles/gles_unittests.cc FILE: ../../../flutter/impeller/renderer/backend/gles/pipeline_gles.cc +FILE: ../../../flutter/impeller/renderer/backend/gles/pipeline_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/pipeline_library_gles.cc FILE: ../../../flutter/impeller/renderer/backend/gles/pipeline_library_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/proc_table_gles.cc @@ -625,14 +638,19 @@ FILE: ../../../flutter/impeller/renderer/backend/gles/proc_table_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/reactor_gles.cc FILE: ../../../flutter/impeller/renderer/backend/gles/reactor_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/render_pass_gles.cc +FILE: ../../../flutter/impeller/renderer/backend/gles/render_pass_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/sampler_gles.cc +FILE: ../../../flutter/impeller/renderer/backend/gles/sampler_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/sampler_library_gles.cc +FILE: ../../../flutter/impeller/renderer/backend/gles/sampler_library_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/shader_function_gles.cc +FILE: ../../../flutter/impeller/renderer/backend/gles/shader_function_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/shader_library_gles.cc FILE: ../../../flutter/impeller/renderer/backend/gles/shader_library_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/surface_gles.cc +FILE: ../../../flutter/impeller/renderer/backend/gles/surface_gles.h FILE: ../../../flutter/impeller/renderer/backend/gles/texture_gles.cc -FILE: ../../../flutter/impeller/renderer/backend/gles/vertex_descriptor_gles.cc +FILE: ../../../flutter/impeller/renderer/backend/gles/texture_gles.h FILE: ../../../flutter/impeller/renderer/backend/metal/allocator_mtl.h FILE: ../../../flutter/impeller/renderer/backend/metal/allocator_mtl.mm FILE: ../../../flutter/impeller/renderer/backend/metal/command_buffer_mtl.h @@ -708,6 +726,8 @@ FILE: ../../../flutter/impeller/renderer/sampler_library.cc FILE: ../../../flutter/impeller/renderer/sampler_library.h FILE: ../../../flutter/impeller/renderer/shader_function.cc FILE: ../../../flutter/impeller/renderer/shader_function.h +FILE: ../../../flutter/impeller/renderer/shader_key.cc +FILE: ../../../flutter/impeller/renderer/shader_key.h FILE: ../../../flutter/impeller/renderer/shader_library.cc FILE: ../../../flutter/impeller/renderer/shader_library.h FILE: ../../../flutter/impeller/renderer/shader_types.cc diff --git a/impeller/BUILD.gn b/impeller/BUILD.gn index 4bb5bc1d040ea..c4b0bbe01264e 100644 --- a/impeller/BUILD.gn +++ b/impeller/BUILD.gn @@ -17,6 +17,14 @@ config("impeller_public_config") { defines += [ "IMPELLER_SUPPORTS_RENDERING=1" ] } + if (impeller_enable_metal) { + defines += [ "IMPELLER_ENABLE_METAL=1" ] + } + + if (impeller_enable_opengles) { + defines += [ "IMPELLER_ENABLE_OPENGLES=1" ] + } + if (is_win) { defines += [ "_USE_MATH_DEFINES", @@ -42,6 +50,7 @@ group("impeller") { "entity", "image", "renderer", + "renderer/backend", "typographer", ] } @@ -68,6 +77,7 @@ executable("impeller_unittests") { "image:image_unittests", "playground", "renderer:renderer_unittests", + "renderer/backend:backend_unittests", "typographer:typographer_unittests", ] } diff --git a/impeller/aiks/aiks_unittests.cc b/impeller/aiks/aiks_unittests.cc index c6aa437ec6477..51f92d202855f 100644 --- a/impeller/aiks/aiks_unittests.cc +++ b/impeller/aiks/aiks_unittests.cc @@ -49,7 +49,7 @@ TEST_P(AiksTest, CanvasCanPushPopCTM) { TEST_P(AiksTest, CanRenderColoredRect) { Canvas canvas; Paint paint; - paint.color = Color::Red(); + paint.color = Color::Blue(); canvas.DrawPath(PathBuilder{} .AddRect(Rect::MakeXYWH(100.0, 100.0, 100.0, 100.0)) .TakePath(), diff --git a/impeller/base/allocation.cc b/impeller/base/allocation.cc index 0a66a24e74de5..91ed6a1ffcd4c 100644 --- a/impeller/base/allocation.cc +++ b/impeller/base/allocation.cc @@ -80,4 +80,24 @@ bool Allocation::Reserve(size_t reserved) { return true; } +std::shared_ptr CreateMappingWithCopy(const uint8_t* contents, + size_t length) { + if (contents == nullptr) { + return nullptr; + } + + auto allocation = std::make_shared(); + if (!allocation->Truncate(length)) { + return nullptr; + } + + std::memmove(allocation->GetBuffer(), contents, length); + + return std::make_shared( + reinterpret_cast(allocation->GetBuffer()), // + allocation->GetLength(), // + [allocation](auto, auto) {} // + ); +} + } // namespace impeller diff --git a/impeller/base/allocation.h b/impeller/base/allocation.h index ffb202562ab18..d2103ab9b074a 100644 --- a/impeller/base/allocation.h +++ b/impeller/base/allocation.h @@ -8,6 +8,7 @@ #include #include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" namespace impeller { @@ -39,4 +40,7 @@ class Allocation { FML_DISALLOW_COPY_AND_ASSIGN(Allocation); }; +std::shared_ptr CreateMappingWithCopy(const uint8_t* contents, + size_t length); + } // namespace impeller diff --git a/impeller/base/backend_cast.h b/impeller/base/backend_cast.h index a6bf9db907375..c3044bb2ac71c 100644 --- a/impeller/base/backend_cast.h +++ b/impeller/base/backend_cast.h @@ -16,6 +16,12 @@ class BackendCast { static const Sub& Cast(const Base& base) { return reinterpret_cast(base); } + + static Sub* Cast(Base* base) { return reinterpret_cast(base); } + + static const Sub* Cast(const Base* base) { + return reinterpret_cast(base); + } }; } // namespace impeller diff --git a/impeller/blobcat/blob_library.cc b/impeller/blobcat/blob_library.cc index 809f40ec344b3..f61c2df1d0b97 100644 --- a/impeller/blobcat/blob_library.cc +++ b/impeller/blobcat/blob_library.cc @@ -72,6 +72,8 @@ BlobLibrary::BlobLibrary(std::shared_ptr mapping) is_valid_ = true; } +BlobLibrary::BlobLibrary(BlobLibrary&&) = default; + BlobLibrary::~BlobLibrary() = default; bool BlobLibrary::IsValid() const { @@ -91,4 +93,22 @@ std::shared_ptr BlobLibrary::GetMapping(Blob::ShaderType type, return found == blobs_.end() ? nullptr : found->second; } +size_t BlobLibrary::IterateAllBlobs( + std::function& mapping)> callback) + const { + if (!IsValid() || !callback) { + return 0u; + } + size_t count = 0u; + for (const auto& blob : blobs_) { + count++; + if (!callback(blob.first.type, blob.first.name, blob.second)) { + break; + } + } + return count; +} + } // namespace impeller diff --git a/impeller/blobcat/blob_library.h b/impeller/blobcat/blob_library.h index 200d775dc8424..c548ec95315d1 100644 --- a/impeller/blobcat/blob_library.h +++ b/impeller/blobcat/blob_library.h @@ -19,6 +19,8 @@ class BlobLibrary { public: BlobLibrary(std::shared_ptr mapping); + BlobLibrary(BlobLibrary&&); + ~BlobLibrary(); bool IsValid() const; @@ -28,6 +30,11 @@ class BlobLibrary { std::shared_ptr GetMapping(Blob::ShaderType type, std::string name) const; + size_t IterateAllBlobs( + std::function& mapping)>) const; + private: struct BlobKey { Blob::ShaderType type = Blob::ShaderType::kFragment; diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index dc699e8445558..6a875b52a9d65 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -13,14 +13,19 @@ constexpr std::string_view kReflectionHeaderTemplate = #pragma once -// Note: The nogncheck decorations are only to make GN not mad at the template -// this file is generated from. There are no GN rule violations in the generated -// file itself. -#include "impeller/renderer/buffer_view.h" // nogncheck -#include "impeller/renderer/command.h" // nogncheck -#include "impeller/renderer/sampler.h" // nogncheck -#include "impeller/renderer/shader_types.h" // nogncheck -#include "impeller/renderer/texture.h" // nogncheck +{# Note: The nogncheck decorations are only to make GN not mad at the template#} +{# this file is generated from. There are no GN rule violations in the generated#} +{# file itself and the no-check declarations will be stripped in generated files.#} +#include "impeller/renderer/buffer_view.h" {# // nogncheck #} + +#include "impeller/renderer/command.h" {# // nogncheck #} + +#include "impeller/renderer/sampler.h" {# // nogncheck #} + +#include "impeller/renderer/shader_types.h" {# // nogncheck #} + +#include "impeller/renderer/texture.h" {# // nogncheck #} + namespace impeller { @@ -31,12 +36,14 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader { static constexpr std::string_view kLabel = "{{camel_case(shader_name)}}"; static constexpr std::string_view kEntrypointName = "{{entrypoint}}"; static constexpr ShaderStage kShaderStage = {{to_shader_stage(shader_stage)}}; + // The generator used to prepare these bindings. Metal generators may be used + // by GLES backends but GLES generators are unsuitable for the metal backend. + static constexpr std::string_view kGeneratorName = "{{get_generator_name()}}"; {% if length(struct_definitions) > 0 %} // =========================================================================== // Struct Definitions ======================================================== // =========================================================================== {% for def in struct_definitions %} - struct {{def.name}} { {% for member in def.members %} {{member.type}} {{member.name}}; // (offset {{member.offset}}, size {{member.byte_length}}) @@ -51,10 +58,11 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader { // =========================================================================== {% for buffer in buffers %} - static constexpr auto kResource{{camel_case(buffer.name)}} = ShaderUniformSlot<{{buffer.name}}> { // {{buffer.name}} + static constexpr auto kResource{{camel_case(buffer.name)}} = ShaderUniformSlot { // {{buffer.name}} "{{buffer.name}}", // name {{buffer.ext_res_0}}u, // binding }; + static ShaderMetadata kMetadata{{camel_case(buffer.name)}}; {% endfor %} {% endif %} @@ -94,6 +102,7 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader { {{sampled_image.ext_res_0}}u, // texture {{sampled_image.ext_res_1}}u, // sampler }; + static ShaderMetadata kMetadata{{camel_case(sampled_image.name)}}; {% endfor %} {% endif %} // =========================================================================== @@ -130,7 +139,7 @@ struct {{camel_case(shader_name)}}{{camel_case(shader_stage)}}Shader { {% endfor %}) { return {{ proto.args.0.argument_name }}.BindResource({% for arg in proto.args %} {% if loop.is_first %} -{{to_shader_stage(shader_stage)}}, kResource{{ proto.name }}, {% else %} +{{to_shader_stage(shader_stage)}}, kResource{{ proto.name }}, kMetadata{{ proto.name }}, {% else %} std::move({{ arg.argument_name }}){% if not loop.is_last %}, {% endif %} {% endif %} {% endfor %}); @@ -164,6 +173,26 @@ static_assert(offsetof(Shader::{{def.name}}, {{member.name}}) == {{member.offset {% endfor %} {% endfor %} +{% for buffer in buffers %} +ShaderMetadata Shader::kMetadata{{camel_case(buffer.name)}} = { + "{{buffer.name}}", // name + std::vector { + {% for member in buffer.type.members %} + ShaderStructMemberMetadata { + {{ member.base_type }}, // type + "{{ member.name }}", // name + {{ member.offset }}, // offset + {{ member.size }}, // size + }, + {% endfor %} + } // members +}; +{% endfor %} + +{% for sampled_image in sampled_images %} +ShaderMetadata Shader::kMetadata{{camel_case(sampled_image.name)}}; +{% endfor %} + } // namespace impeller )~~"; diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index b7bf327c606ad..5adb4dc42ff82 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -35,14 +35,33 @@ static CompilerBackend CreateGLSLCompiler(const spirv_cross::ParsedIR& ir, 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; if (source_options.target_platform == TargetPlatform::kOpenGLES) { sl_options.version = 100; sl_options.es = true; + } else { + sl_options.version = 120; + sl_options.es = false; } gl_compiler->set_common_options(sl_options); return gl_compiler; } +static bool EntryPointMustBeNamedMain(TargetPlatform platform) { + switch (platform) { + case TargetPlatform::kUnknown: + FML_UNREACHABLE(); + case TargetPlatform::kMetalDesktop: + case TargetPlatform::kMetalIOS: + return false; + case TargetPlatform::kFlutterSPIRV: + case TargetPlatform::kOpenGLES: + case TargetPlatform::kOpenGLDesktop: + return true; + } + FML_UNREACHABLE(); +} + static CompilerBackend CreateCompiler(const spirv_cross::ParsedIR& ir, const SourceOptions& source_options) { CompilerBackend compiler; @@ -62,8 +81,11 @@ static CompilerBackend CreateCompiler(const spirv_cross::ParsedIR& ir, return {}; } auto* backend = compiler.GetCompiler(); - backend->rename_entry_point("main", source_options.entry_point_name, - ToExecutionModel(source_options.type)); + if (!EntryPointMustBeNamedMain(source_options.target_platform)) { + backend->rename_entry_point("main", source_options.entry_point_name, + ToExecutionModel(source_options.type)); + } + return compiler; } diff --git a/impeller/compiler/compiler_backend.cc b/impeller/compiler/compiler_backend.cc index 14a6d7868e71e..339f6a8ff47d7 100644 --- a/impeller/compiler/compiler_backend.cc +++ b/impeller/compiler/compiler_backend.cc @@ -7,14 +7,16 @@ namespace impeller { namespace compiler { -CompilerBackend::CompilerBackend(MSLCompiler compiler) : compiler_(compiler) {} +CompilerBackend::CompilerBackend(MSLCompiler compiler) + : CompilerBackend(Type::kMSL, compiler) {} -CompilerBackend::CompilerBackend(GLSLCompiler compiler) : compiler_(compiler) {} +CompilerBackend::CompilerBackend(GLSLCompiler compiler) + : CompilerBackend(Type::kGLSL, compiler) {} CompilerBackend::CompilerBackend() = default; -CompilerBackend::CompilerBackend(Compiler compiler) - : compiler_(std::move(compiler)){}; +CompilerBackend::CompilerBackend(Type type, Compiler compiler) + : type_(type), compiler_(std::move(compiler)){}; CompilerBackend::~CompilerBackend() = default; @@ -80,5 +82,9 @@ CompilerBackend::operator bool() const { return !!GetCompiler(); } +CompilerBackend::Type CompilerBackend::GetType() const { + return type_; +} + } // namespace compiler } // namespace impeller diff --git a/impeller/compiler/compiler_backend.h b/impeller/compiler/compiler_backend.h index 14686b748fc6f..e3c2789fc947f 100644 --- a/impeller/compiler/compiler_backend.h +++ b/impeller/compiler/compiler_backend.h @@ -20,16 +20,23 @@ struct CompilerBackend { using GLSLCompiler = std::shared_ptr; using Compiler = std::variant; + enum class Type { + kMSL, + kGLSL, + }; + CompilerBackend(MSLCompiler compiler); CompilerBackend(GLSLCompiler compiler); - CompilerBackend(Compiler compiler); + CompilerBackend(Type type, Compiler compiler); CompilerBackend(); ~CompilerBackend(); + Type GetType() const; + const spirv_cross::Compiler* operator->() const; operator bool() const; @@ -46,6 +53,7 @@ struct CompilerBackend { spirv_cross::Compiler* GetCompiler(); private: + Type type_ = Type::kMSL; Compiler compiler_; const spirv_cross::CompilerMSL* GetMSLCompiler() const; diff --git a/impeller/compiler/compiler_test.cc b/impeller/compiler/compiler_test.cc index c2ead9c7d961c..b1d7f9b7f1b46 100644 --- a/impeller/compiler/compiler_test.cc +++ b/impeller/compiler/compiler_test.cc @@ -70,7 +70,7 @@ bool CompilerTest::CanCompileAndReflect(const char* fixture_name) const { source_options.working_directory = std::make_shared( flutter::testing::OpenFixturesDirectory()); source_options.entry_point_name = EntryPointFunctionNameFromSourceName( - fixture_name, SourceTypeFromFileName(fixture_name), GetParam()); + fixture_name, SourceTypeFromFileName(fixture_name)); Reflector::Options reflector_options; reflector_options.header_file_name = ReflectionHeaderName(fixture_name); diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index 8482a7613a44f..af47e577c7c1f 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -48,10 +48,10 @@ bool Main(const fml::CommandLine& command_line) { options.defines = switches.defines; options.entry_point_name = EntryPointFunctionNameFromSourceName( switches.source_file_name, - SourceTypeFromFileName(switches.source_file_name), - switches.target_platform); + SourceTypeFromFileName(switches.source_file_name)); Reflector::Options reflector_options; + reflector_options.entry_point_name = options.entry_point_name; reflector_options.shader_name = InferShaderNameFromPath(switches.source_file_name); reflector_options.header_file_name = diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 6fca48dbf763d..fb7a8ac391fc5 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -157,7 +157,7 @@ std::optional Reflector::GenerateTemplateArguments() const { } { - root["entrypoint"] = entrypoints.front().name; + root["entrypoint"] = options_.entry_point_name; root["shader_name"] = options_.shader_name; root["shader_stage"] = ExecutionModelToString(entrypoints.front().execution_model); @@ -275,6 +275,16 @@ std::shared_ptr Reflector::GenerateReflectionCC() const { return InflateTemplate(kReflectionCCTemplate); } +static std::string ToString(CompilerBackend::Type type) { + switch (type) { + case CompilerBackend::Type::kMSL: + return "Metal Shading Language"; + case CompilerBackend::Type::kGLSL: + return "OpenGL Shading Language"; + } + FML_UNREACHABLE(); +} + std::shared_ptr Reflector::InflateTemplate( const std::string_view& tmpl) const { inja::Environment env; @@ -289,6 +299,11 @@ std::shared_ptr Reflector::InflateTemplate( return StringToShaderStage(args.at(0u)->get()); }); + env.add_callback("get_generator_name", 0u, + [type = compiler_.GetType()](inja::Arguments& args) { + return ToString(type); + }); + auto inflated_template = std::make_shared(env.render(tmpl, *template_arguments_)); @@ -332,6 +347,18 @@ std::optional Reflector::ReflectType( result["bit_width"] = type.width; result["vec_size"] = type.vecsize; result["columns"] = type.columns; + auto& members = result["members"] = nlohmann::json::array_t{}; + if (type.basetype == spirv_cross::SPIRType::BaseType::Struct) { + for (const auto& struct_member : ReadStructMembers(type_id)) { + auto member = nlohmann::json::object_t{}; + member["name"] = struct_member.name; + member["type"] = struct_member.type; + member["base_type"] = struct_member.base_type; + member["offset"] = struct_member.offset; + member["size"] = struct_member.byte_length; + members.emplace_back(std::move(member)); + } + } return result; } @@ -429,11 +456,12 @@ 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{ - .type = TypeNameWithPaddingOfSize(alignment_pad), - .name = SPrintF("_PADDING_%s_", - GetMemberNameAtIndex(struct_type, i).c_str()), - .offset = current_byte_offset, - .byte_length = alignment_pad, + TypeNameWithPaddingOfSize(alignment_pad), // type + BaseTypeToString(spirv_cross::SPIRType::BaseType::Void), // basetype + SPrintF("_PADDING_%s_", + GetMemberNameAtIndex(struct_type, i).c_str()), // name + current_byte_offset, // offset + alignment_pad // byte_length }); current_byte_offset += alignment_pad; } @@ -452,10 +480,11 @@ std::vector Reflector::ReadStructMembers( member.vecsize == 4 // ) { result.emplace_back(StructMember{ - .type = "Matrix", - .name = GetMemberNameAtIndex(struct_type, i), - .offset = struct_member_offset, - .byte_length = sizeof(Matrix), + "Matrix", // type + BaseTypeToString(member.basetype), // basetype + GetMemberNameAtIndex(struct_type, i), // name + struct_member_offset, // offset + sizeof(Matrix) // byte_length }); current_byte_offset += sizeof(Matrix); continue; @@ -468,10 +497,11 @@ std::vector Reflector::ReadStructMembers( member.vecsize == 2 // ) { result.emplace_back(StructMember{ - .type = "Point", - .name = GetMemberNameAtIndex(struct_type, i), - .offset = struct_member_offset, - .byte_length = sizeof(Point), + "Point", // type + BaseTypeToString(member.basetype), // basetype + GetMemberNameAtIndex(struct_type, i), // name + struct_member_offset, // offset + sizeof(Point) // byte_length }); current_byte_offset += sizeof(Point); continue; @@ -484,10 +514,11 @@ std::vector Reflector::ReadStructMembers( member.vecsize == 3 // ) { result.emplace_back(StructMember{ - .type = "Vector3", - .name = GetMemberNameAtIndex(struct_type, i), - .offset = struct_member_offset, - .byte_length = sizeof(Vector3), + "Vector3", // type + BaseTypeToString(member.basetype), // basetype + GetMemberNameAtIndex(struct_type, i), // name + struct_member_offset, // offset + sizeof(Vector3) // byte_length }); current_byte_offset += sizeof(Vector3); continue; @@ -500,10 +531,11 @@ std::vector Reflector::ReadStructMembers( member.vecsize == 4 // ) { result.emplace_back(StructMember{ - .type = "Vector4", - .name = GetMemberNameAtIndex(struct_type, i), - .offset = struct_member_offset, - .byte_length = sizeof(Vector4), + "Vector4", // type + BaseTypeToString(member.basetype), // basetype + GetMemberNameAtIndex(struct_type, i), // name + struct_member_offset, // offset + sizeof(Vector4) // byte_length }); current_byte_offset += sizeof(Vector4); continue; @@ -518,10 +550,11 @@ std::vector Reflector::ReadStructMembers( ) { // Add the type directly. result.emplace_back(StructMember{ - .type = maybe_known_type.value().name, - .name = GetMemberNameAtIndex(struct_type, i), - .offset = struct_member_offset, - .byte_length = maybe_known_type.value().byte_size, + maybe_known_type.value().name, // type + BaseTypeToString(member.basetype), // basetype + GetMemberNameAtIndex(struct_type, i), // name + struct_member_offset, // offset + maybe_known_type.value().byte_size // byte_length }); current_byte_offset += maybe_known_type.value().byte_size; continue; @@ -534,10 +567,11 @@ std::vector Reflector::ReadStructMembers( const size_t byte_length = (member.width * member.columns * member.vecsize) / 8u; result.emplace_back(StructMember{ - .type = TypeNameWithPaddingOfSize(byte_length), - .name = GetMemberNameAtIndex(struct_type, i), - .offset = struct_member_offset, - .byte_length = byte_length, + TypeNameWithPaddingOfSize(byte_length), // type + BaseTypeToString(member.basetype), // basetype + GetMemberNameAtIndex(struct_type, i), // name + struct_member_offset, // offset + byte_length // byte_length }); current_byte_offset += byte_length; continue; @@ -551,10 +585,12 @@ std::vector Reflector::ReadStructMembers( if (excess != 0) { const auto padding = max_member_alignment - excess; result.emplace_back(StructMember{ - .type = TypeNameWithPaddingOfSize(padding), - .name = "_PADDING_", - .offset = current_byte_offset, - .byte_length = padding, + TypeNameWithPaddingOfSize(padding), // type + BaseTypeToString( + spirv_cross::SPIRType::BaseType::Void), // basetype + "_PADDING_", // name + current_byte_offset, // offset + padding // byte_length }); } } @@ -595,6 +631,7 @@ nlohmann::json::object_t Reflector::EmitStructDefinition( auto& member = members.emplace_back(nlohmann::json::object_t{}); member["name"] = struc_member.name; member["type"] = struc_member.type; + member["base_type"] = struc_member.base_type; member["offset"] = struc_member.offset; member["byte_length"] = struc_member.byte_length; } @@ -603,6 +640,7 @@ nlohmann::json::object_t Reflector::EmitStructDefinition( struct VertexType { std::string type_name; + std::string base_type_name; std::string variable_name; size_t byte_length = 0u; }; @@ -612,7 +650,8 @@ static VertexType VertexTypeFromInputResource( const spirv_cross::Resource* resource) { VertexType result; result.variable_name = resource->name; - auto type = compiler.get_type(resource->type_id); + const auto type = compiler.get_type(resource->type_id); + result.base_type_name = BaseTypeToString(type.basetype); const auto total_size = type.columns * type.vecsize * type.width / 8u; result.byte_length = total_size; @@ -700,11 +739,13 @@ Reflector::ReflectPerVertexStructDefinition( const auto vertex_type = VertexTypeFromInputResource(*compiler_.GetCompiler(), resource); - StructMember member; - member.name = vertex_type.variable_name; - member.type = vertex_type.type_name; - member.byte_length = vertex_type.byte_length; - member.offset = struc.byte_length; + 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 // byte_length + }; 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 cacaa7c4ea5c3..4e14d5157a526 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -19,14 +19,27 @@ namespace compiler { struct StructMember { std::string type; + std::string base_type; std::string name; size_t offset = 0u; size_t byte_length = 0u; + + StructMember(std::string p_type, + std::string p_base_type, + std::string p_name, + size_t p_offset, + size_t p_byte_length) + : type(std::move(p_type)), + base_type(std::move(p_base_type)), + name(std::move(p_name)), + offset(p_offset), + byte_length(p_byte_length) {} }; class Reflector { public: struct Options { + std::string entry_point_name; std::string shader_name; std::string header_file_name; }; diff --git a/impeller/compiler/types.cc b/impeller/compiler/types.cc index f8a03459ecd00..3cc5130a2fb45 100644 --- a/impeller/compiler/types.cc +++ b/impeller/compiler/types.cc @@ -55,9 +55,8 @@ std::string TargetPlatformToString(TargetPlatform platform) { FML_UNREACHABLE(); } -static std::string UniqueEntryPointFunctionNameFromSourceName( - const std::string& file_name, - SourceType type) { +std::string EntryPointFunctionNameFromSourceName(const std::string& file_name, + SourceType type) { std::stringstream stream; std::filesystem::path file_path(file_name); stream << ToUtf8(file_path.stem().native()) << "_"; @@ -76,22 +75,6 @@ static std::string UniqueEntryPointFunctionNameFromSourceName( return stream.str(); } -std::string EntryPointFunctionNameFromSourceName(const std::string& file_name, - SourceType type, - TargetPlatform platform) { - switch (platform) { - case TargetPlatform::kMetalDesktop: - case TargetPlatform::kMetalIOS: - return UniqueEntryPointFunctionNameFromSourceName(file_name, type); - case TargetPlatform::kUnknown: - case TargetPlatform::kFlutterSPIRV: - case TargetPlatform::kOpenGLES: - case TargetPlatform::kOpenGLDesktop: - return "main"; - } - FML_UNREACHABLE(); -} - bool TargetPlatformNeedsSL(TargetPlatform platform) { switch (platform) { case TargetPlatform::kMetalIOS: diff --git a/impeller/compiler/types.h b/impeller/compiler/types.h index 5ad762e0a31c7..e714463810f78 100644 --- a/impeller/compiler/types.h +++ b/impeller/compiler/types.h @@ -40,8 +40,7 @@ std::string TargetPlatformToString(TargetPlatform platform); std::string TargetPlatformSLExtension(TargetPlatform platform); std::string EntryPointFunctionNameFromSourceName(const std::string& file_name, - SourceType type, - TargetPlatform platform); + SourceType type); bool TargetPlatformNeedsSL(TargetPlatform platform); diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index f7a584f4af126..b838a47460e22 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -52,7 +52,6 @@ ContentContext::ContentContext(std::shared_ptr context) std::move(color_attachments)); clip_pipelines_[{}] = std::make_unique(*context_, clip_pipeline_descriptor); - } else { return; } @@ -93,7 +92,7 @@ std::shared_ptr ContentContext::MakeSubpass( return nullptr; } - if (!sub_renderpass->EncodeCommands(*context->GetTransientsAllocator())) { + if (!sub_renderpass->EncodeCommands(context->GetTransientsAllocator())) { return nullptr; } diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index 4fc0fa74ad512..9898b217add98 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -11,25 +11,25 @@ #include "flutter/fml/macros.h" #include "fml/logging.h" #include "impeller/base/validation.h" +#include "impeller/entity/border_mask_blur.frag.h" +#include "impeller/entity/border_mask_blur.vert.h" #include "impeller/entity/entity.h" -#include "impeller/entity/mtl/border_mask_blur.frag.h" -#include "impeller/entity/mtl/border_mask_blur.vert.h" -#include "impeller/entity/mtl/gaussian_blur.frag.h" -#include "impeller/entity/mtl/gaussian_blur.vert.h" -#include "impeller/entity/mtl/glyph_atlas.frag.h" -#include "impeller/entity/mtl/glyph_atlas.vert.h" -#include "impeller/entity/mtl/gradient_fill.frag.h" -#include "impeller/entity/mtl/gradient_fill.vert.h" -#include "impeller/entity/mtl/solid_fill.frag.h" -#include "impeller/entity/mtl/solid_fill.vert.h" -#include "impeller/entity/mtl/solid_stroke.frag.h" -#include "impeller/entity/mtl/solid_stroke.vert.h" -#include "impeller/entity/mtl/texture_blend.frag.h" -#include "impeller/entity/mtl/texture_blend.vert.h" -#include "impeller/entity/mtl/texture_blend_screen.frag.h" -#include "impeller/entity/mtl/texture_blend_screen.vert.h" -#include "impeller/entity/mtl/texture_fill.frag.h" -#include "impeller/entity/mtl/texture_fill.vert.h" +#include "impeller/entity/gaussian_blur.frag.h" +#include "impeller/entity/gaussian_blur.vert.h" +#include "impeller/entity/glyph_atlas.frag.h" +#include "impeller/entity/glyph_atlas.vert.h" +#include "impeller/entity/gradient_fill.frag.h" +#include "impeller/entity/gradient_fill.vert.h" +#include "impeller/entity/solid_fill.frag.h" +#include "impeller/entity/solid_fill.vert.h" +#include "impeller/entity/solid_stroke.frag.h" +#include "impeller/entity/solid_stroke.vert.h" +#include "impeller/entity/texture_blend.frag.h" +#include "impeller/entity/texture_blend.vert.h" +#include "impeller/entity/texture_blend_screen.frag.h" +#include "impeller/entity/texture_blend_screen.vert.h" +#include "impeller/entity/texture_fill.frag.h" +#include "impeller/entity/texture_fill.vert.h" #include "impeller/renderer/formats.h" namespace impeller { diff --git a/impeller/entity/contents/solid_stroke_contents.h b/impeller/entity/contents/solid_stroke_contents.h index 8b38657141e7d..a4d5722b9abf4 100644 --- a/impeller/entity/contents/solid_stroke_contents.h +++ b/impeller/entity/contents/solid_stroke_contents.h @@ -10,7 +10,7 @@ #include "flutter/fml/macros.h" #include "impeller/entity/contents/contents.h" -#include "impeller/entity/mtl/solid_stroke.vert.h" +#include "impeller/entity/solid_stroke.vert.h" #include "impeller/geometry/color.h" #include "impeller/geometry/path_component.h" #include "impeller/geometry/point.h" diff --git a/impeller/entity/contents/texture_contents.cc b/impeller/entity/contents/texture_contents.cc index ffc926b6b7edc..7aaa6ff99f24f 100644 --- a/impeller/entity/contents/texture_contents.cc +++ b/impeller/entity/contents/texture_contents.cc @@ -6,8 +6,8 @@ #include "impeller/entity/contents/content_context.h" #include "impeller/entity/entity.h" -#include "impeller/entity/mtl/texture_fill.frag.h" -#include "impeller/entity/mtl/texture_fill.vert.h" +#include "impeller/entity/texture_fill.frag.h" +#include "impeller/entity/texture_fill.vert.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/sampler_library.h" #include "impeller/tessellator/tessellator.h" diff --git a/impeller/entity/entity_pass.cc b/impeller/entity/entity_pass.cc index 07ffc248f1143..71e48282fac29 100644 --- a/impeller/entity/entity_pass.cc +++ b/impeller/entity/entity_pass.cc @@ -217,7 +217,7 @@ bool EntityPass::Render(ContentContext& renderer, return false; } - if (!sub_renderpass->EncodeCommands(*context->GetTransientsAllocator())) { + if (!sub_renderpass->EncodeCommands(context->GetTransientsAllocator())) { return false; } diff --git a/impeller/fixtures/box_fade.frag b/impeller/fixtures/box_fade.frag index 47a184644de90..6e6b66bae1f0b 100644 --- a/impeller/fixtures/box_fade.frag +++ b/impeller/fixtures/box_fade.frag @@ -6,7 +6,7 @@ uniform FrameInfo { float current_time; vec2 cursor_position; vec2 window_size; -} frame; +} frame_info; in vec2 interporlated_texture_coordinates; @@ -18,5 +18,5 @@ uniform sampler2D contents2; void main() { vec4 tex1 = texture(contents1, interporlated_texture_coordinates); vec4 tex2 = texture(contents2, interporlated_texture_coordinates); - frag_color = mix(tex1, tex2, clamp(frame.cursor_position.x / frame.window_size.x, 0.0, 1.0)); + frag_color = mix(tex1, tex2, clamp(frame_info.cursor_position.x / frame_info.window_size.x, 0.0, 1.0)); } diff --git a/impeller/fixtures/box_fade.vert b/impeller/fixtures/box_fade.vert index 742d1e34c8a6b..ac3674d80fbc7 100644 --- a/impeller/fixtures/box_fade.vert +++ b/impeller/fixtures/box_fade.vert @@ -4,7 +4,7 @@ uniform UniformBuffer { mat4 mvp; -} uniforms; +} uniform_buffer; in vec3 vertex_position; in vec2 texture_coordinates; @@ -12,6 +12,6 @@ in vec2 texture_coordinates; out vec2 interporlated_texture_coordinates; void main() { - gl_Position = uniforms.mvp * vec4(vertex_position, 1.0); + gl_Position = uniform_buffer.mvp * vec4(vertex_position, 1.0); interporlated_texture_coordinates = texture_coordinates; } diff --git a/impeller/playground/BUILD.gn b/impeller/playground/BUILD.gn index dc4d452ecfb59..8cbf112c3a82e 100644 --- a/impeller/playground/BUILD.gn +++ b/impeller/playground/BUILD.gn @@ -17,6 +17,8 @@ impeller_component("playground") { "widgets.h", ] + deps = [ "../renderer/backend" ] + if (impeller_enable_metal) { sources += [ "backend/metal/playground_impl_mtl.h", diff --git a/impeller/playground/backend/gles/playground_impl_gles.cc b/impeller/playground/backend/gles/playground_impl_gles.cc index b5a47e2bc514b..020b87cdd43ad 100644 --- a/impeller/playground/backend/gles/playground_impl_gles.cc +++ b/impeller/playground/backend/gles/playground_impl_gles.cc @@ -3,35 +3,111 @@ // found in the LICENSE file. #include "impeller/playground/backend/gles/playground_impl_gles.h" + +#define GLFW_INCLUDE_NONE +#import "third_party/glfw/include/GLFW/glfw3.h" + +#include "flutter/fml/build_config.h" +#include "impeller/entity/gles/entity_shaders_gles.h" +#include "impeller/fixtures/gles/fixtures_shaders_gles.h" +#include "impeller/playground/imgui/gles/imgui_shaders_gles.h" #include "impeller/renderer/backend/gles/context_gles.h" +#include "impeller/renderer/backend/gles/surface_gles.h" namespace impeller { -PlaygroundImplGLES::PlaygroundImplGLES() = default; +void PlaygroundImplGLES::DestroyWindowHandle(WindowHandle handle) { + if (!handle) { + return; + } + ::glfwDestroyWindow(reinterpret_cast(handle)); +} + +PlaygroundImplGLES::PlaygroundImplGLES() + : handle_(nullptr, &DestroyWindowHandle) { + ::glfwDefaultWindowHints(); + +#if FML_OS_MACOSX + // ES Profiles are not supported on Mac. + ::glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); +#else // FML_OS_MACOSX + ::glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + ::glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + ::glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); +#endif // FML_OS_MACOSX + ::glfwWindowHint(GLFW_RED_BITS, 8); + ::glfwWindowHint(GLFW_GREEN_BITS, 8); + ::glfwWindowHint(GLFW_BLUE_BITS, 8); + ::glfwWindowHint(GLFW_ALPHA_BITS, 8); + ::glfwWindowHint(GLFW_DEPTH_BITS, 0); // no depth buffer + ::glfwWindowHint(GLFW_STENCIL_BITS, 8); // 8 bit stencil buffer + ::glfwWindowHint(GLFW_SAMPLES, 4); // 4xMSAA + + ::glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + + auto window = ::glfwCreateWindow(1, 1, "Test", nullptr, nullptr); + + ::glfwMakeContextCurrent(window); + + handle_.reset(window); +} PlaygroundImplGLES::~PlaygroundImplGLES() = default; -// |PlaygroundImpl| -std::shared_ptr PlaygroundImplGLES::CreateContext() const { - return std::make_shared(); +static std::vector> +ShaderLibraryMappingsForPlayground() { + return { + std::make_shared( + impeller_entity_shaders_gles_data, + impeller_entity_shaders_gles_length), + std::make_shared( + impeller_fixtures_shaders_gles_data, + impeller_fixtures_shaders_gles_length), + std::make_shared( + impeller_imgui_shaders_gles_data, impeller_imgui_shaders_gles_length), + }; } // |PlaygroundImpl| -bool PlaygroundImplGLES::SetupWindow(WindowHandle handle, - std::shared_ptr context) { - return true; +std::shared_ptr PlaygroundImplGLES::GetContext() const { + auto resolver = [](const char* name) -> void* { + return reinterpret_cast(::glfwGetProcAddress(name)); + }; + auto gl = std::make_unique(resolver); + if (!gl->IsValid()) { + FML_LOG(ERROR) << "Proc table when creating a playground was invalid."; + return nullptr; + } + + return ContextGLES::Create(std::move(gl), + ShaderLibraryMappingsForPlayground()); } // |PlaygroundImpl| -bool PlaygroundImplGLES::TeardownWindow(WindowHandle handle, - std::shared_ptr context) { - return true; +PlaygroundImpl::WindowHandle PlaygroundImplGLES::GetWindowHandle() { + return handle_.get(); } // |PlaygroundImpl| std::unique_ptr PlaygroundImplGLES::AcquireSurfaceFrame( std::shared_ptr context) { - return nullptr; + auto window = reinterpret_cast(GetWindowHandle()); + int width = 0; + int height = 0; + ::glfwGetFramebufferSize(window, &width, &height); + if (width <= 0 || height <= 0) { + return nullptr; + } + SurfaceGLES::SwapCallback swap_callback = [window]() -> bool { + ::glfwSwapBuffers(window); + return true; + }; + return SurfaceGLES::WrapFBO(std::move(context), // + swap_callback, // + 0u, // + PixelFormat::kR8G8B8A8UNormInt, // + ISize::MakeWH(width, height) // + ); } } // namespace impeller diff --git a/impeller/playground/backend/gles/playground_impl_gles.h b/impeller/playground/backend/gles/playground_impl_gles.h index 246d099ea188b..881fe61967bec 100644 --- a/impeller/playground/backend/gles/playground_impl_gles.h +++ b/impeller/playground/backend/gles/playground_impl_gles.h @@ -16,16 +16,15 @@ class PlaygroundImplGLES final : public PlaygroundImpl { ~PlaygroundImplGLES(); private: - // |PlaygroundImpl| - std::shared_ptr CreateContext() const override; + static void DestroyWindowHandle(WindowHandle handle); + using UniqueHandle = std::unique_ptr; + UniqueHandle handle_; // |PlaygroundImpl| - bool SetupWindow(WindowHandle handle, - std::shared_ptr context) override; + std::shared_ptr GetContext() const override; // |PlaygroundImpl| - bool TeardownWindow(WindowHandle handle, - std::shared_ptr context) override; + WindowHandle GetWindowHandle() override; // |PlaygroundImpl| std::unique_ptr AcquireSurfaceFrame( diff --git a/impeller/playground/backend/metal/playground_impl_mtl.h b/impeller/playground/backend/metal/playground_impl_mtl.h index 75f9ef5ba62de..9aa9da659e097 100644 --- a/impeller/playground/backend/metal/playground_impl_mtl.h +++ b/impeller/playground/backend/metal/playground_impl_mtl.h @@ -20,20 +20,19 @@ class PlaygroundImplMTL final : public PlaygroundImpl { private: struct Data; - WindowHandle handle_ = nullptr; + static void DestroyWindowHandle(WindowHandle handle); + using UniqueHandle = std::unique_ptr; + UniqueHandle handle_; + // To ensure that ObjC stuff doesn't leak into C++ TUs. std::unique_ptr data_; + std::shared_ptr context_; // |PlaygroundImpl| - std::shared_ptr CreateContext() const override; - - // |PlaygroundImpl| - bool SetupWindow(WindowHandle handle, - std::shared_ptr context) override; + std::shared_ptr GetContext() const override; // |PlaygroundImpl| - bool TeardownWindow(WindowHandle handle, - std::shared_ptr context) override; + WindowHandle GetWindowHandle() override; // |PlaygroundImpl| std::unique_ptr AcquireSurfaceFrame( diff --git a/impeller/playground/backend/metal/playground_impl_mtl.mm b/impeller/playground/backend/metal/playground_impl_mtl.mm index 9d81f562134cb..3bcd631c34cbc 100644 --- a/impeller/playground/backend/metal/playground_impl_mtl.mm +++ b/impeller/playground/backend/metal/playground_impl_mtl.mm @@ -41,44 +41,54 @@ }; } -PlaygroundImplMTL::PlaygroundImplMTL() : data_(std::make_unique()) {} - -PlaygroundImplMTL::~PlaygroundImplMTL() = default; - -std::shared_ptr PlaygroundImplMTL::CreateContext() const { - return ContextMTL::Create(ShaderLibraryMappingsForPlayground(), - "Playground Library"); +void PlaygroundImplMTL::DestroyWindowHandle(WindowHandle handle) { + if (!handle) { + return; + } + ::glfwDestroyWindow(reinterpret_cast(handle)); } -bool PlaygroundImplMTL::SetupWindow(WindowHandle handle, - std::shared_ptr context) { - if (handle_ != nullptr) { - return false; +PlaygroundImplMTL::PlaygroundImplMTL() + : handle_(nullptr, &DestroyWindowHandle), data_(std::make_unique()) { + ::glfwDefaultWindowHints(); + ::glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + ::glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + auto window = ::glfwCreateWindow(1, 1, "Test", nullptr, nullptr); + if (!window) { + return; + } + auto context = ContextMTL::Create(ShaderLibraryMappingsForPlayground(), + "Playground Library"); + if (!context) { + return; + } + NSWindow* cocoa_window = ::glfwGetCocoaWindow(window); + if (cocoa_window == nil) { + return; } - - handle_ = handle; - - NSWindow* cocoa_window = - ::glfwGetCocoaWindow(reinterpret_cast(handle_)); data_->metal_layer = [CAMetalLayer layer]; data_->metal_layer.device = ContextMTL::Cast(*context).GetMTLDevice(); // This pixel format is one of the documented supported formats. data_->metal_layer.pixelFormat = ToMTLPixelFormat(PixelFormat::kDefaultColor); cocoa_window.contentView.layer = data_->metal_layer; cocoa_window.contentView.wantsLayer = YES; - return true; + + handle_.reset(window); + context_ = std::move(context); } -bool PlaygroundImplMTL::TeardownWindow(WindowHandle handle, - std::shared_ptr context) { - if (handle_ != handle) { - return false; - } - handle_ = nullptr; - data_->metal_layer = nil; - return true; +PlaygroundImplMTL::~PlaygroundImplMTL() = default; + +std::shared_ptr PlaygroundImplMTL::GetContext() const { + return context_; +} + +// |PlaygroundImpl| +PlaygroundImpl::WindowHandle PlaygroundImplMTL::GetWindowHandle() { + return handle_.get(); } +// |PlaygroundImpl| std::unique_ptr PlaygroundImplMTL::AcquireSurfaceFrame( std::shared_ptr context) { if (!data_->metal_layer) { diff --git a/impeller/playground/imgui/imgui_impl_impeller.cc b/impeller/playground/imgui/imgui_impl_impeller.cc index 319bbf7b69a36..71055cf319fb7 100644 --- a/impeller/playground/imgui/imgui_impl_impeller.cc +++ b/impeller/playground/imgui/imgui_impl_impeller.cc @@ -11,8 +11,8 @@ #include "impeller/geometry/scalar.h" #include "impeller/geometry/vector.h" -#include "impeller/playground/imgui/mtl/imgui_raster.frag.h" -#include "impeller/playground/imgui/mtl/imgui_raster.vert.h" +#include "impeller/playground/imgui/imgui_raster.frag.h" +#include "impeller/playground/imgui/imgui_raster.vert.h" #include "third_party/imgui/imgui.h" #include "impeller/geometry/matrix.h" @@ -201,12 +201,10 @@ void ImGui_ImplImpeller_RenderDrawData(ImDrawData* draw_data, impeller::IPoint clip_max(pcmd->ClipRect.z - draw_data->DisplayPos.x, pcmd->ClipRect.w - draw_data->DisplayPos.y); // Ensure the scissor never goes out of bounds. - clip_min.x = std::clamp( - clip_min.x, 0ll, - static_cast(draw_data->DisplaySize.x)); - clip_min.y = std::clamp( - clip_min.y, 0ll, - static_cast(draw_data->DisplaySize.y)); + clip_min.x = std::clamp( + clip_min.x, 0ll, draw_data->DisplaySize.x); + clip_min.y = std::clamp( + clip_min.y, 0ll, draw_data->DisplaySize.y); if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) { continue; // Nothing to render. } @@ -217,9 +215,13 @@ void ImGui_ImplImpeller_RenderDrawData(ImDrawData* draw_data, cmd.viewport = viewport; cmd.scissor = impeller::IRect::MakeLTRB( - std::max(0ll, clip_min.x), std::max(0ll, clip_min.y), - std::min(render_pass.GetRenderTargetSize().width, clip_max.x), - std::min(render_pass.GetRenderTargetSize().height, clip_max.y)); + std::max(0ll, clip_min.x), // + std::max(0ll, clip_min.y), // + std::min( + render_pass.GetRenderTargetSize().width, clip_max.x), // + std::min( + render_pass.GetRenderTargetSize().height, clip_max.y) // + ); cmd.winding = impeller::WindingOrder::kClockwise; cmd.pipeline = bd->pipeline; diff --git a/impeller/playground/imgui/imgui_raster.vert b/impeller/playground/imgui/imgui_raster.vert index 3ff7dc704cc8e..8a103dc6bfeb5 100644 --- a/impeller/playground/imgui/imgui_raster.vert +++ b/impeller/playground/imgui/imgui_raster.vert @@ -5,7 +5,7 @@ uniform UniformBuffer { mat4 mvp; } -uniforms; +uniform_buffer; in vec2 vertex_position; in vec2 texture_coordinates; @@ -15,7 +15,7 @@ out vec2 frag_texture_coordinates; out vec4 frag_vertex_color; void main() { - gl_Position = uniforms.mvp * vec4(vertex_position.xy, 0.0, 1.0); + gl_Position = uniform_buffer.mvp * vec4(vertex_position.xy, 0.0, 1.0); frag_texture_coordinates = texture_coordinates; frag_vertex_color = vertex_color; } diff --git a/impeller/playground/playground.cc b/impeller/playground/playground.cc index f92aa0654dfe2..608c3447ff5ee 100644 --- a/impeller/playground/playground.cc +++ b/impeller/playground/playground.cc @@ -34,23 +34,89 @@ std::string PlaygroundBackendToString(PlaygroundBackend backend) { FML_UNREACHABLE(); } +struct Playground::GLFWInitializer { + GLFWInitializer() { + // This guard is a hack to work around a problem where glfwCreateWindow + // hangs when opening a second window after GLFW has been reinitialized (for + // example, when flipping through multiple playground tests). + // + // Explanation: + // * glfwCreateWindow calls [NSApp run], which begins running the event + // loop on the current thread. + // * GLFW then immediately stops the loop when + // applicationDidFinishLaunching is fired. + // * applicationDidFinishLaunching is only ever fired once during the + // application's lifetime, so subsequent calls to [NSApp run] will always + // hang with this setup. + // * glfwInit resets the flag that guards against [NSApp run] being + // called a second time, which causes the subsequent `glfwCreateWindow` + // to hang indefinitely in the event loop, because + // applicationDidFinishLaunching is never fired. + static std::once_flag sOnceInitializer; + std::call_once(sOnceInitializer, []() { + FML_CHECK(::glfwInit() == GLFW_TRUE); + ::glfwSetErrorCallback([](int code, const char* description) { + FML_LOG(ERROR) << "GLFW Error '" << description << "' (" << code + << ")."; + }); + }); + } +}; + Playground::Playground() - : impl_(PlaygroundImpl::Create(GetParam())), - renderer_(impl_->CreateContext()), - is_valid_(renderer_.IsValid()) {} + : glfw_initializer_(std::make_unique()) {} Playground::~Playground() = default; -bool Playground::IsValid() const { - return is_valid_; -} - PlaygroundBackend Playground::GetBackend() const { return GetParam(); } std::shared_ptr Playground::GetContext() const { - return IsValid() ? renderer_.GetContext() : nullptr; + return renderer_ ? renderer_->GetContext() : nullptr; +} + +static constexpr bool PlatformSupportsBackend(PlaygroundBackend backend) { + switch (backend) { + case PlaygroundBackend::kMetal: +#if IMPELLER_ENABLE_METAL + return true; +#else // IMPELLER_ENABLE_METAL + return false; +#endif // IMPELLER_ENABLE_METAL + case PlaygroundBackend::kOpenGLES: +#if IMPELLER_ENABLE_OPENGLES + return true; +#else // IMPELLER_ENABLE_OPENGLES + return false; +#endif // IMPELLER_ENABLE_OPENGLES + } + FML_UNREACHABLE(); +} + +void Playground::SetUp() { + if (!PlatformSupportsBackend(GetBackend())) { + GTEST_SKIP_("This backend is disabled or isn't supported on this platform"); + } + + impl_ = PlaygroundImpl::Create(GetParam()); + if (!impl_) { + return; + } + auto context = impl_->GetContext(); + if (!context) { + return; + } + auto renderer = std::make_unique(std::move(context)); + if (!renderer->IsValid()) { + return; + } + renderer_ = std::move(renderer); +} + +void Playground::TearDown() { + renderer_.reset(); + impl_.reset(); } static void PlaygroundKeyCallback(GLFWwindow* window, @@ -87,15 +153,11 @@ bool Playground::OpenPlaygroundHere(Renderer::RenderCallback render_callback) { return true; } - if (!IsValid()) { - return false; - } - if (!render_callback) { return true; } - if (!renderer_.IsValid()) { + if (!renderer_ || !renderer_->IsValid()) { return false; } @@ -106,40 +168,12 @@ bool Playground::OpenPlaygroundHere(Renderer::RenderCallback render_callback) { ImGui::StyleColorsDark(); ImGui::GetIO().IniFilename = nullptr; - // This guard is a hack to work around a problem where glfwCreateWindow - // hangs when opening a second window after GLFW has been reinitialized (for - // example, when flipping through multiple playground tests). - // - // Explanation: - // * glfwCreateWindow calls [NSApp run], which begins running the event loop - // on the current thread. - // * GLFW then immediately stops the loop when applicationDidFinishLaunching - // is fired. - // * applicationDidFinishLaunching is only ever fired once during the - // application's lifetime, so subsequent calls to [NSApp run] will always - // hang with this setup. - // * glfwInit resets the flag that guards against [NSApp run] being - // called a second time, which causes the subsequent `glfwCreateWindow` to - // hang indefinitely in the event loop, because - // applicationDidFinishLaunching is never fired. - static bool first_run = true; - if (first_run) { - first_run = false; - if (::glfwInit() != GLFW_TRUE) { - return false; - } - } - - ::glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - - auto window_title = GetWindowTitle(flutter::testing::GetCurrentTestName()); - auto window = - ::glfwCreateWindow(GetWindowSize().width, GetWindowSize().height, - window_title.c_str(), NULL, NULL); + auto window = reinterpret_cast(impl_->GetWindowHandle()); if (!window) { return false; } - + ::glfwSetWindowTitle( + window, GetWindowTitle(flutter::testing::GetCurrentTestName()).c_str()); ::glfwSetWindowUserPointer(window, this); ::glfwSetWindowSizeCallback( window, [](GLFWwindow* window, int width, int height) -> void { @@ -158,19 +192,15 @@ bool Playground::OpenPlaygroundHere(Renderer::RenderCallback render_callback) { ->SetCursorPosition({static_cast(x), static_cast(y)}); }); - fml::ScopedCleanupClosure close_window( - [window]() { ::glfwDestroyWindow(window); }); - ImGui_ImplGlfw_InitForOther(window, true); fml::ScopedCleanupClosure shutdown_imgui([]() { ImGui_ImplGlfw_Shutdown(); }); - ImGui_ImplImpeller_Init(renderer_.GetContext()); + ImGui_ImplImpeller_Init(renderer_->GetContext()); fml::ScopedCleanupClosure shutdown_imgui_impeller( []() { ImGui_ImplImpeller_Shutdown(); }); - if (!impl_->SetupWindow(window, renderer_.GetContext())) { - return false; - } + ::glfwSetWindowSize(window, GetWindowSize().width, GetWindowSize().height); + ::glfwShowWindow(window); while (true) { ::glfwWaitEventsTimeout(1.0 / 30.0); @@ -191,23 +221,21 @@ bool Playground::OpenPlaygroundHere(Renderer::RenderCallback render_callback) { return result; }; - if (!renderer_.Render(impl_->AcquireSurfaceFrame(renderer_.GetContext()), - wrapped_callback)) { + if (!renderer_->Render(impl_->AcquireSurfaceFrame(renderer_->GetContext()), + wrapped_callback)) { VALIDATION_LOG << "Could not render into the surface."; return false; } } - if (!impl_->TeardownWindow(window, renderer_.GetContext())) { - return false; - } + ::glfwHideWindow(window); return true; } std::shared_ptr Playground::CreateTextureForFixture( const char* fixture_name) const { - if (!IsValid()) { + if (!renderer_) { return nullptr; } @@ -235,7 +263,7 @@ std::shared_ptr Playground::CreateTextureForFixture( texture_descriptor.mip_count = 1u; auto texture = - renderer_.GetContext()->GetPermanentsAllocator()->CreateTexture( + renderer_->GetContext()->GetPermanentsAllocator()->CreateTexture( StorageMode::kHostVisible, texture_descriptor); if (!texture) { VALIDATION_LOG << "Could not allocate texture for fixture " << fixture_name; diff --git a/impeller/playground/playground.h b/impeller/playground/playground.h index dc382b0a456c4..66de6d5cc9954 100644 --- a/impeller/playground/playground.h +++ b/impeller/playground/playground.h @@ -30,9 +30,11 @@ class Playground : public ::testing::TestWithParam { static constexpr bool is_enabled() { return is_enabled_; } - PlaygroundBackend GetBackend() const; + void SetUp() override; + + void TearDown() override; - bool IsValid() const; + PlaygroundBackend GetBackend() const; Point GetCursorPosition() const; @@ -52,11 +54,12 @@ class Playground : public ::testing::TestWithParam { static const bool is_enabled_ = false; #endif // IMPELLER_ENABLE_PLAYGROUND + struct GLFWInitializer; + std::unique_ptr glfw_initializer_; std::unique_ptr impl_; - Renderer renderer_; + std::unique_ptr renderer_; Point cursor_position_; ISize window_size_ = ISize{1024, 768}; - bool is_valid_ = false; void SetCursorPosition(Point pos); @@ -67,7 +70,9 @@ class Playground : public ::testing::TestWithParam { #define INSTANTIATE_PLAYGROUND_SUITE(playground) \ INSTANTIATE_TEST_SUITE_P( \ - Play, playground, ::testing::Values(PlaygroundBackend::kMetal), \ + Play, playground, \ + ::testing::Values(PlaygroundBackend::kMetal, \ + PlaygroundBackend::kOpenGLES), \ [](const ::testing::TestParamInfo& info) { \ return PlaygroundBackendToString(info.param); \ }); diff --git a/impeller/playground/playground_impl.cc b/impeller/playground/playground_impl.cc index 276b0f9e6cb65..ba2f19788f05e 100644 --- a/impeller/playground/playground_impl.cc +++ b/impeller/playground/playground_impl.cc @@ -4,18 +4,31 @@ #include "impeller/playground/playground_impl.h" -#include "impeller/playground/backend/gles/playground_impl_gles.h" +#if IMPELLER_ENABLE_METAL #include "impeller/playground/backend/metal/playground_impl_mtl.h" +#endif // IMPELLER_ENABLE_METAL + +#if IMPELLER_ENABLE_OPENGLES +#include "impeller/playground/backend/gles/playground_impl_gles.h" +#endif // IMPELLER_ENABLE_OPENGLES namespace impeller { std::unique_ptr PlaygroundImpl::Create( PlaygroundBackend backend) { switch (backend) { +#if IMPELLER_ENABLE_METAL case PlaygroundBackend::kMetal: return std::make_unique(); +#endif // IMPELLER_ENABLE_METAL +#if IMPELLER_ENABLE_OPENGLES case PlaygroundBackend::kOpenGLES: return std::make_unique(); +#endif // IMPELLER_ENABLE_OPENGLES + default: + FML_CHECK(false) << "Attempted to create playground with backend that " + "isn't available or was disabled on this platform: " + << PlaygroundBackendToString(backend); } FML_UNREACHABLE(); } diff --git a/impeller/playground/playground_impl.h b/impeller/playground/playground_impl.h index c28376e3fe804..d15923b129190 100644 --- a/impeller/playground/playground_impl.h +++ b/impeller/playground/playground_impl.h @@ -19,15 +19,11 @@ class PlaygroundImpl { virtual ~PlaygroundImpl(); - virtual std::shared_ptr CreateContext() const = 0; - using WindowHandle = void*; - virtual bool SetupWindow(WindowHandle handle, - std::shared_ptr context) = 0; + virtual WindowHandle GetWindowHandle() = 0; - virtual bool TeardownWindow(WindowHandle handle, - std::shared_ptr context) = 0; + virtual std::shared_ptr GetContext() const = 0; virtual std::unique_ptr AcquireSurfaceFrame( std::shared_ptr context) = 0; diff --git a/impeller/renderer/BUILD.gn b/impeller/renderer/BUILD.gn index 1c0f72794a4fa..eed1223dd5c17 100644 --- a/impeller/renderer/BUILD.gn +++ b/impeller/renderer/BUILD.gn @@ -50,6 +50,8 @@ impeller_component("renderer") { "sampler_library.h", "shader_function.cc", "shader_function.h", + "shader_key.cc", + "shader_key.h", "shader_library.cc", "shader_library.h", "shader_types.cc", @@ -68,80 +70,6 @@ impeller_component("renderer") { "vertex_descriptor.h", ] - if (impeller_enable_metal) { - sources += [ - "backend/metal/allocator_mtl.h", - "backend/metal/allocator_mtl.mm", - "backend/metal/command_buffer_mtl.h", - "backend/metal/command_buffer_mtl.mm", - "backend/metal/context_mtl.h", - "backend/metal/context_mtl.mm", - "backend/metal/device_buffer_mtl.h", - "backend/metal/device_buffer_mtl.mm", - "backend/metal/formats_mtl.h", - "backend/metal/formats_mtl.mm", - "backend/metal/pipeline_library_mtl.h", - "backend/metal/pipeline_library_mtl.mm", - "backend/metal/pipeline_mtl.h", - "backend/metal/pipeline_mtl.mm", - "backend/metal/render_pass_mtl.h", - "backend/metal/render_pass_mtl.mm", - "backend/metal/sampler_library_mtl.h", - "backend/metal/sampler_library_mtl.mm", - "backend/metal/sampler_mtl.h", - "backend/metal/sampler_mtl.mm", - "backend/metal/shader_function_mtl.h", - "backend/metal/shader_function_mtl.mm", - "backend/metal/shader_library_mtl.h", - "backend/metal/shader_library_mtl.mm", - "backend/metal/surface_mtl.h", - "backend/metal/surface_mtl.mm", - "backend/metal/texture_mtl.h", - "backend/metal/texture_mtl.mm", - "backend/metal/vertex_descriptor_mtl.h", - "backend/metal/vertex_descriptor_mtl.mm", - ] - } - - if (impeller_enable_opengles) { - sources += [ - "backend/gles/allocator_gles.cc", - "backend/gles/allocator_gles.h", - "backend/gles/command_buffer_gles.cc", - "backend/gles/command_buffer_gles.h", - "backend/gles/context_gles.cc", - "backend/gles/context_gles.h", - "backend/gles/device_buffer_gles.cc", - "backend/gles/device_buffer_gles.h", - "backend/gles/formats_gles.cc", - "backend/gles/formats_gles.h", - "backend/gles/pipeline_gles.cc", - "backend/gles/pipeline_gles.h", - "backend/gles/pipeline_library_gles.cc", - "backend/gles/pipeline_library_gles.h", - "backend/gles/proc_table_gles.cc", - "backend/gles/proc_table_gles.h", - "backend/gles/reactor_gles.cc", - "backend/gles/reactor_gles.h", - "backend/gles/render_pass_gles.cc", - "backend/gles/render_pass_gles.h", - "backend/gles/sampler_gles.cc", - "backend/gles/sampler_gles.h", - "backend/gles/sampler_library_gles.cc", - "backend/gles/sampler_library_gles.h", - "backend/gles/shader_function_gles.cc", - "backend/gles/shader_function_gles.h", - "backend/gles/shader_library_gles.cc", - "backend/gles/shader_library_gles.h", - "backend/gles/surface_gles.cc", - "backend/gles/surface_gles.h", - "backend/gles/texture_gles.cc", - "backend/gles/texture_gles.h", - "backend/gles/vertex_descriptor_gles.cc", - "backend/gles/vertex_descriptor_gles.h", - ] - } - public_deps = [ "../base", "../geometry", @@ -150,14 +78,9 @@ impeller_component("renderer") { ] deps = [ "//flutter/fml" ] - frameworks = [] - - if (impeller_enable_metal) { - frameworks = [ "Metal.framework" ] - } } -source_set("renderer_unittests") { +impeller_component("renderer_unittests") { testonly = true sources = [ diff --git a/impeller/renderer/allocator.cc b/impeller/renderer/allocator.cc index 74d9e1d4bc812..8151eec3492fb 100644 --- a/impeller/renderer/allocator.cc +++ b/impeller/renderer/allocator.cc @@ -4,6 +4,9 @@ #include "impeller/renderer/allocator.h" +#include "impeller/renderer/device_buffer.h" +#include "impeller/renderer/range.h" + namespace impeller { Allocator::Allocator() = default; @@ -24,4 +27,27 @@ bool Allocator::RequiresExplicitHostSynchronization(StorageMode mode) { #endif // FML_OS_IOS } +std::shared_ptr Allocator::CreateBufferWithCopy( + const uint8_t* buffer, + size_t length) { + auto new_buffer = CreateBuffer(StorageMode::kHostVisible, length); + + if (!new_buffer) { + return nullptr; + } + + auto entire_range = Range{0, length}; + + if (!new_buffer->CopyHostBuffer(buffer, entire_range)) { + return nullptr; + } + + return new_buffer; +} + +std::shared_ptr Allocator::CreateBufferWithCopy( + const fml::Mapping& mapping) { + return CreateBufferWithCopy(mapping.GetMapping(), mapping.GetSize()); +} + } // namespace impeller diff --git a/impeller/renderer/allocator.h b/impeller/renderer/allocator.h index 56d0483d759ac..10e1eb3f0aaeb 100644 --- a/impeller/renderer/allocator.h +++ b/impeller/renderer/allocator.h @@ -63,12 +63,11 @@ class Allocator { StorageMode mode, const TextureDescriptor& desc) = 0; - virtual std::shared_ptr CreateBufferWithCopy( - const uint8_t* buffer, - size_t length) = 0; + std::shared_ptr CreateBufferWithCopy(const uint8_t* buffer, + size_t length); - virtual std::shared_ptr CreateBufferWithCopy( - const fml::Mapping& mapping) = 0; + std::shared_ptr CreateBufferWithCopy( + const fml::Mapping& mapping); static bool RequiresExplicitHostSynchronization(StorageMode mode); diff --git a/impeller/renderer/backend/BUILD.gn b/impeller/renderer/backend/BUILD.gn new file mode 100644 index 0000000000000..4ce813cfe892e --- /dev/null +++ b/impeller/renderer/backend/BUILD.gn @@ -0,0 +1,26 @@ +# 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. + +import("../../tools/impeller.gni") + +group("backend") { + public_deps = [] + + if (impeller_enable_metal) { + public_deps += [ "metal" ] + } + + if (impeller_enable_opengles) { + public_deps += [ "gles" ] + } +} + +group("backend_unittests") { + testonly = true + + deps = [] + if (impeller_enable_opengles) { + deps += [ "gles:gles_unittests" ] + } +} diff --git a/impeller/renderer/backend/gles/BUILD.gn b/impeller/renderer/backend/gles/BUILD.gn new file mode 100644 index 0000000000000..bb9328061370d --- /dev/null +++ b/impeller/renderer/backend/gles/BUILD.gn @@ -0,0 +1,79 @@ +# 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. + +import("../../../tools/impeller.gni") + +config("gles_config") { + # Generic GL/GLES/EGL/Vulkan headers. Any will do. We just pick one from Angle + # because they are there. + include_dirs = [ "//third_party/angle/include" ] +} + +impeller_component("gles") { + public_configs = [ ":gles_config" ] + + sources = [ + "allocator_gles.cc", + "allocator_gles.h", + "buffer_bindings_gles.cc", + "buffer_bindings_gles.h", + "command_buffer_gles.cc", + "command_buffer_gles.h", + "context_gles.cc", + "context_gles.h", + "device_buffer_gles.cc", + "device_buffer_gles.h", + "formats_gles.cc", + "formats_gles.h", + "gl_description.cc", + "gl_description.h", + "gles.h", + "gles_handle.cc", + "gles_handle.h", + "pipeline_gles.cc", + "pipeline_gles.h", + "pipeline_library_gles.cc", + "pipeline_library_gles.h", + "proc_table_gles.cc", + "proc_table_gles.h", + "reactor_gles.cc", + "reactor_gles.h", + "render_pass_gles.cc", + "render_pass_gles.h", + "sampler_gles.cc", + "sampler_gles.h", + "sampler_library_gles.cc", + "sampler_library_gles.h", + "shader_function_gles.cc", + "shader_function_gles.h", + "shader_library_gles.cc", + "shader_library_gles.h", + "surface_gles.cc", + "surface_gles.h", + "texture_gles.cc", + "texture_gles.h", + ] + + public_deps = [ + "../../:renderer", + "../../../blobcat:blobcat_lib", + "//flutter/fml", + ] +} + +impeller_component("gles_unittests") { + testonly = true + + sources = [ + "gles_test.cc", + "gles_test.h", + "gles_unittests.cc", + ] + + deps = [ + ":gles", + "//flutter/testing", + "//third_party/swiftshader_flutter:swiftshader_gl", + ] +} diff --git a/impeller/renderer/backend/gles/allocator_gles.cc b/impeller/renderer/backend/gles/allocator_gles.cc index d4f2c4e726ce8..52b3ede4a447c 100644 --- a/impeller/renderer/backend/gles/allocator_gles.cc +++ b/impeller/renderer/backend/gles/allocator_gles.cc @@ -4,8 +4,34 @@ #include "impeller/renderer/backend/gles/allocator_gles.h" +#include "impeller/base/config.h" +#include "impeller/renderer/backend/gles/device_buffer_gles.h" +#include "impeller/renderer/backend/gles/texture_gles.h" + namespace impeller { -// +AllocatorGLES::AllocatorGLES(ReactorGLES::Ref reactor) + : reactor_(std::move(reactor)), is_valid_(true) {} + +// |Allocator| +AllocatorGLES::~AllocatorGLES() = default; + +// |Allocator| +bool AllocatorGLES::IsValid() const { + return is_valid_; +} + +// |Allocator| +std::shared_ptr AllocatorGLES::CreateBuffer(StorageMode mode, + size_t length) { + return std::make_shared(reactor_, length, mode); +} + +// |Allocator| +std::shared_ptr AllocatorGLES::CreateTexture( + StorageMode mode, + const TextureDescriptor& desc) { + return std::make_shared(reactor_, std::move(desc)); +} } // namespace impeller diff --git a/impeller/renderer/backend/gles/allocator_gles.h b/impeller/renderer/backend/gles/allocator_gles.h index 9a4cdf09172e8..7b322938d351b 100644 --- a/impeller/renderer/backend/gles/allocator_gles.h +++ b/impeller/renderer/backend/gles/allocator_gles.h @@ -6,6 +6,7 @@ #include "flutter/fml/macros.h" #include "impeller/renderer/allocator.h" +#include "impeller/renderer/backend/gles/reactor_gles.h" namespace impeller { @@ -17,7 +18,10 @@ class AllocatorGLES final : public Allocator { private: friend class ContextGLES; - AllocatorGLES(); + ReactorGLES::Ref reactor_; + bool is_valid_ = false; + + AllocatorGLES(ReactorGLES::Ref reactor); // |Allocator| bool IsValid() const; @@ -31,14 +35,6 @@ class AllocatorGLES final : public Allocator { StorageMode mode, const TextureDescriptor& desc) override; - // |Allocator| - std::shared_ptr CreateBufferWithCopy(const uint8_t* buffer, - size_t length) override; - - // |Allocator| - std::shared_ptr CreateBufferWithCopy( - const fml::Mapping& mapping) override; - FML_DISALLOW_COPY_AND_ASSIGN(AllocatorGLES); }; diff --git a/impeller/renderer/backend/gles/buffer_bindings_gles.cc b/impeller/renderer/backend/gles/buffer_bindings_gles.cc new file mode 100644 index 0000000000000..5c5a36c448958 --- /dev/null +++ b/impeller/renderer/backend/gles/buffer_bindings_gles.cc @@ -0,0 +1,254 @@ +// 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/renderer/backend/gles/buffer_bindings_gles.h" + +#include + +#include "impeller/base/config.h" +#include "impeller/base/validation.h" +#include "impeller/renderer/backend/gles/device_buffer_gles.h" +#include "impeller/renderer/backend/gles/formats_gles.h" + +namespace impeller { + +BufferBindingsGLES::BufferBindingsGLES() = default; + +BufferBindingsGLES::~BufferBindingsGLES() = default; + +bool BufferBindingsGLES::RegisterVertexStageInput( + const ProcTableGLES& gl, + const std::vector& inputs) { + std::vector vertex_attrib_arrays; + size_t offset = 0u; + for (const auto& input : inputs) { + VertexAttribPointer attrib; + attrib.index = input.location; + // Component counts must be 1, 2, 3 or 4. Do that validation now. + if (input.vec_size < 1u || input.vec_size > 4u) { + return false; + } + attrib.size = input.vec_size; + auto type = ToVertexAttribType(input.type); + if (!type.has_value()) { + return false; + } + attrib.type = type.value(); + attrib.normalized = GL_FALSE; + attrib.offset = offset; + offset += (input.bit_width * input.vec_size) / 8; + vertex_attrib_arrays.emplace_back(std::move(attrib)); + } + for (auto& array : vertex_attrib_arrays) { + array.stride = offset; + } + vertex_attrib_arrays_ = std::move(vertex_attrib_arrays); + return true; +} + +static std::string NormalizeUniformKey(const std::string& key) { + std::stringstream stream; + for (size_t i = 0, count = key.length(); i < count; i++) { + auto ch = key.data()[i]; + if (ch == '_') { + continue; + } + stream << static_cast(std::toupper(ch)); + } + return stream.str(); +} + +static std::string CreateUnifiormMemberKey(const std::string& struct_name, + const std::string& member) { + std::stringstream stream; + stream << struct_name << "." << member; + return NormalizeUniformKey(stream.str()); +} + +bool BufferBindingsGLES::ReadUniformsBindings(const ProcTableGLES& gl, + GLuint program) { + if (!gl.IsProgram(program)) { + return false; + } + GLint max_name_size = 0; + gl.GetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_name_size); + + GLint uniform_count = 0; + gl.GetProgramiv(program, GL_ACTIVE_UNIFORMS, &uniform_count); + for (GLint i = 0; i < uniform_count; i++) { + std::vector name; + name.resize(max_name_size); + GLsizei written_count = 0u; + GLint uniform_var_size = 0u; + GLenum uniform_type = GL_FLOAT; + gl.GetActiveUniform(program, // program + i, // index + max_name_size, // buffer_size + &written_count, // length + &uniform_var_size, // size + &uniform_type, // type + name.data() // name + ); + auto location = gl.GetUniformLocation(program, name.data()); + if (location == -1) { + VALIDATION_LOG << "Could not query the location of an active uniform."; + return false; + } + if (written_count <= 0) { + VALIDATION_LOG << "Uniform name could not be read for active uniform."; + return false; + } + if (uniform_var_size != 1) { + VALIDATION_LOG << "Array uniform types are not supported."; + return false; + } + uniform_locations_[NormalizeUniformKey(std::string{ + name.data(), static_cast(written_count)})] = location; + } + return true; +} + +bool BufferBindingsGLES::BindVertexAttributes(const ProcTableGLES& gl, + size_t vertex_offset) const { + for (const auto& array : vertex_attrib_arrays_) { + gl.EnableVertexAttribArray(array.index); + gl.VertexAttribPointer(array.index, // index + array.size, // size (must be 1, 2, 3, or 4) + array.type, // type + array.normalized, // normalized + array.stride, // stride + reinterpret_cast(static_cast( + vertex_offset + array.offset)) // pointer + ); + } + + return true; +} + +bool BufferBindingsGLES::BindUniformData( + const ProcTableGLES& gl, + Allocator& transients_allocator, + const Bindings& vertex_bindings, + const Bindings& fragment_bindings) const { + for (const auto& buffer : vertex_bindings.buffers) { + if (!BindUniformBuffer(gl, transients_allocator, buffer.second)) { + return false; + } + } + for (const auto& buffer : fragment_bindings.buffers) { + if (!BindUniformBuffer(gl, transients_allocator, buffer.second)) { + return false; + } + } + return true; +} + +bool BufferBindingsGLES::UnbindVertexAttributes(const ProcTableGLES& gl) const { + for (const auto& array : vertex_attrib_arrays_) { + gl.DisableVertexAttribArray(array.index); + } + return true; +} + +bool BufferBindingsGLES::BindUniformBuffer(const ProcTableGLES& gl, + Allocator& transients_allocator, + const BufferResource& buffer) const { + const auto* metadata = buffer.isa; + if (metadata == nullptr) { + // Vertex buffer bindings don't have metadata as those definitions are + // already handled by vertex attrib pointers. Keep going. + return true; + } + + auto device_buffer = + buffer.resource.buffer->GetDeviceBuffer(transients_allocator); + if (!device_buffer) { + VALIDATION_LOG << "Device buffer not found."; + return false; + } + const auto& device_buffer_gles = DeviceBufferGLES::Cast(*device_buffer); + const uint8_t* buffer_ptr = device_buffer_gles.GetBufferData()->GetMapping() + + buffer.resource.range.offset; + + if (metadata->members.empty()) { + VALIDATION_LOG << "Uniform buffer had no members. This is currently " + "unsupported in the OpenGL ES backend. Use a uniform " + "buffer block."; + return false; + } + + for (const auto& member : metadata->members) { + if (member.type == ShaderType::kVoid) { + // Void types are used for padding. We are obviously not going to find + // mappings for these. Keep going. + continue; + } + const auto member_key = + CreateUnifiormMemberKey(metadata->name, member.name); + const auto location = uniform_locations_.find(member_key); + if (location == uniform_locations_.end()) { + VALIDATION_LOG << "Location for uniform member not known: " << member_key; + return false; + } + + switch (member.type) { + case ShaderType::kFloat: + switch (member.size) { + case sizeof(Matrix): + gl.UniformMatrix4fv(location->second, // location + 1u, // count + GL_FALSE, // normalize + reinterpret_cast( + buffer_ptr + member.offset) // data + ); + continue; + case sizeof(Vector4): + gl.Uniform4fv(location->second, // location + 1u, // count + reinterpret_cast( + buffer_ptr + member.offset) // data + ); + continue; + case sizeof(Vector2): + gl.Uniform2fv(location->second, // location + 1u, // count + reinterpret_cast( + buffer_ptr + member.offset) // data + ); + continue; + case sizeof(Scalar): + gl.Uniform1fv(location->second, // location + 1u, // count + reinterpret_cast( + buffer_ptr + member.offset) // data + ); + continue; + } + case ShaderType::kBoolean: + case ShaderType::kSignedByte: + case ShaderType::kUnsignedByte: + case ShaderType::kSignedShort: + case ShaderType::kUnsignedShort: + case ShaderType::kSignedInt: + case ShaderType::kUnsignedInt: + case ShaderType::kSignedInt64: + case ShaderType::kUnsignedInt64: + case ShaderType::kAtomicCounter: + case ShaderType::kUnknown: + case ShaderType::kVoid: + case ShaderType::kHalfFloat: + case ShaderType::kDouble: + case ShaderType::kStruct: + case ShaderType::kImage: + case ShaderType::kSampledImage: + case ShaderType::kSampler: + VALIDATION_LOG << "Could not bind uniform buffer data for key: " + << member_key; + return false; + } + } + return true; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/buffer_bindings_gles.h b/impeller/renderer/backend/gles/buffer_bindings_gles.h new file mode 100644 index 0000000000000..10565008499e1 --- /dev/null +++ b/impeller/renderer/backend/gles/buffer_bindings_gles.h @@ -0,0 +1,65 @@ +// 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 "impeller/renderer/backend/gles/gles.h" +#include "impeller/renderer/backend/gles/proc_table_gles.h" +#include "impeller/renderer/command.h" +#include "impeller/renderer/vertex_descriptor.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief Sets up stage bindings for single draw call in the OpenGLES +/// backend. +/// +class BufferBindingsGLES { + public: + BufferBindingsGLES(); + + ~BufferBindingsGLES(); + + bool RegisterVertexStageInput(const ProcTableGLES& gl, + const std::vector& inputs); + + bool ReadUniformsBindings(const ProcTableGLES& gl, GLuint program); + + bool BindVertexAttributes(const ProcTableGLES& gl, + size_t vertex_offset) const; + + bool BindUniformData(const ProcTableGLES& gl, + Allocator& transients_allocator, + const Bindings& vertex_bindings, + const Bindings& fragment_bindings) const; + + bool UnbindVertexAttributes(const ProcTableGLES& gl) const; + + private: + //---------------------------------------------------------------------------- + /// @brief The arguments to glVertexAttribPointer. + /// + struct VertexAttribPointer { + GLuint index = 0u; + GLint size = 4; + GLenum type = GL_FLOAT; + GLenum normalized = GL_FALSE; + GLsizei stride = 0u; + GLsizei offset = 0u; + }; + std::vector vertex_attrib_arrays_; + std::map uniform_locations_; + + bool BindUniformBuffer(const ProcTableGLES& gl, + Allocator& transients_allocator, + const BufferResource& buffer) const; + + FML_DISALLOW_COPY_AND_ASSIGN(BufferBindingsGLES); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/command_buffer_gles.cc b/impeller/renderer/backend/gles/command_buffer_gles.cc index c0c6fc694ac7c..9f14096def6e4 100644 --- a/impeller/renderer/backend/gles/command_buffer_gles.cc +++ b/impeller/renderer/backend/gles/command_buffer_gles.cc @@ -5,35 +5,53 @@ #include "impeller/renderer/backend/gles/command_buffer_gles.h" #include "impeller/base/config.h" +#include "impeller/renderer/backend/gles/render_pass_gles.h" namespace impeller { -CommandBufferGLES::CommandBufferGLES() = default; +CommandBufferGLES::CommandBufferGLES(ReactorGLES::Ref reactor) + : reactor_(std::move(reactor)), + is_valid_(reactor_ && reactor_->IsValid()) {} CommandBufferGLES::~CommandBufferGLES() = default; // |CommandBuffer| -void CommandBufferGLES::SetLabel(const std::string& label) const {} +void CommandBufferGLES::SetLabel(const std::string& label) const { + // Cannot support. +} // |CommandBuffer| bool CommandBufferGLES::IsValid() const { - IMPELLER_UNIMPLEMENTED; + return is_valid_; } // |CommandBuffer| bool CommandBufferGLES::SubmitCommands(CompletionCallback callback) { - IMPELLER_UNIMPLEMENTED; -} + if (!IsValid()) { + return false; + } -// |CommandBuffer| -void CommandBufferGLES::ReserveSpotInQueue() { - IMPELLER_UNIMPLEMENTED; + const auto result = reactor_->React(); + + if (callback) { + callback(result ? CommandBuffer::Status::kCompleted + : CommandBuffer::Status::kError); + } + return result; } // |CommandBuffer| std::shared_ptr CommandBufferGLES::CreateRenderPass( RenderTarget target) const { - IMPELLER_UNIMPLEMENTED; + if (!IsValid()) { + return nullptr; + } + auto pass = std::shared_ptr( + new RenderPassGLES(std::move(target), reactor_)); + if (!pass->IsValid()) { + return nullptr; + } + return pass; } } // namespace impeller diff --git a/impeller/renderer/backend/gles/command_buffer_gles.h b/impeller/renderer/backend/gles/command_buffer_gles.h index 6819f0f50b5b9..6f99e010636b9 100644 --- a/impeller/renderer/backend/gles/command_buffer_gles.h +++ b/impeller/renderer/backend/gles/command_buffer_gles.h @@ -5,6 +5,7 @@ #pragma once #include "flutter/fml/macros.h" +#include "impeller/renderer/backend/gles/reactor_gles.h" #include "impeller/renderer/command_buffer.h" #include "impeller/renderer/render_target.h" @@ -18,7 +19,10 @@ class CommandBufferGLES final : public CommandBuffer { private: friend class ContextGLES; - CommandBufferGLES(); + ReactorGLES::Ref reactor_; + bool is_valid_ = false; + + CommandBufferGLES(ReactorGLES::Ref reactor); // |CommandBuffer| void SetLabel(const std::string& label) const override; @@ -29,9 +33,6 @@ class CommandBufferGLES final : public CommandBuffer { // |CommandBuffer| bool SubmitCommands(CompletionCallback callback) override; - // |CommandBuffer| - void ReserveSpotInQueue() override; - // |CommandBuffer| std::shared_ptr CreateRenderPass( RenderTarget target) const override; diff --git a/impeller/renderer/backend/gles/context_gles.cc b/impeller/renderer/backend/gles/context_gles.cc index efb762ddacc6f..7b57ddb18abec 100644 --- a/impeller/renderer/backend/gles/context_gles.cc +++ b/impeller/renderer/backend/gles/context_gles.cc @@ -9,56 +9,103 @@ namespace impeller { -ContextGLES::ContextGLES() { - auto reactor = std::make_shared(); - if (!reactor->IsValid()) { +std::shared_ptr ContextGLES::Create( + std::unique_ptr gl, + std::vector> shader_libraries) { + return std::shared_ptr( + new ContextGLES(std::move(gl), std::move(shader_libraries))); +} + +ContextGLES::ContextGLES( + std::unique_ptr gl, + std::vector> shader_libraries_mappings) { + reactor_ = std::make_shared(std::move(gl)); + if (!reactor_->IsValid()) { VALIDATION_LOG << "Could not create valid reactor."; return; } + // Create the shader library. + { + auto library = std::shared_ptr( + new ShaderLibraryGLES(std::move(shader_libraries_mappings))); + if (!library->IsValid()) { + VALIDATION_LOG << "Could not create valid shader library."; + return; + } + shader_library_ = std::move(library); + } + + // Create the pipeline library. + { + pipeline_library_ = + std::shared_ptr(new PipelineLibraryGLES(reactor_)); + } + + // Create all allocators. + { + permanents_allocator_ = + std::shared_ptr(new AllocatorGLES(reactor_)); + if (!permanents_allocator_->IsValid()) { + VALIDATION_LOG << "Could not create permanents allocator."; + return; + } + + transients_allocator_ = + std::shared_ptr(new AllocatorGLES(reactor_)); + if (!transients_allocator_->IsValid()) { + VALIDATION_LOG << "Could not create transients allocator."; + return; + } + } + + // Create the sampler library + { + sampler_library_ = + std::shared_ptr(new SamplerLibraryGLES()); + } + is_valid_ = true; } ContextGLES::~ContextGLES() = default; +const ReactorGLES::Ref ContextGLES::GetReactor() const { + return reactor_; +} + bool ContextGLES::IsValid() const { return is_valid_; } std::shared_ptr ContextGLES::GetPermanentsAllocator() const { - IMPELLER_UNIMPLEMENTED; return permanents_allocator_; } std::shared_ptr ContextGLES::GetTransientsAllocator() const { - IMPELLER_UNIMPLEMENTED; return transients_allocator_; } std::shared_ptr ContextGLES::GetShaderLibrary() const { - IMPELLER_UNIMPLEMENTED; return shader_library_; } std::shared_ptr ContextGLES::GetSamplerLibrary() const { - IMPELLER_UNIMPLEMENTED; return sampler_library_; } std::shared_ptr ContextGLES::GetPipelineLibrary() const { - IMPELLER_UNIMPLEMENTED; return pipeline_library_; } std::shared_ptr ContextGLES::CreateRenderCommandBuffer() const { - IMPELLER_UNIMPLEMENTED; - return std::shared_ptr(new CommandBufferGLES()); + return std::shared_ptr(new CommandBufferGLES(reactor_)); } std::shared_ptr ContextGLES::CreateTransferCommandBuffer() const { - IMPELLER_UNIMPLEMENTED; - return std::shared_ptr(new CommandBufferGLES()); + // There is no such concept. Just use a render command buffer. + return CreateRenderCommandBuffer(); } } // namespace impeller diff --git a/impeller/renderer/backend/gles/context_gles.h b/impeller/renderer/backend/gles/context_gles.h index 15e090752c59c..43287f9c7c155 100644 --- a/impeller/renderer/backend/gles/context_gles.h +++ b/impeller/renderer/backend/gles/context_gles.h @@ -10,6 +10,7 @@ #include "impeller/renderer/backend/gles/command_buffer_gles.h" #include "impeller/renderer/backend/gles/pipeline_library_gles.h" #include "impeller/renderer/backend/gles/reactor_gles.h" +#include "impeller/renderer/backend/gles/sampler_library_gles.h" #include "impeller/renderer/backend/gles/shader_library_gles.h" #include "impeller/renderer/context.h" @@ -18,20 +19,27 @@ namespace impeller { class ContextGLES final : public Context, public BackendCast { public: - ContextGLES(); + static std::shared_ptr Create( + std::unique_ptr gl, + std::vector> shader_libraries); // |Context| ~ContextGLES() override; + const ReactorGLES::Ref GetReactor() const; + private: - std::shared_ptr reactor_; + ReactorGLES::Ref reactor_; std::shared_ptr shader_library_; std::shared_ptr pipeline_library_; - std::shared_ptr sampler_library_; + std::shared_ptr sampler_library_; std::shared_ptr permanents_allocator_; std::shared_ptr transients_allocator_; bool is_valid_ = false; + ContextGLES(std::unique_ptr gl, + std::vector> shader_libraries); + // |Context| bool IsValid() const override; diff --git a/impeller/renderer/backend/gles/device_buffer_gles.cc b/impeller/renderer/backend/gles/device_buffer_gles.cc index 1a4392a71b757..f2084cc67c10c 100644 --- a/impeller/renderer/backend/gles/device_buffer_gles.cc +++ b/impeller/renderer/backend/gles/device_buffer_gles.cc @@ -4,8 +4,103 @@ #include "impeller/renderer/backend/gles/device_buffer_gles.h" +#include "flutter/fml/trace_event.h" +#include "impeller/base/allocation.h" +#include "impeller/base/config.h" +#include "impeller/base/validation.h" + namespace impeller { -// +DeviceBufferGLES::DeviceBufferGLES(ReactorGLES::Ref reactor, + size_t size, + StorageMode mode) + : DeviceBuffer(size, mode), + reactor_(std::move(reactor)), + handle_(reactor_ ? reactor_->CreateHandle(HandleType::kBuffer) + : GLESHandle::DeadHandle()) {} + +// |DeviceBuffer| +DeviceBufferGLES::~DeviceBufferGLES() { + if (!reactor_) { + reactor_->CollectHandle(handle_); + } +} + +// |DeviceBuffer| +bool DeviceBufferGLES::CopyHostBuffer(const uint8_t* source, + Range source_range, + size_t offset) { + if (mode_ != StorageMode::kHostVisible) { + // One of the storage modes where a transfer queue must be used. + return false; + } + + if (offset + source_range.length > size_) { + // Out of bounds of this buffer. + return false; + } + + if (!reactor_) { + return false; + } + + auto mapping = + CreateMappingWithCopy(source + source_range.offset, source_range.length); + if (!mapping) { + return false; + } + data_ = std::move(mapping); + return true; +} + +static GLenum ToTarget(DeviceBufferGLES::BindingType type) { + switch (type) { + case DeviceBufferGLES::BindingType::kArrayBuffer: + return GL_ARRAY_BUFFER; + case DeviceBufferGLES::BindingType::kElementArrayBuffer: + return GL_ELEMENT_ARRAY_BUFFER; + } + FML_UNREACHABLE(); +} + +bool DeviceBufferGLES::BindAndUploadDataIfNecessary(BindingType type) const { + if (!reactor_) { + return false; + } + + auto buffer = reactor_->GetGLHandle(handle_); + if (!buffer.has_value()) { + return false; + } + + const auto target_type = ToTarget(type); + const auto& gl = reactor_->GetProcTable(); + + gl.BindBuffer(target_type, buffer.value()); + + if (!uploaded_) { + TRACE_EVENT0("impeller", "BufferData"); + gl.BufferData(target_type, data_->GetSize(), data_->GetMapping(), + GL_STATIC_DRAW); + uploaded_ = true; + } + + return true; +} + +// |DeviceBuffer| +bool DeviceBufferGLES::SetLabel(const std::string& label) { + // Cannot support. + return true; +} + +// |DeviceBuffer| +bool DeviceBufferGLES::SetLabel(const std::string& label, Range range) { + // Cannot support. + return true; +} +std::shared_ptr DeviceBufferGLES::GetBufferData() const { + return data_; +} } // namespace impeller diff --git a/impeller/renderer/backend/gles/device_buffer_gles.h b/impeller/renderer/backend/gles/device_buffer_gles.h index e69de29bb2d1d..076394139622e 100644 --- a/impeller/renderer/backend/gles/device_buffer_gles.h +++ b/impeller/renderer/backend/gles/device_buffer_gles.h @@ -0,0 +1,52 @@ +// 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 "flutter/fml/macros.h" +#include "impeller/base/backend_cast.h" +#include "impeller/renderer/backend/gles/reactor_gles.h" +#include "impeller/renderer/device_buffer.h" + +namespace impeller { + +class DeviceBufferGLES final + : public DeviceBuffer, + public BackendCast { + public: + DeviceBufferGLES(ReactorGLES::Ref reactor, size_t size, StorageMode mode); + + // |DeviceBuffer| + ~DeviceBufferGLES() override; + + std::shared_ptr GetBufferData() const; + + enum class BindingType { + kArrayBuffer, + kElementArrayBuffer, + }; + + [[nodiscard]] bool BindAndUploadDataIfNecessary(BindingType type) const; + + private: + ReactorGLES::Ref reactor_; + GLESHandle handle_; + mutable std::shared_ptr data_; + mutable bool uploaded_ = false; + + // |DeviceBuffer| + bool CopyHostBuffer(const uint8_t* source, + Range source_range, + size_t offset) override; + + // |DeviceBuffer| + bool SetLabel(const std::string& label) override; + + // |DeviceBuffer| + bool SetLabel(const std::string& label, Range range) override; + + FML_DISALLOW_COPY_AND_ASSIGN(DeviceBufferGLES); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/formats_gles.h b/impeller/renderer/backend/gles/formats_gles.h index e69de29bb2d1d..b36a56afeaf11 100644 --- a/impeller/renderer/backend/gles/formats_gles.h +++ b/impeller/renderer/backend/gles/formats_gles.h @@ -0,0 +1,166 @@ +// 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 + +#include "flutter/fml/logging.h" +#include "flutter/fml/macros.h" +#include "impeller/renderer/backend/gles/gles.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/shader_types.h" + +namespace impeller { + +constexpr GLenum ToMode(PrimitiveType primitive_type) { + switch (primitive_type) { + case PrimitiveType::kTriangle: + return GL_TRIANGLES; + case PrimitiveType::kTriangleStrip: + return GL_TRIANGLE_STRIP; + case PrimitiveType::kLine: + return GL_LINES; + case PrimitiveType::kLineStrip: + return GL_LINE_STRIP; + case PrimitiveType::kPoint: + return GL_POINTS; + } + FML_UNREACHABLE(); +} + +constexpr GLenum ToIndexType(IndexType type) { + switch (type) { + case IndexType::kUnknown: + FML_UNREACHABLE(); + case IndexType::k16bit: + return GL_UNSIGNED_SHORT; + case IndexType::k32bit: + return GL_UNSIGNED_INT; + } + FML_UNREACHABLE(); +} + +constexpr GLenum ToStencilOp(StencilOperation op) { + switch (op) { + case StencilOperation::kKeep: + return GL_KEEP; + case StencilOperation::kZero: + return GL_ZERO; + case StencilOperation::kSetToReferenceValue: + return GL_REPLACE; + case StencilOperation::kIncrementClamp: + return GL_INCR; + case StencilOperation::kDecrementClamp: + return GL_DECR; + case StencilOperation::kInvert: + return GL_INVERT; + case StencilOperation::kIncrementWrap: + return GL_INCR_WRAP; + case StencilOperation::kDecrementWrap: + return GL_DECR_WRAP; + } + FML_UNREACHABLE(); +} + +constexpr GLenum ToCompareFunction(CompareFunction func) { + switch (func) { + case CompareFunction::kNever: + return GL_NEVER; + case CompareFunction::kAlways: + return GL_ALWAYS; + case CompareFunction::kLess: + return GL_LESS; + case CompareFunction::kEqual: + return GL_EQUAL; + case CompareFunction::kLessEqual: + return GL_LEQUAL; + case CompareFunction::kGreater: + return GL_GREATER; + case CompareFunction::kNotEqual: + return GL_NOTEQUAL; + case CompareFunction::kGreaterEqual: + return GL_GEQUAL; + } + FML_UNREACHABLE(); +} + +constexpr GLenum ToBlendFactor(BlendFactor factor) { + switch (factor) { + case BlendFactor::kZero: + return GL_ZERO; + case BlendFactor::kOne: + return GL_ONE; + case BlendFactor::kSourceColor: + return GL_SRC_COLOR; + case BlendFactor::kOneMinusSourceColor: + return GL_ONE_MINUS_SRC_COLOR; + case BlendFactor::kSourceAlpha: + return GL_SRC_ALPHA; + case BlendFactor::kOneMinusSourceAlpha: + return GL_ONE_MINUS_SRC_ALPHA; + case BlendFactor::kDestinationColor: + return GL_DST_COLOR; + case BlendFactor::kOneMinusDestinationColor: + return GL_ONE_MINUS_DST_COLOR; + case BlendFactor::kDestinationAlpha: + return GL_DST_ALPHA; + case BlendFactor::kOneMinusDestinationAlpha: + return GL_ONE_MINUS_DST_ALPHA; + case BlendFactor::kSourceAlphaSaturated: + return GL_SRC_ALPHA_SATURATE; + case BlendFactor::kBlendColor: + return GL_CONSTANT_COLOR; + case BlendFactor::kOneMinusBlendColor: + return GL_ONE_MINUS_CONSTANT_COLOR; + case BlendFactor::kBlendAlpha: + return GL_CONSTANT_ALPHA; + case BlendFactor::kOneMinusBlendAlpha: + return GL_ONE_MINUS_CONSTANT_ALPHA; + } + FML_UNREACHABLE(); +} + +constexpr GLenum ToBlendOperation(BlendOperation op) { + switch (op) { + case BlendOperation::kAdd: + return GL_FUNC_ADD; + case BlendOperation::kSubtract: + return GL_FUNC_SUBTRACT; + case BlendOperation::kReverseSubtract: + return GL_FUNC_REVERSE_SUBTRACT; + } + FML_UNREACHABLE(); +} + +constexpr std::optional ToVertexAttribType(ShaderType type) { + switch (type) { + case ShaderType::kSignedByte: + return GL_BYTE; + case ShaderType::kUnsignedByte: + return GL_UNSIGNED_BYTE; + case ShaderType::kSignedShort: + return GL_SHORT; + case ShaderType::kUnsignedShort: + return GL_UNSIGNED_SHORT; + case ShaderType::kFloat: + return GL_FLOAT; + case ShaderType::kUnknown: + case ShaderType::kVoid: + case ShaderType::kBoolean: + case ShaderType::kSignedInt: + case ShaderType::kUnsignedInt: + case ShaderType::kSignedInt64: + case ShaderType::kUnsignedInt64: + case ShaderType::kAtomicCounter: + case ShaderType::kHalfFloat: + case ShaderType::kDouble: + case ShaderType::kStruct: + case ShaderType::kImage: + case ShaderType::kSampledImage: + case ShaderType::kSampler: + return std::nullopt; + } + FML_UNREACHABLE(); +} + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/gl_description.cc b/impeller/renderer/backend/gles/gl_description.cc new file mode 100644 index 0000000000000..9c99639daf575 --- /dev/null +++ b/impeller/renderer/backend/gles/gl_description.cc @@ -0,0 +1,80 @@ +// 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/renderer/backend/gles/gl_description.h" + +#include +#include +#include +#include +#include +#include + +#include "impeller/renderer/backend/gles/proc_table_gles.h" + +namespace impeller { + +static std::string GetGLString(const ProcTableGLES& gl, GLenum name) { + auto str = gl.GetString(name); + if (str == nullptr) { + return ""; + } + return reinterpret_cast(str); +} + +GLDescription::GLDescription(const ProcTableGLES& gl) + : vendor_(GetGLString(gl, GL_VENDOR)), + renderer_(GetGLString(gl, GL_RENDERER)), + gl_version_(GetGLString(gl, GL_VERSION)), + sl_version_(GetGLString(gl, GL_SHADING_LANGUAGE_VERSION)) { + const auto extensions = GetGLString(gl, GL_EXTENSIONS); + std::stringstream extensions_stream(extensions); + std::string extension; + while (std::getline(extensions_stream, extension, ' ')) { + extensions_.insert(extension); + } + is_valid_ = true; +} + +GLDescription::~GLDescription() = default; + +bool GLDescription::IsValid() const { + return is_valid_; +} + +std::string GLDescription::GetString() const { + if (!IsValid()) { + return "Unknown Renderer."; + } + + std::vector> items; + + items.emplace_back(std::make_pair("Vendor", vendor_)); + items.emplace_back(std::make_pair("Renderer", renderer_)); + items.emplace_back(std::make_pair("GL Version", gl_version_)); + items.emplace_back(std::make_pair("Shading Language Version", sl_version_)); + items.emplace_back( + std::make_pair("Extensions", std::to_string(extensions_.size()))); + + size_t max_width = 0u; + for (const auto& item : items) { + max_width = std::max(max_width, item.first.size()); + } + + std::stringstream stream; + stream << "OpenGL Renderer:" << std::endl; + for (const auto& item : items) { + stream << std::setw(max_width + 1) << item.first << ": " << item.second + << std::endl; + } + + const auto pad = std::string(max_width + 3, ' '); + for (const auto& extension : extensions_) { + stream << pad << extension << std::endl; + } + + return stream.str(); +} + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/gl_description.h b/impeller/renderer/backend/gles/gl_description.h new file mode 100644 index 0000000000000..bb6386d535886 --- /dev/null +++ b/impeller/renderer/backend/gles/gl_description.h @@ -0,0 +1,37 @@ +// 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" + +namespace impeller { + +class ProcTableGLES; + +class GLDescription { + public: + GLDescription(const ProcTableGLES& gl); + + ~GLDescription(); + + bool IsValid() const; + + std::string GetString() const; + + private: + std::string vendor_; + std::string renderer_; + std::string gl_version_; + std::string sl_version_; + std::set extensions_; + bool is_valid_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(GLDescription); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/gles.h b/impeller/renderer/backend/gles/gles.h new file mode 100644 index 0000000000000..28f67dae0d3c1 --- /dev/null +++ b/impeller/renderer/backend/gles/gles.h @@ -0,0 +1,7 @@ +// 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 "GLES2/gl2.h" diff --git a/impeller/renderer/backend/gles/vertex_descriptor_gles.cc b/impeller/renderer/backend/gles/gles_handle.cc similarity index 76% rename from impeller/renderer/backend/gles/vertex_descriptor_gles.cc rename to impeller/renderer/backend/gles/gles_handle.cc index 677e729f2cc5f..20841b6486358 100644 --- a/impeller/renderer/backend/gles/vertex_descriptor_gles.cc +++ b/impeller/renderer/backend/gles/gles_handle.cc @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "impeller/renderer/backend/gles/vertex_descriptor_gles.h" +#include "impeller/renderer/backend/gles/gles_handle.h" namespace impeller { diff --git a/impeller/renderer/backend/gles/gles_handle.h b/impeller/renderer/backend/gles/gles_handle.h new file mode 100644 index 0000000000000..ed253be4bb10d --- /dev/null +++ b/impeller/renderer/backend/gles/gles_handle.h @@ -0,0 +1,73 @@ +// 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 +#include + +#include "flutter/fml/hash_combine.h" +#include "flutter/fml/macros.h" +#include "impeller/base/comparable.h" +#include "impeller/renderer/backend/gles/gles.h" + +namespace impeller { + +enum class HandleType { + kUnknown, + kTexture, + kBuffer, + kProgram, +}; + +class ReactorGLES; + +struct GLESHandle { + HandleType type = HandleType::kUnknown; + + static GLESHandle DeadHandle() { + return GLESHandle{HandleType::kUnknown, std::nullopt}; + } + + constexpr bool IsDead() const { return !name_.has_value(); } + + struct Hash { + std::size_t operator()(const GLESHandle& handle) const { + return fml::HashCombine( + std::underlying_type_t(handle.type), + handle.name_); + } + }; + + struct Equal { + bool operator()(const GLESHandle& lhs, const GLESHandle& rhs) const { + return lhs.type == rhs.type && lhs.name_ == rhs.name_; + } + }; + + private: + friend class ReactorGLES; + + std::optional name_; + + GLESHandle(HandleType p_type, UniqueID p_name) + : type(p_type), name_(p_name) {} + + GLESHandle(HandleType p_type, std::optional p_name) + : type(p_type), name_(p_name) {} + + static GLESHandle Create(HandleType type) { + return GLESHandle{type, UniqueID{}}; + } +}; + +using GLESHandleSet = + std::unordered_set; +template +using GLESHandleMap = + std::unordered_map; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/gles_test.cc b/impeller/renderer/backend/gles/gles_test.cc new file mode 100644 index 0000000000000..4684a6de43364 --- /dev/null +++ b/impeller/renderer/backend/gles/gles_test.cc @@ -0,0 +1,22 @@ +// 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/renderer/backend/gles/gles_test.h" + +#include "EGL/egl.h" +#include "impeller/renderer/backend/gles/gles.h" + +namespace impeller { + +GLESTest::GLESTest() = default; + +GLESTest::~GLESTest() = default; + +ProcTableGLES::Resolver GLESTest::GetResolver() const { + return [](const char* name) { + return reinterpret_cast(::eglGetProcAddress(name)); + }; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/gles_test.h b/impeller/renderer/backend/gles/gles_test.h new file mode 100644 index 0000000000000..6736f162ea031 --- /dev/null +++ b/impeller/renderer/backend/gles/gles_test.h @@ -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. + +#pragma once + +#include "flutter/fml/macros.h" +#include "flutter/testing/testing.h" +#include "impeller/renderer/backend/gles/proc_table_gles.h" + +namespace impeller { + +class GLESTest : public ::testing::Test { + public: + GLESTest(); + + ~GLESTest(); + + ProcTableGLES::Resolver GetResolver() const; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(GLESTest); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/gles_unittests.cc b/impeller/renderer/backend/gles/gles_unittests.cc new file mode 100644 index 0000000000000..9f9bbf1bb43df --- /dev/null +++ b/impeller/renderer/backend/gles/gles_unittests.cc @@ -0,0 +1,18 @@ +// 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 "flutter/testing/testing.h" +#include "impeller/renderer/backend/gles/gles_test.h" +#include "impeller/renderer/backend/gles/proc_table_gles.h" + +namespace impeller { +namespace testing { + +TEST_F(GLESTest, CanCreateProcTable) { + ProcTableGLES gl(GetResolver()); + ASSERT_TRUE(gl.IsValid()); +} + +} // namespace testing +} // namespace impeller diff --git a/impeller/renderer/backend/gles/pipeline_gles.cc b/impeller/renderer/backend/gles/pipeline_gles.cc index 3148ec1b176a9..7fa2706acdb11 100644 --- a/impeller/renderer/backend/gles/pipeline_gles.cc +++ b/impeller/renderer/backend/gles/pipeline_gles.cc @@ -6,6 +6,69 @@ namespace impeller { -// +PipelineGLES::PipelineGLES(ReactorGLES::Ref reactor, + std::weak_ptr library, + PipelineDescriptor desc) + : Pipeline(std::move(library), std::move(desc)), + reactor_(std::move(reactor)), + handle_(reactor_ ? reactor_->CreateHandle(HandleType::kProgram) + : GLESHandle::DeadHandle()), + is_valid_(!handle_.IsDead()) {} + +// |Pipeline| +PipelineGLES::~PipelineGLES() { + if (!handle_.IsDead()) { + reactor_->CollectHandle(std::move(handle_)); + } +} + +// |Pipeline| +bool PipelineGLES::IsValid() const { + return is_valid_; +} + +const GLESHandle& PipelineGLES::GetProgramHandle() const { + return handle_; +} + +const BufferBindingsGLES* PipelineGLES::GetBufferBindings() const { + return buffer_bindings_.get(); +} + +bool PipelineGLES::BuildVertexDescriptor(const ProcTableGLES& gl, + GLuint program) { + if (buffer_bindings_) { + return false; + } + auto vtx_desc = std::make_unique(); + if (!vtx_desc->RegisterVertexStageInput( + gl, GetDescriptor().GetVertexDescriptor()->GetStageInputs())) { + return false; + } + if (!vtx_desc->ReadUniformsBindings(gl, program)) { + return false; + } + buffer_bindings_ = std::move(vtx_desc); + return true; +} + +[[nodiscard]] bool PipelineGLES::BindProgram() const { + if (handle_.IsDead()) { + return false; + } + auto handle = reactor_->GetGLHandle(handle_); + if (!handle.has_value()) { + return false; + } + reactor_->GetProcTable().UseProgram(handle.value()); + return true; +} + +[[nodiscard]] bool PipelineGLES::UnbindProgram() const { + if (reactor_) { + reactor_->GetProcTable().UseProgram(0u); + } + return true; +} } // namespace impeller diff --git a/impeller/renderer/backend/gles/pipeline_gles.h b/impeller/renderer/backend/gles/pipeline_gles.h index e69de29bb2d1d..2b775b9bff3e0 100644 --- a/impeller/renderer/backend/gles/pipeline_gles.h +++ b/impeller/renderer/backend/gles/pipeline_gles.h @@ -0,0 +1,53 @@ +// 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 "flutter/fml/macros.h" +#include "impeller/base/backend_cast.h" +#include "impeller/renderer/backend/gles/buffer_bindings_gles.h" +#include "impeller/renderer/backend/gles/gles_handle.h" +#include "impeller/renderer/backend/gles/reactor_gles.h" +#include "impeller/renderer/pipeline.h" + +namespace impeller { + +class PipelineLibraryGLES; + +class PipelineGLES final : public Pipeline, + public BackendCast { + public: + // |Pipeline| + ~PipelineGLES() override; + + const GLESHandle& GetProgramHandle() const; + + [[nodiscard]] bool BindProgram() const; + + [[nodiscard]] bool UnbindProgram() const; + + const BufferBindingsGLES* GetBufferBindings() const; + + [[nodiscard]] bool BuildVertexDescriptor(const ProcTableGLES& gl, + GLuint program); + + private: + friend PipelineLibraryGLES; + + ReactorGLES::Ref reactor_; + GLESHandle handle_; + std::unique_ptr buffer_bindings_; + bool is_valid_ = false; + + // |Pipeline| + bool IsValid() const override; + + PipelineGLES(ReactorGLES::Ref reactor, + std::weak_ptr library, + PipelineDescriptor desc); + + FML_DISALLOW_COPY_AND_ASSIGN(PipelineGLES); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/pipeline_library_gles.cc b/impeller/renderer/backend/gles/pipeline_library_gles.cc index 270a77f16bfff..ea4a441337f0b 100644 --- a/impeller/renderer/backend/gles/pipeline_library_gles.cc +++ b/impeller/renderer/backend/gles/pipeline_library_gles.cc @@ -4,8 +4,223 @@ #include "impeller/renderer/backend/gles/pipeline_library_gles.h" +#include +#include + +#include "flutter/fml/trace_event.h" +#include "impeller/base/promise.h" +#include "impeller/renderer/backend/gles/pipeline_gles.h" +#include "impeller/renderer/backend/gles/shader_function_gles.h" + namespace impeller { -// +PipelineLibraryGLES::PipelineLibraryGLES(ReactorGLES::Ref reactor) + : reactor_(std::move(reactor)) {} + +static std::string GetShaderInfoLog(const ProcTableGLES& gl, GLuint shader) { + GLint log_length = 0; + gl.GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); + if (log_length == 0) { + return ""; + } + auto log_buffer = + reinterpret_cast(std::calloc(log_length, sizeof(char))); + gl.GetShaderInfoLog(shader, log_length, &log_length, log_buffer); + auto log_string = std::string(log_buffer, log_length); + std::free(log_buffer); + return log_string; +} + +static void LogShaderCompilationFailure(const ProcTableGLES& gl, + GLuint shader, + const std::string& name, + const fml::Mapping& source_mapping, + ShaderStage stage) { + std::stringstream stream; + stream << "Failed to compile "; + switch (stage) { + case ShaderStage::kUnknown: + stream << "unknown"; + break; + case ShaderStage::kVertex: + stream << "vertex"; + break; + case ShaderStage::kFragment: + stream << "fragment"; + break; + } + stream << " shader for '" << name << "' with error:" << std::endl; + stream << GetShaderInfoLog(gl, shader) << std::endl; + stream << "Shader source was: " << std::endl; + stream << std::string{reinterpret_cast( + source_mapping.GetMapping()), + source_mapping.GetSize()} + << std::endl; + VALIDATION_LOG << stream.str(); +} + +static bool LinkProgram( + const ReactorGLES& reactor, + std::shared_ptr pipeline, + const std::shared_ptr& vert_function, + const std::shared_ptr& frag_function) { + TRACE_EVENT0("impeller", __FUNCTION__); + + const auto& descriptor = pipeline->GetDescriptor(); + + auto vert_mapping = + ShaderFunctionGLES::Cast(*vert_function).GetSourceMapping(); + auto frag_mapping = + ShaderFunctionGLES::Cast(*frag_function).GetSourceMapping(); + + const auto& gl = reactor.GetProcTable(); + + auto vert_shader = gl.CreateShader(GL_VERTEX_SHADER); + auto frag_shader = gl.CreateShader(GL_FRAGMENT_SHADER); + + if (vert_shader == 0 || frag_shader == 0) { + VALIDATION_LOG << "Could not create shader handles."; + return false; + } + + fml::ScopedCleanupClosure delete_vert_shader( + [&gl, vert_shader]() { gl.DeleteShader(vert_shader); }); + fml::ScopedCleanupClosure delete_frag_shader( + [&gl, frag_shader]() { gl.DeleteShader(frag_shader); }); + + gl.ShaderSourceMapping(vert_shader, *vert_mapping); + gl.ShaderSourceMapping(frag_shader, *frag_mapping); + + gl.CompileShader(vert_shader); + gl.CompileShader(frag_shader); + + GLint vert_status = GL_FALSE; + GLint frag_status = GL_FALSE; + + gl.GetShaderiv(vert_shader, GL_COMPILE_STATUS, &vert_status); + gl.GetShaderiv(vert_shader, GL_COMPILE_STATUS, &frag_status); + + if (vert_status != GL_TRUE) { + LogShaderCompilationFailure(gl, vert_shader, descriptor.GetLabel(), + *vert_mapping, ShaderStage::kVertex); + return false; + } + + if (frag_status != GL_TRUE) { + LogShaderCompilationFailure(gl, frag_shader, descriptor.GetLabel(), + *frag_mapping, ShaderStage::kFragment); + return false; + } + + auto program = reactor.GetGLHandle(pipeline->GetProgramHandle()); + if (!program.has_value()) { + VALIDATION_LOG << "Could not get program handle from reactor."; + return false; + } + + gl.AttachShader(*program, vert_shader); + gl.AttachShader(*program, frag_shader); + + fml::ScopedCleanupClosure detach_vert_shader( + [&gl, program = *program, vert_shader]() { + gl.DetachShader(program, vert_shader); + }); + fml::ScopedCleanupClosure detach_frag_shader( + [&gl, program = *program, frag_shader]() { + gl.DetachShader(program, frag_shader); + }); + + for (const auto& stage_input : + descriptor.GetVertexDescriptor()->GetStageInputs()) { + gl.BindAttribLocation(*program, // + static_cast(stage_input.location), // + stage_input.name // + ); + } + + gl.LinkProgram(*program); + + GLint link_status = GL_FALSE; + gl.GetProgramiv(*program, GL_LINK_STATUS, &link_status); + + if (link_status != GL_TRUE) { + VALIDATION_LOG << "Could not link shader program."; + return false; + } + return true; +} + +// |PipelineLibrary| +PipelineFuture PipelineLibraryGLES::GetRenderPipeline( + PipelineDescriptor descriptor) { + if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) { + return found->second; + } + + if (!reactor_) { + return RealizedFuture>(nullptr); + } + + auto vert_function = descriptor.GetEntrypointForStage(ShaderStage::kVertex); + auto frag_function = descriptor.GetEntrypointForStage(ShaderStage::kFragment); + + if (!vert_function || !frag_function) { + VALIDATION_LOG + << "Could not find stage entrypoint functions in pipeline descriptor."; + return RealizedFuture>(nullptr); + } + + auto promise = std::make_shared>>(); + auto future = PipelineFuture{promise->get_future()}; + pipelines_[descriptor] = future; + auto weak_this = weak_from_this(); + + auto result = reactor_->AddOperation( + [promise, weak_this, reactor_ptr = reactor_, descriptor, vert_function, + frag_function](const ReactorGLES& reactor) { + auto strong_this = weak_this.lock(); + if (!strong_this) { + promise->set_value(nullptr); + VALIDATION_LOG << "Library was collected before a pending pipeline " + "creation could finish."; + return; + } + auto pipeline = std::shared_ptr( + new PipelineGLES(reactor_ptr, strong_this, descriptor)); + auto program = reactor.GetGLHandle(pipeline->GetProgramHandle()); + if (!program.has_value()) { + VALIDATION_LOG << "Could not obtain program handle."; + return; + } + const auto link_result = LinkProgram(reactor, // + pipeline, // + std::move(vert_function), // + std::move(frag_function) // + ); + if (!link_result) { + promise->set_value(nullptr); + VALIDATION_LOG << "Could not link pipeline program."; + return; + } + if (!pipeline->BuildVertexDescriptor(reactor.GetProcTable(), + program.value())) { + promise->set_value(nullptr); + VALIDATION_LOG << "Could not build pipeline vertex descriptors."; + return; + } + if (!pipeline->IsValid()) { + promise->set_value(nullptr); + VALIDATION_LOG << "Pipeline validation checks failed."; + return; + } + promise->set_value(std::move(pipeline)); + }); + FML_CHECK(result); + + return future; +} + +// |PipelineLibrary| +PipelineLibraryGLES::~PipelineLibraryGLES() = default; } // namespace impeller diff --git a/impeller/renderer/backend/gles/pipeline_library_gles.h b/impeller/renderer/backend/gles/pipeline_library_gles.h index a608e19eb85a0..6c87bfda0205a 100644 --- a/impeller/renderer/backend/gles/pipeline_library_gles.h +++ b/impeller/renderer/backend/gles/pipeline_library_gles.h @@ -5,6 +5,7 @@ #pragma once #include "flutter/fml/macros.h" +#include "impeller/renderer/backend/gles/reactor_gles.h" #include "impeller/renderer/pipeline_library.h" namespace impeller { @@ -19,7 +20,10 @@ class PipelineLibraryGLES final : public PipelineLibrary { private: friend ContextGLES; - PipelineLibraryGLES(); + ReactorGLES::Ref reactor_; + PipelineMap pipelines_; + + PipelineLibraryGLES(ReactorGLES::Ref reactor); // |PipelineLibrary| PipelineFuture GetRenderPipeline(PipelineDescriptor descriptor) override; diff --git a/impeller/renderer/backend/gles/proc_table_gles.cc b/impeller/renderer/backend/gles/proc_table_gles.cc index 643bc0ecc2d68..8453d59f8fab0 100644 --- a/impeller/renderer/backend/gles/proc_table_gles.cc +++ b/impeller/renderer/backend/gles/proc_table_gles.cc @@ -4,9 +4,62 @@ #include "impeller/renderer/backend/gles/proc_table_gles.h" +#include + +#include "impeller/base/validation.h" + namespace impeller { -ProcTableGLES::ProcTableGLES() : is_valid_(true) {} +const char* GLErrorToString(GLenum value) { + switch (value) { + case GL_NO_ERROR: + return "GL_NO_ERROR"; + case GL_INVALID_ENUM: + return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: + return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: + return "GL_INVALID_OPERATION"; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "GL_INVALID_FRAMEBUFFER_OPERATION"; + case GL_FRAMEBUFFER_COMPLETE: + return "GL_FRAMEBUFFER_COMPLETE"; + case GL_OUT_OF_MEMORY: + return "GL_OUT_OF_MEMORY"; + } + return "Unknown."; +} + +ProcTableGLES::ProcTableGLES(Resolver resolver) { + if (!resolver) { + return; + } + + auto error_fn = reinterpret_cast(resolver("glGetError")); + if (!error_fn) { + VALIDATION_LOG << "Could not resolve " + << "glGetError"; + return; + } + +#define IMPELLER_PROC(proc_ivar) \ + if (auto fn_ptr = resolver(proc_ivar.name)) { \ + proc_ivar.function = \ + reinterpret_cast(fn_ptr); \ + proc_ivar.error_fn = error_fn; \ + } else { \ + VALIDATION_LOG << "Could not resolve " << proc_ivar.name; \ + return; \ + } + + FOR_EACH_IMPELLER_PROC(IMPELLER_PROC); + +#undef IMPELLER_PROC + + is_valid_ = true; + + description_ = std::make_unique(*this); +} ProcTableGLES::~ProcTableGLES() = default; @@ -14,4 +67,118 @@ bool ProcTableGLES::IsValid() const { return is_valid_; } +void ProcTableGLES::ShaderSourceMapping(GLuint shader, + const fml::Mapping& mapping) const { + const GLchar* sources[] = { + reinterpret_cast(mapping.GetMapping())}; + const GLint lengths[] = {static_cast(mapping.GetSize())}; + ShaderSource(shader, 1u, sources, lengths); +} + +const GLDescription* ProcTableGLES::GetDescription() const { + return description_.get(); +} + +static const char* FramebufferStatusToString(GLenum status) { + switch (status) { + case GL_FRAMEBUFFER_COMPLETE: + return "GL_FRAMEBUFFER_COMPLETE"; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; +#if GL_ES_VERSION_2_0 + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: + return "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS"; +#endif + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; + case GL_FRAMEBUFFER_UNSUPPORTED: + return "GL_FRAMEBUFFER_UNSUPPORTED"; + case GL_INVALID_ENUM: + return "GL_INVALID_ENUM"; + } + + return "Unknown FBO Error Status"; +} + +static const char* AttachmentTypeString(GLint type) { + switch (type) { + case GL_RENDERBUFFER: + return "GL_RENDERBUFFER"; + case GL_TEXTURE: + return "GL_TEXTURE"; + case GL_NONE: + return "GL_NONE"; + } + + return "Unknown Type"; +} + +static std::string DescribeFramebufferAttachment(const ProcTableGLES& gl, + GLenum attachment) { + GLint param = GL_NONE; + gl.GetFramebufferAttachmentParameteriv( + GL_FRAMEBUFFER, // target + attachment, // attachment + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, // parameter name + ¶m // parameter + ); + + if (param != GL_NONE) { + param = GL_NONE; + gl.GetFramebufferAttachmentParameteriv( + GL_FRAMEBUFFER, // target + attachment, // attachment + GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, // parameter name + ¶m // parameter + ); + std::stringstream stream; + stream << AttachmentTypeString(param) << "(" << param << ")"; + return stream.str(); + } + + return "No Attachment"; +} + +std::string ProcTableGLES::DescribeCurrentFramebuffer() const { + GLint framebuffer = GL_NONE; + GetIntegerv(GL_FRAMEBUFFER_BINDING, &framebuffer); + if (IsFramebuffer(framebuffer) == GL_FALSE) { + return "No framebuffer or the default window framebuffer is bound."; + } + + GLenum status = CheckFramebufferStatus(framebuffer); + std::stringstream stream; + stream << "FBO " + << ((framebuffer == GL_NONE) ? "(Default)" + : std::to_string(framebuffer)) + << ": " << FramebufferStatusToString(status) << std::endl; + if (IsCurrentFramebufferComplete()) { + stream << "Framebuffer is complete." << std::endl; + } else { + stream << "Framebuffer is incomplete." << std::endl; + } + stream << "Description: " << std::endl; + stream << "Color Attachment: " + << DescribeFramebufferAttachment(*this, GL_COLOR_ATTACHMENT0) + << std::endl; + stream << "Color Attachment: " + << DescribeFramebufferAttachment(*this, GL_DEPTH_ATTACHMENT) + << std::endl; + stream << "Color Attachment: " + << DescribeFramebufferAttachment(*this, GL_STENCIL_ATTACHMENT) + << std::endl; + return stream.str(); +} + +bool ProcTableGLES::IsCurrentFramebufferComplete() const { + GLint framebuffer = GL_NONE; + GetIntegerv(GL_FRAMEBUFFER_BINDING, &framebuffer); + if (IsFramebuffer(framebuffer) == GL_FALSE) { + // The default framebuffer is always complete. + return true; + } + GLenum status = CheckFramebufferStatus(framebuffer); + return status == GL_FRAMEBUFFER_COMPLETE; +} + } // namespace impeller diff --git a/impeller/renderer/backend/gles/proc_table_gles.h b/impeller/renderer/backend/gles/proc_table_gles.h index cabb5d10c43ca..c306750dfeed3 100644 --- a/impeller/renderer/backend/gles/proc_table_gles.h +++ b/impeller/renderer/backend/gles/proc_table_gles.h @@ -4,20 +4,160 @@ #pragma once +#include +#include + +#include "flutter/fml/logging.h" #include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" +#include "impeller/renderer/backend/gles/gl_description.h" +#include "impeller/renderer/backend/gles/gles.h" namespace impeller { +const char* GLErrorToString(GLenum value); + +struct AutoErrorCheck { + const PFNGLGETERRORPROC error_fn; + const char* name; + + AutoErrorCheck(PFNGLGETERRORPROC error, const char* name) + : error_fn(error), name(name) {} + + ~AutoErrorCheck() { + if (error_fn) { + auto error = error_fn(); + FML_CHECK(error == GL_NO_ERROR) + << "GL Error " << GLErrorToString(error) << "(" << error << ")" + << " encountered on call to " << name; + } + } +}; + +template +struct GLProc { + using GLFunctionType = T; + + //---------------------------------------------------------------------------- + /// The name of the GL function. + /// + const char* name = nullptr; + + //---------------------------------------------------------------------------- + /// The pointer to the GL function. + /// + GLFunctionType* function = nullptr; + + //---------------------------------------------------------------------------- + /// An optional error function. If present, all calls will be followed by an + /// error check. + /// + PFNGLGETERRORPROC error_fn = nullptr; + + //---------------------------------------------------------------------------- + /// @brief Call the GL function with the appropriate parameters. Lookup + /// the documentation for the GL function being called to + /// understand the arguments and return types. The arguments + /// types must match and will be type checked. + /// + template + auto operator()(Args&&... args) const { + FML_DCHECK(function) << "Function named " + << (name != nullptr ? name : "") + << " unavailable."; + AutoErrorCheck error(error_fn, name); + return function(std::forward(args)...); + } +}; + +#define FOR_EACH_IMPELLER_PROC(PROC) \ + PROC(AttachShader); \ + PROC(BindAttribLocation); \ + PROC(BindBuffer); \ + PROC(BlendEquationSeparate); \ + PROC(BlendFuncSeparate); \ + PROC(BufferData); \ + PROC(Clear); \ + PROC(ClearColor); \ + PROC(ClearDepthf); \ + PROC(ClearStencil); \ + PROC(ColorMask); \ + PROC(CompileShader); \ + PROC(CreateProgram); \ + PROC(CreateShader); \ + PROC(CullFace); \ + PROC(DeleteBuffers); \ + PROC(DeleteProgram); \ + PROC(DeleteShader); \ + PROC(DeleteTextures); \ + PROC(DepthFunc); \ + PROC(DepthMask); \ + PROC(DepthRangef); \ + PROC(DetachShader); \ + PROC(Disable); \ + PROC(DisableVertexAttribArray); \ + PROC(DrawElements); \ + PROC(Enable); \ + PROC(EnableVertexAttribArray); \ + PROC(FrontFace); \ + PROC(GenBuffers); \ + PROC(GenTextures); \ + PROC(GetActiveUniform); \ + PROC(GetBooleanv); \ + PROC(GetFloatv); \ + PROC(GetFramebufferAttachmentParameteriv); \ + PROC(GetIntegerv); \ + PROC(GetProgramiv); \ + PROC(GetShaderInfoLog); \ + PROC(GetShaderiv); \ + PROC(GetString); \ + PROC(GetUniformLocation); \ + PROC(IsFramebuffer); \ + PROC(IsProgram); \ + PROC(LinkProgram); \ + PROC(Scissor); \ + PROC(ShaderBinary); \ + PROC(ShaderSource); \ + PROC(StencilFuncSeparate); \ + PROC(StencilMaskSeparate); \ + PROC(StencilOpSeparate); \ + PROC(Uniform1fv); \ + PROC(Uniform1i); \ + PROC(Uniform2fv); \ + PROC(Uniform4fv); \ + PROC(UniformMatrix4fv); \ + PROC(UseProgram); \ + PROC(CheckFramebufferStatus); \ + PROC(VertexAttribPointer); \ + PROC(Viewport); + class ProcTableGLES { public: - ProcTableGLES(); + using Resolver = std::function; + ProcTableGLES(Resolver resolver); ~ProcTableGLES(); +#define IMPELLER_PROC(name) \ + GLProc name = {"gl" #name, nullptr}; + + FOR_EACH_IMPELLER_PROC(IMPELLER_PROC); + +#undef IMPELLER_PROC + bool IsValid() const; + void ShaderSourceMapping(GLuint shader, const fml::Mapping& mapping) const; + + const GLDescription* GetDescription() const; + + std::string DescribeCurrentFramebuffer() const; + + bool IsCurrentFramebufferComplete() const; + private: bool is_valid_ = false; + std::unique_ptr description_; FML_DISALLOW_COPY_AND_ASSIGN(ProcTableGLES); }; diff --git a/impeller/renderer/backend/gles/reactor_gles.cc b/impeller/renderer/backend/gles/reactor_gles.cc index e673632dab9ff..e388734eec318 100644 --- a/impeller/renderer/backend/gles/reactor_gles.cc +++ b/impeller/renderer/backend/gles/reactor_gles.cc @@ -4,15 +4,15 @@ #include "impeller/renderer/backend/gles/reactor_gles.h" +#include "flutter/fml/trace_event.h" #include "impeller/base/validation.h" namespace impeller { -ReactorGLES::ReactorGLES() { - proc_table_ = std::make_unique(); - - if (!proc_table_->IsValid()) { - VALIDATION_LOG << "Could not create valid proc table."; +ReactorGLES::ReactorGLES(std::unique_ptr gl) + : proc_table_(std::move(gl)) { + if (!proc_table_ || !proc_table_->IsValid()) { + VALIDATION_LOG << "Proc table was invalid."; return; } @@ -25,4 +25,149 @@ bool ReactorGLES::IsValid() const { return is_valid_; } +bool ReactorGLES::HasPendingOperations() const { + return !pending_operations_.empty() || !gl_handles_to_collect_.empty(); +} + +const ProcTableGLES& ReactorGLES::GetProcTable() const { + FML_DCHECK(IsValid()); + return *proc_table_; +} + +std::optional ReactorGLES::GetGLHandle(const GLESHandle& handle) const { + auto found = live_gl_handles_.find(handle); + if (found != live_gl_handles_.end()) { + return found->second; + } + return std::nullopt; +} + +bool ReactorGLES::AddOperation(Operation operation) { + if (!operation) { + return false; + } + pending_operations_.emplace_back(std::move(operation)); + return React(); +} + +static std::optional CreateGLHandle(const ProcTableGLES& gl, + HandleType type) { + GLuint handle = GL_NONE; + switch (type) { + case HandleType::kUnknown: + return std::nullopt; + case HandleType::kTexture: + gl.GenTextures(1u, &handle); + return handle; + case HandleType::kBuffer: + gl.GenBuffers(1u, &handle); + return handle; + case HandleType::kProgram: + return gl.CreateProgram(); + } + return std::nullopt; +} + +static bool CollectGLHandle(const ProcTableGLES& gl, + HandleType type, + GLuint handle) { + switch (type) { + case HandleType::kUnknown: + return false; + case HandleType::kTexture: + gl.DeleteTextures(1u, &handle); + return true; + case HandleType::kBuffer: + gl.DeleteBuffers(1u, &handle); + return true; + case HandleType::kProgram: + gl.DeleteProgram(handle); + return true; + } + return false; +} + +GLESHandle ReactorGLES::CreateHandle(HandleType type) { + if (type == HandleType::kUnknown) { + return GLESHandle::DeadHandle(); + } + auto new_handle = GLESHandle::Create(type); + if (new_handle.IsDead()) { + return GLESHandle::DeadHandle(); + } + live_gl_handles_[new_handle] = + in_reaction_ ? CreateGLHandle(GetProcTable(), type) : std::nullopt; + return new_handle; +} + +void ReactorGLES::CollectHandle(GLESHandle handle) { + auto live_handle = live_gl_handles_.find(handle); + if (live_handle == live_gl_handles_.end()) { + return; + } + if (live_handle->second.has_value()) { + gl_handles_to_collect_[live_handle->first] = live_handle->second.value(); + } + live_gl_handles_.erase(live_handle); +} + +bool ReactorGLES::React() { + TRACE_EVENT0("impeller", "ReactorGLES::React"); + in_reaction_ = true; + fml::ScopedCleanupClosure reset_in_reaction([&]() { in_reaction_ = false; }); + while (HasPendingOperations()) { + if (!ReactOnce()) { + return false; + } + } + return true; +} + +bool ReactorGLES::ReactOnce() { + if (!IsValid()) { + return false; + } + + //---------------------------------------------------------------------------- + /// Collect all the handles for whom there is a GL handle sibling. + /// + for (const auto& handle_to_collect : gl_handles_to_collect_) { + if (!CollectGLHandle(GetProcTable(), // proc table + handle_to_collect.first.type, // handle type + handle_to_collect.second // GL handle name + )) { + VALIDATION_LOG << "Could not collect GL handle."; + return false; + } + } + gl_handles_to_collect_.clear(); + + //---------------------------------------------------------------------------- + /// Make sure all pending handles have a GL handle sibling. + /// + for (auto& live_handle : live_gl_handles_) { + if (live_handle.second.has_value()) { + // Already a realized GL handle. + continue; + } + auto gl_handle = CreateGLHandle(GetProcTable(), live_handle.first.type); + if (!gl_handle.has_value()) { + VALIDATION_LOG << "Could not create GL handle."; + return false; + } + live_handle.second = gl_handle; + } + + //---------------------------------------------------------------------------- + /// Flush all pending operations in order. + /// + auto operations = std::move(pending_operations_); + for (const auto& operation : operations) { + operation(*this); + } + pending_operations_.clear(); + + return true; +} + } // namespace impeller diff --git a/impeller/renderer/backend/gles/reactor_gles.h b/impeller/renderer/backend/gles/reactor_gles.h index f3a0d1bd0161d..673e5f790f6fb 100644 --- a/impeller/renderer/backend/gles/reactor_gles.h +++ b/impeller/renderer/backend/gles/reactor_gles.h @@ -4,25 +4,53 @@ #pragma once +#include #include +#include +#include "flutter/fml/closure.h" #include "flutter/fml/macros.h" +#include "impeller/renderer/backend/gles/gles_handle.h" #include "impeller/renderer/backend/gles/proc_table_gles.h" namespace impeller { class ReactorGLES { public: - ReactorGLES(); + using Ref = std::shared_ptr; + + ReactorGLES(std::unique_ptr gl); ~ReactorGLES(); bool IsValid() const; + bool HasPendingOperations() const; + + const ProcTableGLES& GetProcTable() const; + + std::optional GetGLHandle(const GLESHandle& handle) const; + + GLESHandle CreateHandle(HandleType type); + + void CollectHandle(GLESHandle handle); + + using Operation = std::function; + [[nodiscard]] bool AddOperation(Operation operation); + + [[nodiscard]] bool React(); + private: std::unique_ptr proc_table_; + std::vector pending_operations_; + GLESHandleMap> live_gl_handles_; + GLESHandleMap gl_handles_to_collect_; + bool in_reaction_ = false; + bool is_valid_ = false; + bool ReactOnce(); + FML_DISALLOW_COPY_AND_ASSIGN(ReactorGLES); }; diff --git a/impeller/renderer/backend/gles/render_pass_gles.cc b/impeller/renderer/backend/gles/render_pass_gles.cc index 06771a3128558..b605062cfa206 100644 --- a/impeller/renderer/backend/gles/render_pass_gles.cc +++ b/impeller/renderer/backend/gles/render_pass_gles.cc @@ -4,8 +4,416 @@ #include "impeller/renderer/backend/gles/render_pass_gles.h" +#include + +#include "flutter/fml/trace_event.h" +#include "impeller/base/config.h" +#include "impeller/base/validation.h" +#include "impeller/renderer/backend/gles/device_buffer_gles.h" +#include "impeller/renderer/backend/gles/formats_gles.h" +#include "impeller/renderer/backend/gles/pipeline_gles.h" + namespace impeller { -// +RenderPassGLES::RenderPassGLES(RenderTarget target, ReactorGLES::Ref reactor) + : RenderPass(std::move(target)), + reactor_(std::move(reactor)), + is_valid_(reactor_ && reactor_->IsValid()) {} + +// |RenderPass| +RenderPassGLES::~RenderPassGLES() = default; + +// |RenderPass| +bool RenderPassGLES::IsValid() const { + return is_valid_; +} + +// |RenderPass| +void RenderPassGLES::SetLabel(std::string label) { + // Cannot support. +} + +void ConfigureBlending(const ProcTableGLES& gl, + const ColorAttachmentDescriptor* color) { + if (!color->blending_enabled) { + gl.Disable(GL_BLEND); + return; + } + + gl.Enable(GL_BLEND); + gl.BlendFuncSeparate( + ToBlendFactor(color->src_color_blend_factor), // src color + ToBlendFactor(color->dst_color_blend_factor), // dst color + ToBlendFactor(color->src_alpha_blend_factor), // src alpha + ToBlendFactor(color->dst_alpha_blend_factor) // dst alpha + ); + gl.BlendEquationSeparate( + ToBlendOperation(color->color_blend_op), // mode color + ToBlendOperation(color->alpha_blend_op) // mode alpha + ); + { + const auto is_set = [](std::underlying_type_t mask, + ColorWriteMask check) -> GLboolean { + using RawType = decltype(mask); + return (static_cast(mask) & static_cast(mask)) + ? GL_TRUE + : GL_FALSE; + }; + + gl.ColorMask(is_set(color->write_mask, ColorWriteMask::kRed), // red + is_set(color->write_mask, ColorWriteMask::kGreen), // green + is_set(color->write_mask, ColorWriteMask::kBlue), // blue + is_set(color->write_mask, ColorWriteMask::kAlpha) // alpha + ); + } +} + +void ConfigureStencil(GLenum face, + const ProcTableGLES& gl, + const StencilAttachmentDescriptor& stencil, + uint32_t stencil_reference) { + gl.StencilOpSeparate( + face, // face + ToStencilOp(stencil.stencil_failure), // stencil fail + ToStencilOp(stencil.depth_failure), // depth fail + ToStencilOp(stencil.depth_stencil_pass) // depth stencil pass + ); + gl.StencilFuncSeparate(face, // face + ToCompareFunction(stencil.stencil_compare), // func + stencil_reference, // ref + stencil.read_mask // mask + ); + gl.StencilMaskSeparate(face, stencil.write_mask); +} + +void ConfigureStencil(const ProcTableGLES& gl, + const PipelineDescriptor& pipeline, + uint32_t stencil_reference) { + if (!pipeline.HasStencilAttachmentDescriptors()) { + gl.Disable(GL_STENCIL_TEST); + return; + } + + gl.Enable(GL_STENCIL_TEST); + const auto& front = pipeline.GetFrontStencilAttachmentDescriptor(); + const auto& back = pipeline.GetBackStencilAttachmentDescriptor(); + if (front == back) { + ConfigureStencil(GL_FRONT_AND_BACK, gl, *front, stencil_reference); + } else if (front.has_value()) { + ConfigureStencil(GL_FRONT, gl, *front, stencil_reference); + } else if (back.has_value()) { + ConfigureStencil(GL_BACK, gl, *back, stencil_reference); + } else { + FML_UNREACHABLE(); + } +} + +//------------------------------------------------------------------------------ +/// @brief Encapsulates data that will be needed in the reactor for the +/// encoding of commands for this render pass. +/// +struct RenderPassData { + Viewport viewport; + + Color clear_color; + uint32_t clear_stencil = 0u; + Scalar clear_depth = 1.0; + + bool has_depth_attachment = false; + bool has_stencil_attachment = false; + + bool clear_color_attachment = true; + bool clear_depth_attachment = true; + bool clear_stencil_attachment = true; + + bool discard_color_attachment = true; + bool discard_depth_attachment = true; + bool discard_stencil_attachment = true; +}; + +[[nodiscard]] bool EncodeCommandsInReactor( + const RenderPassData& pass_data, + const std::shared_ptr& transients_allocator, + const ReactorGLES& reactor, + const std::vector& commands) { + TRACE_EVENT0("impeller", __FUNCTION__); + + const auto& gl = reactor.GetProcTable(); + + gl.ClearColor(pass_data.clear_color.red, // red + pass_data.clear_color.green, // green + pass_data.clear_color.blue, // blue + pass_data.clear_color.alpha // alpha + ); + if (pass_data.has_depth_attachment) { + gl.ClearDepthf(pass_data.clear_depth); + } + if (pass_data.has_stencil_attachment) { + gl.ClearStencil(pass_data.clear_stencil); + } + + GLenum clear_bits = 0u; + if (pass_data.clear_color_attachment) { + clear_bits |= GL_COLOR_BUFFER_BIT; + } + if (pass_data.clear_depth_attachment) { + clear_bits |= GL_DEPTH_BUFFER_BIT; + } + if (pass_data.clear_stencil_attachment) { + clear_bits |= GL_STENCIL_BUFFER_BIT; + } + gl.Clear(clear_bits); + + for (const auto& command : commands) { + if (command.instance_count != 1u) { + VALIDATION_LOG << "GLES backend does not support instanced rendering."; + return false; + } + + if (!command.pipeline) { + VALIDATION_LOG << "Command has no pipeline specified."; + return false; + } + + const auto& pipeline = PipelineGLES::Cast(*command.pipeline); + + const auto* color_attachment = + pipeline.GetDescriptor().GetLegacyCompatibleColorAttachment(); + if (!color_attachment) { + VALIDATION_LOG + << "Color attachment is too complicated for a legacy renderer."; + return false; + } + + //-------------------------------------------------------------------------- + /// Configure blending. + /// + ConfigureBlending(gl, color_attachment); + + //-------------------------------------------------------------------------- + /// Setup stencil. + /// + ConfigureStencil(gl, pipeline.GetDescriptor(), command.stencil_reference); + + //-------------------------------------------------------------------------- + /// Configure depth. + /// + if (auto depth = + pipeline.GetDescriptor().GetDepthStencilAttachmentDescriptor(); + depth.has_value()) { + gl.Enable(GL_DEPTH_TEST); + gl.DepthFunc(ToCompareFunction(depth->depth_compare)); + gl.DepthMask(depth->depth_write_enabled ? GL_TRUE : GL_FALSE); + } else { + gl.Disable(GL_DEPTH_TEST); + } + + //-------------------------------------------------------------------------- + /// Setup the viewport. + /// + const auto& viewport = command.viewport.value_or(pass_data.viewport); + gl.Viewport(viewport.rect.origin.x, // x + viewport.rect.origin.y, // y + viewport.rect.size.width, // width + viewport.rect.size.height // height + ); + if (pass_data.has_depth_attachment) { + gl.DepthRangef(viewport.depth_range.z_near, viewport.depth_range.z_far); + } + + //-------------------------------------------------------------------------- + /// Setup the scissor rect. + /// + if (command.scissor.has_value()) { + const auto& scissor = command.scissor.value(); + gl.Enable(GL_SCISSOR_TEST); + gl.Scissor(scissor.origin.x, // x + scissor.origin.y, // y + scissor.size.width, // width + scissor.size.width // height + ); + } else { + gl.Disable(GL_SCISSOR_TEST); + } + + //-------------------------------------------------------------------------- + /// Setup culling. + /// + switch (command.cull_mode) { + case CullMode::kNone: + gl.Disable(GL_CULL_FACE); + break; + case CullMode::kFrontFace: + gl.Enable(GL_CULL_FACE); + gl.CullFace(GL_FRONT); + break; + case CullMode::kBackFace: + gl.Enable(GL_CULL_FACE); + gl.CullFace(GL_BACK); + break; + } + //-------------------------------------------------------------------------- + /// Setup winding order. + /// + switch (command.winding) { + case WindingOrder::kClockwise: + gl.FrontFace(GL_CW); + break; + case WindingOrder::kCounterClockwise: + gl.FrontFace(GL_CCW); + break; + } + + if (command.index_type == IndexType::kUnknown) { + return false; + } + + const auto& vertex_desc_gles = pipeline.GetBufferBindings(); + + //-------------------------------------------------------------------------- + /// Bind vertex and index buffers. + /// + auto vertex_buffer_view = command.GetVertexBuffer(); + auto index_buffer_view = command.index_buffer; + + if (!vertex_buffer_view || !index_buffer_view) { + return false; + } + + auto vertex_buffer = + vertex_buffer_view.buffer->GetDeviceBuffer(*transients_allocator); + auto index_buffer = + index_buffer_view.buffer->GetDeviceBuffer(*transients_allocator); + + if (!vertex_buffer || !index_buffer) { + return false; + } + + const auto& vertex_buffer_gles = DeviceBufferGLES::Cast(*vertex_buffer); + if (!vertex_buffer_gles.BindAndUploadDataIfNecessary( + DeviceBufferGLES::BindingType::kArrayBuffer)) { + return false; + } + const auto& index_buffer_gles = DeviceBufferGLES::Cast(*index_buffer); + if (!index_buffer_gles.BindAndUploadDataIfNecessary( + DeviceBufferGLES::BindingType::kElementArrayBuffer)) { + return false; + } + + //-------------------------------------------------------------------------- + /// Bind the pipeline program. + /// + if (!pipeline.BindProgram()) { + return false; + } + + //-------------------------------------------------------------------------- + /// Bind vertex attribs. + /// + if (!vertex_desc_gles->BindVertexAttributes( + gl, vertex_buffer_view.range.offset)) { + return false; + } + + //-------------------------------------------------------------------------- + /// Bind uniform data. + /// + if (!vertex_desc_gles->BindUniformData(gl, // + *transients_allocator, // + command.vertex_bindings, // + command.fragment_bindings // + )) { + return false; + } + + //-------------------------------------------------------------------------- + /// Finally! Invoke the draw call. + /// + gl.DrawElements(ToMode(command.primitive_type), // mode + command.index_count, // count + ToIndexType(command.index_type), // type + reinterpret_cast(static_cast( + index_buffer_view.range.offset)) // indices + ); + + //-------------------------------------------------------------------------- + /// Unbind vertex attribs. + /// + if (!vertex_desc_gles->UnbindVertexAttributes(gl)) { + return false; + } + + //-------------------------------------------------------------------------- + /// Unbind the program pipeline. + /// + if (!pipeline.UnbindProgram()) { + return false; + } + } + + // TODO(csg): Respect the discard flags using glDiscardFramebuffer. Vital for + // mobile GPUs. + + return true; +} + +// |RenderPass| +bool RenderPassGLES::EncodeCommands( + const std::shared_ptr& transients_allocator) const { + if (!IsValid()) { + return false; + } + if (commands_.empty()) { + return true; + } + const auto& render_target = GetRenderTarget(); + if (!render_target.HasColorAttachment(0u)) { + return false; + } + const auto& color0 = render_target.GetColorAttachments().at(0u); + const auto& depth0 = render_target.GetDepthAttachment(); + const auto& stencil0 = render_target.GetStencilAttachment(); + + auto pass_data = std::make_shared(); + pass_data->viewport.rect = Rect::MakeSize(Size(GetRenderTargetSize())); + + //---------------------------------------------------------------------------- + /// Setup color data. + /// + pass_data->clear_color = color0.clear_color; + pass_data->clear_color_attachment = CanClearAttachment(color0.load_action); + pass_data->discard_color_attachment = + CanDiscardAttachmentWhenDone(color0.store_action); + + //---------------------------------------------------------------------------- + /// Setup depth data. + /// + if (depth0.has_value()) { + pass_data->has_depth_attachment = true; + pass_data->clear_depth = depth0->clear_depth; + pass_data->clear_depth_attachment = CanClearAttachment(depth0->load_action); + pass_data->discard_depth_attachment = + CanDiscardAttachmentWhenDone(depth0->store_action); + } + + //---------------------------------------------------------------------------- + /// Setup depth data. + /// + if (stencil0.has_value()) { + pass_data->has_stencil_attachment = true; + pass_data->clear_stencil = stencil0->clear_stencil; + pass_data->clear_stencil_attachment = + CanClearAttachment(stencil0->load_action); + pass_data->discard_stencil_attachment = + CanDiscardAttachmentWhenDone(stencil0->store_action); + } + + return reactor_->AddOperation([pass_data, transients_allocator, + commands = commands_](const auto& reactor) { + auto result = EncodeCommandsInReactor(*pass_data, transients_allocator, + reactor, commands); + FML_CHECK(result) << "Must be able to encode GL commands without error."; + }); +} } // namespace impeller diff --git a/impeller/renderer/backend/gles/render_pass_gles.h b/impeller/renderer/backend/gles/render_pass_gles.h index e69de29bb2d1d..c376c3cc65460 100644 --- a/impeller/renderer/backend/gles/render_pass_gles.h +++ b/impeller/renderer/backend/gles/render_pass_gles.h @@ -0,0 +1,39 @@ +// 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 "flutter/fml/macros.h" +#include "flutter/impeller/renderer/backend/gles/reactor_gles.h" +#include "flutter/impeller/renderer/render_pass.h" + +namespace impeller { + +class RenderPassGLES final : public RenderPass { + public: + // |RenderPass| + ~RenderPassGLES() override; + + private: + friend class CommandBufferGLES; + + ReactorGLES::Ref reactor_; + bool is_valid_ = false; + + RenderPassGLES(RenderTarget target, ReactorGLES::Ref reactor); + + // |RenderPass| + bool IsValid() const override; + + // |RenderPass| + void SetLabel(std::string label) override; + + // |RenderPass| + bool EncodeCommands( + const std::shared_ptr& transients_allocator) const override; + + FML_DISALLOW_COPY_AND_ASSIGN(RenderPassGLES); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/sampler_gles.cc b/impeller/renderer/backend/gles/sampler_gles.cc index cd8c1ba103add..31f1cb908d05d 100644 --- a/impeller/renderer/backend/gles/sampler_gles.cc +++ b/impeller/renderer/backend/gles/sampler_gles.cc @@ -6,6 +6,12 @@ namespace impeller { -// +SamplerGLES::SamplerGLES() = default; + +SamplerGLES::~SamplerGLES() = default; + +bool SamplerGLES::IsValid() const { + return true; +} } // namespace impeller diff --git a/impeller/renderer/backend/gles/sampler_gles.h b/impeller/renderer/backend/gles/sampler_gles.h index e69de29bb2d1d..1d16082d69f19 100644 --- a/impeller/renderer/backend/gles/sampler_gles.h +++ b/impeller/renderer/backend/gles/sampler_gles.h @@ -0,0 +1,29 @@ +// 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 "flutter/fml/macros.h" +#include "impeller/renderer/sampler.h" + +namespace impeller { + +class SamplerLibraryGLES; + +class SamplerGLES final : public Sampler { + public: + ~SamplerGLES(); + + private: + friend class SamplerLibraryGLES; + + SamplerGLES(); + + // |Sampler| + bool IsValid() const override; + + FML_DISALLOW_COPY_AND_ASSIGN(SamplerGLES); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/sampler_library_gles.cc b/impeller/renderer/backend/gles/sampler_library_gles.cc index bff27cd6e133c..9ee24f8370b30 100644 --- a/impeller/renderer/backend/gles/sampler_library_gles.cc +++ b/impeller/renderer/backend/gles/sampler_library_gles.cc @@ -4,8 +4,25 @@ #include "impeller/renderer/backend/gles/sampler_library_gles.h" +#include "impeller/base/config.h" +#include "impeller/renderer/backend/gles/sampler_gles.h" + namespace impeller { -// +SamplerLibraryGLES::SamplerLibraryGLES() = default; + +// |SamplerLibrary| +SamplerLibraryGLES::~SamplerLibraryGLES() = default; + +// |SamplerLibrary| +std::shared_ptr SamplerLibraryGLES::GetSampler( + SamplerDescriptor descriptor) { + auto found = samplers_.find(descriptor); + if (found != samplers_.end()) { + return found->second; + } + return samplers_[std::move(descriptor)] = + std::shared_ptr(new SamplerGLES()); +} } // namespace impeller diff --git a/impeller/renderer/backend/gles/sampler_library_gles.h b/impeller/renderer/backend/gles/sampler_library_gles.h index e69de29bb2d1d..9bf35d6740ec8 100644 --- a/impeller/renderer/backend/gles/sampler_library_gles.h +++ b/impeller/renderer/backend/gles/sampler_library_gles.h @@ -0,0 +1,32 @@ +// 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 "flutter/fml/macros.h" +#include "impeller/renderer/sampler_descriptor.h" +#include "impeller/renderer/sampler_library.h" + +namespace impeller { + +class SamplerLibraryGLES final : public SamplerLibrary { + public: + // |SamplerLibrary| + ~SamplerLibraryGLES() override; + + private: + friend class ContextGLES; + + SamplerMap samplers_; + + SamplerLibraryGLES(); + + // |SamplerLibrary| + std::shared_ptr GetSampler( + SamplerDescriptor descriptor) override; + + FML_DISALLOW_COPY_AND_ASSIGN(SamplerLibraryGLES); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/shader_function_gles.cc b/impeller/renderer/backend/gles/shader_function_gles.cc index db71e891ef58d..d1ee80d674c5e 100644 --- a/impeller/renderer/backend/gles/shader_function_gles.cc +++ b/impeller/renderer/backend/gles/shader_function_gles.cc @@ -6,6 +6,19 @@ namespace impeller { -// +ShaderFunctionGLES::ShaderFunctionGLES( + UniqueID library_id, + ShaderStage stage, + std::string name, + std::shared_ptr mapping) + : ShaderFunction(library_id, std::move(name), stage), + mapping_(std::move(mapping)) {} + +ShaderFunctionGLES::~ShaderFunctionGLES() = default; + +const std::shared_ptr& +ShaderFunctionGLES::GetSourceMapping() const { + return mapping_; +} } // namespace impeller diff --git a/impeller/renderer/backend/gles/shader_function_gles.h b/impeller/renderer/backend/gles/shader_function_gles.h index e69de29bb2d1d..9658ca046dd2d 100644 --- a/impeller/renderer/backend/gles/shader_function_gles.h +++ b/impeller/renderer/backend/gles/shader_function_gles.h @@ -0,0 +1,40 @@ +// 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/base/backend_cast.h" +#include "impeller/renderer/shader_function.h" + +namespace impeller { + +class ShaderLibraryGLES; + +class ShaderFunctionGLES final + : public ShaderFunction, + public BackendCast { + public: + // |ShaderFunction| + ~ShaderFunctionGLES() override; + + const std::shared_ptr& GetSourceMapping() const; + + private: + friend ShaderLibraryGLES; + + std::shared_ptr mapping_; + + ShaderFunctionGLES(UniqueID library_id, + ShaderStage stage, + std::string name, + std::shared_ptr mapping); + + FML_DISALLOW_COPY_AND_ASSIGN(ShaderFunctionGLES); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/shader_library_gles.cc b/impeller/renderer/backend/gles/shader_library_gles.cc index 7074095de2220..99345f14b0196 100644 --- a/impeller/renderer/backend/gles/shader_library_gles.cc +++ b/impeller/renderer/backend/gles/shader_library_gles.cc @@ -4,8 +4,94 @@ #include "impeller/renderer/backend/gles/shader_library_gles.h" +#include + +#include "impeller/base/config.h" +#include "impeller/base/validation.h" +#include "impeller/blobcat/blob_library.h" +#include "impeller/renderer/backend/gles/shader_function_gles.h" + namespace impeller { -// +static ShaderStage ToShaderStage(Blob::ShaderType type) { + switch (type) { + case Blob::ShaderType::kVertex: + return ShaderStage::kVertex; + case Blob::ShaderType::kFragment: + return ShaderStage::kFragment; + } + FML_UNREACHABLE(); +} + +static std::string GLESShaderNameToShaderKeyName(const std::string& name, + ShaderStage stage) { + std::stringstream stream; + stream << name; + switch (stage) { + case ShaderStage::kUnknown: + stream << "_unknown_"; + break; + case ShaderStage::kVertex: + stream << "_vertex_"; + break; + case ShaderStage::kFragment: + stream << "_fragment_"; + break; + } + stream << "main"; + return stream.str(); +} + +ShaderLibraryGLES::ShaderLibraryGLES( + std::vector> shader_libraries) { + ShaderFunctionMap functions; + UniqueID library_id; + auto iterator = [&functions, &library_id](auto type, // + const auto& name, // + const auto& mapping // + ) -> bool { + const auto stage = ToShaderStage(type); + const auto key_name = GLESShaderNameToShaderKeyName(name, stage); + + functions[ShaderKey{key_name, stage}] = std::shared_ptr( + new ShaderFunctionGLES(library_id, // + stage, // + key_name, // + mapping // + )); + + return true; + }; + for (auto library : shader_libraries) { + auto blob_library = BlobLibrary{std::move(library)}; + if (!blob_library.IsValid()) { + VALIDATION_LOG << "Could not construct blob library for shaders."; + return; + } + blob_library.IterateAllBlobs(iterator); + } + + functions_ = functions; + is_valid_ = true; +} + +// |ShaderLibrary| +ShaderLibraryGLES::~ShaderLibraryGLES() = default; + +// |ShaderLibrary| +bool ShaderLibraryGLES::IsValid() const { + return is_valid_; +} + +// |ShaderLibrary| +std::shared_ptr ShaderLibraryGLES::GetFunction( + const std::string_view& name, + ShaderStage stage) { + const auto key = ShaderKey{name, stage}; + if (auto found = functions_.find(key); found != functions_.end()) { + return found->second; + } + return nullptr; +} } // namespace impeller diff --git a/impeller/renderer/backend/gles/shader_library_gles.h b/impeller/renderer/backend/gles/shader_library_gles.h index fcc3e546c3c26..d81c5661c35d1 100644 --- a/impeller/renderer/backend/gles/shader_library_gles.h +++ b/impeller/renderer/backend/gles/shader_library_gles.h @@ -4,15 +4,17 @@ #pragma once +#include + #include "flutter/fml/macros.h" +#include "flutter/fml/mapping.h" +#include "impeller/renderer/shader_key.h" #include "impeller/renderer/shader_library.h" namespace impeller { class ShaderLibraryGLES final : public ShaderLibrary { public: - ShaderLibraryGLES(); - // |ShaderLibrary| ~ShaderLibraryGLES() override; @@ -21,6 +23,11 @@ class ShaderLibraryGLES final : public ShaderLibrary { private: friend class ContextGLES; + ShaderFunctionMap functions_; + bool is_valid_ = false; + + ShaderLibraryGLES( + std::vector> shader_libraries); // |ShaderLibrary| std::shared_ptr GetFunction( diff --git a/impeller/renderer/backend/gles/surface_gles.cc b/impeller/renderer/backend/gles/surface_gles.cc index b80a2519cb757..b13264d688f68 100644 --- a/impeller/renderer/backend/gles/surface_gles.cc +++ b/impeller/renderer/backend/gles/surface_gles.cc @@ -4,8 +4,73 @@ #include "impeller/renderer/backend/gles/surface_gles.h" +#include "flutter/fml/trace_event.h" +#include "impeller/base/config.h" +#include "impeller/renderer/backend/gles/context_gles.h" +#include "impeller/renderer/backend/gles/texture_gles.h" + namespace impeller { -// +std::unique_ptr SurfaceGLES::WrapFBO(std::shared_ptr context, + SwapCallback swap_callback, + GLuint fbo, + PixelFormat color_format, + ISize fbo_size) { + TRACE_EVENT0("impeller", "SurfaceGLES::WrapOnScreenFBO"); + + if (context == nullptr || !context->IsValid() || !swap_callback) { + return nullptr; + } + + const auto& gl_context = ContextGLES::Cast(*context); + + TextureDescriptor color0_tex; + color0_tex.type = TextureType::kTexture2D; // lies + color0_tex.format = color_format; + color0_tex.size = fbo_size; + color0_tex.usage = static_cast(TextureUsage::kRenderTarget); + color0_tex.sample_count = SampleCount::kCount4; + + ColorAttachment color0; + color0.texture = std::make_shared(gl_context.GetReactor(), + std::move(color0_tex)); + color0.clear_color = Color::DarkSlateGray(); + color0.load_action = LoadAction::kClear; + color0.store_action = StoreAction::kStore; + + TextureDescriptor stencil0_tex; + stencil0_tex.type = TextureType::kTexture2D; // lies + stencil0_tex.format = color_format; + stencil0_tex.size = fbo_size; + stencil0_tex.usage = + static_cast(TextureUsage::kRenderTarget); + stencil0_tex.sample_count = SampleCount::kCount4; + + StencilAttachment stencil0; + stencil0.clear_stencil = 0; + stencil0.texture = std::make_shared(gl_context.GetReactor(), + std::move(stencil0_tex)); + stencil0.load_action = LoadAction::kClear; + stencil0.store_action = StoreAction::kDontCare; + + RenderTarget render_target_desc; + + render_target_desc.SetColorAttachment(color0, 0u); + + return std::unique_ptr( + new SurfaceGLES(std::move(swap_callback), std::move(render_target_desc))); +} + +SurfaceGLES::SurfaceGLES(SwapCallback swap_callback, RenderTarget target_desc) + : Surface(std::move(target_desc)), + swap_callback_(std::move(swap_callback)) {} + +// |Surface| +SurfaceGLES::~SurfaceGLES() = default; + +// |Surface| +bool SurfaceGLES::Present() const { + return swap_callback_ ? swap_callback_() : false; +} } // namespace impeller diff --git a/impeller/renderer/backend/gles/surface_gles.h b/impeller/renderer/backend/gles/surface_gles.h index e69de29bb2d1d..0016c41750af3 100644 --- a/impeller/renderer/backend/gles/surface_gles.h +++ b/impeller/renderer/backend/gles/surface_gles.h @@ -0,0 +1,41 @@ +// 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 "impeller/renderer/backend/gles/gles.h" +#include "impeller/renderer/context.h" +#include "impeller/renderer/surface.h" + +namespace impeller { + +class SurfaceGLES final : public Surface { + public: + using SwapCallback = std::function; + + static std::unique_ptr WrapFBO(std::shared_ptr context, + SwapCallback swap_callback, + GLuint fbo, + PixelFormat color_format, + ISize fbo_size); + + // |Surface| + ~SurfaceGLES() override; + + private: + SwapCallback swap_callback_; + + SurfaceGLES(SwapCallback swap_callback, RenderTarget target_desc); + + // |Surface| + bool Present() const override; + + FML_DISALLOW_COPY_AND_ASSIGN(SurfaceGLES); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/texture_gles.cc b/impeller/renderer/backend/gles/texture_gles.cc index 0ebe05e47658d..fdf8471f55394 100644 --- a/impeller/renderer/backend/gles/texture_gles.cc +++ b/impeller/renderer/backend/gles/texture_gles.cc @@ -4,8 +4,53 @@ #include "impeller/renderer/backend/gles/texture_gles.h" +#include "flutter/fml/mapping.h" +#include "impeller/base/allocation.h" +#include "impeller/base/config.h" + namespace impeller { -// +TextureGLES::TextureGLES(std::shared_ptr reactor, + TextureDescriptor desc) + : Texture(std::move(desc)), + reactor_(reactor), + handle_(reactor_->CreateHandle(HandleType::kTexture)) { + if (!GetTextureDescriptor().IsValid()) { + return; + } + + is_valid_ = true; +} + +// |Texture| +TextureGLES::~TextureGLES() { + reactor_->CollectHandle(std::move(handle_)); +} + +// |Texture| +bool TextureGLES::IsValid() const { + return is_valid_; +} + +// |Texture| +void TextureGLES::SetLabel(const std::string_view& label) { + // Unsupported. +} + +// |Texture| +bool TextureGLES::SetContents(const uint8_t* contents, size_t length) { + if (length == 0u) { + return true; + } + + auto mapping = CreateMappingWithCopy(contents, length); + + return reactor_->AddOperation([mapping](const auto& reactor) {}); +} + +// |Texture| +ISize TextureGLES::GetSize() const { + return GetTextureDescriptor().size; +} } // namespace impeller diff --git a/impeller/renderer/backend/gles/texture_gles.h b/impeller/renderer/backend/gles/texture_gles.h index e69de29bb2d1d..7ffba812c8dd3 100644 --- a/impeller/renderer/backend/gles/texture_gles.h +++ b/impeller/renderer/backend/gles/texture_gles.h @@ -0,0 +1,43 @@ +// 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 "flutter/fml/macros.h" +#include "impeller/renderer/backend/gles/gles_handle.h" +#include "impeller/renderer/backend/gles/reactor_gles.h" +#include "impeller/renderer/texture.h" + +namespace impeller { + +class TextureGLES final : public Texture { + public: + TextureGLES(ReactorGLES::Ref reactor, TextureDescriptor desc); + + // |Texture| + ~TextureGLES() override; + + private: + friend class AllocatorMTL; + + ReactorGLES::Ref reactor_; + GLESHandle handle_; + bool is_valid_ = false; + + // |Texture| + void SetLabel(const std::string_view& label) override; + + // |Texture| + bool SetContents(const uint8_t* contents, size_t length) override; + + // |Texture| + bool IsValid() const override; + + // |Texture| + ISize GetSize() const override; + + FML_DISALLOW_COPY_AND_ASSIGN(TextureGLES); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/gles/vertex_descriptor_gles.h b/impeller/renderer/backend/gles/vertex_descriptor_gles.h deleted file mode 100644 index e69de29bb2d1d..0000000000000 diff --git a/impeller/renderer/backend/metal/BUILD.gn b/impeller/renderer/backend/metal/BUILD.gn new file mode 100644 index 0000000000000..4458a07b41105 --- /dev/null +++ b/impeller/renderer/backend/metal/BUILD.gn @@ -0,0 +1,47 @@ +# 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. + +import("../../../tools/impeller.gni") + +impeller_component("metal") { + sources = [ + "allocator_mtl.h", + "allocator_mtl.mm", + "command_buffer_mtl.h", + "command_buffer_mtl.mm", + "context_mtl.h", + "context_mtl.mm", + "device_buffer_mtl.h", + "device_buffer_mtl.mm", + "formats_mtl.h", + "formats_mtl.mm", + "pipeline_library_mtl.h", + "pipeline_library_mtl.mm", + "pipeline_mtl.h", + "pipeline_mtl.mm", + "render_pass_mtl.h", + "render_pass_mtl.mm", + "sampler_library_mtl.h", + "sampler_library_mtl.mm", + "sampler_mtl.h", + "sampler_mtl.mm", + "shader_function_mtl.h", + "shader_function_mtl.mm", + "shader_library_mtl.h", + "shader_library_mtl.mm", + "surface_mtl.h", + "surface_mtl.mm", + "texture_mtl.h", + "texture_mtl.mm", + "vertex_descriptor_mtl.h", + "vertex_descriptor_mtl.mm", + ] + + public_deps = [ + "../../:renderer", + "//flutter/fml", + ] + + frameworks = [ "Metal.framework" ] +} diff --git a/impeller/renderer/backend/metal/allocator_mtl.h b/impeller/renderer/backend/metal/allocator_mtl.h index fc5d8c170da2b..256ed3a6fc6db 100644 --- a/impeller/renderer/backend/metal/allocator_mtl.h +++ b/impeller/renderer/backend/metal/allocator_mtl.h @@ -42,14 +42,6 @@ class AllocatorMTL final : public Allocator { StorageMode mode, const TextureDescriptor& desc) override; - // |Allocator| - std::shared_ptr CreateBufferWithCopy(const uint8_t* buffer, - size_t length) override; - - // |Allocator| - std::shared_ptr CreateBufferWithCopy( - const fml::Mapping& mapping) override; - FML_DISALLOW_COPY_AND_ASSIGN(AllocatorMTL); }; diff --git a/impeller/renderer/backend/metal/allocator_mtl.mm b/impeller/renderer/backend/metal/allocator_mtl.mm index 20ceb1e2c7910..cd5730e0b0994 100644 --- a/impeller/renderer/backend/metal/allocator_mtl.mm +++ b/impeller/renderer/backend/metal/allocator_mtl.mm @@ -89,29 +89,6 @@ static MTLStorageMode ToMTLStorageMode(StorageMode mode) { new DeviceBufferMTL(buffer, length, mode)); } -std::shared_ptr AllocatorMTL::CreateBufferWithCopy( - const uint8_t* buffer, - size_t length) { - auto new_buffer = CreateBuffer(StorageMode::kHostVisible, length); - - if (!new_buffer) { - return nullptr; - } - - auto entire_range = Range{0, length}; - - if (!new_buffer->CopyHostBuffer(buffer, entire_range)) { - return nullptr; - } - - return new_buffer; -} - -std::shared_ptr AllocatorMTL::CreateBufferWithCopy( - const fml::Mapping& mapping) { - return CreateBufferWithCopy(mapping.GetMapping(), mapping.GetSize()); -} - std::shared_ptr AllocatorMTL::CreateTexture( StorageMode mode, const TextureDescriptor& desc) { diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.h b/impeller/renderer/backend/metal/command_buffer_mtl.h index d2e5ee31bab51..d8514b28b0d1c 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.h +++ b/impeller/renderer/backend/metal/command_buffer_mtl.h @@ -22,7 +22,6 @@ class CommandBufferMTL final : public CommandBuffer { friend class ContextMTL; id buffer_ = nullptr; - bool is_valid_ = false; CommandBufferMTL(id queue); @@ -35,9 +34,6 @@ class CommandBufferMTL final : public CommandBuffer { // |CommandBuffer| bool SubmitCommands(CompletionCallback callback) override; - // |CommandBuffer| - void ReserveSpotInQueue() override; - // |CommandBuffer| std::shared_ptr CreateRenderPass( RenderTarget target) const override; diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm index 7e5cacfb3df17..bee10e81a8c1b 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/command_buffer_mtl.mm @@ -145,17 +145,12 @@ static void LogMTLCommandBufferErrorIfPresent(id buffer) { } CommandBufferMTL::CommandBufferMTL(id queue) - : buffer_(CreateCommandBuffer(queue)) { - if (!buffer_) { - return; - } - is_valid_ = true; -} + : buffer_(CreateCommandBuffer(queue)) {} CommandBufferMTL::~CommandBufferMTL() = default; bool CommandBufferMTL::IsValid() const { - return is_valid_; + return buffer_ != nil; } void CommandBufferMTL::SetLabel(const std::string& label) const { @@ -179,8 +174,8 @@ static void LogMTLCommandBufferErrorIfPresent(id buffer) { } bool CommandBufferMTL::SubmitCommands(CompletionCallback callback) { - if (!buffer_) { - // Already committed. This is caller error. + if (!IsValid()) { + // Already committed or was never valid. Either way, this is caller error. if (callback) { callback(Status::kError); } @@ -200,10 +195,6 @@ static void LogMTLCommandBufferErrorIfPresent(id buffer) { return true; } -void CommandBufferMTL::ReserveSpotInQueue() { - [buffer_ enqueue]; -} - std::shared_ptr CommandBufferMTL::CreateRenderPass( RenderTarget target) const { if (!buffer_) { diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.h b/impeller/renderer/backend/metal/device_buffer_mtl.h index c930626c243dd..e167dd86e66de 100644 --- a/impeller/renderer/backend/metal/device_buffer_mtl.h +++ b/impeller/renderer/backend/metal/device_buffer_mtl.h @@ -27,8 +27,6 @@ class DeviceBufferMTL final friend class AllocatorMTL; const id buffer_; - const size_t size_; - const StorageMode mode_; DeviceBufferMTL(id buffer, size_t size, StorageMode mode); @@ -37,23 +35,12 @@ class DeviceBufferMTL final Range source_range, size_t offset) override; - // |DeviceBuffer| - std::shared_ptr MakeTexture(TextureDescriptor desc, - size_t offset) const override; - // |DeviceBuffer| bool SetLabel(const std::string& label) override; // |DeviceBuffer| bool SetLabel(const std::string& label, Range range) override; - // |DeviceBuffer| - BufferView AsBufferView() const override; - - // |Buffer| - std::shared_ptr GetDeviceBuffer( - Allocator& allocator) const override; - FML_DISALLOW_COPY_AND_ASSIGN(DeviceBufferMTL); }; diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.mm b/impeller/renderer/backend/metal/device_buffer_mtl.mm index 58e74fefd8ffe..a25248a517bfe 100644 --- a/impeller/renderer/backend/metal/device_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/device_buffer_mtl.mm @@ -14,7 +14,7 @@ DeviceBufferMTL::DeviceBufferMTL(id buffer, size_t size, StorageMode mode) - : buffer_(buffer), size_(size), mode_(mode) {} + : DeviceBuffer(size, mode), buffer_(buffer) {} DeviceBufferMTL::~DeviceBufferMTL() = default; @@ -22,33 +22,6 @@ return buffer_; } -std::shared_ptr DeviceBufferMTL::MakeTexture(TextureDescriptor desc, - size_t offset) const { - if (!desc.IsValid() || !buffer_) { - return nullptr; - } - - // Avoid overruns. - if (offset + desc.GetByteSizeOfBaseMipLevel() > size_) { - VALIDATION_LOG << "Avoiding buffer overrun when creating texture."; - return nullptr; - } - - if (@available(macOS 10.13, *)) { - auto texture = - [buffer_ newTextureWithDescriptor:ToMTLTextureDescriptor(desc) - offset:offset - bytesPerRow:desc.GetBytesPerRow()]; - if (!texture) { - return nullptr; - } - - return std::make_shared(desc, texture); - } else { - return nullptr; - } -} - [[nodiscard]] bool DeviceBufferMTL::CopyHostBuffer(const uint8_t* source, Range source_range, size_t offset) { @@ -88,12 +61,6 @@ return true; } -// |Buffer| -std::shared_ptr DeviceBufferMTL::GetDeviceBuffer( - Allocator& allocator) const { - return shared_from_this(); -} - bool DeviceBufferMTL::SetLabel(const std::string& label) { if (label.empty()) { return false; @@ -113,11 +80,4 @@ return true; } -BufferView DeviceBufferMTL::AsBufferView() const { - BufferView view; - view.buffer = shared_from_this(); - view.range = {0u, size_}; - return view; -} - } // namespace impeller diff --git a/impeller/renderer/backend/metal/formats_mtl.h b/impeller/renderer/backend/metal/formats_mtl.h index 8fb4fe2040d33..ff92522679891 100644 --- a/impeller/renderer/backend/metal/formats_mtl.h +++ b/impeller/renderer/backend/metal/formats_mtl.h @@ -140,10 +140,6 @@ constexpr MTLBlendOperation ToMTLBlendOperation(BlendOperation type) { return MTLBlendOperationSubtract; case BlendOperation::kReverseSubtract: return MTLBlendOperationReverseSubtract; - case BlendOperation::kMin: - return MTLBlendOperationMin; - case BlendOperation::kMax: - return MTLBlendOperationMax; } return MTLBlendOperationAdd; }; diff --git a/impeller/renderer/backend/metal/pipeline_library_mtl.h b/impeller/renderer/backend/metal/pipeline_library_mtl.h index 41c62da6a77c9..0f941ca6c18f0 100644 --- a/impeller/renderer/backend/metal/pipeline_library_mtl.h +++ b/impeller/renderer/backend/metal/pipeline_library_mtl.h @@ -6,9 +6,6 @@ #include -#include -#include - #include "flutter/fml/macros.h" #include "impeller/renderer/pipeline_library.h" @@ -26,13 +23,8 @@ class PipelineLibraryMTL final : public PipelineLibrary { private: friend ContextMTL; - using Pipelines = - std::unordered_map>, - ComparableHash, - ComparableEqual>; id device_ = nullptr; - Pipelines pipelines_; + PipelineMap pipelines_; PipelineLibraryMTL(id device); diff --git a/impeller/renderer/backend/metal/pipeline_library_mtl.mm b/impeller/renderer/backend/metal/pipeline_library_mtl.mm index e677c931f44ea..66645fc8ba25a 100644 --- a/impeller/renderer/backend/metal/pipeline_library_mtl.mm +++ b/impeller/renderer/backend/metal/pipeline_library_mtl.mm @@ -4,6 +4,7 @@ #include "impeller/renderer/backend/metal/pipeline_library_mtl.h" +#include "impeller/base/promise.h" #include "impeller/renderer/backend/metal/formats_mtl.h" #include "impeller/renderer/backend/metal/pipeline_mtl.h" #include "impeller/renderer/backend/metal/shader_function_mtl.h" @@ -73,11 +74,13 @@ return found->second; } + if (device_ == nil) { + return RealizedFuture>(nullptr); + } + auto promise = std::make_shared>>(); auto future = PipelineFuture{promise->get_future()}; - pipelines_[descriptor] = future; - auto weak_this = weak_from_this(); auto completion_handler = diff --git a/impeller/renderer/backend/metal/pipeline_mtl.h b/impeller/renderer/backend/metal/pipeline_mtl.h index 238fae7944caf..8b0a8b0e150ad 100644 --- a/impeller/renderer/backend/metal/pipeline_mtl.h +++ b/impeller/renderer/backend/metal/pipeline_mtl.h @@ -15,7 +15,7 @@ namespace impeller { class PipelineMTL final : public Pipeline, public BackendCast { public: - // |PipelineMTL| + // |Pipeline| ~PipelineMTL() override; id GetMTLRenderPipelineState() const; @@ -25,7 +25,6 @@ class PipelineMTL final : public Pipeline, private: friend class PipelineLibraryMTL; - Type type_ = Type::kUnknown; id pipeline_state_; id depth_stencil_state_; bool is_valid_ = false; @@ -35,7 +34,7 @@ class PipelineMTL final : public Pipeline, id state, id depth_stencil_state); - // |PipelineMTL| + // |Pipeline| bool IsValid() const override; FML_DISALLOW_COPY_AND_ASSIGN(PipelineMTL); diff --git a/impeller/renderer/backend/metal/pipeline_mtl.mm b/impeller/renderer/backend/metal/pipeline_mtl.mm index 5e8717f00edcc..9fcbe324f3870 100644 --- a/impeller/renderer/backend/metal/pipeline_mtl.mm +++ b/impeller/renderer/backend/metal/pipeline_mtl.mm @@ -16,7 +16,6 @@ if (!pipeline_state_) { return; } - type_ = Type::kRender; is_valid_ = true; } diff --git a/impeller/renderer/backend/metal/render_pass_mtl.h b/impeller/renderer/backend/metal/render_pass_mtl.h index d177c688cc613..7e5e5a42f3c7c 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.h +++ b/impeller/renderer/backend/metal/render_pass_mtl.h @@ -22,8 +22,6 @@ class RenderPassMTL final : public RenderPass { id buffer_ = nil; MTLRenderPassDescriptor* desc_ = nil; - std::vector commands_; - std::shared_ptr transients_buffer_; std::string label_; bool is_valid_ = false; @@ -36,15 +34,10 @@ class RenderPassMTL final : public RenderPass { void SetLabel(std::string label) override; // |RenderPass| - HostBuffer& GetTransientsBuffer() override; + bool EncodeCommands( + const std::shared_ptr& transients_allocator) const override; - // |RenderPass| - bool AddCommand(Command command) override; - - // |RenderPass| - bool EncodeCommands(Allocator& transients_allocator) const override; - - bool EncodeCommands(Allocator& transients_allocator, + bool EncodeCommands(const std::shared_ptr& transients_allocator, id pass) const; FML_DISALLOW_COPY_AND_ASSIGN(RenderPassMTL); diff --git a/impeller/renderer/backend/metal/render_pass_mtl.mm b/impeller/renderer/backend/metal/render_pass_mtl.mm index 897c40a72bb4c..a7d4780e9c511 100644 --- a/impeller/renderer/backend/metal/render_pass_mtl.mm +++ b/impeller/renderer/backend/metal/render_pass_mtl.mm @@ -131,8 +131,7 @@ static bool ConfigureStencilAttachment( RenderPassMTL::RenderPassMTL(id buffer, RenderTarget target) : RenderPass(std::move(target)), buffer_(buffer), - desc_(ToMTLRenderPassDescriptor(GetRenderTarget())), - transients_buffer_(HostBuffer::Create()) { + desc_(ToMTLRenderPassDescriptor(GetRenderTarget())) { if (!buffer_ || !desc_ || !render_target_.IsValid()) { return; } @@ -142,10 +141,6 @@ static bool ConfigureStencilAttachment( RenderPassMTL::~RenderPassMTL() = default; -HostBuffer& RenderPassMTL::GetTransientsBuffer() { - return *transients_buffer_; -} - bool RenderPassMTL::IsValid() const { return is_valid_; } @@ -158,7 +153,8 @@ static bool ConfigureStencilAttachment( transients_buffer_->SetLabel(SPrintF("%s Transients", label_.c_str())); } -bool RenderPassMTL::EncodeCommands(Allocator& transients_allocator) const { +bool RenderPassMTL::EncodeCommands( + const std::shared_ptr& transients_allocator) const { TRACE_EVENT0("impeller", "RenderPassMTL::EncodeCommands"); if (!IsValid()) { return false; @@ -368,24 +364,27 @@ static bool Bind(PassBindingsCache& pass, SamplerMTL::Cast(sampler).GetMTLSamplerState()); } -bool RenderPassMTL::EncodeCommands(Allocator& allocator, +bool RenderPassMTL::EncodeCommands(const std::shared_ptr& allocator, id encoder) const { PassBindingsCache pass_bindings(encoder); auto bind_stage_resources = [&allocator, &pass_bindings]( const Bindings& bindings, ShaderStage stage) -> bool { for (const auto& buffer : bindings.buffers) { - if (!Bind(pass_bindings, allocator, stage, buffer.first, buffer.second)) { + if (!Bind(pass_bindings, *allocator, stage, buffer.first, + buffer.second.resource)) { return false; } } for (const auto& texture : bindings.textures) { - if (!Bind(pass_bindings, stage, texture.first, *texture.second)) { + if (!Bind(pass_bindings, stage, texture.first, + *texture.second.resource)) { return false; } } for (const auto& sampler : bindings.samplers) { - if (!Bind(pass_bindings, stage, sampler.first, *sampler.second)) { + if (!Bind(pass_bindings, stage, sampler.first, + *sampler.second.resource)) { return false; } } @@ -438,8 +437,8 @@ static bool Bind(PassBindingsCache& pass, .originY = v.rect.origin.y, .width = v.rect.size.width, .height = v.rect.size.height, - .znear = v.znear, - .zfar = v.zfar, + .znear = v.depth_range.z_near, + .zfar = v.depth_range.z_far, }; [encoder setViewport:viewport]; } @@ -467,7 +466,7 @@ static bool Bind(PassBindingsCache& pass, if (!index_buffer) { return false; } - auto device_buffer = index_buffer->GetDeviceBuffer(allocator); + auto device_buffer = index_buffer->GetDeviceBuffer(*allocator); if (!device_buffer) { return false; } @@ -492,35 +491,4 @@ static bool Bind(PassBindingsCache& pass, return true; } -bool RenderPassMTL::AddCommand(Command command) { - if (!command) { - VALIDATION_LOG << "Attempted to add an invalid command to the render pass."; - return false; - } - - if (command.scissor.has_value()) { - auto target_rect = IRect({}, render_target_.GetRenderTargetSize()); - if (!target_rect.Contains(command.scissor.value())) { - VALIDATION_LOG << "Cannot apply a scissor that lies outside the bounds " - "of the render target."; - return false; - } - } - - if (command.index_count == 0u) { - // Essentially a no-op. Don't record the command but this is not necessary - // an error either. - return true; - } - - if (command.instance_count == 0u) { - // Essentially a no-op. Don't record the command but this is not necessary - // an error either. - return true; - } - - commands_.emplace_back(std::move(command)); - return true; -} - } // namespace impeller diff --git a/impeller/renderer/backend/metal/sampler_library_mtl.h b/impeller/renderer/backend/metal/sampler_library_mtl.h index e4c3f99aaa8e3..74297ebcb5320 100644 --- a/impeller/renderer/backend/metal/sampler_library_mtl.h +++ b/impeller/renderer/backend/metal/sampler_library_mtl.h @@ -7,7 +7,6 @@ #include #include -#include #include "flutter/fml/macros.h" #include "impeller/base/backend_cast.h" @@ -27,12 +26,8 @@ class SamplerLibraryMTL final private: friend class ContextMTL; - using CachedSamplers = std::unordered_map, - ComparableHash, - ComparableEqual>; id device_ = nullptr; - CachedSamplers samplers_; + SamplerMap samplers_; SamplerLibraryMTL(id device); diff --git a/impeller/renderer/backend/metal/shader_library_mtl.h b/impeller/renderer/backend/metal/shader_library_mtl.h index ec9abbedebcf0..53ac7a407115a 100644 --- a/impeller/renderer/backend/metal/shader_library_mtl.h +++ b/impeller/renderer/backend/metal/shader_library_mtl.h @@ -13,6 +13,7 @@ #include "flutter/fml/macros.h" #include "impeller/base/comparable.h" +#include "impeller/renderer/shader_key.h" #include "impeller/renderer/shader_library.h" namespace impeller { @@ -30,35 +31,9 @@ class ShaderLibraryMTL final : public ShaderLibrary { private: friend class ContextMTL; - struct ShaderKey { - std::string name; - ShaderStage stage = ShaderStage::kUnknown; - - ShaderKey(const std::string_view& p_name, ShaderStage p_stage) - : name({p_name.data(), p_name.size()}), stage(p_stage) {} - - struct Hash { - size_t operator()(const ShaderKey& key) const { - return fml::HashCombine(key.name, key.stage); - } - }; - - struct Equal { - constexpr bool operator()(const ShaderKey& k1, - const ShaderKey& k2) const { - return k1.stage == k2.stage && k1.name == k2.name; - } - }; - }; - - using Functions = std::unordered_map, - ShaderKey::Hash, - ShaderKey::Equal>; - UniqueID library_id_; NSArray>* libraries_ = nullptr; - Functions functions_; + ShaderFunctionMap functions_; bool is_valid_ = false; ShaderLibraryMTL(NSArray>* libraries); diff --git a/impeller/renderer/backend/metal/surface_mtl.h b/impeller/renderer/backend/metal/surface_mtl.h index ae6cd8da0a32a..993ca65c63843 100644 --- a/impeller/renderer/backend/metal/surface_mtl.h +++ b/impeller/renderer/backend/metal/surface_mtl.h @@ -40,14 +40,14 @@ class SurfaceMTL final : public Surface { // |Surface| ~SurfaceMTL() override; - // |Surface| - bool Present() const override; - private: id drawable_ = nil; SurfaceMTL(RenderTarget target, id drawable); + // |Surface| + bool Present() const override; + FML_DISALLOW_COPY_AND_ASSIGN(SurfaceMTL); }; diff --git a/impeller/renderer/backend/metal/surface_mtl.mm b/impeller/renderer/backend/metal/surface_mtl.mm index 42826b1b9e54b..0e391544f7eca 100644 --- a/impeller/renderer/backend/metal/surface_mtl.mm +++ b/impeller/renderer/backend/metal/surface_mtl.mm @@ -93,13 +93,13 @@ stencil0.load_action = LoadAction::kClear; stencil0.store_action = StoreAction::kDontCare; - RenderTarget desc; - desc.SetColorAttachment(color0, 0u); - desc.SetStencilAttachment(stencil0); + RenderTarget render_target_desc; + render_target_desc.SetColorAttachment(color0, 0u); + render_target_desc.SetStencilAttachment(stencil0); // The constructor is private. So make_unique may not be used. return std::unique_ptr( - new SurfaceMTL(std::move(desc), current_drawable)); + new SurfaceMTL(std::move(render_target_desc), current_drawable)); } SurfaceMTL::SurfaceMTL(RenderTarget target, id drawable) diff --git a/impeller/renderer/backend/metal/texture_mtl.h b/impeller/renderer/backend/metal/texture_mtl.h index 02e9cbb4d3e01..389732d0c3a99 100644 --- a/impeller/renderer/backend/metal/texture_mtl.h +++ b/impeller/renderer/backend/metal/texture_mtl.h @@ -20,6 +20,12 @@ class TextureMTL final : public Texture, // |Texture| ~TextureMTL() override; + id GetMTLTexture() const; + + private: + id texture_ = nullptr; + bool is_valid_ = false; + // |Texture| void SetLabel(const std::string_view& label) override; @@ -32,12 +38,6 @@ class TextureMTL final : public Texture, // |Texture| ISize GetSize() const override; - id GetMTLTexture() const; - - private: - id texture_ = nullptr; - bool is_valid_ = false; - FML_DISALLOW_COPY_AND_ASSIGN(TextureMTL); }; diff --git a/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm b/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm index fec416b0041e9..c74ba74a49e48 100644 --- a/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm +++ b/impeller/renderer/backend/metal/vertex_descriptor_mtl.mm @@ -218,20 +218,20 @@ static MTLVertexFormat ReadStageInputFormat(const ShaderStageIOSlot& input) { const size_t vertex_buffer_index = VertexDescriptor::kReservedVertexBufferIndex; - size_t stride = 0u; + size_t offset = 0u; for (const auto& input : stage_inputs_) { auto attrib = descriptor.attributes[input.location]; attrib.format = input.format; - attrib.offset = stride; + attrib.offset = offset; // All vertex inputs are interleaved and tightly packed in one buffer at a // reserved index. attrib.bufferIndex = vertex_buffer_index; - stride += input.length; + offset += input.length; } // Since it's all in one buffer, indicate its layout. auto vertex_layout = descriptor.layouts[vertex_buffer_index]; - vertex_layout.stride = stride; + vertex_layout.stride = offset; vertex_layout.stepRate = 1u; vertex_layout.stepFunction = MTLVertexStepFunctionPerVertex; diff --git a/impeller/renderer/command.cc b/impeller/renderer/command.cc index 13bf4d69b61d1..894e196f51910 100644 --- a/impeller/renderer/command.cc +++ b/impeller/renderer/command.cc @@ -16,25 +16,37 @@ bool Command::BindVertices(const VertexBuffer& buffer) { return false; } - vertex_bindings.buffers[VertexDescriptor::kReservedVertexBufferIndex] = - buffer.vertex_buffer; + vertex_bindings.buffers[VertexDescriptor::kReservedVertexBufferIndex] = { + nullptr, buffer.vertex_buffer}; index_buffer = buffer.index_buffer; index_count = buffer.index_count; index_type = buffer.index_type; return true; } -bool Command::BindResource(ShaderStage stage, size_t binding, BufferView view) { +BufferView Command::GetVertexBuffer() const { + auto found = vertex_bindings.buffers.find( + VertexDescriptor::kReservedVertexBufferIndex); + if (found != vertex_bindings.buffers.end()) { + return found->second.resource; + } + return {}; +} + +bool Command::BindResource(ShaderStage stage, + const ShaderUniformSlot& slot, + const ShaderMetadata& metadata, + BufferView view) { if (!view) { return false; } switch (stage) { case ShaderStage::kVertex: - vertex_bindings.buffers[binding] = view; + vertex_bindings.buffers[slot.binding] = {&metadata, view}; return true; case ShaderStage::kFragment: - fragment_bindings.buffers[binding] = view; + fragment_bindings.buffers[slot.binding] = {&metadata, view}; return true; case ShaderStage::kUnknown: return false; @@ -45,6 +57,7 @@ bool Command::BindResource(ShaderStage stage, size_t binding, BufferView view) { bool Command::BindResource(ShaderStage stage, const SampledImageSlot& slot, + const ShaderMetadata& metadata, std::shared_ptr texture) { if (!texture || !texture->IsValid()) { return false; @@ -56,10 +69,10 @@ bool Command::BindResource(ShaderStage stage, switch (stage) { case ShaderStage::kVertex: - vertex_bindings.textures[slot.texture_index] = texture; + vertex_bindings.textures[slot.texture_index] = {&metadata, texture}; return true; case ShaderStage::kFragment: - fragment_bindings.textures[slot.texture_index] = texture; + fragment_bindings.textures[slot.texture_index] = {&metadata, texture}; return true; case ShaderStage::kUnknown: return false; @@ -70,6 +83,7 @@ bool Command::BindResource(ShaderStage stage, bool Command::BindResource(ShaderStage stage, const SampledImageSlot& slot, + const ShaderMetadata& metadata, std::shared_ptr sampler) { if (!sampler || !sampler->IsValid()) { return false; @@ -81,10 +95,10 @@ bool Command::BindResource(ShaderStage stage, switch (stage) { case ShaderStage::kVertex: - vertex_bindings.samplers[slot.sampler_index] = sampler; + vertex_bindings.samplers[slot.sampler_index] = {&metadata, sampler}; return true; case ShaderStage::kFragment: - fragment_bindings.samplers[slot.sampler_index] = sampler; + fragment_bindings.samplers[slot.sampler_index] = {&metadata, sampler}; return true; case ShaderStage::kUnknown: return false; @@ -95,10 +109,11 @@ bool Command::BindResource(ShaderStage stage, bool Command::BindResource(ShaderStage stage, const SampledImageSlot& slot, + const ShaderMetadata& metadata, std::shared_ptr texture, std::shared_ptr sampler) { - return BindResource(stage, slot, texture) && - BindResource(stage, slot, sampler); + return BindResource(stage, slot, metadata, texture) && + BindResource(stage, slot, metadata, sampler); } } // namespace impeller diff --git a/impeller/renderer/command.h b/impeller/renderer/command.h index d0299c4d5d31a..554a06fd331b5 100644 --- a/impeller/renderer/command.h +++ b/impeller/renderer/command.h @@ -24,10 +24,26 @@ namespace impeller { +template +struct Resource { + using ResourceType = T; + const ShaderMetadata* isa; + ResourceType resource; + + Resource() : isa(nullptr) {} + + Resource(const ShaderMetadata* p_isa, ResourceType p_resource) + : isa(p_isa), resource(p_resource) {} +}; + +using BufferResource = Resource; +using TextureResource = Resource>; +using SamplerResource = Resource>; + struct Bindings { - std::map buffers; - std::map> textures; - std::map> samplers; + std::map buffers; + std::map textures; + std::map samplers; }; //------------------------------------------------------------------------------ @@ -65,13 +81,53 @@ struct Command { /// setting this directly, it usually easier to specify the vertex and index /// buffer bindings directly via a single call to `BindVertices`. /// + /// @see `BindVertices` + /// BufferView index_buffer; + //---------------------------------------------------------------------------- + /// The number of indices to use from the index buffer. Set the vertex and + /// index buffers as well as the index count using a call to `BindVertices`. + /// + /// @see `BindVertices` + /// size_t index_count = 0u; + //---------------------------------------------------------------------------- + /// The type of indices in the index buffer. The indices must be tightly + /// packed in the index buffer. + /// IndexType index_type = IndexType::kUnknown; + //---------------------------------------------------------------------------- + /// The debugging label to use for the command. + /// std::string label; + //---------------------------------------------------------------------------- + /// The type of primitives in the vertex buffer. Set the vertex and index + /// buffers using a call to `BindVertices`. + /// + /// @see `BindVertices` + /// PrimitiveType primitive_type = PrimitiveType::kTriangle; + //---------------------------------------------------------------------------- + /// The orientation of vertices of the front-facing polygons. This usually + /// matters when culling is enabled. + /// + /// @see `cull_mode` + /// WindingOrder winding = WindingOrder::kClockwise; + //---------------------------------------------------------------------------- + /// How to control culling of polygons. The orientation of front-facing + /// polygons is controlled via the `winding` parameter. + /// + /// @see `winding` + /// CullMode cull_mode = CullMode::kNone; + //---------------------------------------------------------------------------- + /// The reference value to use in stenciling operations. Stencil configuration + /// is part of pipeline setup and can be read from the pipelines descriptor. + /// + /// @see `Pipeline` + /// @see `PipelineDescriptor` + /// uint32_t stencil_reference = 0u; //---------------------------------------------------------------------------- /// The offset used when indexing into the vertex buffer. @@ -90,32 +146,47 @@ struct Command { /// If unset, no scissor is applied. /// std::optional scissor; + //---------------------------------------------------------------------------- + /// The number of instances of the given set of vertices to render. Not all + /// backends support rendering more than one instance at a time. + /// + /// @warning Setting this to more than one will limit the availability of + /// backends to use with this command. + /// size_t instance_count = 1u; + //---------------------------------------------------------------------------- + /// @brief Specify the vertex and index buffer to use for this command. + /// + /// @param[in] buffer The vertex and index buffer definition. + /// + /// @return returns if the binding was updated. + /// bool BindVertices(const VertexBuffer& buffer); - template bool BindResource(ShaderStage stage, - const ShaderUniformSlot slot, - BufferView view) { - return BindResource(stage, slot.binding, std::move(view)); - } - - bool BindResource(ShaderStage stage, size_t binding, BufferView view); + const ShaderUniformSlot& slot, + const ShaderMetadata& metadata, + BufferView view); bool BindResource(ShaderStage stage, const SampledImageSlot& slot, + const ShaderMetadata& metadata, std::shared_ptr texture); bool BindResource(ShaderStage stage, const SampledImageSlot& slot, + const ShaderMetadata& metadata, std::shared_ptr sampler); bool BindResource(ShaderStage stage, const SampledImageSlot& slot, + const ShaderMetadata& metadata, std::shared_ptr texture, std::shared_ptr sampler); + BufferView GetVertexBuffer() const; + constexpr operator bool() const { return pipeline && pipeline->IsValid(); } }; diff --git a/impeller/renderer/command_buffer.h b/impeller/renderer/command_buffer.h index 2056b048cb891..c76f127794698 100644 --- a/impeller/renderer/command_buffer.h +++ b/impeller/renderer/command_buffer.h @@ -62,8 +62,6 @@ class CommandBuffer { [[nodiscard]] bool SubmitCommands(); - virtual void ReserveSpotInQueue() = 0; - //---------------------------------------------------------------------------- /// @brief Create a render pass to record render commands into. /// diff --git a/impeller/renderer/device_buffer.cc b/impeller/renderer/device_buffer.cc index 7fd7e74cfc0d3..3346b373def89 100644 --- a/impeller/renderer/device_buffer.cc +++ b/impeller/renderer/device_buffer.cc @@ -6,8 +6,22 @@ namespace impeller { -DeviceBuffer::DeviceBuffer() = default; +DeviceBuffer::DeviceBuffer(size_t size, StorageMode mode) + : size_(size), mode_(mode) {} DeviceBuffer::~DeviceBuffer() = default; +// |Buffer| +std::shared_ptr DeviceBuffer::GetDeviceBuffer( + Allocator& allocator) const { + return shared_from_this(); +} + +BufferView DeviceBuffer::AsBufferView() const { + BufferView view; + view.buffer = shared_from_this(); + view.range = {0u, size_}; + return view; +} + } // namespace impeller diff --git a/impeller/renderer/device_buffer.h b/impeller/renderer/device_buffer.h index e9d1130200650..8c0a13424d16c 100644 --- a/impeller/renderer/device_buffer.h +++ b/impeller/renderer/device_buffer.h @@ -25,29 +25,21 @@ class DeviceBuffer : public Buffer, Range source_range, size_t offset = 0u) = 0; - //---------------------------------------------------------------------------- - /// @brief Create a texture whose contents are the same as that of this - /// buffer. Changes to either the contents of the texture or the - /// buffer will be shared. When using buffer backed textures, - /// implementations may have to disable certain optimizations. - /// - /// @param[in] desc The description of the texture. - /// @param[in] offset The offset of the texture data within buffer. - /// - /// @return The texture whose contents are backed by (a part of) this - /// buffer. - /// - virtual std::shared_ptr MakeTexture(TextureDescriptor desc, - size_t offset = 0u) const = 0; - virtual bool SetLabel(const std::string& label) = 0; virtual bool SetLabel(const std::string& label, Range range) = 0; - virtual BufferView AsBufferView() const = 0; + BufferView AsBufferView() const; + + // |Buffer| + std::shared_ptr GetDeviceBuffer( + Allocator& allocator) const; protected: - DeviceBuffer(); + const size_t size_; + const StorageMode mode_; + + DeviceBuffer(size_t size, StorageMode mode); private: FML_DISALLOW_COPY_AND_ASSIGN(DeviceBuffer); diff --git a/impeller/renderer/formats.h b/impeller/renderer/formats.h index f1083e3e7c459..b77bc129e1da0 100644 --- a/impeller/renderer/formats.h +++ b/impeller/renderer/formats.h @@ -10,6 +10,7 @@ #include #include "flutter/fml/hash_combine.h" +#include "flutter/fml/logging.h" #include "flutter/fml/macros.h" #include "impeller/geometry/color.h" #include "impeller/geometry/rect.h" @@ -86,8 +87,6 @@ enum class BlendOperation { kAdd, kSubtract, kReverseSubtract, - kMin, - kMax, }; enum class LoadAction { @@ -102,6 +101,28 @@ enum class StoreAction { kMultisampleResolve, }; +constexpr bool CanClearAttachment(LoadAction action) { + switch (action) { + case LoadAction::kLoad: + return false; + case LoadAction::kDontCare: + case LoadAction::kClear: + return true; + } + FML_UNREACHABLE(); +} + +constexpr bool CanDiscardAttachmentWhenDone(StoreAction action) { + switch (action) { + case StoreAction::kStore: + return false; + case StoreAction::kDontCare: + case StoreAction::kMultisampleResolve: + return true; + } + FML_UNREACHABLE(); +} + enum class TextureType { kTexture2D, kTexture2DMultisample, @@ -153,17 +174,21 @@ enum class PrimitiveType { // checks. Hence, they are not supported here. }; +struct DepthRange { + Scalar z_near = 0.0; + Scalar z_far = 1.0; +}; + struct Viewport { Rect rect; - Scalar znear = 0.0f; - Scalar zfar = 1.0f; + DepthRange depth_range; }; enum class MinMagFilter { /// Select nearest to the sample point. Most widely supported. kNearest, - /// Select two points and linearly interpolate between them. Some formats may - /// not support this. + /// Select two points and linearly interpolate between them. Some formats + /// may not support this. kLinear, }; diff --git a/impeller/renderer/pipeline.h b/impeller/renderer/pipeline.h index 47df168d8b1dd..16be034562d69 100644 --- a/impeller/renderer/pipeline.h +++ b/impeller/renderer/pipeline.h @@ -39,11 +39,6 @@ using PipelineFuture = std::shared_future>; /// class Pipeline { public: - enum class Type { - kUnknown, - kRender, - }; - virtual ~Pipeline(); virtual bool IsValid() const = 0; diff --git a/impeller/renderer/pipeline_descriptor.cc b/impeller/renderer/pipeline_descriptor.cc index ca0a06ea2e602..54e49d1f1d38a 100644 --- a/impeller/renderer/pipeline_descriptor.cc +++ b/impeller/renderer/pipeline_descriptor.cc @@ -107,6 +107,15 @@ PipelineDescriptor::GetColorAttachmentDescriptor(size_t index) const { : &found->second; } +const ColorAttachmentDescriptor* +PipelineDescriptor::GetLegacyCompatibleColorAttachment() const { + // Legacy renderers may only render to a single color attachment at index 0u. + if (color_attachment_descriptors_.size() != 1u) { + return nullptr; + } + return GetColorAttachmentDescriptor(0u); +} + PipelineDescriptor& PipelineDescriptor::SetDepthPixelFormat( PixelFormat format) { depth_pixel_format_ = format; @@ -174,6 +183,14 @@ PipelineDescriptor::GetStageEntrypoints() const { return entrypoints_; } +std::shared_ptr PipelineDescriptor::GetEntrypointForStage( + ShaderStage stage) const { + if (auto found = entrypoints_.find(stage); found != entrypoints_.end()) { + return found->second; + } + return nullptr; +} + const std::string& PipelineDescriptor::GetLabel() const { return label_; } @@ -187,4 +204,9 @@ PipelineDescriptor::GetBackStencilAttachmentDescriptor() const { return back_stencil_attachment_descriptor_; } +bool PipelineDescriptor::HasStencilAttachmentDescriptors() const { + return front_stencil_attachment_descriptor_.has_value() || + back_stencil_attachment_descriptor_.has_value(); +} + } // namespace impeller diff --git a/impeller/renderer/pipeline_descriptor.h b/impeller/renderer/pipeline_descriptor.h index cfe3d33a679dd..2f3ffb7fa353e 100644 --- a/impeller/renderer/pipeline_descriptor.h +++ b/impeller/renderer/pipeline_descriptor.h @@ -5,11 +5,13 @@ #pragma once #include +#include #include #include #include #include #include +#include #include "flutter/fml/hash_combine.h" #include "flutter/fml/macros.h" @@ -21,6 +23,7 @@ namespace impeller { class ShaderFunction; class VertexDescriptor; +class Pipeline; class PipelineDescriptor final : public Comparable { public: @@ -42,6 +45,9 @@ class PipelineDescriptor final : public Comparable { const std::map>& GetStageEntrypoints() const; + std::shared_ptr GetEntrypointForStage( + ShaderStage stage) const; + PipelineDescriptor& SetVertexDescriptor( std::shared_ptr vertex_descriptor); @@ -60,6 +66,8 @@ class PipelineDescriptor final : public Comparable { const std::map& GetColorAttachmentDescriptors() const; + const ColorAttachmentDescriptor* GetLegacyCompatibleColorAttachment() const; + PipelineDescriptor& SetDepthStencilAttachmentDescriptor( DepthAttachmentDescriptor desc); @@ -79,6 +87,8 @@ class PipelineDescriptor final : public Comparable { std::optional GetBackStencilAttachmentDescriptor() const; + bool HasStencilAttachmentDescriptors() const; + PipelineDescriptor& SetDepthPixelFormat(PixelFormat format); PixelFormat GetDepthPixelFormat() const; @@ -111,4 +121,10 @@ class PipelineDescriptor final : public Comparable { back_stencil_attachment_descriptor_; }; +using PipelineMap = + std::unordered_map>, + ComparableHash, + ComparableEqual>; + } // namespace impeller diff --git a/impeller/renderer/platform.h b/impeller/renderer/platform.h index 1ca8133c328c5..c2783d7581e08 100644 --- a/impeller/renderer/platform.h +++ b/impeller/renderer/platform.h @@ -17,7 +17,7 @@ constexpr size_t DefaultUniformAlignment() { #elif FML_OS_MACOSX return 256u; #else -#error "Unsupported platform". + return 16u; #endif } diff --git a/impeller/renderer/render_pass.cc b/impeller/renderer/render_pass.cc index 411891c2c3f57..cdae1531011d2 100644 --- a/impeller/renderer/render_pass.cc +++ b/impeller/renderer/render_pass.cc @@ -7,7 +7,8 @@ namespace impeller { RenderPass::RenderPass(RenderTarget target) - : render_target_(std::move(target)) {} + : render_target_(std::move(target)), + transients_buffer_(HostBuffer::Create()) {} RenderPass::~RenderPass() = default; @@ -19,4 +20,39 @@ ISize RenderPass::GetRenderTargetSize() const { return render_target_.GetRenderTargetSize(); } +HostBuffer& RenderPass::GetTransientsBuffer() { + return *transients_buffer_; +} + +bool RenderPass::AddCommand(Command command) { + if (!command) { + VALIDATION_LOG << "Attempted to add an invalid command to the render pass."; + return false; + } + + if (command.scissor.has_value()) { + auto target_rect = IRect({}, render_target_.GetRenderTargetSize()); + if (!target_rect.Contains(command.scissor.value())) { + VALIDATION_LOG << "Cannot apply a scissor that lies outside the bounds " + "of the render target."; + return false; + } + } + + if (command.index_count == 0u) { + // Essentially a no-op. Don't record the command but this is not necessary + // an error either. + return true; + } + + if (command.instance_count == 0u) { + // Essentially a no-op. Don't record the command but this is not necessary + // an error either. + return true; + } + + commands_.emplace_back(std::move(command)); + return true; +} + } // namespace impeller diff --git a/impeller/renderer/render_pass.h b/impeller/renderer/render_pass.h index 05e3b62eca2cc..87e85e40afa19 100644 --- a/impeller/renderer/render_pass.h +++ b/impeller/renderer/render_pass.h @@ -35,7 +35,7 @@ class RenderPass { virtual void SetLabel(std::string label) = 0; - virtual HostBuffer& GetTransientsBuffer() = 0; + HostBuffer& GetTransientsBuffer(); //---------------------------------------------------------------------------- /// @brief Record a command for subsequent encoding to the underlying @@ -46,7 +46,7 @@ class RenderPass { /// /// @return If the command was valid for subsequent commitment. /// - virtual bool AddCommand(Command command) = 0; + bool AddCommand(Command command); //---------------------------------------------------------------------------- /// @brief Encode the recorded commands to the underlying command buffer. @@ -56,10 +56,13 @@ class RenderPass { /// @return If the commands were encoded to the underlying command /// buffer. /// - virtual bool EncodeCommands(Allocator& transients_allocator) const = 0; + virtual bool EncodeCommands( + const std::shared_ptr& transients_allocator) const = 0; protected: const RenderTarget render_target_; + std::shared_ptr transients_buffer_; + std::vector commands_; RenderPass(RenderTarget target); diff --git a/impeller/renderer/renderer.cc b/impeller/renderer/renderer.cc index 849e4fc0fa3ba..9a3b39ba4419d 100644 --- a/impeller/renderer/renderer.cc +++ b/impeller/renderer/renderer.cc @@ -63,7 +63,7 @@ bool Renderer::Render(std::unique_ptr surface, return false; } - if (!render_pass->EncodeCommands(*GetContext()->GetTransientsAllocator())) { + if (!render_pass->EncodeCommands(GetContext()->GetTransientsAllocator())) { return false; } diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index 721d1f478502c..a1b87a9abdc39 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -4,12 +4,12 @@ #include "flutter/fml/time/time_point.h" #include "flutter/testing/testing.h" -#include "impeller/fixtures/mtl/box_fade.frag.h" -#include "impeller/fixtures/mtl/box_fade.vert.h" -#include "impeller/fixtures/mtl/instanced_draw.frag.h" -#include "impeller/fixtures/mtl/instanced_draw.vert.h" -#include "impeller/fixtures/mtl/test_texture.frag.h" -#include "impeller/fixtures/mtl/test_texture.vert.h" +#include "impeller/fixtures/box_fade.frag.h" +#include "impeller/fixtures/box_fade.vert.h" +#include "impeller/fixtures/instanced_draw.frag.h" +#include "impeller/fixtures/instanced_draw.vert.h" +#include "impeller/fixtures/test_texture.frag.h" +#include "impeller/fixtures/test_texture.vert.h" #include "impeller/geometry/path_builder.h" #include "impeller/image/compressed_image.h" #include "impeller/image/decompressed_image.h" @@ -270,10 +270,14 @@ TEST_P(RendererTest, CanRenderToTexture) { VS::BindUniformBuffer( cmd, r2t_pass->GetTransientsBuffer().EmplaceUniform(uniforms)); ASSERT_TRUE(r2t_pass->AddCommand(std::move(cmd))); - ASSERT_TRUE(r2t_pass->EncodeCommands(*context->GetTransientsAllocator())); + ASSERT_TRUE(r2t_pass->EncodeCommands(context->GetTransientsAllocator())); } +#if IMPELLER_ENABLE_METAL TEST_P(RendererTest, CanRenderInstanced) { + if (GetBackend() != PlaygroundBackend::kMetal) { + GTEST_SKIP_("Instancing is only supported on Metal."); + } using VS = InstancedDrawVertexShader; using FS = InstancedDrawFragmentShader; @@ -329,6 +333,7 @@ TEST_P(RendererTest, CanRenderInstanced) { return true; })); } +#endif // IMPELLER_ENABLE_METAL } // namespace testing } // namespace impeller diff --git a/impeller/renderer/sampler_descriptor.h b/impeller/renderer/sampler_descriptor.h index 1cdbd3e6f3a16..d028f203fc37c 100644 --- a/impeller/renderer/sampler_descriptor.h +++ b/impeller/renderer/sampler_descriptor.h @@ -4,6 +4,8 @@ #pragma once +#include + #include "flutter/fml/macros.h" #include "impeller/base/comparable.h" #include "impeller/renderer/formats.h" @@ -38,4 +40,9 @@ struct SamplerDescriptor final : public Comparable { } }; +using SamplerMap = std::unordered_map, + ComparableHash, + ComparableEqual>; + } // namespace impeller diff --git a/impeller/renderer/shader_key.cc b/impeller/renderer/shader_key.cc new file mode 100644 index 0000000000000..c27e1e5c3f449 --- /dev/null +++ b/impeller/renderer/shader_key.cc @@ -0,0 +1,11 @@ +// 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/renderer/shader_key.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/renderer/shader_key.h b/impeller/renderer/shader_key.h new file mode 100644 index 0000000000000..197ea5bf56803 --- /dev/null +++ b/impeller/renderer/shader_key.h @@ -0,0 +1,45 @@ +// 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 + +#include "flutter/fml/hash_combine.h" +#include "flutter/fml/macros.h" +#include "impeller/renderer/shader_types.h" + +namespace impeller { + +struct ShaderKey { + std::string name; + ShaderStage stage = ShaderStage::kUnknown; + + ShaderKey(const std::string_view& p_name, ShaderStage p_stage) + : name({p_name.data(), p_name.size()}), stage(p_stage) {} + + struct Hash { + size_t operator()(const ShaderKey& key) const { + return fml::HashCombine(key.name, key.stage); + } + }; + + struct Equal { + constexpr bool operator()(const ShaderKey& k1, const ShaderKey& k2) const { + return k1.stage == k2.stage && k1.name == k2.name; + } + }; +}; + +class ShaderFunction; + +using ShaderFunctionMap = + std::unordered_map, + ShaderKey::Hash, + ShaderKey::Equal>; + +} // namespace impeller diff --git a/impeller/renderer/shader_types.h b/impeller/renderer/shader_types.h index 0f0f9919f02b8..c702157731fc4 100644 --- a/impeller/renderer/shader_types.h +++ b/impeller/renderer/shader_types.h @@ -4,9 +4,9 @@ #pragma once -#include #include #include +#include #include "flutter/fml/hash_combine.h" #include "impeller/geometry/matrix.h" @@ -41,17 +41,24 @@ enum class ShaderType { kSampler, }; -template +struct ShaderStructMemberMetadata { + ShaderType type; + std::string name; + size_t offset; + size_t size; +}; + +struct ShaderMetadata { + std::string name; + std::vector members; +}; + struct ShaderUniformSlot { - using Type = T; const char* name; size_t binding; }; struct ShaderStageIOSlot { - // Statically allocated const string containing advisory debug description. - // This may be absent in release modes and the runtime may not use this string - // for normal operation. const char* name; size_t location; size_t set; diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index b037af41c07b4..fa2a961170ef6 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -4,6 +4,7 @@ import("//build/compiled_action.gni") import("//flutter/common/config.gni") +import("//flutter/testing/testing.gni") declare_args() { # Whether playgrounds are enabled for unit tests. @@ -16,7 +17,7 @@ declare_args() { impeller_enable_metal = is_mac || is_ios # Whether the OpenGLES backend is enabled. - impeller_enable_opengles = is_mac + impeller_enable_opengles = is_mac || is_linux } declare_args() { @@ -39,7 +40,11 @@ declare_args() { # target platform, this target is a no-op. # template("impeller_component") { - if (impeller_supports_platform) { + if (defined(invoker.testonly) && invoker.testonly && !enable_unittests) { + group(target_name) { + not_needed(invoker, "*") + } + } else if (impeller_supports_platform) { target_type = "source_set" if (defined(invoker.target_type)) { target_type = invoker.target_type @@ -191,33 +196,36 @@ template("embed_blob") { } template("impellerc") { - # Optional: invoker.defines specifies a list of valueless macro definitions. assert(defined(invoker.shaders), "Impeller shaders must be specified.") assert(defined(invoker.sl_file_extension), "The extension of the SL file must be specified (metal, glsl, etc..).") - assert(defined(invoker.intermediates_subdir), - "The subdirectory in which to put intermediates must be specified.") assert(defined(invoker.shader_target_flag), "The flag to impellerc for target selection must be specified.") + # Optional: invoker.defines specifies a list of valueless macro definitions. + # Optional: invoker.intermediates_subdir specifies the subdirectory in which + # to put intermediates. + sl_file_extension = invoker.sl_file_extension compiled_action_foreach(target_name) { tool = "//flutter/impeller/compiler:impellerc" sources = invoker.shaders - subdir = invoker.intermediates_subdir + if (defined(invoker.intermediates_subdir)) { + subdir = invoker.intermediates_subdir + generated_dir = "$target_gen_dir/$subdir" + } else { + generated_dir = "$target_gen_dir" + } + shader_target_flag = invoker.shader_target_flag - sl_intermediate = - "$target_gen_dir/$subdir/{{source_file_part}}.$sl_file_extension" - spirv_intermediate = "$target_gen_dir/$subdir/{{source_file_part}}.spirv" - reflection_json_intermediate = - "$target_gen_dir/$subdir/{{source_file_part}}.json" - reflection_header_intermediate = - "$target_gen_dir/$subdir/{{source_file_part}}.h" - reflection_cc_intermediate = - "$target_gen_dir/$subdir/{{source_file_part}}.cc" + sl_intermediate = "$generated_dir/{{source_file_part}}.$sl_file_extension" + spirv_intermediate = "$generated_dir/{{source_file_part}}.spirv" + reflection_json_intermediate = "$generated_dir/{{source_file_part}}.json" + reflection_header_intermediate = "$generated_dir/{{source_file_part}}.h" + reflection_cc_intermediate = "$generated_dir/{{source_file_part}}.cc" outputs = [ sl_intermediate, @@ -225,7 +233,7 @@ template("impellerc") { reflection_cc_intermediate, ] - depfile_path = "$target_gen_dir/$subdir/{{source_file_part}}.d" + depfile_path = "$generated_dir/{{source_file_part}}.d" sl_intermediate_path = rebase_path(sl_intermediate, root_build_dir) spirv_intermediate_path = rebase_path(spirv_intermediate, root_build_dir) @@ -302,7 +310,6 @@ template("impeller_shaders_metal") { impellerc(impellerc_mtl) { shaders = invoker.shaders sl_file_extension = "metal" - intermediates_subdir = "mtl" shader_target_flag = "" defines = [ "IMPELLER_TARGET_METAL" ] if (is_ios) { @@ -360,7 +367,7 @@ template("blobcat_library") { output_path_rebased = rebase_path(output_file, root_build_dir) args = [ "--output=$output_path_rebased" ] foreach(shader, invoker.shaders) { - shader_path = rebase_path(shader, root_out_dir) + shader_path = rebase_path(shader, root_build_dir) args += [ "--input=$shader_path" ] } deps = invoker.deps @@ -380,8 +387,17 @@ template("impeller_shaders_gles") { impellerc(impellerc_gles) { shaders = invoker.shaders sl_file_extension = "gles" - intermediates_subdir = "gles" - shader_target_flag = "--opengl-es" + + # Metal reflectors generate a superset of information. + if (impeller_enable_metal) { + intermediates_subdir = "gles" + } + if (is_mac) { + shader_target_flag = "--opengl-desktop" + } else { + shader_target_flag = "--opengl-es" + } + defines = [ "IMPELLER_TARGET_OPENGLES" ] } @@ -408,24 +424,29 @@ template("impeller_shaders_gles") { } group(target_name) { - public_deps = [ - ":$embed_gles_lib", - ":$reflect_gles", - ] + public_deps = [ ":$embed_gles_lib" ] + + if (!impeller_enable_metal) { + public_deps += [ ":$reflect_gles" ] + } } } template("impeller_shaders") { - mtl_shaders = "mtl_$target_name" - impeller_shaders_metal(mtl_shaders) { - name = invoker.name - shaders = invoker.shaders + if (impeller_enable_metal) { + mtl_shaders = "mtl_$target_name" + impeller_shaders_metal(mtl_shaders) { + name = invoker.name + shaders = invoker.shaders + } } - gles_shaders = "gles_$target_name" - impeller_shaders_gles(gles_shaders) { - name = invoker.name - shaders = invoker.shaders + if (impeller_enable_opengles) { + gles_shaders = "gles_$target_name" + impeller_shaders_gles(gles_shaders) { + name = invoker.name + shaders = invoker.shaders + } } group(target_name) {