Home | History | Annotate | Download | only in chromium
      1 /*
      2  * Copyright (C) 2010 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 
     33 #if USE(ACCELERATED_COMPOSITING)
     34 #include "VideoLayerChromium.h"
     35 
     36 #include "Extensions3DChromium.h"
     37 #include "GraphicsContext3D.h"
     38 #include "LayerRendererChromium.h"
     39 #include "NotImplemented.h"
     40 #include "RenderLayerBacking.h"
     41 #include "VideoFrameChromium.h"
     42 #include "VideoFrameProvider.h"
     43 #include "cc/CCLayerImpl.h"
     44 #include "cc/CCVideoLayerImpl.h"
     45 
     46 namespace WebCore {
     47 
     48 PassRefPtr<VideoLayerChromium> VideoLayerChromium::create(GraphicsLayerChromium* owner,
     49                                                           VideoFrameProvider* provider)
     50 {
     51     return adoptRef(new VideoLayerChromium(owner, provider));
     52 }
     53 
     54 VideoLayerChromium::VideoLayerChromium(GraphicsLayerChromium* owner, VideoFrameProvider* provider)
     55     : LayerChromium(owner)
     56     , m_skipsDraw(true)
     57     , m_frameFormat(VideoFrameChromium::Invalid)
     58     , m_provider(provider)
     59     , m_currentFrame(0)
     60 {
     61     resetFrameParameters();
     62 }
     63 
     64 VideoLayerChromium::~VideoLayerChromium()
     65 {
     66     cleanupResources();
     67     deleteTexturesInUse();
     68 }
     69 
     70 PassRefPtr<CCLayerImpl> VideoLayerChromium::createCCLayerImpl()
     71 {
     72     return CCVideoLayerImpl::create(this);
     73 }
     74 
     75 void VideoLayerChromium::deleteTexturesInUse()
     76 {
     77     if (!layerRenderer())
     78         return;
     79 
     80     GraphicsContext3D* context = layerRendererContext();
     81     for (unsigned plane = 0; plane < VideoFrameChromium::maxPlanes; plane++) {
     82         Texture texture = m_textures[plane];
     83         if (!texture.isEmpty && texture.ownedByLayerRenderer)
     84             GLC(context, context->deleteTexture(texture.id));
     85     }
     86 }
     87 
     88 void VideoLayerChromium::cleanupResources()
     89 {
     90     LayerChromium::cleanupResources();
     91     if (m_currentFrame)
     92         releaseCurrentFrame();
     93     else
     94         resetFrameParameters();
     95 }
     96 
     97 void VideoLayerChromium::updateCompositorResources()
     98 {
     99     if (!m_contentsDirty || !m_owner)
    100         return;
    101 
    102     RenderLayerBacking* backing = static_cast<RenderLayerBacking*>(m_owner->client());
    103     if (!backing || backing->paintingGoesToWindow())
    104         return;
    105 
    106     ASSERT(drawsContent());
    107 
    108     m_skipsDraw = false;
    109     VideoFrameChromium* frame = m_provider->getCurrentFrame();
    110     if (!frame) {
    111         m_skipsDraw = true;
    112         m_provider->putCurrentFrame(frame);
    113         return;
    114     }
    115 
    116     m_frameFormat = frame->format();
    117     unsigned textureFormat = determineTextureFormat(frame);
    118     if (textureFormat == GraphicsContext3D::INVALID_VALUE) {
    119         // FIXME: Implement other paths.
    120         notImplemented();
    121         m_skipsDraw = true;
    122         m_provider->putCurrentFrame(frame);
    123         return;
    124     }
    125 
    126     // If the incoming frame is backed by a texture (i.e. decoded in hardware),
    127     // then we do not need to allocate a texture via the layer renderer. Instead
    128     // we save the texture data then exit.
    129     if (frame->surfaceType() == VideoFrameChromium::TypeTexture) {
    130         releaseCurrentFrame();
    131         saveCurrentFrame(frame);
    132         m_dirtyRect.setSize(FloatSize());
    133         m_contentsDirty = false;
    134         return;
    135     }
    136 
    137     // Allocate textures for planes if they are not allocated already, or
    138     // reallocate textures that are the wrong size for the frame.
    139     GraphicsContext3D* context = layerRendererContext();
    140     bool texturesAllocated = allocateTexturesIfNeeded(context, frame, textureFormat);
    141     if (!texturesAllocated) {
    142         m_skipsDraw = true;
    143         m_provider->putCurrentFrame(frame);
    144         return;
    145     }
    146 
    147     // Update texture planes.
    148     for (unsigned plane = 0; plane < frame->planes(); plane++) {
    149         Texture texture = m_textures[plane];
    150         ASSERT(frame->requiredTextureSize(plane) == texture.size);
    151         updateTexture(context, texture.id, texture.size, textureFormat, frame->data(plane));
    152     }
    153 
    154     m_dirtyRect.setSize(FloatSize());
    155     m_contentsDirty = false;
    156 
    157     m_provider->putCurrentFrame(frame);
    158 }
    159 
    160 void VideoLayerChromium::pushPropertiesTo(CCLayerImpl* layer)
    161 {
    162     LayerChromium::pushPropertiesTo(layer);
    163 
    164     CCVideoLayerImpl* videoLayer = static_cast<CCVideoLayerImpl*>(layer);
    165     videoLayer->setSkipsDraw(m_skipsDraw);
    166     videoLayer->setFrameFormat(m_frameFormat);
    167     for (size_t i = 0; i < 3; ++i)
    168         videoLayer->setTexture(i, m_textures[i]);
    169 }
    170 
    171 
    172 unsigned VideoLayerChromium::determineTextureFormat(const VideoFrameChromium* frame)
    173 {
    174     switch (frame->format()) {
    175     case VideoFrameChromium::YV12:
    176     case VideoFrameChromium::YV16:
    177         return GraphicsContext3D::LUMINANCE;
    178     case VideoFrameChromium::RGBA:
    179         return GraphicsContext3D::RGBA;
    180     default:
    181         break;
    182     }
    183     return GraphicsContext3D::INVALID_VALUE;
    184 }
    185 
    186 bool VideoLayerChromium::allocateTexturesIfNeeded(GraphicsContext3D* context, const VideoFrameChromium* frame, unsigned textureFormat)
    187 {
    188     ASSERT(context);
    189     ASSERT(frame);
    190 
    191     for (unsigned plane = 0; plane < frame->planes(); plane++) {
    192         IntSize requiredTextureSize = frame->requiredTextureSize(plane);
    193         Texture texture = m_textures[plane];
    194 
    195         // If the renderer cannot handle this large of a texture, return false.
    196         // FIXME: Remove this test when tiled layers are implemented.
    197         if (!layerRenderer()->checkTextureSize(requiredTextureSize))
    198             return false;
    199 
    200         if (texture.isEmpty) {
    201             texture.id = layerRenderer()->createLayerTexture();
    202             texture.ownedByLayerRenderer = true;
    203             texture.isEmpty = false;
    204         }
    205 
    206         if (!requiredTextureSize.isZero() && requiredTextureSize != texture.size) {
    207             allocateTexture(context, texture.id, requiredTextureSize, textureFormat);
    208             texture.size = requiredTextureSize;
    209             texture.visibleSize = computeVisibleSize(frame, plane);
    210         }
    211         m_textures[plane] = texture;
    212     }
    213 
    214     return true;
    215 }
    216 
    217 IntSize VideoLayerChromium::computeVisibleSize(const VideoFrameChromium* frame, unsigned plane)
    218 {
    219     int visibleWidth = frame->width(plane);
    220     int visibleHeight = frame->height(plane);
    221     // When there are dead pixels at the edge of the texture, decrease
    222     // the frame width by 1 to prevent the rightmost pixels from
    223     // interpolating with the dead pixels.
    224     if (frame->hasPaddingBytes(plane))
    225         --visibleWidth;
    226 
    227     // In YV12, every 2x2 square of Y values corresponds to one U and
    228     // one V value. If we decrease the width of the UV plane, we must decrease the
    229     // width of the Y texture by 2 for proper alignment. This must happen
    230     // always, even if Y's texture does not have padding bytes.
    231     if (plane == VideoFrameChromium::yPlane && frame->format() == VideoFrameChromium::YV12) {
    232         if (frame->hasPaddingBytes(VideoFrameChromium::uPlane)) {
    233             int originalWidth = frame->width(plane);
    234             visibleWidth = originalWidth - 2;
    235         }
    236     }
    237 
    238     return IntSize(visibleWidth, visibleHeight);
    239 }
    240 
    241 void VideoLayerChromium::allocateTexture(GraphicsContext3D* context, unsigned textureId, const IntSize& dimensions, unsigned textureFormat) const
    242 {
    243     GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, textureId));
    244     GLC(context, context->texImage2DResourceSafe(GraphicsContext3D::TEXTURE_2D, 0, textureFormat, dimensions.width(), dimensions.height(), 0, textureFormat, GraphicsContext3D::UNSIGNED_BYTE));
    245 }
    246 
    247 void VideoLayerChromium::updateTexture(GraphicsContext3D* context, unsigned textureId, const IntSize& dimensions, unsigned format, const void* data) const
    248 {
    249     ASSERT(context);
    250     GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, textureId));
    251     void* mem = static_cast<Extensions3DChromium*>(context->getExtensions())->mapTexSubImage2DCHROMIUM(GraphicsContext3D::TEXTURE_2D, 0, 0, 0, dimensions.width(), dimensions.height(), format, GraphicsContext3D::UNSIGNED_BYTE, Extensions3DChromium::WRITE_ONLY);
    252     if (mem) {
    253         memcpy(mem, data, dimensions.width() * dimensions.height());
    254         GLC(context, static_cast<Extensions3DChromium*>(context->getExtensions())->unmapTexSubImage2DCHROMIUM(mem));
    255     } else {
    256         // If mapTexSubImage2DCHROMIUM fails, then do the slower texSubImage2D
    257         // upload. This does twice the copies as mapTexSubImage2DCHROMIUM, one
    258         // in the command buffer and another to the texture.
    259         GLC(context, context->texSubImage2D(GraphicsContext3D::TEXTURE_2D, 0, 0, 0, dimensions.width(), dimensions.height(), format, GraphicsContext3D::UNSIGNED_BYTE, data));
    260     }
    261 }
    262 
    263 void VideoLayerChromium::releaseCurrentFrame()
    264 {
    265     if (!m_currentFrame)
    266         return;
    267 
    268     m_provider->putCurrentFrame(m_currentFrame);
    269     m_currentFrame = 0;
    270     resetFrameParameters();
    271 }
    272 
    273 void VideoLayerChromium::resetFrameParameters()
    274 {
    275     deleteTexturesInUse();
    276     for (unsigned plane = 0; plane < VideoFrameChromium::maxPlanes; plane++) {
    277         m_textures[plane].id = 0;
    278         m_textures[plane].size = IntSize();
    279         m_textures[plane].visibleSize = IntSize();
    280         m_textures[plane].ownedByLayerRenderer = false;
    281         m_textures[plane].isEmpty = true;
    282     }
    283 }
    284 
    285 void VideoLayerChromium::saveCurrentFrame(VideoFrameChromium* frame)
    286 {
    287     ASSERT(!m_currentFrame);
    288     deleteTexturesInUse();
    289     m_currentFrame = frame;
    290     for (unsigned plane = 0; plane < frame->planes(); plane++) {
    291         m_textures[plane].id = frame->texture(plane);
    292         m_textures[plane].size = frame->requiredTextureSize(plane);
    293         m_textures[plane].visibleSize = computeVisibleSize(frame, plane);
    294         m_textures[plane].ownedByLayerRenderer = false;
    295         m_textures[plane].isEmpty = false;
    296     }
    297 }
    298 
    299 } // namespace WebCore
    300 
    301 #endif // USE(ACCELERATED_COMPOSITING)
    302