Add support for VK_KHR_dynamic_rendering extension.

- MVKDevice track enabling VK_KHR_dynamic_rendering extension features.
- Add MVKCmdBeginRendering and MVKCmdEndRendering command objects
  and associated command pools.
- Add MVKCommandEncoder::beginRendering() which dynamically instantiates
  temporary MVKRenderpass and (imageless) MVKFramebuffer objects to handle
  equivalent renderpass operations.
- MVKCommandEncoder retain and release subpass and framebuffer objects
  to allow transient dynamic instances to be created and destroyed.
- Add support functions to create MVKRenderpass and (imageless)
  MVKFramebuffer objects from VkRenderingInfo.
- MVKRenderpass track VkRenderingFlags.
- MVKRenderSubpass create and track an internal VkPipelineRenderingCreateInfo.
- MVKRenderPassAttachment support LoadOp and StoreOp behavior for
  dynamic rendering suspend and resume.
- Refactor viewMask processing to permit operations on viewMask outside of
  MVKGraphicsPipeline without requiring the presence of an MVKSubpass.
- MVKGraphicsPipeline extract VkPipelineRenderingCreateInfo from MVKRenderSubpass
  if available, or VkGraphicsPipelineCreateInfo::pNext if not.
- MVKCommandBuffer track VkCommandBufferInheritanceRenderingInfo (currently unused).
- (unrelated) vulkan.mm move location of VK_EXT_sample_locations functions
  for layout readability consistency.
This commit is contained in:
Bill Hollings 2022-04-19 07:27:41 -04:00
parent 6b3ef7b51a
commit e3f8ce4ebc
17 changed files with 788 additions and 162 deletions

View File

@ -19,6 +19,7 @@ MoltenVK 1.1.10
Released TBD
- Add support for extensions:
- `VK_KHR_dynamic_rendering`
- `VK_KHR_portability_enumeration` support added to `MoltenVK_icd.json`, and documentation
updated to indicate the impact of the `VK_KHR_portability_enumeration` extension during
runtime loading on *macOS* via the *Vulkan Loader*.

View File

@ -138,6 +138,56 @@ protected:
};
#pragma mark -
#pragma mark MVKCmdBeginRendering
/**
* Vulkan command to begin rendering.
* Template class to balance vector pre-allocations between very common low counts and fewer larger counts.
*/
template <size_t N>
class MVKCmdBeginRendering : public MVKCommand {
public:
VkResult setContent(MVKCommandBuffer*
cmdBuff, const VkRenderingInfo* pRenderingInfo);
void encode(MVKCommandEncoder* cmdEncoder) override;
protected:
MVKCommandTypePool<MVKCommand>* getTypePool(MVKCommandPool* cmdPool) override;
VkRenderingInfo _renderingInfo;
MVKSmallVector<VkRenderingAttachmentInfo, N> _colorAttachments;
VkRenderingAttachmentInfo _depthAttachment;
VkRenderingAttachmentInfo _stencilAttachment;
};
// Concrete template class implementations.
typedef MVKCmdBeginRendering<1> MVKCmdBeginRendering1;
typedef MVKCmdBeginRendering<2> MVKCmdBeginRendering2;
typedef MVKCmdBeginRendering<4> MVKCmdBeginRendering4;
typedef MVKCmdBeginRendering<8> MVKCmdBeginRenderingMulti;
#pragma mark -
#pragma mark MVKCmdEndRendering
/** Vulkan command to end the current dynamic rendering. */
class MVKCmdEndRendering : public MVKCommand {
public:
VkResult setContent(MVKCommandBuffer* cmdBuff);
void encode(MVKCommandEncoder* cmdEncoder) override;
protected:
MVKCommandTypePool<MVKCommand>* getTypePool(MVKCommandPool* cmdPool) override;
};
#pragma mark -
#pragma mark MVKCmdSetSampleLocations

View File

@ -164,6 +164,54 @@ void MVKCmdEndRenderPass::encode(MVKCommandEncoder* cmdEncoder) {
cmdEncoder->endRenderpass();
}
#pragma mark -
#pragma mark MVKCmdBeginRendering
template <size_t N>
VkResult MVKCmdBeginRendering<N>::setContent(MVKCommandBuffer* cmdBuff,
const VkRenderingInfo* pRenderingInfo) {
_renderingInfo = *pRenderingInfo;
// Copy attachments content, redirect info pointers to copied content, and remove any stale pNext refs
_colorAttachments.assign(_renderingInfo.pColorAttachments,
_renderingInfo.pColorAttachments + _renderingInfo.colorAttachmentCount);
_renderingInfo.pColorAttachments = _colorAttachments.data();
for (auto caAtt : _colorAttachments) { caAtt.pNext = nullptr; }
if (mvkSetOrClear(&_depthAttachment, _renderingInfo.pDepthAttachment)) {
_renderingInfo.pDepthAttachment = &_depthAttachment;
}
if (mvkSetOrClear(&_stencilAttachment, _renderingInfo.pStencilAttachment)) {
_renderingInfo.pStencilAttachment = &_stencilAttachment;
}
return VK_SUCCESS;
}
template <size_t N>
void MVKCmdBeginRendering<N>::encode(MVKCommandEncoder* cmdEncoder) {
cmdEncoder->beginRendering(this, &_renderingInfo);
}
template class MVKCmdBeginRendering<1>;
template class MVKCmdBeginRendering<2>;
template class MVKCmdBeginRendering<4>;
template class MVKCmdBeginRendering<8>;
#pragma mark -
#pragma mark MVKCmdEndRendering
VkResult MVKCmdEndRendering::setContent(MVKCommandBuffer* cmdBuff) {
return VK_SUCCESS;
}
void MVKCmdEndRendering::encode(MVKCommandEncoder* cmdEncoder) {
cmdEncoder->endRendering();
}
#pragma mark -
#pragma mark MVKCmdSetSampleLocations

View File

