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