blob: 011833df702ae36d33a621d7c87f7eed2a5afc4e [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/vk/GrVkRenderTarget.h"
#include "include/gpu/GrBackendSurface.h"
#include "include/gpu/GrDirectContext.h"
#include "src/gpu/GrBackendSurfaceMutableStateImpl.h"
#include "src/gpu/GrDirectContextPriv.h"
#include "src/gpu/GrResourceProvider.h"
#include "src/gpu/vk/GrVkCommandBuffer.h"
#include "src/gpu/vk/GrVkDescriptorSet.h"
#include "src/gpu/vk/GrVkFramebuffer.h"
#include "src/gpu/vk/GrVkGpu.h"
#include "src/gpu/vk/GrVkImageView.h"
#include "src/gpu/vk/GrVkResourceProvider.h"
#include "src/gpu/vk/GrVkUtil.h"
#include "include/gpu/vk/GrVkTypes.h"
#define VK_CALL(GPU, X) GR_VK_CALL(GPU->vkInterface(), X)
static int renderpass_features_to_index(bool hasResolve, bool hasStencil,
GrVkRenderPass::SelfDependencyFlags selfDepFlags,
GrVkRenderPass::LoadFromResolve loadFromReslove) {
int index = 0;
if (hasResolve) {
index += 1;
}
if (hasStencil) {
index += 2;
}
if (selfDepFlags & GrVkRenderPass::SelfDependencyFlags::kForInputAttachment) {
index += 4;
}
if (selfDepFlags & GrVkRenderPass::SelfDependencyFlags::kForNonCoherentAdvBlend) {
index += 8;
}
if (loadFromReslove == GrVkRenderPass::LoadFromResolve::kLoad) {
index += 16;
}
return index;
}
// We're virtually derived from GrSurface (via GrRenderTarget) so its
// constructor must be explicitly called.
GrVkRenderTarget::GrVkRenderTarget(GrVkGpu* gpu,
SkISize dimensions,
sk_sp<GrVkImage> colorAttachment,
sk_sp<GrVkImage> resolveAttachment,
CreateType createType)
: GrSurface(gpu, dimensions,
colorAttachment->isProtected() ? GrProtected::kYes : GrProtected::kNo)
// for the moment we only support 1:1 color to stencil
, GrRenderTarget(gpu, dimensions, colorAttachment->numSamples(),
colorAttachment->isProtected() ? GrProtected::kYes : GrProtected::kNo)
, fColorAttachment(std::move(colorAttachment))
, fResolveAttachment(std::move(resolveAttachment))
, fCachedFramebuffers() {
SkASSERT(fColorAttachment);
if (fColorAttachment->numSamples() == 1 && fColorAttachment->supportsInputAttachmentUsage()) {
SkASSERT(!fResolveAttachment);
// When we have a single sampled color attachment, we set both the color and resolve
// to the same attachment. This way if we use DMAA on this render target we will resolve
// to the single target attachment.
fResolveAttachment = fColorAttachment;
}
SkASSERT(!resolveAttachment ||
(fResolveAttachment->isProtected() == fColorAttachment->isProtected()));
SkASSERT(SkToBool(fColorAttachment->vkUsageFlags() & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT));
this->setFlags();
if (createType == CreateType::kDirectlyWrapped) {
this->registerWithCacheWrapped(GrWrapCacheable::kNo);
}
}
GrVkRenderTarget::GrVkRenderTarget(GrVkGpu* gpu,
SkISize dimensions,
sk_sp<GrVkFramebuffer> externalFramebuffer)
: GrSurface(gpu, dimensions,
externalFramebuffer->colorAttachment()->isProtected() ? GrProtected::kYes
: GrProtected::kNo)
, GrRenderTarget(gpu, dimensions, 1,
externalFramebuffer->colorAttachment()->isProtected() ? GrProtected::kYes
: GrProtected::kNo)
, fCachedFramebuffers()
, fExternalFramebuffer(externalFramebuffer) {
SkASSERT(fExternalFramebuffer);
SkASSERT(!fColorAttachment);
SkDEBUGCODE(auto colorAttachment = fExternalFramebuffer->colorAttachment());
SkASSERT(colorAttachment);
SkASSERT(colorAttachment->numSamples() == 1);
SkASSERT(SkToBool(colorAttachment->vkUsageFlags() & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT));
SkASSERT(!SkToBool(colorAttachment->vkUsageFlags() & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT));
this->setFlags();
this->registerWithCacheWrapped(GrWrapCacheable::kNo);
}
void GrVkRenderTarget::setFlags() {
if (this->wrapsSecondaryCommandBuffer()) {
return;
}
GrVkImage* nonMSAAAttachment = this->nonMSAAAttachment();
if (nonMSAAAttachment && nonMSAAAttachment->supportsInputAttachmentUsage()) {
this->setVkRTSupportsInputAttachment();
}
}
sk_sp<GrVkRenderTarget> GrVkRenderTarget::MakeWrappedRenderTarget(
GrVkGpu* gpu,
SkISize dimensions,
int sampleCnt,
const GrVkImageInfo& info,
sk_sp<GrBackendSurfaceMutableStateImpl> mutableState) {
SkASSERT(VK_NULL_HANDLE != info.fImage);
SkASSERT(1 == info.fLevelCount);
SkASSERT(sampleCnt >= 1 && info.fSampleCount >= 1);
int wrappedImageSampleCnt = static_cast<int>(info.fSampleCount);
if (sampleCnt != wrappedImageSampleCnt && wrappedImageSampleCnt != 1) {
return nullptr;
}
sk_sp<GrVkImage> wrappedAttachment =
GrVkImage::MakeWrapped(gpu,
dimensions,
info,
std::move(mutableState),
GrAttachment::UsageFlags::kColorAttachment,
kBorrow_GrWrapOwnership,
GrWrapCacheable::kNo);
if (!wrappedAttachment) {
return nullptr;
}
sk_sp<GrVkImage> colorAttachment;
colorAttachment = std::move(wrappedAttachment);
if (!colorAttachment) {
return nullptr;
}
GrVkRenderTarget* vkRT = new GrVkRenderTarget(gpu,dimensions, std::move(colorAttachment),
nullptr, CreateType::kDirectlyWrapped);
return sk_sp<GrVkRenderTarget>(vkRT);
}
sk_sp<GrVkRenderTarget> GrVkRenderTarget::MakeSecondaryCBRenderTarget(
GrVkGpu* gpu, SkISize dimensions, const GrVkDrawableInfo& vkInfo) {
const GrVkRenderPass* rp = gpu->resourceProvider().findCompatibleExternalRenderPass(
vkInfo.fCompatibleRenderPass, vkInfo.fColorAttachmentIndex);
if (!rp) {
return nullptr;
}
if (vkInfo.fSecondaryCommandBuffer == VK_NULL_HANDLE) {
return nullptr;
}
// We only set the few properties of the GrVkImageInfo that we know like layout and format. The
// others we keep at the default "null" values.
GrVkImageInfo info;
info.fImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
info.fFormat = vkInfo.fFormat;
info.fImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
sk_sp<GrBackendSurfaceMutableStateImpl> mutableState(new GrBackendSurfaceMutableStateImpl(
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_QUEUE_FAMILY_IGNORED));
sk_sp<GrVkImage> colorAttachment =
GrVkImage::MakeWrapped(gpu,
dimensions,
info,
std::move(mutableState),
GrAttachment::UsageFlags::kColorAttachment,
kBorrow_GrWrapOwnership,
GrWrapCacheable::kNo,
true);
std::unique_ptr<GrVkSecondaryCommandBuffer> scb(
GrVkSecondaryCommandBuffer::Create(vkInfo.fSecondaryCommandBuffer, rp));
if (!scb) {
return nullptr;
}
sk_sp<GrVkFramebuffer> framebuffer(new GrVkFramebuffer(
gpu, std::move(colorAttachment), sk_sp<const GrVkRenderPass>(rp),
std::move(scb)));
GrVkRenderTarget* vkRT = new GrVkRenderTarget(gpu, dimensions, std::move(framebuffer));
return sk_sp<GrVkRenderTarget>(vkRT);
}
GrBackendFormat GrVkRenderTarget::backendFormat() const {
if (this->wrapsSecondaryCommandBuffer()) {
return fExternalFramebuffer->colorAttachment()->backendFormat();
}
return fColorAttachment->backendFormat();
}
GrVkImage* GrVkRenderTarget::nonMSAAAttachment() const {
if (fColorAttachment->numSamples() == 1) {
return fColorAttachment.get();
} else {
return fResolveAttachment.get();
}
}
GrVkImage* GrVkRenderTarget::dynamicMSAAAttachment() {
if (fDynamicMSAAAttachment) {
return fDynamicMSAAAttachment.get();
}
const GrVkImage* nonMSAAColorAttachment = this->colorAttachment();
SkASSERT(nonMSAAColorAttachment->numSamples() == 1);
GrVkGpu* gpu = this->getVkGpu();
auto rp = gpu->getContext()->priv().resourceProvider();
const GrBackendFormat& format = nonMSAAColorAttachment->backendFormat();
GrMemoryless memoryless =
gpu->vkCaps().supportsMemorylessAttachments() ? GrMemoryless::kYes : GrMemoryless::kNo;
sk_sp<GrAttachment> msaaAttachment =
rp->getDiscardableMSAAAttachment(nonMSAAColorAttachment->dimensions(),
format,
gpu->caps()->internalMultisampleCount(format),
GrProtected(nonMSAAColorAttachment->isProtected()),
memoryless);
if (!msaaAttachment) {
return nullptr;
}
fDynamicMSAAAttachment = sk_sp<GrVkImage>(static_cast<GrVkImage*>(msaaAttachment.release()));
return fDynamicMSAAAttachment.get();
}
GrVkImage* GrVkRenderTarget::msaaAttachment() {
return this->colorAttachment()->numSamples() == 1 ? this->dynamicMSAAAttachment()
: this->colorAttachment();
}
bool GrVkRenderTarget::canAttemptStencilAttachment(bool useMSAASurface) const {
SkASSERT(!useMSAASurface || this->numSamples() > 1 ||
this->getVkGpu()->vkCaps().supportsDiscardableMSAAForDMSAA());
if (!useMSAASurface && this->numSamples() > 1) {
return false;
}
bool validMSAA = true;
if (useMSAASurface) {
validMSAA = this->numSamples() > 1 ||
(this->getVkGpu()->vkCaps().supportsDiscardableMSAAForDMSAA() &&
this->colorAttachment()->supportsInputAttachmentUsage());
}
// We don't know the status of the stencil attachment for wrapped external secondary command
// buffers so we just assume we don't have one.
return validMSAA && !this->wrapsSecondaryCommandBuffer();
}
bool GrVkRenderTarget::completeStencilAttachment(GrAttachment* stencil, bool useMSAASurface) {
SkASSERT(!this->wrapsSecondaryCommandBuffer());
SkASSERT(!useMSAASurface ||
this->numSamples() > 1 ||
this->getVkGpu()->vkCaps().supportsDiscardableMSAAForDMSAA());
return true;
}
sk_sp<GrVkFramebuffer> GrVkRenderTarget::externalFramebuffer() const {
return fExternalFramebuffer;
}
GrVkResourceProvider::CompatibleRPHandle GrVkRenderTarget::compatibleRenderPassHandle(
bool withResolve,
bool withStencil,
SelfDependencyFlags selfDepFlags,
LoadFromResolve loadFromResolve) {
SkASSERT(!this->wrapsSecondaryCommandBuffer());
const GrVkFramebuffer* fb =
this->getFramebuffer(withResolve, withStencil, selfDepFlags, loadFromResolve);
if (!fb) {
return {};
}
return fb->compatibleRenderPassHandle();
}
const GrVkRenderPass* GrVkRenderTarget::getSimpleRenderPass(bool withResolve,
bool withStencil,
SelfDependencyFlags selfDepFlags,
LoadFromResolve loadFromResolve) {
if (this->wrapsSecondaryCommandBuffer()) {
return fExternalFramebuffer->externalRenderPass();
}
const GrVkFramebuffer* fb =
this->getFramebuffer(withResolve, withStencil, selfDepFlags, loadFromResolve);
if (!fb) {
return nullptr;
}
return fb->compatibleRenderPass();
}
std::pair<const GrVkRenderPass*, GrVkResourceProvider::CompatibleRPHandle>
GrVkRenderTarget::createSimpleRenderPass(bool withResolve,
bool withStencil,
SelfDependencyFlags selfDepFlags,
LoadFromResolve loadFromResolve) {
SkASSERT(!this->wrapsSecondaryCommandBuffer());
GrVkResourceProvider& rp = this->getVkGpu()->resourceProvider();
GrVkResourceProvider::CompatibleRPHandle handle;
const GrVkRenderPass* renderPass = rp.findCompatibleRenderPass(
this, &handle, withResolve, withStencil, selfDepFlags,
loadFromResolve);
SkASSERT(!renderPass || handle.isValid());
return {renderPass, handle};
}
const GrVkFramebuffer* GrVkRenderTarget::getFramebuffer(bool withResolve,
bool withStencil,
SelfDependencyFlags selfDepFlags,
LoadFromResolve loadFromResolve) {
int cacheIndex =
renderpass_features_to_index(withResolve, withStencil, selfDepFlags, loadFromResolve);
SkASSERT(cacheIndex < GrVkRenderTarget::kNumCachedFramebuffers);
if (auto fb = fCachedFramebuffers[cacheIndex]) {
return fb.get();
}
this->createFramebuffer(withResolve, withStencil, selfDepFlags, loadFromResolve);
return fCachedFramebuffers[cacheIndex].get();
}
void GrVkRenderTarget::createFramebuffer(bool withResolve,
bool withStencil,
SelfDependencyFlags selfDepFlags,
LoadFromResolve loadFromResolve) {
SkASSERT(!this->wrapsSecondaryCommandBuffer());
GrVkGpu* gpu = this->getVkGpu();
auto[renderPass, compatibleHandle] =
this->createSimpleRenderPass(withResolve, withStencil, selfDepFlags, loadFromResolve);
if (!renderPass) {
return;
}
SkASSERT(compatibleHandle.isValid());
int cacheIndex =
renderpass_features_to_index(withResolve, withStencil, selfDepFlags, loadFromResolve);
SkASSERT(cacheIndex < GrVkRenderTarget::kNumCachedFramebuffers);
GrVkImage* resolve = withResolve ? this->resolveAttachment() : nullptr;
GrVkImage* colorAttachment = withResolve ? this->msaaAttachment() : this->colorAttachment();
// Stencil attachment view is stored in the base RT stencil attachment
bool useMSAA = this->numSamples() > 1 || withResolve;
GrVkImage* stencil = withStencil ? static_cast<GrVkImage*>(this->getStencilAttachment(useMSAA))
: nullptr;
fCachedFramebuffers[cacheIndex] =
GrVkFramebuffer::Make(gpu, this->dimensions(),
sk_sp<const GrVkRenderPass>(renderPass),
colorAttachment, resolve, stencil, compatibleHandle);
}
void GrVkRenderTarget::getAttachmentsDescriptor(GrVkRenderPass::AttachmentsDescriptor* desc,
GrVkRenderPass::AttachmentFlags* attachmentFlags,
bool withResolve,
bool withStencil) {
SkASSERT(!this->wrapsSecondaryCommandBuffer());
const GrVkImage* colorAttachment =
withResolve ? this->msaaAttachment() : this->colorAttachment();
desc->fColor.fFormat = colorAttachment->imageFormat();
desc->fColor.fSamples = colorAttachment->numSamples();
*attachmentFlags = GrVkRenderPass::kColor_AttachmentFlag;
uint32_t attachmentCount = 1;
if (withResolve) {
desc->fResolve.fFormat = desc->fColor.fFormat;
desc->fResolve.fSamples = 1;
*attachmentFlags |= GrVkRenderPass::kResolve_AttachmentFlag;
++attachmentCount;
}
if (withStencil) {
bool useMSAA = this->numSamples() > 1 || withResolve;
const GrAttachment* stencil = this->getStencilAttachment(useMSAA);
SkASSERT(stencil);
const GrVkImage* vkStencil = static_cast<const GrVkImage*>(stencil);
desc->fStencil.fFormat = vkStencil->imageFormat();
desc->fStencil.fSamples = vkStencil->numSamples();
SkASSERT(desc->fStencil.fSamples == desc->fColor.fSamples);
*attachmentFlags |= GrVkRenderPass::kStencil_AttachmentFlag;
++attachmentCount;
}
desc->fAttachmentCount = attachmentCount;
}
void GrVkRenderTarget::ReconstructAttachmentsDescriptor(const GrVkCaps& vkCaps,
const GrProgramInfo& programInfo,
GrVkRenderPass::AttachmentsDescriptor* desc,
GrVkRenderPass::AttachmentFlags* flags) {
VkFormat format;
SkAssertResult(programInfo.backendFormat().asVkFormat(&format));
desc->fColor.fFormat = format;
desc->fColor.fSamples = programInfo.numSamples();
*flags = GrVkRenderPass::kColor_AttachmentFlag;
uint32_t attachmentCount = 1;
if (vkCaps.programInfoWillUseDiscardableMSAA(programInfo)) {
desc->fResolve.fFormat = desc->fColor.fFormat;
desc->fResolve.fSamples = 1;
*flags |= GrVkRenderPass::kResolve_AttachmentFlag;
++attachmentCount;
}
SkASSERT(!programInfo.isStencilEnabled() || programInfo.needsStencil());
if (programInfo.needsStencil()) {
VkFormat stencilFormat = vkCaps.preferredStencilFormat();
desc->fStencil.fFormat = stencilFormat;
desc->fStencil.fSamples = programInfo.numSamples();
SkASSERT(desc->fStencil.fSamples == desc->fColor.fSamples);
*flags |= GrVkRenderPass::kStencil_AttachmentFlag;
++attachmentCount;
}
desc->fAttachmentCount = attachmentCount;
}
GrVkRenderTarget::~GrVkRenderTarget() {
// either release or abandon should have been called by the owner of this object.
SkASSERT(!fColorAttachment);
SkASSERT(!fResolveAttachment);
SkASSERT(!fDynamicMSAAAttachment);
for (int i = 0; i < kNumCachedFramebuffers; ++i) {
SkASSERT(!fCachedFramebuffers[i]);
}
SkASSERT(!fCachedInputDescriptorSet);
}
void GrVkRenderTarget::releaseInternalObjects() {
fColorAttachment.reset();
fResolveAttachment.reset();
fDynamicMSAAAttachment.reset();
for (int i = 0; i < kNumCachedFramebuffers; ++i) {
if (fCachedFramebuffers[i]) {
fCachedFramebuffers[i].reset();
}
}
if (fCachedInputDescriptorSet) {
fCachedInputDescriptorSet->recycle();
fCachedInputDescriptorSet = nullptr;
}
fExternalFramebuffer.reset();
}
void GrVkRenderTarget::onRelease() {
this->releaseInternalObjects();
GrRenderTarget::onRelease();
}
void GrVkRenderTarget::onAbandon() {
this->releaseInternalObjects();
GrRenderTarget::onAbandon();
}
GrBackendRenderTarget GrVkRenderTarget::getBackendRenderTarget() const {
SkASSERT(!this->wrapsSecondaryCommandBuffer());
// This should only get called with a non-released GrVkRenderTargets.
SkASSERT(!this->wasDestroyed());
// If we have a resolve attachment that is what we return for the backend render target
const GrVkImage* beAttachment = this->externalAttachment();
return GrBackendRenderTarget(beAttachment->width(), beAttachment->height(),
beAttachment->vkImageInfo(), beAttachment->getMutableState());
}
GrVkGpu* GrVkRenderTarget::getVkGpu() const {
SkASSERT(!this->wasDestroyed());
return static_cast<GrVkGpu*>(this->getGpu());
}