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/CommandGraph.h"
17 #include "libANGLE/renderer/vulkan/ContextVk.h"
18 #include "libANGLE/renderer/vulkan/FramebufferVk.h"
19 #include "libANGLE/renderer/vulkan/RendererVk.h"
20 #include "libANGLE/renderer/vulkan/vk_format_utils.h"
21 #include "libANGLE/trace.h"
22
23 namespace rx
24 {
25 namespace
26 {
27 constexpr size_t kDynamicVertexDataSize = 1024 * 1024;
28 constexpr size_t kDynamicIndexDataSize = 1024 * 8;
29
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
StreamVertexData(ContextVk * contextVk,vk::DynamicBuffer * dynamicBuffer,const uint8_t * sourceData,size_t bytesToAllocate,size_t destOffset,size_t vertexCount,size_t stride,VertexCopyFunction vertexLoadFunction,vk::BufferHelper ** bufferOut,VkDeviceSize * bufferOffsetOut)51 angle::Result StreamVertexData(ContextVk *contextVk,
52 vk::DynamicBuffer *dynamicBuffer,
53 const uint8_t *sourceData,
54 size_t bytesToAllocate,
55 size_t destOffset,
56 size_t vertexCount,
57 size_t stride,
58 VertexCopyFunction vertexLoadFunction,
59 vk::BufferHelper **bufferOut,
60 VkDeviceSize *bufferOffsetOut)
61 {
62 uint8_t *dst = nullptr;
63 ANGLE_TRY(dynamicBuffer->allocate(contextVk, bytesToAllocate, &dst, nullptr, bufferOffsetOut,
64 nullptr));
65 *bufferOut = dynamicBuffer->getCurrentBuffer();
66 dst += destOffset;
67 vertexLoadFunction(sourceData, stride, vertexCount, dst);
68
69 ANGLE_TRY(dynamicBuffer->flush(contextVk));
70 return angle::Result::Continue;
71 }
72
GetVertexCount(BufferVk * srcBuffer,const gl::VertexBinding & binding,uint32_t srcFormatSize)73 size_t GetVertexCount(BufferVk *srcBuffer, const gl::VertexBinding &binding, uint32_t srcFormatSize)
74 {
75 // Bytes usable for vertex data.
76 GLint64 bytes = srcBuffer->getSize() - binding.getOffset();
77 if (bytes < srcFormatSize)
78 return 0;
79
80 // Count the last vertex. It may occupy less than a full stride.
81 size_t numVertices = 1;
82 bytes -= srcFormatSize;
83
84 // Count how many strides fit remaining space.
85 if (bytes > 0)
86 numVertices += static_cast<size_t>(bytes) / binding.getStride();
87
88 return numVertices;
89 }
90 } // anonymous namespace
91
VertexArrayVk(ContextVk * contextVk,const gl::VertexArrayState & state)92 VertexArrayVk::VertexArrayVk(ContextVk *contextVk, const gl::VertexArrayState &state)
93 : VertexArrayImpl(state),
94 mCurrentArrayBufferHandles{},
95 mCurrentArrayBufferOffsets{},
96 mCurrentArrayBuffers{},
97 mCurrentElementArrayBufferOffset(0),
98 mCurrentElementArrayBuffer(nullptr),
99 mLineLoopHelper(contextVk->getRenderer()),
100 mDirtyLineLoopTranslation(true)
101 {
102 RendererVk *renderer = contextVk->getRenderer();
103
104 VkBufferCreateInfo createInfo = {};
105 createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
106 createInfo.size = 16;
107 createInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
108 (void)mTheNullBuffer.init(contextVk, createInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
109
110 mCurrentArrayBufferHandles.fill(mTheNullBuffer.getBuffer().getHandle());
111 mCurrentArrayBufferOffsets.fill(0);
112 mCurrentArrayBuffers.fill(&mTheNullBuffer);
113
114 mDynamicVertexData.init(renderer, vk::kVertexBufferUsageFlags, vk::kVertexBufferAlignment,
115 kDynamicVertexDataSize, true);
116
117 // We use an alignment of four for index data. This ensures that compute shaders can read index
118 // elements from "uint" aligned addresses.
119 mDynamicIndexData.init(renderer, vk::kIndexBufferUsageFlags, vk::kIndexBufferAlignment,
120 kDynamicIndexDataSize, true);
121 mTranslatedByteIndexData.init(renderer, vk::kIndexBufferUsageFlags, vk::kIndexBufferAlignment,
122 kDynamicIndexDataSize, true);
123 }
124
~VertexArrayVk()125 VertexArrayVk::~VertexArrayVk() {}
126
destroy(const gl::Context * context)127 void VertexArrayVk::destroy(const gl::Context *context)
128 {
129 ContextVk *contextVk = vk::GetImpl(context);
130
131 mTheNullBuffer.release(contextVk);
132
133 mDynamicVertexData.release(contextVk);
134 mDynamicIndexData.release(contextVk);
135 mTranslatedByteIndexData.release(contextVk);
136 mLineLoopHelper.release(contextVk);
137 }
138
convertIndexBufferGPU(ContextVk * contextVk,BufferVk * bufferVk,const void * indices)139 angle::Result VertexArrayVk::convertIndexBufferGPU(ContextVk *contextVk,
140 BufferVk *bufferVk,
141 const void *indices)
142 {
143 intptr_t offsetIntoSrcData = reinterpret_cast<intptr_t>(indices);
144 size_t srcDataSize = static_cast<size_t>(bufferVk->getSize()) - offsetIntoSrcData;
145
146 mTranslatedByteIndexData.releaseInFlightBuffers(contextVk);
147
148 ANGLE_TRY(mTranslatedByteIndexData.allocate(contextVk, sizeof(GLushort) * srcDataSize, nullptr,
149 nullptr, &mCurrentElementArrayBufferOffset,
150 nullptr));
151 mCurrentElementArrayBuffer = mTranslatedByteIndexData.getCurrentBuffer();
152
153 vk::BufferHelper *dest = mTranslatedByteIndexData.getCurrentBuffer();
154 vk::BufferHelper *src = &bufferVk->getBuffer();
155
156 // Copy relevant section of the source into destination at allocated offset. Note that the
157 // offset returned by allocate() above is in bytes. As is the indices offset pointer.
158 UtilsVk::ConvertIndexParameters params = {};
159 params.srcOffset = static_cast<uint32_t>(offsetIntoSrcData);
160 params.dstOffset = static_cast<uint32_t>(mCurrentElementArrayBufferOffset);
161 params.maxIndex = static_cast<uint32_t>(bufferVk->getSize());
162
163 return contextVk->getUtils().convertIndexBuffer(contextVk, dest, src, params);
164 }
165
convertIndexBufferCPU(ContextVk * contextVk,gl::DrawElementsType indexType,size_t indexCount,const void * sourcePointer)166 angle::Result VertexArrayVk::convertIndexBufferCPU(ContextVk *contextVk,
167 gl::DrawElementsType indexType,
168 size_t indexCount,
169 const void *sourcePointer)
170 {
171 ASSERT(!mState.getElementArrayBuffer() || indexType == gl::DrawElementsType::UnsignedByte);
172
173 mDynamicIndexData.releaseInFlightBuffers(contextVk);
174
175 size_t elementSize = gl::GetDrawElementsTypeSize(indexType);
176 if (indexType == gl::DrawElementsType::UnsignedByte)
177 {
178 // 8-bit indices are not supported by Vulkan, so they are promoted to
179 // 16-bit indices below
180 elementSize = sizeof(GLushort);
181 }
182
183 const size_t amount = elementSize * indexCount;
184 GLubyte *dst = nullptr;
185
186 ANGLE_TRY(mDynamicIndexData.allocate(contextVk, amount, &dst, nullptr,
187 &mCurrentElementArrayBufferOffset, nullptr));
188 mCurrentElementArrayBuffer = mDynamicIndexData.getCurrentBuffer();
189 if (indexType == gl::DrawElementsType::UnsignedByte)
190 {
191 // Unsigned bytes don't have direct support in Vulkan so we have to expand the
192 // memory to a GLushort.
193 const GLubyte *in = static_cast<const GLubyte *>(sourcePointer);
194 GLushort *expandedDst = reinterpret_cast<GLushort *>(dst);
195 bool primitiveRestart = contextVk->getState().isPrimitiveRestartEnabled();
196
197 constexpr GLubyte kUnsignedByteRestartValue = 0xFF;
198 constexpr GLushort kUnsignedShortRestartValue = 0xFFFF;
199
200 if (primitiveRestart)
201 {
202 for (size_t index = 0; index < indexCount; index++)
203 {
204 GLushort value = static_cast<GLushort>(in[index]);
205 if (in[index] == kUnsignedByteRestartValue)
206 {
207 // Convert from 8-bit restart value to 16-bit restart value
208 value = kUnsignedShortRestartValue;
209 }
210 expandedDst[index] = value;
211 }
212 }
213 else
214 {
215 // Fast path for common case.
216 for (size_t index = 0; index < indexCount; index++)
217 {
218 expandedDst[index] = static_cast<GLushort>(in[index]);
219 }
220 }
221 }
222 else
223 {
224 // The primitive restart value is the same for OpenGL and Vulkan,
225 // so there's no need to perform any conversion.
226 memcpy(dst, sourcePointer, amount);
227 }
228 return mDynamicIndexData.flush(contextVk);
229 }
230
231 // We assume the buffer is completely full of the same kind of data and convert
232 // and/or align it as we copy it to a DynamicBuffer. The assumption could be wrong
233 // but the alternative of copying it piecemeal on each draw would have a lot more
234 // overhead.
convertVertexBufferGPU(ContextVk * contextVk,BufferVk * srcBuffer,const gl::VertexBinding & binding,size_t attribIndex,const vk::Format & vertexFormat,ConversionBuffer * conversion,GLuint relativeOffset)235 angle::Result VertexArrayVk::convertVertexBufferGPU(ContextVk *contextVk,
236 BufferVk *srcBuffer,
237 const gl::VertexBinding &binding,
238 size_t attribIndex,
239 const vk::Format &vertexFormat,
240 ConversionBuffer *conversion,
241 GLuint relativeOffset)
242 {
243 const angle::Format &srcFormat = vertexFormat.angleFormat();
244 const angle::Format &destFormat = vertexFormat.bufferFormat();
245
246 ASSERT(binding.getStride() % (srcFormat.pixelBytes / srcFormat.channelCount) == 0);
247
248 unsigned srcFormatSize = srcFormat.pixelBytes;
249 unsigned destFormatSize = destFormat.pixelBytes;
250
251 size_t numVertices = GetVertexCount(srcBuffer, binding, srcFormatSize);
252 if (numVertices == 0)
253 {
254 return angle::Result::Continue;
255 }
256
257 ASSERT(GetVertexInputAlignment(vertexFormat) <= vk::kVertexBufferAlignment);
258
259 // Allocate buffer for results
260 conversion->data.releaseInFlightBuffers(contextVk);
261 ANGLE_TRY(conversion->data.allocate(contextVk, numVertices * destFormatSize, nullptr, nullptr,
262 &conversion->lastAllocationOffset, nullptr));
263
264 ASSERT(conversion->dirty);
265 conversion->dirty = false;
266
267 UtilsVk::ConvertVertexParameters params;
268 params.vertexCount = numVertices;
269 params.srcFormat = &srcFormat;
270 params.destFormat = &destFormat;
271 params.srcStride = binding.getStride();
272 params.srcOffset = binding.getOffset() + relativeOffset;
273 params.destOffset = static_cast<size_t>(conversion->lastAllocationOffset);
274
275 ANGLE_TRY(contextVk->getUtils().convertVertexBuffer(
276 contextVk, conversion->data.getCurrentBuffer(), &srcBuffer->getBuffer(), params));
277
278 return angle::Result::Continue;
279 }
280
convertVertexBufferCPU(ContextVk * contextVk,BufferVk * srcBuffer,const gl::VertexBinding & binding,size_t attribIndex,const vk::Format & vertexFormat,ConversionBuffer * conversion,GLuint relativeOffset)281 angle::Result VertexArrayVk::convertVertexBufferCPU(ContextVk *contextVk,
282 BufferVk *srcBuffer,
283 const gl::VertexBinding &binding,
284 size_t attribIndex,
285 const vk::Format &vertexFormat,
286 ConversionBuffer *conversion,
287 GLuint relativeOffset)
288 {
289 ANGLE_TRACE_EVENT0("gpu.angle", "VertexArrayVk::convertVertexBufferCpu");
290
291 unsigned srcFormatSize = vertexFormat.angleFormat().pixelBytes;
292 unsigned dstFormatSize = vertexFormat.bufferFormat().pixelBytes;
293
294 conversion->data.releaseInFlightBuffers(contextVk);
295
296 size_t numVertices = GetVertexCount(srcBuffer, binding, srcFormatSize);
297 if (numVertices == 0)
298 {
299 return angle::Result::Continue;
300 }
301
302 void *src = nullptr;
303 ANGLE_TRY(srcBuffer->mapImpl(contextVk, &src));
304 const uint8_t *srcBytes = reinterpret_cast<const uint8_t *>(src);
305 srcBytes += binding.getOffset() + relativeOffset;
306 ASSERT(GetVertexInputAlignment(vertexFormat) <= vk::kVertexBufferAlignment);
307 ANGLE_TRY(StreamVertexData(contextVk, &conversion->data, srcBytes, numVertices * dstFormatSize,
308 0, numVertices, binding.getStride(), vertexFormat.vertexLoadFunction,
309 &mCurrentArrayBuffers[attribIndex],
310 &conversion->lastAllocationOffset));
311 srcBuffer->unmapImpl(contextVk);
312
313 ASSERT(conversion->dirty);
314 conversion->dirty = false;
315
316 return angle::Result::Continue;
317 }
318
syncState(const gl::Context * context,const gl::VertexArray::DirtyBits & dirtyBits,gl::VertexArray::DirtyAttribBitsArray * attribBits,gl::VertexArray::DirtyBindingBitsArray * bindingBits)319 angle::Result VertexArrayVk::syncState(const gl::Context *context,
320 const gl::VertexArray::DirtyBits &dirtyBits,
321 gl::VertexArray::DirtyAttribBitsArray *attribBits,
322 gl::VertexArray::DirtyBindingBitsArray *bindingBits)
323 {
324 ASSERT(dirtyBits.any());
325
326 ContextVk *contextVk = vk::GetImpl(context);
327
328 const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes();
329 const std::vector<gl::VertexBinding> &bindings = mState.getVertexBindings();
330
331 for (size_t dirtyBit : dirtyBits)
332 {
333 switch (dirtyBit)
334 {
335 case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER:
336 {
337 gl::Buffer *bufferGL = mState.getElementArrayBuffer();
338 if (bufferGL)
339 {
340 BufferVk *bufferVk = vk::GetImpl(bufferGL);
341 mCurrentElementArrayBuffer = &bufferVk->getBuffer();
342 }
343 else
344 {
345 mCurrentElementArrayBuffer = nullptr;
346 }
347
348 mLineLoopBufferFirstIndex.reset();
349 mLineLoopBufferLastIndex.reset();
350 contextVk->setIndexBufferDirty();
351 mDirtyLineLoopTranslation = true;
352 break;
353 }
354
355 case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA:
356 mLineLoopBufferFirstIndex.reset();
357 mLineLoopBufferLastIndex.reset();
358 contextVk->setIndexBufferDirty();
359 mDirtyLineLoopTranslation = true;
360 break;
361
362 #define ANGLE_VERTEX_DIRTY_ATTRIB_FUNC(INDEX) \
363 case gl::VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX: \
364 if ((*attribBits)[INDEX].to_ulong() == \
365 angle::Bit<unsigned long>(gl::VertexArray::DIRTY_ATTRIB_POINTER_BUFFER)) \
366 { \
367 syncDirtyBuffer(contextVk, bindings[INDEX], INDEX); \
368 } \
369 else \
370 { \
371 ANGLE_TRY(syncDirtyAttrib(contextVk, attribs[INDEX], \
372 bindings[attribs[INDEX].bindingIndex], INDEX)); \
373 } \
374 (*attribBits)[INDEX].reset(); \
375 break;
376
377 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_ATTRIB_FUNC)
378
379 #define ANGLE_VERTEX_DIRTY_BINDING_FUNC(INDEX) \
380 case gl::VertexArray::DIRTY_BIT_BINDING_0 + INDEX: \
381 for (size_t attribIndex : bindings[INDEX].getBoundAttributesMask()) \
382 { \
383 ANGLE_TRY( \
384 syncDirtyAttrib(contextVk, attribs[attribIndex], bindings[INDEX], attribIndex)); \
385 } \
386 (*bindingBits)[INDEX].reset(); \
387 break;
388
389 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BINDING_FUNC)
390
391 #define ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC(INDEX) \
392 case gl::VertexArray::DIRTY_BIT_BUFFER_DATA_0 + INDEX: \
393 ANGLE_TRY(syncDirtyAttrib(contextVk, attribs[INDEX], \
394 bindings[attribs[INDEX].bindingIndex], INDEX)); \
395 break;
396
397 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC)
398
399 default:
400 UNREACHABLE();
401 break;
402 }
403 }
404
405 return angle::Result::Continue;
406 }
407
408 #undef ANGLE_VERTEX_DIRTY_ATTRIB_FUNC
409 #undef ANGLE_VERTEX_DIRTY_BINDING_FUNC
410 #undef ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC
411
setDefaultPackedInput(ContextVk * contextVk,size_t attribIndex)412 ANGLE_INLINE void VertexArrayVk::setDefaultPackedInput(ContextVk *contextVk, size_t attribIndex)
413 {
414 const gl::State &glState = contextVk->getState();
415 const gl::VertexAttribCurrentValueData &defaultValue =
416 glState.getVertexAttribCurrentValues()[attribIndex];
417
418 angle::FormatID format = GetCurrentValueFormatID(defaultValue.Type);
419
420 contextVk->onVertexAttributeChange(attribIndex, 0, 0, format, 0);
421 }
422
syncDirtyAttrib(ContextVk * contextVk,const gl::VertexAttribute & attrib,const gl::VertexBinding & binding,size_t attribIndex)423 angle::Result VertexArrayVk::syncDirtyAttrib(ContextVk *contextVk,
424 const gl::VertexAttribute &attrib,
425 const gl::VertexBinding &binding,
426 size_t attribIndex)
427 {
428 RendererVk *renderer = contextVk->getRenderer();
429 bool anyVertexBufferConvertedOnGpu = false;
430
431 if (attrib.enabled)
432 {
433 gl::Buffer *bufferGL = binding.getBuffer().get();
434 const vk::Format &vertexFormat = renderer->getFormat(attrib.format->id);
435 GLuint stride;
436
437 if (bufferGL)
438 {
439 BufferVk *bufferVk = vk::GetImpl(bufferGL);
440 const angle::Format &angleFormat = vertexFormat.angleFormat();
441 bool bindingIsAligned = BindingIsAligned(binding, angleFormat, angleFormat.channelCount,
442 attrib.relativeOffset);
443
444 if (vertexFormat.vertexLoadRequiresConversion || !bindingIsAligned)
445 {
446 stride = vertexFormat.bufferFormat().pixelBytes;
447
448 ConversionBuffer *conversion = bufferVk->getVertexConversionBuffer(
449 renderer, angleFormat.id, binding.getStride(),
450 binding.getOffset() + attrib.relativeOffset);
451 if (conversion->dirty)
452 {
453 if (bindingIsAligned)
454 {
455 ANGLE_TRY(convertVertexBufferGPU(contextVk, bufferVk, binding, attribIndex,
456 vertexFormat, conversion,
457 attrib.relativeOffset));
458 anyVertexBufferConvertedOnGpu = true;
459 }
460 else
461 {
462 ANGLE_TRY(convertVertexBufferCPU(contextVk, bufferVk, binding, attribIndex,
463 vertexFormat, conversion,
464 attrib.relativeOffset));
465 }
466 }
467
468 mCurrentArrayBuffers[attribIndex] = conversion->data.getCurrentBuffer();
469 mCurrentArrayBufferHandles[attribIndex] =
470 mCurrentArrayBuffers[attribIndex]->getBuffer().getHandle();
471 mCurrentArrayBufferOffsets[attribIndex] =
472 conversion->lastAllocationOffset - attrib.relativeOffset;
473 }
474 else
475 {
476 if (bufferVk->getSize() == 0)
477 {
478 mCurrentArrayBuffers[attribIndex] = &mTheNullBuffer;
479 mCurrentArrayBufferHandles[attribIndex] =
480 mTheNullBuffer.getBuffer().getHandle();
481 mCurrentArrayBufferOffsets[attribIndex] = 0;
482 stride = 0;
483 }
484 else
485 {
486 vk::BufferHelper &bufferHelper = bufferVk->getBuffer();
487
488 mCurrentArrayBuffers[attribIndex] = &bufferHelper;
489 mCurrentArrayBufferHandles[attribIndex] = bufferHelper.getBuffer().getHandle();
490 mCurrentArrayBufferOffsets[attribIndex] = binding.getOffset();
491 stride = binding.getStride();
492 }
493 }
494 }
495 else
496 {
497 mCurrentArrayBuffers[attribIndex] = &mTheNullBuffer;
498 mCurrentArrayBufferHandles[attribIndex] = mTheNullBuffer.getBuffer().getHandle();
499 mCurrentArrayBufferOffsets[attribIndex] = 0;
500 stride = vertexFormat.bufferFormat().pixelBytes;
501 }
502
503 contextVk->onVertexAttributeChange(attribIndex, stride, binding.getDivisor(),
504 attrib.format->id, attrib.relativeOffset);
505 }
506 else
507 {
508 contextVk->invalidateDefaultAttribute(attribIndex);
509
510 // These will be filled out by the ContextVk.
511 mCurrentArrayBuffers[attribIndex] = &mTheNullBuffer;
512 mCurrentArrayBufferHandles[attribIndex] = mTheNullBuffer.getBuffer().getHandle();
513 mCurrentArrayBufferOffsets[attribIndex] = 0;
514
515 setDefaultPackedInput(contextVk, attribIndex);
516 }
517
518 if (anyVertexBufferConvertedOnGpu && renderer->getFeatures().flushAfterVertexConversion.enabled)
519 {
520 ANGLE_TRY(contextVk->flushImpl(nullptr));
521 }
522
523 return angle::Result::Continue;
524 }
525
syncDirtyBuffer(ContextVk * contextVk,const gl::VertexBinding & binding,size_t bindingIndex)526 void VertexArrayVk::syncDirtyBuffer(ContextVk *contextVk,
527 const gl::VertexBinding &binding,
528 size_t bindingIndex)
529 {
530 gl::Buffer *bufferGL = binding.getBuffer().get();
531
532 if (bufferGL)
533 {
534 BufferVk *bufferVk = vk::GetImpl(bufferGL);
535 vk::BufferHelper &bufferHelper = bufferVk->getBuffer();
536 mCurrentArrayBuffers[bindingIndex] = &bufferHelper;
537 mCurrentArrayBufferHandles[bindingIndex] = bufferHelper.getBuffer().getHandle();
538 mCurrentArrayBufferOffsets[bindingIndex] = binding.getOffset();
539 }
540 else
541 {
542 mCurrentArrayBuffers[bindingIndex] = &mTheNullBuffer;
543 mCurrentArrayBufferHandles[bindingIndex] = mTheNullBuffer.getBuffer().getHandle();
544 mCurrentArrayBufferOffsets[bindingIndex] = 0;
545 }
546
547 contextVk->invalidateVertexBuffers();
548 }
549
updateClientAttribs(const gl::Context * context,GLint firstVertex,GLsizei vertexOrIndexCount,GLsizei instanceCount,gl::DrawElementsType indexTypeOrInvalid,const void * indices)550 angle::Result VertexArrayVk::updateClientAttribs(const gl::Context *context,
551 GLint firstVertex,
552 GLsizei vertexOrIndexCount,
553 GLsizei instanceCount,
554 gl::DrawElementsType indexTypeOrInvalid,
555 const void *indices)
556 {
557 ContextVk *contextVk = vk::GetImpl(context);
558 const gl::AttributesMask &clientAttribs = context->getStateCache().getActiveClientAttribsMask();
559
560 ASSERT(clientAttribs.any());
561
562 GLint startVertex;
563 size_t vertexCount;
564 ANGLE_TRY(GetVertexRangeInfo(context, firstVertex, vertexOrIndexCount, indexTypeOrInvalid,
565 indices, 0, &startVertex, &vertexCount));
566
567 RendererVk *renderer = contextVk->getRenderer();
568 mDynamicVertexData.releaseInFlightBuffers(contextVk);
569
570 const auto &attribs = mState.getVertexAttributes();
571 const auto &bindings = mState.getVertexBindings();
572
573 // TODO(fjhenigman): When we have a bunch of interleaved attributes, they end up
574 // un-interleaved, wasting space and copying time. Consider improving on that.
575 for (size_t attribIndex : clientAttribs)
576 {
577 const gl::VertexAttribute &attrib = attribs[attribIndex];
578 const gl::VertexBinding &binding = bindings[attrib.bindingIndex];
579 ASSERT(attrib.enabled && binding.getBuffer().get() == nullptr);
580
581 const vk::Format &vertexFormat = renderer->getFormat(attrib.format->id);
582 GLuint stride = vertexFormat.bufferFormat().pixelBytes;
583
584 ASSERT(GetVertexInputAlignment(vertexFormat) <= vk::kVertexBufferAlignment);
585
586 const uint8_t *src = static_cast<const uint8_t *>(attrib.pointer);
587 if (binding.getDivisor() > 0)
588 {
589 // instanced attrib
590 size_t count = UnsignedCeilDivide(instanceCount, binding.getDivisor());
591 size_t bytesToAllocate = count * stride;
592
593 ANGLE_TRY(StreamVertexData(contextVk, &mDynamicVertexData, src, bytesToAllocate, 0,
594 count, binding.getStride(), vertexFormat.vertexLoadFunction,
595 &mCurrentArrayBuffers[attribIndex],
596 &mCurrentArrayBufferOffsets[attribIndex]));
597 }
598 else
599 {
600 // Allocate space for startVertex + vertexCount so indexing will work. If we don't
601 // start at zero all the indices will be off.
602 // Only vertexCount vertices will be used by the upcoming draw so that is all we copy.
603 size_t bytesToAllocate = (startVertex + vertexCount) * stride;
604 src += startVertex * binding.getStride();
605 size_t destOffset = startVertex * stride;
606
607 ANGLE_TRY(StreamVertexData(
608 contextVk, &mDynamicVertexData, src, bytesToAllocate, destOffset, vertexCount,
609 binding.getStride(), vertexFormat.vertexLoadFunction,
610 &mCurrentArrayBuffers[attribIndex], &mCurrentArrayBufferOffsets[attribIndex]));
611 }
612
613 mCurrentArrayBufferHandles[attribIndex] =
614 mCurrentArrayBuffers[attribIndex]->getBuffer().getHandle();
615 }
616
617 return angle::Result::Continue;
618 }
619
handleLineLoop(ContextVk * contextVk,GLint firstVertex,GLsizei vertexOrIndexCount,gl::DrawElementsType indexTypeOrInvalid,const void * indices,uint32_t * indexCountOut)620 angle::Result VertexArrayVk::handleLineLoop(ContextVk *contextVk,
621 GLint firstVertex,
622 GLsizei vertexOrIndexCount,
623 gl::DrawElementsType indexTypeOrInvalid,
624 const void *indices,
625 uint32_t *indexCountOut)
626 {
627 if (indexTypeOrInvalid != gl::DrawElementsType::InvalidEnum)
628 {
629 // Handle GL_LINE_LOOP drawElements.
630 if (mDirtyLineLoopTranslation)
631 {
632 gl::Buffer *elementArrayBuffer = mState.getElementArrayBuffer();
633
634 if (!elementArrayBuffer)
635 {
636 ANGLE_TRY(mLineLoopHelper.streamIndices(
637 contextVk, indexTypeOrInvalid, vertexOrIndexCount,
638 reinterpret_cast<const uint8_t *>(indices), &mCurrentElementArrayBuffer,
639 &mCurrentElementArrayBufferOffset, indexCountOut));
640 }
641 else
642 {
643 // When using an element array buffer, 'indices' is an offset to the first element.
644 intptr_t offset = reinterpret_cast<intptr_t>(indices);
645 BufferVk *elementArrayBufferVk = vk::GetImpl(elementArrayBuffer);
646 ANGLE_TRY(mLineLoopHelper.getIndexBufferForElementArrayBuffer(
647 contextVk, elementArrayBufferVk, indexTypeOrInvalid, vertexOrIndexCount, offset,
648 &mCurrentElementArrayBuffer, &mCurrentElementArrayBufferOffset, indexCountOut));
649 }
650 }
651
652 // If we've had a drawArrays call with a line loop before, we want to make sure this is
653 // invalidated the next time drawArrays is called since we use the same index buffer for
654 // both calls.
655 mLineLoopBufferFirstIndex.reset();
656 mLineLoopBufferLastIndex.reset();
657 return angle::Result::Continue;
658 }
659
660 // Note: Vertex indexes can be arbitrarily large.
661 uint32_t clampedVertexCount = gl::clampCast<uint32_t>(vertexOrIndexCount);
662
663 // Handle GL_LINE_LOOP drawArrays.
664 size_t lastVertex = static_cast<size_t>(firstVertex + clampedVertexCount);
665 if (!mLineLoopBufferFirstIndex.valid() || !mLineLoopBufferLastIndex.valid() ||
666 mLineLoopBufferFirstIndex != firstVertex || mLineLoopBufferLastIndex != lastVertex)
667 {
668 ANGLE_TRY(mLineLoopHelper.getIndexBufferForDrawArrays(
669 contextVk, clampedVertexCount, firstVertex, &mCurrentElementArrayBuffer,
670 &mCurrentElementArrayBufferOffset));
671
672 mLineLoopBufferFirstIndex = firstVertex;
673 mLineLoopBufferLastIndex = lastVertex;
674 }
675 *indexCountOut = vertexOrIndexCount + 1;
676
677 return angle::Result::Continue;
678 }
679
updateDefaultAttrib(ContextVk * contextVk,size_t attribIndex,VkBuffer bufferHandle,uint32_t offset)680 void VertexArrayVk::updateDefaultAttrib(ContextVk *contextVk,
681 size_t attribIndex,
682 VkBuffer bufferHandle,
683 uint32_t offset)
684 {
685 if (!mState.getEnabledAttributesMask().test(attribIndex))
686 {
687 mCurrentArrayBufferHandles[attribIndex] = bufferHandle;
688 mCurrentArrayBufferOffsets[attribIndex] = offset;
689 mCurrentArrayBuffers[attribIndex] = nullptr;
690
691 setDefaultPackedInput(contextVk, attribIndex);
692 }
693 }
694 } // namespace rx
695