summaryrefslogtreecommitdiffstats
path: root/gfx/angle/checkout/src/libANGLE/VertexArray.h
blob: 16f036c86b006110ea0ebad5cef9f1bfa4b0dd93 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
//
// Copyright 2013 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This class contains prototypes for representing GLES 3 Vertex Array Objects:
//
//   The buffer objects that are to be used by the vertex stage of the GL are collected
//   together to form a vertex array object. All state related to the definition of data used
//   by the vertex processor is encapsulated in a vertex array object.
//

#ifndef LIBANGLE_VERTEXARRAY_H_
#define LIBANGLE_VERTEXARRAY_H_

#include "common/Optional.h"
#include "libANGLE/Constants.h"
#include "libANGLE/Debug.h"
#include "libANGLE/Observer.h"
#include "libANGLE/RefCountObject.h"
#include "libANGLE/VertexAttribute.h"

#include <vector>

namespace rx
{
class GLImplFactory;
class VertexArrayImpl;
}  // namespace rx

namespace gl
{
class Buffer;

constexpr uint32_t kElementArrayBufferIndex = MAX_VERTEX_ATTRIBS;

class VertexArrayState final : angle::NonCopyable
{
  public:
    VertexArrayState(VertexArray *vertexArray, size_t maxAttribs, size_t maxBindings);
    ~VertexArrayState();

    const std::string &getLabel() const { return mLabel; }

    Buffer *getElementArrayBuffer() const { return mElementArrayBuffer.get(); }
    size_t getMaxAttribs() const { return mVertexAttributes.size(); }
    size_t getMaxBindings() const { return mVertexBindings.size(); }
    const AttributesMask &getEnabledAttributesMask() const { return mEnabledAttributesMask; }
    const std::vector<VertexAttribute> &getVertexAttributes() const { return mVertexAttributes; }
    const VertexAttribute &getVertexAttribute(size_t attribIndex) const
    {
        return mVertexAttributes[attribIndex];
    }
    const std::vector<VertexBinding> &getVertexBindings() const { return mVertexBindings; }
    const VertexBinding &getVertexBinding(size_t bindingIndex) const
    {
        return mVertexBindings[bindingIndex];
    }
    const VertexBinding &getBindingFromAttribIndex(size_t attribIndex) const
    {
        return mVertexBindings[mVertexAttributes[attribIndex].bindingIndex];
    }
    size_t getBindingIndexFromAttribIndex(size_t attribIndex) const
    {
        return mVertexAttributes[attribIndex].bindingIndex;
    }

    void setAttribBinding(const Context *context, size_t attribIndex, GLuint newBindingIndex);

    // Extra validation performed on the Vertex Array.
    bool hasEnabledNullPointerClientArray() const;

    // Get all the attributes in an AttributesMask that are using the given binding.
    AttributesMask getBindingToAttributesMask(GLuint bindingIndex) const;

    ComponentTypeMask getVertexAttributesTypeMask() const { return mVertexAttributesTypeMask; }

    AttributesMask getClientMemoryAttribsMask() const { return mClientMemoryAttribsMask; }

    gl::AttributesMask getNullPointerClientMemoryAttribsMask() const
    {
        return mNullPointerClientMemoryAttribsMask;
    }

  private:
    void updateCachedMutableOrNonPersistentArrayBuffers(size_t index);

    friend class VertexArray;
    std::string mLabel;
    std::vector<VertexAttribute> mVertexAttributes;
    SubjectBindingPointer<Buffer> mElementArrayBuffer;
    std::vector<VertexBinding> mVertexBindings;
    AttributesMask mEnabledAttributesMask;
    ComponentTypeMask mVertexAttributesTypeMask;
    AttributesMask mLastSyncedEnabledAttributesMask;

    // This is a performance optimization for buffer binding. Allows element array buffer updates.
    friend class State;

    // From the GLES 3.1 spec:
    // When a generic attribute array is sourced from client memory, the vertex attribute binding
    // state is ignored. Thus we don't have to worry about binding state when using client memory
    // attribs.
    gl::AttributesMask mClientMemoryAttribsMask;
    gl::AttributesMask mNullPointerClientMemoryAttribsMask;

