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 }
436 default: {
437 FATAL("Unknown format: 0x%x", format);
438 }
439 }
440 }
441
442 // Allocates an OpenGL texture that is large enough for a single plane of
443 // a YUV buffer of the given format and returns the texture name in the
444 // `outTextureName` argument.
createYUVGLTex(GLenum textureUnit,GLsizei width,GLsizei height,FrameworkFormat format,bool yuv420888ToNv21,YUVPlane plane,GLuint * outTextureName)445 void YUVConverter::createYUVGLTex(GLenum textureUnit,
446 GLsizei width,
447 GLsizei height,
448 FrameworkFormat format,
449 bool yuv420888ToNv21,
450 YUVPlane plane,
451 GLuint* outTextureName) {
452 YUV_DEBUG_LOG("w:%d h:%d format:%d plane:%d", width, height, format, plane);
453
454 s_gles2.glActiveTexture(textureUnit);
455 s_gles2.glGenTextures(1, outTextureName);
456 s_gles2.glBindTexture(GL_TEXTURE_2D, *outTextureName);
457 s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
458 s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
459 GLint unprevAlignment = 0;
460 s_gles2.glGetIntegerv(GL_UNPACK_ALIGNMENT, &unprevAlignment);
461 s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
462 const GLint textureFormat = getGlTextureFormat(format, yuv420888ToNv21, plane);
463 const GLenum pixelFormat = getGlPixelFormat(format, yuv420888ToNv21, plane);
464 const GLenum pixelType = getGlPixelType(format, yuv420888ToNv21, plane);
465 s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, textureFormat, width, height, 0, pixelFormat, pixelType, NULL);
466 s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, unprevAlignment);
467 s_gles2.glActiveTexture(GL_TEXTURE0);
468 }
469
readYUVTex(GLuint tex,FrameworkFormat format,bool yuv420888ToNv21,YUVPlane plane,void * pixels,uint32_t pixelsStride)470 static void readYUVTex(GLuint tex, FrameworkFormat format, bool yuv420888ToNv21,
471 YUVPlane plane, void* pixels, uint32_t pixelsStride) {
472 YUV_DEBUG_LOG("format%d plane:%d pixels:%p", format, plane, pixels);
473
474 GLuint prevTexture = 0;
475 s_gles2.glGetIntegerv(GL_TEXTURE_BINDING_2D, (GLint*)&prevTexture);
476 s_gles2.glBindTexture(GL_TEXTURE_2D, tex);
477 GLint prevAlignment = 0;
478 s_gles2.glGetIntegerv(GL_PACK_ALIGNMENT, &prevAlignment);
479 s_gles2.glPixelStorei(GL_PACK_ALIGNMENT, 1);
480 GLint prevStride = 0;
481 s_gles2.glGetIntegerv(GL_PACK_ROW_LENGTH, &prevStride);
482 s_gles2.glPixelStorei(GL_PACK_ROW_LENGTH, pixelsStride);
483
484 const GLenum pixelFormat = getGlPixelFormat(format, yuv420888ToNv21, plane);
485 const GLenum pixelType = getGlPixelType(format, yuv420888ToNv21,plane);
486 if (s_gles2.glGetTexImage) {
487 s_gles2.glGetTexImage(GL_TEXTURE_2D, 0, pixelFormat, pixelType, pixels);
488 } else {
489 YUV_DEBUG_LOG("empty glGetTexImage");
490 }
491
492 s_gles2.glPixelStorei(GL_PACK_ROW_LENGTH, prevStride);
493 s_gles2.glPixelStorei(GL_PACK_ALIGNMENT, prevAlignment);
494 s_gles2.glBindTexture(GL_TEXTURE_2D, prevTexture);
495 }
496
497 // Updates a given YUV buffer's plane texture at the coordinates
498 // (x, y, width, height), with the raw YUV data in |pixels|. We
499 // cannot view the result properly until after conversion; this is
500 // 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)501 static void subUpdateYUVGLTex(GLenum texture_unit,
502 GLuint tex,
503 int x,
504 int y,
505 int width,
506 int height,
507 FrameworkFormat format,
508 bool yuv420888ToNv21,
509 YUVPlane plane,
510 const void* pixels) {
511 YUV_DEBUG_LOG("x:%d y:%d w:%d h:%d format:%d plane:%d", x, y, width, height, format, plane);
512
513 const GLenum pixelFormat = getGlPixelFormat(format, yuv420888ToNv21, plane);
514 const GLenum pixelType = getGlPixelType(format, yuv420888ToNv21, plane);
515
516 s_gles2.glActiveTexture(texture_unit);
517 s_gles2.glBindTexture(GL_TEXTURE_2D, tex);
518 GLint unprevAlignment = 0;
519 s_gles2.glGetIntegerv(GL_UNPACK_ALIGNMENT, &unprevAlignment);
520 s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
521 s_gles2.glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, pixelFormat, pixelType, pixels);
522 s_gles2.glPixelStorei(GL_UNPACK_ALIGNMENT, unprevAlignment);
523 s_gles2.glActiveTexture(GL_TEXTURE0);
524 }
525
checkAndUpdateColorAspectsChanged(void * metadata)526 bool YUVConverter::checkAndUpdateColorAspectsChanged(void* metadata) {
527 bool needToUpdateConversionShader = false;
528 if (metadata) {
529 uint64_t type = *(uint64_t*)(metadata);
530 uint8_t* pmetadata = (uint8_t*)(metadata);
531 if (type == 1) {
532 uint64_t primaries = *(uint64_t*)(pmetadata + 8);
533 uint64_t range = *(uint64_t*)(pmetadata + 16);
534 uint64_t transfer = *(uint64_t*)(pmetadata + 24);
535 if (primaries != mColorPrimaries || range != mColorRange ||
536 transfer != mColorTransfer) {
537 mColorPrimaries = primaries;
538 mColorRange = range;
539 mColorTransfer = transfer;
540 needToUpdateConversionShader = true;
541 }
542 }
543 }
544
545 return needToUpdateConversionShader;
546 }
547
createYUVGLShader()548 void YUVConverter::createYUVGLShader() {
549 YUV_DEBUG_LOG("format:%d", mFormat);
550
551 // P010 needs uint samplers.
552 if (mFormat == FRAMEWORK_FORMAT_P010 && !mHasGlsl3Support) {
553 return;
554 }
555
556 static const char kVertShader[] = R"(
557 precision highp float;
558 attribute mediump vec4 aPosition;
559 attribute highp vec2 aTexCoord;
560 varying highp vec2 vTexCoord;
561 void main(void) {
562 gl_Position = aPosition;
563 vTexCoord = aTexCoord;
564 }
565 )";
566
567 static const char kFragShaderVersion3[] = R"(#version 300 es)";
568
569 static const char kFragShaderBegin[] = R"(
570 precision highp float;
571
572 varying highp vec2 vTexCoord;
573
574 uniform highp float uYWidthCutoff;
575 uniform highp float uUVWidthCutoff;
576 )";
577 static const char kFragShaderBeginVersion3[] = R"(
578 precision highp float;
579
580 layout (location = 0) out vec4 FragColor;
581 in highp vec2 vTexCoord;
582
583 uniform highp float uYWidthCutoff;
584 uniform highp float uUVWidthCutoff;
585 )";
586
587 static const char kSamplerUniforms[] = R"(
588 uniform sampler2D uSamplerY;
589 uniform sampler2D uSamplerU;
590 uniform sampler2D uSamplerV;
591 )";
592 static const char kSamplerUniformsUint[] = R"(
593 uniform highp usampler2D uSamplerY;
594 uniform highp usampler2D uSamplerU;
595 uniform highp usampler2D uSamplerV;
596 )";
597
598 static const char kFragShaderMainBegin[] = R"(
599 void main(void) {
600 highp vec2 yTexCoords = vTexCoord;
601 highp vec2 uvTexCoords = vTexCoord;
602
603 // For textures with extra padding for alignment (e.g. YV12 pads to 16),
604 // scale the coordinates to only sample from the non-padded area.
605 yTexCoords.x *= uYWidthCutoff;
606 uvTexCoords.x *= uUVWidthCutoff;
607
608 highp vec3 yuv;
609 )";
610
611 static const char kSampleY[] = R"(
612 yuv[0] = texture2D(uSamplerY, yTexCoords).r;
613 )";
614 static const char kSampleUV[] = R"(
615 yuv[1] = texture2D(uSamplerU, uvTexCoords).r;
616 yuv[2] = texture2D(uSamplerV, uvTexCoords).r;
617 )";
618 static const char kSampleInterleavedUV[] = R"(
619 // Note: uSamplerU and vSamplerV refer to the same texture.
620 yuv[1] = texture2D(uSamplerU, uvTexCoords).r;
621 yuv[2] = texture2D(uSamplerV, uvTexCoords).g;
622 )";
623 static const char kSampleInterleavedVU[] = R"(
624 // Note: uSamplerU and vSamplerV refer to the same texture.
625 yuv[1] = texture2D(uSamplerU, uvTexCoords).g;
626 yuv[2] = texture2D(uSamplerV, uvTexCoords).r;
627 )";
628
629 static const char kSampleP010[] = R"(
630 uint yRaw = texture(uSamplerY, yTexCoords).r;
631 uint uRaw = texture(uSamplerU, uvTexCoords).r;
632 uint vRaw = texture(uSamplerV, uvTexCoords).g;
633
634 // P010 values are stored in the upper 10-bits of 16-bit unsigned shorts.
635 yuv[0] = float(yRaw >> 6) / 1023.0;
636 yuv[1] = float(uRaw >> 6) / 1023.0;
637 yuv[2] = float(vRaw >> 6) / 1023.0;
638 )";
639
640 // default
641 // limited range (2) 601 (4) sRGB transfer (3)
642 static const char kFragShaderMain_2_4_3[] = R"(
643 yuv[0] = yuv[0] - 0.0625;
644 yuv[1] = (yuv[1] - 0.5);
645 yuv[2] = (yuv[2] - 0.5);
646
647 highp float yscale = 1.1643835616438356;
648 highp vec3 rgb = mat3( yscale, yscale, yscale,
649 0, -0.39176229009491365, 2.017232142857143,
650 1.5960267857142856, -0.8129676472377708, 0) * yuv;
651
652 )";
653
654 // full range (1) 601 (4) sRGB transfer (3)
655 static const char kFragShaderMain_1_4_3[] = R"(
656 yuv[0] = yuv[0];
657 yuv[1] = (yuv[1] - 0.5);
658 yuv[2] = (yuv[2] - 0.5);
659
660 highp float yscale = 1.0;
661 highp vec3 rgb = mat3( yscale, yscale, yscale,
662 0, -0.344136* yscale, 1.772* yscale,
663 yscale*1.402, -0.714136* yscale, 0) * yuv;
664
665 )";
666
667 // limited range (2) 709 (1) sRGB transfer (3)
668 static const char kFragShaderMain_2_1_3[] = R"(
669 highp float xscale = 219.0/ 224.0;
670 yuv[0] = yuv[0] - 0.0625;
671 yuv[1] = xscale* (yuv[1] - 0.5);
672 yuv[2] = xscale* (yuv[2] - 0.5);
673
674 highp float yscale = 255.0/219.0;
675 highp vec3 rgb = mat3( yscale, yscale, yscale,
676 0, -0.1873* yscale, 1.8556* yscale,
677 yscale*1.5748, -0.4681* yscale, 0) * yuv;
678
679 )";
680
681 static const char kFragShaderMainEnd[] = R"(
682 gl_FragColor = vec4(rgb, 1.0);
683 }
684 )";
685
686 static const char kFragShaderMainEndVersion3[] = R"(
687 FragColor = vec4(rgb, 1.0);
688 }
689 )";
690 std::string vertShaderSource(kVertShader);
691 std::string fragShaderSource;
692
693 if (mFormat == FRAMEWORK_FORMAT_P010) {
694 fragShaderSource += kFragShaderVersion3;
695 fragShaderSource += kFragShaderBeginVersion3;
696 } else {
697 fragShaderSource += kFragShaderBegin;
698 }
699
700 if (mFormat == FRAMEWORK_FORMAT_P010) {
701 fragShaderSource += kSamplerUniformsUint;
702 } else {
703 fragShaderSource += kSamplerUniforms;
704 }
705
706 fragShaderSource += kFragShaderMainBegin;
707
708 switch (mFormat) {
709 case FRAMEWORK_FORMAT_NV12:
710 case FRAMEWORK_FORMAT_YUV_420_888:
711 case FRAMEWORK_FORMAT_YV12:
712 fragShaderSource += kSampleY;
713 if (isInterleaved(mFormat, mYuv420888ToNv21)) {
714 if (getInterleaveDirection(mFormat, mYuv420888ToNv21) == YUVInterleaveDirection::UV) {
715 fragShaderSource += kSampleInterleavedUV;
716 } else {
717 fragShaderSource += kSampleInterleavedVU;
718 }
719 } else {
720 fragShaderSource += kSampleUV;
721 }
722 break;
723 case FRAMEWORK_FORMAT_P010:
724 fragShaderSource += kSampleP010;
725 break;
726 default:
727 FATAL("%s: invalid format:%d", __FUNCTION__, mFormat);
728 return;
729 }
730
731 if (mColorRange == 1 && mColorPrimaries == 4) {
732 fragShaderSource += kFragShaderMain_1_4_3;
733 } else if (mColorRange == 2 && mColorPrimaries == 1) {
734 fragShaderSource += kFragShaderMain_2_1_3;
735 } else {
736 fragShaderSource += kFragShaderMain_2_4_3;
737 }
738
739 if (mFormat == FRAMEWORK_FORMAT_P010) {
740 fragShaderSource += kFragShaderMainEndVersion3;
741 } else {
742 fragShaderSource += kFragShaderMainEnd;
743 }
744
745 YUV_DEBUG_LOG("format:%d vert-source:%s frag-source:%s", mFormat, vertShaderSource.c_str(), fragShaderSource.c_str());
746
747 const GLchar* const vertShaderSourceChars = vertShaderSource.c_str();
748 const GLchar* const fragShaderSourceChars = fragShaderSource.c_str();
749 const GLint vertShaderSourceLen = vertShaderSource.length();
750 const GLint fragShaderSourceLen = fragShaderSource.length();
751
752 GLuint vertShader = s_gles2.glCreateShader(GL_VERTEX_SHADER);
753 GLuint fragShader = s_gles2.glCreateShader(GL_FRAGMENT_SHADER);
754 s_gles2.glShaderSource(vertShader, 1, &vertShaderSourceChars, &vertShaderSourceLen);
755 s_gles2.glShaderSource(fragShader, 1, &fragShaderSourceChars, &fragShaderSourceLen);
756 s_gles2.glCompileShader(vertShader);
757 s_gles2.glCompileShader(fragShader);
758
759 for (GLuint shader : {vertShader, fragShader}) {
760 GLint status = GL_FALSE;
761 s_gles2.glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
762 if (status == GL_FALSE) {
763 GLchar error[1024];
764 s_gles2.glGetShaderInfoLog(shader, sizeof(error), nullptr, &error[0]);
765 FATAL("Failed to compile YUV conversion shader: %s", error);
766 s_gles2.glDeleteShader(shader);
767 return;
768 }
769 }
770
771 mProgram = s_gles2.glCreateProgram();
772 s_gles2.glAttachShader(mProgram, vertShader);
773 s_gles2.glAttachShader(mProgram, fragShader);
774 s_gles2.glLinkProgram(mProgram);
775
776 GLint status = GL_FALSE;
777 s_gles2.glGetProgramiv(mProgram, GL_LINK_STATUS, &status);
778 if (status == GL_FALSE) {
779 GLchar error[1024];
780 s_gles2.glGetProgramInfoLog(mProgram, sizeof(error), 0, &error[0]);
781 FATAL("Failed to link YUV conversion program: %s", error);
782 s_gles2.glDeleteProgram(mProgram);
783 mProgram = 0;
784 return;
785 }
786
787 mUniformLocYWidthCutoff = s_gles2.glGetUniformLocation(mProgram, "uYWidthCutoff");
788 mUniformLocUVWidthCutoff = s_gles2.glGetUniformLocation(mProgram, "uUVWidthCutoff");
789 mUniformLocSamplerY = s_gles2.glGetUniformLocation(mProgram, "uSamplerY");
790 mUniformLocSamplerU = s_gles2.glGetUniformLocation(mProgram, "uSamplerU");
791 mUniformLocSamplerV = s_gles2.glGetUniformLocation(mProgram, "uSamplerV");
792 mAttributeLocPos = s_gles2.glGetAttribLocation(mProgram, "aPosition");
793 mAttributeLocTexCoord = s_gles2.glGetAttribLocation(mProgram, "aTexCoord");
794
795 s_gles2.glDeleteShader(vertShader);
796 s_gles2.glDeleteShader(fragShader);
797 }
798
createYUVGLFullscreenQuad()799 void YUVConverter::createYUVGLFullscreenQuad() {
800 s_gles2.glGenBuffers(1, &mQuadVertexBuffer);
801 s_gles2.glGenBuffers(1, &mQuadIndexBuffer);
802
803 static const float kVertices[] = {
804 +1, -1, +0, +1, +0,
805 +1, +1, +0, +1, +1,
806 -1, +1, +0, +0, +1,
807 -1, -1, +0, +0, +0,
808 };
809
810 static const GLubyte kIndices[] = { 0, 1, 2, 2, 3, 0 };
811
812 s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mQuadVertexBuffer);
813 s_gles2.glBufferData(GL_ARRAY_BUFFER, sizeof(kVertices), kVertices, GL_STATIC_DRAW);
814 s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mQuadIndexBuffer);
815 s_gles2.glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(kIndices), kIndices, GL_STATIC_DRAW);
816 }
817
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)818 static void doYUVConversionDraw(GLuint program,
819 GLint uniformLocYWidthCutoff,
820 GLint uniformLocUVWidthCutoff,
821 GLint uniformLocYSampler,
822 GLint uniformLocUSampler,
823 GLint uniformLocVSampler,
824 GLint attributeLocTexCoord,
825 GLint attributeLocPos,
826 GLuint quadVertexBuffer,
827 GLuint quadIndexBuffer,
828 float uYWidthCutoff,
829 float uUVWidthCutoff) {
830 const GLsizei kVertexAttribStride = 5 * sizeof(GL_FLOAT);
831 const GLvoid* kVertexAttribPosOffset = (GLvoid*)0;
832 const GLvoid* kVertexAttribCoordOffset = (GLvoid*)(3 * sizeof(GL_FLOAT));
833
834 s_gles2.glUseProgram(program);
835
836 s_gles2.glUniform1f(uniformLocYWidthCutoff, uYWidthCutoff);
837 s_gles2.glUniform1f(uniformLocUVWidthCutoff, uUVWidthCutoff);
838
839 s_gles2.glUniform1i(uniformLocYSampler, 0);
840 s_gles2.glUniform1i(uniformLocUSampler, 1);
841 s_gles2.glUniform1i(uniformLocVSampler, 2);
842
843 s_gles2.glBindBuffer(GL_ARRAY_BUFFER, quadVertexBuffer);
844 s_gles2.glEnableVertexAttribArray(attributeLocPos);
845 s_gles2.glEnableVertexAttribArray(attributeLocTexCoord);
846
847 s_gles2.glVertexAttribPointer(attributeLocPos, 3, GL_FLOAT, false,
848 kVertexAttribStride,
849 kVertexAttribPosOffset);
850 s_gles2.glVertexAttribPointer(attributeLocTexCoord, 2, GL_FLOAT, false,
851 kVertexAttribStride,
852 kVertexAttribCoordOffset);
853
854 s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadIndexBuffer);
855 s_gles2.glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, 0);
856
857 s_gles2.glDisableVertexAttribArray(attributeLocPos);
858 s_gles2.glDisableVertexAttribArray(attributeLocTexCoord);
859 }
860
861 // initialize(): allocate GPU memory for YUV components,
862 // and create shaders and vertex data.
YUVConverter(int width,int height,FrameworkFormat format,bool yuv420888ToNv21)863 YUVConverter::YUVConverter(int width, int height, FrameworkFormat format, bool yuv420888ToNv21)
864 : mWidth(width),
865 mHeight(height),
866 mFormat(format),
867 mColorBufferFormat(format),
868 mYuv420888ToNv21(yuv420888ToNv21) {}
869
init(int width,int height,FrameworkFormat format)870 void YUVConverter::init(int width, int height, FrameworkFormat format) {
871 YUV_DEBUG_LOG("w:%d h:%d format:%d", width, height, format);
872
873 uint32_t yWidth, yHeight = 0, yOffsetBytes, yStridePixels = 0, yStrideBytes;
874 uint32_t uWidth, uHeight = 0, uOffsetBytes, uStridePixels = 0, uStrideBytes;
875 uint32_t vWidth, vHeight = 0, vOffsetBytes, vStridePixels = 0, vStrideBytes;
876 getYUVOffsets(width, height, mFormat, mYuv420888ToNv21,
877 &yWidth, &yHeight, &yOffsetBytes, &yStridePixels, &yStrideBytes,
878 &uWidth, &uHeight, &uOffsetBytes, &uStridePixels, &uStrideBytes,
879 &vWidth, &vHeight, &vOffsetBytes, &vStridePixels, &vStrideBytes);
880 mWidth = width;
881 mHeight = height;
882 if (!mTextureY) {
883 createYUVGLTex(GL_TEXTURE0, yStridePixels, yHeight, mFormat, mYuv420888ToNv21, YUVPlane::Y, &mTextureY);
884 }
885 if (isInterleaved(mFormat, mYuv420888ToNv21)) {
886 if (!mTextureU) {
887 createYUVGLTex(GL_TEXTURE1, uStridePixels, uHeight, mFormat, mYuv420888ToNv21, YUVPlane::UV, &mTextureU);
888 mTextureV = mTextureU;
889 }
890 } else {
891 if (!mTextureU) {
892 createYUVGLTex(GL_TEXTURE1, uStridePixels, uHeight, mFormat, mYuv420888ToNv21, YUVPlane::U, &mTextureU);
893 }
894 if (!mTextureV) {
895 createYUVGLTex(GL_TEXTURE2, vStridePixels, vHeight, mFormat, mYuv420888ToNv21, YUVPlane::V, &mTextureV);
896 }
897 }
898
899 int glesMajor;
900 int glesMinor;
901 emugl::getGlesVersion(&glesMajor, &glesMinor);
902 mHasGlsl3Support = glesMajor >= 3;
903 YUV_DEBUG_LOG("YUVConverter has GLSL ES 3 support:%s (major:%d minor:%d", (mHasGlsl3Support ? "yes" : "no"), glesMajor, glesMinor);
904
905 createYUVGLShader();
906 createYUVGLFullscreenQuad();
907 }
908
saveGLState()909 void YUVConverter::saveGLState() {
910 s_gles2.glGetFloatv(GL_VIEWPORT, mCurrViewport);
911 s_gles2.glGetIntegerv(GL_ACTIVE_TEXTURE, &mCurrTexUnit);
912 s_gles2.glGetIntegerv(GL_TEXTURE_BINDING_2D, &mCurrTexBind);
913 s_gles2.glGetIntegerv(GL_CURRENT_PROGRAM, &mCurrProgram);
914 s_gles2.glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &mCurrVbo);
915 s_gles2.glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &mCurrIbo);
916 }
917
restoreGLState()918 void YUVConverter::restoreGLState() {
919 s_gles2.glViewport(mCurrViewport[0], mCurrViewport[1],
920 mCurrViewport[2], mCurrViewport[3]);
921 s_gles2.glActiveTexture(mCurrTexUnit);
922 s_gles2.glUseProgram(mCurrProgram);
923 s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mCurrVbo);
924 s_gles2.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mCurrIbo);
925 }
926
getDataSize()927 uint32_t YUVConverter::getDataSize() {
928 uint32_t align = (mFormat == FRAMEWORK_FORMAT_YV12) ? 16 : 1;
929 uint32_t yStrideBytes = (mWidth + (align - 1)) & ~(align - 1);
930 uint32_t uvStride = (yStrideBytes / 2 + (align - 1)) & ~(align - 1);
931 uint32_t uvHeight = mHeight / 2;
932 uint32_t dataSize = yStrideBytes * mHeight + 2 * (uvHeight * uvStride);
933 YUV_DEBUG_LOG("w:%d h:%d format:%d has data size:%d", mWidth, mHeight, mFormat, dataSize);
934 return dataSize;
935 }
936
readPixels(uint8_t * pixels,uint32_t pixels_size)937 void YUVConverter::readPixels(uint8_t* pixels, uint32_t pixels_size) {
938 YUV_DEBUG_LOG("w:%d h:%d format:%d pixels:%p pixels-size:%d", mWidth, mHeight, mFormat, pixels, pixels_size);
939
940 uint32_t yWidth, yHeight, yOffsetBytes, yStridePixels, yStrideBytes;
941 uint32_t uWidth, uHeight, uOffsetBytes, uStridePixels, uStrideBytes;
942 uint32_t vWidth, vHeight, vOffsetBytes, vStridePixels, vStrideBytes;
943 getYUVOffsets(mWidth, mHeight, mFormat, mYuv420888ToNv21,
944 &yWidth, &yHeight, &yOffsetBytes, &yStridePixels, &yStrideBytes,
945 &uWidth, &uHeight, &uOffsetBytes, &uStridePixels, &uStrideBytes,
946 &vWidth, &vHeight, &vOffsetBytes, &vStridePixels, &vStrideBytes);
947
948 if (isInterleaved(mFormat, mYuv420888ToNv21)) {
949 readYUVTex(mTextureV, mFormat, mYuv420888ToNv21, YUVPlane::UV, pixels + std::min(uOffsetBytes, vOffsetBytes),
950 uStridePixels);
951 } else {
952 readYUVTex(mTextureU, mFormat, mYuv420888ToNv21, YUVPlane::U, pixels + uOffsetBytes, uStridePixels);
953 readYUVTex(mTextureV, mFormat, mYuv420888ToNv21, YUVPlane::V, pixels + vOffsetBytes, vStridePixels);
954 }
955
956 if (mFormat == FRAMEWORK_FORMAT_NV12 && mColorBufferFormat == FRAMEWORK_FORMAT_YUV_420_888) {
957 NV12ToYUV420PlanarInPlaceConvert(mWidth, mHeight, pixels, pixels);
958 }
959
960 // Read the Y plane last because so that we can use it as a scratch space.
961 readYUVTex(mTextureY, mFormat, mYuv420888ToNv21, YUVPlane::Y, pixels + yOffsetBytes, yStridePixels);
962 }
963
swapTextures(FrameworkFormat format,GLuint * textures,void * metadata)964 void YUVConverter::swapTextures(FrameworkFormat format, GLuint* textures, void* metadata) {
965 if (isInterleaved(format, mYuv420888ToNv21)) {
966 std::swap(textures[0], mTextureY);
967 std::swap(textures[1], mTextureU);
968 mTextureV = mTextureU;
969 } else {
970 std::swap(textures[0], mTextureY);
971 std::swap(textures[1], mTextureU);
972 std::swap(textures[2], mTextureV);
973 }
974
975 mFormat = format;
976
977 const bool needToUpdateConversionShader = checkAndUpdateColorAspectsChanged(metadata);
978 if (needToUpdateConversionShader) {
979 saveGLState();
980 reset();
981 init(mWidth, mHeight, mFormat);
982 }
983
984 mTexturesSwapped = true;
985 }
986
987 // drawConvert: per-frame updates.
988 // Update YUV textures, then draw the fullscreen
989 // quad set up above, which results in a framebuffer
990 // with the RGB colors.
drawConvert(int x,int y,int width,int height,const char * pixels)991 void YUVConverter::drawConvert(int x, int y, int width, int height, const char* pixels) {
992 drawConvertFromFormat(mFormat, x, y, width, height, pixels);
993 }
994
drawConvertFromFormat(FrameworkFormat format,int x,int y,int width,int height,const char * pixels,void * metadata)995 void YUVConverter::drawConvertFromFormat(FrameworkFormat format, int x, int y, int width,
996 int height, const char* pixels, void* metadata) {
997 saveGLState();
998 const bool needToUpdateConversionShader = checkAndUpdateColorAspectsChanged(metadata);
999
1000 if (pixels && (width != mWidth || height != mHeight)) {
1001 reset();
1002 }
1003
1004 bool uploadFormatChanged = !mTexturesSwapped && pixels && (format != mFormat);
1005 bool initNeeded = (mProgram == 0) || uploadFormatChanged || needToUpdateConversionShader;
1006
1007 if (initNeeded) {
1008 if (uploadFormatChanged) {
1009 mFormat = format;
1010 // TODO: missing cherry-picks, put it back
1011 // b/264928117
1012 //mCbFormat = format;
1013 reset();
1014 }
1015 init(width, height, mFormat);
1016 }
1017
1018 if (mFormat == FRAMEWORK_FORMAT_P010 && !mHasGlsl3Support) {
1019 // TODO: perhaps fallback to just software conversion.
1020 return;
1021 }
1022
1023 uint32_t yWidth = 0, yHeight = 0, yOffsetBytes, yStridePixels = 0, yStrideBytes;
1024 uint32_t uWidth = 0, uHeight = 0, uOffsetBytes, uStridePixels = 0, uStrideBytes;
1025 uint32_t vWidth = 0, vHeight = 0, vOffsetBytes, vStridePixels = 0, vStrideBytes;
1026 getYUVOffsets(width, height, mFormat, mYuv420888ToNv21,
1027 &yWidth, &yHeight, &yOffsetBytes, &yStridePixels, &yStrideBytes,
1028 &uWidth, &uHeight, &uOffsetBytes, &uStridePixels, &uStrideBytes,
1029 &vWidth, &vHeight, &vOffsetBytes, &vStridePixels, &vStrideBytes);
1030
1031 YUV_DEBUG_LOG("Updating YUV textures for drawConvert() "
1032 "x:%d y:%d width:%d height:%d "
1033 "yWidth:%d yHeight:%d yOffsetBytes:%d yStridePixels:%d yStrideBytes:%d "
1034 "uWidth:%d uHeight:%d uOffsetBytes:%d uStridePixels:%d uStrideBytes:%d "
1035 "vWidth:%d vHeight:%d vOffsetBytes:%d vStridePixels:%d vStrideBytes:%d ",
1036 x, y, width, height,
1037 yWidth, yHeight, yOffsetBytes, yStridePixels, yStrideBytes,
1038 uWidth, uHeight, uOffsetBytes, uStridePixels, uStrideBytes,
1039 vWidth, vHeight, vOffsetBytes, vStridePixels, vStrideBytes);
1040
1041 s_gles2.glViewport(x, y, width, height);
1042
1043 updateCutoffs(static_cast<float>(yWidth),
1044 static_cast<float>(yStridePixels),
1045 static_cast<float>(uWidth),
1046 static_cast<float>(uStridePixels));
1047
1048 if (pixels) {
1049 subUpdateYUVGLTex(GL_TEXTURE0, mTextureY, x, y, yStridePixels, yHeight, mFormat, mYuv420888ToNv21, YUVPlane::Y, pixels + yOffsetBytes);
1050 if (isInterleaved(mFormat, mYuv420888ToNv21)) {
1051 subUpdateYUVGLTex(GL_TEXTURE1, mTextureU, x, y, uStridePixels, uHeight, mFormat, mYuv420888ToNv21, YUVPlane::UV, pixels + std::min(uOffsetBytes, vOffsetBytes));
1052 } else {
1053 subUpdateYUVGLTex(GL_TEXTURE1, mTextureU, x, y, uStridePixels, uHeight, mFormat, mYuv420888ToNv21, YUVPlane::U, pixels + uOffsetBytes);
1054 subUpdateYUVGLTex(GL_TEXTURE2, mTextureV, x, y, vStridePixels, vHeight, mFormat, mYuv420888ToNv21, YUVPlane::V, pixels + vOffsetBytes);
1055 }
1056 } else {
1057 // special case: draw from texture, only support NV12 for now
1058 // as cuvid's native format is NV12.
1059 // TODO: add more formats if there are such needs in the future.
1060 assert(mFormat == FRAMEWORK_FORMAT_NV12);
1061 }
1062
1063 s_gles2.glActiveTexture(GL_TEXTURE0);
1064 s_gles2.glBindTexture(GL_TEXTURE_2D, mTextureY);
1065 s_gles2.glActiveTexture(GL_TEXTURE1);
1066 s_gles2.glBindTexture(GL_TEXTURE_2D, mTextureU);
1067 s_gles2.glActiveTexture(GL_TEXTURE2);
1068 s_gles2.glBindTexture(GL_TEXTURE_2D, mTextureV);
1069
1070 doYUVConversionDraw(mProgram,
1071 mUniformLocYWidthCutoff,
1072 mUniformLocUVWidthCutoff,
1073 mUniformLocSamplerY,
1074 mUniformLocSamplerU,
1075 mUniformLocSamplerV,
1076 mAttributeLocTexCoord,
1077 mAttributeLocPos,
1078 mQuadVertexBuffer,
1079 mQuadIndexBuffer,
1080 mYWidthCutoff,
1081 mUVWidthCutoff);
1082
1083 restoreGLState();
1084 }
1085
updateCutoffs(float yWidth,float yStridePixels,float uvWidth,float uvStridePixels)1086 void YUVConverter::updateCutoffs(float yWidth, float yStridePixels,
1087 float uvWidth, float uvStridePixels) {
1088 switch (mFormat) {
1089 case FRAMEWORK_FORMAT_YV12:
1090 mYWidthCutoff = yWidth / yStridePixels;
1091 mUVWidthCutoff = uvWidth / uvStridePixels;
1092 break;
1093 case FRAMEWORK_FORMAT_NV12:
1094 case FRAMEWORK_FORMAT_P010:
1095 case FRAMEWORK_FORMAT_YUV_420_888:
1096 mYWidthCutoff = 1.0f;
1097 mUVWidthCutoff = 1.0f;
1098 break;
1099 case FRAMEWORK_FORMAT_GL_COMPATIBLE:
1100 FATAL("Input not a YUV format!");
1101 }
1102 }
1103
reset()1104 void YUVConverter::reset() {
1105 if (mQuadIndexBuffer) s_gles2.glDeleteBuffers(1, &mQuadIndexBuffer);
1106 if (mQuadVertexBuffer) s_gles2.glDeleteBuffers(1, &mQuadVertexBuffer);
1107 if (mProgram) s_gles2.glDeleteProgram(mProgram);
1108 if (mTextureY) s_gles2.glDeleteTextures(1, &mTextureY);
1109 if (isInterleaved(mFormat, mYuv420888ToNv21)) {
1110 if (mTextureU) s_gles2.glDeleteTextures(1, &mTextureU);
1111 } else {
1112 if (mTextureU) s_gles2.glDeleteTextures(1, &mTextureU);
1113 if (mTextureV) s_gles2.glDeleteTextures(1, &mTextureV);
1114 }
1115 mQuadIndexBuffer = 0;
1116 mQuadVertexBuffer = 0;
1117 mProgram = 0;
1118 mTextureY = 0;
1119 mTextureU = 0;
1120 mTextureV = 0;
1121 }
1122
~YUVConverter()1123 YUVConverter::~YUVConverter() {
1124 reset();
1125 }
1126
1127 } // namespace gl
1128 } // namespace gfxstream
1129