bool AAStrokeRectBatch::onCombineIfPossible(GrBatch* t, const GrCaps& caps) { AAStrokeRectBatch* that = t->cast<AAStrokeRectBatch>(); if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(), that->bounds(), caps)) { return false; } // TODO batch across miterstroke changes if (this->miterStroke() != that->miterStroke()) { return false; } // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses // local coords then we won't be able to batch. We could actually upload the viewmatrix // using vertex attributes in these cases, but haven't investigated that if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { return false; } // In the event of two batches, one who can tweak, one who cannot, we just fall back to // not tweaking if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) { fBatch.fCanTweakAlphaForCoverage = false; } if (this->color() != that->color()) { fBatch.fColor = GrColor_ILLEGAL; } fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); this->joinBounds(that->bounds()); return true; }
GrVkPipeline* GrVkPipeline::Create(GrVkGpu* gpu, const GrPipeline& pipeline, const GrPrimitiveProcessor& primProc, VkPipelineShaderStageCreateInfo* shaderStageInfo, int shaderStageCount, GrPrimitiveType primitiveType, const GrVkRenderPass& renderPass, VkPipelineLayout layout, VkPipelineCache cache) { VkPipelineVertexInputStateCreateInfo vertexInputInfo; VkVertexInputBindingDescription bindingDesc; SkSTArray<16, VkVertexInputAttributeDescription> attributeDesc; SkASSERT(primProc.numAttribs() <= gpu->vkCaps().maxVertexAttributes()); VkVertexInputAttributeDescription* pAttribs = attributeDesc.push_back_n(primProc.numAttribs()); setup_vertex_input_state(primProc, &vertexInputInfo, &bindingDesc, 1, pAttribs); VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo; setup_input_assembly_state(primitiveType, &inputAssemblyInfo); VkPipelineDepthStencilStateCreateInfo depthStencilInfo; setup_depth_stencil_state(gpu, pipeline.getStencil(), &depthStencilInfo); GrRenderTarget* rt = pipeline.getRenderTarget(); GrVkRenderTarget* vkRT = static_cast<GrVkRenderTarget*>(rt); VkPipelineViewportStateCreateInfo viewportInfo; setup_viewport_scissor_state(gpu, pipeline, vkRT, &viewportInfo); VkPipelineMultisampleStateCreateInfo multisampleInfo; setup_multisample_state(pipeline, primProc, gpu->caps(), &multisampleInfo); // We will only have one color attachment per pipeline. VkPipelineColorBlendAttachmentState attachmentStates[1]; VkPipelineColorBlendStateCreateInfo colorBlendInfo; setup_color_blend_state(gpu, pipeline, &colorBlendInfo, attachmentStates); VkPipelineRasterizationStateCreateInfo rasterInfo; setup_raster_state(gpu, pipeline, &rasterInfo); VkDynamicState dynamicStates[3]; VkPipelineDynamicStateCreateInfo dynamicInfo; setup_dynamic_state(gpu, pipeline, &dynamicInfo, dynamicStates); VkGraphicsPipelineCreateInfo pipelineCreateInfo; memset(&pipelineCreateInfo, 0, sizeof(VkGraphicsPipelineCreateInfo)); pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipelineCreateInfo.pNext = nullptr; pipelineCreateInfo.flags = 0; pipelineCreateInfo.stageCount = shaderStageCount; pipelineCreateInfo.pStages = shaderStageInfo; pipelineCreateInfo.pVertexInputState = &vertexInputInfo; pipelineCreateInfo.pInputAssemblyState = &inputAssemblyInfo; pipelineCreateInfo.pTessellationState = nullptr; pipelineCreateInfo.pViewportState = &viewportInfo; pipelineCreateInfo.pRasterizationState = &rasterInfo; pipelineCreateInfo.pMultisampleState = &multisampleInfo; pipelineCreateInfo.pDepthStencilState = &depthStencilInfo; pipelineCreateInfo.pColorBlendState = &colorBlendInfo; pipelineCreateInfo.pDynamicState = &dynamicInfo; pipelineCreateInfo.layout = layout; pipelineCreateInfo.renderPass = renderPass.vkRenderPass(); pipelineCreateInfo.subpass = 0; pipelineCreateInfo.basePipelineHandle = VK_NULL_HANDLE; pipelineCreateInfo.basePipelineIndex = -1; VkPipeline vkPipeline; VkResult err = GR_VK_CALL(gpu->vkInterface(), CreateGraphicsPipelines(gpu->device(), cache, 1, &pipelineCreateInfo, nullptr, &vkPipeline)); if (err) { return nullptr; } return new GrVkPipeline(vkPipeline); }
bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) { SkASSERT(!fParsingPath); // Call saveParsedPath() or discardParsedPath(). SkASSERT(fCoverageCountBatches.back().fEndNonScissorIndices == // Call closeCurrentBatch(). fTotalPrimitiveCounts[(int)ScissorMode::kNonScissored]); SkASSERT(fCoverageCountBatches.back().fEndScissorSubBatchIdx == fScissorSubBatches.count()); // Here we build a single instance buffer to share with every internal batch. // // CCPR processs 3 different types of primitives: triangles, quadratics, cubics. Each primitive // type is further divided into instances that require a scissor and those that don't. This // leaves us with 3*2 = 6 independent instance arrays to build for the GPU. // // Rather than place each instance array in its own GPU buffer, we allocate a single // megabuffer and lay them all out side-by-side. We can offset the "baseInstance" parameter in // our draw calls to direct the GPU to the applicable elements within a given array. // // We already know how big to make each of the 6 arrays from fTotalPrimitiveCounts, so layout is // straightforward. Start with triangles and quadratics. They both view the instance buffer as // an array of TriPointInstance[], so we can begin at zero and lay them out one after the other. fBaseInstances[0].fTriangles = 0; fBaseInstances[1].fTriangles = fBaseInstances[0].fTriangles + fTotalPrimitiveCounts[0].fTriangles; fBaseInstances[0].fQuadratics = fBaseInstances[1].fTriangles + fTotalPrimitiveCounts[1].fTriangles; fBaseInstances[1].fQuadratics = fBaseInstances[0].fQuadratics + fTotalPrimitiveCounts[0].fQuadratics; int triEndIdx = fBaseInstances[1].fQuadratics + fTotalPrimitiveCounts[1].fQuadratics; // Wound triangles and cubics both view the same instance buffer as an array of // QuadPointInstance[]. So, reinterpreting the instance data as QuadPointInstance[], we start // them on the first index that will not overwrite previous TriPointInstance data. int quadBaseIdx = GR_CT_DIV_ROUND_UP(triEndIdx * sizeof(TriPointInstance), sizeof(QuadPointInstance)); fBaseInstances[0].fWeightedTriangles = quadBaseIdx; fBaseInstances[1].fWeightedTriangles = fBaseInstances[0].fWeightedTriangles + fTotalPrimitiveCounts[0].fWeightedTriangles; fBaseInstances[0].fCubics = fBaseInstances[1].fWeightedTriangles + fTotalPrimitiveCounts[1].fWeightedTriangles; fBaseInstances[1].fCubics = fBaseInstances[0].fCubics + fTotalPrimitiveCounts[0].fCubics; fBaseInstances[0].fConics = fBaseInstances[1].fCubics + fTotalPrimitiveCounts[1].fCubics; fBaseInstances[1].fConics = fBaseInstances[0].fConics + fTotalPrimitiveCounts[0].fConics; int quadEndIdx = fBaseInstances[1].fConics + fTotalPrimitiveCounts[1].fConics; fInstanceBuffer = onFlushRP->makeBuffer(kVertex_GrBufferType, quadEndIdx * sizeof(QuadPointInstance)); if (!fInstanceBuffer) { return false; } TriPointInstance* triPointInstanceData = static_cast<TriPointInstance*>(fInstanceBuffer->map()); QuadPointInstance* quadPointInstanceData = reinterpret_cast<QuadPointInstance*>(triPointInstanceData); SkASSERT(quadPointInstanceData); PathInfo* nextPathInfo = fPathsInfo.begin(); float atlasOffsetX = 0.0, atlasOffsetY = 0.0; Sk2f atlasOffset; PrimitiveTallies instanceIndices[2] = {fBaseInstances[0], fBaseInstances[1]}; PrimitiveTallies* currIndices = nullptr; SkSTArray<256, int32_t, true> currFan; bool currFanIsTessellated = false; const SkTArray<SkPoint, true>& pts = fGeometry.points(); int ptsIdx = -1; int nextConicWeightIdx = 0; // Expand the ccpr verbs into GPU instance buffers. for (GrCCGeometry::Verb verb : fGeometry.verbs()) { switch (verb) { case GrCCGeometry::Verb::kBeginPath: SkASSERT(currFan.empty()); currIndices = &instanceIndices[(int)nextPathInfo->scissorMode()]; atlasOffsetX = static_cast<float>(nextPathInfo->atlasOffsetX()); atlasOffsetY = static_cast<float>(nextPathInfo->atlasOffsetY()); atlasOffset = {atlasOffsetX, atlasOffsetY}; currFanIsTessellated = nextPathInfo->hasFanTessellation(); if (currFanIsTessellated) { emit_tessellated_fan(nextPathInfo->fanTessellation(), nextPathInfo->fanTessellationCount(), atlasOffset, triPointInstanceData, quadPointInstanceData, currIndices); } ++nextPathInfo; continue; case GrCCGeometry::Verb::kBeginContour: SkASSERT(currFan.empty()); ++ptsIdx; if (!currFanIsTessellated) { currFan.push_back(ptsIdx); } continue; case GrCCGeometry::Verb::kLineTo: ++ptsIdx; if (!currFanIsTessellated) { SkASSERT(!currFan.empty()); currFan.push_back(ptsIdx); } continue; case GrCCGeometry::Verb::kMonotonicQuadraticTo: triPointInstanceData[currIndices->fQuadratics++].set(&pts[ptsIdx], atlasOffset); ptsIdx += 2; if (!currFanIsTessellated) { SkASSERT(!currFan.empty()); currFan.push_back(ptsIdx); } continue; case GrCCGeometry::Verb::kMonotonicCubicTo: quadPointInstanceData[currIndices->fCubics++].set(&pts[ptsIdx], atlasOffsetX, atlasOffsetY); ptsIdx += 3; if (!currFanIsTessellated) { SkASSERT(!currFan.empty()); currFan.push_back(ptsIdx); } continue; case GrCCGeometry::Verb::kMonotonicConicTo: quadPointInstanceData[currIndices->fConics++].setW( &pts[ptsIdx], atlasOffset, fGeometry.getConicWeight(nextConicWeightIdx)); ptsIdx += 2; ++nextConicWeightIdx; if (!currFanIsTessellated) { SkASSERT(!currFan.empty()); currFan.push_back(ptsIdx); } continue; case GrCCGeometry::Verb::kEndClosedContour: // endPt == startPt. if (!currFanIsTessellated) { SkASSERT(!currFan.empty()); currFan.pop_back(); } // fallthru. case GrCCGeometry::Verb::kEndOpenContour: // endPt != startPt. SkASSERT(!currFanIsTessellated || currFan.empty()); if (!currFanIsTessellated && currFan.count() >= 3) { int fanSize = currFan.count(); // Reserve space for emit_recursive_fan. Technically this can grow to // fanSize + log3(fanSize), but we approximate with log2. currFan.push_back_n(SkNextLog2(fanSize)); SkDEBUGCODE(TriPointInstance* end =) emit_recursive_fan(pts, currFan, 0, fanSize, atlasOffset, triPointInstanceData + currIndices->fTriangles); currIndices->fTriangles += fanSize - 2; SkASSERT(triPointInstanceData + currIndices->fTriangles == end); } currFan.reset(); continue; } }