| // Copyright 2018 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/paper/paper_render_funcs.h" |
| |
| #include "src/lib/fxl/logging.h" |
| #include "src/ui/lib/escher/geometry/clip_planes.h" |
| #include "src/ui/lib/escher/paper/paper_render_queue_context.h" |
| #include "src/ui/lib/escher/paper/paper_shader_structs.h" |
| #include "src/ui/lib/escher/renderer/frame.h" |
| #include "src/ui/lib/escher/renderer/render_queue_item.h" |
| #include "src/ui/lib/escher/shape/mesh.h" |
| #include "src/ui/lib/escher/util/trace_macros.h" |
| #include "src/ui/lib/escher/vk/texture.h" |
| |
| namespace { |
| |
| constexpr uint32_t kMeshAttributeBindingLocation_Position2D = 0; |
| constexpr uint32_t kMeshAttributeBindingLocation_Position3D = 0; |
| constexpr uint32_t kMeshAttributeBindingLocation_PositionOffset = 1; |
| constexpr uint32_t kMeshAttributeBindingLocation_UV = 2; |
| constexpr uint32_t kMeshAttributeBindingLocation_PerimeterPos = 3; |
| constexpr uint32_t kMeshAttributeBindingLocation_BlendWeight = 4; |
| |
| } // anonymous namespace |
| |
| namespace escher { |
| |
| void PaperRenderFuncs::MeshData::Bind(CommandBuffer* cb) const { |
| index_binding.Bind(cb); |
| for (uint32_t i = 0; i < vertex_binding_count; ++i) { |
| vertex_bindings[i].Bind(cb); |
| } |
| for (uint32_t i = 0; i < vertex_attribute_count; ++i) { |
| vertex_attributes[i].Bind(cb); |
| } |
| for (uint32_t i = 0; i < uniform_binding_count; ++i) { |
| uniform_bindings[i].Bind(cb); |
| } |
| cb->BindTexture(1, 1, texture); |
| } |
| |
| void PaperRenderFuncs::RenderMesh(CommandBuffer* cb, |
| const RenderQueueContext* context_in, |
| const RenderQueueItem* items, |
| uint32_t instance_count) { |
| TRACE_DURATION("gfx", "PaperRenderFuncs::RenderMesh"); |
| FXL_DCHECK(cb && items && instance_count > 0); |
| FXL_DCHECK(context_in); |
| auto* context = static_cast<const PaperRenderQueueContext*>(context_in); |
| auto* mesh_data = static_cast<const MeshData*>(items[0].object_data); |
| const PaperRendererDrawMode draw_mode = context->draw_mode(); |
| |
| uint32_t num_indices = |
| draw_mode == PaperRendererDrawMode::kShadowVolumeGeometry |
| ? mesh_data->num_shadow_volume_indices |
| : mesh_data->num_indices; |
| if (num_indices == 0) { |
| // The only way this should happen is when rendering shadow-volume geometry |
| // for a non-shadow-caster. |
| FXL_DCHECK(draw_mode == PaperRendererDrawMode::kShadowVolumeGeometry); |
| return; |
| } |
| |
| // Set up per-object state. |
| mesh_data->Bind(cb); |
| |
| // TODO(ES-158): this assumes that all meshes in this render-queue pass are |
| // drawn exactly the same way. We will need something better soon. |
| cb->SetShaderProgram(context->shader_program(), |
| mesh_data->texture->sampler()->is_immutable() |
| ? mesh_data->texture->sampler() |
| : nullptr); |
| |
| // For each instance, set up per-instance state and draw. |
| for (uint32_t i = 0; i < instance_count; ++i) { |
| FXL_DCHECK(items[i].object_data == mesh_data); |
| |
| const MeshDrawData* instance_data = |
| static_cast<const MeshDrawData*>(items[i].instance_data); |
| |
| if (draw_mode == PaperRendererDrawMode::kShadowVolumeGeometry && |
| (instance_data->flags & PaperDrawableFlagBits::kDisableShadowCasting)) { |
| // This instance shouldn't draw shadows; continue to the next one. |
| continue; |
| } |
| |
| auto& b = instance_data->object_properties; |
| cb->BindUniformBuffer(b.descriptor_set_index, b.binding_index, b.buffer, |
| b.offset, b.size); |
| cb->DrawIndexed(num_indices); |
| } |
| } |
| |
| // Helper for PaperRenderFuncs::NewMeshData(). |
| static PaperRenderFuncs::VertexAttributeBinding* FillVertexAttributeBindings( |
| PaperRenderFuncs::VertexAttributeBinding* binding, uint32_t binding_index, |
| MeshAttributes attributes) { |
| using VertexAttributeBinding = PaperRenderFuncs::VertexAttributeBinding; |
| |
| if (attributes & MeshAttribute::kPosition2D) { |
| *binding++ = VertexAttributeBinding{ |
| .binding_index = binding_index, |
| .attribute_index = kMeshAttributeBindingLocation_Position2D, |
| .format = vk::Format::eR32G32Sfloat, |
| .offset = |
| GetMeshAttributeOffset(attributes, MeshAttribute::kPosition2D)}; |
| } |
| if (attributes & MeshAttribute::kPosition3D) { |
| *binding++ = VertexAttributeBinding{ |
| .binding_index = binding_index, |
| .attribute_index = kMeshAttributeBindingLocation_Position3D, |
| .format = vk::Format::eR32G32B32Sfloat, |
| .offset = |
| GetMeshAttributeOffset(attributes, MeshAttribute::kPosition3D)}; |
| } |
| if (attributes & MeshAttribute::kPositionOffset) { |
| *binding++ = VertexAttributeBinding{ |
| .binding_index = binding_index, |
| .attribute_index = kMeshAttributeBindingLocation_PositionOffset, |
| .format = vk::Format::eR32G32Sfloat, |
| .offset = |
| GetMeshAttributeOffset(attributes, MeshAttribute::kPositionOffset)}; |
| } |
| if (attributes & MeshAttribute::kUV) { |
| *binding++ = VertexAttributeBinding{ |
| .binding_index = binding_index, |
| .attribute_index = kMeshAttributeBindingLocation_UV, |
| .format = vk::Format::eR32G32Sfloat, |
| .offset = GetMeshAttributeOffset(attributes, MeshAttribute::kUV)}; |
| } |
| if (attributes & MeshAttribute::kPerimeterPos) { |
| *binding++ = VertexAttributeBinding{ |
| .binding_index = binding_index, |
| .attribute_index = kMeshAttributeBindingLocation_PerimeterPos, |
| .format = vk::Format::eR32G32Sfloat, |
| .offset = |
| GetMeshAttributeOffset(attributes, MeshAttribute::kPerimeterPos)}; |
| } |
| if (attributes & MeshAttribute::kBlendWeight1) { |
| *binding++ = VertexAttributeBinding{ |
| .binding_index = binding_index, |
| .attribute_index = kMeshAttributeBindingLocation_BlendWeight, |
| .format = vk::Format::eR32Sfloat, |
| .offset = |
| GetMeshAttributeOffset(attributes, MeshAttribute::kBlendWeight1)}; |
| } |
| return binding; |
| } |
| |
| PaperRenderFuncs::MeshData* PaperRenderFuncs::NewMeshData( |
| const FramePtr& frame, Mesh* mesh, const TexturePtr& texture, |
| uint32_t num_indices, uint32_t num_shadow_volume_indices) { |
| TRACE_DURATION("gfx", "PaperRenderFuncs::NewMeshData"); |
| FXL_DCHECK(mesh); |
| FXL_DCHECK(texture); |
| auto& mesh_spec = mesh->spec(); |
| |
| // TODO(ES-103): avoid reaching in to impl::CommandBuffer for keep-alive. |
| frame->command_buffer()->KeepAlive(mesh); |
| frame->command_buffer()->KeepAlive(texture.get()); |
| |
| // TODO(ES-104): Replace TakeWaitSemaphore() with something better. |
| frame->command_buffer()->TakeWaitSemaphore( |
| mesh, vk::PipelineStageFlagBits::eVertexInput); |
| |
| auto* obj = frame->Allocate<MeshData>(); |
| |
| obj->index_binding.index_buffer = mesh->vk_index_buffer(); |
| obj->index_binding.index_type = MeshSpec::IndexTypeEnum; |
| obj->index_binding.index_buffer_offset = mesh->index_buffer_offset(); |
| obj->num_indices = num_indices; |
| obj->num_shadow_volume_indices = num_shadow_volume_indices; |
| |
| // Set up vertex buffer bindings. |
| obj->vertex_binding_count = mesh_spec.vertex_buffer_count(); |
| obj->vertex_bindings = |
| frame->AllocateMany<VertexBinding>(obj->vertex_binding_count); |
| |
| { |
| uint32_t binding_count = 0; |
| for (uint32_t i = 0; i < VulkanLimits::kNumVertexBuffers; ++i) { |
| if (auto& attribute_buffer = mesh->attribute_buffer(i)) { |
| // TODO(ES-103): avoid reaching in to impl::CommandBuffer for |
| // keep-alive. |
| frame->command_buffer()->KeepAlive(attribute_buffer.buffer); |
| |
| obj->vertex_bindings[binding_count++] = |
| VertexBinding{.binding_index = i, |
| .buffer = attribute_buffer.buffer->vk(), |
| .offset = attribute_buffer.offset, |
| .stride = attribute_buffer.stride}; |
| } |
| } |
| FXL_DCHECK(binding_count == obj->vertex_binding_count); |
| } |
| |
| // Set up vertex attribute bindings. |
| obj->vertex_attribute_count = mesh_spec.total_attribute_count(); |
| obj->vertex_attributes = |
| frame->AllocateMany<VertexAttributeBinding>(obj->vertex_attribute_count); |
| { |
| VertexAttributeBinding* current = obj->vertex_attributes; |
| for (uint32_t i = 0; i < VulkanLimits::kNumVertexBuffers; ++i) { |
| if (mesh_spec.attribute_count(i) > 0) { |
| current = |
| FillVertexAttributeBindings(current, i, mesh_spec.attributes[i]); |
| } |
| } |
| |
| // Sanity check that we filled in the correct number of attributes. |
| FXL_DCHECK(current == |
| (obj->vertex_attributes + obj->vertex_attribute_count)); |
| } |
| |
| obj->uniform_binding_count = 0; |
| obj->texture = texture.get(); |
| |
| return obj; |
| } |
| |
| PaperRenderFuncs::MeshDrawData* PaperRenderFuncs::NewMeshDrawData( |
| const FramePtr& frame, const mat4& transform, const vec4& color, |
| PaperDrawableFlags flags) { |
| MeshDrawData* draw_data = frame->Allocate<MeshDrawData>(); |
| |
| auto writable_binding = |
| NewPaperShaderUniformBinding<PaperShaderMeshInstance>(frame); |
| writable_binding.first->model_transform = transform; |
| writable_binding.first->color = color; |
| // TODO(ES-152): populate field for vertex-shader clip-planes. |
| |
| draw_data->object_properties = writable_binding.second; |
| draw_data->flags = flags; |
| |
| return draw_data; |
| } |
| |
| } // namespace escher |