[go: nahoru, domu]

[Reland] Use glUnpremultiplyAndDitherCopyCHROMIUM in GPU Raster

This change addresses banding issues by using the newly added
glUnpremultiplyAndDitherCopyCHROMIUM.

In cases where we previously rasterized into a RGBA4444 target, we
instead rasterize into a temporary RGBA8888 target, then do an
unpremultiply/dither copy into the RGBA4444 target.

This means that tiles may be either unpremultiplied or premultiplied, so
we need a way to pipe this information to the GLRenderer, so it can
blend correctly. This change introduces a is_premultiplied property to
ContentDrawQuadBase to handle this.

Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel;master.tryserver.chromium.android:android_optional_gpu_tests_rel
Change-Id: I769a51e6a5f37dd5b9bede42577dbfade17e27e2
Reviewed-on: https://chromium-review.googlesource.com/937991
Reviewed-by: enne <enne@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
Commit-Queue: Eric Karl <ericrk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#543283}
diff --git a/cc/ipc/cc_param_traits_macros.h b/cc/ipc/cc_param_traits_macros.h
index 1812958..1141b23 100644
--- a/cc/ipc/cc_param_traits_macros.h
+++ b/cc/ipc/cc_param_traits_macros.h
@@ -117,6 +117,7 @@
   IPC_STRUCT_TRAITS_MEMBER(tex_coord_rect)
   IPC_STRUCT_TRAITS_MEMBER(texture_size)
   IPC_STRUCT_TRAITS_MEMBER(swizzle_contents)
+  IPC_STRUCT_TRAITS_MEMBER(is_premultiplied)
   IPC_STRUCT_TRAITS_MEMBER(nearest_neighbor)
   IPC_STRUCT_TRAITS_MEMBER(force_anti_aliasing_off)
 IPC_STRUCT_TRAITS_END()
diff --git a/cc/ipc/cc_param_traits_unittest.cc b/cc/ipc/cc_param_traits_unittest.cc
index 97e58901..867bb9c 100644
--- a/cc/ipc/cc_param_traits_unittest.cc
+++ b/cc/ipc/cc_param_traits_unittest.cc
@@ -405,10 +405,10 @@
   pass_cmp->CopyFromAndAppendDrawQuad(texture_in);
 
   TileDrawQuad* tile_in = pass_in->CreateAndAppendDrawQuad<TileDrawQuad>();
-  tile_in->SetAll(shared_state3_in, arbitrary_rect2,
-                  arbitrary_rect1_inside_rect2, arbitrary_bool1,
-                  arbitrary_resourceid3, arbitrary_rectf1, arbitrary_size1,
-                  arbitrary_bool2, arbitrary_bool3, arbitrary_bool4);
+  tile_in->SetAll(
+      shared_state3_in, arbitrary_rect2, arbitrary_rect1_inside_rect2,
+      arbitrary_bool1, arbitrary_resourceid3, arbitrary_rectf1, arbitrary_size1,
+      arbitrary_bool2, arbitrary_bool3, arbitrary_bool4, arbitrary_bool5);
   pass_cmp->CopyFromAndAppendDrawQuad(tile_in);
 
   YUVVideoDrawQuad* yuvvideo_in =
diff --git a/cc/ipc/cc_serialization_perftest.cc b/cc/ipc/cc_serialization_perftest.cc
index d8741b8..19baf09 100644
--- a/cc/ipc/cc_serialization_perftest.cc
+++ b/cc/ipc/cc_serialization_perftest.cc
@@ -361,10 +361,11 @@
           arbitrary_blend_mode2, arbitrary_context_id2);
       for (uint32_t j = 0; j < 6; ++j) {
         auto* tile_in = pass_in->CreateAndAppendDrawQuad<viz::TileDrawQuad>();
-        tile_in->SetAll(
-            shared_state2_in, arbitrary_rect2, arbitrary_rect1_inside_rect2,
-            arbitrary_bool1, arbitrary_resourceid3, arbitrary_rectf1,
-            arbitrary_size1, arbitrary_bool2, arbitrary_bool3, arbitrary_bool4);
+        tile_in->SetAll(shared_state2_in, arbitrary_rect2,
+                        arbitrary_rect1_inside_rect2, arbitrary_bool1,
+                        arbitrary_resourceid3, arbitrary_rectf1,
+                        arbitrary_size1, arbitrary_bool2, arbitrary_bool3,
+                        arbitrary_bool4, arbitrary_bool5);
       }
     }
 
diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc
index 02e162026..b395f9e 100644
--- a/cc/layers/picture_layer_impl.cc
+++ b/cc/layers/picture_layer_impl.cc
@@ -106,7 +106,8 @@
 // don't need any settings. The current approach uses 4 tiles to cover the
 // viewport vertically.
 gfx::Size CalculateGpuTileSize(const gfx::Size& base_tile_size,
-                               const gfx::Size& content_bounds) {
+                               const gfx::Size& content_bounds,
+                               const gfx::Size& max_tile_size) {
   int tile_width = base_tile_size.width();
 
   // Increase the height proportionally as the width decreases, and pad by our
@@ -130,6 +131,11 @@
 
   tile_height = std::max(tile_height, kMinHeightForGpuRasteredTile);
 
+  if (!max_tile_size.IsEmpty()) {
+    tile_width = std::min(tile_width, max_tile_size.width());
+    tile_height = std::min(tile_height, max_tile_size.height());
+  }
+
   return gfx::Size(tile_width, tile_height);
 }
 
@@ -470,7 +476,7 @@
               offset_visible_geometry_rect, needs_blending,
               draw_info.resource_id_for_export(), texture_rect,
               draw_info.resource_size(), draw_info.contents_swizzled(),
-              nearest_neighbor_,
+              draw_info.is_premultiplied(), nearest_neighbor_,
               !layer_tree_impl()->settings().enable_edge_anti_aliasing);
           ValidateQuadResources(quad);
           has_draw_quad = true;
@@ -940,6 +946,9 @@
   int default_tile_width = 0;
   int default_tile_height = 0;
   if (layer_tree_impl()->use_gpu_rasterization()) {
+    gfx::Size max_tile_size =
+        layer_tree_impl()->settings().max_gpu_raster_tile_size;
+
     // Calculate |base_tile_size based| on |gpu_raster_max_texture_size_|,
     // adjusting for ceil operations that may occur due to DSF.
     gfx::Size base_tile_size = ApplyDsfAdjustment(
@@ -948,14 +957,15 @@
     // Set our initial size assuming a |base_tile_size| equal to our
     // |viewport_size|.
     gfx::Size default_tile_size =
-        CalculateGpuTileSize(base_tile_size, content_bounds);
+        CalculateGpuTileSize(base_tile_size, content_bounds, max_tile_size);
 
     // Use half-width GPU tiles when the content_width is greater than our
     // calculated tile size.
     if (content_bounds.width() > default_tile_size.width()) {
       // Divide width by 2 and round up.
       base_tile_size.set_width((base_tile_size.width() + 1) / 2);
-      default_tile_size = CalculateGpuTileSize(base_tile_size, content_bounds);
+      default_tile_size =
+          CalculateGpuTileSize(base_tile_size, content_bounds, max_tile_size);
     }
 
     default_tile_width = default_tile_size.width();
diff --git a/cc/layers/render_surface_unittest.cc b/cc/layers/render_surface_unittest.cc
index 8385acee..714ddd4 100644
--- a/cc/layers/render_surface_unittest.cc
+++ b/cc/layers/render_surface_unittest.cc
@@ -68,7 +68,7 @@
     for (const auto& rect : quad_rects_) {
       auto* quad = render_pass->CreateAndAppendDrawQuad<viz::TileDrawQuad>();
       quad->SetNew(shared_quad_state, rect, rect, needs_blending, 0,
-                   gfx::RectF(rect), bounds(), false, false, false);
+                   gfx::RectF(rect), bounds(), false, false, false, false);
     }
   }
 
diff --git a/cc/raster/bitmap_raster_buffer_provider.cc b/cc/raster/bitmap_raster_buffer_provider.cc
index ab3e5f5..fde06ec 100644
--- a/cc/raster/bitmap_raster_buffer_provider.cc
+++ b/cc/raster/bitmap_raster_buffer_provider.cc
@@ -136,6 +136,11 @@
   return ResourceFormatRequiresSwizzle(viz::RGBA_8888);
 }
 
+bool BitmapRasterBufferProvider::IsResourcePremultiplied(
+    bool must_support_alpha) const {
+  return true;
+}
+
 bool BitmapRasterBufferProvider::CanPartialRasterIntoProvidedResource() const {
   return true;
 }
