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