1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <stdlib.h> 18#include <stdint.h> 19#include <sys/types.h> 20 21#include <utils/Errors.h> 22#include <utils/Log.h> 23 24#include <ui/GraphicBuffer.h> 25 26#include <GLES/gl.h> 27#include <GLES/glext.h> 28 29#include <hardware/hardware.h> 30 31#include "clz.h" 32#include "DisplayHardware/DisplayHardware.h" 33#include "GLExtensions.h" 34#include "TextureManager.h" 35 36namespace android { 37 38// --------------------------------------------------------------------------- 39 40TextureManager::TextureManager() 41 : mGLExtensions(GLExtensions::getInstance()) 42{ 43} 44 45GLenum TextureManager::getTextureTarget(const Image* image) { 46#if defined(GL_OES_EGL_image_external) 47 switch (image->target) { 48 case Texture::TEXTURE_EXTERNAL: 49 return GL_TEXTURE_EXTERNAL_OES; 50 } 51#endif 52 return GL_TEXTURE_2D; 53} 54 55status_t TextureManager::initTexture(Texture* texture) 56{ 57 if (texture->name != -1UL) 58 return INVALID_OPERATION; 59 60 GLuint textureName = -1; 61 glGenTextures(1, &textureName); 62 texture->name = textureName; 63 texture->width = 0; 64 texture->height = 0; 65 66 const GLenum target = GL_TEXTURE_2D; 67 glBindTexture(target, textureName); 68 glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 69 glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 70 glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 71 glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 72 73 return NO_ERROR; 74} 75 76status_t TextureManager::initTexture(Image* pImage, int32_t format) 77{ 78 if (pImage->name != -1UL) 79 return INVALID_OPERATION; 80 81 GLuint textureName = -1; 82 glGenTextures(1, &textureName); 83 pImage->name = textureName; 84 pImage->width = 0; 85 pImage->height = 0; 86 87 GLenum target = GL_TEXTURE_2D; 88#if defined(GL_OES_EGL_image_external) 89 if (GLExtensions::getInstance().haveTextureExternal()) { 90 if (format && isYuvFormat(format)) { 91 target = GL_TEXTURE_EXTERNAL_OES; 92 pImage->target = Texture::TEXTURE_EXTERNAL; 93 } 94 } 95#endif 96 97 glBindTexture(target, textureName); 98 glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 99 glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 100 glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 101 glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 102 103 return NO_ERROR; 104} 105 106bool TextureManager::isSupportedYuvFormat(int format) 107{ 108 switch (format) { 109 case HAL_PIXEL_FORMAT_YV12: 110 return true; 111 } 112 return false; 113} 114 115bool TextureManager::isYuvFormat(int format) 116{ 117 switch (format) { 118 // supported YUV formats 119 case HAL_PIXEL_FORMAT_YV12: 120 // Legacy/deprecated YUV formats 121 case HAL_PIXEL_FORMAT_YCbCr_422_SP: 122 case HAL_PIXEL_FORMAT_YCrCb_420_SP: 123 case HAL_PIXEL_FORMAT_YCbCr_422_I: 124 return true; 125 } 126 127 // Any OEM format needs to be considered 128 if (format>=0x100 && format<=0x1FF) 129 return true; 130 131 return false; 132} 133 134status_t TextureManager::initEglImage(Image* pImage, 135 EGLDisplay dpy, const sp<GraphicBuffer>& buffer) 136{ 137 status_t err = NO_ERROR; 138 if (!pImage->dirty) return err; 139 140 // free the previous image 141 if (pImage->image != EGL_NO_IMAGE_KHR) { 142 eglDestroyImageKHR(dpy, pImage->image); 143 pImage->image = EGL_NO_IMAGE_KHR; 144 } 145 146 // construct an EGL_NATIVE_BUFFER_ANDROID 147 android_native_buffer_t* clientBuf = buffer->getNativeBuffer(); 148 149 // create the new EGLImageKHR 150 const EGLint attrs[] = { 151 EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, 152 EGL_NONE, EGL_NONE 153 }; 154 pImage->image = eglCreateImageKHR( 155 dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, 156 (EGLClientBuffer)clientBuf, attrs); 157 158 if (pImage->image != EGL_NO_IMAGE_KHR) { 159 if (pImage->name == -1UL) { 160 initTexture(pImage, buffer->format); 161 } 162 const GLenum target = getTextureTarget(pImage); 163 glBindTexture(target, pImage->name); 164 glEGLImageTargetTexture2DOES(target, (GLeglImageOES)pImage->image); 165 GLint error = glGetError(); 166 if (error != GL_NO_ERROR) { 167 LOGE("glEGLImageTargetTexture2DOES(%p) failed err=0x%04x", 168 pImage->image, error); 169 err = INVALID_OPERATION; 170 } else { 171 // Everything went okay! 172 pImage->dirty = false; 173 pImage->width = clientBuf->width; 174 pImage->height = clientBuf->height; 175 } 176 } else { 177 LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError()); 178 err = INVALID_OPERATION; 179 } 180 return err; 181} 182 183status_t TextureManager::loadTexture(Texture* texture, 184 const Region& dirty, const GGLSurface& t) 185{ 186 if (texture->name == -1UL) { 187 status_t err = initTexture(texture); 188 LOGE_IF(err, "loadTexture failed in initTexture (%s)", strerror(err)); 189 return err; 190 } 191 192 if (texture->target != Texture::TEXTURE_2D) 193 return INVALID_OPERATION; 194 195 glBindTexture(GL_TEXTURE_2D, texture->name); 196 197 /* 198 * In OpenGL ES we can't specify a stride with glTexImage2D (however, 199 * GL_UNPACK_ALIGNMENT is a limited form of stride). 200 * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we 201 * need to do something reasonable (here creating a bigger texture). 202 * 203 * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT); 204 * 205 * This situation doesn't happen often, but some h/w have a limitation 206 * for their framebuffer (eg: must be multiple of 8 pixels), and 207 * we need to take that into account when using these buffers as 208 * textures. 209 * 210 * This should never be a problem with POT textures 211 */ 212 213 int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format)); 214 unpack = 1 << ((unpack > 3) ? 3 : unpack); 215 glPixelStorei(GL_UNPACK_ALIGNMENT, unpack); 216 217 /* 218 * round to POT if needed 219 */ 220 if (!mGLExtensions.haveNpot()) { 221 texture->NPOTAdjust = true; 222 } 223 224 if (texture->NPOTAdjust) { 225 // find the smallest power-of-two that will accommodate our surface 226 texture->potWidth = 1 << (31 - clz(t.width)); 227 texture->potHeight = 1 << (31 - clz(t.height)); 228 if (texture->potWidth < t.width) texture->potWidth <<= 1; 229 if (texture->potHeight < t.height) texture->potHeight <<= 1; 230 texture->wScale = float(t.width) / texture->potWidth; 231 texture->hScale = float(t.height) / texture->potHeight; 232 } else { 233 texture->potWidth = t.width; 234 texture->potHeight = t.height; 235 } 236 237 Rect bounds(dirty.bounds()); 238 GLvoid* data = 0; 239 if (texture->width != t.width || texture->height != t.height) { 240 texture->width = t.width; 241 texture->height = t.height; 242 243 // texture size changed, we need to create a new one 244 bounds.set(Rect(t.width, t.height)); 245 if (t.width == texture->potWidth && 246 t.height == texture->potHeight) { 247 // we can do it one pass 248 data = t.data; 249 } 250 251 if (t.format == HAL_PIXEL_FORMAT_RGB_565) { 252 glTexImage2D(GL_TEXTURE_2D, 0, 253 GL_RGB, texture->potWidth, texture->potHeight, 0, 254 GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data); 255 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { 256 glTexImage2D(GL_TEXTURE_2D, 0, 257 GL_RGBA, texture->potWidth, texture->potHeight, 0, 258 GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data); 259 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || 260 t.format == HAL_PIXEL_FORMAT_RGBX_8888) { 261 glTexImage2D(GL_TEXTURE_2D, 0, 262 GL_RGBA, texture->potWidth, texture->potHeight, 0, 263 GL_RGBA, GL_UNSIGNED_BYTE, data); 264 } else if (isSupportedYuvFormat(t.format)) { 265 // just show the Y plane of YUV buffers 266 glTexImage2D(GL_TEXTURE_2D, 0, 267 GL_LUMINANCE, texture->potWidth, texture->potHeight, 0, 268 GL_LUMINANCE, GL_UNSIGNED_BYTE, data); 269 } else { 270 // oops, we don't handle this format! 271 LOGE("texture=%d, using format %d, which is not " 272 "supported by the GL", texture->name, t.format); 273 } 274 } 275 if (!data) { 276 if (t.format == HAL_PIXEL_FORMAT_RGB_565) { 277 glTexSubImage2D(GL_TEXTURE_2D, 0, 278 0, bounds.top, t.width, bounds.height(), 279 GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 280 t.data + bounds.top*t.stride*2); 281 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) { 282 glTexSubImage2D(GL_TEXTURE_2D, 0, 283 0, bounds.top, t.width, bounds.height(), 284 GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, 285 t.data + bounds.top*t.stride*2); 286 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 || 287 t.format == HAL_PIXEL_FORMAT_RGBX_8888) { 288 glTexSubImage2D(GL_TEXTURE_2D, 0, 289 0, bounds.top, t.width, bounds.height(), 290 GL_RGBA, GL_UNSIGNED_BYTE, 291 t.data + bounds.top*t.stride*4); 292 } else if (isSupportedYuvFormat(t.format)) { 293 // just show the Y plane of YUV buffers 294 glTexSubImage2D(GL_TEXTURE_2D, 0, 295 0, bounds.top, t.width, bounds.height(), 296 GL_LUMINANCE, GL_UNSIGNED_BYTE, 297 t.data + bounds.top*t.stride); 298 } 299 } 300 return NO_ERROR; 301} 302 303void TextureManager::activateTexture(const Texture& texture, bool filter) 304{ 305 const GLenum target = getTextureTarget(&texture); 306 if (target == GL_TEXTURE_2D) { 307 glBindTexture(GL_TEXTURE_2D, texture.name); 308 glEnable(GL_TEXTURE_2D); 309#if defined(GL_OES_EGL_image_external) 310 if (GLExtensions::getInstance().haveTextureExternal()) { 311 glDisable(GL_TEXTURE_EXTERNAL_OES); 312 } 313 } else { 314 glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture.name); 315 glEnable(GL_TEXTURE_EXTERNAL_OES); 316 glDisable(GL_TEXTURE_2D); 317#endif 318 } 319 320 if (filter) { 321 glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 322 glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 323 } else { 324 glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 325 glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 326 } 327} 328 329void TextureManager::deactivateTextures() 330{ 331 glDisable(GL_TEXTURE_2D); 332#if defined(GL_OES_EGL_image_external) 333 if (GLExtensions::getInstance().haveTextureExternal()) { 334 glDisable(GL_TEXTURE_EXTERNAL_OES); 335 } 336#endif 337} 338 339// --------------------------------------------------------------------------- 340 341}; // namespace android 342