0

[Android WebView] Tie together Chrome's browser compositor with the Android View system.

This patch introduces a new feature to the compositor:
- A setting to enable cleaning the framebuffer, disabled by default. This prevents destroying data below us when rendering non-rectangular views (e.g. during a rotation).

A second required feature will be added by a later patch:
- A device scissor rect that intersects any scissor calculation. Used to ensure we never render outside where the Android View System tells us.

There are also some issues with the Android View side regarding the restoration of the GL state. This patch introduces a temporary workaround by reading and manually restoring the state changed by the compositor. This will go out as soon as the problem is fixed in the Android side.

BUG=161409,154180

Review URL: https://chromiumcodereview.appspot.com/11316310

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@173324 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
leandrogracia@chromium.org
2012-12-15 21:45:52 +00:00
parent 3ff3a45baf
commit f35e232a94
22 changed files with 501 additions and 63 deletions

@ -2,4 +2,5 @@ include_rules = [
"+cc",
"+content/public/browser",
"+ui/gfx",
"+ui/gl",
]

@ -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, &current_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>

@ -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);
};

@ -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': [

@ -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;

@ -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',

@ -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()

@ -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) {

@ -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 {

@ -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

@ -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)

@ -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;

@ -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) { }

@ -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)

@ -27,6 +27,7 @@ class CC_EXPORT LayerTreeSettings {
bool backgroundColorInsteadOfCheckerboard;
bool showOverdrawInTracing;
bool canUseLCDText;
bool shouldClearRootRenderPass;
double refreshRate;
size_t maxPartialTextureUpdates;
size_t numRasterThreads;

@ -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&) { }

@ -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

@ -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

@ -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_

@ -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);

@ -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_;

@ -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.