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