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