• 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 #include "libANGLE/trace.h"
22 
23 namespace rx
24 {
25 namespace
26 {
27 constexpr size_t kDynamicVertexDataSize   = 1024 * 1024;
28 constexpr size_t kDynamicIndexDataSize    = 1024 * 8;
29 constexpr size_t kDynamicIndirectDataSize = sizeof(VkDrawIndexedIndirectCommand) * 8;
30 
BindingIsAligned(const gl::VertexBinding & binding,const angle::Format & angleFormat,unsigned int attribSize,GLuint relativeOffset)31 ANGLE_INLINE bool BindingIsAligned(const gl::VertexBinding &binding,
32                                    const angle::Format &angleFormat,
33                                    unsigned int attribSize,
34                                    GLuint relativeOffset)
35 {
36     GLintptr totalOffset = binding.getOffset() + relativeOffset;
37     GLuint mask          = angleFormat.componentAlignmentMask;
38     if (mask != std::numeric_limits<GLuint>::max())
39     {
40         return ((totalOffset & mask) == 0 && (binding.getStride() & mask) == 0);
41     }
42     else
43     {
44         // To perform the GPU conversion for formats with components that aren't byte-aligned
45         // (for example, A2BGR10 or RGB10A2), one element has to be placed in 4 bytes to perform
46         // the compute shader. So, binding offset and stride has to be aligned to formatSize.
47         unsigned int formatSize = angleFormat.pixelBytes;
48         return (totalOffset % formatSize == 0) && (binding.getStride() % formatSize == 0);
49     }
50 }
51 
WarnOnVertexFormatConversion(ContextVk * contextVk,const vk::Format & vertexFormat,bool compressed,bool insertEventMarker)52 void WarnOnVertexFormatConversion(ContextVk *contextVk,
53                                   const vk::Format &vertexFormat,
54                                   bool compressed,
55                                   bool insertEventMarker)
56 {
57     if (!vertexFormat.getVertexLoadRequiresConversion(compressed))
58     {
59         return;
60     }
61 
62     ANGLE_VK_PERF_WARNING(
63         contextVk, GL_DEBUG_SEVERITY_LOW,
64         "The Vulkan driver does not support vertex attribute format 0x%04X, emulating with 0x%04X",
65         vertexFormat.getIntendedFormat().glInternalFormat,
66         vertexFormat.getActualBufferFormat(compressed).glInternalFormat);
67 }
68 
StreamVertexData(ContextVk * contextVk,vk::DynamicBuffer * dynamicBuffer,const uint8_t * sourceData,size_t bytesToAllocate,size_t destOffset,size_t vertexCount,size_t sourceStride,size_t destStride,VertexCopyFunction vertexLoadFunction,vk::BufferHelper ** bufferOut,VkDeviceSize * bufferOffsetOut,uint32_t replicateCount)69 angle::Result StreamVertexData(ContextVk *contextVk,
70                                vk::DynamicBuffer *dynamicBuffer,
71                                const uint8_t *sourceData,
72                                size_t bytesToAllocate,
73                                size_t destOffset,
74                                size_t vertexCount,
75                                size_t sourceStride,
76                                size_t destStride,
77                                VertexCopyFunction vertexLoadFunction,
78                                vk::BufferHelper **bufferOut,
79                                VkDeviceSize *bufferOffsetOut,
80                                uint32_t replicateCount)
81 {
82     uint8_t *dst = nullptr;
83     ANGLE_TRY(dynamicBuffer->allocate(contextVk, bytesToAllocate, &dst, nullptr, bufferOffsetOut,
84                                       nullptr));
85     *bufferOut = dynamicBuffer->getCurrentBuffer();
86     dst += destOffset;
87     if (replicateCount == 1)
88     {
89         vertexLoadFunction(sourceData, sourceStride, vertexCount, dst);
90     }
91     else
92     {
93         ASSERT(replicateCount > 1);
94         uint32_t sourceRemainingCount = replicateCount - 1;
95         for (size_t dataCopied = 0; dataCopied < bytesToAllocate;
96              dataCopied += destStride, dst += destStride, sourceRemainingCount--)
97         {
98             vertexLoadFunction(sourceData, sourceStride, 1, dst);
99             if (sourceRemainingCount == 0)
100             {
101                 sourceData += sourceStride;
102                 sourceRemainingCount = replicateCount;
103             }
104         }
105     }
106 
107     ANGLE_TRY(dynamicBuffer->flush(contextVk));
108     return angle::Result::Continue;
109 }
110 
GetVertexCount(BufferVk * srcBuffer,const gl::VertexBinding & binding,uint32_t srcFormatSize)111 size_t GetVertexCount(BufferVk *srcBuffer, const gl::VertexBinding &binding, uint32_t srcFormatSize)
112 {
113     // Bytes usable for vertex data.
114     GLint64 bytes = srcBuffer->getSize() - binding.getOffset();
115     if (bytes < srcFormatSize)
116         return 0;
117 
118     // Count the last vertex.  It may occupy less than a full stride.
119     size_t numVertices = 1;
120     bytes -= srcFormatSize;
121 
122     // Count how many strides fit remaining space.
123     if (bytes > 0)
124         numVertices += static_cast<size_t>(bytes) / binding.getStride();
125 
126     return numVertices;
127 }
128 }  // anonymous namespace
129 
VertexArrayVk(ContextVk * contextVk,const gl::VertexArrayState & state)130 VertexArrayVk::VertexArrayVk(ContextVk *contextVk, const gl::VertexArrayState &state)
131     : VertexArrayImpl(state),
132       mCurrentArrayBufferHandles{},
133       mCurrentArrayBufferOffsets{},
134       mCurrentArrayBufferRelativeOffsets{},
135       mCurrentArrayBuffers{},
136       mCurrentElementArrayBufferOffset(0),
137       mCurrentElementArrayBuffer(nullptr),
138       mLineLoopHelper(contextVk->getRenderer()),
139       mDirtyLineLoopTranslation(true)
140 {
141     RendererVk *renderer          = contextVk->getRenderer();
142     vk::BufferHelper &emptyBuffer = contextVk->getEmptyBuffer();
143 
144     mCurrentArrayBufferHandles.fill(emptyBuffer.getBuffer().getHandle());
145     mCurrentArrayBufferOffsets.fill(0);
146     mCurrentArrayBufferRelativeOffsets.fill(0);
147     mCurrentArrayBuffers.fill(&emptyBuffer);
148 
149     mDynamicVertexData.init(renderer, vk::kVertexBufferUsageFlags, vk::kVertexBufferAlignment,
150                             kDynamicVertexDataSize, true, vk::DynamicBufferPolicy::OneShotUse);
151 
152     // We use an alignment of four for index data. This ensures that compute shaders can read index
153     // elements from "uint" aligned addresses.
154     mDynamicIndexData.init(renderer, vk::kIndexBufferUsageFlags, vk::kIndexBufferAlignment,
155                            kDynamicIndexDataSize, true, vk::DynamicBufferPolicy::OneShotUse);
156     mTranslatedByteIndexData.init(renderer, vk::kIndexBufferUsageFlags, vk::kIndexBufferAlignment,
157                                   kDynamicIndexDataSize, true, vk::DynamicBufferPolicy::OneShotUse);
158     mTranslatedByteIndirectData.init(renderer, vk::kIndirectBufferUsageFlags,
159                                      vk::kIndirectBufferAlignment, kDynamicIndirectDataSize, true,
160                                      vk::DynamicBufferPolicy::OneShotUse);
161 }
162 
~VertexArrayVk()163 VertexArrayVk::~VertexArrayVk() {}
164 
destroy(const gl::Context * context)165 void VertexArrayVk::destroy(const gl::Context *context)
166 {
167     ContextVk *contextVk = vk::GetImpl(context);
168 
169     RendererVk *renderer = contextVk->getRenderer();
170 
171     mDynamicVertexData.release(renderer);
172     mDynamicIndexData.release(renderer);
173     mTranslatedByteIndexData.release(renderer);
174     mTranslatedByteIndirectData.release(renderer);
175     mLineLoopHelper.release(contextVk);
176 }
177 
convertIndexBufferGPU(ContextVk * contextVk,BufferVk * bufferVk,const void * indices)178 angle::Result VertexArrayVk::convertIndexBufferGPU(ContextVk *contextVk,
179                                                    BufferVk *bufferVk,
180                                                    const void *indices)
181 {
182     intptr_t offsetIntoSrcData = reinterpret_cast<intptr_t>(indices);
183     size_t srcDataSize         = static_cast<size_t>(bufferVk->getSize()) - offsetIntoSrcData;
184 
185     mTranslatedByteIndexData.releaseInFlightBuffers(contextVk);
186 
187     ANGLE_TRY(mTranslatedByteIndexData.allocate(contextVk, sizeof(GLushort) * srcDataSize, nullptr,
188                                                 nullptr, &mCurrentElementArrayBufferOffset,
189                                                 nullptr));
190     mCurrentElementArrayBuffer = mTranslatedByteIndexData.getCurrentBuffer();
191 
192     vk::BufferHelper *dst        = mTranslatedByteIndexData.getCurrentBuffer();
193     VkDeviceSize srcBufferOffset = 0;
194     vk::BufferHelper *src        = &bufferVk->getBufferAndOffset(&srcBufferOffset);
195 
196     // Copy relevant section of the source into destination at allocated offset.  Note that the
197     // offset returned by allocate() above is in bytes. As is the indices offset pointer.
198     UtilsVk::ConvertIndexParameters params = {};
199     params.srcOffset = static_cast<uint32_t>(offsetIntoSrcData + srcBufferOffset);
200     params.dstOffset = static_cast<uint32_t>(mCurrentElementArrayBufferOffset);
201     params.maxIndex  = static_cast<uint32_t>(bufferVk->getSize());
202 
203     return contextVk->getUtils().convertIndexBuffer(contextVk, dst, src, params);
204 }
205 
convertIndexBufferIndirectGPU(ContextVk * contextVk,vk::BufferHelper * srcIndirectBuf,VkDeviceSize srcIndirectBufOffset,vk::BufferHelper ** indirectBufferVkOut,VkDeviceSize * indirectBufferVkOffsetOut)206 angle::Result VertexArrayVk::convertIndexBufferIndirectGPU(ContextVk *contextVk,
207                                                            vk::BufferHelper *srcIndirectBuf,
208                                                            VkDeviceSize srcIndirectBufOffset,
209                                                            vk::BufferHelper **indirectBufferVkOut,
210                                                            VkDeviceSize *indirectBufferVkOffsetOut)
211 {
212     size_t srcDataSize = static_cast<size_t>(mCurrentElementArrayBuffer->getSize());
213     VkDeviceSize elementArrayBufferOffset = 0;
214     ASSERT(mCurrentElementArrayBuffer == &vk::GetImpl(getState().getElementArrayBuffer())
215                                               ->getBufferAndOffset(&elementArrayBufferOffset));
216     ASSERT(mCurrentElementArrayBufferOffset == elementArrayBufferOffset);
217 
218     mTranslatedByteIndexData.releaseInFlightBuffers(contextVk);
219     mTranslatedByteIndirectData.releaseInFlightBuffers(contextVk);
220 
221     vk::BufferHelper *srcIndexBuf  = mCurrentElementArrayBuffer;
222     VkDeviceSize srcIndexBufOffset = mCurrentElementArrayBufferOffset;
223 
224     VkDeviceSize dstIndirectBufOffset;
225     VkDeviceSize dstIndexBufOffset;
226     ANGLE_TRY(mTranslatedByteIndexData.allocate(contextVk, sizeof(GLushort) * srcDataSize, nullptr,
227                                                 nullptr, &dstIndexBufOffset, nullptr));
228     vk::BufferHelper *dstIndexBuf = mTranslatedByteIndexData.getCurrentBuffer();
229 
230     ANGLE_TRY(mTranslatedByteIndirectData.allocate(contextVk, sizeof(VkDrawIndexedIndirectCommand),
231                                                    nullptr, nullptr, &dstIndirectBufOffset,
232                                                    nullptr));
233     vk::BufferHelper *dstIndirectBuf = mTranslatedByteIndirectData.getCurrentBuffer();
234 
235     // Save new element array buffer
236     mCurrentElementArrayBuffer       = dstIndexBuf;
237     mCurrentElementArrayBufferOffset = dstIndexBufOffset;
238 
239     // Tell caller what new indirect buffer is
240     *indirectBufferVkOut       = dstIndirectBuf;
241     *indirectBufferVkOffsetOut = dstIndirectBufOffset;
242 
243     // Copy relevant section of the source into destination at allocated offset.  Note that the
244     // offset returned by allocate() above is in bytes. As is the indices offset pointer.
245     UtilsVk::ConvertIndexIndirectParameters params = {};
246     params.srcIndirectBufOffset                    = static_cast<uint32_t>(srcIndirectBufOffset);
247     params.srcIndexBufOffset                       = static_cast<uint32_t>(srcIndexBufOffset);
248     params.dstIndexBufOffset                       = static_cast<uint32_t>(dstIndexBufOffset);
249     params.maxIndex                                = static_cast<uint32_t>(srcDataSize);
250     params.dstIndirectBufOffset                    = static_cast<uint32_t>(dstIndirectBufOffset);
251 
252     return contextVk->getUtils().convertIndexIndirectBuffer(contextVk, srcIndirectBuf, srcIndexBuf,
253                                                             dstIndirectBuf, dstIndexBuf, params);
254 }
255 
handleLineLoopIndexIndirect(ContextVk * contextVk,gl::DrawElementsType glIndexType,vk::BufferHelper * srcIndirectBuf,VkDeviceSize indirectBufferOffset,vk::BufferHelper ** indirectBufferOut,VkDeviceSize * indirectBufferOffsetOut)256 angle::Result VertexArrayVk::handleLineLoopIndexIndirect(ContextVk *contextVk,
257                                                          gl::DrawElementsType glIndexType,
258                                                          vk::BufferHelper *srcIndirectBuf,
259                                                          VkDeviceSize indirectBufferOffset,
260                                                          vk::BufferHelper **indirectBufferOut,
261                                                          VkDeviceSize *indirectBufferOffsetOut)
262 {
263     ANGLE_TRY(mLineLoopHelper.streamIndicesIndirect(
264         contextVk, glIndexType, mCurrentElementArrayBuffer, mCurrentElementArrayBufferOffset,
265         srcIndirectBuf, indirectBufferOffset, &mCurrentElementArrayBuffer,
266         &mCurrentElementArrayBufferOffset, indirectBufferOut, indirectBufferOffsetOut));
267 
268     return angle::Result::Continue;
269 }
270 
handleLineLoopIndirectDraw(const gl::Context * context,vk::BufferHelper * indirectBufferVk,VkDeviceSize indirectBufferOffset,vk::BufferHelper ** indirectBufferOut,VkDeviceSize * indirectBufferOffsetOut)271 angle::Result VertexArrayVk::handleLineLoopIndirectDraw(const gl::Context *context,
272                                                         vk::BufferHelper *indirectBufferVk,
273                                                         VkDeviceSize indirectBufferOffset,
274                                                         vk::BufferHelper **indirectBufferOut,
275                                                         VkDeviceSize *indirectBufferOffsetOut)
276 {
277     size_t maxVertexCount = 0;
278     ContextVk *contextVk  = vk::GetImpl(context);
279     const gl::AttributesMask activeAttribs =
280         context->getStateCache().getActiveBufferedAttribsMask();
281 
282     const auto &attribs  = mState.getVertexAttributes();
283     const auto &bindings = mState.getVertexBindings();
284 
285     for (size_t attribIndex : activeAttribs)
286     {
287         const gl::VertexAttribute &attrib = attribs[attribIndex];
288         ASSERT(attrib.enabled);
289         VkDeviceSize bufSize             = this->getCurrentArrayBuffers()[attribIndex]->getSize();
290         const gl::VertexBinding &binding = bindings[attrib.bindingIndex];
291         size_t stride                    = binding.getStride();
292         size_t vertexCount               = static_cast<size_t>(bufSize / stride);
293         if (vertexCount > maxVertexCount)
294         {
295             maxVertexCount = vertexCount;
296         }
297     }
298     ANGLE_TRY(mLineLoopHelper.streamArrayIndirect(contextVk, maxVertexCount + 1, indirectBufferVk,
299                                                   indirectBufferOffset, &mCurrentElementArrayBuffer,
300                                                   &mCurrentElementArrayBufferOffset,
301                                                   indirectBufferOut, indirectBufferOffsetOut));
302 
303     return angle::Result::Continue;
304 }
305 
convertIndexBufferCPU(ContextVk * contextVk,gl::DrawElementsType indexType,size_t indexCount,const void * sourcePointer)306 angle::Result VertexArrayVk::convertIndexBufferCPU(ContextVk *contextVk,
307                                                    gl::DrawElementsType indexType,
308                                                    size_t indexCount,
309                                                    const void *sourcePointer)
310 {
311     ASSERT(!mState.getElementArrayBuffer() || indexType == gl::DrawElementsType::UnsignedByte);
312 
313     mDynamicIndexData.releaseInFlightBuffers(contextVk);
314 
315     size_t elementSize  = contextVk->getVkIndexTypeSize(indexType);
316     const size_t amount = elementSize * indexCount;
317     GLubyte *dst        = nullptr;
318 
319     ANGLE_TRY(mDynamicIndexData.allocate(contextVk, amount, &dst, nullptr,
320                                          &mCurrentElementArrayBufferOffset, nullptr));
321     mCurrentElementArrayBuffer = mDynamicIndexData.getCurrentBuffer();
322     if (contextVk->shouldConvertUint8VkIndexType(indexType))
323     {
324         // Unsigned bytes don't have direct support in Vulkan so we have to expand the
325         // memory to a GLushort.
326         const GLubyte *in     = static_cast<const GLubyte *>(sourcePointer);
327         GLushort *expandedDst = reinterpret_cast<GLushort *>(dst);
328         bool primitiveRestart = contextVk->getState().isPrimitiveRestartEnabled();
329 
330         constexpr GLubyte kUnsignedByteRestartValue   = 0xFF;
331         constexpr GLushort kUnsignedShortRestartValue = 0xFFFF;
332 
333         if (primitiveRestart)
334         {
335             for (size_t index = 0; index < indexCount; index++)
336             {
337                 GLushort value = static_cast<GLushort>(in[index]);
338                 if (in[index] == kUnsignedByteRestartValue)
339                 {
340                     // Convert from 8-bit restart value to 16-bit restart value
341                     value = kUnsignedShortRestartValue;
342                 }
343                 expandedDst[index] = value;
344             }
345         }
346         else
347         {
348             // Fast path for common case.
349             for (size_t index = 0; index < indexCount; index++)
350             {
351                 expandedDst[index] = static_cast<GLushort>(in[index]);
352             }
353         }
354     }
355     else
356     {
357         // The primitive restart value is the same for OpenGL and Vulkan,
358         // so there's no need to perform any conversion.
359         memcpy(dst, sourcePointer, amount);
360     }
361     return mDynamicIndexData.flush(contextVk);
362 }
363 
364 // We assume the buffer is completely full of the same kind of data and convert
365 // and/or align it as we copy it to a DynamicBuffer. The assumption could be wrong
366 // but the alternative of copying it piecemeal on each draw would have a lot more
367 // overhead.
convertVertexBufferGPU(ContextVk * contextVk,BufferVk * srcBuffer,const gl::VertexBinding & binding,size_t attribIndex,const vk::Format & vertexFormat,ConversionBuffer * conversion,GLuint relativeOffset,bool compressed)368 angle::Result VertexArrayVk::convertVertexBufferGPU(ContextVk *contextVk,
369                                                     BufferVk *srcBuffer,
370                                                     const gl::VertexBinding &binding,
371                                                     size_t attribIndex,
372                                                     const vk::Format &vertexFormat,
373                                                     ConversionBuffer *conversion,
374                                                     GLuint relativeOffset,
375                                                     bool compressed)
376 {
377     const angle::Format &srcFormat = vertexFormat.getIntendedFormat();
378     const angle::Format &dstFormat = vertexFormat.getActualBufferFormat(compressed);
379 
380     ASSERT(binding.getStride() % (srcFormat.pixelBytes / srcFormat.channelCount) == 0);
381 
382     unsigned srcFormatSize = srcFormat.pixelBytes;
383     unsigned dstFormatSize = dstFormat.pixelBytes;
384 
385     size_t numVertices = GetVertexCount(srcBuffer, binding, srcFormatSize);
386     if (numVertices == 0)
387     {
388         return angle::Result::Continue;
389     }
390 
391     ASSERT(vertexFormat.getVertexInputAlignment(compressed) <= vk::kVertexBufferAlignment);
392 
393     // Allocate buffer for results
394     conversion->data.releaseInFlightBuffers(contextVk);
395     ANGLE_TRY(conversion->data.allocate(contextVk, numVertices * dstFormatSize, nullptr, nullptr,
396                                         &conversion->lastAllocationOffset, nullptr));
397 
398     ASSERT(conversion->dirty);
399     conversion->dirty = false;
400 
401     VkDeviceSize srcBufferOffset      = 0;
402     vk::BufferHelper *srcBufferHelper = &srcBuffer->getBufferAndOffset(&srcBufferOffset);
403 
404     UtilsVk::ConvertVertexParameters params;
405     params.vertexCount = numVertices;
406     params.srcFormat   = &srcFormat;
407     params.dstFormat   = &dstFormat;
408     params.srcStride   = binding.getStride();
409     params.srcOffset = binding.getOffset() + relativeOffset + static_cast<size_t>(srcBufferOffset);
410     params.dstOffset = static_cast<size_t>(conversion->lastAllocationOffset);
411 
412     ANGLE_TRY(contextVk->getUtils().convertVertexBuffer(
413         contextVk, conversion->data.getCurrentBuffer(), srcBufferHelper, params));
414 
415     return angle::Result::Continue;
416 }
417 
convertVertexBufferCPU(ContextVk * contextVk,BufferVk * srcBuffer,const gl::VertexBinding & binding,size_t attribIndex,const vk::Format & vertexFormat,ConversionBuffer * conversion,GLuint relativeOffset,bool compressed)418 angle::Result VertexArrayVk::convertVertexBufferCPU(ContextVk *contextVk,
419                                                     BufferVk *srcBuffer,
420                                                     const gl::VertexBinding &binding,
421                                                     size_t attribIndex,
422                                                     const vk::Format &vertexFormat,
423                                                     ConversionBuffer *conversion,
424                                                     GLuint relativeOffset,
425                                                     bool compressed)
426 {
427     ANGLE_TRACE_EVENT0("gpu.angle", "VertexArrayVk::convertVertexBufferCpu");
428 
429     unsigned srcFormatSize = vertexFormat.getIntendedFormat().pixelBytes;
430     unsigned dstFormatSize = vertexFormat.getActualBufferFormat(compressed).pixelBytes;
431 
432     conversion->data.releaseInFlightBuffers(contextVk);
433 
434     size_t numVertices = GetVertexCount(srcBuffer, binding, srcFormatSize);
435     if (numVertices == 0)
436     {
437         return angle::Result::Continue;
438     }
439 
440     void *src = nullptr;
441     ANGLE_TRY(srcBuffer->mapImpl(contextVk, GL_MAP_READ_BIT, &src));
442     const uint8_t *srcBytes = reinterpret_cast<const uint8_t *>(src);
443     srcBytes += binding.getOffset() + relativeOffset;
444     ASSERT(vertexFormat.getVertexInputAlignment(compressed) <= vk::kVertexBufferAlignment);
445     ANGLE_TRY(StreamVertexData(
446         contextVk, &conversion->data, srcBytes, numVertices * dstFormatSize, 0, numVertices,
447         binding.getStride(), srcFormatSize, vertexFormat.getVertexLoadFunction(compressed),
448         &mCurrentArrayBuffers[attribIndex], &conversion->lastAllocationOffset, 1));
449     ANGLE_TRY(srcBuffer->unmapImpl(contextVk));
450 
451     ASSERT(conversion->dirty);
452     conversion->dirty = false;
453 
454     return angle::Result::Continue;
455 }
456 
syncState(const gl::Context * context,const gl::VertexArray::DirtyBits & dirtyBits,gl::VertexArray::DirtyAttribBitsArray * attribBits,gl::VertexArray::DirtyBindingBitsArray * bindingBits)457 angle::Result VertexArrayVk::syncState(const gl::Context *context,
458                                        const gl::VertexArray::DirtyBits &dirtyBits,
459                                        gl::VertexArray::DirtyAttribBitsArray *attribBits,
460                                        gl::VertexArray::DirtyBindingBitsArray *bindingBits)
461 {
462     ASSERT(dirtyBits.any());
463 
464     ContextVk *contextVk = vk::GetImpl(context);
465     contextVk->getPerfCounters().vertexArraySyncStateCalls++;
466 
467     const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes();
468     const std::vector<gl::VertexBinding> &bindings  = mState.getVertexBindings();
469 
470     for (size_t dirtyBit : dirtyBits)
471     {
472         switch (dirtyBit)
473         {
474             case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER:
475             case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA:
476             {
477                 gl::Buffer *bufferGL = mState.getElementArrayBuffer();
478                 if (bufferGL && bufferGL->getSize() > 0)
479                 {
480                     // Note that just updating buffer data may still result in a new
481                     // vk::BufferHelper allocation.
482                     BufferVk *bufferVk = vk::GetImpl(bufferGL);
483                     mCurrentElementArrayBuffer =
484                         &bufferVk->getBufferAndOffset(&mCurrentElementArrayBufferOffset);
485                 }
486                 else
487                 {
488                     mCurrentElementArrayBuffer       = nullptr;
489                     mCurrentElementArrayBufferOffset = 0;
490                 }
491 
492                 mLineLoopBufferFirstIndex.reset();
493                 mLineLoopBufferLastIndex.reset();
494                 ANGLE_TRY(contextVk->onIndexBufferChange(mCurrentElementArrayBuffer));
495                 mDirtyLineLoopTranslation = true;
496                 break;
497             }
498 
499 #define ANGLE_VERTEX_DIRTY_ATTRIB_FUNC(INDEX)                                                 \
500     case gl::VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX:                                         \
501     {                                                                                         \
502         const bool bufferOnly =                                                               \
503             (*attribBits)[INDEX].to_ulong() ==                                                \
504             angle::Bit<unsigned long>(gl::VertexArray::DIRTY_ATTRIB_POINTER_BUFFER);          \
505         ANGLE_TRY(syncDirtyAttrib(contextVk, attribs[INDEX],                                  \
506                                   bindings[attribs[INDEX].bindingIndex], INDEX, bufferOnly)); \
507         (*attribBits)[INDEX].reset();                                                         \
508         break;                                                                                \
509     }
510 
511                 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_ATTRIB_FUNC)
512 
513 #define ANGLE_VERTEX_DIRTY_BINDING_FUNC(INDEX)                                          \
514     case gl::VertexArray::DIRTY_BIT_BINDING_0 + INDEX:                                  \
515         for (size_t attribIndex : bindings[INDEX].getBoundAttributesMask())             \
516         {                                                                               \
517             ANGLE_TRY(syncDirtyAttrib(contextVk, attribs[attribIndex], bindings[INDEX], \
518                                       attribIndex, false));                             \
519         }                                                                               \
520         (*bindingBits)[INDEX].reset();                                                  \
521         break;
522 
523                 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BINDING_FUNC)
524 
525 #define ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC(INDEX)                                       \
526     case gl::VertexArray::DIRTY_BIT_BUFFER_DATA_0 + INDEX:                               \
527         ANGLE_TRY(syncDirtyAttrib(contextVk, attribs[INDEX],                             \
528                                   bindings[attribs[INDEX].bindingIndex], INDEX, false)); \
529         break;
530 
531                 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC)
532 
533             default:
534                 UNREACHABLE();
535                 break;
536         }
537     }
538 
539     return angle::Result::Continue;
540 }
541 
542 #undef ANGLE_VERTEX_DIRTY_ATTRIB_FUNC
543 #undef ANGLE_VERTEX_DIRTY_BINDING_FUNC
544 #undef ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC
545 
setDefaultPackedInput(ContextVk * contextVk,size_t attribIndex)546 ANGLE_INLINE angle::Result VertexArrayVk::setDefaultPackedInput(ContextVk *contextVk,
547                                                                 size_t attribIndex)
548 {
549     const gl::State &glState = contextVk->getState();
550     const gl::VertexAttribCurrentValueData &defaultValue =
551         glState.getVertexAttribCurrentValues()[attribIndex];
552 
553     angle::FormatID format = GetCurrentValueFormatID(defaultValue.Type);
554 
555     return contextVk->onVertexAttributeChange(attribIndex, 0, 0, format, false, 0, nullptr);
556 }
557 
updateActiveAttribInfo(ContextVk * contextVk)558 angle::Result VertexArrayVk::updateActiveAttribInfo(ContextVk *contextVk)
559 {
560     const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes();
561     const std::vector<gl::VertexBinding> &bindings  = mState.getVertexBindings();
562 
563     // Update pipeline cache with current active attribute info
564     for (size_t attribIndex : mState.getEnabledAttributesMask())
565     {
566         const gl::VertexAttribute &attrib = attribs[attribIndex];
567         const gl::VertexBinding &binding  = bindings[attribs[attribIndex].bindingIndex];
568 
569         ANGLE_TRY(contextVk->onVertexAttributeChange(
570             attribIndex, mCurrentArrayBufferStrides[attribIndex], binding.getDivisor(),
571             attrib.format->id, mCurrentArrayBufferCompressed.test(attribIndex),
572             mCurrentArrayBufferRelativeOffsets[attribIndex], mCurrentArrayBuffers[attribIndex]));
573     }
574 
575     return angle::Result::Continue;
576 }
577 
syncDirtyAttrib(ContextVk * contextVk,const gl::VertexAttribute & attrib,const gl::VertexBinding & binding,size_t attribIndex,bool bufferOnly)578 angle::Result VertexArrayVk::syncDirtyAttrib(ContextVk *contextVk,
579                                              const gl::VertexAttribute &attrib,
580                                              const gl::VertexBinding &binding,
581                                              size_t attribIndex,
582                                              bool bufferOnly)
583 {
584     RendererVk *renderer = contextVk->getRenderer();
585     if (attrib.enabled)
586     {
587         const vk::Format &vertexFormat = renderer->getFormat(attrib.format->id);
588 
589         GLuint stride;
590         // Init attribute offset to the front-end value
591         mCurrentArrayBufferRelativeOffsets[attribIndex] = attrib.relativeOffset;
592         gl::Buffer *bufferGL                            = binding.getBuffer().get();
593         // Emulated and/or client-side attribs will be streamed
594         bool isStreamingVertexAttrib =
595             (binding.getDivisor() > renderer->getMaxVertexAttribDivisor()) || (bufferGL == nullptr);
596         mStreamingVertexAttribsMask.set(attribIndex, isStreamingVertexAttrib);
597         bool compressed = false;
598 
599         if (bufferGL)
600         {
601             mContentsObservers->disableForBuffer(bufferGL, static_cast<uint32_t>(attribIndex));
602         }
603 
604         if (!isStreamingVertexAttrib && bufferGL->getSize() > 0)
605         {
606             BufferVk *bufferVk                  = vk::GetImpl(bufferGL);
607             const angle::Format &intendedFormat = vertexFormat.getIntendedFormat();
608             bool bindingIsAligned               = BindingIsAligned(
609                 binding, intendedFormat, intendedFormat.channelCount, attrib.relativeOffset);
610 
611             if (renderer->getFeatures().compressVertexData.enabled &&
612                 gl::IsStaticBufferUsage(bufferGL->getUsage()) &&
613                 vertexFormat.canCompressBufferData())
614             {
615                 compressed = true;
616             }
617 
618             bool needsConversion =
619                 vertexFormat.getVertexLoadRequiresConversion(compressed) || !bindingIsAligned;
620 
621             if (needsConversion)
622             {
623                 mContentsObservers->enableForBuffer(bufferGL, static_cast<uint32_t>(attribIndex));
624 
625                 WarnOnVertexFormatConversion(contextVk, vertexFormat, compressed, true);
626 
627                 ConversionBuffer *conversion = bufferVk->getVertexConversionBuffer(
628                     renderer, intendedFormat.id, binding.getStride(),
629                     binding.getOffset() + attrib.relativeOffset, !bindingIsAligned);
630                 if (conversion->dirty)
631                 {
632                     if (compressed)
633                     {
634                         INFO() << "Compressing vertex data in buffer " << bufferGL->id().value
635                                << " from " << ToUnderlying(vertexFormat.getIntendedFormatID())
636                                << " to "
637                                << ToUnderlying(vertexFormat.getActualBufferFormat(true).id) << ".";
638                     }
639 
640                     if (bindingIsAligned)
641                     {
642                         ANGLE_TRY(convertVertexBufferGPU(contextVk, bufferVk, binding, attribIndex,
643                                                          vertexFormat, conversion,
644                                                          attrib.relativeOffset, compressed));
645                     }
646                     else
647                     {
648                         ANGLE_VK_PERF_WARNING(
649                             contextVk, GL_DEBUG_SEVERITY_HIGH,
650                             "GPU stall due to vertex format conversion of unaligned data");
651 
652                         ANGLE_TRY(convertVertexBufferCPU(contextVk, bufferVk, binding, attribIndex,
653                                                          vertexFormat, conversion,
654                                                          attrib.relativeOffset, compressed));
655                     }
656 
657                     // If conversion happens, the destination buffer stride may be changed,
658                     // therefore an attribute change needs to be called. Note that it may trigger
659                     // unnecessary vulkan PSO update when the destination buffer stride does not
660                     // change, but for simplicity just make it conservative
661                     bufferOnly = false;
662                 }
663 
664                 vk::BufferHelper *bufferHelper          = conversion->data.getCurrentBuffer();
665                 mCurrentArrayBuffers[attribIndex]       = bufferHelper;
666                 mCurrentArrayBufferHandles[attribIndex] = bufferHelper->getBuffer().getHandle();
667                 mCurrentArrayBufferOffsets[attribIndex] = conversion->lastAllocationOffset;
668                 // Converted attribs are packed in their own VK buffer so offset is zero
669                 mCurrentArrayBufferRelativeOffsets[attribIndex] = 0;
670 
671                 // Converted buffer is tightly packed
672                 stride = vertexFormat.getActualBufferFormat(compressed).pixelBytes;
673             }
674             else
675             {
676                 if (bufferVk->getSize() == 0)
677                 {
678                     vk::BufferHelper &emptyBuffer = contextVk->getEmptyBuffer();
679 
680                     mCurrentArrayBuffers[attribIndex]       = &emptyBuffer;
681                     mCurrentArrayBufferHandles[attribIndex] = emptyBuffer.getBuffer().getHandle();
682                     mCurrentArrayBufferOffsets[attribIndex] = 0;
683                     stride                                  = 0;
684                 }
685                 else
686                 {
687                     VkDeviceSize bufferOffset         = 0;
688                     vk::BufferHelper &bufferHelper    = bufferVk->getBufferAndOffset(&bufferOffset);
689                     mCurrentArrayBuffers[attribIndex] = &bufferHelper;
690                     mCurrentArrayBufferHandles[attribIndex] = bufferHelper.getBuffer().getHandle();
691 
692                     ASSERT(bufferOffset < bufferHelper.getSize());
693                     VkDeviceSize availableSize = bufferHelper.getSize() - bufferOffset;
694 
695                     // Vulkan requires the offset is within the buffer. We use robust access
696                     // behaviour to reset the offset if it starts outside the buffer.
697                     mCurrentArrayBufferOffsets[attribIndex] =
698                         binding.getOffset() < static_cast<GLint64>(availableSize)
699                             ? binding.getOffset() + bufferOffset
700                             : 0;
701 
702                     stride = binding.getStride();
703                 }
704             }
705         }
706         else
707         {
708             vk::BufferHelper &emptyBuffer           = contextVk->getEmptyBuffer();
709             mCurrentArrayBuffers[attribIndex]       = &emptyBuffer;
710             mCurrentArrayBufferHandles[attribIndex] = emptyBuffer.getBuffer().getHandle();
711             mCurrentArrayBufferOffsets[attribIndex] = 0;
712             // Client side buffer will be transfered to a tightly packed buffer later
713             stride = vertexFormat.getActualBufferFormat(compressed).pixelBytes;
714         }
715 
716         if (bufferOnly)
717         {
718             ANGLE_TRY(contextVk->onVertexBufferChange(mCurrentArrayBuffers[attribIndex]));
719         }
720         else
721         {
722             ANGLE_TRY(contextVk->onVertexAttributeChange(
723                 attribIndex, stride, binding.getDivisor(), attrib.format->id, compressed,
724                 mCurrentArrayBufferRelativeOffsets[attribIndex],
725                 mCurrentArrayBuffers[attribIndex]));
726             // Cache the stride of the attribute
727             mCurrentArrayBufferStrides[attribIndex]    = stride;
728             mCurrentArrayBufferCompressed[attribIndex] = compressed;
729         }
730     }
731     else
732     {
733         contextVk->invalidateDefaultAttribute(attribIndex);
734 
735         // These will be filled out by the ContextVk.
736         vk::BufferHelper &emptyBuffer                   = contextVk->getEmptyBuffer();
737         mCurrentArrayBuffers[attribIndex]               = &emptyBuffer;
738         mCurrentArrayBufferHandles[attribIndex]         = emptyBuffer.getBuffer().getHandle();
739         mCurrentArrayBufferOffsets[attribIndex]         = 0;
740         mCurrentArrayBufferStrides[attribIndex]         = 0;
741         mCurrentArrayBufferCompressed[attribIndex]      = false;
742         mCurrentArrayBufferRelativeOffsets[attribIndex] = 0;
743 
744         ANGLE_TRY(setDefaultPackedInput(contextVk, attribIndex));
745     }
746 
747     return angle::Result::Continue;
748 }
749 
750 // Handle copying client attribs and/or expanding attrib buffer in case where attribute
751 //  divisor value has to be emulated.
updateStreamedAttribs(const gl::Context * context,GLint firstVertex,GLsizei vertexOrIndexCount,GLsizei instanceCount,gl::DrawElementsType indexTypeOrInvalid,const void * indices)752 angle::Result VertexArrayVk::updateStreamedAttribs(const gl::Context *context,
753                                                    GLint firstVertex,
754                                                    GLsizei vertexOrIndexCount,
755                                                    GLsizei instanceCount,
756                                                    gl::DrawElementsType indexTypeOrInvalid,
757                                                    const void *indices)
758 {
759     ContextVk *contextVk = vk::GetImpl(context);
760     const gl::AttributesMask activeAttribs =
761         context->getStateCache().getActiveClientAttribsMask() |
762         context->getStateCache().getActiveBufferedAttribsMask();
763     const gl::AttributesMask activeStreamedAttribs = mStreamingVertexAttribsMask & activeAttribs;
764 
765     // Early return for corner case where emulated buffered attribs are not active
766     if (!activeStreamedAttribs.any())
767         return angle::Result::Continue;
768 
769     GLint startVertex;
770     size_t vertexCount;
771     ANGLE_TRY(GetVertexRangeInfo(context, firstVertex, vertexOrIndexCount, indexTypeOrInvalid,
772                                  indices, 0, &startVertex, &vertexCount));
773 
774     RendererVk *renderer = contextVk->getRenderer();
775     mDynamicVertexData.releaseInFlightBuffers(contextVk);
776 
777     const auto &attribs  = mState.getVertexAttributes();
778     const auto &bindings = mState.getVertexBindings();
779 
780     // TODO: When we have a bunch of interleaved attributes, they end up
781     // un-interleaved, wasting space and copying time.  Consider improving on that.
782     for (size_t attribIndex : activeStreamedAttribs)
783     {
784         const gl::VertexAttribute &attrib = attribs[attribIndex];
785         ASSERT(attrib.enabled);
786         const gl::VertexBinding &binding = bindings[attrib.bindingIndex];
787 
788         const vk::Format &vertexFormat = renderer->getFormat(attrib.format->id);
789         GLuint stride                  = vertexFormat.getActualBufferFormat(false).pixelBytes;
790 
791         bool compressed = false;
792         WarnOnVertexFormatConversion(contextVk, vertexFormat, compressed, false);
793 
794         ASSERT(vertexFormat.getVertexInputAlignment(false) <= vk::kVertexBufferAlignment);
795 
796         const uint8_t *src     = static_cast<const uint8_t *>(attrib.pointer);
797         const uint32_t divisor = binding.getDivisor();
798         if (divisor > 0)
799         {
800             // Instanced attrib
801             if (divisor > renderer->getMaxVertexAttribDivisor())
802             {
803                 // Emulated attrib
804                 BufferVk *bufferVk = nullptr;
805                 if (binding.getBuffer().get() != nullptr)
806                 {
807                     // Map buffer to expand attribs for divisor emulation
808                     bufferVk      = vk::GetImpl(binding.getBuffer().get());
809                     void *buffSrc = nullptr;
810                     ANGLE_TRY(bufferVk->mapImpl(contextVk, GL_MAP_READ_BIT, &buffSrc));
811                     src = reinterpret_cast<const uint8_t *>(buffSrc) + binding.getOffset();
812                 }
813                 // Divisor will be set to 1 & so update buffer to have 1 attrib per instance
814                 size_t bytesToAllocate = instanceCount * stride;
815 
816                 ANGLE_TRY(StreamVertexData(contextVk, &mDynamicVertexData, src, bytesToAllocate, 0,
817                                            instanceCount, binding.getStride(), stride,
818                                            vertexFormat.getVertexLoadFunction(compressed),
819                                            &mCurrentArrayBuffers[attribIndex],
820                                            &mCurrentArrayBufferOffsets[attribIndex], divisor));
821                 if (bufferVk)
822                 {
823                     ANGLE_TRY(bufferVk->unmapImpl(contextVk));
824                 }
825             }
826             else
827             {
828                 ASSERT(binding.getBuffer().get() == nullptr);
829                 size_t count           = UnsignedCeilDivide(instanceCount, divisor);
830                 size_t bytesToAllocate = count * stride;
831 
832                 ANGLE_TRY(StreamVertexData(contextVk, &mDynamicVertexData, src, bytesToAllocate, 0,
833                                            count, binding.getStride(), stride,
834                                            vertexFormat.getVertexLoadFunction(compressed),
835                                            &mCurrentArrayBuffers[attribIndex],
836                                            &mCurrentArrayBufferOffsets[attribIndex], 1));
837             }
838         }
839         else
840         {
841             ASSERT(binding.getBuffer().get() == nullptr);
842             // Allocate space for startVertex + vertexCount so indexing will work.  If we don't
843             // start at zero all the indices will be off.
844             // Only vertexCount vertices will be used by the upcoming draw so that is all we copy.
845             size_t bytesToAllocate = (startVertex + vertexCount) * stride;
846             src += startVertex * binding.getStride();
847             size_t destOffset = startVertex * stride;
848 
849             ANGLE_TRY(StreamVertexData(
850                 contextVk, &mDynamicVertexData, src, bytesToAllocate, destOffset, vertexCount,
851                 binding.getStride(), stride, vertexFormat.getVertexLoadFunction(compressed),
852                 &mCurrentArrayBuffers[attribIndex], &mCurrentArrayBufferOffsets[attribIndex], 1));
853         }
854 
855         mCurrentArrayBufferHandles[attribIndex] =
856             mCurrentArrayBuffers[attribIndex]->getBuffer().getHandle();
857     }
858 
859     return angle::Result::Continue;
860 }
861 
handleLineLoop(ContextVk * contextVk,GLint firstVertex,GLsizei vertexOrIndexCount,gl::DrawElementsType indexTypeOrInvalid,const void * indices,uint32_t * indexCountOut)862 angle::Result VertexArrayVk::handleLineLoop(ContextVk *contextVk,
863                                             GLint firstVertex,
864                                             GLsizei vertexOrIndexCount,
865                                             gl::DrawElementsType indexTypeOrInvalid,
866                                             const void *indices,
867                                             uint32_t *indexCountOut)
868 {
869     if (indexTypeOrInvalid != gl::DrawElementsType::InvalidEnum)
870     {
871         // Handle GL_LINE_LOOP drawElements.
872         if (mDirtyLineLoopTranslation)
873         {
874             gl::Buffer *elementArrayBuffer = mState.getElementArrayBuffer();
875 
876             if (!elementArrayBuffer)
877             {
878                 ANGLE_TRY(mLineLoopHelper.streamIndices(
879                     contextVk, indexTypeOrInvalid, vertexOrIndexCount,
880                     reinterpret_cast<const uint8_t *>(indices), &mCurrentElementArrayBuffer,
881                     &mCurrentElementArrayBufferOffset, indexCountOut));
882             }
883             else
884             {
885                 // When using an element array buffer, 'indices' is an offset to the first element.
886                 intptr_t offset                = reinterpret_cast<intptr_t>(indices);
887                 BufferVk *elementArrayBufferVk = vk::GetImpl(elementArrayBuffer);
888                 ANGLE_TRY(mLineLoopHelper.getIndexBufferForElementArrayBuffer(
889                     contextVk, elementArrayBufferVk, indexTypeOrInvalid, vertexOrIndexCount, offset,
890                     &mCurrentElementArrayBuffer, &mCurrentElementArrayBufferOffset, indexCountOut));
891             }
892         }
893 
894         // If we've had a drawArrays call with a line loop before, we want to make sure this is
895         // invalidated the next time drawArrays is called since we use the same index buffer for
896         // both calls.
897         mLineLoopBufferFirstIndex.reset();
898         mLineLoopBufferLastIndex.reset();
899         return angle::Result::Continue;
900     }
901 
902     // Note: Vertex indexes can be arbitrarily large.
903     uint32_t clampedVertexCount = gl::clampCast<uint32_t>(vertexOrIndexCount);
904 
905     // Handle GL_LINE_LOOP drawArrays.
906     size_t lastVertex = static_cast<size_t>(firstVertex + clampedVertexCount);
907     if (!mLineLoopBufferFirstIndex.valid() || !mLineLoopBufferLastIndex.valid() ||
908         mLineLoopBufferFirstIndex != firstVertex || mLineLoopBufferLastIndex != lastVertex)
909     {
910         ANGLE_TRY(mLineLoopHelper.getIndexBufferForDrawArrays(
911             contextVk, clampedVertexCount, firstVertex, &mCurrentElementArrayBuffer,
912             &mCurrentElementArrayBufferOffset));
913 
914         mLineLoopBufferFirstIndex = firstVertex;
915         mLineLoopBufferLastIndex  = lastVertex;
916     }
917     *indexCountOut = vertexOrIndexCount + 1;
918 
919     return angle::Result::Continue;
920 }
921 
updateDefaultAttrib(ContextVk * contextVk,size_t attribIndex,VkBuffer bufferHandle,vk::BufferHelper * buffer,uint32_t offset)922 angle::Result VertexArrayVk::updateDefaultAttrib(ContextVk *contextVk,
923                                                  size_t attribIndex,
924                                                  VkBuffer bufferHandle,
925                                                  vk::BufferHelper *buffer,
926                                                  uint32_t offset)
927 {
928     if (!mState.getEnabledAttributesMask().test(attribIndex))
929     {
930         mCurrentArrayBufferHandles[attribIndex] = bufferHandle;
931         mCurrentArrayBufferOffsets[attribIndex] = offset;
932         mCurrentArrayBuffers[attribIndex]       = buffer;
933 
934         ANGLE_TRY(setDefaultPackedInput(contextVk, attribIndex));
935     }
936 
937     return angle::Result::Continue;
938 }
939 }  // namespace rx
940