diff options
Diffstat (limited to 'gfx/gl/GLBlitHelper.cpp')
-rw-r--r-- | gfx/gl/GLBlitHelper.cpp | 1033 |
1 files changed, 1033 insertions, 0 deletions
diff --git a/gfx/gl/GLBlitHelper.cpp b/gfx/gl/GLBlitHelper.cpp new file mode 100644 index 000000000..cffa5fbe5 --- /dev/null +++ b/gfx/gl/GLBlitHelper.cpp @@ -0,0 +1,1033 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gfxUtils.h" +#include "GLBlitHelper.h" +#include "GLContext.h" +#include "GLScreenBuffer.h" +#include "ScopedGLHelpers.h" +#include "mozilla/Preferences.h" +#include "ImageContainer.h" +#include "HeapCopyOfStackArray.h" +#include "mozilla/gfx/Matrix.h" +#include "mozilla/UniquePtr.h" + +#ifdef MOZ_WIDGET_ANDROID +#include "AndroidSurfaceTexture.h" +#include "GLImages.h" +#include "GLLibraryEGL.h" +#endif + +#ifdef XP_MACOSX +#include "MacIOSurfaceImage.h" +#include "GLContextCGL.h" +#endif + +using mozilla::layers::PlanarYCbCrImage; +using mozilla::layers::PlanarYCbCrData; + +namespace mozilla { +namespace gl { + +GLBlitHelper::GLBlitHelper(GLContext* gl) + : mGL(gl) + , mTexBlit_Buffer(0) + , mTexBlit_VertShader(0) + , mTex2DBlit_FragShader(0) + , mTex2DRectBlit_FragShader(0) + , mTex2DBlit_Program(0) + , mTex2DRectBlit_Program(0) + , mYFlipLoc(-1) + , mTextureTransformLoc(-1) + , mTexExternalBlit_FragShader(0) + , mTexYUVPlanarBlit_FragShader(0) + , mTexNV12PlanarBlit_FragShader(0) + , mTexExternalBlit_Program(0) + , mTexYUVPlanarBlit_Program(0) + , mTexNV12PlanarBlit_Program(0) + , mFBO(0) + , mSrcTexY(0) + , mSrcTexCb(0) + , mSrcTexCr(0) + , mSrcTexEGL(0) + , mYTexScaleLoc(-1) + , mCbCrTexScaleLoc(-1) + , mYuvColorMatrixLoc(-1) + , mTexWidth(0) + , mTexHeight(0) + , mCurYScale(1.0f) + , mCurCbCrScale(1.0f) +{ +} + +GLBlitHelper::~GLBlitHelper() +{ + if (!mGL->MakeCurrent()) + return; + + DeleteTexBlitProgram(); + + GLuint tex[] = { + mSrcTexY, + mSrcTexCb, + mSrcTexCr, + mSrcTexEGL, + }; + + mSrcTexY = mSrcTexCb = mSrcTexCr = mSrcTexEGL = 0; + mGL->fDeleteTextures(ArrayLength(tex), tex); + + if (mFBO) { + mGL->fDeleteFramebuffers(1, &mFBO); + } + mFBO = 0; +} + +// Allowed to be destructive of state we restore in functions below. +bool +GLBlitHelper::InitTexQuadProgram(BlitType target) +{ + const char kTexBlit_VertShaderSource[] = "\ + #version 100 \n\ + #ifdef GL_ES \n\ + precision mediump float; \n\ + #endif \n\ + attribute vec2 aPosition; \n\ + \n\ + uniform float uYflip; \n\ + varying vec2 vTexCoord; \n\ + \n\ + void main(void) \n\ + { \n\ + vTexCoord = aPosition; \n\ + vTexCoord.y = abs(vTexCoord.y - uYflip); \n\ + vec2 vertPos = aPosition * 2.0 - 1.0; \n\ + gl_Position = vec4(vertPos, 0.0, 1.0); \n\ + } \n\ + "; + + const char kTex2DBlit_FragShaderSource[] = "\ + #version 100 \n\ + #ifdef GL_ES \n\ + #ifdef GL_FRAGMENT_PRECISION_HIGH \n\ + precision highp float; \n\ + #else \n\ + precision mediump float; \n\ + #endif \n\ + #endif \n\ + uniform sampler2D uTexUnit; \n\ + \n\ + varying vec2 vTexCoord; \n\ + \n\ + void main(void) \n\ + { \n\ + gl_FragColor = texture2D(uTexUnit, vTexCoord); \n\ + } \n\ + "; + + const char kTex2DRectBlit_FragShaderSource[] = "\ + #version 100 \n\ + #ifdef GL_FRAGMENT_PRECISION_HIGH \n\ + precision highp float; \n\ + #else \n\ + precision mediump float; \n\ + #endif \n\ + \n\ + uniform sampler2D uTexUnit; \n\ + uniform vec2 uTexCoordMult; \n\ + \n\ + varying vec2 vTexCoord; \n\ + \n\ + void main(void) \n\ + { \n\ + gl_FragColor = texture2DRect(uTexUnit, \n\ + vTexCoord * uTexCoordMult); \n\ + } \n\ + "; +#ifdef ANDROID /* MOZ_WIDGET_ANDROID */ + const char kTexExternalBlit_FragShaderSource[] = "\ + #version 100 \n\ + #extension GL_OES_EGL_image_external : require \n\ + #ifdef GL_FRAGMENT_PRECISION_HIGH \n\ + precision highp float; \n\ + #else \n\ + precision mediump float; \n\ + #endif \n\ + varying vec2 vTexCoord; \n\ + uniform mat4 uTextureTransform; \n\ + uniform samplerExternalOES uTexUnit; \n\ + \n\ + void main() \n\ + { \n\ + gl_FragColor = texture2D(uTexUnit, \n\ + (uTextureTransform * vec4(vTexCoord, 0.0, 1.0)).xy); \n\ + } \n\ + "; +#endif + /* From Rec601: + [R] [1.1643835616438356, 0.0, 1.5960267857142858] [ Y - 16] + [G] = [1.1643835616438358, -0.3917622900949137, -0.8129676472377708] x [Cb - 128] + [B] [1.1643835616438356, 2.017232142857143, 8.862867620416422e-17] [Cr - 128] + + For [0,1] instead of [0,255], and to 5 places: + [R] [1.16438, 0.00000, 1.59603] [ Y - 0.06275] + [G] = [1.16438, -0.39176, -0.81297] x [Cb - 0.50196] + [B] [1.16438, 2.01723, 0.00000] [Cr - 0.50196] + + From Rec709: + [R] [1.1643835616438356, 4.2781193979771426e-17, 1.7927410714285714] [ Y - 16] + [G] = [1.1643835616438358, -0.21324861427372963, -0.532909328559444] x [Cb - 128] + [B] [1.1643835616438356, 2.1124017857142854, 0.0] [Cr - 128] + + For [0,1] instead of [0,255], and to 5 places: + [R] [1.16438, 0.00000, 1.79274] [ Y - 0.06275] + [G] = [1.16438, -0.21325, -0.53291] x [Cb - 0.50196] + [B] [1.16438, 2.11240, 0.00000] [Cr - 0.50196] + */ + const char kTexYUVPlanarBlit_FragShaderSource[] = "\ + #version 100 \n\ + #ifdef GL_ES \n\ + precision mediump float; \n\ + #endif \n\ + varying vec2 vTexCoord; \n\ + uniform sampler2D uYTexture; \n\ + uniform sampler2D uCbTexture; \n\ + uniform sampler2D uCrTexture; \n\ + uniform vec2 uYTexScale; \n\ + uniform vec2 uCbCrTexScale; \n\ + uniform mat3 uYuvColorMatrix; \n\ + void main() \n\ + { \n\ + float y = texture2D(uYTexture, vTexCoord * uYTexScale).r; \n\ + float cb = texture2D(uCbTexture, vTexCoord * uCbCrTexScale).r; \n\ + float cr = texture2D(uCrTexture, vTexCoord * uCbCrTexScale).r; \n\ + y = y - 0.06275; \n\ + cb = cb - 0.50196; \n\ + cr = cr - 0.50196; \n\ + vec3 yuv = vec3(y, cb, cr); \n\ + gl_FragColor.rgb = uYuvColorMatrix * yuv; \n\ + gl_FragColor.a = 1.0; \n\ + } \n\ + "; + +#ifdef XP_MACOSX + const char kTexNV12PlanarBlit_FragShaderSource[] = "\ + #version 100 \n\ + #extension GL_ARB_texture_rectangle : require \n\ + #ifdef GL_ES \n\ + precision mediump float \n\ + #endif \n\ + varying vec2 vTexCoord; \n\ + uniform sampler2DRect uYTexture; \n\ + uniform sampler2DRect uCbCrTexture; \n\ + uniform vec2 uYTexScale; \n\ + uniform vec2 uCbCrTexScale; \n\ + void main() \n\ + { \n\ + float y = texture2DRect(uYTexture, vTexCoord * uYTexScale).r; \n\ + float cb = texture2DRect(uCbCrTexture, vTexCoord * uCbCrTexScale).r; \n\ + float cr = texture2DRect(uCbCrTexture, vTexCoord * uCbCrTexScale).a; \n\ + y = (y - 0.06275) * 1.16438; \n\ + cb = cb - 0.50196; \n\ + cr = cr - 0.50196; \n\ + gl_FragColor.r = y + cr * 1.59603; \n\ + gl_FragColor.g = y - 0.81297 * cr - 0.39176 * cb; \n\ + gl_FragColor.b = y + cb * 2.01723; \n\ + gl_FragColor.a = 1.0; \n\ + } \n\ + "; +#endif + + bool success = false; + + GLuint* programPtr; + GLuint* fragShaderPtr; + const char* fragShaderSource; + switch (target) { + case ConvertEGLImage: + case BlitTex2D: + programPtr = &mTex2DBlit_Program; + fragShaderPtr = &mTex2DBlit_FragShader; + fragShaderSource = kTex2DBlit_FragShaderSource; + break; + case BlitTexRect: + programPtr = &mTex2DRectBlit_Program; + fragShaderPtr = &mTex2DRectBlit_FragShader; + fragShaderSource = kTex2DRectBlit_FragShaderSource; + break; +#ifdef ANDROID + case ConvertSurfaceTexture: + case ConvertGralloc: + programPtr = &mTexExternalBlit_Program; + fragShaderPtr = &mTexExternalBlit_FragShader; + fragShaderSource = kTexExternalBlit_FragShaderSource; + break; +#endif + case ConvertPlanarYCbCr: + programPtr = &mTexYUVPlanarBlit_Program; + fragShaderPtr = &mTexYUVPlanarBlit_FragShader; + fragShaderSource = kTexYUVPlanarBlit_FragShaderSource; + break; +#ifdef XP_MACOSX + case ConvertMacIOSurfaceImage: + programPtr = &mTexNV12PlanarBlit_Program; + fragShaderPtr = &mTexNV12PlanarBlit_FragShader; + fragShaderSource = kTexNV12PlanarBlit_FragShaderSource; + break; +#endif + default: + return false; + } + + GLuint& program = *programPtr; + GLuint& fragShader = *fragShaderPtr; + + // Use do-while(false) to let us break on failure + do { + if (program) { + // Already have it... + success = true; + break; + } + + if (!mTexBlit_Buffer) { + + /* CCW tri-strip: + * 2---3 + * | \ | + * 0---1 + */ + GLfloat verts[] = { + 0.0f, 0.0f, + 1.0f, 0.0f, + 0.0f, 1.0f, + 1.0f, 1.0f + }; + HeapCopyOfStackArray<GLfloat> vertsOnHeap(verts); + + MOZ_ASSERT(!mTexBlit_Buffer); + mGL->fGenBuffers(1, &mTexBlit_Buffer); + mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mTexBlit_Buffer); + + // Make sure we have a sane size. + mGL->fBufferData(LOCAL_GL_ARRAY_BUFFER, vertsOnHeap.ByteLength(), vertsOnHeap.Data(), LOCAL_GL_STATIC_DRAW); + } + + if (!mTexBlit_VertShader) { + + const char* vertShaderSource = kTexBlit_VertShaderSource; + + mTexBlit_VertShader = mGL->fCreateShader(LOCAL_GL_VERTEX_SHADER); + mGL->fShaderSource(mTexBlit_VertShader, 1, &vertShaderSource, nullptr); + mGL->fCompileShader(mTexBlit_VertShader); + } + + MOZ_ASSERT(!fragShader); + fragShader = mGL->fCreateShader(LOCAL_GL_FRAGMENT_SHADER); + mGL->fShaderSource(fragShader, 1, &fragShaderSource, nullptr); + mGL->fCompileShader(fragShader); + + program = mGL->fCreateProgram(); + mGL->fAttachShader(program, mTexBlit_VertShader); + mGL->fAttachShader(program, fragShader); + mGL->fBindAttribLocation(program, 0, "aPosition"); + mGL->fLinkProgram(program); + + if (GLContext::ShouldSpew()) { + GLint status = 0; + mGL->fGetShaderiv(mTexBlit_VertShader, LOCAL_GL_COMPILE_STATUS, &status); + if (status != LOCAL_GL_TRUE) { + NS_ERROR("Vert shader compilation failed."); + + GLint length = 0; + mGL->fGetShaderiv(mTexBlit_VertShader, LOCAL_GL_INFO_LOG_LENGTH, &length); + if (!length) { + printf_stderr("No shader info log available.\n"); + break; + } + + auto buffer = MakeUnique<char[]>(length); + mGL->fGetShaderInfoLog(mTexBlit_VertShader, length, nullptr, buffer.get()); + + printf_stderr("Shader info log (%d bytes): %s\n", length, buffer.get()); + break; + } + + status = 0; + mGL->fGetShaderiv(fragShader, LOCAL_GL_COMPILE_STATUS, &status); + if (status != LOCAL_GL_TRUE) { + NS_ERROR("Frag shader compilation failed."); + + GLint length = 0; + mGL->fGetShaderiv(fragShader, LOCAL_GL_INFO_LOG_LENGTH, &length); + if (!length) { + printf_stderr("No shader info log available.\n"); + break; + } + + auto buffer = MakeUnique<char[]>(length); + mGL->fGetShaderInfoLog(fragShader, length, nullptr, buffer.get()); + + printf_stderr("Shader info log (%d bytes): %s\n", length, buffer.get()); + break; + } + } + + GLint status = 0; + mGL->fGetProgramiv(program, LOCAL_GL_LINK_STATUS, &status); + if (status != LOCAL_GL_TRUE) { + if (GLContext::ShouldSpew()) { + NS_ERROR("Linking blit program failed."); + GLint length = 0; + mGL->fGetProgramiv(program, LOCAL_GL_INFO_LOG_LENGTH, &length); + if (!length) { + printf_stderr("No program info log available.\n"); + break; + } + + auto buffer = MakeUnique<char[]>(length); + mGL->fGetProgramInfoLog(program, length, nullptr, buffer.get()); + + printf_stderr("Program info log (%d bytes): %s\n", length, buffer.get()); + } + break; + } + + // Cache and set attribute and uniform + mGL->fUseProgram(program); + switch (target) { +#ifdef ANDROID + case ConvertSurfaceTexture: + case ConvertGralloc: +#endif + case BlitTex2D: + case BlitTexRect: + case ConvertEGLImage: { + GLint texUnitLoc = mGL->fGetUniformLocation(program, "uTexUnit"); + MOZ_ASSERT(texUnitLoc != -1, "uniform uTexUnit not found"); + mGL->fUniform1i(texUnitLoc, 0); + break; + } + case ConvertPlanarYCbCr: { + GLint texY = mGL->fGetUniformLocation(program, "uYTexture"); + GLint texCb = mGL->fGetUniformLocation(program, "uCbTexture"); + GLint texCr = mGL->fGetUniformLocation(program, "uCrTexture"); + mYTexScaleLoc = mGL->fGetUniformLocation(program, "uYTexScale"); + mCbCrTexScaleLoc = mGL->fGetUniformLocation(program, "uCbCrTexScale"); + mYuvColorMatrixLoc = mGL->fGetUniformLocation(program, "uYuvColorMatrix"); + + DebugOnly<bool> hasUniformLocations = texY != -1 && + texCb != -1 && + texCr != -1 && + mYTexScaleLoc != -1 && + mCbCrTexScaleLoc != -1 && + mYuvColorMatrixLoc != -1; + MOZ_ASSERT(hasUniformLocations, "uniforms not found"); + + mGL->fUniform1i(texY, Channel_Y); + mGL->fUniform1i(texCb, Channel_Cb); + mGL->fUniform1i(texCr, Channel_Cr); + break; + } + case ConvertMacIOSurfaceImage: { +#ifdef XP_MACOSX + GLint texY = mGL->fGetUniformLocation(program, "uYTexture"); + GLint texCbCr = mGL->fGetUniformLocation(program, "uCbCrTexture"); + mYTexScaleLoc = mGL->fGetUniformLocation(program, "uYTexScale"); + mCbCrTexScaleLoc= mGL->fGetUniformLocation(program, "uCbCrTexScale"); + + DebugOnly<bool> hasUniformLocations = texY != -1 && + texCbCr != -1 && + mYTexScaleLoc != -1 && + mCbCrTexScaleLoc != -1; + MOZ_ASSERT(hasUniformLocations, "uniforms not found"); + + mGL->fUniform1i(texY, Channel_Y); + mGL->fUniform1i(texCbCr, Channel_Cb); +#endif + break; + } + default: + return false; + } + MOZ_ASSERT(mGL->fGetAttribLocation(program, "aPosition") == 0); + mYFlipLoc = mGL->fGetUniformLocation(program, "uYflip"); + MOZ_ASSERT(mYFlipLoc != -1, "uniform: uYflip not found"); + mTextureTransformLoc = mGL->fGetUniformLocation(program, "uTextureTransform"); + if (mTextureTransformLoc >= 0) { + // Set identity matrix as default + gfx::Matrix4x4 identity; + mGL->fUniformMatrix4fv(mTextureTransformLoc, 1, false, &identity._11); + } + success = true; + } while (false); + + if (!success) { + // Clean up: + DeleteTexBlitProgram(); + return false; + } + + mGL->fUseProgram(program); + mGL->fEnableVertexAttribArray(0); + mGL->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mTexBlit_Buffer); + mGL->fVertexAttribPointer(0, + 2, + LOCAL_GL_FLOAT, + false, + 0, + nullptr); + return true; +} + +bool +GLBlitHelper::UseTexQuadProgram(BlitType target, const gfx::IntSize& srcSize) +{ + if (!InitTexQuadProgram(target)) { + return false; + } + + if (target == BlitTexRect) { + GLint texCoordMultLoc = mGL->fGetUniformLocation(mTex2DRectBlit_Program, "uTexCoordMult"); + MOZ_ASSERT(texCoordMultLoc != -1, "uniform not found"); + mGL->fUniform2f(texCoordMultLoc, srcSize.width, srcSize.height); + } + + return true; +} + +void +GLBlitHelper::DeleteTexBlitProgram() +{ + if (mTexBlit_Buffer) { + mGL->fDeleteBuffers(1, &mTexBlit_Buffer); + mTexBlit_Buffer = 0; + } + if (mTexBlit_VertShader) { + mGL->fDeleteShader(mTexBlit_VertShader); + mTexBlit_VertShader = 0; + } + if (mTex2DBlit_FragShader) { + mGL->fDeleteShader(mTex2DBlit_FragShader); + mTex2DBlit_FragShader = 0; + } + if (mTex2DRectBlit_FragShader) { + mGL->fDeleteShader(mTex2DRectBlit_FragShader); + mTex2DRectBlit_FragShader = 0; + } + if (mTex2DBlit_Program) { + mGL->fDeleteProgram(mTex2DBlit_Program); + mTex2DBlit_Program = 0; + } + if (mTex2DRectBlit_Program) { + mGL->fDeleteProgram(mTex2DRectBlit_Program); + mTex2DRectBlit_Program = 0; + } + if (mTexExternalBlit_FragShader) { + mGL->fDeleteShader(mTexExternalBlit_FragShader); + mTexExternalBlit_FragShader = 0; + } + if (mTexYUVPlanarBlit_FragShader) { + mGL->fDeleteShader(mTexYUVPlanarBlit_FragShader); + mTexYUVPlanarBlit_FragShader = 0; + } + if (mTexNV12PlanarBlit_FragShader) { + mGL->fDeleteShader(mTexNV12PlanarBlit_FragShader); + mTexNV12PlanarBlit_FragShader = 0; + } + if (mTexExternalBlit_Program) { + mGL->fDeleteProgram(mTexExternalBlit_Program); + mTexExternalBlit_Program = 0; + } + if (mTexYUVPlanarBlit_Program) { + mGL->fDeleteProgram(mTexYUVPlanarBlit_Program); + mTexYUVPlanarBlit_Program = 0; + } + if (mTexNV12PlanarBlit_Program) { + mGL->fDeleteProgram(mTexNV12PlanarBlit_Program); + mTexNV12PlanarBlit_Program = 0; + } +} + +void +GLBlitHelper::BlitFramebufferToFramebuffer(GLuint srcFB, GLuint destFB, + const gfx::IntSize& srcSize, + const gfx::IntSize& destSize, + bool internalFBs) +{ + MOZ_ASSERT(!srcFB || mGL->fIsFramebuffer(srcFB)); + MOZ_ASSERT(!destFB || mGL->fIsFramebuffer(destFB)); + + MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit)); + + ScopedBindFramebuffer boundFB(mGL); + ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false); + + if (internalFBs) { + mGL->Screen()->BindReadFB_Internal(srcFB); + mGL->Screen()->BindDrawFB_Internal(destFB); + } else { + mGL->BindReadFB(srcFB); + mGL->BindDrawFB(destFB); + } + + mGL->fBlitFramebuffer(0, 0, srcSize.width, srcSize.height, + 0, 0, destSize.width, destSize.height, + LOCAL_GL_COLOR_BUFFER_BIT, + LOCAL_GL_NEAREST); +} + +void +GLBlitHelper::BlitFramebufferToFramebuffer(GLuint srcFB, GLuint destFB, + const gfx::IntSize& srcSize, + const gfx::IntSize& destSize, + const GLFormats& srcFormats, + bool internalFBs) +{ + MOZ_ASSERT(!srcFB || mGL->fIsFramebuffer(srcFB)); + MOZ_ASSERT(!destFB || mGL->fIsFramebuffer(destFB)); + + if (mGL->IsSupported(GLFeature::framebuffer_blit)) { + BlitFramebufferToFramebuffer(srcFB, destFB, + srcSize, destSize, + internalFBs); + return; + } + + GLuint tex = CreateTextureForOffscreen(mGL, srcFormats, srcSize); + MOZ_ASSERT(tex); + + BlitFramebufferToTexture(srcFB, tex, srcSize, srcSize, internalFBs); + BlitTextureToFramebuffer(tex, destFB, srcSize, destSize, internalFBs); + + mGL->fDeleteTextures(1, &tex); +} + +void +GLBlitHelper::BindAndUploadYUVTexture(Channel which, + uint32_t width, + uint32_t height, + void* data, + bool needsAllocation) +{ + MOZ_ASSERT(which < Channel_Max, "Invalid channel!"); + GLuint* srcTexArr[3] = {&mSrcTexY, &mSrcTexCb, &mSrcTexCr}; + GLuint& tex = *srcTexArr[which]; + + // RED textures aren't valid in GLES2, and ALPHA textures are not valid in desktop GL Core Profiles. + // So use R8 textures on GL3.0+ and GLES3.0+, but LUMINANCE/LUMINANCE/UNSIGNED_BYTE otherwise. + GLenum format; + GLenum internalFormat; + if (mGL->IsAtLeast(gl::ContextProfile::OpenGLCore, 300) || + mGL->IsAtLeast(gl::ContextProfile::OpenGLES, 300)) { + format = LOCAL_GL_RED; + internalFormat = LOCAL_GL_R8; + } else { + format = LOCAL_GL_LUMINANCE; + internalFormat = LOCAL_GL_LUMINANCE; + } + + if (!tex) { + MOZ_ASSERT(needsAllocation); + tex = CreateTexture(mGL, internalFormat, format, LOCAL_GL_UNSIGNED_BYTE, + gfx::IntSize(width, height), false); + } + mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + which); + + mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, tex); + if (!needsAllocation) { + mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, + 0, + 0, + 0, + width, + height, + format, + LOCAL_GL_UNSIGNED_BYTE, + data); + } else { + mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, + 0, + internalFormat, + width, + height, + 0, + format, + LOCAL_GL_UNSIGNED_BYTE, + data); + } +} + +void +GLBlitHelper::BindAndUploadEGLImage(EGLImage image, GLuint target) +{ + MOZ_ASSERT(image != EGL_NO_IMAGE, "Bad EGLImage"); + + if (!mSrcTexEGL) { + mGL->fGenTextures(1, &mSrcTexEGL); + mGL->fBindTexture(target, mSrcTexEGL); + mGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); + mGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); + mGL->fTexParameteri(target, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST); + mGL->fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST); + } else { + mGL->fBindTexture(target, mSrcTexEGL); + } + mGL->fEGLImageTargetTexture2D(target, image); +} + +#ifdef MOZ_WIDGET_ANDROID + +#define ATTACH_WAIT_MS 50 + +bool +GLBlitHelper::BlitSurfaceTextureImage(layers::SurfaceTextureImage* stImage) +{ + AndroidSurfaceTexture* surfaceTexture = stImage->GetSurfaceTexture(); + + ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0); + + if (NS_FAILED(surfaceTexture->Attach(mGL, PR_MillisecondsToInterval(ATTACH_WAIT_MS)))) + return false; + + // UpdateTexImage() changes the EXTERNAL binding, so save it here + // so we can restore it after. + int oldBinding = 0; + mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_EXTERNAL, &oldBinding); + + surfaceTexture->UpdateTexImage(); + + gfx::Matrix4x4 transform; + surfaceTexture->GetTransformMatrix(transform); + + mGL->fUniformMatrix4fv(mTextureTransformLoc, 1, false, &transform._11); + mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4); + + surfaceTexture->Detach(); + + mGL->fBindTexture(LOCAL_GL_TEXTURE_EXTERNAL, oldBinding); + return true; +} + +bool +GLBlitHelper::BlitEGLImageImage(layers::EGLImageImage* image) +{ + EGLImage eglImage = image->GetImage(); + EGLSync eglSync = image->GetSync(); + + if (eglSync) { + EGLint status = sEGLLibrary.fClientWaitSync(EGL_DISPLAY(), eglSync, 0, LOCAL_EGL_FOREVER); + if (status != LOCAL_EGL_CONDITION_SATISFIED) { + return false; + } + } + + ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0); + + int oldBinding = 0; + mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldBinding); + + BindAndUploadEGLImage(eglImage, LOCAL_GL_TEXTURE_2D); + + mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4); + + mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, oldBinding); + return true; +} + +#endif + +bool +GLBlitHelper::BlitPlanarYCbCrImage(layers::PlanarYCbCrImage* yuvImage) +{ + ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0); + const PlanarYCbCrData* yuvData = yuvImage->GetData(); + + bool needsAllocation = false; + if (mTexWidth != yuvData->mYStride || mTexHeight != yuvData->mYSize.height) { + mTexWidth = yuvData->mYStride; + mTexHeight = yuvData->mYSize.height; + needsAllocation = true; + } + + GLint oldTex[3]; + for (int i = 0; i < 3; i++) { + mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i); + mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldTex[i]); + } + BindAndUploadYUVTexture(Channel_Y, yuvData->mYStride, yuvData->mYSize.height, yuvData->mYChannel, needsAllocation); + BindAndUploadYUVTexture(Channel_Cb, yuvData->mCbCrStride, yuvData->mCbCrSize.height, yuvData->mCbChannel, needsAllocation); + BindAndUploadYUVTexture(Channel_Cr, yuvData->mCbCrStride, yuvData->mCbCrSize.height, yuvData->mCrChannel, needsAllocation); + + if (needsAllocation) { + mGL->fUniform2f(mYTexScaleLoc, (float)yuvData->mYSize.width/yuvData->mYStride, 1.0f); + mGL->fUniform2f(mCbCrTexScaleLoc, (float)yuvData->mCbCrSize.width/yuvData->mCbCrStride, 1.0f); + } + + float* yuvToRgb = gfxUtils::Get3x3YuvColorMatrix(yuvData->mYUVColorSpace); + mGL->fUniformMatrix3fv(mYuvColorMatrixLoc, 1, 0, yuvToRgb); + + mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4); + for (int i = 0; i < 3; i++) { + mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i); + mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, oldTex[i]); + } + return true; +} + +#ifdef XP_MACOSX +bool +GLBlitHelper::BlitMacIOSurfaceImage(layers::MacIOSurfaceImage* ioImage) +{ + ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0); + MacIOSurface* surf = ioImage->GetSurface(); + + GLint oldTex[2]; + for (int i = 0; i < 2; i++) { + mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i); + mGL->fGetIntegerv(LOCAL_GL_TEXTURE_BINDING_2D, &oldTex[i]); + } + + GLuint textures[2]; + mGL->fGenTextures(2, textures); + + mGL->fActiveTexture(LOCAL_GL_TEXTURE0); + mGL->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, textures[0]); + mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); + mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); + surf->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(mGL)->GetCGLContext(), 0); + mGL->fUniform2f(mYTexScaleLoc, surf->GetWidth(0), surf->GetHeight(0)); + + mGL->fActiveTexture(LOCAL_GL_TEXTURE1); + mGL->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, textures[1]); + mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); + mGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); + surf->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(mGL)->GetCGLContext(), 1); + mGL->fUniform2f(mCbCrTexScaleLoc, surf->GetWidth(1), surf->GetHeight(1)); + + mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4); + for (int i = 0; i < 2; i++) { + mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i); + mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, oldTex[i]); + } + + mGL->fDeleteTextures(2, textures); + return true; +} +#endif + +bool +GLBlitHelper::BlitImageToFramebuffer(layers::Image* srcImage, + const gfx::IntSize& destSize, + GLuint destFB, + OriginPos destOrigin) +{ + ScopedGLDrawState autoStates(mGL); + + BlitType type; + OriginPos srcOrigin; + + switch (srcImage->GetFormat()) { + case ImageFormat::PLANAR_YCBCR: + type = ConvertPlanarYCbCr; + srcOrigin = OriginPos::BottomLeft; + break; + +#ifdef MOZ_WIDGET_ANDROID + case ImageFormat::SURFACE_TEXTURE: + type = ConvertSurfaceTexture; + srcOrigin = srcImage->AsSurfaceTextureImage()->GetOriginPos(); + break; + case ImageFormat::EGLIMAGE: + type = ConvertEGLImage; + srcOrigin = srcImage->AsEGLImageImage()->GetOriginPos(); + break; +#endif +#ifdef XP_MACOSX + case ImageFormat::MAC_IOSURFACE: + type = ConvertMacIOSurfaceImage; + srcOrigin = OriginPos::TopLeft; + break; +#endif + + default: + return false; + } + + bool init = InitTexQuadProgram(type); + if (!init) { + return false; + } + + const bool needsYFlip = (srcOrigin != destOrigin); + mGL->fUniform1f(mYFlipLoc, needsYFlip ? (float)1.0 : (float)0.0); + + ScopedBindFramebuffer boundFB(mGL, destFB); + mGL->fColorMask(LOCAL_GL_TRUE, LOCAL_GL_TRUE, LOCAL_GL_TRUE, LOCAL_GL_TRUE); + mGL->fViewport(0, 0, destSize.width, destSize.height); + + switch (type) { + case ConvertPlanarYCbCr: { + const auto saved = mGL->GetIntAs<GLint>(LOCAL_GL_UNPACK_ALIGNMENT); + mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 1); + const auto ret = BlitPlanarYCbCrImage(static_cast<PlanarYCbCrImage*>(srcImage)); + mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, saved); + return ret; + } + +#ifdef MOZ_WIDGET_ANDROID + case ConvertSurfaceTexture: + return BlitSurfaceTextureImage(static_cast<layers::SurfaceTextureImage*>(srcImage)); + + case ConvertEGLImage: + return BlitEGLImageImage(static_cast<layers::EGLImageImage*>(srcImage)); +#endif + +#ifdef XP_MACOSX + case ConvertMacIOSurfaceImage: + return BlitMacIOSurfaceImage(srcImage->AsMacIOSurfaceImage()); +#endif + + default: + return false; + } +} + +bool +GLBlitHelper::BlitImageToTexture(layers::Image* srcImage, + const gfx::IntSize& destSize, + GLuint destTex, + GLenum destTarget, + OriginPos destOrigin) +{ + ScopedFramebufferForTexture autoFBForTex(mGL, destTex, destTarget); + if (!autoFBForTex.IsComplete()) + return false; + + return BlitImageToFramebuffer(srcImage, destSize, autoFBForTex.FB(), destOrigin); +} + +void +GLBlitHelper::BlitTextureToFramebuffer(GLuint srcTex, GLuint destFB, + const gfx::IntSize& srcSize, + const gfx::IntSize& destSize, + GLenum srcTarget, + bool internalFBs) +{ + MOZ_ASSERT(mGL->fIsTexture(srcTex)); + MOZ_ASSERT(!destFB || mGL->fIsFramebuffer(destFB)); + + if (mGL->IsSupported(GLFeature::framebuffer_blit)) { + ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget); + MOZ_DIAGNOSTIC_ASSERT(srcWrapper.IsComplete()); + + BlitFramebufferToFramebuffer(srcWrapper.FB(), destFB, + srcSize, destSize, + internalFBs); + return; + } + + DrawBlitTextureToFramebuffer(srcTex, destFB, srcSize, destSize, srcTarget, + internalFBs); +} + + +void +GLBlitHelper::DrawBlitTextureToFramebuffer(GLuint srcTex, GLuint destFB, + const gfx::IntSize& srcSize, + const gfx::IntSize& destSize, + GLenum srcTarget, + bool internalFBs) +{ + BlitType type; + switch (srcTarget) { + case LOCAL_GL_TEXTURE_2D: + type = BlitTex2D; + break; + case LOCAL_GL_TEXTURE_RECTANGLE_ARB: + type = BlitTexRect; + break; + default: + MOZ_CRASH("GFX: Fatal Error: Bad `srcTarget`."); + break; + } + + ScopedGLDrawState autoStates(mGL); + if (internalFBs) { + mGL->Screen()->BindFB_Internal(destFB); + } else { + mGL->BindFB(destFB); + } + + // Does destructive things to (only!) what we just saved above. + bool good = UseTexQuadProgram(type, srcSize); + if (!good) { + // We're up against the wall, so bail. + MOZ_DIAGNOSTIC_ASSERT(false, + "Error: Failed to prepare to blit texture->framebuffer.\n"); + mGL->fScissor(0, 0, destSize.width, destSize.height); + mGL->fColorMask(1, 1, 1, 1); + mGL->fClear(LOCAL_GL_COLOR_BUFFER_BIT); + return; + } + + mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4); +} + +void +GLBlitHelper::BlitFramebufferToTexture(GLuint srcFB, GLuint destTex, + const gfx::IntSize& srcSize, + const gfx::IntSize& destSize, + GLenum destTarget, + bool internalFBs) +{ + // On the Android 4.3 emulator, IsFramebuffer may return false incorrectly. + MOZ_ASSERT_IF(mGL->Renderer() != GLRenderer::AndroidEmulator, !srcFB || mGL->fIsFramebuffer(srcFB)); + MOZ_ASSERT(mGL->fIsTexture(destTex)); + + if (mGL->IsSupported(GLFeature::framebuffer_blit)) { + ScopedFramebufferForTexture destWrapper(mGL, destTex, destTarget); + + BlitFramebufferToFramebuffer(srcFB, destWrapper.FB(), + srcSize, destSize, + internalFBs); + return; + } + + ScopedBindTexture autoTex(mGL, destTex, destTarget); + + ScopedBindFramebuffer boundFB(mGL); + if (internalFBs) { + mGL->Screen()->BindFB_Internal(srcFB); + } else { + mGL->BindFB(srcFB); + } + + ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false); + mGL->fCopyTexSubImage2D(destTarget, 0, + 0, 0, + 0, 0, + srcSize.width, srcSize.height); +} + +void +GLBlitHelper::BlitTextureToTexture(GLuint srcTex, GLuint destTex, + const gfx::IntSize& srcSize, + const gfx::IntSize& destSize, + GLenum srcTarget, GLenum destTarget) +{ + MOZ_ASSERT(mGL->fIsTexture(srcTex)); + MOZ_ASSERT(mGL->fIsTexture(destTex)); + + // Generally, just use the CopyTexSubImage path + ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget); + + BlitFramebufferToTexture(srcWrapper.FB(), destTex, + srcSize, destSize, destTarget); +} + +} // namespace gl +} // namespace mozilla |