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

Side by Side Diff: content/browser/renderer_host/compositing_iosurface_mac.mm

Issue 490393002: Simplify IOSurface CoreAnimation code: Part 2 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Lower similarity Created 6 years, 4 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
(Empty)
1 // Copyright (c) 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 "content/browser/renderer_host/compositing_iosurface_mac.h"
6
7 #include <OpenGL/CGLIOSurface.h>
8 #include <OpenGL/CGLRenderers.h>
9 #include <OpenGL/OpenGL.h>
10
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/debug/trace_event.h"
14 #include "base/logging.h"
15 #include "base/mac/mac_util.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/threading/platform_thread.h"
18 #include "content/browser/gpu/gpu_data_manager_impl.h"
19 #include "content/browser/renderer_host/compositing_iosurface_context_mac.h"
20 #include "content/browser/renderer_host/render_widget_host_impl.h"
21 #include "content/browser/renderer_host/render_widget_host_view_mac.h"
22 #include "content/common/content_constants_internal.h"
23 #include "gpu/config/gpu_driver_bug_workaround_type.h"
24 #include "media/base/video_util.h"
25 #include "third_party/skia/include/core/SkBitmap.h"
26 #include "ui/gfx/rect.h"
27 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h"
28 #include "ui/gfx/size_conversions.h"
29 #include "ui/gl/gl_context.h"
30
31 #ifdef NDEBUG
32 #define CHECK_GL_ERROR()
33 #define CHECK_AND_SAVE_GL_ERROR()
34 #else
35 #define CHECK_GL_ERROR() do { \
36 GLenum gl_error = glGetError(); \
37 LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GL Error: " << gl_error; \
38 } while (0)
39 #define CHECK_AND_SAVE_GL_ERROR() do { \
40 GLenum gl_error = GetAndSaveGLError(); \
41 LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GL Error: " << gl_error; \
42 } while (0)
43 #endif
44
45 namespace content {
46
47 // static
48 scoped_refptr<CompositingIOSurfaceMac> CompositingIOSurfaceMac::Create() {
49 scoped_refptr<CompositingIOSurfaceContext> offscreen_context =
50 CompositingIOSurfaceContext::Get(
51 CompositingIOSurfaceContext::kOffscreenContextWindowNumber);
52 if (!offscreen_context) {
53 LOG(ERROR) << "Failed to create context for offscreen operations";
54 return NULL;
55 }
56
57 return new CompositingIOSurfaceMac(offscreen_context);
58 }
59
60 CompositingIOSurfaceMac::CompositingIOSurfaceMac(
61 const scoped_refptr<CompositingIOSurfaceContext>& offscreen_context)
62 : offscreen_context_(offscreen_context),
63 io_surface_handle_(0),
64 scale_factor_(1.f),
65 texture_(0),
66 gl_error_(GL_NO_ERROR),
67 eviction_queue_iterator_(eviction_queue_.Get().end()),
68 eviction_has_been_drawn_since_updated_(false) {
69 CHECK(offscreen_context_);
70 }
71
72 CompositingIOSurfaceMac::~CompositingIOSurfaceMac() {
73 {
74 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
75 offscreen_context_->cgl_context());
76 UnrefIOSurfaceWithContextCurrent();
77 }
78 offscreen_context_ = NULL;
79 DCHECK(eviction_queue_iterator_ == eviction_queue_.Get().end());
80 }
81
82 bool CompositingIOSurfaceMac::SetIOSurfaceWithContextCurrent(
83 scoped_refptr<CompositingIOSurfaceContext> current_context,
84 IOSurfaceID io_surface_handle,
85 const gfx::Size& size,
86 float scale_factor) {
87 bool result = MapIOSurfaceToTextureWithContextCurrent(
88 current_context, size, scale_factor, io_surface_handle);
89 EvictionMarkUpdated();
90 return result;
91 }
92
93 int CompositingIOSurfaceMac::GetRendererID() {
94 GLint current_renderer_id = -1;
95 if (CGLGetParameter(offscreen_context_->cgl_context(),
96 kCGLCPCurrentRendererID,
97 &current_renderer_id) == kCGLNoError)
98 return current_renderer_id & kCGLRendererIDMatchingMask;
99 return -1;
100 }
101
102 bool CompositingIOSurfaceMac::DrawIOSurface(
103 scoped_refptr<CompositingIOSurfaceContext> drawing_context,
104 const gfx::Rect& window_rect,
105 float window_scale_factor) {
106 DCHECK_EQ(CGLGetCurrentContext(), drawing_context->cgl_context());
107
108 bool has_io_surface = HasIOSurface();
109 TRACE_EVENT1("browser", "CompositingIOSurfaceMac::DrawIOSurface",
110 "has_io_surface", has_io_surface);
111
112 gfx::Rect pixel_window_rect =
113 ToNearestRect(gfx::ScaleRect(window_rect, window_scale_factor));
114 glViewport(
115 pixel_window_rect.x(), pixel_window_rect.y(),
116 pixel_window_rect.width(), pixel_window_rect.height());
117
118 SurfaceQuad quad;
119 quad.set_size(dip_io_surface_size_, pixel_io_surface_size_);
120
121 glMatrixMode(GL_PROJECTION);
122 glLoadIdentity();
123
124 // Note that the projection keeps things in view units, so the use of
125 // window_rect / dip_io_surface_size_ (as opposed to the pixel_ variants)
126 // below is correct.
127 glOrtho(0, window_rect.width(), window_rect.height(), 0, -1, 1);
128 glMatrixMode(GL_MODELVIEW);
129 glLoadIdentity();
130
131 glDisable(GL_DEPTH_TEST);
132 glDisable(GL_BLEND);
133
134 glColor4f(1, 1, 1, 1);
135 if (has_io_surface) {
136 glEnable(GL_TEXTURE_RECTANGLE_ARB);
137 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_);
138 DrawQuad(quad);
139 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
140 glDisable(GL_TEXTURE_RECTANGLE_ARB);
141 CHECK_AND_SAVE_GL_ERROR();
142
143 // Fill the resize gutters with white.
144 if (window_rect.width() > dip_io_surface_size_.width() ||
145 window_rect.height() > dip_io_surface_size_.height()) {
146 SurfaceQuad filler_quad;
147 if (window_rect.width() > dip_io_surface_size_.width()) {
148 // Draw right-side gutter down to the bottom of the window.
149 filler_quad.set_rect(dip_io_surface_size_.width(), 0.0f,
150 window_rect.width(), window_rect.height());
151 DrawQuad(filler_quad);
152 }
153 if (window_rect.height() > dip_io_surface_size_.height()) {
154 // Draw bottom gutter to the width of the IOSurface.
155 filler_quad.set_rect(
156 0.0f, dip_io_surface_size_.height(),
157 dip_io_surface_size_.width(), window_rect.height());
158 DrawQuad(filler_quad);
159 }
160 }
161
162 // Workaround for issue 158469. Issue a dummy draw call with texture_ not
163 // bound to a texture, in order to shake all references to the IOSurface out
164 // of the driver.
165 glBegin(GL_TRIANGLES);
166 glEnd();
167 CHECK_AND_SAVE_GL_ERROR();
168 } else {
169 // Should match the clear color of RenderWidgetHostViewMac.
170 glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
171 glClear(GL_COLOR_BUFFER_BIT);
172 }
173
174 bool workaround_needed =
175 GpuDataManagerImpl::GetInstance()->IsDriverBugWorkaroundActive(
176 gpu::FORCE_GL_FINISH_AFTER_COMPOSITING);
177 if (workaround_needed) {
178 TRACE_EVENT0("gpu", "glFinish");
179 glFinish();
180 }
181
182 // Check if any of the drawing calls result in an error.
183 GetAndSaveGLError();
184 bool result = true;
185 if (gl_error_ != GL_NO_ERROR) {
186 LOG(ERROR) << "GL error in DrawIOSurface: " << gl_error_;
187 result = false;
188 // If there was an error, clear the screen to a light grey to avoid
189 // rendering artifacts. If we're in a really bad way, this too may
190 // generate an error. Clear the GL error afterwards just in case.
191 glClearColor(0.8, 0.8, 0.8, 1.0);
192 glClear(GL_COLOR_BUFFER_BIT);
193 glGetError();
194 }
195
196 eviction_has_been_drawn_since_updated_ = true;
197 return result;
198 }
199
200 bool CompositingIOSurfaceMac::MapIOSurfaceToTextureWithContextCurrent(
201 const scoped_refptr<CompositingIOSurfaceContext>& current_context,
202 const gfx::Size pixel_size,
203 float scale_factor,
204 IOSurfaceID io_surface_handle) {
205 TRACE_EVENT0("browser", "CompositingIOSurfaceMac::MapIOSurfaceToTexture");
206
207 if (!io_surface_ || io_surface_handle != io_surface_handle_)
208 UnrefIOSurfaceWithContextCurrent();
209
210 pixel_io_surface_size_ = pixel_size;
211 scale_factor_ = scale_factor;
212 dip_io_surface_size_ = gfx::ToFlooredSize(
213 gfx::ScaleSize(pixel_io_surface_size_, 1.0 / scale_factor_));
214
215 // Early-out if the IOSurface has not changed. Note that because IOSurface
216 // sizes are rounded, the same IOSurface may have two different sizes
217 // associated with it.
218 if (io_surface_ && io_surface_handle == io_surface_handle_)
219 return true;
220
221 io_surface_.reset(IOSurfaceLookup(io_surface_handle));
222 // Can fail if IOSurface with that ID was already released by the gpu
223 // process.
224 if (!io_surface_) {
225 UnrefIOSurfaceWithContextCurrent();
226 return false;
227 }
228
229 io_surface_handle_ = io_surface_handle;
230
231 // Actual IOSurface size is rounded up to reduce reallocations during window
232 // resize. Get the actual size to properly map the texture.
233 gfx::Size rounded_size(IOSurfaceGetWidth(io_surface_),
234 IOSurfaceGetHeight(io_surface_));
235
236 glGenTextures(1, &texture_);
237 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_);
238 glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
239 glTexParameterf(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
240 CHECK_AND_SAVE_GL_ERROR();
241 GLuint plane = 0;
242 CGLError cgl_error = CGLTexImageIOSurface2D(
243 current_context->cgl_context(),
244 GL_TEXTURE_RECTANGLE_ARB,
245 GL_RGBA,
246 rounded_size.width(),
247 rounded_size.height(),
248 GL_BGRA,
249 GL_UNSIGNED_INT_8_8_8_8_REV,
250 io_surface_.get(),
251 plane);
252 glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
253 if (cgl_error != kCGLNoError) {
254 LOG(ERROR) << "CGLTexImageIOSurface2D: " << cgl_error;
255 UnrefIOSurfaceWithContextCurrent();
256 return false;
257 }
258 GetAndSaveGLError();
259 if (gl_error_ != GL_NO_ERROR) {
260 LOG(ERROR) << "GL error in MapIOSurfaceToTexture: " << gl_error_;
261 UnrefIOSurfaceWithContextCurrent();
262 return false;
263 }
264 return true;
265 }
266
267 void CompositingIOSurfaceMac::UnrefIOSurface() {
268 gfx::ScopedCGLSetCurrentContext scoped_set_current_context(
269 offscreen_context_->cgl_context());
270 UnrefIOSurfaceWithContextCurrent();
271 }
272
273 void CompositingIOSurfaceMac::DrawQuad(const SurfaceQuad& quad) {
274 TRACE_EVENT0("gpu", "CompositingIOSurfaceMac::DrawQuad");
275
276 glEnableClientState(GL_VERTEX_ARRAY); CHECK_AND_SAVE_GL_ERROR();
277 glEnableClientState(GL_TEXTURE_COORD_ARRAY); CHECK_AND_SAVE_GL_ERROR();
278
279 glVertexPointer(2, GL_FLOAT, sizeof(SurfaceVertex), &quad.verts_[0].x_);
280 glTexCoordPointer(2, GL_FLOAT, sizeof(SurfaceVertex), &quad.verts_[0].tx_);
281 glDrawArrays(GL_QUADS, 0, 4); CHECK_AND_SAVE_GL_ERROR();
282
283 glDisableClientState(GL_VERTEX_ARRAY);
284 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
285 }
286
287 void CompositingIOSurfaceMac::UnrefIOSurfaceWithContextCurrent() {
288 if (texture_) {
289 glDeleteTextures(1, &texture_);
290 texture_ = 0;
291 }
292 pixel_io_surface_size_ = gfx::Size();
293 scale_factor_ = 1;
294 dip_io_surface_size_ = gfx::Size();
295 io_surface_.reset();
296
297 // Forget the ID, because even if it is still around when we want to use it
298 // again, OSX may have reused the same ID for a new tab and we don't want to
299 // blit random tab contents.
300 io_surface_handle_ = 0;
301
302 EvictionMarkEvicted();
303 }
304
305 bool CompositingIOSurfaceMac::HasBeenPoisoned() const {
306 return offscreen_context_->HasBeenPoisoned();
307 }
308
309 GLenum CompositingIOSurfaceMac::GetAndSaveGLError() {
310 GLenum gl_error = glGetError();
311 if (gl_error_ == GL_NO_ERROR)
312 gl_error_ = gl_error;
313 return gl_error;
314 }
315
316 void CompositingIOSurfaceMac::EvictionMarkUpdated() {
317 EvictionMarkEvicted();
318 eviction_queue_.Get().push_back(this);
319 eviction_queue_iterator_ = --eviction_queue_.Get().end();
320 eviction_has_been_drawn_since_updated_ = false;
321 EvictionScheduleDoEvict();
322 }
323
324 void CompositingIOSurfaceMac::EvictionMarkEvicted() {
325 if (eviction_queue_iterator_ == eviction_queue_.Get().end())
326 return;
327 eviction_queue_.Get().erase(eviction_queue_iterator_);
328 eviction_queue_iterator_ = eviction_queue_.Get().end();
329 eviction_has_been_drawn_since_updated_ = false;
330 }
331
332 // static
333 void CompositingIOSurfaceMac::EvictionScheduleDoEvict() {
334 if (eviction_scheduled_)
335 return;
336 if (eviction_queue_.Get().size() <= kMaximumUnevictedSurfaces)
337 return;
338
339 eviction_scheduled_ = true;
340 base::MessageLoop::current()->PostTask(
341 FROM_HERE,
342 base::Bind(&CompositingIOSurfaceMac::EvictionDoEvict));
343 }
344
345 // static
346 void CompositingIOSurfaceMac::EvictionDoEvict() {
347 eviction_scheduled_ = false;
348 // Walk the list of allocated surfaces from least recently used to most
349 // recently used.
350 for (EvictionQueue::iterator it = eviction_queue_.Get().begin();
351 it != eviction_queue_.Get().end();) {
352 CompositingIOSurfaceMac* surface = *it;
353 ++it;
354
355 // If the number of IOSurfaces allocated is less than the threshold,
356 // stop walking the list of surfaces.
357 if (eviction_queue_.Get().size() <= kMaximumUnevictedSurfaces)
358 break;
359
360 // Don't evict anything that has not yet been drawn.
361 if (!surface->eviction_has_been_drawn_since_updated_)
362 continue;
363
364 // Evict the surface.
365 surface->UnrefIOSurface();
366 }
367 }
368
369 // static
370 base::LazyInstance<CompositingIOSurfaceMac::EvictionQueue>
371 CompositingIOSurfaceMac::eviction_queue_;
372
373 // static
374 bool CompositingIOSurfaceMac::eviction_scheduled_ = false;
375
376 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698