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