/* * Copyright 2015 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "src/gpu/vk/GrVkRenderPass.h" #include "src/gpu/GrProcessor.h" #include "src/gpu/vk/GrVkFramebuffer.h" #include "src/gpu/vk/GrVkGpu.h" #include "src/gpu/vk/GrVkRenderTarget.h" #include "src/gpu/vk/GrVkUtil.h" typedef GrVkRenderPass::AttachmentsDescriptor::AttachmentDesc AttachmentDesc; void setup_vk_attachment_description(VkAttachmentDescription* attachment, const AttachmentDesc& desc, VkImageLayout startLayout, VkImageLayout endLayout) { attachment->flags = 0; attachment->format = desc.fFormat; SkAssertResult(GrSampleCountToVkSampleCount(desc.fSamples, &attachment->samples)); switch (startLayout) { case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL: case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL: case VK_IMAGE_LAYOUT_GENERAL: attachment->loadOp = desc.fLoadStoreOps.fLoadOp; attachment->storeOp = desc.fLoadStoreOps.fStoreOp; attachment->stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachment->stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; break; case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL: attachment->loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachment->storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachment->stencilLoadOp = desc.fLoadStoreOps.fLoadOp; attachment->stencilStoreOp = desc.fLoadStoreOps.fStoreOp; break; default: SK_ABORT("Unexpected attachment layout"); } attachment->initialLayout = startLayout; attachment->finalLayout = endLayout == VK_IMAGE_LAYOUT_UNDEFINED ? startLayout : endLayout; } GrVkRenderPass* GrVkRenderPass::CreateSimple(GrVkGpu* gpu, AttachmentsDescriptor* attachmentsDescriptor, AttachmentFlags attachmentFlags, SelfDependencyFlags selfDepFlags, LoadFromResolve loadFromResolve) { static const GrVkRenderPass::LoadStoreOps kBasicLoadStoreOps(VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE); switch (loadFromResolve) { case LoadFromResolve::kNo: return Create(gpu, attachmentFlags, attachmentsDescriptor, kBasicLoadStoreOps, kBasicLoadStoreOps, kBasicLoadStoreOps, selfDepFlags, loadFromResolve); case LoadFromResolve::kLoad: { static const GrVkRenderPass::LoadStoreOps kDiscardLoadStoreOps( VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_DONT_CARE); return Create(gpu, attachmentFlags, attachmentsDescriptor, kDiscardLoadStoreOps, kBasicLoadStoreOps, kBasicLoadStoreOps, selfDepFlags, loadFromResolve); } } SkUNREACHABLE; } GrVkRenderPass* GrVkRenderPass::Create(GrVkGpu* gpu, const GrVkRenderPass& compatibleRenderPass, const LoadStoreOps& colorOp, const LoadStoreOps& resolveOp, const LoadStoreOps& stencilOp) { AttachmentFlags attachmentFlags = compatibleRenderPass.fAttachmentFlags; AttachmentsDescriptor attachmentsDescriptor = compatibleRenderPass.fAttachmentsDescriptor; SelfDependencyFlags selfDepFlags = compatibleRenderPass.fSelfDepFlags; LoadFromResolve loadFromResolve = compatibleRenderPass.fLoadFromResolve; return Create(gpu, attachmentFlags, &attachmentsDescriptor, colorOp, resolveOp, stencilOp, selfDepFlags, loadFromResolve); } GrVkRenderPass* GrVkRenderPass::Create(GrVkGpu* gpu, AttachmentFlags attachmentFlags, AttachmentsDescriptor* attachmentsDescriptor, const LoadStoreOps& colorOp, const LoadStoreOps& resolveOp, const LoadStoreOps& stencilOp, SelfDependencyFlags selfDepFlags, LoadFromResolve loadFromResolve) { SkASSERT(!SkToBool(selfDepFlags & SelfDependencyFlags::kForNonCoherentAdvBlend) || gpu->caps()->advancedBlendEquationSupport()); SkASSERT(!SkToBool(selfDepFlags & SelfDependencyFlags::kForInputAttachment) || gpu->caps()->textureBarrierSupport()); // If we have a resolve attachment, we will always do a resolve into it. Thus it doesn't make // sense not to store the resolve attachment at the end of the render pass. // // Currently today (when not using discardable msaa images) we load and store the the msaa image // and then use the copy resolve command to handle the resolving. If instead we moved to doing // the resolving at the end of the last render pass, we would probably want a separate flag // for having a resolve attachment versus actually doing the resolving. This would allow us to // use the same VkPiplines for render passes where we resolve and those we don't since each will // always have the resolve attachment. The actual resolving or not does not affect render pass // compatibility if there is only one sub pass, just the presence of the attachment or not. SkASSERT(!SkToBool(attachmentFlags & kResolve_AttachmentFlag) || resolveOp.fStoreOp == VK_ATTACHMENT_STORE_OP_STORE); SkASSERT(loadFromResolve == LoadFromResolve::kNo || (SkToBool(attachmentFlags & kColor_AttachmentFlag) && SkToBool(attachmentFlags & kResolve_AttachmentFlag))); #ifdef SK_DEBUG if (loadFromResolve == LoadFromResolve::kLoad) { // If we are loading the resolve image into the msaa color attachment then we should not be // loading or storing the msaa attachment. Additionally we need to make sure we are loading // the resolve so it can be copied into the msaa color attachment. SkASSERT(colorOp.fLoadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE); SkASSERT(colorOp.fStoreOp == VK_ATTACHMENT_STORE_OP_DONT_CARE); SkASSERT(resolveOp.fLoadOp == VK_ATTACHMENT_LOAD_OP_LOAD); } #endif uint32_t numAttachments = attachmentsDescriptor->fAttachmentCount; // Attachment descriptions to be set on the render pass SkTArray attachments(numAttachments); attachments.reset(numAttachments); memset(attachments.begin(), 0, numAttachments * sizeof(VkAttachmentDescription)); // Refs to attachments on the render pass (as described by the VkAttachmentDescription above), // that are used by the subpass. VkAttachmentReference colorRef; VkAttachmentReference resolveRef; VkAttachmentReference resolveLoadInputRef; VkAttachmentReference stencilRef; uint32_t currentAttachment = 0; // Go through each of the attachment types (color, stencil) and set the necessary // on the various Vk structs. VkSubpassDescription subpassDescs[2]; memset(subpassDescs, 0, 2*sizeof(VkSubpassDescription)); const int mainSubpass = loadFromResolve == LoadFromResolve::kLoad ? 1 : 0; VkSubpassDescription& subpassDescMain = subpassDescs[mainSubpass]; subpassDescMain.flags = 0; subpassDescMain.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpassDescMain.inputAttachmentCount = 0; subpassDescMain.pInputAttachments = nullptr; subpassDescMain.pResolveAttachments = nullptr; uint32_t clearValueCount = 0; VkSubpassDependency dependencies[2]; int currentDependency = 0; bool skipSetSubpassDep = false; if (attachmentFlags & kColor_AttachmentFlag) { // set up color attachment bool needsGeneralLayout = SkToBool(selfDepFlags & SelfDependencyFlags::kForInputAttachment); VkImageLayout layout = needsGeneralLayout ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachmentsDescriptor->fColor.fLoadStoreOps = colorOp; setup_vk_attachment_description(&attachments[currentAttachment], attachmentsDescriptor->fColor, layout, layout); // setup subpass use of attachment colorRef.attachment = currentAttachment++; colorRef.layout = layout; subpassDescMain.colorAttachmentCount = 1; if (selfDepFlags != SelfDependencyFlags::kNone) { VkSubpassDependency& dependency = dependencies[currentDependency++]; dependency.srcSubpass = mainSubpass; dependency.dstSubpass = mainSubpass; dependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependency.dstStageMask = 0; dependency.dstAccessMask = 0; if (selfDepFlags & SelfDependencyFlags::kForNonCoherentAdvBlend) { // If we have coherent support we shouldn't be needing a self dependency SkASSERT(!gpu->caps()->advancedCoherentBlendEquationSupport()); dependency.dstStageMask |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependency.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT; } if (selfDepFlags & SelfDependencyFlags::kForInputAttachment) { SkASSERT(gpu->vkCaps().maxInputAttachmentDescriptors()); subpassDescMain.inputAttachmentCount = 1; subpassDescMain.pInputAttachments = &colorRef; dependency.dstStageMask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; dependency.dstAccessMask |= VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; skipSetSubpassDep = true; } } if (VK_ATTACHMENT_LOAD_OP_CLEAR == colorOp.fLoadOp) { clearValueCount = colorRef.attachment + 1; } } else { // I don't think there should ever be a time where we don't have a color attachment SkASSERT(false); SkASSERT(selfDepFlags == SelfDependencyFlags::kNone); colorRef.attachment = VK_ATTACHMENT_UNUSED; colorRef.layout = VK_IMAGE_LAYOUT_UNDEFINED; subpassDescMain.colorAttachmentCount = 0; } subpassDescMain.pColorAttachments = &colorRef; if (attachmentFlags & kResolve_AttachmentFlag) { attachmentsDescriptor->fResolve.fLoadStoreOps = resolveOp; VkImageLayout layout = loadFromResolve == LoadFromResolve::kLoad ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; setup_vk_attachment_description(&attachments[currentAttachment], attachmentsDescriptor->fResolve, layout, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); // setup main subpass use of attachment resolveRef.attachment = currentAttachment++; resolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; subpassDescMain.pResolveAttachments = &resolveRef; // Setup the load subpass and set subpass dependendcies if (loadFromResolve == LoadFromResolve::kLoad) { resolveLoadInputRef.attachment = resolveRef.attachment; resolveLoadInputRef.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; // The load subpass will always be the first VkSubpassDescription& subpassDescLoad = subpassDescs[0]; subpassDescLoad.flags = 0; subpassDescLoad.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpassDescLoad.inputAttachmentCount = 1; subpassDescLoad.pInputAttachments = &resolveLoadInputRef; subpassDescLoad.colorAttachmentCount = 1; subpassDescLoad.pColorAttachments = &colorRef; subpassDescLoad.pResolveAttachments = nullptr; subpassDescLoad.pDepthStencilAttachment = nullptr; subpassDescLoad.preserveAttachmentCount = 0; subpassDescLoad.pPreserveAttachments = nullptr; VkSubpassDependency& dependency = dependencies[currentDependency++]; dependency.srcSubpass = 0; dependency.dstSubpass = mainSubpass; dependency.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; } } if (attachmentFlags & kStencil_AttachmentFlag) { // set up stencil attachment attachmentsDescriptor->fStencil.fLoadStoreOps = stencilOp; setup_vk_attachment_description(&attachments[currentAttachment], attachmentsDescriptor->fStencil, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); // setup subpass use of attachment stencilRef.attachment = currentAttachment++; stencilRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; if (VK_ATTACHMENT_LOAD_OP_CLEAR == stencilOp.fLoadOp) { clearValueCount = std::max(clearValueCount, stencilRef.attachment + 1); } } else { stencilRef.attachment = VK_ATTACHMENT_UNUSED; stencilRef.layout = VK_IMAGE_LAYOUT_UNDEFINED; } subpassDescMain.pDepthStencilAttachment = &stencilRef; subpassDescMain.preserveAttachmentCount = 0; subpassDescMain.pPreserveAttachments = nullptr; SkASSERT(numAttachments == currentAttachment); uint32_t subpassCount = loadFromResolve == LoadFromResolve::kLoad ? 2 : 1; // Create the VkRenderPass compatible with the attachment descriptions above VkRenderPassCreateInfo createInfo; memset(&createInfo, 0, sizeof(VkRenderPassCreateInfo)); createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; createInfo.pNext = nullptr; createInfo.flags = 0; createInfo.attachmentCount = numAttachments; createInfo.pAttachments = attachments.begin(); createInfo.subpassCount = subpassCount; createInfo.pSubpasses = subpassDescs; // skipSetSubpassDep is a non-specification operation if (skipSetSubpassDep && currentDependency == 1) { createInfo.dependencyCount = 0; createInfo.pDependencies = nullptr; } else { createInfo.dependencyCount = currentDependency; createInfo.pDependencies = dependencies; } VkResult result; VkRenderPass renderPass; GR_VK_CALL_RESULT(gpu, result, CreateRenderPass(gpu->device(), &createInfo, nullptr, &renderPass)); if (result != VK_SUCCESS) { return nullptr; } VkExtent2D granularity; // Get granularity for this render pass GR_VK_CALL(gpu->vkInterface(), GetRenderAreaGranularity(gpu->device(), renderPass, &granularity)); return new GrVkRenderPass(gpu, renderPass, attachmentFlags, *attachmentsDescriptor, selfDepFlags, loadFromResolve, granularity, clearValueCount); } GrVkRenderPass::GrVkRenderPass(const GrVkGpu* gpu, VkRenderPass renderPass, AttachmentFlags flags, const AttachmentsDescriptor& descriptor, SelfDependencyFlags selfDepFlags, LoadFromResolve loadFromResolve, const VkExtent2D& granularity, uint32_t clearValueCount) : INHERITED(gpu) , fRenderPass(renderPass) , fAttachmentFlags(flags) , fAttachmentsDescriptor(descriptor) , fSelfDepFlags(selfDepFlags) , fLoadFromResolve(loadFromResolve) , fGranularity(granularity) , fClearValueCount(clearValueCount) { } void GrVkRenderPass::freeGPUData() const { if (!(fAttachmentFlags & kExternal_AttachmentFlag)) { GR_VK_CALL(fGpu->vkInterface(), DestroyRenderPass(fGpu->device(), fRenderPass, nullptr)); } } bool GrVkRenderPass::colorAttachmentIndex(uint32_t* index) const { *index = fColorAttachmentIndex; if ((fAttachmentFlags & kColor_AttachmentFlag) || (fAttachmentFlags & kExternal_AttachmentFlag)) { return true; } return false; } // Works under the assumption that stencil attachment will always be after the color and resolve // attachments. bool GrVkRenderPass::stencilAttachmentIndex(uint32_t* index) const { *index = 0; if (fAttachmentFlags & kColor_AttachmentFlag) { ++(*index); } if (fAttachmentFlags & kStencil_AttachmentFlag) { return true; } return false; } bool GrVkRenderPass::isCompatible(const AttachmentsDescriptor& desc, const AttachmentFlags& flags, SelfDependencyFlags selfDepFlags, LoadFromResolve loadFromResolve) const { SkASSERT(!(fAttachmentFlags & kExternal_AttachmentFlag)); if (flags != fAttachmentFlags) { return false; } if (fAttachmentFlags & kColor_AttachmentFlag) { if (!fAttachmentsDescriptor.fColor.isCompatible(desc.fColor)) { return false; } } if (fAttachmentFlags & kResolve_AttachmentFlag) { if (!fAttachmentsDescriptor.fResolve.isCompatible(desc.fResolve)) { return false; } } if (fAttachmentFlags & kStencil_AttachmentFlag) { if (!fAttachmentsDescriptor.fStencil.isCompatible(desc.fStencil)) { return false; } } if (fSelfDepFlags != selfDepFlags) { return false; } if (fLoadFromResolve != loadFromResolve) { return false; } return true; } bool GrVkRenderPass::isCompatible(GrVkRenderTarget* target, SelfDependencyFlags selfDepFlags, LoadFromResolve loadFromResolve) const { SkASSERT(!(fAttachmentFlags & kExternal_AttachmentFlag)); AttachmentsDescriptor desc; AttachmentFlags flags; target->getAttachmentsDescriptor(&desc, &flags, this->hasResolveAttachment(), this->hasStencilAttachment()); return this->isCompatible(desc, flags, selfDepFlags, loadFromResolve); } bool GrVkRenderPass::isCompatible(const GrVkRenderPass& renderPass) const { SkASSERT(!(fAttachmentFlags & kExternal_AttachmentFlag)); return this->isCompatible(renderPass.fAttachmentsDescriptor, renderPass.fAttachmentFlags, renderPass.fSelfDepFlags, renderPass.fLoadFromResolve); } bool GrVkRenderPass::isCompatibleExternalRP(VkRenderPass renderPass) const { SkASSERT(fAttachmentFlags & kExternal_AttachmentFlag); return fRenderPass == renderPass; } bool GrVkRenderPass::equalLoadStoreOps(const LoadStoreOps& colorOps, const LoadStoreOps& resolveOps, const LoadStoreOps& stencilOps) const { SkASSERT(!(fAttachmentFlags & kExternal_AttachmentFlag)); if (fAttachmentFlags & kColor_AttachmentFlag) { if (fAttachmentsDescriptor.fColor.fLoadStoreOps != colorOps) { return false; } } if (fAttachmentFlags & kResolve_AttachmentFlag) { if (fAttachmentsDescriptor.fResolve.fLoadStoreOps != resolveOps) { return false; } } if (fAttachmentFlags & kStencil_AttachmentFlag) { if (fAttachmentsDescriptor.fStencil.fLoadStoreOps != stencilOps) { return false; } } return true; } void GrVkRenderPass::genKey(GrProcessorKeyBuilder* b) const { GenKey(b, fAttachmentFlags, fAttachmentsDescriptor, fSelfDepFlags, fLoadFromResolve, (uint64_t)fRenderPass); } void GrVkRenderPass::GenKey(GrProcessorKeyBuilder* b, AttachmentFlags attachmentFlags, const AttachmentsDescriptor& attachmentsDescriptor, SelfDependencyFlags selfDepFlags, LoadFromResolve loadFromResolve, uint64_t externalRenderPass) { b->add32(attachmentFlags); if (attachmentFlags & kColor_AttachmentFlag) { b->add32(attachmentsDescriptor.fColor.fFormat); b->add32(attachmentsDescriptor.fColor.fSamples); } if (attachmentFlags & kResolve_AttachmentFlag) { b->add32(attachmentsDescriptor.fResolve.fFormat); b->add32(attachmentsDescriptor.fResolve.fSamples); } if (attachmentFlags & kStencil_AttachmentFlag) { b->add32(attachmentsDescriptor.fStencil.fFormat); b->add32(attachmentsDescriptor.fStencil.fSamples); } uint32_t extraFlags = (uint32_t)selfDepFlags; SkASSERT(extraFlags < (1 << 30)); SkASSERT((uint32_t)loadFromResolve <= 2); extraFlags |= ((uint32_t)loadFromResolve << 30); b->add32(extraFlags); if (attachmentFlags & kExternal_AttachmentFlag) { SkASSERT(!(attachmentFlags & ~kExternal_AttachmentFlag)); b->add32((uint32_t)(externalRenderPass & 0xFFFFFFFF)); b->add32((uint32_t)(externalRenderPass>>32)); } }