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