diff --git a/cc/raster/bitmap_raster_buffer_provider.h b/cc/raster/bitmap_raster_buffer_provider.h
index 4fc33cc..1f33b863 100644
--- a/cc/raster/bitmap_raster_buffer_provider.h
+++ b/cc/raster/bitmap_raster_buffer_provider.h
@@ -34,6 +34,7 @@
   void Flush() override;
   viz::ResourceFormat GetResourceFormat(bool must_support_alpha) const override;
   bool IsResourceSwizzleRequired(bool must_support_alpha) const override;
+  bool IsResourcePremultiplied(bool must_support_alpha) const override;
   bool CanPartialRasterIntoProvidedResource() const override;
   bool IsResourceReadyToDraw(
       const ResourcePool::InUsePoolResource& resource) const override;
diff --git a/cc/raster/gpu_raster_buffer_provider.cc b/cc/raster/gpu_raster_buffer_provider.cc
index 96a97fe..497657f2b 100644
--- a/cc/raster/gpu_raster_buffer_provider.cc
+++ b/cc/raster/gpu_raster_buffer_provider.cc
@@ -28,6 +28,7 @@
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/command_buffer/client/raster_interface.h"
 #include "gpu/command_buffer/common/gpu_memory_buffer_support.h"
+#include "skia/ext/texture_handle.h"
 #include "third_party/skia/include/core/SkMultiPictureDraw.h"
 #include "third_party/skia/include/core/SkPictureRecorder.h"
 #include "third_party/skia/include/core/SkSurface.h"
@@ -38,6 +39,73 @@
 namespace cc {
 namespace {
 
+class ScopedSkSurfaceForUnpremultiplyAndDither {
+ public:
+  ScopedSkSurfaceForUnpremultiplyAndDither(
+      viz::RasterContextProvider* context_provider,
+      const gfx::Rect& playback_rect,
+      const gfx::Rect& raster_full_rect,
+      const gfx::Size& max_tile_size,
+      GLuint texture_id,
+      const gfx::Size& texture_size,
+      bool use_distance_field_text,
+      bool can_use_lcd_text,
+      int msaa_sample_count)
+      : context_provider_(context_provider),
+        texture_id_(texture_id),
+        offset_(playback_rect.OffsetFromOrigin() -
+                raster_full_rect.OffsetFromOrigin()),
+        size_(playback_rect.size()) {
+    // Determine the |intermediate_size| to use for our 32-bit texture. If we
+    // know the max tile size, use that. This prevents GPU cache explosion due
+    // to using lots of different 32-bit texture sizes. Otherwise just use the
+    // exact size of the target texture.
+    gfx::Size intermediate_size;
+    if (!max_tile_size.IsEmpty()) {
+      DCHECK_GE(max_tile_size.width(), texture_size.width());
+      DCHECK_GE(max_tile_size.height(), texture_size.height());
+      intermediate_size = max_tile_size;
+    } else {
+      intermediate_size = texture_size;
+    }
+
+    // Allocate a 32-bit surface for raster. We will copy from that into our
+    // actual surface in destruction.
+    SkImageInfo n32Info = SkImageInfo::MakeN32Premul(
+        intermediate_size.width(), intermediate_size.height());
+    SkSurfaceProps surface_props =
+        LayerTreeResourceProvider::ScopedSkSurface::ComputeSurfaceProps(
+            use_distance_field_text, can_use_lcd_text);
+    surface_ = SkSurface::MakeRenderTarget(
+        context_provider->GrContext(), SkBudgeted::kNo, n32Info,
+        msaa_sample_count, kTopLeft_GrSurfaceOrigin, &surface_props);
+  }
+
+  ~ScopedSkSurfaceForUnpremultiplyAndDither() {
+    // In lost-context cases, |surface_| may be null and there's nothing
+    // meaningful to do here.
+    if (!surface_)
+      return;
+
+    GrBackendObject handle =
+        surface_->getTextureHandle(SkSurface::kFlushRead_BackendHandleAccess);
+    const GrGLTextureInfo* info =
+        skia::GrBackendObjectToGrGLTextureInfo(handle);
+    context_provider_->ContextGL()->UnpremultiplyAndDitherCopyCHROMIUM(
+        info->fID, texture_id_, offset_.x(), offset_.y(), size_.width(),
+        size_.height());
+  }
+
+  SkSurface* surface() { return surface_.get(); }
+
+ private:
+  viz::RasterContextProvider* context_provider_;
+  GLuint texture_id_;
+  gfx::Vector2d offset_;
+  gfx::Size size_;
+  sk_sp<SkSurface> surface_;
+};
+
 static void RasterizeSourceOOP(
     const RasterSource* raster_source,
     bool resource_has_previous_content,
@@ -87,6 +155,9 @@
                      raster_source->requires_clear());
   ri->EndRasterCHROMIUM();
 
+  // TODO(ericrk): Handle unpremultiply+dither for 4444 cases.
+  // https://crbug.com/789153
+
   ri->DeleteTextures(1, &texture_id);
 }
 
@@ -127,7 +198,9 @@
     const RasterSource::PlaybackSettings& playback_settings,
     viz::RasterContextProvider* context_provider,
     bool use_distance_field_text,
-    int msaa_sample_count) {
+    int msaa_sample_count,
+    bool unpremultiply_and_dither,
+    const gfx::Size& max_tile_size) {
   gpu::raster::RasterInterface* ri = context_provider->RasterInterface();
   GLuint texture_id = ri->CreateAndConsumeTexture(
       texture_is_overlay_candidate, gfx::BufferUsage::SCANOUT, resource_format,
@@ -142,12 +215,23 @@
 
   {
     ScopedGrContextAccess gr_context_access(context_provider);
-    LayerTreeResourceProvider::ScopedSkSurface scoped_surface(
-        context_provider->GrContext(), texture_id, texture_target,
-        resource_size, resource_format, use_distance_field_text,
-        playback_settings.use_lcd_text, msaa_sample_count);
-
-    SkSurface* surface = scoped_surface.surface();
+    base::Optional<LayerTreeResourceProvider::ScopedSkSurface> scoped_surface;
+    base::Optional<ScopedSkSurfaceForUnpremultiplyAndDither>
+        scoped_dither_surface;
+    SkSurface* surface;
+    if (!unpremultiply_and_dither) {
+      scoped_surface.emplace(context_provider->GrContext(), texture_id,
+                             texture_target, resource_size, resource_format,
+                             use_distance_field_text,
+                             playback_settings.use_lcd_text, msaa_sample_count);
+      surface = scoped_surface->surface();
+    } else {
+      scoped_dither_surface.emplace(
+          context_provider, playback_rect, raster_full_rect, max_tile_size,
+          texture_id, resource_size, use_distance_field_text,
+          playback_settings.use_lcd_text, msaa_sample_count);
+      surface = scoped_dither_surface->surface();
+    }
 
     // Allocating an SkSurface will fail after a lost context.  Pretend we
     // rasterized, as the contents of the resource don't matter anymore.
@@ -171,6 +255,15 @@
   ri->DeleteTextures(1, &texture_id);
 }
 
+bool ShouldUnpremultiplyAndDitherResource(viz::ResourceFormat format) {
+  switch (format) {
+    case viz::RGBA_4444:
+      return true;
+    default:
+      return false;
+  }
+}
+
 }  // namespace
 
 // Subclass for InUsePoolResource that holds ownership of a gpu-rastered backing
@@ -266,6 +359,7 @@
     bool use_gpu_memory_buffer_resources,
     int gpu_rasterization_msaa_sample_count,
     viz::ResourceFormat preferred_tile_format,
+    const gfx::Size& max_tile_size,
     bool enable_oop_rasterization)
     : compositor_context_provider_(compositor_context_provider),
       worker_context_provider_(worker_context_provider),
@@ -274,6 +368,7 @@
       use_gpu_memory_buffer_resources_(use_gpu_memory_buffer_resources),
       msaa_sample_count_(gpu_rasterization_msaa_sample_count),
       preferred_tile_format_(preferred_tile_format),
+      max_tile_size_(max_tile_size),
       enable_oop_rasterization_(enable_oop_rasterization) {
   DCHECK(compositor_context_provider);
   DCHECK(worker_context_provider);
@@ -344,6 +439,12 @@
   return false;
 }
 
