Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

[Impeller] add triangle fan support and remove drawVertices copying. #55236

Merged
merged 2 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions impeller/core/formats.h
Original file line number Diff line number Diff line change
Expand Up @@ -350,13 +350,13 @@ enum class IndexType {

/// Decides how backend draws pixels based on input vertices.
enum class PrimitiveType : uint8_t {
/// Draws a triage for each separate set of three vertices.
/// Draws a triangle for each separate set of three vertices.
///
/// Vertices [A, B, C, D, E, F] will produce triages
/// [ABC, DEF].
kTriangle,

/// Draws a triage for every adjacent three vertices.
/// Draws a triangle for every adjacent three vertices.
///
/// Vertices [A, B, C, D, E, F] will produce triages
/// [ABC, BCD, CDE, DEF].
Expand All @@ -376,8 +376,14 @@ enum class PrimitiveType : uint8_t {

/// Draws a point at each input vertex.
kPoint,
// Triangle fans are implementation dependent and need extra extensions
// checks. Hence, they are not supported here.

/// Draws a triangle for every two vertices, after the first.
///
/// The first vertex acts as the hub, all following vertices connect with
/// this hub to "fan" out from the first vertex.
///
/// Triangle fans are not supported in Metal and need a capability check.
kTriangleFan,
};

enum class PolygonMode {
Expand Down
1 change: 1 addition & 0 deletions impeller/display_list/aiks_dl_blur_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1076,6 +1076,7 @@ TEST_P(AiksTest, GaussianBlurWithoutDecalSupport) {
FLT_FORWARD(mock_capabilities, old_capabilities,
SupportsTextureToTextureBlits);
FLT_FORWARD(mock_capabilities, old_capabilities, GetDefaultGlyphAtlasFormat);
FLT_FORWARD(mock_capabilities, old_capabilities, SupportsTriangleFan);
ASSERT_TRUE(SetCapabilities(mock_capabilities).ok());

auto texture = DlImageImpeller::Make(CreateTextureForFixture("boston.jpg"));
Expand Down
14 changes: 10 additions & 4 deletions impeller/display_list/dl_dispatcher.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1076,14 +1076,19 @@ void DlDispatcherBase::drawPoints(PointMode mode,
}
}

// |flutter::DlOpReceiver|
void DlDispatcherBase::drawVertices(
const std::shared_ptr<flutter::DlVertices>& vertices,
flutter::DlBlendMode dl_mode) {}

// |flutter::DlOpReceiver|
void ExperimentalDlDispatcher::drawVertices(
const std::shared_ptr<flutter::DlVertices>& vertices,
flutter::DlBlendMode dl_mode) {
AUTO_DEPTH_WATCHER(1u);

GetCanvas().DrawVertices(MakeVertices(vertices), ToBlendMode(dl_mode),
paint_);
GetCanvas().DrawVertices(
std::make_shared<DlVerticesGeometry>(vertices, renderer_),
ToBlendMode(dl_mode), paint_);
}

// |flutter::DlOpReceiver|
Expand Down Expand Up @@ -1349,7 +1354,8 @@ ExperimentalDlDispatcher::ExperimentalDlDispatcher(
bool has_root_backdrop_filter,
flutter::DlBlendMode max_root_blend_mode,
IRect cull_rect)
: canvas_(renderer,
: renderer_(renderer),
canvas_(renderer,
render_target,
has_root_backdrop_filter ||
RequiresReadbackForBlends(renderer, max_root_blend_mode),
Expand Down
8 changes: 6 additions & 2 deletions impeller/display_list/dl_dispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
#include "impeller/aiks/experimental_canvas.h"
#include "impeller/aiks/paint.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/geometry/color.h"

namespace impeller {

Expand Down Expand Up @@ -236,7 +235,7 @@ class DlDispatcherBase : public flutter::DlOpReceiver {

virtual Canvas& GetCanvas() = 0;

private:
protected:
Paint paint_;
Matrix initial_matrix_;

Expand Down Expand Up @@ -314,7 +313,12 @@ class ExperimentalDlDispatcher : public DlDispatcherBase {

void FinishRecording() { canvas_.EndReplay(); }

// |flutter::DlOpReceiver|
void drawVertices(const std::shared_ptr<flutter::DlVertices>& vertices,
flutter::DlBlendMode dl_mode) override;

private:
const ContentContext& renderer_;
ExperimentalCanvas canvas_;

Canvas& GetCanvas() override;
Expand Down
235 changes: 197 additions & 38 deletions impeller/display_list/dl_vertices_geometry.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,61 +5,220 @@
#include "impeller/display_list/dl_vertices_geometry.h"

#include "display_list/dl_vertices.h"
#include "impeller/core/formats.h"
#include "impeller/display_list/skia_conversions.h"
#include "impeller/entity/geometry/vertices_geometry.h"
#include "impeller/geometry/point.h"
#include "third_party/skia/include/core/SkPoint.h"
#include "third_party/skia/include/core/SkRect.h"

namespace impeller {

static Rect ToRect(const SkRect& rect) {
return Rect::MakeLTRB(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
namespace {

// Fan mode isn't natively supported on Metal backends. Unroll into triangle
// mode by manipulating the index array.
//
// In Triangle fan, the first vertex is shared across all triangles, and then
// each sliding window of two vertices plus that first vertex defines a
// triangle.
static std::vector<uint16_t> fromFanIndices(size_t vertex_count,
size_t index_count,
const uint16_t* indices) {
std::vector<uint16_t> unrolled_indices;

// Un-fan index buffer if provided.
if (index_count > 0u) {
if (index_count < 3u) {
return {};
}

auto center_point = indices[0];
for (auto i = 1u; i < index_count - 1; i++) {
unrolled_indices.push_back(center_point);
unrolled_indices.push_back(indices[i]);
unrolled_indices.push_back(indices[i + 1]);
}
} else {
if (vertex_count < 3u) {
return {};
}

// If indices were not provided, create an index buffer that unfans
// triangles instead of re-writing points, colors, et cetera.
for (auto i = 1u; i < vertex_count - 1; i++) {
unrolled_indices.push_back(0);
unrolled_indices.push_back(i);
unrolled_indices.push_back(i + 1);
}
}
return unrolled_indices;
}

static VerticesGeometry::VertexMode ToVertexMode(flutter::DlVertexMode mode) {
switch (mode) {
case flutter::DlVertexMode::kTriangles:
return VerticesGeometry::VertexMode::kTriangles;
case flutter::DlVertexMode::kTriangleStrip:
return VerticesGeometry::VertexMode::kTriangleStrip;
} // namespace

/////// Vertices Geometry ///////

DlVerticesGeometry::DlVerticesGeometry(
const std::shared_ptr<const flutter::DlVertices>& vertices,
const ContentContext& renderer)
: vertices_(vertices) {
performed_normalization_ = MaybePerformIndexNormalization(renderer);
bounds_ = skia_conversions::ToRect(vertices_->bounds());
}

PrimitiveType DlVerticesGeometry::GetPrimitiveType() const {
switch (vertices_->mode()) {
case flutter::DlVertexMode::kTriangleFan:
return VerticesGeometry::VertexMode::kTriangleFan;
};
// Unrolled into triangle mode.
if (performed_normalization_) {
return PrimitiveType::kTriangle;
}
return PrimitiveType::kTriangleFan;
case flutter::DlVertexMode::kTriangleStrip:
return PrimitiveType::kTriangleStrip;
case flutter::DlVertexMode::kTriangles:
return PrimitiveType::kTriangle;
}
}

bool DlVerticesGeometry::HasVertexColors() const {
return vertices_->colors() != nullptr;
}

std::shared_ptr<impeller::VerticesGeometry> MakeVertices(
const std::shared_ptr<const flutter::DlVertices>& vertices) {
auto bounds = ToRect(vertices->bounds());
auto mode = ToVertexMode(vertices->mode());
std::vector<Point> positions(vertices->vertex_count());
for (auto i = 0; i < vertices->vertex_count(); i++) {
positions[i] = skia_conversions::ToPoint(vertices->vertices()[i]);
bool DlVerticesGeometry::HasTextureCoordinates() const {
return vertices_->texture_coordinates() != nullptr;
}

std::optional<Rect> DlVerticesGeometry::GetTextureCoordinateCoverge() const {
if (!HasTextureCoordinates()) {
return std::nullopt;
}
auto vertex_count = vertices_->vertex_count();
if (vertex_count == 0) {
return std::nullopt;
}

std::vector<uint16_t> indices(vertices->index_count());
for (auto i = 0; i < vertices->index_count(); i++) {
indices[i] = vertices->indices()[i];
auto first = vertices_->texture_coordinates();
auto left = first->x();
auto top = first->y();
auto right = first->x();
auto bottom = first->y();
int i = 1;
for (auto it = first + 1; i < vertex_count; ++it, i++) {
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, top, right, bottom);
}

std::vector<Color> colors;
if (vertices->colors()) {
colors.reserve(vertices->vertex_count());
for (auto i = 0; i < vertices->vertex_count(); i++) {
colors.push_back(
skia_conversions::ToColor(vertices->colors()[i]).Premultiply());
}
GeometryResult DlVerticesGeometry::GetPositionBuffer(
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
int vertex_count = vertices_->vertex_count();
BufferView vertex_buffer = renderer.GetTransientsBuffer().Emplace(
vertices_->vertices(), vertex_count * sizeof(SkPoint), alignof(SkPoint));

BufferView index_buffer = {};
auto index_count =
performed_normalization_ ? indices_.size() : vertices_->index_count();
const uint16_t* indices_data =
performed_normalization_ ? indices_.data() : vertices_->indices();
if (index_count) {
index_buffer = renderer.GetTransientsBuffer().Emplace(
indices_data, index_count * sizeof(uint16_t), alignof(uint16_t));
}
std::vector<Point> texture_coordinates;
if (vertices->texture_coordinates()) {
texture_coordinates.reserve(vertices->vertex_count());
for (auto i = 0; i < vertices->vertex_count(); i++) {
texture_coordinates.push_back(
skia_conversions::ToPoint(vertices->texture_coordinates()[i]));
}

return GeometryResult{
.type = GetPrimitiveType(),
.vertex_buffer =
{
.vertex_buffer = vertex_buffer,
.index_buffer = index_buffer,
.vertex_count = index_count > 0 ? index_count : vertex_count,
.index_type =
index_count > 0 ? IndexType::k16bit : IndexType::kNone,
},
.transform = entity.GetShaderTransform(pass),
};
}

GeometryResult DlVerticesGeometry::GetPositionUVColorBuffer(
Rect texture_coverage,
Matrix effect_transform,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
using VS = PorterDuffBlendPipeline::VertexShader;

int vertex_count = vertices_->vertex_count();
Matrix uv_transform =
texture_coverage.GetNormalizingTransform() * effect_transform;
bool has_texture_coordinates = HasTextureCoordinates();
bool has_colors = HasVertexColors();

const SkPoint* coordinates = has_texture_coordinates
? vertices_->texture_coordinates()
: vertices_->vertices();
BufferView vertex_buffer = renderer.GetTransientsBuffer().Emplace(
vertex_count * sizeof(VS::PerVertexData), alignof(VS::PerVertexData),
[&](uint8_t* data) {
VS::PerVertexData* vtx_contents =
reinterpret_cast<VS::PerVertexData*>(data);
for (auto i = 0; i < vertex_count; i++) {
Point texture_coord = skia_conversions::ToPoint(coordinates[i]);
Point uv = uv_transform * texture_coord;
Color color = has_colors
? skia_conversions::ToColor(vertices_->colors()[i])
.Premultiply()
: Color::BlackTransparent();
VS::PerVertexData vertex_data = {
.vertices = skia_conversions::ToPoint(vertices_->vertices()[i]),
.texture_coords = uv,
.color = color};
vtx_contents[i] = vertex_data;
}
});

BufferView index_buffer = {};
auto index_count =
performed_normalization_ ? indices_.size() : vertices_->index_count();
const uint16_t* indices_data =
performed_normalization_ ? indices_.data() : vertices_->indices();
if (index_count) {
index_buffer = renderer.GetTransientsBuffer().Emplace(
indices_data, index_count * sizeof(uint16_t), alignof(uint16_t));
}

return GeometryResult{
.type = GetPrimitiveType(),
.vertex_buffer =
{
.vertex_buffer = vertex_buffer,
.index_buffer = index_buffer,
.vertex_count = index_count > 0 ? index_count : vertex_count,
.index_type =
index_count > 0 ? IndexType::k16bit : IndexType::kNone,
},
.transform = entity.GetShaderTransform(pass),
};
}

std::optional<Rect> DlVerticesGeometry::GetCoverage(
const Matrix& transform) const {
return bounds_.TransformBounds(transform);
}

bool DlVerticesGeometry::MaybePerformIndexNormalization(
const ContentContext& renderer) {
if (vertices_->mode() == flutter::DlVertexMode::kTriangleFan &&
!renderer.GetDeviceCapabilities().SupportsTriangleFan()) {
indices_ = fromFanIndices(vertices_->vertex_count(),
vertices_->index_count(), vertices_->indices());
return true;
}
return std::make_shared<VerticesGeometry>(
positions, indices, texture_coordinates, colors, bounds, mode);
return false;
}

} // namespace impeller
Loading