| // | |
| // Copyright 2002 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. | |
| // | |
| // Framebuffer.h: Defines the gl::Framebuffer class. Implements GL framebuffer | |
| // objects and related functionality. [OpenGL ES 2.0.24] section 4.4 page 105. | |
| #ifndef LIBANGLE_FRAMEBUFFER_H_ | |
| #define LIBANGLE_FRAMEBUFFER_H_ | |
| #include <vector> | |
| #include "common/FixedVector.h" | |
| #include "common/Optional.h" | |
| #include "common/angleutils.h" | |
| #include "libANGLE/Constants.h" | |
| #include "libANGLE/Debug.h" | |
| #include "libANGLE/Error.h" | |
| #include "libANGLE/FramebufferAttachment.h" | |
| #include "libANGLE/Observer.h" | |
| #include "libANGLE/RefCountObject.h" | |
| #include "libANGLE/State.h" | |
| #include "libANGLE/angletypes.h" | |
| namespace rx | |
| { | |
| class GLImplFactory; | |
| class FramebufferImpl; | |
| class RenderbufferImpl; | |
| class SurfaceImpl; | |
| } // namespace rx | |
| namespace egl | |
| { | |
| class Display; | |
| class Surface; | |
| } // namespace egl | |
| namespace gl | |
| { | |
| struct Caps; | |
| class Context; | |
| struct Extensions; | |
| class Framebuffer; | |
| class ImageIndex; | |
| class Renderbuffer; | |
| class TextureCapsMap; | |
| struct FramebufferStatus | |
| { | |
| bool isComplete() const; | |
| static FramebufferStatus Complete(); | |
| static FramebufferStatus Incomplete(GLenum status, const char *reason); | |
| GLenum status = GL_FRAMEBUFFER_COMPLETE; | |
| const char *reason = nullptr; | |
| }; | |
| class FramebufferState final : angle::NonCopyable | |
| { | |
| public: | |
| explicit FramebufferState(rx::Serial serial); | |
| FramebufferState(const Caps &caps, FramebufferID id, rx::Serial serial); | |
| ~FramebufferState(); | |
| const std::string &getLabel() const; | |
| uint32_t getReadIndex() const; | |
| const FramebufferAttachment *getAttachment(const Context *context, GLenum attachment) const; | |
| const FramebufferAttachment *getReadAttachment() const; | |
| const FramebufferAttachment *getFirstNonNullAttachment() const; | |
| const FramebufferAttachment *getFirstColorAttachment() const; | |
| const FramebufferAttachment *getDepthOrStencilAttachment() const; | |
| const FramebufferAttachment *getStencilOrDepthStencilAttachment() const; | |
| const FramebufferAttachment *getColorAttachment(size_t colorAttachment) const; | |
| const FramebufferAttachment *getDepthAttachment() const; | |
| const FramebufferAttachment *getStencilAttachment() const; | |
| const FramebufferAttachment *getDepthStencilAttachment() const; | |
| const FramebufferAttachment *getReadPixelsAttachment(GLenum readFormat) const; | |
| const std::vector<GLenum> &getDrawBufferStates() const { return mDrawBufferStates; } | |
| DrawBufferMask getEnabledDrawBuffers() const { return mEnabledDrawBuffers; } | |
| GLenum getReadBufferState() const { return mReadBufferState; } | |
| const std::vector<FramebufferAttachment> &getColorAttachments() const | |
| { | |
| return mColorAttachments; | |
| } | |
| const DrawBufferMask getColorAttachmentsMask() const { return mColorAttachmentsMask; } | |
| const Extents getAttachmentExtentsIntersection() const; | |
| bool attachmentsHaveSameDimensions() const; | |
| bool hasSeparateDepthAndStencilAttachments() const; | |
| bool colorAttachmentsAreUniqueImages() const; | |
| Box getDimensions() const; | |
| Extents getExtents() const; | |
| const FramebufferAttachment *getDrawBuffer(size_t drawBufferIdx) const; | |
| size_t getDrawBufferCount() const; | |
| GLint getDefaultWidth() const { return mDefaultWidth; } | |
| GLint getDefaultHeight() const { return mDefaultHeight; } | |
| GLint getDefaultSamples() const { return mDefaultSamples; } | |
| bool getDefaultFixedSampleLocations() const { return mDefaultFixedSampleLocations; } | |
| GLint getDefaultLayers() const { return mDefaultLayers; } | |
| bool getFlipY() const { return mFlipY; } | |
| bool hasDepth() const; | |
| bool hasStencil() const; | |
| bool hasExternalTextureAttachment() const; | |
| bool hasYUVAttachment() const; | |
| bool isMultiview() const; | |
| ANGLE_INLINE GLsizei getNumViews() const | |
| { | |
| const FramebufferAttachment *attachment = getFirstNonNullAttachment(); | |
| if (attachment == nullptr) | |
| { | |
| return FramebufferAttachment::kDefaultNumViews; | |
| } | |
| return attachment->getNumViews(); | |
| } | |
| GLint getBaseViewIndex() const; | |
| SrgbWriteControlMode getWriteControlMode() const { return mSrgbWriteControlMode; } | |
| FramebufferID id() const { return mId; } | |
| bool isDefault() const; | |
| const Offset &getSurfaceTextureOffset() const { return mSurfaceTextureOffset; } | |
| rx::Serial getFramebufferSerial() const { return mFramebufferSerial; } | |
| bool isBoundAsDrawFramebuffer(const Context *context) const; | |
| private: | |
| const FramebufferAttachment *getWebGLDepthStencilAttachment() const; | |
| const FramebufferAttachment *getWebGLDepthAttachment() const; | |
| const FramebufferAttachment *getWebGLStencilAttachment() const; | |
| friend class Framebuffer; | |
| // The Framebuffer ID is unique to a Context. | |
| // The Framebuffer Serial is unique to a Share Group. | |
| FramebufferID mId; | |
| rx::Serial mFramebufferSerial; | |
| std::string mLabel; | |
| std::vector<FramebufferAttachment> mColorAttachments; | |
| FramebufferAttachment mDepthAttachment; | |
| FramebufferAttachment mStencilAttachment; | |
| // Tracks all the color buffers attached to this FramebufferDesc | |
| DrawBufferMask mColorAttachmentsMask; | |
| std::vector<GLenum> mDrawBufferStates; | |
| GLenum mReadBufferState; | |
| DrawBufferMask mEnabledDrawBuffers; | |
| ComponentTypeMask mDrawBufferTypeMask; | |
| GLint mDefaultWidth; | |
| GLint mDefaultHeight; | |
| GLint mDefaultSamples; | |
| bool mDefaultFixedSampleLocations; | |
| GLint mDefaultLayers; | |
| bool mFlipY; | |
| // It's necessary to store all this extra state so we can restore attachments | |
| // when DEPTH_STENCIL/DEPTH/STENCIL is unbound in WebGL 1. | |
| FramebufferAttachment mWebGLDepthStencilAttachment; | |
| FramebufferAttachment mWebGLDepthAttachment; | |
| FramebufferAttachment mWebGLStencilAttachment; | |
| bool mWebGLDepthStencilConsistent; | |
| // Tracks if we need to initialize the resources for each attachment. | |
| angle::BitSet<IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS + 2> mResourceNeedsInit; | |
| bool mDefaultFramebufferReadAttachmentInitialized; | |
| FramebufferAttachment mDefaultFramebufferReadAttachment; | |
| // EXT_sRGB_write_control | |
| SrgbWriteControlMode mSrgbWriteControlMode; | |
| Offset mSurfaceTextureOffset; | |
| }; | |
| class Framebuffer final : public angle::ObserverInterface, | |
| public LabeledObject, | |
| public angle::Subject | |
| { | |
| public: | |
| // Constructor to build application-defined framebuffers | |
| Framebuffer(const Caps &caps, | |
| rx::GLImplFactory *factory, | |
| FramebufferID id, | |
| egl::ShareGroup *shareGroup); | |
| // Constructor to build default framebuffers for a surface and context pair | |
| Framebuffer(const Context *context, egl::Surface *surface, egl::Surface *readSurface); | |
| // Constructor to build a fake default framebuffer when surfaceless | |
| Framebuffer(const Context *context, rx::GLImplFactory *factory, egl::Surface *readSurface); | |
| ~Framebuffer() override; | |
| void onDestroy(const Context *context); | |
| void setReadSurface(const Context *context, egl::Surface *readSurface); | |
| void setLabel(const Context *context, const std::string &label) override; | |
| const std::string &getLabel() const override; | |
| rx::FramebufferImpl *getImplementation() const { return mImpl; } | |
| FramebufferID id() const { return mState.mId; } | |
| void setAttachment(const Context *context, | |
| GLenum type, | |
| GLenum binding, | |
| const ImageIndex &textureIndex, | |
| FramebufferAttachmentObject *resource); | |
| void setAttachmentMultisample(const Context *context, | |
| GLenum type, | |
| GLenum binding, | |
| const ImageIndex &textureIndex, | |
| FramebufferAttachmentObject *resource, | |
| GLsizei samples); | |
| void setAttachmentMultiview(const Context *context, | |
| GLenum type, | |
| GLenum binding, | |
| const ImageIndex &textureIndex, | |
| FramebufferAttachmentObject *resource, | |
| GLsizei numViews, | |
| GLint baseViewIndex); | |
| void resetAttachment(const Context *context, GLenum binding); | |
| bool detachTexture(const Context *context, TextureID texture); | |
| bool detachRenderbuffer(const Context *context, RenderbufferID renderbuffer); | |
| const FramebufferAttachment *getColorAttachment(size_t colorAttachment) const; | |
| const FramebufferAttachment *getDepthAttachment() const; | |
| const FramebufferAttachment *getStencilAttachment() const; | |
| const FramebufferAttachment *getDepthStencilAttachment() const; | |
| const FramebufferAttachment *getDepthOrStencilAttachment() const; | |
| const FramebufferAttachment *getStencilOrDepthStencilAttachment() const; | |
| const FramebufferAttachment *getReadColorAttachment() const; | |
| GLenum getReadColorAttachmentType() const; | |
| const FramebufferAttachment *getFirstColorAttachment() const; | |
| const FramebufferAttachment *getFirstNonNullAttachment() const; | |
| const std::vector<FramebufferAttachment> &getColorAttachments() const | |
| { | |
| return mState.mColorAttachments; | |
| } | |
| const FramebufferState &getState() const { return mState; } | |
| const FramebufferAttachment *getAttachment(const Context *context, GLenum attachment) const; | |
| bool isMultiview() const; | |
| bool readDisallowedByMultiview() const; | |
| GLsizei getNumViews() const; | |
| GLint getBaseViewIndex() const; | |
| Extents getExtents() const; | |
| size_t getDrawbufferStateCount() const; | |
| GLenum getDrawBufferState(size_t drawBuffer) const; | |
| const std::vector<GLenum> &getDrawBufferStates() const; | |
| void setDrawBuffers(size_t count, const GLenum *buffers); | |
| const FramebufferAttachment *getDrawBuffer(size_t drawBuffer) const; | |
| ComponentType getDrawbufferWriteType(size_t drawBuffer) const; | |
| ComponentTypeMask getDrawBufferTypeMask() const; | |
| DrawBufferMask getDrawBufferMask() const; | |
| bool hasEnabledDrawBuffer() const; | |
| GLenum getReadBufferState() const; | |
| void setReadBuffer(GLenum buffer); | |
| size_t getNumColorAttachments() const; | |
| bool hasDepth() const; | |
| bool hasStencil() const; | |
| bool hasExternalTextureAttachment() const; | |
| bool hasYUVAttachment() const; | |
| bool usingExtendedDrawBuffers() const; | |
| // This method calls checkStatus. | |
| int getSamples(const Context *context) const; | |
| int getReadBufferResourceSamples(const Context *context) const; | |
| angle::Result getSamplePosition(const Context *context, size_t index, GLfloat *xy) const; | |
| GLint getDefaultWidth() const; | |
| GLint getDefaultHeight() const; | |
| GLint getDefaultSamples() const; | |
| bool getDefaultFixedSampleLocations() const; | |
| GLint getDefaultLayers() const; | |
| bool getFlipY() const; | |
| void setDefaultWidth(const Context *context, GLint defaultWidth); | |
| void setDefaultHeight(const Context *context, GLint defaultHeight); | |
| void setDefaultSamples(const Context *context, GLint defaultSamples); | |
| void setDefaultFixedSampleLocations(const Context *context, bool defaultFixedSampleLocations); | |
| void setDefaultLayers(GLint defaultLayers); | |
| void setFlipY(bool flipY); | |
| void invalidateCompletenessCache(); | |
| ANGLE_INLINE bool cachedStatusValid() { return mCachedStatus.valid(); } | |
| ANGLE_INLINE const FramebufferStatus &checkStatus(const Context *context) const | |
| { | |
| // The default framebuffer is always complete except when it is surfaceless in which | |
| // case it is always unsupported. | |
| ASSERT(!isDefault() || mCachedStatus.valid()); | |
| if (isDefault() || (!hasAnyDirtyBit() && mCachedStatus.valid())) | |
| { | |
| return mCachedStatus.value(); | |
| } | |
| return checkStatusImpl(context); | |
| } | |
| // Helper for checkStatus == GL_FRAMEBUFFER_COMPLETE. | |
| ANGLE_INLINE bool isComplete(const Context *context) const | |
| { | |
| return checkStatus(context).isComplete(); | |
| } | |
| bool hasValidDepthStencil() const; | |
| // Returns the offset into the texture backing the default framebuffer's surface if any. Returns | |
| // zero offset otherwise. The renderer will apply the offset to scissor and viewport rects used | |
| // for draws, clears, and blits. | |
| const Offset &getSurfaceTextureOffset() const; | |
| angle::Result discard(const Context *context, size_t count, const GLenum *attachments); | |
| angle::Result invalidate(const Context *context, size_t count, const GLenum *attachments); | |
| angle::Result invalidateSub(const Context *context, | |
| size_t count, | |
| const GLenum *attachments, | |
| const Rectangle &area); | |
| angle::Result clear(const Context *context, GLbitfield mask); | |
| angle::Result clearBufferfv(const Context *context, | |
| GLenum buffer, | |
| GLint drawbuffer, | |
| const GLfloat *values); | |
| angle::Result clearBufferuiv(const Context *context, | |
| GLenum buffer, | |
| GLint drawbuffer, | |
| const GLuint *values); | |
| angle::Result clearBufferiv(const Context *context, | |
| GLenum buffer, | |
| GLint drawbuffer, | |
| const GLint *values); | |
| angle::Result clearBufferfi(const Context *context, | |
| GLenum buffer, | |
| GLint drawbuffer, | |
| GLfloat depth, | |
| GLint stencil); | |
| GLenum getImplementationColorReadFormat(const Context *context); | |
| GLenum getImplementationColorReadType(const Context *context); | |
| angle::Result readPixels(const Context *context, | |
| const Rectangle &area, | |
| GLenum format, | |
| GLenum type, | |
| const PixelPackState &pack, | |
| Buffer *packBuffer, | |
| void *pixels); | |
| angle::Result blit(const Context *context, | |
| const Rectangle &sourceArea, | |
| const Rectangle &destArea, | |
| GLbitfield mask, | |
| GLenum filter); | |
| bool isDefault() const { return mState.isDefault(); } | |
| enum DirtyBitType : size_t | |
| { | |
| DIRTY_BIT_COLOR_ATTACHMENT_0, | |
| DIRTY_BIT_COLOR_ATTACHMENT_MAX = | |
| DIRTY_BIT_COLOR_ATTACHMENT_0 + IMPLEMENTATION_MAX_DRAW_BUFFERS, | |
| DIRTY_BIT_DEPTH_ATTACHMENT = DIRTY_BIT_COLOR_ATTACHMENT_MAX, | |
| DIRTY_BIT_STENCIL_ATTACHMENT, | |
| DIRTY_BIT_COLOR_BUFFER_CONTENTS_0, | |
| DIRTY_BIT_COLOR_BUFFER_CONTENTS_MAX = | |
| DIRTY_BIT_COLOR_BUFFER_CONTENTS_0 + IMPLEMENTATION_MAX_DRAW_BUFFERS, | |
| DIRTY_BIT_DEPTH_BUFFER_CONTENTS = DIRTY_BIT_COLOR_BUFFER_CONTENTS_MAX, | |
| DIRTY_BIT_STENCIL_BUFFER_CONTENTS, | |
| DIRTY_BIT_DRAW_BUFFERS, | |
| DIRTY_BIT_READ_BUFFER, | |
| DIRTY_BIT_DEFAULT_WIDTH, | |
| DIRTY_BIT_DEFAULT_HEIGHT, | |
| DIRTY_BIT_DEFAULT_SAMPLES, | |
| DIRTY_BIT_DEFAULT_FIXED_SAMPLE_LOCATIONS, | |
| DIRTY_BIT_DEFAULT_LAYERS, | |
| DIRTY_BIT_FRAMEBUFFER_SRGB_WRITE_CONTROL_MODE, | |
| DIRTY_BIT_FLIP_Y, | |
| DIRTY_BIT_UNKNOWN, | |
| DIRTY_BIT_MAX = DIRTY_BIT_UNKNOWN | |
| }; | |
| using DirtyBits = angle::BitSet<DIRTY_BIT_MAX>; | |
| bool hasAnyDirtyBit() const { return mDirtyBits.any(); } | |
| DrawBufferMask getActiveFloat32ColorAttachmentDrawBufferMask() const | |
| { | |
| return mFloat32ColorAttachmentBits & getDrawBufferMask(); | |
| } | |
| bool hasResourceThatNeedsInit() const { return mState.mResourceNeedsInit.any(); } | |
| angle::Result syncState(const Context *context, | |
| GLenum framebufferBinding, | |
| Command command) const; | |
| void setWriteControlMode(SrgbWriteControlMode srgbWriteControlMode); | |
| // Observer implementation | |
| void onSubjectStateChange(angle::SubjectIndex index, angle::SubjectMessage message) override; | |
| bool formsRenderingFeedbackLoopWith(const Context *context) const; | |
| bool formsCopyingFeedbackLoopWith(TextureID copyTextureID, | |
| GLint copyTextureLevel, | |
| GLint copyTextureLayer) const; | |
| angle::Result ensureClearAttachmentsInitialized(const Context *context, GLbitfield mask); | |
| angle::Result ensureClearBufferAttachmentsInitialized(const Context *context, | |
| GLenum buffer, | |
| GLint drawbuffer); | |
| angle::Result ensureDrawAttachmentsInitialized(const Context *context); | |
| // Conservatively initializes both read color and depth. Blit can access the depth buffer. | |
| angle::Result ensureReadAttachmentsInitialized(const Context *context); | |
| Box getDimensions() const; | |
| static const FramebufferID kDefaultDrawFramebufferHandle; | |
| private: | |
| bool detachResourceById(const Context *context, GLenum resourceType, GLuint resourceId); | |
| bool detachMatchingAttachment(const Context *context, | |
| FramebufferAttachment *attachment, | |
| GLenum matchType, | |
| GLuint matchId); | |
| FramebufferStatus checkStatusWithGLFrontEnd(const Context *context) const; | |
| const FramebufferStatus &checkStatusImpl(const Context *context) const; | |
| void setAttachment(const Context *context, | |
| GLenum type, | |
| GLenum binding, | |
| const ImageIndex &textureIndex, | |
| FramebufferAttachmentObject *resource, | |
| GLsizei numViews, | |
| GLuint baseViewIndex, | |
| bool isMultiview, | |
| GLsizei samplesIn); | |
| void commitWebGL1DepthStencilIfConsistent(const Context *context, | |
| GLsizei numViews, | |
| GLuint baseViewIndex, | |
| bool isMultiview, | |
| GLsizei samples); | |
| void setAttachmentImpl(const Context *context, | |
| GLenum type, | |
| GLenum binding, | |
| const ImageIndex &textureIndex, | |
| FramebufferAttachmentObject *resource, | |
| GLsizei numViews, | |
| GLuint baseViewIndex, | |
| bool isMultiview, | |
| GLsizei samples); | |
| void updateAttachment(const Context *context, | |
| FramebufferAttachment *attachment, | |
| size_t dirtyBit, | |
| angle::ObserverBinding *onDirtyBinding, | |
| GLenum type, | |
| GLenum binding, | |
| const ImageIndex &textureIndex, | |
| FramebufferAttachmentObject *resource, | |
| GLsizei numViews, | |
| GLuint baseViewIndex, | |
| bool isMultiview, | |
| GLsizei samples); | |
| void markDrawAttachmentsInitialized(bool color, bool depth, bool stencil); | |
| void markBufferInitialized(GLenum bufferType, GLint bufferIndex); | |
| angle::Result ensureBufferInitialized(const Context *context, | |
| GLenum bufferType, | |
| GLint bufferIndex); | |
| // Checks that we have a partially masked clear: | |
| // * some color channels are masked out | |
| // * some stencil values are masked out | |
| // * scissor test partially overlaps the framebuffer | |
| bool partialClearNeedsInit(const Context *context, bool color, bool depth, bool stencil); | |
| bool partialBufferClearNeedsInit(const Context *context, GLenum bufferType); | |
| FramebufferAttachment *getAttachmentFromSubjectIndex(angle::SubjectIndex index); | |
| ANGLE_INLINE void updateFloat32ColorAttachmentBits(size_t index, const InternalFormat *format) | |
| { | |
| mFloat32ColorAttachmentBits.set(index, format->type == GL_FLOAT); | |
| } | |
| FramebufferState mState; | |
| rx::FramebufferImpl *mImpl; | |
| mutable Optional<FramebufferStatus> mCachedStatus; | |
| std::vector<angle::ObserverBinding> mDirtyColorAttachmentBindings; | |
| angle::ObserverBinding mDirtyDepthAttachmentBinding; | |
| angle::ObserverBinding mDirtyStencilAttachmentBinding; | |
| mutable DirtyBits mDirtyBits; | |
| DrawBufferMask mFloat32ColorAttachmentBits; | |
| // The dirty bits guard is checked when we get a dependent state change message. We verify that | |
| // we don't set a dirty bit that isn't already set, when inside the dirty bits syncState. | |
| mutable Optional<DirtyBits> mDirtyBitsGuard; | |
| }; | |
| using UniqueFramebufferPointer = angle::UniqueObjectPointer<Framebuffer, Context>; | |
| } // namespace gl | |
| #endif // LIBANGLE_FRAMEBUFFER_H_ |