Skip to content

Commit 290a85e

Browse files
Jonah Williamsgaaclarke
andauthored
[Impeller] handle shader ordering bug on macOS. (#165937)
Fixes flutter/flutter#165740 if a user declares interleaved `float` and `sampler2D` uniforms, then we can screw up the binding order. This only manifests on platforms where we don't combine all float/scalar uniform values into a single struct. We should probably start doing that for metal, but refactoring there is out of scope for this patch. I enabled a large number of skipped tests we probably should have turned on by now. oops. --------- Co-authored-by: gaaclarke <30870216+gaaclarke@users.noreply.github.com>
1 parent 1937cae commit 290a85e

File tree

4 files changed

+69
-72
lines changed

4 files changed

+69
-72
lines changed

engine/src/flutter/impeller/entity/contents/runtime_effect_contents.cc

Lines changed: 30 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,9 @@ RuntimeEffectContents::CreatePipeline(const ContentContext& renderer,
168168
const std::shared_ptr<Context>& context = renderer.GetContext();
169169
const std::shared_ptr<ShaderLibrary>& library = context->GetShaderLibrary();
170170
const std::shared_ptr<const Capabilities>& caps = context->GetCapabilities();
171-
const auto color_attachment_format = caps->GetDefaultColorFormat();
172-
const auto stencil_attachment_format = caps->GetDefaultDepthStencilFormat();
171+
const PixelFormat color_attachment_format = caps->GetDefaultColorFormat();
172+
const PixelFormat stencil_attachment_format =
173+
caps->GetDefaultDepthStencilFormat();
173174

174175
using VS = RuntimeEffectVertexShader;
175176

@@ -233,25 +234,37 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
233234
///
234235
BindFragmentCallback bind_callback = [this, &renderer,
235236
&context](RenderPass& pass) {
236-
size_t minimum_sampler_index = 100000000;
237237
size_t buffer_index = 0;
238238
size_t buffer_offset = 0;
239-
239+
size_t sampler_location = 0;
240+
size_t buffer_location = 0;
241+
242+
// Uniforms are ordered in the IPLR according to their
243+
// declaration and the uniform location reflects the correct offset to
244+
// be mapped to - except that it may include all proceeding
245+
// uniforms of a different type. For example, a texture sampler that comes
246+
// after 4 float uniforms may have a location of 4. Since we know that
247+
// the declarations are already ordered, we can track the uniform location
248+
// ourselves.
240249
for (const auto& uniform : runtime_stage_->GetUniforms()) {
241250
std::unique_ptr<ShaderMetadata> metadata = MakeShaderMetadata(uniform);
242251
switch (uniform.type) {
243252
case kSampledImage: {
244-
// Sampler uniforms are ordered in the IPLR according to their
245-
// declaration and the uniform location reflects the correct offset to
246-
// be mapped to - except that it may include all proceeding float
247-
// uniforms. For example, a float sampler that comes after 4 float
248-
// uniforms may have a location of 4. To convert to the actual offset
249-
// we need to find the largest location assigned to a float uniform
250-
// and then subtract this from all uniform locations. This is more or
251-
// less the same operation we previously performed in the shader
252-
// compiler.
253-
minimum_sampler_index =
254-
std::min(minimum_sampler_index, uniform.location);
253+
FML_DCHECK(sampler_location < texture_inputs_.size());
254+
auto& input = texture_inputs_[sampler_location];
255+
256+
raw_ptr<const Sampler> sampler =
257+
context->GetSamplerLibrary()->GetSampler(
258+
input.sampler_descriptor);
259+
260+
SampledImageSlot image_slot;
261+
image_slot.name = uniform.name.c_str();
262+
image_slot.binding = uniform.binding;
263+
image_slot.texture_index = sampler_location;
264+
pass.BindDynamicResource(ShaderStage::kFragment,
265+
DescriptorType::kSampledImage, image_slot,
266+
std::move(metadata), input.texture, sampler);
267+
sampler_location++;
255268
break;
256269
}
257270
case kFloat: {
@@ -268,12 +281,13 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
268281

269282
ShaderUniformSlot uniform_slot;
270283
uniform_slot.name = uniform.name.c_str();
271-
uniform_slot.ext_res_0 = uniform.location;
284+
uniform_slot.ext_res_0 = buffer_location;
272285
pass.BindDynamicResource(ShaderStage::kFragment,
273286
DescriptorType::kUniformBuffer, uniform_slot,
274287
std::move(metadata), std::move(buffer_view));
275288
buffer_index++;
276289
buffer_offset += uniform.GetSize();
290+
buffer_location++;
277291
break;
278292
}
279293
case kStruct: {
@@ -292,34 +306,6 @@ bool RuntimeEffectContents::Render(const ContentContext& renderer,
292306
}
293307
}
294308

295-
size_t sampler_index = 0;
296-
for (const auto& uniform : runtime_stage_->GetUniforms()) {
297-
std::unique_ptr<ShaderMetadata> metadata = MakeShaderMetadata(uniform);
298-
299-
switch (uniform.type) {
300-
case kSampledImage: {
301-
FML_DCHECK(sampler_index < texture_inputs_.size());
302-
auto& input = texture_inputs_[sampler_index];
303-
304-
raw_ptr<const Sampler> sampler =
305-
context->GetSamplerLibrary()->GetSampler(
306-
input.sampler_descriptor);
307-
308-
SampledImageSlot image_slot;
309-
image_slot.name = uniform.name.c_str();
310-
image_slot.binding = uniform.binding;
311-
image_slot.texture_index = uniform.location - minimum_sampler_index;
312-
pass.BindDynamicResource(ShaderStage::kFragment,
313-
DescriptorType::kSampledImage, image_slot,
314-
std::move(metadata), input.texture, sampler);
315-
316-
sampler_index++;
317-
break;
318-
}
319-
default:
320-
continue;
321-
}
322-
}
323309
return true;
324310
};
325311

engine/src/flutter/lib/ui/fixtures/shaders/general_shaders/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ if (enable_unittests) {
1616
"simple.frag",
1717
"uniforms.frag",
1818
"uniforms_sorted.frag",
19+
"uniform_ordering.frag",
1920
"uniform_arrays.frag",
2021
"filter_shader.frag",
2122
"missing_size.frag",
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#version 300 es
2+
3+
// Copyright 2013 The Flutter Authors. All rights reserved.
4+
// Use of this source code is governed by a BSD-style license that can be
5+
// found in the LICENSE file.
6+
7+
uniform float a;
8+
uniform sampler2D u_texture;
9+
uniform float b;
10+
uniform float c;
11+
12+
out vec4 frag_color;
13+
14+
void main() {
15+
if (a == 1.0 && b == 2.0 && c == 3.0) {
16+
frag_color = vec4(0, 1, 0, 1);
17+
} else {
18+
frag_color = vec4(1, 0, 0, 1);
19+
}
20+
}

engine/src/flutter/testing/dart/fragment_shader_test.dart

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -195,21 +195,13 @@ void main() async {
195195
});
196196

197197
test('FragmentShader simple shader renders correctly', () async {
198-
if (impellerEnabled) {
199-
print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823');
200-
return;
201-
}
202198
final FragmentProgram program = await FragmentProgram.fromAsset('functions.frag.iplr');
203199
final FragmentShader shader = program.fragmentShader()..setFloat(0, 1.0);
204200
await _expectShaderRendersGreen(shader);
205201
shader.dispose();
206202
});
207203

208204
test('Reused FragmentShader simple shader renders correctly', () async {
209-
if (impellerEnabled) {
210-
print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823');
211-
return;
212-
}
213205
final FragmentProgram program = await FragmentProgram.fromAsset('functions.frag.iplr');
214206
final FragmentShader shader = program.fragmentShader()..setFloat(0, 1.0);
215207
await _expectShaderRendersGreen(shader);
@@ -221,10 +213,6 @@ void main() async {
221213
});
222214

223215
test('FragmentShader blue-green image renders green', () async {
224-
if (impellerEnabled) {
225-
print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823');
226-
return;
227-
}
228216
final FragmentProgram program = await FragmentProgram.fromAsset('blue_green_sampler.frag.iplr');
229217
final Image blueGreenImage = await _createBlueGreenImage();
230218
final FragmentShader shader = program.fragmentShader()..setImageSampler(0, blueGreenImage);
@@ -234,10 +222,6 @@ void main() async {
234222
});
235223

236224
test('FragmentShader blue-green image renders green - GPU image', () async {
237-
if (impellerEnabled) {
238-
print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823');
239-
return;
240-
}
241225
final FragmentProgram program = await FragmentProgram.fromAsset('blue_green_sampler.frag.iplr');
242226
final Image blueGreenImage = _createBlueGreenImageSync();
243227
final FragmentShader shader = program.fragmentShader()..setImageSampler(0, blueGreenImage);
@@ -247,10 +231,6 @@ void main() async {
247231
});
248232

249233
test('FragmentShader with uniforms renders correctly', () async {
250-
if (impellerEnabled) {
251-
print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823');
252-
return;
253-
}
254234
final FragmentProgram program = await FragmentProgram.fromAsset('uniforms.frag.iplr');
255235

256236
final FragmentShader shader =
@@ -274,10 +254,6 @@ void main() async {
274254
});
275255

276256
test('FragmentShader shader with array uniforms renders correctly', () async {
277-
if (impellerEnabled) {
278-
print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823');
279-
return;
280-
}
281257
final FragmentProgram program = await FragmentProgram.fromAsset('uniform_arrays.frag.iplr');
282258

283259
final FragmentShader shader = program.fragmentShader();
@@ -305,10 +281,6 @@ void main() async {
305281
});
306282

307283
test('FragmentShader Uniforms are sorted correctly', () async {
308-
if (impellerEnabled) {
309-
print('Skipped for Impeller - https://github.com/flutter/flutter/issues/122823');
310-
return;
311-
}
312284
final FragmentProgram program = await FragmentProgram.fromAsset('uniforms_sorted.frag.iplr');
313285

314286
// The shader will not render green if the compiler doesn't keep the
@@ -323,6 +295,24 @@ void main() async {
323295
shader.dispose();
324296
});
325297

298+
test('FragmentShader Uniforms with interleaved textures are sorted ', () async {
299+
final FragmentProgram program = await FragmentProgram.fromAsset('uniform_ordering.frag.iplr');
300+
301+
// The shader will not render green if the compiler doesn't keep the
302+
// uniforms in the right order.
303+
final FragmentShader shader = program.fragmentShader();
304+
shader.setFloat(0, 1);
305+
shader.setFloat(1, 2);
306+
shader.setFloat(2, 3);
307+
308+
final Image blueGreenImage = _createBlueGreenImageSync();
309+
shader.setImageSampler(0, blueGreenImage);
310+
311+
await _expectShaderRendersGreen(shader);
312+
313+
shader.dispose();
314+
});
315+
326316
test('fromAsset throws an exception on invalid assetKey', () async {
327317
bool throws = false;
328318
try {

0 commit comments

Comments
 (0)