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