Bug 1394394 - Update webrender to commit 5edd3da7ee11e1d0caaf0b53cb7f04cfab20e585. r?jrmuizel draft
authorKartikaya Gupta <kgupta@mozilla.com>
Wed, 30 Aug 2017 13:10:20 -0400
changeset 656035 23ab7c2d558b42acf56bcdc05f8c989d95b4d18b
parent 655774 ab2d700fda2b4934d24227216972dce9fac19b74
child 728984 d013837e5ee2f523b75c64c083e56b11b1c29323
push id77037
push userkgupta@mozilla.com
push dateWed, 30 Aug 2017 17:10:42 +0000
reviewersjrmuizel
bugs1394394
milestone57.0a1
Bug 1394394 - Update webrender to commit 5edd3da7ee11e1d0caaf0b53cb7f04cfab20e585. r?jrmuizel MozReview-Commit-ID: 9WovSdHT2I4
gfx/doc/README.webrender
gfx/webrender/res/cs_box_shadow.fs.glsl
gfx/webrender/res/cs_box_shadow.glsl
gfx/webrender/res/cs_box_shadow.vs.glsl
gfx/webrender/res/prim_shared.glsl
gfx/webrender/src/debug_render.rs
gfx/webrender/src/debug_server.rs
gfx/webrender/src/device.rs
gfx/webrender/src/frame.rs
gfx/webrender/src/gpu_types.rs
gfx/webrender/src/internal_types.rs
gfx/webrender/src/lib.rs
gfx/webrender/src/render_task.rs
gfx/webrender/src/renderer.rs
gfx/webrender/src/tiling.rs
gfx/webrender_api/src/api.rs
--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -74,9 +74,9 @@ there is another crate in m-c called moz
 the same folder to store its rust dependencies. If one of the libraries that is
 required by both mozjs_sys and webrender is updated without updating the other
 project's Cargo.lock file, that results in build bustage.
 This means that any time you do this sort of manual update of packages, you need
 to make sure that mozjs_sys also has its Cargo.lock file updated if needed, hence
 the need to run the cargo update command in js/src as well. Hopefully this will
 be resolved soon.
 
-Latest Commit: b7cec8b19d5d6061263c3639031caf41562a2e17
+Latest Commit: 5edd3da7ee11e1d0caaf0b53cb7f04cfab20e585
deleted file mode 100644
--- a/gfx/webrender/res/cs_box_shadow.fs.glsl
+++ /dev/null
@@ -1,147 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-// See http://asciimath.org to render the equations here.
-
-// The Gaussian function used for blurring:
-//
-//     G_sigma(x) = 1/sqrt(2 pi sigma^2) e^(-x^2/(2 sigma^2))
-float gauss(float x, float sigma) {
-    float sigmaPow2 = sigma * sigma;
-    return 1.0 / sqrt(6.283185307179586 * sigmaPow2) * exp(-(x * x) / (2.0 * sigmaPow2));
-}
-
-// An approximation of the error function, which is related to the integral of the Gaussian
-// function:
-//
-//     "erf"(x) = 2/sqrt(pi) int_0^x e^(-t^2) dt
-//              ~~ 1 - 1 / (1 + a_1 x + a_2 x^2 + a_3 x^3 + a_4 x^4)^4
-//
-// where:
-//
-//     a_1 = 0.278393, a_2 = 0.230389, a_3 = 0.000972, a_4 = 0.078108
-//
-// This approximation is accurate to `5 xx 10^-4`, more than accurate enough for our purposes.
-//
-// See: https://en.wikipedia.org/wiki/Error_function#Approximation_with_elementary_functions
-float erf(float x) {
-    bool negative = x < 0.0;
-    if (negative)
-        x = -x;
-    float x2 = x * x;
-    float x3 = x2 * x;
-    float x4 = x2 * x2;
-    float denom = 1.0 + 0.278393 * x + 0.230389 * x2 + 0.000972 * x3 + 0.078108 * x4;
-    float result = 1.0 - 1.0 / (denom * denom * denom * denom);
-    return negative ? -result : result;
-}
-
-// A useful helper for calculating integrals of the Gaussian function via the error function:
-//
-//      "erf"_sigma(x) = 2 int 1/sqrt(2 pi sigma^2) e^(-x^2/(2 sigma^2)) dx
-//                     = "erf"(x/(sigma sqrt(2)))
-float erfSigma(float x, float sigma) {
-    return erf(x / (sigma * 1.4142135623730951));
-}
-
-// Returns the blurred color value from the box itself (not counting any rounded corners). `p_0` is
-// the vector distance to the top left corner of the box; `p_1` is the vector distance to its
-// bottom right corner.
-//
-//      "colorFromRect"_sigma(p_0, p_1)
-//          = int_{p_{0_y}}^{p_{1_y}} int_{p_{1_x}}^{p_{0_x}} G_sigma(y) G_sigma(x) dx dy
-//          = 1/4 ("erf"_sigma(p_{1_x}) - "erf"_sigma(p_{0_x}))
-//              ("erf"_sigma(p_{1_y}) - "erf"_sigma(p_{0_y}))
-float colorFromRect(vec2 p0, vec2 p1, float sigma) {
-    return (erfSigma(p1.x, sigma) - erfSigma(p0.x, sigma)) *
-        (erfSigma(p1.y, sigma) - erfSigma(p0.y, sigma)) / 4.0;
-}
-
-// Returns the `x` coordinate on the ellipse with the given radii for the given `y` coordinate:
-//
-//      "ellipsePoint"(y, y_0, a, b) = a sqrt(1 - ((y - y_0) / b)^2)
-float ellipsePoint(float y, float y0, vec2 radii) {
-    float bStep = (y - y0) / radii.y;
-    return radii.x * sqrt(1.0 - bStep * bStep);
-}
-
-// A helper function to compute the value that needs to be subtracted to accommodate the border
-// corners.
-//
-//     "colorCutout"_sigma(x_{0_l}, x_{0_r}, y_0, y_{min}, y_{max}, a, b)
-//          = int_{y_{min}}^{y_{max}}
-//              int_{x_{0_r} + "ellipsePoint"(y, y_0, a, b)}^{x_{0_r} + a} G_sigma(y) G_sigma(x) dx
-//              + int_{x_{0_l} - a}^{x_{0_l} - "ellipsePoint"(y, y_0, a, b)} G_sigma(y) G_sigma(x)
-//                  dx dy
-//          = int_{y_{min}}^{y_{max}} 1/2 G_sigma(y)
-//              ("erf"_sigma(x_{0_r} + a) - "erf"_sigma(x_{0_r} + "ellipsePoint"(y, y_0, a, b)) +
-//               "erf"_sigma(x_{0_l} - "ellipsePoint"(y, y_0, a, b)) - "erf"_sigma(x_{0_l} - a))
-//
-// with the outer integral evaluated numerically.
-float colorCutoutGeneral(float x0l,
-                         float x0r,
-                         float y0,
-                         float yMin,
-                         float yMax,
-                         vec2 radii,
-                         float sigma) {
-    float sum = 0.0;
-    for (float y = yMin; y <= yMax; y += 1.0) {
-        float xEllipsePoint = ellipsePoint(y, y0, radii);
-        sum += gauss(y, sigma) *
-            (erfSigma(x0r + radii.x, sigma) - erfSigma(x0r + xEllipsePoint, sigma) +
-             erfSigma(x0l - xEllipsePoint, sigma) - erfSigma(x0l - radii.x, sigma));
-    }
-    return sum / 2.0;
-}
-
-// The value that needs to be subtracted to accommodate the top border corners.
-float colorCutoutTop(float x0l, float x0r, float y0, vec2 radii, float sigma) {
-    return colorCutoutGeneral(x0l, x0r, y0, y0, y0 + radii.y, radii, sigma);
-}
-
-// The value that needs to be subtracted to accommodate the bottom border corners.
-float colorCutoutBottom(float x0l, float x0r, float y0, vec2 radii, float sigma) {
-    return colorCutoutGeneral(x0l, x0r, y0, y0 - radii.y, y0, radii, sigma);
-}
-
-// The blurred color value for the point at `pos` with the top left corner of the box at
-// `p_{0_"rect"}` and the bottom right corner of the box at `p_{1_"rect"}`.
-float color(vec2 pos, vec2 p0Rect, vec2 p1Rect, vec2 radii, float sigma) {
-    // Compute the vector distances `p_0` and `p_1`.
-    vec2 p0 = p0Rect - pos, p1 = p1Rect - pos;
-
-    // Compute the basic color `"colorFromRect"_sigma(p_0, p_1)`. This is all we have to do if
-    // the box is unrounded.
-    float cRect = colorFromRect(p0, p1, sigma);
-    if (radii.x == 0.0 || radii.y == 0.0)
-        return cRect;
-
-    // Compute the inner corners of the box, taking border radii into account: `x_{0_l}`,
-    // `y_{0_t}`, `x_{0_r}`, and `y_{0_b}`.
-    float x0l = p0.x + radii.x;
-    float y0t = p1.y - radii.y;
-    float x0r = p1.x - radii.x;
-    float y0b = p0.y + radii.y;
-
-    // Compute the final color:
-    //
-    //     "colorFromRect"_sigma(p_0, p_1) -
-    //          ("colorCutoutTop"_sigma(x_{0_l}, x_{0_r}, y_{0_t}, a, b) +
-    //           "colorCutoutBottom"_sigma(x_{0_l}, x_{0_r}, y_{0_b}, a, b))
-    float cCutoutTop = colorCutoutTop(x0l, x0r, y0t, radii, sigma);
-    float cCutoutBottom = colorCutoutBottom(x0l, x0r, y0b, radii, sigma);
-    return cRect - (cCutoutTop + cCutoutBottom);
-}
-
-void main(void) {
-    vec2 pos = vPos.xy;
-    vec2 p0Rect = vBoxShadowRect.xy, p1Rect = vBoxShadowRect.zw;
-    vec2 radii = vBorderRadii.xy;
-    float sigma = vBlurRadius / 2.0;
-    float value = color(pos, p0Rect, p1Rect, radii, sigma);
-
-    value = max(value, 0.0);
-    oFragColor = dither(vec4(1.0, 1.0, 1.0, vInverted == 1.0 ? 1.0 - value : value));
-}
--- a/gfx/webrender/res/cs_box_shadow.glsl
+++ b/gfx/webrender/res/cs_box_shadow.glsl
@@ -4,8 +4,185 @@
 
 #include shared,prim_shared
 
 varying vec2 vPos;
 flat varying vec2 vBorderRadii;
 flat varying float vBlurRadius;
 flat varying vec4 vBoxShadowRect;
 flat varying float vInverted;
+
+#ifdef WR_VERTEX_SHADER
+in ivec2 aPrimAddress;
+in int aTaskIndex;
+
+void main(void) {
+    RenderTaskData task = fetch_render_task(aTaskIndex);
+    BoxShadow bs = fetch_boxshadow_direct(ivec2(aPrimAddress.x + VECS_PER_PRIM_HEADER, aPrimAddress.y));
+
+    vec2 p0 = task.data0.xy;
+    vec2 p1 = p0 + task.data0.zw;
+
+    vec2 pos = mix(p0, p1, aPosition.xy);
+
+    vBorderRadii = bs.border_radius_edge_size_blur_radius_inverted.xx;
+    vBlurRadius = bs.border_radius_edge_size_blur_radius_inverted.z;
+    vInverted = bs.border_radius_edge_size_blur_radius_inverted.w;
+    vBoxShadowRect = vec4(bs.bs_rect.xy, bs.bs_rect.xy + bs.bs_rect.zw);
+
+    // The fragment shader expects logical units, beginning at where the
+    // blur radius begins.
+    // The first path of the equation gets the virtual position in
+    // logical pixels within the patch rectangle (accounting for
+    // bilinear offset). Then we add the start position of the
+    // box shadow rect and subtract the blur radius to get the
+    // virtual coordinates that the FS expects.
+    vPos = (pos - 1.0 - p0) / uDevicePixelRatio + bs.bs_rect.xy - vec2(2.0 * vBlurRadius);
+
+    gl_Position = uTransform * vec4(pos, 0.0, 1.0);
+}
+#endif
+
+#ifdef WR_FRAGMENT_SHADER
+// See http://asciimath.org to render the equations here.
+
+// The Gaussian function used for blurring:
+//
+//     G_sigma(x) = 1/sqrt(2 pi sigma^2) e^(-x^2/(2 sigma^2))
+float gauss(float x, float sigma) {
+    float sigmaPow2 = sigma * sigma;
+    return 1.0 / sqrt(6.283185307179586 * sigmaPow2) * exp(-(x * x) / (2.0 * sigmaPow2));
+}
+
+// An approximation of the error function, which is related to the integral of the Gaussian
+// function:
+//
+//     "erf"(x) = 2/sqrt(pi) int_0^x e^(-t^2) dt
+//              ~~ 1 - 1 / (1 + a_1 x + a_2 x^2 + a_3 x^3 + a_4 x^4)^4
+//
+// where:
+//
+//     a_1 = 0.278393, a_2 = 0.230389, a_3 = 0.000972, a_4 = 0.078108
+//
+// This approximation is accurate to `5 xx 10^-4`, more than accurate enough for our purposes.
+//
+// See: https://en.wikipedia.org/wiki/Error_function#Approximation_with_elementary_functions
+float erf(float x) {
+    bool negative = x < 0.0;
+    if (negative)
+        x = -x;
+    float x2 = x * x;
+    float x3 = x2 * x;
+    float x4 = x2 * x2;
+    float denom = 1.0 + 0.278393 * x + 0.230389 * x2 + 0.000972 * x3 + 0.078108 * x4;
+    float result = 1.0 - 1.0 / (denom * denom * denom * denom);
+    return negative ? -result : result;
+}
+
+// A useful helper for calculating integrals of the Gaussian function via the error function:
+//
+//      "erf"_sigma(x) = 2 int 1/sqrt(2 pi sigma^2) e^(-x^2/(2 sigma^2)) dx
+//                     = "erf"(x/(sigma sqrt(2)))
+float erfSigma(float x, float sigma) {
+    return erf(x / (sigma * 1.4142135623730951));
+}
+
+// Returns the blurred color value from the box itself (not counting any rounded corners). `p_0` is
+// the vector distance to the top left corner of the box; `p_1` is the vector distance to its
+// bottom right corner.
+//
+//      "colorFromRect"_sigma(p_0, p_1)
+//          = int_{p_{0_y}}^{p_{1_y}} int_{p_{1_x}}^{p_{0_x}} G_sigma(y) G_sigma(x) dx dy
+//          = 1/4 ("erf"_sigma(p_{1_x}) - "erf"_sigma(p_{0_x}))
+//              ("erf"_sigma(p_{1_y}) - "erf"_sigma(p_{0_y}))
+float colorFromRect(vec2 p0, vec2 p1, float sigma) {
+    return (erfSigma(p1.x, sigma) - erfSigma(p0.x, sigma)) *
+        (erfSigma(p1.y, sigma) - erfSigma(p0.y, sigma)) / 4.0;
+}
+
+// Returns the `x` coordinate on the ellipse with the given radii for the given `y` coordinate:
+//
+//      "ellipsePoint"(y, y_0, a, b) = a sqrt(1 - ((y - y_0) / b)^2)
+float ellipsePoint(float y, float y0, vec2 radii) {
+    float bStep = (y - y0) / radii.y;
+    return radii.x * sqrt(1.0 - bStep * bStep);
+}
+
+// A helper function to compute the value that needs to be subtracted to accommodate the border
+// corners.
+//
+//     "colorCutout"_sigma(x_{0_l}, x_{0_r}, y_0, y_{min}, y_{max}, a, b)
+//          = int_{y_{min}}^{y_{max}}
+//              int_{x_{0_r} + "ellipsePoint"(y, y_0, a, b)}^{x_{0_r} + a} G_sigma(y) G_sigma(x) dx
+//              + int_{x_{0_l} - a}^{x_{0_l} - "ellipsePoint"(y, y_0, a, b)} G_sigma(y) G_sigma(x)
+//                  dx dy
+//          = int_{y_{min}}^{y_{max}} 1/2 G_sigma(y)
+//              ("erf"_sigma(x_{0_r} + a) - "erf"_sigma(x_{0_r} + "ellipsePoint"(y, y_0, a, b)) +
+//               "erf"_sigma(x_{0_l} - "ellipsePoint"(y, y_0, a, b)) - "erf"_sigma(x_{0_l} - a))
+//
+// with the outer integral evaluated numerically.
+float colorCutoutGeneral(float x0l,
+                         float x0r,
+                         float y0,
+                         float yMin,
+                         float yMax,
+                         vec2 radii,
+                         float sigma) {
+    float sum = 0.0;
+    for (float y = yMin; y <= yMax; y += 1.0) {
+        float xEllipsePoint = ellipsePoint(y, y0, radii);
+        sum += gauss(y, sigma) *
+            (erfSigma(x0r + radii.x, sigma) - erfSigma(x0r + xEllipsePoint, sigma) +
+             erfSigma(x0l - xEllipsePoint, sigma) - erfSigma(x0l - radii.x, sigma));
+    }
+    return sum / 2.0;
+}
+
+// The value that needs to be subtracted to accommodate the top border corners.
+float colorCutoutTop(float x0l, float x0r, float y0, vec2 radii, float sigma) {
+    return colorCutoutGeneral(x0l, x0r, y0, y0, y0 + radii.y, radii, sigma);
+}
+
+// The value that needs to be subtracted to accommodate the bottom border corners.
+float colorCutoutBottom(float x0l, float x0r, float y0, vec2 radii, float sigma) {
+    return colorCutoutGeneral(x0l, x0r, y0, y0 - radii.y, y0, radii, sigma);
+}
+
+// The blurred color value for the point at `pos` with the top left corner of the box at
+// `p_{0_"rect"}` and the bottom right corner of the box at `p_{1_"rect"}`.
+float color(vec2 pos, vec2 p0Rect, vec2 p1Rect, vec2 radii, float sigma) {
+    // Compute the vector distances `p_0` and `p_1`.
+    vec2 p0 = p0Rect - pos, p1 = p1Rect - pos;
+
+    // Compute the basic color `"colorFromRect"_sigma(p_0, p_1)`. This is all we have to do if
+    // the box is unrounded.
+    float cRect = colorFromRect(p0, p1, sigma);
+    if (radii.x == 0.0 || radii.y == 0.0)
+        return cRect;
+
+    // Compute the inner corners of the box, taking border radii into account: `x_{0_l}`,
+    // `y_{0_t}`, `x_{0_r}`, and `y_{0_b}`.
+    float x0l = p0.x + radii.x;
+    float y0t = p1.y - radii.y;
+    float x0r = p1.x - radii.x;
+    float y0b = p0.y + radii.y;
+
+    // Compute the final color:
+    //
+    //     "colorFromRect"_sigma(p_0, p_1) -
+    //          ("colorCutoutTop"_sigma(x_{0_l}, x_{0_r}, y_{0_t}, a, b) +
+    //           "colorCutoutBottom"_sigma(x_{0_l}, x_{0_r}, y_{0_b}, a, b))
+    float cCutoutTop = colorCutoutTop(x0l, x0r, y0t, radii, sigma);
+    float cCutoutBottom = colorCutoutBottom(x0l, x0r, y0b, radii, sigma);
+    return cRect - (cCutoutTop + cCutoutBottom);
+}
+
+void main(void) {
+    vec2 pos = vPos.xy;
+    vec2 p0Rect = vBoxShadowRect.xy, p1Rect = vBoxShadowRect.zw;
+    vec2 radii = vBorderRadii.xy;
+    float sigma = vBlurRadius / 2.0;
+    float value = color(pos, p0Rect, p1Rect, radii, sigma);
+
+    value = max(value, 0.0);
+    oFragColor = dither(vec4(1.0, 1.0, 1.0, vInverted == 1.0 ? 1.0 - value : value));
+}
+#endif
deleted file mode 100644
--- a/gfx/webrender/res/cs_box_shadow.vs.glsl
+++ /dev/null
@@ -1,30 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-void main(void) {
-    PrimitiveInstance pi = fetch_prim_instance();
-    RenderTaskData task = fetch_render_task(pi.render_task_index);
-    BoxShadow bs = fetch_boxshadow(pi.specific_prim_address);
-
-    vec2 p0 = task.data0.xy;
-    vec2 p1 = p0 + task.data0.zw;
-
-    vec2 pos = mix(p0, p1, aPosition.xy);
-
-    vBorderRadii = bs.border_radius_edge_size_blur_radius_inverted.xx;
-    vBlurRadius = bs.border_radius_edge_size_blur_radius_inverted.z;
-    vInverted = bs.border_radius_edge_size_blur_radius_inverted.w;
-    vBoxShadowRect = vec4(bs.bs_rect.xy, bs.bs_rect.xy + bs.bs_rect.zw);
-
-    // The fragment shader expects logical units, beginning at where the
-    // blur radius begins.
-    // The first path of the equation gets the virtual position in
-    // logical pixels within the patch rectangle (accounting for
-    // bilinear offset). Then we add the start position of the
-    // box shadow rect and subtract the blur radius to get the
-    // virtual coordinates that the FS expects.
-    vPos = (pos - 1.0 - p0) / uDevicePixelRatio + bs.bs_rect.xy - vec2(2.0 * vBlurRadius);
-
-    gl_Position = uTransform * vec4(pos, 0.0, 1.0);
-}
--- a/gfx/webrender/res/prim_shared.glsl
+++ b/gfx/webrender/res/prim_shared.glsl
@@ -136,16 +136,25 @@ vec4[3] fetch_from_resource_cache_3(int 
     ivec2 uv = get_resource_cache_uv(address);
     return vec4[3](
         texelFetchOffset(sResourceCache, uv, 0, ivec2(0, 0)),
         texelFetchOffset(sResourceCache, uv, 0, ivec2(1, 0)),
         texelFetchOffset(sResourceCache, uv, 0, ivec2(2, 0))
     );
 }
 
