Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 1 | // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "content/browser/android/in_process/synchronous_compositor_output_surface.h" |
| 6 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 7 | #include "base/auto_reset.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 8 | #include "base/logging.h" |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 9 | #include "cc/output/begin_frame_args.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 10 | #include "cc/output/compositor_frame.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 11 | #include "cc/output/context_provider.h" |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 12 | #include "cc/output/managed_memory_policy.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 13 | #include "cc/output/output_surface_client.h" |
| 14 | #include "cc/output/software_output_device.h" |
| 15 | #include "content/browser/android/in_process/synchronous_compositor_impl.h" |
| 16 | #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 17 | #include "content/public/browser/browser_thread.h" |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 18 | #include "gpu/command_buffer/client/gl_in_process_context.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 19 | #include "third_party/skia/include/core/SkCanvas.h" |
| 20 | #include "third_party/skia/include/core/SkDevice.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 21 | #include "ui/gfx/rect_conversions.h" |
| 22 | #include "ui/gfx/skia_util.h" |
| 23 | #include "ui/gfx/transform.h" |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 24 | #include "ui/gl/gl_surface.h" |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 25 | #include "webkit/common/gpu/webgraphicscontext3d_in_process_command_buffer_impl.h" |
| 26 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 27 | |
| 28 | namespace content { |
| 29 | |
| 30 | namespace { |
| 31 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 32 | scoped_ptr<WebKit::WebGraphicsContext3D> CreateWebGraphicsContext3D( |
| 33 | scoped_refptr<gfx::GLSurface> surface) { |
| 34 | using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl; |
| 35 | if (!gfx::GLSurface::InitializeOneOff()) |
| 36 | return scoped_ptr<WebKit::WebGraphicsContext3D>(); |
| 37 | |
| 38 | const char* allowed_extensions = "*"; |
| 39 | const gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu; |
| 40 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 41 | WebKit::WebGraphicsContext3D::Attributes attributes; |
| 42 | attributes.antialias = false; |
| 43 | attributes.shareResources = true; |
| 44 | attributes.noAutomaticFlushes = true; |
| 45 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 46 | gpu::GLInProcessContextAttribs in_process_attribs; |
| 47 | WebGraphicsContext3DInProcessCommandBufferImpl::ConvertAttributes( |
| 48 | attributes, &in_process_attribs); |
| 49 | scoped_ptr<gpu::GLInProcessContext> context( |
| 50 | gpu::GLInProcessContext::CreateWithSurface(surface, |
| 51 | attributes.shareResources, |
| 52 | allowed_extensions, |
| 53 | in_process_attribs, |
| 54 | gpu_preference)); |
| 55 | |
| 56 | if (!context.get()) |
| 57 | return scoped_ptr<WebKit::WebGraphicsContext3D>(); |
| 58 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 59 | return scoped_ptr<WebKit::WebGraphicsContext3D>( |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 60 | WebGraphicsContext3DInProcessCommandBufferImpl::WrapContext( |
| 61 | context.Pass(), attributes)); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 62 | } |
| 63 | |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 64 | void DidActivatePendingTree(int routing_id) { |
| 65 | SynchronousCompositorOutputSurfaceDelegate* delegate = |
| 66 | SynchronousCompositorImpl::FromRoutingID(routing_id); |
| 67 | if (delegate) |
| 68 | delegate->DidActivatePendingTree(); |
| 69 | } |
| 70 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 71 | } // namespace |
| 72 | |
| 73 | class SynchronousCompositorOutputSurface::SoftwareDevice |
| 74 | : public cc::SoftwareOutputDevice { |
| 75 | public: |
| 76 | SoftwareDevice(SynchronousCompositorOutputSurface* surface) |
| 77 | : surface_(surface), |
| 78 | null_device_(SkBitmap::kARGB_8888_Config, 1, 1), |
| 79 | null_canvas_(&null_device_) { |
| 80 | } |
| 81 | virtual void Resize(gfx::Size size) OVERRIDE { |
| 82 | // Intentional no-op: canvas size is controlled by the embedder. |
| 83 | } |
| 84 | virtual SkCanvas* BeginPaint(gfx::Rect damage_rect) OVERRIDE { |
Torne (Richard Coles) | 5e3f23d | 2013-06-11 16:24:11 +0100 | [diff] [blame] | 85 | if (!surface_->current_sw_canvas_) { |
| 86 | NOTREACHED() << "BeginPaint with no canvas set"; |
| 87 | return &null_canvas_; |
| 88 | } |
| 89 | LOG_IF(WARNING, surface_->did_swap_buffer_) |
| 90 | << "Mutliple calls to BeginPaint per frame"; |
| 91 | return surface_->current_sw_canvas_; |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 92 | } |
| 93 | virtual void EndPaint(cc::SoftwareFrameData* frame_data) OVERRIDE { |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 94 | } |
| 95 | virtual void CopyToBitmap(gfx::Rect rect, SkBitmap* output) OVERRIDE { |
| 96 | NOTIMPLEMENTED(); |
| 97 | } |
| 98 | |
| 99 | private: |
| 100 | SynchronousCompositorOutputSurface* surface_; |
| 101 | SkDevice null_device_; |
| 102 | SkCanvas null_canvas_; |
| 103 | |
| 104 | DISALLOW_COPY_AND_ASSIGN(SoftwareDevice); |
| 105 | }; |
| 106 | |
| 107 | SynchronousCompositorOutputSurface::SynchronousCompositorOutputSurface( |
| 108 | int routing_id) |
| 109 | : cc::OutputSurface( |
| 110 | scoped_ptr<cc::SoftwareOutputDevice>(new SoftwareDevice(this))), |
| 111 | routing_id_(routing_id), |
| 112 | needs_begin_frame_(false), |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 113 | invoking_composite_(false), |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 114 | did_swap_buffer_(false), |
| 115 | current_sw_canvas_(NULL) { |
| 116 | capabilities_.deferred_gl_initialization = true; |
Ben Murdoch | fb25065 | 2013-07-31 11:42:55 +0100 | [diff] [blame] | 117 | capabilities_.draw_and_swap_full_viewport_every_frame = true; |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 118 | capabilities_.adjust_deadline_for_parent = false; |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 119 | // Cannot call out to GetDelegate() here as the output surface is not |
| 120 | // constructed on the correct thread. |
| 121 | } |
| 122 | |
| 123 | SynchronousCompositorOutputSurface::~SynchronousCompositorOutputSurface() { |
| 124 | DCHECK(CalledOnValidThread()); |
| 125 | SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate(); |
| 126 | if (delegate) |
| 127 | delegate->DidDestroySynchronousOutputSurface(this); |
| 128 | } |
| 129 | |
| 130 | bool SynchronousCompositorOutputSurface::ForcedDrawToSoftwareDevice() const { |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 131 | // |current_sw_canvas_| indicates we're in a DemandDrawSw call. In addition |
| 132 | // |invoking_composite_| == false indicates an attempt to draw outside of |
| 133 | // the synchronous compositor's control: force it into SW path and hence to |
| 134 | // the null canvas (and will log a warning there). |
| 135 | return current_sw_canvas_ != NULL || !invoking_composite_; |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 136 | } |
| 137 | |
| 138 | bool SynchronousCompositorOutputSurface::BindToClient( |
| 139 | cc::OutputSurfaceClient* surface_client) { |
| 140 | DCHECK(CalledOnValidThread()); |
| 141 | if (!cc::OutputSurface::BindToClient(surface_client)) |
| 142 | return false; |
Ben Murdoch | ca12bfa | 2013-07-23 11:17:05 +0100 | [diff] [blame] | 143 | surface_client->SetTreeActivationCallback( |
| 144 | base::Bind(&DidActivatePendingTree, routing_id_)); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 145 | SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate(); |
| 146 | if (delegate) |
| 147 | delegate->DidBindOutputSurface(this); |
Torne (Richard Coles) | a36e592 | 2013-08-05 13:57:33 +0100 | [diff] [blame] | 148 | |
| 149 | const int bytes_limit = 64 * 1024 * 1024; |
| 150 | const int num_resources_limit = 100; |
| 151 | surface_client->SetMemoryPolicy( |
| 152 | cc::ManagedMemoryPolicy(bytes_limit, |
| 153 | cc::ManagedMemoryPolicy::CUTOFF_ALLOW_EVERYTHING, |
| 154 | 0, |
| 155 | cc::ManagedMemoryPolicy::CUTOFF_ALLOW_NOTHING, |
| 156 | num_resources_limit)); |
| 157 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 158 | return true; |
| 159 | } |
| 160 | |
| 161 | void SynchronousCompositorOutputSurface::Reshape( |
| 162 | gfx::Size size, float scale_factor) { |
| 163 | // Intentional no-op: surface size is controlled by the embedder. |
| 164 | } |
| 165 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 166 | void SynchronousCompositorOutputSurface::SetNeedsBeginFrame( |
| 167 | bool enable) { |
| 168 | DCHECK(CalledOnValidThread()); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 169 | cc::OutputSurface::SetNeedsBeginFrame(enable); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 170 | needs_begin_frame_ = enable; |
| 171 | SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate(); |
| 172 | if (delegate) |
| 173 | delegate->SetContinuousInvalidate(needs_begin_frame_); |
| 174 | } |
| 175 | |
| 176 | void SynchronousCompositorOutputSurface::SwapBuffers( |
Torne (Richard Coles) | 5e3f23d | 2013-06-11 16:24:11 +0100 | [diff] [blame] | 177 | cc::CompositorFrame* frame) { |
| 178 | if (!ForcedDrawToSoftwareDevice()) { |
| 179 | DCHECK(context3d()); |
| 180 | context3d()->shallowFlushCHROMIUM(); |
| 181 | } |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 182 | SynchronousCompositorOutputSurfaceDelegate* delegate = GetDelegate(); |
| 183 | if (delegate) |
| 184 | delegate->UpdateFrameMetaData(frame->metadata); |
| 185 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 186 | did_swap_buffer_ = true; |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 187 | DidSwapBuffers(); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 188 | } |
| 189 | |
| 190 | namespace { |
| 191 | void AdjustTransformForClip(gfx::Transform* transform, gfx::Rect clip) { |
| 192 | // The system-provided transform translates us from the screen origin to the |
| 193 | // origin of the clip rect, but CC's draw origin starts at the clip. |
| 194 | transform->matrix().postTranslate(-clip.x(), -clip.y(), 0); |
| 195 | } |
| 196 | } // namespace |
| 197 | |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 198 | bool SynchronousCompositorOutputSurface::InitializeHwDraw( |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 199 | scoped_refptr<gfx::GLSurface> surface, |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 200 | scoped_refptr<cc::ContextProvider> offscreen_context) { |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 201 | DCHECK(CalledOnValidThread()); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 202 | DCHECK(HasClient()); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 203 | DCHECK(!context3d_); |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 204 | DCHECK(surface); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 205 | |
Ben Murdoch | bb1529c | 2013-08-08 10:24:53 +0100 | [diff] [blame^] | 206 | return InitializeAndSetContext3D( |
| 207 | CreateWebGraphicsContext3D(surface).Pass(), offscreen_context); |
Ben Murdoch | 7dbb3d5 | 2013-07-17 14:55:54 +0100 | [diff] [blame] | 208 | } |
| 209 | |
| 210 | void SynchronousCompositorOutputSurface::ReleaseHwDraw() { |
| 211 | cc::OutputSurface::ReleaseGL(); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 212 | } |
| 213 | |
| 214 | bool SynchronousCompositorOutputSurface::DemandDrawHw( |
| 215 | gfx::Size surface_size, |
| 216 | const gfx::Transform& transform, |
Ben Murdoch | fb25065 | 2013-07-31 11:42:55 +0100 | [diff] [blame] | 217 | gfx::Rect clip, |
| 218 | bool stencil_enabled) { |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 219 | DCHECK(CalledOnValidThread()); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 220 | DCHECK(HasClient()); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 221 | DCHECK(context3d()); |
| 222 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 223 | gfx::Transform adjusted_transform = transform; |
| 224 | AdjustTransformForClip(&adjusted_transform, clip); |
| 225 | surface_size_ = surface_size; |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 226 | SetExternalDrawConstraints(adjusted_transform, clip); |
Ben Murdoch | fb25065 | 2013-07-31 11:42:55 +0100 | [diff] [blame] | 227 | SetExternalStencilTest(stencil_enabled); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 228 | InvokeComposite(clip.size()); |
| 229 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 230 | return did_swap_buffer_; |
| 231 | } |
| 232 | |
| 233 | bool SynchronousCompositorOutputSurface::DemandDrawSw(SkCanvas* canvas) { |
| 234 | DCHECK(CalledOnValidThread()); |
| 235 | DCHECK(canvas); |
| 236 | DCHECK(!current_sw_canvas_); |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 237 | base::AutoReset<SkCanvas*> canvas_resetter(¤t_sw_canvas_, canvas); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 238 | |
| 239 | SkIRect canvas_clip; |
| 240 | canvas->getClipDeviceBounds(&canvas_clip); |
| 241 | gfx::Rect clip = gfx::SkIRectToRect(canvas_clip); |
| 242 | |
| 243 | gfx::Transform transform(gfx::Transform::kSkipInitialization); |
| 244 | transform.matrix() = canvas->getTotalMatrix(); // Converts 3x3 matrix to 4x4. |
| 245 | AdjustTransformForClip(&transform, clip); |
| 246 | |
| 247 | surface_size_ = gfx::Size(canvas->getDeviceSize().width(), |
| 248 | canvas->getDeviceSize().height()); |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 249 | SetExternalDrawConstraints(transform, clip); |
Ben Murdoch | fb25065 | 2013-07-31 11:42:55 +0100 | [diff] [blame] | 250 | SetExternalStencilTest(false); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 251 | |
| 252 | InvokeComposite(clip.size()); |
| 253 | |
Torne (Richard Coles) | 5e3f23d | 2013-06-11 16:24:11 +0100 | [diff] [blame] | 254 | return did_swap_buffer_; |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 255 | } |
| 256 | |
| 257 | void SynchronousCompositorOutputSurface::InvokeComposite( |
| 258 | gfx::Size damage_size) { |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 259 | DCHECK(!invoking_composite_); |
| 260 | base::AutoReset<bool> invoking_composite_resetter(&invoking_composite_, true); |
Torne (Richard Coles) | 5e3f23d | 2013-06-11 16:24:11 +0100 | [diff] [blame] | 261 | did_swap_buffer_ = false; |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 262 | SetNeedsRedrawRect(gfx::Rect(damage_size)); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 263 | if (needs_begin_frame_) |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 264 | BeginFrame(cc::BeginFrameArgs::CreateForSynchronousCompositor()); |
Torne (Richard Coles) | 5e3f23d | 2013-06-11 16:24:11 +0100 | [diff] [blame] | 265 | |
| 266 | if (did_swap_buffer_) |
Torne (Richard Coles) | 7d4cd47 | 2013-06-19 11:58:07 +0100 | [diff] [blame] | 267 | OnSwapBuffersComplete(NULL); |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 268 | } |
| 269 | |
Ben Murdoch | eb525c5 | 2013-07-10 11:40:50 +0100 | [diff] [blame] | 270 | void SynchronousCompositorOutputSurface::PostCheckForRetroactiveBeginFrame() { |
| 271 | // Synchronous compositor cannot perform retroactive begin frames, so |
| 272 | // intentionally no-op here. |
| 273 | } |
| 274 | |
Torne (Richard Coles) | 868fa2f | 2013-06-11 10:57:03 +0100 | [diff] [blame] | 275 | // Not using base::NonThreadSafe as we want to enforce a more exacting threading |
| 276 | // requirement: SynchronousCompositorOutputSurface() must only be used on the UI |
| 277 | // thread. |
| 278 | bool SynchronousCompositorOutputSurface::CalledOnValidThread() const { |
| 279 | return BrowserThread::CurrentlyOn(BrowserThread::UI); |
| 280 | } |
| 281 | |
| 282 | SynchronousCompositorOutputSurfaceDelegate* |
| 283 | SynchronousCompositorOutputSurface::GetDelegate() { |
| 284 | return SynchronousCompositorImpl::FromRoutingID(routing_id_); |
| 285 | } |
| 286 | |
| 287 | } // namespace content |