+bool GpuRasterBufferProvider::IsResourcePremultiplied(
+    bool must_support_alpha) const {
+  return !ShouldUnpremultiplyAndDitherResource(
+      GetResourceFormat(must_support_alpha));
+}
+
 bool GpuRasterBufferProvider::CanPartialRasterIntoProvidedResource() const {
   // Partial raster doesn't support MSAA, as the MSAA resolve is unaware of clip
   // rects.
@@ -448,12 +549,13 @@
         transform, playback_settings, worker_context_provider_,
         use_distance_field_text_, msaa_sample_count_);
   } else {
-    RasterizeSource(raster_source, resource_has_previous_content, mailbox,
-                    texture_target, texture_is_overlay_candidate,
-                    texture_storage_allocated, resource_size, resource_format,
-                    color_space, raster_full_rect, playback_rect, transform,
-                    playback_settings, worker_context_provider_,
-                    use_distance_field_text_, msaa_sample_count_);
+    RasterizeSource(
+        raster_source, resource_has_previous_content, mailbox, texture_target,
+        texture_is_overlay_candidate, texture_storage_allocated, resource_size,
+        resource_format, color_space, raster_full_rect, playback_rect,
+        transform, playback_settings, worker_context_provider_,
+        use_distance_field_text_, msaa_sample_count_,
+        ShouldUnpremultiplyAndDitherResource(resource_format), max_tile_size_);
   }
 
   // Generate sync token for cross context synchronization.
diff --git a/cc/raster/gpu_raster_buffer_provider.h b/cc/raster/gpu_raster_buffer_provider.h
index 18a5ec6..f5ec7f2 100644
--- a/cc/raster/gpu_raster_buffer_provider.h
+++ b/cc/raster/gpu_raster_buffer_provider.h
@@ -28,6 +28,7 @@
                           bool use_gpu_memory_buffer_resources,
                           int gpu_rasterization_msaa_sample_count,
                           viz::ResourceFormat preferred_tile_format,
+                          const gfx::Size& max_tile_size,
                           bool enable_oop_rasterization);
   ~GpuRasterBufferProvider() override;
 
@@ -39,6 +40,7 @@
   void Flush() override;
   viz::ResourceFormat GetResourceFormat(bool must_support_alpha) const override;
   bool IsResourceSwizzleRequired(bool must_support_alpha) const override;
+  bool IsResourcePremultiplied(bool must_support_alpha) const override;
   bool CanPartialRasterIntoProvidedResource() const override;
   bool IsResourceReadyToDraw(
       const ResourcePool::InUsePoolResource& resource) const override;
@@ -116,6 +118,7 @@
   const bool use_gpu_memory_buffer_resources_;
   const int msaa_sample_count_;
   const viz::ResourceFormat preferred_tile_format_;
+  const gfx::Size max_tile_size_;
   const bool enable_oop_rasterization_;
 
   DISALLOW_COPY_AND_ASSIGN(GpuRasterBufferProvider);
diff --git a/cc/raster/one_copy_raster_buffer_provider.cc b/cc/raster/one_copy_raster_buffer_provider.cc
index d55894c5..6388dc7 100644
--- a/cc/raster/one_copy_raster_buffer_provider.cc
+++ b/cc/raster/one_copy_raster_buffer_provider.cc
@@ -225,6 +225,13 @@
   return ResourceFormatRequiresSwizzle(GetResourceFormat(must_support_alpha));
 }
 
+bool OneCopyRasterBufferProvider::IsResourcePremultiplied(
+    bool must_support_alpha) const {
+  // TODO(ericrk): Handle unpremultiply/dither in one-copy case as well.
+  // https://crbug.com/789153
+  return true;
+}
+
 bool OneCopyRasterBufferProvider::CanPartialRasterIntoProvidedResource() const {
   // While OneCopyRasterBufferProvider has an internal partial raster
   // implementation, it cannot directly partial raster into the externally
diff --git a/cc/raster/one_copy_raster_buffer_provider.h b/cc/raster/one_copy_raster_buffer_provider.h
index d1408d13..26f5454 100644
--- a/cc/raster/one_copy_raster_buffer_provider.h
+++ b/cc/raster/one_copy_raster_buffer_provider.h
@@ -44,6 +44,7 @@
   void Flush() override;
   viz::ResourceFormat GetResourceFormat(bool must_support_alpha) const override;
   bool IsResourceSwizzleRequired(bool must_support_alpha) const override;
+  bool IsResourcePremultiplied(bool must_support_alpha) const override;
   bool CanPartialRasterIntoProvidedResource() const override;
   bool IsResourceReadyToDraw(
       const ResourcePool::InUsePoolResource& resource) const override;
diff --git a/cc/raster/raster_buffer_provider.h b/cc/raster/raster_buffer_provider.h
index c25f2e9..12b7e0d 100644
--- a/cc/raster/raster_buffer_provider.h
+++ b/cc/raster/raster_buffer_provider.h
@@ -62,6 +62,9 @@
   // Determine if the resource requires swizzling.
   virtual bool IsResourceSwizzleRequired(bool must_support_alpha) const = 0;
 
+  // Determines if the resource is premultiplied.
+  virtual bool IsResourcePremultiplied(bool must_support_alpha) const = 0;
+
   // Determine if the RasterBufferProvider can handle partial raster into
   // the Resource provided in AcquireBufferForRaster.
   virtual bool CanPartialRasterIntoProvidedResource() const = 0;
diff --git a/cc/raster/raster_buffer_provider_perftest.cc b/cc/raster/raster_buffer_provider_perftest.cc
index 7531ddd..0f4dfa3e 100644
--- a/cc/raster/raster_buffer_provider_perftest.cc
+++ b/cc/raster/raster_buffer_provider_perftest.cc
@@ -380,7 +380,7 @@
         raster_buffer_provider_ = std::make_unique<GpuRasterBufferProvider>(
             compositor_context_provider_.get(), worker_context_provider_.get(),
             resource_provider_.get(), false, false, 0,
-            viz::PlatformColor::BestTextureFormat(), false);
+            viz::PlatformColor::BestTextureFormat(), gfx::Size(), false);
         resource_pool_ = std::make_unique<ResourcePool>(
             resource_provider_.get(), task_runner_,
             ResourcePool::kDefaultExpirationDelay, ResourcePool::Mode::kGpu,
diff --git a/cc/raster/raster_buffer_provider_unittest.cc b/cc/raster/raster_buffer_provider_unittest.cc
index e1634fb..1881694 100644
--- a/cc/raster/raster_buffer_provider_unittest.cc
+++ b/cc/raster/raster_buffer_provider_unittest.cc
@@ -182,7 +182,7 @@
         raster_buffer_provider_ = std::make_unique<GpuRasterBufferProvider>(
             context_provider_.get(), worker_context_provider_.get(),
             resource_provider_.get(), false, false, 0,
-            viz::PlatformColor::BestTextureFormat(), false);
+            viz::PlatformColor::BestTextureFormat(), gfx::Size(), false);
         pool_ = std::make_unique<ResourcePool>(
             resource_provider_.get(), base::ThreadTaskRunnerHandle::Get(),
             base::TimeDelta(), ResourcePool::Mode::kGpu, true);
diff --git a/cc/raster/zero_copy_raster_buffer_provider.cc b/cc/raster/zero_copy_raster_buffer_provider.cc
index fd311dd9..c17f9753 100644
--- a/cc/raster/zero_copy_raster_buffer_provider.cc
+++ b/cc/raster/zero_copy_raster_buffer_provider.cc
@@ -250,6 +250,11 @@
   return ResourceFormatRequiresSwizzle(GetResourceFormat(must_support_alpha));
 }
 
+bool ZeroCopyRasterBufferProvider::IsResourcePremultiplied(
+    bool must_support_alpha) const {
+  return true;
+}
+
 bool ZeroCopyRasterBufferProvider::CanPartialRasterIntoProvidedResource()
     const {
   return false;
diff --git a/cc/raster/zero_copy_raster_buffer_provider.h b/cc/raster/zero_copy_raster_buffer_provider.h
index 1c6c6ae7..d7a118e 100644
--- a/cc/raster/zero_copy_raster_buffer_provider.h
+++ b/cc/raster/zero_copy_raster_buffer_provider.h
@@ -42,6 +42,7 @@
   void Flush() override;
   viz::ResourceFormat GetResourceFormat(bool must_support_alpha) const override;
   bool IsResourceSwizzleRequired(bool must_support_alpha) const override;
+  bool IsResourcePremultiplied(bool must_support_alpha) const override;
   bool CanPartialRasterIntoProvidedResource() const override;
   bool IsResourceReadyToDraw(
       const ResourcePool::InUsePoolResource& resource) const override;
diff --git a/cc/resources/layer_tree_resource_provider.cc b/cc/resources/layer_tree_resource_provider.cc
index 620ef69..9fb642c 100644
--- a/cc/resources/layer_tree_resource_provider.cc
+++ b/cc/resources/layer_tree_resource_provider.cc
@@ -997,6 +997,21 @@
   texture_info.fFormat = TextureStorageFormat(format);
   GrBackendTexture backend_texture(size.width(), size.height(),
                                    GrMipMapped::kNo, texture_info);
+  SkSurfaceProps surface_props =
+      ComputeSurfaceProps(use_distance_field_text, can_use_lcd_text);
+  surface_ = SkSurface::MakeFromBackendTextureAsRenderTarget(
+      gr_context, backend_texture, kTopLeft_GrSurfaceOrigin, msaa_sample_count,
+      ResourceFormatToClosestSkColorType(format), nullptr, &surface_props);
+}
+
+LayerTreeResourceProvider::ScopedSkSurface::~ScopedSkSurface() {
+  if (surface_)
+    surface_->prepareForExternalIO();
+}
+
+SkSurfaceProps LayerTreeResourceProvider::ScopedSkSurface::ComputeSurfaceProps(
+    bool use_distance_field_text,
+    bool can_use_lcd_text) {
   uint32_t flags =
       use_distance_field_text ? SkSurfaceProps::kUseDistanceFieldFonts_Flag : 0;
   // Use unknown pixel geometry to disable LCD text.
@@ -1006,14 +1021,7 @@
     surface_props =
         SkSurfaceProps(flags, SkSurfaceProps::kLegacyFontHost_InitType);
   }
