From fb067eab70c5b9f36d152c5501bf263c4254f1df Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 31 Aug 2022 12:07:49 -0700 Subject: [PATCH] [Impeller] Compute shader support (#35750) --- ci/licenses_golden/licenses_flutter | 12 + impeller/blobcat/blob.fbs | 1 + impeller/blobcat/blob_library.cc | 2 + impeller/blobcat/blob_types.h | 1 + impeller/blobcat/blob_writer.cc | 4 + impeller/compiler/code_gen_template.h | 2 + impeller/compiler/reflector.cc | 36 ++- impeller/compiler/reflector.h | 6 +- impeller/entity/contents/content_context.h | 175 ++++++------ .../contents/filters/blend_filter_contents.cc | 6 +- .../entity/contents/filters/filter_contents.h | 2 - impeller/fixtures/BUILD.gn | 5 + impeller/fixtures/sample.comp | 21 +- .../playground/imgui/imgui_impl_impeller.cc | 5 +- impeller/renderer/BUILD.gn | 8 + .../backend/gles/command_buffer_gles.cc | 7 + .../backend/gles/command_buffer_gles.h | 3 + .../renderer/backend/gles/pipeline_gles.h | 5 +- .../backend/gles/pipeline_library_gles.cc | 25 +- .../backend/gles/pipeline_library_gles.h | 7 +- .../backend/gles/shader_library_gles.cc | 2 + impeller/renderer/backend/metal/BUILD.gn | 4 + .../backend/metal/command_buffer_mtl.h | 3 + .../backend/metal/command_buffer_mtl.mm | 15 ++ .../renderer/backend/metal/compute_pass_mtl.h | 44 +++ .../backend/metal/compute_pass_mtl.mm | 252 ++++++++++++++++++ .../backend/metal/compute_pipeline_mtl.h | 41 +++ .../backend/metal/compute_pipeline_mtl.mm | 30 +++ .../backend/metal/pipeline_library_mtl.h | 8 +- .../backend/metal/pipeline_library_mtl.mm | 74 ++++- .../renderer/backend/metal/pipeline_mtl.h | 5 +- .../backend/vulkan/command_buffer_vk.cc | 6 + .../backend/vulkan/command_buffer_vk.h | 3 + .../backend/vulkan/pipeline_library_vk.cc | 22 +- .../backend/vulkan/pipeline_library_vk.h | 7 +- .../renderer/backend/vulkan/pipeline_vk.h | 5 +- .../backend/vulkan/shader_library_vk.cc | 2 + impeller/renderer/command.cc | 9 +- impeller/renderer/command.h | 2 +- impeller/renderer/command_buffer.cc | 13 + impeller/renderer/command_buffer.h | 11 + impeller/renderer/compute_command.cc | 82 ++++++ impeller/renderer/compute_command.h | 81 ++++++ impeller/renderer/compute_pass.cc | 50 ++++ impeller/renderer/compute_pass.h | 72 +++++ impeller/renderer/compute_pipeline_builder.cc | 11 + impeller/renderer/compute_pipeline_builder.h | 78 ++++++ .../renderer/compute_pipeline_descriptor.cc | 66 +++++ .../renderer/compute_pipeline_descriptor.h | 62 +++++ impeller/renderer/pipeline.cc | 48 +++- impeller/renderer/pipeline.h | 91 +++++-- impeller/renderer/pipeline_descriptor.h | 11 +- impeller/renderer/pipeline_library.cc | 18 +- impeller/renderer/pipeline_library.h | 12 +- impeller/renderer/renderer_unittests.cc | 72 ++++- .../runtime_stage/runtime_stage_unittests.cc | 6 +- impeller/tools/impeller.gni | 6 +- 57 files changed, 1473 insertions(+), 184 deletions(-) create mode 100644 impeller/renderer/backend/metal/compute_pass_mtl.h create mode 100644 impeller/renderer/backend/metal/compute_pass_mtl.mm create mode 100644 impeller/renderer/backend/metal/compute_pipeline_mtl.h create mode 100644 impeller/renderer/backend/metal/compute_pipeline_mtl.mm create mode 100644 impeller/renderer/compute_command.cc create mode 100644 impeller/renderer/compute_command.h create mode 100644 impeller/renderer/compute_pass.cc create mode 100644 impeller/renderer/compute_pass.h create mode 100644 impeller/renderer/compute_pipeline_builder.cc create mode 100644 impeller/renderer/compute_pipeline_builder.h create mode 100644 impeller/renderer/compute_pipeline_descriptor.cc create mode 100644 impeller/renderer/compute_pipeline_descriptor.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 7fd4bfea913fd..260ae32ba8cf3 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -768,6 +768,10 @@ FILE: ../../../flutter/impeller/renderer/backend/metal/blit_pass_mtl.h FILE: ../../../flutter/impeller/renderer/backend/metal/blit_pass_mtl.mm FILE: ../../../flutter/impeller/renderer/backend/metal/command_buffer_mtl.h FILE: ../../../flutter/impeller/renderer/backend/metal/command_buffer_mtl.mm +FILE: ../../../flutter/impeller/renderer/backend/metal/compute_pass_mtl.h +FILE: ../../../flutter/impeller/renderer/backend/metal/compute_pass_mtl.mm +FILE: ../../../flutter/impeller/renderer/backend/metal/compute_pipeline_mtl.h +FILE: ../../../flutter/impeller/renderer/backend/metal/compute_pipeline_mtl.mm FILE: ../../../flutter/impeller/renderer/backend/metal/context_mtl.h FILE: ../../../flutter/impeller/renderer/backend/metal/context_mtl.mm FILE: ../../../flutter/impeller/renderer/backend/metal/device_buffer_mtl.h @@ -849,6 +853,14 @@ FILE: ../../../flutter/impeller/renderer/command.cc FILE: ../../../flutter/impeller/renderer/command.h FILE: ../../../flutter/impeller/renderer/command_buffer.cc FILE: ../../../flutter/impeller/renderer/command_buffer.h +FILE: ../../../flutter/impeller/renderer/compute_command.cc +FILE: ../../../flutter/impeller/renderer/compute_command.h +FILE: ../../../flutter/impeller/renderer/compute_pass.cc +FILE: ../../../flutter/impeller/renderer/compute_pass.h +FILE: ../../../flutter/impeller/renderer/compute_pipeline_builder.cc +FILE: ../../../flutter/impeller/renderer/compute_pipeline_builder.h +FILE: ../../../flutter/impeller/renderer/compute_pipeline_descriptor.cc +FILE: ../../../flutter/impeller/renderer/compute_pipeline_descriptor.h FILE: ../../../flutter/impeller/renderer/context.cc FILE: ../../../flutter/impeller/renderer/context.h FILE: ../../../flutter/impeller/renderer/descriptor_set_layout.h diff --git a/impeller/blobcat/blob.fbs b/impeller/blobcat/blob.fbs index 741c6bfca567d..715dd70ccbaef 100644 --- a/impeller/blobcat/blob.fbs +++ b/impeller/blobcat/blob.fbs @@ -7,6 +7,7 @@ namespace impeller.fb; enum Stage:byte { kVertex, kFragment, + kCompute, } table Blob { diff --git a/impeller/blobcat/blob_library.cc b/impeller/blobcat/blob_library.cc index 8282f2cb475df..02ba647adcacf 100644 --- a/impeller/blobcat/blob_library.cc +++ b/impeller/blobcat/blob_library.cc @@ -18,6 +18,8 @@ constexpr BlobShaderType ToShaderType(fb::Stage stage) { return BlobShaderType::kVertex; case fb::Stage::kFragment: return BlobShaderType::kFragment; + case fb::Stage::kCompute: + return BlobShaderType::kCompute; } FML_UNREACHABLE(); } diff --git a/impeller/blobcat/blob_types.h b/impeller/blobcat/blob_types.h index 7c22d907d06ab..37dea53998664 100644 --- a/impeller/blobcat/blob_types.h +++ b/impeller/blobcat/blob_types.h @@ -9,6 +9,7 @@ namespace impeller { enum class BlobShaderType { kVertex, kFragment, + kCompute, }; } // namespace impeller diff --git a/impeller/blobcat/blob_writer.cc b/impeller/blobcat/blob_writer.cc index cb242041cffbb..d3ff7b6242ef0 100644 --- a/impeller/blobcat/blob_writer.cc +++ b/impeller/blobcat/blob_writer.cc @@ -22,6 +22,8 @@ std::optional InferShaderTypefromFileExtension( return BlobShaderType::kVertex; } else if (path == ".frag") { return BlobShaderType::kFragment; + } else if (path == ".comp") { + return BlobShaderType::kCompute; } return std::nullopt; } @@ -88,6 +90,8 @@ constexpr fb::Stage ToStage(BlobShaderType type) { return fb::Stage::kVertex; case BlobShaderType::kFragment: return fb::Stage::kFragment; + case BlobShaderType::kCompute: + return fb::Stage::kCompute; } FML_UNREACHABLE(); } diff --git a/impeller/compiler/code_gen_template.h b/impeller/compiler/code_gen_template.h index 58b9b0a7b9464..56a14f71ba533 100644 --- a/impeller/compiler/code_gen_template.h +++ b/impeller/compiler/code_gen_template.h @@ -20,6 +20,8 @@ constexpr std::string_view kReflectionHeaderTemplate = #include "impeller/renderer/command.h" {# // nogncheck #} +#include "impeller/renderer/compute_command.h" {# // nogncheck #} + #include "impeller/renderer/descriptor_set_layout.h" {# // nogncheck #} #include "impeller/renderer/sampler.h" {# // nogncheck #} diff --git a/impeller/compiler/reflector.cc b/impeller/compiler/reflector.cc index 0df6098086df6..b6fc7b527e7dc 100644 --- a/impeller/compiler/reflector.cc +++ b/impeller/compiler/reflector.cc @@ -84,6 +84,21 @@ static std::string ExecutionModelToString(spv::ExecutionModel model) { } } +static std::string ExecutionModelToCommandTypeName( + spv::ExecutionModel execution_model) { + switch (execution_model) { + case spv::ExecutionModel::ExecutionModelVertex: + case spv::ExecutionModel::ExecutionModelFragment: + case spv::ExecutionModel::ExecutionModelTessellationControl: + case spv::ExecutionModel::ExecutionModelTessellationEvaluation: + return "Command&"; + case spv::ExecutionModel::ExecutionModelGLCompute: + return "ComputeCommand&"; + default: + return "unsupported"; + } +} + static std::string StringToShaderStage(std::string str) { if (str == "vertex") { return "ShaderStage::kVertex"; @@ -209,11 +224,11 @@ std::optional Reflector::GenerateTemplateArguments() const { return std::nullopt; } + auto execution_model = entrypoints.front().execution_model; { root["entrypoint"] = options_.entry_point_name; root["shader_name"] = options_.shader_name; - root["shader_stage"] = - ExecutionModelToString(entrypoints.front().execution_model); + root["shader_stage"] = ExecutionModelToString(execution_model); root["header_file_name"] = options_.header_file_name; } @@ -315,7 +330,8 @@ std::optional Reflector::GenerateTemplateArguments() const { }); } - root["bind_prototypes"] = EmitBindPrototypes(shader_resources); + root["bind_prototypes"] = + EmitBindPrototypes(shader_resources, execution_model); return root; } @@ -887,7 +903,8 @@ std::string Reflector::GetMemberNameAtIndex( } std::vector Reflector::ReflectBindPrototypes( - const spirv_cross::ShaderResources& resources) const { + const spirv_cross::ShaderResources& resources, + spv::ExecutionModel execution_model) const { std::vector prototypes; for (const auto& uniform_buffer : resources.uniform_buffers) { auto& proto = prototypes.emplace_back(BindPrototype{}); @@ -900,7 +917,7 @@ std::vector Reflector::ReflectBindPrototypes( proto.docstring = stream.str(); } proto.args.push_back(BindPrototypeArgument{ - .type_name = "Command&", + .type_name = ExecutionModelToCommandTypeName(execution_model), .argument_name = "command", }); proto.args.push_back(BindPrototypeArgument{ @@ -919,7 +936,7 @@ std::vector Reflector::ReflectBindPrototypes( proto.docstring = stream.str(); } proto.args.push_back(BindPrototypeArgument{ - .type_name = "Command&", + .type_name = ExecutionModelToCommandTypeName(execution_model), .argument_name = "command", }); proto.args.push_back(BindPrototypeArgument{ @@ -938,7 +955,7 @@ std::vector Reflector::ReflectBindPrototypes( proto.docstring = stream.str(); } proto.args.push_back(BindPrototypeArgument{ - .type_name = "Command&", + .type_name = ExecutionModelToCommandTypeName(execution_model), .argument_name = "command", }); proto.args.push_back(BindPrototypeArgument{ @@ -992,8 +1009,9 @@ std::vector Reflector::ReflectBindPrototypes( } nlohmann::json::array_t Reflector::EmitBindPrototypes( - const spirv_cross::ShaderResources& resources) const { - const auto prototypes = ReflectBindPrototypes(resources); + const spirv_cross::ShaderResources& resources, + spv::ExecutionModel execution_model) const { + const auto prototypes = ReflectBindPrototypes(resources, execution_model); nlohmann::json::array_t result; for (const auto& res : prototypes) { auto& item = result.emplace_back(nlohmann::json::object_t{}); diff --git a/impeller/compiler/reflector.h b/impeller/compiler/reflector.h index 808ce56d3b223..2c0de13ce682b 100644 --- a/impeller/compiler/reflector.h +++ b/impeller/compiler/reflector.h @@ -118,10 +118,12 @@ class Reflector { const spirv_cross::TypeID& type_id) const; std::vector ReflectBindPrototypes( - const spirv_cross::ShaderResources& resources) const; + const spirv_cross::ShaderResources& resources, + spv::ExecutionModel execution_model) const; nlohmann::json::array_t EmitBindPrototypes( - const spirv_cross::ShaderResources& resources) const; + const spirv_cross::ShaderResources& resources, + spv::ExecutionModel execution_model) const; std::optional ReflectPerVertexStructDefinition( const spirv_cross::SmallVector& stage_inputs) diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index 89a7e86c6620f..259735340d198 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -63,78 +63,95 @@ #include "impeller/entity/vertices.frag.h" #include "impeller/entity/vertices.vert.h" #include "impeller/renderer/formats.h" +#include "impeller/renderer/pipeline.h" namespace impeller { using LinearGradientFillPipeline = - PipelineT; + RenderPipelineT; using SolidFillPipeline = - PipelineT; + RenderPipelineT; using RadialGradientFillPipeline = - PipelineT; + RenderPipelineT; using SweepGradientFillPipeline = - PipelineT; -using BlendPipeline = PipelineT; + RenderPipelineT; +using BlendPipeline = RenderPipelineT; using RRectBlurPipeline = - PipelineT; -using BlendPipeline = PipelineT; -using BlendColorPipeline = - PipelineT; + RenderPipelineT; +using BlendPipeline = RenderPipelineT; +using BlendColorPipeline = RenderPipelineT; using BlendColorBurnPipeline = - PipelineT; + RenderPipelineT; using BlendColorDodgePipeline = - PipelineT; -using BlendDarkenPipeline = - PipelineT; + RenderPipelineT; +using BlendDarkenPipeline = RenderPipelineT; using BlendDifferencePipeline = - PipelineT; + RenderPipelineT; using BlendExclusionPipeline = - PipelineT; + RenderPipelineT; using BlendHardLightPipeline = - PipelineT; + RenderPipelineT; using BlendHuePipeline = - PipelineT; + RenderPipelineT; using BlendLightenPipeline = - PipelineT; + RenderPipelineT; using BlendLuminosityPipeline = - PipelineT; + RenderPipelineT; using BlendMultiplyPipeline = - PipelineT; + RenderPipelineT; using BlendOverlayPipeline = - PipelineT; + RenderPipelineT; using BlendSaturationPipeline = - PipelineT; -using BlendScreenPipeline = - PipelineT; + RenderPipelineT; +using BlendScreenPipeline = RenderPipelineT; using BlendSoftLightPipeline = - PipelineT; + RenderPipelineT; using TexturePipeline = - PipelineT; -using TiledTexturePipeline = - PipelineT; + RenderPipelineT; +using TiledTexturePipeline = RenderPipelineT; using GaussianBlurPipeline = - PipelineT; + RenderPipelineT; using BorderMaskBlurPipeline = - PipelineT; + RenderPipelineT; using MorphologyFilterPipeline = - PipelineT; + RenderPipelineT; using ColorMatrixColorFilterPipeline = - PipelineT; + RenderPipelineT; using LinearToSrgbFilterPipeline = - PipelineT; + RenderPipelineT; using SrgbToLinearFilterPipeline = - PipelineT; + RenderPipelineT; using SolidStrokePipeline = - PipelineT; + RenderPipelineT; using GlyphAtlasPipeline = - PipelineT; + RenderPipelineT; using VerticesPipeline = - PipelineT; -using AtlasPipeline = PipelineT; + RenderPipelineT; +using AtlasPipeline = + RenderPipelineT; // Instead of requiring new shaders for clips, the solid fill stages are used // to redirect writing to the stencil instead of color attachments. -using ClipPipeline = PipelineT; +using ClipPipeline = + RenderPipelineT; struct ContentContextOptions { SampleCount sample_count = SampleCount::kCount1; @@ -170,170 +187,173 @@ class ContentContext { bool IsValid() const; - std::shared_ptr GetLinearGradientFillPipeline( + std::shared_ptr> GetLinearGradientFillPipeline( ContentContextOptions opts) const { return GetPipeline(linear_gradient_fill_pipelines_, opts); } - std::shared_ptr GetRadialGradientFillPipeline( + std::shared_ptr> GetRadialGradientFillPipeline( ContentContextOptions opts) const { return GetPipeline(radial_gradient_fill_pipelines_, opts); } - std::shared_ptr GetRRectBlurPipeline( + std::shared_ptr> GetRRectBlurPipeline( ContentContextOptions opts) const { return GetPipeline(rrect_blur_pipelines_, opts); } - std::shared_ptr GetSweepGradientFillPipeline( + std::shared_ptr> GetSweepGradientFillPipeline( ContentContextOptions opts) const { return GetPipeline(sweep_gradient_fill_pipelines_, opts); } - std::shared_ptr GetSolidFillPipeline( + std::shared_ptr> GetSolidFillPipeline( ContentContextOptions opts) const { return GetPipeline(solid_fill_pipelines_, opts); } - std::shared_ptr GetBlendPipeline(ContentContextOptions opts) const { + std::shared_ptr> GetBlendPipeline( + ContentContextOptions opts) const { return GetPipeline(texture_blend_pipelines_, opts); } - std::shared_ptr GetTexturePipeline( + std::shared_ptr> GetTexturePipeline( ContentContextOptions opts) const { return GetPipeline(texture_pipelines_, opts); } - std::shared_ptr GetTiledTexturePipeline( + std::shared_ptr> GetTiledTexturePipeline( ContentContextOptions opts) const { return GetPipeline(tiled_texture_pipelines_, opts); } - std::shared_ptr GetGaussianBlurPipeline( + std::shared_ptr> GetGaussianBlurPipeline( ContentContextOptions opts) const { return GetPipeline(gaussian_blur_pipelines_, opts); } - std::shared_ptr GetBorderMaskBlurPipeline( + std::shared_ptr> GetBorderMaskBlurPipeline( ContentContextOptions opts) const { return GetPipeline(border_mask_blur_pipelines_, opts); } - std::shared_ptr GetMorphologyFilterPipeline( + std::shared_ptr> GetMorphologyFilterPipeline( ContentContextOptions opts) const { return GetPipeline(morphology_filter_pipelines_, opts); } - std::shared_ptr GetColorMatrixColorFilterPipeline( - ContentContextOptions opts) const { + std::shared_ptr> + GetColorMatrixColorFilterPipeline(ContentContextOptions opts) const { return GetPipeline(color_matrix_color_filter_pipelines_, opts); } - std::shared_ptr GetLinearToSrgbFilterPipeline( + std::shared_ptr> GetLinearToSrgbFilterPipeline( ContentContextOptions opts) const { return GetPipeline(linear_to_srgb_filter_pipelines_, opts); } - std::shared_ptr GetSrgbToLinearFilterPipeline( + std::shared_ptr> GetSrgbToLinearFilterPipeline( ContentContextOptions opts) const { return GetPipeline(srgb_to_linear_filter_pipelines_, opts); } - std::shared_ptr GetSolidStrokePipeline( + std::shared_ptr> GetSolidStrokePipeline( ContentContextOptions opts) const { return GetPipeline(solid_stroke_pipelines_, opts); } - std::shared_ptr GetClipPipeline(ContentContextOptions opts) const { + std::shared_ptr> GetClipPipeline( + ContentContextOptions opts) const { return GetPipeline(clip_pipelines_, opts); } - std::shared_ptr GetGlyphAtlasPipeline( + std::shared_ptr> GetGlyphAtlasPipeline( ContentContextOptions opts) const { return GetPipeline(glyph_atlas_pipelines_, opts); } - std::shared_ptr GetVerticesPipeline( + std::shared_ptr> GetVerticesPipeline( ContentContextOptions opts) const { return GetPipeline(vertices_pipelines_, opts); } - std::shared_ptr GetAtlasPipeline(ContentContextOptions opts) const { + std::shared_ptr> GetAtlasPipeline( + ContentContextOptions opts) const { return GetPipeline(atlas_pipelines_, opts); } // Advanced blends. - std::shared_ptr GetBlendColorPipeline( + std::shared_ptr> GetBlendColorPipeline( ContentContextOptions opts) const { return GetPipeline(blend_color_pipelines_, opts); } - std::shared_ptr GetBlendColorBurnPipeline( + std::shared_ptr> GetBlendColorBurnPipeline( ContentContextOptions opts) const { return GetPipeline(blend_colorburn_pipelines_, opts); } - std::shared_ptr GetBlendColorDodgePipeline( + std::shared_ptr> GetBlendColorDodgePipeline( ContentContextOptions opts) const { return GetPipeline(blend_colordodge_pipelines_, opts); } - std::shared_ptr GetBlendDarkenPipeline( + std::shared_ptr> GetBlendDarkenPipeline( ContentContextOptions opts) const { return GetPipeline(blend_darken_pipelines_, opts); } - std::shared_ptr GetBlendDifferencePipeline( + std::shared_ptr> GetBlendDifferencePipeline( ContentContextOptions opts) const { return GetPipeline(blend_difference_pipelines_, opts); } - std::shared_ptr GetBlendExclusionPipeline( + std::shared_ptr> GetBlendExclusionPipeline( ContentContextOptions opts) const { return GetPipeline(blend_exclusion_pipelines_, opts); } - std::shared_ptr GetBlendHardLightPipeline( + std::shared_ptr> GetBlendHardLightPipeline( ContentContextOptions opts) const { return GetPipeline(blend_hardlight_pipelines_, opts); } - std::shared_ptr GetBlendHuePipeline( + std::shared_ptr> GetBlendHuePipeline( ContentContextOptions opts) const { return GetPipeline(blend_hue_pipelines_, opts); } - std::shared_ptr GetBlendLightenPipeline( + std::shared_ptr> GetBlendLightenPipeline( ContentContextOptions opts) const { return GetPipeline(blend_lighten_pipelines_, opts); } - std::shared_ptr GetBlendLuminosityPipeline( + std::shared_ptr> GetBlendLuminosityPipeline( ContentContextOptions opts) const { return GetPipeline(blend_luminosity_pipelines_, opts); } - std::shared_ptr GetBlendMultiplyPipeline( + std::shared_ptr> GetBlendMultiplyPipeline( ContentContextOptions opts) const { return GetPipeline(blend_multiply_pipelines_, opts); } - std::shared_ptr GetBlendOverlayPipeline( + std::shared_ptr> GetBlendOverlayPipeline( ContentContextOptions opts) const { return GetPipeline(blend_overlay_pipelines_, opts); } - std::shared_ptr GetBlendSaturationPipeline( + std::shared_ptr> GetBlendSaturationPipeline( ContentContextOptions opts) const { return GetPipeline(blend_saturation_pipelines_, opts); } - std::shared_ptr GetBlendScreenPipeline( + std::shared_ptr> GetBlendScreenPipeline( ContentContextOptions opts) const { return GetPipeline(blend_screen_pipelines_, opts); } - std::shared_ptr GetBlendSoftLightPipeline( + std::shared_ptr> GetBlendSoftLightPipeline( ContentContextOptions opts) const { return GetPipeline(blend_softlight_pipelines_, opts); } @@ -398,8 +418,9 @@ class ContentContext { mutable Variants blend_softlight_pipelines_; template - std::shared_ptr GetPipeline(Variants& container, - ContentContextOptions opts) const { + std::shared_ptr> GetPipeline( + Variants& container, + ContentContextOptions opts) const { if (!IsValid()) { return nullptr; } diff --git a/impeller/entity/contents/filters/blend_filter_contents.cc b/impeller/entity/contents/filters/blend_filter_contents.cc index bc115086d291c..c7d569e939a1e 100644 --- a/impeller/entity/contents/filters/blend_filter_contents.cc +++ b/impeller/entity/contents/filters/blend_filter_contents.cc @@ -24,8 +24,8 @@ BlendFilterContents::BlendFilterContents() { BlendFilterContents::~BlendFilterContents() = default; -using PipelineProc = - std::shared_ptr (ContentContext::*)(ContentContextOptions) const; +using PipelineProc = std::shared_ptr> ( + ContentContext::*)(ContentContextOptions) const; template static std::optional AdvancedBlend( @@ -93,7 +93,7 @@ static std::optional AdvancedBlend( auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); auto options = OptionsFromPassAndEntity(pass, entity); - std::shared_ptr pipeline = + std::shared_ptr> pipeline = std::invoke(pipeline_proc, renderer, options); Command cmd; diff --git a/impeller/entity/contents/filters/filter_contents.h b/impeller/entity/contents/filters/filter_contents.h index 7f1c21aedd3fc..a0c1e5996b577 100644 --- a/impeller/entity/contents/filters/filter_contents.h +++ b/impeller/entity/contents/filters/filter_contents.h @@ -16,8 +16,6 @@ namespace impeller { -class Pipeline; - class FilterContents : public Contents { public: enum class BlurStyle { diff --git a/impeller/fixtures/BUILD.gn b/impeller/fixtures/BUILD.gn index 0c6a3ae73c04d..a31b6045074f5 100644 --- a/impeller/fixtures/BUILD.gn +++ b/impeller/fixtures/BUILD.gn @@ -18,10 +18,15 @@ impeller_shaders("shader_fixtures") { "instanced_draw.vert", "mipmaps.frag", "mipmaps.vert", + "sample.comp", "simple.vert", "test_texture.frag", "test_texture.vert", ] + + if (impeller_enable_opengles) { + gles_exclusions = [ "sample.comp" ] + } } impellerc("runtime_stages") { diff --git a/impeller/fixtures/sample.comp b/impeller/fixtures/sample.comp index 4e76c29968958..f7ffc47179c6f 100644 --- a/impeller/fixtures/sample.comp +++ b/impeller/fixtures/sample.comp @@ -1,5 +1,20 @@ -layout (local_size_x = 16, local_size_y = 16) in; +layout(local_size_x = 128) in; +layout(std430) buffer; -void main(void) { - // Do nothing. +layout(binding = 0) writeonly buffer Output { + vec4 elements[]; +} output_data; + +layout(binding = 1) readonly buffer Input0 { + vec4 elements[]; +} input_data0; + +layout(binding = 2) readonly buffer Input1 { + vec4 elements[]; +} input_data1; + +void main() +{ + uint ident = gl_GlobalInvocationID.x; + output_data.elements[ident] = input_data0.elements[ident] * input_data1.elements[ident]; } diff --git a/impeller/playground/imgui/imgui_impl_impeller.cc b/impeller/playground/imgui/imgui_impl_impeller.cc index b8997b0f1bcf8..d5a0a9851514f 100644 --- a/impeller/playground/imgui/imgui_impl_impeller.cc +++ b/impeller/playground/imgui/imgui_impl_impeller.cc @@ -24,6 +24,7 @@ #include "impeller/renderer/context.h" #include "impeller/renderer/formats.h" #include "impeller/renderer/pipeline_builder.h" +#include "impeller/renderer/pipeline_descriptor.h" #include "impeller/renderer/pipeline_library.h" #include "impeller/renderer/range.h" #include "impeller/renderer/render_pass.h" @@ -36,7 +37,7 @@ struct ImGui_ImplImpeller_Data { std::shared_ptr context; std::shared_ptr font_texture; - std::shared_ptr pipeline; + std::shared_ptr> pipeline; std::shared_ptr sampler; }; @@ -101,7 +102,7 @@ bool ImGui_ImplImpeller_Init(std::shared_ptr context) { } bd->pipeline = - context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); + context->GetPipelineLibrary()->GetPipeline(std::move(desc)).get(); IM_ASSERT(bd->pipeline != nullptr && "Could not create ImGui pipeline."); bd->sampler = context->GetSamplerLibrary()->GetSampler({}); diff --git a/impeller/renderer/BUILD.gn b/impeller/renderer/BUILD.gn index 224631423e559..c3aec4256611b 100644 --- a/impeller/renderer/BUILD.gn +++ b/impeller/renderer/BUILD.gn @@ -20,6 +20,14 @@ impeller_component("renderer") { "command.h", "command_buffer.cc", "command_buffer.h", + "compute_command.cc", + "compute_command.h", + "compute_pass.cc", + "compute_pass.h", + "compute_pipeline_builder.cc", + "compute_pipeline_builder.h", + "compute_pipeline_descriptor.cc", + "compute_pipeline_descriptor.h", "context.cc", "context.h", "descriptor_set_layout.h", diff --git a/impeller/renderer/backend/gles/command_buffer_gles.cc b/impeller/renderer/backend/gles/command_buffer_gles.cc index f6452528995c5..a2038b313bcf2 100644 --- a/impeller/renderer/backend/gles/command_buffer_gles.cc +++ b/impeller/renderer/backend/gles/command_buffer_gles.cc @@ -64,4 +64,11 @@ std::shared_ptr CommandBufferGLES::OnCreateBlitPass() const { return pass; } +// |CommandBuffer| +std::shared_ptr CommandBufferGLES::OnCreateComputePass() const { + // Compute passes aren't supported until GLES 3.2, at which point Vulkan is + // available anyway. + return nullptr; +} + } // namespace impeller diff --git a/impeller/renderer/backend/gles/command_buffer_gles.h b/impeller/renderer/backend/gles/command_buffer_gles.h index a83bab052c03d..88835f2ba1eb8 100644 --- a/impeller/renderer/backend/gles/command_buffer_gles.h +++ b/impeller/renderer/backend/gles/command_buffer_gles.h @@ -41,6 +41,9 @@ class CommandBufferGLES final : public CommandBuffer { // |CommandBuffer| std::shared_ptr OnCreateBlitPass() const override; + // |CommandBuffer| + std::shared_ptr OnCreateComputePass() const override; + FML_DISALLOW_COPY_AND_ASSIGN(CommandBufferGLES); }; diff --git a/impeller/renderer/backend/gles/pipeline_gles.h b/impeller/renderer/backend/gles/pipeline_gles.h index f0e7e21d48119..19d7d837a753b 100644 --- a/impeller/renderer/backend/gles/pipeline_gles.h +++ b/impeller/renderer/backend/gles/pipeline_gles.h @@ -15,8 +15,9 @@ namespace impeller { class PipelineLibraryGLES; -class PipelineGLES final : public Pipeline, - public BackendCast { +class PipelineGLES final + : public Pipeline, + public BackendCast> { public: // |Pipeline| ~PipelineGLES() override; diff --git a/impeller/renderer/backend/gles/pipeline_library_gles.cc b/impeller/renderer/backend/gles/pipeline_library_gles.cc index 0d9d4bc8b34c9..fa4bf10049f99 100644 --- a/impeller/renderer/backend/gles/pipeline_library_gles.cc +++ b/impeller/renderer/backend/gles/pipeline_library_gles.cc @@ -172,14 +172,15 @@ bool PipelineLibraryGLES::IsValid() const { } // |PipelineLibrary| -PipelineFuture PipelineLibraryGLES::GetRenderPipeline( +PipelineFuture PipelineLibraryGLES::GetPipeline( PipelineDescriptor descriptor) { if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) { return found->second; } if (!reactor_) { - return RealizedFuture>(nullptr); + return RealizedFuture>>( + nullptr); } auto vert_function = descriptor.GetEntrypointForStage(ShaderStage::kVertex); @@ -188,11 +189,13 @@ PipelineFuture PipelineLibraryGLES::GetRenderPipeline( if (!vert_function || !frag_function) { VALIDATION_LOG << "Could not find stage entrypoint functions in pipeline descriptor."; - return RealizedFuture>(nullptr); + return RealizedFuture>>( + nullptr); } - auto promise = std::make_shared>>(); - auto future = PipelineFuture{promise->get_future()}; + auto promise = std::make_shared< + std::promise>>>(); + auto future = PipelineFuture{promise->get_future()}; pipelines_[descriptor] = future; auto weak_this = weak_from_this(); @@ -242,6 +245,18 @@ PipelineFuture PipelineLibraryGLES::GetRenderPipeline( return future; } +// |PipelineLibrary| +PipelineFuture PipelineLibraryGLES::GetPipeline( + ComputePipelineDescriptor descriptor) { + auto promise = std::make_shared< + std::promise>>>(); + auto future = + PipelineFuture{promise->get_future()}; + // TODO(dnfield): implement compute for GLES. + promise->set_value(nullptr); + return future; +} + // |PipelineLibrary| PipelineLibraryGLES::~PipelineLibraryGLES() = default; diff --git a/impeller/renderer/backend/gles/pipeline_library_gles.h b/impeller/renderer/backend/gles/pipeline_library_gles.h index 0ac23490128cf..2348fb89dc391 100644 --- a/impeller/renderer/backend/gles/pipeline_library_gles.h +++ b/impeller/renderer/backend/gles/pipeline_library_gles.h @@ -29,7 +29,12 @@ class PipelineLibraryGLES final : public PipelineLibrary { bool IsValid() const override; // |PipelineLibrary| - PipelineFuture GetRenderPipeline(PipelineDescriptor descriptor) override; + PipelineFuture GetPipeline( + PipelineDescriptor descriptor) override; + + // |PipelineLibrary| + PipelineFuture GetPipeline( + ComputePipelineDescriptor descriptor) override; FML_DISALLOW_COPY_AND_ASSIGN(PipelineLibraryGLES); }; diff --git a/impeller/renderer/backend/gles/shader_library_gles.cc b/impeller/renderer/backend/gles/shader_library_gles.cc index b20e46d160f90..0bba3aefebd87 100644 --- a/impeller/renderer/backend/gles/shader_library_gles.cc +++ b/impeller/renderer/backend/gles/shader_library_gles.cc @@ -20,6 +20,8 @@ static ShaderStage ToShaderStage(BlobShaderType type) { return ShaderStage::kVertex; case BlobShaderType::kFragment: return ShaderStage::kFragment; + case BlobShaderType::kCompute: + return ShaderStage::kCompute; } FML_UNREACHABLE(); } diff --git a/impeller/renderer/backend/metal/BUILD.gn b/impeller/renderer/backend/metal/BUILD.gn index 8a1fe8eed2c99..7ad03e9cea138 100644 --- a/impeller/renderer/backend/metal/BUILD.gn +++ b/impeller/renderer/backend/metal/BUILD.gn @@ -14,6 +14,10 @@ impeller_component("metal") { "blit_pass_mtl.mm", "command_buffer_mtl.h", "command_buffer_mtl.mm", + "compute_pass_mtl.h", + "compute_pass_mtl.mm", + "compute_pipeline_mtl.h", + "compute_pipeline_mtl.mm", "context_mtl.h", "context_mtl.mm", "device_buffer_mtl.h", diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.h b/impeller/renderer/backend/metal/command_buffer_mtl.h index e4e09609657a8..eb88a7bc537ca 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.h +++ b/impeller/renderer/backend/metal/command_buffer_mtl.h @@ -40,6 +40,9 @@ class CommandBufferMTL final : public CommandBuffer { // |CommandBuffer| std::shared_ptr OnCreateBlitPass() const override; + // |CommandBuffer| + std::shared_ptr OnCreateComputePass() const override; + FML_DISALLOW_COPY_AND_ASSIGN(CommandBufferMTL); }; diff --git a/impeller/renderer/backend/metal/command_buffer_mtl.mm b/impeller/renderer/backend/metal/command_buffer_mtl.mm index 4a4adc116ac99..8d461692a8d4f 100644 --- a/impeller/renderer/backend/metal/command_buffer_mtl.mm +++ b/impeller/renderer/backend/metal/command_buffer_mtl.mm @@ -5,6 +5,7 @@ #include "impeller/renderer/backend/metal/command_buffer_mtl.h" #include "impeller/renderer/backend/metal/blit_pass_mtl.h" +#include "impeller/renderer/backend/metal/compute_pass_mtl.h" #include "impeller/renderer/backend/metal/render_pass_mtl.h" namespace impeller { @@ -196,4 +197,18 @@ static bool LogMTLCommandBufferErrorIfPresent(id buffer) { return pass; } +std::shared_ptr CommandBufferMTL::OnCreateComputePass() const { + if (!buffer_) { + return nullptr; + } + + auto pass = + std::shared_ptr(new ComputePassMTL(context_, buffer_)); + if (!pass->IsValid()) { + return nullptr; + } + + return pass; +} + } // namespace impeller diff --git a/impeller/renderer/backend/metal/compute_pass_mtl.h b/impeller/renderer/backend/metal/compute_pass_mtl.h new file mode 100644 index 0000000000000..0a2db5798d444 --- /dev/null +++ b/impeller/renderer/backend/metal/compute_pass_mtl.h @@ -0,0 +1,44 @@ +// 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. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/renderer/compute_pass.h" + +namespace impeller { + +class ComputePassMTL final : public ComputePass { + public: + // |RenderPass| + ~ComputePassMTL() override; + + private: + friend class CommandBufferMTL; + + id buffer_ = nil; + std::string label_; + bool is_valid_ = false; + + ComputePassMTL(std::weak_ptr context, + id buffer); + + // |ComputePass| + bool IsValid() const override; + + // |ComputePass| + void OnSetLabel(std::string label) override; + + // |ComputePass| + bool OnEncodeCommands(const Context& context) const override; + + bool EncodeCommands(const std::shared_ptr& allocator, + id pass) const; + + FML_DISALLOW_COPY_AND_ASSIGN(ComputePassMTL); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/compute_pass_mtl.mm b/impeller/renderer/backend/metal/compute_pass_mtl.mm new file mode 100644 index 0000000000000..221c93e1a6d75 --- /dev/null +++ b/impeller/renderer/backend/metal/compute_pass_mtl.mm @@ -0,0 +1,252 @@ +// 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. + +#include "impeller/renderer/backend/metal/compute_pass_mtl.h" + +#include +#include +#include + +#include "flutter/fml/backtrace.h" +#include "flutter/fml/closure.h" +#include "flutter/fml/logging.h" +#include "flutter/fml/trace_event.h" +#include "impeller/base/backend_cast.h" +#include "impeller/renderer/backend/metal/compute_pipeline_mtl.h" +#include "impeller/renderer/backend/metal/device_buffer_mtl.h" +#include "impeller/renderer/backend/metal/formats_mtl.h" +#include "impeller/renderer/backend/metal/sampler_mtl.h" +#include "impeller/renderer/backend/metal/texture_mtl.h" +#include "impeller/renderer/compute_command.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/host_buffer.h" +#include "impeller/renderer/shader_types.h" + +namespace impeller { + +ComputePassMTL::ComputePassMTL(std::weak_ptr context, + id buffer) + : ComputePass(std::move(context)), buffer_(buffer) { + if (!buffer_) { + return; + } + is_valid_ = true; +} + +ComputePassMTL::~ComputePassMTL() = default; + +bool ComputePassMTL::IsValid() const { + return is_valid_; +} + +void ComputePassMTL::OnSetLabel(std::string label) { + if (label.empty()) { + return; + } + label_ = std::move(label); +} + +bool ComputePassMTL::OnEncodeCommands(const Context& context) const { + TRACE_EVENT0("impeller", "ComputePassMTL::EncodeCommands"); + if (!IsValid()) { + return false; + } + + // TODO(dnfield): Support non-serial dispatch type on higher iOS versions. + auto compute_command_encoder = [buffer_ computeCommandEncoder]; + + if (!compute_command_encoder) { + return false; + } + + if (!label_.empty()) { + [compute_command_encoder setLabel:@(label_.c_str())]; + } + + // Success or failure, the pass must end. The buffer can only process one pass + // at a time. + fml::ScopedCleanupClosure auto_end( + [compute_command_encoder]() { [compute_command_encoder endEncoding]; }); + + return EncodeCommands(context.GetResourceAllocator(), + compute_command_encoder); +} + +//----------------------------------------------------------------------------- +/// @brief Ensures that bindings on the pass are not redundantly set or +/// updated. Avoids making the driver do additional checks and makes +/// the frame insights during profiling and instrumentation not +/// complain about the same. +/// +/// There should be no change to rendering if this caching was +/// absent. +/// +struct ComputePassBindingsCache { + explicit ComputePassBindingsCache(id encoder) + : encoder_(encoder) {} + + ComputePassBindingsCache(const ComputePassBindingsCache&) = delete; + + ComputePassBindingsCache(ComputePassBindingsCache&&) = delete; + + void SetComputePipelineState(id pipeline) { + if (pipeline == pipeline_) { + return; + } + pipeline_ = pipeline; + [encoder_ setComputePipelineState:pipeline_]; + } + + void SetBuffer(uint64_t index, uint64_t offset, id buffer) { + auto found = buffers_.find(index); + if (found != buffers_.end() && found->second.buffer == buffer) { + // The right buffer is bound. Check if its offset needs to be updated. + if (found->second.offset == offset) { + // Buffer and its offset is identical. Nothing to do. + return; + } + + // Only the offset needs to be updated. + found->second.offset = offset; + + [encoder_ setBufferOffset:offset atIndex:index]; + return; + } + + buffers_[index] = {buffer, static_cast(offset)}; + [encoder_ setBuffer:buffer offset:offset atIndex:index]; + } + + void SetTexture(uint64_t index, id texture) { + auto found = textures_.find(index); + if (found != textures_.end() && found->second == texture) { + // Already bound. + return; + } + textures_[index] = texture; + [encoder_ setTexture:texture atIndex:index]; + return; + } + + void SetSampler(uint64_t index, id sampler) { + auto found = samplers_.find(index); + if (found != samplers_.end() && found->second == sampler) { + // Already bound. + return; + } + samplers_[index] = sampler; + [encoder_ setSamplerState:sampler atIndex:index]; + return; + } + + private: + struct BufferOffsetPair { + id buffer = nullptr; + size_t offset = 0u; + }; + using BufferMap = std::map; + using TextureMap = std::map>; + using SamplerMap = std::map>; + + const id encoder_; + id pipeline_ = nullptr; + BufferMap buffers_; + TextureMap textures_; + SamplerMap samplers_; +}; + +static bool Bind(ComputePassBindingsCache& pass, + Allocator& allocator, + size_t bind_index, + const BufferView& view) { + if (!view.buffer) { + return false; + } + + auto device_buffer = view.buffer->GetDeviceBuffer(allocator); + if (!device_buffer) { + return false; + } + + auto buffer = DeviceBufferMTL::Cast(*device_buffer).GetMTLBuffer(); + // The Metal call is a void return and we don't want to make it on nil. + if (!buffer) { + return false; + } + + pass.SetBuffer(bind_index, view.range.offset, buffer); + return true; +} + +static bool Bind(ComputePassBindingsCache& pass, + size_t bind_index, + const Texture& texture) { + if (!texture.IsValid()) { + return false; + } + + pass.SetTexture(bind_index, TextureMTL::Cast(texture).GetMTLTexture()); + return true; +} + +static bool Bind(ComputePassBindingsCache& pass, + size_t bind_index, + const Sampler& sampler) { + if (!sampler.IsValid()) { + return false; + } + + pass.SetSampler(bind_index, SamplerMTL::Cast(sampler).GetMTLSamplerState()); + return true; +} + +bool ComputePassMTL::EncodeCommands( + const std::shared_ptr& allocator, + id encoder) const { + ComputePassBindingsCache pass_bindings(encoder); + + fml::closure pop_debug_marker = [encoder]() { [encoder popDebugGroup]; }; + for (const auto& command : commands_) { + fml::ScopedCleanupClosure auto_pop_debug_marker(pop_debug_marker); + if (!command.label.empty()) { + [encoder pushDebugGroup:@(command.label.c_str())]; + } else { + auto_pop_debug_marker.Release(); + } + + // TODO(dnfield): update the compute pipeline descriptor so that it can set + // things like workgroup size etc. + // https://github.com/flutter/flutter/issues/110618 + pass_bindings.SetComputePipelineState( + ComputePipelineMTL::Cast(*command.pipeline) + .GetMTLComputePipelineState()); + + for (const auto& buffer : command.bindings.buffers) { + if (!Bind(pass_bindings, *allocator, buffer.first, + buffer.second.resource)) { + return false; + } + } + + for (const auto& texture : command.bindings.textures) { + if (!Bind(pass_bindings, texture.first, *texture.second.resource)) { + return false; + } + } + for (const auto& sampler : command.bindings.samplers) { + if (!Bind(pass_bindings, sampler.first, *sampler.second.resource)) { + return false; + } + } + } + // TODO(dnfield): use feature detection to support non-uniform threadgroup + // sizes. + // https://github.com/flutter/flutter/issues/110619 + [encoder dispatchThreadgroups:MTLSizeMake(32, 32, 1) + threadsPerThreadgroup:MTLSizeMake(32, 32, 1)]; + + return true; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/compute_pipeline_mtl.h b/impeller/renderer/backend/metal/compute_pipeline_mtl.h new file mode 100644 index 0000000000000..50c33b44f15ce --- /dev/null +++ b/impeller/renderer/backend/metal/compute_pipeline_mtl.h @@ -0,0 +1,41 @@ +// 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. + +#pragma once + +#include + +#include "flutter/fml/macros.h" +#include "impeller/base/backend_cast.h" +#include "impeller/renderer/pipeline.h" + +namespace impeller { + +class ComputePipelineMTL final + : public Pipeline, + public BackendCast> { + public: + // |Pipeline| + ~ComputePipelineMTL() override; + + id GetMTLComputePipelineState() const; + + private: + friend class PipelineLibraryMTL; + + id pipeline_state_; + bool is_valid_ = false; + + ComputePipelineMTL(std::weak_ptr library, + ComputePipelineDescriptor desc, + id state); + + // |Pipeline| + bool IsValid() const override; + + FML_DISALLOW_COPY_AND_ASSIGN(ComputePipelineMTL); +}; + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/compute_pipeline_mtl.mm b/impeller/renderer/backend/metal/compute_pipeline_mtl.mm new file mode 100644 index 0000000000000..b1d38977c9604 --- /dev/null +++ b/impeller/renderer/backend/metal/compute_pipeline_mtl.mm @@ -0,0 +1,30 @@ +// 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. + +#include "impeller/renderer/backend/metal/compute_pipeline_mtl.h" + +namespace impeller { + +ComputePipelineMTL::ComputePipelineMTL(std::weak_ptr library, + ComputePipelineDescriptor desc, + id state) + : Pipeline(std::move(library), std::move(desc)), pipeline_state_(state) { + if (!pipeline_state_) { + return; + } + is_valid_ = true; +} + +ComputePipelineMTL::~ComputePipelineMTL() = default; + +bool ComputePipelineMTL::IsValid() const { + return is_valid_; +} + +id ComputePipelineMTL::GetMTLComputePipelineState() + const { + return pipeline_state_; +} + +} // namespace impeller diff --git a/impeller/renderer/backend/metal/pipeline_library_mtl.h b/impeller/renderer/backend/metal/pipeline_library_mtl.h index 0ac4eab33b7a3..bdfb7a2d55f68 100644 --- a/impeller/renderer/backend/metal/pipeline_library_mtl.h +++ b/impeller/renderer/backend/metal/pipeline_library_mtl.h @@ -25,6 +25,7 @@ class PipelineLibraryMTL final : public PipelineLibrary { id device_ = nullptr; PipelineMap pipelines_; + ComputePipelineMap compute_pipelines_; PipelineLibraryMTL(id device); @@ -32,7 +33,12 @@ class PipelineLibraryMTL final : public PipelineLibrary { bool IsValid() const override; // |PipelineLibrary| - PipelineFuture GetRenderPipeline(PipelineDescriptor descriptor) override; + PipelineFuture GetPipeline( + PipelineDescriptor descriptor) override; + + // |PipelineLibrary| + PipelineFuture GetPipeline( + ComputePipelineDescriptor descriptor) override; FML_DISALLOW_COPY_AND_ASSIGN(PipelineLibraryMTL); }; diff --git a/impeller/renderer/backend/metal/pipeline_library_mtl.mm b/impeller/renderer/backend/metal/pipeline_library_mtl.mm index 779908aed60e7..713fffee42d82 100644 --- a/impeller/renderer/backend/metal/pipeline_library_mtl.mm +++ b/impeller/renderer/backend/metal/pipeline_library_mtl.mm @@ -3,8 +3,10 @@ // found in the LICENSE file. #include "impeller/renderer/backend/metal/pipeline_library_mtl.h" +#include #include "impeller/base/promise.h" +#include "impeller/renderer/backend/metal/compute_pipeline_mtl.h" #include "impeller/renderer/backend/metal/formats_mtl.h" #include "impeller/renderer/backend/metal/pipeline_mtl.h" #include "impeller/renderer/backend/metal/shader_function_mtl.h" @@ -56,6 +58,15 @@ return descriptor; } +static MTLComputePipelineDescriptor* GetMTLComputePipelineDescriptor( + const ComputePipelineDescriptor& desc) { + auto descriptor = [[MTLComputePipelineDescriptor alloc] init]; + descriptor.label = @(desc.GetLabel().c_str()); + descriptor.computeFunction = + ShaderFunctionMTL::Cast(*desc.GetStageEntrypoint()).GetMTLFunction(); + return descriptor; +} + // TODO(csg): Make PipelineDescriptor a struct and move this to formats_mtl. static id CreateDepthStencilDescriptor( const PipelineDescriptor& desc, @@ -74,18 +85,20 @@ } // |PipelineLibrary| -PipelineFuture PipelineLibraryMTL::GetRenderPipeline( +PipelineFuture PipelineLibraryMTL::GetPipeline( PipelineDescriptor descriptor) { if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) { return found->second; } if (!IsValid()) { - return RealizedFuture>(nullptr); + return RealizedFuture>>( + nullptr); } - auto promise = std::make_shared>>(); - auto future = PipelineFuture{promise->get_future()}; + auto promise = std::make_shared< + std::promise>>>(); + auto future = PipelineFuture{promise->get_future()}; pipelines_[descriptor] = future; auto weak_this = weak_from_this(); @@ -121,4 +134,57 @@ return future; } +PipelineFuture PipelineLibraryMTL::GetPipeline( + ComputePipelineDescriptor descriptor) { + if (auto found = compute_pipelines_.find(descriptor); + found != compute_pipelines_.end()) { + return found->second; + } + + if (!IsValid()) { + return RealizedFuture>>( + nullptr); + } + + auto promise = std::make_shared< + std::promise>>>(); + auto future = + PipelineFuture{promise->get_future()}; + compute_pipelines_[descriptor] = future; + auto weak_this = weak_from_this(); + + auto completion_handler = + ^(id _Nullable compute_pipeline_state, + MTLComputePipelineReflection* _Nullable reflection, + NSError* _Nullable error) { + if (error != nil) { + VALIDATION_LOG << "Could not create compute pipeline: " + << error.localizedDescription.UTF8String; + promise->set_value(nullptr); + return; + } + + auto strong_this = weak_this.lock(); + if (!strong_this) { + VALIDATION_LOG << "Library was collected before a pending pipeline " + "creation could finish."; + promise->set_value(nullptr); + return; + } + + auto new_pipeline = std::shared_ptr( + new ComputePipelineMTL(weak_this, + descriptor, // + compute_pipeline_state // + )); + promise->set_value(new_pipeline); + }; + [device_ + newComputePipelineStateWithDescriptor:GetMTLComputePipelineDescriptor( + descriptor) + options:MTLPipelineOptionNone + completionHandler:completion_handler]; + return future; +} + } // namespace impeller diff --git a/impeller/renderer/backend/metal/pipeline_mtl.h b/impeller/renderer/backend/metal/pipeline_mtl.h index 8b0a8b0e150ad..b57c972d9b1e0 100644 --- a/impeller/renderer/backend/metal/pipeline_mtl.h +++ b/impeller/renderer/backend/metal/pipeline_mtl.h @@ -12,8 +12,9 @@ namespace impeller { -class PipelineMTL final : public Pipeline, - public BackendCast { +class PipelineMTL final + : public Pipeline, + public BackendCast> { public: // |Pipeline| ~PipelineMTL() override; diff --git a/impeller/renderer/backend/vulkan/command_buffer_vk.cc b/impeller/renderer/backend/vulkan/command_buffer_vk.cc index c06701d515f18..572826c7aa97e 100644 --- a/impeller/renderer/backend/vulkan/command_buffer_vk.cc +++ b/impeller/renderer/backend/vulkan/command_buffer_vk.cc @@ -133,4 +133,10 @@ std::shared_ptr CommandBufferVK::OnCreateBlitPass() const { FML_UNREACHABLE(); } +std::shared_ptr CommandBufferVK::OnCreateComputePass() const { + // TODO(dnfield): https://github.com/flutter/flutter/issues/110622 + VALIDATION_LOG << "ComputePasses unimplemented for Vulkan"; + return nullptr; +} + } // namespace impeller diff --git a/impeller/renderer/backend/vulkan/command_buffer_vk.h b/impeller/renderer/backend/vulkan/command_buffer_vk.h index ee5cb0bfec2b0..5d42223bf255e 100644 --- a/impeller/renderer/backend/vulkan/command_buffer_vk.h +++ b/impeller/renderer/backend/vulkan/command_buffer_vk.h @@ -51,6 +51,9 @@ class CommandBufferVK final : public CommandBuffer { // |CommandBuffer| std::shared_ptr OnCreateBlitPass() const override; + // |CommandBuffer| + std::shared_ptr OnCreateComputePass() const override; + FML_DISALLOW_COPY_AND_ASSIGN(CommandBufferVK); }; diff --git a/impeller/renderer/backend/vulkan/pipeline_library_vk.cc b/impeller/renderer/backend/vulkan/pipeline_library_vk.cc index 931181c23b373..2c92d56f45808 100644 --- a/impeller/renderer/backend/vulkan/pipeline_library_vk.cc +++ b/impeller/renderer/backend/vulkan/pipeline_library_vk.cc @@ -51,7 +51,7 @@ bool PipelineLibraryVK::IsValid() const { } // |PipelineLibrary| -PipelineFuture PipelineLibraryVK::GetRenderPipeline( +PipelineFuture PipelineLibraryVK::GetPipeline( PipelineDescriptor descriptor) { Lock lock(pipelines_mutex_); if (auto found = pipelines_.find(descriptor); found != pipelines_.end()) { @@ -59,11 +59,13 @@ PipelineFuture PipelineLibraryVK::GetRenderPipeline( } if (!IsValid()) { - return RealizedFuture>(nullptr); + return RealizedFuture>>( + nullptr); } - auto promise = std::make_shared>>(); - auto future = PipelineFuture{promise->get_future()}; + auto promise = std::make_shared< + std::promise>>>(); + auto future = PipelineFuture{promise->get_future()}; pipelines_[descriptor] = future; auto weak_this = weak_from_this(); @@ -85,6 +87,18 @@ PipelineFuture PipelineLibraryVK::GetRenderPipeline( return future; } +// |PipelineLibrary| +PipelineFuture PipelineLibraryVK::GetPipeline( + ComputePipelineDescriptor descriptor) { + auto promise = std::make_shared< + std::promise>>>(); + auto future = + PipelineFuture{promise->get_future()}; + // TODO(dnfield): implement compute for GLES. + promise->set_value(nullptr); + return future; +} + static vk::AttachmentDescription CreatePlaceholderAttachmentDescription( vk::Format format, SampleCount sample_count) { diff --git a/impeller/renderer/backend/vulkan/pipeline_library_vk.h b/impeller/renderer/backend/vulkan/pipeline_library_vk.h index 9ec08fb865dca..09a59e10a5f56 100644 --- a/impeller/renderer/backend/vulkan/pipeline_library_vk.h +++ b/impeller/renderer/backend/vulkan/pipeline_library_vk.h @@ -51,7 +51,12 @@ class PipelineLibraryVK final bool IsValid() const override; // |PipelineLibrary| - PipelineFuture GetRenderPipeline(PipelineDescriptor descriptor) override; + PipelineFuture GetPipeline( + PipelineDescriptor descriptor) override; + + // |PipelineLibrary| + PipelineFuture GetPipeline( + ComputePipelineDescriptor descriptor) override; std::unique_ptr CreatePipeline( const PipelineDescriptor& desc); diff --git a/impeller/renderer/backend/vulkan/pipeline_vk.h b/impeller/renderer/backend/vulkan/pipeline_vk.h index 3a0b9e127c710..1f8011c3ec5da 100644 --- a/impeller/renderer/backend/vulkan/pipeline_vk.h +++ b/impeller/renderer/backend/vulkan/pipeline_vk.h @@ -30,8 +30,9 @@ class PipelineCreateInfoVK { vk::UniqueRenderPass render_pass_; }; -class PipelineVK final : public Pipeline, - public BackendCast { +class PipelineVK final + : public Pipeline, + public BackendCast> { public: PipelineVK(std::weak_ptr library, PipelineDescriptor desc, diff --git a/impeller/renderer/backend/vulkan/shader_library_vk.cc b/impeller/renderer/backend/vulkan/shader_library_vk.cc index e7bb8ec661c82..3e8c40cac3010 100644 --- a/impeller/renderer/backend/vulkan/shader_library_vk.cc +++ b/impeller/renderer/backend/vulkan/shader_library_vk.cc @@ -17,6 +17,8 @@ static ShaderStage ToShaderStage(BlobShaderType type) { return ShaderStage::kVertex; case BlobShaderType::kFragment: return ShaderStage::kFragment; + case BlobShaderType::kCompute: + return ShaderStage::kCompute; } FML_UNREACHABLE(); } diff --git a/impeller/renderer/command.cc b/impeller/renderer/command.cc index c8904ef5fedd4..85165759551ed 100644 --- a/impeller/renderer/command.cc +++ b/impeller/renderer/command.cc @@ -48,9 +48,10 @@ bool Command::BindResource(ShaderStage stage, case ShaderStage::kFragment: fragment_bindings.buffers[slot.binding] = {&metadata, view}; return true; + case ShaderStage::kCompute: + VALIDATION_LOG << "Use ComputeCommands for compute shader stages."; case ShaderStage::kTessellationControl: case ShaderStage::kTessellationEvaluation: - case ShaderStage::kCompute: case ShaderStage::kUnknown: return false; } @@ -77,9 +78,10 @@ bool Command::BindResource(ShaderStage stage, case ShaderStage::kFragment: fragment_bindings.textures[slot.texture_index] = {&metadata, texture}; return true; + case ShaderStage::kCompute: + VALIDATION_LOG << "Use ComputeCommands for compute shader stages."; case ShaderStage::kTessellationControl: case ShaderStage::kTessellationEvaluation: - case ShaderStage::kCompute: case ShaderStage::kUnknown: return false; } @@ -106,10 +108,11 @@ bool Command::BindResource(ShaderStage stage, case ShaderStage::kFragment: fragment_bindings.samplers[slot.sampler_index] = {&metadata, sampler}; return true; + case ShaderStage::kCompute: + VALIDATION_LOG << "Use ComputeCommands for compute shader stages."; case ShaderStage::kUnknown: case ShaderStage::kTessellationControl: case ShaderStage::kTessellationEvaluation: - case ShaderStage::kCompute: return false; } diff --git a/impeller/renderer/command.h b/impeller/renderer/command.h index 5ea699dccbdf0..56e99a0d85343 100644 --- a/impeller/renderer/command.h +++ b/impeller/renderer/command.h @@ -65,7 +65,7 @@ struct Command { //---------------------------------------------------------------------------- /// The pipeline to use for this command. /// - std::shared_ptr pipeline; + std::shared_ptr> pipeline; //---------------------------------------------------------------------------- /// The buffer, texture, and sampler bindings used by the vertex pipeline /// stage. diff --git a/impeller/renderer/command_buffer.cc b/impeller/renderer/command_buffer.cc index 6429afaf3131a..81f5f008c1d55 100644 --- a/impeller/renderer/command_buffer.cc +++ b/impeller/renderer/command_buffer.cc @@ -5,6 +5,7 @@ #include "impeller/renderer/command_buffer.h" #include "flutter/fml/trace_event.h" +#include "impeller/renderer/compute_pass.h" #include "impeller/renderer/render_pass.h" #include "impeller/renderer/render_target.h" @@ -50,4 +51,16 @@ std::shared_ptr CommandBuffer::CreateBlitPass() const { return nullptr; } +std::shared_ptr CommandBuffer::CreateComputePass() const { + if (!IsValid()) { + return nullptr; + } + auto pass = OnCreateComputePass(); + if (pass && pass->IsValid()) { + pass->SetLabel("ComputePass"); + return pass; + } + return nullptr; +} + } // namespace impeller diff --git a/impeller/renderer/command_buffer.h b/impeller/renderer/command_buffer.h index 28158e15561ad..b627f35a9ecce 100644 --- a/impeller/renderer/command_buffer.h +++ b/impeller/renderer/command_buffer.h @@ -9,9 +9,11 @@ #include "flutter/fml/macros.h" #include "impeller/renderer/blit_pass.h" +#include "impeller/renderer/compute_pass.h" namespace impeller { +class ComputePass; class Context; class RenderPass; class RenderTarget; @@ -81,6 +83,13 @@ class CommandBuffer { /// std::shared_ptr CreateBlitPass() const; + //---------------------------------------------------------------------------- + /// @brief Create a compute pass to record compute commands into. + /// + /// @return A valid compute pass or null. + /// + std::shared_ptr CreateComputePass() const; + protected: std::weak_ptr context_; @@ -93,6 +102,8 @@ class CommandBuffer { [[nodiscard]] virtual bool OnSubmitCommands(CompletionCallback callback) = 0; + virtual std::shared_ptr OnCreateComputePass() const = 0; + private: FML_DISALLOW_COPY_AND_ASSIGN(CommandBuffer); }; diff --git a/impeller/renderer/compute_command.cc b/impeller/renderer/compute_command.cc new file mode 100644 index 0000000000000..e20d564a47d0b --- /dev/null +++ b/impeller/renderer/compute_command.cc @@ -0,0 +1,82 @@ +// 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. + +#include "impeller/renderer/compute_command.h" + +#include "impeller/base/validation.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/vertex_descriptor.h" + +namespace impeller { + +bool ComputeCommand::BindResource(ShaderStage stage, + const ShaderUniformSlot& slot, + const ShaderMetadata& metadata, + BufferView view) { + if (stage != ShaderStage::kCompute) { + VALIDATION_LOG << "Use Command for non-compute shader stages."; + return false; + } + if (!view) { + return false; + } + + bindings.buffers[slot.binding] = {&metadata, view}; + return true; +} + +bool ComputeCommand::BindResource(ShaderStage stage, + const SampledImageSlot& slot, + const ShaderMetadata& metadata, + std::shared_ptr texture) { + if (stage != ShaderStage::kCompute) { + VALIDATION_LOG << "Use Command for non-compute shader stages."; + return false; + } + if (!texture || !texture->IsValid()) { + return false; + } + + if (!slot.HasTexture()) { + return true; + } + + bindings.textures[slot.texture_index] = {&metadata, texture}; + return true; +} + +bool ComputeCommand::BindResource(ShaderStage stage, + const SampledImageSlot& slot, + const ShaderMetadata& metadata, + std::shared_ptr sampler) { + if (stage != ShaderStage::kCompute) { + VALIDATION_LOG << "Use Command for non-compute shader stages."; + return false; + } + if (!sampler || !sampler->IsValid()) { + return false; + } + + if (!slot.HasSampler()) { + return true; + } + + bindings.samplers[slot.sampler_index] = {&metadata, sampler}; + return true; +} + +bool ComputeCommand::BindResource(ShaderStage stage, + const SampledImageSlot& slot, + const ShaderMetadata& metadata, + std::shared_ptr texture, + std::shared_ptr sampler) { + if (stage != ShaderStage::kCompute) { + VALIDATION_LOG << "Use Command for non-compute shader stages."; + return false; + } + return BindResource(stage, slot, metadata, texture) && + BindResource(stage, slot, metadata, sampler); +} + +} // namespace impeller diff --git a/impeller/renderer/compute_command.h b/impeller/renderer/compute_command.h new file mode 100644 index 0000000000000..4873b68dbdaf1 --- /dev/null +++ b/impeller/renderer/compute_command.h @@ -0,0 +1,81 @@ +// 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. + +#pragma once + +#include +#include +#include +#include + +#include "flutter/fml/logging.h" +#include "flutter/fml/macros.h" +#include "impeller/geometry/rect.h" +#include "impeller/renderer/buffer_view.h" +#include "impeller/renderer/command.h" +#include "impeller/renderer/compute_pipeline_descriptor.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/pipeline.h" +#include "impeller/renderer/sampler.h" +#include "impeller/renderer/shader_types.h" +#include "impeller/renderer/texture.h" +#include "impeller/renderer/vertex_buffer.h" +#include "impeller/renderer/vertex_buffer_builder.h" +#include "impeller/tessellator/tessellator.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief An object used to specify compute work to the GPU along with +/// references to resources the GPU will used when doing said work. +/// +/// To construct a valid command, follow these steps: +/// * Specify a valid pipeline. +/// * (Optional) Specify a debug label. +/// +/// Command are very lightweight objects and can be created +/// frequently and on demand. The resources referenced in commands +/// views into buffers managed by other allocators and resource +/// managers. +/// +struct ComputeCommand { + //---------------------------------------------------------------------------- + /// The pipeline to use for this command. + /// + std::shared_ptr> pipeline; + //---------------------------------------------------------------------------- + /// The buffer, texture, and sampler bindings used by the compute pipeline + /// stage. + /// + Bindings bindings; + //---------------------------------------------------------------------------- + /// The debugging label to use for the command. + /// + std::string label; + + bool BindResource(ShaderStage stage, + const ShaderUniformSlot& slot, + const ShaderMetadata& metadata, + BufferView view); + + bool BindResource(ShaderStage stage, + const SampledImageSlot& slot, + const ShaderMetadata& metadata, + std::shared_ptr texture); + + bool BindResource(ShaderStage stage, + const SampledImageSlot& slot, + const ShaderMetadata& metadata, + std::shared_ptr sampler); + + bool BindResource(ShaderStage stage, + const SampledImageSlot& slot, + const ShaderMetadata& metadata, + std::shared_ptr texture, + std::shared_ptr sampler); + + constexpr operator bool() const { return pipeline && pipeline->IsValid(); } +}; + +} // namespace impeller diff --git a/impeller/renderer/compute_pass.cc b/impeller/renderer/compute_pass.cc new file mode 100644 index 0000000000000..8fbe6fd1f0c09 --- /dev/null +++ b/impeller/renderer/compute_pass.cc @@ -0,0 +1,50 @@ +// 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. + +#include "impeller/renderer/compute_pass.h" +#include + +#include "impeller/base/strings.h" +#include "impeller/base/validation.h" +#include "impeller/renderer/host_buffer.h" + +namespace impeller { + +ComputePass::ComputePass(std::weak_ptr context) + : context_(std::move(context)), transients_buffer_(HostBuffer::Create()) {} + +ComputePass::~ComputePass() = default; + +HostBuffer& ComputePass::GetTransientsBuffer() { + return *transients_buffer_; +} + +void ComputePass::SetLabel(std::string label) { + if (label.empty()) { + return; + } + transients_buffer_->SetLabel(SPrintF("%s Transients", label.c_str())); + OnSetLabel(std::move(label)); +} + +bool ComputePass::AddCommand(ComputeCommand command) { + if (!command) { + VALIDATION_LOG << "Attempted to add an invalid command to the render pass."; + return false; + } + + commands_.emplace_back(std::move(command)); + return true; +} + +bool ComputePass::EncodeCommands() const { + auto context = context_.lock(); + // The context could have been collected in the meantime. + if (!context) { + return false; + } + return OnEncodeCommands(*context); +} + +} // namespace impeller diff --git a/impeller/renderer/compute_pass.h b/impeller/renderer/compute_pass.h new file mode 100644 index 0000000000000..2f5aebd82dcae --- /dev/null +++ b/impeller/renderer/compute_pass.h @@ -0,0 +1,72 @@ +// 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. + +#pragma once + +#include +#include + +#include "command_buffer.h" +#include "impeller/renderer/compute_command.h" +#include "impeller/renderer/device_buffer.h" +#include "impeller/renderer/texture.h" + +namespace impeller { + +class HostBuffer; +class Allocator; + +//------------------------------------------------------------------------------ +/// @brief Compute passes encode compute shader into the underlying command +/// buffer. +/// +/// @see `CommandBuffer` +/// +class ComputePass { + public: + virtual ~ComputePass(); + + virtual bool IsValid() const = 0; + + void SetLabel(std::string label); + + HostBuffer& GetTransientsBuffer(); + + //---------------------------------------------------------------------------- + /// @brief Record a command for subsequent encoding to the underlying + /// command buffer. No work is encoded into the command buffer at + /// this time. + /// + /// @param[in] command The command + /// + /// @return If the command was valid for subsequent commitment. + /// + bool AddCommand(ComputeCommand command); + + //---------------------------------------------------------------------------- + /// @brief Encode the recorded commands to the underlying command buffer. + /// + /// @param transients_allocator The transients allocator. + /// + /// @return If the commands were encoded to the underlying command + /// buffer. + /// + bool EncodeCommands() const; + + protected: + const std::weak_ptr context_; + std::shared_ptr transients_buffer_; + std::vector commands_; + + explicit ComputePass(std::weak_ptr context); + + virtual void OnSetLabel(std::string label) = 0; + + virtual bool OnEncodeCommands(const Context& context) const = 0; + + private: + FML_DISALLOW_COPY_AND_ASSIGN(ComputePass); +}; + +} // namespace impeller diff --git a/impeller/renderer/compute_pipeline_builder.cc b/impeller/renderer/compute_pipeline_builder.cc new file mode 100644 index 0000000000000..5bc86027add95 --- /dev/null +++ b/impeller/renderer/compute_pipeline_builder.cc @@ -0,0 +1,11 @@ +// 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. + +#include "impeller/renderer/compute_pipeline_builder.h" + +namespace impeller { + +// + +} // namespace impeller diff --git a/impeller/renderer/compute_pipeline_builder.h b/impeller/renderer/compute_pipeline_builder.h new file mode 100644 index 0000000000000..044498d618270 --- /dev/null +++ b/impeller/renderer/compute_pipeline_builder.h @@ -0,0 +1,78 @@ +// 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. + +#pragma once + +#include "flutter/fml/logging.h" +#include "flutter/fml/macros.h" +#include "impeller/base/strings.h" +#include "impeller/base/validation.h" +#include "impeller/renderer/compute_pipeline_descriptor.h" +#include "impeller/renderer/context.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/shader_library.h" +#include "impeller/renderer/vertex_descriptor.h" + +namespace impeller { + +//------------------------------------------------------------------------------ +/// @brief An optional (but highly recommended) utility for creating +/// pipelines from reflected shader information. +/// +/// @tparam Compute_Shader The reflected compute shader information. Found +/// in a generated header file called +/// .comp.h. +/// +template +struct ComputePipelineBuilder { + public: + using ComputeShader = ComputeShader_; + + //---------------------------------------------------------------------------- + /// @brief Create a default pipeline descriptor using the combination + /// reflected shader information. The descriptor can be configured + /// further before a pipeline state object is created using it. + /// + /// @param[in] context The context + /// + /// @return If the combination of reflected shader information is + /// compatible and the requisite functions can be found in the + /// context, a pipeline descriptor. + /// + static std::optional MakeDefaultPipelineDescriptor( + const Context& context) { + ComputePipelineDescriptor desc; + if (InitializePipelineDescriptorDefaults(context, desc)) { + return {std::move(desc)}; + } else { + return std::nullopt; + } + } + + [[nodiscard]] static bool InitializePipelineDescriptorDefaults( + const Context& context, + ComputePipelineDescriptor& desc) { + // Setup debug instrumentation. + desc.SetLabel(SPrintF("%s Pipeline", ComputeShader::kLabel.data())); + + // Resolve pipeline entrypoints. + { + auto compute_function = context.GetShaderLibrary()->GetFunction( + ComputeShader::kEntrypointName, ShaderStage::kCompute); + + if (!compute_function) { + VALIDATION_LOG << "Could not resolve compute pipeline entrypoint '" + << ComputeShader::kEntrypointName + << "' for pipeline named '" << ComputeShader::kLabel + << "'."; + return false; + } + + desc.SetStageEntrypoint(std::move(compute_function)); + } + return true; + } +}; + +} // namespace impeller diff --git a/impeller/renderer/compute_pipeline_descriptor.cc b/impeller/renderer/compute_pipeline_descriptor.cc new file mode 100644 index 0000000000000..37a69eaec955e --- /dev/null +++ b/impeller/renderer/compute_pipeline_descriptor.cc @@ -0,0 +1,66 @@ +// 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. + +#include "impeller/renderer/compute_pipeline_descriptor.h" + +#include "impeller/renderer/formats.h" +#include "impeller/renderer/shader_function.h" +#include "impeller/renderer/shader_library.h" +#include "impeller/renderer/vertex_descriptor.h" + +namespace impeller { + +ComputePipelineDescriptor::ComputePipelineDescriptor() = default; + +ComputePipelineDescriptor::~ComputePipelineDescriptor() = default; + +// Comparable +std::size_t ComputePipelineDescriptor::GetHash() const { + auto seed = fml::HashCombine(); + fml::HashCombineSeed(seed, label_); + if (entrypoint_) { + fml::HashCombineSeed(seed, entrypoint_->GetHash()); + } + return seed; +} + +// Comparable +bool ComputePipelineDescriptor::IsEqual( + const ComputePipelineDescriptor& other) const { + return label_ == other.label_ && + DeepComparePointer(entrypoint_, other.entrypoint_); +} + +ComputePipelineDescriptor& ComputePipelineDescriptor::SetLabel( + std::string label) { + label_ = std::move(label); + return *this; +} + +ComputePipelineDescriptor& ComputePipelineDescriptor::SetStageEntrypoint( + std::shared_ptr function) { + FML_DCHECK(!function || function->GetStage() == ShaderStage::kCompute); + if (!function || function->GetStage() != ShaderStage::kCompute) { + return *this; + } + + if (function->GetStage() == ShaderStage::kUnknown) { + return *this; + } + + entrypoint_ = std::move(function); + + return *this; +} + +std::shared_ptr +ComputePipelineDescriptor::GetStageEntrypoint() const { + return entrypoint_; +} + +const std::string& ComputePipelineDescriptor::GetLabel() const { + return label_; +} + +} // namespace impeller diff --git a/impeller/renderer/compute_pipeline_descriptor.h b/impeller/renderer/compute_pipeline_descriptor.h new file mode 100644 index 0000000000000..3b71d05f3b24e --- /dev/null +++ b/impeller/renderer/compute_pipeline_descriptor.h @@ -0,0 +1,62 @@ +// 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. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "flutter/fml/hash_combine.h" +#include "flutter/fml/macros.h" +#include "impeller/base/comparable.h" +#include "impeller/renderer/formats.h" +#include "impeller/renderer/shader_types.h" +#include "impeller/tessellator/tessellator.h" + +namespace impeller { + +class ShaderFunction; +template +class Pipeline; + +class ComputePipelineDescriptor final + : public Comparable { + public: + ComputePipelineDescriptor(); + + ~ComputePipelineDescriptor(); + + ComputePipelineDescriptor& SetLabel(std::string label); + + const std::string& GetLabel() const; + + ComputePipelineDescriptor& SetStageEntrypoint( + std::shared_ptr function); + + std::shared_ptr GetStageEntrypoint() const; + + // Comparable + std::size_t GetHash() const override; + + // Comparable + bool IsEqual(const ComputePipelineDescriptor& other) const override; + + private: + std::string label_; + std::shared_ptr entrypoint_; +}; + +using ComputePipelineMap = std::unordered_map< + ComputePipelineDescriptor, + std::shared_future>>, + ComparableHash, + ComparableEqual>; + +} // namespace impeller diff --git a/impeller/renderer/pipeline.cc b/impeller/renderer/pipeline.cc index 2e6d5657d849f..4399b2707259d 100644 --- a/impeller/renderer/pipeline.cc +++ b/impeller/renderer/pipeline.cc @@ -4,35 +4,54 @@ #include "impeller/renderer/pipeline.h" +#include "compute_pipeline_descriptor.h" #include "impeller/base/promise.h" +#include "impeller/renderer/compute_pipeline_descriptor.h" #include "impeller/renderer/context.h" #include "impeller/renderer/pipeline_library.h" +#include "pipeline_descriptor.h" namespace impeller { -Pipeline::Pipeline(std::weak_ptr library, - PipelineDescriptor desc) +template +Pipeline::Pipeline(std::weak_ptr library, T desc) : library_(std::move(library)), desc_(std::move(desc)) {} -Pipeline::~Pipeline() = default; +template +Pipeline::~Pipeline() = default; -PipelineFuture CreatePipelineFuture(const Context& context, - std::optional desc) { +PipelineFuture CreatePipelineFuture( + const Context& context, + std::optional desc) { if (!context.IsValid()) { - return RealizedFuture>(nullptr); + return RealizedFuture>>( + nullptr); } - return context.GetPipelineLibrary()->GetRenderPipeline(std::move(desc)); + return context.GetPipelineLibrary()->GetPipeline(std::move(desc)); } -const PipelineDescriptor& Pipeline::GetDescriptor() const { +PipelineFuture CreatePipelineFuture( + const Context& context, + std::optional desc) { + if (!context.IsValid()) { + return RealizedFuture>>( + nullptr); + } + + return context.GetPipelineLibrary()->GetPipeline(std::move(desc)); +} + +template +const T& Pipeline::GetDescriptor() const { return desc_; } -PipelineFuture Pipeline::CreateVariant( - std::function descriptor_callback) const { +template +PipelineFuture Pipeline::CreateVariant( + std::function descriptor_callback) const { if (!descriptor_callback) { - return RealizedFuture>(nullptr); + return RealizedFuture>>(nullptr); } auto copied_desc = desc_; @@ -43,10 +62,13 @@ PipelineFuture Pipeline::CreateVariant( if (!library) { VALIDATION_LOG << "The library from which this pipeline was created was " "already collected."; - return RealizedFuture>(nullptr); + return RealizedFuture>>(nullptr); } - return library->GetRenderPipeline(std::move(copied_desc)); + return library->GetPipeline(std::move(copied_desc)); } +template class Pipeline; +template class Pipeline; + } // namespace impeller diff --git a/impeller/renderer/pipeline.h b/impeller/renderer/pipeline.h index 16be034562d69..a38821a33f0a7 100644 --- a/impeller/renderer/pipeline.h +++ b/impeller/renderer/pipeline.h @@ -6,13 +6,18 @@ #include +#include "compute_pipeline_descriptor.h" #include "flutter/fml/macros.h" +#include "impeller/renderer/compute_pipeline_builder.h" +#include "impeller/renderer/compute_pipeline_descriptor.h" #include "impeller/renderer/context.h" #include "impeller/renderer/pipeline_builder.h" +#include "impeller/renderer/pipeline_descriptor.h" namespace impeller { class PipelineLibrary; +template class Pipeline; // TODO(csg): Using a simple future is sub-optimal since callers that want to @@ -21,7 +26,8 @@ class Pipeline; // would be a concurrency pessimization. // // Use a struct that stores the future and the descriptor separately. -using PipelineFuture = std::shared_future>; +template +using PipelineFuture = std::shared_future>>; //------------------------------------------------------------------------------ /// @brief Describes the fixed function and programmable aspects of @@ -37,6 +43,7 @@ using PipelineFuture = std::shared_future>; /// Impeller offline shader compiler to generate a typed pipeline /// object. /// +template class Pipeline { public: virtual ~Pipeline(); @@ -50,44 +57,90 @@ class Pipeline { /// /// @return The descriptor. /// - const PipelineDescriptor& GetDescriptor() const; + const T& GetDescriptor() const; - PipelineFuture CreateVariant( - std::function descriptor_callback) const; + PipelineFuture CreateVariant( + std::function descriptor_callback) const; protected: - Pipeline(std::weak_ptr library, PipelineDescriptor desc); + Pipeline(std::weak_ptr library, T desc); private: const std::weak_ptr library_; - const PipelineDescriptor desc_; + const T desc_; FML_DISALLOW_COPY_AND_ASSIGN(Pipeline); }; -PipelineFuture CreatePipelineFuture(const Context& context, - std::optional desc); +extern template class Pipeline; +extern template class Pipeline; + +PipelineFuture CreatePipelineFuture( + const Context& context, + std::optional desc); + +PipelineFuture CreatePipelineFuture( + const Context& context, + std::optional desc); template -class PipelineT { +class RenderPipelineT { public: using VertexShader = VertexShader_; using FragmentShader = FragmentShader_; using Builder = PipelineBuilder; - explicit PipelineT(const Context& context) - : PipelineT(CreatePipelineFuture( + explicit RenderPipelineT(const Context& context) + : RenderPipelineT(CreatePipelineFuture( + context, + Builder::MakeDefaultPipelineDescriptor(context))) {} + + explicit RenderPipelineT(const Context& context, + std::optional desc) + : RenderPipelineT(CreatePipelineFuture(context, desc)) {} + + explicit RenderPipelineT(PipelineFuture future) + : pipeline_future_(std::move(future)) {} + + std::shared_ptr> WaitAndGet() { + if (did_wait_) { + return pipeline_; + } + did_wait_ = true; + if (pipeline_future_.valid()) { + pipeline_ = pipeline_future_.get(); + } + return pipeline_; + } + + private: + PipelineFuture pipeline_future_; + std::shared_ptr> pipeline_; + bool did_wait_ = false; + + FML_DISALLOW_COPY_AND_ASSIGN(RenderPipelineT); +}; + +template +class ComputePipelineT { + public: + using ComputeShader = ComputeShader_; + using Builder = ComputePipelineBuilder; + + explicit ComputePipelineT(const Context& context) + : ComputePipelineT(CreatePipelineFuture( context, Builder::MakeDefaultPipelineDescriptor(context))) {} - explicit PipelineT(const Context& context, - std::optional desc) - : PipelineT(CreatePipelineFuture(context, desc)) {} + explicit ComputePipelineT( + const Context& context, + std::optional compute_desc) + : ComputePipelineT(CreatePipelineFuture(context, compute_desc)) {} - explicit PipelineT(PipelineFuture future) + explicit ComputePipelineT(PipelineFuture future) : pipeline_future_(std::move(future)) {} - std::shared_ptr WaitAndGet() { + std::shared_ptr> WaitAndGet() { if (did_wait_) { return pipeline_; } @@ -99,11 +152,11 @@ class PipelineT { } private: - PipelineFuture pipeline_future_; - std::shared_ptr pipeline_; + PipelineFuture pipeline_future_; + std::shared_ptr> pipeline_; bool did_wait_ = false; - FML_DISALLOW_COPY_AND_ASSIGN(PipelineT); + FML_DISALLOW_COPY_AND_ASSIGN(ComputePipelineT); }; } // namespace impeller diff --git a/impeller/renderer/pipeline_descriptor.h b/impeller/renderer/pipeline_descriptor.h index f229fc47342a0..7a2fe6f4553bb 100644 --- a/impeller/renderer/pipeline_descriptor.h +++ b/impeller/renderer/pipeline_descriptor.h @@ -24,6 +24,7 @@ namespace impeller { class ShaderFunction; class VertexDescriptor; +template class Pipeline; class PipelineDescriptor final : public Comparable { @@ -132,10 +133,10 @@ class PipelineDescriptor final : public Comparable { back_stencil_attachment_descriptor_; }; -using PipelineMap = - std::unordered_map>, - ComparableHash, - ComparableEqual>; +using PipelineMap = std::unordered_map< + PipelineDescriptor, + std::shared_future>>, + ComparableHash, + ComparableEqual>; } // namespace impeller diff --git a/impeller/renderer/pipeline_library.cc b/impeller/renderer/pipeline_library.cc index 10bb194bf4101..577034f313e59 100644 --- a/impeller/renderer/pipeline_library.cc +++ b/impeller/renderer/pipeline_library.cc @@ -10,12 +10,24 @@ PipelineLibrary::PipelineLibrary() = default; PipelineLibrary::~PipelineLibrary() = default; -PipelineFuture PipelineLibrary::GetRenderPipeline( +PipelineFuture PipelineLibrary::GetPipeline( std::optional descriptor) { if (descriptor.has_value()) { - return GetRenderPipeline(std::move(descriptor.value())); + return GetPipeline(std::move(descriptor.value())); } - auto promise = std::make_shared>>(); + auto promise = std::make_shared< + std::promise>>>(); + promise->set_value(nullptr); + return promise->get_future(); +} + +PipelineFuture PipelineLibrary::GetPipeline( + std::optional descriptor) { + if (descriptor.has_value()) { + return GetPipeline(std::move(descriptor.value())); + } + auto promise = std::make_shared< + std::promise>>>(); promise->set_value(nullptr); return promise->get_future(); } diff --git a/impeller/renderer/pipeline_library.h b/impeller/renderer/pipeline_library.h index 4b0d01f2d9f51..dc07ef8bdb87f 100644 --- a/impeller/renderer/pipeline_library.h +++ b/impeller/renderer/pipeline_library.h @@ -6,6 +6,7 @@ #include +#include "compute_pipeline_descriptor.h" #include "flutter/fml/macros.h" #include "impeller/renderer/pipeline.h" #include "impeller/renderer/pipeline_descriptor.h" @@ -18,12 +19,19 @@ class PipelineLibrary : public std::enable_shared_from_this { public: virtual ~PipelineLibrary(); - PipelineFuture GetRenderPipeline( + PipelineFuture GetPipeline( std::optional descriptor); + PipelineFuture GetPipeline( + std::optional descriptor); + virtual bool IsValid() const = 0; - virtual PipelineFuture GetRenderPipeline(PipelineDescriptor descriptor) = 0; + virtual PipelineFuture GetPipeline( + PipelineDescriptor descriptor) = 0; + + virtual PipelineFuture GetPipeline( + ComputePipelineDescriptor descriptor) = 0; protected: PipelineLibrary(); diff --git a/impeller/renderer/renderer_unittests.cc b/impeller/renderer/renderer_unittests.cc index ba997d9d00d4f..944ad67ebaf74 100644 --- a/impeller/renderer/renderer_unittests.cc +++ b/impeller/renderer/renderer_unittests.cc @@ -15,6 +15,9 @@ #include "impeller/fixtures/instanced_draw.vert.h" #include "impeller/fixtures/mipmaps.frag.h" #include "impeller/fixtures/mipmaps.vert.h" +#if IMPELLER_ENABLE_METAL || IMPELLER_ENABLE_VULKAN +#include "impeller/fixtures/sample.comp.h" +#endif #include "impeller/fixtures/test_texture.frag.h" #include "impeller/fixtures/test_texture.vert.h" #include "impeller/geometry/path_builder.h" @@ -51,7 +54,7 @@ TEST_P(RendererTest, CanCreateBoxPrimitive) { ASSERT_TRUE(desc.has_value()); desc->SetSampleCount(SampleCount::kCount4); auto box_pipeline = - context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); + context->GetPipelineLibrary()->GetPipeline(std::move(desc)).get(); ASSERT_TRUE(box_pipeline); // Vertex buffer. @@ -117,7 +120,7 @@ TEST_P(RendererTest, CanRenderPerspectiveCube) { desc->SetCullMode(CullMode::kBackFace); desc->SetSampleCount(SampleCount::kCount4); auto pipeline = - context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); + context->GetPipelineLibrary()->GetPipeline(std::move(desc)).get(); ASSERT_TRUE(pipeline); struct Cube { @@ -215,7 +218,7 @@ TEST_P(RendererTest, CanRenderMultiplePrimitives) { ASSERT_TRUE(desc.has_value()); desc->SetSampleCount(SampleCount::kCount4); auto box_pipeline = - context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); + context->GetPipelineLibrary()->GetPipeline(std::move(desc)).get(); ASSERT_TRUE(box_pipeline); // Vertex buffer. @@ -288,7 +291,7 @@ TEST_P(RendererTest, CanRenderToTexture) { BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context); ASSERT_TRUE(pipeline_desc.has_value()); auto box_pipeline = - context->GetPipelineLibrary()->GetRenderPipeline(pipeline_desc).get(); + context->GetPipelineLibrary()->GetPipeline(pipeline_desc).get(); ASSERT_TRUE(box_pipeline); VertexBufferBuilder vertex_builder; @@ -410,10 +413,9 @@ TEST_P(RendererTest, CanRenderInstanced) { auto pipeline = GetContext() ->GetPipelineLibrary() - ->GetRenderPipeline( - PipelineBuilder::MakeDefaultPipelineDescriptor( - *GetContext()) - ->SetSampleCount(SampleCount::kCount4)) + ->GetPipeline(PipelineBuilder::MakeDefaultPipelineDescriptor( + *GetContext()) + ->SetSampleCount(SampleCount::kCount4)) .get(); ASSERT_TRUE(pipeline && pipeline->IsValid()); @@ -456,7 +458,7 @@ TEST_P(RendererTest, CanBlitTextureToTexture) { ASSERT_TRUE(desc.has_value()); desc->SetSampleCount(SampleCount::kCount4); auto mipmaps_pipeline = - context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); + context->GetPipelineLibrary()->GetPipeline(std::move(desc)).get(); ASSERT_TRUE(mipmaps_pipeline); TextureDescriptor texture_desc; @@ -566,7 +568,7 @@ TEST_P(RendererTest, CanGenerateMipmaps) { ASSERT_TRUE(desc.has_value()); desc->SetSampleCount(SampleCount::kCount4); auto mipmaps_pipeline = - context->GetPipelineLibrary()->GetRenderPipeline(std::move(desc)).get(); + context->GetPipelineLibrary()->GetPipeline(std::move(desc)).get(); ASSERT_TRUE(mipmaps_pipeline); auto boston = CreateTextureForFixture("boston.jpg", true); @@ -686,9 +688,8 @@ TEST_P(RendererTest, TheImpeller) { PipelineBuilder::MakeDefaultPipelineDescriptor(*context); ASSERT_TRUE(pipeline_descriptor.has_value()); pipeline_descriptor->SetSampleCount(SampleCount::kCount4); - auto pipeline = context->GetPipelineLibrary() - ->GetRenderPipeline(pipeline_descriptor) - .get(); + auto pipeline = + context->GetPipelineLibrary()->GetPipeline(pipeline_descriptor).get(); ASSERT_TRUE(pipeline && pipeline->IsValid()); auto blue_noise = CreateTextureForFixture("blue_noise.png"); @@ -738,5 +739,50 @@ TEST_P(RendererTest, TheImpeller) { OpenPlaygroundHere(callback); } +#if IMPELLER_ENABLE_METAL || IMPELLER_ENABLE_VULKAN +TEST_P(RendererTest, CanCreateComputePass) { + if (GetParam() == PlaygroundBackend::kOpenGLES) { + GTEST_SKIP_("Compute is not supported on GL."); + } + if (GetParam() == PlaygroundBackend::kVulkan) { + GTEST_SKIP_("Compute is not supported on Vulkan yet."); + } + + using CS = SampleComputeShader; + auto context = GetContext(); + ASSERT_TRUE(context); + using SamplePipelineBuilder = ComputePipelineBuilder; + auto pipeline_desc = + SamplePipelineBuilder::MakeDefaultPipelineDescriptor(*context); + ASSERT_TRUE(pipeline_desc.has_value()); + auto compute_pipeline = + context->GetPipelineLibrary()->GetPipeline(pipeline_desc).get(); + ASSERT_TRUE(compute_pipeline); + + auto cmd_buffer = context->CreateCommandBuffer(); + auto pass = cmd_buffer->CreateComputePass(); + ASSERT_TRUE(pass && pass->IsValid()); + + ComputeCommand cmd; + cmd.label = "Compute"; + cmd.pipeline = compute_pipeline; + + std::vector input_0; + std::vector input_1; + input_0.push_back(CS::Input0{Vector4(2.0, 3.0, 4.0, 5.0)}); + input_1.push_back(CS::Input1{Vector4(6.0, 7.0, 8.0, 9.0)}); + + std::vector output(5); + CS::BindInput0(cmd, + pass->GetTransientsBuffer().EmplaceStorageBuffer(input_0)); + CS::BindInput1(cmd, + pass->GetTransientsBuffer().EmplaceStorageBuffer(input_1)); + CS::BindOutput(cmd, pass->GetTransientsBuffer().EmplaceStorageBuffer(output)); + + ASSERT_TRUE(pass->AddCommand(std::move(cmd))); + ASSERT_TRUE(pass->EncodeCommands()); +} +#endif + } // namespace testing } // namespace impeller diff --git a/impeller/runtime_stage/runtime_stage_unittests.cc b/impeller/runtime_stage/runtime_stage_unittests.cc index 108d59d0742e5..b3aa6208e1cda 100644 --- a/impeller/runtime_stage/runtime_stage_unittests.cc +++ b/impeller/runtime_stage/runtime_stage_unittests.cc @@ -237,10 +237,8 @@ TEST_P(RuntimeStageTest, CanCreatePipelineFromRuntimeStage) { desc.SetColorAttachmentDescriptor(0u, color0); desc.SetStencilAttachmentDescriptors(stencil0); desc.SetStencilPixelFormat(PixelFormat::kDefaultStencil); - auto pipeline = GetContext() - ->GetPipelineLibrary() - ->GetRenderPipeline(std::move(desc)) - .get(); + auto pipeline = + GetContext()->GetPipelineLibrary()->GetPipeline(std::move(desc)).get(); ASSERT_NE(pipeline, nullptr); } diff --git a/impeller/tools/impeller.gni b/impeller/tools/impeller.gni index ba7ee027eae4d..ba9e093667b78 100644 --- a/impeller/tools/impeller.gni +++ b/impeller/tools/impeller.gni @@ -562,7 +562,11 @@ template("impeller_shaders") { gles_shaders = "gles_$target_name" impeller_shaders_gles(gles_shaders) { name = invoker.name - shaders = invoker.shaders + if (defined(invoker.gles_exclusions)) { + shaders = invoker.shaders - invoker.gles_exclusions + } else { + shaders = invoker.shaders + } } }