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

#include "src/gpu/GrCaps.h"
#include "src/gpu/GrDrawingManager.h"
#include "src/gpu/GrProxyProvider.h"
#include "src/gpu/GrRenderTargetProxy.h"
#include "src/gpu/GrSurfaceProxyView.h"

#if SK_GPU_V1
#include "src/gpu/v1/Device_v1.h"
#include "src/gpu/v1/SurfaceDrawContext_v1.h"
#include "src/gpu/v1/SurfaceFillContext_v1.h"
#endif
#if SK_GPU_V2
#include "src/gpu/v2/Device_v2.h"
#include "src/gpu/v2/SurfaceDrawContext_v2.h"
#include "src/gpu/v2/SurfaceFillContext_v2.h"
#endif

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

sk_sp<skgpu::BaseDevice> GrRecordingContextPriv::createDevice(GrColorType colorType,
                                                              sk_sp<GrSurfaceProxy> proxy,
                                                              sk_sp<SkColorSpace> colorSpace,
                                                              GrSurfaceOrigin origin,
                                                              const SkSurfaceProps& props,
                                                              skgpu::BaseDevice::InitContents init) {
#if GR_TEST_UTILS
    if (this->options().fUseSkGpuV2 == GrContextOptions::Enable::kYes) {
#if SK_GPU_V2
        return skgpu::v2::Device::Make(this->context(), colorType, std::move(proxy),
                                       std::move(colorSpace), origin, props, init);
#else
        return nullptr;
#endif // GR_TEST_UTILS
    } else
#endif
    {
#if SK_GPU_V1
        return skgpu::v1::Device::Make(this->context(), colorType, std::move(proxy),
                                       std::move(colorSpace), origin, props, init);
#else
        return nullptr;
#endif
    }
}

sk_sp<skgpu::BaseDevice> GrRecordingContextPriv::createDevice(SkBudgeted budgeted,
                                                              const SkImageInfo& ii,
                                                              SkBackingFit fit,
                                                              int sampleCount,
                                                              GrMipmapped mipmapped,
                                                              GrProtected isProtected,
                                                              GrSurfaceOrigin origin,
                                                              const SkSurfaceProps& props,
                                                              skgpu::BaseDevice::InitContents init) {
#if GR_TEST_UTILS
    if (this->options().fUseSkGpuV2 == GrContextOptions::Enable::kYes) {
#if SK_GPU_V2
        return skgpu::v2::Device::Make(this->context(), budgeted, ii, fit, sampleCount,
                                       mipmapped, isProtected, origin, props, init);
#else
        return nullptr;
#endif
    } else
#endif // GR_TEST_UTILS
    {
#if SK_GPU_V1
        return skgpu::v1::Device::Make(this->context(), budgeted, ii, fit, sampleCount,
                                       mipmapped, isProtected, origin, props, init);
#else
        return nullptr;
#endif
    }
}

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

GrSDFTControl GrRecordingContextPriv::getSDFTControl(bool useSDFTForSmallText) const {
    return GrSDFTControl{
            this->caps()->shaderCaps()->supportsDistanceFieldText(),
            useSDFTForSmallText,
            this->options().fMinDistanceFieldFontSize,
            this->options().fGlyphsAsPathsFontSize};
}

std::unique_ptr<skgpu::SurfaceContext> GrRecordingContextPriv::makeSC(GrSurfaceProxyView readView,
                                                                      const GrColorInfo& info) {
#if GR_TEST_UTILS
    if (this->options().fUseSkGpuV2 == GrContextOptions::Enable::kYes) {
#if SK_GPU_V2
        return nullptr;
#endif
    } else
#endif // GR_TEST_UTILS
    {
#if SK_GPU_V1
        // 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::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.
            GrSwizzle 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::SurfaceContext>(this->context(),
                                                         std::move(readView),
                                                         info);
        }
        SkDEBUGCODE(sc->validate();)
        return sc;
#endif
    }

    return nullptr;
}

std::unique_ptr<skgpu::SurfaceContext> GrRecordingContextPriv::makeSC(const GrImageInfo& info,
                                                                      const GrBackendFormat& format,
                                                                      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);
    if (!proxy) {
        return nullptr;
    }

    GrSwizzle 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::SurfaceFillContext> GrRecordingContextPriv::makeSFC(GrImageInfo info,
                                                                           SkBackingFit fit,
                                                                           int sampleCount,
                                                                           GrMipmapped mipmapped,
                                                                           GrProtected isProtected,
                                                                           GrSurfaceOrigin origin,
                                                                           SkBudgeted budgeted) {

#if GR_TEST_UTILS
    if (this->options().fUseSkGpuV2 == GrContextOptions::Enable::kYes) {
#if SK_GPU_V2
        return nullptr;
#endif
    } else
#endif // GR_TEST_UTILS
    {
#if SK_GPU_V1
        if (info.alphaType() == kPremul_SkAlphaType || info.alphaType() == kOpaque_SkAlphaType) {
            return skgpu::v1::SurfaceDrawContext::Make(this->context(),
                                                       info.colorType(),
                                                       info.refColorSpace(),
                                                       fit,
                                                       info.dimensions(),
                                                       SkSurfaceProps(),
                                                       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);
        if (!proxy) {
            return nullptr;
        }
        GrSwizzle readSwizzle  = this->caps()->getReadSwizzle (format, info.colorType());
        GrSwizzle writeSwizzle = this->caps()->getWriteSwizzle(format, info.colorType());

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

    return nullptr;
}

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

#if GR_TEST_UTILS
    if (this->context()->options().fUseSkGpuV2 == GrContextOptions::Enable::kYes) {
#if SK_GPU_V2
        return nullptr;
#endif
    } else
#endif // GR_TEST_UTILS
    {
#if SK_GPU_V1
        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());
        }

        sk_sp<GrTextureProxy> proxy = this->proxyProvider()->createProxy(format,
                                                                         dimensions,
                                                                         GrRenderable::kYes,
                                                                         sampleCount,
                                                                         mipmapped,
                                                                         fit,
                                                                         budgeted,
                                                                         isProtected);
        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::SurfaceFillContext> sfc;
        sfc = std::make_unique<skgpu::v1::SurfaceFillContext>(this->context(),
                                                              std::move(readView),
                                                              std::move(writeView),
                                                              info.colorInfo());
        sfc->discard();
        return sfc;
#endif
    }

    return nullptr;
}

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

#if GR_TEST_UTILS
    if (this->options().fUseSkGpuV2 == GrContextOptions::Enable::kYes) {
#if SK_GPU_V2
        return nullptr;
#endif
    } else
#endif // GR_TEST_UTILS
    {
#if SK_GPU_V1
        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,
                             fit,
                             sampleCount,
                             mipmapped,
                             isProtected,
                             origin,
                             budgeted);
#endif
    }

    return nullptr;
}

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

#if GR_TEST_UTILS
    if (this->options().fUseSkGpuV2 == GrContextOptions::Enable::kYes) {
#if SK_GPU_V2
        return nullptr;
#endif
    } else
#endif // GR_TEST_UTILS
    {
#if SK_GPU_V1
        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));
        }
        const GrBackendFormat& format = tex.getBackendFormat();
        GrSwizzle readSwizzle, writeSwizzle;
        if (info.colorType() != GrColorType::kUnknown) {
            if (!this->caps()->areColorTypeAndFormatCompatible(info.colorType(), format)) {
                return nullptr;
            }
            readSwizzle  = this->caps()->getReadSwizzle (format, info.colorType());
            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));
#endif
    }

    return nullptr;
}
