Skip to content

Commit

Permalink
[Impeller] add triangle fan support and remove drawVertices copying. (#…
Browse files Browse the repository at this point in the history
…55236)

Vulkan and GLES backends support triangle fans, so only "un-fan" these geometries when necessary. Now that dispatch gives the vertices geometry as a shared_ptr, we can hold onto that directly without performing another copy.
  • Loading branch information
jonahwilliams authored Sep 23, 2024
1 parent a99c747 commit 2561ea1
Show file tree
Hide file tree
Showing 20 changed files with 321 additions and 292 deletions.
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 @@ -1077,6 +1077,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

0 comments on commit 2561ea1

Please sign in to comment.