michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "TextureImageEGL.h" michael@0: #include "GLLibraryEGL.h" michael@0: #include "GLContext.h" michael@0: #include "GLUploadHelpers.h" michael@0: #include "gfxPlatform.h" michael@0: #include "gfx2DGlue.h" michael@0: #include "mozilla/gfx/Types.h" michael@0: michael@0: namespace mozilla { michael@0: namespace gl { michael@0: michael@0: static GLenum michael@0: GLFormatForImage(gfx::SurfaceFormat aFormat) michael@0: { michael@0: switch (aFormat) { michael@0: case gfx::SurfaceFormat::B8G8R8A8: michael@0: case gfx::SurfaceFormat::B8G8R8X8: michael@0: return LOCAL_GL_RGBA; michael@0: case gfx::SurfaceFormat::R5G6B5: michael@0: return LOCAL_GL_RGB; michael@0: case gfx::SurfaceFormat::A8: michael@0: return LOCAL_GL_LUMINANCE; michael@0: default: michael@0: NS_WARNING("Unknown GL format for Surface format"); michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: static GLenum michael@0: GLTypeForImage(gfx::SurfaceFormat aFormat) michael@0: { michael@0: switch (aFormat) { michael@0: case gfx::SurfaceFormat::B8G8R8A8: michael@0: case gfx::SurfaceFormat::B8G8R8X8: michael@0: case gfx::SurfaceFormat::A8: michael@0: return LOCAL_GL_UNSIGNED_BYTE; michael@0: case gfx::SurfaceFormat::R5G6B5: michael@0: return LOCAL_GL_UNSIGNED_SHORT_5_6_5; michael@0: default: michael@0: NS_WARNING("Unknown GL format for Surface format"); michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: TextureImageEGL::TextureImageEGL(GLuint aTexture, michael@0: const nsIntSize& aSize, michael@0: GLenum aWrapMode, michael@0: ContentType aContentType, michael@0: GLContext* aContext, michael@0: Flags aFlags, michael@0: TextureState aTextureState, michael@0: TextureImage::ImageFormat aImageFormat) michael@0: : TextureImage(aSize, aWrapMode, aContentType, aFlags) michael@0: , mGLContext(aContext) michael@0: , mUpdateFormat(gfx::ImageFormatToSurfaceFormat(aImageFormat)) michael@0: , mEGLImage(nullptr) michael@0: , mTexture(aTexture) michael@0: , mSurface(nullptr) michael@0: , mConfig(nullptr) michael@0: , mTextureState(aTextureState) michael@0: , mBound(false) michael@0: { michael@0: if (mUpdateFormat == gfx::SurfaceFormat::UNKNOWN) { michael@0: mUpdateFormat = gfx::ImageFormatToSurfaceFormat( michael@0: gfxPlatform::GetPlatform()->OptimalFormatForContent(GetContentType())); michael@0: } michael@0: michael@0: if (mUpdateFormat == gfx::SurfaceFormat::R5G6B5) { michael@0: mTextureFormat = gfx::SurfaceFormat::R8G8B8X8; michael@0: } else if (mUpdateFormat == gfx::SurfaceFormat::B8G8R8X8) { michael@0: mTextureFormat = gfx::SurfaceFormat::B8G8R8X8; michael@0: } else { michael@0: mTextureFormat = gfx::SurfaceFormat::B8G8R8A8; michael@0: } michael@0: } michael@0: michael@0: TextureImageEGL::~TextureImageEGL() michael@0: { michael@0: if (mGLContext->IsDestroyed() || !mGLContext->IsOwningThreadCurrent()) { michael@0: return; michael@0: } michael@0: michael@0: // If we have a context, then we need to delete the texture; michael@0: // if we don't have a context (either real or shared), michael@0: // then they went away when the contex was deleted, because it michael@0: // was the only one that had access to it. michael@0: mGLContext->MakeCurrent(); michael@0: mGLContext->fDeleteTextures(1, &mTexture); michael@0: ReleaseTexImage(); michael@0: DestroyEGLSurface(); michael@0: } michael@0: michael@0: void michael@0: TextureImageEGL::GetUpdateRegion(nsIntRegion& aForRegion) michael@0: { michael@0: if (mTextureState != Valid) { michael@0: // if the texture hasn't been initialized yet, force the michael@0: // client to paint everything michael@0: aForRegion = nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)); michael@0: } michael@0: michael@0: // We can only draw a rectangle, not subregions due to michael@0: // the way that our texture upload functions work. If michael@0: // needed, we /could/ do multiple texture uploads if we have michael@0: // non-overlapping rects, but that's a tradeoff. michael@0: aForRegion = nsIntRegion(aForRegion.GetBounds()); michael@0: } michael@0: michael@0: gfx::DrawTarget* michael@0: TextureImageEGL::BeginUpdate(nsIntRegion& aRegion) michael@0: { michael@0: NS_ASSERTION(!mUpdateDrawTarget, "BeginUpdate() without EndUpdate()?"); michael@0: michael@0: // determine the region the client will need to repaint michael@0: GetUpdateRegion(aRegion); michael@0: mUpdateRect = aRegion.GetBounds(); michael@0: michael@0: //printf_stderr("BeginUpdate with updateRect [%d %d %d %d]\n", mUpdateRect.x, mUpdateRect.y, mUpdateRect.width, mUpdateRect.height); michael@0: if (!nsIntRect(nsIntPoint(0, 0), gfx::ThebesIntSize(mSize)).Contains(mUpdateRect)) { michael@0: NS_ERROR("update outside of image"); michael@0: return nullptr; michael@0: } michael@0: michael@0: //printf_stderr("creating image surface %dx%d format %d\n", mUpdateRect.width, mUpdateRect.height, mUpdateFormat); michael@0: michael@0: mUpdateDrawTarget = gfx::Factory::CreateDrawTarget(gfx::BackendType::CAIRO, michael@0: gfx::IntSize(mUpdateRect.width, mUpdateRect.height), michael@0: mUpdateFormat); michael@0: michael@0: return mUpdateDrawTarget; michael@0: } michael@0: michael@0: void michael@0: TextureImageEGL::EndUpdate() michael@0: { michael@0: NS_ASSERTION(!!mUpdateDrawTarget, "EndUpdate() without BeginUpdate()?"); michael@0: michael@0: //printf_stderr("EndUpdate: slow path"); michael@0: michael@0: // This is the slower path -- we didn't have any way to set up michael@0: // a fast mapping between our cairo target surface and the GL michael@0: // texture, so we have to upload data. michael@0: michael@0: RefPtr updateSurface = nullptr; michael@0: RefPtr uploadImage = nullptr; michael@0: gfx::IntSize updateSize(mUpdateRect.width, mUpdateRect.height); michael@0: michael@0: NS_ASSERTION(mUpdateDrawTarget->GetSize() == updateSize, michael@0: "Upload image is the wrong size!"); michael@0: michael@0: updateSurface = mUpdateDrawTarget->Snapshot(); michael@0: uploadImage = updateSurface->GetDataSurface(); michael@0: michael@0: if (!uploadImage) { michael@0: return; michael@0: } michael@0: michael@0: mGLContext->MakeCurrent(); michael@0: mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture); michael@0: michael@0: if (mTextureState != Valid) { michael@0: NS_ASSERTION(mUpdateRect.x == 0 && mUpdateRect.y == 0 && michael@0: mUpdateRect.Size() == gfx::ThebesIntSize(mSize), michael@0: "Bad initial update on non-created texture!"); michael@0: michael@0: mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D, michael@0: 0, michael@0: GLFormatForImage(mUpdateFormat), michael@0: mUpdateRect.width, michael@0: mUpdateRect.height, michael@0: 0, michael@0: GLFormatForImage(uploadImage->GetFormat()), michael@0: GLTypeForImage(uploadImage->GetFormat()), michael@0: uploadImage->GetData()); michael@0: } else { michael@0: mGLContext->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, michael@0: 0, michael@0: mUpdateRect.x, michael@0: mUpdateRect.y, michael@0: mUpdateRect.width, michael@0: mUpdateRect.height, michael@0: GLFormatForImage(uploadImage->GetFormat()), michael@0: GLTypeForImage(uploadImage->GetFormat()), michael@0: uploadImage->GetData()); michael@0: } michael@0: michael@0: mUpdateDrawTarget = nullptr; michael@0: mTextureState = Valid; michael@0: return; // mTexture is bound michael@0: } michael@0: michael@0: bool michael@0: TextureImageEGL::DirectUpdate(gfx::DataSourceSurface* aSurf, const nsIntRegion& aRegion, const gfx::IntPoint& aFrom /* = gfx::IntPoint(0,0) */) michael@0: { michael@0: nsIntRect bounds = aRegion.GetBounds(); michael@0: michael@0: nsIntRegion region; michael@0: if (mTextureState != Valid) { michael@0: bounds = nsIntRect(0, 0, mSize.width, mSize.height); michael@0: region = nsIntRegion(bounds); michael@0: } else { michael@0: region = aRegion; michael@0: } michael@0: michael@0: mTextureFormat = michael@0: UploadSurfaceToTexture(mGLContext, michael@0: aSurf, michael@0: region, michael@0: mTexture, michael@0: mTextureState == Created, michael@0: bounds.TopLeft() + nsIntPoint(aFrom.x, aFrom.y), michael@0: false); michael@0: michael@0: mTextureState = Valid; michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: TextureImageEGL::BindTexture(GLenum aTextureUnit) michael@0: { michael@0: // Ensure the texture is allocated before it is used. michael@0: if (mTextureState == Created) { michael@0: Resize(mSize); michael@0: } michael@0: michael@0: mGLContext->fActiveTexture(aTextureUnit); michael@0: mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture); michael@0: mGLContext->fActiveTexture(LOCAL_GL_TEXTURE0); michael@0: } michael@0: michael@0: void michael@0: TextureImageEGL::Resize(const gfx::IntSize& aSize) michael@0: { michael@0: NS_ASSERTION(!mUpdateDrawTarget, "Resize() while in update?"); michael@0: michael@0: if (mSize == aSize && mTextureState != Created) michael@0: return; michael@0: michael@0: mGLContext->fBindTexture(LOCAL_GL_TEXTURE_2D, mTexture); michael@0: michael@0: mGLContext->fTexImage2D(LOCAL_GL_TEXTURE_2D, michael@0: 0, michael@0: GLFormatForImage(mUpdateFormat), michael@0: aSize.width, michael@0: aSize.height, michael@0: 0, michael@0: GLFormatForImage(mUpdateFormat), michael@0: GLTypeForImage(mUpdateFormat), michael@0: nullptr); michael@0: michael@0: mTextureState = Allocated; michael@0: mSize = aSize; michael@0: } michael@0: michael@0: bool michael@0: TextureImageEGL::BindTexImage() michael@0: { michael@0: if (mBound && !ReleaseTexImage()) michael@0: return false; michael@0: michael@0: EGLBoolean success = michael@0: sEGLLibrary.fBindTexImage(EGL_DISPLAY(), michael@0: (EGLSurface)mSurface, michael@0: LOCAL_EGL_BACK_BUFFER); michael@0: michael@0: if (success == LOCAL_EGL_FALSE) michael@0: return false; michael@0: michael@0: mBound = true; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: TextureImageEGL::ReleaseTexImage() michael@0: { michael@0: if (!mBound) michael@0: return true; michael@0: michael@0: EGLBoolean success = michael@0: sEGLLibrary.fReleaseTexImage(EGL_DISPLAY(), michael@0: (EGLSurface)mSurface, michael@0: LOCAL_EGL_BACK_BUFFER); michael@0: michael@0: if (success == LOCAL_EGL_FALSE) michael@0: return false; michael@0: michael@0: mBound = false; michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: TextureImageEGL::DestroyEGLSurface(void) michael@0: { michael@0: if (!mSurface) michael@0: return; michael@0: michael@0: sEGLLibrary.fDestroySurface(EGL_DISPLAY(), mSurface); michael@0: mSurface = nullptr; michael@0: } michael@0: michael@0: already_AddRefed michael@0: CreateTextureImageEGL(GLContext *gl, michael@0: const gfx::IntSize& aSize, michael@0: TextureImage::ContentType aContentType, michael@0: GLenum aWrapMode, michael@0: TextureImage::Flags aFlags, michael@0: TextureImage::ImageFormat aImageFormat) michael@0: { michael@0: nsRefPtr t = new gl::TiledTextureImage(gl, aSize, aContentType, aFlags, aImageFormat); michael@0: return t.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: TileGenFuncEGL(GLContext *gl, michael@0: const nsIntSize& aSize, michael@0: TextureImage::ContentType aContentType, michael@0: TextureImage::Flags aFlags, michael@0: TextureImage::ImageFormat aImageFormat) michael@0: { michael@0: gl->MakeCurrent(); michael@0: michael@0: GLuint texture; michael@0: gl->fGenTextures(1, &texture); michael@0: michael@0: nsRefPtr teximage = michael@0: new TextureImageEGL(texture, aSize, LOCAL_GL_CLAMP_TO_EDGE, aContentType, michael@0: gl, aFlags, TextureImage::Created, aImageFormat); michael@0: michael@0: teximage->BindTexture(LOCAL_GL_TEXTURE0); michael@0: michael@0: GLint texfilter = aFlags & TextureImage::UseNearestFilter ? LOCAL_GL_NEAREST : LOCAL_GL_LINEAR; michael@0: gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, texfilter); michael@0: gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, texfilter); michael@0: gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); michael@0: gl->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); michael@0: michael@0: return teximage.forget(); michael@0: } michael@0: michael@0: } michael@0: }