blob: 19662297cc6eb36f0b6f3bd6107325f81f9ab06b [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/entity/texture_fill.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::shared_ptr<VerticesGeometry> DLVerticesGeometry::MakeVertices(
const flutter::DlVertices* vertices) {
return std::make_shared<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;
}
}
std::optional<Rect> DLVerticesGeometry::GetTextureCoordinateCoverge() const {
if (!HasTextureCoordinates()) {
return std::nullopt;
}
auto vertex_count = vertices_->vertex_count();
auto* dl_texture_coordinates = vertices_->texture_coordinates();
if (vertex_count == 0) {
return std::nullopt;
}
auto left = dl_texture_coordinates[0].x();
auto top = dl_texture_coordinates[0].y();
auto right = dl_texture_coordinates[0].x();
auto bottom = dl_texture_coordinates[0].y();
for (auto i = 0; i < vertex_count; i++) {
left = std::min(left, dl_texture_coordinates[i].x());
top = std::min(top, dl_texture_coordinates[i].y());
right = std::max(right, dl_texture_coordinates[i].x());
bottom = std::max(bottom, dl_texture_coordinates[i].y());
}
return Rect::MakeLTRB(left, top, right, bottom);
}
bool DLVerticesGeometry::HasVertexColors() const {
return vertices_->colors() != nullptr;
}
bool DLVerticesGeometry::HasTextureCoordinates() const {
return vertices_->texture_coordinates() != nullptr;
}
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) {
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 color = Color(dl_color.getRedF(), dl_color.getGreenF(),
dl_color.getBlueF(), dl_color.getAlphaF())
.Premultiply();
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(
Rect texture_coverage,
Matrix effect_transform,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) {
using VS = TexturePipeline::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_texture_coordinates = vertices_->texture_coordinates();
auto size = texture_coverage.size;
auto origin = texture_coverage.origin;
std::vector<VS::PerVertexData> vertex_data(vertex_count);
{
for (auto i = 0; i < vertex_count; i++) {
auto sk_point = dl_vertices[i];
auto texture_coord = dl_texture_coordinates[i];
auto uv = effect_transform *
Point((texture_coord.x() - origin.x) / size.width,
(texture_coord.y() - origin.y) / size.height);
// From experimentation we need to clamp these values to < 1.0 or else
// there can be flickering.
vertex_data[i] = {
.position = Point(sk_point.x(), sk_point.y()),
.texture_coords =
Point(std::clamp(uv.x, 0.0f, 1.0f - kEhCloseEnough),
std::clamp(uv.y, 0.0f, 1.0f - kEhCloseEnough)),
};
}
}
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,
};
}
GeometryVertexType DLVerticesGeometry::GetVertexType() const {
auto* dl_colors = vertices_->colors();
if (dl_colors != nullptr) {
return GeometryVertexType::kColor;
}
auto* dl_texture_coordinates = vertices_->texture_coordinates();
if (dl_texture_coordinates != nullptr) {
return GeometryVertexType::kUV;
}
return GeometryVertexType::kPosition;
}
std::optional<Rect> DLVerticesGeometry::GetCoverage(
const Matrix& transform) const {
return ToRect(vertices_->bounds()).TransformBounds(transform);
}
} // namespace impeller