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

#include "GrBuffer.h"
#include "GrGpu.h"
#include "GrCaps.h"

GrBuffer* GrBuffer::CreateCPUBacked(GrGpu* gpu, size_t sizeInBytes, GrBufferType intendedType,
                                    const void* data) {
    SkASSERT(GrBufferTypeIsVertexOrIndex(intendedType));
    void* cpuData;
    if (gpu->caps()->mustClearUploadedBufferData()) {
        cpuData = sk_calloc_throw(sizeInBytes);
    } else {
        cpuData = sk_malloc_flags(sizeInBytes, SK_MALLOC_THROW);
    }
    if (data) {
        memcpy(cpuData, data, sizeInBytes);
    }
    return new GrBuffer(gpu, sizeInBytes, intendedType, cpuData);
}

GrBuffer::GrBuffer(GrGpu* gpu, size_t sizeInBytes, GrBufferType type, void* cpuData)
    : INHERITED(gpu),
      fMapPtr(nullptr),
      fSizeInBytes(sizeInBytes),
      fAccessPattern(kDynamic_GrAccessPattern),
      fCPUData(cpuData),
      fIntendedType(type) {
    this->registerWithCache(SkBudgeted::kNo);
}

GrBuffer::GrBuffer(GrGpu* gpu, size_t sizeInBytes, GrBufferType type, GrAccessPattern pattern)
    : INHERITED(gpu),
      fMapPtr(nullptr),
      fSizeInBytes(sizeInBytes),
      fAccessPattern(pattern),
      fCPUData(nullptr),
      fIntendedType(type) {
    // Subclass registers with cache.
}

void GrBuffer::ComputeScratchKeyForDynamicVBO(size_t size, GrBufferType intendedType,
                                              GrScratchKey* key) {
    static const GrScratchKey::ResourceType kType = GrScratchKey::GenerateResourceType();
    GrScratchKey::Builder builder(key, kType, 1 + (sizeof(size_t) + 3) / 4);
    // TODO: There's not always reason to cache a buffer by type. In some (all?) APIs it's just
    // a chunk of memory we can use/reuse for any type of data. We really only need to
    // differentiate between the "read" types (e.g. kGpuToCpu_BufferType) and "draw" types.
    builder[0] = intendedType;
    builder[1] = (uint32_t)size;
    if (sizeof(size_t) > 4) {
        builder[2] = (uint32_t)((uint64_t)size >> 32);
    }
}

bool GrBuffer::onUpdateData(const void* src, size_t srcSizeInBytes) {
    SkASSERT(this->isCPUBacked());
    memcpy(fCPUData, src, srcSizeInBytes);
    return true;
}

void GrBuffer::computeScratchKey(GrScratchKey* key) const {
    if (!this->isCPUBacked() && SkIsPow2(fSizeInBytes) &&
        kDynamic_GrAccessPattern == fAccessPattern) {
        ComputeScratchKeyForDynamicVBO(fSizeInBytes, fIntendedType, key);
    }
}