/*
 * 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/GrRecordingContextPriv.h"

#include "include/core/SkColorSpace.h"
#include "src/gpu/ganesh/Device_v1.h"
#include "src/gpu/ganesh/GrCaps.h"
#include "src/gpu/ganesh/GrDrawingManager.h"
#include "src/gpu/ganesh/GrProxyProvider.h"
#include "src/gpu/ganesh/GrRenderTargetProxy.h"
#include "src/gpu/ganesh/GrSurfaceProxyView.h"
#include "src/gpu/ganesh/SurfaceDrawContext.h"
#include "src/gpu/ganesh/SurfaceFillContext.h"

void GrRecordingContextPriv::addOnFlushCallbackObject(GrOnFlushCallbackObject* onFlushCBObject) {
    this->context()->addOnFlushCallbackObject(onFlushCBObject);
}

sk_sp<skgpu::v1::Device> GrRecordingContextPriv::createDevice(GrColorType colorType,
                                                              sk_sp<GrSurfaceProxy> proxy,
                                                              sk_sp<SkColorSpace> colorSpace,
                                                              GrSurfaceOrigin origin,
                                                              const SkSurfaceProps& props,
                                                              skgpu::v1::Device::InitContents init) {
    return skgpu::v1::Device::Make(this->context(), colorType, std::move(proxy),
                                   std::move(colorSpace), origin, props, init);
}

sk_sp<skgpu::v1::Device> GrRecordingContextPriv::createDevice(SkBudgeted budgeted,
                                                              const SkImageInfo& ii,
                                                              SkBackingFit fit,
                                                              int sampleCount,
                                                              GrMipmapped mipmapped,
                                                              GrProtected isProtected,
                                                              GrSurfaceOrigin origin,
                                                              const SkSurfaceProps& props,
                                                              skgpu::v1::Device::InitContents init) {
    return skgpu::v1::Device::Make(this->context(), budgeted, ii, fit, sampleCount,
                                   mipmapped, isProtected, origin, props, init);
}

void GrRecordingContextPriv::moveRenderTasksToDDL(SkDeferredDisplayList* ddl) {
    this->context()->drawingManager()->moveRenderTasksToDDL(ddl);
}

sktext::gpu::SDFTControl GrRecordingContextPriv::getSDFTControl(bool useSDFTForSmallText) const {
#if !defined(SK_DISABLE_SDF_TEXT)
    return sktext::gpu::SDFTControl{
            this->caps()->shaderCaps()->supportsDistanceFieldText(),
            useSDFTForSmallText,
            !this->caps()->disablePerspectiveSDFText(),
            this->options().fMinDistanceFieldFontSize,
            this->options().fGlyphsAsPathsFontSize};
#else
    return sktext::gpu::SDFTControl{};
#endif
}

std::unique_ptr<skgpu::v1::SurfaceContext> GrRecordingContextPriv::makeSC(
        GrSurfaceProxyView readView,
        const GrColorInfo& info) {
    // It is probably not necessary to check if the context is abandoned here since uses of the
    // SurfaceContext which need the context will mostly likely fail later on w/o an issue.
    // However having this here adds some reassurance in case there is a path that doesn't
    // handle an abandoned context correctly. It also lets us early out of some extra work.
    if (this->context()->abandoned()) {
        return nullptr;
    }
    GrSurfaceProxy* proxy = readView.proxy();
    SkASSERT(proxy && proxy->asTextureProxy());

    std::unique_ptr<skgpu::v1::SurfaceContext> sc;
    if (proxy->asRenderTargetProxy()) {
        // Will we ever want a swizzle that is not the default write swizzle for the format and
        // colorType here? If so we will need to manually pass that in.
        skgpu::Swizzle writeSwizzle;
        if (info.colorType() != GrColorType::kUnknown) {
            writeSwizzle = this->caps()->getWriteSwizzle(proxy->backendFormat(),
                                                         info.colorType());
        }
        GrSurfaceProxyView writeView(readView.refProxy(), readView.origin(), writeSwizzle);
        if (info.alphaType() == kPremul_SkAlphaType ||
            info.alphaType() == kOpaque_SkAlphaType) {
            sc = std::make_unique<skgpu::v1::SurfaceDrawContext>(this->context(),
                                                                 std::move(readView),
                                                                 std::move(writeView),
                                                                 info.colorType(),
                                                                 info.refColorSpace(),
                                                                 SkSurfaceProps());
        } else {
            sc = std::make_unique<skgpu::v1::SurfaceFillContext>(this->context(),
                                                                 std::move(readView),
                                                                 std::move(writeView),
                                                                 info);
        }
    } else {
        sc = std::make_unique<skgpu::v1::SurfaceContext>(this->context(),
                                                         std::move(readView),
                                                         info);
    }
    SkDEBUGCODE(sc->validate();)
    return sc;
}

std::unique_ptr<skgpu::v1::SurfaceContext> GrRecordingContextPriv::makeSC(
        const GrImageInfo& info,
        const GrBackendFormat& format,
        std::string_view label,
        SkBackingFit fit,
        GrSurfaceOrigin origin,
        GrRenderable renderable,
        int sampleCount,
        GrMipmapped mipmapped,
        GrProtected isProtected,
        SkBudgeted budgeted) {
    SkASSERT(renderable == GrRenderable::kYes || sampleCount == 1);
    if (this->abandoned()) {
        return nullptr;
    }
    sk_sp<GrTextureProxy> proxy =
            this->proxyProvider()->createProxy(format,
                                               info.dimensions(),
                                               renderable,
                                               sampleCount,
                                               mipmapped,
                                               fit,
                                               budgeted,
                                               isProtected,
                                               label);
    if (!proxy) {
        return nullptr;
    }

    skgpu::Swizzle swizzle;
    if (info.colorType() != GrColorType::kUnknown &&
        !this->caps()->isFormatCompressed(format)) {
        swizzle = this->caps()->getReadSwizzle(format, info.colorType());
    }

    GrSurfaceProxyView view(std::move(proxy), origin, swizzle);
    return this->makeSC(std::move(view), info.colorInfo());
}

std::unique_ptr<skgpu::v1::SurfaceFillContext> GrRecordingContextPriv::makeSFC(
        GrImageInfo info,
        std::string_view label,
        SkBackingFit fit,
        int sampleCount,
        GrMipmapped mipmapped,
        GrProtected isProtected,
        GrSurfaceOrigin origin,
        SkBudgeted budgeted) {

    if (info.alphaType() == kPremul_SkAlphaType || info.alphaType() == kOpaque_SkAlphaType) {
        return skgpu::v1::SurfaceDrawContext::Make(this->context(),
                                                   info.colorType(),
                                                   info.refColorSpace(),
                                                   fit,
                                                   info.dimensions(),
                                                   SkSurfaceProps(),
                                                   label,
                                                   sampleCount,
                                                   mipmapped,
                                                   isProtected,
                                                   origin,
                                                   budgeted);
    }
    GrBackendFormat format = this->caps()->getDefaultBackendFormat(info.colorType(),
                                                                   GrRenderable::kYes);
    sk_sp<GrTextureProxy> proxy =
            this->proxyProvider()->createProxy(format,
                                               info.dimensions(),
                                               GrRenderable::kYes,
                                               sampleCount,
                                               mipmapped,
                                               fit,
                                               budgeted,
                                               isProtected,
                                               label);
    if (!proxy) {
        return nullptr;
    }
    skgpu::Swizzle readSwizzle  = this->caps()->getReadSwizzle (format, info.colorType());
    skgpu::Swizzle writeSwizzle = this->caps()->getWriteSwizzle(format, info.colorType());

    GrSurfaceProxyView readView(            proxy, origin,  readSwizzle);
    GrSurfaceProxyView writeView(std::move(proxy), origin, writeSwizzle);
    std::unique_ptr<skgpu::v1::SurfaceFillContext> sfc;
    sfc = std::make_unique<skgpu::v1::SurfaceFillContext>(this->context(),
                                                          std::move(readView),
                                                          std::move(writeView),
                                                          info.colorInfo());
    sfc->discard();
    return sfc;
}

std::unique_ptr<skgpu::v1::SurfaceFillContext> GrRecordingContextPriv::makeSFC(
        SkAlphaType alphaType,
        sk_sp<SkColorSpace> colorSpace,
        SkISize dimensions,
        SkBackingFit fit,
        const GrBackendFormat& format,
        int sampleCount,
        GrMipmapped mipmapped,
        GrProtected isProtected,
        skgpu::Swizzle readSwizzle,
        skgpu::Swizzle writeSwizzle,
        GrSurfaceOrigin origin,
        SkBudgeted budgeted,
        std::string_view label) {

    SkASSERT(!dimensions.isEmpty());
    SkASSERT(sampleCount >= 1);
    SkASSERT(format.isValid() && format.backend() == fContext->backend());
    if (alphaType == kPremul_SkAlphaType || alphaType == kOpaque_SkAlphaType) {
        return skgpu::v1::SurfaceDrawContext::Make(
                this->context(),
                std::move(colorSpace),
                fit,
                dimensions,
                format,
                sampleCount,
                mipmapped,
                isProtected,
                readSwizzle,
                writeSwizzle,
                origin,
                budgeted,
                SkSurfaceProps(),
                label);
    }

    sk_sp<GrTextureProxy> proxy =
            this->proxyProvider()->createProxy(format,
                                               dimensions,
                                               GrRenderable::kYes,
                                               sampleCount,
                                               mipmapped,
                                               fit,
                                               budgeted,
                                               isProtected,
                                               label);
    if (!proxy) {
        return nullptr;
    }
    GrImageInfo info(GrColorType::kUnknown, alphaType, std::move(colorSpace), dimensions);
    GrSurfaceProxyView readView(            proxy, origin,  readSwizzle);
    GrSurfaceProxyView writeView(std::move(proxy), origin, writeSwizzle);
    std::unique_ptr<skgpu::v1::SurfaceFillContext> sfc;
    sfc = std::make_unique<skgpu::v1::SurfaceFillContext>(this->context(),
                                                          std::move(readView),
                                                          std::move(writeView),
                                                          info.colorInfo());
    sfc->discard();
    return sfc;
}

std::unique_ptr<skgpu::v1::SurfaceFillContext> GrRecordingContextPriv::makeSFCWithFallback(
        GrImageInfo info,
        SkBackingFit fit,
        int sampleCount,
        GrMipmapped mipmapped,
        GrProtected isProtected,
        GrSurfaceOrigin origin,
        SkBudgeted budgeted) {

    if (info.alphaType() == kPremul_SkAlphaType || info.alphaType() == kOpaque_SkAlphaType) {
        return skgpu::v1::SurfaceDrawContext::MakeWithFallback(this->context(),
                                                               info.colorType(),
                                                               info.refColorSpace(),
                                                               fit,
                                                               info.dimensions(),
                                                               SkSurfaceProps(),
                                                               sampleCount,
                                                               mipmapped,
                                                               isProtected,
                                                               origin,
                                                               budgeted);
    }
    const GrCaps* caps = this->caps();

    auto [ct, _] = caps->getFallbackColorTypeAndFormat(info.colorType(), sampleCount);
    if (ct == GrColorType::kUnknown) {
        return nullptr;
    }
    info = info.makeColorType(ct);
    return this->makeSFC(info, "MakeSurfaceContextWithFallback",
                         fit,
                         sampleCount,
                         mipmapped,
                         isProtected,
                         origin,
                         budgeted);
}

std::unique_ptr<skgpu::v1::SurfaceFillContext> GrRecordingContextPriv::makeSFCFromBackendTexture(
        GrColorInfo info,
        const GrBackendTexture& tex,
        int sampleCount,
        GrSurfaceOrigin origin,
        sk_sp<skgpu::RefCntedCallback> releaseHelper) {

    SkASSERT(sampleCount > 0);

    if (info.alphaType() == kPremul_SkAlphaType || info.alphaType() == kOpaque_SkAlphaType) {
        return skgpu::v1::SurfaceDrawContext::MakeFromBackendTexture(this->context(),
                                                                     info.colorType(),
                                                                     info.refColorSpace(),
                                                                     tex,
                                                                     sampleCount,
                                                                     origin,
                                                                     SkSurfaceProps(),
                                                                     std::move(releaseHelper));
    }

    if (info.colorType() == GrColorType::kUnknown) {
        return nullptr;
    }

    const GrBackendFormat& format = tex.getBackendFormat();
    if (!this->caps()->areColorTypeAndFormatCompatible(info.colorType(), format)) {
        return nullptr;
    }
    skgpu::Swizzle readSwizzle  = this->caps()->getReadSwizzle (format, info.colorType());
    skgpu::Swizzle writeSwizzle = this->caps()->getWriteSwizzle(format, info.colorType());

    sk_sp<GrTextureProxy> proxy(this->proxyProvider()->wrapRenderableBackendTexture(
            tex, sampleCount, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo,
            std::move(releaseHelper)));
    if (!proxy) {
        return nullptr;
    }

    GrSurfaceProxyView readView(            proxy, origin,  readSwizzle);
    GrSurfaceProxyView writeView(std::move(proxy), origin, writeSwizzle);

    return std::make_unique<skgpu::v1::SurfaceFillContext>(this->context(),
                                                           std::move(readView),
                                                           std::move(writeView),
                                                           std::move(info));
}
