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