void GrVkGpuRTCommandBuffer::discard() { GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(fRenderTarget); CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo]; if (cbInfo.fIsEmpty) { // Change the render pass to do a don't-care load for both color & stencil GrVkRenderPass::LoadStoreOps vkColorOps(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE); GrVkRenderPass::LoadStoreOps vkStencilOps(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE); const GrVkRenderPass* oldRP = cbInfo.fRenderPass; const GrVkResourceProvider::CompatibleRPHandle& rpHandle = vkRT->compatibleRenderPassHandle(); if (rpHandle.isValid()) { cbInfo.fRenderPass = fGpu->resourceProvider().findRenderPass(rpHandle, vkColorOps, vkStencilOps); } else { cbInfo.fRenderPass = fGpu->resourceProvider().findRenderPass(*vkRT, vkColorOps, vkStencilOps); } SkASSERT(cbInfo.fRenderPass->isCompatible(*oldRP)); oldRP->unref(fGpu); cbInfo.fBounds.join(fRenderTarget->getBoundsRect()); cbInfo.fLoadStoreState = LoadStoreState::kStartsWithDiscard; } }
void GrVkGpuRTCommandBuffer::addAdditionalRenderPass() { GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(fRenderTarget); fCommandBufferInfos[fCurrentCmdInfo].currentCmdBuf()->end(fGpu); CommandBufferInfo& cbInfo = fCommandBufferInfos.push_back(); fCurrentCmdInfo++; GrVkRenderPass::LoadStoreOps vkColorOps(VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE); GrVkRenderPass::LoadStoreOps vkStencilOps(VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE); const GrVkResourceProvider::CompatibleRPHandle& rpHandle = vkRT->compatibleRenderPassHandle(); if (rpHandle.isValid()) { cbInfo.fRenderPass = fGpu->resourceProvider().findRenderPass(rpHandle, vkColorOps, vkStencilOps); } else { cbInfo.fRenderPass = fGpu->resourceProvider().findRenderPass(*vkRT, vkColorOps, vkStencilOps); } cbInfo.fLoadStoreState = LoadStoreState::kLoadAndStore; cbInfo.fCommandBuffers.push_back(fGpu->cmdPool()->findOrCreateSecondaryCommandBuffer(fGpu)); // It shouldn't matter what we set the clear color to here since we will assume loading of the // attachment. memset(&cbInfo.fColorClearValue, 0, sizeof(VkClearValue)); cbInfo.fBounds.setEmpty(); cbInfo.currentCmdBuf()->begin(fGpu, vkRT->framebuffer(), cbInfo.fRenderPass); }
void GrVkGpuCommandBuffer::discard(GrRenderTarget* target) { if (fIsEmpty) { // We will change the render pass to do a clear load instead GrVkRenderPass::LoadStoreOps vkColorOps(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE); GrVkRenderPass::LoadStoreOps vkStencilOps(VK_ATTACHMENT_LOAD_OP_DONT_CARE, VK_ATTACHMENT_STORE_OP_STORE); const GrVkRenderPass* oldRP = fRenderPass; GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(target); const GrVkResourceProvider::CompatibleRPHandle& rpHandle = vkRT->compatibleRenderPassHandle(); if (rpHandle.isValid()) { fRenderPass = fGpu->resourceProvider().findRenderPass(rpHandle, vkColorOps, vkStencilOps); } else { fRenderPass = fGpu->resourceProvider().findRenderPass(*vkRT, vkColorOps, vkStencilOps); } SkASSERT(fRenderPass->isCompatible(*oldRP)); oldRP->unref(fGpu); fStartsWithClear = false; } }
void GrVkGpuRTCommandBuffer::init() { GrVkRenderPass::LoadStoreOps vkColorOps(fVkColorLoadOp, fVkColorStoreOp); GrVkRenderPass::LoadStoreOps vkStencilOps(fVkStencilLoadOp, fVkStencilStoreOp); CommandBufferInfo& cbInfo = fCommandBufferInfos.push_back(); SkASSERT(fCommandBufferInfos.count() == 1); fCurrentCmdInfo = 0; GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(fRenderTarget); const GrVkResourceProvider::CompatibleRPHandle& rpHandle = vkRT->compatibleRenderPassHandle(); if (rpHandle.isValid()) { cbInfo.fRenderPass = fGpu->resourceProvider().findRenderPass(rpHandle, vkColorOps, vkStencilOps); } else { cbInfo.fRenderPass = fGpu->resourceProvider().findRenderPass(*vkRT, vkColorOps, vkStencilOps); } cbInfo.fColorClearValue.color.float32[0] = fClearColor[0]; cbInfo.fColorClearValue.color.float32[1] = fClearColor[1]; cbInfo.fColorClearValue.color.float32[2] = fClearColor[2]; cbInfo.fColorClearValue.color.float32[3] = fClearColor[3]; if (VK_ATTACHMENT_LOAD_OP_CLEAR == fVkColorLoadOp) { cbInfo.fBounds = SkRect::MakeWH(vkRT->width(), vkRT->height()); } else { cbInfo.fBounds.setEmpty(); } if (VK_ATTACHMENT_LOAD_OP_CLEAR == fVkColorLoadOp) { cbInfo.fLoadStoreState = LoadStoreState::kStartsWithClear; } else if (VK_ATTACHMENT_LOAD_OP_LOAD == fVkColorLoadOp && VK_ATTACHMENT_STORE_OP_STORE == fVkColorStoreOp) { cbInfo.fLoadStoreState = LoadStoreState::kLoadAndStore; } else if (VK_ATTACHMENT_LOAD_OP_DONT_CARE == fVkColorLoadOp) { cbInfo.fLoadStoreState = LoadStoreState::kStartsWithDiscard; } cbInfo.fCommandBuffers.push_back(fGpu->cmdPool()->findOrCreateSecondaryCommandBuffer(fGpu)); cbInfo.currentCmdBuf()->begin(fGpu, vkRT->framebuffer(), cbInfo.fRenderPass); }
void GrVkGpuRTCommandBuffer::copy(GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect, const SkIPoint& dstPoint) { CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo]; if (!cbInfo.fIsEmpty || LoadStoreState::kStartsWithClear == cbInfo.fLoadStoreState) { this->addAdditionalRenderPass(); } fPreCommandBufferTasks.emplace<Copy>( src, srcOrigin, srcRect, dstPoint, LoadStoreState::kStartsWithDiscard == cbInfo.fLoadStoreState); ++fCommandBufferInfos[fCurrentCmdInfo].fNumPreCmds; if (LoadStoreState::kLoadAndStore != cbInfo.fLoadStoreState) { // Change the render pass to do a load and store so we don't lose the results of our copy GrVkRenderPass::LoadStoreOps vkColorOps(VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE); GrVkRenderPass::LoadStoreOps vkStencilOps(VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE); const GrVkRenderPass* oldRP = cbInfo.fRenderPass; GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(fRenderTarget); const GrVkResourceProvider::CompatibleRPHandle& rpHandle = vkRT->compatibleRenderPassHandle(); if (rpHandle.isValid()) { cbInfo.fRenderPass = fGpu->resourceProvider().findRenderPass(rpHandle, vkColorOps, vkStencilOps); } else { cbInfo.fRenderPass = fGpu->resourceProvider().findRenderPass(*vkRT, vkColorOps, vkStencilOps); } SkASSERT(cbInfo.fRenderPass->isCompatible(*oldRP)); oldRP->unref(fGpu); cbInfo.fLoadStoreState = LoadStoreState::kLoadAndStore; } }
void GrVkGpuRTCommandBuffer::onClear(const GrFixedClip& clip, const SkPMColor4f& color) { GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(fRenderTarget); // parent class should never let us get here with no RT SkASSERT(!clip.hasWindowRectangles()); CommandBufferInfo& cbInfo = fCommandBufferInfos[fCurrentCmdInfo]; VkClearColorValue vkColor = {{color.fR, color.fG, color.fB, color.fA}}; if (cbInfo.fIsEmpty && !clip.scissorEnabled()) { // Change the render pass to do a clear load GrVkRenderPass::LoadStoreOps vkColorOps(VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE); // Preserve the stencil buffer's load & store settings GrVkRenderPass::LoadStoreOps vkStencilOps(fVkStencilLoadOp, fVkStencilStoreOp); const GrVkRenderPass* oldRP = cbInfo.fRenderPass; const GrVkResourceProvider::CompatibleRPHandle& rpHandle = vkRT->compatibleRenderPassHandle(); if (rpHandle.isValid()) { cbInfo.fRenderPass = fGpu->resourceProvider().findRenderPass(rpHandle, vkColorOps, vkStencilOps); } else { cbInfo.fRenderPass = fGpu->resourceProvider().findRenderPass(*vkRT, vkColorOps, vkStencilOps); } SkASSERT(cbInfo.fRenderPass->isCompatible(*oldRP)); oldRP->unref(fGpu); cbInfo.fColorClearValue.color = {{color.fR, color.fG, color.fB, color.fA}}; cbInfo.fLoadStoreState = LoadStoreState::kStartsWithClear; // Update command buffer bounds cbInfo.fBounds.join(fRenderTarget->getBoundsRect()); return; } // We always do a sub rect clear with clearAttachments since we are inside a render pass VkClearRect clearRect; // Flip rect if necessary SkIRect vkRect; if (!clip.scissorEnabled()) { vkRect.setXYWH(0, 0, fRenderTarget->width(), fRenderTarget->height()); } else if (kBottomLeft_GrSurfaceOrigin != fOrigin) { vkRect = clip.scissorRect(); } else { const SkIRect& scissor = clip.scissorRect(); vkRect.setLTRB(scissor.fLeft, fRenderTarget->height() - scissor.fBottom, scissor.fRight, fRenderTarget->height() - scissor.fTop); } clearRect.rect.offset = { vkRect.fLeft, vkRect.fTop }; clearRect.rect.extent = { (uint32_t)vkRect.width(), (uint32_t)vkRect.height() }; clearRect.baseArrayLayer = 0; clearRect.layerCount = 1; uint32_t colorIndex; SkAssertResult(cbInfo.fRenderPass->colorAttachmentIndex(&colorIndex)); VkClearAttachment attachment; attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; attachment.colorAttachment = colorIndex; attachment.clearValue.color = vkColor; cbInfo.currentCmdBuf()->clearAttachments(fGpu, 1, &attachment, 1, &clearRect); cbInfo.fIsEmpty = false; // Update command buffer bounds if (!clip.scissorEnabled()) { cbInfo.fBounds.join(fRenderTarget->getBoundsRect()); } else { cbInfo.fBounds.join(SkRect::Make(clip.scissorRect())); } return; }
void GrVkGpuCommandBuffer::onClear(GrRenderTarget* target, const GrFixedClip& clip, GrColor color) { // parent class should never let us get here with no RT SkASSERT(target); SkASSERT(!clip.hasWindowRectangles()); VkClearColorValue vkColor; GrColorToRGBAFloat(color, vkColor.float32); GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(target); if (fIsEmpty && !clip.scissorEnabled()) { // We will change the render pass to do a clear load instead GrVkRenderPass::LoadStoreOps vkColorOps(VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE); GrVkRenderPass::LoadStoreOps vkStencilOps(VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE); const GrVkRenderPass* oldRP = fRenderPass; const GrVkResourceProvider::CompatibleRPHandle& rpHandle = vkRT->compatibleRenderPassHandle(); if (rpHandle.isValid()) { fRenderPass = fGpu->resourceProvider().findRenderPass(rpHandle, vkColorOps, vkStencilOps); } else { fRenderPass = fGpu->resourceProvider().findRenderPass(*vkRT, vkColorOps, vkStencilOps); } SkASSERT(fRenderPass->isCompatible(*oldRP)); oldRP->unref(fGpu); GrColorToRGBAFloat(color, fColorClearValue.color.float32); fStartsWithClear = true; return; } // We always do a sub rect clear with clearAttachments since we are inside a render pass VkClearRect clearRect; // Flip rect if necessary SkIRect vkRect; if (!clip.scissorEnabled()) { vkRect.setXYWH(0, 0, vkRT->width(), vkRT->height()); } else if (kBottomLeft_GrSurfaceOrigin != vkRT->origin()) { vkRect = clip.scissorRect(); } else { const SkIRect& scissor = clip.scissorRect(); vkRect.setLTRB(scissor.fLeft, vkRT->height() - scissor.fBottom, scissor.fRight, vkRT->height() - scissor.fTop); } clearRect.rect.offset = { vkRect.fLeft, vkRect.fTop }; clearRect.rect.extent = { (uint32_t)vkRect.width(), (uint32_t)vkRect.height() }; clearRect.baseArrayLayer = 0; clearRect.layerCount = 1; uint32_t colorIndex; SkAssertResult(fRenderPass->colorAttachmentIndex(&colorIndex)); VkClearAttachment attachment; attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; attachment.colorAttachment = colorIndex; attachment.clearValue.color = vkColor; fCommandBuffer->clearAttachments(fGpu, 1, &attachment, 1, &clearRect); fIsEmpty = false; return; }
bool GrVkCopyManager::copySurfaceAsDraw(GrVkGpu* gpu, GrSurface* dst, GrSurfaceOrigin dstOrigin, GrSurface* src, GrSurfaceOrigin srcOrigin, const SkIRect& srcRect, const SkIPoint& dstPoint, bool canDiscardOutsideDstRect) { // None of our copy methods can handle a swizzle. TODO: Make copySurfaceAsDraw handle the // swizzle. if (gpu->caps()->shaderCaps()->configOutputSwizzle(src->config()) != gpu->caps()->shaderCaps()->configOutputSwizzle(dst->config())) { return false; } GrVkRenderTarget* rt = static_cast<GrVkRenderTarget*>(dst->asRenderTarget()); if (!rt) { return false; } GrVkTexture* srcTex = static_cast<GrVkTexture*>(src->asTexture()); if (!srcTex) { return false; } if (VK_NULL_HANDLE == fVertShaderModule) { SkASSERT(VK_NULL_HANDLE == fFragShaderModule && nullptr == fPipelineLayout && nullptr == fVertexBuffer.get() && nullptr == fUniformBuffer.get()); if (!this->createCopyProgram(gpu)) { SkDebugf("Failed to create copy program.\n"); return false; } } SkASSERT(fPipelineLayout); GrVkResourceProvider& resourceProv = gpu->resourceProvider(); GrVkCopyPipeline* pipeline = resourceProv.findOrCreateCopyPipeline(rt, fShaderStageInfo, fPipelineLayout->layout()); if (!pipeline) { return false; } // UPDATE UNIFORM DESCRIPTOR SET int w = srcRect.width(); int h = srcRect.height(); // dst rect edges in NDC (-1 to 1) int dw = dst->width(); int dh = dst->height(); float dx0 = 2.f * dstPoint.fX / dw - 1.f; float dx1 = 2.f * (dstPoint.fX + w) / dw - 1.f; float dy0 = 2.f * dstPoint.fY / dh - 1.f; float dy1 = 2.f * (dstPoint.fY + h) / dh - 1.f; if (kBottomLeft_GrSurfaceOrigin == dstOrigin) { dy0 = -dy0; dy1 = -dy1; } float sx0 = (float)srcRect.fLeft; float sx1 = (float)(srcRect.fLeft + w); float sy0 = (float)srcRect.fTop; float sy1 = (float)(srcRect.fTop + h); int sh = src->height(); if (kBottomLeft_GrSurfaceOrigin == srcOrigin) { sy0 = sh - sy0; sy1 = sh - sy1; } // src rect edges in normalized texture space (0 to 1). int sw = src->width(); sx0 /= sw; sx1 /= sw; sy0 /= sh; sy1 /= sh; float uniData[] = { dx1 - dx0, dy1 - dy0, dx0, dy0, // posXform sx1 - sx0, sy1 - sy0, sx0, sy0 }; // texCoordXform fUniformBuffer->updateData(gpu, uniData, sizeof(uniData), nullptr); const GrVkDescriptorSet* uniformDS = resourceProv.getUniformDescriptorSet(); SkASSERT(uniformDS); VkDescriptorBufferInfo uniBufferInfo; uniBufferInfo.buffer = fUniformBuffer->buffer(); uniBufferInfo.offset = fUniformBuffer->offset(); uniBufferInfo.range = fUniformBuffer->size(); VkWriteDescriptorSet descriptorWrites; descriptorWrites.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptorWrites.pNext = nullptr; descriptorWrites.dstSet = uniformDS->descriptorSet(); descriptorWrites.dstBinding = GrVkUniformHandler::kGeometryBinding; descriptorWrites.dstArrayElement = 0; descriptorWrites.descriptorCount = 1; descriptorWrites.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; descriptorWrites.pImageInfo = nullptr; descriptorWrites.pBufferInfo = &uniBufferInfo; descriptorWrites.pTexelBufferView = nullptr; GR_VK_CALL(gpu->vkInterface(), UpdateDescriptorSets(gpu->device(), 1, &descriptorWrites, 0, nullptr)); // UPDATE SAMPLER DESCRIPTOR SET const GrVkDescriptorSet* samplerDS = gpu->resourceProvider().getSamplerDescriptorSet(fSamplerDSHandle); GrSamplerState samplerState = GrSamplerState::ClampNearest(); GrVkSampler* sampler = resourceProv.findOrCreateCompatibleSampler( samplerState, GrVkYcbcrConversionInfo()); VkDescriptorImageInfo imageInfo; memset(&imageInfo, 0, sizeof(VkDescriptorImageInfo)); imageInfo.sampler = sampler->sampler(); imageInfo.imageView = srcTex->textureView()->imageView(); imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkWriteDescriptorSet writeInfo; memset(&writeInfo, 0, sizeof(VkWriteDescriptorSet)); writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeInfo.pNext = nullptr; writeInfo.dstSet = samplerDS->descriptorSet(); writeInfo.dstBinding = 0; writeInfo.dstArrayElement = 0; writeInfo.descriptorCount = 1; writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeInfo.pImageInfo = &imageInfo; writeInfo.pBufferInfo = nullptr; writeInfo.pTexelBufferView = nullptr; GR_VK_CALL(gpu->vkInterface(), UpdateDescriptorSets(gpu->device(), 1, &writeInfo, 0, nullptr)); VkDescriptorSet vkDescSets[] = { uniformDS->descriptorSet(), samplerDS->descriptorSet() }; GrVkRenderTarget* texRT = static_cast<GrVkRenderTarget*>(srcTex->asRenderTarget()); if (texRT) { gpu->resolveRenderTargetNoFlush(texRT); } // TODO: Make tighter bounds and then adjust bounds for origin and granularity if we see // any perf issues with using the whole bounds SkIRect bounds = SkIRect::MakeWH(rt->width(), rt->height()); // Change layouts of rt and texture. We aren't blending so we don't need color attachment read // access for blending. GrVkImage* targetImage = rt->msaaImage() ? rt->msaaImage() : rt; VkAccessFlags dstAccessFlags = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; if (!canDiscardOutsideDstRect) { // We need to load the color attachment so need to be able to read it. dstAccessFlags |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; } targetImage->setImageLayout(gpu, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, dstAccessFlags, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, false); srcTex->setImageLayout(gpu, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, false); GrStencilAttachment* stencil = rt->renderTargetPriv().getStencilAttachment(); if (stencil) { GrVkStencilAttachment* vkStencil = (GrVkStencilAttachment*)stencil; // We aren't actually using the stencil but we still load and store it so we need // appropriate barriers. // TODO: Once we refactor surface and how we conntect stencil to RTs, we should not even // have the stencil on this render pass if possible. vkStencil->setImageLayout(gpu, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, false); } VkAttachmentLoadOp loadOp = canDiscardOutsideDstRect ? VK_ATTACHMENT_LOAD_OP_DONT_CARE : VK_ATTACHMENT_LOAD_OP_LOAD; GrVkRenderPass::LoadStoreOps vkColorOps(loadOp, VK_ATTACHMENT_STORE_OP_STORE); GrVkRenderPass::LoadStoreOps vkStencilOps(VK_ATTACHMENT_LOAD_OP_LOAD, VK_ATTACHMENT_STORE_OP_STORE); const GrVkRenderPass* renderPass; const GrVkResourceProvider::CompatibleRPHandle& rpHandle = rt->compatibleRenderPassHandle(); if (rpHandle.isValid()) { renderPass = gpu->resourceProvider().findRenderPass(rpHandle, vkColorOps, vkStencilOps); } else { renderPass = gpu->resourceProvider().findRenderPass(*rt, vkColorOps, vkStencilOps); } SkASSERT(renderPass->isCompatible(*rt->simpleRenderPass())); GrVkPrimaryCommandBuffer* cmdBuffer = gpu->currentCommandBuffer(); cmdBuffer->beginRenderPass(gpu, renderPass, nullptr, *rt, bounds, true); GrVkSecondaryCommandBuffer* secondary = gpu->cmdPool()->findOrCreateSecondaryCommandBuffer(gpu); if (!secondary) { return false; } secondary->begin(gpu, rt->framebuffer(), renderPass); secondary->bindPipeline(gpu, pipeline); // Uniform DescriptorSet, Sampler DescriptorSet, and vertex shader uniformBuffer SkSTArray<3, const GrVkRecycledResource*> descriptorRecycledResources; descriptorRecycledResources.push_back(uniformDS); descriptorRecycledResources.push_back(samplerDS); descriptorRecycledResources.push_back(fUniformBuffer->resource()); // One sampler, texture view, and texture SkSTArray<3, const GrVkResource*> descriptorResources; descriptorResources.push_back(sampler); descriptorResources.push_back(srcTex->textureView()); descriptorResources.push_back(srcTex->resource()); secondary->bindDescriptorSets(gpu, descriptorRecycledResources, descriptorResources, fPipelineLayout, 0, 2, vkDescSets, 0, nullptr); // Set Dynamic viewport and stencil // We always use one viewport the size of the RT VkViewport viewport; viewport.x = 0.0f; viewport.y = 0.0f; viewport.width = SkIntToScalar(rt->width()); viewport.height = SkIntToScalar(rt->height()); viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; secondary->setViewport(gpu, 0, 1, &viewport); // We assume the scissor is not enabled so just set it to the whole RT VkRect2D scissor; scissor.extent.width = rt->width(); scissor.extent.height = rt->height(); scissor.offset.x = 0; scissor.offset.y = 0; secondary->setScissor(gpu, 0, 1, &scissor); secondary->bindInputBuffer(gpu, 0, fVertexBuffer.get()); secondary->draw(gpu, 4, 1, 0, 0); secondary->end(gpu); cmdBuffer->executeCommands(gpu, secondary); cmdBuffer->endRenderPass(gpu); secondary->unref(gpu); // Release all temp resources which should now be reffed by the cmd buffer pipeline->unref(gpu); uniformDS->unref(gpu); samplerDS->unref(gpu); sampler->unref(gpu); renderPass->unref(gpu); return true; }