| // Copyright 2019 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/renderer/render_funcs.h" | 
 |  | 
 | #include "src/ui/lib/escher/escher.h" | 
 | #include "src/ui/lib/escher/impl/vulkan_utils.h" | 
 | #include "src/ui/lib/escher/util/trace_macros.h" | 
 | #include "src/ui/lib/escher/vk/render_pass.h" | 
 | #include "src/ui/lib/escher/vk/texture.h" | 
 |  | 
 | #include <vulkan/vulkan.hpp> | 
 |  | 
 | namespace escher { | 
 |  | 
 | // Helper for NewVertexAttributeBindings(). | 
 | static RenderFuncs::VertexAttributeBinding* FillVertexAttributeBindings( | 
 |     const MeshAttributeBindingLocations& attribute_binding_locations, | 
 |     RenderFuncs::VertexAttributeBinding* binding, uint32_t binding_index, | 
 |     MeshAttributes attributes) { | 
 |   using VertexAttributeBinding = RenderFuncs::VertexAttributeBinding; | 
 |  | 
 |   if (attributes & MeshAttribute::kPosition2D) { | 
 |     *binding++ = VertexAttributeBinding{ | 
 |         .binding_index = binding_index, | 
 |         .attribute_index = attribute_binding_locations.position_2d, | 
 |         .format = vk::Format::eR32G32Sfloat, | 
 |         .offset = GetMeshAttributeOffset(attributes, MeshAttribute::kPosition2D)}; | 
 |   } | 
 |   if (attributes & MeshAttribute::kPosition3D) { | 
 |     *binding++ = VertexAttributeBinding{ | 
 |         .binding_index = binding_index, | 
 |         .attribute_index = attribute_binding_locations.position_3d, | 
 |         .format = vk::Format::eR32G32B32Sfloat, | 
 |         .offset = GetMeshAttributeOffset(attributes, MeshAttribute::kPosition3D)}; | 
 |   } | 
 |   if (attributes & MeshAttribute::kPositionOffset) { | 
 |     *binding++ = VertexAttributeBinding{ | 
 |         .binding_index = binding_index, | 
 |         .attribute_index = attribute_binding_locations.position_offset, | 
 |         .format = vk::Format::eR32G32Sfloat, | 
 |         .offset = GetMeshAttributeOffset(attributes, MeshAttribute::kPositionOffset)}; | 
 |   } | 
 |   if (attributes & MeshAttribute::kUV) { | 
 |     *binding++ = | 
 |         VertexAttributeBinding{.binding_index = binding_index, | 
 |                                .attribute_index = attribute_binding_locations.uv, | 
 |                                .format = vk::Format::eR32G32Sfloat, | 
 |                                .offset = GetMeshAttributeOffset(attributes, MeshAttribute::kUV)}; | 
 |   } | 
 |   if (attributes & MeshAttribute::kPerimeterPos) { | 
 |     *binding++ = VertexAttributeBinding{ | 
 |         .binding_index = binding_index, | 
 |         .attribute_index = attribute_binding_locations.perimeter_pos, | 
 |         .format = vk::Format::eR32G32Sfloat, | 
 |         .offset = GetMeshAttributeOffset(attributes, MeshAttribute::kPerimeterPos)}; | 
 |   } | 
 |   if (attributes & MeshAttribute::kBlendWeight1) { | 
 |     *binding++ = VertexAttributeBinding{ | 
 |         .binding_index = binding_index, | 
 |         .attribute_index = attribute_binding_locations.blend_weight1, | 
 |         .format = vk::Format::eR32Sfloat, | 
 |         .offset = GetMeshAttributeOffset(attributes, MeshAttribute::kBlendWeight1)}; | 
 |   } | 
 |   return binding; | 
 | } | 
 |  | 
 | RenderFuncs::VertexAttributeBinding* RenderFuncs::NewVertexAttributeBindings( | 
 |     const MeshAttributeBindingLocations& attribute_binding_locations, BlockAllocator* allocator, | 
 |     const MeshSpec& mesh_spec, uint32_t total_attribute_count) { | 
 |   FX_DCHECK(total_attribute_count == mesh_spec.total_attribute_count()); | 
 |  | 
 |   auto bindings = allocator->AllocateMany<VertexAttributeBinding>(total_attribute_count); | 
 |   { | 
 |     VertexAttributeBinding* current = bindings; | 
 |     for (uint32_t i = 0; i < VulkanLimits::kNumVertexBuffers; ++i) { | 
 |       if (mesh_spec.attribute_count(i) > 0) { | 
 |         current = FillVertexAttributeBindings(attribute_binding_locations, current, i, | 
 |                                               mesh_spec.attributes[i]); | 
 |       } | 
 |     } | 
 |  | 
 |     // Sanity check that we filled in the correct number of attributes. | 
 |     FX_DCHECK(current == (bindings + total_attribute_count)); | 
 |   } | 
 |   return bindings; | 
 | } | 
 |  | 
 | void RenderFuncs::ObtainDepthAndMsaaTextures( | 
 |     Escher* escher, const FramePtr& frame, uint32_t width, uint32_t height, uint32_t sample_count, | 
 |     bool use_transient_attachment, vk::Format depth_stencil_format, vk::Format msaa_format, | 
 |     TexturePtr& depth_texture_inout, TexturePtr& msaa_texture_inout) { | 
 |   const bool realloc_textures = | 
 |       !depth_texture_inout || | 
 |       (depth_texture_inout->image()->use_protected_memory() != frame->use_protected_memory()) || | 
 |       (depth_texture_inout->image()->is_transient() != use_transient_attachment) || | 
 |       width != depth_texture_inout->width() || height != depth_texture_inout->height() || | 
 |       sample_count != depth_texture_inout->image()->info().sample_count; | 
 |   if (!realloc_textures) { | 
 |     return; | 
 |   } | 
 |  | 
 |   vk::ImageUsageFlags image_usage = use_transient_attachment | 
 |                                         ? vk::ImageUsageFlagBits::eTransientAttachment | 
 |                                         : vk::ImageUsageFlags(); | 
 |   vk::MemoryPropertyFlags memory_properties = vk::MemoryPropertyFlagBits::eDeviceLocal; | 
 |   if (use_transient_attachment) { | 
 |     memory_properties |= vk::MemoryPropertyFlagBits::eLazilyAllocated; | 
 |   } | 
 |   if (frame->use_protected_memory()) { | 
 |     memory_properties |= vk::MemoryPropertyFlagBits::eProtected; | 
 |   } | 
 |  | 
 |   // Need to generate a new depth buffer. | 
 |   { | 
 |     TRACE_DURATION("gfx", "RenderFuncs::ObtainDepthAndMsaaTextures (new depth)"); | 
 |     depth_texture_inout = escher->NewAttachmentTexture( | 
 |         depth_stencil_format, width, height, sample_count, vk::Filter::eLinear, image_usage, | 
 |         /*use_unnormalized_coordinates=*/false, memory_properties); | 
 |   } | 
 |   if (sample_count == 1) { | 
 |     msaa_texture_inout = nullptr; | 
 |   } else { | 
 |     TRACE_DURATION("gfx", "RenderFuncs::ObtainDepthAndMsaaTextures (new msaa)"); | 
 |     msaa_texture_inout = escher->NewAttachmentTexture( | 
 |         msaa_format, width, height, sample_count, vk::Filter::eLinear, image_usage, | 
 |         /*use_unnormalized_coordinates=*/false, memory_properties); | 
 |  | 
 |     // Don't transition layout for transient attachment images. | 
 |     if (!use_transient_attachment) { | 
 |       frame->cmds()->ImageBarrier(msaa_texture_inout->image(), vk::ImageLayout::eUndefined, | 
 |                                   vk::ImageLayout::eColorAttachmentOptimal, | 
 |                                   vk::PipelineStageFlagBits::eAllGraphics, vk::AccessFlags(), | 
 |                                   vk::PipelineStageFlagBits::eColorAttachmentOutput, | 
 |                                   vk::AccessFlagBits::eColorAttachmentWrite); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | void RenderFuncs::ObtainDepthTexture(Escher* escher, const bool use_protected_memory, | 
 |                                      const ImageInfo& info, vk::Format depth_stencil_format, | 
 |                                      TexturePtr& depth_texture) { | 
 |   // Support for other sample_counts should fairly easy to add, if necessary. | 
 |   FX_DCHECK(info.sample_count == 1); | 
 |  | 
 |   const bool realloc_textures = | 
 |       !depth_texture || (depth_texture->image()->use_protected_memory() != use_protected_memory) || | 
 |       info.width != depth_texture->width() || info.height != depth_texture->height(); | 
 |  | 
 |   // If the depth buffer does not exist, or if the depth buffer has a different | 
 |   // size than the output buffer, recreate it. | 
 |   if (realloc_textures) { | 
 |     // Need to generate a new depth buffer. | 
 |     { | 
 |       TRACE_DURATION("gfx", "RenderFuncs::ObtainDepthAndMsaaTextures (new depth)"); | 
 |       depth_texture = escher->NewAttachmentTexture( | 
 |           depth_stencil_format, info.width, info.height, 1, vk::Filter::eLinear, | 
 |           vk::ImageUsageFlags(), /*use_unnormalized_coordinates=*/false, | 
 |           use_protected_memory ? vk::MemoryPropertyFlagBits::eProtected | 
 |                                : vk::MemoryPropertyFlags()); | 
 |     } | 
 |   } | 
 | } | 
 | }  // namespace escher |