    // Used for validation cache. Indexed by attribute.
    AttributesMask mCachedMappedArrayBuffers;
    AttributesMask mCachedMutableOrImpersistentArrayBuffers;
    AttributesMask mCachedInvalidMappedArrayBuffer;
};

class VertexArrayBufferContentsObservers final : angle::NonCopyable
{
  public:
    VertexArrayBufferContentsObservers(VertexArray *vertexArray);
    void enableForBuffer(Buffer *buffer, uint32_t bufferIndex);
    void disableForBuffer(Buffer *buffer, uint32_t bufferIndex);

  private:
    VertexArray *mVertexArray;
};

class VertexArray final : public angle::ObserverInterface,
                          public LabeledObject,
                          public angle::Subject
{
  public:
    // Dirty bits for VertexArrays use a hierarchical design. At the top level, each attribute
    // has a single dirty bit. Then an array of MAX_ATTRIBS dirty bits each has a dirty bit for
    // enabled/pointer/format/binding. Bindings are handled similarly. Note that because the
    // total number of dirty bits is 33, it will not be as fast on a 32-bit machine, which
    // can't support the advanced 64-bit scanning intrinsics. We could consider packing the
    // binding and attribute bits together if this becomes a problem.
    //
    // Special note on "DIRTY_ATTRIB_POINTER_BUFFER": this is a special case when the app
    // calls glVertexAttribPointer but only changes a VBO and/or offset binding. This allows
    // the Vulkan back-end to skip performing a pipeline change for performance.
    enum DirtyBitType
    {
        DIRTY_BIT_ELEMENT_ARRAY_BUFFER,
        DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA,

        // Dirty bits for bindings.
        DIRTY_BIT_BINDING_0,
        DIRTY_BIT_BINDING_MAX = DIRTY_BIT_BINDING_0 + gl::MAX_VERTEX_ATTRIB_BINDINGS,

        // We keep separate dirty bits for bound buffers whose data changed since last update.
        DIRTY_BIT_BUFFER_DATA_0   = DIRTY_BIT_BINDING_MAX,
        DIRTY_BIT_BUFFER_DATA_MAX = DIRTY_BIT_BUFFER_DATA_0 + gl::MAX_VERTEX_ATTRIB_BINDINGS,

        // Dirty bits for attributes.
        DIRTY_BIT_ATTRIB_0   = DIRTY_BIT_BUFFER_DATA_MAX,
        DIRTY_BIT_ATTRIB_MAX = DIRTY_BIT_ATTRIB_0 + gl::MAX_VERTEX_ATTRIBS,

        DIRTY_BIT_UNKNOWN = DIRTY_BIT_ATTRIB_MAX,
        DIRTY_BIT_MAX     = DIRTY_BIT_UNKNOWN,
    };

    // We want to keep the number of dirty bits within 64 to keep iteration times fast.
    static_assert(DIRTY_BIT_MAX <= 64, "Too many vertex array dirty bits.");
    // The dirty bit processing has the logic to avoid redundant processing by removing other dirty
    // bits when it processes dirtyBits. This assertion ensures these dirty bit order matches what
    // VertexArrayVk::syncState expects.
    static_assert(DIRTY_BIT_BINDING_0 < DIRTY_BIT_BUFFER_DATA_0,
                  "BINDING dity bits should come before DATA.");
    static_assert(DIRTY_BIT_BUFFER_DATA_0 < DIRTY_BIT_ATTRIB_0,
                  "DATA dity bits should come before ATTRIB.");

    enum DirtyAttribBitType
    {
        DIRTY_ATTRIB_ENABLED,
        DIRTY_ATTRIB_POINTER,
        DIRTY_ATTRIB_FORMAT,
        DIRTY_ATTRIB_BINDING,
        DIRTY_ATTRIB_POINTER_BUFFER,
        DIRTY_ATTRIB_UNKNOWN,
        DIRTY_ATTRIB_MAX = DIRTY_ATTRIB_UNKNOWN,
    };

    enum DirtyBindingBitType
    {
        DIRTY_BINDING_BUFFER,
        DIRTY_BINDING_DIVISOR,
        DIRTY_BINDING_UNKNOWN,
        DIRTY_BINDING_MAX = DIRTY_BINDING_UNKNOWN,
    };

    using DirtyBits                = angle::BitSet<DIRTY_BIT_MAX>;
    using DirtyAttribBits          = angle::BitSet<DIRTY_ATTRIB_MAX>;
    using DirtyBindingBits         = angle::BitSet<DIRTY_BINDING_MAX>;
    using DirtyAttribBitsArray     = std::array<DirtyAttribBits, gl::MAX_VERTEX_ATTRIBS>;
    using DirtyBindingBitsArray    = std::array<DirtyBindingBits, gl::MAX_VERTEX_ATTRIB_BINDINGS>;
    using DirtyObserverBindingBits = angle::BitSet<gl::MAX_VERTEX_ATTRIB_BINDINGS>;

    VertexArray(rx::GLImplFactory *factory,
                VertexArrayID id,
                size_t maxAttribs,
                size_t maxAttribBindings);

    void onDestroy(const Context *context);

    VertexArrayID id() const { return mId; }

    angle::Result setLabel(const Context *context, const std::string &label) override;
    const std::string &getLabel() const override;

    const VertexBinding &getVertexBinding(size_t bindingIndex) const;
    const VertexAttribute &getVertexAttribute(size_t attribIndex) const;
    const VertexBinding &getBindingFromAttribIndex(size_t attribIndex) const
    {
        return mState.getBindingFromAttribIndex(attribIndex);
    }

    // Returns true if the function finds and detaches a bound buffer.
    bool detachBuffer(const Context *context, BufferID bufferID);

    void setVertexAttribDivisor(const Context *context, size_t index, GLuint divisor);
    void enableAttribute(size_t attribIndex, bool enabledState);

    void setVertexAttribPointer(const Context *context,
                                size_t attribIndex,
                                Buffer *boundBuffer,
                                GLint size,
                                VertexAttribType type,
                                bool normalized,
                                GLsizei stride,
                                const void *pointer);

    void setVertexAttribIPointer(const Context *context,
                                 size_t attribIndex,
                                 Buffer *boundBuffer,
                                 GLint size,
                                 VertexAttribType type,
                                 GLsizei stride,
                                 const void *pointer);

    void setVertexAttribFormat(size_t attribIndex,
                               GLint size,
                               VertexAttribType type,
                               bool normalized,
                               bool pureInteger,
                               GLuint relativeOffset);
    void bindVertexBuffer(const Context *context,
                          size_t bindingIndex,
                          Buffer *boundBuffer,
                          GLintptr offset,
                          GLsizei stride);
    void setVertexAttribBinding(const Context *context, size_t attribIndex, GLuint bindingIndex);
    void setVertexBindingDivisor(const Context *context, size_t bindingIndex, GLuint divisor);

    Buffer *getElementArrayBuffer() const { return mState.getElementArrayBuffer(); }
    size_t getMaxAttribs() const { return mState.getMaxAttribs(); }
    size_t getMaxBindings() const { return mState.getMaxBindings(); }

    const std::vector<VertexAttribute> &getVertexAttributes() const
    {
        return mState.getVertexAttributes();
    }
    const std::vector<VertexBinding> &getVertexBindings() const
    {
        return mState.getVertexBindings();
    }

    rx::VertexArrayImpl *getImplementation() const { return mVertexArray; }

    const AttributesMask &getEnabledAttributesMask() const
    {
        return mState.getEnabledAttributesMask();
    }

    gl::AttributesMask getClientAttribsMask() const { return mState.mClientMemoryAttribsMask; }

    bool hasEnabledNullPointerClientArray() const
    {
        return mState.hasEnabledNullPointerClientArray();
    }

    bool hasInvalidMappedArrayBuffer() const
    {
        return mState.mCachedInvalidMappedArrayBuffer.any();
    }

    const VertexArrayState &getState() const { return mState; }

    bool isBufferAccessValidationEnabled() const { return mBufferAccessValidationEnabled; }

    // Observer implementation
    void onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message) override;
    void onBufferContentsChange(uint32_t bufferIndex);

