|  | // 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/escher.h" | 
|  |  | 
|  | #include "src/ui/lib/escher/defaults/default_shader_program_factory.h" | 
|  | #include "src/ui/lib/escher/impl/command_buffer_pool.h" | 
|  | #include "src/ui/lib/escher/impl/frame_manager.h" | 
|  | #if ESCHER_USE_RUNTIME_GLSL | 
|  | #include "third_party/shaderc/libshaderc/include/shaderc/shaderc.hpp"  // nogncheck | 
|  | #endif | 
|  | #include "src/ui/lib/escher/impl/image_cache.h" | 
|  | #include "src/ui/lib/escher/impl/mesh_manager.h" | 
|  | #include "src/ui/lib/escher/impl/vulkan_utils.h" | 
|  | #include "src/ui/lib/escher/profiling/timestamp_profiler.h" | 
|  | #include "src/ui/lib/escher/renderer/batch_gpu_uploader.h" | 
|  | #include "src/ui/lib/escher/renderer/buffer_cache.h" | 
|  | #include "src/ui/lib/escher/renderer/frame.h" | 
|  | #include "src/ui/lib/escher/renderer/sampler_cache.h" | 
|  | #include "src/ui/lib/escher/resources/resource_recycler.h" | 
|  | #include "src/ui/lib/escher/util/hasher.h" | 
|  | #include "src/ui/lib/escher/util/image_utils.h" | 
|  | #include "src/ui/lib/escher/util/trace_macros.h" | 
|  | #include "src/ui/lib/escher/vk/gpu_allocator.h" | 
|  | #include "src/ui/lib/escher/vk/impl/descriptor_set_allocator.h" | 
|  | #include "src/ui/lib/escher/vk/impl/descriptor_set_allocator_cache.h" | 
|  | #include "src/ui/lib/escher/vk/impl/framebuffer_allocator.h" | 
|  | #include "src/ui/lib/escher/vk/impl/pipeline_layout_cache.h" | 
|  | #include "src/ui/lib/escher/vk/impl/render_pass_cache.h" | 
|  | #include "src/ui/lib/escher/vk/pipeline_builder.h" | 
|  | #include "src/ui/lib/escher/vk/texture.h" | 
|  | #include "src/ui/lib/escher/vk/vma_gpu_allocator.h" | 
|  |  | 
|  | namespace escher { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Constructor helper. | 
|  | std::unique_ptr<impl::CommandBufferPool> NewCommandBufferPool( | 
|  | const VulkanContext& context, impl::CommandBufferSequencer* sequencer, | 
|  | bool use_protected_memory) { | 
|  | return std::make_unique<impl::CommandBufferPool>( | 
|  | context.device, context.queue, context.queue_family_index, sequencer, | 
|  | /*supports_graphics_and_compute=*/true, use_protected_memory); | 
|  | } | 
|  |  | 
|  | // Constructor helper. | 
|  | std::unique_ptr<impl::CommandBufferPool> NewTransferCommandBufferPool( | 
|  | const VulkanContext& context, impl::CommandBufferSequencer* sequencer, | 
|  | bool use_protected_memory) { | 
|  | if (!context.transfer_queue) { | 
|  | return nullptr; | 
|  | } else { | 
|  | return std::make_unique<impl::CommandBufferPool>( | 
|  | context.device, context.transfer_queue, context.transfer_queue_family_index, sequencer, | 
|  | /*supports_graphics_and_compute=*/false, use_protected_memory); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Constructor helper. | 
|  | std::unique_ptr<impl::MeshManager> NewMeshManager(impl::CommandBufferPool* main_pool, | 
|  | impl::CommandBufferPool* transfer_pool, | 
|  | GpuAllocator* allocator, | 
|  | ResourceRecycler* resource_recycler) { | 
|  | return std::make_unique<impl::MeshManager>(transfer_pool ? transfer_pool : main_pool, allocator, | 
|  | resource_recycler); | 
|  | } | 
|  |  | 
|  | }  // anonymous namespace | 
|  |  | 
|  | // This version exists because if we were to define default args in the constructor (e.g. | 
|  | // "std::unique_ptr<PipelineBuilder> pipeline_builder = nullptr"), then we would run into | 
|  | // compile problems that can be solved by either: | 
|  | //   - this approach | 
|  | //   - including additional headers in escher.h, instead of forward-declaring | 
|  | // Since many files include escher.h, it is worthwhile to forward-declare as much as possible. | 
|  | Escher::Escher(VulkanDeviceQueuesPtr device) | 
|  | : Escher(device, HackFilesystem::New(), /*gpu_allocator*/ nullptr) {} | 
|  |  | 
|  | // Helper which either returns the provided allocator, or if it is nullptr returns a new one. | 
|  | static std::shared_ptr<GpuAllocator> EnsureAllocator(std::shared_ptr<GpuAllocator> allocator, | 
|  | const VulkanContext& vulkan_context) { | 
|  | return allocator ? std::move(allocator) : std::make_shared<VmaGpuAllocator>(vulkan_context); | 
|  | } | 
|  |  | 
|  | Escher::Escher(VulkanDeviceQueuesPtr device, HackFilesystemPtr filesystem, | 
|  | std::shared_ptr<GpuAllocator> gpu_allocator_in) | 
|  | : device_(std::move(device)), | 
|  | vulkan_context_(device_->GetVulkanContext()), | 
|  | gpu_allocator_(EnsureAllocator(std::move(gpu_allocator_in), vulkan_context_)), | 
|  | command_buffer_sequencer_(std::make_unique<impl::CommandBufferSequencer>()), | 
|  | command_buffer_pool_(NewCommandBufferPool(vulkan_context_, command_buffer_sequencer_.get(), | 
|  | /*use_protected_memory=*/false)), | 
|  | transfer_command_buffer_pool_(NewTransferCommandBufferPool( | 
|  | vulkan_context_, command_buffer_sequencer_.get(), /*use_protected_memory=*/false)), | 
|  | #if ESCHER_USE_RUNTIME_GLSL | 
|  | shaderc_compiler_(std::make_unique<shaderc::Compiler>()), | 
|  | #endif | 
|  | pipeline_builder_(std::make_unique<PipelineBuilder>(device_->vk_device())), | 
|  | weak_factory_(this) { | 
|  | FX_DCHECK(vulkan_context_.instance); | 
|  | FX_DCHECK(vulkan_context_.physical_device); | 
|  | FX_DCHECK(vulkan_context_.device); | 
|  | FX_DCHECK(vulkan_context_.queue); | 
|  | // TODO: additional validation, e.g. ensure that queue supports both graphics | 
|  | // and compute. | 
|  |  | 
|  | // Initialize instance variables that require |weak_factory_| to already have | 
|  | // been initialized. | 
|  | resource_recycler_ = std::make_unique<ResourceRecycler>(GetWeakPtr()); | 
|  | image_cache_ = std::make_unique<impl::ImageCache>(GetWeakPtr(), gpu_allocator()); | 
|  | buffer_cache_ = std::make_unique<BufferCache>(GetWeakPtr()); | 
|  | sampler_cache_ = std::make_unique<SamplerCache>(resource_recycler_->GetWeakPtr()); | 
|  | mesh_manager_ = NewMeshManager(command_buffer_pool(), transfer_command_buffer_pool(), | 
|  | gpu_allocator(), resource_recycler()); | 
|  | descriptor_set_allocator_cache_ = | 
|  | std::make_unique<impl::DescriptorSetAllocatorCache>(vk_device()); | 
|  | pipeline_layout_cache_ = std::make_unique<impl::PipelineLayoutCache>(resource_recycler()); | 
|  | render_pass_cache_ = std::make_unique<impl::RenderPassCache>(resource_recycler()); | 
|  | framebuffer_allocator_ = | 
|  | std::make_unique<impl::FramebufferAllocator>(resource_recycler(), render_pass_cache_.get()); | 
|  | image_view_allocator_ = std::make_unique<ImageViewAllocator>(resource_recycler()); | 
|  | shader_program_factory_ = | 
|  | std::make_unique<DefaultShaderProgramFactory>(GetWeakPtr(), std::move(filesystem)); | 
|  |  | 
|  | frame_manager_ = std::make_unique<impl::FrameManager>(GetWeakPtr()); | 
|  |  | 
|  | semaphore_chain_ = std::make_unique<ChainedSemaphoreGenerator>(vk_device()); | 
|  |  | 
|  | // Query relevant Vulkan properties. | 
|  | auto device_properties = vk_physical_device().getProperties(); | 
|  | timestamp_period_ = device_properties.limits.timestampPeriod; | 
|  | auto queue_properties = | 
|  | vk_physical_device().getQueueFamilyProperties()[vulkan_context_.queue_family_index]; | 
|  | supports_timer_queries_ = queue_properties.timestampValidBits > 0; | 
|  | } | 
|  |  | 
|  | Escher::~Escher() { | 
|  | shader_program_factory_->Clear(); | 
|  | vk_device().waitIdle(); | 
|  | Cleanup(); | 
|  |  | 
|  | // Everything that refers to a ResourceRecycler must be released before their | 
|  | // ResourceRecycler is. | 
|  | image_view_allocator_.reset(); | 
|  | framebuffer_allocator_.reset(); | 
|  | render_pass_cache_.reset(); | 
|  | pipeline_layout_cache_.reset(); | 
|  | mesh_manager_.reset(); | 
|  | descriptor_set_allocator_cache_.reset(); | 
|  | sampler_cache_.reset(); | 
|  |  | 
|  | // ResourceRecyclers must be released before the CommandBufferSequencer is, | 
|  | // since they register themselves with it. | 
|  | resource_recycler_.reset(); | 
|  | buffer_cache_.reset(); | 
|  | } | 
|  |  | 
|  | bool Escher::Cleanup() { | 
|  | TRACE_DURATION("gfx", "Escher::Cleanup"); | 
|  | bool finished = true; | 
|  | finished = command_buffer_pool()->Cleanup() && finished; | 
|  | if (auto pool = transfer_command_buffer_pool()) { | 
|  | finished = pool->Cleanup() && finished; | 
|  | } | 
|  | if (auto pool = protected_command_buffer_pool()) { | 
|  | finished = pool->Cleanup() && finished; | 
|  | } | 
|  | pipeline_builder_->MaybeStorePipelineCacheData(); | 
|  | return finished; | 
|  | } | 
|  |  | 
|  | void Escher::set_pipeline_builder(std::unique_ptr<PipelineBuilder> pipeline_builder) { | 
|  | pipeline_builder_ = std::move(pipeline_builder); | 
|  | } | 
|  |  | 
|  | impl::CommandBufferPool* Escher::protected_command_buffer_pool() { | 
|  | if (allow_protected_memory() && !protected_command_buffer_pool_) { | 
|  | protected_command_buffer_pool_ = | 
|  | NewCommandBufferPool(vulkan_context_, command_buffer_sequencer_.get(), true); | 
|  | } | 
|  | return protected_command_buffer_pool_.get(); | 
|  | } | 
|  |  | 
|  | MeshBuilderPtr Escher::NewMeshBuilder(BatchGpuUploader* gpu_uploader, const MeshSpec& spec, | 
|  | size_t max_vertex_count, size_t max_index_count) { | 
|  | return mesh_manager()->NewMeshBuilder(gpu_uploader, spec, max_vertex_count, max_index_count); | 
|  | } | 
|  |  | 
|  | ImagePtr Escher::NewRgbaImage(BatchGpuUploader* gpu_uploader, uint32_t width, uint32_t height, | 
|  | const uint8_t* bytes) { | 
|  | return image_utils::NewRgbaImage(image_cache(), gpu_uploader, width, height, bytes); | 
|  | } | 
|  |  | 
|  | ImagePtr Escher::NewCheckerboardImage(BatchGpuUploader* gpu_uploader, uint32_t width, | 
|  | uint32_t height) { | 
|  | return image_utils::NewCheckerboardImage(image_cache(), gpu_uploader, width, height); | 
|  | } | 
|  |  | 
|  | ImagePtr Escher::NewGradientImage(BatchGpuUploader* gpu_uploader, uint32_t width, uint32_t height) { | 
|  | return image_utils::NewGradientImage(image_cache(), gpu_uploader, width, height); | 
|  | } | 
|  |  | 
|  | ImagePtr Escher::NewNoiseImage(BatchGpuUploader* gpu_uploader, uint32_t width, uint32_t height) { | 
|  | return image_utils::NewNoiseImage(image_cache(), gpu_uploader, width, height); | 
|  | } | 
|  |  | 
|  | TexturePtr Escher::NewTexture(ImagePtr image, vk::Filter filter, vk::ImageAspectFlags aspect_mask, | 
|  | bool use_unnormalized_coordinates) { | 
|  | TRACE_DURATION("gfx", "Escher::NewTexture (from image)"); | 
|  | return Texture::New(resource_recycler(), std::move(image), filter, aspect_mask, | 
|  | use_unnormalized_coordinates); | 
|  | } | 
|  |  | 
|  | BufferPtr Escher::NewBuffer(vk::DeviceSize size, vk::BufferUsageFlags usage_flags, | 
|  | vk::MemoryPropertyFlags memory_property_flags) { | 
|  | TRACE_DURATION("gfx", "Escher::NewBuffer"); | 
|  | return gpu_allocator()->AllocateBuffer(resource_recycler(), size, usage_flags, | 
|  | memory_property_flags); | 
|  | } | 
|  |  | 
|  | TexturePtr Escher::NewTexture(vk::Format format, uint32_t width, uint32_t height, | 
|  | uint32_t sample_count, vk::ImageUsageFlags usage_flags, | 
|  | vk::Filter filter, vk::ImageAspectFlags aspect_flags, | 
|  | bool use_unnormalized_coordinates, | 
|  | vk::MemoryPropertyFlags memory_flags) { | 
|  | TRACE_DURATION("gfx", "Escher::NewTexture (new image)"); | 
|  | ImageInfo image_info{.format = format, | 
|  | .width = width, | 
|  | .height = height, | 
|  | .sample_count = sample_count, | 
|  | .usage = usage_flags}; | 
|  | image_info.memory_flags |= memory_flags; | 
|  | ImagePtr image = gpu_allocator()->AllocateImage(resource_recycler(), image_info); | 
|  | return Texture::New(resource_recycler(), std::move(image), filter, aspect_flags, | 
|  | use_unnormalized_coordinates); | 
|  | } | 
|  |  | 
|  | TexturePtr Escher::NewAttachmentTexture(vk::Format format, uint32_t width, uint32_t height, | 
|  | uint32_t sample_count, vk::Filter filter, | 
|  | vk::ImageUsageFlags usage_flags, | 
|  | bool use_unnormalized_coordinates, | 
|  | vk::MemoryPropertyFlags memory_flags) { | 
|  | const auto pair = image_utils::IsDepthStencilFormat(format); | 
|  | usage_flags |= (pair.first || pair.second) ? vk::ImageUsageFlagBits::eDepthStencilAttachment | 
|  | : vk::ImageUsageFlagBits::eColorAttachment; | 
|  | return NewTexture(format, width, height, sample_count, usage_flags, filter, | 
|  | image_utils::FormatToColorOrDepthStencilAspectFlags(format), | 
|  | use_unnormalized_coordinates, memory_flags); | 
|  | } | 
|  |  | 
|  | ShaderProgramPtr Escher::GetProgramImpl(const std::string shader_paths[EnumCount<ShaderStage>()], | 
|  | ShaderVariantArgs args) { | 
|  | return shader_program_factory_->GetProgramImpl(shader_paths, std::move(args)); | 
|  | } | 
|  |  | 
|  | FramePtr Escher::NewFrame(const char* trace_literal, uint64_t frame_number, bool enable_gpu_logging, | 
|  | escher::CommandBuffer::Type requested_type, bool use_protected_memory) { | 
|  | TRACE_DURATION("gfx", "escher::Escher::NewFrame "); | 
|  |  | 
|  | // Check the type before cycling the framebuffer/descriptor-set allocators. | 
|  | // Without these checks it is possible to write into a Vulkan resource before | 
|  | // it is finished being used in a previous frame. | 
|  | // TODO(fxbug.dev/7194): The correct solution is not to use multiple Frames per frame. | 
|  | if (requested_type != CommandBuffer::Type::kTransfer) { | 
|  | // TODO(fxbug.dev/7288): Nothing calls Clear() on the DescriptorSetAllocators, so | 
|  | // their internal allocations are currently able to grow without bound. | 
|  | // DescriptorSets are not managed by ResourceRecyclers, so just | 
|  | // adding a call to Clear() here would be dangerous. | 
|  | descriptor_set_allocator_cache_->BeginFrame(); | 
|  | pipeline_layout_cache_->BeginFrame(); | 
|  | } | 
|  | if (requested_type == CommandBuffer::Type::kGraphics) { | 
|  | image_view_allocator_->BeginFrame(); | 
|  | framebuffer_allocator_->BeginFrame(); | 
|  | } | 
|  |  | 
|  | return frame_manager_->NewFrame(trace_literal, frame_number, enable_gpu_logging, requested_type, | 
|  | use_protected_memory); | 
|  | } | 
|  |  | 
|  | uint64_t Escher::GetNumGpuBytesAllocated() { return gpu_allocator()->GetTotalBytesAllocated(); } | 
|  |  | 
|  | }  // namespace escher |