• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
create(GraphicsLayerChromium * owner,VideoFrameProvider * provider)48 PassRefPtr<VideoLayerChromium> VideoLayerChromium::create(GraphicsLayerChromium* owner,
49                                                           VideoFrameProvider* provider)
50 {
51     return adoptRef(new VideoLayerChromium(owner, provider));
52 }
53 
VideoLayerChromium(GraphicsLayerChromium * owner,VideoFrameProvider * provider)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 
~VideoLayerChromium()64 VideoLayerChromium::~VideoLayerChromium()
65 {
66     cleanupResources();
67     deleteTexturesInUse();
68 }
69 
createCCLayerImpl()70 PassRefPtr<CCLayerImpl> VideoLayerChromium::createCCLayerImpl()
71 {
72     return CCVideoLayerImpl::create(this);
73 }
74 
deleteTexturesInUse()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 
cleanupResources()88 void VideoLayerChromium::cleanupResources()
89 {
90     LayerChromium::cleanupResources();
91     if (m_currentFrame)
92         releaseCurrentFrame();
93     else
94         resetFrameParameters();
95 }
96 
updateCompositorResources()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 
pushPropertiesTo(CCLayerImpl * layer)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 
determineTextureFormat(const VideoFrameChromium * frame)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 
allocateTexturesIfNeeded(GraphicsContext3D * context,const VideoFrameChromium * frame,unsigned textureFormat)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 
computeVisibleSize(const VideoFrameChromium * frame,unsigned plane)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 
allocateTexture(GraphicsContext3D * context,unsigned textureId,const IntSize & dimensions,unsigned textureFormat) const241 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 
updateTexture(GraphicsContext3D * context,unsigned textureId,const IntSize & dimensions,unsigned format,const void * data) const247 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 
releaseCurrentFrame()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 
resetFrameParameters()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 
saveCurrentFrame(VideoFrameChromium * frame)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