-  surface_ = SkSurface::MakeFromBackendTextureAsRenderTarget(
-      gr_context, backend_texture, kTopLeft_GrSurfaceOrigin, msaa_sample_count,
-      ResourceFormatToClosestSkColorType(format), nullptr, &surface_props);
-}
-
-LayerTreeResourceProvider::ScopedSkSurface::~ScopedSkSurface() {
-  if (surface_)
-    surface_->prepareForExternalIO();
+  return surface_props;
 }
 
 void LayerTreeResourceProvider::ValidateResource(viz::ResourceId id) const {
diff --git a/cc/resources/layer_tree_resource_provider.h b/cc/resources/layer_tree_resource_provider.h
index 531762a..6d81451 100644
--- a/cc/resources/layer_tree_resource_provider.h
+++ b/cc/resources/layer_tree_resource_provider.h
@@ -294,6 +294,9 @@
 
     SkSurface* surface() const { return surface_.get(); }
 
+    static SkSurfaceProps ComputeSurfaceProps(bool use_distance_field_text,
+                                              bool can_use_lcd_text);
+
    private:
     sk_sp<SkSurface> surface_;
 
diff --git a/cc/test/fake_raster_buffer_provider.cc b/cc/test/fake_raster_buffer_provider.cc
index 4ca560e..efed6c9 100644
--- a/cc/test/fake_raster_buffer_provider.cc
+++ b/cc/test/fake_raster_buffer_provider.cc
@@ -44,6 +44,11 @@
   return ResourceFormatRequiresSwizzle(GetResourceFormat(must_support_alpha));
 }
 
+bool FakeRasterBufferProviderImpl::IsResourcePremultiplied(
+    bool must_support_alpha) const {
+  return true;
+}
+
 bool FakeRasterBufferProviderImpl::CanPartialRasterIntoProvidedResource()
     const {
   return true;
diff --git a/cc/test/fake_raster_buffer_provider.h b/cc/test/fake_raster_buffer_provider.h
index 2b0bf18..159cb39 100644
--- a/cc/test/fake_raster_buffer_provider.h
+++ b/cc/test/fake_raster_buffer_provider.h
@@ -23,6 +23,7 @@
   void Flush() override;
   viz::ResourceFormat GetResourceFormat(bool must_support_alpha) const override;
   bool IsResourceSwizzleRequired(bool must_support_alpha) const override;
+  bool IsResourcePremultiplied(bool must_support_alpha) const override;
   bool CanPartialRasterIntoProvidedResource() const override;
   bool IsResourceReadyToDraw(
       const ResourcePool::InUsePoolResource& resource) const override;
diff --git a/cc/test/layer_tree_pixel_resource_test.cc b/cc/test/layer_tree_pixel_resource_test.cc
index 499db82..2a5c16b 100644
--- a/cc/test/layer_tree_pixel_resource_test.cc
+++ b/cc/test/layer_tree_pixel_resource_test.cc
@@ -71,7 +71,7 @@
       return std::make_unique<GpuRasterBufferProvider>(
           compositor_context_provider, worker_context_provider,
           resource_provider, false, false, 0,
-          viz::PlatformColor::BestTextureFormat(), false);
+          viz::PlatformColor::BestTextureFormat(), gfx::Size(), false);
     case ZERO_COPY:
       EXPECT_TRUE(compositor_context_provider);
       EXPECT_TRUE(gpu_memory_buffer_manager);
diff --git a/cc/test/render_pass_test_utils.cc b/cc/test/render_pass_test_utils.cc
index 8f22f73..10bcbe4 100644
--- a/cc/test/render_pass_test_utils.cc
+++ b/cc/test/render_pass_test_utils.cc
@@ -216,7 +216,7 @@
       to_pass->CreateAndAppendDrawQuad<viz::TileDrawQuad>();
   scaled_tile_quad->SetNew(shared_state, rect, visible_rect, needs_blending,
                            resource2, gfx::RectF(0, 0, 50, 50),
-                           gfx::Size(50, 50), false, false, false);
+                           gfx::Size(50, 50), false, false, false, false);
 
   viz::SharedQuadState* transformed_state =
       to_pass->CreateAndAppendSharedQuadState();
@@ -227,9 +227,10 @@
       transformed_state->quad_to_target_transform * rotation;
   auto* transformed_tile_quad =
       to_pass->CreateAndAppendDrawQuad<viz::TileDrawQuad>();
-  transformed_tile_quad->SetNew(
-      transformed_state, rect, visible_rect, needs_blending, resource3,
-      gfx::RectF(0, 0, 100, 100), gfx::Size(100, 100), false, false, false);
+  transformed_tile_quad->SetNew(transformed_state, rect, visible_rect,
+                                needs_blending, resource3,
+                                gfx::RectF(0, 0, 100, 100), gfx::Size(100, 100),
+                                false, false, false, false);
 
   viz::SharedQuadState* shared_state2 =
       to_pass->CreateAndAppendSharedQuadState();
@@ -239,7 +240,7 @@
   auto* tile_quad = to_pass->CreateAndAppendDrawQuad<viz::TileDrawQuad>();
   tile_quad->SetNew(shared_state2, rect, visible_rect, needs_blending,
                     resource4, gfx::RectF(0, 0, 100, 100), gfx::Size(100, 100),
-                    false, false, false);
+                    false, false, false, false);
 
   viz::ResourceId plane_resources[4];
   for (int i = 0; i < 4; ++i) {
@@ -417,7 +418,7 @@
       to_pass->CreateAndAppendDrawQuad<viz::TileDrawQuad>();
   scaled_tile_quad->SetNew(shared_state, rect, visible_rect, needs_blending,
                            mapped_resource2, gfx::RectF(0, 0, 50, 50),
-                           gfx::Size(50, 50), false, false, false);
+                           gfx::Size(50, 50), false, false, false, false);
 
   viz::SharedQuadState* transformed_state =
       to_pass->CreateAndAppendSharedQuadState();
@@ -428,9 +429,10 @@
       transformed_state->quad_to_target_transform * rotation;
   viz::TileDrawQuad* transformed_tile_quad =
       to_pass->CreateAndAppendDrawQuad<viz::TileDrawQuad>();
-  transformed_tile_quad->SetNew(
-      transformed_state, rect, visible_rect, needs_blending, mapped_resource3,
-      gfx::RectF(0, 0, 100, 100), gfx::Size(100, 100), false, false, false);
+  transformed_tile_quad->SetNew(transformed_state, rect, visible_rect,
+                                needs_blending, mapped_resource3,
+                                gfx::RectF(0, 0, 100, 100), gfx::Size(100, 100),
+                                false, false, false, false);
 
   viz::SharedQuadState* shared_state2 =
       to_pass->CreateAndAppendSharedQuadState();
@@ -441,7 +443,7 @@
       to_pass->CreateAndAppendDrawQuad<viz::TileDrawQuad>();
   tile_quad->SetNew(shared_state2, rect, visible_rect, needs_blending,
                     mapped_resource4, gfx::RectF(0, 0, 100, 100),
-                    gfx::Size(100, 100), false, false, false);
+                    gfx::Size(100, 100), false, false, false, false);
 
   viz::YUVVideoDrawQuad* yuv_quad =
       to_pass->CreateAndAppendDrawQuad<viz::YUVVideoDrawQuad>();
