blob: 908f572659064e7cdc1caac35c78341bc246d1e3 [file] [log] [blame]
Romain Guy8aa195d2013-06-04 18:00:09 -07001/*
2 * Copyright (C) 2013 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
Romain Guy8aa195d2013-06-04 18:00:09 -070017#include "Caches.h"
18#include "Texture.h"
John Reck9372ac32016-01-19 11:46:52 -080019#include "utils/GLUtils.h"
John Reck38e0c322015-11-10 12:19:17 -080020#include "utils/TraceUtils.h"
21
22#include <utils/Log.h>
23
24#include <SkCanvas.h>
Romain Guy8aa195d2013-06-04 18:00:09 -070025
26namespace android {
27namespace uirenderer {
28
Romain Guy253f2c22016-09-28 17:34:42 -070029// Number of bytes used by a texture in the given format
John Reck38e0c322015-11-10 12:19:17 -080030static int bytesPerPixel(GLint glFormat) {
31 switch (glFormat) {
John Reck623d2232016-02-12 08:08:29 -080032 // The wrapped-texture case, usually means a SurfaceTexture
33 case 0:
34 return 0;
Romain Guy253f2c22016-09-28 17:34:42 -070035 case GL_LUMINANCE:
John Reck38e0c322015-11-10 12:19:17 -080036 case GL_ALPHA:
37 return 1;
Romain Guy253f2c22016-09-28 17:34:42 -070038 case GL_SRGB8:
John Reck38e0c322015-11-10 12:19:17 -080039 case GL_RGB:
40 return 3;
Romain Guy253f2c22016-09-28 17:34:42 -070041 case GL_SRGB8_ALPHA8:
John Reck38e0c322015-11-10 12:19:17 -080042 case GL_RGBA:
John Reck38e0c322015-11-10 12:19:17 -080043 return 4;
John Reck1d4e6a02016-02-11 13:22:25 -080044 case GL_RGBA16F:
Romain Guy253f2c22016-09-28 17:34:42 -070045 return 8;
John Reck1d4e6a02016-02-11 13:22:25 -080046 default:
47 LOG_ALWAYS_FATAL("UNKNOWN FORMAT %d", glFormat);
John Reck38e0c322015-11-10 12:19:17 -080048 }
49}
50
Romain Guy8aa195d2013-06-04 18:00:09 -070051void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force,
52 GLenum renderTarget) {
53
John Reck48247a22016-01-22 10:55:32 -080054 if (force || wrapS != mWrapS || wrapT != mWrapT) {
Romain Guy8aa195d2013-06-04 18:00:09 -070055 mWrapS = wrapS;
56 mWrapT = wrapT;
57
58 if (bindTexture) {
John Reck38e0c322015-11-10 12:19:17 -080059 mCaches.textureState().bindTexture(renderTarget, mId);
Romain Guy8aa195d2013-06-04 18:00:09 -070060 }
61
62 glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS);
63 glTexParameteri(renderTarget, GL_TEXTURE_WRAP_T, wrapT);
64 }
65}
66
67void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force,
68 GLenum renderTarget) {
69
John Reck48247a22016-01-22 10:55:32 -080070 if (force || min != mMinFilter || mag != mMagFilter) {
Romain Guy8aa195d2013-06-04 18:00:09 -070071 mMinFilter = min;
72 mMagFilter = mag;
73
74 if (bindTexture) {
John Reck38e0c322015-11-10 12:19:17 -080075 mCaches.textureState().bindTexture(renderTarget, mId);
Romain Guy8aa195d2013-06-04 18:00:09 -070076 }
77
78 if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
79
80 glTexParameteri(renderTarget, GL_TEXTURE_MIN_FILTER, min);
81 glTexParameteri(renderTarget, GL_TEXTURE_MAG_FILTER, mag);
82 }
83}
84
John Reck38e0c322015-11-10 12:19:17 -080085void Texture::deleteTexture() {
86 mCaches.textureState().deleteTexture(mId);
87 mId = 0;
88}
89
Romain Guy253f2c22016-09-28 17:34:42 -070090bool Texture::updateSize(uint32_t width, uint32_t height, GLint internalFormat, GLint format) {
91 if (mWidth == width && mHeight == height &&
92 mFormat == format && mInternalFormat == internalFormat) {
John Reck38e0c322015-11-10 12:19:17 -080093 return false;
94 }
95 mWidth = width;
96 mHeight = height;
97 mFormat = format;
Romain Guy253f2c22016-09-28 17:34:42 -070098 mInternalFormat = internalFormat;
99 notifySizeChanged(mWidth * mHeight * bytesPerPixel(internalFormat));
John Reck38e0c322015-11-10 12:19:17 -0800100 return true;
101}
102
John Reck48247a22016-01-22 10:55:32 -0800103void Texture::resetCachedParams() {
104 mWrapS = GL_REPEAT;
105 mWrapT = GL_REPEAT;
106 mMinFilter = GL_NEAREST_MIPMAP_LINEAR;
107 mMagFilter = GL_LINEAR;
108}
109
Romain Guy253f2c22016-09-28 17:34:42 -0700110void Texture::upload(GLint internalFormat, uint32_t width, uint32_t height,
John Reck38e0c322015-11-10 12:19:17 -0800111 GLenum format, GLenum type, const void* pixels) {
John Reck975591a2016-01-22 16:28:07 -0800112 GL_CHECKPOINT(MODERATE);
Romain Guy253f2c22016-09-28 17:34:42 -0700113 bool needsAlloc = updateSize(width, height, internalFormat, format);
John Reck38e0c322015-11-10 12:19:17 -0800114 if (!mId) {
115 glGenTextures(1, &mId);
116 needsAlloc = true;
John Reck48247a22016-01-22 10:55:32 -0800117 resetCachedParams();
John Reck38e0c322015-11-10 12:19:17 -0800118 }
119 mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId);
120 if (needsAlloc) {
Romain Guy253f2c22016-09-28 17:34:42 -0700121 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0,
John Reck38e0c322015-11-10 12:19:17 -0800122 format, type, pixels);
John Reck66f65cb2016-01-21 09:08:42 -0800123 } else if (pixels) {
Romain Guy253f2c22016-09-28 17:34:42 -0700124 glTexSubImage2D(GL_TEXTURE_2D, 0, internalFormat, mWidth, mHeight, 0,
John Reck38e0c322015-11-10 12:19:17 -0800125 format, type, pixels);
126 }
John Reck975591a2016-01-22 16:28:07 -0800127 GL_CHECKPOINT(MODERATE);
John Reck38e0c322015-11-10 12:19:17 -0800128}
129
Romain Guy253f2c22016-09-28 17:34:42 -0700130static void uploadToTexture(bool resize, GLint internalFormat, GLenum format, GLenum type,
131 GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, const GLvoid * data) {
John Reck38e0c322015-11-10 12:19:17 -0800132
John Reck38e0c322015-11-10 12:19:17 -0800133 const bool useStride = stride != width
134 && Caches::getInstance().extensions().hasUnpackRowLength();
135 if ((stride == width) || useStride) {
136 if (useStride) {
137 glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
138 }
139
140 if (resize) {
Romain Guy253f2c22016-09-28 17:34:42 -0700141 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, data);
John Reck38e0c322015-11-10 12:19:17 -0800142 } else {
143 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
144 }
145
146 if (useStride) {
147 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
148 }
149 } else {
150 // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
151 // if the stride doesn't match the width
152
153 GLvoid * temp = (GLvoid *) malloc(width * height * bpp);
154 if (!temp) return;
155
156 uint8_t * pDst = (uint8_t *)temp;
157 uint8_t * pSrc = (uint8_t *)data;
158 for (GLsizei i = 0; i < height; i++) {
159 memcpy(pDst, pSrc, width * bpp);
160 pDst += width * bpp;
161 pSrc += stride * bpp;
162 }
163
164 if (resize) {
Romain Guy253f2c22016-09-28 17:34:42 -0700165 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, temp);
John Reck38e0c322015-11-10 12:19:17 -0800166 } else {
167 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
168 }
169
170 free(temp);
171 }
172}
173
174static void uploadSkBitmapToTexture(const SkBitmap& bitmap,
Romain Guy253f2c22016-09-28 17:34:42 -0700175 bool resize, GLint internalFormat, GLenum format, GLenum type) {
176 uploadToTexture(resize, internalFormat, format, type, bitmap.rowBytesAsPixels(),
177 bitmap.bytesPerPixel(), bitmap.width(), bitmap.height(), bitmap.getPixels());
John Reck38e0c322015-11-10 12:19:17 -0800178}
179
Romain Guy253f2c22016-09-28 17:34:42 -0700180static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
181 bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType) {
John Reck38e0c322015-11-10 12:19:17 -0800182 switch (colorType) {
183 case kAlpha_8_SkColorType:
184 *outFormat = GL_ALPHA;
Romain Guy253f2c22016-09-28 17:34:42 -0700185 *outInternalFormat = GL_ALPHA;
John Reck38e0c322015-11-10 12:19:17 -0800186 *outType = GL_UNSIGNED_BYTE;
187 break;
188 case kRGB_565_SkColorType:
Romain Guy253f2c22016-09-28 17:34:42 -0700189 if (needSRGB) {
190 // We would ideally use a GL_RGB/GL_SRGB8 texture but the
191 // intermediate Skia bitmap needs to be ARGB_8888
192 *outFormat = GL_RGBA;
193 *outInternalFormat = caches.rgbaInternalFormat();
194 *outType = GL_UNSIGNED_BYTE;
195 } else {
196 *outFormat = GL_RGB;
197 *outInternalFormat = GL_RGB;
198 *outType = GL_UNSIGNED_SHORT_5_6_5;
199 }
John Reck38e0c322015-11-10 12:19:17 -0800200 break;
201 // ARGB_4444 and Index_8 are both upconverted to RGBA_8888
202 case kARGB_4444_SkColorType:
203 case kIndex_8_SkColorType:
204 case kN32_SkColorType:
205 *outFormat = GL_RGBA;
Romain Guy253f2c22016-09-28 17:34:42 -0700206 *outInternalFormat = caches.rgbaInternalFormat(needSRGB);
John Reck38e0c322015-11-10 12:19:17 -0800207 *outType = GL_UNSIGNED_BYTE;
208 break;
Derek Sollenberger88d842f2016-01-20 10:37:30 -0500209 case kGray_8_SkColorType:
Romain Guy253f2c22016-09-28 17:34:42 -0700210 // TODO: Handle sRGB
Derek Sollenberger88d842f2016-01-20 10:37:30 -0500211 *outFormat = GL_LUMINANCE;
Romain Guy253f2c22016-09-28 17:34:42 -0700212 *outInternalFormat = GL_LUMINANCE;
Derek Sollenberger88d842f2016-01-20 10:37:30 -0500213 *outType = GL_UNSIGNED_BYTE;
214 break;
John Reck38e0c322015-11-10 12:19:17 -0800215 default:
216 LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType);
217 break;
218 }
219}
220
221void Texture::upload(const SkBitmap& bitmap) {
222 SkAutoLockPixels alp(bitmap);
223
224 if (!bitmap.readyToDraw()) {
225 ALOGE("Cannot generate texture from bitmap");
226 return;
227 }
228
229 ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height());
230
231 // We could also enable mipmapping if both bitmap dimensions are powers
232 // of 2 but we'd have to deal with size changes. Let's keep this simple
233 const bool canMipMap = mCaches.extensions().hasNPot();
234
235 // If the texture had mipmap enabled but not anymore,
236 // force a glTexImage2D to discard the mipmap levels
237 bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap();
John Reck48247a22016-01-22 10:55:32 -0800238 bool setDefaultParams = false;
John Reck38e0c322015-11-10 12:19:17 -0800239
240 if (!mId) {
241 glGenTextures(1, &mId);
242 needsAlloc = true;
John Reck48247a22016-01-22 10:55:32 -0800243 setDefaultParams = true;
John Reck38e0c322015-11-10 12:19:17 -0800244 }
245
Romain Guy253f2c22016-09-28 17:34:42 -0700246 sk_sp<SkColorSpace> sRGB = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
247 bool needSRGB = bitmap.colorSpace() == sRGB.get();
John Reck38e0c322015-11-10 12:19:17 -0800248
Romain Guy253f2c22016-09-28 17:34:42 -0700249 GLint internalFormat, format, type;
250 colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB, &internalFormat, &format, &type);
251
252 if (updateSize(bitmap.width(), bitmap.height(), internalFormat, format)) {
John Reck38e0c322015-11-10 12:19:17 -0800253 needsAlloc = true;
254 }
255
256 blend = !bitmap.isOpaque();
257 mCaches.textureState().bindTexture(mId);
258
Romain Guy253f2c22016-09-28 17:34:42 -0700259 // TODO: Handle sRGB gray bitmaps
260 bool hasSRGB = mCaches.extensions().hasSRGB();
John Reck38e0c322015-11-10 12:19:17 -0800261 if (CC_UNLIKELY(bitmap.colorType() == kARGB_4444_SkColorType
Romain Guy253f2c22016-09-28 17:34:42 -0700262 || bitmap.colorType() == kIndex_8_SkColorType
263 || (bitmap.colorType() == kRGB_565_SkColorType && hasSRGB && needSRGB))) {
264
John Reck38e0c322015-11-10 12:19:17 -0800265 SkBitmap rgbaBitmap;
Romain Guy253f2c22016-09-28 17:34:42 -0700266 rgbaBitmap.allocPixels(SkImageInfo::MakeN32(
267 mWidth, mHeight, bitmap.alphaType(), hasSRGB ? sRGB : nullptr));
John Reck38e0c322015-11-10 12:19:17 -0800268 rgbaBitmap.eraseColor(0);
269
270 SkCanvas canvas(rgbaBitmap);
271 canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr);
272
Romain Guy253f2c22016-09-28 17:34:42 -0700273 uploadSkBitmapToTexture(rgbaBitmap, needsAlloc, internalFormat, format, type);
John Reck38e0c322015-11-10 12:19:17 -0800274 } else {
Romain Guy253f2c22016-09-28 17:34:42 -0700275 uploadSkBitmapToTexture(bitmap, needsAlloc, internalFormat, format, type);
John Reck38e0c322015-11-10 12:19:17 -0800276 }
277
278 if (canMipMap) {
279 mipMap = bitmap.hasHardwareMipMap();
280 if (mipMap) {
281 glGenerateMipmap(GL_TEXTURE_2D);
282 }
283 }
284
John Reck48247a22016-01-22 10:55:32 -0800285 if (setDefaultParams) {
John Reck38e0c322015-11-10 12:19:17 -0800286 setFilter(GL_NEAREST);
John Reck38e0c322015-11-10 12:19:17 -0800287 setWrap(GL_CLAMP_TO_EDGE);
288 }
289}
290
Romain Guy253f2c22016-09-28 17:34:42 -0700291void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format) {
John Reck38e0c322015-11-10 12:19:17 -0800292 mId = id;
293 mWidth = width;
294 mHeight = height;
295 mFormat = format;
Romain Guy253f2c22016-09-28 17:34:42 -0700296 mInternalFormat = internalFormat;
John Reck38e0c322015-11-10 12:19:17 -0800297 // We're wrapping an existing texture, so don't double count this memory
298 notifySizeChanged(0);
Romain Guybe1b1272013-06-06 14:02:54 -0700299}
300
Romain Guy8aa195d2013-06-04 18:00:09 -0700301}; // namespace uirenderer
302}; // namespace android