summaryrefslogtreecommitdiffstats
path: root/cc/output
diff options
context:
space:
mode:
authorjamesr@chromium.org <jamesr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-18 07:24:30 +0000
committerjamesr@chromium.org <jamesr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-03-18 07:24:30 +0000
commit7f0d825f49dde66a6e9137a4e35460765bc5f0d8 (patch)
tree6944603cf2a97934afad6e599500ff45091316ed /cc/output
parent95e4e1a0fda6929b47702e69e4ddfe384b5d014b (diff)
downloadchromium_src-7f0d825f49dde66a6e9137a4e35460765bc5f0d8.zip
chromium_src-7f0d825f49dde66a6e9137a4e35460765bc5f0d8.tar.gz
chromium_src-7f0d825f49dde66a6e9137a4e35460765bc5f0d8.tar.bz2
Part 4 of cc/ directory shuffles: output
Continuation of https://src.chromium.org/viewvc/chrome?view=rev&revision=188681 BUG=190824 TBR=piman@chromium.org, jschuh@chromium.org Review URL: https://codereview.chromium.org/12912006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@188689 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'cc/output')
-rw-r--r--cc/output/compositor_frame.cc20
-rw-r--r--cc/output/compositor_frame.h32
-rw-r--r--cc/output/compositor_frame_ack.cc15
-rw-r--r--cc/output/compositor_frame_ack.h28
-rw-r--r--cc/output/compositor_frame_metadata.cc19
-rw-r--r--cc/output/compositor_frame_metadata.h43
-rw-r--r--cc/output/context_provider.h42
-rw-r--r--cc/output/delegated_frame_data.cc13
-rw-r--r--cc/output/delegated_frame_data.h27
-rw-r--r--cc/output/delegating_renderer.cc199
-rw-r--r--cc/output/delegating_renderer.h69
-rw-r--r--cc/output/delegating_renderer_unittest.cc136
-rw-r--r--cc/output/direct_renderer.cc345
-rw-r--r--cc/output/direct_renderer.h124
-rw-r--r--cc/output/geometry_binding.cc110
-rw-r--r--cc/output/geometry_binding.h38
-rw-r--r--cc/output/gl_frame_data.cc15
-rw-r--r--cc/output/gl_frame_data.h29
-rw-r--r--cc/output/gl_renderer.cc2232
-rw-r--r--cc/output/gl_renderer.h340
-rw-r--r--cc/output/gl_renderer_draw_cache.cc14
-rw-r--r--cc/output/gl_renderer_draw_cache.h49
-rw-r--r--cc/output/gl_renderer_pixeltest.cc325
-rw-r--r--cc/output/gl_renderer_unittest.cc878
-rw-r--r--cc/output/output_surface.cc111
-rw-r--r--cc/output/output_surface.h107
-rw-r--r--cc/output/output_surface_client.h27
-rw-r--r--cc/output/program_binding.cc145
-rw-r--r--cc/output/program_binding.h90
-rw-r--r--cc/output/render_surface_filters.cc461
-rw-r--r--cc/output/render_surface_filters.h38
-rw-r--r--cc/output/render_surface_filters_unittest.cc146
-rw-r--r--cc/output/renderer.cc17
-rw-r--r--cc/output/renderer.h90
-rw-r--r--cc/output/shader.cc938
-rw-r--r--cc/output/shader.h366
-rw-r--r--cc/output/software_frame_data.cc15
-rw-r--r--cc/output/software_frame_data.h27
-rw-r--r--cc/output/software_output_device.cc61
-rw-r--r--cc/output/software_output_device.h56
-rw-r--r--cc/output/software_renderer.cc418
-rw-r--r--cc/output/software_renderer.h104
-rw-r--r--cc/output/software_renderer_unittest.cc207
-rw-r--r--cc/output/texture_copier.cc115
-rw-r--r--cc/output/texture_copier.h69
-rw-r--r--cc/output/texture_copier_unittest.cc73
46 files changed, 8823 insertions, 0 deletions
diff --git a/cc/output/compositor_frame.cc b/cc/output/compositor_frame.cc
new file mode 100644
index 0000000..e6d713d
--- /dev/null
+++ b/cc/output/compositor_frame.cc
@@ -0,0 +1,20 @@
+// 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/output/compositor_frame.h"
+
+namespace cc {
+
+CompositorFrame::CompositorFrame() {}
+
+CompositorFrame::~CompositorFrame() {}
+
+void CompositorFrame::AssignTo(CompositorFrame* target) {
+ target->delegated_frame_data = delegated_frame_data.Pass();
+ target->gl_frame_data = gl_frame_data.Pass();
+ target->software_frame_data = software_frame_data.Pass();
+ target->metadata = metadata;
+}
+
+} // namespace cc
diff --git a/cc/output/compositor_frame.h b/cc/output/compositor_frame.h
new file mode 100644
index 0000000..bf3ae3e
--- /dev/null
+++ b/cc/output/compositor_frame.h
@@ -0,0 +1,32 @@
+// 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_OUTPUT_COMPOSITOR_FRAME_H_
+#define CC_OUTPUT_COMPOSITOR_FRAME_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/output/compositor_frame_metadata.h"
+#include "cc/output/delegated_frame_data.h"
+#include "cc/output/gl_frame_data.h"
+#include "cc/output/software_frame_data.h"
+
+namespace cc {
+
+class CC_EXPORT CompositorFrame {
+ public:
+ CompositorFrame();
+ ~CompositorFrame();
+
+ CompositorFrameMetadata metadata;
+ scoped_ptr<DelegatedFrameData> delegated_frame_data;
+ scoped_ptr<GLFrameData> gl_frame_data;
+ scoped_ptr<SoftwareFrameData> software_frame_data;
+
+ void AssignTo(CompositorFrame* target);
+};
+
+} // namespace cc
+
+#endif // CC_OUTPUT_COMPOSITOR_FRAME_H_
diff --git a/cc/output/compositor_frame_ack.cc b/cc/output/compositor_frame_ack.cc
new file mode 100644
index 0000000..781c67d
--- /dev/null
+++ b/cc/output/compositor_frame_ack.cc
@@ -0,0 +1,15 @@
+// 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/output/compositor_frame_ack.h"
+
+namespace cc {
+
+CompositorFrameAck::CompositorFrameAck()
+ : last_content_dib(TransportDIB::DefaultHandleValue()) {
+}
+
+CompositorFrameAck::~CompositorFrameAck() {}
+
+} // namespace cc
diff --git a/cc/output/compositor_frame_ack.h b/cc/output/compositor_frame_ack.h
new file mode 100644
index 0000000..be15c61
--- /dev/null
+++ b/cc/output/compositor_frame_ack.h
@@ -0,0 +1,28 @@
+// 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_OUTPUT_COMPOSITOR_FRAME_ACK_H_
+#define CC_OUTPUT_COMPOSITOR_FRAME_ACK_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/output/gl_frame_data.h"
+#include "cc/transferable_resource.h"
+#include "ui/surface/transport_dib.h"
+
+namespace cc {
+
+class CC_EXPORT CompositorFrameAck {
+ public:
+ CompositorFrameAck();
+ ~CompositorFrameAck();
+
+ TransferableResourceArray resources;
+ scoped_ptr<GLFrameData> gl_frame_data;
+ TransportDIB::Handle last_content_dib;
+};
+
+} // namespace cc
+
+#endif // CC_OUTPUT_COMPOSITOR_FRAME_ACK_H_
diff --git a/cc/output/compositor_frame_metadata.cc b/cc/output/compositor_frame_metadata.cc
new file mode 100644
index 0000000..e202c30
--- /dev/null
+++ b/cc/output/compositor_frame_metadata.cc
@@ -0,0 +1,19 @@
+// Copyright (c) 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/output/compositor_frame_metadata.h"
+
+namespace cc {
+
+CompositorFrameMetadata::CompositorFrameMetadata()
+ : device_scale_factor(0),
+ page_scale_factor(0),
+ min_page_scale_factor(0),
+ max_page_scale_factor(0) {
+}
+
+CompositorFrameMetadata::~CompositorFrameMetadata() {
+}
+
+} // namespace cc
diff --git a/cc/output/compositor_frame_metadata.h b/cc/output/compositor_frame_metadata.h
new file mode 100644
index 0000000..c451a5b
--- /dev/null
+++ b/cc/output/compositor_frame_metadata.h
@@ -0,0 +1,43 @@
+// Copyright (c) 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_OUTPUT_COMPOSITOR_FRAME_METADATA_H_
+#define CC_OUTPUT_COMPOSITOR_FRAME_METADATA_H_
+
+#include "cc/base/cc_export.h"
+#include "ui/gfx/size_f.h"
+#include "ui/gfx/vector2d_f.h"
+
+namespace cc {
+
+class CC_EXPORT CompositorFrameMetadata
+{
+ public:
+ CompositorFrameMetadata();
+ ~CompositorFrameMetadata();
+
+ // The device scale factor used to generate this compositor frame.
+ float device_scale_factor;
+
+ // Scroll offset and scale of the root layer. This can be used for tasks
+ // like positioning windowed plugins.
+ gfx::Vector2dF root_scroll_offset;
+ float page_scale_factor;
+
+ // These limits can be used together with the scroll/scale fields above to
+ // determine if scrolling/scaling in a particular direction is possible.
+ gfx::SizeF viewport_size;
+ gfx::SizeF root_layer_size;
+ float min_page_scale_factor;
+ float max_page_scale_factor;
+
+ // Used to position the Android location top bar and page content, whose
+ // precise position is computed by the renderer compositor.
+ gfx::Vector2dF location_bar_offset;
+ gfx::Vector2dF location_bar_content_translation;
+};
+
+} // namespace cc
+
+#endif // CC_OUTPUT_COMPOSITOR_FRAME_METADATA_H_
diff --git a/cc/output/context_provider.h b/cc/output/context_provider.h
new file mode 100644
index 0000000..3b489c7
--- /dev/null
+++ b/cc/output/context_provider.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2013 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_OUTPUT_CONTEXT_PROVIDER_H_
+#define CC_OUTPUT_CONTEXT_PROVIDER_H_
+
+#include "base/memory/ref_counted.h"
+
+class GrContext;
+namespace WebKit { class WebGraphicsContext3D; }
+
+namespace cc {
+
+class ContextProvider : public base::RefCountedThreadSafe<ContextProvider> {
+ public:
+ // Bind the 3d context to the current thread. This should be called before
+ // accessing the contexts. Calling it more than once should have no effect.
+ // Once this function has been called, the class should only be accessed
+ // from the same thread.
+ virtual bool BindToCurrentThread() = 0;
+
+ virtual WebKit::WebGraphicsContext3D* Context3d() = 0;
+ virtual class GrContext* GrContext() = 0;
+
+ // Ask the provider to check if the contexts are valid or lost. If they are,
+ // this should invalidate the provider so that it can be replaced with a new
+ // one.
+ virtual void VerifyContexts() = 0;
+
+ // A method to be called from the main thread that should return true if
+ // the context inside the provider is no longer valid.
+ virtual bool DestroyedOnMainThread() = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<ContextProvider>;
+ virtual ~ContextProvider() {}
+};
+
+} // namespace cc
+
+#endif // CC_OUTPUT_CONTEXT_PROVIDER_H_
diff --git a/cc/output/delegated_frame_data.cc b/cc/output/delegated_frame_data.cc
new file mode 100644
index 0000000..516a3ec
--- /dev/null
+++ b/cc/output/delegated_frame_data.cc
@@ -0,0 +1,13 @@
+// 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/output/delegated_frame_data.h"
+
+namespace cc {
+
+DelegatedFrameData::DelegatedFrameData() {}
+
+DelegatedFrameData::~DelegatedFrameData() {}
+
+} // namespace cc
diff --git a/cc/output/delegated_frame_data.h b/cc/output/delegated_frame_data.h
new file mode 100644
index 0000000..499b549
--- /dev/null
+++ b/cc/output/delegated_frame_data.h
@@ -0,0 +1,27 @@
+// 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_OUTPUT_DELEGATED_FRAME_DATA_H_
+#define CC_OUTPUT_DELEGATED_FRAME_DATA_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/base/scoped_ptr_vector.h"
+#include "cc/render_pass.h"
+#include "cc/transferable_resource.h"
+#include "ui/gfx/size.h"
+
+namespace cc {
+
+class CC_EXPORT DelegatedFrameData {
+ public:
+ DelegatedFrameData();
+ ~DelegatedFrameData();
+
+ TransferableResourceArray resource_list;
+ ScopedPtrVector<RenderPass> render_pass_list;
+};
+
+} // namespace cc
+
+#endif // CC_OUTPUT_DELEGATED_FRAME_DATA_H_
diff --git a/cc/output/delegating_renderer.cc b/cc/output/delegating_renderer.cc
new file mode 100644
index 0000000..26a3f19
--- /dev/null
+++ b/cc/output/delegating_renderer.cc
@@ -0,0 +1,199 @@
+// 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/output/delegating_renderer.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/debug/trace_event.h"
+#include "base/string_util.h"
+#include "base/strings/string_split.h"
+#include "cc/checkerboard_draw_quad.h"
+#include "cc/debug_border_draw_quad.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/output/compositor_frame_ack.h"
+#include "cc/render_pass.h"
+#include "cc/render_pass_draw_quad.h"
+#include "cc/resource_provider.h"
+#include "cc/solid_color_draw_quad.h"
+#include "cc/texture_draw_quad.h"
+#include "cc/tile_draw_quad.h"
+#include "cc/yuv_video_draw_quad.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+
+using WebKit::WebGraphicsContext3D;
+
+namespace cc {
+
+scoped_ptr<DelegatingRenderer> DelegatingRenderer::Create(
+ RendererClient* client,
+ OutputSurface* output_surface,
+ ResourceProvider* resource_provider) {
+ scoped_ptr<DelegatingRenderer> renderer(
+ new DelegatingRenderer(client, output_surface, resource_provider));
+ if (!renderer->Initialize())
+ return scoped_ptr<DelegatingRenderer>();
+ return renderer.Pass();
+}
+
+DelegatingRenderer::DelegatingRenderer(
+ RendererClient* client,
+ OutputSurface* output_surface,
+ ResourceProvider* resource_provider)
+ : Renderer(client),
+ output_surface_(output_surface),
+ resource_provider_(resource_provider),
+ visible_(true) {
+ DCHECK(resource_provider_);
+}
+
+bool DelegatingRenderer::Initialize() {
+ capabilities_.using_partial_swap = false;
+ // TODO(danakj): Throttling - we may want to only allow 1 outstanding frame,
+ // but the parent compositor may pipeline for us.
+ // TODO(danakj): Can we use this in single-thread mode?
+ capabilities_.using_swap_complete_callback = true;
+ capabilities_.max_texture_size = resource_provider_->max_texture_size();
+ capabilities_.best_texture_format = resource_provider_->best_texture_format();
+ capabilities_.allow_partial_texture_updates = false;
+
+ WebGraphicsContext3D* context3d = resource_provider_->GraphicsContext3D();
+
+ if (!context3d) {
+ // Software compositing.
+ return true;
+ }
+
+ if (!context3d->makeContextCurrent())
+ return false;
+
+ context3d->setContextLostCallback(this);
+ context3d->pushGroupMarkerEXT("CompositorContext");
+
+ std::string extensionsString =
+ UTF16ToASCII(context3d->getString(GL_EXTENSIONS));
+
+ std::vector<std::string> extensions;
+ base::SplitString(extensionsString, ' ', &extensions);
+
+ // TODO(danakj): We need non-GPU-specific paths for these things. This
+ // renderer shouldn't need to use context3d extensions directly.
+ bool hasReadBGRA = true;
+ bool hasSetVisibility = true;
+ bool hasIOSurface = true;
+ bool hasARBTextureRect = true;
+ bool hasGpuMemoryManager = true;
+ bool hasEGLImage = true;
+ for (size_t i = 0; i < extensions.size(); ++i) {
+ if (extensions[i] == "GL_EXT_read_format_bgra")
+ hasReadBGRA = true;
+ else if (extensions[i] == "GL_CHROMIUM_set_visibility")
+ hasSetVisibility = true;
+ else if (extensions[i] == "GL_CHROMIUM_iosurface")
+ hasIOSurface = true;
+ else if (extensions[i] == "GL_ARB_texture_rectangle")
+ hasARBTextureRect = true;
+ else if (extensions[i] == "GL_CHROMIUM_gpu_memory_manager")
+ hasGpuMemoryManager = true;
+ else if (extensions[i] == "GL_OES_EGL_image_external")
+ hasEGLImage = true;
+ }
+
+ if (hasIOSurface)
+ DCHECK(hasARBTextureRect);
+
+ capabilities_.using_accelerated_painting =
+ Settings().acceleratePainting &&
+ capabilities_.best_texture_format == GL_BGRA_EXT &&
+ hasReadBGRA;
+
+ // TODO(piman): loop visibility to GPU process?
+ capabilities_.using_set_visibility = hasSetVisibility;
+
+ // TODO(danakj): Support GpuMemoryManager.
+ capabilities_.using_gpu_memory_manager = false;
+
+ capabilities_.using_egl_image = hasEGLImage;
+
+ return true;
+}
+
+DelegatingRenderer::~DelegatingRenderer() {
+ WebGraphicsContext3D* context3d = resource_provider_->GraphicsContext3D();
+ if (context3d)
+ context3d->setContextLostCallback(NULL);
+}
+
+const RendererCapabilities& DelegatingRenderer::Capabilities() const {
+ return capabilities_;
+}
+
+static ResourceProvider::ResourceId AppendToArray(
+ ResourceProvider::ResourceIdArray* array,
+ ResourceProvider::ResourceId id) {
+ array->push_back(id);
+ return id;
+}
+
+void DelegatingRenderer::DrawFrame(
+ RenderPassList& render_passes_in_draw_order) {
+ TRACE_EVENT0("cc", "DelegatingRenderer::drawFrame");
+
+ CompositorFrame out_frame;
+ out_frame.metadata = client_->MakeCompositorFrameMetadata();
+
+ out_frame.delegated_frame_data = make_scoped_ptr(new DelegatedFrameData);
+
+ // Collect all resource ids in the render passes into a ResourceIdArray.
+ ResourceProvider::ResourceIdArray resources;
+ DrawQuad::ResourceIteratorCallback append_to_array =
+ base::Bind(&AppendToArray, &resources);
+ for (size_t i = 0; i < render_passes_in_draw_order.size(); ++i) {
+ RenderPass* render_pass = render_passes_in_draw_order[i];
+ for (size_t j = 0; j < render_pass->quad_list.size(); ++j)
+ render_pass->quad_list[j]->IterateResources(append_to_array);
+ }
+
+ // Move the render passes and resources into the |out_frame|.
+ DelegatedFrameData& out_data = *out_frame.delegated_frame_data;
+ out_data.render_pass_list.swap(render_passes_in_draw_order);
+ resource_provider_->PrepareSendToParent(resources, &out_data.resource_list);
+
+ output_surface_->SendFrameToParentCompositor(&out_frame);
+}
+
+bool DelegatingRenderer::SwapBuffers() {
+ return true;
+}
+
+void DelegatingRenderer::GetFramebufferPixels(void* pixels, gfx::Rect rect) {
+ NOTIMPLEMENTED();
+}
+
+void DelegatingRenderer::ReceiveCompositorFrameAck(
+ const CompositorFrameAck& ack) {
+ resource_provider_->ReceiveFromParent(ack.resources);
+ if (client_->HasImplThread())
+ client_->OnSwapBuffersComplete();
+}
+
+
+bool DelegatingRenderer::IsContextLost() {
+ WebGraphicsContext3D* context3d = resource_provider_->GraphicsContext3D();
+ if (!context3d)
+ return false;
+ return context3d->getGraphicsResetStatusARB() != GL_NO_ERROR;
+}
+
+void DelegatingRenderer::SetVisible(bool visible) {
+ visible_ = visible;
+}
+
+void DelegatingRenderer::onContextLost() {
+ client_->DidLoseOutputSurface();
+}
+
+} // namespace cc
diff --git a/cc/output/delegating_renderer.h b/cc/output/delegating_renderer.h
new file mode 100644
index 0000000..8b938cd
--- /dev/null
+++ b/cc/output/delegating_renderer.h
@@ -0,0 +1,69 @@
+// 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_OUTPUT_DELEGATING_RENDERER_H_
+#define CC_OUTPUT_DELEGATING_RENDERER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/output/renderer.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h"
+
+namespace cc {
+
+class OutputSurface;
+class ResourceProvider;
+
+class CC_EXPORT DelegatingRenderer :
+ public Renderer,
+ public NON_EXPORTED_BASE(
+ WebKit::WebGraphicsContext3D::WebGraphicsContextLostCallback)
+{
+ public:
+ static scoped_ptr<DelegatingRenderer> Create(
+ RendererClient* client,
+ OutputSurface* output_surface,
+ ResourceProvider* resource_provider);
+ virtual ~DelegatingRenderer();
+
+ virtual const RendererCapabilities& Capabilities() const OVERRIDE;
+
+ virtual void DrawFrame(RenderPassList& render_passes_in_draw_order) OVERRIDE;
+
+ virtual void Finish() OVERRIDE {}
+
+ virtual bool SwapBuffers() OVERRIDE;
+
+ virtual void GetFramebufferPixels(void* pixels, gfx::Rect rect) OVERRIDE;
+
+ virtual void ReceiveCompositorFrameAck(const CompositorFrameAck&) OVERRIDE;
+
+ virtual bool IsContextLost() OVERRIDE;
+
+ virtual void SetVisible(bool) OVERRIDE;
+
+ virtual void SendManagedMemoryStats(size_t bytes_visible,
+ size_t bytes_visible_and_nearby,
+ size_t bytes_allocated) OVERRIDE {}
+
+ // WebGraphicsContext3D::WebGraphicsContextLostCallback implementation.
+ virtual void onContextLost() OVERRIDE;
+
+private:
+ DelegatingRenderer(RendererClient* client,
+ OutputSurface* output_surface,
+ ResourceProvider* resource_provider);
+ bool Initialize();
+
+ OutputSurface* output_surface_;
+ ResourceProvider* resource_provider_;
+ RendererCapabilities capabilities_;
+ bool visible_;
+
+ DISALLOW_COPY_AND_ASSIGN(DelegatingRenderer);
+};
+
+} // namespace cc
+
+#endif // CC_OUTPUT_DELEGATING_RENDERER_H_
diff --git a/cc/output/delegating_renderer_unittest.cc b/cc/output/delegating_renderer_unittest.cc
new file mode 100644
index 0000000..5dcdc8c
--- /dev/null
+++ b/cc/output/delegating_renderer_unittest.cc
@@ -0,0 +1,136 @@
+// Copyright 2013 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/output/delegating_renderer.h"
+
+#include "cc/test/fake_output_surface.h"
+#include "cc/test/layer_tree_test_common.h"
+#include "cc/test/render_pass_test_common.h"
+#include "cc/test/render_pass_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace cc {
+
+class DelegatingRendererTest : public ThreadedTest {
+ public:
+ DelegatingRendererTest() : ThreadedTest(), output_surface_(NULL) {}
+ virtual ~DelegatingRendererTest() {}
+
+ virtual scoped_ptr<OutputSurface> createOutputSurface() OVERRIDE {
+ scoped_ptr<TestWebGraphicsContext3D> context3d =
+ TestWebGraphicsContext3D::Create(
+ WebKit::WebGraphicsContext3D::Attributes());
+ context3d_ = context3d.get();
+ scoped_ptr<FakeOutputSurface> output_surface =
+ FakeOutputSurface::CreateDelegating3d(
+ context3d.PassAs<WebKit::WebGraphicsContext3D>());
+ output_surface_ = output_surface.get();
+ return output_surface.PassAs<OutputSurface>();
+ }
+
+ protected:
+ TestWebGraphicsContext3D* context3d_;
+ FakeOutputSurface* output_surface_;
+};
+
+class DelegatingRendererTestDraw : public DelegatingRendererTest {
+ public:
+ virtual void beginTest() OVERRIDE {
+ m_layerTreeHost->SetPageScaleFactorAndLimits(1.f, 0.5f, 4.f);
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void afterTest() OVERRIDE {}
+
+ virtual bool prepareToDrawOnThread(
+ LayerTreeHostImpl*, LayerTreeHostImpl::FrameData* frame, bool result)
+ OVERRIDE {
+ EXPECT_EQ(0u, output_surface_->num_sent_frames());
+
+ CompositorFrame& last_frame = output_surface_->last_sent_frame();
+ EXPECT_FALSE(last_frame.delegated_frame_data);
+ EXPECT_FALSE(last_frame.gl_frame_data);
+ EXPECT_EQ(0.f, last_frame.metadata.min_page_scale_factor);
+ EXPECT_EQ(0.f, last_frame.metadata.max_page_scale_factor);
+ return true;
+ }
+
+ virtual void drawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+ EXPECT_EQ(1u, output_surface_->num_sent_frames());
+
+ CompositorFrame& last_frame = output_surface_->last_sent_frame();
+ DelegatedFrameData* last_frame_data = last_frame.delegated_frame_data.get();
+ ASSERT_TRUE(last_frame.delegated_frame_data);
+ EXPECT_FALSE(last_frame.gl_frame_data);
+ EXPECT_EQ(
+ gfx::Rect(host_impl->device_viewport_size()).ToString(),
+ last_frame_data->render_pass_list.back()->output_rect.ToString());
+ EXPECT_EQ(0.5f, last_frame.metadata.min_page_scale_factor);
+ EXPECT_EQ(4.f, last_frame.metadata.max_page_scale_factor);
+
+ EXPECT_EQ(
+ 0u, last_frame.delegated_frame_data->resource_list.size());
+ EXPECT_EQ(1u, last_frame.delegated_frame_data->render_pass_list.size());
+
+ endTest();
+ }
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(DelegatingRendererTestDraw)
+
+class DelegatingRendererTestResources : public DelegatingRendererTest {
+ public:
+ virtual void beginTest() OVERRIDE {
+ postSetNeedsCommitToMainThread();
+ }
+
+ virtual void afterTest() OVERRIDE {}
+
+ virtual bool prepareToDrawOnThread(
+ LayerTreeHostImpl* host_impl,
+ LayerTreeHostImpl::FrameData* frame,
+ bool result) OVERRIDE {
+
+ frame->render_passes.clear();
+ frame->render_passes_by_id.clear();
+
+ TestRenderPass* child_pass = addRenderPass(
+ frame->render_passes,
+ RenderPass::Id(2, 1),
+ gfx::Rect(3, 3, 10, 10),
+ gfx::Transform());
+ child_pass->AppendOneOfEveryQuadType(
+ host_impl->resource_provider(), RenderPass::Id(0, 0));
+
+ TestRenderPass* pass = addRenderPass(
+ frame->render_passes,
+ RenderPass::Id(1, 1),
+ gfx::Rect(3, 3, 10, 10),
+ gfx::Transform());
+ pass->AppendOneOfEveryQuadType(
+ host_impl->resource_provider(), child_pass->id);
+ return true;
+ }
+
+ virtual void drawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE {
+ EXPECT_EQ(1u, output_surface_->num_sent_frames());
+
+ CompositorFrame& last_frame = output_surface_->last_sent_frame();
+ ASSERT_TRUE(last_frame.delegated_frame_data);
+
+ EXPECT_EQ(2u, last_frame.delegated_frame_data->render_pass_list.size());
+ // Each render pass has 7 resources in it. And the root render pass has a
+ // mask resource used when drawing the child render pass. The number 7 may
+ // change if AppendOneOfEveryQuadType is updated, and the value here should
+ // be updated accordingly.
+ EXPECT_EQ(
+ 15u, last_frame.delegated_frame_data->resource_list.size());
+
+ endTest();
+ }
+};
+
+SINGLE_AND_MULTI_THREAD_TEST_F(DelegatingRendererTestResources)
+
+} // namespace cc
diff --git a/cc/output/direct_renderer.cc b/cc/output/direct_renderer.cc
new file mode 100644
index 0000000..3aed731
--- /dev/null
+++ b/cc/output/direct_renderer.cc
@@ -0,0 +1,345 @@
+// 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/output/direct_renderer.h"
+
+#include <vector>
+
+#include "base/debug/trace_event.h"
+#include "base/metrics/histogram.h"
+#include "cc/base/math_util.h"
+#include "cc/draw_quad.h"
+#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/transform.h"
+
+static gfx::Transform OrthoProjectionMatrix(float left,
+ float right,
+ float bottom,
+ float top) {
+ // Use the standard formula to map the clipping frustum to the cube from
+ // [-1, -1, -1] to [1, 1, 1].
+ float delta_x = right - left;
+ float delta_y = top - bottom;
+ gfx::Transform proj;
+ if (!delta_x || !delta_y)
+ return proj;
+ proj.matrix().setDouble(0, 0, 2.0f / delta_x);
+ proj.matrix().setDouble(0, 3, -(right + left) / delta_x);
+ proj.matrix().setDouble(1, 1, 2.0f / delta_y);
+ proj.matrix().setDouble(1, 3, -(top + bottom) / delta_y);
+
+ // Z component of vertices is always set to zero as we don't use the depth
+ // buffer while drawing.
+ proj.matrix().setDouble(2, 2, 0);
+
+ return proj;
+}
+
+static gfx::Transform window_matrix(int x, int y, int width, int height) {
+ gfx::Transform canvas;
+
+ // Map to window position and scale up to pixel coordinates.
+ canvas.Translate3d(x, y, 0);
+ canvas.Scale3d(width, height, 0);
+
+ // Map from ([-1, -1] to [1, 1]) -> ([0, 0] to [1, 1])
+ canvas.Translate3d(0.5, 0.5, 0.5);
+ canvas.Scale3d(0.5, 0.5, 0.5);
+
+ return canvas;
+}
+
+namespace cc {
+
+DirectRenderer::DrawingFrame::DrawingFrame()
+ : root_render_pass(NULL),
+ current_render_pass(NULL),
+ current_texture(NULL),
+ flipped_y(false) {}
+
+DirectRenderer::DrawingFrame::~DrawingFrame() {}
+
+//
+// static
+gfx::RectF DirectRenderer::QuadVertexRect() {
+ return gfx::RectF(-0.5f, -0.5f, 1.f, 1.f);
+}
+
+// static
+void DirectRenderer::QuadRectTransform(gfx::Transform* quad_rect_transform,
+ const gfx::Transform& quadTransform,
+ const gfx::RectF& quadRect) {
+ *quad_rect_transform = quadTransform;
+ quad_rect_transform->Translate(0.5 * quadRect.width() + quadRect.x(),
+ 0.5 * quadRect.height() + quadRect.y());
+ quad_rect_transform->Scale(quadRect.width(), quadRect.height());
+}
+
+// static
+void DirectRenderer::InitializeMatrices(DrawingFrame& frame,
+ gfx::Rect draw_rect,
+ bool flip_y) {
+ if (flip_y) {
+ frame.projection_matrix = OrthoProjectionMatrix(draw_rect.x(),
+ draw_rect.right(),
+ draw_rect.bottom(),
+ draw_rect.y());
+ } else {
+ frame.projection_matrix = OrthoProjectionMatrix(draw_rect.x(),
+ draw_rect.right(),
+ draw_rect.y(),
+ draw_rect.bottom());
+ }
+ frame.window_matrix =
+ window_matrix(0, 0, draw_rect.width(), draw_rect.height());
+ frame.flipped_y = flip_y;
+}
+
+// static
+gfx::Rect DirectRenderer::MoveScissorToWindowSpace(
+ const DrawingFrame& frame, const gfx::RectF& scissor_rect) {
+ gfx::Rect scissor_rect_in_canvas_space = gfx::ToEnclosingRect(scissor_rect);
+ // The scissor coordinates must be supplied in viewport space so we need to
+ // offset by the relative position of the top left corner of the current
+ // render pass.
+ gfx::Rect framebuffer_output_rect = frame.current_render_pass->output_rect;
+ scissor_rect_in_canvas_space.set_x(
+ scissor_rect_in_canvas_space.x() - framebuffer_output_rect.x());
+ if (frame.flipped_y && !frame.current_texture) {
+ scissor_rect_in_canvas_space.set_y(
+ framebuffer_output_rect.height() -
+ (scissor_rect_in_canvas_space.bottom() - framebuffer_output_rect.y()));
+ } else {
+ scissor_rect_in_canvas_space.set_y(
+ scissor_rect_in_canvas_space.y() - framebuffer_output_rect.y());
+ }
+ return scissor_rect_in_canvas_space;
+}
+
+DirectRenderer::DirectRenderer(RendererClient* client,
+ ResourceProvider* resource_provider)
+ : Renderer(client),
+ resource_provider_(resource_provider) {}
+
+DirectRenderer::~DirectRenderer() {}
+
+void DirectRenderer::SetEnlargePassTextureAmountForTesting(
+ gfx::Vector2d amount) {
+ enlarge_pass_texture_amount_ = amount;
+}
+
+void DirectRenderer::DecideRenderPassAllocationsForFrame(
+ const RenderPassList& render_passes_in_draw_order) {
+ base::hash_map<RenderPass::Id, const RenderPass*> renderPassesInFrame;
+ for (size_t i = 0; i < render_passes_in_draw_order.size(); ++i)
+ renderPassesInFrame.insert(std::pair<RenderPass::Id, const RenderPass*>(
+ render_passes_in_draw_order[i]->id, render_passes_in_draw_order[i]));
+
+ std::vector<RenderPass::Id> passes_to_delete;
+ ScopedPtrHashMap<RenderPass::Id, CachedResource>::const_iterator passIterator;
+ for (passIterator = render_pass_textures_.begin();
+ passIterator != render_pass_textures_.end();
+ ++passIterator) {
+ base::hash_map<RenderPass::Id, const RenderPass*>::const_iterator it =
+ renderPassesInFrame.find(passIterator->first);
+ if (it == renderPassesInFrame.end()) {
+ passes_to_delete.push_back(passIterator->first);
+ continue;
+ }
+
+ const RenderPass* render_pass_in_frame = it->second;
+ gfx::Size required_size = RenderPassTextureSize(render_pass_in_frame);
+ GLenum required_format = RenderPassTextureFormat(render_pass_in_frame);
+ CachedResource* texture = passIterator->second;
+ DCHECK(texture);
+
+ bool size_appropriate = texture->size().width() >= required_size.width() &&
+ texture->size().height() >= required_size.height();
+ if (texture->id() &&
+ (!size_appropriate || texture->format() != required_format))
+ texture->Free();
+ }
+
+ // Delete RenderPass textures from the previous frame that will not be used
+ // again.
+ for (size_t i = 0; i < passes_to_delete.size(); ++i)
+ render_pass_textures_.erase(passes_to_delete[i]);
+
+ for (size_t i = 0; i < render_passes_in_draw_order.size(); ++i) {
+ if (!render_pass_textures_.contains(render_passes_in_draw_order[i]->id)) {
+ scoped_ptr<CachedResource> texture =
+ CachedResource::Create(resource_provider_);
+ render_pass_textures_.set(render_passes_in_draw_order[i]->id,
+ texture.Pass());
+ }
+ }
+}
+
+void DirectRenderer::DrawFrame(RenderPassList& render_passes_in_draw_order) {
+ TRACE_EVENT0("cc", "DirectRenderer::drawFrame");
+ UMA_HISTOGRAM_COUNTS("Renderer4.renderPassCount",
+ render_passes_in_draw_order.size());
+
+ const RenderPass* root_render_pass = render_passes_in_draw_order.back();
+ DCHECK(root_render_pass);
+
+ DrawingFrame frame;
+ frame.root_render_pass = root_render_pass;
+ frame.root_damage_rect =
+ Capabilities().using_partial_swap ?
+ root_render_pass->damage_rect : root_render_pass->output_rect;
+ frame.root_damage_rect.Intersect(gfx::Rect(gfx::Point(), ViewportSize()));
+
+ BeginDrawingFrame(frame);
+ for (size_t i = 0; i < render_passes_in_draw_order.size(); ++i)
+ DrawRenderPass(frame, render_passes_in_draw_order[i]);
+ FinishDrawingFrame(frame);
+
+ render_passes_in_draw_order.clear();
+}
+
+gfx::RectF DirectRenderer::ComputeScissorRectForRenderPass(
+ const DrawingFrame& frame) {
+ gfx::RectF render_pass_scissor = frame.current_render_pass->output_rect;
+
+ if (frame.root_damage_rect == frame.root_render_pass->output_rect)
+ return render_pass_scissor;
+
+ gfx::Transform inverseTransform(gfx::Transform::kSkipInitialization);
+ if (frame.current_render_pass->transform_to_root_target.GetInverse(
+ &inverseTransform)) {
+ // Only intersect inverse-projected damage if the transform is invertible.
+ gfx::RectF damage_rect_in_render_pass_space =
+ MathUtil::ProjectClippedRect(inverseTransform, frame.root_damage_rect);
+ render_pass_scissor.Intersect(damage_rect_in_render_pass_space);
+ }
+
+ return render_pass_scissor;
+}
+
+void DirectRenderer::SetScissorStateForQuad(const DrawingFrame& frame,
+ const DrawQuad& quad) {
+ if (quad.isClipped()) {
+ gfx::RectF quad_scissor_rect = quad.clipRect();
+ SetScissorTestRect(MoveScissorToWindowSpace(frame, quad_scissor_rect));
+ } else {
+ EnsureScissorTestDisabled();
+ }
+}
+
+void DirectRenderer::SetScissorStateForQuadWithRenderPassScissor(
+ const DrawingFrame& frame,
+ const DrawQuad& quad,
+ const gfx::RectF& render_pass_scissor,
+ bool* should_skip_quad) {
+ gfx::RectF quad_scissor_rect = render_pass_scissor;
+
+ if (quad.isClipped())
+ quad_scissor_rect.Intersect(quad.clipRect());
+
+ if (quad_scissor_rect.IsEmpty()) {
+ *should_skip_quad = true;
+ return;
+ }
+
+ *should_skip_quad = false;
+ SetScissorTestRect(MoveScissorToWindowSpace(frame, quad_scissor_rect));
+}
+
+void DirectRenderer::FinishDrawingQuadList() {}
+
+void DirectRenderer::DrawRenderPass(DrawingFrame& frame,
+ const RenderPass* render_pass) {
+ TRACE_EVENT0("cc", "DirectRenderer::drawRenderPass");
+ if (!UseRenderPass(frame, render_pass))
+ return;
+
+ bool using_scissor_as_optimization = Capabilities().using_partial_swap;
+ gfx::RectF render_pass_scissor;
+
+ if (using_scissor_as_optimization) {
+ render_pass_scissor = ComputeScissorRectForRenderPass(frame);
+ SetScissorTestRect(MoveScissorToWindowSpace(frame, render_pass_scissor));
+ }
+
+ if (frame.current_render_pass != frame.root_render_pass ||
+ client_->ShouldClearRootRenderPass()) {
+ if (!using_scissor_as_optimization)
+ EnsureScissorTestDisabled();
+ ClearFramebuffer(frame);
+ }
+
+ const QuadList& quad_list = render_pass->quad_list;
+ for (QuadList::constBackToFrontIterator it = quad_list.backToFrontBegin();
+ it != quad_list.backToFrontEnd();
+ ++it) {
+ const DrawQuad& quad = *(*it);
+ bool should_skip_quad = false;
+
+ if (using_scissor_as_optimization) {
+ SetScissorStateForQuadWithRenderPassScissor(
+ frame, quad, render_pass_scissor, &should_skip_quad);
+ } else {
+ SetScissorStateForQuad(frame, quad);
+ }
+
+ if (!should_skip_quad)
+ DoDrawQuad(frame, *it);
+ }
+ FinishDrawingQuadList();
+
+ CachedResource* texture = render_pass_textures_.get(render_pass->id);
+ if (texture) {
+ texture->set_is_complete(
+ !render_pass->has_occlusion_from_outside_target_surface);
+ }
+}
+
+bool DirectRenderer::UseRenderPass(DrawingFrame& frame,
+ const RenderPass* render_pass) {
+ frame.current_render_pass = render_pass;
+ frame.current_texture = NULL;
+
+ if (render_pass == frame.root_render_pass) {
+ BindFramebufferToOutputSurface(frame);
+ InitializeMatrices(frame, render_pass->output_rect, FlippedFramebuffer());
+ SetDrawViewportSize(render_pass->output_rect.size());
+ return true;
+ }
+
+ CachedResource* texture = render_pass_textures_.get(render_pass->id);
+ DCHECK(texture);
+
+ gfx::Size size = RenderPassTextureSize(render_pass);
+ size.Enlarge(enlarge_pass_texture_amount_.x(),
+ enlarge_pass_texture_amount_.y());
+ if (!texture->id() &&
+ !texture->Allocate(size,
+ RenderPassTextureFormat(render_pass),
+ ResourceProvider::TextureUsageFramebuffer))
+ return false;
+
+ return BindFramebufferToTexture(frame, texture, render_pass->output_rect);
+}
+
+bool DirectRenderer::HaveCachedResourcesForRenderPassId(RenderPass::Id id)
+ const {
+ if (!Settings().cacheRenderPassContents)
+ return false;
+
+ CachedResource* texture = render_pass_textures_.get(id);
+ return texture && texture->id() && texture->is_complete();
+}
+
+// static
+gfx::Size DirectRenderer::RenderPassTextureSize(const RenderPass* render_pass) {
+ return render_pass->output_rect.size();
+}
+
+// static
+GLenum DirectRenderer::RenderPassTextureFormat(const RenderPass* render_pass) {
+ return GL_RGBA;
+}
+
+} // namespace cc
diff --git a/cc/output/direct_renderer.h b/cc/output/direct_renderer.h
new file mode 100644
index 0000000..72dff5d
--- /dev/null
+++ b/cc/output/direct_renderer.h
@@ -0,0 +1,124 @@
+// 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_OUTPUT_DIRECT_RENDERER_H_
+#define CC_OUTPUT_DIRECT_RENDERER_H_
+
+#include "base/basictypes.h"
+#include "cc/base/cc_export.h"
+#include "cc/output/renderer.h"
+#include "cc/resource_provider.h"
+#include "cc/scoped_resource.h"
+
+namespace cc {
+
+class ResourceProvider;
+
+// This is the base class for code shared between the GL and software
+// renderer implementations. "Direct" refers to the fact that it does not
+// delegate rendering to another compositor.
+class CC_EXPORT DirectRenderer : public Renderer {
+ public:
+ virtual ~DirectRenderer();
+
+ ResourceProvider* resource_provider() const { return resource_provider_; }
+
+ virtual void DecideRenderPassAllocationsForFrame(
+ const RenderPassList& render_passes_in_draw_order) OVERRIDE;
+ virtual bool HaveCachedResourcesForRenderPassId(RenderPass::Id id) const
+ OVERRIDE;
+ virtual void DrawFrame(RenderPassList& render_passes_in_draw_order) OVERRIDE;
+
+ struct CC_EXPORT DrawingFrame {
+ DrawingFrame();
+ ~DrawingFrame();
+
+ const RenderPass* root_render_pass;
+ const RenderPass* current_render_pass;
+ const ScopedResource* current_texture;
+
+ gfx::RectF root_damage_rect;
+
+ gfx::Transform projection_matrix;
+ gfx::Transform window_matrix;
+ bool flipped_y;
+ };
+
+ void SetEnlargePassTextureAmountForTesting(gfx::Vector2d amount);
+
+ protected:
+ DirectRenderer(RendererClient* client, ResourceProvider* resource_provider);
+
+ class CachedResource : public ScopedResource {
+ public:
+ static scoped_ptr<CachedResource> Create(
+ ResourceProvider* resource_provider) {
+ return make_scoped_ptr(new CachedResource(resource_provider));
+ }
+ virtual ~CachedResource() {}
+
+ bool is_complete() const { return is_complete_; }
+ void set_is_complete(bool is_complete) { is_complete_ = is_complete; }
+
+ protected:
+ explicit CachedResource(ResourceProvider* resource_provider)
+ : ScopedResource(resource_provider),
+ is_complete_(false) {}
+
+ private:
+ bool is_complete_;
+
+ DISALLOW_COPY_AND_ASSIGN(CachedResource);
+ };
+
+ static gfx::RectF QuadVertexRect();
+ static void QuadRectTransform(gfx::Transform* quad_rect_transform,
+ const gfx::Transform& quad_transform,
+ const gfx::RectF& quad_rect);
+ static void InitializeMatrices(DrawingFrame& frame,
+ gfx::Rect draw_rect,
+ bool flip_y);
+ static gfx::Rect MoveScissorToWindowSpace(const DrawingFrame& frame,
+ const gfx::RectF& scissor_rect);
+ static gfx::RectF ComputeScissorRectForRenderPass(const DrawingFrame& frame);
+ void SetScissorStateForQuad(const DrawingFrame& frame, const DrawQuad& quad);
+ void SetScissorStateForQuadWithRenderPassScissor(
+ const DrawingFrame& frame,
+ const DrawQuad& quad,
+ const gfx::RectF& render_pass_scissor,
+ bool* should_skip_quad);
+
+ static gfx::Size RenderPassTextureSize(const RenderPass* render_pass);
+ static GLenum RenderPassTextureFormat(const RenderPass* render_pass);
+
+ void DrawRenderPass(DrawingFrame& frame, const RenderPass* render_pass);
+ bool UseRenderPass(DrawingFrame& frame, const RenderPass* render_pass);
+
+ virtual void BindFramebufferToOutputSurface(DrawingFrame& frame) = 0;
+ virtual bool BindFramebufferToTexture(DrawingFrame& frame,
+ const ScopedResource* resource,
+ gfx::Rect framebuffer_rect) = 0;
+ virtual void SetDrawViewportSize(gfx::Size viewport_size) = 0;
+ virtual void SetScissorTestRect(gfx::Rect scissor_rect) = 0;
+ virtual void ClearFramebuffer(DrawingFrame& frame) = 0;
+ virtual void DoDrawQuad(DrawingFrame& frame, const DrawQuad* quad) = 0;
+ virtual void BeginDrawingFrame(DrawingFrame& frame) = 0;
+ virtual void FinishDrawingFrame(DrawingFrame& frame) = 0;
+ virtual void FinishDrawingQuadList();
+ virtual bool FlippedFramebuffer() const = 0;
+ virtual void EnsureScissorTestEnabled() = 0;
+ virtual void EnsureScissorTestDisabled() = 0;
+
+ ScopedPtrHashMap<RenderPass::Id, CachedResource> render_pass_textures_;
+ ResourceProvider* resource_provider_;
+
+ private:
+ gfx::Vector2d enlarge_pass_texture_amount_;
+
+ DISALLOW_COPY_AND_ASSIGN(DirectRenderer);
+};
+
+} // namespace cc
+
+#endif // CC_OUTPUT_DIRECT_RENDERER_H_
diff --git a/cc/output/geometry_binding.cc b/cc/output/geometry_binding.cc
new file mode 100644
index 0000000..5c3db34
--- /dev/null
+++ b/cc/output/geometry_binding.cc
@@ -0,0 +1,110 @@
+// Copyright 2011 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/output/geometry_binding.h"
+
+#include "cc/output/gl_renderer.h" // For the GLC() macro.
+#include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "ui/gfx/rect_f.h"
+
+namespace cc {
+
+GeometryBinding::GeometryBinding(WebKit::WebGraphicsContext3D* context,
+ const gfx::RectF& quad_vertex_rect)
+ : context_(context),
+ quad_vertices_vbo_(0),
+ quad_elements_vbo_(0) {
+ float vertices[] = {
+ quad_vertex_rect.x(), quad_vertex_rect.bottom(), 0.0f, 0.0f,
+ 1.0f, quad_vertex_rect.x(), quad_vertex_rect.y(), 0.0f,
+ 0.0f, 0.0f, quad_vertex_rect.right(), quad_vertex_rect.y(),
+ 0.0f, 1.0f, 0.0f, quad_vertex_rect.right(),
+ quad_vertex_rect.bottom(), 0.0f, 1.0f, 1.0f
+ };
+
+ struct Vertex {
+ float a_position[3];
+ float a_texCoord[2];
+ // Index of the vertex, divide by 4 to have the matrix for this quad.
+ float a_index;
+ };
+ struct Quad {
+ Vertex v0, v1, v2, v3;
+ };
+ struct QuadIndex {
+ uint16_t data[6];
+ };
+
+ COMPILE_ASSERT(sizeof(Quad) == 24 * sizeof(float), struct_is_densely_packed);
+ COMPILE_ASSERT(sizeof(QuadIndex) == 6 * sizeof(uint16_t),
+ struct_is_densely_packed);
+
+ Quad quad_list[8];
+ QuadIndex quad_index_list[8];
+ for (int i = 0; i < 8; i++) {
+ Vertex v0 = { quad_vertex_rect.x(), quad_vertex_rect.bottom(), 0.0f, 0.0f,
+ 1.0f, i * 4.0f + 0.0f };
+ Vertex v1 = { quad_vertex_rect.x(), quad_vertex_rect.y(), 0.0f, 0.0f, 0.0f,
+ i * 4.0f + 1.0f };
+ Vertex v2 = { quad_vertex_rect.right(), quad_vertex_rect.y(), 0.0f, 1.0f,
+ 0.0f, i * 4.0f + 2.0f };
+ Vertex v3 = { quad_vertex_rect.right(), quad_vertex_rect.bottom(), 0.0f,
+ 1.0f, 1.0f, i * 4.0f + 3.0f };
+ Quad x = { v0, v1, v2, v3 };
+ quad_list[i] = x;
+ QuadIndex y = { 0 + 4 * i, 1 + 4 * i, 2 + 4 * i, 3 + 4 * i, 0 + 4 * i,
+ 2 + 4 * i };
+ quad_index_list[i] = y;
+ }
+
+ GLC(context_, quad_vertices_vbo_ = context_->createBuffer());
+ GLC(context_, quad_elements_vbo_ = context_->createBuffer());
+ GLC(context_, context_->bindBuffer(GL_ARRAY_BUFFER, quad_vertices_vbo_));
+ GLC(context_,
+ context_->bufferData(
+ GL_ARRAY_BUFFER, sizeof(quad_list), quad_list, GL_STATIC_DRAW));
+ GLC(context_,
+ context_->bindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_elements_vbo_));
+ GLC(context_,
+ context_->bufferData(GL_ELEMENT_ARRAY_BUFFER,
+ sizeof(quad_index_list),
+ quad_index_list,
+ GL_STATIC_DRAW));
+}
+
+GeometryBinding::~GeometryBinding() {
+ GLC(context_, context_->deleteBuffer(quad_vertices_vbo_));
+ GLC(context_, context_->deleteBuffer(quad_elements_vbo_));
+}
+
+void GeometryBinding::PrepareForDraw() {
+ GLC(context_,
+ context_->bindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_elements_vbo_));
+
+ GLC(context_, context_->bindBuffer(GL_ARRAY_BUFFER, quad_vertices_vbo_));
+ GLC(context_,
+ context_->vertexAttribPointer(
+ PositionAttribLocation(), 3, GL_FLOAT, false, 6 * sizeof(float), 0));
+ GLC(context_,
+ context_->vertexAttribPointer(TexCoordAttribLocation(),
+ 2,
+ GL_FLOAT,
+ false,
+ 6 * sizeof(float),
+ 3 * sizeof(float)));
+ GLC(context_,
+ context_->vertexAttribPointer(TriangleIndexAttribLocation(),
+ 1,
+ GL_FLOAT,
+ false,
+ 6 * sizeof(float),
+ 5 * sizeof(float)));
+ GLC(context_, context_->enableVertexAttribArray(PositionAttribLocation()));
+ GLC(context_, context_->enableVertexAttribArray(TexCoordAttribLocation()));
+ GLC(context_,
+ context_->enableVertexAttribArray(TriangleIndexAttribLocation()));
+}
+
+} // namespace cc
diff --git a/cc/output/geometry_binding.h b/cc/output/geometry_binding.h
new file mode 100644
index 0000000..74321aa
--- /dev/null
+++ b/cc/output/geometry_binding.h
@@ -0,0 +1,38 @@
+// Copyright 2011 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_OUTPUT_GEOMETRY_BINDING_H_
+#define CC_OUTPUT_GEOMETRY_BINDING_H_
+
+namespace gfx { class RectF; }
+
+namespace WebKit { class WebGraphicsContext3D; }
+
+namespace cc {
+
+class GeometryBinding {
+ public:
+ GeometryBinding(WebKit::WebGraphicsContext3D* context,
+ const gfx::RectF& quad_vertex_rect);
+ ~GeometryBinding();
+
+ void PrepareForDraw();
+
+ // All layer shaders share the same attribute locations for the vertex
+ // positions and texture coordinates. This allows switching shaders without
+ // rebinding attribute arrays.
+ static int PositionAttribLocation() { return 0; }
+ static int TexCoordAttribLocation() { return 1; }
+ static int TriangleIndexAttribLocation() { return 2; }
+
+ private:
+ WebKit::WebGraphicsContext3D* context_;
+
+ unsigned quad_vertices_vbo_;
+ unsigned quad_elements_vbo_;
+};
+
+} // namespace cc
+
+#endif // CC_OUTPUT_GEOMETRY_BINDING_H_
diff --git a/cc/output/gl_frame_data.cc b/cc/output/gl_frame_data.cc
new file mode 100644
index 0000000..b5b5721
--- /dev/null
+++ b/cc/output/gl_frame_data.cc
@@ -0,0 +1,15 @@
+// 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/output/gl_frame_data.h"
+
+namespace cc {
+
+GLFrameData::GLFrameData()
+ : sync_point(0) {
+}
+
+GLFrameData::~GLFrameData() {}
+
+} // namespace cc
diff --git a/cc/output/gl_frame_data.h b/cc/output/gl_frame_data.h
new file mode 100644
index 0000000..e6d227f
--- /dev/null
+++ b/cc/output/gl_frame_data.h
@@ -0,0 +1,29 @@
+// 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_OUTPUT_GL_FRAME_DATA_H_
+#define CC_OUTPUT_GL_FRAME_DATA_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "cc/base/cc_export.h"
+#include "gpu/command_buffer/common/mailbox.h"
+#include "ui/gfx/size.h"
+
+namespace cc {
+
+class CC_EXPORT GLFrameData {
+ public:
+ GLFrameData();
+ ~GLFrameData();
+
+ gpu::Mailbox mailbox;
+ uint32 sync_point;
+ gfx::Size size;
+};
+
+} // namespace cc
+
+#endif // CC_OUTPUT_GL_FRAME_DATA_H_
diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc
new file mode 100644
index 0000000..1de4180
--- /dev/null
+++ b/cc/output/gl_renderer.cc
@@ -0,0 +1,2232 @@
+// Copyright 2010 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/output/gl_renderer.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/strings/string_split.h"
+#include "build/build_config.h"
+#include "cc/base/math_util.h"
+#include "cc/damage_tracker.h"
+#include "cc/layer_quad.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/output/compositor_frame_metadata.h"
+#include "cc/output/context_provider.h"
+#include "cc/output/geometry_binding.h"
+#include "cc/output/gl_frame_data.h"
+#include "cc/output/output_surface.h"
+#include "cc/output/render_surface_filters.h"
+#include "cc/priority_calculator.h"
+#include "cc/proxy.h"
+#include "cc/render_pass.h"
+#include "cc/scoped_resource.h"
+#include "cc/single_thread_proxy.h"
+#include "cc/stream_video_draw_quad.h"
+#include "cc/texture_draw_quad.h"
+#include "cc/video_layer_impl.h"
+#include "gpu/GLES2/gl2extchromium.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/gpu/GrContext.h"
+#include "third_party/skia/include/gpu/GrTexture.h"
+#include "third_party/skia/include/gpu/SkGpuDevice.h"
+#include "third_party/skia/include/gpu/SkGrTexturePixelRef.h"
+#include "ui/gfx/quad_f.h"
+#include "ui/gfx/rect_conversions.h"
+
+using WebKit::WebGraphicsContext3D;
+using WebKit::WebGraphicsMemoryAllocation;
+
+namespace cc {
+
+namespace {
+
+// TODO(epenner): This should probably be moved to output surface.
+//
+// This implements a simple fence based on client side swaps.
+// This is to isolate the ResourceProvider from 'frames' which
+// it shouldn't need to care about, while still allowing us to
+// enforce good texture recycling behavior strictly throughout
+// the compositor (don't recycle a texture while it's in use).
+class SimpleSwapFence : public ResourceProvider::Fence {
+ public:
+ SimpleSwapFence() : has_passed_(false) {}
+ virtual bool HasPassed() OVERRIDE { return has_passed_; }
+ void SetHasPassed() { has_passed_ = true; }
+ private:
+ virtual ~SimpleSwapFence() {}
+ bool has_passed_;
+};
+
+bool NeedsIOSurfaceReadbackWorkaround() {
+#if defined(OS_MACOSX)
+ return true;
+#else
+ return false;
+#endif
+}
+
+} // anonymous namespace
+
+scoped_ptr<GLRenderer> GLRenderer::Create(RendererClient* client,
+ OutputSurface* output_surface,
+ ResourceProvider* resource_provider) {
+ scoped_ptr<GLRenderer> renderer(
+ new GLRenderer(client, output_surface, resource_provider));
+ if (!renderer->Initialize())
+ return scoped_ptr<GLRenderer>();
+
+ return renderer.Pass();
+}
+
+GLRenderer::GLRenderer(RendererClient* client,
+ OutputSurface* output_surface,
+ ResourceProvider* resource_provider)
+ : DirectRenderer(client, resource_provider),
+ offscreen_framebuffer_id_(0),
+ shared_geometry_quad_(gfx::RectF(-0.5f, -0.5f, 1.0f, 1.0f)),
+ output_surface_(output_surface),
+ context_(output_surface->context3d()),
+ is_viewport_changed_(false),
+ is_backbuffer_discarded_(false),
+ discard_backbuffer_when_not_visible_(false),
+ is_using_bind_uniform_(false),
+ visible_(true),
+ is_scissor_enabled_(false) {
+ DCHECK(context_);
+}
+
+bool GLRenderer::Initialize() {
+ if (!context_->makeContextCurrent())
+ return false;
+
+ context_->setContextLostCallback(this);
+ context_->pushGroupMarkerEXT("CompositorContext");
+
+ std::string extensions_string =
+ UTF16ToASCII(context_->getString(GL_EXTENSIONS));
+ std::vector<std::string> extensions_list;
+ base::SplitString(extensions_string, ' ', &extensions_list);
+ std::set<std::string> extensions(extensions_list.begin(),
+ extensions_list.end());
+
+ if (Settings().acceleratePainting &&
+ extensions.count("GL_EXT_texture_format_BGRA8888") &&
+ extensions.count("GL_EXT_read_format_bgra"))
+ capabilities_.using_accelerated_painting = true;
+ else
+ capabilities_.using_accelerated_painting = false;
+
+ capabilities_.using_partial_swap =
+ Settings().partialSwapEnabled &&
+ extensions.count("GL_CHROMIUM_post_sub_buffer");
+
+ // Use the swapBuffers callback only with the threaded proxy.
+ if (client_->HasImplThread())
+ capabilities_.using_swap_complete_callback =
+ extensions.count("GL_CHROMIUM_swapbuffers_complete_callback");
+ if (capabilities_.using_swap_complete_callback)
+ context_->setSwapBuffersCompleteCallbackCHROMIUM(this);
+
+ capabilities_.using_set_visibility =
+ extensions.count("GL_CHROMIUM_set_visibility");
+
+ if (extensions.count("GL_CHROMIUM_iosurface"))
+ DCHECK(extensions.count("GL_ARB_texture_rectangle"));
+
+ capabilities_.using_gpu_memory_manager =
+ extensions.count("GL_CHROMIUM_gpu_memory_manager") &&
+ Settings().useMemoryManagement;
+ if (capabilities_.using_gpu_memory_manager)
+ context_->setMemoryAllocationChangedCallbackCHROMIUM(this);
+
+ capabilities_.using_egl_image = extensions.count("GL_OES_EGL_image_external");
+
+ capabilities_.max_texture_size = resource_provider_->max_texture_size();
+ capabilities_.best_texture_format = resource_provider_->best_texture_format();
+
+ // The updater can access textures while the GLRenderer is using them.
+ capabilities_.allow_partial_texture_updates = true;
+
+ // Check for texture fast paths. Currently we always use MO8 textures,
+ // so we only need to avoid POT textures if we have an NPOT fast-path.
+ capabilities_.avoid_pow2_textures =
+ extensions.count("GL_CHROMIUM_fast_NPOT_MO8_textures");
+
+ capabilities_.using_offscreen_context3d = true;
+
+ is_using_bind_uniform_ =
+ extensions.count("GL_CHROMIUM_bind_uniform_location");
+
+ // Make sure scissoring starts as disabled.
+ GLC(context_, context_->disable(GL_SCISSOR_TEST));
+ DCHECK(!is_scissor_enabled_);
+
+ if (!InitializeSharedObjects())
+ return false;
+
+ // Make sure the viewport and context gets initialized, even if it is to zero.
+ ViewportChanged();
+ return true;
+}
+
+GLRenderer::~GLRenderer() {
+ context_->setSwapBuffersCompleteCallbackCHROMIUM(NULL);
+ context_->setMemoryAllocationChangedCallbackCHROMIUM(NULL);
+ context_->setContextLostCallback(NULL);
+ CleanupSharedObjects();
+}
+
+const RendererCapabilities& GLRenderer::Capabilities() const {
+ return capabilities_;
+}
+
+WebGraphicsContext3D* GLRenderer::Context() { return context_; }
+
+void GLRenderer::DebugGLCall(WebGraphicsContext3D* context,
+ const char* command,
+ const char* file,
+ int line) {
+ unsigned long error = context->getError();
+ if (error != GL_NO_ERROR)
+ LOG(ERROR) << "GL command failed: File: " << file << "\n\tLine " << line
+ << "\n\tcommand: " << command << ", error "
+ << static_cast<int>(error) << "\n";
+}
+
+void GLRenderer::SetVisible(bool visible) {
+ if (visible_ == visible)
+ return;
+ visible_ = visible;
+
+ EnforceMemoryPolicy();
+
+ // TODO: Replace setVisibilityCHROMIUM with an extension to explicitly manage
+ // front/backbuffers
+ // crbug.com/116049
+ if (capabilities_.using_set_visibility)
+ context_->setVisibilityCHROMIUM(visible);
+}
+
+void GLRenderer::SendManagedMemoryStats(size_t bytes_visible,
+ size_t bytes_visible_and_nearby,
+ size_t bytes_allocated) {
+ WebKit::WebGraphicsManagedMemoryStats stats;
+ stats.bytesVisible = bytes_visible;
+ stats.bytesVisibleAndNearby = bytes_visible_and_nearby;
+ stats.bytesAllocated = bytes_allocated;
+ stats.backbufferRequested = !is_backbuffer_discarded_;
+ context_->sendManagedMemoryStatsCHROMIUM(&stats);
+}
+
+void GLRenderer::ReleaseRenderPassTextures() { render_pass_textures_.clear(); }
+
+void GLRenderer::ViewportChanged() { is_viewport_changed_ = true; }
+
+void GLRenderer::ClearFramebuffer(DrawingFrame& frame) {
+ // On DEBUG builds, opaque render passes are cleared to blue to easily see
+ // regions that were not drawn on the screen.
+ if (frame.current_render_pass->has_transparent_background)
+ GLC(context_, context_->clearColor(0, 0, 0, 0));
+ else
+ GLC(context_, context_->clearColor(0, 0, 1, 1));
+
+#ifdef NDEBUG
+ if (frame.current_render_pass->has_transparent_background)
+#endif
+ context_->clear(GL_COLOR_BUFFER_BIT);
+}
+
+void GLRenderer::BeginDrawingFrame(DrawingFrame& frame) {
+ // FIXME: Remove this once backbuffer is automatically recreated on first use
+ EnsureBackbuffer();
+
+ if (ViewportSize().IsEmpty())
+ return;
+
+ TRACE_EVENT0("cc", "GLRenderer::drawLayers");
+ if (is_viewport_changed_) {
+ // Only reshape when we know we are going to draw. Otherwise, the reshape
+ // can leave the window at the wrong size if we never draw and the proper
+ // viewport size is never set.
+ is_viewport_changed_ = false;
+ output_surface_->Reshape(gfx::Size(ViewportWidth(), ViewportHeight()));
+ }
+
+ MakeContextCurrent();
+ // Bind the common vertex attributes used for drawing all the layers.
+ shared_geometry_->PrepareForDraw();
+
+ GLC(context_, context_->disable(GL_DEPTH_TEST));
+ GLC(context_, context_->disable(GL_CULL_FACE));
+ GLC(context_, context_->colorMask(true, true, true, true));
+ GLC(context_, context_->enable(GL_BLEND));
+ blend_shadow_ = true;
+ GLC(context_, context_->blendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
+ GLC(Context(), Context()->activeTexture(GL_TEXTURE0));
+ program_shadow_ = 0;
+}
+
+void GLRenderer::DoNoOp() {
+ GLC(context_, context_->bindFramebuffer(GL_FRAMEBUFFER, 0));
+ GLC(context_, context_->flush());
+}
+
+void GLRenderer::DoDrawQuad(DrawingFrame& frame, const DrawQuad* quad) {
+ DCHECK(quad->rect.Contains(quad->visible_rect));
+ if (quad->material != DrawQuad::TEXTURE_CONTENT) {
+ FlushTextureQuadCache();
+ }
+
+ switch (quad->material) {
+ case DrawQuad::INVALID:
+ NOTREACHED();
+ break;
+ case DrawQuad::CHECKERBOARD:
+ DrawCheckerboardQuad(frame, CheckerboardDrawQuad::MaterialCast(quad));
+ break;
+ case DrawQuad::DEBUG_BORDER:
+ DrawDebugBorderQuad(frame, DebugBorderDrawQuad::MaterialCast(quad));
+ break;
+ case DrawQuad::IO_SURFACE_CONTENT:
+ DrawIOSurfaceQuad(frame, IOSurfaceDrawQuad::MaterialCast(quad));
+ break;
+ case DrawQuad::RENDER_PASS:
+ DrawRenderPassQuad(frame, RenderPassDrawQuad::MaterialCast(quad));
+ break;
+ case DrawQuad::SOLID_COLOR:
+ DrawSolidColorQuad(frame, SolidColorDrawQuad::MaterialCast(quad));
+ break;
+ case DrawQuad::STREAM_VIDEO_CONTENT:
+ DrawStreamVideoQuad(frame, StreamVideoDrawQuad::MaterialCast(quad));
+ break;
+ case DrawQuad::TEXTURE_CONTENT:
+ EnqueueTextureQuad(frame, TextureDrawQuad::MaterialCast(quad));
+ break;
+ case DrawQuad::TILED_CONTENT:
+ DrawTileQuad(frame, TileDrawQuad::MaterialCast(quad));
+ break;
+ case DrawQuad::YUV_VIDEO_CONTENT:
+ DrawYUVVideoQuad(frame, YUVVideoDrawQuad::MaterialCast(quad));
+ break;
+ }
+}
+
+void GLRenderer::DrawCheckerboardQuad(const DrawingFrame& frame,
+ const CheckerboardDrawQuad* quad) {
+ SetBlendEnabled(quad->ShouldDrawWithBlending());
+
+ const TileCheckerboardProgram* program = GetTileCheckerboardProgram();
+ DCHECK(program && (program->initialized() || IsContextLost()));
+ SetUseProgram(program->program());
+
+ SkColor color = quad->color;
+ GLC(Context(),
+ Context()->uniform4f(program->fragment_shader().colorLocation(),
+ SkColorGetR(color) * (1.0f / 255.0f),
+ SkColorGetG(color) * (1.0f / 255.0f),
+ SkColorGetB(color) * (1.0f / 255.0f),
+ 1));
+
+ const int checkerboard_width = 16;
+ float frequency = 1.0f / checkerboard_width;
+
+ gfx::Rect tile_rect = quad->rect;
+ float tex_offset_x = tile_rect.x() % checkerboard_width;
+ float tex_offset_y = tile_rect.y() % checkerboard_width;
+ float tex_scale_x = tile_rect.width();
+ float tex_scale_y = tile_rect.height();
+ GLC(Context(),
+ Context()->uniform4f(program->fragment_shader().texTransformLocation(),
+ tex_offset_x,
+ tex_offset_y,
+ tex_scale_x,
+ tex_scale_y));
+
+ GLC(Context(),
+ Context()->uniform1f(program->fragment_shader().frequencyLocation(),
+ frequency));
+
+ SetShaderOpacity(quad->opacity(), program->fragment_shader().alphaLocation());
+ DrawQuadGeometry(frame,
+ quad->quadTransform(),
+ quad->rect,
+ program->vertex_shader().matrixLocation());
+}
+
+void GLRenderer::DrawDebugBorderQuad(const DrawingFrame& frame,
+ const DebugBorderDrawQuad* quad) {
+ SetBlendEnabled(quad->ShouldDrawWithBlending());
+
+ static float gl_matrix[16];
+ const DebugBorderProgram* program = GetDebugBorderProgram();
+ DCHECK(program && (program->initialized() || IsContextLost()));
+ SetUseProgram(program->program());
+
+ // Use the full quad_rect for debug quads to not move the edges based on
+ // partial swaps.
+ gfx::Rect layer_rect = quad->rect;
+ gfx::Transform render_matrix = quad->quadTransform();
+ render_matrix.Translate(0.5f * layer_rect.width() + layer_rect.x(),
+ 0.5f * layer_rect.height() + layer_rect.y());
+ render_matrix.Scale(layer_rect.width(), layer_rect.height());
+ GLRenderer::ToGLMatrix(&gl_matrix[0],
+ frame.projection_matrix * render_matrix);
+ GLC(Context(),
+ Context()->uniformMatrix4fv(
+ program->vertex_shader().matrixLocation(), 1, false, &gl_matrix[0]));
+
+ SkColor color = quad->color;
+ float alpha = SkColorGetA(color) * (1.0f / 255.0f);
+
+ GLC(Context(),
+ Context()->uniform4f(program->fragment_shader().colorLocation(),
+ (SkColorGetR(color) * (1.0f / 255.0f)) * alpha,
+ (SkColorGetG(color) * (1.0f / 255.0f)) * alpha,
+ (SkColorGetB(color) * (1.0f / 255.0f)) * alpha,
+ alpha));
+
+ GLC(Context(), Context()->lineWidth(quad->width));
+
+ // The indices for the line are stored in the same array as the triangle
+ // indices.
+ GLC(Context(),
+ Context()->drawElements(GL_LINE_LOOP, 4, GL_UNSIGNED_SHORT, 0));
+}
+
+static inline SkBitmap ApplyFilters(GLRenderer* renderer,
+ const WebKit::WebFilterOperations& filters,
+ ScopedResource* source_texture_resource) {
+ if (filters.isEmpty())
+ return SkBitmap();
+
+ ContextProvider* offscreen_contexts =
+ renderer->resource_provider()->offscreen_context_provider();
+ if (!offscreen_contexts || !offscreen_contexts->GrContext())
+ return SkBitmap();
+
+ ResourceProvider::ScopedWriteLockGL lock(renderer->resource_provider(),
+ source_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();
+
+ SkBitmap source =
+ RenderSurfaceFilters::Apply(filters,
+ lock.texture_id(),
+ source_texture_resource->size(),
+ offscreen_contexts->GrContext());
+
+ // 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->resource_provider()->GraphicsContext3D()->makeContextCurrent();
+ return source;
+}
+
+static SkBitmap ApplyImageFilter(GLRenderer* renderer,
+ SkImageFilter* filter,
+ ScopedResource* source_texture_resource) {
+ if (!filter)
+ return SkBitmap();
+
+ ContextProvider* offscreen_contexts =
+ renderer->resource_provider()->offscreen_context_provider();
+ if (!offscreen_contexts || !offscreen_contexts->GrContext())
+ return SkBitmap();
+
+ ResourceProvider::ScopedWriteLockGL lock(renderer->resource_provider(),
+ source_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.fWidth = source_texture_resource->size().width();
+ backend_texture_description.fHeight =
+ source_texture_resource->size().height();
+ backend_texture_description.fConfig = kSkia8888_GrPixelConfig;
+ backend_texture_description.fTextureHandle = lock.texture_id();
+ backend_texture_description.fOrigin = kTopLeft_GrSurfaceOrigin;
+ skia::RefPtr<GrTexture> 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_texture_resource->size().width(),
+ source_texture_resource->size().height());
+ skia::RefPtr<SkGrPixelRef> pixel_ref =
+ skia::AdoptRef(new SkGrPixelRef(texture.get()));
+ source.setPixelRef(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 = kTopLeft_GrSurfaceOrigin;
+ GrAutoScratchTexture scratchTexture(
+ offscreen_contexts->GrContext(), desc, GrContext::kExact_ScratchTexMatch);
+ skia::RefPtr<GrTexture> backing_store =
+ skia::AdoptRef(scratchTexture.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.
+ SkPaint paint;
+ paint.setImageFilter(filter);
+ canvas.clear(SK_ColorTRANSPARENT);
+ 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->resource_provider()->GraphicsContext3D()->makeContextCurrent();
+
+ return device.accessBitmap(false);
+}
+
+scoped_ptr<ScopedResource> GLRenderer::DrawBackgroundFilters(
+ DrawingFrame& frame,
+ const RenderPassDrawQuad* quad,
+ const gfx::Transform& contents_device_transform,
+ const gfx::Transform& contents_device_transform_inverse) {
+ // 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:
+ // 1. Compute a bounding box around the pixels that will be visible through
+ // the quad.
+ // 2. Read the pixels in the bounding box into a buffer R.
+ // 3. Apply the background filter to R, so that it is applied in the pixels'
+ // coordinate space.
+ // 4. 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.
+ // 5. Draw the background texture for the contents using the same transform as
+ // used to draw the contents itself. This is done without blending to replace
+ // the current background pixels with the new filtered background.
+ // 6. Draw the contents of the quad over drop of the new background with
+ // blending, as per usual. The filtered background pixels will show through
+ // any non-opaque pixels in this draws.
+ //
+ // Pixel copies in this algorithm occur at steps 2, 3, 4, and 5.
+
+ // FIXME: When this algorithm changes, update
+ // LayerTreeHost::prioritizeTextures() accordingly.
+
+ const WebKit::WebFilterOperations& filters = quad->background_filters;
+ if (filters.isEmpty())
+ return scoped_ptr<ScopedResource>();
+
+ // FIXME: 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>();
+ DCHECK(!frame.current_texture);
+
+ // FIXME: Do a single readback for both the surface and replica and cache the
+ // filtered results (once filter textures are not reused).
+ gfx::Rect device_rect = gfx::ToEnclosingRect(MathUtil::MapClippedRect(
+ contents_device_transform, SharedGeometryQuad().BoundingBox()));
+
+ int top, right, bottom, left;
+ filters.getOutsets(top, right, bottom, left);
+ device_rect.Inset(-left, -top, -right, -bottom);
+
+ device_rect.Intersect(frame.current_render_pass->output_rect);
+
+ scoped_ptr<ScopedResource> device_background_texture =
+ ScopedResource::create(resource_provider_);
+ if (!GetFramebufferTexture(device_background_texture.get(), device_rect))
+ return scoped_ptr<ScopedResource>();
+
+ SkBitmap filtered_device_background =
+ ApplyFilters(this, filters, device_background_texture.get());
+ if (!filtered_device_background.getTexture())
+ return scoped_ptr<ScopedResource>();
+
+ GrTexture* texture =
+ reinterpret_cast<GrTexture*>(filtered_device_background.getTexture());
+ int filtered_device_background_texture_id = texture->getTextureHandle();
+
+ scoped_ptr<ScopedResource> background_texture =
+ ScopedResource::create(resource_provider_);
+ if (!background_texture->Allocate(quad->rect.size(),
+ GL_RGBA,
+ ResourceProvider::TextureUsageFramebuffer))
+ return scoped_ptr<ScopedResource>();
+
+ const RenderPass* target_render_pass = frame.current_render_pass;
+ bool using_background_texture =
+ UseScopedTexture(frame, background_texture.get(), quad->rect);
+
+ if (using_background_texture) {
+ // Copy the readback pixels from device to the background texture for the
+ // surface.
+ gfx::Transform device_to_framebuffer_transform;
+ device_to_framebuffer_transform.Translate(
+ quad->rect.width() * 0.5f + quad->rect.x(),
+ quad->rect.height() * 0.5f + quad->rect.y());
+ device_to_framebuffer_transform.Scale(quad->rect.width(),
+ quad->rect.height());
+ device_to_framebuffer_transform.PreconcatTransform(
+ contents_device_transform_inverse);
+
+#ifndef NDEBUG
+ GLC(Context(), Context()->clearColor(0, 0, 1, 1));
+ Context()->clear(GL_COLOR_BUFFER_BIT);
+#endif
+
+ CopyTextureToFramebuffer(frame,
+ filtered_device_background_texture_id,
+ device_rect,
+ device_to_framebuffer_transform);
+ }
+
+ UseRenderPass(frame, target_render_pass);
+
+ if (!using_background_texture)
+ return scoped_ptr<ScopedResource>();
+ return background_texture.Pass();
+}
+
+void GLRenderer::DrawRenderPassQuad(DrawingFrame& frame,
+ const RenderPassDrawQuad* quad) {
+ SetBlendEnabled(quad->ShouldDrawWithBlending());
+
+ CachedResource* contents_texture =
+ render_pass_textures_.get(quad->render_pass_id);
+ if (!contents_texture || !contents_texture->id())
+ return;
+
+ gfx::Transform quad_rect_matrix;
+ QuadRectTransform(&quad_rect_matrix, quad->quadTransform(), quad->rect);
+ gfx::Transform contents_device_transform =
+ frame.window_matrix * frame.projection_matrix * quad_rect_matrix;
+ contents_device_transform.FlattenTo2d();
+
+ // Can only draw surface if device matrix is invertible.
+ gfx::Transform contents_device_transform_inverse(
+ gfx::Transform::kSkipInitialization);
+ if (!contents_device_transform.GetInverse(&contents_device_transform_inverse))
+ return;
+
+ scoped_ptr<ScopedResource> background_texture =
+ DrawBackgroundFilters(frame,
+ quad,
+ contents_device_transform,
+ contents_device_transform_inverse);
+
+ // FIXME: Cache this value so that we don't have to do it for both the surface
+ // and its replica. Apply filters to the contents texture.
+ SkBitmap filter_bitmap;
+ if (quad->filter) {
+ filter_bitmap =
+ ApplyImageFilter(this, quad->filter.get(), contents_texture);
+ } else {
+ filter_bitmap = ApplyFilters(this, quad->filters, contents_texture);
+ }
+
+ // Draw the background texture if there is one.
+ if (background_texture) {
+ DCHECK(background_texture->size() == quad->rect.size());
+ ResourceProvider::ScopedReadLockGL lock(resource_provider_,
+ background_texture->id());
+ CopyTextureToFramebuffer(
+ frame, lock.texture_id(), quad->rect, quad->quadTransform());
+ }
+
+ bool clipped = false;
+ gfx::QuadF device_quad = MathUtil::MapQuad(
+ contents_device_transform, SharedGeometryQuad(), &clipped);
+ DCHECK(!clipped);
+ LayerQuad deviceLayerBounds(gfx::QuadF(device_quad.BoundingBox()));
+ LayerQuad device_layer_edges(device_quad);
+
+ // Use anti-aliasing programs only when necessary.
+ bool use_aa = (!device_quad.IsRectilinear() ||
+ !device_quad.BoundingBox().IsExpressibleAsRect());
+ if (use_aa) {
+ deviceLayerBounds.InflateAntiAliasingDistance();
+ device_layer_edges.InflateAntiAliasingDistance();
+ }
+
+ scoped_ptr<ResourceProvider::ScopedReadLockGL> mask_resource_lock;
+ unsigned mask_texture_id = 0;
+ if (quad->mask_resource_id) {
+ mask_resource_lock.reset(new ResourceProvider::ScopedReadLockGL(
+ resource_provider_, quad->mask_resource_id));
+ mask_texture_id = mask_resource_lock->texture_id();
+ }
+
+ // FIXME: use the background_texture and blend the background in with this
+ // draw instead of having a separate copy of the background texture.
+
+ scoped_ptr<ResourceProvider::ScopedReadLockGL> contents_resource_lock;
+ if (filter_bitmap.getTexture()) {
+ GrTexture* texture =
+ reinterpret_cast<GrTexture*>(filter_bitmap.getTexture());
+ Context()->bindTexture(GL_TEXTURE_2D, texture->getTextureHandle());
+ } else
+ contents_resource_lock = make_scoped_ptr(
+ new ResourceProvider::ScopedSamplerGL(resource_provider_,
+ contents_texture->id(),
+ GL_TEXTURE_2D,
+ GL_LINEAR));
+
+ int shader_quad_location = -1;
+ int shader_edge_location = -1;
+ int shader_mask_sampler_location = -1;
+ int shader_mask_tex_coord_scale_location = -1;
+ int shader_mask_tex_coord_offset_location = -1;
+ int shader_matrix_location = -1;
+ int shader_alpha_location = -1;
+ int shader_tex_transform_location = -1;
+ int shader_tex_scale_location = -1;
+
+ if (use_aa && mask_texture_id) {
+ const RenderPassMaskProgramAA* program = GetRenderPassMaskProgramAA();
+ SetUseProgram(program->program());
+ GLC(Context(),
+ Context()->uniform1i(program->fragment_shader().samplerLocation(), 0));
+
+ shader_quad_location = program->vertex_shader().pointLocation();
+ shader_edge_location = program->fragment_shader().edgeLocation();
+ shader_mask_sampler_location =
+ program->fragment_shader().maskSamplerLocation();
+ shader_mask_tex_coord_scale_location =
+ program->fragment_shader().maskTexCoordScaleLocation();
+ shader_mask_tex_coord_offset_location =
+ program->fragment_shader().maskTexCoordOffsetLocation();
+ shader_matrix_location = program->vertex_shader().matrixLocation();
+ shader_alpha_location = program->fragment_shader().alphaLocation();
+ shader_tex_scale_location = program->vertex_shader().texScaleLocation();
+ } else if (!use_aa && mask_texture_id) {
+ const RenderPassMaskProgram* program = GetRenderPassMaskProgram();
+ SetUseProgram(program->program());
+ GLC(Context(),
+ Context()->uniform1i(program->fragment_shader().samplerLocation(), 0));
+
+ shader_mask_sampler_location =
+ program->fragment_shader().maskSamplerLocation();
+ shader_mask_tex_coord_scale_location =
+ program->fragment_shader().maskTexCoordScaleLocation();
+ shader_mask_tex_coord_offset_location =
+ program->fragment_shader().maskTexCoordOffsetLocation();
+ shader_matrix_location = program->vertex_shader().matrixLocation();
+ shader_alpha_location = program->fragment_shader().alphaLocation();
+ shader_tex_transform_location =
+ program->vertex_shader().texTransformLocation();
+ } else if (use_aa && !mask_texture_id) {
+ const RenderPassProgramAA* program = GetRenderPassProgramAA();
+ SetUseProgram(program->program());
+ GLC(Context(),
+ Context()->uniform1i(program->fragment_shader().samplerLocation(), 0));
+
+ shader_quad_location = program->vertex_shader().pointLocation();
+ shader_edge_location = program->fragment_shader().edgeLocation();
+ shader_matrix_location = program->vertex_shader().matrixLocation();
+ shader_alpha_location = program->fragment_shader().alphaLocation();
+ shader_tex_scale_location = program->vertex_shader().texScaleLocation();
+ } else {
+ const RenderPassProgram* program = GetRenderPassProgram();
+ SetUseProgram(program->program());
+ GLC(Context(),
+ Context()->uniform1i(program->fragment_shader().samplerLocation(), 0));
+
+ shader_matrix_location = program->vertex_shader().matrixLocation();
+ shader_alpha_location = program->fragment_shader().alphaLocation();
+ shader_tex_transform_location =
+ program->vertex_shader().texTransformLocation();
+ }
+
+ float tex_scale_x =
+ quad->rect.width() / static_cast<float>(contents_texture->size().width());
+ float tex_scale_y = quad->rect.height() /
+ static_cast<float>(contents_texture->size().height());
+ DCHECK_LE(tex_scale_x, 1.0f);
+ DCHECK_LE(tex_scale_y, 1.0f);
+
+ if (shader_tex_transform_location != -1) {
+ GLC(Context(),
+ Context()->uniform4f(shader_tex_transform_location,
+ 0.0f,
+ 0.0f,
+ tex_scale_x,
+ tex_scale_y));
+ } else if (shader_tex_scale_location != -1) {
+ GLC(Context(),
+ Context()->uniform2f(
+ shader_tex_scale_location, tex_scale_x, tex_scale_y));
+ } else {
+ DCHECK(IsContextLost());
+ }
+
+ if (shader_mask_sampler_location != -1) {
+ DCHECK(shader_mask_tex_coord_scale_location != 1);
+ DCHECK(shader_mask_tex_coord_offset_location != 1);
+ GLC(Context(), Context()->activeTexture(GL_TEXTURE1));
+ GLC(Context(), Context()->uniform1i(shader_mask_sampler_location, 1));
+ GLC(Context(),
+ Context()->uniform2f(shader_mask_tex_coord_offset_location,
+ quad->mask_uv_rect.x(),
+ quad->mask_uv_rect.y()));
+ GLC(Context(),
+ Context()->uniform2f(shader_mask_tex_coord_scale_location,
+ quad->mask_uv_rect.width() / tex_scale_x,
+ quad->mask_uv_rect.height() / tex_scale_y));
+ resource_provider_->BindForSampling(
+ quad->mask_resource_id, GL_TEXTURE_2D, GL_LINEAR);
+ GLC(Context(), Context()->activeTexture(GL_TEXTURE0));
+ }
+
+ if (shader_edge_location != -1) {
+ float edge[24];
+ device_layer_edges.ToFloatArray(edge);
+ deviceLayerBounds.ToFloatArray(&edge[12]);
+ GLC(Context(), Context()->uniform3fv(shader_edge_location, 8, edge));
+ }
+
+ // 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,
+ device_layer_edges.ToQuadF(),
+ &clipped);
+ DCHECK(!clipped);
+
+ SetShaderOpacity(quad->opacity(), shader_alpha_location);
+ SetShaderQuadF(surface_quad, shader_quad_location);
+ DrawQuadGeometry(
+ frame, quad->quadTransform(), quad->rect, shader_matrix_location);
+
+ // Flush the compositor context before the filter bitmap goes out of
+ // scope, so the draw gets processed before the filter texture gets deleted.
+ if (filter_bitmap.getTexture())
+ context_->flush();
+}
+
+struct SolidColorProgramUniforms {
+ unsigned program;
+ unsigned matrix_location;
+ unsigned color_location;
+ unsigned point_location;
+ unsigned tex_scale_location;
+ unsigned edge_location;
+};
+
+template<class T>
+static void SolidColorUniformLocation(T program,
+ SolidColorProgramUniforms* uniforms) {
+ uniforms->program = program->program();
+ uniforms->matrix_location = program->vertex_shader().matrixLocation();
+ uniforms->color_location = program->fragment_shader().colorLocation();
+ uniforms->point_location = program->vertex_shader().pointLocation();
+ uniforms->tex_scale_location = program->vertex_shader().texScaleLocation();
+ uniforms->edge_location = program->fragment_shader().edgeLocation();
+}
+
+bool GLRenderer::SetupQuadForAntialiasing(
+ const gfx::Transform& device_transform,
+ const DrawQuad* quad,
+ gfx::QuadF* local_quad,
+ float edge[24]) const {
+ gfx::Rect tile_rect = quad->visible_rect;
+
+ bool clipped = false;
+ gfx::QuadF device_layer_quad = MathUtil::MapQuad(
+ device_transform, gfx::QuadF(quad->visibleContentRect()), &clipped);
+ DCHECK(!clipped);
+
+ // TODO(reveman): Axis-aligned is not enough to avoid anti-aliasing.
+ // Bounding rectangle for quad also needs to be expressible as an integer
+ // rectangle. crbug.com/169374
+ bool is_axis_aligned_in_target = device_layer_quad.IsRectilinear();
+ bool use_aa = !clipped && !is_axis_aligned_in_target && quad->IsEdge();
+
+ if (!use_aa)
+ return false;
+
+ LayerQuad device_layer_bounds(gfx::QuadF(device_layer_quad.BoundingBox()));
+ device_layer_bounds.InflateAntiAliasingDistance();
+
+ LayerQuad device_layer_edges(device_layer_quad);
+ device_layer_edges.InflateAntiAliasingDistance();
+
+ device_layer_edges.ToFloatArray(edge);
+ device_layer_bounds.ToFloatArray(&edge[12]);
+
+ gfx::PointF bottom_right = tile_rect.bottom_right();
+ gfx::PointF bottom_left = tile_rect.bottom_left();
+ gfx::PointF top_left = tile_rect.origin();
+ gfx::PointF top_right = tile_rect.top_right();
+
+ // Map points to device space.
+ bottom_right = MathUtil::MapPoint(device_transform, bottom_right, &clipped);
+ DCHECK(!clipped);
+ bottom_left = MathUtil::MapPoint(device_transform, bottom_left, &clipped);
+ DCHECK(!clipped);
+ top_left = MathUtil::MapPoint(device_transform, top_left, &clipped);
+ DCHECK(!clipped);
+ top_right = MathUtil::MapPoint(device_transform, top_right, &clipped);
+ DCHECK(!clipped);
+
+ LayerQuad::Edge bottom_edge(bottom_right, bottom_left);
+ LayerQuad::Edge left_edge(bottom_left, top_left);
+ LayerQuad::Edge top_edge(top_left, top_right);
+ LayerQuad::Edge right_edge(top_right, bottom_right);
+
+ // Only apply anti-aliasing to edges not clipped by culling or scissoring.
+ if (quad->IsTopEdge() && tile_rect.y() == quad->rect.y())
+ top_edge = device_layer_edges.top();
+ if (quad->IsLeftEdge() && tile_rect.x() == quad->rect.x())
+ left_edge = device_layer_edges.left();
+ if (quad->IsRightEdge() && tile_rect.right() == quad->rect.right())
+ right_edge = device_layer_edges.right();
+ if (quad->IsBottomEdge() && tile_rect.bottom() == quad->rect.bottom())
+ bottom_edge = device_layer_edges.bottom();
+
+ float sign = gfx::QuadF(tile_rect).IsCounterClockwise() ? -1 : 1;
+ bottom_edge.scale(sign);
+ left_edge.scale(sign);
+ top_edge.scale(sign);
+ right_edge.scale(sign);
+
+ // Create device space quad.
+ LayerQuad device_quad(left_edge, top_edge, right_edge, bottom_edge);
+
+ // Map device space quad to local space. deviceTransform has no 3d
+ // component since it was flattened, so we don't need to project. We should
+ // have already checked that the transform was uninvertible above.
+ gfx::Transform inverse_device_transform(
+ gfx::Transform::kSkipInitialization);
+ bool did_invert = device_transform.GetInverse(&inverse_device_transform);
+ DCHECK(did_invert);
+ *local_quad = MathUtil::MapQuad(
+ inverse_device_transform, device_quad.ToQuadF(), &clipped);
+ // We should not DCHECK(!clipped) here, because anti-aliasing inflation may
+ // cause deviceQuad to become clipped. To our knowledge this scenario does
+ // not need to be handled differently than the unclipped case.
+
+ return true;
+}
+
+void GLRenderer::DrawSolidColorQuad(const DrawingFrame& frame,
+ const SolidColorDrawQuad* quad) {
+ SetBlendEnabled(quad->ShouldDrawWithBlending());
+ gfx::Rect tile_rect = quad->visible_rect;
+
+ gfx::Transform device_transform =
+ frame.window_matrix * frame.projection_matrix * quad->quadTransform();
+ device_transform.FlattenTo2d();
+ if (!device_transform.IsInvertible())
+ return;
+
+ gfx::QuadF local_quad = gfx::QuadF(gfx::RectF(tile_rect));
+ float edge[24];
+ bool use_aa = SetupQuadForAntialiasing(
+ device_transform, quad, &local_quad, edge);
+
+ SolidColorProgramUniforms uniforms;
+ if (use_aa)
+ SolidColorUniformLocation(GetSolidColorProgramAA(), &uniforms);
+ else
+ SolidColorUniformLocation(GetSolidColorProgram(), &uniforms);
+ SetUseProgram(uniforms.program);
+
+ SkColor color = quad->color;
+ float opacity = quad->opacity();
+ float alpha = (SkColorGetA(color) * (1.0f / 255.0f)) * opacity;
+
+ GLC(Context(),
+ Context()->uniform4f(uniforms.color_location,
+ (SkColorGetR(color) * (1.0f / 255.0f)) * alpha,
+ (SkColorGetG(color) * (1.0f / 255.0f)) * alpha,
+ (SkColorGetB(color) * (1.0f / 255.0f)) * alpha,
+ alpha));
+
+ if (use_aa)
+ GLC(Context(), Context()->uniform3fv(uniforms.edge_location, 8, edge));
+
+ // Enable blending when the quad properties require it or if we decided
+ // to use antialiasing.
+ SetBlendEnabled(quad->ShouldDrawWithBlending() || use_aa);
+
+ // Normalize to tileRect.
+ local_quad.Scale(1.0f / tile_rect.width(), 1.0f / tile_rect.height());
+
+ SetShaderQuadF(local_quad, uniforms.point_location);
+
+ // The transform and vertex data are used to figure out the extents that the
+ // un-antialiased quad should have and which vertex this is and the float
+ // quad passed in via uniform is the actual geometry that gets used to draw
+ // it. This is why this centered rect is used and not the original quadRect.
+ gfx::RectF centered_rect(gfx::PointF(-0.5f * tile_rect.width(),
+ -0.5f * tile_rect.height()),
+ tile_rect.size());
+ DrawQuadGeometry(frame, quad->quadTransform(),
+ centered_rect, uniforms.matrix_location);
+}
+
+struct TileProgramUniforms {
+ unsigned program;
+ unsigned sampler_location;
+ unsigned vertex_tex_transform_location;
+ unsigned fragment_tex_transform_location;
+ unsigned edge_location;
+ unsigned matrix_location;
+ unsigned alpha_location;
+ unsigned point_location;
+};
+
+template <class T>
+static void TileUniformLocation(T program, TileProgramUniforms* uniforms) {
+ uniforms->program = program->program();
+ uniforms->vertex_tex_transform_location =
+ program->vertex_shader().vertexTexTransformLocation();
+ uniforms->matrix_location = program->vertex_shader().matrixLocation();
+ uniforms->point_location = program->vertex_shader().pointLocation();
+
+ uniforms->sampler_location = program->fragment_shader().samplerLocation();
+ uniforms->alpha_location = program->fragment_shader().alphaLocation();
+ uniforms->fragment_tex_transform_location =
+ program->fragment_shader().fragmentTexTransformLocation();
+ uniforms->edge_location = program->fragment_shader().edgeLocation();
+}
+
+void GLRenderer::DrawTileQuad(const DrawingFrame& frame,
+ const TileDrawQuad* quad) {
+ gfx::Rect tile_rect = quad->visible_rect;
+
+ gfx::RectF tex_coord_rect = quad->tex_coord_rect;
+ float tex_to_geom_scale_x = quad->rect.width() / tex_coord_rect.width();
+ float tex_to_geom_scale_y = quad->rect.height() / tex_coord_rect.height();
+
+ // tex_coord_rect corresponds to quad_rect, but quadVisibleRect may be
+ // smaller than quad_rect due to occlusion or clipping. Adjust
+ // tex_coord_rect to match.
+ gfx::Vector2d top_left_diff = tile_rect.origin() - quad->rect.origin();
+ gfx::Vector2d bottom_right_diff =
+ tile_rect.bottom_right() - quad->rect.bottom_right();
+ tex_coord_rect.Inset(top_left_diff.x() / tex_to_geom_scale_x,
+ top_left_diff.y() / tex_to_geom_scale_y,
+ -bottom_right_diff.x() / tex_to_geom_scale_x,
+ -bottom_right_diff.y() / tex_to_geom_scale_y);
+
+ gfx::RectF clamp_geom_rect(tile_rect);
+ gfx::RectF clamp_tex_rect(tex_coord_rect);
+ // Clamp texture coordinates to avoid sampling outside the layer
+ // by deflating the tile region half a texel or half a texel
+ // minus epsilon for one pixel layers. The resulting clamp region
+ // is mapped to the unit square by the vertex shader and mapped
+ // back to normalized texture coordinates by the fragment shader
+ // after being clamped to 0-1 range.
+ const float epsilon = 1.0f / 1024.0f;
+ float tex_clamp_x = std::min(0.5f, 0.5f * clamp_tex_rect.width() - epsilon);
+ float tex_clamp_y = std::min(0.5f, 0.5f * clamp_tex_rect.height() - epsilon);
+ float geom_clamp_x = std::min(tex_clamp_x * tex_to_geom_scale_x,
+ 0.5f * clamp_geom_rect.width() - epsilon);
+ float geom_clamp_y = std::min(tex_clamp_y * tex_to_geom_scale_y,
+ 0.5f * clamp_geom_rect.height() - epsilon);
+ clamp_geom_rect.Inset(geom_clamp_x, geom_clamp_y, geom_clamp_x, geom_clamp_y);
+ clamp_tex_rect.Inset(tex_clamp_x, tex_clamp_y, tex_clamp_x, tex_clamp_y);
+
+ // Map clamping rectangle to unit square.
+ float vertex_tex_translate_x = -clamp_geom_rect.x() / clamp_geom_rect.width();
+ float vertex_tex_translate_y =
+ -clamp_geom_rect.y() / clamp_geom_rect.height();
+ float vertex_tex_scale_x = tile_rect.width() / clamp_geom_rect.width();
+ float vertex_tex_scale_y = tile_rect.height() / clamp_geom_rect.height();
+
+ // Map to normalized texture coordinates.
+ gfx::Size texture_size = quad->texture_size;
+ float fragment_tex_translate_x = clamp_tex_rect.x() / texture_size.width();
+ float fragment_tex_translate_y = clamp_tex_rect.y() / texture_size.height();
+ float fragment_tex_scale_x = clamp_tex_rect.width() / texture_size.width();
+ float fragment_tex_scale_y = clamp_tex_rect.height() / texture_size.height();
+
+ gfx::Transform device_transform =
+ frame.window_matrix * frame.projection_matrix * quad->quadTransform();
+ device_transform.FlattenTo2d();
+ if (!device_transform.IsInvertible())
+ return;
+
+ gfx::QuadF local_quad = gfx::QuadF(gfx::RectF(tile_rect));
+ float edge[24];
+ bool use_aa = SetupQuadForAntialiasing(
+ device_transform, quad, &local_quad, edge);
+
+ TileProgramUniforms uniforms;
+ if (use_aa) {
+ if (quad->swizzle_contents)
+ TileUniformLocation(GetTileProgramSwizzleAA(), &uniforms);
+ else
+ TileUniformLocation(GetTileProgramAA(), &uniforms);
+ } else {
+ if (quad->ShouldDrawWithBlending()) {
+ if (quad->swizzle_contents)
+ TileUniformLocation(GetTileProgramSwizzle(), &uniforms);
+ else
+ TileUniformLocation(GetTileProgram(), &uniforms);
+ } else {
+ if (quad->swizzle_contents)
+ TileUniformLocation(GetTileProgramSwizzleOpaque(), &uniforms);
+ else
+ TileUniformLocation(GetTileProgramOpaque(), &uniforms);
+ }
+ }
+
+ SetUseProgram(uniforms.program);
+ GLC(Context(), Context()->uniform1i(uniforms.sampler_location, 0));
+ bool scaled = (tex_to_geom_scale_x != 1.f || tex_to_geom_scale_y != 1.f);
+ GLenum filter = (use_aa || scaled ||
+ !quad->quadTransform().IsIdentityOrIntegerTranslation())
+ ? GL_LINEAR
+ : GL_NEAREST;
+ ResourceProvider::ScopedSamplerGL quad_resource_lock(
+ resource_provider_, quad->resource_id, GL_TEXTURE_2D, filter);
+
+ if (use_aa) {
+ GLC(Context(), Context()->uniform3fv(uniforms.edge_location, 8, edge));
+
+ GLC(Context(),
+ Context()->uniform4f(uniforms.vertex_tex_transform_location,
+ vertex_tex_translate_x,
+ vertex_tex_translate_y,
+ vertex_tex_scale_x,
+ vertex_tex_scale_y));
+ GLC(Context(),
+ Context()->uniform4f(uniforms.fragment_tex_transform_location,
+ fragment_tex_translate_x,
+ fragment_tex_translate_y,
+ fragment_tex_scale_x,
+ fragment_tex_scale_y));
+ } else {
+ // Move fragment shader transform to vertex shader. We can do this while
+ // still producing correct results as fragment_tex_transform_location
+ // should always be non-negative when tiles are transformed in a way
+ // that could result in sampling outside the layer.
+ vertex_tex_scale_x *= fragment_tex_scale_x;
+ vertex_tex_scale_y *= fragment_tex_scale_y;
+ vertex_tex_translate_x *= fragment_tex_scale_x;
+ vertex_tex_translate_y *= fragment_tex_scale_y;
+ vertex_tex_translate_x += fragment_tex_translate_x;
+ vertex_tex_translate_y += fragment_tex_translate_y;
+
+ GLC(Context(),
+ Context()->uniform4f(uniforms.vertex_tex_transform_location,
+ vertex_tex_translate_x,
+ vertex_tex_translate_y,
+ vertex_tex_scale_x,
+ vertex_tex_scale_y));
+ }
+
+ // Enable blending when the quad properties require it or if we decided
+ // to use antialiasing.
+ SetBlendEnabled(quad->ShouldDrawWithBlending() || use_aa);
+
+ // Normalize to tile_rect.
+ local_quad.Scale(1.0f / tile_rect.width(), 1.0f / tile_rect.height());
+
+ SetShaderOpacity(quad->opacity(), uniforms.alpha_location);
+ SetShaderQuadF(local_quad, uniforms.point_location);
+
+ // The transform and vertex data are used to figure out the extents that the
+ // un-antialiased quad should have and which vertex this is and the float
+ // quad passed in via uniform is the actual geometry that gets used to draw
+ // it. This is why this centered rect is used and not the original quad_rect.
+ gfx::RectF centeredRect(
+ gfx::PointF(-0.5f * tile_rect.width(), -0.5f * tile_rect.height()),
+ tile_rect.size());
+ DrawQuadGeometry(
+ frame, quad->quadTransform(), centeredRect, uniforms.matrix_location);
+}
+
+void GLRenderer::DrawYUVVideoQuad(const DrawingFrame& frame,
+ const YUVVideoDrawQuad* quad) {
+ SetBlendEnabled(quad->ShouldDrawWithBlending());
+
+ const VideoYUVProgram* program = GetVideoYUVProgram();
+ DCHECK(program && (program->initialized() || IsContextLost()));
+
+ const VideoLayerImpl::FramePlane& y_plane = quad->y_plane;
+ const VideoLayerImpl::FramePlane& u_plane = quad->u_plane;
+ const VideoLayerImpl::FramePlane& v_plane = quad->v_plane;
+
+ GLC(Context(), Context()->activeTexture(GL_TEXTURE1));
+ ResourceProvider::ScopedSamplerGL y_plane_lock(
+ resource_provider_, y_plane.resource_id, GL_TEXTURE_2D, GL_LINEAR);
+ GLC(Context(), Context()->activeTexture(GL_TEXTURE2));
+ ResourceProvider::ScopedSamplerGL u_plane_lock(
+ resource_provider_, u_plane.resource_id, GL_TEXTURE_2D, GL_LINEAR);
+ GLC(Context(), Context()->activeTexture(GL_TEXTURE3));
+ ResourceProvider::ScopedSamplerGL v_plane_lock(
+ resource_provider_, v_plane.resource_id, GL_TEXTURE_2D, GL_LINEAR);
+
+ SetUseProgram(program->program());
+
+ GLC(Context(),
+ Context()->uniform2f(program->vertex_shader().texScaleLocation(),
+ quad->tex_scale.width(),
+ quad->tex_scale.height()));
+ GLC(Context(),
+ Context()->uniform1i(program->fragment_shader().yTextureLocation(), 1));
+ GLC(Context(),
+ Context()->uniform1i(program->fragment_shader().uTextureLocation(), 2));
+ GLC(Context(),
+ Context()->uniform1i(program->fragment_shader().vTextureLocation(), 3));
+
+ // These values are magic numbers that are used in the transformation from YUV
+ // to RGB color values. They are taken from the following webpage:
+ // http://www.fourcc.org/fccyvrgb.php
+ float yuv_to_rgb[9] = {
+ 1.164f, 1.164f, 1.164f,
+ 0.0f, -.391f, 2.018f,
+ 1.596f, -.813f, 0.0f,
+ };
+ GLC(Context(),
+ Context()->uniformMatrix3fv(
+ program->fragment_shader().yuvMatrixLocation(), 1, 0, yuv_to_rgb));
+
+ // These values map to 16, 128, and 128 respectively, and are computed
+ // as a fraction over 256 (e.g. 16 / 256 = 0.0625).
+ // They are used in the YUV to RGBA conversion formula:
+ // Y - 16 : Gives 16 values of head and footroom for overshooting
+ // U - 128 : Turns unsigned U into signed U [-128,127]
+ // V - 128 : Turns unsigned V into signed V [-128,127]
+ float yuv_adjust[3] = { -0.0625f, -0.5f, -0.5f, };
+ GLC(Context(),
+ Context()->uniform3fv(
+ program->fragment_shader().yuvAdjLocation(), 1, yuv_adjust));
+
+ SetShaderOpacity(quad->opacity(), program->fragment_shader().alphaLocation());
+ DrawQuadGeometry(frame,
+ quad->quadTransform(),
+ quad->rect,
+ program->vertex_shader().matrixLocation());
+
+ // Reset active texture back to texture 0.
+ GLC(Context(), Context()->activeTexture(GL_TEXTURE0));
+}
+
+void GLRenderer::DrawStreamVideoQuad(const DrawingFrame& frame,
+ const StreamVideoDrawQuad* quad) {
+ SetBlendEnabled(quad->ShouldDrawWithBlending());
+
+ static float gl_matrix[16];
+
+ DCHECK(capabilities_.using_egl_image);
+
+ const VideoStreamTextureProgram* program = GetVideoStreamTextureProgram();
+ SetUseProgram(program->program());
+
+ ToGLMatrix(&gl_matrix[0], quad->matrix);
+ GLC(Context(),
+ Context()->uniformMatrix4fv(
+ program->vertex_shader().texMatrixLocation(), 1, false, gl_matrix));
+
+ GLC(Context(),
+ Context()->bindTexture(GL_TEXTURE_EXTERNAL_OES, quad->texture_id));
+
+ GLC(Context(),
+ Context()->uniform1i(program->fragment_shader().samplerLocation(), 0));
+
+ SetShaderOpacity(quad->opacity(), program->fragment_shader().alphaLocation());
+ DrawQuadGeometry(frame,
+ quad->quadTransform(),
+ quad->rect,
+ program->vertex_shader().matrixLocation());
+}
+
+struct TextureProgramBinding {
+ template <class Program>
+ void Set(Program* program, WebKit::WebGraphicsContext3D* context) {
+ DCHECK(program && (program->initialized() || context->isContextLost()));
+ program_id = program->program();
+ sampler_location = program->fragment_shader().samplerLocation();
+ matrix_location = program->vertex_shader().matrixLocation();
+ alpha_location = program->fragment_shader().alphaLocation();
+ }
+ int program_id;
+ int sampler_location;
+ int matrix_location;
+ int alpha_location;
+};
+
+struct TexTransformTextureProgramBinding : TextureProgramBinding {
+ template <class Program>
+ void Set(Program* program, WebKit::WebGraphicsContext3D* context) {
+ TextureProgramBinding::Set(program, context);
+ tex_transform_location = program->vertex_shader().texTransformLocation();
+ vertex_opacity_location = program->vertex_shader().vertexOpacityLocation();
+ }
+ int tex_transform_location;
+ int vertex_opacity_location;
+};
+
+void GLRenderer::FlushTextureQuadCache() {
+ // Check to see if we have anything to draw.
+ if (draw_cache_.program_id == 0)
+ return;
+
+ // Set the correct blending mode.
+ SetBlendEnabled(draw_cache_.needs_blending);
+
+ // Bind the program to the GL state.
+ SetUseProgram(draw_cache_.program_id);
+
+ // Bind the correct texture sampler location.
+ GLC(Context(), Context()->uniform1i(draw_cache_.sampler_location, 0));
+
+ // Assume the current active textures is 0.
+ ResourceProvider::ScopedReadLockGL locked_quad(resource_provider_,
+ draw_cache_.resource_id);
+ GLC(Context(),
+ Context()->bindTexture(GL_TEXTURE_2D, locked_quad.texture_id()));
+
+ // set up premultiplied alpha.
+ if (!draw_cache_.use_premultiplied_alpha) {
+ // As it turns out, the premultiplied alpha blending function (ONE,
+ // ONE_MINUS_SRC_ALPHA) will never cause the alpha channel to be set to
+ // anything less than 1.0f if it is initialized to that value! Therefore,
+ // premultipliedAlpha being false is the first situation we can generally
+ // see an alpha channel less than 1.0f coming out of the compositor. This is
+ // causing platform differences in some layout tests (see
+ // https://bugs.webkit.org/show_bug.cgi?id=82412), so in this situation, use
+ // a separate blend function for the alpha channel to avoid modifying it.
+ // Don't use colorMask for this as it has performance implications on some
+ // platforms.
+ GLC(Context(),
+ Context()->blendFuncSeparate(
+ GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE));
+ }
+
+ COMPILE_ASSERT(sizeof(Float4) == 4 * sizeof(float), struct_is_densely_packed);
+ COMPILE_ASSERT(sizeof(Float16) == 16 * sizeof(float),
+ struct_is_densely_packed);
+
+ // Upload the tranforms for both points and uvs.
+ GLC(context_,
+ context_->uniformMatrix4fv(
+ static_cast<int>(draw_cache_.matrix_location),
+ static_cast<int>(draw_cache_.matrix_data.size()),
+ false,
+ reinterpret_cast<float*>(&draw_cache_.matrix_data.front())));
+ GLC(context_,
+ context_->uniform4fv(
+ static_cast<int>(draw_cache_.uv_xform_location),
+ static_cast<int>(draw_cache_.uv_xform_data.size()),
+ reinterpret_cast<float*>(&draw_cache_.uv_xform_data.front())));
+ GLC(context_,
+ context_->uniform1fv(
+ static_cast<int>(draw_cache_.vertex_opacity_location),
+ static_cast<int>(draw_cache_.vertex_opacity_data.size()),
+ static_cast<float*>(&draw_cache_.vertex_opacity_data.front())));
+
+ // Draw the quads!
+ GLC(context_,
+ context_->drawElements(GL_TRIANGLES,
+ 6 * draw_cache_.matrix_data.size(),
+ GL_UNSIGNED_SHORT,
+ 0));
+
+ // Clean up after ourselves (reset state set above).
+ if (!draw_cache_.use_premultiplied_alpha)
+ GLC(context_, context_->blendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
+
+ // Clear the cache.
+ draw_cache_.program_id = 0;
+ draw_cache_.uv_xform_data.resize(0);
+ draw_cache_.vertex_opacity_data.resize(0);
+ draw_cache_.matrix_data.resize(0);
+}
+
+void GLRenderer::EnqueueTextureQuad(const DrawingFrame& frame,
+ const TextureDrawQuad* quad) {
+ // Choose the correct texture program binding
+ TexTransformTextureProgramBinding binding;
+ if (quad->flipped)
+ binding.Set(GetTextureProgramFlip(), Context());
+ else
+ binding.Set(GetTextureProgram(), Context());
+
+ int resource_id = quad->resource_id;
+
+ if (draw_cache_.program_id != binding.program_id ||
+ draw_cache_.resource_id != resource_id ||
+ draw_cache_.use_premultiplied_alpha != quad->premultiplied_alpha ||
+ draw_cache_.needs_blending != quad->ShouldDrawWithBlending() ||
+ draw_cache_.matrix_data.size() >= 8) {
+ FlushTextureQuadCache();
+ draw_cache_.program_id = binding.program_id;
+ draw_cache_.resource_id = resource_id;
+ draw_cache_.use_premultiplied_alpha = quad->premultiplied_alpha;
+ draw_cache_.needs_blending = quad->ShouldDrawWithBlending();
+
+ draw_cache_.uv_xform_location = binding.tex_transform_location;
+ draw_cache_.vertex_opacity_location = binding.vertex_opacity_location;
+ draw_cache_.matrix_location = binding.matrix_location;
+ draw_cache_.sampler_location = binding.sampler_location;
+ }
+
+ // Generate the uv-transform
+ gfx::PointF uv0 = quad->uv_top_left;
+ gfx::PointF uv1 = quad->uv_bottom_right;
+ Float4 uv = { { uv0.x(), uv0.y(), uv1.x() - uv0.x(), uv1.y() - uv0.y() } };
+ draw_cache_.uv_xform_data.push_back(uv);
+
+ // Generate the vertex opacity
+ const float opacity = quad->opacity();
+ draw_cache_.vertex_opacity_data.push_back(quad->vertex_opacity[0] * opacity);
+ draw_cache_.vertex_opacity_data.push_back(quad->vertex_opacity[1] * opacity);
+ draw_cache_.vertex_opacity_data.push_back(quad->vertex_opacity[2] * opacity);
+ draw_cache_.vertex_opacity_data.push_back(quad->vertex_opacity[3] * opacity);
+
+ // Generate the transform matrix
+ gfx::Transform quad_rect_matrix;
+ QuadRectTransform(&quad_rect_matrix, quad->quadTransform(), quad->rect);
+ quad_rect_matrix = frame.projection_matrix * quad_rect_matrix;
+
+ Float16 m;
+ quad_rect_matrix.matrix().asColMajorf(m.data);
+ draw_cache_.matrix_data.push_back(m);
+}
+
+void GLRenderer::DrawTextureQuad(const DrawingFrame& frame,
+ const TextureDrawQuad* quad) {
+ TexTransformTextureProgramBinding binding;
+ if (quad->flipped)
+ binding.Set(GetTextureProgramFlip(), Context());
+ else
+ binding.Set(GetTextureProgram(), Context());
+ SetUseProgram(binding.program_id);
+ GLC(Context(), Context()->uniform1i(binding.sampler_location, 0));
+ gfx::PointF uv0 = quad->uv_top_left;
+ gfx::PointF uv1 = quad->uv_bottom_right;
+ GLC(Context(),
+ Context()->uniform4f(binding.tex_transform_location,
+ uv0.x(),
+ uv0.y(),
+ uv1.x() - uv0.x(),
+ uv1.y() - uv0.y()));
+
+ GLC(Context(),
+ Context()->uniform1fv(
+ binding.vertex_opacity_location, 4, quad->vertex_opacity));
+
+ ResourceProvider::ScopedSamplerGL quad_resource_lock(
+ resource_provider_, quad->resource_id, GL_TEXTURE_2D, GL_LINEAR);
+
+ if (!quad->premultiplied_alpha) {
+ // As it turns out, the premultiplied alpha blending function (ONE,
+ // ONE_MINUS_SRC_ALPHA) will never cause the alpha channel to be set to
+ // anything less than 1.0f if it is initialized to that value! Therefore,
+ // premultipliedAlpha being false is the first situation we can generally
+ // see an alpha channel less than 1.0f coming out of the compositor. This is
+ // causing platform differences in some layout tests (see
+ // https://bugs.webkit.org/show_bug.cgi?id=82412), so in this situation, use
+ // a separate blend function for the alpha channel to avoid modifying it.
+ // Don't use colorMask for this as it has performance implications on some
+ // platforms.
+ GLC(Context(),
+ Context()->blendFuncSeparate(
+ GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE));
+ }
+
+ DrawQuadGeometry(
+ frame, quad->quadTransform(), quad->rect, binding.matrix_location);
+
+ if (!quad->premultiplied_alpha)
+ GLC(context_, context_->blendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
+}
+
+void GLRenderer::DrawIOSurfaceQuad(const DrawingFrame& frame,
+ const IOSurfaceDrawQuad* quad) {
+ SetBlendEnabled(quad->ShouldDrawWithBlending());
+
+ TexTransformTextureProgramBinding binding;
+ binding.Set(GetTextureIOSurfaceProgram(), Context());
+
+ SetUseProgram(binding.program_id);
+ GLC(Context(), Context()->uniform1i(binding.sampler_location, 0));
+ if (quad->orientation == IOSurfaceDrawQuad::FLIPPED) {
+ GLC(Context(),
+ Context()->uniform4f(binding.tex_transform_location,
+ 0,
+ quad->io_surface_size.height(),
+ quad->io_surface_size.width(),
+ quad->io_surface_size.height() * -1.0f));
+ } else {
+ GLC(Context(),
+ Context()->uniform4f(binding.tex_transform_location,
+ 0,
+ 0,
+ quad->io_surface_size.width(),
+ quad->io_surface_size.height()));
+ }
+
+ const float vertex_opacity[] = { quad->opacity(), quad->opacity(),
+ quad->opacity(), quad->opacity() };
+ GLC(Context(),
+ Context()->uniform1fv(
+ binding.vertex_opacity_location, 4, vertex_opacity));
+
+ GLC(Context(),
+ Context()->bindTexture(GL_TEXTURE_RECTANGLE_ARB,
+ quad->io_surface_texture_id));
+
+ DrawQuadGeometry(
+ frame, quad->quadTransform(), quad->rect, binding.matrix_location);
+
+ GLC(Context(), Context()->bindTexture(GL_TEXTURE_RECTANGLE_ARB, 0));
+}
+
+void GLRenderer::FinishDrawingFrame(DrawingFrame& frame) {
+ current_framebuffer_lock_.reset();
+ swap_buffer_rect_.Union(gfx::ToEnclosingRect(frame.root_damage_rect));
+
+ GLC(context_, context_->disable(GL_BLEND));
+ blend_shadow_ = false;
+
+ if (Settings().compositorFrameMessage) {
+ CompositorFrame compositor_frame;
+ compositor_frame.metadata = client_->MakeCompositorFrameMetadata();
+ output_surface_->SendFrameToParentCompositor(&compositor_frame);
+ }
+}
+
+void GLRenderer::FinishDrawingQuadList() { FlushTextureQuadCache(); }
+
+bool GLRenderer::FlippedFramebuffer() const { return true; }
+
+void GLRenderer::EnsureScissorTestEnabled() {
+ if (is_scissor_enabled_)
+ return;
+
+ FlushTextureQuadCache();
+ GLC(context_, context_->enable(GL_SCISSOR_TEST));
+ is_scissor_enabled_ = true;
+}
+
+void GLRenderer::EnsureScissorTestDisabled() {
+ if (!is_scissor_enabled_)
+ return;
+
+ FlushTextureQuadCache();
+ GLC(context_, context_->disable(GL_SCISSOR_TEST));
+ is_scissor_enabled_ = false;
+}
+
+void GLRenderer::ToGLMatrix(float* gl_matrix, const gfx::Transform& transform) {
+ transform.matrix().asColMajorf(gl_matrix);
+}
+
+void GLRenderer::SetShaderQuadF(const gfx::QuadF& quad, int quad_location) {
+ if (quad_location == -1)
+ return;
+
+ float point[8];
+ point[0] = quad.p1().x();
+ point[1] = quad.p1().y();
+ point[2] = quad.p2().x();
+ point[3] = quad.p2().y();
+ point[4] = quad.p3().x();
+ point[5] = quad.p3().y();
+ point[6] = quad.p4().x();
+ point[7] = quad.p4().y();
+ GLC(context_, context_->uniform2fv(quad_location, 4, point));
+}
+
+void GLRenderer::SetShaderOpacity(float opacity, int alpha_location) {
+ if (alpha_location != -1)
+ GLC(context_, context_->uniform1f(alpha_location, opacity));
+}
+
+void GLRenderer::SetBlendEnabled(bool enabled) {
+ if (enabled == blend_shadow_)
+ return;
+
+ if (enabled)
+ GLC(context_, context_->enable(GL_BLEND));
+ else
+ GLC(context_, context_->disable(GL_BLEND));
+ blend_shadow_ = enabled;
+}
+
+void GLRenderer::SetUseProgram(unsigned program) {
+ if (program == program_shadow_)
+ return;
+ GLC(context_, context_->useProgram(program));
+ program_shadow_ = program;
+}
+
+void GLRenderer::DrawQuadGeometry(const DrawingFrame& frame,
+ const gfx::Transform& draw_transform,
+ const gfx::RectF& quad_rect,
+ int matrix_location) {
+ gfx::Transform quad_rect_matrix;
+ QuadRectTransform(&quad_rect_matrix, draw_transform, quad_rect);
+ static float gl_matrix[16];
+ ToGLMatrix(&gl_matrix[0], frame.projection_matrix * quad_rect_matrix);
+ GLC(context_,
+ context_->uniformMatrix4fv(matrix_location, 1, false, &gl_matrix[0]));
+
+ GLC(context_, context_->drawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0));
+}
+
+void GLRenderer::CopyTextureToFramebuffer(const DrawingFrame& frame,
+ int texture_id,
+ gfx::Rect rect,
+ const gfx::Transform& draw_matrix) {
+ const RenderPassProgram* program = GetRenderPassProgram();
+
+ GLC(Context(), Context()->bindTexture(GL_TEXTURE_2D, texture_id));
+
+ SetUseProgram(program->program());
+ GLC(Context(),
+ Context()->uniform1i(program->fragment_shader().samplerLocation(), 0));
+ GLC(Context(),
+ Context()->uniform4f(program->vertex_shader().texTransformLocation(),
+ 0.0f,
+ 0.0f,
+ 1.0f,
+ 1.0f));
+ SetShaderOpacity(1, program->fragment_shader().alphaLocation());
+ DrawQuadGeometry(
+ frame, draw_matrix, rect, program->vertex_shader().matrixLocation());
+}
+
+void GLRenderer::Finish() {
+ TRACE_EVENT0("cc", "GLRenderer::finish");
+ context_->finish();
+}
+
+bool GLRenderer::SwapBuffers() {
+ DCHECK(visible_);
+ DCHECK(!is_backbuffer_discarded_);
+
+ TRACE_EVENT0("cc", "GLRenderer::swapBuffers");
+ // We're done! Time to swapbuffers!
+
+ if (capabilities_.using_partial_swap) {
+ // If supported, we can save significant bandwidth by only swapping the
+ // damaged/scissored region (clamped to the viewport)
+ swap_buffer_rect_.Intersect(gfx::Rect(gfx::Point(), ViewportSize()));
+ int flipped_y_pos_of_rect_bottom =
+ ViewportHeight() - swap_buffer_rect_.y() - swap_buffer_rect_.height();
+ output_surface_->PostSubBuffer(gfx::Rect(swap_buffer_rect_.x(),
+ flipped_y_pos_of_rect_bottom,
+ swap_buffer_rect_.width(),
+ swap_buffer_rect_.height()));
+ } else {
+ output_surface_->SwapBuffers();
+ }
+
+ swap_buffer_rect_ = gfx::Rect();
+
+ // We don't have real fences, so we mark read fences as passed
+ // assuming a double-buffered GPU pipeline. A texture can be
+ // written to after one full frame has past since it was last read.
+ if (last_swap_fence_)
+ static_cast<SimpleSwapFence*>(last_swap_fence_.get())->SetHasPassed();
+ last_swap_fence_ = resource_provider_->GetReadLockFence();
+ resource_provider_->SetReadLockFence(new SimpleSwapFence());
+
+ return true;
+}
+
+void GLRenderer::ReceiveCompositorFrameAck(const CompositorFrameAck& ack) {
+ onSwapBuffersComplete();
+}
+
+void GLRenderer::onSwapBuffersComplete() { client_->OnSwapBuffersComplete(); }
+
+void GLRenderer::onMemoryAllocationChanged(
+ WebGraphicsMemoryAllocation allocation) {
+ // Just ignore the memory manager when it says to set the limit to zero
+ // bytes. This will happen when the memory manager thinks that the renderer
+ // is not visible (which the renderer knows better).
+ if (allocation.bytesLimitWhenVisible) {
+ ManagedMemoryPolicy policy(
+ allocation.bytesLimitWhenVisible,
+ PriorityCutoff(allocation.priorityCutoffWhenVisible),
+ allocation.bytesLimitWhenNotVisible,
+ PriorityCutoff(allocation.priorityCutoffWhenNotVisible));
+
+ if (allocation.enforceButDoNotKeepAsPolicy)
+ client_->EnforceManagedMemoryPolicy(policy);
+ else
+ client_->SetManagedMemoryPolicy(policy);
+ }
+
+ bool old_discard_backbuffer_when_not_visible =
+ discard_backbuffer_when_not_visible_;
+ discard_backbuffer_when_not_visible_ = !allocation.suggestHaveBackbuffer;
+ EnforceMemoryPolicy();
+ if (allocation.enforceButDoNotKeepAsPolicy)
+ discard_backbuffer_when_not_visible_ =
+ old_discard_backbuffer_when_not_visible;
+}
+
+ManagedMemoryPolicy::PriorityCutoff GLRenderer::PriorityCutoff(
+ WebKit::WebGraphicsMemoryAllocation::PriorityCutoff priority_cutoff) {
+ // This is simple a 1:1 map, the names differ only because the WebKit names
+ // should be to match the cc names.
+ switch (priority_cutoff) {
+ case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowNothing:
+ return ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING;
+ case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowVisibleOnly:
+ return ManagedMemoryPolicy::CUTOFF_ALLOW_REQUIRED_ONLY;
+ case WebKit::WebGraphicsMemoryAllocation::
+ PriorityCutoffAllowVisibleAndNearby:
+ return ManagedMemoryPolicy::CUTOFF_ALLOW_NICE_TO_HAVE;
+ case WebKit::WebGraphicsMemoryAllocation::PriorityCutoffAllowEverything:
+ return ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING;
+ }
+ NOTREACHED();
+ return ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING;
+}
+
+void GLRenderer::EnforceMemoryPolicy() {
+ if (!visible_) {
+ TRACE_EVENT0("cc", "GLRenderer::enforceMemoryPolicy dropping resources");
+ ReleaseRenderPassTextures();
+ if (discard_backbuffer_when_not_visible_)
+ DiscardBackbuffer();
+ resource_provider_->ReleaseCachedData();
+ GLC(context_, context_->flush());
+ }
+}
+
+void GLRenderer::DiscardBackbuffer() {
+ if (is_backbuffer_discarded_)
+ return;
+
+ output_surface_->DiscardBackbuffer();
+
+ is_backbuffer_discarded_ = true;
+
+ // Damage tracker needs a full reset every time framebuffer is discarded.
+ client_->SetFullRootLayerDamage();
+}
+
+void GLRenderer::EnsureBackbuffer() {
+ if (!is_backbuffer_discarded_)
+ return;
+
+ output_surface_->EnsureBackbuffer();
+ is_backbuffer_discarded_ = false;
+}
+
+void GLRenderer::onContextLost() { client_->DidLoseOutputSurface(); }
+
+void GLRenderer::GetFramebufferPixels(void* pixels, gfx::Rect rect) {
+ DCHECK(rect.right() <= ViewportWidth());
+ DCHECK(rect.bottom() <= ViewportHeight());
+
+ if (!pixels)
+ return;
+
+ MakeContextCurrent();
+
+ bool do_workaround = NeedsIOSurfaceReadbackWorkaround();
+
+ GLuint temporary_texture = 0;
+ GLuint temporary_fbo = 0;
+
+ if (do_workaround) {
+ // On Mac OS X, calling glReadPixels against an FBO whose color attachment
+ // is an IOSurface-backed texture causes corruption of future glReadPixels
+ // calls, even those on different OpenGL contexts. It is believed that this
+ // is the root cause of top crasher
+ // http://crbug.com/99393. <rdar://problem/10949687>
+
+ temporary_texture = context_->createTexture();
+ GLC(context_, context_->bindTexture(GL_TEXTURE_2D, temporary_texture));
+ GLC(context_,
+ context_->texParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+ GLC(context_,
+ context_->texParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+ GLC(context_,
+ context_->texParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
+ GLC(context_,
+ context_->texParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
+ // Copy the contents of the current (IOSurface-backed) framebuffer into a
+ // temporary texture.
+ GLC(context_,
+ context_->copyTexImage2D(GL_TEXTURE_2D,
+ 0,
+ GL_RGBA,
+ 0,
+ 0,
+ ViewportSize().width(),
+ ViewportSize().height(),
+ 0));
+ temporary_fbo = context_->createFramebuffer();
+ // Attach this texture to an FBO, and perform the readback from that FBO.
+ GLC(context_, context_->bindFramebuffer(GL_FRAMEBUFFER, temporary_fbo));
+ GLC(context_,
+ context_->framebufferTexture2D(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D,
+ temporary_texture,
+ 0));
+
+ DCHECK(context_->checkFramebufferStatus(GL_FRAMEBUFFER) ==
+ GL_FRAMEBUFFER_COMPLETE);
+ }
+
+ scoped_array<uint8_t> src_pixels(
+ new uint8_t[rect.width() * rect.height() * 4]);
+ GLC(context_,
+ context_->readPixels(rect.x(),
+ ViewportSize().height() - rect.bottom(),
+ rect.width(),
+ rect.height(),
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ src_pixels.get()));
+
+ uint8_t* dest_pixels = static_cast<uint8_t*>(pixels);
+ size_t row_bytes = rect.width() * 4;
+ int num_rows = rect.height();
+ size_t total_bytes = num_rows * row_bytes;
+ for (size_t dest_y = 0; dest_y < total_bytes; dest_y += row_bytes) {
+ // Flip Y axis.
+ size_t src_y = total_bytes - dest_y - row_bytes;
+ // Swizzle BGRA -> RGBA.
+ for (size_t x = 0; x < row_bytes; x += 4) {
+ dest_pixels[dest_y + (x + 0)] = src_pixels.get()[src_y + (x + 2)];
+ dest_pixels[dest_y + (x + 1)] = src_pixels.get()[src_y + (x + 1)];
+ dest_pixels[dest_y + (x + 2)] = src_pixels.get()[src_y + (x + 0)];
+ dest_pixels[dest_y + (x + 3)] = src_pixels.get()[src_y + (x + 3)];
+ }
+ }
+
+ if (do_workaround) {
+ // Clean up.
+ GLC(context_, context_->bindFramebuffer(GL_FRAMEBUFFER, 0));
+ GLC(context_, context_->bindTexture(GL_TEXTURE_2D, 0));
+ GLC(context_, context_->deleteFramebuffer(temporary_fbo));
+ GLC(context_, context_->deleteTexture(temporary_texture));
+ }
+
+ EnforceMemoryPolicy();
+}
+
+bool GLRenderer::GetFramebufferTexture(ScopedResource* texture,
+ gfx::Rect device_rect) {
+ DCHECK(!texture->id() || (texture->size() == device_rect.size() &&
+ texture->format() == GL_RGB));
+
+ if (!texture->id() && !texture->Allocate(device_rect.size(),
+ GL_RGB,
+ ResourceProvider::TextureUsageAny))
+ return false;
+
+ ResourceProvider::ScopedWriteLockGL lock(resource_provider_, texture->id());
+ GLC(context_, context_->bindTexture(GL_TEXTURE_2D, lock.texture_id()));
+ GLC(context_,
+ context_->copyTexImage2D(GL_TEXTURE_2D,
+ 0,
+ texture->format(),
+ device_rect.x(),
+ device_rect.y(),
+ device_rect.width(),
+ device_rect.height(),
+ 0));
+ return true;
+}
+
+bool GLRenderer::UseScopedTexture(DrawingFrame& frame,
+ const ScopedResource* texture,
+ gfx::Rect viewport_rect) {
+ DCHECK(texture->id());
+ frame.current_render_pass = 0;
+ frame.current_texture = texture;
+
+ return BindFramebufferToTexture(frame, texture, viewport_rect);
+}
+
+void GLRenderer::BindFramebufferToOutputSurface(DrawingFrame& frame) {
+ current_framebuffer_lock_.reset();
+ output_surface_->BindFramebuffer();
+}
+
+bool GLRenderer::BindFramebufferToTexture(DrawingFrame& frame,
+ const ScopedResource* texture,
+ gfx::Rect framebuffer_rect) {
+ DCHECK(texture->id());
+
+ GLC(context_,
+ context_->bindFramebuffer(GL_FRAMEBUFFER, offscreen_framebuffer_id_));
+ current_framebuffer_lock_ =
+ make_scoped_ptr(new ResourceProvider::ScopedWriteLockGL(
+ resource_provider_, texture->id()));
+ unsigned texture_id = current_framebuffer_lock_->texture_id();
+ GLC(context_,
+ context_->framebufferTexture2D(
+ GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_id, 0));
+
+ DCHECK(context_->checkFramebufferStatus(GL_FRAMEBUFFER) ==
+ GL_FRAMEBUFFER_COMPLETE || IsContextLost());
+
+ InitializeMatrices(frame, framebuffer_rect, false);
+ SetDrawViewportSize(framebuffer_rect.size());
+
+ return true;
+}
+
+void GLRenderer::SetScissorTestRect(gfx::Rect scissor_rect) {
+ EnsureScissorTestEnabled();
+
+ // Don't unnecessarily ask the context to change the scissor, because it
+ // may cause undesired GPU pipeline flushes.
+ if (scissor_rect == scissor_rect_)
+ return;
+
+ scissor_rect_ = scissor_rect;
+ FlushTextureQuadCache();
+ GLC(context_,
+ context_->scissor(scissor_rect.x(),
+ scissor_rect.y(),
+ scissor_rect.width(),
+ scissor_rect.height()));
+}
+
+void GLRenderer::SetDrawViewportSize(gfx::Size viewport_size) {
+ GLC(context_,
+ context_->viewport(0, 0, viewport_size.width(), viewport_size.height()));
+}
+
+bool GLRenderer::MakeContextCurrent() { return context_->makeContextCurrent(); }
+
+bool GLRenderer::InitializeSharedObjects() {
+ TRACE_EVENT0("cc", "GLRenderer::initializeSharedObjects");
+ MakeContextCurrent();
+
+ // Create an FBO for doing offscreen rendering.
+ GLC(context_, offscreen_framebuffer_id_ = context_->createFramebuffer());
+
+ // We will always need these programs to render, so create the programs
+ // eagerly so that the shader compilation can start while we do other work.
+ // Other programs are created lazily on first access.
+ shared_geometry_ =
+ make_scoped_ptr(new GeometryBinding(context_, QuadVertexRect()));
+ render_pass_program_ = make_scoped_ptr(new RenderPassProgram(context_));
+ tile_program_ = make_scoped_ptr(new TileProgram(context_));
+ tile_program_opaque_ = make_scoped_ptr(new TileProgramOpaque(context_));
+
+ GLC(context_, context_->flush());
+
+ return true;
+}
+
+const GLRenderer::TileCheckerboardProgram*
+GLRenderer::GetTileCheckerboardProgram() {
+ if (!tile_checkerboard_program_) {
+ tile_checkerboard_program_ =
+ make_scoped_ptr(new TileCheckerboardProgram(context_));
+ }
+ if (!tile_checkerboard_program_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::checkerboardProgram::initalize");
+ tile_checkerboard_program_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return tile_checkerboard_program_.get();
+}
+
+const GLRenderer::DebugBorderProgram* GLRenderer::GetDebugBorderProgram() {
+ if (!debug_border_program_)
+ debug_border_program_ = make_scoped_ptr(new DebugBorderProgram(context_));
+ if (!debug_border_program_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::debugBorderProgram::initialize");
+ debug_border_program_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return debug_border_program_.get();
+}
+
+const GLRenderer::SolidColorProgram* GLRenderer::GetSolidColorProgram() {
+ if (!solid_color_program_)
+ solid_color_program_ = make_scoped_ptr(new SolidColorProgram(context_));
+ if (!solid_color_program_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::solidColorProgram::initialize");
+ solid_color_program_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return solid_color_program_.get();
+}
+
+const GLRenderer::SolidColorProgramAA* GLRenderer::GetSolidColorProgramAA() {
+ if (!solid_color_program_aa_) {
+ solid_color_program_aa_ =
+ make_scoped_ptr(new SolidColorProgramAA(context_));
+ }
+ if (!solid_color_program_aa_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::solidColorProgramAA::initialize");
+ solid_color_program_aa_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return solid_color_program_aa_.get();
+}
+
+const GLRenderer::RenderPassProgram* GLRenderer::GetRenderPassProgram() {
+ DCHECK(render_pass_program_);
+ if (!render_pass_program_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::renderPassProgram::initialize");
+ render_pass_program_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return render_pass_program_.get();
+}
+
+const GLRenderer::RenderPassProgramAA* GLRenderer::GetRenderPassProgramAA() {
+ if (!render_pass_program_aa_)
+ render_pass_program_aa_ =
+ make_scoped_ptr(new RenderPassProgramAA(context_));
+ if (!render_pass_program_aa_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::renderPassProgramAA::initialize");
+ render_pass_program_aa_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return render_pass_program_aa_.get();
+}
+
+const GLRenderer::RenderPassMaskProgram*
+GLRenderer::GetRenderPassMaskProgram() {
+ if (!render_pass_mask_program_)
+ render_pass_mask_program_ =
+ make_scoped_ptr(new RenderPassMaskProgram(context_));
+ if (!render_pass_mask_program_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::renderPassMaskProgram::initialize");
+ render_pass_mask_program_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return render_pass_mask_program_.get();
+}
+
+const GLRenderer::RenderPassMaskProgramAA*
+GLRenderer::GetRenderPassMaskProgramAA() {
+ if (!render_pass_mask_program_aa_)
+ render_pass_mask_program_aa_ =
+ make_scoped_ptr(new RenderPassMaskProgramAA(context_));
+ if (!render_pass_mask_program_aa_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::renderPassMaskProgramAA::initialize");
+ render_pass_mask_program_aa_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return render_pass_mask_program_aa_.get();
+}
+
+const GLRenderer::TileProgram* GLRenderer::GetTileProgram() {
+ DCHECK(tile_program_);
+ if (!tile_program_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::tileProgram::initialize");
+ tile_program_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return tile_program_.get();
+}
+
+const GLRenderer::TileProgramOpaque* GLRenderer::GetTileProgramOpaque() {
+ DCHECK(tile_program_opaque_);
+ if (!tile_program_opaque_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::tileProgramOpaque::initialize");
+ tile_program_opaque_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return tile_program_opaque_.get();
+}
+
+const GLRenderer::TileProgramAA* GLRenderer::GetTileProgramAA() {
+ if (!tile_program_aa_)
+ tile_program_aa_ = make_scoped_ptr(new TileProgramAA(context_));
+ if (!tile_program_aa_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::tileProgramAA::initialize");
+ tile_program_aa_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return tile_program_aa_.get();
+}
+
+const GLRenderer::TileProgramSwizzle* GLRenderer::GetTileProgramSwizzle() {
+ if (!tile_program_swizzle_)
+ tile_program_swizzle_ = make_scoped_ptr(new TileProgramSwizzle(context_));
+ if (!tile_program_swizzle_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::tileProgramSwizzle::initialize");
+ tile_program_swizzle_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return tile_program_swizzle_.get();
+}
+
+const GLRenderer::TileProgramSwizzleOpaque*
+GLRenderer::GetTileProgramSwizzleOpaque() {
+ if (!tile_program_swizzle_opaque_)
+ tile_program_swizzle_opaque_ =
+ make_scoped_ptr(new TileProgramSwizzleOpaque(context_));
+ if (!tile_program_swizzle_opaque_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::tileProgramSwizzleOpaque::initialize");
+ tile_program_swizzle_opaque_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return tile_program_swizzle_opaque_.get();
+}
+
+const GLRenderer::TileProgramSwizzleAA* GLRenderer::GetTileProgramSwizzleAA() {
+ if (!tile_program_swizzle_aa_)
+ tile_program_swizzle_aa_ =
+ make_scoped_ptr(new TileProgramSwizzleAA(context_));
+ if (!tile_program_swizzle_aa_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::tileProgramSwizzleAA::initialize");
+ tile_program_swizzle_aa_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return tile_program_swizzle_aa_.get();
+}
+
+const GLRenderer::TextureProgram* GLRenderer::GetTextureProgram() {
+ if (!texture_program_)
+ texture_program_ = make_scoped_ptr(new TextureProgram(context_));
+ if (!texture_program_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::textureProgram::initialize");
+ texture_program_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return texture_program_.get();
+}
+
+const GLRenderer::TextureProgramFlip* GLRenderer::GetTextureProgramFlip() {
+ if (!texture_program_flip_)
+ texture_program_flip_ = make_scoped_ptr(new TextureProgramFlip(context_));
+ if (!texture_program_flip_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::textureProgramFlip::initialize");
+ texture_program_flip_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return texture_program_flip_.get();
+}
+
+const GLRenderer::TextureIOSurfaceProgram*
+GLRenderer::GetTextureIOSurfaceProgram() {
+ if (!texture_io_surface_program_)
+ texture_io_surface_program_ =
+ make_scoped_ptr(new TextureIOSurfaceProgram(context_));
+ if (!texture_io_surface_program_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::textureIOSurfaceProgram::initialize");
+ texture_io_surface_program_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return texture_io_surface_program_.get();
+}
+
+const GLRenderer::VideoYUVProgram* GLRenderer::GetVideoYUVProgram() {
+ if (!video_yuv_program_)
+ video_yuv_program_ = make_scoped_ptr(new VideoYUVProgram(context_));
+ if (!video_yuv_program_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::videoYUVProgram::initialize");
+ video_yuv_program_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return video_yuv_program_.get();
+}
+
+const GLRenderer::VideoStreamTextureProgram*
+GLRenderer::GetVideoStreamTextureProgram() {
+ if (!Capabilities().using_egl_image)
+ return NULL;
+ if (!video_stream_texture_program_)
+ video_stream_texture_program_ =
+ make_scoped_ptr(new VideoStreamTextureProgram(context_));
+ if (!video_stream_texture_program_->initialized()) {
+ TRACE_EVENT0("cc", "GLRenderer::streamTextureProgram::initialize");
+ video_stream_texture_program_->Initialize(context_, is_using_bind_uniform_);
+ }
+ return video_stream_texture_program_.get();
+}
+
+void GLRenderer::CleanupSharedObjects() {
+ MakeContextCurrent();
+
+ shared_geometry_.reset();
+
+ if (tile_program_)
+ tile_program_->Cleanup(context_);
+ if (tile_program_opaque_)
+ tile_program_opaque_->Cleanup(context_);
+ if (tile_program_swizzle_)
+ tile_program_swizzle_->Cleanup(context_);
+ if (tile_program_swizzle_opaque_)
+ tile_program_swizzle_opaque_->Cleanup(context_);
+ if (tile_program_aa_)
+ tile_program_aa_->Cleanup(context_);
+ if (tile_program_swizzle_aa_)
+ tile_program_swizzle_aa_->Cleanup(context_);
+ if (tile_checkerboard_program_)
+ tile_checkerboard_program_->Cleanup(context_);
+
+ if (render_pass_mask_program_)
+ render_pass_mask_program_->Cleanup(context_);
+ if (render_pass_program_)
+ render_pass_program_->Cleanup(context_);
+ if (render_pass_mask_program_aa_)
+ render_pass_mask_program_aa_->Cleanup(context_);
+ if (render_pass_program_aa_)
+ render_pass_program_aa_->Cleanup(context_);
+
+ if (texture_program_)
+ texture_program_->Cleanup(context_);
+ if (texture_program_flip_)
+ texture_program_flip_->Cleanup(context_);
+ if (texture_io_surface_program_)
+ texture_io_surface_program_->Cleanup(context_);
+
+ if (video_yuv_program_)
+ video_yuv_program_->Cleanup(context_);
+ if (video_stream_texture_program_)
+ video_stream_texture_program_->Cleanup(context_);
+
+ if (debug_border_program_)
+ debug_border_program_->Cleanup(context_);
+ if (solid_color_program_)
+ solid_color_program_->Cleanup(context_);
+ if (solid_color_program_aa_)
+ solid_color_program_aa_->Cleanup(context_);
+
+ if (offscreen_framebuffer_id_)
+ GLC(context_, context_->deleteFramebuffer(offscreen_framebuffer_id_));
+
+ ReleaseRenderPassTextures();
+}
+
+bool GLRenderer::IsContextLost() {
+ return (context_->getGraphicsResetStatusARB() != GL_NO_ERROR);
+}
+
+} // namespace cc
diff --git a/cc/output/gl_renderer.h b/cc/output/gl_renderer.h
new file mode 100644
index 0000000..faa7176
--- /dev/null
+++ b/cc/output/gl_renderer.h
@@ -0,0 +1,340 @@
+// Copyright 2010 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_OUTPUT_GL_RENDERER_H_
+#define CC_OUTPUT_GL_RENDERER_H_
+
+#include "cc/base/cc_export.h"
+#include "cc/checkerboard_draw_quad.h"
+#include "cc/debug_border_draw_quad.h"
+#include "cc/io_surface_draw_quad.h"
+#include "cc/output/direct_renderer.h"
+#include "cc/output/gl_renderer_draw_cache.h"
+#include "cc/output/renderer.h"
+#include "cc/render_pass_draw_quad.h"
+#include "cc/solid_color_draw_quad.h"
+#include "cc/tile_draw_quad.h"
+#include "cc/yuv_video_draw_quad.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsMemoryAllocation.h"
+#include "ui/gfx/quad_f.h"
+
+namespace cc {
+
+class GLRendererShaderTest;
+class OutputSurface;
+class ScopedResource;
+class StreamVideoDrawQuad;
+class TextureDrawQuad;
+class GeometryBinding;
+class ScopedEnsureFramebufferAllocation;
+
+// Class that handles drawing of composited render layers using GL.
+class CC_EXPORT GLRenderer :
+ public DirectRenderer,
+ public NON_EXPORTED_BASE(
+ WebKit::WebGraphicsContext3D::
+ WebGraphicsSwapBuffersCompleteCallbackCHROMIUM),
+ public NON_EXPORTED_BASE(
+ WebKit::WebGraphicsContext3D::
+ WebGraphicsMemoryAllocationChangedCallbackCHROMIUM),
+ public NON_EXPORTED_BASE(
+ WebKit::WebGraphicsContext3D::WebGraphicsContextLostCallback) {
+ public:
+ static scoped_ptr<GLRenderer> Create(RendererClient* client,
+ OutputSurface* output_surface,
+ ResourceProvider* resource_provider);
+
+ virtual ~GLRenderer();
+
+ virtual const RendererCapabilities& Capabilities() const OVERRIDE;
+
+ WebKit::WebGraphicsContext3D* Context();
+
+ virtual void ViewportChanged() OVERRIDE;
+
+ virtual void ReceiveCompositorFrameAck(const CompositorFrameAck& ack)
+ OVERRIDE;
+
+ // Waits for rendering to finish.
+ virtual void Finish() OVERRIDE;
+
+ virtual void DoNoOp() OVERRIDE;
+ // Puts backbuffer onscreen.
+ virtual bool SwapBuffers() OVERRIDE;
+
+ virtual void GetFramebufferPixels(void* pixels, gfx::Rect rect) OVERRIDE;
+
+ virtual bool IsContextLost() OVERRIDE;
+
+ virtual void SetVisible(bool visible) OVERRIDE;
+
+ virtual void SendManagedMemoryStats(size_t bytes_visible,
+ size_t bytes_visible_and_nearby,
+ size_t bytes_allocated) OVERRIDE;
+
+ static void DebugGLCall(WebKit::WebGraphicsContext3D* context,
+ const char* command,
+ const char* file,
+ int line);
+
+ protected:
+ GLRenderer(RendererClient* client,
+ OutputSurface* output_surface,
+ ResourceProvider* resource_provider);
+
+ bool IsBackbufferDiscarded() const { return is_backbuffer_discarded_; }
+ bool Initialize();
+
+ const gfx::QuadF& SharedGeometryQuad() const { return shared_geometry_quad_; }
+ const GeometryBinding* SharedGeometry() const {
+ return shared_geometry_.get();
+ }
+
+ bool GetFramebufferTexture(ScopedResource* resource, gfx::Rect device_rect);
+ void ReleaseRenderPassTextures();
+
+ virtual void BindFramebufferToOutputSurface(DrawingFrame& frame) OVERRIDE;
+ virtual bool BindFramebufferToTexture(DrawingFrame& frame,
+ const ScopedResource* resource,
+ gfx::Rect framebuffer_rect) OVERRIDE;
+ virtual void SetDrawViewportSize(gfx::Size viewport_size) OVERRIDE;
+ virtual void SetScissorTestRect(gfx::Rect scissor_rect) OVERRIDE;
+ virtual void ClearFramebuffer(DrawingFrame& frame) OVERRIDE;
+ virtual void DoDrawQuad(DrawingFrame& frame, const class DrawQuad*) OVERRIDE;
+ virtual void BeginDrawingFrame(DrawingFrame& frame) OVERRIDE;
+ virtual void FinishDrawingFrame(DrawingFrame& frame) OVERRIDE;
+ virtual bool FlippedFramebuffer() const OVERRIDE;
+ virtual void EnsureScissorTestEnabled() OVERRIDE;
+ virtual void EnsureScissorTestDisabled() OVERRIDE;
+ virtual void FinishDrawingQuadList() OVERRIDE;
+
+ private:
+ friend class GLRendererShaderTest;
+
+ static void ToGLMatrix(float* gl_matrix, const gfx::Transform& transform);
+ static ManagedMemoryPolicy::PriorityCutoff PriorityCutoff(
+ WebKit::WebGraphicsMemoryAllocation::PriorityCutoff priority_cutoff);
+
+ void DrawCheckerboardQuad(const DrawingFrame& frame,
+ const CheckerboardDrawQuad* quad);
+ void DrawDebugBorderQuad(const DrawingFrame& frame,
+ const DebugBorderDrawQuad* quad);
+ scoped_ptr<ScopedResource> DrawBackgroundFilters(
+ DrawingFrame& frame,
+ const RenderPassDrawQuad* quad,
+ const gfx::Transform& contents_device_transform,
+ const gfx::Transform& contents_device_transformInverse);
+ void DrawRenderPassQuad(DrawingFrame& frame, const RenderPassDrawQuad* quad);
+ void DrawSolidColorQuad(const DrawingFrame& frame,
+ const SolidColorDrawQuad* quad);
+ void DrawStreamVideoQuad(const DrawingFrame& frame,
+ const StreamVideoDrawQuad* quad);
+ void DrawTextureQuad(const DrawingFrame& frame, const TextureDrawQuad* quad);
+ void EnqueueTextureQuad(const DrawingFrame& frame,
+ const TextureDrawQuad* quad);
+ void FlushTextureQuadCache();
+ void DrawIOSurfaceQuad(const DrawingFrame& frame,
+ const IOSurfaceDrawQuad* quad);
+ void DrawTileQuad(const DrawingFrame& frame, const TileDrawQuad* quad);
+ void DrawYUVVideoQuad(const DrawingFrame& frame,
+ const YUVVideoDrawQuad* quad);
+
+ void SetShaderOpacity(float opacity, int alpha_location);
+ void SetShaderQuadF(const gfx::QuadF& quad, int quad_location);
+ void DrawQuadGeometry(const DrawingFrame& frame,
+ const gfx::Transform& draw_transform,
+ const gfx::RectF& quad_rect,
+ int matrix_location);
+ void SetBlendEnabled(bool enabled);
+ void SetUseProgram(unsigned program);
+
+ void CopyTextureToFramebuffer(const DrawingFrame& frame,
+ int texture_id,
+ gfx::Rect rect,
+ const gfx::Transform& draw_matrix);
+
+ // Check if quad needs antialiasing and if so, inflate the quad and
+ // fill edge array for fragment shader. localQuad is set to
+ // inflated quad if antialiasing is required, otherwise it is left
+ // unchanged. edge array is filled with inflated quad's edge data
+ // if antialiasing is required, otherwise it is left unchanged.
+ // Returns true if quad requires antialiasing and false otherwise.
+ bool SetupQuadForAntialiasing(const gfx::Transform& device_transform,
+ const DrawQuad* quad,
+ gfx::QuadF* local_quad,
+ float edge[24]) const;
+
+ bool UseScopedTexture(DrawingFrame& frame,
+ const ScopedResource* resource,
+ gfx::Rect viewport_rect);
+
+ bool MakeContextCurrent();
+
+ bool InitializeSharedObjects();
+ void CleanupSharedObjects();
+
+ // WebKit::
+ // WebGraphicsContext3D::WebGraphicsSwapBuffersCompleteCallbackCHROMIUM
+ // implementation.
+ virtual void onSwapBuffersComplete() OVERRIDE;
+
+ // WebKit::
+ // WebGraphicsContext3D::WebGraphicsMemoryAllocationChangedCallbackCHROMIUM
+ // implementation.
+ virtual void onMemoryAllocationChanged(
+ WebKit::WebGraphicsMemoryAllocation allocation) OVERRIDE;
+ void DiscardBackbuffer();
+ void EnsureBackbuffer();
+ void EnforceMemoryPolicy();
+
+ // WebGraphicsContext3D::WebGraphicsContextLostCallback implementation.
+ virtual void onContextLost() OVERRIDE;
+
+ RendererCapabilities capabilities_;
+
+ unsigned offscreen_framebuffer_id_;
+
+ scoped_ptr<GeometryBinding> shared_geometry_;
+ gfx::QuadF shared_geometry_quad_;
+
+ // This block of bindings defines all of the programs used by the compositor
+ // itself. Add any new programs here to GLRendererShaderTest.
+
+ // Tiled layer shaders.
+ typedef ProgramBinding<VertexShaderTile, FragmentShaderRGBATexAlpha>
+ TileProgram;
+ typedef ProgramBinding<VertexShaderTile, FragmentShaderRGBATexClampAlphaAA>
+ TileProgramAA;
+ typedef ProgramBinding<VertexShaderTile,
+ FragmentShaderRGBATexClampSwizzleAlphaAA>
+ TileProgramSwizzleAA;
+ typedef ProgramBinding<VertexShaderTile, FragmentShaderRGBATexOpaque>
+ TileProgramOpaque;
+ typedef ProgramBinding<VertexShaderTile, FragmentShaderRGBATexSwizzleAlpha>
+ TileProgramSwizzle;
+ typedef ProgramBinding<VertexShaderTile, FragmentShaderRGBATexSwizzleOpaque>
+ TileProgramSwizzleOpaque;
+ typedef ProgramBinding<VertexShaderPosTex, FragmentShaderCheckerboard>
+ TileCheckerboardProgram;
+
+ // Render surface shaders.
+ typedef ProgramBinding<VertexShaderPosTexTransform,
+ FragmentShaderRGBATexAlpha> RenderPassProgram;
+ typedef ProgramBinding<VertexShaderPosTexTransform,
+ FragmentShaderRGBATexAlphaMask> RenderPassMaskProgram;
+ typedef ProgramBinding<VertexShaderQuad, FragmentShaderRGBATexAlphaAA>
+ RenderPassProgramAA;
+ typedef ProgramBinding<VertexShaderQuad, FragmentShaderRGBATexAlphaMaskAA>
+ RenderPassMaskProgramAA;
+
+ // Texture shaders.
+ typedef ProgramBinding<VertexShaderPosTexTransform,
+ FragmentShaderRGBATexVaryingAlpha> TextureProgram;
+ typedef ProgramBinding<VertexShaderPosTexTransformFlip,
+ FragmentShaderRGBATexVaryingAlpha> TextureProgramFlip;
+ typedef ProgramBinding<VertexShaderPosTexTransform,
+ FragmentShaderRGBATexRectVaryingAlpha>
+ TextureIOSurfaceProgram;
+
+ // Video shaders.
+ typedef ProgramBinding<VertexShaderVideoTransform,
+ FragmentShaderOESImageExternal>
+ VideoStreamTextureProgram;
+ typedef ProgramBinding<VertexShaderPosTexYUVStretch, FragmentShaderYUVVideo>
+ VideoYUVProgram;
+
+ // Special purpose / effects shaders.
+ typedef ProgramBinding<VertexShaderPos, FragmentShaderColor>
+ DebugBorderProgram;
+ typedef ProgramBinding<VertexShaderQuad, FragmentShaderColor>
+ SolidColorProgram;
+ typedef ProgramBinding<VertexShaderQuad, FragmentShaderColorAA>
+ SolidColorProgramAA;
+
+ const TileProgram* GetTileProgram();
+ const TileProgramOpaque* GetTileProgramOpaque();
+ const TileProgramAA* GetTileProgramAA();
+ const TileProgramSwizzle* GetTileProgramSwizzle();
+ const TileProgramSwizzleOpaque* GetTileProgramSwizzleOpaque();
+ const TileProgramSwizzleAA* GetTileProgramSwizzleAA();
+ const TileCheckerboardProgram* GetTileCheckerboardProgram();
+
+ const RenderPassProgram* GetRenderPassProgram();
+ const RenderPassProgramAA* GetRenderPassProgramAA();
+ const RenderPassMaskProgram* GetRenderPassMaskProgram();
+ const RenderPassMaskProgramAA* GetRenderPassMaskProgramAA();
+
+ const TextureProgram* GetTextureProgram();
+ const TextureProgramFlip* GetTextureProgramFlip();
+ const TextureIOSurfaceProgram* GetTextureIOSurfaceProgram();
+
+ const VideoYUVProgram* GetVideoYUVProgram();
+ const VideoStreamTextureProgram* GetVideoStreamTextureProgram();
+
+ const DebugBorderProgram* GetDebugBorderProgram();
+ const SolidColorProgram* GetSolidColorProgram();
+ const SolidColorProgramAA* GetSolidColorProgramAA();
+
+ scoped_ptr<TileProgram> tile_program_;
+ scoped_ptr<TileProgramOpaque> tile_program_opaque_;
+ scoped_ptr<TileProgramAA> tile_program_aa_;
+ scoped_ptr<TileProgramSwizzle> tile_program_swizzle_;
+ scoped_ptr<TileProgramSwizzleOpaque> tile_program_swizzle_opaque_;
+ scoped_ptr<TileProgramSwizzleAA> tile_program_swizzle_aa_;
+ scoped_ptr<TileCheckerboardProgram> tile_checkerboard_program_;
+
+ scoped_ptr<RenderPassProgram> render_pass_program_;
+ scoped_ptr<RenderPassProgramAA> render_pass_program_aa_;
+ scoped_ptr<RenderPassMaskProgram> render_pass_mask_program_;
+ scoped_ptr<RenderPassMaskProgramAA> render_pass_mask_program_aa_;
+
+ scoped_ptr<TextureProgram> texture_program_;
+ scoped_ptr<TextureProgramFlip> texture_program_flip_;
+ scoped_ptr<TextureIOSurfaceProgram> texture_io_surface_program_;
+
+ scoped_ptr<VideoYUVProgram> video_yuv_program_;
+ scoped_ptr<VideoStreamTextureProgram> video_stream_texture_program_;
+
+ scoped_ptr<DebugBorderProgram> debug_border_program_;
+ scoped_ptr<SolidColorProgram> solid_color_program_;
+ scoped_ptr<SolidColorProgramAA> solid_color_program_aa_;
+
+ OutputSurface* output_surface_;
+ WebKit::WebGraphicsContext3D* context_;
+
+ gfx::Rect swap_buffer_rect_;
+ gfx::Rect scissor_rect_;
+ bool is_viewport_changed_;
+ bool is_backbuffer_discarded_;
+ bool discard_backbuffer_when_not_visible_;
+ bool is_using_bind_uniform_;
+ bool visible_;
+ bool is_scissor_enabled_;
+ bool blend_shadow_;
+ unsigned program_shadow_;
+ TexturedQuadDrawCache draw_cache_;
+
+ scoped_ptr<ResourceProvider::ScopedWriteLockGL> current_framebuffer_lock_;
+
+ scoped_refptr<ResourceProvider::Fence> last_swap_fence_;
+
+ DISALLOW_COPY_AND_ASSIGN(GLRenderer);
+};
+
+// Setting DEBUG_GL_CALLS to 1 will call glGetError() after almost every GL
+// call made by the compositor. Useful for debugging rendering issues but
+// will significantly degrade performance.
+#define DEBUG_GL_CALLS 0
+
+#if DEBUG_GL_CALLS && !defined(NDEBUG)
+#define GLC(context, x) \
+ (x, GLRenderer::DebugGLCall (&* context, #x, __FILE__, __LINE__))
+#else
+#define GLC(context, x) (x)
+#endif
+
+}
+
+#endif // CC_OUTPUT_GL_RENDERER_H_
diff --git a/cc/output/gl_renderer_draw_cache.cc b/cc/output/gl_renderer_draw_cache.cc
new file mode 100644
index 0000000..51834c4
--- /dev/null
+++ b/cc/output/gl_renderer_draw_cache.cc
@@ -0,0 +1,14 @@
+// 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/output/gl_renderer_draw_cache.h"
+
+namespace cc {
+
+TexturedQuadDrawCache::TexturedQuadDrawCache()
+ : program_id(0) {}
+
+TexturedQuadDrawCache::~TexturedQuadDrawCache() {}
+
+} // namespace cc
diff --git a/cc/output/gl_renderer_draw_cache.h b/cc/output/gl_renderer_draw_cache.h
new file mode 100644
index 0000000..28d4e71
--- /dev/null
+++ b/cc/output/gl_renderer_draw_cache.h
@@ -0,0 +1,49 @@
+// 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_OUTPUT_GL_RENDERER_DRAW_CACHE_H_
+#define CC_OUTPUT_GL_RENDERER_DRAW_CACHE_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+
+namespace cc {
+
+// Collects 4 floats at a time for easy upload to GL.
+struct Float4 { float data[4]; };
+
+// Collects 16 floats at a time for easy upload to GL.
+struct Float16 { float data[16]; };
+
+// A cache for storing textured quads to be drawn. Stores the minimum required
+// data to tell if two back to back draws only differ in their transform. Quads
+// that only differ by transform may be coalesced into a single draw call.
+struct TexturedQuadDrawCache {
+ TexturedQuadDrawCache();
+ ~TexturedQuadDrawCache();
+
+ // Values tracked to determine if textured quads may be coalesced.
+ int program_id;
+ int resource_id;
+ bool use_premultiplied_alpha;
+ bool needs_blending;
+
+ // Information about the program binding that is required to draw.
+ int uv_xform_location;
+ int vertex_opacity_location;
+ int matrix_location;
+ int sampler_location;
+
+ // A cache for the coalesced quad data.
+ std::vector<Float4> uv_xform_data;
+ std::vector<float> vertex_opacity_data;
+ std::vector<Float16> matrix_data;
+
+ DISALLOW_COPY_AND_ASSIGN(TexturedQuadDrawCache);
+};
+
+} // namespace cc
+
+#endif // CC_OUTPUT_GL_RENDERER_DRAW_CACHE_H_
diff --git a/cc/output/gl_renderer_pixeltest.cc b/cc/output/gl_renderer_pixeltest.cc
new file mode 100644
index 0000000..b8492e6
--- /dev/null
+++ b/cc/output/gl_renderer_pixeltest.cc
@@ -0,0 +1,325 @@
+// 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/output/gl_renderer.h"
+
+#include "cc/draw_quad.h"
+#include "cc/test/pixel_test.h"
+
+namespace cc {
+namespace {
+
+class GLRendererPixelTest : public PixelTest {};
+
+scoped_ptr<RenderPass> CreateTestRootRenderPass(RenderPass::Id id,
+ gfx::Rect rect) {
+ scoped_ptr<RenderPass> pass = RenderPass::Create();
+ const gfx::Rect output_rect = rect;
+ const gfx::RectF damage_rect = rect;
+ const gfx::Transform transform_to_root_target;
+ pass->SetNew(id, output_rect, damage_rect, transform_to_root_target);
+ return pass.Pass();
+}
+
+scoped_ptr<RenderPass> CreateTestRenderPass(
+ RenderPass::Id id,
+ gfx::Rect rect,
+ const gfx::Transform& transform_to_root_target) {
+ scoped_ptr<RenderPass> pass = RenderPass::Create();
+ const gfx::Rect output_rect = rect;
+ const gfx::RectF damage_rect = rect;
+ pass->SetNew(id, output_rect, damage_rect, transform_to_root_target);
+ return pass.Pass();
+}
+
+scoped_ptr<SharedQuadState> CreateTestSharedQuadState(
+ gfx::Transform content_to_target_transform, gfx::Rect rect) {
+ const gfx::Size content_bounds = rect.size();
+ const gfx::Rect visible_content_rect = rect;
+ const gfx::Rect clip_rect = rect;
+ const bool is_clipped = false;
+ const float opacity = 1.0f;
+ scoped_ptr<SharedQuadState> shared_state = SharedQuadState::Create();
+ shared_state->SetAll(content_to_target_transform,
+ content_bounds,
+ visible_content_rect,
+ clip_rect,
+ is_clipped,
+ opacity);
+ return shared_state.Pass();
+}
+
+scoped_ptr<DrawQuad> CreateTestRenderPassDrawQuad(
+ SharedQuadState* shared_state, gfx::Rect rect, RenderPass::Id pass_id) {
+ scoped_ptr<RenderPassDrawQuad> quad = RenderPassDrawQuad::Create();
+ quad->SetNew(shared_state,
+ rect,
+ pass_id,
+ false, // is_replica
+ 0, // mask_resource_id
+ rect, // contents_changed_since_last_frame
+ gfx::RectF(), // mask_uv_rect
+ WebKit::WebFilterOperations(), // foreground filters
+ skia::RefPtr<SkImageFilter>(), // foreground filter
+ WebKit::WebFilterOperations()); // background filters
+
+ return quad.PassAs<DrawQuad>();
+}
+
+
+#if !defined(OS_ANDROID)
+TEST_F(GLRendererPixelTest, simpleGreenRect) {
+ gfx::Rect rect(device_viewport_size_);
+
+ RenderPass::Id id(1, 1);
+ scoped_ptr<RenderPass> pass = CreateTestRootRenderPass(id, rect);
+
+ gfx::Transform content_to_target_transform;
+ scoped_ptr<SharedQuadState> shared_state =
+ CreateTestSharedQuadState(content_to_target_transform, rect);
+
+ scoped_ptr<SolidColorDrawQuad> color_quad = SolidColorDrawQuad::Create();
+ color_quad->SetNew(shared_state.get(), rect, SK_ColorGREEN);
+
+ pass->quad_list.push_back(color_quad.PassAs<DrawQuad>());
+
+ RenderPassList pass_list;
+ pass_list.push_back(pass.Pass());
+
+ renderer_->DrawFrame(pass_list);
+
+ EXPECT_TRUE(PixelsMatchReference(
+ base::FilePath(FILE_PATH_LITERAL("green.png"))));
+}
+
+TEST_F(GLRendererPixelTest, RenderPassChangesSize) {
+ gfx::Rect viewport_rect(device_viewport_size_);
+
+ RenderPass::Id root_pass_id(1, 1);
+ scoped_ptr<RenderPass> root_pass =
+ CreateTestRootRenderPass(root_pass_id, viewport_rect);
+
+ RenderPass::Id child_pass_id(2, 2);
+ gfx::Rect pass_rect(device_viewport_size_);
+ gfx::Transform transform_to_root;
+ scoped_ptr<RenderPass> child_pass =
+ CreateTestRenderPass(child_pass_id, pass_rect, transform_to_root);
+
+ gfx::Transform content_to_target_transform;
+ scoped_ptr<SharedQuadState> shared_state =
+ CreateTestSharedQuadState(content_to_target_transform, viewport_rect);
+
+ scoped_ptr<SolidColorDrawQuad> blue = SolidColorDrawQuad::Create();
+ blue->SetNew(shared_state.get(),
+ gfx::Rect(0,
+ 0,
+ device_viewport_size_.width() / 2,
+ device_viewport_size_.height()),
+ SK_ColorBLUE);
+ scoped_ptr<SolidColorDrawQuad> yellow = SolidColorDrawQuad::Create();
+ yellow->SetNew(shared_state.get(),
+ gfx::Rect(device_viewport_size_.width() / 2,
+ 0,
+ device_viewport_size_.width() / 2,
+ device_viewport_size_.height()),
+ SK_ColorYELLOW);
+
+ child_pass->quad_list.push_back(blue.PassAs<DrawQuad>());
+ child_pass->quad_list.push_back(yellow.PassAs<DrawQuad>());
+
+ scoped_ptr<SharedQuadState> pass_shared_state =
+ CreateTestSharedQuadState(gfx::Transform(), pass_rect);
+ root_pass->quad_list.push_back(
+ CreateTestRenderPassDrawQuad(pass_shared_state.get(),
+ pass_rect,
+ child_pass_id));
+
+ RenderPassList pass_list;
+ pass_list.push_back(child_pass.Pass());
+ pass_list.push_back(root_pass.Pass());
+
+ renderer_->SetEnlargePassTextureAmountForTesting(gfx::Vector2d(50, 75));
+ renderer_->DecideRenderPassAllocationsForFrame(pass_list);
+ renderer_->DrawFrame(pass_list);
+
+ EXPECT_TRUE(PixelsMatchReference(
+ base::FilePath(FILE_PATH_LITERAL("blue_yellow.png"))));
+}
+
+class GLRendererPixelTestWithBackgroundFilter : public GLRendererPixelTest {
+ protected:
+ void DrawFrame() {
+ gfx::Rect device_viewport_rect(device_viewport_size_);
+
+ RenderPass::Id root_id(1, 1);
+ scoped_ptr<RenderPass> root_pass =
+ CreateTestRootRenderPass(root_id, device_viewport_rect);
+ root_pass->has_transparent_background = false;
+
+ gfx::Transform identity_content_to_target_transform;
+
+ RenderPass::Id filter_pass_id(2, 1);
+ gfx::Transform transform_to_root;
+ scoped_ptr<RenderPass> filter_pass =
+ CreateTestRenderPass(filter_pass_id,
+ filter_pass_content_rect_,
+ transform_to_root);
+
+ // A non-visible quad in the filtering render pass.
+ {
+ scoped_ptr<SharedQuadState> shared_state =
+ CreateTestSharedQuadState(identity_content_to_target_transform,
+ filter_pass_content_rect_);
+ scoped_ptr<SolidColorDrawQuad> color_quad = SolidColorDrawQuad::Create();
+ color_quad->SetNew(shared_state.get(),
+ filter_pass_content_rect_,
+ SK_ColorTRANSPARENT);
+ filter_pass->quad_list.push_back(color_quad.PassAs<DrawQuad>());
+ filter_pass->shared_quad_state_list.push_back(shared_state.Pass());
+ }
+
+ {
+ scoped_ptr<SharedQuadState> shared_state =
+ CreateTestSharedQuadState(filter_pass_to_target_transform_,
+ filter_pass_content_rect_);
+ scoped_ptr<RenderPassDrawQuad> filter_pass_quad =
+ RenderPassDrawQuad::Create();
+ filter_pass_quad->SetNew(
+ shared_state.get(),
+ filter_pass_content_rect_,
+ filter_pass_id,
+ false, // is_replica
+ 0, // mask_resource_id
+ filter_pass_content_rect_, // contents_changed_since_last_frame
+ gfx::RectF(), // mask_uv_rect
+ WebKit::WebFilterOperations(), // filters
+ skia::RefPtr<SkImageFilter>(), // filter
+ background_filters_);
+ root_pass->quad_list.push_back(filter_pass_quad.PassAs<DrawQuad>());
+ root_pass->shared_quad_state_list.push_back(shared_state.Pass());
+ }
+
+ const int kColumnWidth = device_viewport_rect.width() / 3;
+
+ gfx::Rect left_rect = gfx::Rect(0, 0, kColumnWidth, 20);
+ for (int i = 0; left_rect.y() < device_viewport_rect.height(); ++i) {
+ scoped_ptr<SharedQuadState> shared_state =
+ CreateTestSharedQuadState(identity_content_to_target_transform,
+ left_rect);
+ scoped_ptr<SolidColorDrawQuad> color_quad = SolidColorDrawQuad::Create();
+ color_quad->SetNew(shared_state.get(), left_rect, SK_ColorGREEN);
+ root_pass->quad_list.push_back(color_quad.PassAs<DrawQuad>());
+ root_pass->shared_quad_state_list.push_back(shared_state.Pass());
+ left_rect += gfx::Vector2d(0, left_rect.height() + 1);
+ }
+
+ gfx::Rect middle_rect = gfx::Rect(kColumnWidth+1, 0, kColumnWidth, 20);
+ for (int i = 0; middle_rect.y() < device_viewport_rect.height(); ++i) {
+ scoped_ptr<SharedQuadState> shared_state =
+ CreateTestSharedQuadState(identity_content_to_target_transform,
+ middle_rect);
+ scoped_ptr<SolidColorDrawQuad> color_quad = SolidColorDrawQuad::Create();
+ color_quad->SetNew(shared_state.get(), middle_rect, SK_ColorRED);
+ root_pass->quad_list.push_back(color_quad.PassAs<DrawQuad>());
+ root_pass->shared_quad_state_list.push_back(shared_state.Pass());
+ middle_rect += gfx::Vector2d(0, middle_rect.height() + 1);
+ }
+
+ gfx::Rect right_rect = gfx::Rect((kColumnWidth+1)*2, 0, kColumnWidth, 20);
+ for (int i = 0; right_rect.y() < device_viewport_rect.height(); ++i) {
+ scoped_ptr<SharedQuadState> shared_state =
+ CreateTestSharedQuadState(identity_content_to_target_transform,
+ right_rect);
+ scoped_ptr<SolidColorDrawQuad> color_quad = SolidColorDrawQuad::Create();
+ color_quad->SetNew(shared_state.get(), right_rect, SK_ColorBLUE);
+ root_pass->quad_list.push_back(color_quad.PassAs<DrawQuad>());
+ root_pass->shared_quad_state_list.push_back(shared_state.Pass());
+ right_rect += gfx::Vector2d(0, right_rect.height() + 1);
+ }
+
+ scoped_ptr<SharedQuadState> shared_state =
+ CreateTestSharedQuadState(identity_content_to_target_transform,
+ device_viewport_rect);
+ scoped_ptr<SolidColorDrawQuad> background_quad =
+ SolidColorDrawQuad::Create();
+ background_quad->SetNew(shared_state.get(),
+ device_viewport_rect,
+ SK_ColorWHITE);
+ root_pass->quad_list.push_back(background_quad.PassAs<DrawQuad>());
+ root_pass->shared_quad_state_list.push_back(shared_state.Pass());
+
+ RenderPassList pass_list;
+ pass_list.push_back(filter_pass.Pass());
+ pass_list.push_back(root_pass.Pass());
+
+ renderer_->DecideRenderPassAllocationsForFrame(pass_list);
+ renderer_->DrawFrame(pass_list);
+ }
+
+ WebKit::WebFilterOperations background_filters_;
+ gfx::Transform filter_pass_to_target_transform_;
+ gfx::Rect filter_pass_content_rect_;
+};
+
+TEST_F(GLRendererPixelTestWithBackgroundFilter, InvertFilter) {
+ background_filters_.append(
+ WebKit::WebFilterOperation::createInvertFilter(1.f));
+
+ filter_pass_content_rect_ = gfx::Rect(device_viewport_size_);
+ filter_pass_content_rect_.Inset(12, 14, 16, 18);
+
+ DrawFrame();
+ EXPECT_TRUE(PixelsMatchReference(
+ base::FilePath(FILE_PATH_LITERAL("background_filter.png"))));
+}
+
+TEST_F(GLRendererPixelTest, AntiAliasing) {
+ gfx::Rect rect(0, 0, 200, 200);
+
+ RenderPass::Id id(1, 1);
+ gfx::Transform transform_to_root;
+ scoped_ptr<RenderPass> pass =
+ CreateTestRenderPass(id, rect, transform_to_root);
+
+ gfx::Transform red_content_to_target_transform;
+ red_content_to_target_transform.Rotate(10);
+ scoped_ptr<SharedQuadState> red_shared_state =
+ CreateTestSharedQuadState(red_content_to_target_transform, rect);
+
+ scoped_ptr<SolidColorDrawQuad> red = SolidColorDrawQuad::Create();
+ red->SetNew(red_shared_state.get(), rect, SK_ColorRED);
+
+ pass->quad_list.push_back(red.PassAs<DrawQuad>());
+
+ gfx::Transform yellow_content_to_target_transform;
+ yellow_content_to_target_transform.Rotate(5);
+ scoped_ptr<SharedQuadState> yellow_shared_state =
+ CreateTestSharedQuadState(yellow_content_to_target_transform, rect);
+
+ scoped_ptr<SolidColorDrawQuad> yellow = SolidColorDrawQuad::Create();
+ yellow->SetNew(yellow_shared_state.get(), rect, SK_ColorYELLOW);
+
+ pass->quad_list.push_back(yellow.PassAs<DrawQuad>());
+
+ gfx::Transform blue_content_to_target_transform;
+ scoped_ptr<SharedQuadState> blue_shared_state =
+ CreateTestSharedQuadState(blue_content_to_target_transform, rect);
+
+ scoped_ptr<SolidColorDrawQuad> blue = SolidColorDrawQuad::Create();
+ blue->SetNew(blue_shared_state.get(), rect, SK_ColorBLUE);
+
+ pass->quad_list.push_back(blue.PassAs<DrawQuad>());
+
+ RenderPassList pass_list;
+ pass_list.push_back(pass.Pass());
+
+ renderer_->DrawFrame(pass_list);
+
+ EXPECT_TRUE(PixelsMatchReference(
+ base::FilePath(FILE_PATH_LITERAL("anti_aliasing.png"))));
+}
+#endif
+
+} // namespace
+} // namespace cc
diff --git a/cc/output/gl_renderer_unittest.cc b/cc/output/gl_renderer_unittest.cc
new file mode 100644
index 0000000..eca58a7
--- /dev/null
+++ b/cc/output/gl_renderer_unittest.cc
@@ -0,0 +1,878 @@
+// 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/output/gl_renderer.h"
+
+#include "cc/draw_quad.h"
+#include "cc/output/compositor_frame_metadata.h"
+#include "cc/prioritized_resource_manager.h"
+#include "cc/resource_provider.h"
+#include "cc/test/fake_impl_proxy.h"
+#include "cc/test/fake_layer_tree_host_impl.h"
+#include "cc/test/fake_output_surface.h"
+#include "cc/test/pixel_test.h"
+#include "cc/test/render_pass_test_common.h"
+#include "cc/test/render_pass_test_utils.h"
+#include "cc/test/test_web_graphics_context_3d.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "ui/gfx/transform.h"
+
+using namespace WebKit;
+
+using testing::_;
+using testing::AnyNumber;
+using testing::AtLeast;
+using testing::Expectation;
+using testing::InSequence;
+using testing::Mock;
+using testing::Return;
+using testing::StrictMock;
+
+namespace cc {
+
+#define EXPECT_PROGRAM_VALID(program_binding) \
+ do { \
+ EXPECT_TRUE(program_binding->program()); \
+ EXPECT_TRUE(program_binding->initialized()); \
+ } while (false)
+
+// Explicitly named to be a friend in GLRenderer for shader access.
+class GLRendererShaderTest : public PixelTest {
+ public:
+ void TestShaders() {
+ ASSERT_FALSE(renderer_->IsContextLost());
+ EXPECT_PROGRAM_VALID(renderer_->GetTileProgram());
+ EXPECT_PROGRAM_VALID(renderer_->GetTileProgramOpaque());
+ EXPECT_PROGRAM_VALID(renderer_->GetTileProgramAA());
+ EXPECT_PROGRAM_VALID(renderer_->GetTileProgramSwizzle());
+ EXPECT_PROGRAM_VALID(renderer_->GetTileProgramSwizzleOpaque());
+ EXPECT_PROGRAM_VALID(renderer_->GetTileProgramSwizzleAA());
+ EXPECT_PROGRAM_VALID(renderer_->GetTileCheckerboardProgram());
+ EXPECT_PROGRAM_VALID(renderer_->GetRenderPassProgram());
+ EXPECT_PROGRAM_VALID(renderer_->GetRenderPassProgramAA());
+ EXPECT_PROGRAM_VALID(renderer_->GetRenderPassMaskProgram());
+ EXPECT_PROGRAM_VALID(renderer_->GetRenderPassMaskProgramAA());
+ EXPECT_PROGRAM_VALID(renderer_->GetTextureProgram());
+ EXPECT_PROGRAM_VALID(renderer_->GetTextureProgramFlip());
+ EXPECT_PROGRAM_VALID(renderer_->GetTextureIOSurfaceProgram());
+ EXPECT_PROGRAM_VALID(renderer_->GetVideoYUVProgram());
+ // This is unlikely to be ever true in tests due to usage of osmesa.
+ if (renderer_->Capabilities().using_egl_image)
+ EXPECT_PROGRAM_VALID(renderer_->GetVideoStreamTextureProgram());
+ else
+ EXPECT_FALSE(renderer_->GetVideoStreamTextureProgram());
+ EXPECT_PROGRAM_VALID(renderer_->GetDebugBorderProgram());
+ EXPECT_PROGRAM_VALID(renderer_->GetSolidColorProgram());
+ EXPECT_PROGRAM_VALID(renderer_->GetSolidColorProgramAA());
+ ASSERT_FALSE(renderer_->IsContextLost());
+ }
+};
+
+namespace {
+
+#if !defined(OS_ANDROID)
+TEST_F(GLRendererShaderTest, AllShadersCompile) {
+ TestShaders();
+}
+#endif
+
+class FrameCountingMemoryAllocationSettingContext : public TestWebGraphicsContext3D {
+public:
+ FrameCountingMemoryAllocationSettingContext() : m_frame(0) { }
+
+ // WebGraphicsContext3D methods.
+
+ // This method would normally do a glSwapBuffers under the hood.
+ virtual void prepareTexture() { m_frame++; }
+ virtual void setMemoryAllocationChangedCallbackCHROMIUM(WebGraphicsMemoryAllocationChangedCallbackCHROMIUM* callback) { m_memoryAllocationChangedCallback = callback; }
+ virtual WebString getString(WebKit::WGC3Denum name)
+ {
+ if (name == GL_EXTENSIONS)
+ return WebString("GL_CHROMIUM_set_visibility GL_CHROMIUM_gpu_memory_manager GL_CHROMIUM_discard_backbuffer");
+ return WebString();
+ }
+
+ // Methods added for test.
+ int frameCount() { return m_frame; }
+ void setMemoryAllocation(WebGraphicsMemoryAllocation allocation)
+ {
+ m_memoryAllocationChangedCallback->onMemoryAllocationChanged(allocation);
+ }
+
+private:
+ int m_frame;
+ WebGraphicsMemoryAllocationChangedCallbackCHROMIUM* m_memoryAllocationChangedCallback;
+};
+
+class FakeRendererClient : public RendererClient {
+public:
+ FakeRendererClient()
+ : m_hostImpl(&m_proxy)
+ , m_setFullRootLayerDamageCount(0)
+ , m_lastCallWasSetVisibility(0)
+ , m_rootLayer(LayerImpl::Create(m_hostImpl.active_tree(), 1))
+ , m_memoryAllocationLimitBytes(PrioritizedResourceManager::defaultMemoryAllocationLimit())
+ {
+ m_rootLayer->CreateRenderSurface();
+ RenderPass::Id renderPassId = m_rootLayer->render_surface()->RenderPassId();
+ scoped_ptr<RenderPass> root_render_pass = RenderPass::Create();
+ root_render_pass->SetNew(renderPassId, gfx::Rect(), gfx::Rect(), gfx::Transform());
+ m_renderPassesInDrawOrder.push_back(root_render_pass.Pass());
+ }
+
+ // RendererClient methods.
+ virtual gfx::Size DeviceViewportSize() const OVERRIDE { static gfx::Size fakeSize(1, 1); return fakeSize; }
+ virtual const LayerTreeSettings& Settings() const OVERRIDE { static LayerTreeSettings fakeSettings; return fakeSettings; }
+ virtual void DidLoseOutputSurface() OVERRIDE { }
+ virtual void OnSwapBuffersComplete() OVERRIDE { }
+ virtual void SetFullRootLayerDamage() OVERRIDE { m_setFullRootLayerDamageCount++; }
+ 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; }
+ virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const
+ OVERRIDE { return CompositorFrameMetadata(); }
+
+ // Methods added for test.
+ int setFullRootLayerDamageCount() const { return m_setFullRootLayerDamageCount; }
+ void setLastCallWasSetVisibilityPointer(bool* lastCallWasSetVisibility) { m_lastCallWasSetVisibility = lastCallWasSetVisibility; }
+
+ RenderPass* root_render_pass() { return m_renderPassesInDrawOrder.back(); }
+ RenderPassList& renderPassesInDrawOrder() { return m_renderPassesInDrawOrder; }
+
+ size_t memoryAllocationLimitBytes() const { return m_memoryAllocationLimitBytes; }
+
+private:
+ FakeImplProxy m_proxy;
+ FakeLayerTreeHostImpl m_hostImpl;
+ int m_setFullRootLayerDamageCount;
+ bool* m_lastCallWasSetVisibility;
+ scoped_ptr<LayerImpl> m_rootLayer;
+ RenderPassList m_renderPassesInDrawOrder;
+ size_t m_memoryAllocationLimitBytes;
+};
+
+class FakeRendererGL : public GLRenderer {
+public:
+ FakeRendererGL(RendererClient* client, OutputSurface* outputSurface, ResourceProvider* resourceProvider) : GLRenderer(client, outputSurface, resourceProvider) { }
+
+ // GLRenderer methods.
+
+ // Changing visibility to public.
+ using GLRenderer::Initialize;
+ using GLRenderer::IsBackbufferDiscarded;
+ using GLRenderer::DoDrawQuad;
+ using GLRenderer::BeginDrawingFrame;
+ using GLRenderer::FinishDrawingQuadList;
+};
+
+class GLRendererTest : public testing::Test {
+protected:
+ GLRendererTest()
+ : m_suggestHaveBackbufferYes(1, true)
+ , m_suggestHaveBackbufferNo(1, false)
+ , m_outputSurface(FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(new FrameCountingMemoryAllocationSettingContext())))
+ , resource_provider_(ResourceProvider::Create(m_outputSurface.get()))
+ , m_renderer(&m_mockClient, m_outputSurface.get(), resource_provider_.get())
+ {
+ }
+
+ virtual void SetUp()
+ {
+ m_renderer.Initialize();
+ }
+
+ void SwapBuffers()
+ {
+ m_renderer.SwapBuffers();
+ }
+
+ FrameCountingMemoryAllocationSettingContext* context() { return static_cast<FrameCountingMemoryAllocationSettingContext*>(m_outputSurface->context3d()); }
+
+ WebGraphicsMemoryAllocation m_suggestHaveBackbufferYes;
+ WebGraphicsMemoryAllocation m_suggestHaveBackbufferNo;
+
+ scoped_ptr<OutputSurface> m_outputSurface;
+ FakeRendererClient m_mockClient;
+ scoped_ptr<ResourceProvider> resource_provider_;
+ FakeRendererGL m_renderer;
+};
+
+// Test GLRenderer discardBackbuffer functionality:
+// Suggest recreating framebuffer when one already exists.
+// Expected: it does nothing.
+TEST_F(GLRendererTest, SuggestBackbufferYesWhenItAlreadyExistsShouldDoNothing)
+{
+ context()->setMemoryAllocation(m_suggestHaveBackbufferYes);
+ EXPECT_EQ(0, m_mockClient.setFullRootLayerDamageCount());
+ EXPECT_FALSE(m_renderer.IsBackbufferDiscarded());
+
+ SwapBuffers();
+ EXPECT_EQ(1, context()->frameCount());
+}
+
+// Test GLRenderer discardBackbuffer functionality:
+// Suggest discarding framebuffer when one exists and the renderer is not visible.
+// Expected: it is discarded and damage tracker is reset.
+TEST_F(GLRendererTest, SuggestBackbufferNoShouldDiscardBackbufferAndDamageRootLayerWhileNotVisible)
+{
+ m_renderer.SetVisible(false);
+ context()->setMemoryAllocation(m_suggestHaveBackbufferNo);
+ EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount());
+ EXPECT_TRUE(m_renderer.IsBackbufferDiscarded());
+}
+
+// Test GLRenderer discardBackbuffer functionality:
+// Suggest discarding framebuffer when one exists and the renderer is visible.
+// Expected: the allocation is ignored.
+TEST_F(GLRendererTest, SuggestBackbufferNoDoNothingWhenVisible)
+{
+ m_renderer.SetVisible(true);
+ context()->setMemoryAllocation(m_suggestHaveBackbufferNo);
+ EXPECT_EQ(0, m_mockClient.setFullRootLayerDamageCount());
+ EXPECT_FALSE(m_renderer.IsBackbufferDiscarded());
+}
+
+
+// Test GLRenderer discardBackbuffer functionality:
+// Suggest discarding framebuffer when one does not exist.
+// Expected: it does nothing.
+TEST_F(GLRendererTest, SuggestBackbufferNoWhenItDoesntExistShouldDoNothing)
+{
+ m_renderer.SetVisible(false);
+ context()->setMemoryAllocation(m_suggestHaveBackbufferNo);
+ EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount());
+ EXPECT_TRUE(m_renderer.IsBackbufferDiscarded());
+
+ context()->setMemoryAllocation(m_suggestHaveBackbufferNo);
+ EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount());
+ EXPECT_TRUE(m_renderer.IsBackbufferDiscarded());
+}
+
+// Test GLRenderer discardBackbuffer functionality:
+// Begin drawing a frame while a framebuffer is discarded.
+// Expected: will recreate framebuffer.
+TEST_F(GLRendererTest, DiscardedBackbufferIsRecreatedForScopeDuration)
+{
+ m_renderer.SetVisible(false);
+ context()->setMemoryAllocation(m_suggestHaveBackbufferNo);
+ EXPECT_TRUE(m_renderer.IsBackbufferDiscarded());
+ EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount());
+
+ m_renderer.SetVisible(true);
+ m_renderer.DrawFrame(m_mockClient.renderPassesInDrawOrder());
+ EXPECT_FALSE(m_renderer.IsBackbufferDiscarded());
+
+ SwapBuffers();
+ EXPECT_EQ(1, context()->frameCount());
+}
+
+TEST_F(GLRendererTest, FramebufferDiscardedAfterReadbackWhenNotVisible)
+{
+ m_renderer.SetVisible(false);
+ context()->setMemoryAllocation(m_suggestHaveBackbufferNo);
+ EXPECT_TRUE(m_renderer.IsBackbufferDiscarded());
+ EXPECT_EQ(1, m_mockClient.setFullRootLayerDamageCount());
+
+ char pixels[4];
+ m_renderer.DrawFrame(m_mockClient.renderPassesInDrawOrder());
+ EXPECT_FALSE(m_renderer.IsBackbufferDiscarded());
+
+ m_renderer.GetFramebufferPixels(pixels, gfx::Rect(0, 0, 1, 1));
+ EXPECT_TRUE(m_renderer.IsBackbufferDiscarded());
+ EXPECT_EQ(2, m_mockClient.setFullRootLayerDamageCount());
+}
+
+class ForbidSynchronousCallContext : public TestWebGraphicsContext3D {
+public:
+ ForbidSynchronousCallContext() { }
+
+ virtual bool getActiveAttrib(WebGLId program, WGC3Duint index, ActiveInfo&) { ADD_FAILURE(); return false; }
+ virtual bool getActiveUniform(WebGLId program, WGC3Duint index, ActiveInfo&) { ADD_FAILURE(); return false; }
+ virtual void getAttachedShaders(WebGLId program, WGC3Dsizei maxCount, WGC3Dsizei* count, WebGLId* shaders) { ADD_FAILURE(); }
+ virtual WGC3Dint getAttribLocation(WebGLId program, const WGC3Dchar* name) { ADD_FAILURE(); return 0; }
+ virtual void getBooleanv(WGC3Denum pname, WGC3Dboolean* value) { ADD_FAILURE(); }
+ virtual void getBufferParameteriv(WGC3Denum target, WGC3Denum pname, WGC3Dint* value) { ADD_FAILURE(); }
+ virtual Attributes getContextAttributes() { ADD_FAILURE(); return attributes_; }
+ virtual WGC3Denum getError() { ADD_FAILURE(); return 0; }
+ virtual void getFloatv(WGC3Denum pname, WGC3Dfloat* value) { ADD_FAILURE(); }
+ virtual void getFramebufferAttachmentParameteriv(WGC3Denum target, WGC3Denum attachment, WGC3Denum pname, WGC3Dint* value) { ADD_FAILURE(); }
+ virtual void getIntegerv(WGC3Denum pname, WGC3Dint* value)
+ {
+ if (pname == GL_MAX_TEXTURE_SIZE)
+ *value = 1024; // MAX_TEXTURE_SIZE is cached client side, so it's OK to query.
+ else
+ ADD_FAILURE();
+ }
+
+ // We allow querying the shader compilation and program link status in debug mode, but not release.
+ virtual void getProgramiv(WebGLId program, WGC3Denum pname, WGC3Dint* value)
+ {
+#ifndef NDEBUG
+ *value = 1;
+#else
+ ADD_FAILURE();
+#endif
+ }
+
+ virtual void getShaderiv(WebGLId shader, WGC3Denum pname, WGC3Dint* value)
+ {
+#ifndef NDEBUG
+ *value = 1;
+#else
+ ADD_FAILURE();
+#endif
+ }
+
+ virtual WebString getString(WGC3Denum name)
+ {
+ // We allow querying the extension string.
+ // FIXME: It'd be better to check that we only do this before starting any other expensive work (like starting a compilation)
+ if (name != GL_EXTENSIONS)
+ ADD_FAILURE();
+ return WebString();
+ }
+
+ virtual WebString getProgramInfoLog(WebGLId program) { ADD_FAILURE(); return WebString(); }
+ virtual void getRenderbufferParameteriv(WGC3Denum target, WGC3Denum pname, WGC3Dint* value) { ADD_FAILURE(); }
+
+ virtual WebString getShaderInfoLog(WebGLId shader) { ADD_FAILURE(); return WebString(); }
+ virtual void getShaderPrecisionFormat(WGC3Denum shadertype, WGC3Denum precisiontype, WGC3Dint* range, WGC3Dint* precision) { ADD_FAILURE(); }
+ virtual WebString getShaderSource(WebGLId shader) { ADD_FAILURE(); return WebString(); }
+ virtual void getTexParameterfv(WGC3Denum target, WGC3Denum pname, WGC3Dfloat* value) { ADD_FAILURE(); }
+ virtual void getTexParameteriv(WGC3Denum target, WGC3Denum pname, WGC3Dint* value) { ADD_FAILURE(); }
+ virtual void getUniformfv(WebGLId program, WGC3Dint location, WGC3Dfloat* value) { ADD_FAILURE(); }
+ virtual void getUniformiv(WebGLId program, WGC3Dint location, WGC3Dint* value) { ADD_FAILURE(); }
+ virtual WGC3Dint getUniformLocation(WebGLId program, const WGC3Dchar* name) { ADD_FAILURE(); return 0; }
+ virtual void getVertexAttribfv(WGC3Duint index, WGC3Denum pname, WGC3Dfloat* value) { ADD_FAILURE(); }
+ virtual void getVertexAttribiv(WGC3Duint index, WGC3Denum pname, WGC3Dint* value) { ADD_FAILURE(); }
+ virtual WGC3Dsizeiptr getVertexAttribOffset(WGC3Duint index, WGC3Denum pname) { ADD_FAILURE(); return 0; }
+};
+
+// This test isn't using the same fixture as GLRendererTest, and you can't mix TEST() and TEST_F() with the same name, hence LRC2.
+TEST(GLRendererTest2, initializationDoesNotMakeSynchronousCalls)
+{
+ FakeRendererClient mockClient;
+ scoped_ptr<OutputSurface> outputSurface(FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(new ForbidSynchronousCallContext)));
+ scoped_ptr<ResourceProvider> resourceProvider(ResourceProvider::Create(outputSurface.get()));
+ FakeRendererGL renderer(&mockClient, outputSurface.get(), resourceProvider.get());
+
+ EXPECT_TRUE(renderer.Initialize());
+}
+
+class LoseContextOnFirstGetContext : public TestWebGraphicsContext3D {
+public:
+ LoseContextOnFirstGetContext()
+ : m_contextLost(false)
+ {
+ }
+
+ virtual bool makeContextCurrent() OVERRIDE
+ {
+ return !m_contextLost;
+ }
+
+ virtual void getProgramiv(WebGLId program, WGC3Denum pname, WGC3Dint* value) OVERRIDE
+ {
+ m_contextLost = true;
+ *value = 0;
+ }
+
+ virtual void getShaderiv(WebGLId shader, WGC3Denum pname, WGC3Dint* value) OVERRIDE
+ {
+ m_contextLost = true;
+ *value = 0;
+ }
+
+ virtual WGC3Denum getGraphicsResetStatusARB() OVERRIDE
+ {
+ return m_contextLost ? 1 : 0;
+ }
+
+private:
+ bool m_contextLost;
+};
+
+TEST(GLRendererTest2, initializationWithQuicklyLostContextDoesNotAssert)
+{
+ FakeRendererClient mockClient;
+ scoped_ptr<OutputSurface> outputSurface(FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(new LoseContextOnFirstGetContext)));
+ scoped_ptr<ResourceProvider> resourceProvider(ResourceProvider::Create(outputSurface.get()));
+ FakeRendererGL renderer(&mockClient, outputSurface.get(), resourceProvider.get());
+
+ renderer.Initialize();
+}
+
+class ContextThatDoesNotSupportMemoryManagmentExtensions : public TestWebGraphicsContext3D {
+public:
+ ContextThatDoesNotSupportMemoryManagmentExtensions() { }
+
+ // WebGraphicsContext3D methods.
+
+ // This method would normally do a glSwapBuffers under the hood.
+ virtual void prepareTexture() { }
+ virtual void setMemoryAllocationChangedCallbackCHROMIUM(WebGraphicsMemoryAllocationChangedCallbackCHROMIUM* callback) { }
+ virtual WebString getString(WebKit::WGC3Denum name) { return WebString(); }
+};
+
+TEST(GLRendererTest2, initializationWithoutGpuMemoryManagerExtensionSupportShouldDefaultToNonZeroAllocation)
+{
+ FakeRendererClient mockClient;
+ scoped_ptr<OutputSurface> outputSurface(FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(new ContextThatDoesNotSupportMemoryManagmentExtensions)));
+ scoped_ptr<ResourceProvider> resourceProvider(ResourceProvider::Create(outputSurface.get()));
+ FakeRendererGL renderer(&mockClient, outputSurface.get(), resourceProvider.get());
+
+ renderer.Initialize();
+
+ EXPECT_GT(mockClient.memoryAllocationLimitBytes(), 0ul);
+}
+
+class ClearCountingContext : public TestWebGraphicsContext3D {
+public:
+ ClearCountingContext() : m_clear(0) { }
+
+ virtual void clear(WGC3Dbitfield)
+ {
+ m_clear++;
+ }
+
+ int clearCount() const { return m_clear; }
+
+private:
+ int m_clear;
+};
+
+TEST(GLRendererTest2, opaqueBackground)
+{
+ FakeRendererClient mockClient;
+ scoped_ptr<OutputSurface> outputSurface(FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(new ClearCountingContext)));
+ ClearCountingContext* context = static_cast<ClearCountingContext*>(outputSurface->context3d());
+ scoped_ptr<ResourceProvider> resourceProvider(ResourceProvider::Create(outputSurface.get()));
+ FakeRendererGL renderer(&mockClient, outputSurface.get(), resourceProvider.get());
+
+ mockClient.root_render_pass()->has_transparent_background = false;
+
+ EXPECT_TRUE(renderer.Initialize());
+
+ renderer.DrawFrame(mockClient.renderPassesInDrawOrder());
+
+ // On DEBUG builds, render passes with opaque background clear to blue to
+ // easily see regions that were not drawn on the screen.
+#ifdef NDEBUG
+ EXPECT_EQ(0, context->clearCount());
+#else
+ EXPECT_EQ(1, context->clearCount());
+#endif
+}
+
+TEST(GLRendererTest2, transparentBackground)
+{
+ FakeRendererClient mockClient;
+ scoped_ptr<OutputSurface> outputSurface(FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(new ClearCountingContext)));
+ ClearCountingContext* context = static_cast<ClearCountingContext*>(outputSurface->context3d());
+ scoped_ptr<ResourceProvider> resourceProvider(ResourceProvider::Create(outputSurface.get()));
+ FakeRendererGL renderer(&mockClient, outputSurface.get(), resourceProvider.get());
+
+ mockClient.root_render_pass()->has_transparent_background = true;
+
+ EXPECT_TRUE(renderer.Initialize());
+
+ renderer.DrawFrame(mockClient.renderPassesInDrawOrder());
+
+ EXPECT_EQ(1, context->clearCount());
+}
+
+class VisibilityChangeIsLastCallTrackingContext : public TestWebGraphicsContext3D {
+public:
+ VisibilityChangeIsLastCallTrackingContext()
+ : m_lastCallWasSetVisibility(0)
+ {
+ }
+
+ // WebGraphicsContext3D methods.
+ virtual void setVisibilityCHROMIUM(bool visible) {
+ if (!m_lastCallWasSetVisibility)
+ return;
+ DCHECK(*m_lastCallWasSetVisibility == false);
+ *m_lastCallWasSetVisibility = true;
+ }
+ virtual void flush() { if (m_lastCallWasSetVisibility) *m_lastCallWasSetVisibility = false; }
+ virtual void deleteTexture(WebGLId) { if (m_lastCallWasSetVisibility) *m_lastCallWasSetVisibility = false; }
+ virtual void deleteFramebuffer(WebGLId) { if (m_lastCallWasSetVisibility) *m_lastCallWasSetVisibility = false; }
+ virtual void deleteRenderbuffer(WebGLId) { if (m_lastCallWasSetVisibility) *m_lastCallWasSetVisibility = false; }
+
+ // This method would normally do a glSwapBuffers under the hood.
+ virtual WebString getString(WebKit::WGC3Denum name)
+ {
+ if (name == GL_EXTENSIONS)
+ return WebString("GL_CHROMIUM_set_visibility GL_CHROMIUM_gpu_memory_manager GL_CHROMIUM_discard_backbuffer");
+ return WebString();
+ }
+
+ // Methods added for test.
+ void setLastCallWasSetVisibilityPointer(bool* lastCallWasSetVisibility) { m_lastCallWasSetVisibility = lastCallWasSetVisibility; }
+
+private:
+ bool* m_lastCallWasSetVisibility;
+};
+
+TEST(GLRendererTest2, visibilityChangeIsLastCall)
+{
+ FakeRendererClient mockClient;
+ scoped_ptr<OutputSurface> outputSurface(FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(new VisibilityChangeIsLastCallTrackingContext)));
+ VisibilityChangeIsLastCallTrackingContext* context = static_cast<VisibilityChangeIsLastCallTrackingContext*>(outputSurface->context3d());
+ scoped_ptr<ResourceProvider> resourceProvider(ResourceProvider::Create(outputSurface.get()));
+ FakeRendererGL renderer(&mockClient, outputSurface.get(), resourceProvider.get());
+
+ EXPECT_TRUE(renderer.Initialize());
+
+ bool lastCallWasSetVisiblity = false;
+ // Ensure that the call to setVisibilityCHROMIUM is the last call issue to the GPU
+ // process, after glFlush is called, and after the RendererClient's enforceManagedMemoryPolicy
+ // is called. Plumb this tracking between both the RenderClient and the Context by giving
+ // them both a pointer to a variable on the stack.
+ context->setLastCallWasSetVisibilityPointer(&lastCallWasSetVisiblity);
+ mockClient.setLastCallWasSetVisibilityPointer(&lastCallWasSetVisiblity);
+ renderer.SetVisible(true);
+ renderer.DrawFrame(mockClient.renderPassesInDrawOrder());
+ renderer.SetVisible(false);
+ EXPECT_TRUE(lastCallWasSetVisiblity);
+}
+
+class TextureStateTrackingContext : public TestWebGraphicsContext3D {
+public:
+ TextureStateTrackingContext()
+ : m_activeTexture(GL_INVALID_ENUM)
+ {
+ }
+
+ virtual WebString getString(WGC3Denum name)
+ {
+ if (name == GL_EXTENSIONS)
+ return WebString("GL_OES_EGL_image_external");
+ return WebString();
+ }
+
+ MOCK_METHOD3(texParameteri, void(WGC3Denum target, WGC3Denum pname, WGC3Dint param));
+ MOCK_METHOD4(drawElements, void(WGC3Denum mode, WGC3Dsizei count, WGC3Denum type, WGC3Dintptr offset));
+
+ virtual void activeTexture(WGC3Denum texture)
+ {
+ EXPECT_NE(texture, m_activeTexture);
+ m_activeTexture = texture;
+ }
+
+ WGC3Denum activeTexture() const { return m_activeTexture; }
+
+private:
+ WGC3Denum m_activeTexture;
+};
+
+TEST(GLRendererTest2, activeTextureState)
+{
+ FakeRendererClient fakeClient;
+ scoped_ptr<OutputSurface> outputSurface(FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(new TextureStateTrackingContext)));
+ TextureStateTrackingContext* context = static_cast<TextureStateTrackingContext*>(outputSurface->context3d());
+ scoped_ptr<ResourceProvider> resourceProvider(ResourceProvider::Create(outputSurface.get()));
+ FakeRendererGL renderer(&fakeClient, outputSurface.get(), resourceProvider.get());
+
+ // During initialization we are allowed to set any texture parameters.
+ EXPECT_CALL(*context, texParameteri(_, _, _)).Times(AnyNumber());
+ EXPECT_TRUE(renderer.Initialize());
+
+ cc::RenderPass::Id id(1, 1);
+ scoped_ptr<TestRenderPass> pass = TestRenderPass::Create();
+ pass->SetNew(id, gfx::Rect(0, 0, 100, 100), gfx::Rect(0, 0, 100, 100), gfx::Transform());
+ pass->AppendOneOfEveryQuadType(resourceProvider.get(), RenderPass::Id(2, 1));
+
+ // Set up expected texture filter state transitions that match the quads
+ // created in AppendOneOfEveryQuadType().
+ Mock::VerifyAndClearExpectations(context);
+ {
+ InSequence sequence;
+
+ // yuv_quad is drawn with the default linear filter.
+ EXPECT_CALL(*context, drawElements(_, _, _, _));
+
+ // tile_quad is drawn with GL_NEAREST because it is not transformed or
+ // scaled.
+ EXPECT_CALL(*context, texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
+ EXPECT_CALL(*context, texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
+ EXPECT_CALL(*context, drawElements(_, _, _, _));
+
+ // transformed_tile_quad uses GL_LINEAR.
+ EXPECT_CALL(*context, drawElements(_, _, _, _));
+
+ // scaled_tile_quad also uses GL_LINEAR.
+ EXPECT_CALL(*context, drawElements(_, _, _, _));
+
+ // The remaining quads also use GL_LINEAR because nearest neighbor
+ // filtering is currently only used with tile quads.
+ EXPECT_CALL(*context, drawElements(_, _, _, _)).Times(6);
+ }
+
+ cc::DirectRenderer::DrawingFrame drawingFrame;
+ renderer.BeginDrawingFrame(drawingFrame);
+ EXPECT_EQ(context->activeTexture(), GL_TEXTURE0);
+
+ for (cc::QuadList::backToFrontIterator it = pass->quad_list.backToFrontBegin();
+ it != pass->quad_list.backToFrontEnd(); ++it) {
+ renderer.DoDrawQuad(drawingFrame, *it);
+ }
+ renderer.FinishDrawingQuadList();
+ EXPECT_EQ(context->activeTexture(), GL_TEXTURE0);
+ Mock::VerifyAndClearExpectations(context);
+}
+
+class NoClearRootRenderPassFakeClient : public FakeRendererClient {
+public:
+ virtual bool ShouldClearRootRenderPass() const OVERRIDE { return false; }
+};
+
+class NoClearRootRenderPassMockContext : public TestWebGraphicsContext3D {
+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, outputSurface.get(), resourceProvider.get());
+ EXPECT_TRUE(renderer.Initialize());
+
+ gfx::Rect viewportRect(mockClient.DeviceViewportSize());
+ ScopedPtrVector<RenderPass>& renderPasses = mockClient.renderPassesInDrawOrder();
+ renderPasses.clear();
+
+ 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);
+
+ // 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());
+
+ // In multiple render passes all but the root pass should clear the framebuffer.
+ Mock::VerifyAndClearExpectations(&mockContext);
+}
+
+class ScissorTestOnClearCheckingContext : public TestWebGraphicsContext3D {
+public:
+ ScissorTestOnClearCheckingContext() : m_scissorEnabled(false) { }
+
+ virtual void clear(WGC3Dbitfield)
+ {
+ EXPECT_FALSE(m_scissorEnabled);
+ }
+
+ virtual void enable(WGC3Denum cap)
+ {
+ if (cap == GL_SCISSOR_TEST)
+ m_scissorEnabled = true;
+ }
+
+ virtual void disable(WGC3Denum cap)
+ {
+ if (cap == GL_SCISSOR_TEST)
+ m_scissorEnabled = false;
+ }
+
+private:
+ bool m_scissorEnabled;
+};
+
+TEST(GLRendererTest2, scissorTestWhenClearing) {
+ FakeRendererClient mockClient;
+ scoped_ptr<OutputSurface> outputSurface(FakeOutputSurface::Create3d(scoped_ptr<WebKit::WebGraphicsContext3D>(new ScissorTestOnClearCheckingContext)));
+ scoped_ptr<ResourceProvider> resourceProvider(ResourceProvider::Create(outputSurface.get()));
+ FakeRendererGL renderer(&mockClient, outputSurface.get(), resourceProvider.get());
+ EXPECT_TRUE(renderer.Initialize());
+ EXPECT_FALSE(renderer.Capabilities().using_partial_swap);
+
+ gfx::Rect viewportRect(mockClient.DeviceViewportSize());
+ ScopedPtrVector<RenderPass>& renderPasses = mockClient.renderPassesInDrawOrder();
+ renderPasses.clear();
+
+ gfx::Rect grandChildRect(25, 25);
+ RenderPass::Id grandChildPassId(3, 0);
+ TestRenderPass* grandChildPass = addRenderPass(renderPasses, grandChildPassId, grandChildRect, gfx::Transform());
+ addClippedQuad(grandChildPass, grandChildRect, SK_ColorYELLOW);
+
+ gfx::Rect childRect(50, 50);
+ RenderPass::Id childPassId(2, 0);
+ TestRenderPass* childPass = addRenderPass(renderPasses, childPassId, childRect, gfx::Transform());
+ addQuad(childPass, childRect, SK_ColorBLUE);
+
+ RenderPass::Id rootPassId(1, 0);
+ TestRenderPass* rootPass = addRenderPass(renderPasses, rootPassId, viewportRect, gfx::Transform());
+ addQuad(rootPass, viewportRect, SK_ColorGREEN);
+
+ addRenderPassQuad(rootPass, childPass);
+ addRenderPassQuad(childPass, grandChildPass);
+
+ renderer.DecideRenderPassAllocationsForFrame(mockClient.renderPassesInDrawOrder());
+ renderer.DrawFrame(mockClient.renderPassesInDrawOrder());
+}
+
+class OutputSurfaceMockContext : public TestWebGraphicsContext3D {
+public:
+ // Specifically override methods even if they are unused (used in conjunction with StrictMock).
+ // We need to make sure that GLRenderer does not issue framebuffer-related GL calls directly. Instead these
+ // are supposed to go through the OutputSurface abstraction.
+ MOCK_METHOD0(ensureBackbufferCHROMIUM, void());
+ MOCK_METHOD0(discardBackbufferCHROMIUM, void());
+ MOCK_METHOD2(bindFramebuffer, void(WGC3Denum target, WebGLId framebuffer));
+ MOCK_METHOD0(prepareTexture, void());
+ MOCK_METHOD2(reshape, void(int width, int height));
+ MOCK_METHOD4(drawElements, void(WGC3Denum mode, WGC3Dsizei count, WGC3Denum type, WGC3Dintptr offset));
+
+ virtual WebString getString(WebKit::WGC3Denum name)
+ {
+ if (name == GL_EXTENSIONS)
+ return WebString("GL_CHROMIUM_post_sub_buffer GL_CHROMIUM_discard_backbuffer");
+ return WebString();
+ }
+};
+
+class MockOutputSurface : public OutputSurface {
+ public:
+ MockOutputSurface()
+ : OutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D>(new StrictMock<OutputSurfaceMockContext>)) { }
+ virtual ~MockOutputSurface() { }
+
+ MOCK_METHOD1(SendFrameToParentCompositor, void(CompositorFrame* frame));
+ MOCK_METHOD0(EnsureBackbuffer, void());
+ MOCK_METHOD0(DiscardBackbuffer, void());
+ MOCK_METHOD1(Reshape, void(gfx::Size size));
+ MOCK_METHOD0(BindFramebuffer, void());
+ MOCK_METHOD1(PostSubBuffer, void(gfx::Rect rect));
+ MOCK_METHOD0(SwapBuffers, void());
+};
+
+class MockOutputSurfaceTest : public testing::Test,
+ public FakeRendererClient {
+protected:
+ MockOutputSurfaceTest()
+ : resource_provider_(ResourceProvider::Create(&m_outputSurface))
+ , m_renderer(this, &m_outputSurface, resource_provider_.get())
+ {
+ }
+
+ virtual void SetUp()
+ {
+ EXPECT_TRUE(m_renderer.Initialize());
+ }
+
+ void SwapBuffers()
+ {
+ m_renderer.SwapBuffers();
+ }
+
+ void DrawFrame()
+ {
+ gfx::Rect viewportRect(DeviceViewportSize());
+ ScopedPtrVector<RenderPass>& renderPasses = renderPassesInDrawOrder();
+ renderPasses.clear();
+
+ RenderPass::Id renderPassId(1, 0);
+ TestRenderPass* renderPass = addRenderPass(renderPasses, renderPassId, viewportRect, gfx::Transform());
+ addQuad(renderPass, viewportRect, SK_ColorGREEN);
+
+ EXPECT_CALL(m_outputSurface, EnsureBackbuffer())
+ .WillRepeatedly(Return());
+
+ EXPECT_CALL(m_outputSurface, Reshape(_))
+ .Times(1);
+
+ EXPECT_CALL(m_outputSurface, BindFramebuffer())
+ .Times(1);
+
+ EXPECT_CALL(*context(), drawElements(_, _, _, _))
+ .Times(1);
+
+ m_renderer.DecideRenderPassAllocationsForFrame(renderPassesInDrawOrder());
+ m_renderer.DrawFrame(renderPassesInDrawOrder());
+ }
+
+ OutputSurfaceMockContext* context() { return static_cast<OutputSurfaceMockContext*>(m_outputSurface.context3d()); }
+
+ StrictMock<MockOutputSurface> m_outputSurface;
+ scoped_ptr<ResourceProvider> resource_provider_;
+ FakeRendererGL m_renderer;
+};
+
+TEST_F(MockOutputSurfaceTest, DrawFrameAndSwap)
+{
+ DrawFrame();
+
+ EXPECT_CALL(m_outputSurface, SwapBuffers())
+ .Times(1);
+ m_renderer.SwapBuffers();
+}
+
+class MockOutputSurfaceTestWithPartialSwap : public MockOutputSurfaceTest {
+public:
+ virtual const LayerTreeSettings& Settings() const OVERRIDE
+ {
+ static LayerTreeSettings fakeSettings;
+ fakeSettings.partialSwapEnabled = true;
+ return fakeSettings;
+ }
+};
+
+TEST_F(MockOutputSurfaceTestWithPartialSwap, DrawFrameAndSwap)
+{
+ DrawFrame();
+
+ EXPECT_CALL(m_outputSurface, PostSubBuffer(_))
+ .Times(1);
+ m_renderer.SwapBuffers();
+}
+
+class MockOutputSurfaceTestWithSendCompositorFrame : public MockOutputSurfaceTest {
+public:
+ virtual const LayerTreeSettings& Settings() const OVERRIDE
+ {
+ static LayerTreeSettings fakeSettings;
+ fakeSettings.compositorFrameMessage = true;
+ return fakeSettings;
+ }
+};
+
+TEST_F(MockOutputSurfaceTestWithSendCompositorFrame, DrawFrame)
+{
+ EXPECT_CALL(m_outputSurface, SendFrameToParentCompositor(_))
+ .Times(1);
+ DrawFrame();
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/output/output_surface.cc b/cc/output/output_surface.cc
new file mode 100644
index 0000000..34ba8d0
--- /dev/null
+++ b/cc/output/output_surface.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 2013 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/output/output_surface.h"
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/strings/string_split.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "third_party/khronos/GLES2/gl2ext.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+
+using std::set;
+using std::string;
+using std::vector;
+
+namespace cc {
+
+OutputSurface::OutputSurface(
+ scoped_ptr<WebKit::WebGraphicsContext3D> context3d)
+ : client_(NULL),
+ context3d_(context3d.Pass()),
+ has_gl_discard_backbuffer_(false) {
+}
+
+OutputSurface::OutputSurface(
+ scoped_ptr<cc::SoftwareOutputDevice> software_device)
+ : client_(NULL),
+ software_device_(software_device.Pass()),
+ has_gl_discard_backbuffer_(false) {
+}
+
+OutputSurface::OutputSurface(
+ scoped_ptr<WebKit::WebGraphicsContext3D> context3d,
+ scoped_ptr<cc::SoftwareOutputDevice> software_device)
+ : client_(NULL),
+ context3d_(context3d.Pass()),
+ software_device_(software_device.Pass()),
+ has_gl_discard_backbuffer_(false) {
+}
+
+OutputSurface::~OutputSurface() {
+}
+
+bool OutputSurface::BindToClient(
+ cc::OutputSurfaceClient* client) {
+ DCHECK(client);
+ client_ = client;
+ if (!context3d_)
+ return true;
+ if (!context3d_->makeContextCurrent())
+ return false;
+
+ string extensionsString = UTF16ToASCII(context3d_->getString(GL_EXTENSIONS));
+ vector<string> extensionsList;
+ base::SplitString(extensionsString, ' ', &extensionsList);
+ set<string> extensions(extensionsList.begin(), extensionsList.end());
+
+ has_gl_discard_backbuffer_ =
+ extensions.count("GL_CHROMIUM_discard_backbuffer");
+
+ return true;
+}
+
+void OutputSurface::SendFrameToParentCompositor(CompositorFrame*) {
+ NOTIMPLEMENTED();
+}
+
+void OutputSurface::EnsureBackbuffer() {
+ DCHECK(context3d_);
+ if (has_gl_discard_backbuffer_)
+ context3d_->ensureBackbufferCHROMIUM();
+}
+
+void OutputSurface::DiscardBackbuffer() {
+ DCHECK(context3d_);
+ if (has_gl_discard_backbuffer_)
+ context3d_->discardBackbufferCHROMIUM();
+}
+
+void OutputSurface::Reshape(gfx::Size size) {
+ DCHECK(context3d_);
+ context3d_->reshape(size.width(), size.height());
+}
+
+void OutputSurface::BindFramebuffer() {
+ DCHECK(context3d_);
+ context3d_->bindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+void OutputSurface::SwapBuffers() {
+ DCHECK(context3d_);
+ // Note that currently this has the same effect as swapBuffers; we should
+ // consider exposing a different entry point on WebGraphicsContext3D.
+ context3d_->prepareTexture();
+}
+
+void OutputSurface::PostSubBuffer(gfx::Rect rect) {
+ DCHECK(context3d_);
+ context3d_->postSubBufferCHROMIUM(
+ rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+} // namespace cc
diff --git a/cc/output/output_surface.h b/cc/output/output_surface.h
new file mode 100644
index 0000000..ce425ed
--- /dev/null
+++ b/cc/output/output_surface.h
@@ -0,0 +1,107 @@
+// 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_OUTPUT_OUTPUT_SURFACE_H_
+#define CC_OUTPUT_OUTPUT_SURFACE_H_
+
+#define USE_CC_OUTPUT_SURFACE // TODO(danakj): Remove this.
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/output/software_output_device.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h"
+
+namespace gfx {
+class Rect;
+class Size;
+}
+
+namespace cc {
+
+class CompositorFrame;
+class OutputSurfaceClient;
+
+// Represents the output surface for a compositor. The compositor owns
+// and manages its destruction. Its lifetime is:
+// 1. Created on the main thread by the LayerTreeHost through its client.
+// 2. Passed to the compositor thread and bound to a client via BindToClient.
+// From here on, it will only be used on the compositor thread.
+// 3. If the 3D context is lost, then the compositor will delete the output
+// surface (on the compositor thread) and go back to step 1.
+class CC_EXPORT OutputSurface {
+ public:
+ OutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D> context3d);
+
+ OutputSurface(scoped_ptr<cc::SoftwareOutputDevice> software_device);
+
+ OutputSurface(scoped_ptr<WebKit::WebGraphicsContext3D> context3d,
+ scoped_ptr<cc::SoftwareOutputDevice> software_device);
+
+ virtual ~OutputSurface();
+
+ struct Capabilities {
+ Capabilities()
+ : has_parent_compositor(false),
+ max_frames_pending(0) {}
+
+ bool has_parent_compositor;
+ int max_frames_pending;
+ };
+
+ const Capabilities& capabilities() const {
+ return capabilities_;
+ }
+
+ // Obtain the 3d context or the software device associated with this output
+ // surface. Either of these may return a null pointer, but not both.
+ // In the event of a lost context, the entire output surface should be
+ // recreated.
+ WebKit::WebGraphicsContext3D* context3d() const {
+ return context3d_.get();
+ }
+
+ SoftwareOutputDevice* software_device() const {
+ return software_device_.get();
+ }
+
+ // Called by the compositor on the compositor thread. This is a place where
+ // thread-specific data for the output surface can be initialized, since from
+ // this point on the output surface will only be used on the compositor
+ // thread.
+ virtual bool BindToClient(OutputSurfaceClient*);
+
+ // Sends frame data to the parent compositor. This should only be called when
+ // capabilities().has_parent_compositor. The implementation may destroy or
+ // steal the contents of the CompositorFrame passed in.
+ virtual void SendFrameToParentCompositor(CompositorFrame*);
+
+ virtual void EnsureBackbuffer();
+ virtual void DiscardBackbuffer();
+
+ virtual void Reshape(gfx::Size size);
+
+ virtual void BindFramebuffer();
+
+ virtual void PostSubBuffer(gfx::Rect rect);
+ virtual void SwapBuffers();
+
+ // Notifies frame-rate smoothness preference. If true, all non-critical
+ // processing should be stopped, or lowered in priority.
+ virtual void UpdateSmoothnessTakesPriority(bool prefer_smoothness) {}
+
+ protected:
+ OutputSurfaceClient* client_;
+ struct cc::OutputSurface::Capabilities capabilities_;
+ scoped_ptr<WebKit::WebGraphicsContext3D> context3d_;
+ scoped_ptr<cc::SoftwareOutputDevice> software_device_;
+ bool has_gl_discard_backbuffer_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OutputSurface);
+};
+
+} // namespace cc
+
+#endif // CC_OUTPUT_OUTPUT_SURFACE_H_
diff --git a/cc/output/output_surface_client.h b/cc/output/output_surface_client.h
new file mode 100644
index 0000000..b8d0ad7
--- /dev/null
+++ b/cc/output/output_surface_client.h
@@ -0,0 +1,27 @@
+// 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_OUTPUT_OUTPUT_SURFACE_CLIENT_H_
+#define CC_OUTPUT_OUTPUT_SURFACE_CLIENT_H_
+
+#include "base/time.h"
+#include "cc/base/cc_export.h"
+
+namespace cc {
+
+class CompositorFrameAck;
+
+class CC_EXPORT OutputSurfaceClient {
+ public:
+ virtual void OnVSyncParametersChanged(base::TimeTicks timebase,
+ base::TimeDelta interval) = 0;
+ virtual void OnSendFrameToParentCompositorAck(const CompositorFrameAck&) = 0;
+
+ protected:
+ virtual ~OutputSurfaceClient() {}
+};
+
+} // namespace cc
+
+#endif // CC_OUTPUT_OUTPUT_SURFACE_CLIENT_H_
diff --git a/cc/output/program_binding.cc b/cc/output/program_binding.cc
new file mode 100644
index 0000000..7793d36
--- /dev/null
+++ b/cc/output/program_binding.cc
@@ -0,0 +1,145 @@
+// Copyright 2011 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/output/program_binding.h"
+
+#include "base/debug/trace_event.h"
+#include "cc/output/geometry_binding.h"
+#include "cc/output/gl_renderer.h" // For the GLC() macro.
+#include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h"
+#include "third_party/khronos/GLES2/gl2.h"
+
+using WebKit::WebGraphicsContext3D;
+
+namespace cc {
+
+ProgramBindingBase::ProgramBindingBase()
+ : program_(0),
+ vertex_shader_id_(0),
+ fragment_shader_id_(0),
+ initialized_(false) {}
+
+ProgramBindingBase::~ProgramBindingBase() {
+ // If you hit these asserts, you initialized but forgot to call Cleanup().
+ DCHECK(!program_);
+ DCHECK(!vertex_shader_id_);
+ DCHECK(!fragment_shader_id_);
+ DCHECK(!initialized_);
+}
+
+void ProgramBindingBase::Init(WebGraphicsContext3D* context,
+ const std::string& vertex_shader,
+ const std::string& fragment_shader) {
+ TRACE_EVENT0("cc", "ProgramBindingBase::init");
+ vertex_shader_id_ = LoadShader(context, GL_VERTEX_SHADER, vertex_shader);
+ if (!vertex_shader_id_) {
+ if (!IsContextLost(context))
+ LOG(ERROR) << "Failed to create vertex shader";
+ return;
+ }
+
+ fragment_shader_id_ =
+ LoadShader(context, GL_FRAGMENT_SHADER, fragment_shader);
+ if (!fragment_shader_id_) {
+ GLC(context, context->deleteShader(vertex_shader_id_));
+ vertex_shader_id_ = 0;
+ if (!IsContextLost(context))
+ LOG(ERROR) << "Failed to create fragment shader";
+ return;
+ }
+
+ program_ =
+ CreateShaderProgram(context, vertex_shader_id_, fragment_shader_id_);
+ DCHECK(program_ || IsContextLost(context));
+}
+
+void ProgramBindingBase::Link(WebGraphicsContext3D* context) {
+ GLC(context, context->linkProgram(program_));
+ CleanupShaders(context);
+ if (!program_)
+ return;
+#ifndef NDEBUG
+ int linked = 0;
+ GLC(context, context->getProgramiv(program_, GL_LINK_STATUS, &linked));
+ if (!linked) {
+ if (!IsContextLost(context))
+ LOG(ERROR) << "Failed to link shader program";
+ GLC(context, context->deleteProgram(program_));
+ }
+#endif
+}
+
+void ProgramBindingBase::Cleanup(WebGraphicsContext3D* context) {
+ initialized_ = false;
+ if (!program_)
+ return;
+
+ DCHECK(context);
+ GLC(context, context->deleteProgram(program_));
+ program_ = 0;
+
+ CleanupShaders(context);
+}
+
+unsigned ProgramBindingBase::LoadShader(WebGraphicsContext3D* context,
+ unsigned type,
+ const std::string& shader_source) {
+ unsigned shader = context->createShader(type);
+ if (!shader)
+ return 0;
+ GLC(context, context->shaderSource(shader, shader_source.data()));
+ GLC(context, context->compileShader(shader));
+#ifndef NDEBUG
+ int compiled = 0;
+ GLC(context, context->getShaderiv(shader, GL_COMPILE_STATUS, &compiled));
+ if (!compiled) {
+ GLC(context, context->deleteShader(shader));
+ return 0;
+ }
+#endif
+ return shader;
+}
+
+unsigned ProgramBindingBase::CreateShaderProgram(WebGraphicsContext3D* context,
+ unsigned vertex_shader,
+ unsigned fragment_shader) {
+ unsigned program_object = context->createProgram();
+ if (!program_object) {
+ if (!IsContextLost(context))
+ LOG(ERROR) << "Failed to create shader program";
+ return 0;
+ }
+
+ GLC(context, context->attachShader(program_object, vertex_shader));
+ GLC(context, context->attachShader(program_object, fragment_shader));
+
+ // Bind the common attrib locations.
+ GLC(context,
+ context->bindAttribLocation(program_object,
+ GeometryBinding::PositionAttribLocation(),
+ "a_position"));
+ GLC(context,
+ context->bindAttribLocation(program_object,
+ GeometryBinding::TexCoordAttribLocation(),
+ "a_texCoord"));
+
+ return program_object;
+}
+
+void ProgramBindingBase::CleanupShaders(WebGraphicsContext3D* context) {
+ if (vertex_shader_id_) {
+ GLC(context, context->deleteShader(vertex_shader_id_));
+ vertex_shader_id_ = 0;
+ }
+ if (fragment_shader_id_) {
+ GLC(context, context->deleteShader(fragment_shader_id_));
+ fragment_shader_id_ = 0;
+ }
+}
+
+bool ProgramBindingBase::IsContextLost(WebGraphicsContext3D* context) {
+ return (context->getGraphicsResetStatusARB() != GL_NO_ERROR);
+}
+
+} // namespace cc
diff --git a/cc/output/program_binding.h b/cc/output/program_binding.h
new file mode 100644
index 0000000..77396cc
--- /dev/null
+++ b/cc/output/program_binding.h
@@ -0,0 +1,90 @@
+// Copyright 2011 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_OUTPUT_PROGRAM_BINDING_H_
+#define CC_OUTPUT_PROGRAM_BINDING_H_
+
+#include <string>
+
+#include "base/logging.h"
+
+namespace WebKit { class WebGraphicsContext3D; }
+
+namespace cc {
+
+class ProgramBindingBase {
+ public:
+ ProgramBindingBase();
+ ~ProgramBindingBase();
+
+ void Init(WebKit::WebGraphicsContext3D* context,
+ const std::string& vertex_shader,
+ const std::string& fragment_shader);
+ void Link(WebKit::WebGraphicsContext3D* context);
+ void Cleanup(WebKit::WebGraphicsContext3D* context);
+
+ unsigned program() const { return program_; }
+ bool initialized() const { return initialized_; }
+
+ protected:
+ unsigned LoadShader(WebKit::WebGraphicsContext3D* context,
+ unsigned type,
+ const std::string& shader_source);
+ unsigned CreateShaderProgram(WebKit::WebGraphicsContext3D* context,
+ unsigned vertex_shader,
+ unsigned fragment_shader);
+ void CleanupShaders(WebKit::WebGraphicsContext3D* context);
+ bool IsContextLost(WebKit::WebGraphicsContext3D* context);
+
+ unsigned program_;
+ unsigned vertex_shader_id_;
+ unsigned fragment_shader_id_;
+ bool initialized_;
+};
+
+template <class VertexShader, class FragmentShader>
+class ProgramBinding : public ProgramBindingBase {
+ public:
+ explicit ProgramBinding(WebKit::WebGraphicsContext3D* context) {
+ ProgramBindingBase::Init(context,
+ vertex_shader_.getShaderString(),
+ fragment_shader_.getShaderString());
+ }
+
+ void Initialize(WebKit::WebGraphicsContext3D* context,
+ bool using_bind_uniform) {
+ DCHECK(context);
+ DCHECK(!initialized_);
+
+ if (IsContextLost(context))
+ return;
+
+ // Need to bind uniforms before linking
+ if (!using_bind_uniform)
+ Link(context);
+
+ int base_uniform_index = 0;
+ vertex_shader_.init(
+ context, program_, using_bind_uniform, &base_uniform_index);
+ fragment_shader_.init(
+ context, program_, using_bind_uniform, &base_uniform_index);
+
+ // Link after binding uniforms
+ if (using_bind_uniform)
+ Link(context);
+
+ initialized_ = true;
+ }
+
+ const VertexShader& vertex_shader() const { return vertex_shader_; }
+ const FragmentShader& fragment_shader() const { return fragment_shader_; }
+
+ private:
+ VertexShader vertex_shader_;
+ FragmentShader fragment_shader_;
+};
+
+} // namespace cc
+
+#endif // CC_OUTPUT_PROGRAM_BINDING_H_
diff --git a/cc/output/render_surface_filters.cc b/cc/output/render_surface_filters.cc
new file mode 100644
index 0000000..dd5e1a1
--- /dev/null
+++ b/cc/output/render_surface_filters.cc
@@ -0,0 +1,461 @@
+// 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/output/render_surface_filters.h"
+
+#include "base/logging.h"
+#include "skia/ext/refptr.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebFilterOperation.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebFilterOperations.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/effects/SkBlurImageFilter.h"
+#include "third_party/skia/include/effects/SkColorMatrixFilter.h"
+#include "third_party/skia/include/effects/SkMagnifierImageFilter.h"
+#include "third_party/skia/include/gpu/SkGpuDevice.h"
+#include "third_party/skia/include/gpu/SkGrPixelRef.h"
+#include "ui/gfx/size_f.h"
+
+namespace cc {
+
+namespace {
+
+void GetBrightnessMatrix(float amount, SkScalar matrix[20]) {
+ // Spec implementation
+ // (http://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/index.html#brightnessEquivalent)
+ // <feFunc[R|G|B] type="linear" slope="[amount]">
+ memset(matrix, 0, 20 * sizeof(SkScalar));
+ matrix[0] = matrix[6] = matrix[12] = amount;
+ matrix[18] = 1.f;
+}
+
+void GetSaturatingBrightnessMatrix(float amount, SkScalar matrix[20]) {
+ // Legacy implementation used by internal clients.
+ // <feFunc[R|G|B] type="linear" intercept="[amount]"/>
+ memset(matrix, 0, 20 * sizeof(SkScalar));
+ matrix[0] = matrix[6] = matrix[12] = matrix[18] = 1.f;
+ matrix[4] = matrix[9] = matrix[14] = amount * 255.f;
+}
+
+void GetContrastMatrix(float amount, SkScalar matrix[20]) {
+ memset(matrix, 0, 20 * sizeof(SkScalar));
+ matrix[0] = matrix[6] = matrix[12] = amount;
+ matrix[4] = matrix[9] = matrix[14] = (-0.5f * amount + 0.5f) * 255.f;
+ matrix[18] = 1.f;
+}
+
+void GetSaturateMatrix(float amount, SkScalar matrix[20]) {
+ // Note, these values are computed to ensure matrixNeedsClamping is false
+ // for amount in [0..1]
+ matrix[0] = 0.213f + 0.787f * amount;
+ matrix[1] = 0.715f - 0.715f * amount;
+ matrix[2] = 1.f - (matrix[0] + matrix[1]);
+ matrix[3] = matrix[4] = 0.f;
+ matrix[5] = 0.213f - 0.213f * amount;
+ matrix[6] = 0.715f + 0.285f * amount;
+ matrix[7] = 1.f - (matrix[5] + matrix[6]);
+ matrix[8] = matrix[9] = 0.f;
+ matrix[10] = 0.213f - 0.213f * amount;
+ matrix[11] = 0.715f - 0.715f * amount;
+ matrix[12] = 1.f - (matrix[10] + matrix[11]);
+ matrix[13] = matrix[14] = 0.f;
+ matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0.f;
+ matrix[18] = 1.f;
+}
+
+void GetHueRotateMatrix(float hue, SkScalar matrix[20]) {
+ const float kPi = 3.1415926535897932384626433832795f;
+
+ float cos_hue = cosf(hue * kPi / 180.f);
+ float sin_hue = sinf(hue * kPi / 180.f);
+ matrix[0] = 0.213f + cos_hue * 0.787f - sin_hue * 0.213f;
+ matrix[1] = 0.715f - cos_hue * 0.715f - sin_hue * 0.715f;
+ matrix[2] = 0.072f - cos_hue * 0.072f + sin_hue * 0.928f;
+ matrix[3] = matrix[4] = 0.f;
+ matrix[5] = 0.213f - cos_hue * 0.213f + sin_hue * 0.143f;
+ matrix[6] = 0.715f + cos_hue * 0.285f + sin_hue * 0.140f;
+ matrix[7] = 0.072f - cos_hue * 0.072f - sin_hue * 0.283f;
+ matrix[8] = matrix[9] = 0.f;
+ matrix[10] = 0.213f - cos_hue * 0.213f - sin_hue * 0.787f;
+ matrix[11] = 0.715f - cos_hue * 0.715f + sin_hue * 0.715f;
+ matrix[12] = 0.072f + cos_hue * 0.928f + sin_hue * 0.072f;
+ matrix[13] = matrix[14] = 0.f;
+ matrix[15] = matrix[16] = matrix[17] = 0.f;
+ matrix[18] = 1.f;
+ matrix[19] = 0.f;
+}
+
+void GetInvertMatrix(float amount, SkScalar matrix[20]) {
+ memset(matrix, 0, 20 * sizeof(SkScalar));
+ matrix[0] = matrix[6] = matrix[12] = 1.f - 2.f * amount;
+ matrix[4] = matrix[9] = matrix[14] = amount * 255.f;
+ matrix[18] = 1.f;
+}
+
+void GetOpacityMatrix(float amount, SkScalar matrix[20]) {
+ memset(matrix, 0, 20 * sizeof(SkScalar));
+ matrix[0] = matrix[6] = matrix[12] = 1.f;
+ matrix[18] = amount;
+}
+
+void GetGrayscaleMatrix(float amount, SkScalar matrix[20]) {
+ // Note, these values are computed to ensure matrixNeedsClamping is false
+ // for amount in [0..1]
+ matrix[0] = 0.2126f + 0.7874f * amount;
+ matrix[1] = 0.7152f - 0.7152f * amount;
+ matrix[2] = 1.f - (matrix[0] + matrix[1]);
+ matrix[3] = matrix[4] = 0.f;
+
+ matrix[5] = 0.2126f - 0.2126f * amount;
+ matrix[6] = 0.7152f + 0.2848f * amount;
+ matrix[7] = 1.f - (matrix[5] + matrix[6]);
+ matrix[8] = matrix[9] = 0.f;
+
+ matrix[10] = 0.2126f - 0.2126f * amount;
+ matrix[11] = 0.7152f - 0.7152f * amount;
+ matrix[12] = 1.f - (matrix[10] + matrix[11]);
+ matrix[13] = matrix[14] = 0.f;
+
+ matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0.f;
+ matrix[18] = 1.f;
+}
+
+void GetSepiaMatrix(float amount, SkScalar matrix[20]) {
+ matrix[0] = 0.393f + 0.607f * amount;
+ matrix[1] = 0.769f - 0.769f * amount;
+ matrix[2] = 0.189f - 0.189f * amount;
+ matrix[3] = matrix[4] = 0.f;
+
+ matrix[5] = 0.349f - 0.349f * amount;
+ matrix[6] = 0.686f + 0.314f * amount;
+ matrix[7] = 0.168f - 0.168f * amount;
+ matrix[8] = matrix[9] = 0.f;
+
+ matrix[10] = 0.272f - 0.272f * amount;
+ matrix[11] = 0.534f - 0.534f * amount;
+ matrix[12] = 0.131f + 0.869f * amount;
+ matrix[13] = matrix[14] = 0.f;
+
+ matrix[15] = matrix[16] = matrix[17] = matrix[19] = 0.f;
+ matrix[18] = 1.f;
+}
+
+// The 5x4 matrix is really a "compressed" version of a 5x5 matrix that'd have
+// (0 0 0 0 1) as a last row, and that would be applied to a 5-vector extended
+// from the 4-vector color with a 1.
+void MultColorMatrix(SkScalar a[20], SkScalar b[20], SkScalar out[20]) {
+ for (int j = 0; j < 4; ++j) {
+ for (int i = 0; i < 5; ++i) {
+ out[i+j*5] = i == 4 ? a[4+j*5] : 0.f;
+ for (int k = 0; k < 4; ++k)
+ out[i+j*5] += a[k+j*5] * b[i+k*5];
+ }
+ }
+}
+
+// To detect if we need to apply clamping after applying a matrix, we check if
+// any output component might go outside of [0, 255] for any combination of
+// input components in [0..255].
+// Each output component is an affine transformation of the input component, so
+// the minimum and maximum values are for any combination of minimum or maximum
+// values of input components (i.e. 0 or 255).
+// E.g. if R' = x*R + y*G + z*B + w*A + t
+// Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the
+// minimum value will be for R=0 if x>0 or R=255 if x<0.
+// Same goes for all components.
+bool ComponentNeedsClamping(SkScalar row[5]) {
+ SkScalar max_value = row[4] / 255.f;
+ SkScalar min_value = row[4] / 255.f;
+ for (int i = 0; i < 4; ++i) {
+ if (row[i] > 0)
+ max_value += row[i];
+ else
+ min_value += row[i];
+ }
+ return (max_value > 1.f) || (min_value < 0.f);
+}
+
+bool MatrixNeedsClamping(SkScalar matrix[20]) {
+ return ComponentNeedsClamping(matrix)
+ || ComponentNeedsClamping(matrix+5)
+ || ComponentNeedsClamping(matrix+10)
+ || ComponentNeedsClamping(matrix+15);
+}
+
+bool GetColorMatrix(const WebKit::WebFilterOperation& op, SkScalar matrix[20]) {
+ switch (op.type()) {
+ case WebKit::WebFilterOperation::FilterTypeBrightness: {
+ GetBrightnessMatrix(op.amount(), matrix);
+ return true;
+ }
+ case WebKit::WebFilterOperation::FilterTypeSaturatingBrightness: {
+ GetSaturatingBrightnessMatrix(op.amount(), matrix);
+ return true;
+ }
+ case WebKit::WebFilterOperation::FilterTypeContrast: {
+ GetContrastMatrix(op.amount(), matrix);
+ return true;
+ }
+ case WebKit::WebFilterOperation::FilterTypeGrayscale: {
+ GetGrayscaleMatrix(1.f - op.amount(), matrix);
+ return true;
+ }
+ case WebKit::WebFilterOperation::FilterTypeSepia: {
+ GetSepiaMatrix(1.f - op.amount(), matrix);
+ return true;
+ }
+ case WebKit::WebFilterOperation::FilterTypeSaturate: {
+ GetSaturateMatrix(op.amount(), matrix);
+ return true;
+ }
+ case WebKit::WebFilterOperation::FilterTypeHueRotate: {
+ GetHueRotateMatrix(op.amount(), matrix);
+ return true;
+ }
+ case WebKit::WebFilterOperation::FilterTypeInvert: {
+ GetInvertMatrix(op.amount(), matrix);
+ return true;
+ }
+ case WebKit::WebFilterOperation::FilterTypeOpacity: {
+ GetOpacityMatrix(op.amount(), matrix);
+ return true;
+ }
+ case WebKit::WebFilterOperation::FilterTypeColorMatrix: {
+ memcpy(matrix, op.matrix(), sizeof(SkScalar[20]));
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
+class FilterBufferState {
+ public:
+ FilterBufferState(GrContext* gr_context,
+ gfx::SizeF size,
+ unsigned texture_id)
+ : gr_context_(gr_context),
+ current_texture_(0) {
+ // Wrap the source texture in a Ganesh platform texture.
+ GrBackendTextureDesc backend_texture_description;
+ backend_texture_description.fWidth = size.width();
+ backend_texture_description.fHeight = size.height();
+ backend_texture_description.fConfig = kSkia8888_GrPixelConfig;
+ backend_texture_description.fTextureHandle = texture_id;
+ skia::RefPtr<GrTexture> texture = skia::AdoptRef(
+ gr_context->wrapBackendTexture(backend_texture_description));
+ // Place the platform texture inside an SkBitmap.
+ source_.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height());
+ skia::RefPtr<SkGrPixelRef> pixel_ref =
+ skia::AdoptRef(new SkGrPixelRef(texture.get()));
+ source_.setPixelRef(pixel_ref.get());
+ }
+
+ ~FilterBufferState() {}
+
+ bool Init(int filter_count) {
+ int scratch_count = std::min(2, filter_count);
+ GrTextureDesc desc;
+ desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+ desc.fSampleCnt = 0;
+ desc.fWidth = source_.width();
+ desc.fHeight = source_.height();
+ desc.fConfig = kSkia8888_GrPixelConfig;
+ for (int i = 0; i < scratch_count; ++i) {
+ GrAutoScratchTexture scratch_texture(
+ gr_context_, desc, GrContext::kExact_ScratchTexMatch);
+ scratch_textures_[i] = skia::AdoptRef(scratch_texture.detach());
+ if (!scratch_textures_[i])
+ return false;
+ }
+ return true;
+ }
+
+ SkCanvas* Canvas() {
+ if (!canvas_.get())
+ CreateCanvas();
+ return canvas_.get();
+ }
+
+ const SkBitmap& Source() { return source_; }
+
+ void Swap() {
+ canvas_->flush();
+ canvas_.clear();
+ device_.clear();
+
+ skia::RefPtr<SkGrPixelRef> pixel_ref = skia::AdoptRef(
+ new SkGrPixelRef(scratch_textures_[current_texture_].get()));
+ source_.setPixelRef(pixel_ref.get());
+ current_texture_ = 1 - current_texture_;
+ }
+
+ private:
+ void CreateCanvas() {
+ DCHECK(scratch_textures_[current_texture_].get());
+ device_ = skia::AdoptRef(new SkGpuDevice(
+ gr_context_, scratch_textures_[current_texture_].get()));
+ canvas_ = skia::AdoptRef(new SkCanvas(device_.get()));
+ canvas_->clear(0x0);
+ }
+
+ GrContext* gr_context_;
+ SkBitmap source_;
+ skia::RefPtr<GrTexture> scratch_textures_[2];
+ int current_texture_;
+ skia::RefPtr<SkGpuDevice> device_;
+ skia::RefPtr<SkCanvas> canvas_;
+};
+
+} // namespace
+
+WebKit::WebFilterOperations RenderSurfaceFilters::Optimize(
+ const WebKit::WebFilterOperations& filters) {
+ WebKit::WebFilterOperations new_list;
+
+ SkScalar accumulated_color_matrix[20];
+ bool have_accumulated_color_matrix = false;
+ for (unsigned i = 0; i < filters.size(); ++i) {
+ const WebKit::WebFilterOperation& op = filters.at(i);
+
+ // If the filter is a color matrix, we may be able to combine it with
+ // following Filter(s) that also are color matrices.
+ SkScalar matrix[20];
+ if (GetColorMatrix(op, matrix)) {
+ if (have_accumulated_color_matrix) {
+ SkScalar newMatrix[20];
+ MultColorMatrix(matrix, accumulated_color_matrix, newMatrix);
+ memcpy(accumulated_color_matrix,
+ newMatrix,
+ sizeof(accumulated_color_matrix));
+ } else {
+ memcpy(accumulated_color_matrix,
+ matrix,
+ sizeof(accumulated_color_matrix));
+ have_accumulated_color_matrix = true;
+ }
+
+ // We can only combine matrices if clamping of color components
+ // would have no effect.
+ if (!MatrixNeedsClamping(accumulated_color_matrix))
+ continue;
+ }
+
+ if (have_accumulated_color_matrix) {
+ new_list.append(WebKit::WebFilterOperation::createColorMatrixFilter(
+ accumulated_color_matrix));
+ }
+ have_accumulated_color_matrix = false;
+
+ switch (op.type()) {
+ case WebKit::WebFilterOperation::FilterTypeBlur:
+ case WebKit::WebFilterOperation::FilterTypeDropShadow:
+ case WebKit::WebFilterOperation::FilterTypeZoom:
+ new_list.append(op);
+ break;
+ case WebKit::WebFilterOperation::FilterTypeBrightness:
+ case WebKit::WebFilterOperation::FilterTypeSaturatingBrightness:
+ case WebKit::WebFilterOperation::FilterTypeContrast:
+ case WebKit::WebFilterOperation::FilterTypeGrayscale:
+ case WebKit::WebFilterOperation::FilterTypeSepia:
+ case WebKit::WebFilterOperation::FilterTypeSaturate:
+ case WebKit::WebFilterOperation::FilterTypeHueRotate:
+ case WebKit::WebFilterOperation::FilterTypeInvert:
+ case WebKit::WebFilterOperation::FilterTypeOpacity:
+ case WebKit::WebFilterOperation::FilterTypeColorMatrix:
+ break;
+ }
+ }
+ if (have_accumulated_color_matrix) {
+ new_list.append(WebKit::WebFilterOperation::createColorMatrixFilter(
+ accumulated_color_matrix));
+ }
+ return new_list;
+}
+
+SkBitmap RenderSurfaceFilters::Apply(const WebKit::WebFilterOperations& filters,
+ unsigned texture_id,
+ gfx::SizeF size,
+ GrContext* gr_context) {
+ DCHECK(gr_context);
+
+ WebKit::WebFilterOperations optimized_filters = Optimize(filters);
+ FilterBufferState state(gr_context, size, texture_id);
+ if (!state.Init(optimized_filters.size()))
+ return SkBitmap();
+
+ for (unsigned i = 0; i < optimized_filters.size(); ++i) {
+ const WebKit::WebFilterOperation& op = optimized_filters.at(i);
+ SkCanvas* canvas = state.Canvas();
+ switch (op.type()) {
+ case WebKit::WebFilterOperation::FilterTypeColorMatrix: {
+ SkPaint paint;
+ skia::RefPtr<SkColorMatrixFilter> filter =
+ skia::AdoptRef(new SkColorMatrixFilter(op.matrix()));
+ paint.setColorFilter(filter.get());
+ canvas->drawBitmap(state.Source(), 0, 0, &paint);
+ break;
+ }
+ case WebKit::WebFilterOperation::FilterTypeBlur: {
+ float std_deviation = op.amount();
+ skia::RefPtr<SkImageFilter> filter =
+ skia::AdoptRef(new SkBlurImageFilter(std_deviation, std_deviation));
+ SkPaint paint;
+ paint.setImageFilter(filter.get());
+ canvas->drawSprite(state.Source(), 0, 0, &paint);
+ break;
+ }
+ case WebKit::WebFilterOperation::FilterTypeDropShadow: {
+ skia::RefPtr<SkImageFilter> blur_filter =
+ skia::AdoptRef(new SkBlurImageFilter(op.amount(), op.amount()));
+ skia::RefPtr<SkColorFilter> color_filter =
+ skia::AdoptRef(SkColorFilter::CreateModeFilter(
+ op.dropShadowColor(), SkXfermode::kSrcIn_Mode));
+ SkPaint paint;
+ paint.setImageFilter(blur_filter.get());
+ paint.setColorFilter(color_filter.get());
+ paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
+ canvas->saveLayer(NULL, &paint);
+ canvas->drawBitmap(state.Source(),
+ op.dropShadowOffset().x,
+ -op.dropShadowOffset().y);
+ canvas->restore();
+ canvas->drawBitmap(state.Source(), 0, 0);
+ break;
+ }
+ case WebKit::WebFilterOperation::FilterTypeZoom: {
+ SkPaint paint;
+ int width = state.Source().width();
+ int height = state.Source().height();
+ skia::RefPtr<SkImageFilter> zoom_filter = skia::AdoptRef(
+ new SkMagnifierImageFilter(
+ SkRect::MakeXYWH(
+ (width - (width / op.amount())) / 2.f,
+ (height - (height / op.amount())) / 2.f,
+ width / op.amount(),
+ height / op.amount()),
+ op.zoomInset()));
+ paint.setImageFilter(zoom_filter.get());
+ canvas->saveLayer(NULL, &paint);
+ canvas->drawBitmap(state.Source(), 0, 0);
+ canvas->restore();
+ break;
+ }
+ case WebKit::WebFilterOperation::FilterTypeBrightness:
+ case WebKit::WebFilterOperation::FilterTypeSaturatingBrightness:
+ case WebKit::WebFilterOperation::FilterTypeContrast:
+ case WebKit::WebFilterOperation::FilterTypeGrayscale:
+ case WebKit::WebFilterOperation::FilterTypeSepia:
+ case WebKit::WebFilterOperation::FilterTypeSaturate:
+ case WebKit::WebFilterOperation::FilterTypeHueRotate:
+ case WebKit::WebFilterOperation::FilterTypeInvert:
+ case WebKit::WebFilterOperation::FilterTypeOpacity:
+ NOTREACHED();
+ break;
+ }
+ state.Swap();
+ }
+ return state.Source();
+}
+
+} // namespace cc
diff --git a/cc/output/render_surface_filters.h b/cc/output/render_surface_filters.h
new file mode 100644
index 0000000..62dde8e3
--- /dev/null
+++ b/cc/output/render_surface_filters.h
@@ -0,0 +1,38 @@
+// 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_OUTPUT_RENDER_SURFACE_FILTERS_H_
+#define CC_OUTPUT_RENDER_SURFACE_FILTERS_H_
+
+#include "cc/base/cc_export.h"
+
+class GrContext;
+class SkBitmap;
+
+namespace gfx {
+class SizeF;
+}
+
+namespace WebKit {
+class WebFilterOperations;
+}
+
+namespace cc {
+
+class CC_EXPORT RenderSurfaceFilters {
+ public:
+ static SkBitmap Apply(const WebKit::WebFilterOperations& filters,
+ unsigned texture_id,
+ gfx::SizeF size,
+ GrContext* gr_context);
+ static WebKit::WebFilterOperations Optimize(
+ const WebKit::WebFilterOperations& filters);
+
+ private:
+ RenderSurfaceFilters();
+};
+
+}
+#endif // CC_OUTPUT_RENDER_SURFACE_FILTERS_H_
diff --git a/cc/output/render_surface_filters_unittest.cc b/cc/output/render_surface_filters_unittest.cc
new file mode 100644
index 0000000..6898f23
--- /dev/null
+++ b/cc/output/render_surface_filters_unittest.cc
@@ -0,0 +1,146 @@
+// 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/output/render_surface_filters.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebFilterOperation.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebFilterOperations.h"
+
+using namespace WebKit;
+
+namespace cc {
+namespace {
+
+// Checks whether op can be combined with a following color matrix.
+bool isCombined(const WebFilterOperation& op)
+{
+ WebFilterOperations filters;
+ filters.append(op);
+ filters.append(WebFilterOperation::createBrightnessFilter(0)); // brightness(0) is identity.
+ WebFilterOperations optimized = RenderSurfaceFilters::Optimize(filters);
+ return optimized.size() == 1;
+}
+
+TEST(RenderSurfaceFiltersTest, testColorMatrixFiltersCombined)
+{
+ // Several filters should always combine for any amount between 0 and 1:
+ // grayscale, saturate, invert, contrast, opacity.
+ EXPECT_TRUE(isCombined(WebFilterOperation::createGrayscaleFilter(0)));
+ // Note that we use 0.3f to avoid "argument is truncated from 'double' to
+ // 'float'" warnings on Windows. 0.5 is exactly representable as a float, so
+ // there is no warning.
+ EXPECT_TRUE(isCombined(WebFilterOperation::createGrayscaleFilter(0.3f)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createGrayscaleFilter(0.5)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createGrayscaleFilter(1)));
+
+ EXPECT_TRUE(isCombined(WebFilterOperation::createSaturateFilter(0)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createSaturateFilter(0.3f)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createSaturateFilter(0.5)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createSaturateFilter(1)));
+
+ EXPECT_TRUE(isCombined(WebFilterOperation::createInvertFilter(0)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createInvertFilter(0.3f)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createInvertFilter(0.5)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createInvertFilter(1)));
+
+ EXPECT_TRUE(isCombined(WebFilterOperation::createContrastFilter(0)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createContrastFilter(0.3f)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createContrastFilter(0.5)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createContrastFilter(1)));
+
+ EXPECT_TRUE(isCombined(WebFilterOperation::createOpacityFilter(0)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createOpacityFilter(0.3f)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createOpacityFilter(0.5)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createOpacityFilter(1)));
+
+ // Brightness combines when amount <= 1
+ EXPECT_TRUE(isCombined(WebFilterOperation::createBrightnessFilter(0.5)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createBrightnessFilter(1)));
+ EXPECT_FALSE(isCombined(WebFilterOperation::createBrightnessFilter(1.5)));
+
+ // SaturatingBrightness combines only when amount == 0
+ EXPECT_TRUE(isCombined(WebFilterOperation::createSaturatingBrightnessFilter(0)));
+ EXPECT_FALSE(isCombined(WebFilterOperation::createSaturatingBrightnessFilter(0.5)));
+ EXPECT_FALSE(isCombined(WebFilterOperation::createSaturatingBrightnessFilter(1)));
+
+ // Several filters should never combine: blur, drop-shadow.
+ EXPECT_FALSE(isCombined(WebFilterOperation::createBlurFilter(3)));
+ EXPECT_FALSE(isCombined(WebFilterOperation::createDropShadowFilter(WebPoint(2, 2), 3, 0xffffffff)));
+
+ // sepia and hue may or may not combine depending on the value.
+ EXPECT_TRUE(isCombined(WebFilterOperation::createSepiaFilter(0)));
+ EXPECT_FALSE(isCombined(WebFilterOperation::createSepiaFilter(1)));
+ EXPECT_TRUE(isCombined(WebFilterOperation::createHueRotateFilter(0)));
+ EXPECT_FALSE(isCombined(WebFilterOperation::createHueRotateFilter(180)));
+
+ float matrix1[20] = {
+ 1, 0, 0, 0, 0,
+ 0, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0,
+ 0, 0, 0, 1, 0,
+ };
+ EXPECT_TRUE(isCombined(WebFilterOperation::createColorMatrixFilter(matrix1)));
+
+ float matrix2[20] = {
+ 1, 1, 0, 0, 0,
+ 0, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0,
+ 0, 0, 0, 1, 0,
+ };
+ EXPECT_FALSE(isCombined(WebFilterOperation::createColorMatrixFilter(matrix2)));
+
+ float matrix3[20] = {
+ 0.25, 0, 0, 0, 255*0.75,
+ 0, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0,
+ 0, 0, 0, 1, 0,
+ };
+ EXPECT_TRUE(isCombined(WebFilterOperation::createColorMatrixFilter(matrix3)));
+
+ float matrix4[20] = {
+ -0.25, 0.75, 0, 0, 255*0.25,
+ 0, 1, 0, 0, 0,
+ 0, 0, 1, 0, 0,
+ 0, 0, 0, 1, 0,
+ };
+ EXPECT_TRUE(isCombined(WebFilterOperation::createColorMatrixFilter(matrix4)));
+}
+
+TEST(RenderSurfaceFiltersTest, testOptimize)
+{
+ WebFilterOperation combines(WebFilterOperation::createBrightnessFilter(1));
+ WebFilterOperation doesntCombine(WebFilterOperation::createBrightnessFilter(1.5));
+
+ WebFilterOperations filters;
+ WebFilterOperations optimized = RenderSurfaceFilters::Optimize(filters);
+ EXPECT_EQ(0u, optimized.size());
+
+ filters.append(combines);
+ optimized = RenderSurfaceFilters::Optimize(filters);
+ EXPECT_EQ(1u, optimized.size());
+
+ filters.append(combines);
+ optimized = RenderSurfaceFilters::Optimize(filters);
+ EXPECT_EQ(1u, optimized.size());
+
+ filters.append(doesntCombine);
+ optimized = RenderSurfaceFilters::Optimize(filters);
+ EXPECT_EQ(1u, optimized.size());
+
+ filters.append(combines);
+ optimized = RenderSurfaceFilters::Optimize(filters);
+ EXPECT_EQ(2u, optimized.size());
+
+ filters.append(doesntCombine);
+ optimized = RenderSurfaceFilters::Optimize(filters);
+ EXPECT_EQ(2u, optimized.size());
+
+ filters.append(doesntCombine);
+ optimized = RenderSurfaceFilters::Optimize(filters);
+ EXPECT_EQ(3u, optimized.size());
+}
+
+} // namespace
+} // namespace cc
diff --git a/cc/output/renderer.cc b/cc/output/renderer.cc
new file mode 100644
index 0000000..7627db7
--- /dev/null
+++ b/cc/output/renderer.cc
@@ -0,0 +1,17 @@
+// 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/output/renderer.h"
+
+namespace cc {
+
+bool Renderer::HaveCachedResourcesForRenderPassId(RenderPass::Id id) const {
+ return false;
+}
+
+bool Renderer::IsContextLost() {
+ return false;
+}
+
+} // namespace cc
diff --git a/cc/output/renderer.h b/cc/output/renderer.h
new file mode 100644
index 0000000..1c309ac
--- /dev/null
+++ b/cc/output/renderer.h
@@ -0,0 +1,90 @@
+// 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_OUTPUT_RENDERER_H_
+#define CC_OUTPUT_RENDERER_H_
+
+#include "base/basictypes.h"
+#include "cc/base/cc_export.h"
+#include "cc/layer_tree_host.h"
+#include "cc/managed_memory_policy.h"
+#include "cc/render_pass.h"
+
+namespace cc {
+
+class CompositorFrameAck;
+class CompositorFrameMetadata;
+class ScopedResource;
+
+class CC_EXPORT RendererClient {
+ public:
+ virtual gfx::Size DeviceViewportSize() const = 0;
+ virtual const LayerTreeSettings& Settings() const = 0;
+ virtual void DidLoseOutputSurface() = 0;
+ virtual void OnSwapBuffersComplete() = 0;
+ virtual void SetFullRootLayerDamage() = 0;
+ virtual void SetManagedMemoryPolicy(const ManagedMemoryPolicy& policy) = 0;
+ virtual void EnforceManagedMemoryPolicy(
+ const ManagedMemoryPolicy& policy) = 0;
+ virtual bool HasImplThread() const = 0;
+ virtual bool ShouldClearRootRenderPass() const = 0;
+ virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const = 0;
+
+ protected:
+ virtual ~RendererClient() {}
+};
+
+class CC_EXPORT Renderer {
+ public:
+ virtual ~Renderer() {}
+
+ virtual const RendererCapabilities& Capabilities() const = 0;
+
+ const LayerTreeSettings& Settings() const { return client_->Settings(); }
+
+ gfx::Size ViewportSize() const { return client_->DeviceViewportSize(); }
+ int ViewportWidth() const { return ViewportSize().width(); }
+ int ViewportHeight() const { return ViewportSize().height(); }
+
+ virtual void ViewportChanged() {}
+ virtual void ReceiveCompositorFrameAck(const CompositorFrameAck& ack) {}
+
+ virtual void DecideRenderPassAllocationsForFrame(
+ const RenderPassList& render_passes_in_draw_order) {}
+ virtual bool HaveCachedResourcesForRenderPassId(RenderPass::Id id) const;
+
+ // This passes ownership of the render passes to the renderer. It should
+ // consume them, and empty the list.
+ virtual void DrawFrame(RenderPassList& render_passes_in_draw_order) = 0;
+
+ // Waits for rendering to finish.
+ virtual void Finish() = 0;
+
+ virtual void DoNoOp() {}
+
+ // Puts backbuffer onscreen.
+ virtual bool SwapBuffers() = 0;
+
+ virtual void GetFramebufferPixels(void* pixels, gfx::Rect rect) = 0;
+
+ virtual bool IsContextLost();
+
+ virtual void SetVisible(bool visible) = 0;
+
+ virtual void SendManagedMemoryStats(size_t bytes_visible,
+ size_t bytes_visible_and_nearby,
+ size_t bytes_allocated) = 0;
+
+ protected:
+ explicit Renderer(RendererClient* client)
+ : client_(client) {}
+
+ RendererClient* client_;
+
+ DISALLOW_COPY_AND_ASSIGN(Renderer);
+};
+
+}
+
+#endif // CC_OUTPUT_RENDERER_H_
diff --git a/cc/output/shader.cc b/cc/output/shader.cc
new file mode 100644
index 0000000..c4ca0a9
--- /dev/null
+++ b/cc/output/shader.cc
@@ -0,0 +1,938 @@
+// Copyright 2011 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/output/shader.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h"
+
+#define SHADER0(Src) #Src
+#define SHADER(Src) SHADER0(Src)
+
+using WebKit::WebGraphicsContext3D;
+
+namespace cc {
+
+namespace {
+
+static void getProgramUniformLocations(WebGraphicsContext3D* context, unsigned program, const char** shaderUniforms, size_t count, size_t maxLocations, int* locations, bool usingBindUniform, int* baseUniformIndex)
+{
+ for (size_t uniformIndex = 0; uniformIndex < count; uniformIndex ++) {
+ DCHECK(uniformIndex < maxLocations);
+
+ if (usingBindUniform) {
+ locations[uniformIndex] = (*baseUniformIndex)++;
+ context->bindUniformLocationCHROMIUM(program, locations[uniformIndex], shaderUniforms[uniformIndex]);
+ } else
+ locations[uniformIndex] = context->getUniformLocation(program, shaderUniforms[uniformIndex]);
+ }
+}
+
+}
+
+VertexShaderPosTex::VertexShaderPosTex()
+ : m_matrixLocation(-1)
+{
+}
+
+void VertexShaderPosTex::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "matrix",
+ };
+ int locations[1];
+
+ getProgramUniformLocations(context, program, shaderUniforms, arraysize(shaderUniforms), arraysize(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_matrixLocation = locations[0];
+ DCHECK(m_matrixLocation != -1);
+}
+
+std::string VertexShaderPosTex::getShaderString() const
+{
+ return SHADER(
+ attribute vec4 a_position;
+ attribute vec2 a_texCoord;
+ uniform mat4 matrix;
+ varying vec2 v_texCoord;
+ void main()
+ {
+ gl_Position = matrix * a_position;
+ v_texCoord = a_texCoord;
+ }
+ );
+}
+
+VertexShaderPosTexYUVStretch::VertexShaderPosTexYUVStretch()
+ : m_matrixLocation(-1)
+ , m_texScaleLocation(-1)
+{
+}
+
+void VertexShaderPosTexYUVStretch::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "matrix",
+ "texScale",
+ };
+ int locations[2];
+
+ getProgramUniformLocations(context, program, shaderUniforms, arraysize(shaderUniforms), arraysize(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_matrixLocation = locations[0];
+ m_texScaleLocation = locations[1];
+ DCHECK(m_matrixLocation != -1 && m_texScaleLocation != -1);
+}
+
+std::string VertexShaderPosTexYUVStretch::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ attribute vec4 a_position;
+ attribute vec2 a_texCoord;
+ uniform mat4 matrix;
+ varying vec2 v_texCoord;
+ uniform vec2 texScale;
+ void main()
+ {
+ gl_Position = matrix * a_position;
+ v_texCoord = a_texCoord * texScale;
+ }
+ );
+}
+
+VertexShaderPos::VertexShaderPos()
+ : m_matrixLocation(-1)
+{
+}
+
+void VertexShaderPos::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "matrix",
+ };
+ int locations[1];
+
+ getProgramUniformLocations(context, program, shaderUniforms, arraysize(shaderUniforms), arraysize(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_matrixLocation = locations[0];
+ DCHECK(m_matrixLocation != -1);
+}
+
+std::string VertexShaderPos::getShaderString() const
+{
+ return SHADER(
+ attribute vec4 a_position;
+ uniform mat4 matrix;
+ void main()
+ {
+ gl_Position = matrix * a_position;
+ }
+ );
+}
+
+VertexShaderPosTexTransform::VertexShaderPosTexTransform()
+ : m_matrixLocation(-1)
+ , m_texTransformLocation(-1)
+ , m_vertexOpacityLocation(-1)
+{
+}
+
+void VertexShaderPosTexTransform::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "matrix",
+ "texTransform",
+ "opacity",
+ };
+ int locations[3];
+
+ getProgramUniformLocations(context, program, shaderUniforms, arraysize(shaderUniforms), arraysize(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_matrixLocation = locations[0];
+ m_texTransformLocation = locations[1];
+ m_vertexOpacityLocation = locations[2];
+ DCHECK(m_matrixLocation != -1 && m_texTransformLocation != -1 && m_vertexOpacityLocation != -1);
+}
+
+std::string VertexShaderPosTexTransform::getShaderString() const
+{
+ return SHADER(
+ attribute vec4 a_position;
+ attribute vec2 a_texCoord;
+ attribute float a_index;
+ uniform mat4 matrix[8];
+ uniform vec4 texTransform[8];
+ uniform float opacity[32];
+ varying vec2 v_texCoord;
+ varying float v_alpha;
+ void main()
+ {
+ gl_Position = matrix[int(a_index * 0.25)] * a_position;
+ vec4 texTrans = texTransform[int(a_index * 0.25)];
+ v_texCoord = a_texCoord * texTrans.zw + texTrans.xy;
+ v_alpha = opacity[int(a_index)];
+ }
+ );
+}
+
+std::string VertexShaderPosTexTransformFlip::getShaderString() const
+{
+ return SHADER(
+ attribute vec4 a_position;
+ attribute vec2 a_texCoord;
+ attribute float a_index;
+ uniform mat4 matrix[8];
+ uniform vec4 texTransform[8];
+ uniform float opacity[32];
+ varying vec2 v_texCoord;
+ varying float v_alpha;
+ void main()
+ {
+ gl_Position = matrix[int(a_index * 0.25)] * a_position;
+ vec4 texTrans = texTransform[int(a_index * 0.25)];
+ v_texCoord = a_texCoord * texTrans.zw + texTrans.xy;
+ v_texCoord.y = 1.0 - v_texCoord.y;
+ v_alpha = opacity[int(a_index)];
+ }
+ );
+}
+
+std::string VertexShaderPosTexIdentity::getShaderString() const
+{
+ return SHADER(
+ attribute vec4 a_position;
+ varying vec2 v_texCoord;
+ void main()
+ {
+ gl_Position = a_position;
+ v_texCoord = (a_position.xy + vec2(1.0)) * 0.5;
+ }
+ );
+}
+
+VertexShaderQuad::VertexShaderQuad()
+ : m_matrixLocation(-1)
+ , m_pointLocation(-1)
+ , m_texScaleLocation(-1)
+{
+}
+
+void VertexShaderQuad::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "matrix",
+ "point",
+ "texScale",
+ };
+ int locations[3];
+
+ getProgramUniformLocations(context, program, shaderUniforms, arraysize(shaderUniforms), arraysize(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_matrixLocation = locations[0];
+ m_pointLocation = locations[1];
+ m_texScaleLocation = locations[2];
+ DCHECK_NE(m_matrixLocation, -1);
+ DCHECK_NE(m_pointLocation, -1);
+ DCHECK_NE(m_texScaleLocation, -1);
+}
+
+std::string VertexShaderQuad::getShaderString() const
+{
+ return SHADER(
+ attribute vec4 a_position;
+ attribute vec2 a_texCoord;
+ uniform mat4 matrix;
+ uniform vec2 point[4];
+ uniform vec2 texScale;
+ varying vec2 v_texCoord;
+ void main()
+ {
+ vec2 complement = abs(a_texCoord - 1.0);
+ vec4 pos = vec4(0.0, 0.0, a_position.z, a_position.w);
+ pos.xy += (complement.x * complement.y) * point[0];
+ pos.xy += (a_texCoord.x * complement.y) * point[1];
+ pos.xy += (a_texCoord.x * a_texCoord.y) * point[2];
+ pos.xy += (complement.x * a_texCoord.y) * point[3];
+ gl_Position = matrix * pos;
+ v_texCoord = (pos.xy + vec2(0.5)) * texScale;
+ }
+ );
+}
+
+VertexShaderTile::VertexShaderTile()
+ : m_matrixLocation(-1)
+ , m_pointLocation(-1)
+ , m_vertexTexTransformLocation(-1)
+{
+}
+
+void VertexShaderTile::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "matrix",
+ "point",
+ "vertexTexTransform",
+ };
+ int locations[3];
+
+ getProgramUniformLocations(context, program, shaderUniforms, arraysize(shaderUniforms), arraysize(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_matrixLocation = locations[0];
+ m_pointLocation = locations[1];
+ m_vertexTexTransformLocation = locations[2];
+ DCHECK(m_matrixLocation != -1 && m_pointLocation != -1 && m_vertexTexTransformLocation != -1);
+}
+
+std::string VertexShaderTile::getShaderString() const
+{
+ return SHADER(
+ attribute vec4 a_position;
+ attribute vec2 a_texCoord;
+ uniform mat4 matrix;
+ uniform vec2 point[4];
+ uniform vec4 vertexTexTransform;
+ varying vec2 v_texCoord;
+ void main()
+ {
+ vec2 complement = abs(a_texCoord - 1.0);
+ vec4 pos = vec4(0.0, 0.0, a_position.z, a_position.w);
+ pos.xy += (complement.x * complement.y) * point[0];
+ pos.xy += (a_texCoord.x * complement.y) * point[1];
+ pos.xy += (a_texCoord.x * a_texCoord.y) * point[2];
+ pos.xy += (complement.x * a_texCoord.y) * point[3];
+ gl_Position = matrix * pos;
+ v_texCoord = pos.xy * vertexTexTransform.zw + vertexTexTransform.xy;
+ }
+ );
+}
+
+VertexShaderVideoTransform::VertexShaderVideoTransform()
+ : m_matrixLocation(-1)
+ , m_texMatrixLocation(-1)
+{
+}
+
+bool VertexShaderVideoTransform::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "matrix",
+ "texMatrix",
+ };
+ int locations[2];
+
+ getProgramUniformLocations(context, program, shaderUniforms, arraysize(shaderUniforms), arraysize(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_matrixLocation = locations[0];
+ m_texMatrixLocation = locations[1];
+ return m_matrixLocation != -1 && m_texMatrixLocation != -1;
+}
+
+std::string VertexShaderVideoTransform::getShaderString() const
+{
+ return SHADER(
+ attribute vec4 a_position;
+ attribute vec2 a_texCoord;
+ uniform mat4 matrix;
+ uniform mat4 texMatrix;
+ varying vec2 v_texCoord;
+ void main()
+ {
+ gl_Position = matrix * a_position;
+ v_texCoord = vec2(texMatrix * vec4(a_texCoord.x, 1.0 - a_texCoord.y, 0.0, 1.0));
+ }
+ );
+}
+
+FragmentTexAlphaBinding::FragmentTexAlphaBinding()
+ : m_samplerLocation(-1)
+ , m_alphaLocation(-1)
+{
+}
+
+void FragmentTexAlphaBinding::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "s_texture",
+ "alpha",
+ };
+ int locations[2];
+
+ getProgramUniformLocations(context, program, shaderUniforms, arraysize(shaderUniforms), arraysize(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_samplerLocation = locations[0];
+ m_alphaLocation = locations[1];
+ DCHECK(m_samplerLocation != -1 && m_alphaLocation != -1);
+}
+
+FragmentTexOpaqueBinding::FragmentTexOpaqueBinding()
+ : m_samplerLocation(-1)
+{
+}
+
+void FragmentTexOpaqueBinding::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "s_texture",
+ };
+ int locations[1];
+
+ getProgramUniformLocations(context, program, shaderUniforms, arraysize(shaderUniforms), arraysize(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_samplerLocation = locations[0];
+ DCHECK(m_samplerLocation != -1);
+}
+
+bool FragmentShaderOESImageExternal::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "s_texture",
+ };
+ int locations[1];
+
+ getProgramUniformLocations(context, program, shaderUniforms, arraysize(shaderUniforms), arraysize(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_samplerLocation = locations[0];
+ return m_samplerLocation != -1;
+}
+
+std::string FragmentShaderOESImageExternal::getShaderString() const
+{
+ // Cannot use the SHADER() macro because of the '#' char
+ return "#extension GL_OES_EGL_image_external : require \n"
+ "precision mediump float;\n"
+ "varying vec2 v_texCoord;\n"
+ "uniform samplerExternalOES s_texture;\n"
+ "void main()\n"
+ "{\n"
+ " vec4 texColor = texture2D(s_texture, v_texCoord);\n"
+ " gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w);\n"
+ "}\n";
+}
+
+std::string FragmentShaderRGBATexAlpha::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ uniform float alpha;
+ void main()
+ {
+ vec4 texColor = texture2D(s_texture, v_texCoord);
+ gl_FragColor = texColor * alpha;
+ }
+ );
+}
+
+std::string FragmentShaderRGBATexVaryingAlpha::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ varying float v_alpha;
+ uniform sampler2D s_texture;
+ void main()
+ {
+ vec4 texColor = texture2D(s_texture, v_texCoord);
+ gl_FragColor = texColor * v_alpha;
+ }
+ );
+}
+
+std::string FragmentShaderRGBATexRectVaryingAlpha::getShaderString() const
+{
+ return "#extension GL_ARB_texture_rectangle : require\n"
+ "precision mediump float;\n"
+ "varying vec2 v_texCoord;\n"
+ "varying float v_alpha;\n"
+ "uniform sampler2DRect s_texture;\n"
+ "void main()\n"
+ "{\n"
+ " vec4 texColor = texture2DRect(s_texture, v_texCoord);\n"
+ " gl_FragColor = texColor * v_alpha;\n"
+ "}\n";
+}
+
+std::string FragmentShaderRGBATexOpaque::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ void main()
+ {
+ vec4 texColor = texture2D(s_texture, v_texCoord);
+ gl_FragColor = vec4(texColor.rgb, 1.0);
+ }
+ );
+}
+
+std::string FragmentShaderRGBATex::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ void main()
+ {
+ gl_FragColor = texture2D(s_texture, v_texCoord);
+ }
+ );
+}
+
+std::string FragmentShaderRGBATexSwizzleAlpha::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ uniform float alpha;
+ void main()
+ {
+ vec4 texColor = texture2D(s_texture, v_texCoord);
+ gl_FragColor = vec4(texColor.z, texColor.y, texColor.x, texColor.w) * alpha;
+ }
+ );
+}
+
+std::string FragmentShaderRGBATexSwizzleOpaque::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ void main()
+ {
+ vec4 texColor = texture2D(s_texture, v_texCoord);
+ gl_FragColor = vec4(texColor.z, texColor.y, texColor.x, 1.0);
+ }
+ );
+}
+
+FragmentShaderRGBATexAlphaAA::FragmentShaderRGBATexAlphaAA()
+ : m_samplerLocation(-1)
+ , m_alphaLocation(-1)
+ , m_edgeLocation(-1)
+{
+}
+
+void FragmentShaderRGBATexAlphaAA::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "s_texture",
+ "alpha",
+ "edge",
+ };
+ int locations[3];
+
+ getProgramUniformLocations(context, program, shaderUniforms, arraysize(shaderUniforms), arraysize(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_samplerLocation = locations[0];
+ m_alphaLocation = locations[1];
+ m_edgeLocation = locations[2];
+ DCHECK(m_samplerLocation != -1 && m_alphaLocation != -1 && m_edgeLocation != -1);
+}
+
+std::string FragmentShaderRGBATexAlphaAA::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ uniform float alpha;
+ uniform vec3 edge[8];
+ void main()
+ {
+ vec4 texColor = texture2D(s_texture, v_texCoord);
+ vec3 pos = vec3(gl_FragCoord.xy, 1);
+ float a0 = clamp(dot(edge[0], pos), 0.0, 1.0);
+ float a1 = clamp(dot(edge[1], pos), 0.0, 1.0);
+ float a2 = clamp(dot(edge[2], pos), 0.0, 1.0);
+ float a3 = clamp(dot(edge[3], pos), 0.0, 1.0);
+ float a4 = clamp(dot(edge[4], pos), 0.0, 1.0);
+ float a5 = clamp(dot(edge[5], pos), 0.0, 1.0);
+ float a6 = clamp(dot(edge[6], pos), 0.0, 1.0);
+ float a7 = clamp(dot(edge[7], pos), 0.0, 1.0);
+ gl_FragColor = texColor * alpha * min(min(a0, a2) * min(a1, a3), min(a4, a6) * min(a5, a7));
+ }
+ );
+}
+
+FragmentTexClampAlphaAABinding::FragmentTexClampAlphaAABinding()
+ : m_samplerLocation(-1)
+ , m_alphaLocation(-1)
+ , m_fragmentTexTransformLocation(-1)
+ , m_edgeLocation(-1)
+{
+}
+
+void FragmentTexClampAlphaAABinding::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "s_texture",
+ "alpha",
+ "fragmentTexTransform",
+ "edge",
+ };
+ int locations[4];
+
+ getProgramUniformLocations(context, program, shaderUniforms, arraysize(shaderUniforms), arraysize(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_samplerLocation = locations[0];
+ m_alphaLocation = locations[1];
+ m_fragmentTexTransformLocation = locations[2];
+ m_edgeLocation = locations[3];
+ DCHECK(m_samplerLocation != -1 && m_alphaLocation != -1 && m_fragmentTexTransformLocation != -1 && m_edgeLocation != -1);
+}
+
+std::string FragmentShaderRGBATexClampAlphaAA::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ uniform float alpha;
+ uniform vec4 fragmentTexTransform;
+ uniform vec3 edge[8];
+ void main()
+ {
+ vec2 texCoord = clamp(v_texCoord, 0.0, 1.0) * fragmentTexTransform.zw + fragmentTexTransform.xy;
+ vec4 texColor = texture2D(s_texture, texCoord);
+ vec3 pos = vec3(gl_FragCoord.xy, 1);
+ float a0 = clamp(dot(edge[0], pos), 0.0, 1.0);
+ float a1 = clamp(dot(edge[1], pos), 0.0, 1.0);
+ float a2 = clamp(dot(edge[2], pos), 0.0, 1.0);
+ float a3 = clamp(dot(edge[3], pos), 0.0, 1.0);
+ float a4 = clamp(dot(edge[4], pos), 0.0, 1.0);
+ float a5 = clamp(dot(edge[5], pos), 0.0, 1.0);
+ float a6 = clamp(dot(edge[6], pos), 0.0, 1.0);
+ float a7 = clamp(dot(edge[7], pos), 0.0, 1.0);
+ gl_FragColor = texColor * alpha * min(min(a0, a2) * min(a1, a3), min(a4, a6) * min(a5, a7));
+ }
+ );
+}
+
+std::string FragmentShaderRGBATexClampSwizzleAlphaAA::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ uniform float alpha;
+ uniform vec4 fragmentTexTransform;
+ uniform vec3 edge[8];
+ void main()
+ {
+ vec2 texCoord = clamp(v_texCoord, 0.0, 1.0) * fragmentTexTransform.zw + fragmentTexTransform.xy;
+ vec4 texColor = texture2D(s_texture, texCoord);
+ vec3 pos = vec3(gl_FragCoord.xy, 1);
+ float a0 = clamp(dot(edge[0], pos), 0.0, 1.0);
+ float a1 = clamp(dot(edge[1], pos), 0.0, 1.0);
+ float a2 = clamp(dot(edge[2], pos), 0.0, 1.0);
+ float a3 = clamp(dot(edge[3], pos), 0.0, 1.0);
+ float a4 = clamp(dot(edge[4], pos), 0.0, 1.0);
+ float a5 = clamp(dot(edge[5], pos), 0.0, 1.0);
+ float a6 = clamp(dot(edge[6], pos), 0.0, 1.0);
+ float a7 = clamp(dot(edge[7], pos), 0.0, 1.0);
+ gl_FragColor = vec4(texColor.z, texColor.y, texColor.x, texColor.w) * alpha * min(min(a0, a2) * min(a1, a3), min(a4, a6) * min(a5, a7));
+ }
+ );
+}
+
+FragmentShaderRGBATexAlphaMask::FragmentShaderRGBATexAlphaMask()
+ : m_samplerLocation(-1)
+ , m_maskSamplerLocation(-1)
+ , m_alphaLocation(-1)
+ , m_maskTexCoordScaleLocation(-1)
+{
+}
+
+void FragmentShaderRGBATexAlphaMask::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "s_texture",
+ "s_mask",
+ "alpha",
+ "maskTexCoordScale",
+ "maskTexCoordOffset",
+ };
+ int locations[5];
+
+ getProgramUniformLocations(context, program, shaderUniforms, arraysize(shaderUniforms), arraysize(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_samplerLocation = locations[0];
+ m_maskSamplerLocation = locations[1];
+ m_alphaLocation = locations[2];
+ m_maskTexCoordScaleLocation = locations[3];
+ m_maskTexCoordOffsetLocation = locations[4];
+ DCHECK(m_samplerLocation != -1 && m_maskSamplerLocation != -1 && m_alphaLocation != -1);
+}
+
+std::string FragmentShaderRGBATexAlphaMask::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ uniform sampler2D s_mask;
+ uniform vec2 maskTexCoordScale;
+ uniform vec2 maskTexCoordOffset;
+ uniform float alpha;
+ void main()
+ {
+ vec4 texColor = texture2D(s_texture, v_texCoord);
+ vec2 maskTexCoord = vec2(maskTexCoordOffset.x + v_texCoord.x * maskTexCoordScale.x, maskTexCoordOffset.y + v_texCoord.y * maskTexCoordScale.y);
+ vec4 maskColor = texture2D(s_mask, maskTexCoord);
+ gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w) * alpha * maskColor.w;
+ }
+ );
+}
+
+FragmentShaderRGBATexAlphaMaskAA::FragmentShaderRGBATexAlphaMaskAA()
+ : m_samplerLocation(-1)
+ , m_maskSamplerLocation(-1)
+ , m_alphaLocation(-1)
+ , m_edgeLocation(-1)
+ , m_maskTexCoordScaleLocation(-1)
+{
+}
+
+void FragmentShaderRGBATexAlphaMaskAA::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "s_texture",
+ "s_mask",
+ "alpha",
+ "edge",
+ "maskTexCoordScale",
+ "maskTexCoordOffset",
+ };
+ int locations[6];
+
+ getProgramUniformLocations(context, program, shaderUniforms, arraysize(shaderUniforms), arraysize(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_samplerLocation = locations[0];
+ m_maskSamplerLocation = locations[1];
+ m_alphaLocation = locations[2];
+ m_edgeLocation = locations[3];
+ m_maskTexCoordScaleLocation = locations[4];
+ m_maskTexCoordOffsetLocation = locations[5];
+ DCHECK(m_samplerLocation != -1 && m_maskSamplerLocation != -1 && m_alphaLocation != -1 && m_edgeLocation != -1);
+}
+
+std::string FragmentShaderRGBATexAlphaMaskAA::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ uniform sampler2D s_mask;
+ uniform vec2 maskTexCoordScale;
+ uniform vec2 maskTexCoordOffset;
+ uniform float alpha;
+ uniform vec3 edge[8];
+ void main()
+ {
+ vec4 texColor = texture2D(s_texture, v_texCoord);
+ vec2 maskTexCoord = vec2(maskTexCoordOffset.x + v_texCoord.x * maskTexCoordScale.x, maskTexCoordOffset.y + v_texCoord.y * maskTexCoordScale.y);
+ vec4 maskColor = texture2D(s_mask, maskTexCoord);
+ vec3 pos = vec3(gl_FragCoord.xy, 1);
+ float a0 = clamp(dot(edge[0], pos), 0.0, 1.0);
+ float a1 = clamp(dot(edge[1], pos), 0.0, 1.0);
+ float a2 = clamp(dot(edge[2], pos), 0.0, 1.0);
+ float a3 = clamp(dot(edge[3], pos), 0.0, 1.0);
+ float a4 = clamp(dot(edge[4], pos), 0.0, 1.0);
+ float a5 = clamp(dot(edge[5], pos), 0.0, 1.0);
+ float a6 = clamp(dot(edge[6], pos), 0.0, 1.0);
+ float a7 = clamp(dot(edge[7], pos), 0.0, 1.0);
+ gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w) * alpha * maskColor.w * min(min(a0, a2) * min(a1, a3), min(a4, a6) * min(a5, a7));
+ }
+ );
+}
+
+FragmentShaderYUVVideo::FragmentShaderYUVVideo()
+ : m_yTextureLocation(-1)
+ , m_uTextureLocation(-1)
+ , m_vTextureLocation(-1)
+ , m_alphaLocation(-1)
+ , m_yuvMatrixLocation(-1)
+ , m_yuvAdjLocation(-1)
+{
+}
+
+void FragmentShaderYUVVideo::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "y_texture",
+ "u_texture",
+ "v_texture",
+ "alpha",
+ "yuv_matrix",
+ "yuv_adj",
+ };
+ int locations[6];
+
+ getProgramUniformLocations(context, program, shaderUniforms, arraysize(shaderUniforms), arraysize(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_yTextureLocation = locations[0];
+ m_uTextureLocation = locations[1];
+ m_vTextureLocation = locations[2];
+ m_alphaLocation = locations[3];
+ m_yuvMatrixLocation = locations[4];
+ m_yuvAdjLocation = locations[5];
+
+ DCHECK(m_yTextureLocation != -1 && m_uTextureLocation != -1 && m_vTextureLocation != -1
+ && m_alphaLocation != -1 && m_yuvMatrixLocation != -1 && m_yuvAdjLocation != -1);
+}
+
+std::string FragmentShaderYUVVideo::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ precision mediump int;
+ varying vec2 v_texCoord;
+ uniform sampler2D y_texture;
+ uniform sampler2D u_texture;
+ uniform sampler2D v_texture;
+ uniform float alpha;
+ uniform vec3 yuv_adj;
+ uniform mat3 yuv_matrix;
+ void main()
+ {
+ float y_raw = texture2D(y_texture, v_texCoord).x;
+ float u_unsigned = texture2D(u_texture, v_texCoord).x;
+ float v_unsigned = texture2D(v_texture, v_texCoord).x;
+ vec3 yuv = vec3(y_raw, u_unsigned, v_unsigned) + yuv_adj;
+ vec3 rgb = yuv_matrix * yuv;
+ gl_FragColor = vec4(rgb, float(1)) * alpha;
+ }
+ );
+}
+
+FragmentShaderColor::FragmentShaderColor()
+ : m_colorLocation(-1)
+{
+}
+
+void FragmentShaderColor::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "color",
+ };
+ int locations[1];
+
+ getProgramUniformLocations(context, program, shaderUniforms, arraysize(shaderUniforms), arraysize(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_colorLocation = locations[0];
+ DCHECK(m_colorLocation != -1);
+}
+
+std::string FragmentShaderColor::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ uniform vec4 color;
+ void main()
+ {
+ gl_FragColor = color;
+ }
+ );
+}
+
+FragmentShaderColorAA::FragmentShaderColorAA()
+ : m_edgeLocation(-1)
+ , m_colorLocation(-1)
+{
+}
+
+void FragmentShaderColorAA::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "edge",
+ "color",
+ };
+ int locations[2];
+
+ getProgramUniformLocations(context, program, shaderUniforms, arraysize(shaderUniforms), arraysize(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_edgeLocation = locations[0];
+ m_colorLocation = locations[1];
+ DCHECK(m_edgeLocation != -1 && m_colorLocation != -1);
+}
+
+std::string FragmentShaderColorAA::getShaderString() const
+{
+ return SHADER(
+ precision mediump float;
+ uniform vec4 color;
+ uniform vec3 edge[8];
+ void main()
+ {
+ vec3 pos = vec3(gl_FragCoord.xy, 1);
+ float a0 = clamp(dot(edge[0], pos), 0.0, 1.0);
+ float a1 = clamp(dot(edge[1], pos), 0.0, 1.0);
+ float a2 = clamp(dot(edge[2], pos), 0.0, 1.0);
+ float a3 = clamp(dot(edge[3], pos), 0.0, 1.0);
+ float a4 = clamp(dot(edge[4], pos), 0.0, 1.0);
+ float a5 = clamp(dot(edge[5], pos), 0.0, 1.0);
+ float a6 = clamp(dot(edge[6], pos), 0.0, 1.0);
+ float a7 = clamp(dot(edge[7], pos), 0.0, 1.0);
+ gl_FragColor = color * min(min(a0, a2) * min(a1, a3), min(a4, a6) * min(a5, a7));
+ }
+ );
+}
+
+FragmentShaderCheckerboard::FragmentShaderCheckerboard()
+ : m_alphaLocation(-1)
+ , m_texTransformLocation(-1)
+ , m_frequencyLocation(-1)
+{
+}
+
+void FragmentShaderCheckerboard::init(WebGraphicsContext3D* context, unsigned program, bool usingBindUniform, int* baseUniformIndex)
+{
+ static const char* shaderUniforms[] = {
+ "alpha",
+ "texTransform",
+ "frequency",
+ "color",
+ };
+ int locations[4];
+
+ getProgramUniformLocations(context, program, shaderUniforms, arraysize(shaderUniforms), arraysize(locations), locations, usingBindUniform, baseUniformIndex);
+
+ m_alphaLocation = locations[0];
+ m_texTransformLocation = locations[1];
+ m_frequencyLocation = locations[2];
+ m_colorLocation = locations[3];
+ DCHECK(m_alphaLocation != -1 && m_texTransformLocation != -1 && m_frequencyLocation != -1 && m_colorLocation != -1);
+}
+
+std::string FragmentShaderCheckerboard::getShaderString() const
+{
+ // Shader based on Example 13-17 of "OpenGL ES 2.0 Programming Guide"
+ // by Munshi, Ginsburg, Shreiner.
+ return SHADER(
+ precision mediump float;
+ precision mediump int;
+ varying vec2 v_texCoord;
+ uniform float alpha;
+ uniform float frequency;
+ uniform vec4 texTransform;
+ uniform vec4 color;
+ void main()
+ {
+ vec4 color1 = vec4(1.0, 1.0, 1.0, 1.0);
+ vec4 color2 = color;
+ vec2 texCoord = clamp(v_texCoord, 0.0, 1.0) * texTransform.zw + texTransform.xy;
+ vec2 coord = mod(floor(texCoord * frequency * 2.0), 2.0);
+ float picker = abs(coord.x - coord.y);
+ gl_FragColor = mix(color1, color2, picker) * alpha;
+ }
+ );
+}
+
+} // namespace cc
diff --git a/cc/output/shader.h b/cc/output/shader.h
new file mode 100644
index 0000000..4afd3a8
--- /dev/null
+++ b/cc/output/shader.h
@@ -0,0 +1,366 @@
+// Copyright 2011 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_OUTPUT_SHADER_H_
+#define CC_OUTPUT_SHADER_H_
+
+#include <string>
+#include "third_party/skia/include/core/SkColorPriv.h"
+
+namespace WebKit {
+class WebGraphicsContext3D;
+}
+
+namespace cc {
+
+class VertexShaderPosTex {
+public:
+ VertexShaderPosTex();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ std::string getShaderString() const;
+
+ int matrixLocation() const { return m_matrixLocation; }
+
+private:
+ int m_matrixLocation;
+};
+
+class VertexShaderPosTexYUVStretch {
+public:
+ VertexShaderPosTexYUVStretch();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ std::string getShaderString() const;
+
+ int matrixLocation() const { return m_matrixLocation; }
+ int texScaleLocation() const { return m_texScaleLocation; }
+
+private:
+ int m_matrixLocation;
+ int m_texScaleLocation;
+};
+
+class VertexShaderPos {
+public:
+ VertexShaderPos();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ std::string getShaderString() const;
+
+ int matrixLocation() const { return m_matrixLocation; }
+
+private:
+ int m_matrixLocation;
+};
+
+class VertexShaderPosTexIdentity {
+public:
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex) { }
+ std::string getShaderString() const;
+};
+
+class VertexShaderPosTexTransform {
+public:
+ VertexShaderPosTexTransform();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ std::string getShaderString() const;
+
+ int matrixLocation() const { return m_matrixLocation; }
+ int texTransformLocation() const { return m_texTransformLocation; }
+ int vertexOpacityLocation() const { return m_vertexOpacityLocation; }
+
+private:
+ int m_matrixLocation;
+ int m_texTransformLocation;
+ int m_vertexOpacityLocation;
+};
+
+class VertexShaderPosTexTransformFlip : public VertexShaderPosTexTransform {
+public:
+ std::string getShaderString() const;
+};
+
+class VertexShaderQuad {
+public:
+ VertexShaderQuad();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ std::string getShaderString() const;
+
+ int matrixLocation() const { return m_matrixLocation; }
+ int pointLocation() const { return m_pointLocation; }
+ int texScaleLocation() const { return m_texScaleLocation; }
+
+private:
+ int m_matrixLocation;
+ int m_pointLocation;
+ int m_texScaleLocation;
+};
+
+class VertexShaderTile {
+public:
+ VertexShaderTile();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ std::string getShaderString() const;
+
+ int matrixLocation() const { return m_matrixLocation; }
+ int pointLocation() const { return m_pointLocation; }
+ int vertexTexTransformLocation() const { return m_vertexTexTransformLocation; }
+
+private:
+ int m_matrixLocation;
+ int m_pointLocation;
+ int m_vertexTexTransformLocation;
+};
+
+class VertexShaderVideoTransform {
+public:
+ VertexShaderVideoTransform();
+
+ bool init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ std::string getShaderString() const;
+
+ int matrixLocation() const { return m_matrixLocation; }
+ int texMatrixLocation() const { return m_texMatrixLocation; }
+
+private:
+ int m_matrixLocation;
+ int m_texMatrixLocation;
+};
+
+class FragmentTexAlphaBinding {
+public:
+ FragmentTexAlphaBinding();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ int alphaLocation() const { return m_alphaLocation; }
+ int edgeLocation() const { return -1; }
+ int fragmentTexTransformLocation() const { return -1; }
+ int samplerLocation() const { return m_samplerLocation; }
+
+private:
+ int m_samplerLocation;
+ int m_alphaLocation;
+};
+
+class FragmentTexOpaqueBinding {
+public:
+ FragmentTexOpaqueBinding();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ int alphaLocation() const { return -1; }
+ int edgeLocation() const { return -1; }
+ int fragmentTexTransformLocation() const { return -1; }
+ int samplerLocation() const { return m_samplerLocation; }
+
+private:
+ int m_samplerLocation;
+};
+
+class FragmentShaderRGBATexVaryingAlpha : public FragmentTexOpaqueBinding {
+public:
+ std::string getShaderString() const;
+};
+
+class FragmentShaderRGBATexAlpha : public FragmentTexAlphaBinding {
+public:
+ std::string getShaderString() const;
+};
+
+class FragmentShaderRGBATexRectVaryingAlpha : public FragmentTexAlphaBinding {
+public:
+ std::string getShaderString() const;
+};
+
+class FragmentShaderRGBATexOpaque : public FragmentTexOpaqueBinding {
+public:
+ std::string getShaderString() const;
+};
+
+class FragmentShaderRGBATex : public FragmentTexOpaqueBinding {
+public:
+ std::string getShaderString() const;
+};
+
+// Swizzles the red and blue component of sampled texel with alpha.
+class FragmentShaderRGBATexSwizzleAlpha : public FragmentTexAlphaBinding {
+public:
+ std::string getShaderString() const;
+};
+
+// Swizzles the red and blue component of sampled texel without alpha.
+class FragmentShaderRGBATexSwizzleOpaque : public FragmentTexOpaqueBinding {
+public:
+ std::string getShaderString() const;
+};
+
+// Fragment shader for external textures.
+class FragmentShaderOESImageExternal : public FragmentTexAlphaBinding {
+public:
+ std::string getShaderString() const;
+ bool init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+private:
+ int m_samplerLocation;
+};
+
+class FragmentShaderRGBATexAlphaAA {
+public:
+ FragmentShaderRGBATexAlphaAA();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ std::string getShaderString() const;
+
+ int alphaLocation() const { return m_alphaLocation; }
+ int samplerLocation() const { return m_samplerLocation; }
+ int edgeLocation() const { return m_edgeLocation; }
+
+private:
+ int m_samplerLocation;
+ int m_alphaLocation;
+ int m_edgeLocation;
+};
+
+class FragmentTexClampAlphaAABinding {
+public:
+ FragmentTexClampAlphaAABinding();
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ int alphaLocation() const { return m_alphaLocation; }
+ int samplerLocation() const { return m_samplerLocation; }
+ int fragmentTexTransformLocation() const { return m_fragmentTexTransformLocation; }
+ int edgeLocation() const { return m_edgeLocation; }
+
+private:
+ int m_samplerLocation;
+ int m_alphaLocation;
+ int m_fragmentTexTransformLocation;
+ int m_edgeLocation;
+};
+
+class FragmentShaderRGBATexClampAlphaAA : public FragmentTexClampAlphaAABinding {
+public:
+ std::string getShaderString() const;
+};
+
+// Swizzles the red and blue component of sampled texel.
+class FragmentShaderRGBATexClampSwizzleAlphaAA : public FragmentTexClampAlphaAABinding {
+public:
+ std::string getShaderString() const;
+};
+
+class FragmentShaderRGBATexAlphaMask {
+public:
+ FragmentShaderRGBATexAlphaMask();
+ std::string getShaderString() const;
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ int alphaLocation() const { return m_alphaLocation; }
+ int samplerLocation() const { return m_samplerLocation; }
+ int maskSamplerLocation() const { return m_maskSamplerLocation; }
+ int maskTexCoordScaleLocation() const { return m_maskTexCoordScaleLocation; }
+ int maskTexCoordOffsetLocation() const { return m_maskTexCoordOffsetLocation; }
+
+private:
+ int m_samplerLocation;
+ int m_maskSamplerLocation;
+ int m_alphaLocation;
+ int m_maskTexCoordScaleLocation;
+ int m_maskTexCoordOffsetLocation;
+};
+
+class FragmentShaderRGBATexAlphaMaskAA {
+public:
+ FragmentShaderRGBATexAlphaMaskAA();
+ std::string getShaderString() const;
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ int alphaLocation() const { return m_alphaLocation; }
+ int samplerLocation() const { return m_samplerLocation; }
+ int maskSamplerLocation() const { return m_maskSamplerLocation; }
+ int edgeLocation() const { return m_edgeLocation; }
+ int maskTexCoordScaleLocation() const { return m_maskTexCoordScaleLocation; }
+ int maskTexCoordOffsetLocation() const { return m_maskTexCoordOffsetLocation; }
+
+private:
+ int m_samplerLocation;
+ int m_maskSamplerLocation;
+ int m_alphaLocation;
+ int m_edgeLocation;
+ int m_maskTexCoordScaleLocation;
+ int m_maskTexCoordOffsetLocation;
+};
+
+class FragmentShaderYUVVideo {
+public:
+ FragmentShaderYUVVideo();
+ std::string getShaderString() const;
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+
+ int yTextureLocation() const { return m_yTextureLocation; }
+ int uTextureLocation() const { return m_uTextureLocation; }
+ int vTextureLocation() const { return m_vTextureLocation; }
+ int alphaLocation() const { return m_alphaLocation; }
+ int yuvMatrixLocation() const { return m_yuvMatrixLocation; }
+ int yuvAdjLocation() const { return m_yuvAdjLocation; }
+
+private:
+ int m_yTextureLocation;
+ int m_uTextureLocation;
+ int m_vTextureLocation;
+ int m_alphaLocation;
+ int m_yuvMatrixLocation;
+ int m_yuvAdjLocation;
+};
+
+class FragmentShaderColor {
+public:
+ FragmentShaderColor();
+ std::string getShaderString() const;
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ int edgeLocation() const { return -1; }
+ int colorLocation() const { return m_colorLocation; }
+
+private:
+ int m_colorLocation;
+};
+
+class FragmentShaderColorAA {
+public:
+ FragmentShaderColorAA();
+ std::string getShaderString() const;
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ int edgeLocation() const { return m_edgeLocation; }
+ int colorLocation() const { return m_colorLocation; }
+
+private:
+ int m_edgeLocation;
+ int m_colorLocation;
+};
+
+class FragmentShaderCheckerboard {
+public:
+ FragmentShaderCheckerboard();
+ std::string getShaderString() const;
+
+ void init(WebKit::WebGraphicsContext3D*, unsigned program, bool usingBindUniform, int* baseUniformIndex);
+ int alphaLocation() const { return m_alphaLocation; }
+ int texTransformLocation() const { return m_texTransformLocation; }
+ int frequencyLocation() const { return m_frequencyLocation; }
+ int colorLocation() const { return m_colorLocation; }
+private:
+ int m_alphaLocation;
+ int m_texTransformLocation;
+ int m_frequencyLocation;
+ int m_colorLocation;
+};
+
+} // namespace cc
+
+#endif // CC_OUTPUT_SHADER_H_
diff --git a/cc/output/software_frame_data.cc b/cc/output/software_frame_data.cc
new file mode 100644
index 0000000..ffecc3b
--- /dev/null
+++ b/cc/output/software_frame_data.cc
@@ -0,0 +1,15 @@
+// Copyright 2013 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/output/software_frame_data.h"
+
+namespace cc {
+
+SoftwareFrameData::SoftwareFrameData()
+ : content_dib(TransportDIB::DefaultHandleValue()) {
+}
+
+SoftwareFrameData::~SoftwareFrameData() {}
+
+} // namespace cc
diff --git a/cc/output/software_frame_data.h b/cc/output/software_frame_data.h
new file mode 100644
index 0000000..aef9635
--- /dev/null
+++ b/cc/output/software_frame_data.h
@@ -0,0 +1,27 @@
+// Copyright 2013 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_OUTPUT_SOFTWARE_FRAME_DATA_H_
+#define CC_OUTPUT_SOFTWARE_FRAME_DATA_H_
+
+#include "base/basictypes.h"
+#include "cc/base/cc_export.h"
+#include "ui/gfx/rect.h"
+#include "ui/surface/transport_dib.h"
+
+namespace cc {
+
+class CC_EXPORT SoftwareFrameData {
+ public:
+ SoftwareFrameData();
+ ~SoftwareFrameData();
+
+ gfx::Size size;
+ gfx::Rect damage_rect;
+ TransportDIB::Handle content_dib;
+};
+
+} // namespace cc
+
+#endif // CC_OUTPUT_SOFTWARE_FRAME_DATA_H_
diff --git a/cc/output/software_output_device.cc b/cc/output/software_output_device.cc
new file mode 100644
index 0000000..174fb4b
--- /dev/null
+++ b/cc/output/software_output_device.cc
@@ -0,0 +1,61 @@
+// Copyright 2013 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/output/software_output_device.h"
+
+#include "base/logging.h"
+#include "cc/output/software_frame_data.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkDevice.h"
+
+namespace cc {
+
+SoftwareOutputDevice::SoftwareOutputDevice() {}
+
+SoftwareOutputDevice::~SoftwareOutputDevice() {}
+
+void SoftwareOutputDevice::Resize(const gfx::Size& viewport_size) {
+ if (viewport_size_ == viewport_size)
+ return;
+
+ viewport_size_ = viewport_size;
+ device_ = skia::AdoptRef(new SkDevice(SkBitmap::kARGB_8888_Config,
+ viewport_size.width(), viewport_size.height(), true));
+ canvas_ = skia::AdoptRef(new SkCanvas(device_.get()));
+}
+
+SkCanvas* SoftwareOutputDevice::BeginPaint(const gfx::Rect& damage_rect) {
+ DCHECK(device_);
+ damage_rect_ = damage_rect;
+ return canvas_.get();
+}
+
+void SoftwareOutputDevice::EndPaint(SoftwareFrameData* frame_data) {
+ DCHECK(device_);
+ if (frame_data) {
+ frame_data->damage_rect = damage_rect_;
+ frame_data->content_dib = TransportDIB::DefaultHandleValue();
+ }
+}
+
+void SoftwareOutputDevice::CopyToBitmap(
+ const gfx::Rect& rect, SkBitmap* output) {
+ DCHECK(device_);
+ SkIRect invertRect = SkIRect::MakeXYWH(
+ rect.x(), viewport_size_.height() - rect.bottom(),
+ rect.width(), rect.height());
+ const SkBitmap& bitmap = device_->accessBitmap(false);
+ bitmap.extractSubset(output, invertRect);
+}
+
+void SoftwareOutputDevice::Scroll(
+ const gfx::Vector2d& delta, const gfx::Rect& clip_rect) {
+ NOTIMPLEMENTED();
+}
+
+void SoftwareOutputDevice::ReclaimDIB(TransportDIB::Handle handle) {
+ NOTIMPLEMENTED();
+}
+
+} // namespace cc
diff --git a/cc/output/software_output_device.h b/cc/output/software_output_device.h
new file mode 100644
index 0000000..5ee08e2
--- /dev/null
+++ b/cc/output/software_output_device.h
@@ -0,0 +1,56 @@
+// 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_OUTPUT_SOFTWARE_OUTPUT_DEVICE_H_
+#define CC_OUTPUT_SOFTWARE_OUTPUT_DEVICE_H_
+
+#include "base/basictypes.h"
+#include "cc/base/cc_export.h"
+#include "skia/ext/refptr.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+#include "ui/gfx/vector2d.h"
+#include "ui/surface/transport_dib.h"
+
+class SkBitmap;
+class SkDevice;
+class SkCanvas;
+
+namespace cc {
+
+class SoftwareFrameData;
+
+// This is a "tear-off" class providing software drawing support to
+// OutputSurface, such as to a platform-provided window framebuffer.
+class CC_EXPORT SoftwareOutputDevice {
+ public:
+
+ SoftwareOutputDevice();
+ virtual ~SoftwareOutputDevice();
+
+ // SoftwareOutputDevice implementation
+ virtual void Resize(const gfx::Size& size);
+
+ virtual SkCanvas* BeginPaint(const gfx::Rect& damage_rect);
+ virtual void EndPaint(SoftwareFrameData* frame_data=NULL);
+
+ virtual void CopyToBitmap(const gfx::Rect& rect, SkBitmap* output);
+ virtual void Scroll(const gfx::Vector2d& delta,
+ const gfx::Rect& clip_rect);
+
+ // TODO(skaslev) Remove this after UberCompositor lands.
+ virtual void ReclaimDIB(TransportDIB::Handle handle);
+
+protected:
+ gfx::Size viewport_size_;
+ gfx::Rect damage_rect_;
+ skia::RefPtr<SkDevice> device_;
+ skia::RefPtr<SkCanvas> canvas_;
+
+ DISALLOW_COPY_AND_ASSIGN(SoftwareOutputDevice);
+};
+
+} // namespace cc
+
+#endif // CC_OUTPUT_SOFTWARE_OUTPUT_DEVICE_H_
diff --git a/cc/output/software_renderer.cc b/cc/output/software_renderer.cc
new file mode 100644
index 0000000..036c611
--- /dev/null
+++ b/cc/output/software_renderer.cc
@@ -0,0 +1,418 @@
+// 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/output/software_renderer.h"
+
+#include "base/debug/trace_event.h"
+#include "cc/base/math_util.h"
+#include "cc/debug_border_draw_quad.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/output/compositor_frame_ack.h"
+#include "cc/output/compositor_frame_metadata.h"
+#include "cc/output/output_surface.h"
+#include "cc/output/software_output_device.h"
+#include "cc/render_pass_draw_quad.h"
+#include "cc/solid_color_draw_quad.h"
+#include "cc/texture_draw_quad.h"
+#include "cc/tile_draw_quad.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkMatrix.h"
+#include "third_party/skia/include/core/SkShader.h"
+#include "third_party/skia/include/effects/SkLayerRasterizer.h"
+#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/skia_util.h"
+#include "ui/gfx/transform.h"
+
+namespace cc {
+
+namespace {
+
+void ToSkMatrix(SkMatrix* flattened, const gfx::Transform& m) {
+ // Convert from 4x4 to 3x3 by dropping the third row and column.
+ flattened->set(0, SkDoubleToScalar(m.matrix().getDouble(0, 0)));
+ flattened->set(1, SkDoubleToScalar(m.matrix().getDouble(0, 1)));
+ flattened->set(2, SkDoubleToScalar(m.matrix().getDouble(0, 3)));
+ flattened->set(3, SkDoubleToScalar(m.matrix().getDouble(1, 0)));
+ flattened->set(4, SkDoubleToScalar(m.matrix().getDouble(1, 1)));
+ flattened->set(5, SkDoubleToScalar(m.matrix().getDouble(1, 3)));
+ flattened->set(6, SkDoubleToScalar(m.matrix().getDouble(3, 0)));
+ flattened->set(7, SkDoubleToScalar(m.matrix().getDouble(3, 1)));
+ flattened->set(8, SkDoubleToScalar(m.matrix().getDouble(3, 3)));
+}
+
+bool IsScaleAndTranslate(const SkMatrix& matrix) {
+ return SkScalarNearlyZero(matrix[SkMatrix::kMSkewX]) &&
+ SkScalarNearlyZero(matrix[SkMatrix::kMSkewY]) &&
+ SkScalarNearlyZero(matrix[SkMatrix::kMPersp0]) &&
+ SkScalarNearlyZero(matrix[SkMatrix::kMPersp1]) &&
+ SkScalarNearlyZero(matrix[SkMatrix::kMPersp2] - 1.0f);
+}
+
+} // anonymous namespace
+
+scoped_ptr<SoftwareRenderer> SoftwareRenderer::Create(
+ RendererClient* client,
+ OutputSurface* output_surface,
+ ResourceProvider* resource_provider) {
+ return make_scoped_ptr(
+ new SoftwareRenderer(client, output_surface, resource_provider));
+}
+
+SoftwareRenderer::SoftwareRenderer(RendererClient* client,
+ OutputSurface* output_surface,
+ ResourceProvider* resource_provider)
+ : DirectRenderer(client, resource_provider),
+ visible_(true),
+ is_scissor_enabled_(false),
+ output_surface_(output_surface),
+ output_device_(output_surface->software_device()),
+ current_canvas_(NULL) {
+ resource_provider_->set_default_resource_type(ResourceProvider::Bitmap);
+
+ capabilities_.max_texture_size = resource_provider_->max_texture_size();
+ capabilities_.best_texture_format = resource_provider_->best_texture_format();
+ capabilities_.using_set_visibility = true;
+ // The updater can access bitmaps while the SoftwareRenderer is using them.
+ capabilities_.allow_partial_texture_updates = true;
+ capabilities_.using_partial_swap = true;
+ if (client_->HasImplThread())
+ capabilities_.using_swap_complete_callback = true;
+ compositor_frame_.software_frame_data.reset(new SoftwareFrameData());
+
+ ViewportChanged();
+}
+
+SoftwareRenderer::~SoftwareRenderer() {}
+
+const RendererCapabilities& SoftwareRenderer::Capabilities() const {
+ return capabilities_;
+}
+
+void SoftwareRenderer::ViewportChanged() {
+ output_device_->Resize(ViewportSize());
+}
+
+void SoftwareRenderer::BeginDrawingFrame(DrawingFrame& frame) {
+ TRACE_EVENT0("cc", "SoftwareRenderer::BeginDrawingFrame");
+ root_canvas_ = output_device_->BeginPaint(
+ gfx::ToEnclosingRect(frame.root_damage_rect));
+}
+
+void SoftwareRenderer::FinishDrawingFrame(DrawingFrame& frame) {
+ TRACE_EVENT0("cc", "SoftwareRenderer::FinishDrawingFrame");
+ current_framebuffer_lock_.reset();
+ current_canvas_ = NULL;
+ root_canvas_ = NULL;
+ if (Settings().compositorFrameMessage) {
+ compositor_frame_.metadata = client_->MakeCompositorFrameMetadata();
+ output_device_->EndPaint(compositor_frame_.software_frame_data.get());
+ } else {
+ output_device_->EndPaint();
+ }
+}
+
+bool SoftwareRenderer::SwapBuffers() {
+ if (Settings().compositorFrameMessage)
+ output_surface_->SendFrameToParentCompositor(&compositor_frame_);
+ return true;
+}
+
+void SoftwareRenderer::ReceiveCompositorFrameAck(
+ const CompositorFrameAck& ack) {
+ if (client_->HasImplThread())
+ client_->OnSwapBuffersComplete();
+ output_device_->ReclaimDIB(ack.last_content_dib);
+}
+
+bool SoftwareRenderer::FlippedFramebuffer() const {
+ return false;
+}
+
+void SoftwareRenderer::EnsureScissorTestEnabled() {
+ is_scissor_enabled_ = true;
+ SetClipRect(scissor_rect_);
+}
+
+void SoftwareRenderer::EnsureScissorTestDisabled() {
+ // There is no explicit notion of enabling/disabling scissoring in software
+ // rendering, but the underlying effect we want is to clear any existing
+ // clipRect on the current SkCanvas. This is done by setting clipRect to
+ // the viewport's dimensions.
+ is_scissor_enabled_ = false;
+ SkDevice* device = current_canvas_->getDevice();
+ SetClipRect(gfx::Rect(device->width(), device->height()));
+}
+
+void SoftwareRenderer::Finish() {}
+
+void SoftwareRenderer::BindFramebufferToOutputSurface(DrawingFrame& frame) {
+ current_framebuffer_lock_.reset();
+ current_canvas_ = root_canvas_;
+}
+
+bool SoftwareRenderer::BindFramebufferToTexture(
+ DrawingFrame& frame,
+ const ScopedResource* texture,
+ gfx::Rect framebuffer_rect) {
+ current_framebuffer_lock_ = make_scoped_ptr(
+ new ResourceProvider::ScopedWriteLockSoftware(
+ resource_provider_, texture->id()));
+ current_canvas_ = current_framebuffer_lock_->sk_canvas();
+ InitializeMatrices(frame, framebuffer_rect, false);
+ SetDrawViewportSize(framebuffer_rect.size());
+
+ return true;
+}
+
+void SoftwareRenderer::SetScissorTestRect(gfx::Rect scissor_rect) {
+ is_scissor_enabled_ = true;
+ scissor_rect_ = scissor_rect;
+ SetClipRect(scissor_rect);
+}
+
+void SoftwareRenderer::SetClipRect(gfx::Rect rect) {
+ // Skia applies the current matrix to clip rects so we reset it temporary.
+ SkMatrix current_matrix = current_canvas_->getTotalMatrix();
+ current_canvas_->resetMatrix();
+ current_canvas_->clipRect(gfx::RectToSkRect(rect), SkRegion::kReplace_Op);
+ current_canvas_->setMatrix(current_matrix);
+}
+
+void SoftwareRenderer::ClearCanvas(SkColor color) {
+ // SkCanvas::clear doesn't respect the current clipping region
+ // so we SkCanvas::drawColor instead if scissoring is active.
+ if (is_scissor_enabled_)
+ current_canvas_->drawColor(color, SkXfermode::kSrc_Mode);
+ else
+ current_canvas_->clear(color);
+}
+
+void SoftwareRenderer::ClearFramebuffer(DrawingFrame& frame) {
+ if (frame.current_render_pass->has_transparent_background) {
+ ClearCanvas(SkColorSetARGB(0, 0, 0, 0));
+ } else {
+#ifndef NDEBUG
+ // On DEBUG builds, opaque render passes are cleared to blue
+ // to easily see regions that were not drawn on the screen.
+ ClearCanvas(SkColorSetARGB(255, 0, 0, 255));
+#endif
+ }
+}
+
+void SoftwareRenderer::SetDrawViewportSize(gfx::Size viewport_size) {}
+
+bool SoftwareRenderer::IsSoftwareResource(
+ ResourceProvider::ResourceId resource_id) const {
+ switch (resource_provider_->GetResourceType(resource_id)) {
+ case ResourceProvider::GLTexture:
+ return false;
+ case ResourceProvider::Bitmap:
+ return true;
+ }
+
+ LOG(FATAL) << "Invalid resource type.";
+ return false;
+}
+
+void SoftwareRenderer::DoDrawQuad(DrawingFrame& frame, const DrawQuad* quad) {
+ TRACE_EVENT0("cc", "SoftwareRenderer::DoDrawQuad");
+ gfx::Transform quad_rect_matrix;
+ QuadRectTransform(&quad_rect_matrix, quad->quadTransform(), quad->rect);
+ gfx::Transform contents_device_transform =
+ frame.window_matrix * frame.projection_matrix * quad_rect_matrix;
+ contents_device_transform.FlattenTo2d();
+ SkMatrix sk_device_matrix;
+ ToSkMatrix(&sk_device_matrix, contents_device_transform);
+ current_canvas_->setMatrix(sk_device_matrix);
+
+ current_paint_.reset();
+ if (!IsScaleAndTranslate(sk_device_matrix)) {
+ current_paint_.setAntiAlias(true);
+ current_paint_.setFilterBitmap(true);
+ }
+
+ if (quad->ShouldDrawWithBlending()) {
+ current_paint_.setAlpha(quad->opacity() * 255);
+ current_paint_.setXfermodeMode(SkXfermode::kSrcOver_Mode);
+ } else {
+ current_paint_.setXfermodeMode(SkXfermode::kSrc_Mode);
+ }
+
+ switch (quad->material) {
+ case DrawQuad::DEBUG_BORDER:
+ DrawDebugBorderQuad(frame, DebugBorderDrawQuad::MaterialCast(quad));
+ break;
+ case DrawQuad::SOLID_COLOR:
+ DrawSolidColorQuad(frame, SolidColorDrawQuad::MaterialCast(quad));
+ break;
+ case DrawQuad::TEXTURE_CONTENT:
+ DrawTextureQuad(frame, TextureDrawQuad::MaterialCast(quad));
+ break;
+ case DrawQuad::TILED_CONTENT:
+ DrawTileQuad(frame, TileDrawQuad::MaterialCast(quad));
+ break;
+ case DrawQuad::RENDER_PASS:
+ DrawRenderPassQuad(frame, RenderPassDrawQuad::MaterialCast(quad));
+ break;
+ default:
+ DrawUnsupportedQuad(frame, quad);
+ break;
+ }
+
+ current_canvas_->resetMatrix();
+}
+
+void SoftwareRenderer::DrawDebugBorderQuad(const DrawingFrame& frame,
+ const DebugBorderDrawQuad* quad) {
+ // We need to apply the matrix manually to have pixel-sized stroke width.
+ SkPoint vertices[4];
+ gfx::RectFToSkRect(QuadVertexRect()).toQuad(vertices);
+ SkPoint transformedVertices[4];
+ current_canvas_->getTotalMatrix().mapPoints(transformedVertices, vertices, 4);
+ current_canvas_->resetMatrix();
+
+ current_paint_.setColor(quad->color);
+ current_paint_.setAlpha(quad->opacity() * SkColorGetA(quad->color));
+ current_paint_.setStyle(SkPaint::kStroke_Style);
+ current_paint_.setStrokeWidth(quad->width);
+ current_canvas_->drawPoints(SkCanvas::kPolygon_PointMode,
+ 4, transformedVertices, current_paint_);
+}
+
+void SoftwareRenderer::DrawSolidColorQuad(const DrawingFrame& frame,
+ const SolidColorDrawQuad* quad) {
+ current_paint_.setColor(quad->color);
+ current_paint_.setAlpha(quad->opacity() * SkColorGetA(quad->color));
+ current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()),
+ current_paint_);
+}
+
+void SoftwareRenderer::DrawTextureQuad(const DrawingFrame& frame,
+ const TextureDrawQuad* quad) {
+ if (!IsSoftwareResource(quad->resource_id)) {
+ DrawUnsupportedQuad(frame, quad);
+ return;
+ }
+
+ // FIXME: Add support for non-premultiplied alpha.
+ ResourceProvider::ScopedReadLockSoftware lock(resource_provider_,
+ quad->resource_id);
+ const SkBitmap* bitmap = lock.sk_bitmap();
+ gfx::RectF uv_rect = gfx::ScaleRect(gfx::BoundingRect(quad->uv_top_left,
+ quad->uv_bottom_right),
+ bitmap->width(),
+ bitmap->height());
+ SkRect sk_uv_rect = gfx::RectFToSkRect(uv_rect);
+ if (quad->flipped)
+ current_canvas_->scale(1, -1);
+ current_canvas_->drawBitmapRectToRect(*bitmap, &sk_uv_rect,
+ gfx::RectFToSkRect(QuadVertexRect()),
+ &current_paint_);
+}
+
+void SoftwareRenderer::DrawTileQuad(const DrawingFrame& frame,
+ const TileDrawQuad* quad) {
+ DCHECK(IsSoftwareResource(quad->resource_id));
+ ResourceProvider::ScopedReadLockSoftware lock(resource_provider_,
+ quad->resource_id);
+
+ SkRect uv_rect = gfx::RectFToSkRect(quad->tex_coord_rect);
+ current_paint_.setFilterBitmap(true);
+ current_canvas_->drawBitmapRectToRect(*lock.sk_bitmap(), &uv_rect,
+ gfx::RectFToSkRect(QuadVertexRect()),
+ &current_paint_);
+}
+
+void SoftwareRenderer::DrawRenderPassQuad(const DrawingFrame& frame,
+ const RenderPassDrawQuad* quad) {
+ CachedResource* content_texture =
+ render_pass_textures_.get(quad->render_pass_id);
+ if (!content_texture || !content_texture->id())
+ return;
+
+ DCHECK(IsSoftwareResource(content_texture->id()));
+ ResourceProvider::ScopedReadLockSoftware lock(resource_provider_,
+ content_texture->id());
+
+ SkRect dest_rect = gfx::RectFToSkRect(QuadVertexRect());
+ SkRect content_rect = SkRect::MakeWH(quad->rect.width(), quad->rect.height());
+
+ SkMatrix content_mat;
+ content_mat.setRectToRect(content_rect, dest_rect,
+ SkMatrix::kFill_ScaleToFit);
+
+ const SkBitmap* content = lock.sk_bitmap();
+ skia::RefPtr<SkShader> shader = skia::AdoptRef(
+ SkShader::CreateBitmapShader(*content,
+ SkShader::kClamp_TileMode,
+ SkShader::kClamp_TileMode));
+ shader->setLocalMatrix(content_mat);
+ current_paint_.setShader(shader.get());
+
+ SkImageFilter* filter = quad->filter.get();
+ if (filter)
+ current_paint_.setImageFilter(filter);
+
+ if (quad->mask_resource_id) {
+ ResourceProvider::ScopedReadLockSoftware mask_lock(resource_provider_,
+ quad->mask_resource_id);
+
+ const SkBitmap* mask = mask_lock.sk_bitmap();
+
+ SkRect mask_rect = SkRect::MakeXYWH(
+ quad->mask_uv_rect.x() * mask->width(),
+ quad->mask_uv_rect.y() * mask->height(),
+ quad->mask_uv_rect.width() * mask->width(),
+ quad->mask_uv_rect.height() * mask->height());
+
+ SkMatrix mask_mat;
+ mask_mat.setRectToRect(mask_rect, dest_rect, SkMatrix::kFill_ScaleToFit);
+
+ skia::RefPtr<SkShader> mask_shader = skia::AdoptRef(
+ SkShader::CreateBitmapShader(*mask,
+ SkShader::kClamp_TileMode,
+ SkShader::kClamp_TileMode));
+ mask_shader->setLocalMatrix(mask_mat);
+
+ SkPaint mask_paint;
+ mask_paint.setShader(mask_shader.get());
+
+ skia::RefPtr<SkLayerRasterizer> mask_rasterizer =
+ skia::AdoptRef(new SkLayerRasterizer);
+ mask_rasterizer->addLayer(mask_paint);
+
+ current_paint_.setRasterizer(mask_rasterizer.get());
+ current_canvas_->drawRect(dest_rect, current_paint_);
+ } else {
+ // FIXME: Apply background filters and blend with content
+ current_canvas_->drawRect(dest_rect, current_paint_);
+ }
+}
+
+void SoftwareRenderer::DrawUnsupportedQuad(const DrawingFrame& frame,
+ const DrawQuad* quad) {
+ current_paint_.setColor(SK_ColorMAGENTA);
+ current_paint_.setAlpha(quad->opacity() * 255);
+ current_canvas_->drawRect(gfx::RectFToSkRect(QuadVertexRect()),
+ current_paint_);
+}
+
+void SoftwareRenderer::GetFramebufferPixels(void* pixels, gfx::Rect rect) {
+ TRACE_EVENT0("cc", "SoftwareRenderer::GetFramebufferPixels");
+ SkBitmap subset_bitmap;
+ output_device_->CopyToBitmap(rect, &subset_bitmap);
+ subset_bitmap.copyPixelsTo(pixels,
+ 4 * rect.width() * rect.height(),
+ 4 * rect.width());
+}
+
+void SoftwareRenderer::SetVisible(bool visible) {
+ if (visible_ == visible)
+ return;
+ visible_ = visible;
+}
+
+} // namespace cc
diff --git a/cc/output/software_renderer.h b/cc/output/software_renderer.h
new file mode 100644
index 0000000..5e2c4c1
--- /dev/null
+++ b/cc/output/software_renderer.h
@@ -0,0 +1,104 @@
+// 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_OUTPUT_SOFTWARE_RENDERER_H_
+#define CC_OUTPUT_SOFTWARE_RENDERER_H_
+
+#include "base/basictypes.h"
+#include "cc/base/cc_export.h"
+#include "cc/output/compositor_frame.h"
+#include "cc/output/direct_renderer.h"
+
+namespace cc {
+
+class OutputSurface;
+class SoftwareOutputDevice;
+class DebugBorderDrawQuad;
+class RendererClient;
+class RenderPassDrawQuad;
+class ResourceProvider;
+class SolidColorDrawQuad;
+class TextureDrawQuad;
+class TileDrawQuad;
+
+class CC_EXPORT SoftwareRenderer : public DirectRenderer {
+ public:
+ static scoped_ptr<SoftwareRenderer> Create(
+ RendererClient* client,
+ OutputSurface* output_surface,
+ ResourceProvider* resource_provider);
+
+ virtual ~SoftwareRenderer();
+ virtual const RendererCapabilities& Capabilities() const OVERRIDE;
+ virtual void ViewportChanged() OVERRIDE;
+ virtual void Finish() OVERRIDE;
+ virtual bool SwapBuffers() OVERRIDE;
+ virtual void GetFramebufferPixels(void* pixels, gfx::Rect rect) OVERRIDE;
+ virtual void SetVisible(bool visible) OVERRIDE;
+ virtual void SendManagedMemoryStats(
+ size_t bytes_visible,
+ size_t bytes_visible_and_nearby,
+ size_t bytes_allocated) OVERRIDE {}
+ virtual void ReceiveCompositorFrameAck(
+ const CompositorFrameAck& ack) OVERRIDE;
+
+ protected:
+ virtual void BindFramebufferToOutputSurface(DrawingFrame& frame) OVERRIDE;
+ virtual bool BindFramebufferToTexture(
+ DrawingFrame& frame,
+ const ScopedResource* texture,
+ gfx::Rect framebuffer_rect) OVERRIDE;
+ virtual void SetDrawViewportSize(gfx::Size viewport_size) OVERRIDE;
+ virtual void SetScissorTestRect(gfx::Rect scissor_rect) OVERRIDE;
+ virtual void ClearFramebuffer(DrawingFrame& frame) OVERRIDE;
+ virtual void DoDrawQuad(DrawingFrame& frame, const DrawQuad* quad) OVERRIDE;
+ virtual void BeginDrawingFrame(DrawingFrame& frame) OVERRIDE;
+ virtual void FinishDrawingFrame(DrawingFrame& frame) OVERRIDE;
+ virtual bool FlippedFramebuffer() const OVERRIDE;
+ virtual void EnsureScissorTestEnabled() OVERRIDE;
+ virtual void EnsureScissorTestDisabled() OVERRIDE;
+
+ private:
+ SoftwareRenderer(
+ RendererClient* client,
+ OutputSurface* output_surface,
+ ResourceProvider* resource_provider);
+
+ void ClearCanvas(SkColor color);
+ void SetClipRect(gfx::Rect rect);
+ bool IsSoftwareResource(ResourceProvider::ResourceId resource_id) const;
+
+ void DrawDebugBorderQuad(const DrawingFrame& frame,
+ const DebugBorderDrawQuad* quad);
+ void DrawSolidColorQuad(const DrawingFrame& frame,
+ const SolidColorDrawQuad* quad);
+ void DrawTextureQuad(const DrawingFrame& frame,
+ const TextureDrawQuad* quad);
+ void DrawTileQuad(const DrawingFrame& frame,
+ const TileDrawQuad* quad);
+ void DrawRenderPassQuad(const DrawingFrame& frame,
+ const RenderPassDrawQuad* quad);
+ void DrawUnsupportedQuad(const DrawingFrame& frame,
+ const DrawQuad* quad);
+
+ RendererCapabilities capabilities_;
+ bool visible_;
+ bool is_scissor_enabled_;
+ gfx::Rect scissor_rect_;
+
+ OutputSurface* output_surface_;
+ SoftwareOutputDevice* output_device_;
+ SkCanvas* root_canvas_;
+ SkCanvas* current_canvas_;
+ SkPaint current_paint_;
+ scoped_ptr<ResourceProvider::ScopedWriteLockSoftware>
+ current_framebuffer_lock_;
+ CompositorFrame compositor_frame_;
+
+ DISALLOW_COPY_AND_ASSIGN(SoftwareRenderer);
+};
+
+}
+
+#endif // CC_OUTPUT_SOFTWARE_RENDERER_H_
diff --git a/cc/output/software_renderer_unittest.cc b/cc/output/software_renderer_unittest.cc
new file mode 100644
index 0000000..93b82b0
--- /dev/null
+++ b/cc/output/software_renderer_unittest.cc
@@ -0,0 +1,207 @@
+// 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/output/software_renderer.h"
+
+#include "cc/output/compositor_frame_metadata.h"
+#include "cc/output/software_output_device.h"
+#include "cc/quad_sink.h"
+#include "cc/render_pass.h"
+#include "cc/render_pass_draw_quad.h"
+#include "cc/solid_color_draw_quad.h"
+#include "cc/test/animation_test_common.h"
+#include "cc/test/fake_output_surface.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"
+
+using namespace WebKit;
+
+namespace cc {
+namespace {
+
+class SoftwareRendererTest : public testing::Test, public RendererClient {
+public:
+ SoftwareRendererTest()
+ : m_shouldClearRootRenderPass(true)
+ {
+ }
+
+ void initializeRenderer() {
+ m_outputSurface = FakeOutputSurface::CreateSoftware(make_scoped_ptr(new SoftwareOutputDevice));
+ resource_provider_ = ResourceProvider::Create(m_outputSurface.get());
+ m_renderer = SoftwareRenderer::Create(this, m_outputSurface.get(), resourceProvider());
+ }
+
+ ResourceProvider* resourceProvider() const { return resource_provider_.get(); }
+ SoftwareRenderer* renderer() const { return m_renderer.get(); }
+ void setViewportSize(const gfx::Size& viewportSize) { m_viewportSize = viewportSize; }
+ void setShouldClearRootRenderPass(bool clearRootRenderPass) { m_shouldClearRootRenderPass = clearRootRenderPass; }
+
+ // RendererClient implementation.
+ virtual gfx::Size DeviceViewportSize() const OVERRIDE { return m_viewportSize; }
+ virtual const LayerTreeSettings& Settings() const OVERRIDE { return m_settings; }
+ virtual void DidLoseOutputSurface() OVERRIDE { }
+ virtual void OnSwapBuffersComplete() OVERRIDE { }
+ virtual void SetFullRootLayerDamage() OVERRIDE { }
+ 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; }
+ virtual CompositorFrameMetadata MakeCompositorFrameMetadata() const
+ OVERRIDE { return CompositorFrameMetadata(); }
+
+protected:
+ scoped_ptr<FakeOutputSurface> m_outputSurface;
+ scoped_ptr<ResourceProvider> resource_provider_;
+ scoped_ptr<SoftwareRenderer> m_renderer;
+ gfx::Size m_viewportSize;
+ LayerTreeSettings m_settings;
+ bool m_shouldClearRootRenderPass;
+};
+
+TEST_F(SoftwareRendererTest, solidColorQuad)
+{
+ gfx::Size outerSize(100, 100);
+ int outerPixels = outerSize.width() * outerSize.height();
+ gfx::Size innerSize(98, 98);
+ gfx::Rect outerRect(outerSize);
+ gfx::Rect innerRect(gfx::Point(1, 1), innerSize);
+ setViewportSize(outerSize);
+
+ initializeRenderer();
+
+ scoped_ptr<SharedQuadState> sharedQuadState = SharedQuadState::Create();
+ sharedQuadState->SetAll(gfx::Transform(), outerSize, outerRect, outerRect, false, 1.0);
+ RenderPass::Id root_render_passId = RenderPass::Id(1, 1);
+ scoped_ptr<TestRenderPass> root_render_pass = TestRenderPass::Create();
+ root_render_pass->SetNew(root_render_passId, outerRect, outerRect, gfx::Transform());
+ scoped_ptr<SolidColorDrawQuad> outerQuad = SolidColorDrawQuad::Create();
+ outerQuad->SetNew(sharedQuadState.get(), outerRect, SK_ColorYELLOW);
+ scoped_ptr<SolidColorDrawQuad> innerQuad = SolidColorDrawQuad::Create();
+ innerQuad->SetNew(sharedQuadState.get(), innerRect, SK_ColorCYAN);
+ root_render_pass->AppendQuad(innerQuad.PassAs<DrawQuad>());
+ root_render_pass->AppendQuad(outerQuad.PassAs<DrawQuad>());
+
+ RenderPassList list;
+ list.push_back(root_render_pass.PassAs<RenderPass>());
+ renderer()->DrawFrame(list);
+
+ scoped_array<SkColor> pixels(new SkColor[DeviceViewportSize().width() * DeviceViewportSize().height()]);
+ renderer()->GetFramebufferPixels(pixels.get(), outerRect);
+
+// FIXME: This fails on Android. Endianness maybe?
+// Yellow: expects 0xFFFFFF00, was 0xFF00FFFF on android.
+// Cyan: expects 0xFF00FFFF, was 0xFFFFFF00 on android.
+// http://crbug.com/154528
+#ifndef OS_ANDROID
+ EXPECT_EQ(SK_ColorYELLOW, pixels[0]);
+ EXPECT_EQ(SK_ColorYELLOW, pixels[outerPixels - 1]);
+ EXPECT_EQ(SK_ColorCYAN, pixels[outerSize.width() + 1]);
+ EXPECT_EQ(SK_ColorCYAN, pixels[outerPixels - outerSize.width() - 2]);
+#endif
+}
+
+TEST_F(SoftwareRendererTest, tileQuad)
+{
+ gfx::Size outerSize(100, 100);
+ int outerPixels = outerSize.width() * outerSize.height();
+ gfx::Size innerSize(98, 98);
+ int innerPixels = innerSize.width() * innerSize.height();
+ gfx::Rect outerRect(outerSize);
+ gfx::Rect innerRect(gfx::Point(1, 1), innerSize);
+ setViewportSize(outerSize);
+ initializeRenderer();
+
+ ResourceProvider::ResourceId resourceYellow = resourceProvider()->CreateResource(outerSize, GL_RGBA, ResourceProvider::TextureUsageAny);
+ ResourceProvider::ResourceId resourceCyan = resourceProvider()->CreateResource(innerSize, GL_RGBA, ResourceProvider::TextureUsageAny);
+
+ SkColor yellow = SK_ColorYELLOW;
+ SkColor cyan = SK_ColorCYAN;
+ scoped_array<SkColor> yellowPixels(new SkColor[outerPixels]);
+ scoped_array<SkColor> cyanPixels(new SkColor[innerPixels]);
+ for (int i = 0; i < outerPixels; i++)
+ yellowPixels[i] = yellow;
+ for (int i = 0; i < innerPixels; i++)
+ cyanPixels[i] = cyan;
+
+ resourceProvider()->SetPixels(resourceYellow, reinterpret_cast<uint8_t*>(yellowPixels.get()), gfx::Rect(outerSize), gfx::Rect(outerSize), gfx::Vector2d());
+ resourceProvider()->SetPixels(resourceCyan, reinterpret_cast<uint8_t*>(cyanPixels.get()), gfx::Rect(innerSize), gfx::Rect(innerSize), gfx::Vector2d());
+
+ gfx::Rect rootRect = gfx::Rect(DeviceViewportSize());
+
+ scoped_ptr<SharedQuadState> sharedQuadState = SharedQuadState::Create();
+ sharedQuadState->SetAll(gfx::Transform(), outerSize, outerRect, outerRect, false, 1.0);
+ RenderPass::Id root_render_passId = RenderPass::Id(1, 1);
+ scoped_ptr<TestRenderPass> root_render_pass = TestRenderPass::Create();
+ root_render_pass->SetNew(root_render_passId, rootRect, rootRect, gfx::Transform());
+ scoped_ptr<TileDrawQuad> outerQuad = TileDrawQuad::Create();
+ outerQuad->SetNew(sharedQuadState.get(), outerRect, outerRect, resourceYellow, gfx::RectF(outerSize), outerSize, false);
+ scoped_ptr<TileDrawQuad> innerQuad = TileDrawQuad::Create();
+ innerQuad->SetNew(sharedQuadState.get(), innerRect, innerRect, resourceCyan, gfx::RectF(innerSize), innerSize, false);
+ root_render_pass->AppendQuad(innerQuad.PassAs<DrawQuad>());
+ root_render_pass->AppendQuad(outerQuad.PassAs<DrawQuad>());
+
+ RenderPassList list;
+ list.push_back(root_render_pass.PassAs<RenderPass>());
+ renderer()->DrawFrame(list);
+
+ scoped_array<SkColor> pixels(new SkColor[DeviceViewportSize().width() * DeviceViewportSize().height()]);
+ renderer()->GetFramebufferPixels(pixels.get(), outerRect);
+
+ EXPECT_EQ(SK_ColorYELLOW, pixels[0]);
+ EXPECT_EQ(SK_ColorYELLOW, pixels[outerPixels - 1]);
+ EXPECT_EQ(SK_ColorCYAN, pixels[outerSize.width() + 1]);
+ 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;
+ scoped_array<SkColor> pixels(new SkColor[viewportPixels]);
+
+ // Draw a fullscreen green quad in a first frame.
+ RenderPass::Id rootClearPassId(1, 0);
+ TestRenderPass* rootClearPass = addRenderPass(list, rootClearPassId, viewportRect, gfx::Transform());
+ addQuad(rootClearPass, viewportRect, SK_ColorGREEN);
+
+ renderer()->DecideRenderPassAllocationsForFrame(list);
+ renderer()->DrawFrame(list);
+ renderer()->GetFramebufferPixels(pixels.get(), viewportRect);
+
+ EXPECT_EQ(SK_ColorGREEN, pixels[0]);
+ EXPECT_EQ(SK_ColorGREEN, pixels[viewportPixels - 1]);
+
+ 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(list, rootSmallerPassId, viewportRect, gfx::Transform());
+ addQuad(rootSmallerPass, smallerRect, SK_ColorMAGENTA);
+
+ renderer()->DecideRenderPassAllocationsForFrame(list);
+ renderer()->DrawFrame(list);
+ 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/output/texture_copier.cc b/cc/output/texture_copier.cc
new file mode 100644
index 0000000..5a8a41d
--- /dev/null
+++ b/cc/output/texture_copier.cc
@@ -0,0 +1,115 @@
+// 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/output/texture_copier.h"
+
+#include "base/debug/trace_event.h"
+#include "build/build_config.h"
+#include "cc/output/gl_renderer.h" // For the GLC() macro.
+#include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h"
+#include "third_party/khronos/GLES2/gl2.h"
+
+namespace cc {
+
+AcceleratedTextureCopier::AcceleratedTextureCopier(
+ WebKit::WebGraphicsContext3D* context,
+ bool using_bind_uniforms)
+ : context_(context), using_bind_uniforms_(using_bind_uniforms) {
+ DCHECK(context_);
+ GLC(context_, fbo_ = context_->createFramebuffer());
+ GLC(context_, position_buffer_ = context_->createBuffer());
+
+ static const float kPositions[4][4] = { { -1, -1, 0, 1 }, { 1, -1, 0, 1 },
+ { 1, 1, 0, 1 }, { -1, 1, 0, 1 } };
+
+ GLC(context_, context_->bindBuffer(GL_ARRAY_BUFFER, position_buffer_));
+ GLC(context_,
+ context_->bufferData(
+ GL_ARRAY_BUFFER, sizeof(kPositions), kPositions, GL_STATIC_DRAW));
+ GLC(context_, context_->bindBuffer(GL_ARRAY_BUFFER, 0));
+
+ blit_program_.reset(new BlitProgram(context_));
+}
+
+AcceleratedTextureCopier::~AcceleratedTextureCopier() {
+ if (blit_program_)
+ blit_program_->Cleanup(context_);
+ if (position_buffer_)
+ GLC(context_, context_->deleteBuffer(position_buffer_));
+ if (fbo_)
+ GLC(context_, context_->deleteFramebuffer(fbo_));
+}
+
+void AcceleratedTextureCopier::CopyTexture(Parameters parameters) {
+ TRACE_EVENT0("cc", "TextureCopier::CopyTexture");
+
+ GLC(context_, context_->disable(GL_SCISSOR_TEST));
+
+ // Note: this code does not restore the viewport, bound program, 2D texture,
+ // framebuffer, buffer or blend enable.
+ GLC(context_, context_->bindFramebuffer(GL_FRAMEBUFFER, fbo_));
+ GLC(context_,
+ context_->framebufferTexture2D(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D,
+ parameters.dest_texture,
+ 0));
+
+#if defined(OS_ANDROID)
+ // Clear destination to improve performance on tiling GPUs.
+ // TODO: Use EXT_discard_framebuffer or skip clearing if it isn't available.
+ GLC(context_, context_->clear(GL_COLOR_BUFFER_BIT));
+#endif
+
+ GLC(context_,
+ context_->bindTexture(GL_TEXTURE_2D, parameters.source_texture));
+ GLC(context_,
+ context_->texParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
+ GLC(context_,
+ context_->texParameteri(
+ GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
+
+ if (!blit_program_->initialized())
+ blit_program_->Initialize(context_, using_bind_uniforms_);
+
+ // TODO: Use EXT_framebuffer_blit if available.
+ GLC(context_, context_->useProgram(blit_program_->program()));
+
+ const int kPositionAttribute = 0;
+ GLC(context_, context_->bindBuffer(GL_ARRAY_BUFFER, position_buffer_));
+ GLC(context_,
+ context_->vertexAttribPointer(
+ kPositionAttribute, 4, GL_FLOAT, false, 0, 0));
+ GLC(context_, context_->enableVertexAttribArray(kPositionAttribute));
+ GLC(context_, context_->bindBuffer(GL_ARRAY_BUFFER, 0));
+
+ GLC(context_,
+ context_->viewport(
+ 0, 0, parameters.size.width(), parameters.size.height()));
+ GLC(context_, context_->disable(GL_BLEND));
+ GLC(context_, context_->drawArrays(GL_TRIANGLE_FAN, 0, 4));
+
+ GLC(context_,
+ context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+ GLC(context_,
+ context_->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+ GLC(context_, context_->disableVertexAttribArray(kPositionAttribute));
+
+ GLC(context_, context_->useProgram(0));
+
+ GLC(context_,
+ context_->framebufferTexture2D(
+ GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0));
+ GLC(context_, context_->bindFramebuffer(GL_FRAMEBUFFER, 0));
+ GLC(context_, context_->bindTexture(GL_TEXTURE_2D, 0));
+
+ GLC(context_, context_->enable(GL_SCISSOR_TEST));
+}
+
+void AcceleratedTextureCopier::Flush() {
+ GLC(context_, context_->flush());
+}
+
+} // namespace cc
diff --git a/cc/output/texture_copier.h b/cc/output/texture_copier.h
new file mode 100644
index 0000000..8500310
--- /dev/null
+++ b/cc/output/texture_copier.h
@@ -0,0 +1,69 @@
+// 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_OUTPUT_TEXTURE_COPIER_H_
+#define CC_OUTPUT_TEXTURE_COPIER_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "cc/base/cc_export.h"
+#include "cc/output/program_binding.h"
+#include "cc/output/shader.h"
+#include "third_party/khronos/GLES2/gl2.h"
+#include "ui/gfx/size.h"
+
+namespace WebKit { class WebGraphicsContext3D; }
+
+namespace cc {
+
+class CC_EXPORT TextureCopier {
+ public:
+ struct Parameters {
+ unsigned source_texture;
+ unsigned dest_texture;
+ gfx::Size size;
+ };
+ // Copy the base level contents of |source_texture| to |dest_texture|. Both
+ // texture objects must be complete and have a base level of |size|
+ // dimensions. The color formats do not need to match, but |dest_texture| must
+ // have a renderable format.
+ virtual void CopyTexture(Parameters parameters) = 0;
+ virtual void Flush() = 0;
+
+ virtual ~TextureCopier() {}
+};
+
+class CC_EXPORT AcceleratedTextureCopier : public TextureCopier {
+ public:
+ static scoped_ptr<AcceleratedTextureCopier> Create(
+ WebKit::WebGraphicsContext3D* context,
+ bool using_bind_uniforms) {
+ return make_scoped_ptr(
+ new AcceleratedTextureCopier(context, using_bind_uniforms));
+ }
+ virtual ~AcceleratedTextureCopier();
+
+ virtual void CopyTexture(Parameters parameters) OVERRIDE;
+ virtual void Flush() OVERRIDE;
+
+ protected:
+ AcceleratedTextureCopier(WebKit::WebGraphicsContext3D* context,
+ bool using_bind_uniforms);
+
+ private:
+ typedef ProgramBinding<VertexShaderPosTexIdentity, FragmentShaderRGBATex>
+ BlitProgram;
+
+ WebKit::WebGraphicsContext3D* context_;
+ GLuint fbo_;
+ GLuint position_buffer_;
+ scoped_ptr<BlitProgram> blit_program_;
+ bool using_bind_uniforms_;
+
+ DISALLOW_COPY_AND_ASSIGN(AcceleratedTextureCopier);
+};
+
+}
+
+#endif // CC_OUTPUT_TEXTURE_COPIER_H_
diff --git a/cc/output/texture_copier_unittest.cc b/cc/output/texture_copier_unittest.cc
new file mode 100644
index 0000000..4069bbf
--- /dev/null
+++ b/cc/output/texture_copier_unittest.cc
@@ -0,0 +1,73 @@
+// Copyright 2011 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/output/texture_copier.h"
+
+#include "cc/test/test_web_graphics_context_3d.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/khronos/GLES2/gl2.h"
+
+using namespace WebKit;
+using testing::InSequence;
+using testing::Test;
+using testing::_;
+
+namespace cc {
+namespace {
+
+class MockContext : public TestWebGraphicsContext3D {
+ public:
+ MOCK_METHOD2(bindFramebuffer, void(WGC3Denum, WebGLId));
+ MOCK_METHOD3(texParameteri,
+ void(WGC3Denum target, WGC3Denum pname, WGC3Dint param));
+ MOCK_METHOD1(disable, void(WGC3Denum cap));
+ MOCK_METHOD1(enable, void(WGC3Denum cap));
+
+ MOCK_METHOD3(drawArrays,
+ void(WGC3Denum mode, WGC3Dint first, WGC3Dsizei count));
+};
+
+TEST(TextureCopierTest, TestDrawArraysCopy) {
+ scoped_ptr<MockContext> mock_context(new MockContext);
+ {
+ InSequence sequence;
+
+ EXPECT_CALL(*mock_context, disable(GL_SCISSOR_TEST));
+
+ // Here we check just some essential properties of copyTexture() to avoid mirroring the full implementation.
+ EXPECT_CALL(*mock_context, bindFramebuffer(GL_FRAMEBUFFER, _));
+
+ // Make sure linear filtering is disabled during the copy.
+ EXPECT_CALL(
+ *mock_context,
+ texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
+ EXPECT_CALL(
+ *mock_context,
+ texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
+
+ EXPECT_CALL(*mock_context, disable(GL_BLEND));
+
+ EXPECT_CALL(*mock_context, drawArrays(_, _, _));
+
+ // Linear filtering, default framebuffer and scissor test should be restored.
+ EXPECT_CALL(*mock_context,
+ texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
+ EXPECT_CALL(*mock_context,
+ texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
+ EXPECT_CALL(*mock_context, bindFramebuffer(GL_FRAMEBUFFER, 0));
+ EXPECT_CALL(*mock_context, enable(GL_SCISSOR_TEST));
+ }
+
+ int source_texture_id = mock_context->createTexture();
+ int dest_texture_id = mock_context->createTexture();
+ gfx::Size size(256, 128);
+ scoped_ptr<AcceleratedTextureCopier> copier(
+ AcceleratedTextureCopier::Create(mock_context.get(), false));
+ TextureCopier::Parameters copy = { source_texture_id, dest_texture_id, size };
+ copier->CopyTexture(copy);
+}
+
+} // namespace
+} // namespace cc