• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2016 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 // BufferVk.cpp:
7 //    Implements the class methods for BufferVk.
8 //
9 
10 #include "libANGLE/renderer/vulkan/BufferVk.h"
11 
12 #include "common/FixedVector.h"
13 #include "common/debug.h"
14 #include "common/mathutil.h"
15 #include "common/utilities.h"
16 #include "libANGLE/Context.h"
17 #include "libANGLE/renderer/vulkan/ContextVk.h"
18 #include "libANGLE/renderer/vulkan/RendererVk.h"
19 #include "libANGLE/trace.h"
20 
21 namespace rx
22 {
23 
24 namespace
25 {
26 // Vertex attribute buffers are used as storage buffers for conversion in compute, where access to
27 // the buffer is made in 4-byte chunks.  Assume the size of the buffer is 4k+n where n is in [0, 3).
28 // On some hardware, reading 4 bytes from address 4k returns 0, making it impossible to read the
29 // last n bytes.  By rounding up the buffer sizes to a multiple of 4, the problem is alleviated.
30 constexpr size_t kBufferSizeGranularity = 4;
31 static_assert(gl::isPow2(kBufferSizeGranularity), "use as alignment, must be power of two");
32 
33 // Start with a fairly small buffer size. We can increase this dynamically as we convert more data.
34 constexpr size_t kConvertedArrayBufferInitialSize = 1024 * 8;
35 
36 // Buffers that have a static usage pattern will be allocated in
37 // device local memory to speed up access to and from the GPU.
38 // Dynamic usage patterns or that are frequently mapped
39 // will now request host cached memory to speed up access from the CPU.
GetPreferredMemoryType(gl::BufferBinding target,gl::BufferUsage usage)40 ANGLE_INLINE VkMemoryPropertyFlags GetPreferredMemoryType(gl::BufferBinding target,
41                                                           gl::BufferUsage usage)
42 {
43     constexpr VkMemoryPropertyFlags kDeviceLocalFlags =
44         (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
45          VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
46     constexpr VkMemoryPropertyFlags kHostCachedFlags =
47         (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
48          VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
49     constexpr VkMemoryPropertyFlags kHostUncachedFlags =
50         (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
51 
52     if (target == gl::BufferBinding::PixelUnpack)
53     {
54         return kHostCachedFlags;
55     }
56 
57     switch (usage)
58     {
59         case gl::BufferUsage::StaticCopy:
60         case gl::BufferUsage::StaticDraw:
61         case gl::BufferUsage::StaticRead:
62             // For static usage, request a device local memory
63             return kDeviceLocalFlags;
64         case gl::BufferUsage::DynamicDraw:
65         case gl::BufferUsage::StreamDraw:
66             // For non-static usage where the CPU performs a write-only access, request
67             // a host uncached memory
68             return kHostUncachedFlags;
69         case gl::BufferUsage::DynamicCopy:
70         case gl::BufferUsage::DynamicRead:
71         case gl::BufferUsage::StreamCopy:
72         case gl::BufferUsage::StreamRead:
73             // For all other types of usage, request a host cached memory
74             return kHostCachedFlags;
75         default:
76             UNREACHABLE();
77             return kHostCachedFlags;
78     }
79 }
80 
GetStorageMemoryType(GLbitfield storageFlags,bool externalBuffer)81 ANGLE_INLINE VkMemoryPropertyFlags GetStorageMemoryType(GLbitfield storageFlags,
82                                                         bool externalBuffer)
83 {
84     constexpr VkMemoryPropertyFlags kDeviceLocalHostVisibleFlags =
85         (VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
86     constexpr VkMemoryPropertyFlags kDeviceLocalHostCoherentFlags =
87         (VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
88          VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
89 
90     const bool isCoherentMap   = (storageFlags & GL_MAP_COHERENT_BIT_EXT) != 0;
91     const bool isPersistentMap = (storageFlags & GL_MAP_PERSISTENT_BIT_EXT) != 0;
92 
93     if (isCoherentMap || isPersistentMap || externalBuffer)
94     {
95         // We currently allocate coherent memory for persistently mapped buffers.
96         // GL_EXT_buffer_storage allows non-coherent memory, but currently the implementation of
97         // |glMemoryBarrier(CLIENT_MAPPED_BUFFER_BARRIER_BIT_EXT)| relies on the mapping being
98         // coherent.
99         //
100         // If persistently mapped buffers ever use non-coherent memory, then said |glMemoryBarrier|
101         // call must result in |vkInvalidateMappedMemoryRanges| for all persistently mapped buffers.
102         return kDeviceLocalHostCoherentFlags;
103     }
104 
105     return kDeviceLocalHostVisibleFlags;
106 }
107 
GetPreferredDynamicBufferInitialSize(RendererVk * renderer,size_t dataSize,gl::BufferUsage usage,size_t * alignmentOut)108 size_t GetPreferredDynamicBufferInitialSize(RendererVk *renderer,
109                                             size_t dataSize,
110                                             gl::BufferUsage usage,
111                                             size_t *alignmentOut)
112 {
113     // The buffer may be used for a number of different operations, so its allocations should
114     // have an alignment that satisifies all.
115     const VkPhysicalDeviceLimits &limitsVk = renderer->getPhysicalDeviceProperties().limits;
116 
117     // All known vendors have power-of-2 alignment requirements, so std::max can work instead of
118     // lcm.
119     ASSERT(gl::isPow2(limitsVk.minUniformBufferOffsetAlignment));
120     ASSERT(gl::isPow2(limitsVk.minStorageBufferOffsetAlignment));
121     ASSERT(gl::isPow2(limitsVk.minTexelBufferOffsetAlignment));
122     ASSERT(gl::isPow2(limitsVk.minMemoryMapAlignment));
123 
124     *alignmentOut = std::max({static_cast<size_t>(limitsVk.minUniformBufferOffsetAlignment),
125                               static_cast<size_t>(limitsVk.minStorageBufferOffsetAlignment),
126                               static_cast<size_t>(limitsVk.minTexelBufferOffsetAlignment),
127                               limitsVk.minMemoryMapAlignment});
128 
129     // mBuffer will be allocated through a DynamicBuffer.  If hinted to be DYNAMIC, have
130     // DynamicBuffer allocate bigger blocks to suballocate from.  Otherwise, let it adapt to the
131     // buffer size automatically (which will allocate BufferHelpers with the same size as this
132     // buffer).
133     const bool isDynamic = usage == gl::BufferUsage::DynamicDraw ||
134                            usage == gl::BufferUsage::DynamicCopy ||
135                            usage == gl::BufferUsage::DynamicRead;
136     // Sub-allocate from a 4KB buffer.  If the buffer allocations are bigger, the dynamic buffer
137     // will adapt to it automatically (and stop sub-allocating).
138     constexpr size_t kDynamicBufferMaxSize = 4 * 1024;
139     const size_t alignedSize               = roundUp(dataSize, *alignmentOut);
140     const size_t suballocationCount        = kDynamicBufferMaxSize / alignedSize;
141     const size_t initialSize               = isDynamic ? alignedSize * suballocationCount : 0;
142 
143     return initialSize;
144 }
145 
ShouldAllocateNewMemoryForUpdate(ContextVk * contextVk,size_t subDataSize,size_t bufferSize)146 ANGLE_INLINE bool ShouldAllocateNewMemoryForUpdate(ContextVk *contextVk,
147                                                    size_t subDataSize,
148                                                    size_t bufferSize)
149 {
150     // A sub data update with size > 50% of buffer size meets the threshold
151     // to acquire a new BufferHelper from the pool.
152     return contextVk->getRenderer()->getFeatures().preferCPUForBufferSubData.enabled ||
153            subDataSize > (bufferSize / 2);
154 }
155 
ShouldUseCPUToCopyData(ContextVk * contextVk,size_t copySize,size_t bufferSize)156 ANGLE_INLINE bool ShouldUseCPUToCopyData(ContextVk *contextVk, size_t copySize, size_t bufferSize)
157 {
158     RendererVk *renderer = contextVk->getRenderer();
159     // For some GPU (ARM) we always prefer using CPU to do copy instead of use GPU to avoid pipeline
160     // bubbles. If GPU is currently busy and data copy size is less than certain threshold, we
161     // choose to use CPU to do data copy over GPU to achieve better parallelism.
162     return renderer->getFeatures().preferCPUForBufferSubData.enabled ||
163            (renderer->isCommandQueueBusy() &&
164             copySize < renderer->getMaxCopyBytesUsingCPUWhenPreservingBufferData());
165 }
166 
IsUsageDynamic(gl::BufferUsage usage)167 ANGLE_INLINE bool IsUsageDynamic(gl::BufferUsage usage)
168 {
169     return (usage == gl::BufferUsage::DynamicDraw || usage == gl::BufferUsage::DynamicCopy ||
170             usage == gl::BufferUsage::DynamicRead);
171 }
172 }  // namespace
173 
174 // ConversionBuffer implementation.
ConversionBuffer(RendererVk * renderer,VkBufferUsageFlags usageFlags,size_t initialSize,size_t alignment,bool hostVisible)175 ConversionBuffer::ConversionBuffer(RendererVk *renderer,
176                                    VkBufferUsageFlags usageFlags,
177                                    size_t initialSize,
178                                    size_t alignment,
179                                    bool hostVisible)
180     : dirty(true), lastAllocationOffset(0)
181 {
182     data.init(renderer, usageFlags, alignment, initialSize, hostVisible,
183               vk::DynamicBufferPolicy::OneShotUse);
184 }
185 
186 ConversionBuffer::~ConversionBuffer() = default;
187 
188 ConversionBuffer::ConversionBuffer(ConversionBuffer &&other) = default;
189 
190 // BufferVk::VertexConversionBuffer implementation.
VertexConversionBuffer(RendererVk * renderer,angle::FormatID formatIDIn,GLuint strideIn,size_t offsetIn,bool hostVisible)191 BufferVk::VertexConversionBuffer::VertexConversionBuffer(RendererVk *renderer,
192                                                          angle::FormatID formatIDIn,
193                                                          GLuint strideIn,
194                                                          size_t offsetIn,
195                                                          bool hostVisible)
196     : ConversionBuffer(renderer,
197                        vk::kVertexBufferUsageFlags,
198                        kConvertedArrayBufferInitialSize,
199                        vk::kVertexBufferAlignment,
200                        hostVisible),
201       formatID(formatIDIn),
202       stride(strideIn),
203       offset(offsetIn)
204 {}
205 
206 BufferVk::VertexConversionBuffer::VertexConversionBuffer(VertexConversionBuffer &&other) = default;
207 
208 BufferVk::VertexConversionBuffer::~VertexConversionBuffer() = default;
209 
210 // BufferVk implementation.
BufferVk(const gl::BufferState & state)211 BufferVk::BufferVk(const gl::BufferState &state)
212     : BufferImpl(state),
213       mBuffer(nullptr),
214       mBufferOffset(0),
215       mMapInvalidateRangeStagingBuffer(nullptr),
216       mMapInvalidateRangeStagingBufferOffset(0),
217       mMapInvalidateRangeMappedPtr(nullptr),
218       mHasValidData(false),
219       mHasBeenReferencedByGPU(false)
220 {}
221 
~BufferVk()222 BufferVk::~BufferVk() {}
223 
destroy(const gl::Context * context)224 void BufferVk::destroy(const gl::Context *context)
225 {
226     ContextVk *contextVk = vk::GetImpl(context);
227 
228     release(contextVk);
229 }
230 
release(ContextVk * contextVk)231 void BufferVk::release(ContextVk *contextVk)
232 {
233     RendererVk *renderer = contextVk->getRenderer();
234     // For external buffers, mBuffer is not a reference to a chunk in mBufferPool.
235     // It was allocated explicitly and needs to be deallocated during release(...)
236     if (mBuffer && mBuffer->isExternalBuffer())
237     {
238         mBuffer->release(renderer);
239     }
240     mShadowBuffer.release();
241     mBufferPool.release(renderer);
242     mHostVisibleBufferPool.release(renderer);
243     mBuffer       = nullptr;
244     mBufferOffset = 0;
245 
246     for (ConversionBuffer &buffer : mVertexConversionBuffers)
247     {
248         buffer.data.release(renderer);
249     }
250 }
251 
initializeShadowBuffer(ContextVk * contextVk,gl::BufferBinding target,size_t size)252 angle::Result BufferVk::initializeShadowBuffer(ContextVk *contextVk,
253                                                gl::BufferBinding target,
254                                                size_t size)
255 {
256     if (!contextVk->getRenderer()->getFeatures().shadowBuffers.enabled)
257     {
258         return angle::Result::Continue;
259     }
260 
261     // For now, enable shadow buffers only for pixel unpack buffers.
262     // If usecases present themselves, we can enable them for other buffer types.
263     // Note: If changed, update the waitForIdle message in BufferVk::copySubData to reflect it.
264     if (target == gl::BufferBinding::PixelUnpack)
265     {
266         // Initialize the shadow buffer
267         mShadowBuffer.init(size);
268 
269         // Allocate required memory. If allocation fails, treat it is a non-fatal error
270         // since we do not need the shadow buffer for functionality
271         ANGLE_TRY(mShadowBuffer.allocate(size));
272     }
273 
274     return angle::Result::Continue;
275 }
276 
initializeHostVisibleBufferPool(ContextVk * contextVk)277 void BufferVk::initializeHostVisibleBufferPool(ContextVk *contextVk)
278 {
279     // These buffers will only be used as transfer sources or transfer targets.
280     constexpr VkImageUsageFlags kUsageFlags =
281         VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
282 
283     // These buffers need to be host visible.
284     constexpr VkMemoryPropertyFlags kDeviceLocalHostCoherentFlags =
285         (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
286 
287     constexpr size_t kBufferHelperAlignment       = 1;
288     constexpr size_t kBufferHelperPoolInitialSize = 0;
289 
290     mHostVisibleBufferPool.initWithFlags(
291         contextVk->getRenderer(), kUsageFlags, kBufferHelperAlignment, kBufferHelperPoolInitialSize,
292         kDeviceLocalHostCoherentFlags, vk::DynamicBufferPolicy::SporadicTextureUpload);
293 }
294 
updateShadowBuffer(const uint8_t * data,size_t size,size_t offset)295 void BufferVk::updateShadowBuffer(const uint8_t *data, size_t size, size_t offset)
296 {
297     if (mShadowBuffer.valid())
298     {
299         mShadowBuffer.updateData(data, size, offset);
300     }
301 }
302 
setExternalBufferData(const gl::Context * context,gl::BufferBinding target,GLeglClientBufferEXT clientBuffer,size_t size,VkMemoryPropertyFlags memoryPropertyFlags)303 angle::Result BufferVk::setExternalBufferData(const gl::Context *context,
304                                               gl::BufferBinding target,
305                                               GLeglClientBufferEXT clientBuffer,
306                                               size_t size,
307                                               VkMemoryPropertyFlags memoryPropertyFlags)
308 {
309     ContextVk *contextVk = vk::GetImpl(context);
310 
311     // Release and re-create the memory and buffer.
312     release(contextVk);
313 
314     // We could potentially use multiple backing buffers for different usages.
315     // For now keep a single buffer with all relevant usage flags.
316     VkImageUsageFlags usageFlags =
317         VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
318         VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
319         VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
320         VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
321 
322     if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
323     {
324         usageFlags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
325     }
326 
327     std::unique_ptr<vk::BufferHelper> buffer = std::make_unique<vk::BufferHelper>();
328 
329     VkBufferCreateInfo createInfo    = {};
330     createInfo.sType                 = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
331     createInfo.flags                 = 0;
332     createInfo.size                  = size;
333     createInfo.usage                 = usageFlags;
334     createInfo.sharingMode           = VK_SHARING_MODE_EXCLUSIVE;
335     createInfo.queueFamilyIndexCount = 0;
336     createInfo.pQueueFamilyIndices   = nullptr;
337 
338     ANGLE_TRY(buffer->initExternal(contextVk, memoryPropertyFlags, createInfo, clientBuffer));
339 
340     ASSERT(!mBuffer);
341     mBuffer       = buffer.release();
342     mBufferOffset = 0;
343 
344     return angle::Result::Continue;
345 }
346 
setDataWithUsageFlags(const gl::Context * context,gl::BufferBinding target,GLeglClientBufferEXT clientBuffer,const void * data,size_t size,gl::BufferUsage usage,GLbitfield flags)347 angle::Result BufferVk::setDataWithUsageFlags(const gl::Context *context,
348                                               gl::BufferBinding target,
349                                               GLeglClientBufferEXT clientBuffer,
350                                               const void *data,
351                                               size_t size,
352                                               gl::BufferUsage usage,
353                                               GLbitfield flags)
354 {
355     VkMemoryPropertyFlags memoryPropertyFlags = 0;
356     bool persistentMapRequired                = false;
357     const bool isExternalBuffer               = clientBuffer != nullptr;
358 
359     switch (usage)
360     {
361         case gl::BufferUsage::InvalidEnum:
362         {
363             // glBufferStorage API call
364             memoryPropertyFlags   = GetStorageMemoryType(flags, isExternalBuffer);
365             persistentMapRequired = (flags & GL_MAP_PERSISTENT_BIT_EXT) != 0;
366             break;
367         }
368         default:
369         {
370             // glBufferData API call
371             memoryPropertyFlags = GetPreferredMemoryType(target, usage);
372             break;
373         }
374     }
375 
376     if (isExternalBuffer)
377     {
378         ANGLE_TRY(setExternalBufferData(context, target, clientBuffer, size, memoryPropertyFlags));
379         if (!mBuffer->isHostVisible())
380         {
381             // If external buffer's memory does not support host visible memory property, we cannot
382             // support a persistent map request.
383             ANGLE_VK_CHECK(vk::GetImpl(context), !persistentMapRequired,
384                            VK_ERROR_MEMORY_MAP_FAILED);
385 
386             // Since external buffer is not host visible, allocate a host visible buffer pool
387             // to handle map/unmap operations.
388             initializeHostVisibleBufferPool(vk::GetImpl(context));
389         }
390 
391         return angle::Result::Continue;
392     }
393     return setDataWithMemoryType(context, target, data, size, memoryPropertyFlags,
394                                  persistentMapRequired, usage);
395 }
396 
setData(const gl::Context * context,gl::BufferBinding target,const void * data,size_t size,gl::BufferUsage usage)397 angle::Result BufferVk::setData(const gl::Context *context,
398                                 gl::BufferBinding target,
399                                 const void *data,
400                                 size_t size,
401                                 gl::BufferUsage usage)
402 {
403     // Assume host visible/coherent memory available.
404     VkMemoryPropertyFlags memoryPropertyFlags = GetPreferredMemoryType(target, usage);
405     return setDataWithMemoryType(context, target, data, size, memoryPropertyFlags, false, usage);
406 }
407 
setDataWithMemoryType(const gl::Context * context,gl::BufferBinding target,const void * data,size_t size,VkMemoryPropertyFlags memoryPropertyFlags,bool persistentMapRequired,gl::BufferUsage usage)408 angle::Result BufferVk::setDataWithMemoryType(const gl::Context *context,
409                                               gl::BufferBinding target,
410                                               const void *data,
411                                               size_t size,
412                                               VkMemoryPropertyFlags memoryPropertyFlags,
413                                               bool persistentMapRequired,
414                                               gl::BufferUsage usage)
415 {
416     ContextVk *contextVk = vk::GetImpl(context);
417     RendererVk *renderer = contextVk->getRenderer();
418 
419     // Reset the flag since the buffer contents are being reinitialized. If the caller passed in
420     // data to fill the buffer, the flag will be updated when the data is copied to the buffer.
421     mHasValidData = false;
422 
423     if (size == 0)
424     {
425         // Nothing to do.
426         return angle::Result::Continue;
427     }
428 
429     const bool wholeSize = size == static_cast<size_t>(mState.getSize());
430 
431     // BufferData call is re-specifying the entire buffer
432     // Release and init a new mBuffer with this new size
433     if (!wholeSize)
434     {
435         // Release and re-create the memory and buffer.
436         release(contextVk);
437 
438         // We could potentially use multiple backing buffers for different usages.
439         // For now keep a single buffer with all relevant usage flags.
440         VkImageUsageFlags usageFlags =
441             VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
442             VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
443             VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
444             VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT |
445             VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
446 
447         if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
448         {
449             usageFlags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
450         }
451 
452         size_t bufferHelperAlignment = 0;
453         const size_t bufferHelperPoolInitialSize =
454             GetPreferredDynamicBufferInitialSize(renderer, size, usage, &bufferHelperAlignment);
455 
456         mBufferPool.initWithFlags(renderer, usageFlags, bufferHelperAlignment,
457                                   bufferHelperPoolInitialSize, memoryPropertyFlags,
458                                   vk::DynamicBufferPolicy::FrequentSmallAllocations);
459 
460         ANGLE_TRY(acquireBufferHelper(contextVk, size, BufferUpdateType::StorageRedefined));
461 
462         // persistentMapRequired may request that the server read from or write to the buffer while
463         // it is mapped. The client's pointer to the data store remains valid so long as the data
464         // store is mapped. So it cannot have shadow buffer
465         if (!persistentMapRequired)
466         {
467             // Initialize the shadow buffer
468             ANGLE_TRY(initializeShadowBuffer(contextVk, target, size));
469         }
470     }
471 
472     if (data)
473     {
474         // Treat full-buffer updates as SubData calls.
475         BufferUpdateType updateType =
476             wholeSize ? BufferUpdateType::ContentsUpdate : BufferUpdateType::StorageRedefined;
477 
478         ANGLE_TRY(setDataImpl(contextVk, static_cast<const uint8_t *>(data), size, 0, updateType));
479     }
480 
481     return angle::Result::Continue;
482 }
483 
setSubData(const gl::Context * context,gl::BufferBinding target,const void * data,size_t size,size_t offset)484 angle::Result BufferVk::setSubData(const gl::Context *context,
485                                    gl::BufferBinding target,
486                                    const void *data,
487                                    size_t size,
488                                    size_t offset)
489 {
490     ASSERT(mBuffer && mBuffer->valid());
491 
492     ContextVk *contextVk = vk::GetImpl(context);
493     ANGLE_TRY(setDataImpl(contextVk, static_cast<const uint8_t *>(data), size, offset,
494                           BufferUpdateType::ContentsUpdate));
495 
496     return angle::Result::Continue;
497 }
498 
copySubData(const gl::Context * context,BufferImpl * source,GLintptr sourceOffset,GLintptr destOffset,GLsizeiptr size)499 angle::Result BufferVk::copySubData(const gl::Context *context,
500                                     BufferImpl *source,
501                                     GLintptr sourceOffset,
502                                     GLintptr destOffset,
503                                     GLsizeiptr size)
504 {
505     ASSERT(mBuffer && mBuffer->valid());
506 
507     ContextVk *contextVk            = vk::GetImpl(context);
508     BufferVk *sourceVk              = GetAs<BufferVk>(source);
509     VkDeviceSize sourceBufferOffset = 0;
510     vk::BufferHelper &sourceBuffer  = sourceVk->getBufferAndOffset(&sourceBufferOffset);
511     ASSERT(sourceBuffer.valid());
512 
513     // If the shadow buffer is enabled for the destination buffer then
514     // we need to update that as well. This will require us to complete
515     // all recorded and in-flight commands involving the source buffer.
516     if (mShadowBuffer.valid())
517     {
518         // Map the source buffer.
519         void *mapPtr;
520         ANGLE_TRY(sourceVk->mapRangeImpl(contextVk, sourceOffset, size, GL_MAP_READ_BIT, &mapPtr));
521 
522         // Update the shadow buffer with data from source buffer
523         updateShadowBuffer(static_cast<uint8_t *>(mapPtr), size, destOffset);
524 
525         // Unmap the source buffer
526         ANGLE_TRY(sourceVk->unmapImpl(contextVk));
527     }
528 
529     // Check for self-dependency.
530     vk::CommandBufferAccess access;
531     if (sourceBuffer.getBufferSerial() == mBuffer->getBufferSerial())
532     {
533         access.onBufferSelfCopy(mBuffer);
534     }
535     else
536     {
537         access.onBufferTransferRead(&sourceBuffer);
538         access.onBufferTransferWrite(mBuffer);
539     }
540 
541     vk::CommandBuffer *commandBuffer;
542     ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer(access, &commandBuffer));
543 
544     // Enqueue a copy command on the GPU.
545     const VkBufferCopy copyRegion = {static_cast<VkDeviceSize>(sourceOffset) + sourceBufferOffset,
546                                      static_cast<VkDeviceSize>(destOffset) + mBufferOffset,
547                                      static_cast<VkDeviceSize>(size)};
548 
549     commandBuffer->copyBuffer(sourceBuffer.getBuffer(), mBuffer->getBuffer(), 1, &copyRegion);
550     mHasBeenReferencedByGPU = true;
551 
552     // The new destination buffer data may require a conversion for the next draw, so mark it dirty.
553     onDataChanged();
554 
555     return angle::Result::Continue;
556 }
557 
handleDeviceLocalBufferMap(ContextVk * contextVk,VkDeviceSize offset,VkDeviceSize size,uint8_t ** mapPtr)558 angle::Result BufferVk::handleDeviceLocalBufferMap(ContextVk *contextVk,
559                                                    VkDeviceSize offset,
560                                                    VkDeviceSize size,
561                                                    uint8_t **mapPtr)
562 {
563     // The buffer is device local, create a copy of the buffer and return its CPU pointer.
564     bool needToReleasePreviousBuffers = false;
565     ANGLE_TRY(mHostVisibleBufferPool.allocate(contextVk, static_cast<size_t>(size), mapPtr, nullptr,
566                                               &mHostVisibleBufferOffset,
567                                               &needToReleasePreviousBuffers));
568     if (needToReleasePreviousBuffers)
569     {
570         // Release previous buffers
571         mHostVisibleBufferPool.releaseInFlightBuffers(contextVk);
572     }
573 
574     // Copy data from device local buffer to host visible staging buffer.
575     vk::BufferHelper *hostVisibleBuffer = mHostVisibleBufferPool.getCurrentBuffer();
576     ASSERT(hostVisibleBuffer && hostVisibleBuffer->valid());
577 
578     VkBufferCopy copyRegion = {mBufferOffset + offset, mHostVisibleBufferOffset, size};
579     ANGLE_TRY(hostVisibleBuffer->copyFromBuffer(contextVk, mBuffer, 1, &copyRegion));
580     ANGLE_TRY(hostVisibleBuffer->waitForIdle(contextVk,
581                                              "GPU stall due to mapping device local buffer",
582                                              RenderPassClosureReason::DeviceLocalBufferMap));
583 
584     return angle::Result::Continue;
585 }
586 
handleDeviceLocalBufferUnmap(ContextVk * contextVk,VkDeviceSize offset,VkDeviceSize size)587 angle::Result BufferVk::handleDeviceLocalBufferUnmap(ContextVk *contextVk,
588                                                      VkDeviceSize offset,
589                                                      VkDeviceSize size)
590 {
591     // Copy data from the host visible buffer into the device local buffer.
592     vk::BufferHelper *hostVisibleBuffer = mHostVisibleBufferPool.getCurrentBuffer();
593     ASSERT(hostVisibleBuffer && hostVisibleBuffer->valid());
594 
595     VkBufferCopy copyRegion = {mHostVisibleBufferOffset, mBufferOffset + offset, size};
596     ANGLE_TRY(mBuffer->copyFromBuffer(contextVk, hostVisibleBuffer, 1, &copyRegion));
597     mHasBeenReferencedByGPU = true;
598 
599     return angle::Result::Continue;
600 }
601 
map(const gl::Context * context,GLenum access,void ** mapPtr)602 angle::Result BufferVk::map(const gl::Context *context, GLenum access, void **mapPtr)
603 {
604     ASSERT(mBuffer && mBuffer->valid());
605     ASSERT(access == GL_WRITE_ONLY_OES);
606 
607     return mapImpl(vk::GetImpl(context), GL_MAP_WRITE_BIT, mapPtr);
608 }
609 
mapRange(const gl::Context * context,size_t offset,size_t length,GLbitfield access,void ** mapPtr)610 angle::Result BufferVk::mapRange(const gl::Context *context,
611                                  size_t offset,
612                                  size_t length,
613                                  GLbitfield access,
614                                  void **mapPtr)
615 {
616     ANGLE_TRACE_EVENT0("gpu.angle", "BufferVk::mapRange");
617     return mapRangeImpl(vk::GetImpl(context), offset, length, access, mapPtr);
618 }
619 
mapImpl(ContextVk * contextVk,GLbitfield access,void ** mapPtr)620 angle::Result BufferVk::mapImpl(ContextVk *contextVk, GLbitfield access, void **mapPtr)
621 {
622     return mapRangeImpl(contextVk, 0, static_cast<VkDeviceSize>(mState.getSize()), access, mapPtr);
623 }
624 
ghostMappedBuffer(ContextVk * contextVk,VkDeviceSize offset,VkDeviceSize length,GLbitfield access,void ** mapPtr)625 angle::Result BufferVk::ghostMappedBuffer(ContextVk *contextVk,
626                                           VkDeviceSize offset,
627                                           VkDeviceSize length,
628                                           GLbitfield access,
629                                           void **mapPtr)
630 {
631     vk::BufferHelper *previousBuffer = nullptr;
632     VkDeviceSize previousOffset      = 0;
633 
634     ++contextVk->getPerfCounters().buffersGhosted;
635 
636     // If we are creating a new buffer because the GPU is using it as read-only, then we
637     // also need to copy the contents of the previous buffer into the new buffer, in
638     // case the caller only updates a portion of the new buffer.
639     previousBuffer = mBuffer;
640     previousOffset = mBufferOffset;
641     ANGLE_TRY(acquireBufferHelper(contextVk, static_cast<size_t>(mState.getSize()),
642                                   BufferUpdateType::ContentsUpdate));
643 
644     // Before returning the new buffer, map the previous buffer and copy its entire
645     // contents into the new buffer.
646     uint8_t *previousBufferMapPtr = nullptr;
647     uint8_t *newBufferMapPtr      = nullptr;
648     ANGLE_TRY(previousBuffer->mapWithOffset(contextVk, &previousBufferMapPtr,
649                                             static_cast<size_t>(previousOffset)));
650     ANGLE_TRY(
651         mBuffer->mapWithOffset(contextVk, &newBufferMapPtr, static_cast<size_t>(mBufferOffset)));
652 
653     ASSERT(previousBuffer->isCoherent());
654     ASSERT(mBuffer->isCoherent());
655 
656     // No need to copy over [offset, offset + length), just around it
657     if ((access & GL_MAP_INVALIDATE_RANGE_BIT) != 0)
658     {
659         if (offset != 0)
660         {
661             memcpy(newBufferMapPtr, previousBufferMapPtr, static_cast<size_t>(offset));
662         }
663         size_t totalSize      = static_cast<size_t>(mState.getSize());
664         size_t remainingStart = static_cast<size_t>(offset + length);
665         size_t remainingSize  = totalSize - remainingStart;
666         if (remainingSize != 0)
667         {
668             memcpy(newBufferMapPtr + remainingStart, previousBufferMapPtr + remainingStart,
669                    remainingSize);
670         }
671     }
672     else
673     {
674         memcpy(newBufferMapPtr, previousBufferMapPtr, static_cast<size_t>(mState.getSize()));
675     }
676 
677     previousBuffer->unmap(contextVk->getRenderer());
678     // Return the already mapped pointer with the offset adjustment to avoid the call to unmap().
679     *mapPtr = newBufferMapPtr + offset;
680 
681     return angle::Result::Continue;
682 }
683 
mapRangeImpl(ContextVk * contextVk,VkDeviceSize offset,VkDeviceSize length,GLbitfield access,void ** mapPtr)684 angle::Result BufferVk::mapRangeImpl(ContextVk *contextVk,
685                                      VkDeviceSize offset,
686                                      VkDeviceSize length,
687                                      GLbitfield access,
688                                      void **mapPtr)
689 {
690     uint8_t **mapPtrBytes = reinterpret_cast<uint8_t **>(mapPtr);
691 
692     if (mShadowBuffer.valid())
693     {
694         // If the app requested a GL_MAP_UNSYNCHRONIZED_BIT access, the spec states -
695         //      No GL error is generated if pending operations which source or modify the
696         //      buffer overlap the mapped region, but the result of such previous and any
697         //      subsequent operations is undefined
698         // To keep the code simple, irrespective of whether the access was GL_MAP_UNSYNCHRONIZED_BIT
699         // or not, just return the shadow buffer.
700         mShadowBuffer.map(static_cast<size_t>(offset), mapPtrBytes);
701         return angle::Result::Continue;
702     }
703 
704     ASSERT(mBuffer && mBuffer->valid());
705 
706     bool hostVisible = mBuffer->isHostVisible();
707 
708     // MAP_UNSYNCHRONIZED_BIT, so immediately map.
709     if ((access & GL_MAP_UNSYNCHRONIZED_BIT) != 0)
710     {
711         if (hostVisible)
712         {
713             return mBuffer->mapWithOffset(contextVk, mapPtrBytes,
714                                           static_cast<size_t>(mBufferOffset + offset));
715         }
716         return handleDeviceLocalBufferMap(contextVk, offset, length, mapPtrBytes);
717     }
718 
719     // Read case
720     if ((access & GL_MAP_WRITE_BIT) == 0)
721     {
722         // If app is not going to write, all we need is to ensure GPU write is finished.
723         // Concurrent reads from CPU and GPU is allowed.
724         if (mBuffer->isCurrentlyInUseForWrite(contextVk->getLastCompletedQueueSerial()))
725         {
726             // If there are pending commands for the resource, flush them.
727             if (mBuffer->usedInRecordedCommands())
728             {
729                 ANGLE_TRY(
730                     contextVk->flushImpl(nullptr, RenderPassClosureReason::BufferWriteThenMap));
731             }
732             ANGLE_TRY(mBuffer->finishGPUWriteCommands(contextVk));
733         }
734         if (hostVisible)
735         {
736             return mBuffer->mapWithOffset(contextVk, mapPtrBytes,
737                                           static_cast<size_t>(mBufferOffset + offset));
738         }
739         return handleDeviceLocalBufferMap(contextVk, offset, length, mapPtrBytes);
740     }
741 
742     // Write case
743     if (!hostVisible)
744     {
745         return handleDeviceLocalBufferMap(contextVk, offset, length, mapPtrBytes);
746     }
747 
748     // Write case, buffer not in use.
749     if (mBuffer->isExternalBuffer() || !isCurrentlyInUse(contextVk))
750     {
751         return mBuffer->mapWithOffset(contextVk, mapPtrBytes,
752                                       static_cast<size_t>(mBufferOffset + offset));
753     }
754 
755     // Write case, buffer in use.
756     //
757     // Here, we try to map the buffer, but it's busy. Instead of waiting for the GPU to
758     // finish, we just allocate a new buffer if:
759     // 1.) Caller has told us it doesn't care about previous contents, or
760     // 2.) The GPU won't write to the buffer.
761 
762     bool rangeInvalidate = (access & GL_MAP_INVALIDATE_RANGE_BIT) != 0;
763     bool entireBufferInvalidated =
764         ((access & GL_MAP_INVALIDATE_BUFFER_BIT) != 0) ||
765         (rangeInvalidate && offset == 0 && static_cast<VkDeviceSize>(mState.getSize()) == length);
766 
767     if (entireBufferInvalidated)
768     {
769         ANGLE_TRY(acquireBufferHelper(contextVk, static_cast<size_t>(mState.getSize()),
770                                       BufferUpdateType::ContentsUpdate));
771         return mBuffer->mapWithOffset(contextVk, mapPtrBytes,
772                                       static_cast<size_t>(mBufferOffset + offset));
773     }
774 
775     bool smallMapRange = (length < static_cast<VkDeviceSize>(mState.getSize()) / 2);
776 
777     if (smallMapRange && rangeInvalidate)
778     {
779         ANGLE_TRY(allocMappedStagingBuffer(
780             contextVk, static_cast<size_t>(length), &mMapInvalidateRangeStagingBuffer,
781             &mMapInvalidateRangeStagingBufferOffset, &mMapInvalidateRangeMappedPtr));
782         *mapPtrBytes = mMapInvalidateRangeMappedPtr;
783         return angle::Result::Continue;
784     }
785 
786     if (!mBuffer->isCurrentlyInUseForWrite(contextVk->getLastCompletedQueueSerial()))
787     {
788         // This will keep the new buffer mapped and update mapPtr, so return immediately.
789         return ghostMappedBuffer(contextVk, offset, length, access, mapPtr);
790     }
791 
792     // Write case (worst case, buffer in use for write)
793     ANGLE_TRY(mBuffer->waitForIdle(contextVk, "GPU stall due to mapping buffer in use by the GPU",
794                                    RenderPassClosureReason::BufferInUseWhenSynchronizedMap));
795     return mBuffer->mapWithOffset(contextVk, mapPtrBytes,
796                                   static_cast<size_t>(mBufferOffset + offset));
797 }
798 
unmap(const gl::Context * context,GLboolean * result)799 angle::Result BufferVk::unmap(const gl::Context *context, GLboolean *result)
800 {
801     ANGLE_TRY(unmapImpl(vk::GetImpl(context)));
802 
803     // This should be false if the contents have been corrupted through external means.  Vulkan
804     // doesn't provide such information.
805     *result = true;
806 
807     return angle::Result::Continue;
808 }
809 
unmapImpl(ContextVk * contextVk)810 angle::Result BufferVk::unmapImpl(ContextVk *contextVk)
811 {
812     ASSERT(mBuffer && mBuffer->valid());
813 
814     bool writeOperation = ((mState.getAccessFlags() & GL_MAP_WRITE_BIT) != 0);
815 
816     if (mMapInvalidateRangeMappedPtr != nullptr)
817     {
818         ASSERT(!mShadowBuffer.valid());
819         ANGLE_TRY(flushMappedStagingBuffer(contextVk, mMapInvalidateRangeStagingBuffer,
820                                            mMapInvalidateRangeStagingBufferOffset,
821                                            static_cast<size_t>(mState.getMapLength()),
822                                            static_cast<size_t>(mState.getMapOffset())));
823         mMapInvalidateRangeMappedPtr = nullptr;
824     }
825     else if (!mShadowBuffer.valid() && mBuffer->isHostVisible())
826     {
827         mBuffer->unmap(contextVk->getRenderer());
828     }
829     else
830     {
831         size_t offset = static_cast<size_t>(mState.getMapOffset());
832         size_t size   = static_cast<size_t>(mState.getMapLength());
833 
834         // If it was a write operation we need to update the buffer with new data.
835         if (writeOperation)
836         {
837             if (mShadowBuffer.valid())
838             {
839                 // We do not yet know if this data will ever be used. Perform a staged
840                 // update which will get flushed if and when necessary.
841                 const uint8_t *data = getShadowBuffer(offset);
842                 ANGLE_TRY(stagedUpdate(contextVk, data, size, offset));
843                 mShadowBuffer.unmap();
844             }
845             else
846             {
847                 // The buffer is device local.
848                 ASSERT(!mBuffer->isHostVisible());
849                 ANGLE_TRY(handleDeviceLocalBufferUnmap(contextVk, offset, size));
850             }
851         }
852     }
853 
854     if (writeOperation)
855     {
856         dataUpdated();
857     }
858 
859     return angle::Result::Continue;
860 }
861 
getSubData(const gl::Context * context,GLintptr offset,GLsizeiptr size,void * outData)862 angle::Result BufferVk::getSubData(const gl::Context *context,
863                                    GLintptr offset,
864                                    GLsizeiptr size,
865                                    void *outData)
866 {
867     ASSERT(offset + size <= getSize());
868     if (!mShadowBuffer.valid())
869     {
870         ASSERT(mBuffer && mBuffer->valid());
871         ContextVk *contextVk = vk::GetImpl(context);
872         void *mapPtr;
873         ANGLE_TRY(mapRangeImpl(contextVk, offset, size, GL_MAP_READ_BIT, &mapPtr));
874         memcpy(outData, mapPtr, size);
875         ANGLE_TRY(unmapImpl(contextVk));
876     }
877     else
878     {
879         memcpy(outData, mShadowBuffer.getCurrentBuffer() + offset, size);
880     }
881     return angle::Result::Continue;
882 }
883 
getIndexRange(const gl::Context * context,gl::DrawElementsType type,size_t offset,size_t count,bool primitiveRestartEnabled,gl::IndexRange * outRange)884 angle::Result BufferVk::getIndexRange(const gl::Context *context,
885                                       gl::DrawElementsType type,
886                                       size_t offset,
887                                       size_t count,
888                                       bool primitiveRestartEnabled,
889                                       gl::IndexRange *outRange)
890 {
891     ContextVk *contextVk = vk::GetImpl(context);
892     RendererVk *renderer = contextVk->getRenderer();
893 
894     // This is a workaround for the mock ICD not implementing buffer memory state.
895     // Could be removed if https://github.com/KhronosGroup/Vulkan-Tools/issues/84 is fixed.
896     if (renderer->isMockICDEnabled())
897     {
898         outRange->start = 0;
899         outRange->end   = 0;
900         return angle::Result::Continue;
901     }
902 
903     ANGLE_TRACE_EVENT0("gpu.angle", "BufferVk::getIndexRange");
904 
905     void *mapPtr;
906     ANGLE_TRY(mapRangeImpl(contextVk, offset, getSize(), GL_MAP_READ_BIT, &mapPtr));
907     *outRange = gl::ComputeIndexRange(type, mapPtr, count, primitiveRestartEnabled);
908     ANGLE_TRY(unmapImpl(contextVk));
909 
910     return angle::Result::Continue;
911 }
912 
updateBuffer(ContextVk * contextVk,const uint8_t * data,size_t size,size_t offset)913 angle::Result BufferVk::updateBuffer(ContextVk *contextVk,
914                                      const uint8_t *data,
915                                      size_t size,
916                                      size_t offset)
917 {
918     if (mBuffer->isHostVisible())
919     {
920         ANGLE_TRY(directUpdate(contextVk, data, size, offset));
921     }
922     else
923     {
924         ANGLE_TRY(stagedUpdate(contextVk, data, size, offset));
925     }
926     return angle::Result::Continue;
927 }
directUpdate(ContextVk * contextVk,const uint8_t * data,size_t size,size_t offset)928 angle::Result BufferVk::directUpdate(ContextVk *contextVk,
929                                      const uint8_t *data,
930                                      size_t size,
931                                      size_t offset)
932 {
933     uint8_t *mapPointer = nullptr;
934 
935     ANGLE_TRY(mBuffer->mapWithOffset(contextVk, &mapPointer,
936                                      static_cast<size_t>(mBufferOffset) + offset));
937     ASSERT(mapPointer);
938 
939     memcpy(mapPointer, data, size);
940 
941     // If the buffer has dynamic usage then the intent is frequent client side updates to the
942     // buffer. Don't CPU unmap the buffer, we will take care of unmapping when releasing the buffer
943     // to either the renderer or mBufferFreeList.
944     if (!IsUsageDynamic(mState.getUsage()))
945     {
946         mBuffer->unmap(contextVk->getRenderer());
947     }
948     ASSERT(mBuffer->isCoherent());
949 
950     return angle::Result::Continue;
951 }
952 
stagedUpdate(ContextVk * contextVk,const uint8_t * data,size_t size,size_t offset)953 angle::Result BufferVk::stagedUpdate(ContextVk *contextVk,
954                                      const uint8_t *data,
955                                      size_t size,
956                                      size_t offset)
957 {
958     // Acquire a "new" staging buffer
959     vk::DynamicBuffer *stagingBuffer = nullptr;
960     VkDeviceSize stagingBufferOffset = 0;
961     uint8_t *mapPointer              = nullptr;
962 
963     ANGLE_TRY(allocMappedStagingBuffer(contextVk, size, &stagingBuffer, &stagingBufferOffset,
964                                        &mapPointer));
965     memcpy(mapPointer, data, size);
966     ANGLE_TRY(
967         flushMappedStagingBuffer(contextVk, stagingBuffer, stagingBufferOffset, size, offset));
968 
969     return angle::Result::Continue;
970 }
971 
allocMappedStagingBuffer(ContextVk * contextVk,size_t size,vk::DynamicBuffer ** stagingBuffer,VkDeviceSize * stagingBufferOffset,uint8_t ** mapPtr)972 angle::Result BufferVk::allocMappedStagingBuffer(ContextVk *contextVk,
973                                                  size_t size,
974                                                  vk::DynamicBuffer **stagingBuffer,
975                                                  VkDeviceSize *stagingBufferOffset,
976                                                  uint8_t **mapPtr)
977 {
978     // Acquire a "new" staging buffer
979     ASSERT(mapPtr);
980     ASSERT(stagingBuffer);
981 
982     *stagingBuffer = contextVk->getStagingBuffer();
983 
984     ASSERT(*stagingBuffer);
985 
986     ANGLE_TRY(
987         (*stagingBuffer)->allocate(contextVk, size, mapPtr, nullptr, stagingBufferOffset, nullptr));
988     ASSERT(*mapPtr);
989 
990     return angle::Result::Continue;
991 }
992 
flushMappedStagingBuffer(ContextVk * contextVk,vk::DynamicBuffer * stagingBuffer,VkDeviceSize stagingBufferOffset,size_t size,size_t offset)993 angle::Result BufferVk::flushMappedStagingBuffer(ContextVk *contextVk,
994                                                  vk::DynamicBuffer *stagingBuffer,
995                                                  VkDeviceSize stagingBufferOffset,
996                                                  size_t size,
997                                                  size_t offset)
998 {
999     ANGLE_TRY(stagingBuffer->flush(contextVk));
1000 
1001     // Enqueue a copy command on the GPU.
1002     VkBufferCopy copyRegion = {stagingBufferOffset, mBufferOffset + offset, size};
1003     ANGLE_TRY(
1004         mBuffer->copyFromBuffer(contextVk, stagingBuffer->getCurrentBuffer(), 1, &copyRegion));
1005     mHasBeenReferencedByGPU = true;
1006 
1007     return angle::Result::Continue;
1008 }
1009 
acquireAndUpdate(ContextVk * contextVk,const uint8_t * data,size_t updateSize,size_t offset,BufferUpdateType updateType)1010 angle::Result BufferVk::acquireAndUpdate(ContextVk *contextVk,
1011                                          const uint8_t *data,
1012                                          size_t updateSize,
1013                                          size_t offset,
1014                                          BufferUpdateType updateType)
1015 {
1016     // Here we acquire a new BufferHelper and directUpdate() the new buffer.
1017     // If the subData size was less than the buffer's size we additionally enqueue
1018     // a GPU copy of the remaining regions from the old mBuffer to the new one.
1019     vk::BufferHelper *src          = mBuffer;
1020     size_t bufferSize              = static_cast<size_t>(mState.getSize());
1021     size_t offsetAfterSubdata      = (offset + updateSize);
1022     bool updateRegionBeforeSubData = mHasValidData && (offset > 0);
1023     bool updateRegionAfterSubData  = mHasValidData && (offsetAfterSubdata < bufferSize);
1024 
1025     uint8_t *srcMapPtrBeforeSubData = nullptr;
1026     uint8_t *srcMapPtrAfterSubData  = nullptr;
1027     if (updateRegionBeforeSubData || updateRegionAfterSubData)
1028     {
1029         // It's possible for acquireBufferHelper() to garbage collect the original (src) buffer
1030         // before copyFromBuffer() has a chance to retain it, so retain it now. This may end up
1031         // double-retaining the buffer, which is a necessary side-effect to prevent a
1032         // use-after-free.
1033         src->retainReadOnly(&contextVk->getResourceUseList());
1034 
1035         // The total bytes that we need to copy from old buffer to new buffer
1036         size_t copySize = bufferSize - updateSize;
1037 
1038         // If the buffer is host visible and the GPU is done writing to, we use the CPU to do the
1039         // copy. We need to save the source buffer pointer before we acquire a new buffer.
1040         if (src->isHostVisible() &&
1041             !src->isCurrentlyInUseForWrite(contextVk->getLastCompletedQueueSerial()) &&
1042             ShouldUseCPUToCopyData(contextVk, copySize, bufferSize))
1043         {
1044             uint8_t *mapPointer = nullptr;
1045             // src buffer will be recycled (or released and unmapped) by acquireBufferHelper
1046             ANGLE_TRY(
1047                 src->mapWithOffset(contextVk, &mapPointer, static_cast<size_t>(mBufferOffset)));
1048             ASSERT(mapPointer);
1049             srcMapPtrBeforeSubData = mapPointer;
1050             srcMapPtrAfterSubData  = mapPointer + offsetAfterSubdata;
1051         }
1052     }
1053 
1054     ANGLE_TRY(acquireBufferHelper(contextVk, bufferSize, updateType));
1055     ANGLE_TRY(updateBuffer(contextVk, data, updateSize, offset));
1056 
1057     constexpr int kMaxCopyRegions = 2;
1058     angle::FixedVector<VkBufferCopy, kMaxCopyRegions> copyRegions;
1059 
1060     if (updateRegionBeforeSubData)
1061     {
1062         if (srcMapPtrBeforeSubData)
1063         {
1064             ASSERT(mBuffer->isHostVisible());
1065             ANGLE_TRY(directUpdate(contextVk, srcMapPtrBeforeSubData, offset, 0));
1066         }
1067         else
1068         {
1069             copyRegions.push_back({0, mBufferOffset, offset});
1070         }
1071     }
1072 
1073     if (updateRegionAfterSubData)
1074     {
1075         size_t copySize = bufferSize - offsetAfterSubdata;
1076         if (srcMapPtrAfterSubData)
1077         {
1078             ASSERT(mBuffer->isHostVisible());
1079             ANGLE_TRY(directUpdate(contextVk, srcMapPtrAfterSubData, copySize, offsetAfterSubdata));
1080         }
1081         else
1082         {
1083             copyRegions.push_back(
1084                 {offsetAfterSubdata, mBufferOffset + offsetAfterSubdata, copySize});
1085         }
1086     }
1087 
1088     if (!copyRegions.empty())
1089     {
1090         ANGLE_TRY(mBuffer->copyFromBuffer(contextVk, src, static_cast<uint32_t>(copyRegions.size()),
1091                                           copyRegions.data()));
1092         mHasBeenReferencedByGPU = true;
1093     }
1094 
1095     return angle::Result::Continue;
1096 }
1097 
setDataImpl(ContextVk * contextVk,const uint8_t * data,size_t size,size_t offset,BufferUpdateType updateType)1098 angle::Result BufferVk::setDataImpl(ContextVk *contextVk,
1099                                     const uint8_t *data,
1100                                     size_t size,
1101                                     size_t offset,
1102                                     BufferUpdateType updateType)
1103 {
1104     // Update shadow buffer
1105     updateShadowBuffer(data, size, offset);
1106 
1107     // if the buffer is currently in use
1108     //     if it isn't an external buffer and sub data size meets threshold
1109     //          acquire a new BufferHelper from the pool
1110     //     else stage the update
1111     // else update the buffer directly
1112     if (isCurrentlyInUse(contextVk))
1113     {
1114         // If BufferVk does not have any valid data, which means there is no data needs to be copied
1115         // from old buffer to new buffer when we acquire a new buffer, we also favor
1116         // acquireAndUpdate over stagedUpdate. This could happen when app calls glBufferData with
1117         // same size and we will try to reuse the existing buffer storage.
1118         if (!mBuffer->isExternalBuffer() &&
1119             (!mHasValidData || ShouldAllocateNewMemoryForUpdate(
1120                                    contextVk, size, static_cast<size_t>(mState.getSize()))))
1121         {
1122             ANGLE_TRY(acquireAndUpdate(contextVk, data, size, offset, updateType));
1123         }
1124         else
1125         {
1126             ANGLE_TRY(stagedUpdate(contextVk, data, size, offset));
1127         }
1128     }
1129     else
1130     {
1131         ANGLE_TRY(updateBuffer(contextVk, data, size, offset));
1132     }
1133 
1134     // Update conversions
1135     dataUpdated();
1136 
1137     return angle::Result::Continue;
1138 }
1139 
getVertexConversionBuffer(RendererVk * renderer,angle::FormatID formatID,GLuint stride,size_t offset,bool hostVisible)1140 ConversionBuffer *BufferVk::getVertexConversionBuffer(RendererVk *renderer,
1141                                                       angle::FormatID formatID,
1142                                                       GLuint stride,
1143                                                       size_t offset,
1144                                                       bool hostVisible)
1145 {
1146     for (VertexConversionBuffer &buffer : mVertexConversionBuffers)
1147     {
1148         if (buffer.formatID == formatID && buffer.stride == stride && buffer.offset == offset)
1149         {
1150             return &buffer;
1151         }
1152     }
1153 
1154     mVertexConversionBuffers.emplace_back(renderer, formatID, stride, offset, hostVisible);
1155     return &mVertexConversionBuffers.back();
1156 }
1157 
dataUpdated()1158 void BufferVk::dataUpdated()
1159 {
1160     for (VertexConversionBuffer &buffer : mVertexConversionBuffers)
1161     {
1162         buffer.dirty = true;
1163     }
1164     // Now we have valid data
1165     mHasValidData = true;
1166 }
1167 
onDataChanged()1168 void BufferVk::onDataChanged()
1169 {
1170     dataUpdated();
1171 }
1172 
acquireBufferHelper(ContextVk * contextVk,size_t sizeInBytes,BufferUpdateType updateType)1173 angle::Result BufferVk::acquireBufferHelper(ContextVk *contextVk,
1174                                             size_t sizeInBytes,
1175                                             BufferUpdateType updateType)
1176 {
1177     // This method should not be called if it is an ExternalBuffer
1178     ASSERT(mBuffer == nullptr || mBuffer->isExternalBuffer() == false);
1179 
1180     bool needToReleasePreviousBuffers = false;
1181     size_t size                       = roundUpPow2(sizeInBytes, kBufferSizeGranularity);
1182 
1183     ANGLE_TRY(mBufferPool.allocate(contextVk, size, nullptr, nullptr, &mBufferOffset,
1184                                    &needToReleasePreviousBuffers));
1185 
1186     // We just got a new range, no one has ever referenced it yet.
1187     mHasBeenReferencedByGPU = false;
1188 
1189     if (needToReleasePreviousBuffers)
1190     {
1191         // Release previous buffers
1192         mBufferPool.releaseInFlightBuffers(contextVk);
1193     }
1194 
1195     mBuffer = mBufferPool.getCurrentBuffer();
1196     ASSERT(mBuffer);
1197 
1198     if (updateType == BufferUpdateType::ContentsUpdate)
1199     {
1200         // Tell the observers (front end) that a new buffer was created, so the necessary
1201         // dirty bits can be set. This allows the buffer views pointing to the old buffer to
1202         // be recreated and point to the new buffer, along with updating the descriptor sets
1203         // to use the new buffer.
1204         onStateChange(angle::SubjectMessage::InternalMemoryAllocationChanged);
1205     }
1206 
1207     return angle::Result::Continue;
1208 }
1209 
isCurrentlyInUse(ContextVk * contextVk) const1210 bool BufferVk::isCurrentlyInUse(ContextVk *contextVk) const
1211 {
1212     return mHasBeenReferencedByGPU &&
1213            mBuffer->isCurrentlyInUse(contextVk->getLastCompletedQueueSerial());
1214 }
1215 
1216 }  // namespace rx
1217