1 // 2 // Copyright 2013 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 // This class contains prototypes for representing GLES 3 Vertex Array Objects: 7 // 8 // The buffer objects that are to be used by the vertex stage of the GL are collected 9 // together to form a vertex array object. All state related to the definition of data used 10 // by the vertex processor is encapsulated in a vertex array object. 11 // 12 13 #ifndef LIBANGLE_VERTEXARRAY_H_ 14 #define LIBANGLE_VERTEXARRAY_H_ 15 16 #include "common/Optional.h" 17 #include "libANGLE/Constants.h" 18 #include "libANGLE/Debug.h" 19 #include "libANGLE/Observer.h" 20 #include "libANGLE/RefCountObject.h" 21 #include "libANGLE/VertexAttribute.h" 22 23 #include <vector> 24 25 namespace rx 26 { 27 class GLImplFactory; 28 class VertexArrayImpl; 29 } // namespace rx 30 31 namespace gl 32 { 33 class Buffer; 34 35 constexpr uint32_t kElementArrayBufferIndex = MAX_VERTEX_ATTRIBS; 36 37 class VertexArrayState final : angle::NonCopyable 38 { 39 public: 40 VertexArrayState(VertexArray *vertexArray, size_t maxAttribs, size_t maxBindings); 41 ~VertexArrayState(); 42 getLabel()43 const std::string &getLabel() const { return mLabel; } 44 getElementArrayBuffer()45 Buffer *getElementArrayBuffer() const { return mElementArrayBuffer.get(); } getMaxAttribs()46 size_t getMaxAttribs() const { return mVertexAttributes.size(); } getMaxBindings()47 size_t getMaxBindings() const { return mVertexBindings.size(); } getEnabledAttributesMask()48 const AttributesMask &getEnabledAttributesMask() const { return mEnabledAttributesMask; } getVertexAttributes()49 const std::vector<VertexAttribute> &getVertexAttributes() const { return mVertexAttributes; } getVertexAttribute(size_t attribIndex)50 const VertexAttribute &getVertexAttribute(size_t attribIndex) const 51 { 52 return mVertexAttributes[attribIndex]; 53 } getVertexBindings()54 const std::vector<VertexBinding> &getVertexBindings() const { return mVertexBindings; } getVertexBinding(size_t bindingIndex)55 const VertexBinding &getVertexBinding(size_t bindingIndex) const 56 { 57 return mVertexBindings[bindingIndex]; 58 } getBindingFromAttribIndex(size_t attribIndex)59 const VertexBinding &getBindingFromAttribIndex(size_t attribIndex) const 60 { 61 return mVertexBindings[mVertexAttributes[attribIndex].bindingIndex]; 62 } getBindingIndexFromAttribIndex(size_t attribIndex)63 size_t getBindingIndexFromAttribIndex(size_t attribIndex) const 64 { 65 return mVertexAttributes[attribIndex].bindingIndex; 66 } 67 68 void setAttribBinding(const Context *context, size_t attribIndex, GLuint newBindingIndex); 69 70 // Extra validation performed on the Vertex Array. 71 bool hasEnabledNullPointerClientArray() const; 72 73 // Get all the attributes in an AttributesMask that are using the given binding. 74 AttributesMask getBindingToAttributesMask(GLuint bindingIndex) const; 75 getVertexAttributesTypeMask()76 ComponentTypeMask getVertexAttributesTypeMask() const { return mVertexAttributesTypeMask; } 77 getClientMemoryAttribsMask()78 AttributesMask getClientMemoryAttribsMask() const { return mClientMemoryAttribsMask; } 79 getNullPointerClientMemoryAttribsMask()80 gl::AttributesMask getNullPointerClientMemoryAttribsMask() const 81 { 82 return mNullPointerClientMemoryAttribsMask; 83 } 84 85 private: 86 void updateCachedMutableOrNonPersistentArrayBuffers(size_t index); 87 88 friend class VertexArray; 89 std::string mLabel; 90 std::vector<VertexAttribute> mVertexAttributes; 91 SubjectBindingPointer<Buffer> mElementArrayBuffer; 92 std::vector<VertexBinding> mVertexBindings; 93 AttributesMask mEnabledAttributesMask; 94 ComponentTypeMask mVertexAttributesTypeMask; 95 96 // This is a performance optimization for buffer binding. Allows element array buffer updates. 97 friend class State; 98 99 // From the GLES 3.1 spec: 100 // When a generic attribute array is sourced from client memory, the vertex attribute binding 101 // state is ignored. Thus we don't have to worry about binding state when using client memory 102 // attribs. 103 gl::AttributesMask mClientMemoryAttribsMask; 104 gl::AttributesMask mNullPointerClientMemoryAttribsMask; 105 106 // Used for validation cache. Indexed by attribute. 107 AttributesMask mCachedMappedArrayBuffers; 108 AttributesMask mCachedMutableOrImpersistentArrayBuffers; 109 AttributesMask mCachedInvalidMappedArrayBuffer; 110 }; 111 112 class VertexArrayBufferContentsObservers final : angle::NonCopyable 113 { 114 public: 115 VertexArrayBufferContentsObservers(VertexArray *vertexArray); 116 void enableForBuffer(Buffer *buffer, uint32_t bufferIndex); 117 void disableForBuffer(Buffer *buffer, uint32_t bufferIndex); 118 119 private: 120 VertexArray *mVertexArray; 121 }; 122 123 class VertexArray final : public angle::ObserverInterface, 124 public LabeledObject, 125 public angle::Subject 126 { 127 public: 128 // Dirty bits for VertexArrays use a hierarchical design. At the top level, each attribute 129 // has a single dirty bit. Then an array of MAX_ATTRIBS dirty bits each has a dirty bit for 130 // enabled/pointer/format/binding. Bindings are handled similarly. Note that because the 131 // total number of dirty bits is 33, it will not be as fast on a 32-bit machine, which 132 // can't support the advanced 64-bit scanning intrinsics. We could consider packing the 133 // binding and attribute bits together if this becomes a problem. 134 // 135 // Special note on "DIRTY_ATTRIB_POINTER_BUFFER": this is a special case when the app 136 // calls glVertexAttribPointer but only changes a VBO and/or offset binding. This allows 137 // the Vulkan back-end to skip performing a pipeline change for performance. 138 enum DirtyBitType 139 { 140 DIRTY_BIT_ELEMENT_ARRAY_BUFFER, 141 DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA, 142 143 // Dirty bits for attributes. 144 DIRTY_BIT_ATTRIB_0, 145 DIRTY_BIT_ATTRIB_MAX = DIRTY_BIT_ATTRIB_0 + gl::MAX_VERTEX_ATTRIBS, 146 147 // Dirty bits for bindings. 148 DIRTY_BIT_BINDING_0 = DIRTY_BIT_ATTRIB_MAX, 149 DIRTY_BIT_BINDING_MAX = DIRTY_BIT_BINDING_0 + gl::MAX_VERTEX_ATTRIB_BINDINGS, 150 151 // We keep separate dirty bits for bound buffers whose data changed since last update. 152 DIRTY_BIT_BUFFER_DATA_0 = DIRTY_BIT_BINDING_MAX, 153 DIRTY_BIT_BUFFER_DATA_MAX = DIRTY_BIT_BUFFER_DATA_0 + gl::MAX_VERTEX_ATTRIB_BINDINGS, 154 155 DIRTY_BIT_UNKNOWN = DIRTY_BIT_BUFFER_DATA_MAX, 156 DIRTY_BIT_MAX = DIRTY_BIT_UNKNOWN, 157 }; 158 159 // We want to keep the number of dirty bits within 64 to keep iteration times fast. 160 static_assert(DIRTY_BIT_MAX <= 64, "Too many vertex array dirty bits."); 161 162 enum DirtyAttribBitType 163 { 164 DIRTY_ATTRIB_ENABLED, 165 DIRTY_ATTRIB_POINTER, 166 DIRTY_ATTRIB_FORMAT, 167 DIRTY_ATTRIB_BINDING, 168 DIRTY_ATTRIB_POINTER_BUFFER, 169 DIRTY_ATTRIB_UNKNOWN, 170 DIRTY_ATTRIB_MAX = DIRTY_ATTRIB_UNKNOWN, 171 }; 172 173 enum DirtyBindingBitType 174 { 175 DIRTY_BINDING_BUFFER, 176 DIRTY_BINDING_DIVISOR, 177 DIRTY_BINDING_UNKNOWN, 178 DIRTY_BINDING_MAX = DIRTY_BINDING_UNKNOWN, 179 }; 180 181 using DirtyBits = angle::BitSet<DIRTY_BIT_MAX>; 182 using DirtyAttribBits = angle::BitSet<DIRTY_ATTRIB_MAX>; 183 using DirtyBindingBits = angle::BitSet<DIRTY_BINDING_MAX>; 184 using DirtyAttribBitsArray = std::array<DirtyAttribBits, gl::MAX_VERTEX_ATTRIBS>; 185 using DirtyBindingBitsArray = std::array<DirtyBindingBits, gl::MAX_VERTEX_ATTRIB_BINDINGS>; 186 187 VertexArray(rx::GLImplFactory *factory, 188 VertexArrayID id, 189 size_t maxAttribs, 190 size_t maxAttribBindings); 191 192 void onDestroy(const Context *context); 193 id()194 VertexArrayID id() const { return mId; } 195 196 void setLabel(const Context *context, const std::string &label) override; 197 const std::string &getLabel() const override; 198 199 const VertexBinding &getVertexBinding(size_t bindingIndex) const; 200 const VertexAttribute &getVertexAttribute(size_t attribIndex) const; getBindingFromAttribIndex(size_t attribIndex)201 const VertexBinding &getBindingFromAttribIndex(size_t attribIndex) const 202 { 203 return mState.getBindingFromAttribIndex(attribIndex); 204 } 205 206 // Returns true if the function finds and detaches a bound buffer. 207 bool detachBuffer(const Context *context, BufferID bufferID); 208 209 void setVertexAttribDivisor(const Context *context, size_t index, GLuint divisor); 210 void enableAttribute(size_t attribIndex, bool enabledState); 211 212 void setVertexAttribPointer(const Context *context, 213 size_t attribIndex, 214 Buffer *boundBuffer, 215 GLint size, 216 VertexAttribType type, 217 bool normalized, 218 GLsizei stride, 219 const void *pointer); 220 221 void setVertexAttribIPointer(const Context *context, 222 size_t attribIndex, 223 Buffer *boundBuffer, 224 GLint size, 225 VertexAttribType type, 226 GLsizei stride, 227 const void *pointer); 228 229 void setVertexAttribFormat(size_t attribIndex, 230 GLint size, 231 VertexAttribType type, 232 bool normalized, 233 bool pureInteger, 234 GLuint relativeOffset); 235 void bindVertexBuffer(const Context *context, 236 size_t bindingIndex, 237 Buffer *boundBuffer, 238 GLintptr offset, 239 GLsizei stride); 240 void setVertexAttribBinding(const Context *context, size_t attribIndex, GLuint bindingIndex); 241 void setVertexBindingDivisor(const Context *context, size_t bindingIndex, GLuint divisor); 242 getElementArrayBuffer()243 Buffer *getElementArrayBuffer() const { return mState.getElementArrayBuffer(); } getMaxAttribs()244 size_t getMaxAttribs() const { return mState.getMaxAttribs(); } getMaxBindings()245 size_t getMaxBindings() const { return mState.getMaxBindings(); } 246 getVertexAttributes()247 const std::vector<VertexAttribute> &getVertexAttributes() const 248 { 249 return mState.getVertexAttributes(); 250 } getVertexBindings()251 const std::vector<VertexBinding> &getVertexBindings() const 252 { 253 return mState.getVertexBindings(); 254 } 255 getImplementation()256 rx::VertexArrayImpl *getImplementation() const { return mVertexArray; } 257 getEnabledAttributesMask()258 const AttributesMask &getEnabledAttributesMask() const 259 { 260 return mState.getEnabledAttributesMask(); 261 } 262 getClientAttribsMask()263 gl::AttributesMask getClientAttribsMask() const { return mState.mClientMemoryAttribsMask; } 264 hasEnabledNullPointerClientArray()265 bool hasEnabledNullPointerClientArray() const 266 { 267 return mState.hasEnabledNullPointerClientArray(); 268 } 269 hasInvalidMappedArrayBuffer()270 bool hasInvalidMappedArrayBuffer() const 271 { 272 return mState.mCachedInvalidMappedArrayBuffer.any(); 273 } 274 getState()275 const VertexArrayState &getState() const { return mState; } 276 isBufferAccessValidationEnabled()277 bool isBufferAccessValidationEnabled() const { return mBufferAccessValidationEnabled; } 278 279 // Observer implementation 280 void onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message) override; 281 void onBufferContentsChange(uint32_t bufferIndex); 282 283 static size_t GetVertexIndexFromDirtyBit(size_t dirtyBit); 284 285 angle::Result syncState(const Context *context); hasAnyDirtyBit()286 bool hasAnyDirtyBit() const { return mDirtyBits.any(); } 287 getAttributesTypeMask()288 ComponentTypeMask getAttributesTypeMask() const { return mState.mVertexAttributesTypeMask; } getAttributesMask()289 AttributesMask getAttributesMask() const { return mState.mEnabledAttributesMask; } 290 291 void onBindingChanged(const Context *context, int incr); 292 bool hasTransformFeedbackBindingConflict(const gl::Context *context) const; 293 getIndexRange(const Context * context,DrawElementsType type,GLsizei indexCount,const void * indices,IndexRange * indexRangeOut)294 ANGLE_INLINE angle::Result getIndexRange(const Context *context, 295 DrawElementsType type, 296 GLsizei indexCount, 297 const void *indices, 298 IndexRange *indexRangeOut) const 299 { 300 Buffer *elementArrayBuffer = mState.mElementArrayBuffer.get(); 301 if (elementArrayBuffer && mIndexRangeCache.get(type, indexCount, indices, indexRangeOut)) 302 { 303 return angle::Result::Continue; 304 } 305 306 return getIndexRangeImpl(context, type, indexCount, indices, indexRangeOut); 307 } 308 setBufferAccessValidationEnabled(bool enabled)309 void setBufferAccessValidationEnabled(bool enabled) 310 { 311 mBufferAccessValidationEnabled = enabled; 312 } 313 314 private: 315 ~VertexArray() override; 316 317 // This is a performance optimization for buffer binding. Allows element array buffer updates. 318 friend class State; 319 320 void setDirtyAttribBit(size_t attribIndex, DirtyAttribBitType dirtyAttribBit); 321 void setDirtyBindingBit(size_t bindingIndex, DirtyBindingBitType dirtyBindingBit); 322 323 DirtyBitType getDirtyBitFromIndex(bool contentsChanged, angle::SubjectIndex index) const; 324 void setDependentDirtyBit(bool contentsChanged, angle::SubjectIndex index); 325 326 // These are used to optimize draw call validation. 327 void updateCachedBufferBindingSize(VertexBinding *binding); 328 void updateCachedTransformFeedbackBindingValidation(size_t bindingIndex, const Buffer *buffer); 329 void updateCachedArrayBuffersMasks(bool isMapped, 330 bool isImmutable, 331 bool isPersistent, 332 const AttributesMask &boundAttributesMask); 333 void updateCachedMappedArrayBuffersBinding(const VertexBinding &binding); 334 335 angle::Result getIndexRangeImpl(const Context *context, 336 DrawElementsType type, 337 GLsizei indexCount, 338 const void *indices, 339 IndexRange *indexRangeOut) const; 340 341 void setVertexAttribPointerImpl(const Context *context, 342 ComponentType componentType, 343 bool pureInteger, 344 size_t attribIndex, 345 Buffer *boundBuffer, 346 GLint size, 347 VertexAttribType type, 348 bool normalized, 349 GLsizei stride, 350 const void *pointer); 351 352 // These two functions return true if the state was dirty. 353 bool setVertexAttribFormatImpl(VertexAttribute *attrib, 354 GLint size, 355 VertexAttribType type, 356 bool normalized, 357 bool pureInteger, 358 GLuint relativeOffset); 359 bool bindVertexBufferImpl(const Context *context, 360 size_t bindingIndex, 361 Buffer *boundBuffer, 362 GLintptr offset, 363 GLsizei stride); 364 365 VertexArrayID mId; 366 367 VertexArrayState mState; 368 DirtyBits mDirtyBits; 369 DirtyAttribBitsArray mDirtyAttribBits; 370 DirtyBindingBitsArray mDirtyBindingBits; 371 Optional<DirtyBits> mDirtyBitsGuard; 372 373 rx::VertexArrayImpl *mVertexArray; 374 375 std::vector<angle::ObserverBinding> mArrayBufferObserverBindings; 376 377 AttributesMask mCachedTransformFeedbackConflictedBindingsMask; 378 379 class IndexRangeCache final : angle::NonCopyable 380 { 381 public: 382 IndexRangeCache(); 383 invalidate()384 void invalidate() { mTypeKey = DrawElementsType::InvalidEnum; } 385 get(DrawElementsType type,GLsizei indexCount,const void * indices,IndexRange * indexRangeOut)386 bool get(DrawElementsType type, 387 GLsizei indexCount, 388 const void *indices, 389 IndexRange *indexRangeOut) 390 { 391 size_t offset = reinterpret_cast<uintptr_t>(indices); 392 if (mTypeKey == type && mIndexCountKey == indexCount && mOffsetKey == offset) 393 { 394 *indexRangeOut = mPayload; 395 return true; 396 } 397 398 return false; 399 } 400 401 void put(DrawElementsType type, 402 GLsizei indexCount, 403 size_t offset, 404 const IndexRange &indexRange); 405 406 private: 407 DrawElementsType mTypeKey; 408 GLsizei mIndexCountKey; 409 size_t mOffsetKey; 410 IndexRange mPayload; 411 }; 412 413 mutable IndexRangeCache mIndexRangeCache; 414 bool mBufferAccessValidationEnabled; 415 VertexArrayBufferContentsObservers mContentsObservers; 416 }; 417 418 } // namespace gl 419 420 #endif // LIBANGLE_VERTEXARRAY_H_ 421