| // Copyright 2017 The Fuchsia 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 "garnet/bin/ui/sketchy/resources/stroke.h" |
| |
| #include "garnet/bin/ui/sketchy/buffer/shared_buffer_pool.h" |
| #include "lib/escher/escher.h" |
| |
| namespace { |
| |
| struct StrokeInfo { |
| uint32_t segment_count; |
| float half_width; |
| uint32_t base_vertex_index; |
| float pixels_per_division; |
| uint32_t division_count; |
| float total_length; |
| }; |
| |
| constexpr float kStrokeHalfWidth = 30.f; // pixels |
| constexpr float kPixelsPerDivision = 4; |
| |
| } // namespace |
| |
| namespace sketchy_service { |
| |
| const ResourceTypeInfo Stroke::kTypeInfo("Stroke", ResourceType::kStroke, |
| ResourceType::kResource); |
| |
| Stroke::Stroke(StrokeTessellator* tessellator, |
| escher::BufferFactory* buffer_factory) |
| : tessellator_(tessellator), |
| stable_path_(kStrokeHalfWidth, kPixelsPerDivision), |
| delta_stable_path_(kStrokeHalfWidth, kPixelsPerDivision), |
| stroke_info_buffer_(buffer_factory->NewBuffer( |
| sizeof(StrokeInfo), vk::BufferUsageFlagBits::eUniformBuffer, |
| vk::MemoryPropertyFlagBits::eHostVisible | |
| vk::MemoryPropertyFlagBits::eHostCoherent)), |
| control_points_buffer_(buffer_factory), |
| re_params_buffer_(buffer_factory), |
| division_counts_buffer_(buffer_factory), |
| cumulative_division_counts_buffer_(buffer_factory), |
| division_segment_index_buffer_(buffer_factory) {} |
| |
| bool Stroke::SetPath(std::unique_ptr<StrokePath> path) { |
| if (fitter_) { |
| FXL_LOG(ERROR) << "Client error: path cannot be set during fitting."; |
| return false; |
| } |
| is_path_updated_ = true; |
| stable_path_.SetPath(std::move(path)); |
| delta_stable_path_.Reset(); |
| return true; |
| } |
| |
| bool Stroke::Begin(glm::vec2 pt) { |
| if (fitter_) { |
| FXL_LOG(ERROR) << "Client error: stroke fitting has already begun."; |
| return false; |
| } |
| |
| is_path_updated_ = true; |
| stable_path_.Reset(); |
| delta_stable_path_.Reset(); |
| fitter_ = std::make_unique<StrokeFitter>(pt); |
| return true; |
| } |
| |
| bool Stroke::Extend(const std::vector<glm::vec2>& sampled_pts) { |
| if (!fitter_) { |
| FXL_LOG(ERROR) << "Client error: stroke fitting has not begun."; |
| return false; |
| } |
| |
| is_path_updated_ = true; |
| fitter_->Extend(sampled_pts); |
| return true; |
| } |
| |
| bool Stroke::Finish() { |
| if (!fitter_) { |
| FXL_LOG(ERROR) << "Client error: stroke fitting has not begun."; |
| return false; |
| } |
| |
| StrokePath delta_path; |
| (void)fitter_->FitAndPop(&delta_path); |
| if (!delta_path.empty()) { |
| is_path_updated_ = true; |
| delta_stable_path_.Extend(delta_path); |
| } |
| fitter_ = nullptr; |
| return true; |
| } |
| |
| void Stroke::TessellateAndMerge(Frame* frame, MeshBuffer* mesh_buffer) { |
| DividedStrokePath delta_unstable_path(kStrokeHalfWidth, kPixelsPerDivision); |
| if (fitter_) { |
| StrokePath delta_path; |
| bool is_stable = fitter_->FitAndPop(&delta_path); |
| if (is_stable) { |
| delta_stable_path_.Extend(delta_path); |
| } else { |
| delta_unstable_path.Extend(delta_path); |
| } |
| } |
| if (stable_path_.empty() && delta_stable_path_.empty() && |
| delta_unstable_path.empty()) { |
| return; |
| } |
| |
| auto command = frame->command(); |
| auto buffer_factory = frame->unshared_buffer_factory(); |
| auto profiler = frame->profiler(); |
| |
| if (is_path_updated_) { |
| if (!delta_stable_path_.empty()) { |
| AppendPathToBuffers(command, buffer_factory, delta_stable_path_, |
| /* is_stable= */ true); |
| stable_path_.Extend(delta_stable_path_.path()); |
| delta_stable_path_.Reset(); |
| } else if (!delta_unstable_path.empty()) { |
| AppendPathToBuffers(command, buffer_factory, delta_unstable_path, |
| /* is_stable= */ false); |
| } else { |
| control_points_buffer_.SetData(command, buffer_factory, |
| stable_path_.control_points_data(), |
| stable_path_.control_points_data_size()); |
| re_params_buffer_.SetData(command, buffer_factory, |
| stable_path_.re_params_data(), |
| stable_path_.re_params_data_size()); |
| division_counts_buffer_.SetData(command, buffer_factory, |
| stable_path_.division_counts_data(), |
| stable_path_.division_counts_data_size()); |
| cumulative_division_counts_buffer_.SetData( |
| command, buffer_factory, |
| stable_path_.cumulative_division_counts_data(), |
| stable_path_.cumulative_division_counts_data_size()); |
| } |
| |
| auto division_segment_indices = |
| stable_path_.PrepareDivisionSegmentIndices(delta_unstable_path); |
| division_segment_index_buffer_.SetData( |
| command, buffer_factory, division_segment_indices.data(), |
| division_segment_indices.size() * sizeof(uint32_t)); |
| } |
| |
| uint32_t base_vertex_index = mesh_buffer->vertex_count(); |
| auto pair = mesh_buffer->Reserve( |
| frame, stable_path_.vertex_count() + delta_unstable_path.vertex_count(), |
| stable_path_.index_count() + delta_unstable_path.index_count(), |
| escher::BoundingBox() |
| .Join(stable_path_.bbox()) |
| .Join(delta_unstable_path.bbox())); |
| const auto& vertex_range = pair.first; |
| const auto& index_range = pair.second; |
| |
| StrokeInfo stroke_info = { |
| .segment_count = static_cast<uint32_t>( |
| stable_path_.segment_count() + delta_unstable_path.segment_count()), |
| .half_width = kStrokeHalfWidth, |
| .base_vertex_index = base_vertex_index, |
| .pixels_per_division = kPixelsPerDivision, |
| .division_count = |
| stable_path_.division_count() + delta_unstable_path.division_count(), |
| .total_length = stable_path_.length() + delta_unstable_path.length()}; |
| memcpy(stroke_info_buffer_->host_ptr(), &stroke_info, sizeof(StrokeInfo)); |
| |
| tessellator_->Dispatch( |
| stroke_info_buffer_, control_points_buffer_.get(), |
| re_params_buffer_.get(), division_counts_buffer_.get(), |
| cumulative_division_counts_buffer_.get(), |
| division_segment_index_buffer_.get(), |
| mesh_buffer->vertex_buffer()->escher_buffer(), vertex_range, |
| mesh_buffer->index_buffer()->escher_buffer(), index_range, command, |
| profiler, |
| stable_path_.division_count() + delta_unstable_path.division_count(), |
| is_path_updated_); |
| is_path_updated_ = false; |
| } |
| |
| void Stroke::AppendPathToBuffers(escher::impl::CommandBuffer* command, |
| escher::BufferFactory* buffer_factory, |
| const DividedStrokePath& path, |
| bool is_stable) { |
| control_points_buffer_.AppendData(command, buffer_factory, |
| path.control_points_data(), |
| path.control_points_data_size(), is_stable); |
| re_params_buffer_.AppendData(command, buffer_factory, path.re_params_data(), |
| path.re_params_data_size(), is_stable); |
| division_counts_buffer_.AppendData( |
| command, buffer_factory, path.division_counts_data(), |
| path.division_counts_data_size(), is_stable); |
| |
| auto cumulative_division_count = |
| path.ComputeCumulativeDivisionCounts(stable_path_.division_count()); |
| cumulative_division_counts_buffer_.AppendData( |
| command, buffer_factory, cumulative_division_count.data(), |
| cumulative_division_count.size() * sizeof(uint32_t), is_stable); |
| } |
| |
| } // namespace sketchy_service |