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