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

Side by Side Diff: ui/gfx/surface/accelerated_surface_mac.cc

Issue 10351002: ui: Move surface/ directory out of gfx/, up to ui/. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix gpu DEPS Created 8 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 | Annotate | Revision Log
« no previous file with comments | « ui/gfx/surface/accelerated_surface_mac.h ('k') | ui/gfx/surface/accelerated_surface_win.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011 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 "ui/gfx/surface/accelerated_surface_mac.h"
6
7 #include "base/logging.h"
8 #include "base/mac/scoped_cftyperef.h"
9 #include "ui/gfx/gl/gl_bindings.h"
10 #include "ui/gfx/gl/gl_context.h"
11 #include "ui/gfx/gl/gl_implementation.h"
12 #include "ui/gfx/gl/gl_surface.h"
13 #include "ui/gfx/gl/scoped_make_current.h"
14 #include "ui/gfx/rect.h"
15 #include "ui/gfx/surface/io_surface_support_mac.h"
16
17 AcceleratedSurface::AcceleratedSurface()
18 : io_surface_id_(0),
19 allocate_fbo_(false),
20 texture_(0),
21 fbo_(0) {
22 }
23
24 AcceleratedSurface::~AcceleratedSurface() {}
25
26 bool AcceleratedSurface::Initialize(
27 gfx::GLContext* share_context,
28 bool allocate_fbo,
29 gfx::GpuPreference gpu_preference) {
30 allocate_fbo_ = allocate_fbo;
31
32 // Ensure GL is initialized before trying to create an offscreen GL context.
33 if (!gfx::GLSurface::InitializeOneOff())
34 return false;
35
36 // Drawing to IOSurfaces via OpenGL only works with Apple's GL and
37 // not with the OSMesa software renderer.
38 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL &&
39 gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL)
40 return false;
41
42 gl_surface_ = gfx::GLSurface::CreateOffscreenGLSurface(
43 false, gfx::Size(1, 1));
44 if (!gl_surface_.get()) {
45 Destroy();
46 return false;
47 }
48
49 gfx::GLShareGroup* share_group =
50 share_context ? share_context->share_group() : NULL;
51
52 gl_context_ = gfx::GLContext::CreateGLContext(
53 share_group,
54 gl_surface_.get(),
55 gpu_preference);
56 if (!gl_context_.get()) {
57 Destroy();
58 return false;
59 }
60
61 // Now we're ready to handle SetSurfaceSize calls, which will
62 // allocate and/or reallocate the IOSurface and associated offscreen
63 // OpenGL structures for rendering.
64 return true;
65 }
66
67 void AcceleratedSurface::Destroy() {
68 // The FBO and texture objects will be destroyed when the OpenGL context,
69 // and any other contexts sharing resources with it, is. We don't want to
70 // make the context current one last time here just in order to delete
71 // these objects.
72
73 // Release the old TransportDIB in the browser.
74 if (!dib_free_callback_.is_null() && transport_dib_.get()) {
75 dib_free_callback_.Run(transport_dib_->id());
76 }
77 transport_dib_.reset();
78
79 gl_context_ = NULL;
80 gl_surface_ = NULL;
81 }
82
83 // Call after making changes to the surface which require a visual update.
84 // Makes the rendering show up in other processes.
85 void AcceleratedSurface::SwapBuffers() {
86 if (io_surface_.get() != NULL) {
87 if (allocate_fbo_) {
88 // Bind and unbind the framebuffer to make changes to the
89 // IOSurface show up in the other process.
90 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
91 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_);
92 glFlush();
93 } else {
94 // Copy the current framebuffer's contents into our "live" texture.
95 // Note that the current GL context might not be ours at this point!
96 // This is deliberate, so that surrounding code using GL can produce
97 // rendering results consumed by the AcceleratedSurface.
98 // Need to save and restore OpenGL state around this call.
99 GLint current_texture = 0;
100 GLenum target_binding = GL_TEXTURE_BINDING_RECTANGLE_ARB;
101 GLenum target = GL_TEXTURE_RECTANGLE_ARB;
102 glGetIntegerv(target_binding, &current_texture);
103 glBindTexture(target, texture_);
104 glCopyTexSubImage2D(target, 0,
105 0, 0,
106 0, 0,
107 real_surface_size_.width(),
108 real_surface_size_.height());
109 glBindTexture(target, current_texture);
110 // This flush is absolutely essential -- it guarantees that the
111 // rendering results are seen by the other process.
112 glFlush();
113 }
114 } else if (transport_dib_.get() != NULL) {
115 // Pre-Mac OS X 10.6, fetch the rendered image from the current frame
116 // buffer and copy it into the TransportDIB.
117 // TODO(dspringer): There are a couple of options that can speed this up.
118 // First is to use async reads into a PBO, second is to use SPI that
119 // allows many tasks to access the same CGSSurface.
120 void* pixel_memory = transport_dib_->memory();
121 if (pixel_memory) {
122 // Note that glReadPixels does an implicit glFlush().
123 glReadPixels(0,
124 0,
125 real_surface_size_.width(),
126 real_surface_size_.height(),
127 GL_BGRA, // This pixel format should have no conversion.
128 GL_UNSIGNED_INT_8_8_8_8_REV,
129 pixel_memory);
130 }
131 }
132 }
133
134 static void AddBooleanValue(CFMutableDictionaryRef dictionary,
135 const CFStringRef key,
136 bool value) {
137 CFDictionaryAddValue(dictionary, key,
138 (value ? kCFBooleanTrue : kCFBooleanFalse));
139 }
140
141 static void AddIntegerValue(CFMutableDictionaryRef dictionary,
142 const CFStringRef key,
143 int32 value) {
144 base::mac::ScopedCFTypeRef<CFNumberRef> number(
145 CFNumberCreate(NULL, kCFNumberSInt32Type, &value));
146 CFDictionaryAddValue(dictionary, key, number.get());
147 }
148
149 // Creates a new OpenGL texture object bound to the given texture target.
150 // Caller owns the returned texture.
151 static GLuint CreateTexture(GLenum target) {
152 GLuint texture = 0;
153 glGenTextures(1, &texture);
154 glBindTexture(target, texture);
155 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
156 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
157 glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
158 glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
159 return texture;
160 }
161
162 void AcceleratedSurface::AllocateRenderBuffers(GLenum target,
163 const gfx::Size& size) {
164 if (!texture_) {
165 // Generate the texture object.
166 texture_ = CreateTexture(target);
167 // Generate and bind the framebuffer object.
168 glGenFramebuffersEXT(1, &fbo_);
169 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_);
170 }
171
172 // Make sure that subsequent set-up code affects the render texture.
173 glBindTexture(target, texture_);
174 }
175
176 bool AcceleratedSurface::SetupFrameBufferObject(GLenum target) {
177 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_);
178 GLenum fbo_status;
179 glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
180 GL_COLOR_ATTACHMENT0_EXT,
181 target,
182 texture_,
183 0);
184 fbo_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
185 return fbo_status == GL_FRAMEBUFFER_COMPLETE_EXT;
186 }
187
188 gfx::Size AcceleratedSurface::ClampToValidDimensions(const gfx::Size& size) {
189 return gfx::Size(std::max(size.width(), 1), std::max(size.height(), 1));
190 }
191
192 bool AcceleratedSurface::MakeCurrent() {
193 if (!gl_context_.get())
194 return false;
195 return gl_context_->MakeCurrent(gl_surface_.get());
196 }
197
198 void AcceleratedSurface::Clear(const gfx::Rect& rect) {
199 DCHECK(gl_context_->IsCurrent(gl_surface_.get()));
200 glClearColor(0, 0, 0, 0);
201 glViewport(0, 0, rect.width(), rect.height());
202 glMatrixMode(GL_PROJECTION);
203 glLoadIdentity();
204 glOrtho(0, rect.width(), 0, rect.height(), -1, 1);
205 glClear(GL_COLOR_BUFFER_BIT);
206 }
207
208 uint32 AcceleratedSurface::SetSurfaceSize(const gfx::Size& size) {
209 if (surface_size_ == size) {
210 // Return 0 to indicate to the caller that no new backing store
211 // allocation occurred.
212 return 0;
213 }
214
215 // Only support IO surfaces if the GL implementation is the native desktop GL.
216 // IO surfaces will not work with, for example, OSMesa software renderer
217 // GL contexts.
218 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL)
219 return 0;
220
221 IOSurfaceSupport* io_surface_support = IOSurfaceSupport::Initialize();
222 if (!io_surface_support)
223 return 0; // Caller can try using SetWindowSizeForTransportDIB().
224
225 gfx::ScopedMakeCurrent make_current(gl_context_.get(), gl_surface_.get());
226 if (!make_current.Succeeded())
227 return 0;
228
229 gfx::Size clamped_size = ClampToValidDimensions(size);
230
231 // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on
232 // Mac OS X and is required for IOSurface interoperability.
233 GLenum target = GL_TEXTURE_RECTANGLE_ARB;
234 if (allocate_fbo_) {
235 AllocateRenderBuffers(target, clamped_size);
236 } else if (!texture_) {
237 // Generate the texture object.
238 texture_ = CreateTexture(target);
239 }
240
241 // Allocate a new IOSurface, which is the GPU resource that can be
242 // shared across processes.
243 base::mac::ScopedCFTypeRef<CFMutableDictionaryRef> properties;
244 properties.reset(CFDictionaryCreateMutable(kCFAllocatorDefault,
245 0,
246 &kCFTypeDictionaryKeyCallBacks,
247 &kCFTypeDictionaryValueCallBacks));
248 AddIntegerValue(properties,
249 io_surface_support->GetKIOSurfaceWidth(),
250 clamped_size.width());
251 AddIntegerValue(properties,
252 io_surface_support->GetKIOSurfaceHeight(),
253 clamped_size.height());
254 AddIntegerValue(properties,
255 io_surface_support->GetKIOSurfaceBytesPerElement(), 4);
256 AddBooleanValue(properties,
257 io_surface_support->GetKIOSurfaceIsGlobal(), true);
258 // I believe we should be able to unreference the IOSurfaces without
259 // synchronizing with the browser process because they are
260 // ultimately reference counted by the operating system.
261 io_surface_.reset(io_surface_support->IOSurfaceCreate(properties));
262
263 // Don't think we need to identify a plane.
264 GLuint plane = 0;
265 CGLError error = io_surface_support->CGLTexImageIOSurface2D(
266 static_cast<CGLContextObj>(gl_context_->GetHandle()),
267 target,
268 GL_RGBA,
269 clamped_size.width(),
270 clamped_size.height(),
271 GL_BGRA,
272 GL_UNSIGNED_INT_8_8_8_8_REV,
273 io_surface_.get(),
274 plane);
275 if (error != kCGLNoError) {
276 DLOG(ERROR) << "CGL error " << error << " during CGLTexImageIOSurface2D";
277 }
278 if (allocate_fbo_) {
279 // Set up the frame buffer object.
280 if (!SetupFrameBufferObject(target)) {
281 DLOG(ERROR) << "Failed to set up frame buffer object";
282 }
283 }
284 surface_size_ = size;
285 real_surface_size_ = clamped_size;
286
287 // Now send back an identifier for the IOSurface. We originally
288 // intended to send back a mach port from IOSurfaceCreateMachPort
289 // but it looks like Chrome IPC would need to be modified to
290 // properly send mach ports between processes. For the time being we
291 // make our IOSurfaces global and send back their identifiers. On
292 // the browser process side the identifier is reconstituted into an
293 // IOSurface for on-screen rendering.
294 io_surface_id_ = io_surface_support->IOSurfaceGetID(io_surface_);
295 return io_surface_id_;
296 }
297
298 uint32 AcceleratedSurface::GetSurfaceId() {
299 return io_surface_id_;
300 }
301
302 TransportDIB::Handle AcceleratedSurface::SetTransportDIBSize(
303 const gfx::Size& size) {
304 if (surface_size_ == size) {
305 // Return an invalid handle to indicate to the caller that no new backing
306 // store allocation occurred.
307 return TransportDIB::DefaultHandleValue();
308 }
309 surface_size_ = size;
310 gfx::Size clamped_size = ClampToValidDimensions(size);
311 real_surface_size_ = clamped_size;
312
313 // Release the old TransportDIB in the browser.
314 if (!dib_free_callback_.is_null() && transport_dib_.get()) {
315 dib_free_callback_.Run(transport_dib_->id());
316 }
317 transport_dib_.reset();
318
319 // Ask the renderer to create a TransportDIB.
320 size_t dib_size =
321 clamped_size.width() * 4 * clamped_size.height(); // 4 bytes per pixel.
322 TransportDIB::Handle dib_handle;
323 if (!dib_alloc_callback_.is_null()) {
324 dib_alloc_callback_.Run(dib_size, &dib_handle);
325 }
326 if (!TransportDIB::is_valid_handle(dib_handle)) {
327 // If the allocator fails, it means the DIB was not created in the browser,
328 // so there is no need to run the deallocator here.
329 return TransportDIB::DefaultHandleValue();
330 }
331 transport_dib_.reset(TransportDIB::Map(dib_handle));
332 if (transport_dib_.get() == NULL) {
333 // TODO(dspringer): if the Map() fails, should the deallocator be run so
334 // that the DIB is deallocated in the browser?
335 return TransportDIB::DefaultHandleValue();
336 }
337
338 if (allocate_fbo_) {
339 DCHECK(gl_context_->IsCurrent(gl_surface_.get()));
340 // Set up the render buffers and reserve enough space on the card for the
341 // framebuffer texture.
342 GLenum target = GL_TEXTURE_RECTANGLE_ARB;
343 AllocateRenderBuffers(target, clamped_size);
344 glTexImage2D(target,
345 0, // mipmap level 0
346 GL_RGBA8, // internal pixel format
347 clamped_size.width(),
348 clamped_size.height(),
349 0, // 0 border
350 GL_BGRA, // Used for consistency
351 GL_UNSIGNED_INT_8_8_8_8_REV,
352 NULL); // No data, just reserve room on the card.
353 SetupFrameBufferObject(target);
354 }
355 return transport_dib_->handle();
356 }
357
358 void AcceleratedSurface::SetTransportDIBAllocAndFree(
359 const base::Callback<void(size_t, TransportDIB::Handle*)>& allocator,
360 const base::Callback<void(TransportDIB::Id)>& deallocator) {
361 dib_alloc_callback_ = allocator;
362 dib_free_callback_ = deallocator;
363 }
OLDNEW
« no previous file with comments | « ui/gfx/surface/accelerated_surface_mac.h ('k') | ui/gfx/surface/accelerated_surface_win.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698