Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[Impeller] order metal samplers according to declared order and not usage order #38115

Merged
merged 10 commits into from
Dec 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions impeller/compiler/compiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,58 @@ 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);

// Set metal resource mappings to be consistent with location based mapping
// used on other backends when creating fragment shaders. This doesn't seem
// to work with the generated bindings for compute shaders, nor for certain
// shaders in the flutter/engine tree.
if (source_options.remap_samplers) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this just be the default?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are still some compilation errors when using this on the flutter/engine shaders. I need to spend some more time tracking down why that is

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM. I'm just catching up on the issue and the conversation in chat. Will take a closer look in the morning.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this works if I also set enable_decoration_binding to true. that also fixes the issue with floats being out of order, but I need to verify that our bindings are still correct.

std::vector<uint32_t> sampler_offsets;
std::vector<uint32_t> float_offsets;
ir.for_each_typed_id<spirv_cross::SPIRVariable>(
[&](uint32_t, const spirv_cross::SPIRVariable& var) {
if (var.storage != spv::StorageClassUniformConstant) {
return;
}
const auto spir_type = sl_compiler->get_type(var.basetype);
auto location = sl_compiler->get_decoration(
var.self, spv::Decoration::DecorationLocation);
if (spir_type.basetype ==
spirv_cross::SPIRType::BaseType::SampledImage) {
sampler_offsets.push_back(location);
} else if (spir_type.basetype ==
spirv_cross::SPIRType::BaseType::Float) {
float_offsets.push_back(location);
}
});
if (sampler_offsets.size() > 0) {
auto start_offset =
*std::min_element(sampler_offsets.begin(), sampler_offsets.end());
for (auto offset : sampler_offsets) {
sl_compiler->add_msl_resource_binding({
.stage = spv::ExecutionModel::ExecutionModelFragment,
.basetype = spirv_cross::SPIRType::BaseType::SampledImage,
.binding = offset,
.count = 1u,
.msl_texture = offset - start_offset,
});
}
}
if (float_offsets.size() > 0) {
auto start_offset =
*std::min_element(float_offsets.begin(), float_offsets.end());
for (auto offset : float_offsets) {
sl_compiler->add_msl_resource_binding({
.stage = spv::ExecutionModel::ExecutionModelFragment,
.basetype = spirv_cross::SPIRType::BaseType::Float,
.binding = offset,
.count = 1u,
.msl_buffer = offset - start_offset,
});
}
}
}

return CompilerBackend(sl_compiler);
}

Expand Down
1 change: 1 addition & 0 deletions impeller/compiler/impellerc_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ bool Main(const fml::CommandLine& command_line) {
switches.source_file_name, options.type, options.source_language,
switches.entry_point);
options.json_format = switches.json_format;
options.remap_samplers = switches.remap_samplers;
options.gles_language_version = switches.gles_language_version;

