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