text: /*
   * 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,
                                     std::string_view label)
          : GrSurface(gpu,
                      dimensions,
                      colorAttachment->isProtected() ? GrProtected::kYes : GrProtected::kNo,
                      label)
          // for the moment we only support 1:1 color to stencil
          , GrRenderTarget(gpu,
                           dimensions,
                           colorAttachment->numSamples(),
                           colorAttachment->isProtected() ? GrProtected::kYes : GrProtected::kNo,
                           label)
          , 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,
                                     std::string_view label)
          : GrSurface(gpu,
                      dimensions,
                      externalFramebuffer->colorAttachment()->isProtected() ? GrProtected::kYes
                                                                            : GrProtected::kNo,
                      label)
          , GrRenderTarget(gpu,
                           dimensions,
                           1,
                           externalFramebuffer->colorAttachment()->isProtected() ? GrProtected::kYes
                                                                                 : GrProtected::kNo,
                           label)
          , 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());
  }
  