/*
 * Copyright 2024 Google LLC
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "include/gpu/graphite/PrecompileContext.h"

#include "include/core/SkTypes.h"
#include "src/gpu/GpuTypesPriv.h"
#include "src/gpu/graphite/GlobalCache.h"
#include "src/gpu/graphite/ResourceProvider.h"
#include "src/gpu/graphite/SerializationUtils.h"
#include "src/gpu/graphite/SharedContext.h"

#if defined(SK_ENABLE_PRECOMPILE)
#include "src/gpu/graphite/ContextUtils.h"
#include "src/gpu/graphite/GraphicsPipelineDesc.h"
#include "src/gpu/graphite/GraphicsPipelineHandle.h"
#include "src/gpu/graphite/Log.h"
#include "src/gpu/graphite/PipelineCreationTask.h"
#include "src/gpu/graphite/RenderPassDesc.h"
#include "src/gpu/graphite/RendererProvider.h"
#include "src/gpu/graphite/RuntimeEffectDictionary.h"
#endif

#include <cstddef>

namespace skgpu::graphite {

#define ASSERT_SINGLE_OWNER SKGPU_ASSERT_SINGLE_OWNER(&fSingleOwner)

PrecompileContext::~PrecompileContext() {
    ASSERT_SINGLE_OWNER
}

PrecompileContext::PrecompileContext(sk_sp<SharedContext> sharedContext)
    : fSharedContext(sharedContext) {

    // ResourceProviders are not thread-safe. Here we create a ResourceProvider
    // specifically for the thread on which precompilation will occur.
    static constexpr size_t kEmptyBudget = 0;
    fResourceProvider =
            fSharedContext->makeResourceProvider(&fSingleOwner, SK_InvalidGenID, kEmptyBudget);
}

void PrecompileContext::purgePipelinesNotUsedInMs(std::chrono::milliseconds msNotUsed) {
    ASSERT_SINGLE_OWNER

    auto purgeTime = skgpu::StdSteadyClock::now() - msNotUsed;

    fSharedContext->globalCache()->purgePipelinesNotUsedSince(purgeTime);
}

void PrecompileContext::reportPipelineStats(StatOptions option) {
    ASSERT_SINGLE_OWNER

    if (option == StatOptions::kPrecompile) {
        fSharedContext->globalCache()->reportPrecompileStats();
    } else {
        fSharedContext->globalCache()->reportCacheStats();
    }
}

bool PrecompileContext::precompile(sk_sp<SkData> serializedPipelineKey) {
#if defined(SK_ENABLE_PRECOMPILE)
    sk_sp<RuntimeEffectDictionary> rtEffectDict = sk_make_sp<RuntimeEffectDictionary>();
    const Caps* caps = fSharedContext->caps();

    GraphicsPipelineDesc pipelineDesc;
    RenderPassDesc renderPassDesc;

    if (!DataToPipelineDesc(caps,
                            fSharedContext->shaderCodeDictionary(),
                            serializedPipelineKey.get(),
                            &pipelineDesc,
                            &renderPassDesc)) {
        return false;
    }

    GraphicsPipelineHandle handle = fResourceProvider->createGraphicsPipelineHandle(
            pipelineDesc,
            renderPassDesc,
            PipelineCreationFlags::kForPrecompilation);
    fResourceProvider->startPipelineCreationTask(rtEffectDict, handle);

    return true;
#else
    return false;
#endif
}

std::string PrecompileContext::getPipelineLabel(sk_sp<SkData> serializedPipelineKey) {
#if defined(SK_ENABLE_PRECOMPILE)
    GraphicsPipelineDesc pipelineDesc;
    RenderPassDesc renderPassDesc;

    if (!DataToPipelineDesc(fSharedContext->caps(),
                            fSharedContext->shaderCodeDictionary(),
                            serializedPipelineKey.get(),
                            &pipelineDesc,
                            &renderPassDesc)) {
        return "";
    }

    const RendererProvider* rendererProvider = fSharedContext->rendererProvider();

    const RenderStep* renderStep = rendererProvider->lookup(pipelineDesc.renderStepID());
    if (!renderStep) {
        return "";
    }

    return GetPipelineLabel(fSharedContext->caps(),
                            fSharedContext->shaderCodeDictionary(),
                            renderPassDesc,
                            renderStep,
                            pipelineDesc.paintParamsID());
#else
    return "";
#endif
}


} // namespace skgpu::graphite
