50dbc0949f
Change-Id: I4c9a2d81a1bc4ccebc78eea56c0de116b98d415e Reviewed-on: https://skia-review.googlesource.com/134330 Commit-Queue: Hal Canary <halcanary@google.com> Auto-Submit: Hal Canary <halcanary@google.com> Reviewed-by: Ben Wagner <bungeman@google.com>
486 lines
16 KiB
C++
486 lines
16 KiB
C++
/*
|
|
* Copyright 2010 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "GrBufferAllocPool.h"
|
|
|
|
#include "GrBuffer.h"
|
|
#include "GrCaps.h"
|
|
#include "GrContext.h"
|
|
#include "GrContextPriv.h"
|
|
#include "GrGpu.h"
|
|
#include "GrResourceProvider.h"
|
|
#include "GrTypes.h"
|
|
#include "SkMacros.h"
|
|
#include "SkSafeMath.h"
|
|
#include "SkTraceEvent.h"
|
|
|
|
#ifdef SK_DEBUG
|
|
#define VALIDATE validate
|
|
#else
|
|
static void VALIDATE(bool = false) {}
|
|
#endif
|
|
|
|
static const size_t MIN_VERTEX_BUFFER_SIZE = 1 << 15;
|
|
static const size_t MIN_INDEX_BUFFER_SIZE = 1 << 12;
|
|
|
|
// page size
|
|
#define GrBufferAllocPool_MIN_BLOCK_SIZE ((size_t)1 << 15)
|
|
|
|
#define UNMAP_BUFFER(block) \
|
|
do { \
|
|
TRACE_EVENT_INSTANT1("skia.gpu", \
|
|
"GrBufferAllocPool Unmapping Buffer", \
|
|
TRACE_EVENT_SCOPE_THREAD, \
|
|
"percent_unwritten", \
|
|
(float)((block).fBytesFree) / (block).fBuffer->gpuMemorySize()); \
|
|
(block).fBuffer->unmap(); \
|
|
} while (false)
|
|
|
|
GrBufferAllocPool::GrBufferAllocPool(GrGpu* gpu, GrBufferType bufferType, size_t blockSize)
|
|
: fBlocks(8) {
|
|
|
|
fGpu = SkRef(gpu);
|
|
fCpuData = nullptr;
|
|
fBufferType = bufferType;
|
|
fBufferPtr = nullptr;
|
|
fMinBlockSize = SkTMax(GrBufferAllocPool_MIN_BLOCK_SIZE, blockSize);
|
|
|
|
fBytesInUse = 0;
|
|
|
|
fBufferMapThreshold = gpu->caps()->bufferMapThreshold();
|
|
}
|
|
|
|
void GrBufferAllocPool::deleteBlocks() {
|
|
if (fBlocks.count()) {
|
|
GrBuffer* buffer = fBlocks.back().fBuffer;
|
|
if (buffer->isMapped()) {
|
|
UNMAP_BUFFER(fBlocks.back());
|
|
}
|
|
}
|
|
while (!fBlocks.empty()) {
|
|
this->destroyBlock();
|
|
}
|
|
SkASSERT(!fBufferPtr);
|
|
}
|
|
|
|
GrBufferAllocPool::~GrBufferAllocPool() {
|
|
VALIDATE();
|
|
this->deleteBlocks();
|
|
sk_free(fCpuData);
|
|
fGpu->unref();
|
|
}
|
|
|
|
void GrBufferAllocPool::reset() {
|
|
VALIDATE();
|
|
fBytesInUse = 0;
|
|
this->deleteBlocks();
|
|
this->resetCpuData(0); // delete all the cpu-side memory
|
|
VALIDATE();
|
|
}
|
|
|
|
void GrBufferAllocPool::unmap() {
|
|
VALIDATE();
|
|
|
|
if (fBufferPtr) {
|
|
BufferBlock& block = fBlocks.back();
|
|
if (block.fBuffer->isMapped()) {
|
|
UNMAP_BUFFER(block);
|
|
} else {
|
|
size_t flushSize = block.fBuffer->gpuMemorySize() - block.fBytesFree;
|
|
this->flushCpuData(fBlocks.back(), flushSize);
|
|
}
|
|
fBufferPtr = nullptr;
|
|
}
|
|
VALIDATE();
|
|
}
|
|
|
|
#ifdef SK_DEBUG
|
|
void GrBufferAllocPool::validate(bool unusedBlockAllowed) const {
|
|
bool wasDestroyed = false;
|
|
if (fBufferPtr) {
|
|
SkASSERT(!fBlocks.empty());
|
|
if (fBlocks.back().fBuffer->isMapped()) {
|
|
GrBuffer* buf = fBlocks.back().fBuffer;
|
|
SkASSERT(buf->mapPtr() == fBufferPtr);
|
|
} else {
|
|
SkASSERT(fCpuData == fBufferPtr);
|
|
}
|
|
} else {
|
|
SkASSERT(fBlocks.empty() || !fBlocks.back().fBuffer->isMapped());
|
|
}
|
|
size_t bytesInUse = 0;
|
|
for (int i = 0; i < fBlocks.count() - 1; ++i) {
|
|
SkASSERT(!fBlocks[i].fBuffer->isMapped());
|
|
}
|
|
for (int i = 0; !wasDestroyed && i < fBlocks.count(); ++i) {
|
|
if (fBlocks[i].fBuffer->wasDestroyed()) {
|
|
wasDestroyed = true;
|
|
} else {
|
|
size_t bytes = fBlocks[i].fBuffer->gpuMemorySize() - fBlocks[i].fBytesFree;
|
|
bytesInUse += bytes;
|
|
SkASSERT(bytes || unusedBlockAllowed);
|
|
}
|
|
}
|
|
|
|
if (!wasDestroyed) {
|
|
SkASSERT(bytesInUse == fBytesInUse);
|
|
if (unusedBlockAllowed) {
|
|
SkASSERT((fBytesInUse && !fBlocks.empty()) ||
|
|
(!fBytesInUse && (fBlocks.count() < 2)));
|
|
} else {
|
|
SkASSERT((0 == fBytesInUse) == fBlocks.empty());
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void* GrBufferAllocPool::makeSpace(size_t size,
|
|
size_t alignment,
|
|
const GrBuffer** buffer,
|
|
size_t* offset) {
|
|
VALIDATE();
|
|
|
|
SkASSERT(buffer);
|
|
SkASSERT(offset);
|
|
|
|
if (fBufferPtr) {
|
|
BufferBlock& back = fBlocks.back();
|
|
size_t usedBytes = back.fBuffer->gpuMemorySize() - back.fBytesFree;
|
|
size_t pad = GrSizeAlignUpPad(usedBytes, alignment);
|
|
if ((size + pad) <= back.fBytesFree) {
|
|
memset((void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes), 0, pad);
|
|
usedBytes += pad;
|
|
*offset = usedBytes;
|
|
*buffer = back.fBuffer;
|
|
back.fBytesFree -= size + pad;
|
|
fBytesInUse += size + pad;
|
|
VALIDATE();
|
|
return (void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes);
|
|
}
|
|
}
|
|
|
|
// We could honor the space request using by a partial update of the current
|
|
// VB (if there is room). But we don't currently use draw calls to GL that
|
|
// allow the driver to know that previously issued draws won't read from
|
|
// the part of the buffer we update. Also, the GL buffer implementation
|
|
// may be cheating on the actual buffer size by shrinking the buffer on
|
|
// updateData() if the amount of data passed is less than the full buffer
|
|
// size.
|
|
|
|
if (!this->createBlock(size)) {
|
|
return nullptr;
|
|
}
|
|
SkASSERT(fBufferPtr);
|
|
|
|
*offset = 0;
|
|
BufferBlock& back = fBlocks.back();
|
|
*buffer = back.fBuffer;
|
|
back.fBytesFree -= size;
|
|
fBytesInUse += size;
|
|
VALIDATE();
|
|
return fBufferPtr;
|
|
}
|
|
|
|
void* GrBufferAllocPool::makeSpaceAtLeast(size_t minSize,
|
|
size_t fallbackSize,
|
|
size_t alignment,
|
|
const GrBuffer** buffer,
|
|
size_t* offset,
|
|
size_t* actualSize) {
|
|
VALIDATE();
|
|
|
|
SkASSERT(buffer);
|
|
SkASSERT(offset);
|
|
SkASSERT(actualSize);
|
|
|
|
if (fBufferPtr) {
|
|
BufferBlock& back = fBlocks.back();
|
|
size_t usedBytes = back.fBuffer->gpuMemorySize() - back.fBytesFree;
|
|
size_t pad = GrSizeAlignUpPad(usedBytes, alignment);
|
|
if ((minSize + pad) <= back.fBytesFree) {
|
|
// Consume padding first, to make subsequent alignment math easier
|
|
memset((void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes), 0, pad);
|
|
usedBytes += pad;
|
|
back.fBytesFree -= pad;
|
|
fBytesInUse += pad;
|
|
|
|
// Give caller all remaining space in this block (but aligned correctly)
|
|
size_t size = GrSizeAlignDown(back.fBytesFree, alignment);
|
|
*offset = usedBytes;
|
|
*buffer = back.fBuffer;
|
|
*actualSize = size;
|
|
back.fBytesFree -= size;
|
|
fBytesInUse += size;
|
|
VALIDATE();
|
|
return (void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes);
|
|
}
|
|
}
|
|
|
|
// We could honor the space request using by a partial update of the current
|
|
// VB (if there is room). But we don't currently use draw calls to GL that
|
|
// allow the driver to know that previously issued draws won't read from
|
|
// the part of the buffer we update. Also, the GL buffer implementation
|
|
// may be cheating on the actual buffer size by shrinking the buffer on
|
|
// updateData() if the amount of data passed is less than the full buffer
|
|
// size.
|
|
|
|
if (!this->createBlock(fallbackSize)) {
|
|
return nullptr;
|
|
}
|
|
SkASSERT(fBufferPtr);
|
|
|
|
*offset = 0;
|
|
BufferBlock& back = fBlocks.back();
|
|
*buffer = back.fBuffer;
|
|
*actualSize = fallbackSize;
|
|
back.fBytesFree -= fallbackSize;
|
|
fBytesInUse += fallbackSize;
|
|
VALIDATE();
|
|
return fBufferPtr;
|
|
}
|
|
|
|
void GrBufferAllocPool::putBack(size_t bytes) {
|
|
VALIDATE();
|
|
|
|
while (bytes) {
|
|
// caller shouldn't try to put back more than they've taken
|
|
SkASSERT(!fBlocks.empty());
|
|
BufferBlock& block = fBlocks.back();
|
|
size_t bytesUsed = block.fBuffer->gpuMemorySize() - block.fBytesFree;
|
|
if (bytes >= bytesUsed) {
|
|
bytes -= bytesUsed;
|
|
fBytesInUse -= bytesUsed;
|
|
// if we locked a vb to satisfy the make space and we're releasing
|
|
// beyond it, then unmap it.
|
|
if (block.fBuffer->isMapped()) {
|
|
UNMAP_BUFFER(block);
|
|
}
|
|
this->destroyBlock();
|
|
} else {
|
|
block.fBytesFree += bytes;
|
|
fBytesInUse -= bytes;
|
|
bytes = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
VALIDATE();
|
|
}
|
|
|
|
bool GrBufferAllocPool::createBlock(size_t requestSize) {
|
|
|
|
size_t size = SkTMax(requestSize, fMinBlockSize);
|
|
SkASSERT(size >= GrBufferAllocPool_MIN_BLOCK_SIZE);
|
|
|
|
VALIDATE();
|
|
|
|
BufferBlock& block = fBlocks.push_back();
|
|
|
|
block.fBuffer = this->getBuffer(size);
|
|
if (!block.fBuffer) {
|
|
fBlocks.pop_back();
|
|
return false;
|
|
}
|
|
|
|
block.fBytesFree = block.fBuffer->gpuMemorySize();
|
|
if (fBufferPtr) {
|
|
SkASSERT(fBlocks.count() > 1);
|
|
BufferBlock& prev = fBlocks.fromBack(1);
|
|
if (prev.fBuffer->isMapped()) {
|
|
UNMAP_BUFFER(prev);
|
|
} else {
|
|
this->flushCpuData(prev, prev.fBuffer->gpuMemorySize() - prev.fBytesFree);
|
|
}
|
|
fBufferPtr = nullptr;
|
|
}
|
|
|
|
SkASSERT(!fBufferPtr);
|
|
|
|
// If the buffer is CPU-backed we map it because it is free to do so and saves a copy.
|
|
// Otherwise when buffer mapping is supported we map if the buffer size is greater than the
|
|
// threshold.
|
|
bool attemptMap = block.fBuffer->isCPUBacked();
|
|
if (!attemptMap && GrCaps::kNone_MapFlags != fGpu->caps()->mapBufferFlags()) {
|
|
attemptMap = size > fBufferMapThreshold;
|
|
}
|
|
|
|
if (attemptMap) {
|
|
fBufferPtr = block.fBuffer->map();
|
|
}
|
|
|
|
if (!fBufferPtr) {
|
|
fBufferPtr = this->resetCpuData(block.fBytesFree);
|
|
}
|
|
|
|
VALIDATE(true);
|
|
|
|
return true;
|
|
}
|
|
|
|
void GrBufferAllocPool::destroyBlock() {
|
|
SkASSERT(!fBlocks.empty());
|
|
|
|
BufferBlock& block = fBlocks.back();
|
|
|
|
SkASSERT(!block.fBuffer->isMapped());
|
|
block.fBuffer->unref();
|
|
fBlocks.pop_back();
|
|
fBufferPtr = nullptr;
|
|
}
|
|
|
|
void* GrBufferAllocPool::resetCpuData(size_t newSize) {
|
|
sk_free(fCpuData);
|
|
if (newSize) {
|
|
if (fGpu->caps()->mustClearUploadedBufferData()) {
|
|
fCpuData = sk_calloc_throw(newSize);
|
|
} else {
|
|
fCpuData = sk_malloc_throw(newSize);
|
|
}
|
|
} else {
|
|
fCpuData = nullptr;
|
|
}
|
|
return fCpuData;
|
|
}
|
|
|
|
|
|
void GrBufferAllocPool::flushCpuData(const BufferBlock& block, size_t flushSize) {
|
|
GrBuffer* buffer = block.fBuffer;
|
|
SkASSERT(buffer);
|
|
SkASSERT(!buffer->isMapped());
|
|
SkASSERT(fCpuData == fBufferPtr);
|
|
SkASSERT(flushSize <= buffer->gpuMemorySize());
|
|
VALIDATE(true);
|
|
|
|
if (GrCaps::kNone_MapFlags != fGpu->caps()->mapBufferFlags() &&
|
|
flushSize > fBufferMapThreshold) {
|
|
void* data = buffer->map();
|
|
if (data) {
|
|
memcpy(data, fBufferPtr, flushSize);
|
|
UNMAP_BUFFER(block);
|
|
return;
|
|
}
|
|
}
|
|
buffer->updateData(fBufferPtr, flushSize);
|
|
VALIDATE(true);
|
|
}
|
|
|
|
GrBuffer* GrBufferAllocPool::getBuffer(size_t size) {
|
|
|
|
auto resourceProvider = fGpu->getContext()->contextPriv().resourceProvider();
|
|
|
|
// Shouldn't have to use this flag (https://bug.skia.org/4156)
|
|
static const uint32_t kFlags = GrResourceProvider::kNoPendingIO_Flag;
|
|
return resourceProvider->createBuffer(size, fBufferType, kDynamic_GrAccessPattern, kFlags);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
GrVertexBufferAllocPool::GrVertexBufferAllocPool(GrGpu* gpu)
|
|
: GrBufferAllocPool(gpu, kVertex_GrBufferType, MIN_VERTEX_BUFFER_SIZE) {
|
|
}
|
|
|
|
void* GrVertexBufferAllocPool::makeSpace(size_t vertexSize,
|
|
int vertexCount,
|
|
const GrBuffer** buffer,
|
|
int* startVertex) {
|
|
|
|
SkASSERT(vertexCount >= 0);
|
|
SkASSERT(buffer);
|
|
SkASSERT(startVertex);
|
|
|
|
size_t offset SK_INIT_TO_AVOID_WARNING;
|
|
void* ptr = INHERITED::makeSpace(SkSafeMath::Mul(vertexSize, vertexCount),
|
|
vertexSize,
|
|
buffer,
|
|
&offset);
|
|
|
|
SkASSERT(0 == offset % vertexSize);
|
|
*startVertex = static_cast<int>(offset / vertexSize);
|
|
return ptr;
|
|
}
|
|
|
|
void* GrVertexBufferAllocPool::makeSpaceAtLeast(size_t vertexSize, int minVertexCount,
|
|
int fallbackVertexCount, const GrBuffer** buffer,
|
|
int* startVertex, int* actualVertexCount) {
|
|
|
|
SkASSERT(minVertexCount >= 0);
|
|
SkASSERT(fallbackVertexCount >= minVertexCount);
|
|
SkASSERT(buffer);
|
|
SkASSERT(startVertex);
|
|
SkASSERT(actualVertexCount);
|
|
|
|
size_t offset SK_INIT_TO_AVOID_WARNING;
|
|
size_t actualSize SK_INIT_TO_AVOID_WARNING;
|
|
void* ptr = INHERITED::makeSpaceAtLeast(SkSafeMath::Mul(vertexSize, minVertexCount),
|
|
SkSafeMath::Mul(vertexSize, fallbackVertexCount),
|
|
vertexSize,
|
|
buffer,
|
|
&offset,
|
|
&actualSize);
|
|
|
|
SkASSERT(0 == offset % vertexSize);
|
|
*startVertex = static_cast<int>(offset / vertexSize);
|
|
|
|
SkASSERT(0 == actualSize % vertexSize);
|
|
SkASSERT(actualSize >= vertexSize * minVertexCount);
|
|
*actualVertexCount = static_cast<int>(actualSize / vertexSize);
|
|
|
|
return ptr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
GrIndexBufferAllocPool::GrIndexBufferAllocPool(GrGpu* gpu)
|
|
: GrBufferAllocPool(gpu, kIndex_GrBufferType, MIN_INDEX_BUFFER_SIZE) {
|
|
}
|
|
|
|
void* GrIndexBufferAllocPool::makeSpace(int indexCount,
|
|
const GrBuffer** buffer,
|
|
int* startIndex) {
|
|
|
|
SkASSERT(indexCount >= 0);
|
|
SkASSERT(buffer);
|
|
SkASSERT(startIndex);
|
|
|
|
size_t offset SK_INIT_TO_AVOID_WARNING;
|
|
void* ptr = INHERITED::makeSpace(SkSafeMath::Mul(indexCount, sizeof(uint16_t)),
|
|
sizeof(uint16_t),
|
|
buffer,
|
|
&offset);
|
|
|
|
SkASSERT(0 == offset % sizeof(uint16_t));
|
|
*startIndex = static_cast<int>(offset / sizeof(uint16_t));
|
|
return ptr;
|
|
}
|
|
|
|
void* GrIndexBufferAllocPool::makeSpaceAtLeast(int minIndexCount, int fallbackIndexCount,
|
|
const GrBuffer** buffer, int* startIndex,
|
|
int* actualIndexCount) {
|
|
SkASSERT(minIndexCount >= 0);
|
|
SkASSERT(fallbackIndexCount >= minIndexCount);
|
|
SkASSERT(buffer);
|
|
SkASSERT(startIndex);
|
|
SkASSERT(actualIndexCount);
|
|
|
|
size_t offset SK_INIT_TO_AVOID_WARNING;
|
|
size_t actualSize SK_INIT_TO_AVOID_WARNING;
|
|
void* ptr = INHERITED::makeSpaceAtLeast(SkSafeMath::Mul(minIndexCount, sizeof(uint16_t)),
|
|
SkSafeMath::Mul(fallbackIndexCount, sizeof(uint16_t)),
|
|
sizeof(uint16_t),
|
|
buffer,
|
|
&offset,
|
|
&actualSize);
|
|
|
|
SkASSERT(0 == offset % sizeof(uint16_t));
|
|
*startIndex = static_cast<int>(offset / sizeof(uint16_t));
|
|
|
|
SkASSERT(0 == actualSize % sizeof(uint16_t));
|
|
SkASSERT(actualSize >= minIndexCount * sizeof(uint16_t));
|
|
*actualIndexCount = static_cast<int>(actualSize / sizeof(uint16_t));
|
|
return ptr;
|
|
}
|