Reflector::Options reflector_options;
Expand Down
1 change: 1 addition & 0 deletions impeller/compiler/source_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ struct SourceOptions {
uint32_t gles_language_version = 100;
std::vector<std::string> defines;
bool json_format = false;
bool remap_samplers = false;

SourceOptions();

Expand Down
4 changes: 4 additions & 0 deletions impeller/compiler/switches.cc
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ void Switches::PrintHelp(std::ostream& stream) {
stream << "[optional] --depfile=<depfile_path>" << std::endl;
stream << "[optional] --gles-language-verision=<number>" << std::endl;
stream << "[optional] --json" << std::endl;
stream << "[optional] --remap-samplers (force metal sampler index to match "
"declared order)"
<< std::endl;
}

Switches::Switches() = default;
Expand Down Expand Up @@ -125,6 +128,7 @@ Switches::Switches(const fml::CommandLine& command_line)
command_line.GetOptionValueWithDefault("reflection-cc", "")),
depfile_path(command_line.GetOptionValueWithDefault("depfile", "")),
json_format(command_line.HasOption("json")),
remap_samplers(command_line.HasOption("remap-samplers")),
gles_language_version(
stoi(command_line.GetOptionValueWithDefault("gles-language-version",
"0"))),
Expand Down
1 change: 1 addition & 0 deletions impeller/compiler/switches.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ struct Switches {
std::string depfile_path;
std::vector<std::string> defines;
bool json_format;
bool remap_samplers;
SourceLanguage source_language = SourceLanguage::kUnknown;
uint32_t gles_language_version;
std::string entry_point;
Expand Down
15 changes: 15 additions & 0 deletions impeller/fixtures/ordering/shader_with_matrix.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.

// Declare matrix in different order than usage.
uniform mat4 matrix;
uniform float floatB;

out vec4 frag_color;

void main() {
vec4 sample_1 = vec4(floatB);
vec4 sample_2 = sample_1 * matrix;
frag_color = sample_1 + sample_2;
}
15 changes: 15 additions & 0 deletions impeller/fixtures/ordering/shader_with_ordered_floats.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.

// Declare floats in different order than usage.
uniform float floatA;
uniform float floatB;

out vec4 frag_color;

void main() {
vec4 sample_1 = vec4(floatB);
vec4 sample_2 = vec4(floatA);
frag_color = sample_1 + sample_2;
}
15 changes: 15 additions & 0 deletions impeller/fixtures/ordering/shader_with_samplers.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.

// Declare samplers in different order than usage.
uniform sampler2D textureA;
uniform sampler2D textureB;

out vec4 frag_color;

void main() {
vec4 sample_1 = texture(textureB, vec2(1.0));
vec4 sample_2 = texture(textureA, vec2(1.0));
frag_color = sample_1 + sample_2;
}
9 changes: 9 additions & 0 deletions impeller/tools/impeller.gni
Original file line number Diff line number Diff line change
Expand Up @@ -241,9 +241,13 @@ template("impellerc") {
iplr = invoker.iplr
}
json = false
remap_samplers = false
if (defined(invoker.json) && invoker.json) {
json = invoker.json
}
if (defined(invoker.remap_samplers) && invoker.remap_samplers) {
remap_samplers = invoker.remap_samplers
}

# Not needed on every path.
not_needed([
Expand All @@ -256,6 +260,8 @@ template("impellerc") {
# Optional: invoker.intermediates_subdir specifies the subdirectory in which
# to put intermediates.
# Optional: invoker.json Causes output format to be JSON instead of flatbuffer.
# Optional: invoker.remap_samplers Output metal samplers according to
# declaration order instead of usage order.

_impellerc(target_name) {
sources = invoker.shaders
Expand Down Expand Up @@ -292,6 +298,9 @@ template("impellerc") {
if (json) {
args += [ "--json" ]
}
if (remap_samplers) {
args += [ "--remap-samplers" ]
}

if (sksl) {
sl_intermediate =
Expand Down
14 changes: 14 additions & 0 deletions lib/ui/fixtures/shaders/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,24 @@ if (enable_unittests) {
json = true
}

impellerc("sampler_order_fixture") {
shaders = [
"//flutter/impeller/fixtures/ordering/shader_with_samplers.frag",
"//flutter/impeller/fixtures/ordering/shader_with_ordered_floats.frag",
"//flutter/impeller/fixtures/ordering/shader_with_matrix.frag",
]
shader_target_flag = "--runtime-stage-metal"
intermediates_subdir = "iplr-remap"
sl_file_extension = "iplr"
iplr = true
remap_samplers = true
}

test_fixtures("fixtures") {
deps = [
":ink_sparkle",
":ink_sparkle_web",
":sampler_order_fixture",
]
fixtures = get_target_outputs(":ink_sparkle")
dest = "$root_gen_dir/flutter/lib/ui"
Expand Down
46 changes: 46 additions & 0 deletions testing/dart/fragment_shader_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,47 @@ void main() async {
shader.dispose();
});

// This test can't rely on actual pixels rendered since it needs to run on a
// metal shader on iOS. instead parse the source code.
test('impellerc orders samplers in metal shader according to declaration and not usage', () async {
if (!Platform.isMacOS) {
return;
}
final Directory directory = shaderDirectory('iplr-remap');
final String data = readAsStringLossy(File(path.join(directory.path, 'shader_with_samplers.frag.iplr')));

const String expected = 'texture2d<float> textureA [[texture(0)]],'
' texture2d<float> textureB [[texture(1)]]';

expect(data, contains(expected));
});

test('impellerc orders floats in metal shader according to declaration and not usage', () async {
if (!Platform.isMacOS) {
return;
}
final Directory directory = shaderDirectory('iplr-remap');
final String data = readAsStringLossy(File(path.join(directory.path, 'shader_with_ordered_floats.frag.iplr')));

const String expected = 'constant float& floatA [[buffer(0)]], '
'constant float& floatB [[buffer(1)]]';

expect(data, contains(expected));
});

test('impellerc orders floats/matrix in metal shader according to declaration and not usage', () async {
if (!Platform.isMacOS) {
return;
}
final Directory directory = shaderDirectory('iplr-remap');
final String data = readAsStringLossy(File(path.join(directory.path, 'shader_with_matrix.frag.iplr')));

const String expected = 'constant float4x4& matrix [[buffer(0)]], '
'constant float& floatB [[buffer(1)]]';

expect(data, contains(expected));
});

// Test all supported GLSL ops. See lib/spirv/lib/src/constants.dart
final Map<String, FragmentProgram> iplrSupportedGLSLOpShaders = await _loadShaderAssets(
path.join('supported_glsl_op_shaders', 'iplr'),
Expand Down Expand Up @@ -484,3 +525,8 @@ Image _createBlueGreenImageSync() {
picture.dispose();
}
}

// Ignore invalid utf8 since file is not actually text.
String readAsStringLossy(File file) {
return convert.utf8.decode(file.readAsBytesSync(), allowMalformed: true);
}