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