forked from firka/flutter
[Impeller] add supprt for JSON output mode (flutter/engine#37123)
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ struct Switches {
|
||||
std::string reflection_cc_name;
|
||||
std::string depfile_path;
|
||||
std::vector<std::string> defines;
|
||||
bool json_format;
|
||||
|
||||
Switches();
|
||||
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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},
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user