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