    static size_t GetVertexIndexFromDirtyBit(size_t dirtyBit);

    angle::Result syncState(const Context *context);
    bool hasAnyDirtyBit() const { return mDirtyBits.any(); }

    ComponentTypeMask getAttributesTypeMask() const { return mState.mVertexAttributesTypeMask; }
    AttributesMask getAttributesMask() const { return mState.mEnabledAttributesMask; }

    void onBindingChanged(const Context *context, int incr);
    bool hasTransformFeedbackBindingConflict(const gl::Context *context) const;

    ANGLE_INLINE angle::Result getIndexRange(const Context *context,
                                             DrawElementsType type,
                                             GLsizei indexCount,
                                             const void *indices,
                                             IndexRange *indexRangeOut) const
    {
        Buffer *elementArrayBuffer = mState.mElementArrayBuffer.get();
        if (elementArrayBuffer && mIndexRangeCache.get(type, indexCount, indices, indexRangeOut))
        {
            return angle::Result::Continue;
        }

        return getIndexRangeImpl(context, type, indexCount, indices, indexRangeOut);
    }

    void setBufferAccessValidationEnabled(bool enabled)
    {
        mBufferAccessValidationEnabled = enabled;
    }

  private:
    ~VertexArray() override;

    // This is a performance optimization for buffer binding. Allows element array buffer updates.
    friend class State;

