diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDraw.mm b/MoltenVK/MoltenVK/Commands/MVKCmdDraw.mm index 6e8a7b58..52f7999e 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCmdDraw.mm +++ b/MoltenVK/MoltenVK/Commands/MVKCmdDraw.mm @@ -194,7 +194,7 @@ void MVKCmdDraw::encode(MVKCommandEncoder* cmdEncoder) { threadsPerThreadgroup: MTLSizeMake(std::max(inControlPointCount, outControlPointCount), 1, 1)]; // Running this stage prematurely ended the render pass, so we have to start it up again. // TODO: On iOS, maybe we could use a tile shader to avoid this. - cmdEncoder->beginMetalRenderPass(); + cmdEncoder->beginMetalRenderPass(true); break; case kMVKGraphicsStageRasterization: if (pipeline->isTessellationPipeline()) { @@ -413,7 +413,7 @@ void MVKCmdDrawIndexed::encode(MVKCommandEncoder* cmdEncoder) { threadsPerThreadgroup: MTLSizeMake(std::max(inControlPointCount, outControlPointCount), 1, 1)]; // Running this stage prematurely ended the render pass, so we have to start it up again. // TODO: On iOS, maybe we could use a tile shader to avoid this. - cmdEncoder->beginMetalRenderPass(); + cmdEncoder->beginMetalRenderPass(true); break; case kMVKGraphicsStageRasterization: if (pipeline->isTessellationPipeline()) { @@ -663,7 +663,7 @@ void MVKCmdDrawIndirect::encode(MVKCommandEncoder* cmdEncoder) { mtlTCIndBuffOfst += sizeof(MTLDispatchThreadgroupsIndirectArguments); // Running this stage prematurely ended the render pass, so we have to start it up again. // TODO: On iOS, maybe we could use a tile shader to avoid this. - cmdEncoder->beginMetalRenderPass(); + cmdEncoder->beginMetalRenderPass(true); break; case kMVKGraphicsStageRasterization: if (pipeline->isTessellationPipeline()) { @@ -909,7 +909,7 @@ void MVKCmdDrawIndexedIndirect::encode(MVKCommandEncoder* cmdEncoder) { mtlTCIndBuffOfst += sizeof(MTLDispatchThreadgroupsIndirectArguments); // Running this stage prematurely ended the render pass, so we have to start it up again. // TODO: On iOS, maybe we could use a tile shader to avoid this. - cmdEncoder->beginMetalRenderPass(); + cmdEncoder->beginMetalRenderPass(true); break; case kMVKGraphicsStageRasterization: if (pipeline->isTessellationPipeline()) { diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h index 8e4a22b5..09e28544 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h +++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h @@ -246,7 +246,7 @@ public: void beginNextSubpass(VkSubpassContents renderpassContents); /** Begins a Metal render pass for the current render subpass. */ - void beginMetalRenderPass(); + void beginMetalRenderPass(bool loadOverride = false); /** Returns the render subpass that is currently active. */ MVKRenderSubpass* getSubpass(); diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm index be4477aa..4aabdd2f 100644 --- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm +++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm @@ -242,12 +242,12 @@ void MVKCommandEncoder::setSubpass(VkSubpassContents subpassContents, uint32_t s } // Creates _mtlRenderEncoder and marks cached render state as dirty so it will be set into the _mtlRenderEncoder. -void MVKCommandEncoder::beginMetalRenderPass() { +void MVKCommandEncoder::beginMetalRenderPass(bool loadOverride) { endCurrentMetalEncoding(); MTLRenderPassDescriptor* mtlRPDesc = [MTLRenderPassDescriptor renderPassDescriptor]; - getSubpass()->populateMTLRenderPassDescriptor(mtlRPDesc, _framebuffer, _clearValues, _isRenderingEntireAttachment); + getSubpass()->populateMTLRenderPassDescriptor(mtlRPDesc, _framebuffer, _clearValues, _isRenderingEntireAttachment, loadOverride); mtlRPDesc.visibilityResultBuffer = _occlusionQueryState.getVisibilityResultMTLBuffer(); if (_device->_pMetalFeatures->layeredRendering) { diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h index ad591b04..769c9fa9 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h +++ b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.h @@ -58,7 +58,8 @@ public: void populateMTLRenderPassDescriptor(MTLRenderPassDescriptor* mtlRPDesc, MVKFramebuffer* framebuffer, MVKVector& clearValues, - bool isRenderingEntireAttachment); + bool isRenderingEntireAttachment, + bool loadOverride = false); /** * Populates the specified vector with the attachments that need to be cleared @@ -111,7 +112,8 @@ public: MVKRenderSubpass* subpass, bool isRenderingEntireAttachment, bool hasResolveAttachment, - bool isStencil); + bool isStencil, + bool loadOverride = false); /** Returns whether this attachment should be cleared in the subpass. */ bool shouldUseClearAttachment(MVKRenderSubpass* subpass); diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm index dc21eb82..67c2980b 100644 --- a/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm +++ b/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm @@ -68,7 +68,8 @@ VkSampleCountFlagBits MVKRenderSubpass::getSampleCount() { void MVKRenderSubpass::populateMTLRenderPassDescriptor(MTLRenderPassDescriptor* mtlRPDesc, MVKFramebuffer* framebuffer, MVKVector& clearValues, - bool isRenderingEntireAttachment) { + bool isRenderingEntireAttachment, + bool loadOverride) { // Populate the Metal color attachments uint32_t caCnt = getColorAttachmentCount(); for (uint32_t caIdx = 0; caIdx < caCnt; caIdx++) { @@ -89,7 +90,8 @@ void MVKRenderSubpass::populateMTLRenderPassDescriptor(MTLRenderPassDescriptor* framebuffer->getAttachment(clrRPAttIdx)->populateMTLRenderPassAttachmentDescriptor(mtlColorAttDesc); if (clrMVKRPAtt->populateMTLRenderPassAttachmentDescriptor(mtlColorAttDesc, this, isRenderingEntireAttachment, - hasResolveAttachment, false)) { + hasResolveAttachment, false, + loadOverride)) { mtlColorAttDesc.clearColor = mvkMTLClearColorFromVkClearValue(clearValues[clrRPAttIdx], clrMVKRPAtt->getFormat()); } @@ -110,8 +112,9 @@ void MVKRenderSubpass::populateMTLRenderPassDescriptor(MTLRenderPassDescriptor* dsImage->populateMTLRenderPassAttachmentDescriptor(mtlDepthAttDesc); if (dsMVKRPAtt->populateMTLRenderPassAttachmentDescriptor(mtlDepthAttDesc, this, isRenderingEntireAttachment, - false, false)) { - mtlDepthAttDesc.clearDepth = mvkMTLClearDepthFromVkClearValue(clearValues[dsRPAttIdx]); + false, false, + loadOverride)) { + mtlDepthAttDesc.clearDepth = mvkMTLClearDepthFromVkClearValue(clearValues[dsRPAttIdx]); } } if (mvkMTLPixelFormatIsStencilFormat(mtlDSFormat)) { @@ -119,7 +122,8 @@ void MVKRenderSubpass::populateMTLRenderPassDescriptor(MTLRenderPassDescriptor* dsImage->populateMTLRenderPassAttachmentDescriptor(mtlStencilAttDesc); if (dsMVKRPAtt->populateMTLRenderPassAttachmentDescriptor(mtlStencilAttDesc, this, isRenderingEntireAttachment, - false, true)) { + false, true, + loadOverride)) { mtlStencilAttDesc.clearStencil = mvkMTLClearStencilFromVkClearValue(clearValues[dsRPAttIdx]); } } @@ -252,13 +256,16 @@ bool MVKRenderPassAttachment::populateMTLRenderPassAttachmentDescriptor(MTLRende MVKRenderSubpass* subpass, bool isRenderingEntireAttachment, bool hasResolveAttachment, - bool isStencil) { + bool isStencil, + bool loadOverride) { bool willClear = false; // Assume the attachment won't be cleared // Only allow clearing of entire attachment if we're actually rendering to the entire // attachment AND we're in the first subpass. - if ( isRenderingEntireAttachment && (subpass->_subpassIndex == _firstUseSubpassIdx) ) { + if ( loadOverride ) { + mtlAttDesc.loadAction = MTLLoadActionLoad; + } else if ( isRenderingEntireAttachment && (subpass->_subpassIndex == _firstUseSubpassIdx) ) { VkAttachmentLoadOp loadOp = isStencil ? _info.stencilLoadOp : _info.loadOp; mtlAttDesc.loadAction = mvkMTLLoadActionFromVkAttachmentLoadOp(loadOp); willClear = (_info.loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR);