1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla Corporation code.
16 : *
17 : * The Initial Developer of the Original Code is Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2010
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Vladimir Vukicevic <vladimir@pobox.com>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "mozilla/layers/PLayers.h"
39 : #include "mozilla/layers/ShadowLayers.h"
40 :
41 : #include "gfxSharedImageSurface.h"
42 :
43 : #include "CanvasLayerOGL.h"
44 :
45 : #include "gfxImageSurface.h"
46 : #include "gfxContext.h"
47 : #include "GLContextProvider.h"
48 : #include "gfxPlatform.h"
49 :
50 : #ifdef XP_WIN
51 : #include "gfxWindowsSurface.h"
52 : #include "WGLLibrary.h"
53 : #endif
54 :
55 : #ifdef XP_MACOSX
56 : #include <OpenGL/OpenGL.h>
57 : #endif
58 :
59 : #ifdef MOZ_X11
60 : #include "gfxXlibSurface.h"
61 : #endif
62 :
63 : using namespace mozilla;
64 : using namespace mozilla::layers;
65 : using namespace mozilla::gl;
66 :
67 : void
68 0 : CanvasLayerOGL::Destroy()
69 : {
70 0 : if (!mDestroyed) {
71 0 : CleanupResources();
72 0 : mDestroyed = true;
73 : }
74 0 : }
75 :
76 : void
77 0 : CanvasLayerOGL::Initialize(const Data& aData)
78 : {
79 0 : NS_ASSERTION(mCanvasSurface == nsnull, "BasicCanvasLayer::Initialize called twice!");
80 :
81 0 : if (aData.mGLContext != nsnull &&
82 : aData.mSurface != nsnull)
83 : {
84 0 : NS_WARNING("CanvasLayerOGL can't have both surface and GLContext");
85 0 : return;
86 : }
87 :
88 0 : mOGLManager->MakeCurrent();
89 :
90 0 : if (aData.mDrawTarget) {
91 0 : mDrawTarget = aData.mDrawTarget;
92 0 : mNeedsYFlip = false;
93 0 : } else if (aData.mSurface) {
94 0 : mCanvasSurface = aData.mSurface;
95 0 : mNeedsYFlip = false;
96 : #if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
97 0 : if (aData.mSurface->GetType() == gfxASurface::SurfaceTypeXlib) {
98 0 : gfxXlibSurface *xsurf = static_cast<gfxXlibSurface*>(aData.mSurface);
99 0 : mPixmap = xsurf->GetGLXPixmap();
100 0 : if (mPixmap) {
101 0 : if (aData.mSurface->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA) {
102 0 : mLayerProgram = gl::RGBALayerProgramType;
103 : } else {
104 0 : mLayerProgram = gl::RGBXLayerProgramType;
105 : }
106 0 : MakeTexture();
107 : }
108 : }
109 : #endif
110 0 : } else if (aData.mGLContext) {
111 0 : if (!aData.mGLContext->IsOffscreen()) {
112 0 : NS_WARNING("CanvasLayerOGL with a non-offscreen GL context given");
113 0 : return;
114 : }
115 :
116 0 : mCanvasGLContext = aData.mGLContext;
117 0 : mGLBufferIsPremultiplied = aData.mGLBufferIsPremultiplied;
118 :
119 0 : mNeedsYFlip = true;
120 : } else {
121 0 : NS_WARNING("CanvasLayerOGL::Initialize called without surface or GL context!");
122 0 : return;
123 : }
124 :
125 0 : mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height);
126 :
127 : // Check the maximum texture size supported by GL. glTexImage2D supports
128 : // images of up to 2 + GL_MAX_TEXTURE_SIZE
129 0 : GLint texSize = gl()->GetMaxTextureSize();
130 0 : if (mBounds.width > (2 + texSize) || mBounds.height > (2 + texSize)) {
131 0 : mDelayedUpdates = true;
132 0 : MakeTexture();
133 : // This should only ever occur with 2d canvas, WebGL can't already have a texture
134 : // of this size can it?
135 0 : NS_ABORT_IF_FALSE(mCanvasSurface || mDrawTarget,
136 : "Invalid texture size when WebGL surface already exists at that size?");
137 : }
138 : }
139 :
140 : void
141 0 : CanvasLayerOGL::MakeTexture()
142 : {
143 0 : if (mTexture != 0)
144 0 : return;
145 :
146 0 : gl()->fGenTextures(1, &mTexture);
147 :
148 0 : gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
149 0 : gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
150 :
151 0 : gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
152 0 : gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
153 0 : gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
154 0 : gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
155 : }
156 :
157 : /**
158 : * Following UpdateSurface(), mTexture on context this->gl() should contain the data we want,
159 : * unless mDelayedUpdates is true because of a too-large surface.
160 : */
161 : void
162 0 : CanvasLayerOGL::UpdateSurface()
163 : {
164 0 : if (!mDirty)
165 0 : return;
166 0 : mDirty = false;
167 :
168 0 : if (mDestroyed || mDelayedUpdates) {
169 0 : return;
170 : }
171 :
172 : #if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
173 0 : if (mPixmap) {
174 0 : return;
175 : }
176 : #endif
177 :
178 0 : if (mCanvasGLContext &&
179 0 : mCanvasGLContext->GetContextType() == gl()->GetContextType())
180 : {
181 : // Can texture share, just make sure it's resolved first
182 0 : mCanvasGLContext->MakeCurrent();
183 0 : mCanvasGLContext->GuaranteeResolve();
184 :
185 0 : if (gl()->BindOffscreenNeedsTexture(mCanvasGLContext) &&
186 : mTexture == 0)
187 : {
188 0 : mOGLManager->MakeCurrent();
189 0 : MakeTexture();
190 : }
191 : } else {
192 0 : nsRefPtr<gfxASurface> updatedAreaSurface;
193 0 : if (mDrawTarget) {
194 : // TODO: This is suboptimal - We should have direct handling for the surface types instead of
195 : // going via a gfxASurface.
196 0 : updatedAreaSurface = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mDrawTarget);
197 0 : } else if (mCanvasSurface) {
198 0 : updatedAreaSurface = mCanvasSurface;
199 0 : } else if (mCanvasGLContext) {
200 : nsRefPtr<gfxImageSurface> updatedAreaImageSurface =
201 : new gfxImageSurface(gfxIntSize(mBounds.width, mBounds.height),
202 0 : gfxASurface::ImageFormatARGB32);
203 : mCanvasGLContext->ReadPixelsIntoImageSurface(0, 0,
204 : mBounds.width,
205 : mBounds.height,
206 0 : updatedAreaImageSurface);
207 0 : updatedAreaSurface = updatedAreaImageSurface;
208 : }
209 :
210 0 : mOGLManager->MakeCurrent();
211 : mLayerProgram =
212 : gl()->UploadSurfaceToTexture(updatedAreaSurface,
213 : mBounds,
214 : mTexture,
215 : false,
216 0 : nsIntPoint(0, 0));
217 : }
218 : }
219 :
220 : void
221 0 : CanvasLayerOGL::RenderLayer(int aPreviousDestination,
222 : const nsIntPoint& aOffset)
223 : {
224 0 : UpdateSurface();
225 0 : FireDidTransactionCallback();
226 :
227 0 : mOGLManager->MakeCurrent();
228 :
229 : // XXX We're going to need a different program depending on if
230 : // mGLBufferIsPremultiplied is TRUE or not. The RGBLayerProgram
231 : // assumes that it's true.
232 :
233 0 : gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
234 :
235 0 : if (mTexture) {
236 0 : gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture);
237 : }
238 :
239 0 : ColorTextureLayerProgram *program = nsnull;
240 :
241 : bool useGLContext = mCanvasGLContext &&
242 0 : mCanvasGLContext->GetContextType() == gl()->GetContextType();
243 :
244 0 : nsIntRect drawRect = mBounds;
245 :
246 0 : if (useGLContext) {
247 0 : gl()->BindTex2DOffscreen(mCanvasGLContext);
248 0 : program = mOGLManager->GetBasicLayerProgram(CanUseOpaqueSurface(), true);
249 0 : } else if (mDelayedUpdates) {
250 0 : NS_ABORT_IF_FALSE(mCanvasSurface || mDrawTarget, "WebGL canvases should always be using full texture upload");
251 :
252 0 : drawRect.IntersectRect(drawRect, GetEffectiveVisibleRegion().GetBounds());
253 :
254 0 : nsRefPtr<gfxASurface> surf = mCanvasSurface;
255 0 : if (mDrawTarget) {
256 0 : surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mDrawTarget);
257 : }
258 :
259 : mLayerProgram =
260 : gl()->UploadSurfaceToTexture(surf,
261 : nsIntRect(0, 0, drawRect.width, drawRect.height),
262 : mTexture,
263 : true,
264 0 : drawRect.TopLeft());
265 : }
266 :
267 0 : if (!program) {
268 0 : program = mOGLManager->GetColorTextureLayerProgram(mLayerProgram);
269 : }
270 :
271 : #if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
272 0 : if (mPixmap && !mDelayedUpdates) {
273 0 : sGLXLibrary.BindTexImage(mPixmap);
274 : }
275 : #endif
276 :
277 0 : gl()->ApplyFilterToBoundTexture(mFilter);
278 :
279 0 : program->Activate();
280 0 : program->SetLayerQuadRect(drawRect);
281 0 : program->SetLayerTransform(GetEffectiveTransform());
282 0 : program->SetLayerOpacity(GetEffectiveOpacity());
283 0 : program->SetRenderOffset(aOffset);
284 0 : program->SetTextureUnit(0);
285 :
286 0 : mOGLManager->BindAndDrawQuad(program, mNeedsYFlip ? true : false);
287 :
288 : #if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
289 0 : if (mPixmap && !mDelayedUpdates) {
290 0 : sGLXLibrary.ReleaseTexImage(mPixmap);
291 : }
292 : #endif
293 :
294 0 : if (useGLContext) {
295 0 : gl()->UnbindTex2DOffscreen(mCanvasGLContext);
296 : }
297 0 : }
298 :
299 : void
300 0 : CanvasLayerOGL::CleanupResources()
301 : {
302 0 : if (mTexture) {
303 0 : GLContext* cx = mOGLManager->glForResources();
304 0 : cx->MakeCurrent();
305 0 : cx->fDeleteTextures(1, &mTexture);
306 : }
307 0 : }
308 :
309 :
310 0 : ShadowCanvasLayerOGL::ShadowCanvasLayerOGL(LayerManagerOGL* aManager)
311 : : ShadowCanvasLayer(aManager, nsnull)
312 : , LayerOGL(aManager)
313 0 : , mNeedsYFlip(false)
314 : {
315 0 : mImplData = static_cast<LayerOGL*>(this);
316 0 : }
317 :
318 0 : ShadowCanvasLayerOGL::~ShadowCanvasLayerOGL()
319 0 : {}
320 :
321 : void
322 0 : ShadowCanvasLayerOGL::Initialize(const Data& aData)
323 : {
324 0 : NS_RUNTIMEABORT("Incompatibe surface type");
325 0 : }
326 :
327 : void
328 0 : ShadowCanvasLayerOGL::Init(const CanvasSurface& aNewFront, bool needYFlip)
329 : {
330 0 : nsRefPtr<gfxASurface> surf = ShadowLayerForwarder::OpenDescriptor(aNewFront);
331 :
332 0 : mTexImage = gl()->CreateTextureImage(surf->GetSize(),
333 : surf->GetContentType(),
334 0 : LOCAL_GL_CLAMP_TO_EDGE);
335 0 : mNeedsYFlip = needYFlip;
336 0 : }
337 :
338 : void
339 0 : ShadowCanvasLayerOGL::Swap(const CanvasSurface& aNewFront,
340 : bool needYFlip,
341 : CanvasSurface* aNewBack)
342 : {
343 0 : if (!mDestroyed) {
344 0 : nsRefPtr<gfxASurface> surf = ShadowLayerForwarder::OpenDescriptor(aNewFront);
345 0 : gfxIntSize sz = surf->GetSize();
346 0 : if (!mTexImage || mTexImage->GetSize() != sz ||
347 0 : mTexImage->GetContentType() != surf->GetContentType()) {
348 0 : Init(aNewFront, needYFlip);
349 : }
350 0 : nsIntRegion updateRegion(nsIntRect(0, 0, sz.width, sz.height));
351 0 : mTexImage->DirectUpdate(surf, updateRegion);
352 : }
353 :
354 0 : *aNewBack = aNewFront;
355 0 : }
356 :
357 : void
358 0 : ShadowCanvasLayerOGL::DestroyFrontBuffer()
359 : {
360 0 : mTexImage = nsnull;
361 0 : }
362 :
363 : void
364 0 : ShadowCanvasLayerOGL::Disconnect()
365 : {
366 0 : Destroy();
367 0 : }
368 :
369 : void
370 0 : ShadowCanvasLayerOGL::Destroy()
371 : {
372 0 : if (!mDestroyed) {
373 0 : mDestroyed = true;
374 0 : mTexImage = nsnull;
375 : }
376 0 : }
377 :
378 : Layer*
379 0 : ShadowCanvasLayerOGL::GetLayer()
380 : {
381 0 : return this;
382 : }
383 :
384 : void
385 0 : ShadowCanvasLayerOGL::RenderLayer(int aPreviousFrameBuffer,
386 : const nsIntPoint& aOffset)
387 : {
388 0 : mOGLManager->MakeCurrent();
389 :
390 : ColorTextureLayerProgram *program =
391 0 : mOGLManager->GetColorTextureLayerProgram(mTexImage->GetShaderProgramType());
392 :
393 :
394 0 : gfx3DMatrix effectiveTransform = GetEffectiveTransform();
395 : #ifdef ANDROID
396 : // Bug 691354
397 : // Using the LINEAR filter we get unexplained artifacts.
398 : // Use NEAREST when no scaling is required.
399 : gfxMatrix matrix;
400 : bool is2D = GetEffectiveTransform().Is2D(&matrix);
401 : if (is2D && !matrix.HasNonTranslationOrFlip()) {
402 : mTexImage->SetFilter(gfxPattern::FILTER_NEAREST);
403 : } else {
404 : mTexImage->SetFilter(mFilter);
405 : }
406 : #else
407 0 : mTexImage->SetFilter(mFilter);
408 : #endif
409 :
410 :
411 0 : program->Activate();
412 0 : program->SetLayerTransform(effectiveTransform);
413 0 : program->SetLayerOpacity(GetEffectiveOpacity());
414 0 : program->SetRenderOffset(aOffset);
415 0 : program->SetTextureUnit(0);
416 :
417 0 : mTexImage->BeginTileIteration();
418 0 : do {
419 0 : TextureImage::ScopedBindTextureAndApplyFilter texBind(mTexImage, LOCAL_GL_TEXTURE0);
420 0 : program->SetLayerQuadRect(mTexImage->GetTileRect());
421 0 : mOGLManager->BindAndDrawQuad(program, mNeedsYFlip); // FIXME flip order of tiles?
422 0 : } while (mTexImage->NextTile());
423 0 : }
424 :
425 : void
426 0 : ShadowCanvasLayerOGL::CleanupResources()
427 : {
428 0 : DestroyFrontBuffer();
429 0 : }
|