Support the VK_KHR_imageless_framebuffer extension.
I'm not sure this is the elegant way but it works in my own
project:)
diff --git a/Docs/MoltenVK_Runtime_UserGuide.md b/Docs/MoltenVK_Runtime_UserGuide.md
index 3ee145f..413436c 100644
--- a/Docs/MoltenVK_Runtime_UserGuide.md
+++ b/Docs/MoltenVK_Runtime_UserGuide.md
@@ -273,6 +273,7 @@
- `VK_KHR_get_memory_requirements2`
- `VK_KHR_get_physical_device_properties2`
- `VK_KHR_get_surface_capabilities2`
+- `VK_KHR_imageless_framebuffer`
- `VK_KHR_image_format_list`
- `VK_KHR_maintenance1`
- `VK_KHR_maintenance2`
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h
index 70c7ba2..c5b76f8 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h
@@ -50,6 +50,7 @@
MVKFramebuffer* _framebuffer;
VkRect2D _renderArea;
VkSubpassContents _contents;
+ MVKSmallVector<MVKImageView*, 8> _imagelessAttachments;
};
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm
index 7f523f1..a987c52 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm
@@ -36,6 +36,20 @@
_framebuffer = (MVKFramebuffer*)pRenderPassBegin->framebuffer;
_renderArea = pRenderPassBegin->renderArea;
+ for (auto* next = (const VkBaseInStructure*)pRenderPassBegin->pNext; next; next = next->pNext) {
+ switch (next->sType) {
+ case VK_STRUCTURE_TYPE_RENDER_PASS_ATTACHMENT_BEGIN_INFO: {
+ const auto* pAttachmentBegin = (VkRenderPassAttachmentBeginInfo*)next;
+ for(uint32_t i = 0; i < pAttachmentBegin->attachmentCount; i++) {
+ _imagelessAttachments.push_back((MVKImageView*)pAttachmentBegin->pAttachments[i]);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
return VK_SUCCESS;
}
@@ -70,7 +84,7 @@
template <size_t N>
void MVKCmdBeginRenderPass<N>::encode(MVKCommandEncoder* cmdEncoder) {
// MVKLogDebug("Encoding vkCmdBeginRenderPass(). Elapsed time: %.6f ms.", mvkGetElapsedMilliseconds());
- cmdEncoder->beginRenderpass(this, _contents, _renderPass, _framebuffer, _renderArea, _clearValues.contents());
+ cmdEncoder->beginRenderpass(this, _contents, _renderPass, _framebuffer, _renderArea, _clearValues.contents(), _imagelessAttachments.contents());
}
template class MVKCmdBeginRenderPass<1>;
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
index 6bc4f4e..9cd4ab5 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
@@ -275,7 +275,8 @@
MVKRenderPass* renderPass,
MVKFramebuffer* framebuffer,
VkRect2D& renderArea,
- MVKArrayRef<VkClearValue> clearValues);
+ MVKArrayRef<VkClearValue> clearValues,
+ MVKArrayRef<MVKImageView*> imageless_attachments);
/** Begins the next render subpass. */
void beginNextSubpass(MVKCommand* subpassCmd, VkSubpassContents renderpassContents);
@@ -494,6 +495,7 @@
VkRect2D _renderArea;
MVKActivatedQueries* _pActivatedQueries;
MVKSmallVector<VkClearValue, kMVKDefaultAttachmentCount> _clearValues;
+ MVKSmallVector<MVKImageView*, kMVKDefaultAttachmentCount> _imagelessAttachments;
id<MTLComputeCommandEncoder> _mtlComputeEncoder;
MVKCommandUse _mtlComputeEncoderUse;
id<MTLBlitCommandEncoder> _mtlBlitEncoder;
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
index 2de0f53..54424cd 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
@@ -288,13 +288,17 @@
MVKRenderPass* renderPass,
MVKFramebuffer* framebuffer,
VkRect2D& renderArea,
- MVKArrayRef<VkClearValue> clearValues) {
+ MVKArrayRef<VkClearValue> clearValues,
+ MVKArrayRef<MVKImageView*> imagelessAttachments) {
_renderPass = renderPass;
_framebuffer = framebuffer;
_renderArea = renderArea;
_isRenderingEntireAttachment = (mvkVkOffset2DsAreEqual(_renderArea.offset, {0,0}) &&
mvkVkExtent2DsAreEqual(_renderArea.extent, _framebuffer->getExtent2D()));
_clearValues.assign(clearValues.begin(), clearValues.end());
+ for(auto* v : imagelessAttachments) {
+ _imagelessAttachments.push_back(v);
+ }
setSubpass(passCmd, subpassContents, 0);
}
@@ -334,7 +338,7 @@
endCurrentMetalEncoding();
MTLRenderPassDescriptor* mtlRPDesc = [MTLRenderPassDescriptor renderPassDescriptor];
- getSubpass()->populateMTLRenderPassDescriptor(mtlRPDesc, _multiviewPassIndex, _framebuffer, _clearValues.contents(), _isRenderingEntireAttachment, loadOverride);
+ getSubpass()->populateMTLRenderPassDescriptor(mtlRPDesc, _multiviewPassIndex, _framebuffer, _imagelessAttachments.contents(), _clearValues.contents(), _isRenderingEntireAttachment, loadOverride);
if (_cmdBuffer->_needsVisibilityResultMTLBuffer) {
if (!_visibilityResultMTLBuffer) {
_visibilityResultMTLBuffer = getTempMTLBuffer(_pDeviceMetalFeatures->maxQueryBufferSize, true, true);
@@ -393,7 +397,10 @@
}
void MVKCommandEncoder::encodeStoreActions(bool storeOverride) {
- getSubpass()->encodeStoreActions(this, _isRenderingEntireAttachment, storeOverride);
+ getSubpass()->encodeStoreActions(this,
+ _isRenderingEntireAttachment,
+ _imagelessAttachments.contents(),
+ storeOverride);
}
MVKRenderSubpass* MVKCommandEncoder::getSubpass() { return _renderPass->getSubpass(_renderSubpassIndex); }
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
index b1bd357..8eef871 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
@@ -738,6 +738,7 @@
const VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT _enabledTexelBuffAlignFeatures;
const VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT _enabledVtxAttrDivFeatures;
const VkPhysicalDevicePortabilitySubsetFeaturesKHR _enabledPortabilityFeatures;
+ const VkPhysicalDeviceImagelessFramebufferFeaturesKHR _enabledImagelessFramebufferFeatures;
/** The list of Vulkan extensions, indicating whether each has been enabled by the app for this device. */
const MVKExtensionList _enabledExtensions;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index e70de7e..013b251 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -267,6 +267,11 @@
inlineUniformBlockFeatures->descriptorBindingInlineUniformBlockUpdateAfterBind = true;
break;
}
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES: {
+ auto* imagelessFramebufferFeatures = (VkPhysicalDeviceImagelessFramebufferFeaturesKHR*)next;
+ imagelessFramebufferFeatures->imagelessFramebuffer = true;
+ break;
+ }
default:
break;
}
@@ -3789,6 +3794,7 @@
_enabledVtxAttrDivFeatures(),
_enabledPrivateDataFeatures(),
_enabledPortabilityFeatures(),
+ _enabledImagelessFramebufferFeatures(),
_enabledExtensions(this),
_isCurrentlyAutoGPUCapturing(false)
{
@@ -3899,11 +3905,16 @@
mvkClear(&_enabledTexelBuffAlignFeatures);
mvkClear(&_enabledVtxAttrDivFeatures);
mvkClear(&_enabledPortabilityFeatures);
+ mvkClear(&_enabledImagelessFramebufferFeatures);
+ VkPhysicalDeviceImagelessFramebufferFeaturesKHR pdImagelessFramebufferFeatures;
+ pdImagelessFramebufferFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES;
+ pdImagelessFramebufferFeatures.pNext = NULL;
+
// Fetch the available physical device features.
VkPhysicalDevicePortabilitySubsetFeaturesKHR pdPortabilityFeatures;
pdPortabilityFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR;
- pdPortabilityFeatures.pNext = NULL;
+ pdPortabilityFeatures.pNext = &pdImagelessFramebufferFeatures;
VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT pdVtxAttrDivFeatures;
pdVtxAttrDivFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT;
@@ -4088,6 +4099,13 @@
&pdPortabilityFeatures.constantAlphaColorBlendFactors, 15);
break;
}
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES: {
+ auto* requestedFeatures = (VkPhysicalDeviceImagelessFramebufferFeaturesKHR*)next;
+ enableFeatures(&_enabledImagelessFramebufferFeatures.imagelessFramebuffer,
+ &requestedFeatures->imagelessFramebuffer,
+ &pdImagelessFramebufferFeatures.imagelessFramebuffer, 1);
+ break;
+ }
default:
break;
}
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.h b/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.h
index 16a2a22..0fc06b4 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.h
@@ -45,6 +45,7 @@
/** Returns the attachment at the specified index. */
inline MVKImageView* getAttachment(uint32_t index) { return _attachments[index]; }
+ inline bool getImageless() { return _imageless; }
#pragma mark Construction
@@ -57,5 +58,7 @@
VkExtent2D _extent;
uint32_t _layerCount;
MVKSmallVector<MVKImageView*, 4> _attachments;
+ bool _imageless;
+ MVKSmallVector<MVKImageView*, 4> _imagelessAttachments;
};
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.mm b/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.mm
index 66af186..18d442a 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKFramebuffer.mm
@@ -28,10 +28,15 @@
_extent = { .width = pCreateInfo->width, .height = pCreateInfo->height };
_layerCount = pCreateInfo->layers;
- // Add attachments
- _attachments.reserve(pCreateInfo->attachmentCount);
- for (uint32_t i = 0; i < pCreateInfo->attachmentCount; i++) {
- _attachments.push_back((MVKImageView*)pCreateInfo->pAttachments[i]);
+ if (pCreateInfo->flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT_KHR) {
+ _imageless = true;
+ }
+ else {
+ _imageless = false;
+ // Add attachments
+ _attachments.reserve(pCreateInfo->attachmentCount);
+ for (uint32_t i = 0; i < pCreateInfo->attachmentCount; i++) {
+ _attachments.push_back((MVKImageView*)pCreateInfo->pAttachments[i]);
+ }
}
}
-
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h
index fdb9077..8c0a89f 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h
@@ -96,6 +96,7 @@
void populateMTLRenderPassDescriptor(MTLRenderPassDescriptor* mtlRPDesc,
uint32_t passIdx,
MVKFramebuffer* framebuffer,
+ const MVKArrayRef<MVKImageView*>& imagelessAttachments,
const MVKArrayRef<VkClearValue>& clearValues,
bool isRenderingEntireAttachment,
bool loadOverride = false);
@@ -117,7 +118,10 @@
uint32_t caIdx, VkImageAspectFlags aspectMask);
/** If a render encoder is active, sets the store actions for all attachments to it. */
- void encodeStoreActions(MVKCommandEncoder* cmdEncoder, bool isRenderingEntireAttachment, bool storeOverride = false);
+ void encodeStoreActions(MVKCommandEncoder* cmdEncoder,
+ bool isRenderingEntireAttachment,
+ const MVKArrayRef<MVKImageView*>& imagelessAttachments,
+ bool storeOverride = false);
/** Constructs an instance for the specified parent renderpass. */
MVKRenderSubpass(MVKRenderPass* renderPass, const VkSubpassDescription* pCreateInfo,
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm
index 4a09569..0b8f2a9 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm
@@ -176,10 +176,12 @@
void MVKRenderSubpass::populateMTLRenderPassDescriptor(MTLRenderPassDescriptor* mtlRPDesc,
uint32_t passIdx,
MVKFramebuffer* framebuffer,
+ const MVKArrayRef<MVKImageView*>& imagelessAttachments,
const MVKArrayRef<VkClearValue>& clearValues,
bool isRenderingEntireAttachment,
bool loadOverride) {
MVKPixelFormats* pixFmts = _renderPass->getPixelFormats();
+ bool imageless = framebuffer->getImageless();
// Populate the Metal color attachments
uint32_t caCnt = getColorAttachmentCount();
@@ -195,7 +197,13 @@
uint32_t rslvRPAttIdx = _resolveAttachments.empty() ? VK_ATTACHMENT_UNUSED : _resolveAttachments[caIdx].attachment;
bool hasResolveAttachment = (rslvRPAttIdx != VK_ATTACHMENT_UNUSED);
if (hasResolveAttachment) {
- framebuffer->getAttachment(rslvRPAttIdx)->populateMTLRenderPassAttachmentDescriptorResolve(mtlColorAttDesc);
+ if (imageless) {
+ imagelessAttachments[rslvRPAttIdx]->populateMTLRenderPassAttachmentDescriptorResolve(mtlColorAttDesc);
+ }
+ else {
+ framebuffer->getAttachment(rslvRPAttIdx)->populateMTLRenderPassAttachmentDescriptorResolve(mtlColorAttDesc);
+ }
+
// In a multiview render pass, we need to override the starting layer to ensure
// only the enabled views are loaded.
if (isMultiview()) {
@@ -209,10 +217,17 @@
// Configure the color attachment
MVKRenderPassAttachment* clrMVKRPAtt = &_renderPass->_attachments[clrRPAttIdx];
- framebuffer->getAttachment(clrRPAttIdx)->populateMTLRenderPassAttachmentDescriptor(mtlColorAttDesc);
+ if (imageless) {
+ imagelessAttachments[clrRPAttIdx]->populateMTLRenderPassAttachmentDescriptor(mtlColorAttDesc);
+ }
+ else {
+ framebuffer->getAttachment(clrRPAttIdx)->populateMTLRenderPassAttachmentDescriptor(mtlColorAttDesc);
+ }
bool isMemorylessAttachment = false;
#if MVK_APPLE_SILICON
- isMemorylessAttachment = framebuffer->getAttachment(clrRPAttIdx)->getMTLTexture(0).storageMode == MTLStorageModeMemoryless;
+ isMemorylessAttachment = imageless
+ ? imagelessAttachments[clrRPAttIdx]->getMTLTexture(0).storageMode == MTLStorageModeMemoryless
+ : framebuffer->getAttachment(clrRPAttIdx)->getMTLTexture(0).storageMode == MTLStorageModeMemoryless;
#endif
if (clrMVKRPAtt->populateMTLRenderPassAttachmentDescriptor(mtlColorAttDesc, this,
isRenderingEntireAttachment,
@@ -236,12 +251,16 @@
uint32_t dsRslvRPAttIdx = _depthStencilResolveAttachment.attachment;
if (dsRPAttIdx != VK_ATTACHMENT_UNUSED) {
MVKRenderPassAttachment* dsMVKRPAtt = &_renderPass->_attachments[dsRPAttIdx];
- MVKImageView* dsImage = framebuffer->getAttachment(dsRPAttIdx);
+ MVKImageView* dsImage = imageless
+ ? imagelessAttachments[dsRPAttIdx]
+ : framebuffer->getAttachment(dsRPAttIdx);
MVKImageView* dsRslvImage = nullptr;
MTLPixelFormat mtlDSFormat = dsImage->getMTLPixelFormat(0);
if (dsRslvRPAttIdx != VK_ATTACHMENT_UNUSED) {
- dsRslvImage = framebuffer->getAttachment(dsRslvRPAttIdx);
+ dsRslvImage = imageless
+ ? imagelessAttachments[dsRslvRPAttIdx]
+ : framebuffer->getAttachment(dsRslvRPAttIdx);
}
if (pixFmts->isDepthFormat(mtlDSFormat)) {
@@ -366,6 +385,7 @@
void MVKRenderSubpass::encodeStoreActions(MVKCommandEncoder* cmdEncoder,
bool isRenderingEntireAttachment,
+ const MVKArrayRef<MVKImageView*>& imagelessAttachments,
bool storeOverride) {
if (!cmdEncoder->_mtlRenderEncoder) { return; }
if (!_renderPass->getDevice()->_pMetalFeatures->deferredStoreActions) { return; }
@@ -377,7 +397,9 @@
bool hasResolveAttachment = _resolveAttachments.empty() ? false : _resolveAttachments[caIdx].attachment != VK_ATTACHMENT_UNUSED;
bool isMemorylessAttachment = false;
#if MVK_APPLE_SILICON
- isMemorylessAttachment = cmdEncoder->_framebuffer->getAttachment(clrRPAttIdx)->getMTLTexture(0).storageMode == MTLStorageModeMemoryless;
+ isMemorylessAttachment = cmdEncoder->_framebuffer->getImageless()
+ ? imagelessAttachments[clrRPAttIdx]->getMTLTexture(0).storageMode == MTLStorageModeMemoryless
+ : cmdEncoder->_framebuffer->getAttachment(clrRPAttIdx)->getMTLTexture(0).storageMode == MTLStorageModeMemoryless;
#endif
_renderPass->_attachments[clrRPAttIdx].encodeStoreAction(cmdEncoder, this, isRenderingEntireAttachment, isMemorylessAttachment, hasResolveAttachment, caIdx, false, storeOverride);
}
@@ -389,7 +411,9 @@
bool hasStencilResolveAttachment = hasResolveAttachment && _stencilResolveMode != VK_RESOLVE_MODE_NONE;
bool isMemorylessAttachment = false;
#if MVK_APPLE_SILICON
- isMemorylessAttachment = cmdEncoder->_framebuffer->getAttachment(dsRPAttIdx)->getMTLTexture(0).storageMode == MTLStorageModeMemoryless;
+ isMemorylessAttachment = cmdEncoder->_framebuffer->getImageless()
+ ? imagelessAttachments[dsRPAttIdx]->getMTLTexture(0).storageMode == MTLStorageModeMemoryless
+ : cmdEncoder->_framebuffer->getAttachment(dsRPAttIdx)->getMTLTexture(0).storageMode == MTLStorageModeMemoryless;
#endif
_renderPass->_attachments[dsRPAttIdx].encodeStoreAction(cmdEncoder, this, isRenderingEntireAttachment, isMemorylessAttachment, hasDepthResolveAttachment, 0, false, storeOverride);
_renderPass->_attachments[dsRPAttIdx].encodeStoreAction(cmdEncoder, this, isRenderingEntireAttachment, isMemorylessAttachment, hasStencilResolveAttachment, 0, true, storeOverride);
diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.def b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
index 5fb1c18..a80de24 100644
--- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def
+++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
@@ -58,6 +58,7 @@
MVK_EXTENSION(KHR_get_memory_requirements2, KHR_GET_MEMORY_REQUIREMENTS_2, DEVICE)
MVK_EXTENSION(KHR_get_physical_device_properties2, KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2, INSTANCE)
MVK_EXTENSION(KHR_get_surface_capabilities2, KHR_GET_SURFACE_CAPABILITIES_2, INSTANCE)
+MVK_EXTENSION(KHR_imageless_framebuffer, KHR_IMAGELESS_FRAMEBUFFER, DEVICE)
MVK_EXTENSION(KHR_image_format_list, KHR_IMAGE_FORMAT_LIST, DEVICE)
MVK_EXTENSION(KHR_maintenance1, KHR_MAINTENANCE1, DEVICE)
MVK_EXTENSION(KHR_maintenance2, KHR_MAINTENANCE2, DEVICE)