| // 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 |