• 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 
SubDataSizeMeetsThreshold(size_t subDataSize,size_t bufferSize)146 ANGLE_INLINE bool SubDataSizeMeetsThreshold(size_t subDataSize, size_t bufferSize)
147 {
148     // A sub data update with size > 50% of buffer size meets the threshold
149     // to acquire a new BufferHelper from the pool.
150     return subDataSize > (bufferSize / 2);
151 }
152 
IsUsageDynamic(gl::BufferUsage usage)153 ANGLE_INLINE bool IsUsageDynamic(gl::BufferUsage usage)
154 {
155     return (usage == gl::BufferUsage::DynamicDraw || usage == gl::BufferUsage::DynamicCopy ||
156             usage == gl::BufferUsage::DynamicRead);
157 }
158 }  // namespace
159 
160 // ConversionBuffer implementation.
ConversionBuffer(RendererVk * renderer,VkBufferUsageFlags usageFlags,size_t initialSize,size_t alignment,bool hostVisible)161 ConversionBuffer::ConversionBuffer(RendererVk *renderer,
162                                    VkBufferUsageFlags usageFlags,
163                                    size_t initialSize,
164                                    size_t alignment,
165                                    bool hostVisible)
166     : dirty(true), lastAllocationOffset(0)
167 {
168     data.init(renderer, usageFlags, alignment, initialSize, hostVisible,
169               vk::DynamicBufferPolicy::OneShotUse);
170 }
171 
172 ConversionBuffer::~ConversionBuffer() = default;
173 
174 ConversionBuffer::ConversionBuffer(ConversionBuffer &&other) = default;
175 
176 // BufferVk::VertexConversionBuffer implementation.
VertexConversionBuffer(RendererVk * renderer,angle::FormatID formatIDIn,GLuint strideIn,size_t offsetIn,bool hostVisible)177 BufferVk::VertexConversionBuffer::VertexConversionBuffer(RendererVk *renderer,
178                                                          angle::FormatID formatIDIn,
179                                                          GLuint strideIn,
180                                                          size_t offsetIn,
181                                                          bool hostVisible)
182     : ConversionBuffer(renderer,
183                        vk::kVertexBufferUsageFlags,
184                        kConvertedArrayBufferInitialSize,
185                        vk::kVertexBufferAlignment,
186                        hostVisible),
187       formatID(formatIDIn),
188       stride(strideIn),
189       offset(offsetIn)
190 {}
191 
192 BufferVk::VertexConversionBuffer::VertexConversionBuffer(VertexConversionBuffer &&other) = default;
193 
194 BufferVk::VertexConversionBuffer::~VertexConversionBuffer() = default;
195 
196 // BufferVk implementation.
BufferVk(const gl::BufferState & state)197 BufferVk::BufferVk(const gl::BufferState &state)
198     : BufferImpl(state), mBuffer(nullptr), mBufferOffset(0)
199 {}
200 
~BufferVk()201 BufferVk::~BufferVk() {}
202 
destroy(const gl::Context * context)203 void BufferVk::destroy(const gl::Context *context)
204 {
205     ContextVk *contextVk = vk::GetImpl(context);
206 
207     release(contextVk);
208 }
209 
release(ContextVk * contextVk)210 void BufferVk::release(ContextVk *contextVk)
211 {
212     RendererVk *renderer = contextVk->getRenderer();
213     // For external buffers, mBuffer is not a reference to a chunk in mBufferPool.
214     // It was allocated explicitly and needs to be deallocated during release(...)
215     if (mBuffer && mBuffer->isExternalBuffer())
216     {
217         mBuffer->release(renderer);
218     }
219     mShadowBuffer.release();
220     mBufferPool.release(renderer);
221     mHostVisibleBufferPool.release(renderer);
222     mBuffer       = nullptr;
223     mBufferOffset = 0;
224 
225     for (ConversionBuffer &buffer : mVertexConversionBuffers)
226     {
227         buffer.data.release(renderer);
228     }
229 }
230 
initializeShadowBuffer(ContextVk * contextVk,gl::BufferBinding target,size_t size)231 angle::Result BufferVk::initializeShadowBuffer(ContextVk *contextVk,
232                                                gl::BufferBinding target,
233                                                size_t size)
234 {
235     if (!contextVk->getRenderer()->getFeatures().shadowBuffers.enabled)
236     {
237         return angle::Result::Continue;
238     }
239 
240     // For now, enable shadow buffers only for pixel unpack buffers.
241     // If usecases present themselves, we can enable them for other buffer types.
242     // Note: If changed, update the waitForIdle message in BufferVk::copySubData to reflect it.
243     if (target == gl::BufferBinding::PixelUnpack)
244     {
245         // Initialize the shadow buffer
246         mShadowBuffer.init(size);
247 
248         // Allocate required memory. If allocation fails, treat it is a non-fatal error
249         // since we do not need the shadow buffer for functionality
250         ANGLE_TRY(mShadowBuffer.allocate(size));
251     }
252 
253     return angle::Result::Continue;
254 }
255 
initializeHostVisibleBufferPool(ContextVk * contextVk)256 void BufferVk::initializeHostVisibleBufferPool(ContextVk *contextVk)
257 {
258     // These buffers will only be used as transfer sources or transfer targets.
259     constexpr VkImageUsageFlags kUsageFlags =
260         VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
261 
262     // These buffers need to be host visible.
263     constexpr VkMemoryPropertyFlags kDeviceLocalHostCoherentFlags =
264         (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
265 
266     constexpr size_t kBufferHelperAlignment       = 1;
267     constexpr size_t kBufferHelperPoolInitialSize = 0;
268 
269     mHostVisibleBufferPool.initWithFlags(
270         contextVk->getRenderer(), kUsageFlags, kBufferHelperAlignment, kBufferHelperPoolInitialSize,
271         kDeviceLocalHostCoherentFlags, vk::DynamicBufferPolicy::SporadicTextureUpload);
272 }
273 
updateShadowBuffer(const uint8_t * data,size_t size,size_t offset)274 void BufferVk::updateShadowBuffer(const uint8_t *data, size_t size, size_t offset)
275 {
276     if (mShadowBuffer.valid())
277     {
278         mShadowBuffer.updateData(data, size, offset);
279     }
280 }
281 
setExternalBufferData(const gl::Context * context,gl::BufferBinding target,GLeglClientBufferEXT clientBuffer,size_t size,VkMemoryPropertyFlags memoryPropertyFlags)282 angle::Result BufferVk::setExternalBufferData(const gl::Context *context,
283                                               gl::BufferBinding target,
284                                               GLeglClientBufferEXT clientBuffer,
285                                               size_t size,
286                                               VkMemoryPropertyFlags memoryPropertyFlags)
287 {
288     ContextVk *contextVk = vk::GetImpl(context);
289 
290     // Release and re-create the memory and buffer.
291     release(contextVk);
292 
293     // We could potentially use multiple backing buffers for different usages.
294     // For now keep a single buffer with all relevant usage flags.
295     VkImageUsageFlags usageFlags =
296         VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
297         VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
298         VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
299         VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
300 
301     if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
302     {
303         usageFlags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
304     }
305 
306     std::unique_ptr<vk::BufferHelper> buffer = std::make_unique<vk::BufferHelper>();
307 
308     VkBufferCreateInfo createInfo    = {};
309     createInfo.sType                 = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
310     createInfo.flags                 = 0;
311     createInfo.size                  = size;
312     createInfo.usage                 = usageFlags;
313     createInfo.sharingMode           = VK_SHARING_MODE_EXCLUSIVE;
314     createInfo.queueFamilyIndexCount = 0;
315     createInfo.pQueueFamilyIndices   = nullptr;
316 
317     ANGLE_TRY(buffer->initExternal(contextVk, memoryPropertyFlags, createInfo, clientBuffer));
318 
319     ASSERT(!mBuffer);
320     mBuffer       = buffer.release();
321     mBufferOffset = 0;
322 
323     return angle::Result::Continue;
324 }
325 
setDataWithUsageFlags(const gl::Context * context,gl::BufferBinding target,GLeglClientBufferEXT clientBuffer,const void * data,size_t size,gl::BufferUsage usage,GLbitfield flags)326 angle::Result BufferVk::setDataWithUsageFlags(const gl::Context *context,
327                                               gl::BufferBinding target,
328                                               GLeglClientBufferEXT clientBuffer,
329                                               const void *data,
330                                               size_t size,
331                                               gl::BufferUsage usage,
332                                               GLbitfield flags)
333 {
334     VkMemoryPropertyFlags memoryPropertyFlags = 0;
335     bool persistentMapRequired                = false;
336     const bool isExternalBuffer               = clientBuffer != nullptr;
337 
338     switch (usage)
339     {
340         case gl::BufferUsage::InvalidEnum:
341         {
342             // glBufferStorage API call
343             memoryPropertyFlags   = GetStorageMemoryType(flags, isExternalBuffer);
344             persistentMapRequired = (flags & GL_MAP_PERSISTENT_BIT_EXT) != 0;
345             break;
346         }
347         default:
348         {
349             // glBufferData API call
350             memoryPropertyFlags = GetPreferredMemoryType(target, usage);
351             break;
352         }
353     }
354 
355     if (isExternalBuffer)
356     {
357         ANGLE_TRY(setExternalBufferData(context, target, clientBuffer, size, memoryPropertyFlags));
358         if (!mBuffer->isHostVisible())
359         {
360             // If external buffer's memory does not support host visible memory property, we cannot
361             // support a persistent map request.
362             ANGLE_VK_CHECK(vk::GetImpl(context), !persistentMapRequired,
363                            VK_ERROR_MEMORY_MAP_FAILED);
364 
365             // Since external buffer is not host visible, allocate a host visible buffer pool
366             // to handle map/unmap operations.
367             initializeHostVisibleBufferPool(vk::GetImpl(context));
368         }
369 
370         return angle::Result::Continue;
371     }
372     return setDataWithMemoryType(context, target, data, size, memoryPropertyFlags,
373                                  persistentMapRequired, usage);
374 }
375 
setData(const gl::Context * context,gl::BufferBinding target,const void * data,size_t size,gl::BufferUsage usage)376 angle::Result BufferVk::setData(const gl::Context *context,
377                                 gl::BufferBinding target,
378                                 const void *data,
379                                 size_t size,
380                                 gl::BufferUsage usage)
381 {
382     // Assume host visible/coherent memory available.
383     VkMemoryPropertyFlags memoryPropertyFlags = GetPreferredMemoryType(target, usage);
384     return setDataWithMemoryType(context, target, data, size, memoryPropertyFlags, false, usage);
385 }
386 
setDataWithMemoryType(const gl::Context * context,gl::BufferBinding target,const void * data,size_t size,VkMemoryPropertyFlags memoryPropertyFlags,bool persistentMapRequired,gl::BufferUsage usage)387 angle::Result BufferVk::setDataWithMemoryType(const gl::Context *context,
388                                               gl::BufferBinding target,
389                                               const void *data,
390                                               size_t size,
391                                               VkMemoryPropertyFlags memoryPropertyFlags,
392                                               bool persistentMapRequired,
393                                               gl::BufferUsage usage)
394 {
395     ContextVk *contextVk = vk::GetImpl(context);
396     RendererVk *renderer = contextVk->getRenderer();
397 
398     if (size == 0)
399     {
400         // Nothing to do.
401         return angle::Result::Continue;
402     }
403 
404     // BufferData call is re-specifying the entire buffer
405     // Release and init a new mBuffer with this new size
406     if (size != static_cast<size_t>(mState.getSize()))
407     {
408         // Release and re-create the memory and buffer.
409         release(contextVk);
410 
411         // We could potentially use multiple backing buffers for different usages.
412         // For now keep a single buffer with all relevant usage flags.
413         VkImageUsageFlags usageFlags =
414             VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
415             VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
416             VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
417             VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT |
418             VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
419 
420         if (contextVk->getFeatures().supportsTransformFeedbackExtension.enabled)
421         {
422             usageFlags |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT;
423         }
424 
425         size_t bufferHelperAlignment = 0;
426         const size_t bufferHelperPoolInitialSize =
427             GetPreferredDynamicBufferInitialSize(renderer, size, usage, &bufferHelperAlignment);
428 
429         mBufferPool.initWithFlags(renderer, usageFlags, bufferHelperAlignment,
430                                   bufferHelperPoolInitialSize, memoryPropertyFlags,
431                                   vk::DynamicBufferPolicy::FrequentSmallAllocations);
432 
433         ANGLE_TRY(acquireBufferHelper(contextVk, size));
434 
435         // persistentMapRequired may request that the server read from or write to the buffer while
436         // it is mapped. The client's pointer to the data store remains valid so long as the data
437         // store is mapped. So it cannot have shadow buffer
438         if (!persistentMapRequired)
439         {
440             // Initialize the shadow buffer
441             ANGLE_TRY(initializeShadowBuffer(contextVk, target, size));
442         }
443     }
444 
445     if (data)
446     {
447         ANGLE_TRY(setDataImpl(contextVk, static_cast<const uint8_t *>(data), size, 0));
448     }
449 
450     return angle::Result::Continue;
451 }
452 
setSubData(const gl::Context * context,gl::BufferBinding target,const void * data,size_t size,size_t offset)453 angle::Result BufferVk::setSubData(const gl::Context *context,
454                                    gl::BufferBinding target,
455                                    const void *data,
456                                    size_t size,
457                                    size_t offset)
458 {
459     ASSERT(mBuffer && mBuffer->valid());
460 
461     ContextVk *contextVk = vk::GetImpl(context);
462     ANGLE_TRY(setDataImpl(contextVk, static_cast<const uint8_t *>(data), size, offset));
463 
464     return angle::Result::Continue;
465 }
466 
copySubData(const gl::Context * context,BufferImpl * source,GLintptr sourceOffset,GLintptr destOffset,GLsizeiptr size)467 angle::Result BufferVk::copySubData(const gl::Context *context,
468                                     BufferImpl *source,
469                                     GLintptr sourceOffset,
470                                     GLintptr destOffset,
471                                     GLsizeiptr size)
472 {
473     ASSERT(mBuffer && mBuffer->valid());
474 
475     ContextVk *contextVk            = vk::GetImpl(context);
476     BufferVk *sourceVk              = GetAs<BufferVk>(source);
477     VkDeviceSize sourceBufferOffset = 0;
478     vk::BufferHelper &sourceBuffer  = sourceVk->getBufferAndOffset(&sourceBufferOffset);
479     ASSERT(sourceBuffer.valid());
480 
481     // If the shadow buffer is enabled for the destination buffer then
482     // we need to update that as well. This will require us to complete
483     // all recorded and in-flight commands involving the source buffer.
484     if (mShadowBuffer.valid())
485     {
486         // Map the source buffer.
487         void *mapPtr;
488         ANGLE_TRY(sourceVk->mapRangeImpl(contextVk, sourceOffset, size, 0, &mapPtr));
489 
490         // Update the shadow buffer with data from source buffer
491         updateShadowBuffer(static_cast<uint8_t *>(mapPtr), size, destOffset);
492 
493         // Unmap the source buffer
494         ANGLE_TRY(sourceVk->unmapImpl(contextVk));
495     }
496 
497     // Check for self-dependency.
498     vk::CommandBufferAccess access;
499     if (sourceBuffer.getBufferSerial() == mBuffer->getBufferSerial())
500     {
501         access.onBufferSelfCopy(mBuffer);
502     }
503     else
504     {
505         access.onBufferTransferRead(&sourceBuffer);
506         access.onBufferTransferWrite(mBuffer);
507     }
508 
509     vk::CommandBuffer *commandBuffer;
510     ANGLE_TRY(contextVk->getOutsideRenderPassCommandBuffer(access, &commandBuffer));
511 
512     // Enqueue a copy command on the GPU.
513     const VkBufferCopy copyRegion = {static_cast<VkDeviceSize>(sourceOffset) + sourceBufferOffset,
514                                      static_cast<VkDeviceSize>(destOffset) + mBufferOffset,
515                                      static_cast<VkDeviceSize>(size)};
516 
517     commandBuffer->copyBuffer(sourceBuffer.getBuffer(), mBuffer->getBuffer(), 1, &copyRegion);
518 
519     // The new destination buffer data may require a conversion for the next draw, so mark it dirty.
520     onDataChanged();
521 
522     return angle::Result::Continue;
523 }
524 
handleDeviceLocalBufferMap(ContextVk * contextVk,VkDeviceSize offset,VkDeviceSize size,void ** mapPtr)525 angle::Result BufferVk::handleDeviceLocalBufferMap(ContextVk *contextVk,
526                                                    VkDeviceSize offset,
527                                                    VkDeviceSize size,
528                                                    void **mapPtr)
529 {
530     // The buffer is device local, create a copy of the buffer and return its CPU pointer.
531     bool needToReleasePreviousBuffers = false;
532     ANGLE_TRY(mHostVisibleBufferPool.allocate(
533         contextVk, static_cast<size_t>(size), reinterpret_cast<uint8_t **>(mapPtr), nullptr,
534         &mHostVisibleBufferOffset, &needToReleasePreviousBuffers));
535     if (needToReleasePreviousBuffers)
536     {
537         // Release previous buffers
538         mHostVisibleBufferPool.releaseInFlightBuffers(contextVk);
539     }
540 
541     // Copy data from device local buffer to host visible staging buffer.
542     vk::BufferHelper *hostVisibleBuffer = mHostVisibleBufferPool.getCurrentBuffer();
543     ASSERT(hostVisibleBuffer && hostVisibleBuffer->valid());
544 
545     VkBufferCopy copyRegion = {mBufferOffset + offset, mHostVisibleBufferOffset, size};
546     ANGLE_TRY(hostVisibleBuffer->copyFromBuffer(contextVk, mBuffer, 1, &copyRegion));
547     ANGLE_TRY(
548         hostVisibleBuffer->waitForIdle(contextVk, "GPU stall due to mapping device local buffer"));
549 
550     return angle::Result::Continue;
551 }
552 
handleDeviceLocalBufferUnmap(ContextVk * contextVk,VkDeviceSize offset,VkDeviceSize size)553 angle::Result BufferVk::handleDeviceLocalBufferUnmap(ContextVk *contextVk,
554                                                      VkDeviceSize offset,
555                                                      VkDeviceSize size)
556 {
557     // Copy data from the host visible buffer into the device local buffer.
558     vk::BufferHelper *hostVisibleBuffer = mHostVisibleBufferPool.getCurrentBuffer();
559     ASSERT(hostVisibleBuffer && hostVisibleBuffer->valid());
560 
561     VkBufferCopy copyRegion = {mHostVisibleBufferOffset, mBufferOffset + offset, size};
562     ANGLE_TRY(mBuffer->copyFromBuffer(contextVk, hostVisibleBuffer, 1, &copyRegion));
563 
564     return angle::Result::Continue;
565 }
566 
map(const gl::Context * context,GLenum access,void ** mapPtr)567 angle::Result BufferVk::map(const gl::Context *context, GLenum access, void **mapPtr)
568 {
569     ASSERT(mBuffer && mBuffer->valid());
570 
571     return mapImpl(vk::GetImpl(context), mapPtr);
572 }
573 
mapRange(const gl::Context * context,size_t offset,size_t length,GLbitfield access,void ** mapPtr)574 angle::Result BufferVk::mapRange(const gl::Context *context,
575                                  size_t offset,
576                                  size_t length,
577                                  GLbitfield access,
578                                  void **mapPtr)
579 {
580     ANGLE_TRACE_EVENT0("gpu.angle", "BufferVk::mapRange");
581     return mapRangeImpl(vk::GetImpl(context), offset, length, access, mapPtr);
582 }
583 
mapImpl(ContextVk * contextVk,void ** mapPtr)584 angle::Result BufferVk::mapImpl(ContextVk *contextVk, void **mapPtr)
585 {
586     return mapRangeImpl(contextVk, 0, static_cast<VkDeviceSize>(mState.getSize()), 0, mapPtr);
587 }
588 
mapRangeImpl(ContextVk * contextVk,VkDeviceSize offset,VkDeviceSize length,GLbitfield access,void ** mapPtr)589 angle::Result BufferVk::mapRangeImpl(ContextVk *contextVk,
590                                      VkDeviceSize offset,
591                                      VkDeviceSize length,
592                                      GLbitfield access,
593                                      void **mapPtr)
594 {
595     if (!mShadowBuffer.valid())
596     {
597         ASSERT(mBuffer && mBuffer->valid());
598 
599         if ((access & GL_MAP_INVALIDATE_BUFFER_BIT) != 0 &&
600             mBuffer->isCurrentlyInUse(contextVk->getLastCompletedQueueSerial()) &&
601             !mBuffer->isExternalBuffer())
602         {
603             // We try to map buffer, but buffer is busy. Caller has told us it doesn't care about
604             // previous content. Instead of wait for GPU to finish, we just allocate a new buffer.
605             ANGLE_TRY(acquireBufferHelper(contextVk, static_cast<size_t>(mState.getSize())));
606         }
607         else if ((access & GL_MAP_UNSYNCHRONIZED_BIT) == 0)
608         {
609             ANGLE_TRY(mBuffer->waitForIdle(contextVk,
610                                            "GPU stall due to mapping buffer in use by the GPU"));
611         }
612 
613         if (mBuffer->isHostVisible())
614         {
615             // If the buffer is host visible, map and return.
616             ANGLE_TRY(mBuffer->mapWithOffset(contextVk, reinterpret_cast<uint8_t **>(mapPtr),
617                                              static_cast<size_t>(mBufferOffset + offset)));
618         }
619         else
620         {
621             // Handle device local buffers.
622             ANGLE_TRY(handleDeviceLocalBufferMap(contextVk, offset, length, mapPtr));
623         }
624     }
625     else
626     {
627         // If the app requested a GL_MAP_UNSYNCHRONIZED_BIT access, the spec states -
628         //      No GL error is generated if pending operations which source or modify the
629         //      buffer overlap the mapped region, but the result of such previous and any
630         //      subsequent operations is undefined
631         // To keep the code simple, irrespective of whether the access was GL_MAP_UNSYNCHRONIZED_BIT
632         // or not, just return the shadow buffer.
633         mShadowBuffer.map(static_cast<size_t>(offset), mapPtr);
634     }
635 
636     return angle::Result::Continue;
637 }
638 
unmap(const gl::Context * context,GLboolean * result)639 angle::Result BufferVk::unmap(const gl::Context *context, GLboolean *result)
640 {
641     ANGLE_TRY(unmapImpl(vk::GetImpl(context)));
642 
643     // This should be false if the contents have been corrupted through external means.  Vulkan
644     // doesn't provide such information.
645     *result = true;
646 
647     return angle::Result::Continue;
648 }
649 
unmapImpl(ContextVk * contextVk)650 angle::Result BufferVk::unmapImpl(ContextVk *contextVk)
651 {
652     ASSERT(mBuffer && mBuffer->valid());
653 
654     bool writeOperation = ((mState.getAccessFlags() & GL_MAP_WRITE_BIT) != 0);
655 
656     if (!mShadowBuffer.valid() && mBuffer->isHostVisible())
657     {
658         mBuffer->unmap(contextVk->getRenderer());
659     }
660     else
661     {
662         size_t offset = static_cast<size_t>(mState.getMapOffset());
663         size_t size   = static_cast<size_t>(mState.getMapLength());
664 
665         // If it was a write operation we need to update the buffer with new data.
666         if (writeOperation)
667         {
668             if (mShadowBuffer.valid())
669             {
670                 // We do not yet know if this data will ever be used. Perform a staged
671                 // update which will get flushed if and when necessary.
672                 const uint8_t *data = getShadowBuffer(offset);
673                 ANGLE_TRY(stagedUpdate(contextVk, data, size, offset));
674                 mShadowBuffer.unmap();
675             }
676             else
677             {
678                 // The buffer is device local.
679                 ASSERT(!mBuffer->isHostVisible());
680                 ANGLE_TRY(handleDeviceLocalBufferUnmap(contextVk, offset, size));
681             }
682         }
683     }
684 
685     if (writeOperation)
686     {
687         markConversionBuffersDirty();
688     }
689 
690     return angle::Result::Continue;
691 }
692 
getSubData(const gl::Context * context,GLintptr offset,GLsizeiptr size,void * outData)693 angle::Result BufferVk::getSubData(const gl::Context *context,
694                                    GLintptr offset,
695                                    GLsizeiptr size,
696                                    void *outData)
697 {
698     ASSERT(offset + size <= getSize());
699     if (!mShadowBuffer.valid())
700     {
701         ASSERT(mBuffer && mBuffer->valid());
702         ContextVk *contextVk = vk::GetImpl(context);
703         void *mapPtr;
704         ANGLE_TRY(mapRangeImpl(contextVk, offset, size, 0, &mapPtr));
705         memcpy(outData, mapPtr, size);
706         ANGLE_TRY(unmapImpl(contextVk));
707     }
708     else
709     {
710         memcpy(outData, mShadowBuffer.getCurrentBuffer() + offset, size);
711     }
712     return angle::Result::Continue;
713 }
714 
getIndexRange(const gl::Context * context,gl::DrawElementsType type,size_t offset,size_t count,bool primitiveRestartEnabled,gl::IndexRange * outRange)715 angle::Result BufferVk::getIndexRange(const gl::Context *context,
716                                       gl::DrawElementsType type,
717                                       size_t offset,
718                                       size_t count,
719                                       bool primitiveRestartEnabled,
720                                       gl::IndexRange *outRange)
721 {
722     ContextVk *contextVk = vk::GetImpl(context);
723     RendererVk *renderer = contextVk->getRenderer();
724 
725     // This is a workaround for the mock ICD not implementing buffer memory state.
726     // Could be removed if https://github.com/KhronosGroup/Vulkan-Tools/issues/84 is fixed.
727     if (renderer->isMockICDEnabled())
728     {
729         outRange->start = 0;
730         outRange->end   = 0;
731         return angle::Result::Continue;
732     }
733 
734     ANGLE_TRACE_EVENT0("gpu.angle", "BufferVk::getIndexRange");
735 
736     void *mapPtr;
737     ANGLE_TRY(mapRangeImpl(contextVk, offset, getSize(), 0, &mapPtr));
738     *outRange = gl::ComputeIndexRange(type, mapPtr, count, primitiveRestartEnabled);
739     ANGLE_TRY(unmapImpl(contextVk));
740 
741     return angle::Result::Continue;
742 }
743 
updateBuffer(ContextVk * contextVk,const uint8_t * data,size_t size,size_t offset)744 angle::Result BufferVk::updateBuffer(ContextVk *contextVk,
745                                      const uint8_t *data,
746                                      size_t size,
747                                      size_t offset)
748 {
749     if (mBuffer->isHostVisible())
750     {
751         ANGLE_TRY(directUpdate(contextVk, data, size, offset));
752     }
753     else
754     {
755         ANGLE_TRY(stagedUpdate(contextVk, data, size, offset));
756     }
757     return angle::Result::Continue;
758 }
directUpdate(ContextVk * contextVk,const uint8_t * data,size_t size,size_t offset)759 angle::Result BufferVk::directUpdate(ContextVk *contextVk,
760                                      const uint8_t *data,
761                                      size_t size,
762                                      size_t offset)
763 {
764     uint8_t *mapPointer = nullptr;
765 
766     ANGLE_TRY(mBuffer->mapWithOffset(contextVk, &mapPointer,
767                                      static_cast<size_t>(mBufferOffset) + offset));
768     ASSERT(mapPointer);
769 
770     memcpy(mapPointer, data, size);
771 
772     // If the buffer has dynamic usage then the intent is frequent client side updates to the
773     // buffer. Don't CPU unmap the buffer, we will take care of unmapping when releasing the buffer
774     // to either the renderer or mBufferFreeList.
775     if (!IsUsageDynamic(mState.getUsage()))
776     {
777         mBuffer->unmap(contextVk->getRenderer());
778     }
779     ASSERT(mBuffer->isCoherent());
780 
781     return angle::Result::Continue;
782 }
783 
stagedUpdate(ContextVk * contextVk,const uint8_t * data,size_t size,size_t offset)784 angle::Result BufferVk::stagedUpdate(ContextVk *contextVk,
785                                      const uint8_t *data,
786                                      size_t size,
787                                      size_t offset)
788 {
789     // Acquire a "new" staging buffer
790     uint8_t *mapPointer              = nullptr;
791     VkDeviceSize stagingBufferOffset = 0;
792 
793     vk::DynamicBuffer *stagingBuffer = contextVk->getStagingBuffer();
794     ANGLE_TRY(stagingBuffer->allocate(contextVk, size, &mapPointer, nullptr, &stagingBufferOffset,
795                                       nullptr));
796     ASSERT(mapPointer);
797 
798     memcpy(mapPointer, data, size);
799     ASSERT(!stagingBuffer->isCoherent());
800     ANGLE_TRY(stagingBuffer->flush(contextVk));
801 
802     // Enqueue a copy command on the GPU.
803     VkBufferCopy copyRegion = {stagingBufferOffset, mBufferOffset + offset, size};
804     ANGLE_TRY(
805         mBuffer->copyFromBuffer(contextVk, stagingBuffer->getCurrentBuffer(), 1, &copyRegion));
806 
807     return angle::Result::Continue;
808 }
809 
acquireAndUpdate(ContextVk * contextVk,const uint8_t * data,size_t updateSize,size_t offset)810 angle::Result BufferVk::acquireAndUpdate(ContextVk *contextVk,
811                                          const uint8_t *data,
812                                          size_t updateSize,
813                                          size_t offset)
814 {
815     // Here we acquire a new BufferHelper and directUpdate() the new buffer.
816     // If the subData size was less than the buffer's size we additionally enqueue
817     // a GPU copy of the remaining regions from the old mBuffer to the new one.
818     vk::BufferHelper *src          = mBuffer;
819     size_t bufferSize              = static_cast<size_t>(mState.getSize());
820     size_t offsetAfterSubdata      = (offset + updateSize);
821     bool updateRegionBeforeSubData = (offset > 0);
822     bool updateRegionAfterSubData  = (offsetAfterSubdata < bufferSize);
823 
824     if (updateRegionBeforeSubData || updateRegionAfterSubData)
825     {
826         src->retain(&contextVk->getResourceUseList());
827     }
828 
829     ANGLE_TRY(acquireBufferHelper(contextVk, bufferSize));
830     ANGLE_TRY(updateBuffer(contextVk, data, updateSize, offset));
831 
832     constexpr int kMaxCopyRegions = 2;
833     angle::FixedVector<VkBufferCopy, kMaxCopyRegions> copyRegions;
834 
835     if (updateRegionBeforeSubData)
836     {
837         copyRegions.push_back({0, mBufferOffset, offset});
838     }
839     if (updateRegionAfterSubData)
840     {
841         copyRegions.push_back({offsetAfterSubdata, mBufferOffset + offsetAfterSubdata,
842                                (bufferSize - offsetAfterSubdata)});
843     }
844 
845     if (!copyRegions.empty())
846     {
847         ANGLE_TRY(mBuffer->copyFromBuffer(contextVk, src, static_cast<uint32_t>(copyRegions.size()),
848                                           copyRegions.data()));
849     }
850 
851     return angle::Result::Continue;
852 }
853 
setDataImpl(ContextVk * contextVk,const uint8_t * data,size_t size,size_t offset)854 angle::Result BufferVk::setDataImpl(ContextVk *contextVk,
855                                     const uint8_t *data,
856                                     size_t size,
857                                     size_t offset)
858 {
859     // Update shadow buffer
860     updateShadowBuffer(data, size, offset);
861 
862     // if the buffer is currently in use
863     //     if it isn't an external buffer and sub data size meets threshold
864     //          acquire a new BufferHelper from the pool
865     //     else stage the update
866     // else update the buffer directly
867     if (mBuffer->isCurrentlyInUse(contextVk->getLastCompletedQueueSerial()))
868     {
869         if (!mBuffer->isExternalBuffer() &&
870             SubDataSizeMeetsThreshold(size, static_cast<size_t>(mState.getSize())))
871         {
872             ANGLE_TRY(acquireAndUpdate(contextVk, data, size, offset));
873         }
874         else
875         {
876             ANGLE_TRY(stagedUpdate(contextVk, data, size, offset));
877         }
878     }
879     else
880     {
881         ANGLE_TRY(updateBuffer(contextVk, data, size, offset));
882     }
883 
884     // Update conversions
885     markConversionBuffersDirty();
886 
887     return angle::Result::Continue;
888 }
889 
getVertexConversionBuffer(RendererVk * renderer,angle::FormatID formatID,GLuint stride,size_t offset,bool hostVisible)890 ConversionBuffer *BufferVk::getVertexConversionBuffer(RendererVk *renderer,
891                                                       angle::FormatID formatID,
892                                                       GLuint stride,
893                                                       size_t offset,
894                                                       bool hostVisible)
895 {
896     for (VertexConversionBuffer &buffer : mVertexConversionBuffers)
897     {
898         if (buffer.formatID == formatID && buffer.stride == stride && buffer.offset == offset)
899         {
900             return &buffer;
901         }
902     }
903 
904     mVertexConversionBuffers.emplace_back(renderer, formatID, stride, offset, hostVisible);
905     return &mVertexConversionBuffers.back();
906 }
907 
markConversionBuffersDirty()908 void BufferVk::markConversionBuffersDirty()
909 {
910     for (VertexConversionBuffer &buffer : mVertexConversionBuffers)
911     {
912         buffer.dirty = true;
913     }
914 }
915 
onDataChanged()916 void BufferVk::onDataChanged()
917 {
918     markConversionBuffersDirty();
919 }
920 
acquireBufferHelper(ContextVk * contextVk,size_t sizeInBytes)921 angle::Result BufferVk::acquireBufferHelper(ContextVk *contextVk, size_t sizeInBytes)
922 {
923     // This method should not be called if it is an ExternalBuffer
924     ASSERT(mBuffer == nullptr || mBuffer->isExternalBuffer() == false);
925 
926     bool needToReleasePreviousBuffers = false;
927     size_t size                       = roundUpPow2(sizeInBytes, kBufferSizeGranularity);
928 
929     ANGLE_TRY(mBufferPool.allocate(contextVk, size, nullptr, nullptr, &mBufferOffset,
930                                    &needToReleasePreviousBuffers));
931 
932     if (needToReleasePreviousBuffers)
933     {
934         // Release previous buffers
935         mBufferPool.releaseInFlightBuffers(contextVk);
936     }
937 
938     mBuffer = mBufferPool.getCurrentBuffer();
939     ASSERT(mBuffer);
940 
941     return angle::Result::Continue;
942 }
943 
944 }  // namespace rx
945