• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/autogen/FeaturesGL_autogen.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 [[nodiscard]] 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->setClipControl(gl::ClipOrigin::LowerLeft,
89                                      gl::ClipDepthMode::NegativeOneToOne);
90         stateManager->setClipDistancesEnable(gl::ClipDistanceEnableBits());
91         stateManager->setDepthClampEnabled(false);
92         stateManager->setBlendEnabled(false);
93         stateManager->setColorMask(true, true, true, true);
94         stateManager->setSampleAlphaToCoverageEnabled(false);
95         stateManager->setSampleCoverageEnabled(false);
96         stateManager->setDepthTestEnabled(false);
97         stateManager->setStencilTestEnabled(false);
98         stateManager->setCullFaceEnabled(false);
99         stateManager->setPolygonMode(gl::PolygonMode::Fill);
100         stateManager->setPolygonOffsetPointEnabled(false);
101         stateManager->setPolygonOffsetLineEnabled(false);
102         stateManager->setPolygonOffsetFillEnabled(false);
103         stateManager->setRasterizerDiscardEnabled(false);
104         stateManager->setLogicOpEnabled(false);
105 
106         stateManager->pauseTransformFeedback();
107         return stateManager->pauseAllQueries(context);
108     }
109 
exit(const gl::Context * context)110     angle::Result exit(const gl::Context *context)
111     {
112         mExited = true;
113 
114         ContextGL *contextGL         = GetImplAs<ContextGL>(context);
115         StateManagerGL *stateManager = contextGL->getStateManager();
116 
117         // XFB resuming will be done automatically
118         return stateManager->resumeAllQueries(context);
119     }
120 
willUseTextureUnit(const gl::Context * context,int unit)121     void willUseTextureUnit(const gl::Context *context, int unit)
122     {
123         ContextGL *contextGL = GetImplAs<ContextGL>(context);
124 
125         if (contextGL->getFunctions()->bindSampler)
126         {
127             contextGL->getStateManager()->bindSampler(unit, 0);
128         }
129     }
130 
131   private:
132     bool mExited = false;
133 };
134 
SetClearState(StateManagerGL * stateManager,bool colorClear,bool depthClear,bool stencilClear,GLbitfield * outClearMask)135 angle::Result SetClearState(StateManagerGL *stateManager,
136                             bool colorClear,
137                             bool depthClear,
138                             bool stencilClear,
139                             GLbitfield *outClearMask)
140 {
141     *outClearMask = 0;
142     if (colorClear)
143     {
144         stateManager->setClearColor(gl::ColorF(0.0f, 0.0f, 0.0f, 0.0f));
145         stateManager->setColorMask(true, true, true, true);
146         *outClearMask |= GL_COLOR_BUFFER_BIT;
147     }
148     if (depthClear)
149     {
150         stateManager->setDepthMask(true);
151         stateManager->setClearDepth(1.0f);
152         *outClearMask |= GL_DEPTH_BUFFER_BIT;
153     }
154     if (stencilClear)
155     {
156         stateManager->setClearStencil(0);
157         *outClearMask |= GL_STENCIL_BUFFER_BIT;
158     }
159 
160     stateManager->setScissorTestEnabled(false);
161 
162     return angle::Result::Continue;
163 }
164 
165 using ClearBindTargetVector = angle::FixedVector<GLenum, 3>;
166 
PrepareForClear(StateManagerGL * stateManager,GLenum sizedInternalFormat,ClearBindTargetVector * outBindtargets,ClearBindTargetVector * outUnbindTargets,GLbitfield * outClearMask)167 angle::Result PrepareForClear(StateManagerGL *stateManager,
168                               GLenum sizedInternalFormat,
169                               ClearBindTargetVector *outBindtargets,
170                               ClearBindTargetVector *outUnbindTargets,
171                               GLbitfield *outClearMask)
172 {
173     const gl::InternalFormat &internalFormatInfo =
174         gl::GetSizedInternalFormatInfo(sizedInternalFormat);
175     bool bindDepth   = internalFormatInfo.depthBits > 0;
176     bool bindStencil = internalFormatInfo.stencilBits > 0;
177     bool bindColor   = !bindDepth && !bindStencil;
178 
179     outBindtargets->clear();
180     if (bindColor)
181     {
182         outBindtargets->push_back(GL_COLOR_ATTACHMENT0);
183     }
184     else
185     {
186         outUnbindTargets->push_back(GL_COLOR_ATTACHMENT0);
187     }
188     if (bindDepth)
189     {
190         outBindtargets->push_back(GL_DEPTH_ATTACHMENT);
191     }
192     else
193     {
194         outUnbindTargets->push_back(GL_DEPTH_ATTACHMENT);
195     }
196     if (bindStencil)
197     {
198         outBindtargets->push_back(GL_STENCIL_ATTACHMENT);
199     }
200     else
201     {
202         outUnbindTargets->push_back(GL_STENCIL_ATTACHMENT);
203     }
204 
205     ANGLE_TRY(SetClearState(stateManager, bindColor, bindDepth, bindStencil, outClearMask));
206 
207     return angle::Result::Continue;
208 }
209 
UnbindAttachment(const gl::Context * context,const FunctionsGL * functions,GLenum framebufferTarget,GLenum attachment)210 angle::Result UnbindAttachment(const gl::Context *context,
211                                const FunctionsGL *functions,
212                                GLenum framebufferTarget,
213                                GLenum attachment)
214 {
215     // Always use framebufferTexture2D as a workaround for an Nvidia driver bug. See
216     // https://anglebug.com/5536 and FeaturesGL.alwaysUnbindFramebufferTexture2D
217     ANGLE_GL_TRY(context,
218                  functions->framebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, 0, 0));
219 
220     return angle::Result::Continue;
221 }
222 
UnbindAttachments(const gl::Context * context,const FunctionsGL * functions,GLenum framebufferTarget,const ClearBindTargetVector & bindTargets)223 angle::Result UnbindAttachments(const gl::Context *context,
224                                 const FunctionsGL *functions,
225                                 GLenum framebufferTarget,
226                                 const ClearBindTargetVector &bindTargets)
227 {
228     for (GLenum bindTarget : bindTargets)
229     {
230         ANGLE_TRY(UnbindAttachment(context, functions, framebufferTarget, bindTarget));
231     }
232     return angle::Result::Continue;
233 }
234 
235 }  // anonymous namespace
236 
BlitGL(const FunctionsGL * functions,const angle::FeaturesGL & features,StateManagerGL * stateManager)237 BlitGL::BlitGL(const FunctionsGL *functions,
238                const angle::FeaturesGL &features,
239                StateManagerGL *stateManager)
240     : mFunctions(functions),
241       mFeatures(features),
242       mStateManager(stateManager),
243       mScratchFBO(0),
244       mVAO(0),
245       mVertexBuffer(0)
246 {
247     for (size_t i = 0; i < ArraySize(mScratchTextures); i++)
248     {
249         mScratchTextures[i] = 0;
250     }
251 
252     ASSERT(mFunctions);
253     ASSERT(mStateManager);
254 }
255 
~BlitGL()256 BlitGL::~BlitGL()
257 {
258     for (const auto &blitProgram : mBlitPrograms)
259     {
260         mStateManager->deleteProgram(blitProgram.second.program);
261     }
262     mBlitPrograms.clear();
263 
264     for (size_t i = 0; i < ArraySize(mScratchTextures); i++)
265     {
266         if (mScratchTextures[i] != 0)
267         {
268             mStateManager->deleteTexture(mScratchTextures[i]);
269             mScratchTextures[i] = 0;
270         }
271     }
272 
273     if (mScratchFBO != 0)
274     {
275         mStateManager->deleteFramebuffer(mScratchFBO);
276         mScratchFBO = 0;
277     }
278 
279     if (mOwnsVAOState)
280     {
281         mStateManager->deleteVertexArray(mVAO);
282         SafeDelete(mVAOState);
283         mVAO = 0;
284     }
285 }
286 
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)287 angle::Result BlitGL::copyImageToLUMAWorkaroundTexture(const gl::Context *context,
288                                                        GLuint texture,
289                                                        gl::TextureType textureType,
290                                                        gl::TextureTarget target,
291                                                        GLenum lumaFormat,
292                                                        size_t level,
293                                                        const gl::Rectangle &sourceArea,
294                                                        GLenum internalFormat,
295                                                        gl::Framebuffer *source)
296 {
297     mStateManager->bindTexture(textureType, texture);
298 
299     // Allocate the texture memory
300     GLenum format   = gl::GetUnsizedFormat(internalFormat);
301     GLenum readType = source->getImplementationColorReadType(context);
302 
303     // getImplementationColorReadType aligns the type with ES client version
304     if (readType == GL_HALF_FLOAT_OES && mFunctions->standard == STANDARD_GL_DESKTOP)
305     {
306         readType = GL_HALF_FLOAT;
307     }
308 
309     gl::PixelUnpackState unpack;
310     ANGLE_TRY(mStateManager->setPixelUnpackState(context, unpack));
311     ANGLE_TRY(mStateManager->setPixelUnpackBuffer(
312         context, context->getState().getTargetBuffer(gl::BufferBinding::PixelUnpack)));
313     ANGLE_GL_TRY_ALWAYS_CHECK(
314         context,
315         mFunctions->texImage2D(ToGLenum(target), static_cast<GLint>(level), internalFormat,
316                                sourceArea.width, sourceArea.height, 0, format, readType, nullptr));
317 
318     return copySubImageToLUMAWorkaroundTexture(context, texture, textureType, target, lumaFormat,
319                                                level, gl::Offset(0, 0, 0), sourceArea, source);
320 }
321 
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)322 angle::Result BlitGL::copySubImageToLUMAWorkaroundTexture(const gl::Context *context,
323                                                           GLuint texture,
324                                                           gl::TextureType textureType,
325                                                           gl::TextureTarget target,
326                                                           GLenum lumaFormat,
327                                                           size_t level,
328                                                           const gl::Offset &destOffset,
329                                                           const gl::Rectangle &sourceArea,
330                                                           gl::Framebuffer *source)
331 {
332     ANGLE_TRY(initializeResources(context));
333 
334     BlitProgram *blitProgram = nullptr;
335     ANGLE_TRY(getBlitProgram(context, gl::TextureType::_2D, GL_FLOAT, GL_FLOAT, &blitProgram));
336 
337     // Blit the framebuffer to the first scratch texture
338     const FramebufferGL *sourceFramebufferGL = GetImplAs<FramebufferGL>(source);
339     mStateManager->bindFramebuffer(GL_FRAMEBUFFER, sourceFramebufferGL->getFramebufferID());
340 
341     GLenum readFormat = source->getImplementationColorReadFormat(context);
342     GLenum readType   = source->getImplementationColorReadType(context);
343 
344     // getImplementationColorReadType aligns the type with ES client version
345     if (readType == GL_HALF_FLOAT_OES && mFunctions->standard == STANDARD_GL_DESKTOP)
346     {
347         readType = GL_HALF_FLOAT;
348     }
349 
350     nativegl::CopyTexImageImageFormat copyTexImageFormat =
351         nativegl::GetCopyTexImageImageFormat(mFunctions, mFeatures, readFormat, readType);
352 
353     mStateManager->bindTexture(gl::TextureType::_2D, mScratchTextures[0]);
354     ANGLE_GL_TRY_ALWAYS_CHECK(
355         context, mFunctions->copyTexImage2D(GL_TEXTURE_2D, 0, copyTexImageFormat.internalFormat,
356                                             sourceArea.x, sourceArea.y, sourceArea.width,
357                                             sourceArea.height, 0));
358 
359     // Set the swizzle of the scratch texture so that the channels sample into the correct emulated
360     // LUMA channels.
361     GLint swizzle[4] = {
362         (lumaFormat == GL_ALPHA) ? GL_ALPHA : GL_RED,
363         (lumaFormat == GL_LUMINANCE_ALPHA) ? GL_ALPHA : GL_ZERO,
364         GL_ZERO,
365         GL_ZERO,
366     };
367     ANGLE_GL_TRY(context,
368                  mFunctions->texParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle));
369 
370     // Make a temporary framebuffer using the second scratch texture to render the swizzled result
371     // to.
372     mStateManager->bindTexture(gl::TextureType::_2D, mScratchTextures[1]);
373     ANGLE_GL_TRY_ALWAYS_CHECK(
374         context, mFunctions->texImage2D(GL_TEXTURE_2D, 0, copyTexImageFormat.internalFormat,
375                                         sourceArea.width, sourceArea.height, 0,
376                                         gl::GetUnsizedFormat(copyTexImageFormat.internalFormat),
377                                         readType, nullptr));
378 
379     mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
380     ANGLE_GL_TRY(context, mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
381                                                            GL_TEXTURE_2D, mScratchTextures[1], 0));
382 
383     // Render to the destination texture, sampling from the scratch texture
384     ScopedGLState scopedState;
385     ANGLE_TRY(scopedState.enter(context, gl::Rectangle(0, 0, sourceArea.width, sourceArea.height)));
386     scopedState.willUseTextureUnit(context, 0);
387 
388     ANGLE_TRY(setScratchTextureParameter(context, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
389     ANGLE_TRY(setScratchTextureParameter(context, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
390 
391     mStateManager->activeTexture(0);
392     mStateManager->bindTexture(gl::TextureType::_2D, mScratchTextures[0]);
393 
394     mStateManager->useProgram(blitProgram->program);
395     ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->sourceTextureLocation, 0));
396     ANGLE_GL_TRY(context, mFunctions->uniform2f(blitProgram->scaleLocation, 1.0, 1.0));
397     ANGLE_GL_TRY(context, mFunctions->uniform2f(blitProgram->offsetLocation, 0.0, 0.0));
398     ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->multiplyAlphaLocation, 0));
399     ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->unMultiplyAlphaLocation, 0));
400 
401     ANGLE_TRY(setVAOState(context));
402     ANGLE_GL_TRY(context, mFunctions->drawArrays(GL_TRIANGLES, 0, 3));
403 
404     // Copy the swizzled texture to the destination texture
405     mStateManager->bindTexture(textureType, texture);
406 
407     if (nativegl::UseTexImage3D(textureType))
408     {
409         ANGLE_GL_TRY(context,
410                      mFunctions->copyTexSubImage3D(ToGLenum(target), static_cast<GLint>(level),
411                                                    destOffset.x, destOffset.y, destOffset.z, 0, 0,
412                                                    sourceArea.width, sourceArea.height));
413     }
414     else
415     {
416         ASSERT(nativegl::UseTexImage2D(textureType));
417         ANGLE_GL_TRY(context, mFunctions->copyTexSubImage2D(
418                                   ToGLenum(target), static_cast<GLint>(level), destOffset.x,
419                                   destOffset.y, 0, 0, sourceArea.width, sourceArea.height));
420     }
421 
422     // Finally orphan the scratch textures so they can be GCed by the driver.
423     ANGLE_TRY(orphanScratchTextures(context));
424     ANGLE_TRY(UnbindAttachment(context, mFunctions, GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0));
425 
426     ANGLE_TRY(scopedState.exit(context));
427     return angle::Result::Continue;
428 }
429 
blitColorBufferWithShader(const gl::Context * context,const gl::Framebuffer * source,const GLuint destTexture,const gl::TextureTarget destTarget,const size_t destLevel,const gl::Rectangle & sourceAreaIn,const gl::Rectangle & destAreaIn,GLenum filter,bool writeAlpha)430 angle::Result BlitGL::blitColorBufferWithShader(const gl::Context *context,
431                                                 const gl::Framebuffer *source,
432                                                 const GLuint destTexture,
433                                                 const gl::TextureTarget destTarget,
434                                                 const size_t destLevel,
435                                                 const gl::Rectangle &sourceAreaIn,
436                                                 const gl::Rectangle &destAreaIn,
437                                                 GLenum filter,
438                                                 bool writeAlpha)
439 {
440     ANGLE_TRY(initializeResources(context));
441     mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
442     ANGLE_GL_TRY(context, mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
443                                                            ToGLenum(destTarget), destTexture,
444                                                            static_cast<GLint>(destLevel)));
445     GLenum status = ANGLE_GL_TRY(context, mFunctions->checkFramebufferStatus(GL_FRAMEBUFFER));
446     if (status != GL_FRAMEBUFFER_COMPLETE)
447     {
448         return angle::Result::Stop;
449     }
450     angle::Result result = blitColorBufferWithShader(context, source, mScratchFBO, sourceAreaIn,
451                                                      destAreaIn, filter, writeAlpha);
452     // Unbind the texture from the the scratch framebuffer.
453     ANGLE_TRY(UnbindAttachment(context, mFunctions, GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0));
454     return result;
455 }
456 
blitColorBufferWithShader(const gl::Context * context,const gl::Framebuffer * source,const gl::Framebuffer * dest,const gl::Rectangle & sourceAreaIn,const gl::Rectangle & destAreaIn,GLenum filter,bool writeAlpha)457 angle::Result BlitGL::blitColorBufferWithShader(const gl::Context *context,
458                                                 const gl::Framebuffer *source,
459                                                 const gl::Framebuffer *dest,
460                                                 const gl::Rectangle &sourceAreaIn,
461                                                 const gl::Rectangle &destAreaIn,
462                                                 GLenum filter,
463                                                 bool writeAlpha)
464 {
465     const FramebufferGL *destGL = GetImplAs<FramebufferGL>(dest);
466     return blitColorBufferWithShader(context, source, destGL->getFramebufferID(), sourceAreaIn,
467                                      destAreaIn, filter, writeAlpha);
468 }
469 
blitColorBufferWithShader(const gl::Context * context,const gl::Framebuffer * source,const GLuint destFramebuffer,const gl::Rectangle & sourceAreaIn,const gl::Rectangle & destAreaIn,GLenum filter,bool writeAlpha)470 angle::Result BlitGL::blitColorBufferWithShader(const gl::Context *context,
471                                                 const gl::Framebuffer *source,
472                                                 const GLuint destFramebuffer,
473                                                 const gl::Rectangle &sourceAreaIn,
474                                                 const gl::Rectangle &destAreaIn,
475                                                 GLenum filter,
476                                                 bool writeAlpha)
477 {
478     ANGLE_TRY(initializeResources(context));
479 
480     BlitProgram *blitProgram = nullptr;
481     ANGLE_TRY(getBlitProgram(context, gl::TextureType::_2D, GL_FLOAT, GL_FLOAT, &blitProgram));
482 
483     // We'll keep things simple by removing reversed coordinates from the rectangles. In the end
484     // we'll apply the reversal to the source texture coordinates if needed. The destination
485     // rectangle will be set to the gl viewport, which can't be reversed.
486     bool reverseX            = sourceAreaIn.isReversedX() != destAreaIn.isReversedX();
487     bool reverseY            = sourceAreaIn.isReversedY() != destAreaIn.isReversedY();
488     gl::Rectangle sourceArea = sourceAreaIn.removeReversal();
489     gl::Rectangle destArea   = destAreaIn.removeReversal();
490 
491     const gl::FramebufferAttachment *readAttachment = source->getReadColorAttachment();
492     ASSERT(readAttachment->getSamples() <= 1);
493 
494     // Compute the part of the source that will be sampled.
495     gl::Rectangle inBoundsSource;
496     {
497         gl::Extents sourceSize = readAttachment->getSize();
498         gl::Rectangle sourceBounds(0, 0, sourceSize.width, sourceSize.height);
499         if (!gl::ClipRectangle(sourceArea, sourceBounds, &inBoundsSource))
500         {
501             // Early out when the sampled part is empty as the blit will be a noop,
502             // and it prevents a division by zero in later computations.
503             return angle::Result::Continue;
504         }
505     }
506 
507     // The blit will be emulated by getting the source of the blit in a texture and sampling it
508     // with CLAMP_TO_EDGE.
509 
510     GLuint textureId;
511 
512     // TODO(cwallez) once texture dirty bits are landed, reuse attached texture instead of using
513     // CopyTexImage2D
514     {
515         textureId = mScratchTextures[0];
516 
517         const gl::InternalFormat &sourceInternalFormat       = *readAttachment->getFormat().info;
518         nativegl::CopyTexImageImageFormat copyTexImageFormat = nativegl::GetCopyTexImageImageFormat(
519             mFunctions, mFeatures, sourceInternalFormat.internalFormat, sourceInternalFormat.type);
520         const FramebufferGL *sourceGL = GetImplAs<FramebufferGL>(source);
521         mStateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, sourceGL->getFramebufferID());
522         mStateManager->bindTexture(gl::TextureType::_2D, textureId);
523 
524         ANGLE_GL_TRY_ALWAYS_CHECK(
525             context, mFunctions->copyTexImage2D(GL_TEXTURE_2D, 0, copyTexImageFormat.internalFormat,
526                                                 inBoundsSource.x, inBoundsSource.y,
527                                                 inBoundsSource.width, inBoundsSource.height, 0));
528 
529         // Translate sourceArea to be relative to the copied image.
530         sourceArea.x -= inBoundsSource.x;
531         sourceArea.y -= inBoundsSource.y;
532 
533         ANGLE_TRY(setScratchTextureParameter(context, GL_TEXTURE_MIN_FILTER, filter));
534         ANGLE_TRY(setScratchTextureParameter(context, GL_TEXTURE_MAG_FILTER, filter));
535         ANGLE_TRY(setScratchTextureParameter(context, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
536         ANGLE_TRY(setScratchTextureParameter(context, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
537     }
538 
539     // Transform the source area to the texture coordinate space (where 0.0 and 1.0 correspond to
540     // the edges of the texture).
541     Vector2 texCoordOffset(
542         static_cast<float>(sourceArea.x) / static_cast<float>(inBoundsSource.width),
543         static_cast<float>(sourceArea.y) / static_cast<float>(inBoundsSource.height));
544     // texCoordScale is equal to the size of the source area in texture coordinates.
545     Vector2 texCoordScale(
546         static_cast<float>(sourceArea.width) / static_cast<float>(inBoundsSource.width),
547         static_cast<float>(sourceArea.height) / static_cast<float>(inBoundsSource.height));
548 
549     if (reverseX)
550     {
551         texCoordOffset.x() = texCoordOffset.x() + texCoordScale.x();
552         texCoordScale.x()  = -texCoordScale.x();
553     }
554     if (reverseY)
555     {
556         texCoordOffset.y() = texCoordOffset.y() + texCoordScale.y();
557         texCoordScale.y()  = -texCoordScale.y();
558     }
559 
560     // Reset all the state except scissor and use the viewport to draw exactly to the destination
561     // rectangle
562     ScopedGLState scopedState;
563     ANGLE_TRY(scopedState.enter(context, destArea, ScopedGLState::KEEP_SCISSOR));
564     scopedState.willUseTextureUnit(context, 0);
565 
566     // Set the write color mask to potentially not write alpha
567     mStateManager->setColorMask(true, true, true, writeAlpha);
568 
569     // Set uniforms
570     mStateManager->activeTexture(0);
571     mStateManager->bindTexture(gl::TextureType::_2D, textureId);
572 
573     mStateManager->useProgram(blitProgram->program);
574     ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->sourceTextureLocation, 0));
575     ANGLE_GL_TRY(context, mFunctions->uniform2f(blitProgram->scaleLocation, texCoordScale.x(),
576                                                 texCoordScale.y()));
577     ANGLE_GL_TRY(context, mFunctions->uniform2f(blitProgram->offsetLocation, texCoordOffset.x(),
578                                                 texCoordOffset.y()));
579     ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->multiplyAlphaLocation, 0));
580     ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->unMultiplyAlphaLocation, 0));
581 
582     mStateManager->bindFramebuffer(GL_DRAW_FRAMEBUFFER, destFramebuffer);
583 
584     ANGLE_TRY(setVAOState(context));
585     ANGLE_GL_TRY(context, mFunctions->drawArrays(GL_TRIANGLES, 0, 3));
586 
587     ANGLE_TRY(scopedState.exit(context));
588     return angle::Result::Continue;
589 }
590 
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)591 angle::Result BlitGL::copySubTexture(const gl::Context *context,
592                                      TextureGL *source,
593                                      size_t sourceLevel,
594                                      GLenum sourceComponentType,
595                                      GLuint destID,
596                                      gl::TextureTarget destTarget,
597                                      size_t destLevel,
598                                      GLenum destComponentType,
599                                      const gl::Extents &sourceSize,
600                                      const gl::Rectangle &sourceArea,
601                                      const gl::Offset &destOffset,
602                                      bool needsLumaWorkaround,
603                                      GLenum lumaFormat,
604                                      bool unpackFlipY,
605                                      bool unpackPremultiplyAlpha,
606                                      bool unpackUnmultiplyAlpha,
607                                      bool *copySucceededOut)
608 {
609     ASSERT(source->getType() == gl::TextureType::_2D ||
610            source->getType() == gl::TextureType::External ||
611            source->getType() == gl::TextureType::Rectangle);
612     ANGLE_TRY(initializeResources(context));
613 
614     // Make sure the destination texture can be rendered to before setting anything else up.  Some
615     // cube maps may not be renderable until all faces have been filled.
616     mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
617     ANGLE_GL_TRY(context, mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
618                                                            ToGLenum(destTarget), destID,
619                                                            static_cast<GLint>(destLevel)));
620     GLenum status = ANGLE_GL_TRY(context, mFunctions->checkFramebufferStatus(GL_FRAMEBUFFER));
621     if (status != GL_FRAMEBUFFER_COMPLETE)
622     {
623         *copySucceededOut = false;
624         return angle::Result::Continue;
625     }
626 
627     BlitProgram *blitProgram = nullptr;
628     ANGLE_TRY(getBlitProgram(context, source->getType(), sourceComponentType, destComponentType,
629                              &blitProgram));
630 
631     // Setup the source texture
632     if (needsLumaWorkaround)
633     {
634         GLint luminance = (lumaFormat == GL_ALPHA) ? GL_ZERO : GL_RED;
635 
636         GLint alpha = GL_RED;
637         if (lumaFormat == GL_LUMINANCE)
638         {
639             alpha = GL_ONE;
640         }
641         else if (lumaFormat == GL_LUMINANCE_ALPHA)
642         {
643             alpha = GL_GREEN;
644         }
645         else
646         {
647             ASSERT(lumaFormat == GL_ALPHA);
648         }
649 
650         GLint swizzle[4] = {luminance, luminance, luminance, alpha};
651         ANGLE_TRY(source->setSwizzle(context, swizzle));
652     }
653     ANGLE_TRY(source->setMinFilter(context, GL_NEAREST));
654     ANGLE_TRY(source->setMagFilter(context, GL_NEAREST));
655     ANGLE_TRY(source->setBaseLevel(context, static_cast<GLuint>(sourceLevel)));
656 
657     // Render to the destination texture, sampling from the source texture
658     ScopedGLState scopedState;
659     ANGLE_TRY(scopedState.enter(
660         context, gl::Rectangle(destOffset.x, destOffset.y, sourceArea.width, sourceArea.height)));
661     scopedState.willUseTextureUnit(context, 0);
662 
663     mStateManager->activeTexture(0);
664     mStateManager->bindTexture(source->getType(), source->getTextureID());
665 
666     Vector2 scale(sourceArea.width, sourceArea.height);
667     Vector2 offset(sourceArea.x, sourceArea.y);
668     if (source->getType() != gl::TextureType::Rectangle)
669     {
670         scale.x() /= static_cast<float>(sourceSize.width);
671         scale.y() /= static_cast<float>(sourceSize.height);
672         offset.x() /= static_cast<float>(sourceSize.width);
673         offset.y() /= static_cast<float>(sourceSize.height);
674     }
675     if (unpackFlipY)
676     {
677         offset.y() += scale.y();
678         scale.y() = -scale.y();
679     }
680 
681     mStateManager->useProgram(blitProgram->program);
682     ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->sourceTextureLocation, 0));
683     ANGLE_GL_TRY(context, mFunctions->uniform2f(blitProgram->scaleLocation, scale.x(), scale.y()));
684     ANGLE_GL_TRY(context,
685                  mFunctions->uniform2f(blitProgram->offsetLocation, offset.x(), offset.y()));
686     if (unpackPremultiplyAlpha == unpackUnmultiplyAlpha)
687     {
688         ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->multiplyAlphaLocation, 0));
689         ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->unMultiplyAlphaLocation, 0));
690     }
691     else
692     {
693         ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->multiplyAlphaLocation,
694                                                     unpackPremultiplyAlpha));
695         ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->unMultiplyAlphaLocation,
696                                                     unpackUnmultiplyAlpha));
697     }
698 
699     ANGLE_TRY(setVAOState(context));
700     ANGLE_GL_TRY(context, mFunctions->drawArrays(GL_TRIANGLES, 0, 3));
701     ANGLE_TRY(UnbindAttachment(context, mFunctions, GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0));
702 
703     *copySucceededOut = true;
704     ANGLE_TRY(scopedState.exit(context));
705     return angle::Result::Continue;
706 }
707 
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)708 angle::Result BlitGL::copySubTextureCPUReadback(const gl::Context *context,
709                                                 TextureGL *source,
710                                                 size_t sourceLevel,
711                                                 GLenum sourceSizedInternalFormat,
712                                                 TextureGL *dest,
713                                                 gl::TextureTarget destTarget,
714                                                 size_t destLevel,
715                                                 GLenum destFormat,
716                                                 GLenum destType,
717                                                 const gl::Extents &sourceSize,
718                                                 const gl::Rectangle &sourceArea,
719                                                 const gl::Offset &destOffset,
720                                                 bool needsLumaWorkaround,
721                                                 GLenum lumaFormat,
722                                                 bool unpackFlipY,
723                                                 bool unpackPremultiplyAlpha,
724                                                 bool unpackUnmultiplyAlpha)
725 {
726     ANGLE_TRY(initializeResources(context));
727 
728     ContextGL *contextGL = GetImplAs<ContextGL>(context);
729 
730     ASSERT(source->getType() == gl::TextureType::_2D ||
731            source->getType() == gl::TextureType::External ||
732            source->getType() == gl::TextureType::Rectangle);
733     const auto &destInternalFormatInfo = gl::GetInternalFormatInfo(destFormat, destType);
734     const gl::InternalFormat &sourceInternalFormatInfo =
735         gl::GetSizedInternalFormatInfo(sourceSizedInternalFormat);
736 
737     gl::Rectangle readPixelsArea = sourceArea;
738 
739     mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
740     bool supportExternalTarget =
741         source->getType() == gl::TextureType::External && context->getExtensions().YUVTargetEXT;
742     GLenum status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
743     if (supportExternalTarget || source->getType() != gl::TextureType::External)
744     {
745         ANGLE_GL_TRY(context, mFunctions->framebufferTexture2D(
746                                   GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, ToGLenum(source->getType()),
747                                   source->getTextureID(), static_cast<GLint>(sourceLevel)));
748         status = ANGLE_GL_TRY(context, mFunctions->checkFramebufferStatus(GL_FRAMEBUFFER));
749     }
750     if (status != GL_FRAMEBUFFER_COMPLETE)
751     {
752         // The source texture cannot be read with glReadPixels. Copy it into another RGBA texture
753         // and read that back instead.
754         nativegl::TexImageFormat texImageFormat = nativegl::GetTexImageFormat(
755             mFunctions, mFeatures, sourceInternalFormatInfo.internalFormat,
756             sourceInternalFormatInfo.format, sourceInternalFormatInfo.type);
757 
758         gl::TextureType scratchTextureType = gl::TextureType::_2D;
759         mStateManager->bindTexture(scratchTextureType, mScratchTextures[0]);
760         ANGLE_GL_TRY_ALWAYS_CHECK(
761             context,
762             mFunctions->texImage2D(ToGLenum(scratchTextureType), 0, texImageFormat.internalFormat,
763                                    sourceArea.width, sourceArea.height, 0, texImageFormat.format,
764                                    texImageFormat.type, nullptr));
765 
766         bool copySucceeded = false;
767         ANGLE_TRY(copySubTexture(
768             context, source, sourceLevel, sourceInternalFormatInfo.componentType,
769             mScratchTextures[0], NonCubeTextureTypeToTarget(scratchTextureType), 0,
770             sourceInternalFormatInfo.componentType, sourceSize, sourceArea, gl::Offset(0, 0, 0),
771             needsLumaWorkaround, lumaFormat, false, false, false, &copySucceeded));
772         if (!copySucceeded)
773         {
774             // No fallback options if we can't render to the scratch texture.
775             return angle::Result::Stop;
776         }
777 
778         // Bind the scratch texture as the readback texture
779         mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
780         ANGLE_GL_TRY(context, mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
781                                                                ToGLenum(scratchTextureType),
782                                                                mScratchTextures[0], 0));
783 
784         // The scratch texture sized to sourceArea so adjust the readpixels area
785         readPixelsArea.x = 0;
786         readPixelsArea.y = 0;
787 
788         // Recheck the status
789         status = ANGLE_GL_TRY(context, mFunctions->checkFramebufferStatus(GL_FRAMEBUFFER));
790     }
791 
792     ASSERT(status == GL_FRAMEBUFFER_COMPLETE);
793 
794     // Create a buffer for holding the source and destination memory
795     const size_t sourcePixelSize = 4;
796     size_t sourceBufferSize      = readPixelsArea.width * readPixelsArea.height * sourcePixelSize;
797     size_t destBufferSize =
798         readPixelsArea.width * readPixelsArea.height * destInternalFormatInfo.pixelBytes;
799     angle::MemoryBuffer *buffer = nullptr;
800     ANGLE_CHECK_GL_ALLOC(contextGL,
801                          context->getScratchBuffer(sourceBufferSize + destBufferSize, &buffer));
802 
803     uint8_t *sourceMemory = buffer->data();
804     uint8_t *destMemory   = buffer->data() + sourceBufferSize;
805 
806     GLenum readPixelsFormat        = GL_NONE;
807     PixelReadFunction readFunction = nullptr;
808     if (sourceInternalFormatInfo.componentType == GL_UNSIGNED_INT)
809     {
810         readPixelsFormat = GL_RGBA_INTEGER;
811         readFunction     = angle::ReadColor<angle::R8G8B8A8, GLuint>;
812     }
813     else
814     {
815         ASSERT(sourceInternalFormatInfo.componentType != GL_INT);
816         readPixelsFormat = GL_RGBA;
817         readFunction     = angle::ReadColor<angle::R8G8B8A8, GLfloat>;
818     }
819 
820     gl::PixelUnpackState unpack;
821     unpack.alignment = 1;
822     ANGLE_TRY(mStateManager->setPixelUnpackState(context, unpack));
823     ANGLE_TRY(mStateManager->setPixelUnpackBuffer(context, nullptr));
824     ANGLE_GL_TRY(context, mFunctions->readPixels(readPixelsArea.x, readPixelsArea.y,
825                                                  readPixelsArea.width, readPixelsArea.height,
826                                                  readPixelsFormat, GL_UNSIGNED_BYTE, sourceMemory));
827 
828     angle::FormatID destFormatID =
829         angle::Format::InternalFormatToID(destInternalFormatInfo.sizedInternalFormat);
830     const auto &destFormatInfo = angle::Format::Get(destFormatID);
831     CopyImageCHROMIUM(
832         sourceMemory, readPixelsArea.width * sourcePixelSize, sourcePixelSize, 0, readFunction,
833         destMemory, readPixelsArea.width * destInternalFormatInfo.pixelBytes,
834         destInternalFormatInfo.pixelBytes, 0, destFormatInfo.pixelWriteFunction,
835         destInternalFormatInfo.format, destInternalFormatInfo.componentType, readPixelsArea.width,
836         readPixelsArea.height, 1, unpackFlipY, unpackPremultiplyAlpha, unpackUnmultiplyAlpha);
837 
838     gl::PixelPackState pack;
839     pack.alignment = 1;
840     ANGLE_TRY(mStateManager->setPixelPackState(context, pack));
841     ANGLE_TRY(mStateManager->setPixelPackBuffer(context, nullptr));
842 
843     nativegl::TexSubImageFormat texSubImageFormat =
844         nativegl::GetTexSubImageFormat(mFunctions, mFeatures, destFormat, destType);
845 
846     mStateManager->bindTexture(dest->getType(), dest->getTextureID());
847     ANGLE_GL_TRY(context, mFunctions->texSubImage2D(
848                               ToGLenum(destTarget), static_cast<GLint>(destLevel), destOffset.x,
849                               destOffset.y, readPixelsArea.width, readPixelsArea.height,
850                               texSubImageFormat.format, texSubImageFormat.type, destMemory));
851 
852     ANGLE_TRY(UnbindAttachment(context, mFunctions, GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0));
853 
854     return angle::Result::Continue;
855 }
856 
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)857 angle::Result BlitGL::copyTexSubImage(const gl::Context *context,
858                                       TextureGL *source,
859                                       size_t sourceLevel,
860                                       TextureGL *dest,
861                                       gl::TextureTarget destTarget,
862                                       size_t destLevel,
863                                       const gl::Rectangle &sourceArea,
864                                       const gl::Offset &destOffset,
865                                       bool *copySucceededOut)
866 {
867     ANGLE_TRY(initializeResources(context));
868 
869     // Make sure the source texture can create a complete framebuffer before continuing.
870     mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
871     ANGLE_GL_TRY(context, mFunctions->framebufferTexture2D(
872                               GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, ToGLenum(source->getType()),
873                               source->getTextureID(), static_cast<GLint>(sourceLevel)));
874     GLenum status = ANGLE_GL_TRY(context, mFunctions->checkFramebufferStatus(GL_FRAMEBUFFER));
875     if (status != GL_FRAMEBUFFER_COMPLETE)
876     {
877         *copySucceededOut = false;
878         return angle::Result::Continue;
879     }
880 
881     mStateManager->bindTexture(dest->getType(), dest->getTextureID());
882 
883     // Handle GL errors during copyTexSubImage2D manually since this can fail for certain formats on
884     // Pixel 2 and 4 and we have fallback paths (blit via shader) in the caller.
885     ClearErrors(context, __FILE__, __FUNCTION__, __LINE__);
886     mFunctions->copyTexSubImage2D(ToGLenum(destTarget), static_cast<GLint>(destLevel), destOffset.x,
887                                   destOffset.y, sourceArea.x, sourceArea.y, sourceArea.width,
888                                   sourceArea.height);
889     // Use getError to retrieve the error directly instead of using CheckError so that we don't
890     // propagate the error to the client and also so that we can handle INVALID_OPERATION specially.
891     const GLenum copyError = mFunctions->getError();
892     // Any error other than NO_ERROR or INVALID_OPERATION is propagated to the client as a failure.
893     // INVALID_OPERATION is ignored and instead copySucceeded is set to false so that the caller can
894     // fallback to another copy/blit implementation.
895     if (ANGLE_UNLIKELY(copyError != GL_NO_ERROR && copyError != GL_INVALID_OPERATION))
896     {
897         // Propagate the error to the client and check for other unexpected errors.
898         ANGLE_TRY(
899             HandleError(context, copyError, "copyTexSubImage2D", __FILE__, __FUNCTION__, __LINE__));
900     }
901     // Even if copyTexSubImage2D fails with GL_INVALID_OPERATION, check for other unexpected errors.
902     ANGLE_TRY(CheckError(context, "copyTexSubImage2D", __FILE__, __FUNCTION__, __LINE__));
903 
904     ANGLE_TRY(UnbindAttachment(context, mFunctions, GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0));
905     *copySucceededOut = copyError == GL_NO_ERROR;
906     return angle::Result::Continue;
907 }
908 
clearRenderableTexture(const gl::Context * context,TextureGL * source,GLenum sizedInternalFormat,int numTextureLayers,const gl::ImageIndex & imageIndex,bool * clearSucceededOut)909 angle::Result BlitGL::clearRenderableTexture(const gl::Context *context,
910                                              TextureGL *source,
911                                              GLenum sizedInternalFormat,
912                                              int numTextureLayers,
913                                              const gl::ImageIndex &imageIndex,
914                                              bool *clearSucceededOut)
915 {
916     ANGLE_TRY(initializeResources(context));
917 
918     ClearBindTargetVector bindTargets;
919     ClearBindTargetVector unbindTargets;
920     GLbitfield clearMask = 0;
921     ANGLE_TRY(PrepareForClear(mStateManager, sizedInternalFormat, &bindTargets, &unbindTargets,
922                               &clearMask));
923 
924     mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
925     ANGLE_TRY(UnbindAttachments(context, mFunctions, GL_FRAMEBUFFER, unbindTargets));
926 
927     if (nativegl::UseTexImage2D(source->getType()))
928     {
929         ASSERT(numTextureLayers == 1);
930         for (GLenum bindTarget : bindTargets)
931         {
932             ANGLE_GL_TRY(context, mFunctions->framebufferTexture2D(
933                                       GL_FRAMEBUFFER, bindTarget, ToGLenum(imageIndex.getTarget()),
934                                       source->getTextureID(), imageIndex.getLevelIndex()));
935         }
936 
937         GLenum status = ANGLE_GL_TRY(context, mFunctions->checkFramebufferStatus(GL_FRAMEBUFFER));
938         if (status == GL_FRAMEBUFFER_COMPLETE)
939         {
940             ANGLE_GL_TRY(context, mFunctions->clear(clearMask));
941         }
942         else
943         {
944             ANGLE_TRY(UnbindAttachments(context, mFunctions, GL_FRAMEBUFFER, bindTargets));
945             *clearSucceededOut = false;
946             return angle::Result::Continue;
947         }
948     }
949     else
950     {
951         ASSERT(nativegl::UseTexImage3D(source->getType()));
952 
953         // Check if it's possible to bind all layers of the texture at once
954         if (mFunctions->framebufferTexture && !imageIndex.hasLayer())
955         {
956             for (GLenum bindTarget : bindTargets)
957             {
958                 ANGLE_GL_TRY(context, mFunctions->framebufferTexture(GL_FRAMEBUFFER, bindTarget,
959                                                                      source->getTextureID(),
960                                                                      imageIndex.getLevelIndex()));
961             }
962 
963             GLenum status =
964                 ANGLE_GL_TRY(context, mFunctions->checkFramebufferStatus(GL_FRAMEBUFFER));
965             if (status == GL_FRAMEBUFFER_COMPLETE)
966             {
967                 ANGLE_GL_TRY(context, mFunctions->clear(clearMask));
968             }
969             else
970             {
971                 ANGLE_TRY(UnbindAttachments(context, mFunctions, GL_FRAMEBUFFER, bindTargets));
972                 *clearSucceededOut = false;
973                 return angle::Result::Continue;
974             }
975         }
976         else
977         {
978             GLint firstLayer = 0;
979             GLint layerCount = numTextureLayers;
980             if (imageIndex.hasLayer())
981             {
982                 firstLayer = imageIndex.getLayerIndex();
983                 layerCount = imageIndex.getLayerCount();
984             }
985 
986             for (GLint layer = 0; layer < layerCount; layer++)
987             {
988                 for (GLenum bindTarget : bindTargets)
989                 {
990                     ANGLE_GL_TRY(context, mFunctions->framebufferTextureLayer(
991                                               GL_FRAMEBUFFER, bindTarget, source->getTextureID(),
992                                               imageIndex.getLevelIndex(), layer + firstLayer));
993                 }
994 
995                 GLenum status =
996                     ANGLE_GL_TRY(context, mFunctions->checkFramebufferStatus(GL_FRAMEBUFFER));
997                 if (status == GL_FRAMEBUFFER_COMPLETE)
998                 {
999                     ANGLE_GL_TRY(context, mFunctions->clear(clearMask));
1000                 }
1001                 else
1002                 {
1003                     ANGLE_TRY(UnbindAttachments(context, mFunctions, GL_FRAMEBUFFER, bindTargets));
1004                     *clearSucceededOut = false;
1005                     return angle::Result::Continue;
1006                 }
1007             }
1008         }
1009     }
1010 
1011     ANGLE_TRY(UnbindAttachments(context, mFunctions, GL_FRAMEBUFFER, bindTargets));
1012     *clearSucceededOut = true;
1013     return angle::Result::Continue;
1014 }
1015 
clearRenderbuffer(const gl::Context * context,RenderbufferGL * source,GLenum sizedInternalFormat)1016 angle::Result BlitGL::clearRenderbuffer(const gl::Context *context,
1017                                         RenderbufferGL *source,
1018                                         GLenum sizedInternalFormat)
1019 {
1020     ANGLE_TRY(initializeResources(context));
1021 
1022     ClearBindTargetVector bindTargets;
1023     ClearBindTargetVector unbindTargets;
1024     GLbitfield clearMask = 0;
1025     ANGLE_TRY(PrepareForClear(mStateManager, sizedInternalFormat, &bindTargets, &unbindTargets,
1026                               &clearMask));
1027 
1028     mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
1029     ANGLE_TRY(UnbindAttachments(context, mFunctions, GL_FRAMEBUFFER, unbindTargets));
1030 
1031     for (GLenum bindTarget : bindTargets)
1032     {
1033         ANGLE_GL_TRY(context,
1034                      mFunctions->framebufferRenderbuffer(
1035                          GL_FRAMEBUFFER, bindTarget, GL_RENDERBUFFER, source->getRenderbufferID()));
1036     }
1037     ANGLE_GL_TRY(context, mFunctions->clear(clearMask));
1038 
1039     // Unbind
1040     for (GLenum bindTarget : bindTargets)
1041     {
1042         ANGLE_GL_TRY(context, mFunctions->framebufferRenderbuffer(GL_FRAMEBUFFER, bindTarget,
1043                                                                   GL_RENDERBUFFER, 0));
1044     }
1045 
1046     return angle::Result::Continue;
1047 }
1048 
clearFramebuffer(const gl::Context * context,bool colorClear,bool depthClear,bool stencilClear,FramebufferGL * source)1049 angle::Result BlitGL::clearFramebuffer(const gl::Context *context,
1050                                        bool colorClear,
1051                                        bool depthClear,
1052                                        bool stencilClear,
1053                                        FramebufferGL *source)
1054 {
1055     // initializeResources skipped because no local state is used
1056 
1057     // Clear all attachments
1058     GLbitfield clearMask = 0;
1059     ANGLE_TRY(SetClearState(mStateManager, colorClear, depthClear, stencilClear, &clearMask));
1060 
1061     mStateManager->bindFramebuffer(GL_FRAMEBUFFER, source->getFramebufferID());
1062     ANGLE_GL_TRY(context, mFunctions->clear(clearMask));
1063 
1064     return angle::Result::Continue;
1065 }
1066 
clearRenderableTextureAlphaToOne(const gl::Context * context,GLuint texture,gl::TextureTarget target,size_t level)1067 angle::Result BlitGL::clearRenderableTextureAlphaToOne(const gl::Context *context,
1068                                                        GLuint texture,
1069                                                        gl::TextureTarget target,
1070                                                        size_t level)
1071 {
1072     // Clearing the alpha of 3D textures is not supported/needed yet.
1073     ASSERT(nativegl::UseTexImage2D(TextureTargetToType(target)));
1074 
1075     ANGLE_TRY(initializeResources(context));
1076 
1077     mStateManager->setClearColor(gl::ColorF(0.0f, 0.0f, 0.0f, 1.0f));
1078     mStateManager->setColorMask(false, false, false, true);
1079     mStateManager->setScissorTestEnabled(false);
1080 
1081     mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
1082     ANGLE_GL_TRY(context, mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
1083                                                            ToGLenum(target), texture,
1084                                                            static_cast<GLint>(level)));
1085     ANGLE_GL_TRY(context, mFunctions->clear(GL_COLOR_BUFFER_BIT));
1086 
1087     // Unbind the texture from the the scratch framebuffer
1088     ANGLE_TRY(UnbindAttachment(context, mFunctions, GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0));
1089 
1090     return angle::Result::Continue;
1091 }
1092 
generateSRGBMipmap(const gl::Context * context,TextureGL * source,GLuint baseLevel,GLuint levelCount,const gl::Extents & sourceBaseLevelSize)1093 angle::Result BlitGL::generateSRGBMipmap(const gl::Context *context,
1094                                          TextureGL *source,
1095                                          GLuint baseLevel,
1096                                          GLuint levelCount,
1097                                          const gl::Extents &sourceBaseLevelSize)
1098 {
1099     ANGLE_TRY(initializeResources(context));
1100 
1101     const gl::TextureType sourceType     = gl::TextureType::_2D;
1102     const gl::TextureTarget sourceTarget = gl::TextureTarget::_2D;
1103 
1104     ScopedGLState scopedState;
1105     ANGLE_TRY(scopedState.enter(
1106         context, gl::Rectangle(0, 0, sourceBaseLevelSize.width, sourceBaseLevelSize.height)));
1107     scopedState.willUseTextureUnit(context, 0);
1108     mStateManager->activeTexture(0);
1109 
1110     // Copy source to a linear intermediate texture.
1111     GLuint linearTexture = mScratchTextures[0];
1112     mStateManager->bindTexture(sourceType, linearTexture);
1113     ANGLE_GL_TRY(context, mFunctions->texImage2D(
1114                               ToGLenum(sourceTarget), 0, mSRGBMipmapGenerationFormat.internalFormat,
1115                               sourceBaseLevelSize.width, sourceBaseLevelSize.height, 0,
1116                               mSRGBMipmapGenerationFormat.format, mSRGBMipmapGenerationFormat.type,
1117                               nullptr));
1118 
1119     mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mScratchFBO);
1120     ANGLE_GL_TRY(context,
1121                  mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
1122                                                   ToGLenum(sourceTarget), linearTexture, 0));
1123     mStateManager->setFramebufferSRGBEnabled(context, true);
1124 
1125     // Use a shader to do the sRGB to linear conversion. glBlitFramebuffer does not always do this
1126     // conversion for us.
1127     BlitProgram *blitProgram = nullptr;
1128     ANGLE_TRY(getBlitProgram(context, sourceType, GL_FLOAT, GL_FLOAT, &blitProgram));
1129 
1130     mStateManager->useProgram(blitProgram->program);
1131     ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->sourceTextureLocation, 0));
1132     ANGLE_GL_TRY(context, mFunctions->uniform2f(blitProgram->scaleLocation, 1.0f, 1.0f));
1133     ANGLE_GL_TRY(context, mFunctions->uniform2f(blitProgram->offsetLocation, 0.0f, 0.0f));
1134     ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->multiplyAlphaLocation, 0));
1135     ANGLE_GL_TRY(context, mFunctions->uniform1i(blitProgram->unMultiplyAlphaLocation, 0));
1136 
1137     mStateManager->bindTexture(sourceType, source->getTextureID());
1138     ANGLE_TRY(source->setMinFilter(context, GL_NEAREST));
1139 
1140     ANGLE_TRY(setVAOState(context));
1141     ANGLE_GL_TRY(context, mFunctions->drawArrays(GL_TRIANGLES, 0, 3));
1142 
1143     // Generate mipmaps on the linear texture
1144     mStateManager->bindTexture(sourceType, linearTexture);
1145     ANGLE_GL_TRY_ALWAYS_CHECK(context, mFunctions->generateMipmap(ToGLenum(sourceTarget)));
1146     ANGLE_GL_TRY(context, mFunctions->texParameteri(ToGLenum(sourceTarget), GL_TEXTURE_MIN_FILTER,
1147                                                     GL_NEAREST));
1148 
1149     // Copy back to the source texture from the mips generated in the linear texture
1150     for (GLuint levelIdx = 0; levelIdx < levelCount; levelIdx++)
1151     {
1152         gl::Extents levelSize(std::max(sourceBaseLevelSize.width >> levelIdx, 1),
1153                               std::max(sourceBaseLevelSize.height >> levelIdx, 1), 1);
1154 
1155         ANGLE_GL_TRY(context, mFunctions->framebufferTexture2D(
1156                                   GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, ToGLenum(sourceTarget),
1157                                   source->getTextureID(), baseLevel + levelIdx));
1158         mStateManager->setViewport(gl::Rectangle(0, 0, levelSize.width, levelSize.height));
1159 
1160         ANGLE_GL_TRY(context, mFunctions->texParameteri(ToGLenum(sourceTarget),
1161                                                         GL_TEXTURE_BASE_LEVEL, levelIdx));
1162 
1163         ANGLE_GL_TRY(context, mFunctions->drawArrays(GL_TRIANGLES, 0, 3));
1164     }
1165 
1166     ANGLE_TRY(orphanScratchTextures(context));
1167     ANGLE_TRY(UnbindAttachment(context, mFunctions, GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0));
1168 
1169     ANGLE_TRY(scopedState.exit(context));
1170     return angle::Result::Continue;
1171 }
1172 
initializeResources(const gl::Context * context)1173 angle::Result BlitGL::initializeResources(const gl::Context *context)
1174 {
1175     if (mResourcesInitialized)
1176     {
1177         return angle::Result::Continue;
1178     }
1179 
1180     for (size_t i = 0; i < ArraySize(mScratchTextures); i++)
1181     {
1182         ANGLE_GL_TRY(context, mFunctions->genTextures(1, &mScratchTextures[i]));
1183     }
1184 
1185     ANGLE_GL_TRY(context, mFunctions->genFramebuffers(1, &mScratchFBO));
1186 
1187     ANGLE_GL_TRY(context, mFunctions->genBuffers(1, &mVertexBuffer));
1188     mStateManager->bindBuffer(gl::BufferBinding::Array, mVertexBuffer);
1189 
1190     // Use a single, large triangle, to avoid arithmetic precision issues where fragments
1191     // with the same Y coordinate don't get exactly the same interpolated texcoord Y.
1192     float vertexData[] = {
1193         -0.5f, 0.0f, 1.5f, 0.0f, 0.5f, 2.0f,
1194     };
1195 
1196     ANGLE_GL_TRY(context, mFunctions->bufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, vertexData,
1197                                                  GL_STATIC_DRAW));
1198 
1199     VertexArrayStateGL *defaultVAOState = mStateManager->getDefaultVAOState();
1200     if (!mFeatures.syncVertexArraysToDefault.enabled)
1201     {
1202         ANGLE_GL_TRY(context, mFunctions->genVertexArrays(1, &mVAO));
1203         mVAOState     = new VertexArrayStateGL(defaultVAOState->attributes.size(),
1204                                                defaultVAOState->bindings.size());
1205         mOwnsVAOState = true;
1206         ANGLE_TRY(setVAOState(context));
1207         ANGLE_TRY(initializeVAOState(context));
1208     }
1209     else
1210     {
1211         mVAO          = mStateManager->getDefaultVAO();
1212         mVAOState     = defaultVAOState;
1213         mOwnsVAOState = false;
1214     }
1215 
1216     constexpr GLenum potentialSRGBMipmapGenerationFormats[] = {
1217         GL_RGBA16, GL_RGBA16F, GL_RGBA32F,
1218         GL_RGBA8,  // RGBA8 can have precision loss when generating mipmaps of a sRGBA8 texture
1219     };
1220     for (GLenum internalFormat : potentialSRGBMipmapGenerationFormats)
1221     {
1222         if (nativegl::SupportsNativeRendering(mFunctions, gl::TextureType::_2D, internalFormat))
1223         {
1224             const gl::InternalFormat &internalFormatInfo =
1225                 gl::GetSizedInternalFormatInfo(internalFormat);
1226 
1227             // Pass the 'format' instead of 'internalFormat' to make sure we use unsized formats
1228             // when available to increase support.
1229             mSRGBMipmapGenerationFormat =
1230                 nativegl::GetTexImageFormat(mFunctions, mFeatures, internalFormatInfo.format,
1231                                             internalFormatInfo.format, internalFormatInfo.type);
1232             break;
1233         }
1234     }
1235     ASSERT(mSRGBMipmapGenerationFormat.internalFormat != GL_NONE);
1236 
1237     mResourcesInitialized = true;
1238     return angle::Result::Continue;
1239 }
1240 
orphanScratchTextures(const gl::Context * context)1241 angle::Result BlitGL::orphanScratchTextures(const gl::Context *context)
1242 {
1243     for (auto texture : mScratchTextures)
1244     {
1245         mStateManager->bindTexture(gl::TextureType::_2D, texture);
1246         gl::PixelUnpackState unpack;
1247         ANGLE_TRY(mStateManager->setPixelUnpackState(context, unpack));
1248         ANGLE_TRY(mStateManager->setPixelUnpackBuffer(context, nullptr));
1249         if (mFunctions->isAtLeastGL(gl::Version(3, 3)))
1250         {
1251             constexpr GLint swizzle[4] = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
1252             ANGLE_GL_TRY(context, mFunctions->texParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA,
1253                                                              swizzle));
1254         }
1255         else if (mFunctions->isAtLeastGLES(gl::Version(3, 0)))
1256         {
1257             ANGLE_GL_TRY(context,
1258                          mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED));
1259             ANGLE_GL_TRY(context,
1260                          mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN));
1261             ANGLE_GL_TRY(context,
1262                          mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_BLUE));
1263             ANGLE_GL_TRY(context,
1264                          mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ALPHA));
1265         }
1266 
1267         ANGLE_GL_TRY(context, mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0));
1268         ANGLE_GL_TRY(context, mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1000));
1269         ANGLE_GL_TRY(context, mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
1270                                                         GL_NEAREST_MIPMAP_LINEAR));
1271         ANGLE_GL_TRY(context,
1272                      mFunctions->texParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
1273         ANGLE_GL_TRY(context, mFunctions->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 0, GL_RGBA,
1274                                                      GL_UNSIGNED_BYTE, nullptr));
1275     }
1276 
1277     return angle::Result::Continue;
1278 }
1279 
setScratchTextureParameter(const gl::Context * context,GLenum param,GLenum value)1280 angle::Result BlitGL::setScratchTextureParameter(const gl::Context *context,
1281                                                  GLenum param,
1282                                                  GLenum value)
1283 {
1284     for (auto texture : mScratchTextures)
1285     {
1286         mStateManager->bindTexture(gl::TextureType::_2D, texture);
1287         ANGLE_GL_TRY(context, mFunctions->texParameteri(GL_TEXTURE_2D, param, value));
1288         ANGLE_GL_TRY(context, mFunctions->texParameteri(GL_TEXTURE_2D, param, value));
1289     }
1290     return angle::Result::Continue;
1291 }
1292 
setVAOState(const gl::Context * context)1293 angle::Result BlitGL::setVAOState(const gl::Context *context)
1294 {
1295     mStateManager->bindVertexArray(mVAO, mVAOState);
1296     if (mFeatures.syncVertexArraysToDefault.enabled)
1297     {
1298         ANGLE_TRY(initializeVAOState(context));
1299     }
1300 
1301     return angle::Result::Continue;
1302 }
1303 
initializeVAOState(const gl::Context * context)1304 angle::Result BlitGL::initializeVAOState(const gl::Context *context)
1305 {
1306     mStateManager->bindBuffer(gl::BufferBinding::Array, mVertexBuffer);
1307 
1308     ANGLE_GL_TRY(context, mFunctions->enableVertexAttribArray(mTexcoordAttribLocation));
1309     ANGLE_GL_TRY(context, mFunctions->vertexAttribPointer(mTexcoordAttribLocation, 2, GL_FLOAT,
1310                                                           GL_FALSE, 0, nullptr));
1311 
1312     VertexAttributeGL &attribute = mVAOState->attributes[mTexcoordAttribLocation];
1313     attribute.enabled            = true;
1314     attribute.format             = &angle::Format::Get(angle::FormatID::R32G32_FLOAT);
1315     attribute.pointer            = nullptr;
1316 
1317     VertexBindingGL &binding = mVAOState->bindings[mTexcoordAttribLocation];
1318     binding.stride           = 8;
1319     binding.offset           = 0;
1320     binding.buffer           = mVertexBuffer;
1321 
1322     if (mFeatures.syncVertexArraysToDefault.enabled)
1323     {
1324         mStateManager->setDefaultVAOStateDirty();
1325     }
1326 
1327     return angle::Result::Continue;
1328 }
1329 
getBlitProgram(const gl::Context * context,gl::TextureType sourceTextureType,GLenum sourceComponentType,GLenum destComponentType,BlitProgram ** program)1330 angle::Result BlitGL::getBlitProgram(const gl::Context *context,
1331                                      gl::TextureType sourceTextureType,
1332                                      GLenum sourceComponentType,
1333                                      GLenum destComponentType,
1334                                      BlitProgram **program)
1335 {
1336 
1337     BlitProgramType programType(sourceTextureType, sourceComponentType, destComponentType);
1338     BlitProgram &result = mBlitPrograms[programType];
1339     if (result.program == 0)
1340     {
1341         result.program = ANGLE_GL_TRY(context, mFunctions->createProgram());
1342 
1343         // Depending on what types need to be output by the shaders, different versions need to be
1344         // used.
1345         constexpr const char *texcoordAttribName = "a_texcoord";
1346         std::string version;
1347         std::string vsInputVariableQualifier;
1348         std::string vsOutputVariableQualifier;
1349         std::string fsInputVariableQualifier;
1350         std::string fsOutputVariableQualifier;
1351         std::string sampleFunction;
1352         if (sourceComponentType != GL_UNSIGNED_INT && destComponentType != GL_UNSIGNED_INT &&
1353             sourceTextureType != gl::TextureType::Rectangle)
1354         {
1355             // Simple case, float-to-float with 2D or external textures.  Only needs ESSL/GLSL 100
1356             version                   = "100";
1357             vsInputVariableQualifier  = "attribute";
1358             vsOutputVariableQualifier = "varying";
1359             fsInputVariableQualifier  = "varying";
1360             fsOutputVariableQualifier = "";
1361             sampleFunction            = "texture2D";
1362         }
1363         else
1364         {
1365             // Need to use a higher version to support non-float output types
1366             if (mFunctions->standard == STANDARD_GL_DESKTOP)
1367             {
1368                 version = "330";
1369             }
1370             else
1371             {
1372                 ASSERT(mFunctions->standard == STANDARD_GL_ES);
1373                 version = "300 es";
1374             }
1375             vsInputVariableQualifier  = "in";
1376             vsOutputVariableQualifier = "out";
1377             fsInputVariableQualifier  = "in";
1378             fsOutputVariableQualifier = "out";
1379             sampleFunction            = "texture";
1380         }
1381 
1382         {
1383             // Compile the vertex shader
1384             std::ostringstream vsSourceStream;
1385             vsSourceStream << "#version " << version << "\n";
1386             vsSourceStream << vsInputVariableQualifier << " vec2 " << texcoordAttribName << ";\n";
1387             vsSourceStream << "uniform vec2 u_scale;\n";
1388             vsSourceStream << "uniform vec2 u_offset;\n";
1389             vsSourceStream << vsOutputVariableQualifier << " vec2 v_texcoord;\n";
1390             vsSourceStream << "\n";
1391             vsSourceStream << "void main()\n";
1392             vsSourceStream << "{\n";
1393             vsSourceStream << "    gl_Position = vec4((" << texcoordAttribName
1394                            << " * 2.0) - 1.0, 0.0, 1.0);\n";
1395             vsSourceStream << "    v_texcoord = " << texcoordAttribName
1396                            << " * u_scale + u_offset;\n";
1397             vsSourceStream << "}\n";
1398 
1399             std::string vsSourceStr  = vsSourceStream.str();
1400             const char *vsSourceCStr = vsSourceStr.c_str();
1401 
1402             GLuint vs = ANGLE_GL_TRY(context, mFunctions->createShader(GL_VERTEX_SHADER));
1403             ANGLE_GL_TRY(context, mFunctions->shaderSource(vs, 1, &vsSourceCStr, nullptr));
1404             ANGLE_GL_TRY(context, mFunctions->compileShader(vs));
1405             ANGLE_TRY(CheckCompileStatus(context, mFunctions, vs));
1406 
1407             ANGLE_GL_TRY(context, mFunctions->attachShader(result.program, vs));
1408             ANGLE_GL_TRY(context, mFunctions->deleteShader(vs));
1409         }
1410 
1411         {
1412             // Sampling texture uniform changes depending on source texture type.
1413             std::string samplerType;
1414             switch (sourceTextureType)
1415             {
1416                 case gl::TextureType::_2D:
1417                     switch (sourceComponentType)
1418                     {
1419                         case GL_UNSIGNED_INT:
1420                             samplerType = "usampler2D";
1421                             break;
1422 
1423                         default:  // Float type
1424                             samplerType = "sampler2D";
1425                             break;
1426                     }
1427                     break;
1428 
1429                 case gl::TextureType::External:
1430                     ASSERT(sourceComponentType != GL_UNSIGNED_INT);
1431                     samplerType = "samplerExternalOES";
1432                     break;
1433 
1434                 case gl::TextureType::Rectangle:
1435                     ASSERT(sourceComponentType != GL_UNSIGNED_INT);
1436                     samplerType = "sampler2DRect";
1437                     break;
1438 
1439                 default:
1440                     UNREACHABLE();
1441                     break;
1442             }
1443 
1444             std::string samplerResultType;
1445             switch (sourceComponentType)
1446             {
1447                 case GL_UNSIGNED_INT:
1448                     samplerResultType = "uvec4";
1449                     break;
1450 
1451                 default:  // Float type
1452                     samplerResultType = "vec4";
1453                     break;
1454             }
1455 
1456             std::string extensionRequirements;
1457             switch (sourceTextureType)
1458             {
1459                 case gl::TextureType::External:
1460                     extensionRequirements = "#extension GL_OES_EGL_image_external : require";
1461                     break;
1462 
1463                 case gl::TextureType::Rectangle:
1464                     if (mFunctions->hasGLExtension("GL_ARB_texture_rectangle"))
1465                     {
1466                         extensionRequirements = "#extension GL_ARB_texture_rectangle : require";
1467                     }
1468                     else
1469                     {
1470                         ASSERT(mFunctions->isAtLeastGL(gl::Version(3, 1)));
1471                     }
1472                     break;
1473 
1474                 default:
1475                     break;
1476             }
1477 
1478             // Output variables depend on the output type
1479             std::string outputType;
1480             std::string outputVariableName;
1481             std::string outputMultiplier;
1482             switch (destComponentType)
1483             {
1484                 case GL_UNSIGNED_INT:
1485                     outputType         = "uvec4";
1486                     outputVariableName = "outputUint";
1487                     outputMultiplier   = "255.0";
1488                     break;
1489 
1490                 default:  //  float type
1491                     if (version == "100")
1492                     {
1493                         outputType         = "";
1494                         outputVariableName = "gl_FragColor";
1495                         outputMultiplier   = "1.0";
1496                     }
1497                     else
1498                     {
1499                         outputType         = "vec4";
1500                         outputVariableName = "outputFloat";
1501                         outputMultiplier   = "1.0";
1502                     }
1503                     break;
1504             }
1505 
1506             // Compile the fragment shader
1507             std::ostringstream fsSourceStream;
1508             fsSourceStream << "#version " << version << "\n";
1509             fsSourceStream << extensionRequirements << "\n";
1510             fsSourceStream << "precision highp float;\n";
1511             fsSourceStream << "uniform " << samplerType << " u_source_texture;\n";
1512 
1513             // Write the rest of the uniforms and varyings
1514             fsSourceStream << "uniform bool u_multiply_alpha;\n";
1515             fsSourceStream << "uniform bool u_unmultiply_alpha;\n";
1516             fsSourceStream << fsInputVariableQualifier << " vec2 v_texcoord;\n";
1517             if (!outputType.empty())
1518             {
1519                 fsSourceStream << fsOutputVariableQualifier << " " << outputType << " "
1520                                << outputVariableName << ";\n";
1521             }
1522 
1523             // Write the main body
1524             fsSourceStream << "\n";
1525             fsSourceStream << "void main()\n";
1526             fsSourceStream << "{\n";
1527 
1528             std::string maxTexcoord;
1529             switch (sourceTextureType)
1530             {
1531                 case gl::TextureType::Rectangle:
1532                     // Valid texcoords are within source texture size
1533                     maxTexcoord = "vec2(textureSize(u_source_texture))";
1534                     break;
1535 
1536                 default:
1537                     // Valid texcoords are in [0, 1]
1538                     maxTexcoord = "vec2(1.0)";
1539                     break;
1540             }
1541 
1542             // discard if the texcoord is invalid so the blitframebuffer workaround doesn't
1543             // write when the point sampled is outside of the source framebuffer.
1544             fsSourceStream << "    if (clamp(v_texcoord, vec2(0.0), " << maxTexcoord
1545                            << ") != v_texcoord)\n";
1546             fsSourceStream << "    {\n";
1547             fsSourceStream << "        discard;\n";
1548             fsSourceStream << "    }\n";
1549 
1550             // Sampling code depends on the input data type
1551             fsSourceStream << "    " << samplerResultType << " color = " << sampleFunction
1552                            << "(u_source_texture, v_texcoord);\n";
1553 
1554             // Perform the premultiply or unmultiply alpha logic
1555             fsSourceStream << "    if (u_multiply_alpha)\n";
1556             fsSourceStream << "    {\n";
1557             fsSourceStream << "        color.xyz = color.xyz * color.a;\n";
1558             fsSourceStream << "    }\n";
1559             fsSourceStream << "    if (u_unmultiply_alpha && color.a != 0.0)\n";
1560             fsSourceStream << "    {\n";
1561             fsSourceStream << "         color.xyz = color.xyz / color.a;\n";
1562             fsSourceStream << "    }\n";
1563 
1564             // Write the conversion to the destionation type
1565             fsSourceStream << "    color = color * " << outputMultiplier << ";\n";
1566 
1567             // Write the output assignment code
1568             fsSourceStream << "    " << outputVariableName << " = " << outputType << "(color);\n";
1569             fsSourceStream << "}\n";
1570 
1571             std::string fsSourceStr  = fsSourceStream.str();
1572             const char *fsSourceCStr = fsSourceStr.c_str();
1573 
1574             GLuint fs = ANGLE_GL_TRY(context, mFunctions->createShader(GL_FRAGMENT_SHADER));
1575             ANGLE_GL_TRY(context, mFunctions->shaderSource(fs, 1, &fsSourceCStr, nullptr));
1576             ANGLE_GL_TRY(context, mFunctions->compileShader(fs));
1577             ANGLE_TRY(CheckCompileStatus(context, mFunctions, fs));
1578 
1579             ANGLE_GL_TRY(context, mFunctions->attachShader(result.program, fs));
1580             ANGLE_GL_TRY(context, mFunctions->deleteShader(fs));
1581         }
1582 
1583         ANGLE_GL_TRY(context, mFunctions->bindAttribLocation(
1584                                   result.program, mTexcoordAttribLocation, texcoordAttribName));
1585         ANGLE_GL_TRY(context, mFunctions->linkProgram(result.program));
1586         ANGLE_TRY(CheckLinkStatus(context, mFunctions, result.program));
1587 
1588         result.sourceTextureLocation = ANGLE_GL_TRY(
1589             context, mFunctions->getUniformLocation(result.program, "u_source_texture"));
1590         result.scaleLocation =
1591             ANGLE_GL_TRY(context, mFunctions->getUniformLocation(result.program, "u_scale"));
1592         result.offsetLocation =
1593             ANGLE_GL_TRY(context, mFunctions->getUniformLocation(result.program, "u_offset"));
1594         result.multiplyAlphaLocation = ANGLE_GL_TRY(
1595             context, mFunctions->getUniformLocation(result.program, "u_multiply_alpha"));
1596         result.unMultiplyAlphaLocation = ANGLE_GL_TRY(
1597             context, mFunctions->getUniformLocation(result.program, "u_unmultiply_alpha"));
1598     }
1599 
1600     *program = &result;
1601     return angle::Result::Continue;
1602 }
1603 
1604 }  // namespace rx
1605