1 //
2 // Copyright 2014 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // Buffer11.cpp Defines the Buffer11 class.
8
9 #include "libANGLE/renderer/d3d/d3d11/Buffer11.h"
10
11 #include <memory>
12
13 #include "common/MemoryBuffer.h"
14 #include "libANGLE/Context.h"
15 #include "libANGLE/renderer/d3d/IndexDataManager.h"
16 #include "libANGLE/renderer/d3d/VertexDataManager.h"
17 #include "libANGLE/renderer/d3d/d3d11/Context11.h"
18 #include "libANGLE/renderer/d3d/d3d11/RenderTarget11.h"
19 #include "libANGLE/renderer/d3d/d3d11/Renderer11.h"
20 #include "libANGLE/renderer/d3d/d3d11/formatutils11.h"
21 #include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
22 #include "libANGLE/renderer/renderer_utils.h"
23
24 namespace rx
25 {
26
27 namespace
28 {
29
30 template <typename T>
ReadIndexValueFromIndices(const uint8_t * data,size_t index)31 GLuint ReadIndexValueFromIndices(const uint8_t *data, size_t index)
32 {
33 return reinterpret_cast<const T *>(data)[index];
34 }
35 typedef GLuint (*ReadIndexValueFunction)(const uint8_t *data, size_t index);
36
37 enum class CopyResult
38 {
39 RECREATED,
40 NOT_RECREATED,
41 };
42
CalculateConstantBufferParams(GLintptr offset,GLsizeiptr size,UINT * outFirstConstant,UINT * outNumConstants)43 void CalculateConstantBufferParams(GLintptr offset,
44 GLsizeiptr size,
45 UINT *outFirstConstant,
46 UINT *outNumConstants)
47 {
48 // The offset must be aligned to 256 bytes (should have been enforced by glBindBufferRange).
49 ASSERT(offset % 256 == 0);
50
51 // firstConstant and numConstants are expressed in constants of 16-bytes. Furthermore they must
52 // be a multiple of 16 constants.
53 *outFirstConstant = static_cast<UINT>(offset / 16);
54
55 // The GL size is not required to be aligned to a 256 bytes boundary.
56 // Round the size up to a 256 bytes boundary then express the results in constants of 16-bytes.
57 *outNumConstants = static_cast<UINT>(rx::roundUpPow2(size, static_cast<GLsizeiptr>(256)) / 16);
58
59 // Since the size is rounded up, firstConstant + numConstants may be bigger than the actual size
60 // of the buffer. This behaviour is explictly allowed according to the documentation on
61 // ID3D11DeviceContext1::PSSetConstantBuffers1
62 // https://msdn.microsoft.com/en-us/library/windows/desktop/hh404649%28v=vs.85%29.aspx
63 }
64
65 } // anonymous namespace
66
67 namespace gl_d3d11
68 {
69
GetD3DMapTypeFromBits(BufferUsage usage,GLbitfield access)70 D3D11_MAP GetD3DMapTypeFromBits(BufferUsage usage, GLbitfield access)
71 {
72 bool readBit = ((access & GL_MAP_READ_BIT) != 0);
73 bool writeBit = ((access & GL_MAP_WRITE_BIT) != 0);
74
75 ASSERT(readBit || writeBit);
76
77 // Note : we ignore the discard bit, because in D3D11, staging buffers
78 // don't accept the map-discard flag (discard only works for DYNAMIC usage)
79
80 if (readBit && !writeBit)
81 {
82 return D3D11_MAP_READ;
83 }
84 else if (writeBit && !readBit)
85 {
86 // Special case for uniform storage - we only allow full buffer updates.
87 return usage == BUFFER_USAGE_UNIFORM || usage == BUFFER_USAGE_STRUCTURED
88 ? D3D11_MAP_WRITE_DISCARD
89 : D3D11_MAP_WRITE;
90 }
91 else if (writeBit && readBit)
92 {
93 return D3D11_MAP_READ_WRITE;
94 }
95 else
96 {
97 UNREACHABLE();
98 return D3D11_MAP_READ;
99 }
100 }
101 } // namespace gl_d3d11
102
103 // Each instance of Buffer11::BufferStorage is specialized for a class of D3D binding points
104 // - vertex/transform feedback buffers
105 // - index buffers
106 // - pixel unpack buffers
107 // - uniform buffers
108 class Buffer11::BufferStorage : angle::NonCopyable
109 {
110 public:
~BufferStorage()111 virtual ~BufferStorage() {}
112
getDataRevision() const113 DataRevision getDataRevision() const { return mRevision; }
getUsage() const114 BufferUsage getUsage() const { return mUsage; }
getSize() const115 size_t getSize() const { return mBufferSize; }
setDataRevision(DataRevision rev)116 void setDataRevision(DataRevision rev) { mRevision = rev; }
117
118 virtual bool isCPUAccessible(GLbitfield access) const = 0;
119
120 virtual bool isGPUAccessible() const = 0;
121
122 virtual angle::Result copyFromStorage(const gl::Context *context,
123 BufferStorage *source,
124 size_t sourceOffset,
125 size_t size,
126 size_t destOffset,
127 CopyResult *resultOut) = 0;
128 virtual angle::Result resize(const gl::Context *context, size_t size, bool preserveData) = 0;
129
130 virtual angle::Result map(const gl::Context *context,
131 size_t offset,
132 size_t length,
133 GLbitfield access,
134 uint8_t **mapPointerOut) = 0;
135 virtual void unmap() = 0;
136
137 angle::Result setData(const gl::Context *context,
138 const uint8_t *data,
139 size_t offset,
140 size_t size);
141
142 void setStructureByteStride(unsigned int structureByteStride);
143
144 protected:
145 BufferStorage(Renderer11 *renderer, BufferUsage usage);
146
147 Renderer11 *mRenderer;
148 DataRevision mRevision;
149 const BufferUsage mUsage;
150 size_t mBufferSize;
151 };
152
153 // A native buffer storage represents an underlying D3D11 buffer for a particular
154 // type of storage.
155 class Buffer11::NativeStorage : public Buffer11::BufferStorage
156 {
157 public:
158 NativeStorage(Renderer11 *renderer, BufferUsage usage, const angle::Subject *onStorageChanged);
159 ~NativeStorage() override;
160
161 bool isCPUAccessible(GLbitfield access) const override;
162
isGPUAccessible() const163 bool isGPUAccessible() const override { return true; }
164
getBuffer() const165 const d3d11::Buffer &getBuffer() const { return mBuffer; }
166 angle::Result copyFromStorage(const gl::Context *context,
167 BufferStorage *source,
168 size_t sourceOffset,
169 size_t size,
170 size_t destOffset,
171 CopyResult *resultOut) override;
172 angle::Result resize(const gl::Context *context, size_t size, bool preserveData) override;
173
174 angle::Result map(const gl::Context *context,
175 size_t offset,
176 size_t length,
177 GLbitfield access,
178 uint8_t **mapPointerOut) override;
179 void unmap() override;
180
181 angle::Result getSRVForFormat(const gl::Context *context,
182 DXGI_FORMAT srvFormat,
183 const d3d11::ShaderResourceView **srvOut);
184 angle::Result getRawUAV(const gl::Context *context,
185 unsigned int offset,
186 unsigned int size,
187 d3d11::UnorderedAccessView **uavOut);
188
189 protected:
190 d3d11::Buffer mBuffer;
191 const angle::Subject *mOnStorageChanged;
192
193 private:
194 static void FillBufferDesc(D3D11_BUFFER_DESC *bufferDesc,
195 Renderer11 *renderer,
196 BufferUsage usage,
197 unsigned int bufferSize);
198 void clearSRVs();
199 void clearUAVs();
200
201 std::map<DXGI_FORMAT, d3d11::ShaderResourceView> mBufferResourceViews;
202 std::map<std::pair<unsigned int, unsigned int>, d3d11::UnorderedAccessView> mBufferRawUAVs;
203 };
204
205 class Buffer11::StructuredBufferStorage : public Buffer11::NativeStorage
206 {
207 public:
208 StructuredBufferStorage(Renderer11 *renderer,
209 BufferUsage usage,
210 const angle::Subject *onStorageChanged);
211 ~StructuredBufferStorage() override;
212 angle::Result resizeStructuredBuffer(const gl::Context *context,
213 unsigned int size,
214 unsigned int structureByteStride);
215 angle::Result getStructuredBufferRangeSRV(const gl::Context *context,
216 unsigned int offset,
217 unsigned int size,
218 unsigned int structureByteStride,
219 const d3d11::ShaderResourceView **bufferOut);
220
221 private:
222 d3d11::ShaderResourceView mStructuredBufferResourceView;
223 };
224
225 // Pack storage represents internal storage for pack buffers. We implement pack buffers
226 // as CPU memory, tied to a staging texture, for asynchronous texture readback.
227 class Buffer11::PackStorage : public Buffer11::BufferStorage
228 {
229 public:
230 explicit PackStorage(Renderer11 *renderer);
231 ~PackStorage() override;
232
isCPUAccessible(GLbitfield access) const233 bool isCPUAccessible(GLbitfield access) const override { return true; }
234
isGPUAccessible() const235 bool isGPUAccessible() const override { return false; }
236
237 angle::Result copyFromStorage(const gl::Context *context,
238 BufferStorage *source,
239 size_t sourceOffset,
240 size_t size,
241 size_t destOffset,
242 CopyResult *resultOut) override;
243 angle::Result resize(const gl::Context *context, size_t size, bool preserveData) override;
244
245 angle::Result map(const gl::Context *context,
246 size_t offset,
247 size_t length,
248 GLbitfield access,
249 uint8_t **mapPointerOut) override;
250 void unmap() override;
251
252 angle::Result packPixels(const gl::Context *context,
253 const gl::FramebufferAttachment &readAttachment,
254 const PackPixelsParams ¶ms);
255
256 private:
257 angle::Result flushQueuedPackCommand(const gl::Context *context);
258
259 TextureHelper11 mStagingTexture;
260 angle::MemoryBuffer mMemoryBuffer;
261 std::unique_ptr<PackPixelsParams> mQueuedPackCommand;
262 PackPixelsParams mPackParams;
263 bool mDataModified;
264 };
265
266 // System memory storage stores a CPU memory buffer with our buffer data.
267 // For dynamic data, it's much faster to update the CPU memory buffer than
268 // it is to update a D3D staging buffer and read it back later.
269 class Buffer11::SystemMemoryStorage : public Buffer11::BufferStorage
270 {
271 public:
272 explicit SystemMemoryStorage(Renderer11 *renderer);
~SystemMemoryStorage()273 ~SystemMemoryStorage() override {}
274
isCPUAccessible(GLbitfield access) const275 bool isCPUAccessible(GLbitfield access) const override { return true; }
276
isGPUAccessible() const277 bool isGPUAccessible() const override { return false; }
278
279 angle::Result copyFromStorage(const gl::Context *context,
280 BufferStorage *source,
281 size_t sourceOffset,
282 size_t size,
283 size_t destOffset,
284 CopyResult *resultOut) override;
285 angle::Result resize(const gl::Context *context, size_t size, bool preserveData) override;
286
287 angle::Result map(const gl::Context *context,
288 size_t offset,
289 size_t length,
290 GLbitfield access,
291 uint8_t **mapPointerOut) override;
292 void unmap() override;
293
getSystemCopy()294 angle::MemoryBuffer *getSystemCopy() { return &mSystemCopy; }
295
296 protected:
297 angle::MemoryBuffer mSystemCopy;
298 };
299
Buffer11(const gl::BufferState & state,Renderer11 * renderer)300 Buffer11::Buffer11(const gl::BufferState &state, Renderer11 *renderer)
301 : BufferD3D(state, renderer),
302 mRenderer(renderer),
303 mSize(0),
304 mMappedStorage(nullptr),
305 mBufferStorages({}),
306 mLatestBufferStorage(nullptr),
307 mDeallocThresholds({}),
308 mIdleness({}),
309 mConstantBufferStorageAdditionalSize(0),
310 mMaxConstantBufferLruCount(0),
311 mStructuredBufferStorageAdditionalSize(0),
312 mMaxStructuredBufferLruCount(0)
313 {}
314
~Buffer11()315 Buffer11::~Buffer11()
316 {
317 for (BufferStorage *&storage : mBufferStorages)
318 {
319 SafeDelete(storage);
320 }
321
322 for (auto &p : mConstantBufferRangeStoragesCache)
323 {
324 SafeDelete(p.second.storage);
325 }
326
327 for (auto &p : mStructuredBufferRangeStoragesCache)
328 {
329 SafeDelete(p.second.storage);
330 }
331
332 mRenderer->onBufferDelete(this);
333 }
334
setData(const gl::Context * context,gl::BufferBinding target,const void * data,size_t size,gl::BufferUsage usage)335 angle::Result Buffer11::setData(const gl::Context *context,
336 gl::BufferBinding target,
337 const void *data,
338 size_t size,
339 gl::BufferUsage usage)
340 {
341 updateD3DBufferUsage(context, usage);
342 return setSubData(context, target, data, size, 0);
343 }
344
getData(const gl::Context * context,const uint8_t ** outData)345 angle::Result Buffer11::getData(const gl::Context *context, const uint8_t **outData)
346 {
347 if (mSize == 0)
348 {
349 // TODO(http://anglebug.com/42261543): This ensures that we don't crash or assert in robust
350 // buffer access behavior mode if there are buffers without any data. However, technically
351 // it should still be possible to draw, with fetches from this buffer returning zero.
352 return angle::Result::Stop;
353 }
354
355 SystemMemoryStorage *systemMemoryStorage = nullptr;
356 ANGLE_TRY(getBufferStorage(context, BUFFER_USAGE_SYSTEM_MEMORY, &systemMemoryStorage));
357
358 ASSERT(systemMemoryStorage->getSize() >= mSize);
359
360 *outData = systemMemoryStorage->getSystemCopy()->data();
361 return angle::Result::Continue;
362 }
363
setSubData(const gl::Context * context,gl::BufferBinding target,const void * data,size_t size,size_t offset)364 angle::Result Buffer11::setSubData(const gl::Context *context,
365 gl::BufferBinding target,
366 const void *data,
367 size_t size,
368 size_t offset)
369 {
370 size_t requiredSize = size + offset;
371
372 if (data && size > 0)
373 {
374 // Use system memory storage for dynamic buffers.
375 // Try using a constant storage for constant buffers
376 BufferStorage *writeBuffer = nullptr;
377 if (target == gl::BufferBinding::Uniform)
378 {
379 // If we are a very large uniform buffer, keep system memory storage around so that we
380 // aren't forced to read back from a constant buffer. We also check the workaround for
381 // Intel - this requires us to use system memory so we don't end up having to copy from
382 // a constant buffer to a staging buffer.
383 // TODO(jmadill): Use Context caps.
384 if (offset == 0 && size >= mSize &&
385 size <= static_cast<UINT>(mRenderer->getNativeCaps().maxUniformBlockSize) &&
386 !mRenderer->getFeatures().useSystemMemoryForConstantBuffers.enabled)
387 {
388 BufferStorage *latestStorage = nullptr;
389 ANGLE_TRY(getLatestBufferStorage(context, &latestStorage));
390 if (latestStorage && (latestStorage->getUsage() == BUFFER_USAGE_STRUCTURED))
391 {
392 ANGLE_TRY(getBufferStorage(context, BUFFER_USAGE_STRUCTURED, &writeBuffer));
393 }
394 else
395 {
396 ANGLE_TRY(getBufferStorage(context, BUFFER_USAGE_UNIFORM, &writeBuffer));
397 }
398 }
399 else
400 {
401 ANGLE_TRY(getBufferStorage(context, BUFFER_USAGE_SYSTEM_MEMORY, &writeBuffer));
402 }
403 }
404 else if (supportsDirectBinding())
405 {
406 ANGLE_TRY(getStagingStorage(context, &writeBuffer));
407 }
408 else
409 {
410 ANGLE_TRY(getBufferStorage(context, BUFFER_USAGE_SYSTEM_MEMORY, &writeBuffer));
411 }
412
413 ASSERT(writeBuffer);
414
415 // Explicitly resize the staging buffer, preserving data if the new data will not
416 // completely fill the buffer
417 if (writeBuffer->getSize() < requiredSize)
418 {
419 bool preserveData = (offset > 0);
420 ANGLE_TRY(writeBuffer->resize(context, requiredSize, preserveData));
421 }
422
423 ANGLE_TRY(writeBuffer->setData(context, static_cast<const uint8_t *>(data), offset, size));
424 onStorageUpdate(writeBuffer);
425 }
426
427 mSize = std::max(mSize, requiredSize);
428 invalidateStaticData(context);
429
430 return angle::Result::Continue;
431 }
432
copySubData(const gl::Context * context,BufferImpl * source,GLintptr sourceOffset,GLintptr destOffset,GLsizeiptr size)433 angle::Result Buffer11::copySubData(const gl::Context *context,
434 BufferImpl *source,
435 GLintptr sourceOffset,
436 GLintptr destOffset,
437 GLsizeiptr size)
438 {
439 Buffer11 *sourceBuffer = GetAs<Buffer11>(source);
440 ASSERT(sourceBuffer != nullptr);
441
442 BufferStorage *copyDest = nullptr;
443 ANGLE_TRY(getLatestBufferStorage(context, ©Dest));
444
445 if (!copyDest)
446 {
447 ANGLE_TRY(getStagingStorage(context, ©Dest));
448 }
449
450 BufferStorage *copySource = nullptr;
451 ANGLE_TRY(sourceBuffer->getLatestBufferStorage(context, ©Source));
452
453 if (!copySource)
454 {
455 ANGLE_TRY(sourceBuffer->getStagingStorage(context, ©Source));
456 }
457
458 ASSERT(copySource && copyDest);
459
460 // A staging buffer is needed if there is no cpu-cpu or gpu-gpu copy path avaiable.
461 if (!copyDest->isGPUAccessible() && !copySource->isCPUAccessible(GL_MAP_READ_BIT))
462 {
463 ANGLE_TRY(sourceBuffer->getStagingStorage(context, ©Source));
464 }
465 else if (!copySource->isGPUAccessible() && !copyDest->isCPUAccessible(GL_MAP_WRITE_BIT))
466 {
467 ANGLE_TRY(getStagingStorage(context, ©Dest));
468 }
469
470 // D3D11 does not allow overlapped copies until 11.1, and only if the
471 // device supports D3D11_FEATURE_DATA_D3D11_OPTIONS::CopyWithOverlap
472 // Get around this via a different source buffer
473 if (copySource == copyDest)
474 {
475 if (copySource->getUsage() == BUFFER_USAGE_STAGING)
476 {
477 ANGLE_TRY(
478 getBufferStorage(context, BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK, ©Source));
479 }
480 else
481 {
482 ANGLE_TRY(getStagingStorage(context, ©Source));
483 }
484 }
485
486 CopyResult copyResult = CopyResult::NOT_RECREATED;
487 ANGLE_TRY(copyDest->copyFromStorage(context, copySource, sourceOffset, size, destOffset,
488 ©Result));
489 onStorageUpdate(copyDest);
490
491 mSize = std::max<size_t>(mSize, destOffset + size);
492 invalidateStaticData(context);
493
494 return angle::Result::Continue;
495 }
496
map(const gl::Context * context,GLenum access,void ** mapPtr)497 angle::Result Buffer11::map(const gl::Context *context, GLenum access, void **mapPtr)
498 {
499 // GL_OES_mapbuffer uses an enum instead of a bitfield for it's access, convert to a bitfield
500 // and call mapRange.
501 ASSERT(access == GL_WRITE_ONLY_OES);
502 return mapRange(context, 0, mSize, GL_MAP_WRITE_BIT, mapPtr);
503 }
504
mapRange(const gl::Context * context,size_t offset,size_t length,GLbitfield access,void ** mapPtr)505 angle::Result Buffer11::mapRange(const gl::Context *context,
506 size_t offset,
507 size_t length,
508 GLbitfield access,
509 void **mapPtr)
510 {
511 ASSERT(!mMappedStorage);
512
513 BufferStorage *latestStorage = nullptr;
514 ANGLE_TRY(getLatestBufferStorage(context, &latestStorage));
515
516 if (latestStorage && (latestStorage->getUsage() == BUFFER_USAGE_PIXEL_PACK ||
517 latestStorage->getUsage() == BUFFER_USAGE_STAGING))
518 {
519 // Latest storage is mappable.
520 mMappedStorage = latestStorage;
521 }
522 else
523 {
524 // Fall back to using the staging buffer if the latest storage does not exist or is not
525 // CPU-accessible.
526 ANGLE_TRY(getStagingStorage(context, &mMappedStorage));
527 }
528
529 Context11 *context11 = GetImplAs<Context11>(context);
530 ANGLE_CHECK_GL_ALLOC(context11, mMappedStorage);
531
532 if ((access & GL_MAP_WRITE_BIT) > 0)
533 {
534 // Update the data revision immediately, since the data might be changed at any time
535 onStorageUpdate(mMappedStorage);
536 invalidateStaticData(context);
537 }
538
539 uint8_t *mappedBuffer = nullptr;
540 ANGLE_TRY(mMappedStorage->map(context, offset, length, access, &mappedBuffer));
541 ASSERT(mappedBuffer);
542
543 *mapPtr = static_cast<void *>(mappedBuffer);
544 return angle::Result::Continue;
545 }
546
unmap(const gl::Context * context,GLboolean * result)547 angle::Result Buffer11::unmap(const gl::Context *context, GLboolean *result)
548 {
549 ASSERT(mMappedStorage);
550 mMappedStorage->unmap();
551 mMappedStorage = nullptr;
552
553 // TODO: detect if we had corruption. if so, return false.
554 *result = GL_TRUE;
555
556 return angle::Result::Continue;
557 }
558
markTransformFeedbackUsage(const gl::Context * context)559 angle::Result Buffer11::markTransformFeedbackUsage(const gl::Context *context)
560 {
561 ANGLE_TRY(markBufferUsage(context, BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK));
562 return angle::Result::Continue;
563 }
564
updateDeallocThreshold(BufferUsage usage)565 void Buffer11::updateDeallocThreshold(BufferUsage usage)
566 {
567 // The following strategy was tuned on the Oort online benchmark (http://oortonline.gl/)
568 // as well as a custom microbenchmark (IndexConversionPerfTest.Run/index_range_d3d11)
569
570 // First readback: 8 unmodified uses before we free buffer memory.
571 // After that, double the threshold each time until we reach the max.
572 if (mDeallocThresholds[usage] == 0)
573 {
574 mDeallocThresholds[usage] = 8;
575 }
576 else if (mDeallocThresholds[usage] < std::numeric_limits<unsigned int>::max() / 2u)
577 {
578 mDeallocThresholds[usage] *= 2u;
579 }
580 else
581 {
582 mDeallocThresholds[usage] = std::numeric_limits<unsigned int>::max();
583 }
584 }
585
586 // Free the storage if we decide it isn't being used very often.
checkForDeallocation(const gl::Context * context,BufferUsage usage)587 angle::Result Buffer11::checkForDeallocation(const gl::Context *context, BufferUsage usage)
588 {
589 mIdleness[usage]++;
590
591 BufferStorage *&storage = mBufferStorages[usage];
592 if (storage != nullptr && mIdleness[usage] > mDeallocThresholds[usage])
593 {
594 BufferStorage *latestStorage = nullptr;
595 ANGLE_TRY(getLatestBufferStorage(context, &latestStorage));
596 if (latestStorage != storage)
597 {
598 SafeDelete(storage);
599 }
600 }
601
602 return angle::Result::Continue;
603 }
604
605 // Keep system memory when we are using it for the canonical version of data.
canDeallocateSystemMemory() const606 bool Buffer11::canDeallocateSystemMemory() const
607 {
608 // Must keep system memory on Intel.
609 if (mRenderer->getFeatures().useSystemMemoryForConstantBuffers.enabled)
610 {
611 return false;
612 }
613
614 return (!mBufferStorages[BUFFER_USAGE_UNIFORM] ||
615 mSize <= static_cast<size_t>(mRenderer->getNativeCaps().maxUniformBlockSize));
616 }
617
markBufferUsage(BufferUsage usage)618 void Buffer11::markBufferUsage(BufferUsage usage)
619 {
620 mIdleness[usage] = 0;
621 }
622
markBufferUsage(const gl::Context * context,BufferUsage usage)623 angle::Result Buffer11::markBufferUsage(const gl::Context *context, BufferUsage usage)
624 {
625 BufferStorage *bufferStorage = nullptr;
626 ANGLE_TRY(getBufferStorage(context, usage, &bufferStorage));
627
628 if (bufferStorage)
629 {
630 onStorageUpdate(bufferStorage);
631 }
632
633 invalidateStaticData(context);
634 return angle::Result::Continue;
635 }
636
garbageCollection(const gl::Context * context,BufferUsage currentUsage)637 angle::Result Buffer11::garbageCollection(const gl::Context *context, BufferUsage currentUsage)
638 {
639 if (currentUsage != BUFFER_USAGE_SYSTEM_MEMORY && canDeallocateSystemMemory())
640 {
641 ANGLE_TRY(checkForDeallocation(context, BUFFER_USAGE_SYSTEM_MEMORY));
642 }
643
644 if (currentUsage != BUFFER_USAGE_STAGING)
645 {
646 ANGLE_TRY(checkForDeallocation(context, BUFFER_USAGE_STAGING));
647 }
648
649 return angle::Result::Continue;
650 }
651
getBuffer(const gl::Context * context,BufferUsage usage,ID3D11Buffer ** bufferOut)652 angle::Result Buffer11::getBuffer(const gl::Context *context,
653 BufferUsage usage,
654 ID3D11Buffer **bufferOut)
655 {
656 NativeStorage *storage = nullptr;
657 ANGLE_TRY(getBufferStorage(context, usage, &storage));
658 *bufferOut = storage->getBuffer().get();
659 return angle::Result::Continue;
660 }
661
getConstantBufferRange(const gl::Context * context,GLintptr offset,GLsizeiptr size,const d3d11::Buffer ** bufferOut,UINT * firstConstantOut,UINT * numConstantsOut)662 angle::Result Buffer11::getConstantBufferRange(const gl::Context *context,
663 GLintptr offset,
664 GLsizeiptr size,
665 const d3d11::Buffer **bufferOut,
666 UINT *firstConstantOut,
667 UINT *numConstantsOut)
668 {
669 NativeStorage *bufferStorage = nullptr;
670 if ((offset == 0 &&
671 size < static_cast<GLsizeiptr>(mRenderer->getNativeCaps().maxUniformBlockSize)) ||
672 mRenderer->getRenderer11DeviceCaps().supportsConstantBufferOffsets)
673 {
674 ANGLE_TRY(getBufferStorage(context, BUFFER_USAGE_UNIFORM, &bufferStorage));
675 CalculateConstantBufferParams(offset, size, firstConstantOut, numConstantsOut);
676 }
677 else
678 {
679 ANGLE_TRY(getConstantBufferRangeStorage(context, offset, size, &bufferStorage));
680 *firstConstantOut = 0;
681 *numConstantsOut = 0;
682 }
683
684 *bufferOut = &bufferStorage->getBuffer();
685 return angle::Result::Continue;
686 }
687
markRawBufferUsage(const gl::Context * context)688 angle::Result Buffer11::markRawBufferUsage(const gl::Context *context)
689 {
690 ANGLE_TRY(markBufferUsage(context, BUFFER_USAGE_RAW_UAV));
691 return angle::Result::Continue;
692 }
693
markTypedBufferUsage(const gl::Context * context)694 angle::Result Buffer11::markTypedBufferUsage(const gl::Context *context)
695 {
696 ANGLE_TRY(markBufferUsage(context, BUFFER_USAGE_TYPED_UAV));
697 return angle::Result::Continue;
698 }
699
getRawUAVRange(const gl::Context * context,GLintptr offset,GLsizeiptr size,d3d11::UnorderedAccessView ** uavOut)700 angle::Result Buffer11::getRawUAVRange(const gl::Context *context,
701 GLintptr offset,
702 GLsizeiptr size,
703 d3d11::UnorderedAccessView **uavOut)
704 {
705 NativeStorage *nativeStorage = nullptr;
706 ANGLE_TRY(getBufferStorage(context, BUFFER_USAGE_RAW_UAV, &nativeStorage));
707
708 return nativeStorage->getRawUAV(context, static_cast<unsigned int>(offset),
709 static_cast<unsigned int>(size), uavOut);
710 }
711
getSRV(const gl::Context * context,DXGI_FORMAT srvFormat,const d3d11::ShaderResourceView ** srvOut)712 angle::Result Buffer11::getSRV(const gl::Context *context,
713 DXGI_FORMAT srvFormat,
714 const d3d11::ShaderResourceView **srvOut)
715 {
716 NativeStorage *nativeStorage = nullptr;
717 ANGLE_TRY(getBufferStorage(context, BUFFER_USAGE_PIXEL_UNPACK, &nativeStorage));
718 return nativeStorage->getSRVForFormat(context, srvFormat, srvOut);
719 }
720
packPixels(const gl::Context * context,const gl::FramebufferAttachment & readAttachment,const PackPixelsParams & params)721 angle::Result Buffer11::packPixels(const gl::Context *context,
722 const gl::FramebufferAttachment &readAttachment,
723 const PackPixelsParams ¶ms)
724 {
725 PackStorage *packStorage = nullptr;
726 ANGLE_TRY(getBufferStorage(context, BUFFER_USAGE_PIXEL_PACK, &packStorage));
727
728 ASSERT(packStorage);
729 ANGLE_TRY(packStorage->packPixels(context, readAttachment, params));
730 onStorageUpdate(packStorage);
731
732 return angle::Result::Continue;
733 }
734
getTotalCPUBufferMemoryBytes() const735 size_t Buffer11::getTotalCPUBufferMemoryBytes() const
736 {
737 size_t allocationSize = 0;
738
739 BufferStorage *staging = mBufferStorages[BUFFER_USAGE_STAGING];
740 allocationSize += staging ? staging->getSize() : 0;
741
742 BufferStorage *sysMem = mBufferStorages[BUFFER_USAGE_SYSTEM_MEMORY];
743 allocationSize += sysMem ? sysMem->getSize() : 0;
744
745 return allocationSize;
746 }
747
748 template <typename StorageOutT>
getBufferStorage(const gl::Context * context,BufferUsage usage,StorageOutT ** storageOut)749 angle::Result Buffer11::getBufferStorage(const gl::Context *context,
750 BufferUsage usage,
751 StorageOutT **storageOut)
752 {
753 ASSERT(0 <= usage && usage < BUFFER_USAGE_COUNT);
754 BufferStorage *&newStorage = mBufferStorages[usage];
755
756 if (!newStorage)
757 {
758 newStorage = allocateStorage(usage);
759 }
760
761 markBufferUsage(usage);
762
763 // resize buffer
764 if (newStorage->getSize() < mSize)
765 {
766 ANGLE_TRY(newStorage->resize(context, mSize, true));
767 }
768
769 ASSERT(newStorage);
770
771 ANGLE_TRY(updateBufferStorage(context, newStorage, 0, mSize));
772 ANGLE_TRY(garbageCollection(context, usage));
773
774 *storageOut = GetAs<StorageOutT>(newStorage);
775 return angle::Result::Continue;
776 }
777
allocateStorage(BufferUsage usage)778 Buffer11::BufferStorage *Buffer11::allocateStorage(BufferUsage usage)
779 {
780 updateDeallocThreshold(usage);
781 switch (usage)
782 {
783 case BUFFER_USAGE_PIXEL_PACK:
784 return new PackStorage(mRenderer);
785 case BUFFER_USAGE_SYSTEM_MEMORY:
786 return new SystemMemoryStorage(mRenderer);
787 case BUFFER_USAGE_INDEX:
788 case BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK:
789 return new NativeStorage(mRenderer, usage, this);
790 case BUFFER_USAGE_STRUCTURED:
791 return new StructuredBufferStorage(mRenderer, usage, nullptr);
792 default:
793 return new NativeStorage(mRenderer, usage, nullptr);
794 }
795 }
796
getConstantBufferRangeStorage(const gl::Context * context,GLintptr offset,GLsizeiptr size,Buffer11::NativeStorage ** storageOut)797 angle::Result Buffer11::getConstantBufferRangeStorage(const gl::Context *context,
798 GLintptr offset,
799 GLsizeiptr size,
800 Buffer11::NativeStorage **storageOut)
801 {
802 BufferStorage *newStorage;
803 {
804 // Keep the cacheEntry in a limited scope because it may be invalidated later in the code if
805 // we need to reclaim some space.
806 BufferCacheEntry *cacheEntry = &mConstantBufferRangeStoragesCache[offset];
807
808 if (!cacheEntry->storage)
809 {
810 cacheEntry->storage = allocateStorage(BUFFER_USAGE_UNIFORM);
811 cacheEntry->lruCount = ++mMaxConstantBufferLruCount;
812 }
813
814 cacheEntry->lruCount = ++mMaxConstantBufferLruCount;
815 newStorage = cacheEntry->storage;
816 }
817
818 markBufferUsage(BUFFER_USAGE_UNIFORM);
819
820 if (newStorage->getSize() < static_cast<size_t>(size))
821 {
822 size_t maximumAllowedAdditionalSize = 2 * getSize();
823
824 size_t sizeDelta = size - newStorage->getSize();
825
826 while (mConstantBufferStorageAdditionalSize + sizeDelta > maximumAllowedAdditionalSize)
827 {
828 auto iter = std::min_element(
829 std::begin(mConstantBufferRangeStoragesCache),
830 std::end(mConstantBufferRangeStoragesCache),
831 [](const BufferCache::value_type &a, const BufferCache::value_type &b) {
832 return a.second.lruCount < b.second.lruCount;
833 });
834
835 ASSERT(iter->second.storage != newStorage);
836 ASSERT(mConstantBufferStorageAdditionalSize >= iter->second.storage->getSize());
837
838 mConstantBufferStorageAdditionalSize -= iter->second.storage->getSize();
839 SafeDelete(iter->second.storage);
840 mConstantBufferRangeStoragesCache.erase(iter);
841 }
842
843 ANGLE_TRY(newStorage->resize(context, size, false));
844 mConstantBufferStorageAdditionalSize += sizeDelta;
845
846 // We don't copy the old data when resizing the constant buffer because the data may be
847 // out-of-date therefore we reset the data revision and let updateBufferStorage() handle the
848 // copy.
849 newStorage->setDataRevision(0);
850 }
851
852 ANGLE_TRY(updateBufferStorage(context, newStorage, offset, size));
853 ANGLE_TRY(garbageCollection(context, BUFFER_USAGE_UNIFORM));
854 *storageOut = GetAs<NativeStorage>(newStorage);
855 return angle::Result::Continue;
856 }
857
getStructuredBufferRangeSRV(const gl::Context * context,unsigned int offset,unsigned int size,unsigned int structureByteStride,const d3d11::ShaderResourceView ** srvOut)858 angle::Result Buffer11::getStructuredBufferRangeSRV(const gl::Context *context,
859 unsigned int offset,
860 unsigned int size,
861 unsigned int structureByteStride,
862 const d3d11::ShaderResourceView **srvOut)
863 {
864 BufferStorage *newStorage;
865
866 {
867 // Keep the cacheEntry in a limited scope because it may be invalidated later in the code if
868 // we need to reclaim some space.
869 StructuredBufferKey structuredBufferKey = StructuredBufferKey(offset, structureByteStride);
870 BufferCacheEntry *cacheEntry = &mStructuredBufferRangeStoragesCache[structuredBufferKey];
871
872 if (!cacheEntry->storage)
873 {
874 cacheEntry->storage = allocateStorage(BUFFER_USAGE_STRUCTURED);
875 cacheEntry->lruCount = ++mMaxStructuredBufferLruCount;
876 }
877
878 cacheEntry->lruCount = ++mMaxStructuredBufferLruCount;
879 newStorage = cacheEntry->storage;
880 }
881
882 StructuredBufferStorage *structuredBufferStorage = GetAs<StructuredBufferStorage>(newStorage);
883
884 markBufferUsage(BUFFER_USAGE_STRUCTURED);
885
886 if (newStorage->getSize() < static_cast<size_t>(size))
887 {
888 size_t maximumAllowedAdditionalSize = 2 * getSize();
889
890 size_t sizeDelta = static_cast<size_t>(size) - newStorage->getSize();
891
892 while (mStructuredBufferStorageAdditionalSize + sizeDelta > maximumAllowedAdditionalSize)
893 {
894 auto iter = std::min_element(std::begin(mStructuredBufferRangeStoragesCache),
895 std::end(mStructuredBufferRangeStoragesCache),
896 [](const StructuredBufferCache::value_type &a,
897 const StructuredBufferCache::value_type &b) {
898 return a.second.lruCount < b.second.lruCount;
899 });
900
901 ASSERT(iter->second.storage != newStorage);
902 ASSERT(mStructuredBufferStorageAdditionalSize >= iter->second.storage->getSize());
903
904 mStructuredBufferStorageAdditionalSize -= iter->second.storage->getSize();
905 SafeDelete(iter->second.storage);
906 mStructuredBufferRangeStoragesCache.erase(iter);
907 }
908
909 ANGLE_TRY(
910 structuredBufferStorage->resizeStructuredBuffer(context, size, structureByteStride));
911 mStructuredBufferStorageAdditionalSize += sizeDelta;
912
913 // We don't copy the old data when resizing the structured buffer because the data may be
914 // out-of-date therefore we reset the data revision and let updateBufferStorage() handle the
915 // copy.
916 newStorage->setDataRevision(0);
917 }
918
919 ANGLE_TRY(updateBufferStorage(context, newStorage, offset, static_cast<size_t>(size)));
920 ANGLE_TRY(garbageCollection(context, BUFFER_USAGE_STRUCTURED));
921 ANGLE_TRY(structuredBufferStorage->getStructuredBufferRangeSRV(context, offset, size,
922 structureByteStride, srvOut));
923 return angle::Result::Continue;
924 }
925
updateBufferStorage(const gl::Context * context,BufferStorage * storage,size_t sourceOffset,size_t storageSize)926 angle::Result Buffer11::updateBufferStorage(const gl::Context *context,
927 BufferStorage *storage,
928 size_t sourceOffset,
929 size_t storageSize)
930 {
931 BufferStorage *latestBuffer = nullptr;
932 ANGLE_TRY(getLatestBufferStorage(context, &latestBuffer));
933
934 ASSERT(storage);
935
936 if (!latestBuffer)
937 {
938 onStorageUpdate(storage);
939 return angle::Result::Continue;
940 }
941
942 if (latestBuffer->getDataRevision() <= storage->getDataRevision())
943 {
944 return angle::Result::Continue;
945 }
946
947 if (latestBuffer->getSize() == 0 || storage->getSize() == 0)
948 {
949 return angle::Result::Continue;
950 }
951
952 // Copy through a staging buffer if we're copying from or to a non-staging, mappable
953 // buffer storage. This is because we can't map a GPU buffer, and copy CPU
954 // data directly. If we're already using a staging buffer we're fine.
955 if (latestBuffer->getUsage() != BUFFER_USAGE_STAGING &&
956 storage->getUsage() != BUFFER_USAGE_STAGING &&
957 (!latestBuffer->isCPUAccessible(GL_MAP_READ_BIT) ||
958 !storage->isCPUAccessible(GL_MAP_WRITE_BIT)))
959 {
960 NativeStorage *stagingBuffer = nullptr;
961 ANGLE_TRY(getStagingStorage(context, &stagingBuffer));
962
963 CopyResult copyResult = CopyResult::NOT_RECREATED;
964 ANGLE_TRY(stagingBuffer->copyFromStorage(context, latestBuffer, 0, latestBuffer->getSize(),
965 0, ©Result));
966 onCopyStorage(stagingBuffer, latestBuffer);
967
968 latestBuffer = stagingBuffer;
969 }
970
971 CopyResult copyResult = CopyResult::NOT_RECREATED;
972 ANGLE_TRY(
973 storage->copyFromStorage(context, latestBuffer, sourceOffset, storageSize, 0, ©Result));
974 // If the D3D buffer has been recreated, we should update our serial.
975 if (copyResult == CopyResult::RECREATED)
976 {
977 updateSerial();
978 }
979 onCopyStorage(storage, latestBuffer);
980 return angle::Result::Continue;
981 }
982
getLatestBufferStorage(const gl::Context * context,Buffer11::BufferStorage ** storageOut) const983 angle::Result Buffer11::getLatestBufferStorage(const gl::Context *context,
984 Buffer11::BufferStorage **storageOut) const
985 {
986 // resize buffer
987 if (mLatestBufferStorage && mLatestBufferStorage->getSize() < mSize)
988 {
989 ANGLE_TRY(mLatestBufferStorage->resize(context, mSize, true));
990 }
991
992 *storageOut = mLatestBufferStorage;
993 return angle::Result::Continue;
994 }
995
996 template <typename StorageOutT>
getStagingStorage(const gl::Context * context,StorageOutT ** storageOut)997 angle::Result Buffer11::getStagingStorage(const gl::Context *context, StorageOutT **storageOut)
998 {
999 return getBufferStorage(context, BUFFER_USAGE_STAGING, storageOut);
1000 }
1001
getSize() const1002 size_t Buffer11::getSize() const
1003 {
1004 return mSize;
1005 }
1006
supportsDirectBinding() const1007 bool Buffer11::supportsDirectBinding() const
1008 {
1009 // Do not support direct buffers for dynamic data. The streaming buffer
1010 // offers better performance for data which changes every frame.
1011 return (mUsage == D3DBufferUsage::STATIC);
1012 }
1013
initializeStaticData(const gl::Context * context)1014 void Buffer11::initializeStaticData(const gl::Context *context)
1015 {
1016 BufferD3D::initializeStaticData(context);
1017 onStateChange(angle::SubjectMessage::SubjectChanged);
1018 }
1019
invalidateStaticData(const gl::Context * context)1020 void Buffer11::invalidateStaticData(const gl::Context *context)
1021 {
1022 BufferD3D::invalidateStaticData(context);
1023 onStateChange(angle::SubjectMessage::SubjectChanged);
1024 }
1025
onCopyStorage(BufferStorage * dest,BufferStorage * source)1026 void Buffer11::onCopyStorage(BufferStorage *dest, BufferStorage *source)
1027 {
1028 ASSERT(source && mLatestBufferStorage);
1029 dest->setDataRevision(source->getDataRevision());
1030
1031 // Only update the latest buffer storage if our usage index is lower. See comment in header.
1032 if (dest->getUsage() < mLatestBufferStorage->getUsage())
1033 {
1034 mLatestBufferStorage = dest;
1035 }
1036 }
1037
onStorageUpdate(BufferStorage * updatedStorage)1038 void Buffer11::onStorageUpdate(BufferStorage *updatedStorage)
1039 {
1040 updatedStorage->setDataRevision(updatedStorage->getDataRevision() + 1);
1041 mLatestBufferStorage = updatedStorage;
1042 }
1043
1044 // Buffer11::BufferStorage implementation
1045
BufferStorage(Renderer11 * renderer,BufferUsage usage)1046 Buffer11::BufferStorage::BufferStorage(Renderer11 *renderer, BufferUsage usage)
1047 : mRenderer(renderer), mRevision(0), mUsage(usage), mBufferSize(0)
1048 {}
1049
setData(const gl::Context * context,const uint8_t * data,size_t offset,size_t size)1050 angle::Result Buffer11::BufferStorage::setData(const gl::Context *context,
1051 const uint8_t *data,
1052 size_t offset,
1053 size_t size)
1054 {
1055 ASSERT(isCPUAccessible(GL_MAP_WRITE_BIT));
1056
1057 // Uniform storage can have a different internal size than the buffer size. Ensure we don't
1058 // overflow.
1059 size_t mapSize = std::min(size, mBufferSize - offset);
1060
1061 uint8_t *writePointer = nullptr;
1062 ANGLE_TRY(map(context, offset, mapSize, GL_MAP_WRITE_BIT, &writePointer));
1063
1064 memcpy(writePointer, data, mapSize);
1065
1066 unmap();
1067
1068 return angle::Result::Continue;
1069 }
1070
1071 // Buffer11::NativeStorage implementation
1072
NativeStorage(Renderer11 * renderer,BufferUsage usage,const angle::Subject * onStorageChanged)1073 Buffer11::NativeStorage::NativeStorage(Renderer11 *renderer,
1074 BufferUsage usage,
1075 const angle::Subject *onStorageChanged)
1076 : BufferStorage(renderer, usage), mBuffer(), mOnStorageChanged(onStorageChanged)
1077 {}
1078
~NativeStorage()1079 Buffer11::NativeStorage::~NativeStorage()
1080 {
1081 clearSRVs();
1082 clearUAVs();
1083 }
1084
isCPUAccessible(GLbitfield access) const1085 bool Buffer11::NativeStorage::isCPUAccessible(GLbitfield access) const
1086 {
1087 if ((access & GL_MAP_READ_BIT) != 0)
1088 {
1089 // Read is more exclusive than write mappability.
1090 return (mUsage == BUFFER_USAGE_STAGING);
1091 }
1092 ASSERT((access & GL_MAP_WRITE_BIT) != 0);
1093 return (mUsage == BUFFER_USAGE_STAGING || mUsage == BUFFER_USAGE_UNIFORM ||
1094 mUsage == BUFFER_USAGE_STRUCTURED);
1095 }
1096
1097 // Returns true if it recreates the direct buffer
copyFromStorage(const gl::Context * context,BufferStorage * source,size_t sourceOffset,size_t size,size_t destOffset,CopyResult * resultOut)1098 angle::Result Buffer11::NativeStorage::copyFromStorage(const gl::Context *context,
1099 BufferStorage *source,
1100 size_t sourceOffset,
1101 size_t size,
1102 size_t destOffset,
1103 CopyResult *resultOut)
1104 {
1105 size_t requiredSize = destOffset + size;
1106
1107 // (Re)initialize D3D buffer if needed
1108 bool preserveData = (destOffset > 0);
1109 if (!mBuffer.valid() || mBufferSize < requiredSize)
1110 {
1111 ANGLE_TRY(resize(context, requiredSize, preserveData));
1112 *resultOut = CopyResult::RECREATED;
1113 }
1114 else
1115 {
1116 *resultOut = CopyResult::NOT_RECREATED;
1117 }
1118
1119 size_t clampedSize = size;
1120 if (mUsage == BUFFER_USAGE_UNIFORM)
1121 {
1122 clampedSize = std::min(clampedSize, mBufferSize - destOffset);
1123 }
1124
1125 if (clampedSize == 0)
1126 {
1127 return angle::Result::Continue;
1128 }
1129
1130 if (source->getUsage() == BUFFER_USAGE_PIXEL_PACK ||
1131 source->getUsage() == BUFFER_USAGE_SYSTEM_MEMORY)
1132 {
1133 ASSERT(source->isCPUAccessible(GL_MAP_READ_BIT) && isCPUAccessible(GL_MAP_WRITE_BIT));
1134
1135 // Uniform buffers must be mapped with write/discard.
1136 ASSERT(!(preserveData && mUsage == BUFFER_USAGE_UNIFORM));
1137
1138 uint8_t *sourcePointer = nullptr;
1139 ANGLE_TRY(source->map(context, sourceOffset, clampedSize, GL_MAP_READ_BIT, &sourcePointer));
1140
1141 auto err = setData(context, sourcePointer, destOffset, clampedSize);
1142 source->unmap();
1143 ANGLE_TRY(err);
1144 }
1145 else
1146 {
1147 D3D11_BOX srcBox;
1148 srcBox.left = static_cast<unsigned int>(sourceOffset);
1149 srcBox.right = static_cast<unsigned int>(sourceOffset + clampedSize);
1150 srcBox.top = 0;
1151 srcBox.bottom = 1;
1152 srcBox.front = 0;
1153 srcBox.back = 1;
1154
1155 const d3d11::Buffer *sourceBuffer = &GetAs<NativeStorage>(source)->getBuffer();
1156
1157 ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
1158 deviceContext->CopySubresourceRegion(mBuffer.get(), 0,
1159 static_cast<unsigned int>(destOffset), 0, 0,
1160 sourceBuffer->get(), 0, &srcBox);
1161 }
1162
1163 return angle::Result::Continue;
1164 }
1165
resize(const gl::Context * context,size_t size,bool preserveData)1166 angle::Result Buffer11::NativeStorage::resize(const gl::Context *context,
1167 size_t size,
1168 bool preserveData)
1169 {
1170 if (size == 0)
1171 {
1172 mBuffer.reset();
1173 mBufferSize = 0;
1174 return angle::Result::Continue;
1175 }
1176
1177 D3D11_BUFFER_DESC bufferDesc;
1178 FillBufferDesc(&bufferDesc, mRenderer, mUsage, static_cast<unsigned int>(size));
1179
1180 d3d11::Buffer newBuffer;
1181 ANGLE_TRY(
1182 mRenderer->allocateResource(SafeGetImplAs<Context11>(context), bufferDesc, &newBuffer));
1183 newBuffer.setInternalName("Buffer11::NativeStorage");
1184
1185 if (mBuffer.valid() && preserveData)
1186 {
1187 // We don't call resize if the buffer is big enough already.
1188 ASSERT(mBufferSize <= size);
1189
1190 D3D11_BOX srcBox;
1191 srcBox.left = 0;
1192 srcBox.right = static_cast<unsigned int>(mBufferSize);
1193 srcBox.top = 0;
1194 srcBox.bottom = 1;
1195 srcBox.front = 0;
1196 srcBox.back = 1;
1197
1198 ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
1199 deviceContext->CopySubresourceRegion(newBuffer.get(), 0, 0, 0, 0, mBuffer.get(), 0,
1200 &srcBox);
1201 }
1202
1203 // No longer need the old buffer
1204 mBuffer = std::move(newBuffer);
1205
1206 mBufferSize = bufferDesc.ByteWidth;
1207
1208 // Free the SRVs.
1209 clearSRVs();
1210
1211 // Free the UAVs.
1212 clearUAVs();
1213
1214 // Notify that the storage has changed.
1215 if (mOnStorageChanged)
1216 {
1217 mOnStorageChanged->onStateChange(angle::SubjectMessage::SubjectChanged);
1218 }
1219
1220 return angle::Result::Continue;
1221 }
1222
1223 // static
FillBufferDesc(D3D11_BUFFER_DESC * bufferDesc,Renderer11 * renderer,BufferUsage usage,unsigned int bufferSize)1224 void Buffer11::NativeStorage::FillBufferDesc(D3D11_BUFFER_DESC *bufferDesc,
1225 Renderer11 *renderer,
1226 BufferUsage usage,
1227 unsigned int bufferSize)
1228 {
1229 bufferDesc->ByteWidth = bufferSize;
1230 bufferDesc->MiscFlags = 0;
1231 bufferDesc->StructureByteStride = 0;
1232
1233 switch (usage)
1234 {
1235 case BUFFER_USAGE_STAGING:
1236 bufferDesc->Usage = D3D11_USAGE_STAGING;
1237 bufferDesc->BindFlags = 0;
1238 bufferDesc->CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
1239 break;
1240
1241 case BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK:
1242 bufferDesc->Usage = D3D11_USAGE_DEFAULT;
1243 bufferDesc->BindFlags = D3D11_BIND_VERTEX_BUFFER;
1244
1245 if (renderer->isES3Capable())
1246 {
1247 bufferDesc->BindFlags |= D3D11_BIND_STREAM_OUTPUT;
1248 }
1249
1250 bufferDesc->CPUAccessFlags = 0;
1251 break;
1252
1253 case BUFFER_USAGE_INDEX:
1254 bufferDesc->Usage = D3D11_USAGE_DEFAULT;
1255 bufferDesc->BindFlags = D3D11_BIND_INDEX_BUFFER;
1256 bufferDesc->CPUAccessFlags = 0;
1257 break;
1258
1259 case BUFFER_USAGE_INDIRECT:
1260 bufferDesc->MiscFlags = D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS;
1261 bufferDesc->Usage = D3D11_USAGE_DEFAULT;
1262 bufferDesc->BindFlags = 0;
1263 bufferDesc->CPUAccessFlags = 0;
1264 break;
1265
1266 case BUFFER_USAGE_PIXEL_UNPACK:
1267 bufferDesc->Usage = D3D11_USAGE_DEFAULT;
1268 bufferDesc->BindFlags = D3D11_BIND_SHADER_RESOURCE;
1269 bufferDesc->CPUAccessFlags = 0;
1270 break;
1271
1272 case BUFFER_USAGE_UNIFORM:
1273 bufferDesc->Usage = D3D11_USAGE_DYNAMIC;
1274 bufferDesc->BindFlags = D3D11_BIND_CONSTANT_BUFFER;
1275 bufferDesc->CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
1276
1277 // Constant buffers must be of a limited size, and aligned to 16 byte boundaries
1278 // For our purposes we ignore any buffer data past the maximum constant buffer size
1279 bufferDesc->ByteWidth = roundUpPow2(bufferDesc->ByteWidth, 16u);
1280
1281 // Note: it seems that D3D11 allows larger buffers on some platforms, but not all.
1282 // (Windows 10 seems to allow larger constant buffers, but not Windows 7)
1283 if (!renderer->getRenderer11DeviceCaps().supportsConstantBufferOffsets)
1284 {
1285 bufferDesc->ByteWidth = std::min<UINT>(
1286 bufferDesc->ByteWidth,
1287 static_cast<UINT>(renderer->getNativeCaps().maxUniformBlockSize));
1288 }
1289 break;
1290
1291 case BUFFER_USAGE_RAW_UAV:
1292 bufferDesc->MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS;
1293 bufferDesc->BindFlags = D3D11_BIND_UNORDERED_ACCESS;
1294 bufferDesc->Usage = D3D11_USAGE_DEFAULT;
1295 bufferDesc->CPUAccessFlags = 0;
1296 break;
1297 case BUFFER_USAGE_TYPED_UAV:
1298 bufferDesc->BindFlags = D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE;
1299 bufferDesc->Usage = D3D11_USAGE_DEFAULT;
1300 bufferDesc->CPUAccessFlags = 0;
1301 bufferDesc->MiscFlags = 0;
1302 break;
1303
1304 default:
1305 UNREACHABLE();
1306 }
1307 }
1308
map(const gl::Context * context,size_t offset,size_t length,GLbitfield access,uint8_t ** mapPointerOut)1309 angle::Result Buffer11::NativeStorage::map(const gl::Context *context,
1310 size_t offset,
1311 size_t length,
1312 GLbitfield access,
1313 uint8_t **mapPointerOut)
1314 {
1315 ASSERT(isCPUAccessible(access));
1316
1317 D3D11_MAPPED_SUBRESOURCE mappedResource;
1318 D3D11_MAP d3dMapType = gl_d3d11::GetD3DMapTypeFromBits(mUsage, access);
1319
1320 ANGLE_TRY(mRenderer->mapResource(context, mBuffer.get(), 0, d3dMapType, 0, &mappedResource));
1321 ASSERT(mappedResource.pData);
1322 *mapPointerOut = static_cast<uint8_t *>(mappedResource.pData) + offset;
1323 return angle::Result::Continue;
1324 }
1325
unmap()1326 void Buffer11::NativeStorage::unmap()
1327 {
1328 ASSERT(isCPUAccessible(GL_MAP_WRITE_BIT) || isCPUAccessible(GL_MAP_READ_BIT));
1329 ID3D11DeviceContext *context = mRenderer->getDeviceContext();
1330 context->Unmap(mBuffer.get(), 0);
1331 }
1332
getSRVForFormat(const gl::Context * context,DXGI_FORMAT srvFormat,const d3d11::ShaderResourceView ** srvOut)1333 angle::Result Buffer11::NativeStorage::getSRVForFormat(const gl::Context *context,
1334 DXGI_FORMAT srvFormat,
1335 const d3d11::ShaderResourceView **srvOut)
1336 {
1337 auto bufferSRVIt = mBufferResourceViews.find(srvFormat);
1338
1339 if (bufferSRVIt != mBufferResourceViews.end())
1340 {
1341 *srvOut = &bufferSRVIt->second;
1342 return angle::Result::Continue;
1343 }
1344
1345 const d3d11::DXGIFormatSize &dxgiFormatInfo = d3d11::GetDXGIFormatSizeInfo(srvFormat);
1346
1347 D3D11_SHADER_RESOURCE_VIEW_DESC bufferSRVDesc;
1348 bufferSRVDesc.Buffer.ElementOffset = 0;
1349 bufferSRVDesc.Buffer.ElementWidth = static_cast<UINT>(mBufferSize) / dxgiFormatInfo.pixelBytes;
1350 bufferSRVDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
1351 bufferSRVDesc.Format = srvFormat;
1352
1353 ANGLE_TRY(mRenderer->allocateResource(GetImplAs<Context11>(context), bufferSRVDesc,
1354 mBuffer.get(), &mBufferResourceViews[srvFormat]));
1355
1356 *srvOut = &mBufferResourceViews[srvFormat];
1357 return angle::Result::Continue;
1358 }
1359
getRawUAV(const gl::Context * context,unsigned int offset,unsigned int size,d3d11::UnorderedAccessView ** uavOut)1360 angle::Result Buffer11::NativeStorage::getRawUAV(const gl::Context *context,
1361 unsigned int offset,
1362 unsigned int size,
1363 d3d11::UnorderedAccessView **uavOut)
1364 {
1365 ASSERT(offset + size <= mBufferSize);
1366
1367 auto bufferRawUAV = mBufferRawUAVs.find({offset, size});
1368 if (bufferRawUAV != mBufferRawUAVs.end())
1369 {
1370 *uavOut = &bufferRawUAV->second;
1371 return angle::Result::Continue;
1372 }
1373
1374 D3D11_UNORDERED_ACCESS_VIEW_DESC bufferUAVDesc;
1375
1376 // DXGI_FORMAT_R32_TYPELESS uses 4 bytes per element
1377 constexpr int kBytesToElement = 4;
1378 bufferUAVDesc.Buffer.FirstElement = offset / kBytesToElement;
1379 bufferUAVDesc.Buffer.NumElements = size / kBytesToElement;
1380 bufferUAVDesc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW;
1381 bufferUAVDesc.Format = DXGI_FORMAT_R32_TYPELESS; // Format must be DXGI_FORMAT_R32_TYPELESS,
1382 // when creating Raw Unordered Access View
1383 bufferUAVDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
1384
1385 ANGLE_TRY(mRenderer->allocateResource(GetImplAs<Context11>(context), bufferUAVDesc,
1386 mBuffer.get(), &mBufferRawUAVs[{offset, size}]));
1387 *uavOut = &mBufferRawUAVs[{offset, size}];
1388 return angle::Result::Continue;
1389 }
1390
clearSRVs()1391 void Buffer11::NativeStorage::clearSRVs()
1392 {
1393 mBufferResourceViews.clear();
1394 }
1395
clearUAVs()1396 void Buffer11::NativeStorage::clearUAVs()
1397 {
1398 mBufferRawUAVs.clear();
1399 }
1400
StructuredBufferStorage(Renderer11 * renderer,BufferUsage usage,const angle::Subject * onStorageChanged)1401 Buffer11::StructuredBufferStorage::StructuredBufferStorage(Renderer11 *renderer,
1402 BufferUsage usage,
1403 const angle::Subject *onStorageChanged)
1404 : NativeStorage(renderer, usage, onStorageChanged), mStructuredBufferResourceView()
1405 {}
1406
~StructuredBufferStorage()1407 Buffer11::StructuredBufferStorage::~StructuredBufferStorage()
1408 {
1409 mStructuredBufferResourceView.reset();
1410 }
1411
resizeStructuredBuffer(const gl::Context * context,unsigned int size,unsigned int structureByteStride)1412 angle::Result Buffer11::StructuredBufferStorage::resizeStructuredBuffer(
1413 const gl::Context *context,
1414 unsigned int size,
1415 unsigned int structureByteStride)
1416 {
1417 if (size == 0)
1418 {
1419 mBuffer.reset();
1420 mBufferSize = 0;
1421 return angle::Result::Continue;
1422 }
1423
1424 D3D11_BUFFER_DESC bufferDesc;
1425 bufferDesc.ByteWidth = size;
1426 bufferDesc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;
1427 bufferDesc.StructureByteStride = structureByteStride;
1428 bufferDesc.Usage = D3D11_USAGE_DYNAMIC;
1429 bufferDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
1430 bufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
1431
1432 d3d11::Buffer newBuffer;
1433 ANGLE_TRY(
1434 mRenderer->allocateResource(SafeGetImplAs<Context11>(context), bufferDesc, &newBuffer));
1435 newBuffer.setInternalName("Buffer11::StructuredBufferStorage");
1436
1437 // No longer need the old buffer
1438 mBuffer = std::move(newBuffer);
1439
1440 mBufferSize = static_cast<size_t>(bufferDesc.ByteWidth);
1441
1442 mStructuredBufferResourceView.reset();
1443
1444 // Notify that the storage has changed.
1445 if (mOnStorageChanged)
1446 {
1447 mOnStorageChanged->onStateChange(angle::SubjectMessage::SubjectChanged);
1448 }
1449
1450 return angle::Result::Continue;
1451 }
1452
getStructuredBufferRangeSRV(const gl::Context * context,unsigned int offset,unsigned int size,unsigned int structureByteStride,const d3d11::ShaderResourceView ** srvOut)1453 angle::Result Buffer11::StructuredBufferStorage::getStructuredBufferRangeSRV(
1454 const gl::Context *context,
1455 unsigned int offset,
1456 unsigned int size,
1457 unsigned int structureByteStride,
1458 const d3d11::ShaderResourceView **srvOut)
1459 {
1460 if (mStructuredBufferResourceView.valid())
1461 {
1462 *srvOut = &mStructuredBufferResourceView;
1463 return angle::Result::Continue;
1464 }
1465
1466 D3D11_SHADER_RESOURCE_VIEW_DESC bufferSRVDesc = {};
1467 bufferSRVDesc.BufferEx.NumElements = structureByteStride == 0u ? 1 : size / structureByteStride;
1468 bufferSRVDesc.BufferEx.FirstElement = 0;
1469 bufferSRVDesc.BufferEx.Flags = 0;
1470 bufferSRVDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFEREX;
1471 bufferSRVDesc.Format = DXGI_FORMAT_UNKNOWN;
1472
1473 ANGLE_TRY(mRenderer->allocateResource(GetImplAs<Context11>(context), bufferSRVDesc,
1474 mBuffer.get(), &mStructuredBufferResourceView));
1475
1476 *srvOut = &mStructuredBufferResourceView;
1477 return angle::Result::Continue;
1478 }
1479
1480 // Buffer11::PackStorage implementation
1481
PackStorage(Renderer11 * renderer)1482 Buffer11::PackStorage::PackStorage(Renderer11 *renderer)
1483 : BufferStorage(renderer, BUFFER_USAGE_PIXEL_PACK), mStagingTexture(), mDataModified(false)
1484 {}
1485
~PackStorage()1486 Buffer11::PackStorage::~PackStorage() {}
1487
copyFromStorage(const gl::Context * context,BufferStorage * source,size_t sourceOffset,size_t size,size_t destOffset,CopyResult * resultOut)1488 angle::Result Buffer11::PackStorage::copyFromStorage(const gl::Context *context,
1489 BufferStorage *source,
1490 size_t sourceOffset,
1491 size_t size,
1492 size_t destOffset,
1493 CopyResult *resultOut)
1494 {
1495 ANGLE_TRY(flushQueuedPackCommand(context));
1496
1497 // For all use cases of pack buffers, we must copy through a readable buffer.
1498 ASSERT(source->isCPUAccessible(GL_MAP_READ_BIT));
1499 uint8_t *sourceData = nullptr;
1500 ANGLE_TRY(source->map(context, sourceOffset, size, GL_MAP_READ_BIT, &sourceData));
1501 ASSERT(destOffset + size <= mMemoryBuffer.size());
1502 memcpy(mMemoryBuffer.data() + destOffset, sourceData, size);
1503 source->unmap();
1504 *resultOut = CopyResult::NOT_RECREATED;
1505 return angle::Result::Continue;
1506 }
1507
resize(const gl::Context * context,size_t size,bool preserveData)1508 angle::Result Buffer11::PackStorage::resize(const gl::Context *context,
1509 size_t size,
1510 bool preserveData)
1511 {
1512 if (size != mBufferSize)
1513 {
1514 Context11 *context11 = GetImplAs<Context11>(context);
1515 ANGLE_CHECK_GL_ALLOC(context11, mMemoryBuffer.resize(size));
1516 mBufferSize = size;
1517 }
1518
1519 return angle::Result::Continue;
1520 }
1521
map(const gl::Context * context,size_t offset,size_t length,GLbitfield access,uint8_t ** mapPointerOut)1522 angle::Result Buffer11::PackStorage::map(const gl::Context *context,
1523 size_t offset,
1524 size_t length,
1525 GLbitfield access,
1526 uint8_t **mapPointerOut)
1527 {
1528 ASSERT(offset + length <= getSize());
1529 // TODO: fast path
1530 // We might be able to optimize out one or more memcpy calls by detecting when
1531 // and if D3D packs the staging texture memory identically to how we would fill
1532 // the pack buffer according to the current pack state.
1533
1534 ANGLE_TRY(flushQueuedPackCommand(context));
1535
1536 mDataModified = (mDataModified || (access & GL_MAP_WRITE_BIT) != 0);
1537
1538 *mapPointerOut = mMemoryBuffer.data() + offset;
1539 return angle::Result::Continue;
1540 }
1541
unmap()1542 void Buffer11::PackStorage::unmap()
1543 {
1544 // No-op
1545 }
1546
packPixels(const gl::Context * context,const gl::FramebufferAttachment & readAttachment,const PackPixelsParams & params)1547 angle::Result Buffer11::PackStorage::packPixels(const gl::Context *context,
1548 const gl::FramebufferAttachment &readAttachment,
1549 const PackPixelsParams ¶ms)
1550 {
1551 ANGLE_TRY(flushQueuedPackCommand(context));
1552
1553 RenderTarget11 *renderTarget = nullptr;
1554 ANGLE_TRY(readAttachment.getRenderTarget(context, 0, &renderTarget));
1555
1556 const TextureHelper11 &srcTexture = renderTarget->getTexture();
1557 ASSERT(srcTexture.valid());
1558 unsigned int srcSubresource = renderTarget->getSubresourceIndex();
1559
1560 mQueuedPackCommand.reset(new PackPixelsParams(params));
1561
1562 gl::Extents srcTextureSize(params.area.width, params.area.height, 1);
1563 if (!mStagingTexture.get() || mStagingTexture.getFormat() != srcTexture.getFormat() ||
1564 mStagingTexture.getExtents() != srcTextureSize)
1565 {
1566 ANGLE_TRY(mRenderer->createStagingTexture(context, srcTexture.getTextureType(),
1567 srcTexture.getFormatSet(), srcTextureSize,
1568 StagingAccess::READ, &mStagingTexture));
1569 }
1570
1571 // ReadPixels from multisampled FBOs isn't supported in current GL
1572 ASSERT(srcTexture.getSampleCount() <= 1);
1573
1574 ID3D11DeviceContext *immediateContext = mRenderer->getDeviceContext();
1575 D3D11_BOX srcBox;
1576 srcBox.left = params.area.x;
1577 srcBox.right = params.area.x + params.area.width;
1578 srcBox.top = params.area.y;
1579 srcBox.bottom = params.area.y + params.area.height;
1580
1581 // Select the correct layer from a 3D attachment
1582 srcBox.front = 0;
1583 if (mStagingTexture.is3D())
1584 {
1585 srcBox.front = static_cast<UINT>(readAttachment.layer());
1586 }
1587 srcBox.back = srcBox.front + 1;
1588
1589 // Asynchronous copy
1590 immediateContext->CopySubresourceRegion(mStagingTexture.get(), 0, 0, 0, 0, srcTexture.get(),
1591 srcSubresource, &srcBox);
1592
1593 return angle::Result::Continue;
1594 }
1595
flushQueuedPackCommand(const gl::Context * context)1596 angle::Result Buffer11::PackStorage::flushQueuedPackCommand(const gl::Context *context)
1597 {
1598 ASSERT(mMemoryBuffer.size() > 0);
1599
1600 if (mQueuedPackCommand)
1601 {
1602 ANGLE_TRY(mRenderer->packPixels(context, mStagingTexture, *mQueuedPackCommand,
1603 mMemoryBuffer.data()));
1604 mQueuedPackCommand.reset(nullptr);
1605 }
1606
1607 return angle::Result::Continue;
1608 }
1609
1610 // Buffer11::SystemMemoryStorage implementation
1611
SystemMemoryStorage(Renderer11 * renderer)1612 Buffer11::SystemMemoryStorage::SystemMemoryStorage(Renderer11 *renderer)
1613 : Buffer11::BufferStorage(renderer, BUFFER_USAGE_SYSTEM_MEMORY)
1614 {}
1615
copyFromStorage(const gl::Context * context,BufferStorage * source,size_t sourceOffset,size_t size,size_t destOffset,CopyResult * resultOut)1616 angle::Result Buffer11::SystemMemoryStorage::copyFromStorage(const gl::Context *context,
1617 BufferStorage *source,
1618 size_t sourceOffset,
1619 size_t size,
1620 size_t destOffset,
1621 CopyResult *resultOut)
1622 {
1623 ASSERT(source->isCPUAccessible(GL_MAP_READ_BIT));
1624 uint8_t *sourceData = nullptr;
1625 ANGLE_TRY(source->map(context, sourceOffset, size, GL_MAP_READ_BIT, &sourceData));
1626 ASSERT(destOffset + size <= mSystemCopy.size());
1627 memcpy(mSystemCopy.data() + destOffset, sourceData, size);
1628 source->unmap();
1629 *resultOut = CopyResult::RECREATED;
1630 return angle::Result::Continue;
1631 }
1632
resize(const gl::Context * context,size_t size,bool preserveData)1633 angle::Result Buffer11::SystemMemoryStorage::resize(const gl::Context *context,
1634 size_t size,
1635 bool preserveData)
1636 {
1637 if (mSystemCopy.size() < size)
1638 {
1639 Context11 *context11 = GetImplAs<Context11>(context);
1640 ANGLE_CHECK_GL_ALLOC(context11, mSystemCopy.resize(size));
1641 mBufferSize = size;
1642 }
1643
1644 return angle::Result::Continue;
1645 }
1646
map(const gl::Context * context,size_t offset,size_t length,GLbitfield access,uint8_t ** mapPointerOut)1647 angle::Result Buffer11::SystemMemoryStorage::map(const gl::Context *context,
1648 size_t offset,
1649 size_t length,
1650 GLbitfield access,
1651 uint8_t **mapPointerOut)
1652 {
1653 ASSERT(!mSystemCopy.empty() && offset + length <= mSystemCopy.size());
1654 *mapPointerOut = mSystemCopy.data() + offset;
1655 return angle::Result::Continue;
1656 }
1657
unmap()1658 void Buffer11::SystemMemoryStorage::unmap()
1659 {
1660 // No-op
1661 }
1662 } // namespace rx
1663