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

Side by Side Diff: content/browser/compositor/software_output_device_mac.mm

Issue 2866683003: Use more than double buffering in SoftwareOutputDeviceMac (Closed)
Patch Set: Fix compile Created 3 years, 7 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 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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 "content/browser/compositor/software_output_device_mac.h" 5 #include "content/browser/compositor/software_output_device_mac.h"
6 6
7 #include "base/mac/foundation_util.h" 7 #include "base/mac/foundation_util.h"
8 #include "base/trace_event/trace_event.h" 8 #include "base/trace_event/trace_event.h"
9 #include "third_party/skia/include/core/SkCanvas.h" 9 #include "third_party/skia/include/core/SkCanvas.h"
10 #include "ui/accelerated_widget_mac/accelerated_widget_mac.h" 10 #include "ui/accelerated_widget_mac/accelerated_widget_mac.h"
11 #include "ui/compositor/compositor.h" 11 #include "ui/compositor/compositor.h"
12 #include "ui/gfx/mac/io_surface.h" 12 #include "ui/gfx/mac/io_surface.h"
13 #include "ui/gfx/skia_util.h" 13 #include "ui/gfx/skia_util.h"
14 14
15 namespace content { 15 namespace content {
16 16
17 SoftwareOutputDeviceMac::Buffer::Buffer() = default;
18 SoftwareOutputDeviceMac::Buffer::~Buffer() = default;
19
17 SoftwareOutputDeviceMac::SoftwareOutputDeviceMac(ui::Compositor* compositor) 20 SoftwareOutputDeviceMac::SoftwareOutputDeviceMac(ui::Compositor* compositor)
18 : compositor_(compositor), scale_factor_(1), current_index_(0) {} 21 : compositor_(compositor) {}
19 22
20 SoftwareOutputDeviceMac::~SoftwareOutputDeviceMac() { 23 SoftwareOutputDeviceMac::~SoftwareOutputDeviceMac() {
21 } 24 }
22 25
23 void SoftwareOutputDeviceMac::Resize(const gfx::Size& pixel_size, 26 void SoftwareOutputDeviceMac::Resize(const gfx::Size& pixel_size,
24 float scale_factor) { 27 float scale_factor) {
25 if (pixel_size_ == pixel_size && scale_factor_ == scale_factor) 28 if (pixel_size_ == pixel_size && scale_factor_ == scale_factor)
26 return; 29 return;
27 30
28 pixel_size_ = pixel_size; 31 pixel_size_ = pixel_size;
29 scale_factor_ = scale_factor; 32 scale_factor_ = scale_factor;
30 33
31 DiscardBackbuffer(); 34 DiscardBackbuffer();
32 } 35 }
33 36
34 void SoftwareOutputDeviceMac::CopyPreviousBufferDamage( 37 void SoftwareOutputDeviceMac::UpdateAndCopyBufferDamage(
38 Buffer* previous_paint_buffer,
35 const SkRegion& new_damage_region) { 39 const SkRegion& new_damage_region) {
36 TRACE_EVENT0("browser", "CopyPreviousBufferDamage"); 40 TRACE_EVENT0("browser", "CopyPreviousBufferDamage");
37 41
38 // The region to copy is the region drawn last frame, minus the region that 42 // Expand the |accumulated_damage| of all buffers to include this frame's
39 // is drawn this frame. 43 // damage.
40 SkRegion copy_region = previous_buffer_damage_region_; 44 for (auto& buffer : buffer_queue_)
45 buffer->accumulated_damage.op(new_damage_region, SkRegion::kUnion_Op);
46
47 // Compute the region to copy from |previous_paint_buffer| to
48 // |current_paint_buffer_| by subtracting |new_damage_region| (which we will
49 // be painting) from |current_paint_buffer_|'s |accumulated_damage|.
50 SkRegion copy_region;
51 current_paint_buffer_->accumulated_damage.swap(copy_region);
41 bool copy_region_nonempty = 52 bool copy_region_nonempty =
42 copy_region.op(new_damage_region, SkRegion::kDifference_Op); 53 copy_region.op(new_damage_region, SkRegion::kDifference_Op);
43 previous_buffer_damage_region_ = new_damage_region; 54 last_copy_region_for_testing_ = copy_region;
44
45 if (!copy_region_nonempty) 55 if (!copy_region_nonempty)
46 return; 56 return;
47 57
48 IOSurfaceRef previous_io_surface = io_surfaces_[!current_index_].get(); 58 // If we have anything to copy, we had better have a buffer to copy it from.
59 if (!previous_paint_buffer) {
60 DLOG(ERROR) << "No previous paint buffer to copy accumulated damage from.";
61 last_copy_region_for_testing_.setEmpty();
62 return;
63 }
64
65 // It is possible for |previous_paint_buffer| to equal
66 // |current_paint_buffer_|, but if it does, we should not need to do a copy.
67 CHECK_NE(previous_paint_buffer, current_paint_buffer_);
68
69 IOSurfaceRef previous_io_surface = previous_paint_buffer->io_surface.get();
49 70
50 { 71 {
51 TRACE_EVENT0("browser", "IOSurfaceLock for software copy"); 72 TRACE_EVENT0("browser", "IOSurfaceLock for software copy");
52 IOReturn io_result = IOSurfaceLock( 73 IOReturn io_result = IOSurfaceLock(
53 previous_io_surface, kIOSurfaceLockReadOnly | kIOSurfaceLockAvoidSync, 74 previous_io_surface, kIOSurfaceLockReadOnly | kIOSurfaceLockAvoidSync,
54 nullptr); 75 nullptr);
55 if (io_result) { 76 if (io_result) {
56 DLOG(ERROR) << "Failed to lock previous IOSurface " << io_result; 77 DLOG(ERROR) << "Failed to lock previous IOSurface " << io_result;
57 return; 78 return;
58 } 79 }
59 } 80 }
60 81
61 uint8_t* pixels = 82 uint8_t* pixels =
62 static_cast<uint8_t*>(IOSurfaceGetBaseAddress(previous_io_surface)); 83 static_cast<uint8_t*>(IOSurfaceGetBaseAddress(previous_io_surface));
63 size_t stride = IOSurfaceGetBytesPerRow(previous_io_surface); 84 size_t stride = IOSurfaceGetBytesPerRow(previous_io_surface);
64 size_t bytes_per_element = 4; 85 size_t bytes_per_element = 4;
65 for (SkRegion::Iterator it(copy_region); !it.done(); it.next()) { 86 for (SkRegion::Iterator it(copy_region); !it.done(); it.next()) {
66 const SkIRect& rect = it.rect(); 87 const SkIRect& rect = it.rect();
67 canvas_->writePixels( 88 current_paint_canvas_->writePixels(
68 SkImageInfo::MakeN32Premul(rect.width(), rect.height()), 89 SkImageInfo::MakeN32Premul(rect.width(), rect.height()),
69 pixels + bytes_per_element * rect.x() + stride * rect.y(), stride, 90 pixels + bytes_per_element * rect.x() + stride * rect.y(), stride,
70 rect.x(), rect.y()); 91 rect.x(), rect.y());
71 } 92 }
72 93
73 { 94 {
74 TRACE_EVENT0("browser", "IOSurfaceUnlock"); 95 TRACE_EVENT0("browser", "IOSurfaceUnlock");
75 IOReturn io_result = IOSurfaceUnlock( 96 IOReturn io_result = IOSurfaceUnlock(
76 previous_io_surface, kIOSurfaceLockReadOnly | kIOSurfaceLockAvoidSync, 97 previous_io_surface, kIOSurfaceLockReadOnly | kIOSurfaceLockAvoidSync,
77 nullptr); 98 nullptr);
78 if (io_result) 99 if (io_result)
79 DLOG(ERROR) << "Failed to unlock previous IOSurface " << io_result; 100 DLOG(ERROR) << "Failed to unlock previous IOSurface " << io_result;
80 } 101 }
81 } 102 }
82 103
83 bool SoftwareOutputDeviceMac::EnsureBuffersExist() {
84 for (int i = 0; i < 2; ++i) {
85 if (!io_surfaces_[i]) {
86 TRACE_EVENT0("browser", "IOSurfaceCreate");
87 io_surfaces_[i].reset(
88 gfx::CreateIOSurface(pixel_size_, gfx::BufferFormat::BGRA_8888));
89 }
90 if (!io_surfaces_[i]) {
91 DLOG(ERROR) << "Failed to allocate IOSurface";
92 return false;
93 }
94 }
95 return true;
96 }
97
98 SkCanvas* SoftwareOutputDeviceMac::BeginPaint( 104 SkCanvas* SoftwareOutputDeviceMac::BeginPaint(
99 const gfx::Rect& new_damage_rect) { 105 const gfx::Rect& new_damage_rect) {
100 if (!EnsureBuffersExist()) 106 // Record the previous paint buffer.
101 return nullptr; 107 Buffer* previous_paint_buffer =
108 buffer_queue_.empty() ? nullptr : buffer_queue_.back().get();
102 109
110 // Find any buffer in the queue that is not in use by the window server, and
111 // re-use it as the buffer for this paint. Note that this can be any buffer in
112 // any position in the list.
113 for (auto iter = buffer_queue_.begin(); iter != buffer_queue_.end(); ++iter) {
114 Buffer* iter_buffer = iter->get();
115 if (IOSurfaceIsInUse(iter_buffer->io_surface))
116 continue;
117 current_paint_buffer_ = iter_buffer;
118 buffer_queue_.splice(buffer_queue_.end(), buffer_queue_, iter);
119 break;
120 }
121
122 // If we failed to find a suitable buffer, allocate a new one, and initialize
123 // it with complete damage.
124 if (!current_paint_buffer_) {
125 std::unique_ptr<Buffer> new_buffer(new Buffer);
126 new_buffer->io_surface.reset(
127 gfx::CreateIOSurface(pixel_size_, gfx::BufferFormat::BGRA_8888));
128 if (!new_buffer->io_surface)
129 return nullptr;
130 // Set the initial damage to be the full buffer.
131 new_buffer->accumulated_damage.setRect(
132 gfx::RectToSkIRect(gfx::Rect(pixel_size_)));
133 current_paint_buffer_ = new_buffer.get();
134 buffer_queue_.push_back(std::move(new_buffer));
135 }
136 DCHECK(current_paint_buffer_);
137
138 // Animating in steady-state should require no more than 4 buffers. If we have
139 // more than that, then purge the older buffers (the window server will
140 // continue to hold on to the IOSurfaces as long as is needed, so the
141 // consequence will be extra allocate-free cycles).
142 size_t kMaxBuffers = 4;
143 while (buffer_queue_.size() > kMaxBuffers)
144 buffer_queue_.pop_front();
145
146 // Lock the |current_paint_buffer_|'s IOSurface and wrap it in
147 // |current_paint_canvas_|.
103 { 148 {
104 TRACE_EVENT0("browser", "IOSurfaceLock for software paint"); 149 TRACE_EVENT0("browser", "IOSurfaceLock for software paint");
105 IOReturn io_result = IOSurfaceLock(io_surfaces_[current_index_], 150 IOReturn io_result = IOSurfaceLock(current_paint_buffer_->io_surface,
106 kIOSurfaceLockAvoidSync, nullptr); 151 kIOSurfaceLockAvoidSync, nullptr);
107 if (io_result) { 152 if (io_result) {
108 DLOG(ERROR) << "Failed to lock IOSurface " << io_result; 153 DLOG(ERROR) << "Failed to lock IOSurface " << io_result;
154 current_paint_buffer_ = nullptr;
109 return nullptr; 155 return nullptr;
110 } 156 }
111 } 157 }
158 {
159 SkPMColor* pixels = static_cast<SkPMColor*>(
160 IOSurfaceGetBaseAddress(current_paint_buffer_->io_surface));
161 size_t stride = IOSurfaceGetBytesPerRow(current_paint_buffer_->io_surface);
162 current_paint_canvas_ = SkCanvas::MakeRasterDirectN32(
163 pixel_size_.width(), pixel_size_.height(), pixels, stride);
164 }
112 165
113 SkPMColor* pixels = static_cast<SkPMColor*>( 166 UpdateAndCopyBufferDamage(previous_paint_buffer,
114 IOSurfaceGetBaseAddress(io_surfaces_[current_index_])); 167 SkRegion(gfx::RectToSkIRect(new_damage_rect)));
115 size_t stride = IOSurfaceGetBytesPerRow(io_surfaces_[current_index_]);
116 168
117 canvas_ = SkCanvas::MakeRasterDirectN32(pixel_size_.width(), 169 return current_paint_canvas_.get();
118 pixel_size_.height(), pixels, stride);
119
120 CopyPreviousBufferDamage(SkRegion(gfx::RectToSkIRect(new_damage_rect)));
121
122 return canvas_.get();
123 } 170 }
124 171
125 void SoftwareOutputDeviceMac::EndPaint() { 172 void SoftwareOutputDeviceMac::EndPaint() {
126 SoftwareOutputDevice::EndPaint(); 173 SoftwareOutputDevice::EndPaint();
127 { 174 {
128 TRACE_EVENT0("browser", "IOSurfaceUnlock"); 175 TRACE_EVENT0("browser", "IOSurfaceUnlock");
129 IOReturn io_result = IOSurfaceUnlock(io_surfaces_[current_index_], 176 IOReturn io_result = IOSurfaceUnlock(current_paint_buffer_->io_surface,
130 kIOSurfaceLockAvoidSync, nullptr); 177 kIOSurfaceLockAvoidSync, nullptr);
131 if (io_result) 178 if (io_result)
132 DLOG(ERROR) << "Failed to unlock IOSurface " << io_result; 179 DLOG(ERROR) << "Failed to unlock IOSurface " << io_result;
133 } 180 }
134 canvas_.reset(); 181 current_paint_canvas_.reset();
135 182
136 ui::AcceleratedWidgetMac* widget = 183 if (compositor_) {
137 ui::AcceleratedWidgetMac::Get(compositor_->widget()); 184 ui::AcceleratedWidgetMac* widget =
138 if (widget) { 185 ui::AcceleratedWidgetMac::Get(compositor_->widget());
139 widget->GotIOSurfaceFrame(io_surfaces_[current_index_], pixel_size_, 186 if (widget) {
140 scale_factor_); 187 widget->GotIOSurfaceFrame(current_paint_buffer_->io_surface, pixel_size_,
141 base::TimeTicks vsync_timebase; 188 scale_factor_);
142 base::TimeDelta vsync_interval; 189 base::TimeTicks vsync_timebase;
143 widget->GetVSyncParameters(&vsync_timebase, &vsync_interval); 190 base::TimeDelta vsync_interval;
144 if (!update_vsync_callback_.is_null()) 191 widget->GetVSyncParameters(&vsync_timebase, &vsync_interval);
145 update_vsync_callback_.Run(vsync_timebase, vsync_interval); 192 if (!update_vsync_callback_.is_null())
193 update_vsync_callback_.Run(vsync_timebase, vsync_interval);
194 }
146 } 195 }
147 196
148 current_index_ = !current_index_; 197 current_paint_buffer_ = nullptr;
149 } 198 }
150 199
151 void SoftwareOutputDeviceMac::DiscardBackbuffer() { 200 void SoftwareOutputDeviceMac::DiscardBackbuffer() {
152 for (int i = 0; i < 2; ++i) 201 buffer_queue_.clear();
153 io_surfaces_[i].reset();
154 } 202 }
155 203
156 void SoftwareOutputDeviceMac::EnsureBackbuffer() {} 204 void SoftwareOutputDeviceMac::EnsureBackbuffer() {}
157 205
158 gfx::VSyncProvider* SoftwareOutputDeviceMac::GetVSyncProvider() { 206 gfx::VSyncProvider* SoftwareOutputDeviceMac::GetVSyncProvider() {
159 return this; 207 return this;
160 } 208 }
161 209
162 void SoftwareOutputDeviceMac::GetVSyncParameters( 210 void SoftwareOutputDeviceMac::GetVSyncParameters(
163 const gfx::VSyncProvider::UpdateVSyncCallback& callback) { 211 const gfx::VSyncProvider::UpdateVSyncCallback& callback) {
164 update_vsync_callback_ = callback; 212 update_vsync_callback_ = callback;
165 } 213 }
166 214
167 } // namespace content 215 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698