1 //
2 // Copyright 2018 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 // vk_helpers:
7 // Helper utilitiy classes that manage Vulkan resources.
8
9 #include "libANGLE/renderer/vulkan/vk_helpers.h"
10
11 #include "common/utilities.h"
12 #include "image_util/loadimage.h"
13 #include "libANGLE/Context.h"
14 #include "libANGLE/renderer/renderer_utils.h"
15 #include "libANGLE/renderer/vulkan/BufferVk.h"
16 #include "libANGLE/renderer/vulkan/ContextVk.h"
17 #include "libANGLE/renderer/vulkan/DisplayVk.h"
18 #include "libANGLE/renderer/vulkan/FramebufferVk.h"
19 #include "libANGLE/renderer/vulkan/RendererVk.h"
20 #include "libANGLE/renderer/vulkan/vk_utils.h"
21 #include "libANGLE/trace.h"
22
23 namespace rx
24 {
25 namespace vk
26 {
27 namespace
28 {
29 // WebGL requires color textures to be initialized to transparent black.
30 constexpr VkClearColorValue kWebGLInitColorValue = {{0, 0, 0, 0}};
31 // When emulating a texture, we want the emulated channels to be 0, with alpha 1.
32 constexpr VkClearColorValue kEmulatedInitColorValue = {{0, 0, 0, 1.0f}};
33 // WebGL requires depth/stencil textures to be initialized to depth=1, stencil=0. We are fine with
34 // these values for emulated depth/stencil textures too.
35 constexpr VkClearDepthStencilValue kWebGLInitDepthStencilValue = {1.0f, 0};
36
37 constexpr VkBufferUsageFlags kLineLoopDynamicBufferUsage =
38 VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT |
39 VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
40 constexpr int kLineLoopDynamicBufferInitialSize = 1024 * 1024;
41
42 // This is an arbitrary max. We can change this later if necessary.
43 constexpr uint32_t kDefaultDescriptorPoolMaxSets = 128;
44
45 struct ImageMemoryBarrierData
46 {
47 // The Vk layout corresponding to the ImageLayout key.
48 VkImageLayout layout;
49 // The stage in which the image is used (or Bottom/Top if not using any specific stage). Unless
50 // Bottom/Top (Bottom used for transition to and Top used for transition from), the two values
51 // should match.
52 VkPipelineStageFlags dstStageMask;
53 VkPipelineStageFlags srcStageMask;
54 // Access mask when transitioning into this layout.
55 VkAccessFlags dstAccessMask;
56 // Access mask when transitioning out from this layout. Note that source access mask never
57 // needs a READ bit, as WAR hazards don't need memory barriers (just execution barriers).
58 VkAccessFlags srcAccessMask;
59
60 // If access is read-only, the execution barrier can be skipped altogether if retransitioning to
61 // the same layout. This is because read-after-read does not need an execution or memory
62 // barrier.
63 //
64 // Otherwise, same-layout transitions only require an execution barrier (and not a memory
65 // barrier).
66 bool sameLayoutTransitionRequiresBarrier;
67 };
68
69 // clang-format off
70 constexpr angle::PackedEnumMap<ImageLayout, ImageMemoryBarrierData> kImageMemoryBarrierData = {
71 {
72 ImageLayout::Undefined,
73 {
74 VK_IMAGE_LAYOUT_UNDEFINED,
75 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
76 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
77 // Transition to: we don't expect to transition into Undefined.
78 0,
79 // Transition from: there's no data in the image to care about.
80 0,
81 false,
82 },
83 },
84 {
85 ImageLayout::ExternalPreInitialized,
86 {
87 VK_IMAGE_LAYOUT_PREINITIALIZED,
88 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
89 VK_PIPELINE_STAGE_HOST_BIT | VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
90 // Transition to: we don't expect to transition into PreInitialized.
91 0,
92 // Transition from: all writes must finish before barrier.
93 VK_ACCESS_MEMORY_WRITE_BIT,
94 false,
95 },
96 },
97 {
98 ImageLayout::TransferSrc,
99 {
100 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
101 VK_PIPELINE_STAGE_TRANSFER_BIT,
102 VK_PIPELINE_STAGE_TRANSFER_BIT,
103 // Transition to: all reads must happen after barrier.
104 VK_ACCESS_TRANSFER_READ_BIT,
105 // Transition from: RAR and WAR don't need memory barrier.
106 0,
107 false,
108 },
109 },
110 {
111 ImageLayout::TransferDst,
112 {
113 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
114 VK_PIPELINE_STAGE_TRANSFER_BIT,
115 VK_PIPELINE_STAGE_TRANSFER_BIT,
116 // Transition to: all writes must happen after barrier.
117 VK_ACCESS_TRANSFER_WRITE_BIT,
118 // Transition from: all writes must finish before barrier.
119 VK_ACCESS_TRANSFER_WRITE_BIT,
120 true,
121 },
122 },
123 {
124 ImageLayout::ComputeShaderReadOnly,
125 {
126 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
127 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
128 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
129 // Transition to: all reads must happen after barrier.
130 VK_ACCESS_SHADER_READ_BIT,
131 // Transition from: RAR and WAR don't need memory barrier.
132 0,
133 false,
134 },
135 },
136 {
137 ImageLayout::ComputeShaderWrite,
138 {
139 VK_IMAGE_LAYOUT_GENERAL,
140 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
141 VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
142 // Transition to: all reads and writes must happen after barrier.
143 VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
144 // Transition from: all writes must finish before barrier.
145 VK_ACCESS_SHADER_WRITE_BIT,
146 true,
147 },
148 },
149 {
150 ImageLayout::ColorAttachment,
151 {
152 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
153 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
154 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
155 // Transition to: all reads and writes must happen after barrier.
156 VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
157 // Transition from: all writes must finish before barrier.
158 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
159 true,
160 },
161 },
162 {
163 ImageLayout::DepthStencilAttachment,
164 {
165 VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
166 VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
167 VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
168 // Transition to: all reads and writes must happen after barrier.
169 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
170 // Transition from: all writes must finish before barrier.
171 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
172 true,
173 },
174 },
175 {
176 ImageLayout::AllGraphicsShadersReadOnly,
177 {
178 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
179 VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
180 VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
181 // Transition to: all reads must happen after barrier.
182 VK_ACCESS_SHADER_READ_BIT,
183 // Transition from: RAR and WAR don't need memory barrier.
184 0,
185 false,
186 },
187 },
188 {
189 ImageLayout::Present,
190 {
191 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
192 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
193 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
194 // transition to: vkQueuePresentKHR automatically performs the appropriate memory barriers:
195 //
196 // > Any writes to memory backing the images referenced by the pImageIndices and
197 // > pSwapchains members of pPresentInfo, that are available before vkQueuePresentKHR
198 // > is executed, are automatically made visible to the read access performed by the
199 // > presentation engine.
200 0,
201 // Transition from: RAR and WAR don't need memory barrier.
202 0,
203 false,
204 },
205 },
206 };
207 // clang-format on
208
GetImageCreateFlags(gl::TextureType textureType)209 VkImageCreateFlags GetImageCreateFlags(gl::TextureType textureType)
210 {
211 switch (textureType)
212 {
213 case gl::TextureType::CubeMap:
214 return VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
215
216 case gl::TextureType::_3D:
217 return VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
218
219 default:
220 return 0;
221 }
222 }
223
HandlePrimitiveRestart(gl::DrawElementsType glIndexType,GLsizei indexCount,const uint8_t * srcPtr,uint8_t * outPtr)224 void HandlePrimitiveRestart(gl::DrawElementsType glIndexType,
225 GLsizei indexCount,
226 const uint8_t *srcPtr,
227 uint8_t *outPtr)
228 {
229 switch (glIndexType)
230 {
231 case gl::DrawElementsType::UnsignedByte:
232 CopyLineLoopIndicesWithRestart<uint8_t, uint16_t>(indexCount, srcPtr, outPtr);
233 break;
234 case gl::DrawElementsType::UnsignedShort:
235 CopyLineLoopIndicesWithRestart<uint16_t, uint16_t>(indexCount, srcPtr, outPtr);
236 break;
237 case gl::DrawElementsType::UnsignedInt:
238 CopyLineLoopIndicesWithRestart<uint32_t, uint32_t>(indexCount, srcPtr, outPtr);
239 break;
240 default:
241 UNREACHABLE();
242 }
243 }
244 } // anonymous namespace
245
246 // DynamicBuffer implementation.
DynamicBuffer()247 DynamicBuffer::DynamicBuffer()
248 : mUsage(0),
249 mHostVisible(false),
250 mInitialSize(0),
251 mBuffer(nullptr),
252 mNextAllocationOffset(0),
253 mLastFlushOrInvalidateOffset(0),
254 mSize(0),
255 mAlignment(0)
256 {}
257
DynamicBuffer(DynamicBuffer && other)258 DynamicBuffer::DynamicBuffer(DynamicBuffer &&other)
259 : mUsage(other.mUsage),
260 mHostVisible(other.mHostVisible),
261 mInitialSize(other.mInitialSize),
262 mBuffer(other.mBuffer),
263 mNextAllocationOffset(other.mNextAllocationOffset),
264 mLastFlushOrInvalidateOffset(other.mLastFlushOrInvalidateOffset),
265 mSize(other.mSize),
266 mAlignment(other.mAlignment),
267 mInFlightBuffers(std::move(other.mInFlightBuffers))
268 {
269 other.mBuffer = nullptr;
270 }
271
init(RendererVk * renderer,VkBufferUsageFlags usage,size_t alignment,size_t initialSize,bool hostVisible)272 void DynamicBuffer::init(RendererVk *renderer,
273 VkBufferUsageFlags usage,
274 size_t alignment,
275 size_t initialSize,
276 bool hostVisible)
277 {
278 mUsage = usage;
279 mHostVisible = hostVisible;
280
281 // Check that we haven't overriden the initial size of the buffer in setMinimumSizeForTesting.
282 if (mInitialSize == 0)
283 {
284 mInitialSize = initialSize;
285 mSize = 0;
286 }
287
288 // Workaround for the mock ICD not supporting allocations greater than 0x1000.
289 // Could be removed if https://github.com/KhronosGroup/Vulkan-Tools/issues/84 is fixed.
290 if (renderer->isMockICDEnabled())
291 {
292 mSize = std::min<size_t>(mSize, 0x1000);
293 }
294
295 updateAlignment(renderer, alignment);
296 }
297
~DynamicBuffer()298 DynamicBuffer::~DynamicBuffer()
299 {
300 ASSERT(mBuffer == nullptr);
301 }
302
allocateNewBuffer(ContextVk * contextVk)303 angle::Result DynamicBuffer::allocateNewBuffer(ContextVk *contextVk)
304 {
305 std::unique_ptr<BufferHelper> buffer = std::make_unique<BufferHelper>();
306
307 VkBufferCreateInfo createInfo = {};
308 createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
309 createInfo.flags = 0;
310 createInfo.size = mSize;
311 createInfo.usage = mUsage;
312 createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
313 createInfo.queueFamilyIndexCount = 0;
314 createInfo.pQueueFamilyIndices = nullptr;
315
316 const VkMemoryPropertyFlags memoryProperty =
317 mHostVisible ? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT : VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
318 ANGLE_TRY(buffer->init(contextVk, createInfo, memoryProperty));
319
320 ASSERT(!mBuffer);
321 mBuffer = buffer.release();
322
323 return angle::Result::Continue;
324 }
325
allocate(ContextVk * contextVk,size_t sizeInBytes,uint8_t ** ptrOut,VkBuffer * bufferOut,VkDeviceSize * offsetOut,bool * newBufferAllocatedOut)326 angle::Result DynamicBuffer::allocate(ContextVk *contextVk,
327 size_t sizeInBytes,
328 uint8_t **ptrOut,
329 VkBuffer *bufferOut,
330 VkDeviceSize *offsetOut,
331 bool *newBufferAllocatedOut)
332 {
333 size_t sizeToAllocate = roundUpPow2(sizeInBytes, mAlignment);
334
335 angle::base::CheckedNumeric<size_t> checkedNextWriteOffset = mNextAllocationOffset;
336 checkedNextWriteOffset += sizeToAllocate;
337
338 if (!checkedNextWriteOffset.IsValid() || checkedNextWriteOffset.ValueOrDie() >= mSize)
339 {
340 if (mBuffer)
341 {
342 ANGLE_TRY(flush(contextVk));
343 mBuffer->unmap(contextVk->getDevice());
344 mBuffer->updateQueueSerial(contextVk->getCurrentQueueSerial());
345
346 mInFlightBuffers.push_back(mBuffer);
347 mBuffer = nullptr;
348 }
349
350 if (sizeToAllocate > mSize)
351 {
352 mSize = std::max(mInitialSize, sizeToAllocate);
353
354 // Clear the free list since the free buffers are now too small.
355 for (BufferHelper *toFree : mBufferFreeList)
356 {
357 toFree->release(contextVk);
358 }
359 mBufferFreeList.clear();
360 }
361
362 // The front of the free list should be the oldest. Thus if it is in use the rest of the
363 // free list should be in use as well.
364 if (mBufferFreeList.empty() || mBufferFreeList.front()->isResourceInUse(contextVk))
365 {
366 ANGLE_TRY(allocateNewBuffer(contextVk));
367 }
368 else
369 {
370 mBuffer = mBufferFreeList.front();
371 mBufferFreeList.erase(mBufferFreeList.begin());
372 }
373
374 ASSERT(mBuffer->getSize() == mSize);
375
376 mNextAllocationOffset = 0;
377 mLastFlushOrInvalidateOffset = 0;
378
379 if (newBufferAllocatedOut != nullptr)
380 {
381 *newBufferAllocatedOut = true;
382 }
383 }
384 else if (newBufferAllocatedOut != nullptr)
385 {
386 *newBufferAllocatedOut = false;
387 }
388
389 ASSERT(mBuffer != nullptr);
390
391 if (bufferOut != nullptr)
392 {
393 *bufferOut = mBuffer->getBuffer().getHandle();
394 }
395
396 // Optionally map() the buffer if possible
397 if (ptrOut)
398 {
399 ASSERT(mHostVisible);
400 uint8_t *mappedMemory;
401 ANGLE_TRY(mBuffer->map(contextVk, &mappedMemory));
402 *ptrOut = mappedMemory + mNextAllocationOffset;
403 }
404
405 *offsetOut = static_cast<VkDeviceSize>(mNextAllocationOffset);
406 mNextAllocationOffset += static_cast<uint32_t>(sizeToAllocate);
407 return angle::Result::Continue;
408 }
409
flush(ContextVk * contextVk)410 angle::Result DynamicBuffer::flush(ContextVk *contextVk)
411 {
412 if (mHostVisible && (mNextAllocationOffset > mLastFlushOrInvalidateOffset))
413 {
414 ASSERT(mBuffer != nullptr);
415 ANGLE_TRY(mBuffer->flush(contextVk, mLastFlushOrInvalidateOffset,
416 mNextAllocationOffset - mLastFlushOrInvalidateOffset));
417 mLastFlushOrInvalidateOffset = mNextAllocationOffset;
418 }
419 return angle::Result::Continue;
420 }
421
invalidate(ContextVk * contextVk)422 angle::Result DynamicBuffer::invalidate(ContextVk *contextVk)
423 {
424 if (mHostVisible && (mNextAllocationOffset > mLastFlushOrInvalidateOffset))
425 {
426 ASSERT(mBuffer != nullptr);
427 ANGLE_TRY(mBuffer->invalidate(contextVk, mLastFlushOrInvalidateOffset,
428 mNextAllocationOffset - mLastFlushOrInvalidateOffset));
429 mLastFlushOrInvalidateOffset = mNextAllocationOffset;
430 }
431 return angle::Result::Continue;
432 }
433
releaseBufferListToContext(ContextVk * contextVk,std::vector<BufferHelper * > * buffers)434 void DynamicBuffer::releaseBufferListToContext(ContextVk *contextVk,
435 std::vector<BufferHelper *> *buffers)
436 {
437 for (BufferHelper *toFree : *buffers)
438 {
439 toFree->release(contextVk);
440 delete toFree;
441 }
442
443 buffers->clear();
444 }
445
releaseBufferListToDisplay(DisplayVk * display,std::vector<GarbageObjectBase> * garbageQueue,std::vector<BufferHelper * > * buffers)446 void DynamicBuffer::releaseBufferListToDisplay(DisplayVk *display,
447 std::vector<GarbageObjectBase> *garbageQueue,
448 std::vector<BufferHelper *> *buffers)
449 {
450 for (BufferHelper *toFree : *buffers)
451 {
452 toFree->release(display, garbageQueue);
453 delete toFree;
454 }
455
456 buffers->clear();
457 }
458
destroyBufferList(VkDevice device,std::vector<BufferHelper * > * buffers)459 void DynamicBuffer::destroyBufferList(VkDevice device, std::vector<BufferHelper *> *buffers)
460 {
461 for (BufferHelper *toFree : *buffers)
462 {
463 toFree->destroy(device);
464 delete toFree;
465 }
466
467 buffers->clear();
468 }
469
release(ContextVk * contextVk)470 void DynamicBuffer::release(ContextVk *contextVk)
471 {
472 reset();
473
474 releaseBufferListToContext(contextVk, &mInFlightBuffers);
475 releaseBufferListToContext(contextVk, &mBufferFreeList);
476
477 if (mBuffer)
478 {
479 mBuffer->unmap(contextVk->getDevice());
480
481 // The buffers may not have been recording commands, but they could be used to store data so
482 // they should live until at most this frame. For example a vertex buffer filled entirely
483 // by the CPU currently never gets a chance to have its serial set.
484 mBuffer->updateQueueSerial(contextVk->getCurrentQueueSerial());
485 mBuffer->release(contextVk);
486 delete mBuffer;
487 mBuffer = nullptr;
488 }
489 }
490
release(DisplayVk * display,std::vector<GarbageObjectBase> * garbageQueue)491 void DynamicBuffer::release(DisplayVk *display, std::vector<GarbageObjectBase> *garbageQueue)
492 {
493 reset();
494
495 releaseBufferListToDisplay(display, garbageQueue, &mInFlightBuffers);
496 releaseBufferListToDisplay(display, garbageQueue, &mBufferFreeList);
497
498 if (mBuffer)
499 {
500 mBuffer->unmap(display->getDevice());
501
502 mBuffer->release(display, garbageQueue);
503 delete mBuffer;
504 mBuffer = nullptr;
505 }
506 }
507
releaseInFlightBuffers(ContextVk * contextVk)508 void DynamicBuffer::releaseInFlightBuffers(ContextVk *contextVk)
509 {
510 for (BufferHelper *toRelease : mInFlightBuffers)
511 {
512 // If the dynamic buffer was resized we cannot reuse the retained buffer.
513 if (toRelease->getSize() < mSize)
514 {
515 toRelease->release(contextVk);
516 }
517 else
518 {
519 mBufferFreeList.push_back(toRelease);
520 }
521 }
522
523 mInFlightBuffers.clear();
524 }
525
destroy(VkDevice device)526 void DynamicBuffer::destroy(VkDevice device)
527 {
528 reset();
529
530 destroyBufferList(device, &mInFlightBuffers);
531 destroyBufferList(device, &mBufferFreeList);
532
533 if (mBuffer)
534 {
535 mBuffer->unmap(device);
536 mBuffer->destroy(device);
537 delete mBuffer;
538 mBuffer = nullptr;
539 }
540 }
541
updateAlignment(RendererVk * renderer,size_t alignment)542 void DynamicBuffer::updateAlignment(RendererVk *renderer, size_t alignment)
543 {
544 ASSERT(alignment > 0);
545
546 size_t atomSize =
547 static_cast<size_t>(renderer->getPhysicalDeviceProperties().limits.nonCoherentAtomSize);
548
549 // We need lcm(alignment, atomSize), we are assuming one divides the other so std::max() could
550 // be used instead.
551 ASSERT(alignment % atomSize == 0 || atomSize % alignment == 0);
552 alignment = std::max(alignment, atomSize);
553 ASSERT(gl::isPow2(alignment));
554
555 // If alignment has changed, make sure the next allocation is done at an aligned offset.
556 if (alignment != mAlignment)
557 {
558 mNextAllocationOffset =
559 roundUpPow2(mNextAllocationOffset, static_cast<uint32_t>(alignment));
560 }
561
562 mAlignment = alignment;
563 }
564
setMinimumSizeForTesting(size_t minSize)565 void DynamicBuffer::setMinimumSizeForTesting(size_t minSize)
566 {
567 // This will really only have an effect next time we call allocate.
568 mInitialSize = minSize;
569
570 // Forces a new allocation on the next allocate.
571 mSize = 0;
572 }
573
reset()574 void DynamicBuffer::reset()
575 {
576 mSize = 0;
577 mNextAllocationOffset = 0;
578 mLastFlushOrInvalidateOffset = 0;
579 }
580
581 // DescriptorPoolHelper implementation.
DescriptorPoolHelper()582 DescriptorPoolHelper::DescriptorPoolHelper() : mFreeDescriptorSets(0) {}
583
584 DescriptorPoolHelper::~DescriptorPoolHelper() = default;
585
hasCapacity(uint32_t descriptorSetCount) const586 bool DescriptorPoolHelper::hasCapacity(uint32_t descriptorSetCount) const
587 {
588 return mFreeDescriptorSets >= descriptorSetCount;
589 }
590
init(Context * context,const std::vector<VkDescriptorPoolSize> & poolSizes,uint32_t maxSets)591 angle::Result DescriptorPoolHelper::init(Context *context,
592 const std::vector<VkDescriptorPoolSize> &poolSizes,
593 uint32_t maxSets)
594 {
595 if (mDescriptorPool.valid())
596 {
597 // This could be improved by recycling the descriptor pool.
598 mDescriptorPool.destroy(context->getDevice());
599 }
600
601 VkDescriptorPoolCreateInfo descriptorPoolInfo = {};
602 descriptorPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
603 descriptorPoolInfo.flags = 0;
604 descriptorPoolInfo.maxSets = maxSets;
605 descriptorPoolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
606 descriptorPoolInfo.pPoolSizes = poolSizes.data();
607
608 mFreeDescriptorSets = maxSets;
609
610 ANGLE_VK_TRY(context, mDescriptorPool.init(context->getDevice(), descriptorPoolInfo));
611 return angle::Result::Continue;
612 }
613
destroy(VkDevice device)614 void DescriptorPoolHelper::destroy(VkDevice device)
615 {
616 mDescriptorPool.destroy(device);
617 }
618
release(ContextVk * contextVk)619 void DescriptorPoolHelper::release(ContextVk *contextVk)
620 {
621 contextVk->releaseObject(contextVk->getCurrentQueueSerial(), &mDescriptorPool);
622 }
623
allocateSets(ContextVk * contextVk,const VkDescriptorSetLayout * descriptorSetLayout,uint32_t descriptorSetCount,VkDescriptorSet * descriptorSetsOut)624 angle::Result DescriptorPoolHelper::allocateSets(ContextVk *contextVk,
625 const VkDescriptorSetLayout *descriptorSetLayout,
626 uint32_t descriptorSetCount,
627 VkDescriptorSet *descriptorSetsOut)
628 {
629 VkDescriptorSetAllocateInfo allocInfo = {};
630 allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
631 allocInfo.descriptorPool = mDescriptorPool.getHandle();
632 allocInfo.descriptorSetCount = descriptorSetCount;
633 allocInfo.pSetLayouts = descriptorSetLayout;
634
635 ASSERT(mFreeDescriptorSets >= descriptorSetCount);
636 mFreeDescriptorSets -= descriptorSetCount;
637
638 ANGLE_VK_TRY(contextVk, mDescriptorPool.allocateDescriptorSets(contextVk->getDevice(),
639 allocInfo, descriptorSetsOut));
640 return angle::Result::Continue;
641 }
642
643 // DynamicDescriptorPool implementation.
DynamicDescriptorPool()644 DynamicDescriptorPool::DynamicDescriptorPool()
645 : mMaxSetsPerPool(kDefaultDescriptorPoolMaxSets), mCurrentPoolIndex(0)
646 {}
647
648 DynamicDescriptorPool::~DynamicDescriptorPool() = default;
649
init(ContextVk * contextVk,const VkDescriptorPoolSize * setSizes,uint32_t setSizeCount)650 angle::Result DynamicDescriptorPool::init(ContextVk *contextVk,
651 const VkDescriptorPoolSize *setSizes,
652 uint32_t setSizeCount)
653 {
654 ASSERT(mCurrentPoolIndex == 0);
655 ASSERT(mDescriptorPools.empty() || (mDescriptorPools.size() == 1 &&
656 mDescriptorPools[0]->get().hasCapacity(mMaxSetsPerPool)));
657
658 mPoolSizes.assign(setSizes, setSizes + setSizeCount);
659 for (uint32_t i = 0; i < setSizeCount; ++i)
660 {
661 mPoolSizes[i].descriptorCount *= mMaxSetsPerPool;
662 }
663
664 mDescriptorPools.push_back(new RefCountedDescriptorPoolHelper());
665 return mDescriptorPools[0]->get().init(contextVk, mPoolSizes, mMaxSetsPerPool);
666 }
667
destroy(VkDevice device)668 void DynamicDescriptorPool::destroy(VkDevice device)
669 {
670 for (RefCountedDescriptorPoolHelper *pool : mDescriptorPools)
671 {
672 ASSERT(!pool->isReferenced());
673 pool->get().destroy(device);
674 delete pool;
675 }
676
677 mDescriptorPools.clear();
678 }
679
release(ContextVk * contextVk)680 void DynamicDescriptorPool::release(ContextVk *contextVk)
681 {
682 for (RefCountedDescriptorPoolHelper *pool : mDescriptorPools)
683 {
684 ASSERT(!pool->isReferenced());
685 pool->get().release(contextVk);
686 delete pool;
687 }
688
689 mDescriptorPools.clear();
690 }
691
allocateSetsAndGetInfo(ContextVk * contextVk,const VkDescriptorSetLayout * descriptorSetLayout,uint32_t descriptorSetCount,RefCountedDescriptorPoolBinding * bindingOut,VkDescriptorSet * descriptorSetsOut,bool * newPoolAllocatedOut)692 angle::Result DynamicDescriptorPool::allocateSetsAndGetInfo(
693 ContextVk *contextVk,
694 const VkDescriptorSetLayout *descriptorSetLayout,
695 uint32_t descriptorSetCount,
696 RefCountedDescriptorPoolBinding *bindingOut,
697 VkDescriptorSet *descriptorSetsOut,
698 bool *newPoolAllocatedOut)
699 {
700 *newPoolAllocatedOut = false;
701
702 if (!bindingOut->valid() || !bindingOut->get().hasCapacity(descriptorSetCount))
703 {
704 if (!mDescriptorPools[mCurrentPoolIndex]->get().hasCapacity(descriptorSetCount))
705 {
706 ANGLE_TRY(allocateNewPool(contextVk));
707 *newPoolAllocatedOut = true;
708 }
709
710 // Make sure the old binding knows the descriptor sets can still be in-use. We only need
711 // to update the serial when we move to a new pool. This is because we only check serials
712 // when we move to a new pool.
713 if (bindingOut->valid())
714 {
715 Serial currentSerial = contextVk->getCurrentQueueSerial();
716 bindingOut->get().updateSerial(currentSerial);
717 }
718
719 bindingOut->set(mDescriptorPools[mCurrentPoolIndex]);
720 }
721
722 return bindingOut->get().allocateSets(contextVk, descriptorSetLayout, descriptorSetCount,
723 descriptorSetsOut);
724 }
725
allocateNewPool(ContextVk * contextVk)726 angle::Result DynamicDescriptorPool::allocateNewPool(ContextVk *contextVk)
727 {
728 bool found = false;
729
730 for (size_t poolIndex = 0; poolIndex < mDescriptorPools.size(); ++poolIndex)
731 {
732 if (!mDescriptorPools[poolIndex]->isReferenced() &&
733 !contextVk->isSerialInUse(mDescriptorPools[poolIndex]->get().getSerial()))
734 {
735 mCurrentPoolIndex = poolIndex;
736 found = true;
737 break;
738 }
739 }
740
741 if (!found)
742 {
743 mDescriptorPools.push_back(new RefCountedDescriptorPoolHelper());
744 mCurrentPoolIndex = mDescriptorPools.size() - 1;
745
746 static constexpr size_t kMaxPools = 99999;
747 ANGLE_VK_CHECK(contextVk, mDescriptorPools.size() < kMaxPools, VK_ERROR_TOO_MANY_OBJECTS);
748 }
749
750 return mDescriptorPools[mCurrentPoolIndex]->get().init(contextVk, mPoolSizes, mMaxSetsPerPool);
751 }
752
setMaxSetsPerPoolForTesting(uint32_t maxSetsPerPool)753 void DynamicDescriptorPool::setMaxSetsPerPoolForTesting(uint32_t maxSetsPerPool)
754 {
755 mMaxSetsPerPool = maxSetsPerPool;
756 }
757
758 // DynamicallyGrowingPool implementation
759 template <typename Pool>
DynamicallyGrowingPool()760 DynamicallyGrowingPool<Pool>::DynamicallyGrowingPool()
761 : mPoolSize(0), mCurrentPool(0), mCurrentFreeEntry(0)
762 {}
763
764 template <typename Pool>
765 DynamicallyGrowingPool<Pool>::~DynamicallyGrowingPool() = default;
766
767 template <typename Pool>
initEntryPool(Context * contextVk,uint32_t poolSize)768 angle::Result DynamicallyGrowingPool<Pool>::initEntryPool(Context *contextVk, uint32_t poolSize)
769 {
770 ASSERT(mPools.empty() && mPoolStats.empty());
771 mPoolSize = poolSize;
772 return angle::Result::Continue;
773 }
774
775 template <typename Pool>
destroyEntryPool()776 void DynamicallyGrowingPool<Pool>::destroyEntryPool()
777 {
778 mPools.clear();
779 mPoolStats.clear();
780 }
781
782 template <typename Pool>
findFreeEntryPool(ContextVk * contextVk)783 bool DynamicallyGrowingPool<Pool>::findFreeEntryPool(ContextVk *contextVk)
784 {
785 Serial lastCompletedQueueSerial = contextVk->getLastCompletedQueueSerial();
786 for (size_t i = 0; i < mPools.size(); ++i)
787 {
788 if (mPoolStats[i].freedCount == mPoolSize &&
789 mPoolStats[i].serial <= lastCompletedQueueSerial)
790 {
791 mCurrentPool = i;
792 mCurrentFreeEntry = 0;
793
794 mPoolStats[i].freedCount = 0;
795
796 return true;
797 }
798 }
799
800 return false;
801 }
802
803 template <typename Pool>
allocateNewEntryPool(ContextVk * contextVk,Pool && pool)804 angle::Result DynamicallyGrowingPool<Pool>::allocateNewEntryPool(ContextVk *contextVk, Pool &&pool)
805 {
806 mPools.push_back(std::move(pool));
807
808 PoolStats poolStats = {0, Serial()};
809 mPoolStats.push_back(poolStats);
810
811 mCurrentPool = mPools.size() - 1;
812 mCurrentFreeEntry = 0;
813
814 return angle::Result::Continue;
815 }
816
817 template <typename Pool>
onEntryFreed(ContextVk * contextVk,size_t poolIndex)818 void DynamicallyGrowingPool<Pool>::onEntryFreed(ContextVk *contextVk, size_t poolIndex)
819 {
820 ASSERT(poolIndex < mPoolStats.size() && mPoolStats[poolIndex].freedCount < mPoolSize);
821
822 // Take note of the current serial to avoid reallocating a query in the same pool
823 mPoolStats[poolIndex].serial = contextVk->getCurrentQueueSerial();
824 ++mPoolStats[poolIndex].freedCount;
825 }
826
827 // DynamicQueryPool implementation
828 DynamicQueryPool::DynamicQueryPool() = default;
829
830 DynamicQueryPool::~DynamicQueryPool() = default;
831
init(ContextVk * contextVk,VkQueryType type,uint32_t poolSize)832 angle::Result DynamicQueryPool::init(ContextVk *contextVk, VkQueryType type, uint32_t poolSize)
833 {
834 ANGLE_TRY(initEntryPool(contextVk, poolSize));
835
836 mQueryType = type;
837 ANGLE_TRY(allocateNewPool(contextVk));
838
839 return angle::Result::Continue;
840 }
841
destroy(VkDevice device)842 void DynamicQueryPool::destroy(VkDevice device)
843 {
844 for (QueryPool &queryPool : mPools)
845 {
846 queryPool.destroy(device);
847 }
848
849 destroyEntryPool();
850 }
851
allocateQuery(ContextVk * contextVk,QueryHelper * queryOut)852 angle::Result DynamicQueryPool::allocateQuery(ContextVk *contextVk, QueryHelper *queryOut)
853 {
854 ASSERT(!queryOut->getQueryPool());
855
856 size_t poolIndex = 0;
857 uint32_t queryIndex = 0;
858 ANGLE_TRY(allocateQuery(contextVk, &poolIndex, &queryIndex));
859
860 queryOut->init(this, poolIndex, queryIndex);
861
862 return angle::Result::Continue;
863 }
864
freeQuery(ContextVk * contextVk,QueryHelper * query)865 void DynamicQueryPool::freeQuery(ContextVk *contextVk, QueryHelper *query)
866 {
867 if (query->getQueryPool())
868 {
869 size_t poolIndex = query->getQueryPoolIndex();
870 ASSERT(query->getQueryPool()->valid());
871
872 freeQuery(contextVk, poolIndex, query->getQuery());
873
874 query->deinit();
875 }
876 }
877
allocateQuery(ContextVk * contextVk,size_t * poolIndex,uint32_t * queryIndex)878 angle::Result DynamicQueryPool::allocateQuery(ContextVk *contextVk,
879 size_t *poolIndex,
880 uint32_t *queryIndex)
881 {
882 if (mCurrentFreeEntry >= mPoolSize)
883 {
884 // No more queries left in this pool, create another one.
885 ANGLE_TRY(allocateNewPool(contextVk));
886 }
887
888 *poolIndex = mCurrentPool;
889 *queryIndex = mCurrentFreeEntry++;
890
891 return angle::Result::Continue;
892 }
893
freeQuery(ContextVk * contextVk,size_t poolIndex,uint32_t queryIndex)894 void DynamicQueryPool::freeQuery(ContextVk *contextVk, size_t poolIndex, uint32_t queryIndex)
895 {
896 ANGLE_UNUSED_VARIABLE(queryIndex);
897 onEntryFreed(contextVk, poolIndex);
898 }
899
allocateNewPool(ContextVk * contextVk)900 angle::Result DynamicQueryPool::allocateNewPool(ContextVk *contextVk)
901 {
902 if (findFreeEntryPool(contextVk))
903 {
904 return angle::Result::Continue;
905 }
906
907 VkQueryPoolCreateInfo queryPoolInfo = {};
908 queryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
909 queryPoolInfo.flags = 0;
910 queryPoolInfo.queryType = mQueryType;
911 queryPoolInfo.queryCount = mPoolSize;
912 queryPoolInfo.pipelineStatistics = 0;
913
914 vk::QueryPool queryPool;
915
916 ANGLE_VK_TRY(contextVk, queryPool.init(contextVk->getDevice(), queryPoolInfo));
917
918 return allocateNewEntryPool(contextVk, std::move(queryPool));
919 }
920
921 // QueryHelper implementation
QueryHelper()922 QueryHelper::QueryHelper() : mDynamicQueryPool(nullptr), mQueryPoolIndex(0), mQuery(0) {}
923
~QueryHelper()924 QueryHelper::~QueryHelper() {}
925
init(const DynamicQueryPool * dynamicQueryPool,const size_t queryPoolIndex,uint32_t query)926 void QueryHelper::init(const DynamicQueryPool *dynamicQueryPool,
927 const size_t queryPoolIndex,
928 uint32_t query)
929 {
930 mDynamicQueryPool = dynamicQueryPool;
931 mQueryPoolIndex = queryPoolIndex;
932 mQuery = query;
933 }
934
deinit()935 void QueryHelper::deinit()
936 {
937 mDynamicQueryPool = nullptr;
938 mQueryPoolIndex = 0;
939 mQuery = 0;
940 }
941
beginQuery(ContextVk * contextVk)942 void QueryHelper::beginQuery(ContextVk *contextVk)
943 {
944 contextVk->getCommandGraph()->beginQuery(getQueryPool(), getQuery());
945 mMostRecentSerial = contextVk->getCurrentQueueSerial();
946 }
947
endQuery(ContextVk * contextVk)948 void QueryHelper::endQuery(ContextVk *contextVk)
949 {
950 contextVk->getCommandGraph()->endQuery(getQueryPool(), getQuery());
951 mMostRecentSerial = contextVk->getCurrentQueueSerial();
952 }
953
writeTimestamp(ContextVk * contextVk)954 void QueryHelper::writeTimestamp(ContextVk *contextVk)
955 {
956 contextVk->getCommandGraph()->writeTimestamp(getQueryPool(), getQuery());
957 mMostRecentSerial = contextVk->getCurrentQueueSerial();
958 }
959
hasPendingWork(ContextVk * contextVk)960 bool QueryHelper::hasPendingWork(ContextVk *contextVk)
961 {
962 // If the renderer has a queue serial higher than the stored one, the command buffers that
963 // recorded this query have already been submitted, so there is no pending work.
964 return mMostRecentSerial == contextVk->getCurrentQueueSerial();
965 }
966
967 // DynamicSemaphorePool implementation
968 DynamicSemaphorePool::DynamicSemaphorePool() = default;
969
970 DynamicSemaphorePool::~DynamicSemaphorePool() = default;
971
init(ContextVk * contextVk,uint32_t poolSize)972 angle::Result DynamicSemaphorePool::init(ContextVk *contextVk, uint32_t poolSize)
973 {
974 ANGLE_TRY(initEntryPool(contextVk, poolSize));
975 ANGLE_TRY(allocateNewPool(contextVk));
976 return angle::Result::Continue;
977 }
978
destroy(VkDevice device)979 void DynamicSemaphorePool::destroy(VkDevice device)
980 {
981 for (auto &semaphorePool : mPools)
982 {
983 for (Semaphore &semaphore : semaphorePool)
984 {
985 semaphore.destroy(device);
986 }
987 }
988
989 destroyEntryPool();
990 }
991
allocateSemaphore(ContextVk * contextVk,SemaphoreHelper * semaphoreOut)992 angle::Result DynamicSemaphorePool::allocateSemaphore(ContextVk *contextVk,
993 SemaphoreHelper *semaphoreOut)
994 {
995 ASSERT(!semaphoreOut->getSemaphore());
996
997 if (mCurrentFreeEntry >= mPoolSize)
998 {
999 // No more queries left in this pool, create another one.
1000 ANGLE_TRY(allocateNewPool(contextVk));
1001 }
1002
1003 semaphoreOut->init(mCurrentPool, &mPools[mCurrentPool][mCurrentFreeEntry++]);
1004
1005 return angle::Result::Continue;
1006 }
1007
freeSemaphore(ContextVk * contextVk,SemaphoreHelper * semaphore)1008 void DynamicSemaphorePool::freeSemaphore(ContextVk *contextVk, SemaphoreHelper *semaphore)
1009 {
1010 if (semaphore->getSemaphore())
1011 {
1012 onEntryFreed(contextVk, semaphore->getSemaphorePoolIndex());
1013 semaphore->deinit();
1014 }
1015 }
1016
allocateNewPool(ContextVk * contextVk)1017 angle::Result DynamicSemaphorePool::allocateNewPool(ContextVk *contextVk)
1018 {
1019 if (findFreeEntryPool(contextVk))
1020 {
1021 return angle::Result::Continue;
1022 }
1023
1024 std::vector<Semaphore> newPool(mPoolSize);
1025
1026 for (Semaphore &semaphore : newPool)
1027 {
1028 ANGLE_VK_TRY(contextVk, semaphore.init(contextVk->getDevice()));
1029 }
1030
1031 // This code is safe as long as the growth of the outer vector in vector<vector<T>> is done by
1032 // moving the inner vectors, making sure references to the inner vector remain intact.
1033 Semaphore *assertMove = mPools.size() > 0 ? mPools[0].data() : nullptr;
1034
1035 ANGLE_TRY(allocateNewEntryPool(contextVk, std::move(newPool)));
1036
1037 ASSERT(assertMove == nullptr || assertMove == mPools[0].data());
1038
1039 return angle::Result::Continue;
1040 }
1041
1042 // SemaphoreHelper implementation
SemaphoreHelper()1043 SemaphoreHelper::SemaphoreHelper() : mSemaphorePoolIndex(0), mSemaphore(0) {}
1044
~SemaphoreHelper()1045 SemaphoreHelper::~SemaphoreHelper() {}
1046
SemaphoreHelper(SemaphoreHelper && other)1047 SemaphoreHelper::SemaphoreHelper(SemaphoreHelper &&other)
1048 : mSemaphorePoolIndex(other.mSemaphorePoolIndex), mSemaphore(other.mSemaphore)
1049 {
1050 other.mSemaphore = nullptr;
1051 }
1052
operator =(SemaphoreHelper && other)1053 SemaphoreHelper &SemaphoreHelper::operator=(SemaphoreHelper &&other)
1054 {
1055 std::swap(mSemaphorePoolIndex, other.mSemaphorePoolIndex);
1056 std::swap(mSemaphore, other.mSemaphore);
1057 return *this;
1058 }
1059
init(const size_t semaphorePoolIndex,const vk::Semaphore * semaphore)1060 void SemaphoreHelper::init(const size_t semaphorePoolIndex, const vk::Semaphore *semaphore)
1061 {
1062 mSemaphorePoolIndex = semaphorePoolIndex;
1063 mSemaphore = semaphore;
1064 }
1065
deinit()1066 void SemaphoreHelper::deinit()
1067 {
1068 mSemaphorePoolIndex = 0;
1069 mSemaphore = nullptr;
1070 }
1071
1072 // LineLoopHelper implementation.
LineLoopHelper(RendererVk * renderer)1073 LineLoopHelper::LineLoopHelper(RendererVk *renderer)
1074 {
1075 // We need to use an alignment of the maximum size we're going to allocate, which is
1076 // VK_INDEX_TYPE_UINT32. When we switch from a drawElement to a drawArray call, the allocations
1077 // can vary in size. According to the Vulkan spec, when calling vkCmdBindIndexBuffer: 'The
1078 // sum of offset and the address of the range of VkDeviceMemory object that is backing buffer,
1079 // must be a multiple of the type indicated by indexType'.
1080 mDynamicIndexBuffer.init(renderer, kLineLoopDynamicBufferUsage, sizeof(uint32_t),
1081 kLineLoopDynamicBufferInitialSize, true);
1082 }
1083
1084 LineLoopHelper::~LineLoopHelper() = default;
1085
getIndexBufferForDrawArrays(ContextVk * contextVk,uint32_t clampedVertexCount,GLint firstVertex,vk::BufferHelper ** bufferOut,VkDeviceSize * offsetOut)1086 angle::Result LineLoopHelper::getIndexBufferForDrawArrays(ContextVk *contextVk,
1087 uint32_t clampedVertexCount,
1088 GLint firstVertex,
1089 vk::BufferHelper **bufferOut,
1090 VkDeviceSize *offsetOut)
1091 {
1092 uint32_t *indices = nullptr;
1093 size_t allocateBytes = sizeof(uint32_t) * (static_cast<size_t>(clampedVertexCount) + 1);
1094
1095 mDynamicIndexBuffer.releaseInFlightBuffers(contextVk);
1096 ANGLE_TRY(mDynamicIndexBuffer.allocate(contextVk, allocateBytes,
1097 reinterpret_cast<uint8_t **>(&indices), nullptr,
1098 offsetOut, nullptr));
1099 *bufferOut = mDynamicIndexBuffer.getCurrentBuffer();
1100
1101 // Note: there could be an overflow in this addition.
1102 uint32_t unsignedFirstVertex = static_cast<uint32_t>(firstVertex);
1103 uint32_t vertexCount = (clampedVertexCount + unsignedFirstVertex);
1104 for (uint32_t vertexIndex = unsignedFirstVertex; vertexIndex < vertexCount; vertexIndex++)
1105 {
1106 *indices++ = vertexIndex;
1107 }
1108 *indices = unsignedFirstVertex;
1109
1110 // Since we are not using the VK_MEMORY_PROPERTY_HOST_COHERENT_BIT flag when creating the
1111 // device memory in the StreamingBuffer, we always need to make sure we flush it after
1112 // writing.
1113 ANGLE_TRY(mDynamicIndexBuffer.flush(contextVk));
1114
1115 return angle::Result::Continue;
1116 }
1117
getIndexBufferForElementArrayBuffer(ContextVk * contextVk,BufferVk * elementArrayBufferVk,gl::DrawElementsType glIndexType,int indexCount,intptr_t elementArrayOffset,vk::BufferHelper ** bufferOut,VkDeviceSize * bufferOffsetOut,uint32_t * indexCountOut)1118 angle::Result LineLoopHelper::getIndexBufferForElementArrayBuffer(ContextVk *contextVk,
1119 BufferVk *elementArrayBufferVk,
1120 gl::DrawElementsType glIndexType,
1121 int indexCount,
1122 intptr_t elementArrayOffset,
1123 vk::BufferHelper **bufferOut,
1124 VkDeviceSize *bufferOffsetOut,
1125 uint32_t *indexCountOut)
1126 {
1127 if (glIndexType == gl::DrawElementsType::UnsignedByte ||
1128 contextVk->getState().isPrimitiveRestartEnabled())
1129 {
1130 ANGLE_TRACE_EVENT0("gpu.angle", "LineLoopHelper::getIndexBufferForElementArrayBuffer");
1131
1132 void *srcDataMapping = nullptr;
1133 ANGLE_TRY(elementArrayBufferVk->mapImpl(contextVk, &srcDataMapping));
1134 ANGLE_TRY(streamIndices(contextVk, glIndexType, indexCount,
1135 static_cast<const uint8_t *>(srcDataMapping) + elementArrayOffset,
1136 bufferOut, bufferOffsetOut, indexCountOut));
1137 elementArrayBufferVk->unmapImpl(contextVk);
1138 return angle::Result::Continue;
1139 }
1140
1141 *indexCountOut = indexCount + 1;
1142
1143 VkIndexType indexType = gl_vk::kIndexTypeMap[glIndexType];
1144 ASSERT(indexType == VK_INDEX_TYPE_UINT16 || indexType == VK_INDEX_TYPE_UINT32);
1145 uint32_t *indices = nullptr;
1146
1147 auto unitSize = (indexType == VK_INDEX_TYPE_UINT16 ? sizeof(uint16_t) : sizeof(uint32_t));
1148 size_t allocateBytes = unitSize * (indexCount + 1) + 1;
1149
1150 mDynamicIndexBuffer.releaseInFlightBuffers(contextVk);
1151 ANGLE_TRY(mDynamicIndexBuffer.allocate(contextVk, allocateBytes,
1152 reinterpret_cast<uint8_t **>(&indices), nullptr,
1153 bufferOffsetOut, nullptr));
1154 *bufferOut = mDynamicIndexBuffer.getCurrentBuffer();
1155
1156 VkDeviceSize sourceOffset = static_cast<VkDeviceSize>(elementArrayOffset);
1157 uint64_t unitCount = static_cast<VkDeviceSize>(indexCount);
1158 angle::FixedVector<VkBufferCopy, 3> copies = {
1159 {sourceOffset, *bufferOffsetOut, unitCount * unitSize},
1160 {sourceOffset, *bufferOffsetOut + unitCount * unitSize, unitSize},
1161 };
1162 if (contextVk->getRenderer()->getFeatures().extraCopyBufferRegion.enabled)
1163 copies.push_back({sourceOffset, *bufferOffsetOut + (unitCount + 1) * unitSize, 1});
1164
1165 ANGLE_TRY(elementArrayBufferVk->copyToBuffer(
1166 contextVk, *bufferOut, static_cast<uint32_t>(copies.size()), copies.data()));
1167 ANGLE_TRY(mDynamicIndexBuffer.flush(contextVk));
1168 return angle::Result::Continue;
1169 }
1170
streamIndices(ContextVk * contextVk,gl::DrawElementsType glIndexType,GLsizei indexCount,const uint8_t * srcPtr,vk::BufferHelper ** bufferOut,VkDeviceSize * bufferOffsetOut,uint32_t * indexCountOut)1171 angle::Result LineLoopHelper::streamIndices(ContextVk *contextVk,
1172 gl::DrawElementsType glIndexType,
1173 GLsizei indexCount,
1174 const uint8_t *srcPtr,
1175 vk::BufferHelper **bufferOut,
1176 VkDeviceSize *bufferOffsetOut,
1177 uint32_t *indexCountOut)
1178 {
1179 VkIndexType indexType = gl_vk::kIndexTypeMap[glIndexType];
1180
1181 uint8_t *indices = nullptr;
1182
1183 auto unitSize = (indexType == VK_INDEX_TYPE_UINT16 ? sizeof(uint16_t) : sizeof(uint32_t));
1184 uint32_t numOutIndices = indexCount + 1;
1185 if (contextVk->getState().isPrimitiveRestartEnabled())
1186 {
1187 numOutIndices = GetLineLoopWithRestartIndexCount(glIndexType, indexCount, srcPtr);
1188 }
1189 *indexCountOut = numOutIndices;
1190 size_t allocateBytes = unitSize * numOutIndices;
1191 ANGLE_TRY(mDynamicIndexBuffer.allocate(contextVk, allocateBytes,
1192 reinterpret_cast<uint8_t **>(&indices), nullptr,
1193 bufferOffsetOut, nullptr));
1194 *bufferOut = mDynamicIndexBuffer.getCurrentBuffer();
1195
1196 if (contextVk->getState().isPrimitiveRestartEnabled())
1197 {
1198 HandlePrimitiveRestart(glIndexType, indexCount, srcPtr, indices);
1199 }
1200 else
1201 {
1202 if (glIndexType == gl::DrawElementsType::UnsignedByte)
1203 {
1204 // Vulkan doesn't support uint8 index types, so we need to emulate it.
1205 ASSERT(indexType == VK_INDEX_TYPE_UINT16);
1206 uint16_t *indicesDst = reinterpret_cast<uint16_t *>(indices);
1207 for (int i = 0; i < indexCount; i++)
1208 {
1209 indicesDst[i] = srcPtr[i];
1210 }
1211
1212 indicesDst[indexCount] = srcPtr[0];
1213 }
1214 else
1215 {
1216 memcpy(indices, srcPtr, unitSize * indexCount);
1217 memcpy(indices + unitSize * indexCount, srcPtr, unitSize);
1218 }
1219 }
1220
1221 ANGLE_TRY(mDynamicIndexBuffer.flush(contextVk));
1222 return angle::Result::Continue;
1223 }
1224
release(ContextVk * contextVk)1225 void LineLoopHelper::release(ContextVk *contextVk)
1226 {
1227 mDynamicIndexBuffer.release(contextVk);
1228 }
1229
destroy(VkDevice device)1230 void LineLoopHelper::destroy(VkDevice device)
1231 {
1232 mDynamicIndexBuffer.destroy(device);
1233 }
1234
1235 // static
Draw(uint32_t count,vk::CommandBuffer * commandBuffer)1236 void LineLoopHelper::Draw(uint32_t count, vk::CommandBuffer *commandBuffer)
1237 {
1238 // Our first index is always 0 because that's how we set it up in createIndexBuffer*.
1239 commandBuffer->drawIndexed(count);
1240 }
1241
1242 // BufferHelper implementation.
BufferHelper()1243 BufferHelper::BufferHelper()
1244 : CommandGraphResource(CommandGraphResourceType::Buffer),
1245 mMemoryPropertyFlags{},
1246 mSize(0),
1247 mMappedMemory(nullptr),
1248 mViewFormat(nullptr),
1249 mCurrentWriteAccess(0),
1250 mCurrentReadAccess(0)
1251 {}
1252
1253 BufferHelper::~BufferHelper() = default;
1254
init(ContextVk * contextVk,const VkBufferCreateInfo & createInfo,VkMemoryPropertyFlags memoryPropertyFlags)1255 angle::Result BufferHelper::init(ContextVk *contextVk,
1256 const VkBufferCreateInfo &createInfo,
1257 VkMemoryPropertyFlags memoryPropertyFlags)
1258 {
1259 mSize = createInfo.size;
1260 ANGLE_VK_TRY(contextVk, mBuffer.init(contextVk->getDevice(), createInfo));
1261 return vk::AllocateBufferMemory(contextVk, memoryPropertyFlags, &mMemoryPropertyFlags, nullptr,
1262 &mBuffer, &mDeviceMemory);
1263 }
1264
destroy(VkDevice device)1265 void BufferHelper::destroy(VkDevice device)
1266 {
1267 unmap(device);
1268 mSize = 0;
1269 mViewFormat = nullptr;
1270
1271 mBuffer.destroy(device);
1272 mBufferView.destroy(device);
1273 mDeviceMemory.destroy(device);
1274 }
1275
release(ContextVk * contextVk)1276 void BufferHelper::release(ContextVk *contextVk)
1277 {
1278 unmap(contextVk->getDevice());
1279 mSize = 0;
1280 mViewFormat = nullptr;
1281
1282 contextVk->releaseObject(getStoredQueueSerial(), &mBuffer);
1283 contextVk->releaseObject(getStoredQueueSerial(), &mBufferView);
1284 contextVk->releaseObject(getStoredQueueSerial(), &mDeviceMemory);
1285 }
1286
release(DisplayVk * display,std::vector<GarbageObjectBase> * garbageQueue)1287 void BufferHelper::release(DisplayVk *display, std::vector<GarbageObjectBase> *garbageQueue)
1288 {
1289 unmap(display->getDevice());
1290 mSize = 0;
1291 mViewFormat = nullptr;
1292
1293 mBuffer.dumpResources(garbageQueue);
1294 mBufferView.dumpResources(garbageQueue);
1295 mDeviceMemory.dumpResources(garbageQueue);
1296 }
1297
needsOnWriteBarrier(VkAccessFlags readAccessType,VkAccessFlags writeAccessType,VkAccessFlags * barrierSrcOut,VkAccessFlags * barrierDstOut)1298 bool BufferHelper::needsOnWriteBarrier(VkAccessFlags readAccessType,
1299 VkAccessFlags writeAccessType,
1300 VkAccessFlags *barrierSrcOut,
1301 VkAccessFlags *barrierDstOut)
1302 {
1303 bool needsBarrier = mCurrentReadAccess != 0 || mCurrentWriteAccess != 0;
1304
1305 // Note: mCurrentReadAccess is not part of barrier src flags as "anything-after-read" is
1306 // satisified by execution barriers alone.
1307 *barrierSrcOut = mCurrentWriteAccess;
1308 *barrierDstOut = readAccessType | writeAccessType;
1309
1310 mCurrentWriteAccess = writeAccessType;
1311 mCurrentReadAccess = readAccessType;
1312
1313 return needsBarrier;
1314 }
1315
onWriteAccess(ContextVk * contextVk,VkAccessFlags readAccessType,VkAccessFlags writeAccessType)1316 void BufferHelper::onWriteAccess(ContextVk *contextVk,
1317 VkAccessFlags readAccessType,
1318 VkAccessFlags writeAccessType)
1319 {
1320 VkAccessFlags barrierSrc, barrierDst;
1321 if (needsOnWriteBarrier(readAccessType, writeAccessType, &barrierSrc, &barrierDst))
1322 {
1323 addGlobalMemoryBarrier(barrierSrc, barrierDst, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
1324 }
1325
1326 bool hostVisible = mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
1327 if (hostVisible && writeAccessType != VK_ACCESS_HOST_WRITE_BIT)
1328 {
1329 contextVk->onHostVisibleBufferWrite();
1330 }
1331 }
1332
copyFromBuffer(ContextVk * contextVk,const Buffer & buffer,VkAccessFlags bufferAccessType,const VkBufferCopy & copyRegion)1333 angle::Result BufferHelper::copyFromBuffer(ContextVk *contextVk,
1334 const Buffer &buffer,
1335 VkAccessFlags bufferAccessType,
1336 const VkBufferCopy ©Region)
1337 {
1338 // 'recordCommands' will implicitly stop any reads from using the old buffer data.
1339 vk::CommandBuffer *commandBuffer = nullptr;
1340 ANGLE_TRY(recordCommands(contextVk, &commandBuffer));
1341
1342 if (mCurrentReadAccess != 0 || mCurrentWriteAccess != 0 || bufferAccessType != 0)
1343 {
1344 // Insert a barrier to ensure reads/writes are complete.
1345 // Use a global memory barrier to keep things simple.
1346 VkMemoryBarrier memoryBarrier = {};
1347 memoryBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
1348 memoryBarrier.srcAccessMask = mCurrentReadAccess | mCurrentWriteAccess | bufferAccessType;
1349 memoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1350
1351 commandBuffer->pipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
1352 VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 1, &memoryBarrier, 0,
1353 nullptr, 0, nullptr);
1354 }
1355
1356 mCurrentWriteAccess = VK_ACCESS_TRANSFER_WRITE_BIT;
1357 mCurrentReadAccess = 0;
1358
1359 commandBuffer->copyBuffer(buffer, mBuffer, 1, ©Region);
1360
1361 return angle::Result::Continue;
1362 }
1363
initBufferView(ContextVk * contextVk,const Format & format)1364 angle::Result BufferHelper::initBufferView(ContextVk *contextVk, const Format &format)
1365 {
1366 ASSERT(format.valid());
1367
1368 if (mBufferView.valid())
1369 {
1370 ASSERT(mViewFormat->vkBufferFormat == format.vkBufferFormat);
1371 return angle::Result::Continue;
1372 }
1373
1374 VkBufferViewCreateInfo viewCreateInfo = {};
1375 viewCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
1376 viewCreateInfo.buffer = mBuffer.getHandle();
1377 viewCreateInfo.format = format.vkBufferFormat;
1378 viewCreateInfo.offset = 0;
1379 viewCreateInfo.range = mSize;
1380
1381 ANGLE_VK_TRY(contextVk, mBufferView.init(contextVk->getDevice(), viewCreateInfo));
1382 mViewFormat = &format;
1383
1384 return angle::Result::Continue;
1385 }
1386
mapImpl(ContextVk * contextVk)1387 angle::Result BufferHelper::mapImpl(ContextVk *contextVk)
1388 {
1389 ANGLE_VK_TRY(contextVk, mDeviceMemory.map(contextVk->getDevice(), 0, mSize, 0, &mMappedMemory));
1390 return angle::Result::Continue;
1391 }
1392
unmap(VkDevice device)1393 void BufferHelper::unmap(VkDevice device)
1394 {
1395 if (mMappedMemory)
1396 {
1397 mDeviceMemory.unmap(device);
1398 mMappedMemory = nullptr;
1399 }
1400 }
1401
flush(ContextVk * contextVk,size_t offset,size_t size)1402 angle::Result BufferHelper::flush(ContextVk *contextVk, size_t offset, size_t size)
1403 {
1404 bool hostVisible = mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
1405 bool hostCoherent = mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
1406 if (hostVisible && !hostCoherent)
1407 {
1408 VkMappedMemoryRange range = {};
1409 range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
1410 range.memory = mDeviceMemory.getHandle();
1411 range.offset = offset;
1412 range.size = size;
1413 ANGLE_VK_TRY(contextVk, vkFlushMappedMemoryRanges(contextVk->getDevice(), 1, &range));
1414 }
1415 return angle::Result::Continue;
1416 }
1417
invalidate(ContextVk * contextVk,size_t offset,size_t size)1418 angle::Result BufferHelper::invalidate(ContextVk *contextVk, size_t offset, size_t size)
1419 {
1420 bool hostVisible = mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
1421 bool hostCoherent = mMemoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
1422 if (hostVisible && !hostCoherent)
1423 {
1424 VkMappedMemoryRange range = {};
1425 range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
1426 range.memory = mDeviceMemory.getHandle();
1427 range.offset = offset;
1428 range.size = size;
1429 ANGLE_VK_TRY(contextVk, vkInvalidateMappedMemoryRanges(contextVk->getDevice(), 1, &range));
1430 }
1431 return angle::Result::Continue;
1432 }
1433
1434 // ImageHelper implementation.
ImageHelper()1435 ImageHelper::ImageHelper()
1436 : CommandGraphResource(CommandGraphResourceType::Image),
1437 mFormat(nullptr),
1438 mSamples(0),
1439 mCurrentLayout(ImageLayout::Undefined),
1440 mCurrentQueueFamilyIndex(std::numeric_limits<uint32_t>::max()),
1441 mLayerCount(0),
1442 mLevelCount(0)
1443 {}
1444
ImageHelper(ImageHelper && other)1445 ImageHelper::ImageHelper(ImageHelper &&other)
1446 : CommandGraphResource(CommandGraphResourceType::Image),
1447 mImage(std::move(other.mImage)),
1448 mDeviceMemory(std::move(other.mDeviceMemory)),
1449 mExtents(other.mExtents),
1450 mFormat(other.mFormat),
1451 mSamples(other.mSamples),
1452 mCurrentLayout(other.mCurrentLayout),
1453 mCurrentQueueFamilyIndex(other.mCurrentQueueFamilyIndex),
1454 mLayerCount(other.mLayerCount),
1455 mLevelCount(other.mLevelCount),
1456 mStagingBuffer(std::move(other.mStagingBuffer)),
1457 mSubresourceUpdates(std::move(other.mSubresourceUpdates))
1458 {
1459 ASSERT(this != &other);
1460 other.mCurrentLayout = ImageLayout::Undefined;
1461 other.mLayerCount = 0;
1462 other.mLevelCount = 0;
1463 }
1464
~ImageHelper()1465 ImageHelper::~ImageHelper()
1466 {
1467 ASSERT(!valid());
1468 }
1469
initStagingBuffer(RendererVk * renderer,const vk::Format & format,VkBufferUsageFlags usageFlags,size_t initialSize)1470 void ImageHelper::initStagingBuffer(RendererVk *renderer,
1471 const vk::Format &format,
1472 VkBufferUsageFlags usageFlags,
1473 size_t initialSize)
1474 {
1475 mStagingBuffer.init(renderer, usageFlags, format.getImageCopyBufferAlignment(), initialSize,
1476 true);
1477 }
1478
init(Context * context,gl::TextureType textureType,const VkExtent3D & extents,const Format & format,GLint samples,VkImageUsageFlags usage,uint32_t mipLevels,uint32_t layerCount)1479 angle::Result ImageHelper::init(Context *context,
1480 gl::TextureType textureType,
1481 const VkExtent3D &extents,
1482 const Format &format,
1483 GLint samples,
1484 VkImageUsageFlags usage,
1485 uint32_t mipLevels,
1486 uint32_t layerCount)
1487 {
1488 return initExternal(context, textureType, extents, format, samples, usage,
1489 ImageLayout::Undefined, nullptr, mipLevels, layerCount);
1490 }
1491
initExternal(Context * context,gl::TextureType textureType,const VkExtent3D & extents,const Format & format,GLint samples,VkImageUsageFlags usage,ImageLayout initialLayout,const void * externalImageCreateInfo,uint32_t mipLevels,uint32_t layerCount)1492 angle::Result ImageHelper::initExternal(Context *context,
1493 gl::TextureType textureType,
1494 const VkExtent3D &extents,
1495 const Format &format,
1496 GLint samples,
1497 VkImageUsageFlags usage,
1498 ImageLayout initialLayout,
1499 const void *externalImageCreateInfo,
1500 uint32_t mipLevels,
1501 uint32_t layerCount)
1502 {
1503 ASSERT(!valid());
1504
1505 mExtents = extents;
1506 mFormat = &format;
1507 mSamples = samples;
1508 mLevelCount = mipLevels;
1509 mLayerCount = layerCount;
1510
1511 // Validate that mLayerCount is compatible with the texture type
1512 ASSERT(textureType != gl::TextureType::_3D || mLayerCount == 1);
1513 ASSERT(textureType != gl::TextureType::_2DArray || mExtents.depth == 1);
1514 ASSERT(textureType != gl::TextureType::External || mLayerCount == 1);
1515 ASSERT(textureType != gl::TextureType::Rectangle || mLayerCount == 1);
1516 ASSERT(textureType != gl::TextureType::CubeMap || mLayerCount == gl::kCubeFaceCount);
1517
1518 VkImageCreateInfo imageInfo = {};
1519 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1520 imageInfo.pNext = externalImageCreateInfo;
1521 imageInfo.flags = GetImageCreateFlags(textureType);
1522 imageInfo.imageType = gl_vk::GetImageType(textureType);
1523 imageInfo.format = format.vkImageFormat;
1524 imageInfo.extent = mExtents;
1525 imageInfo.mipLevels = mipLevels;
1526 imageInfo.arrayLayers = mLayerCount;
1527 imageInfo.samples = gl_vk::GetSamples(samples);
1528 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1529 imageInfo.usage = usage;
1530 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1531 imageInfo.queueFamilyIndexCount = 0;
1532 imageInfo.pQueueFamilyIndices = nullptr;
1533 imageInfo.initialLayout = kImageMemoryBarrierData[initialLayout].layout;
1534
1535 mCurrentLayout = initialLayout;
1536
1537 ANGLE_VK_TRY(context, mImage.init(context->getDevice(), imageInfo));
1538
1539 return angle::Result::Continue;
1540 }
1541
releaseImage(ContextVk * contextVk)1542 void ImageHelper::releaseImage(ContextVk *contextVk)
1543 {
1544 contextVk->releaseObject(getStoredQueueSerial(), &mImage);
1545 contextVk->releaseObject(getStoredQueueSerial(), &mDeviceMemory);
1546 }
1547
releaseImage(DisplayVk * display,std::vector<GarbageObjectBase> * garbageQueue)1548 void ImageHelper::releaseImage(DisplayVk *display, std::vector<GarbageObjectBase> *garbageQueue)
1549 {
1550 mImage.dumpResources(garbageQueue);
1551 mDeviceMemory.dumpResources(garbageQueue);
1552 }
1553
releaseStagingBuffer(ContextVk * contextVk)1554 void ImageHelper::releaseStagingBuffer(ContextVk *contextVk)
1555 {
1556 // Remove updates that never made it to the texture.
1557 for (SubresourceUpdate &update : mSubresourceUpdates)
1558 {
1559 update.release(contextVk);
1560 }
1561 mStagingBuffer.release(contextVk);
1562 mSubresourceUpdates.clear();
1563 }
1564
releaseStagingBuffer(DisplayVk * display,std::vector<GarbageObjectBase> * garbageQueue)1565 void ImageHelper::releaseStagingBuffer(DisplayVk *display,
1566 std::vector<GarbageObjectBase> *garbageQueue)
1567 {
1568 // Remove updates that never made it to the texture.
1569 for (SubresourceUpdate &update : mSubresourceUpdates)
1570 {
1571 update.release(display, garbageQueue);
1572 }
1573 mStagingBuffer.release(display, garbageQueue);
1574 mSubresourceUpdates.clear();
1575 }
1576
resetImageWeakReference()1577 void ImageHelper::resetImageWeakReference()
1578 {
1579 mImage.reset();
1580 }
1581
initMemory(Context * context,const MemoryProperties & memoryProperties,VkMemoryPropertyFlags flags)1582 angle::Result ImageHelper::initMemory(Context *context,
1583 const MemoryProperties &memoryProperties,
1584 VkMemoryPropertyFlags flags)
1585 {
1586 // TODO(jmadill): Memory sub-allocation. http://anglebug.com/2162
1587 ANGLE_TRY(AllocateImageMemory(context, flags, nullptr, &mImage, &mDeviceMemory));
1588 mCurrentQueueFamilyIndex = context->getRenderer()->getQueueFamilyIndex();
1589 return angle::Result::Continue;
1590 }
1591
initExternalMemory(Context * context,const MemoryProperties & memoryProperties,const VkMemoryRequirements & memoryRequirements,const void * extraAllocationInfo,uint32_t currentQueueFamilyIndex,VkMemoryPropertyFlags flags)1592 angle::Result ImageHelper::initExternalMemory(Context *context,
1593 const MemoryProperties &memoryProperties,
1594 const VkMemoryRequirements &memoryRequirements,
1595 const void *extraAllocationInfo,
1596 uint32_t currentQueueFamilyIndex,
1597
1598 VkMemoryPropertyFlags flags)
1599 {
1600 // TODO(jmadill): Memory sub-allocation. http://anglebug.com/2162
1601 ANGLE_TRY(AllocateImageMemoryWithRequirements(context, flags, memoryRequirements,
1602 extraAllocationInfo, &mImage, &mDeviceMemory));
1603 mCurrentQueueFamilyIndex = currentQueueFamilyIndex;
1604 return angle::Result::Continue;
1605 }
1606
initImageView(Context * context,gl::TextureType textureType,VkImageAspectFlags aspectMask,const gl::SwizzleState & swizzleMap,ImageView * imageViewOut,uint32_t baseMipLevel,uint32_t levelCount)1607 angle::Result ImageHelper::initImageView(Context *context,
1608 gl::TextureType textureType,
1609 VkImageAspectFlags aspectMask,
1610 const gl::SwizzleState &swizzleMap,
1611 ImageView *imageViewOut,
1612 uint32_t baseMipLevel,
1613 uint32_t levelCount)
1614 {
1615 return initLayerImageView(context, textureType, aspectMask, swizzleMap, imageViewOut,
1616 baseMipLevel, levelCount, 0, mLayerCount);
1617 }
1618
initLayerImageView(Context * context,gl::TextureType textureType,VkImageAspectFlags aspectMask,const gl::SwizzleState & swizzleMap,ImageView * imageViewOut,uint32_t baseMipLevel,uint32_t levelCount,uint32_t baseArrayLayer,uint32_t layerCount)1619 angle::Result ImageHelper::initLayerImageView(Context *context,
1620 gl::TextureType textureType,
1621 VkImageAspectFlags aspectMask,
1622 const gl::SwizzleState &swizzleMap,
1623 ImageView *imageViewOut,
1624 uint32_t baseMipLevel,
1625 uint32_t levelCount,
1626 uint32_t baseArrayLayer,
1627 uint32_t layerCount)
1628 {
1629 VkImageViewCreateInfo viewInfo = {};
1630 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1631 viewInfo.flags = 0;
1632 viewInfo.image = mImage.getHandle();
1633 viewInfo.viewType = gl_vk::GetImageViewType(textureType);
1634 viewInfo.format = mFormat->vkImageFormat;
1635 if (swizzleMap.swizzleRequired())
1636 {
1637 viewInfo.components.r = gl_vk::GetSwizzle(swizzleMap.swizzleRed);
1638 viewInfo.components.g = gl_vk::GetSwizzle(swizzleMap.swizzleGreen);
1639 viewInfo.components.b = gl_vk::GetSwizzle(swizzleMap.swizzleBlue);
1640 viewInfo.components.a = gl_vk::GetSwizzle(swizzleMap.swizzleAlpha);
1641 }
1642 else
1643 {
1644 viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
1645 viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
1646 viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
1647 viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
1648 }
1649 viewInfo.subresourceRange.aspectMask = aspectMask;
1650 viewInfo.subresourceRange.baseMipLevel = baseMipLevel;
1651 viewInfo.subresourceRange.levelCount = levelCount;
1652 viewInfo.subresourceRange.baseArrayLayer = baseArrayLayer;
1653 viewInfo.subresourceRange.layerCount = layerCount;
1654
1655 ANGLE_VK_TRY(context, imageViewOut->init(context->getDevice(), viewInfo));
1656 return angle::Result::Continue;
1657 }
1658
destroy(VkDevice device)1659 void ImageHelper::destroy(VkDevice device)
1660 {
1661 mImage.destroy(device);
1662 mDeviceMemory.destroy(device);
1663 mCurrentLayout = ImageLayout::Undefined;
1664 mLayerCount = 0;
1665 mLevelCount = 0;
1666 }
1667
init2DWeakReference(VkImage handle,const gl::Extents & glExtents,const Format & format,GLint samples)1668 void ImageHelper::init2DWeakReference(VkImage handle,
1669 const gl::Extents &glExtents,
1670 const Format &format,
1671 GLint samples)
1672 {
1673 ASSERT(!valid());
1674
1675 gl_vk::GetExtent(glExtents, &mExtents);
1676 mFormat = &format;
1677 mSamples = samples;
1678 mCurrentLayout = ImageLayout::Undefined;
1679 mLayerCount = 1;
1680 mLevelCount = 1;
1681
1682 mImage.setHandle(handle);
1683 }
1684
init2DStaging(Context * context,const MemoryProperties & memoryProperties,const gl::Extents & glExtents,const Format & format,VkImageUsageFlags usage,uint32_t layerCount)1685 angle::Result ImageHelper::init2DStaging(Context *context,
1686 const MemoryProperties &memoryProperties,
1687 const gl::Extents &glExtents,
1688 const Format &format,
1689 VkImageUsageFlags usage,
1690 uint32_t layerCount)
1691 {
1692 ASSERT(!valid());
1693
1694 gl_vk::GetExtent(glExtents, &mExtents);
1695 mFormat = &format;
1696 mSamples = 1;
1697 mLayerCount = layerCount;
1698 mLevelCount = 1;
1699
1700 mCurrentLayout = ImageLayout::Undefined;
1701
1702 VkImageCreateInfo imageInfo = {};
1703 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1704 imageInfo.flags = 0;
1705 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1706 imageInfo.format = format.vkImageFormat;
1707 imageInfo.extent = mExtents;
1708 imageInfo.mipLevels = 1;
1709 imageInfo.arrayLayers = mLayerCount;
1710 imageInfo.samples = gl_vk::GetSamples(mSamples);
1711 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
1712 imageInfo.usage = usage;
1713 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1714 imageInfo.queueFamilyIndexCount = 0;
1715 imageInfo.pQueueFamilyIndices = nullptr;
1716 imageInfo.initialLayout = getCurrentLayout();
1717
1718 ANGLE_VK_TRY(context, mImage.init(context->getDevice(), imageInfo));
1719
1720 // Allocate and bind device-local memory.
1721 VkMemoryPropertyFlags memoryPropertyFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
1722 ANGLE_TRY(initMemory(context, memoryProperties, memoryPropertyFlags));
1723
1724 return angle::Result::Continue;
1725 }
1726
getAspectFlags() const1727 VkImageAspectFlags ImageHelper::getAspectFlags() const
1728 {
1729 return GetFormatAspectFlags(mFormat->imageFormat());
1730 }
1731
dumpResources(Serial serial,std::vector<GarbageObject> * garbageQueue)1732 void ImageHelper::dumpResources(Serial serial, std::vector<GarbageObject> *garbageQueue)
1733 {
1734 mImage.dumpResources(serial, garbageQueue);
1735 mDeviceMemory.dumpResources(serial, garbageQueue);
1736 }
1737
getCurrentLayout() const1738 VkImageLayout ImageHelper::getCurrentLayout() const
1739 {
1740 return kImageMemoryBarrierData[mCurrentLayout].layout;
1741 }
1742
getLevelExtents2D(uint32_t level) const1743 gl::Extents ImageHelper::getLevelExtents2D(uint32_t level) const
1744 {
1745 uint32_t width = std::max(mExtents.width >> level, 1u);
1746 uint32_t height = std::max(mExtents.height >> level, 1u);
1747
1748 return gl::Extents(width, height, 1);
1749 }
1750
isLayoutChangeNecessary(ImageLayout newLayout) const1751 bool ImageHelper::isLayoutChangeNecessary(ImageLayout newLayout) const
1752 {
1753 const ImageMemoryBarrierData &layoutData = kImageMemoryBarrierData[mCurrentLayout];
1754
1755 // If transitioning to the same layout, we rarely need a barrier. RAR (read-after-read)
1756 // doesn't need a barrier, and WAW (write-after-write) is guaranteed to not require a barrier
1757 // for color attachment and depth/stencil attachment writes. Transfer dst and shader writes
1758 // are basically the only cases where an execution barrier is still necessary.
1759 bool sameLayoutAndNoNeedForBarrier =
1760 mCurrentLayout == newLayout && !layoutData.sameLayoutTransitionRequiresBarrier;
1761
1762 return !sameLayoutAndNoNeedForBarrier;
1763 }
1764
changeLayout(VkImageAspectFlags aspectMask,ImageLayout newLayout,vk::CommandBuffer * commandBuffer)1765 void ImageHelper::changeLayout(VkImageAspectFlags aspectMask,
1766 ImageLayout newLayout,
1767 vk::CommandBuffer *commandBuffer)
1768 {
1769 if (!isLayoutChangeNecessary(newLayout))
1770 {
1771 return;
1772 }
1773
1774 forceChangeLayoutAndQueue(aspectMask, newLayout, mCurrentQueueFamilyIndex, commandBuffer);
1775 }
1776
changeLayoutAndQueue(VkImageAspectFlags aspectMask,ImageLayout newLayout,uint32_t newQueueFamilyIndex,vk::CommandBuffer * commandBuffer)1777 void ImageHelper::changeLayoutAndQueue(VkImageAspectFlags aspectMask,
1778 ImageLayout newLayout,
1779 uint32_t newQueueFamilyIndex,
1780 vk::CommandBuffer *commandBuffer)
1781 {
1782 ASSERT(isQueueChangeNeccesary(newQueueFamilyIndex));
1783 forceChangeLayoutAndQueue(aspectMask, newLayout, newQueueFamilyIndex, commandBuffer);
1784 }
1785
forceChangeLayoutAndQueue(VkImageAspectFlags aspectMask,ImageLayout newLayout,uint32_t newQueueFamilyIndex,vk::CommandBuffer * commandBuffer)1786 void ImageHelper::forceChangeLayoutAndQueue(VkImageAspectFlags aspectMask,
1787 ImageLayout newLayout,
1788 uint32_t newQueueFamilyIndex,
1789 vk::CommandBuffer *commandBuffer)
1790 {
1791 // If transitioning to the same layout (and there is no queue transfer), an execution barrier
1792 // suffices.
1793 //
1794 // TODO(syoussefi): AMD driver on windows has a bug where an execution barrier is not sufficient
1795 // between transfer dst operations (even if the transfer is not to the same subresource!). A
1796 // workaround may be necessary. http://anglebug.com/3554
1797 if (mCurrentLayout == newLayout && mCurrentQueueFamilyIndex == newQueueFamilyIndex &&
1798 mCurrentLayout != ImageLayout::TransferDst)
1799 {
1800 const ImageMemoryBarrierData &transition = kImageMemoryBarrierData[mCurrentLayout];
1801
1802 // In this case, the image is going to be used in the same way, so the src and dst stage
1803 // masks must be necessarily equal.
1804 ASSERT(transition.srcStageMask == transition.dstStageMask);
1805
1806 commandBuffer->executionBarrier(transition.dstStageMask);
1807 return;
1808 }
1809
1810 const ImageMemoryBarrierData &transitionFrom = kImageMemoryBarrierData[mCurrentLayout];
1811 const ImageMemoryBarrierData &transitionTo = kImageMemoryBarrierData[newLayout];
1812
1813 VkImageMemoryBarrier imageMemoryBarrier = {};
1814 imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
1815 imageMemoryBarrier.srcAccessMask = transitionFrom.srcAccessMask;
1816 imageMemoryBarrier.dstAccessMask = transitionTo.dstAccessMask;
1817 imageMemoryBarrier.oldLayout = transitionFrom.layout;
1818 imageMemoryBarrier.newLayout = transitionTo.layout;
1819 imageMemoryBarrier.srcQueueFamilyIndex = mCurrentQueueFamilyIndex;
1820 imageMemoryBarrier.dstQueueFamilyIndex = newQueueFamilyIndex;
1821 imageMemoryBarrier.image = mImage.getHandle();
1822
1823 // TODO(jmadill): Is this needed for mipped/layer images?
1824 imageMemoryBarrier.subresourceRange.aspectMask = aspectMask;
1825 imageMemoryBarrier.subresourceRange.baseMipLevel = 0;
1826 imageMemoryBarrier.subresourceRange.levelCount = mLevelCount;
1827 imageMemoryBarrier.subresourceRange.baseArrayLayer = 0;
1828 imageMemoryBarrier.subresourceRange.layerCount = mLayerCount;
1829
1830 commandBuffer->imageBarrier(transitionFrom.srcStageMask, transitionTo.dstStageMask,
1831 &imageMemoryBarrier);
1832 mCurrentLayout = newLayout;
1833 mCurrentQueueFamilyIndex = newQueueFamilyIndex;
1834 }
1835
clearColor(const VkClearColorValue & color,uint32_t baseMipLevel,uint32_t levelCount,uint32_t baseArrayLayer,uint32_t layerCount,vk::CommandBuffer * commandBuffer)1836 void ImageHelper::clearColor(const VkClearColorValue &color,
1837 uint32_t baseMipLevel,
1838 uint32_t levelCount,
1839 uint32_t baseArrayLayer,
1840 uint32_t layerCount,
1841 vk::CommandBuffer *commandBuffer)
1842 {
1843 ASSERT(valid());
1844
1845 ASSERT(mCurrentLayout == ImageLayout::TransferDst);
1846
1847 VkImageSubresourceRange range = {};
1848 range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1849 range.baseMipLevel = baseMipLevel;
1850 range.levelCount = levelCount;
1851 range.baseArrayLayer = baseArrayLayer;
1852 range.layerCount = layerCount;
1853
1854 commandBuffer->clearColorImage(mImage, getCurrentLayout(), color, 1, &range);
1855 }
1856
clearDepthStencil(VkImageAspectFlags imageAspectFlags,VkImageAspectFlags clearAspectFlags,const VkClearDepthStencilValue & depthStencil,uint32_t baseMipLevel,uint32_t levelCount,uint32_t baseArrayLayer,uint32_t layerCount,vk::CommandBuffer * commandBuffer)1857 void ImageHelper::clearDepthStencil(VkImageAspectFlags imageAspectFlags,
1858 VkImageAspectFlags clearAspectFlags,
1859 const VkClearDepthStencilValue &depthStencil,
1860 uint32_t baseMipLevel,
1861 uint32_t levelCount,
1862 uint32_t baseArrayLayer,
1863 uint32_t layerCount,
1864 vk::CommandBuffer *commandBuffer)
1865 {
1866 ASSERT(valid());
1867
1868 ASSERT(mCurrentLayout == ImageLayout::TransferDst);
1869
1870 VkImageSubresourceRange clearRange = {
1871 /*aspectMask*/ clearAspectFlags,
1872 /*baseMipLevel*/ baseMipLevel,
1873 /*levelCount*/ levelCount,
1874 /*baseArrayLayer*/ baseArrayLayer,
1875 /*layerCount*/ layerCount,
1876 };
1877
1878 commandBuffer->clearDepthStencilImage(mImage, getCurrentLayout(), depthStencil, 1, &clearRange);
1879 }
1880
clear(const VkClearValue & value,uint32_t mipLevel,uint32_t baseArrayLayer,uint32_t layerCount,vk::CommandBuffer * commandBuffer)1881 void ImageHelper::clear(const VkClearValue &value,
1882 uint32_t mipLevel,
1883 uint32_t baseArrayLayer,
1884 uint32_t layerCount,
1885 vk::CommandBuffer *commandBuffer)
1886 {
1887 const angle::Format &angleFormat = mFormat->angleFormat();
1888 bool isDepthStencil = angleFormat.depthBits > 0 || angleFormat.stencilBits > 0;
1889
1890 if (isDepthStencil)
1891 {
1892 const VkImageAspectFlags aspect = vk::GetDepthStencilAspectFlags(mFormat->imageFormat());
1893 clearDepthStencil(aspect, aspect, value.depthStencil, mipLevel, 1, baseArrayLayer,
1894 layerCount, commandBuffer);
1895 }
1896 else
1897 {
1898 clearColor(value.color, mipLevel, 1, baseArrayLayer, layerCount, commandBuffer);
1899 }
1900 }
1901
getSize(const gl::ImageIndex & index) const1902 gl::Extents ImageHelper::getSize(const gl::ImageIndex &index) const
1903 {
1904 GLint mipLevel = index.getLevelIndex();
1905 // Level 0 should be the size of the extents, after that every time you increase a level
1906 // you shrink the extents by half.
1907 return gl::Extents(std::max(1u, mExtents.width >> mipLevel),
1908 std::max(1u, mExtents.height >> mipLevel), mExtents.depth);
1909 }
1910
1911 // static
Copy(ImageHelper * srcImage,ImageHelper * dstImage,const gl::Offset & srcOffset,const gl::Offset & dstOffset,const gl::Extents & copySize,const VkImageSubresourceLayers & srcSubresource,const VkImageSubresourceLayers & dstSubresource,vk::CommandBuffer * commandBuffer)1912 void ImageHelper::Copy(ImageHelper *srcImage,
1913 ImageHelper *dstImage,
1914 const gl::Offset &srcOffset,
1915 const gl::Offset &dstOffset,
1916 const gl::Extents ©Size,
1917 const VkImageSubresourceLayers &srcSubresource,
1918 const VkImageSubresourceLayers &dstSubresource,
1919 vk::CommandBuffer *commandBuffer)
1920 {
1921 ASSERT(commandBuffer->valid() && srcImage->valid() && dstImage->valid());
1922
1923 ASSERT(srcImage->getCurrentLayout() == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
1924 ASSERT(dstImage->getCurrentLayout() == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
1925
1926 VkImageCopy region = {};
1927 region.srcSubresource = srcSubresource;
1928 region.srcOffset.x = srcOffset.x;
1929 region.srcOffset.y = srcOffset.y;
1930 region.srcOffset.z = srcOffset.z;
1931 region.dstSubresource = dstSubresource;
1932 region.dstOffset.x = dstOffset.x;
1933 region.dstOffset.y = dstOffset.y;
1934 region.dstOffset.z = dstOffset.z;
1935 region.extent.width = copySize.width;
1936 region.extent.height = copySize.height;
1937 region.extent.depth = copySize.depth;
1938
1939 commandBuffer->copyImage(srcImage->getImage(), srcImage->getCurrentLayout(),
1940 dstImage->getImage(), dstImage->getCurrentLayout(), 1, ®ion);
1941 }
1942
generateMipmapsWithBlit(ContextVk * contextVk,GLuint maxLevel)1943 angle::Result ImageHelper::generateMipmapsWithBlit(ContextVk *contextVk, GLuint maxLevel)
1944 {
1945 vk::CommandBuffer *commandBuffer = nullptr;
1946 ANGLE_TRY(recordCommands(contextVk, &commandBuffer));
1947
1948 changeLayout(VK_IMAGE_ASPECT_COLOR_BIT, ImageLayout::TransferDst, commandBuffer);
1949
1950 // We are able to use blitImage since the image format we are using supports it. This
1951 // is a faster way we can generate the mips.
1952 int32_t mipWidth = mExtents.width;
1953 int32_t mipHeight = mExtents.height;
1954
1955 // Manually manage the image memory barrier because it uses a lot more parameters than our
1956 // usual one.
1957 VkImageMemoryBarrier barrier = {};
1958 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
1959 barrier.image = mImage.getHandle();
1960 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1961 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1962 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1963 barrier.subresourceRange.baseArrayLayer = 0;
1964 barrier.subresourceRange.layerCount = mLayerCount;
1965 barrier.subresourceRange.levelCount = 1;
1966
1967 for (uint32_t mipLevel = 1; mipLevel <= maxLevel; mipLevel++)
1968 {
1969 int32_t nextMipWidth = std::max<int32_t>(1, mipWidth >> 1);
1970 int32_t nextMipHeight = std::max<int32_t>(1, mipHeight >> 1);
1971
1972 barrier.subresourceRange.baseMipLevel = mipLevel - 1;
1973 barrier.oldLayout = getCurrentLayout();
1974 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
1975 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1976 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
1977
1978 // We can do it for all layers at once.
1979 commandBuffer->imageBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
1980 &barrier);
1981 VkImageBlit blit = {};
1982 blit.srcOffsets[0] = {0, 0, 0};
1983 blit.srcOffsets[1] = {mipWidth, mipHeight, 1};
1984 blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1985 blit.srcSubresource.mipLevel = mipLevel - 1;
1986 blit.srcSubresource.baseArrayLayer = 0;
1987 blit.srcSubresource.layerCount = mLayerCount;
1988 blit.dstOffsets[0] = {0, 0, 0};
1989 blit.dstOffsets[1] = {nextMipWidth, nextMipHeight, 1};
1990 blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1991 blit.dstSubresource.mipLevel = mipLevel;
1992 blit.dstSubresource.baseArrayLayer = 0;
1993 blit.dstSubresource.layerCount = mLayerCount;
1994
1995 mipWidth = nextMipWidth;
1996 mipHeight = nextMipHeight;
1997
1998 commandBuffer->blitImage(mImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, mImage,
1999 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR);
2000 }
2001
2002 // Transition the last mip level to the same layout as all the other ones, so we can declare
2003 // our whole image layout to be SRC_OPTIMAL.
2004 barrier.subresourceRange.baseMipLevel = maxLevel;
2005 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
2006 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
2007
2008 // We can do it for all layers at once.
2009 commandBuffer->imageBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
2010 &barrier);
2011 // This is just changing the internal state of the image helper so that the next call
2012 // to changeLayout will use this layout as the "oldLayout" argument.
2013 mCurrentLayout = ImageLayout::TransferSrc;
2014
2015 return angle::Result::Continue;
2016 }
2017
resolve(ImageHelper * dest,const VkImageResolve & region,vk::CommandBuffer * commandBuffer)2018 void ImageHelper::resolve(ImageHelper *dest,
2019 const VkImageResolve ®ion,
2020 vk::CommandBuffer *commandBuffer)
2021 {
2022 ASSERT(mCurrentLayout == vk::ImageLayout::TransferSrc);
2023 dest->changeLayout(region.dstSubresource.aspectMask, vk::ImageLayout::TransferDst,
2024 commandBuffer);
2025
2026 commandBuffer->resolveImage(getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dest->getImage(),
2027 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
2028 }
2029
removeStagedUpdates(ContextVk * contextVk,const gl::ImageIndex & index)2030 void ImageHelper::removeStagedUpdates(ContextVk *contextVk, const gl::ImageIndex &index)
2031 {
2032 // Find any staged updates for this index and removes them from the pending list.
2033 uint32_t levelIndex = index.getLevelIndex();
2034 uint32_t layerIndex = index.hasLayer() ? index.getLayerIndex() : 0;
2035
2036 for (size_t index = 0; index < mSubresourceUpdates.size();)
2037 {
2038 auto update = mSubresourceUpdates.begin() + index;
2039 if (update->isUpdateToLayerLevel(layerIndex, levelIndex))
2040 {
2041 update->release(contextVk);
2042 mSubresourceUpdates.erase(update);
2043 }
2044 else
2045 {
2046 index++;
2047 }
2048 }
2049 }
2050
stageSubresourceUpdate(ContextVk * contextVk,const gl::ImageIndex & index,const gl::Extents & glExtents,const gl::Offset & offset,const gl::InternalFormat & formatInfo,const gl::PixelUnpackState & unpack,GLenum type,const uint8_t * pixels,const vk::Format & vkFormat)2051 angle::Result ImageHelper::stageSubresourceUpdate(ContextVk *contextVk,
2052 const gl::ImageIndex &index,
2053 const gl::Extents &glExtents,
2054 const gl::Offset &offset,
2055 const gl::InternalFormat &formatInfo,
2056 const gl::PixelUnpackState &unpack,
2057 GLenum type,
2058 const uint8_t *pixels,
2059 const vk::Format &vkFormat)
2060 {
2061 GLuint inputRowPitch = 0;
2062 ANGLE_VK_CHECK_MATH(contextVk,
2063 formatInfo.computeRowPitch(type, glExtents.width, unpack.alignment,
2064 unpack.rowLength, &inputRowPitch));
2065
2066 GLuint inputDepthPitch = 0;
2067 ANGLE_VK_CHECK_MATH(
2068 contextVk, formatInfo.computeDepthPitch(glExtents.height, unpack.imageHeight, inputRowPitch,
2069 &inputDepthPitch));
2070
2071 GLuint inputSkipBytes = 0;
2072 ANGLE_VK_CHECK_MATH(contextVk,
2073 formatInfo.computeSkipBytes(type, inputRowPitch, inputDepthPitch, unpack,
2074 index.usesTex3D(), &inputSkipBytes));
2075
2076 const angle::Format &storageFormat = vkFormat.imageFormat();
2077
2078 size_t outputRowPitch;
2079 size_t outputDepthPitch;
2080 size_t stencilAllocationSize = 0;
2081 uint32_t bufferRowLength;
2082 uint32_t bufferImageHeight;
2083 size_t allocationSize;
2084
2085 LoadImageFunctionInfo loadFunctionInfo = vkFormat.textureLoadFunctions(type);
2086 LoadImageFunction stencilLoadFunction = nullptr;
2087
2088 if (storageFormat.isBlock)
2089 {
2090 const gl::InternalFormat &storageFormatInfo = vkFormat.getInternalFormatInfo(type);
2091 GLuint rowPitch;
2092 GLuint depthPitch;
2093 GLuint totalSize;
2094
2095 ANGLE_VK_CHECK_MATH(contextVk, storageFormatInfo.computeCompressedImageSize(
2096 gl::Extents(glExtents.width, 1, 1), &rowPitch));
2097 ANGLE_VK_CHECK_MATH(contextVk,
2098 storageFormatInfo.computeCompressedImageSize(
2099 gl::Extents(glExtents.width, glExtents.height, 1), &depthPitch));
2100
2101 ANGLE_VK_CHECK_MATH(contextVk,
2102 storageFormatInfo.computeCompressedImageSize(glExtents, &totalSize));
2103
2104 outputRowPitch = rowPitch;
2105 outputDepthPitch = depthPitch;
2106
2107 angle::CheckedNumeric<uint32_t> checkedRowLength =
2108 rx::CheckedRoundUp<uint32_t>(glExtents.width, storageFormatInfo.compressedBlockWidth);
2109 angle::CheckedNumeric<uint32_t> checkedImageHeight =
2110 rx::CheckedRoundUp<uint32_t>(glExtents.height, storageFormatInfo.compressedBlockHeight);
2111
2112 ANGLE_VK_CHECK_MATH(contextVk, checkedRowLength.IsValid());
2113 ANGLE_VK_CHECK_MATH(contextVk, checkedImageHeight.IsValid());
2114
2115 bufferRowLength = checkedRowLength.ValueOrDie();
2116 bufferImageHeight = checkedImageHeight.ValueOrDie();
2117 allocationSize = totalSize;
2118 }
2119 else
2120 {
2121 ASSERT(storageFormat.pixelBytes != 0);
2122
2123 if (storageFormat.id == angle::FormatID::D24_UNORM_S8_UINT)
2124 {
2125 stencilLoadFunction = angle::LoadX24S8ToS8;
2126 }
2127 if (storageFormat.id == angle::FormatID::D32_FLOAT_S8X24_UINT)
2128 {
2129 // If depth is D32FLOAT_S8, we must pack D32F tightly (no stencil) for CopyBufferToImage
2130 outputRowPitch = sizeof(float) * glExtents.width;
2131
2132 // The generic load functions don't handle tightly packing D32FS8 to D32F & S8 so call
2133 // special case load functions.
2134 loadFunctionInfo.loadFunction = angle::LoadD32FS8X24ToD32F;
2135 stencilLoadFunction = angle::LoadX32S8ToS8;
2136 }
2137 else
2138 {
2139 outputRowPitch = storageFormat.pixelBytes * glExtents.width;
2140 }
2141 outputDepthPitch = outputRowPitch * glExtents.height;
2142
2143 bufferRowLength = glExtents.width;
2144 bufferImageHeight = glExtents.height;
2145
2146 allocationSize = outputDepthPitch * glExtents.depth;
2147
2148 // Note: because the LoadImageFunctionInfo functions are limited to copying a single
2149 // component, we have to special case packed depth/stencil use and send the stencil as a
2150 // separate chunk.
2151 if (storageFormat.depthBits > 0 && storageFormat.stencilBits > 0 &&
2152 formatInfo.depthBits > 0 && formatInfo.stencilBits > 0)
2153 {
2154 // Note: Stencil is always one byte
2155 stencilAllocationSize = glExtents.width * glExtents.height * glExtents.depth;
2156 allocationSize += stencilAllocationSize;
2157 }
2158 }
2159
2160 VkBuffer bufferHandle = VK_NULL_HANDLE;
2161
2162 uint8_t *stagingPointer = nullptr;
2163 VkDeviceSize stagingOffset = 0;
2164 ANGLE_TRY(mStagingBuffer.allocate(contextVk, allocationSize, &stagingPointer, &bufferHandle,
2165 &stagingOffset, nullptr));
2166
2167 const uint8_t *source = pixels + static_cast<ptrdiff_t>(inputSkipBytes);
2168
2169 loadFunctionInfo.loadFunction(glExtents.width, glExtents.height, glExtents.depth, source,
2170 inputRowPitch, inputDepthPitch, stagingPointer, outputRowPitch,
2171 outputDepthPitch);
2172
2173 VkBufferImageCopy copy = {};
2174 VkImageAspectFlags aspectFlags = GetFormatAspectFlags(vkFormat.imageFormat());
2175
2176 copy.bufferOffset = stagingOffset;
2177 copy.bufferRowLength = bufferRowLength;
2178 copy.bufferImageHeight = bufferImageHeight;
2179
2180 copy.imageSubresource.mipLevel = index.getLevelIndex();
2181 copy.imageSubresource.layerCount = index.getLayerCount();
2182
2183 gl_vk::GetOffset(offset, ©.imageOffset);
2184 gl_vk::GetExtent(glExtents, ©.imageExtent);
2185
2186 if (gl::IsArrayTextureType(index.getType()))
2187 {
2188 copy.imageSubresource.baseArrayLayer = offset.z;
2189 copy.imageOffset.z = 0;
2190 copy.imageExtent.depth = 1;
2191 }
2192 else
2193 {
2194 copy.imageSubresource.baseArrayLayer = index.hasLayer() ? index.getLayerIndex() : 0;
2195 }
2196
2197 if (stencilAllocationSize > 0)
2198 {
2199 // Note: Stencil is always one byte
2200 ASSERT((aspectFlags & VK_IMAGE_ASPECT_STENCIL_BIT) != 0);
2201
2202 // Skip over depth data.
2203 stagingPointer += outputDepthPitch * glExtents.depth;
2204 stagingOffset += outputDepthPitch * glExtents.depth;
2205
2206 // recompute pitch for stencil data
2207 outputRowPitch = glExtents.width;
2208 outputDepthPitch = outputRowPitch * glExtents.height;
2209
2210 ASSERT(stencilLoadFunction != nullptr);
2211 stencilLoadFunction(glExtents.width, glExtents.height, glExtents.depth, source,
2212 inputRowPitch, inputDepthPitch, stagingPointer, outputRowPitch,
2213 outputDepthPitch);
2214
2215 VkBufferImageCopy stencilCopy = {};
2216
2217 stencilCopy.bufferOffset = stagingOffset;
2218 stencilCopy.bufferRowLength = bufferRowLength;
2219 stencilCopy.bufferImageHeight = bufferImageHeight;
2220 stencilCopy.imageSubresource.mipLevel = copy.imageSubresource.mipLevel;
2221 stencilCopy.imageSubresource.baseArrayLayer = copy.imageSubresource.baseArrayLayer;
2222 stencilCopy.imageSubresource.layerCount = copy.imageSubresource.layerCount;
2223 stencilCopy.imageOffset = copy.imageOffset;
2224 stencilCopy.imageExtent = copy.imageExtent;
2225 stencilCopy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
2226 mSubresourceUpdates.emplace_back(bufferHandle, stencilCopy);
2227
2228 aspectFlags &= ~VK_IMAGE_ASPECT_STENCIL_BIT;
2229 }
2230
2231 if (IsMaskFlagSet(aspectFlags, static_cast<VkImageAspectFlags>(VK_IMAGE_ASPECT_STENCIL_BIT |
2232 VK_IMAGE_ASPECT_DEPTH_BIT)))
2233 {
2234 // We still have both depth and stencil aspect bits set. That means we have a destination
2235 // buffer that is packed depth stencil and that the application is only loading one aspect.
2236 // Figure out which aspect the user is touching and remove the unused aspect bit.
2237 if (formatInfo.stencilBits > 0)
2238 {
2239 aspectFlags &= ~VK_IMAGE_ASPECT_DEPTH_BIT;
2240 }
2241 else
2242 {
2243 aspectFlags &= ~VK_IMAGE_ASPECT_STENCIL_BIT;
2244 }
2245 }
2246
2247 if (aspectFlags)
2248 {
2249 copy.imageSubresource.aspectMask = aspectFlags;
2250 mSubresourceUpdates.emplace_back(bufferHandle, copy);
2251 }
2252
2253 return angle::Result::Continue;
2254 }
2255
stageSubresourceUpdateAndGetData(ContextVk * contextVk,size_t allocationSize,const gl::ImageIndex & imageIndex,const gl::Extents & glExtents,const gl::Offset & offset,uint8_t ** destData)2256 angle::Result ImageHelper::stageSubresourceUpdateAndGetData(ContextVk *contextVk,
2257 size_t allocationSize,
2258 const gl::ImageIndex &imageIndex,
2259 const gl::Extents &glExtents,
2260 const gl::Offset &offset,
2261 uint8_t **destData)
2262 {
2263 VkBuffer bufferHandle;
2264 VkDeviceSize stagingOffset = 0;
2265 ANGLE_TRY(mStagingBuffer.allocate(contextVk, allocationSize, destData, &bufferHandle,
2266 &stagingOffset, nullptr));
2267
2268 VkBufferImageCopy copy = {};
2269 copy.bufferOffset = stagingOffset;
2270 copy.bufferRowLength = glExtents.width;
2271 copy.bufferImageHeight = glExtents.height;
2272 copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2273 copy.imageSubresource.mipLevel = imageIndex.getLevelIndex();
2274 copy.imageSubresource.baseArrayLayer = imageIndex.hasLayer() ? imageIndex.getLayerIndex() : 0;
2275 copy.imageSubresource.layerCount = imageIndex.getLayerCount();
2276
2277 // Note: Only support color now
2278 ASSERT(getAspectFlags() == VK_IMAGE_ASPECT_COLOR_BIT);
2279
2280 gl_vk::GetOffset(offset, ©.imageOffset);
2281 gl_vk::GetExtent(glExtents, ©.imageExtent);
2282
2283 mSubresourceUpdates.emplace_back(bufferHandle, copy);
2284
2285 return angle::Result::Continue;
2286 }
2287
stageSubresourceUpdateFromFramebuffer(const gl::Context * context,const gl::ImageIndex & index,const gl::Rectangle & sourceArea,const gl::Offset & dstOffset,const gl::Extents & dstExtent,const gl::InternalFormat & formatInfo,FramebufferVk * framebufferVk)2288 angle::Result ImageHelper::stageSubresourceUpdateFromFramebuffer(
2289 const gl::Context *context,
2290 const gl::ImageIndex &index,
2291 const gl::Rectangle &sourceArea,
2292 const gl::Offset &dstOffset,
2293 const gl::Extents &dstExtent,
2294 const gl::InternalFormat &formatInfo,
2295 FramebufferVk *framebufferVk)
2296 {
2297 ContextVk *contextVk = vk::GetImpl(context);
2298
2299 // If the extents and offset is outside the source image, we need to clip.
2300 gl::Rectangle clippedRectangle;
2301 const gl::Extents readExtents = framebufferVk->getReadImageExtents();
2302 if (!ClipRectangle(sourceArea, gl::Rectangle(0, 0, readExtents.width, readExtents.height),
2303 &clippedRectangle))
2304 {
2305 // Empty source area, nothing to do.
2306 return angle::Result::Continue;
2307 }
2308
2309 bool isViewportFlipEnabled = contextVk->isViewportFlipEnabledForDrawFBO();
2310 if (isViewportFlipEnabled)
2311 {
2312 clippedRectangle.y = readExtents.height - clippedRectangle.y - clippedRectangle.height;
2313 }
2314
2315 // 1- obtain a buffer handle to copy to
2316 RendererVk *renderer = contextVk->getRenderer();
2317
2318 const vk::Format &vkFormat = renderer->getFormat(formatInfo.sizedInternalFormat);
2319 const angle::Format &storageFormat = vkFormat.imageFormat();
2320 LoadImageFunctionInfo loadFunction = vkFormat.textureLoadFunctions(formatInfo.type);
2321
2322 size_t outputRowPitch = storageFormat.pixelBytes * clippedRectangle.width;
2323 size_t outputDepthPitch = outputRowPitch * clippedRectangle.height;
2324
2325 VkBuffer bufferHandle = VK_NULL_HANDLE;
2326
2327 uint8_t *stagingPointer = nullptr;
2328 VkDeviceSize stagingOffset = 0;
2329
2330 // The destination is only one layer deep.
2331 size_t allocationSize = outputDepthPitch;
2332 ANGLE_TRY(mStagingBuffer.allocate(contextVk, allocationSize, &stagingPointer, &bufferHandle,
2333 &stagingOffset, nullptr));
2334
2335 const angle::Format ©Format =
2336 GetFormatFromFormatType(formatInfo.internalFormat, formatInfo.type);
2337 PackPixelsParams params(clippedRectangle, copyFormat, static_cast<GLuint>(outputRowPitch),
2338 isViewportFlipEnabled, nullptr, 0);
2339
2340 // 2- copy the source image region to the pixel buffer using a cpu readback
2341 if (loadFunction.requiresConversion)
2342 {
2343 // When a conversion is required, we need to use the loadFunction to read from a temporary
2344 // buffer instead so its an even slower path.
2345 size_t bufferSize =
2346 storageFormat.pixelBytes * clippedRectangle.width * clippedRectangle.height;
2347 angle::MemoryBuffer *memoryBuffer = nullptr;
2348 ANGLE_VK_CHECK_ALLOC(contextVk, context->getScratchBuffer(bufferSize, &memoryBuffer));
2349
2350 // Read into the scratch buffer
2351 ANGLE_TRY(framebufferVk->readPixelsImpl(
2352 contextVk, clippedRectangle, params, VK_IMAGE_ASPECT_COLOR_BIT,
2353 framebufferVk->getColorReadRenderTarget(), memoryBuffer->data()));
2354
2355 // Load from scratch buffer to our pixel buffer
2356 loadFunction.loadFunction(clippedRectangle.width, clippedRectangle.height, 1,
2357 memoryBuffer->data(), outputRowPitch, 0, stagingPointer,
2358 outputRowPitch, 0);
2359 }
2360 else
2361 {
2362 // We read directly from the framebuffer into our pixel buffer.
2363 ANGLE_TRY(framebufferVk->readPixelsImpl(
2364 contextVk, clippedRectangle, params, VK_IMAGE_ASPECT_COLOR_BIT,
2365 framebufferVk->getColorReadRenderTarget(), stagingPointer));
2366 }
2367
2368 // 3- enqueue the destination image subresource update
2369 VkBufferImageCopy copyToImage = {};
2370 copyToImage.bufferOffset = static_cast<VkDeviceSize>(stagingOffset);
2371 copyToImage.bufferRowLength = 0; // Tightly packed data can be specified as 0.
2372 copyToImage.bufferImageHeight = clippedRectangle.height;
2373 copyToImage.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2374 copyToImage.imageSubresource.mipLevel = index.getLevelIndex();
2375 copyToImage.imageSubresource.baseArrayLayer = index.hasLayer() ? index.getLayerIndex() : 0;
2376 copyToImage.imageSubresource.layerCount = index.getLayerCount();
2377 gl_vk::GetOffset(dstOffset, ©ToImage.imageOffset);
2378 gl_vk::GetExtent(dstExtent, ©ToImage.imageExtent);
2379
2380 // 3- enqueue the destination image subresource update
2381 mSubresourceUpdates.emplace_back(bufferHandle, copyToImage);
2382 return angle::Result::Continue;
2383 }
2384
stageSubresourceUpdateFromImage(vk::ImageHelper * image,const gl::ImageIndex & index,const gl::Offset & destOffset,const gl::Extents & glExtents,const VkImageType imageType)2385 void ImageHelper::stageSubresourceUpdateFromImage(vk::ImageHelper *image,
2386 const gl::ImageIndex &index,
2387 const gl::Offset &destOffset,
2388 const gl::Extents &glExtents,
2389 const VkImageType imageType)
2390 {
2391 VkImageCopy copyToImage = {};
2392 copyToImage.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2393 copyToImage.srcSubresource.layerCount = index.getLayerCount();
2394 copyToImage.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2395 copyToImage.dstSubresource.mipLevel = index.getLevelIndex();
2396
2397 if (imageType == VK_IMAGE_TYPE_3D)
2398 {
2399 // These values must be set explicitly to follow the Vulkan spec:
2400 // https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkImageCopy.html
2401 // If either of the calling command’s srcImage or dstImage parameters are of VkImageType
2402 // VK_IMAGE_TYPE_3D, the baseArrayLayer and layerCount members of the corresponding
2403 // subresource must be 0 and 1, respectively
2404 copyToImage.dstSubresource.baseArrayLayer = 0;
2405 copyToImage.dstSubresource.layerCount = 1;
2406 // Preserve the assumption that destOffset.z == "dstSubresource.baseArrayLayer"
2407 ASSERT(destOffset.z == (index.hasLayer() ? index.getLayerIndex() : 0));
2408 }
2409 else
2410 {
2411 copyToImage.dstSubresource.baseArrayLayer = index.hasLayer() ? index.getLayerIndex() : 0;
2412 copyToImage.dstSubresource.layerCount = index.getLayerCount();
2413 }
2414
2415 gl_vk::GetOffset(destOffset, ©ToImage.dstOffset);
2416 gl_vk::GetExtent(glExtents, ©ToImage.extent);
2417
2418 mSubresourceUpdates.emplace_back(image, copyToImage);
2419 }
2420
stageSubresourceRobustClear(const gl::ImageIndex & index,const angle::Format & format)2421 void ImageHelper::stageSubresourceRobustClear(const gl::ImageIndex &index,
2422 const angle::Format &format)
2423 {
2424 stageSubresourceClear(index, format, kWebGLInitColorValue, kWebGLInitDepthStencilValue);
2425 }
2426
stageSubresourceEmulatedClear(const gl::ImageIndex & index,const angle::Format & format)2427 void ImageHelper::stageSubresourceEmulatedClear(const gl::ImageIndex &index,
2428 const angle::Format &format)
2429 {
2430 stageSubresourceClear(index, format, kEmulatedInitColorValue, kWebGLInitDepthStencilValue);
2431 }
2432
stageClearIfEmulatedFormat(const gl::ImageIndex & index,const Format & format)2433 void ImageHelper::stageClearIfEmulatedFormat(const gl::ImageIndex &index, const Format &format)
2434 {
2435 if (format.hasEmulatedImageChannels())
2436 {
2437 stageSubresourceEmulatedClear(index, format.angleFormat());
2438 }
2439 }
2440
stageSubresourceClear(const gl::ImageIndex & index,const angle::Format & format,const VkClearColorValue & colorValue,const VkClearDepthStencilValue & depthStencilValue)2441 void ImageHelper::stageSubresourceClear(const gl::ImageIndex &index,
2442 const angle::Format &format,
2443 const VkClearColorValue &colorValue,
2444 const VkClearDepthStencilValue &depthStencilValue)
2445 {
2446 VkClearValue clearValue;
2447
2448 bool isDepthStencil = format.depthBits > 0 || format.stencilBits > 0;
2449 if (isDepthStencil)
2450 {
2451 clearValue.depthStencil = depthStencilValue;
2452 }
2453 else
2454 {
2455 clearValue.color = colorValue;
2456 }
2457
2458 // Note that clears can arrive out of order from the front-end with respect to staged changes,
2459 // but they are intended to be done first.
2460 mSubresourceUpdates.emplace(mSubresourceUpdates.begin(), clearValue, index);
2461 }
2462
allocateStagingMemory(ContextVk * contextVk,size_t sizeInBytes,uint8_t ** ptrOut,VkBuffer * handleOut,VkDeviceSize * offsetOut,bool * newBufferAllocatedOut)2463 angle::Result ImageHelper::allocateStagingMemory(ContextVk *contextVk,
2464 size_t sizeInBytes,
2465 uint8_t **ptrOut,
2466 VkBuffer *handleOut,
2467 VkDeviceSize *offsetOut,
2468 bool *newBufferAllocatedOut)
2469 {
2470 return mStagingBuffer.allocate(contextVk, sizeInBytes, ptrOut, handleOut, offsetOut,
2471 newBufferAllocatedOut);
2472 }
2473
flushStagedUpdates(ContextVk * contextVk,uint32_t levelStart,uint32_t levelEnd,uint32_t layerStart,uint32_t layerEnd,vk::CommandBuffer * commandBuffer)2474 angle::Result ImageHelper::flushStagedUpdates(ContextVk *contextVk,
2475 uint32_t levelStart,
2476 uint32_t levelEnd,
2477 uint32_t layerStart,
2478 uint32_t layerEnd,
2479 vk::CommandBuffer *commandBuffer)
2480 {
2481 if (mSubresourceUpdates.empty())
2482 {
2483 return angle::Result::Continue;
2484 }
2485
2486 ANGLE_TRY(mStagingBuffer.flush(contextVk));
2487
2488 std::vector<SubresourceUpdate> updatesToKeep;
2489 const VkImageAspectFlags aspectFlags = GetFormatAspectFlags(mFormat->imageFormat());
2490
2491 // Upload levels and layers that don't conflict in parallel. The (level, layer) pair is hashed
2492 // to `(level * mLayerCount + layer) % 64` and used to track whether that subresource is
2493 // currently in transfer. If so, a barrier is inserted. If mLayerCount * mLevelCount > 64,
2494 // there will be a few unnecessary barriers.
2495 constexpr uint32_t kMaxParallelSubresourceUpload = 64;
2496 uint64_t subresourceUploadsInProgress = 0;
2497
2498 // Start in TransferDst.
2499 changeLayout(aspectFlags, vk::ImageLayout::TransferDst, commandBuffer);
2500
2501 for (SubresourceUpdate &update : mSubresourceUpdates)
2502 {
2503 ASSERT(update.updateSource == SubresourceUpdate::UpdateSource::Clear ||
2504 (update.updateSource == SubresourceUpdate::UpdateSource::Buffer &&
2505 update.buffer.bufferHandle != VK_NULL_HANDLE) ||
2506 (update.updateSource == SubresourceUpdate::UpdateSource::Image &&
2507 update.image.image != nullptr && update.image.image->valid()));
2508
2509 uint32_t updateMipLevel;
2510 uint32_t updateBaseLayer;
2511 uint32_t updateLayerCount;
2512 if (update.updateSource == SubresourceUpdate::UpdateSource::Clear)
2513 {
2514 updateMipLevel = update.clear.levelIndex;
2515 updateBaseLayer = update.clear.layerIndex;
2516 updateLayerCount = update.clear.layerCount;
2517 if (updateLayerCount == static_cast<uint32_t>(gl::ImageIndex::kEntireLevel))
2518 {
2519 updateLayerCount = mLayerCount;
2520 }
2521 }
2522 else
2523 {
2524 const VkImageSubresourceLayers &dstSubresource = update.dstSubresource();
2525 updateMipLevel = dstSubresource.mipLevel;
2526 updateBaseLayer = dstSubresource.baseArrayLayer;
2527 updateLayerCount = dstSubresource.layerCount;
2528 ASSERT(updateLayerCount != static_cast<uint32_t>(gl::ImageIndex::kEntireLevel));
2529 }
2530
2531 // If the update level is not within the requested range, skip the update.
2532 const bool isUpdateLevelOutsideRange =
2533 updateMipLevel < levelStart || updateMipLevel >= levelEnd;
2534 // If the update layers don't intersect the requested layers, skip the update.
2535 const bool areUpdateLayersOutsideRange =
2536 updateBaseLayer + updateLayerCount <= layerStart || updateBaseLayer >= layerEnd;
2537
2538 if (isUpdateLevelOutsideRange || areUpdateLayersOutsideRange)
2539 {
2540 updatesToKeep.emplace_back(update);
2541 continue;
2542 }
2543
2544 if (updateLayerCount >= kMaxParallelSubresourceUpload)
2545 {
2546 // If there are more subresources than bits we can track, always insert a barrier.
2547 changeLayout(aspectFlags, vk::ImageLayout::TransferDst, commandBuffer);
2548 subresourceUploadsInProgress = std::numeric_limits<uint64_t>::max();
2549 }
2550 else
2551 {
2552 const uint64_t subresourceHashRange = angle::Bit<uint64_t>(updateLayerCount) - 1;
2553 const uint32_t subresourceHashOffset =
2554 (updateMipLevel * mLayerCount + updateBaseLayer) % kMaxParallelSubresourceUpload;
2555 const uint64_t subresourceHash =
2556 ANGLE_ROTL64(subresourceHashRange, subresourceHashOffset);
2557
2558 if ((subresourceUploadsInProgress & subresourceHash) != 0)
2559 {
2560 // If there's overlap in subresource upload, issue a barrier.
2561 changeLayout(aspectFlags, vk::ImageLayout::TransferDst, commandBuffer);
2562 subresourceUploadsInProgress = 0;
2563 }
2564 subresourceUploadsInProgress |= subresourceHash;
2565 }
2566
2567 if (update.updateSource == SubresourceUpdate::UpdateSource::Clear)
2568 {
2569 clear(update.clear.value, updateMipLevel, updateBaseLayer, updateLayerCount,
2570 commandBuffer);
2571 }
2572 else if (update.updateSource == SubresourceUpdate::UpdateSource::Buffer)
2573 {
2574 commandBuffer->copyBufferToImage(update.buffer.bufferHandle, mImage, getCurrentLayout(),
2575 1, &update.buffer.copyRegion);
2576 }
2577 else
2578 {
2579 update.image.image->changeLayout(aspectFlags, vk::ImageLayout::TransferSrc,
2580 commandBuffer);
2581
2582 update.image.image->addReadDependency(this);
2583
2584 commandBuffer->copyImage(update.image.image->getImage(),
2585 update.image.image->getCurrentLayout(), mImage,
2586 getCurrentLayout(), 1, &update.image.copyRegion);
2587 }
2588
2589 update.release(contextVk);
2590 }
2591
2592 // Only remove the updates that were actually applied to the image.
2593 mSubresourceUpdates = std::move(updatesToKeep);
2594
2595 if (mSubresourceUpdates.empty())
2596 {
2597 mStagingBuffer.releaseInFlightBuffers(contextVk);
2598 }
2599
2600 return angle::Result::Continue;
2601 }
2602
flushAllStagedUpdates(ContextVk * contextVk)2603 angle::Result ImageHelper::flushAllStagedUpdates(ContextVk *contextVk)
2604 {
2605 // Clear the image.
2606 vk::CommandBuffer *commandBuffer = nullptr;
2607 ANGLE_TRY(recordCommands(contextVk, &commandBuffer));
2608 return flushStagedUpdates(contextVk, 0, mLevelCount, 0, mLayerCount, commandBuffer);
2609 }
2610
2611 // ImageHelper::SubresourceUpdate implementation
SubresourceUpdate()2612 ImageHelper::SubresourceUpdate::SubresourceUpdate()
2613 : updateSource(UpdateSource::Buffer), buffer{VK_NULL_HANDLE}
2614 {}
2615
SubresourceUpdate(VkBuffer bufferHandleIn,const VkBufferImageCopy & copyRegionIn)2616 ImageHelper::SubresourceUpdate::SubresourceUpdate(VkBuffer bufferHandleIn,
2617 const VkBufferImageCopy ©RegionIn)
2618 : updateSource(UpdateSource::Buffer), buffer{bufferHandleIn, copyRegionIn}
2619 {}
2620
SubresourceUpdate(vk::ImageHelper * imageIn,const VkImageCopy & copyRegionIn)2621 ImageHelper::SubresourceUpdate::SubresourceUpdate(vk::ImageHelper *imageIn,
2622 const VkImageCopy ©RegionIn)
2623 : updateSource(UpdateSource::Image), image{imageIn, copyRegionIn}
2624 {}
2625
SubresourceUpdate(const VkClearValue & clearValue,const gl::ImageIndex & imageIndex)2626 ImageHelper::SubresourceUpdate::SubresourceUpdate(const VkClearValue &clearValue,
2627 const gl::ImageIndex &imageIndex)
2628 : updateSource(UpdateSource::Clear)
2629 {
2630 clear.value = clearValue;
2631 clear.levelIndex = imageIndex.getLevelIndex();
2632 clear.layerIndex = imageIndex.hasLayer() ? imageIndex.getLayerIndex() : 0;
2633 clear.layerCount = imageIndex.getLayerCount();
2634 }
2635
SubresourceUpdate(const SubresourceUpdate & other)2636 ImageHelper::SubresourceUpdate::SubresourceUpdate(const SubresourceUpdate &other)
2637 : updateSource(other.updateSource)
2638 {
2639 if (updateSource == UpdateSource::Clear)
2640 {
2641 clear = other.clear;
2642 }
2643 else if (updateSource == UpdateSource::Buffer)
2644 {
2645 buffer = other.buffer;
2646 }
2647 else
2648 {
2649 image = other.image;
2650 }
2651 }
2652
release(ContextVk * contextVk)2653 void ImageHelper::SubresourceUpdate::release(ContextVk *contextVk)
2654 {
2655 if (updateSource == UpdateSource::Image)
2656 {
2657 image.image->releaseImage(contextVk);
2658 image.image->releaseStagingBuffer(contextVk);
2659 SafeDelete(image.image);
2660 }
2661 }
2662
release(DisplayVk * display,std::vector<GarbageObjectBase> * garbageQueue)2663 void ImageHelper::SubresourceUpdate::release(DisplayVk *display,
2664 std::vector<GarbageObjectBase> *garbageQueue)
2665 {
2666 if (updateSource == UpdateSource::Image)
2667 {
2668 image.image->releaseImage(display, garbageQueue);
2669 image.image->releaseStagingBuffer(display, garbageQueue);
2670 SafeDelete(image.image);
2671 }
2672 }
2673
isUpdateToLayerLevel(uint32_t layerIndex,uint32_t levelIndex) const2674 bool ImageHelper::SubresourceUpdate::isUpdateToLayerLevel(uint32_t layerIndex,
2675 uint32_t levelIndex) const
2676 {
2677 if (updateSource == UpdateSource::Clear)
2678 {
2679 return clear.levelIndex == levelIndex && clear.layerIndex == layerIndex;
2680 }
2681
2682 const VkImageSubresourceLayers &dst = dstSubresource();
2683 return dst.baseArrayLayer == layerIndex && dst.mipLevel == levelIndex;
2684 }
2685
2686 // FramebufferHelper implementation.
FramebufferHelper()2687 FramebufferHelper::FramebufferHelper() : CommandGraphResource(CommandGraphResourceType::Framebuffer)
2688 {}
2689
2690 FramebufferHelper::~FramebufferHelper() = default;
2691
init(ContextVk * contextVk,const VkFramebufferCreateInfo & createInfo)2692 angle::Result FramebufferHelper::init(ContextVk *contextVk,
2693 const VkFramebufferCreateInfo &createInfo)
2694 {
2695 ANGLE_VK_TRY(contextVk, mFramebuffer.init(contextVk->getDevice(), createInfo));
2696 return angle::Result::Continue;
2697 }
2698
release(ContextVk * contextVk)2699 void FramebufferHelper::release(ContextVk *contextVk)
2700 {
2701 contextVk->releaseObject(getStoredQueueSerial(), &mFramebuffer);
2702 }
2703
2704 // FramebufferHelper implementation.
DispatchHelper()2705 DispatchHelper::DispatchHelper() : CommandGraphResource(CommandGraphResourceType::Dispatcher) {}
2706
2707 DispatchHelper::~DispatchHelper() = default;
2708
2709 // ShaderProgramHelper implementation.
2710 ShaderProgramHelper::ShaderProgramHelper() = default;
2711
2712 ShaderProgramHelper::~ShaderProgramHelper() = default;
2713
valid() const2714 bool ShaderProgramHelper::valid() const
2715 {
2716 // This will need to be extended for compute shader support.
2717 return mShaders[gl::ShaderType::Vertex].valid();
2718 }
2719
destroy(VkDevice device)2720 void ShaderProgramHelper::destroy(VkDevice device)
2721 {
2722 mGraphicsPipelines.destroy(device);
2723 mComputePipeline.destroy(device);
2724 for (BindingPointer<ShaderAndSerial> &shader : mShaders)
2725 {
2726 shader.reset();
2727 }
2728 }
2729
release(ContextVk * contextVk)2730 void ShaderProgramHelper::release(ContextVk *contextVk)
2731 {
2732 mGraphicsPipelines.release(contextVk);
2733 contextVk->releaseObject(mComputePipeline.getSerial(), &mComputePipeline.get());
2734 for (BindingPointer<ShaderAndSerial> &shader : mShaders)
2735 {
2736 shader.reset();
2737 }
2738 }
2739
setShader(gl::ShaderType shaderType,RefCounted<ShaderAndSerial> * shader)2740 void ShaderProgramHelper::setShader(gl::ShaderType shaderType, RefCounted<ShaderAndSerial> *shader)
2741 {
2742 mShaders[shaderType].set(shader);
2743 }
2744
getComputePipeline(Context * context,const PipelineLayout & pipelineLayout,PipelineAndSerial ** pipelineOut)2745 angle::Result ShaderProgramHelper::getComputePipeline(Context *context,
2746 const PipelineLayout &pipelineLayout,
2747 PipelineAndSerial **pipelineOut)
2748 {
2749 if (mComputePipeline.valid())
2750 {
2751 *pipelineOut = &mComputePipeline;
2752 return angle::Result::Continue;
2753 }
2754
2755 RendererVk *renderer = context->getRenderer();
2756
2757 VkPipelineShaderStageCreateInfo shaderStage = {};
2758 VkComputePipelineCreateInfo createInfo = {};
2759
2760 shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
2761 shaderStage.flags = 0;
2762 shaderStage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
2763 shaderStage.module = mShaders[gl::ShaderType::Compute].get().get().getHandle();
2764 shaderStage.pName = "main";
2765 shaderStage.pSpecializationInfo = nullptr;
2766
2767 createInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
2768 createInfo.flags = 0;
2769 createInfo.stage = shaderStage;
2770 createInfo.layout = pipelineLayout.getHandle();
2771 createInfo.basePipelineHandle = VK_NULL_HANDLE;
2772 createInfo.basePipelineIndex = 0;
2773
2774 vk::PipelineCache *pipelineCache = nullptr;
2775 ANGLE_TRY(renderer->getPipelineCache(&pipelineCache));
2776 ANGLE_VK_TRY(context, mComputePipeline.get().initCompute(context->getDevice(), createInfo,
2777 *pipelineCache));
2778
2779 *pipelineOut = &mComputePipeline;
2780 return angle::Result::Continue;
2781 }
2782
2783 } // namespace vk
2784 } // namespace rx
2785