Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(457)

Side by Side Diff: cc/trees/layer_tree_host_impl.cc

Issue 915083004: cc: Make occlusion a draw property. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: occlusiondrawproperty: notvirtual Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2011 The Chromium Authors. All rights reserved. 1 // Copyright 2011 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 "cc/trees/layer_tree_host_impl.h" 5 #include "cc/trees/layer_tree_host_impl.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <limits> 8 #include <limits>
9 9
10 #include "base/basictypes.h" 10 #include "base/basictypes.h"
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
61 #include "cc/resources/software_rasterizer.h" 61 #include "cc/resources/software_rasterizer.h"
62 #include "cc/resources/texture_mailbox_deleter.h" 62 #include "cc/resources/texture_mailbox_deleter.h"
63 #include "cc/resources/tile_task_worker_pool.h" 63 #include "cc/resources/tile_task_worker_pool.h"
64 #include "cc/resources/ui_resource_bitmap.h" 64 #include "cc/resources/ui_resource_bitmap.h"
65 #include "cc/resources/zero_copy_tile_task_worker_pool.h" 65 #include "cc/resources/zero_copy_tile_task_worker_pool.h"
66 #include "cc/scheduler/delay_based_time_source.h" 66 #include "cc/scheduler/delay_based_time_source.h"
67 #include "cc/trees/damage_tracker.h" 67 #include "cc/trees/damage_tracker.h"
68 #include "cc/trees/layer_tree_host.h" 68 #include "cc/trees/layer_tree_host.h"
69 #include "cc/trees/layer_tree_host_common.h" 69 #include "cc/trees/layer_tree_host_common.h"
70 #include "cc/trees/layer_tree_impl.h" 70 #include "cc/trees/layer_tree_impl.h"
71 #include "cc/trees/occlusion_tracker.h"
72 #include "cc/trees/single_thread_proxy.h" 71 #include "cc/trees/single_thread_proxy.h"
73 #include "cc/trees/tree_synchronizer.h" 72 #include "cc/trees/tree_synchronizer.h"
74 #include "gpu/command_buffer/client/gles2_interface.h" 73 #include "gpu/command_buffer/client/gles2_interface.h"
75 #include "gpu/GLES2/gl2extchromium.h" 74 #include "gpu/GLES2/gl2extchromium.h"
76 #include "ui/gfx/frame_time.h" 75 #include "ui/gfx/frame_time.h"
77 #include "ui/gfx/geometry/rect_conversions.h" 76 #include "ui/gfx/geometry/rect_conversions.h"
78 #include "ui/gfx/geometry/size_conversions.h" 77 #include "ui/gfx/geometry/size_conversions.h"
79 #include "ui/gfx/geometry/vector2d_conversions.h" 78 #include "ui/gfx/geometry/vector2d_conversions.h"
80 79
81 namespace cc { 80 namespace cc {
(...skipping 503 matching lines...) Expand 10 before | Expand all | Expand 10 after
585 !output_surface_->capabilities().deferred_gl_initialization) 584 !output_surface_->capabilities().deferred_gl_initialization)
586 << output_surface_->capabilities().delegated_rendering << " " 585 << output_surface_->capabilities().delegated_rendering << " "
587 << output_surface_->capabilities().deferred_gl_initialization; 586 << output_surface_->capabilities().deferred_gl_initialization;
588 return DRAW_MODE_SOFTWARE; 587 return DRAW_MODE_SOFTWARE;
589 } 588 }
590 } 589 }
591 590
592 static void AppendQuadsForLayer( 591 static void AppendQuadsForLayer(
593 RenderPass* target_render_pass, 592 RenderPass* target_render_pass,
594 LayerImpl* layer, 593 LayerImpl* layer,
595 const OcclusionTracker<LayerImpl>& occlusion_tracker,
596 AppendQuadsData* append_quads_data) { 594 AppendQuadsData* append_quads_data) {
597 layer->AppendQuads( 595 layer->AppendQuads(target_render_pass,
598 target_render_pass, 596 layer->draw_properties().occlusion_in_content_space,
599 occlusion_tracker.GetCurrentOcclusionForLayer(layer->draw_transform()), 597 append_quads_data);
600 append_quads_data);
601 } 598 }
602 599
603 static void AppendQuadsForRenderSurfaceLayer( 600 static void AppendQuadsForRenderSurfaceLayer(
604 RenderPass* target_render_pass, 601 RenderPass* target_render_pass,
605 LayerImpl* layer, 602 LayerImpl* layer,
606 const RenderPass* contributing_render_pass, 603 const RenderPass* contributing_render_pass,
607 const OcclusionTracker<LayerImpl>& occlusion_tracker,
608 AppendQuadsData* append_quads_data) { 604 AppendQuadsData* append_quads_data) {
609 RenderSurfaceImpl* surface = layer->render_surface(); 605 RenderSurfaceImpl* surface = layer->render_surface();
610 const gfx::Transform& draw_transform = surface->draw_transform(); 606 const gfx::Transform& draw_transform = surface->draw_transform();
611 const Occlusion& occlusion = 607 const Occlusion& occlusion = surface->occlusion_in_content_space();
612 occlusion_tracker.GetCurrentOcclusionForContributingSurface(
613 draw_transform);
614 SkColor debug_border_color = surface->GetDebugBorderColor(); 608 SkColor debug_border_color = surface->GetDebugBorderColor();
615 float debug_border_width = surface->GetDebugBorderWidth(); 609 float debug_border_width = surface->GetDebugBorderWidth();
616 LayerImpl* mask_layer = layer->mask_layer(); 610 LayerImpl* mask_layer = layer->mask_layer();
617 611
618 surface->AppendQuads(target_render_pass, draw_transform, occlusion, 612 surface->AppendQuads(target_render_pass, draw_transform, occlusion,
619 debug_border_color, debug_border_width, mask_layer, 613 debug_border_color, debug_border_width, mask_layer,
620 append_quads_data, contributing_render_pass->id); 614 append_quads_data, contributing_render_pass->id);
621 615
622 // Add replica after the surface so that it appears below the surface. 616 // Add replica after the surface so that it appears below the surface.
623 if (layer->has_replica()) { 617 if (layer->has_replica()) {
624 const gfx::Transform& replica_draw_transform = 618 const gfx::Transform& replica_draw_transform =
625 surface->replica_draw_transform(); 619 surface->replica_draw_transform();
626 const Occlusion& replica_occlusion = 620 const Occlusion& replica_occlusion =
627 occlusion_tracker.GetCurrentOcclusionForContributingSurface( 621 surface->replica_occlusion_in_content_space();
628 replica_draw_transform);
629 SkColor replica_debug_border_color = surface->GetReplicaDebugBorderColor(); 622 SkColor replica_debug_border_color = surface->GetReplicaDebugBorderColor();
630 float replica_debug_border_width = surface->GetReplicaDebugBorderWidth(); 623 float replica_debug_border_width = surface->GetReplicaDebugBorderWidth();
631 // TODO(danakj): By using the same RenderSurfaceImpl for both the 624 // TODO(danakj): By using the same RenderSurfaceImpl for both the
632 // content and its reflection, it's currently not possible to apply a 625 // content and its reflection, it's currently not possible to apply a
633 // separate mask to the reflection layer or correctly handle opacity in 626 // separate mask to the reflection layer or correctly handle opacity in
634 // reflections (opacity must be applied after drawing both the layer and its 627 // reflections (opacity must be applied after drawing both the layer and its
635 // reflection). The solution is to introduce yet another RenderSurfaceImpl 628 // reflection). The solution is to introduce yet another RenderSurfaceImpl
636 // to draw the layer and its reflection in. For now we only apply a separate 629 // to draw the layer and its reflection in. For now we only apply a separate
637 // reflection mask if the contents don't have a mask of their own. 630 // reflection mask if the contents don't have a mask of their own.
638 LayerImpl* replica_mask_layer = 631 LayerImpl* replica_mask_layer =
639 mask_layer ? mask_layer : layer->replica_layer()->mask_layer(); 632 mask_layer ? mask_layer : layer->replica_layer()->mask_layer();
640 633
641 surface->AppendQuads(target_render_pass, replica_draw_transform, 634 surface->AppendQuads(target_render_pass, replica_draw_transform,
642 replica_occlusion, replica_debug_border_color, 635 replica_occlusion, replica_debug_border_color,
643 replica_debug_border_width, replica_mask_layer, 636 replica_debug_border_width, replica_mask_layer,
644 append_quads_data, contributing_render_pass->id); 637 append_quads_data, contributing_render_pass->id);
645 } 638 }
646 } 639 }
647 640
648 static void AppendQuadsToFillScreen( 641 static void AppendQuadsToFillScreen(const gfx::Rect& root_scroll_layer_rect,
649 const gfx::Rect& root_scroll_layer_rect, 642 RenderPass* target_render_pass,
650 RenderPass* target_render_pass, 643 LayerImpl* root_layer,
651 LayerImpl* root_layer, 644 SkColor screen_background_color,
652 SkColor screen_background_color, 645 const Region& fill_region) {
653 const OcclusionTracker<LayerImpl>& occlusion_tracker) {
654 if (!root_layer || !SkColorGetA(screen_background_color)) 646 if (!root_layer || !SkColorGetA(screen_background_color))
655 return; 647 return;
656
657 Region fill_region = occlusion_tracker.ComputeVisibleRegionInScreen();
658 if (fill_region.IsEmpty()) 648 if (fill_region.IsEmpty())
659 return; 649 return;
660 650
661 // Manually create the quad state for the gutter quads, as the root layer 651 // Manually create the quad state for the gutter quads, as the root layer
662 // doesn't have any bounds and so can't generate this itself. 652 // doesn't have any bounds and so can't generate this itself.
663 // TODO(danakj): Make the gutter quads generated by the solid color layer 653 // TODO(danakj): Make the gutter quads generated by the solid color layer
664 // (make it smarter about generating quads to fill unoccluded areas). 654 // (make it smarter about generating quads to fill unoccluded areas).
665 655
666 gfx::Rect root_target_rect = root_layer->render_surface()->content_rect(); 656 gfx::Rect root_target_rect = root_layer->render_surface()->content_rect();
667 float opacity = 1.f; 657 float opacity = 1.f;
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
751 // entire root surface. This will disable partial-swap/scissor optimizations 741 // entire root surface. This will disable partial-swap/scissor optimizations
752 // that would prevent the HUD from updating, since the HUD does not cause 742 // that would prevent the HUD from updating, since the HUD does not cause
753 // damage itself, to prevent it from messing with damage visualizations. Since 743 // damage itself, to prevent it from messing with damage visualizations. Since
754 // damage visualizations are done off the LayerImpls and RenderSurfaceImpls, 744 // damage visualizations are done off the LayerImpls and RenderSurfaceImpls,
755 // changing the RenderPass does not affect them. 745 // changing the RenderPass does not affect them.
756 if (active_tree_->hud_layer()) { 746 if (active_tree_->hud_layer()) {
757 RenderPass* root_pass = frame->render_passes.back(); 747 RenderPass* root_pass = frame->render_passes.back();
758 root_pass->damage_rect = root_pass->output_rect; 748 root_pass->damage_rect = root_pass->output_rect;
759 } 749 }
760 750
761 OcclusionTracker<LayerImpl> occlusion_tracker( 751 // Grab this region here before iterating layers. Taking copy requests from
vmpstr 2015/02/13 00:24:54 Do you mean to say that UnoccludedScreenSpaceRegio
danakj 2015/02/13 00:27:07 No, i mean that the dirty bit is set to true so as
762 active_tree_->root_layer()->render_surface()->content_rect()); 752 // the layers while constructing the render passes will dirty the render
763 occlusion_tracker.set_minimum_tracking_size( 753 // surface layer list and this unoccluded region.
764 settings_.minimum_occlusion_tracking_size); 754 const Region& unoccluded_screen_space_region =
765 755 active_tree_->UnoccludedScreenSpaceRegion();
766 // Add quads to the Render passes in front-to-back order to allow for testing
767 // occlusion and performing culling during the tree walk.
768 typedef LayerIterator<LayerImpl> LayerIteratorType;
769 756
770 // Typically when we are missing a texture and use a checkerboard quad, we 757 // Typically when we are missing a texture and use a checkerboard quad, we
771 // still draw the frame. However when the layer being checkerboarded is moving 758 // still draw the frame. However when the layer being checkerboarded is moving
772 // due to an impl-animation, we drop the frame to avoid flashing due to the 759 // due to an impl-animation, we drop the frame to avoid flashing due to the
773 // texture suddenly appearing in the future. 760 // texture suddenly appearing in the future.
774 DrawResult draw_result = DRAW_SUCCESS; 761 DrawResult draw_result = DRAW_SUCCESS;
775 // When we have a copy request for a layer, we need to draw no matter 762 // When we have a copy request for a layer, we need to draw no matter
776 // what, as the layer may disappear after this frame. 763 // what, as the layer may disappear after this frame.
777 bool have_copy_request = false; 764 bool have_copy_request = false;
778 765
779 int layers_drawn = 0; 766 int layers_drawn = 0;
780 767
781 const DrawMode draw_mode = GetDrawMode(); 768 const DrawMode draw_mode = GetDrawMode();
782 769
783 int num_missing_tiles = 0; 770 int num_missing_tiles = 0;
784 int num_incomplete_tiles = 0; 771 int num_incomplete_tiles = 0;
785 772
786 LayerIteratorType end = 773 auto end = LayerIterator<LayerImpl>::End(frame->render_surface_layer_list);
787 LayerIteratorType::End(frame->render_surface_layer_list); 774 for (auto it =
788 for (LayerIteratorType it = 775 LayerIterator<LayerImpl>::Begin(frame->render_surface_layer_list);
789 LayerIteratorType::Begin(frame->render_surface_layer_list); 776 it != end; ++it) {
790 it != end;
791 ++it) {
792 RenderPassId target_render_pass_id = 777 RenderPassId target_render_pass_id =
793 it.target_render_surface_layer()->render_surface()->GetRenderPassId(); 778 it.target_render_surface_layer()->render_surface()->GetRenderPassId();
794 RenderPass* target_render_pass = 779 RenderPass* target_render_pass =
795 frame->render_passes_by_id[target_render_pass_id]; 780 frame->render_passes_by_id[target_render_pass_id];
796 781
797 occlusion_tracker.EnterLayer(it);
798
799 AppendQuadsData append_quads_data; 782 AppendQuadsData append_quads_data;
800 783
801 if (it.represents_target_render_surface()) { 784 if (it.represents_target_render_surface()) {
802 if (it->HasCopyRequest()) { 785 if (it->HasCopyRequest()) {
803 have_copy_request = true; 786 have_copy_request = true;
804 it->TakeCopyRequestsAndTransformToTarget( 787 it->TakeCopyRequestsAndTransformToTarget(
805 &target_render_pass->copy_requests); 788 &target_render_pass->copy_requests);
806 } 789 }
807 } else if (it.represents_contributing_render_surface() && 790 } else if (it.represents_contributing_render_surface() &&
808 it->render_surface()->contributes_to_drawn_surface()) { 791 it->render_surface()->contributes_to_drawn_surface()) {
809 RenderPassId contributing_render_pass_id = 792 RenderPassId contributing_render_pass_id =
810 it->render_surface()->GetRenderPassId(); 793 it->render_surface()->GetRenderPassId();
811 RenderPass* contributing_render_pass = 794 RenderPass* contributing_render_pass =
812 frame->render_passes_by_id[contributing_render_pass_id]; 795 frame->render_passes_by_id[contributing_render_pass_id];
813 AppendQuadsForRenderSurfaceLayer(target_render_pass, 796 AppendQuadsForRenderSurfaceLayer(target_render_pass,
814 *it, 797 *it,
815 contributing_render_pass, 798 contributing_render_pass,
816 occlusion_tracker,
817 &append_quads_data); 799 &append_quads_data);
818 } else if (it.represents_itself() && 800 } else if (it.represents_itself() &&
819 !it->visible_content_rect().IsEmpty()) { 801 !it->visible_content_rect().IsEmpty()) {
820 bool occluded = 802 bool occluded =
821 occlusion_tracker.GetCurrentOcclusionForLayer(it->draw_transform()) 803 it->draw_properties().occlusion_in_content_space.IsOccluded(
822 .IsOccluded(it->visible_content_rect()); 804 it->visible_content_rect());
823 if (!occluded && it->WillDraw(draw_mode, resource_provider_.get())) { 805 if (!occluded && it->WillDraw(draw_mode, resource_provider_.get())) {
824 DCHECK_EQ(active_tree_, it->layer_tree_impl()); 806 DCHECK_EQ(active_tree_, it->layer_tree_impl());
825 807
826 frame->will_draw_layers.push_back(*it); 808 frame->will_draw_layers.push_back(*it);
827 809
828 if (it->HasContributingDelegatedRenderPasses()) { 810 if (it->HasContributingDelegatedRenderPasses()) {
829 RenderPassId contributing_render_pass_id = 811 RenderPassId contributing_render_pass_id =
830 it->FirstContributingRenderPassId(); 812 it->FirstContributingRenderPassId();
831 while (frame->render_passes_by_id.find(contributing_render_pass_id) != 813 while (frame->render_passes_by_id.find(contributing_render_pass_id) !=
832 frame->render_passes_by_id.end()) { 814 frame->render_passes_by_id.end()) {
833 RenderPass* render_pass = 815 RenderPass* render_pass =
834 frame->render_passes_by_id[contributing_render_pass_id]; 816 frame->render_passes_by_id[contributing_render_pass_id];
835 817
836 AppendQuadsForLayer(render_pass, 818 AppendQuadsForLayer(render_pass,
837 *it, 819 *it,
838 occlusion_tracker,
839 &append_quads_data); 820 &append_quads_data);
840 821
841 contributing_render_pass_id = 822 contributing_render_pass_id =
842 it->NextContributingRenderPassId(contributing_render_pass_id); 823 it->NextContributingRenderPassId(contributing_render_pass_id);
843 } 824 }
844 } 825 }
845 826
846 AppendQuadsForLayer(target_render_pass, 827 AppendQuadsForLayer(target_render_pass,
847 *it, 828 *it,
848 occlusion_tracker,
849 &append_quads_data); 829 &append_quads_data);
850 830
851 // For layers that represent themselves, add composite frame timing 831 // For layers that represent themselves, add composite frame timing
852 // requests if the visible rect intersects the requested rect. 832 // requests if the visible rect intersects the requested rect.
853 for (const auto& request : it->frame_timing_requests()) { 833 for (const auto& request : it->frame_timing_requests()) {
854 const gfx::Rect& request_content_rect = 834 const gfx::Rect& request_content_rect =
855 it->LayerRectToContentRect(request.rect()); 835 it->LayerRectToContentRect(request.rect());
856 if (request_content_rect.Intersects(it->visible_content_rect())) { 836 if (request_content_rect.Intersects(it->visible_content_rect())) {
857 frame->composite_events.push_back( 837 frame->composite_events.push_back(
858 FrameTimingTracker::FrameAndRectIds( 838 FrameTimingTracker::FrameAndRectIds(
(...skipping 19 matching lines...) Expand all
878 it->draw_transform_is_animating(); 858 it->draw_transform_is_animating();
879 if (layer_has_animating_transform) 859 if (layer_has_animating_transform)
880 draw_result = DRAW_ABORTED_CHECKERBOARD_ANIMATIONS; 860 draw_result = DRAW_ABORTED_CHECKERBOARD_ANIMATIONS;
881 } 861 }
882 862
883 if (append_quads_data.num_incomplete_tiles || 863 if (append_quads_data.num_incomplete_tiles ||
884 append_quads_data.num_missing_tiles) { 864 append_quads_data.num_missing_tiles) {
885 if (RequiresHighResToDraw()) 865 if (RequiresHighResToDraw())
886 draw_result = DRAW_ABORTED_MISSING_HIGH_RES_CONTENT; 866 draw_result = DRAW_ABORTED_MISSING_HIGH_RES_CONTENT;
887 } 867 }
888
889 occlusion_tracker.LeaveLayer(it);
890 } 868 }
891 869
892 if (have_copy_request || 870 if (have_copy_request ||
893 output_surface_->capabilities().draw_and_swap_full_viewport_every_frame) 871 output_surface_->capabilities().draw_and_swap_full_viewport_every_frame)
894 draw_result = DRAW_SUCCESS; 872 draw_result = DRAW_SUCCESS;
895 873
896 #if DCHECK_IS_ON() 874 #if DCHECK_IS_ON()
897 for (const auto& render_pass : frame->render_passes) { 875 for (const auto& render_pass : frame->render_passes) {
898 for (const auto& quad : render_pass->quad_list) 876 for (const auto& quad : render_pass->quad_list)
899 DCHECK(quad->shared_quad_state); 877 DCHECK(quad->shared_quad_state);
900 DCHECK(frame->render_passes_by_id.find(render_pass->id) != 878 DCHECK(frame->render_passes_by_id.find(render_pass->id) !=
901 frame->render_passes_by_id.end()); 879 frame->render_passes_by_id.end());
902 } 880 }
903 #endif 881 #endif
904 DCHECK(frame->render_passes.back()->output_rect.origin().IsOrigin()); 882 DCHECK(frame->render_passes.back()->output_rect.origin().IsOrigin());
905 883
906 if (!active_tree_->has_transparent_background()) { 884 if (!active_tree_->has_transparent_background()) {
907 frame->render_passes.back()->has_transparent_background = false; 885 frame->render_passes.back()->has_transparent_background = false;
908 AppendQuadsToFillScreen( 886 AppendQuadsToFillScreen(
909 active_tree_->RootScrollLayerDeviceViewportBounds(), 887 active_tree_->RootScrollLayerDeviceViewportBounds(),
910 frame->render_passes.back(), 888 frame->render_passes.back(), active_tree_->root_layer(),
911 active_tree_->root_layer(), 889 active_tree_->background_color(), unoccluded_screen_space_region);
912 active_tree_->background_color(),
913 occlusion_tracker);
914 } 890 }
915 891
916 RemoveRenderPasses(CullRenderPassesWithNoQuads(), frame); 892 RemoveRenderPasses(CullRenderPassesWithNoQuads(), frame);
917 renderer_->DecideRenderPassAllocationsForFrame(frame->render_passes); 893 renderer_->DecideRenderPassAllocationsForFrame(frame->render_passes);
918 894
919 // Any copy requests left in the tree are not going to get serviced, and 895 // Any copy requests left in the tree are not going to get serviced, and
920 // should be aborted. 896 // should be aborted.
921 ScopedPtrVector<CopyOutputRequest> requests_to_abort; 897 ScopedPtrVector<CopyOutputRequest> requests_to_abort;
922 while (!active_tree_->LayersWithCopyOutputRequest().empty()) { 898 while (!active_tree_->LayersWithCopyOutputRequest().empty()) {
923 LayerImpl* layer = active_tree_->LayersWithCopyOutputRequest().back(); 899 LayerImpl* layer = active_tree_->LayersWithCopyOutputRequest().back();
(...skipping 2571 matching lines...) Expand 10 before | Expand all | Expand 10 after
3495 (*it)->OnSetNeedsRedrawOnImpl(); 3471 (*it)->OnSetNeedsRedrawOnImpl();
3496 } 3472 }
3497 3473
3498 void LayerTreeHostImpl::NotifySwapPromiseMonitorsOfForwardingToMainThread() { 3474 void LayerTreeHostImpl::NotifySwapPromiseMonitorsOfForwardingToMainThread() {
3499 std::set<SwapPromiseMonitor*>::iterator it = swap_promise_monitor_.begin(); 3475 std::set<SwapPromiseMonitor*>::iterator it = swap_promise_monitor_.begin();
3500 for (; it != swap_promise_monitor_.end(); it++) 3476 for (; it != swap_promise_monitor_.end(); it++)
3501 (*it)->OnForwardScrollUpdateToMainThreadOnImpl(); 3477 (*it)->OnForwardScrollUpdateToMainThreadOnImpl();
3502 } 3478 }
3503 3479
3504 } // namespace cc 3480 } // namespace cc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698