/src/mozilla-central/gfx/gl/GLBlitHelper.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | /* vim: set ts=8 sts=4 et sw=4 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "gfxUtils.h" |
8 | | #include "GLBlitHelper.h" |
9 | | #include "GLContext.h" |
10 | | #include "GLScreenBuffer.h" |
11 | | #include "ScopedGLHelpers.h" |
12 | | #include "mozilla/Preferences.h" |
13 | | #include "ImageContainer.h" |
14 | | #include "HeapCopyOfStackArray.h" |
15 | | #include "mozilla/ArrayUtils.h" |
16 | | #include "mozilla/gfx/Logging.h" |
17 | | #include "mozilla/gfx/Matrix.h" |
18 | | #include "mozilla/UniquePtr.h" |
19 | | #include "GPUVideoImage.h" |
20 | | |
21 | | #ifdef MOZ_WIDGET_ANDROID |
22 | | #include "GeneratedJNIWrappers.h" |
23 | | #include "AndroidSurfaceTexture.h" |
24 | | #include "GLImages.h" |
25 | | #include "GLLibraryEGL.h" |
26 | | #endif |
27 | | |
28 | | #ifdef XP_MACOSX |
29 | | #include "MacIOSurfaceImage.h" |
30 | | #include "GLContextCGL.h" |
31 | | #endif |
32 | | |
33 | | using mozilla::layers::PlanarYCbCrImage; |
34 | | using mozilla::layers::PlanarYCbCrData; |
35 | | |
36 | | namespace mozilla { |
37 | | namespace gl { |
38 | | |
39 | | // -- |
40 | | |
41 | | const char* const kFragHeader_Tex2D = "\ |
42 | | #define SAMPLER sampler2D \n\ |
43 | | #if __VERSION__ >= 130 \n\ |
44 | | #define TEXTURE texture \n\ |
45 | | #else \n\ |
46 | | #define TEXTURE texture2D \n\ |
47 | | #endif \n\ |
48 | | "; |
49 | | const char* const kFragHeader_Tex2DRect = "\ |
50 | | #define SAMPLER sampler2DRect \n\ |
51 | | #if __VERSION__ >= 130 \n\ |
52 | | #define TEXTURE texture \n\ |
53 | | #else \n\ |
54 | | #define TEXTURE texture2DRect \n\ |
55 | | #endif \n\ |
56 | | "; |
57 | | const char* const kFragHeader_TexExt = "\ |
58 | | #extension GL_OES_EGL_image_external : require \n\ |
59 | | #if __VERSION__ >= 130 \n\ |
60 | | #define TEXTURE texture \n\ |
61 | | #else \n\ |
62 | | #define TEXTURE texture2D \n\ |
63 | | #endif \n\ |
64 | | #define SAMPLER samplerExternalOES \n\ |
65 | | "; |
66 | | |
67 | | const char* const kFragBody_RGBA = "\ |
68 | | VARYING vec2 vTexCoord0; \n\ |
69 | | uniform SAMPLER uTex0; \n\ |
70 | | \n\ |
71 | | void main(void) \n\ |
72 | | { \n\ |
73 | | FRAG_COLOR = TEXTURE(uTex0, vTexCoord0); \n\ |
74 | | } \n\ |
75 | | "; |
76 | | const char* const kFragBody_CrYCb = "\ |
77 | | VARYING vec2 vTexCoord0; \n\ |
78 | | uniform SAMPLER uTex0; \n\ |
79 | | uniform MAT4X3 uColorMatrix; \n\ |
80 | | \n\ |
81 | | void main(void) \n\ |
82 | | { \n\ |
83 | | vec4 yuv = vec4(TEXTURE(uTex0, vTexCoord0).gbr, \n\ |
84 | | 1.0); \n\ |
85 | | FRAG_COLOR = vec4((uColorMatrix * yuv).rgb, 1.0); \n\ |
86 | | } \n\ |
87 | | "; |
88 | | const char* const kFragBody_NV12 = "\ |
89 | | VARYING vec2 vTexCoord0; \n\ |
90 | | VARYING vec2 vTexCoord1; \n\ |
91 | | uniform SAMPLER uTex0; \n\ |
92 | | uniform SAMPLER uTex1; \n\ |
93 | | uniform MAT4X3 uColorMatrix; \n\ |
94 | | \n\ |
95 | | void main(void) \n\ |
96 | | { \n\ |
97 | | vec4 yuv = vec4(TEXTURE(uTex0, vTexCoord0).x, \n\ |
98 | | TEXTURE(uTex1, vTexCoord1).xy, \n\ |
99 | | 1.0); \n\ |
100 | | FRAG_COLOR = vec4((uColorMatrix * yuv).rgb, 1.0); \n\ |
101 | | } \n\ |
102 | | "; |
103 | | const char* const kFragBody_PlanarYUV = "\ |
104 | | VARYING vec2 vTexCoord0; \n\ |
105 | | VARYING vec2 vTexCoord1; \n\ |
106 | | uniform SAMPLER uTex0; \n\ |
107 | | uniform SAMPLER uTex1; \n\ |
108 | | uniform SAMPLER uTex2; \n\ |
109 | | uniform MAT4X3 uColorMatrix; \n\ |
110 | | \n\ |
111 | | void main(void) \n\ |
112 | | { \n\ |
113 | | vec4 yuv = vec4(TEXTURE(uTex0, vTexCoord0).x, \n\ |
114 | | TEXTURE(uTex1, vTexCoord1).x, \n\ |
115 | | TEXTURE(uTex2, vTexCoord1).x, \n\ |
116 | | 1.0); \n\ |
117 | | FRAG_COLOR = vec4((uColorMatrix * yuv).rgb, 1.0); \n\ |
118 | | } \n\ |
119 | | "; |
120 | | |
121 | | // -- |
122 | | |
123 | | template<uint8_t N> |
124 | | /*static*/ Mat<N> |
125 | | Mat<N>::Zero() |
126 | 0 | { |
127 | 0 | Mat<N> ret; |
128 | 0 | for (auto& x : ret.m) { |
129 | 0 | x = 0.0f; |
130 | 0 | } |
131 | 0 | return ret; |
132 | 0 | } |
133 | | |
134 | | template<uint8_t N> |
135 | | /*static*/ Mat<N> |
136 | | Mat<N>::I() |
137 | 0 | { |
138 | 0 | auto ret = Mat<N>::Zero(); |
139 | 0 | for (uint8_t i = 0; i < N; i++) { |
140 | 0 | ret.at(i,i) = 1.0f; |
141 | 0 | } |
142 | 0 | return ret; |
143 | 0 | } |
144 | | |
145 | | template<uint8_t N> |
146 | | Mat<N> |
147 | | Mat<N>::operator*(const Mat<N>& r) const |
148 | | { |
149 | | Mat<N> ret; |
150 | | for (uint8_t x = 0; x < N; x++) { |
151 | | for (uint8_t y = 0; y < N; y++) { |
152 | | float sum = 0.0f; |
153 | | for (uint8_t i = 0; i < N; i++) { |
154 | | sum += at(i,y) * r.at(x,i); |
155 | | } |
156 | | ret.at(x,y) = sum; |
157 | | } |
158 | | } |
159 | | return ret; |
160 | | } |
161 | | |
162 | | Mat3 |
163 | | SubRectMat3(const float x, const float y, const float w, const float h) |
164 | 0 | { |
165 | 0 | auto ret = Mat3::Zero(); |
166 | 0 | ret.at(0,0) = w; |
167 | 0 | ret.at(1,1) = h; |
168 | 0 | ret.at(2,0) = x; |
169 | 0 | ret.at(2,1) = y; |
170 | 0 | ret.at(2,2) = 1.0f; |
171 | 0 | return ret; |
172 | 0 | } |
173 | | |
174 | | Mat3 |
175 | | SubRectMat3(const gfx::IntRect& subrect, const gfx::IntSize& size) |
176 | 0 | { |
177 | 0 | return SubRectMat3(float(subrect.X()) / size.width, |
178 | 0 | float(subrect.Y()) / size.height, |
179 | 0 | float(subrect.Width()) / size.width, |
180 | 0 | float(subrect.Height()) / size.height); |
181 | 0 | } |
182 | | |
183 | | Mat3 |
184 | | SubRectMat3(const gfx::IntRect& bigSubrect, const gfx::IntSize& smallSize, |
185 | | const gfx::IntSize& divisors) |
186 | 0 | { |
187 | 0 | const float x = float(bigSubrect.X()) / divisors.width; |
188 | 0 | const float y = float(bigSubrect.Y()) / divisors.height; |
189 | 0 | const float w = float(bigSubrect.Width()) / divisors.width; |
190 | 0 | const float h = float(bigSubrect.Height()) / divisors.height; |
191 | 0 | return SubRectMat3(x / smallSize.width, |
192 | 0 | y / smallSize.height, |
193 | 0 | w / smallSize.width, |
194 | 0 | h / smallSize.height); |
195 | 0 | } |
196 | | |
197 | | // -- |
198 | | |
199 | | ScopedSaveMultiTex::ScopedSaveMultiTex(GLContext* const gl, const uint8_t texCount, |
200 | | const GLenum texTarget) |
201 | | : mGL(*gl) |
202 | | , mTexCount(texCount) |
203 | | , mTexTarget(texTarget) |
204 | | , mOldTexUnit(mGL.GetIntAs<GLenum>(LOCAL_GL_ACTIVE_TEXTURE)) |
205 | 0 | { |
206 | 0 | GLenum texBinding; |
207 | 0 | switch (mTexTarget) { |
208 | 0 | case LOCAL_GL_TEXTURE_2D: |
209 | 0 | texBinding = LOCAL_GL_TEXTURE_BINDING_2D; |
210 | 0 | break; |
211 | 0 | case LOCAL_GL_TEXTURE_RECTANGLE: |
212 | 0 | texBinding = LOCAL_GL_TEXTURE_BINDING_RECTANGLE; |
213 | 0 | break; |
214 | 0 | case LOCAL_GL_TEXTURE_EXTERNAL: |
215 | 0 | texBinding = LOCAL_GL_TEXTURE_BINDING_EXTERNAL; |
216 | 0 | break; |
217 | 0 | default: |
218 | 0 | gfxCriticalError() << "Unhandled texTarget: " << texTarget; |
219 | 0 | } |
220 | 0 |
|
221 | 0 | for (uint8_t i = 0; i < mTexCount; i++) { |
222 | 0 | mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + i); |
223 | 0 | if (mGL.IsSupported(GLFeature::sampler_objects)) { |
224 | 0 | mOldTexSampler[i] = mGL.GetIntAs<GLuint>(LOCAL_GL_SAMPLER_BINDING); |
225 | 0 | mGL.fBindSampler(i, 0); |
226 | 0 | } |
227 | 0 | mOldTex[i] = mGL.GetIntAs<GLuint>(texBinding); |
228 | 0 | } |
229 | 0 | } |
230 | | |
231 | | ScopedSaveMultiTex::~ScopedSaveMultiTex() |
232 | 0 | { |
233 | 0 | for (uint8_t i = 0; i < mTexCount; i++) { |
234 | 0 | mGL.fActiveTexture(LOCAL_GL_TEXTURE0 + i); |
235 | 0 | if (mGL.IsSupported(GLFeature::sampler_objects)) { |
236 | 0 | mGL.fBindSampler(i, mOldTexSampler[i]); |
237 | 0 | } |
238 | 0 | mGL.fBindTexture(mTexTarget, mOldTex[i]); |
239 | 0 | } |
240 | 0 | mGL.fActiveTexture(mOldTexUnit); |
241 | 0 | } |
242 | | |
243 | | // -- |
244 | | |
245 | | class ScopedBindArrayBuffer final |
246 | | { |
247 | | GLContext& mGL; |
248 | | const GLuint mOldVBO; |
249 | | |
250 | | public: |
251 | | ScopedBindArrayBuffer(GLContext* const gl, const GLuint vbo) |
252 | | : mGL(*gl) |
253 | | , mOldVBO(mGL.GetIntAs<GLuint>(LOCAL_GL_ARRAY_BUFFER_BINDING)) |
254 | 0 | { |
255 | 0 | mGL.fBindBuffer(LOCAL_GL_ARRAY_BUFFER, vbo); |
256 | 0 | } |
257 | | |
258 | | ~ScopedBindArrayBuffer() |
259 | 0 | { |
260 | 0 | mGL.fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mOldVBO); |
261 | 0 | } |
262 | | }; |
263 | | |
264 | | // -- |
265 | | |
266 | | class ScopedShader final |
267 | | { |
268 | | GLContext& mGL; |
269 | | const GLuint mName; |
270 | | |
271 | | public: |
272 | | ScopedShader(GLContext* const gl, const GLenum shaderType) |
273 | | : mGL(*gl) |
274 | | , mName(mGL.fCreateShader(shaderType)) |
275 | 0 | { } |
276 | | |
277 | | ~ScopedShader() |
278 | 0 | { |
279 | 0 | mGL.fDeleteShader(mName); |
280 | 0 | } |
281 | | |
282 | 0 | operator GLuint() const { return mName; } |
283 | | }; |
284 | | |
285 | | // -- |
286 | | |
287 | | class SaveRestoreCurrentProgram final |
288 | | { |
289 | | GLContext& mGL; |
290 | | const GLuint mOld; |
291 | | |
292 | | public: |
293 | | explicit SaveRestoreCurrentProgram(GLContext* const gl) |
294 | | : mGL(*gl) |
295 | | , mOld(mGL.GetIntAs<GLuint>(LOCAL_GL_CURRENT_PROGRAM)) |
296 | 0 | { } |
297 | | |
298 | | ~SaveRestoreCurrentProgram() |
299 | 0 | { |
300 | 0 | mGL.fUseProgram(mOld); |
301 | 0 | } |
302 | | }; |
303 | | |
304 | | // -- |
305 | | |
306 | | class ScopedDrawBlitState final |
307 | | { |
308 | | GLContext& mGL; |
309 | | |
310 | | const bool blend; |
311 | | const bool cullFace; |
312 | | const bool depthTest; |
313 | | const bool dither; |
314 | | const bool polyOffsFill; |
315 | | const bool sampleAToC; |
316 | | const bool sampleCover; |
317 | | const bool scissor; |
318 | | const bool stencil; |
319 | | Maybe<bool> rasterizerDiscard; |
320 | | |
321 | | realGLboolean colorMask[4]; |
322 | | GLint viewport[4]; |
323 | | |
324 | | public: |
325 | | ScopedDrawBlitState(GLContext* const gl, const gfx::IntSize& destSize) |
326 | | : mGL(*gl) |
327 | | , blend (mGL.PushEnabled(LOCAL_GL_BLEND, false)) |
328 | | , cullFace (mGL.PushEnabled(LOCAL_GL_CULL_FACE, false)) |
329 | | , depthTest (mGL.PushEnabled(LOCAL_GL_DEPTH_TEST, false)) |
330 | | , dither (mGL.PushEnabled(LOCAL_GL_DITHER, true)) |
331 | | , polyOffsFill(mGL.PushEnabled(LOCAL_GL_POLYGON_OFFSET_FILL, false)) |
332 | | , sampleAToC (mGL.PushEnabled(LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE, false)) |
333 | | , sampleCover (mGL.PushEnabled(LOCAL_GL_SAMPLE_COVERAGE, false)) |
334 | | , scissor (mGL.PushEnabled(LOCAL_GL_SCISSOR_TEST, false)) |
335 | | , stencil (mGL.PushEnabled(LOCAL_GL_STENCIL_TEST, false)) |
336 | 0 | { |
337 | 0 | if (mGL.IsSupported(GLFeature::transform_feedback2)) { |
338 | 0 | // Technically transform_feedback2 requires transform_feedback, which actually |
339 | 0 | // adds RASTERIZER_DISCARD. |
340 | 0 | rasterizerDiscard = Some(mGL.PushEnabled(LOCAL_GL_RASTERIZER_DISCARD, false)); |
341 | 0 | } |
342 | 0 |
|
343 | 0 | mGL.fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK, colorMask); |
344 | 0 | mGL.fColorMask(true, true, true, true); |
345 | 0 |
|
346 | 0 | mGL.fGetIntegerv(LOCAL_GL_VIEWPORT, viewport); |
347 | 0 | MOZ_ASSERT(destSize.width && destSize.height); |
348 | 0 | mGL.fViewport(0, 0, destSize.width, destSize.height); |
349 | 0 | } |
350 | | |
351 | | ~ScopedDrawBlitState() |
352 | 0 | { |
353 | 0 | mGL.SetEnabled(LOCAL_GL_BLEND, blend ); |
354 | 0 | mGL.SetEnabled(LOCAL_GL_CULL_FACE, cullFace ); |
355 | 0 | mGL.SetEnabled(LOCAL_GL_DEPTH_TEST, depthTest ); |
356 | 0 | mGL.SetEnabled(LOCAL_GL_DITHER, dither ); |
357 | 0 | mGL.SetEnabled(LOCAL_GL_POLYGON_OFFSET_FILL, polyOffsFill); |
358 | 0 | mGL.SetEnabled(LOCAL_GL_SAMPLE_ALPHA_TO_COVERAGE, sampleAToC ); |
359 | 0 | mGL.SetEnabled(LOCAL_GL_SAMPLE_COVERAGE, sampleCover ); |
360 | 0 | mGL.SetEnabled(LOCAL_GL_SCISSOR_TEST, scissor ); |
361 | 0 | mGL.SetEnabled(LOCAL_GL_STENCIL_TEST, stencil ); |
362 | 0 | if (rasterizerDiscard) { |
363 | 0 | mGL.SetEnabled(LOCAL_GL_RASTERIZER_DISCARD, rasterizerDiscard.value()); |
364 | 0 | } |
365 | 0 |
|
366 | 0 | mGL.fColorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]); |
367 | 0 | mGL.fViewport(viewport[0], viewport[1], viewport[2], viewport[3]); |
368 | 0 | } |
369 | | }; |
370 | | |
371 | | // -- |
372 | | |
373 | | DrawBlitProg::DrawBlitProg(const GLBlitHelper* const parent, const GLuint prog) |
374 | | : mParent(*parent) |
375 | | , mProg(prog) |
376 | | , mLoc_uDestMatrix(mParent.mGL->fGetUniformLocation(mProg, "uDestMatrix")) |
377 | | , mLoc_uTexMatrix0(mParent.mGL->fGetUniformLocation(mProg, "uTexMatrix0")) |
378 | | , mLoc_uTexMatrix1(mParent.mGL->fGetUniformLocation(mProg, "uTexMatrix1")) |
379 | | , mLoc_uColorMatrix(mParent.mGL->fGetUniformLocation(mProg, "uColorMatrix")) |
380 | 0 | { |
381 | 0 | MOZ_ASSERT(mLoc_uDestMatrix != -1); |
382 | 0 | MOZ_ASSERT(mLoc_uTexMatrix0 != -1); |
383 | 0 | if (mLoc_uColorMatrix != -1) { |
384 | 0 | MOZ_ASSERT(mLoc_uTexMatrix1 != -1); |
385 | 0 |
|
386 | 0 | const auto& gl = mParent.mGL; |
387 | 0 | int32_t numActiveUniforms = 0; |
388 | 0 | gl->fGetProgramiv(mProg, LOCAL_GL_ACTIVE_UNIFORMS, &numActiveUniforms); |
389 | 0 |
|
390 | 0 | const size_t kMaxNameSize = 32; |
391 | 0 | char name[kMaxNameSize] = {0}; |
392 | 0 | GLint size = 0; |
393 | 0 | GLenum type = 0; |
394 | 0 | for (int32_t i = 0; i < numActiveUniforms; i++) { |
395 | 0 | gl->fGetActiveUniform(mProg, i, kMaxNameSize, nullptr, &size, &type, name); |
396 | 0 | if (strcmp("uColorMatrix", name) == 0) { |
397 | 0 | mType_uColorMatrix = type; |
398 | 0 | break; |
399 | 0 | } |
400 | 0 | } |
401 | 0 | MOZ_ASSERT(mType_uColorMatrix); |
402 | 0 | } |
403 | 0 | } |
404 | | |
405 | | DrawBlitProg::~DrawBlitProg() |
406 | 0 | { |
407 | 0 | const auto& gl = mParent.mGL; |
408 | 0 | if (!gl->MakeCurrent()) |
409 | 0 | return; |
410 | 0 | |
411 | 0 | gl->fDeleteProgram(mProg); |
412 | 0 | } |
413 | | |
414 | | void |
415 | | DrawBlitProg::Draw(const BaseArgs& args, const YUVArgs* const argsYUV) const |
416 | 0 | { |
417 | 0 | const auto& gl = mParent.mGL; |
418 | 0 |
|
419 | 0 | const SaveRestoreCurrentProgram oldProg(gl); |
420 | 0 | gl->fUseProgram(mProg); |
421 | 0 |
|
422 | 0 | // -- |
423 | 0 |
|
424 | 0 | Mat3 destMatrix; |
425 | 0 | if (args.destRect) { |
426 | 0 | const auto& destRect = args.destRect.value(); |
427 | 0 | destMatrix = SubRectMat3(destRect.X() / args.destSize.width, |
428 | 0 | destRect.Y() / args.destSize.height, |
429 | 0 | destRect.Width() / args.destSize.width, |
430 | 0 | destRect.Height() / args.destSize.height); |
431 | 0 | } else { |
432 | 0 | destMatrix = Mat3::I(); |
433 | 0 | } |
434 | 0 |
|
435 | 0 | if (args.yFlip) { |
436 | 0 | // Apply the y-flip matrix before the destMatrix. |
437 | 0 | // That is, flip y=[0-1] to y=[1-0] before we restrict to the destRect. |
438 | 0 | destMatrix.at(2,1) += destMatrix.at(1,1); |
439 | 0 | destMatrix.at(1,1) *= -1.0f; |
440 | 0 | } |
441 | 0 |
|
442 | 0 | gl->fUniformMatrix3fv(mLoc_uDestMatrix, 1, false, destMatrix.m); |
443 | 0 | gl->fUniformMatrix3fv(mLoc_uTexMatrix0, 1, false, args.texMatrix0.m); |
444 | 0 |
|
445 | 0 | MOZ_ASSERT(bool(argsYUV) == (mLoc_uColorMatrix != -1)); |
446 | 0 | if (argsYUV) { |
447 | 0 | gl->fUniformMatrix3fv(mLoc_uTexMatrix1, 1, false, argsYUV->texMatrix1.m); |
448 | 0 |
|
449 | 0 | const auto& colorMatrix = gfxUtils::YuvToRgbMatrix4x4ColumnMajor(argsYUV->colorSpace); |
450 | 0 | float mat4x3[4*3]; |
451 | 0 | switch (mType_uColorMatrix) { |
452 | 0 | case LOCAL_GL_FLOAT_MAT4: |
453 | 0 | gl->fUniformMatrix4fv(mLoc_uColorMatrix, 1, false, colorMatrix); |
454 | 0 | break; |
455 | 0 | case LOCAL_GL_FLOAT_MAT4x3: |
456 | 0 | for (int x = 0; x < 4; x++) { |
457 | 0 | for (int y = 0; y < 3; y++) { |
458 | 0 | mat4x3[3*x+y] = colorMatrix[4*x+y]; |
459 | 0 | } |
460 | 0 | } |
461 | 0 | gl->fUniformMatrix4x3fv(mLoc_uColorMatrix, 1, false, mat4x3); |
462 | 0 | break; |
463 | 0 | default: |
464 | 0 | gfxCriticalError() << "Bad mType_uColorMatrix: " |
465 | 0 | << gfx::hexa(mType_uColorMatrix); |
466 | 0 | } |
467 | 0 | } |
468 | 0 |
|
469 | 0 | // -- |
470 | 0 |
|
471 | 0 | const ScopedDrawBlitState drawState(gl, args.destSize); |
472 | 0 |
|
473 | 0 | GLuint oldVAO; |
474 | 0 | GLint vaa0Enabled; |
475 | 0 | GLint vaa0Size; |
476 | 0 | GLenum vaa0Type; |
477 | 0 | GLint vaa0Normalized; |
478 | 0 | GLsizei vaa0Stride; |
479 | 0 | GLvoid* vaa0Pointer; |
480 | 0 | if (mParent.mQuadVAO) { |
481 | 0 | oldVAO = gl->GetIntAs<GLuint>(LOCAL_GL_VERTEX_ARRAY_BINDING); |
482 | 0 | gl->fBindVertexArray(mParent.mQuadVAO); |
483 | 0 | } else { |
484 | 0 | gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED, &vaa0Enabled); |
485 | 0 | gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE, &vaa0Size); |
486 | 0 | gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE, (GLint*)&vaa0Type); |
487 | 0 | gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, &vaa0Normalized); |
488 | 0 | gl->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE, (GLint*)&vaa0Stride); |
489 | 0 | gl->fGetVertexAttribPointerv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER, &vaa0Pointer); |
490 | 0 |
|
491 | 0 | gl->fEnableVertexAttribArray(0); |
492 | 0 | const ScopedBindArrayBuffer bindVBO(gl, mParent.mQuadVBO); |
493 | 0 | gl->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, false, 0, 0); |
494 | 0 | } |
495 | 0 |
|
496 | 0 | gl->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4); |
497 | 0 |
|
498 | 0 | if (mParent.mQuadVAO) { |
499 | 0 | gl->fBindVertexArray(oldVAO); |
500 | 0 | } else { |
501 | 0 | if (vaa0Enabled) { |
502 | 0 | gl->fEnableVertexAttribArray(0); |
503 | 0 | } else { |
504 | 0 | gl->fDisableVertexAttribArray(0); |
505 | 0 | } |
506 | 0 | gl->fVertexAttribPointer(0, vaa0Size, vaa0Type, bool(vaa0Normalized), vaa0Stride, |
507 | 0 | vaa0Pointer); |
508 | 0 | } |
509 | 0 | } |
510 | | |
511 | | // -- |
512 | | |
513 | | GLBlitHelper::GLBlitHelper(GLContext* const gl) |
514 | | : mGL(gl) |
515 | | , mDrawBlitProg_VertShader(mGL->fCreateShader(LOCAL_GL_VERTEX_SHADER)) |
516 | | //, mYuvUploads_YSize(0, 0) |
517 | | //, mYuvUploads_UVSize(0, 0) |
518 | 0 | { |
519 | 0 | mGL->fGenBuffers(1, &mQuadVBO); |
520 | 0 | { |
521 | 0 | const ScopedBindArrayBuffer bindVBO(mGL, mQuadVBO); |
522 | 0 |
|
523 | 0 | const float quadData[] = { |
524 | 0 | 0, 0, |
525 | 0 | 1, 0, |
526 | 0 | 0, 1, |
527 | 0 | 1, 1 |
528 | 0 | }; |
529 | 0 | const HeapCopyOfStackArray<float> heapQuadData(quadData); |
530 | 0 | mGL->fBufferData(LOCAL_GL_ARRAY_BUFFER, heapQuadData.ByteLength(), |
531 | 0 | heapQuadData.Data(), LOCAL_GL_STATIC_DRAW); |
532 | 0 |
|
533 | 0 | if (mGL->IsSupported(GLFeature::vertex_array_object)) { |
534 | 0 | const auto prev = mGL->GetIntAs<GLuint>(LOCAL_GL_VERTEX_ARRAY_BINDING); |
535 | 0 |
|
536 | 0 | mGL->fGenVertexArrays(1, &mQuadVAO); |
537 | 0 | mGL->fBindVertexArray(mQuadVAO); |
538 | 0 | mGL->fEnableVertexAttribArray(0); |
539 | 0 | mGL->fVertexAttribPointer(0, 2, LOCAL_GL_FLOAT, false, 0, 0); |
540 | 0 |
|
541 | 0 | mGL->fBindVertexArray(prev); |
542 | 0 | } |
543 | 0 | } |
544 | 0 |
|
545 | 0 | // -- |
546 | 0 |
|
547 | 0 | const auto glslVersion = mGL->ShadingLanguageVersion(); |
548 | 0 |
|
549 | 0 | // Always use 100 on ES because some devices have OES_EGL_image_external but not |
550 | 0 | // OES_EGL_image_external_essl3. We could just use 100 in that particular case, but |
551 | 0 | // this is a lot easier and is not harmful to other usages. |
552 | 0 | if (mGL->IsGLES()) { |
553 | 0 | mDrawBlitProg_VersionLine = nsCString("#version 100\n"); |
554 | 0 | } else if (glslVersion >= 130) { |
555 | 0 | mDrawBlitProg_VersionLine = nsPrintfCString("#version %u\n", glslVersion); |
556 | 0 | } |
557 | 0 |
|
558 | 0 | const char kVertSource[] = "\ |
559 | 0 | #if __VERSION__ >= 130 \n\ |
560 | 0 | #define ATTRIBUTE in \n\ |
561 | 0 | #define VARYING out \n\ |
562 | 0 | #else \n\ |
563 | 0 | #define ATTRIBUTE attribute \n\ |
564 | 0 | #define VARYING varying \n\ |
565 | 0 | #endif \n\ |
566 | 0 | \n\ |
567 | 0 | ATTRIBUTE vec2 aVert; // [0.0-1.0] \n\ |
568 | 0 | \n\ |
569 | 0 | uniform mat3 uDestMatrix; \n\ |
570 | 0 | uniform mat3 uTexMatrix0; \n\ |
571 | 0 | uniform mat3 uTexMatrix1; \n\ |
572 | 0 | \n\ |
573 | 0 | VARYING vec2 vTexCoord0; \n\ |
574 | 0 | VARYING vec2 vTexCoord1; \n\ |
575 | 0 | \n\ |
576 | 0 | void main(void) \n\ |
577 | 0 | { \n\ |
578 | 0 | vec2 destPos = (uDestMatrix * vec3(aVert, 1.0)).xy; \n\ |
579 | 0 | gl_Position = vec4(destPos * 2.0 - 1.0, 0.0, 1.0); \n\ |
580 | 0 | \n\ |
581 | 0 | vTexCoord0 = (uTexMatrix0 * vec3(aVert, 1.0)).xy; \n\ |
582 | 0 | vTexCoord1 = (uTexMatrix1 * vec3(aVert, 1.0)).xy; \n\ |
583 | 0 | } \n\ |
584 | 0 | "; |
585 | 0 | const char* const parts[] = { |
586 | 0 | mDrawBlitProg_VersionLine.get(), |
587 | 0 | kVertSource |
588 | 0 | }; |
589 | 0 | mGL->fShaderSource(mDrawBlitProg_VertShader, ArrayLength(parts), parts, nullptr); |
590 | 0 | mGL->fCompileShader(mDrawBlitProg_VertShader); |
591 | 0 | } |
592 | | |
593 | | GLBlitHelper::~GLBlitHelper() |
594 | 0 | { |
595 | 0 | for (const auto& pair : mDrawBlitProgs) { |
596 | 0 | const auto& ptr = pair.second; |
597 | 0 | delete ptr; |
598 | 0 | } |
599 | 0 | mDrawBlitProgs.clear(); |
600 | 0 |
|
601 | 0 | if (!mGL->MakeCurrent()) |
602 | 0 | return; |
603 | 0 | |
604 | 0 | mGL->fDeleteShader(mDrawBlitProg_VertShader); |
605 | 0 | mGL->fDeleteBuffers(1, &mQuadVBO); |
606 | 0 |
|
607 | 0 | if (mQuadVAO) { |
608 | 0 | mGL->fDeleteVertexArrays(1, &mQuadVAO); |
609 | 0 | } |
610 | 0 | } |
611 | | |
612 | | // -- |
613 | | |
614 | | const DrawBlitProg* |
615 | | GLBlitHelper::GetDrawBlitProg(const DrawBlitProg::Key& key) const |
616 | 0 | { |
617 | 0 | const auto& res = mDrawBlitProgs.insert({key, nullptr}); |
618 | 0 | auto& pair = *(res.first); |
619 | 0 | const auto& didInsert = res.second; |
620 | 0 | if (didInsert) { |
621 | 0 | pair.second = CreateDrawBlitProg(pair.first); |
622 | 0 | } |
623 | 0 | return pair.second; |
624 | 0 | } |
625 | | |
626 | | const DrawBlitProg* |
627 | | GLBlitHelper::CreateDrawBlitProg(const DrawBlitProg::Key& key) const |
628 | 0 | { |
629 | 0 | const char kFragHeader_Global[] = "\ |
630 | 0 | #ifdef GL_ES \n\ |
631 | 0 | #ifdef GL_FRAGMENT_PRECISION_HIGH \n\ |
632 | 0 | precision highp float; \n\ |
633 | 0 | #else \n\ |
634 | 0 | precision mediump float; \n\ |
635 | 0 | #endif \n\ |
636 | 0 | #endif \n\ |
637 | 0 | \n\ |
638 | 0 | #if __VERSION__ >= 130 \n\ |
639 | 0 | #define VARYING in \n\ |
640 | 0 | #define FRAG_COLOR oFragColor \n\ |
641 | 0 | out vec4 FRAG_COLOR; \n\ |
642 | 0 | #else \n\ |
643 | 0 | #define VARYING varying \n\ |
644 | 0 | #define FRAG_COLOR gl_FragColor \n\ |
645 | 0 | #endif \n\ |
646 | 0 | \n\ |
647 | 0 | #if __VERSION__ >= 120 \n\ |
648 | 0 | #define MAT4X3 mat4x3 \n\ |
649 | 0 | #else \n\ |
650 | 0 | #define MAT4X3 mat4 \n\ |
651 | 0 | #endif \n\ |
652 | 0 | "; |
653 | 0 |
|
654 | 0 | const ScopedShader fs(mGL, LOCAL_GL_FRAGMENT_SHADER); |
655 | 0 | const char* const parts[] = { |
656 | 0 | mDrawBlitProg_VersionLine.get(), |
657 | 0 | key.fragHeader, |
658 | 0 | kFragHeader_Global, |
659 | 0 | key.fragBody |
660 | 0 | }; |
661 | 0 | mGL->fShaderSource(fs, ArrayLength(parts), parts, nullptr); |
662 | 0 | mGL->fCompileShader(fs); |
663 | 0 |
|
664 | 0 | const auto prog = mGL->fCreateProgram(); |
665 | 0 | mGL->fAttachShader(prog, mDrawBlitProg_VertShader); |
666 | 0 | mGL->fAttachShader(prog, fs); |
667 | 0 |
|
668 | 0 | mGL->fBindAttribLocation(prog, 0, "aPosition"); |
669 | 0 | mGL->fLinkProgram(prog); |
670 | 0 |
|
671 | 0 | GLenum status = 0; |
672 | 0 | mGL->fGetProgramiv(prog, LOCAL_GL_LINK_STATUS, (GLint*)&status); |
673 | 0 | if (status == LOCAL_GL_TRUE) { |
674 | 0 | const SaveRestoreCurrentProgram oldProg(mGL); |
675 | 0 | mGL->fUseProgram(prog); |
676 | 0 | const char* samplerNames[] = { |
677 | 0 | "uTex0", |
678 | 0 | "uTex1", |
679 | 0 | "uTex2" |
680 | 0 | }; |
681 | 0 | for (int i = 0; i < 3; i++) { |
682 | 0 | const auto loc = mGL->fGetUniformLocation(prog, samplerNames[i]); |
683 | 0 | if (loc == -1) |
684 | 0 | break; |
685 | 0 | mGL->fUniform1i(loc, i); |
686 | 0 | } |
687 | 0 |
|
688 | 0 | return new DrawBlitProg(this, prog); |
689 | 0 | } |
690 | 0 |
|
691 | 0 | GLuint progLogLen = 0; |
692 | 0 | mGL->fGetProgramiv(prog, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&progLogLen); |
693 | 0 | const UniquePtr<char[]> progLog(new char[progLogLen+1]); |
694 | 0 | mGL->fGetProgramInfoLog(prog, progLogLen, nullptr, progLog.get()); |
695 | 0 | progLog[progLogLen] = 0; |
696 | 0 |
|
697 | 0 | const auto& vs = mDrawBlitProg_VertShader; |
698 | 0 | GLuint vsLogLen = 0; |
699 | 0 | mGL->fGetShaderiv(vs, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&vsLogLen); |
700 | 0 | const UniquePtr<char[]> vsLog(new char[vsLogLen+1]); |
701 | 0 | mGL->fGetShaderInfoLog(vs, vsLogLen, nullptr, vsLog.get()); |
702 | 0 | vsLog[vsLogLen] = 0; |
703 | 0 |
|
704 | 0 | GLuint fsLogLen = 0; |
705 | 0 | mGL->fGetShaderiv(fs, LOCAL_GL_INFO_LOG_LENGTH, (GLint*)&fsLogLen); |
706 | 0 | const UniquePtr<char[]> fsLog(new char[fsLogLen+1]); |
707 | 0 | mGL->fGetShaderInfoLog(fs, fsLogLen, nullptr, fsLog.get()); |
708 | 0 | fsLog[fsLogLen] = 0; |
709 | 0 |
|
710 | 0 | gfxCriticalError() << "DrawBlitProg link failed:\n" |
711 | 0 | << "progLog: " << progLog.get() << "\n" |
712 | 0 | << "vsLog: " << vsLog.get() << "\n" |
713 | 0 | << "fsLog: " << fsLog.get() << "\n"; |
714 | 0 | return nullptr; |
715 | 0 | } |
716 | | |
717 | | // ----------------------------------------------------------------------------- |
718 | | |
719 | | bool |
720 | | GLBlitHelper::BlitImageToFramebuffer(layers::Image* const srcImage, |
721 | | const gfx::IntSize& destSize, |
722 | | const OriginPos destOrigin) |
723 | 0 | { |
724 | 0 | switch (srcImage->GetFormat()) { |
725 | 0 | case ImageFormat::PLANAR_YCBCR: |
726 | 0 | return BlitImage(static_cast<PlanarYCbCrImage*>(srcImage), destSize, destOrigin); |
727 | 0 |
|
728 | | #ifdef MOZ_WIDGET_ANDROID |
729 | | case ImageFormat::SURFACE_TEXTURE: |
730 | | return BlitImage(static_cast<layers::SurfaceTextureImage*>(srcImage), destSize, |
731 | | destOrigin); |
732 | | #endif |
733 | | #ifdef XP_MACOSX |
734 | | case ImageFormat::MAC_IOSURFACE: |
735 | | return BlitImage(srcImage->AsMacIOSurfaceImage(), destSize, destOrigin); |
736 | | #endif |
737 | | #ifdef XP_WIN |
738 | | case ImageFormat::GPU_VIDEO: |
739 | | return BlitImage(static_cast<layers::GPUVideoImage*>(srcImage), destSize, |
740 | | destOrigin); |
741 | | case ImageFormat::D3D11_YCBCR_IMAGE: |
742 | | return BlitImage((layers::D3D11YCbCrImage*)srcImage, destSize, destOrigin); |
743 | | case ImageFormat::D3D9_RGB32_TEXTURE: |
744 | | return false; // todo |
745 | | #endif |
746 | 0 | default: |
747 | 0 | gfxCriticalError() << "Unhandled srcImage->GetFormat(): " |
748 | 0 | << uint32_t(srcImage->GetFormat()); |
749 | 0 | return false; |
750 | 0 | } |
751 | 0 | } |
752 | | |
753 | | // ------------------------------------- |
754 | | |
755 | | #ifdef MOZ_WIDGET_ANDROID |
756 | | bool |
757 | | GLBlitHelper::BlitImage(layers::SurfaceTextureImage* srcImage, const gfx::IntSize& destSize, |
758 | | const OriginPos destOrigin) const |
759 | | { |
760 | | AndroidSurfaceTextureHandle handle = srcImage->GetHandle(); |
761 | | const auto& surfaceTexture = java::GeckoSurfaceTexture::Lookup(handle); |
762 | | |
763 | | if (!surfaceTexture) { |
764 | | return false; |
765 | | } |
766 | | |
767 | | const ScopedBindTextureUnit boundTU(mGL, LOCAL_GL_TEXTURE0); |
768 | | |
769 | | if (!surfaceTexture->IsAttachedToGLContext((int64_t)mGL)) { |
770 | | GLuint tex; |
771 | | mGL->MakeCurrent(); |
772 | | mGL->fGenTextures(1, &tex); |
773 | | |
774 | | if (NS_FAILED(surfaceTexture->AttachToGLContext((int64_t)mGL, tex))) { |
775 | | mGL->fDeleteTextures(1, &tex); |
776 | | return false; |
777 | | } |
778 | | } |
779 | | |
780 | | const ScopedBindTexture savedTex(mGL, surfaceTexture->GetTexName(), LOCAL_GL_TEXTURE_EXTERNAL); |
781 | | surfaceTexture->UpdateTexImage(); |
782 | | |
783 | | gfx::Matrix4x4 transform4; |
784 | | AndroidSurfaceTexture::GetTransformMatrix(java::sdk::SurfaceTexture::Ref::From(surfaceTexture), |
785 | | &transform4); |
786 | | Mat3 transform3; |
787 | | transform3.at(0,0) = transform4._11; |
788 | | transform3.at(0,1) = transform4._12; |
789 | | transform3.at(0,2) = transform4._14; |
790 | | transform3.at(1,0) = transform4._21; |
791 | | transform3.at(1,1) = transform4._22; |
792 | | transform3.at(1,2) = transform4._24; |
793 | | transform3.at(2,0) = transform4._41; |
794 | | transform3.at(2,1) = transform4._42; |
795 | | transform3.at(2,2) = transform4._44; |
796 | | |
797 | | // We don't do w-divison, so if these aren't what we expect, we're probably doing |
798 | | // something wrong. |
799 | | MOZ_ASSERT(transform3.at(0,2) == 0); |
800 | | MOZ_ASSERT(transform3.at(1,2) == 0); |
801 | | MOZ_ASSERT(transform3.at(2,2) == 1); |
802 | | |
803 | | const auto& srcOrigin = srcImage->GetOriginPos(); |
804 | | |
805 | | // I honestly have no idea why this logic is flipped, but changing the |
806 | | // source origin would mean we'd have to flip it in the compositor |
807 | | // which makes just as little sense as this. |
808 | | const bool yFlip = (srcOrigin == destOrigin); |
809 | | |
810 | | const auto& prog = GetDrawBlitProg({kFragHeader_TexExt, kFragBody_RGBA}); |
811 | | MOZ_RELEASE_ASSERT(prog); |
812 | | |
813 | | // There is no padding on these images, so we can use the GetTransformMatrix directly. |
814 | | const DrawBlitProg::BaseArgs baseArgs = { transform3, yFlip, destSize, Nothing() }; |
815 | | prog->Draw(baseArgs, nullptr); |
816 | | |
817 | | if (surfaceTexture->IsSingleBuffer()) { |
818 | | surfaceTexture->ReleaseTexImage(); |
819 | | } |
820 | | |
821 | | return true; |
822 | | } |
823 | | #endif |
824 | | |
825 | | // ------------------------------------- |
826 | | |
827 | | bool |
828 | | GuessDivisors(const gfx::IntSize& ySize, const gfx::IntSize& uvSize, |
829 | | gfx::IntSize* const out_divisors) |
830 | 0 | { |
831 | 0 | const gfx::IntSize divisors((ySize.width == uvSize.width ) ? 1 : 2, |
832 | 0 | (ySize.height == uvSize.height) ? 1 : 2); |
833 | 0 | if (uvSize.width * divisors.width != ySize.width || |
834 | 0 | uvSize.height * divisors.height != ySize.height) |
835 | 0 | { |
836 | 0 | return false; |
837 | 0 | } |
838 | 0 | *out_divisors = divisors; |
839 | 0 | return true; |
840 | 0 | } |
841 | | |
842 | | bool |
843 | | GLBlitHelper::BlitImage(layers::PlanarYCbCrImage* const yuvImage, |
844 | | const gfx::IntSize& destSize, const OriginPos destOrigin) |
845 | 0 | { |
846 | 0 | const auto& prog = GetDrawBlitProg({kFragHeader_Tex2D, kFragBody_PlanarYUV}); |
847 | 0 | MOZ_RELEASE_ASSERT(prog); |
848 | 0 |
|
849 | 0 | if (!mYuvUploads[0]) { |
850 | 0 | mGL->fGenTextures(3, mYuvUploads); |
851 | 0 | const ScopedBindTexture bindTex(mGL, mYuvUploads[0]); |
852 | 0 | mGL->TexParams_SetClampNoMips(); |
853 | 0 | mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[1]); |
854 | 0 | mGL->TexParams_SetClampNoMips(); |
855 | 0 | mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[2]); |
856 | 0 | mGL->TexParams_SetClampNoMips(); |
857 | 0 | } |
858 | 0 |
|
859 | 0 | // -- |
860 | 0 |
|
861 | 0 | const PlanarYCbCrData* const yuvData = yuvImage->GetData(); |
862 | 0 |
|
863 | 0 | if (yuvData->mYSkip || yuvData->mCbSkip || yuvData->mCrSkip || |
864 | 0 | yuvData->mYSize.width < 0 || yuvData->mYSize.height < 0 || |
865 | 0 | yuvData->mCbCrSize.width < 0 || yuvData->mCbCrSize.height < 0 || |
866 | 0 | yuvData->mYStride < 0 || yuvData->mCbCrStride < 0) |
867 | 0 | { |
868 | 0 | gfxCriticalError() << "Unusual PlanarYCbCrData: " |
869 | 0 | << yuvData->mYSkip << "," |
870 | 0 | << yuvData->mCbSkip << "," |
871 | 0 | << yuvData->mCrSkip << ", " |
872 | 0 | << yuvData->mYSize.width << "," |
873 | 0 | << yuvData->mYSize.height << ", " |
874 | 0 | << yuvData->mCbCrSize.width << "," |
875 | 0 | << yuvData->mCbCrSize.height << ", " |
876 | 0 | << yuvData->mYStride << "," |
877 | 0 | << yuvData->mCbCrStride; |
878 | 0 | return false; |
879 | 0 | } |
880 | 0 |
|
881 | 0 | gfx::IntSize divisors; |
882 | 0 | if (!GuessDivisors(yuvData->mYSize, yuvData->mCbCrSize, &divisors)) { |
883 | 0 | gfxCriticalError() << "GuessDivisors failed:" |
884 | 0 | << yuvData->mYSize.width << "," |
885 | 0 | << yuvData->mYSize.height << ", " |
886 | 0 | << yuvData->mCbCrSize.width << "," |
887 | 0 | << yuvData->mCbCrSize.height; |
888 | 0 | return false; |
889 | 0 | } |
890 | 0 |
|
891 | 0 | // -- |
892 | 0 |
|
893 | 0 | // RED textures aren't valid in GLES2, and ALPHA textures are not valid in desktop GL Core Profiles. |
894 | 0 | // So use R8 textures on GL3.0+ and GLES3.0+, but LUMINANCE/LUMINANCE/UNSIGNED_BYTE otherwise. |
895 | 0 | GLenum internalFormat; |
896 | 0 | GLenum unpackFormat; |
897 | 0 | if (mGL->IsAtLeast(gl::ContextProfile::OpenGLCore, 300) || |
898 | 0 | mGL->IsAtLeast(gl::ContextProfile::OpenGLES, 300)) |
899 | 0 | { |
900 | 0 | internalFormat = LOCAL_GL_R8; |
901 | 0 | unpackFormat = LOCAL_GL_RED; |
902 | 0 | } else { |
903 | 0 | internalFormat = LOCAL_GL_LUMINANCE; |
904 | 0 | unpackFormat = LOCAL_GL_LUMINANCE; |
905 | 0 | } |
906 | 0 |
|
907 | 0 | // -- |
908 | 0 |
|
909 | 0 | const ScopedSaveMultiTex saveTex(mGL, 3, LOCAL_GL_TEXTURE_2D); |
910 | 0 | const ResetUnpackState reset(mGL); |
911 | 0 | const gfx::IntSize yTexSize(yuvData->mYStride, yuvData->mYSize.height); |
912 | 0 | const gfx::IntSize uvTexSize(yuvData->mCbCrStride, yuvData->mCbCrSize.height); |
913 | 0 |
|
914 | 0 | if (yTexSize != mYuvUploads_YSize || |
915 | 0 | uvTexSize != mYuvUploads_UVSize) |
916 | 0 | { |
917 | 0 | mYuvUploads_YSize = yTexSize; |
918 | 0 | mYuvUploads_UVSize = uvTexSize; |
919 | 0 |
|
920 | 0 | mGL->fActiveTexture(LOCAL_GL_TEXTURE0); |
921 | 0 | mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[0]); |
922 | 0 | mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, internalFormat, |
923 | 0 | yTexSize.width, yTexSize.height, 0, |
924 | 0 | unpackFormat, LOCAL_GL_UNSIGNED_BYTE, nullptr); |
925 | 0 | for (int i = 1; i < 3; i++) { |
926 | 0 | mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + i); |
927 | 0 | mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[i]); |
928 | 0 | mGL->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, internalFormat, |
929 | 0 | uvTexSize.width, uvTexSize.height, 0, |
930 | 0 | unpackFormat, LOCAL_GL_UNSIGNED_BYTE, nullptr); |
931 | 0 | } |
932 | 0 | } |
933 | 0 |
|
934 | 0 | // -- |
935 | 0 |
|
936 | 0 | mGL->fActiveTexture(LOCAL_GL_TEXTURE0); |
937 | 0 | mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[0]); |
938 | 0 | mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0, |
939 | 0 | yTexSize.width, yTexSize.height, |
940 | 0 | unpackFormat, LOCAL_GL_UNSIGNED_BYTE, yuvData->mYChannel); |
941 | 0 | mGL->fActiveTexture(LOCAL_GL_TEXTURE1); |
942 | 0 | mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[1]); |
943 | 0 | mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0, |
944 | 0 | uvTexSize.width, uvTexSize.height, |
945 | 0 | unpackFormat, LOCAL_GL_UNSIGNED_BYTE, yuvData->mCbChannel); |
946 | 0 | mGL->fActiveTexture(LOCAL_GL_TEXTURE2); |
947 | 0 | mGL->fBindTexture(LOCAL_GL_TEXTURE_2D, mYuvUploads[2]); |
948 | 0 | mGL->fTexSubImage2D(LOCAL_GL_TEXTURE_2D, 0, 0, 0, |
949 | 0 | uvTexSize.width, uvTexSize.height, |
950 | 0 | unpackFormat, LOCAL_GL_UNSIGNED_BYTE, yuvData->mCrChannel); |
951 | 0 |
|
952 | 0 | // -- |
953 | 0 |
|
954 | 0 | const auto& clipRect = yuvData->GetPictureRect(); |
955 | 0 | const auto srcOrigin = OriginPos::BottomLeft; |
956 | 0 | const bool yFlip = (destOrigin != srcOrigin); |
957 | 0 |
|
958 | 0 | const DrawBlitProg::BaseArgs baseArgs = { |
959 | 0 | SubRectMat3(clipRect, yTexSize), |
960 | 0 | yFlip, destSize, Nothing() |
961 | 0 | }; |
962 | 0 | const DrawBlitProg::YUVArgs yuvArgs = { |
963 | 0 | SubRectMat3(clipRect, uvTexSize, divisors), |
964 | 0 | yuvData->mYUVColorSpace |
965 | 0 | }; |
966 | 0 | prog->Draw(baseArgs, &yuvArgs); |
967 | 0 | return true; |
968 | 0 | } |
969 | | |
970 | | // ------------------------------------- |
971 | | |
972 | | #ifdef XP_MACOSX |
973 | | bool |
974 | | GLBlitHelper::BlitImage(layers::MacIOSurfaceImage* const srcImage, |
975 | | const gfx::IntSize& destSize, const OriginPos destOrigin) const |
976 | | { |
977 | | MacIOSurface* const iosurf = srcImage->GetSurface(); |
978 | | if (mGL->GetContextType() != GLContextType::CGL) { |
979 | | MOZ_ASSERT(false); |
980 | | return false; |
981 | | } |
982 | | const auto glCGL = static_cast<GLContextCGL*>(mGL); |
983 | | const auto cglContext = glCGL->GetCGLContext(); |
984 | | |
985 | | const auto& srcOrigin = OriginPos::BottomLeft; |
986 | | |
987 | | DrawBlitProg::BaseArgs baseArgs; |
988 | | baseArgs.yFlip = (destOrigin != srcOrigin); |
989 | | baseArgs.destSize = destSize; |
990 | | |
991 | | DrawBlitProg::YUVArgs yuvArgs; |
992 | | yuvArgs.colorSpace = YUVColorSpace::BT601; |
993 | | |
994 | | const DrawBlitProg::YUVArgs* pYuvArgs = nullptr; |
995 | | |
996 | | auto planes = iosurf->GetPlaneCount(); |
997 | | if (!planes) { |
998 | | planes = 1; // Bad API. No cookie. |
999 | | } |
1000 | | |
1001 | | const GLenum texTarget = LOCAL_GL_TEXTURE_RECTANGLE; |
1002 | | const char* const fragHeader = kFragHeader_Tex2DRect; |
1003 | | |
1004 | | const ScopedSaveMultiTex saveTex(mGL, planes, texTarget); |
1005 | | const ScopedTexture tex0(mGL); |
1006 | | const ScopedTexture tex1(mGL); |
1007 | | const ScopedTexture tex2(mGL); |
1008 | | const GLuint texs[3] = { |
1009 | | tex0, |
1010 | | tex1, |
1011 | | tex2 |
1012 | | }; |
1013 | | |
1014 | | const auto pixelFormat = iosurf->GetPixelFormat(); |
1015 | | const auto formatChars = (const char*)&pixelFormat; |
1016 | | const char formatStr[] = { |
1017 | | formatChars[3], |
1018 | | formatChars[2], |
1019 | | formatChars[1], |
1020 | | formatChars[0], |
1021 | | 0 |
1022 | | }; |
1023 | | if (mGL->ShouldSpew()) { |
1024 | | printf_stderr("iosurf format: %s (0x%08x)\n", formatStr, uint32_t(pixelFormat)); |
1025 | | } |
1026 | | |
1027 | | const char* fragBody; |
1028 | | GLenum internalFormats[3] = {0, 0, 0}; |
1029 | | GLenum unpackFormats[3] = {0, 0, 0}; |
1030 | | GLenum unpackTypes[3] = { LOCAL_GL_UNSIGNED_BYTE, |
1031 | | LOCAL_GL_UNSIGNED_BYTE, |
1032 | | LOCAL_GL_UNSIGNED_BYTE }; |
1033 | | switch (planes) { |
1034 | | case 1: |
1035 | | fragBody = kFragBody_RGBA; |
1036 | | internalFormats[0] = LOCAL_GL_RGBA; |
1037 | | unpackFormats[0] = LOCAL_GL_RGBA; |
1038 | | break; |
1039 | | case 2: |
1040 | | fragBody = kFragBody_NV12; |
1041 | | if (mGL->Version() >= 300) { |
1042 | | internalFormats[0] = LOCAL_GL_R8; |
1043 | | unpackFormats[0] = LOCAL_GL_RED; |
1044 | | internalFormats[1] = LOCAL_GL_RG8; |
1045 | | unpackFormats[1] = LOCAL_GL_RG; |
1046 | | } else { |
1047 | | internalFormats[0] = LOCAL_GL_LUMINANCE; |
1048 | | unpackFormats[0] = LOCAL_GL_LUMINANCE; |
1049 | | internalFormats[1] = LOCAL_GL_LUMINANCE_ALPHA; |
1050 | | unpackFormats[1] = LOCAL_GL_LUMINANCE_ALPHA; |
1051 | | } |
1052 | | pYuvArgs = &yuvArgs; |
1053 | | break; |
1054 | | case 3: |
1055 | | fragBody = kFragBody_PlanarYUV; |
1056 | | if (mGL->Version() >= 300) { |
1057 | | internalFormats[0] = LOCAL_GL_R8; |
1058 | | unpackFormats[0] = LOCAL_GL_RED; |
1059 | | } else { |
1060 | | internalFormats[0] = LOCAL_GL_LUMINANCE; |
1061 | | unpackFormats[0] = LOCAL_GL_LUMINANCE; |
1062 | | } |
1063 | | internalFormats[1] = internalFormats[0]; |
1064 | | internalFormats[2] = internalFormats[0]; |
1065 | | unpackFormats[1] = unpackFormats[0]; |
1066 | | unpackFormats[2] = unpackFormats[0]; |
1067 | | pYuvArgs = &yuvArgs; |
1068 | | break; |
1069 | | default: |
1070 | | gfxCriticalError() << "Unexpected plane count: " << planes; |
1071 | | return false; |
1072 | | } |
1073 | | |
1074 | | if (pixelFormat == '2vuy') { |
1075 | | fragBody = kFragBody_CrYCb; |
1076 | | // APPLE_rgb_422 adds RGB_RAW_422_APPLE for `internalFormat`, but only RGB seems |
1077 | | // to work? |
1078 | | internalFormats[0] = LOCAL_GL_RGB; |
1079 | | unpackFormats[0] = LOCAL_GL_RGB_422_APPLE; |
1080 | | unpackTypes[0] = LOCAL_GL_UNSIGNED_SHORT_8_8_APPLE; |
1081 | | pYuvArgs = &yuvArgs; |
1082 | | } |
1083 | | |
1084 | | for (uint32_t p = 0; p < planes; p++) { |
1085 | | mGL->fActiveTexture(LOCAL_GL_TEXTURE0 + p); |
1086 | | mGL->fBindTexture(texTarget, texs[p]); |
1087 | | mGL->TexParams_SetClampNoMips(texTarget); |
1088 | | |
1089 | | const auto width = iosurf->GetDevicePixelWidth(p); |
1090 | | const auto height = iosurf->GetDevicePixelHeight(p); |
1091 | | auto err = iosurf->CGLTexImageIOSurface2D(cglContext, texTarget, |
1092 | | internalFormats[p], width, height, |
1093 | | unpackFormats[p], unpackTypes[p], p); |
1094 | | if (err) { |
1095 | | const nsPrintfCString errStr("CGLTexImageIOSurface2D(context, target, 0x%04x," |
1096 | | " %u, %u, 0x%04x, 0x%04x, iosurfPtr, %u) -> %i", |
1097 | | internalFormats[p], uint32_t(width), |
1098 | | uint32_t(height), unpackFormats[p], |
1099 | | unpackTypes[p], p, err); |
1100 | | gfxCriticalError() << errStr.get() << " (iosurf format: " << formatStr << ")"; |
1101 | | return false; |
1102 | | } |
1103 | | |
1104 | | if (p == 0) { |
1105 | | baseArgs.texMatrix0 = SubRectMat3(0, 0, width, height); |
1106 | | yuvArgs.texMatrix1 = SubRectMat3(0, 0, width / 2.0, height / 2.0); |
1107 | | } |
1108 | | } |
1109 | | |
1110 | | const auto& prog = GetDrawBlitProg({fragHeader, fragBody}); |
1111 | | if (!prog) |
1112 | | return false; |
1113 | | |
1114 | | prog->Draw(baseArgs, pYuvArgs); |
1115 | | return true; |
1116 | | } |
1117 | | #endif |
1118 | | |
1119 | | // ----------------------------------------------------------------------------- |
1120 | | |
1121 | | void |
1122 | | GLBlitHelper::DrawBlitTextureToFramebuffer(const GLuint srcTex, |
1123 | | const gfx::IntSize& srcSize, |
1124 | | const gfx::IntSize& destSize, |
1125 | | const GLenum srcTarget) const |
1126 | 0 | { |
1127 | 0 | const char* fragHeader; |
1128 | 0 | Mat3 texMatrix0; |
1129 | 0 | switch (srcTarget) { |
1130 | 0 | case LOCAL_GL_TEXTURE_2D: |
1131 | 0 | fragHeader = kFragHeader_Tex2D; |
1132 | 0 | texMatrix0 = Mat3::I(); |
1133 | 0 | break; |
1134 | 0 | case LOCAL_GL_TEXTURE_RECTANGLE_ARB: |
1135 | 0 | fragHeader = kFragHeader_Tex2DRect; |
1136 | 0 | texMatrix0 = SubRectMat3(0, 0, srcSize.width, srcSize.height); |
1137 | 0 | break; |
1138 | 0 | default: |
1139 | 0 | gfxCriticalError() << "Unexpected srcTarget: " << srcTarget; |
1140 | 0 | return; |
1141 | 0 | } |
1142 | 0 | const auto& prog = GetDrawBlitProg({ fragHeader, kFragBody_RGBA}); |
1143 | 0 | MOZ_ASSERT(prog); |
1144 | 0 |
|
1145 | 0 | const ScopedSaveMultiTex saveTex(mGL, 1, srcTarget); |
1146 | 0 | mGL->fBindTexture(srcTarget, srcTex); |
1147 | 0 |
|
1148 | 0 | const bool yFlip = false; |
1149 | 0 | const DrawBlitProg::BaseArgs baseArgs = { texMatrix0, yFlip, destSize, Nothing() }; |
1150 | 0 | prog->Draw(baseArgs); |
1151 | 0 | } |
1152 | | |
1153 | | // ----------------------------------------------------------------------------- |
1154 | | |
1155 | | void |
1156 | | GLBlitHelper::BlitFramebuffer(const gfx::IntSize& srcSize, |
1157 | | const gfx::IntSize& destSize, |
1158 | | GLuint filter) const |
1159 | 0 | { |
1160 | 0 | MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit)); |
1161 | 0 |
|
1162 | 0 | const ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false); |
1163 | 0 | mGL->fBlitFramebuffer(0, 0, srcSize.width, srcSize.height, |
1164 | 0 | 0, 0, destSize.width, destSize.height, |
1165 | 0 | LOCAL_GL_COLOR_BUFFER_BIT, |
1166 | 0 | filter); |
1167 | 0 | } |
1168 | | |
1169 | | // -- |
1170 | | |
1171 | | void |
1172 | | GLBlitHelper::BlitFramebufferToFramebuffer(const GLuint srcFB, const GLuint destFB, |
1173 | | const gfx::IntSize& srcSize, |
1174 | | const gfx::IntSize& destSize, |
1175 | | GLuint filter) const |
1176 | 0 | { |
1177 | 0 | MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit)); |
1178 | 0 | MOZ_ASSERT(!srcFB || mGL->fIsFramebuffer(srcFB)); |
1179 | 0 | MOZ_ASSERT(!destFB || mGL->fIsFramebuffer(destFB)); |
1180 | 0 |
|
1181 | 0 | const ScopedBindFramebuffer boundFB(mGL); |
1182 | 0 | mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, srcFB); |
1183 | 0 | mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destFB); |
1184 | 0 |
|
1185 | 0 | BlitFramebuffer(srcSize, destSize, filter); |
1186 | 0 | } |
1187 | | |
1188 | | void |
1189 | | GLBlitHelper::BlitTextureToFramebuffer(GLuint srcTex, const gfx::IntSize& srcSize, |
1190 | | const gfx::IntSize& destSize, |
1191 | | GLenum srcTarget) const |
1192 | 0 | { |
1193 | 0 | MOZ_ASSERT(mGL->fIsTexture(srcTex)); |
1194 | 0 |
|
1195 | 0 | if (mGL->IsSupported(GLFeature::framebuffer_blit)) { |
1196 | 0 | const ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget); |
1197 | 0 | const ScopedBindFramebuffer bindFB(mGL); |
1198 | 0 | mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, srcWrapper.FB()); |
1199 | 0 | BlitFramebuffer(srcSize, destSize); |
1200 | 0 | return; |
1201 | 0 | } |
1202 | 0 |
|
1203 | 0 | DrawBlitTextureToFramebuffer(srcTex, srcSize, destSize, srcTarget); |
1204 | 0 | } |
1205 | | |
1206 | | void |
1207 | | GLBlitHelper::BlitFramebufferToTexture(GLuint destTex, |
1208 | | const gfx::IntSize& srcSize, |
1209 | | const gfx::IntSize& destSize, |
1210 | | GLenum destTarget) const |
1211 | 0 | { |
1212 | 0 | MOZ_ASSERT(mGL->fIsTexture(destTex)); |
1213 | 0 |
|
1214 | 0 | if (mGL->IsSupported(GLFeature::framebuffer_blit)) { |
1215 | 0 | const ScopedFramebufferForTexture destWrapper(mGL, destTex, destTarget); |
1216 | 0 | const ScopedBindFramebuffer bindFB(mGL); |
1217 | 0 | mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER, destWrapper.FB()); |
1218 | 0 | BlitFramebuffer(srcSize, destSize); |
1219 | 0 | return; |
1220 | 0 | } |
1221 | 0 |
|
1222 | 0 | ScopedBindTexture autoTex(mGL, destTex, destTarget); |
1223 | 0 | ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false); |
1224 | 0 | mGL->fCopyTexSubImage2D(destTarget, 0, |
1225 | 0 | 0, 0, |
1226 | 0 | 0, 0, |
1227 | 0 | srcSize.width, srcSize.height); |
1228 | 0 | } |
1229 | | |
1230 | | void |
1231 | | GLBlitHelper::BlitTextureToTexture(GLuint srcTex, GLuint destTex, |
1232 | | const gfx::IntSize& srcSize, |
1233 | | const gfx::IntSize& destSize, |
1234 | | GLenum srcTarget, GLenum destTarget) const |
1235 | 0 | { |
1236 | 0 | MOZ_ASSERT(mGL->fIsTexture(srcTex)); |
1237 | 0 | MOZ_ASSERT(mGL->fIsTexture(destTex)); |
1238 | 0 |
|
1239 | 0 | // Start down the CopyTexSubImage path, not the DrawBlit path. |
1240 | 0 | const ScopedFramebufferForTexture srcWrapper(mGL, srcTex, srcTarget); |
1241 | 0 | const ScopedBindFramebuffer bindFB(mGL, srcWrapper.FB()); |
1242 | 0 | BlitFramebufferToTexture(destTex, srcSize, destSize, destTarget); |
1243 | 0 | } |
1244 | | |
1245 | | } // namespace gl |
1246 | | } // namespace mozilla |