4208d52cbd37f3a45f9a8804fc445450af82e179
[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 "NotImplemented.h"
35 #include "TextStream.h"
36 #include "cc/CCIOSurfaceDrawQuad.h"
37 #include "cc/CCLayerTreeHostImpl.h"
38 #include "cc/CCProxy.h"
39 #include "cc/CCQuadSink.h"
40 #include "cc/CCResourceProvider.h"
41 #include "cc/CCStreamVideoDrawQuad.h"
42 #include "cc/CCTextureDrawQuad.h"
43 #include "cc/CCYUVVideoDrawQuad.h"
44 #include <public/WebVideoFrame.h>
45 #include <wtf/text/WTFString.h>
46
47 namespace WebCore {
48
49 CCVideoLayerImpl::CCVideoLayerImpl(int id, WebKit::WebVideoFrameProvider* provider)
50     : CCLayerImpl(id)
51     , m_provider(provider)
52     , m_frame(0)
53     , m_externalTextureResource(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()->resourceProvider());
79
80 #if !ASSERT_DISABLED
81     for (unsigned i = 0; i < WebKit::WebVideoFrame::maxPlanes; ++i)
82         ASSERT(!m_framePlanes[i].resourceId);
83     ASSERT(!m_externalTextureResource);
84 #endif
85 }
86
87 void CCVideoLayerImpl::stopUsingProvider()
88 {
89     // Block the provider from shutting down until this client is done
90     // using the frame.
91     MutexLocker locker(m_providerMutex);
92     ASSERT(!m_frame);
93     m_provider = 0;
94 }
95
96 // Convert WebKit::WebVideoFrame::Format to GraphicsContext3D's format enum values.
97 static GC3Denum convertVFCFormatToGC3DFormat(const WebKit::WebVideoFrame& frame)
98 {
99     switch (frame.format()) {
100     case WebKit::WebVideoFrame::FormatYV12:
101     case WebKit::WebVideoFrame::FormatYV16:
102         return GraphicsContext3D::LUMINANCE;
103     case WebKit::WebVideoFrame::FormatNativeTexture:
104         return frame.textureTarget();
105     case WebKit::WebVideoFrame::FormatInvalid:
106     case WebKit::WebVideoFrame::FormatRGB32:
107     case WebKit::WebVideoFrame::FormatEmpty:
108     case WebKit::WebVideoFrame::FormatI420:
109         notImplemented();
110     }
111     return GraphicsContext3D::INVALID_VALUE;
112 }
113
114 void CCVideoLayerImpl::willDraw(CCResourceProvider* resourceProvider)
115 {
116     ASSERT(CCProxy::isImplThread());
117     CCLayerImpl::willDraw(resourceProvider);
118
119     // Explicitly lock and unlock the provider mutex so it can be held from
120     // willDraw to didDraw. Since the compositor thread is in the middle of
121     // drawing, the layer will not be destroyed before didDraw is called.
122     // Therefore, the only thing that will prevent this lock from being released
123     // is the GPU process locking it. As the GPU process can't cause the
124     // destruction of the provider (calling stopUsingProvider), holding this
125     // lock should not cause a deadlock.
126     m_providerMutex.lock();
127
128     willDrawInternal(resourceProvider);
129     freeUnusedPlaneData(resourceProvider);
130
131     if (!m_frame)
132         m_providerMutex.unlock();
133 }
134
135 void CCVideoLayerImpl::willDrawInternal(CCResourceProvider* resourceProvider)
136 {
137     ASSERT(CCProxy::isImplThread());
138     ASSERT(!m_externalTextureResource);
139
140     if (!m_provider) {
141         m_frame = 0;
142         return;
143     }
144
145     m_frame = m_provider->getCurrentFrame();
146
147     if (!m_frame)
148         return;
149
150     m_format = convertVFCFormatToGC3DFormat(*m_frame);
151
152     if (m_format == GraphicsContext3D::INVALID_VALUE) {
153         m_provider->putCurrentFrame(m_frame);
154         m_frame = 0;
155         return;
156     }
157
158     if (m_frame->planes() > WebKit::WebVideoFrame::maxPlanes) {
159         m_provider->putCurrentFrame(m_frame);
160         m_frame = 0;
161         return;
162     }
163
164     if (!allocatePlaneData(resourceProvider)) {
165         m_provider->putCurrentFrame(m_frame);
166         m_frame = 0;
167         return;
168     }
169
170     if (!copyPlaneData(resourceProvider)) {
171         m_provider->putCurrentFrame(m_frame);
172         m_frame = 0;
173         return;
174     }
175
176     if (m_format == GraphicsContext3D::TEXTURE_2D)
177         m_externalTextureResource = resourceProvider->createResourceFromExternalTexture(m_frame->textureId());
178 }
179
180 void CCVideoLayerImpl::appendQuads(CCQuadSink& quadList, const CCSharedQuadState* sharedQuadState, bool&)
181 {
182     ASSERT(CCProxy::isImplThread());
183
184     if (!m_frame)
185         return;
186
187     // FIXME: When we pass quads out of process, we need to double-buffer, or
188     // otherwise synchonize use of all textures in the quad.
189
190     IntRect quadRect(IntPoint(), contentBounds());
191
192     switch (m_format) {
193     case GraphicsContext3D::LUMINANCE: {
194         // YUV software decoder.
195         const FramePlane& yPlane = m_framePlanes[WebKit::WebVideoFrame::yPlane];
196         const FramePlane& uPlane = m_framePlanes[WebKit::WebVideoFrame::uPlane];
197         const FramePlane& vPlane = m_framePlanes[WebKit::WebVideoFrame::vPlane];
198         OwnPtr<CCYUVVideoDrawQuad> yuvVideoQuad = CCYUVVideoDrawQuad::create(sharedQuadState, quadRect, yPlane, uPlane, vPlane);
199         quadList.append(yuvVideoQuad.release());
200         break;
201     }
202     case GraphicsContext3D::RGBA: {
203         // RGBA software decoder.
204         const FramePlane& plane = m_framePlanes[WebKit::WebVideoFrame::rgbPlane];
205         float widthScaleFactor = static_cast<float>(plane.visibleSize.width()) / plane.size.width();
206
207         bool premultipliedAlpha = true;
208         FloatRect uvRect(0, 0, widthScaleFactor, 1);
209         bool flipped = false;
210         OwnPtr<CCTextureDrawQuad> textureQuad = CCTextureDrawQuad::create(sharedQuadState, quadRect, plane.resourceId, premultipliedAlpha, uvRect, flipped);
211         quadList.append(textureQuad.release());
212         break;
213     }
214     case GraphicsContext3D::TEXTURE_2D: {
215         // NativeTexture hardware decoder.
216         bool premultipliedAlpha = true;
217         FloatRect uvRect(0, 0, 1, 1);
218         bool flipped = false;
219         OwnPtr<CCTextureDrawQuad> textureQuad = CCTextureDrawQuad::create(sharedQuadState, quadRect, m_externalTextureResource, premultipliedAlpha, uvRect, flipped);
220         quadList.append(textureQuad.release());
221         break;
222     }
223     case Extensions3D::TEXTURE_RECTANGLE_ARB: {
224         IntSize textureSize(m_frame->width(), m_frame->height()); 
225         OwnPtr<CCIOSurfaceDrawQuad> ioSurfaceQuad = CCIOSurfaceDrawQuad::create(sharedQuadState, quadRect, textureSize, m_frame->textureId(), CCIOSurfaceDrawQuad::Unflipped);
226         quadList.append(ioSurfaceQuad.release());
227         break;
228     }
229     case Extensions3DChromium::GL_TEXTURE_EXTERNAL_OES: {
230         // StreamTexture hardware decoder.
231         OwnPtr<CCStreamVideoDrawQuad> streamVideoQuad = CCStreamVideoDrawQuad::create(sharedQuadState, quadRect, m_frame->textureId(), m_streamTextureMatrix);
232         quadList.append(streamVideoQuad.release());
233         break;
234     }
235     default:
236         CRASH(); // Someone updated convertVFCFormatToGC3DFormat above but update this!
237     }
238 }
239
240 void CCVideoLayerImpl::didDraw(CCResourceProvider* resourceProvider)
241 {
242     ASSERT(CCProxy::isImplThread());
243     CCLayerImpl::didDraw(resourceProvider);
244
245     if (!m_frame)
246         return;
247
248     if (m_format == GraphicsContext3D::TEXTURE_2D) {
249         ASSERT(m_externalTextureResource);
250         // FIXME: the following assert will not be true when sending resources to a
251         // parent compositor. We will probably need to hold on to m_frame for
252         // longer, and have several "current frames" in the pipeline.
253         ASSERT(!resourceProvider->inUseByConsumer(m_externalTextureResource));
254         resourceProvider->deleteResource(m_externalTextureResource);
255         m_externalTextureResource = 0;
256     }
257
258     m_provider->putCurrentFrame(m_frame);
259     m_frame = 0;
260
261     m_providerMutex.unlock();
262 }
263
264 static int videoFrameDimension(int originalDimension, unsigned plane, int format)
265 {
266     if (format == WebKit::WebVideoFrame::FormatYV12 && plane != WebKit::WebVideoFrame::yPlane)
267         return originalDimension / 2;
268     return originalDimension;
269 }
270
271 static bool hasPaddingBytes(const WebKit::WebVideoFrame& frame, unsigned plane)
272 {
273     return frame.stride(plane) > videoFrameDimension(frame.width(), plane, frame.format());
274 }
275
276 IntSize CCVideoLayerImpl::computeVisibleSize(const WebKit::WebVideoFrame& frame, unsigned plane)
277 {
278     int visibleWidth = videoFrameDimension(frame.width(), plane, frame.format());
279     int originalWidth = visibleWidth;
280     int visibleHeight = videoFrameDimension(frame.height(), plane, frame.format());
281
282     // When there are dead pixels at the edge of the texture, decrease
283     // the frame width by 1 to prevent the rightmost pixels from
284     // interpolating with the dead pixels.
285     if (hasPaddingBytes(frame, plane))
286         --visibleWidth;
287
288     // In YV12, every 2x2 square of Y values corresponds to one U and
289     // one V value. If we decrease the width of the UV plane, we must decrease the
290     // width of the Y texture by 2 for proper alignment. This must happen
291     // always, even if Y's texture does not have padding bytes.
292     if (plane == WebKit::WebVideoFrame::yPlane && frame.format() == WebKit::WebVideoFrame::FormatYV12) {
293         if (hasPaddingBytes(frame, WebKit::WebVideoFrame::uPlane))
294             visibleWidth = originalWidth - 2;
295     }
296
297     return IntSize(visibleWidth, visibleHeight);
298 }
299
300 bool CCVideoLayerImpl::FramePlane::allocateData(CCResourceProvider* resourceProvider)
301 {
302     if (resourceId)
303         return true;
304
305     resourceId = resourceProvider->createResource(CCRenderer::ImplPool, size, format, CCResourceProvider::TextureUsageAny);
306     return resourceId;
307 }
308
309 void CCVideoLayerImpl::FramePlane::freeData(CCResourceProvider* resourceProvider)
310 {
311     if (!resourceId)
312         return;
313
314     resourceProvider->deleteResource(resourceId);
315     resourceId = 0;
316 }
317
318 bool CCVideoLayerImpl::allocatePlaneData(CCResourceProvider* resourceProvider)
319 {
320     int maxTextureSize = resourceProvider->maxTextureSize();
321     for (unsigned planeIndex = 0; planeIndex < m_frame->planes(); ++planeIndex) {
322         CCVideoLayerImpl::FramePlane& plane = m_framePlanes[planeIndex];
323
324         IntSize requiredTextureSize(m_frame->stride(planeIndex), videoFrameDimension(m_frame->height(), planeIndex, m_frame->format()));
325         // FIXME: Remove the test against maxTextureSize when tiled layers are implemented.
326         if (requiredTextureSize.isZero() || requiredTextureSize.width() > maxTextureSize || requiredTextureSize.height() > maxTextureSize)
327             return false;
328
329         if (plane.size != requiredTextureSize || plane.format != m_format) {
330             plane.freeData(resourceProvider);
331             plane.size = requiredTextureSize;
332             plane.format = m_format;
333         }
334
335         if (!plane.resourceId) {
336             if (!plane.allocateData(resourceProvider))
337                 return false;
338             plane.visibleSize = computeVisibleSize(*m_frame, planeIndex);
339         }
340     }
341     return true;
342 }
343
344 bool CCVideoLayerImpl::copyPlaneData(CCResourceProvider* resourceProvider)
345 {
346     size_t softwarePlaneCount = m_frame->planes();
347     if (!softwarePlaneCount)
348         return true;
349
350     for (size_t softwarePlaneIndex = 0; softwarePlaneIndex < softwarePlaneCount; ++softwarePlaneIndex) {
351         CCVideoLayerImpl::FramePlane& plane = m_framePlanes[softwarePlaneIndex];
352         const uint8_t* softwarePlanePixels = static_cast<const uint8_t*>(m_frame->data(softwarePlaneIndex));
353         IntRect planeRect(IntPoint(), plane.size);
354         resourceProvider->upload(plane.resourceId, softwarePlanePixels, planeRect, planeRect, planeRect);
355     }
356     return true;
357 }
358
359 void CCVideoLayerImpl::freePlaneData(CCResourceProvider* resourceProvider)
360 {
361     for (unsigned i = 0; i < WebKit::WebVideoFrame::maxPlanes; ++i)
362         m_framePlanes[i].freeData(resourceProvider);
363 }
364
365 void CCVideoLayerImpl::freeUnusedPlaneData(CCResourceProvider* resourceProvider)
366 {
367     unsigned firstUnusedPlane = m_frame ? m_frame->planes() : 0;
368     for (unsigned i = firstUnusedPlane; i < WebKit::WebVideoFrame::maxPlanes; ++i)
369         m_framePlanes[i].freeData(resourceProvider);
370 }
371
372 void CCVideoLayerImpl::didReceiveFrame()
373 {
374     setNeedsRedraw();
375 }
376
377 void CCVideoLayerImpl::didUpdateMatrix(const float matrix[16])
378 {
379     m_streamTextureMatrix = WebKit::WebTransformationMatrix(
380         matrix[0], matrix[1], matrix[2], matrix[3],
381         matrix[4], matrix[5], matrix[6], matrix[7],
382         matrix[8], matrix[9], matrix[10], matrix[11],
383         matrix[12], matrix[13], matrix[14], matrix[15]);
384     setNeedsRedraw();
385 }
386
387 void CCVideoLayerImpl::didLoseContext()
388 {
389     freePlaneData(layerTreeHostImpl()->resourceProvider());
390 }
391
392 void CCVideoLayerImpl::setNeedsRedraw()
393 {
394     layerTreeHostImpl()->setNeedsRedraw();
395 }
396
397 void CCVideoLayerImpl::dumpLayerProperties(TextStream& ts, int indent) const
398 {
399     writeIndent(ts, indent);
400     ts << "video layer\n";
401     CCLayerImpl::dumpLayerProperties(ts, indent);
402 }
403
404 }
405
406 #endif // USE(ACCELERATED_COMPOSITING)