Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Flutter GPU] Runtime shader import. #48875

Merged
merged 27 commits into from
Dec 14, 2023

Conversation

bdero
Copy link
Member

@bdero bdero commented Dec 11, 2023

  • Add --shader-bundle mode to impellerc that takes a simple JSON spec and produces a single flatbuffer with a pack of named shaders.
  • Added "single invocation" mode to the impellerc GN templates.
  • Record vertex attribute reflection information.
  • Light refactoring of the compiler frontend to make compiler invocations easier to follow.

Example shader bundle spec (json form of the yaml spec as shown in the Flutter GPU doc):

{
    "UnlitFragment": {
        "type": "fragment",
        "file": "shaders/flutter_gpu_unlit.frag"
    },
    "UnlitVertex": {
        "type": "vertex",
        "file": "shaders/flutter_gpu_unlit.vert"
    },
    "TextureFragment": {
        "type": "fragment",
        "file": "shaders/flutter_gpu_texture.frag"
    },
    "TextureVertex": {
        "type": "vertex",
        "file": "shaders/flutter_gpu_texture.vert"
    }
}

Example impellerc invocation:

impellerc \
  --include=~/projects/flutter/engine/src/flutter/impeller/compiler/shader_lib \
  --runtime-stage-metal \
  --sl=assets/TestLibrary.shaderbundle \
  --shader-bundle='{"UnlitFragment": {"type": "fragment", "file": "shaders/flutter_gpu_unlit.frag"}, "UnlitVertex": {"type": "vertex", "file": "shaders/flutter_gpu_unlit.vert"}, "TextureFragment": {"type": "fragment", "file": "shaders/flutter_gpu_texture.frag"}, "TextureVertex": {"type": "vertex", "file": "shaders/flutter_gpu_texture.vert"}}'

Runtime usage:

    /// Add a render pass encoder to the command buffer so that we can start
    /// encoding commands.
    final encoder = commandBuffer.createRenderPass(renderTarget);

    /// Load a shader bundle asset.
    final library =
        gpu.ShaderLibrary.fromAsset('assets/TestLibrary.shaderbundle')!;

    /// Create a RenderPipeline using shaders from the asset.
    final vertex = library['TextureVertex']!;
    final fragment = library['TextureFragment']!;
    final pipeline = gpu.gpuContext.createRenderPipeline(vertex, fragment);

    encoder.bindPipeline(pipeline);
Screen.Recording.2023-12-10.at.5.15.28.AM.mov

@bdero bdero self-assigned this Dec 11, 2023
@bdero bdero force-pushed the bdero/dart-gpu-compiler branch from a91b5da to 1e2910d Compare December 11, 2023 06:11
@bdero bdero force-pushed the bdero/dart-gpu-compiler branch from 3b88d00 to c9176dc Compare December 11, 2023 23:19
@bdero bdero marked this pull request as ready for review December 12, 2023 07:50
@bdero
Copy link
Member Author

bdero commented Dec 12, 2023

Alright, I think the testing story is looking pretty good for this. We have:

  • Tests for all the parser branches/error messages & a CLI switches test.
  • A test that runs the compiler from json string to flatbuffer and verifies the results.
  • A shader bundle being built as part of the fixture through the impellerc GN rule.
  • renderer_dart_unittests deserializes the fixture, and a test is using the shader library to form a pipeline and submit a valid raster command.

Copy link
Member

@jonahwilliams jonahwilliams left a comment

Choose a reason for hiding this comment

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

LGTM, mostly nits

@@ -353,6 +351,35 @@ std::shared_ptr<RuntimeStageData> Reflector::GenerateRuntimeStageData() const {
uniform_description.array_elements = GetArrayElements(spir_type);
data->AddUniformDescription(std::move(uniform_description));
}

// We only need to worry about reflecting vertex attributes.
Copy link
Member

Choose a reason for hiding this comment

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

don't we already reflect over the vertex attributes somewhere in compiler.cc?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, for the reflection C header. We pack the same data here.

