#import #include "drape/metal/metal_base_context.hpp" #include "drape/mesh_object.hpp" #include "drape/pointers.hpp" #include "base/assert.hpp" #include #include #include namespace dp { namespace metal { namespace { MTLPrimitiveType GetPrimitiveType(MeshObject::DrawPrimitive primitive) { switch (primitive) { case MeshObject::DrawPrimitive::Triangles: return MTLPrimitiveTypeTriangle; case MeshObject::DrawPrimitive::TriangleStrip: return MTLPrimitiveTypeTriangleStrip; case MeshObject::DrawPrimitive::LineStrip: return MTLPrimitiveTypeLineStrip; } CHECK(false, ("Unsupported type")); } } // namespace class MetalMeshObjectImpl : public MeshObjectImpl { public: MetalMeshObjectImpl(ref_ptr mesh) : m_mesh(std::move(mesh)) {} void Build(ref_ptr context, ref_ptr program) override { ref_ptr metalContext = context; id device = metalContext->GetMetalDevice(); m_geometryBuffers.resize(m_mesh->m_buffers.size()); for (size_t i = 0; i < m_mesh->m_buffers.size(); i++) { auto const sizeInBytes = m_mesh->m_buffers[i]->GetSizeInBytes(); if (sizeInBytes == 0) continue; m_geometryBuffers[i] = [device newBufferWithBytes:m_mesh->m_buffers[i]->GetData() length:sizeInBytes options:MTLResourceCPUCacheModeWriteCombined]; std::ostringstream ss; ss << "MeshVB:"; for (size_t j = 0; j < m_mesh->m_buffers[i]->m_attributes.size(); j++) { ss << m_mesh->m_buffers[i]->m_attributes[j].m_attributeName; if (j + 1 < m_mesh->m_buffers[i]->m_attributes.size()) ss << "+"; } m_geometryBuffers[i].label = @(ss.str().c_str()); } if (!m_mesh->m_indices.empty()) { m_indexBuffer = [device newBufferWithBytes:m_mesh->m_indices.data() length:m_mesh->m_indices.size() * sizeof(uint16_t) options:MTLResourceCPUCacheModeWriteCombined]; m_indexBuffer.label = @"MeshIB"; } } void Reset() override { m_geometryBuffers.clear(); m_indexBuffer = nil; } void UpdateBuffer(ref_ptr context, uint32_t bufferInd) override { UNUSED_VALUE(context); CHECK_LESS(bufferInd, static_cast(m_geometryBuffers.size()), ()); auto & buffer = m_mesh->m_buffers[bufferInd]; auto const sizeInBytes = buffer->GetSizeInBytes(); CHECK(sizeInBytes != 0, ()); uint8_t * bufferPointer = (uint8_t *)[m_geometryBuffers[bufferInd] contents]; memcpy(bufferPointer, buffer->GetData(), sizeInBytes); } void UpdateIndexBuffer(ref_ptr context) override { UNUSED_VALUE(context); CHECK(m_indexBuffer != nil, ()); auto const sizeInBytes = m_mesh->m_indices.size() * sizeof(uint16_t); CHECK(sizeInBytes != 0, ()); uint8_t * bufferPointer = (uint8_t *)[m_indexBuffer contents]; memcpy(bufferPointer, m_mesh->m_indices.data(), sizeInBytes); } void Bind(ref_ptr program) override {} void Unbind() override {} void DrawPrimitives(ref_ptr context, uint32_t vertexCount, uint32_t startVertex) override { ref_ptr metalContext = context; if (!metalContext->HasAppliedPipelineState()) return; id encoder = metalContext->GetCommandEncoder(); for (size_t i = 0; i < m_geometryBuffers.size(); i++) [encoder setVertexBuffer:m_geometryBuffers[i] offset:0 atIndex:i]; [encoder drawPrimitives:GetPrimitiveType(m_mesh->m_drawPrimitive) vertexStart:startVertex vertexCount:vertexCount]; } void DrawPrimitivesIndexed(ref_ptr context, uint32_t indexCount, uint32_t startIndex) override { ref_ptr metalContext = context; if (!metalContext->HasAppliedPipelineState()) return; id encoder = metalContext->GetCommandEncoder(); for (size_t i = 0; i < m_geometryBuffers.size(); i++) [encoder setVertexBuffer:m_geometryBuffers[i] offset:0 atIndex:i]; CHECK(m_indexBuffer != nil, ()); [encoder drawIndexedPrimitives:GetPrimitiveType(m_mesh->m_drawPrimitive) indexCount:indexCount indexType:MTLIndexTypeUInt16 indexBuffer:m_indexBuffer indexBufferOffset:startIndex * sizeof(uint16_t)]; } private: ref_ptr m_mesh; std::vector> m_geometryBuffers; id m_indexBuffer; }; } // namespace metal #ifdef OMIM_METAL_AVAILABLE void MeshObject::InitForMetal() { m_impl = make_unique_dp(make_ref(this)); } #endif // OMIM_METAL_AVAILABLE } // namespace dp