[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/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.