Comment on lines +370 to +371
input_description.set = compiler_->get_decoration(
input.id, spv::Decoration::DecorationDescriptorSet);
Copy link
Member

Choose a reason for hiding this comment

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

FYI the vulkan backend can't support specifying different descriptor sets right now

Copy link
Member Author

Choose a reason for hiding this comment

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

Good to know. I just tried to replicate the way we pack the header template for now. My guess is that we can probably get away with removing a couple of these fields everywhere.

return nullptr;
}

// TODO(bdero): Vertex attributes.
Copy link
Member

Choose a reason for hiding this comment

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

file an issue for this?

Copy link
Member Author

Choose a reason for hiding this comment

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

Oops, this is a leftover comment. We've already got the attributes packed! :)

Comment on lines 47 to 49
for (auto it = name.begin(); it != name.end(); it++) {
*it = std::tolower(static_cast<unsigned char>(*it));
}
Copy link
Member

Choose a reason for hiding this comment

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

If you need to lowercase the string I think there is a utility for doing that explicitly instead of mutating the function arg

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 didn't manage to find one, but I did notice another place where I did this same thing in the compiler like a year ago. So I went ahead and added one to use in both places.

Comment on lines 67 to 72
if (source_language == "glsl") {
return SourceLanguage::kGLSL;
} else if (source_language == "hlsl") {
return SourceLanguage::kHLSL;
}
return SourceLanguage::kUnknown;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (source_language == "glsl") {
return SourceLanguage::kGLSL;
} else if (source_language == "hlsl") {
return SourceLanguage::kHLSL;
}
return SourceLanguage::kUnknown;
if (source_language == "glsl") {
return SourceLanguage::kGLSL;
}
if (source_language == "hlsl") {
return SourceLanguage::kHLSL;
}
return SourceLanguage::kUnknown;

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

std::string entry_point;
};

using ShaderBundleConfig = std::map<std::string, ShaderConfig>;
Copy link
Member

Choose a reason for hiding this comment

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

Not that it is super important but this should probably be an unordered_map unless you need the ordering properties.

Copy link
Member Author

Choose a reason for hiding this comment

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

Nah order isn't important for this, done.

// Hold a Dart-side reference to shaders in the library as they're wrapped for
// the first time. This prevents the wrapper from getting prematurely
// destroyed.
Map<String, Shader> shaders_ = {};
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Map<String, Shader> shaders_ = {};
final Map<String, Shader> shaders_ = {};

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

