[Impeller] add supprt for JSON output mode (flutter/engine#37123)

This commit is contained in:
Jonah Williams
2022-10-29 09:38:16 -07:00
committed by GitHub
parent 77dd1a5630
commit 4a72fe9fae
10 changed files with 226 additions and 3 deletions

View File

@@ -70,6 +70,7 @@ bool Main(const fml::CommandLine& command_line) {
options.defines = switches.defines;
options.entry_point_name = EntryPointFunctionNameFromSourceName(
switches.source_file_name, options.type);
options.json_format = switches.json_format;
Reflector::Options reflector_options;
reflector_options.target_platform = switches.target_platform;
@@ -136,7 +137,9 @@ bool Main(const fml::CommandLine& command_line) {
if (sksl_mapping) {
stage_data->SetSkSLData(sksl_mapping);
}
auto stage_data_mapping = stage_data->CreateMapping();
auto stage_data_mapping = options.json_format
? stage_data->CreateJsonMapping()
: stage_data->CreateMapping();
if (!stage_data_mapping) {
std::cerr << "Runtime stage data could not be created." << std::endl;
return false;

View File

@@ -7,6 +7,8 @@
#include <array>
#include <optional>
#include "inja/inja.hpp"
#include "impeller/base/validation.h"
#include "impeller/runtime_stage/runtime_stage_flatbuffers.h"
@@ -52,6 +54,24 @@ static std::optional<fb::Stage> ToStage(spv::ExecutionModel stage) {
FML_UNREACHABLE();
}
static std::optional<uint32_t> ToJsonStage(spv::ExecutionModel stage) {
switch (stage) {
case spv::ExecutionModel::ExecutionModelVertex:
return 0; // fb::Stage::kVertex;
case spv::ExecutionModel::ExecutionModelFragment:
return 1; // fb::Stage::kFragment;
case spv::ExecutionModel::ExecutionModelGLCompute:
return 2; // fb::Stage::kCompute;
case spv::ExecutionModel::ExecutionModelTessellationControl:
return 3; // fb::Stage::kTessellationControl;
case spv::ExecutionModel::ExecutionModelTessellationEvaluation:
return 4; // fb::Stage::kTessellationEvaluation;
default:
return std::nullopt;
}
FML_UNREACHABLE();
}
static std::optional<fb::TargetPlatform> ToTargetPlatform(
TargetPlatform platform) {
switch (platform) {
@@ -72,6 +92,25 @@ static std::optional<fb::TargetPlatform> ToTargetPlatform(
FML_UNREACHABLE();
}
static std::optional<uint32_t> ToJsonTargetPlatform(TargetPlatform platform) {
switch (platform) {
case TargetPlatform::kUnknown:
case TargetPlatform::kMetalDesktop:
case TargetPlatform::kMetalIOS:
case TargetPlatform::kOpenGLES:
case TargetPlatform::kOpenGLDesktop:
case TargetPlatform::kVulkan:
return std::nullopt;
case TargetPlatform::kSkSL:
return 0; // fb::TargetPlatform::kSkSL;
case TargetPlatform::kRuntimeStageMetal:
return 1; // fb::TargetPlatform::kMetal;
case TargetPlatform::kRuntimeStageGLES:
return 2; // fb::TargetPlatform::kOpenGLES;
}
FML_UNREACHABLE();
}
static std::optional<fb::UniformDataType> ToType(
spirv_cross::SPIRType::BaseType type) {
switch (type) {
@@ -117,6 +156,138 @@ static std::optional<fb::UniformDataType> ToType(
FML_UNREACHABLE();
}
static std::optional<uint32_t> ToJsonType(
spirv_cross::SPIRType::BaseType type) {
switch (type) {
case spirv_cross::SPIRType::Boolean:
return 0; // fb::UniformDataType::kBoolean;
case spirv_cross::SPIRType::SByte:
return 1; // fb::UniformDataType::kSignedByte;
case spirv_cross::SPIRType::UByte:
return 2; // fb::UniformDataType::kUnsignedByte;
case spirv_cross::SPIRType::Short:
return 3; // fb::UniformDataType::kSignedShort;
case spirv_cross::SPIRType::UShort:
return 4; // fb::UniformDataType::kUnsignedShort;
case spirv_cross::SPIRType::Int:
return 5; // fb::UniformDataType::kSignedInt;
case spirv_cross::SPIRType::UInt:
return 6; // fb::UniformDataType::kUnsignedInt;
case spirv_cross::SPIRType::Int64:
return 7; // fb::UniformDataType::kSignedInt64;
case spirv_cross::SPIRType::UInt64:
return 8; // fb::UniformDataType::kUnsignedInt64;
case spirv_cross::SPIRType::Half:
return 9; // b::UniformDataType::kHalfFloat;
case spirv_cross::SPIRType::Float:
return 10; // fb::UniformDataType::kFloat;
case spirv_cross::SPIRType::Double:
return 11; // fb::UniformDataType::kDouble;
case spirv_cross::SPIRType::SampledImage:
return 12; // fb::UniformDataType::kSampledImage;
case spirv_cross::SPIRType::AccelerationStructure:
case spirv_cross::SPIRType::AtomicCounter:
case spirv_cross::SPIRType::Char:
case spirv_cross::SPIRType::ControlPointArray:
case spirv_cross::SPIRType::Image:
case spirv_cross::SPIRType::Interpolant:
case spirv_cross::SPIRType::RayQuery:
case spirv_cross::SPIRType::Sampler:
case spirv_cross::SPIRType::Struct:
case spirv_cross::SPIRType::Unknown:
case spirv_cross::SPIRType::Void:
return std::nullopt;
}
FML_UNREACHABLE();
}
static const char* kStageKey = "stage";
static const char* kTargetPlatformKey = "target_platform";
static const char* kEntrypointKey = "entrypoint";
static const char* kUniformsKey = "uniforms";
static const char* kShaderKey = "sksl";
static const char* kUniformNameKey = "name";
static const char* kUniformLocationKey = "location";
static const char* kUniformTypeKey = "type";
static const char* kUniformRowsKey = "rows";
static const char* kUniformColumnsKey = "columns";
static const char* kUniformBitWidthKey = "bit_width";
static const char* kUniformArrayElementsKey = "array_elements";
std::shared_ptr<fml::Mapping> RuntimeStageData::CreateJsonMapping() const {
// Runtime Stage Data JSON format
// {
// "stage": 0,
// "target_platform": "",
// "entrypoint": "",
// "shader": "",
// "sksl": "",
// "uniforms": [
// {
// "name": "..",
// "location": 0,
// "type": 0,
// "rows": 0,
// "columns": 0,
// "bit_width": 0,
// "array_elements": 0,
// }
// ]
// },
nlohmann::json root;
const auto stage = ToJsonStage(stage_);
if (!stage.has_value()) {
VALIDATION_LOG << "Invalid runtime stage.";
return nullptr;
}
root[kStageKey] = stage.value();
const auto target_platform = ToJsonTargetPlatform(target_platform_);
if (!target_platform.has_value()) {
VALIDATION_LOG << "Invalid target platform for runtime stage.";
return nullptr;
}
root[kTargetPlatformKey] = target_platform.value();
if (shader_->GetSize() > 0u) {
std::string shader(reinterpret_cast<const char*>(shader_->GetMapping()),
shader_->GetSize());
root[kShaderKey] = shader.c_str();
}
auto& uniforms = root[kUniformsKey] = nlohmann::json::array_t{};
for (const auto& uniform : uniforms_) {
nlohmann::json uniform_object;
uniform_object[kUniformNameKey] = uniform.name.c_str();
uniform_object[kUniformLocationKey] = uniform.location;
uniform_object[kUniformRowsKey] = uniform.rows;
uniform_object[kUniformColumnsKey] = uniform.columns;
auto uniform_type = ToJsonType(uniform.type);
if (!uniform_type.has_value()) {
VALIDATION_LOG << "Invalid uniform type for runtime stage.";
return nullptr;
}
uniform_object[kUniformTypeKey] = uniform_type.value();
uniform_object[kUniformBitWidthKey] = uniform.bit_width;
if (uniform.array_elements.has_value()) {
uniform_object[kUniformArrayElementsKey] = uniform.array_elements.value();
} else {
uniform_object[kUniformArrayElementsKey] = 0;
}
uniforms.push_back(uniform_object);
}
auto json_string = std::make_shared<std::string>(root.dump(2u));
return std::make_shared<fml::NonOwnedMapping>(
reinterpret_cast<const uint8_t*>(json_string->data()),
json_string->size(), [json_string](auto, auto) {});
}
std::shared_ptr<fml::Mapping> RuntimeStageData::CreateMapping() const {
// The high level object API is used here for writing to the buffer. This is
// just a convenience.

View File

@@ -41,6 +41,8 @@ class RuntimeStageData {
std::shared_ptr<fml::Mapping> CreateMapping() const;
std::shared_ptr<fml::Mapping> CreateJsonMapping() const;
private:
const std::string entrypoint_;
const spv::ExecutionModel stage_;

View File

@@ -24,6 +24,7 @@ struct SourceOptions {
std::string file_name = "main.glsl";
std::string entry_point_name = "main";
std::vector<std::string> defines;
bool json_format = false;
SourceOptions();

View File

@@ -60,6 +60,7 @@ void Switches::PrintHelp(std::ostream& stream) {
stream << "[optional,multiple] --include=<include_directory>" << std::endl;
stream << "[optional,multiple] --define=<define>" << std::endl;
stream << "[optional] --depfile=<depfile_path>" << std::endl;
stream << "[optional] --json" << std::endl;
}
Switches::Switches() = default;
@@ -112,7 +113,8 @@ Switches::Switches(const fml::CommandLine& command_line)
command_line.GetOptionValueWithDefault("reflection-header", "")),
reflection_cc_name(
command_line.GetOptionValueWithDefault("reflection-cc", "")),
depfile_path(command_line.GetOptionValueWithDefault("depfile", "")) {
depfile_path(command_line.GetOptionValueWithDefault("depfile", "")),
json_format(command_line.HasOption("json")) {
if (!working_directory || !working_directory->is_valid()) {
return;
}

View File

@@ -31,6 +31,7 @@ struct Switches {
std::string reflection_cc_name;
std::string depfile_path;
std::vector<std::string> defines;
bool json_format;
Switches();

View File

@@ -236,6 +236,10 @@ template("impellerc") {
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([
@@ -247,6 +251,7 @@ template("impellerc") {
# 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
@@ -275,6 +280,10 @@ template("impellerc") {
"$shader_target_flag",
]
if (json) {
args += [ "--json" ]
}
if (sksl) {
sl_intermediate =
"$generated_dir/{{source_file_part}}.${invoker.sl_file_extension}"

View File

@@ -24,8 +24,20 @@ if (enable_unittests) {
iplr = true
}
impellerc("ink_sparkle_web") {
shaders = [ "//flutter/impeller/fixtures/ink_sparkle.frag" ]
shader_target_flag = "--sksl"
intermediates_subdir = "iplr-json"
sl_file_extension = "iplr"
iplr = true
json = true
}
test_fixtures("fixtures") {
deps = [ ":ink_sparkle" ]
deps = [
":ink_sparkle",
":ink_sparkle_web",
]
fixtures = get_target_outputs(":ink_sparkle")
dest = "$root_gen_dir/flutter/lib/ui"
}

View File

@@ -4,6 +4,7 @@
import 'dart:async';
import 'dart:collection';
import 'dart:convert' as convert;
import 'dart:io';
import 'dart:typed_data';
import 'dart:ui';
@@ -20,6 +21,26 @@ void main() async {
return true;
}());
test('impellerc produces reasonable JSON encoded IPLR files', () async {
final String path = Platform.environment['FLUTTER_FRAGMENT_SHADER_TEST_PATH']!;
final Object? rawData = convert.json.decode(
File('$path/gen/flutter/lib/ui/fixtures/shaders/iplr-json/ink_sparkle.frag.iplr').readAsStringSync());
expect(rawData is Map<String, Object?>, true);
final Map<String, Object?> data = rawData! as Map<String, Object?>;
expect(data['sksl'] is String, true);
expect(data['uniforms'] is List<Object?>, true);
final Object? rawUniformData = (data['uniforms']! as List<Object?>)[0];
expect(rawUniformData is Map<String, Object?>, true);
final Map<String, Object?> uniformData = rawUniformData! as Map<String, Object?>;
expect(uniformData['location'] is int, true);
});
test('FragmentShader setSampler throws with out-of-bounds index', () async {
final FragmentProgram program = await FragmentProgram.fromAsset(
'blue_green_sampler.frag.iplr',

View File

@@ -536,6 +536,7 @@ def GatherDartTest(
command_args,
forbidden_output=forbidden_output,
expect_failure=expect_failure,
extra_env={'FLUTTER_FRAGMENT_SHADER_TEST_PATH': build_dir},
)