1 //
2 // Copyright 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // BlitGL.cpp: Implements the BlitGL class, a helper for blitting textures
8
9 #include "libANGLE/renderer/gl/BlitGL.h"
10
11 #include "common/FixedVector.h"
12 #include "common/utilities.h"
13 #include "common/vector_utils.h"
14 #include "image_util/copyimage.h"
15 #include "libANGLE/Context.h"
16 #include "libANGLE/Framebuffer.h"
17 #include "libANGLE/formatutils.h"
18 #include "libANGLE/renderer/Format.h"
19 #include "libANGLE/renderer/gl/ContextGL.h"
20 #include "libANGLE/renderer/gl/FramebufferGL.h"
21 #include "libANGLE/renderer/gl/FunctionsGL.h"
22 #include "libANGLE/renderer/gl/RenderbufferGL.h"
23 #include "libANGLE/renderer/gl/StateManagerGL.h"
24 #include "libANGLE/renderer/gl/TextureGL.h"
25 #include "libANGLE/renderer/gl/formatutilsgl.h"
26 #include "libANGLE/renderer/gl/renderergl_utils.h"
27 #include "libANGLE/renderer/renderer_utils.h"
28 #include "platform/FeaturesGL.h"
29
30 using angle::Vector2;
31
32 namespace rx
33 {
34
35 namespace
36 {
37
CheckCompileStatus(const gl::Context * context,const rx::FunctionsGL * functions,GLuint shader)38 angle::Result CheckCompileStatus(const gl::Context *context,
39 const rx::FunctionsGL *functions,
40 GLuint shader)
41 {
42 GLint compileStatus = GL_FALSE;
43 ANGLE_GL_TRY(context, functions->getShaderiv(shader, GL_COMPILE_STATUS, &compileStatus));
44
45 ASSERT(compileStatus == GL_TRUE);
46 ANGLE_CHECK(GetImplAs<ContextGL>(context), compileStatus == GL_TRUE,
47 "Failed to compile internal blit shader.", GL_OUT_OF_MEMORY);
48
49 return angle::Result::Continue;
50 }
51
CheckLinkStatus(const gl::Context * context,const rx::FunctionsGL * functions,GLuint program)52 angle::Result CheckLinkStatus(const gl::Context *context,
53 const rx::FunctionsGL *functions,
54 GLuint program)
55 {
56 GLint linkStatus = GL_FALSE;
57 ANGLE_GL_TRY(context, functions->getProgramiv(program, GL_LINK_STATUS, &linkStatus));
58 ASSERT(linkStatus == GL_TRUE);
59 ANGLE_CHECK(GetImplAs<ContextGL>(context), linkStatus == GL_TRUE,
60 "Failed to link internal blit program.", GL_OUT_OF_MEMORY);
61
62 return angle::Result::Continue;
63 }
64
65 class ScopedGLState : angle::NonCopyable
66 {
67 public:
68 enum
69 {
70 KEEP_SCISSOR = 1,
71 };
72
ScopedGLState()73 ScopedGLState() {}
74
~ScopedGLState()75 ~ScopedGLState() { ASSERT(mExited); }
76
enter(const gl::Context * context,gl::Rectangle viewport,int keepState=0)77 angle::Result enter(const gl::Context *context, gl::Rectangle viewport, int keepState = 0)
78 {
79 ContextGL *contextGL = GetImplAs<ContextGL>(context);
80 StateManagerGL *stateManager = contextGL->getStateManager();
81
82 if (!(keepState & KEEP_SCISSOR))
83 {
84 stateManager->setScissorTestEnabled(false);
85 }
86 stateManager->setViewport(viewport);
87 stateManager->setDepthRange(0.0f, 1.0f);
88 stateManager->setBlendEnabled(false);
89 stateManager->setColorMask(true, true, true, true);
90 stateManager->setSampleAlphaToCoverageEnabled(false);
91 stateManager->setSampleCoverageEnabled(false);
92 stateManager->setDepthTestEnabled(false);
93 stateManager->setStencilTestEnabled(false);
94 stateManager->setCullFaceEnabled(false);
95 stateManager->setPolygonOffsetFillEnabled(false);
96 stateManager->setRasterizerDiscardEnabled(false);
97
98 stateManager->pauseTransformFeedback();
99 return stateManager->pauseAllQueries(context);
100 }
101
exit(const gl::Context * context)102 angle::Result exit(const gl::Context *context)
103 {
104 mExited = true;
105
106 ContextGL *contextGL = GetImplAs<ContextGL>(context);
107 StateManagerGL *stateManager = contextGL->getStateManager();
108
109 // XFB resuming will be done automatically
110 return stateManager->resumeAllQueries(context);
111 }
112
willUseTextureUnit(const gl::Context * context,int unit)113 void willUseTextureUnit(const gl::Context *context, int unit)
114 {
115 ContextGL *contextGL = GetImplAs<ContextGL>(context);
116
117 if (contextGL->getFunctions()->bindSampler)
118 {
119 contextGL->getStateManager()->bindSampler(unit, 0);
120 }
121 }
122
123 private:
124 bool mExited = false;
125 };
126
SetClearState(StateManagerGL * stateManager,bool colorClear,bool depthClear,bool stencilClear,GLbitfield * outClearMask)127 angle::Result SetClearState(StateManagerGL *stateManager,
128 bool colorClear,
129 bool depthClear,
130 bool stencilClear,
131 GLbitfield *outClearMask)
132 {
133 *outClearMask = 0;
134 if (colorClear)
135 {
136 stateManager->setClearColor(gl::ColorF(0.0f, 0.0f, 0.0f, 0.0f));
137 stateManager->setColorMask(true, true, true, true);
138 *outClearMask |= GL_COLOR_BUFFER_BIT;
139 }
140 if (depthClear)
141 {
142 stateManager->setDepthMask(true);
143 stateManager->setClearDepth(1.0f);
144 *outClearMask |= GL_DEPTH_BUFFER_BIT;
145 }
146 if (stencilClear)
147 {
148 stateManager->setClearStencil(0);
149 *outClearMask |= GL_STENCIL_BUFFER_BIT;
150 }
151
152 stateManager->setScissorTestEnabled(false);
153
154 return angle::Result::Continue;
155 }
156
157 using ClearBindTargetVector = angle::FixedVector<GLenum, 3>;
158
PrepareForClear(StateManagerGL * stateManager,GLenum sizedInternalFormat,ClearBindTargetVector * outBindtargets,ClearBindTargetVector * outUnbindTargets,GLbitfield * outClearMask)159 angle::Result PrepareForClear(StateManagerGL *stateManager,
160 GLenum sizedInternalFormat,
161 ClearBindTargetVector *outBindtargets,
162 ClearBindTargetVector *outUnbindTargets,
163 GLbitfield *outClearMask)
164 {
165 const gl::InternalFormat &internalFormatInfo =
166 gl::GetSizedInternalFormatInfo(sizedInternalFormat);
167 bool bindDepth = internalFormatInfo.depthBits > 0;
168 bool bindStencil = internalFormatInfo.stencilBits > 0;
169 bool bindColor = !bindDepth && !bindStencil;
170
171 outBindtargets->clear();
172 if (bindColor)
173 {
174 outBindtargets->push_back(GL_COLOR_ATTACHMENT0);
175 }
176 else
177 {
178 outUnbindTargets->push_back(GL_COLOR_ATTACHMENT0);
179 }
180 if (bindDepth)
181 {
182 outBindtargets->push_back(GL_DEPTH_ATTACHMENT);
183 }
184 else
185 {
186 outUnbindTargets->push_back(GL_DEPTH_ATTACHMENT);
187 }
188 if (bindStencil)
189 {
190 outBindtargets->push_back(GL_STENCIL_ATTACHMENT);
191 }
192 else
193 {
194 outUnbindTargets->push_back(GL_STENCIL_ATTACHMENT);
195 }
196
197 ANGLE_TRY(SetClearState(stateManager, bindColor, bindDepth, bindStencil, outClearMask));
198
199 return angle::Result::Continue;
200 }
201
UnbindAttachments(const gl::Context * context,const FunctionsGL * functions,GLenum framebufferTarget,const ClearBindTargetVector & bindTargets)202 angle::Result UnbindAttachments(const gl::Context *context,
203 const FunctionsGL *functions,
204 GLenum framebufferTarget,
205 const ClearBindTargetVector &bindTargets)
206 {
207 for (GLenum bindTarget : bindTargets)
208 {
209 ANGLE_GL_TRY(context, functions->framebufferRenderbuffer(framebufferTarget, bindTarget,
210 GL_RENDERBUFFER, 0));
211 }
212 return angle::Result::Continue;
213 }
214
215 } // anonymous namespace
216
BlitGL(const FunctionsGL * functions,const angle::FeaturesGL & features,StateManagerGL * stateManager)217 BlitGL::BlitGL(const FunctionsGL *functions,
218 const angle::FeaturesGL &features,
219 StateManagerGL *stateManager)
220 : mFunctions(functions),
221 mFeatures(features),
222 mStateManager(stateManager),
223 mScratchFBO(0),
224 mVAO(0),
225 mVertexBuffer(0)
226 {
227 for (size_t i = 0; i < ArraySize(mScratchTextures); i++)
228 {
229 mScratchTextures[i] = 0;
230 }
231
232 ASSERT(mFunctions);
233 ASSERT(mStateManager);
234 }
235
~BlitGL()236 BlitGL::~BlitGL()
237 {
238 for (const auto &blitProgram : mBlitPrograms)
239 {
240 mStateManager->deleteProgram(blitProgram.second.program);
241 }
242 mBlitPrograms.clear();
243
244 for (size_t i = 0; i < ArraySize(mScratchTextures); i++)
245 {
246 if (mScratchTextures[i] != 0)
247 {
248 mStateManager->deleteTexture(mScratchTextures[i]);
249 mScratchTextures[i] = 0;
250 }
251 }
252
253 if (mScratchFBO != 0)
254 {
255 mStateManager->deleteFramebuffer(mScratchFBO);
256 mScratchFBO = 0;
257 }
258
259 if (mVAO != 0)
260 {
261 mStateManager->deleteVertexArray(mVAO);
262 mVAO = 0;
263 }
264 }
265
copyImageToLUMAWorkaroundTexture(const gl::Context * context,GLuint texture,gl::TextureType textureType,gl::TextureTarget target,GLenum lumaFormat,size_t level,const gl::Rectangle & sourceArea,GLenum internalFormat,gl::Framebuffer * source)266 angle::Result BlitGL::copyImageToLUMAWorkaroundTexture(const gl::Context *context,
267 GLuint texture,
268 gl::TextureType textureType,
269 gl::TextureTarget target,
270 GLenum lumaFormat,
271 size_t level,
272 const gl::Rectangle &sourceArea,
273 GLenum internalFormat,
274 gl::Framebuffer *source)
275 {
276 mStateManager->bindTexture(textureType, texture);
277
278 // Allocate the texture memory
279 GLenum format = gl::GetUnsizedFormat(internalFormat);
280 GLenum readType = source->getImplementationColorReadType(context);
281
282 gl::PixelUnpackState unpack;
283 mStateManager->setPixelUnpackState(unpack);
284 mStateManager->setPixelUnpackBuffer(
285 context->getState().getTargetBuffer(gl::BufferBinding::PixelUnpack));
286 ANGLE_GL_TRY_ALWAYS_CHECK(
287 context,
288 mFunctions->texImage2D(ToGLenum(target), static_cast<GLint>(level), internalFormat,
289 sourceArea.width, sourceArea.height, 0, format, readType, nullptr));
290
291 return copySubImageToLUMAWorkaroundTexture(context, texture, textureType, target, lumaFormat,
292 level, gl::Offset(0, 0, 0), sourceArea, source);
293 }
294
copySubImageToLUMAWorkaroundTexture(const gl::Context * context,GLuint texture,gl::TextureType textureType,gl::TextureTarget target,GLenum lumaFormat,size_t level,const gl::Offset & destOffset,const gl::Rectangle & sourceArea,gl::Framebuffer * source)295 angle::Result BlitGL::copySubImageToLUMAWorkaroundTexture(const gl::Context *context,
296 GLuint texture,
297 gl::TextureType textureType,
298 gl::TextureTarget target,
299 GLenum lumaFormat,
300 size_t level,
301 const gl::Offset &destOffset,
302 const gl::Rectangle &sourceArea,
303 gl::Framebuffer *source)
304 {
305 ANGLE_TRY(initializeResources(context));
306
307 BlitProgram *blitProgram = nullptr;
308 ANGLE_TRY(getBlitProgram(context, gl::TextureType::_2D, GL_FLOAT, GL_FLOAT, &blitProgram));
309
310 // Blit the framebuffer to the first scratch texture
311 const FramebufferGL *sourceFramebufferGL = GetImplAs<FramebufferGL>(source);
312 mStateManager->bindFramebuffer(GL_FRAMEBUFFER, sourceFramebufferGL->getFramebufferID());
313
314 GLenum readFormat = source->getImplementationColorReadFormat(context);
315 GLenum readType = source->getImplementationColorReadType(context);
316
317 nativegl::CopyTexImageImageFormat copyTexImageFormat =
318 nativegl::GetCopyTexImageImageFormat(mFunctions, mFeatures, readFormat, readType);
319
320 mStateManager->bindTexture(gl::TextureType::_2D, mScratchTextures[0]);
321 ANGLE_GL_TRY_ALWAYS_CHECK(
322 context, mFunctions->copyTexImage2D(GL_TEXTURE_2D, 0, copyTexImageFormat.internalFormat,
323 sourceArea.x, sourceArea.y, sourceArea.width,
324 sourceArea.height, 0));
325
326 // Set the swizzle of the scratch texture so that the channels sample into the correct emulated
327 // LUMA channels.
328 GLint swizzle[4] = {
329 (lumaFormat == GL_ALPHA) ? GL_ALPHA : GL_RED,
330 (lumaFormat == GL_LUMINANCE_ALPHA) ? GL_ALPHA : GL_ZERO,
331 GL_ZERO,
332 GL_ZERO,
333 };
334 ANGLE_GL_TRY(context,
335 mFunctions->texParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle));
336
337 // Make a temporary framebuffer using the second scratch texture to render the swizzled result
338 // to.
339 mStateManager->bindTexture(gl::TextureType::_2D, mScratchTextures[1]);
340 ANGLE_GL_TRY_ALWAYS_CHECK(
341 context, mFunctions->texImage2D(GL_TEXTURE_2D, 0, copyTexImageFormat.internalFormat,
342 sourceArea.width, sourceArea.height, 0,
343 gl::GetUnsizedFormat(copyTexImageFormat.internalFormat),
344 readType, nullptr));
345
346 mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
347 ANGLE_GL_TRY(context, mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
348 GL_TEXTURE_2D, mScratchTextures[1], 0));
349
350 // Render to the destination texture, sampling from the scratch texture
351 ScopedGLState scopedState;
352 ANGLE_TRY(scopedState.enter(context, gl::Rectangle(0, 0, sourceArea.width, sourceArea.height)));
353 scopedState.willUseTextureUnit(context, 0);
354
355 ANGLE_TRY(setScratchTextureParameter(context, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
356 ANGLE_TRY(setScratchTextureParameter(context, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
357
358 mStateManager->activeTexture(0);
359 mStateManager->bindTexture(gl::TextureType::_2D, mScratchTextures[0]);
360
361 mStateManager->useProgram(blitProgram->program);
362 ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->sourceTextureLocation, 0));
363 ANGLE_GL_TRY(context, mFunctions->uniform2f(blitProgram->scaleLocation, 1.0, 1.0));
364 ANGLE_GL_TRY(context, mFunctions->uniform2f(blitProgram->offsetLocation, 0.0, 0.0));
365 ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->multiplyAlphaLocation, 0));
366 ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->unMultiplyAlphaLocation, 0));
367
368 mStateManager->bindVertexArray(mVAO, 0);
369 ANGLE_GL_TRY(context, mFunctions->drawArrays(GL_TRIANGLES, 0, 3));
370
371 // Copy the swizzled texture to the destination texture
372 mStateManager->bindTexture(textureType, texture);
373
374 if (nativegl::UseTexImage3D(textureType))
375 {
376 ANGLE_GL_TRY(context,
377 mFunctions->copyTexSubImage3D(ToGLenum(target), static_cast<GLint>(level),
378 destOffset.x, destOffset.y, destOffset.z, 0, 0,
379 sourceArea.width, sourceArea.height));
380 }
381 else
382 {
383 ASSERT(nativegl::UseTexImage2D(textureType));
384 ANGLE_GL_TRY(context, mFunctions->copyTexSubImage2D(
385 ToGLenum(target), static_cast<GLint>(level), destOffset.x,
386 destOffset.y, 0, 0, sourceArea.width, sourceArea.height));
387 }
388
389 // Finally orphan the scratch textures so they can be GCed by the driver.
390 ANGLE_TRY(orphanScratchTextures(context));
391
392 ANGLE_TRY(scopedState.exit(context));
393 return angle::Result::Continue;
394 }
395
blitColorBufferWithShader(const gl::Context * context,const gl::Framebuffer * source,const gl::Framebuffer * dest,const gl::Rectangle & sourceAreaIn,const gl::Rectangle & destAreaIn,GLenum filter)396 angle::Result BlitGL::blitColorBufferWithShader(const gl::Context *context,
397 const gl::Framebuffer *source,
398 const gl::Framebuffer *dest,
399 const gl::Rectangle &sourceAreaIn,
400 const gl::Rectangle &destAreaIn,
401 GLenum filter)
402 {
403 ANGLE_TRY(initializeResources(context));
404
405 BlitProgram *blitProgram = nullptr;
406 ANGLE_TRY(getBlitProgram(context, gl::TextureType::_2D, GL_FLOAT, GL_FLOAT, &blitProgram));
407
408 // We'll keep things simple by removing reversed coordinates from the rectangles. In the end
409 // we'll apply the reversal to the source texture coordinates if needed. The destination
410 // rectangle will be set to the gl viewport, which can't be reversed.
411 bool reverseX = sourceAreaIn.isReversedX() != destAreaIn.isReversedX();
412 bool reverseY = sourceAreaIn.isReversedY() != destAreaIn.isReversedY();
413 gl::Rectangle sourceArea = sourceAreaIn.removeReversal();
414 gl::Rectangle destArea = destAreaIn.removeReversal();
415
416 const gl::FramebufferAttachment *readAttachment = source->getReadColorAttachment();
417 ASSERT(readAttachment->getSamples() <= 1);
418
419 // Compute the part of the source that will be sampled.
420 gl::Rectangle inBoundsSource;
421 {
422 gl::Extents sourceSize = readAttachment->getSize();
423 gl::Rectangle sourceBounds(0, 0, sourceSize.width, sourceSize.height);
424 if (!gl::ClipRectangle(sourceArea, sourceBounds, &inBoundsSource))
425 {
426 // Early out when the sampled part is empty as the blit will be a noop,
427 // and it prevents a division by zero in later computations.
428 return angle::Result::Continue;
429 }
430 }
431
432 // The blit will be emulated by getting the source of the blit in a texture and sampling it
433 // with CLAMP_TO_EDGE.
434
435 GLuint textureId;
436
437 // TODO(cwallez) once texture dirty bits are landed, reuse attached texture instead of using
438 // CopyTexImage2D
439 {
440 textureId = mScratchTextures[0];
441
442 GLenum format = readAttachment->getFormat().info->internalFormat;
443 const FramebufferGL *sourceGL = GetImplAs<FramebufferGL>(source);
444 mStateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, sourceGL->getFramebufferID());
445 mStateManager->bindTexture(gl::TextureType::_2D, textureId);
446
447 ANGLE_GL_TRY_ALWAYS_CHECK(
448 context,
449 mFunctions->copyTexImage2D(GL_TEXTURE_2D, 0, format, inBoundsSource.x, inBoundsSource.y,
450 inBoundsSource.width, inBoundsSource.height, 0));
451
452 // Translate sourceArea to be relative to the copied image.
453 sourceArea.x -= inBoundsSource.x;
454 sourceArea.y -= inBoundsSource.y;
455
456 ANGLE_TRY(setScratchTextureParameter(context, GL_TEXTURE_MIN_FILTER, filter));
457 ANGLE_TRY(setScratchTextureParameter(context, GL_TEXTURE_MAG_FILTER, filter));
458 ANGLE_TRY(setScratchTextureParameter(context, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
459 ANGLE_TRY(setScratchTextureParameter(context, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
460 }
461
462 // Transform the source area to the texture coordinate space (where 0.0 and 1.0 correspond to
463 // the edges of the texture).
464 Vector2 texCoordOffset(
465 static_cast<float>(sourceArea.x) / static_cast<float>(inBoundsSource.width),
466 static_cast<float>(sourceArea.y) / static_cast<float>(inBoundsSource.height));
467 // texCoordScale is equal to the size of the source area in texture coordinates.
468 Vector2 texCoordScale(
469 static_cast<float>(sourceArea.width) / static_cast<float>(inBoundsSource.width),
470 static_cast<float>(sourceArea.height) / static_cast<float>(inBoundsSource.height));
471
472 if (reverseX)
473 {
474 texCoordOffset.x() = texCoordOffset.x() + texCoordScale.x();
475 texCoordScale.x() = -texCoordScale.x();
476 }
477 if (reverseY)
478 {
479 texCoordOffset.y() = texCoordOffset.y() + texCoordScale.y();
480 texCoordScale.y() = -texCoordScale.y();
481 }
482
483 // Reset all the state except scissor and use the viewport to draw exactly to the destination
484 // rectangle
485 ScopedGLState scopedState;
486 ANGLE_TRY(scopedState.enter(context, destArea, ScopedGLState::KEEP_SCISSOR));
487 scopedState.willUseTextureUnit(context, 0);
488
489 // Set uniforms
490 mStateManager->activeTexture(0);
491 mStateManager->bindTexture(gl::TextureType::_2D, textureId);
492
493 mStateManager->useProgram(blitProgram->program);
494 ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->sourceTextureLocation, 0));
495 ANGLE_GL_TRY(context, mFunctions->uniform2f(blitProgram->scaleLocation, texCoordScale.x(),
496 texCoordScale.y()));
497 ANGLE_GL_TRY(context, mFunctions->uniform2f(blitProgram->offsetLocation, texCoordOffset.x(),
498 texCoordOffset.y()));
499 ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->multiplyAlphaLocation, 0));
500 ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->unMultiplyAlphaLocation, 0));
501
502 const FramebufferGL *destGL = GetImplAs<FramebufferGL>(dest);
503 mStateManager->bindFramebuffer(GL_DRAW_FRAMEBUFFER, destGL->getFramebufferID());
504
505 mStateManager->bindVertexArray(mVAO, 0);
506 ANGLE_GL_TRY(context, mFunctions->drawArrays(GL_TRIANGLES, 0, 3));
507
508 ANGLE_TRY(scopedState.exit(context));
509 return angle::Result::Continue;
510 }
511
copySubTexture(const gl::Context * context,TextureGL * source,size_t sourceLevel,GLenum sourceComponentType,GLuint destID,gl::TextureTarget destTarget,size_t destLevel,GLenum destComponentType,const gl::Extents & sourceSize,const gl::Rectangle & sourceArea,const gl::Offset & destOffset,bool needsLumaWorkaround,GLenum lumaFormat,bool unpackFlipY,bool unpackPremultiplyAlpha,bool unpackUnmultiplyAlpha,bool * copySucceededOut)512 angle::Result BlitGL::copySubTexture(const gl::Context *context,
513 TextureGL *source,
514 size_t sourceLevel,
515 GLenum sourceComponentType,
516 GLuint destID,
517 gl::TextureTarget destTarget,
518 size_t destLevel,
519 GLenum destComponentType,
520 const gl::Extents &sourceSize,
521 const gl::Rectangle &sourceArea,
522 const gl::Offset &destOffset,
523 bool needsLumaWorkaround,
524 GLenum lumaFormat,
525 bool unpackFlipY,
526 bool unpackPremultiplyAlpha,
527 bool unpackUnmultiplyAlpha,
528 bool *copySucceededOut)
529 {
530 ASSERT(source->getType() == gl::TextureType::_2D ||
531 source->getType() == gl::TextureType::External ||
532 source->getType() == gl::TextureType::Rectangle);
533 ANGLE_TRY(initializeResources(context));
534
535 // Make sure the destination texture can be rendered to before setting anything else up. Some
536 // cube maps may not be renderable until all faces have been filled.
537 mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
538 ANGLE_GL_TRY(context, mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
539 ToGLenum(destTarget), destID,
540 static_cast<GLint>(destLevel)));
541 GLenum status = ANGLE_GL_TRY(context, mFunctions->checkFramebufferStatus(GL_FRAMEBUFFER));
542 if (status != GL_FRAMEBUFFER_COMPLETE)
543 {
544 *copySucceededOut = false;
545 return angle::Result::Continue;
546 }
547
548 BlitProgram *blitProgram = nullptr;
549 ANGLE_TRY(getBlitProgram(context, source->getType(), sourceComponentType, destComponentType,
550 &blitProgram));
551
552 // Setup the source texture
553 if (needsLumaWorkaround)
554 {
555 GLint luminance = (lumaFormat == GL_ALPHA) ? GL_ZERO : GL_RED;
556
557 GLint alpha = GL_RED;
558 if (lumaFormat == GL_LUMINANCE)
559 {
560 alpha = GL_ONE;
561 }
562 else if (lumaFormat == GL_LUMINANCE_ALPHA)
563 {
564 alpha = GL_GREEN;
565 }
566 else
567 {
568 ASSERT(lumaFormat == GL_ALPHA);
569 }
570
571 GLint swizzle[4] = {luminance, luminance, luminance, alpha};
572 ANGLE_TRY(source->setSwizzle(context, swizzle));
573 }
574 ANGLE_TRY(source->setMinFilter(context, GL_NEAREST));
575 ANGLE_TRY(source->setMagFilter(context, GL_NEAREST));
576 ANGLE_TRY(source->setBaseLevel(context, static_cast<GLuint>(sourceLevel)));
577
578 // Render to the destination texture, sampling from the source texture
579 ScopedGLState scopedState;
580 ANGLE_TRY(scopedState.enter(
581 context, gl::Rectangle(destOffset.x, destOffset.y, sourceArea.width, sourceArea.height)));
582 scopedState.willUseTextureUnit(context, 0);
583
584 mStateManager->activeTexture(0);
585 mStateManager->bindTexture(source->getType(), source->getTextureID());
586
587 Vector2 scale(sourceArea.width, sourceArea.height);
588 Vector2 offset(sourceArea.x, sourceArea.y);
589 if (source->getType() != gl::TextureType::Rectangle)
590 {
591 scale.x() /= static_cast<float>(sourceSize.width);
592 scale.y() /= static_cast<float>(sourceSize.height);
593 offset.x() /= static_cast<float>(sourceSize.width);
594 offset.y() /= static_cast<float>(sourceSize.height);
595 }
596 if (unpackFlipY)
597 {
598 offset.y() += scale.y();
599 scale.y() = -scale.y();
600 }
601
602 mStateManager->useProgram(blitProgram->program);
603 ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->sourceTextureLocation, 0));
604 ANGLE_GL_TRY(context, mFunctions->uniform2f(blitProgram->scaleLocation, scale.x(), scale.y()));
605 ANGLE_GL_TRY(context,
606 mFunctions->uniform2f(blitProgram->offsetLocation, offset.x(), offset.y()));
607 if (unpackPremultiplyAlpha == unpackUnmultiplyAlpha)
608 {
609 ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->multiplyAlphaLocation, 0));
610 ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->unMultiplyAlphaLocation, 0));
611 }
612 else
613 {
614 ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->multiplyAlphaLocation,
615 unpackPremultiplyAlpha));
616 ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->unMultiplyAlphaLocation,
617 unpackUnmultiplyAlpha));
618 }
619
620 mStateManager->bindVertexArray(mVAO, 0);
621 ANGLE_GL_TRY(context, mFunctions->drawArrays(GL_TRIANGLES, 0, 3));
622
623 *copySucceededOut = true;
624 ANGLE_TRY(scopedState.exit(context));
625 return angle::Result::Continue;
626 }
627
copySubTextureCPUReadback(const gl::Context * context,TextureGL * source,size_t sourceLevel,GLenum sourceSizedInternalFormat,TextureGL * dest,gl::TextureTarget destTarget,size_t destLevel,GLenum destFormat,GLenum destType,const gl::Extents & sourceSize,const gl::Rectangle & sourceArea,const gl::Offset & destOffset,bool needsLumaWorkaround,GLenum lumaFormat,bool unpackFlipY,bool unpackPremultiplyAlpha,bool unpackUnmultiplyAlpha)628 angle::Result BlitGL::copySubTextureCPUReadback(const gl::Context *context,
629 TextureGL *source,
630 size_t sourceLevel,
631 GLenum sourceSizedInternalFormat,
632 TextureGL *dest,
633 gl::TextureTarget destTarget,
634 size_t destLevel,
635 GLenum destFormat,
636 GLenum destType,
637 const gl::Extents &sourceSize,
638 const gl::Rectangle &sourceArea,
639 const gl::Offset &destOffset,
640 bool needsLumaWorkaround,
641 GLenum lumaFormat,
642 bool unpackFlipY,
643 bool unpackPremultiplyAlpha,
644 bool unpackUnmultiplyAlpha)
645 {
646 ANGLE_TRY(initializeResources(context));
647
648 ContextGL *contextGL = GetImplAs<ContextGL>(context);
649
650 ASSERT(source->getType() == gl::TextureType::_2D ||
651 source->getType() == gl::TextureType::External ||
652 source->getType() == gl::TextureType::Rectangle);
653 const auto &destInternalFormatInfo = gl::GetInternalFormatInfo(destFormat, destType);
654 const gl::InternalFormat &sourceInternalFormatInfo =
655 gl::GetSizedInternalFormatInfo(sourceSizedInternalFormat);
656
657 gl::Rectangle readPixelsArea = sourceArea;
658
659 mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
660 ANGLE_GL_TRY(context, mFunctions->framebufferTexture2D(
661 GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, ToGLenum(source->getType()),
662 source->getTextureID(), static_cast<GLint>(sourceLevel)));
663 GLenum status = ANGLE_GL_TRY(context, mFunctions->checkFramebufferStatus(GL_FRAMEBUFFER));
664 if (status != GL_FRAMEBUFFER_COMPLETE)
665 {
666 // The source texture cannot be read with glReadPixels. Copy it into another RGBA texture
667 // and read that back instead.
668 nativegl::TexImageFormat texImageFormat = nativegl::GetTexImageFormat(
669 mFunctions, mFeatures, sourceInternalFormatInfo.internalFormat,
670 sourceInternalFormatInfo.format, sourceInternalFormatInfo.type);
671
672 gl::TextureType scratchTextureType = gl::TextureType::_2D;
673 mStateManager->bindTexture(scratchTextureType, mScratchTextures[0]);
674 ANGLE_GL_TRY_ALWAYS_CHECK(
675 context,
676 mFunctions->texImage2D(ToGLenum(scratchTextureType), 0, texImageFormat.internalFormat,
677 sourceArea.width, sourceArea.height, 0, texImageFormat.format,
678 texImageFormat.type, nullptr));
679
680 bool copySucceeded = false;
681 ANGLE_TRY(copySubTexture(
682 context, source, sourceLevel, sourceInternalFormatInfo.componentType,
683 mScratchTextures[0], NonCubeTextureTypeToTarget(scratchTextureType), 0,
684 sourceInternalFormatInfo.componentType, sourceSize, sourceArea, gl::Offset(0, 0, 0),
685 needsLumaWorkaround, lumaFormat, false, false, false, ©Succeeded));
686 if (!copySucceeded)
687 {
688 // No fallback options if we can't render to the scratch texture.
689 return angle::Result::Stop;
690 }
691
692 // Bind the scratch texture as the readback texture
693 mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
694 ANGLE_GL_TRY(context, mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
695 ToGLenum(scratchTextureType),
696 mScratchTextures[0], 0));
697
698 // The scratch texture sized to sourceArea so adjust the readpixels area
699 readPixelsArea.x = 0;
700 readPixelsArea.y = 0;
701
702 // Recheck the status
703 status = ANGLE_GL_TRY(context, mFunctions->checkFramebufferStatus(GL_FRAMEBUFFER));
704 }
705
706 ASSERT(status == GL_FRAMEBUFFER_COMPLETE);
707
708 // Create a buffer for holding the source and destination memory
709 const size_t sourcePixelSize = 4;
710 size_t sourceBufferSize = readPixelsArea.width * readPixelsArea.height * sourcePixelSize;
711 size_t destBufferSize =
712 readPixelsArea.width * readPixelsArea.height * destInternalFormatInfo.pixelBytes;
713 angle::MemoryBuffer *buffer = nullptr;
714 ANGLE_CHECK_GL_ALLOC(contextGL,
715 context->getScratchBuffer(sourceBufferSize + destBufferSize, &buffer));
716
717 uint8_t *sourceMemory = buffer->data();
718 uint8_t *destMemory = buffer->data() + sourceBufferSize;
719
720 GLenum readPixelsFormat = GL_NONE;
721 PixelReadFunction readFunction = nullptr;
722 if (sourceInternalFormatInfo.componentType == GL_UNSIGNED_INT)
723 {
724 readPixelsFormat = GL_RGBA_INTEGER;
725 readFunction = angle::ReadColor<angle::R8G8B8A8, GLuint>;
726 }
727 else
728 {
729 ASSERT(sourceInternalFormatInfo.componentType != GL_INT);
730 readPixelsFormat = GL_RGBA;
731 readFunction = angle::ReadColor<angle::R8G8B8A8, GLfloat>;
732 }
733
734 gl::PixelUnpackState unpack;
735 unpack.alignment = 1;
736 mStateManager->setPixelUnpackState(unpack);
737 mStateManager->setPixelUnpackBuffer(nullptr);
738 ANGLE_GL_TRY(context, mFunctions->readPixels(readPixelsArea.x, readPixelsArea.y,
739 readPixelsArea.width, readPixelsArea.height,
740 readPixelsFormat, GL_UNSIGNED_BYTE, sourceMemory));
741
742 angle::FormatID destFormatID =
743 angle::Format::InternalFormatToID(destInternalFormatInfo.sizedInternalFormat);
744 const auto &destFormatInfo = angle::Format::Get(destFormatID);
745 CopyImageCHROMIUM(
746 sourceMemory, readPixelsArea.width * sourcePixelSize, sourcePixelSize, 0, readFunction,
747 destMemory, readPixelsArea.width * destInternalFormatInfo.pixelBytes,
748 destInternalFormatInfo.pixelBytes, 0, destFormatInfo.pixelWriteFunction,
749 destInternalFormatInfo.format, destInternalFormatInfo.componentType, readPixelsArea.width,
750 readPixelsArea.height, 1, unpackFlipY, unpackPremultiplyAlpha, unpackUnmultiplyAlpha);
751
752 gl::PixelPackState pack;
753 pack.alignment = 1;
754 mStateManager->setPixelPackState(pack);
755 mStateManager->setPixelPackBuffer(nullptr);
756
757 nativegl::TexSubImageFormat texSubImageFormat =
758 nativegl::GetTexSubImageFormat(mFunctions, mFeatures, destFormat, destType);
759
760 mStateManager->bindTexture(dest->getType(), dest->getTextureID());
761 ANGLE_GL_TRY(context, mFunctions->texSubImage2D(
762 ToGLenum(destTarget), static_cast<GLint>(destLevel), destOffset.x,
763 destOffset.y, readPixelsArea.width, readPixelsArea.height,
764 texSubImageFormat.format, texSubImageFormat.type, destMemory));
765
766 return angle::Result::Continue;
767 }
768
copyTexSubImage(const gl::Context * context,TextureGL * source,size_t sourceLevel,TextureGL * dest,gl::TextureTarget destTarget,size_t destLevel,const gl::Rectangle & sourceArea,const gl::Offset & destOffset,bool * copySucceededOut)769 angle::Result BlitGL::copyTexSubImage(const gl::Context *context,
770 TextureGL *source,
771 size_t sourceLevel,
772 TextureGL *dest,
773 gl::TextureTarget destTarget,
774 size_t destLevel,
775 const gl::Rectangle &sourceArea,
776 const gl::Offset &destOffset,
777 bool *copySucceededOut)
778 {
779 ANGLE_TRY(initializeResources(context));
780
781 // Make sure the source texture can create a complete framebuffer before continuing.
782 mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
783 ANGLE_GL_TRY(context, mFunctions->framebufferTexture2D(
784 GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, ToGLenum(source->getType()),
785 source->getTextureID(), static_cast<GLint>(sourceLevel)));
786 GLenum status = ANGLE_GL_TRY(context, mFunctions->checkFramebufferStatus(GL_FRAMEBUFFER));
787 if (status != GL_FRAMEBUFFER_COMPLETE)
788 {
789 *copySucceededOut = false;
790 return angle::Result::Continue;
791 }
792
793 mStateManager->bindTexture(dest->getType(), dest->getTextureID());
794
795 ANGLE_GL_TRY(context,
796 mFunctions->copyTexSubImage2D(ToGLenum(destTarget), static_cast<GLint>(destLevel),
797 destOffset.x, destOffset.y, sourceArea.x,
798 sourceArea.y, sourceArea.width, sourceArea.height));
799
800 *copySucceededOut = true;
801 return angle::Result::Continue;
802 }
803
clearRenderableTexture(const gl::Context * context,TextureGL * source,GLenum sizedInternalFormat,int numTextureLayers,const gl::ImageIndex & imageIndex,bool * clearSucceededOut)804 angle::Result BlitGL::clearRenderableTexture(const gl::Context *context,
805 TextureGL *source,
806 GLenum sizedInternalFormat,
807 int numTextureLayers,
808 const gl::ImageIndex &imageIndex,
809 bool *clearSucceededOut)
810 {
811 ANGLE_TRY(initializeResources(context));
812
813 ClearBindTargetVector bindTargets;
814 ClearBindTargetVector unbindTargets;
815 GLbitfield clearMask = 0;
816 ANGLE_TRY(PrepareForClear(mStateManager, sizedInternalFormat, &bindTargets, &unbindTargets,
817 &clearMask));
818
819 mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
820 ANGLE_TRY(UnbindAttachments(context, mFunctions, GL_FRAMEBUFFER, unbindTargets));
821
822 if (nativegl::UseTexImage2D(source->getType()))
823 {
824 ASSERT(numTextureLayers == 1);
825 for (GLenum bindTarget : bindTargets)
826 {
827 ANGLE_GL_TRY(context, mFunctions->framebufferTexture2D(
828 GL_FRAMEBUFFER, bindTarget, ToGLenum(imageIndex.getTarget()),
829 source->getTextureID(), imageIndex.getLevelIndex()));
830 }
831
832 GLenum status = ANGLE_GL_TRY(context, mFunctions->checkFramebufferStatus(GL_FRAMEBUFFER));
833 if (status == GL_FRAMEBUFFER_COMPLETE)
834 {
835 ANGLE_GL_TRY(context, mFunctions->clear(clearMask));
836 }
837 else
838 {
839 ANGLE_TRY(UnbindAttachments(context, mFunctions, GL_FRAMEBUFFER, bindTargets));
840 *clearSucceededOut = false;
841 return angle::Result::Continue;
842 }
843 }
844 else
845 {
846 ASSERT(nativegl::UseTexImage3D(source->getType()));
847
848 // Check if it's possible to bind all layers of the texture at once
849 if (mFunctions->framebufferTexture && !imageIndex.hasLayer())
850 {
851 for (GLenum bindTarget : bindTargets)
852 {
853 ANGLE_GL_TRY(context, mFunctions->framebufferTexture(GL_FRAMEBUFFER, bindTarget,
854 source->getTextureID(),
855 imageIndex.getLevelIndex()));
856 }
857
858 GLenum status =
859 ANGLE_GL_TRY(context, mFunctions->checkFramebufferStatus(GL_FRAMEBUFFER));
860 if (status == GL_FRAMEBUFFER_COMPLETE)
861 {
862 ANGLE_GL_TRY(context, mFunctions->clear(clearMask));
863 }
864 else
865 {
866 ANGLE_TRY(UnbindAttachments(context, mFunctions, GL_FRAMEBUFFER, bindTargets));
867 *clearSucceededOut = false;
868 return angle::Result::Continue;
869 }
870 }
871 else
872 {
873 GLint firstLayer = 0;
874 GLint layerCount = numTextureLayers;
875 if (imageIndex.hasLayer())
876 {
877 firstLayer = imageIndex.getLayerIndex();
878 layerCount = imageIndex.getLayerCount();
879 }
880
881 for (GLint layer = 0; layer < layerCount; layer++)
882 {
883 for (GLenum bindTarget : bindTargets)
884 {
885 ANGLE_GL_TRY(context, mFunctions->framebufferTextureLayer(
886 GL_FRAMEBUFFER, bindTarget, source->getTextureID(),
887 imageIndex.getLevelIndex(), layer + firstLayer));
888 }
889
890 GLenum status =
891 ANGLE_GL_TRY(context, mFunctions->checkFramebufferStatus(GL_FRAMEBUFFER));
892 if (status == GL_FRAMEBUFFER_COMPLETE)
893 {
894 ANGLE_GL_TRY(context, mFunctions->clear(clearMask));
895 }
896 else
897 {
898 ANGLE_TRY(UnbindAttachments(context, mFunctions, GL_FRAMEBUFFER, bindTargets));
899 *clearSucceededOut = false;
900 return angle::Result::Continue;
901 }
902 }
903 }
904 }
905
906 ANGLE_TRY(UnbindAttachments(context, mFunctions, GL_FRAMEBUFFER, bindTargets));
907 *clearSucceededOut = true;
908 return angle::Result::Continue;
909 }
910
clearRenderbuffer(const gl::Context * context,RenderbufferGL * source,GLenum sizedInternalFormat)911 angle::Result BlitGL::clearRenderbuffer(const gl::Context *context,
912 RenderbufferGL *source,
913 GLenum sizedInternalFormat)
914 {
915 ANGLE_TRY(initializeResources(context));
916
917 ClearBindTargetVector bindTargets;
918 ClearBindTargetVector unbindTargets;
919 GLbitfield clearMask = 0;
920 ANGLE_TRY(PrepareForClear(mStateManager, sizedInternalFormat, &bindTargets, &unbindTargets,
921 &clearMask));
922
923 mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
924 ANGLE_TRY(UnbindAttachments(context, mFunctions, GL_FRAMEBUFFER, unbindTargets));
925
926 for (GLenum bindTarget : bindTargets)
927 {
928 ANGLE_GL_TRY(context,
929 mFunctions->framebufferRenderbuffer(
930 GL_FRAMEBUFFER, bindTarget, GL_RENDERBUFFER, source->getRenderbufferID()));
931 }
932 ANGLE_GL_TRY(context, mFunctions->clear(clearMask));
933
934 // Unbind
935 for (GLenum bindTarget : bindTargets)
936 {
937 ANGLE_GL_TRY(context, mFunctions->framebufferRenderbuffer(GL_FRAMEBUFFER, bindTarget,
938 GL_RENDERBUFFER, 0));
939 }
940
941 return angle::Result::Continue;
942 }
943
clearFramebuffer(const gl::Context * context,FramebufferGL * source)944 angle::Result BlitGL::clearFramebuffer(const gl::Context *context, FramebufferGL *source)
945 {
946 // initializeResources skipped because no local state is used
947
948 // Clear all attachments
949 GLbitfield clearMask = 0;
950 ANGLE_TRY(SetClearState(mStateManager, true, true, true, &clearMask));
951
952 mStateManager->bindFramebuffer(GL_FRAMEBUFFER, source->getFramebufferID());
953 ANGLE_GL_TRY(context, mFunctions->clear(clearMask));
954
955 return angle::Result::Continue;
956 }
957
clearRenderableTextureAlphaToOne(const gl::Context * context,GLuint texture,gl::TextureTarget target,size_t level)958 angle::Result BlitGL::clearRenderableTextureAlphaToOne(const gl::Context *context,
959 GLuint texture,
960 gl::TextureTarget target,
961 size_t level)
962 {
963 // Clearing the alpha of 3D textures is not supported/needed yet.
964 ASSERT(nativegl::UseTexImage2D(TextureTargetToType(target)));
965
966 ANGLE_TRY(initializeResources(context));
967
968 mStateManager->setClearColor(gl::ColorF(0.0f, 0.0f, 0.0f, 1.0f));
969 mStateManager->setColorMask(false, false, false, true);
970 mStateManager->setScissorTestEnabled(false);
971
972 mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
973 ANGLE_GL_TRY(context, mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
974 ToGLenum(target), texture,
975 static_cast<GLint>(level)));
976 ANGLE_GL_TRY(context, mFunctions->clear(GL_COLOR_BUFFER_BIT));
977
978 // Unbind the texture from the the scratch framebuffer
979 ANGLE_GL_TRY(context, mFunctions->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
980 GL_RENDERBUFFER, 0));
981
982 return angle::Result::Continue;
983 }
984
initializeResources(const gl::Context * context)985 angle::Result BlitGL::initializeResources(const gl::Context *context)
986 {
987 for (size_t i = 0; i < ArraySize(mScratchTextures); i++)
988 {
989 if (mScratchTextures[i] == 0)
990 {
991 ANGLE_GL_TRY(context, mFunctions->genTextures(1, &mScratchTextures[i]));
992 }
993 }
994
995 if (mScratchFBO == 0)
996 {
997 ANGLE_GL_TRY(context, mFunctions->genFramebuffers(1, &mScratchFBO));
998 }
999
1000 if (mVertexBuffer == 0)
1001 {
1002 ANGLE_GL_TRY(context, mFunctions->genBuffers(1, &mVertexBuffer));
1003 mStateManager->bindBuffer(gl::BufferBinding::Array, mVertexBuffer);
1004
1005 // Use a single, large triangle, to avoid arithmetic precision issues where fragments
1006 // with the same Y coordinate don't get exactly the same interpolated texcoord Y.
1007 float vertexData[] = {
1008 -0.5f, 0.0f, 1.5f, 0.0f, 0.5f, 2.0f,
1009 };
1010
1011 ANGLE_GL_TRY(context, mFunctions->bufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, vertexData,
1012 GL_STATIC_DRAW));
1013 }
1014
1015 if (mVAO == 0)
1016 {
1017 ANGLE_GL_TRY(context, mFunctions->genVertexArrays(1, &mVAO));
1018
1019 mStateManager->bindVertexArray(mVAO, 0);
1020 mStateManager->bindBuffer(gl::BufferBinding::Array, mVertexBuffer);
1021
1022 // Enable all attributes with the same buffer so that it doesn't matter what location the
1023 // texcoord attribute is assigned
1024 GLint maxAttributes = 0;
1025 ANGLE_GL_TRY(context, mFunctions->getIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxAttributes));
1026
1027 for (GLint i = 0; i < maxAttributes; i++)
1028 {
1029 ANGLE_GL_TRY(context, mFunctions->enableVertexAttribArray(i));
1030 ANGLE_GL_TRY(context,
1031 mFunctions->vertexAttribPointer(i, 2, GL_FLOAT, GL_FALSE, 0, nullptr));
1032 }
1033 }
1034
1035 return angle::Result::Continue;
1036 }
1037
orphanScratchTextures(const gl::Context * context)1038 angle::Result BlitGL::orphanScratchTextures(const gl::Context *context)
1039 {
1040 for (auto texture : mScratchTextures)
1041 {
1042 mStateManager->bindTexture(gl::TextureType::_2D, texture);
1043 gl::PixelUnpackState unpack;
1044 mStateManager->setPixelUnpackState(unpack);
1045 mStateManager->setPixelUnpackBuffer(nullptr);
1046 GLint swizzle[4] = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
1047 ANGLE_GL_TRY(context,
1048 mFunctions->texParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle));
1049 ANGLE_GL_TRY(context, mFunctions->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA,
1050 GL_UNSIGNED_BYTE, nullptr));
1051 }
1052 return angle::Result::Continue;
1053 }
1054
setScratchTextureParameter(const gl::Context * context,GLenum param,GLenum value)1055 angle::Result BlitGL::setScratchTextureParameter(const gl::Context *context,
1056 GLenum param,
1057 GLenum value)
1058 {
1059 for (auto texture : mScratchTextures)
1060 {
1061 mStateManager->bindTexture(gl::TextureType::_2D, texture);
1062 ANGLE_GL_TRY(context, mFunctions->texParameteri(GL_TEXTURE_2D, param, value));
1063 ANGLE_GL_TRY(context, mFunctions->texParameteri(GL_TEXTURE_2D, param, value));
1064 }
1065 return angle::Result::Continue;
1066 }
1067
getBlitProgram(const gl::Context * context,gl::TextureType sourceTextureType,GLenum sourceComponentType,GLenum destComponentType,BlitProgram ** program)1068 angle::Result BlitGL::getBlitProgram(const gl::Context *context,
1069 gl::TextureType sourceTextureType,
1070 GLenum sourceComponentType,
1071 GLenum destComponentType,
1072 BlitProgram **program)
1073 {
1074
1075 BlitProgramType programType(sourceTextureType, sourceComponentType, destComponentType);
1076 BlitProgram &result = mBlitPrograms[programType];
1077 if (result.program == 0)
1078 {
1079 result.program = ANGLE_GL_TRY(context, mFunctions->createProgram());
1080
1081 // Depending on what types need to be output by the shaders, different versions need to be
1082 // used.
1083 std::string version;
1084 std::string vsInputVariableQualifier;
1085 std::string vsOutputVariableQualifier;
1086 std::string fsInputVariableQualifier;
1087 std::string fsOutputVariableQualifier;
1088 std::string sampleFunction;
1089 if (sourceComponentType != GL_UNSIGNED_INT && destComponentType != GL_UNSIGNED_INT &&
1090 sourceTextureType != gl::TextureType::Rectangle)
1091 {
1092 // Simple case, float-to-float with 2D or external textures. Only needs ESSL/GLSL 100
1093 version = "100";
1094 vsInputVariableQualifier = "attribute";
1095 vsOutputVariableQualifier = "varying";
1096 fsInputVariableQualifier = "varying";
1097 fsOutputVariableQualifier = "";
1098 sampleFunction = "texture2D";
1099 }
1100 else
1101 {
1102 // Need to use a higher version to support non-float output types
1103 if (mFunctions->standard == STANDARD_GL_DESKTOP)
1104 {
1105 version = "330";
1106 }
1107 else
1108 {
1109 ASSERT(mFunctions->standard == STANDARD_GL_ES);
1110 version = "300 es";
1111 }
1112 vsInputVariableQualifier = "in";
1113 vsOutputVariableQualifier = "out";
1114 fsInputVariableQualifier = "in";
1115 fsOutputVariableQualifier = "out";
1116 sampleFunction = "texture";
1117 }
1118
1119 {
1120 // Compile the vertex shader
1121 std::ostringstream vsSourceStream;
1122 vsSourceStream << "#version " << version << "\n";
1123 vsSourceStream << vsInputVariableQualifier << " vec2 a_texcoord;\n";
1124 vsSourceStream << "uniform vec2 u_scale;\n";
1125 vsSourceStream << "uniform vec2 u_offset;\n";
1126 vsSourceStream << vsOutputVariableQualifier << " vec2 v_texcoord;\n";
1127 vsSourceStream << "\n";
1128 vsSourceStream << "void main()\n";
1129 vsSourceStream << "{\n";
1130 vsSourceStream << " gl_Position = vec4((a_texcoord * 2.0) - 1.0, 0.0, 1.0);\n";
1131 vsSourceStream << " v_texcoord = a_texcoord * u_scale + u_offset;\n";
1132 vsSourceStream << "}\n";
1133
1134 std::string vsSourceStr = vsSourceStream.str();
1135 const char *vsSourceCStr = vsSourceStr.c_str();
1136
1137 GLuint vs = ANGLE_GL_TRY(context, mFunctions->createShader(GL_VERTEX_SHADER));
1138 ANGLE_GL_TRY(context, mFunctions->shaderSource(vs, 1, &vsSourceCStr, nullptr));
1139 ANGLE_GL_TRY(context, mFunctions->compileShader(vs));
1140 ANGLE_TRY(CheckCompileStatus(context, mFunctions, vs));
1141
1142 ANGLE_GL_TRY(context, mFunctions->attachShader(result.program, vs));
1143 ANGLE_GL_TRY(context, mFunctions->deleteShader(vs));
1144 }
1145
1146 {
1147 // Sampling texture uniform changes depending on source texture type.
1148 std::string samplerType;
1149 switch (sourceTextureType)
1150 {
1151 case gl::TextureType::_2D:
1152 switch (sourceComponentType)
1153 {
1154 case GL_UNSIGNED_INT:
1155 samplerType = "usampler2D";
1156 break;
1157
1158 default: // Float type
1159 samplerType = "sampler2D";
1160 break;
1161 }
1162 break;
1163
1164 case gl::TextureType::External:
1165 ASSERT(sourceComponentType != GL_UNSIGNED_INT);
1166 samplerType = "samplerExternalOES";
1167 break;
1168
1169 case gl::TextureType::Rectangle:
1170 ASSERT(sourceComponentType != GL_UNSIGNED_INT);
1171 samplerType = "sampler2DRect";
1172 break;
1173
1174 default:
1175 UNREACHABLE();
1176 break;
1177 }
1178
1179 std::string samplerResultType;
1180 switch (sourceComponentType)
1181 {
1182 case GL_UNSIGNED_INT:
1183 samplerResultType = "uvec4";
1184 break;
1185
1186 default: // Float type
1187 samplerResultType = "vec4";
1188 break;
1189 }
1190
1191 std::string extensionRequirements;
1192 switch (sourceTextureType)
1193 {
1194 case gl::TextureType::External:
1195 extensionRequirements = "#extension GL_OES_EGL_image_external : require";
1196 break;
1197
1198 case gl::TextureType::Rectangle:
1199 if (mFunctions->hasGLExtension("GL_ARB_texture_rectangle"))
1200 {
1201 extensionRequirements = "#extension GL_ARB_texture_rectangle : require";
1202 }
1203 else
1204 {
1205 ASSERT(mFunctions->isAtLeastGL(gl::Version(3, 1)));
1206 }
1207 break;
1208
1209 default:
1210 break;
1211 }
1212
1213 // Output variables depend on the output type
1214 std::string outputType;
1215 std::string outputVariableName;
1216 std::string outputMultiplier;
1217 switch (destComponentType)
1218 {
1219 case GL_UNSIGNED_INT:
1220 outputType = "uvec4";
1221 outputVariableName = "outputUint";
1222 outputMultiplier = "255.0";
1223 break;
1224
1225 default: // float type
1226 if (version == "100")
1227 {
1228 outputType = "";
1229 outputVariableName = "gl_FragColor";
1230 outputMultiplier = "1.0";
1231 }
1232 else
1233 {
1234 outputType = "vec4";
1235 outputVariableName = "outputFloat";
1236 outputMultiplier = "1.0";
1237 }
1238 break;
1239 }
1240
1241 // Compile the fragment shader
1242 std::ostringstream fsSourceStream;
1243 fsSourceStream << "#version " << version << "\n";
1244 fsSourceStream << extensionRequirements << "\n";
1245 fsSourceStream << "precision highp float;\n";
1246 fsSourceStream << "uniform " << samplerType << " u_source_texture;\n";
1247
1248 // Write the rest of the uniforms and varyings
1249 fsSourceStream << "uniform bool u_multiply_alpha;\n";
1250 fsSourceStream << "uniform bool u_unmultiply_alpha;\n";
1251 fsSourceStream << fsInputVariableQualifier << " vec2 v_texcoord;\n";
1252 if (!outputType.empty())
1253 {
1254 fsSourceStream << fsOutputVariableQualifier << " " << outputType << " "
1255 << outputVariableName << ";\n";
1256 }
1257
1258 // Write the main body
1259 fsSourceStream << "\n";
1260 fsSourceStream << "void main()\n";
1261 fsSourceStream << "{\n";
1262
1263 std::string maxTexcoord;
1264 switch (sourceTextureType)
1265 {
1266 case gl::TextureType::Rectangle:
1267 // Valid texcoords are within source texture size
1268 maxTexcoord = "vec2(textureSize(u_source_texture))";
1269 break;
1270
1271 default:
1272 // Valid texcoords are in [0, 1]
1273 maxTexcoord = "vec2(1.0)";
1274 break;
1275 }
1276
1277 // discard if the texcoord is invalid so the blitframebuffer workaround doesn't
1278 // write when the point sampled is outside of the source framebuffer.
1279 fsSourceStream << " if (clamp(v_texcoord, vec2(0.0), " << maxTexcoord
1280 << ") != v_texcoord)\n";
1281 fsSourceStream << " {\n";
1282 fsSourceStream << " discard;\n";
1283 fsSourceStream << " }\n";
1284
1285 // Sampling code depends on the input data type
1286 fsSourceStream << " " << samplerResultType << " color = " << sampleFunction
1287 << "(u_source_texture, v_texcoord);\n";
1288
1289 // Perform the premultiply or unmultiply alpha logic
1290 fsSourceStream << " if (u_multiply_alpha)\n";
1291 fsSourceStream << " {\n";
1292 fsSourceStream << " color.xyz = color.xyz * color.a;\n";
1293 fsSourceStream << " }\n";
1294 fsSourceStream << " if (u_unmultiply_alpha && color.a != 0.0)\n";
1295 fsSourceStream << " {\n";
1296 fsSourceStream << " color.xyz = color.xyz / color.a;\n";
1297 fsSourceStream << " }\n";
1298
1299 // Write the conversion to the destionation type
1300 fsSourceStream << " color = color * " << outputMultiplier << ";\n";
1301
1302 // Write the output assignment code
1303 fsSourceStream << " " << outputVariableName << " = " << outputType << "(color);\n";
1304 fsSourceStream << "}\n";
1305
1306 std::string fsSourceStr = fsSourceStream.str();
1307 const char *fsSourceCStr = fsSourceStr.c_str();
1308
1309 GLuint fs = ANGLE_GL_TRY(context, mFunctions->createShader(GL_FRAGMENT_SHADER));
1310 ANGLE_GL_TRY(context, mFunctions->shaderSource(fs, 1, &fsSourceCStr, nullptr));
1311 ANGLE_GL_TRY(context, mFunctions->compileShader(fs));
1312 ANGLE_TRY(CheckCompileStatus(context, mFunctions, fs));
1313
1314 ANGLE_GL_TRY(context, mFunctions->attachShader(result.program, fs));
1315 ANGLE_GL_TRY(context, mFunctions->deleteShader(fs));
1316 }
1317
1318 ANGLE_GL_TRY(context, mFunctions->linkProgram(result.program));
1319 ANGLE_TRY(CheckLinkStatus(context, mFunctions, result.program));
1320
1321 result.sourceTextureLocation = ANGLE_GL_TRY(
1322 context, mFunctions->getUniformLocation(result.program, "u_source_texture"));
1323 result.scaleLocation =
1324 ANGLE_GL_TRY(context, mFunctions->getUniformLocation(result.program, "u_scale"));
1325 result.offsetLocation =
1326 ANGLE_GL_TRY(context, mFunctions->getUniformLocation(result.program, "u_offset"));
1327 result.multiplyAlphaLocation = ANGLE_GL_TRY(
1328 context, mFunctions->getUniformLocation(result.program, "u_multiply_alpha"));
1329 result.unMultiplyAlphaLocation = ANGLE_GL_TRY(
1330 context, mFunctions->getUniformLocation(result.program, "u_unmultiply_alpha"));
1331 }
1332
1333 *program = &result;
1334 return angle::Result::Continue;
1335 }
1336
1337 } // namespace rx
1338