diff --git a/cc/tiles/tile_draw_info.cc b/cc/tiles/tile_draw_info.cc
index 0e51c2b2..0c09fa28 100644
--- a/cc/tiles/tile_draw_info.cc
+++ b/cc/tiles/tile_draw_info.cc
@@ -22,7 +22,8 @@
 
 void TileDrawInfo::SetResource(ResourcePool::InUsePoolResource resource,
                                bool resource_is_checker_imaged,
-                               bool contents_swizzled) {
+                               bool contents_swizzled,
+                               bool is_premultiplied) {
   DCHECK(!resource_);
   DCHECK(resource);
 
@@ -30,6 +31,7 @@
   is_resource_ready_to_draw_ = false;
   resource_is_checker_imaged_ = resource_is_checker_imaged;
   contents_swizzled_ = contents_swizzled;
+  is_premultiplied_ = is_premultiplied;
   resource_ = std::move(resource);
 }
 
@@ -45,6 +47,7 @@
   is_resource_ready_to_draw_ = false;
   resource_is_checker_imaged_ = false;
   contents_swizzled_ = false;
+  is_premultiplied_ = false;
   return std::move(resource_);
 }
 
diff --git a/cc/tiles/tile_draw_info.h b/cc/tiles/tile_draw_info.h
index 5ae3cbe0..c401258 100644
--- a/cc/tiles/tile_draw_info.h
+++ b/cc/tiles/tile_draw_info.h
@@ -73,6 +73,7 @@
   }
 
   bool contents_swizzled() const { return contents_swizzled_; }
+  bool is_premultiplied() const { return is_premultiplied_; }
 
   bool requires_resource() const {
     return mode_ == RESOURCE_MODE || mode_ == OOM_MODE;
@@ -101,7 +102,8 @@
 
   void SetResource(ResourcePool::InUsePoolResource resource,
                    bool resource_is_checker_imaged,
-                   bool contents_swizzled);
+                   bool contents_swizzled,
+                   bool is_premultiplied);
   ResourcePool::InUsePoolResource TakeResource();
 
   void set_resource_ready_for_draw() {
@@ -120,6 +122,7 @@
   SkColor solid_color_ = SK_ColorWHITE;
   ResourcePool::InUsePoolResource resource_;
   bool contents_swizzled_ = false;
+  bool is_premultiplied_ = false;
   bool is_resource_ready_to_draw_ = false;
 
   // Set to true if |resource_| was rasterized with checker-imaged content. The
diff --git a/cc/tiles/tile_manager.cc b/cc/tiles/tile_manager.cc
index 4815f07..e5ed9cd 100644
--- a/cc/tiles/tile_manager.cc
+++ b/cc/tiles/tile_manager.cc
@@ -1272,9 +1272,11 @@
   TileDrawInfo& draw_info = tile->draw_info();
   bool needs_swizzle =
       raster_buffer_provider_->IsResourceSwizzleRequired(!tile->is_opaque());
+  bool is_premultiplied =
+      raster_buffer_provider_->IsResourcePremultiplied(!tile->is_opaque());
   draw_info.SetResource(std::move(resource),
                         raster_task_was_scheduled_with_checker_images,
-                        needs_swizzle);
+                        needs_swizzle, is_premultiplied);
   if (raster_task_was_scheduled_with_checker_images)
     num_of_tiles_with_checker_images_++;
 
diff --git a/cc/tiles/tile_manager.h b/cc/tiles/tile_manager.h
index 1c4a90d..4170c15 100644
--- a/cc/tiles/tile_manager.h
+++ b/cc/tiles/tile_manager.h
@@ -205,7 +205,7 @@
             gpu::GPU_IO, gpu::CommandBufferId::FromUnsafeValue(1), 1);
       }
       resource_pool_->PrepareForExport(resource);
-      draw_info.SetResource(std::move(resource), false, false);
+      draw_info.SetResource(std::move(resource), false, false, false);
       draw_info.set_resource_ready_for_draw();
     }
   }
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index adbca55..be3c78f 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -2640,7 +2640,8 @@
         compositor_context_provider, worker_context_provider,
         resource_provider_.get(), settings_.use_distance_field_text,
         settings_.resource_settings.use_gpu_memory_buffer_resources,
-        msaa_sample_count, settings_.preferred_tile_format, oop_raster_enabled);
+        msaa_sample_count, settings_.preferred_tile_format,
+        settings_.max_gpu_raster_tile_size, oop_raster_enabled);
   }
 
   bool use_zero_copy = settings_.use_zero_copy;
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index eab889e7..4869951 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -8299,7 +8299,7 @@
     test_blending_draw_quad->SetNew(
         shared_quad_state, quad_rect_, visible_quad_rect, needs_blending,
         resource_id_, gfx::RectF(0.f, 0.f, 1.f, 1.f), gfx::Size(1, 1), false,
-        false, false);
+        false, false, false);
 
     EXPECT_EQ(blend_, test_blending_draw_quad->ShouldDrawWithBlending());
     EXPECT_EQ(has_render_surface_,
diff --git a/cc/trees/layer_tree_host_pixeltest_tiles.cc b/cc/trees/layer_tree_host_pixeltest_tiles.cc
index 7156d69..b2c81a9 100644
--- a/cc/trees/layer_tree_host_pixeltest_tiles.cc
+++ b/cc/trees/layer_tree_host_pixeltest_tiles.cc
@@ -24,6 +24,8 @@
   FULL_ONE_COPY,
   PARTIAL_GPU,
   FULL_GPU,
+  PARTIAL_GPU_LOW_BIT_DEPTH,
+  FULL_GPU_LOW_BIT_DEPTH,
   PARTIAL_BITMAP,
   FULL_BITMAP,
 };
@@ -55,6 +57,16 @@
         settings->gpu_rasterization_forced = true;
         settings->use_partial_raster = false;
         break;
+      case PARTIAL_GPU_LOW_BIT_DEPTH:
+        settings->gpu_rasterization_forced = true;
+        settings->use_partial_raster = true;
+        settings->preferred_tile_format = viz::RGBA_4444;
+        break;
+      case FULL_GPU_LOW_BIT_DEPTH:
+        settings->gpu_rasterization_forced = true;
+        settings->use_partial_raster = false;
+        settings->preferred_tile_format = viz::RGBA_4444;
+        break;
     }
   }
 
@@ -81,6 +93,8 @@
       case FULL_ONE_COPY:
       case PARTIAL_GPU:
       case FULL_GPU:
+      case PARTIAL_GPU_LOW_BIT_DEPTH:
+      case FULL_GPU_LOW_BIT_DEPTH:
         test_type = PIXEL_TEST_GL;
         break;
       case PARTIAL_BITMAP:
@@ -120,9 +134,12 @@
     PaintFlags flags;
     flags.setStyle(PaintFlags::kFill_Style);
 
-    flags.setColor(SK_ColorBLUE);
+    // Use custom colors with 0xF2 rather than the default blue/yellow (which
+    // use 0xFF), as the default won't show dither patterns as it exactly maps
+    // to a 16-bit color.
+    flags.setColor(SkColorSetRGB(0x00, 0x00, 0xF2));
     display_list->push<DrawRectOp>(gfx::RectToSkRect(blue_rect), flags);
-    flags.setColor(SK_ColorYELLOW);
+    flags.setColor(SkColorSetRGB(0xF2, 0xF2, 0x00));
     display_list->push<DrawRectOp>(gfx::RectToSkRect(yellow_rect), flags);
 
     display_list->EndPaintOfUnpaired(PaintableRegion());
@@ -247,6 +264,20 @@
       base::FilePath(FILE_PATH_LITERAL("blue_yellow_flipped.png")));
 }
 
+TEST_F(LayerTreeHostTilesTestPartialInvalidation,
+       PartialRaster_SingleThread_GpuRaster_LowBitDepth) {
+  RunRasterPixelTest(false, PARTIAL_GPU_LOW_BIT_DEPTH, picture_layer_,
+                     base::FilePath(FILE_PATH_LITERAL(
+                         "blue_yellow_partial_flipped_dither.png")));
+}
+
+TEST_F(LayerTreeHostTilesTestPartialInvalidation,
+       FullRaster_SingleThread_GpuRaster_LowBitDepth) {
+  RunRasterPixelTest(
+      false, FULL_GPU_LOW_BIT_DEPTH, picture_layer_,
+      base::FilePath(FILE_PATH_LITERAL("blue_yellow_flipped_dither.png")));
+}
+
 }  // namespace
 }  // namespace cc
 
diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h
index dfbc8a5..189d7d7 100644
--- a/cc/trees/layer_tree_settings.h
+++ b/cc/trees/layer_tree_settings.h
@@ -71,6 +71,9 @@
   double background_animation_rate = 1.0;
   gfx::Size default_tile_size;
   gfx::Size max_untiled_layer_size;
