/*
 * Copyright 2017 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/GrSimpleMeshDrawOpHelper.h"

#include "src/gpu/ganesh/GrAppliedClip.h"
#include "src/gpu/ganesh/GrProcessorSet.h"
#include "src/gpu/ganesh/GrProgramInfo.h"
#include "src/gpu/ganesh/GrUserStencilSettings.h"
#include "src/gpu/ganesh/SkGr.h"
#include "src/gpu/ganesh/geometry/GrRect.h"

GrSimpleMeshDrawOpHelper::GrSimpleMeshDrawOpHelper(GrProcessorSet* processorSet,
                                                   GrAAType aaType,
                                                   InputFlags inputFlags)
        : fProcessors(processorSet)
        , fPipelineFlags((GrPipeline::InputFlags)inputFlags)
        , fAAType((int)aaType)
        , fUsesLocalCoords(false)
        , fCompatibleWithCoverageAsAlpha(false) {
    SkDEBUGCODE(fDidAnalysis = false);
    SkDEBUGCODE(fMadePipeline = false);
}

GrSimpleMeshDrawOpHelper::~GrSimpleMeshDrawOpHelper() {
    if (fProcessors) {
        fProcessors->~GrProcessorSet();
    }
}

GrDrawOp::FixedFunctionFlags GrSimpleMeshDrawOpHelper::fixedFunctionFlags() const {
    return GrAATypeIsHW(this->aaType()) ? GrDrawOp::FixedFunctionFlags::kUsesHWAA
                                        : GrDrawOp::FixedFunctionFlags::kNone;
}

bool GrSimpleMeshDrawOpHelper::isCompatible(const GrSimpleMeshDrawOpHelper& that,
                                            const GrCaps& caps, const SkRect& thisBounds,
                                            const SkRect& thatBounds, bool ignoreAAType) const {
    if (SkToBool(fProcessors) != SkToBool(that.fProcessors)) {
        return false;
    }
    if (fProcessors) {
        if (*fProcessors != *that.fProcessors) {
            return false;
        }
    }

#ifdef SK_DEBUG
    if (ignoreAAType) {
        // If we're ignoring AA it should be bc we already know they are the same or that
        // the are different but are compatible (i.e., one is AA and the other is None)
        SkASSERT(fAAType == that.fAAType ||
                 GrMeshDrawOp::CanUpgradeAAOnMerge(this->aaType(), that.aaType()));
    }
#endif

    bool result = fPipelineFlags == that.fPipelineFlags &&
                  (ignoreAAType || fAAType == that.fAAType);
    SkASSERT(!result || fCompatibleWithCoverageAsAlpha == that.fCompatibleWithCoverageAsAlpha);
    SkASSERT(!result || fUsesLocalCoords == that.fUsesLocalCoords);
    return result;
}

GrProcessorSet::Analysis GrSimpleMeshDrawOpHelper::finalizeProcessors(
        const GrCaps& caps, const GrAppliedClip* clip, GrClampType clampType,
        GrProcessorAnalysisCoverage geometryCoverage, SkPMColor4f* geometryColor, bool* wideColor) {
    GrProcessorAnalysisColor color = *geometryColor;
    auto result = this->finalizeProcessors(caps, clip, clampType, geometryCoverage, &color);
    color.isConstant(geometryColor);
    if (wideColor) {
        *wideColor = !geometryColor->fitsInBytes();
    }
    return result;
}

GrProcessorSet::Analysis GrSimpleMeshDrawOpHelper::finalizeProcessors(
        const GrCaps& caps, const GrAppliedClip* clip, const GrUserStencilSettings* userStencil,
        GrClampType clampType, GrProcessorAnalysisCoverage geometryCoverage,
        GrProcessorAnalysisColor* geometryColor) {
    SkDEBUGCODE(fDidAnalysis = true);
    GrProcessorSet::Analysis analysis;
    if (fProcessors) {
        GrProcessorAnalysisCoverage coverage = geometryCoverage;
        if (GrProcessorAnalysisCoverage::kNone == coverage) {
            coverage = (clip && clip->hasCoverageFragmentProcessor())
                               ? GrProcessorAnalysisCoverage::kSingleChannel
                               : GrProcessorAnalysisCoverage::kNone;
        }
        SkPMColor4f overrideColor;
        analysis = fProcessors->finalize(*geometryColor, coverage, clip, userStencil, caps,
                                         clampType, &overrideColor);
        if (analysis.inputColorIsOverridden()) {
            *geometryColor = overrideColor;
        }
    } else {
        analysis = GrProcessorSet::EmptySetAnalysis();
    }
    fUsesLocalCoords = analysis.usesLocalCoords();
    fCompatibleWithCoverageAsAlpha = analysis.isCompatibleWithCoverageAsAlpha();
    return analysis;
}

const GrPipeline* GrSimpleMeshDrawOpHelper::CreatePipeline(
                                                const GrCaps* caps,
                                                SkArenaAlloc* arena,
                                                skgpu::Swizzle writeViewSwizzle,
                                                GrAppliedClip&& appliedClip,
                                                const GrDstProxyView& dstProxyView,
                                                GrProcessorSet&& processorSet,
                                                GrPipeline::InputFlags pipelineFlags) {
    GrPipeline::InitArgs pipelineArgs;

    pipelineArgs.fInputFlags = pipelineFlags;
    pipelineArgs.fCaps = caps;
    pipelineArgs.fDstProxyView = dstProxyView;
    pipelineArgs.fWriteSwizzle = writeViewSwizzle;

    return arena->make<GrPipeline>(pipelineArgs,
                                   std::move(processorSet),
                                   std::move(appliedClip));
}

const GrPipeline* GrSimpleMeshDrawOpHelper::CreatePipeline(
                                                GrOpFlushState* flushState,
                                                GrProcessorSet&& processorSet,
                                                GrPipeline::InputFlags pipelineFlags) {
    return CreatePipeline(&flushState->caps(),
                          flushState->allocator(),
                          flushState->writeView().swizzle(),
                          flushState->detachAppliedClip(),
                          flushState->dstProxyView(),
                          std::move(processorSet),
                          pipelineFlags);
}

const GrPipeline* GrSimpleMeshDrawOpHelper::createPipeline(GrOpFlushState* flushState) {
    return CreatePipeline(&flushState->caps(),
                          flushState->allocator(),
                          flushState->writeView().swizzle(),
                          flushState->detachAppliedClip(),
                          flushState->dstProxyView(),
                          this->detachProcessorSet(),
                          this->pipelineFlags());
}

const GrPipeline* GrSimpleMeshDrawOpHelper::createPipeline(
        const GrCaps* caps,
        SkArenaAlloc* arena,
        skgpu::Swizzle writeViewSwizzle,
        GrAppliedClip&& appliedClip,
        const GrDstProxyView& dstProxyView) {
    return GrSimpleMeshDrawOpHelper::CreatePipeline(caps,
                                                    arena,
                                                    writeViewSwizzle,
                                                    std::move(appliedClip),
                                                    dstProxyView,
                                                    this->detachProcessorSet(),
                                                    this->pipelineFlags());
}

GrProgramInfo* GrSimpleMeshDrawOpHelper::CreateProgramInfo(
            const GrCaps* caps,
            SkArenaAlloc* arena,
            const GrSurfaceProxyView& writeView,
            bool usesMSAASurface,
            GrAppliedClip&& appliedClip,
            const GrDstProxyView& dstProxyView,
            GrGeometryProcessor* geometryProcessor,
            GrProcessorSet&& processorSet,
            GrPrimitiveType primitiveType,
            GrXferBarrierFlags renderPassXferBarriers,
            GrLoadOp colorLoadOp,
            GrPipeline::InputFlags pipelineFlags,
            const GrUserStencilSettings* stencilSettings) {
    auto pipeline = CreatePipeline(caps,
                                   arena,
                                   writeView.swizzle(),
                                   std::move(appliedClip),
                                   dstProxyView,
                                   std::move(processorSet),
                                   pipelineFlags);

    return CreateProgramInfo(caps, arena, pipeline, writeView, usesMSAASurface, geometryProcessor,
                             primitiveType, renderPassXferBarriers, colorLoadOp, stencilSettings);
}

GrProgramInfo* GrSimpleMeshDrawOpHelper::CreateProgramInfo(const GrCaps* caps,
                                                           SkArenaAlloc* arena,
                                                           const GrPipeline* pipeline,
                                                           const GrSurfaceProxyView& writeView,
                                                           bool usesMSAASurface,
                                                           GrGeometryProcessor* geometryProcessor,
                                                           GrPrimitiveType primitiveType,
                                                           GrXferBarrierFlags xferBarrierFlags,
                                                           GrLoadOp colorLoadOp,
                                                           const GrUserStencilSettings* stencilSettings) {
    auto tmp = arena->make<GrProgramInfo>(*caps,
                                          writeView,
                                          usesMSAASurface,
                                          pipeline,
                                          stencilSettings,
                                          geometryProcessor,
                                          primitiveType,
                                          xferBarrierFlags,
                                          colorLoadOp);
    return tmp;
}

GrProgramInfo* GrSimpleMeshDrawOpHelper::createProgramInfo(
                                            const GrCaps* caps,
                                            SkArenaAlloc* arena,
                                            const GrSurfaceProxyView& writeView,
                                            bool usesMSAASurface,
                                            GrAppliedClip&& appliedClip,
                                            const GrDstProxyView& dstProxyView,
                                            GrGeometryProcessor* gp,
                                            GrPrimitiveType primType,
                                            GrXferBarrierFlags renderPassXferBarriers,
                                            GrLoadOp colorLoadOp) {
    return CreateProgramInfo(caps,
                             arena,
                             writeView,
                             usesMSAASurface,
                             std::move(appliedClip),
                             dstProxyView,
                             gp,
                             this->detachProcessorSet(),
                             primType,
                             renderPassXferBarriers,
                             colorLoadOp,
                             this->pipelineFlags());
}

#if GR_TEST_UTILS
static void dump_pipeline_flags(GrPipeline::InputFlags flags, SkString* result) {
    if (GrPipeline::InputFlags::kNone != flags) {
        if (flags & GrPipeline::InputFlags::kSnapVerticesToPixelCenters) {
            result->append("Snap vertices to pixel center.\n");
        }
        if (flags & GrPipeline::InputFlags::kWireframe) {
            result->append("Wireframe enabled.\n");
        }
        if (flags & GrPipeline::InputFlags::kConservativeRaster) {
            result->append("Conservative raster enabled.\n");
        }
        return;
    }
    result->append("No pipeline flags\n");
}

SkString GrSimpleMeshDrawOpHelper::dumpInfo() const {
    const GrProcessorSet& processors = fProcessors ? *fProcessors : GrProcessorSet::EmptySet();
    SkString result = processors.dumpProcessors();
    result.append("AA Type: ");
    switch (this->aaType()) {
        case GrAAType::kNone:
            result.append(" none\n");
            break;
        case GrAAType::kCoverage:
            result.append(" coverage\n");
            break;
        case GrAAType::kMSAA:
            result.append(" msaa\n");
            break;
    }
    dump_pipeline_flags(fPipelineFlags, &result);
    return result;
}
#endif
