1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "ImageLayerOGL.h"
7 #include <stdint.h> // for uint32_t
8 #include "mozilla-config.h" // for GL_PROVIDER_GLX
9 #include "GLContext.h" // for GLContext, etc
10 #include "ImageContainer.h" // for CairoImage, etc
11 #include "ImageTypes.h" // for ImageFormat::CAIRO_SURFACE, etc
12 #include "SharedTextureImage.h" // for SharedTextureImage::Data, etc
13 #include "gfx3DMatrix.h" // for gfx3DMatrix
14 #include "gfxASurface.h" // for gfxASurface, etc
15 #include "gfxImageSurface.h" // for gfxImageSurface
16 #include "gfxUtils.h" // for NextPowerOfTwo
17 #include "mozilla/gfx/BaseSize.h" // for BaseSize
18 #include "mozilla/gfx/Types.h" // for SurfaceFormat
19 #include "mozilla/layers/LayersTypes.h"
20 #include "nsAutoRef.h" // for nsCountedRef, nsAutoRefBase
21 #include "nsCOMPtr.h" // for nsCOMPtr, already_AddRefed
22 #include "nsDebug.h" // for NS_ASSERTION, NS_ERROR
23 #include "nsIRunnable.h" // for nsIRunnable
24 #include "nsPoint.h" // for nsIntPoint
25 #include "nsRect.h" // for nsIntRect
26 #include "nsSize.h" // for nsIntSize
27 #include "nsThreadUtils.h" // for nsRunnable
28 #include "nscore.h" // for NS_IMETHOD
29 #include "LayerManagerOGL.h" // for LayerOGL::GLContext, etc
30 #if defined(GL_PROVIDER_GLX)
31 # include "GLXLibrary.h"
32 # include "gfxXlibSurface.h"
35 using namespace mozilla::gfx
;
36 using namespace mozilla::gl
;
44 * This is an event used to unref a GLContext on the main thread and
45 * optionally delete a texture associated with that context.
47 class TextureDeleter
: public nsRunnable
{
49 TextureDeleter(already_AddRefed
<GLContext
> aContext
,
51 : mContext(aContext
), mTexture(aTexture
)
53 NS_ASSERTION(aTexture
, "TextureDeleter instantiated with nothing to do");
57 mContext
->MakeCurrent();
58 mContext
->fDeleteTextures(1, &mTexture
);
60 // Ensure context is released on the main thread
65 nsRefPtr
<GLContext
> mContext
;
69 GLTexture::GLTexture()
74 GLTexture::~GLTexture()
80 GLTexture::Allocate(GLContext
*aContext
)
82 NS_ASSERTION(aContext
->IsGlobalSharedContext() || aContext
->IsOwningThreadCurrent(),
83 "Can only allocate texture on context's owning thread or with cx sharing");
89 mContext
->MakeCurrent();
90 mContext
->fGenTextures(1, &mTexture
);
94 GLTexture::TakeFrom(GLTexture
*aOther
)
98 mContext
= aOther
->mContext
.forget();
99 mTexture
= aOther
->mTexture
;
100 aOther
->mTexture
= 0;
107 NS_ASSERTION(!mTexture
, "Can't delete texture without a context");
111 if (mContext
->IsDestroyed() && !mContext
->IsGlobalSharedContext()) {
112 mContext
= mContext
->GetSharedContext();
114 NS_ASSERTION(!mTexture
,
115 "Context has been destroyed and couldn't find a shared context!");
121 if (mContext
->IsOwningThreadCurrent() || mContext
->IsGlobalSharedContext()) {
122 mContext
->MakeCurrent();
123 mContext
->fDeleteTextures(1, &mTexture
);
125 already_AddRefed
<GLContext
> context
= mContext
.forget();
126 nsCOMPtr
<nsIRunnable
> runnable
= new TextureDeleter(context
, mTexture
);
127 context
.get()->DispatchToOwningThread(runnable
);
136 TextureRecycleBin::TextureRecycleBin()
137 : mLock("mozilla.layers.TextureRecycleBin.mLock")
142 TextureRecycleBin::RecycleTexture(GLTexture
*aTexture
, TextureType aType
,
143 const gfxIntSize
& aSize
)
145 MutexAutoLock
lock(mLock
);
147 if (!aTexture
->IsAllocated())
150 if (!mRecycledTextures
[aType
].IsEmpty() && aSize
!= mRecycledTextureSizes
[aType
]) {
151 mRecycledTextures
[aType
].Clear();
153 mRecycledTextureSizes
[aType
] = aSize
;
154 mRecycledTextures
[aType
].AppendElement()->TakeFrom(aTexture
);
158 TextureRecycleBin::GetTexture(TextureType aType
, const gfxIntSize
& aSize
,
159 GLContext
*aContext
, GLTexture
*aOutTexture
)
161 MutexAutoLock
lock(mLock
);
163 if (mRecycledTextures
[aType
].IsEmpty() || mRecycledTextureSizes
[aType
] != aSize
) {
164 aOutTexture
->Allocate(aContext
);
167 uint32_t last
= mRecycledTextures
[aType
].Length() - 1;
168 aOutTexture
->TakeFrom(&mRecycledTextures
[aType
].ElementAt(last
));
169 mRecycledTextures
[aType
].RemoveElementAt(last
);
172 struct ImageOGLBackendData
: public ImageBackendData
178 AllocateTextureSharedTexture(SharedTextureImage
*aTexImage
, mozilla::gl::GLContext
* aGL
, GLenum aTarget
)
180 nsAutoPtr
<ImageOGLBackendData
> backendData(
181 new ImageOGLBackendData
);
183 backendData
->mTexture
.Allocate(aGL
);
185 aGL
->fBindTexture(aTarget
, backendData
->mTexture
.GetTextureID());
186 aGL
->fTexParameteri(aTarget
, LOCAL_GL_TEXTURE_MIN_FILTER
, LOCAL_GL_LINEAR
);
187 aGL
->fTexParameteri(aTarget
, LOCAL_GL_TEXTURE_MAG_FILTER
, LOCAL_GL_LINEAR
);
188 aGL
->fTexParameteri(aTarget
, LOCAL_GL_TEXTURE_WRAP_S
, LOCAL_GL_CLAMP_TO_EDGE
);
189 aGL
->fTexParameteri(aTarget
, LOCAL_GL_TEXTURE_WRAP_T
, LOCAL_GL_CLAMP_TO_EDGE
);
191 aTexImage
->SetBackendData(LAYERS_OPENGL
, backendData
.forget());
195 ImageLayerOGL::GetLayer()
201 ImageLayerOGL::RenderLayer(int,
202 const nsIntPoint
& aOffset
)
204 nsRefPtr
<ImageContainer
> container
= GetContainer();
206 if (!container
|| mOGLManager
->CompositingDisabled())
209 mOGLManager
->MakeCurrent();
211 AutoLockImage
autoLock(container
);
213 Image
*image
= autoLock
.GetImage();
218 NS_ASSERTION(image
->GetFormat() != REMOTE_IMAGE_BITMAP
,
219 "Remote images aren't handled yet in OGL layers!");
221 if (image
->GetFormat() == PLANAR_YCBCR
) {
222 PlanarYCbCrImage
*yuvImage
=
223 static_cast<PlanarYCbCrImage
*>(image
);
225 if (!yuvImage
->IsValid()) {
229 PlanarYCbCrOGLBackendData
*data
=
230 static_cast<PlanarYCbCrOGLBackendData
*>(yuvImage
->GetBackendData(LAYERS_OPENGL
));
232 if (data
&& data
->mTextures
->GetGLContext() != gl()) {
233 // If these textures were allocated by another layer manager,
234 // clear them out and re-allocate below.
236 yuvImage
->SetBackendData(LAYERS_OPENGL
, nullptr);
240 AllocateTexturesYCbCr(yuvImage
);
241 data
= static_cast<PlanarYCbCrOGLBackendData
*>(yuvImage
->GetBackendData(LAYERS_OPENGL
));
244 if (!data
|| data
->mTextures
->GetGLContext() != gl()) {
245 // XXX - Can this ever happen? If so I need to fix this!
250 gl()->fActiveTexture(LOCAL_GL_TEXTURE2
);
251 gl()->fBindTexture(LOCAL_GL_TEXTURE_2D
, data
->mTextures
[2].GetTextureID());
252 gl()->ApplyFilterToBoundTexture(mFilter
);
253 gl()->fActiveTexture(LOCAL_GL_TEXTURE1
);
254 gl()->fBindTexture(LOCAL_GL_TEXTURE_2D
, data
->mTextures
[1].GetTextureID());
255 gl()->ApplyFilterToBoundTexture(mFilter
);
256 gl()->fActiveTexture(LOCAL_GL_TEXTURE0
);
257 gl()->fBindTexture(LOCAL_GL_TEXTURE_2D
, data
->mTextures
[0].GetTextureID());
258 gl()->ApplyFilterToBoundTexture(mFilter
);
260 ShaderProgramOGL
*program
= mOGLManager
->GetProgram(YCbCrLayerProgramType
,
264 program
->SetLayerQuadRect(nsIntRect(0, 0,
265 yuvImage
->GetSize().width
,
266 yuvImage
->GetSize().height
));
267 program
->SetLayerTransform(GetEffectiveTransform());
268 program
->SetTextureTransform(gfx3DMatrix());
269 program
->SetLayerOpacity(GetEffectiveOpacity());
270 program
->SetRenderOffset(aOffset
);
271 program
->SetYCbCrTextureUnits(0, 1, 2);
272 program
->LoadMask(GetMaskLayer());
274 mOGLManager
->BindAndDrawQuadWithTextureRect(program
,
275 yuvImage
->GetData()->GetPictureRect(),
276 nsIntSize(yuvImage
->GetData()->mYSize
.width
,
277 yuvImage
->GetData()->mYSize
.height
));
279 // We shouldn't need to do this, but do it anyway just in case
280 // someone else forgets.
281 gl()->fActiveTexture(LOCAL_GL_TEXTURE0
);
282 } else if (image
->GetFormat() == CAIRO_SURFACE
) {
283 CairoImage
*cairoImage
=
284 static_cast<CairoImage
*>(image
);
286 if (!cairoImage
->mSurface
) {
290 NS_ASSERTION(cairoImage
->mSurface
->GetContentType() != gfxASurface::CONTENT_ALPHA
,
291 "Image layer has alpha image");
293 CairoOGLBackendData
*data
=
294 static_cast<CairoOGLBackendData
*>(cairoImage
->GetBackendData(LAYERS_OPENGL
));
296 if (data
&& data
->mTexture
.GetGLContext() != gl()) {
297 // If this texture was allocated by another layer manager, clear
298 // it out and re-allocate below.
300 cairoImage
->SetBackendData(LAYERS_OPENGL
, nullptr);
304 AllocateTexturesCairo(cairoImage
);
305 data
= static_cast<CairoOGLBackendData
*>(cairoImage
->GetBackendData(LAYERS_OPENGL
));
308 if (!data
|| data
->mTexture
.GetGLContext() != gl()) {
309 // XXX - Can this ever happen? If so I need to fix this!
315 gl()->fActiveTexture(LOCAL_GL_TEXTURE0
);
316 gl()->fBindTexture(LOCAL_GL_TEXTURE_2D
, data
->mTexture
.GetTextureID());
318 ShaderProgramOGL
*program
= mOGLManager
->GetProgram(data
->mLayerProgram
, GetMaskLayer());
320 gl()->ApplyFilterToBoundTexture(mFilter
);
323 program
->SetLayerQuadRect(nsIntRect(0, 0,
324 cairoImage
->GetSize().width
,
325 cairoImage
->GetSize().height
));
326 program
->SetLayerTransform(GetEffectiveTransform());
327 program
->SetTextureTransform(gfx3DMatrix());
328 program
->SetLayerOpacity(GetEffectiveOpacity());
329 program
->SetRenderOffset(aOffset
);
330 program
->SetTextureUnit(0);
331 program
->LoadMask(GetMaskLayer());
333 mOGLManager
->BindAndDrawQuad(program
);
334 } else if (image
->GetFormat() == SHARED_TEXTURE
) {
335 SharedTextureImage
* texImage
=
336 static_cast<SharedTextureImage
*>(image
);
337 const SharedTextureImage::Data
* data
= texImage
->GetData();
338 GLContext::SharedHandleDetails handleDetails
;
339 if (!gl()->GetSharedHandleDetails(data
->mShareType
, data
->mHandle
, handleDetails
)) {
340 NS_ERROR("Failed to get shared handle details");
344 ShaderProgramType programType
=
345 ShaderProgramFromTargetAndFormat(handleDetails
.mTarget
,
346 handleDetails
.mTextureFormat
);
347 ShaderProgramOGL
* program
= mOGLManager
->GetProgram(programType
, GetMaskLayer());
350 if (programType
== RGBARectLayerProgramType
) {
351 // 2DRect case, get the multiplier right for a sampler2DRect
352 program
->SetTexCoordMultiplier(data
->mSize
.width
, data
->mSize
.height
);
354 program
->SetLayerTransform(GetEffectiveTransform());
355 program
->SetTextureTransform(gfx3DMatrix());
356 program
->SetLayerOpacity(GetEffectiveOpacity());
357 program
->SetRenderOffset(aOffset
);
358 program
->SetTextureUnit(0);
359 program
->SetTextureTransform(handleDetails
.mTextureTransform
);
360 program
->LoadMask(GetMaskLayer());
362 if (!texImage
->GetBackendData(LAYERS_OPENGL
)) {
363 AllocateTextureSharedTexture(texImage
, gl(), handleDetails
.mTarget
);
366 ImageOGLBackendData
*backendData
=
367 static_cast<ImageOGLBackendData
*>(texImage
->GetBackendData(LAYERS_OPENGL
));
368 gl()->fActiveTexture(LOCAL_GL_TEXTURE0
);
369 gl()->fBindTexture(handleDetails
.mTarget
, backendData
->mTexture
.GetTextureID());
371 if (!gl()->AttachSharedHandle(data
->mShareType
, data
->mHandle
)) {
372 NS_ERROR("Failed to bind shared texture handle");
376 gl()->ApplyFilterToBoundTexture(handleDetails
.mTarget
, mFilter
);
377 program
->SetLayerQuadRect(nsIntRect(nsIntPoint(0, 0), data
->mSize
));
378 mOGLManager
->BindAndDrawQuad(program
, data
->mInverted
);
379 gl()->fBindTexture(handleDetails
.mTarget
, 0);
380 gl()->DetachSharedHandle(data
->mShareType
, data
->mHandle
);
382 GetContainer()->NotifyPaintedImage(image
);
386 SetClamping(GLContext
* aGL
, GLuint aTexture
)
388 aGL
->fBindTexture(LOCAL_GL_TEXTURE_2D
, aTexture
);
389 aGL
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_WRAP_S
, LOCAL_GL_CLAMP_TO_EDGE
);
390 aGL
->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_WRAP_T
, LOCAL_GL_CLAMP_TO_EDGE
);
394 UploadYUVToTexture(GLContext
* gl
, const PlanarYCbCrImage::Data
& aData
,
395 GLTexture
* aYTexture
,
396 GLTexture
* aUTexture
,
397 GLTexture
* aVTexture
)
399 nsIntRect
size(0, 0, aData
.mYSize
.width
, aData
.mYSize
.height
);
400 GLuint texture
= aYTexture
->GetTextureID();
401 nsRefPtr
<gfxASurface
> surf
= new gfxImageSurface(aData
.mYChannel
,
404 gfxASurface::ImageFormatA8
);
405 gl
->UploadSurfaceToTexture(surf
, size
, texture
, true);
407 size
= nsIntRect(0, 0, aData
.mCbCrSize
.width
, aData
.mCbCrSize
.height
);
408 texture
= aUTexture
->GetTextureID();
409 surf
= new gfxImageSurface(aData
.mCbChannel
,
412 gfxASurface::ImageFormatA8
);
413 gl
->UploadSurfaceToTexture(surf
, size
, texture
, true);
415 texture
= aVTexture
->GetTextureID();
416 surf
= new gfxImageSurface(aData
.mCrChannel
,
419 gfxASurface::ImageFormatA8
);
420 gl
->UploadSurfaceToTexture(surf
, size
, texture
, true);
423 ImageLayerOGL::ImageLayerOGL(LayerManagerOGL
*aManager
)
424 : ImageLayer(aManager
, nullptr)
426 , mTextureRecycleBin(new TextureRecycleBin())
428 mImplData
= static_cast<LayerOGL
*>(this);
432 ImageLayerOGL::AllocateTexturesYCbCr(PlanarYCbCrImage
*aImage
)
434 if (!aImage
->IsValid())
437 nsAutoPtr
<PlanarYCbCrOGLBackendData
> backendData(
438 new PlanarYCbCrOGLBackendData
);
440 const PlanarYCbCrImage::Data
*data
= aImage
->GetData();
444 mTextureRecycleBin
->GetTexture(TextureRecycleBin::TEXTURE_Y
, data
->mYSize
, gl(), &backendData
->mTextures
[0]);
445 SetClamping(gl(), backendData
->mTextures
[0].GetTextureID());
447 mTextureRecycleBin
->GetTexture(TextureRecycleBin::TEXTURE_C
, data
->mCbCrSize
, gl(), &backendData
->mTextures
[1]);
448 SetClamping(gl(), backendData
->mTextures
[1].GetTextureID());
450 mTextureRecycleBin
->GetTexture(TextureRecycleBin::TEXTURE_C
, data
->mCbCrSize
, gl(), &backendData
->mTextures
[2]);
451 SetClamping(gl(), backendData
->mTextures
[2].GetTextureID());
453 UploadYUVToTexture(gl(), *data
,
454 &backendData
->mTextures
[0],
455 &backendData
->mTextures
[1],
456 &backendData
->mTextures
[2]);
458 backendData
->mYSize
= data
->mYSize
;
459 backendData
->mCbCrSize
= data
->mCbCrSize
;
460 backendData
->mTextureRecycleBin
= mTextureRecycleBin
;
462 aImage
->SetBackendData(LAYERS_OPENGL
, backendData
.forget());
466 ImageLayerOGL::AllocateTexturesCairo(CairoImage
*aImage
)
468 nsAutoPtr
<CairoOGLBackendData
> backendData(
469 new CairoOGLBackendData
);
471 GLTexture
&texture
= backendData
->mTexture
;
473 texture
.Allocate(gl());
475 if (!texture
.IsAllocated()) {
479 mozilla::gl::GLContext
*gl
= texture
.GetGLContext();
482 GLuint tex
= texture
.GetTextureID();
483 gl
->fActiveTexture(LOCAL_GL_TEXTURE0
);
485 SetClamping(gl
, tex
);
487 #if defined(GL_PROVIDER_GLX)
488 if (aImage
->mSurface
->GetType() == gfxASurface::SurfaceTypeXlib
) {
489 gfxXlibSurface
*xsurf
=
490 static_cast<gfxXlibSurface
*>(aImage
->mSurface
.get());
491 GLXPixmap pixmap
= xsurf
->GetGLXPixmap();
493 backendData
->mLayerProgram
= ShaderProgramFromContentType(aImage
->mSurface
->GetContentType());
495 aImage
->SetBackendData(LAYERS_OPENGL
, backendData
.forget());
497 sDefGLXLib
.BindTexImage(pixmap
);
503 gfx::SurfaceFormat format
=
504 gl
->UploadSurfaceToTexture(aImage
->mSurface
,
505 nsIntRect(0,0, aImage
->mSize
.width
, aImage
->mSize
.height
),
507 backendData
->mLayerProgram
= ShaderProgramFromSurfaceFormat(format
);
509 aImage
->SetBackendData(LAYERS_OPENGL
, backendData
.forget());
513 * Returns a size that is larger than and closest to aSize where both
514 * width and height are powers of two.
515 * If the OpenGL setup is capable of using non-POT textures, then it
516 * will just return aSize.
519 CalculatePOTSize(const gfxIntSize
& aSize
, GLContext
* gl
)
521 if (gl
->CanUploadNonPowerOfTwo())
524 return gfxIntSize(NextPowerOfTwo(aSize
.width
), NextPowerOfTwo(aSize
.height
));
528 ImageLayerOGL::LoadAsTexture(GLuint aTextureUnit
, gfxIntSize
* aSize
)
530 // this method shares a lot of code with RenderLayer, but it doesn't seem
531 // to be possible to factor it out into a helper method
533 if (!GetContainer()) {
537 AutoLockImage
autoLock(GetContainer());
539 Image
*image
= autoLock
.GetImage();
544 if (image
->GetFormat() != CAIRO_SURFACE
) {
548 CairoImage
* cairoImage
= static_cast<CairoImage
*>(image
);
550 if (!cairoImage
->mSurface
) {
554 CairoOGLBackendData
*data
= static_cast<CairoOGLBackendData
*>(
555 cairoImage
->GetBackendData(LAYERS_OPENGL
));
558 NS_ASSERTION(cairoImage
->mSurface
->GetContentType() == gfxASurface::CONTENT_ALPHA
,
559 "OpenGL mask layers must be backed by alpha surfaces");
561 // allocate a new texture and save the details in the backend data
562 data
= new CairoOGLBackendData
;
563 data
->mTextureSize
= CalculatePOTSize(cairoImage
->mSize
, gl());
565 GLTexture
&texture
= data
->mTexture
;
566 texture
.Allocate(mOGLManager
->gl());
568 if (!texture
.IsAllocated()) {
572 mozilla::gl::GLContext
*texGL
= texture
.GetGLContext();
573 texGL
->MakeCurrent();
575 GLuint texID
= texture
.GetTextureID();
577 gfx::SurfaceFormat format
=
578 texGL
->UploadSurfaceToTexture(cairoImage
->mSurface
,
580 data
->mTextureSize
.width
,
581 data
->mTextureSize
.height
),
582 texID
, true, nsIntPoint(0,0), false,
584 data
->mLayerProgram
= ShaderProgramFromSurfaceFormat(format
);
586 cairoImage
->SetBackendData(LAYERS_OPENGL
, data
);
589 gl()->fActiveTexture(aTextureUnit
);
590 gl()->fBindTexture(LOCAL_GL_TEXTURE_2D
, texID
);
591 gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_MIN_FILTER
,
593 gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_MAG_FILTER
,
595 gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_WRAP_S
,
596 LOCAL_GL_CLAMP_TO_EDGE
);
597 gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D
, LOCAL_GL_TEXTURE_WRAP_T
,
598 LOCAL_GL_CLAMP_TO_EDGE
);
600 gl()->fActiveTexture(aTextureUnit
);
601 gl()->fBindTexture(LOCAL_GL_TEXTURE_2D
, data
->mTexture
.GetTextureID());
604 *aSize
= data
->mTextureSize
;