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