1 // 2 // Copyright 2002 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 7 // Framebuffer.h: Defines the gl::Framebuffer class. Implements GL framebuffer 8 // objects and related functionality. [OpenGL ES 2.0.24] section 4.4 page 105. 9 10 #ifndef LIBANGLE_FRAMEBUFFER_H_ 11 #define LIBANGLE_FRAMEBUFFER_H_ 12 13 #include <vector> 14 15 #include "common/FixedVector.h" 16 #include "common/Optional.h" 17 #include "common/angleutils.h" 18 #include "libANGLE/Constants.h" 19 #include "libANGLE/Debug.h" 20 #include "libANGLE/Error.h" 21 #include "libANGLE/FramebufferAttachment.h" 22 #include "libANGLE/Observer.h" 23 #include "libANGLE/RefCountObject.h" 24 #include "libANGLE/State.h" 25 #include "libANGLE/angletypes.h" 26 27 namespace rx 28 { 29 class GLImplFactory; 30 class FramebufferImpl; 31 class RenderbufferImpl; 32 class SurfaceImpl; 33 } // namespace rx 34 35 namespace egl 36 { 37 class Display; 38 class Surface; 39 } // namespace egl 40 41 namespace gl 42 { 43 struct Caps; 44 class Context; 45 struct Extensions; 46 class Framebuffer; 47 class ImageIndex; 48 class PixelLocalStorage; 49 class Renderbuffer; 50 class TextureCapsMap; 51 52 struct FramebufferStatus 53 { 54 bool isComplete() const; 55 56 static FramebufferStatus Complete(); 57 static FramebufferStatus Incomplete(GLenum status, const char *reason); 58 59 GLenum status = GL_FRAMEBUFFER_COMPLETE; 60 const char *reason = nullptr; 61 }; 62 63 class FramebufferState final : angle::NonCopyable 64 { 65 public: 66 explicit FramebufferState(rx::UniqueSerial serial); 67 FramebufferState(const Caps &caps, FramebufferID id, rx::UniqueSerial serial); 68 ~FramebufferState(); 69 70 const std::string &getLabel() const; 71 uint32_t getReadIndex() const; 72 73 const FramebufferAttachment *getAttachment(const Context *context, GLenum attachment) const; 74 const FramebufferAttachment *getReadAttachment() const; 75 const FramebufferAttachment *getFirstNonNullAttachment() const; 76 const FramebufferAttachment *getFirstColorAttachment() const; 77 const FramebufferAttachment *getDepthOrStencilAttachment() const; 78 const FramebufferAttachment *getStencilOrDepthStencilAttachment() const; 79 const FramebufferAttachment *getColorAttachment(size_t colorAttachment) const; 80 const FramebufferAttachment *getDepthAttachment() const; 81 const FramebufferAttachment *getStencilAttachment() const; 82 const FramebufferAttachment *getDepthStencilAttachment() const; 83 const FramebufferAttachment *getReadPixelsAttachment(GLenum readFormat) const; 84 getDrawBufferStates()85 const DrawBuffersVector<GLenum> &getDrawBufferStates() const { return mDrawBufferStates; } getEnabledDrawBuffers()86 DrawBufferMask getEnabledDrawBuffers() const { return mEnabledDrawBuffers; } getReadBufferState()87 GLenum getReadBufferState() const { return mReadBufferState; } 88 getColorAttachments()89 const DrawBuffersVector<FramebufferAttachment> &getColorAttachments() const 90 { 91 return mColorAttachments; 92 } getColorAttachmentsMask()93 const DrawBufferMask getColorAttachmentsMask() const { return mColorAttachmentsMask; } 94 95 const Extents getAttachmentExtentsIntersection() const; 96 bool attachmentsHaveSameDimensions() const; 97 bool hasSeparateDepthAndStencilAttachments() const; 98 bool colorAttachmentsAreUniqueImages() const; 99 Box getDimensions() const; 100 Extents getExtents() const; 101 102 const FramebufferAttachment *getDrawBuffer(size_t drawBufferIdx) const; 103 size_t getDrawBufferCount() const; 104 getDefaultWidth()105 GLint getDefaultWidth() const { return mDefaultWidth; } getDefaultHeight()106 GLint getDefaultHeight() const { return mDefaultHeight; } getDefaultSamples()107 GLint getDefaultSamples() const { return mDefaultSamples; } getDefaultFixedSampleLocations()108 bool getDefaultFixedSampleLocations() const { return mDefaultFixedSampleLocations; } getDefaultLayers()109 GLint getDefaultLayers() const { return mDefaultLayers; } getFlipY()110 bool getFlipY() const { return mFlipY; } 111 112 bool hasDepth() const; 113 bool hasStencil() const; 114 GLuint getStencilBitCount() const; 115 116 bool hasExternalTextureAttachment() const; 117 bool hasYUVAttachment() const; 118 119 bool isMultiview() const; 120 getNumViews()121 ANGLE_INLINE GLsizei getNumViews() const 122 { 123 const FramebufferAttachment *attachment = getFirstNonNullAttachment(); 124 if (attachment == nullptr) 125 { 126 return FramebufferAttachment::kDefaultNumViews; 127 } 128 return attachment->getNumViews(); 129 } 130 131 GLint getBaseViewIndex() const; 132 getWriteControlMode()133 SrgbWriteControlMode getWriteControlMode() const { return mSrgbWriteControlMode; } 134 id()135 FramebufferID id() const { return mId; } 136 137 bool isDefault() const; 138 getSurfaceTextureOffset()139 const Offset &getSurfaceTextureOffset() const { return mSurfaceTextureOffset; } 140 getFramebufferSerial()141 rx::UniqueSerial getFramebufferSerial() const { return mFramebufferSerial; } 142 143 bool isBoundAsDrawFramebuffer(const Context *context) const; 144 isFoveationEnabled()145 bool isFoveationEnabled() const { return mFoveationState.isFoveated(); } 146 getFoveationState()147 const FoveationState &getFoveationState() const { return mFoveationState; } 148 149 private: 150 const FramebufferAttachment *getWebGLDepthStencilAttachment() const; 151 const FramebufferAttachment *getWebGLDepthAttachment() const; 152 const FramebufferAttachment *getWebGLStencilAttachment() const; 153 154 friend class Framebuffer; 155 156 // The Framebuffer ID is unique to a Context. 157 // The Framebuffer UniqueSerial is unique to a Share Group. 158 FramebufferID mId; 159 rx::UniqueSerial mFramebufferSerial; 160 std::string mLabel; 161 162 DrawBuffersVector<FramebufferAttachment> mColorAttachments; 163 FramebufferAttachment mDepthAttachment; 164 FramebufferAttachment mStencilAttachment; 165 166 // Tracks all the color buffers attached to this FramebufferDesc 167 DrawBufferMask mColorAttachmentsMask; 168 169 DrawBuffersVector<GLenum> mDrawBufferStates; 170 GLenum mReadBufferState; 171 DrawBufferMask mEnabledDrawBuffers; 172 ComponentTypeMask mDrawBufferTypeMask; 173 174 GLint mDefaultWidth; 175 GLint mDefaultHeight; 176 GLint mDefaultSamples; 177 bool mDefaultFixedSampleLocations; 178 GLint mDefaultLayers; 179 bool mFlipY; 180 181 // It's necessary to store all this extra state so we can restore attachments 182 // when DEPTH_STENCIL/DEPTH/STENCIL is unbound in WebGL 1. 183 FramebufferAttachment mWebGLDepthStencilAttachment; 184 FramebufferAttachment mWebGLDepthAttachment; 185 FramebufferAttachment mWebGLStencilAttachment; 186 bool mWebGLDepthStencilConsistent; 187 188 // Tracks if we need to initialize the resources for each attachment. 189 angle::BitSet<IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS + 2> mResourceNeedsInit; 190 191 bool mDefaultFramebufferReadAttachmentInitialized; 192 FramebufferAttachment mDefaultFramebufferReadAttachment; 193 194 // EXT_sRGB_write_control 195 SrgbWriteControlMode mSrgbWriteControlMode; 196 197 Offset mSurfaceTextureOffset; 198 199 // GL_QCOM_framebuffer_foveated 200 FoveationState mFoveationState; 201 }; 202 203 class Framebuffer final : public angle::ObserverInterface, 204 public LabeledObject, 205 public angle::Subject 206 { 207 public: 208 // Constructor to build default framebuffers. 209 Framebuffer(const Context *context, rx::GLImplFactory *factory); 210 // Constructor to build application-defined framebuffers 211 Framebuffer(const Context *context, rx::GLImplFactory *factory, FramebufferID id); 212 213 ~Framebuffer() override; 214 void onDestroy(const Context *context); 215 216 egl::Error setSurfaces(const Context *context, 217 egl::Surface *surface, 218 egl::Surface *readSurface); 219 void setReadSurface(const Context *context, egl::Surface *readSurface); 220 egl::Error unsetSurfaces(const Context *context); 221 angle::Result setLabel(const Context *context, const std::string &label) override; 222 const std::string &getLabel() const override; 223 getImplementation()224 rx::FramebufferImpl *getImplementation() const { return mImpl; } 225 id()226 FramebufferID id() const { return mState.mId; } 227 228 void setAttachment(const Context *context, 229 GLenum type, 230 GLenum binding, 231 const ImageIndex &textureIndex, 232 FramebufferAttachmentObject *resource); 233 void setAttachmentMultisample(const Context *context, 234 GLenum type, 235 GLenum binding, 236 const ImageIndex &textureIndex, 237 FramebufferAttachmentObject *resource, 238 GLsizei samples); 239 void setAttachmentMultiview(const Context *context, 240 GLenum type, 241 GLenum binding, 242 const ImageIndex &textureIndex, 243 FramebufferAttachmentObject *resource, 244 GLsizei numViews, 245 GLint baseViewIndex); 246 void resetAttachment(const Context *context, GLenum binding); 247 248 bool detachTexture(Context *context, TextureID texture); 249 bool detachRenderbuffer(Context *context, RenderbufferID renderbuffer); 250 251 const FramebufferAttachment *getColorAttachment(size_t colorAttachment) const; 252 const FramebufferAttachment *getDepthAttachment() const; 253 const FramebufferAttachment *getStencilAttachment() const; 254 const FramebufferAttachment *getDepthStencilAttachment() const; 255 const FramebufferAttachment *getDepthOrStencilAttachment() const; 256 const FramebufferAttachment *getStencilOrDepthStencilAttachment() const; 257 const FramebufferAttachment *getReadColorAttachment() const; 258 GLenum getReadColorAttachmentType() const; 259 const FramebufferAttachment *getFirstColorAttachment() const; 260 const FramebufferAttachment *getFirstNonNullAttachment() const; 261 getColorAttachments()262 const DrawBuffersVector<FramebufferAttachment> &getColorAttachments() const 263 { 264 return mState.mColorAttachments; 265 } 266 getState()267 const FramebufferState &getState() const { return mState; } 268 269 const FramebufferAttachment *getAttachment(const Context *context, GLenum attachment) const; 270 bool isMultiview() const; 271 bool readDisallowedByMultiview() const; 272 GLsizei getNumViews() const; 273 GLint getBaseViewIndex() const; 274 Extents getExtents() const; 275 276 size_t getDrawbufferStateCount() const; 277 GLenum getDrawBufferState(size_t drawBuffer) const; 278 const DrawBuffersVector<GLenum> &getDrawBufferStates() const; 279 void setDrawBuffers(size_t count, const GLenum *buffers); 280 const FramebufferAttachment *getDrawBuffer(size_t drawBuffer) const; 281 ComponentType getDrawbufferWriteType(size_t drawBuffer) const; 282 ComponentTypeMask getDrawBufferTypeMask() const; 283 DrawBufferMask getDrawBufferMask() const; 284 bool hasEnabledDrawBuffer() const; 285 286 GLenum getReadBufferState() const; 287 void setReadBuffer(GLenum buffer); 288 getNumColorAttachments()289 size_t getNumColorAttachments() const { return mState.mColorAttachments.size(); } hasDepth()290 bool hasDepth() const { return mState.hasDepth(); } hasStencil()291 bool hasStencil() const { return mState.hasStencil(); } getStencilBitCount()292 GLuint getStencilBitCount() const { return mState.getStencilBitCount(); } 293 hasExternalTextureAttachment()294 bool hasExternalTextureAttachment() const { return mState.hasExternalTextureAttachment(); } hasYUVAttachment()295 bool hasYUVAttachment() const { return mState.hasYUVAttachment(); } 296 297 // This method calls checkStatus. 298 int getSamples(const Context *context) const; 299 int getReadBufferResourceSamples(const Context *context) const; 300 301 angle::Result getSamplePosition(const Context *context, size_t index, GLfloat *xy) const; 302 303 GLint getDefaultWidth() const; 304 GLint getDefaultHeight() const; 305 GLint getDefaultSamples() const; 306 bool getDefaultFixedSampleLocations() const; 307 GLint getDefaultLayers() const; 308 bool getFlipY() const; 309 void setDefaultWidth(const Context *context, GLint defaultWidth); 310 void setDefaultHeight(const Context *context, GLint defaultHeight); 311 void setDefaultSamples(const Context *context, GLint defaultSamples); 312 void setDefaultFixedSampleLocations(const Context *context, bool defaultFixedSampleLocations); 313 void setDefaultLayers(GLint defaultLayers); 314 void setFlipY(bool flipY); 315 316 bool isFoveationEnabled() const; 317 void setFoveatedFeatureBits(const GLuint features); 318 GLuint getFoveatedFeatureBits() const; 319 bool isFoveationConfigured() const; 320 void configureFoveation(); 321 void setFocalPoint(uint32_t layer, 322 uint32_t focalPointIndex, 323 float focalX, 324 float focalY, 325 float gainX, 326 float gainY, 327 float foveaArea); 328 const FocalPoint &getFocalPoint(uint32_t layer, uint32_t focalPoint) const; 329 GLuint getSupportedFoveationFeatures() const; hasAnyAttachmentChanged()330 bool hasAnyAttachmentChanged() const { return mAttachmentChangedAfterEnablingFoveation; } 331 332 void invalidateCompletenessCache(); cachedStatusValid()333 ANGLE_INLINE bool cachedStatusValid() { return mCachedStatus.valid(); } 334 checkStatus(const Context * context)335 ANGLE_INLINE const FramebufferStatus &checkStatus(const Context *context) const 336 { 337 // The default framebuffer is always complete except when it is surfaceless in which 338 // case it is always unsupported. 339 ASSERT(!isDefault() || mCachedStatus.valid()); 340 if (isDefault() || (!hasAnyDirtyBit() && mCachedStatus.valid())) 341 { 342 return mCachedStatus.value(); 343 } 344 345 return checkStatusImpl(context); 346 } 347 348 // Helper for checkStatus == GL_FRAMEBUFFER_COMPLETE. isComplete(const Context * context)349 ANGLE_INLINE bool isComplete(const Context *context) const 350 { 351 return checkStatus(context).isComplete(); 352 } 353 354 bool hasValidDepthStencil() const; 355 356 // Returns the offset into the texture backing the default framebuffer's surface if any. Returns 357 // zero offset otherwise. The renderer will apply the offset to scissor and viewport rects used 358 // for draws, clears, and blits. 359 const Offset &getSurfaceTextureOffset() const; 360 361 angle::Result discard(const Context *context, size_t count, const GLenum *attachments); 362 angle::Result invalidate(const Context *context, size_t count, const GLenum *attachments); 363 angle::Result invalidateSub(const Context *context, 364 size_t count, 365 const GLenum *attachments, 366 const Rectangle &area); 367 368 angle::Result clear(const Context *context, GLbitfield mask); 369 angle::Result clearBufferfv(const Context *context, 370 GLenum buffer, 371 GLint drawbuffer, 372 const GLfloat *values); 373 angle::Result clearBufferuiv(const Context *context, 374 GLenum buffer, 375 GLint drawbuffer, 376 const GLuint *values); 377 angle::Result clearBufferiv(const Context *context, 378 GLenum buffer, 379 GLint drawbuffer, 380 const GLint *values); 381 angle::Result clearBufferfi(const Context *context, 382 GLenum buffer, 383 GLint drawbuffer, 384 GLfloat depth, 385 GLint stencil); 386 387 GLenum getImplementationColorReadFormat(const Context *context); 388 GLenum getImplementationColorReadType(const Context *context); 389 390 angle::Result readPixels(const Context *context, 391 const Rectangle &area, 392 GLenum format, 393 GLenum type, 394 const PixelPackState &pack, 395 Buffer *packBuffer, 396 void *pixels); 397 398 angle::Result blit(const Context *context, 399 const Rectangle &sourceArea, 400 const Rectangle &destArea, 401 GLbitfield mask, 402 GLenum filter); isDefault()403 bool isDefault() const { return mState.isDefault(); } 404 405 enum DirtyBitType : size_t 406 { 407 DIRTY_BIT_COLOR_ATTACHMENT_0, 408 DIRTY_BIT_COLOR_ATTACHMENT_MAX = 409 DIRTY_BIT_COLOR_ATTACHMENT_0 + IMPLEMENTATION_MAX_DRAW_BUFFERS, 410 DIRTY_BIT_DEPTH_ATTACHMENT = DIRTY_BIT_COLOR_ATTACHMENT_MAX, 411 DIRTY_BIT_STENCIL_ATTACHMENT, 412 DIRTY_BIT_COLOR_BUFFER_CONTENTS_0, 413 DIRTY_BIT_COLOR_BUFFER_CONTENTS_MAX = 414 DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 + IMPLEMENTATION_MAX_DRAW_BUFFERS, 415 DIRTY_BIT_DEPTH_BUFFER_CONTENTS = DIRTY_BIT_COLOR_BUFFER_CONTENTS_MAX, 416 DIRTY_BIT_STENCIL_BUFFER_CONTENTS, 417 DIRTY_BIT_DRAW_BUFFERS, 418 DIRTY_BIT_READ_BUFFER, 419 DIRTY_BIT_DEFAULT_WIDTH, 420 DIRTY_BIT_DEFAULT_HEIGHT, 421 DIRTY_BIT_DEFAULT_SAMPLES, 422 DIRTY_BIT_DEFAULT_FIXED_SAMPLE_LOCATIONS, 423 DIRTY_BIT_DEFAULT_LAYERS, 424 DIRTY_BIT_FRAMEBUFFER_SRGB_WRITE_CONTROL_MODE, 425 DIRTY_BIT_FLIP_Y, 426 DIRTY_BIT_FOVEATION, 427 DIRTY_BIT_UNKNOWN, 428 DIRTY_BIT_MAX = DIRTY_BIT_UNKNOWN 429 }; 430 431 using DirtyBits = angle::BitSet<DIRTY_BIT_MAX>; hasAnyDirtyBit()432 bool hasAnyDirtyBit() const { return mDirtyBits.any(); } 433 getActiveFloat32ColorAttachmentDrawBufferMask()434 DrawBufferMask getActiveFloat32ColorAttachmentDrawBufferMask() const 435 { 436 return mFloat32ColorAttachmentBits & getDrawBufferMask(); 437 } 438 getActiveSharedExponentColorAttachmentDrawBufferMask()439 DrawBufferMask getActiveSharedExponentColorAttachmentDrawBufferMask() const 440 { 441 return mSharedExponentColorAttachmentBits & getDrawBufferMask(); 442 } 443 hasResourceThatNeedsInit()444 bool hasResourceThatNeedsInit() const { return mState.mResourceNeedsInit.any(); } 445 446 angle::Result syncState(const Context *context, 447 GLenum framebufferBinding, 448 Command command) const; 449 450 void setWriteControlMode(SrgbWriteControlMode srgbWriteControlMode); 451 452 // Observer implementation 453 void onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message) override; 454 455 bool formsRenderingFeedbackLoopWith(const Context *context) const; 456 bool formsCopyingFeedbackLoopWith(TextureID copyTextureID, 457 GLint copyTextureLevel, 458 GLint copyTextureLayer) const; 459 460 angle::Result ensureClearAttachmentsInitialized(const Context *context, GLbitfield mask); 461 angle::Result ensureClearBufferAttachmentsInitialized(const Context *context, 462 GLenum buffer, 463 GLint drawbuffer); 464 angle::Result ensureDrawAttachmentsInitialized(const Context *context); 465 466 // Conservatively initializes both read color and depth. Blit can access the depth buffer. 467 angle::Result ensureReadAttachmentsInitialized(const Context *context); 468 Box getDimensions() const; 469 470 // ANGLE_shader_pixel_local_storage. 471 // Lazily creates a PixelLocalStorage object for this Framebuffer. 472 PixelLocalStorage &getPixelLocalStorage(const Context *); 473 // Returns nullptr if the pixel local storage object has not been created yet. peekPixelLocalStorage()474 PixelLocalStorage *peekPixelLocalStorage() const { return mPixelLocalStorage.get(); } 475 // Detaches the the pixel local storage object so the Context can call deleteContextObjects(). 476 std::unique_ptr<PixelLocalStorage> detachPixelLocalStorage(); 477 478 static const FramebufferID kDefaultDrawFramebufferHandle; 479 480 private: 481 bool detachResourceById(Context *context, GLenum resourceType, GLuint resourceId); 482 bool detachMatchingAttachment(Context *context, 483 FramebufferAttachment *attachment, 484 GLenum matchType, 485 GLuint matchId); 486 FramebufferStatus checkStatusWithGLFrontEnd(const Context *context) const; 487 const FramebufferStatus &checkStatusImpl(const Context *context) const; 488 void setAttachment(const Context *context, 489 GLenum type, 490 GLenum binding, 491 const ImageIndex &textureIndex, 492 FramebufferAttachmentObject *resource, 493 GLsizei numViews, 494 GLuint baseViewIndex, 495 bool isMultiview, 496 GLsizei samplesIn); 497 void commitWebGL1DepthStencilIfConsistent(const Context *context, 498 GLsizei numViews, 499 GLuint baseViewIndex, 500 bool isMultiview, 501 GLsizei samples); 502 void setAttachmentImpl(const Context *context, 503 GLenum type, 504 GLenum binding, 505 const ImageIndex &textureIndex, 506 FramebufferAttachmentObject *resource, 507 GLsizei numViews, 508 GLuint baseViewIndex, 509 bool isMultiview, 510 GLsizei samples); 511 void updateAttachment(const Context *context, 512 FramebufferAttachment *attachment, 513 size_t dirtyBit, 514 angle::ObserverBinding *onDirtyBinding, 515 GLenum type, 516 GLenum binding, 517 const ImageIndex &textureIndex, 518 FramebufferAttachmentObject *resource, 519 GLsizei numViews, 520 GLuint baseViewIndex, 521 bool isMultiview, 522 GLsizei samples); 523 524 void markAttachmentsInitialized(const DrawBufferMask &color, bool depth, bool stencil); 525 526 // Checks that we have a partially masked clear: 527 // * some color channels are masked out 528 // * some stencil values are masked out 529 // * scissor test partially overlaps the framebuffer 530 bool partialClearNeedsInit(const Context *context, bool color, bool depth, bool stencil); 531 bool partialBufferClearNeedsInit(const Context *context, GLenum bufferType); 532 533 FramebufferAttachment *getAttachmentFromSubjectIndex(angle::SubjectIndex index); 534 updateFloat32AndSharedExponentColorAttachmentBits(size_t index,const InternalFormat * format)535 ANGLE_INLINE void updateFloat32AndSharedExponentColorAttachmentBits( 536 size_t index, 537 const InternalFormat *format) 538 { 539 mFloat32ColorAttachmentBits.set(index, format->type == GL_FLOAT); 540 mSharedExponentColorAttachmentBits.set(index, format->type == GL_UNSIGNED_INT_5_9_9_9_REV); 541 } 542 543 angle::Result syncAllDrawAttachmentState(const Context *context, Command command) const; 544 angle::Result syncAttachmentState(const Context *context, 545 Command command, 546 const FramebufferAttachment *attachment) const; 547 548 FramebufferState mState; 549 rx::FramebufferImpl *mImpl; 550 551 mutable Optional<FramebufferStatus> mCachedStatus; 552 DrawBuffersVector<angle::ObserverBinding> mDirtyColorAttachmentBindings; 553 angle::ObserverBinding mDirtyDepthAttachmentBinding; 554 angle::ObserverBinding mDirtyStencilAttachmentBinding; 555 556 mutable DirtyBits mDirtyBits; 557 DrawBufferMask mFloat32ColorAttachmentBits; 558 DrawBufferMask mSharedExponentColorAttachmentBits; 559 560 // The dirty bits guard is checked when we get a dependent state change message. We verify that 561 // we don't set a dirty bit that isn't already set, when inside the dirty bits syncState. 562 mutable Optional<DirtyBits> mDirtyBitsGuard; 563 564 // ANGLE_shader_pixel_local_storage 565 std::unique_ptr<PixelLocalStorage> mPixelLocalStorage; 566 567 // QCOM_framebuffer_foveated 568 bool mAttachmentChangedAfterEnablingFoveation; 569 }; 570 571 using UniqueFramebufferPointer = angle::UniqueObjectPointer<Framebuffer, Context>; 572 573 } // namespace gl 574 575 #endif // LIBANGLE_FRAMEBUFFER_H_ 576