1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "cc/resource_provider.h"
8 #include "base/logging.h"
9 #include "cc/output_surface.h"
10 #include "cc/scoped_ptr_deque.h"
11 #include "cc/scoped_ptr_hash_map.h"
12 #include "cc/test/fake_output_surface.h"
13 #include "cc/test/test_web_graphics_context_3d.h"
14 #include "gpu/GLES2/gl2extchromium.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "third_party/WebKit/Source/Platform/chromium/public/WebGraphicsContext3D.h"
18 #include "third_party/khronos/GLES2/gl2.h"
19 #include "third_party/khronos/GLES2/gl2ext.h"
20 #include "ui/gfx/rect.h"
22 using namespace WebKit
;
25 using testing::NiceMock
;
26 using testing::Return
;
27 using testing::StrictMock
;
33 size_t textureSize(const gfx::Size
& size
, WGC3Denum format
)
35 unsigned int componentsPerPixel
= 4;
36 unsigned int bytesPerComponent
= 1;
37 return size
.width() * size
.height() * componentsPerPixel
* bytesPerComponent
;
41 Texture(const gfx::Size
& size
, WGC3Denum format
)
44 , data(new uint8_t[textureSize(size
, format
)])
50 scoped_array
<uint8_t> data
;
53 // Shared data between multiple ResourceProviderContext. This contains mailbox
54 // contents as well as information about sync points.
55 class ContextSharedData
{
57 static scoped_ptr
<ContextSharedData
> create() { return make_scoped_ptr(new ContextSharedData()); }
59 unsigned insertSyncPoint() { return m_nextSyncPoint
++; }
61 void genMailbox(WGC3Dbyte
* mailbox
)
63 memset(mailbox
, 0, sizeof(WGC3Dbyte
[64]));
64 memcpy(mailbox
, &m_nextMailBox
, sizeof(m_nextMailBox
));
68 void produceTexture(const WGC3Dbyte
* mailboxName
, unsigned syncPoint
, scoped_ptr
<Texture
> texture
)
71 memcpy(&mailbox
, mailboxName
, sizeof(mailbox
));
72 ASSERT_TRUE(mailbox
&& mailbox
< m_nextMailBox
);
73 m_textures
.set(mailbox
, texture
.Pass());
74 ASSERT_LT(m_syncPointForMailbox
[mailbox
], syncPoint
);
75 m_syncPointForMailbox
[mailbox
] = syncPoint
;
78 scoped_ptr
<Texture
> consumeTexture(const WGC3Dbyte
* mailboxName
, unsigned syncPoint
)
81 memcpy(&mailbox
, mailboxName
, sizeof(mailbox
));
82 DCHECK(mailbox
&& mailbox
< m_nextMailBox
);
84 // If the latest sync point the context has waited on is before the sync
85 // point for when the mailbox was set, pretend we never saw that
87 if (m_syncPointForMailbox
[mailbox
] > syncPoint
) {
89 return scoped_ptr
<Texture
>();
91 return m_textures
.take(mailbox
);
100 unsigned m_nextSyncPoint
;
101 unsigned m_nextMailBox
;
102 typedef ScopedPtrHashMap
<unsigned, Texture
> TextureMap
;
103 TextureMap m_textures
;
104 base::hash_map
<unsigned, unsigned> m_syncPointForMailbox
;
107 class ResourceProviderContext
: public TestWebGraphicsContext3D
{
109 static scoped_ptr
<ResourceProviderContext
> create(ContextSharedData
* sharedData
) { return make_scoped_ptr(new ResourceProviderContext(Attributes(), sharedData
)); }
111 virtual unsigned insertSyncPoint()
113 unsigned syncPoint
= m_sharedData
->insertSyncPoint();
114 // Commit the produceTextureCHROMIUM calls at this point, so that
115 // they're associated with the sync point.
116 for (PendingProduceTextureList::iterator it
= m_pendingProduceTextures
.begin(); it
!= m_pendingProduceTextures
.end(); ++it
)
117 m_sharedData
->produceTexture((*it
)->mailbox
, syncPoint
, (*it
)->texture
.Pass());
118 m_pendingProduceTextures
.clear();
122 virtual void waitSyncPoint(unsigned syncPoint
)
124 m_lastWaitedSyncPoint
= std::max(syncPoint
, m_lastWaitedSyncPoint
);
127 virtual void bindTexture(WGC3Denum target
, WebGLId texture
)
129 ASSERT_EQ(target
, GL_TEXTURE_2D
);
130 ASSERT_TRUE(!texture
|| m_textures
.find(texture
) != m_textures
.end());
131 m_currentTexture
= texture
;
134 virtual WebGLId
createTexture()
136 WebGLId id
= TestWebGraphicsContext3D::createTexture();
137 m_textures
.add(id
, scoped_ptr
<Texture
>());
141 virtual void deleteTexture(WebGLId id
)
143 TextureMap::iterator it
= m_textures
.find(id
);
144 ASSERT_FALSE(it
== m_textures
.end());
145 m_textures
.erase(it
);
146 if (m_currentTexture
== id
)
147 m_currentTexture
= 0;
150 virtual void texStorage2DEXT(WGC3Denum target
, WGC3Dint levels
, WGC3Duint internalformat
,
151 WGC3Dint width
, WGC3Dint height
)
153 ASSERT_TRUE(m_currentTexture
);
154 ASSERT_EQ(target
, GL_TEXTURE_2D
);
155 ASSERT_EQ(levels
, 1);
156 WGC3Denum format
= GL_RGBA
;
157 switch (internalformat
) {
161 format
= GL_BGRA_EXT
;
166 allocateTexture(gfx::Size(width
, height
), format
);
169 virtual void texImage2D(WGC3Denum target
, WGC3Dint level
, WGC3Denum internalformat
, WGC3Dsizei width
, WGC3Dsizei height
, WGC3Dint border
, WGC3Denum format
, WGC3Denum type
, const void* pixels
)
171 ASSERT_TRUE(m_currentTexture
);
172 ASSERT_EQ(target
, GL_TEXTURE_2D
);
174 ASSERT_EQ(internalformat
, format
);
175 ASSERT_FALSE(border
);
176 ASSERT_EQ(type
, GL_UNSIGNED_BYTE
);
177 allocateTexture(gfx::Size(width
, height
), format
);
179 setPixels(0, 0, width
, height
, pixels
);
182 virtual void texSubImage2D(WGC3Denum target
, WGC3Dint level
, WGC3Dint xoffset
, WGC3Dint yoffset
, WGC3Dsizei width
, WGC3Dsizei height
, WGC3Denum format
, WGC3Denum type
, const void* pixels
)
184 ASSERT_TRUE(m_currentTexture
);
185 ASSERT_EQ(target
, GL_TEXTURE_2D
);
187 ASSERT_TRUE(m_textures
.get(m_currentTexture
));
188 ASSERT_EQ(m_textures
.get(m_currentTexture
)->format
, format
);
189 ASSERT_EQ(type
, GL_UNSIGNED_BYTE
);
191 setPixels(xoffset
, yoffset
, width
, height
, pixels
);
194 virtual void genMailboxCHROMIUM(WGC3Dbyte
* mailbox
) { return m_sharedData
->genMailbox(mailbox
); }
195 virtual void produceTextureCHROMIUM(WGC3Denum target
, const WGC3Dbyte
* mailbox
)
197 ASSERT_TRUE(m_currentTexture
);
198 ASSERT_EQ(target
, GL_TEXTURE_2D
);
200 // Delay moving the texture into the mailbox until the next
201 // insertSyncPoint, so that it is not visible to other contexts that
202 // haven't waited on that sync point.
203 scoped_ptr
<PendingProduceTexture
> pending(new PendingProduceTexture
);
204 memcpy(pending
->mailbox
, mailbox
, sizeof(pending
->mailbox
));
205 pending
->texture
= m_textures
.take(m_currentTexture
);
206 m_textures
.set(m_currentTexture
, scoped_ptr
<Texture
>());
207 m_pendingProduceTextures
.push_back(pending
.Pass());
210 virtual void consumeTextureCHROMIUM(WGC3Denum target
, const WGC3Dbyte
* mailbox
)
212 ASSERT_TRUE(m_currentTexture
);
213 ASSERT_EQ(target
, GL_TEXTURE_2D
);
214 m_textures
.set(m_currentTexture
, m_sharedData
->consumeTexture(mailbox
, m_lastWaitedSyncPoint
));
217 void getPixels(const gfx::Size
& size
, WGC3Denum format
, uint8_t* pixels
)
219 ASSERT_TRUE(m_currentTexture
);
220 Texture
* texture
= m_textures
.get(m_currentTexture
);
221 ASSERT_TRUE(texture
);
222 ASSERT_EQ(texture
->size
, size
);
223 ASSERT_EQ(texture
->format
, format
);
224 memcpy(pixels
, texture
->data
.get(), textureSize(size
, format
));
229 return m_textures
.size();
233 ResourceProviderContext(const Attributes
& attrs
, ContextSharedData
* sharedData
)
234 : TestWebGraphicsContext3D(attrs
)
235 , m_sharedData(sharedData
)
236 , m_currentTexture(0)
237 , m_lastWaitedSyncPoint(0)
241 void allocateTexture(const gfx::Size
& size
, WGC3Denum format
)
243 ASSERT_TRUE(m_currentTexture
);
244 m_textures
.set(m_currentTexture
, make_scoped_ptr(new Texture(size
, format
)));
247 void setPixels(int xoffset
, int yoffset
, int width
, int height
, const void* pixels
)
249 ASSERT_TRUE(m_currentTexture
);
250 Texture
* texture
= m_textures
.get(m_currentTexture
);
251 ASSERT_TRUE(texture
);
252 ASSERT_TRUE(xoffset
>= 0 && xoffset
+width
<= texture
->size
.width());
253 ASSERT_TRUE(yoffset
>= 0 && yoffset
+height
<= texture
->size
.height());
255 size_t inPitch
= textureSize(gfx::Size(width
, 1), texture
->format
);
256 size_t outPitch
= textureSize(gfx::Size(texture
->size
.width(), 1), texture
->format
);
257 uint8_t* dest
= texture
->data
.get() + yoffset
* outPitch
+ textureSize(gfx::Size(xoffset
, 1), texture
->format
);
258 const uint8_t* src
= static_cast<const uint8_t*>(pixels
);
259 for (int i
= 0; i
< height
; ++i
) {
260 memcpy(dest
, src
, inPitch
);
266 typedef ScopedPtrHashMap
<WebGLId
, Texture
> TextureMap
;
267 struct PendingProduceTexture
{
268 WGC3Dbyte mailbox
[64];
269 scoped_ptr
<Texture
> texture
;
271 typedef ScopedPtrDeque
<PendingProduceTexture
> PendingProduceTextureList
;
272 ContextSharedData
* m_sharedData
;
273 WebGLId m_currentTexture
;
274 TextureMap m_textures
;
275 unsigned m_lastWaitedSyncPoint
;
276 PendingProduceTextureList m_pendingProduceTextures
;
279 class ResourceProviderTest
: public testing::TestWithParam
<ResourceProvider::ResourceType
> {
281 ResourceProviderTest()
282 : m_sharedData(ContextSharedData::create())
283 , m_outputSurface(FakeOutputSurface::Create3d(ResourceProviderContext::create(m_sharedData
.get()).PassAs
<WebKit::WebGraphicsContext3D
>().PassAs
<WebKit::WebGraphicsContext3D
>()))
284 , m_resourceProvider(ResourceProvider::create(m_outputSurface
.get()))
286 m_resourceProvider
->setDefaultResourceType(GetParam());
289 ResourceProviderContext
* context() { return static_cast<ResourceProviderContext
*>(m_outputSurface
->context3d()); }
291 void getResourcePixels(ResourceProvider::ResourceId id
, const gfx::Size
& size
, WGC3Denum format
, uint8_t* pixels
)
293 if (GetParam() == ResourceProvider::GLTexture
) {
294 ResourceProvider::ScopedReadLockGL
lockGL(m_resourceProvider
.get(), id
);
295 ASSERT_NE(0U, lockGL
.textureId());
296 context()->bindTexture(GL_TEXTURE_2D
, lockGL
.textureId());
297 context()->getPixels(size
, format
, pixels
);
298 } else if (GetParam() == ResourceProvider::Bitmap
) {
299 ResourceProvider::ScopedReadLockSoftware
lockSoftware(m_resourceProvider
.get(), id
);
300 memcpy(pixels
, lockSoftware
.skBitmap()->getPixels(), lockSoftware
.skBitmap()->getSize());
304 void expectNumResources(int count
)
306 EXPECT_EQ(count
, static_cast<int>(m_resourceProvider
->numResources()));
307 if (GetParam() == ResourceProvider::GLTexture
)
308 EXPECT_EQ(count
, context()->textureCount());
312 scoped_ptr
<ContextSharedData
> m_sharedData
;
313 scoped_ptr
<OutputSurface
> m_outputSurface
;
314 scoped_ptr
<ResourceProvider
> m_resourceProvider
;
317 TEST_P(ResourceProviderTest
, Basic
)
319 gfx::Size
size(1, 1);
320 WGC3Denum format
= GL_RGBA
;
321 size_t pixelSize
= textureSize(size
, format
);
322 ASSERT_EQ(4U, pixelSize
);
324 ResourceProvider::ResourceId id
= m_resourceProvider
->createResource(size
, format
, ResourceProvider::TextureUsageAny
);
325 expectNumResources(1);
327 uint8_t data
[4] = {1, 2, 3, 4};
328 gfx::Rect
rect(gfx::Point(), size
);
329 m_resourceProvider
->setPixels(id
, data
, rect
, rect
, gfx::Vector2d());
331 uint8_t result
[4] = {0};
332 getResourcePixels(id
, size
, format
, result
);
333 EXPECT_EQ(0, memcmp(data
, result
, pixelSize
));
335 m_resourceProvider
->deleteResource(id
);
336 expectNumResources(0);
339 TEST_P(ResourceProviderTest
, Upload
)
341 gfx::Size
size(2, 2);
342 WGC3Denum format
= GL_RGBA
;
343 size_t pixelSize
= textureSize(size
, format
);
344 ASSERT_EQ(16U, pixelSize
);
346 ResourceProvider::ResourceId id
= m_resourceProvider
->createResource(size
, format
, ResourceProvider::TextureUsageAny
);
348 uint8_t image
[16] = {0};
349 gfx::Rect
imageRect(gfx::Point(), size
);
350 m_resourceProvider
->setPixels(id
, image
, imageRect
, imageRect
, gfx::Vector2d());
352 for (uint8_t i
= 0 ; i
< pixelSize
; ++i
)
355 uint8_t result
[16] = {0};
357 gfx::Rect
sourceRect(0, 0, 1, 1);
358 gfx::Vector2d
destOffset(0, 0);
359 m_resourceProvider
->setPixels(id
, image
, imageRect
, sourceRect
, destOffset
);
361 uint8_t expected
[16] = {0, 1, 2, 3, 0, 0, 0, 0,
362 0, 0, 0, 0, 0, 0, 0, 0};
363 getResourcePixels(id
, size
, format
, result
);
364 EXPECT_EQ(0, memcmp(expected
, result
, pixelSize
));
367 gfx::Rect
sourceRect(0, 0, 1, 1);
368 gfx::Vector2d
destOffset(1, 1);
369 m_resourceProvider
->setPixels(id
, image
, imageRect
, sourceRect
, destOffset
);
371 uint8_t expected
[16] = {0, 1, 2, 3, 0, 0, 0, 0,
372 0, 0, 0, 0, 0, 1, 2, 3};
373 getResourcePixels(id
, size
, format
, result
);
374 EXPECT_EQ(0, memcmp(expected
, result
, pixelSize
));
377 gfx::Rect
sourceRect(1, 0, 1, 1);
378 gfx::Vector2d
destOffset(0, 1);
379 m_resourceProvider
->setPixels(id
, image
, imageRect
, sourceRect
, destOffset
);
381 uint8_t expected
[16] = {0, 1, 2, 3, 0, 0, 0, 0,
382 4, 5, 6, 7, 0, 1, 2, 3};
383 getResourcePixels(id
, size
, format
, result
);
384 EXPECT_EQ(0, memcmp(expected
, result
, pixelSize
));
387 gfx::Rect
offsetImageRect(gfx::Point(100, 100), size
);
388 gfx::Rect
sourceRect(100, 100, 1, 1);
389 gfx::Vector2d
destOffset(1, 0);
390 m_resourceProvider
->setPixels(id
, image
, offsetImageRect
, sourceRect
, destOffset
);
392 uint8_t expected
[16] = {0, 1, 2, 3, 0, 1, 2, 3,
393 4, 5, 6, 7, 0, 1, 2, 3};
394 getResourcePixels(id
, size
, format
, result
);
395 EXPECT_EQ(0, memcmp(expected
, result
, pixelSize
));
399 m_resourceProvider
->deleteResource(id
);
402 TEST_P(ResourceProviderTest
, TransferResources
)
404 // Resource transfer is only supported with GL textures for now.
405 if (GetParam() != ResourceProvider::GLTexture
)
408 scoped_ptr
<OutputSurface
> childOutputSurface(FakeOutputSurface::Create3d(ResourceProviderContext::create(m_sharedData
.get()).PassAs
<WebKit::WebGraphicsContext3D
>()));
409 scoped_ptr
<ResourceProvider
> childResourceProvider(ResourceProvider::create(childOutputSurface
.get()));
411 gfx::Size
size(1, 1);
412 WGC3Denum format
= GL_RGBA
;
413 size_t pixelSize
= textureSize(size
, format
);
414 ASSERT_EQ(4U, pixelSize
);
416 ResourceProvider::ResourceId id1
= childResourceProvider
->createResource(size
, format
, ResourceProvider::TextureUsageAny
);
417 uint8_t data1
[4] = {1, 2, 3, 4};
418 gfx::Rect
rect(gfx::Point(), size
);
419 childResourceProvider
->setPixels(id1
, data1
, rect
, rect
, gfx::Vector2d());
421 ResourceProvider::ResourceId id2
= childResourceProvider
->createResource(size
, format
, ResourceProvider::TextureUsageAny
);
422 uint8_t data2
[4] = {5, 5, 5, 5};
423 childResourceProvider
->setPixels(id2
, data2
, rect
, rect
, gfx::Vector2d());
425 int childId
= m_resourceProvider
->createChild();
428 // Transfer some resources to the parent.
429 ResourceProvider::ResourceIdArray resourceIdsToTransfer
;
430 resourceIdsToTransfer
.push_back(id1
);
431 resourceIdsToTransfer
.push_back(id2
);
432 TransferableResourceArray list
;
433 childResourceProvider
->prepareSendToParent(resourceIdsToTransfer
, &list
);
434 ASSERT_EQ(2u, list
.size());
435 EXPECT_NE(0u, list
[0].sync_point
);
436 EXPECT_NE(0u, list
[1].sync_point
);
437 EXPECT_TRUE(childResourceProvider
->inUseByConsumer(id1
));
438 EXPECT_TRUE(childResourceProvider
->inUseByConsumer(id2
));
439 m_resourceProvider
->receiveFromChild(childId
, list
);
442 EXPECT_EQ(2u, m_resourceProvider
->numResources());
443 ResourceProvider::ResourceIdMap resourceMap
= m_resourceProvider
->getChildToParentMap(childId
);
444 ResourceProvider::ResourceId mappedId1
= resourceMap
[id1
];
445 ResourceProvider::ResourceId mappedId2
= resourceMap
[id2
];
446 EXPECT_NE(0u, mappedId1
);
447 EXPECT_NE(0u, mappedId2
);
448 EXPECT_FALSE(m_resourceProvider
->inUseByConsumer(id1
));
449 EXPECT_FALSE(m_resourceProvider
->inUseByConsumer(id2
));
451 uint8_t result
[4] = {0};
452 getResourcePixels(mappedId1
, size
, format
, result
);
453 EXPECT_EQ(0, memcmp(data1
, result
, pixelSize
));
455 getResourcePixels(mappedId2
, size
, format
, result
);
456 EXPECT_EQ(0, memcmp(data2
, result
, pixelSize
));
459 // Check that transfering again the same resource from the child to the
461 ResourceProvider::ResourceIdArray resourceIdsToTransfer
;
462 resourceIdsToTransfer
.push_back(id1
);
463 TransferableResourceArray list
;
464 childResourceProvider
->prepareSendToParent(resourceIdsToTransfer
, &list
);
465 EXPECT_EQ(0u, list
.size());
469 // Transfer resources back from the parent to the child.
470 ResourceProvider::ResourceIdArray resourceIdsToTransfer
;
471 resourceIdsToTransfer
.push_back(mappedId1
);
472 resourceIdsToTransfer
.push_back(mappedId2
);
473 TransferableResourceArray list
;
474 m_resourceProvider
->prepareSendToChild(childId
, resourceIdsToTransfer
, &list
);
475 ASSERT_EQ(2u, list
.size());
476 EXPECT_NE(0u, list
[0].sync_point
);
477 EXPECT_NE(0u, list
[1].sync_point
);
478 childResourceProvider
->receiveFromParent(list
);
480 EXPECT_FALSE(childResourceProvider
->inUseByConsumer(id1
));
481 EXPECT_FALSE(childResourceProvider
->inUseByConsumer(id2
));
483 ResourceProviderContext
* childContext3D
= static_cast<ResourceProviderContext
*>(childOutputSurface
->context3d());
485 ResourceProvider::ScopedReadLockGL
lock(childResourceProvider
.get(), id1
);
486 ASSERT_NE(0U, lock
.textureId());
487 childContext3D
->bindTexture(GL_TEXTURE_2D
, lock
.textureId());
488 childContext3D
->getPixels(size
, format
, result
);
489 EXPECT_EQ(0, memcmp(data1
, result
, pixelSize
));
492 ResourceProvider::ScopedReadLockGL
lock(childResourceProvider
.get(), id2
);
493 ASSERT_NE(0U, lock
.textureId());
494 childContext3D
->bindTexture(GL_TEXTURE_2D
, lock
.textureId());
495 childContext3D
->getPixels(size
, format
, result
);
496 EXPECT_EQ(0, memcmp(data2
, result
, pixelSize
));
500 // Transfer resources to the parent again.
501 ResourceProvider::ResourceIdArray resourceIdsToTransfer
;
502 resourceIdsToTransfer
.push_back(id1
);
503 resourceIdsToTransfer
.push_back(id2
);
504 TransferableResourceArray list
;
505 childResourceProvider
->prepareSendToParent(resourceIdsToTransfer
, &list
);
506 ASSERT_EQ(2u, list
.size());
507 EXPECT_NE(0u, list
[0].sync_point
);
508 EXPECT_NE(0u, list
[1].sync_point
);
509 EXPECT_TRUE(childResourceProvider
->inUseByConsumer(id1
));
510 EXPECT_TRUE(childResourceProvider
->inUseByConsumer(id2
));
511 m_resourceProvider
->receiveFromChild(childId
, list
);
514 EXPECT_EQ(2u, m_resourceProvider
->numResources());
515 m_resourceProvider
->destroyChild(childId
);
516 EXPECT_EQ(0u, m_resourceProvider
->numResources());
519 TEST_P(ResourceProviderTest
, DeleteTransferredResources
)
521 // Resource transfer is only supported with GL textures for now.
522 if (GetParam() != ResourceProvider::GLTexture
)
525 scoped_ptr
<OutputSurface
> childOutputSurface(FakeOutputSurface::Create3d(ResourceProviderContext::create(m_sharedData
.get()).PassAs
<WebKit::WebGraphicsContext3D
>()));
526 scoped_ptr
<ResourceProvider
> childResourceProvider(ResourceProvider::create(childOutputSurface
.get()));
528 gfx::Size
size(1, 1);
529 WGC3Denum format
= GL_RGBA
;
530 size_t pixelSize
= textureSize(size
, format
);
531 ASSERT_EQ(4U, pixelSize
);
533 ResourceProvider::ResourceId id
= childResourceProvider
->createResource(size
, format
, ResourceProvider::TextureUsageAny
);
534 uint8_t data
[4] = {1, 2, 3, 4};
535 gfx::Rect
rect(gfx::Point(), size
);
536 childResourceProvider
->setPixels(id
, data
, rect
, rect
, gfx::Vector2d());
538 int childId
= m_resourceProvider
->createChild();
541 // Transfer some resource to the parent.
542 ResourceProvider::ResourceIdArray resourceIdsToTransfer
;
543 resourceIdsToTransfer
.push_back(id
);
544 TransferableResourceArray list
;
545 childResourceProvider
->prepareSendToParent(resourceIdsToTransfer
, &list
);
546 ASSERT_EQ(1u, list
.size());
547 EXPECT_NE(0u, list
[0].sync_point
);
548 EXPECT_TRUE(childResourceProvider
->inUseByConsumer(id
));
549 m_resourceProvider
->receiveFromChild(childId
, list
);
552 // Delete textures in the child, while they are transfered.
553 childResourceProvider
->deleteResource(id
);
554 EXPECT_EQ(1u, childResourceProvider
->numResources());
557 // Transfer resources back from the parent to the child.
558 ResourceProvider::ResourceIdMap resourceMap
= m_resourceProvider
->getChildToParentMap(childId
);
559 ResourceProvider::ResourceId mappedId
= resourceMap
[id
];
560 EXPECT_NE(0u, mappedId
);
561 ResourceProvider::ResourceIdArray resourceIdsToTransfer
;
562 resourceIdsToTransfer
.push_back(mappedId
);
563 TransferableResourceArray list
;
564 m_resourceProvider
->prepareSendToChild(childId
, resourceIdsToTransfer
, &list
);
565 ASSERT_EQ(1u, list
.size());
566 EXPECT_NE(0u, list
[0].sync_point
);
567 childResourceProvider
->receiveFromParent(list
);
569 EXPECT_EQ(0u, childResourceProvider
->numResources());
572 void ReleaseTextureMailbox(unsigned* releaseSyncPoint
, unsigned syncPoint
) {
573 *releaseSyncPoint
= syncPoint
;
576 TEST_P(ResourceProviderTest
, TransferMailboxResources
)
578 // Resource transfer is only supported with GL textures for now.
579 if (GetParam() != ResourceProvider::GLTexture
)
581 unsigned texture
= context()->createTexture();
582 context()->bindTexture(GL_TEXTURE_2D
, texture
);
583 uint8_t data
[4] = {1, 2, 3, 4};
584 context()->texImage2D(GL_TEXTURE_2D
, 0, GL_RGBA
, 1, 1, 0, GL_RGBA
, GL_UNSIGNED_BYTE
, &data
);
586 context()->genMailboxCHROMIUM(mailbox
.name
);
587 context()->produceTextureCHROMIUM(GL_TEXTURE_2D
, mailbox
.name
);
588 unsigned syncPoint
= context()->insertSyncPoint();
590 // All the logic below assumes that the sync points are all positive.
591 EXPECT_LT(0u, syncPoint
);
593 unsigned releaseSyncPoint
= 0;
594 TextureMailbox::ReleaseCallback callback
= base::Bind(ReleaseTextureMailbox
, &releaseSyncPoint
);
595 ResourceProvider::ResourceId resource
= m_resourceProvider
->createResourceFromTextureMailbox(TextureMailbox(mailbox
, callback
, syncPoint
));
596 EXPECT_EQ(1u, context()->textureCount());
597 EXPECT_EQ(0u, releaseSyncPoint
);
600 // Transfer the resource, expect the sync points to be consistent.
601 ResourceProvider::ResourceIdArray resourceIdsToTransfer
;
602 resourceIdsToTransfer
.push_back(resource
);
603 TransferableResourceArray list
;
604 m_resourceProvider
->prepareSendToParent(resourceIdsToTransfer
, &list
);
605 ASSERT_EQ(1u, list
.size());
606 EXPECT_LE(syncPoint
, list
[0].sync_point
);
607 EXPECT_EQ(0u, memcmp(mailbox
.name
, list
[0].mailbox
.name
, sizeof(mailbox
.name
)));
608 EXPECT_EQ(0u, releaseSyncPoint
);
610 context()->waitSyncPoint(list
[0].sync_point
);
611 unsigned otherTexture
= context()->createTexture();
612 context()->bindTexture(GL_TEXTURE_2D
, otherTexture
);
613 context()->consumeTextureCHROMIUM(GL_TEXTURE_2D
, mailbox
.name
);
614 uint8_t testData
[4] = {0};
615 context()->getPixels(gfx::Size(1, 1), GL_RGBA
, testData
);
616 EXPECT_EQ(0u, memcmp(data
, testData
, sizeof(data
)));
617 context()->produceTextureCHROMIUM(GL_TEXTURE_2D
, mailbox
.name
);
618 context()->deleteTexture(otherTexture
);
619 list
[0].sync_point
= context()->insertSyncPoint();
620 EXPECT_LT(0u, list
[0].sync_point
);
622 // Receive the resource, then delete it, expect the sync points to be consistent.
623 m_resourceProvider
->receiveFromParent(list
);
624 EXPECT_EQ(1u, context()->textureCount());
625 EXPECT_EQ(0u, releaseSyncPoint
);
627 m_resourceProvider
->deleteResource(resource
);
628 EXPECT_LE(list
[0].sync_point
, releaseSyncPoint
);
632 // We're going to do the same thing as above, but testing the case where we
633 // delete the resource before we receive it back.
634 syncPoint
= releaseSyncPoint
;
635 EXPECT_LT(0u, syncPoint
);
636 releaseSyncPoint
= 0;
637 resource
= m_resourceProvider
->createResourceFromTextureMailbox(TextureMailbox(mailbox
, callback
, syncPoint
));
638 EXPECT_EQ(1u, context()->textureCount());
639 EXPECT_EQ(0u, releaseSyncPoint
);
642 // Transfer the resource, expect the sync points to be consistent.
643 ResourceProvider::ResourceIdArray resourceIdsToTransfer
;
644 resourceIdsToTransfer
.push_back(resource
);
645 TransferableResourceArray list
;
646 m_resourceProvider
->prepareSendToParent(resourceIdsToTransfer
, &list
);
647 ASSERT_EQ(1u, list
.size());
648 EXPECT_LE(syncPoint
, list
[0].sync_point
);
649 EXPECT_EQ(0u, memcmp(mailbox
.name
, list
[0].mailbox
.name
, sizeof(mailbox
.name
)));
650 EXPECT_EQ(0u, releaseSyncPoint
);
652 context()->waitSyncPoint(list
[0].sync_point
);
653 unsigned otherTexture
= context()->createTexture();
654 context()->bindTexture(GL_TEXTURE_2D
, otherTexture
);
655 context()->consumeTextureCHROMIUM(GL_TEXTURE_2D
, mailbox
.name
);
656 uint8_t testData
[4] = {0};
657 context()->getPixels(gfx::Size(1, 1), GL_RGBA
, testData
);
658 EXPECT_EQ(0u, memcmp(data
, testData
, sizeof(data
)));
659 context()->produceTextureCHROMIUM(GL_TEXTURE_2D
, mailbox
.name
);
660 context()->deleteTexture(otherTexture
);
661 list
[0].sync_point
= context()->insertSyncPoint();
662 EXPECT_LT(0u, list
[0].sync_point
);
664 // Delete the resource, which shouldn't do anything.
665 m_resourceProvider
->deleteResource(resource
);
666 EXPECT_EQ(1u, context()->textureCount());
667 EXPECT_EQ(0u, releaseSyncPoint
);
669 // Then receive the resource which should release the mailbox, expect the sync points to be consistent.
670 m_resourceProvider
->receiveFromParent(list
);
671 EXPECT_LE(list
[0].sync_point
, releaseSyncPoint
);
674 context()->waitSyncPoint(releaseSyncPoint
);
675 context()->bindTexture(GL_TEXTURE_2D
, texture
);
676 context()->consumeTextureCHROMIUM(GL_TEXTURE_2D
, mailbox
.name
);
677 context()->deleteTexture(texture
);
680 class TextureStateTrackingContext
: public TestWebGraphicsContext3D
{
682 MOCK_METHOD2(bindTexture
, void(WGC3Denum target
, WebGLId texture
));
683 MOCK_METHOD3(texParameteri
, void(WGC3Denum target
, WGC3Denum pname
, WGC3Dint param
));
685 // Force all textures to be "1" so we can test for them.
686 virtual WebKit::WebGLId
NextTextureId() { return 1; }
689 TEST_P(ResourceProviderTest
, ScopedSampler
)
691 // Sampling is only supported for GL textures.
692 if (GetParam() != ResourceProvider::GLTexture
)
695 scoped_ptr
<OutputSurface
> outputSurface(FakeOutputSurface::Create3d(scoped_ptr
<WebKit::WebGraphicsContext3D
>(new TextureStateTrackingContext
)));
696 TextureStateTrackingContext
* context
= static_cast<TextureStateTrackingContext
*>(outputSurface
->context3d());
697 scoped_ptr
<ResourceProvider
> resourceProvider(ResourceProvider::create(outputSurface
.get()));
699 gfx::Size
size(1, 1);
700 WGC3Denum format
= GL_RGBA
;
703 // Check that the texture gets created with the right sampler settings.
704 EXPECT_CALL(*context
, bindTexture(GL_TEXTURE_2D
, textureId
))
705 .Times(2); // Once to create and once to allocate.
706 EXPECT_CALL(*context
, texParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
));
707 EXPECT_CALL(*context
, texParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
));
708 EXPECT_CALL(*context
, texParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
));
709 EXPECT_CALL(*context
, texParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
));
710 EXPECT_CALL(*context
, texParameteri(GL_TEXTURE_2D
, GL_TEXTURE_POOL_CHROMIUM
, GL_TEXTURE_POOL_UNMANAGED_CHROMIUM
));
711 ResourceProvider::ResourceId id
= resourceProvider
->createResource(size
, format
, ResourceProvider::TextureUsageAny
);
712 resourceProvider
->allocateForTesting(id
);
714 // Creating a sampler with the default filter should not change any texture
717 EXPECT_CALL(*context
, bindTexture(GL_TEXTURE_2D
, textureId
));
718 ResourceProvider::ScopedSamplerGL
sampler(resourceProvider
.get(), id
, GL_TEXTURE_2D
, GL_LINEAR
);
721 // Using a different filter should be reflected in the texture parameters.
723 EXPECT_CALL(*context
, bindTexture(GL_TEXTURE_2D
, textureId
));
724 EXPECT_CALL(*context
, texParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_NEAREST
));
725 EXPECT_CALL(*context
, texParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_NEAREST
));
726 ResourceProvider::ScopedSamplerGL
sampler(resourceProvider
.get(), id
, GL_TEXTURE_2D
, GL_NEAREST
);
729 // Test resetting to the default filter.
731 EXPECT_CALL(*context
, bindTexture(GL_TEXTURE_2D
, textureId
));
732 EXPECT_CALL(*context
, texParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
));
733 EXPECT_CALL(*context
, texParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
));
734 ResourceProvider::ScopedSamplerGL
sampler(resourceProvider
.get(), id
, GL_TEXTURE_2D
, GL_LINEAR
);
737 Mock::VerifyAndClearExpectations(context
);
740 TEST_P(ResourceProviderTest
, ManagedResource
)
742 // Sampling is only supported for GL textures.
743 if (GetParam() != ResourceProvider::GLTexture
)
746 scoped_ptr
<OutputSurface
> outputSurface(FakeOutputSurface::Create3d(scoped_ptr
<WebKit::WebGraphicsContext3D
>(new TextureStateTrackingContext
)));
747 TextureStateTrackingContext
* context
= static_cast<TextureStateTrackingContext
*>(outputSurface
->context3d());
748 scoped_ptr
<ResourceProvider
> resourceProvider(ResourceProvider::create(outputSurface
.get()));
750 gfx::Size
size(1, 1);
751 WGC3Denum format
= GL_RGBA
;
754 // Check that the texture gets created with the right sampler settings.
755 EXPECT_CALL(*context
, bindTexture(GL_TEXTURE_2D
, textureId
));
756 EXPECT_CALL(*context
, texParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MIN_FILTER
, GL_LINEAR
));
757 EXPECT_CALL(*context
, texParameteri(GL_TEXTURE_2D
, GL_TEXTURE_MAG_FILTER
, GL_LINEAR
));
758 EXPECT_CALL(*context
, texParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_S
, GL_CLAMP_TO_EDGE
));
759 EXPECT_CALL(*context
, texParameteri(GL_TEXTURE_2D
, GL_TEXTURE_WRAP_T
, GL_CLAMP_TO_EDGE
));
760 EXPECT_CALL(*context
, texParameteri(GL_TEXTURE_2D
, GL_TEXTURE_POOL_CHROMIUM
, GL_TEXTURE_POOL_MANAGED_CHROMIUM
));
761 ResourceProvider::ResourceId id
= resourceProvider
->createManagedResource(size
, format
, ResourceProvider::TextureUsageAny
);
763 Mock::VerifyAndClearExpectations(context
);
766 class AllocationTrackingContext3D
: public TestWebGraphicsContext3D
{
768 MOCK_METHOD0(createTexture
, WebGLId());
769 MOCK_METHOD1(deleteTexture
, void(WebGLId texture_id
));
770 MOCK_METHOD2(bindTexture
, void(WGC3Denum target
, WebGLId texture
));
771 MOCK_METHOD9(texImage2D
, void(WGC3Denum target
, WGC3Dint level
, WGC3Denum internalformat
,
772 WGC3Dsizei width
, WGC3Dsizei height
, WGC3Dint border
, WGC3Denum format
,
773 WGC3Denum type
, const void* pixels
));
774 MOCK_METHOD9(texSubImage2D
, void(WGC3Denum target
, WGC3Dint level
, WGC3Dint xoffset
, WGC3Dint yoffset
,
775 WGC3Dsizei width
, WGC3Dsizei height
, WGC3Denum format
,
776 WGC3Denum type
, const void* pixels
));
777 MOCK_METHOD9(asyncTexImage2DCHROMIUM
, void(WGC3Denum target
, WGC3Dint level
, WGC3Denum internalformat
,
778 WGC3Dsizei width
, WGC3Dsizei height
, WGC3Dint border
, WGC3Denum format
,
779 WGC3Denum type
, const void* pixels
));
780 MOCK_METHOD9(asyncTexSubImage2DCHROMIUM
, void(WGC3Denum target
, WGC3Dint level
, WGC3Dint xoffset
, WGC3Dint yoffset
,
781 WGC3Dsizei width
, WGC3Dsizei height
, WGC3Denum format
,
782 WGC3Denum type
, const void* pixels
));
785 TEST_P(ResourceProviderTest
, TextureAllocation
)
787 // Only for GL textures.
788 if (GetParam() != ResourceProvider::GLTexture
)
790 scoped_ptr
<WebKit::WebGraphicsContext3D
> mock_context(
791 static_cast<WebKit::WebGraphicsContext3D
*>(new NiceMock
<AllocationTrackingContext3D
>));
792 scoped_ptr
<OutputSurface
> outputSurface(FakeOutputSurface::Create3d(mock_context
.Pass()));
794 gfx::Size
size(2, 2);
795 gfx::Vector2d
offset(0, 0);
796 gfx::Rect
rect(0, 0, 2, 2);
797 WGC3Denum format
= GL_RGBA
;
798 ResourceProvider::ResourceId id
= 0;
799 uint8_t pixels
[16] = {0};
802 AllocationTrackingContext3D
* context
= static_cast<AllocationTrackingContext3D
*>(outputSurface
->context3d());
803 scoped_ptr
<ResourceProvider
> resourceProvider(ResourceProvider::create(outputSurface
.get()));
805 // Lazy allocation. Don't allocate when creating the resource.
806 EXPECT_CALL(*context
, createTexture()).WillOnce(Return(textureId
));
807 EXPECT_CALL(*context
, deleteTexture(textureId
)).Times(1);
808 EXPECT_CALL(*context
, bindTexture(GL_TEXTURE_2D
, textureId
)).Times(1);
809 EXPECT_CALL(*context
, texImage2D(_
,_
,_
,_
,_
,_
,_
,_
,_
)).Times(0);
810 EXPECT_CALL(*context
, asyncTexImage2DCHROMIUM(_
,_
,_
,_
,_
,_
,_
,_
,_
)).Times(0);
811 id
= resourceProvider
->createResource(size
, format
, ResourceProvider::TextureUsageAny
);
812 resourceProvider
->deleteResource(id
);
813 Mock::VerifyAndClearExpectations(context
);
815 // Do allocate when we set the pixels.
816 EXPECT_CALL(*context
, createTexture()).WillOnce(Return(textureId
));
817 EXPECT_CALL(*context
, deleteTexture(textureId
)).Times(1);
818 EXPECT_CALL(*context
, bindTexture(GL_TEXTURE_2D
, textureId
)).Times(3);
819 EXPECT_CALL(*context
, texImage2D(_
,_
,_
,2,2,_
,_
,_
,_
)).Times(1);
820 EXPECT_CALL(*context
, texSubImage2D(_
,_
,_
,_
,2,2,_
,_
,_
)).Times(1);
821 id
= resourceProvider
->createResource(size
, format
, ResourceProvider::TextureUsageAny
);
822 resourceProvider
->setPixels(id
, pixels
, rect
, rect
, offset
);
823 resourceProvider
->deleteResource(id
);
824 Mock::VerifyAndClearExpectations(context
);
826 // Same for setPixelsFromBuffer
827 EXPECT_CALL(*context
, createTexture()).WillOnce(Return(textureId
));
828 EXPECT_CALL(*context
, deleteTexture(textureId
)).Times(1);
829 EXPECT_CALL(*context
, bindTexture(GL_TEXTURE_2D
, textureId
)).Times(3);
830 EXPECT_CALL(*context
, texImage2D(_
,_
,_
,2,2,_
,_
,_
,_
)).Times(1);
831 EXPECT_CALL(*context
, texSubImage2D(_
,_
,_
,_
,2,2,_
,_
,_
)).Times(1);
832 id
= resourceProvider
->createResource(size
, format
, ResourceProvider::TextureUsageAny
);
833 resourceProvider
->acquirePixelBuffer(id
);
834 resourceProvider
->setPixelsFromBuffer(id
);
835 resourceProvider
->releasePixelBuffer(id
);
836 resourceProvider
->deleteResource(id
);
837 Mock::VerifyAndClearExpectations(context
);
839 // Same for async version.
840 EXPECT_CALL(*context
, createTexture()).WillOnce(Return(textureId
));
841 EXPECT_CALL(*context
, deleteTexture(textureId
)).Times(1);
842 EXPECT_CALL(*context
, bindTexture(GL_TEXTURE_2D
, textureId
)).Times(2);
843 EXPECT_CALL(*context
, asyncTexImage2DCHROMIUM(_
,_
,_
,2,2,_
,_
,_
,_
)).Times(1);
844 id
= resourceProvider
->createResource(size
, format
, ResourceProvider::TextureUsageAny
);
845 resourceProvider
->acquirePixelBuffer(id
);
846 resourceProvider
->beginSetPixels(id
);
847 resourceProvider
->releasePixelBuffer(id
);
848 resourceProvider
->deleteResource(id
);
849 Mock::VerifyAndClearExpectations(context
);
852 INSTANTIATE_TEST_CASE_P(ResourceProviderTests
,
853 ResourceProviderTest
,
854 ::testing::Values(ResourceProvider::GLTexture
,
855 ResourceProvider::Bitmap
));