+vec4[4] fetch_from_resource_cache_4_direct(ivec2 address) {
+    return vec4[4](
+        texelFetchOffset(sResourceCache, address, 0, ivec2(0, 0)),
+        texelFetchOffset(sResourceCache, address, 0, ivec2(1, 0)),
+        texelFetchOffset(sResourceCache, address, 0, ivec2(2, 0)),
+        texelFetchOffset(sResourceCache, address, 0, ivec2(3, 0))
+    );
+}
+
 vec4[4] fetch_from_resource_cache_4(int address) {
     ivec2 uv = get_resource_cache_uv(address);
     return vec4[4](
         texelFetchOffset(sResourceCache, uv, 0, ivec2(0, 0)),
         texelFetchOffset(sResourceCache, uv, 0, ivec2(1, 0)),
         texelFetchOffset(sResourceCache, uv, 0, ivec2(2, 0)),
         texelFetchOffset(sResourceCache, uv, 0, ivec2(3, 0))
     );
@@ -747,16 +756,21 @@ struct BoxShadow {
     vec4 border_radius_edge_size_blur_radius_inverted;
 };
 
 BoxShadow fetch_boxshadow(int address) {
     vec4 data[4] = fetch_from_resource_cache_4(address);
     return BoxShadow(data[0], data[1], data[2], data[3]);
 }
 
+BoxShadow fetch_boxshadow_direct(ivec2 address) {
+    vec4 data[4] = fetch_from_resource_cache_4_direct(address);
+    return BoxShadow(data[0], data[1], data[2], data[3]);
+}
+
 void write_clip(vec2 global_pos, ClipArea area) {
     vec2 texture_size = vec2(textureSize(sCacheA8, 0).xy);
     vec2 uv = global_pos + area.task_bounds.xy - area.screen_origin_target_index.xy;
     vClipMaskUvBounds = area.task_bounds / texture_size.xyxy;
     vClipMaskUv = vec3(uv / texture_size, area.screen_origin_target_index.z);
 }
 #endif //WR_VERTEX_SHADER
 
--- a/gfx/webrender/src/debug_render.rs
+++ b/gfx/webrender/src/debug_render.rs
@@ -1,21 +1,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use debug_font_data;
-use device::{Device, GpuMarker, Program, VAO, TextureId, VertexDescriptor};
+use device::{Device, GpuMarker, Program, VAO, Texture, TextureSlot, VertexDescriptor};
 use device::{TextureFilter, VertexAttribute, VertexUsageHint, VertexAttributeKind, TextureTarget};
 use euclid::{Transform3D, Point2D, Size2D, Rect};
-use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, TextureSampler};
+use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE};
 use internal_types::RenderTargetMode;
 use std::f32;
 use api::{ColorU, ImageFormat, DeviceUintSize};
 
+#[derive(Debug, Copy, Clone)]
+enum DebugSampler {
+    Font,
+}
+
+impl Into<TextureSlot> for DebugSampler {
+    fn into(self) -> TextureSlot {
+        match self {
+            DebugSampler::Font => TextureSlot(0),
+        }
+    }
+}
+
 const DESC_FONT: VertexDescriptor = VertexDescriptor {
     vertex_attributes: &[
         VertexAttribute { name: "aPosition", count: 2, kind: VertexAttributeKind::F32 },
         VertexAttribute { name: "aColor", count: 4, kind: VertexAttributeKind::U8Norm },
         VertexAttribute { name: "aColorTexCoord", count: 2, kind: VertexAttributeKind::F32 },
     ],
     instance_attributes: &[]
 };
@@ -66,41 +79,45 @@ impl DebugColorVertex {
     }
 }
 
 pub struct DebugRenderer {
     font_vertices: Vec<DebugFontVertex>,
     font_indices: Vec<u32>,
     font_program: Program,
     font_vao: VAO,
-    font_texture_id: TextureId,
+    font_texture: Texture,
 
     tri_vertices: Vec<DebugColorVertex>,
     tri_indices: Vec<u32>,
     tri_vao: VAO,
     line_vertices: Vec<DebugColorVertex>,
     line_vao: VAO,
     color_program: Program,
 }
 
 impl DebugRenderer {
     pub fn new(device: &mut Device) -> DebugRenderer {
         let font_program = device.create_program("debug_font",
                                                  "",
                                                  &DESC_FONT).unwrap();
+        device.bind_shader_samplers(&font_program, &[
+            ("sColor0", DebugSampler::Font)
+        ]);
+
         let color_program = device.create_program("debug_color",
                                                   "",
                                                   &DESC_COLOR).unwrap();
 
-        let font_vao = device.create_vao(&DESC_FONT, 32);
-        let line_vao = device.create_vao(&DESC_COLOR, 32);
-        let tri_vao = device.create_vao(&DESC_COLOR, 32);
+        let font_vao = device.create_vao(&DESC_FONT);
+        let line_vao = device.create_vao(&DESC_COLOR);
+        let tri_vao = device.create_vao(&DESC_COLOR);
 
-        let font_texture_id = device.create_texture_ids(1, TextureTarget::Array)[0];
-        device.init_texture(font_texture_id,
+        let mut font_texture = device.create_texture(TextureTarget::Array);
+        device.init_texture(&mut font_texture,
                             debug_font_data::BMP_WIDTH,
                             debug_font_data::BMP_HEIGHT,
                             ImageFormat::A8,
                             TextureFilter::Linear,
                             RenderTargetMode::None,
                             1,
                             Some(&debug_font_data::FONT_BITMAP));
 
@@ -110,21 +127,22 @@ impl DebugRenderer {
             line_vertices: Vec::new(),
             tri_vao,
             tri_vertices: Vec::new(),
             tri_indices: Vec::new(),
             font_program,
             color_program,
             font_vao,
             line_vao,
-            font_texture_id,
+            font_texture,
         }
     }
 
     pub fn deinit(self, device: &mut Device) {
+        device.delete_texture(self.font_texture);
         device.delete_program(self.font_program);
         device.delete_program(self.color_program);
         device.delete_vao(self.tri_vao);
         device.delete_vao(self.line_vao);
         device.delete_vao(self.font_vao);
     }
 
     pub fn line_height(&self) -> f32 {
@@ -260,17 +278,17 @@ impl DebugRenderer {
                                             VertexUsageHint::Dynamic);
             device.draw_nonindexed_lines(0, self.line_vertices.len() as i32);
         }
 
         // Glyph
         if !self.font_indices.is_empty() {
             device.bind_program(&self.font_program);
             device.set_uniforms(&self.font_program, &projection);
-            device.bind_texture(TextureSampler::Color0, self.font_texture_id);
+            device.bind_texture(DebugSampler::Font, &self.font_texture);
             device.bind_vao(&self.font_vao);
             device.update_vao_indices(&self.font_vao,
                                       &self.font_indices,
                                       VertexUsageHint::Dynamic);
             device.update_vao_main_vertices(&self.font_vao,
                                             &self.font_vertices,
                                             VertexUsageHint::Dynamic);
             device.draw_triangles_u32(0, self.font_indices.len() as i32);
--- a/gfx/webrender/src/debug_server.rs
+++ b/gfx/webrender/src/debug_server.rs
@@ -10,17 +10,17 @@ use std::sync::mpsc::Sender;
 
 use ws;
 
 // Messages that are sent from the render backend to the renderer
 // debug command queue. These are sent in a separate queue so
 // that none of these types are exposed to the RenderApi interfaces.
 // We can't use select!() as it's not stable...
 pub enum DebugMsg {
-    FetchBatches(ws::Sender),
+    FetchPasses(ws::Sender),
 }
 
 // Represents a connection to a client.
 struct Server {
     ws: ws::Sender,
     debug_tx: Sender<DebugMsg>,
     api_tx: MsgSender<ApiMsg>,
 }
@@ -43,18 +43,18 @@ impl ws::Handler for Server {
                         DebugCommand::EnableTextureCacheDebug(false)
                     }
                     "enable_render_target_debug" => {
                         DebugCommand::EnableRenderTargetDebug(true)
                     }
                     "disable_render_target_debug" => {
                         DebugCommand::EnableRenderTargetDebug(false)
                     }
-                    "fetch_batches" => {
-                        let msg = DebugMsg::FetchBatches(self.ws.clone());
+                    "fetch_passes" => {
+                        let msg = DebugMsg::FetchPasses(self.ws.clone());
                         self.debug_tx.send(msg).unwrap();
                         DebugCommand::Flush
                     }
                     msg => {
                         println!("unknown msg {}", msg);
                         return Ok(());
                     }
                 };
@@ -87,57 +87,106 @@ impl DebugServer {
                 debug_tx: debug_tx.clone(),
                 api_tx: api_tx.clone(),
             }
         }).unwrap();
 
         let broadcaster = socket.broadcaster();
 
         let join_handle = Some(thread::spawn(move || {
-            socket.listen("127.0.0.1:3583").unwrap();
+            if let Err(..) = socket.listen("127.0.0.1:3583") {
+                println!("ERROR: Unable to bind debugger websocket (port may be in use).");
+            }
         }));
 
         DebugServer {
             join_handle,
             broadcaster,
             debug_rx,
         }
     }
 }
 
 impl Drop for DebugServer {
     fn drop(&mut self) {
-        self.broadcaster.shutdown().unwrap();
-        self.join_handle.take().unwrap().join().unwrap();
+        self.broadcaster.shutdown().ok();
+        self.join_handle.take().unwrap().join().ok();
     }
 }
 
-// A serializable list of debug information about batches
+// A serializable list of debug information about passes
 // that can be sent to the client.
+
 #[derive(Serialize)]
