diff --git a/packages/flutter_tools/lib/src/build_system/tools/shader_compiler.dart b/packages/flutter_tools/lib/src/build_system/tools/shader_compiler.dart index 14089a9357e7c..48869380bd697 100644 --- a/packages/flutter_tools/lib/src/build_system/tools/shader_compiler.dart +++ b/packages/flutter_tools/lib/src/build_system/tools/shader_compiler.dart @@ -173,9 +173,9 @@ class ShaderCompiler { } final String shaderLibPath = _fs.path.join(impellerc.parent.absolute.path, 'shader_lib'); - final cmd = [ + List makeImpellercCommand(List targets) => [ impellerc.path, - ..._shaderTargetsFromTargetPlatform(targetPlatform), + ...targets, '--iplr', if (targetPlatform == TargetPlatform.web_javascript) '--json', '--sl=$outputPath', @@ -185,18 +185,64 @@ class ShaderCompiler { '--include=${input.parent.path}', '--include=$shaderLibPath', ]; - _logger.printTrace('shaderc command: $cmd'); - final Process impellercProcess = await _processManager.start(cmd); - final int code = await impellercProcess.exitCode; - if (code != 0) { - final String stdout = await utf8.decodeStream(impellercProcess.stdout); - final String stderr = await utf8.decodeStream(impellercProcess.stderr); - _logger.printTrace(stdout); - _logger.printError(stderr); + + var failure = false; + var retryWithoutSksl = false; + + final List shaderTargets = _shaderTargetsFromTargetPlatform(targetPlatform); + final List cmd = makeImpellercCommand(shaderTargets); + _logger.printTrace('impellerc command: $cmd'); + final ProcessResult result = await _processManager.run( + cmd, + stderrEncoding: utf8, + stdoutEncoding: utf8, + ); + if (result.exitCode != 0) { + + // Maybe retry impellerc command without --sksl. + if (!(shaderTargets.length > 1 && shaderTargets.contains('--sksl'))) { + // The original command did not target sksl or targeted only sksl, so + // we can't retry without --sksl. + _logger.printTrace((result.stdout as String).trim()); + _logger.printError((result.stderr as String).trim()); + failure = true; + } else { + retryWithoutSksl = true; + } + } + + if (retryWithoutSksl) { + shaderTargets.remove('--sksl'); + final List retryCmd = makeImpellercCommand(shaderTargets); + _logger.printTrace('Retrying impellerc command without sksl: $cmd'); + final ProcessResult retryResult = await _processManager.run( + retryCmd, + stderrEncoding: utf8, + stdoutEncoding: utf8, + ); + if (retryResult.exitCode != 0) { + // Retry failed. + _logger.printTrace((retryResult.stdout as String).trim()); + _logger.printError((retryResult.stderr as String).trim()); + failure = true; + } else { + // Retry succeeded. Don't fail, but log the original (sksl) error and a + // warning message. + _logger.printError('impellerc SkSL error: ${result.stderr}'.trim()); + // The warning message must begin with the exact string "warning: " to + // ensure that these non-fatal log messages show up when building with + // the Xcode backend. + _logger.printError( + 'warning: Shader `${input.path}` is incompatible with SkSL. This ' + 'shader will not load when running with the Skia backend.'); + } + } + + if (failure) { if (fatal) { throw ShaderCompilerException._( 'Shader compilation of "${input.path}" to "$outputPath" ' - 'failed with exit code $code.', + 'failed with exit code ${result.exitCode}.', ); } return false;