OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "services/gfx/compositor/backend/gpu_rasterizer.h" | 5 #include "services/gfx/compositor/backend/gpu_rasterizer.h" |
6 | 6 |
7 #ifndef GL_GLEXT_PROTOTYPES | 7 #ifndef GL_GLEXT_PROTOTYPES |
8 #define GL_GLEXT_PROTOTYPES | 8 #define GL_GLEXT_PROTOTYPES |
9 #endif | 9 #endif |
10 | 10 |
11 #include <GLES2/gl2.h> | 11 #include <GLES2/gl2.h> |
12 #include <GLES2/gl2extmojo.h> | 12 #include <GLES2/gl2extmojo.h> |
13 #include <MGL/mgl.h> | 13 #include <MGL/mgl.h> |
14 #include <MGL/mgl_echo.h> | 14 #include <MGL/mgl_echo.h> |
15 #include <MGL/mgl_onscreen.h> | 15 #include <MGL/mgl_onscreen.h> |
16 #include <MGL/mgl_signal_sync_point.h> | |
17 | 16 |
18 #include "base/bind.h" | 17 #include "base/bind.h" |
19 #include "base/location.h" | 18 #include "base/location.h" |
20 #include "base/logging.h" | 19 #include "base/logging.h" |
21 #include "base/message_loop/message_loop.h" | 20 #include "base/message_loop/message_loop.h" |
22 #include "base/time/time.h" | 21 #include "base/time/time.h" |
23 #include "base/trace_event/trace_event.h" | 22 #include "base/trace_event/trace_event.h" |
24 #include "services/gfx/compositor/backend/vsync_scheduler.h" | |
25 #include "services/gfx/compositor/render/render_frame.h" | 23 #include "services/gfx/compositor/render/render_frame.h" |
26 | 24 |
27 namespace compositor { | 25 namespace compositor { |
28 namespace { | 26 namespace { |
| 27 // Timeout for receiving initial viewport parameters from the GPU service. |
29 constexpr int64_t kViewportParameterTimeoutMs = 1000; | 28 constexpr int64_t kViewportParameterTimeoutMs = 1000; |
| 29 |
| 30 // Default vsync interval when the GPU service failed to provide viewport |
| 31 // parameters promptly. |
30 constexpr int64_t kDefaultVsyncIntervalUs = 100000; // deliberately sluggish | 32 constexpr int64_t kDefaultVsyncIntervalUs = 100000; // deliberately sluggish |
31 } | 33 } |
32 | 34 |
33 GpuRasterizer::GpuRasterizer(mojo::ContextProviderPtr context_provider, | 35 GpuRasterizer::GpuRasterizer(mojo::ContextProviderPtr context_provider, |
34 const scoped_refptr<VsyncScheduler>& scheduler, | 36 Callbacks* callbacks) |
35 const scoped_refptr<base::TaskRunner>& task_runner, | |
36 const base::Closure& error_callback) | |
37 : context_provider_(context_provider.Pass()), | 37 : context_provider_(context_provider.Pass()), |
38 scheduler_(scheduler), | 38 callbacks_(callbacks), |
39 task_runner_(task_runner), | |
40 error_callback_(error_callback), | |
41 viewport_parameter_listener_binding_(this), | 39 viewport_parameter_listener_binding_(this), |
42 viewport_parameter_timeout_(false, false), | 40 viewport_parameter_timeout_(false, false), |
43 weak_ptr_factory_(this) { | 41 weak_ptr_factory_(this) { |
44 DCHECK(context_provider_); | 42 DCHECK(context_provider_); |
45 DCHECK(scheduler_); | 43 DCHECK(callbacks_); |
46 DCHECK(task_runner_); | |
47 | 44 |
48 context_provider_.set_connection_error_handler( | 45 context_provider_.set_connection_error_handler( |
49 base::Bind(&GpuRasterizer::OnContextProviderConnectionError, | 46 base::Bind(&GpuRasterizer::OnContextProviderConnectionError, |
50 base::Unretained(this))); | 47 base::Unretained(this))); |
51 CreateContext(); | 48 CreateContext(); |
52 } | 49 } |
53 | 50 |
54 GpuRasterizer::~GpuRasterizer() { | 51 GpuRasterizer::~GpuRasterizer() { |
55 DestroyContext(); | 52 DestroyContext(); |
56 } | 53 } |
(...skipping 12 matching lines...) Expand all Loading... |
69 } | 66 } |
70 | 67 |
71 void GpuRasterizer::InitContext( | 68 void GpuRasterizer::InitContext( |
72 mojo::InterfaceHandle<mojo::CommandBuffer> command_buffer) { | 69 mojo::InterfaceHandle<mojo::CommandBuffer> command_buffer) { |
73 DCHECK(!gl_context_); | 70 DCHECK(!gl_context_); |
74 DCHECK(!ganesh_context_); | 71 DCHECK(!ganesh_context_); |
75 DCHECK(!ganesh_surface_); | 72 DCHECK(!ganesh_surface_); |
76 | 73 |
77 if (!command_buffer) { | 74 if (!command_buffer) { |
78 LOG(ERROR) << "Could not create GL context."; | 75 LOG(ERROR) << "Could not create GL context."; |
79 PostErrorCallback(); | 76 callbacks_->OnRasterizerError(); |
80 return; | 77 return; |
81 } | 78 } |
82 | 79 |
83 gl_context_ = mojo::GLContext::CreateFromCommandBuffer( | 80 gl_context_ = mojo::GLContext::CreateFromCommandBuffer( |
84 mojo::CommandBufferPtr::Create(std::move(command_buffer))); | 81 mojo::CommandBufferPtr::Create(std::move(command_buffer))); |
| 82 DCHECK(!gl_context_->is_lost()); |
85 gl_context_->AddObserver(this); | 83 gl_context_->AddObserver(this); |
86 ganesh_context_ = new mojo::skia::GaneshContext(gl_context_); | 84 ganesh_context_ = new mojo::skia::GaneshContext(gl_context_); |
87 | 85 |
88 if (have_viewport_parameters_) { | 86 if (have_viewport_parameters_) { |
89 ApplyViewportParameters(); | 87 ApplyViewportParameters(); |
90 } else { | 88 } else { |
91 viewport_parameter_timeout_.Start( | 89 viewport_parameter_timeout_.Start( |
92 FROM_HERE, | 90 FROM_HERE, |
93 base::TimeDelta::FromMilliseconds(kViewportParameterTimeoutMs), | 91 base::TimeDelta::FromMilliseconds(kViewportParameterTimeoutMs), |
94 base::Bind(&GpuRasterizer::OnViewportParameterTimeout, | 92 base::Bind(&GpuRasterizer::OnViewportParameterTimeout, |
95 base::Unretained(this))); | 93 base::Unretained(this))); |
96 } | 94 } |
97 | |
98 if (frame_) | |
99 Draw(); | |
100 } | 95 } |
101 | 96 |
102 void GpuRasterizer::AbandonContext() { | 97 void GpuRasterizer::AbandonContext() { |
103 if (gl_context_) | |
104 scheduler_->Stop(); | |
105 | |
106 if (viewport_parameter_listener_binding_.is_bound()) { | 98 if (viewport_parameter_listener_binding_.is_bound()) { |
107 viewport_parameter_timeout_.Stop(); | 99 viewport_parameter_timeout_.Stop(); |
108 viewport_parameter_listener_binding_.Close(); | 100 viewport_parameter_listener_binding_.Close(); |
109 } | 101 } |
| 102 |
| 103 if (ready_) { |
| 104 while (frames_in_progress_) |
| 105 DrawFinished(false /*presented*/); |
| 106 ready_ = false; |
| 107 callbacks_->OnRasterizerSuspended(); |
| 108 } |
110 } | 109 } |
111 | 110 |
112 void GpuRasterizer::DestroyContext() { | 111 void GpuRasterizer::DestroyContext() { |
113 AbandonContext(); | 112 AbandonContext(); |
114 | 113 |
115 if (gl_context_) { | 114 if (gl_context_) { |
116 ganesh_context_ = nullptr; | 115 ganesh_context_ = nullptr; |
117 gl_context_ = nullptr; | 116 gl_context_ = nullptr; |
118 | 117 |
119 // Do this after releasing the GL context so that we will already have | 118 // Do this after releasing the GL context so that we will already have |
120 // told the Ganesh context to abandon its context. | 119 // told the Ganesh context to abandon its context. |
121 ganesh_surface_.reset(); | 120 ganesh_surface_.reset(); |
122 } | 121 } |
123 } | 122 } |
124 | 123 |
125 void GpuRasterizer::OnContextProviderConnectionError() { | 124 void GpuRasterizer::OnContextProviderConnectionError() { |
126 LOG(ERROR) << "Context provider connection lost."; | 125 LOG(ERROR) << "Context provider connection lost."; |
127 PostErrorCallback(); | 126 |
| 127 callbacks_->OnRasterizerError(); |
128 } | 128 } |
129 | 129 |
130 void GpuRasterizer::OnContextLost() { | 130 void GpuRasterizer::OnContextLost() { |
131 LOG(WARNING) << "GL context lost!"; | 131 LOG(WARNING) << "GL context lost!"; |
132 | 132 |
133 AbandonContext(); | 133 AbandonContext(); |
134 frames_pending_ = 0u; | |
135 | |
136 base::MessageLoop::current()->PostTask( | 134 base::MessageLoop::current()->PostTask( |
137 FROM_HERE, base::Bind(&GpuRasterizer::RecreateContextAfterLoss, | 135 FROM_HERE, base::Bind(&GpuRasterizer::RecreateContextAfterLoss, |
138 weak_ptr_factory_.GetWeakPtr())); | 136 weak_ptr_factory_.GetWeakPtr())); |
139 } | 137 } |
140 | 138 |
141 void GpuRasterizer::RecreateContextAfterLoss() { | 139 void GpuRasterizer::RecreateContextAfterLoss() { |
142 LOG(WARNING) << "Recreating GL context."; | 140 LOG(WARNING) << "Recreating GL context."; |
143 | 141 |
144 DestroyContext(); | 142 DestroyContext(); |
145 CreateContext(); | 143 CreateContext(); |
(...skipping 14 matching lines...) Expand all Loading... |
160 int64_t interval) { | 158 int64_t interval) { |
161 DVLOG(1) << "Vsync parameters: timebase=" << timebase | 159 DVLOG(1) << "Vsync parameters: timebase=" << timebase |
162 << ", interval=" << interval; | 160 << ", interval=" << interval; |
163 | 161 |
164 if (!have_viewport_parameters_) { | 162 if (!have_viewport_parameters_) { |
165 viewport_parameter_timeout_.Stop(); | 163 viewport_parameter_timeout_.Stop(); |
166 have_viewport_parameters_ = true; | 164 have_viewport_parameters_ = true; |
167 } | 165 } |
168 vsync_timebase_ = timebase; | 166 vsync_timebase_ = timebase; |
169 vsync_interval_ = interval; | 167 vsync_interval_ = interval; |
170 | 168 ApplyViewportParameters(); |
171 if (gl_context_ && !gl_context_->is_lost()) | |
172 ApplyViewportParameters(); | |
173 } | 169 } |
174 | 170 |
175 void GpuRasterizer::ApplyViewportParameters() { | 171 void GpuRasterizer::ApplyViewportParameters() { |
176 DCHECK(have_viewport_parameters_); | 172 DCHECK(have_viewport_parameters_); |
177 DCHECK(gl_context_); | |
178 | 173 |
179 // TODO(jeffbrown): This shouldn't be hardcoded. | 174 if (gl_context_ && !gl_context_->is_lost()) { |
180 // Need to do some real tuning and possibly determine values adaptively. | 175 ready_ = true; |
181 int64_t update_phase = -vsync_interval_; | 176 callbacks_->OnRasterizerReady(vsync_timebase_, vsync_interval_); |
182 int64_t snapshot_phase = -vsync_interval_ / 6; | |
183 int64_t presentation_phase = vsync_interval_; | |
184 if (!scheduler_->Start(vsync_timebase_, vsync_interval_, update_phase, | |
185 snapshot_phase, presentation_phase)) { | |
186 LOG(ERROR) << "Received invalid vsync parameters: timebase=" | |
187 << vsync_timebase_ << ", interval=" << vsync_interval_; | |
188 PostErrorCallback(); | |
189 } | 177 } |
190 } | 178 } |
191 | 179 |
192 constexpr uint32_t kMaxFramesPending = 2; | 180 void GpuRasterizer::DrawFrame(const scoped_refptr<RenderFrame>& frame) { |
| 181 DCHECK(frame); |
| 182 DCHECK(ready_); |
| 183 DCHECK(gl_context_); |
| 184 DCHECK(!gl_context_->is_lost()); |
| 185 DCHECK(ganesh_context_); |
193 | 186 |
194 void GpuRasterizer::SubmitFrame(const scoped_refptr<RenderFrame>& frame, | 187 uint32_t frame_number = total_frames_++; |
195 const FrameCallback& frame_callback) { | 188 frames_in_progress_++; |
196 TRACE_EVENT0("gfx", "GpuRasterizer::SubmitFrame"); | 189 TRACE_EVENT1("gfx", "GpuRasterizer::DrawFrame", "num", frame_number); |
197 DCHECK(frame); | |
198 | |
199 if (frame_ && !frame_callback_.is_null()) | |
200 frame_callback_.Run(false); // frame discarded | |
201 | |
202 frame_ = frame; | |
203 frame_callback_ = frame_callback; | |
204 | |
205 if (gl_context_ && !gl_context_->is_lost()) { | |
206 if (frames_pending_ == kMaxFramesPending) { | |
207 TRACE_EVENT_INSTANT0("gfx", "GpuRasterizer dropping", | |
208 TRACE_EVENT_SCOPE_THREAD); | |
209 LOG(ERROR) << "too many frames pending, dropping"; | |
210 frame_callback_.Run(false); | |
211 return; | |
212 } | |
213 Draw(); | |
214 } else | |
215 frame_callback_.Run(false); | |
216 } | |
217 | |
218 static void DidEcho(void* context) { | |
219 TRACE_EVENT_ASYNC_END0("gfx", "SwapBuffers Echo", context); | |
220 auto cb = static_cast<base::Closure*>(context); | |
221 cb->Run(); | |
222 delete cb; | |
223 } | |
224 | |
225 void GpuRasterizer::Draw() { | |
226 TRACE_EVENT0("gfx", "GpuRasterizer::Draw"); | |
227 DCHECK(gl_context_); | |
228 DCHECK(ganesh_context_); | |
229 DCHECK(frame_); | |
230 | 190 |
231 mojo::GLContext::Scope gl_scope(gl_context_); | 191 mojo::GLContext::Scope gl_scope(gl_context_); |
232 | 192 |
233 // Update the viewport. | 193 // Update the viewport. |
234 const SkIRect& viewport = frame_->viewport(); | 194 const SkIRect& viewport = frame->viewport(); |
235 bool stale_surface = false; | 195 bool stale_surface = false; |
236 if (!ganesh_surface_ || | 196 if (!ganesh_surface_ || |
237 ganesh_surface_->surface()->width() != viewport.width() || | 197 ganesh_surface_->surface()->width() != viewport.width() || |
238 ganesh_surface_->surface()->height() != viewport.height()) { | 198 ganesh_surface_->surface()->height() != viewport.height()) { |
239 glResizeCHROMIUM(viewport.width(), viewport.height(), 1.0f); | 199 glResizeCHROMIUM(viewport.width(), viewport.height(), 1.0f); |
240 glViewport(viewport.x(), viewport.y(), viewport.width(), viewport.height()); | 200 glViewport(viewport.x(), viewport.y(), viewport.width(), viewport.height()); |
241 stale_surface = true; | 201 stale_surface = true; |
242 } | 202 } |
243 | 203 |
244 // Paint the frame. | 204 // Draw the frame content. |
245 { | 205 { |
246 mojo::skia::GaneshContext::Scope ganesh_scope(ganesh_context_); | 206 mojo::skia::GaneshContext::Scope ganesh_scope(ganesh_context_); |
247 | 207 |
248 if (stale_surface) { | 208 if (stale_surface) { |
249 ganesh_surface_.reset( | 209 ganesh_surface_.reset( |
250 new mojo::skia::GaneshFramebufferSurface(ganesh_scope)); | 210 new mojo::skia::GaneshFramebufferSurface(ganesh_scope)); |
251 } | 211 } |
252 | 212 |
253 frame_->Paint(ganesh_surface_->canvas()); | 213 frame->Draw(ganesh_surface_->canvas()); |
254 } | 214 } |
255 | 215 |
256 // Swap buffers and listen for completion. | 216 // Swap buffers. |
257 // TODO: Investigate using |MGLSignalSyncPoint| to wait for completion. | |
258 { | 217 { |
259 TRACE_EVENT0("gfx", "MGLSwapBuffers"); | 218 TRACE_EVENT0("gfx", "MGLSwapBuffers"); |
260 MGLSwapBuffers(); | 219 MGLSwapBuffers(); |
261 } | 220 } |
262 base::Closure* echo_callback = new base::Closure( | 221 |
263 base::Bind(&GpuRasterizer::DidEchoCallback, | 222 // Listen for completion. |
264 weak_ptr_factory_.GetWeakPtr(), frame_callback_)); | 223 TRACE_EVENT_ASYNC_BEGIN0("gfx", "MGLEcho", frame_number); |
265 frame_callback_.Reset(); | 224 MGLEcho(&GpuRasterizer::OnMGLEchoReply, this); |
266 TRACE_EVENT_ASYNC_BEGIN0("gfx", "SwapBuffers Echo", echo_callback); | |
267 MGLEcho(DidEcho, echo_callback); | |
268 frames_pending_++; | |
269 TRACE_COUNTER1("gfx", "GpuRasterizer::frames_pending_", frames_pending_); | |
270 } | 225 } |
271 | 226 |
272 void GpuRasterizer::DidEchoCallback(FrameCallback frame_callback) { | 227 void GpuRasterizer::DrawFinished(bool presented) { |
273 frames_pending_--; | 228 DCHECK(frames_in_progress_); |
274 TRACE_COUNTER1("gfx", "GpuRasterizer::frames_pending_", frames_pending_); | 229 |
275 TRACE_EVENT0("gfx", "GpuRasterizer::DidEchoCallback"); | 230 uint32_t frame_number = total_frames_ - frames_in_progress_; |
276 // Signal pending callback for backpressure. | 231 frames_in_progress_--; |
277 if (!frame_callback.is_null()) { | 232 TRACE_EVENT2("gfx", "GpuRasterizer::DrawFinished", "num", frame_number, |
278 frame_callback.Run(true); | 233 "presented", presented); |
279 frame_callback.Reset(); | 234 TRACE_EVENT_ASYNC_END0("gfx", "MGLEcho", frame_number); |
280 } | 235 |
| 236 callbacks_->OnRasterizerFinishedDraw(presented); |
281 } | 237 } |
282 | 238 |
283 void GpuRasterizer::PostErrorCallback() { | 239 void GpuRasterizer::OnMGLEchoReply(void* context) { |
284 task_runner_->PostTask(FROM_HERE, error_callback_); | 240 auto rasterizer = static_cast<GpuRasterizer*>(context); |
| 241 if (rasterizer->ready_) |
| 242 rasterizer->DrawFinished(true /*presented*/); |
285 } | 243 } |
286 | 244 |
287 } // namespace compositor | 245 } // namespace compositor |
OLD | NEW |