/*
 * Copyright 2020 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/GrStagingBufferManager.h"

#include "include/gpu/GrDirectContext.h"
#include "src/gpu/GrContextPriv.h"
#include "src/gpu/GrGpu.h"
#include "src/gpu/GrResourceProvider.h"

GrStagingBufferManager::Slice GrStagingBufferManager::allocateStagingBufferSlice(
        size_t size, size_t requiredAlignment) {
    StagingBuffer* buffer = nullptr;
    size_t offset = 0;
    for (size_t i = 0; i < fBuffers.size(); ++i) {
        size_t totalBufferSize = fBuffers[i].fBuffer->size();
        size_t currentOffset = fBuffers[i].fOffset;
        offset = GrAlignTo(currentOffset, requiredAlignment);
        if (totalBufferSize - offset >= size) {
            buffer = &fBuffers[i];
            break;
        }
    }

    if (!buffer) {
        GrResourceProvider* resourceProvider = fGpu->getContext()->priv().resourceProvider();
        size_t bufferSize = std::max(size, kMinStagingBufferSize);
        sk_sp<GrGpuBuffer> newBuffer = resourceProvider->createBuffer(
            bufferSize, GrGpuBufferType::kXferCpuToGpu, kDynamic_GrAccessPattern, nullptr);
        if (!newBuffer) {
            return {}; // invalid slice
        }
        void* mapPtr = newBuffer->map();
        if (!mapPtr) {
            return {}; // invalid slice
        }
        fBuffers.emplace_back(std::move(newBuffer), mapPtr);
        buffer = &fBuffers.back();
        offset = 0;
    }

    SkASSERT(buffer);
    SkASSERT(buffer->remaining() >= size);

    buffer->fOffset = offset + size;
    char* offsetMapPtr = static_cast<char*>(buffer->fMapPtr) + offset;
    return {buffer->fBuffer.get(), offset, offsetMapPtr};
}

void GrStagingBufferManager::detachBuffers() {
    for (size_t i = 0; i < fBuffers.size(); ++i) {
        fBuffers[i].fBuffer->unmap();
        fGpu->takeOwnershipOfBuffer(std::move(fBuffers[i].fBuffer));
    }
    fBuffers.clear();
}
