Bug 1232902. Update ANGLE to chromium/2592
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Tue, 15 Dec 2015 23:15:12 -0500
changeset 276599 f982d05e0cde64865fe11e1e0f4c6799497ff437
parent 276598 1101f5bec98449a2c033c0f8df80b2d8885870d5
child 276600 83d4ac0132629bf89b17e62aa91961b7b51053f2
push id69216
push userjmuizelaar@mozilla.com
push dateWed, 16 Dec 2015 15:14:08 +0000
treeherdermozilla-inbound@f982d05e0cde [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1232902
milestone46.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1232902. Update ANGLE to chromium/2592
gfx/angle/BUILD.gn
gfx/angle/DEPS
gfx/angle/README.md
gfx/angle/include/EGL/eglext.h
gfx/angle/src/commit.h
gfx/angle/src/common/debug.h
gfx/angle/src/common/string_utils.cpp
gfx/angle/src/common/string_utils.h
gfx/angle/src/common/string_utils_unittest.cpp
gfx/angle/src/compiler/translator/ASTMetadataHLSL.cpp
gfx/angle/src/compiler/translator/Compiler.cpp
gfx/angle/src/compiler/translator/Compiler.h
gfx/angle/src/compiler/translator/ForLoopUnroll.cpp
gfx/angle/src/compiler/translator/ForLoopUnroll.h
gfx/angle/src/compiler/translator/OutputHLSL.cpp
gfx/angle/src/compiler/translator/OutputHLSL.h
gfx/angle/src/compiler/translator/ParseContext.cpp
gfx/angle/src/compiler/translator/SymbolTable.cpp
gfx/angle/src/compiler/translator/SymbolTable.h
gfx/angle/src/compiler/translator/Types.cpp
gfx/angle/src/compiler/translator/ValidateLimitations.cpp
gfx/angle/src/compiler/translator/ValidateLimitations.h
gfx/angle/src/compiler/translator/intermOut.cpp
gfx/angle/src/libANGLE/BinaryStream.h
gfx/angle/src/libANGLE/BinaryStream_unittest.cpp
gfx/angle/src/libANGLE/Caps.cpp
gfx/angle/src/libANGLE/Caps.h
gfx/angle/src/libANGLE/Context.cpp
gfx/angle/src/libANGLE/Context.h
gfx/angle/src/libANGLE/Device.cpp
gfx/angle/src/libANGLE/Device.h
gfx/angle/src/libANGLE/Display.cpp
gfx/angle/src/libANGLE/Display.h
gfx/angle/src/libANGLE/Framebuffer.cpp
gfx/angle/src/libANGLE/Framebuffer.h
gfx/angle/src/libANGLE/FramebufferAttachment.h
gfx/angle/src/libANGLE/Program.cpp
gfx/angle/src/libANGLE/Program.h
gfx/angle/src/libANGLE/Renderbuffer.cpp
gfx/angle/src/libANGLE/Renderbuffer.h
gfx/angle/src/libANGLE/State.cpp
gfx/angle/src/libANGLE/State.h
gfx/angle/src/libANGLE/Surface.cpp
gfx/angle/src/libANGLE/Surface.h
gfx/angle/src/libANGLE/Surface_unittest.cpp
gfx/angle/src/libANGLE/Texture.cpp
gfx/angle/src/libANGLE/Texture.h
gfx/angle/src/libANGLE/TransformFeedback.cpp
gfx/angle/src/libANGLE/TransformFeedback.h
gfx/angle/src/libANGLE/angletypes.cpp
gfx/angle/src/libANGLE/angletypes.h
gfx/angle/src/libANGLE/formatutils.cpp
gfx/angle/src/libANGLE/renderer/DeviceImpl.h
gfx/angle/src/libANGLE/renderer/DisplayImpl.cpp
gfx/angle/src/libANGLE/renderer/FramebufferImpl.h
gfx/angle/src/libANGLE/renderer/FramebufferImpl_mock.h
gfx/angle/src/libANGLE/renderer/ProgramImpl.h
gfx/angle/src/libANGLE/renderer/ProgramImpl_mock.h
gfx/angle/src/libANGLE/renderer/SurfaceImpl.h
gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.cpp
gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.h
gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.cpp
gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.h
gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.cpp
gfx/angle/src/libANGLE/renderer/d3d/FramebufferD3D.cpp
gfx/angle/src/libANGLE/renderer/d3d/FramebufferD3D.h
gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.cpp
gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.h
gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.cpp
gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.h
gfx/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp
gfx/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.h
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Clear11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.h
gfx/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.h
gfx/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.h
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.h
gfx/angle/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.h
gfx/angle/src/libANGLE/renderer/d3d/d3d9/Framebuffer9.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d9/Framebuffer9.h
gfx/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.cpp
gfx/angle/src/libANGLE/renderer/d3d/d3d9/Renderer9.h
gfx/angle/src/libANGLE/renderer/gl/FramebufferGL.cpp
gfx/angle/src/libANGLE/renderer/gl/FramebufferGL.h
gfx/angle/src/libANGLE/renderer/gl/ProgramGL.cpp
gfx/angle/src/libANGLE/renderer/gl/ProgramGL.h
gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm
gfx/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.h
gfx/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.mm
gfx/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h
gfx/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.mm
gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.cpp
gfx/angle/src/libANGLE/renderer/gl/glx/DisplayGLX.h
gfx/angle/src/libANGLE/renderer/gl/glx/PbufferSurfaceGLX.cpp
gfx/angle/src/libANGLE/renderer/gl/glx/PbufferSurfaceGLX.h
gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.cpp
gfx/angle/src/libANGLE/renderer/gl/glx/WindowSurfaceGLX.h
gfx/angle/src/libANGLE/renderer/gl/renderergl_utils.cpp
gfx/angle/src/libANGLE/renderer/gl/wgl/FunctionsWGL.cpp
gfx/angle/src/libANGLE/renderer/gl/wgl/FunctionsWGL.h
gfx/angle/src/libANGLE/renderer/gl/wgl/PbufferSurfaceWGL.cpp
gfx/angle/src/libANGLE/renderer/gl/wgl/PbufferSurfaceWGL.h
gfx/angle/src/libANGLE/renderer/gl/wgl/WindowSurfaceWGL.cpp
gfx/angle/src/libANGLE/renderer/gl/wgl/WindowSurfaceWGL.h
gfx/angle/src/libANGLE/validationEGL.cpp
gfx/angle/src/libANGLE/validationEGL.h
gfx/angle/src/libANGLE/validationES.cpp
gfx/angle/src/libANGLE/validationES.h
gfx/angle/src/libANGLE/validationES2.cpp
gfx/angle/src/libANGLE/validationES2.h
gfx/angle/src/libANGLE/validationES3.cpp
gfx/angle/src/libANGLE/validationES3.h
gfx/angle/src/libANGLE/validationES_unittest.cpp
gfx/angle/src/libEGL.gypi
gfx/angle/src/libEGL/libEGL.cpp
gfx/angle/src/libEGL/libEGL.def
gfx/angle/src/libGLESv2.gypi
gfx/angle/src/libGLESv2/entry_points_egl.cpp
gfx/angle/src/libGLESv2/entry_points_egl_ext.cpp
gfx/angle/src/libGLESv2/entry_points_egl_ext.h
gfx/angle/src/libGLESv2/entry_points_gles_2_0.cpp
gfx/angle/src/libGLESv2/entry_points_gles_2_0_ext.cpp
gfx/angle/src/libGLESv2/entry_points_gles_3_0.cpp
gfx/angle/src/tests/angle_end2end_tests.gypi
gfx/angle/src/tests/angle_unittests.gypi
gfx/angle/src/tests/compiler_tests/MalformedShader_test.cpp
gfx/angle/src/tests/compiler_tests/TypeTracking_test.cpp
gfx/angle/src/tests/deqp.gypi
gfx/angle/src/tests/deqp_support/angle_deqp_gtest.cpp
gfx/angle/src/tests/deqp_support/dEQP-EGL-cases.txt.gz
gfx/angle/src/tests/deqp_support/dEQP-GLES2-cases.txt.gz
gfx/angle/src/tests/deqp_support/dEQP-GLES3-cases.txt.gz
gfx/angle/src/tests/deqp_support/deqp_gles2_test_expectations.txt
gfx/angle/src/tests/deqp_support/deqp_gles3_test_expectations.txt
gfx/angle/src/tests/deqp_support/generate_case_lists.py
gfx/angle/src/tests/deqp_tests/deqp_test_main.cpp
gfx/angle/src/tests/deqp_tests/deqp_tests.cpp
gfx/angle/src/tests/deqp_tests/deqp_tests.h
gfx/angle/src/tests/deqp_tests/deqp_tests.txt
gfx/angle/src/tests/deqp_tests/generate_deqp_tests.py
gfx/angle/src/tests/egl_tests/EGLDeviceTest.cpp
gfx/angle/src/tests/egl_tests/EGLX11VisualTest.cpp
gfx/angle/src/tests/gl_tests/MipmapTest.cpp
gfx/angle/src/tests/gl_tests/QueryDisplayAttribTest.cpp
gfx/angle/src/tests/gl_tests/TextureTest.cpp
gfx/angle/src/tests/test_utils/ANGLETest.cpp
gfx/angle/src/tests/test_utils/ANGLETest.h
gfx/angle/src/tests/tests.gyp
gfx/angle/src/tests/third_party/gpu_test_expectations/HowToMakeChanges.md
gfx/angle/src/tests/third_party/gpu_test_expectations/angle-mods.patch
gfx/angle/src/tests/third_party/gpu_test_expectations/angle_config.h
gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_info.cc
gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_info.h
gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_test_config.cc
gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_test_config.h
gfx/angle/src/tests/third_party/gpu_test_expectations/gpu_test_expectations_parser.cc
--- a/gfx/angle/BUILD.gn
+++ b/gfx/angle/BUILD.gn
@@ -217,16 +217,17 @@ config("libANGLE_config") {
   if (angle_enable_gl) {
     defines += [ "ANGLE_ENABLE_OPENGL" ]
   }
   if (use_x11) {
     defines += [ "ANGLE_USE_X11" ]
   }
   defines += [
     "GL_GLEXT_PROTOTYPES",
+    "EGL_EGLEXT_PROTOTYPES",
   ]
 
   if (is_win) {
     defines += [
       "GL_APICALL=",
       "EGLAPI=",
     ]
   } else {
--- a/gfx/angle/DEPS
+++ b/gfx/angle/DEPS
@@ -14,17 +14,17 @@ deps = {
   "src/tests/third_party/googlemock":
       Var('chromium_git') + "/external/googlemock.git@b2cb211e49d872101d991201362d7b97d7d69910",
 
   # Cherry is a dEQP management GUI written in Go. We use it for viewing test results.
   "third_party/cherry":
       "https://android.googlesource.com/platform/external/cherry@af6c09fe05115f0cca61ae23ee871bda27cf1ff5",
 
   "third_party/deqp/src":
-      "https://android.googlesource.com/platform/external/deqp@92f7752da82925ca5e7288c5b4814efa7a381d89",
+      "https://android.googlesource.com/platform/external/deqp@cc0ded6c77267bbb14d21aac358fc5d9690c07f8",
 
   "third_party/libpng":
       "https://android.googlesource.com/platform/external/libpng@094e181e79a3d6c23fd005679025058b7df1ad6c",
 
   "third_party/zlib":
       Var('chromium_git') + "/chromium/src/third_party/zlib@afd8c4593c010c045902f6c0501718f1823064a3",
 
   "buildtools":
--- a/gfx/angle/README.md
+++ b/gfx/angle/README.md
@@ -2,38 +2,42 @@
 The goal of ANGLE is to allow users of multiple operating systems to seamlessly run WebGL and other OpenGL ES content by translating OpenGL ES API calls to one of the hardware-supported APIs available for that platform. ANGLE currently provides translation from OpenGL ES 2.0 to desktop OpenGL, Direct3D 9, and Direct3D 11. Support for translation from OpenGL ES 3.0 to all of these APIs is nearing completion, and future plans include enabling validated ES-to-ES support.
 
 |                |  Direct3D 9   |    Direct3D 11      |    Desktop GL      |    GL ES  |
 |----------------|:-------------:|:-------------------:|:------------------:|:---------:|
 | OpenGL ES 2.0  |    complete   |      complete       |     complete       |   planned |
 | OpenGL ES 3.0  |               |  nearing completion | nearing completion |   planned |
 [Level of OpenGL ES support via backing renderers]
 
+
 |             |    Direct3D 9  |   Direct3D 11  |   Desktop GL  |
 |------------:|:--------------:|:--------------:|:-------------:|
 | Windows     |        *       |        *       |       *       |
 | Linux       |                |                |       *       |
 | Mac OS X    |                |                |   in progress |
 [Platform support via backing renderers]
 
 ANGLE v1.0.772 was certified compliant by passing the ES 2.0.3 conformance tests in October 2011. ANGLE also provides an implementation of the EGL 1.4 specification.
 
 ANGLE is used as the default WebGL backend for both Google Chrome and Mozilla Firefox on Windows platforms. Chrome uses ANGLE for all graphics rendering on Windows, including the accelerated Canvas2D implementation and the Native Client sandbox environment.
 
 Portions of the ANGLE shader compiler are used as a shader validator and translator by WebGL implementations across multiple platforms. It is used on Mac OS X, Linux, and in mobile variants of the browsers. Having one shader validator helps to ensure that a consistent set of GLSL ES shaders are accepted across browsers and platforms. The shader translator can be used to translate shaders to other shading languages, and to optionally apply shader modifications to work around bugs or quirks in the native graphics drivers. The translator targets Desktop GLSL, Direct3D HLSL, and even ESSL for native GLES2 platforms.
 
+##Browsing
+Browse ANGLE's source in the [repository](https://chromium.googlesource.com/angle/angle)
+
 ##Building
-View the [Dev setup instructions](doc/DevSetup.md).
+View the [Dev setup instructions](doc/DevSetup.md). For generating a Windows Store version of ANGLE view the [Windows Store instructions](doc/BuildingAngleForWindowsStore.md)
 
 ##Contributing
 * Join our [Google group](https://groups.google.com/group/angleproject) to keep up to date.
 * Join us on IRC in the #ANGLEproject channel on FreeNode.
 * Read about ANGLE development in our [documentation](doc).
 * Become a [code contributor](doc/ContributingCode.md).
 * Refer to ANGLE's [coding standard](doc/CodingStandard.md).
 * Learn how to [build ANGLE for Chromium development](doc/BuildingAngleForChromiumDevelopment.md).
 * [Choose an ANGLE branch](doc/ChoosingANGLEBranch.md) to track in your own project.
 * File bugs in the [issue tracker](http://code.google.com/p/angleproject/issues/list) (preferably with an isolated test-case).
 * Read about WebGL on the [Khronos WebGL Wiki](http://khronos.org/webgl/wiki/Main_Page).
-* Learn about implementation details in the [OpenGL Insights chapter on ANGLE](http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-ANGLE.pdf) and this [ANGLE presentation](https://code.google.com/p/angleproject/downloads/detail?name=ANGLE%20and%20Cross-Platform%20WebGL%20Support.pdf&can=2&q=).
+* Learn about implementation details in the [OpenGL Insights chapter on ANGLE](http://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-ANGLE.pdf) and this [ANGLE presentation](https://drive.google.com/file/d/0Bw29oYeC09QbbHoxNE5EUFh0RGs/view?usp=sharing).
 * Learn about the past, present, and future of the ANGLE implementation in [this recent presentation](https://docs.google.com/presentation/d/1CucIsdGVDmdTWRUbg68IxLE5jXwCb2y1E9YVhQo0thg/pub?start=false&loop=false).
 * If you use ANGLE in your own project, we'd love to hear about it!
 
--- a/gfx/angle/include/EGL/eglext.h
+++ b/gfx/angle/include/EGL/eglext.h
@@ -494,16 +494,21 @@ EGLAPI EGLBoolean EGLAPIENTRY eglQuerySu
 #define EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE 0x320E
 #endif /* EGL_ANGLE_platform_angle_opengl */
 
 #ifndef EGL_ANGLE_window_fixed_size
 #define EGL_ANGLE_window_fixed_size 1
 #define EGL_FIXED_SIZE_ANGLE              0x3201
 #endif /* EGL_ANGLE_window_fixed_size */
 
+#ifndef EGL_ANGLE_x11_visual
+#define EGL_ANGLE_x11_visual
+#define EGL_X11_VISUAL_ID_ANGLE 0x33A3
+#endif /* EGL_ANGLE_x11_visual */
+
 #ifndef EGL_ARM_pixmap_multisample_discard
 #define EGL_ARM_pixmap_multisample_discard 1
 #define EGL_DISCARD_SAMPLES_ARM           0x3286
 #endif /* EGL_ARM_pixmap_multisample_discard */
 
 #ifndef EGL_EXT_buffer_age
 #define EGL_EXT_buffer_age 1
 #define EGL_BUFFER_AGE_EXT                0x313D
@@ -534,16 +539,26 @@ typedef EGLBoolean (EGLAPIENTRYP PFNEGLQ
 #ifdef EGL_EGLEXT_PROTOTYPES
 EGLAPI EGLBoolean EGLAPIENTRY eglQueryDeviceAttribEXT (EGLDeviceEXT device, EGLint attribute, EGLAttrib *value);
 EGLAPI const char *EGLAPIENTRY eglQueryDeviceStringEXT (EGLDeviceEXT device, EGLint name);
 EGLAPI EGLBoolean EGLAPIENTRY eglQueryDevicesEXT (EGLint max_devices, EGLDeviceEXT *devices, EGLint *num_devices);
 EGLAPI EGLBoolean EGLAPIENTRY eglQueryDisplayAttribEXT (EGLDisplay dpy, EGLint attribute, EGLAttrib *value);
 #endif
 #endif /* EGL_EXT_device_base */
 
+#ifndef EGL_ANGLE_device_creation
+#define EGL_ANGLE_device_creation 1
+typedef EGLDeviceEXT (EGLAPIENTRYP PFNEGLCREATEDEVICEANGLEPROC) (EGLint device_type, void *native_device, const EGLAttrib *attrib_list);
+typedef EGLBoolean (EGLAPIENTRYP PFNEGLRELEASEDEVICEANGLEPROC) (EGLDeviceEXT device);
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLDeviceEXT EGLAPIENTRY eglCreateDeviceANGLE (EGLint device_type, void *native_device, const EGLAttrib *attrib_list);
+EGLAPI EGLBoolean EGLAPIENTRY eglReleaseDeviceANGLE (EGLDeviceEXT device);
+#endif
+#endif /* EGL_ANGLE_device_creation */
+
 #ifndef EGL_EXT_device_drm
 #define EGL_EXT_device_drm 1
 #define EGL_DRM_DEVICE_FILE_EXT           0x3233
 #endif /* EGL_EXT_device_drm */
 
 #ifndef EGL_EXT_device_enumeration
 #define EGL_EXT_device_enumeration 1
 #endif /* EGL_EXT_device_enumeration */
--- a/gfx/angle/src/commit.h
+++ b/gfx/angle/src/commit.h
@@ -1,3 +1,3 @@
-#define ANGLE_COMMIT_HASH "316930d51ea9"
+#define ANGLE_COMMIT_HASH "d00f803db3a2"
 #define ANGLE_COMMIT_HASH_SIZE 12
-#define ANGLE_COMMIT_DATE "2015-12-03 16:34:05 -0500"
+#define ANGLE_COMMIT_DATE "2015-12-15 23:09:49 -0500"
--- a/gfx/angle/src/common/debug.h
+++ b/gfx/angle/src/common/debug.h
@@ -100,17 +100,17 @@ bool DebugAnnotationsActive();
 #if defined(ANGLE_TRACE_ENABLED)
 #undef ANGLE_TRACE_ENABLED
 #endif
 
 // A macro asserting a condition and outputting failures to the debug log
 #if !defined(NDEBUG)
 #define ASSERT(expression) { \
     if(!(expression)) \
-        ERR("\t! Assert failed in %s(%d): "#expression"\n", __FUNCTION__, __LINE__); \
+        ERR("\t! Assert failed in %s(%d): %s\n", __FUNCTION__, __LINE__, #expression); \
         assert(expression); \
     } ANGLE_EMPTY_STATEMENT
 #define UNUSED_ASSERTION_VARIABLE(variable)
 #else
 #define ASSERT(expression) (void(0))
 #define UNUSED_ASSERTION_VARIABLE(variable) ((void)variable)
 #endif
 
--- a/gfx/angle/src/common/string_utils.cpp
+++ b/gfx/angle/src/common/string_utils.cpp
@@ -10,54 +10,98 @@
 #include "string_utils.h"
 
 #include <fstream>
 #include <sstream>
 
 namespace angle
 {
 
-void SplitString(const std::string &input,
-                 char delimiter,
-                 std::vector<std::string> *tokensOut)
+const char kWhitespaceASCII[] = " \f\n\r\t\v";
+
+std::vector<std::string> SplitString(const std::string &input,
+                                     const std::string &delimiters,
+                                     WhitespaceHandling whitespace,
+                                     SplitResult resultType)
 {
-    std::istringstream stream(input);
-    std::string token;
+    std::vector<std::string> result;
+    if (input.empty())
+    {
+        return result;
+    }
+
+    std::string::size_type start = 0;
+    while (start != std::string::npos)
+    {
+        auto end = input.find_first_of(delimiters, start);
 
-    while (std::getline(stream, token, delimiter))
-    {
-        if (!token.empty())
+        std::string piece;
+        if (end == std::string::npos)
+        {
+            piece = input.substr(start);
+            start = std::string::npos;
+        }
+        else
         {
-            tokensOut->push_back(token);
+            piece = input.substr(start, end - start);
+            start = end + 1;
+        }
+
+        if (whitespace == TRIM_WHITESPACE)
+        {
+            piece = TrimString(piece, kWhitespaceASCII);
+        }
+
+        if (resultType == SPLIT_WANT_ALL || !piece.empty())
+        {
+            result.push_back(piece);
         }
     }
+
+    return result;
 }
 
 void SplitStringAlongWhitespace(const std::string &input,
                                 std::vector<std::string> *tokensOut)
 {
-    const char *delimiters = " \f\n\r\t\v";
 
     std::istringstream stream(input);
     std::string line;
 
     while (std::getline(stream, line))
     {
         size_t prev = 0, pos;
-        while ((pos = line.find_first_of(delimiters, prev)) != std::string::npos)
+        while ((pos = line.find_first_of(kWhitespaceASCII, prev)) != std::string::npos)
         {
             if (pos > prev)
                 tokensOut->push_back(line.substr(prev, pos - prev));
             prev = pos + 1;
         }
         if (prev < line.length())
             tokensOut->push_back(line.substr(prev, std::string::npos));
     }
 }
 
+std::string TrimString(const std::string &input, const std::string &trimChars)
+{
+    auto begin = input.find_first_not_of(trimChars);
+    if (begin == std::string::npos)
+    {
+        return "";
+    }
+
+    std::string::size_type end = input.find_last_not_of(trimChars);
+    if (end == std::string::npos)
+    {
+        return input.substr(begin);
+    }
+
+    return input.substr(begin, end - begin + 1);
+}
+
 bool HexStringToUInt(const std::string &input, unsigned int *uintOut)
 {
     unsigned int offset = 0;
 
     if (input.size() >= 2 && input[0] == '0' && input[1] == 'x')
     {
         offset = 2u;
     }
--- a/gfx/angle/src/common/string_utils.h
+++ b/gfx/angle/src/common/string_utils.h
@@ -11,22 +11,39 @@
 #define LIBANGLE_STRING_UTILS_H_
 
 #include <string>
 #include <vector>
 
 namespace angle
 {
 
-void SplitString(const std::string &input,
-                 char delimiter,
-                 std::vector<std::string> *tokensOut);
+extern const char kWhitespaceASCII[];
+
+enum WhitespaceHandling
+{
+    KEEP_WHITESPACE,
+    TRIM_WHITESPACE,
+};
+
+enum SplitResult
+{
+    SPLIT_WANT_ALL,
+    SPLIT_WANT_NONEMPTY,
+};
+
+std::vector<std::string> SplitString(const std::string &input,
+                                     const std::string &delimiters,
+                                     WhitespaceHandling whitespace,
+                                     SplitResult resultType);
 
 void SplitStringAlongWhitespace(const std::string &input,
                                 std::vector<std::string> *tokensOut);
 
+std::string TrimString(const std::string &input, const std::string &trimChars);
+
 bool HexStringToUInt(const std::string &input, unsigned int *uintOut);
 
 bool ReadFileToString(const std::string &path, std::string *stringOut);
 
 }
 
 #endif // LIBANGLE_STRING_UTILS_H_
--- a/gfx/angle/src/common/string_utils_unittest.cpp
+++ b/gfx/angle/src/common/string_utils_unittest.cpp
@@ -11,44 +11,111 @@
 
 #include <gtest/gtest.h>
 
 using namespace angle;
 
 namespace
 {
 
-// Basic functionality tests for SplitString
-TEST(StringUtilsTest, SplitStringBasic)
+// Basic SplitString tests
+TEST(StringUtilsTest, SplitString_Basics)
 {
-    std::string testString("AxBxCxxxDExxFGHx");
-    std::vector<std::string> tokens;
-    SplitString(testString, 'x', &tokens);
+    std::vector<std::string> r;
+
+    r = SplitString(std::string(), ",:;", KEEP_WHITESPACE, SPLIT_WANT_ALL);
+    EXPECT_TRUE(r.empty());
+
+    // Empty separator list
+    r = SplitString("hello, world", "", KEEP_WHITESPACE, SPLIT_WANT_ALL);
+    ASSERT_EQ(1u, r.size());
+    EXPECT_EQ("hello, world", r[0]);
+
+    // Should split on any of the separators.
+    r = SplitString("::,,;;", ",:;", KEEP_WHITESPACE, SPLIT_WANT_ALL);
+    ASSERT_EQ(7u, r.size());
+    for (auto str : r)
+        ASSERT_TRUE(str.empty());
 
-    ASSERT_EQ(5u, tokens.size());
-    EXPECT_EQ("A", tokens[0]);
-    EXPECT_EQ("B", tokens[1]);
-    EXPECT_EQ("C", tokens[2]);
-    EXPECT_EQ("DE", tokens[3]);
-    EXPECT_EQ("FGH", tokens[4]);
+    r = SplitString("red, green; blue:", ",:;", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+    ASSERT_EQ(3u, r.size());
+    EXPECT_EQ("red", r[0]);
+    EXPECT_EQ("green", r[1]);
+    EXPECT_EQ("blue", r[2]);
+
+    // Want to split a string along whitespace sequences.
+    r = SplitString("  red green   \tblue\n", " \t\n", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+    ASSERT_EQ(3u, r.size());
+    EXPECT_EQ("red", r[0]);
+    EXPECT_EQ("green", r[1]);
+    EXPECT_EQ("blue", r[2]);
+
+    // Weird case of splitting on spaces but not trimming.
+    r = SplitString(" red ", " ", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+    ASSERT_EQ(3u, r.size());
+    EXPECT_EQ("", r[0]);  // Before the first space.
+    EXPECT_EQ("red", r[1]);
+    EXPECT_EQ("", r[2]);  // After the last space.
 }
 
-// Basic functionality tests for SplitStringAlongWhitespace
-TEST(StringUtilsTest, SplitStringAlongWhitespaceBasic)
+// Check different whitespace and result types for SplitString
+TEST(StringUtilsTest, SplitString_WhitespaceAndResultType)
 {
-    std::string testString("A B\nC\r\tDE\v\fFGH \t\r\n");
-    std::vector<std::string> tokens;
-    SplitStringAlongWhitespace(testString, &tokens);
+    std::vector<std::string> r;
+
+    // Empty input handling.
+    r = SplitString(std::string(), ",", KEEP_WHITESPACE, SPLIT_WANT_ALL);
+    EXPECT_TRUE(r.empty());
+    r = SplitString(std::string(), ",", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
+    EXPECT_TRUE(r.empty());
+
+    // Input string is space and we're trimming.
+    r = SplitString(" ", ",", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+    ASSERT_EQ(1u, r.size());
+    EXPECT_EQ("", r[0]);
+    r = SplitString(" ", ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+    EXPECT_TRUE(r.empty());
 
-    ASSERT_EQ(5u, tokens.size());
-    EXPECT_EQ("A", tokens[0]);
-    EXPECT_EQ("B", tokens[1]);
-    EXPECT_EQ("C", tokens[2]);
-    EXPECT_EQ("DE", tokens[3]);
-    EXPECT_EQ("FGH", tokens[4]);
+    // Test all 4 combinations of flags on ", ,".
+    r = SplitString(", ,", ",", KEEP_WHITESPACE, SPLIT_WANT_ALL);
+    ASSERT_EQ(3u, r.size());
+    EXPECT_EQ("", r[0]);
+    EXPECT_EQ(" ", r[1]);
+    EXPECT_EQ("", r[2]);
+    r = SplitString(", ,", ",", KEEP_WHITESPACE, SPLIT_WANT_NONEMPTY);
+    ASSERT_EQ(1u, r.size());
+    ASSERT_EQ(" ", r[0]);
+    r = SplitString(", ,", ",", TRIM_WHITESPACE, SPLIT_WANT_ALL);
+    ASSERT_EQ(3u, r.size());
+    EXPECT_EQ("", r[0]);
+    EXPECT_EQ("", r[1]);
+    EXPECT_EQ("", r[2]);
+    r = SplitString(", ,", ",", TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
+    ASSERT_TRUE(r.empty());
+}
+
+// Tests for TrimString
+TEST(StringUtilsTest, TrimString)
+{
+    // Basic tests
+    EXPECT_EQ("a", TrimString("a", kWhitespaceASCII));
+    EXPECT_EQ("a", TrimString(" a", kWhitespaceASCII));
+    EXPECT_EQ("a", TrimString("a ", kWhitespaceASCII));
+    EXPECT_EQ("a", TrimString(" a ", kWhitespaceASCII));
+
+    // Tests with empty strings
+    EXPECT_EQ("", TrimString("", kWhitespaceASCII));
+    EXPECT_EQ("", TrimString(" \n\r\t", kWhitespaceASCII));
+    EXPECT_EQ(" foo ", TrimString(" foo ", ""));
+
+    // Tests it doesn't removes characters in the middle
+    EXPECT_EQ("foo bar", TrimString(" foo bar ", kWhitespaceASCII));
+
+    // Test with non-whitespace trimChars
+    EXPECT_EQ(" ", TrimString("foo bar", "abcdefghijklmnopqrstuvwxyz"));
 }
 
 // Basic functionality tests for HexStringToUInt
 TEST(StringUtilsTest, HexStringToUIntBasic)
 {
     unsigned int uintValue;
 
     std::string emptyString;
--- a/gfx/angle/src/compiler/translator/ASTMetadataHLSL.cpp
+++ b/gfx/angle/src/compiler/translator/ASTMetadataHLSL.cpp
@@ -61,23 +61,23 @@ class PullGradient : public TIntermTrave
             // A control flow's using a gradient means its parents are too.
             if (mMetadata->mControlFlowsContainingGradient.count(node)> 0 && !mParents.empty())
             {
                 mMetadata->mControlFlowsContainingGradient.insert(mParents.back());
             }
         }
     }
 
-    bool visitLoop(Visit visit, TIntermLoop *loop)
+    bool visitLoop(Visit visit, TIntermLoop *loop) override
     {
         visitControlFlow(visit, loop);
         return true;
     }
 
-    bool visitSelection(Visit visit, TIntermSelection *selection)
+    bool visitSelection(Visit visit, TIntermSelection *selection) override
     {
         visitControlFlow(visit, selection);
         return true;
     }
 
     bool visitUnary(Visit visit, TIntermUnary *node) override
     {
         if (visit == PreVisit)
@@ -328,17 +328,17 @@ class PushDiscontinuousLoops : public TI
     }
 
     void traverse(TIntermAggregate *node)
     {
         node->traverse(this);
         ASSERT(mNestedDiscont == (mMetadata->mCalledInDiscontinuousLoop ? 1 : 0));
     }
 
-    bool visitLoop(Visit visit, TIntermLoop *loop)
+    bool visitLoop(Visit visit, TIntermLoop *loop) override
     {
         bool isDiscontinuous = mMetadata->mDiscontinuousLoops.count(loop) > 0;
 
         if (visit == PreVisit && isDiscontinuous)
         {
             mNestedDiscont++;
         }
         else if (visit == PostVisit && isDiscontinuous)
--- a/gfx/angle/src/compiler/translator/Compiler.cpp
+++ b/gfx/angle/src/compiler/translator/Compiler.cpp
@@ -289,22 +289,24 @@ TIntermNode *TCompiler::compileTreeImpl(
             success = enforceTimingRestrictions(root, (compileOptions & SH_DEPENDENCY_GRAPH) != 0);
 
         if (success && shaderSpec == SH_CSS_SHADERS_SPEC)
             rewriteCSSShader(root);
 
         // Unroll for-loop markup needs to happen after validateLimitations pass.
         if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX))
         {
-            ForLoopUnrollMarker marker(ForLoopUnrollMarker::kIntegerIndex);
+            ForLoopUnrollMarker marker(ForLoopUnrollMarker::kIntegerIndex,
+                                       shouldRunLoopAndIndexingValidation(compileOptions));
             root->traverse(&marker);
         }
         if (success && (compileOptions & SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX))
         {
-            ForLoopUnrollMarker marker(ForLoopUnrollMarker::kSamplerArrayIndex);
+            ForLoopUnrollMarker marker(ForLoopUnrollMarker::kSamplerArrayIndex,
+                                       shouldRunLoopAndIndexingValidation(compileOptions));
             root->traverse(&marker);
             if (marker.samplerArrayIndexIsFloatLoopIndex())
             {
                 infoSink.info.prefix(EPrefixError);
                 infoSink.info << "sampler array index is float loop index";
                 success = false;
             }
         }
@@ -418,49 +420,56 @@ bool TCompiler::InitBuiltInSymbolTable(c
     integer.array = false;
 
     TPublicType floatingPoint;
     floatingPoint.type = EbtFloat;
     floatingPoint.primarySize = 1;
     floatingPoint.secondarySize = 1;
     floatingPoint.array = false;
 
-    TPublicType sampler;
-    sampler.primarySize = 1;
-    sampler.secondarySize = 1;
-    sampler.array = false;
-
     switch(shaderType)
     {
       case GL_FRAGMENT_SHADER:
         symbolTable.setDefaultPrecision(integer, EbpMedium);
         break;
       case GL_VERTEX_SHADER:
         symbolTable.setDefaultPrecision(integer, EbpHigh);
         symbolTable.setDefaultPrecision(floatingPoint, EbpHigh);
         break;
       default:
         assert(false && "Language not supported");
     }
-    // We set defaults for all the sampler types, even those that are
+    // Set defaults for sampler types that have default precision, even those that are
     // only available if an extension exists.
-    for (int samplerType = EbtGuardSamplerBegin + 1;
-         samplerType < EbtGuardSamplerEnd; ++samplerType)
-    {
-        sampler.type = static_cast<TBasicType>(samplerType);
-        symbolTable.setDefaultPrecision(sampler, EbpLow);
-    }
+    // New sampler types in ESSL3 don't have default precision. ESSL1 types do.
+    initSamplerDefaultPrecision(EbtSampler2D);
+    initSamplerDefaultPrecision(EbtSamplerCube);
+    // SamplerExternalOES is specified in the extension to have default precision.
+    initSamplerDefaultPrecision(EbtSamplerExternalOES);
+    // It isn't specified whether Sampler2DRect has default precision.
+    initSamplerDefaultPrecision(EbtSampler2DRect);
 
     InsertBuiltInFunctions(shaderType, shaderSpec, resources, symbolTable);
 
     IdentifyBuiltIns(shaderType, shaderSpec, resources, symbolTable);
 
     return true;
 }
 
+void TCompiler::initSamplerDefaultPrecision(TBasicType samplerType)
+{
+    ASSERT(samplerType > EbtGuardSamplerBegin && samplerType < EbtGuardSamplerEnd);
+    TPublicType sampler;
+    sampler.primarySize   = 1;
+    sampler.secondarySize = 1;
+    sampler.array         = false;
+    sampler.type          = samplerType;
+    symbolTable.setDefaultPrecision(sampler, EbpLow);
+}
+
 void TCompiler::setResourceString()
 {
     std::ostringstream strstream;
     strstream << ":MaxVertexAttribs:" << compileResources.MaxVertexAttribs
               << ":MaxVertexUniformVectors:" << compileResources.MaxVertexUniformVectors
               << ":MaxVaryingVectors:" << compileResources.MaxVaryingVectors
               << ":MaxVertexTextureImageUnits:" << compileResources.MaxVertexTextureImageUnits
               << ":MaxCombinedTextureImageUnits:" << compileResources.MaxCombinedTextureImageUnits
@@ -684,17 +693,17 @@ bool TCompiler::validateOutputs(TIntermN
 void TCompiler::rewriteCSSShader(TIntermNode* root)
 {
     RenameFunction renamer("main(", "css_main(");
     root->traverse(&renamer);
 }
 
 bool TCompiler::validateLimitations(TIntermNode* root)
 {
-    ValidateLimitations validate(shaderType, infoSink.info);
+    ValidateLimitations validate(shaderType, &infoSink.info);
     root->traverse(&validate);
     return validate.numErrors() == 0;
 }
 
 bool TCompiler::enforceTimingRestrictions(TIntermNode* root, bool outputGraph)
 {
     if (shaderSpec != SH_WEBGL_SPEC)
     {
--- a/gfx/angle/src/compiler/translator/Compiler.h
+++ b/gfx/angle/src/compiler/translator/Compiler.h
@@ -174,16 +174,18 @@ class TCompiler : public TShHandleBase
 
   private:
     // Creates the function call DAG for further analysis, returning false if there is a recursion
     bool initCallDag(TIntermNode *root);
     // Return false if "main" doesn't exist
     bool tagUsedFunctions();
     void internalTagUsedFunction(size_t index);
 
+    void initSamplerDefaultPrecision(TBasicType samplerType);
+
     // Removes unused function declarations and prototypes from the AST
     class UnusedPredicate;
     bool pruneUnusedFunctions(TIntermNode *root);
 
     TIntermNode *compileTreeImpl(const char *const shaderStrings[],
                                  size_t numStrings,
                                  const int compileOptions);
 
--- a/gfx/angle/src/compiler/translator/ForLoopUnroll.cpp
+++ b/gfx/angle/src/compiler/translator/ForLoopUnroll.cpp
@@ -1,16 +1,19 @@
 //
 // Copyright (c) 2002-2013 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.
 //
 
 #include "compiler/translator/ForLoopUnroll.h"
 
+#include "compiler/translator/ValidateLimitations.h"
+#include "angle_gl.h"
+
 bool ForLoopUnrollMarker::visitBinary(Visit, TIntermBinary *node)
 {
     if (mUnrollCondition != kSamplerArrayIndex)
         return true;
 
     // If a sampler array index is also the loop index,
     //   1) if the index type is integer, mark the loop for unrolling;
     //   2) if the index type if float, set a flag to later fail compile.
@@ -33,33 +36,45 @@ bool ForLoopUnrollMarker::visitBinary(Vi
       default:
         break;
     }
     return true;
 }
 
 bool ForLoopUnrollMarker::visitLoop(Visit, TIntermLoop *node)
 {
-    if (mUnrollCondition == kIntegerIndex)
+    bool canBeUnrolled = mHasRunLoopValidation;
+    if (!mHasRunLoopValidation)
+    {
+        canBeUnrolled = ValidateLimitations::IsLimitedForLoop(node);
+    }
+    if (mUnrollCondition == kIntegerIndex && canBeUnrolled)
     {
         // Check if loop index type is integer.
-        // This is called after ValidateLimitations pass, so all the calls
-        // should be valid. See ValidateLimitations::validateForLoopInit().
+        // This is called after ValidateLimitations pass, so the loop has the limited form specified
+        // in ESSL 1.00 appendix A.
         TIntermSequence *declSeq = node->getInit()->getAsAggregate()->getSequence();
         TIntermSymbol *symbol = (*declSeq)[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode();
         if (symbol->getBasicType() == EbtInt)
             node->setUnrollFlag(true);
     }
 
     TIntermNode *body = node->getBody();
-    if (body != NULL)
+    if (body != nullptr)
     {
-        mLoopStack.push(node);
-        body->traverse(this);
-        mLoopStack.pop();
+        if (canBeUnrolled)
+        {
+            mLoopStack.push(node);
+            body->traverse(this);
+            mLoopStack.pop();
+        }
+        else
+        {
+            body->traverse(this);
+        }
     }
     // The loop is fully processed - no need to visit children.
     return false;
 }
 
 void ForLoopUnrollMarker::visitSymbol(TIntermSymbol* symbol)
 {
     if (!mVisitSamplerArrayIndexNodeInsideLoop)
--- a/gfx/angle/src/compiler/translator/ForLoopUnroll.h
+++ b/gfx/angle/src/compiler/translator/ForLoopUnroll.h
@@ -19,21 +19,22 @@ class ForLoopUnrollMarker : public TInte
 {
   public:
     enum UnrollCondition
     {
         kIntegerIndex,
         kSamplerArrayIndex
     };
 
-    ForLoopUnrollMarker(UnrollCondition condition)
+    ForLoopUnrollMarker(UnrollCondition condition, bool hasRunLoopValidation)
         : TIntermTraverser(true, false, false),
           mUnrollCondition(condition),
           mSamplerArrayIndexIsFloatLoopIndex(false),
-          mVisitSamplerArrayIndexNodeInsideLoop(false)
+          mVisitSamplerArrayIndexNodeInsideLoop(false),
+          mHasRunLoopValidation(hasRunLoopValidation)
     {
     }
 
     bool visitBinary(Visit, TIntermBinary *node) override;
     bool visitLoop(Visit, TIntermLoop *node) override;
     void visitSymbol(TIntermSymbol *node) override;
 
     bool samplerArrayIndexIsFloatLoopIndex() const
@@ -41,11 +42,12 @@ class ForLoopUnrollMarker : public TInte
         return mSamplerArrayIndexIsFloatLoopIndex;
     }
 
   private:
     UnrollCondition mUnrollCondition;
     TLoopStack mLoopStack;
     bool mSamplerArrayIndexIsFloatLoopIndex;
     bool mVisitSamplerArrayIndexNodeInsideLoop;
+    bool mHasRunLoopValidation;
 };
 
 #endif // COMPILER_TRANSLATOR_FORLOOPUNROLL_H_
--- a/gfx/angle/src/compiler/translator/OutputHLSL.cpp
+++ b/gfx/angle/src/compiler/translator/OutputHLSL.cpp
@@ -234,17 +234,17 @@ void OutputHLSL::output(TIntermNode *tre
     mInfoSinkStack.push(&mFooter);
     if (!mDeferredGlobalInitializers.empty())
     {
         writeDeferredGlobalInitializers(mFooter);
     }
     mInfoSinkStack.pop();
 
     mInfoSinkStack.push(&mHeader);
-    header(&builtInFunctionEmulator);
+    header(mHeader, &builtInFunctionEmulator);
     mInfoSinkStack.pop();
 
     objSink << mHeader.c_str();
     objSink << mBody.c_str();
     objSink << mFooter.c_str();
 
     builtInFunctionEmulator.Cleanup();
 }
@@ -329,20 +329,18 @@ TString OutputHLSL::structInitializerStr
         }
     }
 
     init += preIndentString + "}" + (indent == 0 ? ";" : ",") + "\n";
 
     return init;
 }
 
-void OutputHLSL::header(const BuiltInFunctionEmulator *builtInFunctionEmulator)
+void OutputHLSL::header(TInfoSinkBase &out, const BuiltInFunctionEmulator *builtInFunctionEmulator)
 {
-    TInfoSinkBase &out = getInfoSink();
-
     TString varyings;
     TString attributes;
     TString flaggedStructs;
 
     for (std::map<TIntermTyped*, TString>::const_iterator flaggedStructIt = mFlaggedStructMappedNames.begin(); flaggedStructIt != mFlaggedStructMappedNames.end(); flaggedStructIt++)
     {
         TIntermTyped *structNode = flaggedStructIt->first;
         const TString &mappedName = flaggedStructIt->second;
@@ -899,16 +897,27 @@ void OutputHLSL::header(const BuiltInFun
                 out << "    int face = (int)negative + (int)yMajor * 2 + (int)zMajor * 4;\n";
 
                 out << "    float u = xMajor ? -t.z : (yMajor && t.y < 0.0f ? -t.x : t.x);\n";
                 out << "    float v = yMajor ? t.z : (negative ? t.y : -t.y);\n";
                 out << "    float m = xMajor ? t.x : (yMajor ? t.y : t.z);\n";
 
                 out << "    t.x = (u * 0.5f / m) + 0.5f;\n";
                 out << "    t.y = (v * 0.5f / m) + 0.5f;\n";
+
+                // Mip level computation.
+                if (textureFunction->method == TextureFunction::IMPLICIT)
+                {
+                    out << "    float2 tSized = float2(t.x * width, t.y * height);\n"
+                           "    float2 dx = ddx(tSized);\n"
+                           "    float2 dy = ddy(tSized);\n"
+                           "    float lod = 0.5f * log2(max(dot(dx, dx), dot(dy, dy)));\n"
+                           "    mip = uint(min(max(round(lod), 0), levels - 1));\n"
+                           "    x.GetDimensions(mip, width, height, layers, levels);\n";
+                }
             }
             else if (IsIntegerSampler(textureFunction->sampler) &&
                      textureFunction->method != TextureFunction::FETCH)
             {
                 if (IsSampler2D(textureFunction->sampler))
                 {
                     if (IsSamplerArray(textureFunction->sampler))
                     {
@@ -1440,45 +1449,45 @@ void OutputHLSL::visitRaw(TIntermRaw *no
 }
 
 void OutputHLSL::outputEqual(Visit visit, const TType &type, TOperator op, TInfoSinkBase &out)
 {
     if (type.isScalar() && !type.isArray())
     {
         if (op == EOpEqual)
         {
-            outputTriplet(visit, "(", " == ", ")", out);
+            outputTriplet(out, visit, "(", " == ", ")");
         }
         else
         {
-            outputTriplet(visit, "(", " != ", ")", out);
+            outputTriplet(out, visit, "(", " != ", ")");
         }
     }
     else
     {
         if (visit == PreVisit && op == EOpNotEqual)
         {
             out << "!";
         }
 
         if (type.isArray())
         {
             const TString &functionName = addArrayEqualityFunction(type);
-            outputTriplet(visit, (functionName + "(").c_str(), ", ", ")", out);
+            outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")");
         }
         else if (type.getBasicType() == EbtStruct)
         {
             const TStructure &structure = *type.getStruct();
             const TString &functionName = addStructEqualityFunction(structure);
-            outputTriplet(visit, (functionName + "(").c_str(), ", ", ")", out);
+            outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")");
         }
         else
         {
             ASSERT(type.isMatrix() || type.isVector());
-            outputTriplet(visit, "all(", " == ", ")", out);
+            outputTriplet(out, visit, "all(", " == ", ")");
         }
     }
 }
 
 bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node)
 {
     TInfoSinkBase &out = getInfoSink();
 
@@ -1508,21 +1517,21 @@ bool OutputHLSL::visitBinary(Visit visit
                 }
                 out << ")";
                 return false;
             }
             // ArrayReturnValueToOutParameter should have eliminated expressions where a function call is assigned.
             ASSERT(rightAgg == nullptr || rightAgg->getOp() != EOpFunctionCall);
 
             const TString &functionName = addArrayAssignmentFunction(node->getType());
-            outputTriplet(visit, (functionName + "(").c_str(), ", ", ")");
+            outputTriplet(out, visit, (functionName + "(").c_str(), ", ", ")");
         }
         else
         {
-            outputTriplet(visit, "(", " = ", ")");
+            outputTriplet(out, visit, "(", " = ", ")");
         }
         break;
       case EOpInitialize:
         if (visit == PreVisit)
         {
             // GLSL allows to write things like "float x = x;" where a new variable x is defined
             // and the value of an existing variable x is assigned. HLSL uses C semantics (the
             // new variable is created before the assignment is evaluated), so we need to convert
@@ -1555,21 +1564,31 @@ bool OutputHLSL::visitBinary(Visit visit
                 return false;
             }
         }
         else if (visit == InVisit)
         {
             out << " = ";
         }
         break;
-      case EOpAddAssign:               outputTriplet(visit, "(", " += ", ")");          break;
-      case EOpSubAssign:               outputTriplet(visit, "(", " -= ", ")");          break;
-      case EOpMulAssign:               outputTriplet(visit, "(", " *= ", ")");          break;
-      case EOpVectorTimesScalarAssign: outputTriplet(visit, "(", " *= ", ")");          break;
-      case EOpMatrixTimesScalarAssign: outputTriplet(visit, "(", " *= ", ")");          break;
+      case EOpAddAssign:
+          outputTriplet(out, visit, "(", " += ", ")");
+          break;
+      case EOpSubAssign:
+          outputTriplet(out, visit, "(", " -= ", ")");
+          break;
+      case EOpMulAssign:
+          outputTriplet(out, visit, "(", " *= ", ")");
+          break;
+      case EOpVectorTimesScalarAssign:
+          outputTriplet(out, visit, "(", " *= ", ")");
+          break;
+      case EOpMatrixTimesScalarAssign:
+          outputTriplet(out, visit, "(", " *= ", ")");
+          break;
       case EOpVectorTimesMatrixAssign:
         if (visit == PreVisit)
         {
             out << "(";
         }
         else if (visit == InVisit)
         {
             out << " = mul(";
@@ -1592,47 +1611,61 @@ bool OutputHLSL::visitBinary(Visit visit
             node->getLeft()->traverse(this);
             out << "), transpose(";
         }
         else
         {
             out << "))))";
         }
         break;
-      case EOpDivAssign:               outputTriplet(visit, "(", " /= ", ")");          break;
-      case EOpIModAssign:              outputTriplet(visit, "(", " %= ", ")");          break;
-      case EOpBitShiftLeftAssign:      outputTriplet(visit, "(", " <<= ", ")");         break;
-      case EOpBitShiftRightAssign:     outputTriplet(visit, "(", " >>= ", ")");         break;
-      case EOpBitwiseAndAssign:        outputTriplet(visit, "(", " &= ", ")");          break;
-      case EOpBitwiseXorAssign:        outputTriplet(visit, "(", " ^= ", ")");          break;
-      case EOpBitwiseOrAssign:         outputTriplet(visit, "(", " |= ", ")");          break;
+      case EOpDivAssign:
+          outputTriplet(out, visit, "(", " /= ", ")");
+          break;
+      case EOpIModAssign:
+          outputTriplet(out, visit, "(", " %= ", ")");
+          break;
+      case EOpBitShiftLeftAssign:
+          outputTriplet(out, visit, "(", " <<= ", ")");
+          break;
+      case EOpBitShiftRightAssign:
+          outputTriplet(out, visit, "(", " >>= ", ")");
+          break;
+      case EOpBitwiseAndAssign:
+          outputTriplet(out, visit, "(", " &= ", ")");
+          break;
+      case EOpBitwiseXorAssign:
+          outputTriplet(out, visit, "(", " ^= ", ")");
+          break;
+      case EOpBitwiseOrAssign:
+          outputTriplet(out, visit, "(", " |= ", ")");
+          break;
       case EOpIndexDirect:
         {
             const TType& leftType = node->getLeft()->getType();
             if (leftType.isInterfaceBlock())
             {
                 if (visit == PreVisit)
                 {
                     TInterfaceBlock* interfaceBlock = leftType.getInterfaceBlock();
                     const int arrayIndex = node->getRight()->getAsConstantUnion()->getIConst(0);
                     mReferencedInterfaceBlocks[interfaceBlock->instanceName()] = node->getLeft()->getAsSymbolNode();
                     out << mUniformHLSL->interfaceBlockInstanceString(*interfaceBlock, arrayIndex);
                     return false;
                 }
             }
             else
             {
-                outputTriplet(visit, "", "[", "]");
+                outputTriplet(out, visit, "", "[", "]");
             }
         }
         break;
       case EOpIndexIndirect:
         // We do not currently support indirect references to interface blocks
         ASSERT(node->getLeft()->getBasicType() != EbtInterfaceBlock);
-        outputTriplet(visit, "", "[", "]");
+        outputTriplet(out, visit, "", "[", "]");
         break;
       case EOpIndexDirectStruct:
         if (visit == InVisit)
         {
             const TStructure* structure = node->getLeft()->getType().getStruct();
             const TIntermConstantUnion* index = node->getRight()->getAsConstantUnion();
             const TField* field = structure->fields()[index->getIConst(0)];
             out << "." + DecorateField(field->name(), *structure);
@@ -1682,226 +1715,354 @@ bool OutputHLSL::visitBinary(Visit visit
                     else UNREACHABLE();
                 }
             }
             else UNREACHABLE();
 
             return false;   // Fully processed
         }
         break;
-      case EOpAdd:               outputTriplet(visit, "(", " + ", ")"); break;
-      case EOpSub:               outputTriplet(visit, "(", " - ", ")"); break;
-      case EOpMul:               outputTriplet(visit, "(", " * ", ")"); break;
-      case EOpDiv:               outputTriplet(visit, "(", " / ", ")"); break;
-      case EOpIMod:              outputTriplet(visit, "(", " % ", ")"); break;
-      case EOpBitShiftLeft:      outputTriplet(visit, "(", " << ", ")"); break;
-      case EOpBitShiftRight:     outputTriplet(visit, "(", " >> ", ")"); break;
-      case EOpBitwiseAnd:        outputTriplet(visit, "(", " & ", ")"); break;
-      case EOpBitwiseXor:        outputTriplet(visit, "(", " ^ ", ")"); break;
-      case EOpBitwiseOr:         outputTriplet(visit, "(", " | ", ")"); break;
+      case EOpAdd:
+          outputTriplet(out, visit, "(", " + ", ")");
+          break;
+      case EOpSub:
+          outputTriplet(out, visit, "(", " - ", ")");
+          break;
+      case EOpMul:
+          outputTriplet(out, visit, "(", " * ", ")");
+          break;
+      case EOpDiv:
+          outputTriplet(out, visit, "(", " / ", ")");
+          break;
+      case EOpIMod:
+          outputTriplet(out, visit, "(", " % ", ")");
+          break;
+      case EOpBitShiftLeft:
+          outputTriplet(out, visit, "(", " << ", ")");
+          break;
+      case EOpBitShiftRight:
+          outputTriplet(out, visit, "(", " >> ", ")");
+          break;
+      case EOpBitwiseAnd:
+          outputTriplet(out, visit, "(", " & ", ")");
+          break;
+      case EOpBitwiseXor:
+          outputTriplet(out, visit, "(", " ^ ", ")");
+          break;
+      case EOpBitwiseOr:
+          outputTriplet(out, visit, "(", " | ", ")");
+          break;
       case EOpEqual:
       case EOpNotEqual:
         outputEqual(visit, node->getLeft()->getType(), node->getOp(), out);
         break;
-      case EOpLessThan:          outputTriplet(visit, "(", " < ", ")");   break;
-      case EOpGreaterThan:       outputTriplet(visit, "(", " > ", ")");   break;
-      case EOpLessThanEqual:     outputTriplet(visit, "(", " <= ", ")");  break;
-      case EOpGreaterThanEqual:  outputTriplet(visit, "(", " >= ", ")");  break;
-      case EOpVectorTimesScalar: outputTriplet(visit, "(", " * ", ")");   break;
-      case EOpMatrixTimesScalar: outputTriplet(visit, "(", " * ", ")");   break;
-      case EOpVectorTimesMatrix: outputTriplet(visit, "mul(", ", transpose(", "))"); break;
-      case EOpMatrixTimesVector: outputTriplet(visit, "mul(transpose(", "), ", ")"); break;
-      case EOpMatrixTimesMatrix: outputTriplet(visit, "transpose(mul(transpose(", "), transpose(", ")))"); break;
+      case EOpLessThan:
+          outputTriplet(out, visit, "(", " < ", ")");
+          break;
+      case EOpGreaterThan:
+          outputTriplet(out, visit, "(", " > ", ")");
+          break;
+      case EOpLessThanEqual:
+          outputTriplet(out, visit, "(", " <= ", ")");
+          break;
+      case EOpGreaterThanEqual:
+          outputTriplet(out, visit, "(", " >= ", ")");
+          break;
+      case EOpVectorTimesScalar:
+          outputTriplet(out, visit, "(", " * ", ")");
+          break;
+      case EOpMatrixTimesScalar:
+          outputTriplet(out, visit, "(", " * ", ")");
+          break;
+      case EOpVectorTimesMatrix:
+          outputTriplet(out, visit, "mul(", ", transpose(", "))");
+          break;
+      case EOpMatrixTimesVector:
+          outputTriplet(out, visit, "mul(transpose(", "), ", ")");
+          break;
+      case EOpMatrixTimesMatrix:
+          outputTriplet(out, visit, "transpose(mul(transpose(", "), transpose(", ")))");
+          break;
       case EOpLogicalOr:
         // HLSL doesn't short-circuit ||, so we assume that || affected by short-circuiting have been unfolded.
         ASSERT(!node->getRight()->hasSideEffects());
-        outputTriplet(visit, "(", " || ", ")");
+        outputTriplet(out, visit, "(", " || ", ")");
         return true;
       case EOpLogicalXor:
         mUsesXor = true;
-        outputTriplet(visit, "xor(", ", ", ")");
+        outputTriplet(out, visit, "xor(", ", ", ")");
         break;
       case EOpLogicalAnd:
         // HLSL doesn't short-circuit &&, so we assume that && affected by short-circuiting have been unfolded.
         ASSERT(!node->getRight()->hasSideEffects());
-        outputTriplet(visit, "(", " && ", ")");
+        outputTriplet(out, visit, "(", " && ", ")");
         return true;
       default: UNREACHABLE();
     }
 
     return true;
 }
 
 bool OutputHLSL::visitUnary(Visit visit, TIntermUnary *node)
 {
+    TInfoSinkBase &out = getInfoSink();
+
     switch (node->getOp())
     {
-      case EOpNegative:         outputTriplet(visit, "(-", "", ")");         break;
-      case EOpPositive:         outputTriplet(visit, "(+", "", ")");         break;
-      case EOpVectorLogicalNot: outputTriplet(visit, "(!", "", ")");         break;
-      case EOpLogicalNot:       outputTriplet(visit, "(!", "", ")");         break;
-      case EOpBitwiseNot:       outputTriplet(visit, "(~", "", ")");         break;
-      case EOpPostIncrement:    outputTriplet(visit, "(", "", "++)");        break;
-      case EOpPostDecrement:    outputTriplet(visit, "(", "", "--)");        break;
-      case EOpPreIncrement:     outputTriplet(visit, "(++", "", ")");        break;
-      case EOpPreDecrement:     outputTriplet(visit, "(--", "", ")");        break;
-      case EOpRadians:          outputTriplet(visit, "radians(", "", ")");   break;
-      case EOpDegrees:          outputTriplet(visit, "degrees(", "", ")");   break;
-      case EOpSin:              outputTriplet(visit, "sin(", "", ")");       break;
-      case EOpCos:              outputTriplet(visit, "cos(", "", ")");       break;
-      case EOpTan:              outputTriplet(visit, "tan(", "", ")");       break;
-      case EOpAsin:             outputTriplet(visit, "asin(", "", ")");      break;
-      case EOpAcos:             outputTriplet(visit, "acos(", "", ")");      break;
-      case EOpAtan:             outputTriplet(visit, "atan(", "", ")");      break;
-      case EOpSinh:             outputTriplet(visit, "sinh(", "", ")");      break;
-      case EOpCosh:             outputTriplet(visit, "cosh(", "", ")");      break;
-      case EOpTanh:             outputTriplet(visit, "tanh(", "", ")");      break;
+        case EOpNegative:
+            outputTriplet(out, visit, "(-", "", ")");
+            break;
+        case EOpPositive:
+            outputTriplet(out, visit, "(+", "", ")");
+            break;
+        case EOpVectorLogicalNot:
+            outputTriplet(out, visit, "(!", "", ")");
+            break;
+        case EOpLogicalNot:
+            outputTriplet(out, visit, "(!", "", ")");
+            break;
+        case EOpBitwiseNot:
+            outputTriplet(out, visit, "(~", "", ")");
+            break;
+        case EOpPostIncrement:
+            outputTriplet(out, visit, "(", "", "++)");
+            break;
+        case EOpPostDecrement:
+            outputTriplet(out, visit, "(", "", "--)");
+            break;
+        case EOpPreIncrement:
+            outputTriplet(out, visit, "(++", "", ")");
+            break;
+        case EOpPreDecrement:
+            outputTriplet(out, visit, "(--", "", ")");
+            break;
+        case EOpRadians:
+            outputTriplet(out, visit, "radians(", "", ")");
+            break;
+        case EOpDegrees:
+            outputTriplet(out, visit, "degrees(", "", ")");
+            break;
+        case EOpSin:
+            outputTriplet(out, visit, "sin(", "", ")");
+            break;
+        case EOpCos:
+            outputTriplet(out, visit, "cos(", "", ")");
+            break;
+        case EOpTan:
+            outputTriplet(out, visit, "tan(", "", ")");
+            break;
+        case EOpAsin:
+            outputTriplet(out, visit, "asin(", "", ")");
+            break;
+        case EOpAcos:
+            outputTriplet(out, visit, "acos(", "", ")");
+            break;
+        case EOpAtan:
+            outputTriplet(out, visit, "atan(", "", ")");
+            break;
+        case EOpSinh:
+            outputTriplet(out, visit, "sinh(", "", ")");
+            break;
+        case EOpCosh:
+            outputTriplet(out, visit, "cosh(", "", ")");
+            break;
+        case EOpTanh:
+            outputTriplet(out, visit, "tanh(", "", ")");
+            break;
       case EOpAsinh:
         ASSERT(node->getUseEmulatedFunction());
-        writeEmulatedFunctionTriplet(visit, "asinh(");
+        writeEmulatedFunctionTriplet(out, visit, "asinh(");
         break;
       case EOpAcosh:
         ASSERT(node->getUseEmulatedFunction());
-        writeEmulatedFunctionTriplet(visit, "acosh(");
+        writeEmulatedFunctionTriplet(out, visit, "acosh(");
         break;
       case EOpAtanh:
         ASSERT(node->getUseEmulatedFunction());
-        writeEmulatedFunctionTriplet(visit, "atanh(");
+        writeEmulatedFunctionTriplet(out, visit, "atanh(");
         break;
-      case EOpExp:              outputTriplet(visit, "exp(", "", ")");       break;
-      case EOpLog:              outputTriplet(visit, "log(", "", ")");       break;
-      case EOpExp2:             outputTriplet(visit, "exp2(", "", ")");      break;
-      case EOpLog2:             outputTriplet(visit, "log2(", "", ")");      break;
-      case EOpSqrt:             outputTriplet(visit, "sqrt(", "", ")");      break;
-      case EOpInverseSqrt:      outputTriplet(visit, "rsqrt(", "", ")");     break;
-      case EOpAbs:              outputTriplet(visit, "abs(", "", ")");       break;
-      case EOpSign:             outputTriplet(visit, "sign(", "", ")");      break;
-      case EOpFloor:            outputTriplet(visit, "floor(", "", ")");     break;
-      case EOpTrunc:            outputTriplet(visit, "trunc(", "", ")");     break;
-      case EOpRound:            outputTriplet(visit, "round(", "", ")");     break;
+      case EOpExp:
+          outputTriplet(out, visit, "exp(", "", ")");
+          break;
+      case EOpLog:
+          outputTriplet(out, visit, "log(", "", ")");
+          break;
+      case EOpExp2:
+          outputTriplet(out, visit, "exp2(", "", ")");
+          break;
+      case EOpLog2:
+          outputTriplet(out, visit, "log2(", "", ")");
+          break;
+      case EOpSqrt:
+          outputTriplet(out, visit, "sqrt(", "", ")");
+          break;
+      case EOpInverseSqrt:
+          outputTriplet(out, visit, "rsqrt(", "", ")");
+          break;
+      case EOpAbs:
+          outputTriplet(out, visit, "abs(", "", ")");
+          break;
+      case EOpSign:
+          outputTriplet(out, visit, "sign(", "", ")");
+          break;
+      case EOpFloor:
+          outputTriplet(out, visit, "floor(", "", ")");
+          break;
+      case EOpTrunc:
+          outputTriplet(out, visit, "trunc(", "", ")");
+          break;
+      case EOpRound:
+          outputTriplet(out, visit, "round(", "", ")");
+          break;
       case EOpRoundEven:
         ASSERT(node->getUseEmulatedFunction());
-        writeEmulatedFunctionTriplet(visit, "roundEven(");
+        writeEmulatedFunctionTriplet(out, visit, "roundEven(");
         break;
-      case EOpCeil:             outputTriplet(visit, "ceil(", "", ")");      break;
-      case EOpFract:            outputTriplet(visit, "frac(", "", ")");      break;
+      case EOpCeil:
+          outputTriplet(out, visit, "ceil(", "", ")");
+          break;
+      case EOpFract:
+          outputTriplet(out, visit, "frac(", "", ")");
+          break;
       case EOpIsNan:
-        outputTriplet(visit, "isnan(", "", ")");
+          outputTriplet(out, visit, "isnan(", "", ")");
         mRequiresIEEEStrictCompiling = true;
         break;
-      case EOpIsInf:            outputTriplet(visit, "isinf(", "", ")");     break;
-      case EOpFloatBitsToInt:   outputTriplet(visit, "asint(", "", ")");     break;
-      case EOpFloatBitsToUint:  outputTriplet(visit, "asuint(", "", ")");    break;
-      case EOpIntBitsToFloat:   outputTriplet(visit, "asfloat(", "", ")");   break;
-      case EOpUintBitsToFloat:  outputTriplet(visit, "asfloat(", "", ")");   break;
+      case EOpIsInf:
+          outputTriplet(out, visit, "isinf(", "", ")");
+          break;
+      case EOpFloatBitsToInt:
+          outputTriplet(out, visit, "asint(", "", ")");
+          break;
+      case EOpFloatBitsToUint:
+          outputTriplet(out, visit, "asuint(", "", ")");
+          break;
+      case EOpIntBitsToFloat:
+          outputTriplet(out, visit, "asfloat(", "", ")");
+          break;
+      case EOpUintBitsToFloat:
+          outputTriplet(out, visit, "asfloat(", "", ")");
+          break;
       case EOpPackSnorm2x16:
         ASSERT(node->getUseEmulatedFunction());
-        writeEmulatedFunctionTriplet(visit, "packSnorm2x16(");
+        writeEmulatedFunctionTriplet(out, visit, "packSnorm2x16(");
         break;
       case EOpPackUnorm2x16:
         ASSERT(node->getUseEmulatedFunction());
-        writeEmulatedFunctionTriplet(visit, "packUnorm2x16(");
+        writeEmulatedFunctionTriplet(out, visit, "packUnorm2x16(");
         break;
       case EOpPackHalf2x16:
         ASSERT(node->getUseEmulatedFunction());
-        writeEmulatedFunctionTriplet(visit, "packHalf2x16(");
+        writeEmulatedFunctionTriplet(out, visit, "packHalf2x16(");
         break;
       case EOpUnpackSnorm2x16:
         ASSERT(node->getUseEmulatedFunction());
-        writeEmulatedFunctionTriplet(visit, "unpackSnorm2x16(");
+        writeEmulatedFunctionTriplet(out, visit, "unpackSnorm2x16(");
         break;
       case EOpUnpackUnorm2x16:
         ASSERT(node->getUseEmulatedFunction());
-        writeEmulatedFunctionTriplet(visit, "unpackUnorm2x16(");
+        writeEmulatedFunctionTriplet(out, visit, "unpackUnorm2x16(");
         break;
       case EOpUnpackHalf2x16:
         ASSERT(node->getUseEmulatedFunction());
-        writeEmulatedFunctionTriplet(visit, "unpackHalf2x16(");
+        writeEmulatedFunctionTriplet(out, visit, "unpackHalf2x16(");
         break;
-      case EOpLength:           outputTriplet(visit, "length(", "", ")");    break;
-      case EOpNormalize:        outputTriplet(visit, "normalize(", "", ")"); break;
+      case EOpLength:
+          outputTriplet(out, visit, "length(", "", ")");
+          break;
+      case EOpNormalize:
+          outputTriplet(out, visit, "normalize(", "", ")");
+          break;
       case EOpDFdx:
         if(mInsideDiscontinuousLoop || mOutputLod0Function)
         {
-            outputTriplet(visit, "(", "", ", 0.0)");
+            outputTriplet(out, visit, "(", "", ", 0.0)");
         }
         else
         {
-            outputTriplet(visit, "ddx(", "", ")");
+            outputTriplet(out, visit, "ddx(", "", ")");
         }
         break;
       case EOpDFdy:
         if(mInsideDiscontinuousLoop || mOutputLod0Function)
         {
-            outputTriplet(visit, "(", "", ", 0.0)");
+            outputTriplet(out, visit, "(", "", ", 0.0)");
         }
         else
         {
-           outputTriplet(visit, "ddy(", "", ")");
+            outputTriplet(out, visit, "ddy(", "", ")");
         }
         break;
       case EOpFwidth:
         if(mInsideDiscontinuousLoop || mOutputLod0Function)
         {
-            outputTriplet(visit, "(", "", ", 0.0)");
+            outputTriplet(out, visit, "(", "", ", 0.0)");
         }
         else
         {
-            outputTriplet(visit, "fwidth(", "", ")");
+            outputTriplet(out, visit, "fwidth(", "", ")");
         }
         break;
-      case EOpTranspose:        outputTriplet(visit, "transpose(", "", ")");   break;
-      case EOpDeterminant:      outputTriplet(visit, "determinant(transpose(", "", "))"); break;
+      case EOpTranspose:
+          outputTriplet(out, visit, "transpose(", "", ")");
+          break;
+      case EOpDeterminant:
+          outputTriplet(out, visit, "determinant(transpose(", "", "))");
+          break;
       case EOpInverse:
         ASSERT(node->getUseEmulatedFunction());
-        writeEmulatedFunctionTriplet(visit, "inverse(");
+        writeEmulatedFunctionTriplet(out, visit, "inverse(");
         break;
 
-      case EOpAny:              outputTriplet(visit, "any(", "", ")");       break;
-      case EOpAll:              outputTriplet(visit, "all(", "", ")");       break;
+      case EOpAny:
+          outputTriplet(out, visit, "any(", "", ")");
+          break;
+      case EOpAll:
+          outputTriplet(out, visit, "all(", "", ")");
+          break;
       default: UNREACHABLE();
     }
 
     return true;
 }
 
 bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node)
 {
     TInfoSinkBase &out = getInfoSink();
 
     switch (node->getOp())
     {
       case EOpSequence:
         {
             if (mInsideFunction)
             {
-                outputLineDirective(node->getLine().first_line);
+                outputLineDirective(out, node->getLine().first_line);
                 out << "{\n";
             }
 
             for (TIntermSequence::iterator sit = node->getSequence()->begin(); sit != node->getSequence()->end(); sit++)
             {
-                outputLineDirective((*sit)->getLine().first_line);
+                outputLineDirective(out, (*sit)->getLine().first_line);
 
                 (*sit)->traverse(this);
 
                 // Don't output ; after case labels, they're terminated by :
                 // This is needed especially since outputting a ; after a case statement would turn empty
                 // case statements into non-empty case statements, disallowing fall-through from them.
                 // Also no need to output ; after selection (if) statements or sequences. This is done just
                 // for code clarity.
                 TIntermSelection *asSelection = (*sit)->getAsSelectionNode();
                 ASSERT(asSelection == nullptr || !asSelection->usesTernaryOperator());
                 if ((*sit)->getAsCaseNode() == nullptr && asSelection == nullptr && !IsSequence(*sit))
                     out << ";\n";
             }
 
             if (mInsideFunction)
             {
-                outputLineDirective(node->getLine().last_line);
+                outputLineDirective(out, node->getLine().last_line);
                 out << "}\n";
             }
 
             return false;
         }
       case EOpDeclaration:
         if (visit == PreVisit)
         {
@@ -2012,17 +2173,19 @@ bool OutputHLSL::visitAggregate(Visit vi
                 mOutputLod0Function = true;
                 node->traverse(this);
                 mOutputLod0Function = false;
             }
 
             return false;
         }
         break;
-      case EOpComma:            outputTriplet(visit, "(", ", ", ")");                break;
+      case EOpComma:
+          outputTriplet(out, visit, "(", ", ", ")");
+          break;
       case EOpFunction:
         {
             ASSERT(mCurrentFunctionMetadata == nullptr);
             TString name = TFunction::unmangleName(node->getNameObj().getString());
 
             size_t index = mCallDag.findIndex(node);
             ASSERT(index != CallDAG::InvalidIndex);
             mCurrentFunctionMetadata = &mASTMetadataList[index];
@@ -2245,122 +2408,210 @@ bool OutputHLSL::visitAggregate(Visit vi
                 }
             }
 
             out << ")";
 
             return false;
         }
         break;
-      case EOpParameters:       outputTriplet(visit, "(", ", ", ")\n{\n");                                break;
-      case EOpConstructFloat:   outputConstructor(visit, node->getType(), "vec1", node->getSequence());  break;
-      case EOpConstructVec2:    outputConstructor(visit, node->getType(), "vec2", node->getSequence());  break;
-      case EOpConstructVec3:    outputConstructor(visit, node->getType(), "vec3", node->getSequence());  break;
-      case EOpConstructVec4:    outputConstructor(visit, node->getType(), "vec4", node->getSequence());  break;
-      case EOpConstructBool:    outputConstructor(visit, node->getType(), "bvec1", node->getSequence()); break;
-      case EOpConstructBVec2:   outputConstructor(visit, node->getType(), "bvec2", node->getSequence()); break;
-      case EOpConstructBVec3:   outputConstructor(visit, node->getType(), "bvec3", node->getSequence()); break;
-      case EOpConstructBVec4:   outputConstructor(visit, node->getType(), "bvec4", node->getSequence()); break;
-      case EOpConstructInt:     outputConstructor(visit, node->getType(), "ivec1", node->getSequence()); break;
-      case EOpConstructIVec2:   outputConstructor(visit, node->getType(), "ivec2", node->getSequence()); break;
-      case EOpConstructIVec3:   outputConstructor(visit, node->getType(), "ivec3", node->getSequence()); break;
-      case EOpConstructIVec4:   outputConstructor(visit, node->getType(), "ivec4", node->getSequence()); break;
-      case EOpConstructUInt:    outputConstructor(visit, node->getType(), "uvec1", node->getSequence()); break;
-      case EOpConstructUVec2:   outputConstructor(visit, node->getType(), "uvec2", node->getSequence()); break;
-      case EOpConstructUVec3:   outputConstructor(visit, node->getType(), "uvec3", node->getSequence()); break;
-      case EOpConstructUVec4:   outputConstructor(visit, node->getType(), "uvec4", node->getSequence()); break;
-      case EOpConstructMat2:    outputConstructor(visit, node->getType(), "mat2", node->getSequence());  break;
-      case EOpConstructMat2x3:  outputConstructor(visit, node->getType(), "mat2x3", node->getSequence());  break;
-      case EOpConstructMat2x4:  outputConstructor(visit, node->getType(), "mat2x4", node->getSequence());  break;
-      case EOpConstructMat3x2:  outputConstructor(visit, node->getType(), "mat3x2", node->getSequence());  break;
-      case EOpConstructMat3:    outputConstructor(visit, node->getType(), "mat3", node->getSequence());  break;
-      case EOpConstructMat3x4:  outputConstructor(visit, node->getType(), "mat3x4", node->getSequence());  break;
-      case EOpConstructMat4x2:  outputConstructor(visit, node->getType(), "mat4x2", node->getSequence());  break;
-      case EOpConstructMat4x3:  outputConstructor(visit, node->getType(), "mat4x3", node->getSequence());  break;
-      case EOpConstructMat4:    outputConstructor(visit, node->getType(), "mat4", node->getSequence());  break;
+        case EOpParameters:
+            outputTriplet(out, visit, "(", ", ", ")\n{\n");
+            break;
+        case EOpConstructFloat:
+            outputConstructor(out, visit, node->getType(), "vec1", node->getSequence());
+            break;
+        case EOpConstructVec2:
+            outputConstructor(out, visit, node->getType(), "vec2", node->getSequence());
+            break;
+        case EOpConstructVec3:
+            outputConstructor(out, visit, node->getType(), "vec3", node->getSequence());
+            break;
+        case EOpConstructVec4:
+            outputConstructor(out, visit, node->getType(), "vec4", node->getSequence());
+            break;
+        case EOpConstructBool:
+            outputConstructor(out, visit, node->getType(), "bvec1", node->getSequence());
+            break;
+        case EOpConstructBVec2:
+            outputConstructor(out, visit, node->getType(), "bvec2", node->getSequence());
+            break;
+        case EOpConstructBVec3:
+            outputConstructor(out, visit, node->getType(), "bvec3", node->getSequence());
+            break;
+        case EOpConstructBVec4:
+            outputConstructor(out, visit, node->getType(), "bvec4", node->getSequence());
+            break;
+        case EOpConstructInt:
+            outputConstructor(out, visit, node->getType(), "ivec1", node->getSequence());
+            break;
+        case EOpConstructIVec2:
+            outputConstructor(out, visit, node->getType(), "ivec2", node->getSequence());
+            break;
+        case EOpConstructIVec3:
+            outputConstructor(out, visit, node->getType(), "ivec3", node->getSequence());
+            break;
+        case EOpConstructIVec4:
+            outputConstructor(out, visit, node->getType(), "ivec4", node->getSequence());
+            break;
+        case EOpConstructUInt:
+            outputConstructor(out, visit, node->getType(), "uvec1", node->getSequence());
+            break;
+        case EOpConstructUVec2:
+            outputConstructor(out, visit, node->getType(), "uvec2", node->getSequence());
+            break;
+        case EOpConstructUVec3:
+            outputConstructor(out, visit, node->getType(), "uvec3", node->getSequence());
+            break;
+        case EOpConstructUVec4:
+            outputConstructor(out, visit, node->getType(), "uvec4", node->getSequence());
+            break;
+        case EOpConstructMat2:
+            outputConstructor(out, visit, node->getType(), "mat2", node->getSequence());
+            break;
+        case EOpConstructMat2x3:
+            outputConstructor(out, visit, node->getType(), "mat2x3", node->getSequence());
+            break;
+        case EOpConstructMat2x4:
+            outputConstructor(out, visit, node->getType(), "mat2x4", node->getSequence());
+            break;
+        case EOpConstructMat3x2:
+            outputConstructor(out, visit, node->getType(), "mat3x2", node->getSequence());
+            break;
+        case EOpConstructMat3:
+            outputConstructor(out, visit, node->getType(), "mat3", node->getSequence());
+            break;
+        case EOpConstructMat3x4:
+            outputConstructor(out, visit, node->getType(), "mat3x4", node->getSequence());
+            break;
+        case EOpConstructMat4x2:
+            outputConstructor(out, visit, node->getType(), "mat4x2", node->getSequence());
+            break;
+        case EOpConstructMat4x3:
+            outputConstructor(out, visit, node->getType(), "mat4x3", node->getSequence());
+            break;
+        case EOpConstructMat4:
+            outputConstructor(out, visit, node->getType(), "mat4", node->getSequence());
+            break;
       case EOpConstructStruct:
         {
             if (node->getType().isArray())
             {
                 UNIMPLEMENTED();
             }
             const TString &structName = StructNameString(*node->getType().getStruct());
             mStructureHLSL->addConstructor(node->getType(), structName, node->getSequence());
-            outputTriplet(visit, (structName + "_ctor(").c_str(), ", ", ")");
+            outputTriplet(out, visit, (structName + "_ctor(").c_str(), ", ", ")");
         }
         break;
-      case EOpLessThan:         outputTriplet(visit, "(", " < ", ")");                 break;
-      case EOpGreaterThan:      outputTriplet(visit, "(", " > ", ")");                 break;
-      case EOpLessThanEqual:    outputTriplet(visit, "(", " <= ", ")");                break;
-      case EOpGreaterThanEqual: outputTriplet(visit, "(", " >= ", ")");                break;
-      case EOpVectorEqual:      outputTriplet(visit, "(", " == ", ")");                break;
-      case EOpVectorNotEqual:   outputTriplet(visit, "(", " != ", ")");                break;
+        case EOpLessThan:
+            outputTriplet(out, visit, "(", " < ", ")");
+            break;
+        case EOpGreaterThan:
+            outputTriplet(out, visit, "(", " > ", ")");
+            break;
+        case EOpLessThanEqual:
+            outputTriplet(out, visit, "(", " <= ", ")");
+            break;
+        case EOpGreaterThanEqual:
+            outputTriplet(out, visit, "(", " >= ", ")");
+            break;
+        case EOpVectorEqual:
+            outputTriplet(out, visit, "(", " == ", ")");
+            break;
+        case EOpVectorNotEqual:
+            outputTriplet(out, visit, "(", " != ", ")");
+            break;
       case EOpMod:
         ASSERT(node->getUseEmulatedFunction());
-        writeEmulatedFunctionTriplet(visit, "mod(");
+        writeEmulatedFunctionTriplet(out, visit, "mod(");
         break;
-      case EOpModf:             outputTriplet(visit, "modf(", ", ", ")");              break;
-      case EOpPow:              outputTriplet(visit, "pow(", ", ", ")");               break;
+      case EOpModf:
+          outputTriplet(out, visit, "modf(", ", ", ")");
+          break;
+      case EOpPow:
+          outputTriplet(out, visit, "pow(", ", ", ")");
+          break;
       case EOpAtan:
         ASSERT(node->getSequence()->size() == 2);   // atan(x) is a unary operator
         ASSERT(node->getUseEmulatedFunction());
-        writeEmulatedFunctionTriplet(visit, "atan(");
+        writeEmulatedFunctionTriplet(out, visit, "atan(");
         break;
-      case EOpMin:           outputTriplet(visit, "min(", ", ", ")");           break;
-      case EOpMax:           outputTriplet(visit, "max(", ", ", ")");           break;
-      case EOpClamp:         outputTriplet(visit, "clamp(", ", ", ")");         break;
+      case EOpMin:
+          outputTriplet(out, visit, "min(", ", ", ")");
+          break;
+      case EOpMax:
+          outputTriplet(out, visit, "max(", ", ", ")");
+          break;
+      case EOpClamp:
+          outputTriplet(out, visit, "clamp(", ", ", ")");
+          break;
       case EOpMix:
         {
             TIntermTyped *lastParamNode = (*(node->getSequence()))[2]->getAsTyped();
             if (lastParamNode->getType().getBasicType() == EbtBool)
             {
                 // There is no HLSL equivalent for ESSL3 built-in "genType mix (genType x, genType y, genBType a)",
                 // so use emulated version.
                 ASSERT(node->getUseEmulatedFunction());
-                writeEmulatedFunctionTriplet(visit, "mix(");
+                writeEmulatedFunctionTriplet(out, visit, "mix(");
             }
             else
             {
-                outputTriplet(visit, "lerp(", ", ", ")");
+                outputTriplet(out, visit, "lerp(", ", ", ")");
             }
         }
         break;
-      case EOpStep:          outputTriplet(visit, "step(", ", ", ")");          break;
-      case EOpSmoothStep:    outputTriplet(visit, "smoothstep(", ", ", ")");    break;
-      case EOpDistance:      outputTriplet(visit, "distance(", ", ", ")");      break;
-      case EOpDot:           outputTriplet(visit, "dot(", ", ", ")");           break;
-      case EOpCross:         outputTriplet(visit, "cross(", ", ", ")");         break;
+        case EOpStep:
+            outputTriplet(out, visit, "step(", ", ", ")");
+            break;
+        case EOpSmoothStep:
+            outputTriplet(out, visit, "smoothstep(", ", ", ")");
+            break;
+        case EOpDistance:
+            outputTriplet(out, visit, "distance(", ", ", ")");
+            break;
+        case EOpDot:
+            outputTriplet(out, visit, "dot(", ", ", ")");
+            break;
+        case EOpCross:
+            outputTriplet(out, visit, "cross(", ", ", ")");
+            break;
       case EOpFaceForward:
         ASSERT(node->getUseEmulatedFunction());
-        writeEmulatedFunctionTriplet(visit, "faceforward(");
+        writeEmulatedFunctionTriplet(out, visit, "faceforward(");
         break;
-      case EOpReflect:       outputTriplet(visit, "reflect(", ", ", ")");       break;
-      case EOpRefract:       outputTriplet(visit, "refract(", ", ", ")");       break;
+      case EOpReflect:
+          outputTriplet(out, visit, "reflect(", ", ", ")");
+          break;
+      case EOpRefract:
+          outputTriplet(out, visit, "refract(", ", ", ")");
+          break;
       case EOpOuterProduct:
         ASSERT(node->getUseEmulatedFunction());
-        writeEmulatedFunctionTriplet(visit, "outerProduct(");
+        writeEmulatedFunctionTriplet(out, visit, "outerProduct(");
         break;
-      case EOpMul:           outputTriplet(visit, "(", " * ", ")");             break;
+      case EOpMul:
+          outputTriplet(out, visit, "(", " * ", ")");
+          break;
       default: UNREACHABLE();
     }
 
     return true;
 }
 
-void OutputHLSL::writeSelection(TIntermSelection *node)
+void OutputHLSL::writeSelection(TInfoSinkBase &out, TIntermSelection *node)
 {
-    TInfoSinkBase &out = getInfoSink();
-
     out << "if (";
 
     node->getCondition()->traverse(this);
 
     out << ")\n";
 
-    outputLineDirective(node->getLine().first_line);
+    outputLineDirective(out, node->getLine().first_line);
 
     bool discard = false;
 
     if (node->getTrueBlock())
     {
         // The trueBlock child node will output braces.
         ASSERT(IsSequence(node->getTrueBlock()));
 
@@ -2371,30 +2622,30 @@ void OutputHLSL::writeSelection(TIntermS
     }
     else
     {
         // TODO(oetuaho): Check if the semicolon inside is necessary.
         // It's there as a result of conservative refactoring of the output.
         out << "{;}\n";
     }
 
-    outputLineDirective(node->getLine().first_line);
+    outputLineDirective(out, node->getLine().first_line);
 
     if (node->getFalseBlock())
     {
         out << "else\n";
 
-        outputLineDirective(node->getFalseBlock()->getLine().first_line);
+        outputLineDirective(out, node->getFalseBlock()->getLine().first_line);
 
         // Either this is "else if" or the falseBlock child node will output braces.
         ASSERT(IsSequence(node->getFalseBlock()) || node->getFalseBlock()->getAsSelectionNode() != nullptr);
 
         node->getFalseBlock()->traverse(this);
 
-        outputLineDirective(node->getFalseBlock()->getLine().first_line);
+        outputLineDirective(out, node->getFalseBlock()->getLine().first_line);
 
         // Detect false discard
         discard = (discard || FindDiscard::search(node->getFalseBlock()));
     }
 
     // ANGLE issue 486: Detect problematic conditional discard
     if (discard)
     {
@@ -2416,84 +2667,88 @@ bool OutputHLSL::visitSelection(Visit vi
     }
 
     // D3D errors when there is a gradient operation in a loop in an unflattened if.
     if (mShaderType == GL_FRAGMENT_SHADER && mCurrentFunctionMetadata->hasGradientLoop(node))
     {
         out << "FLATTEN ";
     }
 
-    writeSelection(node);
+    writeSelection(out, node);
 
     return false;
 }
 
 bool OutputHLSL::visitSwitch(Visit visit, TIntermSwitch *node)
 {
+    TInfoSinkBase &out = getInfoSink();
+
     if (node->getStatementList())
     {
         node->setStatementList(RemoveSwitchFallThrough::removeFallThrough(node->getStatementList()));
-        outputTriplet(visit, "switch (", ") ", "");
+        outputTriplet(out, visit, "switch (", ") ", "");
         // The curly braces get written when visiting the statementList aggregate
     }
     else
     {
         // No statementList, so it won't output curly braces
-        outputTriplet(visit, "switch (", ") {", "}\n");
+        outputTriplet(out, visit, "switch (", ") {", "}\n");
     }
     return true;
 }
 
 bool OutputHLSL::visitCase(Visit visit, TIntermCase *node)
 {
+    TInfoSinkBase &out = getInfoSink();
+
     if (node->hasCondition())
     {
-        outputTriplet(visit, "case (", "", "):\n");
+        outputTriplet(out, visit, "case (", "", "):\n");
         return true;
     }
     else
     {
-        TInfoSinkBase &out = getInfoSink();
         out << "default:\n";
         return false;
     }
 }
 
 void OutputHLSL::visitConstantUnion(TIntermConstantUnion *node)
 {
-    writeConstantUnion(node->getType(), node->getUnionArrayPointer());
+    TInfoSinkBase &out = getInfoSink();
+    writeConstantUnion(out, node->getType(), node->getUnionArrayPointer());
 }
 
 bool OutputHLSL::visitLoop(Visit visit, TIntermLoop *node)
 {
     mNestedLoopDepth++;
 
     bool wasDiscontinuous = mInsideDiscontinuousLoop;
     mInsideDiscontinuousLoop = mInsideDiscontinuousLoop ||
     mCurrentFunctionMetadata->mDiscontinuousLoops.count(node) > 0;
 
+    TInfoSinkBase &out = getInfoSink();
+
     if (mOutputType == SH_HLSL9_OUTPUT)
     {
-        if (handleExcessiveLoop(node))
+        if (handleExcessiveLoop(out, node))
         {
             mInsideDiscontinuousLoop = wasDiscontinuous;
             mNestedLoopDepth--;
 
             return false;
         }
     }
 
-    TInfoSinkBase &out = getInfoSink();
-
     const char *unroll = mCurrentFunctionMetadata->hasGradientInCallGraph(node) ? "LOOP" : "";
     if (node->getType() == ELoopDoWhile)
     {
         out << "{" << unroll << " do\n";
 
-        outputLineDirective(node->getLine().first_line);
+        outputLineDirective(out, node->getLine().first_line);
     }
     else
     {
         out << "{" << unroll << " for(";
 
         if (node->getInit())
         {
             node->getInit()->traverse(this);
@@ -2510,37 +2765,37 @@ bool OutputHLSL::visitLoop(Visit visit, 
 
         if (node->getExpression())
         {
             node->getExpression()->traverse(this);
         }
 
         out << ")\n";
 
-        outputLineDirective(node->getLine().first_line);
+        outputLineDirective(out, node->getLine().first_line);
     }
 
     if (node->getBody())
     {
         // The loop body node will output braces.
         ASSERT(IsSequence(node->getBody()));
         node->getBody()->traverse(this);
     }
     else
     {
         // TODO(oetuaho): Check if the semicolon inside is necessary.
         // It's there as a result of conservative refactoring of the output.
         out << "{;}\n";
     }
 
-    outputLineDirective(node->getLine().first_line);
+    outputLineDirective(out, node->getLine().first_line);
 
     if (node->getType() == ELoopDoWhile)
     {
-        outputLineDirective(node->getCondition()->getLine().first_line);
+        outputLineDirective(out, node->getCondition()->getLine().first_line);
         out << "while(\n";
 
         node->getCondition()->traverse(this);
 
         out << ");";
     }
 
     out << "}\n";
@@ -2553,17 +2808,17 @@ bool OutputHLSL::visitLoop(Visit visit, 
 
 bool OutputHLSL::visitBranch(Visit visit, TIntermBranch *node)
 {
     TInfoSinkBase &out = getInfoSink();
 
     switch (node->getFlowOp())
     {
       case EOpKill:
-        outputTriplet(visit, "discard;\n", "", "");
+          outputTriplet(out, visit, "discard;\n", "", "");
         break;
       case EOpBreak:
         if (visit == PreVisit)
         {
             if (mNestedLoopDepth > 1)
             {
                 mUsesNestedBreak = true;
             }
@@ -2575,17 +2830,19 @@ bool OutputHLSL::visitBranch(Visit visit
                 out << " = true; break;}\n";
             }
             else
             {
                 out << "break;\n";
             }
         }
         break;
-      case EOpContinue: outputTriplet(visit, "continue;\n", "", ""); break;
+      case EOpContinue:
+          outputTriplet(out, visit, "continue;\n", "", "");
+          break;
       case EOpReturn:
         if (visit == PreVisit)
         {
             if (node->getExpression())
             {
                 out << "return ";
             }
             else
@@ -2637,20 +2894,19 @@ bool OutputHLSL::isSingleStatement(TInte
         }
     }
 
     return true;
 }
 
 // Handle loops with more than 254 iterations (unsupported by D3D9) by splitting them
 // (The D3D documentation says 255 iterations, but the compiler complains at anything more than 254).
-bool OutputHLSL::handleExcessiveLoop(TIntermLoop *node)
+bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node)
 {
     const int MAX_LOOP_ITERATIONS = 254;
-    TInfoSinkBase &out = getInfoSink();
 
     // Parse loops of the form:
     // for(int index = initial; index [comparator] limit; index += increment)
     TIntermSymbol *index = NULL;
     TOperator comparator = EOpNull;
     int initial = 0;
     int limit = 0;
     int increment = 0;
@@ -2807,25 +3063,25 @@ bool OutputHLSL::handleExcessiveLoop(TIn
                 out << clampedLimit;
 
                 out << "; ";
                 index->traverse(this);
                 out << " += ";
                 out << increment;
                 out << ")\n";
 
-                outputLineDirective(node->getLine().first_line);
+                outputLineDirective(out, node->getLine().first_line);
                 out << "{\n";
 
                 if (node->getBody())
                 {
                     node->getBody()->traverse(this);
                 }
 
-                outputLineDirective(node->getLine().first_line);
+                outputLineDirective(out, node->getLine().first_line);
                 out << ";}\n";
 
                 if (!firstLoopFragment)
                 {
                     out << "}\n";
                 }
 
                 firstLoopFragment = false;
@@ -2841,43 +3097,40 @@ bool OutputHLSL::handleExcessiveLoop(TIn
             return true;
         }
         else UNIMPLEMENTED();
     }
 
     return false;   // Not handled as an excessive loop
 }
 
-void OutputHLSL::outputTriplet(Visit visit, const char *preString, const char *inString, const char *postString, TInfoSinkBase &out)
+void OutputHLSL::outputTriplet(TInfoSinkBase &out,
+                               Visit visit,
+                               const char *preString,
+                               const char *inString,
+                               const char *postString)
 {
     if (visit == PreVisit)
     {
         out << preString;
     }
     else if (visit == InVisit)
     {
         out << inString;
     }
     else if (visit == PostVisit)
     {
         out << postString;
     }
 }
 
-void OutputHLSL::outputTriplet(Visit visit, const char *preString, const char *inString, const char *postString)
-{
-    outputTriplet(visit, preString, inString, postString, getInfoSink());
-}
-
-void OutputHLSL::outputLineDirective(int line)
+void OutputHLSL::outputLineDirective(TInfoSinkBase &out, int line)
 {
     if ((mCompileOptions & SH_LINE_DIRECTIVES) && (line > 0))
     {
-        TInfoSinkBase &out = getInfoSink();
-
         out << "\n";
         out << "#line " << line;
 
         if (mSourcePath)
         {
             out << " \"" << mSourcePath << "\"";
         }
 
@@ -2924,23 +3177,26 @@ TString OutputHLSL::initializer(const TT
         {
             string += ", ";
         }
     }
 
     return "{" + string + "}";
 }
 
-void OutputHLSL::outputConstructor(Visit visit, const TType &type, const char *name, const TIntermSequence *parameters)
+void OutputHLSL::outputConstructor(TInfoSinkBase &out,
+                                   Visit visit,
+                                   const TType &type,
+                                   const char *name,
+                                   const TIntermSequence *parameters)
 {
     if (type.isArray())
     {
         UNIMPLEMENTED();
     }
-    TInfoSinkBase &out = getInfoSink();
 
     if (visit == PreVisit)
     {
         mStructureHLSL->addConstructor(type, name, parameters);
 
         out << name << "(";
     }
     else if (visit == InVisit)
@@ -2948,34 +3204,33 @@ void OutputHLSL::outputConstructor(Visit
         out << ", ";
     }
     else if (visit == PostVisit)
     {
         out << ")";
     }
 }
 
-const TConstantUnion *OutputHLSL::writeConstantUnion(const TType &type,
+const TConstantUnion *OutputHLSL::writeConstantUnion(TInfoSinkBase &out,
+                                                     const TType &type,
                                                      const TConstantUnion *const constUnion)
 {
-    TInfoSinkBase &out = getInfoSink();
-
     const TConstantUnion *constUnionIterated = constUnion;
 
     const TStructure* structure = type.getStruct();
     if (structure)
     {
         out << StructNameString(*structure) + "_ctor(";
 
         const TFieldList& fields = structure->fields();
 
         for (size_t i = 0; i < fields.size(); i++)
         {
             const TType *fieldType = fields[i]->type();
-            constUnionIterated     = writeConstantUnion(*fieldType, constUnionIterated);
+            constUnionIterated     = writeConstantUnion(out, *fieldType, constUnionIterated);
 
             if (i != fields.size() - 1)
             {
                 out << ", ";
             }
         }
 
         out << ")";
@@ -2994,20 +3249,20 @@ const TConstantUnion *OutputHLSL::writeC
         {
             out << ")";
         }
     }
 
     return constUnionIterated;
 }
 
-void OutputHLSL::writeEmulatedFunctionTriplet(Visit visit, const char *preStr)
+void OutputHLSL::writeEmulatedFunctionTriplet(TInfoSinkBase &out, Visit visit, const char *preStr)
 {
     TString preString = BuiltInFunctionEmulator::GetEmulatedFunctionName(preStr);
-    outputTriplet(visit, preString.c_str(), ", ", ")");
+    outputTriplet(out, visit, preString.c_str(), ", ", ")");
 }
 
 bool OutputHLSL::writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *symbolNode, TIntermTyped *expression)
 {
     sh::SearchSymbol searchSymbol(symbolNode->getSymbol());
     expression->traverse(&searchSymbol);
 
     if (searchSymbol.foundMatch())
@@ -3112,18 +3367,17 @@ void OutputHLSL::writeDeferredGlobalInit
             {
                 ASSERT(mInfoSinkStack.top() == &out);
                 expression->traverse(this);
             }
             out << ";\n";
         }
         else if (selection != nullptr)
         {
-            ASSERT(mInfoSinkStack.top() == &out);
-            writeSelection(selection);
+            writeSelection(out, selection);
         }
         else
         {
             UNREACHABLE();
         }
     }
 
     out << "}\n"
--- a/gfx/angle/src/compiler/translator/OutputHLSL.h
+++ b/gfx/angle/src/compiler/translator/OutputHLSL.h
@@ -45,59 +45,68 @@ class OutputHLSL : public TIntermTravers
 
     static TString initializer(const TType &type);
 
     TInfoSinkBase &getInfoSink() { ASSERT(!mInfoSinkStack.empty()); return *mInfoSinkStack.top(); }
 
     static bool canWriteAsHLSLLiteral(TIntermTyped *expression);
 
   protected:
-    void header(const BuiltInFunctionEmulator *builtInFunctionEmulator);
+    void header(TInfoSinkBase &out, const BuiltInFunctionEmulator *builtInFunctionEmulator);
 
     // Visit AST nodes and output their code to the body stream
     void visitSymbol(TIntermSymbol*);
     void visitRaw(TIntermRaw*);
     void visitConstantUnion(TIntermConstantUnion*);
     bool visitBinary(Visit visit, TIntermBinary*);
     bool visitUnary(Visit visit, TIntermUnary*);
     bool visitSelection(Visit visit, TIntermSelection*);
     bool visitSwitch(Visit visit, TIntermSwitch *);
     bool visitCase(Visit visit, TIntermCase *);
     bool visitAggregate(Visit visit, TIntermAggregate*);
     bool visitLoop(Visit visit, TIntermLoop*);
     bool visitBranch(Visit visit, TIntermBranch*);
 
     bool isSingleStatement(TIntermNode *node);
-    bool handleExcessiveLoop(TIntermLoop *node);
+    bool handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node);
 
     // Emit one of three strings depending on traverse phase. Called with literal strings so using const char* instead of TString.
-    void outputTriplet(Visit visit, const char *preString, const char *inString, const char *postString, TInfoSinkBase &out);
-    void outputTriplet(Visit visit, const char *preString, const char *inString, const char *postString);
-    void outputLineDirective(int line);
+    void outputTriplet(TInfoSinkBase &out,
+                       Visit visit,
+                       const char *preString,
+                       const char *inString,
+                       const char *postString);
+    void outputLineDirective(TInfoSinkBase &out, int line);
     TString argumentString(const TIntermSymbol *symbol);
     int vectorSize(const TType &type) const;
 
     // Emit constructor. Called with literal names so using const char* instead of TString.
-    void outputConstructor(Visit visit, const TType &type, const char *name, const TIntermSequence *parameters);
-    const TConstantUnion *writeConstantUnion(const TType &type, const TConstantUnion *constUnion);
+    void outputConstructor(TInfoSinkBase &out,
+                           Visit visit,
+                           const TType &type,
+                           const char *name,
+                           const TIntermSequence *parameters);
+    const TConstantUnion *writeConstantUnion(TInfoSinkBase &out,
+                                             const TType &type,
+                                             const TConstantUnion *constUnion);
 
     void outputEqual(Visit visit, const TType &type, TOperator op, TInfoSinkBase &out);
 
-    void writeEmulatedFunctionTriplet(Visit visit, const char *preStr);
+    void writeEmulatedFunctionTriplet(TInfoSinkBase &out, Visit visit, const char *preStr);
     void makeFlaggedStructMaps(const std::vector<TIntermTyped *> &flaggedStructs);
 
     // Returns true if it found a 'same symbol' initializer (initializer that references the variable it's initting)
     bool writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *symbolNode, TIntermTyped *expression);
     // Returns true if variable initializer could be written using literal {} notation.
     bool writeConstantInitialization(TInfoSinkBase &out,
                                      TIntermSymbol *symbolNode,
                                      TIntermTyped *expression);
 
     void writeDeferredGlobalInitializers(TInfoSinkBase &out);
-    void writeSelection(TIntermSelection *node);
+    void writeSelection(TInfoSinkBase &out, TIntermSelection *node);
 
     // Returns the function name
     TString addStructEqualityFunction(const TStructure &structure);
     TString addArrayEqualityFunction(const TType &type);
     TString addArrayAssignmentFunction(const TType &type);
     TString addArrayConstructIntoFunction(const TType &type);
 
     // Ensures if the type is a struct, the struct is defined
--- a/gfx/angle/src/compiler/translator/ParseContext.cpp
+++ b/gfx/angle/src/compiler/translator/ParseContext.cpp
@@ -221,34 +221,35 @@ void TParseContext::binaryOpError(const 
 }
 
 bool TParseContext::precisionErrorCheck(const TSourceLoc &line,
                                         TPrecision precision,
                                         TBasicType type)
 {
     if (!mChecksPrecisionErrors)
         return false;
-    switch (type)
+    if (precision == EbpUndefined)
     {
-        case EbtFloat:
-            if (precision == EbpUndefined)
-            {
+        switch (type)
+        {
+            case EbtFloat:
                 error(line, "No precision specified for (float)", "");
                 return true;
-            }
-            break;
-        case EbtInt:
-            if (precision == EbpUndefined)
-            {
+            case EbtInt:
+            case EbtUInt:
+                UNREACHABLE();  // there's always a predeclared qualifier
                 error(line, "No precision specified (int)", "");
                 return true;
-            }
-            break;
-        default:
-            return false;
+            default:
+                if (IsSampler(type))
+                {
+                    error(line, "No precision specified (sampler)", "");
+                    return true;
+                }
+        }
     }
     return false;
 }
 
 //
 // Both test and if necessary, spit out an error, to see if the node is really
 // an l-value that can be operated on this way.
 //
--- a/gfx/angle/src/compiler/translator/SymbolTable.cpp
+++ b/gfx/angle/src/compiler/translator/SymbolTable.cpp
@@ -275,17 +275,17 @@ TPrecision TSymbolTable::getDefaultPreci
     if (!SupportsPrecision(type))
         return EbpUndefined;
 
     // unsigned integers use the same precision as signed
     TBasicType baseType = (type == EbtUInt) ? EbtInt : type;
 
     int level = static_cast<int>(precisionStack.size()) - 1;
     assert(level >= 0); // Just to be safe. Should not happen.
-    // If we dont find anything we return this. Should we error check this?
+    // If we dont find anything we return this. Some types don't have predefined default precision.
     TPrecision prec = EbpUndefined;
     while (level >= 0)
     {
         PrecisionStackLevel::iterator it = precisionStack[level]->find(baseType);
         if (it != precisionStack[level]->end())
         {
             prec = (*it).second;
             break;
--- a/gfx/angle/src/compiler/translator/SymbolTable.h
+++ b/gfx/angle/src/compiler/translator/SymbolTable.h
@@ -430,16 +430,18 @@ class TSymbolTable : angle::NonCopyable
     }
 
     void dump(TInfoSink &infoSink) const;
 
     bool setDefaultPrecision(const TPublicType &type, TPrecision prec)
     {
         if (!SupportsPrecision(type.type))
             return false;
+        if (type.type == EbtUInt)
+            return false;  // ESSL 3.00.4 section 4.5.4
         if (type.isAggregate())
             return false; // Not allowed to set for aggregate types
         int indexOfLastElement = static_cast<int>(precisionStack.size()) - 1;
         // Uses map operator [], overwrites the current value
         (*precisionStack[indexOfLastElement])[type.type] = prec;
         return true;
     }
 
--- a/gfx/angle/src/compiler/translator/Types.cpp
+++ b/gfx/angle/src/compiler/translator/Types.cpp
@@ -54,16 +54,37 @@ TType::TType(const TPublicType &p)
         structure = p.userDef->getStruct();
 }
 
 bool TStructure::equals(const TStructure &other) const
 {
     return (uniqueId() == other.uniqueId());
 }
 
+TString TType::getCompleteString() const
+{
+    TStringStream stream;
+
+    if (invariant)
+        stream << "invariant ";
+    if (qualifier != EvqTemporary && qualifier != EvqGlobal)
+        stream << getQualifierString() << " ";
+    if (precision != EbpUndefined)
+        stream << getPrecisionString() << " ";
+    if (array)
+        stream << "array[" << getArraySize() << "] of ";
+    if (isMatrix())
+        stream << getCols() << "X" << getRows() << " matrix of ";
+    else if (isVector())
+        stream << getNominalSize() << "-component vector of ";
+
+    stream << getBasicString();
+    return stream.str();
+}
+
 //
 // Recursively generate mangled names.
 //
 TString TType::buildMangledName() const
 {
     TString mangledName;
     if (isMatrix())
         mangledName += 'm';
--- a/gfx/angle/src/compiler/translator/ValidateLimitations.cpp
+++ b/gfx/angle/src/compiler/translator/ValidateLimitations.cpp
@@ -48,39 +48,62 @@ class ValidateConstIndexExpr : public TI
 
   private:
     bool mValid;
     TLoopStack& mLoopStack;
 };
 
 }  // namespace anonymous
 
-ValidateLimitations::ValidateLimitations(sh::GLenum shaderType,
-                                         TInfoSinkBase &sink)
+ValidateLimitations::ValidateLimitations(sh::GLenum shaderType, TInfoSinkBase *sink)
     : TIntermTraverser(true, false, false),
       mShaderType(shaderType),
       mSink(sink),
-      mNumErrors(0)
+      mNumErrors(0),
+      mValidateIndexing(true),
+      mValidateInnerLoops(true)
+{
+}
+
+// static
+bool ValidateLimitations::IsLimitedForLoop(TIntermLoop *loop)
 {
+    // The shader type doesn't matter in this case.
+    ValidateLimitations validate(GL_FRAGMENT_SHADER, nullptr);
+    validate.mValidateIndexing   = false;
+    validate.mValidateInnerLoops = false;
+    if (!validate.validateLoopType(loop))
+        return false;
+    if (!validate.validateForLoopHeader(loop))
+        return false;
+    TIntermNode *body = loop->getBody();
+    if (body != nullptr)
+    {
+        validate.mLoopStack.push(loop);
+        body->traverse(&validate);
+        validate.mLoopStack.pop();
+    }
+    return (validate.mNumErrors == 0);
 }
 
 bool ValidateLimitations::visitBinary(Visit, TIntermBinary *node)
 {
     // Check if loop index is modified in the loop body.
     validateOperation(node, node->getLeft());
 
     // Check indexing.
     switch (node->getOp())
     {
       case EOpIndexDirect:
       case EOpIndexIndirect:
-        validateIndexing(node);
-        break;
+          if (mValidateIndexing)
+              validateIndexing(node);
+          break;
       default:
-        break;
+          break;
     }
     return true;
 }
 
 bool ValidateLimitations::visitUnary(Visit, TIntermUnary *node)
 {
     // Check if loop index is modified in the loop body.
     validateOperation(node, node->getOperand());
@@ -97,16 +120,19 @@ bool ValidateLimitations::visitAggregate
       default:
         break;
     }
     return true;
 }
 
 bool ValidateLimitations::visitLoop(Visit, TIntermLoop *node)
 {
+    if (!mValidateInnerLoops)
+        return true;
+
     if (!validateLoopType(node))
         return false;
 
     if (!validateForLoopHeader(node))
         return false;
 
     TIntermNode *body = node->getBody();
     if (body != NULL)
@@ -118,19 +144,22 @@ bool ValidateLimitations::visitLoop(Visi
 
     // The loop is fully processed - no need to visit children.
     return false;
 }
 
 void ValidateLimitations::error(TSourceLoc loc,
                                 const char *reason, const char *token)
 {
-    mSink.prefix(EPrefixError);
-    mSink.location(loc);
-    mSink << "'" << token << "' : " << reason << "\n";
+    if (mSink)
+    {
+        mSink->prefix(EPrefixError);
+        mSink->location(loc);
+        (*mSink) << "'" << token << "' : " << reason << "\n";
+    }
     ++mNumErrors;
 }
 
 bool ValidateLimitations::withinLoopBody() const
 {
     return !mLoopStack.empty();
 }
 
@@ -428,18 +457,18 @@ bool ValidateLimitations::validateOperat
               "Loop index cannot be statically assigned to within the body of the loop",
               symbol->getSymbol().c_str());
     }
     return true;
 }
 
 bool ValidateLimitations::isConstExpr(TIntermNode *node)
 {
-    ASSERT(node != NULL);
-    return node->getAsConstantUnion() != NULL;
+    ASSERT(node != nullptr);
+    return node->getAsConstantUnion() != nullptr && node->getAsTyped()->getQualifier() == EvqConst;
 }
 
 bool ValidateLimitations::isConstIndexExpr(TIntermNode *node)
 {
     ASSERT(node != NULL);
 
     ValidateConstIndexExpr validate(mLoopStack);
     node->traverse(&validate);
@@ -448,23 +477,16 @@ bool ValidateLimitations::isConstIndexEx
 
 bool ValidateLimitations::validateIndexing(TIntermBinary *node)
 {
     ASSERT((node->getOp() == EOpIndexDirect) ||
            (node->getOp() == EOpIndexIndirect));
 
     bool valid = true;
     TIntermTyped *index = node->getRight();
-    // The index expression must have integral type.
-    if (!index->isScalarInt()) {
-        error(index->getLine(),
-              "Index expression must have integral type",
-              index->getCompleteString().c_str());
-        valid = false;
-    }
     // The index expession must be a constant-index-expression unless
     // the operand is a uniform in a vertex shader.
     TIntermTyped *operand = node->getLeft();
     bool skip = (mShaderType == GL_VERTEX_SHADER) &&
                 (operand->getQualifier() == EvqUniform);
     if (!skip && !isConstIndexExpr(index))
     {
         error(index->getLine(), "Index expression must be constant", "[]");
--- a/gfx/angle/src/compiler/translator/ValidateLimitations.h
+++ b/gfx/angle/src/compiler/translator/ValidateLimitations.h
@@ -12,25 +12,27 @@
 
 class TInfoSinkBase;
 
 // Traverses intermediate tree to ensure that the shader does not exceed the
 // minimum functionality mandated in GLSL 1.0 spec, Appendix A.
 class ValidateLimitations : public TIntermTraverser
 {
   public:
-    ValidateLimitations(sh::GLenum shaderType, TInfoSinkBase &sink);
+    ValidateLimitations(sh::GLenum shaderType, TInfoSinkBase *sink);
 
     int numErrors() const { return mNumErrors; }
 
     bool visitBinary(Visit, TIntermBinary *) override;
     bool visitUnary(Visit, TIntermUnary *) override;
     bool visitAggregate(Visit, TIntermAggregate *) override;
     bool visitLoop(Visit, TIntermLoop *) override;
 
+    static bool IsLimitedForLoop(TIntermLoop *node);
+
   private:
     void error(TSourceLoc loc, const char *reason, const char *token);
 
     bool withinLoopBody() const;
     bool isLoopIndex(TIntermSymbol *symbol);
     bool validateLoopType(TIntermLoop *node);
 
     bool validateForLoopHeader(TIntermLoop *node);
@@ -46,14 +48,16 @@ class ValidateLimitations : public TInte
 
     // Returns true if indexing does not exceed the minimum functionality
     // mandated in GLSL 1.0 spec, Appendix A, Section 5.
     bool isConstExpr(TIntermNode *node);
     bool isConstIndexExpr(TIntermNode *node);
     bool validateIndexing(TIntermBinary *node);
 
     sh::GLenum mShaderType;
-    TInfoSinkBase &mSink;
+    TInfoSinkBase *mSink;
     int mNumErrors;
     TLoopStack mLoopStack;
+    bool mValidateIndexing;
+    bool mValidateInnerLoops;
 };
 
 #endif // COMPILER_TRANSLATOR_VALIDATELIMITATIONS_H_
--- a/gfx/angle/src/compiler/translator/intermOut.cpp
+++ b/gfx/angle/src/compiler/translator/intermOut.cpp
@@ -60,38 +60,16 @@ void OutputTreeText(TInfoSinkBase &sink,
     sink.location(node->getLine());
 
     for (i = 0; i < depth; ++i)
         sink << "  ";
 }
 
 }  // namespace anonymous
 
-
-TString TType::getCompleteString() const
-{
-    TStringStream stream;
-
-    if (invariant)
-        stream << "invariant ";
-    if (qualifier != EvqTemporary && qualifier != EvqGlobal)
-        stream << getQualifierString() << " ";
-    if (precision != EbpUndefined)
-        stream << getPrecisionString() << " ";
-    if (array)
-        stream << "array[" << getArraySize() << "] of ";
-    if (isMatrix())
-        stream << getCols() << "X" << getRows() << " matrix of ";
-    else if (isVector())
-        stream << getNominalSize() << "-component vector of ";
-
-    stream << getBasicString();
-    return stream.str();
-}
-
 //
 // The rest of the file are the traversal functions.  The last one
 // is the one that starts the traversal.
 //
 // Return true from interior nodes to have the external traversal
 // continue on to children.  If you process children yourself,
 // return false.
 //
--- a/gfx/angle/src/libANGLE/BinaryStream.h
+++ b/gfx/angle/src/libANGLE/BinaryStream.h
@@ -87,29 +87,29 @@ class BinaryInputStream : angle::NonCopy
         size_t length;
         readInt(&length);
 
         if (mError)
         {
             return;
         }
 
-        if (mOffset + length > mLength)
+        if (!rx::IsUnsignedAdditionSafe(mOffset, length) || mOffset + length > mLength)
         {
             mError = true;
             return;
         }
 
         v->assign(reinterpret_cast<const char *>(mData) + mOffset, length);
         mOffset += length;
     }
 
     void skip(size_t length)
     {
-        if (mOffset + length > mLength)
+        if (!rx::IsUnsignedAdditionSafe(mOffset, length) || mOffset + length > mLength)
         {
             mError = true;
             return;
         }
 
         mOffset += length;
     }
 
@@ -139,19 +139,25 @@ class BinaryInputStream : angle::NonCopy
     const uint8_t *mData;
     size_t mLength;
 
     template <typename T>
     void read(T *v, size_t num)
     {
         StaticAssertIsFundamental<T>();
 
+        if (!rx::IsUnsignedMultiplicationSafe(num, sizeof(T)))
+        {
+            mError = true;
+            return;
+        }
+
         size_t length = num * sizeof(T);
 
-        if (mOffset + length > mLength)
+        if (!rx::IsUnsignedAdditionSafe(mOffset, length) || mOffset + length > mLength)
         {
             mError = true;
             return;
         }
 
         memcpy(v, mData + mOffset, length);
         mOffset += length;
     }
new file mode 100644
--- /dev/null
+++ b/gfx/angle/src/libANGLE/BinaryStream_unittest.cpp
@@ -0,0 +1,71 @@
+//
+// Copyright (c) 2015 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.
+//
+
+// BinaryStream_unittest.cpp: Unit tests of the binary stream classes.
+
+#include <gtest/gtest.h>
+
+#include "libANGLE/BinaryStream.h"
+
+namespace angle
+{
+
+// Test that errors are properly generated for overflows.
+TEST(BinaryInputStream, Overflow)
+{
+    const uint8_t goodValue = 2;
+    const uint8_t badValue = 255;
+
+    const size_t dataSize = 1024;
+    const size_t slopSize = 1024;
+
+    std::vector<uint8_t> data(dataSize + slopSize);
+    std::fill(data.begin(), data.begin() + dataSize, goodValue);
+    std::fill(data.begin() + dataSize, data.end(), badValue);
+
+    std::vector<uint8_t> outputData(dataSize);
+
+    auto checkDataIsSafe = [=](uint8_t item)
+    {
+        return item == goodValue;
+    };
+
+    {
+        // One large read
+        gl::BinaryInputStream stream(data.data(), dataSize);
+        stream.readBytes(outputData.data(), dataSize);
+        ASSERT_FALSE(stream.error());
+        ASSERT_TRUE(std::all_of(outputData.begin(), outputData.end(), checkDataIsSafe));
+        ASSERT_TRUE(stream.endOfStream());
+    }
+
+    {
+        // Two half-sized reads
+        gl::BinaryInputStream stream(data.data(), dataSize);
+        stream.readBytes(outputData.data(), dataSize / 2);
+        ASSERT_FALSE(stream.error());
+        stream.readBytes(outputData.data() + dataSize / 2, dataSize / 2);
+        ASSERT_FALSE(stream.error());
+        ASSERT_TRUE(std::all_of(outputData.begin(), outputData.end(), checkDataIsSafe));
+        ASSERT_TRUE(stream.endOfStream());
+    }
+
+    {
+        // One large read that is too big
+        gl::BinaryInputStream stream(data.data(), dataSize);
+        stream.readBytes(outputData.data(), dataSize + 1);
+        ASSERT_TRUE(stream.error());
+    }
+
+    {
+        // Two reads, one that overflows the offset
+        gl::BinaryInputStream stream(data.data(), dataSize);
+        stream.readBytes(outputData.data(), dataSize - 1);
+        ASSERT_FALSE(stream.error());
+        stream.readBytes(outputData.data(), std::numeric_limits<size_t>::max() - dataSize - 2);
+    }
+}
+}
--- a/gfx/angle/src/libANGLE/Caps.cpp
+++ b/gfx/angle/src/libANGLE/Caps.cpp
@@ -109,16 +109,17 @@ Extensions::Extensions()
       textureRG(false),
       textureCompressionDXT1(false),
       textureCompressionDXT3(false),
       textureCompressionDXT5(false),
       textureCompressionASTCHDR(false),
       textureCompressionASTCLDR(false),
       compressedETC1RGB8Texture(false),
       depthTextures(false),
+      depth32(false),
       textureStorage(false),
       textureNPOT(false),
       drawBuffers(false),
       textureFilterAnisotropic(false),
       maxTextureAnisotropy(false),
       occlusionQueryBoolean(false),
       fence(false),
       timerQuery(false),
@@ -172,16 +173,17 @@ std::vector<std::string> Extensions::get
     InsertExtensionString("GL_EXT_texture_compression_dxt1",     textureCompressionDXT1,    &extensionStrings);
     InsertExtensionString("GL_ANGLE_texture_compression_dxt3",   textureCompressionDXT3,    &extensionStrings);
     InsertExtensionString("GL_ANGLE_texture_compression_dxt5",   textureCompressionDXT5,    &extensionStrings);
     InsertExtensionString("GL_KHR_texture_compression_astc_hdr", textureCompressionASTCHDR, &extensionStrings);
     InsertExtensionString("GL_KHR_texture_compression_astc_ldr", textureCompressionASTCLDR, &extensionStrings);
     InsertExtensionString("GL_OES_compressed_ETC1_RGB8_texture", compressedETC1RGB8Texture, &extensionStrings);
     InsertExtensionString("GL_EXT_sRGB",                         sRGB,                      &extensionStrings);
     InsertExtensionString("GL_ANGLE_depth_texture",              depthTextures,             &extensionStrings);
+    InsertExtensionString("GL_OES_depth32",                      depth32,                   &extensionStrings);
     InsertExtensionString("GL_EXT_texture_storage",              textureStorage,            &extensionStrings);
     InsertExtensionString("GL_OES_texture_npot",                 textureNPOT,               &extensionStrings);
     InsertExtensionString("GL_EXT_draw_buffers",                 drawBuffers,               &extensionStrings);
     InsertExtensionString("GL_EXT_texture_filter_anisotropic",   textureFilterAnisotropic,  &extensionStrings);
     InsertExtensionString("GL_EXT_occlusion_query_boolean",      occlusionQueryBoolean,     &extensionStrings);
     InsertExtensionString("GL_NV_fence",                         fence,                     &extensionStrings);
     InsertExtensionString("GL_ANGLE_timer_query",                timerQuery,                &extensionStrings);
     InsertExtensionString("GL_EXT_robustness",                   robustness,                &extensionStrings);
@@ -430,16 +432,25 @@ static bool DetermineDepthTextureSupport
     std::vector<GLenum> requiredFormats;
     requiredFormats.push_back(GL_DEPTH_COMPONENT16);
     requiredFormats.push_back(GL_DEPTH_COMPONENT32_OES);
     requiredFormats.push_back(GL_DEPTH24_STENCIL8_OES);
 
     return GetFormatSupport(textureCaps, requiredFormats, true, true, true);
 }
 
+// Check for GL_OES_depth32
+static bool DetermineDepth32Support(const TextureCapsMap &textureCaps)
+{
+    std::vector<GLenum> requiredFormats;
+    requiredFormats.push_back(GL_DEPTH_COMPONENT32_OES);
+
+    return GetFormatSupport(textureCaps, requiredFormats, false, false, true);
+}
+
 // Check for GL_EXT_color_buffer_float
 static bool DetermineColorBufferFloatSupport(const TextureCapsMap &textureCaps)
 {
     std::vector<GLenum> requiredFormats;
     requiredFormats.push_back(GL_R16F);
     requiredFormats.push_back(GL_RG16F);
     requiredFormats.push_back(GL_RGBA16F);
     requiredFormats.push_back(GL_R32F);
@@ -463,16 +474,17 @@ void Extensions::setTextureExtensionSupp
     textureCompressionDXT1 = DetermineDXT1TextureSupport(textureCaps);
     textureCompressionDXT3 = DetermineDXT3TextureSupport(textureCaps);
     textureCompressionDXT5 = DetermineDXT5TextureSupport(textureCaps);
     textureCompressionASTCHDR = DetermineASTCTextureSupport(textureCaps);
     textureCompressionASTCLDR = textureCompressionASTCHDR;
     compressedETC1RGB8Texture = DetermineETC1RGB8TextureSupport(textureCaps);
     sRGB = DetermineSRGBTextureSupport(textureCaps);
     depthTextures = DetermineDepthTextureSupport(textureCaps);
+    depth32                   = DetermineDepth32Support(textureCaps);
     colorBufferFloat = DetermineColorBufferFloatSupport(textureCaps);
 }
 
 TypePrecision::TypePrecision()
 {
     range[0] = 0;
     range[1] = 0;
     precision = 0;
@@ -631,33 +643,41 @@ std::vector<std::string> DeviceExtension
     InsertExtensionString("EGL_ANGLE_device_d3d",                          deviceD3D,                      &extensionStrings);
 
     return extensionStrings;
 }
 
 ClientExtensions::ClientExtensions()
     : clientExtensions(false),
       platformBase(false),
+      platformDevice(false),
       platformANGLE(false),
       platformANGLED3D(false),
       platformANGLEOpenGL(false),
+      deviceCreation(false),
+      deviceCreationD3D11(false),
+      x11Visual(false),
       clientGetAllProcAddresses(false)
 {
 }
 
 std::vector<std::string> ClientExtensions::getStrings() const
 {
     std::vector<std::string> extensionStrings;
 
     // clang-format off
     //                   | Extension name                         | Supported flag           | Output vector   |
     InsertExtensionString("EGL_EXT_client_extensions",             clientExtensions,          &extensionStrings);
     InsertExtensionString("EGL_EXT_platform_base",                 platformBase,              &extensionStrings);
+    InsertExtensionString("EGL_EXT_platform_device",               platformDevice,            &extensionStrings);
     InsertExtensionString("EGL_ANGLE_platform_angle",              platformANGLE,             &extensionStrings);
     InsertExtensionString("EGL_ANGLE_platform_angle_d3d",          platformANGLED3D,          &extensionStrings);
     InsertExtensionString("EGL_ANGLE_platform_angle_opengl",       platformANGLEOpenGL,       &extensionStrings);
+    InsertExtensionString("EGL_ANGLE_device_creation",             deviceCreation,            &extensionStrings);
+    InsertExtensionString("EGL_ANGLE_device_creation_d3d11",       deviceCreationD3D11,       &extensionStrings);
+    InsertExtensionString("EGL_ANGLE_x11_visual",                  x11Visual,                 &extensionStrings);
     InsertExtensionString("EGL_KHR_client_get_all_proc_addresses", clientGetAllProcAddresses, &extensionStrings);
     // clang-format on
 
     return extensionStrings;
 }
 
 }
--- a/gfx/angle/src/libANGLE/Caps.h
+++ b/gfx/angle/src/libANGLE/Caps.h
@@ -78,17 +78,17 @@ struct Extensions
     // GL_EXT_texture_format_BGRA8888
     // GL_OES_texture_half_float, GL_OES_texture_half_float_linear
     // GL_OES_texture_float, GL_OES_texture_float_linear
     // GL_EXT_texture_rg
     // GL_EXT_texture_compression_dxt1, GL_ANGLE_texture_compression_dxt3, GL_ANGLE_texture_compression_dxt5
     // GL_KHR_texture_compression_astc_hdr, GL_KHR_texture_compression_astc_ldr
     // GL_OES_compressed_ETC1_RGB8_texture
     // GL_EXT_sRGB
-    // GL_ANGLE_depth_texture
+    // GL_ANGLE_depth_texture, GL_OES_depth32
     // GL_EXT_color_buffer_float
     void setTextureExtensionSupport(const TextureCapsMap &textureCaps);
 
     // ES2 Extension support
 
     // GL_OES_element_index_uint
     bool elementIndexUint;
 
@@ -153,16 +153,20 @@ struct Extensions
     // GL_EXT_sRGB
     // Implies that TextureCaps for GL_SRGB8_ALPHA8 and GL_SRGB8 exist
     // TODO: Don't advertise this extension in ES3
     bool sRGB;
 
     // GL_ANGLE_depth_texture
     bool depthTextures;
 
+    // GL_OES_depth32
+    // Allows DEPTH_COMPONENT32_OES as a valid Renderbuffer format.
+    bool depth32;
+
     // GL_EXT_texture_storage
     bool textureStorage;
 
     // GL_OES_texture_npot
     bool textureNPOT;
 
     // GL_EXT_draw_buffers
     bool drawBuffers;
@@ -462,24 +466,36 @@ struct ClientExtensions
     std::vector<std::string> getStrings() const;
 
     // EGL_EXT_client_extensions
     bool clientExtensions;
 
     // EGL_EXT_platform_base
     bool platformBase;
 
+    // EGL_EXT_platform_device
+    bool platformDevice;
+
     // EGL_ANGLE_platform_angle
     bool platformANGLE;
 
     // EGL_ANGLE_platform_angle_d3d
     bool platformANGLED3D;
 
     // EGL_ANGLE_platform_angle_opengl
     bool platformANGLEOpenGL;
 
+    // EGL_ANGLE_device_creation
+    bool deviceCreation;
+
+    // EGL_ANGLE_device_creation_d3d11
+    bool deviceCreationD3D11;
+
+    // EGL_ANGLE_x11_visual
+    bool x11Visual;
+
     // EGL_KHR_client_get_all_proc_addresses
     bool clientGetAllProcAddresses;
 };
 
 }
 
 #endif // LIBANGLE_CAPS_H_
--- a/gfx/angle/src/libANGLE/Context.cpp
+++ b/gfx/angle/src/libANGLE/Context.cpp
@@ -139,18 +139,16 @@ Context::Context(const egl::Config *conf
     bindPixelUnpackBuffer(0);
 
     if (mClientVersion >= 3)
     {
         // [OpenGL ES 3.0.2] section 2.14.1 pg 85:
         // In the initial state, a default transform feedback object is bound and treated as
         // a transform feedback object with a name of zero. That object is bound any time
         // BindTransformFeedback is called with id of zero
-        mTransformFeedbackZero.set(
-            new TransformFeedback(mRenderer->createTransformFeedback(), 0, mCaps));
         bindTransformFeedback(0);
     }
 
     mHasBeenCurrent = false;
     mContextLost = false;
     mResetStatus = GL_NO_ERROR;
     mResetStrategy = (notifyResets ? GL_LOSE_CONTEXT_ON_RESET_EXT : GL_NO_RESET_NOTIFICATION_EXT);
     mRobustAccess = robustAccess;
@@ -184,20 +182,22 @@ Context::~Context()
         }
     }
 
     for (auto vertexArray : mVertexArrayMap)
     {
         SafeDelete(vertexArray.second);
     }
 
-    mTransformFeedbackZero.set(NULL);
     for (auto transformFeedback : mTransformFeedbackMap)
     {
-        transformFeedback.second->release();
+        if (transformFeedback.second != nullptr)
+        {
+            transformFeedback.second->release();
+        }
     }
 
     for (auto &zeroTexture : mZeroTextures)
     {
         zeroTexture.second.set(NULL);
     }
     mZeroTextures.clear();
 
@@ -319,38 +319,31 @@ GLsync Context::createFenceSync()
 {
     GLuint handle = mResourceManager->createFenceSync();
 
     return reinterpret_cast<GLsync>(static_cast<uintptr_t>(handle));
 }
 
 GLuint Context::createVertexArray()
 {
-    GLuint handle = mVertexArrayHandleAllocator.allocate();
-
-    // Although the spec states VAO state is not initialized until the object is bound,
-    // we create it immediately. The resulting behaviour is transparent to the application,
-    // since it's not currently possible to access the state until the object is bound.
-    VertexArray *vertexArray = new VertexArray(mRenderer, handle, MAX_VERTEX_ATTRIBS);
-    mVertexArrayMap[handle] = vertexArray;
-    return handle;
+    GLuint vertexArray           = mVertexArrayHandleAllocator.allocate();
+    mVertexArrayMap[vertexArray] = nullptr;
+    return vertexArray;
 }
 
 GLuint Context::createSampler()
 {
     return mResourceManager->createSampler();
 }
 
 GLuint Context::createTransformFeedback()
 {
-    GLuint handle = mTransformFeedbackAllocator.allocate();
-    TransformFeedback *transformFeedback = new TransformFeedback(mRenderer->createTransformFeedback(), handle, mCaps);
-    transformFeedback->addRef();
-    mTransformFeedbackMap[handle] = transformFeedback;
-    return handle;
+    GLuint transformFeedback                 = mTransformFeedbackAllocator.allocate();
+    mTransformFeedbackMap[transformFeedback] = nullptr;
+    return transformFeedback;
 }
 
 // Returns an unused framebuffer name
 GLuint Context::createFramebuffer()
 {
     GLuint handle = mFramebufferHandleAllocator.allocate();
 
     mFramebufferMap[handle] = NULL;
@@ -423,25 +416,28 @@ void Context::deleteFenceSync(GLsync fen
     // wait commands finish. However, since the name becomes invalid, we cannot query the fence,
     // and since our API is currently designed for being called from a single thread, we can delete
     // the fence immediately.
     mResourceManager->deleteFenceSync(static_cast<GLuint>(reinterpret_cast<uintptr_t>(fenceSync)));
 }
 
 void Context::deleteVertexArray(GLuint vertexArray)
 {
-    auto vertexArrayObject = mVertexArrayMap.find(vertexArray);
-
-    if (vertexArrayObject != mVertexArrayMap.end())
+    auto iter = mVertexArrayMap.find(vertexArray);
+    if (iter != mVertexArrayMap.end())
     {
-        detachVertexArray(vertexArray);
+        VertexArray *vertexArrayObject = iter->second;
+        if (vertexArrayObject != nullptr)
+        {
+            detachVertexArray(vertexArray);
+            delete vertexArrayObject;
+        }
 
-        mVertexArrayHandleAllocator.release(vertexArrayObject->first);
-        delete vertexArrayObject->second;
-        mVertexArrayMap.erase(vertexArrayObject);
+        mVertexArrayMap.erase(iter);
+        mVertexArrayHandleAllocator.release(vertexArray);
     }
 }
 
 void Context::deleteSampler(GLuint sampler)
 {
     if (mResourceManager->getSampler(sampler))
     {
         detachSampler(sampler);
@@ -450,20 +446,25 @@ void Context::deleteSampler(GLuint sampl
     mResourceManager->deleteSampler(sampler);
 }
 
 void Context::deleteTransformFeedback(GLuint transformFeedback)
 {
     auto iter = mTransformFeedbackMap.find(transformFeedback);
     if (iter != mTransformFeedbackMap.end())
     {
-        detachTransformFeedback(transformFeedback);
+        TransformFeedback *transformFeedbackObject = iter->second;
+        if (transformFeedbackObject != nullptr)
+        {
+            detachTransformFeedback(transformFeedback);
+            transformFeedbackObject->release();
+        }
+
+        mTransformFeedbackMap.erase(iter);
         mTransformFeedbackAllocator.release(transformFeedback);
-        iter->second->release();
-        mTransformFeedbackMap.erase(iter);
     }
 }
 
 void Context::deleteFramebuffer(GLuint framebuffer)
 {
     FramebufferMap::iterator framebufferObject = mFramebufferMap.find(framebuffer);
 
     if (framebufferObject != mFramebufferMap.end())
@@ -530,43 +531,28 @@ Renderbuffer *Context::getRenderbuffer(G
 FenceSync *Context::getFenceSync(GLsync handle) const
 {
     return mResourceManager->getFenceSync(static_cast<GLuint>(reinterpret_cast<uintptr_t>(handle)));
 }
 
 VertexArray *Context::getVertexArray(GLuint handle) const
 {
     auto vertexArray = mVertexArrayMap.find(handle);
-
-    if (vertexArray == mVertexArrayMap.end())
-    {
-        return NULL;
-    }
-    else
-    {
-        return vertexArray->second;
-    }
+    return (vertexArray != mVertexArrayMap.end()) ? vertexArray->second : nullptr;
 }
 
 Sampler *Context::getSampler(GLuint handle) const
 {
     return mResourceManager->getSampler(handle);
 }
 
 TransformFeedback *Context::getTransformFeedback(GLuint handle) const
 {
-    if (handle == 0)
-    {
-        return mTransformFeedbackZero.get();
-    }
-    else
-    {
-        TransformFeedbackMap::const_iterator iter = mTransformFeedbackMap.find(handle);
-        return (iter != mTransformFeedbackMap.end()) ? iter->second : NULL;
-    }
+    auto iter = mTransformFeedbackMap.find(handle);
+    return (iter != mTransformFeedbackMap.end()) ? iter->second : nullptr;
 }
 
 bool Context::isSampler(GLuint samplerName) const
 {
     return mResourceManager->isSampler(samplerName);
 }
 
 void Context::bindArrayBuffer(unsigned int buffer)
@@ -626,21 +612,17 @@ void Context::bindRenderbuffer(GLuint re
 {
     mResourceManager->checkRenderbufferAllocation(renderbuffer);
 
     mState.setRenderbufferBinding(getRenderbuffer(renderbuffer));
 }
 
 void Context::bindVertexArray(GLuint vertexArray)
 {
-    if (!getVertexArray(vertexArray))
-    {
-        VertexArray *vertexArrayObject = new VertexArray(mRenderer, vertexArray, MAX_VERTEX_ATTRIBS);
-        mVertexArrayMap[vertexArray] = vertexArrayObject;
-    }
+    checkVertexArrayAllocation(vertexArray);
 
     mState.setVertexArrayBinding(getVertexArray(vertexArray));
 }
 
 void Context::bindSampler(GLuint textureUnit, GLuint sampler)
 {
     ASSERT(textureUnit < mCaps.maxCombinedTextureImageUnits);
     mResourceManager->checkSamplerAllocation(sampler);
@@ -706,16 +688,18 @@ void Context::bindPixelUnpackBuffer(GLui
 
 void Context::useProgram(GLuint program)
 {
     mState.setProgram(getProgram(program));
 }
 
 void Context::bindTransformFeedback(GLuint transformFeedback)
 {
+    checkTransformFeedbackAllocation(transformFeedback);
+
     mState.setTransformFeedbackBinding(getTransformFeedback(transformFeedback));
 }
 
 Error Context::beginQuery(GLenum target, GLuint query)
 {
     Query *queryObject = getQuery(query, true, target);
     ASSERT(queryObject);
 
@@ -1088,30 +1072,16 @@ bool Context::getQueryParameterInfo(GLen
                 *numParams = 1;
             }
             else
             {
                 return false;
             }
         }
         return true;
-      case GL_PIXEL_PACK_BUFFER_BINDING:
-      case GL_PIXEL_UNPACK_BUFFER_BINDING:
-        {
-            if (mExtensions.pixelBufferObject)
-            {
-                *type = GL_INT;
-                *numParams = 1;
-            }
-            else
-            {
-                return false;
-            }
-        }
-        return true;
       case GL_MAX_VIEWPORT_DIMS:
         {
             *type = GL_INT;
             *numParams = 2;
         }
         return true;
       case GL_VIEWPORT:
       case GL_SCISSOR_BOX:
@@ -1205,16 +1175,25 @@ bool Context::getQueryParameterInfo(GLen
         case GL_VERTEX_ARRAY_BINDING:
             if ((mClientVersion < 3) && !mExtensions.vertexArrayObject)
             {
                 return false;
             }
             *type      = GL_INT;
             *numParams = 1;
             return true;
+        case GL_PIXEL_PACK_BUFFER_BINDING:
+        case GL_PIXEL_UNPACK_BUFFER_BINDING:
+            if ((mClientVersion < 3) && !mExtensions.pixelBufferObject)
+            {
+                return false;
+            }
+            *type      = GL_INT;
+            *numParams = 1;
+            return true;
     }
 
     if (mClientVersion < 3)
     {
         return false;
     }
 
     // Check for ES3.0+ parameter names
@@ -1266,16 +1245,17 @@ bool Context::getQueryParameterInfo(GLen
             *type = GL_INT_64_ANGLEX;
             *numParams = 1;
         }
         return true;
 
       case GL_TRANSFORM_FEEDBACK_ACTIVE:
       case GL_TRANSFORM_FEEDBACK_PAUSED:
       case GL_PRIMITIVE_RESTART_FIXED_INDEX:
+      case GL_RASTERIZER_DISCARD:
         {
             *type = GL_BOOL;
             *numParams = 1;
         }
         return true;
 
       case GL_MAX_TEXTURE_LOD_BIAS:
         {
@@ -1487,41 +1467,67 @@ EGLenum Context::getRenderBuffer() const
         return backAttachment->getSurface()->getRenderBuffer();
     }
     else
     {
         return EGL_NONE;
     }
 }
 
+void Context::checkVertexArrayAllocation(GLuint vertexArray)
+{
+    if (!getVertexArray(vertexArray))
+    {
+        VertexArray *vertexArrayObject =
+            new VertexArray(mRenderer, vertexArray, MAX_VERTEX_ATTRIBS);
+        mVertexArrayMap[vertexArray] = vertexArrayObject;
+    }
+}
+
+void Context::checkTransformFeedbackAllocation(GLuint transformFeedback)
+{
+    if (!getTransformFeedback(transformFeedback))
+    {
+        TransformFeedback *transformFeedbackObject =
+            new TransformFeedback(mRenderer->createTransformFeedback(), transformFeedback, mCaps);
+        transformFeedbackObject->addRef();
+        mTransformFeedbackMap[transformFeedback] = transformFeedbackObject;
+    }
+}
+
+bool Context::isVertexArrayGenerated(GLuint vertexArray)
+{
+    return mVertexArrayMap.find(vertexArray) != mVertexArrayMap.end();
+}
+
+bool Context::isTransformFeedbackGenerated(GLuint transformFeedback)
+{
+    return mTransformFeedbackMap.find(transformFeedback) != mTransformFeedbackMap.end();
+}
+
 void Context::detachTexture(GLuint texture)
 {
     // Simple pass-through to State's detachTexture method, as textures do not require
     // allocation map management either here or in the resource manager at detach time.
     // Zero textures are held by the Context, and we don't attempt to request them from
     // the State.
     mState.detachTexture(mZeroTextures, texture);
 }
 
 void Context::detachBuffer(GLuint buffer)
 {
-    // Buffer detachment is handled by Context, because the buffer must also be
-    // attached from any VAOs in existence, and Context holds the VAO map.
-
-    // [OpenGL ES 2.0.24] section 2.9 page 22:
-    // If a buffer object is deleted while it is bound, all bindings to that object in the current context
-    // (i.e. in the thread that called Delete-Buffers) are reset to zero.
+    // Simple pass-through to State's detachBuffer method, since
+    // only buffer attachments to container objects that are bound to the current context
+    // should be detached. And all those are available in State.
 
-    mState.removeArrayBufferBinding(buffer);
-
-    // mark as freed among the vertex array objects
-    for (auto &vaoPair : mVertexArrayMap)
-    {
-        vaoPair.second->detachBuffer(buffer);
-    }
+    // [OpenGL ES 3.2] section 5.1.2 page 45:
+    // Attachments to unbound container objects, such as
+    // deletion of a buffer attached to a vertex array object which is not bound to the context,
+    // are not affected and continue to act as references on the deleted object
+    mState.detachBuffer(buffer);
 }
 
 void Context::detachFramebuffer(GLuint framebuffer)
 {
     // Framebuffer detachment is handled by Context, because 0 is a valid
     // Framebuffer object, and a pointer to it must be passed from Context
     // to State at binding time.
 
--- a/gfx/angle/src/libANGLE/Context.h
+++ b/gfx/angle/src/libANGLE/Context.h
@@ -148,16 +148,19 @@ class Context final : public ValidationC
 
     Texture *getTargetTexture(GLenum target) const;
     Texture *getSamplerTexture(unsigned int sampler, GLenum type) const;
 
     Compiler *getCompiler() const;
 
     bool isSampler(GLuint samplerName) const;
 
+    bool isVertexArrayGenerated(GLuint vertexArray);
+    bool isTransformFeedbackGenerated(GLuint vertexArray);
+
     void getBooleanv(GLenum pname, GLboolean *params);
     void getFloatv(GLenum pname, GLfloat *params);
     void getIntegerv(GLenum pname, GLint *params);
     void getInteger64v(GLenum pname, GLint64 *params);
 
     bool getIndexedIntegerv(GLenum target, GLuint index, GLint *data);
     bool getIndexedInteger64v(GLenum target, GLuint index, GLint64 *data);
 
@@ -212,16 +215,19 @@ class Context final : public ValidationC
     rx::Renderer *getRenderer() { return mRenderer; }
 
     State &getState() { return mState; }
 
     void syncRendererState();
     void syncRendererState(const State::DirtyBits &bitMask);
 
   private:
+    void checkVertexArrayAllocation(GLuint vertexArray);
+    void checkTransformFeedbackAllocation(GLuint transformFeedback);
+
     void detachBuffer(GLuint buffer);
     void detachTexture(GLuint texture);
     void detachFramebuffer(GLuint framebuffer);
     void detachRenderbuffer(GLuint renderbuffer);
     void detachVertexArray(GLuint vertexArray);
     void detachTransformFeedback(GLuint transformFeedback);
     void detachSampler(GLuint sampler);
 
@@ -260,17 +266,16 @@ class Context final : public ValidationC
     typedef std::map<GLuint, Query*> QueryMap;
     QueryMap mQueryMap;
     HandleAllocator mQueryHandleAllocator;
 
     typedef std::map<GLuint, VertexArray*> VertexArrayMap;
     VertexArrayMap mVertexArrayMap;
     HandleAllocator mVertexArrayHandleAllocator;
 
-    BindingPointer<TransformFeedback> mTransformFeedbackZero;
     typedef std::map<GLuint, TransformFeedback*> TransformFeedbackMap;
     TransformFeedbackMap mTransformFeedbackMap;
     HandleAllocator mTransformFeedbackAllocator;
 
     std::string mRendererString;
     std::string mExtensionString;
     std::vector<std::string> mExtensionStrings;
 
--- a/gfx/angle/src/libANGLE/Device.cpp
+++ b/gfx/angle/src/libANGLE/Device.cpp
@@ -13,39 +13,94 @@
 
 #include <platform/Platform.h>
 #include <EGL/eglext.h>
 
 #include "common/debug.h"
 #include "common/platform.h"
 #include "libANGLE/renderer/DeviceImpl.h"
 
+#if defined(ANGLE_ENABLE_D3D11)
+#include "libANGLE/renderer/d3d/DeviceD3D.h"
+#endif
+
 namespace egl
 {
 
 template <typename T>
 static std::string GenerateExtensionsString(const T &extensions)
 {
     std::vector<std::string> extensionsVector = extensions.getStrings();
 
     std::ostringstream stream;
     std::copy(extensionsVector.begin(), extensionsVector.end(), std::ostream_iterator<std::string>(stream, " "));
     return stream.str();
 }
 
-Device::Device(Display *display, rx::DeviceImpl *impl)
-    : mDisplay(display),
-      mImplementation(impl)
+typedef std::set<egl::Device *> DeviceSet;
+static DeviceSet *GetDeviceSet()
+{
+    static DeviceSet devices;
+    return &devices;
+}
+
+// Static factory methods
+egl::Error Device::CreateDevice(void *devicePointer, EGLint deviceType, Device **outDevice)
+{
+#if defined(ANGLE_ENABLE_D3D11)
+    if (deviceType == EGL_D3D11_DEVICE_ANGLE)
+    {
+        rx::DeviceD3D *deviceD3D = new rx::DeviceD3D();
+        egl::Error error = deviceD3D->initialize(devicePointer, deviceType, EGL_TRUE);
+        if (error.isError())
+        {
+            *outDevice = nullptr;
+            return error;
+        }
+
+        *outDevice = new Device(nullptr, deviceD3D);
+        GetDeviceSet()->insert(*outDevice);
+        return egl::Error(EGL_SUCCESS);
+    }
+#endif
+
+    // Note that creating an EGL device from inputted D3D9 parameters isn't currently supported
+    *outDevice = nullptr;
+    return egl::Error(EGL_BAD_ATTRIBUTE);
+}
+
+egl::Error Device::CreateDevice(Display *owningDisplay, rx::DeviceImpl *impl, Device **outDevice)
+{
+    *outDevice = new Device(owningDisplay, impl);
+    GetDeviceSet()->insert(*outDevice);
+    return egl::Error(EGL_SUCCESS);
+}
+
+bool Device::IsValidDevice(Device *device)
+{
+    const DeviceSet *deviceSet = GetDeviceSet();
+    return deviceSet->find(device) != deviceSet->end();
+}
+
+Device::Device(Display *owningDisplay, rx::DeviceImpl *impl)
+    : mOwningDisplay(owningDisplay), mImplementation(impl)
 {
     initDeviceExtensions();
 }
 
 Device::~Device()
 {
+    ASSERT(GetDeviceSet()->find(this) != GetDeviceSet()->end());
+    GetDeviceSet()->erase(this);
 
+    if (mImplementation->deviceExternallySourced())
+    {
+        // If the device isn't externally sourced then it is up to the renderer to delete the impl
+        SafeDelete(mImplementation);
+    }
 }
 
 Error Device::getDevice(EGLAttrib *value)
 {
     void *nativeDevice = nullptr;
     egl::Error error = getImplementation()->getDevice(&nativeDevice);
     *value = reinterpret_cast<EGLAttrib>(nativeDevice);
     return error;
--- a/gfx/angle/src/libANGLE/Device.h
+++ b/gfx/angle/src/libANGLE/Device.h
@@ -19,32 +19,39 @@ namespace rx
 class DeviceImpl;
 }
 
 namespace egl
 {
 class Device final : angle::NonCopyable
 {
   public:
-    Device(Display *display, rx::DeviceImpl *impl);
     virtual ~Device();
 
     Error getDevice(EGLAttrib *value);
-    Display *getDisplay() { return mDisplay; };
+    Display *getOwningDisplay() { return mOwningDisplay; };
     EGLint getType();
 
     const DeviceExtensions &getExtensions() const;
     const std::string &getExtensionString() const;
 
     rx::DeviceImpl *getImplementation() { return mImplementation; }
 
+    static egl::Error CreateDevice(void *devicePointer, EGLint deviceType, Device **outDevice);
+    static egl::Error CreateDevice(Display *owningDisplay,
+                                   rx::DeviceImpl *impl,
+                                   Device **outDevice);
+
+    static bool IsValidDevice(Device *device);
+
   private:
+    Device(Display *owningDisplay, rx::DeviceImpl *impl);
     void initDeviceExtensions();
 
-    Display *mDisplay;
+    Display *mOwningDisplay;
     rx::DeviceImpl *mImplementation;
 
     DeviceExtensions mDeviceExtensions;
     std::string mDeviceExtensionString;
 };
 
 }
 
--- a/gfx/angle/src/libANGLE/Display.cpp
+++ b/gfx/angle/src/libANGLE/Display.cpp
@@ -80,24 +80,62 @@ typedef std::map<EGLNativeWindowType, Su
 // Get a map of all EGL window surfaces to validate that no window has more than one EGL surface
 // associated with it.
 static WindowSurfaceMap *GetWindowSurfaces()
 {
     static WindowSurfaceMap windowSurfaces;
     return &windowSurfaces;
 }
 
-typedef std::map<EGLNativeDisplayType, Display*> DisplayMap;
-static DisplayMap *GetDisplayMap()
+typedef std::map<EGLNativeDisplayType, Display *> ANGLEPlatformDisplayMap;
+static ANGLEPlatformDisplayMap *GetANGLEPlatformDisplayMap()
 {
-    static DisplayMap displays;
+    static ANGLEPlatformDisplayMap displays;
+    return &displays;
+}
+
+typedef std::map<Device *, Display *> DevicePlatformDisplayMap;
+static DevicePlatformDisplayMap *GetDevicePlatformDisplayMap()
+{
+    static DevicePlatformDisplayMap displays;
     return &displays;
 }
 
-rx::DisplayImpl *CreateDisplayImpl(const AttributeMap &attribMap)
+rx::DisplayImpl *CreateDisplayFromDevice(Device *eglDevice)
+{
+    rx::DisplayImpl *impl = nullptr;
+
+    switch (eglDevice->getType())
+    {
+#if defined(ANGLE_ENABLE_D3D11)
+        case EGL_D3D11_DEVICE_ANGLE:
+            impl = new rx::DisplayD3D();
+            break;
+#endif
+#if defined(ANGLE_ENABLE_D3D9)
+        case EGL_D3D9_DEVICE_ANGLE:
+            // Currently the only way to get EGLDeviceEXT representing a D3D9 device
+            // is to retrieve one from an already-existing EGLDisplay.
+            // When eglGetPlatformDisplayEXT is called with a D3D9 EGLDeviceEXT,
+            // the already-existing display should be returned.
+            // Therefore this codepath to create a new display from the device
+            // should never be hit.
+            UNREACHABLE();
+            break;
+#endif
+        default:
+            UNREACHABLE();
+            break;
+    }
+
+    ASSERT(impl != nullptr);
+    return impl;
+}
+
+rx::DisplayImpl *CreateDisplayFromAttribs(const AttributeMap &attribMap)
 {
     rx::DisplayImpl *impl = nullptr;
     EGLint displayType = attribMap.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE);
     switch (displayType)
     {
       case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE:
 #if defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11)
         // Default to D3D displays
@@ -144,76 +182,145 @@ rx::DisplayImpl *CreateDisplayImpl(const
     }
 
     ASSERT(impl != nullptr);
     return impl;
 }
 
 }
 
-Display *Display::getDisplay(EGLNativeDisplayType displayId, const AttributeMap &attribMap)
+Display *Display::GetDisplayFromAttribs(void *native_display, const AttributeMap &attribMap)
 {
     // Initialize the global platform if not already
     InitDefaultPlatformImpl();
 
-    Display *display = NULL;
+    Display *display = nullptr;
 
-    DisplayMap *displays = GetDisplayMap();
-    DisplayMap::const_iterator iter = displays->find(displayId);
+    EGLNativeDisplayType displayId = static_cast<EGLNativeDisplayType>(native_display);
+
+    ANGLEPlatformDisplayMap *displays            = GetANGLEPlatformDisplayMap();
+    ANGLEPlatformDisplayMap::const_iterator iter = displays->find(displayId);
     if (iter != displays->end())
     {
         display = iter->second;
     }
 
     if (display == nullptr)
     {
         // Validate the native display
         if (!Display::isValidNativeDisplay(displayId))
         {
             return NULL;
         }
 
-        display = new Display(displayId);
+        display = new Display(EGL_PLATFORM_ANGLE_ANGLE, displayId, nullptr);
         displays->insert(std::make_pair(displayId, display));
     }
 
     // Apply new attributes if the display is not initialized yet.
     if (!display->isInitialized())
     {
-        rx::DisplayImpl* impl = CreateDisplayImpl(attribMap);
+        rx::DisplayImpl *impl = CreateDisplayFromAttribs(attribMap);
         display->setAttributes(impl, attribMap);
     }
 
     return display;
 }
 
-Display::Display(EGLNativeDisplayType displayId)
+Display *Display::GetDisplayFromDevice(void *native_display)
+{
+    // Initialize the global platform if not already
+    InitDefaultPlatformImpl();
+
+    Display *display = nullptr;
+
+    Device *eglDevice = reinterpret_cast<Device *>(native_display);
+    ASSERT(Device::IsValidDevice(eglDevice));
+
+    ANGLEPlatformDisplayMap *anglePlatformDisplays   = GetANGLEPlatformDisplayMap();
+    DevicePlatformDisplayMap *devicePlatformDisplays = GetDevicePlatformDisplayMap();
+
+    // First see if this eglDevice is in use by a Display created using ANGLE platform
+    for (auto &displayMapEntry : *anglePlatformDisplays)
+    {
+        egl::Display *iterDisplay = displayMapEntry.second;
+        if (iterDisplay->getDevice() == eglDevice)
+        {
+            display = iterDisplay;
+        }
+    }
+
+    if (display == nullptr)
+    {
+        // See if the eglDevice is in use by a Display created using the DEVICE platform
+        DevicePlatformDisplayMap::const_iterator iter = devicePlatformDisplays->find(eglDevice);
+        if (iter != devicePlatformDisplays->end())
+        {
+            display = iter->second;
+        }
+    }
+
+    if (display == nullptr)
+    {
+        // Otherwise create a new Display
+        display = new Display(EGL_PLATFORM_DEVICE_EXT, 0, eglDevice);
+        devicePlatformDisplays->insert(std::make_pair(eglDevice, display));
+    }
+
+    // Apply new attributes if the display is not initialized yet.
+    if (!display->isInitialized())
+    {
+        rx::DisplayImpl *impl = CreateDisplayFromDevice(eglDevice);
+        display->setAttributes(impl, egl::AttributeMap());
+    }
+
+    return display;
+}
+
+Display::Display(EGLenum platform, EGLNativeDisplayType displayId, Device *eglDevice)
     : mImplementation(nullptr),
       mDisplayId(displayId),
       mAttributeMap(),
       mConfigSet(),
       mContextSet(),
       mInitialized(false),
       mCaps(),
       mDisplayExtensions(),
       mDisplayExtensionString(),
       mVendorString(),
-      mDevice(nullptr)
+      mDevice(eglDevice),
+      mPlatform(platform)
 {
 }
 
 Display::~Display()
 {
     terminate();
 
-    DisplayMap *displays = GetDisplayMap();
-    DisplayMap::iterator iter = displays->find(mDisplayId);
-    if (iter != displays->end())
+    if (mPlatform == EGL_PLATFORM_ANGLE_ANGLE)
+    {
+        ANGLEPlatformDisplayMap *displays      = GetANGLEPlatformDisplayMap();
+        ANGLEPlatformDisplayMap::iterator iter = displays->find(mDisplayId);
+        if (iter != displays->end())
+        {
+            displays->erase(iter);
+        }
+    }
+    else if (mPlatform == EGL_PLATFORM_DEVICE_EXT)
     {
-        displays->erase(iter);
+        DevicePlatformDisplayMap *displays      = GetDevicePlatformDisplayMap();
+        DevicePlatformDisplayMap::iterator iter = displays->find(mDevice);
+        if (iter != displays->end())
+        {
+            displays->erase(iter);
+        }
+    }
+    else
+    {
+        UNREACHABLE();
     }
 
     SafeDelete(mDevice);
     SafeDelete(mImplementation);
 }
 
 void Display::setAttributes(rx::DisplayImpl *impl, const AttributeMap &attribMap)
 {
@@ -259,29 +366,44 @@ Error Display::initialize()
     {
         mImplementation->terminate();
         return Error(EGL_NOT_INITIALIZED);
     }
 
     initDisplayExtensions();
     initVendorString();
 
-    if (mDisplayExtensions.deviceQuery)
+    // Populate the Display's EGLDeviceEXT if the Display wasn't created using one
+    if (mPlatform != EGL_PLATFORM_DEVICE_EXT)
     {
-        rx::DeviceImpl *impl = nullptr;
-        error = mImplementation->getDevice(&impl);
-        if (error.isError())
+        if (mDisplayExtensions.deviceQuery)
         {
-            return error;
+            rx::DeviceImpl *impl = nullptr;
+            error = mImplementation->getDevice(&impl);
+            if (error.isError())
+            {
+                return error;
+            }
+
+            error = Device::CreateDevice(this, impl, &mDevice);
+            if (error.isError())
+            {
+                return error;
+            }
         }
-        mDevice = new Device(this, impl);
+        else
+        {
+            mDevice = nullptr;
+        }
     }
     else
     {
-        mDevice = nullptr;
+        // For EGL_PLATFORM_DEVICE_EXT, mDevice should always be populated using
+        // an external device
+        ASSERT(mDevice != nullptr);
     }
 
     mInitialized = true;
 
     return Error(EGL_SUCCESS);
 }
 
 void Display::terminate()
@@ -293,18 +415,30 @@ void Display::terminate()
         destroyContext(*mContextSet.begin());
     }
 
     while (!mImageSet.empty())
     {
         destroyImage(*mImageSet.begin());
     }
 
+    while (!mImplementation->getSurfaceSet().empty())
+    {
+        destroySurface(*mImplementation->getSurfaceSet().begin());
+    }
+
     mConfigSet.clear();
 
+    if (mDevice != nullptr && mDevice->getOwningDisplay() != nullptr)
+    {
+        // Don't delete the device if it was created externally using eglCreateDeviceANGLE
+        // We also shouldn't set it to null in case eglInitialize() is called again later
+        SafeDelete(mDevice);
+    }
+
     mImplementation->terminate();
 
     mInitialized = false;
 
     // Never de-init default platform.. terminate is not that final.
 }
 
 std::vector<const Config*> Display::getConfigs(const egl::AttributeMap &attribs) const
@@ -700,22 +834,32 @@ static ClientExtensions GenerateClientEx
     ClientExtensions extensions;
 
     extensions.clientExtensions = true;
     extensions.platformBase = true;
     extensions.platformANGLE = true;
 
 #if defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11)
     extensions.platformANGLED3D = true;
+    extensions.platformDevice   = true;
 #endif
 
 #if defined(ANGLE_ENABLE_OPENGL)
     extensions.platformANGLEOpenGL = true;
 #endif
 
+#if defined(ANGLE_ENABLE_D3D11)
+    extensions.deviceCreation      = true;
+    extensions.deviceCreationD3D11 = true;
+#endif
+
+#if defined(ANGLE_USE_X11)
+    extensions.x11Visual = true;
+#endif
+
     extensions.clientGetAllProcAddresses = true;
 
     return extensions;
 }
 
 template <typename T>
 static std::string GenerateExtensionsString(const T &extensions)
 {
@@ -750,18 +894,27 @@ void Display::initDisplayExtensions()
 
 bool Display::isValidNativeWindow(EGLNativeWindowType window) const
 {
     return mImplementation->isValidNativeWindow(window);
 }
 
 bool Display::isValidDisplay(const egl::Display *display)
 {
-    const DisplayMap *displayMap = GetDisplayMap();
-    for (const auto &displayPair : *displayMap)
+    const ANGLEPlatformDisplayMap *anglePlatformDisplayMap = GetANGLEPlatformDisplayMap();
+    for (const auto &displayPair : *anglePlatformDisplayMap)
+    {
+        if (displayPair.second == display)
+        {
+            return true;
+        }
+    }
+
+    const DevicePlatformDisplayMap *devicePlatformDisplayMap = GetDevicePlatformDisplayMap();
+    for (const auto &displayPair : *devicePlatformDisplayMap)
     {
         if (displayPair.second == display)
         {
             return true;
         }
     }
 
     return false;
--- a/gfx/angle/src/libANGLE/Display.h
+++ b/gfx/angle/src/libANGLE/Display.h
@@ -39,17 +39,18 @@ class Surface;
 class Display final : angle::NonCopyable
 {
   public:
     ~Display();
 
     Error initialize();
     void terminate();
 
-    static egl::Display *getDisplay(EGLNativeDisplayType displayId, const AttributeMap &attribMap);
+    static egl::Display *GetDisplayFromDevice(void *native_display);
+    static egl::Display *GetDisplayFromAttribs(void *native_display, const AttributeMap &attribMap);
 
     static const ClientExtensions &getClientExtensions();
     static const std::string &getClientExtensionString();
 
     std::vector<const Config*> getConfigs(const egl::AttributeMap &attribs) const;
     bool getConfigAttrib(const Config *configuration, EGLint attribute, EGLint *value);
 
     Error createWindowSurface(const Config *configuration, EGLNativeWindowType window, const AttributeMap &attribs,
@@ -96,19 +97,20 @@ class Display final : angle::NonCopyable
     const std::string &getExtensionString() const;
     const std::string &getVendorString() const;
 
     const AttributeMap &getAttributeMap() const { return mAttributeMap; }
     EGLNativeDisplayType getNativeDisplayId() const { return mDisplayId; }
 
     rx::DisplayImpl *getImplementation() { return mImplementation; }
     Device *getDevice() const;
+    EGLenum getPlatform() const { return mPlatform; }
 
   private:
-    Display(EGLNativeDisplayType displayId);
+    Display(EGLenum platform, EGLNativeDisplayType displayId, Device *eglDevice);
 
     void setAttributes(rx::DisplayImpl *impl, const AttributeMap &attribMap);
 
     Error restoreLostDevice();
 
     void initDisplayExtensions();
     void initVendorString();
 
@@ -130,13 +132,14 @@ class Display final : angle::NonCopyable
     Caps mCaps;
 
     DisplayExtensions mDisplayExtensions;
     std::string mDisplayExtensionString;
 
     std::string mVendorString;
 
     Device *mDevice;
+    EGLenum mPlatform;
 };
 
 }
 
 #endif   // LIBANGLE_DISPLAY_H_
--- a/gfx/angle/src/libANGLE/Framebuffer.cpp
+++ b/gfx/angle/src/libANGLE/Framebuffer.cpp
@@ -4,16 +4,17 @@
 // found in the LICENSE file.
 //
 
 // Framebuffer.cpp: Implements the gl::Framebuffer class. Implements GL framebuffer
 // objects and related functionality. [OpenGL ES 2.0.24] section 4.4 page 105.
 
 #include "libANGLE/Framebuffer.h"
 
+#include "common/Optional.h"
 #include "common/utilities.h"
 #include "libANGLE/Config.h"
 #include "libANGLE/Context.h"
 #include "libANGLE/FramebufferAttachment.h"
 #include "libANGLE/Renderbuffer.h"
 #include "libANGLE/Surface.h"
 #include "libANGLE/Texture.h"
 #include "libANGLE/formatutils.h"
@@ -119,16 +120,52 @@ const FramebufferAttachment *Framebuffer
         mDepthAttachment.id() == mStencilAttachment.id())
     {
         return &mDepthAttachment;
     }
 
     return nullptr;
 }
 
+bool Framebuffer::Data::attachmentsHaveSameDimensions() const
+{
+    Optional<Extents> attachmentSize;
+
+    auto hasMismatchedSize = [&attachmentSize](const FramebufferAttachment &attachment)
+    {
+        if (!attachment.isAttached())
+        {
+            return false;
+        }
+
+        if (!attachmentSize.valid())
+        {
+            attachmentSize = attachment.getSize();
+            return false;
+        }
+
+        return (attachment.getSize() != attachmentSize.value());
+    };
+
+    for (const auto &attachment : mColorAttachments)
+    {
+        if (hasMismatchedSize(attachment))
+        {
+            return false;
+        }
+    }
+
+    if (hasMismatchedSize(mDepthAttachment))
+    {
+        return false;
+    }
+
+    return !hasMismatchedSize(mStencilAttachment);
+}
+
 Framebuffer::Framebuffer(const Caps &caps, rx::ImplFactory *factory, GLuint id)
     : mData(caps), mImpl(factory->createFramebuffer(mData)), mId(id)
 {
     ASSERT(mId != 0);
     ASSERT(mImpl != nullptr);
 }
 
 Framebuffer::Framebuffer(rx::SurfaceImpl *surface)
@@ -283,16 +320,21 @@ bool Framebuffer::hasEnabledColorAttachm
     return false;
 }
 
 size_t Framebuffer::getNumColorBuffers() const
 {
     return mData.mColorAttachments.size();
 }
 
+bool Framebuffer::hasDepth() const
+{
+    return (mData.mDepthAttachment.isAttached() && mData.mDepthAttachment.getDepthSize() > 0);
+}
+
 bool Framebuffer::hasStencil() const
 {
     return (mData.mStencilAttachment.isAttached() && mData.mStencilAttachment.getStencilSize() > 0);
 }
 
 bool Framebuffer::usingExtendedDrawBuffers() const
 {
     for (size_t colorAttachment = 1; colorAttachment < mData.mColorAttachments.size(); ++colorAttachment)
@@ -310,62 +352,60 @@ GLenum Framebuffer::checkStatus(const gl
 {
     // 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.
     if (mId == 0)
     {
         return GL_FRAMEBUFFER_COMPLETE;
     }
 
-    int width = 0;
-    int height = 0;
     unsigned int colorbufferSize = 0;
     int samples = -1;
     bool missingAttachment = true;
 
     for (const FramebufferAttachment &colorAttachment : mData.mColorAttachments)
     {
         if (colorAttachment.isAttached())
         {
-            if (colorAttachment.getWidth() == 0 || colorAttachment.getHeight() == 0)
+            const Extents &size = colorAttachment.getSize();
+            if (size.width == 0 || size.height == 0)
             {
                 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
             }
 
             GLenum internalformat = colorAttachment.getInternalFormat();
             const TextureCaps &formatCaps = data.textureCaps->get(internalformat);
             const InternalFormat &formatInfo = GetInternalFormatInfo(internalformat);
             if (colorAttachment.type() == GL_TEXTURE)
             {
                 if (!formatCaps.renderable)
                 {
-                    return GL_FRAMEBUFFER_UNSUPPORTED;
+                    return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
                 }
 
                 if (formatInfo.depthBits > 0 || formatInfo.stencilBits > 0)
                 {
                     return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
                 }
+
+                if (colorAttachment.layer() >= size.depth)
+                {
+                    return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
+                }
             }
             else if (colorAttachment.type() == GL_RENDERBUFFER)
             {
                 if (!formatCaps.renderable || formatInfo.depthBits > 0 || formatInfo.stencilBits > 0)
                 {
                     return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
                 }
             }
 
             if (!missingAttachment)
             {
-                // all color attachments must have the same width and height
-                if (colorAttachment.getWidth() != width || colorAttachment.getHeight() != height)
-                {
-                    return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
-                }
-
                 // APPLE_framebuffer_multisample, which EXT_draw_buffers refers to, requires that
                 // all color attachments have the same number of samples for the FBO to be complete.
                 if (colorAttachment.getSamples() != samples)
                 {
                     return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT;
                 }
 
                 // in GLES 2.0, all color attachments attachments must have the same number of bitplanes
@@ -375,29 +415,28 @@ GLenum Framebuffer::checkStatus(const gl
                     if (formatInfo.pixelBytes != colorbufferSize)
                     {
                         return GL_FRAMEBUFFER_UNSUPPORTED;
                     }
                 }
             }
             else
             {
-                width = colorAttachment.getWidth();
-                height = colorAttachment.getHeight();
                 samples = colorAttachment.getSamples();
                 colorbufferSize = formatInfo.pixelBytes;
                 missingAttachment = false;
             }
         }
     }
 
     const FramebufferAttachment &depthAttachment = mData.mDepthAttachment;
     if (depthAttachment.isAttached())
     {
-        if (depthAttachment.getWidth() == 0 || depthAttachment.getHeight() == 0)
+        const Extents &size = depthAttachment.getSize();
+        if (size.width == 0 || size.height == 0)
         {
             return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
         }
 
         GLenum internalformat = depthAttachment.getInternalFormat();
         const TextureCaps &formatCaps = data.textureCaps->get(internalformat);
         const InternalFormat &formatInfo = GetInternalFormatInfo(internalformat);
         if (depthAttachment.type() == GL_TEXTURE)
@@ -405,17 +444,17 @@ GLenum Framebuffer::checkStatus(const gl
             // depth texture attachments require OES/ANGLE_depth_texture
             if (!data.extensions->depthTextures)
             {
                 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
             }
 
             if (!formatCaps.renderable)
             {
-                return GL_FRAMEBUFFER_UNSUPPORTED;
+                return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
             }
 
             if (formatInfo.depthBits == 0)
             {
                 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
             }
         }
         else if (depthAttachment.type() == GL_RENDERBUFFER)
@@ -423,35 +462,30 @@ GLenum Framebuffer::checkStatus(const gl
             if (!formatCaps.renderable || formatInfo.depthBits == 0)
             {
                 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
             }
         }
 
         if (missingAttachment)
         {
-            width = depthAttachment.getWidth();
-            height = depthAttachment.getHeight();
             samples = depthAttachment.getSamples();
             missingAttachment = false;
         }
-        else if (width != depthAttachment.getWidth() || height != depthAttachment.getHeight())
-        {
-            return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
-        }
         else if (samples != depthAttachment.getSamples())
         {
             return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_ANGLE;
         }
     }
 
     const FramebufferAttachment &stencilAttachment = mData.mStencilAttachment;
     if (stencilAttachment.isAttached())
     {
-        if (stencilAttachment.getWidth() == 0 || stencilAttachment.getHeight() == 0)
+        const Extents &size = stencilAttachment.getSize();
+        if (size.width == 0 || size.height == 0)
         {
             return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
         }
 
         GLenum internalformat = stencilAttachment.getInternalFormat();
         const TextureCaps &formatCaps = data.textureCaps->get(internalformat);
         const InternalFormat &formatInfo = GetInternalFormatInfo(internalformat);
         if (stencilAttachment.type() == GL_TEXTURE)
@@ -460,17 +494,17 @@ GLenum Framebuffer::checkStatus(const gl
             // of OES_packed_depth_stencil + OES/ANGLE_depth_texture
             if (!data.extensions->depthTextures)
             {
                 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
             }
 
             if (!formatCaps.renderable)
             {
-                return GL_FRAMEBUFFER_UNSUPPORTED;
+                return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
             }
 
             if (formatInfo.stencilBits == 0)
             {
                 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
             }
         }
         else if (stencilAttachment.type() == GL_RENDERBUFFER)
@@ -478,38 +512,44 @@ GLenum Framebuffer::checkStatus(const gl
             if (!formatCaps.renderable || formatInfo.stencilBits == 0)
             {
                 return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT;
             }
         }
 
         if (missingAttachment)
         {
-            width = stencilAttachment.getWidth();
-            height = stencilAttachment.getHeight();
             samples = stencilAttachment.getSamples();
             missingAttachment = false;
         }
-        else if (width != stencilAttachment.getWidth() || height != stencilAttachment.getHeight())
-        {
-            return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
-        }
         else if (samples != stencilAttachment.getSamples())
         {
             return GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_ANGLE;
         }
     }
 
     // we need to have at least one attachment to be complete
     if (missingAttachment)
     {
         return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
     }
 
-    return mImpl->checkStatus();
+    // In ES 2.0, all color attachments must have the same width and height.
+    // In ES 3.0, there is no such restriction.
+    if (data.clientVersion < 3 && !mData.attachmentsHaveSameDimensions())
+    {
+        return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS;
+    }
+
+    if (!mImpl->checkStatus())
+    {
+        return GL_FRAMEBUFFER_UNSUPPORTED;
+    }
+
+    return GL_FRAMEBUFFER_COMPLETE;
 }
 
 Error Framebuffer::discard(size_t count, const GLenum *attachments)
 {
     return mImpl->discard(count, attachments);
 }
 
 Error Framebuffer::invalidate(size_t count, const GLenum *attachments)
@@ -519,65 +559,89 @@ Error Framebuffer::invalidate(size_t cou
 
 Error Framebuffer::invalidateSub(size_t count, const GLenum *attachments, const gl::Rectangle &area)
 {
     return mImpl->invalidateSub(count, attachments, area);
 }
 
 Error Framebuffer::clear(Context *context, GLbitfield mask)
 {
+    if (context->getState().isRasterizerDiscardEnabled())
+    {
+        return gl::Error(GL_NO_ERROR);
+    }
+
     // Sync the clear state
     context->syncRendererState(context->getState().clearStateBitMask());
 
     return mImpl->clear(context->getData(), mask);
 }
 
 Error Framebuffer::clearBufferfv(Context *context,
                                  GLenum buffer,
                                  GLint drawbuffer,
                                  const GLfloat *values)
 {
+    if (context->getState().isRasterizerDiscardEnabled())
+    {
+        return gl::Error(GL_NO_ERROR);
+    }
+
     // Sync the clear state
     context->syncRendererState(context->getState().clearStateBitMask());
-
-    return mImpl->clearBufferfv(context->getState(), buffer, drawbuffer, values);
+    return mImpl->clearBufferfv(context->getData(), buffer, drawbuffer, values);
 }
 
 Error Framebuffer::clearBufferuiv(Context *context,
                                   GLenum buffer,
                                   GLint drawbuffer,
                                   const GLuint *values)
 {
+    if (context->getState().isRasterizerDiscardEnabled())
+    {
+        return gl::Error(GL_NO_ERROR);
+    }
+
     // Sync the clear state
     context->syncRendererState(context->getState().clearStateBitMask());
 
-    return mImpl->clearBufferuiv(context->getState(), buffer, drawbuffer, values);
+    return mImpl->clearBufferuiv(context->getData(), buffer, drawbuffer, values);
 }
 
 Error Framebuffer::clearBufferiv(Context *context,
                                  GLenum buffer,
                                  GLint drawbuffer,
                                  const GLint *values)
 {
+    if (context->getState().isRasterizerDiscardEnabled())
+    {
+        return gl::Error(GL_NO_ERROR);
+    }
+
     // Sync the clear state
     context->syncRendererState(context->getState().clearStateBitMask());
 
-    return mImpl->clearBufferiv(context->getState(), buffer, drawbuffer, values);
+    return mImpl->clearBufferiv(context->getData(), buffer, drawbuffer, values);
 }
 
 Error Framebuffer::clearBufferfi(Context *context,
                                  GLenum buffer,
                                  GLint drawbuffer,
                                  GLfloat depth,
                                  GLint stencil)
 {
+    if (context->getState().isRasterizerDiscardEnabled())
+    {
+        return gl::Error(GL_NO_ERROR);
+    }
+
     // Sync the clear state
     context->syncRendererState(context->getState().clearStateBitMask());
 
-    return mImpl->clearBufferfi(context->getState(), buffer, drawbuffer, depth, stencil);
+    return mImpl->clearBufferfi(context->getData(), buffer, drawbuffer, depth, stencil);
 }
 
 GLenum Framebuffer::getImplementationColorReadFormat() const
 {
     return mImpl->getImplementationColorReadFormat();
 }
 
 GLenum Framebuffer::getImplementationColorReadType() const
--- a/gfx/angle/src/libANGLE/Framebuffer.h
+++ b/gfx/angle/src/libANGLE/Framebuffer.h
@@ -61,16 +61,18 @@ class Framebuffer
         const FramebufferAttachment *getColorAttachment(size_t colorAttachment) const;
         const FramebufferAttachment *getDepthAttachment() const;
         const FramebufferAttachment *getStencilAttachment() const;
         const FramebufferAttachment *getDepthStencilAttachment() const;
 
         const std::vector<GLenum> &getDrawBufferStates() const { return mDrawBufferStates; }
         const std::vector<FramebufferAttachment> &getColorAttachments() const { return mColorAttachments; }
 
+        bool attachmentsHaveSameDimensions() const;
+
       private:
         friend class Framebuffer;
 
         std::vector<FramebufferAttachment> mColorAttachments;
         FramebufferAttachment mDepthAttachment;
         FramebufferAttachment mStencilAttachment;
 
         std::vector<GLenum> mDrawBufferStates;
@@ -110,16 +112,17 @@ class Framebuffer
     void setDrawBuffers(size_t count, const GLenum *buffers);
 
     GLenum getReadBufferState() const;
     void setReadBuffer(GLenum buffer);
 
     bool isEnabledColorAttachment(size_t colorAttachment) const;
     bool hasEnabledColorAttachment() const;
     size_t getNumColorBuffers() const;
+    bool hasDepth() const;
     bool hasStencil() const;
     int getSamples(const gl::Data &data) const;
     bool usingExtendedDrawBuffers() const;
 
     GLenum checkStatus(const gl::Data &data) const;
     bool hasValidDepthStencil() const;
 
     Error discard(size_t count, const GLenum *attachments);
--- a/gfx/angle/src/libANGLE/FramebufferAttachment.h
+++ b/gfx/angle/src/libANGLE/FramebufferAttachment.h
@@ -7,16 +7,17 @@
 // FramebufferAttachment.h: Defines the wrapper class gl::FramebufferAttachment, as well as the
 // objects and related functionality. [OpenGL ES 2.0.24] section 4.4.3 page 108.
 
 #ifndef LIBANGLE_FRAMEBUFFERATTACHMENT_H_
 #define LIBANGLE_FRAMEBUFFERATTACHMENT_H_
 
 #include "angle_gl.h"
 #include "common/angleutils.h"
+#include "libANGLE/angletypes.h"
 #include "libANGLE/Error.h"
 #include "libANGLE/ImageIndex.h"
 
 namespace egl
 {
 class Surface;
 }
 
@@ -106,18 +107,20 @@ class FramebufferAttachment final
     GLuint id() const;
 
     // These methods are only legal to call on Texture attachments
     const ImageIndex &getTextureImageIndex() const;
     GLenum cubeMapFace() const;
     GLint mipLevel() const;
     GLint layer() const;
 
-    GLsizei getWidth() const;
-    GLsizei getHeight() const;
+    // The size of the underlying resource the attachment points to. The 'depth' value will
+    // correspond to a 3D texture depth or the layer count of a 2D array texture. For Surfaces and
+    // Renderbuffers, it will always be 1.
+    Extents getSize() const;
     GLenum getInternalFormat() const;
     GLsizei getSamples() const;
     GLenum type() const { return mType; }
     bool isAttached() const { return mType != GL_NONE; }
 
     Renderbuffer *getRenderbuffer() const;
     Texture *getTexture() const;
     const egl::Surface *getSurface() const;
@@ -143,40 +146,34 @@ class FramebufferAttachment final
 
 // A base class for objects that FBO Attachments may point to.
 class FramebufferAttachmentObject
 {
   public:
     FramebufferAttachmentObject() {}
     virtual ~FramebufferAttachmentObject() {}
 
-    virtual GLsizei getAttachmentWidth(const FramebufferAttachment::Target &target) const = 0;
-    virtual GLsizei getAttachmentHeight(const FramebufferAttachment::Target &target) const = 0;
+    virtual Extents getAttachmentSize(const FramebufferAttachment::Target &target) const = 0;
     virtual GLenum getAttachmentInternalFormat(const FramebufferAttachment::Target &target) const = 0;
     virtual GLsizei getAttachmentSamples(const FramebufferAttachment::Target &target) const = 0;
 
     virtual void onAttach() = 0;
     virtual void onDetach() = 0;
     virtual GLuint getId() const = 0;
 
     Error getAttachmentRenderTarget(const FramebufferAttachment::Target &target,
                                     rx::FramebufferAttachmentRenderTarget **rtOut) const;
 
   protected:
     virtual rx::FramebufferAttachmentObjectImpl *getAttachmentImpl() const = 0;
 };
 
-inline GLsizei FramebufferAttachment::getWidth() const
+inline Extents FramebufferAttachment::getSize() const
 {
-    return mResource->getAttachmentWidth(mTarget);
-}
-
-inline GLsizei FramebufferAttachment::getHeight() const
-{
-    return mResource->getAttachmentHeight(mTarget);
+    return mResource->getAttachmentSize(mTarget);
 }
 
 inline GLenum FramebufferAttachment::getInternalFormat() const
 {
     return mResource->getAttachmentInternalFormat(mTarget);
 }
 
 inline GLsizei FramebufferAttachment::getSamples() const
--- a/gfx/angle/src/libANGLE/Program.cpp
+++ b/gfx/angle/src/libANGLE/Program.cpp
@@ -220,17 +220,18 @@ VariableLocation::VariableLocation(const
       element(element),
       index(index)
 {
 }
 
 Program::Data::Data()
     : mAttachedFragmentShader(nullptr),
       mAttachedVertexShader(nullptr),
-      mTransformFeedbackBufferMode(GL_INTERLEAVED_ATTRIBS)
+      mTransformFeedbackBufferMode(GL_INTERLEAVED_ATTRIBS),
+      mBinaryRetrieveableHint(false)
 {
 }
 
 Program::Data::~Data()
 {
     if (mAttachedVertexShader != nullptr)
     {
         mAttachedVertexShader->release();
@@ -786,16 +787,28 @@ GLint Program::getBinaryLength() const
     if (error.isError())
     {
         return 0;
     }
 
     return length;
 }
 
+void Program::setBinaryRetrievableHint(bool retrievable)
+{
+    // TODO(jmadill) : replace with dirty bits
+    mProgram->setBinaryRetrievableHint(retrievable);
+    mData.mBinaryRetrieveableHint = retrievable;
+}
+
+bool Program::getBinaryRetrievableHint() const
+{
+    return mData.mBinaryRetrieveableHint;
+}
+
 void Program::release()
 {
     mRefCount--;
 
     if (mRefCount == 0 && mDeleteStatus)
     {
         mResourceManager->deleteProgram(mHandle);
     }
--- a/gfx/angle/src/libANGLE/Program.h
+++ b/gfx/angle/src/libANGLE/Program.h
@@ -210,16 +210,18 @@ class Program : angle::NonCopyable
         //  3. Uniform block uniforms
         // This makes sampler validation easier, since we don't need a separate list.
         std::vector<LinkedUniform> mUniforms;
         std::vector<VariableLocation> mUniformLocations;
         std::vector<UniformBlock> mUniformBlocks;
 
         // TODO(jmadill): use unordered/hash map when available
         std::map<int, VariableLocation> mOutputVariables;
+
+        bool mBinaryRetrieveableHint;
     };
 
     Program(rx::ImplFactory *factory, ResourceManager *manager, GLuint handle);
     ~Program();
 
     GLuint id() const { return mHandle; }
 
     rx::ProgramImpl *getImplementation() { return mProgram; }
@@ -232,16 +234,18 @@ class Program : angle::NonCopyable
     void bindAttributeLocation(GLuint index, const char *name);
 
     Error link(const gl::Data &data);
     bool isLinked() const;
 
     Error loadBinary(GLenum binaryFormat, const void *binary, GLsizei length);
     Error saveBinary(GLenum *binaryFormat, void *binary, GLsizei bufSize, GLsizei *length) const;
     GLint getBinaryLength() const;
+    void setBinaryRetrievableHint(bool retrievable);
+    bool getBinaryRetrievableHint() const;
 
     int getInfoLogLength() const;
     void getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog) const;
     void getAttachedShaders(GLsizei maxCount, GLsizei *count, GLuint *shaders) const;
 
     GLuint getAttributeLocation(const std::string &name) const;
     bool isAttribLocationActive(size_t attribLocation) const;
 
--- a/gfx/angle/src/libANGLE/Renderbuffer.cpp
+++ b/gfx/angle/src/libANGLE/Renderbuffer.cpp
@@ -160,9 +160,14 @@ void Renderbuffer::onDetach()
 {
     release();
 }
 
 GLuint Renderbuffer::getId() const
 {
     return id();
 }
+
+Extents Renderbuffer::getAttachmentSize(const FramebufferAttachment::Target & /*target*/) const
+{
+    return Extents(mWidth, mHeight, 1);
 }
+}
--- a/gfx/angle/src/libANGLE/Renderbuffer.h
+++ b/gfx/angle/src/libANGLE/Renderbuffer.h
@@ -45,18 +45,17 @@ class Renderbuffer : public egl::ImageSi
     GLuint getRedSize() const;
     GLuint getGreenSize() const;
     GLuint getBlueSize() const;
     GLuint getAlphaSize() const;
     GLuint getDepthSize() const;
     GLuint getStencilSize() const;
 
     // FramebufferAttachmentObject Impl
-    GLsizei getAttachmentWidth(const FramebufferAttachment::Target &/*target*/) const override { return getWidth(); }
-    GLsizei getAttachmentHeight(const FramebufferAttachment::Target &/*target*/) const override { return getHeight(); }
+    Extents getAttachmentSize(const FramebufferAttachment::Target &target) const override;
     GLenum getAttachmentInternalFormat(const FramebufferAttachment::Target &/*target*/) const override { return getInternalFormat(); }
     GLsizei getAttachmentSamples(const FramebufferAttachment::Target &/*target*/) const override { return getSamples(); }
 
     void onAttach() override;
     void onDetach() override;
     GLuint getId() const override;
 
   private:
--- a/gfx/angle/src/libANGLE/State.cpp
+++ b/gfx/angle/src/libANGLE/State.cpp
@@ -994,27 +994,16 @@ void State::setArrayBufferBinding(Buffer
     mArrayBuffer.set(buffer);
 }
 
 GLuint State::getArrayBufferId() const
 {
     return mArrayBuffer.id();
 }
 
-bool State::removeArrayBufferBinding(GLuint buffer)
-{
-    if (mArrayBuffer.id() == buffer)
-    {
-        mArrayBuffer.set(NULL);
-        return true;
-    }
-
-    return false;
-}
-
 void State::setGenericUniformBufferBinding(Buffer *buffer)
 {
     mGenericUniformBuffer.set(buffer);
 }
 
 void State::setIndexedUniformBufferBinding(GLuint index, Buffer *buffer, GLintptr offset, GLsizeiptr size)
 {
     mUniformBuffers[index].set(buffer, offset, size);
@@ -1057,16 +1046,38 @@ Buffer *State::getTargetBuffer(GLenum ta
       case GL_PIXEL_PACK_BUFFER:         return mPack.pixelBuffer.get();
       case GL_PIXEL_UNPACK_BUFFER:       return mUnpack.pixelBuffer.get();
       case GL_TRANSFORM_FEEDBACK_BUFFER: return mTransformFeedback->getGenericBuffer().get();
       case GL_UNIFORM_BUFFER:            return mGenericUniformBuffer.get();
       default: UNREACHABLE();            return NULL;
     }
 }
 
+void State::detachBuffer(GLuint bufferName)
+{
+    BindingPointer<Buffer> *buffers[] = {&mArrayBuffer,        &mCopyReadBuffer,
+                                         &mCopyWriteBuffer,    &mPack.pixelBuffer,
+                                         &mUnpack.pixelBuffer, &mGenericUniformBuffer};
+    for (auto buffer : buffers)
+    {
+        if (buffer->id() == bufferName)
+        {
+            buffer->set(nullptr);
+        }
+    }
+
+    TransformFeedback *curTransformFeedback = getCurrentTransformFeedback();
+    if (curTransformFeedback)
+    {
+        curTransformFeedback->detachBuffer(bufferName);
+    }
+
+    getVertexArray()->detachBuffer(bufferName);
+}
+
 void State::setEnableVertexAttribArray(unsigned int attribNum, bool enabled)
 {
     getVertexArray()->enableAttribute(attribNum, enabled);
     mDirtyBits.set(DIRTY_BIT_VERTEX_ARRAY_OBJECT);
 }
 
 void State::setVertexAttribf(GLuint index, const GLfloat values[4])
 {
@@ -1281,16 +1292,19 @@ void State::getBooleanv(GLenum pname, GL
       case GL_DEPTH_TEST:                *params = mDepthStencil.depthTest;       break;
       case GL_BLEND:                     *params = mBlend.blend;                  break;
       case GL_DITHER:                    *params = mBlend.dither;                 break;
       case GL_TRANSFORM_FEEDBACK_ACTIVE: *params = getCurrentTransformFeedback()->isActive() ? GL_TRUE : GL_FALSE; break;
       case GL_TRANSFORM_FEEDBACK_PAUSED: *params = getCurrentTransformFeedback()->isPaused() ? GL_TRUE : GL_FALSE; break;
       case GL_PRIMITIVE_RESTART_FIXED_INDEX:
           *params = mPrimitiveRestart;
           break;
+      case GL_RASTERIZER_DISCARD:
+          *params = isRasterizerDiscardEnabled() ? GL_TRUE : GL_FALSE;
+          break;
       default:
         UNREACHABLE();
         break;
     }
 }
 
 void State::getFloatv(GLenum pname, GLfloat *params)
 {
@@ -1521,17 +1535,17 @@ void State::getIntegerv(const gl::Data &
         ASSERT(mActiveSampler < mMaxCombinedTextureImageUnits);
         *params =
             getSamplerTextureId(static_cast<unsigned int>(mActiveSampler), GL_TEXTURE_2D_ARRAY);
         break;
       case GL_UNIFORM_BUFFER_BINDING:
         *params = mGenericUniformBuffer.id();
         break;
       case GL_TRANSFORM_FEEDBACK_BINDING:
-        *params = mTransformFeedback->id();
+        *params = mTransformFeedback.id();
         break;
       case GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
         *params = mTransformFeedback->getGenericBuffer().id();
         break;
       case GL_COPY_READ_BUFFER_BINDING:
         *params = mCopyReadBuffer.id();
         break;
       case GL_COPY_WRITE_BUFFER_BINDING:
--- a/gfx/angle/src/libANGLE/State.h
+++ b/gfx/angle/src/libANGLE/State.h
@@ -193,33 +193,34 @@ class State : angle::NonCopyable
     void setActiveQuery(GLenum target, Query *query);
     GLuint getActiveQueryId(GLenum target) const;
     Query *getActiveQuery(GLenum target) const;
 
     //// Typed buffer binding point manipulation ////
     // GL_ARRAY_BUFFER
     void setArrayBufferBinding(Buffer *buffer);
     GLuint getArrayBufferId() const;
-    bool removeArrayBufferBinding(GLuint buffer);
 
     // GL_UNIFORM_BUFFER - Both indexed and generic targets
     void setGenericUniformBufferBinding(Buffer *buffer);
     void setIndexedUniformBufferBinding(GLuint index, Buffer *buffer, GLintptr offset, GLsizeiptr size);
     const OffsetBindingPointer<Buffer> &getIndexedUniformBuffer(size_t index) const;
 
     // GL_COPY_[READ/WRITE]_BUFFER
     void setCopyReadBufferBinding(Buffer *buffer);
     void setCopyWriteBufferBinding(Buffer *buffer);
 
     // GL_PIXEL[PACK/UNPACK]_BUFFER
     void setPixelPackBufferBinding(Buffer *buffer);
     void setPixelUnpackBufferBinding(Buffer *buffer);
 
     // Retrieve typed buffer by target (non-indexed)
     Buffer *getTargetBuffer(GLenum target) const;
+    // Detach a buffer from all bindings
+    void detachBuffer(GLuint bufferName);
 
     // Vertex attrib manipulation
     void setEnableVertexAttribArray(unsigned int attribNum, bool enabled);
     void setVertexAttribf(GLuint index, const GLfloat values[4]);
     void setVertexAttribu(GLuint index, const GLuint values[4]);
     void setVertexAttribi(GLuint index, const GLint values[4]);
     void setVertexAttribState(unsigned int attribNum, Buffer *boundBuffer, GLint size, GLenum type,
                               bool normalized, bool pureInteger, GLsizei stride, const void *pointer);
--- a/gfx/angle/src/libANGLE/Surface.cpp
+++ b/gfx/angle/src/libANGLE/Surface.cpp
@@ -180,17 +180,17 @@ EGLint Surface::getHeight() const
 }
 
 Error Surface::bindTexImage(gl::Texture *texture, EGLint buffer)
 {
     ASSERT(!mTexture.get());
 
     texture->bindTexImageFromSurface(this);
     mTexture.set(texture);
-    return mImplementation->bindTexImage(buffer);
+    return mImplementation->bindTexImage(texture, buffer);
 }
 
 Error Surface::releaseTexImage(EGLint buffer)
 {
     ASSERT(mTexture.get());
     mTexture->releaseTexImageFromSurface();
     mTexture.set(nullptr);
 
@@ -198,16 +198,21 @@ Error Surface::releaseTexImage(EGLint bu
 }
 
 void Surface::releaseTexImageFromTexture()
 {
     ASSERT(mTexture.get());
     mTexture.set(nullptr);
 }
 
+gl::Extents Surface::getAttachmentSize(const gl::FramebufferAttachment::Target & /*target*/) const
+{
+    return gl::Extents(getWidth(), getHeight(), 1);
+}
+
 GLenum Surface::getAttachmentInternalFormat(const gl::FramebufferAttachment::Target &target) const
 {
     const egl::Config *config = getConfig();
     return (target.binding() == GL_BACK ? config->renderTargetFormat : config->depthStencilFormat);
 }
 
 GLsizei Surface::getAttachmentSamples(const gl::FramebufferAttachment::Target &target) const
 {
--- a/gfx/angle/src/libANGLE/Surface.h
+++ b/gfx/angle/src/libANGLE/Surface.h
@@ -65,18 +65,17 @@ class Surface final : public gl::Framebu
     EGLenum getTextureTarget() const;
 
     gl::Texture *getBoundTexture() const { return mTexture.get(); }
     gl::Framebuffer *getDefaultFramebuffer() { return mDefaultFramebuffer; }
 
     EGLint isFixedSize() const;
 
     // FramebufferAttachmentObject implementation
-    GLsizei getAttachmentWidth(const gl::FramebufferAttachment::Target &/*target*/) const override { return getWidth(); }
-    GLsizei getAttachmentHeight(const gl::FramebufferAttachment::Target &/*target*/) const override { return getHeight(); }
+    gl::Extents getAttachmentSize(const gl::FramebufferAttachment::Target &target) const override;
     GLenum getAttachmentInternalFormat(const gl::FramebufferAttachment::Target &target) const override;
     GLsizei getAttachmentSamples(const gl::FramebufferAttachment::Target &target) const override;
 
     void onAttach() override {}
     void onDetach() override {}
     GLuint getId() const override;
 
   private:
--- a/gfx/angle/src/libANGLE/Surface_unittest.cpp
+++ b/gfx/angle/src/libANGLE/Surface_unittest.cpp
@@ -26,17 +26,17 @@ class MockSurfaceImpl : public rx::Surfa
     virtual ~MockSurfaceImpl() { destroy(); }
 
     MOCK_METHOD0(initialize, egl::Error());
     MOCK_METHOD1(createDefaultFramebuffer,
                  rx::FramebufferImpl *(const gl::Framebuffer::Data &data));
     MOCK_METHOD0(swap, egl::Error());
     MOCK_METHOD4(postSubBuffer, egl::Error(EGLint, EGLint, EGLint, EGLint));
     MOCK_METHOD2(querySurfacePointerANGLE, egl::Error(EGLint, void**));
-    MOCK_METHOD1(bindTexImage, egl::Error(EGLint));
+    MOCK_METHOD2(bindTexImage, egl::Error(gl::Texture*, EGLint));
     MOCK_METHOD1(releaseTexImage, egl::Error(EGLint));
     MOCK_METHOD1(setSwapInterval, void(EGLint));
     MOCK_CONST_METHOD0(getWidth, EGLint());
     MOCK_CONST_METHOD0(getHeight, EGLint());
     MOCK_CONST_METHOD0(isPostSubBufferSupported, EGLint(void));
     MOCK_CONST_METHOD0(getSwapBehavior, EGLint(void));
     MOCK_METHOD2(getAttachmentRenderTarget, gl::Error(const gl::FramebufferAttachment::Target &, rx::FramebufferAttachmentRenderTarget **));
 
--- a/gfx/angle/src/libANGLE/Texture.cpp
+++ b/gfx/angle/src/libANGLE/Texture.cpp
@@ -834,26 +834,19 @@ Texture::SamplerCompletenessCache::Sampl
       samplerState(),
       filterable(false),
       clientVersion(0),
       supportsNPOT(false),
       samplerComplete(false)
 {
 }
 
-GLsizei Texture::getAttachmentWidth(const gl::FramebufferAttachment::Target &target) const
+Extents Texture::getAttachmentSize(const gl::FramebufferAttachment::Target &target) const
 {
-    return static_cast<GLsizei>(
-        getWidth(target.textureIndex().type, target.textureIndex().mipIndex));
-}
-
-GLsizei Texture::getAttachmentHeight(const gl::FramebufferAttachment::Target &target) const
-{
-    return static_cast<GLsizei>(
-        getHeight(target.textureIndex().type, target.textureIndex().mipIndex));
+    return getImageDesc(target.textureIndex().type, target.textureIndex().mipIndex).size;
 }
 
 GLenum Texture::getAttachmentInternalFormat(const gl::FramebufferAttachment::Target &target) const
 {
     return getInternalFormat(target.textureIndex().type, target.textureIndex().mipIndex);
 }
 
 GLsizei Texture::getAttachmentSamples(const gl::FramebufferAttachment::Target &/*target*/) const
--- a/gfx/angle/src/libANGLE/Texture.h
+++ b/gfx/angle/src/libANGLE/Texture.h
@@ -161,18 +161,17 @@ class Texture final : public egl::ImageS
     Error generateMipmaps();
 
     egl::Surface *getBoundSurface() const;
 
     rx::TextureImpl *getImplementation() { return mTexture; }
     const rx::TextureImpl *getImplementation() const { return mTexture; }
 
     // FramebufferAttachmentObject implementation
-    GLsizei getAttachmentWidth(const FramebufferAttachment::Target &target) const override;
-    GLsizei getAttachmentHeight(const FramebufferAttachment::Target &target) const override;
+    Extents getAttachmentSize(const FramebufferAttachment::Target &target) const override;
     GLenum getAttachmentInternalFormat(const FramebufferAttachment::Target &target) const override;
     GLsizei getAttachmentSamples(const FramebufferAttachment::Target &target) const override;
 
     void onAttach() override;
     void onDetach() override;
     GLuint getId() const override;
 
   private:
--- a/gfx/angle/src/libANGLE/TransformFeedback.cpp
+++ b/gfx/angle/src/libANGLE/TransformFeedback.cpp
@@ -80,16 +80,34 @@ GLenum TransformFeedback::getPrimitiveMo
 }
 
 void TransformFeedback::bindGenericBuffer(Buffer *buffer)
 {
     mGenericBuffer.set(buffer);
     mImplementation->bindGenericBuffer(mGenericBuffer);
 }
 
+void TransformFeedback::detachBuffer(GLuint bufferName)
+{
+    for (size_t index = 0; index < mIndexedBuffers.size(); index++)
+    {
+        if (mIndexedBuffers[index].id() == bufferName)
+        {
+            mIndexedBuffers[index].set(nullptr);
+            mImplementation->bindIndexedBuffer(index, mIndexedBuffers[index]);
+        }
+    }
+
+    if (mGenericBuffer.id() == bufferName)
+    {
+        mGenericBuffer.set(nullptr);
+        mImplementation->bindGenericBuffer(mGenericBuffer);
+    }
+}
+
 const BindingPointer<Buffer> &TransformFeedback::getGenericBuffer() const
 {
     return mGenericBuffer;
 }
 
 void TransformFeedback::bindIndexedBuffer(size_t index, Buffer *buffer, size_t offset, size_t size)
 {
     ASSERT(index < mIndexedBuffers.size());
--- a/gfx/angle/src/libANGLE/TransformFeedback.h
+++ b/gfx/angle/src/libANGLE/TransformFeedback.h
@@ -40,16 +40,18 @@ class TransformFeedback : public RefCoun
 
     void bindGenericBuffer(Buffer *buffer);
     const BindingPointer<Buffer> &getGenericBuffer() const;
 
     void bindIndexedBuffer(size_t index, Buffer *buffer, size_t offset, size_t size);
     const OffsetBindingPointer<Buffer> &getIndexedBuffer(size_t index) const;
     size_t getIndexedBufferCount() const;
 
+    void detachBuffer(GLuint bufferName);
+
     rx::TransformFeedbackImpl *getImplementation();
     const rx::TransformFeedbackImpl *getImplementation() const;
 
   private:
     rx::TransformFeedbackImpl* mImplementation;
 
     bool mActive;
     GLenum mPrimitiveMode;
--- a/gfx/angle/src/libANGLE/angletypes.cpp
+++ b/gfx/angle/src/libANGLE/angletypes.cpp
@@ -128,9 +128,18 @@ bool Box::operator==(const Box &other) c
             width == other.width && height == other.height && depth == other.depth);
 }
 
 bool Box::operator!=(const Box &other) const
 {
     return !(*this == other);
 }
 
+bool operator==(const Extents &lhs, const Extents &rhs)
+{
+    return lhs.width == rhs.width && lhs.height == rhs.height && lhs.depth == rhs.depth;
 }
+
+bool operator!=(const Extents &lhs, const Extents &rhs)
+{
+    return !(lhs == rhs);
+}
+}
--- a/gfx/angle/src/libANGLE/angletypes.h
+++ b/gfx/angle/src/libANGLE/angletypes.h
@@ -63,23 +63,31 @@ template <typename T>
 bool operator!=(const Color<T> &a, const Color<T> &b);
 
 typedef Color<float> ColorF;
 typedef Color<int> ColorI;
 typedef Color<unsigned int> ColorUI;
 
 struct Rectangle
 {
+    Rectangle() : x(0), y(0), width(0), height(0) {}
+    Rectangle(int x_in, int y_in, int width_in, int height_in)
+        : x(x_in), y(y_in), width(width_in), height(height_in)
+    {
+    }
+
+    int x0() const { return x; }
+    int y0() const { return y; }
+    int x1() const { return x + width; }
+    int y1() const { return y + height; }
+
     int x;
     int y;
     int width;
     int height;
-
-    Rectangle() : x(0), y(0), width(0), height(0) { }
-    Rectangle(int x_in, int y_in, int width_in, int height_in) : x(x_in), y(y_in), width(width_in), height(height_in) { }
 };
 
 bool operator==(const Rectangle &a, const Rectangle &b);
 bool operator!=(const Rectangle &a, const Rectangle &b);
 
 bool ClipRectangle(const Rectangle &source, const Rectangle &clip, Rectangle *intersection);
 
 struct Offset
@@ -99,16 +107,19 @@ struct Extents
     int depth;
 
     Extents() : width(0), height(0), depth(0) { }
     Extents(int width_, int height_, int depth_) : width(width_), height(height_), depth(depth_) { }
 
     bool empty() const { return (width * height * depth) == 0; }
 };
 
+bool operator==(const Extents &lhs, const Extents &rhs);
+bool operator!=(const Extents &lhs, const Extents &rhs);
+
 struct Box
 {
     int x;
     int y;
     int z;
     int width;
     int height;
     int depth;
--- a/gfx/angle/src/libANGLE/formatutils.cpp
+++ b/gfx/angle/src/libANGLE/formatutils.cpp
@@ -411,17 +411,17 @@ static InternalFormatInfoMap BuildIntern
     map.insert(InternalFormatInfoPair(GL_RGB32F,            RGBAFormat(32, 32, 32,  0, 0, GL_RGB,          GL_FLOAT,                        GL_FLOAT, false, RequireESOrExt<3, &Extensions::textureFloat>,                                   RequireESOrExt<3, &Extensions::textureFloat>,                                   RequireExt<&Extensions::textureFloatLinear>    )));
     map.insert(InternalFormatInfoPair(GL_RGBA32F,           RGBAFormat(32, 32, 32, 32, 0, GL_RGBA,         GL_FLOAT,                        GL_FLOAT, false, RequireESOrExt<3, &Extensions::textureFloat>,                                   RequireESOrExt<3, &Extensions::textureFloat>,                                   RequireExt<&Extensions::textureFloatLinear>    )));
 
     // Depth stencil formats
     //                               | Internal format         |                  | D |S | X | Format            | Type                             | Component type        | Supported                                    | Renderable                                                                         | Filterable                                  |
     map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT16,     DepthStencilFormat(16, 0,  0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT,                 GL_UNSIGNED_NORMALIZED, RequireES<2>,                                  RequireES<2>,                                                                        RequireESOrExt<3, &Extensions::depthTextures>)));
     map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT24,     DepthStencilFormat(24, 0,  0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT,                   GL_UNSIGNED_NORMALIZED, RequireES<3>,                                  RequireES<3>,                                                                        RequireESOrExt<3, &Extensions::depthTextures>)));
     map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT32F,    DepthStencilFormat(32, 0,  0, GL_DEPTH_COMPONENT, GL_FLOAT,                          GL_FLOAT,               RequireES<3>,                                  RequireES<3>,                                                                        RequireESOrExt<3, &Extensions::depthTextures>)));
-    map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT32_OES, DepthStencilFormat(32, 0,  0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT,                   GL_UNSIGNED_NORMALIZED, RequireExt<&Extensions::depthTextures>,        RequireExt<&Extensions::depthTextures>,                                              AlwaysSupported                              )));
+    map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT32_OES, DepthStencilFormat(32, 0,  0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT,                   GL_UNSIGNED_NORMALIZED, RequireExtOrExt<&Extensions::depthTextures, &Extensions::depth32>, RequireExtOrExt<&Extensions::depthTextures, &Extensions::depth32>, AlwaysSupported                            )));
     map.insert(InternalFormatInfoPair(GL_DEPTH24_STENCIL8,      DepthStencilFormat(24, 8,  0, GL_DEPTH_STENCIL,   GL_UNSIGNED_INT_24_8,              GL_UNSIGNED_NORMALIZED, RequireESOrExt<3, &Extensions::depthTextures>, RequireESOrExtOrExt<3, &Extensions::depthTextures, &Extensions::packedDepthStencil>, AlwaysSupported                              )));
     map.insert(InternalFormatInfoPair(GL_DEPTH32F_STENCIL8,     DepthStencilFormat(32, 8, 24, GL_DEPTH_STENCIL,   GL_FLOAT_32_UNSIGNED_INT_24_8_REV, GL_FLOAT,               RequireES<3>,                                  RequireES<3>,                                                                        AlwaysSupported                              )));
     // STENCIL_INDEX8 is special-cased, see around the bottom of the list.
 
     // Luminance alpha formats
     //                               | Internal format          |          | L | A | Format            | Type            | Component type        | Supported                                                                    | Renderable    | Filterable    |
     map.insert(InternalFormatInfoPair(GL_ALPHA8_EXT,             LUMAFormat( 0,  8, GL_ALPHA,           GL_UNSIGNED_BYTE, GL_UNSIGNED_NORMALIZED, RequireExt<&Extensions::textureStorage>,                                      NeverSupported, AlwaysSupported)));
     map.insert(InternalFormatInfoPair(GL_LUMINANCE8_EXT,         LUMAFormat( 8,  0, GL_LUMINANCE,       GL_UNSIGNED_BYTE, GL_UNSIGNED_NORMALIZED, RequireExt<&Extensions::textureStorage>,                                      NeverSupported, AlwaysSupported)));
--- a/gfx/angle/src/libANGLE/renderer/DeviceImpl.h
+++ b/gfx/angle/src/libANGLE/renderer/DeviceImpl.h
@@ -24,13 +24,14 @@ class DeviceImpl : angle::NonCopyable
 {
   public:
     DeviceImpl();
     virtual ~DeviceImpl();
 
     virtual egl::Error getDevice(void **outValue) = 0;
     virtual EGLint getType() = 0;
     virtual void generateExtensions(egl::DeviceExtensions *outExtensions) const = 0;
+    virtual bool deviceExternallySourced() = 0;
 };
 
 }
 
 #endif // LIBANGLE_RENDERER_DEVICEIMPL_H_
--- a/gfx/angle/src/libANGLE/renderer/DisplayImpl.cpp
+++ b/gfx/angle/src/libANGLE/renderer/DisplayImpl.cpp
@@ -16,20 +16,17 @@ namespace rx
 DisplayImpl::DisplayImpl()
     : mExtensionsInitialized(false),
       mCapsInitialized(false)
 {
 }
 
 DisplayImpl::~DisplayImpl()
 {
-    while (!mSurfaceSet.empty())
-    {
-        destroySurface(*mSurfaceSet.begin());
-    }
+    ASSERT(mSurfaceSet.empty());
 }
 
 void DisplayImpl::destroySurface(egl::Surface *surface)
 {
     mSurfaceSet.erase(surface);
     surface->onDestroy();
 }
 
--- a/gfx/angle/src/libANGLE/renderer/FramebufferImpl.h
+++ b/gfx/angle/src/libANGLE/renderer/FramebufferImpl.h
@@ -39,29 +39,42 @@ class FramebufferImpl : angle::NonCopyab
     virtual void setDrawBuffers(size_t count, const GLenum *buffers) = 0;
     virtual void setReadBuffer(GLenum buffer) = 0;
 
     virtual gl::Error discard(size_t count, const GLenum *attachments) = 0;
     virtual gl::Error invalidate(size_t count, const GLenum *attachments) = 0;
     virtual gl::Error invalidateSub(size_t count, const GLenum *attachments, const gl::Rectangle &area) = 0;
 
     virtual gl::Error clear(const gl::Data &data, GLbitfield mask) = 0;
-    virtual gl::Error clearBufferfv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLfloat *values) = 0;
-    virtual gl::Error clearBufferuiv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLuint *values) = 0;
-    virtual gl::Error clearBufferiv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLint *values) = 0;
-    virtual gl::Error clearBufferfi(const gl::State &state, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) = 0;
+    virtual gl::Error clearBufferfv(const gl::Data &data,
+                                    GLenum buffer,
+                                    GLint drawbuffer,
+                                    const GLfloat *values) = 0;
+    virtual gl::Error clearBufferuiv(const gl::Data &data,
+                                     GLenum buffer,
+                                     GLint drawbuffer,
+                                     const GLuint *values) = 0;
+    virtual gl::Error clearBufferiv(const gl::Data &data,
+                                    GLenum buffer,
+                                    GLint drawbuffer,
+                                    const GLint *values) = 0;
+    virtual gl::Error clearBufferfi(const gl::Data &data,
+                                    GLenum buffer,
+                                    GLint drawbuffer,
+                                    GLfloat depth,
+                                    GLint stencil) = 0;
 
     virtual GLenum getImplementationColorReadFormat() const = 0;
     virtual GLenum getImplementationColorReadType() const = 0;
     virtual gl::Error readPixels(const gl::State &state, const gl::Rectangle &area, GLenum format, GLenum type, GLvoid *pixels) const = 0;
 
     virtual gl::Error blit(const gl::State &state, const gl::Rectangle &sourceArea, const gl::Rectangle &destArea,
                            GLbitfield mask, GLenum filter, const gl::Framebuffer *sourceFramebuffer) = 0;
 
-    virtual GLenum checkStatus() const = 0;
+    virtual bool checkStatus() const = 0;
 
     const gl::Framebuffer::Data &getData() const { return mData; }
 
   protected:
     const gl::Framebuffer::Data &mData;
 };
 
 }
--- a/gfx/angle/src/libANGLE/renderer/FramebufferImpl_mock.h
+++ b/gfx/angle/src/libANGLE/renderer/FramebufferImpl_mock.h
@@ -31,35 +31,35 @@ class MockFramebufferImpl : public rx::F
     MOCK_METHOD2(setDrawBuffers, void(size_t, const GLenum *));
     MOCK_METHOD1(setReadBuffer, void(GLenum));
 
     MOCK_METHOD2(discard, gl::Error(size_t, const GLenum *));
     MOCK_METHOD2(invalidate, gl::Error(size_t, const GLenum *));
     MOCK_METHOD3(invalidateSub, gl::Error(size_t, const GLenum *, const gl::Rectangle &));
 
     MOCK_METHOD2(clear, gl::Error(const gl::Data &, GLbitfield));
-    MOCK_METHOD4(clearBufferfv, gl::Error(const gl::State &, GLenum, GLint, const GLfloat *));
-    MOCK_METHOD4(clearBufferuiv, gl::Error(const gl::State &, GLenum, GLint, const GLuint *));
-    MOCK_METHOD4(clearBufferiv, gl::Error(const gl::State &, GLenum, GLint, const GLint *));
-    MOCK_METHOD5(clearBufferfi, gl::Error(const gl::State &, GLenum, GLint, GLfloat, GLint));
+    MOCK_METHOD4(clearBufferfv, gl::Error(const gl::Data &, GLenum, GLint, const GLfloat *));
+    MOCK_METHOD4(clearBufferuiv, gl::Error(const gl::Data &, GLenum, GLint, const GLuint *));
+    MOCK_METHOD4(clearBufferiv, gl::Error(const gl::Data &, GLenum, GLint, const GLint *));
+    MOCK_METHOD5(clearBufferfi, gl::Error(const gl::Data &, GLenum, GLint, GLfloat, GLint));
 
     MOCK_CONST_METHOD0(getImplementationColorReadFormat, GLenum());
     MOCK_CONST_METHOD0(getImplementationColorReadType, GLenum());
     MOCK_CONST_METHOD5(
         readPixels,
         gl::Error(const gl::State &, const gl::Rectangle &, GLenum, GLenum, GLvoid *));
 
     MOCK_METHOD6(blit,
                  gl::Error(const gl::State &,
                            const gl::Rectangle &,
                            const gl::Rectangle &,
                            GLbitfield,
                            GLenum,
                            const gl::Framebuffer *));
 
-    MOCK_CONST_METHOD0(checkStatus, GLenum());
+    MOCK_CONST_METHOD0(checkStatus, bool());
 
     MOCK_METHOD0(destroy, void());
 };
 
 }  // namespace rx
 
 #endif  // LIBANGLE_RENDERER_FRAMEBUFFERIMPLMOCK_H_
--- a/gfx/angle/src/libANGLE/renderer/ProgramImpl.h
+++ b/gfx/angle/src/libANGLE/renderer/ProgramImpl.h
@@ -32,16 +32,17 @@ struct LinkResult
 class ProgramImpl : angle::NonCopyable
 {
   public:
     ProgramImpl(const gl::Program::Data &data) : mData(data) {}
     virtual ~ProgramImpl() {}
 
     virtual LinkResult load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) = 0;
     virtual gl::Error save(gl::BinaryOutputStream *stream) = 0;
+    virtual void setBinaryRetrievableHint(bool retrievable) = 0;
 
     virtual LinkResult link(const gl::Data &data, gl::InfoLog &infoLog) = 0;
     virtual GLboolean validate(const gl::Caps &caps, gl::InfoLog *infoLog) = 0;
 
     virtual void setUniform1fv(GLint location, GLsizei count, const GLfloat *v) = 0;
     virtual void setUniform2fv(GLint location, GLsizei count, const GLfloat *v) = 0;
     virtual void setUniform3fv(GLint location, GLsizei count, const GLfloat *v) = 0;
     virtual void setUniform4fv(GLint location, GLsizei count, const GLfloat *v) = 0;
--- a/gfx/angle/src/libANGLE/renderer/ProgramImpl_mock.h
+++ b/gfx/angle/src/libANGLE/renderer/ProgramImpl_mock.h
@@ -20,16 +20,17 @@ namespace rx
 class MockProgramImpl : public rx::ProgramImpl
 {
   public:
     MockProgramImpl() : ProgramImpl(gl::Program::Data()) {}
     virtual ~MockProgramImpl() { destroy(); }
 
     MOCK_METHOD2(load, LinkResult(gl::InfoLog &, gl::BinaryInputStream *));
     MOCK_METHOD1(save, gl::Error(gl::BinaryOutputStream *));
+    MOCK_METHOD1(setBinaryRetrievableHint, void(bool));
 
     MOCK_METHOD2(link, LinkResult(const gl::Data &, gl::InfoLog &));
     MOCK_METHOD2(validate, GLboolean(const gl::Caps &, gl::InfoLog *));
 
     MOCK_METHOD3(setUniform1fv, void(GLint, GLsizei, const GLfloat *));
     MOCK_METHOD3(setUniform2fv, void(GLint, GLsizei, const GLfloat *));
     MOCK_METHOD3(setUniform3fv, void(GLint, GLsizei, const GLfloat *));
     MOCK_METHOD3(setUniform4fv, void(GLint, GLsizei, const GLfloat *));
--- a/gfx/angle/src/libANGLE/renderer/SurfaceImpl.h
+++ b/gfx/angle/src/libANGLE/renderer/SurfaceImpl.h
@@ -31,17 +31,17 @@ class SurfaceImpl : public FramebufferAt
     SurfaceImpl();
     virtual ~SurfaceImpl();
 
     virtual egl::Error initialize() = 0;
     virtual FramebufferImpl *createDefaultFramebuffer(const gl::Framebuffer::Data &data) = 0;
     virtual egl::Error swap() = 0;
     virtual egl::Error postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) = 0;
     virtual egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) = 0;
-    virtual egl::Error bindTexImage(EGLint buffer) = 0;
+    virtual egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) = 0;
     virtual egl::Error releaseTexImage(EGLint buffer) = 0;
     virtual void setSwapInterval(EGLint interval) = 0;
 
     // width and height can change with client window resizing
     virtual EGLint getWidth() const = 0;
     virtual EGLint getHeight() const = 0;
 
     virtual EGLint isPostSubBufferSupported() const = 0;
--- a/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.cpp
+++ b/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.cpp
@@ -32,20 +32,25 @@ BufferD3D::BufferD3D(BufferFactoryD3D *f
     updateSerial();
 }
 
 BufferD3D::~BufferD3D()
 {
     SafeDelete(mStaticVertexBuffer);
     SafeDelete(mStaticIndexBuffer);
 
-    // Empty the cache of static vertex buffers too
+    emptyStaticBufferCache();
+}
+
+void BufferD3D::emptyStaticBufferCache()
+{
     if (mStaticBufferCache != nullptr)
     {
         SafeDeleteContainer(*mStaticBufferCache);
+        SafeDelete(mStaticBufferCache);
     }
 
     mStaticBufferCacheTotalSize = 0;
 }
 
 void BufferD3D::updateSerial()
 {
     mSerial = mNextSerial++;
@@ -197,23 +202,17 @@ void BufferD3D::reinitOutOfDateStaticDat
         mStaticVertexBufferOutOfDate = false;
     }
 }
 
 void BufferD3D::invalidateStaticData(D3DBufferInvalidationType invalidationType)
 {
     if (invalidationType == D3D_BUFFER_INVALIDATE_WHOLE_CACHE && mStaticBufferCache != nullptr)
     {
-        // Empty the cache of static vertex buffers too
-        for (StaticVertexBufferInterface *staticBuffer : *mStaticBufferCache)
-        {
-            SafeDelete(staticBuffer);
-        }
-        mStaticBufferCache->clear();
-        mStaticBufferCacheTotalSize = 0;
+        emptyStaticBufferCache();
     }
 
     if ((mStaticVertexBuffer && mStaticVertexBuffer->getBufferSize() != 0) || (mStaticIndexBuffer && mStaticIndexBuffer->getBufferSize() != 0))
     {
         SafeDelete(mStaticVertexBuffer);
         SafeDelete(mStaticIndexBuffer);
 
         // If the buffer was created with a static usage then we recreate the static
--- a/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.h
+++ b/gfx/angle/src/libANGLE/renderer/d3d/BufferD3D.h
@@ -66,16 +66,17 @@ class BufferD3D : public BufferImpl
                             size_t offset,
                             size_t count,
                             bool primitiveRestartEnabled,
                             gl::IndexRange *outRange) override;
 
   protected:
     void updateSerial();
     void updateD3DBufferUsage(GLenum usage);
+    void emptyStaticBufferCache();
 
     BufferFactoryD3D *mFactory;
     unsigned int mSerial;
     static unsigned int mNextSerial;
 
     StaticVertexBufferInterface *mStaticVertexBuffer;
     StaticIndexBufferInterface *mStaticIndexBuffer;
     std::vector<StaticVertexBufferInterface *> *mStaticBufferCache;
--- a/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.cpp
+++ b/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.cpp
@@ -12,26 +12,88 @@
 #include "libANGLE/Device.h"
 #include "libANGLE/Display.h"
 
 #include <EGL/eglext.h>
 
 namespace rx
 {
 
-DeviceD3D::DeviceD3D(void *device, EGLint deviceType) : mDevice(device), mDeviceType(deviceType)
+DeviceD3D::DeviceD3D()
+    : mDevice(0), mDeviceType(0), mDeviceExternallySourced(false), mIsInitialized(false)
+{
+}
+
+DeviceD3D::~DeviceD3D()
 {
+#if defined(ANGLE_ENABLE_D3D11)
+    if (mDeviceType == EGL_D3D11_DEVICE_ANGLE)
+    {
+        // DeviceD3D holds a ref to an externally-sourced D3D11 device. We must release it.
+        ID3D11Device *device = reinterpret_cast<ID3D11Device *>(mDevice);
+        device->Release();
+    }
+#endif
 }
 
 egl::Error DeviceD3D::getDevice(void **outValue)
 {
+    if (!mIsInitialized)
+    {
+        *outValue = nullptr;
+        return egl::Error(EGL_BAD_DEVICE_EXT);
+    }
+
     *outValue = mDevice;
     return egl::Error(EGL_SUCCESS);
 }
 
+egl::Error DeviceD3D::initialize(void *device,
+                                 EGLint deviceType,
+                                 EGLBoolean deviceExternallySourced)
+{
+    ASSERT(!mIsInitialized);
+    if (mIsInitialized)
+    {
+        return egl::Error(EGL_BAD_DEVICE_EXT);
+    }
+
+    mDevice                  = device;
+    mDeviceType              = deviceType;
+    mDeviceExternallySourced = !!deviceExternallySourced;
+
+#if defined(ANGLE_ENABLE_D3D11)
+    if (mDeviceType == EGL_D3D11_DEVICE_ANGLE)
+    {
+        // Validate the device
+        IUnknown *iunknown = reinterpret_cast<IUnknown *>(device);
+
+        ID3D11Device *d3dDevice = nullptr;
+        HRESULT hr =
+            iunknown->QueryInterface(__uuidof(ID3D11Device), reinterpret_cast<void **>(&d3dDevice));
+        if (FAILED(hr))
+        {
+            return egl::Error(EGL_BAD_ATTRIBUTE, "Invalid D3D device passed into EGLDeviceEXT");
+        }
+
+        // The QI to ID3D11Device adds a ref to the D3D11 device.
+        // Deliberately don't release the ref here, so that the DeviceD3D holds a ref to the
+        // D3D11 device.
+    }
+    else
+#endif
+    {
+        ASSERT(!mDeviceExternallySourced);
+    }
+
+    mIsInitialized = true;
+
+    return egl::Error(EGL_SUCCESS);
+}
+
 EGLint DeviceD3D::getType()
 {
     return mDeviceType;
 }
 
 void DeviceD3D::generateExtensions(egl::DeviceExtensions *outExtensions) const
 {
     outExtensions->deviceD3D = true;
--- a/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.h
+++ b/gfx/angle/src/libANGLE/renderer/d3d/DeviceD3D.h
@@ -13,22 +13,27 @@
 #include "libANGLE/renderer/DeviceImpl.h"
 #include "libANGLE/renderer/d3d/RendererD3D.h"
 
 namespace rx
 {
 class DeviceD3D : public DeviceImpl
 {
   public:
-    DeviceD3D(void *device, EGLint deviceType);
+    DeviceD3D();
+    ~DeviceD3D() override;
 
+    egl::Error initialize(void *device, EGLint deviceType, EGLBoolean external);
     egl::Error getDevice(void **outValue) override;
     EGLint getType() override;
     void generateExtensions(egl::DeviceExtensions *outExtensions) const override;
+    bool deviceExternallySourced() override { return mDeviceExternallySourced; }
 
   private:
     void *mDevice;
     EGLint mDeviceType;
+    bool mDeviceExternallySourced;
+    bool mIsInitialized;
 };
 
 }
 
 #endif // LIBANGLE_RENDERER_D3D_DEVICED3D_H_
--- a/gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.cpp
+++ b/gfx/angle/src/libANGLE/renderer/d3d/DisplayD3D.cpp
@@ -50,20 +50,23 @@ static RendererD3D *CreateTypedRendererD
 }
 
 egl::Error CreateRendererD3D(egl::Display *display, RendererD3D **outRenderer)
 {
     ASSERT(outRenderer != nullptr);
 
     std::vector<CreateRendererD3DFunction> rendererCreationFunctions;
 
-    const auto &attribMap = display->getAttributeMap();
-    EGLNativeDisplayType nativeDisplay = display->getNativeDisplayId();
+    if (display->getPlatform() == EGL_PLATFORM_ANGLE_ANGLE)
+    {
+        const auto &attribMap              = display->getAttributeMap();
+        EGLNativeDisplayType nativeDisplay = display->getNativeDisplayId();
 
-    EGLint requestedDisplayType = attribMap.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE);
+        EGLint requestedDisplayType =
+            attribMap.get(EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE);
 
 #   if defined(ANGLE_ENABLE_D3D11)
         if (nativeDisplay == EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE ||
             nativeDisplay == EGL_D3D11_ONLY_DISPLAY_ANGLE ||
             requestedDisplayType == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
         {
             rendererCreationFunctions.push_back(CreateTypedRendererD3D<Renderer11>);
         }
@@ -72,37 +75,51 @@ egl::Error CreateRendererD3D(egl::Displa
 #   if defined(ANGLE_ENABLE_D3D9)
         if (nativeDisplay == EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE ||
             requestedDisplayType == EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE)
         {
             rendererCreationFunctions.push_back(CreateTypedRendererD3D<Renderer9>);
         }
 #   endif
 
-    if (nativeDisplay != EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE &&
-        nativeDisplay != EGL_D3D11_ONLY_DISPLAY_ANGLE &&
-        requestedDisplayType == EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE)
-    {
+        if (nativeDisplay != EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE &&
+            nativeDisplay != EGL_D3D11_ONLY_DISPLAY_ANGLE &&
+            requestedDisplayType == EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE)
+        {
         // The default display is requested, try the D3D9 and D3D11 renderers, order them using
         // the definition of ANGLE_DEFAULT_D3D11
 #       if ANGLE_DEFAULT_D3D11
 #           if defined(ANGLE_ENABLE_D3D11)
-                rendererCreationFunctions.push_back(CreateTypedRendererD3D<Renderer11>);
+            rendererCreationFunctions.push_back(CreateTypedRendererD3D<Renderer11>);
 #           endif
 #           if defined(ANGLE_ENABLE_D3D9)
-                rendererCreationFunctions.push_back(CreateTypedRendererD3D<Renderer9>);
+            rendererCreationFunctions.push_back(CreateTypedRendererD3D<Renderer9>);
 #           endif
 #       else
 #           if defined(ANGLE_ENABLE_D3D9)
-                rendererCreationFunctions.push_back(CreateTypedRendererD3D<Renderer9>);
+            rendererCreationFunctions.push_back(CreateTypedRendererD3D<Renderer9>);
 #           endif
 #           if defined(ANGLE_ENABLE_D3D11)
-                rendererCreationFunctions.push_back(CreateTypedRendererD3D<Renderer11>);
+            rendererCreationFunctions.push_back(CreateTypedRendererD3D<Renderer11>);
 #           endif
 #       endif
+        }
+    }
+    else if (display->getPlatform() == EGL_PLATFORM_DEVICE_EXT)
+    {
+#if defined(ANGLE_ENABLE_D3D11)
+        if (display->getDevice()->getType() == EGL_D3D11_DEVICE_ANGLE)
+        {
+            rendererCreationFunctions.push_back(CreateTypedRendererD3D<Renderer11>);
+        }
+#endif
+    }
+    else
+    {
+        UNIMPLEMENTED();
     }
 
     egl::Error result(EGL_NOT_INITIALIZED, "No available renderers.");
     for (size_t i = 0; i < rendererCreationFunctions.size(); i++)
     {
         RendererD3D *renderer = rendererCreationFunctions[i](display);
         result = renderer->initialize();
 
--- a/gfx/angle/src/libANGLE/renderer/d3d/FramebufferD3D.cpp
+++ b/gfx/angle/src/libANGLE/renderer/d3d/FramebufferD3D.cpp
@@ -120,23 +120,26 @@ void FramebufferD3D::setDrawBuffers(size
 void FramebufferD3D::setReadBuffer(GLenum)
 {
 }
 
 gl::Error FramebufferD3D::clear(const gl::Data &data, GLbitfield mask)
 {
     const gl::State &state = *data.state;
     ClearParameters clearParams = GetClearParameters(state, mask);
-    return clear(state, clearParams);
+    return clear(data, clearParams);
 }
 
-gl::Error FramebufferD3D::clearBufferfv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLfloat *values)
+gl::Error FramebufferD3D::clearBufferfv(const gl::Data &data,
+                                        GLenum buffer,
+                                        GLint drawbuffer,
+                                        const GLfloat *values)
 {
     // glClearBufferfv can be called to clear the color buffer or depth buffer
-    ClearParameters clearParams = GetClearParameters(state, 0);
+    ClearParameters clearParams = GetClearParameters(*data.state, 0);
 
     if (buffer == GL_COLOR)
     {
         for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++)
         {
             clearParams.clearColor[i] = (drawbuffer == static_cast<int>(i));
         }
         clearParams.colorFClearValue = gl::ColorF(values[0], values[1], values[2], values[3]);
@@ -144,37 +147,43 @@ gl::Error FramebufferD3D::clearBufferfv(
     }
 
     if (buffer == GL_DEPTH)
     {
         clearParams.clearDepth = true;
         clearParams.depthClearValue = values[0];
     }
 
-    return clear(state, clearParams);
+    return clear(data, clearParams);
 }
 
-gl::Error FramebufferD3D::clearBufferuiv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLuint *values)
+gl::Error FramebufferD3D::clearBufferuiv(const gl::Data &data,
+                                         GLenum buffer,
+                                         GLint drawbuffer,
+                                         const GLuint *values)
 {
     // glClearBufferuiv can only be called to clear a color buffer
-    ClearParameters clearParams = GetClearParameters(state, 0);
+    ClearParameters clearParams = GetClearParameters(*data.state, 0);
     for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++)
     {
         clearParams.clearColor[i] = (drawbuffer == static_cast<int>(i));
     }
     clearParams.colorUIClearValue = gl::ColorUI(values[0], values[1], values[2], values[3]);
     clearParams.colorClearType = GL_UNSIGNED_INT;
 
-    return clear(state, clearParams);
+    return clear(data, clearParams);
 }
 
-gl::Error FramebufferD3D::clearBufferiv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLint *values)
+gl::Error FramebufferD3D::clearBufferiv(const gl::Data &data,
+                                        GLenum buffer,
+                                        GLint drawbuffer,
+                                        const GLint *values)
 {
     // glClearBufferiv can be called to clear the color buffer or stencil buffer
-    ClearParameters clearParams = GetClearParameters(state, 0);
+    ClearParameters clearParams = GetClearParameters(*data.state, 0);
 
     if (buffer == GL_COLOR)
     {
         for (unsigned int i = 0; i < ArraySize(clearParams.clearColor); i++)
         {
             clearParams.clearColor[i] = (drawbuffer == static_cast<int>(i));
         }
         clearParams.colorIClearValue = gl::ColorI(values[0], values[1], values[2], values[3]);
@@ -182,29 +191,33 @@ gl::Error FramebufferD3D::clearBufferiv(
     }
 
     if (buffer == GL_STENCIL)
     {
         clearParams.clearStencil = true;
         clearParams.stencilClearValue = values[1];
     }
 
-    return clear(state, clearParams);
+    return clear(data, clearParams);
 }
 
-gl::Error FramebufferD3D::clearBufferfi(const gl::State &state, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil)
+gl::Error FramebufferD3D::clearBufferfi(const gl::Data &data,
+                                        GLenum buffer,
+                                        GLint drawbuffer,
+                                        GLfloat depth,
+                                        GLint stencil)
 {
     // glClearBufferfi can only be called to clear a depth stencil buffer
-    ClearParameters clearParams = GetClearParameters(state, 0);
+    ClearParameters clearParams   = GetClearParameters(*data.state, 0);
     clearParams.clearDepth = true;
     clearParams.depthClearValue = depth;
     clearParams.clearStencil = true;
     clearParams.stencilClearValue = stencil;
 
-    return clear(state, clearParams);
+    return clear(data, clearParams);
 }
 
 GLenum FramebufferD3D::getImplementationColorReadFormat() const
 {
     const gl::FramebufferAttachment *readAttachment = mData.getReadAttachment();
 
     if (readAttachment == nullptr)
     {
@@ -297,47 +310,53 @@ gl::Error FramebufferD3D::blit(const gl:
         {
             return error;
         }
     }
 
     return gl::Error(GL_NO_ERROR);
 }
 
-GLenum FramebufferD3D::checkStatus() const
+bool FramebufferD3D::checkStatus() const
 {
     // if we have both a depth and stencil buffer, they must refer to the same object
     // since we only support packed_depth_stencil and not separate depth and stencil
     if (mData.getDepthAttachment() != nullptr && mData.getStencilAttachment() != nullptr &&
         mData.getDepthStencilAttachment() == nullptr)
     {
-        return GL_FRAMEBUFFER_UNSUPPORTED;
+        return false;
     }
 
     // D3D11 does not allow for overlapping RenderTargetViews, so ensure uniqueness
     const auto &colorAttachments = mData.getColorAttachments();
     for (size_t colorAttachment = 0; colorAttachment < colorAttachments.size(); colorAttachment++)
     {
         const gl::FramebufferAttachment &attachment = colorAttachments[colorAttachment];
         if (attachment.isAttached())
         {
             for (size_t prevColorAttachment = 0; prevColorAttachment < colorAttachment; prevColorAttachment++)
             {
                 const gl::FramebufferAttachment &prevAttachment = colorAttachments[prevColorAttachment];
                 if (prevAttachment.isAttached() &&
                     (attachment.id() == prevAttachment.id() &&
                      attachment.type() == prevAttachment.type()))
                 {
-                    return GL_FRAMEBUFFER_UNSUPPORTED;
+                    return false;
                 }
             }
         }
     }
 
-    return GL_FRAMEBUFFER_COMPLETE;
+    // D3D requires all render targets to have the same dimensions.
+    if (!mData.attachmentsHaveSameDimensions())
+    {
+        return false;
+    }
+
+    return true;
 }
 
 const gl::AttachmentList &FramebufferD3D::getColorAttachmentsForRender(
     const WorkaroundsD3D &workarounds) const
 {
     if (!mInvalidateColorAttachmentCache)
     {
         return mColorAttachmentsForRender;
--- a/gfx/angle/src/libANGLE/renderer/d3d/FramebufferD3D.h
+++ b/gfx/angle/src/libANGLE/renderer/d3d/FramebufferD3D.h
@@ -62,39 +62,52 @@ class FramebufferD3D : public Framebuffe
     void onUpdateDepthAttachment() override;
     void onUpdateStencilAttachment() override;
     void onUpdateDepthStencilAttachment() override;
 
     void setDrawBuffers(size_t count, const GLenum *buffers) override;
     void setReadBuffer(GLenum buffer) override;
 
     gl::Error clear(const gl::Data &data, GLbitfield mask) override;
-    gl::Error clearBufferfv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLfloat *values) override;
-    gl::Error clearBufferuiv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLuint *values) override;
-    gl::Error clearBufferiv(const gl::State &state, GLenum buffer, GLint drawbuffer, const GLint *values) override;
-    gl::Error clearBufferfi(const gl::State &state, GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil) override;
+    gl::Error clearBufferfv(const gl::Data &data,
+                            GLenum buffer,
+                            GLint drawbuffer,
+                            const GLfloat *values) override;
+    gl::Error clearBufferuiv(const gl::Data &data,
+                             GLenum buffer,
+                             GLint drawbuffer,
+                             const GLuint *values) override;
+    gl::Error clearBufferiv(const gl::Data &data,
+                            GLenum buffer,
+                            GLint drawbuffer,
+                            const GLint *values) override;
+    gl::Error clearBufferfi(const gl::Data &data,
+                            GLenum buffer,
+                            GLint drawbuffer,
+                            GLfloat depth,
+                            GLint stencil) override;
 
     GLenum getImplementationColorReadFormat() const override;
     GLenum getImplementationColorReadType() const override;
     gl::Error readPixels(const gl::State &state, const gl::Rectangle &area, GLenum format, GLenum type, GLvoid *pixels) const override;
 
     gl::Error blit(const gl::State &state, const gl::Rectangle &sourceArea, const gl::Rectangle &destArea,
                    GLbitfield mask, GLenum filter, const gl::Framebuffer *sourceFramebuffer) override;
 
-    GLenum checkStatus() const override;
+    bool checkStatus() const override;
 
     const gl::AttachmentList &getColorAttachmentsForRender(const WorkaroundsD3D &workarounds) const;
 
   protected:
     // Cache variable
     mutable gl::AttachmentList mColorAttachmentsForRender;
     mutable bool mInvalidateColorAttachmentCache;
 
   private:
-    virtual gl::Error clear(const gl::State &state, const ClearParameters &clearParams) = 0;
+    virtual gl::Error clear(const gl::Data &data, const ClearParameters &clearParams) = 0;
 
     virtual gl::Error readPixelsImpl(const gl::Rectangle &area,
                                      GLenum format,
                                      GLenum type,
                                      size_t outputPitch,
                                      const gl::PixelPackState &pack,
                                      uint8_t *pixels) const = 0;
 
--- a/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -418,18 +418,17 @@ int ProgramD3DMetadata::getRendererMajor
 
 bool ProgramD3DMetadata::usesBroadcast(const gl::Data &data) const
 {
     return (mFragmentShader->usesFragColor() && data.clientVersion < 3);
 }
 
 bool ProgramD3DMetadata::usesFragDepth(const gl::Program::Data &programData) const
 {
-    // TODO(jmadill): Rename this or check if we need it for version 300
-    return (getMajorShaderVersion() < 300 && mFragmentShader->usesFragDepth());
+    return mFragmentShader->usesFragDepth();
 }
 
 bool ProgramD3DMetadata::usesPointCoord() const
 {
     return mFragmentShader->usesPointCoord();
 }
 
 bool ProgramD3DMetadata::usesFragCoord() const
@@ -1100,16 +1099,20 @@ gl::Error ProgramD3D::save(gl::BinaryOut
         size_t geometryShaderSize = geometryExe->getLength();
         stream->writeInt(geometryShaderSize);
         stream->writeBytes(geometryExe->getFunction(), geometryShaderSize);
     }
 
     return gl::Error(GL_NO_ERROR);
 }
 
+void ProgramD3D::setBinaryRetrievableHint(bool /* retrievable */)
+{
+}
+
 gl::Error ProgramD3D::getPixelExecutableForFramebuffer(const gl::Framebuffer *fbo,
                                                        ShaderExecutableD3D **outExecutable)
 {
     mPixelShaderOutputFormatCache.clear();
 
     const FramebufferD3D *fboD3D = GetImplAs<FramebufferD3D>(fbo);
     const gl::AttachmentList &colorbuffers =
         fboD3D->getColorAttachmentsForRender(mRenderer->getWorkarounds());
--- a/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.h
+++ b/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.h
@@ -145,18 +145,19 @@ class ProgramD3D : public ProgramImpl
     GLint getUsedSamplerRange(gl::SamplerType type) const;
     void updateSamplerMapping();
 
     bool usesPointSize() const { return mUsesPointSize; }
     bool usesPointSpriteEmulation() const;
     bool usesGeometryShader(GLenum drawMode) const;
     bool usesInstancedPointSpriteEmulation() const;
 
-    LinkResult load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream);
-    gl::Error save(gl::BinaryOutputStream *stream);
+    LinkResult load(gl::InfoLog &infoLog, gl::BinaryInputStream *stream) override;
+    gl::Error save(gl::BinaryOutputStream *stream) override;
+    void setBinaryRetrievableHint(bool retrievable) override;
 
     gl::Error getPixelExecutableForFramebuffer(const gl::Framebuffer *fbo,
                                                ShaderExecutableD3D **outExectuable);
     gl::Error getPixelExecutableForOutputLayout(const std::vector<GLenum> &outputLayout,
                                                 ShaderExecutableD3D **outExectuable,
                                                 gl::InfoLog *infoLog);
     gl::Error getVertexExecutableForInputLayout(const gl::InputLayout &inputLayout,
                                                 ShaderExecutableD3D **outExectuable,
--- a/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.cpp
+++ b/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.cpp
@@ -40,30 +40,27 @@ const int ScratchMemoryBufferLifetime = 
 
 const uintptr_t RendererD3D::DirtyPointer = std::numeric_limits<uintptr_t>::max();
 
 RendererD3D::RendererD3D(egl::Display *display)
     : mDisplay(display),
       mDeviceLost(false),
       mAnnotator(nullptr),
       mScratchMemoryBufferResetCounter(0),
-      mWorkaroundsInitialized(false),
-      mEGLDevice(nullptr)
+      mWorkaroundsInitialized(false)
 {
 }
 
 RendererD3D::~RendererD3D()
 {
     cleanup();
 }
 
 void RendererD3D::cleanup()
 {
-    SafeDelete(mEGLDevice);
-
     mScratchMemoryBuffer.resize(0);
     for (auto &incompleteTexture : mIncompleteTextures)
     {
         incompleteTexture.second.set(NULL);
     }
     mIncompleteTextures.clear();
 
     if (mAnnotator != nullptr)
@@ -151,23 +148,17 @@ gl::Error RendererD3D::genericDrawElemen
         return error;
     }
 
     if (!applyPrimitiveType(mode, count, usesPointSize))
     {
         return gl::Error(GL_NO_ERROR);
     }
 
-    error = applyRenderTarget(data, mode, false);
-    if (error.isError())
-    {
-        return error;
-    }
-
-    error = applyState(data, mode);
+    error = updateState(data, mode);
     if (error.isError())
     {
         return error;
     }
 
     TranslatedIndexData indexInfo;
     indexInfo.indexRange = indexRange;
 
@@ -241,23 +232,17 @@ gl::Error RendererD3D::genericDrawArrays
         return error;
     }
 
     if (!applyPrimitiveType(mode, count, usesPointSize))
     {
         return gl::Error(GL_NO_ERROR);
     }
 
-    error = applyRenderTarget(data, mode, false);
-    if (error.isError())
-    {
-        return error;
-    }
-
-    error = applyState(data, mode);
+    error = updateState(data, mode);
     if (error.isError())
     {
         return error;
     }
 
     applyTransformFeedbackBuffers(*data.state);
 
     error = applyVertexBuffer(*data.state, mode, first, count, instances, nullptr);
@@ -341,55 +326,18 @@ gl::Error RendererD3D::generateSwizzles(
     if (error.isError())
     {
         return error;
     }
 
     return gl::Error(GL_NO_ERROR);
 }
 
-// Applies the render target surface, depth stencil surface, viewport rectangle and
-// scissor rectangle to the renderer
-gl::Error RendererD3D::applyRenderTarget(const gl::Data &data, GLenum drawMode, bool ignoreViewport)
+unsigned int RendererD3D::GetBlendSampleMask(const gl::Data &data, int samples)
 {
-    const gl::Framebuffer *framebufferObject = data.state->getDrawFramebuffer();
-    ASSERT(framebufferObject && framebufferObject->checkStatus(data) == GL_FRAMEBUFFER_COMPLETE);
-
-    gl::Error error = applyRenderTarget(framebufferObject);
-    if (error.isError())
-    {
-        return error;
-    }
-
-    float nearZ = data.state->getNearPlane();
-    float farZ = data.state->getFarPlane();
-    setViewport(data.state->getViewport(), nearZ, farZ, drawMode,
-                data.state->getRasterizerState().frontFace, ignoreViewport);
-
-    setScissorRectangle(data.state->getScissor(), data.state->isScissorTestEnabled());
-
-    return gl::Error(GL_NO_ERROR);
-}
-
-// Applies the fixed-function state (culling, depth test, alpha blending, stenciling, etc) to the Direct3D device
-gl::Error RendererD3D::applyState(const gl::Data &data, GLenum drawMode)
-{
-    const gl::Framebuffer *framebufferObject = data.state->getDrawFramebuffer();
-    int samples = framebufferObject->getSamples(data);
-
-    gl::RasterizerState rasterizer = data.state->getRasterizerState();
-    rasterizer.pointDrawMode = (drawMode == GL_POINTS);
-    rasterizer.multiSample = (samples != 0);
-
-    gl::Error error = setRasterizerState(rasterizer);
-    if (error.isError())
-    {
-        return error;
-    }
-
     unsigned int mask = 0;
     if (data.state->isSampleCoverageEnabled())
     {
         GLclampf coverageValue = data.state->getSampleCoverageValue();
         if (coverageValue != 0)
         {
             float threshold = 0.5f;
 
@@ -410,31 +358,18 @@ gl::Error RendererD3D::applyState(const 
         {
             mask = ~mask;
         }
     }
     else
     {
         mask = 0xFFFFFFFF;
     }
-    error = setBlendState(framebufferObject, data.state->getBlendState(),
-                          data.state->getBlendColor(), mask);
-    if (error.isError())
-    {
-        return error;
-    }
 
-    error = setDepthStencilState(data.state->getDepthStencilState(), data.state->getStencilRef(),
-                                 data.state->getStencilBackRef(), rasterizer.frontFace == GL_CCW);
-    if (error.isError())
-    {
-        return error;
-    }
-
-    return gl::Error(GL_NO_ERROR);
+    return mask;
 }
 
 // Applies the shaders and shader constants to the Direct3D device
 gl::Error RendererD3D::applyShaders(const gl::Data &data, GLenum drawMode)
 {
     gl::Program *program = data.state->getProgram();
     ProgramD3D *programD3D = GetImplAs<ProgramD3D>(program);
     programD3D->updateCachedInputLayout(*data.state);
@@ -732,22 +667,9 @@ void RendererD3D::initializeDebugAnnotat
     gl::InitializeDebugAnnotations(mAnnotator);
 }
 
 gl::DebugAnnotator *RendererD3D::getAnnotator()
 {
     ASSERT(mAnnotator);
     return mAnnotator;
 }
-
-egl::Error RendererD3D::getEGLDevice(DeviceImpl **device)
-{
-    egl::Error error = initializeEGLDevice(&mEGLDevice);
-    if (error.isError())
-    {
-        return error;
-    }
-
-    *device = static_cast<DeviceImpl *>(mEGLDevice);
-
-    return egl::Error(EGL_SUCCESS);
 }
-}
--- a/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.h
+++ b/gfx/angle/src/libANGLE/renderer/d3d/RendererD3D.h
@@ -148,28 +148,17 @@ class RendererD3D : public Renderer, pub
     virtual gl::Error generateSwizzle(gl::Texture *texture) = 0;
     virtual gl::Error setSamplerState(gl::SamplerType type, int index, gl::Texture *texture, const gl::SamplerState &sampler) = 0;
     virtual gl::Error setTexture(gl::SamplerType type, int index, gl::Texture *texture) = 0;
 
     virtual gl::Error setUniformBuffers(const gl::Data &data,
                                         const std::vector<GLint> &vertexUniformBuffers,
                                         const std::vector<GLint> &fragmentUniformBuffers) = 0;
 
-    virtual gl::Error setRasterizerState(const gl::RasterizerState &rasterState) = 0;
-    virtual gl::Error setBlendState(const gl::Framebuffer *framebuffer,
-                                    const gl::BlendState &blendState,
-                                    const gl::ColorF &blendColor,
-                                    unsigned int sampleMask) = 0;
-
-    virtual gl::Error setDepthStencilState(const gl::DepthStencilState &depthStencilState, int stencilRef,
-                                           int stencilBackRef, bool frontFaceCCW) = 0;
-
-    virtual void setScissorRectangle(const gl::Rectangle &scissor, bool enabled) = 0;
-    virtual void setViewport(const gl::Rectangle &viewport, float zNear, float zFar, GLenum drawMode, GLenum frontFace,
-                             bool ignoreViewport) = 0;
+    virtual gl::Error updateState(const gl::Data &data, GLenum drawMode) = 0;
 
     virtual gl::Error applyRenderTarget(const gl::Framebuffer *frameBuffer) = 0;
     virtual gl::Error applyUniforms(const ProgramD3D &programD3D,
                                     GLenum drawMode,
                                     const std::vector<D3DUniform *> &uniformArray) = 0;
     virtual bool applyPrimitiveType(GLenum primitiveType, GLsizei elementCount, bool usesPointSize) = 0;
     virtual gl::Error applyVertexBuffer(const gl::State &state, GLenum mode, GLint first, GLsizei count, GLsizei instances, SourceIndexData *sourceIndexInfo) = 0;
     virtual gl::Error applyIndexBuffer(const gl::Data &data,
@@ -250,28 +239,27 @@ class RendererD3D : public Renderer, pub
     // EXT_debug_marker
     void insertEventMarker(GLsizei length, const char *marker) override;
     void pushGroupMarker(GLsizei length, const char *marker) override;
     void popGroupMarker() override;
 
     // In D3D11, faster than calling setTexture a jillion times
     virtual gl::Error clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd) = 0;
 
-    egl::Error getEGLDevice(DeviceImpl **device);
+    virtual egl::Error getEGLDevice(DeviceImpl **device) = 0;
 
   protected:
     virtual bool getLUID(LUID *adapterLuid) const = 0;
     virtual gl::Error applyShadersImpl(const gl::Data &data, GLenum drawMode) = 0;
 
     void cleanup();
 
     virtual void createAnnotator() = 0;
 
-    virtual egl::Error initializeEGLDevice(DeviceD3D **outDevice) = 0;
-
+    static unsigned int GetBlendSampleMask(const gl::Data &data, int samples);
     // dirtyPointer is a special value that will make the comparison with any valid pointer fail and force the renderer to re-apply the state.
     static const uintptr_t DirtyPointer;
 
     egl::Display *mDisplay;
     bool mDeviceLost;
 
     void initializeDebugAnnotator();
     gl::DebugAnnotator *mAnnotator;
@@ -306,17 +294,16 @@ class RendererD3D : public Renderer, pub
                                        GLsizei instances) = 0;
 
     //FIXME(jmadill): std::array is currently prohibited by Chromium style guide
     typedef std::array<gl::Texture*, gl::IMPLEMENTATION_MAX_FRAMEBUFFER_ATTACHMENTS> FramebufferTextureArray;
 
     gl::Error generateSwizzles(const gl::Data &data, gl::SamplerType type);
     gl::Error generateSwizzles(const gl::Data &data);
 
-    gl::Error applyRenderTarget(const gl::Data &data, GLenum drawMode, bool ignoreViewport);
     gl::Error applyState(const gl::Data &data, GLenum drawMode);
     gl::Error applyShaders(const gl::Data &data, GLenum drawMode);
     gl::Error applyTextures(const gl::Data &data, gl::SamplerType shaderType,
                             const FramebufferTextureArray &framebufferTextures, size_t framebufferTextureCount);
     gl::Error applyTextures(const gl::Data &data);
 
     bool skipDraw(const gl::Data &data, GLenum drawMode);
     void markTransformFeedbackUsage(const gl::Data &data);
@@ -329,18 +316,16 @@ class RendererD3D : public Renderer, pub
     virtual WorkaroundsD3D generateWorkarounds() const = 0;
 
     gl::TextureMap mIncompleteTextures;
     MemoryBuffer mScratchMemoryBuffer;
     unsigned int mScratchMemoryBufferResetCounter;
 
     mutable bool mWorkaroundsInitialized;
     mutable WorkaroundsD3D mWorkarounds;
-
-    DeviceD3D *mEGLDevice;
 };
 
 struct dx_VertexConstants
 {
     float depthRange[4];
     float viewAdjust[4];
     float viewCoords[4];
 };
--- a/gfx/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp
+++ b/gfx/angle/src/libANGLE/renderer/d3d/SurfaceD3D.cpp
@@ -86,17 +86,17 @@ egl::Error SurfaceD3D::initialize()
     return egl::Error(EGL_SUCCESS);
 }
 
 FramebufferImpl *SurfaceD3D::createDefaultFramebuffer(const gl::Framebuffer::Data &data)
 {
     return mRenderer->createFramebuffer(data);
 }
 
-egl::Error SurfaceD3D::bindTexImage(EGLint)
+egl::Error SurfaceD3D::bindTexImage(gl::Texture *, EGLint)
 {
     return egl::Error(EGL_SUCCESS);
 }
 
 egl::Error SurfaceD3D::releaseTexImage(EGLint)
 {
     return egl::Error(EGL_SUCCESS);
 }
--- a/gfx/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h
+++ b/gfx/angle/src/libANGLE/renderer/d3d/SurfaceD3D.h
@@ -33,17 +33,17 @@ class SurfaceD3D : public SurfaceImpl
     void releaseSwapChain();
 
     egl::Error initialize() override;
     FramebufferImpl *createDefaultFramebuffer(const gl::Framebuffer::Data &data) override;
 
     egl::Error swap() override;
     egl::Error postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) override;
     egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override;
-    egl::Error bindTexImage(EGLint buffer) override;
+    egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) override;
     egl::Error releaseTexImage(EGLint buffer) override;
     void setSwapInterval(EGLint interval) override;
 
     EGLint getWidth() const override;
     EGLint getHeight() const override;
 
     EGLint isPostSubBufferSupported() const override;
     EGLint getSwapBehavior() const override;
--- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp
+++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.cpp
@@ -191,16 +191,33 @@ inline unsigned int GetSwizzleIndex(GLen
       case GL_ZERO:  colorIndex = 4; break;
       case GL_ONE:   colorIndex = 5; break;
       default:       UNREACHABLE();  break;
     }
 
     return colorIndex;
 }
 
+D3D11_BLEND_DESC GetAlphaMaskBlendStateDesc()
+{
+    D3D11_BLEND_DESC desc;
+    memset(&desc, 0, sizeof(desc));
+    desc.RenderTarget[0].BlendEnable           = TRUE;
+    desc.RenderTarget[0].SrcBlend              = D3D11_BLEND_ONE;
+    desc.RenderTarget[0].DestBlend             = D3D11_BLEND_ZERO;
+    desc.RenderTarget[0].BlendOp               = D3D11_BLEND_OP_ADD;
+    desc.RenderTarget[0].SrcBlendAlpha         = D3D11_BLEND_ZERO;
+    desc.RenderTarget[0].DestBlendAlpha        = D3D11_BLEND_ZERO;
+    desc.RenderTarget[0].BlendOpAlpha          = D3D11_BLEND_OP_ADD;
+    desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_RED |
+                                                 D3D11_COLOR_WRITE_ENABLE_GREEN |
+                                                 D3D11_COLOR_WRITE_ENABLE_BLUE;
+    return desc;
+}
+
 D3D11_INPUT_ELEMENT_DESC quad2DLayout[] =
 {
     { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
     { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0 },
 };
 
 D3D11_INPUT_ELEMENT_DESC quad3DLayout[] =
 {
@@ -231,16 +248,17 @@ Blit11::Blit11(Renderer11 *renderer)
                "Blit11 2D depth pixel shader"),
       mQuad3DIL(quad3DLayout,
                 ArraySize(quad3DLayout),
                 g_VS_Passthrough3D,
                 ArraySize(g_VS_Passthrough3D),
                 "Blit11 3D input layout"),
       mQuad3DVS(g_VS_Passthrough3D, ArraySize(g_VS_Passthrough3D), "Blit11 3D vertex shader"),
       mQuad3DGS(g_GS_Passthrough3D, ArraySize(g_GS_Passthrough3D), "Blit11 3D geometry shader"),
+      mAlphaMaskBlendState(GetAlphaMaskBlendStateDesc(), "Blit11 Alpha Mask Blend"),
       mSwizzleCB(nullptr)
 {
 }
 
 Blit11::~Blit11()
 {
     freeResources();
 
@@ -724,19 +742,26 @@ gl::Error Blit11::swizzleTexture(ID3D11S
     ID3D11Buffer *const nullBuffer = nullptr;
     deviceContext->IASetVertexBuffers(0, 1, &nullBuffer, &zero, &zero);
 
     mRenderer->markAllStateDirty();
 
     return gl::Error(GL_NO_ERROR);
 }
 
-gl::Error Blit11::copyTexture(ID3D11ShaderResourceView *source, const gl::Box &sourceArea, const gl::Extents &sourceSize,
-                              ID3D11RenderTargetView *dest, const gl::Box &destArea, const gl::Extents &destSize,
-                              const gl::Rectangle *scissor, GLenum destFormat, GLenum filter)
+gl::Error Blit11::copyTexture(ID3D11ShaderResourceView *source,
+                              const gl::Box &sourceArea,
+                              const gl::Extents &sourceSize,
+                              ID3D11RenderTargetView *dest,
+                              const gl::Box &destArea,
+                              const gl::Extents &destSize,
+                              const gl::Rectangle *scissor,
+                              GLenum destFormat,
+                              GLenum filter,
+                              bool maskOffAlpha)
 {
     gl::Error error = initResources();
     if (error.isError())
     {
         return error;
     }
 
     HRESULT result;
@@ -779,17 +804,26 @@ gl::Error Blit11::copyTexture(ID3D11Shad
                                 &stride, &drawCount, &topology);
 
     deviceContext->Unmap(mVertexBuffer, 0);
 
     // Apply vertex buffer
     deviceContext->IASetVertexBuffers(0, 1, &mVertexBuffer, &stride, &startIdx);
 
     // Apply state
-    deviceContext->OMSetBlendState(nullptr, nullptr, 0xFFFFFFF);
+    if (maskOffAlpha)
+    {
+        ID3D11BlendState *blendState = mAlphaMaskBlendState.resolve(mRenderer->getDevice());
+        ASSERT(blendState);
+        deviceContext->OMSetBlendState(blendState, nullptr, 0xFFFFFFF);
+    }
+    else
+    {
+        deviceContext->OMSetBlendState(nullptr, nullptr, 0xFFFFFFF);
+    }
     deviceContext->OMSetDepthStencilState(nullptr, 0xFFFFFFFF);
 
     if (scissor)
     {
         D3D11_RECT scissorRect;
         scissorRect.left = scissor->x;
         scissorRect.right = scissor->x + scissor->width;
         scissorRect.top = scissor->y;
--- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.h
+++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Blit11.h
@@ -24,19 +24,26 @@ class Blit11 : angle::NonCopyable
 {
   public:
     explicit Blit11(Renderer11 *renderer);
     ~Blit11();
 
     gl::Error swizzleTexture(ID3D11ShaderResourceView *source, ID3D11RenderTargetView *dest, const gl::Extents &size,
                              GLenum swizzleRed, GLenum swizzleGreen, GLenum swizzleBlue, GLenum swizzleAlpha);
 
-    gl::Error copyTexture(ID3D11ShaderResourceView *source, const gl::Box &sourceArea, const gl::Extents &sourceSize,
-                          ID3D11RenderTargetView *dest, const gl::Box &destArea, const gl::Extents &destSize,
-                          const gl::Rectangle *scissor, GLenum destFormat, GLenum filter);
+    gl::Error copyTexture(ID3D11ShaderResourceView *source,
+                          const gl::Box &sourceArea,
+                          const gl::Extents &sourceSize,
+                          ID3D11RenderTargetView *dest,
+                          const gl::Box &destArea,
+                          const gl::Extents &destSize,
+                          const gl::Rectangle *scissor,
+                          GLenum destFormat,
+                          GLenum filter,
+                          bool maskOffAlpha);
 
     gl::Error copyStencil(ID3D11Resource *source, unsigned int sourceSubresource, const gl::Box &sourceArea, const gl::Extents &sourceSize,
                           ID3D11Resource *dest, unsigned int destSubresource, const gl::Box &destArea, const gl::Extents &destSize,
                           const gl::Rectangle *scissor);
 
     gl::Error copyDepth(ID3D11ShaderResourceView *source, const gl::Box &sourceArea, const gl::Extents &sourceSize,
                         ID3D11DepthStencilView *dest, const gl::Box &destArea, const gl::Extents &destSize,
                         const gl::Rectangle *scissor);
@@ -162,14 +169,16 @@ class Blit11 : angle::NonCopyable
     d3d11::LazyInputLayout mQuad2DIL;
     d3d11::LazyShader<ID3D11VertexShader> mQuad2DVS;
     d3d11::LazyShader<ID3D11PixelShader> mDepthPS;
 
     d3d11::LazyInputLayout mQuad3DIL;
     d3d11::LazyShader<ID3D11VertexShader> mQuad3DVS;
     d3d11::LazyShader<ID3D11GeometryShader> mQuad3DGS;
 
+    d3d11::LazyBlendState mAlphaMaskBlendState;
+
     ID3D11Buffer *mSwizzleCB;
 };
 
 }
 
 #endif   // LIBANGLE_RENDERER_D3D_D3D11_BLIT11_H_
--- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Clear11.cpp
+++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Clear11.cpp
@@ -224,31 +224,25 @@ gl::Error Clear11::clearFramebuffer(cons
     // To clear the remaining buffers, quads must be drawn containing an int, uint or float vertex color
     // attribute.
 
     gl::Extents framebufferSize;
 
     const gl::FramebufferAttachment *colorAttachment = fboData.getFirstColorAttachment();
     if (colorAttachment != nullptr)
     {
-        framebufferSize.width = colorAttachment->getWidth();
-        framebufferSize.height = colorAttachment->getHeight();
-        framebufferSize.depth = 1;
+        framebufferSize = colorAttachment->getSize();
     }
     else if (depthAttachment != nullptr)
     {
-        framebufferSize.width = depthAttachment->getWidth();
-        framebufferSize.height = depthAttachment->getHeight();
-        framebufferSize.depth = 1;
+        framebufferSize = depthAttachment->getSize();
     }
     else if (stencilAttachment != nullptr)
     {
-        framebufferSize.width = stencilAttachment->getWidth();
-        framebufferSize.height = stencilAttachment->getHeight();
-        framebufferSize.depth = 1;
+        framebufferSize = stencilAttachment->getSize();
     }
     else
     {
         UNREACHABLE();
         return gl::Error(GL_INVALID_OPERATION);
     }
 
     if (clearParams.scissorEnabled && (clearParams.scissor.x >= framebufferSize.width || 
--- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.cpp
+++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.cpp
@@ -86,17 +86,17 @@ gl::Error Framebuffer11::invalidateSwizz
     if (error.isError())
     {
         return error;
     }
 
     return gl::Error(GL_NO_ERROR);
 }
 
-gl::Error Framebuffer11::clear(const gl::State &state, const ClearParameters &clearParams)
+gl::Error Framebuffer11::clear(const gl::Data &data, const ClearParameters &clearParams)
 {
     Clear11 *clearer = mRenderer->getClearer();
     gl::Error error = clearer->clearFramebuffer(clearParams, mData);
     if (error.isError())
     {
         return error;
     }
 
--- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.h
+++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Framebuffer11.h
@@ -24,17 +24,17 @@ class Framebuffer11 : public Framebuffer
     gl::Error discard(size_t count, const GLenum *attachments) override;
     gl::Error invalidate(size_t count, const GLenum *attachments) override;
     gl::Error invalidateSub(size_t count, const GLenum *attachments, const gl::Rectangle &area) override;
 
     // Invalidate the cached swizzles of all bound texture attachments.
     gl::Error invalidateSwizzles() const;
 
   private:
-    gl::Error clear(const gl::State &state, const ClearParameters &clearParams) override;
+    gl::Error clear(const gl::Data &data, const ClearParameters &clearParams) override;
 
     gl::Error readPixelsImpl(const gl::Rectangle &area,
                              GLenum format,
                              GLenum type,
                              size_t outputPitch,
                              const gl::PixelPackState &pack,
                              uint8_t *pixels) const override;
 
--- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.cpp
+++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.cpp
@@ -312,68 +312,21 @@ gl::Error InputLayoutCache::applyVertexB
     }
 
     if (instancedPointSpritesActive)
     {
         layout.flags |= PackedAttributeLayout::FLAG_INSTANCED_SPRITES_ACTIVE;
     }
 
     ID3D11InputLayout *inputLayout = nullptr;
-
-    auto layoutMapIt = mLayoutMap.find(layout);
-    if (layoutMapIt != mLayoutMap.end())
-    {
-        inputLayout = layoutMapIt->second;
-    }
-    else
+    gl::Error error = findInputLayout(layout, inputElementCount, inputElements, programD3D,
+                                      sortedAttributes, unsortedAttributes.size(), &inputLayout);
+    if (error.isError())
     {
-        const gl::InputLayout &shaderInputLayout =
-            GetInputLayout(sortedAttributes, unsortedAttributes.size());
-
-        ShaderExecutableD3D *shader = nullptr;
-        gl::Error error = programD3D->getVertexExecutableForInputLayout(shaderInputLayout, &shader, nullptr);
-        if (error.isError())
-        {
-            return error;
-        }
-
-        ShaderExecutableD3D *shader11 = GetAs<ShaderExecutable11>(shader);
-
-        D3D11_INPUT_ELEMENT_DESC descs[gl::MAX_VERTEX_ATTRIBS];
-        for (unsigned int j = 0; j < inputElementCount; ++j)
-        {
-            descs[j] = inputElements[j];
-        }
-
-        HRESULT result = mDevice->CreateInputLayout(descs, inputElementCount, shader11->getFunction(), shader11->getLength(), &inputLayout);
-        if (FAILED(result))
-        {
-            return gl::Error(GL_OUT_OF_MEMORY, "Failed to create internal input layout, HRESULT: 0x%08x", result);
-        }
-
-        if (mLayoutMap.size() >= mCacheSize)
-        {
-            TRACE("Overflowed the limit of %u input layouts, purging half the cache.", mCacheSize);
-
-            // Randomly release every second element
-            auto it = mLayoutMap.begin();
-            while (it != mLayoutMap.end())
-            {
-                it++;
-                if (it != mLayoutMap.end())
-                {
-                    // Calling std::map::erase invalidates the current iterator, so make a copy.
-                    auto eraseIt = it++;
-                    SafeRelease(eraseIt->second);
-                    mLayoutMap.erase(eraseIt);
-                }
-            }
-        }
-
-        mLayoutMap[layout] = inputLayout;
+        return error;
     }
 
     if (inputLayout != mCurrentIL)
     {
         mDeviceContext->IASetInputLayout(inputLayout);
         mCurrentIL = inputLayout;
     }
 
@@ -401,17 +354,17 @@ gl::Error InputLayoutCache::applyVertexB
             {
                 buffer = vertexBuffer->getBuffer();
             }
             else if (indexedPointSpriteEmulationActive)
             {
                 if (sourceInfo->srcBuffer != nullptr)
                 {
                     const uint8_t *bufferData = nullptr;
-                    gl::Error error = sourceInfo->srcBuffer->getData(&bufferData);
+                    error = sourceInfo->srcBuffer->getData(&bufferData);
                     if (error.isError())
                     {
                         return error;
                     }
                     ASSERT(bufferData != nullptr);
 
                     ptrdiff_t offset = reinterpret_cast<ptrdiff_t>(sourceInfo->srcIndices);
                     sourceInfo->srcBuffer = nullptr;
@@ -556,9 +509,75 @@ gl::Error InputLayoutCache::applyVertexB
         ASSERT(minDiff <= maxDiff && maxDiff < gl::MAX_VERTEX_ATTRIBS);
         mDeviceContext->IASetVertexBuffers(minDiff, maxDiff - minDiff + 1, mCurrentBuffers + minDiff,
                                            mCurrentVertexStrides + minDiff, mCurrentVertexOffsets + minDiff);
     }
 
     return gl::Error(GL_NO_ERROR);
 }
 
+gl::Error InputLayoutCache::findInputLayout(
+    const PackedAttributeLayout &layout,
+    unsigned int inputElementCount,
+    const D3D11_INPUT_ELEMENT_DESC inputElements[gl::MAX_VERTEX_ATTRIBS],
+    ProgramD3D *programD3D,
+    const TranslatedAttribute *sortedAttributes[gl::MAX_VERTEX_ATTRIBS],
+    size_t attributeCount,
+    ID3D11InputLayout **inputLayout)
+{
+    if (inputElementCount == 0)
+    {
+        *inputLayout = nullptr;
+        return gl::Error(GL_NO_ERROR);
+    }
+
+    auto layoutMapIt = mLayoutMap.find(layout);
+    if (layoutMapIt != mLayoutMap.end())
+    {
+        *inputLayout = layoutMapIt->second;
+        return gl::Error(GL_NO_ERROR);
+    }
+
+    const gl::InputLayout &shaderInputLayout = GetInputLayout(sortedAttributes, attributeCount);
+
+    ShaderExecutableD3D *shader = nullptr;
+    gl::Error error =
+        programD3D->getVertexExecutableForInputLayout(shaderInputLayout, &shader, nullptr);
+    if (error.isError())
+    {
+        return error;
+    }
+
+    ShaderExecutableD3D *shader11 = GetAs<ShaderExecutable11>(shader);
+
+    HRESULT result =
+        mDevice->CreateInputLayout(inputElements, inputElementCount, shader11->getFunction(),
+                                   shader11->getLength(), inputLayout);
+    if (FAILED(result))
+    {
+        return gl::Error(GL_OUT_OF_MEMORY,
+                         "Failed to create internal input layout, HRESULT: 0x%08x", result);
+    }
+
+    if (mLayoutMap.size() >= mCacheSize)
+    {
+        TRACE("Overflowed the limit of %u input layouts, purging half the cache.", mCacheSize);
+
+        // Randomly release every second element
+        auto it = mLayoutMap.begin();
+        while (it != mLayoutMap.end())
+        {
+            it++;
+            if (it != mLayoutMap.end())
+            {
+                // Calling std::map::erase invalidates the current iterator, so make a copy.
+                auto eraseIt = it++;
+                SafeRelease(eraseIt->second);
+                mLayoutMap.erase(eraseIt);
+            }
+        }
+    }
+
+    mLayoutMap[layout] = *inputLayout;
+    return gl::Error(GL_NO_ERROR);
 }
+
+}  // namespace rx
--- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.h
+++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/InputLayoutCache.h
@@ -26,16 +26,17 @@ namespace gl
 class Program;
 }
 
 namespace rx
 {
 struct TranslatedAttribute;
 struct TranslatedIndexData;
 struct SourceIndexData;
+class ProgramD3D;
 
 class InputLayoutCache : angle::NonCopyable
 {
   public:
     InputLayoutCache();
     virtual ~InputLayoutCache();
 
     void initialize(ID3D11Device *device, ID3D11DeviceContext *context);
@@ -71,16 +72,24 @@ class InputLayoutCache : angle::NonCopya
             FLAG_INSTANCED_SPRITES_ACTIVE = 0x4,
         };
 
         size_t numAttributes;
         unsigned int flags;
         uint32_t attributeData[gl::MAX_VERTEX_ATTRIBS];
     };
 
+    gl::Error findInputLayout(const PackedAttributeLayout &layout,
+                              unsigned int inputElementCount,
+                              const D3D11_INPUT_ELEMENT_DESC inputElements[gl::MAX_VERTEX_ATTRIBS],
+                              ProgramD3D *programD3D,
+                              const TranslatedAttribute *sortedAttributes[gl::MAX_VERTEX_ATTRIBS],
+                              size_t attributeCount,
+                              ID3D11InputLayout **inputLayout);
+
     std::map<PackedAttributeLayout, ID3D11InputLayout *> mLayoutMap;
 
     ID3D11InputLayout *mCurrentIL;
     ID3D11Buffer *mCurrentBuffers[gl::MAX_VERTEX_ATTRIBS];
     UINT mCurrentVertexStrides[gl::MAX_VERTEX_ATTRIBS];
     UINT mCurrentVertexOffsets[gl::MAX_VERTEX_ATTRIBS];
 
     ID3D11Buffer *mPointSpriteVertexBuffer;
@@ -89,11 +98,11 @@ class InputLayoutCache : angle::NonCopya
     unsigned int mCacheSize;
     unsigned long long mCounter;
 
     ID3D11Device *mDevice;
     ID3D11DeviceContext *mDeviceContext;
     D3D_FEATURE_LEVEL mFeatureLevel;
 };
 
-}
+}  // namespace rx
 
 #endif // LIBANGLE_RENDERER_D3D_D3D11_INPUTLAYOUTCACHE_H_
--- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.cpp
+++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.cpp
@@ -16,16 +16,17 @@
 #include "libANGLE/FramebufferAttachment.h"
 #include "libANGLE/renderer/d3d/FramebufferD3D.h"
 #include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
 #include "libANGLE/renderer/d3d/d3d11/Renderer11.h"
 #include "third_party/murmurhash/MurmurHash3.h"
 
 namespace rx
 {
+using namespace gl_d3d11;
 
 template <typename mapType>
 static void ClearStateMap(mapType &map)
 {
     for (typename mapType::iterator i = map.begin(); i != map.end(); i++)
     {
         SafeRelease(i->second.first);
     }
@@ -292,78 +293,97 @@ std::size_t RenderStateCache::hashDepthS
     return hash;
 }
 
 bool RenderStateCache::compareDepthStencilStates(const gl::DepthStencilState &a, const gl::DepthStencilState &b)
 {
     return memcmp(&a, &b, sizeof(gl::DepthStencilState)) == 0;
 }
 
-gl::Error RenderStateCache::getDepthStencilState(const gl::DepthStencilState &dsState, ID3D11DepthStencilState **outDSState)
+gl::Error RenderStateCache::getDepthStencilState(const gl::DepthStencilState &originalState,
+                                                 bool disableDepth,
+                                                 bool disableStencil,
+                                                 ID3D11DepthStencilState **outDSState)
 {
     if (!mDevice)
     {
         return gl::Error(GL_OUT_OF_MEMORY, "Internal error, RenderStateCache is not initialized.");
     }
 
-    DepthStencilStateMap::iterator keyIter = mDepthStencilStateCache.find(dsState);
+    gl::DepthStencilState glState = originalState;
+    if (disableDepth)
+    {
+        glState.depthTest = false;
+        glState.depthMask = false;
+    }
+
+    if (disableStencil)
+    {
+        glState.stencilWritemask     = 0;
+        glState.stencilBackWritemask = 0;
+        glState.stencilTest          = false;
+    }
+
+    auto keyIter = mDepthStencilStateCache.find(glState);
     if (keyIter != mDepthStencilStateCache.end())
     {
         DepthStencilStateCounterPair &state = keyIter->second;
         state.second = mCounter++;
         *outDSState = state.first;
         return gl::Error(GL_NO_ERROR);
     }
-    else
+
+    if (mDepthStencilStateCache.size() >= kMaxDepthStencilStates)
     {
-        if (mDepthStencilStateCache.size() >= kMaxDepthStencilStates)
-        {
-            TRACE("Overflowed the limit of %u depth stencil states, removing the least recently used "
-                  "to make room.", kMaxDepthStencilStates);
+        TRACE(
+            "Overflowed the limit of %u depth stencil states, removing the least recently used "
+            "to make room.",
+            kMaxDepthStencilStates);
 
-            DepthStencilStateMap::iterator leastRecentlyUsed = mDepthStencilStateCache.begin();
-            for (DepthStencilStateMap::iterator i = mDepthStencilStateCache.begin(); i != mDepthStencilStateCache.end(); i++)
+        auto leastRecentlyUsed = mDepthStencilStateCache.begin();
+        for (auto i = mDepthStencilStateCache.begin(); i != mDepthStencilStateCache.end(); i++)
+        {
+            if (i->second.second < leastRecentlyUsed->second.second)
             {
-                if (i->second.second < leastRecentlyUsed->second.second)
-                {
-                    leastRecentlyUsed = i;
-                }
+                leastRecentlyUsed = i;
             }
-            SafeRelease(leastRecentlyUsed->second.first);
-            mDepthStencilStateCache.erase(leastRecentlyUsed);
         }
+        SafeRelease(leastRecentlyUsed->second.first);
+        mDepthStencilStateCache.erase(leastRecentlyUsed);
+    }
 
-        D3D11_DEPTH_STENCIL_DESC dsDesc = { 0 };
-        dsDesc.DepthEnable = dsState.depthTest ? TRUE : FALSE;
-        dsDesc.DepthWriteMask = gl_d3d11::ConvertDepthMask(dsState.depthMask);
-        dsDesc.DepthFunc = gl_d3d11::ConvertComparison(dsState.depthFunc);
-        dsDesc.StencilEnable = dsState.stencilTest ? TRUE : FALSE;
-        dsDesc.StencilReadMask = gl_d3d11::ConvertStencilMask(dsState.stencilMask);
-        dsDesc.StencilWriteMask = gl_d3d11::ConvertStencilMask(dsState.stencilWritemask);
-        dsDesc.FrontFace.StencilFailOp = gl_d3d11::ConvertStencilOp(dsState.stencilFail);
-        dsDesc.FrontFace.StencilDepthFailOp = gl_d3d11::ConvertStencilOp(dsState.stencilPassDepthFail);
-        dsDesc.FrontFace.StencilPassOp = gl_d3d11::ConvertStencilOp(dsState.stencilPassDepthPass);
-        dsDesc.FrontFace.StencilFunc = gl_d3d11::ConvertComparison(dsState.stencilFunc);
-        dsDesc.BackFace.StencilFailOp = gl_d3d11::ConvertStencilOp(dsState.stencilBackFail);
-        dsDesc.BackFace.StencilDepthFailOp = gl_d3d11::ConvertStencilOp(dsState.stencilBackPassDepthFail);
-        dsDesc.BackFace.StencilPassOp = gl_d3d11::ConvertStencilOp(dsState.stencilBackPassDepthPass);
-        dsDesc.BackFace.StencilFunc = gl_d3d11::ConvertComparison(dsState.stencilBackFunc);
+    D3D11_DEPTH_STENCIL_DESC dsDesc     = {0};
+    dsDesc.DepthEnable                  = glState.depthTest ? TRUE : FALSE;
+    dsDesc.DepthWriteMask               = ConvertDepthMask(glState.depthMask);
+    dsDesc.DepthFunc                    = ConvertComparison(glState.depthFunc);
+    dsDesc.StencilEnable                = glState.stencilTest ? TRUE : FALSE;
+    dsDesc.StencilReadMask              = ConvertStencilMask(glState.stencilMask);
+    dsDesc.StencilWriteMask             = ConvertStencilMask(glState.stencilWritemask);
+    dsDesc.FrontFace.StencilFailOp      = ConvertStencilOp(glState.stencilFail);
+    dsDesc.FrontFace.StencilDepthFailOp = ConvertStencilOp(glState.stencilPassDepthFail);
+    dsDesc.FrontFace.StencilPassOp      = ConvertStencilOp(glState.stencilPassDepthPass);
+    dsDesc.FrontFace.StencilFunc        = ConvertComparison(glState.stencilFunc);
+    dsDesc.BackFace.StencilFailOp       = ConvertStencilOp(glState.stencilBackFail);
+    dsDesc.BackFace.StencilDepthFailOp  = ConvertStencilOp(glState.stencilBackPassDepthFail);
+    dsDesc.BackFace.StencilPassOp       = ConvertStencilOp(glState.stencilBackPassDepthPass);
+    dsDesc.BackFace.StencilFunc         = ConvertComparison(glState.stencilBackFunc);
 
-        ID3D11DepthStencilState *dx11DepthStencilState = NULL;
-        HRESULT result = mDevice->CreateDepthStencilState(&dsDesc, &dx11DepthStencilState);
-        if (FAILED(result) || !dx11DepthStencilState)
-        {
-            return gl::Error(GL_OUT_OF_MEMORY, "Unable to create a ID3D11DepthStencilState, HRESULT: 0x%X.", result);
-        }
+    ID3D11DepthStencilState *dx11DepthStencilState = NULL;
+    HRESULT result = mDevice->CreateDepthStencilState(&dsDesc, &dx11DepthStencilState);
+    if (FAILED(result) || !dx11DepthStencilState)
+    {
+        return gl::Error(GL_OUT_OF_MEMORY,
+                         "Unable to create a ID3D11DepthStencilState, HRESULT: 0x%X.", result);
+    }
 
-        mDepthStencilStateCache.insert(std::make_pair(dsState, std::make_pair(dx11DepthStencilState, mCounter++)));
+    mDepthStencilStateCache.insert(
+        std::make_pair(glState, std::make_pair(dx11DepthStencilState, mCounter++)));
 
-        *outDSState = dx11DepthStencilState;
-        return gl::Error(GL_NO_ERROR);
-    }
+    *outDSState = dx11DepthStencilState;
+    return gl::Error(GL_NO_ERROR);
 }
 
 std::size_t RenderStateCache::hashSamplerState(const gl::SamplerState &samplerState)
 {
     static const unsigned int seed = 0xABCDEF98;
 
     std::size_t hash = 0;
     MurmurHash3_x86_32(&samplerState, sizeof(gl::SamplerState), seed, &hash);
--- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.h
+++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.h
@@ -31,17 +31,20 @@ class RenderStateCache : angle::NonCopya
     RenderStateCache(Renderer11 *renderer);
     virtual ~RenderStateCache();
 
     void initialize(ID3D11Device *device);
     void clear();
 
     gl::Error getBlendState(const gl::Framebuffer *framebuffer, const gl::BlendState &blendState, ID3D11BlendState **outBlendState);
     gl::Error getRasterizerState(const gl::RasterizerState &rasterState, bool scissorEnabled, ID3D11RasterizerState **outRasterizerState);
-    gl::Error getDepthStencilState(const gl::DepthStencilState &dsState, ID3D11DepthStencilState **outDSState);
+    gl::Error getDepthStencilState(const gl::DepthStencilState &dsState,
+                                   bool disableDepth,
+                                   bool disableStencil,
+                                   ID3D11DepthStencilState **outDSState);
     gl::Error getSamplerState(const gl::SamplerState &samplerState, ID3D11SamplerState **outSamplerState);
 
   private:
     Renderer11 *mRenderer;
     unsigned long long mCounter;
 
     // Blend state cache
     struct BlendStateKey
--- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
+++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
@@ -465,18 +465,17 @@ void Renderer11::SRVCache::clear()
 
     memset(&mCurrentSRVs[0], 0, sizeof(SRVRecord) * mCurrentSRVs.size());
     mHighestUsedSRV = 0;
 }
 
 Renderer11::Renderer11(egl::Display *display)
     : RendererD3D(display),
       mStateCache(this),
-      mCurStencilSize(0),
-      mStateManager(this),
+      mStateManager(),
       mLastHistogramUpdateTime(ANGLEPlatformCurrent()->monotonicallyIncreasingTime()),
       mDebug(nullptr)
 {
     mVertexDataManager = NULL;
     mIndexDataManager = NULL;
 
     mLineLoopIB = NULL;
     mTriangleFanIB = NULL;
@@ -495,16 +494,18 @@ Renderer11::Renderer11(egl::Display *dis
     mRenderer11DeviceCaps.supportsConstantBufferOffsets = false;
     mRenderer11DeviceCaps.supportsDXGI1_2 = false;
     mRenderer11DeviceCaps.B5G6R5support = 0;
     mRenderer11DeviceCaps.B4G4R4A4support = 0;
     mRenderer11DeviceCaps.B5G5R5A1support = 0;
 
     mD3d11Module = NULL;
     mDxgiModule = NULL;
+    mCreatedWithDeviceEXT = false;
+    mEGLDevice            = nullptr;
 
     mDevice = NULL;
     mDeviceContext = NULL;
     mDeviceContext1 = NULL;
     mDxgiAdapter = NULL;
     mDxgiFactory = NULL;
 
     mDriverConstantBufferVS = NULL;
@@ -513,146 +514,101 @@ Renderer11::Renderer11(egl::Display *dis
     mAppliedVertexShader = NULL;
     mAppliedGeometryShader = NULL;
     mAppliedPixelShader = NULL;
 
     mAppliedNumXFBBindings = static_cast<size_t>(-1);
 
     ZeroMemory(&mAdapterDescription, sizeof(mAdapterDescription));
 
-    const auto &attributes = mDisplay->getAttributeMap();
-
-    EGLint requestedMajorVersion = attributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, EGL_DONT_CARE);
-    EGLint requestedMinorVersion = attributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, EGL_DONT_CARE);
-
-    if (requestedMajorVersion == EGL_DONT_CARE || requestedMajorVersion >= 11)
+    if (mDisplay->getPlatform() == EGL_PLATFORM_ANGLE_ANGLE)
     {
-        if (requestedMinorVersion == EGL_DONT_CARE || requestedMinorVersion >= 0)
+        const auto &attributes = mDisplay->getAttributeMap();
+
+        EGLint requestedMajorVersion =
+            attributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, EGL_DONT_CARE);
+        EGLint requestedMinorVersion =
+            attributes.get(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, EGL_DONT_CARE);
+
+        if (requestedMajorVersion == EGL_DONT_CARE || requestedMajorVersion >= 11)
         {
-            mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_11_0);
+            if (requestedMinorVersion == EGL_DONT_CARE || requestedMinorVersion >= 0)
+            {
+                mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_11_0);
+            }
+        }
+
+        if (requestedMajorVersion == EGL_DONT_CARE || requestedMajorVersion >= 10)
+        {
+            if (requestedMinorVersion == EGL_DONT_CARE || requestedMinorVersion >= 1)
+            {
+                mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_10_1);
+            }
+            if (requestedMinorVersion == EGL_DONT_CARE || requestedMinorVersion >= 0)
+            {
+                mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_10_0);
+            }
         }
-    }
-
-    if (requestedMajorVersion == EGL_DONT_CARE || requestedMajorVersion >= 10)
-    {
-        if (requestedMinorVersion == EGL_DONT_CARE || requestedMinorVersion >= 1)
+
+        if (requestedMajorVersion == 9 && requestedMinorVersion == 3)
+        {
+            mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_9_3);
+        }
+
+        EGLint requestedDeviceType = attributes.get(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE,
+                                                    EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE);
+        switch (requestedDeviceType)
         {
-            mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_10_1);
-        }
-        if (requestedMinorVersion == EGL_DONT_CARE || requestedMinorVersion >= 0)
-        {
-            mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_10_0);
+            case EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE:
+                mDriverType = D3D_DRIVER_TYPE_HARDWARE;
+                break;
+
+            case EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE:
+                mDriverType = D3D_DRIVER_TYPE_WARP;
+                break;
+
+            case EGL_PLATFORM_ANGLE_DEVICE_TYPE_REFERENCE_ANGLE:
+                mDriverType = D3D_DRIVER_TYPE_REFERENCE;
+                break;
+
+            case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE:
+                mDriverType = D3D_DRIVER_TYPE_NULL;
+                break;
+
+            default:
+                UNREACHABLE();
         }
     }
-
-    if (requestedMajorVersion == 9 && requestedMinorVersion == 3)
-    {
-        mAvailableFeatureLevels.push_back(D3D_FEATURE_LEVEL_9_3);
-    }
-
-    EGLint requestedDeviceType = attributes.get(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE,
-                                                EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE);
-    switch (requestedDeviceType)
+    else if (display->getPlatform() == EGL_PLATFORM_DEVICE_EXT)
     {
-      case EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE:
-        mDriverType = D3D_DRIVER_TYPE_HARDWARE;
-        break;
-
-      case EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE:
-        mDriverType = D3D_DRIVER_TYPE_WARP;
-        break;
-
-      case EGL_PLATFORM_ANGLE_DEVICE_TYPE_REFERENCE_ANGLE:
-        mDriverType = D3D_DRIVER_TYPE_REFERENCE;
-        break;
-
-      case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE:
-        mDriverType = D3D_DRIVER_TYPE_NULL;
-        break;
-
-      default:
-        UNREACHABLE();
+        mEGLDevice = GetImplAs<DeviceD3D>(display->getDevice());
+        ASSERT(mEGLDevice != nullptr);
+        mCreatedWithDeviceEXT = true;
     }
 
     initializeDebugAnnotator();
 }
 
 Renderer11::~Renderer11()
 {
     release();
 }
 
 #ifndef __d3d11_1_h__
 #define D3D11_MESSAGE_ID_DEVICE_DRAW_RENDERTARGETVIEW_NOT_SET ((D3D11_MESSAGE_ID)3146081)
 #endif
 
 egl::Error Renderer11::initialize()
 {
-#if !defined(ANGLE_ENABLE_WINDOWS_STORE)
-    PFN_D3D11_CREATE_DEVICE D3D11CreateDevice = nullptr;
-    {
-        SCOPED_ANGLE_HISTOGRAM_TIMER("GPU.ANGLE.Renderer11InitializeDLLsMS");
-        TRACE_EVENT0("gpu.angle", "Renderer11::initialize (Load DLLs)");
-        mDxgiModule = LoadLibrary(TEXT("dxgi.dll"));
-        mD3d11Module = LoadLibrary(TEXT("d3d11.dll"));
-
-        if (mD3d11Module == nullptr || mDxgiModule == nullptr)
-        {
-            return egl::Error(EGL_NOT_INITIALIZED,
-                              D3D11_INIT_MISSING_DEP,
-                              "Could not load D3D11 or DXGI library.");
-        }
-
-        // create the D3D11 device
-        ASSERT(mDevice == nullptr);
-        D3D11CreateDevice = reinterpret_cast<PFN_D3D11_CREATE_DEVICE>(GetProcAddress(mD3d11Module, "D3D11CreateDevice"));
-
-        if (D3D11CreateDevice == nullptr)
-        {
-            return egl::Error(EGL_NOT_INITIALIZED,
-                              D3D11_INIT_MISSING_DEP,
-                              "Could not retrieve D3D11CreateDevice address.");
-        }
-    }
-#endif
-
     HRESULT result = S_OK;
-#ifdef _DEBUG
+
+    egl::Error error = initializeD3DDevice();
+    if (error.isError())
     {
-        TRACE_EVENT0("gpu.angle", "D3D11CreateDevice (Debug)");
-        result = D3D11CreateDevice(
-            NULL, mDriverType, NULL, D3D11_CREATE_DEVICE_DEBUG, mAvailableFeatureLevels.data(),
-            static_cast<unsigned int>(mAvailableFeatureLevels.size()), D3D11_SDK_VERSION, &mDevice,
-            &(mRenderer11DeviceCaps.featureLevel), &mDeviceContext);
-    }
-
-    if (!mDevice || FAILED(result))
-    {
-        ERR("Failed creating Debug D3D11 device - falling back to release runtime.\n");
-    }
-
-    if (!mDevice || FAILED(result))
-#endif
-    {
-        SCOPED_ANGLE_HISTOGRAM_TIMER("GPU.ANGLE.D3D11CreateDeviceMS");
-        TRACE_EVENT0("gpu.angle", "D3D11CreateDevice");
-
-        result = D3D11CreateDevice(NULL, mDriverType, NULL, 0, mAvailableFeatureLevels.data(),
-                                   static_cast<unsigned int>(mAvailableFeatureLevels.size()),
-                                   D3D11_SDK_VERSION, &mDevice,
-                                   &(mRenderer11DeviceCaps.featureLevel), &mDeviceContext);
-
-        // Cleanup done by destructor
-        if (!mDevice || FAILED(result))
-        {
-            ANGLE_HISTOGRAM_SPARSE_SLOWLY("GPU.ANGLE.D3D11CreateDeviceError", static_cast<int>(result));
-            return egl::Error(EGL_NOT_INITIALIZED,
-                              D3D11_INIT_CREATEDEVICE_ERROR,
-                              "Could not create D3D11 device.");
-        }
+        return error;
     }
 
 #if !defined(ANGLE_ENABLE_WINDOWS_STORE)
 #if !ANGLE_SKIP_DXGI_1_2_CHECK
     {
         TRACE_EVENT0("gpu.angle", "Renderer11::initialize (DXGICheck)");
         // In order to create a swap chain for an HWND owned by another process, DXGI 1.2 is required.
         // The easiest way to check is to query for a IDXGIDevice2.
@@ -791,16 +747,116 @@ egl::Error Renderer11::initialize()
     mDebug = d3d11::DynamicCastComObject<ID3D11Debug>(mDevice);
 #endif
 
     initializeDevice();
 
     return egl::Error(EGL_SUCCESS);
 }
 
+egl::Error Renderer11::initializeD3DDevice()
+{
+    HRESULT result = S_OK;
+
+    if (!mCreatedWithDeviceEXT)
+    {
+#if !defined(ANGLE_ENABLE_WINDOWS_STORE)
+        PFN_D3D11_CREATE_DEVICE D3D11CreateDevice = nullptr;
+        {
+            SCOPED_ANGLE_HISTOGRAM_TIMER("GPU.ANGLE.Renderer11InitializeDLLsMS");
+            TRACE_EVENT0("gpu.angle", "Renderer11::initialize (Load DLLs)");
+            mDxgiModule  = LoadLibrary(TEXT("dxgi.dll"));
+            mD3d11Module = LoadLibrary(TEXT("d3d11.dll"));
+
+            if (mD3d11Module == nullptr || mDxgiModule == nullptr)
+            {
+                return egl::Error(EGL_NOT_INITIALIZED, D3D11_INIT_MISSING_DEP,
+                                  "Could not load D3D11 or DXGI library.");
+            }
+
+            // create the D3D11 device
+            ASSERT(mDevice == nullptr);
+            D3D11CreateDevice = reinterpret_cast<PFN_D3D11_CREATE_DEVICE>(
+                GetProcAddress(mD3d11Module, "D3D11CreateDevice"));
+
+            if (D3D11CreateDevice == nullptr)
+            {
+                return egl::Error(EGL_NOT_INITIALIZED, D3D11_INIT_MISSING_DEP,
+                                  "Could not retrieve D3D11CreateDevice address.");
+            }
+        }
+#endif
+
+#ifdef _DEBUG
+        {
+            TRACE_EVENT0("gpu.angle", "D3D11CreateDevice (Debug)");
+            result = D3D11CreateDevice(
+                NULL, mDriverType, NULL, D3D11_CREATE_DEVICE_DEBUG, mAvailableFeatureLevels.data(),
+                static_cast<unsigned int>(mAvailableFeatureLevels.size()), D3D11_SDK_VERSION,
+                &mDevice, &(mRenderer11DeviceCaps.featureLevel), &mDeviceContext);
+        }
+
+        if (!mDevice || FAILED(result))
+        {
+            ERR("Failed creating Debug D3D11 device - falling back to release runtime.\n");
+        }
+
+        if (!mDevice || FAILED(result))
+#endif
+        {
+            SCOPED_ANGLE_HISTOGRAM_TIMER("GPU.ANGLE.D3D11CreateDeviceMS");
+            TRACE_EVENT0("gpu.angle", "D3D11CreateDevice");
+
+            result = D3D11CreateDevice(NULL, mDriverType, NULL, 0, mAvailableFeatureLevels.data(),
+                                       static_cast<unsigned int>(mAvailableFeatureLevels.size()),
+                                       D3D11_SDK_VERSION, &mDevice,
+                                       &(mRenderer11DeviceCaps.featureLevel), &mDeviceContext);
+
+            // Cleanup done by destructor
+            if (!mDevice || FAILED(result))
+            {
+                ANGLE_HISTOGRAM_SPARSE_SLOWLY("GPU.ANGLE.D3D11CreateDeviceError",
+                                              static_cast<int>(result));
+                return egl::Error(EGL_NOT_INITIALIZED, D3D11_INIT_CREATEDEVICE_ERROR,
+                                  "Could not create D3D11 device.");
+            }
+        }
+    }
+    else
+    {
+        // We should use the inputted D3D11 device instead
+        void *device     = nullptr;
+        egl::Error error = mEGLDevice->getDevice(&device);
+        if (error.isError())
+        {
+            return error;
+        }
+
+        ID3D11Device *d3dDevice = reinterpret_cast<ID3D11Device *>(device);
+        if (FAILED(d3dDevice->GetDeviceRemovedReason()))
+        {
+            return egl::Error(EGL_NOT_INITIALIZED, "Inputted D3D11 device has been lost.");
+        }
+
+        if (d3dDevice->GetFeatureLevel() < D3D_FEATURE_LEVEL_9_3)
+        {
+            return egl::Error(EGL_NOT_INITIALIZED,
+                              "Inputted D3D11 device must be Feature Level 9_3 or greater.");
+        }
+
+        // The Renderer11 adds a ref to the inputted D3D11 device, like D3D11CreateDevice does.
+        mDevice = d3dDevice;
+        mDevice->AddRef();
+        mDevice->GetImmediateContext(&mDeviceContext);
+        mRenderer11DeviceCaps.featureLevel = mDevice->GetFeatureLevel();
+    }
+
+    return egl::Error(EGL_SUCCESS);
+}
+
 // do any one-time device initialization
 // NOTE: this is also needed after a device lost/reset
 // to reset the scene status and ensure the default states are reset.
 void Renderer11::initializeDevice()
 {
     SCOPED_ANGLE_HISTOGRAM_TIMER("GPU.ANGLE.Renderer11InitializeDeviceMS");
     TRACE_EVENT0("gpu.angle", "Renderer11::initializeDevice");
 
@@ -829,16 +885,18 @@ void Renderer11::initializeDevice()
     {
         ASSERT(!mTrim);
         mTrim = new Trim11(this);
     }
 
     ASSERT(!mPixelTransfer);
     mPixelTransfer = new PixelTransfer11(this);
 
+    mStateManager.initialize(mDeviceContext, &mStateCache, &mRenderer11DeviceCaps);
+
     const gl::Caps &rendererCaps = getRendererCaps();
 
     mForceSetVertexSamplerStates.resize(rendererCaps.maxVertexTextureImageUnits);
     mCurVertexSamplerStates.resize(rendererCaps.maxVertexTextureImageUnits);
 
     mForceSetPixelSamplerStates.resize(rendererCaps.maxTextureImageUnits);
     mCurPixelSamplerStates.resize(rendererCaps.maxTextureImageUnits);
 
@@ -1353,216 +1411,66 @@ gl::Error Renderer11::setUniformBuffers(
                 mCurrentConstantBufferPSSize[uniformBufferIndex] = uniformBufferSize;
             }
         }
     }
 
     return gl::Error(GL_NO_ERROR);
 }
 
-gl::Error Renderer11::setRasterizerState(const gl::RasterizerState &rasterState)
+gl::Error Renderer11::updateState(const gl::Data &data, GLenum drawMode)
 {
-    if (mForceSetRasterState || memcmp(&rasterState, &mCurRasterState, sizeof(gl::RasterizerState)) != 0)
+    // Applies the render target surface, depth stencil surface, viewport rectangle and
+    // scissor rectangle to the renderer
+    const gl::Framebuffer *framebufferObject = data.state->getDrawFramebuffer();
+    ASSERT(framebufferObject && framebufferObject->checkStatus(data) == GL_FRAMEBUFFER_COMPLETE);
+    gl::Error error = applyRenderTarget(framebufferObject);
+    if (error.isError())
     {
-        ID3D11RasterizerState *dxRasterState = NULL;
-        gl::Error error = mStateCache.getRasterizerState(rasterState, mScissorEnabled, &dxRasterState);
-        if (error.isError())
-        {
-            return error;
-        }
-
-        mDeviceContext->RSSetState(dxRasterState);
-
-        mCurRasterState = rasterState;
+        return error;
     }
 
-    mForceSetRasterState = false;
-
-    return gl::Error(GL_NO_ERROR);
+    // Setting viewport state
+    mStateManager.setViewport(data.caps, data.state->getViewport(), data.state->getNearPlane(),
+                              data.state->getFarPlane());
+
+    // Setting scissor state
+    mStateManager.setScissorRectangle(data.state->getScissor(), data.state->isScissorTestEnabled());
+
+    // Applying rasterizer state to D3D11 device
+    int samples                    = framebufferObject->getSamples(data);
+    gl::RasterizerState rasterizer = data.state->getRasterizerState();
+    rasterizer.pointDrawMode       = (drawMode == GL_POINTS);
+    rasterizer.multiSample         = (samples != 0);
+
+    error = mStateManager.setRasterizerState(rasterizer);
+    if (error.isError())
+    {
+        return error;
+    }
+
+    // Setting blend state
+    unsigned int mask = GetBlendSampleMask(data, samples);
+    error = mStateManager.setBlendState(framebufferObject, data.state->getBlendState(),
+                                        data.state->getBlendColor(), mask);
+    if (error.isError())
+    {
+        return error;
+    }
+
+    // Setting depth stencil state
+    error = mStateManager.setDepthStencilState(*data.state);
+    return error;
 }
 
 void Renderer11::syncState(const gl::State &state, const gl::State::DirtyBits &bitmask)
 {
     mStateManager.syncState(state, bitmask);
 }
 
-gl::Error Renderer11::setBlendState(const gl::Framebuffer *framebuffer,
-                                    const gl::BlendState &blendState,
-                                    const gl::ColorF &blendColor,
-                                    unsigned int sampleMask)
-{
-    return mStateManager.setBlendState(framebuffer, blendState, blendColor, sampleMask);
-}
-
-gl::Error Renderer11::setDepthStencilState(const gl::DepthStencilState &depthStencilState, int stencilRef,
-                                           int stencilBackRef, bool frontFaceCCW)
-{
-    if (mForceSetDepthStencilState ||
-        memcmp(&depthStencilState, &mCurDepthStencilState, sizeof(gl::DepthStencilState)) != 0 ||
-        stencilRef != mCurStencilRef || stencilBackRef != mCurStencilBackRef)
-    {
-        // get the maximum size of the stencil ref
-        unsigned int maxStencil = 0;
-        if (depthStencilState.stencilTest && mCurStencilSize > 0)
-        {
-            maxStencil = (1 << mCurStencilSize) - 1;
-        }
-        ASSERT((depthStencilState.stencilWritemask & maxStencil) ==
-               (depthStencilState.stencilBackWritemask & maxStencil));
-        ASSERT(stencilRef == stencilBackRef);
-        ASSERT((depthStencilState.stencilMask & maxStencil) ==
-               (depthStencilState.stencilBackMask & maxStencil));
-
-        ID3D11DepthStencilState *dxDepthStencilState = NULL;
-        gl::Error error = mStateCache.getDepthStencilState(depthStencilState, &dxDepthStencilState);
-        if (error.isError())
-        {
-            return error;
-        }
-
-        ASSERT(dxDepthStencilState);
-
-        // Max D3D11 stencil reference value is 0xFF, corresponding to the max 8 bits in a stencil buffer
-        // GL specifies we should clamp the ref value to the nearest bit depth when doing stencil ops
-        static_assert(D3D11_DEFAULT_STENCIL_READ_MASK == 0xFF, "Unexpected value of D3D11_DEFAULT_STENCIL_READ_MASK");
-        static_assert(D3D11_DEFAULT_STENCIL_WRITE_MASK == 0xFF, "Unexpected value of D3D11_DEFAULT_STENCIL_WRITE_MASK");
-        UINT dxStencilRef = std::min<UINT>(stencilRef, 0xFFu);
-
-        mDeviceContext->OMSetDepthStencilState(dxDepthStencilState, dxStencilRef);
-
-        mCurDepthStencilState = depthStencilState;
-        mCurStencilRef = stencilRef;
-        mCurStencilBackRef = stencilBackRef;
-    }
-
-    mForceSetDepthStencilState = false;
-
-    return gl::Error(GL_NO_ERROR);
-}
-
-void Renderer11::setScissorRectangle(const gl::Rectangle &scissor, bool enabled)
-{
-    if (mForceSetScissor || memcmp(&scissor, &mCurScissor, sizeof(gl::Rectangle)) != 0 ||
-        enabled != mScissorEnabled)
-    {
-        if (enabled)
-        {
-            D3D11_RECT rect;
-            rect.left = std::max(0, scissor.x);
-            rect.top = std::max(0, scissor.y);
-            rect.right = scissor.x + std::max(0, scissor.width);
-            rect.bottom = scissor.y + std::max(0, scissor.height);
-
-            mDeviceContext->RSSetScissorRects(1, &rect);
-        }
-
-        if (enabled != mScissorEnabled)
-        {
-            mForceSetRasterState = true;
-        }
-
-        mCurScissor = scissor;
-        mScissorEnabled = enabled;
-    }
-
-    mForceSetScissor = false;
-}
-
-void Renderer11::setViewport(const gl::Rectangle &viewport, float zNear, float zFar, GLenum drawMode, GLenum frontFace,
-                             bool ignoreViewport)
-{
-    gl::Rectangle actualViewport = viewport;
-    float actualZNear = gl::clamp01(zNear);
-    float actualZFar = gl::clamp01(zFar);
-    if (ignoreViewport)
-    {
-        actualViewport.x = 0;
-        actualViewport.y = 0;
-        actualViewport.width  = static_cast<int>(mRenderTargetDesc.width);
-        actualViewport.height = static_cast<int>(mRenderTargetDesc.height);
-        actualZNear = 0.0f;
-        actualZFar = 1.0f;
-    }
-
-    bool viewportChanged = mForceSetViewport || memcmp(&actualViewport, &mCurViewport, sizeof(gl::Rectangle)) != 0 ||
-                           actualZNear != mCurNear || actualZFar != mCurFar;
-
-    if (viewportChanged)
-    {
-        const gl::Caps& caps = getRendererCaps();
-
-        int dxMaxViewportBoundsX = static_cast<int>(caps.maxViewportWidth);
-        int dxMaxViewportBoundsY = static_cast<int>(caps.maxViewportHeight);
-        int dxMinViewportBoundsX = -dxMaxViewportBoundsX;
-        int dxMinViewportBoundsY = -dxMaxViewportBoundsY;
-
-        if (mRenderer11DeviceCaps.featureLevel <= D3D_FEATURE_LEVEL_9_3)
-        {
-            // Feature Level 9 viewports shouldn't exceed the dimensions of the rendertarget.
-            dxMaxViewportBoundsX = static_cast<int>(mRenderTargetDesc.width);
-            dxMaxViewportBoundsY = static_cast<int>(mRenderTargetDesc.height);
-            dxMinViewportBoundsX = 0;
-            dxMinViewportBoundsY = 0;
-        }
-
-        int dxViewportTopLeftX = gl::clamp(actualViewport.x, dxMinViewportBoundsX, dxMaxViewportBoundsX);
-        int dxViewportTopLeftY = gl::clamp(actualViewport.y, dxMinViewportBoundsY, dxMaxViewportBoundsY);
-        int dxViewportWidth =    gl::clamp(actualViewport.width, 0, dxMaxViewportBoundsX - dxViewportTopLeftX);
-        int dxViewportHeight =   gl::clamp(actualViewport.height, 0, dxMaxViewportBoundsY - dxViewportTopLeftY);
-
-        D3D11_VIEWPORT dxViewport;
-        dxViewport.TopLeftX = static_cast<float>(dxViewportTopLeftX);
-        dxViewport.TopLeftY = static_cast<float>(dxViewportTopLeftY);
-        dxViewport.Width =    static_cast<float>(dxViewportWidth);
-        dxViewport.Height =   static_cast<float>(dxViewportHeight);
-        dxViewport.MinDepth = actualZNear;
-        dxViewport.MaxDepth = actualZFar;
-
-        mDeviceContext->RSSetViewports(1, &dxViewport);
-
-        mCurViewport = actualViewport;
-        mCurNear = actualZNear;
-        mCurFar = actualZFar;
-
-        // On Feature Level 9_*, we must emulate large and/or negative viewports in the shaders using viewAdjust (like the D3D9 renderer).
-        if (mRenderer11DeviceCaps.featureLevel <= D3D_FEATURE_LEVEL_9_3)
-        {
-            mVertexConstants.viewAdjust[0] = static_cast<float>((actualViewport.width  - dxViewportWidth)  + 2 * (actualViewport.x - dxViewportTopLeftX)) / dxViewport.Width;
-            mVertexConstants.viewAdjust[1] = static_cast<float>((actualViewport.height - dxViewportHeight) + 2 * (actualViewport.y - dxViewportTopLeftY)) / dxViewport.Height;
-            mVertexConstants.viewAdjust[2] = static_cast<float>(actualViewport.width) / dxViewport.Width;
-            mVertexConstants.viewAdjust[3] = static_cast<float>(actualViewport.height) / dxViewport.Height;
-        }
-
-        mPixelConstants.viewCoords[0] = actualViewport.width  * 0.5f;
-        mPixelConstants.viewCoords[1] = actualViewport.height * 0.5f;
-        mPixelConstants.viewCoords[2] = actualViewport.x + (actualViewport.width  * 0.5f);
-        mPixelConstants.viewCoords[3] = actualViewport.y + (actualViewport.height * 0.5f);
-
-        // Instanced pointsprite emulation requires ViewCoords to be defined in the
-        // the vertex shader.
-        mVertexConstants.viewCoords[0] = mPixelConstants.viewCoords[0];
-        mVertexConstants.viewCoords[1] = mPixelConstants.viewCoords[1];
-        mVertexConstants.viewCoords[2] = mPixelConstants.viewCoords[2];
-        mVertexConstants.viewCoords[3] = mPixelConstants.viewCoords[3];
-
-        mPixelConstants.depthFront[0] = (actualZFar - actualZNear) * 0.5f;
-        mPixelConstants.depthFront[1] = (actualZNear + actualZFar) * 0.5f;
-
-        mVertexConstants.depthRange[0] = actualZNear;
-        mVertexConstants.depthRange[1] = actualZFar;
-        mVertexConstants.depthRange[2] = actualZFar - actualZNear;
-
-        mPixelConstants.depthRange[0] = actualZNear;
-        mPixelConstants.depthRange[1] = actualZFar;
-        mPixelConstants.depthRange[2] = actualZFar - actualZNear;
-    }
-
-    mForceSetViewport = false;
-}
-
 bool Renderer11::applyPrimitiveType(GLenum mode, GLsizei count, bool usesPointSize)
 {
     D3D11_PRIMITIVE_TOPOLOGY primitiveTopology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
 
     GLsizei minCount = 0;
 
     switch (mode)
     {
@@ -1630,17 +1538,18 @@ gl::Error Renderer11::applyRenderTarget(
 
         if (colorbuffer)
         {
             // the draw buffer must be either "none", "back" for the default buffer or the same index as this color (in order)
 
             // check for zero-sized default framebuffer, which is a special case.
             // in this case we do not wish to modify any state and just silently return false.
             // this will not report any gl error but will cause the calling method to return.
-            if (colorbuffer->getWidth() == 0 || colorbuffer->getHeight() == 0)
+            const gl::Extents &size = colorbuffer->getSize();
+            if (size.width == 0 || size.height == 0)
             {
                 return gl::Error(GL_NO_ERROR);
             }
 
             // Extract the render target dimensions and view
             RenderTarget11 *renderTarget = NULL;
             gl::Error error = colorbuffer->getRenderTarget(&renderTarget);
             if (error.isError())
@@ -1691,63 +1600,52 @@ gl::Error Renderer11::applyRenderTarget(
         ASSERT(framebufferDSV);
 
         // If there is no render buffer, the width, height and format values come from
         // the depth stencil
         if (missingColorRenderTarget)
         {
             renderTargetWidth = depthStencilRenderTarget->getWidth();
             renderTargetHeight = depthStencilRenderTarget->getHeight();
-            renderTargetFormat = depthStencilRenderTarget->getDXGIFormat();
         }
 
         // Unbind render target SRVs from the shader here to prevent D3D11 warnings.
         if (depthStencil->type() == GL_TEXTURE)
         {
             uintptr_t depthStencilResource = reinterpret_cast<uintptr_t>(GetViewResource(framebufferDSV));
             const gl::ImageIndex &index = depthStencil->getTextureImageIndex();
             // The index doesn't need to be corrected for the small compressed texture workaround
             // because a rendertarget is never compressed.
             unsetConflictingSRVs(gl::SAMPLER_VERTEX, depthStencilResource, index);
             unsetConflictingSRVs(gl::SAMPLER_PIXEL, depthStencilResource, index);
         }
 
         unsigned int stencilSize = depthStencil->getStencilSize();
-        if (!mDepthStencilInitialized || stencilSize != mCurStencilSize)
-        {
-            mCurStencilSize            = stencilSize;
-            mForceSetDepthStencilState = true;
-        }
+        mStateManager.updateStencilSizeIfChanged(mDepthStencilInitialized, stencilSize);
     }
 
     // Apply the render target and depth stencil
-    if (!mRenderTargetDescInitialized || !mDepthStencilInitialized ||
+    if (!mDepthStencilInitialized ||
         memcmp(framebufferRTVs, mAppliedRTVs, sizeof(framebufferRTVs)) != 0 ||
         reinterpret_cast<uintptr_t>(framebufferDSV) != mAppliedDSV)
     {
         mDeviceContext->OMSetRenderTargets(getRendererCaps().maxDrawBuffers, framebufferRTVs, framebufferDSV);
-
-        mRenderTargetDesc.width = renderTargetWidth;
-        mRenderTargetDesc.height = renderTargetHeight;
-        mRenderTargetDesc.format = renderTargetFormat;
-        mForceSetViewport = true;
-        mForceSetScissor = true;
+        mStateManager.setViewportBounds(renderTargetWidth, renderTargetHeight);
         mStateManager.forceSetBlendState();
 
         if (!mDepthStencilInitialized)
         {
-            mForceSetRasterState = true;
+            mStateManager.forceSetRasterState();
         }
 
         for (size_t rtIndex = 0; rtIndex < ArraySize(framebufferRTVs); rtIndex++)
         {
             mAppliedRTVs[rtIndex] = reinterpret_cast<uintptr_t>(framebufferRTVs[rtIndex]);
         }
         mAppliedDSV = reinterpret_cast<uintptr_t>(framebufferDSV);
-        mRenderTargetDescInitialized = true;
         mDepthStencilInitialized = true;
     }
 
     const Framebuffer11 *framebuffer11 = GetImplAs<Framebuffer11>(framebuffer);
     gl::Error error = framebuffer11->invalidateSwizzles();
     if (error.isError())
     {
         return error;
@@ -2450,33 +2348,37 @@ gl::Error Renderer11::applyUniforms(cons
         ASSERT(SUCCEEDED(result));
         if (FAILED(result))
         {
             return gl::Error(GL_OUT_OF_MEMORY, "Failed to create pixel shader constant buffer, result: 0x%X.", result);
         }
         mDeviceContext->PSSetConstantBuffers(1, 1, &mDriverConstantBufferPS);
     }
 
-    if (memcmp(&mVertexConstants, &mAppliedVertexConstants, sizeof(dx_VertexConstants)) != 0)
+    const dx_VertexConstants &vertexConstants = mStateManager.getVertexConstants();
+    if (memcmp(&vertexConstants, &mAppliedVertexConstants, sizeof(dx_VertexConstants)) != 0)
     {
         ASSERT(mDriverConstantBufferVS != nullptr);
         if (mDriverConstantBufferVS)
         {
-            mDeviceContext->UpdateSubresource(mDriverConstantBufferVS, 0, NULL, &mVertexConstants, 16, 0);
-            memcpy(&mAppliedVertexConstants, &mVertexConstants, sizeof(dx_VertexConstants));
+            mDeviceContext->UpdateSubresource(mDriverConstantBufferVS, 0, NULL, &vertexConstants,
+                                              16, 0);
+            memcpy(&mAppliedVertexConstants, &vertexConstants, sizeof(dx_VertexConstants));
         }
     }
 
-    if (memcmp(&mPixelConstants, &mAppliedPixelConstants, sizeof(dx_PixelConstants)) != 0)
+    const dx_PixelConstants &pixelConstants = mStateManager.getPixelConstants();
+    if (memcmp(&pixelConstants, &mAppliedPixelConstants, sizeof(dx_PixelConstants)) != 0)
     {
         ASSERT(mDriverConstantBufferPS != nullptr);
         if (mDriverConstantBufferPS)
         {
-            mDeviceContext->UpdateSubresource(mDriverConstantBufferPS, 0, NULL, &mPixelConstants, 16, 0);
-            memcpy(&mAppliedPixelConstants, &mPixelConstants, sizeof(dx_PixelConstants));
+            mDeviceContext->UpdateSubresource(mDriverConstantBufferPS, 0, NULL, &pixelConstants, 16,
+                                              0);
+            memcpy(&mAppliedPixelConstants, &pixelConstants, sizeof(dx_PixelConstants));
         }
     }
 
     // GSSetConstantBuffers triggers device removal on 9_3, so we should only call it if necessary
     if (programD3D.usesGeometryShader(drawMode))
     {
         // needed for the point sprite geometry shader
         if (mCurrentGeometryConstantBuffer != mDriverConstantBufferPS)
@@ -2498,17 +2400,16 @@ void Renderer11::markAllStateDirty()
     TRACE_EVENT0("gpu.angle", "Renderer11::markAllStateDirty");
 
     for (size_t rtIndex = 0; rtIndex < ArraySize(mAppliedRTVs); rtIndex++)
     {
         mAppliedRTVs[rtIndex] = DirtyPointer;
     }
     mAppliedDSV = DirtyPointer;
     mDepthStencilInitialized = false;
-    mRenderTargetDescInitialized = false;
 
     // We reset the current SRV data because it might not be in sync with D3D's state
     // anymore. For example when a currently used SRV is used as an RTV, D3D silently
     // remove it from its state.
     mCurVertexSRVs.clear();
     mCurPixelSRVs.clear();
 
     ASSERT(mForceSetVertexSamplerStates.size() == mCurVertexSRVs.size());
@@ -2519,20 +2420,20 @@ void Renderer11::markAllStateDirty()
 
     ASSERT(mForceSetPixelSamplerStates.size() == mCurPixelSRVs.size());
     for (size_t fsamplerId = 0; fsamplerId < mForceSetPixelSamplerStates.size(); ++fsamplerId)
     {
         mForceSetPixelSamplerStates[fsamplerId] = true;
     }
 
     mStateManager.forceSetBlendState();
-    mForceSetRasterState = true;
-    mForceSetDepthStencilState = true;
-    mForceSetScissor = true;
-    mForceSetViewport = true;
+    mStateManager.forceSetDepthStencilState();
+    mStateManager.forceSetRasterState();
+    mStateManager.forceSetScissorState();
+    mStateManager.forceSetViewportState();
 
     mAppliedIB = NULL;
     mAppliedIBFormat = DXGI_FORMAT_UNKNOWN;
     mAppliedIBOffset = 0;
 
     mAppliedVertexShader = DirtyPointer;
     mAppliedGeometryShader = DirtyPointer;
     mAppliedPixelShader = DirtyPointer;
@@ -2650,16 +2551,23 @@ bool Renderer11::testDeviceResettable()
 }
 
 void Renderer11::release()
 {
     RendererD3D::cleanup();
 
     releaseDeviceResources();
 
+    if (!mCreatedWithDeviceEXT)
+    {
+        // Only delete the device if the Renderer11 owns it
+        // Otherwise we should keep it around in case we try to reinitialize the renderer later
+        SafeDelete(mEGLDevice);
+    }
+
     SafeRelease(mDxgiFactory);
     SafeRelease(mDxgiAdapter);
 
     SafeRelease(mDeviceContext1);
 
     if (mDeviceContext)
     {
         mDeviceContext->ClearState();
@@ -2875,17 +2783,18 @@ gl::Error Renderer11::copyImage2D(const 
     gl::Box sourceArea(sourceRect.x, sourceRect.y, 0, sourceRect.width, sourceRect.height, 1);
     gl::Extents sourceSize(sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1);
 
     gl::Box destArea(destOffset.x, destOffset.y, 0, sourceRect.width, sourceRect.height, 1);
     gl::Extents destSize(destRenderTarget->getWidth(), destRenderTarget->getHeight(), 1);
 
     // Use nearest filtering because source and destination are the same size for the direct
     // copy
-    error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, destFormat, GL_NEAREST);
+    error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL,
+                               destFormat, GL_NEAREST, false);
     if (error.isError())
     {
         return error;
     }
 
     storage11->invalidateSwizzleCacheLevel(level);
 
     return gl::Error(GL_NO_ERROR);
@@ -2926,17 +2835,18 @@ gl::Error Renderer11::copyImageCube(cons
     gl::Box sourceArea(sourceRect.x, sourceRect.y, 0, sourceRect.width, sourceRect.height, 1);
     gl::Extents sourceSize(sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1);
 
     gl::Box destArea(destOffset.x, destOffset.y, 0, sourceRect.width, sourceRect.height, 1);
     gl::Extents destSize(destRenderTarget->getWidth(), destRenderTarget->getHeight(), 1);
 
     // Use nearest filtering because source and destination are the same size for the direct
     // copy
-    error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, destFormat, GL_NEAREST);
+    error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL,
+                               destFormat, GL_NEAREST, false);
     if (error.isError())
     {
         return error;
     }
 
     storage11->invalidateSwizzleCacheLevel(level);
 
     return gl::Error(GL_NO_ERROR);
@@ -2977,17 +2887,18 @@ gl::Error Renderer11::copyImage3D(const 
     gl::Box sourceArea(sourceRect.x, sourceRect.y, 0, sourceRect.width, sourceRect.height, 1);
     gl::Extents sourceSize(sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1);
 
     gl::Box destArea(destOffset.x, destOffset.y, 0, sourceRect.width, sourceRect.height, 1);
     gl::Extents destSize(destRenderTarget->getWidth(), destRenderTarget->getHeight(), 1);
 
     // Use nearest filtering because source and destination are the same size for the direct
     // copy
-    error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, destFormat, GL_NEAREST);
+    error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL,
+                               destFormat, GL_NEAREST, false);
     if (error.isError())
     {
         return error;
     }
 
     storage11->invalidateSwizzleCacheLevel(level);
 
     return gl::Error(GL_NO_ERROR);
@@ -3028,17 +2939,18 @@ gl::Error Renderer11::copyImage2DArray(c
     gl::Box sourceArea(sourceRect.x, sourceRect.y, 0, sourceRect.width, sourceRect.height, 1);
     gl::Extents sourceSize(sourceRenderTarget->getWidth(), sourceRenderTarget->getHeight(), 1);
 
     gl::Box destArea(destOffset.x, destOffset.y, 0, sourceRect.width, sourceRect.height, 1);
     gl::Extents destSize(destRenderTarget->getWidth(), destRenderTarget->getHeight(), 1);
 
     // Use nearest filtering because source and destination are the same size for the direct
     // copy
-    error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL, destFormat, GL_NEAREST);
+    error = mBlit->copyTexture(source, sourceArea, sourceSize, dest, destArea, destSize, NULL,
+                               destFormat, GL_NEAREST, false);
     if (error.isError())
     {
         return error;
     }
 
     storage11->invalidateSwizzleCacheLevel(level);
 
     return gl::Error(GL_NO_ERROR);
@@ -3797,19 +3709,25 @@ gl::Error Renderer11::packPixels(ID3D11T
         }
     }
 
     mDeviceContext->Unmap(readTexture, 0);
 
     return gl::Error(GL_NO_ERROR);
 }
 
-gl::Error Renderer11::blitRenderbufferRect(const gl::Rectangle &readRect, const gl::Rectangle &drawRect, RenderTargetD3D *readRenderTarget,
-                                           RenderTargetD3D *drawRenderTarget, GLenum filter, const gl::Rectangle *scissor,
-                                           bool colorBlit, bool depthBlit, bool stencilBlit)
+gl::Error Renderer11::blitRenderbufferRect(const gl::Rectangle &readRectIn,
+                                           const gl::Rectangle &drawRectIn,
+                                           RenderTargetD3D *readRenderTarget,
+                                           RenderTargetD3D *drawRenderTarget,
+                                           GLenum filter,
+                                           const gl::Rectangle *scissor,
+                                           bool colorBlit,
+                                           bool depthBlit,
+                                           bool stencilBlit)
 {
     // Since blitRenderbufferRect is called for each render buffer that needs to be blitted,
     // it should never be the case that both color and depth/stencil need to be blitted at
     // at the same time.
     ASSERT(colorBlit != (depthBlit || stencilBlit));
 
     RenderTarget11 *drawRenderTarget11 = GetAs<RenderTarget11>(drawRenderTarget);
     if (!drawRenderTarget)
@@ -3865,41 +3783,121 @@ gl::Error Renderer11::blitRenderbufferRe
         SafeRelease(readTexture);
         SafeRelease(readSRV);
         return gl::Error(GL_OUT_OF_MEMORY, "Failed to retrieve the internal read render target view from the read render target.");
     }
 
     gl::Extents readSize(readRenderTarget->getWidth(), readRenderTarget->getHeight(), 1);
     gl::Extents drawSize(drawRenderTarget->getWidth(), drawRenderTarget->getHeight(), 1);
 
+    // From the spec:
+    // "The actual region taken from the read framebuffer is limited to the intersection of the
+    // source buffers being transferred, which may include the color buffer selected by the read
+    // buffer, the depth buffer, and / or the stencil buffer depending on mask."
+    // This means negative x and y are out of bounds, and not to be read from. We handle this here
+    // by internally scaling the read and draw rectangles.
+    gl::Rectangle readRect = readRectIn;
+    gl::Rectangle drawRect = drawRectIn;
+    auto readToDrawX       = [&drawRectIn, &readRectIn](int readOffset)
+    {
+        double readToDrawScale =
+            static_cast<double>(drawRectIn.width) / static_cast<double>(readRectIn.width);
+        return static_cast<int>(round(static_cast<double>(readOffset) * readToDrawScale));
+    };
+    if (readRect.x < 0)
+    {
+        int readOffset = -readRect.x;
+        readRect.x += readOffset;
+        readRect.width -= readOffset;
+
+        int drawOffset = readToDrawX(readOffset);
+        drawRect.x += drawOffset;
+        drawRect.width -= drawOffset;
+    }
+
+    auto readToDrawY = [&drawRectIn, &readRectIn](int readOffset)
+    {
+        double readToDrawScale =
+            static_cast<double>(drawRectIn.height) / static_cast<double>(readRectIn.height);
+        return static_cast<int>(round(static_cast<double>(readOffset) * readToDrawScale));
+    };
+    if (readRect.y < 0)
+    {
+        int readOffset = -readRect.y;
+        readRect.y += readOffset;
+        readRect.height -= readOffset;
+
+        int drawOffset = readToDrawY(readOffset);
+        drawRect.y += drawOffset;
+        drawRect.height -= drawOffset;
+    }
+
+    if (readRect.x1() < 0)
+    {
+        int readOffset = -readRect.x1();
+        readRect.width += readOffset;
+
+        int drawOffset = readToDrawX(readOffset);
+        drawRect.width += drawOffset;
+    }
+
+    if (readRect.y1() < 0)
+    {
+        int readOffset = -readRect.y1();
+        readRect.height += readOffset;
+
+        int drawOffset = readToDrawY(readOffset);
+        drawRect.height += drawOffset;
+    }
+
     bool scissorNeeded = scissor && gl::ClipRectangle(drawRect, *scissor, NULL);
 
-    bool wholeBufferCopy = !scissorNeeded &&
-                           readRect.x == 0 && readRect.width == readSize.width &&
-                           readRect.y == 0 && readRect.height == readSize.height &&
-                           drawRect.x == 0 && drawRect.width == drawSize.width &&
-                           drawRect.y == 0 && drawRect.height == drawSize.height;
+    const auto &destFormatInfo = gl::GetInternalFormatInfo(drawRenderTarget->getInternalFormat());
+    const auto &srcFormatInfo  = gl::GetInternalFormatInfo(readRenderTarget->getInternalFormat());
+    const auto &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(drawRenderTarget11->getDXGIFormat());
+
+    // Some blits require masking off emulated texture channels. eg: from RGBA8 to RGB8, we
+    // emulate RGB8 with RGBA8, so we need to mask off the alpha channel when we copy.
+
+    gl::Color<bool> colorMask;
+    colorMask.red = (srcFormatInfo.redBits > 0) && (destFormatInfo.redBits == 0) &&
+                    (dxgiFormatInfo.redBits > 0);
+    colorMask.green = (srcFormatInfo.greenBits > 0) && (destFormatInfo.greenBits == 0) &&
+                      (dxgiFormatInfo.greenBits > 0);
+    colorMask.blue = (srcFormatInfo.blueBits > 0) && (destFormatInfo.blueBits == 0) &&
+                     (dxgiFormatInfo.blueBits > 0);
+    colorMask.alpha = (srcFormatInfo.alphaBits > 0) && (destFormatInfo.alphaBits == 0) &&
+                      (dxgiFormatInfo.alphaBits > 0);
+
+    // We only currently support masking off the alpha channel.
+    bool colorMaskingNeeded = colorMask.alpha;
+    ASSERT(!colorMask.red && !colorMask.green && !colorMask.blue);
+
+    bool wholeBufferCopy = !scissorNeeded && !colorMaskingNeeded && readRect.x == 0 &&
+                           readRect.width == readSize.width && readRect.y == 0 &&
+                           readRect.height == readSize.height && drawRect.x == 0 &&
+                           drawRect.width == drawSize.width && drawRect.y == 0 &&
+                           drawRect.height == drawSize.height;
 
     bool stretchRequired = readRect.width != drawRect.width || readRect.height != drawRect.height;
 
     bool flipRequired = readRect.width < 0 || readRect.height < 0 || drawRect.width < 0 || drawRect.height < 0;
 
     bool outOfBounds = readRect.x < 0 || readRect.x + readRect.width > readSize.width ||
                        readRect.y < 0 || readRect.y + readRect.height > readSize.height ||
                        drawRect.x < 0 || drawRect.x + drawRect.width > drawSize.width ||
                        drawRect.y < 0 || drawRect.y + drawRect.height > drawSize.height;
 
-    const d3d11::DXGIFormat &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(drawRenderTarget11->getDXGIFormat());
     bool partialDSBlit = (dxgiFormatInfo.depthBits > 0 && depthBlit) != (dxgiFormatInfo.stencilBits > 0 && stencilBlit);
 
     gl::Error result(GL_NO_ERROR);
 
     if (readRenderTarget11->getDXGIFormat() == drawRenderTarget11->getDXGIFormat() &&
         !stretchRequired && !outOfBounds && !flipRequired && !partialDSBlit &&
-        (!(depthBlit || stencilBlit) || wholeBufferCopy))
+        !colorMaskingNeeded && (!(depthBlit || stencilBlit) || wholeBufferCopy))
     {
         UINT dstX = drawRect.x;
         UINT dstY = drawRect.y;
 
         D3D11_BOX readBox;
         readBox.left = readRect.x;
         readBox.right = readRect.x + readRect.width;
         readBox.top = readRect.y;
@@ -3959,19 +3957,20 @@ gl::Error Renderer11::blitRenderbufferRe
         else if (stencilBlit)
         {
             result = mBlit->copyStencil(readTexture, readSubresource, readArea, readSize,
                                         drawTexture, drawSubresource, drawArea, drawSize,
                                         scissor);
         }
         else
         {
-            GLenum format = gl::GetInternalFormatInfo(drawRenderTarget->getInternalFormat()).format;
+            // We don't currently support masking off any other channel than alpha
+            bool maskOffAlpha = colorMaskingNeeded && colorMask.alpha;
             result = mBlit->copyTexture(readSRV, readArea, readSize, drawRTV, drawArea, drawSize,
-                                        scissor, format, filter);
+                                        scissor, destFormatInfo.format, filter, maskOffAlpha);
         }
     }
 
     SafeRelease(readTexture);
     SafeRelease(readSRV);
 
     return result;
 }
@@ -4165,19 +4164,28 @@ gl::Error Renderer11::clearTextures(gl::
     for (size_t samplerIndex = rangeStart; samplerIndex < rangeEnd; ++samplerIndex)
     {
         currentSRVs.update(samplerIndex, nullptr);
     }
 
     return gl::Error(GL_NO_ERROR);
 }
 
-egl::Error Renderer11::initializeEGLDevice(DeviceD3D **outDevice)
+egl::Error Renderer11::getEGLDevice(DeviceImpl **device)
 {
-    if (*outDevice == nullptr)
+    if (mEGLDevice == nullptr)
     {
         ASSERT(mDevice != nullptr);
-        *outDevice = new DeviceD3D(reinterpret_cast<void *>(mDevice), EGL_D3D11_DEVICE_ANGLE);
+        mEGLDevice       = new DeviceD3D();
+        egl::Error error = mEGLDevice->initialize(reinterpret_cast<void *>(mDevice),
+                                                  EGL_D3D11_DEVICE_ANGLE, EGL_FALSE);
+
+        if (error.isError())
+        {
+            SafeDelete(mEGLDevice);
+            return error;
+        }
     }
 
+    *device = static_cast<DeviceImpl *>(mEGLDevice);
     return egl::Error(EGL_SUCCESS);
 }
 }
--- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
+++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/Renderer11.h
@@ -116,28 +116,17 @@ class Renderer11 : public RendererD3D
     virtual gl::Error generateSwizzle(gl::Texture *texture);
     virtual gl::Error setSamplerState(gl::SamplerType type, int index, gl::Texture *texture, const gl::SamplerState &sampler);
     virtual gl::Error setTexture(gl::SamplerType type, int index, gl::Texture *texture);
 
     gl::Error setUniformBuffers(const gl::Data &data,
                                 const std::vector<GLint> &vertexUniformBuffers,
                                 const std::vector<GLint> &fragmentUniformBuffers) override;
 
-    virtual gl::Error setRasterizerState(const gl::RasterizerState &rasterState);
-    gl::Error setBlendState(const gl::Framebuffer *framebuffer,
-                            const gl::BlendState &blendState,
-                            const gl::ColorF &blendColor,
-                            unsigned int sampleMask) override;
-
-    virtual gl::Error setDepthStencilState(const gl::DepthStencilState &depthStencilState, int stencilRef,
-                                           int stencilBackRef, bool frontFaceCCW);
-
-    virtual void setScissorRectangle(const gl::Rectangle &scissor, bool enabled);
-    virtual void setViewport(const gl::Rectangle &viewport, float zNear, float zFar, GLenum drawMode, GLenum frontFace,
-                             bool ignoreViewport);
+    gl::Error updateState(const gl::Data &data, GLenum drawMode) override;
 
     virtual bool applyPrimitiveType(GLenum mode, GLsizei count, bool usesPointSize);
     gl::Error applyRenderTarget(const gl::Framebuffer *frameBuffer) override;
     gl::Error applyUniforms(const ProgramD3D &programD3D,
                             GLenum drawMode,
                             const std::vector<D3DUniform *> &uniformArray) override;
     virtual gl::Error applyVertexBuffer(const gl::State &state, GLenum mode, GLint first, GLsizei count, GLsizei instances, SourceIndexData *sourceIndexInfo);
     gl::Error applyIndexBuffer(const gl::Data &data,
@@ -280,23 +269,23 @@ class Renderer11 : public RendererD3D
     const Renderer11DeviceCaps &getRenderer11DeviceCaps() { return mRenderer11DeviceCaps; };
 
     RendererClass getRendererClass() const override { return RENDERER_D3D11; }
     InputLayoutCache *getInputLayoutCache() { return &mInputLayoutCache; }
 
     void onSwap();
     void onBufferDelete(const Buffer11 *deleted);
 
+    egl::Error getEGLDevice(DeviceImpl **device) override;
+
   protected:
     void createAnnotator() override;
     gl::Error clearTextures(gl::SamplerType samplerType, size_t rangeStart, size_t rangeEnd) override;
     gl::Error applyShadersImpl(const gl::Data &data, GLenum drawMode) override;
 
-    egl::Error initializeEGLDevice(DeviceD3D **outDevice) override;
-
     void syncState(const gl::State &state, const gl::State::DirtyBits &bitmask) override;
 
   private:
     gl::Error drawArraysImpl(const gl::Data &data,
                              GLenum mode,
                              GLsizei count,
                              GLsizei instances) override;
     gl::Error drawElementsImpl(const gl::Data &data,
@@ -332,39 +321,32 @@ class Renderer11 : public RendererD3D
     void populateRenderer11DeviceCaps();
 
     void updateHistograms();
 
     HMODULE mD3d11Module;
     HMODULE mDxgiModule;
     std::vector<D3D_FEATURE_LEVEL> mAvailableFeatureLevels;
     D3D_DRIVER_TYPE mDriverType;
+    bool mCreatedWithDeviceEXT;
+    DeviceD3D *mEGLDevice;
 
     HLSLCompiler mCompiler;
 
+    egl::Error initializeD3DDevice();
     void initializeDevice();
     void releaseDeviceResources();
     void release();
 
     RenderStateCache mStateCache;
 
     // current render target states
     uintptr_t mAppliedRTVs[gl::IMPLEMENTATION_MAX_DRAW_BUFFERS];
     uintptr_t mAppliedDSV;
     bool mDepthStencilInitialized;
-    bool mRenderTargetDescInitialized;
-    unsigned int mCurStencilSize;
-
-    struct RenderTargetDesc
-    {
-        size_t width;
-        size_t height;
-        DXGI_FORMAT format;
-    };
-    RenderTargetDesc mRenderTargetDesc;
 
     // Currently applied sampler states
     std::vector<bool> mForceSetVertexSamplerStates;
     std::vector<gl::SamplerState> mCurVertexSamplerStates;
 
     std::vector<bool> mForceSetPixelSamplerStates;
     std::vector<gl::SamplerState> mCurPixelSamplerStates;
 
@@ -408,37 +390,16 @@ class Renderer11 : public RendererD3D
     SRVCache mCurVertexSRVs;
     SRVCache mCurPixelSRVs;
 
     // A block of NULL pointers, cached so we don't re-allocate every draw call
     std::vector<ID3D11ShaderResourceView*> mNullSRVs;
 
     StateManager11 mStateManager;
 
-    // Currently applied rasterizer state
-    bool mForceSetRasterState;
-    gl::RasterizerState mCurRasterState;
-
-    // Currently applied depth stencil state
-    bool mForceSetDepthStencilState;
-    gl::DepthStencilState mCurDepthStencilState;
-    int mCurStencilRef;
-    int mCurStencilBackRef;
-
-    // Currently applied scissor rectangle
-    bool mForceSetScissor;
-    bool mScissorEnabled;
-    gl::Rectangle mCurScissor;
-
-    // Currently applied viewport
-    bool mForceSetViewport;
-    gl::Rectangle mCurViewport;
-    float mCurNear;
-    float mCurFar;
-
     // Currently applied primitive topology
     D3D11_PRIMITIVE_TOPOLOGY mCurrentPrimitiveTopology;
 
     // Currently applied index buffer
     ID3D11Buffer *mAppliedIB;
     DXGI_FORMAT mAppliedIBFormat;
     unsigned int mAppliedIBOffset;
     bool mAppliedIBChanged;
@@ -454,25 +415,23 @@ class Renderer11 : public RendererD3D
                                                                                  // which may differ from GLs, due
                                                                                  // to different append behavior
 
     // Currently applied shaders
     uintptr_t mAppliedVertexShader;
     uintptr_t mAppliedGeometryShader;
     uintptr_t mAppliedPixelShader;
 
-    dx_VertexConstants mVertexConstants;
     dx_VertexConstants mAppliedVertexConstants;
     ID3D11Buffer *mDriverConstantBufferVS;
     ID3D11Buffer *mCurrentVertexConstantBuffer;
     unsigned int mCurrentConstantBufferVS[gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS];
     GLintptr mCurrentConstantBufferVSOffset[gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS];
     GLsizeiptr mCurrentConstantBufferVSSize[gl::IMPLEMENTATION_MAX_VERTEX_SHADER_UNIFORM_BUFFERS];
 
-    dx_PixelConstants mPixelConstants;
     dx_PixelConstants mAppliedPixelConstants;
     ID3D11Buffer *mDriverConstantBufferPS;
     ID3D11Buffer *mCurrentPixelConstantBuffer;
     unsigned int mCurrentConstantBufferPS[gl::IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS];
     GLintptr mCurrentConstantBufferPSOffset[gl::IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS];
     GLsizeiptr mCurrentConstantBufferPSSize[gl::IMPLEMENTATION_MAX_FRAGMENT_SHADER_UNIFORM_BUFFERS];
 
     ID3D11Buffer *mCurrentGeometryConstantBuffer;
--- a/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
+++ b/gfx/angle/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
@@ -9,41 +9,111 @@
 #include "libANGLE/renderer/d3d/d3d11/StateManager11.h"
 
 #include "common/BitSetIterator.h"
 #include "libANGLE/renderer/d3d/d3d11/Renderer11.h"
 
 namespace rx
 {
 
-StateManager11::StateManager11(Renderer11 *renderer11)
+StateManager11::StateManager11()
     : mBlendStateIsDirty(false),
       mCurBlendColor(0, 0, 0, 0),
       mCurSampleMask(0),
-      mRenderer11(renderer11)
+      mDepthStencilStateIsDirty(false),
+      mCurStencilRef(0),
+      mCurStencilBackRef(0),
+      mCurStencilSize(0),
+      mRasterizerStateIsDirty(false),
+      mScissorStateIsDirty(false),
+      mCurScissorEnabled(false),
+      mCurScissorRect(),
+      mViewportStateIsDirty(false),
+      mCurViewport(),
+      mCurNear(0.0f),
+      mCurFar(0.0f),
+      mViewportBounds(),
+      mRenderer11DeviceCaps(nullptr),
+      mDeviceContext(nullptr),
+      mStateCache(nullptr)
 {
     mCurBlendState.blend                 = false;
     mCurBlendState.sourceBlendRGB        = GL_ONE;
     mCurBlendState.destBlendRGB          = GL_ZERO;
     mCurBlendState.sourceBlendAlpha      = GL_ONE;
     mCurBlendState.destBlendAlpha        = GL_ZERO;
     mCurBlendState.blendEquationRGB      = GL_FUNC_ADD;
     mCurBlendState.blendEquationAlpha    = GL_FUNC_ADD;
     mCurBlendState.colorMaskRed          = true;
     mCurBlendState.colorMaskBlue         = true;
     mCurBlendState.colorMaskGreen        = true;
     mCurBlendState.colorMaskAlpha        = true;
     mCurBlendState.sampleAlphaToCoverage = false;
     mCurBlendState.dither                = false;
+
+    mCurDepthStencilState.depthTest                = false;
+    mCurDepthStencilState.depthFunc                = GL_LESS;
+    mCurDepthStencilState.depthMask                = true;
+    mCurDepthStencilState.stencilTest              = false;
+    mCurDepthStencilState.stencilMask              = true;
+    mCurDepthStencilState.stencilFail              = GL_KEEP;
+    mCurDepthStencilState.stencilPassDepthFail     = GL_KEEP;
+    mCurDepthStencilState.stencilPassDepthPass     = GL_KEEP;
+    mCurDepthStencilState.stencilWritemask         = static_cast<GLuint>(-1);
+    mCurDepthStencilState.stencilBackFunc          = GL_ALWAYS;
+    mCurDepthStencilState.stencilBackMask          = static_cast<GLuint>(-1);
+    mCurDepthStencilState.stencilBackFail          = GL_KEEP;
+    mCurDepthStencilState.stencilBackPassDepthFail = GL_KEEP;
+    mCurDepthStencilState.stencilBackPassDepthPass = GL_KEEP;
+    mCurDepthStencilState.stencilBackWritemask     = static_cast<GLuint>(-1);
+
+    mCurRasterState.rasterizerDiscard   = false;
+    mCurRasterState.cullFace            = false;
+    mCurRasterState.cullMode            = GL_BACK;
+    mCurRasterState.frontFace           = GL_CCW;
+    mCurRasterState.polygonOffsetFill   = false;
+    mCurRasterState.polygonOffsetFactor = 0.0f;
+    mCurRasterState.polygonOffsetUnits  = 0.0f;
+    mCurRasterState.pointDrawMode       = false;
+    mCurRasterState.multiSample         = false;
 }
 
 StateManager11::~StateManager11()
 {
 }
 
+void StateManager11::initialize(ID3D11DeviceContext *deviceContext,
+                                RenderStateCache *stateCache,
+                                Renderer11DeviceCaps *renderer11DeviceCaps)
+{
+    mDeviceContext        = deviceContext;
+    mStateCache           = stateCache;
+    mRenderer11DeviceCaps = renderer11DeviceCaps;
+}
+
+void StateManager11::updateStencilSizeIfChanged(bool depthStencilInitialized,
+                                                unsigned int stencilSize)
+{
+    if (!depthStencilInitialized || stencilSize != mCurStencilSize)
+    {
+        mCurStencilSize           = stencilSize;
+        mDepthStencilStateIsDirty = true;
+    }
+}
+
+void StateManager11::setViewportBounds(const int width, const int height)
+{
+    if (mRenderer11DeviceCaps->featureLevel <= D3D_FEATURE_LEVEL_9_3 &&
+        (mViewportBounds.width != width || mViewportBounds.height != height))
+    {
+        mViewportBounds       = gl::Extents(width, height, 1);
+        mViewportStateIsDirty = true;
+    }
+}
+
 void StateManager11::syncState(const gl::State &state, const gl::State::DirtyBits &dirtyBits)
 {
     for (unsigned int dirtyBit : angle::IterateBitSet(dirtyBits))
     {
         switch (dirtyBit)
         {
             case gl::State::DIRTY_BIT_BLEND_EQUATIONS:
             {
@@ -99,63 +169,414 @@ void StateManager11::syncState(const gl:
                 break;
             }
             case gl::State::DIRTY_BIT_BLEND_COLOR:
                 if (state.getBlendColor() != mCurBlendColor)
                 {
                     mBlendStateIsDirty = true;
                 }
                 break;
+            case gl::State::DIRTY_BIT_DEPTH_MASK:
+                if (state.getDepthStencilState().depthMask != mCurDepthStencilState.depthMask)
+                {
+                    mDepthStencilStateIsDirty = true;
+                }
+                break;
+            case gl::State::DIRTY_BIT_DEPTH_TEST_ENABLED:
+                if (state.getDepthStencilState().depthTest != mCurDepthStencilState.depthTest)
+                {
+                    mDepthStencilStateIsDirty = true;
+                }
+                break;
+            case gl::State::DIRTY_BIT_DEPTH_FUNC:
+                if (state.getDepthStencilState().depthFunc != mCurDepthStencilState.depthFunc)
+                {
+                    mDepthStencilStateIsDirty = true;
+                }
+                break;
+            case gl::State::DIRTY_BIT_STENCIL_TEST_ENABLED:
+                if (state.getDepthStencilState().stencilTest != mCurDepthStencilState.stencilTest)
+                {
+                    mDepthStencilStateIsDirty = true;
+                }
+                break;
+            case gl::State::DIRTY_BIT_STENCIL_FUNCS_FRONT:
+            {
+                const gl::DepthStencilState &depthStencil = state.getDepthStencilState();
+                if (depthStencil.stencilFunc != mCurDepthStencilState.stencilFunc ||
+                    depthStencil.stencilMask != mCurDepthStencilState.stencilMask ||
+                    state.getStencilRef() != mCurStencilRef)
+                {
+                    mDepthStencilStateIsDirty = true;
+                }
+                break;
+            }
+            case gl::State::DIRTY_BIT_STENCIL_FUNCS_BACK:
+            {
+                const gl::DepthStencilState &depthStencil = state.getDepthStencilState();
+                if (depthStencil.stencilBackFunc != mCurDepthStencilState.stencilBackFunc ||
+                    depthStencil.stencilBackMask != mCurDepthStencilState.stencilBackMask ||
+                    state.getStencilBackRef() != mCurStencilBackRef)
+                {
+                    mDepthStencilStateIsDirty = true;
+                }
+                break;
+            }
+            case gl::State::DIRTY_BIT_STENCIL_WRITEMASK_FRONT:
+                if (state.getDepthStencilState().stencilWritemask !=
+                    mCurDepthStencilState.stencilWritemask)
+                {
+                    mDepthStencilStateIsDirty = true;
+                }
+                break;
+            case gl::State::DIRTY_BIT_STENCIL_WRITEMASK_BACK:
+                if (state.getDepthStencilState().stencilBackWritemask !=
+                    mCurDepthStencilState.stencilBackWritemask)
+                {
+                    mDepthStencilStateIsDirty = true;
+                }
+                break;
+            case gl::State::DIRTY_BIT_STENCIL_OPS_FRONT:
+            {
+                const gl::DepthStencilState &depthStencil = state.getDepthStencilState();
+                if (depthStencil.stencilFail != mCurDepthStencilState.stencilFail ||
+                    depthStencil.stencilPassDepthFail !=
+                        mCurDepthStencilState.stencilPassDepthFail ||
+                    depthStencil.stencilPassDepthPass != mCurDepthStencilState.stencilPassDepthPass)
+                {
+                    mDepthStencilStateIsDirty = true;
+                }
+                break;
+            }
+            case gl::State::DIRTY_BIT_STENCIL_OPS_BACK:
+            {
+                const gl::DepthStencilState &depthStencil = state.getDepthStencilState();
+                if (depthStencil.stencilBackFail != mCurDepthStencilState.stencilBackFail ||
+                    depthStencil.stencilBackPassDepthFail !=
+                        mCurDepthStencilState.stencilBackPassDepthFail ||
+                    depthStencil.stencilBackPassDepthPass !=
+                        mCurDepthStencilState.stencilBackPassDepthPass)
+                {
+                    mDepthStencilStateIsDirty = true;
+                }
+                break;
+            }
+            case gl::State::DIRTY_BIT_CULL_FACE_ENABLED:
+                if (state.getRasterizerState().cullFace != mCurRasterState.cullFace)
+                {
+                    mRasterizerStateIsDirty = true;
+                }
+                break;
+            case gl::State::DIRTY_BIT_CULL_FACE:
+                if (state.getRasterizerState().cullMode != mCurRasterState.cullMode)
+                {
+                    mRasterizerStateIsDirty = true;
+                }
+                break;
+            case gl::State::DIRTY_BIT_FRONT_FACE:
+                if (state.getRasterizerState().frontFace != mCurRasterState.frontFace)
+                {
+                    mRasterizerStateIsDirty = true;
+                }
+                break;
+            case gl::State::DIRTY_BIT_POLYGON_OFFSET_FILL_ENABLED:
+                if (state.getRasterizerState().polygonOffsetFill !=
+                    mCurRasterState.polygonOffsetFill)
+                {
+                    mRasterizerStateIsDirty = true;
+                }
+                break;
+            case gl::State::DIRTY_BIT_POLYGON_OFFSET:
+            {
+                const gl::RasterizerState &rasterState = state.getRasterizerState();
+                if (rasterState.polygonOffsetFactor != mCurRasterState.polygonOffsetFactor ||
+                    rasterState.polygonOffsetUnits != mCurRasterState.polygonOffsetUnits)
+                {
+                    mRasterizerStateIsDirty = true;
+                }
+                break;
+            }
+            case gl::State::DIRTY_BIT_RASTERIZER_DISCARD_ENABLED:
+                if (state.getRasterizerState().rasterizerDiscard !=
+                    mCurRasterState.rasterizerDiscard)
+                {
+                    mRasterizerStateIsDirty = true;
+                }
+                break;
+            case gl::State::DIRTY_BIT_SCISSOR:
+                if (state.getScissor() != mCurScissorRect)
+                {
+                    mScissorStateIsDirty = true;
+                }
+                break;
+            case gl::State::DIRTY_BIT_SCISSOR_TEST_ENABLED:
+                if (state.isScissorTestEnabled() != mCurScissorEnabled)
+                {
+                    mScissorStateIsDirty = true;
+                    // Rasterizer state update needs mCurScissorsEnabled and updates when it changes
+                    mRasterizerStateIsDirty = true;
+                }
+                break;
+            case gl::State::DIRTY_BIT_DEPTH_RANGE:
+                if (state.getNearPlane() != mCurNear || state.getFarPlane() != mCurFar)
+                {
+                    mViewportStateIsDirty = true;
+                }
+                break;
+            case gl::State::DIRTY_BIT_VIEWPORT:
+                if (state.getViewport() != mCurViewport)
+                {
+                    mViewportStateIsDirty = true;
+                }
+                break;
             default:
                 break;
         }
     }
 }
 
 gl::Error StateManager11::setBlendState(const gl::Framebuffer *framebuffer,
                                         const gl::BlendState &blendState,
                                         const gl::ColorF &blendColor,
                                         unsigned int sampleMask)
 {
-    if (mBlendStateIsDirty || sampleMask != mCurSampleMask)
+    if (!mBlendStateIsDirty && sampleMask == mCurSampleMask)
+    {
+        return gl::Error(GL_NO_ERROR);
+    }
+
+    ID3D11BlendState *dxBlendState = nullptr;
+    gl::Error error = mStateCache->getBlendState(framebuffer, blendState, &dxBlendState);
+    if (error.isError())
+    {
+        return error;
+    }
+
+    ASSERT(dxBlendState != nullptr);
+
+    float blendColors[4] = {0.0f};
+    if (blendState.sourceBlendRGB != GL_CONSTANT_ALPHA &&
+        blendState.sourceBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA &&
+        blendState.destBlendRGB != GL_CONSTANT_ALPHA &&
+        blendState.destBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA)
     {
-        ID3D11BlendState *dxBlendState = nullptr;
-        gl::Error error =
-            mRenderer11->getStateCache().getBlendState(framebuffer, blendState, &dxBlendState);
-        if (error.isError())
-        {
-            return error;
-        }
+        blendColors[0] = blendColor.red;
+        blendColors[1] = blendColor.green;
+        blendColors[2] = blendColor.blue;
+        blendColors[3] = blendColor.alpha;
+    }
+    else
+    {
+        blendColors[0] = blendColor.alpha;
+        blendColors[1] = blendColor.alpha;
+        blendColors[2] = blendColor.alpha;
+        blendColors[3] = blendColor.alpha;
+    }
+
+    mDeviceContext->OMSetBlendState(dxBlendState, blendColors, sampleMask);
 
-        ASSERT(dxBlendState != nullptr);
+    mCurBlendState = blendState;
+    mCurBlendColor = blendColor;
+    mCurSampleMask = sampleMask;
+
+    mBlendStateIsDirty = false;
+
+    return error;
+}
+
+gl::Error StateManager11::setDepthStencilState(const gl::State &glState)
+{
+    const auto &fbo = *glState.getDrawFramebuffer();
+
+    // Disable the depth test/depth write if we are using a stencil-only attachment.
+    // This is because ANGLE emulates stencil-only with D24S8 on D3D11 - we should neither read
+    // nor write to the unused depth part of this emulated texture.
+    bool disableDepth = (!fbo.hasDepth() && fbo.hasStencil());
 
-        float blendColors[4] = {0.0f};
-        if (blendState.sourceBlendRGB != GL_CONSTANT_ALPHA &&
-            blendState.sourceBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA &&
-            blendState.destBlendRGB != GL_CONSTANT_ALPHA &&
-            blendState.destBlendRGB != GL_ONE_MINUS_CONSTANT_ALPHA)
-        {
-            blendColors[0] = blendColor.red;
-            blendColors[1] = blendColor.green;
-            blendColors[2] = blendColor.blue;
-            blendColors[3] = blendColor.alpha;
-        }
-        else
-        {
-            blendColors[0] = blendColor.alpha;
-            blendColors[1] = blendColor.alpha;
-            blendColors[2] = blendColor.alpha;
-            blendColors[3] = blendColor.alpha;
-        }
+    // Similarly we disable the stencil portion of the DS attachment if the app only binds depth.
+    bool disableStencil = (fbo.hasDepth() && !fbo.hasStencil());
+
+    // CurDisableDepth/Stencil are reset automatically after we call forceSetDepthStencilState.
+    if (!mDepthStencilStateIsDirty && mCurDisableDepth.valid() &&
+        disableDepth == mCurDisableDepth.value() && mCurDisableStencil.valid() &&
+        disableStencil == mCurDisableStencil.value())
+    {
+        return gl::Error(GL_NO_ERROR);
+    }
+
+    const auto &depthStencilState = glState.getDepthStencilState();
+    int stencilRef                = glState.getStencilRef();
+    int stencilBackRef            = glState.getStencilBackRef();
+
+    // get the maximum size of the stencil ref
+    unsigned int maxStencil = 0;
+    if (depthStencilState.stencilTest && mCurStencilSize > 0)
+    {
+        maxStencil = (1 << mCurStencilSize) - 1;
+    }
+    ASSERT((depthStencilState.stencilWritemask & maxStencil) ==
+           (depthStencilState.stencilBackWritemask & maxStencil));
+    ASSERT(stencilRef == stencilBackRef);
+    ASSERT((depthStencilState.stencilMask & maxStencil) ==
+           (depthStencilState.stencilBackMask & maxStencil));
 
-        mRenderer11->getDeviceContext()->OMSetBlendState(dxBlendState, blendColors, sampleMask);
+    ID3D11DepthStencilState *dxDepthStencilState = NULL;
+    gl::Error error = mStateCache->getDepthStencilState(depthStencilState, disableDepth,
+                                                        disableStencil, &dxDepthStencilState);
+    if (error.isError())
+    {
+        return error;
+    }
+
+    ASSERT(dxDepthStencilState);
 
-        mCurBlendState = blendState;
-        mCurBlendColor = blendColor;
-        mCurSampleMask = sampleMask;
+    // Max D3D11 stencil reference value is 0xFF,
+    // corresponding to the max 8 bits in a stencil buffer
+    // GL specifies we should clamp the ref value to the
+    // nearest bit depth when doing stencil ops
+    static_assert(D3D11_DEFAULT_STENCIL_READ_MASK == 0xFF,
+                  "Unexpected value of D3D11_DEFAULT_STENCIL_READ_MASK");
+    static_assert(D3D11_DEFAULT_STENCIL_WRITE_MASK == 0xFF,
+                  "Unexpected value of D3D11_DEFAULT_STENCIL_WRITE_MASK");
+    UINT dxStencilRef = std::min<UINT>(stencilRef, 0xFFu);
 
-        mBlendStateIsDirty = false;
-    }
+    mDeviceContext->OMSetDepthStencilState(dxDepthStencilState, dxStencilRef);
+
+    mCurDepthStencilState = depthStencilState;
+    mCurStencilRef        = stencilRef;
+    mCurStencilBackRef    = stencilBackRef;
+    mCurDisableDepth      = disableDepth;
+    mCurDisableStencil    = disableStencil;
+
+    mDepthStencilStateIsDirty = false;
 
     return gl::Error(GL_NO_ERROR);
 }
 
+gl::Error StateManager11::setRasterizerState(const gl::RasterizerState &rasterState)
+{
+    if (!mRasterizerStateIsDirty)
+    {
+        return gl::Error(GL_NO_ERROR);
+    }
+
+    ID3D11RasterizerState *dxRasterState = nullptr;
+    gl::Error error =
+        mStateCache->getRasterizerState(rasterState, mCurScissorEnabled, &dxRasterState);
+    if (error.isError())
+    {
+        return error;
+    }
+
+    mDeviceContext->RSSetState(dxRasterState);
+
+    mCurRasterState         = rasterState;
+    mRasterizerStateIsDirty = false;
+
+    return error;
+}
+
+void StateManager11::setScissorRectangle(const gl::Rectangle &scissor, bool enabled)
+{
+    if (!mScissorStateIsDirty)
+        return;
+
+    if (enabled)
+    {
+        D3D11_RECT rect;
+        rect.left   = std::max(0, scissor.x);
+        rect.top    = std::max(0, scissor.y);
+        rect.right  = scissor.x + std::max(0, scissor.width);
+        rect.bottom = scissor.y + std::max(0, scissor.height);
+
+        mDeviceContext->RSSetScissorRects(1, &rect);
+    }
+
+    mCurScissorRect      = scissor;
+    mCurScissorEnabled   = enabled;
+    mScissorStateIsDirty = false;
+}
+
+void StateManager11::setViewport(const gl::Caps *caps,
+                                 const gl::Rectangle &viewport,
+                                 float zNear,
+                                 float zFar)
+{
+    if (!mViewportStateIsDirty)
+        return;
+
+    float actualZNear = gl::clamp01(zNear);
+    float actualZFar  = gl::clamp01(zFar);
+
+    int dxMaxViewportBoundsX = static_cast<int>(caps->maxViewportWidth);
+    int dxMaxViewportBoundsY = static_cast<int>(caps->maxViewportHeight);
+    int dxMinViewportBoundsX = -dxMaxViewportBoundsX;
+    int dxMinViewportBoundsY = -dxMaxViewportBoundsY;
+
+    if (mRenderer11DeviceCaps->featureLevel <= D3D_FEATURE_LEVEL_9_3)
+    {
+        // Feature Level 9 viewports shouldn't exceed the dimensions of the rendertarget.
+        dxMaxViewportBoundsX = static_cast<int>(mViewportBounds.width);
+        dxMaxViewportBoundsY = static_cast<int>(mViewportBounds.height);
+        dxMinViewportBoundsX = 0;
+        dxMinViewportBoundsY = 0;
+    }
+
+    int dxViewportTopLeftX = gl::clamp(viewport.x, dxMinViewportBoundsX, dxMaxViewportBoundsX);
+    int dxViewportTopLeftY = gl::clamp(viewport.y, dxMinViewportBoundsY, dxMaxViewportBoundsY);
+    int dxViewportWidth    = gl::clamp(viewport.width, 0, dxMaxViewportBoundsX - dxViewportTopLeftX);
+    int dxViewportHeight   = gl::clamp(viewport.height, 0, dxMaxViewportBoundsY - dxViewportTopLeftY);
+
+    D3D11_VIEWPORT dxViewport;
+    dxViewport.TopLeftX = static_cast<float>(dxViewportTopLeftX);
+    dxViewport.TopLeftY = static_cast<float>(dxViewportTopLeftY);
+    dxViewport.Width    = static_cast<float>(dxViewportWidth);
+    dxViewport.Height   = static_cast<float>(dxViewportHeight);
+    dxViewport.MinDepth = actualZNear;
+    dxViewport.MaxDepth = actualZFar;
+
+    mDeviceContext->RSSetViewports(1, &dxViewport);
+
+    mCurViewport = viewport;
+    mCurNear     = actualZNear;
+    mCurFar      = actualZFar;
+
+    // On Feature Level 9_*, we must emulate large and/or negative viewports in the shaders
+    // using viewAdjust (like the D3D9 renderer).
+    if (mRenderer11DeviceCaps->featureLevel <= D3D_FEATURE_LEVEL_9_3)
+    {
+        mVertexConstants.viewAdjust[0] = static_cast<float>((viewport.width - dxViewportWidth) +
+                                                            2 * (viewport.x - dxViewportTopLeftX)) /
+                                         dxViewport.Width;
+        mVertexConstants.viewAdjust[1] = static_cast<float>((viewport.height - dxViewportHeight) +
+                                                            2 * (viewport.y - dxViewportTopLeftY)) /
+                                         dxViewport.Height;
+        mVertexConstants.viewAdjust[2] = static_cast<float>(viewport.width) / dxViewport.Width;
+        mVertexConstants.viewAdjust[3] = static_cast<float>(viewport.height) / dxViewport.Height;
+    }
+
+    mPixelConstants.viewCoords[0] = viewport.width * 0.5f;
+    mPixelConstants.viewCoords[1] = viewport.height * 0.5f;
+    mPixelConstants.viewCoords[2] = viewport.x + (viewport.width * 0.5f);
+    mPixelConstants.viewCoords[3] = viewport.y + (viewport.height * 0.5f);
+