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