blob: d9bdc6acec347ce72bdde43c7f8974d82ca9cb6c [file] [log] [blame]
Mathias Agopiand606de62010-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"
33#include "TextureManager.h"
34
35namespace android {
36
37// ---------------------------------------------------------------------------
38
Andreas Hubere049a952010-06-25 09:25:19 -070039TextureManager::TextureManager(uint32_t flags)
40 : mFlags(flags)
Mathias Agopiand606de62010-05-10 20:06:11 -070041{
42}
43
Mathias Agopian0a917752010-06-14 21:20:00 -070044GLenum TextureManager::getTextureTarget(const Image* image) {
45#if defined(GL_OES_texture_external)
46 switch (image->target) {
47 case Texture::TEXTURE_EXTERNAL:
48 return GL_TEXTURE_EXTERNAL_OES;
49 }
50#endif
51 return GL_TEXTURE_2D;
52}
53
54status_t TextureManager::initTexture(Texture* texture)
Mathias Agopiand606de62010-05-10 20:06:11 -070055{
Mathias Agopian0a917752010-06-14 21:20:00 -070056 if (texture->name != -1UL)
57 return INVALID_OPERATION;
58
Mathias Agopiand606de62010-05-10 20:06:11 -070059 GLuint textureName = -1;
60 glGenTextures(1, &textureName);
Mathias Agopian0a917752010-06-14 21:20:00 -070061 texture->name = textureName;
62 texture->width = 0;
63 texture->height = 0;
64
65 const GLenum target = GL_TEXTURE_2D;
66 glBindTexture(target, textureName);
67 glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
68 glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
69 glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
70 glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
71
72 return NO_ERROR;
73}
74
75status_t TextureManager::initTexture(Image* pImage, int32_t format)
76{
77 if (pImage->name != -1UL)
78 return INVALID_OPERATION;
79
80 GLuint textureName = -1;
81 glGenTextures(1, &textureName);
82 pImage->name = textureName;
83 pImage->width = 0;
84 pImage->height = 0;
85
86 GLenum target = GL_TEXTURE_2D;
87#if defined(GL_OES_texture_external)
Andreas Hubere049a952010-06-25 09:25:19 -070088 if (format && isSupportedYuvFormat(format)) {
89 target = GL_TEXTURE_EXTERNAL_OES;
90 pImage->target = Texture::TEXTURE_EXTERNAL;
Mathias Agopian0a917752010-06-14 21:20:00 -070091 }
92#endif
93
94 glBindTexture(target, textureName);
95 glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
96 glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
97 glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
98 glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
99
100 return NO_ERROR;
Mathias Agopiand606de62010-05-10 20:06:11 -0700101}
102
103bool TextureManager::isSupportedYuvFormat(int format)
104{
Mathias Agopian0a917752010-06-14 21:20:00 -0700105 return isYuvFormat(format);
106}
107
108bool TextureManager::isYuvFormat(int format)
109{
Mathias Agopiand606de62010-05-10 20:06:11 -0700110 switch (format) {
Mathias Agopian0a917752010-06-14 21:20:00 -0700111 case HAL_PIXEL_FORMAT_NV16:
112 case HAL_PIXEL_FORMAT_NV21:
113 case HAL_PIXEL_FORMAT_IYUV:
114 case HAL_PIXEL_FORMAT_YUV9:
115 case HAL_PIXEL_FORMAT_YUY2:
116 case HAL_PIXEL_FORMAT_UYVY:
117 case HAL_PIXEL_FORMAT_NV12:
118 case HAL_PIXEL_FORMAT_NV61:
Mathias Agopian0a917752010-06-14 21:20:00 -0700119 case HAL_PIXEL_FORMAT_NV12_ADRENO_TILED:
Mathias Agopian0a917752010-06-14 21:20:00 -0700120 return true;
Mathias Agopiand606de62010-05-10 20:06:11 -0700121 }
122 return false;
123}
124
Mathias Agopian0a917752010-06-14 21:20:00 -0700125status_t TextureManager::initEglImage(Image* pImage,
Mathias Agopiand606de62010-05-10 20:06:11 -0700126 EGLDisplay dpy, const sp<GraphicBuffer>& buffer)
127{
128 status_t err = NO_ERROR;
Mathias Agopian0a917752010-06-14 21:20:00 -0700129 if (!pImage->dirty) return err;
Mathias Agopiand606de62010-05-10 20:06:11 -0700130
131 // free the previous image
Mathias Agopian0a917752010-06-14 21:20:00 -0700132 if (pImage->image != EGL_NO_IMAGE_KHR) {
133 eglDestroyImageKHR(dpy, pImage->image);
134 pImage->image = EGL_NO_IMAGE_KHR;
Mathias Agopiand606de62010-05-10 20:06:11 -0700135 }
136
137 // construct an EGL_NATIVE_BUFFER_ANDROID
138 android_native_buffer_t* clientBuf = buffer->getNativeBuffer();
139
140 // create the new EGLImageKHR
141 const EGLint attrs[] = {
142 EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
143 EGL_NONE, EGL_NONE
144 };
Mathias Agopian0a917752010-06-14 21:20:00 -0700145 pImage->image = eglCreateImageKHR(
Mathias Agopiand606de62010-05-10 20:06:11 -0700146 dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
147 (EGLClientBuffer)clientBuf, attrs);
148
Mathias Agopian0a917752010-06-14 21:20:00 -0700149 if (pImage->image != EGL_NO_IMAGE_KHR) {
150 if (pImage->name == -1UL) {
151 initTexture(pImage, buffer->format);
Mathias Agopiand606de62010-05-10 20:06:11 -0700152 }
Mathias Agopian0a917752010-06-14 21:20:00 -0700153 const GLenum target = getTextureTarget(pImage);
154 glBindTexture(target, pImage->name);
155 glEGLImageTargetTexture2DOES(target, (GLeglImageOES)pImage->image);
Mathias Agopiand606de62010-05-10 20:06:11 -0700156 GLint error = glGetError();
157 if (error != GL_NO_ERROR) {
158 LOGE("glEGLImageTargetTexture2DOES(%p) failed err=0x%04x",
Mathias Agopian0a917752010-06-14 21:20:00 -0700159 pImage->image, error);
Mathias Agopiand606de62010-05-10 20:06:11 -0700160 err = INVALID_OPERATION;
161 } else {
162 // Everything went okay!
Mathias Agopian0a917752010-06-14 21:20:00 -0700163 pImage->dirty = false;
164 pImage->width = clientBuf->width;
165 pImage->height = clientBuf->height;
Mathias Agopiand606de62010-05-10 20:06:11 -0700166 }
167 } else {
168 LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError());
169 err = INVALID_OPERATION;
170 }
171 return err;
172}
173
174status_t TextureManager::loadTexture(Texture* texture,
175 const Region& dirty, const GGLSurface& t)
176{
177 if (texture->name == -1UL) {
Mathias Agopian0a917752010-06-14 21:20:00 -0700178 status_t err = initTexture(texture);
179 LOGE_IF(err, "loadTexture failed in initTexture (%s)", strerror(err));
180 return err;
Mathias Agopiand606de62010-05-10 20:06:11 -0700181 }
182
Mathias Agopian0a917752010-06-14 21:20:00 -0700183 if (texture->target != GL_TEXTURE_2D)
184 return INVALID_OPERATION;
185
Mathias Agopiand606de62010-05-10 20:06:11 -0700186 glBindTexture(GL_TEXTURE_2D, texture->name);
187
188 /*
189 * In OpenGL ES we can't specify a stride with glTexImage2D (however,
190 * GL_UNPACK_ALIGNMENT is a limited form of stride).
191 * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we
192 * need to do something reasonable (here creating a bigger texture).
193 *
194 * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT);
195 *
196 * This situation doesn't happen often, but some h/w have a limitation
197 * for their framebuffer (eg: must be multiple of 8 pixels), and
198 * we need to take that into account when using these buffers as
199 * textures.
200 *
201 * This should never be a problem with POT textures
202 */
203
204 int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format));
205 unpack = 1 << ((unpack > 3) ? 3 : unpack);
206 glPixelStorei(GL_UNPACK_ALIGNMENT, unpack);
207
208 /*
209 * round to POT if needed
210 */
Andreas Hubere049a952010-06-25 09:25:19 -0700211 if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) {
Mathias Agopiand606de62010-05-10 20:06:11 -0700212 texture->NPOTAdjust = true;
213 }
214
215 if (texture->NPOTAdjust) {
216 // find the smallest power-of-two that will accommodate our surface
217 texture->potWidth = 1 << (31 - clz(t.width));
218 texture->potHeight = 1 << (31 - clz(t.height));
219 if (texture->potWidth < t.width) texture->potWidth <<= 1;
220 if (texture->potHeight < t.height) texture->potHeight <<= 1;
221 texture->wScale = float(t.width) / texture->potWidth;
222 texture->hScale = float(t.height) / texture->potHeight;
223 } else {
224 texture->potWidth = t.width;
225 texture->potHeight = t.height;
226 }
227
228 Rect bounds(dirty.bounds());
229 GLvoid* data = 0;
230 if (texture->width != t.width || texture->height != t.height) {
231 texture->width = t.width;
232 texture->height = t.height;
233
234 // texture size changed, we need to create a new one
235 bounds.set(Rect(t.width, t.height));
236 if (t.width == texture->potWidth &&
237 t.height == texture->potHeight) {
238 // we can do it one pass
239 data = t.data;
240 }
241
242 if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
243 glTexImage2D(GL_TEXTURE_2D, 0,
244 GL_RGB, texture->potWidth, texture->potHeight, 0,
245 GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
246 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
247 glTexImage2D(GL_TEXTURE_2D, 0,
248 GL_RGBA, texture->potWidth, texture->potHeight, 0,
249 GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
250 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
251 t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
252 glTexImage2D(GL_TEXTURE_2D, 0,
253 GL_RGBA, texture->potWidth, texture->potHeight, 0,
254 GL_RGBA, GL_UNSIGNED_BYTE, data);
Mathias Agopian0a917752010-06-14 21:20:00 -0700255 } else if (isYuvFormat(t.format)) {
Mathias Agopiand606de62010-05-10 20:06:11 -0700256 // just show the Y plane of YUV buffers
257 glTexImage2D(GL_TEXTURE_2D, 0,
258 GL_LUMINANCE, texture->potWidth, texture->potHeight, 0,
259 GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
260 } else {
261 // oops, we don't handle this format!
262 LOGE("texture=%d, using format %d, which is not "
263 "supported by the GL", texture->name, t.format);
264 }
265 }
266 if (!data) {
267 if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
268 glTexSubImage2D(GL_TEXTURE_2D, 0,
269 0, bounds.top, t.width, bounds.height(),
270 GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
271 t.data + bounds.top*t.stride*2);
272 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
273 glTexSubImage2D(GL_TEXTURE_2D, 0,
274 0, bounds.top, t.width, bounds.height(),
275 GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4,
276 t.data + bounds.top*t.stride*2);
277 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
278 t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
279 glTexSubImage2D(GL_TEXTURE_2D, 0,
280 0, bounds.top, t.width, bounds.height(),
281 GL_RGBA, GL_UNSIGNED_BYTE,
282 t.data + bounds.top*t.stride*4);
Mathias Agopian0a917752010-06-14 21:20:00 -0700283 } else if (isYuvFormat(t.format)) {
Mathias Agopiand606de62010-05-10 20:06:11 -0700284 // just show the Y plane of YUV buffers
285 glTexSubImage2D(GL_TEXTURE_2D, 0,
286 0, bounds.top, t.width, bounds.height(),
287 GL_LUMINANCE, GL_UNSIGNED_BYTE,
288 t.data + bounds.top*t.stride);
289 }
290 }
291 return NO_ERROR;
292}
293
Mathias Agopian0a917752010-06-14 21:20:00 -0700294void TextureManager::activateTexture(const Texture& texture, bool filter)
295{
296 const GLenum target = getTextureTarget(&texture);
Andreas Hubere049a952010-06-25 09:25:19 -0700297
298 glBindTexture(target, texture.name);
299 glEnable(target);
300
Mathias Agopian0a917752010-06-14 21:20:00 -0700301#if defined(GL_OES_texture_external)
Andreas Hubere049a952010-06-25 09:25:19 -0700302 if (texture.target == Texture::TEXTURE_2D) {
303 glDisable(GL_TEXTURE_EXTERNAL_OES);
304 } else {
Mathias Agopian0a917752010-06-14 21:20:00 -0700305 glDisable(GL_TEXTURE_2D);
306 }
307#endif
308
309 if (filter) {
310 glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
311 glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
312 } else {
313 glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
314 glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
315 }
316}
317
318void TextureManager::deactivateTextures()
319{
320 glDisable(GL_TEXTURE_2D);
321#if defined(GL_OES_texture_external)
Andreas Hubere049a952010-06-25 09:25:19 -0700322 glDisable(GL_TEXTURE_EXTERNAL_OES);
Mathias Agopian0a917752010-06-14 21:20:00 -0700323#endif
324}
325
Mathias Agopiand606de62010-05-10 20:06:11 -0700326// ---------------------------------------------------------------------------
327
328}; // namespace android