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.
7 #include "CCResourceProvider.h"
9 #include "CCGraphicsContext.h"
10 #include "CCSingleThreadProxy.h" // For DebugScopedSetImplThread
11 #include "CompositorFakeWebGraphicsContext3D.h"
12 #include "Extensions3DChromium.h"
13 #include "FakeWebCompositorOutputSurface.h"
14 #include <gtest/gtest.h>
15 #include <public/WebGraphicsContext3D.h>
16 #include <wtf/HashMap.h>
17 #include <wtf/OwnArrayPtr.h>
18 #include <wtf/OwnPtr.h>
20 using namespace WebCore
;
21 using namespace WebKit
;
25 size_t textureSize(const IntSize
& size
, WGC3Denum format
)
27 unsigned int componentsPerPixel
= 4;
28 unsigned int bytesPerComponent
= 1;
29 return size
.width() * size
.height() * componentsPerPixel
* bytesPerComponent
;
33 Texture(const IntSize
& size
, WGC3Denum format
)
36 , data(adoptArrayPtr(new uint8_t[textureSize(size
, format
)]))
42 OwnArrayPtr
<uint8_t> data
;
45 // Shared data between multiple ResourceProviderContext. This contains mailbox
46 // contents as well as information about sync points.
47 class ContextSharedData
{
49 static PassOwnPtr
<ContextSharedData
> create() { return adoptPtr(new ContextSharedData()); }
51 unsigned insertSyncPoint() { return m_nextSyncPoint
++; }
53 void genMailbox(WGC3Dbyte
* mailbox
)
55 memset(mailbox
, 0, sizeof(WGC3Dbyte
[64]));
56 memcpy(mailbox
, &m_nextMailBox
, sizeof(m_nextMailBox
));
60 void produceTexture(const WGC3Dbyte
* mailboxName
, unsigned syncPoint
, PassOwnPtr
<Texture
> texture
)
63 memcpy(&mailbox
, mailboxName
, sizeof(mailbox
));
64 ASSERT(mailbox
&& mailbox
< m_nextMailBox
);
65 m_textures
.set(mailbox
, texture
);
66 ASSERT(m_syncPointForMailbox
.get(mailbox
) < syncPoint
);
67 m_syncPointForMailbox
.set(mailbox
, syncPoint
);
70 PassOwnPtr
<Texture
> consumeTexture(const WGC3Dbyte
* mailboxName
, unsigned syncPoint
)
73 memcpy(&mailbox
, mailboxName
, sizeof(mailbox
));
74 ASSERT(mailbox
&& mailbox
< m_nextMailBox
);
76 // If the latest sync point the context has waited on is before the sync
77 // point for when the mailbox was set, pretend we never saw that
79 if (m_syncPointForMailbox
.get(mailbox
) < syncPoint
)
81 return m_textures
.take(mailbox
);
90 unsigned m_nextSyncPoint
;
91 unsigned m_nextMailBox
;
92 typedef HashMap
<unsigned, OwnPtr
<Texture
> > TextureMap
;
93 TextureMap m_textures
;
94 HashMap
<unsigned, unsigned> m_syncPointForMailbox
;
97 class ResourceProviderContext
: public CompositorFakeWebGraphicsContext3D
{
99 static PassOwnPtr
<ResourceProviderContext
> create(ContextSharedData
* sharedData
) { return adoptPtr(new ResourceProviderContext(Attributes(), sharedData
)); }
101 virtual unsigned insertSyncPoint()
103 unsigned syncPoint
= m_sharedData
->insertSyncPoint();
104 // Commit the produceTextureCHROMIUM calls at this point, so that
105 // they're associated with the sync point.
106 for (PendingProduceTextureList::iterator it
= m_pendingProduceTextures
.begin(); it
!= m_pendingProduceTextures
.end(); ++it
)
107 m_sharedData
->produceTexture((*it
)->mailbox
, syncPoint
, (*it
)->texture
.release());
108 m_pendingProduceTextures
.clear();
112 virtual void waitSyncPoint(unsigned syncPoint
)
114 m_lastWaitedSyncPoint
= std::max(syncPoint
, m_lastWaitedSyncPoint
);
117 virtual void bindTexture(WGC3Denum target
, WebGLId texture
)
119 ASSERT(target
== GraphicsContext3D::TEXTURE_2D
);
120 ASSERT(!texture
|| m_textures
.find(texture
) != m_textures
.end());
121 m_currentTexture
= texture
;
124 virtual WebGLId
createTexture()
126 WebGLId id
= CompositorFakeWebGraphicsContext3D::createTexture();
127 m_textures
.add(id
, nullptr);
131 virtual void deleteTexture(WebGLId id
)
133 TextureMap::iterator it
= m_textures
.find(id
);
134 ASSERT(it
!= m_textures
.end());
135 m_textures
.remove(it
);
136 if (m_currentTexture
== id
)
137 m_currentTexture
= 0;
140 virtual void texStorage2DEXT(WGC3Denum target
, WGC3Dint levels
, WGC3Duint internalformat
,
141 WGC3Dint width
, WGC3Dint height
)
143 ASSERT(m_currentTexture
);
144 ASSERT(target
== GraphicsContext3D::TEXTURE_2D
);
146 WGC3Denum format
= GraphicsContext3D::RGBA
;
147 switch (internalformat
) {
148 case Extensions3D::RGBA8_OES
:
150 case Extensions3DChromium::BGRA8_EXT
:
151 format
= Extensions3D::BGRA_EXT
;
154 ASSERT_NOT_REACHED();
156 allocateTexture(IntSize(width
, height
), format
);
159 virtual void texImage2D(WGC3Denum target
, WGC3Dint level
, WGC3Denum internalformat
, WGC3Dsizei width
, WGC3Dsizei height
, WGC3Dint border
, WGC3Denum format
, WGC3Denum type
, const void* pixels
)
161 ASSERT(m_currentTexture
);
162 ASSERT(target
== GraphicsContext3D::TEXTURE_2D
);
164 ASSERT(internalformat
== format
);
166 ASSERT(type
== GraphicsContext3D::UNSIGNED_BYTE
);
167 allocateTexture(IntSize(width
, height
), format
);
169 setPixels(0, 0, width
, height
, pixels
);
172 virtual void texSubImage2D(WGC3Denum target
, WGC3Dint level
, WGC3Dint xoffset
, WGC3Dint yoffset
, WGC3Dsizei width
, WGC3Dsizei height
, WGC3Denum format
, WGC3Denum type
, const void* pixels
)
174 ASSERT(m_currentTexture
);
175 ASSERT(target
== GraphicsContext3D::TEXTURE_2D
);
177 ASSERT(m_textures
.get(m_currentTexture
));
178 ASSERT(m_textures
.get(m_currentTexture
)->format
== format
);
179 ASSERT(type
== GraphicsContext3D::UNSIGNED_BYTE
);
181 setPixels(xoffset
, yoffset
, width
, height
, pixels
);
184 virtual void genMailboxCHROMIUM(WGC3Dbyte
* mailbox
) { return m_sharedData
->genMailbox(mailbox
); }
185 virtual void produceTextureCHROMIUM(WGC3Denum target
, const WGC3Dbyte
* mailbox
)
187 ASSERT(m_currentTexture
);
188 ASSERT(target
== GraphicsContext3D::TEXTURE_2D
);
190 // Delay movind the texture into the mailbox until the next
191 // insertSyncPoint, so that it is not visible to other contexts that
192 // haven't waited on that sync point.
193 OwnPtr
<PendingProduceTexture
> pending(adoptPtr(new PendingProduceTexture
));
194 memcpy(pending
->mailbox
, mailbox
, sizeof(pending
->mailbox
));
195 pending
->texture
= m_textures
.take(m_currentTexture
);
196 m_textures
.set(m_currentTexture
, nullptr);
197 m_pendingProduceTextures
.append(pending
.release());
200 virtual void consumeTextureCHROMIUM(WGC3Denum target
, const WGC3Dbyte
* mailbox
)
202 ASSERT(m_currentTexture
);
203 ASSERT(target
== GraphicsContext3D::TEXTURE_2D
);
204 m_textures
.set(m_currentTexture
, m_sharedData
->consumeTexture(mailbox
, m_lastWaitedSyncPoint
));
207 void getPixels(const IntSize
& size
, WGC3Denum format
, uint8_t* pixels
)
209 ASSERT(m_currentTexture
);
210 Texture
* texture
= m_textures
.get(m_currentTexture
);
212 ASSERT(texture
->size
== size
);
213 ASSERT(texture
->format
== format
);
214 memcpy(pixels
, texture
->data
.get(), textureSize(size
, format
));
219 return m_textures
.size();
223 ResourceProviderContext(const Attributes
& attrs
, ContextSharedData
* sharedData
)
224 : CompositorFakeWebGraphicsContext3D(attrs
)
225 , m_sharedData(sharedData
)
226 , m_currentTexture(0)
227 , m_lastWaitedSyncPoint(0)
231 void allocateTexture(const IntSize
& size
, WGC3Denum format
)
233 ASSERT(m_currentTexture
);
234 m_textures
.set(m_currentTexture
, adoptPtr(new Texture(size
, format
)));
237 void setPixels(int xoffset
, int yoffset
, int width
, int height
, const void* pixels
)
239 ASSERT(m_currentTexture
);
240 Texture
* texture
= m_textures
.get(m_currentTexture
);
242 ASSERT(xoffset
>= 0 && xoffset
+width
<= texture
->size
.width());
243 ASSERT(yoffset
>= 0 && yoffset
+height
<= texture
->size
.height());
245 size_t inPitch
= textureSize(IntSize(width
, 1), texture
->format
);
246 size_t outPitch
= textureSize(IntSize(texture
->size
.width(), 1), texture
->format
);
247 uint8_t* dest
= texture
->data
.get() + yoffset
* outPitch
+ textureSize(IntSize(xoffset
, 1), texture
->format
);
248 const uint8_t* src
= static_cast<const uint8_t*>(pixels
);
249 for (int i
= 0; i
< height
; ++i
) {
250 memcpy(dest
, src
, inPitch
);
256 typedef HashMap
<WebGLId
, OwnPtr
<Texture
> > TextureMap
;
257 struct PendingProduceTexture
{
258 WGC3Dbyte mailbox
[64];
259 OwnPtr
<Texture
> texture
;
261 typedef Deque
<OwnPtr
<PendingProduceTexture
> > PendingProduceTextureList
;
262 ContextSharedData
* m_sharedData
;
263 WebGLId m_currentTexture
;
264 TextureMap m_textures
;
265 unsigned m_lastWaitedSyncPoint
;
266 PendingProduceTextureList m_pendingProduceTextures
;
269 class CCResourceProviderTest
: public testing::TestWithParam
<CCResourceProvider::ResourceType
> {
271 CCResourceProviderTest()
272 : m_sharedData(ContextSharedData::create())
273 , m_context(FakeWebCompositorOutputSurface::create(ResourceProviderContext::create(m_sharedData
.get())))
274 , m_resourceProvider(CCResourceProvider::create(m_context
.get()))
276 m_resourceProvider
->setDefaultResourceType(GetParam());
279 ResourceProviderContext
* context() { return static_cast<ResourceProviderContext
*>(m_context
->context3D()); }
281 void getResourcePixels(CCResourceProvider::ResourceId id
, const IntSize
& size
, WGC3Denum format
, uint8_t* pixels
)
283 if (GetParam() == CCResourceProvider::GLTexture
) {
284 CCResourceProvider::ScopedReadLockGL
lockGL(m_resourceProvider
.get(), id
);
285 ASSERT_NE(0U, lockGL
.textureId());
286 context()->bindTexture(GraphicsContext3D::TEXTURE_2D
, lockGL
.textureId());
287 context()->getPixels(size
, format
, pixels
);
288 } else if (GetParam() == CCResourceProvider::Bitmap
) {
289 CCResourceProvider::ScopedReadLockSoftware
lockSoftware(m_resourceProvider
.get(), id
);
290 memcpy(pixels
, lockSoftware
.skBitmap()->getPixels(), lockSoftware
.skBitmap()->getSize());
294 void expectNumResources(int count
)
296 EXPECT_EQ(count
, static_cast<int>(m_resourceProvider
->numResources()));
297 if (GetParam() == CCResourceProvider::GLTexture
)
298 EXPECT_EQ(count
, context()->textureCount());
302 DebugScopedSetImplThread implThread
;
303 OwnPtr
<ContextSharedData
> m_sharedData
;
304 OwnPtr
<CCGraphicsContext
> m_context
;
305 OwnPtr
<CCResourceProvider
> m_resourceProvider
;
308 TEST_P(CCResourceProviderTest
, Basic
)
311 WGC3Denum format
= GraphicsContext3D::RGBA
;
313 size_t pixelSize
= textureSize(size
, format
);
314 ASSERT_EQ(4U, pixelSize
);
316 CCResourceProvider::ResourceId id
= m_resourceProvider
->createResource(pool
, size
, format
, CCResourceProvider::TextureUsageAny
);
317 expectNumResources(1);
319 uint8_t data
[4] = {1, 2, 3, 4};
320 IntRect
rect(IntPoint(), size
);
321 m_resourceProvider
->upload(id
, data
, rect
, rect
, IntSize());
323 uint8_t result
[4] = {0};
324 getResourcePixels(id
, size
, format
, result
);
325 EXPECT_EQ(0, memcmp(data
, result
, pixelSize
));
327 m_resourceProvider
->deleteResource(id
);
328 expectNumResources(0);
331 TEST_P(CCResourceProviderTest
, DeleteOwnedResources
)
334 WGC3Denum format
= GraphicsContext3D::RGBA
;
338 for (int i
= 0; i
< count
; ++i
)
339 m_resourceProvider
->createResource(pool
, size
, format
, CCResourceProvider::TextureUsageAny
);
340 expectNumResources(3);
342 m_resourceProvider
->deleteOwnedResources(pool
+1);
343 expectNumResources(3);
345 m_resourceProvider
->deleteOwnedResources(pool
);
346 expectNumResources(0);
349 TEST_P(CCResourceProviderTest
, Upload
)
352 WGC3Denum format
= GraphicsContext3D::RGBA
;
354 size_t pixelSize
= textureSize(size
, format
);
355 ASSERT_EQ(16U, pixelSize
);
357 CCResourceProvider::ResourceId id
= m_resourceProvider
->createResource(pool
, size
, format
, CCResourceProvider::TextureUsageAny
);
359 uint8_t image
[16] = {0};
360 IntRect
imageRect(IntPoint(), size
);
361 m_resourceProvider
->upload(id
, image
, imageRect
, imageRect
, IntSize());
363 for (uint8_t i
= 0 ; i
< pixelSize
; ++i
)
366 uint8_t result
[16] = {0};
368 IntRect
sourceRect(0, 0, 1, 1);
369 IntSize
destOffset(0, 0);
370 m_resourceProvider
->upload(id
, image
, imageRect
, sourceRect
, destOffset
);
372 uint8_t expected
[16] = {0, 1, 2, 3, 0, 0, 0, 0,
373 0, 0, 0, 0, 0, 0, 0, 0};
374 getResourcePixels(id
, size
, format
, result
);
375 EXPECT_EQ(0, memcmp(expected
, result
, pixelSize
));
378 IntRect
sourceRect(0, 0, 1, 1);
379 IntSize
destOffset(1, 1);
380 m_resourceProvider
->upload(id
, image
, imageRect
, sourceRect
, destOffset
);
382 uint8_t expected
[16] = {0, 1, 2, 3, 0, 0, 0, 0,
383 0, 0, 0, 0, 0, 1, 2, 3};
384 getResourcePixels(id
, size
, format
, result
);
385 EXPECT_EQ(0, memcmp(expected
, result
, pixelSize
));
388 IntRect
sourceRect(1, 0, 1, 1);
389 IntSize
destOffset(0, 1);
390 m_resourceProvider
->upload(id
, image
, imageRect
, sourceRect
, destOffset
);
392 uint8_t expected
[16] = {0, 1, 2, 3, 0, 0, 0, 0,
393 4, 5, 6, 7, 0, 1, 2, 3};
394 getResourcePixels(id
, size
, format
, result
);
395 EXPECT_EQ(0, memcmp(expected
, result
, pixelSize
));
398 IntRect
offsetImageRect(IntPoint(100, 100), size
);
399 IntRect
sourceRect(100, 100, 1, 1);
400 IntSize
destOffset(1, 0);
401 m_resourceProvider
->upload(id
, image
, offsetImageRect
, sourceRect
, destOffset
);
403 uint8_t expected
[16] = {0, 1, 2, 3, 0, 1, 2, 3,
404 4, 5, 6, 7, 0, 1, 2, 3};
405 getResourcePixels(id
, size
, format
, result
);
406 EXPECT_EQ(0, memcmp(expected
, result
, pixelSize
));
410 m_resourceProvider
->deleteResource(id
);
413 TEST_P(CCResourceProviderTest
, TransferResources
)
415 // Resource transfer is only supported with GL textures for now.
416 if (GetParam() != CCResourceProvider::GLTexture
)
419 OwnPtr
<CCGraphicsContext
> childContext(FakeWebCompositorOutputSurface::create(ResourceProviderContext::create(m_sharedData
.get())));
420 OwnPtr
<CCResourceProvider
> childResourceProvider(CCResourceProvider::create(childContext
.get()));
423 WGC3Denum format
= GraphicsContext3D::RGBA
;
425 size_t pixelSize
= textureSize(size
, format
);
426 ASSERT_EQ(4U, pixelSize
);
428 CCResourceProvider::ResourceId id1
= childResourceProvider
->createResource(pool
, size
, format
, CCResourceProvider::TextureUsageAny
);
429 uint8_t data1
[4] = {1, 2, 3, 4};
430 IntRect
rect(IntPoint(), size
);
431 childResourceProvider
->upload(id1
, data1
, rect
, rect
, IntSize());
433 CCResourceProvider::ResourceId id2
= childResourceProvider
->createResource(pool
, size
, format
, CCResourceProvider::TextureUsageAny
);
434 uint8_t data2
[4] = {5, 5, 5, 5};
435 childResourceProvider
->upload(id2
, data2
, rect
, rect
, IntSize());
438 int childId
= m_resourceProvider
->createChild(childPool
);
441 // Transfer some resources to the parent.
442 CCResourceProvider::ResourceIdArray resourceIdsToTransfer
;
443 resourceIdsToTransfer
.append(id1
);
444 resourceIdsToTransfer
.append(id2
);
445 CCResourceProvider::TransferableResourceList list
= childResourceProvider
->prepareSendToParent(resourceIdsToTransfer
);
446 EXPECT_NE(0u, list
.syncPoint
);
447 EXPECT_EQ(2u, list
.resources
.size());
448 EXPECT_TRUE(childResourceProvider
->inUseByConsumer(id1
));
449 EXPECT_TRUE(childResourceProvider
->inUseByConsumer(id2
));
450 m_resourceProvider
->receiveFromChild(childId
, list
);
453 EXPECT_EQ(2u, m_resourceProvider
->numResources());
454 EXPECT_EQ(2u, m_resourceProvider
->mailboxCount());
455 CCResourceProvider::ResourceIdMap resourceMap
= m_resourceProvider
->getChildToParentMap(childId
);
456 CCResourceProvider::ResourceId mappedId1
= resourceMap
.get(id1
);
457 CCResourceProvider::ResourceId mappedId2
= resourceMap
.get(id2
);
458 EXPECT_NE(0u, mappedId1
);
459 EXPECT_NE(0u, mappedId2
);
460 EXPECT_FALSE(m_resourceProvider
->inUseByConsumer(id1
));
461 EXPECT_FALSE(m_resourceProvider
->inUseByConsumer(id2
));
463 uint8_t result
[4] = {0};
464 getResourcePixels(mappedId1
, size
, format
, result
);
465 EXPECT_EQ(0, memcmp(data1
, result
, pixelSize
));
467 getResourcePixels(mappedId2
, size
, format
, result
);
468 EXPECT_EQ(0, memcmp(data2
, result
, pixelSize
));
471 // Check that transfering again the same resource from the child to the
473 CCResourceProvider::ResourceIdArray resourceIdsToTransfer
;
474 resourceIdsToTransfer
.append(id1
);
475 CCResourceProvider::TransferableResourceList list
= childResourceProvider
->prepareSendToParent(resourceIdsToTransfer
);
476 EXPECT_EQ(0u, list
.syncPoint
);
477 EXPECT_EQ(0u, list
.resources
.size());
481 // Transfer resources back from the parent to the child.
482 CCResourceProvider::ResourceIdArray resourceIdsToTransfer
;
483 resourceIdsToTransfer
.append(mappedId1
);
484 resourceIdsToTransfer
.append(mappedId2
);
485 CCResourceProvider::TransferableResourceList list
= m_resourceProvider
->prepareSendToChild(childId
, resourceIdsToTransfer
);
486 EXPECT_NE(0u, list
.syncPoint
);
487 EXPECT_EQ(2u, list
.resources
.size());
488 childResourceProvider
->receiveFromParent(list
);
490 EXPECT_EQ(0u, m_resourceProvider
->mailboxCount());
491 EXPECT_EQ(2u, childResourceProvider
->mailboxCount());
492 EXPECT_FALSE(childResourceProvider
->inUseByConsumer(id1
));
493 EXPECT_FALSE(childResourceProvider
->inUseByConsumer(id2
));
495 ResourceProviderContext
* childContext3D
= static_cast<ResourceProviderContext
*>(childContext
->context3D());
497 CCResourceProvider::ScopedReadLockGL
lock(childResourceProvider
.get(), id1
);
498 ASSERT_NE(0U, lock
.textureId());
499 childContext3D
->bindTexture(GraphicsContext3D::TEXTURE_2D
, lock
.textureId());
500 childContext3D
->getPixels(size
, format
, result
);
501 EXPECT_EQ(0, memcmp(data1
, result
, pixelSize
));
504 CCResourceProvider::ScopedReadLockGL
lock(childResourceProvider
.get(), id2
);
505 ASSERT_NE(0U, lock
.textureId());
506 childContext3D
->bindTexture(GraphicsContext3D::TEXTURE_2D
, lock
.textureId());
507 childContext3D
->getPixels(size
, format
, result
);
508 EXPECT_EQ(0, memcmp(data2
, result
, pixelSize
));
512 // Transfer resources to the parent again.
513 CCResourceProvider::ResourceIdArray resourceIdsToTransfer
;
514 resourceIdsToTransfer
.append(id1
);
515 resourceIdsToTransfer
.append(id2
);
516 CCResourceProvider::TransferableResourceList list
= childResourceProvider
->prepareSendToParent(resourceIdsToTransfer
);
517 EXPECT_NE(0u, list
.syncPoint
);
518 EXPECT_EQ(2u, list
.resources
.size());
519 EXPECT_TRUE(childResourceProvider
->inUseByConsumer(id1
));
520 EXPECT_TRUE(childResourceProvider
->inUseByConsumer(id2
));
521 m_resourceProvider
->receiveFromChild(childId
, list
);
524 EXPECT_EQ(2u, m_resourceProvider
->numResources());
525 m_resourceProvider
->destroyChild(childId
);
526 EXPECT_EQ(0u, m_resourceProvider
->numResources());
527 EXPECT_EQ(0u, m_resourceProvider
->mailboxCount());
530 INSTANTIATE_TEST_CASE_P(CCResourceProviderTests
,
531 CCResourceProviderTest
,
532 ::testing::Values(CCResourceProvider::GLTexture
,
533 CCResourceProvider::Bitmap
));