Implement GL_OES_surfaceless_context

BUG=angleproject:1651

Change-Id: I733ccedad7c7424cdb70e21ef8d48b2a15ccdfd7
Reviewed-on: https://chromium-review.googlesource.com/434762
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/libANGLE/Caps.cpp b/src/libANGLE/Caps.cpp
index 56d70b8..665b816 100644
--- a/src/libANGLE/Caps.cpp
+++ b/src/libANGLE/Caps.cpp
@@ -211,7 +211,8 @@
       multisampleCompatibility(false),
       framebufferMixedSamples(false),
       textureNorm16(false),
-      pathRendering(false)
+      pathRendering(false),
+      surfacelessContext(false)
 {
 }
 
@@ -643,6 +644,7 @@
         map["GL_CHROMIUM_framebuffer_mixed_samples"] = esOnlyExtension(&Extensions::framebufferMixedSamples);
         map["GL_EXT_texture_norm16"] = esOnlyExtension(&Extensions::textureNorm16);
         map["GL_CHROMIUM_path_rendering"] = esOnlyExtension(&Extensions::pathRendering);
+        map["GL_OES_surfaceless_context"] = esOnlyExtension(&Extensions::surfacelessContext);
         // clang-format on
 
         return map;
diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h
index 959f07a..fbe29d6 100644
--- a/src/libANGLE/Caps.h
+++ b/src/libANGLE/Caps.h
@@ -348,6 +348,9 @@
 
     // GL_CHROMIUM_path_rendering
     bool pathRendering;
+
+    // GL_OES_surfaceless_context
+    bool surfacelessContext;
 };
 
 struct ExtensionInfo
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 9e0bf31..c44bab6 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -258,7 +258,8 @@
       mContextLostForced(false),
       mResetStrategy(GetResetStrategy(attribs)),
       mRobustAccess(GetRobustAccess(attribs)),
