OLD | NEW |
| (Empty) |
1 // Copyright 2012 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 "cc/output/direct_renderer.h" | |
6 | |
7 #include <utility> | |
8 #include <vector> | |
9 | |
10 #include "base/containers/hash_tables.h" | |
11 #include "base/containers/scoped_ptr_hash_map.h" | |
12 #include "base/metrics/histogram.h" | |
13 #include "base/trace_event/trace_event.h" | |
14 #include "cc/base/math_util.h" | |
15 #include "cc/output/bsp_tree.h" | |
16 #include "cc/output/bsp_walk_action.h" | |
17 #include "cc/output/copy_output_request.h" | |
18 #include "cc/quads/draw_quad.h" | |
19 #include "ui/gfx/geometry/rect_conversions.h" | |
20 #include "ui/gfx/transform.h" | |
21 | |
22 static gfx::Transform OrthoProjectionMatrix(float left, | |
23 float right, | |
24 float bottom, | |
25 float top) { | |
26 // Use the standard formula to map the clipping frustum to the cube from | |
27 // [-1, -1, -1] to [1, 1, 1]. | |
28 float delta_x = right - left; | |
29 float delta_y = top - bottom; | |
30 gfx::Transform proj; | |
31 if (!delta_x || !delta_y) | |
32 return proj; | |
33 proj.matrix().set(0, 0, 2.0f / delta_x); | |
34 proj.matrix().set(0, 3, -(right + left) / delta_x); | |
35 proj.matrix().set(1, 1, 2.0f / delta_y); | |
36 proj.matrix().set(1, 3, -(top + bottom) / delta_y); | |
37 | |
38 // Z component of vertices is always set to zero as we don't use the depth | |
39 // buffer while drawing. | |
40 proj.matrix().set(2, 2, 0); | |
41 | |
42 return proj; | |
43 } | |
44 | |
45 static gfx::Transform window_matrix(int x, int y, int width, int height) { | |
46 gfx::Transform canvas; | |
47 | |
48 // Map to window position and scale up to pixel coordinates. | |
49 canvas.Translate3d(x, y, 0); | |
50 canvas.Scale3d(width, height, 0); | |
51 | |
52 // Map from ([-1, -1] to [1, 1]) -> ([0, 0] to [1, 1]) | |
53 canvas.Translate3d(0.5, 0.5, 0.5); | |
54 canvas.Scale3d(0.5, 0.5, 0.5); | |
55 | |
56 return canvas; | |
57 } | |
58 | |
59 namespace cc { | |
60 | |
61 DirectRenderer::DrawingFrame::DrawingFrame() | |
62 : root_render_pass(NULL), current_render_pass(NULL), current_texture(NULL) { | |
63 } | |
64 | |
65 DirectRenderer::DrawingFrame::~DrawingFrame() {} | |
66 | |
67 // | |
68 // static | |
69 gfx::RectF DirectRenderer::QuadVertexRect() { | |
70 return gfx::RectF(-0.5f, -0.5f, 1.f, 1.f); | |
71 } | |
72 | |
73 // static | |
74 void DirectRenderer::QuadRectTransform(gfx::Transform* quad_rect_transform, | |
75 const gfx::Transform& quad_transform, | |
76 const gfx::RectF& quad_rect) { | |
77 *quad_rect_transform = quad_transform; | |
78 quad_rect_transform->Translate(0.5 * quad_rect.width() + quad_rect.x(), | |
79 0.5 * quad_rect.height() + quad_rect.y()); | |
80 quad_rect_transform->Scale(quad_rect.width(), quad_rect.height()); | |
81 } | |
82 | |
83 void DirectRenderer::InitializeViewport(DrawingFrame* frame, | |
84 const gfx::Rect& draw_rect, | |
85 const gfx::Rect& viewport_rect, | |
86 const gfx::Size& surface_size) { | |
87 DCHECK_GE(viewport_rect.x(), 0); | |
88 DCHECK_GE(viewport_rect.y(), 0); | |
89 DCHECK_LE(viewport_rect.right(), surface_size.width()); | |
90 DCHECK_LE(viewport_rect.bottom(), surface_size.height()); | |
91 bool flip_y = FlippedFramebuffer(frame); | |
92 if (flip_y) { | |
93 frame->projection_matrix = OrthoProjectionMatrix(draw_rect.x(), | |
94 draw_rect.right(), | |
95 draw_rect.bottom(), | |
96 draw_rect.y()); | |
97 } else { | |
98 frame->projection_matrix = OrthoProjectionMatrix(draw_rect.x(), | |
99 draw_rect.right(), | |
100 draw_rect.y(), | |
101 draw_rect.bottom()); | |
102 } | |
103 | |
104 gfx::Rect window_rect = viewport_rect; | |
105 if (flip_y) | |
106 window_rect.set_y(surface_size.height() - viewport_rect.bottom()); | |
107 frame->window_matrix = window_matrix(window_rect.x(), | |
108 window_rect.y(), | |
109 window_rect.width(), | |
110 window_rect.height()); | |
111 SetDrawViewport(window_rect); | |
112 | |
113 current_draw_rect_ = draw_rect; | |
114 current_viewport_rect_ = viewport_rect; | |
115 current_surface_size_ = surface_size; | |
116 } | |
117 | |
118 gfx::Rect DirectRenderer::MoveFromDrawToWindowSpace( | |
119 const DrawingFrame* frame, | |
120 const gfx::Rect& draw_rect) const { | |
121 gfx::Rect window_rect = draw_rect; | |
122 window_rect -= current_draw_rect_.OffsetFromOrigin(); | |
123 window_rect += current_viewport_rect_.OffsetFromOrigin(); | |
124 if (FlippedFramebuffer(frame)) | |
125 window_rect.set_y(current_surface_size_.height() - window_rect.bottom()); | |
126 return window_rect; | |
127 } | |
128 | |
129 DirectRenderer::DirectRenderer(RendererClient* client, | |
130 const RendererSettings* settings, | |
131 OutputSurface* output_surface, | |
132 ResourceProvider* resource_provider) | |
133 : Renderer(client, settings), | |
134 output_surface_(output_surface), | |
135 resource_provider_(resource_provider), | |
136 overlay_processor_( | |
137 new OverlayProcessor(output_surface, resource_provider)) { | |
138 overlay_processor_->Initialize(); | |
139 } | |
140 | |
141 DirectRenderer::~DirectRenderer() {} | |
142 | |
143 void DirectRenderer::SetEnlargePassTextureAmountForTesting( | |
144 const gfx::Vector2d& amount) { | |
145 enlarge_pass_texture_amount_ = amount; | |
146 } | |
147 | |
148 void DirectRenderer::DecideRenderPassAllocationsForFrame( | |
149 const RenderPassList& render_passes_in_draw_order) { | |
150 if (!resource_provider_) | |
151 return; | |
152 | |
153 base::hash_map<RenderPassId, gfx::Size> render_passes_in_frame; | |
154 for (size_t i = 0; i < render_passes_in_draw_order.size(); ++i) | |
155 render_passes_in_frame.insert(std::pair<RenderPassId, gfx::Size>( | |
156 render_passes_in_draw_order[i]->id, | |
157 RenderPassTextureSize(render_passes_in_draw_order[i]))); | |
158 | |
159 std::vector<RenderPassId> passes_to_delete; | |
160 base::ScopedPtrHashMap<RenderPassId, scoped_ptr<ScopedResource>>::const_iterat
or | |
161 pass_iter; | |
162 for (pass_iter = render_pass_textures_.begin(); | |
163 pass_iter != render_pass_textures_.end(); | |
164 ++pass_iter) { | |
165 base::hash_map<RenderPassId, gfx::Size>::const_iterator it = | |
166 render_passes_in_frame.find(pass_iter->first); | |
167 if (it == render_passes_in_frame.end()) { | |
168 passes_to_delete.push_back(pass_iter->first); | |
169 continue; | |
170 } | |
171 | |
172 gfx::Size required_size = it->second; | |
173 ScopedResource* texture = pass_iter->second; | |
174 DCHECK(texture); | |
175 | |
176 bool size_appropriate = texture->size().width() >= required_size.width() && | |
177 texture->size().height() >= required_size.height(); | |
178 if (texture->id() && !size_appropriate) | |
179 texture->Free(); | |
180 } | |
181 | |
182 // Delete RenderPass textures from the previous frame that will not be used | |
183 // again. | |
184 for (size_t i = 0; i < passes_to_delete.size(); ++i) | |
185 render_pass_textures_.erase(passes_to_delete[i]); | |
186 | |
187 for (size_t i = 0; i < render_passes_in_draw_order.size(); ++i) { | |
188 if (!render_pass_textures_.contains(render_passes_in_draw_order[i]->id)) { | |
189 scoped_ptr<ScopedResource> texture = | |
190 ScopedResource::Create(resource_provider_); | |
191 render_pass_textures_.set(render_passes_in_draw_order[i]->id, | |
192 texture.Pass()); | |
193 } | |
194 } | |
195 } | |
196 | |
197 void DirectRenderer::DrawFrame(RenderPassList* render_passes_in_draw_order, | |
198 float device_scale_factor, | |
199 const gfx::Rect& device_viewport_rect, | |
200 const gfx::Rect& device_clip_rect, | |
201 bool disable_picture_quad_image_filtering) { | |
202 TRACE_EVENT0("cc", "DirectRenderer::DrawFrame"); | |
203 UMA_HISTOGRAM_COUNTS("Renderer4.renderPassCount", | |
204 render_passes_in_draw_order->size()); | |
205 | |
206 const RenderPass* root_render_pass = render_passes_in_draw_order->back(); | |
207 DCHECK(root_render_pass); | |
208 | |
209 DrawingFrame frame; | |
210 frame.render_passes_in_draw_order = render_passes_in_draw_order; | |
211 frame.root_render_pass = root_render_pass; | |
212 frame.root_damage_rect = Capabilities().using_partial_swap | |
213 ? root_render_pass->damage_rect | |
214 : root_render_pass->output_rect; | |
215 frame.root_damage_rect.Intersect(gfx::Rect(device_viewport_rect.size())); | |
216 frame.device_viewport_rect = device_viewport_rect; | |
217 frame.device_clip_rect = device_clip_rect; | |
218 frame.disable_picture_quad_image_filtering = | |
219 disable_picture_quad_image_filtering; | |
220 | |
221 overlay_processor_->ProcessForOverlays(render_passes_in_draw_order, | |
222 &frame.overlay_list); | |
223 | |
224 EnsureBackbuffer(); | |
225 | |
226 // Only reshape when we know we are going to draw. Otherwise, the reshape | |
227 // can leave the window at the wrong size if we never draw and the proper | |
228 // viewport size is never set. | |
229 output_surface_->Reshape(device_viewport_rect.size(), device_scale_factor); | |
230 | |
231 BeginDrawingFrame(&frame); | |
232 for (size_t i = 0; i < render_passes_in_draw_order->size(); ++i) { | |
233 RenderPass* pass = render_passes_in_draw_order->at(i); | |
234 DrawRenderPass(&frame, pass); | |
235 | |
236 for (ScopedPtrVector<CopyOutputRequest>::iterator it = | |
237 pass->copy_requests.begin(); | |
238 it != pass->copy_requests.end(); | |
239 ++it) { | |
240 if (it != pass->copy_requests.begin()) { | |
241 // Doing a readback is destructive of our state on Mac, so make sure | |
242 // we restore the state between readbacks. http://crbug.com/99393. | |
243 UseRenderPass(&frame, pass); | |
244 } | |
245 CopyCurrentRenderPassToBitmap(&frame, pass->copy_requests.take(it)); | |
246 } | |
247 } | |
248 FinishDrawingFrame(&frame); | |
249 | |
250 render_passes_in_draw_order->clear(); | |
251 } | |
252 | |
253 gfx::Rect DirectRenderer::ComputeScissorRectForRenderPass( | |
254 const DrawingFrame* frame) { | |
255 gfx::Rect render_pass_scissor = frame->current_render_pass->output_rect; | |
256 | |
257 if (frame->root_damage_rect == frame->root_render_pass->output_rect || | |
258 !frame->current_render_pass->copy_requests.empty()) | |
259 return render_pass_scissor; | |
260 | |
261 gfx::Transform inverse_transform(gfx::Transform::kSkipInitialization); | |
262 if (frame->current_render_pass->transform_to_root_target.GetInverse( | |
263 &inverse_transform)) { | |
264 // Only intersect inverse-projected damage if the transform is invertible. | |
265 gfx::Rect damage_rect_in_render_pass_space = | |
266 MathUtil::ProjectEnclosingClippedRect(inverse_transform, | |
267 frame->root_damage_rect); | |
268 render_pass_scissor.Intersect(damage_rect_in_render_pass_space); | |
269 } | |
270 | |
271 return render_pass_scissor; | |
272 } | |
273 | |
274 bool DirectRenderer::NeedDeviceClip(const DrawingFrame* frame) const { | |
275 if (frame->current_render_pass != frame->root_render_pass) | |
276 return false; | |
277 | |
278 return !frame->device_clip_rect.Contains(frame->device_viewport_rect); | |
279 } | |
280 | |
281 gfx::Rect DirectRenderer::DeviceClipRectInDrawSpace( | |
282 const DrawingFrame* frame) const { | |
283 gfx::Rect device_clip_rect = frame->device_clip_rect; | |
284 device_clip_rect -= current_viewport_rect_.OffsetFromOrigin(); | |
285 device_clip_rect += current_draw_rect_.OffsetFromOrigin(); | |
286 return device_clip_rect; | |
287 } | |
288 | |
289 gfx::Rect DirectRenderer::DeviceViewportRectInDrawSpace( | |
290 const DrawingFrame* frame) const { | |
291 gfx::Rect device_viewport_rect = frame->device_viewport_rect; | |
292 device_viewport_rect -= current_viewport_rect_.OffsetFromOrigin(); | |
293 device_viewport_rect += current_draw_rect_.OffsetFromOrigin(); | |
294 return device_viewport_rect; | |
295 } | |
296 | |
297 gfx::Rect DirectRenderer::OutputSurfaceRectInDrawSpace( | |
298 const DrawingFrame* frame) const { | |
299 if (frame->current_render_pass == frame->root_render_pass) { | |
300 gfx::Rect output_surface_rect(output_surface_->SurfaceSize()); | |
301 output_surface_rect -= current_viewport_rect_.OffsetFromOrigin(); | |
302 output_surface_rect += current_draw_rect_.OffsetFromOrigin(); | |
303 return output_surface_rect; | |
304 } else { | |
305 return frame->current_render_pass->output_rect; | |
306 } | |
307 } | |
308 | |
309 bool DirectRenderer::ShouldSkipQuad(const DrawQuad& quad, | |
310 const gfx::Rect& render_pass_scissor) { | |
311 if (render_pass_scissor.IsEmpty()) | |
312 return true; | |
313 | |
314 if (quad.isClipped()) { | |
315 gfx::Rect r = quad.clipRect(); | |
316 r.Intersect(render_pass_scissor); | |
317 return r.IsEmpty(); | |
318 } | |
319 | |
320 return false; | |
321 } | |
322 | |
323 void DirectRenderer::SetScissorStateForQuad( | |
324 const DrawingFrame* frame, | |
325 const DrawQuad& quad, | |
326 const gfx::Rect& render_pass_scissor, | |
327 bool use_render_pass_scissor) { | |
328 if (use_render_pass_scissor) { | |
329 gfx::Rect quad_scissor_rect = render_pass_scissor; | |
330 if (quad.isClipped()) | |
331 quad_scissor_rect.Intersect(quad.clipRect()); | |
332 SetScissorTestRectInDrawSpace(frame, quad_scissor_rect); | |
333 return; | |
334 } else if (quad.isClipped()) { | |
335 SetScissorTestRectInDrawSpace(frame, quad.clipRect()); | |
336 return; | |
337 } | |
338 | |
339 EnsureScissorTestDisabled(); | |
340 } | |
341 | |
342 void DirectRenderer::SetScissorTestRectInDrawSpace( | |
343 const DrawingFrame* frame, | |
344 const gfx::Rect& draw_space_rect) { | |
345 gfx::Rect window_space_rect = | |
346 MoveFromDrawToWindowSpace(frame, draw_space_rect); | |
347 SetScissorTestRect(window_space_rect); | |
348 } | |
349 | |
350 void DirectRenderer::FinishDrawingQuadList() {} | |
351 | |
352 void DirectRenderer::DoDrawPolygon(const DrawPolygon& poly, | |
353 DrawingFrame* frame, | |
354 const gfx::Rect& render_pass_scissor, | |
355 bool use_render_pass_scissor) { | |
356 SetScissorStateForQuad(frame, *poly.original_ref(), render_pass_scissor, | |
357 use_render_pass_scissor); | |
358 | |
359 // If the poly has not been split, then it is just a normal DrawQuad, | |
360 // and we should save any extra processing that would have to be done. | |
361 if (!poly.is_split()) { | |
362 DoDrawQuad(frame, poly.original_ref(), NULL); | |
363 return; | |
364 } | |
365 | |
366 std::vector<gfx::QuadF> quads; | |
367 poly.ToQuads2D(&quads); | |
368 for (size_t i = 0; i < quads.size(); ++i) { | |
369 DoDrawQuad(frame, poly.original_ref(), &quads[i]); | |
370 } | |
371 } | |
372 | |
373 void DirectRenderer::FlushPolygons(ScopedPtrDeque<DrawPolygon>* poly_list, | |
374 DrawingFrame* frame, | |
375 const gfx::Rect& render_pass_scissor, | |
376 bool use_render_pass_scissor) { | |
377 if (poly_list->empty()) { | |
378 return; | |
379 } | |
380 | |
381 BspTree bsp_tree(poly_list); | |
382 BspWalkActionDrawPolygon action_handler(this, frame, render_pass_scissor, | |
383 use_render_pass_scissor); | |
384 bsp_tree.TraverseWithActionHandler(&action_handler); | |
385 DCHECK(poly_list->empty()); | |
386 } | |
387 | |
388 void DirectRenderer::DrawRenderPass(DrawingFrame* frame, | |
389 const RenderPass* render_pass) { | |
390 TRACE_EVENT0("cc", "DirectRenderer::DrawRenderPass"); | |
391 if (!UseRenderPass(frame, render_pass)) | |
392 return; | |
393 | |
394 const gfx::Rect surface_rect_in_draw_space = | |
395 OutputSurfaceRectInDrawSpace(frame); | |
396 gfx::Rect render_pass_scissor_in_draw_space = surface_rect_in_draw_space; | |
397 | |
398 if (frame->current_render_pass == frame->root_render_pass) { | |
399 render_pass_scissor_in_draw_space.Intersect( | |
400 DeviceViewportRectInDrawSpace(frame)); | |
401 } | |
402 | |
403 if (Capabilities().using_partial_swap) { | |
404 render_pass_scissor_in_draw_space.Intersect( | |
405 ComputeScissorRectForRenderPass(frame)); | |
406 } | |
407 | |
408 if (NeedDeviceClip(frame)) { | |
409 render_pass_scissor_in_draw_space.Intersect( | |
410 DeviceClipRectInDrawSpace(frame)); | |
411 } | |
412 | |
413 bool render_pass_is_clipped = | |
414 !render_pass_scissor_in_draw_space.Contains(surface_rect_in_draw_space); | |
415 bool is_root_render_pass = | |
416 frame->current_render_pass == frame->root_render_pass; | |
417 bool has_external_stencil_test = | |
418 is_root_render_pass && output_surface_->HasExternalStencilTest(); | |
419 bool should_clear_surface = | |
420 !has_external_stencil_test && | |
421 (!is_root_render_pass || settings_->should_clear_root_render_pass); | |
422 | |
423 // If |has_external_stencil_test| we can't discard or clear. Make sure we | |
424 // don't need to. | |
425 DCHECK_IMPLIES(has_external_stencil_test, | |
426 !frame->current_render_pass->has_transparent_background); | |
427 | |
428 SurfaceInitializationMode mode; | |
429 if (should_clear_surface && render_pass_is_clipped) { | |
430 mode = SURFACE_INITIALIZATION_MODE_SCISSORED_CLEAR; | |
431 } else if (should_clear_surface) { | |
432 mode = SURFACE_INITIALIZATION_MODE_FULL_SURFACE_CLEAR; | |
433 } else { | |
434 mode = SURFACE_INITIALIZATION_MODE_PRESERVE; | |
435 } | |
436 | |
437 PrepareSurfaceForPass( | |
438 frame, mode, | |
439 MoveFromDrawToWindowSpace(frame, render_pass_scissor_in_draw_space)); | |
440 | |
441 const QuadList& quad_list = render_pass->quad_list; | |
442 ScopedPtrDeque<DrawPolygon> poly_list; | |
443 | |
444 int next_polygon_id = 0; | |
445 int last_sorting_context_id = 0; | |
446 for (auto it = quad_list.BackToFrontBegin(); it != quad_list.BackToFrontEnd(); | |
447 ++it) { | |
448 const DrawQuad& quad = **it; | |
449 gfx::QuadF send_quad(quad.visible_rect); | |
450 | |
451 if (render_pass_is_clipped && | |
452 ShouldSkipQuad(quad, render_pass_scissor_in_draw_space)) { | |
453 continue; | |
454 } | |
455 | |
456 if (last_sorting_context_id != quad.shared_quad_state->sorting_context_id) { | |
457 last_sorting_context_id = quad.shared_quad_state->sorting_context_id; | |
458 FlushPolygons(&poly_list, frame, render_pass_scissor_in_draw_space, | |
459 render_pass_is_clipped); | |
460 } | |
461 | |
462 // This layer is in a 3D sorting context so we add it to the list of | |
463 // polygons to go into the BSP tree. | |
464 if (quad.shared_quad_state->sorting_context_id != 0) { | |
465 scoped_ptr<DrawPolygon> new_polygon(new DrawPolygon( | |
466 *it, quad.visible_rect, quad.quadTransform(), next_polygon_id++)); | |
467 if (new_polygon->points().size() > 2u) { | |
468 poly_list.push_back(new_polygon.Pass()); | |
469 } | |
470 continue; | |
471 } | |
472 | |
473 // We are not in a 3d sorting context, so we should draw the quad normally. | |
474 SetScissorStateForQuad(frame, quad, render_pass_scissor_in_draw_space, | |
475 render_pass_is_clipped); | |
476 | |
477 DoDrawQuad(frame, &quad, nullptr); | |
478 } | |
479 FlushPolygons(&poly_list, frame, render_pass_scissor_in_draw_space, | |
480 render_pass_is_clipped); | |
481 FinishDrawingQuadList(); | |
482 } | |
483 | |
484 bool DirectRenderer::UseRenderPass(DrawingFrame* frame, | |
485 const RenderPass* render_pass) { | |
486 frame->current_render_pass = render_pass; | |
487 frame->current_texture = NULL; | |
488 | |
489 if (render_pass == frame->root_render_pass) { | |
490 BindFramebufferToOutputSurface(frame); | |
491 InitializeViewport(frame, | |
492 render_pass->output_rect, | |
493 frame->device_viewport_rect, | |
494 output_surface_->SurfaceSize()); | |
495 return true; | |
496 } | |
497 | |
498 ScopedResource* texture = render_pass_textures_.get(render_pass->id); | |
499 DCHECK(texture); | |
500 | |
501 gfx::Size size = RenderPassTextureSize(render_pass); | |
502 size.Enlarge(enlarge_pass_texture_amount_.x(), | |
503 enlarge_pass_texture_amount_.y()); | |
504 if (!texture->id()) | |
505 texture->Allocate( | |
506 size, ResourceProvider::TEXTURE_HINT_IMMUTABLE_FRAMEBUFFER, RGBA_8888); | |
507 DCHECK(texture->id()); | |
508 | |
509 if (BindFramebufferToTexture(frame, texture, render_pass->output_rect)) { | |
510 InitializeViewport(frame, render_pass->output_rect, | |
511 gfx::Rect(render_pass->output_rect.size()), | |
512 render_pass->output_rect.size()); | |
513 return true; | |
514 } | |
515 | |
516 return false; | |
517 } | |
518 | |
519 bool DirectRenderer::HasAllocatedResourcesForTesting(RenderPassId id) const { | |
520 ScopedResource* texture = render_pass_textures_.get(id); | |
521 return texture && texture->id(); | |
522 } | |
523 | |
524 // static | |
525 gfx::Size DirectRenderer::RenderPassTextureSize(const RenderPass* render_pass) { | |
526 return render_pass->output_rect.size(); | |
527 } | |
528 | |
529 } // namespace cc | |
OLD | NEW |