xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/wgpu/VertexArrayWgpu.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2024 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 // VertexArrayWgpu.cpp:
7 //    Implements the class methods for VertexArrayWgpu.
8 //
9 
10 #include "libANGLE/renderer/wgpu/VertexArrayWgpu.h"
11 
12 #include "common/debug.h"
13 #include "libANGLE/renderer/wgpu/ContextWgpu.h"
14 
15 namespace rx
16 {
17 
VertexArrayWgpu(const gl::VertexArrayState & data)18 VertexArrayWgpu::VertexArrayWgpu(const gl::VertexArrayState &data) : VertexArrayImpl(data)
19 {
20     // Pre-initialize mCurrentIndexBuffer to a streaming buffer because no index buffer dirty bit is
21     // triggered if our first draw call has no buffer bound.
22     mCurrentIndexBuffer = &mStreamingIndexBuffer;
23 }
24 
syncState(const gl::Context * context,const gl::VertexArray::DirtyBits & dirtyBits,gl::VertexArray::DirtyAttribBitsArray * attribBits,gl::VertexArray::DirtyBindingBitsArray * bindingBits)25 angle::Result VertexArrayWgpu::syncState(const gl::Context *context,
26                                          const gl::VertexArray::DirtyBits &dirtyBits,
27                                          gl::VertexArray::DirtyAttribBitsArray *attribBits,
28                                          gl::VertexArray::DirtyBindingBitsArray *bindingBits)
29 {
30     ASSERT(dirtyBits.any());
31 
32     ContextWgpu *contextWgpu = GetImplAs<ContextWgpu>(context);
33 
34     const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes();
35     const std::vector<gl::VertexBinding> &bindings  = mState.getVertexBindings();
36 
37     gl::AttributesMask syncedAttributes;
38 
39     for (auto iter = dirtyBits.begin(), endIter = dirtyBits.end(); iter != endIter; ++iter)
40     {
41         size_t dirtyBit = *iter;
42         switch (dirtyBit)
43         {
44             case gl::VertexArray::DIRTY_BIT_LOST_OBSERVATION:
45                 break;
46 
47             case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER:
48                 ANGLE_TRY(syncDirtyElementArrayBuffer(contextWgpu));
49                 contextWgpu->invalidateIndexBuffer();
50                 break;
51 
52             case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA:
53                 break;
54 
55 #define ANGLE_VERTEX_DIRTY_ATTRIB_FUNC(INDEX)                                     \
56     case gl::VertexArray::DIRTY_BIT_ATTRIB_0 + INDEX:                             \
57         ANGLE_TRY(syncDirtyAttrib(contextWgpu, attribs[INDEX],                    \
58                                   bindings[attribs[INDEX].bindingIndex], INDEX)); \
59         (*attribBits)[INDEX].reset();                                             \
60         syncedAttributes.set(INDEX);                                              \
61         break;
62 
63                 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_ATTRIB_FUNC)
64 
65 #define ANGLE_VERTEX_DIRTY_BINDING_FUNC(INDEX)                                    \
66     case gl::VertexArray::DIRTY_BIT_BINDING_0 + INDEX:                            \
67         ANGLE_TRY(syncDirtyAttrib(contextWgpu, attribs[INDEX],                    \
68                                   bindings[attribs[INDEX].bindingIndex], INDEX)); \
69         (*bindingBits)[INDEX].reset();                                            \
70         syncedAttributes.set(INDEX);                                              \
71         break;
72 
73                 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BINDING_FUNC)
74 
75 #define ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC(INDEX)                                \
76     case gl::VertexArray::DIRTY_BIT_BUFFER_DATA_0 + INDEX:                        \
77         ANGLE_TRY(syncDirtyAttrib(contextWgpu, attribs[INDEX],                    \
78                                   bindings[attribs[INDEX].bindingIndex], INDEX)); \
79         syncedAttributes.set(INDEX);                                              \
80         break;
81 
82                 ANGLE_VERTEX_INDEX_CASES(ANGLE_VERTEX_DIRTY_BUFFER_DATA_FUNC)
83             default:
84                 break;
85         }
86     }
87 
88     for (size_t syncedAttribIndex : syncedAttributes)
89     {
90         contextWgpu->setVertexAttribute(syncedAttribIndex, mCurrentAttribs[syncedAttribIndex]);
91         contextWgpu->invalidateVertexBuffer(syncedAttribIndex);
92     }
93     return angle::Result::Continue;
94 }
95 
syncClientArrays(const gl::Context * context,const gl::AttributesMask & activeAttributesMask,GLint first,GLsizei count,GLsizei instanceCount,gl::DrawElementsType drawElementsTypeOrInvalid,const void * indices,GLint baseVertex,bool primitiveRestartEnabled,const void ** adjustedIndicesPtr)96 angle::Result VertexArrayWgpu::syncClientArrays(const gl::Context *context,
97                                                 const gl::AttributesMask &activeAttributesMask,
98                                                 GLint first,
99                                                 GLsizei count,
100                                                 GLsizei instanceCount,
101                                                 gl::DrawElementsType drawElementsTypeOrInvalid,
102                                                 const void *indices,
103                                                 GLint baseVertex,
104                                                 bool primitiveRestartEnabled,
105                                                 const void **adjustedIndicesPtr)
106 {
107     *adjustedIndicesPtr = indices;
108 
109     gl::AttributesMask clientAttributesToSync = mState.getClientMemoryAttribsMask() &
110                                                 mState.getEnabledAttributesMask() &
111                                                 activeAttributesMask;
112     bool indexedDrawCallWithNoIndexBuffer =
113         drawElementsTypeOrInvalid != gl::DrawElementsType::InvalidEnum &&
114         !mState.getElementArrayBuffer();
115 
116     if (!clientAttributesToSync.any() && !indexedDrawCallWithNoIndexBuffer)
117     {
118         return angle::Result::Continue;
119     }
120 
121     ContextWgpu *contextWgpu = webgpu::GetImpl(context);
122     wgpu::Device device      = webgpu::GetDevice(context);
123 
124     // If any attributes need to be streamed, we need to know the index range.
125     std::optional<gl::IndexRange> indexRange;
126     if (clientAttributesToSync.any())
127     {
128         GLint startVertex  = 0;
129         size_t vertexCount = 0;
130         ANGLE_TRY(GetVertexRangeInfo(context, first, count, drawElementsTypeOrInvalid, indices,
131                                      baseVertex, &startVertex, &vertexCount));
132         indexRange = gl::IndexRange(startVertex, startVertex + vertexCount - 1, 0);
133     }
134 
135     // Pre-compute the total size of all streamed vertex and index data so a single staging buffer
136     // can be used
137     size_t stagingBufferSize = 0;
138 
139     std::optional<size_t> indexDataSize;
140     if (indexedDrawCallWithNoIndexBuffer)
141     {
142         indexDataSize = gl::GetDrawElementsTypeSize(drawElementsTypeOrInvalid) * count;
143         stagingBufferSize +=
144             rx::roundUpPow2(indexDataSize.value(), webgpu::kBufferCopyToBufferAlignment);
145     }
146 
147     const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes();
148     const std::vector<gl::VertexBinding> &bindings  = mState.getVertexBindings();
149 
150     if (clientAttributesToSync.any())
151     {
152         for (size_t attribIndex : clientAttributesToSync)
153         {
154             const gl::VertexAttribute &attrib = attribs[attribIndex];
155             const gl::VertexBinding &binding  = bindings[attrib.bindingIndex];
156 
157             size_t typeSize = gl::ComputeVertexAttributeTypeSize(attrib);
158             size_t attribSize =
159                 typeSize * gl::ComputeVertexBindingElementCount(
160                                binding.getDivisor(), indexRange->vertexCount(), instanceCount);
161             stagingBufferSize += rx::roundUpPow2(attribSize, webgpu::kBufferCopyToBufferAlignment);
162         }
163     }
164 
165     ASSERT(stagingBufferSize > 0);
166     ASSERT(stagingBufferSize % webgpu::kBufferSizeAlignment == 0);
167     webgpu::BufferHelper stagingBuffer;
168     ANGLE_TRY(stagingBuffer.initBuffer(device, stagingBufferSize,
169                                        wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite,
170                                        webgpu::MapAtCreation::Yes));
171 
172     struct BufferCopy
173     {
174         uint64_t sourceOffset;
175         webgpu::BufferHelper *dest;
176         uint64_t size;
177     };
178     std::vector<BufferCopy> stagingUploads;
179 
180     uint8_t *stagingData              = stagingBuffer.getMapWritePointer(0, stagingBufferSize);
181     size_t currentStagingDataPosition = 0;
182 
183     auto ensureStreamingBufferCreated = [device](webgpu::BufferHelper &buffer, size_t size,
184                                                  wgpu::BufferUsage usage, bool *outNewBuffer) {
185         if (buffer.valid() && buffer.requestedSize() >= size)
186         {
187             ASSERT(buffer.getBuffer().GetUsage() == usage);
188             *outNewBuffer = false;
189             return angle::Result::Continue;
190         }
191 
192         ANGLE_TRY(buffer.initBuffer(device, size, usage, webgpu::MapAtCreation::No));
193         *outNewBuffer = true;
194         return angle::Result::Continue;
195     };
196 
197     if (indexedDrawCallWithNoIndexBuffer)
198     {
199         ASSERT(indexDataSize.has_value());
200         memcpy(stagingData + currentStagingDataPosition, indices, indexDataSize.value());
201 
202         size_t copySize =
203             rx::roundUpPow2(indexDataSize.value(), webgpu::kBufferCopyToBufferAlignment);
204 
205         bool newBuffer = false;
206         ANGLE_TRY(ensureStreamingBufferCreated(
207             mStreamingIndexBuffer, copySize, wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Index,
208             &newBuffer));
209         if (newBuffer)
210         {
211             contextWgpu->invalidateIndexBuffer();
212         }
213 
214         stagingUploads.push_back({currentStagingDataPosition, &mStreamingIndexBuffer, copySize});
215 
216         currentStagingDataPosition += copySize;
217 
218         // Indices are streamed to the start of the buffer. Tell the draw call command to use 0 for
219         // firstIndex.
220         *adjustedIndicesPtr = 0;
221     }
222 
223     for (size_t attribIndex : clientAttributesToSync)
224     {
225         const gl::VertexAttribute &attrib = attribs[attribIndex];
226         const gl::VertexBinding &binding  = bindings[attrib.bindingIndex];
227 
228         size_t streamedVertexCount = gl::ComputeVertexBindingElementCount(
229             binding.getDivisor(), indexRange->vertexCount(), instanceCount);
230 
231         const size_t sourceStride = ComputeVertexAttributeStride(attrib, binding);
232         const size_t typeSize     = gl::ComputeVertexAttributeTypeSize(attrib);
233 
234         // Vertices do not apply the 'start' offset when the divisor is non-zero even when doing
235         // a non-instanced draw call
236         const size_t firstIndex = (binding.getDivisor() == 0) ? indexRange->start : 0;
237 
238         // Attributes using client memory ignore the VERTEX_ATTRIB_BINDING state.
239         // https://www.opengl.org/registry/specs/ARB/vertex_attrib_binding.txt
240         const uint8_t *inputPointer = static_cast<const uint8_t *>(attrib.pointer);
241 
242         // Pack the data when copying it, user could have supplied a very large stride that
243         // would cause the buffer to be much larger than needed.
244         if (typeSize == sourceStride)
245         {
246             // Can copy in one go, the data is packed
247             memcpy(stagingData + currentStagingDataPosition,
248                    inputPointer + (sourceStride * firstIndex), streamedVertexCount * typeSize);
249         }
250         else
251         {
252             for (size_t vertexIdx = 0; vertexIdx < streamedVertexCount; vertexIdx++)
253             {
254                 uint8_t *out = stagingData + currentStagingDataPosition + (typeSize * vertexIdx);
255                 const uint8_t *in = inputPointer + sourceStride * (vertexIdx + firstIndex);
256                 memcpy(out, in, typeSize);
257             }
258         }
259 
260         size_t copySize =
261             rx::roundUpPow2(streamedVertexCount * typeSize, webgpu::kBufferCopyToBufferAlignment);
262 
263         bool newBuffer = false;
264         ANGLE_TRY(ensureStreamingBufferCreated(
265             mStreamingArrayBuffers[attribIndex], copySize,
266             wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Vertex, &newBuffer));
267         if (newBuffer)
268         {
269             contextWgpu->invalidateVertexBuffer(attribIndex);
270         }
271 
272         stagingUploads.push_back(
273             {currentStagingDataPosition, &mStreamingArrayBuffers[attribIndex], copySize});
274 
275         currentStagingDataPosition += copySize;
276     }
277 
278     ANGLE_TRY(stagingBuffer.unmap());
279     ANGLE_TRY(contextWgpu->flush(webgpu::RenderPassClosureReason::VertexArrayStreaming));
280 
281     contextWgpu->ensureCommandEncoderCreated();
282     wgpu::CommandEncoder &commandEncoder = contextWgpu->getCurrentCommandEncoder();
283 
284     for (const BufferCopy &copy : stagingUploads)
285     {
286         commandEncoder.CopyBufferToBuffer(stagingBuffer.getBuffer(), copy.sourceOffset,
287                                           copy.dest->getBuffer(), 0, copy.size);
288     }
289 
290     return angle::Result::Continue;
291 }
292 
syncDirtyAttrib(ContextWgpu * contextWgpu,const gl::VertexAttribute & attrib,const gl::VertexBinding & binding,size_t attribIndex)293 angle::Result VertexArrayWgpu::syncDirtyAttrib(ContextWgpu *contextWgpu,
294                                                const gl::VertexAttribute &attrib,
295                                                const gl::VertexBinding &binding,
296                                                size_t attribIndex)
297 {
298     if (attrib.enabled)
299     {
300         SetBitField(mCurrentAttribs[attribIndex].enabled, true);
301         const webgpu::Format &webgpuFormat =
302             contextWgpu->getFormat(attrib.format->glInternalFormat);
303         SetBitField(mCurrentAttribs[attribIndex].format, webgpuFormat.getActualWgpuVertexFormat());
304         SetBitField(mCurrentAttribs[attribIndex].shaderLocation, attribIndex);
305         SetBitField(mCurrentAttribs[attribIndex].stride, binding.getStride());
306 
307         gl::Buffer *bufferGl = binding.getBuffer().get();
308         if (bufferGl && bufferGl->getSize() > 0)
309         {
310             SetBitField(mCurrentAttribs[attribIndex].offset,
311                         reinterpret_cast<uintptr_t>(attrib.pointer));
312             BufferWgpu *bufferWgpu            = webgpu::GetImpl(bufferGl);
313             mCurrentArrayBuffers[attribIndex] = &(bufferWgpu->getBuffer());
314         }
315         else
316         {
317             SetBitField(mCurrentAttribs[attribIndex].offset, 0);
318             mCurrentArrayBuffers[attribIndex] = &mStreamingArrayBuffers[attribIndex];
319         }
320     }
321     else
322     {
323         memset(&mCurrentAttribs[attribIndex], 0, sizeof(webgpu::PackedVertexAttribute));
324         mCurrentArrayBuffers[attribIndex] = nullptr;
325     }
326 
327     return angle::Result::Continue;
328 }
329 
syncDirtyElementArrayBuffer(ContextWgpu * contextWgpu)330 angle::Result VertexArrayWgpu::syncDirtyElementArrayBuffer(ContextWgpu *contextWgpu)
331 {
332     gl::Buffer *bufferGl = mState.getElementArrayBuffer();
333     if (bufferGl)
334     {
335         BufferWgpu *buffer  = webgpu::GetImpl(bufferGl);
336         mCurrentIndexBuffer = &buffer->getBuffer();
337     }
338     else
339     {
340         mCurrentIndexBuffer = &mStreamingIndexBuffer;
341     }
342 
343     return angle::Result::Continue;
344 }
345 
346 }  // namespace rx
347