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