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