blob: 3e1adf2452ff8ca8ce682ef6ab7d9540be334d6b [file] [log] [blame]
/*
* 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/GrD3DBuffer.h"
#include "src/gpu/d3d/GrD3DGpu.h"
#include "src/gpu/d3d/GrD3DUtil.h"
#ifdef SK_DEBUG
#define VALIDATE() this->validate()
#else
#define VALIDATE() do {} while(false)
#endif
static gr_cp<ID3D12Resource> make_d3d_buffer(GrD3DGpu* gpu,
size_t size,
GrGpuBufferType intendedType,
GrAccessPattern accessPattern,
D3D12_RESOURCE_STATES* resourceState,
sk_sp<GrD3DAlloc>* alloc) {
D3D12_HEAP_TYPE heapType;
if (accessPattern == kStatic_GrAccessPattern) {
SkASSERT(intendedType != GrGpuBufferType::kXferCpuToGpu &&
intendedType != GrGpuBufferType::kXferGpuToCpu);
heapType = D3D12_HEAP_TYPE_DEFAULT;
// Needs to be transitioned to appropriate state to be read in shader
*resourceState = D3D12_RESOURCE_STATE_COPY_DEST;
} else {
if (intendedType == GrGpuBufferType::kXferGpuToCpu) {
heapType = D3D12_HEAP_TYPE_READBACK;
// Cannot be changed
*resourceState = D3D12_RESOURCE_STATE_COPY_DEST;
} else {
heapType = D3D12_HEAP_TYPE_UPLOAD;
// Cannot be changed
// Includes VERTEX_AND_CONSTANT_BUFFER, INDEX_BUFFER, INDIRECT_ARGUMENT, and COPY_SOURCE
*resourceState = D3D12_RESOURCE_STATE_GENERIC_READ;
}
}
D3D12_RESOURCE_DESC bufferDesc = {};
bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
bufferDesc.Alignment = 0; // default alignment
bufferDesc.Width = size;
bufferDesc.Height = 1;
bufferDesc.DepthOrArraySize = 1;
bufferDesc.MipLevels = 1;
bufferDesc.Format = DXGI_FORMAT_UNKNOWN;
bufferDesc.SampleDesc.Count = 1;
bufferDesc.SampleDesc.Quality = 0; // Doesn't apply to buffers
bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
bufferDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
gr_cp<ID3D12Resource> resource = gpu->memoryAllocator()->createResource(
heapType, &bufferDesc, *resourceState, alloc, nullptr);
return resource;
}
sk_sp<GrD3DBuffer> GrD3DBuffer::Make(GrD3DGpu* gpu, size_t size, GrGpuBufferType intendedType,
GrAccessPattern accessPattern) {
SkASSERT(!gpu->protectedContext() || (accessPattern != kStatic_GrAccessPattern));
D3D12_RESOURCE_STATES resourceState;
sk_sp<GrD3DAlloc> alloc;
gr_cp<ID3D12Resource> resource = make_d3d_buffer(gpu, size, intendedType, accessPattern,
&resourceState, &alloc);
if (!resource) {
return nullptr;
}
return sk_sp<GrD3DBuffer>(new GrD3DBuffer(gpu, size, intendedType, accessPattern,
std::move(resource), std::move(alloc),
resourceState));
}
GrD3DBuffer::GrD3DBuffer(GrD3DGpu* gpu, size_t size, GrGpuBufferType intendedType,
GrAccessPattern accessPattern, gr_cp<ID3D12Resource> bufferResource,
sk_sp<GrD3DAlloc> alloc, D3D12_RESOURCE_STATES resourceState)
: INHERITED(gpu, size, intendedType, accessPattern)
, fResourceState(resourceState)
, fD3DResource(std::move(bufferResource))
, fAlloc(std::move(alloc)) {
this->registerWithCache(SkBudgeted::kYes);
// TODO: persistently map UPLOAD resources?
VALIDATE();
}
void GrD3DBuffer::setResourceState(const GrD3DGpu* gpu,
D3D12_RESOURCE_STATES newResourceState) {
if (newResourceState == fResourceState ||
// GENERIC_READ encapsulates a lot of different read states
(fResourceState == D3D12_RESOURCE_STATE_GENERIC_READ &&
SkToBool(newResourceState | fResourceState))) {
return;
}
D3D12_RESOURCE_TRANSITION_BARRIER barrier = {};
barrier.pResource = this->d3dResource();
barrier.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.StateBefore = fResourceState;
barrier.StateAfter = newResourceState;
gpu->addBufferResourceBarriers(this, 1, &barrier);
fResourceState = newResourceState;
}
void GrD3DBuffer::releaseResource() {
if (this->wasDestroyed()) {
return;
}
if (fMapPtr) {
this->internalUnmap(this->size());
fMapPtr = nullptr;
}
SkASSERT(fD3DResource);
SkASSERT(fAlloc);
fD3DResource.reset();
fAlloc.reset();
}
void GrD3DBuffer::onRelease() {
this->releaseResource();
this->INHERITED::onRelease();
}
void GrD3DBuffer::onAbandon() {
this->releaseResource();
this->INHERITED::onAbandon();
}
void GrD3DBuffer::onMap() {
this->internalMap(this->size());
}
void GrD3DBuffer::onUnmap() {
this->internalUnmap(this->size());
}
bool GrD3DBuffer::onUpdateData(const void* src, size_t size) {
SkASSERT(src);
if (size > this->size()) {
return false;
}
if (!fD3DResource) {
return false;
}
this->internalMap(size);
if (!fMapPtr) {
return false;
}
if (this->accessPattern() == kStatic_GrAccessPattern) {
// We should never call this method on static buffers in protected contexts.
SkASSERT(!this->getD3DGpu()->protectedContext());
//*** any alignment restrictions?
}
memcpy(fMapPtr, src, size);
this->internalUnmap(size);
return true;
}
void GrD3DBuffer::internalMap(size_t size) {
// TODO: if UPLOAD heap type, could be persistently mapped (i.e., this would be a no-op)
if (this->wasDestroyed()) {
return;
}
SkASSERT(fD3DResource);
SkASSERT(!this->isMapped());
SkASSERT(this->size() >= size);
VALIDATE();
if (this->accessPattern() == kStatic_GrAccessPattern) {
SkASSERT(!fStagingBuffer);
GrStagingBufferManager::Slice slice =
this->getD3DGpu()->stagingBufferManager()->allocateStagingBufferSlice(size);
if (!slice.fBuffer) {
return;
}
fStagingBuffer = static_cast<const GrD3DBuffer*>(slice.fBuffer)->d3dResource();
fStagingOffset = slice.fOffset;
fMapPtr = slice.fOffsetMapPtr;
} else {
D3D12_RANGE range;
range.Begin = 0;
range.End = size;
fD3DResource->Map(0, &range, &fMapPtr);
}
VALIDATE();
}
void GrD3DBuffer::internalUnmap(size_t size) {
// TODO: if UPLOAD heap type, could be persistently mapped (i.e., this would be a no-op)
if (this->wasDestroyed()) {
return;
}
SkASSERT(fD3DResource);
SkASSERT(this->isMapped());
VALIDATE();
#ifdef SK_BUILD_FOR_MAC
// In both cases the size needs to be 4-byte aligned on Mac
sizeInBytes = SkAlign4(sizeInBytes);
#endif
if (this->accessPattern() == kStatic_GrAccessPattern) {
SkASSERT(fStagingBuffer);
this->setResourceState(this->getD3DGpu(), D3D12_RESOURCE_STATE_COPY_DEST);
this->getD3DGpu()->currentCommandList()->copyBufferToBuffer(
sk_ref_sp<GrD3DBuffer>(this), 0, fStagingBuffer, fStagingOffset, size);
fStagingBuffer = nullptr;
} else {
D3D12_RANGE range;
range.Begin = 0;
// For READBACK heaps, unmap requires an empty range
range.End = fResourceState == D3D12_RESOURCE_STATE_COPY_DEST ? 0 : size;
SkASSERT(this->size() >= size);
fD3DResource->Unmap(0, &range);
}
fMapPtr = nullptr;
VALIDATE();
}
#ifdef SK_DEBUG
void GrD3DBuffer::validate() const {
SkASSERT(this->intendedType() == GrGpuBufferType::kVertex ||
this->intendedType() == GrGpuBufferType::kIndex ||
this->intendedType() == GrGpuBufferType::kDrawIndirect ||
this->intendedType() == GrGpuBufferType::kXferCpuToGpu ||
this->intendedType() == GrGpuBufferType::kXferGpuToCpu);
}
#endif