+  // If set, indicates the largest tile size we will use for GPU Raster. If not
+  // set, no limit is enforced.
+  gfx::Size max_gpu_raster_tile_size;
   gfx::Size minimum_occlusion_tracking_size;
   // 3000 pixels should give sufficient area for prepainting.
   // Note this value is specified with an ideal contents scale in mind. That
diff --git a/components/viz/common/quads/content_draw_quad_base.cc b/components/viz/common/quads/content_draw_quad_base.cc
index f19b0b2..fc84e35 100644
--- a/components/viz/common/quads/content_draw_quad_base.cc
+++ b/components/viz/common/quads/content_draw_quad_base.cc
@@ -23,6 +23,7 @@
                                  const gfx::RectF& tex_coord_rect,
                                  const gfx::Size& texture_size,
                                  bool swizzle_contents,
+                                 bool is_premultiplied,
                                  bool nearest_neighbor,
                                  bool force_anti_aliasing_off) {
   DrawQuad::SetAll(shared_quad_state, material, rect, visible_rect,
@@ -30,6 +31,7 @@
   this->tex_coord_rect = tex_coord_rect;
   this->texture_size = texture_size;
   this->swizzle_contents = swizzle_contents;
+  this->is_premultiplied = is_premultiplied;
   this->nearest_neighbor = nearest_neighbor;
   this->force_anti_aliasing_off = force_anti_aliasing_off;
 }
@@ -42,6 +44,7 @@
                                  const gfx::RectF& tex_coord_rect,
                                  const gfx::Size& texture_size,
                                  bool swizzle_contents,
+                                 bool is_premultiplied,
                                  bool nearest_neighbor,
                                  bool force_anti_aliasing_off) {
   DrawQuad::SetAll(shared_quad_state, material, rect, visible_rect,
@@ -49,6 +52,7 @@
   this->tex_coord_rect = tex_coord_rect;
   this->texture_size = texture_size;
   this->swizzle_contents = swizzle_contents;
+  this->is_premultiplied = is_premultiplied;
   this->nearest_neighbor = nearest_neighbor;
   this->force_anti_aliasing_off = force_anti_aliasing_off;
 }
diff --git a/components/viz/common/quads/content_draw_quad_base.h b/components/viz/common/quads/content_draw_quad_base.h
index 473fb4a..9b738be 100644
--- a/components/viz/common/quads/content_draw_quad_base.h
+++ b/components/viz/common/quads/content_draw_quad_base.h
@@ -28,6 +28,7 @@
               const gfx::RectF& tex_coord_rect,
               const gfx::Size& texture_size,
               bool swizzle_contents,
+              bool is_premultiplied,
               bool nearest_neighbor,
               bool force_anti_aliasing_off);
 
@@ -39,12 +40,14 @@
               const gfx::RectF& tex_coord_rect,
               const gfx::Size& texture_size,
               bool swizzle_contents,
+              bool is_premultiplied,
               bool nearest_neighbor,
               bool force_anti_aliasing_off);
 
   gfx::RectF tex_coord_rect;
   gfx::Size texture_size;
   bool swizzle_contents = false;
+  bool is_premultiplied = false;
   bool nearest_neighbor = false;
   bool force_anti_aliasing_off = false;
 
diff --git a/components/viz/common/quads/draw_quad_unittest.cc b/components/viz/common/quads/draw_quad_unittest.cc
index d53adc0..c225a7c 100644
--- a/components/viz/common/quads/draw_quad_unittest.cc
+++ b/components/viz/common/quads/draw_quad_unittest.cc
@@ -352,13 +352,15 @@
   gfx::RectF tex_coord_rect(31.f, 12.f, 54.f, 20.f);
   gfx::Size texture_size(85, 32);
   bool swizzle_contents = true;
+  bool contents_premultiplied = true;
   bool nearest_neighbor = true;
   bool force_anti_aliasing_off = false;
   CREATE_SHARED_STATE();
 
   CREATE_QUAD_NEW(TileDrawQuad, visible_rect, needs_blending, resource_id,
                   tex_coord_rect, texture_size, swizzle_contents,
-                  nearest_neighbor, force_anti_aliasing_off);
+                  contents_premultiplied, nearest_neighbor,
+                  force_anti_aliasing_off);
   EXPECT_EQ(DrawQuad::TILED_CONTENT, copy_quad->material);
   EXPECT_EQ(visible_rect, copy_quad->visible_rect);
   EXPECT_EQ(needs_blending, copy_quad->needs_blending);
@@ -369,7 +371,8 @@
   EXPECT_EQ(nearest_neighbor, copy_quad->nearest_neighbor);
 
   CREATE_QUAD_ALL(TileDrawQuad, resource_id, tex_coord_rect, texture_size,
-                  swizzle_contents, nearest_neighbor, force_anti_aliasing_off);
+                  swizzle_contents, contents_premultiplied, nearest_neighbor,
+                  force_anti_aliasing_off);
   EXPECT_EQ(DrawQuad::TILED_CONTENT, copy_quad->material);
   EXPECT_EQ(resource_id, copy_quad->resource_id());
   EXPECT_EQ(tex_coord_rect, copy_quad->tex_coord_rect);
@@ -599,13 +602,15 @@
   gfx::RectF tex_coord_rect(31.f, 12.f, 54.f, 20.f);
   gfx::Size texture_size(85, 32);
   bool swizzle_contents = true;
+  bool contents_premultiplied = true;
   bool nearest_neighbor = true;
   bool force_anti_aliasing_off = false;
 
   CREATE_SHARED_STATE();
   CREATE_QUAD_NEW(TileDrawQuad, visible_rect, needs_blending, resource_id,
                   tex_coord_rect, texture_size, swizzle_contents,
-                  nearest_neighbor, force_anti_aliasing_off);
+                  contents_premultiplied, nearest_neighbor,
+                  force_anti_aliasing_off);
   EXPECT_EQ(resource_id, quad_new->resource_id());
   EXPECT_EQ(1, IterateAndCount(quad_new));
   EXPECT_EQ(resource_id + 1, quad_new->resource_id());
diff --git a/components/viz/common/quads/picture_draw_quad.cc b/components/viz/common/quads/picture_draw_quad.cc
index 4df5c55..2a51e85 100644
--- a/components/viz/common/quads/picture_draw_quad.cc
+++ b/components/viz/common/quads/picture_draw_quad.cc
@@ -32,8 +32,8 @@
   ContentDrawQuadBase::SetNew(
       shared_quad_state, DrawQuad::PICTURE_CONTENT, rect, visible_rect,
       needs_blending, tex_coord_rect, texture_size,
-      !PlatformColor::SameComponentOrder(texture_format), nearest_neighbor,
-      false);
+      !PlatformColor::SameComponentOrder(texture_format), false,
+      nearest_neighbor, false);
   this->content_rect = content_rect;
   this->contents_scale = contents_scale;
   this->display_item_list = std::move(display_item_list);
@@ -55,8 +55,8 @@
   ContentDrawQuadBase::SetAll(
       shared_quad_state, DrawQuad::PICTURE_CONTENT, rect, visible_rect,
       needs_blending, tex_coord_rect, texture_size,
-      !PlatformColor::SameComponentOrder(texture_format), nearest_neighbor,
-      false);
+      !PlatformColor::SameComponentOrder(texture_format), false,
+      nearest_neighbor, false);
   this->content_rect = content_rect;
   this->contents_scale = contents_scale;
   this->display_item_list = std::move(display_item_list);
diff --git a/components/viz/common/quads/tile_draw_quad.cc b/components/viz/common/quads/tile_draw_quad.cc
index 07836c0..72e8dee 100644
--- a/components/viz/common/quads/tile_draw_quad.cc
+++ b/components/viz/common/quads/tile_draw_quad.cc
@@ -22,12 +22,13 @@
                           const gfx::RectF& tex_coord_rect,
                           const gfx::Size& texture_size,
                           bool swizzle_contents,
+                          bool is_premultiplied,
                           bool nearest_neighbor,
                           bool force_anti_aliasing_off) {
   ContentDrawQuadBase::SetNew(shared_quad_state, DrawQuad::TILED_CONTENT, rect,
                               visible_rect, needs_blending, tex_coord_rect,
-                              texture_size, swizzle_contents, nearest_neighbor,
-                              force_anti_aliasing_off);
+                              texture_size, swizzle_contents, is_premultiplied,
+                              nearest_neighbor, force_anti_aliasing_off);
   resources.ids[kResourceIdIndex] = resource_id;
   resources.count = 1;
 }
