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