• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2016 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // VertexArrayVk.cpp:
7 //    Implements the class methods for VertexArrayVk.
8 //
9 
10 #include "libANGLE/renderer/vulkan/VertexArrayVk.h"
11 
12 #include "common/debug.h"
13 #include "common/utilities.h"
14 #include "libANGLE/Context.h"
15 #include "libANGLE/renderer/vulkan/BufferVk.h"
16 #include "libANGLE/renderer/vulkan/ContextVk.h"
17 #include "libANGLE/renderer/vulkan/FramebufferVk.h"
18 #include "libANGLE/renderer/vulkan/vk_format_utils.h"
19 #include "libANGLE/renderer/vulkan/vk_renderer.h"
20 #include "libANGLE/renderer/vulkan/vk_resource.h"
21 
22 namespace rx
23 {
24 namespace
25 {
26 constexpr int kStreamIndexBufferCachedIndexCount = 6;
27 constexpr int kMaxCachedStreamIndexBuffers       = 4;
28 constexpr size_t kDefaultValueSize               = sizeof(gl::VertexAttribCurrentValueData::Values);
29 
BindingIsAligned(const gl::VertexBinding & binding,const angle::Format & angleFormat,unsigned int attribSize,GLuint relativeOffset)30 ANGLE_INLINE bool BindingIsAligned(const gl::VertexBinding &binding,
31                                    const angle::Format &angleFormat,
32                                    unsigned int attribSize,
33                                    GLuint relativeOffset)
34 {
35     GLintptr totalOffset = binding.getOffset() + relativeOffset;
36     GLuint mask          = angleFormat.componentAlignmentMask;
37     if (mask != std::numeric_limits<GLuint>::max())
38     {
39         return ((totalOffset & mask) == 0 && (binding.getStride() & mask) == 0);
40     }
41     else
42     {
43         // To perform the GPU conversion for formats with components that aren't byte-aligned
44         // (for example, A2BGR10 or RGB10A2), one element has to be placed in 4 bytes to perform
45         // the compute shader. So, binding offset and stride has to be aligned to formatSize.
46         unsigned int formatSize = angleFormat.pixelBytes;
47         return (totalOffset % formatSize == 0) && (binding.getStride() % formatSize == 0);
48     }
49 }
50 
ClientBindingAligned(const gl::VertexAttribute & attrib,GLuint stride,size_t alignment)51 ANGLE_INLINE bool ClientBindingAligned(const gl::VertexAttribute &attrib,
52                                        GLuint stride,
53                                        size_t alignment)
54 {
55     return reinterpret_cast<intptr_t>(attrib.pointer) % alignment == 0 && stride % alignment == 0;
56 }
57 
ShouldCombineAttributes(vk::Renderer * renderer,const gl::VertexAttribute & attrib,const gl::VertexBinding & binding)58 bool ShouldCombineAttributes(vk::Renderer *renderer,
59                              const gl::VertexAttribute &attrib,
60                              const gl::VertexBinding &binding)
61 {
62     if (!renderer->getFeatures().enableMergeClientAttribBuffer.enabled)
63     {
64         return false;
65     }
66     const vk::Format &vertexFormat = renderer->getFormat(attrib.format->id);
67     return !vertexFormat.getVertexLoadRequiresConversion(false) && binding.getDivisor() == 0 &&
68            ClientBindingAligned(attrib, binding.getStride(),
69                                 vertexFormat.getVertexInputAlignment(false));
70 }
71 
WarnOnVertexFormatConversion(ContextVk * contextVk,const vk::Format & vertexFormat,bool compressed,bool insertEventMarker)72 void WarnOnVertexFormatConversion(ContextVk *contextVk,
73                                   const vk::Format &vertexFormat,
74                                   bool compressed,
75                                   bool insertEventMarker)
76 {
77     if (!vertexFormat.getVertexLoadRequiresConversion(compressed))
78     {
79         return;
80     }
81 
82     ANGLE_VK_PERF_WARNING(
83         contextVk, GL_DEBUG_SEVERITY_LOW,
84         "The Vulkan driver does not support vertex attribute format 0x%04X, emulating with 0x%04X",
85         vertexFormat.getIntendedFormat().glInternalFormat,
86         vertexFormat.getActualBufferFormat(compressed).glInternalFormat);
87 }
88 
StreamVertexData(ContextVk * contextVk,vk::BufferHelper * dstBufferHelper,const uint8_t * srcData,size_t bytesToCopy,size_t dstOffset,size_t vertexCount,size_t srcStride,VertexCopyFunction vertexLoadFunction)89 angle::Result StreamVertexData(ContextVk *contextVk,
90                                vk::BufferHelper *dstBufferHelper,
91                                const uint8_t *srcData,
92                                size_t bytesToCopy,
93                                size_t dstOffset,
94                                size_t vertexCount,
95                                size_t srcStride,
96                                VertexCopyFunction vertexLoadFunction)
97 {
98     vk::Renderer *renderer = contextVk->getRenderer();
99 
100     uint8_t *dst = dstBufferHelper->getMappedMemory() + dstOffset;
101 
102     if (vertexLoadFunction != nullptr)
103     {
104         vertexLoadFunction(srcData, srcStride, vertexCount, dst);
105     }
106     else
107     {
108         memcpy(dst, srcData, bytesToCopy);
109     }
110 
111     ANGLE_TRY(dstBufferHelper->flush(renderer));
112 
113     return angle::Result::Continue;
114 }
115 
StreamVertexDataWithDivisor(ContextVk * contextVk,vk::BufferHelper * dstBufferHelper,const uint8_t * srcData,size_t bytesToAllocate,size_t srcStride,size_t dstStride,VertexCopyFunction vertexLoadFunction,uint32_t divisor,size_t numSrcVertices)116 angle::Result StreamVertexDataWithDivisor(ContextVk *contextVk,
117                                           vk::BufferHelper *dstBufferHelper,
118                                           const uint8_t *srcData,
119                                           size_t bytesToAllocate,
120                                           size_t srcStride,
121                                           size_t dstStride,
122                                           VertexCopyFunction vertexLoadFunction,
123                                           uint32_t divisor,
124                                           size_t numSrcVertices)
125 {
126     vk::Renderer *renderer = contextVk->getRenderer();
127 
128     uint8_t *dst = dstBufferHelper->getMappedMemory();
129 
130     // Each source vertex is used `divisor` times before advancing. Clamp to avoid OOB reads.
131     size_t clampedSize = std::min(numSrcVertices * dstStride * divisor, bytesToAllocate);
132 
133     ASSERT(clampedSize % dstStride == 0);
134     ASSERT(divisor > 0);
135 
136     uint32_t srcVertexUseCount = 0;
137     for (size_t dataCopied = 0; dataCopied < clampedSize; dataCopied += dstStride)
138     {
139         vertexLoadFunction(srcData, srcStride, 1, dst);
140         srcVertexUseCount++;
141         if (srcVertexUseCount == divisor)
142         {
143             srcData += srcStride;
144             srcVertexUseCount = 0;
145         }
146         dst += dstStride;
147     }
148 
149     // Satisfy robustness constraints (only if extension enabled)
150     if (contextVk->getExtensions().robustnessAny())
151     {
152         if (clampedSize < bytesToAllocate)
153         {
154             memset(dst, 0, bytesToAllocate - clampedSize);
155         }
156     }
157 
158     ANGLE_TRY(dstBufferHelper->flush(renderer));
159 
160     return angle::Result::Continue;
161 }
162 
GetVertexCount(BufferVk * srcBuffer,const gl::VertexBinding & binding,uint32_t srcFormatSize)163 size_t GetVertexCount(BufferVk *srcBuffer, const gl::VertexBinding &binding, uint32_t srcFormatSize)
164 {
165     // Bytes usable for vertex data.
166     GLint64 bytes = srcBuffer->getSize() - binding.getOffset();
167     if (bytes < srcFormatSize)
168     {
169         return 0;
170     }
171 
172     // Count the last vertex.  It may occupy less than a full stride.
173     // This is also correct if stride happens to be less than srcFormatSize.
174     size_t numVertices = 1;
175     bytes -= srcFormatSize;
176 
177     GLuint stride = binding.getStride();
178     if (stride == 0)
179     {
180         stride = srcFormatSize;
181     }
182 
183     // Count how many strides fit remaining space.
184     if (bytes > 0)
185     {
186         numVertices += static_cast<size_t>(bytes) / stride;
187     }
188 
189     return numVertices;
190 }
191 }  // anonymous namespace
192 
VertexArrayVk(ContextVk * contextVk,const gl::VertexArrayState & state)193 VertexArrayVk::VertexArrayVk(ContextVk *contextVk, const gl::VertexArrayState &state)
194     : VertexArrayImpl(state),
195       mCurrentArrayBufferHandles{},
196       mCurrentArrayBufferOffsets{},
197       mCurrentArrayBufferRelativeOffsets{},
198       mCurrentArrayBuffers{},
199       mCurrentArrayBufferStrides{},
200       mCurrentArrayBufferDivisors{},
201       mCurrentElementArrayBuffer(nullptr),
202       mLineLoopHelper(contextVk->getRenderer()),
203       mDirtyLineLoopTranslation(true)
204 {
205     vk::BufferHelper &emptyBuffer = contextVk->getEmptyBuffer();
206 
207     mCurrentArrayBufferHandles.fill(emptyBuffer.getBuffer().getHandle());
208     mCurrentArrayBufferOffsets.fill(0);
209     mCurrentArrayBufferRelativeOffsets.fill(0);
210     mCurrentArrayBuffers.fill(&emptyBuffer);
211     mCurrentArrayBufferStrides.fill(0);
212     mCurrentArrayBufferDivisors.fill(0);
213 
214     mBindingDirtyBitsRequiresPipelineUpdate.set(gl::VertexArray::DIRTY_BINDING_DIVISOR);
215     if (!contextVk->getRenderer()->useVertexInputBindingStrideDynamicState())
216     {
217         mBindingDirtyBitsRequiresPipelineUpdate.set(gl::VertexArray::DIRTY_BINDING_STRIDE);
218     }
219 
220     // All but DIRTY_ATTRIB_POINTER_BUFFER requires graphics pipeline update
221     mAttribDirtyBitsRequiresPipelineUpdate.set(gl::VertexArray::DIRTY_ATTRIB_ENABLED);
222     mAttribDirtyBitsRequiresPipelineUpdate.set(gl::VertexArray::DIRTY_ATTRIB_POINTER);
223     mAttribDirtyBitsRequiresPipelineUpdate.set(gl::VertexArray::DIRTY_ATTRIB_FORMAT);
224     mAttribDirtyBitsRequiresPipelineUpdate.set(gl::VertexArray::DIRTY_ATTRIB_BINDING);
225 }
226 
~VertexArrayVk()227 VertexArrayVk::~VertexArrayVk() {}
228 
destroy(const gl::Context * context)229 void VertexArrayVk::destroy(const gl::Context *context)
230 {
231     ContextVk *contextVk = vk::GetImpl(context);
232 
233     vk::Renderer *renderer = contextVk->getRenderer();
234 
235     for (std::unique_ptr<vk::BufferHelper> &buffer : mCachedStreamIndexBuffers)
236     {
237         buffer->release(renderer);
238     }
239 
240     mStreamedIndexData.release(renderer);
241     mTranslatedByteIndexData.release(renderer);
242     mTranslatedByteIndirectData.release(renderer);
243     mLineLoopHelper.release(contextVk);
244 }
245 
convertIndexBufferGPU(ContextVk * contextVk,BufferVk * bufferVk,const void * indices)246 angle::Result VertexArrayVk::convertIndexBufferGPU(ContextVk *contextVk,
247                                                    BufferVk *bufferVk,
248                                                    const void *indices)
249 {
250     intptr_t offsetIntoSrcData = reinterpret_cast<intptr_t>(indices);
251     size_t srcDataSize         = static_cast<size_t>(bufferVk->getSize()) - offsetIntoSrcData;
252 
253     // Allocate buffer for results
254     ANGLE_TRY(contextVk->initBufferForVertexConversion(&mTranslatedByteIndexData,
255                                                        sizeof(GLushort) * srcDataSize,
256                                                        vk::MemoryHostVisibility::NonVisible));
257     mCurrentElementArrayBuffer = &mTranslatedByteIndexData;
258 
259     vk::BufferHelper *dst = &mTranslatedByteIndexData;
260     vk::BufferHelper *src = &bufferVk->getBuffer();
261 
262     // Copy relevant section of the source into destination at allocated offset.  Note that the
263     // offset returned by allocate() above is in bytes. As is the indices offset pointer.
264     UtilsVk::ConvertIndexParameters params = {};
265     params.srcOffset                       = static_cast<uint32_t>(offsetIntoSrcData);
266     params.dstOffset                       = 0;
267     params.maxIndex                        = static_cast<uint32_t>(bufferVk->getSize());
268 
269     return contextVk->getUtils().convertIndexBuffer(contextVk, dst, src, params);
270 }
271 
convertIndexBufferIndirectGPU(ContextVk * contextVk,vk::BufferHelper * srcIndirectBuf,VkDeviceSize srcIndirectBufOffset,vk::BufferHelper ** indirectBufferVkOut)272 angle::Result VertexArrayVk::convertIndexBufferIndirectGPU(ContextVk *contextVk,
273                                                            vk::BufferHelper *srcIndirectBuf,
274                                                            VkDeviceSize srcIndirectBufOffset,
275                                                            vk::BufferHelper **indirectBufferVkOut)
276 {
277     size_t srcDataSize = static_cast<size_t>(mCurrentElementArrayBuffer->getSize());
278     ASSERT(mCurrentElementArrayBuffer ==
279            &vk::GetImpl(getState().getElementArrayBuffer())->getBuffer());
280 
281     vk::BufferHelper *srcIndexBuf = mCurrentElementArrayBuffer;
282 
283     // Allocate buffer for results
284     ANGLE_TRY(contextVk->initBufferForVertexConversion(&mTranslatedByteIndexData,
285                                                        sizeof(GLushort) * srcDataSize,
286                                                        vk::MemoryHostVisibility::NonVisible));
287     vk::BufferHelper *dstIndexBuf = &mTranslatedByteIndexData;
288 
289     ANGLE_TRY(contextVk->initBufferForVertexConversion(&mTranslatedByteIndirectData,
290                                                        sizeof(VkDrawIndexedIndirectCommand),
291                                                        vk::MemoryHostVisibility::NonVisible));
292     vk::BufferHelper *dstIndirectBuf = &mTranslatedByteIndirectData;
293 
294     // Save new element array buffer
295     mCurrentElementArrayBuffer = dstIndexBuf;
296     // Tell caller what new indirect buffer is
297     *indirectBufferVkOut = dstIndirectBuf;
298 
299     // Copy relevant section of the source into destination at allocated offset.  Note that the
300     // offset returned by allocate() above is in bytes. As is the indices offset pointer.
301     UtilsVk::ConvertIndexIndirectParameters params = {};
302     params.srcIndirectBufOffset                    = static_cast<uint32_t>(srcIndirectBufOffset);
303     params.srcIndexBufOffset                       = 0;
304     params.dstIndexBufOffset                       = 0;
305     params.maxIndex                                = static_cast<uint32_t>(srcDataSize);
306     params.dstIndirectBufOffset                    = 0;
307 
308     return contextVk->getUtils().convertIndexIndirectBuffer(contextVk, srcIndirectBuf, srcIndexBuf,
309                                                             dstIndirectBuf, dstIndexBuf, params);
310 }
311 
handleLineLoopIndexIndirect(ContextVk * contextVk,gl::DrawElementsType glIndexType,vk::BufferHelper * srcIndirectBuf,VkDeviceSize indirectBufferOffset,vk::BufferHelper ** indirectBufferOut)312 angle::Result VertexArrayVk::handleLineLoopIndexIndirect(ContextVk *contextVk,
313                                                          gl::DrawElementsType glIndexType,
314                                                          vk::BufferHelper *srcIndirectBuf,
315                                                          VkDeviceSize indirectBufferOffset,
316                                                          vk::BufferHelper **indirectBufferOut)
317 {
318     ANGLE_TRY(mLineLoopHelper.streamIndicesIndirect(
319         contextVk, glIndexType, mCurrentElementArrayBuffer, srcIndirectBuf, indirectBufferOffset,
320         &mCurrentElementArrayBuffer, indirectBufferOut));
321 
322     return angle::Result::Continue;
323 }
324 
handleLineLoopIndirectDraw(const gl::Context * context,vk::BufferHelper * indirectBufferVk,VkDeviceSize indirectBufferOffset,vk::BufferHelper ** indirectBufferOut)325 angle::Result VertexArrayVk::handleLineLoopIndirectDraw(const gl::Context *context,
326                                                         vk::BufferHelper *indirectBufferVk,
327                                                         VkDeviceSize indirectBufferOffset,
328                                                         vk::BufferHelper **indirectBufferOut)
329 {
330     size_t maxVertexCount = 0;
331     ContextVk *contextVk  = vk::GetImpl(context);
332     const gl::AttributesMask activeAttribs =
333         context->getStateCache().getActiveBufferedAttribsMask();
334 
335     const auto &attribs  = mState.getVertexAttributes();
336     const auto &bindings = mState.getVertexBindings();
337 
338     for (size_t attribIndex : activeAttribs)
339     {
340         const gl::VertexAttribute &attrib = attribs[attribIndex];
341         ASSERT(attrib.enabled);
342         VkDeviceSize bufSize             = getCurrentArrayBuffers()[attribIndex]->getSize();
343         const gl::VertexBinding &binding = bindings[attrib.bindingIndex];
344         size_t stride                    = binding.getStride();
345         size_t vertexCount               = static_cast<size_t>(bufSize / stride);
346         if (vertexCount > maxVertexCount)
347         {
348             maxVertexCount = vertexCount;
349         }
350     }
351     ANGLE_TRY(mLineLoopHelper.streamArrayIndirect(contextVk, maxVertexCount + 1, indirectBufferVk,
352                                                   indirectBufferOffset, &mCurrentElementArrayBuffer,
353                                                   indirectBufferOut));
354 
355     return angle::Result::Continue;
356 }
357 
convertIndexBufferCPU(ContextVk * contextVk,gl::DrawElementsType indexType,size_t indexCount,const void * sourcePointer,BufferBindingDirty * bindingDirty)358 angle::Result VertexArrayVk::convertIndexBufferCPU(ContextVk *contextVk,
359                                                    gl::DrawElementsType indexType,
360                                                    size_t indexCount,
361                                                    const void *sourcePointer,
362                                                    BufferBindingDirty *bindingDirty)
363 {
364     ASSERT(!mState.getElementArrayBuffer() || indexType == gl::DrawElementsType::UnsignedByte);
365     vk::Renderer *renderer = contextVk->getRenderer();
366     size_t elementSize     = contextVk->getVkIndexTypeSize(indexType);
367     const size_t amount    = elementSize * indexCount;
368 
369     // Applications often time draw a quad with two triangles. This is try to catch all the
370     // common used element array buffer with pre-created BufferHelper objects to improve
371     // performance.
372     if (indexCount == kStreamIndexBufferCachedIndexCount &&
373         indexType == gl::DrawElementsType::UnsignedShort)
374     {
375         for (std::unique_ptr<vk::BufferHelper> &buffer : mCachedStreamIndexBuffers)
376         {
377             void *ptr = buffer->getMappedMemory();
378             if (memcmp(sourcePointer, ptr, amount) == 0)
379             {
380                 // Found a matching cached buffer, use the cached internal index buffer.
381                 *bindingDirty              = mCurrentElementArrayBuffer == buffer.get()
382                                                  ? BufferBindingDirty::No
383                                                  : BufferBindingDirty::Yes;
384                 mCurrentElementArrayBuffer = buffer.get();
385                 return angle::Result::Continue;
386             }
387         }
388 
389         // If we still have capacity, cache this index buffer for future use.
390         if (mCachedStreamIndexBuffers.size() < kMaxCachedStreamIndexBuffers)
391         {
392             std::unique_ptr<vk::BufferHelper> buffer = std::make_unique<vk::BufferHelper>();
393             ANGLE_TRY(contextVk->initBufferAllocation(
394                 buffer.get(),
395                 renderer->getVertexConversionBufferMemoryTypeIndex(
396                     vk::MemoryHostVisibility::Visible),
397                 amount, renderer->getVertexConversionBufferAlignment(), BufferUsageType::Static));
398             memcpy(buffer->getMappedMemory(), sourcePointer, amount);
399             ANGLE_TRY(buffer->flush(renderer));
400 
401             mCachedStreamIndexBuffers.push_back(std::move(buffer));
402 
403             *bindingDirty              = BufferBindingDirty::Yes;
404             mCurrentElementArrayBuffer = mCachedStreamIndexBuffers.back().get();
405             return angle::Result::Continue;
406         }
407     }
408 
409     ANGLE_TRY(contextVk->initBufferForVertexConversion(&mStreamedIndexData, amount,
410                                                        vk::MemoryHostVisibility::Visible));
411     GLubyte *dst = mStreamedIndexData.getMappedMemory();
412 
413     *bindingDirty              = BufferBindingDirty::Yes;
414     mCurrentElementArrayBuffer = &mStreamedIndexData;
415 
416     if (contextVk->shouldConvertUint8VkIndexType(indexType))
417     {
418         // Unsigned bytes don't have direct support in Vulkan so we have to expand the
419         // memory to a GLushort.
420         const GLubyte *in     = static_cast<const GLubyte *>(sourcePointer);
421         GLushort *expandedDst = reinterpret_cast<GLushort *>(dst);
422         bool primitiveRestart = contextVk->getState().isPrimitiveRestartEnabled();
423 
424         constexpr GLubyte kUnsignedByteRestartValue   = 0xFF;
425         constexpr GLushort kUnsignedShortRestartValue = 0xFFFF;
426 
427         if (primitiveRestart)
428         {
429             for (size_t index = 0; index < indexCount; index++)
430             {
431                 GLushort value = static_cast<GLushort>(in[index]);
432                 if (in[index] == kUnsignedByteRestartValue)
433                 {
434                     // Convert from 8-bit restart value to 16-bit restart value
435                     value = kUnsignedShortRestartValue;
436                 }
437                 expandedDst[index] = value;
438             }
439         }
440         else
441         {
442             // Fast path for common case.
443             for (size_t index = 0; index < indexCount; index++)
444             {
445                 expandedDst[index] = static_cast<GLushort>(in[index]);
446             }
447         }
448     }
449     else
450     {
451         // The primitive restart value is the same for OpenGL and Vulkan,
452         // so there's no need to perform any conversion.
453         memcpy(dst, sourcePointer, amount);
454     }
455     return mStreamedIndexData.flush(contextVk->getRenderer());
456 }
457 
458 // We assume the buffer is completely full of the same kind of data and convert
459 // and/or align it as we copy it to a buffer. The assumption could be wrong
460 // but the alternative of copying it piecemeal on each draw would have a lot more
461 // overhead.
convertVertexBufferGPU(ContextVk * contextVk,BufferVk * srcBuffer,const gl::VertexBinding & binding,size_t attribIndex,const vk::Format & vertexFormat,ConversionBuffer * conversion,GLuint relativeOffset,bool compressed)462 angle::Result VertexArrayVk::convertVertexBufferGPU(ContextVk *contextVk,
463                                                     BufferVk *srcBuffer,
464                                                     const gl::VertexBinding &binding,
465                                                     size_t attribIndex,
466                                                     const vk::Format &vertexFormat,
467                                                     ConversionBuffer *conversion,
468                                                     GLuint relativeOffset,
469                                                     bool compressed)
470 {
471     const angle::Format &srcFormat = vertexFormat.getIntendedFormat();
472     const angle::Format &dstFormat = vertexFormat.getActualBufferFormat(compressed);
473 
474     ASSERT(binding.getStride() % (srcFormat.pixelBytes / srcFormat.channelCount) == 0);
475 
476     unsigned srcFormatSize = srcFormat.pixelBytes;
477     unsigned dstFormatSize = dstFormat.pixelBytes;
478 
479     size_t numVertices = GetVertexCount(srcBuffer, binding, srcFormatSize);
480     if (numVertices == 0)
481     {
482         return angle::Result::Continue;
483     }
484     ASSERT(vertexFormat.getVertexInputAlignment(compressed) <= vk::kVertexBufferAlignment);
485 
486     // Allocate buffer for results
487     vk::BufferHelper *dstBuffer = conversion->data.get();
488     ANGLE_TRY(contextVk->initBufferForVertexConversion(dstBuffer, numVertices * dstFormatSize,
489                                                        vk::MemoryHostVisibility::NonVisible));
490 
491     ASSERT(conversion->dirty);
492     conversion->dirty = false;
493 
494     vk::BufferHelper *srcBufferHelper = &srcBuffer->getBuffer();
495 
496     UtilsVk::ConvertVertexParameters params;
497     params.vertexCount = numVertices;
498     params.srcFormat   = &srcFormat;
499     params.dstFormat   = &dstFormat;
500     params.srcStride   = binding.getStride();
501     params.srcOffset   = binding.getOffset() + relativeOffset;
502     params.dstOffset   = 0;
503 
504     ANGLE_TRY(
505         contextVk->getUtils().convertVertexBuffer(contextVk, dstBuffer, srcBufferHelper, params));
506 
507     return angle::Result::Continue;
508 }
509 
convertVertexBufferCPU(ContextVk * contextVk,BufferVk * srcBuffer,const gl::VertexBinding & binding,size_t attribIndex,const vk::Format & vertexFormat,ConversionBuffer * conversion,GLuint relativeOffset,bool compressed)510 angle::Result VertexArrayVk::convertVertexBufferCPU(ContextVk *contextVk,
511                                                     BufferVk *srcBuffer,
512                                                     const gl::VertexBinding &binding,
513                                                     size_t attribIndex,
514                                                     const vk::Format &vertexFormat,
515                                                     ConversionBuffer *conversion,
516                                                     GLuint relativeOffset,
517                                                     bool compressed)
518 {
519     ANGLE_TRACE_EVENT0("gpu.angle", "VertexArrayVk::convertVertexBufferCpu");
520 
521     unsigned srcFormatSize = vertexFormat.getIntendedFormat().pixelBytes;
522     unsigned dstFormatSize = vertexFormat.getActualBufferFormat(compressed).pixelBytes;
523 
524     size_t numVertices = GetVertexCount(srcBuffer, binding, srcFormatSize);
525     if (numVertices == 0)
526     {
527         return angle::Result::Continue;
528     }
529 
530     void *src = nullptr;
531     ANGLE_TRY(srcBuffer->mapImpl(contextVk, GL_MAP_READ_BIT, &src));
532     const uint8_t *srcBytes = reinterpret_cast<const uint8_t *>(src);
533     srcBytes += binding.getOffset() + relativeOffset;
534     ASSERT(vertexFormat.getVertexInputAlignment(compressed) <= vk::kVertexBufferAlignment);
535 
536     vk::BufferHelper *dstBufferHelper = conversion->data.get();
537     // Allocate buffer for results
538     ANGLE_TRY(contextVk->initBufferForVertexConversion(dstBufferHelper, numVertices * dstFormatSize,
539                                                        vk::MemoryHostVisibility::Visible));
540 
541     ANGLE_TRY(StreamVertexData(contextVk, dstBufferHelper, srcBytes, numVertices * dstFormatSize, 0,
542                                numVertices, binding.getStride(),
543                                vertexFormat.getVertexLoadFunction(compressed)));
544     ANGLE_TRY(srcBuffer->unmapImpl(contextVk));
545     mCurrentArrayBuffers[attribIndex]      = dstBufferHelper;
546     mCurrentArrayBufferSerial[attribIndex] = dstBufferHelper->getBufferSerial();
547 
548     ASSERT(conversion->dirty);
549     conversion->dirty = false;
550 
551     return angle::Result::Continue;
552 }
553 
updateCurrentElementArrayBuffer()554 void VertexArrayVk::updateCurrentElementArrayBuffer()
555 {
556     ASSERT(mState.getElementArrayBuffer() != nullptr);
557     ASSERT(mState.getElementArrayBuffer()->getSize() > 0);
558 
559     BufferVk *bufferVk         = vk::GetImpl(mState.getElementArrayBuffer());
560     mCurrentElementArrayBuffer = &bufferVk->getBuffer();
561 }
562 
syncState(const gl::Context * context,const gl::VertexArray::DirtyBits & dirtyBits,gl::VertexArray::DirtyAttribBitsArray * attribBits,gl::VertexArray::DirtyBindingBitsArray * bindingBits)563 angle::Result VertexArrayVk::syncState(const gl::Context *context,
564                                        const gl::VertexArray::DirtyBits &dirtyBits,
565                                        gl::VertexArray::DirtyAttribBitsArray *attribBits,
566                                        gl::VertexArray::DirtyBindingBitsArray *bindingBits)
567 {
568     ASSERT(dirtyBits.any());
569 
570     ContextVk *contextVk = vk::GetImpl(context);
571     contextVk->getPerfCounters().vertexArraySyncStateCalls++;
572 
573     const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes();
574     const std::vector<gl::VertexBinding> &bindings  = mState.getVertexBindings();
575 
576     for (auto iter = dirtyBits.begin(), endIter = dirtyBits.end(); iter != endIter; ++iter)
577     {
578         size_t dirtyBit = *iter;
579         switch (dirtyBit)
580         {
581             case gl::VertexArray::DIRTY_BIT_LOST_OBSERVATION:
582             {
583                 // If vertex array was not observing while unbound, we need to check buffer's
584                 // internal storage and take action if buffer storage has changed while not
585                 // observing.
586                 if (contextVk->getRenderer()->getFeatures().compressVertexData.enabled ||
587                     mContentsObservers->any())
588                 {
589                     // We may have lost buffer content change when it became non-current. In that
590                     // case we always assume buffer has changed. If compressVertexData.enabled is
591                     // true, it also depends on buffer usage which may have changed.
592                     iter.setLaterBits(
593                         gl::VertexArray::DirtyBits(mState.getBufferBindingMask().to_ulong()
594                                                    << gl::VertexArray::DIRTY_BIT_BINDING_0));
595                 }
596                 else
597                 {
598                     for (size_t bindingIndex : mState.getBufferBindingMask())
599                     {
600                         const gl::Buffer *bufferGL    = bindings[bindingIndex].getBuffer().get();
601                         vk::BufferSerial bufferSerial = vk::GetImpl(bufferGL)->getBufferSerial();
602                         for (size_t attribIndex : bindings[bindingIndex].getBoundAttributesMask())
603                         {
604                             if (attribs[attribIndex].enabled &&
605                                 (!bufferSerial.valid() ||
606                                  bufferSerial != mCurrentArrayBufferSerial[attribIndex]))
607                             {
608                                 iter.setLaterBit(gl::VertexArray::DIRTY_BIT_BINDING_0 +
609                                                  bindingIndex);
610                                 break;
611                             }
612                         }
613                     }
614                 }
615                 break;
616             }
617 
618             case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER:
619             case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA:
620             {
621                 gl::Buffer *bufferGL = mState.getElementArrayBuffer();
622                 if (bufferGL && bufferGL->getSize() > 0)
623                 {
624                     // Note that just updating buffer data may still result in a new
625                     // vk::BufferHelper allocation.
626                     updateCurrentElementArrayBuffer();
627                 }
628                 else
629                 {
630                     mCurrentElementArrayBuffer = nullptr;
631                 }
632 
633                 mLineLoopBufferFirstIndex.reset();
634                 mLineLoopBufferLastIndex.reset();
635                 ANGLE_TRY(contextVk->onIndexBufferChange(mCurrentElementArrayBuffer));
636                 mDirtyLineLoopTranslation = true;
637                 break;
638             }
639 
640 #define ANGLE_VERTEX_DIRTY_ATTRIB_FUNC(INDEX)                                                 \
641     case gl::VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX:                                         \
642     {                                                                                         \
643         gl::VertexArray::DirtyAttribBits dirtyAttribBitsRequiresPipelineUpdate =              \
644             (*attribBits)[INDEX] & mAttribDirtyBitsRequiresPipelineUpdate;                    \
645         const bool bufferOnly = dirtyAttribBitsRequiresPipelineUpdate.none();                 \
646         ANGLE_TRY(syncDirtyAttrib(contextVk, attribs[INDEX],                                  \
647                                   bindings[attribs[INDEX].bindingIndex], INDEX, bufferOnly)); \
648         (*attribBits)[INDEX].reset();                                                         \
649         break;                                                                                \
650     }
651 
652                 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_ATTRIB_FUNC)
653 
654 // Since BINDING already implies DATA and ATTRIB change, we remove these here to avoid redundant
655 // processing.
656 #define ANGLE_VERTEX_DIRTY_BINDING_FUNC(INDEX)                                          \
657     case gl::VertexArray::DIRTY_BIT_BINDING_0 + INDEX:                                  \
658     {                                                                                   \
659         gl::VertexArray::DirtyBindingBits dirtyBindingBitsRequirePipelineUpdate =       \
660             (*bindingBits)[INDEX] & mBindingDirtyBitsRequiresPipelineUpdate;            \
661                                                                                         \
662         for (size_t attribIndex : bindings[INDEX].getBoundAttributesMask())             \
663         {                                                                               \
664             gl::VertexArray::DirtyAttribBits dirtyAttribBitsRequiresPipelineUpdate =    \
665                 (*attribBits)[attribIndex] & mAttribDirtyBitsRequiresPipelineUpdate;    \
666             const bool bufferOnly = dirtyBindingBitsRequirePipelineUpdate.none() &&     \
667                                     dirtyAttribBitsRequiresPipelineUpdate.none();       \
668             ANGLE_TRY(syncDirtyAttrib(contextVk, attribs[attribIndex], bindings[INDEX], \
669                                       attribIndex, bufferOnly));                        \
670             iter.resetLaterBit(gl::VertexArray::DIRTY_BIT_BUFFER_DATA_0 + attribIndex); \
671             iter.resetLaterBit(gl::VertexArray::DIRTY_BIT_ATTRIB_0 + attribIndex);      \
672             (*attribBits)[attribIndex].reset();                                         \
673         }                                                                               \
674         (*bindingBits)[INDEX].reset();                                                  \
675         break;                                                                          \
676     }
677 
678                 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BINDING_FUNC)
679 
680 #define ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC(INDEX)                                       \
681     case gl::VertexArray::DIRTY_BIT_BUFFER_DATA_0 + INDEX:                               \
682         ANGLE_TRY(syncDirtyAttrib(contextVk, attribs[INDEX],                             \
683                                   bindings[attribs[INDEX].bindingIndex], INDEX, false)); \
684         iter.resetLaterBit(gl::VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX);                 \
685         (*attribBits)[INDEX].reset();                                                    \
686         break;
687 
688                 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC)
689 
690             default:
691                 UNREACHABLE();
692                 break;
693         }
694     }
695 
696     return angle::Result::Continue;
697 }  // namespace rx
698 
699 #undef ANGLE_VERTEX_DIRTY_ATTRIB_FUNC
700 #undef ANGLE_VERTEX_DIRTY_BINDING_FUNC
701 #undef ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC
702 
setDefaultPackedInput(ContextVk * contextVk,size_t attribIndex,angle::FormatID * formatOut)703 ANGLE_INLINE angle::Result VertexArrayVk::setDefaultPackedInput(ContextVk *contextVk,
704                                                                 size_t attribIndex,
705                                                                 angle::FormatID *formatOut)
706 {
707     const gl::State &glState = contextVk->getState();
708     const gl::VertexAttribCurrentValueData &defaultValue =
709         glState.getVertexAttribCurrentValues()[attribIndex];
710 
711     *formatOut = GetCurrentValueFormatID(defaultValue.Type);
712 
713     return contextVk->onVertexAttributeChange(attribIndex, 0, 0, *formatOut, false, 0, nullptr);
714 }
715 
updateActiveAttribInfo(ContextVk * contextVk)716 angle::Result VertexArrayVk::updateActiveAttribInfo(ContextVk *contextVk)
717 {
718     const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes();
719     const std::vector<gl::VertexBinding> &bindings  = mState.getVertexBindings();
720 
721     // Update pipeline cache with current active attribute info
722     for (size_t attribIndex : mState.getEnabledAttributesMask())
723     {
724         const gl::VertexAttribute &attrib = attribs[attribIndex];
725         const gl::VertexBinding &binding  = bindings[attribs[attribIndex].bindingIndex];
726         const angle::FormatID format      = attrib.format->id;
727 
728         ANGLE_TRY(contextVk->onVertexAttributeChange(
729             attribIndex, mCurrentArrayBufferStrides[attribIndex], binding.getDivisor(), format,
730             mCurrentArrayBufferCompressed.test(attribIndex),
731             mCurrentArrayBufferRelativeOffsets[attribIndex], mCurrentArrayBuffers[attribIndex]));
732 
733         mCurrentArrayBufferFormats[attribIndex] = format;
734     }
735 
736     return angle::Result::Continue;
737 }
738 
syncDirtyAttrib(ContextVk * contextVk,const gl::VertexAttribute & attrib,const gl::VertexBinding & binding,size_t attribIndex,bool bufferOnly)739 angle::Result VertexArrayVk::syncDirtyAttrib(ContextVk *contextVk,
740                                              const gl::VertexAttribute &attrib,
741                                              const gl::VertexBinding &binding,
742                                              size_t attribIndex,
743                                              bool bufferOnly)
744 {
745     vk::Renderer *renderer = contextVk->getRenderer();
746     if (attrib.enabled)
747     {
748         const vk::Format &vertexFormat = renderer->getFormat(attrib.format->id);
749 
750         // Init attribute offset to the front-end value
751         mCurrentArrayBufferRelativeOffsets[attribIndex] = attrib.relativeOffset;
752         gl::Buffer *bufferGL                            = binding.getBuffer().get();
753         // Emulated and/or client-side attribs will be streamed
754         bool isStreamingVertexAttrib =
755             (binding.getDivisor() > renderer->getMaxVertexAttribDivisor()) || (bufferGL == nullptr);
756         // If we sre switching between streaming and buffer mode, set bufferOnly to false since we
757         // are actually changing the buffer.
758         if (bufferOnly && isStreamingVertexAttrib != mStreamingVertexAttribsMask.test(attribIndex))
759         {
760             bufferOnly = false;
761         }
762         mStreamingVertexAttribsMask.set(attribIndex, isStreamingVertexAttrib);
763         bool compressed = false;
764 
765         if (bufferGL)
766         {
767             mContentsObservers->disableForBuffer(bufferGL, static_cast<uint32_t>(attribIndex));
768         }
769 
770         if (!isStreamingVertexAttrib && bufferGL->getSize() > 0)
771         {
772             BufferVk *bufferVk                  = vk::GetImpl(bufferGL);
773             const angle::Format &intendedFormat = vertexFormat.getIntendedFormat();
774             size_t numVertices    = GetVertexCount(bufferVk, binding, intendedFormat.pixelBytes);
775             bool bindingIsAligned = BindingIsAligned(
776                 binding, intendedFormat, intendedFormat.channelCount, attrib.relativeOffset);
777 
778             if (renderer->getFeatures().compressVertexData.enabled &&
779                 gl::IsStaticBufferUsage(bufferGL->getUsage()) &&
780                 vertexFormat.canCompressBufferData())
781             {
782                 compressed = true;
783             }
784 
785             bool needsConversion =
786                 numVertices > 0 &&
787                 (vertexFormat.getVertexLoadRequiresConversion(compressed) || !bindingIsAligned);
788 
789             if (needsConversion)
790             {
791                 mContentsObservers->enableForBuffer(bufferGL, static_cast<uint32_t>(attribIndex));
792 
793                 WarnOnVertexFormatConversion(contextVk, vertexFormat, compressed, true);
794 
795                 ConversionBuffer *conversion = bufferVk->getVertexConversionBuffer(
796                     renderer, intendedFormat.id, binding.getStride(),
797                     binding.getOffset() + attrib.relativeOffset, !bindingIsAligned);
798                 if (conversion->dirty)
799                 {
800                     if (compressed)
801                     {
802                         INFO() << "Compressing vertex data in buffer " << bufferGL->id().value
803                                << " from " << ToUnderlying(vertexFormat.getIntendedFormatID())
804                                << " to "
805                                << ToUnderlying(vertexFormat.getActualBufferFormat(true).id) << ".";
806                     }
807 
808                     if (bindingIsAligned)
809                     {
810                         ANGLE_TRY(convertVertexBufferGPU(contextVk, bufferVk, binding, attribIndex,
811                                                          vertexFormat, conversion,
812                                                          attrib.relativeOffset, compressed));
813                     }
814                     else
815                     {
816                         ANGLE_VK_PERF_WARNING(
817                             contextVk, GL_DEBUG_SEVERITY_HIGH,
818                             "GPU stall due to vertex format conversion of unaligned data");
819 
820                         ANGLE_TRY(convertVertexBufferCPU(contextVk, bufferVk, binding, attribIndex,
821                                                          vertexFormat, conversion,
822                                                          attrib.relativeOffset, compressed));
823                     }
824 
825                     // If conversion happens, the destination buffer stride may be changed,
826                     // therefore an attribute change needs to be called. Note that it may trigger
827                     // unnecessary vulkan PSO update when the destination buffer stride does not
828                     // change, but for simplicity just make it conservative
829                     bufferOnly = false;
830                 }
831 
832                 vk::BufferHelper *bufferHelper         = conversion->data.get();
833                 mCurrentArrayBuffers[attribIndex]      = bufferHelper;
834                 mCurrentArrayBufferSerial[attribIndex] = bufferHelper->getBufferSerial();
835                 VkDeviceSize bufferOffset;
836                 mCurrentArrayBufferHandles[attribIndex] =
837                     bufferHelper
838                         ->getBufferForVertexArray(contextVk, bufferHelper->getSize(), &bufferOffset)
839                         .getHandle();
840                 mCurrentArrayBufferOffsets[attribIndex] = bufferOffset;
841                 // Converted attribs are packed in their own VK buffer so offset is zero
842                 mCurrentArrayBufferRelativeOffsets[attribIndex] = 0;
843 
844                 // Converted buffer is tightly packed
845                 mCurrentArrayBufferStrides[attribIndex] =
846                     vertexFormat.getActualBufferFormat(compressed).pixelBytes;
847             }
848             else
849             {
850                 if (numVertices == 0)
851                 {
852                     vk::BufferHelper &emptyBuffer = contextVk->getEmptyBuffer();
853 
854                     mCurrentArrayBuffers[attribIndex]       = &emptyBuffer;
855                     mCurrentArrayBufferSerial[attribIndex]  = emptyBuffer.getBufferSerial();
856                     mCurrentArrayBufferHandles[attribIndex] = emptyBuffer.getBuffer().getHandle();
857                     mCurrentArrayBufferOffsets[attribIndex] = emptyBuffer.getOffset();
858                     mCurrentArrayBufferStrides[attribIndex] = 0;
859                 }
860                 else
861                 {
862                     vk::BufferHelper &bufferHelper         = bufferVk->getBuffer();
863                     mCurrentArrayBuffers[attribIndex]      = &bufferHelper;
864                     mCurrentArrayBufferSerial[attribIndex] = bufferHelper.getBufferSerial();
865                     VkDeviceSize bufferOffset;
866                     mCurrentArrayBufferHandles[attribIndex] =
867                         bufferHelper
868                             .getBufferForVertexArray(contextVk, bufferVk->getSize(), &bufferOffset)
869                             .getHandle();
870 
871                     // Vulkan requires the offset is within the buffer. We use robust access
872                     // behaviour to reset the offset if it starts outside the buffer.
873                     mCurrentArrayBufferOffsets[attribIndex] =
874                         binding.getOffset() < static_cast<GLint64>(bufferVk->getSize())
875                             ? binding.getOffset() + bufferOffset
876                             : bufferOffset;
877 
878                     mCurrentArrayBufferStrides[attribIndex] = binding.getStride();
879                 }
880             }
881         }
882         else
883         {
884             vk::BufferHelper &emptyBuffer           = contextVk->getEmptyBuffer();
885             mCurrentArrayBuffers[attribIndex]       = &emptyBuffer;
886             mCurrentArrayBufferSerial[attribIndex]  = emptyBuffer.getBufferSerial();
887             mCurrentArrayBufferHandles[attribIndex] = emptyBuffer.getBuffer().getHandle();
888             mCurrentArrayBufferOffsets[attribIndex] = emptyBuffer.getOffset();
889 
890             bool combined = ShouldCombineAttributes(renderer, attrib, binding);
891             mCurrentArrayBufferStrides[attribIndex] =
892                 combined ? binding.getStride()
893                          : vertexFormat.getActualBufferFormat(compressed).pixelBytes;
894         }
895 
896         if (bufferOnly)
897         {
898             ANGLE_TRY(contextVk->onVertexBufferChange(mCurrentArrayBuffers[attribIndex]));
899         }
900         else
901         {
902             const angle::FormatID format = attrib.format->id;
903             ANGLE_TRY(contextVk->onVertexAttributeChange(
904                 attribIndex, mCurrentArrayBufferStrides[attribIndex], binding.getDivisor(), format,
905                 compressed, mCurrentArrayBufferRelativeOffsets[attribIndex],
906                 mCurrentArrayBuffers[attribIndex]));
907 
908             mCurrentArrayBufferFormats[attribIndex]    = format;
909             mCurrentArrayBufferCompressed[attribIndex] = compressed;
910             mCurrentArrayBufferDivisors[attribIndex]   = binding.getDivisor();
911         }
912     }
913     else
914     {
915         contextVk->invalidateDefaultAttribute(attribIndex);
916 
917         // These will be filled out by the ContextVk.
918         vk::BufferHelper &emptyBuffer                   = contextVk->getEmptyBuffer();
919         mCurrentArrayBuffers[attribIndex]               = &emptyBuffer;
920         mCurrentArrayBufferSerial[attribIndex]          = emptyBuffer.getBufferSerial();
921         mCurrentArrayBufferHandles[attribIndex]         = emptyBuffer.getBuffer().getHandle();
922         mCurrentArrayBufferOffsets[attribIndex]         = emptyBuffer.getOffset();
923         mCurrentArrayBufferStrides[attribIndex]         = 0;
924         mCurrentArrayBufferDivisors[attribIndex]        = 0;
925         mCurrentArrayBufferCompressed[attribIndex]      = false;
926         mCurrentArrayBufferRelativeOffsets[attribIndex] = 0;
927 
928         ANGLE_TRY(setDefaultPackedInput(contextVk, attribIndex,
929                                         &mCurrentArrayBufferFormats[attribIndex]));
930     }
931 
932     return angle::Result::Continue;
933 }
934 
mergeClientAttribsRange(vk::Renderer * renderer,const gl::AttributesMask activeStreamedAttribs,size_t startVertex,size_t endVertex,std::array<AttributeRange,gl::MAX_VERTEX_ATTRIBS> & mergeRangesOut,std::array<size_t,gl::MAX_VERTEX_ATTRIBS> & mergedIndexesOut) const935 gl::AttributesMask VertexArrayVk::mergeClientAttribsRange(
936     vk::Renderer *renderer,
937     const gl::AttributesMask activeStreamedAttribs,
938     size_t startVertex,
939     size_t endVertex,
940     std::array<AttributeRange, gl::MAX_VERTEX_ATTRIBS> &mergeRangesOut,
941     std::array<size_t, gl::MAX_VERTEX_ATTRIBS> &mergedIndexesOut) const
942 {
943     const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes();
944     const std::vector<gl::VertexBinding> &bindings  = mState.getVertexBindings();
945     gl::AttributesMask attributeMaskCanCombine;
946     angle::FixedVector<size_t, gl::MAX_VERTEX_ATTRIBS> combinedIndexes;
947     for (size_t attribIndex : activeStreamedAttribs)
948     {
949         const gl::VertexAttribute &attrib = attribs[attribIndex];
950         ASSERT(attrib.enabled);
951         const gl::VertexBinding &binding = bindings[attrib.bindingIndex];
952         const vk::Format &vertexFormat   = renderer->getFormat(attrib.format->id);
953         bool combined                    = ShouldCombineAttributes(renderer, attrib, binding);
954         attributeMaskCanCombine.set(attribIndex, combined);
955         if (combined)
956         {
957             combinedIndexes.push_back(attribIndex);
958         }
959         GLuint pixelBytes      = vertexFormat.getActualBufferFormat(false).pixelBytes;
960         size_t destStride      = combined ? binding.getStride() : pixelBytes;
961         uintptr_t startAddress = reinterpret_cast<uintptr_t>(attrib.pointer);
962         mergeRangesOut[attribIndex].startAddr = startAddress;
963         mergeRangesOut[attribIndex].endAddr =
964             startAddress + (endVertex - 1) * destStride + pixelBytes;
965         mergeRangesOut[attribIndex].copyStartAddr =
966             startAddress + startVertex * binding.getStride();
967         mergedIndexesOut[attribIndex] = attribIndex;
968     }
969     if (attributeMaskCanCombine.none())
970     {
971         return attributeMaskCanCombine;
972     }
973     auto comp = [&mergeRangesOut](size_t a, size_t b) -> bool {
974         return mergeRangesOut[a] < mergeRangesOut[b];
975     };
976     // Only sort combined range indexes.
977     std::sort(combinedIndexes.begin(), combinedIndexes.end(), comp);
978     // Merge combined range span.
979     auto next = combinedIndexes.begin();
980     auto cur  = next++;
981     while (next != combinedIndexes.end() || (cur != next))
982     {
983         // Cur and next overlaps: merge next into cur and move next.
984         if (next != combinedIndexes.end() &&
985             mergeRangesOut[*cur].endAddr >= mergeRangesOut[*next].startAddr)
986         {
987             mergeRangesOut[*cur].endAddr =
988                 std::max(mergeRangesOut[*cur].endAddr, mergeRangesOut[*next].endAddr);
989             mergeRangesOut[*cur].copyStartAddr =
990                 std::min(mergeRangesOut[*cur].copyStartAddr, mergeRangesOut[*next].copyStartAddr);
991             mergedIndexesOut[*next] = mergedIndexesOut[*cur];
992             ++next;
993         }
994         else
995         {
996             ++cur;
997             if (cur != next)
998             {
999                 mergeRangesOut[*cur] = mergeRangesOut[*(cur - 1)];
1000             }
1001             else if (next != combinedIndexes.end())
1002             {
1003                 ++next;
1004             }
1005         }
1006     }
1007     return attributeMaskCanCombine;
1008 }
1009 
1010 // Handle copying client attribs and/or expanding attrib buffer in case where attribute
1011 // divisor value has to be emulated.
updateStreamedAttribs(const gl::Context * context,GLint firstVertex,GLsizei vertexOrIndexCount,GLsizei instanceCount,gl::DrawElementsType indexTypeOrInvalid,const void * indices)1012 angle::Result VertexArrayVk::updateStreamedAttribs(const gl::Context *context,
1013                                                    GLint firstVertex,
1014                                                    GLsizei vertexOrIndexCount,
1015                                                    GLsizei instanceCount,
1016                                                    gl::DrawElementsType indexTypeOrInvalid,
1017                                                    const void *indices)
1018 {
1019     ContextVk *contextVk   = vk::GetImpl(context);
1020     vk::Renderer *renderer = contextVk->getRenderer();
1021 
1022     const gl::AttributesMask activeAttribs =
1023         context->getStateCache().getActiveClientAttribsMask() |
1024         context->getStateCache().getActiveBufferedAttribsMask();
1025     const gl::AttributesMask activeStreamedAttribs = mStreamingVertexAttribsMask & activeAttribs;
1026 
1027     // Early return for corner case where emulated buffered attribs are not active
1028     if (!activeStreamedAttribs.any())
1029     {
1030         return angle::Result::Continue;
1031     }
1032 
1033     GLint startVertex;
1034     size_t vertexCount;
1035     ANGLE_TRY(GetVertexRangeInfo(context, firstVertex, vertexOrIndexCount, indexTypeOrInvalid,
1036                                  indices, 0, &startVertex, &vertexCount));
1037 
1038     ASSERT(vertexCount > 0);
1039     const auto &attribs  = mState.getVertexAttributes();
1040     const auto &bindings = mState.getVertexBindings();
1041 
1042     std::array<size_t, gl::MAX_VERTEX_ATTRIBS> mergedIndexes;
1043     std::array<AttributeRange, gl::MAX_VERTEX_ATTRIBS> mergeRanges;
1044     std::array<vk::BufferHelper *, gl::MAX_VERTEX_ATTRIBS> attribBufferHelper = {};
1045     auto mergeAttribMask =
1046         mergeClientAttribsRange(renderer, activeStreamedAttribs, startVertex,
1047                                 startVertex + vertexCount, mergeRanges, mergedIndexes);
1048 
1049     for (size_t attribIndex : activeStreamedAttribs)
1050     {
1051         const gl::VertexAttribute &attrib = attribs[attribIndex];
1052         ASSERT(attrib.enabled);
1053         const gl::VertexBinding &binding = bindings[attrib.bindingIndex];
1054 
1055         const vk::Format &vertexFormat = renderer->getFormat(attrib.format->id);
1056         GLuint pixelBytes              = vertexFormat.getActualBufferFormat(false).pixelBytes;
1057 
1058         const bool compressed = false;
1059         ASSERT(vertexFormat.getVertexInputAlignment(false) <= vk::kVertexBufferAlignment);
1060 
1061         vk::BufferHelper *vertexDataBuffer = nullptr;
1062         const uint8_t *src                 = static_cast<const uint8_t *>(attrib.pointer);
1063         const uint32_t divisor             = binding.getDivisor();
1064 
1065         bool combined            = mergeAttribMask.test(attribIndex);
1066         GLuint stride            = combined ? binding.getStride() : pixelBytes;
1067         VkDeviceSize startOffset = 0;
1068         if (divisor > 0)
1069         {
1070             // Instanced attrib
1071             if (divisor > renderer->getMaxVertexAttribDivisor())
1072             {
1073                 // Divisor will be set to 1 & so update buffer to have 1 attrib per instance
1074                 size_t bytesToAllocate = instanceCount * stride;
1075 
1076                 // Allocate buffer for results
1077                 ANGLE_TRY(contextVk->allocateStreamedVertexBuffer(attribIndex, bytesToAllocate,
1078                                                                   &vertexDataBuffer));
1079 
1080                 gl::Buffer *bufferGL = binding.getBuffer().get();
1081                 if (bufferGL != nullptr)
1082                 {
1083                     // Only do the data copy if src buffer is valid.
1084                     if (bufferGL->getSize() > 0)
1085                     {
1086                         // Map buffer to expand attribs for divisor emulation
1087                         BufferVk *bufferVk = vk::GetImpl(binding.getBuffer().get());
1088                         void *buffSrc      = nullptr;
1089                         ANGLE_TRY(bufferVk->mapImpl(contextVk, GL_MAP_READ_BIT, &buffSrc));
1090                         src = reinterpret_cast<const uint8_t *>(buffSrc) + binding.getOffset();
1091 
1092                         uint32_t srcAttributeSize =
1093                             static_cast<uint32_t>(ComputeVertexAttributeTypeSize(attrib));
1094 
1095                         size_t numVertices = GetVertexCount(bufferVk, binding, srcAttributeSize);
1096 
1097                         ANGLE_TRY(StreamVertexDataWithDivisor(
1098                             contextVk, vertexDataBuffer, src, bytesToAllocate, binding.getStride(),
1099                             stride, vertexFormat.getVertexLoadFunction(compressed), divisor,
1100                             numVertices));
1101 
1102                         ANGLE_TRY(bufferVk->unmapImpl(contextVk));
1103                     }
1104                     else if (contextVk->getExtensions().robustnessAny())
1105                     {
1106                         // Satisfy robustness constraints (only if extension enabled)
1107                         uint8_t *dst = vertexDataBuffer->getMappedMemory();
1108                         memset(dst, 0, bytesToAllocate);
1109                     }
1110                 }
1111                 else
1112                 {
1113                     size_t numVertices = instanceCount;
1114                     ANGLE_TRY(StreamVertexDataWithDivisor(
1115                         contextVk, vertexDataBuffer, src, bytesToAllocate, binding.getStride(),
1116                         stride, vertexFormat.getVertexLoadFunction(compressed), divisor,
1117                         numVertices));
1118                 }
1119             }
1120             else
1121             {
1122                 ASSERT(binding.getBuffer().get() == nullptr);
1123                 size_t count           = UnsignedCeilDivide(instanceCount, divisor);
1124                 size_t bytesToAllocate = count * stride;
1125 
1126                 // Allocate buffer for results
1127                 ANGLE_TRY(contextVk->allocateStreamedVertexBuffer(attribIndex, bytesToAllocate,
1128                                                                   &vertexDataBuffer));
1129 
1130                 ANGLE_TRY(StreamVertexData(contextVk, vertexDataBuffer, src, bytesToAllocate, 0,
1131                                            count, binding.getStride(),
1132                                            vertexFormat.getVertexLoadFunction(compressed)));
1133             }
1134         }
1135         else
1136         {
1137             ASSERT(binding.getBuffer().get() == nullptr);
1138             size_t mergedAttribIdx      = mergedIndexes[attribIndex];
1139             const AttributeRange &range = mergeRanges[attribIndex];
1140             if (attribBufferHelper[mergedAttribIdx] == nullptr)
1141             {
1142                 size_t destOffset =
1143                     combined ? range.copyStartAddr - range.startAddr : startVertex * stride;
1144                 size_t bytesToAllocate = range.endAddr - range.startAddr;
1145                 ANGLE_TRY(contextVk->allocateStreamedVertexBuffer(
1146                     mergedAttribIdx, bytesToAllocate, &attribBufferHelper[mergedAttribIdx]));
1147                 ANGLE_TRY(StreamVertexData(
1148                     contextVk, attribBufferHelper[mergedAttribIdx],
1149                     (const uint8_t *)range.copyStartAddr, bytesToAllocate - destOffset, destOffset,
1150                     vertexCount, binding.getStride(),
1151                     combined ? nullptr : vertexFormat.getVertexLoadFunction(compressed)));
1152             }
1153             vertexDataBuffer = attribBufferHelper[mergedAttribIdx];
1154             startOffset      = combined ? (uintptr_t)attrib.pointer - range.startAddr : 0;
1155         }
1156         ASSERT(vertexDataBuffer != nullptr);
1157         mCurrentArrayBuffers[attribIndex]      = vertexDataBuffer;
1158         mCurrentArrayBufferSerial[attribIndex] = vertexDataBuffer->getBufferSerial();
1159         VkDeviceSize bufferOffset;
1160         mCurrentArrayBufferHandles[attribIndex] =
1161             vertexDataBuffer
1162                 ->getBufferForVertexArray(contextVk, vertexDataBuffer->getSize(), &bufferOffset)
1163                 .getHandle();
1164         mCurrentArrayBufferOffsets[attribIndex]  = bufferOffset + startOffset;
1165         mCurrentArrayBufferStrides[attribIndex]  = stride;
1166         mCurrentArrayBufferDivisors[attribIndex] = divisor;
1167     }
1168 
1169     return angle::Result::Continue;
1170 }
1171 
handleLineLoop(ContextVk * contextVk,GLint firstVertex,GLsizei vertexOrIndexCount,gl::DrawElementsType indexTypeOrInvalid,const void * indices,uint32_t * indexCountOut)1172 angle::Result VertexArrayVk::handleLineLoop(ContextVk *contextVk,
1173                                             GLint firstVertex,
1174                                             GLsizei vertexOrIndexCount,
1175                                             gl::DrawElementsType indexTypeOrInvalid,
1176                                             const void *indices,
1177                                             uint32_t *indexCountOut)
1178 {
1179     if (indexTypeOrInvalid != gl::DrawElementsType::InvalidEnum)
1180     {
1181         // Handle GL_LINE_LOOP drawElements.
1182         if (mDirtyLineLoopTranslation)
1183         {
1184             gl::Buffer *elementArrayBuffer = mState.getElementArrayBuffer();
1185 
1186             if (!elementArrayBuffer)
1187             {
1188                 ANGLE_TRY(
1189                     mLineLoopHelper.streamIndices(contextVk, indexTypeOrInvalid, vertexOrIndexCount,
1190                                                   reinterpret_cast<const uint8_t *>(indices),
1191                                                   &mCurrentElementArrayBuffer, indexCountOut));
1192             }
1193             else
1194             {
1195                 // When using an element array buffer, 'indices' is an offset to the first element.
1196                 intptr_t offset                = reinterpret_cast<intptr_t>(indices);
1197                 BufferVk *elementArrayBufferVk = vk::GetImpl(elementArrayBuffer);
1198                 ANGLE_TRY(mLineLoopHelper.getIndexBufferForElementArrayBuffer(
1199                     contextVk, elementArrayBufferVk, indexTypeOrInvalid, vertexOrIndexCount, offset,
1200                     &mCurrentElementArrayBuffer, indexCountOut));
1201             }
1202         }
1203 
1204         // If we've had a drawArrays call with a line loop before, we want to make sure this is
1205         // invalidated the next time drawArrays is called since we use the same index buffer for
1206         // both calls.
1207         mLineLoopBufferFirstIndex.reset();
1208         mLineLoopBufferLastIndex.reset();
1209         return angle::Result::Continue;
1210     }
1211 
1212     // Note: Vertex indexes can be arbitrarily large.
1213     uint32_t clampedVertexCount = gl::clampCast<uint32_t>(vertexOrIndexCount);
1214 
1215     // Handle GL_LINE_LOOP drawArrays.
1216     size_t lastVertex = static_cast<size_t>(firstVertex + clampedVertexCount);
1217     if (!mLineLoopBufferFirstIndex.valid() || !mLineLoopBufferLastIndex.valid() ||
1218         mLineLoopBufferFirstIndex != firstVertex || mLineLoopBufferLastIndex != lastVertex)
1219     {
1220         ANGLE_TRY(mLineLoopHelper.getIndexBufferForDrawArrays(
1221             contextVk, clampedVertexCount, firstVertex, &mCurrentElementArrayBuffer));
1222 
1223         mLineLoopBufferFirstIndex = firstVertex;
1224         mLineLoopBufferLastIndex  = lastVertex;
1225     }
1226     *indexCountOut = vertexOrIndexCount + 1;
1227 
1228     return angle::Result::Continue;
1229 }
1230 
updateDefaultAttrib(ContextVk * contextVk,size_t attribIndex)1231 angle::Result VertexArrayVk::updateDefaultAttrib(ContextVk *contextVk, size_t attribIndex)
1232 {
1233     if (!mState.getEnabledAttributesMask().test(attribIndex))
1234     {
1235         vk::BufferHelper *bufferHelper;
1236         ANGLE_TRY(
1237             contextVk->allocateStreamedVertexBuffer(attribIndex, kDefaultValueSize, &bufferHelper));
1238 
1239         const gl::VertexAttribCurrentValueData &defaultValue =
1240             contextVk->getState().getVertexAttribCurrentValues()[attribIndex];
1241         uint8_t *ptr = bufferHelper->getMappedMemory();
1242         memcpy(ptr, &defaultValue.Values, kDefaultValueSize);
1243         ANGLE_TRY(bufferHelper->flush(contextVk->getRenderer()));
1244 
1245         VkDeviceSize bufferOffset;
1246         mCurrentArrayBufferHandles[attribIndex] =
1247             bufferHelper->getBufferForVertexArray(contextVk, kDefaultValueSize, &bufferOffset)
1248                 .getHandle();
1249         mCurrentArrayBufferOffsets[attribIndex]  = bufferOffset;
1250         mCurrentArrayBuffers[attribIndex]        = bufferHelper;
1251         mCurrentArrayBufferSerial[attribIndex]   = bufferHelper->getBufferSerial();
1252         mCurrentArrayBufferStrides[attribIndex]  = 0;
1253         mCurrentArrayBufferDivisors[attribIndex] = 0;
1254 
1255         ANGLE_TRY(setDefaultPackedInput(contextVk, attribIndex,
1256                                         &mCurrentArrayBufferFormats[attribIndex]));
1257     }
1258 
1259     return angle::Result::Continue;
1260 }
1261 }  // namespace rx
1262