@@ -40,12 +41,13 @@
                           const gfx::RectF& tex_coord_rect,
                           const gfx::Size& texture_size,
                           bool swizzle_contents,
+                          bool is_premultiplied,
                           bool nearest_neighbor,
                           bool force_anti_aliasing_off) {
   ContentDrawQuadBase::SetAll(shared_quad_state, DrawQuad::TILED_CONTENT, rect,
                               visible_rect, needs_blending, tex_coord_rect,
-                              texture_size, swizzle_contents, nearest_neighbor,
-                              force_anti_aliasing_off);
+                              texture_size, swizzle_contents, is_premultiplied,
+                              nearest_neighbor, force_anti_aliasing_off);
   resources.ids[kResourceIdIndex] = resource_id;
   resources.count = 1;
 }
diff --git a/components/viz/common/quads/tile_draw_quad.h b/components/viz/common/quads/tile_draw_quad.h
index 3d51c5df..3fd00ed 100644
--- a/components/viz/common/quads/tile_draw_quad.h
+++ b/components/viz/common/quads/tile_draw_quad.h
@@ -30,6 +30,7 @@
               const gfx::RectF& tex_coord_rect,
               const gfx::Size& texture_size,
               bool swizzle_contents,
+              bool is_premultiplied,
               bool nearest_neighbor,
               bool force_anti_aliasing_off);
 
@@ -44,6 +45,7 @@
               const gfx::RectF& tex_coord_rect,
               const gfx::Size& texture_size,
               bool swizzle_contents,
+              bool is_premultiplied,
               bool nearest_neighbor,
               bool force_anti_aliasing_off);
 
diff --git a/components/viz/service/display/gl_renderer.cc b/components/viz/service/display/gl_renderer.cc
index 7b96160..e6e852a 100644
--- a/components/viz/service/display/gl_renderer.cc
+++ b/components/viz/service/display/gl_renderer.cc
@@ -1942,8 +1942,10 @@
 
   SetUseProgram(
       ProgramKey::Tile(tex_coord_precision, sampler, USE_AA,
-                       quad->swizzle_contents ? DO_SWIZZLE : NO_SWIZZLE, false,
-                       false, tint_gl_composited_content_),
+                       quad->swizzle_contents ? DO_SWIZZLE : NO_SWIZZLE,
+                       quad->is_premultiplied ? PREMULTIPLIED_ALPHA
+                                              : NON_PREMULTIPLIED_ALPHA,
+                       false, false, tint_gl_composited_content_),
       quad_resource_lock.color_space(),
       current_frame()->current_render_pass->color_space);
 
@@ -2029,6 +2031,8 @@
   SetUseProgram(
       ProgramKey::Tile(tex_coord_precision, sampler, NO_AA,
                        quad->swizzle_contents ? DO_SWIZZLE : NO_SWIZZLE,
+                       quad->is_premultiplied ? PREMULTIPLIED_ALPHA
+                                              : NON_PREMULTIPLIED_ALPHA,
                        !quad->ShouldDrawWithBlending(), has_tex_clamp_rect,
                        tint_gl_composited_content_),
       quad_resource_lock.color_space(),
diff --git a/components/viz/service/display/gl_renderer_unittest.cc b/components/viz/service/display/gl_renderer_unittest.cc
index 5fee46e..de23d53e 100644
--- a/components/viz/service/display/gl_renderer_unittest.cc
+++ b/components/viz/service/display/gl_renderer_unittest.cc
@@ -290,26 +290,46 @@
     TestShader(ProgramKey::Texture(precision, sampler, NON_PREMULTIPLIED_ALPHA,
                                    true, false, false));
 
-    TestShader(ProgramKey::Tile(precision, sampler, USE_AA, NO_SWIZZLE, false,
-                                false, false));
-    TestShader(ProgramKey::Tile(precision, sampler, USE_AA, DO_SWIZZLE, false,
-                                false, false));
-    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, NO_SWIZZLE, false,
-                                false, false));
-    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, DO_SWIZZLE, false,
-                                false, false));
-    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, NO_SWIZZLE, true,
-                                false, false));
-    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, DO_SWIZZLE, true,
-                                false, false));
-    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, NO_SWIZZLE, false,
-                                true, false));
-    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, DO_SWIZZLE, false,
-                                true, false));
-    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, NO_SWIZZLE, true,
-                                true, false));
-    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, DO_SWIZZLE, true,
-                                true, false));
+    TestShader(ProgramKey::Tile(precision, sampler, USE_AA, NO_SWIZZLE,
+                                PREMULTIPLIED_ALPHA, false, false, false));
+    TestShader(ProgramKey::Tile(precision, sampler, USE_AA, DO_SWIZZLE,
+                                PREMULTIPLIED_ALPHA, false, false, false));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, NO_SWIZZLE,
+                                PREMULTIPLIED_ALPHA, false, false, false));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, DO_SWIZZLE,
+                                PREMULTIPLIED_ALPHA, false, false, false));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, NO_SWIZZLE,
+                                PREMULTIPLIED_ALPHA, true, false, false));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, DO_SWIZZLE,
+                                PREMULTIPLIED_ALPHA, true, false, false));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, NO_SWIZZLE,
+                                PREMULTIPLIED_ALPHA, false, true, false));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, DO_SWIZZLE,
+                                PREMULTIPLIED_ALPHA, false, true, false));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, NO_SWIZZLE,
+                                PREMULTIPLIED_ALPHA, true, true, false));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, DO_SWIZZLE,
+                                PREMULTIPLIED_ALPHA, true, true, false));
+    TestShader(ProgramKey::Tile(precision, sampler, USE_AA, NO_SWIZZLE,
+                                NON_PREMULTIPLIED_ALPHA, false, false, false));
+    TestShader(ProgramKey::Tile(precision, sampler, USE_AA, DO_SWIZZLE,
+                                NON_PREMULTIPLIED_ALPHA, false, false, false));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, NO_SWIZZLE,
+                                NON_PREMULTIPLIED_ALPHA, false, false, false));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, DO_SWIZZLE,
+                                NON_PREMULTIPLIED_ALPHA, false, false, false));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, NO_SWIZZLE,
+                                NON_PREMULTIPLIED_ALPHA, true, false, false));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, DO_SWIZZLE,
+                                NON_PREMULTIPLIED_ALPHA, true, false, false));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, NO_SWIZZLE,
+                                NON_PREMULTIPLIED_ALPHA, false, true, false));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, DO_SWIZZLE,
+                                NON_PREMULTIPLIED_ALPHA, false, true, false));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, NO_SWIZZLE,
+                                NON_PREMULTIPLIED_ALPHA, true, true, false));
+    TestShader(ProgramKey::Tile(precision, sampler, NO_AA, DO_SWIZZLE,
+                                NON_PREMULTIPLIED_ALPHA, true, true, false));
 
     // Iterate over alpha plane, nv12, and color_lut parameters.
     UVTextureMode uv_modes[2] = {UV_TEXTURE_MODE_UV, UV_TEXTURE_MODE_U_V};
diff --git a/components/viz/service/display/program_binding.cc b/components/viz/service/display/program_binding.cc
index 742c7a2..9436dc3 100644
--- a/components/viz/service/display/program_binding.cc
+++ b/components/viz/service/display/program_binding.cc
@@ -63,6 +63,7 @@
                             SamplerType sampler,
                             AAMode aa_mode,
                             SwizzleMode swizzle_mode,
+                            PremultipliedAlphaMode premultiplied_alpha,
                             bool is_opaque,
                             bool has_tex_clamp_rect,
                             bool tint_color) {
@@ -75,6 +76,7 @@
   result.is_opaque_ = is_opaque;
   result.has_tex_clamp_rect_ = has_tex_clamp_rect;
   result.has_tint_color_matrix_ = tint_color;
+  result.premultiplied_alpha_ = premultiplied_alpha;
   return result;
 }
 
diff --git a/components/viz/service/display/program_binding.h b/components/viz/service/display/program_binding.h
index 42fed4d..8b28c57 100644
--- a/components/viz/service/display/program_binding.h
+++ b/components/viz/service/display/program_binding.h
@@ -82,6 +82,7 @@
                          SamplerType sampler,
                          AAMode aa_mode,
                          SwizzleMode swizzle_mode,
+                         PremultipliedAlphaMode premultiplied_alpha,
                          bool is_opaque,
                          bool has_tex_clamp_rect,
                          bool tint_color);
diff --git a/components/viz/service/display/renderer_pixeltest.cc b/components/viz/service/display/renderer_pixeltest.cc
index 7fa21dd..e7c3158 100644
--- a/components/viz/service/display/renderer_pixeltest.cc
+++ b/components/viz/service/display/renderer_pixeltest.cc
@@ -2801,6 +2801,7 @@
       CreateTestRenderPass(id, rect, transform_to_root);
 
   bool swizzle_contents = true;
