| // Copyright 2016 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 "src/ui/lib/escher/impl/mesh_manager.h" |
| |
| #include <iterator> |
| |
| #include "src/ui/lib/escher/geometry/types.h" |
| #include "src/ui/lib/escher/impl/command_buffer_pool.h" |
| #include "src/ui/lib/escher/impl/vulkan_utils.h" |
| #include "src/ui/lib/escher/resources/resource_recycler.h" |
| #include "src/ui/lib/escher/vk/buffer.h" |
| #include "src/ui/lib/escher/vk/gpu_allocator.h" |
| #include "src/ui/lib/escher/vk/vulkan_context.h" |
| |
| namespace escher { |
| namespace impl { |
| |
| MeshManager::MeshManager(CommandBufferPool* command_buffer_pool, GpuAllocator* allocator, |
| ResourceRecycler* resource_recycler) |
| : command_buffer_pool_(command_buffer_pool), |
| allocator_(allocator), |
| resource_recycler_(resource_recycler), |
| device_(command_buffer_pool->device()), |
| queue_(command_buffer_pool->queue()), |
| builder_count_(0) {} |
| |
| MeshManager::~MeshManager() { FX_DCHECK(builder_count_ == 0); } |
| |
| MeshBuilderPtr MeshManager::NewMeshBuilder(BatchGpuUploader* gpu_uploader, const MeshSpec& spec, |
| size_t max_vertex_count, size_t max_index_count) { |
| FX_DCHECK(spec.IsValidOneBufferMesh()); |
| size_t stride = spec.stride(0); |
| |
| return AdoptRef(new MeshManager::MeshBuilder(this, spec, max_vertex_count, max_index_count, |
| std::move(gpu_uploader))); |
| } |
| |
| MeshManager::MeshBuilder::MeshBuilder(MeshManager* manager, const MeshSpec& spec, |
| size_t max_vertex_count, size_t max_index_count, |
| BatchGpuUploader* gpu_uploader) |
| : escher::MeshBuilder(max_vertex_count, max_index_count, spec.stride(0)), |
| manager_(manager), |
| spec_(spec), |
| is_built_(false), |
| gpu_uploader_(gpu_uploader) { |
| FX_DCHECK(spec.IsValidOneBufferMesh()); |
| } |
| |
| MeshManager::MeshBuilder::~MeshBuilder() {} |
| |
| BoundingBox MeshManager::MeshBuilder::ComputeBoundingBox2D() const { |
| FX_DCHECK(spec_.attribute_offset(0, MeshAttribute::kPosition2D) == 0); |
| const uint8_t* vertex_ptr = vertex_staging_buffer_.data(); |
| |
| const vec2* pos = reinterpret_cast<const vec2*>(vertex_ptr); |
| vec3 min(*pos, 0); |
| vec3 max(*pos, 0); |
| |
| for (size_t i = 1; i < vertex_count_; ++i) { |
| vertex_ptr += vertex_stride_; |
| pos = reinterpret_cast<const vec2*>(vertex_ptr); |
| min = glm::min(min, vec3(*pos, 0)); |
| max = glm::max(max, vec3(*pos, 0)); |
| } |
| |
| return BoundingBox(min, max); |
| } |
| |
| BoundingBox MeshManager::MeshBuilder::ComputeBoundingBox3D() const { |
| FX_DCHECK(spec_.attribute_offset(0, MeshAttribute::kPosition3D) == 0); |
| const uint8_t* vertex_ptr = vertex_staging_buffer_.data(); |
| |
| const vec3* pos = reinterpret_cast<const vec3*>(vertex_ptr); |
| vec3 min(*pos); |
| vec3 max(*pos); |
| |
| for (size_t i = 1; i < vertex_count_; ++i) { |
| vertex_ptr += vertex_stride_; |
| pos = reinterpret_cast<const vec3*>(vertex_ptr); |
| min = glm::min(min, *pos); |
| max = glm::max(max, *pos); |
| } |
| |
| return BoundingBox(min, max); |
| } |
| |
| BoundingBox MeshManager::MeshBuilder::ComputeBoundingBox() const { |
| FX_DCHECK(vertex_count_ > 0); |
| FX_DCHECK(spec_.IsValidOneBufferMesh()); |
| return spec_.has_attribute(0, MeshAttribute::kPosition2D) ? ComputeBoundingBox2D() |
| : ComputeBoundingBox3D(); |
| } |
| |
| MeshPtr MeshManager::MeshBuilder::Build() { |
| FX_DCHECK(!is_built_); |
| if (is_built_) { |
| return MeshPtr(); |
| } |
| is_built_ = true; |
| |
| vk::Device device = manager_->device_; |
| GpuAllocator* allocator = manager_->allocator_; |
| |
| // TODO: use eTransferDstOptimal instead of eTransferDst? |
| auto vertex_buffer = allocator->AllocateBuffer( |
| manager_->resource_recycler(), vertex_count_ * vertex_stride_, |
| vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferSrc | |
| vk::BufferUsageFlagBits::eTransferDst, |
| vk::MemoryPropertyFlagBits::eDeviceLocal); |
| auto index_buffer = allocator->AllocateBuffer( |
| manager_->resource_recycler(), index_count_ * sizeof(uint32_t), |
| vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferSrc | |
| vk::BufferUsageFlagBits::eTransferDst, |
| vk::MemoryPropertyFlagBits::eDeviceLocal); |
| |
| // Calculate bounding box using the staging buffers before they are cleared. |
| auto bounding_box = ComputeBoundingBox(); |
| |
| // |vertex_staging_buffer_| and |index_staging_buffer_| will be cleared after |
| // being moved to BatchGpuUploader, so that we will be able to reuse |
| // MeshBuilder. |
| gpu_uploader_->ScheduleWriteBuffer(vertex_buffer, std::move(vertex_staging_buffer_), |
| /* target_offset */ 0, |
| /* copy_size */ vertex_count_ * vertex_stride_); |
| gpu_uploader_->ScheduleWriteBuffer(index_buffer, std::move(index_staging_buffer_), |
| /* target_offset */ 0, |
| /* copy_size */ index_count_ * sizeof(uint32_t)); |
| |
| auto result = fxl::MakeRefCounted<Mesh>(manager_->resource_recycler(), spec_, |
| std::move(bounding_box), vertex_count_, index_count_, |
| std::move(vertex_buffer), std::move(index_buffer)); |
| |
| // Clear the vertex staging buffer and index staging buffer for future reuse. |
| vertex_count_ = 0U; |
| index_count_ = 0; |
| |
| return result; |
| } |
| |
| } // namespace impl |
| } // namespace escher |