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:
parent
6b3ef7b51a
commit
e3f8ce4ebc
@ -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*.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user