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 // FramebufferGL.cpp: Implements the class methods for FramebufferGL.
8
9 #include "libANGLE/renderer/gl/FramebufferGL.h"
10
11 #include "common/bitset_utils.h"
12 #include "common/debug.h"
13 #include "libANGLE/ErrorStrings.h"
14 #include "libANGLE/FramebufferAttachment.h"
15 #include "libANGLE/State.h"
16 #include "libANGLE/angletypes.h"
17 #include "libANGLE/formatutils.h"
18 #include "libANGLE/queryconversions.h"
19 #include "libANGLE/renderer/ContextImpl.h"
20 #include "libANGLE/renderer/gl/BlitGL.h"
21 #include "libANGLE/renderer/gl/ClearMultiviewGL.h"
22 #include "libANGLE/renderer/gl/ContextGL.h"
23 #include "libANGLE/renderer/gl/FunctionsGL.h"
24 #include "libANGLE/renderer/gl/RenderbufferGL.h"
25 #include "libANGLE/renderer/gl/StateManagerGL.h"
26 #include "libANGLE/renderer/gl/TextureGL.h"
27 #include "libANGLE/renderer/gl/formatutilsgl.h"
28 #include "libANGLE/renderer/gl/renderergl_utils.h"
29 #include "platform/PlatformMethods.h"
30 #include "platform/autogen/FeaturesGL_autogen.h"
31
32 using namespace gl;
33 using angle::CheckedNumeric;
34
35 namespace rx
36 {
37
38 namespace
39 {
40
41 struct BlitFramebufferBounds
42 {
43 gl::Rectangle sourceBounds;
44 gl::Rectangle sourceRegion;
45
46 gl::Rectangle destBounds;
47 gl::Rectangle destRegion;
48
49 bool xFlipped;
50 bool yFlipped;
51 };
52
GetBlitFramebufferBounds(const gl::Context * context,const gl::Rectangle & sourceArea,const gl::Rectangle & destArea)53 static BlitFramebufferBounds GetBlitFramebufferBounds(const gl::Context *context,
54 const gl::Rectangle &sourceArea,
55 const gl::Rectangle &destArea)
56 {
57 BlitFramebufferBounds bounds;
58
59 const Framebuffer *sourceFramebuffer = context->getState().getReadFramebuffer();
60 const Framebuffer *destFramebuffer = context->getState().getDrawFramebuffer();
61
62 gl::Extents readSize = sourceFramebuffer->getExtents();
63 gl::Extents drawSize = destFramebuffer->getExtents();
64
65 bounds.sourceBounds = gl::Rectangle(0, 0, readSize.width, readSize.height);
66 bounds.sourceRegion = sourceArea.removeReversal();
67
68 bounds.destBounds = gl::Rectangle(0, 0, drawSize.width, drawSize.height);
69 bounds.destRegion = destArea.removeReversal();
70
71 bounds.xFlipped = sourceArea.isReversedX() != destArea.isReversedX();
72 bounds.yFlipped = sourceArea.isReversedY() != destArea.isReversedY();
73
74 return bounds;
75 }
76
BindFramebufferAttachment(const FunctionsGL * functions,GLenum attachmentPoint,const FramebufferAttachment * attachment,const angle::FeaturesGL & features)77 void BindFramebufferAttachment(const FunctionsGL *functions,
78 GLenum attachmentPoint,
79 const FramebufferAttachment *attachment,
80 const angle::FeaturesGL &features)
81 {
82 if (attachment)
83 {
84 if (attachment->type() == GL_TEXTURE)
85 {
86 const Texture *texture = attachment->getTexture();
87 const TextureGL *textureGL = GetImplAs<TextureGL>(texture);
88
89 if (texture->getType() == TextureType::_2D ||
90 texture->getType() == TextureType::_2DMultisample ||
91 texture->getType() == TextureType::Rectangle ||
92 texture->getType() == TextureType::External)
93 {
94 if (attachment->isRenderToTexture())
95 {
96 if (functions->framebufferTexture2DMultisampleEXT)
97 {
98 functions->framebufferTexture2DMultisampleEXT(
99 GL_FRAMEBUFFER, attachmentPoint, ToGLenum(texture->getType()),
100 textureGL->getTextureID(), attachment->mipLevel(),
101 attachment->getSamples());
102 }
103 else
104 {
105 ASSERT(functions->framebufferTexture2DMultisampleIMG);
106 functions->framebufferTexture2DMultisampleIMG(
107 GL_FRAMEBUFFER, attachmentPoint, ToGLenum(texture->getType()),
108 textureGL->getTextureID(), attachment->mipLevel(),
109 attachment->getSamples());
110 }
111 }
112 else
113 {
114 functions->framebufferTexture2D(
115 GL_FRAMEBUFFER, attachmentPoint, ToGLenum(texture->getType()),
116 textureGL->getTextureID(), attachment->mipLevel());
117 }
118 }
119 else if (attachment->isLayered())
120 {
121 TextureType textureType = texture->getType();
122 ASSERT(textureType == TextureType::_2DArray || textureType == TextureType::_3D ||
123 textureType == TextureType::CubeMap ||
124 textureType == TextureType::_2DMultisampleArray ||
125 textureType == TextureType::CubeMapArray);
126 functions->framebufferTexture(GL_FRAMEBUFFER, attachmentPoint,
127 textureGL->getTextureID(), attachment->mipLevel());
128 }
129 else if (texture->getType() == TextureType::CubeMap)
130 {
131 functions->framebufferTexture2D(GL_FRAMEBUFFER, attachmentPoint,
132 ToGLenum(attachment->cubeMapFace()),
133 textureGL->getTextureID(), attachment->mipLevel());
134 }
135 else if (texture->getType() == TextureType::_2DArray ||
136 texture->getType() == TextureType::_3D ||
137 texture->getType() == TextureType::_2DMultisampleArray ||
138 texture->getType() == TextureType::CubeMapArray)
139 {
140 if (attachment->isMultiview())
141 {
142 ASSERT(functions->framebufferTexture);
143 functions->framebufferTexture(GL_FRAMEBUFFER, attachmentPoint,
144 textureGL->getTextureID(),
145 attachment->mipLevel());
146 }
147 else
148 {
149 functions->framebufferTextureLayer(GL_FRAMEBUFFER, attachmentPoint,
150 textureGL->getTextureID(),
151 attachment->mipLevel(), attachment->layer());
152 }
153 }
154 else
155 {
156 UNREACHABLE();
157 }
158 }
159 else if (attachment->type() == GL_RENDERBUFFER)
160 {
161 const Renderbuffer *renderbuffer = attachment->getRenderbuffer();
162 const RenderbufferGL *renderbufferGL = GetImplAs<RenderbufferGL>(renderbuffer);
163
164 if (features.alwaysUnbindFramebufferTexture2D.enabled)
165 {
166 functions->framebufferTexture2D(GL_FRAMEBUFFER, attachmentPoint, GL_TEXTURE_2D, 0,
167 0);
168 }
169
170 functions->framebufferRenderbuffer(GL_FRAMEBUFFER, attachmentPoint, GL_RENDERBUFFER,
171 renderbufferGL->getRenderbufferID());
172 }
173 else
174 {
175 UNREACHABLE();
176 }
177 }
178 else
179 {
180 // Unbind this attachment
181 functions->framebufferTexture2D(GL_FRAMEBUFFER, attachmentPoint, GL_TEXTURE_2D, 0, 0);
182 }
183 }
184
AreAllLayersActive(const FramebufferAttachment & attachment)185 bool AreAllLayersActive(const FramebufferAttachment &attachment)
186 {
187 int baseViewIndex = attachment.getBaseViewIndex();
188 if (baseViewIndex != 0)
189 {
190 return false;
191 }
192 const ImageIndex &imageIndex = attachment.getTextureImageIndex();
193 int numLayers = static_cast<int>(
194 attachment.getTexture()->getDepth(imageIndex.getTarget(), imageIndex.getLevelIndex()));
195 return (attachment.getNumViews() == numLayers);
196 }
197
RequiresMultiviewClear(const FramebufferState & state,bool scissorTestEnabled)198 bool RequiresMultiviewClear(const FramebufferState &state, bool scissorTestEnabled)
199 {
200 // Get one attachment and check whether all layers are attached.
201 const FramebufferAttachment *attachment = nullptr;
202 bool allTextureArraysAreFullyAttached = true;
203 for (const FramebufferAttachment &colorAttachment : state.getColorAttachments())
204 {
205 if (colorAttachment.isAttached())
206 {
207 if (!colorAttachment.isMultiview())
208 {
209 return false;
210 }
211 attachment = &colorAttachment;
212 allTextureArraysAreFullyAttached =
213 allTextureArraysAreFullyAttached && AreAllLayersActive(*attachment);
214 }
215 }
216
217 const FramebufferAttachment *depthAttachment = state.getDepthAttachment();
218 if (depthAttachment)
219 {
220 if (!depthAttachment->isMultiview())
221 {
222 return false;
223 }
224 attachment = depthAttachment;
225 allTextureArraysAreFullyAttached =
226 allTextureArraysAreFullyAttached && AreAllLayersActive(*attachment);
227 }
228 const FramebufferAttachment *stencilAttachment = state.getStencilAttachment();
229 if (stencilAttachment)
230 {
231 if (!stencilAttachment->isMultiview())
232 {
233 return false;
234 }
235 attachment = stencilAttachment;
236 allTextureArraysAreFullyAttached =
237 allTextureArraysAreFullyAttached && AreAllLayersActive(*attachment);
238 }
239
240 if (attachment == nullptr)
241 {
242 return false;
243 }
244 if (attachment->isMultiview())
245 {
246 // If all layers of each texture array are active, then there is no need to issue a
247 // special multiview clear.
248 return !allTextureArraysAreFullyAttached;
249 }
250 return false;
251 }
252
IsEmulatedAlphaChannelTextureAttachment(const FramebufferAttachment * attachment)253 bool IsEmulatedAlphaChannelTextureAttachment(const FramebufferAttachment *attachment)
254 {
255 if (!attachment || attachment->type() != GL_TEXTURE)
256 {
257 return false;
258 }
259
260 const Texture *texture = attachment->getTexture();
261 const TextureGL *textureGL = GetImplAs<TextureGL>(texture);
262 return textureGL->hasEmulatedAlphaChannel(attachment->getTextureImageIndex());
263 }
264
265 class [[nodiscard]] ScopedEXTTextureNorm16ReadbackWorkaround
266 {
267 public:
ScopedEXTTextureNorm16ReadbackWorkaround()268 ScopedEXTTextureNorm16ReadbackWorkaround()
269 : tmpPixels(nullptr), clientPixels(nullptr), enabled(false)
270 {}
271
~ScopedEXTTextureNorm16ReadbackWorkaround()272 ~ScopedEXTTextureNorm16ReadbackWorkaround()
273 {
274 if (tmpPixels)
275 {
276 delete[] tmpPixels;
277 }
278 }
279
Initialize(const gl::Context * context,const gl::Rectangle & area,GLenum originalReadFormat,GLenum format,GLenum type,GLuint skipBytes,GLuint rowBytes,GLuint pixelBytes,GLubyte * pixels)280 angle::Result Initialize(const gl::Context *context,
281 const gl::Rectangle &area,
282 GLenum originalReadFormat,
283 GLenum format,
284 GLenum type,
285 GLuint skipBytes,
286 GLuint rowBytes,
287 GLuint pixelBytes,
288 GLubyte *pixels)
289 {
290 // Separate from constructor as there may be checked math result exception that needs to
291 // early return
292 ASSERT(tmpPixels == nullptr);
293 ASSERT(clientPixels == nullptr);
294
295 ContextGL *contextGL = GetImplAs<ContextGL>(context);
296 const angle::FeaturesGL &features = GetFeaturesGL(context);
297
298 enabled = features.readPixelsUsingImplementationColorReadFormatForNorm16.enabled &&
299 type == GL_UNSIGNED_SHORT && originalReadFormat == GL_RGBA &&
300 (format == GL_RED || format == GL_RG);
301
302 clientPixels = pixels;
303
304 if (enabled)
305 {
306 CheckedNumeric<GLuint> checkedRowBytes(rowBytes);
307 CheckedNumeric<GLuint> checkedRows(area.height);
308 CheckedNumeric<GLuint> checkedSkipBytes(skipBytes);
309 auto checkedAllocatedBytes = checkedSkipBytes + checkedRowBytes * checkedRows;
310 if (rowBytes < area.width * pixelBytes)
311 {
312 checkedAllocatedBytes += area.width * pixelBytes - rowBytes;
313 }
314 ANGLE_CHECK_GL_MATH(contextGL, checkedAllocatedBytes.IsValid());
315 const GLuint allocatedBytes = checkedAllocatedBytes.ValueOrDie();
316 tmpPixels = new GLubyte[allocatedBytes];
317 memset(tmpPixels, 0, allocatedBytes);
318 }
319
320 return angle::Result::Continue;
321 }
322
Pixels() const323 GLubyte *Pixels() const { return tmpPixels ? tmpPixels : clientPixels; }
324
IsEnabled() const325 bool IsEnabled() const { return enabled; }
326
327 private:
328 // Temporarily allocated pixel readback buffer
329 GLubyte *tmpPixels;
330 // Client pixel array pointer passed to readPixels
331 GLubyte *clientPixels;
332
333 bool enabled;
334 };
335
336 // Workaround to rearrange pixels read by RED/RG to RGBA for RGBA/UNSIGNED_SHORT pixel type
337 // combination
RearrangeEXTTextureNorm16Pixels(const gl::Context * context,const gl::Rectangle & area,GLenum originalReadFormat,GLenum format,GLenum type,GLuint skipBytes,GLuint rowBytes,GLuint pixelBytes,const gl::PixelPackState & pack,GLubyte * clientPixels,GLubyte * tmpPixels)338 angle::Result RearrangeEXTTextureNorm16Pixels(const gl::Context *context,
339 const gl::Rectangle &area,
340 GLenum originalReadFormat,
341 GLenum format,
342 GLenum type,
343 GLuint skipBytes,
344 GLuint rowBytes,
345 GLuint pixelBytes,
346 const gl::PixelPackState &pack,
347 GLubyte *clientPixels,
348 GLubyte *tmpPixels)
349 {
350 ASSERT(tmpPixels != nullptr);
351 ASSERT(originalReadFormat == GL_RGBA);
352 ASSERT(format == GL_RED_EXT || format == GL_RG_EXT);
353 ASSERT(type == GL_UNSIGNED_SHORT);
354
355 ContextGL *contextGL = GetImplAs<ContextGL>(context);
356
357 const gl::InternalFormat &glFormatOriginal =
358 gl::GetInternalFormatInfo(originalReadFormat, type);
359
360 GLuint originalReadFormatRowBytes = 0;
361 ANGLE_CHECK_GL_MATH(
362 contextGL, glFormatOriginal.computeRowPitch(type, area.width, pack.alignment,
363 pack.rowLength, &originalReadFormatRowBytes));
364 GLuint originalReadFormatSkipBytes = 0;
365 ANGLE_CHECK_GL_MATH(contextGL,
366 glFormatOriginal.computeSkipBytes(type, originalReadFormatRowBytes, 0, pack,
367 false, &originalReadFormatSkipBytes));
368
369 GLuint originalReadFormatPixelBytes = glFormatOriginal.computePixelBytes(type);
370 GLuint alphaChannelBytes = glFormatOriginal.alphaBits / 8;
371
372 ASSERT(originalReadFormatPixelBytes > pixelBytes);
373 ASSERT(originalReadFormatPixelBytes > alphaChannelBytes);
374 ASSERT(alphaChannelBytes != 0);
375 ASSERT(glFormatOriginal.alphaBits % 8 == 0);
376
377 // Populating rearrangedPixels values from pixels
378 GLubyte *srcRowStart = tmpPixels;
379 GLubyte *dstRowStart = clientPixels;
380
381 srcRowStart += skipBytes;
382 dstRowStart += originalReadFormatSkipBytes;
383
384 for (GLint y = 0; y < area.height; ++y)
385 {
386 GLubyte *src = srcRowStart;
387 GLubyte *dst = dstRowStart;
388 for (GLint x = 0; x < area.width; ++x)
389 {
390 GLushort *srcPixel = reinterpret_cast<GLushort *>(src);
391 GLushort *dstPixel = reinterpret_cast<GLushort *>(dst);
392 dstPixel[0] = srcPixel[0];
393 dstPixel[1] = format == GL_RG ? srcPixel[1] : 0;
394 // Set other channel of RGBA to 0 (GB when format == GL_RED, B when format == GL_RG)
395 dstPixel[2] = 0;
396 // Set alpha channel to 1
397 dstPixel[3] = 0xFFFF;
398
399 src += pixelBytes;
400 dst += originalReadFormatPixelBytes;
401 }
402
403 srcRowStart += rowBytes;
404 dstRowStart += originalReadFormatRowBytes;
405 }
406
407 return angle::Result::Continue;
408 }
409
IsValidUnsignedShortReadPixelsFormat(GLenum readFormat,const gl::Context * context)410 bool IsValidUnsignedShortReadPixelsFormat(GLenum readFormat, const gl::Context *context)
411 {
412 return (readFormat == GL_RED) || (readFormat == GL_RG) || (readFormat == GL_RGBA) ||
413 ((readFormat == GL_DEPTH_COMPONENT) && (context->getExtensions().readDepthNV));
414 }
415
416 // Returns true for all colors except
417 // - transparent/opaque black
418 // - transparent/opaque white
IsNonTrivialClearColor(const GLfloat * color)419 bool IsNonTrivialClearColor(const GLfloat *color)
420 {
421 return !(((color[0] == 0.0f && color[1] == 0.0f && color[2] == 0.0f) ||
422 (color[0] == 1.0f && color[1] == 1.0f && color[2] == 1.0f)) &&
423 (color[3] == 0.0f || color[3] == 1.0f));
424 }
425
426 // Returns true for all colors except
427 // - (0, 0, 0, 0 or 1)
428 // - (1, 1, 1, 0 or 1)
IsNonTrivialClearColor(const GLuint * color)429 bool IsNonTrivialClearColor(const GLuint *color)
430 {
431 return !(((color[0] == 0 && color[1] == 0 && color[2] == 0) ||
432 (color[0] == 1 && color[1] == 1 && color[2] == 1)) &&
433 (color[3] == 0 || color[3] == 1));
434 }
435
436 } // namespace
437
FramebufferGL(const gl::FramebufferState & data,GLuint id,bool emulatedAlpha)438 FramebufferGL::FramebufferGL(const gl::FramebufferState &data, GLuint id, bool emulatedAlpha)
439 : FramebufferImpl(data),
440 mFramebufferID(id),
441 mHasEmulatedAlphaAttachment(emulatedAlpha),
442 mAppliedEnabledDrawBuffers(1)
443 {
444 ASSERT((isDefault() && id == 0) || !isDefault());
445 }
446
~FramebufferGL()447 FramebufferGL::~FramebufferGL()
448 {
449 ASSERT(mFramebufferID == 0);
450 }
451
destroy(const gl::Context * context)452 void FramebufferGL::destroy(const gl::Context *context)
453 {
454 if (mFramebufferID)
455 {
456 ASSERT(!isDefault());
457 StateManagerGL *stateManager = GetStateManagerGL(context);
458 stateManager->deleteFramebuffer(mFramebufferID);
459 mFramebufferID = 0;
460 }
461 }
462
discard(const gl::Context * context,size_t count,const GLenum * attachments)463 angle::Result FramebufferGL::discard(const gl::Context *context,
464 size_t count,
465 const GLenum *attachments)
466 {
467 // glInvalidateFramebuffer accepts the same enums as glDiscardFramebufferEXT
468 return invalidate(context, count, attachments);
469 }
470
invalidate(const gl::Context * context,size_t count,const GLenum * attachments)471 angle::Result FramebufferGL::invalidate(const gl::Context *context,
472 size_t count,
473 const GLenum *attachments)
474 {
475 const GLenum *finalAttachmentsPtr = attachments;
476
477 std::vector<GLenum> modifiedAttachments;
478 if (modifyInvalidateAttachmentsForEmulatedDefaultFBO(count, attachments, &modifiedAttachments))
479 {
480 finalAttachmentsPtr = modifiedAttachments.data();
481 }
482
483 const FunctionsGL *functions = GetFunctionsGL(context);
484 StateManagerGL *stateManager = GetStateManagerGL(context);
485
486 // Since this function is just a hint, only call a native function if it exists.
487 if (functions->invalidateFramebuffer)
488 {
489 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
490 functions->invalidateFramebuffer(GL_FRAMEBUFFER, static_cast<GLsizei>(count),
491 finalAttachmentsPtr);
492 }
493 else if (functions->discardFramebufferEXT)
494 {
495 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
496 functions->discardFramebufferEXT(GL_FRAMEBUFFER, static_cast<GLsizei>(count),
497 finalAttachmentsPtr);
498 }
499
500 return angle::Result::Continue;
501 }
502
invalidateSub(const gl::Context * context,size_t count,const GLenum * attachments,const gl::Rectangle & area)503 angle::Result FramebufferGL::invalidateSub(const gl::Context *context,
504 size_t count,
505 const GLenum *attachments,
506 const gl::Rectangle &area)
507 {
508
509 const GLenum *finalAttachmentsPtr = attachments;
510
511 std::vector<GLenum> modifiedAttachments;
512 if (modifyInvalidateAttachmentsForEmulatedDefaultFBO(count, attachments, &modifiedAttachments))
513 {
514 finalAttachmentsPtr = modifiedAttachments.data();
515 }
516
517 const FunctionsGL *functions = GetFunctionsGL(context);
518 StateManagerGL *stateManager = GetStateManagerGL(context);
519
520 // Since this function is just a hint and not available until OpenGL 4.3, only call it if it is
521 // available.
522 if (functions->invalidateSubFramebuffer)
523 {
524 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
525 functions->invalidateSubFramebuffer(GL_FRAMEBUFFER, static_cast<GLsizei>(count),
526 finalAttachmentsPtr, area.x, area.y, area.width,
527 area.height);
528 }
529
530 return angle::Result::Continue;
531 }
532
clear(const gl::Context * context,GLbitfield mask)533 angle::Result FramebufferGL::clear(const gl::Context *context, GLbitfield mask)
534 {
535 ContextGL *contextGL = GetImplAs<ContextGL>(context);
536 const FunctionsGL *functions = GetFunctionsGL(context);
537 StateManagerGL *stateManager = GetStateManagerGL(context);
538
539 syncClearState(context, mask);
540 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
541
542 if (!RequiresMultiviewClear(mState, context->getState().isScissorTestEnabled()))
543 {
544 functions->clear(mask);
545 }
546 else
547 {
548 ClearMultiviewGL *multiviewClearer = GetMultiviewClearer(context);
549 multiviewClearer->clearMultiviewFBO(mState, context->getState().getScissor(),
550 ClearMultiviewGL::ClearCommandType::Clear, mask,
551 GL_NONE, 0, nullptr, 0.0f, 0);
552 }
553
554 contextGL->markWorkSubmitted();
555
556 // Perform cheaper checks first, relying on short-circuiting
557 if ((mask & GL_COLOR_BUFFER_BIT) != 0 && mState.getEnabledDrawBuffers().hasGaps() &&
558 GetFeaturesGL(context).clearsWithGapsNeedFlush.enabled &&
559 IsNonTrivialClearColor(context->getState().getColorClearValue().data()))
560 {
561 return contextGL->flush(context);
562 }
563
564 return angle::Result::Continue;
565 }
566
clearBufferfv(const gl::Context * context,GLenum buffer,GLint drawbuffer,const GLfloat * values)567 angle::Result FramebufferGL::clearBufferfv(const gl::Context *context,
568 GLenum buffer,
569 GLint drawbuffer,
570 const GLfloat *values)
571 {
572 ContextGL *contextGL = GetImplAs<ContextGL>(context);
573 const FunctionsGL *functions = GetFunctionsGL(context);
574 StateManagerGL *stateManager = GetStateManagerGL(context);
575
576 syncClearBufferState(context, buffer, drawbuffer);
577 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
578
579 if (!RequiresMultiviewClear(mState, context->getState().isScissorTestEnabled()))
580 {
581 functions->clearBufferfv(buffer, drawbuffer, values);
582 }
583 else
584 {
585 ClearMultiviewGL *multiviewClearer = GetMultiviewClearer(context);
586 multiviewClearer->clearMultiviewFBO(mState, context->getState().getScissor(),
587 ClearMultiviewGL::ClearCommandType::ClearBufferfv,
588 static_cast<GLbitfield>(0u), buffer, drawbuffer,
589 reinterpret_cast<const uint8_t *>(values), 0.0f, 0);
590 }
591
592 contextGL->markWorkSubmitted();
593
594 // Perform cheaper checks first, relying on short-circuiting
595 if (buffer == GL_COLOR && mState.getEnabledDrawBuffers().hasGaps() &&
596 GetFeaturesGL(context).clearsWithGapsNeedFlush.enabled && IsNonTrivialClearColor(values))
597 {
598 return contextGL->flush(context);
599 }
600
601 return angle::Result::Continue;
602 }
603
clearBufferuiv(const gl::Context * context,GLenum buffer,GLint drawbuffer,const GLuint * values)604 angle::Result FramebufferGL::clearBufferuiv(const gl::Context *context,
605 GLenum buffer,
606 GLint drawbuffer,
607 const GLuint *values)
608 {
609 ContextGL *contextGL = GetImplAs<ContextGL>(context);
610 const FunctionsGL *functions = GetFunctionsGL(context);
611 StateManagerGL *stateManager = GetStateManagerGL(context);
612
613 syncClearBufferState(context, buffer, drawbuffer);
614 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
615
616 if (!RequiresMultiviewClear(mState, context->getState().isScissorTestEnabled()))
617 {
618 functions->clearBufferuiv(buffer, drawbuffer, values);
619 }
620 else
621 {
622 ClearMultiviewGL *multiviewClearer = GetMultiviewClearer(context);
623 multiviewClearer->clearMultiviewFBO(mState, context->getState().getScissor(),
624 ClearMultiviewGL::ClearCommandType::ClearBufferuiv,
625 static_cast<GLbitfield>(0u), buffer, drawbuffer,
626 reinterpret_cast<const uint8_t *>(values), 0.0f, 0);
627 }
628
629 contextGL->markWorkSubmitted();
630
631 // Perform cheaper checks first, relying on short-circuiting
632 if (mState.getEnabledDrawBuffers().hasGaps() &&
633 GetFeaturesGL(context).clearsWithGapsNeedFlush.enabled && IsNonTrivialClearColor(values))
634 {
635 return contextGL->flush(context);
636 }
637
638 return angle::Result::Continue;
639 }
640
clearBufferiv(const gl::Context * context,GLenum buffer,GLint drawbuffer,const GLint * values)641 angle::Result FramebufferGL::clearBufferiv(const gl::Context *context,
642 GLenum buffer,
643 GLint drawbuffer,
644 const GLint *values)
645 {
646 ContextGL *contextGL = GetImplAs<ContextGL>(context);
647 const FunctionsGL *functions = GetFunctionsGL(context);
648 StateManagerGL *stateManager = GetStateManagerGL(context);
649
650 syncClearBufferState(context, buffer, drawbuffer);
651 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
652
653 if (!RequiresMultiviewClear(mState, context->getState().isScissorTestEnabled()))
654 {
655 functions->clearBufferiv(buffer, drawbuffer, values);
656 }
657 else
658 {
659 ClearMultiviewGL *multiviewClearer = GetMultiviewClearer(context);
660 multiviewClearer->clearMultiviewFBO(mState, context->getState().getScissor(),
661 ClearMultiviewGL::ClearCommandType::ClearBufferiv,
662 static_cast<GLbitfield>(0u), buffer, drawbuffer,
663 reinterpret_cast<const uint8_t *>(values), 0.0f, 0);
664 }
665
666 contextGL->markWorkSubmitted();
667
668 // Perform cheaper checks first, relying on short-circuiting
669 if (buffer == GL_COLOR && mState.getEnabledDrawBuffers().hasGaps() &&
670 GetFeaturesGL(context).clearsWithGapsNeedFlush.enabled &&
671 IsNonTrivialClearColor(reinterpret_cast<const GLuint *>(values)))
672 {
673 return contextGL->flush(context);
674 }
675
676 return angle::Result::Continue;
677 }
678
clearBufferfi(const gl::Context * context,GLenum buffer,GLint drawbuffer,GLfloat depth,GLint stencil)679 angle::Result FramebufferGL::clearBufferfi(const gl::Context *context,
680 GLenum buffer,
681 GLint drawbuffer,
682 GLfloat depth,
683 GLint stencil)
684 {
685 ContextGL *contextGL = GetImplAs<ContextGL>(context);
686 const FunctionsGL *functions = GetFunctionsGL(context);
687 StateManagerGL *stateManager = GetStateManagerGL(context);
688
689 syncClearBufferState(context, buffer, drawbuffer);
690 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
691
692 if (!RequiresMultiviewClear(mState, context->getState().isScissorTestEnabled()))
693 {
694 functions->clearBufferfi(buffer, drawbuffer, depth, stencil);
695 }
696 else
697 {
698 ClearMultiviewGL *multiviewClearer = GetMultiviewClearer(context);
699 multiviewClearer->clearMultiviewFBO(mState, context->getState().getScissor(),
700 ClearMultiviewGL::ClearCommandType::ClearBufferfi,
701 static_cast<GLbitfield>(0u), buffer, drawbuffer,
702 nullptr, depth, stencil);
703 }
704
705 contextGL->markWorkSubmitted();
706 return angle::Result::Continue;
707 }
708
readPixels(const gl::Context * context,const gl::Rectangle & area,GLenum format,GLenum type,const gl::PixelPackState & pack,gl::Buffer * packBuffer,void * pixels)709 angle::Result FramebufferGL::readPixels(const gl::Context *context,
710 const gl::Rectangle &area,
711 GLenum format,
712 GLenum type,
713 const gl::PixelPackState &pack,
714 gl::Buffer *packBuffer,
715 void *pixels)
716 {
717 ContextGL *contextGL = GetImplAs<ContextGL>(context);
718 const FunctionsGL *functions = GetFunctionsGL(context);
719 StateManagerGL *stateManager = GetStateManagerGL(context);
720 const angle::FeaturesGL &features = GetFeaturesGL(context);
721 gl::PixelPackState packState = pack;
722
723 // Clip read area to framebuffer.
724 const auto *readAttachment = mState.getReadPixelsAttachment(format);
725 const gl::Extents fbSize = readAttachment->getSize();
726 const gl::Rectangle fbRect(0, 0, fbSize.width, fbSize.height);
727 gl::Rectangle clippedArea;
728 if (!ClipRectangle(area, fbRect, &clippedArea))
729 {
730 // nothing to read
731 return angle::Result::Continue;
732 }
733
734 GLenum attachmentReadFormat =
735 readAttachment->getFormat().info->getReadPixelsFormat(context->getExtensions());
736 nativegl::ReadPixelsFormat readPixelsFormat =
737 nativegl::GetReadPixelsFormat(functions, features, attachmentReadFormat, format, type);
738 GLenum readFormat = readPixelsFormat.format;
739 GLenum readType = readPixelsFormat.type;
740 if (features.readPixelsUsingImplementationColorReadFormatForNorm16.enabled &&
741 readType == GL_UNSIGNED_SHORT)
742 {
743 ANGLE_CHECK(contextGL, IsValidUnsignedShortReadPixelsFormat(readFormat, context),
744 "glReadPixels: GL_IMPLEMENTATION_COLOR_READ_FORMAT advertised by the driver is "
745 "not handled by RGBA16 readPixels workaround.",
746 GL_INVALID_OPERATION);
747 }
748
749 GLenum framebufferTarget =
750 stateManager->getHasSeparateFramebufferBindings() ? GL_READ_FRAMEBUFFER : GL_FRAMEBUFFER;
751 stateManager->bindFramebuffer(framebufferTarget, mFramebufferID);
752
753 bool useOverlappingRowsWorkaround = features.packOverlappingRowsSeparatelyPackBuffer.enabled &&
754 packBuffer && packState.rowLength != 0 &&
755 packState.rowLength < clippedArea.width;
756
757 GLubyte *outPtr = static_cast<GLubyte *>(pixels);
758 int leftClip = clippedArea.x - area.x;
759 int topClip = clippedArea.y - area.y;
760 if (leftClip || topClip)
761 {
762 // Adjust destination to match portion clipped off left and/or top.
763 const gl::InternalFormat &glFormat = gl::GetInternalFormatInfo(readFormat, readType);
764
765 GLuint rowBytes = 0;
766 ANGLE_CHECK_GL_MATH(contextGL,
767 glFormat.computeRowPitch(readType, area.width, packState.alignment,
768 packState.rowLength, &rowBytes));
769 outPtr += leftClip * glFormat.pixelBytes + topClip * rowBytes;
770 }
771
772 if (packState.rowLength == 0 && clippedArea.width != area.width)
773 {
774 // No rowLength was specified so it will derive from read width, but clipping changed the
775 // read width. Use the original width so we fill the user's buffer as they intended.
776 packState.rowLength = area.width;
777 }
778
779 // We want to use rowLength, but that might not be supported.
780 bool cannotSetDesiredRowLength =
781 packState.rowLength && !GetImplAs<ContextGL>(context)->getNativeExtensions().packSubimageNV;
782
783 bool usePackSkipWorkaround = features.emulatePackSkipRowsAndPackSkipPixels.enabled &&
784 (packState.skipRows != 0 || packState.skipPixels != 0);
785
786 if (cannotSetDesiredRowLength || useOverlappingRowsWorkaround || usePackSkipWorkaround)
787 {
788 return readPixelsRowByRow(context, clippedArea, format, readFormat, readType, packState,
789 outPtr);
790 }
791
792 bool useLastRowPaddingWorkaround = false;
793 if (features.packLastRowSeparatelyForPaddingInclusion.enabled)
794 {
795 ANGLE_TRY(ShouldApplyLastRowPaddingWorkaround(
796 contextGL, gl::Extents(clippedArea.width, clippedArea.height, 1), packState, packBuffer,
797 readFormat, readType, false, outPtr, &useLastRowPaddingWorkaround));
798 }
799
800 return readPixelsAllAtOnce(context, clippedArea, format, readFormat, readType, packState,
801 outPtr, useLastRowPaddingWorkaround);
802 }
803
blit(const gl::Context * context,const gl::Rectangle & sourceArea,const gl::Rectangle & destArea,GLbitfield mask,GLenum filter)804 angle::Result FramebufferGL::blit(const gl::Context *context,
805 const gl::Rectangle &sourceArea,
806 const gl::Rectangle &destArea,
807 GLbitfield mask,
808 GLenum filter)
809 {
810 ContextGL *contextGL = GetImplAs<ContextGL>(context);
811 const FunctionsGL *functions = GetFunctionsGL(context);
812 StateManagerGL *stateManager = GetStateManagerGL(context);
813 const angle::FeaturesGL &features = GetFeaturesGL(context);
814
815 const Framebuffer *sourceFramebuffer = context->getState().getReadFramebuffer();
816 const Framebuffer *destFramebuffer = context->getState().getDrawFramebuffer();
817
818 const FramebufferAttachment *colorReadAttachment = sourceFramebuffer->getReadColorAttachment();
819
820 GLsizei readAttachmentSamples = 0;
821 if (colorReadAttachment != nullptr)
822 {
823 // Blitting requires that the textures be single sampled. getSamples will return
824 // emulated sample number, but the EXT_multisampled_render_to_texture extension will
825 // take care of resolving the texture, so even if emulated samples > 0, we should still
826 // be able to blit as long as the underlying resource samples is single sampled.
827 readAttachmentSamples = colorReadAttachment->getResourceSamples();
828 }
829
830 bool needManualColorBlit = false;
831
832 // TODO(cwallez) when the filter is LINEAR and both source and destination are SRGB, we
833 // could avoid doing a manual blit.
834
835 // Prior to OpenGL 4.4 BlitFramebuffer (section 18.3.1 of GL 4.3 core profile) reads:
836 // When values are taken from the read buffer, no linearization is performed, even
837 // if the format of the buffer is SRGB.
838 // Starting from OpenGL 4.4 (section 18.3.1) it reads:
839 // When values are taken from the read buffer, if FRAMEBUFFER_SRGB is enabled and the
840 // value of FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING for the framebuffer attachment
841 // corresponding to the read buffer is SRGB, the red, green, and blue components are
842 // converted from the non-linear sRGB color space according [...].
843 {
844 bool sourceSRGB =
845 colorReadAttachment != nullptr && colorReadAttachment->getColorEncoding() == GL_SRGB;
846 needManualColorBlit =
847 needManualColorBlit || (sourceSRGB && functions->isAtMostGL(gl::Version(4, 3)));
848 }
849
850 // Prior to OpenGL 4.2 BlitFramebuffer (section 4.3.2 of GL 4.1 core profile) reads:
851 // Blit operations bypass the fragment pipeline. The only fragment operations which
852 // affect a blit are the pixel ownership test and scissor test.
853 // Starting from OpenGL 4.2 (section 4.3.2) it reads:
854 // When values are written to the draw buffers, blit operations bypass the fragment
855 // pipeline. The only fragment operations which affect a blit are the pixel ownership
856 // test, the scissor test and sRGB conversion.
857 if (!needManualColorBlit)
858 {
859 bool destSRGB = false;
860 for (size_t i = 0; i < destFramebuffer->getDrawbufferStateCount(); ++i)
861 {
862 const FramebufferAttachment *attachment = destFramebuffer->getDrawBuffer(i);
863 if (attachment && attachment->getColorEncoding() == GL_SRGB)
864 {
865 destSRGB = true;
866 break;
867 }
868 }
869
870 needManualColorBlit =
871 needManualColorBlit || (destSRGB && functions->isAtMostGL(gl::Version(4, 1)));
872 }
873
874 // If the destination has an emulated alpha channel, we need to blit with a shader with alpha
875 // writes disabled.
876 if (mHasEmulatedAlphaAttachment)
877 {
878 needManualColorBlit = true;
879 }
880
881 // Enable FRAMEBUFFER_SRGB if needed
882 stateManager->setFramebufferSRGBEnabledForFramebuffer(context, true, this);
883
884 GLenum blitMask = mask;
885 if (needManualColorBlit && (mask & GL_COLOR_BUFFER_BIT) && readAttachmentSamples <= 1)
886 {
887 BlitGL *blitter = GetBlitGL(context);
888 ANGLE_TRY(blitter->blitColorBufferWithShader(context, sourceFramebuffer, destFramebuffer,
889 sourceArea, destArea, filter,
890 !mHasEmulatedAlphaAttachment));
891 blitMask &= ~GL_COLOR_BUFFER_BIT;
892 }
893
894 if (blitMask == 0)
895 {
896 return angle::Result::Continue;
897 }
898
899 const FramebufferGL *sourceFramebufferGL = GetImplAs<FramebufferGL>(sourceFramebuffer);
900 stateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, sourceFramebufferGL->getFramebufferID());
901 stateManager->bindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferID);
902
903 gl::Rectangle finalSourceArea(sourceArea);
904 gl::Rectangle finalDestArea(destArea);
905
906 if (features.adjustSrcDstRegionForBlitFramebuffer.enabled)
907 {
908 angle::Result result = adjustSrcDstRegion(context, finalSourceArea, finalDestArea,
909 &finalSourceArea, &finalDestArea);
910 if (result != angle::Result::Continue)
911 {
912 return result;
913 }
914 }
915 if (features.clipSrcRegionForBlitFramebuffer.enabled)
916 {
917 angle::Result result = clipSrcRegion(context, finalSourceArea, finalDestArea,
918 &finalSourceArea, &finalDestArea);
919 if (result != angle::Result::Continue)
920 {
921 return result;
922 }
923 }
924
925 functions->blitFramebuffer(finalSourceArea.x, finalSourceArea.y, finalSourceArea.x1(),
926 finalSourceArea.y1(), finalDestArea.x, finalDestArea.y,
927 finalDestArea.x1(), finalDestArea.y1(), blitMask, filter);
928
929 contextGL->markWorkSubmitted();
930 return angle::Result::Continue;
931 }
932
adjustSrcDstRegion(const gl::Context * context,const gl::Rectangle & sourceArea,const gl::Rectangle & destArea,gl::Rectangle * newSourceArea,gl::Rectangle * newDestArea)933 angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context,
934 const gl::Rectangle &sourceArea,
935 const gl::Rectangle &destArea,
936 gl::Rectangle *newSourceArea,
937 gl::Rectangle *newDestArea)
938 {
939 BlitFramebufferBounds bounds = GetBlitFramebufferBounds(context, sourceArea, destArea);
940
941 if (bounds.destRegion.width == 0 || bounds.sourceRegion.width == 0 ||
942 bounds.destRegion.height == 0 || bounds.sourceRegion.height == 0)
943 {
944 return angle::Result::Stop;
945 }
946 if (!ClipRectangle(bounds.destBounds, bounds.destRegion, nullptr))
947 {
948 return angle::Result::Stop;
949 }
950
951 if (!bounds.destBounds.encloses(bounds.destRegion))
952 {
953 // destRegion is not within destBounds. We want to adjust it to a
954 // reasonable size. This is done by halving the destRegion until it is at
955 // most twice the size of the framebuffer. We cut it in half instead
956 // of arbitrarily shrinking it to fit so that we don't end up with
957 // non-power-of-two scale factors which could mess up pixel interpolation.
958 // Naively clipping the dst rect and then proportionally sizing the
959 // src rect yields incorrect results.
960
961 GLuint destXHalvings = 0;
962 GLuint destYHalvings = 0;
963 GLint destOriginX = bounds.destRegion.x;
964 GLint destOriginY = bounds.destRegion.y;
965
966 GLint destClippedWidth = bounds.destRegion.width;
967 while (destClippedWidth > 2 * bounds.destBounds.width)
968 {
969 destClippedWidth = destClippedWidth / 2;
970 destXHalvings++;
971 }
972
973 GLint destClippedHeight = bounds.destRegion.height;
974 while (destClippedHeight > 2 * bounds.destBounds.height)
975 {
976 destClippedHeight = destClippedHeight / 2;
977 destYHalvings++;
978 }
979
980 // Before this block, we check that the two rectangles intersect.
981 // Now, compute the location of a new region origin such that we use the
982 // scaled dimensions but the new region has the same intersection as the
983 // original region.
984
985 GLint left = bounds.destRegion.x0();
986 GLint right = bounds.destRegion.x1();
987 GLint top = bounds.destRegion.y0();
988 GLint bottom = bounds.destRegion.y1();
989
990 GLint extraXOffset = 0;
991 if (left >= 0 && left < bounds.destBounds.width)
992 {
993 // Left edge is in-bounds
994 destOriginX = bounds.destRegion.x;
995 }
996 else if (right > 0 && right <= bounds.destBounds.width)
997 {
998 // Right edge is in-bounds
999 destOriginX = right - destClippedWidth;
1000 }
1001 else
1002 {
1003 // Region completely spans bounds
1004 extraXOffset = (bounds.destRegion.width - destClippedWidth) / 2;
1005 destOriginX = bounds.destRegion.x + extraXOffset;
1006 }
1007
1008 GLint extraYOffset = 0;
1009 if (top >= 0 && top < bounds.destBounds.height)
1010 {
1011 // Top edge is in-bounds
1012 destOriginY = bounds.destRegion.y;
1013 }
1014 else if (bottom > 0 && bottom <= bounds.destBounds.height)
1015 {
1016 // Bottom edge is in-bounds
1017 destOriginY = bottom - destClippedHeight;
1018 }
1019 else
1020 {
1021 // Region completely spans bounds
1022 extraYOffset = (bounds.destRegion.height - destClippedHeight) / 2;
1023 destOriginY = bounds.destRegion.y + extraYOffset;
1024 }
1025
1026 // Offsets from the bottom left corner of the original region to
1027 // the bottom left corner of the clipped region.
1028 // This value (after it is scaled) is the respective offset we will apply
1029 // to the src origin.
1030
1031 CheckedNumeric<GLuint> checkedXOffset(destOriginX - bounds.destRegion.x - extraXOffset / 2);
1032 CheckedNumeric<GLuint> checkedYOffset(destOriginY - bounds.destRegion.y - extraYOffset / 2);
1033
1034 // if X/Y is reversed, use the top/right out-of-bounds region to compute
1035 // the origin offset instead of the left/bottom out-of-bounds region
1036 if (bounds.xFlipped)
1037 {
1038 checkedXOffset =
1039 (bounds.destRegion.x1() - (destOriginX + destClippedWidth) + extraXOffset / 2);
1040 }
1041 if (bounds.yFlipped)
1042 {
1043 checkedYOffset =
1044 (bounds.destRegion.y1() - (destOriginY + destClippedHeight) + extraYOffset / 2);
1045 }
1046
1047 // These offsets should never overflow
1048 GLuint xOffset, yOffset;
1049 if (!checkedXOffset.AssignIfValid(&xOffset) || !checkedYOffset.AssignIfValid(&yOffset))
1050 {
1051 UNREACHABLE();
1052 return angle::Result::Stop;
1053 }
1054
1055 bounds.destRegion =
1056 gl::Rectangle(destOriginX, destOriginY, destClippedWidth, destClippedHeight);
1057
1058 // Adjust the src region by the same factor
1059 bounds.sourceRegion = gl::Rectangle(bounds.sourceRegion.x + (xOffset >> destXHalvings),
1060 bounds.sourceRegion.y + (yOffset >> destYHalvings),
1061 bounds.sourceRegion.width >> destXHalvings,
1062 bounds.sourceRegion.height >> destYHalvings);
1063
1064 // if the src was scaled to 0, set it to 1 so the src is non-empty
1065 if (bounds.sourceRegion.width == 0)
1066 {
1067 bounds.sourceRegion.width = 1;
1068 }
1069 if (bounds.sourceRegion.height == 0)
1070 {
1071 bounds.sourceRegion.height = 1;
1072 }
1073 }
1074
1075 if (!bounds.sourceBounds.encloses(bounds.sourceRegion))
1076 {
1077 // sourceRegion is not within sourceBounds. We want to adjust it to a
1078 // reasonable size. This is done by halving the sourceRegion until it is at
1079 // most twice the size of the framebuffer. We cut it in half instead
1080 // of arbitrarily shrinking it to fit so that we don't end up with
1081 // non-power-of-two scale factors which could mess up pixel interpolation.
1082 // Naively clipping the source rect and then proportionally sizing the
1083 // dest rect yields incorrect results.
1084
1085 GLuint sourceXHalvings = 0;
1086 GLuint sourceYHalvings = 0;
1087 GLint sourceOriginX = bounds.sourceRegion.x;
1088 GLint sourceOriginY = bounds.sourceRegion.y;
1089
1090 GLint sourceClippedWidth = bounds.sourceRegion.width;
1091 while (sourceClippedWidth > 2 * bounds.sourceBounds.width)
1092 {
1093 sourceClippedWidth = sourceClippedWidth / 2;
1094 sourceXHalvings++;
1095 }
1096
1097 GLint sourceClippedHeight = bounds.sourceRegion.height;
1098 while (sourceClippedHeight > 2 * bounds.sourceBounds.height)
1099 {
1100 sourceClippedHeight = sourceClippedHeight / 2;
1101 sourceYHalvings++;
1102 }
1103
1104 // Before this block, we check that the two rectangles intersect.
1105 // Now, compute the location of a new region origin such that we use the
1106 // scaled dimensions but the new region has the same intersection as the
1107 // original region.
1108
1109 GLint left = bounds.sourceRegion.x0();
1110 GLint right = bounds.sourceRegion.x1();
1111 GLint top = bounds.sourceRegion.y0();
1112 GLint bottom = bounds.sourceRegion.y1();
1113
1114 GLint extraXOffset = 0;
1115 if (left >= 0 && left < bounds.sourceBounds.width)
1116 {
1117 // Left edge is in-bounds
1118 sourceOriginX = bounds.sourceRegion.x;
1119 }
1120 else if (right > 0 && right <= bounds.sourceBounds.width)
1121 {
1122 // Right edge is in-bounds
1123 sourceOriginX = right - sourceClippedWidth;
1124 }
1125 else
1126 {
1127 // Region completely spans bounds
1128 extraXOffset = (bounds.sourceRegion.width - sourceClippedWidth) / 2;
1129 sourceOriginX = bounds.sourceRegion.x + extraXOffset;
1130 }
1131
1132 GLint extraYOffset = 0;
1133 if (top >= 0 && top < bounds.sourceBounds.height)
1134 {
1135 // Top edge is in-bounds
1136 sourceOriginY = bounds.sourceRegion.y;
1137 }
1138 else if (bottom > 0 && bottom <= bounds.sourceBounds.height)
1139 {
1140 // Bottom edge is in-bounds
1141 sourceOriginY = bottom - sourceClippedHeight;
1142 }
1143 else
1144 {
1145 // Region completely spans bounds
1146 extraYOffset = (bounds.sourceRegion.height - sourceClippedHeight) / 2;
1147 sourceOriginY = bounds.sourceRegion.y + extraYOffset;
1148 }
1149
1150 // Offsets from the bottom left corner of the original region to
1151 // the bottom left corner of the clipped region.
1152 // This value (after it is scaled) is the respective offset we will apply
1153 // to the dest origin.
1154
1155 CheckedNumeric<GLuint> checkedXOffset(sourceOriginX - bounds.sourceRegion.x -
1156 extraXOffset / 2);
1157 CheckedNumeric<GLuint> checkedYOffset(sourceOriginY - bounds.sourceRegion.y -
1158 extraYOffset / 2);
1159
1160 // if X/Y is reversed, use the top/right out-of-bounds region to compute
1161 // the origin offset instead of the left/bottom out-of-bounds region
1162 if (bounds.xFlipped)
1163 {
1164 checkedXOffset = (bounds.sourceRegion.x1() - (sourceOriginX + sourceClippedWidth) +
1165 extraXOffset / 2);
1166 }
1167 if (bounds.yFlipped)
1168 {
1169 checkedYOffset = (bounds.sourceRegion.y1() - (sourceOriginY + sourceClippedHeight) +
1170 extraYOffset / 2);
1171 }
1172
1173 // These offsets should never overflow
1174 GLuint xOffset, yOffset;
1175 if (!checkedXOffset.AssignIfValid(&xOffset) || !checkedYOffset.AssignIfValid(&yOffset))
1176 {
1177 UNREACHABLE();
1178 return angle::Result::Stop;
1179 }
1180
1181 bounds.sourceRegion =
1182 gl::Rectangle(sourceOriginX, sourceOriginY, sourceClippedWidth, sourceClippedHeight);
1183
1184 // Adjust the dest region by the same factor
1185 bounds.destRegion = gl::Rectangle(bounds.destRegion.x + (xOffset >> sourceXHalvings),
1186 bounds.destRegion.y + (yOffset >> sourceYHalvings),
1187 bounds.destRegion.width >> sourceXHalvings,
1188 bounds.destRegion.height >> sourceYHalvings);
1189 }
1190 // Set the src and dst endpoints. If they were previously flipped,
1191 // set them as flipped.
1192 *newSourceArea = bounds.sourceRegion.flip(sourceArea.isReversedX(), sourceArea.isReversedY());
1193 *newDestArea = bounds.destRegion.flip(destArea.isReversedX(), destArea.isReversedY());
1194
1195 return angle::Result::Continue;
1196 }
1197
clipSrcRegion(const gl::Context * context,const gl::Rectangle & sourceArea,const gl::Rectangle & destArea,gl::Rectangle * newSourceArea,gl::Rectangle * newDestArea)1198 angle::Result FramebufferGL::clipSrcRegion(const gl::Context *context,
1199 const gl::Rectangle &sourceArea,
1200 const gl::Rectangle &destArea,
1201 gl::Rectangle *newSourceArea,
1202 gl::Rectangle *newDestArea)
1203 {
1204 BlitFramebufferBounds bounds = GetBlitFramebufferBounds(context, sourceArea, destArea);
1205
1206 if (bounds.destRegion.width == 0 || bounds.sourceRegion.width == 0 ||
1207 bounds.destRegion.height == 0 || bounds.sourceRegion.height == 0)
1208 {
1209 return angle::Result::Stop;
1210 }
1211 if (!ClipRectangle(bounds.destBounds, bounds.destRegion, nullptr))
1212 {
1213 return angle::Result::Stop;
1214 }
1215
1216 if (!bounds.sourceBounds.encloses(bounds.sourceRegion))
1217 {
1218 // If pixels lying outside the read framebuffer, adjust src region
1219 // and dst region to appropriate in-bounds regions respectively.
1220 gl::Rectangle realSourceRegion;
1221 if (!ClipRectangle(bounds.sourceRegion, bounds.sourceBounds, &realSourceRegion))
1222 {
1223 return angle::Result::Stop;
1224 }
1225 GLuint xOffset = realSourceRegion.x - bounds.sourceRegion.x;
1226 GLuint yOffset = realSourceRegion.y - bounds.sourceRegion.y;
1227
1228 // if X/Y is reversed, use the top/right out-of-bounds region for mapping
1229 // to dst region, instead of left/bottom out-of-bounds region for mapping.
1230 if (bounds.xFlipped)
1231 {
1232 xOffset = bounds.sourceRegion.x1() - realSourceRegion.x1();
1233 }
1234 if (bounds.yFlipped)
1235 {
1236 yOffset = bounds.sourceRegion.y1() - realSourceRegion.y1();
1237 }
1238
1239 GLfloat destMappingWidth = static_cast<GLfloat>(realSourceRegion.width) *
1240 bounds.destRegion.width / bounds.sourceRegion.width;
1241 GLfloat destMappingHeight = static_cast<GLfloat>(realSourceRegion.height) *
1242 bounds.destRegion.height / bounds.sourceRegion.height;
1243 GLfloat destMappingXOffset =
1244 static_cast<GLfloat>(xOffset) * bounds.destRegion.width / bounds.sourceRegion.width;
1245 GLfloat destMappingYOffset =
1246 static_cast<GLfloat>(yOffset) * bounds.destRegion.height / bounds.sourceRegion.height;
1247
1248 GLuint destMappingX0 =
1249 static_cast<GLuint>(std::round(bounds.destRegion.x + destMappingXOffset));
1250 GLuint destMappingY0 =
1251 static_cast<GLuint>(std::round(bounds.destRegion.y + destMappingYOffset));
1252
1253 GLuint destMappingX1 = static_cast<GLuint>(
1254 std::round(bounds.destRegion.x + destMappingXOffset + destMappingWidth));
1255 GLuint destMappingY1 = static_cast<GLuint>(
1256 std::round(bounds.destRegion.y + destMappingYOffset + destMappingHeight));
1257
1258 bounds.destRegion =
1259 gl::Rectangle(destMappingX0, destMappingY0, destMappingX1 - destMappingX0,
1260 destMappingY1 - destMappingY0);
1261
1262 bounds.sourceRegion = realSourceRegion;
1263 }
1264 // Set the src and dst endpoints. If they were previously flipped,
1265 // set them as flipped.
1266 *newSourceArea = bounds.sourceRegion.flip(sourceArea.isReversedX(), sourceArea.isReversedY());
1267 *newDestArea = bounds.destRegion.flip(destArea.isReversedX(), destArea.isReversedY());
1268
1269 return angle::Result::Continue;
1270 }
1271
getSamplePosition(const gl::Context * context,size_t index,GLfloat * xy) const1272 angle::Result FramebufferGL::getSamplePosition(const gl::Context *context,
1273 size_t index,
1274 GLfloat *xy) const
1275 {
1276 const FunctionsGL *functions = GetFunctionsGL(context);
1277 StateManagerGL *stateManager = GetStateManagerGL(context);
1278
1279 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
1280 functions->getMultisamplefv(GL_SAMPLE_POSITION, static_cast<GLuint>(index), xy);
1281 return angle::Result::Continue;
1282 }
1283
shouldSyncStateBeforeCheckStatus() const1284 bool FramebufferGL::shouldSyncStateBeforeCheckStatus() const
1285 {
1286 return true;
1287 }
1288
checkStatus(const gl::Context * context) const1289 gl::FramebufferStatus FramebufferGL::checkStatus(const gl::Context *context) const
1290 {
1291 const FunctionsGL *functions = GetFunctionsGL(context);
1292 StateManagerGL *stateManager = GetStateManagerGL(context);
1293
1294 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
1295 GLenum status = functions->checkFramebufferStatus(GL_FRAMEBUFFER);
1296 if (status != GL_FRAMEBUFFER_COMPLETE)
1297 {
1298 WARN() << "GL framebuffer returned incomplete: " << gl::FmtHex(status);
1299 return gl::FramebufferStatus::Incomplete(GL_FRAMEBUFFER_UNSUPPORTED,
1300 gl::err::kFramebufferIncompleteDriverUnsupported);
1301 }
1302
1303 return gl::FramebufferStatus::Complete();
1304 }
1305
ensureAttachmentsInitialized(const gl::Context * context,const gl::DrawBufferMask & colorAttachments,bool depth,bool stencil)1306 angle::Result FramebufferGL::ensureAttachmentsInitialized(
1307 const gl::Context *context,
1308 const gl::DrawBufferMask &colorAttachments,
1309 bool depth,
1310 bool stencil)
1311 {
1312 if (colorAttachments != getState().getEnabledDrawBuffers())
1313 {
1314 // Fall back to the default implementation when there are gaps in the enabled draw buffers
1315 // to avoid modifying the draw buffer state.
1316 return FramebufferImpl::ensureAttachmentsInitialized(context, colorAttachments, depth,
1317 stencil);
1318 }
1319
1320 BlitGL *blitter = GetBlitGL(context);
1321 return blitter->clearFramebuffer(context, colorAttachments, depth, stencil, this);
1322 }
1323
syncState(const gl::Context * context,GLenum binding,const gl::Framebuffer::DirtyBits & dirtyBits,gl::Command command)1324 angle::Result FramebufferGL::syncState(const gl::Context *context,
1325 GLenum binding,
1326 const gl::Framebuffer::DirtyBits &dirtyBits,
1327 gl::Command command)
1328 {
1329 // Don't need to sync state for the default FBO.
1330 if (isDefault())
1331 {
1332 return angle::Result::Continue;
1333 }
1334
1335 const FunctionsGL *functions = GetFunctionsGL(context);
1336 StateManagerGL *stateManager = GetStateManagerGL(context);
1337
1338 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
1339
1340 // A pointer to one of the attachments for which the texture or the render buffer is not zero.
1341 const FramebufferAttachment *attachment = nullptr;
1342
1343 for (auto dirtyBit : dirtyBits)
1344 {
1345 switch (dirtyBit)
1346 {
1347 case Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT:
1348 {
1349 const FramebufferAttachment *newAttachment = mState.getDepthAttachment();
1350 BindFramebufferAttachment(functions, GL_DEPTH_ATTACHMENT, newAttachment,
1351 GetFeaturesGL(context));
1352 if (newAttachment)
1353 {
1354 attachment = newAttachment;
1355 }
1356 break;
1357 }
1358 case Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT:
1359 {
1360 const FramebufferAttachment *newAttachment = mState.getStencilAttachment();
1361 BindFramebufferAttachment(functions, GL_STENCIL_ATTACHMENT, newAttachment,
1362 GetFeaturesGL(context));
1363 if (newAttachment)
1364 {
1365 attachment = newAttachment;
1366 }
1367 break;
1368 }
1369 case Framebuffer::DIRTY_BIT_DRAW_BUFFERS:
1370 {
1371 const auto &drawBuffers = mState.getDrawBufferStates();
1372 functions->drawBuffers(static_cast<GLsizei>(drawBuffers.size()),
1373 drawBuffers.data());
1374 mAppliedEnabledDrawBuffers = mState.getEnabledDrawBuffers();
1375 break;
1376 }
1377 case Framebuffer::DIRTY_BIT_READ_BUFFER:
1378 functions->readBuffer(mState.getReadBufferState());
1379 break;
1380 case Framebuffer::DIRTY_BIT_DEFAULT_WIDTH:
1381 functions->framebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH,
1382 mState.getDefaultWidth());
1383 break;
1384 case Framebuffer::DIRTY_BIT_DEFAULT_HEIGHT:
1385 functions->framebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT,
1386 mState.getDefaultHeight());
1387 break;
1388 case Framebuffer::DIRTY_BIT_DEFAULT_SAMPLES:
1389 functions->framebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_SAMPLES,
1390 mState.getDefaultSamples());
1391 break;
1392 case Framebuffer::DIRTY_BIT_DEFAULT_FIXED_SAMPLE_LOCATIONS:
1393 functions->framebufferParameteri(
1394 GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS,
1395 gl::ConvertToGLBoolean(mState.getDefaultFixedSampleLocations()));
1396 break;
1397 case Framebuffer::DIRTY_BIT_DEFAULT_LAYERS:
1398 functions->framebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_LAYERS_EXT,
1399 mState.getDefaultLayers());
1400 break;
1401 case Framebuffer::DIRTY_BIT_FLIP_Y:
1402 ASSERT(functions->framebufferParameteri || functions->framebufferParameteriMESA);
1403 if (functions->framebufferParameteri)
1404 {
1405 functions->framebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA,
1406 gl::ConvertToGLBoolean(mState.getFlipY()));
1407 }
1408 else
1409 {
1410 functions->framebufferParameteriMESA(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA,
1411 gl::ConvertToGLBoolean(mState.getFlipY()));
1412 }
1413 break;
1414 default:
1415 {
1416 static_assert(Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0, "FB dirty bits");
1417 if (dirtyBit < Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX)
1418 {
1419 size_t index =
1420 static_cast<size_t>(dirtyBit - Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0);
1421 const FramebufferAttachment *newAttachment = mState.getColorAttachment(index);
1422 BindFramebufferAttachment(functions,
1423 static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + index),
1424 newAttachment, GetFeaturesGL(context));
1425 if (newAttachment)
1426 {
1427 attachment = newAttachment;
1428 }
1429
1430 // Hiding an alpha channel is only supported when it's the first attachment
1431 // currently. Assert that these emulated textures are not bound to a framebuffer
1432 // using MRT.
1433 if (index == 0)
1434 {
1435 mHasEmulatedAlphaAttachment =
1436 IsEmulatedAlphaChannelTextureAttachment(attachment);
1437 }
1438 ASSERT(index == 0 || !IsEmulatedAlphaChannelTextureAttachment(attachment));
1439 }
1440 break;
1441 }
1442 }
1443 }
1444
1445 if (attachment && mState.id() == context->getState().getDrawFramebuffer()->id())
1446 {
1447 stateManager->updateMultiviewBaseViewLayerIndexUniform(
1448 context->getState().getProgramExecutable(), getState());
1449 }
1450
1451 return angle::Result::Continue;
1452 }
1453
updateDefaultFramebufferID(GLuint framebufferID)1454 void FramebufferGL::updateDefaultFramebufferID(GLuint framebufferID)
1455 {
1456 // We only update framebufferID for a default frambuffer, and the framebufferID is created
1457 // externally. ANGLE doesn't owne it.
1458 ASSERT(isDefault());
1459 mFramebufferID = framebufferID;
1460 }
1461
hasEmulatedAlphaChannelTextureAttachment() const1462 bool FramebufferGL::hasEmulatedAlphaChannelTextureAttachment() const
1463 {
1464 return mHasEmulatedAlphaAttachment;
1465 }
1466
syncClearState(const gl::Context * context,GLbitfield mask)1467 void FramebufferGL::syncClearState(const gl::Context *context, GLbitfield mask)
1468 {
1469 StateManagerGL *stateManager = GetStateManagerGL(context);
1470 const angle::FeaturesGL &features = GetFeaturesGL(context);
1471
1472 // Clip origin must not affect scissor box but some drivers flip it for clear ops.
1473 if (context->getState().isScissorTestEnabled())
1474 {
1475 stateManager->setClipControl(ClipOrigin::LowerLeft, ClipDepthMode::NegativeOneToOne);
1476 }
1477
1478 if (features.doesSRGBClearsOnLinearFramebufferAttachments.enabled &&
1479 (mask & GL_COLOR_BUFFER_BIT) != 0 && !isDefault())
1480 {
1481 bool hasSRGBAttachment = false;
1482 for (const auto &attachment : mState.getColorAttachments())
1483 {
1484 if (attachment.isAttached() && attachment.getColorEncoding() == GL_SRGB)
1485 {
1486 hasSRGBAttachment = true;
1487 break;
1488 }
1489 }
1490
1491 stateManager->setFramebufferSRGBEnabled(context, hasSRGBAttachment);
1492 }
1493 else
1494 {
1495 stateManager->setFramebufferSRGBEnabled(context, !isDefault());
1496 }
1497 }
1498
syncClearBufferState(const gl::Context * context,GLenum buffer,GLint drawBuffer)1499 void FramebufferGL::syncClearBufferState(const gl::Context *context,
1500 GLenum buffer,
1501 GLint drawBuffer)
1502 {
1503 StateManagerGL *stateManager = GetStateManagerGL(context);
1504 const angle::FeaturesGL &features = GetFeaturesGL(context);
1505
1506 // Clip origin must not affect scissor box but some drivers flip it for clear ops.
1507 if (context->getState().isScissorTestEnabled())
1508 {
1509 stateManager->setClipControl(ClipOrigin::LowerLeft, ClipDepthMode::NegativeOneToOne);
1510 }
1511
1512 if (features.doesSRGBClearsOnLinearFramebufferAttachments.enabled && buffer == GL_COLOR &&
1513 !isDefault())
1514 {
1515 // If doing a clear on a color buffer, set SRGB blend enabled only if the color buffer
1516 // is an SRGB format.
1517 const auto &drawbufferState = mState.getDrawBufferStates();
1518 const auto &colorAttachments = mState.getColorAttachments();
1519
1520 const FramebufferAttachment *attachment = nullptr;
1521 if (drawbufferState[drawBuffer] >= GL_COLOR_ATTACHMENT0 &&
1522 drawbufferState[drawBuffer] < GL_COLOR_ATTACHMENT0 + colorAttachments.size())
1523 {
1524 size_t attachmentIdx =
1525 static_cast<size_t>(drawbufferState[drawBuffer] - GL_COLOR_ATTACHMENT0);
1526 attachment = &colorAttachments[attachmentIdx];
1527 }
1528
1529 if (attachment != nullptr)
1530 {
1531 stateManager->setFramebufferSRGBEnabled(context,
1532 attachment->getColorEncoding() == GL_SRGB);
1533 }
1534 }
1535 else
1536 {
1537 stateManager->setFramebufferSRGBEnabled(context, !isDefault());
1538 }
1539 }
1540
modifyInvalidateAttachmentsForEmulatedDefaultFBO(size_t count,const GLenum * attachments,std::vector<GLenum> * modifiedAttachments) const1541 bool FramebufferGL::modifyInvalidateAttachmentsForEmulatedDefaultFBO(
1542 size_t count,
1543 const GLenum *attachments,
1544 std::vector<GLenum> *modifiedAttachments) const
1545 {
1546 bool needsModification = isDefault() && mFramebufferID != 0;
1547 if (!needsModification)
1548 {
1549 return false;
1550 }
1551
1552 modifiedAttachments->resize(count);
1553 for (size_t i = 0; i < count; i++)
1554 {
1555 switch (attachments[i])
1556 {
1557 case GL_COLOR:
1558 (*modifiedAttachments)[i] = GL_COLOR_ATTACHMENT0;
1559 break;
1560
1561 case GL_DEPTH:
1562 (*modifiedAttachments)[i] = GL_DEPTH_ATTACHMENT;
1563 break;
1564
1565 case GL_STENCIL:
1566 (*modifiedAttachments)[i] = GL_STENCIL_ATTACHMENT;
1567 break;
1568
1569 default:
1570 UNREACHABLE();
1571 break;
1572 }
1573 }
1574
1575 return true;
1576 }
1577
readPixelsRowByRow(const gl::Context * context,const gl::Rectangle & area,GLenum originalReadFormat,GLenum format,GLenum type,const gl::PixelPackState & pack,GLubyte * pixels) const1578 angle::Result FramebufferGL::readPixelsRowByRow(const gl::Context *context,
1579 const gl::Rectangle &area,
1580 GLenum originalReadFormat,
1581 GLenum format,
1582 GLenum type,
1583 const gl::PixelPackState &pack,
1584 GLubyte *pixels) const
1585 {
1586 ContextGL *contextGL = GetImplAs<ContextGL>(context);
1587 const FunctionsGL *functions = GetFunctionsGL(context);
1588 StateManagerGL *stateManager = GetStateManagerGL(context);
1589 GLubyte *originalReadFormatPixels = pixels;
1590
1591 const gl::InternalFormat &glFormat = gl::GetInternalFormatInfo(format, type);
1592
1593 GLuint rowBytes = 0;
1594 ANGLE_CHECK_GL_MATH(contextGL, glFormat.computeRowPitch(type, area.width, pack.alignment,
1595 pack.rowLength, &rowBytes));
1596 GLuint skipBytes = 0;
1597 ANGLE_CHECK_GL_MATH(contextGL,
1598 glFormat.computeSkipBytes(type, rowBytes, 0, pack, false, &skipBytes));
1599
1600 ScopedEXTTextureNorm16ReadbackWorkaround workaround;
1601 angle::Result result =
1602 workaround.Initialize(context, area, originalReadFormat, format, type, skipBytes, rowBytes,
1603 glFormat.computePixelBytes(type), pixels);
1604 if (result != angle::Result::Continue)
1605 {
1606 return result;
1607 }
1608
1609 gl::PixelPackState directPack;
1610 directPack.alignment = 1;
1611 ANGLE_TRY(stateManager->setPixelPackState(context, directPack));
1612
1613 GLubyte *readbackPixels = workaround.Pixels();
1614 readbackPixels += skipBytes;
1615 for (GLint y = area.y; y < area.y + area.height; ++y)
1616 {
1617 ANGLE_GL_TRY(context,
1618 functions->readPixels(area.x, y, area.width, 1, format, type, readbackPixels));
1619 readbackPixels += rowBytes;
1620 }
1621
1622 if (workaround.IsEnabled())
1623 {
1624 return RearrangeEXTTextureNorm16Pixels(
1625 context, area, originalReadFormat, format, type, skipBytes, rowBytes,
1626 glFormat.computePixelBytes(type), pack, originalReadFormatPixels, workaround.Pixels());
1627 }
1628
1629 return angle::Result::Continue;
1630 }
1631
readPixelsAllAtOnce(const gl::Context * context,const gl::Rectangle & area,GLenum originalReadFormat,GLenum format,GLenum type,const gl::PixelPackState & pack,GLubyte * pixels,bool readLastRowSeparately) const1632 angle::Result FramebufferGL::readPixelsAllAtOnce(const gl::Context *context,
1633 const gl::Rectangle &area,
1634 GLenum originalReadFormat,
1635 GLenum format,
1636 GLenum type,
1637 const gl::PixelPackState &pack,
1638 GLubyte *pixels,
1639 bool readLastRowSeparately) const
1640 {
1641 ContextGL *contextGL = GetImplAs<ContextGL>(context);
1642 const FunctionsGL *functions = GetFunctionsGL(context);
1643 StateManagerGL *stateManager = GetStateManagerGL(context);
1644 GLubyte *originalReadFormatPixels = pixels;
1645
1646 const gl::InternalFormat &glFormat = gl::GetInternalFormatInfo(format, type);
1647
1648 GLuint rowBytes = 0;
1649 ANGLE_CHECK_GL_MATH(contextGL, glFormat.computeRowPitch(type, area.width, pack.alignment,
1650 pack.rowLength, &rowBytes));
1651 GLuint skipBytes = 0;
1652 ANGLE_CHECK_GL_MATH(contextGL,
1653 glFormat.computeSkipBytes(type, rowBytes, 0, pack, false, &skipBytes));
1654
1655 ScopedEXTTextureNorm16ReadbackWorkaround workaround;
1656 angle::Result result =
1657 workaround.Initialize(context, area, originalReadFormat, format, type, skipBytes, rowBytes,
1658 glFormat.computePixelBytes(type), pixels);
1659 if (result != angle::Result::Continue)
1660 {
1661 return result;
1662 }
1663
1664 GLint height = area.height - readLastRowSeparately;
1665 if (height > 0)
1666 {
1667 ANGLE_TRY(stateManager->setPixelPackState(context, pack));
1668 ANGLE_GL_TRY(context, functions->readPixels(area.x, area.y, area.width, height, format,
1669 type, workaround.Pixels()));
1670 }
1671
1672 if (readLastRowSeparately)
1673 {
1674 gl::PixelPackState directPack;
1675 directPack.alignment = 1;
1676 ANGLE_TRY(stateManager->setPixelPackState(context, directPack));
1677
1678 GLubyte *readbackPixels = workaround.Pixels();
1679 readbackPixels += skipBytes + (area.height - 1) * rowBytes;
1680 ANGLE_GL_TRY(context, functions->readPixels(area.x, area.y + area.height - 1, area.width, 1,
1681 format, type, readbackPixels));
1682 }
1683
1684 if (workaround.IsEnabled())
1685 {
1686 return RearrangeEXTTextureNorm16Pixels(
1687 context, area, originalReadFormat, format, type, skipBytes, rowBytes,
1688 glFormat.computePixelBytes(type), pack, originalReadFormatPixels, workaround.Pixels());
1689 }
1690
1691 return angle::Result::Continue;
1692 }
1693 } // namespace rx
1694