blob: ae1c08391990fdd805a5a275f650a5ec47e1d587 [file] [log] [blame]
// 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