diff options
-rw-r--r-- | android_webview/native/DEPS | 1 | ||||
-rw-r--r-- | android_webview/native/aw_contents.cc | 231 | ||||
-rw-r--r-- | android_webview/native/aw_contents.h | 14 | ||||
-rw-r--r-- | android_webview/native/webview_native.gyp | 1 | ||||
-rw-r--r-- | android_webview/public/browser/draw_gl.h | 5 | ||||
-rw-r--r-- | cc/cc_tests.gyp | 2 | ||||
-rw-r--r-- | cc/delegated_renderer_layer_impl_unittest.cc | 35 | ||||
-rw-r--r-- | cc/direct_renderer.cc | 3 | ||||
-rw-r--r-- | cc/gl_renderer_pixeltest.cc | 1 | ||||
-rw-r--r-- | cc/gl_renderer_unittest.cc | 67 | ||||
-rw-r--r-- | cc/layer_tree_host_impl.cc | 5 | ||||
-rw-r--r-- | cc/layer_tree_host_impl.h | 1 | ||||
-rw-r--r-- | cc/layer_tree_host_impl_unittest.cc | 1 | ||||
-rw-r--r-- | cc/layer_tree_settings.cc | 1 | ||||
-rw-r--r-- | cc/layer_tree_settings.h | 1 | ||||
-rw-r--r-- | cc/renderer.h | 7 | ||||
-rw-r--r-- | cc/software_renderer_unittest.cc | 65 | ||||
-rw-r--r-- | cc/test/render_pass_test_utils.cc | 61 | ||||
-rw-r--r-- | cc/test/render_pass_test_utils.h | 44 | ||||
-rw-r--r-- | content/browser/renderer_host/compositor_impl_android.cc | 13 | ||||
-rw-r--r-- | content/browser/renderer_host/compositor_impl_android.h | 2 | ||||
-rw-r--r-- | content/public/browser/android/compositor.h | 3 |
22 files changed, 501 insertions, 63 deletions
diff --git a/android_webview/native/DEPS b/android_webview/native/DEPS index 04c6a9d..4f94198 100644 --- a/android_webview/native/DEPS +++ b/android_webview/native/DEPS @@ -2,4 +2,5 @@ include_rules = [ "+cc", "+content/public/browser", "+ui/gfx", + "+ui/gl", ] diff --git a/android_webview/native/aw_contents.cc b/android_webview/native/aw_contents.cc index 7cbcbc9..74edfa1 100644 --- a/android_webview/native/aw_contents.cc +++ b/android_webview/native/aw_contents.cc @@ -18,6 +18,7 @@ #include "base/android/jni_string.h" #include "base/bind.h" #include "base/callback.h" +#include "base/debug/trace_event.h" #include "base/message_loop.h" #include "base/pickle.h" #include "base/supports_user_data.h" @@ -33,6 +34,18 @@ #include "jni/AwContents_jni.h" #include "net/base/x509_certificate.h" #include "ui/gfx/transform.h" +#include "ui/gl/gl_bindings.h" + +// TODO(leandrogracia): remove when crbug.com/164140 is closed. +// Borrowed from gl2ext.h. Cannot be included due to conflicts with +// gl_bindings.h and the EGL library methods (eglGetCurrentContext). +#ifndef GL_TEXTURE_EXTERNAL_OES +#define GL_TEXTURE_EXTERNAL_OES 0x8D65 +#endif + +#ifndef GL_TEXTURE_BINDING_EXTERNAL_OES +#define GL_TEXTURE_BINDING_EXTERNAL_OES 0x8D67 +#endif using base::android::AttachCurrentThread; using base::android::ConvertJavaStringToUTF16; @@ -109,6 +122,7 @@ class NullCompositor : public content::Compositor { virtual void DeleteTexture(WebKit::WebGLId texture_id) OVERRIDE {} virtual void CopyTextureToBitmap(WebKit::WebGLId texture_id, gfx::JavaBitmap& bitmap) OVERRIDE {} + virtual void SetHasTransparentBackground(bool flag) OVERRIDE {} }; } // namespace @@ -127,7 +141,8 @@ AwContents::AwContents(JNIEnv* env, new AwWebContentsDelegate(env, web_contents_delegate)), view_visible_(false), compositor_visible_(false), - is_composite_pending_(false) { + is_composite_pending_(false), + last_frame_context_(NULL) { android_webview::AwBrowserDependencyFactory* dependency_factory = android_webview::AwBrowserDependencyFactory::GetInstance(); @@ -137,6 +152,17 @@ AwContents::AwContents(JNIEnv* env, SetWebContents(dependency_factory->CreateWebContents(private_browsing)); } +void AwContents::ResetCompositor() { + if (UseCompositorDirectDraw()) { + compositor_.reset(content::Compositor::Create(this)); + if (webview_layer_.get()) + AttachWebViewLayer(); + } else { + LOG(WARNING) << "Running on unsupported device: using null Compositor"; + compositor_.reset(new NullCompositor); + } +} + void AwContents::SetWebContents(content::WebContents* web_contents) { web_contents_.reset(web_contents); web_contents_->SetUserData(kAwContentsUserDataKey, @@ -144,12 +170,7 @@ void AwContents::SetWebContents(content::WebContents* web_contents) { web_contents_->SetDelegate(web_contents_delegate_.get()); render_view_host_ext_.reset(new AwRenderViewHostExt(web_contents_.get())); - if (UseCompositorDirectDraw()) { - compositor_.reset(content::Compositor::Create(this)); - } else { - LOG(WARNING) << "Running on unsupported device: using null Compositor"; - compositor_.reset(new NullCompositor); - } + ResetCompositor(); } void AwContents::SetWebContents(JNIEnv* env, jobject obj, jint new_wc) { @@ -164,24 +185,178 @@ AwContents::~AwContents() { } void AwContents::DrawGL(AwDrawGLInfo* draw_info) { - // TODO(joth): Use the |draw_info| parameters. - DLOG(INFO) << "Unimplemented AwContents::DrawGL params" - << " clip_left=" << draw_info->clip_left - << " clip_top=" << draw_info->clip_top - << " clip_right=" << draw_info->clip_right - << " clip_bottom=" << draw_info->clip_bottom; - if (compositor_visible_ != view_visible_) { - compositor_visible_ = view_visible_; - compositor_->SetVisible(compositor_visible_); - } - if (!compositor_visible_ || draw_info->mode == AwDrawGLInfo::kModeProcess) + TRACE_EVENT0("AwContents", "AwContents::DrawGL"); + + if (!webview_layer_.get() || draw_info->mode == AwDrawGLInfo::kModeProcess) return; DCHECK_EQ(draw_info->mode, AwDrawGLInfo::kModeDraw); + SetCompositorVisibility(view_visible_); + if (!compositor_visible_) + return; + + // TODO(leandrogracia): remove when crbug.com/164140 is closed. + // --------------------------------------------------------------------------- + GLint texture_external_oes_binding; + glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &texture_external_oes_binding); + + GLint vertex_array_buffer_binding; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &vertex_array_buffer_binding); + + GLint index_array_buffer_binding; + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &index_array_buffer_binding); + + GLint pack_alignment; + glGetIntegerv(GL_PACK_ALIGNMENT, &pack_alignment); + + GLint unpack_alignment; + glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_alignment); + + struct { + GLint enabled; + GLint size; + GLint type; + GLint normalized; + GLint stride; + GLvoid* pointer; + } vertex_attrib[3]; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(vertex_attrib); ++i) { + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, + &vertex_attrib[i].enabled); + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_SIZE, + &vertex_attrib[i].size); + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_TYPE, + &vertex_attrib[i].type); + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, + &vertex_attrib[i].normalized); + glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE, + &vertex_attrib[i].stride); + glGetVertexAttribPointerv(i, GL_VERTEX_ATTRIB_ARRAY_POINTER, + &vertex_attrib[i].pointer); + } + + GLboolean depth_test; + glGetBooleanv(GL_DEPTH_TEST, &depth_test); + + GLboolean cull_face; + glGetBooleanv(GL_CULL_FACE, &cull_face); + + GLboolean color_mask[4]; + glGetBooleanv(GL_COLOR_WRITEMASK, color_mask); + + GLboolean blend_enabled; + glGetBooleanv(GL_BLEND, &blend_enabled); + + GLint blend_src_rgb; + glGetIntegerv(GL_BLEND_SRC_RGB, &blend_src_rgb); + + GLint blend_src_alpha; + glGetIntegerv(GL_BLEND_SRC_ALPHA, &blend_src_alpha); + + GLint blend_dest_rgb; + glGetIntegerv(GL_BLEND_DST_RGB, &blend_dest_rgb); + + GLint blend_dest_alpha; + glGetIntegerv(GL_BLEND_DST_ALPHA, &blend_dest_alpha); + + GLint active_texture; + glGetIntegerv(GL_ACTIVE_TEXTURE, &active_texture); + + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT, viewport); + + GLboolean scissor_test; + glGetBooleanv(GL_SCISSOR_TEST, &scissor_test); + + GLint scissor_box[4]; + glGetIntegerv(GL_SCISSOR_BOX, scissor_box); + + GLint current_program; + glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program); + // --------------------------------------------------------------------------- + + // We need to watch if the current Android context has changed and enforce + // a clean-up in the compositor. + EGLContext current_context = eglGetCurrentContext(); + if (!current_context) { + LOG(WARNING) << "No current context attached. Skipping composite."; + return; + } + + if (last_frame_context_ != current_context) { + if (last_frame_context_) + ResetCompositor(); + last_frame_context_ = current_context; + } + + compositor_->SetWindowBounds(gfx::Size(draw_info->width, draw_info->height)); + compositor_->SetHasTransparentBackground(!draw_info->is_layer); + + gfx::Transform transform; + transform.matrix().setColMajorf(draw_info->transform); + webview_layer_->setTransform(transform); + compositor_->Composite(); is_composite_pending_ = false; + + // TODO(leandrogracia): remove when crbug.com/164140 is closed. + // --------------------------------------------------------------------------- + glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_external_oes_binding); + glBindBuffer(GL_ARRAY_BUFFER, vertex_array_buffer_binding); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_array_buffer_binding); + + glPixelStorei(GL_PACK_ALIGNMENT, pack_alignment); + glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment); + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(vertex_attrib); ++i) { + glVertexAttribPointer(i, vertex_attrib[i].size, + vertex_attrib[i].type, vertex_attrib[i].normalized, + vertex_attrib[i].stride, vertex_attrib[i].pointer); + + if (vertex_attrib[i].enabled) + glEnableVertexAttribArray(i); + else + glDisableVertexAttribArray(i); + } + + if (depth_test) + glEnable(GL_DEPTH_TEST); + else + glDisable(GL_DEPTH_TEST); + + if (cull_face) + glEnable(GL_CULL_FACE); + else + glDisable(GL_CULL_FACE); + + glColorMask(color_mask[0], color_mask[1], color_mask[2], + color_mask[3]); + + if (blend_enabled) + glEnable(GL_BLEND); + else + glDisable(GL_BLEND); + + glBlendFuncSeparate(blend_src_rgb, blend_dest_rgb, + blend_src_alpha, blend_dest_alpha); + + glActiveTexture(active_texture); + + glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + + if (scissor_test) + glEnable(GL_SCISSOR_TEST); + else + glDisable(GL_SCISSOR_TEST); + + glScissor(scissor_box[0], scissor_box[1], scissor_box[2], + scissor_box[3]); + + glUseProgram(current_program); + // --------------------------------------------------------------------------- } jint AwContents::GetWebContents(JNIEnv* env, jobject obj) { @@ -192,7 +367,14 @@ void AwContents::DidInitializeContentViewCore(JNIEnv* env, jobject obj, jint content_view_core) { ContentViewCore* core = reinterpret_cast<ContentViewCore*>(content_view_core); DCHECK(core == ContentViewCore::FromWebContents(web_contents_.get())); - compositor_->SetRootLayer(core->GetLayer()); + webview_layer_ = cc::Layer::create(); + webview_layer_->addChild(core->GetLayer()); + AttachWebViewLayer(); +} + +void AwContents::AttachWebViewLayer() { + DCHECK(webview_layer_.get()); + compositor_->SetRootLayer(webview_layer_.get()); Invalidate(); } @@ -403,7 +585,7 @@ void AwContents::OnFindResultReceived(int active_ordinal, } void AwContents::ScheduleComposite() { - // TODO(joth): Call back out to framework attachFunctor (Java side) from here. + TRACE_EVENT0("AwContents", "AwContents::ScheduleComposite"); Invalidate(); } @@ -421,6 +603,13 @@ void AwContents::Invalidate() { Java_AwContents_invalidate(env, obj.obj()); } +void AwContents::SetCompositorVisibility(bool visible) { + if (compositor_visible_ != visible) { + compositor_visible_ = visible; + compositor_->SetVisible(compositor_visible_); + } +} + void AwContents::OnSwapBuffersCompleted() { } @@ -505,7 +694,7 @@ void AwContents::OnAttachedToWindow(JNIEnv* env, jobject obj, int w, int h) { void AwContents::OnDetachedFromWindow(JNIEnv* env, jobject obj) { view_visible_ = false; - // TODO(joth): Request a DrawGL (kModeProcess) call, to tell the compositor. + SetCompositorVisibility(false); } base::android::ScopedJavaLocalRef<jbyteArray> diff --git a/android_webview/native/aw_contents.h b/android_webview/native/aw_contents.h index 6a542c9..410f5d5 100644 --- a/android_webview/native/aw_contents.h +++ b/android_webview/native/aw_contents.h @@ -16,6 +16,7 @@ #include "content/public/browser/android/compositor.h" #include "content/public/browser/javascript_dialogs.h" +typedef void* EGLContext; class TabContents; namespace cc { @@ -121,18 +122,27 @@ class AwContents : public FindHelper::Listener, private: void Invalidate(); void SetWebContents(content::WebContents* web_contents); + void SetCompositorVisibility(bool visible); + void ResetCompositor(); + void AttachWebViewLayer(); JavaObjectWeakGlobalRef java_ref_; scoped_ptr<content::WebContents> web_contents_; scoped_ptr<AwWebContentsDelegate> web_contents_delegate_; scoped_ptr<AwRenderViewHostExt> render_view_host_ext_; scoped_ptr<FindHelper> find_helper_; + scoped_ptr<content::WebContents> pending_contents_; + + // Compositor-specific state. scoped_ptr<content::Compositor> compositor_; - // State to track if the view is visible, and if the compositor knows yet. + scoped_refptr<cc::Layer> webview_layer_; bool view_visible_; bool compositor_visible_; bool is_composite_pending_; - scoped_ptr<content::WebContents> pending_contents_; + + // Used only for detecting Android View System context changes. + // Not to be used between draw calls. + EGLContext last_frame_context_; DISALLOW_COPY_AND_ASSIGN(AwContents); }; diff --git a/android_webview/native/webview_native.gyp b/android_webview/native/webview_native.gyp index 865b870..a4b62f6 100644 --- a/android_webview/native/webview_native.gyp +++ b/android_webview/native/webview_native.gyp @@ -13,6 +13,7 @@ '../../base/base.gyp:base_static', '../../content/content.gyp:web_contents_delegate_android', '../../skia/skia.gyp:skia', + '../../ui/gl/gl.gyp:gl', 'android_webview_native_jni', ], 'include_dirs': [ diff --git a/android_webview/public/browser/draw_gl.h b/android_webview/public/browser/draw_gl.h index 75a3a61..8bea559 100644 --- a/android_webview/public/browser/draw_gl.h +++ b/android_webview/public/browser/draw_gl.h @@ -38,7 +38,10 @@ struct AwDrawGLInfo { kStatusMaskDone = 0x0, kStatusMaskDraw = 0x1, kStatusMaskInvoke = 0x2, - } status_mask; + }; + + // Output: mask indicating the status after calling the functor. + unsigned int status_mask; // Output: dirty region to redraw. float dirty_left; diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp index 1781533..58661a0 100644 --- a/cc/cc_tests.gyp +++ b/cc/cc_tests.gyp @@ -100,6 +100,8 @@ 'test/pixel_test_output_surface.h', 'test/render_pass_test_common.cc', 'test/render_pass_test_common.h', + 'test/render_pass_test_utils.cc', + 'test/render_pass_test_utils.h', 'test/scheduler_test_common.cc', 'test/scheduler_test_common.h', 'test/tiled_layer_test_common.cc', diff --git a/cc/delegated_renderer_layer_impl_unittest.cc b/cc/delegated_renderer_layer_impl_unittest.cc index 9106831..68e47a9 100644 --- a/cc/delegated_renderer_layer_impl_unittest.cc +++ b/cc/delegated_renderer_layer_impl_unittest.cc @@ -20,6 +20,7 @@ #include "cc/test/geometry_test_utils.h" #include "cc/test/mock_quad_culler.h" #include "cc/test/render_pass_test_common.h" +#include "cc/test/render_pass_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/transform.h" @@ -51,40 +52,6 @@ protected: scoped_ptr<LayerTreeHostImpl> m_hostImpl; }; -static TestRenderPass* addRenderPass(ScopedPtrVector<RenderPass>& passList, RenderPass::Id id, gfx::Rect outputRect, gfx::Transform rootTransform) -{ - scoped_ptr<TestRenderPass> pass(TestRenderPass::Create()); - pass->SetNew(id, outputRect, outputRect, rootTransform); - TestRenderPass* saved = pass.get(); - passList.append(pass.PassAs<RenderPass>()); - return saved; -} - -static SolidColorDrawQuad* addQuad(TestRenderPass* pass, gfx::Rect rect, SkColor color) -{ - MockQuadCuller quadSink(pass->quad_list, pass->shared_quad_state_list); - AppendQuadsData data(pass->id); - SharedQuadState* sharedState = quadSink.useSharedQuadState(SharedQuadState::Create()); - sharedState->SetAll(gfx::Transform(), rect, rect, rect, false, 1); - scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create(); - quad->SetNew(sharedState, rect, color); - SolidColorDrawQuad* quadPtr = quad.get(); - quadSink.append(quad.PassAs<DrawQuad>(), data); - return quadPtr; -} - -static void addRenderPassQuad(TestRenderPass* toPass, TestRenderPass* contributingPass) -{ - MockQuadCuller quadSink(toPass->quad_list, toPass->shared_quad_state_list); - AppendQuadsData data(toPass->id); - gfx::Rect outputRect = contributingPass->output_rect; - SharedQuadState* sharedState = quadSink.useSharedQuadState(SharedQuadState::Create()); - sharedState->SetAll(gfx::Transform(), outputRect, outputRect, outputRect, false, 1); - scoped_ptr<RenderPassDrawQuad> quad = RenderPassDrawQuad::Create(); - quad->SetNew(sharedState, outputRect, contributingPass->id, false, 0, outputRect, gfx::RectF()); - quadSink.append(quad.PassAs<DrawQuad>(), data); -} - class DelegatedRendererLayerImplTestSimple : public DelegatedRendererLayerImplTest { public: DelegatedRendererLayerImplTestSimple() diff --git a/cc/direct_renderer.cc b/cc/direct_renderer.cc index 1682e91..d5dc975 100644 --- a/cc/direct_renderer.cc +++ b/cc/direct_renderer.cc @@ -238,7 +238,8 @@ void DirectRenderer::drawRenderPass(DrawingFrame& frame, const RenderPass* rende setScissorTestRect(moveScissorToWindowSpace(frame, renderPassScissor)); } - clearFramebuffer(frame); + if (frame.currentRenderPass != frame.rootRenderPass || m_client->shouldClearRootRenderPass()) + clearFramebuffer(frame); const QuadList& quadList = renderPass->quad_list; for (QuadList::constBackToFrontIterator it = quadList.backToFrontBegin(); it != quadList.backToFrontEnd(); ++it) { diff --git a/cc/gl_renderer_pixeltest.cc b/cc/gl_renderer_pixeltest.cc index ee2a015..6d35668 100644 --- a/cc/gl_renderer_pixeltest.cc +++ b/cc/gl_renderer_pixeltest.cc @@ -41,6 +41,7 @@ class FakeRendererClient : public RendererClient { virtual void enforceManagedMemoryPolicy( const ManagedMemoryPolicy&) OVERRIDE {} virtual bool hasImplThread() const OVERRIDE { return false; } + virtual bool shouldClearRootRenderPass() const { return true; } }; class GLRendererPixelTest : public testing::Test { diff --git a/cc/gl_renderer_unittest.cc b/cc/gl_renderer_unittest.cc index 8521627..5eee6ac 100644 --- a/cc/gl_renderer_unittest.cc +++ b/cc/gl_renderer_unittest.cc @@ -12,6 +12,7 @@ #include "cc/test/fake_output_surface.h" #include "cc/test/fake_web_graphics_context_3d.h" #include "cc/test/render_pass_test_common.h" +#include "cc/test/render_pass_test_utils.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/khronos/GLES2/gl2.h" @@ -22,6 +23,8 @@ using namespace WebKitTests; using testing::_; using testing::AnyNumber; +using testing::AtLeast; +using testing::Expectation; using testing::InSequence; using testing::Mock; @@ -82,6 +85,7 @@ public: virtual void setManagedMemoryPolicy(const ManagedMemoryPolicy& policy) OVERRIDE { m_memoryAllocationLimitBytes = policy.bytesLimitWhenVisible; } virtual void enforceManagedMemoryPolicy(const ManagedMemoryPolicy& policy) OVERRIDE { if (m_lastCallWasSetVisibility) *m_lastCallWasSetVisibility = false; } virtual bool hasImplThread() const OVERRIDE { return false; } + virtual bool shouldClearRootRenderPass() const OVERRIDE { return true; } // Methods added for test. int setFullRootLayerDamageCount() const { return m_setFullRootLayerDamageCount; } @@ -578,5 +582,68 @@ TEST(GLRendererTest2, activeTextureState) Mock::VerifyAndClearExpectations(context); } +class NoClearRootRenderPassFakeClient : public FakeRendererClient { +public: + virtual bool shouldClearRootRenderPass() const { return false; } +}; + +class NoClearRootRenderPassMockContext : public FakeWebGraphicsContext3D { +public: + MOCK_METHOD1(clear, void(WGC3Dbitfield mask)); + MOCK_METHOD4(drawElements, void(WGC3Denum mode, WGC3Dsizei count, WGC3Denum type, WGC3Dintptr offset)); +}; + +TEST(GLRendererTest2, shouldClearRootRenderPass) +{ + NoClearRootRenderPassFakeClient mockClient; + scoped_ptr<OutputSurface> outputSurface(FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(new NoClearRootRenderPassMockContext))); + NoClearRootRenderPassMockContext* mockContext = static_cast<NoClearRootRenderPassMockContext*>(outputSurface->Context3D()); + + scoped_ptr<ResourceProvider> resourceProvider(ResourceProvider::create(outputSurface.get())); + FakeRendererGL renderer(&mockClient, resourceProvider.get()); + EXPECT_TRUE(renderer.initialize()); + + gfx::Rect viewportRect(mockClient.deviceViewportSize()); + ScopedPtrVector<RenderPass> renderPasses; + + RenderPass::Id rootPassId(1, 0); + TestRenderPass* rootPass = addRenderPass(renderPasses, rootPassId, viewportRect, gfx::Transform()); + addQuad(rootPass, viewportRect, SK_ColorGREEN); + + RenderPass::Id childPassId(2, 0); + TestRenderPass* childPass = addRenderPass(renderPasses, childPassId, viewportRect, gfx::Transform()); + addQuad(childPass, viewportRect, SK_ColorBLUE); + + addRenderPassQuad(rootPass, childPass); + + mockClient.renderPassesInDrawOrder().clear(); + mockClient.renderPassesInDrawOrder().push_back(childPass); + mockClient.renderPassesInDrawOrder().push_back(rootPass); + mockClient.renderPasses().set(rootPassId, renderPasses.take(0)); + mockClient.renderPasses().set(childPassId, renderPasses.take(1)); + + // First render pass is not the root one, clearing should happen. + EXPECT_CALL(*mockContext, clear(GL_COLOR_BUFFER_BIT)) + .Times(AtLeast(1)); + + Expectation firstRenderPass = EXPECT_CALL(*mockContext, drawElements(_, _, _, _)) + .Times(1); + + // The second render pass is the root one, clearing should be prevented. + EXPECT_CALL(*mockContext, clear(GL_COLOR_BUFFER_BIT)) + .Times(0) + .After(firstRenderPass); + + EXPECT_CALL(*mockContext, drawElements(_, _, _, _)) + .Times(AnyNumber()) + .After(firstRenderPass); + + renderer.decideRenderPassAllocationsForFrame(mockClient.renderPassesInDrawOrder()); + renderer.drawFrame(mockClient.renderPassesInDrawOrder(), mockClient.renderPasses()); + + // In multiple render passes all but the root pass should clear the framebuffer. + Mock::VerifyAndClearExpectations(&mockContext); +} + } // namespace } // namespace cc diff --git a/cc/layer_tree_host_impl.cc b/cc/layer_tree_host_impl.cc index 89ebdde..8ac47bc 100644 --- a/cc/layer_tree_host_impl.cc +++ b/cc/layer_tree_host_impl.cc @@ -829,6 +829,11 @@ void LayerTreeHostImpl::ScheduleCheckForCompletedSetPixels() m_client->setNeedsRedrawOnImplThread(); } +bool LayerTreeHostImpl::shouldClearRootRenderPass() const +{ + return m_settings.shouldClearRootRenderPass; +} + void LayerTreeHostImpl::setManagedMemoryPolicy(const ManagedMemoryPolicy& policy) { if (m_managedMemoryPolicy == policy) diff --git a/cc/layer_tree_host_impl.h b/cc/layer_tree_host_impl.h index f591080..9943da2 100644 --- a/cc/layer_tree_host_impl.h +++ b/cc/layer_tree_host_impl.h @@ -170,6 +170,7 @@ public: virtual void setManagedMemoryPolicy(const ManagedMemoryPolicy& policy) OVERRIDE; virtual void enforceManagedMemoryPolicy(const ManagedMemoryPolicy& policy) OVERRIDE; virtual bool hasImplThread() const OVERRIDE; + virtual bool shouldClearRootRenderPass() const OVERRIDE; // TileManagerClient implementation. virtual void ScheduleManageTiles() OVERRIDE; diff --git a/cc/layer_tree_host_impl_unittest.cc b/cc/layer_tree_host_impl_unittest.cc index a2846b5..8f52e9d 100644 --- a/cc/layer_tree_host_impl_unittest.cc +++ b/cc/layer_tree_host_impl_unittest.cc @@ -4228,6 +4228,7 @@ public: virtual void setManagedMemoryPolicy(const ManagedMemoryPolicy& policy) OVERRIDE { } virtual void enforceManagedMemoryPolicy(const ManagedMemoryPolicy& policy) OVERRIDE { } virtual bool hasImplThread() const OVERRIDE { return false; } + virtual bool shouldClearRootRenderPass() const OVERRIDE { return true; } protected: TestRenderer(ResourceProvider* resourceProvider, Proxy* proxy) : GLRenderer(this, resourceProvider) { } diff --git a/cc/layer_tree_settings.cc b/cc/layer_tree_settings.cc index 27723fc..4ef2b7b 100644 --- a/cc/layer_tree_settings.cc +++ b/cc/layer_tree_settings.cc @@ -24,6 +24,7 @@ LayerTreeSettings::LayerTreeSettings() , backgroundColorInsteadOfCheckerboard(false) , showOverdrawInTracing(false) , canUseLCDText(true) + , shouldClearRootRenderPass(true) , refreshRate(0) , maxPartialTextureUpdates(std::numeric_limits<size_t>::max()) , numRasterThreads(1) diff --git a/cc/layer_tree_settings.h b/cc/layer_tree_settings.h index e10e0d3..e5ca724 100644 --- a/cc/layer_tree_settings.h +++ b/cc/layer_tree_settings.h @@ -27,6 +27,7 @@ class CC_EXPORT LayerTreeSettings { bool backgroundColorInsteadOfCheckerboard; bool showOverdrawInTracing; bool canUseLCDText; + bool shouldClearRootRenderPass; double refreshRate; size_t maxPartialTextureUpdates; size_t numRasterThreads; diff --git a/cc/renderer.h b/cc/renderer.h index 87346aa..2adf7c0 100644 --- a/cc/renderer.h +++ b/cc/renderer.h @@ -26,6 +26,7 @@ public: virtual void setManagedMemoryPolicy(const ManagedMemoryPolicy& policy) = 0; virtual void enforceManagedMemoryPolicy(const ManagedMemoryPolicy& policy) = 0; virtual bool hasImplThread() const = 0; + virtual bool shouldClearRootRenderPass() const = 0; protected: virtual ~RendererClient() { } }; @@ -38,9 +39,9 @@ public: const LayerTreeSettings& settings() const { return m_client->settings(); } - gfx::Size viewportSize() { return m_client->deviceViewportSize(); } - int viewportWidth() { return viewportSize().width(); } - int viewportHeight() { return viewportSize().height(); } + gfx::Size viewportSize() const { return m_client->deviceViewportSize(); } + int viewportWidth() const { return viewportSize().width(); } + int viewportHeight() const { return viewportSize().height(); } virtual void viewportChanged() { } virtual void receiveCompositorFrameAck(const CompositorFrameAck&) { } diff --git a/cc/software_renderer_unittest.cc b/cc/software_renderer_unittest.cc index 529d9c7..f61a7e3 100644 --- a/cc/software_renderer_unittest.cc +++ b/cc/software_renderer_unittest.cc @@ -13,6 +13,7 @@ #include "cc/test/fake_software_output_device.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/render_pass_test_common.h" +#include "cc/test/render_pass_test_utils.h" #include "cc/tile_draw_quad.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -25,6 +26,11 @@ namespace { class SoftwareRendererTest : public testing::Test, public RendererClient { public: + SoftwareRendererTest() + : m_shouldClearRootRenderPass(true) + { + } + void initializeRenderer() { m_outputSurface = FakeOutputSurface::CreateSoftware(scoped_ptr<SoftwareOutputDevice>(new FakeSoftwareOutputDevice)); m_resourceProvider = ResourceProvider::create(m_outputSurface.get()); @@ -35,7 +41,8 @@ public: FakeOutputSurface* outputSurface() const { return m_outputSurface.get(); } ResourceProvider* resourceProvider() const { return m_resourceProvider.get(); } SoftwareRenderer* renderer() const { return m_renderer.get(); } - void setViewportSize(gfx::Size viewportSize) { m_viewportSize = viewportSize; } + void setViewportSize(const gfx::Size& viewportSize) { m_viewportSize = viewportSize; } + void setShouldClearRootRenderPass(bool clearRootRenderPass) { m_shouldClearRootRenderPass = clearRootRenderPass; } // RendererClient implementation. virtual const gfx::Size& deviceViewportSize() const OVERRIDE { return m_viewportSize; } @@ -46,6 +53,7 @@ public: virtual void setManagedMemoryPolicy(const ManagedMemoryPolicy& policy) OVERRIDE { }; virtual void enforceManagedMemoryPolicy(const ManagedMemoryPolicy& policy) OVERRIDE { }; virtual bool hasImplThread() const OVERRIDE { return false; } + virtual bool shouldClearRootRenderPass() const OVERRIDE { return m_shouldClearRootRenderPass; } protected: scoped_ptr<FakeOutputSurface> m_outputSurface; @@ -53,6 +61,7 @@ protected: scoped_ptr<SoftwareRenderer> m_renderer; gfx::Size m_viewportSize; LayerTreeSettings m_settings; + bool m_shouldClearRootRenderPass; }; TEST_F(SoftwareRendererTest, solidColorQuad) @@ -154,5 +163,59 @@ TEST_F(SoftwareRendererTest, tileQuad) EXPECT_EQ(SK_ColorCYAN, pixels[outerPixels - outerSize.width() - 2]); } +TEST_F(SoftwareRendererTest, shouldClearRootRenderPass) +{ + gfx::Rect viewportRect(gfx::Size(100, 100)); + size_t viewportPixels = viewportRect.width() * viewportRect.height(); + setViewportSize(viewportRect.size()); + setShouldClearRootRenderPass(false); + initializeRenderer(); + + RenderPassList list; + RenderPassIdHashMap hashmap; + ScopedPtrVector<RenderPass> renderPasses; + scoped_array<SkColor> pixels(new SkColor[viewportPixels]); + + // Draw a fullscreen green quad in a first frame. + RenderPass::Id rootClearPassId(1, 0); + TestRenderPass* rootClearPass = addRenderPass(renderPasses, rootClearPassId, viewportRect, gfx::Transform()); + addQuad(rootClearPass, viewportRect, SK_ColorGREEN); + + list.push_back(rootClearPass); + hashmap.set(rootClearPassId, renderPasses.take(0)); + + renderer()->decideRenderPassAllocationsForFrame(list); + renderer()->drawFrame(list, hashmap); + renderer()->getFramebufferPixels(pixels.get(), viewportRect); + + EXPECT_EQ(SK_ColorGREEN, pixels[0]); + EXPECT_EQ(SK_ColorGREEN, pixels[viewportPixels - 1]); + + renderPasses.clear(); + hashmap.clear(); + list.clear(); + + // Draw a smaller magenta rect without filling the viewport in a separate frame. + gfx::Rect smallerRect(20, 20, 60, 60); + + RenderPass::Id rootSmallerPassId(2, 0); + TestRenderPass* rootSmallerPass = addRenderPass(renderPasses, rootSmallerPassId, viewportRect, gfx::Transform()); + addQuad(rootSmallerPass, smallerRect, SK_ColorMAGENTA); + + list.push_back(rootSmallerPass); + hashmap.set(rootSmallerPassId, renderPasses.take(0)); + + renderer()->decideRenderPassAllocationsForFrame(list); + renderer()->drawFrame(list, hashmap); + renderer()->getFramebufferPixels(pixels.get(), viewportRect); + + // If we didn't clear, the borders should still be green. + EXPECT_EQ(SK_ColorGREEN, pixels[0]); + EXPECT_EQ(SK_ColorGREEN, pixels[viewportPixels - 1]); + + EXPECT_EQ(SK_ColorMAGENTA, pixels[smallerRect.y() * viewportRect.width() + smallerRect.x()]); + EXPECT_EQ(SK_ColorMAGENTA, pixels[(smallerRect.bottom() - 1) * viewportRect.width() + smallerRect.right() - 1]); +} + } // namespace } // namespace cc diff --git a/cc/test/render_pass_test_utils.cc b/cc/test/render_pass_test_utils.cc new file mode 100644 index 0000000..579bc1e --- /dev/null +++ b/cc/test/render_pass_test_utils.cc @@ -0,0 +1,61 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "cc/test/render_pass_test_utils.h" + +#include "cc/append_quads_data.h" +#include "cc/quad_sink.h" +#include "cc/render_pass_draw_quad.h" +#include "cc/shared_quad_state.h" +#include "cc/solid_color_draw_quad.h" +#include "cc/test/mock_quad_culler.h" +#include "cc/test/render_pass_test_common.h" +#include "ui/gfx/rect.h" + +using WebKitTests::TestRenderPass; + +namespace cc { + +TestRenderPass* addRenderPass(ScopedPtrVector<RenderPass>& passList, + RenderPass::Id id, + const gfx::Rect& outputRect, + const gfx::Transform& rootTransform) { + scoped_ptr<TestRenderPass> pass(TestRenderPass::Create()); + pass->SetNew(id, outputRect, outputRect, rootTransform); + TestRenderPass* saved = pass.get(); + passList.append(pass.PassAs<RenderPass>()); + return saved; +} + +SolidColorDrawQuad* addQuad(TestRenderPass* pass, + const gfx::Rect& rect, + SkColor color) { + MockQuadCuller quadSink(pass->quad_list, pass->shared_quad_state_list); + AppendQuadsData data(pass->id); + SharedQuadState* sharedState = + quadSink.useSharedQuadState(SharedQuadState::Create()); + sharedState->SetAll(gfx::Transform(), rect, rect, rect, false, 1); + scoped_ptr<SolidColorDrawQuad> quad = SolidColorDrawQuad::Create(); + quad->SetNew(sharedState, rect, color); + SolidColorDrawQuad* quadPtr = quad.get(); + quadSink.append(quad.PassAs<DrawQuad>(), data); + return quadPtr; +} + +void addRenderPassQuad(TestRenderPass* toPass, + TestRenderPass* contributingPass) { + MockQuadCuller quadSink(toPass->quad_list, toPass->shared_quad_state_list); + AppendQuadsData data(toPass->id); + gfx::Rect outputRect = contributingPass->output_rect; + SharedQuadState* sharedState = + quadSink.useSharedQuadState(SharedQuadState::Create()); + sharedState->SetAll(gfx::Transform(), outputRect, outputRect, outputRect, + false, 1); + scoped_ptr<RenderPassDrawQuad> quad = RenderPassDrawQuad::Create(); + quad->SetNew(sharedState, outputRect, contributingPass->id, false, 0, + outputRect, gfx::RectF()); + quadSink.append(quad.PassAs<DrawQuad>(), data); +} + +} // namespace cc diff --git a/cc/test/render_pass_test_utils.h b/cc/test/render_pass_test_utils.h new file mode 100644 index 0000000..c06fbd6 --- /dev/null +++ b/cc/test/render_pass_test_utils.h @@ -0,0 +1,44 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CC_TEST_RENDER_PASS_TEST_UTILS_H_ +#define CC_TEST_RENDER_PASS_TEST_UTILS_H_ + +#include "cc/render_pass.h" +#include "cc/scoped_ptr_vector.h" +#include "third_party/skia/include/core/SkColor.h" + +namespace gfx { +class Rect; +class Transform; +} + +namespace WebKitTests { +class TestRenderPass; +} + +namespace cc { + +class SolidColorDrawQuad; + +// Adds a new render pass with the provided properties to the given +// render pass list. +WebKitTests::TestRenderPass* addRenderPass( + ScopedPtrVector<RenderPass>& passList, + RenderPass::Id id, + const gfx::Rect& outputRect, + const gfx::Transform& rootTransform); + +// Adds a solid quad to a given render pass. +SolidColorDrawQuad* addQuad(WebKitTests::TestRenderPass* pass, + const gfx::Rect& rect, + SkColor color); + +// Adds a render pass quad to an existing render pass. +void addRenderPassQuad(WebKitTests::TestRenderPass* toPass, + WebKitTests::TestRenderPass* contributingPass); + +} // namespace cc + +#endif // CC_TEST_RENDER_PASS_TEST_UTILS_H_ diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc index 1d6bd2f..a4b9534 100644 --- a/content/browser/renderer_host/compositor_impl_android.cc +++ b/content/browser/renderer_host/compositor_impl_android.cc @@ -123,6 +123,7 @@ bool CompositorImpl::UsesDirectGL() { CompositorImpl::CompositorImpl(Compositor::Client* client) : root_layer_(cc::Layer::create()), + has_transparent_background_(false), window_(NULL), surface_id_(0), client_(client), @@ -172,6 +173,11 @@ void CompositorImpl::SetVisible(bool visible) { cc::LayerTreeSettings settings; settings.refreshRate = 60.0; + // Do not clear the framebuffer when rendering into external GL contexts + // like Android View System's. + if (UsesDirectGL()) + settings.shouldClearRootRenderPass = false; + scoped_ptr<cc::Thread> impl_thread; if (g_impl_thread) impl_thread = cc::ThreadImpl::createForDifferentThread( @@ -183,6 +189,7 @@ void CompositorImpl::SetVisible(bool visible) { host_->setVisible(true); host_->setSurfaceReady(); host_->setViewportSize(size_, size_); + host_->setHasTransparentBackground(has_transparent_background_); } } @@ -196,6 +203,12 @@ void CompositorImpl::SetWindowBounds(const gfx::Size& size) { root_layer_->setBounds(size); } +void CompositorImpl::SetHasTransparentBackground(bool flag) { + has_transparent_background_ = flag; + if (host_.get()) + host_->setHasTransparentBackground(flag); +} + bool CompositorImpl::CompositeAndReadback(void *pixels, const gfx::Rect& rect) { if (host_.get()) return host_->compositeAndReadback(pixels, rect); diff --git a/content/browser/renderer_host/compositor_impl_android.h b/content/browser/renderer_host/compositor_impl_android.h index 1514fcf51..9ce37a4 100644 --- a/content/browser/renderer_host/compositor_impl_android.h +++ b/content/browser/renderer_host/compositor_impl_android.h @@ -48,6 +48,7 @@ class CONTENT_EXPORT CompositorImpl virtual void SetWindowSurface(ANativeWindow* window) OVERRIDE; virtual void SetVisible(bool visible) OVERRIDE; virtual void SetWindowBounds(const gfx::Size& size) OVERRIDE; + virtual void SetHasTransparentBackground(bool flag) OVERRIDE; virtual bool CompositeAndReadback( void *pixels, const gfx::Rect& rect) OVERRIDE; virtual void Composite() OVERRIDE; @@ -89,6 +90,7 @@ class CONTENT_EXPORT CompositorImpl scoped_ptr<cc::LayerTreeHost> host_; gfx::Size size_; + bool has_transparent_background_; ANativeWindow* window_; int surface_id_; diff --git a/content/public/browser/android/compositor.h b/content/public/browser/android/compositor.h index bca7fe7..d0f98c2 100644 --- a/content/public/browser/android/compositor.h +++ b/content/public/browser/android/compositor.h @@ -71,6 +71,9 @@ class CONTENT_EXPORT Compositor { // Set the output surface handle which the compositor renders into. virtual void SetWindowSurface(ANativeWindow* window) = 0; + // Tells the view tree to assume a transparent background when rendering. + virtual void SetHasTransparentBackground(bool flag) = 0; + // Attempts to composite and read back the result into the provided buffer. // The buffer must be at least window width * height * 4 (RGBA) bytes large. // The buffer is not modified if false is returned. |