46e578f634c8ea4b23f6056c3f353f93090970b5
[WebKit-https.git] / Source / WebCore / platform / graphics / chromium / cc / CCVideoLayerImpl.cpp
1 /*
2  * Copyright (C) 2011 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
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27
28 #if USE(ACCELERATED_COMPOSITING)
29
30 #include "cc/CCVideoLayerImpl.h"
31
32 #include "Extensions3DChromium.h"
33 #include "GraphicsContext3D.h"
34 #include "LayerTextureSubImage.h"
35 #include "NotImplemented.h"
36 #include "TextStream.h"
37 #include "TextureManager.h" // For TextureAllocator
38 #include "cc/CCGraphicsContext.h"
39 #include "cc/CCLayerTreeHostImpl.h"
40 #include "cc/CCProxy.h"
41 #include "cc/CCQuadCuller.h"
42 #include "cc/CCStreamVideoDrawQuad.h"
43 #include "cc/CCTextureDrawQuad.h"
44 #include "cc/CCYUVVideoDrawQuad.h"
45 #include <public/WebVideoFrame.h>
46 #include <wtf/text/WTFString.h>
47
48 namespace WebCore {
49
50 CCVideoLayerImpl::CCVideoLayerImpl(int id, WebKit::WebVideoFrameProvider* provider)
51     : CCLayerImpl(id)
52     , m_provider(provider)
53     , m_frame(0)
54 {
55     // This matrix is the default transformation for stream textures, and flips on the Y axis.
56     m_streamTextureMatrix = WebKit::WebTransformationMatrix(
57         1, 0, 0, 0,
58         0, -1, 0, 0,
59         0, 0, 1, 0,
60         0, 1, 0, 1);
61
62     // This only happens during a commit on the compositor thread while the main
63     // thread is blocked. That makes this a thread-safe call to set the video
64     // frame provider client that does not require a lock. The same is true of
65     // the call in the destructor.
66     ASSERT(CCProxy::isMainThreadBlocked());
67     m_provider->setVideoFrameProviderClient(this);
68 }
69
70 CCVideoLayerImpl::~CCVideoLayerImpl()
71 {
72     // See comment in constructor for why this doesn't need a lock.
73     ASSERT(CCProxy::isMainThreadBlocked());
74     if (m_provider) {
75         m_provider->setVideoFrameProviderClient(0);
76         m_provider = 0;
77     }
78     freePlaneData(layerTreeHostImpl()->layerRenderer());
79
80 #if !ASSERT_DISABLED
81     for (unsigned i = 0; i < WebKit::WebVideoFrame::maxPlanes; ++i)
82         ASSERT(!m_framePlanes[i].textureId);
83 #endif
84 }
85
86 void CCVideoLayerImpl::stopUsingProvider()
87 {
88     // Block the provider from shutting down until this client is done
89     // using the frame.
90     MutexLocker locker(m_providerMutex);
91     ASSERT(!m_frame);
92     m_provider = 0;
93 }
94
95 // Convert WebKit::WebVideoFrame::Format to GraphicsContext3D's format enum values.
96 static GC3Denum convertVFCFormatToGC3DFormat(const WebKit::WebVideoFrame& frame)
97 {
98     switch (frame.format()) {
99     case WebKit::WebVideoFrame::FormatYV12:
100     case WebKit::WebVideoFrame::FormatYV16:
101         return GraphicsContext3D::LUMINANCE;
102     case WebKit::WebVideoFrame::FormatNativeTexture:
103         return frame.textureTarget();
104     case WebKit::WebVideoFrame::FormatInvalid:
105     case WebKit::WebVideoFrame::FormatRGB32:
106     case WebKit::WebVideoFrame::FormatEmpty:
107     case WebKit::WebVideoFrame::FormatI420:
108         notImplemented();
109     }
110     return GraphicsContext3D::INVALID_VALUE;
111 }
112
113 void CCVideoLayerImpl::willDraw(CCRenderer* layerRenderer, CCGraphicsContext* context)
114 {
115     ASSERT(CCProxy::isImplThread());
116     CCLayerImpl::willDraw(layerRenderer, context);
117
118     // Explicitly lock and unlock the provider mutex so it can be held from
119     // willDraw to didDraw. Since the compositor thread is in the middle of
120     // drawing, the layer will not be destroyed before didDraw is called.
121     // Therefore, the only thing that will prevent this lock from being released
122     // is the GPU process locking it. As the GPU process can't cause the
123     // destruction of the provider (calling stopUsingProvider), holding this
124     // lock should not cause a deadlock.
125     m_providerMutex.lock();
126
127     willDrawInternal(layerRenderer, context);
128     freeUnusedPlaneData(layerRenderer);
129
130     if (!m_frame)
131         m_providerMutex.unlock();
132 }
133
134 void CCVideoLayerImpl::willDrawInternal(CCRenderer* layerRenderer, CCGraphicsContext* context)
135 {
136     ASSERT(CCProxy::isImplThread());
137
138     if (!m_provider) {
139         m_frame = 0;
140         return;
141     }
142
143     m_frame = m_provider->getCurrentFrame();
144
145     if (!m_frame)
146         return;
147
148     m_format = convertVFCFormatToGC3DFormat(*m_frame);
149
150     if (m_format == GraphicsContext3D::INVALID_VALUE) {
151         m_provider->putCurrentFrame(m_frame);
152         m_frame = 0;
153         return;
154     }
155
156     if (m_frame->planes() > WebKit::WebVideoFrame::maxPlanes) {
157         m_provider->putCurrentFrame(m_frame);
158         m_frame = 0;
159         return;
160     }
161
162     if (!allocatePlaneData(layerRenderer)) {
163         m_provider->putCurrentFrame(m_frame);
164         m_frame = 0;
165         return;
166     }
167
168     if (!copyPlaneData(layerRenderer, context)) {
169         m_provider->putCurrentFrame(m_frame);
170         m_frame = 0;
171         return;
172     }
173 }
174
175 void CCVideoLayerImpl::appendQuads(CCQuadCuller& quadList, const CCSharedQuadState* sharedQuadState, bool&)
176 {
177     ASSERT(CCProxy::isImplThread());
178
179     if (!m_frame)
180         return;
181
182     // FIXME: When we pass quads out of process, we need to double-buffer, or
183     // otherwise synchonize use of all textures in the quad.
184
185     IntRect quadRect(IntPoint(), bounds());
186
187     switch (m_format) {
188     case GraphicsContext3D::LUMINANCE: {
189         // YUV software decoder.
190         const FramePlane& yPlane = m_framePlanes[WebKit::WebVideoFrame::yPlane];
191         const FramePlane& uPlane = m_framePlanes[WebKit::WebVideoFrame::uPlane];
192         const FramePlane& vPlane = m_framePlanes[WebKit::WebVideoFrame::vPlane];
193         OwnPtr<CCYUVVideoDrawQuad> yuvVideoQuad = CCYUVVideoDrawQuad::create(sharedQuadState, quadRect, yPlane, uPlane, vPlane);
194         quadList.append(yuvVideoQuad.release());
195         break;
196     }
197     case GraphicsContext3D::RGBA: {
198         // RGBA software decoder.
199         const FramePlane& plane = m_framePlanes[WebKit::WebVideoFrame::rgbPlane];
200         float widthScaleFactor = static_cast<float>(plane.visibleSize.width()) / plane.size.width();
201
202         bool premultipliedAlpha = true;
203         FloatRect uvRect(0, 0, widthScaleFactor, 1);
204         bool flipped = false;
205         OwnPtr<CCTextureDrawQuad> textureQuad = CCTextureDrawQuad::create(sharedQuadState, quadRect, plane.textureId, premultipliedAlpha, uvRect, flipped);
206         quadList.append(textureQuad.release());
207         break;
208     }
209     case GraphicsContext3D::TEXTURE_2D: {
210         // NativeTexture hardware decoder.
211         bool premultipliedAlpha = true;
212         FloatRect uvRect(0, 0, 1, 1);
213 #if defined(OS_CHROMEOS) && defined(__ARMEL__)
214         bool flipped = true; // Under the covers, implemented by OpenMAX IL.
215 #elif defined(OS_WINDOWS)
216         bool flipped = true; // Under the covers, implemented by DXVA.
217 #else
218         bool flipped = false; // LibVA (cros/intel), MacOS.
219 #endif
220         OwnPtr<CCTextureDrawQuad> textureQuad = CCTextureDrawQuad::create(sharedQuadState, quadRect, m_frame->textureId(), premultipliedAlpha, uvRect, flipped);
221         quadList.append(textureQuad.release());
222         break;
223     }
224     case Extensions3DChromium::GL_TEXTURE_EXTERNAL_OES: {
225         // StreamTexture hardware decoder.
226         OwnPtr<CCStreamVideoDrawQuad> streamVideoQuad = CCStreamVideoDrawQuad::create(sharedQuadState, quadRect, m_frame->textureId(), m_streamTextureMatrix);
227         quadList.append(streamVideoQuad.release());
228         break;
229     }
230     default:
231         CRASH(); // Someone updated convertVFCFormatToGC3DFormat above but update this!
232     }
233 }
234
235 void CCVideoLayerImpl::didDraw()
236 {
237     ASSERT(CCProxy::isImplThread());
238     CCLayerImpl::didDraw();
239
240     if (!m_frame)
241         return;
242
243     m_provider->putCurrentFrame(m_frame);
244     m_frame = 0;
245
246     m_providerMutex.unlock();
247 }
248
249 static int videoFrameDimension(int originalDimension, unsigned plane, int format)
250 {
251     if (format == WebKit::WebVideoFrame::FormatYV12 && plane != WebKit::WebVideoFrame::yPlane)
252         return originalDimension / 2;
253     return originalDimension;
254 }
255
256 static bool hasPaddingBytes(const WebKit::WebVideoFrame& frame, unsigned plane)
257 {
258     return frame.stride(plane) > videoFrameDimension(frame.width(), plane, frame.format());
259 }
260
261 IntSize CCVideoLayerImpl::computeVisibleSize(const WebKit::WebVideoFrame& frame, unsigned plane)
262 {
263     int visibleWidth = videoFrameDimension(frame.width(), plane, frame.format());
264     int originalWidth = visibleWidth;
265     int visibleHeight = videoFrameDimension(frame.height(), plane, frame.format());
266
267     // When there are dead pixels at the edge of the texture, decrease
268     // the frame width by 1 to prevent the rightmost pixels from
269     // interpolating with the dead pixels.
270     if (hasPaddingBytes(frame, plane))
271         --visibleWidth;
272
273     // In YV12, every 2x2 square of Y values corresponds to one U and
274     // one V value. If we decrease the width of the UV plane, we must decrease the
275     // width of the Y texture by 2 for proper alignment. This must happen
276     // always, even if Y's texture does not have padding bytes.
277     if (plane == WebKit::WebVideoFrame::yPlane && frame.format() == WebKit::WebVideoFrame::FormatYV12) {
278         if (hasPaddingBytes(frame, WebKit::WebVideoFrame::uPlane))
279             visibleWidth = originalWidth - 2;
280     }
281
282     return IntSize(visibleWidth, visibleHeight);
283 }
284
285 bool CCVideoLayerImpl::FramePlane::allocateData(CCRenderer* layerRenderer)
286 {
287     if (textureId)
288         return true;
289
290     textureId = layerRenderer->contentsTextureAllocator()->createTexture(size, format);
291     return textureId;
292 }
293
294 void CCVideoLayerImpl::FramePlane::freeData(CCRenderer* layerRenderer)
295 {
296     if (!textureId)
297         return;
298
299     layerRenderer->contentsTextureAllocator()->deleteTexture(textureId, size, format);
300     textureId = 0;
301 }
302
303 bool CCVideoLayerImpl::allocatePlaneData(CCRenderer* layerRenderer)
304 {
305     int maxTextureSize = layerRenderer->capabilities().maxTextureSize;
306     for (unsigned planeIndex = 0; planeIndex < m_frame->planes(); ++planeIndex) {
307         CCVideoLayerImpl::FramePlane& plane = m_framePlanes[planeIndex];
308
309         IntSize requiredTextureSize(m_frame->stride(planeIndex), videoFrameDimension(m_frame->height(), planeIndex, m_frame->format()));
310         // FIXME: Remove the test against maxTextureSize when tiled layers are implemented.
311         if (requiredTextureSize.isZero() || requiredTextureSize.width() > maxTextureSize || requiredTextureSize.height() > maxTextureSize)
312             return false;
313
314         if (plane.size != requiredTextureSize || plane.format != m_format) {
315             plane.freeData(layerRenderer);
316             plane.size = requiredTextureSize;
317             plane.format = m_format;
318         }
319
320         if (!plane.textureId) {
321             if (!plane.allocateData(layerRenderer))
322                 return false;
323             plane.visibleSize = computeVisibleSize(*m_frame, planeIndex);
324         }
325     }
326     return true;
327 }
328
329 bool CCVideoLayerImpl::copyPlaneData(CCRenderer* layerRenderer, CCGraphicsContext* context)
330 {
331     size_t softwarePlaneCount = m_frame->planes();
332     if (!softwarePlaneCount)
333         return true;
334
335     GraphicsContext3D* context3d = context->context3D();
336     if (!context3d) {
337         // FIXME: Implement this path for software compositing.
338         return false;
339     }
340
341     LayerTextureSubImage uploader(true);
342     for (size_t softwarePlaneIndex = 0; softwarePlaneIndex < softwarePlaneCount; ++softwarePlaneIndex) {
343         CCVideoLayerImpl::FramePlane& plane = m_framePlanes[softwarePlaneIndex];
344         const uint8_t* softwarePlanePixels = static_cast<const uint8_t*>(m_frame->data(softwarePlaneIndex));
345         IntRect planeRect(IntPoint(), plane.size);
346
347         context3d->bindTexture(GraphicsContext3D::TEXTURE_2D, plane.textureId);
348         uploader.setSubImageSize(plane.size);
349         uploader.upload(softwarePlanePixels, planeRect, planeRect, planeRect, plane.format, context);
350     }
351     return true;
352 }
353
354 void CCVideoLayerImpl::freePlaneData(CCRenderer* layerRenderer)
355 {
356     for (unsigned i = 0; i < WebKit::WebVideoFrame::maxPlanes; ++i)
357         m_framePlanes[i].freeData(layerRenderer);
358 }
359
360 void CCVideoLayerImpl::freeUnusedPlaneData(CCRenderer* layerRenderer)
361 {
362     unsigned firstUnusedPlane = m_frame ? m_frame->planes() : 0;
363     for (unsigned i = firstUnusedPlane; i < WebKit::WebVideoFrame::maxPlanes; ++i)
364         m_framePlanes[i].freeData(layerRenderer);
365 }
366
367 void CCVideoLayerImpl::didReceiveFrame()
368 {
369     setNeedsRedraw();
370 }
371
372 void CCVideoLayerImpl::didUpdateMatrix(const float matrix[16])
373 {
374     m_streamTextureMatrix = WebKit::WebTransformationMatrix(
375         matrix[0], matrix[1], matrix[2], matrix[3],
376         matrix[4], matrix[5], matrix[6], matrix[7],
377         matrix[8], matrix[9], matrix[10], matrix[11],
378         matrix[12], matrix[13], matrix[14], matrix[15]);
379     setNeedsRedraw();
380 }
381
382 void CCVideoLayerImpl::didLoseContext()
383 {
384     freePlaneData(layerTreeHostImpl()->layerRenderer());
385 }
386
387 void CCVideoLayerImpl::setNeedsRedraw()
388 {
389     layerTreeHostImpl()->setNeedsRedraw();
390 }
391
392 void CCVideoLayerImpl::dumpLayerProperties(TextStream& ts, int indent) const
393 {
394     writeIndent(ts, indent);
395     ts << "video layer\n";
396     CCLayerImpl::dumpLayerProperties(ts, indent);
397 }
398
399 }
400
401 #endif // USE(ACCELERATED_COMPOSITING)