blob: 3b326dfdcf001fb7fb6f0c2a3bac4994a3add68f [file] [log] [blame]
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -07001/*
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"
Mathias Agopian781953d2010-06-25 18:02:21 -070033#include "GLExtensions.h"
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -070034#include "TextureManager.h"
35
36namespace android {
37
38// ---------------------------------------------------------------------------
39
Mathias Agopian781953d2010-06-25 18:02:21 -070040TextureManager::TextureManager()
41 : mGLExtensions(GLExtensions::getInstance())
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -070042{
43}
44
Mathias Agopianf8b4b442010-06-14 21:20:00 -070045GLenum TextureManager::getTextureTarget(const Image* image) {
46#if defined(GL_OES_texture_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)
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -070056{
Mathias Agopianf8b4b442010-06-14 21:20:00 -070057 if (texture->name != -1UL)
58 return INVALID_OPERATION;
59
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -070060 GLuint textureName = -1;
61 glGenTextures(1, &textureName);
Mathias Agopianf8b4b442010-06-14 21:20:00 -070062 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_texture_external)
Mathias Agopian781953d2010-06-25 18:02:21 -070089 if (GLExtensions::getInstance().haveTextureExternal()) {
Mathias Agopian61c55c42010-07-01 21:17:56 -070090 if (format && isYuvFormat(format)) {
Mathias Agopian781953d2010-06-25 18:02:21 -070091 target = GL_TEXTURE_EXTERNAL_OES;
92 pImage->target = Texture::TEXTURE_EXTERNAL;
93 }
Mathias Agopianf8b4b442010-06-14 21:20:00 -070094 }
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;
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -0700104}
105
106bool TextureManager::isSupportedYuvFormat(int format)
107{
Mathias Agopian61c55c42010-07-01 21:17:56 -0700108 switch (format) {
109 case HAL_PIXEL_FORMAT_YV12:
Mathias Agopian61c55c42010-07-01 21:17:56 -0700110 return true;
111 }
112 return false;
Mathias Agopianf8b4b442010-06-14 21:20:00 -0700113}
114
115bool TextureManager::isYuvFormat(int format)
116{
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -0700117 switch (format) {
Mathias Agopian61c55c42010-07-01 21:17:56 -0700118 // supported YUV formats
119 case HAL_PIXEL_FORMAT_YV12:
Mathias Agopian61c55c42010-07-01 21:17:56 -0700120 // 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 case HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED:
Mathias Agopianf8b4b442010-06-14 21:20:00 -0700125 return true;
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -0700126 }
Mathias Agopian61c55c42010-07-01 21:17:56 -0700127
128 // Any OEM format needs to be considered
129 if (format>=0x100 && format<=0x1FF)
130 return true;
131
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -0700132 return false;
133}
134
Mathias Agopianf8b4b442010-06-14 21:20:00 -0700135status_t TextureManager::initEglImage(Image* pImage,
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -0700136 EGLDisplay dpy, const sp<GraphicBuffer>& buffer)
137{
138 status_t err = NO_ERROR;
Mathias Agopianf8b4b442010-06-14 21:20:00 -0700139 if (!pImage->dirty) return err;
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -0700140
141 // free the previous image
Mathias Agopianf8b4b442010-06-14 21:20:00 -0700142 if (pImage->image != EGL_NO_IMAGE_KHR) {
143 eglDestroyImageKHR(dpy, pImage->image);
144 pImage->image = EGL_NO_IMAGE_KHR;
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -0700145 }
146
147 // construct an EGL_NATIVE_BUFFER_ANDROID
148 android_native_buffer_t* clientBuf = buffer->getNativeBuffer();
149
150 // create the new EGLImageKHR
151 const EGLint attrs[] = {
152 EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
153 EGL_NONE, EGL_NONE
154 };
Mathias Agopianf8b4b442010-06-14 21:20:00 -0700155 pImage->image = eglCreateImageKHR(
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -0700156 dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
157 (EGLClientBuffer)clientBuf, attrs);
158
Mathias Agopianf8b4b442010-06-14 21:20:00 -0700159 if (pImage->image != EGL_NO_IMAGE_KHR) {
160 if (pImage->name == -1UL) {
161 initTexture(pImage, buffer->format);
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -0700162 }
Mathias Agopianf8b4b442010-06-14 21:20:00 -0700163 const GLenum target = getTextureTarget(pImage);
164 glBindTexture(target, pImage->name);
165 glEGLImageTargetTexture2DOES(target, (GLeglImageOES)pImage->image);
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -0700166 GLint error = glGetError();
167 if (error != GL_NO_ERROR) {
168 LOGE("glEGLImageTargetTexture2DOES(%p) failed err=0x%04x",
Mathias Agopianf8b4b442010-06-14 21:20:00 -0700169 pImage->image, error);
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -0700170 err = INVALID_OPERATION;
171 } else {
172 // Everything went okay!
Mathias Agopianf8b4b442010-06-14 21:20:00 -0700173 pImage->dirty = false;
174 pImage->width = clientBuf->width;
175 pImage->height = clientBuf->height;
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -0700176 }
177 } else {
178 LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError());
179 err = INVALID_OPERATION;
180 }
181 return err;
182}
183
184status_t TextureManager::loadTexture(Texture* texture,
185 const Region& dirty, const GGLSurface& t)
186{
187 if (texture->name == -1UL) {
Mathias Agopianf8b4b442010-06-14 21:20:00 -0700188 status_t err = initTexture(texture);
189 LOGE_IF(err, "loadTexture failed in initTexture (%s)", strerror(err));
190 return err;
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -0700191 }
192
Mathias Agopianf8b4b442010-06-14 21:20:00 -0700193 if (texture->target != GL_TEXTURE_2D)
194 return INVALID_OPERATION;
195
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -0700196 glBindTexture(GL_TEXTURE_2D, texture->name);
197
198 /*
199 * In OpenGL ES we can't specify a stride with glTexImage2D (however,
200 * GL_UNPACK_ALIGNMENT is a limited form of stride).
201 * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we
202 * need to do something reasonable (here creating a bigger texture).
203 *
204 * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT);
205 *
206 * This situation doesn't happen often, but some h/w have a limitation
207 * for their framebuffer (eg: must be multiple of 8 pixels), and
208 * we need to take that into account when using these buffers as
209 * textures.
210 *
211 * This should never be a problem with POT textures
212 */
213
214 int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format));
215 unpack = 1 << ((unpack > 3) ? 3 : unpack);
216 glPixelStorei(GL_UNPACK_ALIGNMENT, unpack);
217
218 /*
219 * round to POT if needed
220 */
Mathias Agopian781953d2010-06-25 18:02:21 -0700221 if (!mGLExtensions.haveNpot()) {
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -0700222 texture->NPOTAdjust = true;
223 }
224
225 if (texture->NPOTAdjust) {
226 // find the smallest power-of-two that will accommodate our surface
227 texture->potWidth = 1 << (31 - clz(t.width));
228 texture->potHeight = 1 << (31 - clz(t.height));
229 if (texture->potWidth < t.width) texture->potWidth <<= 1;
230 if (texture->potHeight < t.height) texture->potHeight <<= 1;
231 texture->wScale = float(t.width) / texture->potWidth;
232 texture->hScale = float(t.height) / texture->potHeight;
233 } else {
234 texture->potWidth = t.width;
235 texture->potHeight = t.height;
236 }
237
238 Rect bounds(dirty.bounds());
239 GLvoid* data = 0;
240 if (texture->width != t.width || texture->height != t.height) {
241 texture->width = t.width;
242 texture->height = t.height;
243
244 // texture size changed, we need to create a new one
245 bounds.set(Rect(t.width, t.height));
246 if (t.width == texture->potWidth &&
247 t.height == texture->potHeight) {
248 // we can do it one pass
249 data = t.data;
250 }
251
252 if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
253 glTexImage2D(GL_TEXTURE_2D, 0,
254 GL_RGB, texture->potWidth, texture->potHeight, 0,
255 GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
256 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
257 glTexImage2D(GL_TEXTURE_2D, 0,
258 GL_RGBA, texture->potWidth, texture->potHeight, 0,
259 GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
260 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
261 t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
262 glTexImage2D(GL_TEXTURE_2D, 0,
263 GL_RGBA, texture->potWidth, texture->potHeight, 0,
264 GL_RGBA, GL_UNSIGNED_BYTE, data);
Mathias Agopian61c55c42010-07-01 21:17:56 -0700265 } else if (isSupportedYuvFormat(t.format)) {
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -0700266 // just show the Y plane of YUV buffers
267 glTexImage2D(GL_TEXTURE_2D, 0,
268 GL_LUMINANCE, texture->potWidth, texture->potHeight, 0,
269 GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
270 } else {
271 // oops, we don't handle this format!
272 LOGE("texture=%d, using format %d, which is not "
273 "supported by the GL", texture->name, t.format);
274 }
275 }
276 if (!data) {
277 if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
278 glTexSubImage2D(GL_TEXTURE_2D, 0,
279 0, bounds.top, t.width, bounds.height(),
280 GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
281 t.data + bounds.top*t.stride*2);
282 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
283 glTexSubImage2D(GL_TEXTURE_2D, 0,
284 0, bounds.top, t.width, bounds.height(),
285 GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4,
286 t.data + bounds.top*t.stride*2);
287 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
288 t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
289 glTexSubImage2D(GL_TEXTURE_2D, 0,
290 0, bounds.top, t.width, bounds.height(),
291 GL_RGBA, GL_UNSIGNED_BYTE,
292 t.data + bounds.top*t.stride*4);
Mathias Agopian61c55c42010-07-01 21:17:56 -0700293 } else if (isSupportedYuvFormat(t.format)) {
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -0700294 // just show the Y plane of YUV buffers
295 glTexSubImage2D(GL_TEXTURE_2D, 0,
296 0, bounds.top, t.width, bounds.height(),
297 GL_LUMINANCE, GL_UNSIGNED_BYTE,
298 t.data + bounds.top*t.stride);
299 }
300 }
301 return NO_ERROR;
302}
303
Mathias Agopianf8b4b442010-06-14 21:20:00 -0700304void TextureManager::activateTexture(const Texture& texture, bool filter)
305{
306 const GLenum target = getTextureTarget(&texture);
Mathias Agopian8fa4c8112010-06-28 19:54:17 -0700307 if (target == GL_TEXTURE_2D) {
Mathias Agopian781953d2010-06-25 18:02:21 -0700308 glBindTexture(GL_TEXTURE_2D, texture.name);
309 glEnable(GL_TEXTURE_2D);
Mathias Agopianf8b4b442010-06-14 21:20:00 -0700310#if defined(GL_OES_texture_external)
Mathias Agopian781953d2010-06-25 18:02:21 -0700311 if (GLExtensions::getInstance().haveTextureExternal()) {
312 glDisable(GL_TEXTURE_EXTERNAL_OES);
313 }
Mathias Agopian8fa4c8112010-06-28 19:54:17 -0700314 } else {
Mathias Agopian781953d2010-06-25 18:02:21 -0700315 glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture.name);
316 glEnable(GL_TEXTURE_EXTERNAL_OES);
Mathias Agopianf8b4b442010-06-14 21:20:00 -0700317 glDisable(GL_TEXTURE_2D);
Mathias Agopianf8b4b442010-06-14 21:20:00 -0700318#endif
Mathias Agopian8fa4c8112010-06-28 19:54:17 -0700319 }
Mathias Agopianf8b4b442010-06-14 21:20:00 -0700320
321 if (filter) {
322 glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
323 glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
324 } else {
325 glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
326 glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
327 }
328}
329
330void TextureManager::deactivateTextures()
331{
332 glDisable(GL_TEXTURE_2D);
333#if defined(GL_OES_texture_external)
Mathias Agopian781953d2010-06-25 18:02:21 -0700334 if (GLExtensions::getInstance().haveTextureExternal()) {
335 glDisable(GL_TEXTURE_EXTERNAL_OES);
336 }
Mathias Agopianf8b4b442010-06-14 21:20:00 -0700337#endif
338}
339
Mathias Agopian9f2c4fd2010-05-10 20:06:11 -0700340// ---------------------------------------------------------------------------
341
342}; // namespace android