@ -176,6 +176,7 @@ protected:
MVKCommandPool* _commandPool;
std::atomic_flag _isExecutingNonConcurrently;
VkCommandBufferInheritanceInfo _secondaryInheritanceInfo;
VkCommandBufferInheritanceRenderingInfo _inerhitanceRenderingInfo;
id<MTLCommandBuffer> _prefilledMTLCmdBuffer = nil;
MVKCommandEncodingContext* _immediateCmdEncodingContext = nullptr;
MVKCommandEncoder* _immediateCmdEncoder = nullptr;
@ -223,7 +224,7 @@ public:
VkSubpassContents subpassContents,
MVKRenderPass* renderPass,
MVKFramebuffer* framebuffer,
VkRect2D& renderArea,
const VkRect2D& renderArea,
MVKArrayRef<VkClearValue> clearValues,
MVKArrayRef<MVKImageView*> attachments,
MVKArrayRef<MVKArrayRef<MTLSamplePosition>> subpassSamplePositions);
@ -237,6 +238,9 @@ public:
/** Sets the dynamic custom sample positions to use when rendering. */
void setDynamicSamplePositions(MVKArrayRef<MTLSamplePosition> dynamicSamplePositions);
/** Begins dynamic rendering. */
void beginRendering(MVKCommand* rendCmd, const VkRenderingInfo* pRenderingInfo);
/** Begins a Metal render pass for the current render subpass. */
void beginMetalRenderPass(MVKCommandUse cmdUse);
@ -293,6 +297,9 @@ public:
/** Ends the current renderpass. */
void endRenderpass();
/** Ends the current dymamic rendering. */
void endRendering();
/**
* Ends all encoding operations on the current Metal command encoder.
*
@ -435,6 +442,8 @@ public:
MVKCommandEncoder(MVKCommandBuffer* cmdBuffer);
~MVKCommandEncoder() override;
protected:
void addActivatedQueries(MVKQueryPool* pQueryPool, uint32_t query, uint32_t queryCount);
void finishQueries();
@ -446,6 +455,8 @@ protected:
bool hasTimestampStageCounterQueries() { return !_timestampStageCounterQueries.empty(); }
id<MTLFence> getStageCountersMTLFence();
MVKArrayRef<MTLSamplePosition> getCustomSamplePositions();
void setRenderPass(MVKRenderPass* renderPass);
void setFramebuffer(MVKFramebuffer* framebuffer);
typedef struct GPUCounterQuery {
MVKGPUCounterQueryPool* queryPool = nullptr;

View File

@ -47,10 +47,25 @@ VkResult MVKCommandBuffer::begin(const VkCommandBufferBeginInfo* pBeginInfo) {
// If this is a secondary command buffer, and contains inheritance info, set the inheritance info and determine
// whether it contains render pass continuation info. Otherwise, clear the inheritance info, and ignore it.
const VkCommandBufferInheritanceInfo* pInheritInfo = (_isSecondary ? pBeginInfo->pInheritanceInfo : NULL);
// Also check for and set any dynamic rendering inheritance info.
const VkCommandBufferInheritanceInfo* pInheritInfo = (_isSecondary ? pBeginInfo->pInheritanceInfo : nullptr);
const VkCommandBufferInheritanceRenderingInfo* pInerhitanceRenderingInfo = nullptr;
bool hasInheritInfo = mvkSetOrClear(&_secondaryInheritanceInfo, pInheritInfo);
_doesContinueRenderPass = mvkAreAllFlagsEnabled(usage, VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT) && hasInheritInfo;
if (hasInheritInfo) {
for (const auto* next = (VkBaseInStructure*)_secondaryInheritanceInfo.pNext; next; next = next->pNext) {
switch (next->sType) {
case VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_RENDERING_INFO: {
pInerhitanceRenderingInfo = (VkCommandBufferInheritanceRenderingInfo*)next;
break;
}
default:
break;
}
}
}
mvkSetOrClear(&_inerhitanceRenderingInfo, pInerhitanceRenderingInfo);
if(canPrefill()) {
@autoreleasepool {
uint32_t qIdx = 0;
@ -274,8 +289,8 @@ void MVKCommandEncoder::encode(id<MTLCommandBuffer> mtlCmdBuff,
}
void MVKCommandEncoder::beginEncoding(id<MTLCommandBuffer> mtlCmdBuff, MVKCommandEncodingContext* pEncodingContext) {
_framebuffer = nullptr;
_renderPass = nullptr;
setRenderPass(nullptr);
setFramebuffer(nullptr);
_subpassContents = VK_SUBPASS_CONTENTS_INLINE;
_renderSubpassIndex = 0;
_multiviewPassIndex = 0;
@ -316,16 +331,69 @@ void MVKCommandEncoder::encodeSecondary(MVKCommandBuffer* secondaryCmdBuffer) {
}
}
// Sets the renderpass object, releasing the old object, and retaining the new object.
// Retaining the new is performed first, in case the old and new are the same object.
// With dynamic rendering. the object is transient and only lives as long as the duration
// of the current renderpass. To make it transient, it is released by the calling code
// after it has been retained here, so that when it is released again here at the end
// of the renderpass, it will automatically be destroyed. App-created objects are not
// released by the calling code, and will not be destroyed by the release here.
void MVKCommandEncoder::setRenderPass(MVKRenderPass* renderPass) {
if (renderPass) { renderPass->retain(); }
if (_renderPass) { _renderPass->release(); }
_renderPass = renderPass;
}
// Sets the framebuffer object, releasing the old object, and retaining the new object.
// Retaining the new is performed first, in case the old and new are the same object.
// With dynamic rendering. the object is transient and only lives as long as the duration
// of the current renderpass. To make it transient, it is released by the calling code
// after it has been retained here, so that when it is released again here at the end
// of the renderpass, it will automatically be destroyed. App-created objects are not
// released by the calling code, and will not be destroyed by the release here.
void MVKCommandEncoder::setFramebuffer(MVKFramebuffer* framebuffer) {
if (framebuffer) { framebuffer->retain(); }
if (_framebuffer) { _framebuffer->release(); }
_framebuffer = framebuffer;
}
void MVKCommandEncoder::beginRendering(MVKCommand* rendCmd, const VkRenderingInfo* pRenderingInfo) {
VkSubpassContents contents = (mvkIsAnyFlagEnabled(pRenderingInfo->flags, VK_RENDERING_CONTENTS_SECONDARY_COMMAND_BUFFERS_BIT)
? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS
: VK_SUBPASS_CONTENTS_INLINE);
uint32_t maxAttCnt = (pRenderingInfo->colorAttachmentCount + 1) * 2;
MVKImageView* attachments[maxAttCnt];
VkClearValue clearValues[maxAttCnt];
uint32_t attCnt = mvkGetAttachments(pRenderingInfo, attachments, clearValues);
MVKDevice* mvkDvc = getDevice();
MVKRenderPass* mvkRP = mvkCreateRenderPass(mvkDvc, pRenderingInfo);
MVKFramebuffer* mvkFB = mvkCreateFramebuffer(mvkDvc, pRenderingInfo, mvkRP);
beginRenderpass(rendCmd, contents, mvkRP, mvkFB,
pRenderingInfo->renderArea,
MVKArrayRef(clearValues, attCnt),
MVKArrayRef(attachments, attCnt),
MVKArrayRef<MVKArrayRef<MTLSamplePosition>>());
// Once retained by this encoder, mark these objects as transient by releasing them from their
// initial creation retain, so they will be destroyed when released at the end of the renderpass.
mvkRP->release();
mvkFB->release();
}
void MVKCommandEncoder::beginRenderpass(MVKCommand* passCmd,
VkSubpassContents subpassContents,
MVKRenderPass* renderPass,
MVKFramebuffer* framebuffer,
VkRect2D& renderArea,
const VkRect2D& renderArea,
MVKArrayRef<VkClearValue> clearValues,
MVKArrayRef<MVKImageView*> attachments,
MVKArrayRef<MVKArrayRef<MTLSamplePosition>> subpassSamplePositions) {
_renderPass = renderPass;
_framebuffer = framebuffer;
setRenderPass(renderPass);
setFramebuffer(framebuffer);
_renderArea = renderArea;
_isRenderingEntireAttachment = (mvkVkOffset2DsAreEqual(_renderArea.offset, {0,0}) &&
mvkVkExtent2DsAreEqual(_renderArea.extent, getFramebufferExtent()));
@ -636,12 +704,15 @@ void MVKCommandEncoder::finalizeDispatchState() {
_computePushConstants.encode();
}
void MVKCommandEncoder::endRendering() {
endRenderpass();
}
void MVKCommandEncoder::endRenderpass() {
encodeStoreActions();
endMetalRenderEncoding();
_renderPass = nullptr;
_framebuffer = nullptr;
setRenderPass(nullptr);
setFramebuffer(nullptr);
_attachments.clear();
_renderSubpassIndex = 0;
}
@ -965,6 +1036,14 @@ MVKCommandEncoder::MVKCommandEncoder(MVKCommandBuffer* cmdBuffer) : MVKBaseDevic
_mtlBlitEncoderUse = kMVKCommandUseNone;
_pEncodingContext = nullptr;
_stageCountersMTLFence = nil;
_renderPass = nullptr;
_framebuffer = nullptr;
}
// Release rendering objects in case this instance is destroyed before ending the current renderpass.
MVKCommandEncoder::~MVKCommandEncoder() {
setRenderPass(nullptr);
setFramebuffer(nullptr);
}

View File

@ -78,6 +78,8 @@ MVK_CMD_TYPE_POOL(BindComputePipeline)
MVK_CMD_TYPE_POOLS_FROM_5_THRESHOLDS(BeginRenderPass, 1, 2, 0, 1, 2)
MVK_CMD_TYPE_POOL(NextSubpass)
MVK_CMD_TYPE_POOL(EndRenderPass)
MVK_CMD_TYPE_POOLS_FROM_3_THRESHOLDS(BeginRendering, 1, 2, 4)
MVK_CMD_TYPE_POOL(EndRendering)
MVK_CMD_TYPE_POOL(SetSampleLocations)
MVK_CMD_TYPE_POOLS_FROM_THRESHOLD(ExecuteCommands, 1)
MVK_CMD_TYPE_POOLS_FROM_2_THRESHOLDS(BindDescriptorSetsStatic, 1, 4)

View File

@ -600,9 +600,11 @@ public:
const VkAllocationCallbacks* pAllocator);
MVKRenderPass* createRenderPass(const VkRenderPassCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator);
const VkAllocationCallbacks* pAllocator,
VkRenderingFlags renderingFlags = 0);
MVKRenderPass* createRenderPass(const VkRenderPassCreateInfo2* pCreateInfo,
const VkAllocationCallbacks* pAllocator);
const VkAllocationCallbacks* pAllocator,
VkRenderingFlags renderingFlags = 0);
void destroyRenderPass(MVKRenderPass* mvkRP,
const VkAllocationCallbacks* pAllocator);
@ -668,6 +670,15 @@ public:
/** Invalidates the memory regions. */
VkResult invalidateMappedMemoryRanges(uint32_t memRangeCount, const VkMappedMemoryRange* pMemRanges);
/** Returns the number of Metal render passes needed to render all views. */
uint32_t getMultiviewMetalPassCount(uint32_t viewMask) const;
/** Returns the first view to be rendered in the given multiview pass. */
uint32_t getFirstViewIndexInMetalPass(uint32_t viewMask, uint32_t passIdx) const;
/** Returns the number of views to be rendered in the given multiview pass. */
uint32_t getViewCountInMetalPass(uint32_t viewMask, uint32_t passIdx) const;
/** Log all performance statistics. */
void logPerformanceSummary();
@ -771,6 +782,7 @@ public:
const VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT _enabledVtxAttrDivFeatures;
const VkPhysicalDevicePortabilitySubsetFeaturesKHR _enabledPortabilityFeatures;
const VkPhysicalDeviceImagelessFramebufferFeaturesKHR _enabledImagelessFramebufferFeatures;
const VkPhysicalDeviceDynamicRenderingFeatures _enabledDynamicRenderingFeatures;
/** The list of Vulkan extensions, indicating whether each has been enabled by the app for this device. */
const MVKExtensionList _enabledExtensions;

View File

