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

Commit 3140ad9

Browse files
[Impeller] order metal samplers according to declared order and not usage order (#38115)
* [Impeller] order metal samplers according to declared order and not use order * ++ * always enabl remapping * Revert "always enabl remapping" This reverts commit 2fffb05. * ++ * add test * ++ * ++ * only run on mac
1 parent 6e91204 commit 3140ad9

File tree

11 files changed

+173
-0
lines changed

11 files changed

+173
-0
lines changed

impeller/compiler/compiler.cc

+52
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,58 @@ static CompilerBackend CreateMSLCompiler(const spirv_cross::ParsedIR& ir,
3535
sl_options.msl_version =
3636
spirv_cross::CompilerMSL::Options::make_msl_version(1, 2);
3737
sl_compiler->set_msl_options(sl_options);
38+
39+
// Set metal resource mappings to be consistent with location based mapping
40+
// used on other backends when creating fragment shaders. This doesn't seem
41+
// to work with the generated bindings for compute shaders, nor for certain
42+
// shaders in the flutter/engine tree.
43+
if (source_options.remap_samplers) {
44+
std::vector<uint32_t> sampler_offsets;
45+
std::vector<uint32_t> float_offsets;
46+
ir.for_each_typed_id<spirv_cross::SPIRVariable>(
47+
[&](uint32_t, const spirv_cross::SPIRVariable& var) {
48+
if (var.storage != spv::StorageClassUniformConstant) {
49+
return;
50+
}
51+
const auto spir_type = sl_compiler->get_type(var.basetype);
52+
auto location = sl_compiler->get_decoration(
53+
var.self, spv::Decoration::DecorationLocation);
54+
if (spir_type.basetype ==
55+
spirv_cross::SPIRType::BaseType::SampledImage) {
56+
sampler_offsets.push_back(location);
57+
} else if (spir_type.basetype ==
58+
spirv_cross::SPIRType::BaseType::Float) {
59+
float_offsets.push_back(location);
60+
}
61+
});
62+
if (sampler_offsets.size() > 0) {
63+
auto start_offset =
64+
*std::min_element(sampler_offsets.begin(), sampler_offsets.end());
65+
for (auto offset : sampler_offsets) {
66+
sl_compiler->add_msl_resource_binding({
67+
.stage = spv::ExecutionModel::ExecutionModelFragment,
68+
.basetype = spirv_cross::SPIRType::BaseType::SampledImage,
69+
.binding = offset,
70+
.count = 1u,
71+
.msl_texture = offset - start_offset,
72+
});
73+
}
74+
}
75+
if (float_offsets.size() > 0) {
76+
auto start_offset =
77+
*std::min_element(float_offsets.begin(), float_offsets.end());
78+
for (auto offset : float_offsets) {
79+
sl_compiler->add_msl_resource_binding({
80+
.stage = spv::ExecutionModel::ExecutionModelFragment,
81+
.basetype = spirv_cross::SPIRType::BaseType::Float,
82+
.binding = offset,
83+
.count = 1u,
84+
.msl_buffer = offset - start_offset,
85+
});
86+
}
87+
}
88+
}
89+
3890
return CompilerBackend(sl_compiler);
3991
}
4092

impeller/compiler/impellerc_main.cc

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ bool Main(const fml::CommandLine& command_line) {
7373
switches.source_file_name, options.type, options.source_language,
7474
switches.entry_point);
7575
options.json_format = switches.json_format;
76+
options.remap_samplers = switches.remap_samplers;
7677
options.gles_language_version = switches.gles_language_version;
7778

7879
Reflector::Options reflector_options;

impeller/compiler/source_options.h

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct SourceOptions {
2727
uint32_t gles_language_version = 100;
2828
std::vector<std::string> defines;
2929
bool json_format = false;
30+
bool remap_samplers = false;
3031

3132
SourceOptions();
3233

impeller/compiler/switches.cc

+4
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ void Switches::PrintHelp(std::ostream& stream) {
7171
stream << "[optional] --depfile=<depfile_path>" << std::endl;
7272
stream << "[optional] --gles-language-verision=<number>" << std::endl;
7373
stream << "[optional] --json" << std::endl;
74+
stream << "[optional] --remap-samplers (force metal sampler index to match "
75+
"declared order)"
76+
<< std::endl;
7477
}
7578

7679
Switches::Switches() = default;
@@ -125,6 +128,7 @@ Switches::Switches(const fml::CommandLine& command_line)
125128
command_line.GetOptionValueWithDefault("reflection-cc", "")),
126129
depfile_path(command_line.GetOptionValueWithDefault("depfile", "")),
127130
json_format(command_line.HasOption("json")),
131+
remap_samplers(command_line.HasOption("remap-samplers")),
128132
gles_language_version(
129133
stoi(command_line.GetOptionValueWithDefault("gles-language-version",
130134
"0"))),

impeller/compiler/switches.h

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ struct Switches {
3232
std::string depfile_path;
3333
std::vector<std::string> defines;
3434
bool json_format;
35+
bool remap_samplers;
3536
SourceLanguage source_language = SourceLanguage::kUnknown;
3637
uint32_t gles_language_version;
3738
std::string entry_point;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// Declare matrix in different order than usage.
6+
uniform mat4 matrix;
7+
uniform float floatB;
8+
9+
out vec4 frag_color;
10+
11+
void main() {
12+
vec4 sample_1 = vec4(floatB);
13+
vec4 sample_2 = sample_1 * matrix;
14+
frag_color = sample_1 + sample_2;
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// Declare floats in different order than usage.
6+
uniform float floatA;
7+
uniform float floatB;
8+
9+
out vec4 frag_color;
10+
11+
void main() {
12+
vec4 sample_1 = vec4(floatB);
13+
vec4 sample_2 = vec4(floatA);
14+
frag_color = sample_1 + sample_2;
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// Declare samplers in different order than usage.
6+
uniform sampler2D textureA;
7+
uniform sampler2D textureB;
8+
9+
out vec4 frag_color;
10+
11+
void main() {
12+
vec4 sample_1 = texture(textureB, vec2(1.0));
13+
vec4 sample_2 = texture(textureA, vec2(1.0));
14+
frag_color = sample_1 + sample_2;
15+
}

impeller/tools/impeller.gni

+9
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,13 @@ template("impellerc") {
241241
iplr = invoker.iplr
242242
}
243243
json = false
244+
remap_samplers = false
244245
if (defined(invoker.json) && invoker.json) {
245246
json = invoker.json
246247
}
248+
if (defined(invoker.remap_samplers) && invoker.remap_samplers) {
249+
remap_samplers = invoker.remap_samplers
250+
}
247251

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

260266
_impellerc(target_name) {
261267
sources = invoker.shaders
@@ -292,6 +298,9 @@ template("impellerc") {
292298
if (json) {
293299
args += [ "--json" ]
294300
}
301+
if (remap_samplers) {
302+
args += [ "--remap-samplers" ]
303+
}
295304

296305
if (sksl) {
297306
sl_intermediate =

lib/ui/fixtures/shaders/BUILD.gn

+14
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,24 @@ if (enable_unittests) {
3333
json = true
3434
}
3535

36+
impellerc("sampler_order_fixture") {
37+
shaders = [
38+
"//flutter/impeller/fixtures/ordering/shader_with_samplers.frag",
39+
"//flutter/impeller/fixtures/ordering/shader_with_ordered_floats.frag",
40+
"//flutter/impeller/fixtures/ordering/shader_with_matrix.frag",
41+
]
42+
shader_target_flag = "--runtime-stage-metal"
43+
intermediates_subdir = "iplr-remap"
44+
sl_file_extension = "iplr"
45+
iplr = true
46+
remap_samplers = true
47+
}
48+
3649
test_fixtures("fixtures") {
3750
deps = [
3851
":ink_sparkle",
3952
":ink_sparkle_web",
53+
":sampler_order_fixture",
4054
]
4155
fixtures = get_target_outputs(":ink_sparkle")
4256
dest = "$root_gen_dir/flutter/lib/ui"

testing/dart/fragment_shader_test.dart

+46
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,47 @@ void main() async {
332332
shader.dispose();
333333
});
334334

335+
// This test can't rely on actual pixels rendered since it needs to run on a
336+
// metal shader on iOS. instead parse the source code.
337+
test('impellerc orders samplers in metal shader according to declaration and not usage', () async {
338+
if (!Platform.isMacOS) {
339+
return;
340+
}
341+
final Directory directory = shaderDirectory('iplr-remap');
342+
final String data = readAsStringLossy(File(path.join(directory.path, 'shader_with_samplers.frag.iplr')));
343+
344+
const String expected = 'texture2d<float> textureA [[texture(0)]],'
345+
' texture2d<float> textureB [[texture(1)]]';
346+
347+
expect(data, contains(expected));
348+
});
349+
350+
test('impellerc orders floats in metal shader according to declaration and not usage', () async {
351+
if (!Platform.isMacOS) {
352+
return;
353+
}
354+
final Directory directory = shaderDirectory('iplr-remap');
355+
final String data = readAsStringLossy(File(path.join(directory.path, 'shader_with_ordered_floats.frag.iplr')));
356+
357+
const String expected = 'constant float& floatA [[buffer(0)]], '
358+
'constant float& floatB [[buffer(1)]]';
359+
360+
expect(data, contains(expected));
361+
});
362+
363+
test('impellerc orders floats/matrix in metal shader according to declaration and not usage', () async {
364+
if (!Platform.isMacOS) {
365+
return;
366+
}
367+
final Directory directory = shaderDirectory('iplr-remap');
368+
final String data = readAsStringLossy(File(path.join(directory.path, 'shader_with_matrix.frag.iplr')));
369+
370+
const String expected = 'constant float4x4& matrix [[buffer(0)]], '
371+
'constant float& floatB [[buffer(1)]]';
372+
373+
expect(data, contains(expected));
374+
});
375+
335376
// Test all supported GLSL ops. See lib/spirv/lib/src/constants.dart
336377
final Map<String, FragmentProgram> iplrSupportedGLSLOpShaders = await _loadShaderAssets(
337378
path.join('supported_glsl_op_shaders', 'iplr'),
@@ -484,3 +525,8 @@ Image _createBlueGreenImageSync() {
484525
picture.dispose();
485526
}
486527
}
528+
529+
// Ignore invalid utf8 since file is not actually text.
530+
String readAsStringLossy(File file) {
531+
return convert.utf8.decode(file.readAsBytesSync(), allowMalformed: true);
532+
}

0 commit comments

Comments
 (0)