+  bool contents_premultiplied = true;
   bool needs_blending = false;
   bool nearest_neighbor = true;
   bool force_anti_aliasing_off = true;
@@ -2813,7 +2814,8 @@
   TileDrawQuad* hole = pass->CreateAndAppendDrawQuad<TileDrawQuad>();
   hole->SetNew(hole_shared_state, rect, rect, needs_blending, mapped_resource,
                gfx::RectF(gfx::Rect(tile_size)), tile_size, swizzle_contents,
-               nearest_neighbor, force_anti_aliasing_off);
+               contents_premultiplied, nearest_neighbor,
+               force_anti_aliasing_off);
 
   gfx::Transform green_quad_to_target_transform;
   SharedQuadState* green_shared_state = CreateTestSharedQuadState(
@@ -3196,6 +3198,7 @@
 TYPED_TEST(RendererPixelTest, TileDrawQuadNearestNeighbor) {
   gfx::Rect viewport(this->device_viewport_size_);
   bool swizzle_contents = true;
+  bool contents_premultiplied = true;
   bool needs_blending = true;
   bool nearest_neighbor = true;
   bool force_anti_aliasing_off = false;
@@ -3238,7 +3241,8 @@
   auto* quad = pass->CreateAndAppendDrawQuad<TileDrawQuad>();
   quad->SetNew(shared_state, viewport, viewport, needs_blending,
                mapped_resource, gfx::RectF(gfx::Rect(tile_size)), tile_size,
-               swizzle_contents, nearest_neighbor, force_anti_aliasing_off);
+               swizzle_contents, contents_premultiplied, nearest_neighbor,
+               force_anti_aliasing_off);
 
   RenderPassList pass_list;
   pass_list.push_back(std::move(pass));
@@ -3726,6 +3730,7 @@
 TEST_F(GLRendererPixelTest, TileQuadClamping) {
   gfx::Rect viewport(this->device_viewport_size_);
   bool swizzle_contents = true;
+  bool contents_premultiplied = true;
   bool needs_blending = true;
   bool nearest_neighbor = false;
   bool use_aa = false;
@@ -3779,7 +3784,8 @@
   auto* quad = pass->CreateAndAppendDrawQuad<TileDrawQuad>();
   quad->SetNew(quad_shared, gfx::Rect(layer_size), gfx::Rect(layer_size),
                needs_blending, mapped_resource, tex_coord_rect, tile_size,
-               swizzle_contents, nearest_neighbor, use_aa);
+               swizzle_contents, contents_premultiplied, nearest_neighbor,
+               use_aa);
 
   // Green background.
   SharedQuadState* background_shared =
diff --git a/components/viz/service/display/software_renderer_unittest.cc b/components/viz/service/display/software_renderer_unittest.cc
index 6f4f4e0f..899bc24c 100644
--- a/components/viz/service/display/software_renderer_unittest.cc
+++ b/components/viz/service/display/software_renderer_unittest.cc
@@ -198,11 +198,11 @@
   auto* inner_quad = root_render_pass->CreateAndAppendDrawQuad<TileDrawQuad>();
   inner_quad->SetNew(shared_quad_state, inner_rect, inner_rect, needs_blending,
                      mapped_resource_cyan, gfx::RectF(gfx::SizeF(inner_size)),
-                     inner_size, false, false, false);
+                     inner_size, false, false, false, false);
   auto* outer_quad = root_render_pass->CreateAndAppendDrawQuad<TileDrawQuad>();
   outer_quad->SetNew(shared_quad_state, outer_rect, outer_rect, needs_blending,
                      mapped_resource_yellow, gfx::RectF(gfx::SizeF(outer_size)),
-                     outer_size, false, false, false);
+                     outer_size, false, false, false, false);
 
   RenderPassList list;
   list.push_back(std::move(root_render_pass));
@@ -260,7 +260,7 @@
   auto* quad = root_render_pass->CreateAndAppendDrawQuad<TileDrawQuad>();
   quad->SetNew(shared_quad_state, tile_rect, tile_rect, needs_blending,
                mapped_resource_cyan, gfx::RectF(gfx::SizeF(tile_size)),
-               tile_size, false, false, false);
+               tile_size, false, false, false, false);
   quad->visible_rect = visible_rect;
 
   RenderPassList list;
diff --git a/components/viz/test/data/blue_yellow_flipped.png b/components/viz/test/data/blue_yellow_flipped.png
index 04d17b6..cd5259fa 100644
--- a/components/viz/test/data/blue_yellow_flipped.png
+++ b/components/viz/test/data/blue_yellow_flipped.png
Binary files differ
diff --git a/components/viz/test/data/blue_yellow_flipped_dither.png b/components/viz/test/data/blue_yellow_flipped_dither.png
new file mode 100644
index 0000000..0908ffe
--- /dev/null
+++ b/components/viz/test/data/blue_yellow_flipped_dither.png
Binary files differ
diff --git a/components/viz/test/data/blue_yellow_partial_flipped.png b/components/viz/test/data/blue_yellow_partial_flipped.png
index 60648b7..1a127847 100644
--- a/components/viz/test/data/blue_yellow_partial_flipped.png
+++ b/components/viz/test/data/blue_yellow_partial_flipped.png
Binary files differ
diff --git a/components/viz/test/data/blue_yellow_partial_flipped_dither.png b/components/viz/test/data/blue_yellow_partial_flipped_dither.png
new file mode 100644
index 0000000..1edf658
--- /dev/null
+++ b/components/viz/test/data/blue_yellow_partial_flipped_dither.png
Binary files differ
diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc
index c819f5b..1e882a24 100644
--- a/content/renderer/gpu/render_widget_compositor.cc
+++ b/content/renderer/gpu/render_widget_compositor.cc
@@ -553,6 +553,11 @@
         base::SysInfo::AmountOfPhysicalMemoryMB() <= 512 &&
         !using_synchronous_compositor) {
       settings.preferred_tile_format = viz::RGBA_4444;
+      // We need to allocate an additional RGBA_8888 intermediate for each tile
+      // rasterization when rastering to RGBA_4444 to allow for dithering.
+      // Setting a reasonable sized max tile size allows this intermediate to
+      // be consistently reused.
+      settings.max_gpu_raster_tile_size = gfx::Size(512, 256);
     }
   }
 
diff --git a/services/viz/public/cpp/compositing/quads_struct_traits.cc b/services/viz/public/cpp/compositing/quads_struct_traits.cc
index 412c5d3..d7967fc 100644
--- a/services/viz/public/cpp/compositing/quads_struct_traits.cc
+++ b/services/viz/public/cpp/compositing/quads_struct_traits.cc
@@ -158,6 +158,7 @@
   }
 
   quad->swizzle_contents = data.swizzle_contents();
+  quad->is_premultiplied = data.is_premultiplied();
   quad->nearest_neighbor = data.nearest_neighbor();
   quad->force_anti_aliasing_off = data.force_anti_aliasing_off();
   quad->resources.ids[viz::TileDrawQuad::kResourceIdIndex] = data.resource_id();
diff --git a/services/viz/public/cpp/compositing/quads_struct_traits.h b/services/viz/public/cpp/compositing/quads_struct_traits.h
index c7b07abc..c5fd0ec9 100644
--- a/services/viz/public/cpp/compositing/quads_struct_traits.h
+++ b/services/viz/public/cpp/compositing/quads_struct_traits.h
@@ -350,6 +350,11 @@
     return quad->swizzle_contents;
   }
 
+  static bool is_premultiplied(const viz::DrawQuad& input) {
+    const viz::TileDrawQuad* quad = viz::TileDrawQuad::MaterialCast(&input);
+    return quad->is_premultiplied;
+  }
+
   static bool nearest_neighbor(const viz::DrawQuad& input) {
     const viz::TileDrawQuad* quad = viz::TileDrawQuad::MaterialCast(&input);
     return quad->nearest_neighbor;
diff --git a/services/viz/public/interfaces/compositing/quads.mojom b/services/viz/public/interfaces/compositing/quads.mojom
index a8b9bc1..39dcb53 100644
--- a/services/viz/public/interfaces/compositing/quads.mojom
+++ b/services/viz/public/interfaces/compositing/quads.mojom
@@ -78,6 +78,7 @@
   gfx.mojom.RectF tex_coord_rect;
   gfx.mojom.Size texture_size;
   bool swizzle_contents;
+  bool is_premultiplied;
   uint32 resource_id;
   bool nearest_neighbor;
   bool force_anti_aliasing_off;