diff options
author | jamesr@chromium.org <jamesr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-18 07:24:30 +0000 |
---|---|---|
committer | jamesr@chromium.org <jamesr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-18 07:24:30 +0000 |
commit | 7f0d825f49dde66a6e9137a4e35460765bc5f0d8 (patch) | |
tree | 6944603cf2a97934afad6e599500ff45091316ed /cc/output | |
parent | 95e4e1a0fda6929b47702e69e4ddfe384b5d014b (diff) | |
download | chromium_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')
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()), + ¤t_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()), + ¤t_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 |