From 504e9b3ce492db6ff6ac0717a2c9823abb58fd42 Mon Sep 17 00:00:00 2001 From: Zachary Anderson Date: Sat, 2 Jul 2022 08:54:52 -0700 Subject: [PATCH 1/8] [impellerc] Adds an SkSL backend --- impeller/compiler/BUILD.gn | 2 ++ impeller/compiler/spirv_sksl.cc | 39 +++++++++++++++++++++++++++++++++ impeller/compiler/spirv_sksl.h | 39 +++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 impeller/compiler/spirv_sksl.cc create mode 100644 impeller/compiler/spirv_sksl.h diff --git a/impeller/compiler/BUILD.gn b/impeller/compiler/BUILD.gn index 9e2de582a9a6d..4c2b94802bad6 100644 --- a/impeller/compiler/BUILD.gn +++ b/impeller/compiler/BUILD.gn @@ -28,6 +28,8 @@ impeller_component("compiler_lib") { "runtime_stage_data.h", "source_options.cc", "source_options.h", + "spirv_sksl.cc", + "spirv_sksl.h", "switches.cc", "switches.h", "types.cc", diff --git a/impeller/compiler/spirv_sksl.cc b/impeller/compiler/spirv_sksl.cc new file mode 100644 index 0000000000000..d8d93f0fe4a97 --- /dev/null +++ b/impeller/compiler/spirv_sksl.cc @@ -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. + +#include "impeller/compiler/spirv_sksl.h" + +using namespace spv; +using namespace SPIRV_CROSS_NAMESPACE; +using namespace std; + +namespace impeller { +namespace compiler { + +string CompilerSkSL::compile() { + // Do not deal with ES-isms like precision, older extensions and such. + options.es = false; + options.version = 450; + backend.float_literal_suffix = true; + backend.double_literal_suffix = false; + backend.long_long_literal_suffix = true; + backend.uint32_t_literal_suffix = true; + backend.basic_int_type = "int32_t"; + backend.basic_uint_type = "uint32_t"; + backend.swizzle_is_function = true; + backend.shared_is_implied = true; + backend.unsized_array_supported = false; + backend.explicit_struct_type = true; + backend.use_initializer_list = true; + + return spirv_cross::CompilerGLSL::compile(); +} + +void CompilerSkSL::emit_header() { + statement("// This SkSL shader is autogenerated by spirv-cross."); + // TODO(zra): Add #defines for SkSL things that have different names +} + +} // namespace compiler +} // namespace impeller \ No newline at end of file diff --git a/impeller/compiler/spirv_sksl.h b/impeller/compiler/spirv_sksl.h new file mode 100644 index 0000000000000..1463191437e09 --- /dev/null +++ b/impeller/compiler/spirv_sksl.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 +#include +#include + +#include "flutter/fml/logging.h" +#include "flutter/fml/macros.h" +#include "spirv_glsl.hpp" + +namespace impeller { +namespace compiler { + +class CompilerSkSL : public spirv_cross::CompilerGLSL { + public: + explicit CompilerSkSL(std::vector spirv_) + : CompilerGLSL(std::move(spirv_)) {} + + CompilerSkSL(const uint32_t* ir_, size_t word_count) + : CompilerGLSL(ir_, word_count) {} + + explicit CompilerSkSL(const spirv_cross::ParsedIR& ir_) + : spirv_cross::CompilerGLSL(ir_) {} + + explicit CompilerSkSL(spirv_cross::ParsedIR&& ir_) + : spirv_cross::CompilerGLSL(std::move(ir_)) {} + + std::string compile() override; + + private: + void emit_header() override; +}; + +} // namespace compiler +} // namespace impeller From e5176b5a07df3ed1e88f48ed24a30ebc71a27fdf Mon Sep 17 00:00:00 2001 From: Zachary Anderson Date: Tue, 5 Jul 2022 22:40:36 -0700 Subject: [PATCH 2/8] WIP --- impeller/compiler/compiler.cc | 14 +- impeller/compiler/compiler_backend.cc | 17 +++ impeller/compiler/compiler_backend.h | 13 +- impeller/compiler/impellerc_main.cc | 1 + impeller/compiler/reflector.cc | 2 + impeller/compiler/runtime_stage_data.cc | 1 + impeller/compiler/spirv_sksl.cc | 172 ++++++++++++++++++++++-- impeller/compiler/spirv_sksl.h | 8 ++ impeller/compiler/switches.cc | 1 + impeller/compiler/types.cc | 9 +- impeller/compiler/types.h | 1 + 11 files changed, 219 insertions(+), 20 deletions(-) diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 3df96cc40155e..28e048a7b5056 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -27,7 +27,7 @@ static CompilerBackend CreateMSLCompiler(const spirv_cross::ParsedIR& ir, sl_options.msl_version = spirv_cross::CompilerMSL::Options::make_msl_version(1, 2); sl_compiler->set_msl_options(sl_options); - return sl_compiler; + return CompilerBackend(sl_compiler); } static CompilerBackend CreateGLSLCompiler(const spirv_cross::ParsedIR& ir, @@ -44,7 +44,13 @@ static CompilerBackend CreateGLSLCompiler(const spirv_cross::ParsedIR& ir, sl_options.es = false; } gl_compiler->set_common_options(sl_options); - return gl_compiler; + return CompilerBackend(gl_compiler); +} + +static CompilerBackend CreateSkSLCompiler(const spirv_cross::ParsedIR& ir, + const SourceOptions& source_options) { + auto sksl_compiler = std::make_shared(ir); + return CompilerBackend(sksl_compiler); } static bool EntryPointMustBeNamedMain(TargetPlatform platform) { @@ -56,6 +62,7 @@ static bool EntryPointMustBeNamedMain(TargetPlatform platform) { case TargetPlatform::kRuntimeStageMetal: return false; case TargetPlatform::kFlutterSPIRV: + case TargetPlatform::kSkSL: case TargetPlatform::kOpenGLES: case TargetPlatform::kOpenGLDesktop: case TargetPlatform::kRuntimeStageGLES: @@ -80,6 +87,8 @@ static CompilerBackend CreateCompiler(const spirv_cross::ParsedIR& ir, case TargetPlatform::kOpenGLDesktop: compiler = CreateGLSLCompiler(ir, source_options); break; + case TargetPlatform::kSkSL: + compiler = CreateSkSLCompiler(ir, source_options); } if (!compiler) { return {}; @@ -151,6 +160,7 @@ Compiler::Compiler(const fml::Mapping& source_mapping, shaderc_spirv_version::shaderc_spirv_version_1_0); break; case TargetPlatform::kFlutterSPIRV: + case TargetPlatform::kSkSL: // TODO(zra): Allow optimizations. // With any optimization level above 'zero' enabled, shaderc will emit // ops that are not supported by the Engine's SPIR-V -> SkSL transpiler. // In particular, with 'shaderc_optimization_level_size' enabled, it will diff --git a/impeller/compiler/compiler_backend.cc b/impeller/compiler/compiler_backend.cc index cb44cffa41e36..1df6332498c49 100644 --- a/impeller/compiler/compiler_backend.cc +++ b/impeller/compiler/compiler_backend.cc @@ -15,6 +15,9 @@ CompilerBackend::CompilerBackend(MSLCompiler compiler) CompilerBackend::CompilerBackend(GLSLCompiler compiler) : CompilerBackend(Type::kGLSL, compiler) {} +CompilerBackend::CompilerBackend(SkSLCompiler compiler) + : CompilerBackend(Type::kSkSL, compiler) {} + CompilerBackend::CompilerBackend() = default; CompilerBackend::CompilerBackend(Type type, Compiler compiler) @@ -54,6 +57,10 @@ const spirv_cross::Compiler* CompilerBackend::GetCompiler() const { return compiler; } + if (auto compiler = GetSkSLCompiler()) { + return compiler; + } + return nullptr; } @@ -64,6 +71,9 @@ spirv_cross::Compiler* CompilerBackend::GetCompiler() { if (auto* glsl = std::get_if(&compiler_)) { return glsl->get(); } + if (auto* sksl = std::get_if(&compiler_)) { + return sksl->get(); + } return nullptr; } @@ -81,6 +91,13 @@ const spirv_cross::CompilerGLSL* CompilerBackend::GetGLSLCompiler() const { return nullptr; } +const CompilerSkSL* CompilerBackend::GetSkSLCompiler() const { + if (auto* sksl = std::get_if(&compiler_)) { + return sksl->get(); + } + return nullptr; +} + CompilerBackend::operator bool() const { return !!GetCompiler(); } diff --git a/impeller/compiler/compiler_backend.h b/impeller/compiler/compiler_backend.h index a7210931d9301..b8225fce186ef 100644 --- a/impeller/compiler/compiler_backend.h +++ b/impeller/compiler/compiler_backend.h @@ -11,6 +11,7 @@ #include "flutter/fml/macros.h" #include "spirv_glsl.hpp" #include "spirv_msl.hpp" +#include "spirv_sksl.h" namespace impeller { namespace compiler { @@ -18,16 +19,20 @@ namespace compiler { struct CompilerBackend { using MSLCompiler = std::shared_ptr; using GLSLCompiler = std::shared_ptr; - using Compiler = std::variant; + using SkSLCompiler = std::shared_ptr; + using Compiler = std::variant; enum class Type { kMSL, kGLSL, + kSkSL, }; - CompilerBackend(MSLCompiler compiler); + explicit CompilerBackend(MSLCompiler compiler); - CompilerBackend(GLSLCompiler compiler); + explicit CompilerBackend(GLSLCompiler compiler); + + explicit CompilerBackend(SkSLCompiler compiler); CompilerBackend(Type type, Compiler compiler); @@ -59,6 +64,8 @@ struct CompilerBackend { const spirv_cross::CompilerMSL* GetMSLCompiler() const; const spirv_cross::CompilerGLSL* GetGLSLCompiler() const; + + const CompilerSkSL* GetSkSLCompiler() const; }; } // namespace compiler diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index 79ff437355df9..38b96d29b0b1e 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -172,6 +172,7 @@ bool Main(const fml::CommandLine& command_line) { case TargetPlatform::kOpenGLDesktop: case TargetPlatform::kRuntimeStageMetal: case TargetPlatform::kRuntimeStageGLES: + case TargetPlatform::kSkSL: result_file = switches.sl_file_name; break; case TargetPlatform::kFlutterSPIRV: diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 0693ef34d68b7..a8d099d042e42 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -342,6 +342,8 @@ static std::string ToString(CompilerBackend::Type type) { return "Metal Shading Language"; case CompilerBackend::Type::kGLSL: return "OpenGL Shading Language"; + case CompilerBackend::Type::kSkSL: + return "SkSL Shading Language"; } FML_UNREACHABLE(); } diff --git a/impeller/compiler/runtime_stage_data.cc b/impeller/compiler/runtime_stage_data.cc index 07b187eb7e3d4..20809a25c52e8 100644 --- a/impeller/compiler/runtime_stage_data.cc +++ b/impeller/compiler/runtime_stage_data.cc @@ -55,6 +55,7 @@ static std::optional ToTargetPlatform( case TargetPlatform::kMetalDesktop: case TargetPlatform::kMetalIOS: case TargetPlatform::kFlutterSPIRV: + case TargetPlatform::kSkSL: case TargetPlatform::kOpenGLES: case TargetPlatform::kOpenGLDesktop: return std::nullopt; diff --git a/impeller/compiler/spirv_sksl.cc b/impeller/compiler/spirv_sksl.cc index d8d93f0fe4a97..2f4e7e3f3beff 100644 --- a/impeller/compiler/spirv_sksl.cc +++ b/impeller/compiler/spirv_sksl.cc @@ -6,34 +6,178 @@ using namespace spv; using namespace SPIRV_CROSS_NAMESPACE; -using namespace std; namespace impeller { namespace compiler { -string CompilerSkSL::compile() { +std::string CompilerSkSL::compile() { // Do not deal with ES-isms like precision, older extensions and such. options.es = false; options.version = 450; - backend.float_literal_suffix = true; + options.vulkan_semantics = false; + backend.allow_precision_qualifiers = false; + backend.float_literal_suffix = false; backend.double_literal_suffix = false; - backend.long_long_literal_suffix = true; - backend.uint32_t_literal_suffix = true; - backend.basic_int_type = "int32_t"; - backend.basic_uint_type = "uint32_t"; - backend.swizzle_is_function = true; - backend.shared_is_implied = true; - backend.unsized_array_supported = false; - backend.explicit_struct_type = true; - backend.use_initializer_list = true; + backend.long_long_literal_suffix = false; + backend.uint32_t_literal_suffix = false; + backend.basic_int_type = "int"; + backend.basic_uint_type = "uint"; + backend.basic_int16_type = "short"; + backend.basic_uint16_type = "ushort"; + // TODO(zra): This doesn't work at least because we need to handle outputs + // in a different way. Instead this should do what spirv_cpp.cpp does and + // actually reimplement most of spirv_glsl.cpp. return spirv_cross::CompilerGLSL::compile(); } void CompilerSkSL::emit_header() { statement("// This SkSL shader is autogenerated by spirv-cross."); - // TODO(zra): Add #defines for SkSL things that have different names +} + +std::string CompilerSkSL::type_to_glsl(const spirv_cross::SPIRType& type, + uint32_t id) { + std::string result = spirv_cross::CompilerGLSL::type_to_glsl(type, id); + + // Rewrite the type where SkSL spells it differently. + + if (type.vecsize == 1 && type.columns == 1) { // Scalar builtins. + switch (type.basetype) { + case SPIRType::Boolean: + return "bool"; + case SPIRType::SByte: + return backend.basic_int8_type; + case SPIRType::UByte: + return backend.basic_uint8_type; + case SPIRType::Short: + return backend.basic_int16_type; + case SPIRType::UShort: + return backend.basic_uint16_type; + case SPIRType::Int: + return backend.basic_int_type; + case SPIRType::UInt: + return backend.basic_uint_type; + case SPIRType::AtomicCounter: + return "atomic_uint"; + case SPIRType::Half: + return "half"; + case SPIRType::Float: + return "float"; + case SPIRType::Double: + return "float"; + case SPIRType::Int64: + return "int"; + case SPIRType::UInt64: + return "uint"; + case SPIRType::Void: + return "void"; + default: + return "???"; + } + } else if (type.vecsize > 1 && type.columns == 1) { // Vector builtins. + switch (type.basetype) { + case SPIRType::Boolean: + return join("bool", type.vecsize); + case SPIRType::SByte: + return join("i8vec", type.vecsize); + case SPIRType::UByte: + return join("u8vec", type.vecsize); + case SPIRType::Short: + return join("short", type.vecsize); + case SPIRType::UShort: + return join("ushort", type.vecsize); + case SPIRType::Int: + return join("int", type.vecsize); + case SPIRType::UInt: + return join("uint", type.vecsize); + case SPIRType::Half: + return join("half", type.vecsize); + case SPIRType::Float: + return join("float", type.vecsize); + case SPIRType::Double: + return join("float", type.vecsize); + case SPIRType::Int64: + return join("int", type.vecsize); + case SPIRType::UInt64: + return join("uint", type.vecsize); + default: + return "???"; + } + } else if (type.vecsize == type.columns) { // Simple Matrix builtin + switch (type.basetype) { + case SPIRType::Boolean: + return join("bool", type.vecsize, "x", type.vecsize); + case SPIRType::Int: + return join("int", type.vecsize, "x", type.vecsize); + case SPIRType::UInt: + return join("uint", type.vecsize, "x", type.vecsize); + case SPIRType::Half: + return join("half", type.vecsize, "x", type.vecsize); + case SPIRType::Float: + return join("float", type.vecsize, "x", type.vecsize); + case SPIRType::Double: + return join("float", type.vecsize, "x", type.vecsize); + // Matrix types not supported for int64/uint64. + default: + return "???"; + } + } else { + switch (type.basetype) { + case SPIRType::Boolean: + return join("bool", type.columns, "x", type.vecsize); + case SPIRType::Int: + return join("int", type.columns, "x", type.vecsize); + case SPIRType::UInt: + return join("uint", type.columns, "x", type.vecsize); + case SPIRType::Half: + return join("half", type.columns, "x", type.vecsize); + case SPIRType::Float: + return join("float", type.columns, "x", type.vecsize); + case SPIRType::Double: + return join("float", type.columns, "x", type.vecsize); + // Matrix types not supported for int64/uint64. + default: + return "???"; + } + } + + return result; +} + +std::string CompilerSkSL::builtin_to_glsl(spv::BuiltIn builtin, + spv::StorageClass storage) { + std::string result = + spirv_cross::CompilerGLSL::builtin_to_glsl(builtin, storage); + + switch (builtin) { + case BuiltInPosition: + return "sk_Position"; + case BuiltInPointSize: + return "sk_PointSize"; + case BuiltInVertexId: + return "sk_VertexID"; + case BuiltInInstanceId: + return "sk_InstanceID"; + case BuiltInVertexIndex: + return "sk_VertexID"; + case BuiltInInstanceIndex: + return "sk_InstanceID"; + case BuiltInFragCoord: + return "sk_FragCoord"; + case BuiltInFrontFacing: + return "sk_Clockwise"; + default: + break; + } + + return result; +} + +void CompilerSkSL::emit_uniform(const spirv_cross::SPIRVariable& var) { + spirv_cross::CompilerGLSL::add_resource_name(var.self); + spirv_cross::CompilerGLSL::statement( + spirv_cross::CompilerGLSL::variable_decl(var), ";"); } } // namespace compiler -} // namespace impeller \ No newline at end of file +} // namespace impeller diff --git a/impeller/compiler/spirv_sksl.h b/impeller/compiler/spirv_sksl.h index 1463191437e09..f8264b95566ee 100644 --- a/impeller/compiler/spirv_sksl.h +++ b/impeller/compiler/spirv_sksl.h @@ -33,6 +33,14 @@ class CompilerSkSL : public spirv_cross::CompilerGLSL { private: void emit_header() override; + + std::string type_to_glsl(const spirv_cross::SPIRType& type, + uint32_t id = 0) override; + + std::string builtin_to_glsl(spv::BuiltIn builtin, + spv::StorageClass storage) override; + + void emit_uniform(const spirv_cross::SPIRVariable& var) override; }; } // namespace compiler diff --git a/impeller/compiler/switches.cc b/impeller/compiler/switches.cc index aae3f076021bb..85e1ed80db945 100644 --- a/impeller/compiler/switches.cc +++ b/impeller/compiler/switches.cc @@ -18,6 +18,7 @@ static const std::map kKnownPlatforms = { {"opengl-es", TargetPlatform::kOpenGLES}, {"opengl-desktop", TargetPlatform::kOpenGLDesktop}, {"flutter-spirv", TargetPlatform::kFlutterSPIRV}, + {"sksl", TargetPlatform::kSkSL}, {"runtime-stage-metal", TargetPlatform::kRuntimeStageMetal}, {"runtime-stage-gles", TargetPlatform::kRuntimeStageGLES}, }; diff --git a/impeller/compiler/types.cc b/impeller/compiler/types.cc index 77634329b58ec..05fa2083cab0a 100644 --- a/impeller/compiler/types.cc +++ b/impeller/compiler/types.cc @@ -67,6 +67,8 @@ std::string TargetPlatformToString(TargetPlatform platform) { return "RuntimeStageMetal"; case TargetPlatform::kRuntimeStageGLES: return "RuntimeStageGLES"; + case TargetPlatform::kSkSL: + return "SkSL"; } FML_UNREACHABLE(); } @@ -108,6 +110,7 @@ bool TargetPlatformNeedsSL(TargetPlatform platform) { case TargetPlatform::kOpenGLDesktop: case TargetPlatform::kRuntimeStageMetal: case TargetPlatform::kRuntimeStageGLES: + case TargetPlatform::kSkSL: return true; case TargetPlatform::kUnknown: case TargetPlatform::kFlutterSPIRV: @@ -127,6 +130,7 @@ bool TargetPlatformNeedsReflection(TargetPlatform platform) { return true; case TargetPlatform::kUnknown: case TargetPlatform::kFlutterSPIRV: + case TargetPlatform::kSkSL: return false; } FML_UNREACHABLE(); @@ -202,6 +206,7 @@ spirv_cross::CompilerMSL::Options::Platform TargetPlatformToMSLPlatform( case TargetPlatform::kMetalDesktop: return spirv_cross::CompilerMSL::Options::Platform::macOS; case TargetPlatform::kFlutterSPIRV: + case TargetPlatform::kSkSL: case TargetPlatform::kOpenGLES: case TargetPlatform::kOpenGLDesktop: case TargetPlatform::kRuntimeStageGLES: @@ -238,6 +243,7 @@ std::string TargetPlatformSLExtension(TargetPlatform platform) { case TargetPlatform::kRuntimeStageMetal: return "metal"; case TargetPlatform::kFlutterSPIRV: + case TargetPlatform::kSkSL: case TargetPlatform::kOpenGLES: case TargetPlatform::kOpenGLDesktop: case TargetPlatform::kRuntimeStageGLES: @@ -266,6 +272,7 @@ bool TargetPlatformIsOpenGL(TargetPlatform platform) { case TargetPlatform::kMetalIOS: case TargetPlatform::kUnknown: case TargetPlatform::kFlutterSPIRV: + case TargetPlatform::kSkSL: return false; } FML_UNREACHABLE(); @@ -279,11 +286,11 @@ bool TargetPlatformIsMetal(TargetPlatform platform) { return true; case TargetPlatform::kUnknown: case TargetPlatform::kFlutterSPIRV: + case TargetPlatform::kSkSL: case TargetPlatform::kOpenGLES: case TargetPlatform::kOpenGLDesktop: case TargetPlatform::kRuntimeStageGLES: return false; - break; } FML_UNREACHABLE(); } diff --git a/impeller/compiler/types.h b/impeller/compiler/types.h index 3df651f4accc7..95d0257f9c99f 100644 --- a/impeller/compiler/types.h +++ b/impeller/compiler/types.h @@ -34,6 +34,7 @@ enum class TargetPlatform { kOpenGLDesktop, kRuntimeStageMetal, kRuntimeStageGLES, + kSkSL, }; bool TargetPlatformIsMetal(TargetPlatform platform); From ed467d5f4c7199db726198fea0e17a7a7cd35e37 Mon Sep 17 00:00:00 2001 From: Zachary Anderson Date: Wed, 6 Jul 2022 10:54:24 -0700 Subject: [PATCH 3/8] WIP --- impeller/compiler/spirv_sksl.cc | 212 ++++++++++++++++++++++++++++++-- impeller/compiler/spirv_sksl.h | 2 + 2 files changed, 205 insertions(+), 9 deletions(-) diff --git a/impeller/compiler/spirv_sksl.cc b/impeller/compiler/spirv_sksl.cc index 2f4e7e3f3beff..70cbc814f7dc8 100644 --- a/impeller/compiler/spirv_sksl.cc +++ b/impeller/compiler/spirv_sksl.cc @@ -11,30 +11,224 @@ namespace impeller { namespace compiler { std::string CompilerSkSL::compile() { + ir.fixup_reserved_names(); + // Do not deal with ES-isms like precision, older extensions and such. options.es = false; options.version = 450; options.vulkan_semantics = false; + backend.allow_precision_qualifiers = false; - backend.float_literal_suffix = false; + backend.basic_int16_type = "short"; + backend.basic_int_type = "int"; + backend.basic_uint16_type = "ushort"; + backend.basic_uint_type = "uint"; backend.double_literal_suffix = false; + backend.float_literal_suffix = false; backend.long_long_literal_suffix = false; + backend.needs_row_major_load_workaround = true; + backend.nonuniform_qualifier = ""; + backend.support_precise_qualifier = false; backend.uint32_t_literal_suffix = false; - backend.basic_int_type = "int"; - backend.basic_uint_type = "uint"; - backend.basic_int16_type = "short"; - backend.basic_uint16_type = "ushort"; + backend.use_array_constructor = true; + backend.workgroup_size_is_hidden = true; + + fixup_anonymous_struct_names(); + fixup_type_alias(); + reorder_type_alias(); + build_function_control_flow_graphs_and_analyze(); + fixup_image_load_store_access(); + update_active_builtins(); + analyze_image_and_sampler_usage(); + analyze_interlocked_resource_usage(); + + uint32_t pass_count = 0; + do { + reset(pass_count); + + // Move constructor for this type is broken on GCC 4.9 ... + buffer.reset(); - // TODO(zra): This doesn't work at least because we need to handle outputs - // in a different way. Instead this should do what spirv_cpp.cpp does and - // actually reimplement most of spirv_glsl.cpp. - return spirv_cross::CompilerGLSL::compile(); + emit_header(); + emit_resources(); + + emit_function(get(ir.default_entry_point), Bitset()); + + pass_count++; + } while (is_forcing_recompilation()); + + // TODO(zra): Rewrite main to return something. + + return buffer.str(); } void CompilerSkSL::emit_header() { statement("// This SkSL shader is autogenerated by spirv-cross."); } +void CompilerSkSL::emit_resources() { + bool emitted = false; + + for (auto& id : ir.ids) { + if (id.get_type() == TypeConstant) { + auto& c = id.get(); + bool needs_declaration = c.specialization || c.is_used_as_lut; + if (needs_declaration) { + if (!options.vulkan_semantics && c.specialization) { + c.specialization_constant_macro_name = constant_value_macro_name( + get_decoration(c.self, DecorationSpecId)); + } + emit_constant(c); + emitted = true; + } + } else if (id.get_type() == TypeConstantOp) { + emit_specialization_constant_op(id.get()); + emitted = true; + } + } + + if (emitted) { + statement(""); + } + emitted = false; + + // Output all basic struct types which are not Block or BufferBlock as these + // are declared inplace when such variables are instantiated. + for (auto& id : ir.ids) { + if (id.get_type() == TypeType) { + auto& type = id.get(); + if (type.basetype == SPIRType::Struct && type.array.empty() && + !type.pointer && + (!ir.meta[type.self].decoration.decoration_flags.get( + DecorationBlock) && + !ir.meta[type.self].decoration.decoration_flags.get( + DecorationBufferBlock))) { + emit_struct(type); + emitted = true; + } + } + } + + if (emitted) { + statement(""); + } + emitted = false; + + // Output UBOs and SSBOs + for (auto& id : ir.ids) { + if (id.get_type() == TypeVariable) { + auto& var = id.get(); + auto& type = get(var.basetype); + + if (var.storage != StorageClassFunction && type.pointer && + type.storage == StorageClassUniform && !is_hidden_variable(var) && + (ir.meta[type.self].decoration.decoration_flags.get( + DecorationBlock) || + ir.meta[type.self].decoration.decoration_flags.get( + DecorationBufferBlock))) { + emit_buffer_block(var); + emitted = true; + } + } + } + + if (emitted) { + statement(""); + } + emitted = false; + + // Output push constant blocks + for (auto& id : ir.ids) { + if (id.get_type() == TypeVariable) { + auto& var = id.get(); + auto& type = get(var.basetype); + if (!is_hidden_variable(var) && var.storage != StorageClassFunction && + type.pointer && type.storage == StorageClassPushConstant) { + emit_push_constant_block(var); + emitted = true; + } + } + } + + if (emitted) { + statement(""); + } + emitted = false; + + // Output Uniform Constants (values, samplers, images, etc). + for (auto& id : ir.ids) { + if (id.get_type() == TypeVariable) { + auto& var = id.get(); + auto& type = get(var.basetype); + if (var.storage != StorageClassFunction && !is_hidden_variable(var) && + type.pointer && + (type.storage == StorageClassUniformConstant || + type.storage == StorageClassAtomicCounter)) { + emit_uniform(var); + emitted = true; + } + } + } + + if (emitted) { + statement(""); + } + emitted = false; + + // Output in/out interfaces. + for (auto& id : ir.ids) { + if (id.get_type() == TypeVariable) { + auto& var = id.get(); + auto& type = get(var.basetype); + if (var.storage != StorageClassFunction && !is_hidden_variable(var) && + type.pointer && + (var.storage == StorageClassInput || + var.storage == StorageClassOutput) && + interface_variable_exists_in_entry_point(var.self)) { + emit_interface_block(var); + emitted = true; + } + } + } + + if (emitted) { + statement(""); + } + emitted = false; + + // Global variables. + for (auto global : global_variables) { + auto& var = get(global); + if (is_hidden_variable(var, true)) { + continue; + } + if (var.storage != StorageClassOutput) { + if (!variable_is_lut(var)) { + add_resource_name(var.self); + std::string initializer; + if (options.force_zero_initialized_variables && + var.storage == StorageClassPrivate && !var.initializer && + !var.static_expression && + type_can_zero_initialize(get_variable_data_type(var))) { + initializer = join(" = ", to_zero_initialized_expression( + get_variable_data_type_id(var))); + } + statement(variable_decl(var), initializer, ";"); + emitted = true; + } + } else if (var.initializer && + maybe_get(var.initializer) != nullptr) { + emit_output_variable_initializer(var); + } + } + + if (emitted) { + statement(""); + } + + declare_undefined_values(); +} + std::string CompilerSkSL::type_to_glsl(const spirv_cross::SPIRType& type, uint32_t id) { std::string result = spirv_cross::CompilerGLSL::type_to_glsl(type, id); diff --git a/impeller/compiler/spirv_sksl.h b/impeller/compiler/spirv_sksl.h index f8264b95566ee..1a0e838862fb5 100644 --- a/impeller/compiler/spirv_sksl.h +++ b/impeller/compiler/spirv_sksl.h @@ -34,6 +34,8 @@ class CompilerSkSL : public spirv_cross::CompilerGLSL { private: void emit_header() override; + void emit_resources(); + std::string type_to_glsl(const spirv_cross::SPIRType& type, uint32_t id = 0) override; From fa9c64b1f362d801df577e4b2f9942ebaf99c846 Mon Sep 17 00:00:00 2001 From: Zach Anderson Date: Wed, 6 Jul 2022 15:14:21 -0700 Subject: [PATCH 4/8] WIP --- impeller/compiler/spirv_sksl.cc | 168 +++++++++++++++++++++++++++++++- impeller/compiler/spirv_sksl.h | 8 ++ lib/ui/painting.dart | 48 +++++---- 3 files changed, 201 insertions(+), 23 deletions(-) diff --git a/impeller/compiler/spirv_sksl.cc b/impeller/compiler/spirv_sksl.cc index 70cbc814f7dc8..f5bdfa1b32fa9 100644 --- a/impeller/compiler/spirv_sksl.cc +++ b/impeller/compiler/spirv_sksl.cc @@ -57,13 +57,19 @@ std::string CompilerSkSL::compile() { pass_count++; } while (is_forcing_recompilation()); - // TODO(zra): Rewrite main to return something. + statement("half4 main(float2 iFragCoord)"); + begin_scope(); + statement(" sk_FragCoord = float4(iFragCoord, 0, 0);"); + statement(" __main();"); + statement(" return " + output_name_ + ";"); + end_scope(); return buffer.str(); } void CompilerSkSL::emit_header() { statement("// This SkSL shader is autogenerated by spirv-cross."); + statement("float4 sk_FragCoord;"); } void CompilerSkSL::emit_resources() { @@ -156,6 +162,7 @@ void CompilerSkSL::emit_resources() { emitted = false; // Output Uniform Constants (values, samplers, images, etc). + std::vector uniforms; for (auto& id : ir.ids) { if (id.get_type() == TypeVariable) { auto& var = id.get(); @@ -164,12 +171,28 @@ void CompilerSkSL::emit_resources() { type.pointer && (type.storage == StorageClassUniformConstant || type.storage == StorageClassAtomicCounter)) { - emit_uniform(var); + uniforms.push_back(&var); emitted = true; } } } + // The uniforms have to be emitted in order of increasing location. + std::sort( + uniforms.begin(), uniforms.end(), + [this](spirv_cross::SPIRVariable* var1, spirv_cross::SPIRVariable* var2) { + if (!has_decoration(var1->self, DecorationLocation) || + !has_decoration(var2->self, DecorationLocation)) { + return false; + } + return get_decoration(var1->self, DecorationLocation) < + get_decoration(var2->self, DecorationLocation); + }); + + for (auto var : uniforms) { + emit_uniform(*var); + } + if (emitted) { statement(""); } @@ -229,6 +252,147 @@ void CompilerSkSL::emit_resources() { declare_undefined_values(); } +void CompilerSkSL::emit_interface_block(const SPIRVariable& var) { + auto& type = get(var.basetype); + + const char* qual = to_storage_qualifiers_glsl(var); + + // Either make it plain in/out or in/out blocks depending on what shader is + // doing ... + bool block = + ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock); + if (block) { + // Workaround to make sure we can emit "patch in/out" correctly. + fixup_io_block_patch_qualifiers(var); + + // Block names should never alias. + auto block_name = to_name(type.self, false); + + // The namespace for I/O blocks is separate from other variables in GLSL. + auto& block_namespace = type.storage == StorageClassInput + ? block_input_names + : block_output_names; + + // Shaders never use the block by interface name, so we don't + // have to track this other than updating name caches. + if (block_name.empty() || + block_namespace.find(block_name) != end(block_namespace)) { + block_name = get_fallback_name(type.self); + } else { + block_namespace.insert(block_name); + } + + // If for some reason buffer_name is an illegal name, make a final fallback + // to a workaround name. This cannot conflict with anything else, so we're + // safe now. + if (block_name.empty()) { + block_name = join("_", get(var.basetype).self, "_", var.self); + } + + // Instance names cannot alias block names. + resource_names.insert(block_name); + + bool is_patch = has_decoration(var.self, DecorationPatch); + statement(layout_for_variable(var), (is_patch ? "patch " : ""), qual, + block_name); + begin_scope(); + + type.member_name_cache.clear(); + + uint32_t i = 0; + for (auto& member : type.member_types) { + add_member_name(type, i); + emit_struct_member(type, member, i); + i++; + } + + add_resource_name(var.self); + end_scope_decl(join(to_name(var.self), type_to_array_glsl(type))); + statement(""); + } else { + add_resource_name(var.self); + statement(variable_decl(type, to_name(var.self), var.self), ";"); + if (output_name_.empty()) { + output_name_ = to_name(var.self); + } + } +} + +void CompilerSkSL::emit_function_prototype(SPIRFunction& func, + const Bitset& return_flags) { + if (func.self != ir.default_entry_point) { + add_function_overload(func); + } + + // Avoid shadow declarations. + local_variable_names = resource_names; + + std::string decl; + + auto& type = get(func.return_type); + decl += flags_to_qualifiers_glsl(type, return_flags); + decl += type_to_glsl(type); + decl += type_to_array_glsl(type); + decl += " "; + + if (func.self == ir.default_entry_point) { + // SkSL requires a return value from main, so we wrap the entire entrypoint. + // The rest of this method is copied from spirv_glsl.cpp. + decl += "__main"; + processing_entry_point = true; + } else { + decl += to_name(func.self); + } + + decl += "("; + SmallVector arglist; + for (auto& arg : func.arguments) { + // Do not pass in separate images or samplers if we're remapping + // to combined image samplers. + if (skip_argument(arg.id)) { + continue; + } + + // Might change the variable name if it already exists in this function. + // SPIRV OpName doesn't have any semantic effect, so it's valid for an + // implementation to use same name for variables. Since we want to make the + // GLSL debuggable and somewhat sane, use fallback names for variables which + // are duplicates. + add_local_variable_name(arg.id); + + arglist.push_back(argument_decl(arg)); + + // Hold a pointer to the parameter so we can invalidate the readonly field + // if needed. + auto* var = maybe_get(arg.id); + if (var) { + var->parameter = &arg; + } + } + + for (auto& arg : func.shadow_arguments) { + // Might change the variable name if it already exists in this function. + // SPIRV OpName doesn't have any semantic effect, so it's valid for an + // implementation to use same name for variables. Since we want to make the + // GLSL debuggable and somewhat sane, use fallback names for variables which + // are duplicates. + add_local_variable_name(arg.id); + + arglist.push_back(argument_decl(arg)); + + // Hold a pointer to the parameter so we can invalidate the readonly field + // if needed. + auto* var = maybe_get(arg.id); + if (var) { + var->parameter = &arg; + } + } + + decl += merge(arglist); + decl += ")"; + statement(decl); +} + std::string CompilerSkSL::type_to_glsl(const spirv_cross::SPIRType& type, uint32_t id) { std::string result = spirv_cross::CompilerGLSL::type_to_glsl(type, id); diff --git a/impeller/compiler/spirv_sksl.h b/impeller/compiler/spirv_sksl.h index 1a0e838862fb5..753468e37f35f 100644 --- a/impeller/compiler/spirv_sksl.h +++ b/impeller/compiler/spirv_sksl.h @@ -32,10 +32,18 @@ class CompilerSkSL : public spirv_cross::CompilerGLSL { std::string compile() override; private: + std::string output_name_; + void emit_header() override; void emit_resources(); + void emit_interface_block(const spirv_cross::SPIRVariable& var); + + void emit_function_prototype( + spirv_cross::SPIRFunction& func, + const spirv_cross::Bitset& return_flags) override; + std::string type_to_glsl(const spirv_cross::SPIRType& type, uint32_t id = 0) override; diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 92fa4967a15b5..5c082ae64c8d0 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -3907,30 +3907,36 @@ class FragmentProgram extends NativeFieldWrapperClass1 { /// SPIR-V not meeting this specification will throw an exception. static Future compile({ required ByteBuffer spirv, + ByteBuffer? sksl, bool debugPrint = false, }) { // Delay compilation without creating a timer, which interacts poorly with the // flutter test framework. See: https://github.com/flutter/flutter/issues/104084 - return Future.microtask(() => FragmentProgram._(spirv: spirv, debugPrint: debugPrint)); + return Future.microtask(() => FragmentProgram._(spirv: spirv, sksl: sksl, debugPrint: debugPrint)); } @pragma('vm:entry-point') FragmentProgram._({ required ByteBuffer spirv, + ByteBuffer? sksl, bool debugPrint = false, }) { _constructor(); - final spv.TranspileResult result = spv.transpile( - spirv, - spv.TargetLanguage.sksl, - ); - _init(result.src, debugPrint); - _uniformFloatCount = result.uniformFloatCount; - _samplerCount = result.samplerCount; + if (sksl == null) { + final spv.TranspileResult result = spv.transpile( + spirv, + spv.TargetLanguage.sksl, + ); + _init(result.src, debugPrint); + // _uniformFloatCount = result.uniformFloatCount; + // _samplerCount = result.samplerCount; + } else { + _init(utf8.decode(sksl.asUint8List()), debugPrint); + } } - late final int _uniformFloatCount; - late final int _samplerCount; + // late final int _uniformFloatCount; + // late final int _samplerCount; void _constructor() native 'FragmentProgram_constructor'; void _init(String sksl, bool debugPrint) native 'FragmentProgram_init'; @@ -3988,23 +3994,23 @@ class FragmentProgram extends NativeFieldWrapperClass1 { Float32List? floatUniforms, List? samplerUniforms, }) { - if (floatUniforms == null) { - floatUniforms = Float32List(_uniformFloatCount); - } - if (floatUniforms.length != _uniformFloatCount) { - throw ArgumentError( - 'floatUniforms size: ${floatUniforms.length} must match given shader uniform count: $_uniformFloatCount.'); - } - if (_samplerCount > 0 && (samplerUniforms == null || samplerUniforms.length != _samplerCount)) { - throw ArgumentError('samplerUniforms must have length $_samplerCount'); - } + // if (floatUniforms == null) { + // floatUniforms = Float32List(_uniformFloatCount); + // } + // if (floatUniforms.length != _uniformFloatCount) { + // throw ArgumentError( + // 'floatUniforms size: ${floatUniforms.length} must match given shader uniform count: $_uniformFloatCount.'); + // } + // if (_samplerCount > 0 && (samplerUniforms == null || samplerUniforms.length != _samplerCount)) { + // throw ArgumentError('samplerUniforms must have length $_samplerCount'); + // } if (samplerUniforms == null) { samplerUniforms = []; } else { samplerUniforms = [...samplerUniforms]; } final _FragmentShader shader = _FragmentShader( - this, Float32List.fromList(floatUniforms), samplerUniforms); + this, Float32List.fromList(floatUniforms!), samplerUniforms); _shader(shader, floatUniforms, samplerUniforms); return shader; } From 4565abd4e8443040aea6d1c49b3927e26a4c7085 Mon Sep 17 00:00:00 2001 From: Zach Anderson Date: Fri, 8 Jul 2022 12:39:28 -0700 Subject: [PATCH 5/8] WIP --- BUILD.gn | 6 +- impeller/compiler/spirv_sksl.cc | 473 +++++++----------- impeller/compiler/spirv_sksl.h | 26 +- impeller/tools/impeller.gni | 12 + lib/spirv/test/general_shaders/BUILD.gn | 30 +- .../test/supported_glsl_op_shaders/BUILD.gn | 86 ++-- lib/spirv/test/supported_op_shaders/BUILD.gn | 78 +-- lib/ui/painting.dart | 84 +++- testing/dart/fragment_shader_test.dart | 268 +++++++++- testing/dart/shader_test_file_utils.dart | 6 +- testing/dart/spirv_exception_test.dart | 2 +- 11 files changed, 640 insertions(+), 431 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index 6226da6525712..9b1d448ba2617 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -161,9 +161,9 @@ group("unittests") { "//flutter/flow:flow_unittests", "//flutter/fml:fml_unittests", "//flutter/lib/spirv/test/exception_shaders:spirv_compile_exception_shaders", - "//flutter/lib/spirv/test/general_shaders:spirv_compile_general_shaders", - "//flutter/lib/spirv/test/supported_glsl_op_shaders:spirv_compile_supported_glsl_shaders", - "//flutter/lib/spirv/test/supported_op_shaders:spirv_compile_supported_op_shaders", + "//flutter/lib/spirv/test/general_shaders", + "//flutter/lib/spirv/test/supported_glsl_op_shaders", + "//flutter/lib/spirv/test/supported_op_shaders", "//flutter/lib/ui:ui_unittests", "//flutter/runtime:dart_plugin_registrant_unittests", "//flutter/runtime:no_dart_plugin_registrant_unittests", diff --git a/impeller/compiler/spirv_sksl.cc b/impeller/compiler/spirv_sksl.cc index f5bdfa1b32fa9..b35fb28412d4f 100644 --- a/impeller/compiler/spirv_sksl.cc +++ b/impeller/compiler/spirv_sksl.cc @@ -13,10 +13,15 @@ namespace compiler { std::string CompilerSkSL::compile() { ir.fixup_reserved_names(); - // Do not deal with ES-isms like precision, older extensions and such. + if (get_execution_model() != ExecutionModelFragment) { + SPIRV_CROSS_THROW("Only fragment shaders are supported.'"); + return ""; + } + options.es = false; - options.version = 450; + options.version = 300; options.vulkan_semantics = false; + options.enable_420pack_extension = false; backend.allow_precision_qualifiers = false; backend.basic_int16_type = "short"; @@ -57,9 +62,8 @@ std::string CompilerSkSL::compile() { pass_count++; } while (is_forcing_recompilation()); - statement("half4 main(float2 iFragCoord)"); + statement("half4 main(float2 ignored)"); begin_scope(); - statement(" sk_FragCoord = float4(iFragCoord, 0, 0);"); statement(" __main();"); statement(" return " + output_name_ + ";"); end_scope(); @@ -69,10 +73,30 @@ std::string CompilerSkSL::compile() { void CompilerSkSL::emit_header() { statement("// This SkSL shader is autogenerated by spirv-cross."); - statement("float4 sk_FragCoord;"); + statement(""); + + // This builtin is copied from + // //third_party/skia/src/sksl/sksl_rt_shader.sksl. gl_FragCoord is translated + // to sk_FragCoord instead of using the argument to 'main' passed in the + // SkSL universe. + statement("layout(builtin=15) float4 sk_FragCoord;"); + statement(""); } -void CompilerSkSL::emit_resources() { +void CompilerSkSL::emit_uniform(const SPIRVariable& var) { + auto& type = get(var.basetype); + add_resource_name(var.self); + statement(layout_for_variable(var), variable_decl(var), ";"); + + // The Flutter FragmentProgram implementation passes additional unifroms along + // with shader uniforms that encode the shader width and height. + if (type.basetype == SPIRType::SampledImage) { + std::string name = to_name(var.self); + statement("uniform half2 " + name + "_size;"); + } +} + +bool CompilerSkSL::emit_constant_resources() { bool emitted = false; for (auto& id : ir.ids) { @@ -93,10 +117,11 @@ void CompilerSkSL::emit_resources() { } } - if (emitted) { - statement(""); - } - emitted = false; + return emitted; +} + +bool CompilerSkSL::emit_struct_resources() { + bool emitted = false; // Output all basic struct types which are not Block or BufferBlock as these // are declared inplace when such variables are instantiated. @@ -115,12 +140,11 @@ void CompilerSkSL::emit_resources() { } } - if (emitted) { - statement(""); - } - emitted = false; + return emitted; +} - // Output UBOs and SSBOs +void CompilerSkSL::detect_unsupported_resources() { + // UBOs and SSBOs are not supported. for (auto& id : ir.ids) { if (id.get_type() == TypeVariable) { auto& var = id.get(); @@ -132,37 +156,32 @@ void CompilerSkSL::emit_resources() { DecorationBlock) || ir.meta[type.self].decoration.decoration_flags.get( DecorationBufferBlock))) { - emit_buffer_block(var); - emitted = true; + SPIRV_CROSS_THROW("SkSL does not support UBOs or SSBOs: '" + + get_name(var.self) + "'"); } } } - if (emitted) { - statement(""); - } - emitted = false; - - // Output push constant blocks + // Push constant blocks are not supported. for (auto& id : ir.ids) { if (id.get_type() == TypeVariable) { auto& var = id.get(); auto& type = get(var.basetype); if (!is_hidden_variable(var) && var.storage != StorageClassFunction && type.pointer && type.storage == StorageClassPushConstant) { - emit_push_constant_block(var); - emitted = true; + SPIRV_CROSS_THROW("SkSL does not support push constant blocks: '" + + get_name(var.self) + "'"); } } } +} - if (emitted) { - statement(""); - } - emitted = false; +bool CompilerSkSL::emit_uniform_resources() { + bool emitted = false; // Output Uniform Constants (values, samplers, images, etc). - std::vector uniforms; + std::vector regular_uniforms; + std::vector shader_uniforms; for (auto& id : ir.ids) { if (id.get_type() == TypeVariable) { auto& var = id.get(); @@ -171,34 +190,36 @@ void CompilerSkSL::emit_resources() { type.pointer && (type.storage == StorageClassUniformConstant || type.storage == StorageClassAtomicCounter)) { - uniforms.push_back(&var); + // Separate out the uniforms that will be of SkSL 'shader' type since + // we need to make sure they are emitted only after the other uniforms. + if (type.basetype == SPIRType::SampledImage) { + shader_uniforms.push_back(var.self); + } else { + regular_uniforms.push_back(var.self); + } emitted = true; } } } - // The uniforms have to be emitted in order of increasing location. - std::sort( - uniforms.begin(), uniforms.end(), - [this](spirv_cross::SPIRVariable* var1, spirv_cross::SPIRVariable* var2) { - if (!has_decoration(var1->self, DecorationLocation) || - !has_decoration(var2->self, DecorationLocation)) { - return false; - } - return get_decoration(var1->self, DecorationLocation) < - get_decoration(var2->self, DecorationLocation); - }); - - for (auto var : uniforms) { - emit_uniform(*var); + for (const auto& id : regular_uniforms) { + auto& var = get(id); + emit_uniform(var); } - if (emitted) { - statement(""); + for (const auto& id : shader_uniforms) { + auto& var = get(id); + emit_uniform(var); } - emitted = false; - // Output in/out interfaces. + return emitted; +} + +bool CompilerSkSL::emit_output_resources() { + bool emitted = false; + + // Output 'out' variables. These are restricted to the cases handled by + // SkSL in 'emit_interface_block'. for (auto& id : ir.ids) { if (id.get_type() == TypeVariable) { auto& var = id.get(); @@ -214,12 +235,12 @@ void CompilerSkSL::emit_resources() { } } - if (emitted) { - statement(""); - } - emitted = false; + return emitted; +} + +bool CompilerSkSL::emit_global_variable_resources() { + bool emitted = false; - // Global variables. for (auto global : global_variables) { auto& var = get(global); if (is_hidden_variable(var, true)) { @@ -245,7 +266,29 @@ void CompilerSkSL::emit_resources() { } } - if (emitted) { + return emitted; +} + +void CompilerSkSL::emit_resources() { + detect_unsupported_resources(); + + if (emit_constant_resources()) { + statement(""); + } + + if (emit_struct_resources()) { + statement(""); + } + + if (emit_uniform_resources()) { + statement(""); + } + + if (emit_output_resources()) { + statement(""); + } + + if (emit_global_variable_resources()) { statement(""); } @@ -254,287 +297,117 @@ void CompilerSkSL::emit_resources() { void CompilerSkSL::emit_interface_block(const SPIRVariable& var) { auto& type = get(var.basetype); - - const char* qual = to_storage_qualifiers_glsl(var); - - // Either make it plain in/out or in/out blocks depending on what shader is - // doing ... bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock); if (block) { - // Workaround to make sure we can emit "patch in/out" correctly. - fixup_io_block_patch_qualifiers(var); - - // Block names should never alias. - auto block_name = to_name(type.self, false); - - // The namespace for I/O blocks is separate from other variables in GLSL. - auto& block_namespace = type.storage == StorageClassInput - ? block_input_names - : block_output_names; - - // Shaders never use the block by interface name, so we don't - // have to track this other than updating name caches. - if (block_name.empty() || - block_namespace.find(block_name) != end(block_namespace)) { - block_name = get_fallback_name(type.self); - } else { - block_namespace.insert(block_name); - } - - // If for some reason buffer_name is an illegal name, make a final fallback - // to a workaround name. This cannot conflict with anything else, so we're - // safe now. - if (block_name.empty()) { - block_name = join("_", get(var.basetype).self, "_", var.self); - } - - // Instance names cannot alias block names. - resource_names.insert(block_name); - - bool is_patch = has_decoration(var.self, DecorationPatch); - statement(layout_for_variable(var), (is_patch ? "patch " : ""), qual, - block_name); - begin_scope(); - - type.member_name_cache.clear(); - - uint32_t i = 0; - for (auto& member : type.member_types) { - add_member_name(type, i); - emit_struct_member(type, member, i); - i++; - } + SPIRV_CROSS_THROW("Interface blocks are not supported: '" + + to_name(var.self) + "'"); + } - add_resource_name(var.self); - end_scope_decl(join(to_name(var.self), type_to_array_glsl(type))); - statement(""); + // The output is emitted as a global variable, which is returned from the + // wrapper around the 'main' function. Only one output variable is allowed. + add_resource_name(var.self); + statement(variable_decl(type, to_name(var.self), var.self), ";"); + if (output_name_.empty()) { + output_name_ = to_name(var.self); } else { - add_resource_name(var.self); - statement(variable_decl(type, to_name(var.self), var.self), ";"); - if (output_name_.empty()) { - output_name_ = to_name(var.self); - } + SPIRV_CROSS_THROW("Only one output variable is supported: '" + + to_name(var.self) + "'"); } } void CompilerSkSL::emit_function_prototype(SPIRFunction& func, const Bitset& return_flags) { + // If this is not the entrypoint, then no special processsing for SkSL is + // required. if (func.self != ir.default_entry_point) { - add_function_overload(func); + CompilerGLSL::emit_function_prototype(func, return_flags); + return; } - // Avoid shadow declarations. - local_variable_names = resource_names; - - std::string decl; - auto& type = get(func.return_type); - decl += flags_to_qualifiers_glsl(type, return_flags); - decl += type_to_glsl(type); - decl += type_to_array_glsl(type); - decl += " "; - - if (func.self == ir.default_entry_point) { - // SkSL requires a return value from main, so we wrap the entire entrypoint. - // The rest of this method is copied from spirv_glsl.cpp. - decl += "__main"; - processing_entry_point = true; - } else { - decl += to_name(func.self); + if (type.basetype != SPIRType::Void) { + SPIRV_CROSS_THROW("Return type of the entrypoint function must be 'void'"); } - decl += "("; - SmallVector arglist; - for (auto& arg : func.arguments) { - // Do not pass in separate images or samplers if we're remapping - // to combined image samplers. - if (skip_argument(arg.id)) { - continue; - } - - // Might change the variable name if it already exists in this function. - // SPIRV OpName doesn't have any semantic effect, so it's valid for an - // implementation to use same name for variables. Since we want to make the - // GLSL debuggable and somewhat sane, use fallback names for variables which - // are duplicates. - add_local_variable_name(arg.id); - - arglist.push_back(argument_decl(arg)); - - // Hold a pointer to the parameter so we can invalidate the readonly field - // if needed. - auto* var = maybe_get(arg.id); - if (var) { - var->parameter = &arg; - } + if (func.arguments.size() != 0) { + SPIRV_CROSS_THROW( + "The entry point function should not acept any parameters."); } - for (auto& arg : func.shadow_arguments) { - // Might change the variable name if it already exists in this function. - // SPIRV OpName doesn't have any semantic effect, so it's valid for an - // implementation to use same name for variables. Since we want to make the - // GLSL debuggable and somewhat sane, use fallback names for variables which - // are duplicates. - add_local_variable_name(arg.id); - - arglist.push_back(argument_decl(arg)); - - // Hold a pointer to the parameter so we can invalidate the readonly field - // if needed. - auto* var = maybe_get(arg.id); - if (var) { - var->parameter = &arg; - } - } + processing_entry_point = true; - decl += merge(arglist); - decl += ")"; - statement(decl); + // If this is the entrypoint of a fragment shader, then GLSL requires the + // prototype to be "void main()", and so it is safe to rewrite as + // "void __main()". + statement("void __main()"); } -std::string CompilerSkSL::type_to_glsl(const spirv_cross::SPIRType& type, - uint32_t id) { - std::string result = spirv_cross::CompilerGLSL::type_to_glsl(type, id); - - // Rewrite the type where SkSL spells it differently. - - if (type.vecsize == 1 && type.columns == 1) { // Scalar builtins. - switch (type.basetype) { - case SPIRType::Boolean: - return "bool"; - case SPIRType::SByte: - return backend.basic_int8_type; - case SPIRType::UByte: - return backend.basic_uint8_type; - case SPIRType::Short: - return backend.basic_int16_type; - case SPIRType::UShort: - return backend.basic_uint16_type; - case SPIRType::Int: - return backend.basic_int_type; - case SPIRType::UInt: - return backend.basic_uint_type; - case SPIRType::AtomicCounter: - return "atomic_uint"; - case SPIRType::Half: - return "half"; - case SPIRType::Float: - return "float"; - case SPIRType::Double: - return "float"; - case SPIRType::Int64: - return "int"; - case SPIRType::UInt64: - return "uint"; - case SPIRType::Void: - return "void"; - default: - return "???"; - } - } else if (type.vecsize > 1 && type.columns == 1) { // Vector builtins. - switch (type.basetype) { - case SPIRType::Boolean: - return join("bool", type.vecsize); - case SPIRType::SByte: - return join("i8vec", type.vecsize); - case SPIRType::UByte: - return join("u8vec", type.vecsize); - case SPIRType::Short: - return join("short", type.vecsize); - case SPIRType::UShort: - return join("ushort", type.vecsize); - case SPIRType::Int: - return join("int", type.vecsize); - case SPIRType::UInt: - return join("uint", type.vecsize); - case SPIRType::Half: - return join("half", type.vecsize); - case SPIRType::Float: - return join("float", type.vecsize); - case SPIRType::Double: - return join("float", type.vecsize); - case SPIRType::Int64: - return join("int", type.vecsize); - case SPIRType::UInt64: - return join("uint", type.vecsize); - default: - return "???"; - } - } else if (type.vecsize == type.columns) { // Simple Matrix builtin - switch (type.basetype) { - case SPIRType::Boolean: - return join("bool", type.vecsize, "x", type.vecsize); - case SPIRType::Int: - return join("int", type.vecsize, "x", type.vecsize); - case SPIRType::UInt: - return join("uint", type.vecsize, "x", type.vecsize); - case SPIRType::Half: - return join("half", type.vecsize, "x", type.vecsize); - case SPIRType::Float: - return join("float", type.vecsize, "x", type.vecsize); - case SPIRType::Double: - return join("float", type.vecsize, "x", type.vecsize); - // Matrix types not supported for int64/uint64. - default: - return "???"; - } - } else { - switch (type.basetype) { - case SPIRType::Boolean: - return join("bool", type.columns, "x", type.vecsize); - case SPIRType::Int: - return join("int", type.columns, "x", type.vecsize); - case SPIRType::UInt: - return join("uint", type.columns, "x", type.vecsize); - case SPIRType::Half: - return join("half", type.columns, "x", type.vecsize); - case SPIRType::Float: - return join("float", type.columns, "x", type.vecsize); - case SPIRType::Double: - return join("float", type.columns, "x", type.vecsize); - // Matrix types not supported for int64/uint64. - default: - return "???"; - } +std::string CompilerSkSL::image_type_glsl(const spirv_cross::SPIRType& type, + uint32_t id) { + if (type.basetype != SPIRType::SampledImage || type.image.dim != Dim2D) { + SPIRV_CROSS_THROW("Only sampler2D uniform image types are supported."); + return "???"; } - - return result; + return "shader"; } std::string CompilerSkSL::builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) { - std::string result = + std::string gl_builtin = spirv_cross::CompilerGLSL::builtin_to_glsl(builtin, storage); - switch (builtin) { - case BuiltInPosition: - return "sk_Position"; - case BuiltInPointSize: - return "sk_PointSize"; - case BuiltInVertexId: - return "sk_VertexID"; - case BuiltInInstanceId: - return "sk_InstanceID"; - case BuiltInVertexIndex: - return "sk_VertexID"; - case BuiltInInstanceIndex: - return "sk_InstanceID"; case BuiltInFragCoord: return "sk_FragCoord"; - case BuiltInFrontFacing: - return "sk_Clockwise"; default: + SPIRV_CROSS_THROW("Builtin '" + gl_builtin + "' is not supported."); break; } - return result; + return "???"; } -void CompilerSkSL::emit_uniform(const spirv_cross::SPIRVariable& var) { - spirv_cross::CompilerGLSL::add_resource_name(var.self); - spirv_cross::CompilerGLSL::statement( - spirv_cross::CompilerGLSL::variable_decl(var), ";"); +std::string CompilerSkSL::to_texture_op( + const Instruction& i, + bool sparse, + bool* forward, + SmallVector& inherited_expressions) { + auto op = static_cast(i.op); + if (op != OpImageSampleImplicitLod) { + SPIRV_CROSS_THROW("Only simple shader sampling is supported."); + return "???"; + } + return CompilerGLSL::to_texture_op(i, sparse, forward, inherited_expressions); +} + +std::string CompilerSkSL::to_function_name( + const spirv_cross::CompilerGLSL::TextureFunctionNameArguments& args) { + std::string name = to_expression(args.base.img); + return name + ".eval"; +} + +std::string CompilerSkSL::to_function_args(const TextureFunctionArguments& args, + bool* p_forward) { + std::string name = to_expression(args.base.img); + + std::string glsl_args = CompilerGLSL::to_function_args(args, p_forward); + + // GLSL puts the shader as the first argument, but in SkSL the shader is + // implicitly passed as the reciever of the 'eval' method. Therefore, the + // shader is removed from the GLSL argument list. + std::string no_shader; + auto npos = glsl_args.find(", "); // The first ','. + if (npos != std::string::npos) { + no_shader = glsl_args.substr(npos + 1); // The string after the first ','. + } + + if (no_shader.empty()) { + SPIRV_CROSS_THROW("Unexpected shader sampling arguments: '(" + glsl_args + + ")'"); + return "()"; + } + + return name + "_size * " + no_shader; } } // namespace compiler diff --git a/impeller/compiler/spirv_sksl.h b/impeller/compiler/spirv_sksl.h index 753468e37f35f..3fbe4cc1fdcae 100644 --- a/impeller/compiler/spirv_sksl.h +++ b/impeller/compiler/spirv_sksl.h @@ -36,6 +36,14 @@ class CompilerSkSL : public spirv_cross::CompilerGLSL { void emit_header() override; + void emit_uniform(const spirv_cross::SPIRVariable& var) override; + + void detect_unsupported_resources(); + bool emit_constant_resources(); + bool emit_struct_resources(); + bool emit_uniform_resources(); + bool emit_output_resources(); + bool emit_global_variable_resources(); void emit_resources(); void emit_interface_block(const spirv_cross::SPIRVariable& var); @@ -44,13 +52,25 @@ class CompilerSkSL : public spirv_cross::CompilerGLSL { spirv_cross::SPIRFunction& func, const spirv_cross::Bitset& return_flags) override; - std::string type_to_glsl(const spirv_cross::SPIRType& type, - uint32_t id = 0) override; + std::string image_type_glsl(const spirv_cross::SPIRType& type, + uint32_t id = 0) override; std::string builtin_to_glsl(spv::BuiltIn builtin, spv::StorageClass storage) override; - void emit_uniform(const spirv_cross::SPIRVariable& var) override; + std::string to_texture_op( + const spirv_cross::Instruction& i, + bool sparse, + bool* forward, + spirv_cross::SmallVector& inherited_expressions) override; + + std::string to_function_name( + const spirv_cross::CompilerGLSL::TextureFunctionNameArguments& args) + override; + + std::string to_function_args( + const spirv_cross::CompilerGLSL::TextureFunctionArguments& args, + bool* p_forward) override; }; } // namespace compiler diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index 3ba80e79a321f..8e92129c97a17 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -237,6 +237,7 @@ template("impellerc") { "The flag to impellerc for target selection must be specified.") flutter_spirv = invoker.shader_target_flag == "--flutter-spirv" + sksl = invoker.shader_target_flag == "--sksl" if (!flutter_spirv) { assert( defined(invoker.sl_file_extension), @@ -275,8 +276,19 @@ template("impellerc") { ] if (flutter_spirv) { + not_needed([ "sksl" ]) args += [ "--spirv=$spirv_intermediate_path" ] outputs = [ spirv_intermediate ] + } else if (sksl) { + sl_intermediate = + "$generated_dir/{{source_file_part}}.${invoker.sl_file_extension}" + sl_intermediate_path = rebase_path(sl_intermediate, root_build_dir) + args += [ + "--sl=$sl_intermediate_path", + "--spirv=$spirv_intermediate_path", + ] + + outputs = [ sl_intermediate ] } else { sl_intermediate = "$generated_dir/{{source_file_part}}.${invoker.sl_file_extension}" diff --git a/lib/spirv/test/general_shaders/BUILD.gn b/lib/spirv/test/general_shaders/BUILD.gn index 2edcb014ea138..e12f315b8dafd 100644 --- a/lib/spirv/test/general_shaders/BUILD.gn +++ b/lib/spirv/test/general_shaders/BUILD.gn @@ -7,15 +7,31 @@ import("//flutter/impeller/tools/impeller.gni") import("//flutter/testing/testing.gni") if (enable_unittests) { - impellerc("spirv_compile_general_shaders") { - shaders = [ - "blue_green_sampler.frag", - "children_and_uniforms.frag", - "functions.frag", - "simple.frag", - "uniforms.frag", + test_shaders = [ + "blue_green_sampler.frag", + "children_and_uniforms.frag", + "functions.frag", + "simple.frag", + "uniforms.frag", + ] + + group("general_shaders") { + deps = [ + ":sksl_compile_general_shaders", + ":spirv_compile_general_shaders", ] + } + impellerc("spirv_compile_general_shaders") { + shaders = test_shaders shader_target_flag = "--flutter-spirv" + intermediates_subdir = "spirv" + } + + impellerc("sksl_compile_general_shaders") { + shaders = test_shaders + shader_target_flag = "--sksl" + intermediates_subdir = "sksl" + sl_file_extension = "sksl" } } diff --git a/lib/spirv/test/supported_glsl_op_shaders/BUILD.gn b/lib/spirv/test/supported_glsl_op_shaders/BUILD.gn index b2c78ee13facb..3f8b442af6419 100644 --- a/lib/spirv/test/supported_glsl_op_shaders/BUILD.gn +++ b/lib/spirv/test/supported_glsl_op_shaders/BUILD.gn @@ -7,43 +7,59 @@ import("//flutter/impeller/tools/impeller.gni") import("//flutter/testing/testing.gni") if (enable_unittests) { - impellerc("spirv_compile_supported_glsl_shaders") { - shaders = [ - "10_fract.frag", - "11_radians.frag", - "12_degrees.frag", - "13_sin.frag", - "14_cos.frag", - "15_tan.frag", - "16_asin.frag", - "17_acos.frag", - "18_atan.frag", - "25_atan2.frag", - "26_pow.frag", - "27_exp.frag", - "28_log.frag", - "29_exp2.frag", - "30_log2.frag", - "31_sqrt.frag", - "32_inversesqrt.frag", - "37_fmin.frag", - "40_fmax.frag", - "43_fclamp.frag", - "46_fmix.frag", - "48_step.frag", - "49_smoothstep.frag", - "4_abs.frag", - "66_length.frag", - "67_distance.frag", - "68_cross.frag", - "69_normalize.frag", - "6_sign.frag", - "70_faceforward.frag", - "71_reflect.frag", - "8_floor.frag", - "9_ceil.frag", + test_shaders = [ + "10_fract.frag", + "11_radians.frag", + "12_degrees.frag", + "13_sin.frag", + "14_cos.frag", + "15_tan.frag", + "16_asin.frag", + "17_acos.frag", + "18_atan.frag", + "25_atan2.frag", + "26_pow.frag", + "27_exp.frag", + "28_log.frag", + "29_exp2.frag", + "30_log2.frag", + "31_sqrt.frag", + "32_inversesqrt.frag", + "37_fmin.frag", + "40_fmax.frag", + "43_fclamp.frag", + "46_fmix.frag", + "48_step.frag", + "49_smoothstep.frag", + "4_abs.frag", + "66_length.frag", + "67_distance.frag", + "68_cross.frag", + "69_normalize.frag", + "6_sign.frag", + "70_faceforward.frag", + "71_reflect.frag", + "8_floor.frag", + "9_ceil.frag", + ] + + group("supported_glsl_op_shaders") { + deps = [ + ":sksl_compile_supported_glsl_shaders", + ":spirv_compile_supported_glsl_shaders", ] + } + impellerc("spirv_compile_supported_glsl_shaders") { + shaders = test_shaders shader_target_flag = "--flutter-spirv" + intermediates_subdir = "spirv" + } + + impellerc("sksl_compile_supported_glsl_shaders") { + shaders = test_shaders + shader_target_flag = "--sksl" + intermediates_subdir = "sksl" + sl_file_extension = "sksl" } } diff --git a/lib/spirv/test/supported_op_shaders/BUILD.gn b/lib/spirv/test/supported_op_shaders/BUILD.gn index e81be7f649fd2..13e3ba55fceb5 100644 --- a/lib/spirv/test/supported_op_shaders/BUILD.gn +++ b/lib/spirv/test/supported_op_shaders/BUILD.gn @@ -7,39 +7,55 @@ import("//flutter/impeller/tools/impeller.gni") import("//flutter/testing/testing.gni") if (enable_unittests) { - impellerc("spirv_compile_supported_op_shaders") { - shaders = [ - "127_OpFNegate.frag", - "129_OpFAdd.frag", - "131_OpFSub.frag", - "142_OpVectorTimesScalar.frag", - "143_OpMatrixTimesScalar.frag", - "144_OpVectorTimesMatrix.frag", - "145_OpMatrixTimesVector.frag", - "146_OpMatrixTimesMatrix.frag", - "148_OpDot.frag", - "164_OpLogicalEqual.frag", - "165_OpLogicalNotEqual.frag", - "166_OpLogicalOr.frag", - "167_OpLogicalAnd.frag", - "168_OpLogicalNot.frag", - "180_OpFOrdEqual.frag", - "183_OpFUnordNotEqual.frag", - "184_OpFOrdLessThan.frag", - "186_OpFOrdGreaterThan.frag", - "188_OpFOrdLessThanEqual.frag", - "190_OpFOrdGreaterThanEqual.frag", - "19_OpTypeVoid.frag", - "20_OpTypeBool.frag", - "21_OpTypeInt.frag", - "22_OpTypeFloat.frag", - "23_OpTypeVector.frag", - "246_OpLoopMerge.frag", - "24_OpTypeMatrix.frag", - "250_OpBranchConditional.frag", - "33_OpTypeFunction.frag", + test_shaders = [ + "127_OpFNegate.frag", + "129_OpFAdd.frag", + "131_OpFSub.frag", + "142_OpVectorTimesScalar.frag", + "143_OpMatrixTimesScalar.frag", + "144_OpVectorTimesMatrix.frag", + "145_OpMatrixTimesVector.frag", + "146_OpMatrixTimesMatrix.frag", + "148_OpDot.frag", + "164_OpLogicalEqual.frag", + "165_OpLogicalNotEqual.frag", + "166_OpLogicalOr.frag", + "167_OpLogicalAnd.frag", + "168_OpLogicalNot.frag", + "180_OpFOrdEqual.frag", + "183_OpFUnordNotEqual.frag", + "184_OpFOrdLessThan.frag", + "186_OpFOrdGreaterThan.frag", + "188_OpFOrdLessThanEqual.frag", + "190_OpFOrdGreaterThanEqual.frag", + "19_OpTypeVoid.frag", + "20_OpTypeBool.frag", + "21_OpTypeInt.frag", + "22_OpTypeFloat.frag", + "23_OpTypeVector.frag", + "246_OpLoopMerge.frag", + "24_OpTypeMatrix.frag", + "250_OpBranchConditional.frag", + "33_OpTypeFunction.frag", + ] + + group("supported_op_shaders") { + deps = [ + ":sksl_compile_supported_op_shaders", + ":spirv_compile_supported_op_shaders", ] + } + impellerc("spirv_compile_supported_op_shaders") { + shaders = test_shaders shader_target_flag = "--flutter-spirv" + intermediates_subdir = "spirv" + } + + impellerc("sksl_compile_supported_op_shaders") { + shaders = test_shaders + shader_target_flag = "--sksl" + intermediates_subdir = "sksl" + sl_file_extension = "sksl" } } diff --git a/lib/ui/painting.dart b/lib/ui/painting.dart index 5c082ae64c8d0..b57bbaad28250 100644 --- a/lib/ui/painting.dart +++ b/lib/ui/painting.dart @@ -3906,37 +3906,69 @@ class FragmentProgram extends NativeFieldWrapperClass1 { /// [A current specification of valid SPIR-V is here.](https://github.com/flutter/engine/blob/master/lib/spirv/README.md) /// SPIR-V not meeting this specification will throw an exception. static Future compile({ - required ByteBuffer spirv, - ByteBuffer? sksl, + ByteBuffer? spirv, + ByteBuffer? raw, + int? uniformFloatCount, + int? samplerCount, bool debugPrint = false, }) { + final bool spirvNull = spirv == null; + final bool rawNull = raw == null; + if (spirvNull && rawNull) { + throw ArgumentError( + 'FragmentProgram.compile must be passed either the "spirv" or the ' + '"raw" argument.', + ); + } + if (!spirvNull && !rawNull) { + throw ArgumentError( + 'FragmentProgram.compile must be passed only one of the "spirv" or the ' + '"raw" arguments.', + ); + } + if (!rawNull && uniformFloatCount == null && samplerCount == null) { + throw ArgumentError( + 'FragmentProgram.compile requires the "uniformFloatCount" or the ' + '"samplerCount" argument when passing the "raw" argument.', + ); + } // Delay compilation without creating a timer, which interacts poorly with the // flutter test framework. See: https://github.com/flutter/flutter/issues/104084 - return Future.microtask(() => FragmentProgram._(spirv: spirv, sksl: sksl, debugPrint: debugPrint)); + return Future.microtask(() => FragmentProgram._( + spirv: spirv, + raw: raw, + uniformFloatCount: uniformFloatCount ?? 0, + samplerCount: samplerCount ?? 0, + debugPrint: debugPrint, + )); } @pragma('vm:entry-point') FragmentProgram._({ - required ByteBuffer spirv, - ByteBuffer? sksl, + ByteBuffer? spirv, + ByteBuffer? raw, + required int uniformFloatCount, + required int samplerCount, bool debugPrint = false, }) { _constructor(); - if (sksl == null) { + if (raw == null) { final spv.TranspileResult result = spv.transpile( - spirv, + spirv!, spv.TargetLanguage.sksl, ); _init(result.src, debugPrint); - // _uniformFloatCount = result.uniformFloatCount; - // _samplerCount = result.samplerCount; + _uniformFloatCount = result.uniformFloatCount; + _samplerCount = result.samplerCount; } else { - _init(utf8.decode(sksl.asUint8List()), debugPrint); + _init(utf8.decode(raw.asUint8List()), debugPrint); + _uniformFloatCount = uniformFloatCount; + _samplerCount = samplerCount; } } - // late final int _uniformFloatCount; - // late final int _samplerCount; + late final int _uniformFloatCount; + late final int _samplerCount; void _constructor() native 'FragmentProgram_constructor'; void _init(String sksl, bool debugPrint) native 'FragmentProgram_init'; @@ -3994,23 +4026,29 @@ class FragmentProgram extends NativeFieldWrapperClass1 { Float32List? floatUniforms, List? samplerUniforms, }) { - // if (floatUniforms == null) { - // floatUniforms = Float32List(_uniformFloatCount); - // } - // if (floatUniforms.length != _uniformFloatCount) { - // throw ArgumentError( - // 'floatUniforms size: ${floatUniforms.length} must match given shader uniform count: $_uniformFloatCount.'); - // } - // if (_samplerCount > 0 && (samplerUniforms == null || samplerUniforms.length != _samplerCount)) { - // throw ArgumentError('samplerUniforms must have length $_samplerCount'); - // } + if (floatUniforms == null) { + floatUniforms = Float32List(_uniformFloatCount); + } + if (floatUniforms.length != _uniformFloatCount) { + throw ArgumentError( + 'floatUniforms size: ${floatUniforms.length} must match given shader ' + 'uniform count: $_uniformFloatCount.', + ); + } + if (_samplerCount > 0 && + (samplerUniforms == null || samplerUniforms.length != _samplerCount)) { + throw ArgumentError('samplerUniforms must have length $_samplerCount'); + } if (samplerUniforms == null) { samplerUniforms = []; } else { samplerUniforms = [...samplerUniforms]; } final _FragmentShader shader = _FragmentShader( - this, Float32List.fromList(floatUniforms!), samplerUniforms); + this, + Float32List.fromList(floatUniforms), + samplerUniforms, + ); _shader(shader, floatUniforms, samplerUniforms); return shader; } diff --git a/testing/dart/fragment_shader_test.dart b/testing/dart/fragment_shader_test.dart index 4d352a9e26683..b9140dfdebfc6 100644 --- a/testing/dart/fragment_shader_test.dart +++ b/testing/dart/fragment_shader_test.dart @@ -15,7 +15,9 @@ import 'shader_test_file_utils.dart'; void main() { test('throws exception for invalid shader', () async { - final ByteBuffer invalidBytes = Uint8List.fromList([1, 2, 3, 4, 5]).buffer; + final ByteBuffer invalidBytes = Uint8List.fromList( + [1, 2, 3, 4, 5], + ).buffer; try { await FragmentProgram.compile(spirv: invalidBytes); fail('expected compile to throw an exception'); @@ -24,7 +26,10 @@ void main() { }); test('simple shader renders correctly', () async { - final Uint8List shaderBytes = await spvFile('general_shaders', 'functions.frag.spirv').readAsBytes(); + final Uint8List shaderBytes = await shaderFile( + path.join('general_shaders', 'spirv'), + 'functions.frag.spirv', + ).readAsBytes(); final FragmentProgram program = await FragmentProgram.compile( spirv: shaderBytes.buffer, ); @@ -34,8 +39,26 @@ void main() { _expectShaderRendersGreen(shader); }); + test('simple sksl shader renders correctly', () async { + final Uint8List shaderBytes = await shaderFile( + path.join('general_shaders', 'sksl'), + 'functions.frag.sksl', + ).readAsBytes(); + final FragmentProgram program = await FragmentProgram.compile( + raw: shaderBytes.buffer, + uniformFloatCount: 1, + ); + final Shader shader = program.shader( + floatUniforms: Float32List.fromList([1]), + ); + _expectShaderRendersGreen(shader); + }); + test('shader with functions renders green', () async { - final ByteBuffer spirv = spvFile('general_shaders', 'functions.frag.spirv').readAsBytesSync().buffer; + final ByteBuffer spirv = shaderFile( + path.join('general_shaders', 'spirv'), + 'functions.frag.spirv', + ).readAsBytesSync().buffer; final FragmentProgram program = await FragmentProgram.compile( spirv: spirv, ); @@ -45,8 +68,26 @@ void main() { _expectShaderRendersGreen(shader); }); + test('sksl shader with functions renders green', () async { + final ByteBuffer sksl = shaderFile( + path.join('general_shaders', 'sksl'), + 'functions.frag.sksl', + ).readAsBytesSync().buffer; + final FragmentProgram program = await FragmentProgram.compile( + raw: sksl, + uniformFloatCount: 1, + ); + final Shader shader = program.shader( + floatUniforms: Float32List.fromList([1]), + ); + _expectShaderRendersGreen(shader); + }); + test('blue-green image renders green', () async { - final ByteBuffer spirv = spvFile('general_shaders', 'blue_green_sampler.frag.spirv').readAsBytesSync().buffer; + final ByteBuffer spirv = shaderFile( + path.join('general_shaders', 'spirv'), + 'blue_green_sampler.frag.spirv', + ).readAsBytesSync().buffer; final FragmentProgram program = await FragmentProgram.compile( debugPrint: true, spirv: spirv, @@ -55,14 +96,41 @@ void main() { final ImageShader imageShader = ImageShader( blueGreenImage, TileMode.clamp, TileMode.clamp, _identityMatrix); final Shader shader = program.shader( + floatUniforms: Float32List.fromList([]), + samplerUniforms: [imageShader], + ); + await _expectShaderRendersGreen(shader); + }); + + test('sksl blue-green image renders green', () async { + final ByteBuffer sksl = shaderFile( + path.join('general_shaders', 'sksl'), + 'blue_green_sampler.frag.sksl', + ).readAsBytesSync().buffer; + final FragmentProgram program = await FragmentProgram.compile( + debugPrint: true, + raw: sksl, + uniformFloatCount: 0, + samplerCount: 1, + ); + final Image blueGreenImage = await _createBlueGreenImage(); + final ImageShader imageShader = ImageShader( + blueGreenImage, TileMode.clamp, TileMode.clamp, _identityMatrix); + final Shader shader = program.shader( + floatUniforms: Float32List.fromList([]), samplerUniforms: [imageShader], ); await _expectShaderRendersGreen(shader); }); test('shader with uniforms renders correctly', () async { - final Uint8List shaderBytes = await spvFile('general_shaders', 'uniforms.frag.spirv').readAsBytes(); - final FragmentProgram program = await FragmentProgram.compile(spirv: shaderBytes.buffer); + final Uint8List shaderBytes = await shaderFile( + path.join('general_shaders', 'spirv'), + 'uniforms.frag.spirv', + ).readAsBytes(); + final FragmentProgram program = await FragmentProgram.compile( + spirv: shaderBytes.buffer, + ); final Shader shader = program.shader( floatUniforms: Float32List.fromList([ @@ -85,23 +153,74 @@ void main() { expect(toFloat(renderedBytes.getUint8(3)), closeTo(1.0, epsilon)); }); + test('sksl shader with uniforms renders correctly', () async { + final Uint8List shaderBytes = await shaderFile( + path.join('general_shaders', 'sksl'), + 'uniforms.frag.sksl', + ).readAsBytes(); + final FragmentProgram program = await FragmentProgram.compile( + raw: shaderBytes.buffer, + uniformFloatCount: 7, + ); + + final Shader shader = program.shader( + floatUniforms: Float32List.fromList([ + 0.0, // iFloatUniform + 0.25, // iVec2Uniform.x + 0.75, // iVec2Uniform.y + 0.0, // iMat2Uniform[0][0] + 0.0, // iMat2Uniform[0][1] + 0.0, // iMat2Uniform[1][0] + 1.0, // iMat2Uniform[1][1] + ])); + + final ByteData renderedBytes = (await _imageByteDataFromShader( + shader: shader, + ))!; + + expect(toFloat(renderedBytes.getUint8(0)), closeTo(0.0, epsilon)); + expect(toFloat(renderedBytes.getUint8(1)), closeTo(0.25, epsilon)); + expect(toFloat(renderedBytes.getUint8(2)), closeTo(0.75, epsilon)); + expect(toFloat(renderedBytes.getUint8(3)), closeTo(1.0, epsilon)); + }); + // Test all supported GLSL ops. See lib/spirv/lib/src/constants.dart - final Map supportedGLSLOpShaders = - _loadSpv('supported_glsl_op_shaders'); + final Map supportedGLSLOpShaders = _loadShaders( + path.join('supported_glsl_op_shaders', 'spirv'), + '.spirv', + ); expect(supportedGLSLOpShaders.isNotEmpty, true); _expectShadersRenderGreen(supportedGLSLOpShaders); _expectShadersHaveOp(supportedGLSLOpShaders, true /* glsl ops */); + final Map skslSupportedGLSLOpShaders = _loadShaders( + path.join('supported_glsl_op_shaders', 'sksl'), + '.sksl', + ); + expect(skslSupportedGLSLOpShaders.isNotEmpty, true); + _expectSkSLShadersRenderGreen(skslSupportedGLSLOpShaders); + // Test all supported instructions. See lib/spirv/lib/src/constants.dart - final Map supportedOpShaders = - _loadSpv('supported_op_shaders'); + final Map supportedOpShaders = _loadShaders( + path.join('supported_op_shaders', 'spirv'), + '.spirv', + ); expect(supportedOpShaders.isNotEmpty, true); _expectShadersRenderGreen(supportedOpShaders); _expectShadersHaveOp(supportedOpShaders, false /* glsl ops */); + final Map skslSupportedOpShaders = _loadShaders( + path.join('supported_op_shaders', 'sksl'), + '.sksl', + ); + expect(skslSupportedOpShaders.isNotEmpty, true); + _expectSkSLShadersRenderGreen(skslSupportedOpShaders); + test('equality depends on floatUniforms', () async { - final ByteBuffer spirv = spvFile('general_shaders', 'simple.frag.spirv') - .readAsBytesSync().buffer; + final ByteBuffer spirv = shaderFile( + path.join('general_shaders', 'spirv'), + 'simple.frag.spirv', + ).readAsBytesSync().buffer; final FragmentProgram program = await FragmentProgram.compile(spirv: spirv); final Float32List ones = Float32List.fromList([1]); final Float32List zeroes = Float32List.fromList([0]); @@ -121,11 +240,42 @@ void main() { } }); + test('sksl equality depends on floatUniforms', () async { + final ByteBuffer sksl = shaderFile( + path.join('general_shaders', 'sksl'), + 'simple.frag.sksl', + ).readAsBytesSync().buffer; + final FragmentProgram program = await FragmentProgram.compile( + raw: sksl, + uniformFloatCount: 1, + ); + final Float32List ones = Float32List.fromList([1]); + final Float32List zeroes = Float32List.fromList([0]); + + { + final Shader a = program.shader(floatUniforms: ones); + final Shader b = program.shader(floatUniforms: ones); + expect(a, b); + expect(a.hashCode, b.hashCode); + } + + { + final Shader a = program.shader(floatUniforms: ones); + final Shader b = program.shader(floatUniforms: zeroes); + expect(a, notEquals(b)); + expect(a.hashCode, notEquals(b.hashCode)); + } + }); + test('equality depends on spirv', () async { - final ByteBuffer spirvA = spvFile('general_shaders', 'simple.frag.spirv') - .readAsBytesSync().buffer; - final ByteBuffer spirvB = spvFile('general_shaders', 'uniforms.frag.spirv') - .readAsBytesSync().buffer; + final ByteBuffer spirvA = shaderFile( + path.join('general_shaders', 'spirv'), + 'simple.frag.spirv', + ).readAsBytesSync().buffer; + final ByteBuffer spirvB = shaderFile( + path.join('general_shaders', 'spirv'), + 'uniforms.frag.spirv', + ).readAsBytesSync().buffer; final FragmentProgram programA = await FragmentProgram.compile(spirv: spirvA); final FragmentProgram programB = await FragmentProgram.compile(spirv: spirvB); final Shader a = programA.shader(); @@ -135,20 +285,70 @@ void main() { expect(a.hashCode, notEquals(b.hashCode)); }); + test('equality depends on data', () async { + final ByteBuffer skslA = shaderFile( + path.join('general_shaders', 'sksl'), + 'simple.frag.sksl', + ).readAsBytesSync().buffer; + final ByteBuffer skslB = shaderFile( + path.join('general_shaders', 'sksl'), + 'uniforms.frag.sksl', + ).readAsBytesSync().buffer; + final FragmentProgram programA = await FragmentProgram.compile( + raw: skslA, + uniformFloatCount: 1, + ); + final FragmentProgram programB = await FragmentProgram.compile( + raw: skslB, + uniformFloatCount: 7, + ); + final Shader a = programA.shader(); + final Shader b = programB.shader(); + + expect(a, notEquals(b)); + expect(a.hashCode, notEquals(b.hashCode)); + }); + test('Compilation does not create a Timer object', () async { - final ByteBuffer spirvA = spvFile('general_shaders', 'simple.frag.spirv') - .readAsBytesSync().buffer; + final ByteBuffer spirvA = shaderFile( + path.join('general_shaders', 'spirv'), + 'simple.frag.spirv', + ).readAsBytesSync().buffer; bool createdTimer = false; - final ZoneSpecification specification = ZoneSpecification(createTimer: (Zone self, ZoneDelegate parent, Zone zone, Duration duration, void Function() f) { - createdTimer = true; - return parent.createTimer(zone, duration, f); - }); + final ZoneSpecification specification = ZoneSpecification( + createTimer: (Zone self, ZoneDelegate parent, Zone zone, Duration duration, void Function() f) { + createdTimer = true; + return parent.createTimer(zone, duration, f); + }, + ); await runZoned(() async { await FragmentProgram.compile(spirv: spirvA); }, zoneSpecification: specification); expect(createdTimer, false); }); + + test('sksl "compile" does not create a Timer object', () async { + final ByteBuffer skslA = shaderFile( + path.join('general_shaders', 'sksl'), + 'simple.frag.sksl', + ).readAsBytesSync().buffer; + bool createdTimer = false; + final ZoneSpecification specification = ZoneSpecification( + createTimer: (Zone self, ZoneDelegate parent, Zone zone, Duration duration, void Function() f) { + createdTimer = true; + return parent.createTimer(zone, duration, f); + }, + ); + await runZoned(() async { + await FragmentProgram.compile( + raw: skslA, + uniformFloatCount: 1, + ); + }, zoneSpecification: specification); + + expect(createdTimer, false); + }); } // Expect that all of the spirv shaders in this folder render green. @@ -168,6 +368,24 @@ void _expectShadersRenderGreen(Map shaders) { } } +// Expect that all of the shaders in this folder render green. +// Keeping the outer loop of the test synchronous allows for easy printing +// of the file name within the test case. +void _expectSkSLShadersRenderGreen(Map shaders) { + for (final String key in shaders.keys) { + test('SkSL $key renders green', () async { + final FragmentProgram program = await FragmentProgram.compile( + raw: shaders[key]!, + uniformFloatCount: 1, + ); + final Shader shader = program.shader( + floatUniforms: Float32List.fromList([1]), + ); + _expectShaderRendersGreen(shader); + }); + } +} + void _expectShadersHaveOp(Map shaders, bool glsl) { for (final String key in shaders.keys) { test('$key contains opcode', () { @@ -244,17 +462,17 @@ Future _imageByteDataFromShader({ // $FLUTTER_BUILD_DIRECTORY/gen/flutter/lib/spirv/test/$leafFolderName // This is synchronous so that tests can be inside of a loop with // the proper test name. -Map _loadSpv(String leafFolderName) { +Map _loadShaders(String leafFolderName, String ext) { final Map out = SplayTreeMap(); - final Directory directory = spvDirectory(leafFolderName); + final Directory directory = shaderDirectory(leafFolderName); if (!directory.existsSync()) { return out; } directory .listSync() - .where((FileSystemEntity entry) => path.extension(entry.path) == '.spirv') + .where((FileSystemEntity entry) => path.extension(entry.path) == ext) .forEach((FileSystemEntity entry) { final String key = path.basenameWithoutExtension(entry.path); out[key] = (entry as File).readAsBytesSync().buffer; diff --git a/testing/dart/shader_test_file_utils.dart b/testing/dart/shader_test_file_utils.dart index ebc5b35a40ade..17360abbe767e 100644 --- a/testing/dart/shader_test_file_utils.dart +++ b/testing/dart/shader_test_file_utils.dart @@ -17,7 +17,7 @@ const String _testPath = 'gen/flutter/lib/spirv/test'; /// Gets the [Directory] of .spv files that are generated by `lib/spirv/test`. /// /// `folderName` is a leaf folder within the generated directory. -Directory spvDirectory(String leafFolderName) { +Directory shaderDirectory(String leafFolderName) { return Directory(path.joinAll([ ...path.split(_flutterBuildDirectoryPath()), ...path.split(_testPath), @@ -30,11 +30,11 @@ Directory spvDirectory(String leafFolderName) { /// `folderName` is the leaf folder within the generated directory. /// /// `fileName` is the name of the filer within `folderName`. -File spvFile(String folderName, String fileName) { +File shaderFile(String folderName, String fileName) { return File(path.joinAll([ ...path.split(_flutterBuildDirectoryPath()), ...path.split(_testPath), folderName, fileName, ])); -} \ No newline at end of file +} diff --git a/testing/dart/spirv_exception_test.dart b/testing/dart/spirv_exception_test.dart index 5d96e34220d7f..013024cc05de1 100644 --- a/testing/dart/spirv_exception_test.dart +++ b/testing/dart/spirv_exception_test.dart @@ -33,7 +33,7 @@ void main() { } Stream _exceptionShaders() async* { - final Directory dir = spvDirectory('exception_shaders'); + final Directory dir = shaderDirectory('exception_shaders'); await for (final FileSystemEntity entry in dir.list()) { if (entry is! File) { continue; From 5c0c7699f168648f4e6c62e11f54fd7ab08edc09 Mon Sep 17 00:00:00 2001 From: Zach Anderson Date: Fri, 8 Jul 2022 13:04:24 -0700 Subject: [PATCH 6/8] WIP --- ci/licenses_golden/licenses_flutter | 2 ++ impeller/compiler/spirv_sksl.cc | 16 +++++++--------- lib/spirv/lib/spirv.dart | 1 - lib/spirv/lib/src/constants.dart | 1 - lib/web_ui/lib/painting.dart | 5 ++++- testing/dart/fragment_shader_test.dart | 8 ++------ 6 files changed, 15 insertions(+), 18 deletions(-) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 04e3b9e9fefca..23bb99b4b353c 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -491,6 +491,8 @@ FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/texture.glsl FILE: ../../../flutter/impeller/compiler/shader_lib/impeller/types.glsl FILE: ../../../flutter/impeller/compiler/source_options.cc FILE: ../../../flutter/impeller/compiler/source_options.h +FILE: ../../../flutter/impeller/compiler/spirv_sksl.cc +FILE: ../../../flutter/impeller/compiler/spirv_sksl.h FILE: ../../../flutter/impeller/compiler/switches.cc FILE: ../../../flutter/impeller/compiler/switches.h FILE: ../../../flutter/impeller/compiler/types.cc diff --git a/impeller/compiler/spirv_sksl.cc b/impeller/compiler/spirv_sksl.cc index b35fb28412d4f..63db4285e8d7a 100644 --- a/impeller/compiler/spirv_sksl.cc +++ b/impeller/compiler/spirv_sksl.cc @@ -180,8 +180,8 @@ bool CompilerSkSL::emit_uniform_resources() { bool emitted = false; // Output Uniform Constants (values, samplers, images, etc). - std::vector regular_uniforms; - std::vector shader_uniforms; + std::vector regular_uniforms; + std::vector shader_uniforms; for (auto& id : ir.ids) { if (id.get_type() == TypeVariable) { auto& var = id.get(); @@ -343,8 +343,7 @@ void CompilerSkSL::emit_function_prototype(SPIRFunction& func, statement("void __main()"); } -std::string CompilerSkSL::image_type_glsl(const spirv_cross::SPIRType& type, - uint32_t id) { +std::string CompilerSkSL::image_type_glsl(const SPIRType& type, uint32_t id) { if (type.basetype != SPIRType::SampledImage || type.image.dim != Dim2D) { SPIRV_CROSS_THROW("Only sampler2D uniform image types are supported."); return "???"; @@ -352,10 +351,9 @@ std::string CompilerSkSL::image_type_glsl(const spirv_cross::SPIRType& type, return "shader"; } -std::string CompilerSkSL::builtin_to_glsl(spv::BuiltIn builtin, - spv::StorageClass storage) { - std::string gl_builtin = - spirv_cross::CompilerGLSL::builtin_to_glsl(builtin, storage); +std::string CompilerSkSL::builtin_to_glsl(BuiltIn builtin, + StorageClass storage) { + std::string gl_builtin = CompilerGLSL::builtin_to_glsl(builtin, storage); switch (builtin) { case BuiltInFragCoord: return "sk_FragCoord"; @@ -381,7 +379,7 @@ std::string CompilerSkSL::to_texture_op( } std::string CompilerSkSL::to_function_name( - const spirv_cross::CompilerGLSL::TextureFunctionNameArguments& args) { + const CompilerGLSL::TextureFunctionNameArguments& args) { std::string name = to_expression(args.base.img); return name + ".eval"; } diff --git a/lib/spirv/lib/spirv.dart b/lib/spirv/lib/spirv.dart index 56b0b67921ee6..14cd1d6ffcfb4 100644 --- a/lib/spirv/lib/spirv.dart +++ b/lib/spirv/lib/spirv.dart @@ -6,7 +6,6 @@ library spirv; import 'dart:convert'; -import 'dart:math'; import 'dart:typed_data'; // These parts only contain private members, all public diff --git a/lib/spirv/lib/src/constants.dart b/lib/spirv/lib/src/constants.dart index a5549822005e8..79d1a855779f9 100644 --- a/lib/spirv/lib/src/constants.dart +++ b/lib/spirv/lib/src/constants.dart @@ -79,7 +79,6 @@ const int _opVectorShuffle = 79; const int _opCompositeConstruct = 80; const int _opCompositeExtract = 81; const int _opImageSampleImplicitLod = 87; -const int _opImageQuerySize = 104; const int _opConvertFToS = 110; const int _opConvertSToF = 111; const int _opFNegate = 127; diff --git a/lib/web_ui/lib/painting.dart b/lib/web_ui/lib/painting.dart index 7883b77b7a95d..ad9def4c9508b 100644 --- a/lib/web_ui/lib/painting.dart +++ b/lib/web_ui/lib/painting.dart @@ -836,7 +836,10 @@ class ImageDescriptor { class FragmentProgram { static Future compile({ - required ByteBuffer spirv, + ByteBuffer? spirv, + ByteBuffer? raw, + int? uniformFloatCount, + int? samplerCount, bool debugPrint = false, }) { throw UnsupportedError('FragmentProgram is not supported for the CanvasKit or HTML renderers.'); diff --git a/testing/dart/fragment_shader_test.dart b/testing/dart/fragment_shader_test.dart index b9140dfdebfc6..87d8be1f7fd9f 100644 --- a/testing/dart/fragment_shader_test.dart +++ b/testing/dart/fragment_shader_test.dart @@ -484,7 +484,6 @@ Map _loadShaders(String leafFolderName, String ext) { const int _shaderImageDimension = 4; const Color _greenColor = Color(0xFF00FF00); -const Color _blueColor = Color(0xFF0000FF); // Precision for checking uniform values. const double epsilon = 0.5 / 255.0; @@ -497,8 +496,8 @@ String toHexString(int color) => '#${color.toRadixString(16)}'; // 10x10 image where the left half is blue and the right half is // green. Future _createBlueGreenImage() async { - final int length = 10; - final int bytesPerPixel = 4; + const int length = 10; + const int bytesPerPixel = 4; final Uint8List pixels = Uint8List(length * length * bytesPerPixel); int i = 0; for (int y = 0; y < length; y++) { @@ -523,9 +522,6 @@ Future _createBlueGreenImage() async { return frame.image; } -// A single uniform with value 1. -final Float32List _singleUniform = Float32List.fromList([1]); - final Float64List _identityMatrix = Float64List.fromList([ 1, 0, 0, 0, 0, 1, 0, 0, From ad1d1658c61af28b9cb65d1e87c1d1e3df767755 Mon Sep 17 00:00:00 2001 From: Zach Anderson Date: Sat, 9 Jul 2022 16:31:45 -0700 Subject: [PATCH 7/8] Address comments --- impeller/compiler/compiler.cc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 28e048a7b5056..54b577252211d 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -160,7 +160,6 @@ Compiler::Compiler(const fml::Mapping& source_mapping, shaderc_spirv_version::shaderc_spirv_version_1_0); break; case TargetPlatform::kFlutterSPIRV: - case TargetPlatform::kSkSL: // TODO(zra): Allow optimizations. // With any optimization level above 'zero' enabled, shaderc will emit // ops that are not supported by the Engine's SPIR-V -> SkSL transpiler. // In particular, with 'shaderc_optimization_level_size' enabled, it will @@ -175,6 +174,19 @@ Compiler::Compiler(const fml::Mapping& source_mapping, spirv_options.SetTargetSpirv( shaderc_spirv_version::shaderc_spirv_version_1_0); break; + case TargetPlatform::kSkSL: + // When any optimization level above 'zero' is enabled, the phi merges at + // loop continue blocks are rendered using syntax that is supported in + // GLSL, but not in SkSL. + // https://bugs.chromium.org/p/skia/issues/detail?id=13518. + spirv_options.SetOptimizationLevel( + shaderc_optimization_level::shaderc_optimization_level_zero); + spirv_options.SetTargetEnvironment( + shaderc_target_env::shaderc_target_env_opengl, + shaderc_env_version::shaderc_env_version_opengl_4_5); + spirv_options.SetTargetSpirv( + shaderc_spirv_version::shaderc_spirv_version_1_0); + break; case TargetPlatform::kUnknown: COMPILER_ERROR << "Target platform invalid."; return; From 114bfb5b26b72c1e86fefadf9044c296fc86dd27 Mon Sep 17 00:00:00 2001 From: Zach Anderson Date: Wed, 13 Jul 2022 09:03:00 -0700 Subject: [PATCH 8/8] Remove reference to sk_FragCoord --- impeller/compiler/spirv_sksl.cc | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/impeller/compiler/spirv_sksl.cc b/impeller/compiler/spirv_sksl.cc index 63db4285e8d7a..a16f93476cb65 100644 --- a/impeller/compiler/spirv_sksl.cc +++ b/impeller/compiler/spirv_sksl.cc @@ -62,8 +62,9 @@ std::string CompilerSkSL::compile() { pass_count++; } while (is_forcing_recompilation()); - statement("half4 main(float2 ignored)"); + statement("half4 main(float2 iFragCoord)"); begin_scope(); + statement(" flutter_FragCoord = float4(iFragCoord, 0, 0);"); statement(" __main();"); statement(" return " + output_name_ + ";"); end_scope(); @@ -74,12 +75,7 @@ std::string CompilerSkSL::compile() { void CompilerSkSL::emit_header() { statement("// This SkSL shader is autogenerated by spirv-cross."); statement(""); - - // This builtin is copied from - // //third_party/skia/src/sksl/sksl_rt_shader.sksl. gl_FragCoord is translated - // to sk_FragCoord instead of using the argument to 'main' passed in the - // SkSL universe. - statement("layout(builtin=15) float4 sk_FragCoord;"); + statement("float4 flutter_FragCoord;"); statement(""); } @@ -356,7 +352,7 @@ std::string CompilerSkSL::builtin_to_glsl(BuiltIn builtin, std::string gl_builtin = CompilerGLSL::builtin_to_glsl(builtin, storage); switch (builtin) { case BuiltInFragCoord: - return "sk_FragCoord"; + return "flutter_FragCoord"; default: SPIRV_CROSS_THROW("Builtin '" + gl_builtin + "' is not supported."); break;