diff options
author | rosca <rosca@adobe.com> | 2014-10-22 02:54:12 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-10-22 09:55:42 +0000 |
commit | 7b73f83a1d6c212c3f4bc915f495baf1b4671dbf (patch) | |
tree | b5aab8594d131c702f314455b03c89b2d7396c14 | |
parent | 547c1dd78570986435140b5f632ef927fd5168f8 (diff) | |
download | chromium_src-7b73f83a1d6c212c3f4bc915f495baf1b4671dbf.zip chromium_src-7b73f83a1d6c212c3f4bc915f495baf1b4671dbf.tar.gz chromium_src-7b73f83a1d6c212c3f4bc915f495baf1b4671dbf.tar.bz2 |
Implement mix-blend-mode in GL renderer using shaders.
This implementation brings in several improvements for blending:
- it's done faster, due to reducing the number of readbacks.
- the backdrop doesn't suffer any transformation, so the result is more
correct.
- the results are similar to the ones obtained using the software paths.
- blending is always applied after filters, even when the filters
can be expressed using a color matrix.
The initial experiment: https://codereview.chromium.org/555133002/.
BUG=243223
Review URL: https://codereview.chromium.org/658483003
Cr-Commit-Position: refs/heads/master@{#300657}
-rw-r--r-- | cc/layers/delegated_renderer_layer_impl_unittest.cc | 7 | ||||
-rw-r--r-- | cc/output/gl_renderer.cc | 533 | ||||
-rw-r--r-- | cc/output/gl_renderer.h | 55 | ||||
-rw-r--r-- | cc/output/gl_renderer_unittest.cc | 493 | ||||
-rw-r--r-- | cc/output/program_binding.h | 5 | ||||
-rw-r--r-- | cc/output/shader.cc | 373 | ||||
-rw-r--r-- | cc/output/shader.h | 78 | ||||
-rw-r--r-- | cc/output/software_renderer.cc | 6 | ||||
-rw-r--r-- | cc/test/data/background_filter_blur_off_axis.png | bin | 7289 -> 7304 bytes | |||
-rw-r--r-- | cc/test/data/blending_render_pass.png | bin | 0 -> 2765 bytes | |||
-rw-r--r-- | cc/test/data/blending_render_pass_cm.png | bin | 0 -> 2876 bytes | |||
-rw-r--r-- | cc/test/data/blending_render_pass_mask.png | bin | 0 -> 3568 bytes | |||
-rw-r--r-- | cc/test/data/blending_render_pass_mask_cm.png | bin | 0 -> 3719 bytes | |||
-rw-r--r-- | cc/test/render_pass_test_utils.cc | 5 | ||||
-rw-r--r-- | cc/test/render_pass_test_utils.h | 3 | ||||
-rw-r--r-- | cc/trees/layer_tree_host_pixeltest_blending.cc | 311 | ||||
-rw-r--r-- | cc/trees/layer_tree_settings.cc | 1 | ||||
-rw-r--r-- | cc/trees/layer_tree_settings.h | 1 |
18 files changed, 1346 insertions, 525 deletions
diff --git a/cc/layers/delegated_renderer_layer_impl_unittest.cc b/cc/layers/delegated_renderer_layer_impl_unittest.cc index e66cc766..3fb1c48 100644 --- a/cc/layers/delegated_renderer_layer_impl_unittest.cc +++ b/cc/layers/delegated_renderer_layer_impl_unittest.cc @@ -1416,7 +1416,12 @@ TEST_F(DelegatedRendererLayerImplTest, Occlusion) { pass1_id, gfx::Rect(layer_size), gfx::Transform()); - AddRenderPassQuad(pass1, pass2, 0, FilterOperations(), transform); + AddRenderPassQuad(pass1, + pass2, + 0, + FilterOperations(), + transform, + SkXfermode::kSrcOver_Mode); delegated_renderer_layer_impl->SetFrameDataForRenderPasses( 1.f, &delegated_render_passes); diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc index 61d233a..3c6bbfe 100644 --- a/cc/output/gl_renderer.cc +++ b/cc/output/gl_renderer.cc @@ -124,6 +124,44 @@ SamplerType SamplerTypeFromTextureTarget(GLenum target) { } } +BlendMode BlendModeFromSkXfermode(SkXfermode::Mode mode) { + switch (mode) { + case SkXfermode::kSrcOver_Mode: + return BlendModeNormal; + case SkXfermode::kOverlay_Mode: + return BlendModeOverlay; + case SkXfermode::kDarken_Mode: + return BlendModeDarken; + case SkXfermode::kLighten_Mode: + return BlendModeLighten; + case SkXfermode::kColorDodge_Mode: + return BlendModeColorDodge; + case SkXfermode::kColorBurn_Mode: + return BlendModeColorBurn; + case SkXfermode::kHardLight_Mode: + return BlendModeHardLight; + case SkXfermode::kSoftLight_Mode: + return BlendModeSoftLight; + case SkXfermode::kDifference_Mode: + return BlendModeDifference; + case SkXfermode::kExclusion_Mode: + return BlendModeExclusion; + case SkXfermode::kMultiply_Mode: + return BlendModeMultiply; + case SkXfermode::kHue_Mode: + return BlendModeHue; + case SkXfermode::kSaturation_Mode: + return BlendModeSaturation; + case SkXfermode::kColor_Mode: + return BlendModeColor; + case SkXfermode::kLuminosity_Mode: + return BlendModeLuminosity; + default: + NOTREACHED(); + return BlendModeNormal; + } +} + // Smallest unit that impact anti-aliasing output. We use this to // determine when anti-aliasing is unnecessary. const float kAntiAliasingEpsilon = 1.0f / 1024.0f; @@ -724,140 +762,6 @@ void GLRenderer::RestoreBlendFuncToDefault(SkXfermode::Mode blend_mode) { GLC(gl_, gl_->BlendEquation(GL_FUNC_ADD)); } -static skia::RefPtr<SkImage> ApplyBlendModeWithBackdrop( - scoped_ptr<GLRenderer::ScopedUseGrContext> use_gr_context, - ResourceProvider* resource_provider, - skia::RefPtr<SkImage> source_bitmap_with_filters, - ScopedResource* source_texture_resource, - ScopedResource* background_texture_resource, - SkXfermode::Mode blend_mode) { - if (!use_gr_context) - return source_bitmap_with_filters; - - DCHECK(background_texture_resource); - DCHECK(source_texture_resource); - - gfx::Size source_size = source_texture_resource->size(); - gfx::Size background_size = background_texture_resource->size(); - - DCHECK_LE(background_size.width(), source_size.width()); - DCHECK_LE(background_size.height(), source_size.height()); - - int source_texture_with_filters_id; - scoped_ptr<ResourceProvider::ScopedReadLockGL> lock; - if (source_bitmap_with_filters) { - DCHECK_EQ(source_size.width(), source_bitmap_with_filters->width()); - DCHECK_EQ(source_size.height(), source_bitmap_with_filters->height()); - GrTexture* texture = - reinterpret_cast<GrTexture*>(source_bitmap_with_filters->getTexture()); - source_texture_with_filters_id = texture->getTextureHandle(); - } else { - lock.reset(new ResourceProvider::ScopedReadLockGL( - resource_provider, source_texture_resource->id())); - source_texture_with_filters_id = lock->texture_id(); - } - - ResourceProvider::ScopedReadLockGL lock_background( - resource_provider, background_texture_resource->id()); - - // Wrap the source texture in a Ganesh platform texture. - GrBackendTextureDesc backend_texture_description; - backend_texture_description.fConfig = kSkia8888_GrPixelConfig; - backend_texture_description.fOrigin = kBottomLeft_GrSurfaceOrigin; - - backend_texture_description.fWidth = source_size.width(); - backend_texture_description.fHeight = source_size.height(); - backend_texture_description.fTextureHandle = source_texture_with_filters_id; - skia::RefPtr<GrTexture> source_texture = - skia::AdoptRef(use_gr_context->context()->wrapBackendTexture( - backend_texture_description)); - if (!source_texture) { - TRACE_EVENT_INSTANT0( - "cc", - "ApplyBlendModeWithBackdrop wrap source texture failed", - TRACE_EVENT_SCOPE_THREAD); - return skia::RefPtr<SkImage>(); - } - - backend_texture_description.fWidth = background_size.width(); - backend_texture_description.fHeight = background_size.height(); - backend_texture_description.fTextureHandle = lock_background.texture_id(); - skia::RefPtr<GrTexture> background_texture = - skia::AdoptRef(use_gr_context->context()->wrapBackendTexture( - backend_texture_description)); - if (!background_texture) { - TRACE_EVENT_INSTANT0( - "cc", - "ApplyBlendModeWithBackdrop wrap background texture failed", - TRACE_EVENT_SCOPE_THREAD); - return skia::RefPtr<SkImage>(); - } - - SkImageInfo source_info = - SkImageInfo::MakeN32Premul(source_size.width(), source_size.height()); - // Place the platform texture inside an SkBitmap. - SkBitmap source; - source.setInfo(source_info); - skia::RefPtr<SkGrPixelRef> source_pixel_ref = - skia::AdoptRef(new SkGrPixelRef(source_info, source_texture.get())); - source.setPixelRef(source_pixel_ref.get()); - - SkImageInfo background_info = SkImageInfo::MakeN32Premul( - background_size.width(), background_size.height()); - - SkBitmap background; - background.setInfo(background_info); - skia::RefPtr<SkGrPixelRef> background_pixel_ref = - skia::AdoptRef(new SkGrPixelRef( - background_info, background_texture.get())); - background.setPixelRef(background_pixel_ref.get()); - - // Create a scratch texture for backing store. - GrTextureDesc desc; - desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit; - desc.fSampleCnt = 0; - desc.fWidth = source.width(); - desc.fHeight = source.height(); - desc.fConfig = kSkia8888_GrPixelConfig; - desc.fOrigin = kBottomLeft_GrSurfaceOrigin; - skia::RefPtr<GrTexture> backing_store = - skia::AdoptRef(use_gr_context->context()->refScratchTexture( - desc, GrContext::kExact_ScratchTexMatch)); - if (!backing_store) { - TRACE_EVENT_INSTANT0( - "cc", - "ApplyBlendModeWithBackdrop scratch texture allocation failed", - TRACE_EVENT_SCOPE_THREAD); - return source_bitmap_with_filters; - } - - // Create a device and canvas using that backing store. - skia::RefPtr<SkSurface> surface = skia::AdoptRef( - SkSurface::NewRenderTargetDirect(backing_store->asRenderTarget())); - if (!surface) - return skia::RefPtr<SkImage>(); - skia::RefPtr<SkCanvas> canvas = skia::SharePtr(surface->getCanvas()); - - // Draw the source bitmap through the filter to the canvas. - canvas->clear(SK_ColorTRANSPARENT); - canvas->drawSprite(background, 0, 0); - SkPaint paint; - paint.setXfermodeMode(blend_mode); - canvas->drawSprite(source, 0, 0, &paint); - - skia::RefPtr<SkImage> image = skia::AdoptRef(surface->newImageSnapshot()); - if (!image || !image->getTexture()) { - return skia::RefPtr<SkImage>(); - } - - // Flush the GrContext to ensure all buffered GL calls are drawn to the - // backing store before we access and return it, and have cc begin using the - // GL context again. - canvas->flush(); - - return image; -} - bool GLRenderer::ShouldApplyBackgroundFilters(DrawingFrame* frame, const RenderPassDrawQuad* quad) { if (quad->background_filters.IsEmpty()) @@ -879,7 +783,8 @@ bool GLRenderer::ShouldApplyBackgroundFilters(DrawingFrame* frame, gfx::Rect GLRenderer::GetBackdropBoundingBoxForRenderPassQuad( DrawingFrame* frame, const RenderPassDrawQuad* quad, - const gfx::Transform& contents_device_transform) { + const gfx::Transform& contents_device_transform, + bool use_aa) { gfx::Rect backdrop_rect = gfx::ToEnclosingRect(MathUtil::MapClippedRect( contents_device_transform, SharedGeometryQuad().BoundingBox())); @@ -889,6 +794,11 @@ gfx::Rect GLRenderer::GetBackdropBoundingBoxForRenderPassQuad( backdrop_rect.Inset(-left, -top, -right, -bottom); } + if (!backdrop_rect.IsEmpty() && use_aa) { + const int kOutsetForAntialiasing = 1; + backdrop_rect.Inset(-kOutsetForAntialiasing, -kOutsetForAntialiasing); + } + backdrop_rect.Intersect( MoveFromDrawToWindowSpace(frame->current_render_pass->output_rect)); return backdrop_rect; @@ -933,7 +843,6 @@ GLRenderer::ApplyInverseTransformForBackgroundFilters( DrawingFrame* frame, const RenderPassDrawQuad* quad, const gfx::Transform& contents_device_transform_inverse, - ScopedResource* device_background_texture, skia::RefPtr<SkImage> filtered_device_background, const gfx::Rect& backdrop_bounding_rect) { // This method draws a background filter, which applies a filter to any pixels @@ -960,18 +869,9 @@ GLRenderer::ApplyInverseTransformForBackgroundFilters( // TODO(danakj): When this algorithm changes, update // LayerTreeHost::PrioritizeTextures() accordingly. - DCHECK(device_background_texture); + DCHECK(filtered_device_background); - int filtered_device_background_texture_id = 0; - scoped_ptr<ResourceProvider::ScopedReadLockGL> lock; - if (filtered_device_background) { - GrTexture* texture = filtered_device_background->getTexture(); - filtered_device_background_texture_id = texture->getTextureHandle(); - } else { - lock.reset(new ResourceProvider::ScopedReadLockGL( - resource_provider_, device_background_texture->id())); - filtered_device_background_texture_id = lock->texture_id(); - } + GrTexture* texture = filtered_device_background->getTexture(); scoped_ptr<ScopedResource> background_texture = ScopedResource::Create(resource_provider_); @@ -1005,7 +905,7 @@ GLRenderer::ApplyInverseTransformForBackgroundFilters( bool flip_vertically = false; CopyTextureToFramebuffer(frame, - filtered_device_background_texture_id, + texture->getTextureHandle(), backdrop_bounding_rect, device_to_framebuffer_transform, flip_vertically); @@ -1021,9 +921,9 @@ GLRenderer::ApplyInverseTransformForBackgroundFilters( void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, const RenderPassDrawQuad* quad) { SkXfermode::Mode blend_mode = quad->shared_quad_state->blend_mode; - SetBlendEnabled(quad->ShouldDrawWithBlending() || - (!IsDefaultBlendMode(blend_mode) && - CanApplyBlendModeUsingBlendFunc(blend_mode))); + SetBlendEnabled( + CanApplyBlendModeUsingBlendFunc(blend_mode) && + (quad->ShouldDrawWithBlending() || !IsDefaultBlendMode(blend_mode))); ScopedResource* contents_texture = render_pass_textures_.get(quad->render_pass_id); @@ -1042,44 +942,70 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, if (!contents_device_transform.GetInverse(&contents_device_transform_inverse)) return; + bool clipped = false; + gfx::QuadF device_quad = MathUtil::MapQuad( + contents_device_transform, SharedGeometryQuad(), &clipped); + // Use anti-aliasing programs only when necessary. + bool use_aa = + !clipped && + (settings_->force_antialiasing || !device_quad.IsRectilinear() || + !gfx::IsNearestRectWithinDistance(device_quad.BoundingBox(), + kAntiAliasingEpsilon)); + bool need_background_texture = !CanApplyBlendModeUsingBlendFunc(blend_mode) || ShouldApplyBackgroundFilters(frame, quad); scoped_ptr<ScopedResource> background_texture; + skia::RefPtr<SkImage> background_image; + gfx::Rect background_rect; if (need_background_texture) { + // Compute a bounding box around the pixels that will be visible through + // the quad. + background_rect = GetBackdropBoundingBoxForRenderPassQuad( + frame, quad, contents_device_transform, use_aa); + } + + if (!background_rect.IsEmpty()) { // The pixels from the filtered background should completely replace the // current pixel values. bool disable_blending = blend_enabled(); if (disable_blending) SetBlendEnabled(false); - // Compute a bounding box around the pixels that will be visible through - // the quad. - gfx::Rect backdrop_rect = GetBackdropBoundingBoxForRenderPassQuad( - frame, quad, contents_device_transform); - // Read the pixels in the bounding box into a buffer R. scoped_ptr<ScopedResource> scoped_background_texture = - GetBackdropTexture(backdrop_rect); + GetBackdropTexture(background_rect); skia::RefPtr<SkImage> background_with_filters; - if (ShouldApplyBackgroundFilters(frame, quad)) { + if (ShouldApplyBackgroundFilters(frame, quad) && + scoped_background_texture) { // Apply the background filters to R, so that it is applied in the pixels' // coordinate space. background_with_filters = ApplyBackgroundFilters(frame, quad, scoped_background_texture.get()); } - // Apply the quad's inverse transform to map the pixels in R into the - // quad's content space. This implicitly clips R by the content bounds of - // the quad since the destination texture has bounds matching the quad's - // content. - background_texture = ApplyInverseTransformForBackgroundFilters( - frame, - quad, - contents_device_transform_inverse, - scoped_background_texture.get(), - background_with_filters, - backdrop_rect); + + if (CanApplyBlendModeUsingBlendFunc(blend_mode) && + background_with_filters) { + // The background with filters will be copied to the frame buffer. + // Apply the quad's inverse transform to map the pixels in R into the + // quad's content space. This implicitly clips R by the content bounds of + // the quad since the destination texture has bounds matching the quad's + // content. + background_texture = ApplyInverseTransformForBackgroundFilters( + frame, + quad, + contents_device_transform_inverse, + background_with_filters, + background_rect); + } else if (!CanApplyBlendModeUsingBlendFunc(blend_mode)) { + if (background_with_filters) { + // The background with filters will be used as backdrop for blending. + background_image = background_with_filters; + } else { + background_texture = scoped_background_texture.Pass(); + } + } if (disable_blending) SetBlendEnabled(true); @@ -1117,49 +1043,27 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, } } - if (background_texture) { - if (CanApplyBlendModeUsingBlendFunc(blend_mode)) { - // Draw the background texture if it has some filters applied. - DCHECK(ShouldApplyBackgroundFilters(frame, quad)); - DCHECK(background_texture->size() == quad->rect.size()); - ResourceProvider::ScopedReadLockGL lock(resource_provider_, - background_texture->id()); - - // The background_texture is oriented the same as the frame buffer. The - // transform we are copying with has a vertical flip, so flip the contents - // in the shader to maintain orientation - bool flip_vertically = true; - - CopyTextureToFramebuffer(frame, - lock.texture_id(), - quad->rect, - quad->quadTransform(), - flip_vertically); - } else { - // If blending is applied using shaders, the background texture with - // filters will be used as backdrop for blending operation, so we don't - // need to copy it to the frame buffer. - filter_image = - ApplyBlendModeWithBackdrop(ScopedUseGrContext::Create(this, frame), - resource_provider_, - filter_image, - contents_texture, - background_texture.get(), - quad->shared_quad_state->blend_mode); - } + if (background_texture && ShouldApplyBackgroundFilters(frame, quad)) { + // Draw the background texture if it has some filters applied. + DCHECK(CanApplyBlendModeUsingBlendFunc(blend_mode)); + DCHECK(background_texture->size() == quad->rect.size()); + ResourceProvider::ScopedReadLockGL lock(resource_provider_, + background_texture->id()); + + // The background_texture is oriented the same as the frame buffer. The + // transform we are copying with has a vertical flip, so flip the contents + // in the shader to maintain orientation + bool flip_vertically = true; + + CopyTextureToFramebuffer(frame, + lock.texture_id(), + quad->rect, + quad->quadTransform(), + flip_vertically); } - bool clipped = false; - gfx::QuadF device_quad = MathUtil::MapQuad( - contents_device_transform, SharedGeometryQuad(), &clipped); LayerQuad device_layer_bounds(gfx::QuadF(device_quad.BoundingBox())); LayerQuad device_layer_edges(device_quad); - - // Use anti-aliasing programs only when necessary. - bool use_aa = - !clipped && (!device_quad.IsRectilinear() || - !gfx::IsNearestRectWithinDistance(device_quad.BoundingBox(), - kAntiAliasingEpsilon)); if (use_aa) { device_layer_bounds.InflateAntiAliasingDistance(); device_layer_edges.InflateAntiAliasingDistance(); @@ -1211,10 +1115,17 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, int shader_color_matrix_location = -1; int shader_color_offset_location = -1; int shader_tex_transform_location = -1; + int shader_backdrop_location = -1; + int shader_backdrop_rect_location = -1; + + BlendMode shader_blend_mode = ((background_texture || background_image) && + !CanApplyBlendModeUsingBlendFunc(blend_mode)) + ? BlendModeFromSkXfermode(blend_mode) + : BlendModeNormal; if (use_aa && mask_texture_id && !use_color_matrix) { const RenderPassMaskProgramAA* program = - GetRenderPassMaskProgramAA(tex_coord_precision); + GetRenderPassMaskProgramAA(tex_coord_precision, shader_blend_mode); SetUseProgram(program->program()); GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); @@ -1231,9 +1142,12 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, shader_alpha_location = program->fragment_shader().alpha_location(); shader_tex_transform_location = program->vertex_shader().tex_transform_location(); + shader_backdrop_location = program->fragment_shader().backdrop_location(); + shader_backdrop_rect_location = + program->fragment_shader().backdrop_rect_location(); } else if (!use_aa && mask_texture_id && !use_color_matrix) { const RenderPassMaskProgram* program = - GetRenderPassMaskProgram(tex_coord_precision); + GetRenderPassMaskProgram(tex_coord_precision, shader_blend_mode); SetUseProgram(program->program()); GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); @@ -1247,9 +1161,12 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, shader_alpha_location = program->fragment_shader().alpha_location(); shader_tex_transform_location = program->vertex_shader().tex_transform_location(); + shader_backdrop_location = program->fragment_shader().backdrop_location(); + shader_backdrop_rect_location = + program->fragment_shader().backdrop_rect_location(); } else if (use_aa && !mask_texture_id && !use_color_matrix) { const RenderPassProgramAA* program = - GetRenderPassProgramAA(tex_coord_precision); + GetRenderPassProgramAA(tex_coord_precision, shader_blend_mode); SetUseProgram(program->program()); GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); @@ -1260,9 +1177,13 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, shader_alpha_location = program->fragment_shader().alpha_location(); shader_tex_transform_location = program->vertex_shader().tex_transform_location(); + shader_backdrop_location = program->fragment_shader().backdrop_location(); + shader_backdrop_rect_location = + program->fragment_shader().backdrop_rect_location(); } else if (use_aa && mask_texture_id && use_color_matrix) { const RenderPassMaskColorMatrixProgramAA* program = - GetRenderPassMaskColorMatrixProgramAA(tex_coord_precision); + GetRenderPassMaskColorMatrixProgramAA(tex_coord_precision, + shader_blend_mode); SetUseProgram(program->program()); GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); @@ -1283,9 +1204,13 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, program->fragment_shader().color_matrix_location(); shader_color_offset_location = program->fragment_shader().color_offset_location(); + shader_backdrop_location = program->fragment_shader().backdrop_location(); + shader_backdrop_rect_location = + program->fragment_shader().backdrop_rect_location(); } else if (use_aa && !mask_texture_id && use_color_matrix) { const RenderPassColorMatrixProgramAA* program = - GetRenderPassColorMatrixProgramAA(tex_coord_precision); + GetRenderPassColorMatrixProgramAA(tex_coord_precision, + shader_blend_mode); SetUseProgram(program->program()); GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); @@ -1300,9 +1225,13 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, program->fragment_shader().color_matrix_location(); shader_color_offset_location = program->fragment_shader().color_offset_location(); + shader_backdrop_location = program->fragment_shader().backdrop_location(); + shader_backdrop_rect_location = + program->fragment_shader().backdrop_rect_location(); } else if (!use_aa && mask_texture_id && use_color_matrix) { const RenderPassMaskColorMatrixProgram* program = - GetRenderPassMaskColorMatrixProgram(tex_coord_precision); + GetRenderPassMaskColorMatrixProgram(tex_coord_precision, + shader_blend_mode); SetUseProgram(program->program()); GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); @@ -1320,9 +1249,12 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, program->fragment_shader().color_matrix_location(); shader_color_offset_location = program->fragment_shader().color_offset_location(); + shader_backdrop_location = program->fragment_shader().backdrop_location(); + shader_backdrop_rect_location = + program->fragment_shader().backdrop_rect_location(); } else if (!use_aa && !mask_texture_id && use_color_matrix) { const RenderPassColorMatrixProgram* program = - GetRenderPassColorMatrixProgram(tex_coord_precision); + GetRenderPassColorMatrixProgram(tex_coord_precision, shader_blend_mode); SetUseProgram(program->program()); GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); @@ -1334,9 +1266,12 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, program->fragment_shader().color_matrix_location(); shader_color_offset_location = program->fragment_shader().color_offset_location(); + shader_backdrop_location = program->fragment_shader().backdrop_location(); + shader_backdrop_rect_location = + program->fragment_shader().backdrop_rect_location(); } else { const RenderPassProgram* program = - GetRenderPassProgram(tex_coord_precision); + GetRenderPassProgram(tex_coord_precision, shader_blend_mode); SetUseProgram(program->program()); GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); @@ -1344,6 +1279,9 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, shader_alpha_location = program->fragment_shader().alpha_location(); shader_tex_transform_location = program->vertex_shader().tex_transform_location(); + shader_backdrop_location = program->fragment_shader().backdrop_location(); + shader_backdrop_rect_location = + program->fragment_shader().backdrop_rect_location(); } float tex_scale_x = quad->rect.width() / static_cast<float>(contents_texture->size().width()); @@ -1363,6 +1301,7 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, tex_scale_x, -tex_scale_y)); + GLint last_texture_unit = 0; scoped_ptr<ResourceProvider::ScopedSamplerGL> shader_mask_sampler_lock; if (shader_mask_sampler_location != -1) { DCHECK_NE(shader_mask_tex_coord_scale_location, 1); @@ -1383,6 +1322,8 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, gl_->Uniform2f(shader_mask_tex_coord_scale_location, mask_uv_rect.width() / tex_scale_x, -mask_uv_rect.height() / tex_scale_y)); + + last_texture_unit = 1; } if (shader_edge_location != -1) { @@ -1418,6 +1359,37 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, GLC(gl_, gl_->Uniform4fv(shader_color_offset_location, 1, offset)); } + scoped_ptr<ResourceProvider::ScopedSamplerGL> shader_background_sampler_lock; + if (shader_backdrop_location != -1) { + DCHECK(background_texture || background_image); + DCHECK_NE(shader_backdrop_location, 0); + DCHECK_NE(shader_backdrop_rect_location, 0); + + GLC(gl_, gl_->Uniform1i(shader_backdrop_location, ++last_texture_unit)); + + GLC(gl_, + gl_->Uniform4f(shader_backdrop_rect_location, + background_rect.x(), + background_rect.y(), + background_rect.width(), + background_rect.height())); + + if (background_image) { + GrTexture* texture = background_image->getTexture(); + GLC(gl_, gl_->ActiveTexture(GL_TEXTURE0 + last_texture_unit)); + gl_->BindTexture(GL_TEXTURE_2D, texture->getTextureHandle()); + GLC(gl_, gl_->ActiveTexture(GL_TEXTURE0)); + } else { + shader_background_sampler_lock = make_scoped_ptr( + new ResourceProvider::ScopedSamplerGL(resource_provider_, + background_texture->id(), + GL_TEXTURE0 + last_texture_unit, + GL_LINEAR)); + DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), + shader_background_sampler_lock->target()); + } + } + // Map device space quad to surface space. contents_device_transform has no 3d // component since it was flattened, so we don't need to project. gfx::QuadF surface_quad = MathUtil::MapQuad(contents_device_transform_inverse, @@ -2353,7 +2325,8 @@ void GLRenderer::CopyTextureToFramebuffer(const DrawingFrame* frame, TexCoordPrecision tex_coord_precision = TexCoordPrecisionRequired( gl_, &highp_threshold_cache_, highp_threshold_min_, rect.bottom_right()); - const RenderPassProgram* program = GetRenderPassProgram(tex_coord_precision); + const RenderPassProgram* program = + GetRenderPassProgram(tex_coord_precision, BlendModeNormal); SetUseProgram(program->program()); GLC(gl_, gl_->Uniform1i(program->fragment_shader().sampler_location(), 0)); @@ -2832,112 +2805,155 @@ const GLRenderer::SolidColorProgramAA* GLRenderer::GetSolidColorProgramAA() { } const GLRenderer::RenderPassProgram* GLRenderer::GetRenderPassProgram( - TexCoordPrecision precision) { + TexCoordPrecision precision, + BlendMode blend_mode) { DCHECK_GE(precision, 0); DCHECK_LT(precision, NumTexCoordPrecisions); - RenderPassProgram* program = &render_pass_program_[precision]; + DCHECK_GE(blend_mode, 0); + DCHECK_LT(blend_mode, NumBlendModes); + RenderPassProgram* program = &render_pass_program_[precision][blend_mode]; if (!program->initialized()) { TRACE_EVENT0("cc", "GLRenderer::renderPassProgram::initialize"); - program->Initialize( - output_surface_->context_provider(), precision, SamplerType2D); + program->Initialize(output_surface_->context_provider(), + precision, + SamplerType2D, + blend_mode); } return program; } const GLRenderer::RenderPassProgramAA* GLRenderer::GetRenderPassProgramAA( - TexCoordPrecision precision) { + TexCoordPrecision precision, + BlendMode blend_mode) { DCHECK_GE(precision, 0); DCHECK_LT(precision, NumTexCoordPrecisions); - RenderPassProgramAA* program = &render_pass_program_aa_[precision]; + DCHECK_GE(blend_mode, 0); + DCHECK_LT(blend_mode, NumBlendModes); + RenderPassProgramAA* program = + &render_pass_program_aa_[precision][blend_mode]; if (!program->initialized()) { TRACE_EVENT0("cc", "GLRenderer::renderPassProgramAA::initialize"); - program->Initialize( - output_surface_->context_provider(), precision, SamplerType2D); + program->Initialize(output_surface_->context_provider(), + precision, + SamplerType2D, + blend_mode); } return program; } const GLRenderer::RenderPassMaskProgram* GLRenderer::GetRenderPassMaskProgram( - TexCoordPrecision precision) { + TexCoordPrecision precision, + BlendMode blend_mode) { DCHECK_GE(precision, 0); DCHECK_LT(precision, NumTexCoordPrecisions); - RenderPassMaskProgram* program = &render_pass_mask_program_[precision]; + DCHECK_GE(blend_mode, 0); + DCHECK_LT(blend_mode, NumBlendModes); + RenderPassMaskProgram* program = + &render_pass_mask_program_[precision][blend_mode]; if (!program->initialized()) { TRACE_EVENT0("cc", "GLRenderer::renderPassMaskProgram::initialize"); - program->Initialize( - output_surface_->context_provider(), precision, SamplerType2D); + program->Initialize(output_surface_->context_provider(), + precision, + SamplerType2D, + blend_mode); } return program; } const GLRenderer::RenderPassMaskProgramAA* -GLRenderer::GetRenderPassMaskProgramAA(TexCoordPrecision precision) { +GLRenderer::GetRenderPassMaskProgramAA(TexCoordPrecision precision, + BlendMode blend_mode) { DCHECK_GE(precision, 0); DCHECK_LT(precision, NumTexCoordPrecisions); - RenderPassMaskProgramAA* program = &render_pass_mask_program_aa_[precision]; + DCHECK_GE(blend_mode, 0); + DCHECK_LT(blend_mode, NumBlendModes); + RenderPassMaskProgramAA* program = + &render_pass_mask_program_aa_[precision][blend_mode]; if (!program->initialized()) { TRACE_EVENT0("cc", "GLRenderer::renderPassMaskProgramAA::initialize"); - program->Initialize( - output_surface_->context_provider(), precision, SamplerType2D); + program->Initialize(output_surface_->context_provider(), + precision, + SamplerType2D, + blend_mode); } return program; } const GLRenderer::RenderPassColorMatrixProgram* -GLRenderer::GetRenderPassColorMatrixProgram(TexCoordPrecision precision) { +GLRenderer::GetRenderPassColorMatrixProgram(TexCoordPrecision precision, + BlendMode blend_mode) { DCHECK_GE(precision, 0); DCHECK_LT(precision, NumTexCoordPrecisions); + DCHECK_GE(blend_mode, 0); + DCHECK_LT(blend_mode, NumBlendModes); RenderPassColorMatrixProgram* program = - &render_pass_color_matrix_program_[precision]; + &render_pass_color_matrix_program_[precision][blend_mode]; if (!program->initialized()) { TRACE_EVENT0("cc", "GLRenderer::renderPassColorMatrixProgram::initialize"); - program->Initialize( - output_surface_->context_provider(), precision, SamplerType2D); + program->Initialize(output_surface_->context_provider(), + precision, + SamplerType2D, + blend_mode); } return program; } const GLRenderer::RenderPassColorMatrixProgramAA* -GLRenderer::GetRenderPassColorMatrixProgramAA(TexCoordPrecision precision) { +GLRenderer::GetRenderPassColorMatrixProgramAA(TexCoordPrecision precision, + BlendMode blend_mode) { DCHECK_GE(precision, 0); DCHECK_LT(precision, NumTexCoordPrecisions); + DCHECK_GE(blend_mode, 0); + DCHECK_LT(blend_mode, NumBlendModes); RenderPassColorMatrixProgramAA* program = - &render_pass_color_matrix_program_aa_[precision]; + &render_pass_color_matrix_program_aa_[precision][blend_mode]; if (!program->initialized()) { TRACE_EVENT0("cc", "GLRenderer::renderPassColorMatrixProgramAA::initialize"); - program->Initialize( - output_surface_->context_provider(), precision, SamplerType2D); + program->Initialize(output_surface_->context_provider(), + precision, + SamplerType2D, + blend_mode); } return program; } const GLRenderer::RenderPassMaskColorMatrixProgram* -GLRenderer::GetRenderPassMaskColorMatrixProgram(TexCoordPrecision precision) { +GLRenderer::GetRenderPassMaskColorMatrixProgram(TexCoordPrecision precision, + BlendMode blend_mode) { DCHECK_GE(precision, 0); DCHECK_LT(precision, NumTexCoordPrecisions); + DCHECK_GE(blend_mode, 0); + DCHECK_LT(blend_mode, NumBlendModes); RenderPassMaskColorMatrixProgram* program = - &render_pass_mask_color_matrix_program_[precision]; + &render_pass_mask_color_matrix_program_[precision][blend_mode]; if (!program->initialized()) { TRACE_EVENT0("cc", "GLRenderer::renderPassMaskColorMatrixProgram::initialize"); - program->Initialize( - output_surface_->context_provider(), precision, SamplerType2D); + program->Initialize(output_surface_->context_provider(), + precision, + SamplerType2D, + blend_mode); } return program; } const GLRenderer::RenderPassMaskColorMatrixProgramAA* -GLRenderer::GetRenderPassMaskColorMatrixProgramAA(TexCoordPrecision precision) { +GLRenderer::GetRenderPassMaskColorMatrixProgramAA(TexCoordPrecision precision, + BlendMode blend_mode) { DCHECK_GE(precision, 0); DCHECK_LT(precision, NumTexCoordPrecisions); + DCHECK_GE(blend_mode, 0); + DCHECK_LT(blend_mode, NumBlendModes); RenderPassMaskColorMatrixProgramAA* program = - &render_pass_mask_color_matrix_program_aa_[precision]; + &render_pass_mask_color_matrix_program_aa_[precision][blend_mode]; if (!program->initialized()) { TRACE_EVENT0("cc", "GLRenderer::renderPassMaskColorMatrixProgramAA::initialize"); - program->Initialize( - output_surface_->context_provider(), precision, SamplerType2D); + program->Initialize(output_surface_->context_provider(), + precision, + SamplerType2D, + blend_mode); } return program; } @@ -3163,15 +3179,16 @@ void GLRenderer::CleanupSharedObjects() { tile_program_aa_[i][j].Cleanup(gl_); tile_program_swizzle_aa_[i][j].Cleanup(gl_); } - - render_pass_mask_program_[i].Cleanup(gl_); - render_pass_program_[i].Cleanup(gl_); - render_pass_mask_program_aa_[i].Cleanup(gl_); - render_pass_program_aa_[i].Cleanup(gl_); - render_pass_color_matrix_program_[i].Cleanup(gl_); - render_pass_mask_color_matrix_program_aa_[i].Cleanup(gl_); - render_pass_color_matrix_program_aa_[i].Cleanup(gl_); - render_pass_mask_color_matrix_program_[i].Cleanup(gl_); + for (int j = 0; j < NumBlendModes; j++) { + render_pass_mask_program_[i][j].Cleanup(gl_); + render_pass_program_[i][j].Cleanup(gl_); + render_pass_mask_program_aa_[i][j].Cleanup(gl_); + render_pass_program_aa_[i][j].Cleanup(gl_); + render_pass_color_matrix_program_[i][j].Cleanup(gl_); + render_pass_mask_color_matrix_program_aa_[i][j].Cleanup(gl_); + render_pass_color_matrix_program_aa_[i][j].Cleanup(gl_); + render_pass_mask_color_matrix_program_[i][j].Cleanup(gl_); + } texture_program_[i].Cleanup(gl_); nonpremultiplied_texture_program_[i].Cleanup(gl_); diff --git a/cc/output/gl_renderer.h b/cc/output/gl_renderer.h index a379dc0..676e7b7 100644 --- a/cc/output/gl_renderer.h +++ b/cc/output/gl_renderer.h @@ -153,7 +153,8 @@ class CC_EXPORT GLRenderer : public DirectRenderer { gfx::Rect GetBackdropBoundingBoxForRenderPassQuad( DrawingFrame* frame, const RenderPassDrawQuad* quad, - const gfx::Transform& contents_device_transform); + const gfx::Transform& contents_device_transform, + bool use_aa); scoped_ptr<ScopedResource> GetBackdropTexture(const gfx::Rect& bounding_rect); static bool ShouldApplyBackgroundFilters(DrawingFrame* frame, @@ -166,7 +167,6 @@ class CC_EXPORT GLRenderer : public DirectRenderer { DrawingFrame* frame, const RenderPassDrawQuad* quad, const gfx::Transform& contents_device_transform_inverse, - ScopedResource* background_texture, skia::RefPtr<SkImage> backdrop_bitmap, const gfx::Rect& backdrop_bounding_rect); @@ -328,22 +328,28 @@ class CC_EXPORT GLRenderer : public DirectRenderer { const TileCheckerboardProgram* GetTileCheckerboardProgram(); - const RenderPassProgram* GetRenderPassProgram( - TexCoordPrecision precision); - const RenderPassProgramAA* GetRenderPassProgramAA( - TexCoordPrecision precision); + const RenderPassProgram* GetRenderPassProgram(TexCoordPrecision precision, + BlendMode blend_mode); + const RenderPassProgramAA* GetRenderPassProgramAA(TexCoordPrecision precision, + BlendMode blend_mode); const RenderPassMaskProgram* GetRenderPassMaskProgram( - TexCoordPrecision precision); + TexCoordPrecision precision, + BlendMode blend_mode); const RenderPassMaskProgramAA* GetRenderPassMaskProgramAA( - TexCoordPrecision precision); + TexCoordPrecision precision, + BlendMode blend_mode); const RenderPassColorMatrixProgram* GetRenderPassColorMatrixProgram( - TexCoordPrecision precision); + TexCoordPrecision precision, + BlendMode blend_mode); const RenderPassColorMatrixProgramAA* GetRenderPassColorMatrixProgramAA( - TexCoordPrecision precision); + TexCoordPrecision precision, + BlendMode blend_mode); const RenderPassMaskColorMatrixProgram* GetRenderPassMaskColorMatrixProgram( - TexCoordPrecision precision); + TexCoordPrecision precision, + BlendMode blend_mode); const RenderPassMaskColorMatrixProgramAA* - GetRenderPassMaskColorMatrixProgramAA(TexCoordPrecision precision); + GetRenderPassMaskColorMatrixProgramAA(TexCoordPrecision precision, + BlendMode blend_mode); const TextureProgram* GetTextureProgram( TexCoordPrecision precision); @@ -388,18 +394,21 @@ class CC_EXPORT GLRenderer : public DirectRenderer { nonpremultiplied_texture_background_program_[NumTexCoordPrecisions]; TextureProgram texture_io_surface_program_[NumTexCoordPrecisions]; - RenderPassProgram render_pass_program_[NumTexCoordPrecisions]; - RenderPassProgramAA render_pass_program_aa_[NumTexCoordPrecisions]; - RenderPassMaskProgram render_pass_mask_program_[NumTexCoordPrecisions]; - RenderPassMaskProgramAA render_pass_mask_program_aa_[NumTexCoordPrecisions]; + RenderPassProgram render_pass_program_[NumTexCoordPrecisions][NumBlendModes]; + RenderPassProgramAA + render_pass_program_aa_[NumTexCoordPrecisions][NumBlendModes]; + RenderPassMaskProgram + render_pass_mask_program_[NumTexCoordPrecisions][NumBlendModes]; + RenderPassMaskProgramAA + render_pass_mask_program_aa_[NumTexCoordPrecisions][NumBlendModes]; RenderPassColorMatrixProgram - render_pass_color_matrix_program_[NumTexCoordPrecisions]; - RenderPassColorMatrixProgramAA - render_pass_color_matrix_program_aa_[NumTexCoordPrecisions]; - RenderPassMaskColorMatrixProgram - render_pass_mask_color_matrix_program_[NumTexCoordPrecisions]; - RenderPassMaskColorMatrixProgramAA - render_pass_mask_color_matrix_program_aa_[NumTexCoordPrecisions]; + render_pass_color_matrix_program_[NumTexCoordPrecisions][NumBlendModes]; + RenderPassColorMatrixProgramAA render_pass_color_matrix_program_aa_ + [NumTexCoordPrecisions][NumBlendModes]; + RenderPassMaskColorMatrixProgram render_pass_mask_color_matrix_program_ + [NumTexCoordPrecisions][NumBlendModes]; + RenderPassMaskColorMatrixProgramAA render_pass_mask_color_matrix_program_aa_ + [NumTexCoordPrecisions][NumBlendModes]; VideoYUVProgram video_yuv_program_[NumTexCoordPrecisions]; VideoYUVAProgram video_yuva_program_[NumTexCoordPrecisions]; diff --git a/cc/output/gl_renderer_unittest.cc b/cc/output/gl_renderer_unittest.cc index 6234e9e..8de90c4 100644 --- a/cc/output/gl_renderer_unittest.cc +++ b/cc/output/gl_renderer_unittest.cc @@ -55,6 +55,44 @@ class GLRendererTest : public testing::Test { EXPECT_TRUE((program_binding)->initialized()); \ } while (false) +static inline SkXfermode::Mode BlendModeToSkXfermode(BlendMode blend_mode) { + switch (blend_mode) { + case BlendModeNormal: + return SkXfermode::kSrcOver_Mode; + case BlendModeOverlay: + return SkXfermode::kOverlay_Mode; + case BlendModeDarken: + return SkXfermode::kDarken_Mode; + case BlendModeLighten: + return SkXfermode::kLighten_Mode; + case BlendModeColorDodge: + return SkXfermode::kColorDodge_Mode; + case BlendModeColorBurn: + return SkXfermode::kColorBurn_Mode; + case BlendModeHardLight: + return SkXfermode::kHardLight_Mode; + case BlendModeSoftLight: + return SkXfermode::kSoftLight_Mode; + case BlendModeDifference: + return SkXfermode::kDifference_Mode; + case BlendModeExclusion: + return SkXfermode::kExclusion_Mode; + case BlendModeMultiply: + return SkXfermode::kMultiply_Mode; + case BlendModeHue: + return SkXfermode::kHue_Mode; + case BlendModeSaturation: + return SkXfermode::kSaturation_Mode; + case BlendModeColor: + return SkXfermode::kColor_Mode; + case BlendModeLuminosity: + return SkXfermode::kLuminosity_Mode; + case NumBlendModes: + NOTREACHED(); + } + return SkXfermode::kSrcOver_Mode; +} + // Explicitly named to be a friend in GLRenderer for shader access. class GLRendererShaderPixelTest : public GLRendererPixelTest { public: @@ -70,18 +108,25 @@ class GLRendererShaderPixelTest : public GLRendererPixelTest { } void TestShadersWithTexCoordPrecision(TexCoordPrecision precision) { - EXPECT_PROGRAM_VALID(renderer()->GetRenderPassProgram(precision)); - EXPECT_PROGRAM_VALID(renderer()->GetRenderPassProgramAA(precision)); - EXPECT_PROGRAM_VALID(renderer()->GetRenderPassMaskProgram(precision)); - EXPECT_PROGRAM_VALID(renderer()->GetRenderPassMaskProgramAA(precision)); - EXPECT_PROGRAM_VALID( - renderer()->GetRenderPassColorMatrixProgram(precision)); - EXPECT_PROGRAM_VALID( - renderer()->GetRenderPassMaskColorMatrixProgramAA(precision)); - EXPECT_PROGRAM_VALID( - renderer()->GetRenderPassColorMatrixProgramAA(precision)); - EXPECT_PROGRAM_VALID( - renderer()->GetRenderPassMaskColorMatrixProgram(precision)); + for (int i = 0; i < NumBlendModes; ++i) { + BlendMode blend_mode = static_cast<BlendMode>(i); + EXPECT_PROGRAM_VALID( + renderer()->GetRenderPassProgram(precision, blend_mode)); + EXPECT_PROGRAM_VALID( + renderer()->GetRenderPassProgramAA(precision, blend_mode)); + EXPECT_PROGRAM_VALID( + renderer()->GetRenderPassMaskProgram(precision, blend_mode)); + EXPECT_PROGRAM_VALID( + renderer()->GetRenderPassMaskProgramAA(precision, blend_mode)); + EXPECT_PROGRAM_VALID( + renderer()->GetRenderPassColorMatrixProgram(precision, blend_mode)); + EXPECT_PROGRAM_VALID(renderer()->GetRenderPassMaskColorMatrixProgramAA( + precision, blend_mode)); + EXPECT_PROGRAM_VALID( + renderer()->GetRenderPassColorMatrixProgramAA(precision, blend_mode)); + EXPECT_PROGRAM_VALID(renderer()->GetRenderPassMaskColorMatrixProgram( + precision, blend_mode)); + } EXPECT_PROGRAM_VALID(renderer()->GetTextureProgram(precision)); EXPECT_PROGRAM_VALID( renderer()->GetNonPremultipliedTextureProgram(precision)); @@ -202,61 +247,84 @@ class GLRendererShaderTest : public GLRendererTest { resource_provider_.get())); } - void TestRenderPassProgram(TexCoordPrecision precision) { - EXPECT_PROGRAM_VALID(&renderer_->render_pass_program_[precision]); - EXPECT_EQ(renderer_->render_pass_program_[precision].program(), - renderer_->program_shadow_); - } - - void TestRenderPassColorMatrixProgram(TexCoordPrecision precision) { + void TestRenderPassProgram(TexCoordPrecision precision, + BlendMode blend_mode) { EXPECT_PROGRAM_VALID( - &renderer_->render_pass_color_matrix_program_[precision]); - EXPECT_EQ(renderer_->render_pass_color_matrix_program_[precision].program(), + &renderer_->render_pass_program_[precision][blend_mode]); + EXPECT_EQ(renderer_->render_pass_program_[precision][blend_mode].program(), renderer_->program_shadow_); } - void TestRenderPassMaskProgram(TexCoordPrecision precision) { - EXPECT_PROGRAM_VALID(&renderer_->render_pass_mask_program_[precision]); - EXPECT_EQ(renderer_->render_pass_mask_program_[precision].program(), - renderer_->program_shadow_); + void TestRenderPassColorMatrixProgram(TexCoordPrecision precision, + BlendMode blend_mode) { + EXPECT_PROGRAM_VALID( + &renderer_->render_pass_color_matrix_program_[precision][blend_mode]); + EXPECT_EQ( + renderer_->render_pass_color_matrix_program_[precision][blend_mode] + .program(), + renderer_->program_shadow_); } - void TestRenderPassMaskColorMatrixProgram(TexCoordPrecision precision) { + void TestRenderPassMaskProgram(TexCoordPrecision precision, + BlendMode blend_mode) { EXPECT_PROGRAM_VALID( - &renderer_->render_pass_mask_color_matrix_program_[precision]); + &renderer_->render_pass_mask_program_[precision][blend_mode]); EXPECT_EQ( - renderer_->render_pass_mask_color_matrix_program_[precision].program(), + renderer_->render_pass_mask_program_[precision][blend_mode].program(), renderer_->program_shadow_); } - void TestRenderPassProgramAA(TexCoordPrecision precision) { - EXPECT_PROGRAM_VALID(&renderer_->render_pass_program_aa_[precision]); - EXPECT_EQ(renderer_->render_pass_program_aa_[precision].program(), - renderer_->program_shadow_); + void TestRenderPassMaskColorMatrixProgram(TexCoordPrecision precision, + BlendMode blend_mode) { + EXPECT_PROGRAM_VALID( + &renderer_ + ->render_pass_mask_color_matrix_program_[precision][blend_mode]); + EXPECT_EQ( + renderer_->render_pass_mask_color_matrix_program_[precision][blend_mode] + .program(), + renderer_->program_shadow_); } - void TestRenderPassColorMatrixProgramAA(TexCoordPrecision precision) { + void TestRenderPassProgramAA(TexCoordPrecision precision, + BlendMode blend_mode) { EXPECT_PROGRAM_VALID( - &renderer_->render_pass_color_matrix_program_aa_[precision]); + &renderer_->render_pass_program_aa_[precision][blend_mode]); EXPECT_EQ( - renderer_->render_pass_color_matrix_program_aa_[precision].program(), + renderer_->render_pass_program_aa_[precision][blend_mode].program(), renderer_->program_shadow_); } - void TestRenderPassMaskProgramAA(TexCoordPrecision precision) { - EXPECT_PROGRAM_VALID(&renderer_->render_pass_mask_program_aa_[precision]); - EXPECT_EQ(renderer_->render_pass_mask_program_aa_[precision].program(), - renderer_->program_shadow_); + void TestRenderPassColorMatrixProgramAA(TexCoordPrecision precision, + BlendMode blend_mode) { + EXPECT_PROGRAM_VALID( + &renderer_ + ->render_pass_color_matrix_program_aa_[precision][blend_mode]); + EXPECT_EQ( + renderer_->render_pass_color_matrix_program_aa_[precision][blend_mode] + .program(), + renderer_->program_shadow_); } - void TestRenderPassMaskColorMatrixProgramAA(TexCoordPrecision precision) { + void TestRenderPassMaskProgramAA(TexCoordPrecision precision, + BlendMode blend_mode) { EXPECT_PROGRAM_VALID( - &renderer_->render_pass_mask_color_matrix_program_aa_[precision]); - EXPECT_EQ(renderer_->render_pass_mask_color_matrix_program_aa_[precision] + &renderer_->render_pass_mask_program_aa_[precision][blend_mode]); + EXPECT_EQ(renderer_->render_pass_mask_program_aa_[precision][blend_mode] .program(), renderer_->program_shadow_); } + void TestRenderPassMaskColorMatrixProgramAA(TexCoordPrecision precision, + BlendMode blend_mode) { + EXPECT_PROGRAM_VALID(&renderer_->render_pass_mask_color_matrix_program_aa_ + [precision][blend_mode]); + EXPECT_EQ( + renderer_ + ->render_pass_mask_color_matrix_program_aa_[precision][blend_mode] + .program(), + renderer_->program_shadow_); + } + void TestSolidColorProgramAA() { EXPECT_PROGRAM_VALID(&renderer_->solid_color_program_aa_); EXPECT_EQ(renderer_->solid_color_program_aa_.program(), @@ -1348,191 +1416,224 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadShaderPermutations) { gfx::Transform transform_causing_aa; transform_causing_aa.Rotate(20.0); - // RenderPassProgram - child_pass = AddRenderPass(&render_passes_in_draw_order_, - child_pass_id, - child_rect, - gfx::Transform()); + for (int i = 0; i < NumBlendModes; ++i) { + BlendMode blend_mode = static_cast<BlendMode>(i); + SkXfermode::Mode xfer_mode = BlendModeToSkXfermode(blend_mode); + // RenderPassProgram + render_passes_in_draw_order_.clear(); + child_pass = AddRenderPass(&render_passes_in_draw_order_, + child_pass_id, + child_rect, + gfx::Transform()); + + root_pass = AddRenderPass(&render_passes_in_draw_order_, + root_pass_id, + viewport_rect, + gfx::Transform()); + + AddRenderPassQuad(root_pass, + child_pass, + 0, + FilterOperations(), + gfx::Transform(), + xfer_mode); - root_pass = AddRenderPass(&render_passes_in_draw_order_, - root_pass_id, - viewport_rect, - gfx::Transform()); + renderer_->DecideRenderPassAllocationsForFrame( + render_passes_in_draw_order_); + renderer_->DrawFrame(&render_passes_in_draw_order_, + 1.f, + viewport_rect, + viewport_rect, + false); + TestRenderPassProgram(TexCoordPrecisionMedium, blend_mode); - AddRenderPassQuad( - root_pass, child_pass, 0, FilterOperations(), gfx::Transform()); + // RenderPassColorMatrixProgram + render_passes_in_draw_order_.clear(); - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - renderer_->DrawFrame(&render_passes_in_draw_order_, - 1.f, - viewport_rect, - viewport_rect, - false); - TestRenderPassProgram(TexCoordPrecisionMedium); + child_pass = AddRenderPass(&render_passes_in_draw_order_, + child_pass_id, + child_rect, + transform_causing_aa); - // RenderPassColorMatrixProgram - render_passes_in_draw_order_.clear(); + root_pass = AddRenderPass(&render_passes_in_draw_order_, + root_pass_id, + viewport_rect, + gfx::Transform()); - child_pass = AddRenderPass(&render_passes_in_draw_order_, - child_pass_id, - child_rect, - transform_causing_aa); + AddRenderPassQuad( + root_pass, child_pass, 0, filters, gfx::Transform(), xfer_mode); - root_pass = AddRenderPass(&render_passes_in_draw_order_, - root_pass_id, - viewport_rect, - gfx::Transform()); + renderer_->DecideRenderPassAllocationsForFrame( + render_passes_in_draw_order_); + renderer_->DrawFrame(&render_passes_in_draw_order_, + 1.f, + viewport_rect, + viewport_rect, + false); + TestRenderPassColorMatrixProgram(TexCoordPrecisionMedium, blend_mode); - AddRenderPassQuad(root_pass, child_pass, 0, filters, gfx::Transform()); + // RenderPassMaskProgram + render_passes_in_draw_order_.clear(); - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - renderer_->DrawFrame(&render_passes_in_draw_order_, - 1.f, - viewport_rect, - viewport_rect, - false); - TestRenderPassColorMatrixProgram(TexCoordPrecisionMedium); + child_pass = AddRenderPass(&render_passes_in_draw_order_, + child_pass_id, + child_rect, + gfx::Transform()); - // RenderPassMaskProgram - render_passes_in_draw_order_.clear(); + root_pass = AddRenderPass(&render_passes_in_draw_order_, + root_pass_id, + viewport_rect, + gfx::Transform()); - child_pass = AddRenderPass(&render_passes_in_draw_order_, - child_pass_id, - child_rect, - gfx::Transform()); + AddRenderPassQuad(root_pass, + child_pass, + mask, + FilterOperations(), + gfx::Transform(), + xfer_mode); - root_pass = AddRenderPass(&render_passes_in_draw_order_, - root_pass_id, - viewport_rect, - gfx::Transform()); + renderer_->DecideRenderPassAllocationsForFrame( + render_passes_in_draw_order_); + renderer_->DrawFrame(&render_passes_in_draw_order_, + 1.f, + viewport_rect, + viewport_rect, + false); + TestRenderPassMaskProgram(TexCoordPrecisionMedium, blend_mode); - AddRenderPassQuad( - root_pass, child_pass, mask, FilterOperations(), gfx::Transform()); + // RenderPassMaskColorMatrixProgram + render_passes_in_draw_order_.clear(); - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - renderer_->DrawFrame(&render_passes_in_draw_order_, - 1.f, - viewport_rect, - viewport_rect, - false); - TestRenderPassMaskProgram(TexCoordPrecisionMedium); + child_pass = AddRenderPass(&render_passes_in_draw_order_, + child_pass_id, + child_rect, + gfx::Transform()); - // RenderPassMaskColorMatrixProgram - render_passes_in_draw_order_.clear(); + root_pass = AddRenderPass(&render_passes_in_draw_order_, + root_pass_id, + viewport_rect, + gfx::Transform()); - child_pass = AddRenderPass(&render_passes_in_draw_order_, - child_pass_id, - child_rect, - gfx::Transform()); + AddRenderPassQuad( + root_pass, child_pass, mask, filters, gfx::Transform(), xfer_mode); - root_pass = AddRenderPass(&render_passes_in_draw_order_, - root_pass_id, - viewport_rect, - gfx::Transform()); - - AddRenderPassQuad(root_pass, child_pass, mask, filters, gfx::Transform()); - - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - renderer_->DrawFrame(&render_passes_in_draw_order_, - 1.f, - viewport_rect, - viewport_rect, - false); - TestRenderPassMaskColorMatrixProgram(TexCoordPrecisionMedium); + renderer_->DecideRenderPassAllocationsForFrame( + render_passes_in_draw_order_); + renderer_->DrawFrame(&render_passes_in_draw_order_, + 1.f, + viewport_rect, + viewport_rect, + false); + TestRenderPassMaskColorMatrixProgram(TexCoordPrecisionMedium, blend_mode); - // RenderPassProgramAA - render_passes_in_draw_order_.clear(); + // RenderPassProgramAA + render_passes_in_draw_order_.clear(); - child_pass = AddRenderPass(&render_passes_in_draw_order_, - child_pass_id, - child_rect, - transform_causing_aa); + child_pass = AddRenderPass(&render_passes_in_draw_order_, + child_pass_id, + child_rect, + transform_causing_aa); - root_pass = AddRenderPass(&render_passes_in_draw_order_, - root_pass_id, - viewport_rect, - gfx::Transform()); + root_pass = AddRenderPass(&render_passes_in_draw_order_, + root_pass_id, + viewport_rect, + gfx::Transform()); - AddRenderPassQuad( - root_pass, child_pass, 0, FilterOperations(), transform_causing_aa); + AddRenderPassQuad(root_pass, + child_pass, + 0, + FilterOperations(), + transform_causing_aa, + xfer_mode); - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - renderer_->DrawFrame(&render_passes_in_draw_order_, - 1.f, - viewport_rect, - viewport_rect, - false); - TestRenderPassProgramAA(TexCoordPrecisionMedium); + renderer_->DecideRenderPassAllocationsForFrame( + render_passes_in_draw_order_); + renderer_->DrawFrame(&render_passes_in_draw_order_, + 1.f, + viewport_rect, + viewport_rect, + false); + TestRenderPassProgramAA(TexCoordPrecisionMedium, blend_mode); - // RenderPassColorMatrixProgramAA - render_passes_in_draw_order_.clear(); + // RenderPassColorMatrixProgramAA + render_passes_in_draw_order_.clear(); - child_pass = AddRenderPass(&render_passes_in_draw_order_, - child_pass_id, - child_rect, - transform_causing_aa); + child_pass = AddRenderPass(&render_passes_in_draw_order_, + child_pass_id, + child_rect, + transform_causing_aa); - root_pass = AddRenderPass(&render_passes_in_draw_order_, - root_pass_id, - viewport_rect, - gfx::Transform()); + root_pass = AddRenderPass(&render_passes_in_draw_order_, + root_pass_id, + viewport_rect, + gfx::Transform()); - AddRenderPassQuad(root_pass, child_pass, 0, filters, transform_causing_aa); + AddRenderPassQuad( + root_pass, child_pass, 0, filters, transform_causing_aa, xfer_mode); - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - renderer_->DrawFrame(&render_passes_in_draw_order_, - 1.f, - viewport_rect, - viewport_rect, - false); - TestRenderPassColorMatrixProgramAA(TexCoordPrecisionMedium); + renderer_->DecideRenderPassAllocationsForFrame( + render_passes_in_draw_order_); + renderer_->DrawFrame(&render_passes_in_draw_order_, + 1.f, + viewport_rect, + viewport_rect, + false); + TestRenderPassColorMatrixProgramAA(TexCoordPrecisionMedium, blend_mode); - // RenderPassMaskProgramAA - render_passes_in_draw_order_.clear(); + // RenderPassMaskProgramAA + render_passes_in_draw_order_.clear(); - child_pass = AddRenderPass(&render_passes_in_draw_order_, - child_pass_id, - child_rect, - transform_causing_aa); + child_pass = AddRenderPass(&render_passes_in_draw_order_, + child_pass_id, + child_rect, + transform_causing_aa); - root_pass = AddRenderPass(&render_passes_in_draw_order_, - root_pass_id, - viewport_rect, - gfx::Transform()); + root_pass = AddRenderPass(&render_passes_in_draw_order_, + root_pass_id, + viewport_rect, + gfx::Transform()); - AddRenderPassQuad( - root_pass, child_pass, mask, FilterOperations(), transform_causing_aa); + AddRenderPassQuad(root_pass, + child_pass, + mask, + FilterOperations(), + transform_causing_aa, + xfer_mode); - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - renderer_->DrawFrame(&render_passes_in_draw_order_, - 1.f, - viewport_rect, - viewport_rect, - false); - TestRenderPassMaskProgramAA(TexCoordPrecisionMedium); + renderer_->DecideRenderPassAllocationsForFrame( + render_passes_in_draw_order_); + renderer_->DrawFrame(&render_passes_in_draw_order_, + 1.f, + viewport_rect, + viewport_rect, + false); + TestRenderPassMaskProgramAA(TexCoordPrecisionMedium, blend_mode); - // RenderPassMaskColorMatrixProgramAA - render_passes_in_draw_order_.clear(); + // RenderPassMaskColorMatrixProgramAA + render_passes_in_draw_order_.clear(); - child_pass = AddRenderPass(&render_passes_in_draw_order_, - child_pass_id, - child_rect, - transform_causing_aa); + child_pass = AddRenderPass(&render_passes_in_draw_order_, + child_pass_id, + child_rect, + transform_causing_aa); - root_pass = AddRenderPass(&render_passes_in_draw_order_, - root_pass_id, - viewport_rect, - transform_causing_aa); + root_pass = AddRenderPass(&render_passes_in_draw_order_, + root_pass_id, + viewport_rect, + transform_causing_aa); - AddRenderPassQuad(root_pass, child_pass, mask, filters, transform_causing_aa); + AddRenderPassQuad( + root_pass, child_pass, mask, filters, transform_causing_aa, xfer_mode); - renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); - renderer_->DrawFrame(&render_passes_in_draw_order_, - 1.f, - viewport_rect, - viewport_rect, - false); - TestRenderPassMaskColorMatrixProgramAA(TexCoordPrecisionMedium); + renderer_->DecideRenderPassAllocationsForFrame( + render_passes_in_draw_order_); + renderer_->DrawFrame(&render_passes_in_draw_order_, + 1.f, + viewport_rect, + viewport_rect, + false); + TestRenderPassMaskColorMatrixProgramAA(TexCoordPrecisionMedium, blend_mode); + } } // At this time, the AA code path cannot be taken if the surface's rect would @@ -1567,8 +1668,12 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadSkipsAAForClippingTransform) { viewport_rect, gfx::Transform()); - AddRenderPassQuad( - root_pass, child_pass, 0, FilterOperations(), transform_preventing_aa); + AddRenderPassQuad(root_pass, + child_pass, + 0, + FilterOperations(), + transform_preventing_aa, + SkXfermode::kSrcOver_Mode); renderer_->DecideRenderPassAllocationsForFrame(render_passes_in_draw_order_); renderer_->DrawFrame(&render_passes_in_draw_order_, @@ -1579,7 +1684,7 @@ TEST_F(GLRendererShaderTest, DrawRenderPassQuadSkipsAAForClippingTransform) { // If use_aa incorrectly ignores clipping, it will use the // RenderPassProgramAA shader instead of the RenderPassProgram. - TestRenderPassProgram(TexCoordPrecisionMedium); + TestRenderPassProgram(TexCoordPrecisionMedium, BlendModeNormal); } TEST_F(GLRendererShaderTest, DrawSolidColorShader) { diff --git a/cc/output/program_binding.h b/cc/output/program_binding.h index 912329e..722c782 100644 --- a/cc/output/program_binding.h +++ b/cc/output/program_binding.h @@ -58,13 +58,16 @@ class ProgramBinding : public ProgramBindingBase { void Initialize(ContextProvider* context_provider, TexCoordPrecision precision, - SamplerType sampler) { + SamplerType sampler, + BlendMode blend_mode = BlendModeNormal) { DCHECK(context_provider); DCHECK(!initialized_); if (context_provider->IsContextLost()) return; + fragment_shader_.set_blend_mode(blend_mode); + if (!ProgramBindingBase::Init( context_provider->ContextGL(), vertex_shader_.GetShaderString(), diff --git a/cc/output/shader.cc b/cc/output/shader.cc index 8a25214..98bde8d 100644 --- a/cc/output/shader.cc +++ b/cc/output/shader.cc @@ -13,9 +13,10 @@ #define SHADER0(Src) #Src #define VERTEX_SHADER(Src) SetVertexTexCoordPrecision(SHADER0(Src)) -#define FRAGMENT_SHADER(Src) \ - SetFragmentTexCoordPrecision(precision, \ - SetFragmentSamplerType(sampler, SHADER0(Src))) +#define FRAGMENT_SHADER(Src) \ + SetFragmentTexCoordPrecision( \ + precision, \ + SetFragmentSamplerType(sampler, SetBlendModeFunctions(SHADER0(Src)))) using gpu::gles2::GLES2Interface; @@ -666,6 +667,308 @@ std::string VertexShaderVideoTransform::GetShaderString() const { // clang-format on } +#define BLEND_MODE_UNIFORMS "s_backdropTexture", "backdropRect" +#define UNUSED_BLEND_MODE_UNIFORMS (is_default_blend_mode() ? 2 : 0) +#define BLEND_MODE_SET_LOCATIONS(X, POS) \ + if (!is_default_blend_mode()) { \ + DCHECK_LT(static_cast<size_t>(POS) + 1, arraysize(X)); \ + backdrop_location_ = locations[POS]; \ + backdrop_rect_location_ = locations[POS + 1]; \ + } + +FragmentTexBlendMode::FragmentTexBlendMode() + : backdrop_location_(-1), + backdrop_rect_location_(-1), + blend_mode_(BlendModeNormal) { +} + +std::string FragmentTexBlendMode::SetBlendModeFunctions( + std::string shader_string) const { + if (shader_string.find("ApplyBlendMode") == std::string::npos) + return shader_string; + + if (is_default_blend_mode()) { + return "#define ApplyBlendMode(X) (X)\n" + shader_string; + } + + // clang-format off + static const std::string kFunctionApplyBlendMode = SHADER0( + // clang-format on + uniform SamplerType s_backdropTexture; + uniform TexCoordPrecision vec4 backdropRect; + + vec4 GetBackdropColor() { + TexCoordPrecision vec2 bgTexCoord = gl_FragCoord.xy - backdropRect.xy; + bgTexCoord.x /= backdropRect.z; + bgTexCoord.y /= backdropRect.w; + return TextureLookup(s_backdropTexture, bgTexCoord); + } + + vec4 ApplyBlendMode(vec4 src) { + vec4 dst = GetBackdropColor(); + return Blend(src, dst); + } + // clang-format off + ); + // clang-format on + + return "precision mediump float;" + GetHelperFunctions() + + GetBlendFunction() + kFunctionApplyBlendMode + shader_string; +} + +std::string FragmentTexBlendMode::GetHelperFunctions() const { + // clang-format off + static const std::string kFunctionHardLight = SHADER0( + // clang-format on + vec3 hardLight(vec4 src, vec4 dst) { + vec3 result; + result.r = + (2.0 * src.r <= src.a) + ? (2.0 * src.r * dst.r) + : (src.a * dst.a - 2.0 * (dst.a - dst.r) * (src.a - src.r)); + result.g = + (2.0 * src.g <= src.a) + ? (2.0 * src.g * dst.g) + : (src.a * dst.a - 2.0 * (dst.a - dst.g) * (src.a - src.g)); + result.b = + (2.0 * src.b <= src.a) + ? (2.0 * src.b * dst.b) + : (src.a * dst.a - 2.0 * (dst.a - dst.b) * (src.a - src.b)); + result.rgb += src.rgb * (1.0 - dst.a) + dst.rgb * (1.0 - src.a); + return result; + } + // clang-format off + ); + + static const std::string kFunctionColorDodgeComponent = SHADER0( + // clang-format on + float getColorDodgeComponent( + float srcc, float srca, float dstc, float dsta) { + if (0.0 == dstc) + return srcc * (1.0 - dsta); + float d = srca - srcc; + if (0.0 == d) + return srca * dsta + srcc * (1.0 - dsta) + dstc * (1.0 - srca); + d = min(dsta, dstc * srca / d); + return d * srca + srcc * (1.0 - dsta) + dstc * (1.0 - srca); + } + // clang-format off + ); + + static const std::string kFunctionColorBurnComponent = SHADER0( + // clang-format on + float getColorBurnComponent( + float srcc, float srca, float dstc, float dsta) { + if (dsta == dstc) + return srca * dsta + srcc * (1.0 - dsta) + dstc * (1.0 - srca); + if (0.0 == srcc) + return dstc * (1.0 - srca); + float d = max(0.0, dsta - (dsta - dstc) * srca / srcc); + return srca * d + srcc * (1.0 - dsta) + dstc * (1.0 - srca); + } + // clang-format off + ); + + static const std::string kFunctionSoftLightComponentPosDstAlpha = SHADER0( + // clang-format on + float getSoftLightComponent( + float srcc, float srca, float dstc, float dsta) { + if (2.0 * srcc <= srca) { + return (dstc * dstc * (srca - 2.0 * srcc)) / dsta + + (1.0 - dsta) * srcc + dstc * (-srca + 2.0 * srcc + 1.0); + } else if (4.0 * dstc <= dsta) { + float DSqd = dstc * dstc; + float DCub = DSqd * dstc; + float DaSqd = dsta * dsta; + float DaCub = DaSqd * dsta; + return (-DaCub * srcc + + DaSqd * (srcc - dstc * (3.0 * srca - 6.0 * srcc - 1.0)) + + 12.0 * dsta * DSqd * (srca - 2.0 * srcc) - + 16.0 * DCub * (srca - 2.0 * srcc)) / + DaSqd; + } else { + return -sqrt(dsta * dstc) * (srca - 2.0 * srcc) - dsta * srcc + + dstc * (srca - 2.0 * srcc + 1.0) + srcc; + } + } + // clang-format off + ); + + static const std::string kFunctionLum = SHADER0( + // clang-format on + float luminance(vec3 color) { return dot(vec3(0.3, 0.59, 0.11), color); } + + vec3 set_luminance(vec3 hueSat, float alpha, vec3 lumColor) { + float diff = luminance(lumColor - hueSat); + vec3 outColor = hueSat + diff; + float outLum = luminance(outColor); + float minComp = min(min(outColor.r, outColor.g), outColor.b); + float maxComp = max(max(outColor.r, outColor.g), outColor.b); + if (minComp < 0.0) { + outColor = outLum + + ((outColor - vec3(outLum, outLum, outLum)) * outLum) / + (outLum - minComp); + } + if (maxComp > alpha) { + outColor = + outLum + + ((outColor - vec3(outLum, outLum, outLum)) * (alpha - outLum)) / + (maxComp - outLum); + } + return outColor; + } + // clang-format off + ); + + static const std::string kFunctionSat = SHADER0( + // clang-format on + float saturation(vec3 color) { + return max(max(color.r, color.g), color.b) - + min(min(color.r, color.g), color.b); + } + + vec3 set_saturation_helper( + float minComp, float midComp, float maxComp, float sat) { + if (minComp < maxComp) { + vec3 result; + result.r = 0.0; + result.g = sat * (midComp - minComp) / (maxComp - minComp); + result.b = sat; + return result; + } else { + return vec3(0, 0, 0); + } + } + + vec3 set_saturation(vec3 hueLumColor, vec3 satColor) { + float sat = saturation(satColor); + if (hueLumColor.r <= hueLumColor.g) { + if (hueLumColor.g <= hueLumColor.b) { + hueLumColor.rgb = set_saturation_helper( + hueLumColor.r, hueLumColor.g, hueLumColor.b, sat); + } else if (hueLumColor.r <= hueLumColor.b) { + hueLumColor.rbg = set_saturation_helper( + hueLumColor.r, hueLumColor.b, hueLumColor.g, sat); + } else { + hueLumColor.brg = set_saturation_helper( + hueLumColor.b, hueLumColor.r, hueLumColor.g, sat); + } + } else if (hueLumColor.r <= hueLumColor.b) { + hueLumColor.grb = set_saturation_helper( + hueLumColor.g, hueLumColor.r, hueLumColor.b, sat); + } else if (hueLumColor.g <= hueLumColor.b) { + hueLumColor.gbr = set_saturation_helper( + hueLumColor.g, hueLumColor.b, hueLumColor.r, sat); + } else { + hueLumColor.bgr = set_saturation_helper( + hueLumColor.b, hueLumColor.g, hueLumColor.r, sat); + } + return hueLumColor; + } + // clang-format off + ); + // clang-format on + + switch (blend_mode_) { + case BlendModeOverlay: + case BlendModeHardLight: + return kFunctionHardLight; + case BlendModeColorDodge: + return kFunctionColorDodgeComponent; + case BlendModeColorBurn: + return kFunctionColorBurnComponent; + case BlendModeSoftLight: + return kFunctionSoftLightComponentPosDstAlpha; + case BlendModeHue: + case BlendModeSaturation: + return kFunctionLum + kFunctionSat; + case BlendModeColor: + case BlendModeLuminosity: + return kFunctionLum; + default: + return std::string(); + } +} + +std::string FragmentTexBlendMode::GetBlendFunction() const { + return "vec4 Blend(vec4 src, vec4 dst) {" + " vec4 result;" + " result.a = src.a + (1.0 - src.a) * dst.a;" + + GetBlendFunctionBodyForRGB() + + " return result;" + "}"; +} + +std::string FragmentTexBlendMode::GetBlendFunctionBodyForRGB() const { + switch (blend_mode_) { + case BlendModeLighten: + return "result.rgb = max((1.0 - src.a) * dst.rgb + src.rgb," + " (1.0 - dst.a) * src.rgb + dst.rgb);"; + case BlendModeOverlay: + return "result.rgb = hardLight(dst, src);"; + case BlendModeDarken: + return "result.rgb = min((1.0 - src.a) * dst.rgb + src.rgb," + " (1.0 - dst.a) * src.rgb + dst.rgb);"; + case BlendModeColorDodge: + return "result.r = getColorDodgeComponent(src.r, src.a, dst.r, dst.a);" + "result.g = getColorDodgeComponent(src.g, src.a, dst.g, dst.a);" + "result.b = getColorDodgeComponent(src.b, src.a, dst.b, dst.a);"; + case BlendModeColorBurn: + return "result.r = getColorBurnComponent(src.r, src.a, dst.r, dst.a);" + "result.g = getColorBurnComponent(src.g, src.a, dst.g, dst.a);" + "result.b = getColorBurnComponent(src.b, src.a, dst.b, dst.a);"; + case BlendModeHardLight: + return "result.rgb = hardLight(src, dst);"; + case BlendModeSoftLight: + return "if (0.0 == dst.a) {" + " result.rgb = src.rgb;" + "} else {" + " result.r = getSoftLightComponent(src.r, src.a, dst.r, dst.a);" + " result.g = getSoftLightComponent(src.g, src.a, dst.g, dst.a);" + " result.b = getSoftLightComponent(src.b, src.a, dst.b, dst.a);" + "}"; + case BlendModeDifference: + return "result.rgb = src.rgb + dst.rgb -" + " 2.0 * min(src.rgb * dst.a, dst.rgb * src.a);"; + case BlendModeExclusion: + return "result.rgb = dst.rgb + src.rgb - 2.0 * dst.rgb * src.rgb;"; + case BlendModeMultiply: + return "result.rgb = (1.0 - src.a) * dst.rgb +" + " (1.0 - dst.a) * src.rgb + src.rgb * dst.rgb;"; + case BlendModeHue: + return "vec4 dstSrcAlpha = dst * src.a;" + "result.rgb =" + " set_luminance(set_saturation(src.rgb * dst.a," + " dstSrcAlpha.rgb)," + " dstSrcAlpha.a," + " dstSrcAlpha.rgb);" + "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;"; + case BlendModeSaturation: + return "vec4 dstSrcAlpha = dst * src.a;" + "result.rgb = set_luminance(set_saturation(dstSrcAlpha.rgb," + " src.rgb * dst.a)," + " dstSrcAlpha.a," + " dstSrcAlpha.rgb);" + "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;"; + case BlendModeColor: + return "vec4 srcDstAlpha = src * dst.a;" + "result.rgb = set_luminance(srcDstAlpha.rgb," + " srcDstAlpha.a," + " dst.rgb * src.a);" + "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;"; + case BlendModeLuminosity: + return "vec4 srcDstAlpha = src * dst.a;" + "result.rgb = set_luminance(dst.rgb * src.a," + " srcDstAlpha.a," + " srcDstAlpha.rgb);" + "result.rgb += (1.0 - src.a) * dst.rgb + (1.0 - dst.a) * src.rgb;"; + default: + NOTREACHED(); + // simple alpha compositing + return "result.rgb = src.rgb * src.a + dst.rgb * dst.a * (1 - src.a)"; + } +} + FragmentTexAlphaBinding::FragmentTexAlphaBinding() : sampler_location_(-1), alpha_location_(-1) { } @@ -674,18 +977,19 @@ void FragmentTexAlphaBinding::Init(GLES2Interface* context, unsigned program, int* base_uniform_index) { static const char* uniforms[] = { - "s_texture", "alpha", + "s_texture", "alpha", BLEND_MODE_UNIFORMS, }; int locations[arraysize(uniforms)]; GetProgramUniformLocations(context, program, - arraysize(uniforms), + arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS, uniforms, locations, base_uniform_index); sampler_location_ = locations[0]; alpha_location_ = locations[1]; + BLEND_MODE_SET_LOCATIONS(locations, 2); } FragmentTexColorMatrixAlphaBinding::FragmentTexColorMatrixAlphaBinding() @@ -699,13 +1003,13 @@ void FragmentTexColorMatrixAlphaBinding::Init(GLES2Interface* context, unsigned program, int* base_uniform_index) { static const char* uniforms[] = { - "s_texture", "alpha", "colorMatrix", "colorOffset", + "s_texture", "alpha", "colorMatrix", "colorOffset", BLEND_MODE_UNIFORMS, }; int locations[arraysize(uniforms)]; GetProgramUniformLocations(context, program, - arraysize(uniforms), + arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS, uniforms, locations, base_uniform_index); @@ -713,6 +1017,7 @@ void FragmentTexColorMatrixAlphaBinding::Init(GLES2Interface* context, alpha_location_ = locations[1]; color_matrix_location_ = locations[2]; color_offset_location_ = locations[3]; + BLEND_MODE_SET_LOCATIONS(locations, 4); } FragmentTexOpaqueBinding::FragmentTexOpaqueBinding() : sampler_location_(-1) { @@ -747,7 +1052,7 @@ std::string FragmentShaderRGBATexAlpha::GetShaderString( uniform float alpha; void main() { vec4 texColor = TextureLookup(s_texture, v_texCoord); - gl_FragColor = texColor * alpha; + gl_FragColor = ApplyBlendMode(texColor * alpha); } // clang-format off ); // NOLINT(whitespace/parens) @@ -773,7 +1078,7 @@ std::string FragmentShaderRGBATexColorMatrixAlpha::GetShaderString( texColor = colorMatrix * texColor + colorOffset; texColor.rgb *= texColor.a; texColor = clamp(texColor, 0.0, 1.0); - gl_FragColor = texColor * alpha; + gl_FragColor = ApplyBlendMode(texColor * alpha); } // clang-format off ); // NOLINT(whitespace/parens) @@ -966,18 +1271,19 @@ void FragmentShaderRGBATexAlphaAA::Init(GLES2Interface* context, unsigned program, int* base_uniform_index) { static const char* uniforms[] = { - "s_texture", "alpha", + "s_texture", "alpha", BLEND_MODE_UNIFORMS, }; int locations[arraysize(uniforms)]; GetProgramUniformLocations(context, program, - arraysize(uniforms), + arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS, uniforms, locations, base_uniform_index); sampler_location_ = locations[0]; alpha_location_ = locations[1]; + BLEND_MODE_SET_LOCATIONS(locations, 2); } std::string FragmentShaderRGBATexAlphaAA::GetShaderString( @@ -997,7 +1303,7 @@ std::string FragmentShaderRGBATexAlphaAA::GetShaderString( vec4 d4 = min(edge_dist[0], edge_dist[1]); vec2 d2 = min(d4.xz, d4.yw); float aa = clamp(gl_FragCoord.w * min(d2.x, d2.y), 0.0, 1.0); - gl_FragColor = texColor * alpha * aa; + gl_FragColor = ApplyBlendMode(texColor * alpha * aa); } // clang-format off ); // NOLINT(whitespace/parens) @@ -1097,13 +1403,18 @@ void FragmentShaderRGBATexAlphaMask::Init(GLES2Interface* context, unsigned program, int* base_uniform_index) { static const char* uniforms[] = { - "s_texture", "s_mask", "alpha", "maskTexCoordScale", "maskTexCoordOffset", + "s_texture", + "s_mask", + "alpha", + "maskTexCoordScale", + "maskTexCoordOffset", + BLEND_MODE_UNIFORMS, }; int locations[arraysize(uniforms)]; GetProgramUniformLocations(context, program, - arraysize(uniforms), + arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS, uniforms, locations, base_uniform_index); @@ -1112,6 +1423,7 @@ void FragmentShaderRGBATexAlphaMask::Init(GLES2Interface* context, alpha_location_ = locations[2]; mask_tex_coord_scale_location_ = locations[3]; mask_tex_coord_offset_location_ = locations[4]; + BLEND_MODE_SET_LOCATIONS(locations, 5); } std::string FragmentShaderRGBATexAlphaMask::GetShaderString( @@ -1133,7 +1445,7 @@ std::string FragmentShaderRGBATexAlphaMask::GetShaderString( vec2(maskTexCoordOffset.x + v_texCoord.x * maskTexCoordScale.x, maskTexCoordOffset.y + v_texCoord.y * maskTexCoordScale.y); vec4 maskColor = TextureLookup(s_mask, maskTexCoord); - gl_FragColor = texColor * alpha * maskColor.w; + gl_FragColor = ApplyBlendMode(texColor * alpha * maskColor.w); } // clang-format off ); // NOLINT(whitespace/parens) @@ -1152,13 +1464,18 @@ void FragmentShaderRGBATexAlphaMaskAA::Init(GLES2Interface* context, unsigned program, int* base_uniform_index) { static const char* uniforms[] = { - "s_texture", "s_mask", "alpha", "maskTexCoordScale", "maskTexCoordOffset", + "s_texture", + "s_mask", + "alpha", + "maskTexCoordScale", + "maskTexCoordOffset", + BLEND_MODE_UNIFORMS, }; int locations[arraysize(uniforms)]; GetProgramUniformLocations(context, program, - arraysize(uniforms), + arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS, uniforms, locations, base_uniform_index); @@ -1167,6 +1484,7 @@ void FragmentShaderRGBATexAlphaMaskAA::Init(GLES2Interface* context, alpha_location_ = locations[2]; mask_tex_coord_scale_location_ = locations[3]; mask_tex_coord_offset_location_ = locations[4]; + BLEND_MODE_SET_LOCATIONS(locations, 5); } std::string FragmentShaderRGBATexAlphaMaskAA::GetShaderString( @@ -1193,7 +1511,7 @@ std::string FragmentShaderRGBATexAlphaMaskAA::GetShaderString( vec4 d4 = min(edge_dist[0], edge_dist[1]); vec2 d2 = min(d4.xz, d4.yw); float aa = clamp(gl_FragCoord.w * min(d2.x, d2.y), 0.0, 1.0); - gl_FragColor = texColor * alpha * maskColor.w * aa; + gl_FragColor = ApplyBlendMode(texColor * alpha * maskColor.w * aa); } // clang-format off ); // NOLINT(whitespace/parens) @@ -1222,12 +1540,13 @@ void FragmentShaderRGBATexAlphaMaskColorMatrixAA::Init( "maskTexCoordOffset", "colorMatrix", "colorOffset", + BLEND_MODE_UNIFORMS, }; int locations[arraysize(uniforms)]; GetProgramUniformLocations(context, program, - arraysize(uniforms), + arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS, uniforms, locations, base_uniform_index); @@ -1238,6 +1557,7 @@ void FragmentShaderRGBATexAlphaMaskColorMatrixAA::Init( mask_tex_coord_offset_location_ = locations[4]; color_matrix_location_ = locations[5]; color_offset_location_ = locations[6]; + BLEND_MODE_SET_LOCATIONS(locations, 7); } std::string FragmentShaderRGBATexAlphaMaskColorMatrixAA::GetShaderString( @@ -1271,7 +1591,7 @@ std::string FragmentShaderRGBATexAlphaMaskColorMatrixAA::GetShaderString( vec4 d4 = min(edge_dist[0], edge_dist[1]); vec2 d2 = min(d4.xz, d4.yw); float aa = clamp(gl_FragCoord.w * min(d2.x, d2.y), 0.0, 1.0); - gl_FragColor = texColor * alpha * maskColor.w * aa; + gl_FragColor = ApplyBlendMode(texColor * alpha * maskColor.w * aa); } // clang-format off ); // NOLINT(whitespace/parens) @@ -1290,13 +1610,13 @@ void FragmentShaderRGBATexAlphaColorMatrixAA::Init(GLES2Interface* context, unsigned program, int* base_uniform_index) { static const char* uniforms[] = { - "s_texture", "alpha", "colorMatrix", "colorOffset", + "s_texture", "alpha", "colorMatrix", "colorOffset", BLEND_MODE_UNIFORMS, }; int locations[arraysize(uniforms)]; GetProgramUniformLocations(context, program, - arraysize(uniforms), + arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS, uniforms, locations, base_uniform_index); @@ -1304,6 +1624,7 @@ void FragmentShaderRGBATexAlphaColorMatrixAA::Init(GLES2Interface* context, alpha_location_ = locations[1]; color_matrix_location_ = locations[2]; color_offset_location_ = locations[3]; + BLEND_MODE_SET_LOCATIONS(locations, 4); } std::string FragmentShaderRGBATexAlphaColorMatrixAA::GetShaderString( @@ -1330,7 +1651,7 @@ std::string FragmentShaderRGBATexAlphaColorMatrixAA::GetShaderString( vec4 d4 = min(edge_dist[0], edge_dist[1]); vec2 d2 = min(d4.xz, d4.yw); float aa = clamp(gl_FragCoord.w * min(d2.x, d2.y), 0.0, 1.0); - gl_FragColor = texColor * alpha * aa; + gl_FragColor = ApplyBlendMode(texColor * alpha * aa); } // clang-format off ); // NOLINT(whitespace/parens) @@ -1356,12 +1677,13 @@ void FragmentShaderRGBATexAlphaMaskColorMatrix::Init(GLES2Interface* context, "maskTexCoordOffset", "colorMatrix", "colorOffset", + BLEND_MODE_UNIFORMS, }; int locations[arraysize(uniforms)]; GetProgramUniformLocations(context, program, - arraysize(uniforms), + arraysize(uniforms) - UNUSED_BLEND_MODE_UNIFORMS, uniforms, locations, base_uniform_index); @@ -1372,6 +1694,7 @@ void FragmentShaderRGBATexAlphaMaskColorMatrix::Init(GLES2Interface* context, mask_tex_coord_offset_location_ = locations[4]; color_matrix_location_ = locations[5]; color_offset_location_ = locations[6]; + BLEND_MODE_SET_LOCATIONS(locations, 7); } std::string FragmentShaderRGBATexAlphaMaskColorMatrix::GetShaderString( @@ -1400,7 +1723,7 @@ std::string FragmentShaderRGBATexAlphaMaskColorMatrix::GetShaderString( vec2(maskTexCoordOffset.x + v_texCoord.x * maskTexCoordScale.x, maskTexCoordOffset.y + v_texCoord.y * maskTexCoordScale.y); vec4 maskColor = TextureLookup(s_mask, maskTexCoord); - gl_FragColor = texColor * alpha * maskColor.w; + gl_FragColor = ApplyBlendMode(texColor * alpha * maskColor.w); } // clang-format off ); // NOLINT(whitespace/parens) diff --git a/cc/output/shader.h b/cc/output/shader.h index 3039c80..0384366 100644 --- a/cc/output/shader.h +++ b/cc/output/shader.h @@ -38,6 +38,25 @@ enum SamplerType { NumSamplerTypes = 4 }; +enum BlendMode { + BlendModeNormal, + BlendModeOverlay, + BlendModeDarken, + BlendModeLighten, + BlendModeColorDodge, + BlendModeColorBurn, + BlendModeHardLight, + BlendModeSoftLight, + BlendModeDifference, + BlendModeExclusion, + BlendModeMultiply, + BlendModeHue, + BlendModeSaturation, + BlendModeColor, + BlendModeLuminosity, + NumBlendModes +}; + // Note: The highp_threshold_cache must be provided by the caller to make // the caching multi-thread/context safe in an easy low-overhead manner. // The caller must make sure to clear highp_threshold_cache to 0, so it can be @@ -279,7 +298,32 @@ class VertexShaderVideoTransform { DISALLOW_COPY_AND_ASSIGN(VertexShaderVideoTransform); }; -class FragmentTexAlphaBinding { +class FragmentTexBlendMode { + public: + int backdrop_location() const { return backdrop_location_; } + int backdrop_rect_location() const { return backdrop_rect_location_; } + + BlendMode blend_mode() const { return blend_mode_; } + void set_blend_mode(BlendMode blend_mode) { blend_mode_ = blend_mode; } + bool is_default_blend_mode() const { return blend_mode_ == BlendModeNormal; } + + protected: + FragmentTexBlendMode(); + + std::string SetBlendModeFunctions(std::string shader_string) const; + + int backdrop_location_; + int backdrop_rect_location_; + + private: + BlendMode blend_mode_; + + std::string GetHelperFunctions() const; + std::string GetBlendFunction() const; + std::string GetBlendFunctionBodyForRGB() const; +}; + +class FragmentTexAlphaBinding : public FragmentTexBlendMode { public: FragmentTexAlphaBinding(); @@ -297,7 +341,7 @@ class FragmentTexAlphaBinding { DISALLOW_COPY_AND_ASSIGN(FragmentTexAlphaBinding); }; -class FragmentTexColorMatrixAlphaBinding { +class FragmentTexColorMatrixAlphaBinding : public FragmentTexBlendMode { public: FragmentTexColorMatrixAlphaBinding(); @@ -317,7 +361,7 @@ class FragmentTexColorMatrixAlphaBinding { int color_offset_location_; }; -class FragmentTexOpaqueBinding { +class FragmentTexOpaqueBinding : public FragmentTexBlendMode { public: FragmentTexOpaqueBinding(); @@ -335,7 +379,7 @@ class FragmentTexOpaqueBinding { DISALLOW_COPY_AND_ASSIGN(FragmentTexOpaqueBinding); }; -class FragmentTexBackgroundBinding { +class FragmentTexBackgroundBinding : public FragmentTexBlendMode { public: FragmentTexBackgroundBinding(); @@ -417,7 +461,7 @@ class FragmentShaderRGBATexSwizzleOpaque : public FragmentTexOpaqueBinding { TexCoordPrecision precision, SamplerType sampler) const; }; -class FragmentShaderRGBATexAlphaAA { +class FragmentShaderRGBATexAlphaAA : public FragmentTexBlendMode { public: FragmentShaderRGBATexAlphaAA(); @@ -437,7 +481,7 @@ class FragmentShaderRGBATexAlphaAA { DISALLOW_COPY_AND_ASSIGN(FragmentShaderRGBATexAlphaAA); }; -class FragmentTexClampAlphaAABinding { +class FragmentTexClampAlphaAABinding : public FragmentTexBlendMode { public: FragmentTexClampAlphaAABinding(); @@ -473,7 +517,7 @@ class FragmentShaderRGBATexClampSwizzleAlphaAA TexCoordPrecision precision, SamplerType sampler) const; }; -class FragmentShaderRGBATexAlphaMask { +class FragmentShaderRGBATexAlphaMask : public FragmentTexBlendMode { public: FragmentShaderRGBATexAlphaMask(); std::string GetShaderString( @@ -502,7 +546,7 @@ class FragmentShaderRGBATexAlphaMask { DISALLOW_COPY_AND_ASSIGN(FragmentShaderRGBATexAlphaMask); }; -class FragmentShaderRGBATexAlphaMaskAA { +class FragmentShaderRGBATexAlphaMaskAA : public FragmentTexBlendMode { public: FragmentShaderRGBATexAlphaMaskAA(); std::string GetShaderString( @@ -531,7 +575,8 @@ class FragmentShaderRGBATexAlphaMaskAA { DISALLOW_COPY_AND_ASSIGN(FragmentShaderRGBATexAlphaMaskAA); }; -class FragmentShaderRGBATexAlphaMaskColorMatrixAA { +class FragmentShaderRGBATexAlphaMaskColorMatrixAA + : public FragmentTexBlendMode { public: FragmentShaderRGBATexAlphaMaskColorMatrixAA(); std::string GetShaderString( @@ -562,7 +607,7 @@ class FragmentShaderRGBATexAlphaMaskColorMatrixAA { int color_offset_location_; }; -class FragmentShaderRGBATexAlphaColorMatrixAA { +class FragmentShaderRGBATexAlphaColorMatrixAA : public FragmentTexBlendMode { public: FragmentShaderRGBATexAlphaColorMatrixAA(); std::string GetShaderString( @@ -583,7 +628,7 @@ class FragmentShaderRGBATexAlphaColorMatrixAA { int color_offset_location_; }; -class FragmentShaderRGBATexAlphaMaskColorMatrix { +class FragmentShaderRGBATexAlphaMaskColorMatrix : public FragmentTexBlendMode { public: FragmentShaderRGBATexAlphaMaskColorMatrix(); std::string GetShaderString( @@ -614,7 +659,7 @@ class FragmentShaderRGBATexAlphaMaskColorMatrix { int color_offset_location_; }; -class FragmentShaderYUVVideo { +class FragmentShaderYUVVideo : public FragmentTexBlendMode { public: FragmentShaderYUVVideo(); std::string GetShaderString( @@ -641,8 +686,7 @@ class FragmentShaderYUVVideo { DISALLOW_COPY_AND_ASSIGN(FragmentShaderYUVVideo); }; - -class FragmentShaderYUVAVideo { +class FragmentShaderYUVAVideo : public FragmentTexBlendMode { public: FragmentShaderYUVAVideo(); std::string GetShaderString( @@ -672,7 +716,7 @@ class FragmentShaderYUVAVideo { DISALLOW_COPY_AND_ASSIGN(FragmentShaderYUVAVideo); }; -class FragmentShaderColor { +class FragmentShaderColor : public FragmentTexBlendMode { public: FragmentShaderColor(); std::string GetShaderString( @@ -689,7 +733,7 @@ class FragmentShaderColor { DISALLOW_COPY_AND_ASSIGN(FragmentShaderColor); }; -class FragmentShaderColorAA { +class FragmentShaderColorAA : public FragmentTexBlendMode { public: FragmentShaderColorAA(); std::string GetShaderString( @@ -706,7 +750,7 @@ class FragmentShaderColorAA { DISALLOW_COPY_AND_ASSIGN(FragmentShaderColorAA); }; -class FragmentShaderCheckerboard { +class FragmentShaderCheckerboard : public FragmentTexBlendMode { public: FragmentShaderCheckerboard(); std::string GetShaderString( diff --git a/cc/output/software_renderer.cc b/cc/output/software_renderer.cc index c3cb828..acc897f 100644 --- a/cc/output/software_renderer.cc +++ b/cc/output/software_renderer.cc @@ -239,14 +239,16 @@ void SoftwareRenderer::DoDrawQuad(DrawingFrame* frame, const DrawQuad* quad) { current_canvas_->setMatrix(sk_device_matrix); current_paint_.reset(); - if (!IsScaleAndIntegerTranslate(sk_device_matrix)) { + if (settings_->force_antialiasing || + !IsScaleAndIntegerTranslate(sk_device_matrix)) { // TODO(danakj): Until we can enable AA only on exterior edges of the // layer, disable AA if any interior edges are present. crbug.com/248175 bool all_four_edges_are_exterior = quad->IsTopEdge() && quad->IsLeftEdge() && quad->IsBottomEdge() && quad->IsRightEdge(); - if (settings_->allow_antialiasing && all_four_edges_are_exterior) + if (settings_->allow_antialiasing && + (settings_->force_antialiasing || all_four_edges_are_exterior)) current_paint_.setAntiAlias(true); current_paint_.setFilterLevel(SkPaint::kLow_FilterLevel); } diff --git a/cc/test/data/background_filter_blur_off_axis.png b/cc/test/data/background_filter_blur_off_axis.png Binary files differindex b5777f4..050ec54 100644 --- a/cc/test/data/background_filter_blur_off_axis.png +++ b/cc/test/data/background_filter_blur_off_axis.png diff --git a/cc/test/data/blending_render_pass.png b/cc/test/data/blending_render_pass.png Binary files differnew file mode 100644 index 0000000..76fe369 --- /dev/null +++ b/cc/test/data/blending_render_pass.png diff --git a/cc/test/data/blending_render_pass_cm.png b/cc/test/data/blending_render_pass_cm.png Binary files differnew file mode 100644 index 0000000..9a72d44 --- /dev/null +++ b/cc/test/data/blending_render_pass_cm.png diff --git a/cc/test/data/blending_render_pass_mask.png b/cc/test/data/blending_render_pass_mask.png Binary files differnew file mode 100644 index 0000000..8b63f35 --- /dev/null +++ b/cc/test/data/blending_render_pass_mask.png diff --git a/cc/test/data/blending_render_pass_mask_cm.png b/cc/test/data/blending_render_pass_mask_cm.png Binary files differnew file mode 100644 index 0000000..4e210aa --- /dev/null +++ b/cc/test/data/blending_render_pass_mask_cm.png diff --git a/cc/test/render_pass_test_utils.cc b/cc/test/render_pass_test_utils.cc index 519c74a..c923b63 100644 --- a/cc/test/render_pass_test_utils.cc +++ b/cc/test/render_pass_test_utils.cc @@ -111,7 +111,8 @@ void AddRenderPassQuad(TestRenderPass* to_pass, TestRenderPass* contributing_pass, ResourceProvider::ResourceId mask_resource_id, const FilterOperations& filters, - gfx::Transform transform) { + gfx::Transform transform, + SkXfermode::Mode blend_mode) { gfx::Rect output_rect = contributing_pass->output_rect; SharedQuadState* shared_state = to_pass->CreateAndAppendSharedQuadState(); shared_state->SetAll(transform, @@ -120,7 +121,7 @@ void AddRenderPassQuad(TestRenderPass* to_pass, output_rect, false, 1, - SkXfermode::kSrcOver_Mode, + blend_mode, 0); RenderPassDrawQuad* quad = to_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>(); diff --git a/cc/test/render_pass_test_utils.h b/cc/test/render_pass_test_utils.h index 7bf026c..49ea113 100644 --- a/cc/test/render_pass_test_utils.h +++ b/cc/test/render_pass_test_utils.h @@ -53,7 +53,8 @@ void AddRenderPassQuad(TestRenderPass* toPass, TestRenderPass* contributing_pass, ResourceProvider::ResourceId mask_resource_id, const FilterOperations& filters, - gfx::Transform transform); + gfx::Transform transform, + SkXfermode::Mode blend_mode); } // namespace cc diff --git a/cc/trees/layer_tree_host_pixeltest_blending.cc b/cc/trees/layer_tree_host_pixeltest_blending.cc index f01d426..bebd612 100644 --- a/cc/trees/layer_tree_host_pixeltest_blending.cc +++ b/cc/trees/layer_tree_host_pixeltest_blending.cc @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "cc/layers/image_layer.h" #include "cc/layers/solid_color_layer.h" -#include "cc/layers/texture_layer.h" #include "cc/test/layer_tree_pixel_test.h" #include "cc/test/pixel_comparator.h" @@ -22,7 +22,36 @@ SkXfermode::Mode const kBlendModes[] = { SkXfermode::kHue_Mode, SkXfermode::kSaturation_Mode, SkXfermode::kColor_Mode, SkXfermode::kLuminosity_Mode}; +SkColor kCSSTestColors[] = { + 0xffff0000, // red + 0xff00ff00, // lime + 0xff0000ff, // blue + 0xff00ffff, // aqua + 0xffff00ff, // fuchsia + 0xffffff00, // yellow + 0xff008000, // green + 0xff800000, // maroon + 0xff000080, // navy + 0xff800080, // purple + 0xff808000, // olive + 0xff008080, // teal + 0xfffa8072, // salmon + 0xffc0c0c0, // silver + 0xff000000, // black + 0xff808080, // gray + 0x80000000, // black with transparency + 0xffffffff, // white + 0x80ffffff, // white with transparency + 0x00000000 // transparent +}; + const int kBlendModesCount = arraysize(kBlendModes); +const int kCSSTestColorsCount = arraysize(kCSSTestColors); + +using RenderPassOptions = uint32; +const uint32 kUseMasks = 1 << 0; +const uint32 kUseAntialiasing = 1 << 1; +const uint32 kUseColorMatrix = 1 << 2; class LayerTreeHostBlendingPixelTest : public LayerTreePixelTest { public: @@ -30,6 +59,10 @@ class LayerTreeHostBlendingPixelTest : public LayerTreePixelTest { pixel_comparator_.reset(new FuzzyPixelOffByOneComparator(true)); } + virtual void InitializeSettings(LayerTreeSettings* settings) override { + settings->force_antialiasing = force_antialiasing_; + } + protected: void RunBlendingWithRootPixelTestType(PixelTestType type) { const int kLaneWidth = 15; @@ -82,6 +115,175 @@ class LayerTreeHostBlendingPixelTest : public LayerTreePixelTest { root, base::FilePath(FILE_PATH_LITERAL("blending_transparent.png"))); } + + scoped_refptr<Layer> CreateColorfulBackdropLayer(int width, int height) { + // Draw the backdrop with horizontal lanes. + const int kLaneWidth = width; + const int kLaneHeight = height / kCSSTestColorsCount; + SkBitmap backing_store; + backing_store.allocN32Pixels(width, height); + SkCanvas canvas(backing_store); + canvas.clear(SK_ColorTRANSPARENT); + for (int i = 0; i < kCSSTestColorsCount; ++i) { + SkPaint paint; + paint.setColor(kCSSTestColors[i]); + canvas.drawRect( + SkRect::MakeXYWH(0, i * kLaneHeight, kLaneWidth, kLaneHeight), paint); + } + scoped_refptr<ImageLayer> layer = ImageLayer::Create(); + layer->SetIsDrawable(true); + layer->SetBounds(gfx::Size(width, height)); + layer->SetBitmap(backing_store); + return layer; + } + + void SetupMaskLayer(scoped_refptr<Layer> layer) { + const int kMaskOffset = 5; + gfx::Size bounds = layer->bounds(); + scoped_refptr<ImageLayer> mask = ImageLayer::Create(); + mask->SetIsDrawable(true); + mask->SetIsMask(true); + mask->SetBounds(bounds); + + SkBitmap bitmap; + bitmap.allocN32Pixels(bounds.width(), bounds.height()); + SkCanvas canvas(bitmap); + SkPaint paint; + paint.setColor(SK_ColorWHITE); + canvas.clear(SK_ColorTRANSPARENT); + canvas.drawRect(SkRect::MakeXYWH(kMaskOffset, + kMaskOffset, + bounds.width() - kMaskOffset * 2, + bounds.height() - kMaskOffset * 2), + paint); + mask->SetBitmap(bitmap); + layer->SetMaskLayer(mask.get()); + } + + void SetupColorMatrix(scoped_refptr<Layer> layer) { + FilterOperations filter_operations; + filter_operations.Append(FilterOperation::CreateSepiaFilter(1.f)); + layer->SetFilters(filter_operations); + } + + void CreateBlendingColorLayers(int width, + int height, + scoped_refptr<Layer> background, + RenderPassOptions flags) { + const int kLanesCount = kBlendModesCount + 4; + int lane_width = width / kLanesCount; + const SkColor kMiscOpaqueColor = 0xffc86464; + const SkColor kMiscTransparentColor = 0x80c86464; + const SkXfermode::Mode kCoeffBlendMode = SkXfermode::kScreen_Mode; + const SkXfermode::Mode kShaderBlendMode = SkXfermode::kColorBurn_Mode; + // add vertical lanes with each of the blend modes + for (int i = 0; i < kLanesCount; ++i) { + gfx::Rect child_rect(i * lane_width, 0, lane_width, height); + SkXfermode::Mode blend_mode = SkXfermode::kSrcOver_Mode; + float opacity = 1.f; + SkColor color = kMiscOpaqueColor; + + if (i < kBlendModesCount) { + blend_mode = kBlendModes[i]; + } else if (i == kBlendModesCount) { + blend_mode = kCoeffBlendMode; + opacity = 0.5f; + } else if (i == kBlendModesCount + 1) { + blend_mode = kCoeffBlendMode; + color = kMiscTransparentColor; + } else if (i == kBlendModesCount + 2) { + blend_mode = kShaderBlendMode; + opacity = 0.5f; + } else if (i == kBlendModesCount + 3) { + blend_mode = kShaderBlendMode; + color = kMiscTransparentColor; + } + + scoped_refptr<SolidColorLayer> lane = + CreateSolidColorLayer(child_rect, color); + lane->SetBlendMode(blend_mode); + lane->SetOpacity(opacity); + lane->SetForceRenderSurface(true); + if (flags & kUseMasks) + SetupMaskLayer(lane); + if (flags & kUseColorMatrix) { + SetupColorMatrix(lane); + } + background->AddChild(lane); + } + } + + void RunBlendingWithRenderPass(PixelTestType type, + const base::FilePath::CharType* expected_path, + RenderPassOptions flags) { + const int kRootSize = 400; + + scoped_refptr<SolidColorLayer> root = + CreateSolidColorLayer(gfx::Rect(kRootSize, kRootSize), SK_ColorWHITE); + scoped_refptr<Layer> background = + CreateColorfulBackdropLayer(kRootSize, kRootSize); + + background->SetIsRootForIsolatedGroup(true); + root->AddChild(background); + + CreateBlendingColorLayers(kRootSize, kRootSize, background.get(), flags); + + this->impl_side_painting_ = false; + this->force_antialiasing_ = (flags & kUseAntialiasing); + + if ((flags & kUseAntialiasing) && (type == GL_WITH_BITMAP)) { + // Anti aliasing causes differences up to 7 pixels at the edges. + int large_error_allowed = 7; + // Blending results might differ with one pixel. + int small_error_allowed = 1; + // Most of the errors are one pixel errors. + float percentage_pixels_small_error = 12.5f; + // Because of anti-aliasing, around 3% of pixels (at the edges) have + // bigger errors (from small_error_allowed + 1 to large_error_allowed). + float percentage_pixels_error = 15.0f; + // The average error is still close to 1. + float average_error_allowed_in_bad_pixels = 1.3f; + + // The sepia filter generates more small errors, but the number of large + // errors remains around 3%. + if (flags & kUseColorMatrix) { + percentage_pixels_small_error = 26.f; + percentage_pixels_error = 29.f; + } + + // Anti-aliased pixels in combination with non-separable blend modes and + // a white background produces several black pixels (6 for these tests). + // Having a mask will hide the black pixels. + // TODO(rosca): fix this issue for non-separable blend modes. + if (!(flags & kUseMasks)) + large_error_allowed = 255; + + pixel_comparator_.reset( + new FuzzyPixelComparator(false, // discard_alpha + percentage_pixels_error, + percentage_pixels_small_error, + average_error_allowed_in_bad_pixels, + large_error_allowed, + small_error_allowed)); + } else if ((flags & kUseColorMatrix) && (type == GL_WITH_BITMAP)) { + float percentage_pixels_error = 100.f; + float percentage_pixels_small_error = 0.f; + float average_error_allowed_in_bad_pixels = 1.f; + int large_error_allowed = 2; + int small_error_allowed = 0; + pixel_comparator_.reset( + new FuzzyPixelComparator(false, // discard_alpha + percentage_pixels_error, + percentage_pixels_small_error, + average_error_allowed_in_bad_pixels, + large_error_allowed, + small_error_allowed)); + } + + RunPixelTest(type, root, base::FilePath(expected_path)); + } + + bool force_antialiasing_ = false; }; TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRoot_GL) { @@ -128,6 +330,113 @@ TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithTransparent_Software) { RunBlendingWithTransparentPixelTestType(SOFTWARE_WITH_BITMAP); } +// Tests for render passes +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPass_GL) { + RunBlendingWithRenderPass( + GL_WITH_BITMAP, FILE_PATH_LITERAL("blending_render_pass.png"), 0); +} + +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPass_Software) { + RunBlendingWithRenderPass( + SOFTWARE_WITH_BITMAP, FILE_PATH_LITERAL("blending_render_pass.png"), 0); +} + +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassAA_GL) { + RunBlendingWithRenderPass(GL_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass.png"), + kUseAntialiasing); +} + +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassAA_Software) { + RunBlendingWithRenderPass(SOFTWARE_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass.png"), + kUseAntialiasing); +} + +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMask_GL) { + RunBlendingWithRenderPass(GL_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_mask.png"), + kUseMasks); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassWithMask_Software) { + RunBlendingWithRenderPass(SOFTWARE_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_mask.png"), + kUseMasks); +} + +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassWithMaskAA_GL) { + RunBlendingWithRenderPass(GL_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_mask.png"), + kUseMasks | kUseAntialiasing); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassWithMaskAA_Software) { + RunBlendingWithRenderPass(SOFTWARE_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_mask.png"), + kUseMasks | kUseAntialiasing); +} + +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassColorMatrix_GL) { + RunBlendingWithRenderPass(GL_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_cm.png"), + kUseColorMatrix); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassColorMatrix_Software) { + RunBlendingWithRenderPass(SOFTWARE_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_cm.png"), + kUseColorMatrix); +} + +TEST_F(LayerTreeHostBlendingPixelTest, BlendingWithRenderPassColorMatrixAA_GL) { + RunBlendingWithRenderPass(GL_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_cm.png"), + kUseAntialiasing | kUseColorMatrix); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassColorMatrixAA_Software) { + RunBlendingWithRenderPass(SOFTWARE_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_cm.png"), + kUseAntialiasing | kUseColorMatrix); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassWithMaskColorMatrix_GL) { + RunBlendingWithRenderPass( + GL_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_mask_cm.png"), + kUseMasks | kUseColorMatrix); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassWithMaskColorMatrix_Software) { + RunBlendingWithRenderPass( + SOFTWARE_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_mask_cm.png"), + kUseMasks | kUseColorMatrix); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassWithMaskColorMatrixAA_GL) { + RunBlendingWithRenderPass( + GL_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_mask_cm.png"), + kUseMasks | kUseAntialiasing | kUseColorMatrix); +} + +TEST_F(LayerTreeHostBlendingPixelTest, + BlendingWithRenderPassWithMaskColorMatrixAA_Software) { + RunBlendingWithRenderPass( + SOFTWARE_WITH_BITMAP, + FILE_PATH_LITERAL("blending_render_pass_mask_cm.png"), + kUseMasks | kUseAntialiasing | kUseColorMatrix); +} + } // namespace } // namespace cc diff --git a/cc/trees/layer_tree_settings.cc b/cc/trees/layer_tree_settings.cc index 8e11015..e4d5f1d 100644 --- a/cc/trees/layer_tree_settings.cc +++ b/cc/trees/layer_tree_settings.cc @@ -15,6 +15,7 @@ namespace cc { LayerTreeSettings::LayerTreeSettings() : impl_side_painting(false), allow_antialiasing(true), + force_antialiasing(false), throttle_frame_production(true), single_thread_proxy_scheduler(true), begin_frame_scheduling_enabled(false), diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h index c0c1453..fff1084 100644 --- a/cc/trees/layer_tree_settings.h +++ b/cc/trees/layer_tree_settings.h @@ -20,6 +20,7 @@ class CC_EXPORT LayerTreeSettings { bool impl_side_painting; bool allow_antialiasing; + bool force_antialiasing; bool throttle_frame_production; bool single_thread_proxy_scheduler; bool begin_frame_scheduling_enabled; |