diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 8e31f539c527f..e8acef0ee81c9 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1295,6 +1295,7 @@ ORIGIN: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur_alp ORIGIN: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur_alpha_nodecal.frag + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur_noalpha_decal.frag + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur_noalpha_nodecal.frag + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/shaders/geometry/points.vert + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/glyph_atlas.frag + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/glyph_atlas.vert + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/shaders/glyph_atlas_color.frag + ../../../flutter/LICENSE @@ -3912,6 +3913,7 @@ FILE: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur_alpha FILE: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur_alpha_nodecal.frag FILE: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur_noalpha_decal.frag FILE: ../../../flutter/impeller/entity/shaders/gaussian_blur/gaussian_blur_noalpha_nodecal.frag +FILE: ../../../flutter/impeller/entity/shaders/geometry/points.vert FILE: ../../../flutter/impeller/entity/shaders/glyph_atlas.frag FILE: ../../../flutter/impeller/entity/shaders/glyph_atlas.vert FILE: ../../../flutter/impeller/entity/shaders/glyph_atlas_color.frag diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 8ee384fc5aab6..6ac4df2f0d4fe 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -383,6 +383,25 @@ void Canvas::RestoreClip() { GetCurrentPass().AddEntity(entity); } +void Canvas::DrawPoints(std::vector points, + Scalar radius, + const Paint& paint, + PointStyle point_style) { + if (radius <= 0) { + return; + } + + Entity entity; + entity.SetTransformation(GetCurrentTransformation()); + entity.SetStencilDepth(GetStencilDepth()); + entity.SetBlendMode(paint.blend_mode); + entity.SetContents(paint.WithFilters(paint.CreateContentsForGeometry( + Geometry::MakePointField(std::move(points), radius, + /*round=*/point_style == PointStyle::kRound)))); + + GetCurrentPass().AddEntity(entity); +} + void Canvas::DrawPicture(Picture picture) { if (!picture.pass) { return; diff --git a/impeller/aiks/canvas.h b/impeller/aiks/canvas.h index 54be12491b09d..5833317c0072b 100644 --- a/impeller/aiks/canvas.h +++ b/impeller/aiks/canvas.h @@ -37,6 +37,14 @@ struct CanvasStackEntry { bool contains_clips = false; }; +enum class PointStyle { + /// @brief Points are drawn as squares. + kRound, + + /// @brief Points are drawn as circles. + kSquare, +}; + class Canvas { public: struct DebugOptions { @@ -100,6 +108,11 @@ class Canvas { void DrawCircle(Point center, Scalar radius, const Paint& paint); + void DrawPoints(std::vector, + Scalar radius, + const Paint& paint, + PointStyle point_style); + void DrawImage(const std::shared_ptr& image, Point offset, const Paint& paint, diff --git a/impeller/display_list/dl_dispatcher.cc b/impeller/display_list/dl_dispatcher.cc index c73ea1f9962c6..be3fb80af7d94 100644 --- a/impeller/display_list/dl_dispatcher.cc +++ b/impeller/display_list/dl_dispatcher.cc @@ -930,16 +930,17 @@ void DlDispatcher::drawPoints(PointMode mode, Paint paint = paint_; paint.style = Paint::Style::kStroke; switch (mode) { - case flutter::DlCanvas::PointMode::kPoints: - if (paint.stroke_cap == Cap::kButt) { - paint.stroke_cap = Cap::kSquare; + case flutter::DlCanvas::PointMode::kPoints: { + // Cap::kButt is also treated as a square. + auto point_style = paint.stroke_cap == Cap::kRound ? PointStyle::kRound + : PointStyle::kSquare; + auto radius = paint.stroke_width; + if (radius > 0) { + radius /= 2.0; } - for (uint32_t i = 0; i < count; i++) { - Point p0 = skia_conversions::ToPoint(points[i]); - auto path = PathBuilder{}.AddLine(p0, p0).TakePath(); - canvas_.DrawPath(path, paint); - } - break; + canvas_.DrawPoints(skia_conversions::ToPoints(points, count), radius, + paint, point_style); + } break; case flutter::DlCanvas::PointMode::kLines: for (uint32_t i = 1; i < count; i += 2) { Point p0 = skia_conversions::ToPoint(points[i - 1]); diff --git a/impeller/display_list/skia_conversions.cc b/impeller/display_list/skia_conversions.cc index 16c8978bbc6ce..e488b39a9d4ff 100644 --- a/impeller/display_list/skia_conversions.cc +++ b/impeller/display_list/skia_conversions.cc @@ -26,6 +26,14 @@ std::vector ToRects(const SkRect tex[], int count) { return result; } +std::vector ToPoints(const SkPoint points[], int count) { + std::vector result(count); + for (auto i = 0; i < count; i++) { + result[i] = ToPoint(points[i]); + } + return result; +} + PathBuilder::RoundingRadii ToRoundingRadii(const SkRRect& rrect) { using Corner = SkRRect::Corner; PathBuilder::RoundingRadii radii; diff --git a/impeller/display_list/skia_conversions.h b/impeller/display_list/skia_conversions.h index 933875bee7101..5f1524674240d 100644 --- a/impeller/display_list/skia_conversions.h +++ b/impeller/display_list/skia_conversions.h @@ -26,6 +26,8 @@ std::optional ToRect(const SkRect* rect); std::vector ToRects(const SkRect tex[], int count); +std::vector ToPoints(const SkPoint points[], int count); + Point ToPoint(const SkPoint& point); Color ToColor(const SkColor& color); diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 0cfde08d51875..9a32be2f05872 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -96,6 +96,7 @@ impeller_shaders("modern_entity_shaders") { "shaders/linear_gradient_ssbo_fill.frag", "shaders/radial_gradient_ssbo_fill.frag", "shaders/sweep_gradient_ssbo_fill.frag", + "shaders/geometry/points.vert", ] } diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index 4503b40cdbe6f..70e7b16ac6e23 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -144,6 +144,7 @@ void ContentContextOptions::ApplyToPipelineDescriptor( desc.SetPrimitiveType(primitive_type); desc.SetPolygonMode(wireframe ? PolygonMode::kLine : PolygonMode::kFill); + desc.SetEnableRasterization(enable_rasterization); } template @@ -161,6 +162,22 @@ static std::unique_ptr CreateDefaultPipeline( return std::make_unique(context, desc); } +template +static std::unique_ptr CreateDefaultNonRenderingPipeline( + const Context& context) { + auto desc = PipelineT::Builder::MakeDefaultPipelineDescriptor(context); + if (!desc.has_value()) { + return nullptr; + } + // Apply default ContentContextOptions to the descriptor. + const auto default_color_fmt = + context.GetCapabilities()->GetDefaultColorFormat(); + ContentContextOptions{.color_attachment_pixel_format = default_color_fmt, + .enable_rasterization = false} + .ApplyToPipelineDescriptor(*desc); + return std::make_unique(context, desc); +} + ContentContext::ContentContext(std::shared_ptr context) : context_(std::move(context)), tessellator_(std::make_shared()), @@ -296,6 +313,12 @@ ContentContext::ContentContext(std::shared_ptr context) porter_duff_blend_pipelines_[{}] = CreateDefaultPipeline(*context_); + if (context_->GetCapabilities()->SupportsDisabledRasterization()) { + point_field_geometry_pipelines_[{}] = + CreateDefaultNonRenderingPipeline( + *context_); + } + if (solid_fill_pipelines_[{}]->GetDescriptor().has_value()) { auto clip_pipeline_descriptor = solid_fill_pipelines_[{}]->GetDescriptor().value(); diff --git a/impeller/entity/contents/content_context.h b/impeller/entity/contents/content_context.h index 5042ff54ff187..25f2dc36c66ae 100644 --- a/impeller/entity/contents/content_context.h +++ b/impeller/entity/contents/content_context.h @@ -39,6 +39,7 @@ #include "impeller/entity/linear_to_srgb_filter.vert.h" #include "impeller/entity/morphology_filter.frag.h" #include "impeller/entity/morphology_filter.vert.h" +#include "impeller/entity/points.vert.h" #include "impeller/entity/porter_duff_blend.frag.h" #include "impeller/entity/radial_gradient_fill.frag.h" #include "impeller/entity/rrect_blur.frag.h" @@ -275,6 +276,10 @@ using FramebufferBlendSoftLightPipeline = RenderPipelineT; +/// Geometry Pipelines +using PointFieldGeometryPipeline = + RenderPipelineT; + /// Pipeline state configuration. /// /// Each unique combination of these options requires a different pipeline state @@ -294,13 +299,14 @@ struct ContentContextOptions { std::optional color_attachment_pixel_format; bool has_stencil_attachment = true; bool wireframe = false; + bool enable_rasterization = true; struct Hash { constexpr std::size_t operator()(const ContentContextOptions& o) const { - return fml::HashCombine(o.sample_count, o.blend_mode, o.stencil_compare, - o.stencil_operation, o.primitive_type, - o.color_attachment_pixel_format, - o.has_stencil_attachment, o.wireframe); + return fml::HashCombine( + o.sample_count, o.blend_mode, o.stencil_compare, o.stencil_operation, + o.primitive_type, o.color_attachment_pixel_format, + o.has_stencil_attachment, o.wireframe, o.enable_rasterization); } }; @@ -315,7 +321,8 @@ struct ContentContextOptions { lhs.color_attachment_pixel_format == rhs.color_attachment_pixel_format && lhs.has_stencil_attachment == rhs.has_stencil_attachment && - lhs.wireframe == rhs.wireframe; + lhs.wireframe == rhs.wireframe && + lhs.enable_rasterization == rhs.enable_rasterization; } }; @@ -660,6 +667,12 @@ class ContentContext { return GetPipeline(framebuffer_blend_softlight_pipelines_, opts); } + std::shared_ptr> GetPointFieldGeometryPipeline( + ContentContextOptions opts) const { + FML_DCHECK(GetDeviceCapabilities().SupportsDisabledRasterization()); + return GetPipeline(point_field_geometry_pipelines_, opts); + } + std::shared_ptr GetContext() const; std::shared_ptr GetGlyphAtlasContext( @@ -783,6 +796,8 @@ class ContentContext { mutable Variants framebuffer_blend_softlight_pipelines_; + mutable Variants point_field_geometry_pipelines_; + template std::shared_ptr> GetPipeline( Variants& container, diff --git a/impeller/entity/geometry.cc b/impeller/entity/geometry.cc index cdedf311cc479..e24b324f6bf44 100644 --- a/impeller/entity/geometry.cc +++ b/impeller/entity/geometry.cc @@ -11,6 +11,7 @@ #include "impeller/entity/texture_fill.vert.h" #include "impeller/geometry/matrix.h" #include "impeller/geometry/path_builder.h" +#include "impeller/renderer/command_buffer.h" #include "impeller/renderer/render_pass.h" #include "impeller/tessellator/tessellator.h" @@ -64,6 +65,14 @@ std::unique_ptr Geometry::MakeFillPath(const Path& path) { return std::make_unique(path); } +// static +std::unique_ptr Geometry::MakePointField(std::vector points, + Scalar radius, + bool round) { + return std::make_unique(std::move(points), radius, round); +} + +// static std::unique_ptr Geometry::MakeStrokePath(const Path& path, Scalar stroke_width, Scalar miter_limit, @@ -797,4 +806,230 @@ std::optional RectGeometry::GetCoverage(const Matrix& transform) const { return rect_.TransformBounds(transform); } +/////// PointFieldGeometry Geometry /////// + +PointFieldGeometry::PointFieldGeometry(std::vector points, + Scalar radius, + bool round) + : points_(std::move(points)), radius_(radius), round_(round) {} + +PointFieldGeometry::~PointFieldGeometry() = default; + +GeometryResult PointFieldGeometry::GetPositionBuffer( + const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) { + if (radius_ < 0.0) { + return {}; + } + auto determinant = entity.GetTransformation().GetDeterminant(); + if (determinant == 0) { + return {}; + } + + Scalar min_size = 1.0f / sqrt(std::abs(determinant)); + Scalar radius = std::max(radius_, min_size); + + if (!renderer.GetDeviceCapabilities().SupportsDisabledRasterization()) { + return GetPositionBufferCPU(renderer, entity, pass, radius); + } + + auto vertices_per_geom = ComputeCircleDivisions( + entity.GetTransformation().GetMaxBasisLength() * radius, round_); + auto points_per_circle = 3 + (vertices_per_geom - 3) * 3; + auto total = points_per_circle * points_.size(); + auto& host_buffer = pass.GetTransientsBuffer(); + + using VS = PointFieldGeometryPipeline::VertexShader; + + auto vtx_buffer = VertexBuffer{ + .vertex_buffer = host_buffer.Emplace( + points_.data(), points_.size() * sizeof(Point), alignof(Point)), + .vertex_count = points_.size(), + .index_type = IndexType::kNone, + }; + + DeviceBufferDescriptor buffer_desc; + buffer_desc.size = total * sizeof(Point); + buffer_desc.storage_mode = StorageMode::kDevicePrivate; + + auto buffer = + renderer.GetContext()->GetResourceAllocator()->CreateBuffer(buffer_desc); + + Command cmd; + cmd.label = "Points Geometry"; + + auto options = OptionsFromPass(pass); + options.blend_mode = BlendMode::kSource; + options.stencil_compare = CompareFunction::kAlways; + options.stencil_operation = StencilOperation::kKeep; + options.enable_rasterization = false; + options.sample_count = SampleCount::kCount1; + cmd.pipeline = renderer.GetPointFieldGeometryPipeline(options); + + VS::FrameInfo frame_info; + frame_info.radius = radius; + frame_info.radian_start = round_ ? 0.0f : 0.785398f; + frame_info.radian_step = k2Pi / vertices_per_geom; + frame_info.points_per_circle = points_per_circle; + frame_info.divisions_per_circle = vertices_per_geom; + + VS::BindFrameInfo(cmd, host_buffer.EmplaceUniform(frame_info)); + VS::BindGeometryData( + cmd, {.buffer = buffer, .range = Range{0, total * sizeof(Point)}}); + + cmd.BindVertices(vtx_buffer); + + // Ensure correct synchronization by submitting vertex computation into + // a different render pass. + { + auto render_target = RenderTarget::CreateOffscreen( + *renderer.GetContext(), // context + {1, 1}, // size + "Geometry Snapshot", // label + RenderTarget:: + kDefaultColorAttachmentConfigNonRendering, // color_attachment_config + std::nullopt // stencil_attachment_config + ); + auto cmd_buffer = renderer.GetContext()->CreateCommandBuffer(); + auto vertex_render_pass = cmd_buffer->CreateRenderPass(render_target); + + if (!vertex_render_pass->AddCommand(std::move(cmd)) || + !vertex_render_pass->EncodeCommands() || + !cmd_buffer->SubmitCommands()) { + return {}; + } + } + + return { + .type = PrimitiveType::kTriangle, + .vertex_buffer = {.vertex_buffer = {.buffer = buffer, + .range = + Range{0, total * sizeof(Point)}}, + .vertex_count = total, + .index_type = IndexType::kNone}, + .transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + entity.GetTransformation(), + .prevent_overdraw = false, + }; +} + +GeometryResult PointFieldGeometry::GetPositionBufferCPU( + const ContentContext& renderer, + const Entity& entity, + RenderPass& pass, + Scalar radius) { + auto vertices_per_geom = ComputeCircleDivisions( + entity.GetTransformation().GetMaxBasisLength() * radius, round_); + auto points_per_circle = 3 + (vertices_per_geom - 3) * 3; + auto total = points_per_circle * points_.size(); + auto& host_buffer = pass.GetTransientsBuffer(); + auto radian_start = round_ ? 0.0f : 0.785398f; + auto radian_step = k2Pi / vertices_per_geom; + + VertexBufferBuilder vtx_builder; + vtx_builder.Reserve(total); + + /// Precompute all relative points and angles for a fixed geometry size. + auto elapsed_angle = radian_start; + std::vector angle_table(vertices_per_geom); + for (auto i = 0u; i < vertices_per_geom; i++) { + angle_table[i] = Point(cos(elapsed_angle), sin(elapsed_angle)) * radius; + elapsed_angle += radian_step; + } + + for (auto i = 0u; i < points_.size(); i++) { + auto center = points_[i]; + + auto origin = center + angle_table[0]; + vtx_builder.AppendVertex({origin}); + + auto pt1 = center + angle_table[1]; + vtx_builder.AppendVertex({pt1}); + + auto pt2 = center + angle_table[2]; + vtx_builder.AppendVertex({pt2}); + + for (auto j = 0u; j < vertices_per_geom - 3; j++) { + vtx_builder.AppendVertex({origin}); + vtx_builder.AppendVertex({pt2}); + + pt2 = center + angle_table[j + 3]; + vtx_builder.AppendVertex({pt2}); + } + } + + return { + .type = PrimitiveType::kTriangle, + .vertex_buffer = vtx_builder.CreateVertexBuffer(host_buffer), + .transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) * + entity.GetTransformation(), + .prevent_overdraw = false, + }; +} + +GeometryResult PointFieldGeometry::GetPositionUVBuffer( + Rect texture_coverage, + Matrix effect_transform, + const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) { + FML_UNREACHABLE(); +} + +/// @brief Compute the exact storage size needed to store the resulting +/// buffer. +/// @return +size_t PointFieldGeometry::ComputeCircleDivisions(Scalar scaled_radius, + bool round) { + if (!round) { + return 4; + } + + // Note: these values are approximated based on the values returned from + // the decomposition of 4 cubics performed by Path::CreatePolyline. + if (scaled_radius < 1.0) { + return 4; + } + if (scaled_radius < 2.0) { + return 8; + } + if (scaled_radius < 12.0) { + return 24; + } + if (scaled_radius < 22.0) { + return 34; + } + return std::min(scaled_radius, 140.0f); +} + +// |Geometry| +GeometryVertexType PointFieldGeometry::GetVertexType() const { + return GeometryVertexType::kPosition; +} + +// |Geometry| +std::optional PointFieldGeometry::GetCoverage( + const Matrix& transform) const { + if (points_.size() > 0) { + // Doesn't use MakePointBounds as this isn't resilient to points that + // lie along the same axis. + auto first = points_.begin(); + auto last = points_.end(); + auto left = first->x; + auto top = first->y; + auto right = first->x; + auto bottom = first->y; + for (auto it = first + 1; it < last; ++it) { + left = std::min(left, it->x); + top = std::min(top, it->y); + right = std::max(right, it->x); + bottom = std::max(bottom, it->y); + } + return Rect::MakeLTRB(left - radius_, top - radius_, right + radius_, + bottom + radius_); + } + return std::nullopt; +} + } // namespace impeller diff --git a/impeller/entity/geometry.h b/impeller/entity/geometry.h index e3dd56deda339..cac8007a6ba58 100644 --- a/impeller/entity/geometry.h +++ b/impeller/entity/geometry.h @@ -57,6 +57,10 @@ class Geometry { static std::unique_ptr MakeRect(Rect rect); + static std::unique_ptr MakePointField(std::vector points, + Scalar radius, + bool round); + virtual GeometryResult GetPositionBuffer(const ContentContext& renderer, const Entity& entity, RenderPass& pass) = 0; @@ -260,4 +264,43 @@ class RectGeometry : public Geometry { FML_DISALLOW_COPY_AND_ASSIGN(RectGeometry); }; +class PointFieldGeometry : public Geometry { + public: + PointFieldGeometry(std::vector points, Scalar radius, bool round); + + ~PointFieldGeometry(); + + private: + // |Geometry| + GeometryResult GetPositionBuffer(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) override; + + // |Geometry| + GeometryResult GetPositionUVBuffer(Rect texture_coverage, + Matrix effect_transform, + const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) override; + + // |Geometry| + GeometryVertexType GetVertexType() const override; + + // |Geometry| + std::optional GetCoverage(const Matrix& transform) const override; + + static size_t ComputeCircleDivisions(Scalar scaled_radius, bool round); + + GeometryResult GetPositionBufferCPU(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass, + Scalar radius); + + std::vector points_; + Scalar radius_; + bool round_; + + FML_DISALLOW_COPY_AND_ASSIGN(PointFieldGeometry); +}; + } // namespace impeller diff --git a/impeller/entity/shaders/geometry/points.vert b/impeller/entity/shaders/geometry/points.vert new file mode 100644 index 0000000000000..6e8910cb917c9 --- /dev/null +++ b/impeller/entity/shaders/geometry/points.vert @@ -0,0 +1,54 @@ +// 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 +#include + +uniform FrameInfo { + float16_t radius; + float16_t radian_start; + float16_t radian_step; + int points_per_circle; + int divisions_per_circle; +} +frame_info; + +layout(std430) writeonly buffer GeometryData { + vec2 geometry[]; +} +geometry_data; + +in vec2 center; + +void main() { + // The buffer offset we start writing to is the number of data per circle * + // number of previous circles. + int bufer_offset = gl_VertexIndex * frame_info.points_per_circle; + + float16_t elapsed_angle = frame_info.radian_start; + + vec2 origin = + center + vec2(cos(elapsed_angle), sin(elapsed_angle)) * frame_info.radius; + geometry_data.geometry[bufer_offset++] = origin; + + elapsed_angle += frame_info.radian_step; + vec2 pt1 = + center + vec2(cos(elapsed_angle), sin(elapsed_angle)) * frame_info.radius; + geometry_data.geometry[bufer_offset++] = pt1; + + elapsed_angle += frame_info.radian_step; + vec2 pt2 = + center + vec2(cos(elapsed_angle), sin(elapsed_angle)) * frame_info.radius; + geometry_data.geometry[bufer_offset++] = pt2; + + for (int i = 0; i < frame_info.divisions_per_circle - 3; i++) { + geometry_data.geometry[bufer_offset++] = origin; + geometry_data.geometry[bufer_offset++] = pt2; + + elapsed_angle += frame_info.radian_step; + pt2 = center + + vec2(cos(elapsed_angle), sin(elapsed_angle)) * frame_info.radius; + geometry_data.geometry[bufer_offset++] = pt2; + } +} diff --git a/impeller/renderer/backend/metal/context_mtl.mm b/impeller/renderer/backend/metal/context_mtl.mm index c83f5b7075c47..e37757e44195d 100644 --- a/impeller/renderer/backend/metal/context_mtl.mm +++ b/impeller/renderer/backend/metal/context_mtl.mm @@ -45,6 +45,17 @@ static bool DeviceSupportsComputeSubgroups(id device) { return supports_subgroups; } +static bool SupportsDisabledRasterization() { + // TOOD(jonahwilliams): on macOS playgrounds there seems to be some alignment + // issues with the device buffers but I haven't been able to trigger any sort + // of validation warnings. Disabling there for now. +#if FML_OS_IOS + return true; +#else + return false; +#endif // FML_OS_IOS +} + static std::unique_ptr InferMetalCapabilities( id device, PixelFormat color_format) { @@ -62,6 +73,7 @@ static bool DeviceSupportsComputeSubgroups(id device) { .SetSupportsComputeSubgroups(DeviceSupportsComputeSubgroups(device)) .SetSupportsReadFromResolve(true) .SetSupportsReadFromOnscreenTexture(true) + .SetSupportsDisabledRasterization(SupportsDisabledRasterization()) .Build(); } diff --git a/impeller/renderer/backend/metal/pipeline_library_mtl.mm b/impeller/renderer/backend/metal/pipeline_library_mtl.mm index c65e31f1e7a43..e7b74e55499e2 100644 --- a/impeller/renderer/backend/metal/pipeline_library_mtl.mm +++ b/impeller/renderer/backend/metal/pipeline_library_mtl.mm @@ -27,6 +27,7 @@ auto descriptor = [[MTLRenderPipelineDescriptor alloc] init]; descriptor.label = @(desc.GetLabel().c_str()); descriptor.rasterSampleCount = static_cast(desc.GetSampleCount()); + descriptor.rasterizationEnabled = desc.GetRasterizationEnabled(); for (const auto& entry : desc.GetStageEntrypoints()) { if (entry.first == ShaderStage::kVertex) { diff --git a/impeller/renderer/backend/vulkan/capabilities_vk.cc b/impeller/renderer/backend/vulkan/capabilities_vk.cc index ff4410f15935a..1416ed07e0436 100644 --- a/impeller/renderer/backend/vulkan/capabilities_vk.cc +++ b/impeller/renderer/backend/vulkan/capabilities_vk.cc @@ -352,6 +352,10 @@ bool CapabilitiesVK::SupportsDecalTileMode() const { return true; } +bool CapabilitiesVK::SupportsDisabledRasterization() const { + return true; +} + // |Capabilities| PixelFormat CapabilitiesVK::GetDefaultColorFormat() const { return color_format_; diff --git a/impeller/renderer/backend/vulkan/capabilities_vk.h b/impeller/renderer/backend/vulkan/capabilities_vk.h index 006ed6f0e0889..90d01b445bd9f 100644 --- a/impeller/renderer/backend/vulkan/capabilities_vk.h +++ b/impeller/renderer/backend/vulkan/capabilities_vk.h @@ -79,6 +79,9 @@ class CapabilitiesVK final : public Capabilities, // |Capabilities| bool SupportsDecalTileMode() const override; + // |Capabilities| + bool SupportsDisabledRasterization() const override; + // |Capabilities| PixelFormat GetDefaultColorFormat() const override; diff --git a/impeller/renderer/backend/vulkan/render_pass_vk.cc b/impeller/renderer/backend/vulkan/render_pass_vk.cc index 87a201eae43df..40c8da7fe8420 100644 --- a/impeller/renderer/backend/vulkan/render_pass_vk.cc +++ b/impeller/renderer/backend/vulkan/render_pass_vk.cc @@ -522,7 +522,6 @@ static bool EncodeCommand(const Context& context, 0u // first instance ); } else { - FML_LOG(ERROR) << "HERE!!!"; cmd_buffer.draw(command.vertex_count, // vertex count command.instance_count, // instance count command.base_vertex, // vertex offset diff --git a/impeller/renderer/capabilities.cc b/impeller/renderer/capabilities.cc index d0531f5f265b2..bb41c2644261a 100644 --- a/impeller/renderer/capabilities.cc +++ b/impeller/renderer/capabilities.cc @@ -66,6 +66,11 @@ class StandardCapabilities final : public Capabilities { return supports_decal_tile_mode_; } + // |Capabilities| + bool SupportsDisabledRasterization() const override { + return supports_disabled_rasterization_; + } + // |Capabilities| PixelFormat GetDefaultColorFormat() const override { return default_color_format_; @@ -88,6 +93,7 @@ class StandardCapabilities final : public Capabilities { bool supports_read_from_onscreen_texture, bool supports_read_from_resolve, bool supports_decal_tile_mode, + bool supports_disabled_rasterization, PixelFormat default_color_format, PixelFormat default_stencil_format) : has_threading_restrictions_(has_threading_restrictions), @@ -102,6 +108,7 @@ class StandardCapabilities final : public Capabilities { supports_read_from_onscreen_texture), supports_read_from_resolve_(supports_read_from_resolve), supports_decal_tile_mode_(supports_decal_tile_mode), + supports_disabled_rasterization_(supports_disabled_rasterization), default_color_format_(default_color_format), default_stencil_format_(default_stencil_format) {} @@ -118,6 +125,7 @@ class StandardCapabilities final : public Capabilities { bool supports_read_from_onscreen_texture_ = false; bool supports_read_from_resolve_ = false; bool supports_decal_tile_mode_ = false; + bool supports_disabled_rasterization_ = false; PixelFormat default_color_format_ = PixelFormat::kUnknown; PixelFormat default_stencil_format_ = PixelFormat::kUnknown; @@ -202,6 +210,12 @@ CapabilitiesBuilder& CapabilitiesBuilder::SetSupportsDecalTileMode(bool value) { return *this; } +CapabilitiesBuilder& CapabilitiesBuilder::SetSupportsDisabledRasterization( + bool value) { + supports_disabled_rasterization_ = value; + return *this; +} + std::unique_ptr CapabilitiesBuilder::Build() { return std::unique_ptr(new StandardCapabilities( // has_threading_restrictions_, // @@ -215,6 +229,7 @@ std::unique_ptr CapabilitiesBuilder::Build() { supports_read_from_onscreen_texture_, // supports_read_from_resolve_, // supports_decal_tile_mode_, // + supports_disabled_rasterization_, // *default_color_format_, // *default_stencil_format_ // )); diff --git a/impeller/renderer/capabilities.h b/impeller/renderer/capabilities.h index 6fb1118dd7c95..a0ce72baa0f62 100644 --- a/impeller/renderer/capabilities.h +++ b/impeller/renderer/capabilities.h @@ -37,6 +37,8 @@ class Capabilities { virtual bool SupportsDecalTileMode() const = 0; + virtual bool SupportsDisabledRasterization() const = 0; + virtual PixelFormat GetDefaultColorFormat() const = 0; virtual PixelFormat GetDefaultStencilFormat() const = 0; @@ -79,6 +81,8 @@ class CapabilitiesBuilder { CapabilitiesBuilder& SetSupportsDecalTileMode(bool value); + CapabilitiesBuilder& SetSupportsDisabledRasterization(bool value); + std::unique_ptr Build(); private: @@ -93,6 +97,7 @@ class CapabilitiesBuilder { bool supports_read_from_onscreen_texture_ = false; bool supports_read_from_resolve_ = false; bool supports_decal_tile_mode_ = false; + bool supports_disabled_rasterization_ = false; std::optional default_color_format_ = std::nullopt; std::optional default_stencil_format_ = std::nullopt; diff --git a/impeller/renderer/capabilities_unittests.cc b/impeller/renderer/capabilities_unittests.cc index 1dc9d17e3c678..1b95a01481e1f 100644 --- a/impeller/renderer/capabilities_unittests.cc +++ b/impeller/renderer/capabilities_unittests.cc @@ -29,6 +29,7 @@ CAPABILITY_TEST(SupportsComputeSubgroups, false); CAPABILITY_TEST(SupportsReadFromOnscreenTexture, false); CAPABILITY_TEST(SupportsReadFromResolve, false); CAPABILITY_TEST(SupportsDecalTileMode, false); +CAPABILITY_TEST(SupportsDisabledRasterization, false); } // namespace testing } // namespace impeller diff --git a/impeller/renderer/pipeline_builder.h b/impeller/renderer/pipeline_builder.h index b27c4bc753b55..a567f43c23093 100644 --- a/impeller/renderer/pipeline_builder.h +++ b/impeller/renderer/pipeline_builder.h @@ -16,6 +16,15 @@ namespace impeller { +class NonRenderingFragment { + public: + static constexpr std::string_view kLabel = ""; + static constexpr std::string_view kEntrypointName = ""; + static constexpr ShaderStage kShaderStage = ShaderStage::kFragment; + static constexpr std::string_view kGeneratorName = ""; + static constexpr std::array kDescriptorSetLayouts{}; +}; + //------------------------------------------------------------------------------ /// @brief An optional (but highly recommended) utility for creating /// pipelines from reflected shader information. @@ -52,35 +61,54 @@ struct PipelineBuilder { PipelineDescriptor desc; if (InitializePipelineDescriptorDefaults(context, desc)) { return {std::move(desc)}; - } else { - return std::nullopt; } + return std::nullopt; } [[nodiscard]] static bool InitializePipelineDescriptorDefaults( const Context& context, PipelineDescriptor& desc) { // Setup debug instrumentation. - desc.SetLabel(SPrintF("%s Pipeline", FragmentShader::kLabel.data())); + if (!std::is_same::value) { + desc.SetLabel(SPrintF("%s Pipeline", FragmentShader::kLabel.data())); + } else { + desc.SetLabel(SPrintF("%s Pipeline", VertexShader::kLabel.data())); + } // Resolve pipeline entrypoints. { auto vertex_function = context.GetShaderLibrary()->GetFunction( VertexShader::kEntrypointName, ShaderStage::kVertex); - auto fragment_function = context.GetShaderLibrary()->GetFunction( - FragmentShader::kEntrypointName, ShaderStage::kFragment); - - if (!vertex_function || !fragment_function) { - VALIDATION_LOG << "Could not resolve pipeline entrypoint(s) '" - << VertexShader::kEntrypointName << "' and '" - << FragmentShader::kEntrypointName - << "' for pipeline named '" << VertexShader::kLabel - << "'."; - return false; + + std::shared_ptr fragment_function; + if (!std::is_same::value) { + fragment_function = context.GetShaderLibrary()->GetFunction( + FragmentShader::kEntrypointName, ShaderStage::kFragment); + } + + if (!std::is_same::value) { + if (!vertex_function || !fragment_function) { + VALIDATION_LOG << "Could not resolve pipeline entrypoint(s) '" + << VertexShader::kEntrypointName << "' and '" + << FragmentShader::kEntrypointName + << "' for pipeline named '" << VertexShader::kLabel + << "'."; + return false; + } + } else { + if (!vertex_function) { + VALIDATION_LOG << "Could not resolve pipeline entrypoint(s) '" + << FragmentShader::kEntrypointName + << "' for pipeline named '" << VertexShader::kLabel + << "'."; + return false; + } } desc.AddStageEntrypoint(std::move(vertex_function)); - desc.AddStageEntrypoint(std::move(fragment_function)); + if (fragment_function != nullptr) { + desc.AddStageEntrypoint(std::move(fragment_function)); + } } // Setup the vertex descriptor from reflected information. diff --git a/impeller/renderer/pipeline_descriptor.cc b/impeller/renderer/pipeline_descriptor.cc index eef517b30c847..a0541a60c4168 100644 --- a/impeller/renderer/pipeline_descriptor.cc +++ b/impeller/renderer/pipeline_descriptor.cc @@ -42,6 +42,7 @@ std::size_t PipelineDescriptor::GetHash() const { fml::HashCombineSeed(seed, cull_mode_); fml::HashCombineSeed(seed, primitive_type_); fml::HashCombineSeed(seed, polygon_mode_); + fml::HashCombineSeed(seed, enable_rasterization_); return seed; } @@ -61,7 +62,8 @@ bool PipelineDescriptor::IsEqual(const PipelineDescriptor& other) const { winding_order_ == other.winding_order_ && cull_mode_ == other.cull_mode_ && primitive_type_ == other.primitive_type_ && - polygon_mode_ == other.polygon_mode_; + polygon_mode_ == other.polygon_mode_ && + enable_rasterization_ == other.enable_rasterization_; } PipelineDescriptor& PipelineDescriptor::SetLabel(std::string label) { @@ -235,6 +237,14 @@ PixelFormat PipelineDescriptor::GetDepthPixelFormat() const { return depth_pixel_format_; } +void PipelineDescriptor::SetEnableRasterization(bool value) { + enable_rasterization_ = value; +} + +bool PipelineDescriptor::GetRasterizationEnabled() const { + return enable_rasterization_; +} + std::optional PipelineDescriptor::GetBackStencilAttachmentDescriptor() const { return back_stencil_attachment_descriptor_; diff --git a/impeller/renderer/pipeline_descriptor.h b/impeller/renderer/pipeline_descriptor.h index 3d0eddf880e28..0769c7231a585 100644 --- a/impeller/renderer/pipeline_descriptor.h +++ b/impeller/renderer/pipeline_descriptor.h @@ -131,6 +131,10 @@ class PipelineDescriptor final : public Comparable { PolygonMode GetPolygonMode() const; + void SetEnableRasterization(bool value); + + bool GetRasterizationEnabled() const; + private: std::string label_; SampleCount sample_count_ = SampleCount::kCount1; @@ -149,6 +153,7 @@ class PipelineDescriptor final : public Comparable { back_stencil_attachment_descriptor_; PrimitiveType primitive_type_ = PrimitiveType::kTriangle; PolygonMode polygon_mode_ = PolygonMode::kFill; + bool enable_rasterization_ = true; }; } // namespace impeller diff --git a/impeller/renderer/render_target.h b/impeller/renderer/render_target.h index bc54976a030a8..2b44a3c9445e0 100644 --- a/impeller/renderer/render_target.h +++ b/impeller/renderer/render_target.h @@ -53,6 +53,12 @@ class RenderTarget final { .store_action = StoreAction::kDontCare, .clear_color = Color::BlackTransparent()}; + static constexpr AttachmentConfig kDefaultColorAttachmentConfigNonRendering = + {.storage_mode = StorageMode::kDevicePrivate, + .load_action = LoadAction::kDontCare, + .store_action = StoreAction::kDontCare, + .clear_color = Color::BlackTransparent()}; + static RenderTarget CreateOffscreen( const Context& context, ISize size, diff --git a/impeller/tools/malioc.json b/impeller/tools/malioc.json index 0ea54a4a9d0c4..2361bcb96c997 100644 --- a/impeller/tools/malioc.json +++ b/impeller/tools/malioc.json @@ -12067,6 +12067,67 @@ } } }, + "flutter/impeller/entity/points.vert.vkspv": { + "Mali-G78": { + "core": "Mali-G78", + "filename": "flutter/impeller/entity/points.vert.vkspv", + "has_uniform_computation": true, + "type": "Vertex", + "variants": { + "Position": { + "fp16_arithmetic": 35, + "has_stack_spilling": false, + "performance": { + "longest_path_bound_pipelines": [ + null + ], + "longest_path_cycles": [ + null, + null, + null, + null, + null, + null + ], + "pipelines": [ + "arith_total", + "arith_fma", + "arith_cvt", + "arith_sfu", + "load_store", + "texture" + ], + "shortest_path_bound_pipelines": [ + "load_store" + ], + "shortest_path_cycles": [ + 0.203125, + 0.046875, + 0.203125, + 0.0625, + 3.0, + 0.0 + ], + "total_bound_pipelines": [ + "load_store" + ], + "total_cycles": [ + 0.375, + 0.21875, + 0.375, + 0.1875, + 5.0, + 0.0 + ] + }, + "stack_spill_bytes": 0, + "thread_occupancy": 100, + "uniform_registers_used": 14, + "work_registers_used": 17 + } + } + } + }, "flutter/impeller/entity/porter_duff_blend.frag.vkspv": { "Mali-G78": { "core": "Mali-G78",