71958a92632c8d30820fa8762a353ee4f2a6afee
[gecko.git] / ImageLayerOGL.cpp
blob71958a92632c8d30820fa8762a353ee4f2a6afee
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"
33 #endif
35 using namespace mozilla::gfx;
36 using namespace mozilla::gl;
38 namespace mozilla {
39 namespace layers {
41 class Layer;
43 /**
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 {
48 public:
49 TextureDeleter(already_AddRefed<GLContext> aContext,
50 GLuint aTexture)
51 : mContext(aContext), mTexture(aTexture)
53 NS_ASSERTION(aTexture, "TextureDeleter instantiated with nothing to do");
56 NS_IMETHOD Run() {
57 mContext->MakeCurrent();
58 mContext->fDeleteTextures(1, &mTexture);
60 // Ensure context is released on the main thread
61 mContext = nullptr;
62 return NS_OK;
65 nsRefPtr<GLContext> mContext;
66 GLuint mTexture;
69 GLTexture::GLTexture()
70 : mTexture(0)
74 GLTexture::~GLTexture()
76 Release();
79 void
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");
85 Release();
87 mContext = aContext;
89 mContext->MakeCurrent();
90 mContext->fGenTextures(1, &mTexture);
93 void
94 GLTexture::TakeFrom(GLTexture *aOther)
96 Release();
98 mContext = aOther->mContext.forget();
99 mTexture = aOther->mTexture;
100 aOther->mTexture = 0;
103 void
104 GLTexture::Release()
106 if (!mContext) {
107 NS_ASSERTION(!mTexture, "Can't delete texture without a context");
108 return;
111 if (mContext->IsDestroyed() && !mContext->IsGlobalSharedContext()) {
112 mContext = mContext->GetSharedContext();
113 if (!mContext) {
114 NS_ASSERTION(!mTexture,
115 "Context has been destroyed and couldn't find a shared context!");
116 return;
120 if (mTexture) {
121 if (mContext->IsOwningThreadCurrent() || mContext->IsGlobalSharedContext()) {
122 mContext->MakeCurrent();
123 mContext->fDeleteTextures(1, &mTexture);
124 } else {
125 already_AddRefed<GLContext> context = mContext.forget();
126 nsCOMPtr<nsIRunnable> runnable = new TextureDeleter(context, mTexture);
127 context.get()->DispatchToOwningThread(runnable);
130 mTexture = 0;
133 mContext = nullptr;
136 TextureRecycleBin::TextureRecycleBin()
137 : mLock("mozilla.layers.TextureRecycleBin.mLock")
141 void
142 TextureRecycleBin::RecycleTexture(GLTexture *aTexture, TextureType aType,
143 const gfxIntSize& aSize)
145 MutexAutoLock lock(mLock);
147 if (!aTexture->IsAllocated())
148 return;
150 if (!mRecycledTextures[aType].IsEmpty() && aSize != mRecycledTextureSizes[aType]) {
151 mRecycledTextures[aType].Clear();
153 mRecycledTextureSizes[aType] = aSize;
154 mRecycledTextures[aType].AppendElement()->TakeFrom(aTexture);
157 void
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);
165 return;
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
174 GLTexture mTexture;
177 void
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());
194 Layer*
195 ImageLayerOGL::GetLayer()
197 return this;
200 void
201 ImageLayerOGL::RenderLayer(int,
202 const nsIntPoint& aOffset)
204 nsRefPtr<ImageContainer> container = GetContainer();
206 if (!container || mOGLManager->CompositingDisabled())
207 return;
209 mOGLManager->MakeCurrent();
211 AutoLockImage autoLock(container);
213 Image *image = autoLock.GetImage();
214 if (!image) {
215 return;
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()) {
226 return;
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.
235 data = nullptr;
236 yuvImage->SetBackendData(LAYERS_OPENGL, nullptr);
239 if (!data) {
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!
246 return;
249 gl()->MakeCurrent();
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,
261 GetMaskLayer());
263 program->Activate();
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) {
287 return;
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.
299 data = nullptr;
300 cairoImage->SetBackendData(LAYERS_OPENGL, nullptr);
303 if (!data) {
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!
310 return;
313 gl()->MakeCurrent();
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);
322 program->Activate();
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");
341 return;
344 ShaderProgramType programType =
345 ShaderProgramFromTargetAndFormat(handleDetails.mTarget,
346 handleDetails.mTextureFormat);
347 ShaderProgramOGL* program = mOGLManager->GetProgram(programType, GetMaskLayer());
349 program->Activate();
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");
373 return;
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);
385 static void
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);
393 static void
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,
402 aData.mYSize,
403 aData.mYStride,
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,
410 aData.mCbCrSize,
411 aData.mCbCrStride,
412 gfxASurface::ImageFormatA8);
413 gl->UploadSurfaceToTexture(surf, size, texture, true);
415 texture = aVTexture->GetTextureID();
416 surf = new gfxImageSurface(aData.mCrChannel,
417 aData.mCbCrSize,
418 aData.mCbCrStride,
419 gfxASurface::ImageFormatA8);
420 gl->UploadSurfaceToTexture(surf, size, texture, true);
423 ImageLayerOGL::ImageLayerOGL(LayerManagerOGL *aManager)
424 : ImageLayer(aManager, nullptr)
425 , LayerOGL(aManager)
426 , mTextureRecycleBin(new TextureRecycleBin())
428 mImplData = static_cast<LayerOGL*>(this);
431 void
432 ImageLayerOGL::AllocateTexturesYCbCr(PlanarYCbCrImage *aImage)
434 if (!aImage->IsValid())
435 return;
437 nsAutoPtr<PlanarYCbCrOGLBackendData> backendData(
438 new PlanarYCbCrOGLBackendData);
440 const PlanarYCbCrImage::Data *data = aImage->GetData();
442 gl()->MakeCurrent();
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());
465 void
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()) {
476 return;
479 mozilla::gl::GLContext *gl = texture.GetGLContext();
480 gl->MakeCurrent();
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();
492 if (pixmap) {
493 backendData->mLayerProgram = ShaderProgramFromContentType(aImage->mSurface->GetContentType());
495 aImage->SetBackendData(LAYERS_OPENGL, backendData.forget());
497 sDefGLXLib.BindTexImage(pixmap);
499 return;
502 #endif
503 gfx::SurfaceFormat format =
504 gl->UploadSurfaceToTexture(aImage->mSurface,
505 nsIntRect(0,0, aImage->mSize.width, aImage->mSize.height),
506 tex, true);
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.
518 static gfxIntSize
519 CalculatePOTSize(const gfxIntSize& aSize, GLContext* gl)
521 if (gl->CanUploadNonPowerOfTwo())
522 return aSize;
524 return gfxIntSize(NextPowerOfTwo(aSize.width), NextPowerOfTwo(aSize.height));
527 bool
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()) {
534 return false;
537 AutoLockImage autoLock(GetContainer());
539 Image *image = autoLock.GetImage();
540 if (!image) {
541 return false;
544 if (image->GetFormat() != CAIRO_SURFACE) {
545 return false;
548 CairoImage* cairoImage = static_cast<CairoImage*>(image);
550 if (!cairoImage->mSurface) {
551 return false;
554 CairoOGLBackendData *data = static_cast<CairoOGLBackendData*>(
555 cairoImage->GetBackendData(LAYERS_OPENGL));
557 if (!data) {
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()) {
569 return false;
572 mozilla::gl::GLContext *texGL = texture.GetGLContext();
573 texGL->MakeCurrent();
575 GLuint texID = texture.GetTextureID();
577 gfx::SurfaceFormat format =
578 texGL->UploadSurfaceToTexture(cairoImage->mSurface,
579 nsIntRect(0,0,
580 data->mTextureSize.width,
581 data->mTextureSize.height),
582 texID, true, nsIntPoint(0,0), false,
583 aTextureUnit);
584 data->mLayerProgram = ShaderProgramFromSurfaceFormat(format);
586 cairoImage->SetBackendData(LAYERS_OPENGL, data);
588 gl()->MakeCurrent();
589 gl()->fActiveTexture(aTextureUnit);
590 gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, texID);
591 gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER,
592 LOCAL_GL_LINEAR);
593 gl()->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER,
594 LOCAL_GL_LINEAR);
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);
599 } else {
600 gl()->fActiveTexture(aTextureUnit);
601 gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, data->mTexture.GetTextureID());
604 *aSize = data->mTextureSize;
605 return true;
608 } /* layers */
609 } /* mozilla */