diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index 39f38578b6..84d326750e 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -174,7 +174,7 @@ void MetalDriver::createIndexBufferR(Handle ibh, ElementType elem void MetalDriver::createBufferObjectR(Handle boh, uint32_t byteCount, BufferObjectBinding bindingType) { - // TODO + construct_handle(mHandleMap, boh, *mContext, byteCount); } void MetalDriver::createTextureR(Handle th, SamplerType target, uint8_t levels, @@ -327,8 +327,7 @@ Handle MetalDriver::createIndexBufferS() noexcept { } Handle MetalDriver::createBufferObjectS() noexcept { - // TODO - return {}; + return alloc_handle(); } Handle MetalDriver::createTextureS() noexcept { @@ -411,7 +410,7 @@ void MetalDriver::destroyIndexBuffer(Handle ibh) { void MetalDriver::destroyBufferObject(Handle boh) { if (boh) { - // TODO + destruct_handle(mHandleMap, boh); } } @@ -668,12 +667,18 @@ void MetalDriver::updateIndexBuffer(Handle ibh, BufferDescriptor& void MetalDriver::updateBufferObject(Handle boh, BufferDescriptor&& data, uint32_t byteOffset) { - // TODO + auto* bo = handle_cast(mHandleMap, boh); + bo->updateBuffer(data.buffer, data.size, byteOffset); + scheduleDestroy(std::move(data)); } void MetalDriver::setVertexBufferObject(Handle vbh, size_t index, Handle boh) { - // TODO + auto* vertexBuffer = handle_cast(mHandleMap, vbh); + auto* bufferObject = handle_cast(mHandleMap, boh); + assert_invariant(index < vertexBuffer->buffers.size()); + assert_invariant(bufferObject->getBuffer()); + vertexBuffer->buffers[index] = bufferObject->getBuffer(); } void MetalDriver::update2DImage(Handle th, uint32_t level, uint32_t xoffset, @@ -1289,9 +1294,28 @@ void MetalDriver::draw(backend::PipelineState ps, Handle rph) withRange:samplerRange]; // Bind the vertex buffers. + + MetalBuffer* buffers[MAX_VERTEX_ATTRIBUTE_COUNT]; + size_t vertexBufferOffsets[MAX_VERTEX_ATTRIBUTE_COUNT]; + size_t bufferIndex = 0; + + auto vb = primitive->vertexBuffer; + for (uint32_t attributeIndex = 0; attributeIndex < vb->attributes.size(); attributeIndex++) { + const auto& attribute = vb->attributes[attributeIndex]; + if (attribute.buffer == Attribute::BUFFER_UNUSED) { + continue; + } + + assert_invariant(vb->buffers[attribute.buffer]); + buffers[bufferIndex] = vb->buffers[attribute.buffer].get(); + vertexBufferOffsets[bufferIndex] = attribute.offset; + bufferIndex++; + } + + const auto bufferCount = bufferIndex; MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder, - VERTEX_BUFFER_START, MetalBuffer::Stage::VERTEX, primitive->buffers.data(), - primitive->offsets.data(), primitive->buffers.size()); + VERTEX_BUFFER_START, MetalBuffer::Stage::VERTEX, buffers, + vertexBufferOffsets, bufferCount); // Bind the zero buffer, used for missing vertex attributes. static const char bytes[16] = { 0 }; diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 7d021b6c80..cfc1be1e8e 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -112,12 +112,24 @@ private: void* frameCompletedUserData = nullptr; }; +class MetalBufferObject : public HwBufferObject { +public: + MetalBufferObject(MetalContext& context, uint32_t byteCount); + + void updateBuffer(void* data, size_t size, uint32_t byteOffset); + const std::shared_ptr& getBuffer() const { return buffer; } + +private: + uint32_t byteCount; + std::shared_ptr buffer = nullptr; + +}; + struct MetalVertexBuffer : public HwVertexBuffer { MetalVertexBuffer(MetalContext& context, uint8_t bufferCount, uint8_t attributeCount, uint32_t vertexCount, AttributeArray const& attributes, bool bufferObjectsEnabled); - ~MetalVertexBuffer(); - std::vector buffers; + std::vector> buffers; }; struct MetalIndexBuffer : public HwIndexBuffer { @@ -142,9 +154,6 @@ struct MetalRenderPrimitive : public HwRenderPrimitive { // This struct is used to create the pipeline description to describe vertex assembly. VertexDescription vertexDescription = {}; - - std::vector buffers; - std::vector offsets; }; struct MetalProgram : public HwProgram { diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index a7186e0c88..0a374762f7 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -266,12 +266,27 @@ void MetalSwapChain::scheduleFrameCompletedCallback() { }]; } +MetalBufferObject::MetalBufferObject(MetalContext& context, uint32_t byteCount) + : byteCount(byteCount), buffer(std::make_shared(context, byteCount)) {} + +void MetalBufferObject::updateBuffer(void* data, size_t size, uint32_t byteOffset) { + assert_invariant(byteOffset + size <= byteCount); + buffer->copyIntoBuffer(data, size); +} + MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, uint8_t bufferCount, uint8_t attributeCount, uint32_t vertexCount, AttributeArray const& attributes, bool bufferObjectsEnabled) : HwVertexBuffer(bufferCount, attributeCount, vertexCount, attributes, bufferObjectsEnabled) { buffers.reserve(bufferCount); + if (bufferObjectsEnabled) { + // If BufferObjects are used, we don't need to make any allocations. Simply reserve enough + // "slots" for future calls to setVertexBufferObject. + buffers.resize(bufferCount); + return; + } + for (uint8_t bufferIndex = 0; bufferIndex < bufferCount; ++bufferIndex) { // Calculate buffer size. uint32_t size = 0; @@ -282,21 +297,14 @@ MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, uint8_t bufferCount, } } - MetalBuffer* buffer = nullptr; + std::shared_ptr buffer = nullptr; if (size > 0) { - buffer = new MetalBuffer(context, size); + buffer = std::make_shared(context, size); } buffers.push_back(buffer); } } -MetalVertexBuffer::~MetalVertexBuffer() { - for (auto* b : buffers) { - delete b; - } - buffers.clear(); -} - MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, uint8_t elementSize, uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount), buffer(context, elementSize * indexCount, true) { } @@ -310,10 +318,6 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalInde const size_t attributeCount = vertexBuffer->attributes.size(); - buffers.clear(); - buffers.reserve(attributeCount); - offsets.clear(); - offsets.reserve(attributeCount); vertexDescription = {}; // Each attribute gets its own vertex buffer. @@ -340,9 +344,6 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalInde continue; } - buffers.push_back(vertexBuffer->buffers[attribute.buffer]); - offsets.push_back(attribute.offset); - vertexDescription.attributes[attributeIndex] = { .format = getMetalFormat(attribute.type, attribute.flags & Attribute::FLAG_NORMALIZED),