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

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

Issue 2866683003: Use more than double buffering in SoftwareOutputDeviceMac (Closed)
Patch Set: 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;
44
45 if (!copy_region_nonempty) 54 if (!copy_region_nonempty)
46 return; 55 return;
47 56
48 IOSurfaceRef previous_io_surface = io_surfaces_[!current_index_].get(); 57 // If we have anything to copy, we had better have a buffer to copy it from.
58 if (!previous_paint_buffer) {
59 DLOG(ERROR) << "No previous paint buffer to copy accumulated damage from.";
60 return;
61 }
jbauman 2017/05/05 19:19:25 Maybe add a DCHECK_NE(previous_paint_buffer, curre
ccameron 2017/05/05 22:06:08 Done.
62
63 IOSurfaceRef previous_io_surface = previous_paint_buffer->io_surface.get();
49 64
50 { 65 {
51 TRACE_EVENT0("browser", "IOSurfaceLock for software copy"); 66 TRACE_EVENT0("browser", "IOSurfaceLock for software copy");
52 IOReturn io_result = IOSurfaceLock( 67 IOReturn io_result = IOSurfaceLock(
53 previous_io_surface, kIOSurfaceLockReadOnly | kIOSurfaceLockAvoidSync, 68 previous_io_surface, kIOSurfaceLockReadOnly | kIOSurfaceLockAvoidSync,
54 nullptr); 69 nullptr);
55 if (io_result) { 70 if (io_result) {
56 DLOG(ERROR) << "Failed to lock previous IOSurface " << io_result; 71 DLOG(ERROR) << "Failed to lock previous IOSurface " << io_result;
57 return; 72 return;
58 } 73 }
59 } 74 }
60 75
61 uint8_t* pixels = 76 uint8_t* pixels =
62 static_cast<uint8_t*>(IOSurfaceGetBaseAddress(previous_io_surface)); 77 static_cast<uint8_t*>(IOSurfaceGetBaseAddress(previous_io_surface));
63 size_t stride = IOSurfaceGetBytesPerRow(previous_io_surface); 78 size_t stride = IOSurfaceGetBytesPerRow(previous_io_surface);
64 size_t bytes_per_element = 4; 79 size_t bytes_per_element = 4;
65 for (SkRegion::Iterator it(copy_region); !it.done(); it.next()) { 80 for (SkRegion::Iterator it(copy_region); !it.done(); it.next()) {
66 const SkIRect& rect = it.rect(); 81 const SkIRect& rect = it.rect();
67 canvas_->writePixels( 82 current_paint_canvas_->writePixels(
68 SkImageInfo::MakeN32Premul(rect.width(), rect.height()), 83 SkImageInfo::MakeN32Premul(rect.width(), rect.height()),
69 pixels + bytes_per_element * rect.x() + stride * rect.y(), stride, 84 pixels + bytes_per_element * rect.x() + stride * rect.y(), stride,
70 rect.x(), rect.y()); 85 rect.x(), rect.y());
71 } 86 }
72 87
73 { 88 {
74 TRACE_EVENT0("browser", "IOSurfaceUnlock"); 89 TRACE_EVENT0("browser", "IOSurfaceUnlock");
75 IOReturn io_result = IOSurfaceUnlock( 90 IOReturn io_result = IOSurfaceUnlock(
76 previous_io_surface, kIOSurfaceLockReadOnly | kIOSurfaceLockAvoidSync, 91 previous_io_surface, kIOSurfaceLockReadOnly | kIOSurfaceLockAvoidSync,
77 nullptr); 92 nullptr);
78 if (io_result) 93 if (io_result)
79 DLOG(ERROR) << "Failed to unlock previous IOSurface " << io_result; 94 DLOG(ERROR) << "Failed to unlock previous IOSurface " << io_result;
80 } 95 }
81 } 96 }
82 97
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( 98 SkCanvas* SoftwareOutputDeviceMac::BeginPaint(
99 const gfx::Rect& new_damage_rect) { 99 const gfx::Rect& new_damage_rect) {
100 if (!EnsureBuffersExist()) 100 // Record the previous paint buffer.
101 return nullptr; 101 Buffer* previous_paint_buffer =
102 buffer_queue_.empty() ? nullptr : buffer_queue_.back().get();
102 103
104 // Find any buffer in the queue that is not in use by the window server, and
105 // re-use it as the buffer for this paint. Note that this can be any buffer in
106 // any position in the list.
107 for (auto iter = buffer_queue_.begin(); iter != buffer_queue_.end(); ++iter) {
108 Buffer* iter_buffer = iter->get();
109 if (IOSurfaceIsInUse(iter_buffer->io_surface))
110 continue;
111 current_paint_buffer_ = iter_buffer;
112 buffer_queue_.splice(buffer_queue_.end(), buffer_queue_, iter);
113 break;
114 }
115
116 // If we failed to find a suitable buffer, allocate a new one, and initialize
117 // it with complete damage.
118 if (!current_paint_buffer_) {
119 std::unique_ptr<Buffer> new_buffer(new Buffer);
120 new_buffer->io_surface.reset(
121 gfx::CreateIOSurface(pixel_size_, gfx::BufferFormat::BGRA_8888));
122 if (!new_buffer->io_surface)
123 return nullptr;
124 // Set the initial damage to be the full buffer.
125 new_buffer->accumulated_damage.setRect(
126 gfx::RectToSkIRect(gfx::Rect(pixel_size_)));
127 current_paint_buffer_ = new_buffer.get();
128 buffer_queue_.push_back(std::move(new_buffer));
129 }
130 DCHECK(current_paint_buffer_);
131
132 // Animating in steady-state should require no more than 4 buffers. If we have
133 // more than that, then purge the older buffers (the window server will
134 // continue to hold on to the IOSurfaces as long as is needed, so the
135 // consequence will be extra allocate-free cycles).
136 size_t kMaxBuffers = 4;
137 while (buffer_queue_.size() > kMaxBuffers)
138 buffer_queue_.pop_front();
139
140 // Lock the |current_paint_buffer_|'s IOSurface and wrap it in
141 // |current_paint_canvas_|.
103 { 142 {
104 TRACE_EVENT0("browser", "IOSurfaceLock for software paint"); 143 TRACE_EVENT0("browser", "IOSurfaceLock for software paint");
105 IOReturn io_result = IOSurfaceLock(io_surfaces_[current_index_], 144 IOReturn io_result = IOSurfaceLock(current_paint_buffer_->io_surface,
106 kIOSurfaceLockAvoidSync, nullptr); 145 kIOSurfaceLockAvoidSync, nullptr);
107 if (io_result) { 146 if (io_result) {
108 DLOG(ERROR) << "Failed to lock IOSurface " << io_result; 147 DLOG(ERROR) << "Failed to lock IOSurface " << io_result;
148 current_paint_buffer_ = nullptr;
109 return nullptr; 149 return nullptr;
110 } 150 }
111 } 151 }
152 {
153 SkPMColor* pixels = static_cast<SkPMColor*>(
154 IOSurfaceGetBaseAddress(current_paint_buffer_->io_surface));
155 size_t stride = IOSurfaceGetBytesPerRow(current_paint_buffer_->io_surface);
156 current_paint_canvas_ = SkCanvas::MakeRasterDirectN32(
157 pixel_size_.width(), pixel_size_.height(), pixels, stride);
158 }
112 159
113 SkPMColor* pixels = static_cast<SkPMColor*>( 160 UpdateAndCopyBufferDamage(previous_paint_buffer,
114 IOSurfaceGetBaseAddress(io_surfaces_[current_index_])); 161 SkRegion(gfx::RectToSkIRect(new_damage_rect)));
115 size_t stride = IOSurfaceGetBytesPerRow(io_surfaces_[current_index_]);
116 162
117 canvas_ = SkCanvas::MakeRasterDirectN32(pixel_size_.width(), 163 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 } 164 }
124 165
125 void SoftwareOutputDeviceMac::EndPaint() { 166 void SoftwareOutputDeviceMac::EndPaint() {
126 SoftwareOutputDevice::EndPaint(); 167 SoftwareOutputDevice::EndPaint();
127 { 168 {
128 TRACE_EVENT0("browser", "IOSurfaceUnlock"); 169 TRACE_EVENT0("browser", "IOSurfaceUnlock");
129 IOReturn io_result = IOSurfaceUnlock(io_surfaces_[current_index_], 170 IOReturn io_result = IOSurfaceUnlock(current_paint_buffer_->io_surface,
130 kIOSurfaceLockAvoidSync, nullptr); 171 kIOSurfaceLockAvoidSync, nullptr);
131 if (io_result) 172 if (io_result)
132 DLOG(ERROR) << "Failed to unlock IOSurface " << io_result; 173 DLOG(ERROR) << "Failed to unlock IOSurface " << io_result;
133 } 174 }
134 canvas_.reset(); 175 current_paint_canvas_.reset();
135 176
136 ui::AcceleratedWidgetMac* widget = 177 ui::AcceleratedWidgetMac* widget =
137 ui::AcceleratedWidgetMac::Get(compositor_->widget()); 178 ui::AcceleratedWidgetMac::Get(compositor_->widget());
138 if (widget) { 179 if (widget) {
139 widget->GotIOSurfaceFrame(io_surfaces_[current_index_], pixel_size_, 180 widget->GotIOSurfaceFrame(current_paint_buffer_->io_surface, pixel_size_,
140 scale_factor_); 181 scale_factor_);
141 base::TimeTicks vsync_timebase; 182 base::TimeTicks vsync_timebase;
142 base::TimeDelta vsync_interval; 183 base::TimeDelta vsync_interval;
143 widget->GetVSyncParameters(&vsync_timebase, &vsync_interval); 184 widget->GetVSyncParameters(&vsync_timebase, &vsync_interval);
144 if (!update_vsync_callback_.is_null()) 185 if (!update_vsync_callback_.is_null())
145 update_vsync_callback_.Run(vsync_timebase, vsync_interval); 186 update_vsync_callback_.Run(vsync_timebase, vsync_interval);
146 } 187 }
147 188
148 current_index_ = !current_index_; 189 current_paint_buffer_ = nullptr;
149 } 190 }
150 191
151 void SoftwareOutputDeviceMac::DiscardBackbuffer() { 192 void SoftwareOutputDeviceMac::DiscardBackbuffer() {
152 for (int i = 0; i < 2; ++i) 193 buffer_queue_.clear();
153 io_surfaces_[i].reset();
154 } 194 }
155 195
156 void SoftwareOutputDeviceMac::EnsureBackbuffer() {} 196 void SoftwareOutputDeviceMac::EnsureBackbuffer() {}
157 197
158 gfx::VSyncProvider* SoftwareOutputDeviceMac::GetVSyncProvider() { 198 gfx::VSyncProvider* SoftwareOutputDeviceMac::GetVSyncProvider() {
159 return this; 199 return this;
160 } 200 }
161 201
162 void SoftwareOutputDeviceMac::GetVSyncParameters( 202 void SoftwareOutputDeviceMac::GetVSyncParameters(
163 const gfx::VSyncProvider::UpdateVSyncCallback& callback) { 203 const gfx::VSyncProvider::UpdateVSyncCallback& callback) {
164 update_vsync_callback_ = callback; 204 update_vsync_callback_ = callback;
165 } 205 }
166 206
167 } // namespace content 207 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698