Create ring buffer for managing D3D uniforms.

* Adds a base class for the ring buffer (to be used by Metal as well),
  which tracks the current available space. APIs will need to
  implement creation of the buffer in the subclass.
* The API implementation will need to store SubmitData on command buffer
  submit, and then pass it to finishSubmit when the command buffer
  finishes.

Change-Id: I4cc5e4a72d259ee9d15dac0e964819d4562da3d7
Bug: skia:9935
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/291936
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
Jim Van Verth 2020-06-01 11:34:49 -04:00 committed by Skia Commit-Bot
parent 7ca48cc1b2
commit 3eadce263c
15 changed files with 310 additions and 7 deletions

View File

@ -179,6 +179,8 @@ skia_gpu_sources = [
"$_src/gpu/GrResourceProvider.cpp",
"$_src/gpu/GrResourceProvider.h",
"$_src/gpu/GrResourceProviderPriv.h",
"$_src/gpu/GrRingBuffer.cpp",
"$_src/gpu/GrRingBuffer.h",
"$_src/gpu/GrSPIRVUniformHandler.cpp",
"$_src/gpu/GrSPIRVUniformHandler.h",
"$_src/gpu/GrSPIRVVaryingHandler.cpp",
@ -755,6 +757,8 @@ skia_direct3d_sources = [
"$_src/gpu/d3d/GrD3DCaps.h",
"$_src/gpu/d3d/GrD3DCommandList.cpp",
"$_src/gpu/d3d/GrD3DCommandList.h",
"$_src/gpu/d3d/GrD3DConstantRingBuffer.cpp",
"$_src/gpu/d3d/GrD3DConstantRingBuffer.h",
"$_src/gpu/d3d/GrD3DCpuDescriptorManager.cpp",
"$_src/gpu/d3d/GrD3DCpuDescriptorManager.h",
"$_src/gpu/d3d/GrD3DDescriptorHeap.cpp",

89
src/gpu/GrRingBuffer.cpp Normal file
View File

@ -0,0 +1,89 @@
/*
* 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/GrRingBuffer.h"
// Get offset into buffer that has enough space for size
// Returns fTotalSize if no space
size_t GrRingBuffer::getAllocationOffset(size_t size) {
// capture current state locally (because fTail could be overwritten by the completion handler)
size_t head, tail;
SkAutoSpinlock lock(fMutex);
head = fHead;
tail = fTail;
// The head and tail indices increment without bound, wrapping with overflow,
// so we need to mod them down to the actual bounds of the allocation to determine
// which blocks are available.
size_t modHead = head & (fTotalSize - 1);
size_t modTail = tail & (fTotalSize - 1);
bool full = (head != tail && modHead == modTail);
if (full) {
return fTotalSize;
}
// case 1: free space lies at the beginning and/or the end of the buffer
if (modHead >= modTail) {
// check for room at the end
if (fTotalSize - modHead < size) {
// no room at the end, check the beginning
if (modTail < size) {
// no room at the beginning
return fTotalSize;
}
// we are going to allocate from the beginning, adjust head to '0' position
head += fTotalSize - modHead;
modHead = 0;
}
// case 2: free space lies in the middle of the buffer, check for room there
} else if (modTail - modHead < size) {
// no room in the middle
return fTotalSize;
}
fHead = GrAlignTo(head + size, fAlignment);
return modHead;
}
GrRingBuffer::Slice GrRingBuffer::suballocate(size_t size) {
size_t offset = this->getAllocationOffset(size);
if (offset < fTotalSize) {
return { fBuffer, offset };
}
// Try to grow allocation (old allocation will age out).
fTotalSize *= 2;
fBuffer = this->createBuffer(fTotalSize);
SkASSERT(fBuffer);
SkAutoSpinlock lock(fMutex);
fHead = 0;
fTail = 0;
fGenID++;
offset = this->getAllocationOffset(size);
SkASSERT(offset < fTotalSize);
return { fBuffer, offset };
}
// used when current command buffer/command list is submitted
GrRingBuffer::SubmitData GrRingBuffer::startSubmit() {
SubmitData submitData;
SkAutoSpinlock lock(fMutex);
submitData.fBuffer = fBuffer;
submitData.fLastHead = fHead;
submitData.fGenID = fGenID;
return submitData;
}
// used when current command buffer/command list is completed
void GrRingBuffer::finishSubmit(const GrRingBuffer::SubmitData& submitData) {
SkAutoSpinlock lock(fMutex);
if (submitData.fGenID == fGenID) {
fTail = submitData.fLastHead;
}
}

69
src/gpu/GrRingBuffer.h Normal file
View File

@ -0,0 +1,69 @@
/*
* Copyright 2020 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrRingBuffer_DEFINED
#define GrRingBuffer_DEFINED
#include "src/gpu/GrGpuBuffer.h"
#include "include/private/SkSpinlock.h"
/**
* A wrapper for a GPU buffer that allocates slices in a continuous ring
*/
class GrRingBuffer : public SkRefCnt {
public:
GrRingBuffer(sk_sp<GrGpuBuffer> buffer, size_t size, size_t alignment)
: fBuffer(std::move(buffer))
, fTotalSize(size)
, fAlignment(alignment)
, fHead(0)
, fTail(0)
, fGenID(0) {
// We increment fHead and fTail without bound and let overflow handle any wrapping.
// Because of this, size needs to be a power of two.
SkASSERT(SkIsPow2(size));
}
virtual ~GrRingBuffer() = default;
struct Slice {
sk_sp<GrGpuBuffer> fBuffer;
size_t fOffset;
};
Slice suballocate(size_t size);
class SubmitData {
public:
GrGpuBuffer* buffer() const { return fBuffer.get(); }
private:
friend class GrRingBuffer;
sk_sp<GrGpuBuffer> fBuffer;
size_t fLastHead;
size_t fGenID;
};
// Backends should call startSubmit() at submit time, and finishSubmit() when the
// command buffer/list finishes.
SubmitData startSubmit();
void finishSubmit(const SubmitData&);
size_t size() const { return fTotalSize; }
private:
virtual sk_sp<GrGpuBuffer> createBuffer(size_t size) = 0;
size_t getAllocationOffset(size_t size);
sk_sp<GrGpuBuffer> fBuffer;
size_t fTotalSize;
size_t fAlignment;
size_t fHead SK_GUARDED_BY(fMutex); // where we start allocating
size_t fTail SK_GUARDED_BY(fMutex); // where we start deallocating
uint64_t fGenID SK_GUARDED_BY(fMutex); // incremented when createBuffer is called
SkSpinlock fMutex;
};
#endif

View File

@ -200,7 +200,8 @@ GrD3DDirectCommandList::GrD3DDirectCommandList(gr_cp<ID3D12CommandAllocator> all
, fCurrentVertexStride(0)
, fCurrentInstanceBuffer(nullptr)
, fCurrentInstanceStride(0)
, fCurrentIndexBuffer(nullptr) {
, fCurrentIndexBuffer(nullptr)
, fCurrentConstantRingBuffer(nullptr) {
}
void GrD3DDirectCommandList::onReset() {
@ -210,6 +211,10 @@ void GrD3DDirectCommandList::onReset() {
fCurrentInstanceBuffer = nullptr;
fCurrentInstanceStride = 0;
fCurrentIndexBuffer = nullptr;
if (fCurrentConstantRingBuffer) {
fCurrentConstantRingBuffer->finishSubmit(fConstantRingBufferSubmitData);
fCurrentConstantRingBuffer = nullptr;
}
}
void GrD3DDirectCommandList::setPipelineState(sk_sp<GrD3DPipelineState> pipelineState) {
@ -218,6 +223,16 @@ void GrD3DDirectCommandList::setPipelineState(sk_sp<GrD3DPipelineState> pipeline
this->addResource(std::move(pipelineState));
}
void GrD3DDirectCommandList::setCurrentConstantBuffer(
const sk_sp<GrD3DConstantRingBuffer>& constantBuffer) {
fCurrentConstantRingBuffer = constantBuffer.get();
if (fCurrentConstantRingBuffer) {
fConstantRingBufferSubmitData = constantBuffer->startSubmit();
this->addResource(
static_cast<GrD3DBuffer*>(fConstantRingBufferSubmitData.buffer())->resource());
}
}
void GrD3DDirectCommandList::setStencilRef(unsigned int stencilRef) {
SkASSERT(fIsActive);
fCommandList->OMSetStencilRef(stencilRef);

View File

@ -12,11 +12,13 @@
#include "include/gpu/d3d/GrD3DTypes.h"
#include "include/private/SkColorData.h"
#include "src/gpu/GrManagedResource.h"
#include "src/gpu/d3d/GrD3DConstantRingBuffer.h"
#include <memory>
class GrD3DGpu;
class GrD3DBuffer;
class GrD3DConstantRingBuffer;
class GrD3DPipelineState;
class GrD3DRenderTarget;
class GrD3DRootSignature;
@ -122,6 +124,8 @@ public:
void setPipelineState(sk_sp<GrD3DPipelineState> pipelineState);
void setCurrentConstantBuffer(const sk_sp<GrD3DConstantRingBuffer>& constantBuffer);
void setStencilRef(unsigned int stencilRef);
void setBlendFactor(const float blendFactor[4]);
void setPrimitiveTopology(D3D12_PRIMITIVE_TOPOLOGY primitiveTopology);
@ -154,6 +158,9 @@ private:
const GrD3DBuffer* fCurrentInstanceBuffer;
size_t fCurrentInstanceStride;
const GrD3DBuffer* fCurrentIndexBuffer;
GrD3DConstantRingBuffer* fCurrentConstantRingBuffer;
GrD3DConstantRingBuffer::SubmitData fConstantRingBufferSubmitData;
};
class GrD3DCopyCommandList : public GrD3DCommandList {

View File

@ -0,0 +1,30 @@
/*
* 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/d3d/GrD3DConstantRingBuffer.h"
#include "src/gpu/d3d/GrD3DBuffer.h"
#include "src/gpu/d3d/GrD3DGpu.h"
sk_sp<GrD3DConstantRingBuffer> GrD3DConstantRingBuffer::Make(GrD3DGpu* gpu, size_t size,
size_t alignment) {
sk_sp<GrGpuBuffer> buffer = GrD3DBuffer::Make(gpu, size, GrGpuBufferType::kVertex,
kDynamic_GrAccessPattern);
if (!buffer) {
return nullptr;
}
return sk_sp<GrD3DConstantRingBuffer>(new GrD3DConstantRingBuffer(std::move(buffer), size,
alignment, gpu));
}
sk_sp<GrGpuBuffer> GrD3DConstantRingBuffer::createBuffer(size_t size) {
// Make sure the old buffer is added to the current command list
fGpu->resourceProvider().prepForSubmit();
return GrD3DBuffer::Make(fGpu, size, GrGpuBufferType::kVertex, kDynamic_GrAccessPattern);
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2020 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrD3DConstantRingBuffer_DEFINED
#define GrD3DConstantRingBuffer_DEFINED
#include "src/gpu/GrRingBuffer.h"
class GrD3DGpu;
class GrD3DConstantRingBuffer : public GrRingBuffer {
public:
static sk_sp<GrD3DConstantRingBuffer> Make(GrD3DGpu* gpu, size_t size, size_t alignment);
private:
GrD3DConstantRingBuffer(sk_sp<GrGpuBuffer> buffer, size_t size, size_t alignment, GrD3DGpu* gpu)
: INHERITED(std::move(buffer), size, alignment)
, fGpu(gpu) {}
~GrD3DConstantRingBuffer() override = default;
sk_sp<GrGpuBuffer> createBuffer(size_t size) override;
GrD3DGpu* fGpu;
typedef GrRingBuffer INHERITED;
};
#endif

View File

@ -111,6 +111,8 @@ GrOpsRenderPass* GrD3DGpu::getOpsRenderPass(
bool GrD3DGpu::submitDirectCommandList(SyncQueue sync) {
SkASSERT(fCurrentDirectCommandList);
fResourceProvider.prepForSubmit();
GrD3DDirectCommandList::SubmitResult result = fCurrentDirectCommandList->submit(fQueue.get());
if (result == GrD3DDirectCommandList::SubmitResult::kFailure) {
return false;

View File

@ -160,7 +160,7 @@ bool GrD3DOpsRenderPass::onBindPipeline(const GrProgramInfo& info, const SkRect&
return false;
}
fCurrentPipelineState->setData(fRenderTarget, info);
fCurrentPipelineState->setData(fGpu, fRenderTarget, info);
fGpu->currentCommandList()->setGraphicsRootSignature(fCurrentPipelineState->rootSignature());
fGpu->currentCommandList()->setPipelineState(fCurrentPipelineState);

View File

@ -42,7 +42,7 @@ GrD3DPipelineState::GrD3DPipelineState(
, fVertexStride(vertexStride)
, fInstanceStride(instanceStride) {}
void GrD3DPipelineState::setData(const GrRenderTarget* renderTarget,
void GrD3DPipelineState::setData(GrD3DGpu* gpu, const GrRenderTarget* renderTarget,
const GrProgramInfo& programInfo) {
this->setRenderTargetState(renderTarget, programInfo.origin());
@ -62,6 +62,9 @@ void GrD3DPipelineState::setData(const GrRenderTarget* renderTarget,
fXferProcessor->setData(fDataManager, programInfo.pipeline().getXferProcessor(),
dstTexture, offset);
}
// TODO: use returned virtual address to create a CBV and set in command list
(void) fDataManager.uploadConstants(gpu);
}
void GrD3DPipelineState::setRenderTargetState(const GrRenderTarget* rt, GrSurfaceOrigin origin) {

View File

@ -52,7 +52,7 @@ public:
ID3D12PipelineState* pipelineState() const { return fPipelineState.get(); }
const sk_sp<GrD3DRootSignature>& rootSignature() const { return fRootSignature; }
void setData(const GrRenderTarget* renderTarget, const GrProgramInfo& programInfo);
void setData(GrD3DGpu*, const GrRenderTarget* renderTarget, const GrProgramInfo& programInfo);
void setAndBindTextures(const GrPrimitiveProcessor& primProc,
const GrSurfaceProxy* const primProcTextures[],

View File

@ -7,6 +7,9 @@
#include "src/gpu/d3d/GrD3DPipelineStateDataManager.h"
#include "src/gpu/d3d/GrD3DGpu.h"
#include "src/gpu/d3d/GrD3DResourceProvider.h"
GrD3DPipelineStateDataManager::GrD3DPipelineStateDataManager(const UniformInfoArray& uniforms,
uint32_t uniformSize)
: INHERITED(uniforms.count(), uniformSize) {
@ -26,3 +29,13 @@ GrD3DPipelineStateDataManager::GrD3DPipelineStateDataManager(const UniformInfoAr
++i;
}
}
D3D12_GPU_VIRTUAL_ADDRESS GrD3DPipelineStateDataManager::uploadConstants(GrD3DGpu* gpu) {
if (fUniformsDirty) {
fConstantBufferAddress = gpu->resourceProvider().uploadConstantData(fUniformData.get(),
fUniformSize);
fUniformsDirty = false;
}
return fConstantBufferAddress;
}

View File

@ -13,6 +13,9 @@
#include "include/gpu/d3d/GrD3DTypes.h"
#include "src/gpu/GrSPIRVUniformHandler.h"
class GrD3DConstantRingBuffer;
class GrD3DGpu;
class GrD3DPipelineStateDataManager : public GrUniformDataManager {
public:
typedef GrSPIRVUniformHandler::UniformInfoArray UniformInfoArray;
@ -20,9 +23,11 @@ public:
GrD3DPipelineStateDataManager(const UniformInfoArray&,
uint32_t uniformSize);
// TODO: upload to uniform buffer
D3D12_GPU_VIRTUAL_ADDRESS uploadConstants(GrD3DGpu* gpu);
private:
D3D12_GPU_VIRTUAL_ADDRESS fConstantBufferAddress;
typedef GrUniformDataManager INHERITED;
};

View File

@ -9,6 +9,7 @@
#include "include/gpu/GrContextOptions.h"
#include "src/gpu/GrContextPriv.h"
#include "src/gpu/d3d/GrD3DBuffer.h"
#include "src/gpu/d3d/GrD3DCommandList.h"
#include "src/gpu/d3d/GrD3DGpu.h"
#include "src/gpu/d3d/GrD3DPipelineState.h"
@ -17,8 +18,7 @@
GrD3DResourceProvider::GrD3DResourceProvider(GrD3DGpu* gpu)
: fGpu(gpu)
, fCpuDescriptorManager(gpu)
, fPipelineStateCache(new PipelineStateCache(gpu)) {
}
, fPipelineStateCache(new PipelineStateCache(gpu)) {}
std::unique_ptr<GrD3DDirectCommandList> GrD3DResourceProvider::findOrCreateDirectCommandList() {
if (fAvailableDirectCommandLists.count()) {
@ -99,6 +99,33 @@ sk_sp<GrD3DPipelineState> GrD3DResourceProvider::findOrCreateCompatiblePipelineS
return fPipelineStateCache->refPipelineState(rt, info);
}
D3D12_GPU_VIRTUAL_ADDRESS GrD3DResourceProvider::uploadConstantData(void* data, size_t size) {
// constant size has to be aligned to 256
constexpr int kConstantAlignment = 256;
// Due to dependency on the resource cache we can't initialize this in the constructor, so
// we do so it here.
if (!fConstantBuffer) {
fConstantBuffer = GrD3DConstantRingBuffer::Make(fGpu, 128 * 1024, kConstantAlignment);
SkASSERT(fConstantBuffer);
}
// upload the data
size_t paddedSize = GrAlignTo(size, kConstantAlignment);
GrRingBuffer::Slice slice = fConstantBuffer->suballocate(paddedSize);
char* destPtr = static_cast<char*>(slice.fBuffer->map()) + slice.fOffset;
memcpy(destPtr, data, size);
// create the associated constant buffer view descriptor
GrD3DBuffer* d3dBuffer = static_cast<GrD3DBuffer*>(slice.fBuffer.get());
D3D12_GPU_VIRTUAL_ADDRESS gpuAddress = d3dBuffer->d3dResource()->GetGPUVirtualAddress();
return gpuAddress + slice.fOffset;
}
void GrD3DResourceProvider::prepForSubmit() {
fGpu->currentCommandList()->setCurrentConstantBuffer(fConstantBuffer);
}
////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef GR_PIPELINE_STATE_CACHE_STATS

View File

@ -12,6 +12,7 @@
#include "include/private/SkTArray.h"
#include "src/core/SkLRUCache.h"
#include "src/gpu/GrProgramDesc.h"
#include "src/gpu/d3d/GrD3DConstantRingBuffer.h"
#include "src/gpu/d3d/GrD3DCpuDescriptorManager.h"
#include "src/gpu/d3d/GrD3DRootSignature.h"
@ -51,6 +52,9 @@ public:
sk_sp<GrD3DPipelineState> findOrCreateCompatiblePipelineState(GrRenderTarget*,
const GrProgramInfo&);
D3D12_GPU_VIRTUAL_ADDRESS uploadConstantData(void* data, size_t size);
void prepForSubmit();
private:
#ifdef SK_DEBUG
#define GR_PIPELINE_STATE_CACHE_STATS
@ -89,6 +93,8 @@ private:
GrD3DCpuDescriptorManager fCpuDescriptorManager;
sk_sp<GrD3DConstantRingBuffer> fConstantBuffer;
std::unique_ptr<PipelineStateCache> fPipelineStateCache;
};