--- src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.mm.orig
+++ src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.mm
@@ -43,6 +43,7 @@
     const rx::FunctionsGL *mFunctions;
 
     GLuint mReadFramebuffer;
+    GLuint mPresentProgram;
 }
 - (id)initWithSharedState:(rx::SharedSwapState *)swapState
               withContext:(CGLContextObj)displayContext
@@ -136,19 +137,73 @@
         [self setNeedsDisplay];
     }
 
-    // TODO(cwallez) support 2.1 contexts too that don't have blitFramebuffer nor the
-    // GL_DRAW_FRAMEBUFFER_BINDING query
-    GLint drawFBO;
-    mFunctions->getIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &drawFBO);
-
-    mFunctions->bindFramebuffer(GL_FRAMEBUFFER, mReadFramebuffer);
-    mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-                                     texture.texture, 0);
-
-    mFunctions->bindFramebuffer(GL_READ_FRAMEBUFFER, mReadFramebuffer);
-    mFunctions->bindFramebuffer(GL_DRAW_FRAMEBUFFER, drawFBO);
-    mFunctions->blitFramebuffer(0, 0, texture.width, texture.height, 0, 0, texture.width,
-                                texture.height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+    if (mFunctions->blitFramebuffer != nullptr)
+    {
+        GLint drawFBO;
+        mFunctions->getIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &drawFBO);
+
+        mFunctions->bindFramebuffer(GL_FRAMEBUFFER, mReadFramebuffer);
+        mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                                         texture.texture, 0);
+
+        mFunctions->bindFramebuffer(GL_READ_FRAMEBUFFER, mReadFramebuffer);
+        mFunctions->bindFramebuffer(GL_DRAW_FRAMEBUFFER, drawFBO);
+        mFunctions->blitFramebuffer(0, 0, texture.width, texture.height, 0, 0, texture.width,
+                                    texture.height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
+    }
+    else
+    {
+        // GL 2.x without GL_EXT_framebuffer_blit (e.g. Mac OS X 10.6): draw the presented
+        // texture with a minimal GLSL 1.10 program instead. This context is private to the
+        // layer, so state set here cannot interfere with ANGLE's state tracking.
+        if (mPresentProgram == 0)
+        {
+            const char *vsSource =
+                "attribute vec2 pos;\n"
+                "varying vec2 texcoord;\n"
+                "void main()\n"
+                "{\n"
+                "    gl_Position = vec4(pos, 0.0, 1.0);\n"
+                "    texcoord    = pos * 0.5 + 0.5;\n"
+                "}\n";
+            const char *fsSource =
+                "varying vec2 texcoord;\n"
+                "uniform sampler2D tex;\n"
+                "void main()\n"
+                "{\n"
+                "    gl_FragColor = texture2D(tex, texcoord);\n"
+                "}\n";
+
+            GLuint vs = mFunctions->createShader(GL_VERTEX_SHADER);
+            mFunctions->shaderSource(vs, 1, &vsSource, nullptr);
+            mFunctions->compileShader(vs);
+
+            GLuint fs = mFunctions->createShader(GL_FRAGMENT_SHADER);
+            mFunctions->shaderSource(fs, 1, &fsSource, nullptr);
+            mFunctions->compileShader(fs);
+
+            mPresentProgram = mFunctions->createProgram();
+            mFunctions->attachShader(mPresentProgram, vs);
+            mFunctions->attachShader(mPresentProgram, fs);
+            mFunctions->bindAttribLocation(mPresentProgram, 0, "pos");
+            mFunctions->linkProgram(mPresentProgram);
+            mFunctions->deleteShader(vs);
+            mFunctions->deleteShader(fs);
+
+            mFunctions->useProgram(mPresentProgram);
+            mFunctions->uniform1i(mFunctions->getUniformLocation(mPresentProgram, "tex"), 0);
+        }
+
+        static const GLfloat kQuad[] = {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f};
+
+        mFunctions->activeTexture(GL_TEXTURE0);
+        mFunctions->bindTexture(GL_TEXTURE_2D, texture.texture);
+        mFunctions->useProgram(mPresentProgram);
+        mFunctions->bindBuffer(GL_ARRAY_BUFFER, 0);
+        mFunctions->vertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, kQuad);
+        mFunctions->enableVertexAttribArray(0);
+        mFunctions->drawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    }
 
     // Call the super method to flush the context
     [super drawInCGLContext:glContext
@@ -232,6 +287,10 @@
                                    ANGLE_UNSAFE_TODO(mSwapState.textures[i].texture));
         mFunctions->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA,
                                GL_UNSIGNED_BYTE, nullptr);
+        // The layer's no-blit present path samples these textures; without filters set they
+        // are incomplete (default MIN_FILTER requires mipmaps).
+        mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
         ANGLE_UNSAFE_TODO(mSwapState.textures[i].width)  = width;
         ANGLE_UNSAFE_TODO(mSwapState.textures[i].height) = height;
         ANGLE_UNSAFE_TODO(mSwapState.textures[i].swapId) = 0;
@@ -246,8 +305,6 @@
     [mLayer addSublayer:mSwapLayer];
 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
     [mSwapLayer setContentsScale:[mLayer contentsScale]];
-#else
-    [mSwapLayer setContentsScale:1.0];
 #endif
 
     mFunctions->genRenderbuffers(1, &mDSRenderbuffer);
