blob: 59c542954ed721a228fc6f4574ccb3a953317f11 [file] [log] [blame]
/*
* 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/ganesh/vk/GrVkRenderPass.h"
#include "src/gpu/KeyBuilder.h"
#include "src/gpu/ganesh/GrProcessor.h"
#include "src/gpu/ganesh/vk/GrVkFramebuffer.h"
#include "src/gpu/ganesh/vk/GrVkGpu.h"
#include "src/gpu/ganesh/vk/GrVkRenderTarget.h"
#include "src/gpu/ganesh/vk/GrVkUtil.h"
#include "src/gpu/vk/VulkanUtilsPriv.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(skgpu::SampleCountToVkSampleCount(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<VkAttachmentDescription> 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;
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;
}
}
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;
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;
if (!target->getAttachmentsDescriptor(&desc, &flags, this->hasResolveAttachment(),
this->hasStencilAttachment())) {
return false;
}
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(skgpu::KeyBuilder* b) const {
GenKey(b, fAttachmentFlags, fAttachmentsDescriptor, fSelfDepFlags,
fLoadFromResolve, (uint64_t)fRenderPass);
}
void GrVkRenderPass::GenKey(skgpu::KeyBuilder* 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));
}
}