| /* |
| * 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)); |
| } |
| } |