Stricter FBO completeness checks

dEQP-GLES2.functional.fbo.completeness.*:
39 Fail -> 0 Fail

Based on similar logic in Swiftshader's libGLESv2/libGLESv2.cpp

Also cache the result of glCheckFramebufferStatus,
if already called from somewhere else.

Change-Id: I3fb42dd6902cfce4043d08fd459a7069ef491f7e
diff --git a/system/GLESv2_enc/GL2Encoder.cpp b/system/GLESv2_enc/GL2Encoder.cpp
index 9eccfee..28070fa 100755
--- a/system/GLESv2_enc/GL2Encoder.cpp
+++ b/system/GLESv2_enc/GL2Encoder.cpp
@@ -18,6 +18,12 @@
 #include <assert.h>
 #include <ctype.h>
 
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <GLES2/gl2platform.h>
+
+#include <GLES3/gl3.h>
+
 #ifndef MIN
 #define MIN(a, b) ((a) < (b) ? (a) : (b))
 #endif
@@ -128,6 +134,7 @@
     OVERRIDE(glGenRenderbuffers);
     OVERRIDE(glDeleteRenderbuffers);
     OVERRIDE(glBindRenderbuffer);
+    OVERRIDE(glRenderbufferStorage);
     OVERRIDE(glFramebufferRenderbuffer);
 
     OVERRIDE(glGenFramebuffers);
@@ -136,6 +143,8 @@
     OVERRIDE(glFramebufferTexture2D);
     OVERRIDE(glFramebufferTexture3DOES);
     OVERRIDE(glGetFramebufferAttachmentParameteriv);
+
+    OVERRIDE(glCheckFramebufferStatus);
 }
 
 GL2Encoder::~GL2Encoder()
