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

Side by Side Diff: ui/gfx/compositor/compositor_gl.cc

Issue 7552039: Vend common GL context (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Merge with ToT Created 9 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 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 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 "ui/gfx/compositor/compositor_gl.h" 5 #include "ui/gfx/compositor/compositor_gl.h"
6 6
7 #include "base/basictypes.h" 7 #include "base/basictypes.h"
8 #include "base/compiler_specific.h" 8 #include "base/compiler_specific.h"
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h" 10 #include "base/memory/scoped_ptr.h"
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
77 }; 77 };
78 78
79 class TextureProgramSwizzleGL : public ui::TextureProgramGL { 79 class TextureProgramSwizzleGL : public ui::TextureProgramGL {
80 public: 80 public:
81 TextureProgramSwizzleGL() {} 81 TextureProgramSwizzleGL() {}
82 virtual bool Initialize(); 82 virtual bool Initialize();
83 private: 83 private:
84 DISALLOW_COPY_AND_ASSIGN(TextureProgramSwizzleGL); 84 DISALLOW_COPY_AND_ASSIGN(TextureProgramSwizzleGL);
85 }; 85 };
86 86
87 // We share between compositor contexts so that we don't have to compile
88 // shaders if they have already been compiled in another context.
89 class SharedResources {
90 public:
91 static SharedResources* GetInstance();
92
93 // Creates a context with shaders active.
94 scoped_refptr<gfx::GLContext> CreateContext(gfx::GLSurface* surface);
95 void ContextDestroyed();
96
97 ui::TextureProgramGL* program_no_swizzle() {
98 return program_no_swizzle_.get();
99 }
100
101 ui::TextureProgramGL* program_swizzle() { return program_swizzle_.get(); }
102
103 private:
104 friend struct DefaultSingletonTraits<SharedResources>;
105
106 SharedResources();
107 virtual ~SharedResources();
108
109 scoped_refptr<gfx::GLShareGroup> share_group_;
110 scoped_ptr<ui::TextureProgramGL> program_swizzle_;
111 scoped_ptr<ui::TextureProgramGL> program_no_swizzle_;
112
113 DISALLOW_COPY_AND_ASSIGN(SharedResources);
114 };
115
116 GLuint CompileShader(GLenum type, const GLchar* source) { 87 GLuint CompileShader(GLenum type, const GLchar* source) {
117 GLuint shader = glCreateShader(type); 88 GLuint shader = glCreateShader(type);
118 if (!shader) 89 if (!shader)
119 return 0; 90 return 0;
120 91
121 glShaderSource(shader, 1, &source, 0); 92 glShaderSource(shader, 1, &source, 0);
122 glCompileShader(shader); 93 glCompileShader(shader);
123 94
124 GLint compiled; 95 GLint compiled;
125 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); 96 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
168 " gl_FragColor = texture2D(u_tex, v_texCoord).zyxw;" 139 " gl_FragColor = texture2D(u_tex, v_texCoord).zyxw;"
169 "}"; 140 "}";
170 141
171 frag_shader_ = CompileShader(GL_FRAGMENT_SHADER, frag_shader_source); 142 frag_shader_ = CompileShader(GL_FRAGMENT_SHADER, frag_shader_source);
172 if (!frag_shader_) 143 if (!frag_shader_)
173 return false; 144 return false;
174 145
175 return InitializeCommon(); 146 return InitializeCommon();
176 } 147 }
177 148
178 SharedResources::SharedResources() {
179 }
180
181 SharedResources::~SharedResources() {
182 }
183
184 // static
185 SharedResources* SharedResources::GetInstance() {
186 return Singleton<SharedResources>::get();
187 }
188
189 scoped_refptr<gfx::GLContext> SharedResources::CreateContext(
190 gfx::GLSurface* surface) {
191 if (share_group_.get()) {
192 return gfx::GLContext::CreateGLContext(share_group_.get(), surface);
193 } else {
194 scoped_refptr<gfx::GLContext> context(
195 gfx::GLContext::CreateGLContext(NULL, surface));
196 context->MakeCurrent(surface);
197
198 if (!program_no_swizzle_.get()) {
199 scoped_ptr<ui::TextureProgramGL> temp_program(
200 new TextureProgramNoSwizzleGL());
201 if (!temp_program->Initialize()) {
202 LOG(ERROR) << "Unable to initialize shaders (context = "
203 << static_cast<void*>(context.get()) << ")";
204 return NULL;
205 }
206 program_no_swizzle_.swap(temp_program);
207 }
208
209 if (!program_swizzle_.get()) {
210 scoped_ptr<ui::TextureProgramGL> temp_program(
211 new TextureProgramSwizzleGL());
212 if (!temp_program->Initialize()) {
213 LOG(ERROR) << "Unable to initialize shaders (context = "
214 << static_cast<void*>(context.get()) << ")";
215 return NULL;
216 }
217 program_swizzle_.swap(temp_program);
218 }
219
220 share_group_ = context->share_group();
221 return context;
222 }
223 }
224
225 void SharedResources::ContextDestroyed() {
226 if (share_group_.get() && share_group_->GetHandle() == NULL) {
227 share_group_ = NULL;
228 program_no_swizzle_.reset();
229 program_swizzle_.reset();
230 }
231 }
232
233 } // namespace 149 } // namespace
234 150
235 namespace ui { 151 namespace ui {
236 152
237 TextureProgramGL::TextureProgramGL() 153 TextureProgramGL::TextureProgramGL()
238 : program_(0), 154 : program_(0),
239 a_pos_loc_(0), 155 a_pos_loc_(0),
240 a_tex_loc_(0), 156 a_tex_loc_(0),
241 u_tex_loc_(0), 157 u_tex_loc_(0),
242 u_mat_loc_(0) { 158 u_mat_loc_(0) {
(...skipping 25 matching lines...) Expand all
268 184
269 // Store locations of program inputs. 185 // Store locations of program inputs.
270 a_pos_loc_ = glGetAttribLocation(program_, "a_position"); 186 a_pos_loc_ = glGetAttribLocation(program_, "a_position");
271 a_tex_loc_ = glGetAttribLocation(program_, "a_texCoord"); 187 a_tex_loc_ = glGetAttribLocation(program_, "a_texCoord");
272 u_tex_loc_ = glGetUniformLocation(program_, "u_tex"); 188 u_tex_loc_ = glGetUniformLocation(program_, "u_tex");
273 u_mat_loc_ = glGetUniformLocation(program_, "u_matViewProjection"); 189 u_mat_loc_ = glGetUniformLocation(program_, "u_matViewProjection");
274 190
275 return true; 191 return true;
276 } 192 }
277 193
278 TextureGL::TextureGL(CompositorGL* compositor) : texture_id_(0), 194 SharedResources::SharedResources() : initialized_(false) {
279 compositor_(compositor) {
280 } 195 }
281 196
282 TextureGL::TextureGL(CompositorGL* compositor, 197
283 const gfx::Size& size) 198 SharedResources::~SharedResources() {
284 : texture_id_(0), 199 }
285 size_(size), 200
286 compositor_(compositor) { 201 // static
202 SharedResources* SharedResources::GetInstance() {
203 // We use LeakSingletonTraits so that we don't race with
Ken Russell (switch to Gerrit) 2011/08/25 04:42:17 Leak -> Leaky
204 // the tear down of the gl_bindings.
205 SharedResources* instance = Singleton<SharedResources,
206 LeakySingletonTraits<SharedResources> >::get();
207 if (instance->Initialize()) {
208 return instance;
209 } else {
210 instance->Destroy();
211 return NULL;
212 }
213 }
214
215 bool SharedResources::Initialize() {
216 if (initialized_)
217 return true;
Ken Russell (switch to Gerrit) 2011/08/25 04:42:17 If you had threading problems in the initializatio
jonathan.backer 2011/08/25 18:57:18 Yup. This is the origin of it, as commented in the
218
219 {
220 // The following line of code exists soley to disable IO restrictions
221 // on this thread long enough to perform the GL bindings.
222 // TODO(wjmaclean) Remove this when GL initialisation cleaned up.
223 base::ThreadRestrictions::ScopedAllowIO allow_io;
224 if (!gfx::GLSurface::InitializeOneOff() ||
225 gfx::GetGLImplementation() == gfx::kGLImplementationNone) {
226 LOG(ERROR) << "Could not load the GL bindings";
227 return false;
228 }
229 }
230
231 surface_ = gfx::GLSurface::CreateOffscreenGLSurface(false, gfx::Size(1, 1));
232 if (!surface_.get()) {
233 LOG(ERROR) << "Unable to create offscreen GL surface.";
234 return false;
235 }
236
237 context_ = gfx::GLContext::CreateGLContext(NULL, surface_.get());
238 if (!context_.get()) {
239 LOG(ERROR) << "Unable to create GL context.";
240 return false;
241 }
242
243 program_no_swizzle_.reset();
244 program_swizzle_.reset();
245
246 context_->MakeCurrent(surface_.get());
247
248 scoped_ptr<ui::TextureProgramGL> temp_program_no_swizzle(
249 new TextureProgramNoSwizzleGL());
250 if (!temp_program_no_swizzle->Initialize()) {
251 LOG(ERROR) << "Unable to initialize shader.";
252 return false;
253 }
254
255 scoped_ptr<ui::TextureProgramGL> temp_program_swizzle(
256 new TextureProgramSwizzleGL());
257 if (!temp_program_swizzle->Initialize()) {
258 LOG(ERROR) << "Unable to initialize shader.";
259 return false;
260 }
261
262 program_no_swizzle_.swap(temp_program_no_swizzle);
263 program_swizzle_.swap(temp_program_swizzle);
264
265 initialized_ = true;
266 return true;
267 }
268
269 void SharedResources::Destroy() {
270 program_swizzle_.reset();
271 program_no_swizzle_.reset();
272
273 context_ = NULL;
274 surface_ = NULL;
275
276 initialized_ = false;
277 }
278
279 bool SharedResources::MakeSharedContextCurrent() {
280 if (!initialized_)
281 return false;
282 else
283 return context_->MakeCurrent(surface_.get());
284 }
285
286 scoped_refptr<gfx::GLContext> SharedResources::CreateContext(
287 gfx::GLSurface* surface) {
288 if (initialized_)
289 return gfx::GLContext::CreateGLContext(context_->share_group(), surface);
290 else
291 return NULL;
292 }
293
294 TextureGL::TextureGL() : texture_id_(0) {
295 }
296
297 TextureGL::TextureGL(const gfx::Size& size) : texture_id_(0), size_(size) {
287 } 298 }
288 299
289 TextureGL::~TextureGL() { 300 TextureGL::~TextureGL() {
290 if (texture_id_) { 301 if (texture_id_) {
291 compositor_->MakeCurrent(); 302 SharedResources* instance = SharedResources::GetInstance();
303 DCHECK(instance);
304 instance->MakeSharedContextCurrent();
292 glDeleteTextures(1, &texture_id_); 305 glDeleteTextures(1, &texture_id_);
293 } 306 }
294 } 307 }
295 308
296 void TextureGL::SetCanvas(const SkCanvas& canvas, 309 void TextureGL::SetCanvas(const SkCanvas& canvas,
297 const gfx::Point& origin, 310 const gfx::Point& origin,
298 const gfx::Size& overall_size) { 311 const gfx::Size& overall_size) {
299 const SkBitmap& bitmap = canvas.getDevice()->accessBitmap(false); 312 const SkBitmap& bitmap = canvas.getDevice()->accessBitmap(false);
300 // Verify bitmap pixels are contiguous. 313 // Verify bitmap pixels are contiguous.
301 DCHECK_EQ(bitmap.rowBytes(), 314 DCHECK_EQ(bitmap.rowBytes(),
(...skipping 23 matching lines...) Expand all
325 size_.width(), size_.height(), 0, 338 size_.width(), size_.height(), 0,
326 GL_RGBA, GL_UNSIGNED_BYTE, pixels); 339 GL_RGBA, GL_UNSIGNED_BYTE, pixels);
327 } else { // Uploading partial texture. 340 } else { // Uploading partial texture.
328 glBindTexture(GL_TEXTURE_2D, texture_id_); 341 glBindTexture(GL_TEXTURE_2D, texture_id_);
329 glTexSubImage2D(GL_TEXTURE_2D, 0, origin.x(), origin.y(), 342 glTexSubImage2D(GL_TEXTURE_2D, 0, origin.x(), origin.y(),
330 bitmap.width(), bitmap.height(), 343 bitmap.width(), bitmap.height(),
331 GL_RGBA, GL_UNSIGNED_BYTE, pixels); 344 GL_RGBA, GL_UNSIGNED_BYTE, pixels);
332 } 345 }
333 } 346 }
334 347
335 void TextureGL::Draw(const ui::TextureDrawParams& params) { 348 void TextureGL::Draw(const ui::TextureDrawParams& params,
336 DCHECK(compositor_->program_swizzle()); 349 const gfx::Size& surface_size) {
337 Draw(params, gfx::Rect(0, 0, size_.width(), size_.height())); 350 Draw(params, gfx::Rect(0, 0, size_.width(), size_.height()), surface_size);
338 } 351 }
339 352
340 void TextureGL::Draw(const ui::TextureDrawParams& params, 353 void TextureGL::Draw(const ui::TextureDrawParams& params,
341 const gfx::Rect& clip_bounds_in_texture) { 354 const gfx::Rect& clip_bounds_in_texture,
342 DCHECK(compositor_->program_swizzle()); 355 const gfx::Size& surface_size) {
343 DrawInternal(*compositor_->program_swizzle(), params, clip_bounds_in_texture); 356 SharedResources* instance = SharedResources::GetInstance();
357 DCHECK(instance);
358 DrawInternal(*instance->program_swizzle(),
359 params,
360 clip_bounds_in_texture,
361 surface_size);
344 } 362 }
363
345 void TextureGL::DrawInternal(const ui::TextureProgramGL& program, 364 void TextureGL::DrawInternal(const ui::TextureProgramGL& program,
346 const ui::TextureDrawParams& params, 365 const ui::TextureDrawParams& params,
347 const gfx::Rect& clip_bounds_in_texture) { 366 const gfx::Rect& clip_bounds_in_texture,
348 // clip clip_bounds_in_layer to size of texture 367 const gfx::Size& surface_size) {
368 // Clip clip_bounds_in_texture to size of texture
349 gfx::Rect clip_bounds = clip_bounds_in_texture.Intersect( 369 gfx::Rect clip_bounds = clip_bounds_in_texture.Intersect(
350 gfx::Rect(gfx::Point(0, 0), size_)); 370 gfx::Rect(gfx::Point(0, 0), size_));
351 371
352 if (params.blend) 372 if (params.blend)
353 glEnable(GL_BLEND); 373 glEnable(GL_BLEND);
354 else 374 else
355 glDisable(GL_BLEND); 375 glDisable(GL_BLEND);
356 376
357 program.Use(); 377 program.Use();
358 378
359 glActiveTexture(GL_TEXTURE0); 379 glActiveTexture(GL_TEXTURE0);
360 glUniform1i(program.u_tex_loc(), 0); 380 glUniform1i(program.u_tex_loc(), 0);
361 glBindTexture(GL_TEXTURE_2D, texture_id_); 381 glBindTexture(GL_TEXTURE_2D, texture_id_);
362 382
363 gfx::Size window_size = compositor_->GetSize();
364
365 ui::Transform t; 383 ui::Transform t;
366 t.ConcatTranslate(1, 1); 384 t.ConcatTranslate(1, 1);
367 t.ConcatScale(size_.width()/2.0f, size_.height()/2.0f); 385 t.ConcatScale(size_.width()/2.0f, size_.height()/2.0f);
368 t.ConcatTranslate(0, -size_.height()); 386 t.ConcatTranslate(0, -size_.height());
369 t.ConcatScale(1, -1); 387 t.ConcatScale(1, -1);
370 388
371 t.ConcatTransform(params.transform); // Add view transform. 389 t.ConcatTransform(params.transform); // Add view transform.
372 390
373 t.ConcatTranslate(0, -window_size.height()); 391 t.ConcatTranslate(0, -surface_size.height());
374 t.ConcatScale(1, -1); 392 t.ConcatScale(1, -1);
375 t.ConcatTranslate(-window_size.width() / 2.0f, -window_size.height() / 2.0f); 393 t.ConcatTranslate(-surface_size.width() / 2.0f,
376 t.ConcatScale(2.0f / window_size.width(), 2.0f / window_size.height()); 394 -surface_size.height() / 2.0f);
395 t.ConcatScale(2.0f / surface_size.width(),
396 2.0f / surface_size.height());
377 397
378 GLfloat m[16]; 398 GLfloat m[16];
379 t.matrix().asColMajorf(m); 399 t.matrix().asColMajorf(m);
380 400
381 // TODO(pkotwicz) window_size != size_, fix this
382 SkRect texture_rect = SkRect::MakeXYWH( 401 SkRect texture_rect = SkRect::MakeXYWH(
383 clip_bounds.x(), 402 clip_bounds.x(),
384 clip_bounds.y(), 403 clip_bounds.y(),
385 clip_bounds.width(), 404 clip_bounds.width(),
386 clip_bounds.height()); 405 clip_bounds.height());
387 406
388 ui::Transform texture_rect_transform; 407 ui::Transform texture_rect_transform;
389 texture_rect_transform.ConcatScale(1.0f / size_.width(), 408 texture_rect_transform.ConcatScale(1.0f / size_.width(),
390 1.0f / size_.height()); 409 1.0f / size_.height());
391 SkMatrix texture_transform_matrix = texture_rect_transform.matrix(); 410 SkMatrix texture_transform_matrix = texture_rect_transform.matrix();
(...skipping 30 matching lines...) Expand all
422 glEnableVertexAttribArray(program.a_pos_loc()); 441 glEnableVertexAttribArray(program.a_pos_loc());
423 glEnableVertexAttribArray(program.a_tex_loc()); 442 glEnableVertexAttribArray(program.a_tex_loc());
424 443
425 glUniformMatrix4fv(program.u_mat_loc(), 1, GL_FALSE, m); 444 glUniformMatrix4fv(program.u_mat_loc(), 1, GL_FALSE, m);
426 445
427 glDrawArrays(GL_TRIANGLE_FAN, 0, 4); 446 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
428 } 447 }
429 448
430 CompositorGL::CompositorGL(gfx::AcceleratedWidget widget, 449 CompositorGL::CompositorGL(gfx::AcceleratedWidget widget,
431 const gfx::Size& size) 450 const gfx::Size& size)
432 : size_(size), 451 : Compositor(size),
433 started_(false) { 452 started_(false) {
434 gl_surface_ = gfx::GLSurface::CreateViewGLSurface(false, widget); 453 gl_surface_ = gfx::GLSurface::CreateViewGLSurface(false, widget);
435 gl_context_ = SharedResources::GetInstance()-> 454 gl_context_ = SharedResources::GetInstance()->
436 CreateContext(gl_surface_.get()); 455 CreateContext(gl_surface_.get());
437 gl_context_->MakeCurrent(gl_surface_.get()); 456 gl_context_->MakeCurrent(gl_surface_.get());
438 glColorMask(true, true, true, true); 457 glColorMask(true, true, true, true);
439 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 458 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
440 } 459 }
441 460
442 CompositorGL::~CompositorGL() { 461 CompositorGL::~CompositorGL() {
443 gl_context_ = NULL; 462 gl_context_ = NULL;
444 SharedResources::GetInstance()->ContextDestroyed();
445 } 463 }
446 464
447 void CompositorGL::MakeCurrent() { 465 void CompositorGL::MakeCurrent() {
448 gl_context_->MakeCurrent(gl_surface_.get()); 466 gl_context_->MakeCurrent(gl_surface_.get());
449 } 467 }
450 468
451 gfx::Size CompositorGL::GetSize() {
452 return size_;
453 }
454
455 TextureProgramGL* CompositorGL::program_no_swizzle() {
456 return SharedResources::GetInstance()->program_no_swizzle();
457 }
458
459 TextureProgramGL* CompositorGL::program_swizzle() {
460 return SharedResources::GetInstance()->program_swizzle();
461 }
462
463 Texture* CompositorGL::CreateTexture() { 469 Texture* CompositorGL::CreateTexture() {
464 Texture* texture = new TextureGL(this); 470 Texture* texture = new TextureGL();
465 return texture; 471 return texture;
466 } 472 }
467 473
468 void CompositorGL::NotifyStart() { 474 void CompositorGL::NotifyStart() {
469 started_ = true; 475 started_ = true;
470 gl_context_->MakeCurrent(gl_surface_.get()); 476 gl_context_->MakeCurrent(gl_surface_.get());
471 glViewport(0, 0, size_.width(), size_.height()); 477 glViewport(0, 0, size().width(), size().height());
472 glColorMask(true, true, true, true); 478 glColorMask(true, true, true, true);
473 479
474 #if defined(DEBUG) 480 #if defined(DEBUG)
475 // Clear to 'psychedelic' purple to make it easy to spot un-rendered regions. 481 // Clear to 'psychedelic' purple to make it easy to spot un-rendered regions.
476 glClearColor(223.0 / 255, 0, 1, 1); 482 glClearColor(223.0 / 255, 0, 1, 1);
477 glClear(GL_COLOR_BUFFER_BIT); 483 glClear(GL_COLOR_BUFFER_BIT);
478 #endif 484 #endif
479 // Do not clear in release: root layer is responsible for drawing every pixel. 485 // Do not clear in release: root layer is responsible for drawing every pixel.
480 } 486 }
481 487
482 void CompositorGL::NotifyEnd() { 488 void CompositorGL::NotifyEnd() {
483 DCHECK(started_); 489 DCHECK(started_);
484 gl_surface_->SwapBuffers(); 490 gl_surface_->SwapBuffers();
485 started_ = false; 491 started_ = false;
486 } 492 }
487 493
488 void CompositorGL::Blur(const gfx::Rect& bounds) { 494 void CompositorGL::Blur(const gfx::Rect& bounds) {
489 NOTIMPLEMENTED(); 495 NOTIMPLEMENTED();
490 } 496 }
491 497
492 void CompositorGL::SchedulePaint() { 498 void CompositorGL::SchedulePaint() {
493 // TODO: X doesn't provide coalescing of regions, its left to the toolkit. 499 // TODO: X doesn't provide coalescing of regions, its left to the toolkit.
494 NOTIMPLEMENTED(); 500 NOTIMPLEMENTED();
495 } 501 }
496 502
497 void CompositorGL::OnWidgetSizeChanged(const gfx::Size& size) { 503 void CompositorGL::OnWidgetSizeChanged(const gfx::Size& size) {
498 size_ = size; 504 SetSize(size);
499 } 505 }
500 506
501 // static 507 // static
502 Compositor* Compositor::Create(gfx::AcceleratedWidget widget, 508 Compositor* Compositor::Create(gfx::AcceleratedWidget widget,
503 const gfx::Size& size) { 509 const gfx::Size& size) {
504 // The following line of code exists soley to disable IO restrictions 510 if (SharedResources::GetInstance() == NULL)
505 // on this thread long enough to perform the GL bindings. 511 return NULL;
506 // TODO(wjmaclean) Remove this when GL initialisation cleaned up. 512 else
507 base::ThreadRestrictions::ScopedAllowIO allow_io;
508 if (gfx::GLSurface::InitializeOneOff() &&
509 gfx::GetGLImplementation() != gfx::kGLImplementationNone)
510 return new CompositorGL(widget, size); 513 return new CompositorGL(widget, size);
511 return NULL;
512 } 514 }
513 515
514 } // namespace ui 516 } // namespace ui
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698