Chromium Code Reviews| Index: cc/output/gl_renderer.cc |
| diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc |
| index f4b1e575d3920391c6e667c5bc230b1f0496730a..714b5379e74ae7319cca82c726d5aa6254821c57 100644 |
| --- a/cc/output/gl_renderer.cc |
| +++ b/cc/output/gl_renderer.cc |
| @@ -603,11 +603,128 @@ static SkBitmap ApplyImageFilter(GLRenderer* renderer, |
| return device.accessBitmap(false); |
| } |
| -scoped_ptr<ScopedResource> GLRenderer::DrawBackgroundFilters( |
| +static SkBitmap ApplyBlendModeWithBackdrop(GLRenderer* renderer, |
| + ContextProvider* offscreen_contexts, |
| + SkBitmap source_bitmap_with_filters, |
| + ScopedResource* source_texture_resource, |
| + ScopedResource* background_texture_resource, |
| + SkXfermode::Mode blendMode) { |
| + if (!offscreen_contexts || !offscreen_contexts->GrContext()) |
| + 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.getTexture()) { |
| + DCHECK(source_size.width() == source_bitmap_with_filters.width()); |
| + DCHECK(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(renderer->resource_provider(), |
| + source_texture_resource->id())); |
| + source_texture_with_filters_id = lock->texture_id(); |
| + } |
| + |
| + ResourceProvider::ScopedReadLockGL lock_background( |
| + renderer->resource_provider(), background_texture_resource->id()); |
| + |
| + // Flush the compositor context to ensure that textures there are available |
| + // in the shared context. Do this after locking/creating the compositor |
| + // texture. |
| + renderer->resource_provider()->Flush(); |
| + |
| + // Make sure skia uses the correct GL context. |
| + offscreen_contexts->Context3d()->makeContextCurrent(); |
| + |
| + // 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(offscreen_contexts->GrContext()->wrapBackendTexture( |
| + backend_texture_description)); |
| + |
| + 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(offscreen_contexts->GrContext()->wrapBackendTexture( |
| + backend_texture_description)); |
| + |
| + // Place the platform texture inside an SkBitmap. |
| + SkBitmap source; |
| + source.setConfig(SkBitmap::kARGB_8888_Config, |
| + source_size.width(), source_size.height()); |
| + skia::RefPtr<SkGrPixelRef> source_pixel_ref = |
| + skia::AdoptRef(new SkGrPixelRef(source_texture.get())); |
| + source.setPixelRef(source_pixel_ref.get()); |
| + |
| + SkBitmap background; |
| + background.setConfig(SkBitmap::kARGB_8888_Config, |
| + background_size.width(), background_size.height()); |
| + skia::RefPtr<SkGrPixelRef> background_pixel_ref = |
| + skia::AdoptRef(new SkGrPixelRef(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; |
| + GrAutoScratchTexture scratch_texture( |
| + offscreen_contexts->GrContext(), desc, GrContext::kExact_ScratchTexMatch); |
| + skia::RefPtr<GrTexture> backing_store = |
| + skia::AdoptRef(scratch_texture.detach()); |
| + |
| + // Create a device and canvas using that backing store. |
| + SkGpuDevice device(offscreen_contexts->GrContext(), backing_store.get()); |
| + SkCanvas canvas(&device); |
| + |
| + // Draw the source bitmap through the filter to the canvas. |
| + canvas.clear(SK_ColorTRANSPARENT); |
| + canvas.drawSprite(background, 0, 0); |
| + SkPaint paint; |
| + paint.setXfermodeMode(blendMode); |
| + canvas.drawSprite(source, 0, 0, &paint); |
| + |
| + // Flush skia context so that all the rendered stuff appears on the |
| + // texture. |
| + offscreen_contexts->GrContext()->flush(); |
| + |
| + // Flush the GL context so rendering results from this context are |
| + // visible in the compositor's context. |
| + offscreen_contexts->Context3d()->flush(); |
| + |
| + // Use the compositor's GL context again. |
| + renderer->Context()->makeContextCurrent(); |
| + |
| + return device.accessBitmap(false); |
| +} |
| + |
| +scoped_ptr<ScopedResource> GLRenderer::CreateBackgroundTextureWithFilters( |
| DrawingFrame* frame, |
| const RenderPassDrawQuad* quad, |
| const gfx::Transform& contents_device_transform, |
| - const gfx::Transform& contents_device_transform_inverse) { |
| + const gfx::Transform& contents_device_transform_inverse, |
| + bool& background_changed) { |
| // This method draws a background filter, which applies a filter to any pixels |
| // behind the quad and seen through its background. The algorithm works as |
| // follows: |
| @@ -633,13 +750,13 @@ scoped_ptr<ScopedResource> GLRenderer::DrawBackgroundFilters( |
| FilterOperations filters = |
| RenderSurfaceFilters::Optimize(quad->background_filters); |
| - DCHECK(!filters.IsEmpty()); |
| + DCHECK(!filters.IsEmpty() || quad->blend_mode != SkXfermode::kSrcOver_Mode); |
| // TODO(danakj): We only allow background filters on an opaque render surface |
| // because other surfaces may contain translucent pixels, and the contents |
| // behind those translucent pixels wouldn't have the filter applied. |
| - if (frame->current_render_pass->has_transparent_background) |
| - return scoped_ptr<ScopedResource>(); |
| + bool apply_background_filters = !filters.IsEmpty() && |
| + !frame->current_render_pass->has_transparent_background; |
| DCHECK(!frame->current_texture); |
| // TODO(danakj): Do a single readback for both the surface and replica and |
| @@ -663,22 +780,31 @@ scoped_ptr<ScopedResource> GLRenderer::DrawBackgroundFilters( |
| } else { |
| ResourceProvider::ScopedWriteLockGL lock(resource_provider_, |
| device_background_texture->id()); |
| - GetFramebufferTexture(lock.texture_id(), |
| - device_background_texture->format(), |
| - window_rect); |
| + GetFramebufferTextureSubImage(lock.texture_id(), |
| + gfx::Point(), |
| + window_rect); |
| } |
| - SkBitmap filtered_device_background = |
| - ApplyFilters(this, |
| - frame->offscreen_context_provider, |
| - filters, |
| - device_background_texture.get()); |
| - if (!filtered_device_background.getTexture()) |
| - return scoped_ptr<ScopedResource>(); |
| + int filtered_device_background_texture_id = 0; |
| - GrTexture* texture = |
| - reinterpret_cast<GrTexture*>(filtered_device_background.getTexture()); |
| - int filtered_device_background_texture_id = texture->getTextureHandle(); |
| + SkBitmap filtered_device_background; |
| + if (apply_background_filters) |
| + filtered_device_background = ApplyFilters(this, |
| + frame->offscreen_context_provider, filters, |
| + device_background_texture.get()); |
| + |
| + scoped_ptr<ResourceProvider::ScopedReadLockGL> lock; |
| + if (filtered_device_background.getTexture()) { |
| + GrTexture* texture = |
| + reinterpret_cast<GrTexture*>(filtered_device_background.getTexture()); |
| + filtered_device_background_texture_id = texture->getTextureHandle(); |
| + background_changed = true; |
| + } else { |
| + lock.reset(new ResourceProvider::ScopedReadLockGL(resource_provider_, |
| + device_background_texture->id())); |
| + filtered_device_background_texture_id = lock->texture_id(); |
| + background_changed = false; |
| + } |
| scoped_ptr<ScopedResource> background_texture = |
| ScopedResource::create(resource_provider_); |
| @@ -749,19 +875,22 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, |
| if (!contents_device_transform.GetInverse(&contents_device_transform_inverse)) |
| return; |
| + bool applyBlendMode = quad->blend_mode != SkXfermode::kSrcOver_Mode; |
| + bool backgroundChanged = false; |
| scoped_ptr<ScopedResource> background_texture; |
| - if (!quad->background_filters.IsEmpty()) { |
| + if (!quad->background_filters.IsEmpty() || applyBlendMode) { |
| // The pixels from the filtered background should completely replace the |
| // current pixel values. |
| bool disable_blending = blend_enabled(); |
| if (disable_blending) |
| SetBlendEnabled(false); |
| - background_texture = DrawBackgroundFilters( |
| + background_texture = CreateBackgroundTextureWithFilters( |
| frame, |
| quad, |
| contents_device_transform, |
| - contents_device_transform_inverse); |
| + contents_device_transform_inverse, |
| + backgroundChanged); |
| if (disable_blending) |
| SetBlendEnabled(true); |
| @@ -809,8 +938,14 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, |
| } |
| } |
| + if (background_texture && applyBlendMode) { |
| + filter_bitmap = ApplyBlendModeWithBackdrop(this, |
| + frame->offscreen_context_provider, filter_bitmap, |
| + contents_texture, background_texture.get(), quad->blend_mode); |
| + } |
| + |
| // Draw the background texture if there is one. |
| - if (background_texture) { |
| + if (background_texture && backgroundChanged) { |
| DCHECK(background_texture->size() == quad->rect.size()); |
| ResourceProvider::ScopedReadLockGL lock(resource_provider_, |
| background_texture->id()); |
| @@ -2460,6 +2595,28 @@ void GLRenderer::PassOnSkBitmap( |
| request->SendBitmapResult(bitmap.Pass()); |
| } |
| +void GLRenderer::GetFramebufferTextureSubImage(unsigned texture_id, |
| + gfx::Point offset, |
| + gfx::Rect window_rect) { |
| + DCHECK(texture_id); |
| + DCHECK_GE(window_rect.x(), 0); |
| + DCHECK_GE(window_rect.y(), 0); |
| + DCHECK_LE(window_rect.right(), current_surface_size_.width()); |
| + DCHECK_LE(window_rect.bottom(), current_surface_size_.height()); |
| + |
| + GLC(context_, context_->bindTexture(GL_TEXTURE_2D, texture_id)); |
| + GLC(context_, |
| + context_->copyTexSubImage2D(GL_TEXTURE_2D, |
|
shawnsingh
2013/09/25 05:33:53
Will this be certainly fast on all drivers/hardwar
rosca
2013/09/25 18:00:00
Are you referring to the copyTexSubImage2D functio
|
| + 0, |
| + offset.x(), |
| + offset.y(), |
| + window_rect.x(), |
| + window_rect.y(), |
| + window_rect.width(), |
| + window_rect.height())); |
| + GLC(context_, context_->bindTexture(GL_TEXTURE_2D, 0)); |
| +} |
| + |
| void GLRenderer::GetFramebufferTexture( |
| unsigned texture_id, ResourceFormat texture_format, gfx::Rect window_rect) { |
| DCHECK(texture_id); |