Comment on lines 36 to 37
if (result != null) {
shaders_[shaderName] = result;
Copy link
Member

Choose a reason for hiding this comment

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

this seems like it should be an error condition?

Copy link
Member

Choose a reason for hiding this comment

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

maybe something like this:

Shader? result = shaders_.putIfAbsent(shaderName, () {
  var result = _getShader(shaderName, Shader._());
  if (result == null) {
    throw Exception('..');
  }
}

Copy link
Member Author

Choose a reason for hiding this comment

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

The intention is to return null if the user tries to look up a shader that doesn't exist in the bundle.

Copy link
Member

Choose a reason for hiding this comment

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

If the shader bundle can't change over time I would let the map contain null entries then:

Map<String, Shader?> shaders_ = {};

Then this function becomes:

return shaders_.putIfAbsent(shaderName, () => _getShader(shaderName, Shader._()));

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah good point. Done.

@@ -76,6 +76,10 @@ test_fixtures("file_fixtures") {
"blue_noise.png",
"boston.jpg",
"embarcadero.jpg",
"flutter_gpu_unlit.frag",
Copy link
Member

Choose a reason for hiding this comment

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

nit: alphabetize

Copy link
Member Author

Choose a reason for hiding this comment

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

Done. Someday I'll learn my abcs :^)

compiled_action_foreach(target_name) {
forward_variables_from(invoker, "*")
tool = "//flutter/impeller/compiler:impellerc"
if (invoker.single_invocation) {
Copy link
Member

Choose a reason for hiding this comment

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

Please document the template parameter in the comment on the template.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

])

# Optional: invoker.iplr Causes --sl output to be in iplr format.
# Optional: invoker.iplr_bundle specifies a Flutter GPU shader bundle configuration.
# Optional: invoker.shader_bundle specifies a Flutter GPU shader bundle configuration.
Copy link
Member

Choose a reason for hiding this comment

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

These comments about the parameters are in a bit of weird spot. They should probably be moved up above line 278, and all the accepted parameters should be documented. Like if this template accepts single_invocation, then it should be documented in the docstring for this template.

Copy link
Member Author

Choose a reason for hiding this comment

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

Alrighty, I moved these up to the top of the template and tried to document the missing ones accurately.

@@ -68,6 +70,7 @@ impeller_component("compiler_lib") {
"//flutter/fml",

# All third_party deps must be included by the global license script.
"//flutter/impeller/shader_bundle:shader_bundle_flatbuffers",
Copy link
Member

Choose a reason for hiding this comment

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

The comment on the line above seems to be referring to the third_party deps on the line below. Maybe this line should instead be grouped right below //flutter/fml instead of below this comment?

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

stream << "[optional] --source-language=glsl|hlsl (default: glsl)"
<< std::endl;
stream << "[optional] --entry-point=<entry_point_name> (default: main; "
"ignored for glsl)"
<< std::endl;
stream << "[optional] --iplr (causes --sl file to be emitted in iplr format)"
<< std::endl;
stream << "[optional] --iplr-bundle=<bundle_spec> (causes --sl file to be "
"emitted in the iplr bundle format)"
stream << "[optional] --shader-bundle=<bundle_spec> (causes --sl file to be "
Copy link
Member

Choose a reason for hiding this comment

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

Any thoughts about reading the json spec in from a file instead of the flag itself being json-valued? I wonder which will be easier for tools that wrap/call impellerc.

Copy link
Member Author

Choose a reason for hiding this comment

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

My hunch is that storing it in a file before invoking is more of a pain given the ephemeral nature. Although it would be slightly more convenient for the many of manual invocations I've been doing over the past couple of days. : )

@bdero bdero added the autosubmit Merge PR when tree becomes green via auto submit App label Dec 14, 2023
@auto-submit auto-submit bot merged commit a141712 into flutter:main Dec 14, 2023
28 checks passed
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Dec 14, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Dec 14, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Dec 14, 2023
auto-submit bot pushed a commit to flutter/flutter that referenced this pull request Dec 14, 2023
…140130)

flutter/engine@9f7004e...923f9e2

2023-12-14 98614782+auto-submit[bot]@users.noreply.github.com Reverts "[Windows] Move to `FlutterCompositor` for rendering" (flutter/engine#49015)
2023-12-14 magder@google.com Add xcprivacy privacy manifest to iOS framework (flutter/engine#48951)
2023-12-14 30870216+gaaclarke@users.noreply.github.com [Impeller] Made the new blur support 1D blurs (flutter/engine#49001)
2023-12-14 skia-flutter-autoroll@skia.org Roll Skia from 69c02c9d56b2 to 188515347032 (1 revision) (flutter/engine#49005)
2023-12-14 bdero@google.com [Impeller] Add golden for clipped+transformed blur. (flutter/engine#48886)
2023-12-14 bdero@google.com [Flutter GPU] Runtime shader import. (flutter/engine#48875)
2023-12-13 737941+loic-sharma@users.noreply.github.com [Windows] Move to `FlutterCompositor` for rendering (flutter/engine#48849)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC jsimmons@google.com,rmistry@google.com,zra@google.com on the revert to ensure that a human
is aware of the problem.

To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
@bdero
Copy link
Member Author

bdero commented Jan 3, 2024

This is part of the Flutter GPU MVP umbrella bug: flutter/flutter#130921

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
autosubmit Merge PR when tree becomes green via auto submit App e: impeller
Projects
No open projects
Status: ✅ Done
Development

Successfully merging this pull request may close these issues.

3 participants