1/* 2 * Copyright (C) 2013 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31#include "config.h" 32 33#include "platform/graphics/gpu/DrawingBuffer.h" 34 35#include "platform/RuntimeEnabledFeatures.h" 36#include "platform/graphics/ImageBuffer.h" 37#include "platform/graphics/UnacceleratedImageBufferSurface.h" 38#include "platform/graphics/gpu/Extensions3DUtil.h" 39#include "platform/graphics/test/MockWebGraphicsContext3D.h" 40#include "public/platform/Platform.h" 41#include "public/platform/WebExternalTextureMailbox.h" 42#include "wtf/RefPtr.h" 43 44#include <gmock/gmock.h> 45#include <gtest/gtest.h> 46 47using namespace WebCore; 48using namespace blink; 49using testing::Test; 50using testing::_; 51 52namespace { 53 54class FakeContextEvictionManager : public ContextEvictionManager { 55public: 56 void forciblyLoseOldestContext(const String& reason) { } 57 IntSize oldestContextSize() { return IntSize(); } 58}; 59 60class WebGraphicsContext3DForTests : public MockWebGraphicsContext3D { 61public: 62 WebGraphicsContext3DForTests() 63 : MockWebGraphicsContext3D() 64 , m_boundTexture(0) 65 , m_currentMailboxByte(0) 66 , m_mostRecentlyWaitedSyncPoint(0) 67 , m_currentImageId(1) { } 68 69 virtual void bindTexture(WGC3Denum target, WebGLId texture) 70 { 71 if (target == GL_TEXTURE_2D) { 72 m_boundTexture = texture; 73 } 74 } 75 76 virtual void texImage2D(WGC3Denum target, WGC3Dint level, WGC3Denum internalformat, WGC3Dsizei width, WGC3Dsizei height, WGC3Dint border, WGC3Denum format, WGC3Denum type, const void* pixels) 77 { 78 if (target == GL_TEXTURE_2D && !level) { 79 m_textureSizes.set(m_boundTexture, IntSize(width, height)); 80 } 81 } 82 83 virtual void genMailboxCHROMIUM(WGC3Dbyte* mailbox) 84 { 85 ++m_currentMailboxByte; 86 WebExternalTextureMailbox temp; 87 memset(mailbox, m_currentMailboxByte, sizeof(temp.name)); 88 } 89 90 virtual void produceTextureCHROMIUM(WGC3Denum target, const WGC3Dbyte* mailbox) 91 { 92 ASSERT_EQ(target, static_cast<WGC3Denum>(GL_TEXTURE_2D)); 93 m_mostRecentlyProducedSize = m_textureSizes.get(m_boundTexture); 94 } 95 96 IntSize mostRecentlyProducedSize() 97 { 98 return m_mostRecentlyProducedSize; 99 } 100 101 virtual unsigned insertSyncPoint() 102 { 103 static unsigned syncPointGenerator = 0; 104 return ++syncPointGenerator; 105 } 106 107 virtual void waitSyncPoint(unsigned syncPoint) 108 { 109 m_mostRecentlyWaitedSyncPoint = syncPoint; 110 } 111 112 virtual WGC3Duint createImageCHROMIUM(WGC3Dsizei width, WGC3Dsizei height, WGC3Denum internalformat, WGC3Denum usage) 113 { 114 m_imageSizes.set(m_currentImageId, IntSize(width, height)); 115 return m_currentImageId++; 116 } 117 118 MOCK_METHOD1(destroyImageMock, void(WGC3Duint imageId)); 119 void destroyImageCHROMIUM(WGC3Duint imageId) 120 { 121 m_imageSizes.remove(imageId); 122 // No textures should be bound to this. 123 ASSERT(m_imageToTextureMap.find(imageId) == m_imageToTextureMap.end()); 124 m_imageSizes.remove(imageId); 125 destroyImageMock(imageId); 126 } 127 128 MOCK_METHOD1(bindTexImage2DMock, void(WGC3Dint imageId)); 129 void bindTexImage2DCHROMIUM(WGC3Denum target, WGC3Dint imageId) 130 { 131 if (target == GL_TEXTURE_2D) { 132 m_textureSizes.set(m_boundTexture, m_imageSizes.find(imageId)->value); 133 m_imageToTextureMap.set(imageId, m_boundTexture); 134 bindTexImage2DMock(imageId); 135 } 136 } 137 138 MOCK_METHOD1(releaseTexImage2DMock, void(WGC3Dint imageId)); 139 void releaseTexImage2DCHROMIUM(WGC3Denum target, WGC3Dint imageId) 140 { 141 if (target == GL_TEXTURE_2D) { 142 m_imageSizes.set(m_currentImageId, IntSize()); 143 m_imageToTextureMap.remove(imageId); 144 releaseTexImage2DMock(imageId); 145 } 146 } 147 148 unsigned mostRecentlyWaitedSyncPoint() 149 { 150 return m_mostRecentlyWaitedSyncPoint; 151 } 152 153 WGC3Duint nextImageIdToBeCreated() 154 { 155 return m_currentImageId; 156 } 157 158private: 159 WebGLId m_boundTexture; 160 HashMap<WebGLId, IntSize> m_textureSizes; 161 WGC3Dbyte m_currentMailboxByte; 162 IntSize m_mostRecentlyProducedSize; 163 unsigned m_mostRecentlyWaitedSyncPoint; 164 WGC3Duint m_currentImageId; 165 HashMap<WGC3Duint, IntSize> m_imageSizes; 166 HashMap<WGC3Duint, WebGLId> m_imageToTextureMap; 167}; 168 169static const int initialWidth = 100; 170static const int initialHeight = 100; 171static const int alternateHeight = 50; 172 173class DrawingBufferForTests : public DrawingBuffer { 174public: 175 static PassRefPtr<DrawingBufferForTests> create(PassOwnPtr<blink::WebGraphicsContext3D> context, 176 const IntSize& size, PreserveDrawingBuffer preserve, PassRefPtr<ContextEvictionManager> contextEvictionManager) 177 { 178 OwnPtr<Extensions3DUtil> extensionsUtil = Extensions3DUtil::create(context.get()); 179 RefPtr<DrawingBufferForTests> drawingBuffer = 180 adoptRef(new DrawingBufferForTests(context, extensionsUtil.release(), preserve, contextEvictionManager)); 181 if (!drawingBuffer->initialize(size)) { 182 drawingBuffer->beginDestruction(); 183 return PassRefPtr<DrawingBufferForTests>(); 184 } 185 return drawingBuffer.release(); 186 } 187 188 DrawingBufferForTests(PassOwnPtr<blink::WebGraphicsContext3D> context, 189 PassOwnPtr<Extensions3DUtil> extensionsUtil, 190 PreserveDrawingBuffer preserve, 191 PassRefPtr<ContextEvictionManager> contextEvictionManager) 192 : DrawingBuffer(context, extensionsUtil, false /* multisampleExtensionSupported */, 193 false /* packedDepthStencilExtensionSupported */, preserve, blink::WebGraphicsContext3D::Attributes(), contextEvictionManager) 194 , m_live(0) 195 { } 196 197 virtual ~DrawingBufferForTests() 198 { 199 if (m_live) 200 *m_live = false; 201 } 202 203 bool* m_live; 204}; 205 206class DrawingBufferTest : public Test { 207protected: 208 virtual void SetUp() 209 { 210 RefPtr<FakeContextEvictionManager> contextEvictionManager = adoptRef(new FakeContextEvictionManager()); 211 OwnPtr<WebGraphicsContext3DForTests> context = adoptPtr(new WebGraphicsContext3DForTests); 212 m_context = context.get(); 213 m_drawingBuffer = DrawingBufferForTests::create(context.release(), 214 IntSize(initialWidth, initialHeight), DrawingBuffer::Preserve, contextEvictionManager.release()); 215 } 216 217 WebGraphicsContext3DForTests* webContext() 218 { 219 return m_context; 220 } 221 222 WebGraphicsContext3DForTests* m_context; 223 RefPtr<DrawingBufferForTests> m_drawingBuffer; 224}; 225 226TEST_F(DrawingBufferTest, testPaintRenderingResultsToCanvas) 227{ 228 OwnPtr<ImageBufferSurface> imageBufferSurface = adoptPtr(new UnacceleratedImageBufferSurface(IntSize(initialWidth, initialHeight))); 229 EXPECT_FALSE(!imageBufferSurface); 230 EXPECT_TRUE(imageBufferSurface->isValid()); 231 OwnPtr<ImageBuffer> imageBuffer = ImageBuffer::create(imageBufferSurface.release()); 232 EXPECT_FALSE(!imageBuffer); 233 EXPECT_FALSE(imageBuffer->isAccelerated()); 234 EXPECT_FALSE(imageBuffer->bitmap().isNull()); 235 m_drawingBuffer->paintRenderingResultsToCanvas(imageBuffer.get()); 236 EXPECT_FALSE(imageBuffer->isAccelerated()); 237 EXPECT_FALSE(imageBuffer->bitmap().isNull()); 238 m_drawingBuffer->beginDestruction(); 239} 240 241TEST_F(DrawingBufferTest, verifyResizingProperlyAffectsMailboxes) 242{ 243 blink::WebExternalTextureMailbox mailbox; 244 245 IntSize initialSize(initialWidth, initialHeight); 246 IntSize alternateSize(initialWidth, alternateHeight); 247 248 // Produce one mailbox at size 100x100. 249 m_drawingBuffer->markContentsChanged(); 250 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); 251 EXPECT_EQ(initialSize, webContext()->mostRecentlyProducedSize()); 252 253 // Resize to 100x50. 254 m_drawingBuffer->reset(IntSize(initialWidth, alternateHeight)); 255 m_drawingBuffer->mailboxReleased(mailbox); 256 257 // Produce a mailbox at this size. 258 m_drawingBuffer->markContentsChanged(); 259 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); 260 EXPECT_EQ(alternateSize, webContext()->mostRecentlyProducedSize()); 261 262 // Reset to initial size. 263 m_drawingBuffer->reset(IntSize(initialWidth, initialHeight)); 264 m_drawingBuffer->mailboxReleased(mailbox); 265 266 // Prepare another mailbox and verify that it's the correct size. 267 m_drawingBuffer->markContentsChanged(); 268 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); 269 EXPECT_EQ(initialSize, webContext()->mostRecentlyProducedSize()); 270 271 // Prepare one final mailbox and verify that it's the correct size. 272 m_drawingBuffer->mailboxReleased(mailbox); 273 m_drawingBuffer->markContentsChanged(); 274 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); 275 EXPECT_EQ(initialSize, webContext()->mostRecentlyProducedSize()); 276 m_drawingBuffer->beginDestruction(); 277} 278 279TEST_F(DrawingBufferTest, verifyDestructionCompleteAfterAllMailboxesReleased) 280{ 281 bool live = true; 282 m_drawingBuffer->m_live = &live; 283 284 blink::WebExternalTextureMailbox mailbox1; 285 blink::WebExternalTextureMailbox mailbox2; 286 blink::WebExternalTextureMailbox mailbox3; 287 288 IntSize initialSize(initialWidth, initialHeight); 289 290 // Produce mailboxes. 291 m_drawingBuffer->markContentsChanged(); 292 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox1, 0)); 293 m_drawingBuffer->markContentsChanged(); 294 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox2, 0)); 295 m_drawingBuffer->markContentsChanged(); 296 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox3, 0)); 297 298 m_drawingBuffer->markContentsChanged(); 299 m_drawingBuffer->mailboxReleased(mailbox1); 300 301 m_drawingBuffer->beginDestruction(); 302 EXPECT_EQ(live, true); 303 304 DrawingBufferForTests* weakPointer = m_drawingBuffer.get(); 305 m_drawingBuffer.clear(); 306 EXPECT_EQ(live, true); 307 308 weakPointer->markContentsChanged(); 309 weakPointer->mailboxReleased(mailbox2); 310 EXPECT_EQ(live, true); 311 312 weakPointer->markContentsChanged(); 313 weakPointer->mailboxReleased(mailbox3); 314 EXPECT_EQ(live, false); 315} 316 317class TextureMailboxWrapper { 318public: 319 explicit TextureMailboxWrapper(const blink::WebExternalTextureMailbox& mailbox) 320 : m_mailbox(mailbox) 321 { } 322 323 bool operator==(const TextureMailboxWrapper& other) const 324 { 325 return !memcmp(m_mailbox.name, other.m_mailbox.name, sizeof(m_mailbox.name)); 326 } 327 328private: 329 blink::WebExternalTextureMailbox m_mailbox; 330}; 331 332TEST_F(DrawingBufferTest, verifyRecyclingMailboxesByFIFO) 333{ 334 blink::WebExternalTextureMailbox mailbox1; 335 blink::WebExternalTextureMailbox mailbox2; 336 blink::WebExternalTextureMailbox mailbox3; 337 338 // Produce mailboxes. 339 m_drawingBuffer->markContentsChanged(); 340 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox1, 0)); 341 m_drawingBuffer->markContentsChanged(); 342 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox2, 0)); 343 m_drawingBuffer->markContentsChanged(); 344 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox3, 0)); 345 346 // Release mailboxes by specific order; 2, 3, 1. 347 m_drawingBuffer->markContentsChanged(); 348 m_drawingBuffer->mailboxReleased(mailbox2); 349 m_drawingBuffer->markContentsChanged(); 350 m_drawingBuffer->mailboxReleased(mailbox3); 351 m_drawingBuffer->markContentsChanged(); 352 m_drawingBuffer->mailboxReleased(mailbox1); 353 354 // The first recycled mailbox must be 2. 355 blink::WebExternalTextureMailbox recycledMailbox; 356 m_drawingBuffer->markContentsChanged(); 357 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&recycledMailbox, 0)); 358 EXPECT_EQ(TextureMailboxWrapper(mailbox2), TextureMailboxWrapper(recycledMailbox)); 359 360 // The second recycled mailbox must be 3. 361 m_drawingBuffer->markContentsChanged(); 362 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&recycledMailbox, 0)); 363 EXPECT_EQ(TextureMailboxWrapper(mailbox3), TextureMailboxWrapper(recycledMailbox)); 364 365 // The third recycled mailbox must be 1. 366 m_drawingBuffer->markContentsChanged(); 367 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&recycledMailbox, 0)); 368 EXPECT_EQ(TextureMailboxWrapper(mailbox1), TextureMailboxWrapper(recycledMailbox)); 369 370 m_drawingBuffer->mailboxReleased(mailbox1); 371 m_drawingBuffer->mailboxReleased(mailbox2); 372 m_drawingBuffer->mailboxReleased(mailbox3); 373 m_drawingBuffer->beginDestruction(); 374} 375 376TEST_F(DrawingBufferTest, verifyInsertAndWaitSyncPointCorrectly) 377{ 378 blink::WebExternalTextureMailbox mailbox; 379 380 // Produce mailboxes. 381 m_drawingBuffer->markContentsChanged(); 382 EXPECT_EQ(0u, webContext()->mostRecentlyWaitedSyncPoint()); 383 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); 384 // prepareMailbox() does not wait for any sync point. 385 EXPECT_EQ(0u, webContext()->mostRecentlyWaitedSyncPoint()); 386 387 unsigned waitSyncPoint = webContext()->insertSyncPoint(); 388 mailbox.syncPoint = waitSyncPoint; 389 m_drawingBuffer->mailboxReleased(mailbox); 390 // m_drawingBuffer will wait for the sync point when recycling. 391 EXPECT_EQ(0u, webContext()->mostRecentlyWaitedSyncPoint()); 392 393 m_drawingBuffer->markContentsChanged(); 394 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); 395 // m_drawingBuffer waits for the sync point when recycling in prepareMailbox(). 396 EXPECT_EQ(waitSyncPoint, webContext()->mostRecentlyWaitedSyncPoint()); 397 398 m_drawingBuffer->beginDestruction(); 399 waitSyncPoint = webContext()->insertSyncPoint(); 400 mailbox.syncPoint = waitSyncPoint; 401 m_drawingBuffer->mailboxReleased(mailbox); 402 // m_drawingBuffer waits for the sync point because the destruction is in progress. 403 EXPECT_EQ(waitSyncPoint, webContext()->mostRecentlyWaitedSyncPoint()); 404} 405 406class DrawingBufferImageChromiumTest : public DrawingBufferTest { 407protected: 408 virtual void SetUp() 409 { 410 RefPtr<FakeContextEvictionManager> contextEvictionManager = adoptRef(new FakeContextEvictionManager()); 411 OwnPtr<WebGraphicsContext3DForTests> context = adoptPtr(new WebGraphicsContext3DForTests); 412 m_context = context.get(); 413 RuntimeEnabledFeatures::setWebGLImageChromiumEnabled(true); 414 m_imageId0 = webContext()->nextImageIdToBeCreated(); 415 EXPECT_CALL(*webContext(), bindTexImage2DMock(m_imageId0)).Times(1); 416 m_drawingBuffer = DrawingBufferForTests::create(context.release(), 417 IntSize(initialWidth, initialHeight), DrawingBuffer::Preserve, contextEvictionManager.release()); 418 testing::Mock::VerifyAndClearExpectations(webContext()); 419 } 420 421 virtual void TearDown() 422 { 423 RuntimeEnabledFeatures::setWebGLImageChromiumEnabled(false); 424 } 425 WGC3Duint m_imageId0; 426}; 427 428TEST_F(DrawingBufferImageChromiumTest, verifyResizingReallocatesImages) 429{ 430 blink::WebExternalTextureMailbox mailbox; 431 432 IntSize initialSize(initialWidth, initialHeight); 433 IntSize alternateSize(initialWidth, alternateHeight); 434 435 WGC3Duint m_imageId1 = webContext()->nextImageIdToBeCreated(); 436 EXPECT_CALL(*webContext(), bindTexImage2DMock(m_imageId1)).Times(1); 437 // Produce one mailbox at size 100x100. 438 m_drawingBuffer->markContentsChanged(); 439 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); 440 EXPECT_EQ(initialSize, webContext()->mostRecentlyProducedSize()); 441 EXPECT_TRUE(mailbox.allowOverlay); 442 testing::Mock::VerifyAndClearExpectations(webContext()); 443 444 WGC3Duint m_imageId2 = webContext()->nextImageIdToBeCreated(); 445 EXPECT_CALL(*webContext(), bindTexImage2DMock(m_imageId2)).Times(1); 446 EXPECT_CALL(*webContext(), destroyImageMock(m_imageId0)).Times(1); 447 EXPECT_CALL(*webContext(), releaseTexImage2DMock(m_imageId0)).Times(1); 448 // Resize to 100x50. 449 m_drawingBuffer->reset(IntSize(initialWidth, alternateHeight)); 450 m_drawingBuffer->mailboxReleased(mailbox); 451 testing::Mock::VerifyAndClearExpectations(webContext()); 452 453 WGC3Duint m_imageId3 = webContext()->nextImageIdToBeCreated(); 454 EXPECT_CALL(*webContext(), bindTexImage2DMock(m_imageId3)).Times(1); 455 EXPECT_CALL(*webContext(), destroyImageMock(m_imageId1)).Times(1); 456 EXPECT_CALL(*webContext(), releaseTexImage2DMock(m_imageId1)).Times(1); 457 // Produce a mailbox at this size. 458 m_drawingBuffer->markContentsChanged(); 459 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); 460 EXPECT_EQ(alternateSize, webContext()->mostRecentlyProducedSize()); 461 EXPECT_TRUE(mailbox.allowOverlay); 462 testing::Mock::VerifyAndClearExpectations(webContext()); 463 464 WGC3Duint m_imageId4 = webContext()->nextImageIdToBeCreated(); 465 EXPECT_CALL(*webContext(), bindTexImage2DMock(m_imageId4)).Times(1); 466 EXPECT_CALL(*webContext(), destroyImageMock(m_imageId2)).Times(1); 467 EXPECT_CALL(*webContext(), releaseTexImage2DMock(m_imageId2)).Times(1); 468 // Reset to initial size. 469 m_drawingBuffer->reset(IntSize(initialWidth, initialHeight)); 470 m_drawingBuffer->mailboxReleased(mailbox); 471 testing::Mock::VerifyAndClearExpectations(webContext()); 472 473 WGC3Duint m_imageId5 = webContext()->nextImageIdToBeCreated(); 474 EXPECT_CALL(*webContext(), bindTexImage2DMock(m_imageId5)).Times(1); 475 EXPECT_CALL(*webContext(), destroyImageMock(m_imageId3)).Times(1); 476 EXPECT_CALL(*webContext(), releaseTexImage2DMock(m_imageId3)).Times(1); 477 // Prepare another mailbox and verify that it's the correct size. 478 m_drawingBuffer->markContentsChanged(); 479 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); 480 EXPECT_EQ(initialSize, webContext()->mostRecentlyProducedSize()); 481 EXPECT_TRUE(mailbox.allowOverlay); 482 testing::Mock::VerifyAndClearExpectations(webContext()); 483 484 // Prepare one final mailbox and verify that it's the correct size. 485 m_drawingBuffer->mailboxReleased(mailbox); 486 m_drawingBuffer->markContentsChanged(); 487 EXPECT_TRUE(m_drawingBuffer->prepareMailbox(&mailbox, 0)); 488 EXPECT_EQ(initialSize, webContext()->mostRecentlyProducedSize()); 489 EXPECT_TRUE(mailbox.allowOverlay); 490 m_drawingBuffer->mailboxReleased(mailbox); 491 492 EXPECT_CALL(*webContext(), destroyImageMock(m_imageId5)).Times(1); 493 EXPECT_CALL(*webContext(), releaseTexImage2DMock(m_imageId5)).Times(1); 494 EXPECT_CALL(*webContext(), destroyImageMock(m_imageId4)).Times(1); 495 EXPECT_CALL(*webContext(), releaseTexImage2DMock(m_imageId4)).Times(1); 496 m_drawingBuffer->beginDestruction(); 497 testing::Mock::VerifyAndClearExpectations(webContext()); 498} 499 500class DepthStencilTrackingContext : public MockWebGraphicsContext3D { 501public: 502 DepthStencilTrackingContext() 503 : m_nextRenderBufferId(1) 504 , m_stencilAttachment(0) 505 , m_depthAttachment(0) { } 506 virtual ~DepthStencilTrackingContext() { } 507 508 int numAllocatedRenderBuffer() const { return m_nextRenderBufferId - 1; } 509 WebGLId stencilAttachment() const { return m_stencilAttachment; } 510 WebGLId depthAttachment() const { return m_depthAttachment; } 511 512 virtual WebString getString(WGC3Denum type) OVERRIDE 513 { 514 if (type == GL_EXTENSIONS) { 515 return WebString::fromUTF8("GL_OES_packed_depth_stencil"); 516 } 517 return WebString(); 518 } 519 520 virtual WebGLId createRenderbuffer() OVERRIDE 521 { 522 return ++m_nextRenderBufferId; 523 } 524 525 virtual void framebufferRenderbuffer(WGC3Denum target, WGC3Denum attachment, WGC3Denum renderbuffertarget, WebGLId renderbuffer) OVERRIDE 526 { 527 if (attachment == GL_STENCIL_ATTACHMENT) { 528 m_stencilAttachment = renderbuffer; 529 } else { 530 m_depthAttachment = renderbuffer; 531 } 532 } 533 534 virtual void getIntegerv(WGC3Denum ptype, WGC3Dint* value) OVERRIDE 535 { 536 switch (ptype) { 537 case GL_DEPTH_BITS: 538 *value = m_depthAttachment ? 24 : 0; 539 return; 540 case GL_STENCIL_BITS: 541 *value = m_stencilAttachment ? 8 : 0; 542 return; 543 } 544 MockWebGraphicsContext3D::getIntegerv(ptype, value); 545 } 546 547private: 548 WebGLId m_nextRenderBufferId; 549 WebGLId m_stencilAttachment; 550 WebGLId m_depthAttachment; 551}; 552 553struct DepthStencilTestCase { 554 DepthStencilTestCase(bool requestStencil, bool requestDepth, int expectedRenderBuffers, bool expectDepthStencil, const char* const testCaseName) 555 : requestStencil(requestStencil) 556 , requestDepth(requestDepth) 557 , expectDepthStencil(expectDepthStencil) 558 , expectedRenderBuffers(expectedRenderBuffers) 559 , testCaseName(testCaseName) { } 560 561 bool requestStencil; 562 bool requestDepth; 563 bool expectDepthStencil; 564 int expectedRenderBuffers; 565 const char* const testCaseName; 566}; 567 568// This tests that when the packed depth+stencil extension is supported DrawingBuffer always allocates 569// a single packed renderbuffer if either is requested and properly computes the actual context attributes 570// as defined by WebGL. We always allocate a packed buffer in this case since many desktop OpenGL drivers 571// that support this extension do not consider a framebuffer with only a depth or a stencil buffer attached 572// to be complete. 573TEST(DrawingBufferDepthStencilTest, packedDepthStencilSupported) 574{ 575 DepthStencilTestCase cases[] = { 576 DepthStencilTestCase(false, false, false, 0, "neither"), 577 DepthStencilTestCase(true, false, true, 1, "stencil only"), 578 DepthStencilTestCase(false, true, true, 1, "depth only"), 579 DepthStencilTestCase(true, true, true, 1, "both"), 580 }; 581 582 for (size_t i = 0; i < arraysize(cases); i++) { 583 SCOPED_TRACE(cases[i].testCaseName); 584 OwnPtr<DepthStencilTrackingContext> context = adoptPtr(new DepthStencilTrackingContext); 585 DepthStencilTrackingContext* trackingContext = context.get(); 586 DrawingBuffer::PreserveDrawingBuffer preserve = DrawingBuffer::Preserve; 587 RefPtr<ContextEvictionManager> contextEvictionManager = adoptRef(new FakeContextEvictionManager); 588 589 blink::WebGraphicsContext3D::Attributes requestedAttributes; 590 requestedAttributes.stencil = cases[i].requestStencil; 591 requestedAttributes.depth = cases[i].requestDepth; 592 RefPtr<DrawingBuffer> drawingBuffer = DrawingBuffer::create(context.release(), IntSize(10, 10), preserve, requestedAttributes, contextEvictionManager); 593 594 EXPECT_EQ(cases[i].requestDepth, drawingBuffer->getActualAttributes().depth); 595 EXPECT_EQ(cases[i].requestStencil, drawingBuffer->getActualAttributes().stencil); 596 EXPECT_EQ(cases[i].expectedRenderBuffers, trackingContext->numAllocatedRenderBuffer()); 597 if (cases[i].expectDepthStencil) { 598 EXPECT_EQ(trackingContext->stencilAttachment(), trackingContext->depthAttachment()); 599 } else if (cases[i].requestStencil || cases[i].requestDepth) { 600 EXPECT_NE(trackingContext->stencilAttachment(), trackingContext->depthAttachment()); 601 } else { 602 EXPECT_EQ(0u, trackingContext->stencilAttachment()); 603 EXPECT_EQ(0u, trackingContext->depthAttachment()); 604 } 605 606 drawingBuffer->reset(IntSize(10, 20)); 607 EXPECT_EQ(cases[i].requestDepth, drawingBuffer->getActualAttributes().depth); 608 EXPECT_EQ(cases[i].requestStencil, drawingBuffer->getActualAttributes().stencil); 609 EXPECT_EQ(cases[i].expectedRenderBuffers, trackingContext->numAllocatedRenderBuffer()); 610 if (cases[i].expectDepthStencil) { 611 EXPECT_EQ(trackingContext->stencilAttachment(), trackingContext->depthAttachment()); 612 } else if (cases[i].requestStencil || cases[i].requestDepth) { 613 EXPECT_NE(trackingContext->stencilAttachment(), trackingContext->depthAttachment()); 614 } else { 615 EXPECT_EQ(0u, trackingContext->stencilAttachment()); 616 EXPECT_EQ(0u, trackingContext->depthAttachment()); 617 } 618 619 drawingBuffer->beginDestruction(); 620 } 621} 622 623} // namespace 624