| /* | |
| * 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/ganesh/d3d/GrD3DBuffer.h" | |
| #include "src/gpu/ganesh/d3d/GrD3DGpu.h" | |
| #include "src/gpu/ganesh/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, | |
| /*label=*/"MakeD3DBuffer")); | |
| } | |
| GrD3DBuffer::GrD3DBuffer(GrD3DGpu* gpu, size_t size, GrGpuBufferType intendedType, | |
| GrAccessPattern accessPattern, gr_cp<ID3D12Resource> bufferResource, | |
| sk_sp<GrD3DAlloc> alloc, | |
| D3D12_RESOURCE_STATES resourceState, | |
| std::string_view label) | |
| : INHERITED(gpu, size, intendedType, accessPattern, label) | |
| , fResourceState(resourceState) | |
| , fD3DResource(std::move(bufferResource)) | |
| , fAlloc(std::move(alloc)) { | |
| this->registerWithCache(skgpu::Budgeted::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->unmap(); | |
| } | |
| 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(MapType type) { | |
| fMapPtr = this->internalMap(type, 0, this->size()); | |
| } | |
| void GrD3DBuffer::onUnmap(MapType type) { | |
| this->internalUnmap(type, 0, this->size()); | |
| } | |
| bool GrD3DBuffer::onClearToZero() { | |
| if (!fD3DResource) { | |
| return false; | |
| } | |
| if (this->accessPattern() == kStatic_GrAccessPattern) { | |
| GrStagingBufferManager::Slice slice = | |
| this->getD3DGpu()->stagingBufferManager()->allocateStagingBufferSlice(this->size()); | |
| if (!slice.fBuffer) { | |
| return false; | |
| } | |
| std::memset(slice.fOffsetMapPtr, 0, this->size()); | |
| this->setResourceState(this->getD3DGpu(), D3D12_RESOURCE_STATE_COPY_DEST); | |
| this->getD3DGpu()->currentCommandList()->copyBufferToBuffer( | |
| sk_ref_sp<GrD3DBuffer>(this), | |
| 0, | |
| static_cast<const GrD3DBuffer*>(slice.fBuffer)->d3dResource(), | |
| slice.fOffset, | |
| this->size()); | |
| return true; | |
| } | |
| void* ptr = this->internalMap(MapType::kWriteDiscard, 0, this->size()); | |
| if (!ptr) { | |
| return false; | |
| } | |
| std::memset(ptr, 0, this->size()); | |
| this->internalUnmap(MapType::kWriteDiscard, 0, this->size()); | |
| return true; | |
| } | |
| bool GrD3DBuffer::onUpdateData(const void* src, size_t offset, size_t size, bool /*preserve*/) { | |
| if (!fD3DResource) { | |
| return false; | |
| } | |
| void* ptr = this->internalMap(MapType::kWriteDiscard, offset, size); | |
| if (!ptr) { | |
| 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(ptr, src, size); | |
| this->internalUnmap(MapType::kWriteDiscard, offset, size); | |
| return true; | |
| } | |
| void* GrD3DBuffer::internalMap(MapType type, size_t offset, size_t size) { | |
| // TODO: if UPLOAD heap type, could be persistently mapped (i.e., this would be a no-op) | |
| SkASSERT(fD3DResource); | |
| SkASSERT(!this->isMapped()); | |
| SkASSERT(offset + size <= this->size()); | |
| VALIDATE(); | |
| if (this->accessPattern() == kStatic_GrAccessPattern) { | |
| if (type == MapType::kRead) { | |
| return nullptr; | |
| } | |
| SkASSERT(!fStagingBuffer); | |
| GrStagingBufferManager::Slice slice = | |
| this->getD3DGpu()->stagingBufferManager()->allocateStagingBufferSlice(size); | |
| if (!slice.fBuffer) { | |
| return nullptr; | |
| } | |
| fStagingBuffer = static_cast<const GrD3DBuffer*>(slice.fBuffer)->d3dResource(); | |
| fStagingOffset = slice.fOffset; | |
| VALIDATE(); | |
| return slice.fOffsetMapPtr; | |
| } | |
| D3D12_RANGE range; | |
| range.Begin = offset; | |
| // The range passed here indicates the portion of the resource that may be | |
| // read. If we're only writing then pass an empty range. | |
| range.End = type == MapType::kRead ? offset + size : offset; | |
| void* result; | |
| if (fD3DResource->Map(0, &range, &result) != S_OK) { | |
| return nullptr; | |
| } | |
| if (result) { | |
| result = SkTAddOffset<void>(result, offset); | |
| } | |
| VALIDATE(); | |
| return result; | |
| } | |
| void GrD3DBuffer::internalUnmap(MapType type, size_t offset, size_t size) { | |
| // TODO: if UPLOAD heap type, could be persistently mapped (i.e., this would be a no-op) | |
| SkASSERT(fD3DResource); | |
| SkASSERT(offset + size <= this->size()); | |
| VALIDATE(); | |
| if (this->accessPattern() == kStatic_GrAccessPattern) { | |
| SkASSERT(type != GrGpuBuffer::MapType::kRead); | |
| SkASSERT(fStagingBuffer); | |
| this->setResourceState(this->getD3DGpu(), D3D12_RESOURCE_STATE_COPY_DEST); | |
| this->getD3DGpu()->currentCommandList()->copyBufferToBuffer( | |
| sk_ref_sp<GrD3DBuffer>(this), | |
| offset, | |
| fStagingBuffer, | |
| fStagingOffset, | |
| size); | |
| fStagingBuffer = nullptr; | |
| } else { | |
| D3D12_RANGE range; | |
| range.Begin = offset; | |
| range.End = type == MapType::kWriteDiscard ? offset + size : offset; | |
| // For READBACK heaps, unmap requires an empty range | |
| SkASSERT(fResourceState != D3D12_RESOURCE_STATE_COPY_DEST || range.Begin == range.End); | |
| fD3DResource->Unmap(0, &range); | |
| } | |
| VALIDATE(); | |
| } | |
| void GrD3DBuffer::onSetLabel() { | |
| SkASSERT(fD3DResource); | |
| if (!this->getLabel().empty()) { | |
| const std::wstring label = L"_Skia_" + GrD3DMultiByteToWide(this->getLabel()); | |
| this->d3dResource()->SetName(label.c_str()); | |
| } | |
| } | |
| #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 |