    void setDirtyAttribBit(size_t attribIndex, DirtyAttribBitType dirtyAttribBit);
    void setDirtyBindingBit(size_t bindingIndex, DirtyBindingBitType dirtyBindingBit);
    void clearDirtyAttribBit(size_t attribIndex, DirtyAttribBitType dirtyAttribBit);

    DirtyBitType getDirtyBitFromIndex(bool contentsChanged, angle::SubjectIndex index) const;
    void setDependentDirtyBit(bool contentsChanged, angle::SubjectIndex index);

    // These are used to optimize draw call validation.
    void updateCachedBufferBindingSize(VertexBinding *binding);
    void updateCachedTransformFeedbackBindingValidation(size_t bindingIndex, const Buffer *buffer);
    void updateCachedArrayBuffersMasks(bool isMapped,
                                       bool isImmutable,
                                       bool isPersistent,
                                       const AttributesMask &boundAttributesMask);
    void updateCachedMappedArrayBuffersBinding(const VertexBinding &binding);

    angle::Result getIndexRangeImpl(const Context *context,
                                    DrawElementsType type,
                                    GLsizei indexCount,
                                    const void *indices,
                                    IndexRange *indexRangeOut) const;

    void setVertexAttribPointerImpl(const Context *context,
                                    ComponentType componentType,
                                    bool pureInteger,
                                    size_t attribIndex,
                                    Buffer *boundBuffer,
                                    GLint size,
                                    VertexAttribType type,
                                    bool normalized,
                                    GLsizei stride,
                                    const void *pointer);

    // These two functions return true if the state was dirty.
    bool setVertexAttribFormatImpl(VertexAttribute *attrib,
                                   GLint size,
                                   VertexAttribType type,
                                   bool normalized,
                                   bool pureInteger,
                                   GLuint relativeOffset);
    bool bindVertexBufferImpl(const Context *context,
                              size_t bindingIndex,
                              Buffer *boundBuffer,
                              GLintptr offset,
                              GLsizei stride);

    void onBind(const Context *context);
    void onUnbind(const Context *context);

    VertexArrayID mId;

    VertexArrayState mState;
    DirtyBits mDirtyBits;
    DirtyAttribBitsArray mDirtyAttribBits;
    DirtyBindingBitsArray mDirtyBindingBits;
    Optional<DirtyBits> mDirtyBitsGuard;

    rx::VertexArrayImpl *mVertexArray;

    std::vector<angle::ObserverBinding> mArrayBufferObserverBindings;
    // Track which observer in mArrayBufferObserverBindings is not currently been removed from
    // subject's observer list.
    DirtyObserverBindingBits mDirtyObserverBindingBits;

    AttributesMask mCachedTransformFeedbackConflictedBindingsMask;

    class IndexRangeCache final : angle::NonCopyable
    {
      public:
        IndexRangeCache();

        void invalidate() { mTypeKey = DrawElementsType::InvalidEnum; }

        bool get(DrawElementsType type,
                 GLsizei indexCount,
                 const void *indices,
                 IndexRange *indexRangeOut)
        {
            size_t offset = reinterpret_cast<uintptr_t>(indices);
            if (mTypeKey == type && mIndexCountKey == indexCount && mOffsetKey == offset)
            {
                *indexRangeOut = mPayload;
                return true;
            }

            return false;
        }

        void put(DrawElementsType type,
                 GLsizei indexCount,
                 size_t offset,
                 const IndexRange &indexRange);

      private:
        DrawElementsType mTypeKey;
        GLsizei mIndexCountKey;
        size_t mOffsetKey;
        IndexRange mPayload;
    };

    mutable IndexRangeCache mIndexRangeCache;
    bool mBufferAccessValidationEnabled;
    VertexArrayBufferContentsObservers mContentsObservers;
};

}  // namespace gl

#endif  // LIBANGLE_VERTEXARRAY_H_