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