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, ©Region);
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, ©Region));
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, ©Region));
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, ©Region));
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