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