/*
 * Copyright 2022 Google LLC
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "src/gpu/graphite/PipelineData.h"

#include "src/core/SkChecksum.h"
#include "src/gpu/graphite/ShaderCodeDictionary.h"

namespace skgpu::graphite {

PipelineDataGatherer::PipelineDataGatherer(const Caps* caps, Layout layout)
        : fCaps(caps), fUniformManager(layout) {}

void PipelineDataGatherer::resetWithNewLayout(Layout layout) {
    fUniformManager.resetWithNewLayout(layout);
    fTextureDataBlock.reset();
}

#ifdef SK_DEBUG
void PipelineDataGatherer::checkReset() {
    SkASSERT(fTextureDataBlock.empty());
    SkASSERT(fUniformManager.isReset());
}

void PipelineDataGatherer::setExpectedUniforms(SkSpan<const Uniform> expectedUniforms) {
    fUniformManager.setExpectedUniforms(expectedUniforms);
}
#endif // SK_DEBUG

////////////////////////////////////////////////////////////////////////////////////////////////////
UniformDataBlock* UniformDataBlock::Make(const UniformDataBlock& other, SkArenaAlloc* arena) {
    static constexpr size_t kUniformAlignment = alignof(void*);
    char* mem = static_cast<char*>(arena->makeBytesAlignedTo(other.size(), kUniformAlignment));
    memcpy(mem, other.data(), other.size());

    return arena->make([&](void* ptr) {
        return new (ptr) UniformDataBlock(SkSpan<const char>(mem, other.size()));
    });
}

uint32_t UniformDataBlock::hash() const {
    return SkChecksum::Hash32(fData.data(), fData.size());
}

////////////////////////////////////////////////////////////////////////////////////////////////////
TextureDataBlock* TextureDataBlock::Make(const TextureDataBlock& other,
                                             SkArenaAlloc* arena) {
    return arena->make([&](void *ptr) {
        return new (ptr) TextureDataBlock(other);
    });
}

bool TextureDataBlock::operator==(const TextureDataBlock& other) const {
    if (fTextureData.size() != other.fTextureData.size()) {
        return false;
    }

    for (size_t i = 0; i < fTextureData.size(); ++i) {
        if (fTextureData[i] != other.fTextureData[i]) {
            return false;
        }
    }

    return true;
}

uint32_t TextureDataBlock::hash() const {
    uint32_t hash = 0;

    for (auto& d : fTextureData) {
        SamplerDesc samplerKey = std::get<1>(d);
        hash = SkChecksum::Hash32(&samplerKey, sizeof(samplerKey), hash);

        // Because the lifetime of the TextureDataCache is for just one Recording and the
        // TextureDataBlocks hold refs on their proxies, we can just use the proxy's pointer
        // for the hash here.
        uintptr_t proxy = reinterpret_cast<uintptr_t>(std::get<0>(d).get());
        hash = SkChecksum::Hash32(&proxy, sizeof(proxy), hash);
    }

    return hash;
}

#ifdef SK_DEBUG
UniformExpectationsValidator::UniformExpectationsValidator(PipelineDataGatherer *gatherer,
                                                           SkSpan<const Uniform> expectedUniforms)
        : fGatherer(gatherer) {
    fGatherer->setExpectedUniforms(expectedUniforms);
}
#endif

} // namespace skgpu::graphite
