1 /*
2 * Copyright (C) 2016 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 "YUVConverter.h"
18
19 #include <assert.h>
20 #include <stdio.h>
21 #include <string>
22
23 #include "OpenGLESDispatch/DispatchTables.h"
24 #include "host-common/feature_control.h"
25 #include "host-common/opengl/misc.h"
26
27 namespace gfxstream {
28 namespace gl {
29
30 #define FATAL(fmt,...) do { \
31 fprintf(stderr, "%s: FATAL: " fmt "\n", __func__, ##__VA_ARGS__); \
32 assert(false); \
33 } while(0)
34
35 #define YUV_CONVERTER_DEBUG 0
36
37 #if YUV_CONVERTER_DEBUG
38 #define YUV_DEBUG_LOG(fmt, ...) \
39 fprintf(stderr, "yuv-converter: %s %s:%d " fmt "\n", __FILE__, __func__, __LINE__, \
40 ##__VA_ARGS__);
41 #else
42 #define YUV_DEBUG_LOG(fmt, ...)
43 #endif
44
isInterleaved(FrameworkFormat format,bool yuv420888ToNv21)45 bool isInterleaved(FrameworkFormat format, bool yuv420888ToNv21) {
46 switch (format) {
47 case FRAMEWORK_FORMAT_NV12:
48 case FRAMEWORK_FORMAT_P010:
49 return true;
50 case FRAMEWORK_FORMAT_YUV_420_888:
51 return yuv420888ToNv21;
52 case FRAMEWORK_FORMAT_YV12:
53 return false;
54 default:
55 FATAL("Invalid for format:%d", format);
56 return false;
57 }
58 }
59
60 enum class YUVInterleaveDirection {
61 VU = 0,
62 UV = 1,
63 };
64
getInterleaveDirection(FrameworkFormat format,bool yuv420888ToNv21)65 YUVInterleaveDirection getInterleaveDirection(FrameworkFormat format, bool yuv420888ToNv21) {
66 if (!isInterleaved(format, yuv420888ToNv21)) {
67 FATAL("Format:%d not interleaved", format);
68 }
69
70 switch (format) {
71 case FRAMEWORK_FORMAT_NV12:
72 case FRAMEWORK_FORMAT_P010:
73 return YUVInterleaveDirection::UV;
74 case FRAMEWORK_FORMAT_YUV_420_888:
75 if (yuv420888ToNv21) {
76 return YUVInterleaveDirection::VU;
77 }
78 FATAL("Format:%d not interleaved", format);
79 return YUVInterleaveDirection::UV;
80 case FRAMEWORK_FORMAT_YV12:
81 default:
82 FATAL("Format:%d not interleaved", format);
83 return YUVInterleaveDirection::UV;
84 }
85 }
86
getGlTextureFormat(FrameworkFormat format,bool yuv420888ToNv21,YUVPlane plane)87 GLint getGlTextureFormat(FrameworkFormat format, bool yuv420888ToNv21, YUVPlane plane) {
88 switch (format) {
89 case FRAMEWORK_FORMAT_YV12:
90 switch (plane) {
91 case YUVPlane::Y:
92 case YUVPlane::U:
93 case YUVPlane::V:
94 return GL_R8;
95 case YUVPlane::UV:
96 FATAL("Invalid plane for format:%d", format);
97 return 0;
98 }
99 case FRAMEWORK_FORMAT_YUV_420_888:
100 if (yuv420888ToNv21) {
101 switch (plane) {
102 case YUVPlane::Y:
103 return GL_R8;
104 case YUVPlane::UV:
105 return GL_RG8;
106 case YUVPlane::U:
107 case YUVPlane::V:
108 FATAL("Invalid plane for format:%d", format);
109 return 0;
110 }
111 } else {
112 switch (plane) {
113 case YUVPlane::Y:
114 case YUVPlane::U:
115 case YUVPlane::V:
116 return GL_R8;
117 case YUVPlane::UV:
118 FATAL("Invalid plane for format:%d", format);
119 return 0;
120 }
121 }
122 case FRAMEWORK_FORMAT_NV12:
123 switch (plane) {
124 case YUVPlane::Y:
125 return GL_R8;
126 case YUVPlane::UV:
127 return GL_RG8;
128 case YUVPlane::U:
129 case YUVPlane::V:
130 FATAL("Invalid plane for format:%d", format);
131 return 0;
132 }
133 case FRAMEWORK_FORMAT_P010:
134 switch (plane) {
135 case YUVPlane::Y:
136 return GL_R16UI;
137 case YUVPlane::UV:
138 return GL_RG16UI;
139 case YUVPlane::U:
140 case YUVPlane::V:
141 FATAL("Invalid plane for format:%d", format);
142 return 0;
143 }
144 default:
145 FATAL("Invalid format:%d", format);
146 return 0;
147 }
148 }
149
getGlPixelFormat(FrameworkFormat format,bool yuv420888ToNv21,YUVPlane plane)150 GLenum getGlPixelFormat(FrameworkFormat format, bool yuv420888ToNv21, YUVPlane plane) {
151 switch (format) {
152 case FRAMEWORK_FORMAT_YV12:
153 switch (plane) {
154 case YUVPlane::Y:
155 case YUVPlane::U:
156 case YUVPlane::V:
157 return GL_RED;
158 case YUVPlane::UV:
159 FATAL("Invalid plane for format:%d", format);
160 return 0;
161 }
162 case FRAMEWORK_FORMAT_YUV_420_888:
163 if (yuv420888ToNv21) {
164 switch (plane) {
165 case YUVPlane::Y:
166 return GL_RED;
167 case YUVPlane::UV:
168 return GL_RG;
169 case YUVPlane::U:
170 case YUVPlane::V:
171 FATAL("Invalid plane for format:%d", format);
172 return 0;
173 }
174 } else {
175 switch (plane) {
176 case YUVPlane::Y:
177 case YUVPlane::U:
178 case YUVPlane::V:
179 return GL_RED;
180 case YUVPlane::UV:
181 FATAL("Invalid plane for format:%d", format);
182 return 0;
183 }
184 }
185 case FRAMEWORK_FORMAT_NV12:
186 switch (plane) {
187 case YUVPlane::Y:
188 return GL_RED;
189 case YUVPlane::UV:
190 return GL_RG;
191 case YUVPlane::U:
192 case YUVPlane::V:
193 FATAL("Invalid plane for format:%d", format);
194 return 0;
195 }
196 case FRAMEWORK_FORMAT_P010:
197 switch (plane) {
198 case YUVPlane::Y:
199 return GL_RED_INTEGER;
200 case YUVPlane::UV:
201 return GL_RG_INTEGER;
202 case YUVPlane::U:
203 case YUVPlane::V:
204 FATAL("Invalid plane for format:%d", format);
205 return 0;
206 }
207 default:
208 FATAL("Invalid format:%d", format);
209 return 0;
210 }
211 }
212
getGlPixelType(FrameworkFormat format,bool yuv420888ToNv21,YUVPlane plane)213 GLsizei getGlPixelType(FrameworkFormat format, bool yuv420888ToNv21, YUVPlane plane) {
214 switch (format) {
215 case FRAMEWORK_FORMAT_YV12:
216 switch (plane) {
217 case YUVPlane::Y:
218 case YUVPlane::U:
219 case YUVPlane::V:
220 return GL_UNSIGNED_BYTE;
221 case YUVPlane::UV:
222 FATAL("Invalid plane for format:%d", format);
223 return 0;
224 }
225 case FRAMEWORK_FORMAT_YUV_420_888:
226 if (yuv420888ToNv21) {
227 switch (plane) {
228 case YUVPlane::Y:
229 case YUVPlane::UV:
230 return GL_UNSIGNED_BYTE;
231 case YUVPlane::U:
232 case YUVPlane::V:
233 FATAL("Invalid plane for format:%d", format);
234 return 0;
235 }
236 } else {
237 switch (plane) {
238 case YUVPlane::Y:
239 case YUVPlane::U:
240 case YUVPlane::V:
241 return GL_UNSIGNED_BYTE;
242 case YUVPlane::UV:
243 FATAL("Invalid plane for format:%d", format);
244 return 0;
245 }
246 }
247 case FRAMEWORK_FORMAT_NV12:
248 switch (plane) {
249 case YUVPlane::Y:
250 case YUVPlane::UV:
251 return GL_UNSIGNED_BYTE;
252 case YUVPlane::U:
253 case YUVPlane::V:
254 FATAL("Invalid plane for format:%d", format);
255 return 0;
256 }
257 case FRAMEWORK_FORMAT_P010:
258 switch (plane) {
259 case YUVPlane::Y:
260 case YUVPlane::UV:
261 return GL_UNSIGNED_SHORT;
262 case YUVPlane::U:
263 case YUVPlane::V:
264 FATAL("Invalid plane for format:%d", format);
265 return 0;
266 }
267 default:
268 FATAL("Invalid format:%d", format);
269 return 0;
270 }
271 }
272
273 // NV12 and YUV420 are all packed
NV12ToYUV420PlanarInPlaceConvert(int nWidth,int nHeight,uint8_t * pFrame,uint8_t * pQuad)274 static void NV12ToYUV420PlanarInPlaceConvert(int nWidth,
275 int nHeight,
276 uint8_t* pFrame,
277 uint8_t* pQuad) {
278 std::vector<uint8_t> tmp;
279 if (pQuad == nullptr) {
280 tmp.resize(nWidth * nHeight / 4);
281 pQuad = tmp.data();
282 }
283 int nPitch = nWidth;
284 uint8_t *puv = pFrame + nPitch * nHeight, *pu = puv,
285 *pv = puv + nPitch * nHeight / 4;
286 for (int y = 0; y < nHeight / 2; y++) {
287 for (int x = 0; x < nWidth / 2; x++) {
288 pu[y * nPitch / 2 + x] = puv[y * nPitch + x * 2];
289 pQuad[y * nWidth / 2 + x] = puv[y * nPitch + x * 2 + 1];
290 }
291 }
292 memcpy(pv, pQuad, nWidth * nHeight / 4);
293 }
294
alignToPower2(uint32_t val,uint32_t align)295 inline uint32_t alignToPower2(uint32_t val, uint32_t align) {
296 return (val + (align - 1)) & ~(align - 1);
297 }
298
299 // getYUVOffsets(), given a YUV-formatted buffer that is arranged
300 // according to the spec
301 // https://developer.android.com/reference/android/graphics/ImageFormat.html#YUV
302 // In particular, Android YUV widths are aligned to 16 pixels.
303 // Inputs:
304 // |yv12|: the YUV-formatted buffer
305 // Outputs:
306 // |yOffsetBytes|: offset into |yv12| of the start of the Y component
307 // |uOffsetBytes|: offset into |yv12| of the start of the U component
308 // |vOffsetBytes|: offset into |yv12| of the start of the V component
getYUVOffsets(int width,int height,FrameworkFormat format,bool yuv420888ToNv21,uint32_t * yWidth,uint32_t * yHeight,uint32_t * yOffsetBytes,uint32_t * yStridePixels,uint32_t * yStrideBytes,uint32_t * uWidth,uint32_t * uHeight,uint32_t * uOffsetBytes,uint32_t * uStridePixels,uint32_t * uStrideBytes,uint32_t * vWidth,uint32_t * vHeight,uint32_t * vOffsetBytes,uint32_t * vStridePixels,uint32_t * vStrideBytes)309 static void getYUVOffsets(int width,
310 int height,
311 FrameworkFormat format,
312 bool yuv420888ToNv21,
313 uint32_t* yWidth,
314 uint32_t* yHeight,
315 uint32_t* yOffsetBytes,
316 uint32_t* yStridePixels,
317 uint32_t* yStrideBytes,
318 uint32_t* uWidth,
319 uint32_t* uHeight,
320 uint32_t* uOffsetBytes,
321 uint32_t* uStridePixels,
322 uint32_t* uStrideBytes,
323 uint32_t* vWidth,
324 uint32_t* vHeight,
325 uint32_t* vOffsetBytes,
326 uint32_t* vStridePixels,
327 uint32_t* vStrideBytes) {
328 switch (format) {
329 case FRAMEWORK_FORMAT_YV12: {
330 *yWidth = width;
331 *yHeight = height;
332 *yOffsetBytes = 0;
333 // Luma stride is 32 bytes aligned in minigbm, 16 in goldfish
334 // gralloc.
335 *yStridePixels = alignToPower2(width, emugl::getGrallocImplementation() == MINIGBM
336 ? 32 : 16);
337 *yStrideBytes = *yStridePixels;
338
339 // Chroma stride is 16 bytes aligned.
340 *vWidth = width / 2;
341 *vHeight = height / 2;
342 *vOffsetBytes = (*yStrideBytes) * (*yHeight);
343 *vStridePixels = alignToPower2((*yStridePixels) / 2, 16);
344 *vStrideBytes = (*vStridePixels);
345
346 *uWidth = width / 2;
347 *uHeight = height / 2;
348 *uOffsetBytes = (*vOffsetBytes) + ((*vStrideBytes) * (*vHeight));
349 *uStridePixels = alignToPower2((*yStridePixels) / 2, 16);
350 *uStrideBytes = *uStridePixels;
351 break;
352 }
353 case FRAMEWORK_FORMAT_YUV_420_888: {
354 if (yuv420888ToNv21) {
355 *yWidth = width;
356 *yHeight = height;
357 *yOffsetBytes = 0;
358 *yStridePixels = width;
359 *yStrideBytes = *yStridePixels;
360
361 *vWidth = width / 2;
362 *vHeight = height / 2;
363 *vOffsetBytes = (*yStrideBytes) * (*yHeight);
364 *vStridePixels = (*yStridePixels) / 2;
365 *vStrideBytes = (*vStridePixels);
366
367 *uWidth = width / 2;
368 *uHeight = height / 2;
369 *uOffsetBytes = (*vOffsetBytes) + 1;
370 *uStridePixels = (*yStridePixels) / 2;
371 *uStrideBytes = *uStridePixels;
372 } else {
373 *yWidth = width;
374 *yHeight = height;
375 *yOffsetBytes = 0;
376 *yStridePixels = width;
377 *yStrideBytes = *yStridePixels;
378
379 *uWidth = width / 2;
380 *uHeight = height / 2;
381 *uOffsetBytes = (*yStrideBytes) * (*yHeight);
382 *uStridePixels = (*yStridePixels) / 2;
383 *uStrideBytes = *uStridePixels;
384
385 *vWidth = width / 2;
386 *vHeight = height / 2;
387 *vOffsetBytes = (*uOffsetBytes) + ((*uStrideBytes) * (*uHeight));
388 *vStridePixels = (*yStridePixels) / 2;
389 *vStrideBytes = (*vStridePixels);
390 }
391 break;
392 }
393 case FRAMEWORK_FORMAT_NV12: {
394 *yWidth = width;
395 *yHeight = height;
396 *yOffsetBytes = 0;
397 *yStridePixels = width;
398 *yStrideBytes = *yStridePixels;
399
400 *uWidth = width / 2;
401 *uHeight = height / 2;
402 *uOffsetBytes = (*yStrideBytes) * (*yHeight);
403 *uStridePixels = (*yStridePixels) / 2;
404 *uStrideBytes = *uStridePixels;
405
406 *vWidth = width / 2;
407 *vHeight = height / 2;
408 *vOffsetBytes = (*uOffsetBytes) + 1;
409 *vStridePixels = (*yStridePixels) / 2;
410 *vStrideBytes = (*vStridePixels);
411 break;
412 }
413 case FRAMEWORK_FORMAT_P010: {
414 *yWidth = width;
415 *yHeight = height;
416 *yOffsetBytes = 0;
417 *yStridePixels = width;
418 *yStrideBytes = (*yStridePixels) * /*bytes per pixel=*/2;
419
420 *uWidth = width / 2;
421 *uHeight = height / 2;
422 *uOffsetBytes = (*yStrideBytes) * (*yHeight);
423 *uStridePixels = (*uWidth);
424 *uStrideBytes = *uStridePixels * /*bytes per pixel=*/2;
425
426 *vWidth = width / 2;
427 *vHeight = height / 2;
428 *vOffsetBytes = (*uOffsetBytes) + 2;
429 *vStridePixels = (*vWidth);
430 *vStrideBytes = (*vStridePixels) * /*bytes per pixel=*/2;
431 break;
432 }
433 case FRAMEWORK_FORMAT_GL_COMPATIBLE: {
434 FATAL("Input not a YUV format! (FRAMEWORK_FORMAT_GL_COMPATIBLE)");
435 break;
436 }
437 default: {
438 FATAL("Unknown format: 0x%x", format);
439 break;
440 }
441 }
442 }
443
444 // Allocates an OpenGL texture that is large enough for a single plane of
445 // a YUV buffer of the given format and returns the texture name in the
446 // `outTextureName` argument.
createYUVGLTex(GLenum textureUnit,GLsizei width,GLsizei height,FrameworkFormat format,bool yuv420888ToNv21,YUVPlane plane,GLuint * outTextureName)447 void YUVConverter::createYUVGLTex(GLenum textureUnit,
448 GLsizei width,
449 GLsizei height,
450 FrameworkFormat format,
451 bool yuv420888ToNv21,
452 YUVPlane plane,
453 GLuint* outTextureName) {
454 YUV_DEBUG_LOG("w:%d h:%d format:%d plane:%d", width, height, format, plane);
455
456 s_gles2.glActiveTexture(textureUnit);
457 s_gles2.glGenTextures(1, outTextureName);
458 s_gles2.glBindTexture(GL_TEXTURE_2D, *outTextureName);
459 s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
460 s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
461 GLint unprevAlignment = 0;
462 s_gles2.glGetIntegerv(GL_UNPACK_ALIGNMENT, &unprevAlignment);
463 s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
464 const GLint textureFormat = getGlTextureFormat(format, yuv420888ToNv21, plane);
465 const GLenum pixelFormat = getGlPixelFormat(format, yuv420888ToNv21, plane);
466 const GLenum pixelType = getGlPixelType(format, yuv420888ToNv21, plane);
467 s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, textureFormat, width, height, 0, pixelFormat, pixelType, NULL);
468 s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, unprevAlignment);
469 s_gles2.glActiveTexture(GL_TEXTURE0);
470 }
471
readYUVTex(GLuint tex,FrameworkFormat format,bool yuv420888ToNv21,YUVPlane plane,void * pixels,uint32_t pixelsStride)472 static void readYUVTex(GLuint tex, FrameworkFormat format, bool yuv420888ToNv21,
473 YUVPlane plane, void* pixels, uint32_t pixelsStride) {
474 YUV_DEBUG_LOG("format%d plane:%d pixels:%p", format, plane, pixels);
475
476 GLuint prevTexture = 0;
477 s_gles2.glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&prevTexture);
478 s_gles2.glBindTexture(GL_TEXTURE_2D, tex);
479 GLint prevAlignment = 0;
480 s_gles2.glGetIntegerv(GL_PACK_ALIGNMENT, &prevAlignment);
481 s_gles2.glPixelStorei(GL_PACK_ALIGNMENT, 1);
482 GLint prevStride = 0;
483 s_gles2.glGetIntegerv(GL_PACK_ROW_LENGTH, &prevStride);
484 s_gles2.glPixelStorei(GL_PACK_ROW_LENGTH, pixelsStride);
485
486 const GLenum pixelFormat = getGlPixelFormat(format, yuv420888ToNv21, plane);
487 const GLenum pixelType = getGlPixelType(format, yuv420888ToNv21,plane);
488 if (s_gles2.glGetTexImage) {
489 s_gles2.glGetTexImage(GL_TEXTURE_2D, 0, pixelFormat, pixelType, pixels);
490 } else {
491 YUV_DEBUG_LOG("empty glGetTexImage");
492 }
493
494 s_gles2.glPixelStorei(GL_PACK_ROW_LENGTH, prevStride);
495 s_gles2.glPixelStorei(GL_PACK_ALIGNMENT, prevAlignment);
496 s_gles2.glBindTexture(GL_TEXTURE_2D, prevTexture);
497 }
498
499 // Updates a given YUV buffer's plane texture at the coordinates
500 // (x, y, width, height), with the raw YUV data in |pixels|. We
501 // cannot view the result properly until after conversion; this is
502 // to be used only as input to the conversion shader.
subUpdateYUVGLTex(GLenum texture_unit,GLuint tex,int x,int y,int width,int height,FrameworkFormat format,bool yuv420888ToNv21,YUVPlane plane,const void * pixels)503 static void subUpdateYUVGLTex(GLenum texture_unit,
504 GLuint tex,
505 int x,
506 int y,
507 int width,
508 int height,
509 FrameworkFormat format,
510 bool yuv420888ToNv21,
511 YUVPlane plane,
512 const void* pixels) {
513 YUV_DEBUG_LOG("x:%d y:%d w:%d h:%d format:%d plane:%d", x, y, width, height, format, plane);
514
515 const GLenum pixelFormat = getGlPixelFormat(format, yuv420888ToNv21, plane);
516 const GLenum pixelType = getGlPixelType(format, yuv420888ToNv21, plane);
517
518 s_gles2.glActiveTexture(texture_unit);
519 s_gles2.glBindTexture(GL_TEXTURE_2D, tex);
520 GLint unprevAlignment = 0;
521 s_gles2.glGetIntegerv(GL_UNPACK_ALIGNMENT, &unprevAlignment);
522 s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
523 s_gles2.glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, pixelFormat, pixelType, pixels);
524 s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, unprevAlignment);
525 s_gles2.glActiveTexture(GL_TEXTURE0);
526 }
527
checkAndUpdateColorAspectsChanged(void * metadata)528 bool YUVConverter::checkAndUpdateColorAspectsChanged(void* metadata) {
529 bool needToUpdateConversionShader = false;
530 if (metadata) {
531 uint64_t type = *(uint64_t*)(metadata);
532 uint8_t* pmetadata = (uint8_t*)(metadata);
533 if (type == 1) {
534 uint64_t primaries = *(uint64_t*)(pmetadata + 8);
535 uint64_t range = *(uint64_t*)(pmetadata + 16);
536 uint64_t transfer = *(uint64_t*)(pmetadata + 24);
537 if (primaries != mColorPrimaries || range != mColorRange ||
538 transfer != mColorTransfer) {
539 mColorPrimaries = primaries;
540 mColorRange = range;
541 mColorTransfer = transfer;
542 needToUpdateConversionShader = true;
543 }
544 }
545 }
546
547 return needToUpdateConversionShader;
548 }
549
createYUVGLShader()550 void YUVConverter::createYUVGLShader() {
551 YUV_DEBUG_LOG("format:%d", mFormat);
552
553 // P010 needs uint samplers.
554 if (mFormat == FRAMEWORK_FORMAT_P010 && !mHasGlsl3Support) {
555 return;
556 }
557
558 static const char kVertShader[] = R"(
559 precision highp float;
560 attribute mediump vec4 aPosition;
561 attribute highp vec2 aTexCoord;
562 varying highp vec2 vTexCoord;
563 void main(void) {
564 gl_Position = aPosition;
565 vTexCoord = aTexCoord;
566 }
567 )";
568
569 static const char kFragShaderVersion3[] = R"(#version 300 es)";
570
571 static const char kFragShaderBegin[] = R"(
572 precision highp float;
573
574 varying highp vec2 vTexCoord;
575
576 uniform highp float uYWidthCutoff;
577 uniform highp float uUVWidthCutoff;
578 )";
579 static const char kFragShaderBeginVersion3[] = R"(
580 precision highp float;
581
582 layout (location = 0) out vec4 FragColor;
583 in highp vec2 vTexCoord;
584
585 uniform highp float uYWidthCutoff;
586 uniform highp float uUVWidthCutoff;
587 )";
588
589 static const char kSamplerUniforms[] = R"(
590 uniform sampler2D uSamplerY;
591 uniform sampler2D uSamplerU;
592 uniform sampler2D uSamplerV;
593 )";
594 static const char kSamplerUniformsUint[] = R"(
595 uniform highp usampler2D uSamplerY;
596 uniform highp usampler2D uSamplerU;
597 uniform highp usampler2D uSamplerV;
598 )";
599
600 static const char kFragShaderMainBegin[] = R"(
601 void main(void) {
602 highp vec2 yTexCoords = vTexCoord;
603 highp vec2 uvTexCoords = vTexCoord;
604
605 // For textures with extra padding for alignment (e.g. YV12 pads to 16),
606 // scale the coordinates to only sample from the non-padded area.
607 yTexCoords.x *= uYWidthCutoff;
608 uvTexCoords.x *= uUVWidthCutoff;
609
610 highp vec3 yuv;
611 )";
612
613 static const char kSampleY[] = R"(
614 yuv[0] = texture2D(uSamplerY, yTexCoords).r;
615 )";
616 static const char kSampleUV[] = R"(
617 yuv[1] = texture2D(uSamplerU, uvTexCoords).r;
618 yuv[2] = texture2D(uSamplerV, uvTexCoords).r;
619 )";
620 static const char kSampleInterleavedUV[] = R"(
621 // Note: uSamplerU and vSamplerV refer to the same texture.
622 yuv[1] = texture2D(uSamplerU, uvTexCoords).r;
623 yuv[2] = texture2D(uSamplerV, uvTexCoords).g;
624 )";
625 static const char kSampleInterleavedVU[] = R"(
626 // Note: uSamplerU and vSamplerV refer to the same texture.
627 yuv[1] = texture2D(uSamplerU, uvTexCoords).g;
628 yuv[2] = texture2D(uSamplerV, uvTexCoords).r;
629 )";
630
631 static const char kSampleP010[] = R"(
632 uint yRaw = texture(uSamplerY, yTexCoords).r;
633 uint uRaw = texture(uSamplerU, uvTexCoords).r;
634 uint vRaw = texture(uSamplerV, uvTexCoords).g;
635
636 // P010 values are stored in the upper 10-bits of 16-bit unsigned shorts.
637 yuv[0] = float(yRaw >> 6) / 1023.0;
638 yuv[1] = float(uRaw >> 6) / 1023.0;
639 yuv[2] = float(vRaw >> 6) / 1023.0;
640 )";
641
642 // default
643 // limited range (2) 601 (4) sRGB transfer (3)
644 static const char kFragShaderMain_2_4_3[] = R"(
645 yuv[0] = yuv[0] - 0.0625;
646 yuv[1] = (yuv[1] - 0.5);
647 yuv[2] = (yuv[2] - 0.5);
648
649 highp float yscale = 1.1643835616438356;
650 highp vec3 rgb = mat3( yscale, yscale, yscale,
651 0, -0.39176229009491365, 2.017232142857143,
652 1.5960267857142856, -0.8129676472377708, 0) * yuv;
653
654 )";
655
656 // full range (1) 601 (4) sRGB transfer (3)
657 static const char kFragShaderMain_1_4_3[] = R"(
658 yuv[0] = yuv[0];
659 yuv[1] = (yuv[1] - 0.5);
660 yuv[2] = (yuv[2] - 0.5);
661
662 highp float yscale = 1.0;
663 highp vec3 rgb = mat3( yscale, yscale, yscale,
664 0, -0.344136* yscale, 1.772* yscale,
665 yscale*1.402, -0.714136* yscale, 0) * yuv;
666
667 )";
668
669 // limited range (2) 709 (1) sRGB transfer (3)
670 static const char kFragShaderMain_2_1_3[] = R"(
671 highp float xscale = 219.0/ 224.0;
672 yuv[0] = yuv[0] - 0.0625;
673 yuv[1] = xscale* (yuv[1] - 0.5);
674 yuv[2] = xscale* (yuv[2] - 0.5);
675
676 highp float yscale = 255.0/219.0;
677 highp vec3 rgb = mat3( yscale, yscale, yscale,
678 0, -0.1873* yscale, 1.8556* yscale,
679 yscale*1.5748, -0.4681* yscale, 0) * yuv;
680
681 )";
682
683 static const char kFragShaderMainEnd[] = R"(
684 gl_FragColor = vec4(rgb, 1.0);
685 }
686 )";
687
688 static const char kFragShaderMainEndVersion3[] = R"(
689 FragColor = vec4(rgb, 1.0);
690 }
691 )";
692 std::string vertShaderSource(kVertShader);
693 std::string fragShaderSource;
694
695 if (mFormat == FRAMEWORK_FORMAT_P010) {
696 fragShaderSource += kFragShaderVersion3;
697 fragShaderSource += kFragShaderBeginVersion3;
698 } else {
699 fragShaderSource += kFragShaderBegin;
700 }
701
702 if (mFormat == FRAMEWORK_FORMAT_P010) {
703 fragShaderSource += kSamplerUniformsUint;
704 } else {
705 fragShaderSource += kSamplerUniforms;
706 }
707
708 fragShaderSource += kFragShaderMainBegin;
709
710 switch (mFormat) {
711 case FRAMEWORK_FORMAT_NV12:
712 case FRAMEWORK_FORMAT_YUV_420_888:
713 case FRAMEWORK_FORMAT_YV12:
714 fragShaderSource += kSampleY;
715 if (isInterleaved(mFormat, mYuv420888ToNv21)) {
716 if (getInterleaveDirection(mFormat, mYuv420888ToNv21) == YUVInterleaveDirection::UV) {
717 fragShaderSource += kSampleInterleavedUV;
718 } else {
719 fragShaderSource += kSampleInterleavedVU;
720 }
721 } else {
722 fragShaderSource += kSampleUV;
723 }
724 break;
725 case FRAMEWORK_FORMAT_P010:
726 fragShaderSource += kSampleP010;
727 break;
728 default:
729 FATAL("%s: invalid format:%d", __FUNCTION__, mFormat);
730 return;
731 }
732
733 if (mColorRange == 1 && mColorPrimaries == 4) {
734 fragShaderSource += kFragShaderMain_1_4_3;
735 } else if (mColorRange == 2 && mColorPrimaries == 1) {
736 fragShaderSource += kFragShaderMain_2_1_3;
737 } else {
738 fragShaderSource += kFragShaderMain_2_4_3;
739 }
740
741 if (mFormat == FRAMEWORK_FORMAT_P010) {
742 fragShaderSource += kFragShaderMainEndVersion3;
743 } else {
744 fragShaderSource += kFragShaderMainEnd;
745 }
746
747 YUV_DEBUG_LOG("format:%d vert-source:%s frag-source:%s", mFormat, vertShaderSource.c_str(), fragShaderSource.c_str());
748
749 const GLchar* const vertShaderSourceChars = vertShaderSource.c_str();
750 const GLchar* const fragShaderSourceChars = fragShaderSource.c_str();
751 const GLint vertShaderSourceLen = vertShaderSource.length();
752 const GLint fragShaderSourceLen = fragShaderSource.length();
753
754 GLuint vertShader = s_gles2.glCreateShader(GL_VERTEX_SHADER);
755 GLuint fragShader = s_gles2.glCreateShader(GL_FRAGMENT_SHADER);
756 s_gles2.glShaderSource(vertShader, 1, &vertShaderSourceChars, &vertShaderSourceLen);
757 s_gles2.glShaderSource(fragShader, 1, &fragShaderSourceChars, &fragShaderSourceLen);
758 s_gles2.glCompileShader(vertShader);
759 s_gles2.glCompileShader(fragShader);
760
761 for (GLuint shader : {vertShader, fragShader}) {
762 GLint status = GL_FALSE;
763 s_gles2.glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
764 if (status == GL_FALSE) {
765 GLchar error[1024];
766 s_gles2.glGetShaderInfoLog(shader, sizeof(error), nullptr, &error[0]);
767 FATAL("Failed to compile YUV conversion shader: %s", error);
768 s_gles2.glDeleteShader(shader);
769 return;
770 }
771 }
772
773 mProgram = s_gles2.glCreateProgram();
774 s_gles2.glAttachShader(mProgram, vertShader);
775 s_gles2.glAttachShader(mProgram, fragShader);
776 s_gles2.glLinkProgram(mProgram);
777
778 GLint status = GL_FALSE;
779 s_gles2.glGetProgramiv(mProgram, GL_LINK_STATUS, &status);
780 if (status == GL_FALSE) {
781 GLchar error[1024];
782 s_gles2.glGetProgramInfoLog(mProgram, sizeof(error), 0, &error[0]);
783 FATAL("Failed to link YUV conversion program: %s", error);
784 s_gles2.glDeleteProgram(mProgram);
785 mProgram = 0;
786 return;
787 }
788
789 mUniformLocYWidthCutoff = s_gles2.glGetUniformLocation(mProgram, "uYWidthCutoff");
790 mUniformLocUVWidthCutoff = s_gles2.glGetUniformLocation(mProgram, "uUVWidthCutoff");
791 mUniformLocSamplerY = s_gles2.glGetUniformLocation(mProgram, "uSamplerY");
792 mUniformLocSamplerU = s_gles2.glGetUniformLocation(mProgram, "uSamplerU");
793 mUniformLocSamplerV = s_gles2.glGetUniformLocation(mProgram, "uSamplerV");
794 mAttributeLocPos = s_gles2.glGetAttribLocation(mProgram, "aPosition");
795 mAttributeLocTexCoord = s_gles2.glGetAttribLocation(mProgram, "aTexCoord");
796
797 s_gles2.glDeleteShader(vertShader);
798 s_gles2.glDeleteShader(fragShader);
799 }
800
createYUVGLFullscreenQuad()801 void YUVConverter::createYUVGLFullscreenQuad() {
802 s_gles2.glGenBuffers(1, &mQuadVertexBuffer);
803 s_gles2.glGenBuffers(1, &mQuadIndexBuffer);
804
805 static const float kVertices[] = {
806 +1, -1, +0, +1, +0,
807 +1, +1, +0, +1, +1,
808 -1, +1, +0, +0, +1,
809 -1, -1, +0, +0, +0,
810 };
811
812 static const GLubyte kIndices[] = { 0, 1, 2, 2, 3, 0 };
813
814 s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mQuadVertexBuffer);
815 s_gles2.glBufferData(GL_ARRAY_BUFFER, sizeof(kVertices), kVertices, GL_STATIC_DRAW);
816 s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mQuadIndexBuffer);
817 s_gles2.glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(kIndices), kIndices, GL_STATIC_DRAW);
818 }
819
doYUVConversionDraw(GLuint program,GLint uniformLocYWidthCutoff,GLint uniformLocUVWidthCutoff,GLint uniformLocYSampler,GLint uniformLocUSampler,GLint uniformLocVSampler,GLint attributeLocTexCoord,GLint attributeLocPos,GLuint quadVertexBuffer,GLuint quadIndexBuffer,float uYWidthCutoff,float uUVWidthCutoff)820 static void doYUVConversionDraw(GLuint program,
821 GLint uniformLocYWidthCutoff,
822 GLint uniformLocUVWidthCutoff,
823 GLint uniformLocYSampler,
824 GLint uniformLocUSampler,
825 GLint uniformLocVSampler,
826 GLint attributeLocTexCoord,
827 GLint attributeLocPos,
828 GLuint quadVertexBuffer,
829 GLuint quadIndexBuffer,
830 float uYWidthCutoff,
831 float uUVWidthCutoff) {
832 const GLsizei kVertexAttribStride = 5 * sizeof(GL_FLOAT);
833 const GLvoid* kVertexAttribPosOffset = (GLvoid*)0;
834 const GLvoid* kVertexAttribCoordOffset = (GLvoid*)(3 * sizeof(GL_FLOAT));
835
836 s_gles2.glUseProgram(program);
837
838 s_gles2.glUniform1f(uniformLocYWidthCutoff, uYWidthCutoff);
839 s_gles2.glUniform1f(uniformLocUVWidthCutoff, uUVWidthCutoff);
840
841 s_gles2.glUniform1i(uniformLocYSampler, 0);
842 s_gles2.glUniform1i(uniformLocUSampler, 1);
843 s_gles2.glUniform1i(uniformLocVSampler, 2);
844
845 s_gles2.glBindBuffer(GL_ARRAY_BUFFER, quadVertexBuffer);
846 s_gles2.glEnableVertexAttribArray(attributeLocPos);
847 s_gles2.glEnableVertexAttribArray(attributeLocTexCoord);
848
849 s_gles2.glVertexAttribPointer(attributeLocPos, 3, GL_FLOAT, false,
850 kVertexAttribStride,
851 kVertexAttribPosOffset);
852 s_gles2.glVertexAttribPointer(attributeLocTexCoord, 2, GL_FLOAT, false,
853 kVertexAttribStride,
854 kVertexAttribCoordOffset);
855
856 s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadIndexBuffer);
857 s_gles2.glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, 0);
858
859 s_gles2.glDisableVertexAttribArray(attributeLocPos);
860 s_gles2.glDisableVertexAttribArray(attributeLocTexCoord);
861 }
862
863 // initialize(): allocate GPU memory for YUV components,
864 // and create shaders and vertex data.
YUVConverter(int width,int height,FrameworkFormat format,bool yuv420888ToNv21)865 YUVConverter::YUVConverter(int width, int height, FrameworkFormat format, bool yuv420888ToNv21)
866 : mWidth(width),
867 mHeight(height),
868 mFormat(format),
869 mColorBufferFormat(format),
870 mYuv420888ToNv21(yuv420888ToNv21) {}
871
init(int width,int height,FrameworkFormat format)872 void YUVConverter::init(int width, int height, FrameworkFormat format) {
873 YUV_DEBUG_LOG("w:%d h:%d format:%d", width, height, format);
874
875 uint32_t yWidth, yHeight = 0, yOffsetBytes, yStridePixels = 0, yStrideBytes;
876 uint32_t uWidth, uHeight = 0, uOffsetBytes, uStridePixels = 0, uStrideBytes;
877 uint32_t vWidth, vHeight = 0, vOffsetBytes, vStridePixels = 0, vStrideBytes;
878 getYUVOffsets(width, height, mFormat, mYuv420888ToNv21,
879 &yWidth, &yHeight, &yOffsetBytes, &yStridePixels, &yStrideBytes,
880 &uWidth, &uHeight, &uOffsetBytes, &uStridePixels, &uStrideBytes,
881 &vWidth, &vHeight, &vOffsetBytes, &vStridePixels, &vStrideBytes);
882 mWidth = width;
883 mHeight = height;
884 if (!mTextureY) {
885 createYUVGLTex(GL_TEXTURE0, yStridePixels, yHeight, mFormat, mYuv420888ToNv21, YUVPlane::Y, &mTextureY);
886 }
887 if (isInterleaved(mFormat, mYuv420888ToNv21)) {
888 if (!mTextureU) {
889 createYUVGLTex(GL_TEXTURE1, uStridePixels, uHeight, mFormat, mYuv420888ToNv21, YUVPlane::UV, &mTextureU);
890 mTextureV = mTextureU;
891 }
892 } else {
893 if (!mTextureU) {
894 createYUVGLTex(GL_TEXTURE1, uStridePixels, uHeight, mFormat, mYuv420888ToNv21, YUVPlane::U, &mTextureU);
895 }
896 if (!mTextureV) {
897 createYUVGLTex(GL_TEXTURE2, vStridePixels, vHeight, mFormat, mYuv420888ToNv21, YUVPlane::V, &mTextureV);
898 }
899 }
900
901 int glesMajor;
902 int glesMinor;
903 emugl::getGlesVersion(&glesMajor, &glesMinor);
904 mHasGlsl3Support = glesMajor >= 3;
905 YUV_DEBUG_LOG("YUVConverter has GLSL ES 3 support:%s (major:%d minor:%d", (mHasGlsl3Support ? "yes" : "no"), glesMajor, glesMinor);
906
907 createYUVGLShader();
908 createYUVGLFullscreenQuad();
909 }
910
saveGLState()911 void YUVConverter::saveGLState() {
912 s_gles2.glGetFloatv(GL_VIEWPORT, mCurrViewport);
913 s_gles2.glGetIntegerv(GL_ACTIVE_TEXTURE, &mCurrTexUnit);
914 s_gles2.glGetIntegerv(GL_TEXTURE_BINDING_2D, &mCurrTexBind);
915 s_gles2.glGetIntegerv(GL_CURRENT_PROGRAM, &mCurrProgram);
916 s_gles2.glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &mCurrVbo);
917 s_gles2.glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &mCurrIbo);
918 }
919
restoreGLState()920 void YUVConverter::restoreGLState() {
921 s_gles2.glViewport(mCurrViewport[0], mCurrViewport[1],
922 mCurrViewport[2], mCurrViewport[3]);
923 s_gles2.glActiveTexture(mCurrTexUnit);
924 s_gles2.glUseProgram(mCurrProgram);
925 s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mCurrVbo);
926 s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mCurrIbo);
927 }
928
getDataSize()929 uint32_t YUVConverter::getDataSize() {
930 uint32_t align = (mFormat == FRAMEWORK_FORMAT_YV12) ? 16 : 1;
931 uint32_t yStrideBytes = (mWidth + (align - 1)) & ~(align - 1);
932 uint32_t uvStride = (yStrideBytes / 2 + (align - 1)) & ~(align - 1);
933 uint32_t uvHeight = mHeight / 2;
934 uint32_t dataSize = yStrideBytes * mHeight + 2 * (uvHeight * uvStride);
935 YUV_DEBUG_LOG("w:%d h:%d format:%d has data size:%d", mWidth, mHeight, mFormat, dataSize);
936 return dataSize;
937 }
938
readPixels(uint8_t * pixels,uint32_t pixels_size)939 void YUVConverter::readPixels(uint8_t* pixels, uint32_t pixels_size) {
940 YUV_DEBUG_LOG("w:%d h:%d format:%d pixels:%p pixels-size:%d", mWidth, mHeight, mFormat, pixels, pixels_size);
941
942 uint32_t yWidth, yHeight, yOffsetBytes, yStridePixels, yStrideBytes;
943 uint32_t uWidth, uHeight, uOffsetBytes, uStridePixels, uStrideBytes;
944 uint32_t vWidth, vHeight, vOffsetBytes, vStridePixels, vStrideBytes;
945 getYUVOffsets(mWidth, mHeight, mFormat, mYuv420888ToNv21,
946 &yWidth, &yHeight, &yOffsetBytes, &yStridePixels, &yStrideBytes,
947 &uWidth, &uHeight, &uOffsetBytes, &uStridePixels, &uStrideBytes,
948 &vWidth, &vHeight, &vOffsetBytes, &vStridePixels, &vStrideBytes);
949
950 if (isInterleaved(mFormat, mYuv420888ToNv21)) {
951 readYUVTex(mTextureV, mFormat, mYuv420888ToNv21, YUVPlane::UV, pixels + std::min(uOffsetBytes, vOffsetBytes),
952 uStridePixels);
953 } else {
954 readYUVTex(mTextureU, mFormat, mYuv420888ToNv21, YUVPlane::U, pixels + uOffsetBytes, uStridePixels);
955 readYUVTex(mTextureV, mFormat, mYuv420888ToNv21, YUVPlane::V, pixels + vOffsetBytes, vStridePixels);
956 }
957
958 if (mFormat == FRAMEWORK_FORMAT_NV12 && mColorBufferFormat == FRAMEWORK_FORMAT_YUV_420_888) {
959 NV12ToYUV420PlanarInPlaceConvert(mWidth, mHeight, pixels, pixels);
960 }
961
962 // Read the Y plane last because so that we can use it as a scratch space.
963 readYUVTex(mTextureY, mFormat, mYuv420888ToNv21, YUVPlane::Y, pixels + yOffsetBytes, yStridePixels);
964 }
965
swapTextures(FrameworkFormat format,GLuint * textures,void * metadata)966 void YUVConverter::swapTextures(FrameworkFormat format, GLuint* textures, void* metadata) {
967 if (isInterleaved(format, mYuv420888ToNv21)) {
968 std::swap(textures[0], mTextureY);
969 std::swap(textures[1], mTextureU);
970 mTextureV = mTextureU;
971 } else {
972 std::swap(textures[0], mTextureY);
973 std::swap(textures[1], mTextureU);
974 std::swap(textures[2], mTextureV);
975 }
976
977 mFormat = format;
978
979 const bool needToUpdateConversionShader = checkAndUpdateColorAspectsChanged(metadata);
980 if (needToUpdateConversionShader) {
981 saveGLState();
982 reset();
983 init(mWidth, mHeight, mFormat);
984 }
985
986 mTexturesSwapped = true;
987 }
988
989 // drawConvert: per-frame updates.
990 // Update YUV textures, then draw the fullscreen
991 // quad set up above, which results in a framebuffer
992 // with the RGB colors.
drawConvert(int x,int y,int width,int height,const char * pixels)993 void YUVConverter::drawConvert(int x, int y, int width, int height, const char* pixels) {
994 drawConvertFromFormat(mFormat, x, y, width, height, pixels);
995 }
996
drawConvertFromFormat(FrameworkFormat format,int x,int y,int width,int height,const char * pixels,void * metadata)997 void YUVConverter::drawConvertFromFormat(FrameworkFormat format, int x, int y, int width,
998 int height, const char* pixels, void* metadata) {
999 saveGLState();
1000 const bool needToUpdateConversionShader = checkAndUpdateColorAspectsChanged(metadata);
1001
1002 if (pixels && (width != mWidth || height != mHeight)) {
1003 reset();
1004 }
1005
1006 bool uploadFormatChanged = !mTexturesSwapped && pixels && (format != mFormat);
1007 bool initNeeded = (mProgram == 0) || uploadFormatChanged || needToUpdateConversionShader;
1008
1009 if (initNeeded) {
1010 if (uploadFormatChanged) {
1011 mFormat = format;
1012 // TODO: missing cherry-picks, put it back
1013 // b/264928117
1014 //mCbFormat = format;
1015 reset();
1016 }
1017 init(width, height, mFormat);
1018 }
1019
1020 if (mFormat == FRAMEWORK_FORMAT_P010 && !mHasGlsl3Support) {
1021 // TODO: perhaps fallback to just software conversion.
1022 return;
1023 }
1024
1025 uint32_t yWidth = 0, yHeight = 0, yOffsetBytes, yStridePixels = 0, yStrideBytes;
1026 uint32_t uWidth = 0, uHeight = 0, uOffsetBytes, uStridePixels = 0, uStrideBytes;
1027 uint32_t vWidth = 0, vHeight = 0, vOffsetBytes, vStridePixels = 0, vStrideBytes;
1028 getYUVOffsets(width, height, mFormat, mYuv420888ToNv21,
1029 &yWidth, &yHeight, &yOffsetBytes, &yStridePixels, &yStrideBytes,
1030 &uWidth, &uHeight, &uOffsetBytes, &uStridePixels, &uStrideBytes,
1031 &vWidth, &vHeight, &vOffsetBytes, &vStridePixels, &vStrideBytes);
1032
1033 YUV_DEBUG_LOG("Updating YUV textures for drawConvert() "
1034 "x:%d y:%d width:%d height:%d "
1035 "yWidth:%d yHeight:%d yOffsetBytes:%d yStridePixels:%d yStrideBytes:%d "
1036 "uWidth:%d uHeight:%d uOffsetBytes:%d uStridePixels:%d uStrideBytes:%d "
1037 "vWidth:%d vHeight:%d vOffsetBytes:%d vStridePixels:%d vStrideBytes:%d ",
1038 x, y, width, height,
1039 yWidth, yHeight, yOffsetBytes, yStridePixels, yStrideBytes,
1040 uWidth, uHeight, uOffsetBytes, uStridePixels, uStrideBytes,
1041 vWidth, vHeight, vOffsetBytes, vStridePixels, vStrideBytes);
1042
1043 s_gles2.glViewport(x, y, width, height);
1044
1045 updateCutoffs(static_cast<float>(yWidth),
1046 static_cast<float>(yStridePixels),
1047 static_cast<float>(uWidth),
1048 static_cast<float>(uStridePixels));
1049
1050 if (pixels) {
1051 subUpdateYUVGLTex(GL_TEXTURE0, mTextureY, x, y, yStridePixels, yHeight, mFormat, mYuv420888ToNv21, YUVPlane::Y, pixels + yOffsetBytes);
1052 if (isInterleaved(mFormat, mYuv420888ToNv21)) {
1053 subUpdateYUVGLTex(GL_TEXTURE1, mTextureU, x, y, uStridePixels, uHeight, mFormat, mYuv420888ToNv21, YUVPlane::UV, pixels + std::min(uOffsetBytes, vOffsetBytes));
1054 } else {
1055 subUpdateYUVGLTex(GL_TEXTURE1, mTextureU, x, y, uStridePixels, uHeight, mFormat, mYuv420888ToNv21, YUVPlane::U, pixels + uOffsetBytes);
1056 subUpdateYUVGLTex(GL_TEXTURE2, mTextureV, x, y, vStridePixels, vHeight, mFormat, mYuv420888ToNv21, YUVPlane::V, pixels + vOffsetBytes);
1057 }
1058 } else {
1059 // special case: draw from texture, only support NV12 for now
1060 // as cuvid's native format is NV12.
1061 // TODO: add more formats if there are such needs in the future.
1062 assert(mFormat == FRAMEWORK_FORMAT_NV12);
1063 }
1064
1065 s_gles2.glActiveTexture(GL_TEXTURE0);
1066 s_gles2.glBindTexture(GL_TEXTURE_2D, mTextureY);
1067 s_gles2.glActiveTexture(GL_TEXTURE1);
1068 s_gles2.glBindTexture(GL_TEXTURE_2D, mTextureU);
1069 s_gles2.glActiveTexture(GL_TEXTURE2);
1070 s_gles2.glBindTexture(GL_TEXTURE_2D, mTextureV);
1071
1072 doYUVConversionDraw(mProgram,
1073 mUniformLocYWidthCutoff,
1074 mUniformLocUVWidthCutoff,
1075 mUniformLocSamplerY,
1076 mUniformLocSamplerU,
1077 mUniformLocSamplerV,
1078 mAttributeLocTexCoord,
1079 mAttributeLocPos,
1080 mQuadVertexBuffer,
1081 mQuadIndexBuffer,
1082 mYWidthCutoff,
1083 mUVWidthCutoff);
1084
1085 restoreGLState();
1086 }
1087
updateCutoffs(float yWidth,float yStridePixels,float uvWidth,float uvStridePixels)1088 void YUVConverter::updateCutoffs(float yWidth, float yStridePixels,
1089 float uvWidth, float uvStridePixels) {
1090 switch (mFormat) {
1091 case FRAMEWORK_FORMAT_YV12:
1092 mYWidthCutoff = yWidth / yStridePixels;
1093 mUVWidthCutoff = uvWidth / uvStridePixels;
1094 break;
1095 case FRAMEWORK_FORMAT_NV12:
1096 case FRAMEWORK_FORMAT_P010:
1097 case FRAMEWORK_FORMAT_YUV_420_888:
1098 mYWidthCutoff = 1.0f;
1099 mUVWidthCutoff = 1.0f;
1100 break;
1101 case FRAMEWORK_FORMAT_GL_COMPATIBLE:
1102 FATAL("Input not a YUV format!");
1103 }
1104 }
1105
reset()1106 void YUVConverter::reset() {
1107 if (mQuadIndexBuffer) s_gles2.glDeleteBuffers(1, &mQuadIndexBuffer);
1108 if (mQuadVertexBuffer) s_gles2.glDeleteBuffers(1, &mQuadVertexBuffer);
1109 if (mProgram) s_gles2.glDeleteProgram(mProgram);
1110 if (mTextureY) s_gles2.glDeleteTextures(1, &mTextureY);
1111 if (isInterleaved(mFormat, mYuv420888ToNv21)) {
1112 if (mTextureU) s_gles2.glDeleteTextures(1, &mTextureU);
1113 } else {
1114 if (mTextureU) s_gles2.glDeleteTextures(1, &mTextureU);
1115 if (mTextureV) s_gles2.glDeleteTextures(1, &mTextureV);
1116 }
1117 mQuadIndexBuffer = 0;
1118 mQuadVertexBuffer = 0;
1119 mProgram = 0;
1120 mTextureY = 0;
1121 mTextureU = 0;
1122 mTextureV = 0;
1123 }
1124
~YUVConverter()1125 YUVConverter::~YUVConverter() {
1126 reset();
1127 }
1128
1129 } // namespace gl
1130 } // namespace gfxstream
1131