Move GrMtlBufferManager functionality to better places
This is setting up for handling buffer recycling in the resource provider. Bug: skia:8243 Change-Id: Ie850b68bdcac455128aa3e89ae8adf62f17bddea Reviewed-on: https://skia-review.googlesource.com/c/skia/+/211988 Reviewed-by: Brian Osman <brianosman@google.com> Commit-Queue: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
parent
fd24b1fe84
commit
35a67eba61
@ -54,27 +54,4 @@ private:
|
|||||||
typedef GrGpuBuffer INHERITED;
|
typedef GrGpuBuffer INHERITED;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GrMtlBufferManager {
|
|
||||||
public:
|
|
||||||
GrMtlBufferManager(GrMtlGpu* gpu)
|
|
||||||
: fGpu(gpu), fBufferAllocation(nil), fAllocationSize(0), fNextOffset(0) {}
|
|
||||||
|
|
||||||
~GrMtlBufferManager() {
|
|
||||||
fBufferAllocation = nil; // Just to be sure
|
|
||||||
}
|
|
||||||
|
|
||||||
id<MTLBuffer> getDynamicAllocation(size_t size, size_t* offset);
|
|
||||||
void setVertexBuffer(id<MTLRenderCommandEncoder>, const GrMtlBuffer*, size_t index);
|
|
||||||
void setFragmentBuffer(id<MTLRenderCommandEncoder>, const GrMtlBuffer*, size_t index);
|
|
||||||
void resetBindings();
|
|
||||||
|
|
||||||
private:
|
|
||||||
GrMtlGpu* fGpu;
|
|
||||||
id<MTLBuffer> fBufferAllocation;
|
|
||||||
size_t fAllocationSize;
|
|
||||||
size_t fNextOffset;
|
|
||||||
static constexpr size_t kNumBindings = GrMtlUniformHandler::kLastUniformBinding + 3;
|
|
||||||
id<MTLBuffer> fBufferBindings[kNumBindings];
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -106,7 +106,7 @@ void GrMtlBuffer::internalMap(size_t sizeInBytes) {
|
|||||||
VALIDATE();
|
VALIDATE();
|
||||||
SkASSERT(!this->isMapped());
|
SkASSERT(!this->isMapped());
|
||||||
if (fIsDynamic) {
|
if (fIsDynamic) {
|
||||||
fMtlBuffer = this->mtlGpu()->bufferManager().getDynamicAllocation(sizeInBytes, &fOffset);
|
fMtlBuffer = this->mtlGpu()->resourceProvider().getDynamicBuffer(sizeInBytes, &fOffset);
|
||||||
fMappedBuffer = fMtlBuffer;
|
fMappedBuffer = fMtlBuffer;
|
||||||
fMapPtr = static_cast<char*>(fMtlBuffer.contents) + fOffset;
|
fMapPtr = static_cast<char*>(fMtlBuffer.contents) + fOffset;
|
||||||
} else {
|
} else {
|
||||||
@ -171,96 +171,3 @@ void GrMtlBuffer::validate() const {
|
|||||||
fMappedBuffer.length <= fMtlBuffer.length);
|
fMappedBuffer.length <= fMtlBuffer.length);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
id<MTLBuffer> GrMtlBufferManager::getDynamicAllocation(size_t size, size_t* offset) {
|
|
||||||
static size_t kSharedDynamicBufferSize = 16*1024;
|
|
||||||
|
|
||||||
// The idea here is that we create a ring buffer which is used for all dynamic allocations
|
|
||||||
// below a certain size. When a dynamic GrMtlBuffer is mapped, it grabs a portion of this
|
|
||||||
// buffer and uses it. On a subsequent map it will grab a different portion of the buffer.
|
|
||||||
// This prevents the buffer from overwriting itself before it's submitted to the command
|
|
||||||
// stream.
|
|
||||||
|
|
||||||
// Create a new buffer if we need to.
|
|
||||||
// If the requested size is larger than the shared buffer size, then we'll
|
|
||||||
// just make the allocation and the owning GrMtlBuffer will manage it (this
|
|
||||||
// only happens with buffers created by GrBufferAllocPool).
|
|
||||||
//
|
|
||||||
// TODO: By sending addCompletedHandler: to MTLCommandBuffer we can track when buffers
|
|
||||||
// are no longer in use and recycle them rather than creating a new one each time.
|
|
||||||
if (fAllocationSize - fNextOffset < size) {
|
|
||||||
size_t allocSize = (size >= kSharedDynamicBufferSize) ? size : kSharedDynamicBufferSize;
|
|
||||||
id<MTLBuffer> buffer;
|
|
||||||
SK_BEGIN_AUTORELEASE_BLOCK
|
|
||||||
buffer = [fGpu->device() newBufferWithLength: allocSize
|
|
||||||
#ifdef SK_BUILD_FOR_MAC
|
|
||||||
options: MTLResourceStorageModeManaged];
|
|
||||||
#else
|
|
||||||
options: MTLResourceStorageModeShared];
|
|
||||||
#endif
|
|
||||||
SK_END_AUTORELEASE_BLOCK
|
|
||||||
if (nil == buffer) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size >= kSharedDynamicBufferSize) {
|
|
||||||
*offset = 0;
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
fBufferAllocation = buffer;
|
|
||||||
fNextOffset = 0;
|
|
||||||
fAllocationSize = kSharedDynamicBufferSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grab the next available block
|
|
||||||
*offset = fNextOffset;
|
|
||||||
fNextOffset += size;
|
|
||||||
// Uniform buffer offsets need to be aligned to the nearest 256-byte boundary.
|
|
||||||
fNextOffset = GrSizeAlignUp(fNextOffset, 256);
|
|
||||||
|
|
||||||
return fBufferAllocation;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrMtlBufferManager::setVertexBuffer(id<MTLRenderCommandEncoder> encoder,
|
|
||||||
const GrMtlBuffer* buffer,
|
|
||||||
size_t index) {
|
|
||||||
SkASSERT(index < 4);
|
|
||||||
id<MTLBuffer> mtlVertexBuffer = buffer->mtlBuffer();
|
|
||||||
SkASSERT(mtlVertexBuffer);
|
|
||||||
// Apple recommends using setVertexBufferOffset: when changing the offset
|
|
||||||
// for a currently bound vertex buffer, rather than setVertexBuffer:
|
|
||||||
if (fBufferBindings[index] != mtlVertexBuffer) {
|
|
||||||
[encoder setVertexBuffer: mtlVertexBuffer
|
|
||||||
offset: 0
|
|
||||||
atIndex: index];
|
|
||||||
fBufferBindings[index] = mtlVertexBuffer;
|
|
||||||
}
|
|
||||||
[encoder setVertexBufferOffset: buffer->offset()
|
|
||||||
atIndex: index];
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrMtlBufferManager::setFragmentBuffer(id<MTLRenderCommandEncoder> encoder,
|
|
||||||
const GrMtlBuffer* buffer,
|
|
||||||
size_t index) {
|
|
||||||
SkASSERT(index < kNumBindings);
|
|
||||||
id<MTLBuffer> mtlFragmentBuffer = buffer->mtlBuffer();
|
|
||||||
// Apple recommends using setFragmentBufferOffset: when changing the offset
|
|
||||||
// for a currently bound fragment buffer, rather than setFragmentBuffer:
|
|
||||||
if (mtlFragmentBuffer) {
|
|
||||||
if (fBufferBindings[index] != mtlFragmentBuffer) {
|
|
||||||
[encoder setFragmentBuffer: mtlFragmentBuffer
|
|
||||||
offset: 0
|
|
||||||
atIndex: index];
|
|
||||||
fBufferBindings[index] = mtlFragmentBuffer;
|
|
||||||
}
|
|
||||||
[encoder setFragmentBufferOffset: buffer->offset()
|
|
||||||
atIndex: index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GrMtlBufferManager::resetBindings() {
|
|
||||||
for (size_t i = 0; i < kNumBindings; ++i) {
|
|
||||||
fBufferBindings[i] = nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
#include "src/gpu/GrGpu.h"
|
#include "src/gpu/GrGpu.h"
|
||||||
#include "src/gpu/GrSemaphore.h"
|
#include "src/gpu/GrSemaphore.h"
|
||||||
|
|
||||||
#include "src/gpu/mtl/GrMtlBuffer.h"
|
|
||||||
#include "src/gpu/mtl/GrMtlCaps.h"
|
#include "src/gpu/mtl/GrMtlCaps.h"
|
||||||
#include "src/gpu/mtl/GrMtlCopyManager.h"
|
#include "src/gpu/mtl/GrMtlCopyManager.h"
|
||||||
#include "src/gpu/mtl/GrMtlResourceProvider.h"
|
#include "src/gpu/mtl/GrMtlResourceProvider.h"
|
||||||
@ -49,8 +48,6 @@ public:
|
|||||||
|
|
||||||
GrMtlResourceProvider& resourceProvider() { return fResourceProvider; }
|
GrMtlResourceProvider& resourceProvider() { return fResourceProvider; }
|
||||||
|
|
||||||
GrMtlBufferManager& bufferManager() { return fBufferManager; }
|
|
||||||
|
|
||||||
GrMtlCommandBuffer* commandBuffer();
|
GrMtlCommandBuffer* commandBuffer();
|
||||||
|
|
||||||
enum SyncQueue {
|
enum SyncQueue {
|
||||||
@ -230,7 +227,6 @@ private:
|
|||||||
|
|
||||||
GrMtlCopyManager fCopyManager;
|
GrMtlCopyManager fCopyManager;
|
||||||
GrMtlResourceProvider fResourceProvider;
|
GrMtlResourceProvider fResourceProvider;
|
||||||
GrMtlBufferManager fBufferManager;
|
|
||||||
|
|
||||||
bool fDisconnected;
|
bool fDisconnected;
|
||||||
|
|
||||||
|
@ -101,7 +101,6 @@ GrMtlGpu::GrMtlGpu(GrContext* context, const GrContextOptions& options,
|
|||||||
, fCompiler(new SkSL::Compiler())
|
, fCompiler(new SkSL::Compiler())
|
||||||
, fCopyManager(this)
|
, fCopyManager(this)
|
||||||
, fResourceProvider(this)
|
, fResourceProvider(this)
|
||||||
, fBufferManager(this)
|
|
||||||
, fDisconnected(false) {
|
, fDisconnected(false) {
|
||||||
fMtlCaps.reset(new GrMtlCaps(options, fDevice, featureSet));
|
fMtlCaps.reset(new GrMtlCaps(options, fDevice, featureSet));
|
||||||
fCaps = fMtlCaps;
|
fCaps = fMtlCaps;
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#import <metal/metal.h>
|
#import <metal/metal.h>
|
||||||
|
|
||||||
typedef uint32_t GrColor;
|
typedef uint32_t GrColor;
|
||||||
|
class GrMtlBuffer;
|
||||||
class GrMtlPipelineState;
|
class GrMtlPipelineState;
|
||||||
class GrMtlRenderTarget;
|
class GrMtlRenderTarget;
|
||||||
|
|
||||||
@ -121,6 +122,9 @@ private:
|
|||||||
const GrBuffer* instanceBuffer, int instanceCount,
|
const GrBuffer* instanceBuffer, int instanceCount,
|
||||||
int baseInstance, GrPrimitiveRestart) final;
|
int baseInstance, GrPrimitiveRestart) final;
|
||||||
|
|
||||||
|
void setVertexBuffer(id<MTLRenderCommandEncoder>, const GrMtlBuffer*, size_t index);
|
||||||
|
void resetBufferBindings();
|
||||||
|
|
||||||
GrMtlGpu* fGpu;
|
GrMtlGpu* fGpu;
|
||||||
// GrRenderTargetProxy bounds
|
// GrRenderTargetProxy bounds
|
||||||
#ifdef SK_DEBUG
|
#ifdef SK_DEBUG
|
||||||
@ -138,6 +142,9 @@ private:
|
|||||||
|
|
||||||
CommandBufferInfo fCommandBufferInfo;
|
CommandBufferInfo fCommandBufferInfo;
|
||||||
|
|
||||||
|
static constexpr size_t kNumBindings = GrMtlUniformHandler::kLastUniformBinding + 3;
|
||||||
|
id<MTLBuffer> fBufferBindings[kNumBindings];
|
||||||
|
|
||||||
typedef GrGpuRTCommandBuffer INHERITED;
|
typedef GrGpuRTCommandBuffer INHERITED;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -232,7 +232,7 @@ void GrMtlGpuRTCommandBuffer::onDraw(const GrPrimitiveProcessor& primProc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fActiveRenderCmdEncoder = nil;
|
fActiveRenderCmdEncoder = nil;
|
||||||
fGpu->bufferManager().resetBindings();
|
this->resetBufferBindings();
|
||||||
fCommandBufferInfo.fBounds.join(bounds);
|
fCommandBufferInfo.fBounds.join(bounds);
|
||||||
SK_END_AUTORELEASE_BLOCK
|
SK_END_AUTORELEASE_BLOCK
|
||||||
}
|
}
|
||||||
@ -330,16 +330,14 @@ void GrMtlGpuRTCommandBuffer::bindGeometry(const GrBuffer* vertexBuffer,
|
|||||||
SkASSERT(!static_cast<const GrGpuBuffer*>(vertexBuffer)->isMapped());
|
SkASSERT(!static_cast<const GrGpuBuffer*>(vertexBuffer)->isMapped());
|
||||||
|
|
||||||
const GrMtlBuffer* grMtlBuffer = static_cast<const GrMtlBuffer*>(vertexBuffer);
|
const GrMtlBuffer* grMtlBuffer = static_cast<const GrMtlBuffer*>(vertexBuffer);
|
||||||
fGpu->bufferManager().setVertexBuffer(fActiveRenderCmdEncoder, grMtlBuffer,
|
this->setVertexBuffer(fActiveRenderCmdEncoder, grMtlBuffer, bufferIndex++);
|
||||||
bufferIndex++);
|
|
||||||
}
|
}
|
||||||
if (instanceBuffer) {
|
if (instanceBuffer) {
|
||||||
SkASSERT(!instanceBuffer->isCpuBuffer());
|
SkASSERT(!instanceBuffer->isCpuBuffer());
|
||||||
SkASSERT(!static_cast<const GrGpuBuffer*>(instanceBuffer)->isMapped());
|
SkASSERT(!static_cast<const GrGpuBuffer*>(instanceBuffer)->isMapped());
|
||||||
|
|
||||||
const GrMtlBuffer* grMtlBuffer = static_cast<const GrMtlBuffer*>(instanceBuffer);
|
const GrMtlBuffer* grMtlBuffer = static_cast<const GrMtlBuffer*>(instanceBuffer);
|
||||||
fGpu->bufferManager().setVertexBuffer(fActiveRenderCmdEncoder, grMtlBuffer,
|
this->setVertexBuffer(fActiveRenderCmdEncoder, grMtlBuffer, bufferIndex++);
|
||||||
bufferIndex++);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,3 +391,27 @@ void GrMtlGpuRTCommandBuffer::sendIndexedInstancedMeshToGpu(GrPrimitiveType prim
|
|||||||
baseInstance:baseInstance];
|
baseInstance:baseInstance];
|
||||||
fGpu->stats()->incNumDraws();
|
fGpu->stats()->incNumDraws();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GrMtlGpuRTCommandBuffer::setVertexBuffer(id<MTLRenderCommandEncoder> encoder,
|
||||||
|
const GrMtlBuffer* buffer,
|
||||||
|
size_t index) {
|
||||||
|
SkASSERT(index < 4);
|
||||||
|
id<MTLBuffer> mtlVertexBuffer = buffer->mtlBuffer();
|
||||||
|
SkASSERT(mtlVertexBuffer);
|
||||||
|
// Apple recommends using setVertexBufferOffset: when changing the offset
|
||||||
|
// for a currently bound vertex buffer, rather than setVertexBuffer:
|
||||||
|
if (fBufferBindings[index] != mtlVertexBuffer) {
|
||||||
|
[encoder setVertexBuffer: mtlVertexBuffer
|
||||||
|
offset: 0
|
||||||
|
atIndex: index];
|
||||||
|
fBufferBindings[index] = mtlVertexBuffer;
|
||||||
|
}
|
||||||
|
[encoder setVertexBufferOffset: buffer->offset()
|
||||||
|
atIndex: index];
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrMtlGpuRTCommandBuffer::resetBufferBindings() {
|
||||||
|
for (size_t i = 0; i < kNumBindings; ++i) {
|
||||||
|
fBufferBindings[i] = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -42,6 +42,8 @@ public:
|
|||||||
// Finds or creates a compatible MTLSamplerState based on the GrSamplerState.
|
// Finds or creates a compatible MTLSamplerState based on the GrSamplerState.
|
||||||
GrMtlSampler* findOrCreateCompatibleSampler(const GrSamplerState&, uint32_t maxMipLevel);
|
GrMtlSampler* findOrCreateCompatibleSampler(const GrSamplerState&, uint32_t maxMipLevel);
|
||||||
|
|
||||||
|
id<MTLBuffer> getDynamicBuffer(size_t size, size_t* offset);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#ifdef SK_DEBUG
|
#ifdef SK_DEBUG
|
||||||
#define GR_PIPELINE_STATE_CACHE_STATS
|
#define GR_PIPELINE_STATE_CACHE_STATS
|
||||||
@ -92,6 +94,14 @@ private:
|
|||||||
|
|
||||||
SkTDynamicHash<GrMtlSampler, GrMtlSampler::Key> fSamplers;
|
SkTDynamicHash<GrMtlSampler, GrMtlSampler::Key> fSamplers;
|
||||||
SkTDynamicHash<GrMtlDepthStencil, GrMtlDepthStencil::Key> fDepthStencilStates;
|
SkTDynamicHash<GrMtlDepthStencil, GrMtlDepthStencil::Key> fDepthStencilStates;
|
||||||
|
|
||||||
|
// Buffer state
|
||||||
|
struct BufferState {
|
||||||
|
id<MTLBuffer> fAllocation;
|
||||||
|
size_t fAllocationSize;
|
||||||
|
size_t fNextOffset;
|
||||||
|
};
|
||||||
|
BufferState fBufferState;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "src/gpu/mtl/GrMtlResourceProvider.h"
|
#include "src/gpu/mtl/GrMtlResourceProvider.h"
|
||||||
|
|
||||||
|
#include "src/gpu/mtl/GrMtlCommandBuffer.h"
|
||||||
#include "src/gpu/mtl/GrMtlCopyManager.h"
|
#include "src/gpu/mtl/GrMtlCopyManager.h"
|
||||||
#include "src/gpu/mtl/GrMtlGpu.h"
|
#include "src/gpu/mtl/GrMtlGpu.h"
|
||||||
#include "src/gpu/mtl/GrMtlPipelineState.h"
|
#include "src/gpu/mtl/GrMtlPipelineState.h"
|
||||||
@ -15,7 +16,8 @@
|
|||||||
#include "src/sksl/SkSLCompiler.h"
|
#include "src/sksl/SkSLCompiler.h"
|
||||||
|
|
||||||
GrMtlResourceProvider::GrMtlResourceProvider(GrMtlGpu* gpu)
|
GrMtlResourceProvider::GrMtlResourceProvider(GrMtlGpu* gpu)
|
||||||
: fGpu(gpu) {
|
: fGpu(gpu)
|
||||||
|
, fBufferState({nil, 0, 0}) {
|
||||||
fPipelineStateCache.reset(new PipelineStateCache(gpu));
|
fPipelineStateCache.reset(new PipelineStateCache(gpu));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,3 +153,53 @@ GrMtlPipelineState* GrMtlResourceProvider::PipelineStateCache::refPipelineState(
|
|||||||
}
|
}
|
||||||
return (*entry)->fPipelineState.get();
|
return (*entry)->fPipelineState.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id<MTLBuffer> GrMtlResourceProvider::getDynamicBuffer(size_t size, size_t* offset) {
|
||||||
|
static size_t kSharedDynamicBufferSize = 16*1024;
|
||||||
|
|
||||||
|
// The idea here is that we create a ring buffer which is used for all dynamic allocations
|
||||||
|
// below a certain size. When a dynamic GrMtlBuffer is mapped, it grabs a portion of this
|
||||||
|
// buffer and uses it. On a subsequent map it will grab a different portion of the buffer.
|
||||||
|
// This prevents the buffer from overwriting itself before it's submitted to the command
|
||||||
|
// stream.
|
||||||
|
|
||||||
|
// Create a new buffer if we need to.
|
||||||
|
// If the requested size is larger than the shared buffer size, then we'll
|
||||||
|
// just make the allocation and the owning GrMtlBuffer will manage it (this
|
||||||
|
// only happens with buffers created by GrBufferAllocPool).
|
||||||
|
//
|
||||||
|
// TODO: By sending addCompletedHandler: to MTLCommandBuffer we can track when buffers
|
||||||
|
// are no longer in use and recycle them rather than creating a new one each time.
|
||||||
|
if (fBufferState.fAllocationSize - fBufferState.fNextOffset < size) {
|
||||||
|
size_t allocSize = (size >= kSharedDynamicBufferSize) ? size : kSharedDynamicBufferSize;
|
||||||
|
id<MTLBuffer> buffer;
|
||||||
|
SK_BEGIN_AUTORELEASE_BLOCK
|
||||||
|
buffer = [fGpu->device() newBufferWithLength: allocSize
|
||||||
|
#ifdef SK_BUILD_FOR_MAC
|
||||||
|
options: MTLResourceStorageModeManaged];
|
||||||
|
#else
|
||||||
|
options: MTLResourceStorageModeShared];
|
||||||
|
#endif
|
||||||
|
SK_END_AUTORELEASE_BLOCK
|
||||||
|
if (nil == buffer) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size >= kSharedDynamicBufferSize) {
|
||||||
|
*offset = 0;
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
fBufferState.fAllocation = buffer;
|
||||||
|
fBufferState.fNextOffset = 0;
|
||||||
|
fBufferState.fAllocationSize = kSharedDynamicBufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab the next available block
|
||||||
|
*offset = fBufferState.fNextOffset;
|
||||||
|
fBufferState.fNextOffset += size;
|
||||||
|
// Uniform buffer offsets need to be aligned to the nearest 256-byte boundary.
|
||||||
|
fBufferState.fNextOffset = GrSizeAlignUp(fBufferState.fNextOffset, 256);
|
||||||
|
|
||||||
|
return fBufferState.fAllocation;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user