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"
12 #include "base/string_split.h"
13 #include "base/string_util.h"
15 #include "CCRendererGL.h" // For the GLC() macro.
16 #include "Extensions3DChromium.h"
18 #include "LayerTextureSubImage.h"
20 #include <public/WebGraphicsContext3D.h>
21 #include <wtf/HashSet.h>
23 using WebKit::WebGraphicsContext3D
;
27 static GC3Denum
textureToStorageFormat(GC3Denum textureFormat
)
29 GC3Denum storageFormat
= Extensions3D::RGBA8_OES
;
30 switch (textureFormat
) {
31 case GraphicsContext3D::RGBA
:
33 case Extensions3D::BGRA_EXT
:
34 storageFormat
= Extensions3DChromium::BGRA8_EXT
;
44 static bool isTextureFormatSupportedForStorage(GC3Denum format
)
46 return (format
== GraphicsContext3D::RGBA
|| format
== Extensions3D::BGRA_EXT
);
49 PassOwnPtr
<CCResourceProvider
> CCResourceProvider::create(CCGraphicsContext
* context
)
51 OwnPtr
<CCResourceProvider
> resourceProvider(adoptPtr(new CCResourceProvider(context
)));
52 if (!resourceProvider
->initialize())
54 return resourceProvider
.release();
57 CCResourceProvider::~CCResourceProvider()
61 WebGraphicsContext3D
* CCResourceProvider::graphicsContext3D()
63 ASSERT(CCProxy::isImplThread());
64 return m_context
->context3D();
67 bool CCResourceProvider::inUseByConsumer(ResourceId id
)
69 ASSERT(CCProxy::isImplThread());
70 ResourceMap::iterator it
= m_resources
.find(id
);
71 ASSERT(it
!= m_resources
.end());
72 return !!it
->second
.lockForReadCount
|| it
->second
.exported
;
75 CCResourceProvider::ResourceId
CCResourceProvider::createResource(int pool
, const IntSize
& size
, GC3Denum format
, TextureUsageHint hint
)
77 switch (m_defaultResourceType
) {
79 return createGLTexture(pool
, size
, format
, hint
);
81 ASSERT(format
== GraphicsContext3D::RGBA
);
82 return createBitmap(pool
, size
);
89 CCResourceProvider::ResourceId
CCResourceProvider::createGLTexture(int pool
, const IntSize
& size
, GC3Denum format
, TextureUsageHint hint
)
91 ASSERT(CCProxy::isImplThread());
92 unsigned textureId
= 0;
93 WebGraphicsContext3D
* context3d
= m_context
->context3D();
95 GLC(context3d
, textureId
= context3d
->createTexture());
96 GLC(context3d
, context3d
->bindTexture(GraphicsContext3D::TEXTURE_2D
, textureId
));
97 GLC(context3d
, context3d
->texParameteri(GraphicsContext3D::TEXTURE_2D
, GraphicsContext3D::TEXTURE_MIN_FILTER
, GraphicsContext3D::LINEAR
));
98 GLC(context3d
, context3d
->texParameteri(GraphicsContext3D::TEXTURE_2D
, GraphicsContext3D::TEXTURE_MAG_FILTER
, GraphicsContext3D::LINEAR
));
99 GLC(context3d
, context3d
->texParameteri(GraphicsContext3D::TEXTURE_2D
, GraphicsContext3D::TEXTURE_WRAP_S
, GraphicsContext3D::CLAMP_TO_EDGE
));
100 GLC(context3d
, context3d
->texParameteri(GraphicsContext3D::TEXTURE_2D
, GraphicsContext3D::TEXTURE_WRAP_T
, GraphicsContext3D::CLAMP_TO_EDGE
));
102 if (m_useTextureUsageHint
&& hint
== TextureUsageFramebuffer
)
103 GLC(context3d
, context3d
->texParameteri(GraphicsContext3D::TEXTURE_2D
, Extensions3DChromium::GL_TEXTURE_USAGE_ANGLE
, Extensions3DChromium::GL_FRAMEBUFFER_ATTACHMENT_ANGLE
));
104 if (m_useTextureStorageExt
&& isTextureFormatSupportedForStorage(format
)) {
105 GC3Denum storageFormat
= textureToStorageFormat(format
);
106 GLC(context3d
, context3d
->texStorage2DEXT(GraphicsContext3D::TEXTURE_2D
, 1, storageFormat
, size
.width(), size
.height()));
108 GLC(context3d
, context3d
->texImage2D(GraphicsContext3D::TEXTURE_2D
, 0, format
, size
.width(), size
.height(), 0, format
, GraphicsContext3D::UNSIGNED_BYTE
, 0));
109 ResourceId id
= m_nextId
++;
110 Resource
resource(textureId
, pool
, size
, format
);
111 m_resources
.add(id
, resource
);
115 CCResourceProvider::ResourceId
CCResourceProvider::createBitmap(int pool
, const IntSize
& size
)
117 ASSERT(CCProxy::isImplThread());
119 uint8_t* pixels
= new uint8_t[size
.width() * size
.height() * 4];
121 ResourceId id
= m_nextId
++;
122 Resource
resource(pixels
, pool
, size
, GraphicsContext3D::RGBA
);
123 m_resources
.add(id
, resource
);
127 CCResourceProvider::ResourceId
CCResourceProvider::createResourceFromExternalTexture(unsigned textureId
)
129 ASSERT(CCProxy::isImplThread());
130 ASSERT(m_context
->context3D());
131 ResourceId id
= m_nextId
++;
132 Resource
resource(textureId
, 0, IntSize(), 0);
133 resource
.external
= true;
134 m_resources
.add(id
, resource
);
138 void CCResourceProvider::deleteResource(ResourceId id
)
140 ASSERT(CCProxy::isImplThread());
141 ResourceMap::iterator it
= m_resources
.find(id
);
142 ASSERT(it
!= m_resources
.end());
143 ASSERT(!it
->second
.lockedForWrite
);
144 ASSERT(!it
->second
.lockForReadCount
);
146 if (it
->second
.glId
&& !it
->second
.external
) {
147 WebGraphicsContext3D
* context3d
= m_context
->context3D();
149 GLC(context3d
, context3d
->deleteTexture(it
->second
.glId
));
151 if (it
->second
.pixels
)
152 delete it
->second
.pixels
;
154 m_resources
.remove(it
);
157 void CCResourceProvider::deleteOwnedResources(int pool
)
159 ASSERT(CCProxy::isImplThread());
160 ResourceIdArray toDelete
;
161 for (ResourceMap::iterator it
= m_resources
.begin(); it
!= m_resources
.end(); ++it
) {
162 if (it
->second
.pool
== pool
&& !it
->second
.external
)
163 toDelete
.append(it
->first
);
165 for (ResourceIdArray::iterator it
= toDelete
.begin(); it
!= toDelete
.end(); ++it
)
169 CCResourceProvider::ResourceType
CCResourceProvider::resourceType(ResourceId id
)
171 ResourceMap::iterator it
= m_resources
.find(id
);
172 ASSERT(it
!= m_resources
.end());
173 return it
->second
.type
;
176 void CCResourceProvider::upload(ResourceId id
, const uint8_t* image
, const IntRect
& imageRect
, const IntRect
& sourceRect
, const IntSize
& destOffset
)
178 ASSERT(CCProxy::isImplThread());
179 ResourceMap::iterator it
= m_resources
.find(id
);
180 ASSERT(it
!= m_resources
.end());
181 ASSERT(!it
->second
.lockedForWrite
);
182 ASSERT(!it
->second
.lockForReadCount
);
183 ASSERT(!it
->second
.external
);
185 if (it
->second
.glId
) {
186 WebGraphicsContext3D
* context3d
= m_context
->context3D();
188 ASSERT(m_texSubImage
.get());
189 context3d
->bindTexture(GraphicsContext3D::TEXTURE_2D
, it
->second
.glId
);
190 m_texSubImage
->upload(image
, imageRect
, sourceRect
, destOffset
, it
->second
.format
, context3d
);
193 if (it
->second
.pixels
) {
195 srcFull
.setConfig(SkBitmap::kARGB_8888_Config
, imageRect
.width(), imageRect
.height());
196 srcFull
.setPixels(const_cast<uint8_t*>(image
));
198 SkIRect skSourceRect
= SkIRect::MakeXYWH(sourceRect
.x(), sourceRect
.y(), sourceRect
.width(), sourceRect
.height());
199 skSourceRect
.offset(-imageRect
.x(), -imageRect
.y());
200 srcFull
.extractSubset(&srcSubset
, skSourceRect
);
202 ScopedWriteLockSoftware
lock(this, id
);
203 SkCanvas
* dest
= lock
.skCanvas();
204 dest
->writePixels(srcSubset
, destOffset
.width(), destOffset
.height());
208 void CCResourceProvider::flush()
210 ASSERT(CCProxy::isImplThread());
211 WebGraphicsContext3D
* context3d
= m_context
->context3D();
216 bool CCResourceProvider::shallowFlushIfSupported()
218 ASSERT(CCProxy::isImplThread());
219 WebGraphicsContext3D
* context3d
= m_context
->context3D();
220 if (!context3d
|| !m_useShallowFlush
)
223 context3d
->shallowFlushCHROMIUM();
227 const CCResourceProvider::Resource
* CCResourceProvider::lockForRead(ResourceId id
)
229 ASSERT(CCProxy::isImplThread());
230 ResourceMap::iterator it
= m_resources
.find(id
);
231 ASSERT(it
!= m_resources
.end());
232 ASSERT(!it
->second
.lockedForWrite
);
233 it
->second
.lockForReadCount
++;
237 void CCResourceProvider::unlockForRead(ResourceId id
)
239 ASSERT(CCProxy::isImplThread());
240 ResourceMap::iterator it
= m_resources
.find(id
);
241 ASSERT(it
!= m_resources
.end());
242 ASSERT(it
->second
.lockForReadCount
> 0);
243 it
->second
.lockForReadCount
--;
246 const CCResourceProvider::Resource
* CCResourceProvider::lockForWrite(ResourceId id
)
248 ASSERT(CCProxy::isImplThread());
249 ResourceMap::iterator it
= m_resources
.find(id
);
250 ASSERT(it
!= m_resources
.end());
251 ASSERT(!it
->second
.lockedForWrite
);
252 ASSERT(!it
->second
.lockForReadCount
);
253 ASSERT(!it
->second
.external
);
254 it
->second
.lockedForWrite
= true;
258 void CCResourceProvider::unlockForWrite(ResourceId id
)
260 ASSERT(CCProxy::isImplThread());
261 ResourceMap::iterator it
= m_resources
.find(id
);
262 ASSERT(it
!= m_resources
.end());
263 ASSERT(it
->second
.lockedForWrite
);
264 ASSERT(!it
->second
.external
);
265 it
->second
.lockedForWrite
= false;
268 CCResourceProvider::ScopedReadLockGL::ScopedReadLockGL(CCResourceProvider
* resourceProvider
, CCResourceProvider::ResourceId resourceId
)
269 : m_resourceProvider(resourceProvider
)
270 , m_resourceId(resourceId
)
271 , m_textureId(resourceProvider
->lockForRead(resourceId
)->glId
)
276 CCResourceProvider::ScopedReadLockGL::~ScopedReadLockGL()
278 m_resourceProvider
->unlockForRead(m_resourceId
);
281 CCResourceProvider::ScopedWriteLockGL::ScopedWriteLockGL(CCResourceProvider
* resourceProvider
, CCResourceProvider::ResourceId resourceId
)
282 : m_resourceProvider(resourceProvider
)
283 , m_resourceId(resourceId
)
284 , m_textureId(resourceProvider
->lockForWrite(resourceId
)->glId
)
289 CCResourceProvider::ScopedWriteLockGL::~ScopedWriteLockGL()
291 m_resourceProvider
->unlockForWrite(m_resourceId
);
294 void CCResourceProvider::populateSkBitmapWithResource(SkBitmap
* skBitmap
, const Resource
* resource
)
296 ASSERT(resource
->pixels
);
297 ASSERT(resource
->format
== GraphicsContext3D::RGBA
);
298 skBitmap
->setConfig(SkBitmap::kARGB_8888_Config
, resource
->size
.width(), resource
->size
.height());
299 skBitmap
->setPixels(resource
->pixels
);
302 CCResourceProvider::ScopedReadLockSoftware::ScopedReadLockSoftware(CCResourceProvider
* resourceProvider
, CCResourceProvider::ResourceId resourceId
)
303 : m_resourceProvider(resourceProvider
)
304 , m_resourceId(resourceId
)
306 CCResourceProvider::populateSkBitmapWithResource(&m_skBitmap
, resourceProvider
->lockForRead(resourceId
));
309 CCResourceProvider::ScopedReadLockSoftware::~ScopedReadLockSoftware()
311 m_resourceProvider
->unlockForRead(m_resourceId
);
314 CCResourceProvider::ScopedWriteLockSoftware::ScopedWriteLockSoftware(CCResourceProvider
* resourceProvider
, CCResourceProvider::ResourceId resourceId
)
315 : m_resourceProvider(resourceProvider
)
316 , m_resourceId(resourceId
)
318 CCResourceProvider::populateSkBitmapWithResource(&m_skBitmap
, resourceProvider
->lockForWrite(resourceId
));
319 m_skCanvas
.setBitmapDevice(m_skBitmap
);
322 CCResourceProvider::ScopedWriteLockSoftware::~ScopedWriteLockSoftware()
324 m_resourceProvider
->unlockForWrite(m_resourceId
);
327 CCResourceProvider::CCResourceProvider(CCGraphicsContext
* context
)
331 , m_defaultResourceType(GLTexture
)
332 , m_useTextureStorageExt(false)
333 , m_useTextureUsageHint(false)
334 , m_useShallowFlush(false)
335 , m_maxTextureSize(0)
339 bool CCResourceProvider::initialize()
341 ASSERT(CCProxy::isImplThread());
342 WebGraphicsContext3D
* context3d
= m_context
->context3D();
344 m_maxTextureSize
= INT_MAX
;
346 // FIXME: Implement this path for software compositing.
349 if (!context3d
->makeContextCurrent())
352 std::string extensionsString
= UTF16ToASCII(context3d
->getString(GraphicsContext3D::EXTENSIONS
));
353 std::vector
<std::string
> extensions
;
354 base::SplitString(extensionsString
, ' ', &extensions
);
355 bool useMapSub
= false;
356 for (size_t i
= 0; i
< extensions
.size(); ++i
) {
357 if (extensions
[i
] == "GL_EXT_texture_storage")
358 m_useTextureStorageExt
= true;
359 else if (extensions
[i
] == "GL_ANGLE_texture_usage")
360 m_useTextureUsageHint
= true;
361 else if (extensions
[i
] == "GL_CHROMIUM_map_sub")
363 else if (extensions
[i
] == "GL_CHROMIUM_shallow_flush")
364 m_useShallowFlush
= true;
367 m_texSubImage
= adoptPtr(new LayerTextureSubImage(useMapSub
));
368 GLC(context3d
, context3d
->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE
, &m_maxTextureSize
));
372 int CCResourceProvider::createChild(int pool
)
374 ASSERT(CCProxy::isImplThread());
376 childInfo
.pool
= pool
;
377 int child
= m_nextChild
++;
378 m_children
.add(child
, childInfo
);
382 void CCResourceProvider::destroyChild(int child
)
384 ASSERT(CCProxy::isImplThread());
385 ChildMap::iterator it
= m_children
.find(child
);
386 ASSERT(it
!= m_children
.end());
387 deleteOwnedResources(it
->second
.pool
);
388 m_children
.remove(it
);
392 const CCResourceProvider::ResourceIdMap
& CCResourceProvider::getChildToParentMap(int child
) const
394 ASSERT(CCProxy::isImplThread());
395 ChildMap::const_iterator it
= m_children
.find(child
);
396 ASSERT(it
!= m_children
.end());
397 return it
->second
.childToParentMap
;
400 CCResourceProvider::TransferableResourceList
CCResourceProvider::prepareSendToParent(const ResourceIdArray
& resources
)
402 ASSERT(CCProxy::isImplThread());
403 TransferableResourceList list
;
405 WebGraphicsContext3D
* context3d
= m_context
->context3D();
406 if (!context3d
|| !context3d
->makeContextCurrent()) {
407 // FIXME: Implement this path for software compositing.
410 for (ResourceIdArray::const_iterator it
= resources
.begin(); it
!= resources
.end(); ++it
) {
411 TransferableResource resource
;
412 if (transferResource(context3d
, *it
, &resource
)) {
413 m_resources
.find(*it
)->second
.exported
= true;
414 list
.resources
.append(resource
);
417 if (list
.resources
.size())
418 list
.syncPoint
= context3d
->insertSyncPoint();
422 CCResourceProvider::TransferableResourceList
CCResourceProvider::prepareSendToChild(int child
, const ResourceIdArray
& resources
)
424 ASSERT(CCProxy::isImplThread());
425 TransferableResourceList list
;
427 WebGraphicsContext3D
* context3d
= m_context
->context3D();
428 if (!context3d
|| !context3d
->makeContextCurrent()) {
429 // FIXME: Implement this path for software compositing.
432 Child
& childInfo
= m_children
.find(child
)->second
;
433 for (ResourceIdArray::const_iterator it
= resources
.begin(); it
!= resources
.end(); ++it
) {
434 TransferableResource resource
;
435 if (!transferResource(context3d
, *it
, &resource
))
436 ASSERT_NOT_REACHED();
437 resource
.id
= childInfo
.parentToChildMap
.get(*it
);
438 childInfo
.parentToChildMap
.remove(*it
);
439 childInfo
.childToParentMap
.remove(resource
.id
);
440 list
.resources
.append(resource
);
443 if (list
.resources
.size())
444 list
.syncPoint
= context3d
->insertSyncPoint();
448 void CCResourceProvider::receiveFromChild(int child
, const TransferableResourceList
& resources
)
450 ASSERT(CCProxy::isImplThread());
451 WebGraphicsContext3D
* context3d
= m_context
->context3D();
452 if (!context3d
|| !context3d
->makeContextCurrent()) {
453 // FIXME: Implement this path for software compositing.
456 if (resources
.syncPoint
) {
457 // NOTE: If the parent is a browser and the child a renderer, the parent
458 // is not supposed to have its context wait, because that could induce
459 // deadlocks and/or security issues. The caller is responsible for
460 // waiting asynchronously, and resetting syncPoint before calling this.
461 // However if the parent is a renderer (e.g. browser tag), it may be ok
462 // (and is simpler) to wait.
463 GLC(context3d
, context3d
->waitSyncPoint(resources
.syncPoint
));
465 Child
& childInfo
= m_children
.find(child
)->second
;
466 for (Vector
<TransferableResource
>::const_iterator it
= resources
.resources
.begin(); it
!= resources
.resources
.end(); ++it
) {
468 GLC(context3d
, textureId
= context3d
->createTexture());
469 GLC(context3d
, context3d
->bindTexture(GraphicsContext3D::TEXTURE_2D
, textureId
));
470 GLC(context3d
, context3d
->consumeTextureCHROMIUM(GraphicsContext3D::TEXTURE_2D
, it
->mailbox
.name
));
471 ResourceId id
= m_nextId
++;
472 Resource
resource(textureId
, childInfo
.pool
, it
->size
, it
->format
);
473 m_resources
.add(id
, resource
);
474 m_mailboxes
.append(it
->mailbox
);
475 childInfo
.parentToChildMap
.add(id
, it
->id
);
476 childInfo
.childToParentMap
.add(it
->id
, id
);
480 void CCResourceProvider::receiveFromParent(const TransferableResourceList
& resources
)
482 ASSERT(CCProxy::isImplThread());
483 WebGraphicsContext3D
* context3d
= m_context
->context3D();
484 if (!context3d
|| !context3d
->makeContextCurrent()) {
485 // FIXME: Implement this path for software compositing.
488 if (resources
.syncPoint
)
489 GLC(context3d
, context3d
->waitSyncPoint(resources
.syncPoint
));
490 for (Vector
<TransferableResource
>::const_iterator it
= resources
.resources
.begin(); it
!= resources
.resources
.end(); ++it
) {
491 Resource
& resource
= m_resources
.find(it
->id
)->second
;
492 ASSERT(resource
.exported
);
493 resource
.exported
= false;
494 GLC(context3d
, context3d
->bindTexture(GraphicsContext3D::TEXTURE_2D
, resource
.glId
));
495 GLC(context3d
, context3d
->consumeTextureCHROMIUM(GraphicsContext3D::TEXTURE_2D
, it
->mailbox
.name
));
496 m_mailboxes
.append(it
->mailbox
);
500 bool CCResourceProvider::transferResource(WebGraphicsContext3D
* context
, ResourceId id
, TransferableResource
* resource
)
502 ASSERT(CCProxy::isImplThread());
503 ResourceMap::const_iterator it
= m_resources
.find(id
);
504 ASSERT(it
!= m_resources
.end());
505 ASSERT(!it
->second
.lockedForWrite
);
506 ASSERT(!it
->second
.lockForReadCount
);
507 ASSERT(!it
->second
.external
);
508 if (it
->second
.exported
)
511 resource
->format
= it
->second
.format
;
512 resource
->size
= it
->second
.size
;
513 if (!m_mailboxes
.isEmpty())
514 resource
->mailbox
= m_mailboxes
.takeFirst();
516 GLC(context
, context
->genMailboxCHROMIUM(resource
->mailbox
.name
));
517 GLC(context
, context
->bindTexture(GraphicsContext3D::TEXTURE_2D
, it
->second
.glId
));
518 GLC(context
, context
->produceTextureCHROMIUM(GraphicsContext3D::TEXTURE_2D
, resource
->mailbox
.name
));
522 void CCResourceProvider::trimMailboxDeque()
524 // Trim the mailbox deque to the maximum number of resources we may need to
526 // If we have a parent, any non-external resource not already transfered is
527 // eligible to be sent to the parent. Otherwise, all resources belonging to
528 // a child might need to be sent back to the child.
529 size_t maxMailboxCount
= 0;
530 if (m_context
->capabilities().hasParentCompositor
) {
531 for (ResourceMap::iterator it
= m_resources
.begin(); it
!= m_resources
.end(); ++it
) {
532 if (!it
->second
.exported
&& !it
->second
.external
)
536 HashSet
<int> childPoolSet
;
537 for (ChildMap::iterator it
= m_children
.begin(); it
!= m_children
.end(); ++it
)
538 childPoolSet
.add(it
->second
.pool
);
539 for (ResourceMap::iterator it
= m_resources
.begin(); it
!= m_resources
.end(); ++it
) {
540 if (childPoolSet
.contains(it
->second
.pool
))
544 while (m_mailboxes
.size() > maxMailboxCount
)
545 m_mailboxes
.removeFirst();