blob: 1cab24c1fb861e2506c5e837f80bfef4ecb4c6f2 [file] [log] [blame]
// 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/display_list/display_list_vertices_geometry.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/entity.h"
#include "impeller/entity/position_color.vert.h"
#include "impeller/geometry/matrix.h"
#include "impeller/geometry/path_builder.h"
#include "impeller/geometry/point.h"
#include "impeller/renderer/device_buffer.h"
#include "impeller/renderer/render_pass.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);
}
// Fan mode isn't natively supported. 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(
const flutter::DlVertices* vertices) {
FML_DCHECK(vertices->vertex_count() >= 3);
FML_DCHECK(vertices->mode() == flutter::DlVertexMode::kTriangleFan);
std::vector<uint16_t> indices;
// Un-fan index buffer if provided.
if (vertices->index_count() > 0) {
auto* dl_indices = vertices->indices();
auto center_point = dl_indices[0];
for (int i = 1; i < vertices->index_count() - 1; i++) {
indices.push_back(center_point);
indices.push_back(dl_indices[i]);
indices.push_back(dl_indices[i + 1]);
}
} else {
// If indices were not provided, create an index buffer that unfans
// triangles instead of re-writing points, colors, et cetera.
for (int i = 1; i < vertices->vertex_count() - 1; i++) {
indices.push_back(0);
indices.push_back(i);
indices.push_back(i + 1);
}
}
return indices;
}
/////// Vertices Geometry ///////
// static
std::unique_ptr<VerticesGeometry> DLVerticesGeometry::MakeVertices(
const flutter::DlVertices* vertices) {
return std::make_unique<DLVerticesGeometry>(vertices);
}
DLVerticesGeometry::DLVerticesGeometry(const flutter::DlVertices* vertices)
: vertices_(vertices) {
NormalizeIndices();
}
DLVerticesGeometry::~DLVerticesGeometry() = default;
void DLVerticesGeometry::NormalizeIndices() {
// Convert triangle fan if present.
if (vertices_->mode() == flutter::DlVertexMode::kTriangleFan) {
normalized_indices_ = fromFanIndices(vertices_);
return;
}
auto index_count = vertices_->index_count();
auto vertex_count = vertices_->vertex_count();
if (index_count != 0 || vertex_count == 0) {
return;
}
normalized_indices_.reserve(vertex_count);
for (auto i = 0; i < vertex_count; i++) {
normalized_indices_.push_back(i);
}
}
static PrimitiveType GetPrimitiveType(const flutter::DlVertices* vertices) {
switch (vertices->mode()) {
case flutter::DlVertexMode::kTriangles:
return PrimitiveType::kTriangle;
case flutter::DlVertexMode::kTriangleStrip:
return PrimitiveType::kTriangleStrip;
case flutter::DlVertexMode::kTriangleFan:
// Unrolled into triangle mode.
return PrimitiveType::kTriangle;
}
}
GeometryResult DLVerticesGeometry::GetPositionBuffer(
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) {
auto index_count = normalized_indices_.size() == 0
? vertices_->index_count()
: normalized_indices_.size();
auto vertex_count = vertices_->vertex_count();
auto* dl_indices = normalized_indices_.size() == 0
? vertices_->indices()
: normalized_indices_.data();
auto* dl_vertices = vertices_->vertices();
size_t total_vtx_bytes = vertex_count * sizeof(float) * 2;
size_t total_idx_bytes = index_count * sizeof(uint16_t);
DeviceBufferDescriptor buffer_desc;
buffer_desc.size = total_vtx_bytes + total_idx_bytes;
buffer_desc.storage_mode = StorageMode::kHostVisible;
auto buffer =
renderer.GetContext()->GetResourceAllocator()->CreateBuffer(buffer_desc);
if (!buffer->CopyHostBuffer(reinterpret_cast<const uint8_t*>(dl_vertices),
Range{0, total_vtx_bytes}, 0)) {
return {};
}
if (!buffer->CopyHostBuffer(
reinterpret_cast<uint8_t*>(const_cast<uint16_t*>(dl_indices)),
Range{0, total_idx_bytes}, total_vtx_bytes)) {
return {};
}
return GeometryResult{
.type = GetPrimitiveType(vertices_),
.vertex_buffer =
{
.vertex_buffer = {.buffer = buffer,
.range = Range{0, total_vtx_bytes}},
.index_buffer = {.buffer = buffer,
.range =
Range{total_vtx_bytes, total_idx_bytes}},
.index_count = index_count,
.index_type = IndexType::k16bit,
},
.transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
entity.GetTransformation(),
.prevent_overdraw = false,
};
}
GeometryResult DLVerticesGeometry::GetPositionColorBuffer(
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
Color paint_color,
BlendMode blend_mode) {
using VS = GeometryColorPipeline::VertexShader;
auto index_count = normalized_indices_.size() == 0
? vertices_->index_count()
: normalized_indices_.size();
auto vertex_count = vertices_->vertex_count();
auto* dl_indices = normalized_indices_.size() == 0
? vertices_->indices()
: normalized_indices_.data();
auto* dl_vertices = vertices_->vertices();
auto* dl_colors = vertices_->colors();
std::vector<VS::PerVertexData> vertex_data(vertex_count);
{
for (auto i = 0; i < vertex_count; i++) {
auto dl_color = dl_colors[i];
auto pre_color = Color(dl_color.getRedF(), dl_color.getGreenF(),
dl_color.getBlueF(), dl_color.getAlphaF());
auto color = Color::BlendColor(paint_color, pre_color, blend_mode);
auto sk_point = dl_vertices[i];
vertex_data[i] = {
.position = Point(sk_point.x(), sk_point.y()),
.color = color,
};
}
}
size_t total_vtx_bytes = vertex_data.size() * sizeof(VS::PerVertexData);
size_t total_idx_bytes = index_count * sizeof(uint16_t);
DeviceBufferDescriptor buffer_desc;
buffer_desc.size = total_vtx_bytes + total_idx_bytes;
buffer_desc.storage_mode = StorageMode::kHostVisible;
auto buffer =
renderer.GetContext()->GetResourceAllocator()->CreateBuffer(buffer_desc);
if (!buffer->CopyHostBuffer(reinterpret_cast<uint8_t*>(vertex_data.data()),
Range{0, total_vtx_bytes}, 0)) {
return {};
}
if (!buffer->CopyHostBuffer(
reinterpret_cast<uint8_t*>(const_cast<uint16_t*>(dl_indices)),
Range{0, total_idx_bytes}, total_vtx_bytes)) {
return {};
}
return GeometryResult{
.type = GetPrimitiveType(vertices_),
.vertex_buffer =
{
.vertex_buffer = {.buffer = buffer,
.range = Range{0, total_vtx_bytes}},
.index_buffer = {.buffer = buffer,
.range =
Range{total_vtx_bytes, total_idx_bytes}},
.index_count = index_count,
.index_type = IndexType::k16bit,
},
.transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
entity.GetTransformation(),
.prevent_overdraw = false,
};
}
GeometryResult DLVerticesGeometry::GetPositionUVBuffer(
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) {
// TODO(jonahwilliams): support texture coordinates in vertices
// https://github.com/flutter/flutter/issues/109956
return {};
}
GeometryVertexType DLVerticesGeometry::GetVertexType() const {
auto* dl_colors = vertices_->colors();
if (dl_colors != nullptr) {
return GeometryVertexType::kColor;
}
return GeometryVertexType::kPosition;
}
std::optional<Rect> DLVerticesGeometry::GetCoverage(
const Matrix& transform) const {
return ToRect(vertices_->bounds());
}
} // namespace impeller