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