-      mCurrentSurface(nullptr)
+      mCurrentSurface(nullptr),
+      mSurfacelessFramebuffer(nullptr)
 {
     if (mRobustAccess)
     {
@@ -426,6 +427,8 @@
     }
     mZeroTextures.clear();
 
+    SafeDelete(mSurfacelessFramebuffer);
+
     if (mCurrentSurface != nullptr)
     {
         releaseSurface();
@@ -436,8 +439,6 @@
 
 void Context::makeCurrent(egl::Surface *surface)
 {
-    ASSERT(surface != nullptr);
-
     if (!mHasBeenCurrent)
     {
         initRendererString();
@@ -457,13 +458,27 @@
     {
         releaseSurface();
     }
-    surface->setIsCurrent(true);
-    mCurrentSurface = surface;
+
+    Framebuffer *newDefault = nullptr;
+    if (surface != nullptr)
+    {
+        surface->setIsCurrent(true);
+        mCurrentSurface = surface;
+        newDefault      = surface->getDefaultFramebuffer();
+    }
+    else
+    {
+        if (mSurfacelessFramebuffer == nullptr)
+        {
+            mSurfacelessFramebuffer = new Framebuffer(mImplementation.get());
+        }
+
+        newDefault = mSurfacelessFramebuffer;
+    }
 
     // Update default framebuffer, the binding of the previous default
     // framebuffer (or lack of) will have a nullptr.
     {
-        Framebuffer *newDefault = surface->getDefaultFramebuffer();
         if (mGLState.getReadFramebuffer() == nullptr)
         {
             mGLState.setReadFramebufferBinding(newDefault);
@@ -2436,6 +2451,9 @@
     // Enable the no error extension if the context was created with the flag.
     mExtensions.noError = mSkipValidation;
 
+    // Enable surfaceless to advertise we'll have the correct behavior when there is no default FBO
+    mExtensions.surfacelessContext = true;
+
     // Explicitly enable GL_KHR_debug
     mExtensions.debug                   = true;
     mExtensions.maxDebugMessageLength   = 1024;
diff --git a/src/libANGLE/Context.h b/src/libANGLE/Context.h
index b94570a..6023828 100644
--- a/src/libANGLE/Context.h
+++ b/src/libANGLE/Context.h
@@ -702,6 +702,7 @@
     GLenum mResetStrategy;
     bool mRobustAccess;
     egl::Surface *mCurrentSurface;
+    Framebuffer *mSurfacelessFramebuffer;
 
     State::DirtyBits mTexImageDirtyBits;
     State::DirtyObjects mTexImageDirtyObjects;
diff --git a/src/libANGLE/Framebuffer.cpp b/src/libANGLE/Framebuffer.cpp
index 5ab77ad..2a4a110 100644
--- a/src/libANGLE/Framebuffer.cpp
+++ b/src/libANGLE/Framebuffer.cpp
@@ -242,12 +242,6 @@
     return mDrawBufferStates.size();
 }
 
-Error Framebuffer::getSamplePosition(size_t index, GLfloat *xy) const
-{
-    ANGLE_TRY(mImpl->getSamplePosition(index, xy));
-    return gl::NoError();
-}
-
 bool FramebufferState::colorAttachmentsAreUniqueImages() const
 {
     for (size_t firstAttachmentIdx = 0; firstAttachmentIdx < mColorAttachments.size();
@@ -311,6 +305,18 @@
         ChannelBinding(this, static_cast<SignalToken>(DIRTY_BIT_COLOR_ATTACHMENT_0)));
 }
 
+Framebuffer::Framebuffer(rx::GLImplFactory *factory)
+    : mState(),
+      mImpl(factory->createFramebuffer(mState)),
+      mId(0),
+      mCachedStatus(GL_FRAMEBUFFER_UNDEFINED_OES),
+      mDirtyDepthAttachmentBinding(this, DIRTY_BIT_DEPTH_ATTACHMENT),
+      mDirtyStencilAttachmentBinding(this, DIRTY_BIT_STENCIL_ATTACHMENT)
+{
+    mDirtyColorAttachmentBindings.push_back(
+        ChannelBinding(this, static_cast<SignalToken>(DIRTY_BIT_COLOR_ATTACHMENT_0)));
+}
+
 Framebuffer::~Framebuffer()
 {
     SafeDelete(mImpl);
@@ -511,11 +517,15 @@
 
 GLenum Framebuffer::checkStatus(const ContextState &state)
 {
-    // The default framebuffer *must* always be complete, though it may not be
-    // subject to the same rules as application FBOs. ie, it could have 0x0 size.
+    // The default framebuffer is always complete except when it is surfaceless in which
+    // case it is always unsupported. We return early because the default framebuffer may
+    // not be subject to the same rules as application FBOs. ie, it could have 0x0 size.
     if (mId == 0)
     {
-        return GL_FRAMEBUFFER_COMPLETE;
+        ASSERT(mCachedStatus.valid());
+        ASSERT(mCachedStatus.value() == GL_FRAMEBUFFER_COMPLETE ||
+               mCachedStatus.value() == GL_FRAMEBUFFER_UNDEFINED_OES);
+        return mCachedStatus.value();
     }
 
     if (hasAnyDirtyBit() || !mCachedStatus.valid())
@@ -911,6 +921,12 @@
     return 0;
 }
 
+Error Framebuffer::getSamplePosition(size_t index, GLfloat *xy) const
+{
+    ANGLE_TRY(mImpl->getSamplePosition(index, xy));
+    return gl::NoError();
+}
+
 bool Framebuffer::hasValidDepthStencil() const
 {
     return mState.getDepthStencilAttachment() != nullptr;
@@ -991,7 +1007,10 @@
     {
         mImpl->syncState(mDirtyBits);
         mDirtyBits.reset();
-        mCachedStatus.reset();
+        if (mId != 0)
+        {
+            mCachedStatus.reset();
+        }
     }
 }
 
diff --git a/src/libANGLE/Framebuffer.h b/src/libANGLE/Framebuffer.h
index ccab31d..478ce08 100644
--- a/src/libANGLE/Framebuffer.h
+++ b/src/libANGLE/Framebuffer.h
@@ -99,8 +99,13 @@
 class Framebuffer final : public LabeledObject, public angle::SignalReceiver
 {
   public:
+    // Constructor to build application-defined framebuffers
     Framebuffer(const Caps &caps, rx::GLImplFactory *factory, GLuint id);
+    // Constructor to build default framebuffers for a surface
     Framebuffer(rx::SurfaceImpl *surface);
+    // Constructor to build a fake default framebuffer when surfaceless
+    Framebuffer(rx::GLImplFactory *factory);
+
     virtual ~Framebuffer();
 
     void setLabel(const std::string &label) override;