diff --git a/impeller/compiler/compiler.cc b/impeller/compiler/compiler.cc index 268ba39df93e3..c2397c521edc0 100644 --- a/impeller/compiler/compiler.cc +++ b/impeller/compiler/compiler.cc @@ -265,12 +265,24 @@ Compiler::Compiler(const fml::Mapping& source_mapping, // here are irrelevant and get in the way of generating reflection code. spirv_options.SetGenerateDebugInfo(); - // Expects GLSL 4.60 (Core Profile). - // https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.60.pdf - spirv_options.SetSourceLanguage( - shaderc_source_language::shaderc_source_language_glsl); - spirv_options.SetForcedVersionProfile(460, - shaderc_profile::shaderc_profile_core); + switch (options_.source_language) { + case SourceLanguage::kGLSL: + // Expects GLSL 4.60 (Core Profile). + // https://www.khronos.org/registry/OpenGL/specs/gl/GLSLangSpec.4.60.pdf + spirv_options.SetSourceLanguage( + shaderc_source_language::shaderc_source_language_glsl); + spirv_options.SetForcedVersionProfile( + 460, shaderc_profile::shaderc_profile_core); + break; + case SourceLanguage::kHLSL: + spirv_options.SetSourceLanguage( + shaderc_source_language::shaderc_source_language_hlsl); + break; + case SourceLanguage::kUnknown: + COMPILER_ERROR << "Source language invalid."; + return; + } + SetLimitations(spirv_options); switch (source_options.target_platform) { @@ -347,7 +359,9 @@ Compiler::Compiler(const fml::Mapping& source_mapping, shaderc::Compiler spv_compiler; if (!spv_compiler.IsValid()) { - COMPILER_ERROR << "Could not initialize the GLSL to SPIRV compiler."; + COMPILER_ERROR << "Could not initialize the " + << SourceLanguageToString(options_.source_language) + << " to SPIRV compiler."; return; } @@ -364,7 +378,8 @@ Compiler::Compiler(const fml::Mapping& source_mapping, )); if (spv_result_->GetCompilationStatus() != shaderc_compilation_status::shaderc_compilation_status_success) { - COMPILER_ERROR << "GLSL to SPIRV failed; " + COMPILER_ERROR << SourceLanguageToString(options_.source_language) + << " to SPIRV failed; " << ShaderCErrorToString(spv_result_->GetCompilationStatus()) << ". " << spv_result_->GetNumErrors() << " error(s) and " << spv_result_->GetNumWarnings() << " warning(s)."; diff --git a/impeller/compiler/compiler_test.cc b/impeller/compiler/compiler_test.cc index a0031fbb5c088..472951782f46f 100644 --- a/impeller/compiler/compiler_test.cc +++ b/impeller/compiler/compiler_test.cc @@ -66,19 +66,21 @@ std::unique_ptr CompilerTest::GetReflectionJson( } bool CompilerTest::CanCompileAndReflect(const char* fixture_name, - SourceType source_type) const { + SourceType source_type, + SourceLanguage source_language) const { auto fixture = flutter::testing::OpenFixtureAsMapping(fixture_name); - if (!fixture->GetMapping()) { + if (!fixture || !fixture->GetMapping()) { VALIDATION_LOG << "Could not find shader in fixtures: " << fixture_name; return false; } SourceOptions source_options(fixture_name, source_type); source_options.target_platform = GetParam(); + source_options.source_language = source_language; source_options.working_directory = std::make_shared( flutter::testing::OpenFixturesDirectory()); source_options.entry_point_name = EntryPointFunctionNameFromSourceName( - fixture_name, SourceTypeFromFileName(fixture_name)); + fixture_name, SourceTypeFromFileName(fixture_name), source_language); Reflector::Options reflector_options; reflector_options.header_file_name = ReflectionHeaderName(fixture_name); diff --git a/impeller/compiler/compiler_test.h b/impeller/compiler/compiler_test.h index 0831a95bf13e3..374c06933ac14 100644 --- a/impeller/compiler/compiler_test.h +++ b/impeller/compiler/compiler_test.h @@ -26,7 +26,8 @@ class CompilerTest : public ::testing::TestWithParam { bool CanCompileAndReflect( const char* fixture_name, - SourceType source_type = SourceType::kUnknown) const; + SourceType source_type = SourceType::kUnknown, + SourceLanguage source_language = SourceLanguage::kGLSL) const; private: fml::UniqueFD intermediates_directory_; diff --git a/impeller/compiler/compiler_unittests.cc b/impeller/compiler/compiler_unittests.cc index c7add5a55c7f1..ed96fffac463b 100644 --- a/impeller/compiler/compiler_unittests.cc +++ b/impeller/compiler/compiler_unittests.cc @@ -28,6 +28,13 @@ TEST(CompilerTest, ShaderKindMatchingIsSuccessful) { TEST_P(CompilerTest, CanCompile) { ASSERT_TRUE(CanCompileAndReflect("sample.vert")); ASSERT_TRUE(CanCompileAndReflect("sample.vert", SourceType::kVertexShader)); + ASSERT_TRUE(CanCompileAndReflect("sample.vert", SourceType::kVertexShader, + SourceLanguage::kGLSL)); +} + +TEST_P(CompilerTest, CanCompileHLSL) { + ASSERT_TRUE(CanCompileAndReflect( + "simple.vert.hlsl", SourceType::kVertexShader, SourceLanguage::kHLSL)); } TEST_P(CompilerTest, CanCompileTessellationControlShader) { diff --git a/impeller/compiler/impellerc_main.cc b/impeller/compiler/impellerc_main.cc index e6a7e9a44ac95..9d4bf89172e52 100644 --- a/impeller/compiler/impellerc_main.cc +++ b/impeller/compiler/impellerc_main.cc @@ -59,6 +59,7 @@ bool Main(const fml::CommandLine& command_line) { SourceOptions options; options.target_platform = switches.target_platform; + options.source_language = switches.source_language; if (switches.input_type == SourceType::kUnknown) { options.type = SourceTypeFromFileName(switches.source_file_name); } else { @@ -69,7 +70,7 @@ bool Main(const fml::CommandLine& command_line) { options.include_dirs = switches.include_directories; options.defines = switches.defines; options.entry_point_name = EntryPointFunctionNameFromSourceName( - switches.source_file_name, options.type); + switches.source_file_name, options.type, options.source_language); options.json_format = switches.json_format; Reflector::Options reflector_options; diff --git a/impeller/compiler/source_options.h b/impeller/compiler/source_options.h index 875b61eb57432..921ee5cb5c494 100644 --- a/impeller/compiler/source_options.h +++ b/impeller/compiler/source_options.h @@ -19,6 +19,7 @@ namespace compiler { struct SourceOptions { SourceType type = SourceType::kUnknown; TargetPlatform target_platform = TargetPlatform::kUnknown; + SourceLanguage source_language = SourceLanguage::kUnknown; std::shared_ptr working_directory; std::vector include_dirs; std::string file_name = "main.glsl"; diff --git a/impeller/compiler/switches.cc b/impeller/compiler/switches.cc index 1e73352b484c9..25caa1c968fe5 100644 --- a/impeller/compiler/switches.cc +++ b/impeller/compiler/switches.cc @@ -4,10 +4,13 @@ #include "impeller/compiler/switches.h" +#include +#include #include #include #include "flutter/fml/file.h" +#include "impeller/compiler/types.h" #include "impeller/compiler/utilities.h" namespace impeller { @@ -44,7 +47,7 @@ void Switches::PrintHelp(std::ostream& stream) { stream << " --" << platform.first; } stream << " ]" << std::endl; - stream << "--input=" << std::endl; + stream << "--input=" << std::endl; stream << "[optional] --input-kind={"; for (const auto& source_type : kKnownSourceTypes) { stream << source_type.first << ", "; @@ -52,6 +55,8 @@ void Switches::PrintHelp(std::ostream& stream) { stream << "}" << std::endl; stream << "--sl=" << std::endl; stream << "--spirv=" << std::endl; + stream << "[optional] --source-language=glsl|hlsl (default: glsl)" + << std::endl; stream << "[optional] --iplr (causes --sl file to be emitted in iplr format)" << std::endl; stream << "[optional] --reflection-json=" << std::endl; @@ -120,6 +125,16 @@ Switches::Switches(const fml::CommandLine& command_line) return; } + auto language = + command_line.GetOptionValueWithDefault("source-language", "glsl"); + std::transform(language.begin(), language.end(), language.begin(), + [](char x) { return std::tolower(x); }); + if (language == "glsl") { + source_language = SourceLanguage::kGLSL; + } else if (language == "hlsl") { + source_language = SourceLanguage::kHLSL; + } + for (const auto& include_dir_path : command_line.GetOptionValues("include")) { if (!include_dir_path.data()) { continue; @@ -161,6 +176,11 @@ bool Switches::AreValid(std::ostream& explain) const { valid = false; } + if (source_language == SourceLanguage::kUnknown) { + explain << "Invalid source language type." << std::endl; + valid = false; + } + if (!working_directory || !working_directory->is_valid()) { explain << "Could not figure out working directory." << std::endl; valid = false; diff --git a/impeller/compiler/switches.h b/impeller/compiler/switches.h index 2cffa5cc922a0..95d7b4d1701d5 100644 --- a/impeller/compiler/switches.h +++ b/impeller/compiler/switches.h @@ -32,6 +32,7 @@ struct Switches { std::string depfile_path; std::vector defines; bool json_format; + SourceLanguage source_language = SourceLanguage::kUnknown; Switches(); diff --git a/impeller/compiler/switches_unittests.cc b/impeller/compiler/switches_unittests.cc index 54ca1e293d4da..3db243ee889dc 100644 --- a/impeller/compiler/switches_unittests.cc +++ b/impeller/compiler/switches_unittests.cc @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include +#include + #include "flutter/fml/command_line.h" #include "flutter/fml/file.h" #include "flutter/testing/testing.h" @@ -12,6 +15,19 @@ namespace impeller { namespace compiler { namespace testing { +Switches MakeSwitchesDesktopGL( + std::initializer_list additional_options = {}) { + std::vector options = {"--opengl-desktop", "--input=input.vert", + "--sl=output.vert", + "--spirv=output.spirv"}; + options.insert(options.end(), additional_options.begin(), + additional_options.end()); + + auto cl = fml::CommandLineFromIteratorsWithArgv0("impellerc", options.begin(), + options.end()); + return Switches(cl); +} + TEST(SwitchesTest, DoesntMangleUnicodeIncludes) { const char* directory_name = "test_shader_include_�"; fml::CreateDirectory(flutter::testing::OpenFixturesDirectory(), @@ -21,16 +37,26 @@ TEST(SwitchesTest, DoesntMangleUnicodeIncludes) { std::string(flutter::testing::GetFixturesPath()) + "/" + directory_name; auto include_option = "--include=" + include_path; - const auto cl = fml::CommandLineFromInitializerList( - {"impellerc", "--opengl-desktop", "--input=input.vert", - "--sl=output.vert", "--spirv=output.spirv", include_option.c_str()}); - Switches switches(cl); + Switches switches = MakeSwitchesDesktopGL({include_option.c_str()}); + ASSERT_TRUE(switches.AreValid(std::cout)); ASSERT_EQ(switches.include_directories.size(), 1u); ASSERT_NE(switches.include_directories[0].dir, nullptr); ASSERT_EQ(switches.include_directories[0].name, include_path); } +TEST(SwitchesTest, SourceLanguageDefaultsToGLSL) { + Switches switches = MakeSwitchesDesktopGL(); + ASSERT_TRUE(switches.AreValid(std::cout)); + ASSERT_EQ(switches.source_language, SourceLanguage::kGLSL); +} + +TEST(SwitchesTest, SourceLanguageCanBeSetToHLSL) { + Switches switches = MakeSwitchesDesktopGL({"--source-language=hLsL"}); + ASSERT_TRUE(switches.AreValid(std::cout)); + ASSERT_EQ(switches.source_language, SourceLanguage::kHLSL); +} + } // namespace testing } // namespace compiler } // namespace impeller diff --git a/impeller/compiler/types.cc b/impeller/compiler/types.cc index cf1dae31a126e..a931b32b50cc8 100644 --- a/impeller/compiler/types.cc +++ b/impeller/compiler/types.cc @@ -74,8 +74,25 @@ std::string TargetPlatformToString(TargetPlatform platform) { FML_UNREACHABLE(); } -std::string EntryPointFunctionNameFromSourceName(const std::string& file_name, - SourceType type) { +std::string SourceLanguageToString(SourceLanguage source_language) { + switch (source_language) { + case SourceLanguage::kUnknown: + return "Unknown"; + case SourceLanguage::kGLSL: + return "GLSL"; + case SourceLanguage::kHLSL: + return "HLSL"; + } +} + +std::string EntryPointFunctionNameFromSourceName( + const std::string& file_name, + SourceType type, + SourceLanguage source_language) { + if (source_language == SourceLanguage::kHLSL) { + return "main"; + } + std::stringstream stream; std::filesystem::path file_path(file_name); stream << Utf8FromPath(file_path.stem()) << "_"; diff --git a/impeller/compiler/types.h b/impeller/compiler/types.h index 754f2020f5f40..56b8a51f1e7f4 100644 --- a/impeller/compiler/types.h +++ b/impeller/compiler/types.h @@ -37,6 +37,12 @@ enum class TargetPlatform { kSkSL, }; +enum class SourceLanguage { + kUnknown, + kGLSL, + kHLSL, +}; + bool TargetPlatformIsMetal(TargetPlatform platform); bool TargetPlatformIsOpenGL(TargetPlatform platform); @@ -47,10 +53,14 @@ std::string SourceTypeToString(SourceType type); std::string TargetPlatformToString(TargetPlatform platform); +std::string SourceLanguageToString(SourceLanguage source_language); + std::string TargetPlatformSLExtension(TargetPlatform platform); -std::string EntryPointFunctionNameFromSourceName(const std::string& file_name, - SourceType type); +std::string EntryPointFunctionNameFromSourceName( + const std::string& file_name, + SourceType type, + SourceLanguage source_language); bool TargetPlatformNeedsSL(TargetPlatform platform); diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index ca6984e0f5b57..930d9bbdfe6d5 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -61,6 +61,7 @@ test_fixtures("file_fixtures") { "sample.tese", "sample.vert", "sample_with_binding.vert", + "simple.vert.hlsl", "sa%m#ple.vert", "struct_def_bug.vert", "table_mountain_nx.png", diff --git a/impeller/fixtures/simple.vert.hlsl b/impeller/fixtures/simple.vert.hlsl new file mode 100644 index 0000000000000..421add3ae2922 --- /dev/null +++ b/impeller/fixtures/simple.vert.hlsl @@ -0,0 +1,18 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +struct VertexInput { + float3 position : POSITION; +}; + +struct VertexOutput { + float4 position : SV_POSITION; +}; + +VertexOutput +main(VertexInput input) { + VertexOutput output; + output.position = float4(input.position, 1.0); + return output; +}