Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 |
| OLD | NEW |