diff options
Diffstat (limited to 'src/core/delegated_frame_node.cpp')
-rw-r--r-- | src/core/delegated_frame_node.cpp | 407 |
1 files changed, 213 insertions, 194 deletions
diff --git a/src/core/delegated_frame_node.cpp b/src/core/delegated_frame_node.cpp index e12873c81..8d09794be 100644 --- a/src/core/delegated_frame_node.cpp +++ b/src/core/delegated_frame_node.cpp @@ -55,6 +55,7 @@ #include "base/bind.h" #include "cc/output/delegated_frame_data.h" #include "cc/quads/checkerboard_draw_quad.h" +#include "cc/quads/debug_border_draw_quad.h" #include "cc/quads/draw_quad.h" #include "cc/quads/render_pass_draw_quad.h" #include "cc/quads/solid_color_draw_quad.h" @@ -62,80 +63,60 @@ #include "cc/quads/texture_draw_quad.h" #include "cc/quads/tile_draw_quad.h" #include "cc/quads/yuv_video_draw_quad.h" +#include "content/common/host_shared_bitmap_manager.h" #include <QOpenGLContext> -#include <QOpenGLFramebufferObject> #include <QOpenGLFunctions> -#include <QSGAbstractRenderer> -#include <QSGEngine> #include <QSGSimpleRectNode> #include <QSGSimpleTextureNode> #include <QSGTexture> +#include <private/qsgadaptationlayer_p.h> #if !defined(QT_NO_EGL) #include <EGL/egl.h> #include <EGL/eglext.h> #endif -class RenderPassTexture : public QSGTexture, protected QOpenGLFunctions -{ -public: - RenderPassTexture(const cc::RenderPass::Id &id); - - const cc::RenderPass::Id &id() const { return m_id; } - void bind(); - - int textureId() const { return m_fbo ? m_fbo->texture() : 0; } - QSize textureSize() const { return m_rect.size(); } - bool hasAlphaChannel() const { return m_format != GL_RGB; } - bool hasMipmaps() const { return false; } - - void setRect(const QRect &rect) { m_rect = rect; } - void setFormat(GLenum format) { m_format = format; } - QSGNode *rootNode() { return m_rootNode.data(); } - - void grab(); - -private: - cc::RenderPass::Id m_id; - QRect m_rect; - GLenum m_format; - - QScopedPointer<QSGEngine> m_sgEngine; - QScopedPointer<QSGRootNode> m_rootNode; - QScopedPointer<QSGAbstractRenderer> m_renderer; - QScopedPointer<QOpenGLFramebufferObject> m_fbo; -}; - class MailboxTexture : public QSGTexture, protected QOpenGLFunctions { public: - MailboxTexture(const cc::TransferableResource &resource); + MailboxTexture(const gpu::MailboxHolder &mailboxHolder, const QSize textureSize); virtual int textureId() const Q_DECL_OVERRIDE { return m_textureId; } - void setTextureSize(const QSize& size) { m_textureSize = size; } virtual QSize textureSize() const Q_DECL_OVERRIDE { return m_textureSize; } virtual bool hasAlphaChannel() const Q_DECL_OVERRIDE { return m_hasAlpha; } void setHasAlphaChannel(bool hasAlpha) { m_hasAlpha = hasAlpha; } virtual bool hasMipmaps() const Q_DECL_OVERRIDE { return false; } virtual void bind() Q_DECL_OVERRIDE; - bool needsToFetch() const { return !m_textureId; } - cc::TransferableResource &resource() { return m_resource; } - cc::ReturnedResource returnResource(); + gpu::MailboxHolder &mailboxHolder() { return m_mailboxHolder; } void fetchTexture(gpu::gles2::MailboxManager *mailboxManager); void setTarget(GLenum target); - void incImportCount() { ++m_importCount; } private: - cc::TransferableResource m_resource; + gpu::MailboxHolder m_mailboxHolder; int m_textureId; QSize m_textureSize; bool m_hasAlpha; GLenum m_target; - int m_importCount; #ifdef Q_OS_QNX EGLStreamData m_eglStreamData; #endif }; +class ResourceHolder { +public: + ResourceHolder(const cc::TransferableResource &resource); + QSharedPointer<QSGTexture> initTexture(bool quadIsAllOpaque, RenderWidgetHostViewQtDelegate *apiDelegate = 0); + QSGTexture *texture() const { return m_texture.data(); } + cc::TransferableResource &transferableResource() { return m_resource; } + cc::ReturnedResource returnResource(); + void incImportCount() { ++m_importCount; } + bool needsToFetch() const { return !m_resource.is_software && m_texture && !m_texture.data()->textureId(); } + +private: + QWeakPointer<QSGTexture> m_texture; + cc::TransferableResource m_resource; + int m_importCount; +}; + class RectClipNode : public QSGClipNode { public: @@ -144,23 +125,13 @@ private: QSGGeometry m_geometry; }; -static inline QSharedPointer<RenderPassTexture> findRenderPassTexture(const cc::RenderPass::Id &id, const QList<QSharedPointer<RenderPassTexture> > &list) -{ - Q_FOREACH (const QSharedPointer<RenderPassTexture> &texture, list) - if (texture->id() == id) - return texture; - return QSharedPointer<RenderPassTexture>(); -} - -static inline QSharedPointer<MailboxTexture> &findMailboxTexture(unsigned resourceId - , QHash<unsigned, QSharedPointer<MailboxTexture> > &usedTextures - , QHash<unsigned, QSharedPointer<MailboxTexture> > &candidateTextures) +static inline QSharedPointer<QSGLayer> findRenderPassLayer(const cc::RenderPass::Id &id, const QList<QPair<cc::RenderPass::Id, QSharedPointer<QSGLayer> > > &list) { - QSharedPointer<MailboxTexture> &texture = usedTextures[resourceId]; - if (!texture) - texture = candidateTextures.take(resourceId); - Q_ASSERT(texture); - return texture; + typedef QPair<cc::RenderPass::Id, QSharedPointer<QSGLayer> > Pair; + Q_FOREACH (const Pair &pair, list) + if (pair.first == id) + return pair.second; + return QSharedPointer<QSGLayer>(); } static QSGNode *buildRenderPassChain(QSGNode *chainParent) @@ -294,59 +265,20 @@ static void deleteChromiumSync(gfx::TransferableFence *sync) Q_ASSERT(!*sync); } -RenderPassTexture::RenderPassTexture(const cc::RenderPass::Id &id) - : QSGTexture() - , m_id(id) - , m_format(GL_RGBA) - , m_sgEngine(new QSGEngine) - , m_rootNode(new QSGRootNode) -{ - initializeOpenGLFunctions(); -} - -void RenderPassTexture::bind() -{ - glBindTexture(GL_TEXTURE_2D, m_fbo ? m_fbo->texture() : 0); - updateBindOptions(); -} - -void RenderPassTexture::grab() -{ - if (!m_renderer) { - m_sgEngine->initialize(QOpenGLContext::currentContext()); - m_renderer.reset(m_sgEngine->createRenderer()); - m_renderer->setRootNode(m_rootNode.data()); - } - - if (!m_fbo || m_fbo->size() != m_rect.size() || m_fbo->format().internalTextureFormat() != m_format) - { - QOpenGLFramebufferObjectFormat format; - format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); - format.setInternalTextureFormat(m_format); - - m_fbo.reset(new QOpenGLFramebufferObject(m_rect.size(), format)); - glBindTexture(GL_TEXTURE_2D, m_fbo->texture()); - updateBindOptions(true); - } - - m_renderer->setDeviceRect(m_rect.size()); - m_renderer->setViewportRect(m_rect.size()); - QRectF mirrored(m_rect.left(), m_rect.bottom(), m_rect.width(), -m_rect.height()); - m_renderer->setProjectionMatrixToRect(mirrored); - m_renderer->setClearColor(Qt::transparent); - - m_renderer->renderScene(m_fbo->handle()); -} - -MailboxTexture::MailboxTexture(const cc::TransferableResource &resource) - : m_resource(resource) +MailboxTexture::MailboxTexture(const gpu::MailboxHolder &mailboxHolder, const QSize textureSize) + : m_mailboxHolder(mailboxHolder) , m_textureId(0) - , m_textureSize(toQt(resource.size)) + , m_textureSize(textureSize) , m_hasAlpha(false) , m_target(GL_TEXTURE_2D) - , m_importCount(1) { initializeOpenGLFunctions(); + + // Assume that resources without a size will be used with a full source rect. + // Setting a size of 1x1 will let any texture node compute a normalized source + // rect of (0, 0) to (1, 1) while an empty texture size would set (0, 0) on all corners. + if (m_textureSize.isEmpty()) + m_textureSize = QSize(1, 1); } void MailboxTexture::bind() @@ -373,7 +305,55 @@ void MailboxTexture::setTarget(GLenum target) m_target = target; } -cc::ReturnedResource MailboxTexture::returnResource() +void MailboxTexture::fetchTexture(gpu::gles2::MailboxManager *mailboxManager) +{ + gpu::gles2::Texture *tex = ConsumeTexture(mailboxManager, m_target, m_mailboxHolder.mailbox); + + // The texture might already have been deleted (e.g. when navigating away from a page). + if (tex) { + m_textureId = service_id(tex); +#ifdef Q_OS_QNX + if (m_target == GL_TEXTURE_EXTERNAL_OES) { + m_eglStreamData = eglstream_connect_consumer(tex); + } +#endif + } +} + +ResourceHolder::ResourceHolder(const cc::TransferableResource &resource) + : m_resource(resource) + , m_importCount(1) +{ +} + +QSharedPointer<QSGTexture> ResourceHolder::initTexture(bool quadNeedsBlending, RenderWidgetHostViewQtDelegate *apiDelegate) +{ + QSharedPointer<QSGTexture> texture = m_texture.toStrongRef(); + if (!texture) { + if (m_resource.is_software) { + Q_ASSERT(apiDelegate); + scoped_ptr<cc::SharedBitmap> sharedBitmap = content::HostSharedBitmapManager::current()->GetSharedBitmapFromId(m_resource.size, m_resource.mailbox_holder.mailbox); + // QSG interprets QImage::hasAlphaChannel meaning that a node should enable blending + // to draw it but Chromium keeps this information in the quads. + // The input format is currently always Format_ARGB32_Premultiplied, so assume that all + // alpha bytes are 0xff if quads aren't requesting blending and avoid the conversion + // from Format_ARGB32_Premultiplied to Format_RGB32 just to get hasAlphaChannel to + // return false. + QImage::Format format = quadNeedsBlending ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; + QImage image(sharedBitmap->pixels(), m_resource.size.width(), m_resource.size.height(), format); + texture.reset(apiDelegate->createTextureFromImage(image.copy())); + } else { + texture.reset(new MailboxTexture(m_resource.mailbox_holder, toQt(m_resource.size))); + static_cast<MailboxTexture *>(texture.data())->setHasAlphaChannel(quadNeedsBlending); + } + m_texture = texture; + } + // All quads using a resource should request the same blending state. + Q_ASSERT(texture->hasAlphaChannel() || !quadNeedsBlending); + return texture; +} + +cc::ReturnedResource ResourceHolder::returnResource() { cc::ReturnedResource returned; // The ResourceProvider ensures that the resource isn't used by the parent compositor's GL @@ -389,20 +369,6 @@ cc::ReturnedResource MailboxTexture::returnResource() return returned; } -void MailboxTexture::fetchTexture(gpu::gles2::MailboxManager *mailboxManager) -{ - gpu::gles2::Texture *tex = ConsumeTexture(mailboxManager, m_target, m_resource.mailbox_holder.mailbox); - - // The texture might already have been deleted (e.g. when navigating away from a page). - if (tex) { - m_textureId = service_id(tex); -#ifdef Q_OS_QNX - if (m_target == GL_TEXTURE_EXTERNAL_OES) { - m_eglStreamData = eglstream_connect_consumer(tex); - } -#endif - } -} RectClipNode::RectClipNode(const QRectF &rect) : m_geometry(QSGGeometry::defaultAttributes_Point2D(), 4) @@ -429,9 +395,12 @@ void DelegatedFrameNode::preprocess() // We can now wait for the Chromium GPU thread to produce textures that will be // rendered on our quads and fetch the IDs from the mailboxes we were given. QList<MailboxTexture *> mailboxesToFetch; - Q_FOREACH (const QSharedPointer<MailboxTexture> &mailboxTexture, m_data->mailboxTextures.values()) - if (mailboxTexture->needsToFetch()) - mailboxesToFetch.append(mailboxTexture.data()); + typedef QHash<unsigned, QSharedPointer<ResourceHolder> >::const_iterator ResourceHolderIterator; + ResourceHolderIterator end = m_chromiumCompositorData->resourceHolders.constEnd(); + for (ResourceHolderIterator it = m_chromiumCompositorData->resourceHolders.constBegin(); it != end ; ++it) { + if ((*it)->needsToFetch()) + mailboxesToFetch.append(static_cast<MailboxTexture *>((*it)->texture())); + } if (!mailboxesToFetch.isEmpty()) { QMap<uint32, gfx::TransferableFence> transferredFences; @@ -442,7 +411,7 @@ void DelegatedFrameNode::preprocess() Q_FOREACH (MailboxTexture *mailboxTexture, mailboxesToFetch) { m_numPendingSyncPoints++; - AddSyncPointCallbackOnGpuThread(gpuMessageLoop, syncPointManager, mailboxTexture->resource().mailbox_holder.sync_point, base::Bind(&DelegatedFrameNode::syncPointRetired, this, &mailboxesToFetch)); + AddSyncPointCallbackOnGpuThread(gpuMessageLoop, syncPointManager, mailboxTexture->mailboxHolder().sync_point, base::Bind(&DelegatedFrameNode::syncPointRetired, this, &mailboxesToFetch)); } m_mailboxesFetchedWaitCond.wait(&m_mutex); @@ -452,7 +421,7 @@ void DelegatedFrameNode::preprocess() // Tell GL to wait until Chromium is done generating resource textures on the GPU thread // for each mailbox. We can safely start referencing those textures onto geometries afterward. Q_FOREACH (MailboxTexture *mailboxTexture, mailboxesToFetch) { - gfx::TransferableFence fence = transferredFences.take(mailboxTexture->resource().mailbox_holder.sync_point); + gfx::TransferableFence fence = transferredFences.take(mailboxTexture->mailboxHolder().sync_point); waitChromiumSync(&fence); deleteChromiumSync(&fence); } @@ -464,14 +433,19 @@ void DelegatedFrameNode::preprocess() } // Then render any intermediate RenderPass in order. - Q_FOREACH (const QSharedPointer<RenderPassTexture> &renderPass, m_renderPassTextures) - renderPass->grab(); + typedef QPair<cc::RenderPass::Id, QSharedPointer<QSGLayer> > Pair; + Q_FOREACH (const Pair &pair, m_sgObjects.renderPassLayers) { + // The layer is non-live, request a one-time update here. + pair.second->scheduleUpdate(); + // Proceed with the actual update. + pair.second->updateTexture(); + } } -void DelegatedFrameNode::commit(DelegatedFrameNodeData* data, cc::ReturnedResourceArray *resourcesToRelease) +void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, cc::ReturnedResourceArray *resourcesToRelease, RenderWidgetHostViewQtDelegate *apiDelegate) { - m_data = data; - cc::DelegatedFrameData* frameData = m_data->frameData.get(); + m_chromiumCompositorData = chromiumCompositorData; + cc::DelegatedFrameData* frameData = m_chromiumCompositorData->frameData.get(); if (!frameData) return; @@ -479,14 +453,15 @@ void DelegatedFrameNode::commit(DelegatedFrameNodeData* data, cc::ReturnedResour // countering the scale of devicePixel-scaled tiles when rendering them // to the final surface. QMatrix4x4 matrix; - matrix.scale(1 / m_data->frameDevicePixelRatio, 1 / m_data->frameDevicePixelRatio); + matrix.scale(1 / m_chromiumCompositorData->frameDevicePixelRatio, 1 / m_chromiumCompositorData->frameDevicePixelRatio); setMatrix(matrix); - // Keep the old texture lists around to find the ones we can re-use. - QList<QSharedPointer<RenderPassTexture> > oldRenderPassTextures; - m_renderPassTextures.swap(oldRenderPassTextures); - QHash<unsigned, QSharedPointer<MailboxTexture> > mailboxTextureCandidates; - m_data->mailboxTextures.swap(mailboxTextureCandidates); + // Keep the old objects in scope to hold a ref on layers, resources and textures + // that we can re-use. Destroy the remaining objects before returning. + SGObjects previousSGObjects; + qSwap(m_sgObjects, previousSGObjects); + QHash<unsigned, QSharedPointer<ResourceHolder> > resourceCandidates; + qSwap(m_chromiumCompositorData->resourceHolders, resourceCandidates); // A frame's resource_list only contains the new resources to be added to the scene. Quads can // still reference resources that were added in previous frames. Add them to the list of @@ -494,14 +469,21 @@ void DelegatedFrameNode::commit(DelegatedFrameNodeData* data, cc::ReturnedResour // to the producing child compositor. for (unsigned i = 0; i < frameData->resource_list.size(); ++i) { const cc::TransferableResource &res = frameData->resource_list.at(i); - if (QSharedPointer<MailboxTexture> texture = mailboxTextureCandidates.value(res.id)) - texture->incImportCount(); + if (QSharedPointer<ResourceHolder> resource = resourceCandidates.value(res.id)) + resource->incImportCount(); else - mailboxTextureCandidates[res.id] = QSharedPointer<MailboxTexture>(new MailboxTexture(res)); + resourceCandidates[res.id] = QSharedPointer<ResourceHolder>(new ResourceHolder(res)); } frameData->resource_list.clear(); + // There is currently no way to know which and how quads changed since the last frame. + // We have to reconstruct the node chain with their geometries on every update. + // Intermediate render pass node chains are going to be destroyed when previousSGObjects + // goes out of scope together with any QSGLayer that could reference them. + while (QSGNode *oldChain = firstChild()) + delete oldChain; + // The RenderPasses list is actually a tree where a parent RenderPass is connected // to its dependencies through a RenderPass::Id reference in one or more RenderPassQuads. // The list is already ordered with intermediate RenderPasses placed before their @@ -515,21 +497,24 @@ void DelegatedFrameNode::commit(DelegatedFrameNodeData* data, cc::ReturnedResour QSGNode *renderPassParent = 0; if (pass != rootRenderPass) { - QSharedPointer<RenderPassTexture> rpTexture = findRenderPassTexture(pass->id, oldRenderPassTextures); - if (!rpTexture) - rpTexture = QSharedPointer<RenderPassTexture>(new RenderPassTexture(pass->id)); - m_renderPassTextures.append(rpTexture); - rpTexture->setRect(toQt(pass->output_rect)); - rpTexture->setFormat(pass->has_transparent_background ? GL_RGBA : GL_RGB); - renderPassParent = rpTexture->rootNode(); + QSharedPointer<QSGLayer> rpLayer = findRenderPassLayer(pass->id, previousSGObjects.renderPassLayers); + if (!rpLayer) { + rpLayer = QSharedPointer<QSGLayer>(apiDelegate->createLayer()); + // Avoid any premature texture update since we need to wait + // for the GPU thread to produce the dependent resources first. + rpLayer->setLive(false); + } + QSharedPointer<QSGRootNode> rootNode(new QSGRootNode); + rpLayer->setRect(toQt(pass->output_rect)); + rpLayer->setSize(toQt(pass->output_rect.size())); + rpLayer->setFormat(pass->has_transparent_background ? GL_RGBA : GL_RGB); + rpLayer->setItem(rootNode.data()); + m_sgObjects.renderPassLayers.append(QPair<cc::RenderPass::Id, QSharedPointer<QSGLayer> >(pass->id, rpLayer)); + m_sgObjects.renderPassRootNodes.append(rootNode); + renderPassParent = rootNode.data(); } else renderPassParent = this; - // There is currently no way to know which and how quads changed since the last frame. - // We have to reconstruct the node chain with their geometries on every update. - while (QSGNode *oldChain = renderPassParent->firstChild()) - delete oldChain; - QSGNode *renderPassChain = buildRenderPassChain(renderPassParent); const cc::SharedQuadState *currentLayerState = 0; QSGNode *currentLayerChain = 0; @@ -555,37 +540,28 @@ void DelegatedFrameNode::commit(DelegatedFrameNodeData* data, cc::ReturnedResour break; } case cc::DrawQuad::RENDER_PASS: { const cc::RenderPassDrawQuad *renderPassQuad = cc::RenderPassDrawQuad::MaterialCast(quad); - QSGTexture *texture = findRenderPassTexture(renderPassQuad->render_pass_id, m_renderPassTextures).data(); + QSGTexture *layer = findRenderPassLayer(renderPassQuad->render_pass_id, m_sgObjects.renderPassLayers).data(); // cc::GLRenderer::DrawRenderPassQuad silently ignores missing render passes. - if (!texture) + if (!layer) continue; - QSGSimpleTextureNode *textureNode = new QSGSimpleTextureNode; - textureNode->setRect(toQt(quad->rect)); - textureNode->setTexture(texture); - currentLayerChain->appendChildNode(textureNode); + // Only QSGImageNode currently supports QSGLayer textures. + QSGImageNode *imageNode = apiDelegate->createImageNode(); + imageNode->setTargetRect(toQt(quad->rect)); + imageNode->setInnerTargetRect(toQt(quad->rect)); + imageNode->setTexture(layer); + imageNode->update(); + currentLayerChain->appendChildNode(imageNode); break; } case cc::DrawQuad::TEXTURE_CONTENT: { const cc::TextureDrawQuad *tquad = cc::TextureDrawQuad::MaterialCast(quad); - QSharedPointer<MailboxTexture> &texture = findMailboxTexture(tquad->resource_id, m_data->mailboxTextures, mailboxTextureCandidates); - - // FIXME: TransferableResource::size isn't always set properly for TextureDrawQuads, use the size of its DrawQuad::rect instead. - texture->setTextureSize(toQt(quad->rect.size())); - - // TransferableResource::format seems to always be GL_BGRA even though it might not - // contain any pixel with alpha < 1.0. The information about if they need blending - // for the contents itself is actually stored in quads. - // Tell the scene graph to enable blending for a texture only when at least one quad asks for it. - // Do not rely on DrawQuad::ShouldDrawWithBlending() since the shared_quad_state->opacity - // case will be handled by QtQuick by fetching this information from QSGOpacityNodes. - if (!quad->visible_rect.IsEmpty() && !quad->opaque_rect.Contains(quad->visible_rect)) - texture->setHasAlphaChannel(true); + ResourceHolder *resource = findAndHoldResource(tquad->resource_id, resourceCandidates); QSGSimpleTextureNode *textureNode = new QSGSimpleTextureNode; textureNode->setTextureCoordinatesTransform(tquad->flipped ? QSGSimpleTextureNode::MirrorVertically : QSGSimpleTextureNode::NoTransform); textureNode->setRect(toQt(quad->rect)); - textureNode->setFiltering(texture->resource().filter == GL_LINEAR ? QSGTexture::Linear : QSGTexture::Nearest); - textureNode->setTexture(texture.data()); + textureNode->setFiltering(resource->transferableResource().filter == GL_LINEAR ? QSGTexture::Linear : QSGTexture::Nearest); + textureNode->setTexture(initAndHoldTexture(resource, quad->ShouldDrawWithBlending(), apiDelegate)); currentLayerChain->appendChildNode(textureNode); break; } case cc::DrawQuad::SOLID_COLOR: { @@ -601,46 +577,67 @@ void DelegatedFrameNode::commit(DelegatedFrameNodeData* data, cc::ReturnedResour rectangleNode->setColor(toQt(scquad->color)); currentLayerChain->appendChildNode(rectangleNode); break; + } case cc::DrawQuad::DEBUG_BORDER: { + const cc::DebugBorderDrawQuad *dbquad = cc::DebugBorderDrawQuad::MaterialCast(quad); + QSGGeometryNode *geometryNode = new QSGGeometryNode; + + QSGGeometry *geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 4); + geometry->setDrawingMode(GL_LINE_LOOP); + geometry->setLineWidth(dbquad->width); + // QSGGeometry::updateRectGeometry would actually set the corners in the following order: + // top-left, bottom-left, top-right, bottom-right, leading to a nice criss cross, instead + // of having a closed loop. + const gfx::Rect &r(dbquad->rect); + geometry->vertexDataAsPoint2D()[0].set(r.x(), r.y()); + geometry->vertexDataAsPoint2D()[1].set(r.x() + r.width(), r.y()); + geometry->vertexDataAsPoint2D()[2].set(r.x() + r.width(), r.y() + r.height()); + geometry->vertexDataAsPoint2D()[3].set(r.x(), r.y() + r.height()); + geometryNode->setGeometry(geometry); + + QSGFlatColorMaterial *material = new QSGFlatColorMaterial; + material->setColor(toQt(dbquad->color)); + geometryNode->setMaterial(material); + + geometryNode->setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial); + currentLayerChain->appendChildNode(geometryNode); + break; } case cc::DrawQuad::TILED_CONTENT: { const cc::TileDrawQuad *tquad = cc::TileDrawQuad::MaterialCast(quad); - QSharedPointer<MailboxTexture> &texture = findMailboxTexture(tquad->resource_id, m_data->mailboxTextures, mailboxTextureCandidates); - - if (!quad->visible_rect.IsEmpty() && !quad->opaque_rect.Contains(quad->visible_rect)) - texture->setHasAlphaChannel(true); + ResourceHolder *resource = findAndHoldResource(tquad->resource_id, resourceCandidates); QSGSimpleTextureNode *textureNode = new QSGSimpleTextureNode; textureNode->setRect(toQt(quad->rect)); - textureNode->setFiltering(texture->resource().filter == GL_LINEAR ? QSGTexture::Linear : QSGTexture::Nearest); - textureNode->setTexture(texture.data()); - - // FIXME: Find out if we can implement a QSGSimpleTextureNode::setSourceRect instead of this hack. - // This has to be done at the end since many QSGSimpleTextureNode methods would overwrite this. - QSGGeometry::updateTexturedRectGeometry(textureNode->geometry(), textureNode->rect(), textureNode->texture()->convertToNormalizedSourceRect(toQt(tquad->tex_coord_rect))); + textureNode->setSourceRect(toQt(tquad->tex_coord_rect)); + textureNode->setFiltering(resource->transferableResource().filter == GL_LINEAR ? QSGTexture::Linear : QSGTexture::Nearest); + textureNode->setTexture(initAndHoldTexture(resource, quad->ShouldDrawWithBlending(), apiDelegate)); currentLayerChain->appendChildNode(textureNode); break; } case cc::DrawQuad::YUV_VIDEO_CONTENT: { const cc::YUVVideoDrawQuad *vquad = cc::YUVVideoDrawQuad::MaterialCast(quad); - QSharedPointer<MailboxTexture> &yTexture = findMailboxTexture(vquad->y_plane_resource_id, m_data->mailboxTextures, mailboxTextureCandidates); - QSharedPointer<MailboxTexture> &uTexture = findMailboxTexture(vquad->u_plane_resource_id, m_data->mailboxTextures, mailboxTextureCandidates); - QSharedPointer<MailboxTexture> &vTexture = findMailboxTexture(vquad->v_plane_resource_id, m_data->mailboxTextures, mailboxTextureCandidates); - - // Do not use a reference for this one, it might be null. - QSharedPointer<MailboxTexture> aTexture; + ResourceHolder *yResource = findAndHoldResource(vquad->y_plane_resource_id, resourceCandidates); + ResourceHolder *uResource = findAndHoldResource(vquad->u_plane_resource_id, resourceCandidates); + ResourceHolder *vResource = findAndHoldResource(vquad->v_plane_resource_id, resourceCandidates); + ResourceHolder *aResource = 0; // This currently requires --enable-vp8-alpha-playback and needs a video with alpha data to be triggered. if (vquad->a_plane_resource_id) - aTexture = findMailboxTexture(vquad->a_plane_resource_id, m_data->mailboxTextures, mailboxTextureCandidates); + aResource = findAndHoldResource(vquad->a_plane_resource_id, resourceCandidates); - YUVVideoNode *videoNode = new YUVVideoNode(yTexture.data(), uTexture.data(), vTexture.data(), aTexture.data(), toQt(vquad->tex_coord_rect)); + YUVVideoNode *videoNode = new YUVVideoNode( + initAndHoldTexture(yResource, quad->ShouldDrawWithBlending()), + initAndHoldTexture(uResource, quad->ShouldDrawWithBlending()), + initAndHoldTexture(vResource, quad->ShouldDrawWithBlending()), + aResource ? initAndHoldTexture(aResource, quad->ShouldDrawWithBlending()) : 0, toQt(vquad->tex_coord_rect)); videoNode->setRect(toQt(quad->rect)); currentLayerChain->appendChildNode(videoNode); break; #ifdef GL_OES_EGL_image_external } case cc::DrawQuad::STREAM_VIDEO_CONTENT: { const cc::StreamVideoDrawQuad *squad = cc::StreamVideoDrawQuad::MaterialCast(quad); - QSharedPointer<MailboxTexture> &texture = findMailboxTexture(squad->resource_id, m_data->mailboxTextures, mailboxTextureCandidates); + ResourceHolder *resource = findAndHoldResource(squad->resource_id, resourceCandidates); + MailboxTexture *texture = static_cast<MailboxTexture *>(initAndHoldTexture(resource, quad->ShouldDrawWithBlending())); texture->setTarget(GL_TEXTURE_EXTERNAL_OES); // since this is not default TEXTURE_2D type - StreamVideoNode *svideoNode = new StreamVideoNode(texture.data()); + StreamVideoNode *svideoNode = new StreamVideoNode(texture); svideoNode->setRect(toQt(squad->rect)); svideoNode->setTextureMatrix(toQt(squad->matrix.matrix())); currentLayerChain->appendChildNode(svideoNode); @@ -653,8 +650,30 @@ void DelegatedFrameNode::commit(DelegatedFrameNodeData* data, cc::ReturnedResour } // Send resources of remaining candidates back to the child compositors so that they can be freed or reused. - Q_FOREACH (const QSharedPointer<MailboxTexture> &mailboxTexture, mailboxTextureCandidates.values()) - resourcesToRelease->push_back(mailboxTexture->returnResource()); + typedef QHash<unsigned, QSharedPointer<ResourceHolder> >::const_iterator ResourceHolderIterator; + ResourceHolderIterator end = resourceCandidates.constEnd(); + for (ResourceHolderIterator it = resourceCandidates.constBegin(); it != end ; ++it) + resourcesToRelease->push_back((*it)->returnResource()); +} + +ResourceHolder *DelegatedFrameNode::findAndHoldResource(unsigned resourceId, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates) +{ + // ResourceHolders must survive when the scene graph destroys our node branch + QSharedPointer<ResourceHolder> &resource = m_chromiumCompositorData->resourceHolders[resourceId]; + if (!resource) + resource = candidates.take(resourceId); + Q_ASSERT(resource); + return resource.data(); +} + +QSGTexture *DelegatedFrameNode::initAndHoldTexture(ResourceHolder *resource, bool quadIsAllOpaque, RenderWidgetHostViewQtDelegate *apiDelegate) +{ + // QSGTextures must be destroyed in the scene graph thread as part of the QSGNode tree, + // so we can't store them with the ResourceHolder in m_chromiumCompositorData. + // Hold them through a QSharedPointer solely on the root DelegatedFrameNode of the web view + // and access them through a QWeakPointer from the resource holder to find them later. + m_sgObjects.textureStrongRefs.append(resource->initTexture(quadIsAllOpaque, apiDelegate)); + return m_sgObjects.textureStrongRefs.last().data(); } void DelegatedFrameNode::fetchTexturesAndUnlockQt(DelegatedFrameNode *frameNode, QList<MailboxTexture *> *mailboxesToFetch) |