@ -277,6 +277,11 @@ void MVKPhysicalDevice::getFeatures(VkPhysicalDeviceFeatures2* features) {
imagelessFramebufferFeatures->imagelessFramebuffer = true;
break;
}
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES: {
auto* dynamicRenderingFeatures = (VkPhysicalDeviceDynamicRenderingFeatures*)next;
dynamicRenderingFeatures->dynamicRendering = true;
break;
}
default:
break;
}
@ -3511,13 +3516,15 @@ void MVKDevice::destroyFramebuffer(MVKFramebuffer* mvkFB,
}
MVKRenderPass* MVKDevice::createRenderPass(const VkRenderPassCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator) {
return new MVKRenderPass(this, pCreateInfo);
const VkAllocationCallbacks* pAllocator,
VkRenderingFlags renderingFlags) {
return new MVKRenderPass(this, pCreateInfo, renderingFlags);
}
MVKRenderPass* MVKDevice::createRenderPass(const VkRenderPassCreateInfo2* pCreateInfo,
const VkAllocationCallbacks* pAllocator) {
return new MVKRenderPass(this, pCreateInfo);
const VkAllocationCallbacks* pAllocator,
VkRenderingFlags renderingFlags) {
return new MVKRenderPass(this, pCreateInfo, renderingFlags);
}
void MVKDevice::destroyRenderPass(MVKRenderPass* mvkRP,
@ -3735,6 +3742,57 @@ VkResult MVKDevice::invalidateMappedMemoryRanges(uint32_t memRangeCount, const V
}
}
uint32_t MVKDevice::getMultiviewMetalPassCount(uint32_t viewMask) const {
if ( !viewMask ) { return 0; }
if ( !_physicalDevice->canUseInstancingForMultiview() ) {
// If we can't use instanced drawing for this, we'll have to unroll the render pass.
return __builtin_popcount(viewMask);
}
uint32_t mask = viewMask;
uint32_t count;
// Step through each clump until there are no more clumps. I'll know this has
// happened when the mask becomes 0, since mvkGetNextViewMaskGroup() clears each group of bits
// as it finds them, and returns the remainder of the mask.
for (count = 0; mask != 0; ++count) {
mask = mvkGetNextViewMaskGroup(mask, nullptr, nullptr);
}
return count;
}
uint32_t MVKDevice::getFirstViewIndexInMetalPass(uint32_t viewMask, uint32_t passIdx) const {
if ( !viewMask ) { return 0; }
assert(passIdx < getMultiviewMetalPassCount(viewMask));
uint32_t mask = viewMask;
uint32_t startView = 0, viewCount = 0;
if ( !_physicalDevice->canUseInstancingForMultiview() ) {
for (uint32_t i = 0; mask != 0; ++i) {
mask = mvkGetNextViewMaskGroup(mask, &startView, &viewCount);
while (passIdx-- > 0 && viewCount-- > 0) {
startView++;
}
}
} else {
for (uint32_t i = 0; i <= passIdx; ++i) {
mask = mvkGetNextViewMaskGroup(mask, &startView, nullptr);
}
}
return startView;
}
uint32_t MVKDevice::getViewCountInMetalPass(uint32_t viewMask, uint32_t passIdx) const {
if ( !viewMask ) { return 0; }
assert(passIdx < getMultiviewMetalPassCount(viewMask));
if ( !_physicalDevice->canUseInstancingForMultiview() ) {
return 1;
}
uint32_t mask = viewMask;
uint32_t viewCount = 0;
for (uint32_t i = 0; i <= passIdx; ++i) {
mask = mvkGetNextViewMaskGroup(mask, nullptr, &viewCount);
}
return viewCount;
}
#pragma mark Metal
@ -3916,6 +3974,7 @@ MVKDevice::MVKDevice(MVKPhysicalDevice* physicalDevice, const VkDeviceCreateInfo
_enabledPrivateDataFeatures(),
_enabledPortabilityFeatures(),
_enabledImagelessFramebufferFeatures(),
_enabledDynamicRenderingFeatures(),
_enabledExtensions(this),
_isCurrentlyAutoGPUCapturing(false)
{
@ -4044,10 +4103,15 @@ void MVKDevice::enableFeatures(const VkDeviceCreateInfo* pCreateInfo) {
mvkClear(&_enabledVtxAttrDivFeatures);
mvkClear(&_enabledPortabilityFeatures);
mvkClear(&_enabledImagelessFramebufferFeatures);
mvkClear(&_enabledDynamicRenderingFeatures);
VkPhysicalDeviceDynamicRenderingFeatures pdDynamicRenderingFeatures;
pdDynamicRenderingFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES;
pdDynamicRenderingFeatures.pNext = NULL;
VkPhysicalDeviceImagelessFramebufferFeaturesKHR pdImagelessFramebufferFeatures;
pdImagelessFramebufferFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES;
pdImagelessFramebufferFeatures.pNext = NULL;
pdImagelessFramebufferFeatures.pNext = &pdDynamicRenderingFeatures;
// Fetch the available physical device features.
VkPhysicalDevicePortabilitySubsetFeaturesKHR pdPortabilityFeatures;
@ -4244,6 +4308,13 @@ void MVKDevice::enableFeatures(const VkDeviceCreateInfo* pCreateInfo) {
&pdImagelessFramebufferFeatures.imagelessFramebuffer, 1);
break;
}
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES: {
auto* requestedFeatures = (VkPhysicalDeviceDynamicRenderingFeatures*)next;
enableFeatures(&_enabledDynamicRenderingFeatures.dynamicRendering,
&requestedFeatures->dynamicRendering,
&pdDynamicRenderingFeatures.dynamicRendering, 1);
break;
}
default:
break;
}

View File

@ -70,3 +70,11 @@ protected:
uint32_t _layerCount;
};
#pragma mark -
#pragma mark Support functions
/** Returns an image-less MVKFramebuffer object created from the rendering info. */
MVKFramebuffer* mvkCreateFramebuffer(MVKDevice* device,
const VkRenderingInfo* pRenderingInfo,
MVKRenderPass* mvkRenderPass);

View File

