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