-pub struct BatchInfo {
-    kind: &'static str,
-    count: usize,
+pub enum BatchKind {
+    Clip,
+    Cache,
+    Opaque,
+    Alpha,
 }
 
 #[derive(Serialize)]
-pub struct BatchList {
+pub struct PassList {
     kind: &'static str,
-    batches: Vec<BatchInfo>,
+    passes: Vec<Pass>,
+}
+
+impl PassList {
+    pub fn new() -> PassList {
+        PassList {
+            kind: "passes",
+            passes: Vec::new(),
+        }
+    }
+
+    pub fn add(&mut self, pass: Pass) {
+        self.passes.push(pass);
+    }
+}
+
+#[derive(Serialize)]
+pub struct Pass {
+    targets: Vec<Target>,
 }
 
-impl BatchList {
-    pub fn new() -> BatchList {
-        BatchList {
-            kind: "batches",
+impl Pass {
+    pub fn new() -> Pass {
+        Pass {
+            targets: Vec::new(),
+        }
+    }
+
+    pub fn add(&mut self, target: Target) {
+        self.targets.push(target);
+    }
+}
+
+#[derive(Serialize)]
+pub struct Target {
+    kind: &'static str,
+    batches: Vec<Batch>,
+}
+
+impl Target {
+    pub fn new(kind: &'static str) -> Target {
+        Target {
+            kind,
             batches: Vec::new(),
         }
     }
 
-    pub fn push(&mut self, kind: &'static str, count: usize) {
+    pub fn add(&mut self, kind: BatchKind, description: &str, count: usize) {
         if count > 0 {
-            self.batches.push(BatchInfo {
+            self.batches.push(Batch {
                 kind,
+                description: description.to_owned(),
                 count,
             });
         }
     }
 }
+
+#[derive(Serialize)]
+struct Batch {
+    kind: BatchKind,
+    description: String,
+    count: usize,
+}
--- a/gfx/webrender/src/device.rs
+++ b/gfx/webrender/src/device.rs
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use euclid::Transform3D;
 use gleam::gl;
-use internal_types::{RenderTargetMode, TextureSampler, DEFAULT_TEXTURE, FastHashMap};
+use internal_types::RenderTargetMode;
 use super::shader_source;
 use std::fs::File;
 use std::io::Read;
 use std::iter::repeat;
 use std::mem;
 use std::ops::Add;
 use std::path::PathBuf;
 use std::ptr;
@@ -48,16 +48,21 @@ const GL_FORMAT_BGRA_GLES: gl::GLuint = 
 const SHADER_VERSION_GL: &str = "#version 150\n";
 const SHADER_VERSION_GLES: &str = "#version 300 es\n";
 
 const SHADER_KIND_VERTEX: &str = "#define WR_VERTEX_SHADER\n";
 const SHADER_KIND_FRAGMENT: &str = "#define WR_FRAGMENT_SHADER\n";
 const SHADER_IMPORT: &str = "#include ";
 const SHADER_LINE_MARKER: &str = "#line 1\n";
 
+pub struct TextureSlot(pub usize);
+
+// In some places we need to temporarily bind a texture to any slot.
+const DEFAULT_TEXTURE: TextureSlot = TextureSlot(0);
+
 #[repr(u32)]
 pub enum DepthFunction {
     Less = gl::LESS,
     LessEqual = gl::LEQUAL,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum TextureTarget {
@@ -279,16 +284,22 @@ impl VertexAttribute {
                                            stride,
                                            offset);
             }
         }
     }
 }
 
 impl VertexDescriptor {
+    fn instance_stride(&self) -> u32 {
+        self.instance_attributes
+            .iter()
+            .map(|attr| attr.size_in_bytes()).sum()
+    }
+
     fn bind(&self,
             gl: &gl::Gl,
             main: VBOId,
             instance: VBOId) {
         main.bind(gl);
 
         let vertex_stride: u32 = self.vertex_attributes
                                     .iter()
@@ -302,19 +313,17 @@ impl VertexDescriptor {
                              vertex_stride as gl::GLint,
                              vertex_offset,
                              gl);
             vertex_offset += attr.size_in_bytes();
         }
 
         if !self.instance_attributes.is_empty() {
             instance.bind(gl);
-            let instance_stride: u32 = self.instance_attributes
-                                           .iter()
-                                           .map(|attr| attr.size_in_bytes()).sum();
+            let instance_stride = self.instance_stride();
             let mut instance_offset = 0;
 
             let base_attr = self.vertex_attributes.len() as u32;
 
             for (i, attr) in self.instance_attributes.iter().enumerate() {
                 let attr_index = base_attr + i as u32;
                 attr.bind_to_vao(attr_index,
                                  1,
@@ -322,38 +331,16 @@ impl VertexDescriptor {
                                  instance_offset,
                                  gl);
                 instance_offset += attr.size_in_bytes();
             }
         }
     }
 }
 
-impl TextureId {
-    pub fn bind(&self, gl: &gl::Gl) {
-        gl.bind_texture(self.target, self.name);
-    }
-
-    pub fn new(name: gl::GLuint, texture_target: TextureTarget) -> TextureId {
-        TextureId {
-            name,
-            target: texture_target.to_gl_target(),
-        }
-    }
-
-    pub fn invalid() -> TextureId {
-        TextureId {
-            name: 0,
-            target: gl::TEXTURE_2D,
-        }
-    }
-
-    pub fn is_valid(&self) -> bool { *self != TextureId::invalid() }
-}
-
 impl VBOId {
     fn bind(&self, gl: &gl::Gl) {
         gl.bind_buffer(gl::ARRAY_BUFFER, self.0);
     }
 }
 
 impl IBOId {
     fn bind(&self, gl: &gl::Gl) {
@@ -366,37 +353,61 @@ impl FBOId {
         let target = match target {
             FBOTarget::Read => gl::READ_FRAMEBUFFER,
             FBOTarget::Draw => gl::DRAW_FRAMEBUFFER,
         };
         gl.bind_framebuffer(target, self.0);
     }
 }
 
-struct Texture {
-    gl: Rc<gl::Gl>,
+pub struct ExternalTexture {
     id: gl::GLuint,
+    target: gl::GLuint,
+}
+
+impl ExternalTexture {
+    pub fn new(id: u32, target: TextureTarget) -> ExternalTexture {
+        ExternalTexture {
+            id,
+            target: target.to_gl_target(),
+        }
+    }
+}
+
+pub struct Texture {
+    id: gl::GLuint,
+    target: gl::GLuint,
     layer_count: i32,
     format: ImageFormat,
     width: u32,
     height: u32,
 
     filter: TextureFilter,
     mode: RenderTargetMode,
     fbo_ids: Vec<FBOId>,
     depth_rb: Option<RBOId>,
 }
 
+impl Texture {
+    pub fn get_dimensions(&self) -> DeviceUintSize {
+        DeviceUintSize::new(self.width, self.height)
+    }
+
+    pub fn get_render_target_layer_count(&self) -> usize {
+        self.fbo_ids.len()
+    }
+
+    pub fn get_layer_count(&self) -> i32 {
+        self.layer_count
+    }
+}
+
 impl Drop for Texture {
     fn drop(&mut self) {
-        if !self.fbo_ids.is_empty() {
-            let fbo_ids: Vec<_> = self.fbo_ids.iter().map(|&FBOId(fbo_id)| fbo_id).collect();
-            self.gl.delete_framebuffers(&fbo_ids[..]);
-        }
-        self.gl.delete_textures(&[self.id]);
+        debug_assert!(thread::panicking() || self.id == 0);
     }
 }
 
 pub struct Program {
     id: gl::GLuint,
     u_transform: gl::GLint,
     u_device_pixel_ratio: gl::GLint,
 }
@@ -407,32 +418,26 @@ impl Drop for Program {
     }
 }
 
 pub struct VAO {
     id: gl::GLuint,
     ibo_id: IBOId,
     main_vbo_id: VBOId,
     instance_vbo_id: VBOId,
-    instance_stride: gl::GLint,
+    instance_stride: usize,
     owns_vertices_and_indices: bool,
 }
 
 impl Drop for VAO {
     fn drop(&mut self) {
         debug_assert!(thread::panicking() || self.id == 0, "renderer::deinit not called");
     }
 }
 
-#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug, Copy, Clone)]
-pub struct TextureId {
-    name: gl::GLuint,
-    target: gl::GLuint,
-}
-
 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
 pub struct FBOId(gl::GLuint);
 
 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
 pub struct RBOId(gl::GLuint);
 
 #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
 pub struct VBOId(gl::GLuint);
@@ -742,17 +747,17 @@ pub struct Capabilities {
 pub enum ShaderError {
     Compilation(String, String), // name, error mssage
     Link(String, String), // name, error message
 }
 
 pub struct Device {
     gl: Rc<gl::Gl>,
     // device state
-    bound_textures: [TextureId; 16],
+    bound_textures: [gl::GLuint; 16],
     bound_program: gl::GLuint,
     bound_vao: gl::GLuint,
     bound_pbo: PBOId,
     bound_read_fbo: FBOId,
     bound_draw_fbo: FBOId,
     default_read_fbo: gl::GLuint,
     default_draw_fbo: gl::GLuint,
     device_pixel_ratio: f32,
@@ -760,17 +765,16 @@ pub struct Device {
     // HW or API capabilties
     capabilities: Capabilities,
 
     // debug
     inside_frame: bool,
 
     // resources
     resource_override_path: Option<PathBuf>,
-    textures: FastHashMap<TextureId, Texture>,
 
     max_texture_size: u32,
 
     // Frame counter. This is used to map between CPU
     // frames and GPU frames.
     frame_id: FrameId,
 }
 
@@ -787,27 +791,25 @@ impl Device {
             // every frame by the call to begin_frame().
             device_pixel_ratio: 1.0,
             inside_frame: false,
 
             capabilities: Capabilities {
                 supports_multisampling: false, //TODO
             },
 
-            bound_textures: [ TextureId::invalid(); 16 ],
+            bound_textures: [0; 16],
             bound_program: 0,
             bound_vao: 0,
             bound_pbo: PBOId(0),
             bound_read_fbo: FBOId(0),
             bound_draw_fbo: FBOId(0),
             default_read_fbo: 0,
             default_draw_fbo: 0,
 
-            textures: FastHashMap::default(),
-
             max_texture_size,
             frame_id: FrameId(0),
         }
     }
 
     pub fn gl(&self) -> &gl::Gl {
         &*self.gl
     }
@@ -820,17 +822,17 @@ impl Device {
         self.max_texture_size
     }
 
     pub fn get_capabilities(&self) -> &Capabilities {
         &self.capabilities
     }
 
     pub fn reset_state(&mut self) {
-        self.bound_textures = [ TextureId::invalid(); 16 ];
+        self.bound_textures = [0; 16];
         self.bound_vao = 0;
         self.bound_pbo = PBOId(0);
         self.bound_read_fbo = FBOId(0);
         self.bound_draw_fbo = FBOId(0);
     }
 
     pub fn compile_shader(gl: &gl::Gl,
                           name: &str,
@@ -861,17 +863,17 @@ impl Device {
         // Retrive the currently set FBO.
         let default_read_fbo = self.gl.get_integer_v(gl::READ_FRAMEBUFFER_BINDING);
         self.default_read_fbo = default_read_fbo as gl::GLuint;
         let default_draw_fbo = self.gl.get_integer_v(gl::DRAW_FRAMEBUFFER_BINDING);
         self.default_draw_fbo = default_draw_fbo as gl::GLuint;
 
         // Texture state
         for i in 0..self.bound_textures.len() {
-            self.bound_textures[i] = TextureId::invalid();
+            self.bound_textures[i] = 0;
             self.gl.active_texture(gl::TEXTURE0 + i as gl::GLuint);
             self.gl.bind_texture(gl::TEXTURE_2D, 0);
         }
 
         // Shader state
         self.bound_program = 0;
         self.gl.use_program(0);
 
@@ -889,50 +891,64 @@ impl Device {
         self.gl.bind_buffer(gl::PIXEL_UNPACK_BUFFER, 0);
 
         // Default is sampler 0, always
         self.gl.active_texture(gl::TEXTURE0);
 
         self.frame_id
     }
 
-    pub fn bind_texture(&mut self,
-                        sampler: TextureSampler,
-                        texture_id: TextureId) {
+    pub fn bind_texture<S>(&mut self,
+                           sampler: S,
+                           texture: &Texture) where S: Into<TextureSlot> {
         debug_assert!(self.inside_frame);
 
-        let sampler_index = sampler as usize;
-        if self.bound_textures[sampler_index] != texture_id {
-            self.bound_textures[sampler_index] = texture_id;
+        let sampler_index = sampler.into().0;
+        if self.bound_textures[sampler_index] != texture.id {
+            self.bound_textures[sampler_index] = texture.id;
             self.gl.active_texture(gl::TEXTURE0 + sampler_index as gl::GLuint);
-            texture_id.bind(self.gl());
+            self.gl.bind_texture(texture.target, texture.id);
             self.gl.active_texture(gl::TEXTURE0);
         }
     }
 
-    pub fn bind_read_target(&mut self, texture_id: Option<(TextureId, i32)>) {
+    pub fn bind_external_texture<S>(&mut self,
+                                    sampler: S,
+                                    external_texture: &ExternalTexture) where S: Into<TextureSlot> {
         debug_assert!(self.inside_frame);
 
-        let fbo_id = texture_id.map_or(FBOId(self.default_read_fbo), |texture_id| {
-            self.textures.get(&texture_id.0).unwrap().fbo_ids[texture_id.1 as usize]
+        let sampler_index = sampler.into().0;
+        if self.bound_textures[sampler_index] != external_texture.id {
+            self.bound_textures[sampler_index] = external_texture.id;
+            self.gl.active_texture(gl::TEXTURE0 + sampler_index as gl::GLuint);
+            self.gl.bind_texture(external_texture.target, external_texture.id);
+            self.gl.active_texture(gl::TEXTURE0);
+        }
+    }
+
+    pub fn bind_read_target(&mut self, texture_and_layer: Option<(&Texture, i32)>) {
+        debug_assert!(self.inside_frame);
+
+        let fbo_id = texture_and_layer.map_or(FBOId(self.default_read_fbo), |texture_and_layer| {
+            texture_and_layer.0.fbo_ids[texture_and_layer.1 as usize]
         });
 
         if self.bound_read_fbo != fbo_id {
             self.bound_read_fbo = fbo_id;
             fbo_id.bind(self.gl(), FBOTarget::Read);
         }
     }
 
     pub fn bind_draw_target(&mut self,
-                            texture_id: Option<(TextureId, i32)>,
+                            texture_and_layer: Option<(&Texture, i32)>,
                             dimensions: Option<DeviceUintSize>) {
         debug_assert!(self.inside_frame);
 
-        let fbo_id = texture_id.map_or(FBOId(self.default_draw_fbo), |texture_id| {
-            self.textures.get(&texture_id.0).unwrap().fbo_ids[texture_id.1 as usize]
+        let fbo_id = texture_and_layer.map_or(FBOId(self.default_draw_fbo), |texture_and_layer| {
+            texture_and_layer.0.fbo_ids[texture_and_layer.1 as usize]
         });
 
         if self.bound_draw_fbo != fbo_id {
             self.bound_draw_fbo = fbo_id;
             fbo_id.bind(self.gl(), FBOTarget::Draw);
         }
 
         if let Some(dimensions) = dimensions {
@@ -944,58 +960,29 @@ impl Device {
         debug_assert!(self.inside_frame);
 
         if self.bound_program != program.id {
             self.gl.use_program(program.id);
             self.bound_program = program.id;
         }
     }
 
-    pub fn create_texture_ids(&mut self,
-                              count: i32,
-                              target: TextureTarget) -> Vec<TextureId> {
-        let id_list = self.gl.gen_textures(count);
-        let mut texture_ids = Vec::new();
-
-        for id in id_list {
-            let texture_id = TextureId {
-                name: id,
-                target: target.to_gl_target(),
-            };
-
-            let texture = Texture {
-                gl: Rc::clone(&self.gl),
-                id,
-                width: 0,
-                height: 0,
-                layer_count: 0,
-                format: ImageFormat::Invalid,
-                filter: TextureFilter::Nearest,
-                mode: RenderTargetMode::None,
-                fbo_ids: vec![],
-                depth_rb: None,
-            };
-
-            debug_assert!(self.textures.contains_key(&texture_id) == false);
-            self.textures.insert(texture_id, texture);
-
-            texture_ids.push(texture_id);
+    pub fn create_texture(&mut self, target: TextureTarget) -> Texture {
+        Texture {
+            id: self.gl.gen_textures(1)[0],
+            target: target.to_gl_target(),
+            width: 0,
+            height: 0,
+            layer_count: 0,
+            format: ImageFormat::Invalid,
+            filter: TextureFilter::Nearest,
+            mode: RenderTargetMode::None,
+            fbo_ids: vec![],
+            depth_rb: None,
         }
-
-        texture_ids
-    }
-
-    pub fn get_texture_layer_count(&self, texture_id: TextureId) -> i32 {
-        let texture = &self.textures[&texture_id];
-        texture.layer_count
-    }
-
-    pub fn get_texture_dimensions(&self, texture_id: TextureId) -> DeviceUintSize {
-        let texture = &self.textures[&texture_id];
-        DeviceUintSize::new(texture.width, texture.height)
     }
 
     fn set_texture_parameters(&mut self, target: gl::GLuint, filter: TextureFilter) {
         let filter = match filter {
             TextureFilter::Nearest => {
                 gl::NEAREST
             }
             TextureFilter::Linear => {
@@ -1006,116 +993,107 @@ impl Device {
         self.gl.tex_parameter_i(target, gl::TEXTURE_MAG_FILTER, filter as gl::GLint);
         self.gl.tex_parameter_i(target, gl::TEXTURE_MIN_FILTER, filter as gl::GLint);
 
         self.gl.tex_parameter_i(target, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as gl::GLint);
         self.gl.tex_parameter_i(target, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as gl::GLint);
     }
 
     pub fn init_texture(&mut self,
-                        texture_id: TextureId,
+                        texture: &mut Texture,
                         width: u32,
                         height: u32,
                         format: ImageFormat,
                         filter: TextureFilter,
                         mode: RenderTargetMode,
                         layer_count: i32,
                         pixels: Option<&[u8]>) {
         debug_assert!(self.inside_frame);
 
-        let resized;
-        {
-            let texture = self.textures.get_mut(&texture_id).expect("Didn't find texture!");
-            texture.format = format;
-            resized = texture.width != width || texture.height != height;
-            texture.width = width;
-            texture.height = height;
-            texture.filter = filter;
-            texture.layer_count = layer_count;
-            texture.mode = mode;
-        }
+        let resized = texture.width != width || texture.height != height;
+
+        texture.format = format;
+        texture.width = width;
+        texture.height = height;
+        texture.filter = filter;
+        texture.layer_count = layer_count;
+        texture.mode = mode;
 
         let (internal_format, gl_format) = gl_texture_formats_for_image_format(self.gl(), format);
         let type_ = gl_type_for_texture_format(format);
 
         match mode {
             RenderTargetMode::RenderTarget => {
-                self.bind_texture(DEFAULT_TEXTURE, texture_id);
-                self.set_texture_parameters(texture_id.target, filter);
-                self.update_texture_storage(texture_id, layer_count, resized);
+                self.bind_texture(DEFAULT_TEXTURE, texture);
+                self.set_texture_parameters(texture.target, filter);
+                self.update_texture_storage(texture, layer_count, resized);
             }
             RenderTargetMode::None => {
-                self.bind_texture(DEFAULT_TEXTURE, texture_id);
-                self.set_texture_parameters(texture_id.target, filter);
+                self.bind_texture(DEFAULT_TEXTURE, texture);
+                self.set_texture_parameters(texture.target, filter);
                 let expanded_data: Vec<u8>;
                 let actual_pixels = if pixels.is_some() &&
                                        format == ImageFormat::A8 &&
                                        cfg!(any(target_arch="arm", target_arch="aarch64")) {
                     expanded_data = pixels.unwrap().iter().flat_map(|&byte| repeat(byte).take(4)).collect();
                     Some(expanded_data.as_slice())
                 } else {
                     pixels
                 };
 
-                match texture_id.target {
+                match texture.target {
                     gl::TEXTURE_2D_ARRAY => {
                         self.gl.tex_image_3d(gl::TEXTURE_2D_ARRAY,
                                              0,
                                              internal_format as gl::GLint,
                                              width as gl::GLint,
                                              height as gl::GLint,
                                              layer_count,
                                              0,
                                              gl_format,
                                              type_,
                                              actual_pixels);
                     }
                     gl::TEXTURE_2D |
                     gl::TEXTURE_RECTANGLE |
                     gl::TEXTURE_EXTERNAL_OES => {
-                        self.gl.tex_image_2d(texture_id.target,
+                        self.gl.tex_image_2d(texture.target,
                                              0,
                                              internal_format as gl::GLint,
                                              width as gl::GLint, height as gl::GLint,
                                              0,
                                              gl_format,
                                              type_,
                                              actual_pixels);
                     }
                     _ => panic!("BUG: Unexpected texture target!"),
                 }
             }
         }
     }
 
-    pub fn get_render_target_layer_count(&self, texture_id: TextureId) -> usize {
-        self.textures[&texture_id].fbo_ids.len()
-    }
-
     /// Updates the texture storage for the texture, creating
     /// FBOs as required.
-    pub fn update_texture_storage(&mut self,
-                                  texture_id: TextureId,
-                                  layer_count: i32,
-                                  resized: bool) {
-        let texture = self.textures.get_mut(&texture_id).unwrap();
-
+    fn update_texture_storage(&mut self,
+                              texture: &mut Texture,
+                              layer_count: i32,
+                              resized: bool) {
         assert!(layer_count > 0);
-        assert_eq!(texture_id.target, gl::TEXTURE_2D_ARRAY);
+        assert_eq!(texture.target, gl::TEXTURE_2D_ARRAY);
 
         let current_layer_count = texture.fbo_ids.len() as i32;
         // If the texture is already the required size skip.
         if current_layer_count == layer_count && !resized {
             return;
         }
 
         let (internal_format, gl_format) = gl_texture_formats_for_image_format(&*self.gl, texture.format);
         let type_ = gl_type_for_texture_format(texture.format);
 
-        self.gl.tex_image_3d(texture_id.target,
+        self.gl.tex_image_3d(texture.target,
                              0,
                              internal_format as gl::GLint,
                              texture.width as gl::GLint,
                              texture.height as gl::GLint,
                              layer_count,
                              0,
                              gl_format,
                              type_,
@@ -1146,38 +1124,38 @@ impl Device {
                                      gl::DEPTH_COMPONENT24,
                                      texture.width as gl::GLsizei,
                                      texture.height as gl::GLsizei);
 
         for (fbo_index, fbo_id) in texture.fbo_ids.iter().enumerate() {
             self.gl.bind_framebuffer(gl::FRAMEBUFFER, fbo_id.0);
             self.gl.framebuffer_texture_layer(gl::FRAMEBUFFER,
                                               gl::COLOR_ATTACHMENT0,
-                                              texture_id.name,
+                                              texture.id,
                                               0,
                                               fbo_index as gl::GLint);
             self.gl.framebuffer_renderbuffer(gl::FRAMEBUFFER,
                                              gl::DEPTH_ATTACHMENT,
                                              gl::RENDERBUFFER,
                                              depth_rb);
         }
 
         // TODO(gw): Hack! Modify the code above to use the normal binding interfaces the device exposes.
         self.gl.bind_framebuffer(gl::READ_FRAMEBUFFER, self.bound_read_fbo.0);
         self.gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, self.bound_draw_fbo.0);
     }
 
     pub fn blit_render_target(&mut self,
-                              src_texture: Option<(TextureId, i32)>,
+                              src_texture: Option<(&Texture, i32)>,
                               src_rect: Option<DeviceIntRect>,
                               dest_rect: DeviceIntRect) {
         debug_assert!(self.inside_frame);
 
         let src_rect = src_rect.unwrap_or_else(|| {
-            let texture = self.textures.get(&src_texture.unwrap().0).expect("unknown texture id!");
+            let texture = src_texture.unwrap().0;
             DeviceIntRect::new(DeviceIntPoint::zero(),
                                DeviceIntSize::new(texture.width as gl::GLint,
                                                   texture.height as gl::GLint))
         });
 
         self.bind_read_target(src_texture);
 
         self.gl.blit_framebuffer(src_rect.origin.x,
@@ -1187,67 +1165,75 @@ impl Device {
                                   dest_rect.origin.x,
                                   dest_rect.origin.y,
                                   dest_rect.origin.x + dest_rect.size.width,
                                   dest_rect.origin.y + dest_rect.size.height,
                                   gl::COLOR_BUFFER_BIT,
                                   gl::LINEAR);
     }
 
-    pub fn deinit_texture(&mut self, texture_id: TextureId) {
+    pub fn free_texture_storage(&mut self, texture: &mut Texture) {
         debug_assert!(self.inside_frame);
 
-        self.bind_texture(DEFAULT_TEXTURE, texture_id);
+        if texture.format == ImageFormat::Invalid {
+            return;
+        }
 
-        let texture = self.textures.get_mut(&texture_id).unwrap();
+        self.bind_texture(DEFAULT_TEXTURE, texture);
+
         let (internal_format, gl_format) = gl_texture_formats_for_image_format(&*self.gl, texture.format);
         let type_ = gl_type_for_texture_format(texture.format);
 
-        match texture_id.target {
+        match texture.target {
             gl::TEXTURE_2D_ARRAY => {
                 self.gl.tex_image_3d(gl::TEXTURE_2D_ARRAY,
                                      0,
                                      internal_format as gl::GLint,
                                      0,
                                      0,
                                      0,
                                      0,
                                      gl_format,
                                      type_,
                                      None);
             }
             _ => {
-                self.gl.tex_image_2d(texture_id.target,
+                self.gl.tex_image_2d(texture.target,
                                      0,
                                      internal_format,
                                      0,
                                      0,
                                      0,
                                      gl_format,
                                      type_,
                                      None);
             }
         }
 
-
         if let Some(RBOId(depth_rb)) = texture.depth_rb.take() {
             self.gl.delete_renderbuffers(&[depth_rb]);
         }
 
         if !texture.fbo_ids.is_empty() {
             let fbo_ids: Vec<_> = texture.fbo_ids.drain(..).map(|FBOId(fbo_id)| fbo_id).collect();
             self.gl.delete_framebuffers(&fbo_ids[..]);
         }
 
         texture.format = ImageFormat::Invalid;
         texture.width = 0;
         texture.height = 0;
         texture.layer_count = 0;
     }
 
+    pub fn delete_texture(&mut self, mut texture: Texture) {
+        self.free_texture_storage(&mut texture);
+        self.gl.delete_textures(&[texture.id]);
+        texture.id = 0;
+    }
+
     pub fn delete_program(&mut self, mut program: Program) {
         self.gl.delete_program(program.id);
         program.id = 0;
     }
 
     pub fn create_program(&mut self,
                           base_filename: &str,
                           features: &str,
@@ -1321,58 +1307,29 @@ impl Device {
         let program = Program {
             id: pid,
             u_transform,
             u_device_pixel_ratio,
         };
 
         self.bind_program(&program);
 
-        // TODO(gw): Abstract these to not be part of the device code!
-        let u_color_0 = self.gl.get_uniform_location(program.id, "sColor0");
-        if u_color_0 != -1 {
-            self.gl.uniform_1i(u_color_0, TextureSampler::Color0 as i32);
-        }
-        let u_color1 = self.gl.get_uniform_location(program.id, "sColor1");
-        if u_color1 != -1 {
-            self.gl.uniform_1i(u_color1, TextureSampler::Color1 as i32);
-        }
-        let u_color_2 = self.gl.get_uniform_location(program.id, "sColor2");
-        if u_color_2 != -1 {
-            self.gl.uniform_1i(u_color_2, TextureSampler::Color2 as i32);
-        }
-        let u_noise = self.gl.get_uniform_location(program.id, "sDither");
-        if u_noise != -1 {
-            self.gl.uniform_1i(u_noise, TextureSampler::Dither as i32);
-        }
-        let u_cache_a8 = self.gl.get_uniform_location(program.id, "sCacheA8");
-        if u_cache_a8 != -1 {
-            self.gl.uniform_1i(u_cache_a8, TextureSampler::CacheA8 as i32);
+        Ok(program)
+    }
+
+    pub fn bind_shader_samplers<S>(&mut self,
+                                   program: &Program,
+                                   bindings: &[(&'static str, S)]) where S: Into<TextureSlot> + Copy {
+        for binding in bindings {
+            let u_location = self.gl.get_uniform_location(program.id, binding.0);
+            if u_location != -1 {
+                self.bind_program(program);
+                self.gl.uniform_1i(u_location, binding.1.into().0 as gl::GLint);
+            }
         }
-        let u_cache_rgba8 = self.gl.get_uniform_location(program.id, "sCacheRGBA8");
-        if u_cache_rgba8 != -1 {
-            self.gl.uniform_1i(u_cache_rgba8, TextureSampler::CacheRGBA8 as i32);
-        }
-
-        let u_layers = self.gl.get_uniform_location(program.id, "sLayers");
-        if u_layers != -1 {
-            self.gl.uniform_1i(u_layers, TextureSampler::Layers as i32);
-        }
-
-        let u_tasks = self.gl.get_uniform_location(program.id, "sRenderTasks");
-        if u_tasks != -1 {
-            self.gl.uniform_1i(u_tasks, TextureSampler::RenderTasks as i32);
-        }
-
-        let u_resource_cache = self.gl.get_uniform_location(program.id, "sResourceCache");
-        if u_resource_cache != -1 {
-            self.gl.uniform_1i(u_resource_cache, TextureSampler::ResourceCache as i32);
-        }
-
-        Ok(program)
     }
 
     pub fn get_uniform_location(&self, program: &Program, name: &str) -> UniformLocation {
         UniformLocation(self.gl.get_uniform_location(program.id, name))
     }
 
     pub fn set_uniform_2f(&self, uniform: UniformLocation, x: f32, y: f32) {
         debug_assert!(self.inside_frame);
@@ -1426,27 +1383,27 @@ impl Device {
 
         self.gl.buffer_data_untyped(gl::PIXEL_UNPACK_BUFFER,
                                     new_size as isize,
                                     ptr::null(),
                                     gl::STREAM_DRAW);
     }
 
     pub fn update_texture_from_pbo(&mut self,
-                                   texture_id: TextureId,
+                                   texture: &Texture,
                                    x0: u32,
                                    y0: u32,
                                    width: u32,
                                    height: u32,
                                    layer_index: i32,
                                    stride: Option<u32>,
                                    offset: usize) {
         debug_assert!(self.inside_frame);
 
-        let (gl_format, bpp, data_type) = match self.textures.get(&texture_id).unwrap().format {
+        let (gl_format, bpp, data_type) = match texture.format {
             ImageFormat::A8 => (GL_FORMAT_A, 1, gl::UNSIGNED_BYTE),
             ImageFormat::RGB8 => (gl::RGB, 3, gl::UNSIGNED_BYTE),
             ImageFormat::BGRA8 => (get_gl_format_bgra(self.gl()), 4, gl::UNSIGNED_BYTE),
             ImageFormat::RG8 => (gl::RG, 2, gl::UNSIGNED_BYTE),
             ImageFormat::RGBAF32 => (gl::RGBA, 16, gl::FLOAT),
             ImageFormat::Invalid => unreachable!(),
         };
 
@@ -1454,36 +1411,36 @@ impl Device {
             Some(value) => value / bpp,
             None => width,
         };
 
         if let Some(..) = stride {
             self.gl.pixel_store_i(gl::UNPACK_ROW_LENGTH, row_length as gl::GLint);
         }
 
-        self.bind_texture(DEFAULT_TEXTURE, texture_id);
+        self.bind_texture(DEFAULT_TEXTURE, texture);
 
-        match texture_id.target {
+        match texture.target {
             gl::TEXTURE_2D_ARRAY => {
-                self.gl.tex_sub_image_3d_pbo(texture_id.target,
+                self.gl.tex_sub_image_3d_pbo(texture.target,
                                              0,
                                              x0 as gl::GLint,
                                              y0 as gl::GLint,
                                              layer_index,
                                              width as gl::GLint,
                                              height as gl::GLint,
                                              1,
                                              gl_format,
                                              data_type,
                                              offset);
             }
             gl::TEXTURE_2D |
             gl::TEXTURE_RECTANGLE |
             gl::TEXTURE_EXTERNAL_OES => {
-                self.gl.tex_sub_image_2d_pbo(texture_id.target,
+                self.gl.tex_sub_image_2d_pbo(texture.target,
                                              0,
                                              x0 as gl::GLint,
                                              y0 as gl::GLint,
                                              width as gl::GLint,
                                              height as gl::GLint,
                                              gl_format,
                                              data_type,
                                              offset);
@@ -1506,57 +1463,55 @@ impl Device {
         }
     }
 
     fn create_vao_with_vbos(&mut self,
                             descriptor: &VertexDescriptor,
                             main_vbo_id: VBOId,
                             instance_vbo_id: VBOId,
                             ibo_id: IBOId,
-                            instance_stride: gl::GLint,
                             owns_vertices_and_indices: bool)
                             -> VAO {
         debug_assert!(self.inside_frame);
 
+        let instance_stride = descriptor.instance_stride();
         let vao_id = self.gl.gen_vertex_arrays(1)[0];
 
         self.gl.bind_vertex_array(vao_id);
 
         descriptor.bind(self.gl(), main_vbo_id, instance_vbo_id);
         ibo_id.bind(self.gl()); // force it to be a part of VAO
 
         let vao = VAO {
             id: vao_id,
             ibo_id,
             main_vbo_id,
             instance_vbo_id,
-            instance_stride,
+            instance_stride: instance_stride as usize,
             owns_vertices_and_indices,
         };
 
         self.gl.bind_vertex_array(0);
 
         vao
     }
 
     pub fn create_vao(&mut self,
-                      descriptor: &VertexDescriptor,
-                      inst_stride: gl::GLint) -> VAO {
+                      descriptor: &VertexDescriptor) -> VAO {
         debug_assert!(self.inside_frame);
 
         let buffer_ids = self.gl.gen_buffers(3);
         let ibo_id = IBOId(buffer_ids[0]);
         let main_vbo_id = VBOId(buffer_ids[1]);
         let intance_vbo_id = VBOId(buffer_ids[2]);
 
         self.create_vao_with_vbos(descriptor,
                                   main_vbo_id,
                                   intance_vbo_id,
                                   ibo_id,
-                                  inst_stride,
                                   true)
     }
 
     pub fn delete_vao(&mut self, mut vao: VAO) {
         self.gl.delete_vertex_arrays(&[vao.id]);
         vao.id = 0;
 
         if vao.owns_vertices_and_indices {
@@ -1564,28 +1519,26 @@ impl Device {
             self.gl.delete_buffers(&[vao.main_vbo_id.0]);
         }
 
         self.gl.delete_buffers(&[vao.instance_vbo_id.0])
     }
 
     pub fn create_vao_with_new_instances(&mut self,
                                          descriptor: &VertexDescriptor,
-                                         inst_stride: gl::GLint,
                                          base_vao: &VAO) -> VAO {
         debug_assert!(self.inside_frame);
 
         let buffer_ids = self.gl.gen_buffers(1);
         let intance_vbo_id = VBOId(buffer_ids[0]);
 
         self.create_vao_with_vbos(descriptor,
                                   base_vao.main_vbo_id,
                                   intance_vbo_id,
                                   base_vao.ibo_id,
-                                  inst_stride,
                                   false)
     }
 
     pub fn update_vao_main_vertices<V>(&mut self,
                                        vao: &VAO,
                                        vertices: &[V],
                                        usage_hint: VertexUsageHint) {
         debug_assert!(self.inside_frame);
--- a/gfx/webrender/src/frame.rs
+++ b/gfx/webrender/src/frame.rs
@@ -31,16 +31,17 @@ static DEFAULT_SCROLLBAR_COLOR: ColorF =
 ///        contained the nested display list.
 ///     2. Other ClipIds (that aren't custom or reference frames) are assumed to be
 ///        local to the nested display list and are converted to an id that is unique
 ///        outside of the nested display list as well.
 ///
 /// This structure keeps track of what ids are the "root" for one particular level of
 /// nesting as well as keeping and index, which can make ClipIds used internally unique
 /// in the full ClipScrollTree.
+#[derive(Debug)]
 struct NestedDisplayListInfo {
     /// The index of this nested display list, which is used to generate
     /// new ClipIds for clips that are defined inside it.
     nest_index: u64,
 
     /// The ClipId of the scroll frame node which contains this nested
     /// display list. This is used to replace references to the root with
     /// the proper ClipId.
@@ -77,16 +78,23 @@ impl NestedDisplayListInfo {
         }
 
         if id.is_root_scroll_node() {
             self.clip_node_id
         } else {
             self.convert_id_to_nested(id)
         }
     }
+
+    fn convert_new_id_to_nested(&self, id: &ClipId) -> ClipId {
+        if id.pipeline_id() != self.clip_node_id.pipeline_id() {
+            return *id;
+        }
+        self.convert_id_to_nested(id)
+    }
 }
 
 struct FlattenContext<'a> {
     scene: &'a Scene,
     builder: &'a mut FrameBuilder,
     tiled_image_map: TiledImageMap,
     replacements: Vec<(ClipId, ClipId)>,
     nested_display_list_info: Vec<NestedDisplayListInfo>,
@@ -118,17 +126,17 @@ impl<'a> FlattenContext<'a> {
     }
 
     fn pop_nested_display_list_ids(&mut self) {
         self.nested_display_list_info.pop();
     }
 
     fn convert_new_id_to_nested(&self, id: &ClipId) -> ClipId {
         if let Some(nested_info) = self.nested_display_list_info.last() {
-            nested_info.convert_id_to_nested(id)
+            nested_info.convert_new_id_to_nested(id)
         } else {
             *id
         }
     }
 
     fn convert_clip_scroll_info_to_nested(&self, info: &mut ClipAndScrollInfo) {
         if let Some(nested_info) = self.nested_display_list_info.last() {
             info.scroll_node_id = nested_info.convert_scroll_id_to_nested(&info.scroll_node_id);
new file mode 100644
--- /dev/null
+++ b/gfx/webrender/src/gpu_types.rs
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use gpu_cache::GpuCacheAddress;
+use render_task::RenderTaskAddress;
+
+// Contains type that must exactly match the same structures declared in GLSL.
+
+// Instance structure for box shadows being drawn into target cache.
+#[derive(Debug)]
+#[repr(C)]
+pub struct BoxShadowCacheInstance {
+    pub prim_address: GpuCacheAddress,
+    pub task_index: RenderTaskAddress,
+}
--- a/gfx/webrender/src/internal_types.rs
+++ b/gfx/webrender/src/internal_types.rs
@@ -46,42 +46,16 @@ pub enum SourceTexture {
     External(ExternalImageData),
     CacheA8,
     CacheRGBA8,
 }
 
 pub const ORTHO_NEAR_PLANE: f32 = -1000000.0;
 pub const ORTHO_FAR_PLANE: f32 = 1000000.0;
 
-#[derive(Debug, PartialEq, Eq)]
-pub enum TextureSampler {
-    Color0,
-    Color1,
-    Color2,
-    CacheA8,
-    CacheRGBA8,
-    ResourceCache,
-    Layers,
-    RenderTasks,
-    Dither,
-}
-
-impl TextureSampler {
-    pub fn color(n: usize) -> TextureSampler {
-        match n {
-            0 => TextureSampler::Color0,
-            1 => TextureSampler::Color1,
-            2 => TextureSampler::Color2,
-            _ => {
-                panic!("There are only 3 color samplers.");
-            }
-        }
-    }
-}
-
 /// Optional textures that can be used as a source in the shaders.
 /// Textures that are not used by the batch are equal to TextureId::invalid().
 #[derive(Copy, Clone, Debug)]
 pub struct BatchTextures {
     pub colors: [SourceTexture; 3],
 }
 
 impl BatchTextures {
@@ -97,19 +71,16 @@ impl BatchTextures {
                 SourceTexture::CacheRGBA8,
                 SourceTexture::Invalid,
                 SourceTexture::Invalid,
             ]
         }
     }
 }
 
-// In some places we need to temporarily bind a texture to any slot.
-pub const DEFAULT_TEXTURE: TextureSampler = TextureSampler::Color0;
-
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum RenderTargetMode {
     None,
     RenderTarget,
 }
 
 #[derive(Debug)]
 pub enum TextureUpdateSource {
--- a/gfx/webrender/src/lib.rs
+++ b/gfx/webrender/src/lib.rs
@@ -61,16 +61,17 @@ mod device;
 mod ellipse;
 mod frame;
 mod frame_builder;
 mod freelist;
 mod geometry;
 mod glyph_cache;
 mod glyph_rasterizer;
 mod gpu_cache;
+mod gpu_types;
 mod internal_types;
 mod mask_cache;
 mod prim_store;
 mod print_tree;
 mod profiler;
 mod record;
 mod render_backend;
 mod render_task;
--- a/gfx/webrender/src/render_task.rs
+++ b/gfx/webrender/src/render_task.rs
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use gpu_cache::GpuCacheHandle;
 use internal_types::HardwareCompositeOp;
 use mask_cache::MaskCacheInfo;
 use prim_store::{BoxShadowPrimitiveCacheKey, PrimitiveIndex};
-use std::{cmp, f32, i32, mem, usize};
+use std::{cmp, f32, i32, usize};
 use tiling::{ClipScrollGroupIndex, PackedLayerIndex, RenderPass, RenderTargetIndex};
 use tiling::{RenderTargetKind, StackingContextIndex};
 use api::{ClipId, DeviceIntLength, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
 use api::{FilterOp, MixBlendMode};
 
 const FLOATS_PER_RENDER_TASK_INFO: usize = 12;
 
 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
@@ -157,24 +157,16 @@ pub struct CacheMaskTask {
     pub geometry_kind: MaskGeometryKind,
 }
 
 #[derive(Debug, Clone)]
 pub struct RenderTaskData {
     pub data: [f32; FLOATS_PER_RENDER_TASK_INFO],
 }
 
-impl Default for RenderTaskData {
-    fn default() -> RenderTaskData {
-        RenderTaskData {
-            data: unsafe { mem::uninitialized() },
-        }
-    }
-}
-
 #[derive(Debug, Clone)]
 pub enum RenderTaskKind {
     Alpha(AlphaRenderTask),
     CachePrimitive(PrimitiveIndex),
     CacheMask(CacheMaskTask),
     VerticalBlur(DeviceIntLength),
     HorizontalBlur(DeviceIntLength),
     Readback(DeviceIntRect),
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -12,62 +12,59 @@
 #[cfg(not(feature = "debugger"))]
 use api::ApiMsg;
 #[cfg(not(feature = "debugger"))]
 use api::channel::MsgSender;
 use api::DebugCommand;
 use debug_colors;
 use debug_render::DebugRenderer;
 #[cfg(feature = "debugger")]
-use debug_server::{BatchList, DebugMsg, DebugServer};
-use device::{DepthFunction, Device, FrameId, Program, TextureId, VertexDescriptor, GpuMarker, GpuProfiler, PBOId};
+use debug_server::{self, DebugMsg, DebugServer};
+use device::{DepthFunction, Device, FrameId, Program, Texture, VertexDescriptor, GpuMarker, GpuProfiler, PBOId};
 use device::{GpuTimer, TextureFilter, VAO, VertexUsageHint, FileWatcherHandler, TextureTarget, ShaderError};
-use device::{get_gl_format_bgra, VertexAttribute, VertexAttributeKind};
+use device::{ExternalTexture, get_gl_format_bgra, TextureSlot, VertexAttribute, VertexAttributeKind};
 use euclid::{Transform3D, rect};
 use frame_builder::FrameBuilderConfig;
 use gleam::gl;
 use gpu_cache::{GpuBlockData, GpuCacheUpdate, GpuCacheUpdateList};
 use internal_types::{FastHashMap, CacheTextureId, RendererFrame, ResultMsg, TextureUpdateOp};
 use internal_types::{TextureUpdateList, RenderTargetMode, TextureUpdateSource};
-use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, SourceTexture};
-use internal_types::{BatchTextures, TextureSampler};
+use internal_types::{BatchTextures, ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, SourceTexture};
 use profiler::{Profiler, BackendProfileCounters};
 use profiler::{GpuProfileTag, RendererProfileTimers, RendererProfileCounters};
 use record::ApiRecordingReceiver;
 use render_backend::RenderBackend;
 use render_task::RenderTaskTree;
 #[cfg(feature = "debugger")]
 use serde_json;
 use std;
 use std::cmp;
 use std::collections::VecDeque;
 use std::f32;
-use std::marker::PhantomData;
 use std::mem;
 use std::path::PathBuf;
 use std::rc::Rc;
 use std::sync::{Arc, Mutex};
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::thread;
 use texture_cache::TextureCache;
 use rayon::ThreadPool;
 use rayon::Configuration as ThreadPoolConfig;
-use tiling::{AlphaBatchKey, AlphaBatchKind, BlurCommand, Frame, RenderTarget};
-use tiling::{AlphaRenderTarget, CacheClipInstance, PrimitiveInstance, ColorRenderTarget, RenderTargetKind};
+use tiling::{AlphaBatchKey, AlphaBatchKind, Frame, RenderTarget};
+use tiling::{AlphaRenderTarget, PrimitiveInstance, ColorRenderTarget, RenderTargetKind};
 use time::precise_time_ns;
 use thread_profiler::{register_thread_with_profiler, write_profile};
 use util::TransformedRectKind;
 use api::{ColorF, Epoch, PipelineId, RenderApiSender, RenderNotifier};
 use api::{ExternalImageId, ExternalImageType, ImageFormat};
 use api::{DeviceIntRect, DeviceUintRect, DeviceIntPoint, DeviceIntSize, DeviceUintSize};
 use api::{BlobImageRenderer, channel, FontRenderMode};
 use api::{YuvColorSpace, YuvFormat};
 use api::{YUV_COLOR_SPACES, YUV_FORMATS};
 
-pub const GPU_DATA_TEXTURE_POOL: usize = 5;
 pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024;
 
 const GPU_TAG_CACHE_BOX_SHADOW: GpuProfileTag = GpuProfileTag { label: "C_BoxShadow", color: debug_colors::BLACK };
 const GPU_TAG_CACHE_CLIP: GpuProfileTag = GpuProfileTag { label: "C_Clip", color: debug_colors::PURPLE };
 const GPU_TAG_CACHE_TEXT_RUN: GpuProfileTag = GpuProfileTag { label: "C_TextRun", color: debug_colors::MISTYROSE };
 const GPU_TAG_CACHE_LINE: GpuProfileTag = GpuProfileTag { label: "C_Line", color: debug_colors::BROWN };
 const GPU_TAG_SETUP_TARGET: GpuProfileTag = GpuProfileTag { label: "target", color: debug_colors::SLATEGREY };
 const GPU_TAG_SETUP_DATA: GpuProfileTag = GpuProfileTag { label: "data init", color: debug_colors::LIGHTGREY };
@@ -98,17 +95,24 @@ impl AlphaBatchKind {
     fn debug_name(&self) -> &'static str {
         match *self {
             AlphaBatchKind::Composite { .. } => "Composite",
             AlphaBatchKind::HardwareComposite => "HardwareComposite",
             AlphaBatchKind::SplitComposite => "SplitComposite",
             AlphaBatchKind::Blend => "Blend",
             AlphaBatchKind::Rectangle => "Rectangle",
             AlphaBatchKind::TextRun => "TextRun",
-            AlphaBatchKind::Image(..) => "Image",
+            AlphaBatchKind::Image(image_buffer_kind, ..) => {
+                match image_buffer_kind {
+                    ImageBufferKind::Texture2D => "Image (2D)",
+                    ImageBufferKind::TextureRect => "Image (Rect)",
+                    ImageBufferKind::TextureExternal => "Image (External)",
+                    ImageBufferKind::Texture2DArray => "Image (Array)",
+                }
+            },
             AlphaBatchKind::YuvImage(..) => "YuvImage",
             AlphaBatchKind::AlignedGradient => "AlignedGradient",
             AlphaBatchKind::AngleGradient => "AngleGradient",
             AlphaBatchKind::RadialGradient => "RadialGradient",
             AlphaBatchKind::BoxShadow => "BoxShadow",
             AlphaBatchKind::CacheImage => "CacheImage",
             AlphaBatchKind::BorderCorner => "BorderCorner",
             AlphaBatchKind::BorderEdge => "BorderEdge",
@@ -121,16 +125,58 @@ bitflags! {
     #[derive(Default)]
     pub struct DebugFlags: u32 {
         const PROFILER_DBG      = 1 << 0;
         const RENDER_TARGET_DBG = 1 << 1;
         const TEXTURE_CACHE_DBG = 1 << 2;
     }
 }
 
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+enum TextureSampler {
+    Color0,
+    Color1,
+    Color2,
+    CacheA8,
+    CacheRGBA8,
+    ResourceCache,
+    Layers,
+    RenderTasks,
+    Dither,
+}
+
+impl TextureSampler {
+    fn color(n: usize) -> TextureSampler {
+        match n {
+            0 => TextureSampler::Color0,
+            1 => TextureSampler::Color1,
+            2 => TextureSampler::Color2,
+            _ => {
+                panic!("There are only 3 color samplers.");
+            }
+        }
+    }
+}
+
+impl Into<TextureSlot> for TextureSampler {
+    fn into(self) -> TextureSlot {
+        match self {
+            TextureSampler::Color0 => TextureSlot(0),
+            TextureSampler::Color1 => TextureSlot(1),
+            TextureSampler::Color2 => TextureSlot(2),
+            TextureSampler::CacheA8 => TextureSlot(3),
+            TextureSampler::CacheRGBA8 => TextureSlot(4),
+            TextureSampler::ResourceCache => TextureSlot(5),
+            TextureSampler::Layers => TextureSlot(6),
+            TextureSampler::RenderTasks => TextureSlot(7),
+            TextureSampler::Dither => TextureSlot(8),
+        }
+    }
+}
+
 #[derive(Debug, Clone, Copy)]
 #[repr(C)]
 pub struct PackedVertex {
     pub pos: [f32; 2],
 }
 
 const DESC_PRIM_INSTANCES: VertexDescriptor = VertexDescriptor {
     vertex_attributes: &[
@@ -160,20 +206,32 @@ const DESC_CLIP: VertexDescriptor = Vert
     instance_attributes: &[
         VertexAttribute { name: "aClipRenderTaskIndex", count: 1, kind: VertexAttributeKind::I32 },
         VertexAttribute { name: "aClipLayerIndex", count: 1, kind: VertexAttributeKind::I32 },
         VertexAttribute { name: "aClipSegment", count: 1, kind: VertexAttributeKind::I32 },
         VertexAttribute { name: "aClipDataResourceAddress", count: 4, kind: VertexAttributeKind::U16 },
     ]
 };
 
+const DESC_CACHE_BOX_SHADOW: VertexDescriptor = VertexDescriptor {
+    vertex_attributes: &[
+        VertexAttribute { name: "aPosition", count: 2, kind: VertexAttributeKind::F32 },
+    ],
+    instance_attributes: &[
+        VertexAttribute { name: "aPrimAddress", count: 2, kind: VertexAttributeKind::U16 },
+        VertexAttribute { name: "aTaskIndex", count: 1, kind: VertexAttributeKind::I32 },
+    ]
+};
+
 enum VertexArrayKind {
     Primitive,
     Blur,
     Clip,
+
+    CacheBoxShadow,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub enum VertexFormat {
     PrimitiveInstances,
     Blur,
 }
 
@@ -287,84 +345,117 @@ struct SourceTextureResolver {
     /// A vector for fast resolves of texture cache IDs to
     /// native texture IDs. This maps to a free-list managed
     /// by the backend thread / texture cache. We free the
     /// texture memory associated with a TextureId when its
     /// texture cache ID is freed by the texture cache, but
     /// reuse the TextureId when the texture caches's free
     /// list reuses the texture cache ID. This saves having to
     /// use a hashmap, and allows a flat vector for performance.
-    cache_texture_id_map: Vec<TextureId>,
+    cache_texture_map: Vec<Texture>,
 
     /// Map of external image IDs to native textures.
-    external_images: FastHashMap<(ExternalImageId, u8), TextureId>,
+    external_images: FastHashMap<(ExternalImageId, u8), ExternalTexture>,
 
     /// A special 1x1 dummy cache texture used for shaders that expect to work
     /// with the cache but are actually running in the first pass
     /// when no target is yet provided as a cache texture input.
-    dummy_cache_texture_id: TextureId,
+    dummy_cache_texture: Texture,
 
     /// The current cache textures.
-    cache_rgba8_texture: Option<TextureId>,
-    cache_a8_texture: Option<TextureId>,
+    cache_rgba8_texture: Option<Texture>,
+    cache_a8_texture: Option<Texture>,
 }
 
 impl SourceTextureResolver {
     fn new(device: &mut Device) -> SourceTextureResolver {
-        let dummy_cache_texture_id = device.create_texture_ids(1, TextureTarget::Array)[0];
-        device.init_texture(dummy_cache_texture_id,
+        let mut dummy_cache_texture = device.create_texture(TextureTarget::Array);
+        device.init_texture(&mut dummy_cache_texture,
                             1,
                             1,
                             ImageFormat::BGRA8,
                             TextureFilter::Linear,
                             RenderTargetMode::RenderTarget,
                             1,
                             None);
 
         SourceTextureResolver {
-            cache_texture_id_map: Vec::new(),
+            cache_texture_map: Vec::new(),
             external_images: FastHashMap::default(),
-            dummy_cache_texture_id,
+            dummy_cache_texture,
             cache_a8_texture: None,
             cache_rgba8_texture: None,
         }
     }
 
     fn deinit(self, device: &mut Device) {
-        device.deinit_texture(self.dummy_cache_texture_id);
+        device.delete_texture(self.dummy_cache_texture);
+
+        for texture in self.cache_texture_map {
+            device.delete_texture(texture);
+        }
     }
 
     fn set_cache_textures(&mut self,
-                          a8_texture: Option<TextureId>,
-                          rgba8_texture: Option<TextureId>) {
+                          a8_texture: Option<Texture>,
+                          rgba8_texture: Option<Texture>) {
+        // todo(gw): make the texture recycling cleaner...
+        debug_assert!(self.cache_a8_texture.is_none());
+        debug_assert!(self.cache_rgba8_texture.is_none());
         self.cache_a8_texture = a8_texture;
         self.cache_rgba8_texture = rgba8_texture;
     }
 
+    // Bind a source texture to the device.
+    fn bind(&self,
+            texture_id: &SourceTexture,
+            sampler: TextureSampler,
+            device: &mut Device) {
+        match *texture_id {
+            SourceTexture::Invalid => {}
+            SourceTexture::CacheA8 => {
+                let texture = self.cache_a8_texture.as_ref().unwrap_or(&self.dummy_cache_texture);
+                device.bind_texture(sampler, texture);
+            }
+            SourceTexture::CacheRGBA8 => {
+                let texture = self.cache_rgba8_texture.as_ref().unwrap_or(&self.dummy_cache_texture);
+                device.bind_texture(sampler, texture);
+            }
+            SourceTexture::External(external_image) => {
+                let texture = self.external_images
+                                  .get(&(external_image.id, external_image.channel_index))
+                                  .expect("BUG: External image should be resolved by now!");
+                device.bind_external_texture(sampler, texture);
+            }
+            SourceTexture::TextureCache(index) => {
+                let texture = &self.cache_texture_map[index.0];
+                device.bind_texture(sampler, texture);
+            }
+        }
+    }
+
     // Get the real (OpenGL) texture ID for a given source texture.
     // For a texture cache texture, the IDs are stored in a vector
     // map for fast access.
-    fn resolve(&self, texture_id: &SourceTexture) -> TextureId {
+    fn resolve(&self, texture_id: &SourceTexture) -> Option<&Texture> {
         match *texture_id {
             SourceTexture::Invalid => {
-                TextureId::invalid()
+                None
             }
             SourceTexture::CacheA8 => {
-                self.cache_a8_texture.unwrap_or(self.dummy_cache_texture_id)
+                Some(self.cache_a8_texture.as_ref().unwrap_or(&self.dummy_cache_texture))
             }
             SourceTexture::CacheRGBA8 => {
-                self.cache_rgba8_texture.unwrap_or(self.dummy_cache_texture_id)
+                Some(self.cache_rgba8_texture.as_ref().unwrap_or(&self.dummy_cache_texture))
             }
-            SourceTexture::External(external_image) => {
-                *self.external_images
-                     .get(&(external_image.id, external_image.channel_index))
-                     .expect("BUG: External image should be resolved by now!")
+            SourceTexture::External(..) => {
+                panic!("BUG: External textures cannot be resolved, they can only be bound.");
             }
             SourceTexture::TextureCache(index) => {
-                self.cache_texture_id_map[index.0]
+                Some(&self.cache_texture_map[index.0])
             }
         }
     }
 }
 
 #[derive(Debug, Copy, Clone, PartialEq)]
 pub enum BlendMode {
     None,
@@ -385,35 +476,39 @@ impl CacheRow {
         CacheRow {
             is_dirty: false,
         }
     }
 }
 
 /// The device-specific representation of the cache texture in gpu_cache.rs
 struct CacheTexture {
-    texture_id: TextureId,
+    texture: Texture,
     pbo_id: PBOId,
     rows: Vec<CacheRow>,
     cpu_blocks: Vec<GpuBlockData>,
 }
 
 impl CacheTexture {
     fn new(device: &mut Device) -> CacheTexture {
-        let texture_id = device.create_texture_ids(1, TextureTarget::Default)[0];
+        let texture = device.create_texture(TextureTarget::Default);
         let pbo_id = device.create_pbo();
 
         CacheTexture {
-            texture_id,
+            texture,
             pbo_id,
             rows: Vec::new(),
             cpu_blocks: Vec::new(),
         }
     }
 
+    fn deinit(self, device: &mut Device) {
+        device.delete_texture(self.texture);
+    }
+
     fn apply_patch(&mut self,
                    update: &GpuCacheUpdate,
                    blocks: &[GpuBlockData]) {
         match update {
             &GpuCacheUpdate::Copy { block_index, block_count, address } => {
                 let row = address.v as usize;
 
                 // Ensure that the CPU-side shadow copy of the GPU cache data has enough
@@ -435,21 +530,21 @@ impl CacheTexture {
                     data[i] = blocks[block_index + i];
                 }
             }
         }
     }
 
     fn update(&mut self, device: &mut Device, updates: &GpuCacheUpdateList) {
         // See if we need to create or resize the texture.
-        let current_dimensions = device.get_texture_dimensions(self.texture_id);
+        let current_dimensions = self.texture.get_dimensions();
         if updates.height > current_dimensions.height {
             // Create a f32 texture that can be used for the vertex shader
             // to fetch data from.
-            device.init_texture(self.texture_id,
+            device.init_texture(&mut self.texture,
                                 MAX_VERTEX_TEXTURE_WIDTH as u32,
                                 updates.height as u32,
                                 ImageFormat::RGBAF32,
                                 TextureFilter::Nearest,
                                 RenderTargetMode::None,
                                 1,
                                 None);
 
@@ -478,17 +573,17 @@ impl CacheTexture {
             if row.is_dirty {
                 // Get the data for this row and push to the PBO.
                 let block_index = row_index * MAX_VERTEX_TEXTURE_WIDTH;
                 let cpu_blocks = &self.cpu_blocks[block_index..(block_index + MAX_VERTEX_TEXTURE_WIDTH)];
                 device.update_pbo_data(cpu_blocks);
 
                 // Insert a command to copy the PBO data to the right place in
                 // the GPU-side cache texture.
-                device.update_texture_from_pbo(self.texture_id,
+                device.update_texture_from_pbo(&self.texture,
                                                0,
                                                row_index as u32,
                                                MAX_VERTEX_TEXTURE_WIDTH as u32,
                                                1,
                                                0,
                                                None,
                                                0);
 
@@ -502,117 +597,93 @@ impl CacheTexture {
             }
         }
 
         // Ensure that other texture updates won't read from this PBO.
         device.bind_pbo(None);
     }
 }
 
-
-trait GpuStoreLayout {
-    fn image_format() -> ImageFormat;
-
-    fn texture_width<T>() -> usize;
+struct VertexDataTexture {
+    texture: Texture,
+    pbo: PBOId,
+}
 
-    fn texture_filter() -> TextureFilter;
+impl VertexDataTexture {
+    fn new(device: &mut Device) -> VertexDataTexture {
+        let texture = device.create_texture(TextureTarget::Default);
+        let pbo = device.create_pbo();
 
-    fn texel_size() -> usize {
-        match Self::image_format() {
-            ImageFormat::BGRA8 => 4,
-            ImageFormat::RGBAF32 => 16,
-            _ => unreachable!(),
+        VertexDataTexture {
+            texture,
+            pbo,
         }
     }
 
-    fn texels_per_item<T>() -> usize {
-        let item_size = mem::size_of::<T>();
-        let texel_size = Self::texel_size();
-        debug_assert!(item_size % texel_size == 0);
-        item_size / texel_size
-    }
-
-    fn items_per_row<T>() -> usize {
-        Self::texture_width::<T>() / Self::texels_per_item::<T>()
-    }
-
-    fn rows_per_item<T>() -> usize {
-        Self::texels_per_item::<T>() / Self::texture_width::<T>()
-    }
-}
-
-struct GpuDataTexture<L> {
-    id: TextureId,
-    layout: PhantomData<L>,
-}
-
-impl<L: GpuStoreLayout> GpuDataTexture<L> {
-    fn new(device: &mut Device) -> GpuDataTexture<L> {
-        let id = device.create_texture_ids(1, TextureTarget::Default)[0];
-
-        GpuDataTexture {
-            id,
-            layout: PhantomData,
-        }
-    }
-
-    fn init<T: Default>(&mut self,
-                        device: &mut Device,
-                        data: &mut Vec<T>) {
+    fn update<T>(&mut self,
+                 device: &mut Device,
+                 data: &mut Vec<T>) {
         if data.is_empty() {
             return;
         }
 
-        let items_per_row = L::items_per_row::<T>();
-        let rows_per_item = L::rows_per_item::<T>();
+        debug_assert!(mem::size_of::<T>() % 16 == 0);
+        let texels_per_item = mem::size_of::<T>() / 16;
+        let items_per_row = MAX_VERTEX_TEXTURE_WIDTH / texels_per_item;
 
         // Extend the data array to be a multiple of the row size.
         // This ensures memory safety when the array is passed to
         // OpenGL to upload to the GPU.
         if items_per_row != 0 {
             while data.len() % items_per_row != 0 {
-                data.push(T::default());
+                data.push(unsafe { mem::uninitialized() });
             }
         }
 
-        let height = if items_per_row != 0 {
-            data.len() / items_per_row
-        } else {
-            data.len() * rows_per_item
-        };
+        let width = (MAX_VERTEX_TEXTURE_WIDTH - (MAX_VERTEX_TEXTURE_WIDTH % texels_per_item)) as u32;
+        let needed_height = (data.len() / items_per_row) as u32;
+
+        // Determine if the texture needs to be resized.
+        let texture_size = self.texture.get_dimensions();
+
+        if needed_height > texture_size.height {
+            let new_height = (needed_height + 127) & !127;
+
+            device.init_texture(&mut self.texture,
+                                width,
+                                new_height,
+                                ImageFormat::RGBAF32,
+                                TextureFilter::Nearest,
+                                RenderTargetMode::None,
+                                1,
+                                None);
+        }
 
-        device.init_texture(self.id,
-                            L::texture_width::<T>() as u32,
-                            height as u32,
-                            L::image_format(),
-                            L::texture_filter(),
-                            RenderTargetMode::None,
-                            1,
-                            Some(unsafe { mem::transmute(data.as_slice()) } ));
+        // Bind a PBO to do the texture upload.
+        // Updating the texture via PBO avoids CPU-side driver stalls.
+        device.bind_pbo(Some(self.pbo));
+        device.update_pbo_data(data);
+        device.update_texture_from_pbo(&self.texture,
+                                       0,
+                                       0,
+                                       width,
+                                       needed_height,
+                                       0,
+                                       None,
+                                       0);
+
+        // Ensure that other texture updates won't read from this PBO.
+        device.bind_pbo(None);
+    }
+
+    fn deinit(self, device: &mut Device) {
+        device.delete_texture(self.texture);
     }
 }
 
-pub struct VertexDataTextureLayout {}
-
-impl GpuStoreLayout for VertexDataTextureLayout {
-    fn image_format() -> ImageFormat {
-        ImageFormat::RGBAF32
-    }
-
-    fn texture_width<T>() -> usize {
-        MAX_VERTEX_TEXTURE_WIDTH - (MAX_VERTEX_TEXTURE_WIDTH % Self::texels_per_item::<T>())
-    }
-
-    fn texture_filter() -> TextureFilter {
-        TextureFilter::Nearest
-    }
-}
-
-type VertexDataTexture = GpuDataTexture<VertexDataTextureLayout>;
-
 const TRANSFORM_FEATURE: &str = "TRANSFORM";
 const SUBPIXEL_AA_FEATURE: &str = "SUBPIXEL_AA";
 const CLIP_FEATURE: &str = "CLIP";
 
 enum ShaderKind {
     Primitive,
     Cache(VertexFormat),
     ClipCache,
@@ -763,51 +834,56 @@ fn create_prim_shader(name: &'static str
 
     debug!("PrimShader {}", name);
 
     let vertex_descriptor = match vertex_format {
         VertexFormat::PrimitiveInstances => DESC_PRIM_INSTANCES,
         VertexFormat::Blur => DESC_BLUR,
     };
 
-    device.create_program(name,
-                          &prefix,
-                          &vertex_descriptor)
+    let program = device.create_program(name,
+                                        &prefix,
+                                        &vertex_descriptor);
+
+    if let Ok(ref program) = program {
+        device.bind_shader_samplers(program, &[
+            ("sColor0", TextureSampler::Color0),
+            ("sColor1", TextureSampler::Color1),
+            ("sColor2", TextureSampler::Color2),
+            ("sDither", TextureSampler::Dither),
+            ("sCacheA8", TextureSampler::CacheA8),
+            ("sCacheRGBA8", TextureSampler::CacheRGBA8),
+            ("sLayers", TextureSampler::Layers),
+            ("sRenderTasks", TextureSampler::RenderTasks),
+            ("sResourceCache", TextureSampler::ResourceCache),
+        ]);
+    }
+
+    program
 }
 
 fn create_clip_shader(name: &'static str, device: &mut Device) -> Result<Program, ShaderError> {
     let prefix = format!("#define WR_MAX_VERTEX_TEXTURE_WIDTH {}\n
                           #define WR_FEATURE_TRANSFORM\n",
                           MAX_VERTEX_TEXTURE_WIDTH);
 
     debug!("ClipShader {}", name);
 
-    device.create_program(name, &prefix, &DESC_CLIP)
-}
+    let program = device.create_program(name, &prefix, &DESC_CLIP);
 
-struct GpuDataTextures {
-    layer_texture: VertexDataTexture,
-    render_task_texture: VertexDataTexture,
-}
-
-impl GpuDataTextures {
-    fn new(device: &mut Device) -> GpuDataTextures {
-        GpuDataTextures {
-            layer_texture: VertexDataTexture::new(device),
-            render_task_texture: VertexDataTexture::new(device),
-        }
+    if let Ok(ref program) = program {
+        device.bind_shader_samplers(program, &[
+            ("sColor0", TextureSampler::Color0),
+            ("sLayers", TextureSampler::Layers),
+            ("sRenderTasks", TextureSampler::RenderTasks),
+            ("sResourceCache", TextureSampler::ResourceCache),
+        ]);
     }
 
-    fn init_frame(&mut self, device: &mut Device, frame: &mut Frame) {
-        self.layer_texture.init(device, &mut frame.layer_texture_data);
-        self.render_task_texture.init(device, &mut frame.render_tasks.task_data);
-
-        device.bind_texture(TextureSampler::Layers, self.layer_texture.id);
-        device.bind_texture(TextureSampler::RenderTasks, self.render_task_texture.id);
-    }
+    program
 }
 
 #[derive(Clone, Debug, PartialEq)]
 pub enum ReadPixelsFormat {
     Rgba8,
     Bgra8,
 }
 
@@ -875,38 +951,38 @@ pub struct Renderer {
     debug: DebugRenderer,
     debug_flags: DebugFlags,
     enable_batcher: bool,
     backend_profile_counters: BackendProfileCounters,
     profile_counters: RendererProfileCounters,
     profiler: Profiler,
     last_time: u64,
 
-    color_render_targets: Vec<TextureId>,
-    alpha_render_targets: Vec<TextureId>,
+    color_render_targets: Vec<Texture>,
+    alpha_render_targets: Vec<Texture>,
 
     gpu_profile: GpuProfiler<GpuProfileTag>,
     prim_vao: VAO,
     blur_vao: VAO,
     clip_vao: VAO,
+    box_shadow_vao: VAO,
 
-    gdt_index: usize,
-    gpu_data_textures: [GpuDataTextures; GPU_DATA_TEXTURE_POOL],
-
+    layer_texture: VertexDataTexture,
+    render_task_texture: VertexDataTexture,
     gpu_cache_texture: CacheTexture,
 
     pipeline_epoch_map: FastHashMap<PipelineId, Epoch>,
 
     // Manages and resolves source textures IDs to real texture IDs.
     texture_resolver: SourceTextureResolver,
 
     // A PBO used to do asynchronous texture cache uploads.
     texture_cache_upload_pbo: PBOId,
 
-    dither_matrix_texture_id: Option<TextureId>,
+    dither_matrix_texture: Option<Texture>,
 
     /// Optional trait object that allows the client
     /// application to provide external buffers for image data.
     external_image_handler: Option<Box<ExternalImageHandler>>,
 
     /// List of profile results from previous frames. Can be retrieved
     /// via get_frame_profiles().
     cpu_profiles: VecDeque<CpuProfile>,
@@ -1239,53 +1315,45 @@ impl Renderer {
                                      options.precache_shaders)
         };
 
         let texture_cache = TextureCache::new(max_device_size);
         let max_texture_size = texture_cache.max_texture_size();
 
         let backend_profile_counters = BackendProfileCounters::new();
 
-        let dither_matrix_texture_id = if options.enable_dithering {
+        let dither_matrix_texture = if options.enable_dithering {
             let dither_matrix: [u8; 64] = [
                 00, 48, 12, 60, 03, 51, 15, 63,
                 32, 16, 44, 28, 35, 19, 47, 31,
                 08, 56, 04, 52, 11, 59, 07, 55,
                 40, 24, 36, 20, 43, 27, 39, 23,
                 02, 50, 14, 62, 01, 49, 13, 61,
                 34, 18, 46, 30, 33, 17, 45, 29,
                 10, 58, 06, 54, 09, 57, 05, 53,
                 42, 26, 38, 22, 41, 25, 37, 21
             ];
 
-            let id = device.create_texture_ids(1, TextureTarget::Default)[0];
-            device.init_texture(id,
+            let mut texture = device.create_texture(TextureTarget::Default);
+            device.init_texture(&mut texture,
                                 8,
                                 8,
                                 ImageFormat::A8,
                                 TextureFilter::Nearest,
                                 RenderTargetMode::None,
                                 1,
                                 Some(&dither_matrix));
 
-            Some(id)
+            Some(texture)
         } else {
             None
         };
 
         let debug_renderer = DebugRenderer::new(&mut device);
 
-        let gpu_data_textures = [
-            GpuDataTextures::new(&mut device),
-            GpuDataTextures::new(&mut device),
-            GpuDataTextures::new(&mut device),
-            GpuDataTextures::new(&mut device),
-            GpuDataTextures::new(&mut device),
-        ];
-
         let x0 = 0.0;
         let y0 = 0.0;
         let x1 = 1.0;
         let y1 = 1.0;
 
         let quad_indices: [u16; 6] = [ 0, 1, 2, 2, 1, 3 ];
         let quad_vertices = [
             PackedVertex {
@@ -1297,37 +1365,36 @@ impl Renderer {
             PackedVertex {
                 pos: [x0, y1],
             },
             PackedVertex {
                 pos: [x1, y1],
             },
         ];
 
-        let prim_vao = device.create_vao(&DESC_PRIM_INSTANCES,
-                                         mem::size_of::<PrimitiveInstance>() as i32);
+        let prim_vao = device.create_vao(&DESC_PRIM_INSTANCES);
         device.bind_vao(&prim_vao);
         device.update_vao_indices(&prim_vao,
                                   &quad_indices,
                                   VertexUsageHint::Static);
         device.update_vao_main_vertices(&prim_vao,
                                         &quad_vertices,
                                         VertexUsageHint::Static);
 
-        let blur_vao = device.create_vao_with_new_instances(&DESC_BLUR,
-                                                            mem::size_of::<BlurCommand>() as i32,
-                                                            &prim_vao);
-        let clip_vao = device.create_vao_with_new_instances(&DESC_CLIP,
-                                                            mem::size_of::<CacheClipInstance>() as i32,
-                                                            &prim_vao);
+        let blur_vao = device.create_vao_with_new_instances(&DESC_BLUR, &prim_vao);
+        let clip_vao = device.create_vao_with_new_instances(&DESC_CLIP, &prim_vao);
+        let box_shadow_vao = device.create_vao_with_new_instances(&DESC_CACHE_BOX_SHADOW, &prim_vao);
 
         let texture_cache_upload_pbo = device.create_pbo();
 
         let texture_resolver = SourceTextureResolver::new(&mut device);
 
+        let layer_texture = VertexDataTexture::new(&mut device);
+        let render_task_texture = VertexDataTexture::new(&mut device);
+
         device.end_frame();
 
         let backend_notifier = Arc::clone(&notifier);
 
         let default_font_render_mode = match (options.enable_aa, options.enable_subpixel_aa) {
             (true, true) => FontRenderMode::Subpixel,
             (true, false) => FontRenderMode::Alpha,
             (false, _) => FontRenderMode::Mono,
@@ -1418,21 +1485,22 @@ impl Renderer {
             clear_color: options.clear_color,
             enable_clear_scissor: options.enable_clear_scissor,
             last_time: 0,
             color_render_targets: Vec::new(),
             alpha_render_targets: Vec::new(),
             gpu_profile,
             prim_vao,
             blur_vao,
+            box_shadow_vao,
             clip_vao,
-            gdt_index: 0,
-            gpu_data_textures,
+            layer_texture,
+            render_task_texture,
             pipeline_epoch_map: FastHashMap::default(),
-            dither_matrix_texture_id,
+            dither_matrix_texture,
             external_image_handler: None,
             cpu_profiles: VecDeque::new(),
             gpu_profiles: VecDeque::new(),
             gpu_cache_texture,
             texture_cache_upload_pbo,
             texture_resolver,
         };
 
@@ -1531,57 +1599,69 @@ impl Renderer {
         // Avoid unused param warning.
         let _ = &self.debug_server;
     }
 
     #[cfg(feature = "debugger")]
     fn update_debug_server(&self) {
         while let Ok(msg) = self.debug_server.debug_rx.try_recv() {
             match msg {
-                DebugMsg::FetchBatches(sender) => {
-                    let mut batch_list = BatchList::new();
+                DebugMsg::FetchPasses(sender) => {
+                    let mut debug_passes = debug_server::PassList::new();
 
                     if let Some(frame) = self.current_frame.as_ref().and_then(|frame| frame.frame.as_ref()) {
                         for pass in &frame.passes {
+                            let mut debug_pass = debug_server::Pass::new();
+
                             for target in &pass.alpha_targets.targets {
-                                batch_list.push("[Clip] Clear", target.clip_batcher.border_clears.len());
-                                batch_list.push("[Clip] Borders", target.clip_batcher.borders.len());
-                                batch_list.push("[Clip] Rectangles", target.clip_batcher.rectangles.len());
+                                let mut debug_target = debug_server::Target::new("A8");
+
+                                debug_target.add(debug_server::BatchKind::Clip, "Clear", target.clip_batcher.border_clears.len());
+                                debug_target.add(debug_server::BatchKind::Clip, "Borders", target.clip_batcher.borders.len());
+                                debug_target.add(debug_server::BatchKind::Clip, "Rectangles", target.clip_batcher.rectangles.len());
                                 for (_, items) in target.clip_batcher.images.iter() {
-                                    batch_list.push("[Clip] Image mask", items.len());
+                                    debug_target.add(debug_server::BatchKind::Clip, "Image mask", items.len());
                                 }
+
+                                debug_pass.add(debug_target);
                             }
 
                             for target in &pass.color_targets.targets {
-                                batch_list.push("[Cache] Vertical Blur", target.vertical_blurs.len());
-                                batch_list.push("[Cache] Horizontal Blur", target.horizontal_blurs.len());
-                                batch_list.push("[Cache] Box Shadow", target.box_shadow_cache_prims.len());
-                                batch_list.push("[Cache] Text Shadow", target.text_run_cache_prims.len());
-                                batch_list.push("[Cache] Lines", target.line_cache_prims.len());
+                                let mut debug_target = debug_server::Target::new("RGBA8");
+
+                                debug_target.add(debug_server::BatchKind::Cache, "Vertical Blur", target.vertical_blurs.len());
+                                debug_target.add(debug_server::BatchKind::Cache, "Horizontal Blur", target.horizontal_blurs.len());
+                                debug_target.add(debug_server::BatchKind::Cache, "Box Shadow", target.box_shadow_cache_prims.len());
+                                debug_target.add(debug_server::BatchKind::Cache, "Text Shadow", target.text_run_cache_prims.len());
+                                debug_target.add(debug_server::BatchKind::Cache, "Lines", target.line_cache_prims.len());
 
                                 for batch in target.alpha_batcher
                                                    .batch_list
                                                    .opaque_batch_list
                                                    .batches
                                                    .iter()
                                                    .rev() {
-                                    batch_list.push(batch.key.kind.debug_name(), batch.instances.len());
+                                    debug_target.add(debug_server::BatchKind::Opaque, batch.key.kind.debug_name(), batch.instances.len());
                                 }
 
                                 for batch in &target.alpha_batcher
                                                     .batch_list
                                                     .alpha_batch_list
                                                     .batches {
-                                    batch_list.push(batch.key.kind.debug_name(), batch.instances.len());
+                                    debug_target.add(debug_server::BatchKind::Alpha, batch.key.kind.debug_name(), batch.instances.len());
                                 }
+
+                                debug_pass.add(debug_target);
                             }
+
+                            debug_passes.add(debug_pass);
                         }
                     }
 
-                    let json = serde_json::to_string(&batch_list).unwrap();
+                    let json = serde_json::to_string(&debug_passes).unwrap();
                     sender.send(json).ok();
                 }
             }
         }
     }
 
     fn handle_debug_command(&mut self, command: DebugCommand) {
         match command {
@@ -1663,17 +1743,17 @@ impl Renderer {
                         self.device.disable_depth();
                         self.device.set_blend(false);
                         //self.update_shaders();
 
                         self.update_texture_cache();
 
                         self.update_gpu_cache(frame);
 
-                        self.device.bind_texture(TextureSampler::ResourceCache, self.gpu_cache_texture.texture_id);
+                        self.device.bind_texture(TextureSampler::ResourceCache, &self.gpu_cache_texture.texture);
 
                         frame_id
                     };
 
                     self.draw_tile_frame(frame, &framebuffer_size);
 
                     self.gpu_profile.end_frame();
                     cpu_frame_id
@@ -1745,38 +1825,37 @@ impl Renderer {
         let _gm = GpuMarker::new(self.device.rc_gl(), "texture cache update");
         let mut pending_texture_updates = mem::replace(&mut self.pending_texture_updates, vec![]);
 
         for update_list in pending_texture_updates.drain(..) {
             for update in update_list.updates {
                 match update.op {
                     TextureUpdateOp::Create { width, height, layer_count, format, filter, mode } => {
                         let CacheTextureId(cache_texture_index) = update.id;
-                        if self.texture_resolver.cache_texture_id_map.len() == cache_texture_index {
+                        if self.texture_resolver.cache_texture_map.len() == cache_texture_index {
                             // Create a new native texture, as requested by the texture cache.
-                            let texture_id = self.device
-                                                 .create_texture_ids(1, TextureTarget::Array)[0];
-                            self.texture_resolver.cache_texture_id_map.push(texture_id);
+                            let texture = self.device.create_texture(TextureTarget::Array);
+                            self.texture_resolver.cache_texture_map.push(texture);
                         }
-                        let texture_id = self.texture_resolver.cache_texture_id_map[cache_texture_index];
+                        let texture = &mut self.texture_resolver.cache_texture_map[cache_texture_index];
 
                         // Ensure no PBO is bound when creating the texture storage,
                         // or GL will attempt to read data from there.
                         self.device.bind_pbo(None);
-                        self.device.init_texture(texture_id,
+                        self.device.init_texture(texture,
                                                  width,
                                                  height,
                                                  format,
                                                  filter,
                                                  mode,
                                                  layer_count,
                                                  None);
                     }
                     TextureUpdateOp::Update { rect, source, stride, layer_index, offset } => {
-                        let texture_id = self.texture_resolver.cache_texture_id_map[update.id.0];
+                        let texture = &self.texture_resolver.cache_texture_map[update.id.0];
 
                         // Bind a PBO to do the texture upload.
                         // Updating the texture via PBO avoids CPU-side driver stalls.
                         self.device.bind_pbo(Some(self.texture_cache_upload_pbo));
 
                         match source {
                             TextureUpdateSource::Bytes { data }  => {
                                 self.device.update_pbo_data(&data[offset as usize..]);
@@ -1790,55 +1869,55 @@ impl Renderer {
                                         self.device.update_pbo_data(&data[offset as usize..]);
                                     }
                                     _ => panic!("No external buffer found"),
                                 };
                                 handler.unlock(id, channel_index);
                             }
                         }
 
-                        self.device.update_texture_from_pbo(texture_id,
+                        self.device.update_texture_from_pbo(texture,
                                                             rect.origin.x,
                                                             rect.origin.y,
                                                             rect.size.width,
                                                             rect.size.height,
                                                             layer_index,
                                                             stride,
                                                             0);
                     }
                     TextureUpdateOp::Free => {
-                        let texture_id = self.texture_resolver.cache_texture_id_map[update.id.0];
-                        self.device.deinit_texture(texture_id);
+                        let texture = &mut self.texture_resolver.cache_texture_map[update.id.0];
+                        self.device.free_texture_storage(texture);
                     }
                 }
             }
         }
 
         // Ensure that other texture updates won't read from this PBO.
         self.device.bind_pbo(None);
     }
 
     fn draw_instanced_batch<T>(&mut self,
                                data: &[T],
                                vertex_array_kind: VertexArrayKind,
                                textures: &BatchTextures) {
         for i in 0..textures.colors.len() {
-            let texture_id = self.texture_resolver.resolve(&textures.colors[i]);
-            self.device.bind_texture(TextureSampler::color(i), texture_id);
+            self.texture_resolver.bind(&textures.colors[i], TextureSampler::color(i), &mut self.device);
         }
 
         // TODO: this probably isn't the best place for this.
-        if let Some(id) = self.dither_matrix_texture_id {
-            self.device.bind_texture(TextureSampler::Dither, id);
+        if let Some(ref texture) = self.dither_matrix_texture {
+            self.device.bind_texture(TextureSampler::Dither, texture);
         }
 
         let vao = match vertex_array_kind {
             VertexArrayKind::Primitive => &self.prim_vao,
             VertexArrayKind::Clip => &self.clip_vao,
             VertexArrayKind::Blur => &self.blur_vao,
+            VertexArrayKind::CacheBoxShadow => &self.box_shadow_vao,
         };
 
         self.device.bind_vao(vao);
 
         if self.enable_batcher {
             self.device.update_vao_instances(vao, data, VertexUsageHint::Stream);
             self.device.draw_indexed_triangles_instanced_u16(6, data.len() as i32);
             self.profile_counters.draw_calls.inc();
@@ -1853,17 +1932,17 @@ impl Renderer {
         self.profile_counters.vertices.add(6 * data.len());
     }
 
     fn submit_batch(&mut self,
                     key: &AlphaBatchKey,
                     instances: &[PrimitiveInstance],
                     projection: &Transform3D<f32>,
                     render_tasks: &RenderTaskTree,
-                    render_target: Option<(TextureId, i32)>,
+                    render_target: Option<(&Texture, i32)>,
                     target_dimensions: DeviceUintSize) {
         let transform_kind = key.flags.transform_kind();
         let needs_clipping = key.flags.needs_clipping();
         debug_assert!(!needs_clipping ||
                       match key.blend_mode {
                           BlendMode::Alpha |
                           BlendMode::PremultipliedAlpha |
                           BlendMode::Subpixel(..) => true,
@@ -1960,22 +2039,22 @@ impl Renderer {
         };
 
         // Handle special case readback for composites.
         match key.kind {
             AlphaBatchKind::Composite { task_id, source_id, backdrop_id } => {
                 // composites can't be grouped together because
                 // they may overlap and affect each other.
                 debug_assert!(instances.len() == 1);
-                let cache_texture = self.texture_resolver.resolve(&SourceTexture::CacheRGBA8);
+                let cache_texture = self.texture_resolver.resolve(&SourceTexture::CacheRGBA8).unwrap();
 
                 // Before submitting the composite batch, do the
                 // framebuffer readbacks that are needed for each
                 // composite operation in this batch.
-                let cache_texture_dimensions = self.device.get_texture_dimensions(cache_texture);
+                let cache_texture_dimensions = cache_texture.get_dimensions();
 
                 let source = render_tasks.get(source_id);
                 let backdrop = render_tasks.get(task_id);
                 let readback = render_tasks.get(backdrop_id);
 
                 let (readback_rect, readback_layer) = readback.get_target_rect();
                 let (backdrop_rect, _) = backdrop.get_target_rect();
                 let backdrop_screen_origin = backdrop.as_alpha_batch().screen_origin;
@@ -2022,17 +2101,17 @@ impl Renderer {
 
         let _gm = self.gpu_profile.add_marker(marker);
         self.draw_instanced_batch(instances,
                                   VertexArrayKind::Primitive,
                                   &key.textures);
     }
 
     fn draw_color_target(&mut self,
-        render_target: Option<(TextureId, i32)>,
+        render_target: Option<(&Texture, i32)>,
         target: &ColorRenderTarget,
         target_size: DeviceUintSize,
         clear_color: Option<[f32; 4]>,
         render_tasks: &RenderTaskTree,
         projection: &Transform3D<f32>,
     ) {
         {
             let _gm = self.gpu_profile.add_marker(GPU_TAG_SETUP_TARGET);
@@ -2086,17 +2165,17 @@ impl Renderer {
         }
 
         // Draw any box-shadow caches for this target.
         if !target.box_shadow_cache_prims.is_empty() {
             self.device.set_blend(false);
             let _gm = self.gpu_profile.add_marker(GPU_TAG_CACHE_BOX_SHADOW);
             self.cs_box_shadow.bind(&mut self.device, projection);
             self.draw_instanced_batch(&target.box_shadow_cache_prims,
-                                      VertexArrayKind::Primitive,
+                                      VertexArrayKind::CacheBoxShadow,
                                       &BatchTextures::no_texture());
         }
 
         // Draw any textrun caches for this target. For now, this
         // is only used to cache text runs that are to be blurred
         // for text-shadow support. In the future it may be worth
         // considering using this for (some) other text runs, since
         // it removes the overhead of submitting many small glyphs
@@ -2189,17 +2268,17 @@ impl Renderer {
 
             self.device.disable_depth();
             self.device.set_blend(false);
             self.gpu_profile.done_sampler();
         }
     }
 
     fn draw_alpha_target(&mut self,
-        render_target: (TextureId, i32),
+        render_target: (&Texture, i32),
         target: &AlphaRenderTarget,
         target_size: DeviceUintSize,
         projection: &Transform3D<f32>,
     ) {
         self.gpu_profile.add_sampler(GPU_SAMPLER_TAG_ALPHA);
 
         {
             let _gm = self.gpu_profile.add_marker(GPU_TAG_SETUP_TARGET);
@@ -2307,24 +2386,24 @@ impl Renderer {
                             ext_image.image_type);
                     }
                 };
 
                 // In order to produce the handle, the external image handler may call into
                 // the GL context and change some states.
                 self.device.reset_state();
 
-                let texture_id = match image.source {
-                    ExternalImageSource::NativeTexture(texture_id) => TextureId::new(texture_id, texture_target),
+                let texture = match image.source {
+                    ExternalImageSource::NativeTexture(texture_id) => ExternalTexture::new(texture_id, texture_target),
                     _ => panic!("No native texture found."),
                 };
 
                 self.texture_resolver
                     .external_images
-                    .insert((ext_image.id, ext_image.channel_index), texture_id);
+                    .insert((ext_image.id, ext_image.channel_index), texture);
 
                 let update = GpuCacheUpdate::Copy {
                     block_index: 0,
                     block_count: 1,
                     address: deferred_resolve.address,
                 };
 
                 let blocks = [ [image.u0, image.v0, image.u1, image.v1].into(), [0.0; 4].into() ];
@@ -2345,71 +2424,72 @@ impl Renderer {
         }
     }
 
     fn start_frame(&mut self, frame: &mut Frame) {
         let _gm = self.gpu_profile.add_marker(GPU_TAG_SETUP_DATA);
 
         // Assign render targets to the passes.
         for pass in &mut frame.passes {
-            debug_assert!(pass.color_texture_id.is_none());
-            debug_assert!(pass.alpha_texture_id.is_none());
+            debug_assert!(pass.color_texture.is_none());
+            debug_assert!(pass.alpha_texture.is_none());
 
             if pass.needs_render_target_kind(RenderTargetKind::Color) {
-                pass.color_texture_id = Some(self.color_render_targets
-                                                 .pop()
-                                                 .unwrap_or_else(|| {
-                                                     self.device
-                                                         .create_texture_ids(1, TextureTarget::Array)[0]
-                                                  }));
+                pass.color_texture = Some(self.color_render_targets
+                                              .pop()
+                                              .unwrap_or_else(|| {
+                                                  self.device
+                                                      .create_texture(TextureTarget::Array)
+                                               }));
             }
 
             if pass.needs_render_target_kind(RenderTargetKind::Alpha) {
-                pass.alpha_texture_id = Some(self.alpha_render_targets
-                                                 .pop()
-                                                 .unwrap_or_else(|| {
-                                                     self.device
-                                                         .create_texture_ids(1, TextureTarget::Array)[0]
-                                                  }));
+                pass.alpha_texture = Some(self.alpha_render_targets
+                                              .pop()
+                                              .unwrap_or_else(|| {
+                                                  self.device
+                                                      .create_texture(TextureTarget::Array)
+                                               }));
             }
         }
 
 
         // Init textures and render targets to match this scene.
-        for pass in &frame.passes {
-            if let Some(texture_id) = pass.color_texture_id {
-                let target_count = pass.required_target_count(RenderTargetKind::Color);
-                self.device.init_texture(texture_id,
+        for pass in &mut frame.passes {
+            let color_target_count = pass.required_target_count(RenderTargetKind::Color);
+            let alpha_target_count = pass.required_target_count(RenderTargetKind::Alpha);
+
+            if let Some(texture) = pass.color_texture.as_mut() {
+                self.device.init_texture(texture,
                                          frame.cache_size.width as u32,
                                          frame.cache_size.height as u32,
                                          ImageFormat::BGRA8,
                                          TextureFilter::Linear,
                                          RenderTargetMode::RenderTarget,
-                                         target_count as i32,
+                                         color_target_count as i32,
                                          None);
             }
-            if let Some(texture_id) = pass.alpha_texture_id {
-                let target_count = pass.required_target_count(RenderTargetKind::Alpha);
-                self.device.init_texture(texture_id,
+            if let Some(texture) = pass.alpha_texture.as_mut() {
+                self.device.init_texture(texture,
                                          frame.cache_size.width as u32,
                                          frame.cache_size.height as u32,
                                          ImageFormat::A8,
                                          TextureFilter::Nearest,
                                          RenderTargetMode::RenderTarget,
-                                         target_count as i32,
+                                         alpha_target_count as i32,
                                          None);
             }
         }
 
-        // TODO(gw): This is a hack / workaround for #728.
-        // We should find a better way to implement these updates rather
-        // than wasting this extra memory, but for now it removes a large
-        // number of driver stalls.
-        self.gpu_data_textures[self.gdt_index].init_frame(&mut self.device, frame);
-        self.gdt_index = (self.gdt_index + 1) % GPU_DATA_TEXTURE_POOL;
+        self.layer_texture.update(&mut self.device, &mut frame.layer_texture_data);
+        self.render_task_texture.update(&mut self.device, &mut frame.render_tasks.task_data);
+
+        self.device.bind_texture(TextureSampler::Layers, &self.layer_texture.texture);
+        self.device.bind_texture(TextureSampler::RenderTasks, &self.render_task_texture.texture);
+
         self.texture_resolver.set_cache_textures(None, None);
     }
 
     fn draw_tile_frame(&mut self,
                        frame: &mut Frame,
                        framebuffer_size: &DeviceUintSize) {
         let _gm = GpuMarker::new(self.device.rc_gl(), "tile frame draw");
 
@@ -2454,50 +2534,58 @@ impl Renderer {
                     projection = Transform3D::ortho(0.0,
                                                  size.width as f32,
                                                  0.0,
                                                  size.height as f32,
                                                  ORTHO_NEAR_PLANE,
                                                  ORTHO_FAR_PLANE);
                 }
 
-                let cache_a8_texture = self.texture_resolver.resolve(&SourceTexture::CacheA8);
-                let cache_rgba8_texture = self.texture_resolver.resolve(&SourceTexture::CacheRGBA8);
-                self.device.bind_texture(TextureSampler::CacheA8, cache_a8_texture);
-                self.device.bind_texture(TextureSampler::CacheRGBA8, cache_rgba8_texture);
+                self.texture_resolver.bind(&SourceTexture::CacheA8, TextureSampler::CacheA8, &mut self.device);
+                self.texture_resolver.bind(&SourceTexture::CacheRGBA8, TextureSampler::CacheRGBA8, &mut self.device);
 
                 for (target_index, target) in pass.alpha_targets.targets.iter().enumerate() {
-                    self.draw_alpha_target((pass.alpha_texture_id.unwrap(), target_index as i32),
+                    self.draw_alpha_target((pass.alpha_texture.as_ref().unwrap(), target_index as i32),
                                            target,
                                            *size,
                                            &projection);
                 }
 
                 for (target_index, target) in pass.color_targets.targets.iter().enumerate() {
-                    let render_target = pass.color_texture_id.map(|texture_id| {
-                        (texture_id, target_index as i32)
+                    let render_target = pass.color_texture.as_ref().map(|texture| {
+                        (texture, target_index as i32)
                     });
                     self.draw_color_target(render_target,
                                            target,
                                            *size,
                                            clear_color,
                                            &frame.render_tasks,
                                            &projection);
 
                 }
 
-                self.texture_resolver.set_cache_textures(pass.alpha_texture_id, pass.color_texture_id);
+                // Return the texture IDs to the pool for next frame.
+                if let Some(texture) = self.texture_resolver.cache_rgba8_texture.take() {
+                    self.color_render_targets.push(texture);
+                }
+                if let Some(texture) = self.texture_resolver.cache_a8_texture.take() {
+                    self.alpha_render_targets.push(texture);
+                }
 
-                // Return the texture IDs to the pool for next frame.
-                if let Some(texture_id) = pass.color_texture_id.take() {
-                    self.color_render_targets.push(texture_id);
-                }
-                if let Some(texture_id) = pass.alpha_texture_id.take() {
-                    self.alpha_render_targets.push(texture_id);
-                }
+                self.texture_resolver.set_cache_textures(pass.alpha_texture.take(),
+                                                         pass.color_texture.take());
+
+            }
+
+            // Return the texture IDs to the pool for next frame.
+            if let Some(texture) = self.texture_resolver.cache_rgba8_texture.take() {
+                self.color_render_targets.push(texture);
+            }
+            if let Some(texture) = self.texture_resolver.cache_a8_texture.take() {
+                self.alpha_render_targets.push(texture);
             }
 
             self.color_render_targets.reverse();
             self.alpha_render_targets.reverse();
             self.draw_render_target_debug(framebuffer_size);
             self.draw_texture_cache_debug(framebuffer_size);
         }
 
@@ -2532,69 +2620,69 @@ impl Renderer {
         let num_textures = self.color_render_targets.iter().chain(self.alpha_render_targets.iter()).count() as i32;
 
         if num_textures * (size + spacing) > fb_width {
             let factor = fb_width as f32 / (num_textures * (size + spacing)) as f32;
             size = (size as f32 * factor) as i32;
             spacing = (spacing as f32 * factor) as i32;
         }
 
-        for (i, texture_id) in self.color_render_targets.iter().chain(self.alpha_render_targets.iter()).enumerate() {
-            let layer_count = self.device.get_render_target_layer_count(*texture_id);
+        for (i, texture) in self.color_render_targets.iter().chain(self.alpha_render_targets.iter()).enumerate() {
+            let layer_count = texture.get_render_target_layer_count();
             for layer_index in 0..layer_count {
                 let x = fb_width - (spacing + size) * (i as i32 + 1);
                 let y = spacing;
 
                 let dest_rect = rect(x, y, size, size);
                 self.device.blit_render_target(
-                    Some((*texture_id, layer_index as i32)),
+                    Some((texture, layer_index as i32)),
                     None,
                     dest_rect
                 );
             }
         }
     }
 
     fn draw_texture_cache_debug(&mut self, framebuffer_size: &DeviceUintSize) {
         if !self.debug_flags.contains(TEXTURE_CACHE_DBG) {
             return;
         }
 
         let mut spacing = 16;
         let mut size = 512;
         let fb_width = framebuffer_size.width as i32;
         let num_layers: i32 = self.texture_resolver
-                                  .cache_texture_id_map
+                                  .cache_texture_map
                                   .iter()
-                                  .map(|id| {
-                                      self.device.get_texture_layer_count(*id)
+                                  .map(|texture| {
+                                      texture.get_layer_count()
                                   })
                                   .sum();
 
         if num_layers * (size + spacing) > fb_width {
             let factor = fb_width as f32 / (num_layers * (size + spacing)) as f32;
             size = (size as f32 * factor) as i32;
             spacing = (spacing as f32 * factor) as i32;
         }
 
         let mut i = 0;
-        for texture_id in &self.texture_resolver.cache_texture_id_map {
+        for texture in &self.texture_resolver.cache_texture_map {
             let y = spacing + if self.debug_flags.contains(RENDER_TARGET_DBG) { 528 } else { 0 };
 
-            let layer_count = self.device.get_texture_layer_count(*texture_id);
+            let layer_count = texture.get_layer_count();
             for layer_index in 0..layer_count {
                 let x = fb_width - (spacing + size) * (i as i32 + 1);
 
                 // If we have more targets than fit on one row in screen, just early exit.
                 if x > fb_width {
                     return;
                 }
 
                 let dest_rect = rect(x, y, size, size);
-                self.device.blit_render_target(Some((*texture_id, layer_index)), None, dest_rect);
+                self.device.blit_render_target(Some((texture, layer_index)), None, dest_rect);
                 i += 1;
             }
         }
     }
 
     pub fn read_pixels_rgba8(&self, rect: DeviceUintRect) -> Vec<u8> {
         let mut pixels = vec![0u8; (4 * rect.size.width * rect.size.height) as usize];
         self.read_pixels_into(rect, ReadPixelsFormat::Rgba8, &mut pixels);
@@ -2619,20 +2707,33 @@ impl Renderer {
                                                  gl_type,
                                                  output);
     }
 
     // De-initialize the Renderer safely, assuming the GL is still alive and active.
     pub fn deinit(mut self) {
         //Note: this is a fake frame, only needed because texture deletion is require to happen inside a frame
         self.device.begin_frame(1.0);
+        self.gpu_cache_texture.deinit(&mut self.device);
+        if let Some(dither_matrix_texture) = self.dither_matrix_texture {
+            self.device.delete_texture(dither_matrix_texture);
+        }
+        self.layer_texture.deinit(&mut self.device);
+        self.render_task_texture.deinit(&mut self.device);
+        for texture in self.alpha_render_targets {
+            self.device.delete_texture(texture);
+        }
+        for texture in self.color_render_targets {
+            self.device.delete_texture(texture);
+        }
         self.texture_resolver.deinit(&mut self.device);
         self.device.delete_vao(self.prim_vao);
         self.device.delete_vao(self.clip_vao);
         self.device.delete_vao(self.blur_vao);
+        self.device.delete_vao(self.box_shadow_vao);
         self.debug.deinit(&mut self.device);
         self.cs_box_shadow.deinit(&mut self.device);
         self.cs_text_run.deinit(&mut self.device);
         self.cs_line.deinit(&mut self.device);
         self.cs_blur.deinit(&mut self.device);
         self.cs_clip_rectangle.deinit(&mut self.device);
         self.cs_clip_image.deinit(&mut self.device);
         self.cs_clip_border.deinit(&mut self.device);
--- a/gfx/webrender/src/tiling.rs
+++ b/gfx/webrender/src/tiling.rs
@@ -1,15 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use border::{BorderCornerInstance, BorderCornerSide};
-use device::TextureId;
+use device::Texture;
 use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuCacheUpdateList};
+use gpu_types::BoxShadowCacheInstance;
 use internal_types::BatchTextures;
 use internal_types::{FastHashMap, SourceTexture};
 use mask_cache::MaskCacheInfo;
 use prim_store::{CLIP_DATA_GPU_BLOCKS, DeferredResolve};
 use prim_store::{PrimitiveIndex, PrimitiveKind, PrimitiveMetadata, PrimitiveStore};
 use profiler::FrameProfileCounters;
 use render_task::{AlphaRenderItem, MaskGeometryKind, MaskSegment};
 use render_task::{RenderTaskAddress, RenderTaskId, RenderTaskKey, RenderTaskKind};
@@ -900,17 +901,17 @@ impl<T: RenderTarget> RenderTargetList<T
 
         (origin, RenderTargetIndex(self.targets.len() - 1))
     }
 }
 
 /// A render target represents a number of rendering operations on a surface.
 pub struct ColorRenderTarget {
     pub alpha_batcher: AlphaBatcher,
-    pub box_shadow_cache_prims: Vec<PrimitiveInstance>,
+    pub box_shadow_cache_prims: Vec<BoxShadowCacheInstance>,
     // List of text runs to be cached to this render target.
     // TODO(gw): For now, assume that these all come from
     //           the same source texture id. This is almost
     //           always true except for pathological test
     //           cases with more than 4k x 4k of unique
     //           glyphs visible. Once the future glyph / texture
     //           cache changes land, this restriction will
     //           be removed anyway.
@@ -992,22 +993,20 @@ impl RenderTarget for ColorRenderTarget 
             }
             RenderTaskKind::CachePrimitive(prim_index) => {
                 let prim_metadata = ctx.prim_store.get_metadata(prim_index);
 
                 let prim_address = prim_metadata.gpu_location.as_int(gpu_cache);
 
                 match prim_metadata.prim_kind {
                     PrimitiveKind::BoxShadow => {
-                        let instance = SimplePrimitiveInstance::new(prim_address,
-                                                                    render_tasks.get_task_address(task_id),
-                                                                    RenderTaskAddress(0),
-                                                                    PackedLayerIndex(0),
-                                                                    0);     // z is disabled for rendering cache primitives
-                        self.box_shadow_cache_prims.push(instance.build(0, 0, 0));
+                        self.box_shadow_cache_prims.push(BoxShadowCacheInstance {
+                            prim_address: gpu_cache.get_address(&prim_metadata.gpu_location),
+                            task_index: render_tasks.get_task_address(task_id),
+                        });
                     }
                     PrimitiveKind::TextShadow => {
                         let prim = &ctx.prim_store.cpu_text_shadows[prim_metadata.cpu_prim_index.0];
 
                         // todo(gw): avoid / recycle this allocation...
                         let mut instances = Vec::new();
 
                         let task_index = render_tasks.get_task_address(task_id);
@@ -1139,30 +1138,30 @@ impl RenderTarget for AlphaRenderTarget 
 ///
 /// A render pass can have several render targets if there wasn't enough space in one
 /// target to do all of the rendering for that pass.
 pub struct RenderPass {
     pub is_framebuffer: bool,
     tasks: Vec<RenderTaskId>,
     pub color_targets: RenderTargetList<ColorRenderTarget>,
     pub alpha_targets: RenderTargetList<AlphaRenderTarget>,
-    pub color_texture_id: Option<TextureId>,
-    pub alpha_texture_id: Option<TextureId>,
+    pub color_texture: Option<Texture>,
+    pub alpha_texture: Option<Texture>,
     dynamic_tasks: FastHashMap<RenderTaskKey, DynamicTaskInfo>,
 }
 
 impl RenderPass {
     pub fn new(is_framebuffer: bool, size: DeviceUintSize) -> RenderPass {
         RenderPass {
             is_framebuffer,
             color_targets: RenderTargetList::new(size, is_framebuffer),
             alpha_targets: RenderTargetList::new(size, false),
             tasks: vec![],
-            color_texture_id: None,
-            alpha_texture_id: None,
+            color_texture: None,
+            alpha_texture: None,
             dynamic_tasks: FastHashMap::default(),
         }
     }
 
     pub fn add_render_task(&mut self, task_id: RenderTaskId) {
         self.tasks.push(task_id);
     }
 
@@ -1170,17 +1169,16 @@ impl RenderPass {
         if self.is_framebuffer {
             false
         } else {
             self.required_target_count(kind) > 0
         }
     }
 
     pub fn required_target_count(&self, kind: RenderTargetKind) -> usize {
-        debug_assert!(!self.is_framebuffer);        // framebuffer never needs targets
         match kind {
             RenderTargetKind::Color => self.color_targets.target_count(),
             RenderTargetKind::Alpha => self.alpha_targets.target_count(),
         }
     }
 
     pub fn build(&mut self,
                  ctx: &RenderTargetContext,
@@ -1589,30 +1587,24 @@ impl ClipScrollGroup {
 #[derive(Debug, Clone)]
 #[repr(C)]
 pub struct PackedLayer {
     pub transform: LayerToWorldTransform,
     pub inv_transform: WorldToLayerTransform,
     pub local_clip_rect: LayerRect,
 }
 
-impl Default for PackedLayer {
-    fn default() -> PackedLayer {
+impl PackedLayer {
+    pub fn empty() -> PackedLayer {
         PackedLayer {
             transform: LayerToWorldTransform::identity(),
             inv_transform: WorldToLayerTransform::identity(),
             local_clip_rect: LayerRect::zero(),
         }
     }
-}
-
-impl PackedLayer {
-    pub fn empty() -> PackedLayer {
-        Default::default()
-    }
 
     pub fn set_transform(&mut self, transform: LayerToWorldTransform) -> bool {
         self.transform = transform;
         match self.transform.inverse() {
             Some(inv) => {
                 self.inv_transform = inv;
                 true
             }
--- a/gfx/webrender_api/src/api.rs
+++ b/gfx/webrender_api/src/api.rs
@@ -65,16 +65,24 @@ impl ResourceUpdates {
 
     pub fn add_native_font(&mut self, key: FontKey, native_handle: NativeFontHandle) {
         self.updates.push(ResourceUpdate::AddFont(AddFont::Native(key, native_handle)));
     }
 
     pub fn delete_font(&mut self, key: FontKey) {
         self.updates.push(ResourceUpdate::DeleteFont(key));
     }
+
+    pub fn merge(&mut self, mut other: ResourceUpdates) {
+        self.updates.append(&mut other.updates);
+    }
+
+    pub fn clear(&mut self) {
+        self.updates.clear()
+    }
 }
 
 #[derive(Clone, Deserialize, Serialize)]
 pub struct AddImage {
     pub key: ImageKey,
     pub descriptor: ImageDescriptor,
     pub data: ImageData,
     pub tiling: Option<TileSize>,