@ -85,8 +85,8 @@ MVKFramebuffer::MVKFramebuffer(MVKDevice* device,
_extent = { .width = pCreateInfo->width, .height = pCreateInfo->height };
_layerCount = pCreateInfo->layers;
if (!(pCreateInfo->flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT_KHR)) {
// Add attachments
// If this is not an image-less framebuffer, add the attachments
if ( !mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT) ) {
_attachments.reserve(pCreateInfo->attachmentCount);
for (uint32_t i = 0; i < pCreateInfo->attachmentCount; i++) {
_attachments.push_back((MVKImageView*)pCreateInfo->pAttachments[i]);
@ -98,3 +98,47 @@ MVKFramebuffer::~MVKFramebuffer() {
[_mtlDummyTex release];
}
#pragma mark -
#pragma mark Support functions
MVKFramebuffer* mvkCreateFramebuffer(MVKDevice* device,
const VkRenderingInfo* pRenderingInfo,
MVKRenderPass* mvkRenderPass) {
uint32_t attCnt = 0;
VkExtent3D fbExtent = {};
for (uint32_t caIdx = 0; caIdx < pRenderingInfo->colorAttachmentCount; caIdx++) {
auto& clrAtt = pRenderingInfo->pColorAttachments[caIdx];
if (clrAtt.imageView) {
fbExtent = ((MVKImageView*)clrAtt.imageView)->getExtent3D();
attCnt++;
if (clrAtt.resolveImageView && clrAtt.resolveMode != VK_RESOLVE_MODE_NONE) {
attCnt++;
}
}
}
auto* pDSAtt = pRenderingInfo->pDepthAttachment ? pRenderingInfo->pDepthAttachment : pRenderingInfo->pStencilAttachment;
if (pDSAtt) {
if (pDSAtt->imageView) {
fbExtent = ((MVKImageView*)pDSAtt->imageView)->getExtent3D();
attCnt++;
}
if (pDSAtt->resolveImageView && pDSAtt->resolveMode != VK_RESOLVE_MODE_NONE) {
attCnt++;
}
}
VkFramebufferCreateInfo fbCreateInfo;
fbCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
fbCreateInfo.pNext = nullptr;
fbCreateInfo.flags = VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT;
fbCreateInfo.renderPass = (VkRenderPass)mvkRenderPass;
fbCreateInfo.attachmentCount = attCnt;
fbCreateInfo.pAttachments = nullptr;
fbCreateInfo.width = fbExtent.width;
fbCreateInfo.height = fbExtent.height;
fbCreateInfo.layers = pRenderingInfo->layerCount;
return device->createFramebuffer(&fbCreateInfo, nullptr);
}

View File

@ -570,7 +570,13 @@ public:
/** Returns the Metal pixel format of this image view. */
MTLPixelFormat getMTLPixelFormat(uint8_t planeIndex = 0) { return planeIndex < _planes.size() ? _planes[planeIndex]->_mtlPixFmt : MTLPixelFormatInvalid; } // Guard against destroyed instance retained in a descriptor.
/** Returns the Vulkan pixel format of this image view. */
VkFormat getVkFormat(uint8_t planeIndex = 0) { return getPixelFormats()->getVkFormat(getMTLPixelFormat(planeIndex)); }
/** Returns the number of samples for each pixel of this image view. */
VkSampleCountFlagBits getSampleCount() { return _image->getSampleCount(); }
/** Returns the packed component swizzle of this image view. */
uint32_t getPackedSwizzle() { return _planes.empty() ? 0 : _planes[0]->getPackedSwizzle(); } // Guard against destroyed instance retained in a descriptor.

View File

@ -630,6 +630,8 @@ void MVKInstance::initProcAddrs() {
ADD_DVC_EXT_ENTRY_POINT(vkCmdBeginRenderPass2KHR, KHR_CREATE_RENDERPASS_2);
ADD_DVC_EXT_ENTRY_POINT(vkCmdNextSubpass2KHR, KHR_CREATE_RENDERPASS_2);
ADD_DVC_EXT_ENTRY_POINT(vkCmdEndRenderPass2KHR, KHR_CREATE_RENDERPASS_2);
ADD_DVC_EXT_ENTRY_POINT(vkCmdBeginRenderingKHR, KHR_DYNAMIC_RENDERING);
ADD_DVC_EXT_ENTRY_POINT(vkCmdEndRenderingKHR, KHR_DYNAMIC_RENDERING);
ADD_DVC_EXT_ENTRY_POINT(vkCmdPushDescriptorSetKHR, KHR_PUSH_DESCRIPTOR);
ADD_DVC_EXT2_ENTRY_POINT(vkCmdPushDescriptorSetWithTemplateKHR, KHR_PUSH_DESCRIPTOR, KHR_DESCRIPTOR_UPDATE_TEMPLATE);
ADD_DVC_EXT_ENTRY_POINT(vkCreateSwapchainKHR, KHR_SWAPCHAIN);

View File

@ -367,6 +367,20 @@ id<MTLComputePipelineState> MVKGraphicsPipeline::getTessVertexStageIndex32State(
#pragma mark Construction
// Extracts and returns a VkPipelineRenderingCreateInfo from the renderPass or pNext chain of pCreateInfo, or returns null if not found
static const VkPipelineRenderingCreateInfo* getRenderingCreateInfo(const VkGraphicsPipelineCreateInfo* pCreateInfo) {
if (pCreateInfo->renderPass) {
return ((MVKRenderPass*)pCreateInfo->renderPass)->getSubpass(pCreateInfo->subpass)->getPipelineRenderingCreateInfo();
}
for (const auto* next = (VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) {
switch (next->sType) {
case VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO: return (VkPipelineRenderingCreateInfo*)next;
default: break;
}
}
return nullptr;
}
MVKGraphicsPipeline::MVKGraphicsPipeline(MVKDevice* device,
MVKPipelineCache* pipelineCache,
MVKPipeline* parent,
@ -374,11 +388,10 @@ MVKGraphicsPipeline::MVKGraphicsPipeline(MVKDevice* device,
MVKPipeline(device, pipelineCache, (MVKPipelineLayout*)pCreateInfo->layout, parent) {
// Determine rasterization early, as various other structs are validated and interpreted in this context.
MVKRenderPass* mvkRendPass = (MVKRenderPass*)pCreateInfo->renderPass;
MVKRenderSubpass* mvkRenderSubpass = mvkRendPass->getSubpass(pCreateInfo->subpass);
const VkPipelineRenderingCreateInfo* pRendInfo = getRenderingCreateInfo(pCreateInfo);
_isRasterizing = !isRasterizationDisabled(pCreateInfo);
_isRasterizingColor = _isRasterizing && mvkRenderSubpass->hasColorAttachments();
_isRasterizingDepthStencil = _isRasterizing && mvkRenderSubpass->hasDepthStencilAttachment();
_isRasterizingColor = _isRasterizing && mvkHasColorAttachments(pRendInfo);
_isRasterizingDepthStencil = _isRasterizing && mvkGetDepthStencilFormat(pRendInfo) != VK_FORMAT_UNDEFINED;
// Get the tessellation shaders, if present. Do this now, because we need to extract
// reflection data from them that informs everything else.
@ -556,9 +569,8 @@ void MVKGraphicsPipeline::initMTLRenderPipelineState(const VkGraphicsPipelineCre
if (!isTessellationPipeline()) {
MTLRenderPipelineDescriptor* plDesc = newMTLRenderPipelineDescriptor(pCreateInfo, reflectData); // temp retain
if (plDesc) {
MVKRenderPass* mvkRendPass = (MVKRenderPass*)pCreateInfo->renderPass;
MVKRenderSubpass* mvkSubpass = mvkRendPass->getSubpass(pCreateInfo->subpass);
if (mvkSubpass->isMultiview()) {
const VkPipelineRenderingCreateInfo* pRendInfo = getRenderingCreateInfo(pCreateInfo);
if (pRendInfo && mvkIsMultiview(pRendInfo->viewMask)) {
// We need to adjust the step rate for per-instance attributes to account for the
// extra instances needed to render all views. But, there's a problem: vertex input
// descriptions are static pipeline state. If we need multiple passes, and some have
@ -566,8 +578,8 @@ void MVKGraphicsPipeline::initMTLRenderPipelineState(const VkGraphicsPipelineCre
// for these passes. We'll need to make a pipeline for every pass view count we can see
// in the render pass. This really sucks.
std::unordered_set<uint32_t> viewCounts;
for (uint32_t passIdx = 0; passIdx < mvkSubpass->getMultiviewMetalPassCount(); ++passIdx) {
viewCounts.insert(mvkSubpass->getViewCountInMetalPass(passIdx));
for (uint32_t passIdx = 0; passIdx < getDevice()->getMultiviewMetalPassCount(pRendInfo->viewMask); ++passIdx) {
viewCounts.insert(getDevice()->getViewCountInMetalPass(pRendInfo->viewMask, passIdx));
}
auto count = viewCounts.cbegin();
adjustVertexInputForMultiview(plDesc.vertexDescriptor, pCreateInfo->pVertexInputState, *count);
@ -1451,11 +1463,6 @@ void MVKGraphicsPipeline::addTessellationToPipeline(MTLRenderPipelineDescriptor*
void MVKGraphicsPipeline::addFragmentOutputToPipeline(MTLRenderPipelineDescriptor* plDesc,
const VkGraphicsPipelineCreateInfo* pCreateInfo) {
// Retrieve the render subpass for which this pipeline is being constructed
MVKRenderPass* mvkRendPass = (MVKRenderPass*)pCreateInfo->renderPass;
MVKRenderSubpass* mvkRenderSubpass = mvkRendPass->getSubpass(pCreateInfo->subpass);
// Topology
if (pCreateInfo->pInputAssemblyState) {
plDesc.inputPrimitiveTopologyMVK = isRenderingPoints(pCreateInfo)
@ -1463,14 +1470,17 @@ void MVKGraphicsPipeline::addFragmentOutputToPipeline(MTLRenderPipelineDescripto
: mvkMTLPrimitiveTopologyClassFromVkPrimitiveTopology(pCreateInfo->pInputAssemblyState->topology);
}
// Color attachments - must ignore bad pColorBlendState pointer if rasterization is disabled or subpass has no color attachments
const VkPipelineRenderingCreateInfo* pRendInfo = getRenderingCreateInfo(pCreateInfo);
// Color attachments - must ignore bad pColorBlendState pointer if rasterization is disabled or subpass has no color attachments
uint32_t caCnt = 0;
if (_isRasterizingColor && pCreateInfo->pColorBlendState) {
if (_isRasterizingColor && pRendInfo && pCreateInfo->pColorBlendState) {
for (uint32_t caIdx = 0; caIdx < pCreateInfo->pColorBlendState->attachmentCount; caIdx++) {
const VkPipelineColorBlendAttachmentState* pCA = &pCreateInfo->pColorBlendState->pAttachments[caIdx];
MTLRenderPipelineColorAttachmentDescriptor* colorDesc = plDesc.colorAttachments[caIdx];
colorDesc.pixelFormat = getPixelFormats()->getMTLPixelFormat(mvkRenderSubpass->getColorAttachmentFormat(caIdx));
MTLPixelFormat mtlPixFmt = getPixelFormats()->getMTLPixelFormat(pRendInfo->pColorAttachmentFormats[caIdx]);
MTLRenderPipelineColorAttachmentDescriptor* colorDesc = plDesc.colorAttachments[caIdx];
colorDesc.pixelFormat = mtlPixFmt;
if (colorDesc.pixelFormat == MTLPixelFormatRGB9E5Float) {
// Metal doesn't allow disabling individual channels for a RGB9E5 render target.
// Either all must be disabled or none must be disabled.
@ -1483,7 +1493,7 @@ void MVKGraphicsPipeline::addFragmentOutputToPipeline(MTLRenderPipelineDescripto
// Don't set the blend state if we're not using this attachment.
// The pixel format will be MTLPixelFormatInvalid in that case, and
// Metal asserts if we turn on blending with that pixel format.
if (mvkRenderSubpass->isColorAttachmentUsed(caIdx)) {
if (mtlPixFmt) {
caCnt++;
colorDesc.blendingEnabled = pCA->blendEnable;
colorDesc.rgbBlendOperation = mvkMTLBlendOperationFromVkBlendOp(pCA->colorBlendOp);
@ -1498,7 +1508,7 @@ void MVKGraphicsPipeline::addFragmentOutputToPipeline(MTLRenderPipelineDescripto
// Depth & stencil attachments
MVKPixelFormats* pixFmts = getPixelFormats();
MTLPixelFormat mtlDSFormat = pixFmts->getMTLPixelFormat(mvkRenderSubpass->getDepthStencilFormat());
MTLPixelFormat mtlDSFormat = pixFmts->getMTLPixelFormat(mvkGetDepthStencilFormat(pRendInfo));
if (pixFmts->isDepthFormat(mtlDSFormat)) { plDesc.depthAttachmentPixelFormat = mtlDSFormat; }
if (pixFmts->isStencilFormat(mtlDSFormat)) { plDesc.stencilAttachmentPixelFormat = mtlDSFormat; }
@ -1515,9 +1525,13 @@ void MVKGraphicsPipeline::addFragmentOutputToPipeline(MTLRenderPipelineDescripto
// Multisampling - must ignore allowed bad pMultisampleState pointer if rasterization disabled
if (_isRasterizing && pCreateInfo->pMultisampleState) {
plDesc.sampleCount = mvkSampleCountFromVkSampleCountFlagBits(pCreateInfo->pMultisampleState->rasterizationSamples);
mvkRenderSubpass->setDefaultSampleCount(pCreateInfo->pMultisampleState->rasterizationSamples);
plDesc.alphaToCoverageEnabled = pCreateInfo->pMultisampleState->alphaToCoverageEnable;
plDesc.alphaToOneEnabled = pCreateInfo->pMultisampleState->alphaToOneEnable;
// If the pipeline uses a specific render subpass, set its default sample count
if (pCreateInfo->renderPass) {
((MVKRenderPass*)pCreateInfo->renderPass)->getSubpass(pCreateInfo->subpass)->setDefaultSampleCount(pCreateInfo->pMultisampleState->rasterizationSamples);
}
}
}
@ -1574,10 +1588,9 @@ void MVKGraphicsPipeline::initShaderConversionConfig(SPIRVToMSLConversionConfigu
// view range buffer as for the indirect paramters buffer.
_viewRangeBufferIndex = _indirectParamsIndex;
MVKRenderPass* mvkRendPass = (MVKRenderPass*)pCreateInfo->renderPass;
MVKRenderSubpass* mvkRenderSubpass = mvkRendPass->getSubpass(pCreateInfo->subpass);
const VkPipelineRenderingCreateInfo* pRendInfo = getRenderingCreateInfo(pCreateInfo);
MVKPixelFormats* pixFmts = getPixelFormats();
MTLPixelFormat mtlDSFormat = pixFmts->getMTLPixelFormat(mvkRenderSubpass->getDepthStencilFormat());
MTLPixelFormat mtlDSFormat = pixFmts->getMTLPixelFormat(mvkGetDepthStencilFormat(pRendInfo));
// Disable any unused color attachments, because Metal validation can complain if the
// fragment shader outputs a color value without a corresponding color attachment.
@ -1588,7 +1601,7 @@ void MVKGraphicsPipeline::initShaderConversionConfig(SPIRVToMSLConversionConfigu
shaderConfig.options.mslOptions.enable_frag_output_mask = hasA2C ? 1 : 0;
if (_isRasterizingColor && pCreateInfo->pColorBlendState) {
for (uint32_t caIdx = 0; caIdx < pCreateInfo->pColorBlendState->attachmentCount; caIdx++) {
if (mvkRenderSubpass->isColorAttachmentUsed(caIdx)) {
if (mvkIsColorAttachmentUsed(pRendInfo, caIdx)) {
mvkEnableFlags(shaderConfig.options.mslOptions.enable_frag_output_mask, 1 << caIdx);
}
}
@ -1602,7 +1615,7 @@ void MVKGraphicsPipeline::initShaderConversionConfig(SPIRVToMSLConversionConfigu
shaderConfig.options.shouldFlipVertexY = mvkConfig().shaderConversionFlipVertexY;
shaderConfig.options.mslOptions.swizzle_texture_samples = _fullImageViewSwizzle && !getDevice()->_pMetalFeatures->nativeTextureSwizzle;
shaderConfig.options.mslOptions.tess_domain_origin_lower_left = pTessDomainOriginState && pTessDomainOriginState->domainOrigin == VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT;
shaderConfig.options.mslOptions.multiview = mvkRendPass->isMultiview();
shaderConfig.options.mslOptions.multiview = mvkIsMultiview(pRendInfo->viewMask);
shaderConfig.options.mslOptions.multiview_layered_rendering = getPhysicalDevice()->canUseInstancingForMultiview();
shaderConfig.options.mslOptions.view_index_from_device_index = mvkAreAllFlagsEnabled(pCreateInfo->flags, VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT);
#if MVK_MACOS

View File

@ -42,7 +42,6 @@ class MVKRenderSubpass : public MVKBaseObject {
public:
/** Returns the Vulkan API opaque object controlling this object. */
MVKVulkanAPIObject* getVulkanAPIObject() override;
@ -83,10 +82,10 @@ public:
void setDefaultSampleCount(VkSampleCountFlagBits count) { _defaultSampleCount = count; }
/** Returns whether or not this is a multiview subpass. */
bool isMultiview() const { return _viewMask != 0; }
bool isMultiview() const { return _pipelineRenderingCreateInfo.viewMask != 0; }
/** Returns the total number of views to be rendered. */
uint32_t getViewCount() const { return __builtin_popcount(_viewMask); }
uint32_t getViewCount() const { return __builtin_popcount(_pipelineRenderingCreateInfo.viewMask); }
/** Returns the number of Metal render passes needed to render all views. */
uint32_t getMultiviewMetalPassCount() const;
@ -100,6 +99,9 @@ public:
/** Returns the number of views to be rendered in all multiview passes up to the given one. */
uint32_t getViewCountUpToMetalPass(uint32_t passIdx) const;
/** Returns pipeline rendering create info that describes this subpass. */
const VkPipelineRenderingCreateInfo* getPipelineRenderingCreateInfo() { return &_pipelineRenderingCreateInfo; }
/**
* Populates the specified Metal MTLRenderPassDescriptor with content from this
* instance, the specified framebuffer, and the specified array of clear values
@ -151,19 +153,21 @@ private:
uint32_t getViewMaskGroupForMetalPass(uint32_t passIdx);
MVKMTLFmtCaps getRequiredFormatCapabilitiesForAttachmentAt(uint32_t rpAttIdx);
void populatePipelineRenderingCreateInfo();
MVKRenderPass* _renderPass;
uint32_t _subpassIndex;
uint32_t _viewMask;
MVKSmallVector<VkAttachmentReference2, kMVKDefaultAttachmentCount> _inputAttachments;
MVKSmallVector<VkAttachmentReference2, kMVKDefaultAttachmentCount> _colorAttachments;
MVKSmallVector<VkAttachmentReference2, kMVKDefaultAttachmentCount> _resolveAttachments;
MVKSmallVector<uint32_t, kMVKDefaultAttachmentCount> _preserveAttachments;
MVKSmallVector<VkFormat, kMVKDefaultAttachmentCount> _colorAttachmentFormats;
VkPipelineRenderingCreateInfo _pipelineRenderingCreateInfo;
VkAttachmentReference2 _depthStencilAttachment;
VkAttachmentReference2 _depthStencilResolveAttachment;
VkResolveModeFlagBits _depthResolveMode = VK_RESOLVE_MODE_NONE;
VkResolveModeFlagBits _stencilResolveMode = VK_RESOLVE_MODE_NONE;
VkSampleCountFlagBits _defaultSampleCount = VK_SAMPLE_COUNT_1_BIT;
uint32_t _subpassIndex;
};
@ -214,11 +218,9 @@ public:
/** Returns whether this attachment should be cleared in the subpass. */
bool shouldClearAttachment(MVKRenderSubpass* subpass, bool isStencil);
/** Constructs an instance for the specified parent renderpass. */
MVKRenderPassAttachment(MVKRenderPass* renderPass,
const VkAttachmentDescription* pCreateInfo);
/** Constructs an instance for the specified parent renderpass. */
MVKRenderPassAttachment(MVKRenderPass* renderPass,
const VkAttachmentDescription2* pCreateInfo);
@ -270,11 +272,13 @@ public:
/** Returns whether or not this render pass is a multiview render pass. */
bool isMultiview() const;
/** Constructs an instance for the specified device. */
MVKRenderPass(MVKDevice* device, const VkRenderPassCreateInfo* pCreateInfo);
MVKRenderPass(MVKDevice* device,
const VkRenderPassCreateInfo* pCreateInfo,
VkRenderingFlags renderingFlags = 0);
/** Constructs an instance for the specified device. */
MVKRenderPass(MVKDevice* device, const VkRenderPassCreateInfo2* pCreateInfo);
MVKRenderPass(MVKDevice* device,
const VkRenderPassCreateInfo2* pCreateInfo,
VkRenderingFlags renderingFlags = 0);
protected:
friend class MVKRenderSubpass;
@ -285,6 +289,44 @@ protected:
MVKSmallVector<MVKRenderPassAttachment> _attachments;
MVKSmallVector<MVKRenderSubpass> _subpasses;
MVKSmallVector<VkSubpassDependency2> _subpassDependencies;
VkRenderingFlags _renderingFlags = 0;
};
#pragma mark -
#pragma mark Support functions
/** Returns a MVKRenderPass object created from the rendering info. */
MVKRenderPass* mvkCreateRenderPass(MVKDevice* device, const VkRenderingInfo* pRenderingInfo);
/**
* Extracts the usable attachments and their clear values from the rendering info,
* and sets them in the corresponding arrays, which must be large enough to hold
* all of the extracted values, and returns the number of attachments extracted.
* For consistency, the clear value of any resolve attachments are populated,
* even though they are ignored.
*/
uint32_t mvkGetAttachments(const VkRenderingInfo* pRenderingInfo,
MVKImageView* attachments[],
VkClearValue clearValues[]);
/** Returns whether the view mask uses multiview. */
static inline bool mvkIsMultiview(uint32_t viewMask) { return viewMask != 0; }
/** Returns whether the attachment is being used. */
bool mvkIsColorAttachmentUsed(const VkPipelineRenderingCreateInfo* pRendInfo, uint32_t colorAttIdx);
/** Returns whether any attachment is being used. */
bool mvkHasColorAttachments(const VkPipelineRenderingCreateInfo* pRendInfo);
/** Extracts and returns the combined depth/stencil format . */
VkFormat mvkGetDepthStencilFormat(const VkPipelineRenderingCreateInfo* pRendInfo);
/**
* Extracts the first view, number of views, and the portion of the mask
* to be rendered from the lowest clump of set bits in a view mask.
*/
uint32_t mvkGetNextViewMaskGroup(uint32_t viewMask, uint32_t* startView,
uint32_t* viewCount, uint32_t *groupMask = nullptr);

View File

@ -93,102 +93,42 @@ VkSampleCountFlagBits MVKRenderSubpass::getSampleCount() {
return VK_SAMPLE_COUNT_1_BIT;
}
// Extract the first view, number of views, and the portion of the mask to be rendered from
// the lowest clump of set bits in a view mask.
static uint32_t getNextViewMaskGroup(uint32_t viewMask, uint32_t* startView, uint32_t* viewCount, uint32_t *groupMask = nullptr) {
// First, find the first set bit. This is the start of the next clump of views to be rendered.
// n.b. ffs(3) returns a 1-based index. This actually bit me during development of this feature.
int pos = ffs(viewMask) - 1;
int end = pos;
if (groupMask) { *groupMask = 0; }
// Now we'll step through the bits one at a time until we find a bit that isn't set.
// This is one past the end of the next clump. Clear the bits as we go, so we can use
// ffs(3) again on the next clump.
// TODO: Find a way to make this faster.
while (viewMask & (1 << end)) {
if (groupMask) { *groupMask |= viewMask & (1 << end); }
viewMask &= ~(1 << (end++));
}
if (startView) { *startView = pos; }
if (viewCount) { *viewCount = end - pos; }
return viewMask;
}
// Get the portion of the view mask that will be rendered in the specified Metal render pass.
uint32_t MVKRenderSubpass::getViewMaskGroupForMetalPass(uint32_t passIdx) {
if (!_viewMask) { return 0; }
if (!_pipelineRenderingCreateInfo.viewMask) { return 0; }
assert(passIdx < getMultiviewMetalPassCount());
if (!_renderPass->getPhysicalDevice()->canUseInstancingForMultiview()) {
return 1 << getFirstViewIndexInMetalPass(passIdx);
}
uint32_t mask = _viewMask, groupMask = 0;
uint32_t mask = _pipelineRenderingCreateInfo.viewMask, groupMask = 0;
for (uint32_t i = 0; i <= passIdx; ++i) {
mask = getNextViewMaskGroup(mask, nullptr, nullptr, &groupMask);
mask = mvkGetNextViewMaskGroup(mask, nullptr, nullptr, &groupMask);
}
return groupMask;
}
uint32_t MVKRenderSubpass::getMultiviewMetalPassCount() const {
if (!_viewMask) { return 0; }
if (!_renderPass->getPhysicalDevice()->canUseInstancingForMultiview()) {
// If we can't use instanced drawing for this, we'll have to unroll the render pass.
return __builtin_popcount(_viewMask);
}
uint32_t mask = _viewMask;
uint32_t count;
// Step through each clump until there are no more clumps. I'll know this has
// happened when the mask becomes 0, since getNextViewMaskGroup() clears each group of bits
// as it finds them, and returns the remainder of the mask.
for (count = 0; mask != 0; ++count) {
mask = getNextViewMaskGroup(mask, nullptr, nullptr);
}
return count;
return _renderPass->getDevice()->getMultiviewMetalPassCount(_pipelineRenderingCreateInfo.viewMask);
}
uint32_t MVKRenderSubpass::getFirstViewIndexInMetalPass(uint32_t passIdx) const {
if (!_viewMask) { return 0; }
assert(passIdx < getMultiviewMetalPassCount());
uint32_t mask = _viewMask;
uint32_t startView = 0, viewCount = 0;
if (!_renderPass->getPhysicalDevice()->canUseInstancingForMultiview()) {
for (uint32_t i = 0; mask != 0; ++i) {
mask = getNextViewMaskGroup(mask, &startView, &viewCount);
while (passIdx-- > 0 && viewCount-- > 0) {
startView++;
}
}
} else {
for (uint32_t i = 0; i <= passIdx; ++i) {
mask = getNextViewMaskGroup(mask, &startView, nullptr);
}
}
return startView;
return _renderPass->getDevice()->getFirstViewIndexInMetalPass(_pipelineRenderingCreateInfo.viewMask, passIdx);
}
uint32_t MVKRenderSubpass::getViewCountInMetalPass(uint32_t passIdx) const {
if (!_viewMask) { return 0; }
assert(passIdx < getMultiviewMetalPassCount());
if (!_renderPass->getPhysicalDevice()->canUseInstancingForMultiview()) {
return 1;
}
uint32_t mask = _viewMask;
uint32_t viewCount = 0;
for (uint32_t i = 0; i <= passIdx; ++i) {
mask = getNextViewMaskGroup(mask, nullptr, &viewCount);
}
return viewCount;
return _renderPass->getDevice()->getViewCountInMetalPass(_pipelineRenderingCreateInfo.viewMask, passIdx);
}
uint32_t MVKRenderSubpass::getViewCountUpToMetalPass(uint32_t passIdx) const {
if (!_viewMask) { return 0; }
if (!_pipelineRenderingCreateInfo.viewMask) { return 0; }
if (!_renderPass->getPhysicalDevice()->canUseInstancingForMultiview()) {
return passIdx+1;
}
uint32_t mask = _viewMask;
uint32_t mask = _pipelineRenderingCreateInfo.viewMask;
uint32_t totalViewCount = 0;
for (uint32_t i = 0; i <= passIdx; ++i) {
uint32_t viewCount;
mask = getNextViewMaskGroup(mask, nullptr, &viewCount);
mask = mvkGetNextViewMaskGroup(mask, nullptr, &viewCount);
totalViewCount += viewCount;
}
return totalViewCount;
@ -314,9 +254,7 @@ void MVKRenderSubpass::populateMTLRenderPassDescriptor(MTLRenderPassDescriptor*
// If Metal does not support rendering without attachments, create a dummy attachment to pass Metal validation.
if (caUsedCnt == 0 && dsRPAttIdx == VK_ATTACHMENT_UNUSED) {
if (_renderPass->getDevice()->_pMetalFeatures->renderWithoutAttachments) {
#if MVK_MACOS_OR_IOS
mtlRPDesc.defaultRasterSampleCount = mvkSampleCountFromVkSampleCountFlagBits(_defaultSampleCount);
#endif
} else {
MTLRenderPassColorAttachmentDescriptor* mtlColorAttDesc = mtlRPDesc.colorAttachments[0];
mtlColorAttDesc.texture = framebuffer->getDummyAttachmentMTLTexture(this, passIdx);
@ -467,13 +405,32 @@ void MVKRenderSubpass::resolveUnresolvableAttachments(MVKCommandEncoder* cmdEnco
}
}
// Must be called after renderpass has both subpasses and attachments bound
void MVKRenderSubpass::populatePipelineRenderingCreateInfo() {
MVKPixelFormats* pixFmts = _renderPass->getPixelFormats();
_pipelineRenderingCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
_pipelineRenderingCreateInfo.pNext = nullptr;
uint32_t caCnt = getColorAttachmentCount();
for (uint32_t caIdx = 0; caIdx < caCnt; caIdx++) {
_colorAttachmentFormats.push_back(getColorAttachmentFormat(caIdx));
}
_pipelineRenderingCreateInfo.pColorAttachmentFormats = _colorAttachmentFormats.data();
_pipelineRenderingCreateInfo.colorAttachmentCount = caCnt;
VkFormat dsFmt = getDepthStencilFormat();
MTLPixelFormat dsMTLFmt = pixFmts->getMTLPixelFormat(dsFmt);
_pipelineRenderingCreateInfo.depthAttachmentFormat = pixFmts->isDepthFormat(dsMTLFmt) ? dsFmt : VK_FORMAT_UNDEFINED;
_pipelineRenderingCreateInfo.stencilAttachmentFormat = pixFmts->isStencilFormat(dsMTLFmt) ? dsFmt : VK_FORMAT_UNDEFINED;
}
MVKRenderSubpass::MVKRenderSubpass(MVKRenderPass* renderPass,
const VkSubpassDescription* pCreateInfo,
const VkRenderPassInputAttachmentAspectCreateInfo* pInputAspects,
uint32_t viewMask) {
_renderPass = renderPass;
_subpassIndex = (uint32_t)_renderPass->_subpasses.size();
_viewMask = viewMask;
_pipelineRenderingCreateInfo.viewMask = viewMask;
// Add attachments
_inputAttachments.reserve(pCreateInfo->inputAttachmentCount);
@ -535,7 +492,7 @@ MVKRenderSubpass::MVKRenderSubpass(MVKRenderPass* renderPass,
_renderPass = renderPass;
_subpassIndex = (uint32_t)_renderPass->_subpasses.size();
_viewMask = pCreateInfo->viewMask;
_pipelineRenderingCreateInfo.viewMask = pCreateInfo->viewMask;
// Add attachments
_inputAttachments.reserve(pCreateInfo->inputAttachmentCount);
@ -600,11 +557,13 @@ bool MVKRenderPassAttachment::populateMTLRenderPassAttachmentDescriptor(MTLRende
#if MVK_APPLE_SILICON
isMemorylessAttachment = attachment->getMTLTexture().storageMode == MTLStorageModeMemoryless;
#endif
bool isResuming = mvkIsAnyFlagEnabled(_renderPass->_renderingFlags, VK_RENDERING_RESUMING_BIT);
// Only allow clearing of entire attachment if we're actually
// rendering to the entire attachment AND we're in the first subpass.
// If the renderpass was suspended, and is now being resumed, load the contents.
MTLLoadAction mtlLA;
if (loadOverride || !isRenderingEntireAttachment || !isFirstUseOfAttachment(subpass)) {
if (loadOverride || isResuming || !isRenderingEntireAttachment || !isFirstUseOfAttachment(subpass)) {
mtlLA = MTLLoadActionLoad;
} else {
VkAttachmentLoadOp loadOp = isStencil ? _info.stencilLoadOp : _info.loadOp;
@ -679,14 +638,14 @@ void MVKRenderPassAttachment::populateMultiviewClearRects(MVKSmallVector<VkClear
VkRect2D renderArea = cmdEncoder->clipToRenderArea({{0, 0}, {kMVKUndefinedLargeUInt32, kMVKUndefinedLargeUInt32}});
uint32_t startView, viewCount;
do {
clearMask = getNextViewMaskGroup(clearMask, &startView, &viewCount);
clearMask = mvkGetNextViewMaskGroup(clearMask, &startView, &viewCount);
clearRects.push_back({renderArea, startView, viewCount});
} while (clearMask);
}
bool MVKRenderPassAttachment::isFirstUseOfAttachment(MVKRenderSubpass* subpass) {
if ( subpass->isMultiview() ) {
return _firstUseViewMasks[subpass->_subpassIndex] == subpass->_viewMask;
return _firstUseViewMasks[subpass->_subpassIndex] == subpass->_pipelineRenderingCreateInfo.viewMask;
} else {
return _firstUseSubpassIdx == subpass->_subpassIndex;
}
@ -694,7 +653,7 @@ bool MVKRenderPassAttachment::isFirstUseOfAttachment(MVKRenderSubpass* subpass)
bool MVKRenderPassAttachment::isLastUseOfAttachment(MVKRenderSubpass* subpass) {
if ( subpass->isMultiview() ) {
return _lastUseViewMasks[subpass->_subpassIndex] == subpass->_viewMask;
return _lastUseViewMasks[subpass->_subpassIndex] == subpass->_pipelineRenderingCreateInfo.viewMask;
} else {
return _lastUseSubpassIdx == subpass->_subpassIndex;
}
@ -707,7 +666,12 @@ MTLStoreAction MVKRenderPassAttachment::getMTLStoreAction(MVKRenderSubpass* subp
bool canResolveFormat,
bool isStencil,
bool storeOverride) {
// If a resolve attachment exists, this attachment must resolve once complete.
// If the renderpass is going to be suspended, and resumed later, store the contents to preserve them until then.
bool isSuspending = mvkIsAnyFlagEnabled(_renderPass->_renderingFlags, VK_RENDERING_SUSPENDING_BIT);
if (isSuspending) { return MTLStoreActionStore; }
// If a resolve attachment exists, this attachment must resolve once complete.
if (hasResolveAttachment && canResolveFormat && !_renderPass->getDevice()->_pMetalFeatures->combinedStoreResolveAction) {
return MTLStoreActionMultisampleResolve;
}
@ -759,7 +723,7 @@ void MVKRenderPassAttachment::validateFormat() {
_firstUseSubpassIdx = min(spIdx, _firstUseSubpassIdx);
_lastUseSubpassIdx = max(spIdx, _lastUseSubpassIdx);
if ( subPass.isMultiview() ) {
uint32_t viewMask = subPass._viewMask;
uint32_t viewMask = subPass._pipelineRenderingCreateInfo.viewMask;
std::for_each(_lastUseViewMasks.begin(), _lastUseViewMasks.end(), [viewMask](uint32_t& mask) { mask &= ~viewMask; });
_lastUseViewMasks.push_back(viewMask);
std::for_each(_firstUseViewMasks.begin(), _firstUseViewMasks.end(), [&viewMask](uint32_t mask) { viewMask &= ~mask; });
@ -821,7 +785,10 @@ VkExtent2D MVKRenderPass::getRenderAreaGranularity() {
bool MVKRenderPass::isMultiview() const { return _subpasses[0].isMultiview(); }
MVKRenderPass::MVKRenderPass(MVKDevice* device,
const VkRenderPassCreateInfo* pCreateInfo) : MVKVulkanAPIDeviceObject(device) {
const VkRenderPassCreateInfo* pCreateInfo,
VkRenderingFlags renderingFlags) :
MVKVulkanAPIDeviceObject(device),
_renderingFlags(renderingFlags) {
const VkRenderPassInputAttachmentAspectCreateInfo* pInputAspectCreateInfo = nullptr;
const VkRenderPassMultiviewCreateInfo* pMultiviewCreateInfo = nullptr;
@ -874,10 +841,19 @@ MVKRenderPass::MVKRenderPass(MVKDevice* device,
for (uint32_t i = 0; i < pCreateInfo->attachmentCount; i++) {
_attachments.emplace_back(this, &pCreateInfo->pAttachments[i]);
}
// Populate additional subpass info after attachments added.
for (auto& mvkSP : _subpasses) {
mvkSP.populatePipelineRenderingCreateInfo();
}
}
MVKRenderPass::MVKRenderPass(MVKDevice* device,
const VkRenderPassCreateInfo2* pCreateInfo) : MVKVulkanAPIDeviceObject(device) {
const VkRenderPassCreateInfo2* pCreateInfo,
VkRenderingFlags renderingFlags) :
MVKVulkanAPIDeviceObject(device),
_renderingFlags(renderingFlags) {
// Add subpasses and dependencies first
_subpasses.reserve(pCreateInfo->subpassCount);
@ -894,6 +870,244 @@ MVKRenderPass::MVKRenderPass(MVKDevice* device,
for (uint32_t i = 0; i < pCreateInfo->attachmentCount; i++) {
_attachments.emplace_back(this, &pCreateInfo->pAttachments[i]);
}
// Populate additional subpass info after attachments added.
for (auto& mvkSP : _subpasses) {
mvkSP.populatePipelineRenderingCreateInfo();
}
}
#pragma mark -
#pragma mark Support functions
// Adds the rendering attachment info to the array of attachment descriptors at the index,
// and increments the index, for both the base view and the resolve view, if it is present.
static void mvkAddAttachmentDescriptor(const VkRenderingAttachmentInfo* pAttInfo,
const VkRenderingAttachmentInfo* pStencilAttInfo,
VkAttachmentDescription2 attachmentDescriptors[],
uint32_t& attDescIdx) {
VkAttachmentDescription2 attDesc;
attDesc.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2;
attDesc.pNext = nullptr;
attDesc.flags = 0;
attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attDesc.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
// Handle stencil-only possibility.
if ( !pAttInfo ) { pAttInfo = pStencilAttInfo; }
if (pAttInfo && pAttInfo->imageView) {
MVKImageView* mvkImgView = (MVKImageView*)pAttInfo->imageView;
attDesc.format = mvkImgView->getVkFormat();
attDesc.samples = mvkImgView->getSampleCount();
attDesc.loadOp = pAttInfo->loadOp;
attDesc.storeOp = pAttInfo->storeOp;
attDesc.stencilLoadOp = pStencilAttInfo ? pStencilAttInfo->loadOp : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attDesc.stencilStoreOp = pStencilAttInfo ? pStencilAttInfo->storeOp : VK_ATTACHMENT_STORE_OP_DONT_CARE;
attDesc.initialLayout = pAttInfo->imageLayout;
attDesc.finalLayout = pAttInfo->imageLayout;
attachmentDescriptors[attDescIdx++] = attDesc;
if (pAttInfo->resolveImageView && pAttInfo->resolveMode != VK_RESOLVE_MODE_NONE) {
attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attDesc.stencilStoreOp = pStencilAttInfo ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE;
attDesc.initialLayout = pAttInfo->resolveImageLayout;
attDesc.finalLayout = pAttInfo->resolveImageLayout;
attachmentDescriptors[attDescIdx++] = attDesc;
}
}
}
MVKRenderPass* mvkCreateRenderPass(MVKDevice* device, const VkRenderingInfo* pRenderingInfo) {
// Renderpass attachments are sequentially indexed in this order:
// [color, color-resolve], ..., ds, ds-resolve
// skipping any attachments that do not have a VkImageView
uint32_t maxAttDescCnt = (pRenderingInfo->colorAttachmentCount + 1) * 2;
VkAttachmentDescription2 attachmentDescriptors[maxAttDescCnt];
VkAttachmentReference2 colorAttachmentRefs[pRenderingInfo->colorAttachmentCount];
VkAttachmentReference2 resolveAttachmentRefs[pRenderingInfo->colorAttachmentCount];
VkAttachmentReference2 attRef;
attRef.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2;
attRef.pNext = nullptr;
attRef.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
uint32_t attDescIdx = 0;
uint32_t caRefIdx = 0;
bool hasClrRslvAtt = false;
for (uint32_t caIdx = 0; caIdx < pRenderingInfo->colorAttachmentCount; caIdx++) {
auto& clrAtt = pRenderingInfo->pColorAttachments[caIdx];
if (clrAtt.imageView) {
attRef.layout = clrAtt.imageLayout;
attRef.attachment = attDescIdx;
colorAttachmentRefs[caRefIdx] = attRef;
if (clrAtt.resolveImageView && clrAtt.resolveMode != VK_RESOLVE_MODE_NONE) {
attRef.layout = clrAtt.resolveImageLayout;
attRef.attachment = attDescIdx + 1;
resolveAttachmentRefs[caRefIdx] = attRef;
hasClrRslvAtt = true;
}
caRefIdx++;
}
mvkAddAttachmentDescriptor(&clrAtt, nullptr, attachmentDescriptors, attDescIdx);
}
// Combine depth and stencil attachments into one depth-stencil attachment.
// If both depth and stencil are present, their views and layouts must match.
VkAttachmentReference2 dsAttRef;
VkAttachmentReference2 dsRslvAttRef;
VkResolveModeFlagBits depthResolveMode = VK_RESOLVE_MODE_NONE;
VkResolveModeFlagBits stencilResolveMode = VK_RESOLVE_MODE_NONE;
attRef.aspectMask = 0;
attRef.layout = VK_IMAGE_LAYOUT_UNDEFINED;
VkImageLayout rslvLayout = VK_IMAGE_LAYOUT_UNDEFINED;
if (pRenderingInfo->pDepthAttachment && pRenderingInfo->pDepthAttachment->imageView) {
attRef.aspectMask |= VK_IMAGE_ASPECT_DEPTH_BIT;
depthResolveMode = pRenderingInfo->pDepthAttachment->resolveMode;
attRef.layout = pRenderingInfo->pDepthAttachment->imageLayout;
rslvLayout = pRenderingInfo->pDepthAttachment->resolveImageLayout;
}
if (pRenderingInfo->pStencilAttachment && pRenderingInfo->pStencilAttachment->imageView) {
attRef.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
stencilResolveMode = pRenderingInfo->pStencilAttachment->resolveMode;
attRef.layout = pRenderingInfo->pStencilAttachment->imageLayout;
rslvLayout = pRenderingInfo->pStencilAttachment->resolveImageLayout;
}
attRef.attachment = attRef.aspectMask ? attDescIdx : VK_ATTACHMENT_UNUSED;
dsAttRef = attRef;
attRef.layout = rslvLayout;
attRef.attachment = attDescIdx + 1;
dsRslvAttRef = attRef;
mvkAddAttachmentDescriptor(pRenderingInfo->pDepthAttachment,
pRenderingInfo->pStencilAttachment,
attachmentDescriptors, attDescIdx);
// Depth/stencil resolve handled via VkSubpassDescription2 pNext
VkSubpassDescriptionDepthStencilResolve dsRslv;
dsRslv.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE;
dsRslv.pNext = nullptr;
dsRslv.depthResolveMode = depthResolveMode;
dsRslv.stencilResolveMode = stencilResolveMode;
dsRslv.pDepthStencilResolveAttachment = &dsRslvAttRef;
bool hasDSRslvAtt = depthResolveMode != VK_RESOLVE_MODE_NONE || stencilResolveMode != VK_RESOLVE_MODE_NONE;
// Define the subpass
VkSubpassDescription2 spDesc;
spDesc.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2;
spDesc.pNext = hasDSRslvAtt ? &dsRslv : nullptr;
spDesc.flags = 0;
spDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
spDesc.viewMask = pRenderingInfo->viewMask;
spDesc.inputAttachmentCount = 0;
spDesc.pInputAttachments = nullptr;
spDesc.colorAttachmentCount = caRefIdx;
spDesc.pColorAttachments = colorAttachmentRefs;
spDesc.pResolveAttachments = hasClrRslvAtt ? resolveAttachmentRefs : nullptr;;
spDesc.pDepthStencilAttachment = &dsAttRef;
spDesc.preserveAttachmentCount = 0;
spDesc.pPreserveAttachments = nullptr;
// Define the renderpass
VkRenderPassCreateInfo2 rpCreateInfo;
rpCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2;
rpCreateInfo.pNext = nullptr;
rpCreateInfo.flags = 0;
rpCreateInfo.attachmentCount = attDescIdx;
rpCreateInfo.pAttachments = attachmentDescriptors;
rpCreateInfo.subpassCount = 1;
rpCreateInfo.pSubpasses = &spDesc;
rpCreateInfo.dependencyCount = 0;
rpCreateInfo.pDependencies = nullptr;
rpCreateInfo.correlatedViewMaskCount = 0;
rpCreateInfo.pCorrelatedViewMasks = nullptr;
return device->createRenderPass(&rpCreateInfo, nullptr, pRenderingInfo->flags);
}
uint32_t mvkGetAttachments(const VkRenderingInfo* pRenderingInfo,
MVKImageView* attachments[],
VkClearValue clearValues[]) {
// Renderpass attachments are sequentially indexed in this order:
// [color, color-resolve], ..., ds, ds-resolve
// skipping any attachments that do not have a VkImageView
// For consistency, we populate the clear value of any resolve attachments, even though they are ignored.
uint32_t attIdx = 0;
for (uint32_t caIdx = 0; caIdx < pRenderingInfo->colorAttachmentCount; caIdx++) {
auto& clrAtt = pRenderingInfo->pColorAttachments[caIdx];
if (clrAtt.imageView) {
clearValues[attIdx] = clrAtt.clearValue;
attachments[attIdx++] = (MVKImageView*)clrAtt.imageView;
if (clrAtt.resolveImageView && clrAtt.resolveMode != VK_RESOLVE_MODE_NONE) {
clearValues[attIdx] = clrAtt.clearValue;
attachments[attIdx++] = (MVKImageView*)clrAtt.resolveImageView;
}
}
}
// We need to combine the DS attachments into one
auto* pDSAtt = pRenderingInfo->pDepthAttachment ? pRenderingInfo->pDepthAttachment : pRenderingInfo->pStencilAttachment;
if (pDSAtt) {
if (pDSAtt->imageView) {
clearValues[attIdx] = pDSAtt->clearValue;
attachments[attIdx++] = (MVKImageView*)pDSAtt->imageView;
}
if (pDSAtt->resolveImageView && pDSAtt->resolveMode != VK_RESOLVE_MODE_NONE) {
clearValues[attIdx] = pDSAtt->clearValue;
attachments[attIdx++] = (MVKImageView*)pDSAtt->resolveImageView;
}
}
return attIdx;
}
bool mvkIsColorAttachmentUsed(const VkPipelineRenderingCreateInfo* pRendInfo, uint32_t colorAttIdx) {
return pRendInfo && pRendInfo->pColorAttachmentFormats[colorAttIdx];
}
bool mvkHasColorAttachments(const VkPipelineRenderingCreateInfo* pRendInfo) {
if (pRendInfo) {
for (uint32_t caIdx = 0; caIdx < pRendInfo->colorAttachmentCount; caIdx++) {
if (mvkIsColorAttachmentUsed(pRendInfo, caIdx)) { return true; }
}
}
return false;
}
VkFormat mvkGetDepthStencilFormat(const VkPipelineRenderingCreateInfo* pRendInfo) {
return (pRendInfo
? (pRendInfo->depthAttachmentFormat
? pRendInfo->depthAttachmentFormat
: pRendInfo->stencilAttachmentFormat)
: VK_FORMAT_UNDEFINED);
}
uint32_t mvkGetNextViewMaskGroup(uint32_t viewMask, uint32_t* startView, uint32_t* viewCount, uint32_t *groupMask) {
// First, find the first set bit. This is the start of the next clump of views to be rendered.
// n.b. ffs(3) returns a 1-based index. This actually bit me during development of this feature.
int pos = ffs(viewMask) - 1;
int end = pos;
if (groupMask) { *groupMask = 0; }
// Now we'll step through the bits one at a time until we find a bit that isn't set.
// This is one past the end of the next clump. Clear the bits as we go, so we can use
// ffs(3) again on the next clump.
// TODO: Find a way to make this faster.
while (viewMask & (1 << end)) {
if (groupMask) { *groupMask |= viewMask & (1 << end); }
viewMask &= ~(1 << (end++));
}
if (startView) { *startView = pos; }
if (viewCount) { *viewCount = end - pos; }
return viewMask;
}

View File

@ -51,6 +51,7 @@ MVK_EXTENSION(KHR_descriptor_update_template, KHR_DESCRIPTOR_UPDATE_TEMPLAT
MVK_EXTENSION(KHR_device_group, KHR_DEVICE_GROUP, DEVICE, 10.11, 8.0)
MVK_EXTENSION(KHR_device_group_creation, KHR_DEVICE_GROUP_CREATION, INSTANCE, 10.11, 8.0)
MVK_EXTENSION(KHR_driver_properties, KHR_DRIVER_PROPERTIES, DEVICE, 10.11, 8.0)
MVK_EXTENSION(KHR_dynamic_rendering, KHR_DYNAMIC_RENDERING, DEVICE, 10.11, 8.0)
MVK_EXTENSION(KHR_external_fence, KHR_EXTERNAL_FENCE, DEVICE, 10.11, 8.0)
MVK_EXTENSION(KHR_external_fence_capabilities, KHR_EXTERNAL_FENCE_CAPABILITIES, INSTANCE, 10.11, 8.0)
MVK_EXTENSION(KHR_external_memory, KHR_EXTERNAL_MEMORY, DEVICE, 10.11, 8.0)

View File

@ -2374,6 +2374,28 @@ MVK_PUBLIC_VULKAN_SYMBOL void vkCmdEndRenderPass2KHR(
}
#pragma mark -
#pragma mark VK_KHR_dynamic_rendering extension
void vkCmdBeginRenderingKHR(
VkCommandBuffer commandBuffer,
const VkRenderingInfo* pRenderingInfo) {
MVKTraceVulkanCallStart();
MVKAddCmdFrom3Thresholds(BeginRendering, pRenderingInfo->colorAttachmentCount,
1, 2, 4, commandBuffer, pRenderingInfo);
MVKTraceVulkanCallEnd();
}
void vkCmdEndRenderingKHR(
VkCommandBuffer commandBuffer) {
MVKTraceVulkanCallStart();
MVKAddCmd(EndRendering, commandBuffer);
MVKTraceVulkanCallEnd();
}
#pragma mark -
#pragma mark VK_KHR_descriptor_update_template extension
@ -3070,6 +3092,29 @@ MVK_PUBLIC_VULKAN_SYMBOL void vkGetPrivateDataEXT(
MVKTraceVulkanCallEnd();
}
#pragma mark -
#pragma mark VK_EXT_sample_locations extension
void vkGetPhysicalDeviceMultisamplePropertiesEXT(
VkPhysicalDevice physicalDevice,
VkSampleCountFlagBits samples,
VkMultisamplePropertiesEXT* pMultisampleProperties) {
MVKTraceVulkanCallStart();
MVKPhysicalDevice* mvkPD = MVKPhysicalDevice::getMVKPhysicalDevice(physicalDevice);
mvkPD->getMultisampleProperties(samples, pMultisampleProperties);
MVKTraceVulkanCallEnd();
}
void vkCmdSetSampleLocationsEXT(
VkCommandBuffer commandBuffer,
const VkSampleLocationsInfoEXT* pSampleLocationsInfo) {
MVKTraceVulkanCallStart();
MVKAddCmd(SetSampleLocations, commandBuffer, pSampleLocationsInfo);
MVKTraceVulkanCallEnd();
}
#pragma mark -
#pragma mark VK_GOOGLE_display_timing extension
@ -3098,29 +3143,6 @@ MVK_PUBLIC_VULKAN_SYMBOL VkResult vkGetPastPresentationTimingGOOGLE(
return rslt;
}
#pragma mark -
#pragma mark VK_EXT_sample_locations extension
void vkGetPhysicalDeviceMultisamplePropertiesEXT(
VkPhysicalDevice physicalDevice,
VkSampleCountFlagBits samples,
VkMultisamplePropertiesEXT* pMultisampleProperties) {
MVKTraceVulkanCallStart();
MVKPhysicalDevice* mvkPD = MVKPhysicalDevice::getMVKPhysicalDevice(physicalDevice);
mvkPD->getMultisampleProperties(samples, pMultisampleProperties);
MVKTraceVulkanCallEnd();
}
void vkCmdSetSampleLocationsEXT(
VkCommandBuffer commandBuffer,
const VkSampleLocationsInfoEXT* pSampleLocationsInfo) {
MVKTraceVulkanCallStart();
MVKAddCmd(SetSampleLocations, commandBuffer, pSampleLocationsInfo);
MVKTraceVulkanCallEnd();
}
#pragma mark -
#pragma mark iOS & macOS surface extensions