blob: 42af1e49c0a46c82d28bfd622ce408fa7e28ec08 [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 <utility>
#include <utility>
#include "impeller/core/formats.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();
}
VerticesGeometry::~VerticesGeometry() = default;
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) {
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);
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*>(vertices_.data()),
Range{0, total_vtx_bytes}, 0)) {
return {};
}
if (index_count > 0 &&
!buffer->CopyHostBuffer(
reinterpret_cast<uint8_t*>(const_cast<uint16_t*>(indices_.data())),
Range{0, total_idx_bytes}, total_vtx_bytes)) {
return {};
}
return GeometryResult{
.type = GetPrimitiveType(),
.vertex_buffer =
{
.vertex_buffer = {.buffer = buffer,
.range = Range{0, total_vtx_bytes}},
.index_buffer = {.buffer = buffer,
.range =
Range{total_vtx_bytes, total_idx_bytes}},
.vertex_count = index_count > 0 ? index_count : vertex_count,
.index_type =
index_count > 0 ? IndexType::k16bit : IndexType::kNone,
},
.transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
entity.GetTransformation(),
.prevent_overdraw = false,
};
}
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();
std::vector<VS::PerVertexData> vertex_data(vertex_count);
{
for (auto i = 0u; i < vertex_count; i++) {
vertex_data[i] = {
.position = vertices_[i],
.color = colors_[i],
};
}
}
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 (index_count > 0 &&
!buffer->CopyHostBuffer(
reinterpret_cast<uint8_t*>(const_cast<uint16_t*>(indices_.data())),
Range{0, total_idx_bytes}, total_vtx_bytes)) {
return {};
}
return GeometryResult{
.type = GetPrimitiveType(),
.vertex_buffer =
{
.vertex_buffer = {.buffer = buffer,
.range = Range{0, total_vtx_bytes}},
.index_buffer = {.buffer = buffer,
.range =
Range{total_vtx_bytes, total_idx_bytes}},
.vertex_count = index_count > 0 ? index_count : vertex_count,
.index_type =
index_count > 0 ? IndexType::k16bit : IndexType::kNone,
},
.transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
entity.GetTransformation(),
.prevent_overdraw = false,
};
}
GeometryResult VerticesGeometry::GetPositionUVBuffer(
Rect texture_coverage,
Matrix effect_transform,
const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) {
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();
std::vector<VS::PerVertexData> vertex_data(vertex_count);
{
for (auto i = 0u; i < vertex_count; 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.
vertex_data[i] = {
.position = vertex,
.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 (index_count > 0u &&
!buffer->CopyHostBuffer(
reinterpret_cast<uint8_t*>(const_cast<uint16_t*>(indices_.data())),
Range{0, total_idx_bytes}, total_vtx_bytes)) {
return {};
}
return GeometryResult{
.type = GetPrimitiveType(),
.vertex_buffer =
{
.vertex_buffer = {.buffer = buffer,
.range = Range{0, total_vtx_bytes}},
.index_buffer = {.buffer = buffer,
.range =
Range{total_vtx_bytes, total_idx_bytes}},
.vertex_count = index_count > 0 ? index_count : vertex_count,
.index_type =
index_count > 0 ? IndexType::k16bit : IndexType::kNone,
},
.transform = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
entity.GetTransformation(),
.prevent_overdraw = false,
};
}
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