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/vk_format_utils.h"
19 #include "libANGLE/renderer/vulkan/vk_renderer.h"
20 #include "libANGLE/renderer/vulkan/vk_resource.h"
21
22 namespace rx
23 {
24 namespace
25 {
26 constexpr int kStreamIndexBufferCachedIndexCount = 6;
27 constexpr int kMaxCachedStreamIndexBuffers = 4;
28 constexpr size_t kDefaultValueSize = sizeof(gl::VertexAttribCurrentValueData::Values);
29
BindingIsAligned(const gl::VertexBinding & binding,const angle::Format & angleFormat,unsigned int attribSize,GLuint relativeOffset)30 ANGLE_INLINE bool BindingIsAligned(const gl::VertexBinding &binding,
31 const angle::Format &angleFormat,
32 unsigned int attribSize,
33 GLuint relativeOffset)
34 {
35 GLintptr totalOffset = binding.getOffset() + relativeOffset;
36 GLuint mask = angleFormat.componentAlignmentMask;
37 if (mask != std::numeric_limits<GLuint>::max())
38 {
39 return ((totalOffset & mask) == 0 && (binding.getStride() & mask) == 0);
40 }
41 else
42 {
43 // To perform the GPU conversion for formats with components that aren't byte-aligned
44 // (for example, A2BGR10 or RGB10A2), one element has to be placed in 4 bytes to perform
45 // the compute shader. So, binding offset and stride has to be aligned to formatSize.
46 unsigned int formatSize = angleFormat.pixelBytes;
47 return (totalOffset % formatSize == 0) && (binding.getStride() % formatSize == 0);
48 }
49 }
50
ClientBindingAligned(const gl::VertexAttribute & attrib,GLuint stride,size_t alignment)51 ANGLE_INLINE bool ClientBindingAligned(const gl::VertexAttribute &attrib,
52 GLuint stride,
53 size_t alignment)
54 {
55 return reinterpret_cast<intptr_t>(attrib.pointer) % alignment == 0 && stride % alignment == 0;
56 }
57
ShouldCombineAttributes(vk::Renderer * renderer,const gl::VertexAttribute & attrib,const gl::VertexBinding & binding)58 bool ShouldCombineAttributes(vk::Renderer *renderer,
59 const gl::VertexAttribute &attrib,
60 const gl::VertexBinding &binding)
61 {
62 if (!renderer->getFeatures().enableMergeClientAttribBuffer.enabled)
63 {
64 return false;
65 }
66 const vk::Format &vertexFormat = renderer->getFormat(attrib.format->id);
67 return !vertexFormat.getVertexLoadRequiresConversion(false) && binding.getDivisor() == 0 &&
68 ClientBindingAligned(attrib, binding.getStride(),
69 vertexFormat.getVertexInputAlignment(false));
70 }
71
WarnOnVertexFormatConversion(ContextVk * contextVk,const vk::Format & vertexFormat,bool compressed,bool insertEventMarker)72 void WarnOnVertexFormatConversion(ContextVk *contextVk,
73 const vk::Format &vertexFormat,
74 bool compressed,
75 bool insertEventMarker)
76 {
77 if (!vertexFormat.getVertexLoadRequiresConversion(compressed))
78 {
79 return;
80 }
81
82 ANGLE_VK_PERF_WARNING(
83 contextVk, GL_DEBUG_SEVERITY_LOW,
84 "The Vulkan driver does not support vertex attribute format 0x%04X, emulating with 0x%04X",
85 vertexFormat.getIntendedFormat().glInternalFormat,
86 vertexFormat.getActualBufferFormat(compressed).glInternalFormat);
87 }
88
StreamVertexData(ContextVk * contextVk,vk::BufferHelper * dstBufferHelper,const uint8_t * srcData,size_t bytesToCopy,size_t dstOffset,size_t vertexCount,size_t srcStride,VertexCopyFunction vertexLoadFunction)89 angle::Result StreamVertexData(ContextVk *contextVk,
90 vk::BufferHelper *dstBufferHelper,
91 const uint8_t *srcData,
92 size_t bytesToCopy,
93 size_t dstOffset,
94 size_t vertexCount,
95 size_t srcStride,
96 VertexCopyFunction vertexLoadFunction)
97 {
98 vk::Renderer *renderer = contextVk->getRenderer();
99
100 uint8_t *dst = dstBufferHelper->getMappedMemory() + dstOffset;
101
102 if (vertexLoadFunction != nullptr)
103 {
104 vertexLoadFunction(srcData, srcStride, vertexCount, dst);
105 }
106 else
107 {
108 memcpy(dst, srcData, bytesToCopy);
109 }
110
111 ANGLE_TRY(dstBufferHelper->flush(renderer));
112
113 return angle::Result::Continue;
114 }
115
StreamVertexDataWithDivisor(ContextVk * contextVk,vk::BufferHelper * dstBufferHelper,const uint8_t * srcData,size_t bytesToAllocate,size_t srcStride,size_t dstStride,VertexCopyFunction vertexLoadFunction,uint32_t divisor,size_t numSrcVertices)116 angle::Result StreamVertexDataWithDivisor(ContextVk *contextVk,
117 vk::BufferHelper *dstBufferHelper,
118 const uint8_t *srcData,
119 size_t bytesToAllocate,
120 size_t srcStride,
121 size_t dstStride,
122 VertexCopyFunction vertexLoadFunction,
123 uint32_t divisor,
124 size_t numSrcVertices)
125 {
126 vk::Renderer *renderer = contextVk->getRenderer();
127
128 uint8_t *dst = dstBufferHelper->getMappedMemory();
129
130 // Each source vertex is used `divisor` times before advancing. Clamp to avoid OOB reads.
131 size_t clampedSize = std::min(numSrcVertices * dstStride * divisor, bytesToAllocate);
132
133 ASSERT(clampedSize % dstStride == 0);
134 ASSERT(divisor > 0);
135
136 uint32_t srcVertexUseCount = 0;
137 for (size_t dataCopied = 0; dataCopied < clampedSize; dataCopied += dstStride)
138 {
139 vertexLoadFunction(srcData, srcStride, 1, dst);
140 srcVertexUseCount++;
141 if (srcVertexUseCount == divisor)
142 {
143 srcData += srcStride;
144 srcVertexUseCount = 0;
145 }
146 dst += dstStride;
147 }
148
149 // Satisfy robustness constraints (only if extension enabled)
150 if (contextVk->getExtensions().robustnessAny())
151 {
152 if (clampedSize < bytesToAllocate)
153 {
154 memset(dst, 0, bytesToAllocate - clampedSize);
155 }
156 }
157
158 ANGLE_TRY(dstBufferHelper->flush(renderer));
159
160 return angle::Result::Continue;
161 }
162
GetVertexCount(BufferVk * srcBuffer,const gl::VertexBinding & binding,uint32_t srcFormatSize)163 size_t GetVertexCount(BufferVk *srcBuffer, const gl::VertexBinding &binding, uint32_t srcFormatSize)
164 {
165 // Bytes usable for vertex data.
166 GLint64 bytes = srcBuffer->getSize() - binding.getOffset();
167 if (bytes < srcFormatSize)
168 {
169 return 0;
170 }
171
172 // Count the last vertex. It may occupy less than a full stride.
173 // This is also correct if stride happens to be less than srcFormatSize.
174 size_t numVertices = 1;
175 bytes -= srcFormatSize;
176
177 GLuint stride = binding.getStride();
178 if (stride == 0)
179 {
180 stride = srcFormatSize;
181 }
182
183 // Count how many strides fit remaining space.
184 if (bytes > 0)
185 {
186 numVertices += static_cast<size_t>(bytes) / stride;
187 }
188
189 return numVertices;
190 }
191 } // anonymous namespace
192
VertexArrayVk(ContextVk * contextVk,const gl::VertexArrayState & state)193 VertexArrayVk::VertexArrayVk(ContextVk *contextVk, const gl::VertexArrayState &state)
194 : VertexArrayImpl(state),
195 mCurrentArrayBufferHandles{},
196 mCurrentArrayBufferOffsets{},
197 mCurrentArrayBufferRelativeOffsets{},
198 mCurrentArrayBuffers{},
199 mCurrentArrayBufferStrides{},
200 mCurrentArrayBufferDivisors{},
201 mCurrentElementArrayBuffer(nullptr),
202 mLineLoopHelper(contextVk->getRenderer()),
203 mDirtyLineLoopTranslation(true)
204 {
205 vk::BufferHelper &emptyBuffer = contextVk->getEmptyBuffer();
206
207 mCurrentArrayBufferHandles.fill(emptyBuffer.getBuffer().getHandle());
208 mCurrentArrayBufferOffsets.fill(0);
209 mCurrentArrayBufferRelativeOffsets.fill(0);
210 mCurrentArrayBuffers.fill(&emptyBuffer);
211 mCurrentArrayBufferStrides.fill(0);
212 mCurrentArrayBufferDivisors.fill(0);
213
214 mBindingDirtyBitsRequiresPipelineUpdate.set(gl::VertexArray::DIRTY_BINDING_DIVISOR);
215 if (!contextVk->getRenderer()->useVertexInputBindingStrideDynamicState())
216 {
217 mBindingDirtyBitsRequiresPipelineUpdate.set(gl::VertexArray::DIRTY_BINDING_STRIDE);
218 }
219
220 // All but DIRTY_ATTRIB_POINTER_BUFFER requires graphics pipeline update
221 mAttribDirtyBitsRequiresPipelineUpdate.set(gl::VertexArray::DIRTY_ATTRIB_ENABLED);
222 mAttribDirtyBitsRequiresPipelineUpdate.set(gl::VertexArray::DIRTY_ATTRIB_POINTER);
223 mAttribDirtyBitsRequiresPipelineUpdate.set(gl::VertexArray::DIRTY_ATTRIB_FORMAT);
224 mAttribDirtyBitsRequiresPipelineUpdate.set(gl::VertexArray::DIRTY_ATTRIB_BINDING);
225 }
226
~VertexArrayVk()227 VertexArrayVk::~VertexArrayVk() {}
228
destroy(const gl::Context * context)229 void VertexArrayVk::destroy(const gl::Context *context)
230 {
231 ContextVk *contextVk = vk::GetImpl(context);
232
233 vk::Renderer *renderer = contextVk->getRenderer();
234
235 for (std::unique_ptr<vk::BufferHelper> &buffer : mCachedStreamIndexBuffers)
236 {
237 buffer->release(renderer);
238 }
239
240 mStreamedIndexData.release(renderer);
241 mTranslatedByteIndexData.release(renderer);
242 mTranslatedByteIndirectData.release(renderer);
243 mLineLoopHelper.release(contextVk);
244 }
245
convertIndexBufferGPU(ContextVk * contextVk,BufferVk * bufferVk,const void * indices)246 angle::Result VertexArrayVk::convertIndexBufferGPU(ContextVk *contextVk,
247 BufferVk *bufferVk,
248 const void *indices)
249 {
250 intptr_t offsetIntoSrcData = reinterpret_cast<intptr_t>(indices);
251 size_t srcDataSize = static_cast<size_t>(bufferVk->getSize()) - offsetIntoSrcData;
252
253 // Allocate buffer for results
254 ANGLE_TRY(contextVk->initBufferForVertexConversion(&mTranslatedByteIndexData,
255 sizeof(GLushort) * srcDataSize,
256 vk::MemoryHostVisibility::NonVisible));
257 mCurrentElementArrayBuffer = &mTranslatedByteIndexData;
258
259 vk::BufferHelper *dst = &mTranslatedByteIndexData;
260 vk::BufferHelper *src = &bufferVk->getBuffer();
261
262 // Copy relevant section of the source into destination at allocated offset. Note that the
263 // offset returned by allocate() above is in bytes. As is the indices offset pointer.
264 UtilsVk::ConvertIndexParameters params = {};
265 params.srcOffset = static_cast<uint32_t>(offsetIntoSrcData);
266 params.dstOffset = 0;
267 params.maxIndex = static_cast<uint32_t>(bufferVk->getSize());
268
269 return contextVk->getUtils().convertIndexBuffer(contextVk, dst, src, params);
270 }
271
convertIndexBufferIndirectGPU(ContextVk * contextVk,vk::BufferHelper * srcIndirectBuf,VkDeviceSize srcIndirectBufOffset,vk::BufferHelper ** indirectBufferVkOut)272 angle::Result VertexArrayVk::convertIndexBufferIndirectGPU(ContextVk *contextVk,
273 vk::BufferHelper *srcIndirectBuf,
274 VkDeviceSize srcIndirectBufOffset,
275 vk::BufferHelper **indirectBufferVkOut)
276 {
277 size_t srcDataSize = static_cast<size_t>(mCurrentElementArrayBuffer->getSize());
278 ASSERT(mCurrentElementArrayBuffer ==
279 &vk::GetImpl(getState().getElementArrayBuffer())->getBuffer());
280
281 vk::BufferHelper *srcIndexBuf = mCurrentElementArrayBuffer;
282
283 // Allocate buffer for results
284 ANGLE_TRY(contextVk->initBufferForVertexConversion(&mTranslatedByteIndexData,
285 sizeof(GLushort) * srcDataSize,
286 vk::MemoryHostVisibility::NonVisible));
287 vk::BufferHelper *dstIndexBuf = &mTranslatedByteIndexData;
288
289 ANGLE_TRY(contextVk->initBufferForVertexConversion(&mTranslatedByteIndirectData,
290 sizeof(VkDrawIndexedIndirectCommand),
291 vk::MemoryHostVisibility::NonVisible));
292 vk::BufferHelper *dstIndirectBuf = &mTranslatedByteIndirectData;
293
294 // Save new element array buffer
295 mCurrentElementArrayBuffer = dstIndexBuf;
296 // Tell caller what new indirect buffer is
297 *indirectBufferVkOut = dstIndirectBuf;
298
299 // Copy relevant section of the source into destination at allocated offset. Note that the
300 // offset returned by allocate() above is in bytes. As is the indices offset pointer.
301 UtilsVk::ConvertIndexIndirectParameters params = {};
302 params.srcIndirectBufOffset = static_cast<uint32_t>(srcIndirectBufOffset);
303 params.srcIndexBufOffset = 0;
304 params.dstIndexBufOffset = 0;
305 params.maxIndex = static_cast<uint32_t>(srcDataSize);
306 params.dstIndirectBufOffset = 0;
307
308 return contextVk->getUtils().convertIndexIndirectBuffer(contextVk, srcIndirectBuf, srcIndexBuf,
309 dstIndirectBuf, dstIndexBuf, params);
310 }
311
handleLineLoopIndexIndirect(ContextVk * contextVk,gl::DrawElementsType glIndexType,vk::BufferHelper * srcIndirectBuf,VkDeviceSize indirectBufferOffset,vk::BufferHelper ** indirectBufferOut)312 angle::Result VertexArrayVk::handleLineLoopIndexIndirect(ContextVk *contextVk,
313 gl::DrawElementsType glIndexType,
314 vk::BufferHelper *srcIndirectBuf,
315 VkDeviceSize indirectBufferOffset,
316 vk::BufferHelper **indirectBufferOut)
317 {
318 ANGLE_TRY(mLineLoopHelper.streamIndicesIndirect(
319 contextVk, glIndexType, mCurrentElementArrayBuffer, srcIndirectBuf, indirectBufferOffset,
320 &mCurrentElementArrayBuffer, indirectBufferOut));
321
322 return angle::Result::Continue;
323 }
324
handleLineLoopIndirectDraw(const gl::Context * context,vk::BufferHelper * indirectBufferVk,VkDeviceSize indirectBufferOffset,vk::BufferHelper ** indirectBufferOut)325 angle::Result VertexArrayVk::handleLineLoopIndirectDraw(const gl::Context *context,
326 vk::BufferHelper *indirectBufferVk,
327 VkDeviceSize indirectBufferOffset,
328 vk::BufferHelper **indirectBufferOut)
329 {
330 size_t maxVertexCount = 0;
331 ContextVk *contextVk = vk::GetImpl(context);
332 const gl::AttributesMask activeAttribs =
333 context->getStateCache().getActiveBufferedAttribsMask();
334
335 const auto &attribs = mState.getVertexAttributes();
336 const auto &bindings = mState.getVertexBindings();
337
338 for (size_t attribIndex : activeAttribs)
339 {
340 const gl::VertexAttribute &attrib = attribs[attribIndex];
341 ASSERT(attrib.enabled);
342 VkDeviceSize bufSize = getCurrentArrayBuffers()[attribIndex]->getSize();
343 const gl::VertexBinding &binding = bindings[attrib.bindingIndex];
344 size_t stride = binding.getStride();
345 size_t vertexCount = static_cast<size_t>(bufSize / stride);
346 if (vertexCount > maxVertexCount)
347 {
348 maxVertexCount = vertexCount;
349 }
350 }
351 ANGLE_TRY(mLineLoopHelper.streamArrayIndirect(contextVk, maxVertexCount + 1, indirectBufferVk,
352 indirectBufferOffset, &mCurrentElementArrayBuffer,
353 indirectBufferOut));
354
355 return angle::Result::Continue;
356 }
357
convertIndexBufferCPU(ContextVk * contextVk,gl::DrawElementsType indexType,size_t indexCount,const void * sourcePointer,BufferBindingDirty * bindingDirty)358 angle::Result VertexArrayVk::convertIndexBufferCPU(ContextVk *contextVk,
359 gl::DrawElementsType indexType,
360 size_t indexCount,
361 const void *sourcePointer,
362 BufferBindingDirty *bindingDirty)
363 {
364 ASSERT(!mState.getElementArrayBuffer() || indexType == gl::DrawElementsType::UnsignedByte);
365 vk::Renderer *renderer = contextVk->getRenderer();
366 size_t elementSize = contextVk->getVkIndexTypeSize(indexType);
367 const size_t amount = elementSize * indexCount;
368
369 // Applications often time draw a quad with two triangles. This is try to catch all the
370 // common used element array buffer with pre-created BufferHelper objects to improve
371 // performance.
372 if (indexCount == kStreamIndexBufferCachedIndexCount &&
373 indexType == gl::DrawElementsType::UnsignedShort)
374 {
375 for (std::unique_ptr<vk::BufferHelper> &buffer : mCachedStreamIndexBuffers)
376 {
377 void *ptr = buffer->getMappedMemory();
378 if (memcmp(sourcePointer, ptr, amount) == 0)
379 {
380 // Found a matching cached buffer, use the cached internal index buffer.
381 *bindingDirty = mCurrentElementArrayBuffer == buffer.get()
382 ? BufferBindingDirty::No
383 : BufferBindingDirty::Yes;
384 mCurrentElementArrayBuffer = buffer.get();
385 return angle::Result::Continue;
386 }
387 }
388
389 // If we still have capacity, cache this index buffer for future use.
390 if (mCachedStreamIndexBuffers.size() < kMaxCachedStreamIndexBuffers)
391 {
392 std::unique_ptr<vk::BufferHelper> buffer = std::make_unique<vk::BufferHelper>();
393 ANGLE_TRY(contextVk->initBufferAllocation(
394 buffer.get(),
395 renderer->getVertexConversionBufferMemoryTypeIndex(
396 vk::MemoryHostVisibility::Visible),
397 amount, renderer->getVertexConversionBufferAlignment(), BufferUsageType::Static));
398 memcpy(buffer->getMappedMemory(), sourcePointer, amount);
399 ANGLE_TRY(buffer->flush(renderer));
400
401 mCachedStreamIndexBuffers.push_back(std::move(buffer));
402
403 *bindingDirty = BufferBindingDirty::Yes;
404 mCurrentElementArrayBuffer = mCachedStreamIndexBuffers.back().get();
405 return angle::Result::Continue;
406 }
407 }
408
409 ANGLE_TRY(contextVk->initBufferForVertexConversion(&mStreamedIndexData, amount,
410 vk::MemoryHostVisibility::Visible));
411 GLubyte *dst = mStreamedIndexData.getMappedMemory();
412
413 *bindingDirty = BufferBindingDirty::Yes;
414 mCurrentElementArrayBuffer = &mStreamedIndexData;
415
416 if (contextVk->shouldConvertUint8VkIndexType(indexType))
417 {
418 // Unsigned bytes don't have direct support in Vulkan so we have to expand the
419 // memory to a GLushort.
420 const GLubyte *in = static_cast<const GLubyte *>(sourcePointer);
421 GLushort *expandedDst = reinterpret_cast<GLushort *>(dst);
422 bool primitiveRestart = contextVk->getState().isPrimitiveRestartEnabled();
423
424 constexpr GLubyte kUnsignedByteRestartValue = 0xFF;
425 constexpr GLushort kUnsignedShortRestartValue = 0xFFFF;
426
427 if (primitiveRestart)
428 {
429 for (size_t index = 0; index < indexCount; index++)
430 {
431 GLushort value = static_cast<GLushort>(in[index]);
432 if (in[index] == kUnsignedByteRestartValue)
433 {
434 // Convert from 8-bit restart value to 16-bit restart value
435 value = kUnsignedShortRestartValue;
436 }
437 expandedDst[index] = value;
438 }
439 }
440 else
441 {
442 // Fast path for common case.
443 for (size_t index = 0; index < indexCount; index++)
444 {
445 expandedDst[index] = static_cast<GLushort>(in[index]);
446 }
447 }
448 }
449 else
450 {
451 // The primitive restart value is the same for OpenGL and Vulkan,
452 // so there's no need to perform any conversion.
453 memcpy(dst, sourcePointer, amount);
454 }
455 return mStreamedIndexData.flush(contextVk->getRenderer());
456 }
457
458 // We assume the buffer is completely full of the same kind of data and convert
459 // and/or align it as we copy it to a buffer. The assumption could be wrong
460 // but the alternative of copying it piecemeal on each draw would have a lot more
461 // overhead.
convertVertexBufferGPU(ContextVk * contextVk,BufferVk * srcBuffer,const gl::VertexBinding & binding,size_t attribIndex,const vk::Format & vertexFormat,ConversionBuffer * conversion,GLuint relativeOffset,bool compressed)462 angle::Result VertexArrayVk::convertVertexBufferGPU(ContextVk *contextVk,
463 BufferVk *srcBuffer,
464 const gl::VertexBinding &binding,
465 size_t attribIndex,
466 const vk::Format &vertexFormat,
467 ConversionBuffer *conversion,
468 GLuint relativeOffset,
469 bool compressed)
470 {
471 const angle::Format &srcFormat = vertexFormat.getIntendedFormat();
472 const angle::Format &dstFormat = vertexFormat.getActualBufferFormat(compressed);
473
474 ASSERT(binding.getStride() % (srcFormat.pixelBytes / srcFormat.channelCount) == 0);
475
476 unsigned srcFormatSize = srcFormat.pixelBytes;
477 unsigned dstFormatSize = dstFormat.pixelBytes;
478
479 size_t numVertices = GetVertexCount(srcBuffer, binding, srcFormatSize);
480 if (numVertices == 0)
481 {
482 return angle::Result::Continue;
483 }
484 ASSERT(vertexFormat.getVertexInputAlignment(compressed) <= vk::kVertexBufferAlignment);
485
486 // Allocate buffer for results
487 vk::BufferHelper *dstBuffer = conversion->data.get();
488 ANGLE_TRY(contextVk->initBufferForVertexConversion(dstBuffer, numVertices * dstFormatSize,
489 vk::MemoryHostVisibility::NonVisible));
490
491 ASSERT(conversion->dirty);
492 conversion->dirty = false;
493
494 vk::BufferHelper *srcBufferHelper = &srcBuffer->getBuffer();
495
496 UtilsVk::ConvertVertexParameters params;
497 params.vertexCount = numVertices;
498 params.srcFormat = &srcFormat;
499 params.dstFormat = &dstFormat;
500 params.srcStride = binding.getStride();
501 params.srcOffset = binding.getOffset() + relativeOffset;
502 params.dstOffset = 0;
503
504 ANGLE_TRY(
505 contextVk->getUtils().convertVertexBuffer(contextVk, dstBuffer, srcBufferHelper, params));
506
507 return angle::Result::Continue;
508 }
509
convertVertexBufferCPU(ContextVk * contextVk,BufferVk * srcBuffer,const gl::VertexBinding & binding,size_t attribIndex,const vk::Format & vertexFormat,ConversionBuffer * conversion,GLuint relativeOffset,bool compressed)510 angle::Result VertexArrayVk::convertVertexBufferCPU(ContextVk *contextVk,
511 BufferVk *srcBuffer,
512 const gl::VertexBinding &binding,
513 size_t attribIndex,
514 const vk::Format &vertexFormat,
515 ConversionBuffer *conversion,
516 GLuint relativeOffset,
517 bool compressed)
518 {
519 ANGLE_TRACE_EVENT0("gpu.angle", "VertexArrayVk::convertVertexBufferCpu");
520
521 unsigned srcFormatSize = vertexFormat.getIntendedFormat().pixelBytes;
522 unsigned dstFormatSize = vertexFormat.getActualBufferFormat(compressed).pixelBytes;
523
524 size_t numVertices = GetVertexCount(srcBuffer, binding, srcFormatSize);
525 if (numVertices == 0)
526 {
527 return angle::Result::Continue;
528 }
529
530 void *src = nullptr;
531 ANGLE_TRY(srcBuffer->mapImpl(contextVk, GL_MAP_READ_BIT, &src));
532 const uint8_t *srcBytes = reinterpret_cast<const uint8_t *>(src);
533 srcBytes += binding.getOffset() + relativeOffset;
534 ASSERT(vertexFormat.getVertexInputAlignment(compressed) <= vk::kVertexBufferAlignment);
535
536 vk::BufferHelper *dstBufferHelper = conversion->data.get();
537 // Allocate buffer for results
538 ANGLE_TRY(contextVk->initBufferForVertexConversion(dstBufferHelper, numVertices * dstFormatSize,
539 vk::MemoryHostVisibility::Visible));
540
541 ANGLE_TRY(StreamVertexData(contextVk, dstBufferHelper, srcBytes, numVertices * dstFormatSize, 0,
542 numVertices, binding.getStride(),
543 vertexFormat.getVertexLoadFunction(compressed)));
544 ANGLE_TRY(srcBuffer->unmapImpl(contextVk));
545 mCurrentArrayBuffers[attribIndex] = dstBufferHelper;
546 mCurrentArrayBufferSerial[attribIndex] = dstBufferHelper->getBufferSerial();
547
548 ASSERT(conversion->dirty);
549 conversion->dirty = false;
550
551 return angle::Result::Continue;
552 }
553
updateCurrentElementArrayBuffer()554 void VertexArrayVk::updateCurrentElementArrayBuffer()
555 {
556 ASSERT(mState.getElementArrayBuffer() != nullptr);
557 ASSERT(mState.getElementArrayBuffer()->getSize() > 0);
558
559 BufferVk *bufferVk = vk::GetImpl(mState.getElementArrayBuffer());
560 mCurrentElementArrayBuffer = &bufferVk->getBuffer();
561 }
562
syncState(const gl::Context * context,const gl::VertexArray::DirtyBits & dirtyBits,gl::VertexArray::DirtyAttribBitsArray * attribBits,gl::VertexArray::DirtyBindingBitsArray * bindingBits)563 angle::Result VertexArrayVk::syncState(const gl::Context *context,
564 const gl::VertexArray::DirtyBits &dirtyBits,
565 gl::VertexArray::DirtyAttribBitsArray *attribBits,
566 gl::VertexArray::DirtyBindingBitsArray *bindingBits)
567 {
568 ASSERT(dirtyBits.any());
569
570 ContextVk *contextVk = vk::GetImpl(context);
571 contextVk->getPerfCounters().vertexArraySyncStateCalls++;
572
573 const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes();
574 const std::vector<gl::VertexBinding> &bindings = mState.getVertexBindings();
575
576 for (auto iter = dirtyBits.begin(), endIter = dirtyBits.end(); iter != endIter; ++iter)
577 {
578 size_t dirtyBit = *iter;
579 switch (dirtyBit)
580 {
581 case gl::VertexArray::DIRTY_BIT_LOST_OBSERVATION:
582 {
583 // If vertex array was not observing while unbound, we need to check buffer's
584 // internal storage and take action if buffer storage has changed while not
585 // observing.
586 if (contextVk->getRenderer()->getFeatures().compressVertexData.enabled ||
587 mContentsObservers->any())
588 {
589 // We may have lost buffer content change when it became non-current. In that
590 // case we always assume buffer has changed. If compressVertexData.enabled is
591 // true, it also depends on buffer usage which may have changed.
592 iter.setLaterBits(
593 gl::VertexArray::DirtyBits(mState.getBufferBindingMask().to_ulong()
594 << gl::VertexArray::DIRTY_BIT_BINDING_0));
595 }
596 else
597 {
598 for (size_t bindingIndex : mState.getBufferBindingMask())
599 {
600 const gl::Buffer *bufferGL = bindings[bindingIndex].getBuffer().get();
601 vk::BufferSerial bufferSerial = vk::GetImpl(bufferGL)->getBufferSerial();
602 for (size_t attribIndex : bindings[bindingIndex].getBoundAttributesMask())
603 {
604 if (attribs[attribIndex].enabled &&
605 (!bufferSerial.valid() ||
606 bufferSerial != mCurrentArrayBufferSerial[attribIndex]))
607 {
608 iter.setLaterBit(gl::VertexArray::DIRTY_BIT_BINDING_0 +
609 bindingIndex);
610 break;
611 }
612 }
613 }
614 }
615 break;
616 }
617
618 case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER:
619 case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA:
620 {
621 gl::Buffer *bufferGL = mState.getElementArrayBuffer();
622 if (bufferGL && bufferGL->getSize() > 0)
623 {
624 // Note that just updating buffer data may still result in a new
625 // vk::BufferHelper allocation.
626 updateCurrentElementArrayBuffer();
627 }
628 else
629 {
630 mCurrentElementArrayBuffer = nullptr;
631 }
632
633 mLineLoopBufferFirstIndex.reset();
634 mLineLoopBufferLastIndex.reset();
635 ANGLE_TRY(contextVk->onIndexBufferChange(mCurrentElementArrayBuffer));
636 mDirtyLineLoopTranslation = true;
637 break;
638 }
639
640 #define ANGLE_VERTEX_DIRTY_ATTRIB_FUNC(INDEX) \
641 case gl::VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX: \
642 { \
643 gl::VertexArray::DirtyAttribBits dirtyAttribBitsRequiresPipelineUpdate = \
644 (*attribBits)[INDEX] & mAttribDirtyBitsRequiresPipelineUpdate; \
645 const bool bufferOnly = dirtyAttribBitsRequiresPipelineUpdate.none(); \
646 ANGLE_TRY(syncDirtyAttrib(contextVk, attribs[INDEX], \
647 bindings[attribs[INDEX].bindingIndex], INDEX, bufferOnly)); \
648 (*attribBits)[INDEX].reset(); \
649 break; \
650 }
651
652 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_ATTRIB_FUNC)
653
654 // Since BINDING already implies DATA and ATTRIB change, we remove these here to avoid redundant
655 // processing.
656 #define ANGLE_VERTEX_DIRTY_BINDING_FUNC(INDEX) \
657 case gl::VertexArray::DIRTY_BIT_BINDING_0 + INDEX: \
658 { \
659 gl::VertexArray::DirtyBindingBits dirtyBindingBitsRequirePipelineUpdate = \
660 (*bindingBits)[INDEX] & mBindingDirtyBitsRequiresPipelineUpdate; \
661 \
662 for (size_t attribIndex : bindings[INDEX].getBoundAttributesMask()) \
663 { \
664 gl::VertexArray::DirtyAttribBits dirtyAttribBitsRequiresPipelineUpdate = \
665 (*attribBits)[attribIndex] & mAttribDirtyBitsRequiresPipelineUpdate; \
666 const bool bufferOnly = dirtyBindingBitsRequirePipelineUpdate.none() && \
667 dirtyAttribBitsRequiresPipelineUpdate.none(); \
668 ANGLE_TRY(syncDirtyAttrib(contextVk, attribs[attribIndex], bindings[INDEX], \
669 attribIndex, bufferOnly)); \
670 iter.resetLaterBit(gl::VertexArray::DIRTY_BIT_BUFFER_DATA_0 + attribIndex); \
671 iter.resetLaterBit(gl::VertexArray::DIRTY_BIT_ATTRIB_0 + attribIndex); \
672 (*attribBits)[attribIndex].reset(); \
673 } \
674 (*bindingBits)[INDEX].reset(); \
675 break; \
676 }
677
678 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BINDING_FUNC)
679
680 #define ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC(INDEX) \
681 case gl::VertexArray::DIRTY_BIT_BUFFER_DATA_0 + INDEX: \
682 ANGLE_TRY(syncDirtyAttrib(contextVk, attribs[INDEX], \
683 bindings[attribs[INDEX].bindingIndex], INDEX, false)); \
684 iter.resetLaterBit(gl::VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX); \
685 (*attribBits)[INDEX].reset(); \
686 break;
687
688 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC)
689
690 default:
691 UNREACHABLE();
692 break;
693 }
694 }
695
696 return angle::Result::Continue;
697 } // namespace rx
698
699 #undef ANGLE_VERTEX_DIRTY_ATTRIB_FUNC
700 #undef ANGLE_VERTEX_DIRTY_BINDING_FUNC
701 #undef ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC
702
setDefaultPackedInput(ContextVk * contextVk,size_t attribIndex,angle::FormatID * formatOut)703 ANGLE_INLINE angle::Result VertexArrayVk::setDefaultPackedInput(ContextVk *contextVk,
704 size_t attribIndex,
705 angle::FormatID *formatOut)
706 {
707 const gl::State &glState = contextVk->getState();
708 const gl::VertexAttribCurrentValueData &defaultValue =
709 glState.getVertexAttribCurrentValues()[attribIndex];
710
711 *formatOut = GetCurrentValueFormatID(defaultValue.Type);
712
713 return contextVk->onVertexAttributeChange(attribIndex, 0, 0, *formatOut, false, 0, nullptr);
714 }
715
updateActiveAttribInfo(ContextVk * contextVk)716 angle::Result VertexArrayVk::updateActiveAttribInfo(ContextVk *contextVk)
717 {
718 const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes();
719 const std::vector<gl::VertexBinding> &bindings = mState.getVertexBindings();
720
721 // Update pipeline cache with current active attribute info
722 for (size_t attribIndex : mState.getEnabledAttributesMask())
723 {
724 const gl::VertexAttribute &attrib = attribs[attribIndex];
725 const gl::VertexBinding &binding = bindings[attribs[attribIndex].bindingIndex];
726 const angle::FormatID format = attrib.format->id;
727
728 ANGLE_TRY(contextVk->onVertexAttributeChange(
729 attribIndex, mCurrentArrayBufferStrides[attribIndex], binding.getDivisor(), format,
730 mCurrentArrayBufferCompressed.test(attribIndex),
731 mCurrentArrayBufferRelativeOffsets[attribIndex], mCurrentArrayBuffers[attribIndex]));
732
733 mCurrentArrayBufferFormats[attribIndex] = format;
734 }
735
736 return angle::Result::Continue;
737 }
738
syncDirtyAttrib(ContextVk * contextVk,const gl::VertexAttribute & attrib,const gl::VertexBinding & binding,size_t attribIndex,bool bufferOnly)739 angle::Result VertexArrayVk::syncDirtyAttrib(ContextVk *contextVk,
740 const gl::VertexAttribute &attrib,
741 const gl::VertexBinding &binding,
742 size_t attribIndex,
743 bool bufferOnly)
744 {
745 vk::Renderer *renderer = contextVk->getRenderer();
746 if (attrib.enabled)
747 {
748 const vk::Format &vertexFormat = renderer->getFormat(attrib.format->id);
749
750 // Init attribute offset to the front-end value
751 mCurrentArrayBufferRelativeOffsets[attribIndex] = attrib.relativeOffset;
752 gl::Buffer *bufferGL = binding.getBuffer().get();
753 // Emulated and/or client-side attribs will be streamed
754 bool isStreamingVertexAttrib =
755 (binding.getDivisor() > renderer->getMaxVertexAttribDivisor()) || (bufferGL == nullptr);
756 // If we sre switching between streaming and buffer mode, set bufferOnly to false since we
757 // are actually changing the buffer.
758 if (bufferOnly && isStreamingVertexAttrib != mStreamingVertexAttribsMask.test(attribIndex))
759 {
760 bufferOnly = false;
761 }
762 mStreamingVertexAttribsMask.set(attribIndex, isStreamingVertexAttrib);
763 bool compressed = false;
764
765 if (bufferGL)
766 {
767 mContentsObservers->disableForBuffer(bufferGL, static_cast<uint32_t>(attribIndex));
768 }
769
770 if (!isStreamingVertexAttrib && bufferGL->getSize() > 0)
771 {
772 BufferVk *bufferVk = vk::GetImpl(bufferGL);
773 const angle::Format &intendedFormat = vertexFormat.getIntendedFormat();
774 size_t numVertices = GetVertexCount(bufferVk, binding, intendedFormat.pixelBytes);
775 bool bindingIsAligned = BindingIsAligned(
776 binding, intendedFormat, intendedFormat.channelCount, attrib.relativeOffset);
777
778 if (renderer->getFeatures().compressVertexData.enabled &&
779 gl::IsStaticBufferUsage(bufferGL->getUsage()) &&
780 vertexFormat.canCompressBufferData())
781 {
782 compressed = true;
783 }
784
785 bool needsConversion =
786 numVertices > 0 &&
787 (vertexFormat.getVertexLoadRequiresConversion(compressed) || !bindingIsAligned);
788
789 if (needsConversion)
790 {
791 mContentsObservers->enableForBuffer(bufferGL, static_cast<uint32_t>(attribIndex));
792
793 WarnOnVertexFormatConversion(contextVk, vertexFormat, compressed, true);
794
795 ConversionBuffer *conversion = bufferVk->getVertexConversionBuffer(
796 renderer, intendedFormat.id, binding.getStride(),
797 binding.getOffset() + attrib.relativeOffset, !bindingIsAligned);
798 if (conversion->dirty)
799 {
800 if (compressed)
801 {
802 INFO() << "Compressing vertex data in buffer " << bufferGL->id().value
803 << " from " << ToUnderlying(vertexFormat.getIntendedFormatID())
804 << " to "
805 << ToUnderlying(vertexFormat.getActualBufferFormat(true).id) << ".";
806 }
807
808 if (bindingIsAligned)
809 {
810 ANGLE_TRY(convertVertexBufferGPU(contextVk, bufferVk, binding, attribIndex,
811 vertexFormat, conversion,
812 attrib.relativeOffset, compressed));
813 }
814 else
815 {
816 ANGLE_VK_PERF_WARNING(
817 contextVk, GL_DEBUG_SEVERITY_HIGH,
818 "GPU stall due to vertex format conversion of unaligned data");
819
820 ANGLE_TRY(convertVertexBufferCPU(contextVk, bufferVk, binding, attribIndex,
821 vertexFormat, conversion,
822 attrib.relativeOffset, compressed));
823 }
824
825 // If conversion happens, the destination buffer stride may be changed,
826 // therefore an attribute change needs to be called. Note that it may trigger
827 // unnecessary vulkan PSO update when the destination buffer stride does not
828 // change, but for simplicity just make it conservative
829 bufferOnly = false;
830 }
831
832 vk::BufferHelper *bufferHelper = conversion->data.get();
833 mCurrentArrayBuffers[attribIndex] = bufferHelper;
834 mCurrentArrayBufferSerial[attribIndex] = bufferHelper->getBufferSerial();
835 VkDeviceSize bufferOffset;
836 mCurrentArrayBufferHandles[attribIndex] =
837 bufferHelper
838 ->getBufferForVertexArray(contextVk, bufferHelper->getSize(), &bufferOffset)
839 .getHandle();
840 mCurrentArrayBufferOffsets[attribIndex] = bufferOffset;
841 // Converted attribs are packed in their own VK buffer so offset is zero
842 mCurrentArrayBufferRelativeOffsets[attribIndex] = 0;
843
844 // Converted buffer is tightly packed
845 mCurrentArrayBufferStrides[attribIndex] =
846 vertexFormat.getActualBufferFormat(compressed).pixelBytes;
847 }
848 else
849 {
850 if (numVertices == 0)
851 {
852 vk::BufferHelper &emptyBuffer = contextVk->getEmptyBuffer();
853
854 mCurrentArrayBuffers[attribIndex] = &emptyBuffer;
855 mCurrentArrayBufferSerial[attribIndex] = emptyBuffer.getBufferSerial();
856 mCurrentArrayBufferHandles[attribIndex] = emptyBuffer.getBuffer().getHandle();
857 mCurrentArrayBufferOffsets[attribIndex] = emptyBuffer.getOffset();
858 mCurrentArrayBufferStrides[attribIndex] = 0;
859 }
860 else
861 {
862 vk::BufferHelper &bufferHelper = bufferVk->getBuffer();
863 mCurrentArrayBuffers[attribIndex] = &bufferHelper;
864 mCurrentArrayBufferSerial[attribIndex] = bufferHelper.getBufferSerial();
865 VkDeviceSize bufferOffset;
866 mCurrentArrayBufferHandles[attribIndex] =
867 bufferHelper
868 .getBufferForVertexArray(contextVk, bufferVk->getSize(), &bufferOffset)
869 .getHandle();
870
871 // Vulkan requires the offset is within the buffer. We use robust access
872 // behaviour to reset the offset if it starts outside the buffer.
873 mCurrentArrayBufferOffsets[attribIndex] =
874 binding.getOffset() < static_cast<GLint64>(bufferVk->getSize())
875 ? binding.getOffset() + bufferOffset
876 : bufferOffset;
877
878 mCurrentArrayBufferStrides[attribIndex] = binding.getStride();
879 }
880 }
881 }
882 else
883 {
884 vk::BufferHelper &emptyBuffer = contextVk->getEmptyBuffer();
885 mCurrentArrayBuffers[attribIndex] = &emptyBuffer;
886 mCurrentArrayBufferSerial[attribIndex] = emptyBuffer.getBufferSerial();
887 mCurrentArrayBufferHandles[attribIndex] = emptyBuffer.getBuffer().getHandle();
888 mCurrentArrayBufferOffsets[attribIndex] = emptyBuffer.getOffset();
889
890 bool combined = ShouldCombineAttributes(renderer, attrib, binding);
891 mCurrentArrayBufferStrides[attribIndex] =
892 combined ? binding.getStride()
893 : vertexFormat.getActualBufferFormat(compressed).pixelBytes;
894 }
895
896 if (bufferOnly)
897 {
898 ANGLE_TRY(contextVk->onVertexBufferChange(mCurrentArrayBuffers[attribIndex]));
899 }
900 else
901 {
902 const angle::FormatID format = attrib.format->id;
903 ANGLE_TRY(contextVk->onVertexAttributeChange(
904 attribIndex, mCurrentArrayBufferStrides[attribIndex], binding.getDivisor(), format,
905 compressed, mCurrentArrayBufferRelativeOffsets[attribIndex],
906 mCurrentArrayBuffers[attribIndex]));
907
908 mCurrentArrayBufferFormats[attribIndex] = format;
909 mCurrentArrayBufferCompressed[attribIndex] = compressed;
910 mCurrentArrayBufferDivisors[attribIndex] = binding.getDivisor();
911 }
912 }
913 else
914 {
915 contextVk->invalidateDefaultAttribute(attribIndex);
916
917 // These will be filled out by the ContextVk.
918 vk::BufferHelper &emptyBuffer = contextVk->getEmptyBuffer();
919 mCurrentArrayBuffers[attribIndex] = &emptyBuffer;
920 mCurrentArrayBufferSerial[attribIndex] = emptyBuffer.getBufferSerial();
921 mCurrentArrayBufferHandles[attribIndex] = emptyBuffer.getBuffer().getHandle();
922 mCurrentArrayBufferOffsets[attribIndex] = emptyBuffer.getOffset();
923 mCurrentArrayBufferStrides[attribIndex] = 0;
924 mCurrentArrayBufferDivisors[attribIndex] = 0;
925 mCurrentArrayBufferCompressed[attribIndex] = false;
926 mCurrentArrayBufferRelativeOffsets[attribIndex] = 0;
927
928 ANGLE_TRY(setDefaultPackedInput(contextVk, attribIndex,
929 &mCurrentArrayBufferFormats[attribIndex]));
930 }
931
932 return angle::Result::Continue;
933 }
934
mergeClientAttribsRange(vk::Renderer * renderer,const gl::AttributesMask activeStreamedAttribs,size_t startVertex,size_t endVertex,std::array<AttributeRange,gl::MAX_VERTEX_ATTRIBS> & mergeRangesOut,std::array<size_t,gl::MAX_VERTEX_ATTRIBS> & mergedIndexesOut) const935 gl::AttributesMask VertexArrayVk::mergeClientAttribsRange(
936 vk::Renderer *renderer,
937 const gl::AttributesMask activeStreamedAttribs,
938 size_t startVertex,
939 size_t endVertex,
940 std::array<AttributeRange, gl::MAX_VERTEX_ATTRIBS> &mergeRangesOut,
941 std::array<size_t, gl::MAX_VERTEX_ATTRIBS> &mergedIndexesOut) const
942 {
943 const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes();
944 const std::vector<gl::VertexBinding> &bindings = mState.getVertexBindings();
945 gl::AttributesMask attributeMaskCanCombine;
946 angle::FixedVector<size_t, gl::MAX_VERTEX_ATTRIBS> combinedIndexes;
947 for (size_t attribIndex : activeStreamedAttribs)
948 {
949 const gl::VertexAttribute &attrib = attribs[attribIndex];
950 ASSERT(attrib.enabled);
951 const gl::VertexBinding &binding = bindings[attrib.bindingIndex];
952 const vk::Format &vertexFormat = renderer->getFormat(attrib.format->id);
953 bool combined = ShouldCombineAttributes(renderer, attrib, binding);
954 attributeMaskCanCombine.set(attribIndex, combined);
955 if (combined)
956 {
957 combinedIndexes.push_back(attribIndex);
958 }
959 GLuint pixelBytes = vertexFormat.getActualBufferFormat(false).pixelBytes;
960 size_t destStride = combined ? binding.getStride() : pixelBytes;
961 uintptr_t startAddress = reinterpret_cast<uintptr_t>(attrib.pointer);
962 mergeRangesOut[attribIndex].startAddr = startAddress;
963 mergeRangesOut[attribIndex].endAddr =
964 startAddress + (endVertex - 1) * destStride + pixelBytes;
965 mergeRangesOut[attribIndex].copyStartAddr =
966 startAddress + startVertex * binding.getStride();
967 mergedIndexesOut[attribIndex] = attribIndex;
968 }
969 if (attributeMaskCanCombine.none())
970 {
971 return attributeMaskCanCombine;
972 }
973 auto comp = [&mergeRangesOut](size_t a, size_t b) -> bool {
974 return mergeRangesOut[a] < mergeRangesOut[b];
975 };
976 // Only sort combined range indexes.
977 std::sort(combinedIndexes.begin(), combinedIndexes.end(), comp);
978 // Merge combined range span.
979 auto next = combinedIndexes.begin();
980 auto cur = next++;
981 while (next != combinedIndexes.end() || (cur != next))
982 {
983 // Cur and next overlaps: merge next into cur and move next.
984 if (next != combinedIndexes.end() &&
985 mergeRangesOut[*cur].endAddr >= mergeRangesOut[*next].startAddr)
986 {
987 mergeRangesOut[*cur].endAddr =
988 std::max(mergeRangesOut[*cur].endAddr, mergeRangesOut[*next].endAddr);
989 mergeRangesOut[*cur].copyStartAddr =
990 std::min(mergeRangesOut[*cur].copyStartAddr, mergeRangesOut[*next].copyStartAddr);
991 mergedIndexesOut[*next] = mergedIndexesOut[*cur];
992 ++next;
993 }
994 else
995 {
996 ++cur;
997 if (cur != next)
998 {
999 mergeRangesOut[*cur] = mergeRangesOut[*(cur - 1)];
1000 }
1001 else if (next != combinedIndexes.end())
1002 {
1003 ++next;
1004 }
1005 }
1006 }
1007 return attributeMaskCanCombine;
1008 }
1009
1010 // Handle copying client attribs and/or expanding attrib buffer in case where attribute
1011 // divisor value has to be emulated.
updateStreamedAttribs(const gl::Context * context,GLint firstVertex,GLsizei vertexOrIndexCount,GLsizei instanceCount,gl::DrawElementsType indexTypeOrInvalid,const void * indices)1012 angle::Result VertexArrayVk::updateStreamedAttribs(const gl::Context *context,
1013 GLint firstVertex,
1014 GLsizei vertexOrIndexCount,
1015 GLsizei instanceCount,
1016 gl::DrawElementsType indexTypeOrInvalid,
1017 const void *indices)
1018 {
1019 ContextVk *contextVk = vk::GetImpl(context);
1020 vk::Renderer *renderer = contextVk->getRenderer();
1021
1022 const gl::AttributesMask activeAttribs =
1023 context->getStateCache().getActiveClientAttribsMask() |
1024 context->getStateCache().getActiveBufferedAttribsMask();
1025 const gl::AttributesMask activeStreamedAttribs = mStreamingVertexAttribsMask & activeAttribs;
1026
1027 // Early return for corner case where emulated buffered attribs are not active
1028 if (!activeStreamedAttribs.any())
1029 {
1030 return angle::Result::Continue;
1031 }
1032
1033 GLint startVertex;
1034 size_t vertexCount;
1035 ANGLE_TRY(GetVertexRangeInfo(context, firstVertex, vertexOrIndexCount, indexTypeOrInvalid,
1036 indices, 0, &startVertex, &vertexCount));
1037
1038 ASSERT(vertexCount > 0);
1039 const auto &attribs = mState.getVertexAttributes();
1040 const auto &bindings = mState.getVertexBindings();
1041
1042 std::array<size_t, gl::MAX_VERTEX_ATTRIBS> mergedIndexes;
1043 std::array<AttributeRange, gl::MAX_VERTEX_ATTRIBS> mergeRanges;
1044 std::array<vk::BufferHelper *, gl::MAX_VERTEX_ATTRIBS> attribBufferHelper = {};
1045 auto mergeAttribMask =
1046 mergeClientAttribsRange(renderer, activeStreamedAttribs, startVertex,
1047 startVertex + vertexCount, mergeRanges, mergedIndexes);
1048
1049 for (size_t attribIndex : activeStreamedAttribs)
1050 {
1051 const gl::VertexAttribute &attrib = attribs[attribIndex];
1052 ASSERT(attrib.enabled);
1053 const gl::VertexBinding &binding = bindings[attrib.bindingIndex];
1054
1055 const vk::Format &vertexFormat = renderer->getFormat(attrib.format->id);
1056 GLuint pixelBytes = vertexFormat.getActualBufferFormat(false).pixelBytes;
1057
1058 const bool compressed = false;
1059 ASSERT(vertexFormat.getVertexInputAlignment(false) <= vk::kVertexBufferAlignment);
1060
1061 vk::BufferHelper *vertexDataBuffer = nullptr;
1062 const uint8_t *src = static_cast<const uint8_t *>(attrib.pointer);
1063 const uint32_t divisor = binding.getDivisor();
1064
1065 bool combined = mergeAttribMask.test(attribIndex);
1066 GLuint stride = combined ? binding.getStride() : pixelBytes;
1067 VkDeviceSize startOffset = 0;
1068 if (divisor > 0)
1069 {
1070 // Instanced attrib
1071 if (divisor > renderer->getMaxVertexAttribDivisor())
1072 {
1073 // Divisor will be set to 1 & so update buffer to have 1 attrib per instance
1074 size_t bytesToAllocate = instanceCount * stride;
1075
1076 // Allocate buffer for results
1077 ANGLE_TRY(contextVk->allocateStreamedVertexBuffer(attribIndex, bytesToAllocate,
1078 &vertexDataBuffer));
1079
1080 gl::Buffer *bufferGL = binding.getBuffer().get();
1081 if (bufferGL != nullptr)
1082 {
1083 // Only do the data copy if src buffer is valid.
1084 if (bufferGL->getSize() > 0)
1085 {
1086 // Map buffer to expand attribs for divisor emulation
1087 BufferVk *bufferVk = vk::GetImpl(binding.getBuffer().get());
1088 void *buffSrc = nullptr;
1089 ANGLE_TRY(bufferVk->mapImpl(contextVk, GL_MAP_READ_BIT, &buffSrc));
1090 src = reinterpret_cast<const uint8_t *>(buffSrc) + binding.getOffset();
1091
1092 uint32_t srcAttributeSize =
1093 static_cast<uint32_t>(ComputeVertexAttributeTypeSize(attrib));
1094
1095 size_t numVertices = GetVertexCount(bufferVk, binding, srcAttributeSize);
1096
1097 ANGLE_TRY(StreamVertexDataWithDivisor(
1098 contextVk, vertexDataBuffer, src, bytesToAllocate, binding.getStride(),
1099 stride, vertexFormat.getVertexLoadFunction(compressed), divisor,
1100 numVertices));
1101
1102 ANGLE_TRY(bufferVk->unmapImpl(contextVk));
1103 }
1104 else if (contextVk->getExtensions().robustnessAny())
1105 {
1106 // Satisfy robustness constraints (only if extension enabled)
1107 uint8_t *dst = vertexDataBuffer->getMappedMemory();
1108 memset(dst, 0, bytesToAllocate);
1109 }
1110 }
1111 else
1112 {
1113 size_t numVertices = instanceCount;
1114 ANGLE_TRY(StreamVertexDataWithDivisor(
1115 contextVk, vertexDataBuffer, src, bytesToAllocate, binding.getStride(),
1116 stride, vertexFormat.getVertexLoadFunction(compressed), divisor,
1117 numVertices));
1118 }
1119 }
1120 else
1121 {
1122 ASSERT(binding.getBuffer().get() == nullptr);
1123 size_t count = UnsignedCeilDivide(instanceCount, divisor);
1124 size_t bytesToAllocate = count * stride;
1125
1126 // Allocate buffer for results
1127 ANGLE_TRY(contextVk->allocateStreamedVertexBuffer(attribIndex, bytesToAllocate,
1128 &vertexDataBuffer));
1129
1130 ANGLE_TRY(StreamVertexData(contextVk, vertexDataBuffer, src, bytesToAllocate, 0,
1131 count, binding.getStride(),
1132 vertexFormat.getVertexLoadFunction(compressed)));
1133 }
1134 }
1135 else
1136 {
1137 ASSERT(binding.getBuffer().get() == nullptr);
1138 size_t mergedAttribIdx = mergedIndexes[attribIndex];
1139 const AttributeRange &range = mergeRanges[attribIndex];
1140 if (attribBufferHelper[mergedAttribIdx] == nullptr)
1141 {
1142 size_t destOffset =
1143 combined ? range.copyStartAddr - range.startAddr : startVertex * stride;
1144 size_t bytesToAllocate = range.endAddr - range.startAddr;
1145 ANGLE_TRY(contextVk->allocateStreamedVertexBuffer(
1146 mergedAttribIdx, bytesToAllocate, &attribBufferHelper[mergedAttribIdx]));
1147 ANGLE_TRY(StreamVertexData(
1148 contextVk, attribBufferHelper[mergedAttribIdx],
1149 (const uint8_t *)range.copyStartAddr, bytesToAllocate - destOffset, destOffset,
1150 vertexCount, binding.getStride(),
1151 combined ? nullptr : vertexFormat.getVertexLoadFunction(compressed)));
1152 }
1153 vertexDataBuffer = attribBufferHelper[mergedAttribIdx];
1154 startOffset = combined ? (uintptr_t)attrib.pointer - range.startAddr : 0;
1155 }
1156 ASSERT(vertexDataBuffer != nullptr);
1157 mCurrentArrayBuffers[attribIndex] = vertexDataBuffer;
1158 mCurrentArrayBufferSerial[attribIndex] = vertexDataBuffer->getBufferSerial();
1159 VkDeviceSize bufferOffset;
1160 mCurrentArrayBufferHandles[attribIndex] =
1161 vertexDataBuffer
1162 ->getBufferForVertexArray(contextVk, vertexDataBuffer->getSize(), &bufferOffset)
1163 .getHandle();
1164 mCurrentArrayBufferOffsets[attribIndex] = bufferOffset + startOffset;
1165 mCurrentArrayBufferStrides[attribIndex] = stride;
1166 mCurrentArrayBufferDivisors[attribIndex] = divisor;
1167 }
1168
1169 return angle::Result::Continue;
1170 }
1171
handleLineLoop(ContextVk * contextVk,GLint firstVertex,GLsizei vertexOrIndexCount,gl::DrawElementsType indexTypeOrInvalid,const void * indices,uint32_t * indexCountOut)1172 angle::Result VertexArrayVk::handleLineLoop(ContextVk *contextVk,
1173 GLint firstVertex,
1174 GLsizei vertexOrIndexCount,
1175 gl::DrawElementsType indexTypeOrInvalid,
1176 const void *indices,
1177 uint32_t *indexCountOut)
1178 {
1179 if (indexTypeOrInvalid != gl::DrawElementsType::InvalidEnum)
1180 {
1181 // Handle GL_LINE_LOOP drawElements.
1182 if (mDirtyLineLoopTranslation)
1183 {
1184 gl::Buffer *elementArrayBuffer = mState.getElementArrayBuffer();
1185
1186 if (!elementArrayBuffer)
1187 {
1188 ANGLE_TRY(
1189 mLineLoopHelper.streamIndices(contextVk, indexTypeOrInvalid, vertexOrIndexCount,
1190 reinterpret_cast<const uint8_t *>(indices),
1191 &mCurrentElementArrayBuffer, indexCountOut));
1192 }
1193 else
1194 {
1195 // When using an element array buffer, 'indices' is an offset to the first element.
1196 intptr_t offset = reinterpret_cast<intptr_t>(indices);
1197 BufferVk *elementArrayBufferVk = vk::GetImpl(elementArrayBuffer);
1198 ANGLE_TRY(mLineLoopHelper.getIndexBufferForElementArrayBuffer(
1199 contextVk, elementArrayBufferVk, indexTypeOrInvalid, vertexOrIndexCount, offset,
1200 &mCurrentElementArrayBuffer, indexCountOut));
1201 }
1202 }
1203
1204 // If we've had a drawArrays call with a line loop before, we want to make sure this is
1205 // invalidated the next time drawArrays is called since we use the same index buffer for
1206 // both calls.
1207 mLineLoopBufferFirstIndex.reset();
1208 mLineLoopBufferLastIndex.reset();
1209 return angle::Result::Continue;
1210 }
1211
1212 // Note: Vertex indexes can be arbitrarily large.
1213 uint32_t clampedVertexCount = gl::clampCast<uint32_t>(vertexOrIndexCount);
1214
1215 // Handle GL_LINE_LOOP drawArrays.
1216 size_t lastVertex = static_cast<size_t>(firstVertex + clampedVertexCount);
1217 if (!mLineLoopBufferFirstIndex.valid() || !mLineLoopBufferLastIndex.valid() ||
1218 mLineLoopBufferFirstIndex != firstVertex || mLineLoopBufferLastIndex != lastVertex)
1219 {
1220 ANGLE_TRY(mLineLoopHelper.getIndexBufferForDrawArrays(
1221 contextVk, clampedVertexCount, firstVertex, &mCurrentElementArrayBuffer));
1222
1223 mLineLoopBufferFirstIndex = firstVertex;
1224 mLineLoopBufferLastIndex = lastVertex;
1225 }
1226 *indexCountOut = vertexOrIndexCount + 1;
1227
1228 return angle::Result::Continue;
1229 }
1230
updateDefaultAttrib(ContextVk * contextVk,size_t attribIndex)1231 angle::Result VertexArrayVk::updateDefaultAttrib(ContextVk *contextVk, size_t attribIndex)
1232 {
1233 if (!mState.getEnabledAttributesMask().test(attribIndex))
1234 {
1235 vk::BufferHelper *bufferHelper;
1236 ANGLE_TRY(
1237 contextVk->allocateStreamedVertexBuffer(attribIndex, kDefaultValueSize, &bufferHelper));
1238
1239 const gl::VertexAttribCurrentValueData &defaultValue =
1240 contextVk->getState().getVertexAttribCurrentValues()[attribIndex];
1241 uint8_t *ptr = bufferHelper->getMappedMemory();
1242 memcpy(ptr, &defaultValue.Values, kDefaultValueSize);
1243 ANGLE_TRY(bufferHelper->flush(contextVk->getRenderer()));
1244
1245 VkDeviceSize bufferOffset;
1246 mCurrentArrayBufferHandles[attribIndex] =
1247 bufferHelper->getBufferForVertexArray(contextVk, kDefaultValueSize, &bufferOffset)
1248 .getHandle();
1249 mCurrentArrayBufferOffsets[attribIndex] = bufferOffset;
1250 mCurrentArrayBuffers[attribIndex] = bufferHelper;
1251 mCurrentArrayBufferSerial[attribIndex] = bufferHelper->getBufferSerial();
1252 mCurrentArrayBufferStrides[attribIndex] = 0;
1253 mCurrentArrayBufferDivisors[attribIndex] = 0;
1254
1255 ANGLE_TRY(setDefaultPackedInput(contextVk, attribIndex,
1256 &mCurrentArrayBufferFormats[attribIndex]));
1257 }
1258
1259 return angle::Result::Continue;
1260 }
1261 } // namespace rx
1262