diff --git a/plume_metal.cpp b/plume_metal.cpp index 50a02b8..d4ffe4d 100644 --- a/plume_metal.cpp +++ b/plume_metal.cpp @@ -26,11 +26,6 @@ namespace plume { static constexpr size_t PUSH_CONSTANT_MAX_INDEX = 15; static constexpr size_t VERTEX_BUFFER_MAX_INDEX = 30; - static constexpr uint32_t COLOR_COUNT = DESCRIPTOR_SET_MAX_INDEX; - static constexpr uint32_t DEPTH_INDEX = COLOR_COUNT; - static constexpr uint32_t STENCIL_INDEX = DEPTH_INDEX + 1; - static constexpr uint32_t ATTACHMENT_COUNT = STENCIL_INDEX + 1; - // MARK: - Prototypes MTL::PixelFormat mapPixelFormat(RenderFormat format); @@ -1435,9 +1430,17 @@ namespace plume { // Set index right after push constants, clamp at Metal's limit of 31 const uint32_t vertexBufferIndex = std::min(PUSH_CONSTANT_MAX_INDEX + 1 + inputSlot.index, VERTEX_BUFFER_MAX_INDEX); MTL::VertexBufferLayoutDescriptor *layout = vertexDescriptor->layouts()->object(vertexBufferIndex); - layout->setStride(inputSlot.stride); - layout->setStepFunction(mapVertexStepFunction(inputSlot.classification)); - layout->setStepRate((layout->stepFunction() == MTL::VertexStepFunctionPerInstance) ? inputSlot.stride : 1); + if (inputSlot.stride == 0) { + // Metal does not support stride 0, we must provide a + // substitute "null" buffer to match behaviour of other robust APIs. + layout->setStride(1); + layout->setStepFunction(MTL::VertexStepFunctionConstant); + layout->setStepRate(0); + } else { + layout->setStride(inputSlot.stride); + layout->setStepFunction(mapVertexStepFunction(inputSlot.classification)); + layout->setStepRate((layout->stepFunction() == MTL::VertexStepFunctionPerInstance) ? inputSlot.stride : 1); + } } for (uint32_t i = 0; i < desc.inputElementsCount; i++) { @@ -1605,6 +1608,10 @@ namespace plume { } MetalDescriptorSet::~MetalDescriptorSet() { + for (const auto resource: toReleaseOnDestruction) { + resource->release(); + } + for (const auto &entry : resourceEntries) { if (entry.resource != nullptr) { entry.resource->release(); @@ -1700,8 +1707,7 @@ namespace plume { if (dtype != MTL::DataTypeSampler) { if (resourceEntries[descriptorIndex].resource != nullptr) { - resourceEntries[descriptorIndex].resource->release(); - resourceEntries[descriptorIndex].resource = nullptr; + toReleaseOnDestruction.push_back(resourceEntries[descriptorIndex].resource); } } @@ -2185,7 +2191,7 @@ namespace plume { pushConstants[rangeIndex].binding = range.binding; pushConstants[rangeIndex].set = range.set; pushConstants[rangeIndex].offset = range.offset; - pushConstants[rangeIndex].size = range.size; + pushConstants[rangeIndex].size = alignUp(range.size); pushConstants[rangeIndex].stageFlags = range.stageFlags; dirtyComputeState.pushConstants = 1; @@ -2236,7 +2242,7 @@ namespace plume { pushConstants[rangeIndex].binding = range.binding; pushConstants[rangeIndex].set = range.set; pushConstants[rangeIndex].offset = range.offset; - pushConstants[rangeIndex].size = range.size; + pushConstants[rangeIndex].size = alignUp(range.size); pushConstants[rangeIndex].stageFlags = range.stageFlags; dirtyGraphicsState.pushConstants = 1; @@ -2291,9 +2297,14 @@ namespace plume { // Check for changes in bindings for (uint32_t i = 0; i < viewCount; i++) { const MetalBuffer* interfaceBuffer = static_cast(views[i].buffer.ref); - const uint64_t newOffset = views[i].buffer.offset; + uint64_t newOffset = views[i].buffer.offset; const uint32_t newIndex = startSlot + i; + if (interfaceBuffer == nullptr) { + interfaceBuffer = static_cast(queue->device->nullBuffer.get()); + newOffset = 0; + } + vertexBuffers[i] = interfaceBuffer->mtl; vertexBufferOffsets[i] = newOffset; vertexBufferIndices[i] = newIndex; @@ -2931,10 +2942,21 @@ namespace plume { } if (dirtyGraphicsState.vertexBuffers) { + std::array slotUsed{}; + for (uint32_t i = 0; i < viewCount; i++) { // Bind right after the push constants, up till the max vertex buffer index const uint32_t bindIndex = std::min(PUSH_CONSTANT_MAX_INDEX + 1 + vertexBufferIndices[i], VERTEX_BUFFER_MAX_INDEX); activeRenderEncoder->setVertexBuffer(vertexBuffers[i], vertexBufferOffsets[i], bindIndex); + slotUsed[bindIndex] = true; + } + + // Bind all unbound slots to the null buffer + auto nullBuffer = static_cast(queue->device->nullBuffer.get()); + for (uint32_t i = PUSH_CONSTANT_MAX_INDEX + 1; i <= VERTEX_BUFFER_MAX_INDEX; i++) { + if (!slotUsed[i]) { + activeRenderEncoder->setVertexBuffer(nullBuffer->mtl, 0, i); + } } stateCache.lastVertexBuffers = vertexBuffers; @@ -3231,6 +3253,8 @@ namespace plume { capabilities.dynamicDepthBias = true; capabilities.uma = mtl->hasUnifiedMemory(); capabilities.queryPools = false; + + nullBuffer = createBuffer(RenderBufferDesc::DefaultBuffer(16, RenderBufferFlag::VERTEX)); } MetalDevice::~MetalDevice() { diff --git a/plume_metal.h b/plume_metal.h index 55b0b6b..375f1c4 100644 --- a/plume_metal.h +++ b/plume_metal.h @@ -209,6 +209,7 @@ namespace plume { std::vector descriptors; MetalArgumentBuffer argumentBuffer; std::vector resourceEntries; + std::vector toReleaseOnDestruction; MetalDescriptorSet(MetalDevice *device, const RenderDescriptorSetDesc &desc); MetalDescriptorSet(MetalDevice *device, uint32_t entryCount); @@ -593,6 +594,8 @@ namespace plume { // Blit functionality MTL::BlitPassDescriptor *sharedBlitDescriptor = nullptr; + std::unique_ptr nullBuffer; + explicit MetalDevice(MetalInterface *renderInterface, const std::string &preferredDeviceName); ~MetalDevice() override; std::unique_ptr createDescriptorSet(const RenderDescriptorSetDesc &desc) override;