[chromium] LayerRendererChromium is not getting visibility messages in single threade...
[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 "LayerRendererChromium.h" // For GLC macro
35 #include "LayerTextureSubImage.h"
36 #include "NotImplemented.h"
37 #include "TextStream.h"
38 #include "TextureManager.h" // For TextureAllocator
39 #include "cc/CCGraphicsContext.h"
40 #include "cc/CCLayerTreeHostImpl.h"
41 #include "cc/CCProxy.h"
42 #include "cc/CCQuadCuller.h"
43 #include "cc/CCStreamVideoDrawQuad.h"
44 #include "cc/CCTextureDrawQuad.h"
45 #include "cc/CCYUVVideoDrawQuad.h"
46 #include <public/WebVideoFrame.h>
47 #include <wtf/text/WTFString.h>
48
49 namespace WebCore {
50
51 CCVideoLayerImpl::CCVideoLayerImpl(int id, WebKit::WebVideoFrameProvider* provider)
52     : CCLayerImpl(id)
53     , m_provider(provider)
54     , m_frame(0)
55 {
56     // This matrix is the default transformation for stream textures, and flips on the Y axis.
57     m_streamTextureMatrix = WebKit::WebTransformationMatrix(
58         1, 0, 0, 0,
59         0, -1, 0, 0,
60         0, 0, 1, 0,
61         0, 1, 0, 1);
62
63     // This only happens during a commit on the compositor thread while the main
64     // thread is blocked. That makes this a thread-safe call to set the video
65     // frame provider client that does not require a lock. The same is true of
66     // the call in the destructor.
67     ASSERT(CCProxy::isMainThreadBlocked());
68     m_provider->setVideoFrameProviderClient(this);
69 }
70
71 CCVideoLayerImpl::~CCVideoLayerImpl()
72 {
73     // See comment in constructor for why this doesn't need a lock.
74     ASSERT(CCProxy::isMainThreadBlocked());
75     if (m_provider) {
76         m_provider->setVideoFrameProviderClient(0);
77         m_provider = 0;
78     }
79     freePlaneData(layerTreeHostImpl()->context());
80
81 #if !ASSERT_DISABLED
82     for (unsigned i = 0; i < WebKit::WebVideoFrame::maxPlanes; ++i)
83         ASSERT(!m_framePlanes[i].textureId);
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(CCRenderer* layerRenderer, CCGraphicsContext* context)
115 {
116     ASSERT(CCProxy::isImplThread());
117     CCLayerImpl::willDraw(layerRenderer, context);
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(layerRenderer, context);
129     freeUnusedPlaneData(context);
130
131     if (!m_frame)
132         m_providerMutex.unlock();
133 }
134
135 void CCVideoLayerImpl::willDrawInternal(CCRenderer* layerRenderer, CCGraphicsContext* context)
136 {
137     ASSERT(CCProxy::isImplThread());
138
139     if (!m_provider) {
140         m_frame = 0;
141         return;
142     }
143
144     m_frame = m_provider->getCurrentFrame();
145
146     if (!m_frame)
147         return;
148
149     m_format = convertVFCFormatToGC3DFormat(*m_frame);
150
151     if (m_format == GraphicsContext3D::INVALID_VALUE) {
152         m_provider->putCurrentFrame(m_frame);
153         m_frame = 0;
154         return;
155     }
156
157     if (m_frame->planes() > WebKit::WebVideoFrame::maxPlanes) {
158         m_provider->putCurrentFrame(m_frame);
159         m_frame = 0;
160         return;
161     }
162
163     if (!allocatePlaneData(layerRenderer, context)) {
164         m_provider->putCurrentFrame(m_frame);
165         m_frame = 0;
166         return;
167     }
168
169     if (!copyPlaneData(layerRenderer, context)) {
170         m_provider->putCurrentFrame(m_frame);
171         m_frame = 0;
172         return;
173     }
174 }
175
176 void CCVideoLayerImpl::appendQuads(CCQuadCuller& quadList, const CCSharedQuadState* sharedQuadState, bool&)
177 {
178     ASSERT(CCProxy::isImplThread());
179
180     if (!m_frame)
181         return;
182
183     // FIXME: When we pass quads out of process, we need to double-buffer, or
184     // otherwise synchonize use of all textures in the quad.
185
186     IntRect quadRect(IntPoint(), bounds());
187
188     switch (m_format) {
189     case GraphicsContext3D::LUMINANCE: {
190         // YUV software decoder.
191         const FramePlane& yPlane = m_framePlanes[WebKit::WebVideoFrame::yPlane];
192         const FramePlane& uPlane = m_framePlanes[WebKit::WebVideoFrame::uPlane];
193         const FramePlane& vPlane = m_framePlanes[WebKit::WebVideoFrame::vPlane];
194         OwnPtr<CCYUVVideoDrawQuad> yuvVideoQuad = CCYUVVideoDrawQuad::create(sharedQuadState, quadRect, yPlane, uPlane, vPlane);
195         quadList.append(yuvVideoQuad.release());
196         break;
197     }
198     case GraphicsContext3D::RGBA: {
199         // RGBA software decoder.
200         const FramePlane& plane = m_framePlanes[WebKit::WebVideoFrame::rgbPlane];
201         float widthScaleFactor = static_cast<float>(plane.visibleSize.width()) / plane.size.width();
202
203         bool premultipliedAlpha = true;
204         FloatRect uvRect(0, 0, widthScaleFactor, 1);
205         bool flipped = false;
206         OwnPtr<CCTextureDrawQuad> textureQuad = CCTextureDrawQuad::create(sharedQuadState, quadRect, plane.textureId, premultipliedAlpha, uvRect, flipped);
207         quadList.append(textureQuad.release());
208         break;
209     }
210     case GraphicsContext3D::TEXTURE_2D: {
211         // NativeTexture hardware decoder.
212         bool premultipliedAlpha = true;
213         FloatRect uvRect(0, 0, 1, 1);
214 #if defined(OS_CHROMEOS) && defined(__ARMEL__)
215         bool flipped = true; // Under the covers, implemented by OpenMAX IL.
216 #elif defined(OS_WINDOWS)
217         bool flipped = true; // Under the covers, implemented by DXVA.
218 #else
219         bool flipped = false; // LibVA (cros/intel), MacOS.
220 #endif
221         OwnPtr<CCTextureDrawQuad> textureQuad = CCTextureDrawQuad::create(sharedQuadState, quadRect, m_frame->textureId(), premultipliedAlpha, uvRect, flipped);
222         quadList.append(textureQuad.release());
223         break;
224     }
225     case Extensions3DChromium::GL_TEXTURE_EXTERNAL_OES: {
226         // StreamTexture hardware decoder.
227         OwnPtr<CCStreamVideoDrawQuad> streamVideoQuad = CCStreamVideoDrawQuad::create(sharedQuadState, quadRect, m_frame->textureId(), m_streamTextureMatrix);
228         quadList.append(streamVideoQuad.release());
229         break;
230     }
231     default:
232         CRASH(); // Someone updated convertVFCFormatToGC3DFormat above but update this!
233     }
234 }
235
236 void CCVideoLayerImpl::didDraw()
237 {
238     ASSERT(CCProxy::isImplThread());
239     CCLayerImpl::didDraw();
240
241     if (!m_frame)
242         return;
243
244     m_provider->putCurrentFrame(m_frame);
245     m_frame = 0;
246
247     m_providerMutex.unlock();
248 }
249
250 static int videoFrameDimension(int originalDimension, unsigned plane, int format)
251 {
252     if (format == WebKit::WebVideoFrame::FormatYV12 && plane != WebKit::WebVideoFrame::yPlane)
253         return originalDimension / 2;
254     return originalDimension;
255 }
256
257 static bool hasPaddingBytes(const WebKit::WebVideoFrame& frame, unsigned plane)
258 {
259     return frame.stride(plane) > videoFrameDimension(frame.width(), plane, frame.format());
260 }
261
262 IntSize CCVideoLayerImpl::computeVisibleSize(const WebKit::WebVideoFrame& frame, unsigned plane)
263 {
264     int visibleWidth = videoFrameDimension(frame.width(), plane, frame.format());
265     int originalWidth = visibleWidth;
266     int visibleHeight = videoFrameDimension(frame.height(), plane, frame.format());
267
268     // When there are dead pixels at the edge of the texture, decrease
269     // the frame width by 1 to prevent the rightmost pixels from
270     // interpolating with the dead pixels.
271     if (hasPaddingBytes(frame, plane))
272         --visibleWidth;
273
274     // In YV12, every 2x2 square of Y values corresponds to one U and
275     // one V value. If we decrease the width of the UV plane, we must decrease the
276     // width of the Y texture by 2 for proper alignment. This must happen
277     // always, even if Y's texture does not have padding bytes.
278     if (plane == WebKit::WebVideoFrame::yPlane && frame.format() == WebKit::WebVideoFrame::FormatYV12) {
279         if (hasPaddingBytes(frame, WebKit::WebVideoFrame::uPlane))
280             visibleWidth = originalWidth - 2;
281     }
282
283     return IntSize(visibleWidth, visibleHeight);
284 }
285
286 bool CCVideoLayerImpl::FramePlane::allocateData(CCGraphicsContext* context)
287 {
288     if (textureId)
289         return true;
290
291     GraphicsContext3D* context3D = context->context3D();
292     if (!context3D)
293         return false;
294
295     GLC(context3D, textureId = context3D->createTexture());
296     GLC(context3D, context3D->bindTexture(GraphicsContext3D::TEXTURE_2D, textureId));
297     // Do basic linear filtering on resize.
298     GLC(context3D, context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::LINEAR));
299     GLC(context3D, context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_MAG_FILTER, GraphicsContext3D::LINEAR));
300     // NPOT textures in GL ES only work when the wrap mode is set to GraphicsContext3D::CLAMP_TO_EDGE.
301     GLC(context3D, context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_S, GraphicsContext3D::CLAMP_TO_EDGE));
302     GLC(context3D, context3D->texParameteri(GraphicsContext3D::TEXTURE_2D, GraphicsContext3D::TEXTURE_WRAP_T, GraphicsContext3D::CLAMP_TO_EDGE));
303
304     GLC(context3D, context3D->texImage2DResourceSafe(GraphicsContext3D::TEXTURE_2D, 0, format, size.width(), size.height(), 0, format, GraphicsContext3D::UNSIGNED_BYTE));
305
306     return textureId;
307 }
308
309 void CCVideoLayerImpl::FramePlane::freeData(CCGraphicsContext* context)
310 {
311     if (!textureId)
312         return;
313
314     GraphicsContext3D* context3D = context->context3D();
315     if (!context3D)
316         return;
317
318     GLC(context3D, context3D->deleteTexture(textureId));
319     textureId = 0;
320 }
321
322 bool CCVideoLayerImpl::allocatePlaneData(CCRenderer* layerRenderer, CCGraphicsContext* context)
323 {
324     int maxTextureSize = layerRenderer->capabilities().maxTextureSize;
325     for (unsigned planeIndex = 0; planeIndex < m_frame->planes(); ++planeIndex) {
326         CCVideoLayerImpl::FramePlane& plane = m_framePlanes[planeIndex];
327
328         IntSize requiredTextureSize(m_frame->stride(planeIndex), videoFrameDimension(m_frame->height(), planeIndex, m_frame->format()));
329         // FIXME: Remove the test against maxTextureSize when tiled layers are implemented.
330         if (requiredTextureSize.isZero() || requiredTextureSize.width() > maxTextureSize || requiredTextureSize.height() > maxTextureSize)
331             return false;
332
333         if (plane.size != requiredTextureSize || plane.format != m_format) {
334             plane.freeData(context);
335             plane.size = requiredTextureSize;
336             plane.format = m_format;
337         }
338
339         if (!plane.textureId) {
340             if (!plane.allocateData(context))
341                 return false;
342             plane.visibleSize = computeVisibleSize(*m_frame, planeIndex);
343         }
344     }
345     return true;
346 }
347
348 bool CCVideoLayerImpl::copyPlaneData(CCRenderer* layerRenderer, CCGraphicsContext* context)
349 {
350     size_t softwarePlaneCount = m_frame->planes();
351     if (!softwarePlaneCount)
352         return true;
353
354     GraphicsContext3D* context3d = context->context3D();
355     if (!context3d) {
356         // FIXME: Implement this path for software compositing.
357         return false;
358     }
359
360     LayerTextureSubImage uploader(true);
361     for (size_t softwarePlaneIndex = 0; softwarePlaneIndex < softwarePlaneCount; ++softwarePlaneIndex) {
362         CCVideoLayerImpl::FramePlane& plane = m_framePlanes[softwarePlaneIndex];
363         const uint8_t* softwarePlanePixels = static_cast<const uint8_t*>(m_frame->data(softwarePlaneIndex));
364         IntRect planeRect(IntPoint(), plane.size);
365
366         context3d->bindTexture(GraphicsContext3D::TEXTURE_2D, plane.textureId);
367         uploader.setSubImageSize(plane.size);
368         uploader.upload(softwarePlanePixels, planeRect, planeRect, planeRect, plane.format, context);
369     }
370     return true;
371 }
372
373 void CCVideoLayerImpl::freePlaneData(CCGraphicsContext* context)
374 {
375     for (unsigned i = 0; i < WebKit::WebVideoFrame::maxPlanes; ++i)
376         m_framePlanes[i].freeData(context);
377 }
378
379 void CCVideoLayerImpl::freeUnusedPlaneData(CCGraphicsContext* context)
380 {
381     unsigned firstUnusedPlane = m_frame ? m_frame->planes() : 0;
382     for (unsigned i = firstUnusedPlane; i < WebKit::WebVideoFrame::maxPlanes; ++i)
383         m_framePlanes[i].freeData(context);
384 }
385
386 void CCVideoLayerImpl::didReceiveFrame()
387 {
388     setNeedsRedraw();
389 }
390
391 void CCVideoLayerImpl::didUpdateMatrix(const float matrix[16])
392 {
393     m_streamTextureMatrix = WebKit::WebTransformationMatrix(
394         matrix[0], matrix[1], matrix[2], matrix[3],
395         matrix[4], matrix[5], matrix[6], matrix[7],
396         matrix[8], matrix[9], matrix[10], matrix[11],
397         matrix[12], matrix[13], matrix[14], matrix[15]);
398     setNeedsRedraw();
399 }
400
401 void CCVideoLayerImpl::didLoseContext()
402 {
403     freePlaneData(layerTreeHostImpl()->context());
404 }
405
406 void CCVideoLayerImpl::setNeedsRedraw()
407 {
408     layerTreeHostImpl()->setNeedsRedraw();
409 }
410
411 void CCVideoLayerImpl::dumpLayerProperties(TextStream& ts, int indent) const
412 {
413     writeIndent(ts, indent);
414     ts << "video layer\n";
415     CCLayerImpl::dumpLayerProperties(ts, indent);
416 }
417
418 }
419
420 #endif // USE(ACCELERATED_COMPOSITING)