blob: 2c20acf26d59059b066661141f8e0b6d563be7d8 [file] [log] [blame]
# Copyright 2013 The Flutter Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/compiled_action.gni")
import("//flutter/common/config.gni")
import("//flutter/impeller/tools/malioc.gni")
import("//flutter/testing/testing.gni")
declare_args() {
impeller_debug =
flutter_runtime_mode == "debug" || flutter_runtime_mode == "profile"
# Whether the Metal backend is enabled.
impeller_enable_metal = is_mac || is_ios
# Whether the OpenGLES backend is enabled.
impeller_enable_opengles = is_linux || is_win || is_android
# Whether the Vulkan backend is enabled.
impeller_enable_vulkan = is_linux || is_win || is_android
# Whether to use a prebuilt impellerc.
# If this is the empty string, impellerc will be built.
# If it is non-empty, it should be the absolute path to impellerc.
impeller_use_prebuilt_impellerc = ""
# Whether to use a prebuilt scenec.
# If this is the empty string, scenec will be built.
# If it is non-empty, it should be the absolute path to scenec.
impeller_use_prebuilt_scenec = ""
# If enabled, all OpenGL calls will be traced. Because additional trace
# overhead may be substantial, this is not enabled by default.
impeller_trace_all_gl_calls = false
# Call glGetError after each OpenGL call and log failures.
impeller_error_check_all_gl_calls = is_debug
# Enable experimental 3D scene rendering.
impeller_enable_3d = false
}
declare_args() {
# Whether Impeller supports rendering on the platform.
impeller_supports_rendering =
impeller_enable_metal || impeller_enable_opengles ||
impeller_enable_vulkan
impeller_enable_compute = impeller_enable_vulkan || impeller_enable_metal
}
# ------------------------------------------------------------------------------
# @brief Define an Impeller component. Components are different
# Impeller subsystems part of the umbrella framework.
#
# @param[optional] target_type The type of the component. This can be any of
# the target types supported by GN. Defaults to
# source_set. If Impeller is not supported on the
# target platform, this target is a no-op.
#
template("impeller_component") {
if (defined(invoker.testonly) && invoker.testonly && !enable_unittests) {
group(target_name) {
not_needed(invoker, "*")
}
} else {
target_type = "source_set"
if (defined(invoker.target_type)) {
target_type = invoker.target_type
}
target(target_type, target_name) {
forward_variables_from(invoker, "*")
if (!defined(invoker.public_configs)) {
public_configs = []
}
public_configs += [ "//flutter/impeller:impeller_public_config" ]
if (!defined(invoker.cflags)) {
cflags = []
}
cflags += [ "-Wthread-safety-analysis" ]
if (!defined(invoker.cflags_objc)) {
cflags_objc = []
}
if (is_ios || is_mac) {
cflags_objc += flutter_cflags_objc_arc
}
if (!defined(invoker.cflags_objcc)) {
cflags_objcc = []
}
if (is_ios || is_mac) {
cflags_objcc += flutter_cflags_objcc_arc
}
}
}
}
# ------------------------------------------------------------------------------
# @brief Build a Metal Library. The output is a single file. Use
# get_target_outputs to get its location on build.
#
# @param[required] name The name of the Metal library.
#
# @param[required] sources The GLSL (4.60) sources to compiled into the Metal
# library.
#
# @param[required] metal_version The Metal language version to compile for.
#
template("metal_library") {
assert(is_ios || is_mac)
assert(defined(invoker.name), "Metal library name must be specified.")
assert(defined(invoker.sources), "Metal source files must be specified.")
assert(defined(invoker.metal_version),
"Metal language version must be specified.")
metal_library_name = invoker.name
action("$target_name") {
forward_variables_from(invoker,
"*",
[
"inputs",
"outputs",
"script",
"depfile",
"args",
])
inputs = invoker.sources
metal_library_path = "$root_out_dir/shaders/$metal_library_name.metallib"
metal_library_symbols_path =
"$root_out_dir/shaders/$metal_library_name.metallibsym"
outputs = [
metal_library_path,
metal_library_symbols_path,
]
script = "//flutter/impeller/tools/build_metal_library.py"
depfile = "$target_gen_dir/mtl/$metal_library_name.depfile"
args = [
"--output",
rebase_path(metal_library_path, root_out_dir),
"--depfile",
rebase_path(depfile),
"--metal-version=$metal_version",
]
if (is_ios) {
if (use_ios_simulator) {
args += [ "--platform=ios-simulator" ]
} else {
args += [ "--platform=ios" ]
}
} else if (is_mac) {
args += [ "--platform=mac" ]
} else {
assert(false, "Unsupported platform for generating Metal shaders.")
}
foreach(source, invoker.sources) {
args += [
"--source",
rebase_path(source, root_out_dir),
]
}
}
}
template("embed_blob") {
assert(defined(invoker.symbol_name), "The symbol name must be specified.")
assert(defined(invoker.blob), "The blob file to embed must be specified")
assert(defined(invoker.hdr),
"The header file containing the symbol name must be specified.")
assert(defined(invoker.cc),
"The CC file containing the symbol data must be specified.")
assert(defined(invoker.deps), "The target dependencies must be specified")
gen_blob_target_name = "gen_blob_$target_name"
action(gen_blob_target_name) {
inputs = [ invoker.blob ]
outputs = [
invoker.hdr,
invoker.cc,
]
args = [
"--symbol-name",
invoker.symbol_name,
"--output-header",
rebase_path(invoker.hdr),
"--output-source",
rebase_path(invoker.cc),
"--source",
rebase_path(invoker.blob),
]
script = "//flutter/impeller/tools/xxd.py"
deps = invoker.deps
}
embed_config = "embed_$target_name"
config(embed_config) {
include_dirs = [ get_path_info(
get_label_info("//flutter/impeller:impeller", "target_gen_dir"),
"dir") ]
}
source_set(target_name) {
public_configs = [ ":$embed_config" ]
sources = get_target_outputs(":$gen_blob_target_name")
deps = [ ":$gen_blob_target_name" ]
}
}
# Dispatches to the build or prebuilt impellerc depending on the value of
# the impeller_use_prebuilt_impellerc argument. Forwards all variables to
# compiled_action_foreach or action_foreach as appropriate.
template("_impellerc") {
if (impeller_use_prebuilt_impellerc == "") {
compiled_action_foreach(target_name) {
forward_variables_from(invoker, "*")
tool = "//flutter/impeller/compiler:impellerc"
}
} else {
action_foreach(target_name) {
forward_variables_from(invoker, "*", [ "args" ])
script = "//build/gn_run_binary.py"
impellerc_path =
rebase_path(impeller_use_prebuilt_impellerc, root_build_dir)
args = [ impellerc_path ] + invoker.args
}
}
}
template("impellerc") {
assert(defined(invoker.shaders), "Impeller shaders must be specified.")
assert(defined(invoker.shader_target_flag),
"The flag to impellerc for target selection must be specified.")
assert(defined(invoker.sl_file_extension),
"The extension of the SL file must be specified (metal, glsl, etc..).")
sksl = invoker.shader_target_flag == "--sksl"
iplr = false
if (defined(invoker.iplr) && invoker.iplr) {
iplr = invoker.iplr
}
json = false
if (defined(invoker.json) && invoker.json) {
json = invoker.json
}
# Not needed on every path.
not_needed([
"iplr",
"sksl",
])
# Optional: invoker.iplr Causes --sl output to be in iplr format.
# Optional: invoker.defines specifies a list of valueless macro definitions.
# Optional: invoker.intermediates_subdir specifies the subdirectory in which
# to put intermediates.
# Optional: invoker.json Causes output format to be JSON instead of flatbuffer.
_impellerc(target_name) {
sources = invoker.shaders
if (defined(invoker.intermediates_subdir)) {
subdir = invoker.intermediates_subdir
generated_dir = "$target_gen_dir/$subdir"
} else {
generated_dir = "$target_gen_dir"
}
shader_target_flag = invoker.shader_target_flag
spirv_intermediate = "$generated_dir/{{source_file_part}}.spirv"
spirv_intermediate_path = rebase_path(spirv_intermediate, root_build_dir)
depfile_path = "$generated_dir/{{source_file_part}}.d"
depfile_intermediate_path = rebase_path(depfile_path, root_build_dir)
depfile = depfile_path
shader_lib_dir = rebase_path("//flutter/impeller/compiler/shader_lib")
args = [
"--input={{source}}",
"--include={{source_dir}}",
"--include=$shader_lib_dir",
"--depfile=$depfile_intermediate_path",
"$shader_target_flag",
]
if (defined(invoker.gles_language_version)) {
gles_language_version = invoker.gles_language_version
args += [ "--gles-language-version=$gles_language_version" ]
}
if (defined(invoker.metal_version)) {
assert(is_mac || is_ios)
metal_version = invoker.metal_version
args += [ "--metal-version=$metal_version" ]
}
if (defined(invoker.use_half_textures) && invoker.use_half_textures) {
args += [ "--use-half-textures" ]
}
if (json) {
args += [ "--json" ]
}
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",
]
if (iplr) {
args += [ "--iplr" ]
}
outputs = [ sl_intermediate ]
} else {
sl_intermediate =
"$generated_dir/{{source_file_part}}.${invoker.sl_file_extension}"
reflection_json_intermediate = "$generated_dir/{{source_file_part}}.json"
reflection_header_intermediate = "$generated_dir/{{source_file_part}}.h"
reflection_cc_intermediate = "$generated_dir/{{source_file_part}}.cc"
sl_intermediate_path = rebase_path(sl_intermediate, root_build_dir)
reflection_json_path =
rebase_path(reflection_json_intermediate, root_build_dir)
reflection_header_path =
rebase_path(reflection_header_intermediate, root_build_dir)
reflection_cc_path =
rebase_path(reflection_cc_intermediate, root_build_dir)
args += [
"--sl=$sl_intermediate_path",
"--spirv=$spirv_intermediate_path",
"--reflection-json=$reflection_json_path",
"--reflection-header=$reflection_header_path",
"--reflection-cc=$reflection_cc_path",
]
if (iplr) {
args += [ "--iplr" ]
}
outputs = [
sl_intermediate,
reflection_header_intermediate,
reflection_cc_intermediate,
]
}
if (defined(invoker.defines)) {
foreach(def, invoker.defines) {
args += [ "--define=$def" ]
}
}
}
}
template("impellerc_reflect") {
assert(
defined(invoker.impellerc_invocation),
"The target that specifies the ImpellerC invocation to reflect must be defined.")
reflect_config = "reflect_$target_name"
config(reflect_config) {
include_dirs = [ get_path_info(
get_label_info("//flutter/impeller:impeller", "target_gen_dir"),
"dir") ]
}
impellerc_invocation = invoker.impellerc_invocation
source_set(target_name) {
public_configs = [ ":$reflect_config" ]
public = filter_include(get_target_outputs(impellerc_invocation), [ "*.h" ])
sources = filter_include(get_target_outputs(impellerc_invocation),
[
"*.h",
"*.cc",
"*.mm",
])
deps = [
"//flutter/impeller/core",
impellerc_invocation,
]
}
}
template("_impeller_shaders_metal") {
assert(defined(invoker.shaders), "Impeller shaders must be specified.")
assert(defined(invoker.name), "Name of the shader library must be specified.")
metal_version = "1.2"
if (defined(invoker.metal_version)) {
metal_version = invoker.metal_version
}
use_half_textures = false
if (defined(invoker.use_half_textures) && invoker.use_half_textures) {
use_half_textures = invoker.use_half_textures
}
shaders_base_name = string_join("",
[
invoker.name,
"_shaders",
])
impellerc_mtl = "impellerc_$target_name"
impellerc(impellerc_mtl) {
shaders = invoker.shaders
metal_version = metal_version
sl_file_extension = "metal"
use_half_textures = use_half_textures
shader_target_flag = ""
defines = [ "IMPELLER_TARGET_METAL" ]
if (is_ios) {
shader_target_flag = "--metal-ios"
defines += [ "IMPELLER_TARGET_METAL_IOS" ]
} else if (is_mac) {
shader_target_flag = "--metal-desktop"
defines += [ "IMPELLER_TARGET_METAL_DESKTOP" ]
} else {
assert(false, "Metal not supported on this platform.")
}
}
mtl_lib = "genlib_$target_name"
metal_library(mtl_lib) {
name = invoker.name
metal_version = metal_version
sources =
filter_include(get_target_outputs(":$impellerc_mtl"), [ "*.metal" ])
deps = [ ":$impellerc_mtl" ]
}
reflect_mtl = "reflect_$target_name"
impellerc_reflect(reflect_mtl) {
impellerc_invocation = ":$impellerc_mtl"
}
embed_mtl_lib = "embed_$target_name"
embed_blob(embed_mtl_lib) {
metal_library_files = get_target_outputs(":$mtl_lib")
symbol_name = shaders_base_name
blob = metal_library_files[0]
hdr = "$target_gen_dir/mtl/$shaders_base_name.h"
cc = "$target_gen_dir/mtl/$shaders_base_name.cc"
deps = [ ":$mtl_lib" ]
}
group(target_name) {
public_deps = [
":$embed_mtl_lib",
":$reflect_mtl",
]
}
}
template("blobcat_library") {
assert(defined(invoker.shaders),
"The shaders to build the library from must be specified.")
assert(defined(invoker.deps), "Target dependencies must be specified.")
output_file = "$target_gen_dir/$target_name.shaderblob"
compiled_action(target_name) {
tool = "//flutter/impeller/blobcat"
inputs = invoker.shaders
outputs = [ output_file ]
output_path_rebased = rebase_path(output_file, root_build_dir)
args = [ "--output=$output_path_rebased" ]
foreach(shader, invoker.shaders) {
shader_path = rebase_path(shader, root_build_dir)
args += [ "--input=$shader_path" ]
}
deps = invoker.deps
}
}
template("_impeller_shaders_gles") {
assert(defined(invoker.shaders), "Impeller shaders must be specified.")
assert(defined(invoker.name), "Name of the shader library must be specified.")
assert(defined(invoker.analyze), "Whether to analyze must be specified.")
shaders_base_name = string_join("",
[
invoker.name,
"_shaders_gles",
])
impellerc_gles = "impellerc_$target_name"
impellerc(impellerc_gles) {
shaders = invoker.shaders
sl_file_extension = "gles"
if (defined(invoker.gles_language_version)) {
gles_language_version = invoker.gles_language_version
}
# Metal reflectors generate a superset of information.
if (impeller_enable_metal || impeller_enable_vulkan) {
intermediates_subdir = "gles"
}
if (is_mac) {
shader_target_flag = "--opengl-desktop"
} else {
shader_target_flag = "--opengl-es"
}
defines = [ "IMPELLER_TARGET_OPENGLES" ]
}
gles_shaders =
filter_include(get_target_outputs(":$impellerc_gles"), [ "*.gles" ])
if (invoker.analyze) {
analyze_lib = "analyze_$target_name"
malioc_analyze_shaders(analyze_lib) {
shaders = gles_shaders
if (defined(invoker.gles_language_version)) {
gles_language_version = invoker.gles_language_version
}
deps = [ ":$impellerc_gles" ]
}
}
gles_lib = "genlib_$target_name"
blobcat_library(gles_lib) {
shaders = gles_shaders
deps = [ ":$impellerc_gles" ]
}
reflect_gles = "reflect_$target_name"
impellerc_reflect(reflect_gles) {
impellerc_invocation = ":$impellerc_gles"
}
embed_gles_lib = "embed_$target_name"
embed_blob(embed_gles_lib) {
gles_library_files = get_target_outputs(":$gles_lib")
symbol_name = shaders_base_name
blob = gles_library_files[0]
hdr = "$target_gen_dir/gles/$shaders_base_name.h"
cc = "$target_gen_dir/gles/$shaders_base_name.cc"
deps = [ ":$gles_lib" ]
}
group(target_name) {
public_deps = [ ":$embed_gles_lib" ]
if (invoker.analyze) {
public_deps += [ ":$analyze_lib" ]
}
if (!impeller_enable_metal && !impeller_enable_vulkan) {
public_deps += [ ":$reflect_gles" ]
}
}
}
template("_impeller_shaders_vk") {
assert(defined(invoker.shaders), "Impeller shaders must be specified.")
assert(defined(invoker.name), "Name of the shader library must be specified.")
assert(defined(invoker.analyze), "Whether to analyze must be specified.")
shaders_base_name = string_join("",
[
invoker.name,
"_shaders_vk",
])
impellerc_vk = "impellerc_$target_name"
impellerc(impellerc_vk) {
shaders = invoker.shaders
sl_file_extension = "vkspv"
# Metal reflectors generate a superset of information.
if (impeller_enable_metal) {
if (defined(invoker.metal_version)) {
metal_version = invoker.metal_version
}
intermediates_subdir = "vk"
}
shader_target_flag = "--vulkan"
defines = [ "IMPELLER_TARGET_VULKAN" ]
}
vk_shaders =
filter_include(get_target_outputs(":$impellerc_vk"), [ "*.vkspv" ])
if (invoker.analyze) {
analyze_lib = "analyze_$target_name"
malioc_analyze_shaders(analyze_lib) {
shaders = vk_shaders
if (defined(invoker.vulkan_language_version)) {
vulkan_language_version = invoker.vulkan_language_version
}
deps = [ ":$impellerc_vk" ]
}
}
vk_lib = "genlib_$target_name"
blobcat_library(vk_lib) {
shaders = vk_shaders
deps = [ ":$impellerc_vk" ]
}
reflect_vk = "reflect_$target_name"
impellerc_reflect(reflect_vk) {
impellerc_invocation = ":$impellerc_vk"
}
embed_vk_lib = "embed_$target_name"
embed_blob(embed_vk_lib) {
vk_library_files = get_target_outputs(":$vk_lib")
symbol_name = shaders_base_name
blob = vk_library_files[0]
hdr = "$target_gen_dir/vk/$shaders_base_name.h"
cc = "$target_gen_dir/vk/$shaders_base_name.cc"
deps = [ ":$vk_lib" ]
}
group(target_name) {
public_deps = [ ":$embed_vk_lib" ]
if (invoker.analyze) {
public_deps += [ ":$analyze_lib" ]
}
if (!impeller_enable_metal) {
public_deps += [ ":$reflect_vk" ]
}
}
}
# ------------------------------------------------------------------------------
# @brief A target for precompiled shaders and the associated
# generated reflection library.
#
# @param[required] name
#
# The base name for the reflected C++ library.
#
# @param[required] shaders
#
# A list of GLSL shader files.
#
# @param[optional] analyze
#
# Whether to analyze shaders with malioc. Defaults to false. Shaders will
# only be analyzed if the GN argument "impeller_malioc_path" is defined.
#
# @param[optional] enable_opengles
#
# Whether to compile the shaders for the OpenGL ES backend. Defaults to the
# value of the GN argument "impeller_enable_opengles".
#
# @param[optional] gles_language_version
#
# The GLES version required by the shaders.
#
# @param[optional] metal_version
#
# The version of the Metal API required by the shaders.
#
# @param[optional] vulkan_language_version
#
# The SPIR-V version required by the shaders.
#
# @param[options] use_half_textures
#
# Whether the metal shader is using half-precision textures and requires
# openGL semantics when compilig SPIR-V.
#
template("impeller_shaders") {
if (defined(invoker.metal_version)) {
metal_version = invoker.metal_version
}
use_half_textures = false
if (defined(invoker.use_half_textures) && invoker.use_half_textures) {
use_half_textures = true
}
not_needed([
"metal_version",
"use_half_textures",
])
enable_opengles = impeller_enable_opengles
if (defined(invoker.enable_opengles)) {
enable_opengles = invoker.enable_opengles
}
if (impeller_enable_metal) {
if (!defined(metal_version)) {
metal_version = "1.2"
}
not_needed(invoker, [ "analyze" ])
mtl_shaders = "mtl_$target_name"
_impeller_shaders_metal(mtl_shaders) {
name = invoker.name
shaders = invoker.shaders
metal_version = metal_version
use_half_textures = use_half_textures
}
}
if (enable_opengles) {
analyze = true
if (defined(invoker.analyze) && !invoker.analyze) {
analyze = false
}
gles_shaders = "gles_$target_name"
_impeller_shaders_gles(gles_shaders) {
name = invoker.name
if (defined(invoker.gles_language_version)) {
gles_language_version = invoker.gles_language_version
}
if (defined(invoker.gles_exclusions)) {
shaders = invoker.shaders - invoker.gles_exclusions
} else {
shaders = invoker.shaders
}
analyze = analyze
}
}
if (impeller_enable_vulkan) {
analyze = true
if (defined(invoker.analyze) && !invoker.analyze) {
analyze = false
}
vk_shaders = "vk_$target_name"
_impeller_shaders_vk(vk_shaders) {
name = invoker.name
if (defined(invoker.vulkan_language_version)) {
vulkan_language_version = invoker.vulkan_language_version
}
shaders = invoker.shaders
analyze = analyze
}
}
if (!impeller_supports_rendering) {
not_needed(invoker, "*")
}
group(target_name) {
public_deps = []
if (impeller_enable_metal) {
public_deps += [ ":$mtl_shaders" ]
}
if (enable_opengles) {
public_deps += [ ":$gles_shaders" ]
}
if (impeller_enable_vulkan) {
public_deps += [ ":$vk_shaders" ]
}
}
}
# Dispatches to the build or prebuilt scenec depending on the value of
# the impeller_use_prebuilt_scenec argument. Forwards all variables to
# compiled_action_foreach or action_foreach as appropriate.
template("_scenec") {
if (impeller_use_prebuilt_scenec == "") {
compiled_action_foreach(target_name) {
forward_variables_from(invoker, "*")
tool = "//flutter/impeller/scene/importer:scenec"
}
} else {
action_foreach(target_name) {
forward_variables_from(invoker, "*", [ "args" ])
script = "//build/gn_run_binary.py"
scenec_path = rebase_path(impeller_use_prebuilt_scenec, root_build_dir)
args = [ scenec_path ] + invoker.args
}
}
}
template("scenec") {
assert(defined(invoker.geometry), "Geometry input files must be specified.")
assert(defined(invoker.type),
"The type of geometry to be parsed (gltf, etc..).")
_scenec(target_name) {
sources = invoker.geometry
generated_dir = "$target_gen_dir"
input_type = invoker.type
args = [
"--input={{source}}",
"--input-type=$input_type",
]
output = "$generated_dir/{{source_file_part}}.ipscene"
output_path = rebase_path(output, root_build_dir)
args += [ "--output=$output_path" ]
outputs = [ output ]
}
}