1 /*
2 * Copyright 2020 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/gpu/ganesh/d3d/GrD3DBuffer.h"
9
10 #include "src/gpu/ganesh/d3d/GrD3DGpu.h"
11 #include "src/gpu/ganesh/d3d/GrD3DUtil.h"
12
13 #ifdef SK_DEBUG
14 #define VALIDATE() this->validate()
15 #else
16 #define VALIDATE() do {} while(false)
17 #endif
18
make_d3d_buffer(GrD3DGpu * gpu,size_t size,GrGpuBufferType intendedType,GrAccessPattern accessPattern,D3D12_RESOURCE_STATES * resourceState,sk_sp<GrD3DAlloc> * alloc)19 static gr_cp<ID3D12Resource> make_d3d_buffer(GrD3DGpu* gpu,
20 size_t size,
21 GrGpuBufferType intendedType,
22 GrAccessPattern accessPattern,
23 D3D12_RESOURCE_STATES* resourceState,
24 sk_sp<GrD3DAlloc>* alloc) {
25 D3D12_HEAP_TYPE heapType;
26 if (accessPattern == kStatic_GrAccessPattern) {
27 SkASSERT(intendedType != GrGpuBufferType::kXferCpuToGpu &&
28 intendedType != GrGpuBufferType::kXferGpuToCpu);
29 heapType = D3D12_HEAP_TYPE_DEFAULT;
30 // Needs to be transitioned to appropriate state to be read in shader
31 *resourceState = D3D12_RESOURCE_STATE_COPY_DEST;
32 } else {
33 if (intendedType == GrGpuBufferType::kXferGpuToCpu) {
34 heapType = D3D12_HEAP_TYPE_READBACK;
35 // Cannot be changed
36 *resourceState = D3D12_RESOURCE_STATE_COPY_DEST;
37 } else {
38 heapType = D3D12_HEAP_TYPE_UPLOAD;
39 // Cannot be changed
40 // Includes VERTEX_AND_CONSTANT_BUFFER, INDEX_BUFFER, INDIRECT_ARGUMENT, and COPY_SOURCE
41 *resourceState = D3D12_RESOURCE_STATE_GENERIC_READ;
42 }
43 }
44
45 D3D12_RESOURCE_DESC bufferDesc = {};
46 bufferDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
47 bufferDesc.Alignment = 0; // default alignment
48 bufferDesc.Width = size;
49 bufferDesc.Height = 1;
50 bufferDesc.DepthOrArraySize = 1;
51 bufferDesc.MipLevels = 1;
52 bufferDesc.Format = DXGI_FORMAT_UNKNOWN;
53 bufferDesc.SampleDesc.Count = 1;
54 bufferDesc.SampleDesc.Quality = 0; // Doesn't apply to buffers
55 bufferDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
56 bufferDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
57
58 gr_cp<ID3D12Resource> resource = gpu->memoryAllocator()->createResource(
59 heapType, &bufferDesc, *resourceState, alloc, nullptr);
60
61 return resource;
62 }
63
Make(GrD3DGpu * gpu,size_t size,GrGpuBufferType intendedType,GrAccessPattern accessPattern)64 sk_sp<GrD3DBuffer> GrD3DBuffer::Make(GrD3DGpu* gpu, size_t size, GrGpuBufferType intendedType,
65 GrAccessPattern accessPattern) {
66 SkASSERT(!gpu->protectedContext() || (accessPattern != kStatic_GrAccessPattern));
67 D3D12_RESOURCE_STATES resourceState;
68
69 sk_sp<GrD3DAlloc> alloc;
70 gr_cp<ID3D12Resource> resource = make_d3d_buffer(gpu, size, intendedType, accessPattern,
71 &resourceState, &alloc);
72 if (!resource) {
73 return nullptr;
74 }
75
76 return sk_sp<GrD3DBuffer>(new GrD3DBuffer(gpu, size, intendedType, accessPattern,
77 std::move(resource), std::move(alloc),
78 resourceState,
79 /*label=*/"MakeD3DBuffer"));
80 }
81
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)82 GrD3DBuffer::GrD3DBuffer(GrD3DGpu* gpu, size_t size, GrGpuBufferType intendedType,
83 GrAccessPattern accessPattern, gr_cp<ID3D12Resource> bufferResource,
84 sk_sp<GrD3DAlloc> alloc,
85 D3D12_RESOURCE_STATES resourceState,
86 std::string_view label)
87 : INHERITED(gpu, size, intendedType, accessPattern, label)
88 , fResourceState(resourceState)
89 , fD3DResource(std::move(bufferResource))
90 , fAlloc(std::move(alloc)) {
91 this->registerWithCache(skgpu::Budgeted::kYes);
92
93 // TODO: persistently map UPLOAD resources?
94
95 VALIDATE();
96 }
97
setResourceState(const GrD3DGpu * gpu,D3D12_RESOURCE_STATES newResourceState)98 void GrD3DBuffer::setResourceState(const GrD3DGpu* gpu,
99 D3D12_RESOURCE_STATES newResourceState) {
100 if (newResourceState == fResourceState ||
101 // GENERIC_READ encapsulates a lot of different read states
102 (fResourceState == D3D12_RESOURCE_STATE_GENERIC_READ &&
103 SkToBool(newResourceState | fResourceState))) {
104 return;
105 }
106
107 D3D12_RESOURCE_TRANSITION_BARRIER barrier = {};
108 barrier.pResource = this->d3dResource();
109 barrier.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
110 barrier.StateBefore = fResourceState;
111 barrier.StateAfter = newResourceState;
112
113 gpu->addBufferResourceBarriers(this, 1, &barrier);
114
115 fResourceState = newResourceState;
116 }
117
releaseResource()118 void GrD3DBuffer::releaseResource() {
119 if (this->wasDestroyed()) {
120 return;
121 }
122
123 if (fMapPtr) {
124 this->unmap();
125 }
126
127 SkASSERT(fD3DResource);
128 SkASSERT(fAlloc);
129 fD3DResource.reset();
130 fAlloc.reset();
131 }
132
onRelease()133 void GrD3DBuffer::onRelease() {
134 this->releaseResource();
135 this->INHERITED::onRelease();
136 }
137
onAbandon()138 void GrD3DBuffer::onAbandon() {
139 this->releaseResource();
140 this->INHERITED::onAbandon();
141 }
142
onMap(MapType type)143 void GrD3DBuffer::onMap(MapType type) {
144 fMapPtr = this->internalMap(type, 0, this->size());
145 }
146
onUnmap(MapType type)147 void GrD3DBuffer::onUnmap(MapType type) {
148 this->internalUnmap(type, 0, this->size());
149 }
150
onClearToZero()151 bool GrD3DBuffer::onClearToZero() {
152 if (!fD3DResource) {
153 return false;
154 }
155
156 if (this->accessPattern() == kStatic_GrAccessPattern) {
157 GrStagingBufferManager::Slice slice =
158 this->getD3DGpu()->stagingBufferManager()->allocateStagingBufferSlice(this->size());
159 if (!slice.fBuffer) {
160 return false;
161 }
162 std::memset(slice.fOffsetMapPtr, 0, this->size());
163 this->setResourceState(this->getD3DGpu(), D3D12_RESOURCE_STATE_COPY_DEST);
164 this->getD3DGpu()->currentCommandList()->copyBufferToBuffer(
165 sk_ref_sp<GrD3DBuffer>(this),
166 0,
167 static_cast<const GrD3DBuffer*>(slice.fBuffer)->d3dResource(),
168 slice.fOffset,
169 this->size());
170 return true;
171 }
172
173 void* ptr = this->internalMap(MapType::kWriteDiscard, 0, this->size());
174 if (!ptr) {
175 return false;
176 }
177 std::memset(ptr, 0, this->size());
178 this->internalUnmap(MapType::kWriteDiscard, 0, this->size());
179
180 return true;
181 }
182
onUpdateData(const void * src,size_t offset,size_t size,bool)183 bool GrD3DBuffer::onUpdateData(const void* src, size_t offset, size_t size, bool /*preserve*/) {
184 if (!fD3DResource) {
185 return false;
186 }
187
188 void* ptr = this->internalMap(MapType::kWriteDiscard, offset, size);
189 if (!ptr) {
190 return false;
191 }
192 if (this->accessPattern() == kStatic_GrAccessPattern) {
193 // We should never call this method on static buffers in protected contexts.
194 SkASSERT(!this->getD3DGpu()->protectedContext());
195 //*** any alignment restrictions?
196 }
197 memcpy(ptr, src, size);
198 this->internalUnmap(MapType::kWriteDiscard, offset, size);
199
200 return true;
201 }
202
internalMap(MapType type,size_t offset,size_t size)203 void* GrD3DBuffer::internalMap(MapType type, size_t offset, size_t size) {
204 // TODO: if UPLOAD heap type, could be persistently mapped (i.e., this would be a no-op)
205 SkASSERT(fD3DResource);
206 SkASSERT(!this->isMapped());
207 SkASSERT(offset + size <= this->size());
208
209 VALIDATE();
210
211 if (this->accessPattern() == kStatic_GrAccessPattern) {
212 if (type == MapType::kRead) {
213 return nullptr;
214 }
215 SkASSERT(!fStagingBuffer);
216 GrStagingBufferManager::Slice slice =
217 this->getD3DGpu()->stagingBufferManager()->allocateStagingBufferSlice(size);
218 if (!slice.fBuffer) {
219 return nullptr;
220 }
221 fStagingBuffer = static_cast<const GrD3DBuffer*>(slice.fBuffer)->d3dResource();
222 fStagingOffset = slice.fOffset;
223 VALIDATE();
224 return slice.fOffsetMapPtr;
225 }
226
227 D3D12_RANGE range;
228 range.Begin = offset;
229 // The range passed here indicates the portion of the resource that may be
230 // read. If we're only writing then pass an empty range.
231 range.End = type == MapType::kRead ? offset + size : offset;
232 void* result;
233 if (fD3DResource->Map(0, &range, &result) != S_OK) {
234 return nullptr;
235 }
236 if (result) {
237 result = SkTAddOffset<void>(result, offset);
238 }
239 VALIDATE();
240 return result;
241 }
242
internalUnmap(MapType type,size_t offset,size_t size)243 void GrD3DBuffer::internalUnmap(MapType type, size_t offset, size_t size) {
244 // TODO: if UPLOAD heap type, could be persistently mapped (i.e., this would be a no-op)
245 SkASSERT(fD3DResource);
246 SkASSERT(offset + size <= this->size());
247 VALIDATE();
248
249 if (this->accessPattern() == kStatic_GrAccessPattern) {
250 SkASSERT(type != GrGpuBuffer::MapType::kRead);
251 SkASSERT(fStagingBuffer);
252 this->setResourceState(this->getD3DGpu(), D3D12_RESOURCE_STATE_COPY_DEST);
253 this->getD3DGpu()->currentCommandList()->copyBufferToBuffer(
254 sk_ref_sp<GrD3DBuffer>(this),
255 offset,
256 fStagingBuffer,
257 fStagingOffset,
258 size);
259 fStagingBuffer = nullptr;
260 } else {
261 D3D12_RANGE range;
262 range.Begin = offset;
263 range.End = type == MapType::kWriteDiscard ? offset + size : offset;
264 // For READBACK heaps, unmap requires an empty range
265 SkASSERT(fResourceState != D3D12_RESOURCE_STATE_COPY_DEST || range.Begin == range.End);
266 fD3DResource->Unmap(0, &range);
267 }
268
269 VALIDATE();
270 }
271
onSetLabel()272 void GrD3DBuffer::onSetLabel() {
273 SkASSERT(fD3DResource);
274 if (!this->getLabel().empty()) {
275 const std::wstring label = L"_Skia_" + GrD3DMultiByteToWide(this->getLabel());
276 this->d3dResource()->SetName(label.c_str());
277 }
278 }
279
280 #ifdef SK_DEBUG
validate() const281 void GrD3DBuffer::validate() const {
282 SkASSERT(this->intendedType() == GrGpuBufferType::kVertex ||
283 this->intendedType() == GrGpuBufferType::kIndex ||
284 this->intendedType() == GrGpuBufferType::kDrawIndirect ||
285 this->intendedType() == GrGpuBufferType::kXferCpuToGpu ||
286 this->intendedType() == GrGpuBufferType::kXferGpuToCpu);
287 }
288 #endif
289