| /* | |
| * Copyright 2020 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/ops/DrawAtlasPathOp.h" | |
| #include "include/core/SkSamplingOptions.h" | |
| #include "include/gpu/ganesh/GrRecordingContext.h" | |
| #include "include/private/base/SkAlignedStorage.h" | |
| #include "include/private/base/SkAssert.h" | |
| #include "include/private/base/SkOnce.h" | |
| #include "include/private/base/SkPoint_impl.h" | |
| #include "include/private/base/SkTArray.h" | |
| #include "src/core/SkSLTypeShared.h" | |
| #include "src/gpu/BufferWriter.h" | |
| #include "src/gpu/KeyBuilder.h" | |
| #include "src/gpu/ResourceKey.h" | |
| #include "src/gpu/Swizzle.h" | |
| #include "src/gpu/ganesh/GrAppliedClip.h" | |
| #include "src/gpu/ganesh/GrBuffer.h" | |
| #include "src/gpu/ganesh/GrCaps.h" | |
| #include "src/gpu/ganesh/GrDstProxyView.h" | |
| #include "src/gpu/ganesh/GrGeometryProcessor.h" | |
| #include "src/gpu/ganesh/GrGpuBuffer.h" | |
| #include "src/gpu/ganesh/GrOpFlushState.h" | |
| #include "src/gpu/ganesh/GrPipeline.h" | |
| #include "src/gpu/ganesh/GrProcessorAnalysis.h" | |
| #include "src/gpu/ganesh/GrProgramInfo.h" | |
| #include "src/gpu/ganesh/GrRecordingContextPriv.h" | |
| #include "src/gpu/ganesh/GrRenderTargetProxy.h" | |
| #include "src/gpu/ganesh/GrResourceProvider.h" | |
| #include "src/gpu/ganesh/GrSamplerState.h" | |
| #include "src/gpu/ganesh/GrShaderCaps.h" | |
| #include "src/gpu/ganesh/GrShaderVar.h" | |
| #include "src/gpu/ganesh/GrSurfaceProxy.h" | |
| #include "src/gpu/ganesh/GrUserStencilSettings.h" | |
| #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h" | |
| #include "src/gpu/ganesh/glsl/GrGLSLUniformHandler.h" | |
| #include "src/gpu/ganesh/glsl/GrGLSLVarying.h" | |
| #include "src/gpu/ganesh/glsl/GrGLSLVertexGeoBuilder.h" | |
| #include <memory> | |
| class GrGLSLProgramDataManager; | |
| enum class GrXferBarrierFlags; | |
| using namespace skia_private; | |
| namespace { | |
| class DrawAtlasPathShader : public GrGeometryProcessor { | |
| public: | |
| DrawAtlasPathShader(bool usesLocalCoords, | |
| const skgpu::ganesh::AtlasInstancedHelper* atlasHelper, | |
| const GrShaderCaps& shaderCaps) | |
| : GrGeometryProcessor(kDrawAtlasPathShader_ClassID) | |
| , fUsesLocalCoords(usesLocalCoords) | |
| , fAtlasHelper(atlasHelper) | |
| , fAtlasAccess(GrSamplerState::Filter::kNearest, | |
| fAtlasHelper->proxy()->backendFormat(), | |
| fAtlasHelper->atlasSwizzle()) { | |
| if (!shaderCaps.fVertexIDSupport) { | |
| constexpr static Attribute kUnitCoordAttrib( | |
| "unitCoord", kFloat2_GrVertexAttribType, SkSLType::kFloat2); | |
| this->setVertexAttributesWithImplicitOffsets(&kUnitCoordAttrib, 1); | |
| } | |
| fAttribs.emplace_back("fillBounds", kFloat4_GrVertexAttribType, SkSLType::kFloat4); | |
| if (fUsesLocalCoords) { | |
| fAttribs.emplace_back("affineMatrix", kFloat4_GrVertexAttribType, SkSLType::kFloat4); | |
| fAttribs.emplace_back("translate", kFloat2_GrVertexAttribType, SkSLType::kFloat2); | |
| } | |
| SkASSERT(fAttribs.size() == this->colorAttribIdx()); | |
| fAttribs.emplace_back("color", kFloat4_GrVertexAttribType, SkSLType::kHalf4); | |
| fAtlasHelper->appendInstanceAttribs(&fAttribs); | |
| SkASSERT(fAttribs.size() <= kMaxInstanceAttribs); | |
| this->setInstanceAttributesWithImplicitOffsets(fAttribs.data(), fAttribs.size()); | |
| this->setTextureSamplerCnt(1); | |
| } | |
| private: | |
| class Impl; | |
| int colorAttribIdx() const { return fUsesLocalCoords ? 3 : 1; } | |
| const char* name() const override { return "DrawAtlasPathShader"; } | |
| void addToKey(const GrShaderCaps&, skgpu::KeyBuilder* b) const override { | |
| b->addBits(1, fUsesLocalCoords, "localCoords"); | |
| fAtlasHelper->getKeyBits(b); | |
| } | |
| const TextureSampler& onTextureSampler(int) const override { return fAtlasAccess; } | |
| std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override; | |
| const bool fUsesLocalCoords; | |
| const skgpu::ganesh::AtlasInstancedHelper* const fAtlasHelper; | |
| TextureSampler fAtlasAccess; | |
| constexpr static int kMaxInstanceAttribs = 6; | |
| STArray<kMaxInstanceAttribs, GrGeometryProcessor::Attribute> fAttribs; | |
| }; | |
| class DrawAtlasPathShader::Impl : public ProgramImpl { | |
| public: | |
| void setData(const GrGLSLProgramDataManager& pdman, | |
| const GrShaderCaps&, | |
| const GrGeometryProcessor& geomProc) override { | |
| auto* atlasHelper = geomProc.cast<DrawAtlasPathShader>().fAtlasHelper; | |
| atlasHelper->setUniformData(pdman, fAtlasAdjustUniform); | |
| } | |
| private: | |
| void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override { | |
| const auto& shader = args.fGeomProc.cast<DrawAtlasPathShader>(); | |
| args.fVaryingHandler->emitAttributes(shader); | |
| if (args.fShaderCaps->fVertexIDSupport) { | |
| // If we don't have sk_VertexID support then "unitCoord" already came in as a vertex | |
| // attrib. | |
| args.fVertBuilder->codeAppendf( | |
| "float2 unitCoord = float2(sk_VertexID & 1, sk_VertexID >> 1);"); | |
| } | |
| args.fVertBuilder->codeAppendf( | |
| "float2 devCoord = mix(fillBounds.xy, fillBounds.zw, unitCoord);"); | |
| gpArgs->fPositionVar.set(SkSLType::kFloat2, "devCoord"); | |
| if (shader.fUsesLocalCoords) { | |
| args.fVertBuilder->codeAppendf( | |
| "float2x2 M = float2x2(affineMatrix.xy, affineMatrix.zw);" | |
| "float2 localCoord = inverse(M) * (devCoord - translate);"); | |
| gpArgs->fLocalCoordVar.set(SkSLType::kFloat2, "localCoord"); | |
| } | |
| args.fFragBuilder->codeAppendf("half4 %s = half4(1);", args.fOutputCoverage); | |
| shader.fAtlasHelper->injectShaderCode(args, gpArgs->fPositionVar, &fAtlasAdjustUniform); | |
| args.fFragBuilder->codeAppendf("half4 %s;", args.fOutputColor); | |
| args.fVaryingHandler->addPassThroughAttribute( | |
| shader.fAttribs[shader.colorAttribIdx()].asShaderVar(), | |
| args.fOutputColor, | |
| GrGLSLVaryingHandler::Interpolation::kCanBeFlat); | |
| } | |
| GrGLSLUniformHandler::UniformHandle fAtlasAdjustUniform; | |
| }; | |
| std::unique_ptr<GrGeometryProcessor::ProgramImpl> DrawAtlasPathShader::makeProgramImpl( | |
| const GrShaderCaps&) const { | |
| return std::make_unique<Impl>(); | |
| } | |
| } // anonymous namespace | |
| namespace skgpu::ganesh { | |
| GrProcessorSet::Analysis DrawAtlasPathOp::finalize(const GrCaps& caps, const GrAppliedClip* clip, | |
| GrClampType clampType) { | |
| const GrProcessorSet::Analysis& analysis = fProcessors.finalize( | |
| fHeadInstance->fColor, GrProcessorAnalysisCoverage::kSingleChannel, clip, | |
| &GrUserStencilSettings::kUnused, caps, clampType, &fHeadInstance->fColor); | |
| fUsesLocalCoords = analysis.usesLocalCoords(); | |
| return analysis; | |
| } | |
| GrOp::CombineResult DrawAtlasPathOp::onCombineIfPossible(GrOp* op, SkArenaAlloc*, const GrCaps&) { | |
| auto that = op->cast<DrawAtlasPathOp>(); | |
| if (!fAtlasHelper.isCompatible(that->fAtlasHelper) || | |
| fProcessors != that->fProcessors) { | |
| return CombineResult::kCannotCombine; | |
| } | |
| SkASSERT(fUsesLocalCoords == that->fUsesLocalCoords); | |
| *fTailInstance = that->fHeadInstance; | |
| fTailInstance = that->fTailInstance; | |
| fInstanceCount += that->fInstanceCount; | |
| return CombineResult::kMerged; | |
| } | |
| void DrawAtlasPathOp::prepareProgram(const GrCaps& caps, SkArenaAlloc* arena, | |
| const GrSurfaceProxyView& writeView, bool usesMSAASurface, | |
| GrAppliedClip&& appliedClip, | |
| const GrDstProxyView& dstProxyView, | |
| GrXferBarrierFlags renderPassXferBarriers, | |
| GrLoadOp colorLoadOp) { | |
| SkASSERT(!fProgram); | |
| GrPipeline::InitArgs initArgs; | |
| initArgs.fCaps = ∩︀ | |
| initArgs.fDstProxyView = dstProxyView; | |
| initArgs.fWriteSwizzle = writeView.swizzle(); | |
| auto pipeline = arena->make<GrPipeline>(initArgs, std::move(fProcessors), | |
| std::move(appliedClip)); | |
| auto shader = arena->make<DrawAtlasPathShader>(fUsesLocalCoords, &fAtlasHelper, | |
| *caps.shaderCaps()); | |
| fProgram = arena->make<GrProgramInfo>(caps, writeView, usesMSAASurface, pipeline, | |
| &GrUserStencilSettings::kUnused, shader, | |
| GrPrimitiveType::kTriangleStrip, | |
| renderPassXferBarriers, colorLoadOp); | |
| } | |
| void DrawAtlasPathOp::onPrePrepare(GrRecordingContext* rContext, | |
| const GrSurfaceProxyView& writeView, | |
| GrAppliedClip* appliedClip, const GrDstProxyView& dstProxyView, | |
| GrXferBarrierFlags renderPassXferBarriers, | |
| GrLoadOp colorLoadOp) { | |
| // DMSAA is not supported on DDL. | |
| bool usesMSAASurface = writeView.asRenderTargetProxy()->numSamples() > 1; | |
| this->prepareProgram(*rContext->priv().caps(), rContext->priv().recordTimeAllocator(), | |
| writeView, usesMSAASurface, std::move(*appliedClip), dstProxyView, | |
| renderPassXferBarriers, colorLoadOp); | |
| SkASSERT(fProgram); | |
| rContext->priv().recordProgramInfo(fProgram); | |
| } | |
| SKGPU_DECLARE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey); | |
| void DrawAtlasPathOp::onPrepare(GrOpFlushState* flushState) { | |
| if (!fProgram) { | |
| this->prepareProgram(flushState->caps(), flushState->allocator(), flushState->writeView(), | |
| flushState->usesMSAASurface(), flushState->detachAppliedClip(), | |
| flushState->dstProxyView(), flushState->renderPassBarriers(), | |
| flushState->colorLoadOp()); | |
| SkASSERT(fProgram); | |
| } | |
| if (VertexWriter instanceWriter = flushState->makeVertexWriter( | |
| fProgram->geomProc().instanceStride(), fInstanceCount, &fInstanceBuffer, | |
| &fBaseInstance)) { | |
| for (const Instance* i = fHeadInstance; i; i = i->fNext) { | |
| instanceWriter << SkRect::Make(i->fFillBounds) | |
| << VertexWriter::If(fUsesLocalCoords, | |
| i->fLocalToDeviceIfUsingLocalCoords) | |
| << i->fColor; | |
| fAtlasHelper.writeInstanceData(&instanceWriter, &i->fAtlasInstance); | |
| } | |
| } | |
| if (!flushState->caps().shaderCaps()->fVertexIDSupport) { | |
| constexpr static SkPoint kUnitQuad[4] = {{0,0}, {0,1}, {1,0}, {1,1}}; | |
| SKGPU_DEFINE_STATIC_UNIQUE_KEY(gUnitQuadBufferKey); | |
| fVertexBufferIfNoIDSupport = flushState->resourceProvider()->findOrMakeStaticBuffer( | |
| GrGpuBufferType::kVertex, sizeof(kUnitQuad), kUnitQuad, gUnitQuadBufferKey); | |
| } | |
| } | |
| void DrawAtlasPathOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) { | |
| if (fProgram->geomProc().hasVertexAttributes() && !fVertexBufferIfNoIDSupport) { | |
| return; | |
| } | |
| flushState->bindPipelineAndScissorClip(*fProgram, this->bounds()); | |
| flushState->bindTextures(fProgram->geomProc(), *fAtlasHelper.proxy(), fProgram->pipeline()); | |
| flushState->bindBuffers(nullptr, std::move(fInstanceBuffer), fVertexBufferIfNoIDSupport); | |
| flushState->drawInstanced(fInstanceCount, fBaseInstance, 4, 0); | |
| } | |
| } // namespace skgpu::ganesh |