blob: 7215603347116f7afacde0127be4635fe4c7e9f1 [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/entity/geometry/vertices_geometry.h"
#include <cstdint>
#include <utility>
#include "impeller/core/buffer_view.h"
namespace impeller {
// 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 std::vector<Point>& vertices,
const std::vector<uint16_t>& indices) {
std::vector<uint16_t> unrolled_indices;
// Un-fan index buffer if provided.
if (indices.size() > 0u) {
if (indices.size() < 3u) {
return {};
}
auto center_point = indices[0];
for (auto i = 1u; i < indices.size() - 1; i++) {
unrolled_indices.push_back(center_point);
unrolled_indices.push_back(indices[i]);
unrolled_indices.push_back(indices[i + 1]);
}
} else {
if (vertices.size() < 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 < vertices.size() - 1; i++) {
unrolled_indices.push_back(0);
unrolled_indices.push_back(i);
unrolled_indices.push_back(i + 1);
}
}
return unrolled_indices;
}
/////// Vertices Geometry ///////
VerticesGeometry::VerticesGeometry(std::vector<Point> vertices,
std::vector<uint16_t> indices,
std::vector<Point> texture_coordinates,
std::vector<Color> colors,
Rect bounds,
VertexMode vertex_mode)
: vertices_(std::move(vertices)),
colors_(std::move(colors)),
texture_coordinates_(std::move(texture_coordinates)),
indices_(std::move(indices)),
bounds_(bounds),
vertex_mode_(vertex_mode) {
NormalizeIndices();
}
PrimitiveType VerticesGeometry::GetPrimitiveType() const {
switch (vertex_mode_) {
case VerticesGeometry::VertexMode::kTriangleFan:
// Unrolled into triangle mode.
return PrimitiveType::kTriangle;
case VerticesGeometry::VertexMode::kTriangleStrip:
return PrimitiveType::kTriangleStrip;
case VerticesGeometry::VertexMode::kTriangles:
return PrimitiveType::kTriangle;
}
}
void VerticesGeometry::NormalizeIndices() {
// Convert triangle fan if present.
if (vertex_mode_ == VerticesGeometry::VertexMode::kTriangleFan) {
indices_ = fromFanIndices(vertices_, indices_);
return;
}
}
bool VerticesGeometry::HasVertexColors() const {
return colors_.size() > 0;
}
bool VerticesGeometry::HasTextureCoordinates() const {
return texture_coordinates_.size() > 0;
}
std::optional<Rect> VerticesGeometry::GetTextureCoordinateCoverge() const {
if (!HasTextureCoordinates()) {
return std::nullopt;
}
auto vertex_count = vertices_.size();
if (vertex_count == 0) {
return std::nullopt;
}
return Rect::MakePointBounds(texture_coordinates_.begin(),
texture_coordinates_.end());
}
GeometryResult VerticesGeometry::GetPositionBuffer(
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
auto index_count = indices_.size();
auto vertex_count = vertices_.size();
size_t total_vtx_bytes = vertex_count * sizeof(float) * 2;
size_t total_idx_bytes = index_count * sizeof(uint16_t);
auto vertex_buffer = renderer.GetTransientsBuffer().Emplace(
reinterpret_cast<const uint8_t*>(vertices_.data()), total_vtx_bytes,
alignof(float));
BufferView index_buffer = {};
if (index_count) {
index_buffer = renderer.GetTransientsBuffer().Emplace(
indices_.data(), total_idx_bytes, 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 = pass.GetOrthographicTransform() * entity.GetTransform(),
};
}
GeometryResult VerticesGeometry::GetPositionColorBuffer(
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) {
using VS = GeometryColorPipeline::VertexShader;
auto index_count = indices_.size();
auto vertex_count = vertices_.size();
size_t total_vtx_bytes = vertex_count * sizeof(VS::PerVertexData);
size_t total_idx_bytes = index_count * sizeof(uint16_t);
auto vertex_buffer = renderer.GetTransientsBuffer().Emplace(
total_vtx_bytes, alignof(VS::PerVertexData), [&](uint8_t* data) {
VS::PerVertexData* vtx_contents =
reinterpret_cast<VS::PerVertexData*>(data);
for (auto i = 0u; i < vertices_.size(); i++) {
VS::PerVertexData vertex_data = {
.position = vertices_[i],
.color = colors_[i],
};
std::memcpy(vtx_contents++, &vertex_data, sizeof(VS::PerVertexData));
}
});
BufferView index_buffer = {};
if (index_count > 0) {
index_buffer = renderer.GetTransientsBuffer().Emplace(
indices_.data(), total_idx_bytes, 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 = pass.GetOrthographicTransform() * entity.GetTransform(),
};
}
GeometryResult VerticesGeometry::GetPositionUVBuffer(
Rect texture_coverage,
Matrix effect_transform,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
using VS = TexturePipeline::VertexShader;
auto index_count = indices_.size();
auto vertex_count = vertices_.size();
auto uv_transform =
texture_coverage.GetNormalizingTransform() * effect_transform;
auto has_texture_coordinates = HasTextureCoordinates();
size_t total_vtx_bytes = vertices_.size() * sizeof(VS::PerVertexData);
size_t total_idx_bytes = index_count * sizeof(uint16_t);
auto vertex_buffer = renderer.GetTransientsBuffer().Emplace(
total_vtx_bytes, alignof(VS::PerVertexData), [&](uint8_t* data) {
VS::PerVertexData* vtx_contents =
reinterpret_cast<VS::PerVertexData*>(data);
for (auto i = 0u; i < vertices_.size(); i++) {
auto vertex = vertices_[i];
auto texture_coord =
has_texture_coordinates ? texture_coordinates_[i] : vertices_[i];
auto uv = uv_transform * texture_coord;
// From experimentation we need to clamp these values to < 1.0 or else
// there can be flickering.
VS::PerVertexData vertex_data = {
.position = vertex,
.texture_coords =
Point(std::clamp(uv.x, 0.0f, 1.0f - kEhCloseEnough),
std::clamp(uv.y, 0.0f, 1.0f - kEhCloseEnough)),
};
std::memcpy(vtx_contents++, &vertex_data, sizeof(VS::PerVertexData));
}
});
BufferView index_buffer = {};
if (index_count > 0) {
index_buffer = renderer.GetTransientsBuffer().Emplace(
indices_.data(), total_idx_bytes, 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 = pass.GetOrthographicTransform() * entity.GetTransform(),
};
}
GeometryVertexType VerticesGeometry::GetVertexType() const {
if (HasVertexColors()) {
return GeometryVertexType::kColor;
}
if (HasTextureCoordinates()) {
return GeometryVertexType::kUV;
}
return GeometryVertexType::kPosition;
}
std::optional<Rect> VerticesGeometry::GetCoverage(
const Matrix& transform) const {
return bounds_.TransformBounds(transform);
}
} // namespace impeller