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 115 bool hasExternalTextureAttachment() const; 116 bool hasYUVAttachment() const; 117 118 bool isMultiview() const; 119 getNumViews()120 ANGLE_INLINE GLsizei getNumViews() const 121 { 122 const FramebufferAttachment *attachment = getFirstNonNullAttachment(); 123 if (attachment == nullptr) 124 { 125 return FramebufferAttachment::kDefaultNumViews; 126 } 127 return attachment->getNumViews(); 128 } 129 130 GLint getBaseViewIndex() const; 131 getWriteControlMode()132 SrgbWriteControlMode getWriteControlMode() const { return mSrgbWriteControlMode; } 133 id()134 FramebufferID id() const { return mId; } 135 136 bool isDefault() const; 137 getSurfaceTextureOffset()138 const Offset &getSurfaceTextureOffset() const { return mSurfaceTextureOffset; } 139 getFramebufferSerial()140 rx::UniqueSerial getFramebufferSerial() const { return mFramebufferSerial; } 141 142 bool isBoundAsDrawFramebuffer(const Context *context) const; 143 isFoveationEnabled()144 bool isFoveationEnabled() const { return mFoveationState.isFoveated(); } 145 getFoveationState()146 const FoveationState &getFoveationState() const { return mFoveationState; } 147 148 private: 149 const FramebufferAttachment *getWebGLDepthStencilAttachment() const; 150 const FramebufferAttachment *getWebGLDepthAttachment() const; 151 const FramebufferAttachment *getWebGLStencilAttachment() const; 152 153 friend class Framebuffer; 154 155 // The Framebuffer ID is unique to a Context. 156 // The Framebuffer UniqueSerial is unique to a Share Group. 157 FramebufferID mId; 158 rx::UniqueSerial mFramebufferSerial; 159 std::string mLabel; 160 161 DrawBuffersVector<FramebufferAttachment> mColorAttachments; 162 FramebufferAttachment mDepthAttachment; 163 FramebufferAttachment mStencilAttachment; 164 165 // Tracks all the color buffers attached to this FramebufferDesc 166 DrawBufferMask mColorAttachmentsMask; 167 168 DrawBuffersVector<GLenum> mDrawBufferStates; 169 GLenum mReadBufferState; 170 DrawBufferMask mEnabledDrawBuffers; 171 ComponentTypeMask mDrawBufferTypeMask; 172 173 GLint mDefaultWidth; 174 GLint mDefaultHeight; 175 GLint mDefaultSamples; 176 bool mDefaultFixedSampleLocations; 177 GLint mDefaultLayers; 178 bool mFlipY; 179 180 // It's necessary to store all this extra state so we can restore attachments 181 // when DEPTH_STENCIL/DEPTH/STENCIL is unbound in WebGL 1. 182 FramebufferAttachment mWebGLDepthStencilAttachment; 183 FramebufferAttachment mWebGLDepthAttachment; 184 FramebufferAttachment mWebGLStencilAttachment; 185 bool mWebGLDepthStencilConsistent; 186 187 // Tracks if we need to initialize the resources for each attachment. 188 angle::BitSet<IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS + 2> mResourceNeedsInit; 189 190 bool mDefaultFramebufferReadAttachmentInitialized; 191 FramebufferAttachment mDefaultFramebufferReadAttachment; 192 193 // EXT_sRGB_write_control 194 SrgbWriteControlMode mSrgbWriteControlMode; 195 196 Offset mSurfaceTextureOffset; 197 198 // GL_QCOM_framebuffer_foveated 199 FoveationState mFoveationState; 200 }; 201 202 class Framebuffer final : public angle::ObserverInterface, 203 public LabeledObject, 204 public angle::Subject 205 { 206 public: 207 // Constructor to build default framebuffers. 208 Framebuffer(const Context *context, rx::GLImplFactory *factory); 209 // Constructor to build application-defined framebuffers 210 Framebuffer(const Context *context, rx::GLImplFactory *factory, FramebufferID id); 211 212 ~Framebuffer() override; 213 void onDestroy(const Context *context); 214 215 egl::Error setSurfaces(const Context *context, 216 egl::Surface *surface, 217 egl::Surface *readSurface); 218 void setReadSurface(const Context *context, egl::Surface *readSurface); 219 egl::Error unsetSurfaces(const Context *context); 220 angle::Result setLabel(const Context *context, const std::string &label) override; 221 const std::string &getLabel() const override; 222 getImplementation()223 rx::FramebufferImpl *getImplementation() const { return mImpl; } 224 id()225 FramebufferID id() const { return mState.mId; } 226 227 void setAttachment(const Context *context, 228 GLenum type, 229 GLenum binding, 230 const ImageIndex &textureIndex, 231 FramebufferAttachmentObject *resource); 232 void setAttachmentMultisample(const Context *context, 233 GLenum type, 234 GLenum binding, 235 const ImageIndex &textureIndex, 236 FramebufferAttachmentObject *resource, 237 GLsizei samples); 238 void setAttachmentMultiview(const Context *context, 239 GLenum type, 240 GLenum binding, 241 const ImageIndex &textureIndex, 242 FramebufferAttachmentObject *resource, 243 GLsizei numViews, 244 GLint baseViewIndex); 245 void resetAttachment(const Context *context, GLenum binding); 246 247 bool detachTexture(Context *context, TextureID texture); 248 bool detachRenderbuffer(Context *context, RenderbufferID renderbuffer); 249 250 const FramebufferAttachment *getColorAttachment(size_t colorAttachment) const; 251 const FramebufferAttachment *getDepthAttachment() const; 252 const FramebufferAttachment *getStencilAttachment() const; 253 const FramebufferAttachment *getDepthStencilAttachment() const; 254 const FramebufferAttachment *getDepthOrStencilAttachment() const; 255 const FramebufferAttachment *getStencilOrDepthStencilAttachment() const; 256 const FramebufferAttachment *getReadColorAttachment() const; 257 GLenum getReadColorAttachmentType() const; 258 const FramebufferAttachment *getFirstColorAttachment() const; 259 const FramebufferAttachment *getFirstNonNullAttachment() const; 260 getColorAttachments()261 const DrawBuffersVector<FramebufferAttachment> &getColorAttachments() const 262 { 263 return mState.mColorAttachments; 264 } 265 getState()266 const FramebufferState &getState() const { return mState; } 267 268 const FramebufferAttachment *getAttachment(const Context *context, GLenum attachment) const; 269 bool isMultiview() const; 270 bool readDisallowedByMultiview() const; 271 GLsizei getNumViews() const; 272 GLint getBaseViewIndex() const; 273 Extents getExtents() const; 274 275 size_t getDrawbufferStateCount() const; 276 GLenum getDrawBufferState(size_t drawBuffer) const; 277 const DrawBuffersVector<GLenum> &getDrawBufferStates() const; 278 void setDrawBuffers(size_t count, const GLenum *buffers); 279 const FramebufferAttachment *getDrawBuffer(size_t drawBuffer) const; 280 ComponentType getDrawbufferWriteType(size_t drawBuffer) const; 281 ComponentTypeMask getDrawBufferTypeMask() const; 282 DrawBufferMask getDrawBufferMask() const; 283 bool hasEnabledDrawBuffer() const; 284 285 GLenum getReadBufferState() const; 286 void setReadBuffer(GLenum buffer); 287 288 size_t getNumColorAttachments() const; 289 bool hasDepth() const; 290 bool hasStencil() const; 291 292 bool hasExternalTextureAttachment() const; 293 bool hasYUVAttachment() const; 294 295 bool usingExtendedDrawBuffers() const; 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