@@ -1347,8 +1356,13 @@
         GLenum format, GLenum type, const GLvoid* pixels)
 {
     GL2Encoder* ctx = (GL2Encoder*)self;
+    GLClientState* state = ctx->m_state;
     if (target == GL_TEXTURE_2D || target == GL_TEXTURE_EXTERNAL_OES) {
         ctx->override2DTextureTarget(target);
+        state->setBoundTextureInternalFormat(target, internalformat);
+        state->setBoundTextureFormat(target, format);
+        state->setBoundTextureType(target, type);
+
         ctx->m_glTexImage2D_enc(ctx, target, level, internalformat, width,
                 height, border, format, type, pixels);
         ctx->restore2DTextureTarget();
@@ -1428,6 +1442,84 @@
     state->bindRenderbuffer(target, renderbuffer);
 }
 
+void GL2Encoder::s_glRenderbufferStorage(void* self,
+        GLenum target, GLenum internalformat,
+        GLsizei width, GLsizei height) {
+    GL2Encoder* ctx = (GL2Encoder*) self;
+    GLClientState* state = ctx->m_state;
+
+    SET_ERROR_IF(target != GL_RENDERBUFFER, GL_INVALID_ENUM);
+    switch (internalformat) {
+    // Funny internal formats
+    // that will cause an incomplete framebuffer
+    // attachment error. For dEQP,
+    // we can also just abort early here in
+    // RenderbufferStorage with a GL_INVALID_ENUM.
+    case GL_DEPTH_COMPONENT32F:
+    case GL_R8:
+    case GL_R8UI:
+    case GL_R8I:
+    case GL_R16UI:
+    case GL_R16I:
+    case GL_R32UI:
+    case GL_R32I:
+    case GL_RG8:
+    case GL_RG8UI:
+    case GL_RG8I:
+    case GL_RG16UI:
+    case GL_RG16I:
+    case GL_RG32UI:
+    case GL_RG32I:
+    case GL_SRGB8_ALPHA8:
+    case GL_RGB10_A2:
+    case GL_RGBA8UI:
+    case GL_RGBA8I:
+    case GL_RGB10_A2UI:
+    case GL_RGBA16UI:
+    case GL_RGBA16I:
+    case GL_RGBA32I:
+    case GL_RGBA32UI:
+    case GL_R11F_G11F_B10F:
+    case GL_R32F:
+    case GL_RG32F:
+    case GL_RGB32F:
+    case GL_RGBA32F:
+        SET_ERROR_IF(true, GL_INVALID_ENUM);
+        break;
+    // These 4 formats are still not OK,
+    // but dEQP expects GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT or
+    // GL_FRAMEBUFFER_UNSUPPORTED,
+    // not a GL_INVALID_ENUM from earlier on.
+    // So let's forward these to the rest of
+    // FBO initialization
+    case GL_R16F:
+    case GL_RG16F:
+    case GL_RGB16F:
+    case GL_RGBA16F:
+    // These formats are OK
+    case GL_DEPTH_COMPONENT16:
+    case GL_DEPTH_COMPONENT24:
+    case GL_DEPTH_COMPONENT32_OES:
+    case GL_RGBA4:
+    case GL_RGB5_A1:
+    case GL_RGB565:
+    case GL_RGB8_OES:
+    case GL_RGBA8_OES:
+    case GL_STENCIL_INDEX8:
+    case GL_DEPTH32F_STENCIL8:
+    case GL_DEPTH24_STENCIL8_OES:
+        break;
+    // Everything else: still not OK,
+    // and they need the GL_INVALID_ENUM
+    default:
+        SET_ERROR_IF(true, GL_INVALID_ENUM);
+    }
+
+    state->setBoundRenderbufferFormat(internalformat);
+    ctx->m_glRenderbufferStorage_enc(self, target, internalformat,
+                                     width, height);
+}
+
 void GL2Encoder::s_glFramebufferRenderbuffer(void* self,
         GLenum target, GLenum attachment,
         GLenum renderbuffertarget, GLuint renderbuffer) {
@@ -1509,3 +1601,71 @@
 
     ctx->m_glGetFramebufferAttachmentParameteriv_enc(self, target, attachment, pname, params);
 }
+
+bool GL2Encoder::isCompleteFbo(const GLClientState* state,
+                               GLenum attachment) const {
+    FboFormatInfo fbo_format_info;
+    state->getBoundFramebufferFormat(attachment, &fbo_format_info);
+
+    bool res;
+    switch (fbo_format_info.type) {
+    case FBO_ATTACHMENT_RENDERBUFFER:
+        switch (fbo_format_info.rb_format) {
+        case GL_R16F:
+        case GL_RG16F:
+        case GL_RGB16F:
+        case GL_RGBA16F:
+            res = false;
+            break;
+        case GL_STENCIL_INDEX8:
+            if (attachment == GL_STENCIL_ATTACHMENT) {
+                res = true;
+            } else {
+                res = false;
+            }
+            break;
+        default:
+            res = true;
+        }
+        break;
+    case FBO_ATTACHMENT_TEXTURE:
+        // No float/half-float formats allowed for RGB(A)
+        if (fbo_format_info.tex_internalformat == GL_RGB ||
+            fbo_format_info.tex_internalformat == GL_RGBA) {
+            switch (fbo_format_info.tex_type) {
+            case GL_FLOAT:
+            case GL_HALF_FLOAT_OES:
+                res = false;
+                break;
+            default:
+                res = true;
+            }
+        } else {
+            res = true;
+        }
+        break;
+    case FBO_ATTACHMENT_NONE:
+        res = true;
+        break;
+    default:
+        res = true;
+    }
+    return res;
+}
+
+GLenum GL2Encoder::s_glCheckFramebufferStatus(void* self, GLenum target) {
+    GL2Encoder* ctx = (GL2Encoder*)self;
+    GLClientState* state = ctx->m_state;
+
+    bool complete = ctx->isCompleteFbo(state, GL_COLOR_ATTACHMENT0) &&
+                    ctx->isCompleteFbo(state, GL_DEPTH_ATTACHMENT) &&
+                    ctx->isCompleteFbo(state, GL_STENCIL_ATTACHMENT);
+    if (!complete) {
+        state->setCheckFramebufferStatus(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
+        return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+    } else {
+        GLenum host_checkstatus = ctx->m_glCheckFramebufferStatus_enc(self, target);
+        state->setCheckFramebufferStatus(host_checkstatus);
+        return host_checkstatus;
+    }
+}