blob: 09e107eae1329e64ccdecf17c5915cf7a09bacd2 [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
Romain Guy253f2c22016-09-28 17:34:42 -0700174static void colorTypeToGlFormatAndType(const Caches& caches, SkColorType colorType,
175 bool needSRGB, GLint* outInternalFormat, GLint* outFormat, GLint* outType) {
John Reck38e0c322015-11-10 12:19:17 -0800176 switch (colorType) {
177 case kAlpha_8_SkColorType:
178 *outFormat = GL_ALPHA;
Romain Guy253f2c22016-09-28 17:34:42 -0700179 *outInternalFormat = GL_ALPHA;
John Reck38e0c322015-11-10 12:19:17 -0800180 *outType = GL_UNSIGNED_BYTE;
181 break;
182 case kRGB_565_SkColorType:
Romain Guy253f2c22016-09-28 17:34:42 -0700183 if (needSRGB) {
184 // We would ideally use a GL_RGB/GL_SRGB8 texture but the
185 // intermediate Skia bitmap needs to be ARGB_8888
186 *outFormat = GL_RGBA;
187 *outInternalFormat = caches.rgbaInternalFormat();
188 *outType = GL_UNSIGNED_BYTE;
189 } else {
190 *outFormat = GL_RGB;
191 *outInternalFormat = GL_RGB;
192 *outType = GL_UNSIGNED_SHORT_5_6_5;
193 }
John Reck38e0c322015-11-10 12:19:17 -0800194 break;
195 // ARGB_4444 and Index_8 are both upconverted to RGBA_8888
196 case kARGB_4444_SkColorType:
197 case kIndex_8_SkColorType:
198 case kN32_SkColorType:
199 *outFormat = GL_RGBA;
Romain Guy253f2c22016-09-28 17:34:42 -0700200 *outInternalFormat = caches.rgbaInternalFormat(needSRGB);
John Reck38e0c322015-11-10 12:19:17 -0800201 *outType = GL_UNSIGNED_BYTE;
202 break;
Derek Sollenberger88d842f2016-01-20 10:37:30 -0500203 case kGray_8_SkColorType:
Romain Guy253f2c22016-09-28 17:34:42 -0700204 // TODO: Handle sRGB
Derek Sollenberger88d842f2016-01-20 10:37:30 -0500205 *outFormat = GL_LUMINANCE;
Romain Guy253f2c22016-09-28 17:34:42 -0700206 *outInternalFormat = GL_LUMINANCE;
Derek Sollenberger88d842f2016-01-20 10:37:30 -0500207 *outType = GL_UNSIGNED_BYTE;
208 break;
John Reck38e0c322015-11-10 12:19:17 -0800209 default:
210 LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType);
211 break;
212 }
213}
214
sergeyv98fa4f92016-10-24 15:35:21 -0700215void Texture::upload(Bitmap& bitmap) {
John Reck38e0c322015-11-10 12:19:17 -0800216 if (!bitmap.readyToDraw()) {
217 ALOGE("Cannot generate texture from bitmap");
218 return;
219 }
220
221 ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height());
222
223 // We could also enable mipmapping if both bitmap dimensions are powers
224 // of 2 but we'd have to deal with size changes. Let's keep this simple
225 const bool canMipMap = mCaches.extensions().hasNPot();
226
227 // If the texture had mipmap enabled but not anymore,
228 // force a glTexImage2D to discard the mipmap levels
229 bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap();
John Reck48247a22016-01-22 10:55:32 -0800230 bool setDefaultParams = false;
John Reck38e0c322015-11-10 12:19:17 -0800231
232 if (!mId) {
233 glGenTextures(1, &mId);
234 needsAlloc = true;
John Reck48247a22016-01-22 10:55:32 -0800235 setDefaultParams = true;
John Reck38e0c322015-11-10 12:19:17 -0800236 }
237
Romain Guy253f2c22016-09-28 17:34:42 -0700238 sk_sp<SkColorSpace> sRGB = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
sergeyv98fa4f92016-10-24 15:35:21 -0700239 bool needSRGB = bitmap.info().colorSpace() == sRGB.get();
John Reck38e0c322015-11-10 12:19:17 -0800240
Romain Guy253f2c22016-09-28 17:34:42 -0700241 GLint internalFormat, format, type;
242 colorTypeToGlFormatAndType(mCaches, bitmap.colorType(), needSRGB, &internalFormat, &format, &type);
243
244 if (updateSize(bitmap.width(), bitmap.height(), internalFormat, format)) {
John Reck38e0c322015-11-10 12:19:17 -0800245 needsAlloc = true;
246 }
247
248 blend = !bitmap.isOpaque();
249 mCaches.textureState().bindTexture(mId);
250
Romain Guy253f2c22016-09-28 17:34:42 -0700251 // TODO: Handle sRGB gray bitmaps
252 bool hasSRGB = mCaches.extensions().hasSRGB();
John Reck38e0c322015-11-10 12:19:17 -0800253 if (CC_UNLIKELY(bitmap.colorType() == kARGB_4444_SkColorType
Romain Guy253f2c22016-09-28 17:34:42 -0700254 || bitmap.colorType() == kIndex_8_SkColorType
255 || (bitmap.colorType() == kRGB_565_SkColorType && hasSRGB && needSRGB))) {
256
John Reck38e0c322015-11-10 12:19:17 -0800257 SkBitmap rgbaBitmap;
Romain Guy253f2c22016-09-28 17:34:42 -0700258 rgbaBitmap.allocPixels(SkImageInfo::MakeN32(
sergeyv98fa4f92016-10-24 15:35:21 -0700259 mWidth, mHeight, bitmap.info().alphaType(), hasSRGB ? sRGB : nullptr));
John Reck38e0c322015-11-10 12:19:17 -0800260 rgbaBitmap.eraseColor(0);
261
262 SkCanvas canvas(rgbaBitmap);
sergeyv98fa4f92016-10-24 15:35:21 -0700263 SkBitmap skBitmap;
264 bitmap.getSkBitmap(&skBitmap);
265 canvas.drawBitmap(skBitmap, 0.0f, 0.0f, nullptr);
John Reck38e0c322015-11-10 12:19:17 -0800266
sergeyv98fa4f92016-10-24 15:35:21 -0700267 uploadToTexture(needsAlloc, internalFormat, format, type, rgbaBitmap.rowBytesAsPixels(),
268 rgbaBitmap.bytesPerPixel(), rgbaBitmap.width(),
269 rgbaBitmap.height(), rgbaBitmap.getPixels());
270
John Reck38e0c322015-11-10 12:19:17 -0800271 } else {
sergeyv98fa4f92016-10-24 15:35:21 -0700272 uploadToTexture(needsAlloc, internalFormat, format, type, bitmap.rowBytesAsPixels(),
273 bitmap.info().bytesPerPixel(), bitmap.width(), bitmap.height(), bitmap.pixels());
John Reck38e0c322015-11-10 12:19:17 -0800274 }
275
276 if (canMipMap) {
277 mipMap = bitmap.hasHardwareMipMap();
278 if (mipMap) {
279 glGenerateMipmap(GL_TEXTURE_2D);
280 }
281 }
282
John Reck48247a22016-01-22 10:55:32 -0800283 if (setDefaultParams) {
John Reck38e0c322015-11-10 12:19:17 -0800284 setFilter(GL_NEAREST);
John Reck38e0c322015-11-10 12:19:17 -0800285 setWrap(GL_CLAMP_TO_EDGE);
286 }
287}
288
Romain Guy253f2c22016-09-28 17:34:42 -0700289void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint internalFormat, GLint format) {
John Reck38e0c322015-11-10 12:19:17 -0800290 mId = id;
291 mWidth = width;
292 mHeight = height;
293 mFormat = format;
Romain Guy253f2c22016-09-28 17:34:42 -0700294 mInternalFormat = internalFormat;
John Reck38e0c322015-11-10 12:19:17 -0800295 // We're wrapping an existing texture, so don't double count this memory
296 notifySizeChanged(0);
Romain Guybe1b1272013-06-06 14:02:54 -0700297}
298
Romain Guy8aa195d2013-06-04 18:00:09 -0700299}; // namespace uirenderer
300}; // namespace android