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

Side by Side Diff: third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.cpp

Issue 2401513002: DrawingBuffer cleanup: Merge structures (Closed)
Patch Set: Move frontbuffer reset to mailbox return Created 4 years, 2 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
« no previous file with comments | « third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * Copyright (c) 2010, Google Inc. All rights reserved. 2 * Copyright (c) 2010, Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
175 m_wantStencil(wantStencil), 175 m_wantStencil(wantStencil),
176 m_chromiumImageUsage(chromiumImageUsage) { 176 m_chromiumImageUsage(chromiumImageUsage) {
177 memset(m_colorMask, 0, 4 * sizeof(GLboolean)); 177 memset(m_colorMask, 0, 4 * sizeof(GLboolean));
178 memset(m_clearColor, 0, 4 * sizeof(GLfloat)); 178 memset(m_clearColor, 0, 4 * sizeof(GLfloat));
179 // Used by browser tests to detect the use of a DrawingBuffer. 179 // Used by browser tests to detect the use of a DrawingBuffer.
180 TRACE_EVENT_INSTANT0("test_gpu", "DrawingBufferCreation", 180 TRACE_EVENT_INSTANT0("test_gpu", "DrawingBufferCreation",
181 TRACE_EVENT_SCOPE_GLOBAL); 181 TRACE_EVENT_SCOPE_GLOBAL);
182 } 182 }
183 183
184 DrawingBuffer::~DrawingBuffer() { 184 DrawingBuffer::~DrawingBuffer() {
185 ASSERT(m_destructionInProgress); 185 DCHECK(m_destructionInProgress);
186 ASSERT(m_textureMailboxes.isEmpty());
187 m_layer.reset(); 186 m_layer.reset();
188 m_contextProvider.reset(); 187 m_contextProvider.reset();
189 } 188 }
190 189
191 void DrawingBuffer::markContentsChanged() { 190 void DrawingBuffer::markContentsChanged() {
192 m_contentsChanged = true; 191 m_contentsChanged = true;
193 m_contentsChangeCommitted = false; 192 m_contentsChangeCommitted = false;
194 } 193 }
195 194
196 bool DrawingBuffer::bufferClearNeeded() const { 195 bool DrawingBuffer::bufferClearNeeded() const {
(...skipping 14 matching lines...) Expand all
211 210
212 WebGraphicsContext3DProvider* DrawingBuffer::contextProvider() { 211 WebGraphicsContext3DProvider* DrawingBuffer::contextProvider() {
213 return m_contextProvider.get(); 212 return m_contextProvider.get();
214 } 213 }
215 214
216 void DrawingBuffer::setIsHidden(bool hidden) { 215 void DrawingBuffer::setIsHidden(bool hidden) {
217 if (m_isHidden == hidden) 216 if (m_isHidden == hidden)
218 return; 217 return;
219 m_isHidden = hidden; 218 m_isHidden = hidden;
220 if (m_isHidden) 219 if (m_isHidden)
221 freeRecycledMailboxes(); 220 m_recycledMailboxQueue.clear();
222 } 221 }
223 222
224 void DrawingBuffer::setFilterQuality(SkFilterQuality filterQuality) { 223 void DrawingBuffer::setFilterQuality(SkFilterQuality filterQuality) {
225 if (m_filterQuality != filterQuality) { 224 if (m_filterQuality != filterQuality) {
226 m_filterQuality = filterQuality; 225 m_filterQuality = filterQuality;
227 if (m_layer) 226 if (m_layer)
228 m_layer->setNearestNeighbor(filterQuality == kNone_SkFilterQuality); 227 m_layer->setNearestNeighbor(filterQuality == kNone_SkFilterQuality);
229 } 228 }
230 } 229 }
231 230
232 bool DrawingBuffer::requiresAlphaChannelToBePreserved() { 231 bool DrawingBuffer::requiresAlphaChannelToBePreserved() {
233 return !m_drawFramebufferBinding && 232 return !m_drawFramebufferBinding &&
234 defaultBufferRequiresAlphaChannelToBePreserved(); 233 defaultBufferRequiresAlphaChannelToBePreserved();
235 } 234 }
236 235
237 bool DrawingBuffer::defaultBufferRequiresAlphaChannelToBePreserved() { 236 bool DrawingBuffer::defaultBufferRequiresAlphaChannelToBePreserved() {
238 if (wantExplicitResolve()) { 237 if (wantExplicitResolve()) {
239 return !m_wantAlphaChannel && 238 return !m_wantAlphaChannel &&
240 getMultisampledRenderbufferFormat() == GL_RGBA8_OES; 239 getMultisampledRenderbufferFormat() == GL_RGBA8_OES;
241 } 240 }
242 241
243 bool rgbEmulation = 242 bool rgbEmulation =
244 contextProvider()->getCapabilities().emulate_rgb_buffer_with_rgba || 243 contextProvider()->getCapabilities().emulate_rgb_buffer_with_rgba ||
245 (shouldUseChromiumImage() && 244 (shouldUseChromiumImage() &&
246 contextProvider()->getCapabilities().chromium_image_rgb_emulation); 245 contextProvider()->getCapabilities().chromium_image_rgb_emulation);
247 return !m_wantAlphaChannel && rgbEmulation; 246 return !m_wantAlphaChannel && rgbEmulation;
248 } 247 }
249 248
250 void DrawingBuffer::freeRecycledMailboxes() {
251 while (!m_recycledMailboxQueue.isEmpty()) {
252 RefPtr<RecycledMailbox> recycled = m_recycledMailboxQueue.takeLast();
253 deleteMailbox(recycled->mailbox, recycled->syncToken);
254 }
255 }
256
257 std::unique_ptr<cc::SharedBitmap> DrawingBuffer::createOrRecycleBitmap() { 249 std::unique_ptr<cc::SharedBitmap> DrawingBuffer::createOrRecycleBitmap() {
258 auto it = std::remove_if( 250 auto it = std::remove_if(
259 m_recycledBitmaps.begin(), m_recycledBitmaps.end(), 251 m_recycledBitmaps.begin(), m_recycledBitmaps.end(),
260 [this](const RecycledBitmap& bitmap) { return bitmap.size != m_size; }); 252 [this](const RecycledBitmap& bitmap) { return bitmap.size != m_size; });
261 m_recycledBitmaps.shrink(it - m_recycledBitmaps.begin()); 253 m_recycledBitmaps.shrink(it - m_recycledBitmaps.begin());
262 254
263 if (!m_recycledBitmaps.isEmpty()) { 255 if (!m_recycledBitmaps.isEmpty()) {
264 RecycledBitmap recycled = std::move(m_recycledBitmaps.last()); 256 RecycledBitmap recycled = std::move(m_recycledBitmaps.last());
265 m_recycledBitmaps.removeLast(); 257 m_recycledBitmaps.removeLast();
266 DCHECK(recycled.size == m_size); 258 DCHECK(recycled.size == m_size);
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
298 // software frames, until we get a new context, since the compositor will 290 // software frames, until we get a new context, since the compositor will
299 // be trying to get a new context and may change modes. 291 // be trying to get a new context and may change modes.
300 if (m_gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR) 292 if (m_gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR)
301 return false; 293 return false;
302 294
303 TRACE_EVENT0("blink,rail", "DrawingBuffer::prepareMailbox"); 295 TRACE_EVENT0("blink,rail", "DrawingBuffer::prepareMailbox");
304 296
305 if (m_newMailboxCallback) 297 if (m_newMailboxCallback)
306 (*m_newMailboxCallback)(); 298 (*m_newMailboxCallback)();
307 299
308 // Resolve the multisampled buffer into m_colorBuffer texture. 300 // Resolve the multisampled buffer into m_backColorBuffer texture.
309 if (m_antiAliasingMode != None) 301 if (m_antiAliasingMode != None)
310 commit(); 302 commit();
311 303
312 if (m_softwareRendering && !forceGpuResult) { 304 if (m_softwareRendering && !forceGpuResult) {
313 std::unique_ptr<cc::SharedBitmap> bitmap = createOrRecycleBitmap(); 305 std::unique_ptr<cc::SharedBitmap> bitmap = createOrRecycleBitmap();
314 if (!bitmap) 306 if (!bitmap)
315 return false; 307 return false;
316 unsigned char* pixels = bitmap->pixels(); 308 unsigned char* pixels = bitmap->pixels();
317 DCHECK(pixels); 309 DCHECK(pixels);
318 310
(...skipping 20 matching lines...) Expand all
339 if (m_webGLVersion > WebGL1) { 331 if (m_webGLVersion > WebGL1) {
340 m_gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); 332 m_gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
341 } 333 }
342 334
343 // We must restore the texture binding since creating new textures, 335 // We must restore the texture binding since creating new textures,
344 // consuming and producing mailboxes changes it. 336 // consuming and producing mailboxes changes it.
345 ScopedTextureUnit0BindingRestorer restorer(m_gl, m_activeTextureUnit, 337 ScopedTextureUnit0BindingRestorer restorer(m_gl, m_activeTextureUnit,
346 m_texture2DBinding); 338 m_texture2DBinding);
347 339
348 // First try to recycle an old buffer. 340 // First try to recycle an old buffer.
349 RefPtr<MailboxInfo> mailboxInfo = takeRecycledMailbox(); 341 RefPtr<ColorBuffer> colorBufferForMailbox = takeRecycledMailbox();
350 342
351 // No buffer available to recycle, create a new one. 343 // No buffer available to recycle, create a new one.
352 if (!mailboxInfo) 344 if (!colorBufferForMailbox)
353 mailboxInfo = createNewMailbox(createTextureAndAllocateMemory(m_size)); 345 colorBufferForMailbox = createTextureAndAllocateMemory(m_size);
354 346
355 if (m_preserveDrawingBuffer == Discard) { 347 if (m_preserveDrawingBuffer == Discard) {
356 std::swap(mailboxInfo->textureInfo, m_colorBuffer); 348 std::swap(colorBufferForMailbox, m_backColorBuffer);
357 attachColorBufferToReadFramebuffer(); 349 attachColorBufferToReadFramebuffer();
358 350
359 if (m_discardFramebufferSupported) { 351 if (m_discardFramebufferSupported) {
360 // Explicitly discard the framebuffer to save GPU memory bandwidth for 352 // Explicitly discard the framebuffer to save GPU memory bandwidth for
361 // tile-based GPU arch. 353 // tile-based GPU arch.
362 const GLenum attachments[3] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, 354 const GLenum attachments[3] = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT,
363 GL_STENCIL_ATTACHMENT}; 355 GL_STENCIL_ATTACHMENT};
364 m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fbo); 356 m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fbo);
365 m_gl->DiscardFramebufferEXT(GL_FRAMEBUFFER, 3, attachments); 357 m_gl->DiscardFramebufferEXT(GL_FRAMEBUFFER, 3, attachments);
366 } 358 }
367 } else { 359 } else {
368 m_gl->CopySubTextureCHROMIUM( 360 m_gl->CopySubTextureCHROMIUM(
369 m_colorBuffer.textureId, mailboxInfo->textureInfo.textureId, 0, 0, 0, 0, 361 m_backColorBuffer->textureId, colorBufferForMailbox->textureId, 0, 0, 0,
370 m_size.width(), m_size.height(), GL_FALSE, GL_FALSE, GL_FALSE); 362 0, m_size.width(), m_size.height(), GL_FALSE, GL_FALSE, GL_FALSE);
371 } 363 }
372 364
373 restoreFramebufferBindings(); 365 restoreFramebufferBindings();
374 restorePixelUnpackBufferBindings(); 366 restorePixelUnpackBufferBindings();
375 m_contentsChanged = false; 367 m_contentsChanged = false;
376 368
377 m_gl->ProduceTextureDirectCHROMIUM(mailboxInfo->textureInfo.textureId, 369 m_gl->ProduceTextureDirectCHROMIUM(colorBufferForMailbox->textureId,
378 mailboxInfo->textureInfo.parameters.target, 370 colorBufferForMailbox->parameters.target,
379 mailboxInfo->mailbox.name); 371 colorBufferForMailbox->mailbox.name);
380 const GLuint64 fenceSync = m_gl->InsertFenceSyncCHROMIUM(); 372 const GLuint64 fenceSync = m_gl->InsertFenceSyncCHROMIUM();
381 #if OS(MACOSX) 373 #if OS(MACOSX)
382 m_gl->DescheduleUntilFinishedCHROMIUM(); 374 m_gl->DescheduleUntilFinishedCHROMIUM();
383 #endif 375 #endif
384 m_gl->Flush(); 376 m_gl->Flush();
385 gpu::SyncToken syncToken; 377 gpu::SyncToken syncToken;
386 m_gl->GenSyncTokenCHROMIUM(fenceSync, syncToken.GetData()); 378 m_gl->GenSyncTokenCHROMIUM(fenceSync, syncToken.GetData());
387 379
388 bool isOverlayCandidate = mailboxInfo->textureInfo.imageId != 0; 380 bool isOverlayCandidate = colorBufferForMailbox->imageId != 0;
389 bool secureOutputOnly = false; 381 bool secureOutputOnly = false;
390 *outMailbox = cc::TextureMailbox(mailboxInfo->mailbox, syncToken, 382 *outMailbox = cc::TextureMailbox(colorBufferForMailbox->mailbox, syncToken,
391 mailboxInfo->textureInfo.parameters.target, 383 colorBufferForMailbox->parameters.target,
392 gfx::Size(m_size.width(), m_size.height()), 384 gfx::Size(m_size.width(), m_size.height()),
393 isOverlayCandidate, secureOutputOnly); 385 isOverlayCandidate, secureOutputOnly);
394 386
395 // This holds a ref on the DrawingBuffer that will keep it alive until the 387 // This holds a ref on the DrawingBuffer that will keep it alive until the
396 // mailbox is released (and while the release callback is running). 388 // mailbox is released (and while the release callback is running).
397 auto func = WTF::bind(&DrawingBuffer::gpuMailboxReleased, 389 auto func = WTF::bind(&DrawingBuffer::gpuMailboxReleased,
398 RefPtr<DrawingBuffer>(this), mailboxInfo->mailbox); 390 RefPtr<DrawingBuffer>(this), colorBufferForMailbox);
399 *outReleaseCallback = 391 *outReleaseCallback =
400 cc::SingleReleaseCallback::Create(convertToBaseCallback(std::move(func))); 392 cc::SingleReleaseCallback::Create(convertToBaseCallback(std::move(func)));
401 393
402 m_frontColorBuffer = {mailboxInfo->mailbox, syncToken, 394 // Point |m_frontColorBuffer| to the buffer that we are presenting, and
403 mailboxInfo->textureInfo}; 395 // update its sync token.
396 colorBufferForMailbox->produceSyncToken = syncToken;
397 m_frontColorBuffer = colorBufferForMailbox;
404 setBufferClearNeeded(true); 398 setBufferClearNeeded(true);
405 return true; 399 return true;
406 } 400 }
407 401
408 void DrawingBuffer::gpuMailboxReleased(const gpu::Mailbox& mailbox, 402 void DrawingBuffer::gpuMailboxReleased(RefPtr<ColorBuffer> colorBuffer,
409 const gpu::SyncToken& syncToken, 403 const gpu::SyncToken& syncToken,
410 bool lostResource) { 404 bool lostResource) {
411 if (m_destructionInProgress || 405 // If the mailbox has been returned by the compositor then it is no
406 // longer being presented, and so is no longer the front buffer.
407 if (colorBuffer == m_frontColorBuffer)
408 m_frontColorBuffer = nullptr;
409
410 // Update the SyncToken to ensure that we will wait for it even if we
411 // immediately destroy this buffer.
412 colorBuffer->receiveSyncToken = syncToken;
413
414 if (m_destructionInProgress || colorBuffer->size != m_size ||
412 m_gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR || lostResource || 415 m_gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR || lostResource ||
413 m_isHidden) { 416 m_isHidden) {
414 deleteMailbox(mailbox, syncToken);
415 return; 417 return;
416 } 418 }
417 419
418 // Creation of image backed mailboxes is very expensive, so be less 420 // Creation of image backed mailboxes is very expensive, so be less
419 // aggressive about pruning them. Pruning is done in FIFO order. 421 // aggressive about pruning them. Pruning is done in FIFO order.
420 size_t cacheLimit = 1; 422 size_t cacheLimit = 1;
421 if (shouldUseChromiumImage()) 423 if (shouldUseChromiumImage())
422 cacheLimit = 4; 424 cacheLimit = 4;
423 while (m_recycledMailboxQueue.size() >= cacheLimit) { 425 while (m_recycledMailboxQueue.size() >= cacheLimit)
424 RefPtr<RecycledMailbox> recycled = m_recycledMailboxQueue.takeLast(); 426 m_recycledMailboxQueue.takeLast();
425 deleteMailbox(recycled->mailbox, recycled->syncToken);
426 }
427 427
428 RefPtr<MailboxInfo> mailboxInfo; 428 m_recycledMailboxQueue.prepend(colorBuffer);
429 for (size_t i = 0; i < m_textureMailboxes.size(); i++) {
430 if (m_textureMailboxes[i]->mailbox == mailbox) {
431 mailboxInfo = m_textureMailboxes[i];
432 break;
433 }
434 }
435 DCHECK(mailboxInfo);
436 if (mailboxInfo->size != m_size) {
437 deleteMailbox(mailbox, syncToken);
438 return;
439 }
440
441 m_recycledMailboxQueue.prepend(
442 adoptRef(new RecycledMailbox(mailbox, syncToken)));
443 } 429 }
444 430
445 void DrawingBuffer::softwareMailboxReleased( 431 void DrawingBuffer::softwareMailboxReleased(
446 std::unique_ptr<cc::SharedBitmap> bitmap, 432 std::unique_ptr<cc::SharedBitmap> bitmap,
447 const IntSize& size, 433 const IntSize& size,
448 const gpu::SyncToken& syncToken, 434 const gpu::SyncToken& syncToken,
449 bool lostResource) { 435 bool lostResource) {
450 DCHECK(!syncToken.HasData()); // No sync tokens for software resources. 436 DCHECK(!syncToken.HasData()); // No sync tokens for software resources.
451 if (m_destructionInProgress || lostResource || m_isHidden || size != m_size) 437 if (m_destructionInProgress || lostResource || m_isHidden || size != m_size)
452 return; // Just delete the bitmap. 438 return; // Just delete the bitmap.
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
575 } else { 561 } else {
576 GLenum format = 562 GLenum format =
577 defaultBufferRequiresAlphaChannelToBePreserved() ? GL_RGBA : GL_RGB; 563 defaultBufferRequiresAlphaChannelToBePreserved() ? GL_RGBA : GL_RGB;
578 parameters.creationInternalColorFormat = format; 564 parameters.creationInternalColorFormat = format;
579 parameters.internalColorFormat = format; 565 parameters.internalColorFormat = format;
580 parameters.colorFormat = format; 566 parameters.colorFormat = format;
581 } 567 }
582 return parameters; 568 return parameters;
583 } 569 }
584 570
585 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::takeRecycledMailbox() { 571 PassRefPtr<DrawingBuffer::ColorBuffer> DrawingBuffer::takeRecycledMailbox() {
586 if (m_recycledMailboxQueue.isEmpty()) 572 if (m_recycledMailboxQueue.isEmpty())
587 return nullptr; 573 return nullptr;
588 574
589 RefPtr<RecycledMailbox> recycled = m_recycledMailboxQueue.takeLast(); 575 RefPtr<ColorBuffer> recycled = m_recycledMailboxQueue.takeLast();
590 if (recycled->syncToken.HasData()) 576 DCHECK(recycled->size == m_size);
591 m_gl->WaitSyncTokenCHROMIUM(recycled->syncToken.GetData()); 577 if (recycled->receiveSyncToken.HasData())
592 578 m_gl->WaitSyncTokenCHROMIUM(recycled->receiveSyncToken.GetData());
593 RefPtr<MailboxInfo> mailboxInfo; 579 return recycled;
594 for (size_t i = 0; i < m_textureMailboxes.size(); i++) {
595 if (m_textureMailboxes[i]->mailbox == recycled->mailbox) {
596 mailboxInfo = m_textureMailboxes[i];
597 break;
598 }
599 }
600 DCHECK(mailboxInfo);
601 DCHECK(mailboxInfo->size == m_size);
602 return mailboxInfo.release();
603 } 580 }
604 581
605 PassRefPtr<DrawingBuffer::MailboxInfo> DrawingBuffer::createNewMailbox( 582 DrawingBuffer::ColorBuffer::ColorBuffer(DrawingBuffer* drawingBuffer,
606 const TextureInfo& info) { 583 const TextureParameters& parameters,
607 RefPtr<MailboxInfo> returnMailbox = adoptRef(new MailboxInfo); 584 const IntSize& size)
608 m_gl->GenMailboxCHROMIUM(returnMailbox->mailbox.name); 585 : drawingBuffer(drawingBuffer), parameters(parameters), size(size) {
609 returnMailbox->textureInfo = info; 586 drawingBuffer->contextGL()->GenMailboxCHROMIUM(mailbox.name);
610 returnMailbox->size = m_size;
611 m_textureMailboxes.append(returnMailbox);
612 return returnMailbox.release();
613 } 587 }
614 588
615 void DrawingBuffer::deleteMailbox(const gpu::Mailbox& mailbox, 589 DrawingBuffer::ColorBuffer::~ColorBuffer() {
616 const gpu::SyncToken& syncToken) { 590 gpu::gles2::GLES2Interface* gl = drawingBuffer->contextGL();
617 if (syncToken.HasData()) 591 if (receiveSyncToken.HasData())
618 m_gl->WaitSyncTokenCHROMIUM(syncToken.GetConstData()); 592 gl->WaitSyncTokenCHROMIUM(receiveSyncToken.GetConstData());
619 for (size_t i = 0; i < m_textureMailboxes.size(); i++) { 593 if (imageId) {
620 if (m_textureMailboxes[i]->mailbox == mailbox) { 594 gl->BindTexture(parameters.target, textureId);
621 deleteChromiumImageForTexture(&m_textureMailboxes[i]->textureInfo); 595 gl->ReleaseTexImage2DCHROMIUM(parameters.target, imageId);
622 m_gl->DeleteTextures(1, &m_textureMailboxes[i]->textureInfo.textureId); 596 gl->DestroyImageCHROMIUM(imageId);
623 m_textureMailboxes.remove(i);
624 return;
625 }
626 } 597 }
627 ASSERT_NOT_REACHED(); 598 gl->DeleteTextures(1, &textureId);
628 } 599 }
629 600
630 bool DrawingBuffer::initialize(const IntSize& size, bool useMultisampling) { 601 bool DrawingBuffer::initialize(const IntSize& size, bool useMultisampling) {
631 if (m_gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR) { 602 if (m_gl->GetGraphicsResetStatusKHR() != GL_NO_ERROR) {
632 // Need to try to restore the context again later. 603 // Need to try to restore the context again later.
633 return false; 604 return false;
634 } 605 }
635 606
636 m_gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize); 607 m_gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &m_maxTextureSize);
637 608
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
699 m_gl->Flush(); 670 m_gl->Flush();
700 } 671 }
701 672
702 // Assume that the destination target is GL_TEXTURE_2D. 673 // Assume that the destination target is GL_TEXTURE_2D.
703 if (!Extensions3DUtil::canUseCopyTextureCHROMIUM( 674 if (!Extensions3DUtil::canUseCopyTextureCHROMIUM(
704 GL_TEXTURE_2D, internalFormat, destType, level)) 675 GL_TEXTURE_2D, internalFormat, destType, level))
705 return false; 676 return false;
706 677
707 // Contexts may be in a different share group. We must transfer the texture 678 // Contexts may be in a different share group. We must transfer the texture
708 // through a mailbox first. 679 // through a mailbox first.
709 GLint textureId = 0;
710 GLenum target = 0; 680 GLenum target = 0;
711 gpu::Mailbox mailbox; 681 gpu::Mailbox mailbox;
712 gpu::SyncToken produceSyncToken; 682 gpu::SyncToken produceSyncToken;
713 if (sourceBuffer == FrontBuffer && m_frontColorBuffer.texInfo.textureId) { 683 if (sourceBuffer == FrontBuffer && m_frontColorBuffer) {
714 textureId = m_frontColorBuffer.texInfo.textureId; 684 target = m_frontColorBuffer->parameters.target;
715 target = m_frontColorBuffer.texInfo.parameters.target; 685 mailbox = m_frontColorBuffer->mailbox;
716 mailbox = m_frontColorBuffer.mailbox; 686 produceSyncToken = m_frontColorBuffer->produceSyncToken;
717 produceSyncToken = m_frontColorBuffer.produceSyncToken;
718 } else { 687 } else {
719 textureId = m_colorBuffer.textureId; 688 target = m_backColorBuffer->parameters.target;
720 target = m_colorBuffer.parameters.target;
721 m_gl->GenMailboxCHROMIUM(mailbox.name); 689 m_gl->GenMailboxCHROMIUM(mailbox.name);
722 m_gl->ProduceTextureDirectCHROMIUM(textureId, target, mailbox.name); 690 m_gl->ProduceTextureDirectCHROMIUM(m_backColorBuffer->textureId, target,
691 mailbox.name);
723 const GLuint64 fenceSync = m_gl->InsertFenceSyncCHROMIUM(); 692 const GLuint64 fenceSync = m_gl->InsertFenceSyncCHROMIUM();
724 m_gl->Flush(); 693 m_gl->Flush();
725 m_gl->GenSyncTokenCHROMIUM(fenceSync, produceSyncToken.GetData()); 694 m_gl->GenSyncTokenCHROMIUM(fenceSync, produceSyncToken.GetData());
726 } 695 }
727 696
728 DCHECK(produceSyncToken.HasData()); 697 DCHECK(produceSyncToken.HasData());
729 gl->WaitSyncTokenCHROMIUM(produceSyncToken.GetConstData()); 698 gl->WaitSyncTokenCHROMIUM(produceSyncToken.GetConstData());
730 GLuint sourceTexture = 699 GLuint sourceTexture =
731 gl->CreateAndConsumeTextureCHROMIUM(target, mailbox.name); 700 gl->CreateAndConsumeTextureCHROMIUM(target, mailbox.name);
732 701
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
778 m_layer->clearTexture(); 747 m_layer->clearTexture();
779 748
780 m_gl->Flush(); 749 m_gl->Flush();
781 } 750 }
782 751
783 void DrawingBuffer::beginDestruction() { 752 void DrawingBuffer::beginDestruction() {
784 ASSERT(!m_destructionInProgress); 753 ASSERT(!m_destructionInProgress);
785 m_destructionInProgress = true; 754 m_destructionInProgress = true;
786 755
787 clearPlatformLayer(); 756 clearPlatformLayer();
788 freeRecycledMailboxes(); 757 m_recycledMailboxQueue.clear();
789 758
790 if (m_multisampleFBO) 759 if (m_multisampleFBO)
791 m_gl->DeleteFramebuffers(1, &m_multisampleFBO); 760 m_gl->DeleteFramebuffers(1, &m_multisampleFBO);
792 761
793 if (m_fbo) 762 if (m_fbo)
794 m_gl->DeleteFramebuffers(1, &m_fbo); 763 m_gl->DeleteFramebuffers(1, &m_fbo);
795 764
796 if (m_multisampleRenderbuffer) 765 if (m_multisampleRenderbuffer)
797 m_gl->DeleteRenderbuffers(1, &m_multisampleRenderbuffer); 766 m_gl->DeleteRenderbuffers(1, &m_multisampleRenderbuffer);
798 767
799 if (m_depthStencilBuffer) 768 if (m_depthStencilBuffer)
800 m_gl->DeleteRenderbuffers(1, &m_depthStencilBuffer); 769 m_gl->DeleteRenderbuffers(1, &m_depthStencilBuffer);
801 770
802 if (m_colorBuffer.textureId) {
803 deleteChromiumImageForTexture(&m_colorBuffer);
804 m_gl->DeleteTextures(1, &m_colorBuffer.textureId);
805 }
806
807 m_size = IntSize(); 771 m_size = IntSize();
808 772
809 m_colorBuffer = TextureInfo(); 773 m_backColorBuffer = nullptr;
810 m_frontColorBuffer = FrontBufferInfo(); 774 m_frontColorBuffer = nullptr;
811 m_multisampleRenderbuffer = 0; 775 m_multisampleRenderbuffer = 0;
812 m_depthStencilBuffer = 0; 776 m_depthStencilBuffer = 0;
813 m_multisampleFBO = 0; 777 m_multisampleFBO = 0;
814 m_fbo = 0; 778 m_fbo = 0;
815 779
816 if (m_layer) 780 if (m_layer)
817 GraphicsLayer::unregisterContentsLayer(m_layer->layer()); 781 GraphicsLayer::unregisterContentsLayer(m_layer->layer());
818 } 782 }
819 783
820 GLuint DrawingBuffer::createColorTexture(const TextureParameters& parameters) { 784 GLuint DrawingBuffer::createColorTexture(const TextureParameters& parameters) {
821 GLuint offscreenColorTexture; 785 GLuint offscreenColorTexture;
822 m_gl->GenTextures(1, &offscreenColorTexture); 786 m_gl->GenTextures(1, &offscreenColorTexture);
823 m_gl->BindTexture(parameters.target, offscreenColorTexture); 787 m_gl->BindTexture(parameters.target, offscreenColorTexture);
824 m_gl->TexParameteri(parameters.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 788 m_gl->TexParameteri(parameters.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
825 m_gl->TexParameteri(parameters.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 789 m_gl->TexParameteri(parameters.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
826 m_gl->TexParameteri(parameters.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 790 m_gl->TexParameteri(parameters.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
827 m_gl->TexParameteri(parameters.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 791 m_gl->TexParameteri(parameters.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
828 return offscreenColorTexture; 792 return offscreenColorTexture;
829 } 793 }
830 794
831 bool DrawingBuffer::resizeDefaultFramebuffer(const IntSize& size) { 795 bool DrawingBuffer::resizeDefaultFramebuffer(const IntSize& size) {
832 // Resize or create m_colorBuffer. 796 // Recreate m_backColorBuffer.
833 if (m_colorBuffer.textureId) { 797 m_backColorBuffer = createTextureAndAllocateMemory(size);
834 deleteChromiumImageForTexture(&m_colorBuffer);
835 m_gl->DeleteTextures(1, &m_colorBuffer.textureId);
836 }
837 m_colorBuffer = createTextureAndAllocateMemory(size);
838 798
839 attachColorBufferToReadFramebuffer(); 799 attachColorBufferToReadFramebuffer();
840 800
841 if (wantExplicitResolve()) { 801 if (wantExplicitResolve()) {
842 m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO); 802 m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_multisampleFBO);
843 m_gl->BindRenderbuffer(GL_RENDERBUFFER, m_multisampleRenderbuffer); 803 m_gl->BindRenderbuffer(GL_RENDERBUFFER, m_multisampleRenderbuffer);
844 m_gl->RenderbufferStorageMultisampleCHROMIUM( 804 m_gl->RenderbufferStorageMultisampleCHROMIUM(
845 GL_RENDERBUFFER, m_sampleCount, getMultisampledRenderbufferFormat(), 805 GL_RENDERBUFFER, m_sampleCount, getMultisampledRenderbufferFormat(),
846 size.width(), size.height()); 806 size.width(), size.height());
847 807
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
929 if (!resizeDefaultFramebuffer(adjustedSize)) { 889 if (!resizeDefaultFramebuffer(adjustedSize)) {
930 adjustedSize.scale(s_resourceAdjustedRatio); 890 adjustedSize.scale(s_resourceAdjustedRatio);
931 continue; 891 continue;
932 } 892 }
933 break; 893 break;
934 } while (!adjustedSize.isEmpty()); 894 } while (!adjustedSize.isEmpty());
935 895
936 m_size = adjustedSize; 896 m_size = adjustedSize;
937 // Free all mailboxes, because they are now of the wrong size. Only the 897 // Free all mailboxes, because they are now of the wrong size. Only the
938 // first call in this loop has any effect. 898 // first call in this loop has any effect.
939 freeRecycledMailboxes(); 899 m_recycledMailboxQueue.clear();
900 m_recycledBitmaps.clear();
940 901
941 if (adjustedSize.isEmpty()) 902 if (adjustedSize.isEmpty())
942 return false; 903 return false;
943 } 904 }
944 905
945 m_gl->Disable(GL_SCISSOR_TEST); 906 m_gl->Disable(GL_SCISSOR_TEST);
946 m_gl->ClearColor(0, 0, 0, 907 m_gl->ClearColor(0, 0, 0,
947 defaultBufferRequiresAlphaChannelToBePreserved() ? 1 : 0); 908 defaultBufferRequiresAlphaChannelToBePreserved() ? 1 : 0);
948 m_gl->ColorMask(true, true, true, true); 909 m_gl->ColorMask(true, true, true, true);
949 910
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
1062 dataSize *= width; 1023 dataSize *= width;
1063 dataSize *= height; 1024 dataSize *= height;
1064 if (!dataSize.IsValid()) 1025 if (!dataSize.IsValid())
1065 return false; 1026 return false;
1066 1027
1067 WTF::ArrayBufferContents pixels(width * height, 4, 1028 WTF::ArrayBufferContents pixels(width * height, 4,
1068 WTF::ArrayBufferContents::NotShared, 1029 WTF::ArrayBufferContents::NotShared,
1069 WTF::ArrayBufferContents::DontInitialize); 1030 WTF::ArrayBufferContents::DontInitialize);
1070 1031
1071 GLuint fbo = 0; 1032 GLuint fbo = 0;
1072 if (sourceBuffer == FrontBuffer && m_frontColorBuffer.texInfo.textureId) { 1033 if (sourceBuffer == FrontBuffer && m_frontColorBuffer) {
1073 m_gl->GenFramebuffers(1, &fbo); 1034 m_gl->GenFramebuffers(1, &fbo);
1074 m_gl->BindFramebuffer(GL_FRAMEBUFFER, fbo); 1035 m_gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
1075 m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 1036 m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
1076 m_frontColorBuffer.texInfo.parameters.target, 1037 m_frontColorBuffer->parameters.target,
1077 m_frontColorBuffer.texInfo.textureId, 0); 1038 m_frontColorBuffer->textureId, 0);
1078 } else { 1039 } else {
1079 m_gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer()); 1040 m_gl->BindFramebuffer(GL_FRAMEBUFFER, framebuffer());
1080 } 1041 }
1081 1042
1082 readBackFramebuffer(static_cast<unsigned char*>(pixels.data()), width, height, 1043 readBackFramebuffer(static_cast<unsigned char*>(pixels.data()), width, height,
1083 ReadbackRGBA, WebGLImageConversion::AlphaDoNothing); 1044 ReadbackRGBA, WebGLImageConversion::AlphaDoNothing);
1084 flipVertically(static_cast<uint8_t*>(pixels.data()), width, height); 1045 flipVertically(static_cast<uint8_t*>(pixels.data()), width, height);
1085 1046
1086 if (fbo) { 1047 if (fbo) {
1087 m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 1048 m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
1088 m_frontColorBuffer.texInfo.parameters.target, 0, 1049 m_frontColorBuffer->parameters.target, 0, 0);
1089 0);
1090 m_gl->DeleteFramebuffers(1, &fbo); 1050 m_gl->DeleteFramebuffers(1, &fbo);
1091 } 1051 }
1092 1052
1093 restoreFramebufferBindings(); 1053 restoreFramebufferBindings();
1094 1054
1095 pixels.transfer(contents); 1055 pixels.transfer(contents);
1096 return true; 1056 return true;
1097 } 1057 }
1098 1058
1099 void DrawingBuffer::readBackFramebuffer(unsigned char* pixels, 1059 void DrawingBuffer::readBackFramebuffer(unsigned char* pixels,
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
1163 NOTREACHED(); 1123 NOTREACHED();
1164 } 1124 }
1165 m_gl->TexStorage2DEXT(GL_TEXTURE_2D, 1, internalStorageFormat, width, 1125 m_gl->TexStorage2DEXT(GL_TEXTURE_2D, 1, internalStorageFormat, width,
1166 height); 1126 height);
1167 return; 1127 return;
1168 } 1128 }
1169 m_gl->TexImage2D(target, 0, internalformat, width, height, border, format, 1129 m_gl->TexImage2D(target, 0, internalformat, width, height, border, format,
1170 type, 0); 1130 type, 0);
1171 } 1131 }
1172 1132
1173 void DrawingBuffer::deleteChromiumImageForTexture(TextureInfo* info) { 1133 void DrawingBuffer::clearChromiumImageAlpha(const ColorBuffer& info) {
1174 if (info->imageId) {
1175 m_gl->BindTexture(info->parameters.target, info->textureId);
1176 m_gl->ReleaseTexImage2DCHROMIUM(info->parameters.target, info->imageId);
1177 m_gl->DestroyImageCHROMIUM(info->imageId);
1178 info->imageId = 0;
1179 }
1180 }
1181
1182 void DrawingBuffer::clearChromiumImageAlpha(const TextureInfo& info) {
1183 if (m_wantAlphaChannel) 1134 if (m_wantAlphaChannel)
1184 return; 1135 return;
1185 if (!contextProvider()->getCapabilities().chromium_image_rgb_emulation) 1136 if (!contextProvider()->getCapabilities().chromium_image_rgb_emulation)
1186 return; 1137 return;
1187 1138
1188 GLuint fbo = 0; 1139 GLuint fbo = 0;
1189 m_gl->GenFramebuffers(1, &fbo); 1140 m_gl->GenFramebuffers(1, &fbo);
1190 m_gl->BindFramebuffer(GL_FRAMEBUFFER, fbo); 1141 m_gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
1191 m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 1142 m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
1192 info.parameters.target, info.textureId, 0); 1143 info.parameters.target, info.textureId, 0);
1193 m_gl->ClearColor(0, 0, 0, 1); 1144 m_gl->ClearColor(0, 0, 0, 1);
1194 m_gl->ColorMask(false, false, false, true); 1145 m_gl->ColorMask(false, false, false, true);
1195 m_gl->Clear(GL_COLOR_BUFFER_BIT); 1146 m_gl->Clear(GL_COLOR_BUFFER_BIT);
1196 m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 1147 m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
1197 info.parameters.target, 0, 0); 1148 info.parameters.target, 0, 0);
1198 m_gl->DeleteFramebuffers(1, &fbo); 1149 m_gl->DeleteFramebuffers(1, &fbo);
1199 restoreFramebufferBindings(); 1150 restoreFramebufferBindings();
1200 m_gl->ClearColor(m_clearColor[0], m_clearColor[1], m_clearColor[2], 1151 m_gl->ClearColor(m_clearColor[0], m_clearColor[1], m_clearColor[2],
1201 m_clearColor[3]); 1152 m_clearColor[3]);
1202 m_gl->ColorMask(m_colorMask[0], m_colorMask[1], m_colorMask[2], 1153 m_gl->ColorMask(m_colorMask[0], m_colorMask[1], m_colorMask[2],
1203 m_colorMask[3]); 1154 m_colorMask[3]);
1204 } 1155 }
1205 1156
1206 DrawingBuffer::TextureInfo DrawingBuffer::createTextureAndAllocateMemory( 1157 RefPtr<DrawingBuffer::ColorBuffer>
1207 const IntSize& size) { 1158 DrawingBuffer::createTextureAndAllocateMemory(const IntSize& size) {
1208 if (!shouldUseChromiumImage()) 1159 if (!shouldUseChromiumImage())
1209 return createDefaultTextureAndAllocateMemory(size); 1160 return createDefaultTextureAndAllocateMemory(size);
1210 1161
1211 TextureParameters parameters = chromiumImageTextureParameters(); 1162 TextureParameters parameters = chromiumImageTextureParameters();
1212 GLuint imageId = m_gl->CreateGpuMemoryBufferImageCHROMIUM( 1163 GLuint imageId = m_gl->CreateGpuMemoryBufferImageCHROMIUM(
1213 size.width(), size.height(), parameters.creationInternalColorFormat, 1164 size.width(), size.height(), parameters.creationInternalColorFormat,
1214 GC3D_SCANOUT_CHROMIUM); 1165 GC3D_SCANOUT_CHROMIUM);
1215 GLuint textureId = createColorTexture(parameters); 1166 GLuint textureId = createColorTexture(parameters);
1216 if (imageId) { 1167 if (imageId) {
1217 m_gl->BindTexImage2DCHROMIUM(parameters.target, imageId); 1168 m_gl->BindTexImage2DCHROMIUM(parameters.target, imageId);
1218 } 1169 }
1219 1170
1220 TextureInfo info; 1171 RefPtr<ColorBuffer> info(adoptRef(new ColorBuffer(this, parameters, size)));
1221 info.textureId = textureId; 1172 info->textureId = textureId;
1222 info.imageId = imageId; 1173 info->imageId = imageId;
1223 info.parameters = parameters; 1174 clearChromiumImageAlpha(*info);
1224 clearChromiumImageAlpha(info);
1225 return info; 1175 return info;
1226 } 1176 }
1227 1177
1228 DrawingBuffer::TextureInfo DrawingBuffer::createDefaultTextureAndAllocateMemory( 1178 RefPtr<DrawingBuffer::ColorBuffer>
1229 const IntSize& size) { 1179 DrawingBuffer::createDefaultTextureAndAllocateMemory(const IntSize& size) {
1230 DrawingBuffer::TextureInfo info;
1231 TextureParameters parameters = defaultTextureParameters(); 1180 TextureParameters parameters = defaultTextureParameters();
1232 info.parameters = parameters; 1181 RefPtr<ColorBuffer> info(adoptRef(new ColorBuffer(this, parameters, size)));
1233 info.textureId = createColorTexture(parameters); 1182 info->textureId = createColorTexture(parameters);
1234 allocateConditionallyImmutableTexture( 1183 allocateConditionallyImmutableTexture(
1235 parameters.target, parameters.creationInternalColorFormat, size.width(), 1184 parameters.target, parameters.creationInternalColorFormat, size.width(),
1236 size.height(), 0, parameters.colorFormat, GL_UNSIGNED_BYTE); 1185 size.height(), 0, parameters.colorFormat, GL_UNSIGNED_BYTE);
1237 return info; 1186 return info;
1238 } 1187 }
1239 1188
1240 void DrawingBuffer::attachColorBufferToReadFramebuffer() { 1189 void DrawingBuffer::attachColorBufferToReadFramebuffer() {
1241 m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fbo); 1190 m_gl->BindFramebuffer(GL_FRAMEBUFFER, m_fbo);
1242 1191
1243 GLenum target = m_colorBuffer.parameters.target; 1192 GLenum target = m_backColorBuffer->parameters.target;
1244 GLenum id = m_colorBuffer.textureId; 1193 GLenum id = m_backColorBuffer->textureId;
1245 1194
1246 m_gl->BindTexture(target, id); 1195 m_gl->BindTexture(target, id);
1247 1196
1248 if (m_antiAliasingMode == MSAAImplicitResolve) 1197 if (m_antiAliasingMode == MSAAImplicitResolve)
1249 m_gl->FramebufferTexture2DMultisampleEXT( 1198 m_gl->FramebufferTexture2DMultisampleEXT(
1250 GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, id, 0, m_sampleCount); 1199 GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, id, 0, m_sampleCount);
1251 else 1200 else
1252 m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, id, 1201 m_gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, id,
1253 0); 1202 0);
1254 1203
(...skipping 29 matching lines...) Expand all
1284 // the public interface for WebGL does not support GL_TEXTURE_RECTANGLE. 1233 // the public interface for WebGL does not support GL_TEXTURE_RECTANGLE.
1285 m_gl->BindTexture(GL_TEXTURE_2D, m_texture2DBinding); 1234 m_gl->BindTexture(GL_TEXTURE_2D, m_texture2DBinding);
1286 } 1235 }
1287 1236
1288 bool DrawingBuffer::shouldUseChromiumImage() { 1237 bool DrawingBuffer::shouldUseChromiumImage() {
1289 return RuntimeEnabledFeatures::webGLImageChromiumEnabled() && 1238 return RuntimeEnabledFeatures::webGLImageChromiumEnabled() &&
1290 m_chromiumImageUsage == AllowChromiumImage; 1239 m_chromiumImageUsage == AllowChromiumImage;
1291 } 1240 }
1292 1241
1293 } // namespace blink 1242 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/platform/graphics/gpu/DrawingBuffer.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698