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/FeaturesGL.h"
30 #include "platform/PlatformMethods.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 ANGLE_NO_DISCARD 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 } // namespace
417
FramebufferGL(const gl::FramebufferState & data,GLuint id,bool isDefault,bool emulatedAlpha)418 FramebufferGL::FramebufferGL(const gl::FramebufferState &data,
419 GLuint id,
420 bool isDefault,
421 bool emulatedAlpha)
422 : FramebufferImpl(data),
423 mFramebufferID(id),
424 mIsDefault(isDefault),
425 mHasEmulatedAlphaAttachment(emulatedAlpha),
426 mAppliedEnabledDrawBuffers(1)
427 {}
428
~FramebufferGL()429 FramebufferGL::~FramebufferGL()
430 {
431 ASSERT(mFramebufferID == 0);
432 }
433
destroy(const gl::Context * context)434 void FramebufferGL::destroy(const gl::Context *context)
435 {
436 StateManagerGL *stateManager = GetStateManagerGL(context);
437 stateManager->deleteFramebuffer(mFramebufferID);
438 mFramebufferID = 0;
439 }
440
discard(const gl::Context * context,size_t count,const GLenum * attachments)441 angle::Result FramebufferGL::discard(const gl::Context *context,
442 size_t count,
443 const GLenum *attachments)
444 {
445 // glInvalidateFramebuffer accepts the same enums as glDiscardFramebufferEXT
446 return invalidate(context, count, attachments);
447 }
448
invalidate(const gl::Context * context,size_t count,const GLenum * attachments)449 angle::Result FramebufferGL::invalidate(const gl::Context *context,
450 size_t count,
451 const GLenum *attachments)
452 {
453 const GLenum *finalAttachmentsPtr = attachments;
454
455 std::vector<GLenum> modifiedAttachments;
456 if (modifyInvalidateAttachmentsForEmulatedDefaultFBO(count, attachments, &modifiedAttachments))
457 {
458 finalAttachmentsPtr = modifiedAttachments.data();
459 }
460
461 const FunctionsGL *functions = GetFunctionsGL(context);
462 StateManagerGL *stateManager = GetStateManagerGL(context);
463
464 // Since this function is just a hint, only call a native function if it exists.
465 if (functions->invalidateFramebuffer)
466 {
467 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
468 functions->invalidateFramebuffer(GL_FRAMEBUFFER, static_cast<GLsizei>(count),
469 finalAttachmentsPtr);
470 }
471 else if (functions->discardFramebufferEXT)
472 {
473 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
474 functions->discardFramebufferEXT(GL_FRAMEBUFFER, static_cast<GLsizei>(count),
475 finalAttachmentsPtr);
476 }
477
478 return angle::Result::Continue;
479 }
480
invalidateSub(const gl::Context * context,size_t count,const GLenum * attachments,const gl::Rectangle & area)481 angle::Result FramebufferGL::invalidateSub(const gl::Context *context,
482 size_t count,
483 const GLenum *attachments,
484 const gl::Rectangle &area)
485 {
486
487 const GLenum *finalAttachmentsPtr = attachments;
488
489 std::vector<GLenum> modifiedAttachments;
490 if (modifyInvalidateAttachmentsForEmulatedDefaultFBO(count, attachments, &modifiedAttachments))
491 {
492 finalAttachmentsPtr = modifiedAttachments.data();
493 }
494
495 const FunctionsGL *functions = GetFunctionsGL(context);
496 StateManagerGL *stateManager = GetStateManagerGL(context);
497
498 // Since this function is just a hint and not available until OpenGL 4.3, only call it if it is
499 // available.
500 if (functions->invalidateSubFramebuffer)
501 {
502 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
503 functions->invalidateSubFramebuffer(GL_FRAMEBUFFER, static_cast<GLsizei>(count),
504 finalAttachmentsPtr, area.x, area.y, area.width,
505 area.height);
506 }
507
508 return angle::Result::Continue;
509 }
510
clear(const gl::Context * context,GLbitfield mask)511 angle::Result FramebufferGL::clear(const gl::Context *context, GLbitfield mask)
512 {
513 ContextGL *contextGL = GetImplAs<ContextGL>(context);
514 const FunctionsGL *functions = GetFunctionsGL(context);
515 StateManagerGL *stateManager = GetStateManagerGL(context);
516
517 syncClearState(context, mask);
518 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
519
520 if (!RequiresMultiviewClear(mState, context->getState().isScissorTestEnabled()))
521 {
522 functions->clear(mask);
523 }
524 else
525 {
526 ClearMultiviewGL *multiviewClearer = GetMultiviewClearer(context);
527 multiviewClearer->clearMultiviewFBO(mState, context->getState().getScissor(),
528 ClearMultiviewGL::ClearCommandType::Clear, mask,
529 GL_NONE, 0, nullptr, 0.0f, 0);
530 }
531
532 contextGL->markWorkSubmitted();
533 return angle::Result::Continue;
534 }
535
clearBufferfv(const gl::Context * context,GLenum buffer,GLint drawbuffer,const GLfloat * values)536 angle::Result FramebufferGL::clearBufferfv(const gl::Context *context,
537 GLenum buffer,
538 GLint drawbuffer,
539 const GLfloat *values)
540 {
541 ContextGL *contextGL = GetImplAs<ContextGL>(context);
542 const FunctionsGL *functions = GetFunctionsGL(context);
543 StateManagerGL *stateManager = GetStateManagerGL(context);
544
545 syncClearBufferState(context, buffer, drawbuffer);
546 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
547
548 if (!RequiresMultiviewClear(mState, context->getState().isScissorTestEnabled()))
549 {
550 functions->clearBufferfv(buffer, drawbuffer, values);
551 }
552 else
553 {
554 ClearMultiviewGL *multiviewClearer = GetMultiviewClearer(context);
555 multiviewClearer->clearMultiviewFBO(mState, context->getState().getScissor(),
556 ClearMultiviewGL::ClearCommandType::ClearBufferfv,
557 static_cast<GLbitfield>(0u), buffer, drawbuffer,
558 reinterpret_cast<const uint8_t *>(values), 0.0f, 0);
559 }
560
561 contextGL->markWorkSubmitted();
562 return angle::Result::Continue;
563 }
564
clearBufferuiv(const gl::Context * context,GLenum buffer,GLint drawbuffer,const GLuint * values)565 angle::Result FramebufferGL::clearBufferuiv(const gl::Context *context,
566 GLenum buffer,
567 GLint drawbuffer,
568 const GLuint *values)
569 {
570 ContextGL *contextGL = GetImplAs<ContextGL>(context);
571 const FunctionsGL *functions = GetFunctionsGL(context);
572 StateManagerGL *stateManager = GetStateManagerGL(context);
573
574 syncClearBufferState(context, buffer, drawbuffer);
575 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
576
577 if (!RequiresMultiviewClear(mState, context->getState().isScissorTestEnabled()))
578 {
579 functions->clearBufferuiv(buffer, drawbuffer, values);
580 }
581 else
582 {
583 ClearMultiviewGL *multiviewClearer = GetMultiviewClearer(context);
584 multiviewClearer->clearMultiviewFBO(mState, context->getState().getScissor(),
585 ClearMultiviewGL::ClearCommandType::ClearBufferuiv,
586 static_cast<GLbitfield>(0u), buffer, drawbuffer,
587 reinterpret_cast<const uint8_t *>(values), 0.0f, 0);
588 }
589
590 contextGL->markWorkSubmitted();
591 return angle::Result::Continue;
592 }
593
clearBufferiv(const gl::Context * context,GLenum buffer,GLint drawbuffer,const GLint * values)594 angle::Result FramebufferGL::clearBufferiv(const gl::Context *context,
595 GLenum buffer,
596 GLint drawbuffer,
597 const GLint *values)
598 {
599 ContextGL *contextGL = GetImplAs<ContextGL>(context);
600 const FunctionsGL *functions = GetFunctionsGL(context);
601 StateManagerGL *stateManager = GetStateManagerGL(context);
602
603 syncClearBufferState(context, buffer, drawbuffer);
604 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
605
606 if (!RequiresMultiviewClear(mState, context->getState().isScissorTestEnabled()))
607 {
608 functions->clearBufferiv(buffer, drawbuffer, values);
609 }
610 else
611 {
612 ClearMultiviewGL *multiviewClearer = GetMultiviewClearer(context);
613 multiviewClearer->clearMultiviewFBO(mState, context->getState().getScissor(),
614 ClearMultiviewGL::ClearCommandType::ClearBufferiv,
615 static_cast<GLbitfield>(0u), buffer, drawbuffer,
616 reinterpret_cast<const uint8_t *>(values), 0.0f, 0);
617 }
618
619 contextGL->markWorkSubmitted();
620 return angle::Result::Continue;
621 }
622
clearBufferfi(const gl::Context * context,GLenum buffer,GLint drawbuffer,GLfloat depth,GLint stencil)623 angle::Result FramebufferGL::clearBufferfi(const gl::Context *context,
624 GLenum buffer,
625 GLint drawbuffer,
626 GLfloat depth,
627 GLint stencil)
628 {
629 ContextGL *contextGL = GetImplAs<ContextGL>(context);
630 const FunctionsGL *functions = GetFunctionsGL(context);
631 StateManagerGL *stateManager = GetStateManagerGL(context);
632
633 syncClearBufferState(context, buffer, drawbuffer);
634 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
635
636 if (!RequiresMultiviewClear(mState, context->getState().isScissorTestEnabled()))
637 {
638 functions->clearBufferfi(buffer, drawbuffer, depth, stencil);
639 }
640 else
641 {
642 ClearMultiviewGL *multiviewClearer = GetMultiviewClearer(context);
643 multiviewClearer->clearMultiviewFBO(mState, context->getState().getScissor(),
644 ClearMultiviewGL::ClearCommandType::ClearBufferfi,
645 static_cast<GLbitfield>(0u), buffer, drawbuffer,
646 nullptr, depth, stencil);
647 }
648
649 contextGL->markWorkSubmitted();
650 return angle::Result::Continue;
651 }
652
readPixels(const gl::Context * context,const gl::Rectangle & area,GLenum format,GLenum type,const gl::PixelPackState & pack,gl::Buffer * packBuffer,void * pixels)653 angle::Result FramebufferGL::readPixels(const gl::Context *context,
654 const gl::Rectangle &area,
655 GLenum format,
656 GLenum type,
657 const gl::PixelPackState &pack,
658 gl::Buffer *packBuffer,
659 void *pixels)
660 {
661 ContextGL *contextGL = GetImplAs<ContextGL>(context);
662 const FunctionsGL *functions = GetFunctionsGL(context);
663 StateManagerGL *stateManager = GetStateManagerGL(context);
664 const angle::FeaturesGL &features = GetFeaturesGL(context);
665 gl::PixelPackState packState = pack;
666
667 // Clip read area to framebuffer.
668 const auto *readAttachment = mState.getReadPixelsAttachment(format);
669 const gl::Extents fbSize = readAttachment->getSize();
670 const gl::Rectangle fbRect(0, 0, fbSize.width, fbSize.height);
671 gl::Rectangle clippedArea;
672 if (!ClipRectangle(area, fbRect, &clippedArea))
673 {
674 // nothing to read
675 return angle::Result::Continue;
676 }
677
678 GLenum attachmentReadFormat =
679 readAttachment->getFormat().info->getReadPixelsFormat(context->getExtensions());
680 nativegl::ReadPixelsFormat readPixelsFormat =
681 nativegl::GetReadPixelsFormat(functions, features, attachmentReadFormat, format, type);
682 GLenum readFormat = readPixelsFormat.format;
683 GLenum readType = readPixelsFormat.type;
684 if (features.readPixelsUsingImplementationColorReadFormatForNorm16.enabled &&
685 readType == GL_UNSIGNED_SHORT)
686 {
687 ANGLE_CHECK(contextGL, IsValidUnsignedShortReadPixelsFormat(readFormat, context),
688 "glReadPixels: GL_IMPLEMENTATION_COLOR_READ_FORMAT advertised by the driver is "
689 "not handled by RGBA16 readPixels workaround.",
690 GL_INVALID_OPERATION);
691 }
692
693 GLenum framebufferTarget =
694 stateManager->getHasSeparateFramebufferBindings() ? GL_READ_FRAMEBUFFER : GL_FRAMEBUFFER;
695 stateManager->bindFramebuffer(framebufferTarget, mFramebufferID);
696
697 bool useOverlappingRowsWorkaround = features.packOverlappingRowsSeparatelyPackBuffer.enabled &&
698 packBuffer && packState.rowLength != 0 &&
699 packState.rowLength < clippedArea.width;
700
701 GLubyte *outPtr = static_cast<GLubyte *>(pixels);
702 int leftClip = clippedArea.x - area.x;
703 int topClip = clippedArea.y - area.y;
704 if (leftClip || topClip)
705 {
706 // Adjust destination to match portion clipped off left and/or top.
707 const gl::InternalFormat &glFormat = gl::GetInternalFormatInfo(readFormat, readType);
708
709 GLuint rowBytes = 0;
710 ANGLE_CHECK_GL_MATH(contextGL,
711 glFormat.computeRowPitch(readType, area.width, packState.alignment,
712 packState.rowLength, &rowBytes));
713 outPtr += leftClip * glFormat.pixelBytes + topClip * rowBytes;
714 }
715
716 if (packState.rowLength == 0 && clippedArea.width != area.width)
717 {
718 // No rowLength was specified so it will derive from read width, but clipping changed the
719 // read width. Use the original width so we fill the user's buffer as they intended.
720 packState.rowLength = area.width;
721 }
722
723 // We want to use rowLength, but that might not be supported.
724 bool cannotSetDesiredRowLength =
725 packState.rowLength && !GetImplAs<ContextGL>(context)->getNativeExtensions().packSubimageNV;
726
727 bool usePackSkipWorkaround = features.emulatePackSkipRowsAndPackSkipPixels.enabled &&
728 (packState.skipRows != 0 || packState.skipPixels != 0);
729
730 if (cannotSetDesiredRowLength || useOverlappingRowsWorkaround || usePackSkipWorkaround)
731 {
732 return readPixelsRowByRow(context, clippedArea, format, readFormat, readType, packState,
733 outPtr);
734 }
735
736 bool useLastRowPaddingWorkaround = false;
737 if (features.packLastRowSeparatelyForPaddingInclusion.enabled)
738 {
739 ANGLE_TRY(ShouldApplyLastRowPaddingWorkaround(
740 contextGL, gl::Extents(clippedArea.width, clippedArea.height, 1), packState, packBuffer,
741 readFormat, readType, false, outPtr, &useLastRowPaddingWorkaround));
742 }
743
744 return readPixelsAllAtOnce(context, clippedArea, format, readFormat, readType, packState,
745 outPtr, useLastRowPaddingWorkaround);
746 }
747
blit(const gl::Context * context,const gl::Rectangle & sourceArea,const gl::Rectangle & destArea,GLbitfield mask,GLenum filter)748 angle::Result FramebufferGL::blit(const gl::Context *context,
749 const gl::Rectangle &sourceArea,
750 const gl::Rectangle &destArea,
751 GLbitfield mask,
752 GLenum filter)
753 {
754 ContextGL *contextGL = GetImplAs<ContextGL>(context);
755 const FunctionsGL *functions = GetFunctionsGL(context);
756 StateManagerGL *stateManager = GetStateManagerGL(context);
757 const angle::FeaturesGL &features = GetFeaturesGL(context);
758
759 const Framebuffer *sourceFramebuffer = context->getState().getReadFramebuffer();
760 const Framebuffer *destFramebuffer = context->getState().getDrawFramebuffer();
761
762 const FramebufferAttachment *colorReadAttachment = sourceFramebuffer->getReadColorAttachment();
763
764 GLsizei readAttachmentSamples = 0;
765 if (colorReadAttachment != nullptr)
766 {
767 // Blitting requires that the textures be single sampled. getSamples will return
768 // emulated sample number, but the EXT_multisampled_render_to_texture extension will
769 // take care of resolving the texture, so even if emulated samples > 0, we should still
770 // be able to blit as long as the underlying resource samples is single sampled.
771 readAttachmentSamples = colorReadAttachment->getResourceSamples();
772 }
773
774 bool needManualColorBlit = false;
775
776 // TODO(cwallez) when the filter is LINEAR and both source and destination are SRGB, we
777 // could avoid doing a manual blit.
778
779 // Prior to OpenGL 4.4 BlitFramebuffer (section 18.3.1 of GL 4.3 core profile) reads:
780 // When values are taken from the read buffer, no linearization is performed, even
781 // if the format of the buffer is SRGB.
782 // Starting from OpenGL 4.4 (section 18.3.1) it reads:
783 // When values are taken from the read buffer, if FRAMEBUFFER_SRGB is enabled and the
784 // value of FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING for the framebuffer attachment
785 // corresponding to the read buffer is SRGB, the red, green, and blue components are
786 // converted from the non-linear sRGB color space according [...].
787 {
788 bool sourceSRGB =
789 colorReadAttachment != nullptr && colorReadAttachment->getColorEncoding() == GL_SRGB;
790 needManualColorBlit =
791 needManualColorBlit || (sourceSRGB && functions->isAtMostGL(gl::Version(4, 3)));
792 }
793
794 // Prior to OpenGL 4.2 BlitFramebuffer (section 4.3.2 of GL 4.1 core profile) reads:
795 // Blit operations bypass the fragment pipeline. The only fragment operations which
796 // affect a blit are the pixel ownership test and scissor test.
797 // Starting from OpenGL 4.2 (section 4.3.2) it reads:
798 // When values are written to the draw buffers, blit operations bypass the fragment
799 // pipeline. The only fragment operations which affect a blit are the pixel ownership
800 // test, the scissor test and sRGB conversion.
801 if (!needManualColorBlit)
802 {
803 bool destSRGB = false;
804 for (size_t i = 0; i < destFramebuffer->getDrawbufferStateCount(); ++i)
805 {
806 const FramebufferAttachment *attachment = destFramebuffer->getDrawBuffer(i);
807 if (attachment && attachment->getColorEncoding() == GL_SRGB)
808 {
809 destSRGB = true;
810 break;
811 }
812 }
813
814 needManualColorBlit =
815 needManualColorBlit || (destSRGB && functions->isAtMostGL(gl::Version(4, 1)));
816 }
817
818 // If the destination has an emulated alpha channel, we need to blit with a shader with alpha
819 // writes disabled.
820 if (mHasEmulatedAlphaAttachment)
821 {
822 needManualColorBlit = true;
823 }
824
825 // Enable FRAMEBUFFER_SRGB if needed
826 stateManager->setFramebufferSRGBEnabledForFramebuffer(context, true, this);
827
828 GLenum blitMask = mask;
829 if (needManualColorBlit && (mask & GL_COLOR_BUFFER_BIT) && readAttachmentSamples <= 1)
830 {
831 BlitGL *blitter = GetBlitGL(context);
832 ANGLE_TRY(blitter->blitColorBufferWithShader(context, sourceFramebuffer, destFramebuffer,
833 sourceArea, destArea, filter,
834 !mHasEmulatedAlphaAttachment));
835 blitMask &= ~GL_COLOR_BUFFER_BIT;
836 }
837
838 if (blitMask == 0)
839 {
840 return angle::Result::Continue;
841 }
842
843 const FramebufferGL *sourceFramebufferGL = GetImplAs<FramebufferGL>(sourceFramebuffer);
844 stateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, sourceFramebufferGL->getFramebufferID());
845 stateManager->bindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferID);
846
847 gl::Rectangle finalSourceArea(sourceArea);
848 gl::Rectangle finalDestArea(destArea);
849
850 if (features.adjustSrcDstRegionForBlitFramebuffer.enabled)
851 {
852 angle::Result result = adjustSrcDstRegion(context, finalSourceArea, finalDestArea,
853 &finalSourceArea, &finalDestArea);
854 if (result != angle::Result::Continue)
855 {
856 return result;
857 }
858 }
859 if (features.clipSrcRegionForBlitFramebuffer.enabled)
860 {
861 angle::Result result = clipSrcRegion(context, finalSourceArea, finalDestArea,
862 &finalSourceArea, &finalDestArea);
863 if (result != angle::Result::Continue)
864 {
865 return result;
866 }
867 }
868
869 functions->blitFramebuffer(finalSourceArea.x, finalSourceArea.y, finalSourceArea.x1(),
870 finalSourceArea.y1(), finalDestArea.x, finalDestArea.y,
871 finalDestArea.x1(), finalDestArea.y1(), blitMask, filter);
872
873 contextGL->markWorkSubmitted();
874 return angle::Result::Continue;
875 }
876
adjustSrcDstRegion(const gl::Context * context,const gl::Rectangle & sourceArea,const gl::Rectangle & destArea,gl::Rectangle * newSourceArea,gl::Rectangle * newDestArea)877 angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context,
878 const gl::Rectangle &sourceArea,
879 const gl::Rectangle &destArea,
880 gl::Rectangle *newSourceArea,
881 gl::Rectangle *newDestArea)
882 {
883 BlitFramebufferBounds bounds = GetBlitFramebufferBounds(context, sourceArea, destArea);
884
885 if (bounds.destRegion.width == 0 || bounds.sourceRegion.width == 0 ||
886 bounds.destRegion.height == 0 || bounds.sourceRegion.height == 0)
887 {
888 return angle::Result::Stop;
889 }
890 if (!ClipRectangle(bounds.destBounds, bounds.destRegion, nullptr))
891 {
892 return angle::Result::Stop;
893 }
894
895 if (!bounds.destBounds.encloses(bounds.destRegion))
896 {
897 // destRegion is not within destBounds. We want to adjust it to a
898 // reasonable size. This is done by halving the destRegion until it is at
899 // most twice the size of the framebuffer. We cut it in half instead
900 // of arbitrarily shrinking it to fit so that we don't end up with
901 // non-power-of-two scale factors which could mess up pixel interpolation.
902 // Naively clipping the dst rect and then proportionally sizing the
903 // src rect yields incorrect results.
904
905 GLuint destXHalvings = 0;
906 GLuint destYHalvings = 0;
907 GLint destOriginX = bounds.destRegion.x;
908 GLint destOriginY = bounds.destRegion.y;
909
910 GLint destClippedWidth = bounds.destRegion.width;
911 while (destClippedWidth > 2 * bounds.destBounds.width)
912 {
913 destClippedWidth = destClippedWidth / 2;
914 destXHalvings++;
915 }
916
917 GLint destClippedHeight = bounds.destRegion.height;
918 while (destClippedHeight > 2 * bounds.destBounds.height)
919 {
920 destClippedHeight = destClippedHeight / 2;
921 destYHalvings++;
922 }
923
924 // Before this block, we check that the two rectangles intersect.
925 // Now, compute the location of a new region origin such that we use the
926 // scaled dimensions but the new region has the same intersection as the
927 // original region.
928
929 GLint left = bounds.destRegion.x0();
930 GLint right = bounds.destRegion.x1();
931 GLint top = bounds.destRegion.y0();
932 GLint bottom = bounds.destRegion.y1();
933
934 GLint extraXOffset = 0;
935 if (left >= 0 && left < bounds.destBounds.width)
936 {
937 // Left edge is in-bounds
938 destOriginX = bounds.destRegion.x;
939 }
940 else if (right > 0 && right <= bounds.destBounds.width)
941 {
942 // Right edge is in-bounds
943 destOriginX = right - destClippedWidth;
944 }
945 else
946 {
947 // Region completely spans bounds
948 extraXOffset = (bounds.destRegion.width - destClippedWidth) / 2;
949 destOriginX = bounds.destRegion.x + extraXOffset;
950 }
951
952 GLint extraYOffset = 0;
953 if (top >= 0 && top < bounds.destBounds.height)
954 {
955 // Top edge is in-bounds
956 destOriginY = bounds.destRegion.y;
957 }
958 else if (bottom > 0 && bottom <= bounds.destBounds.height)
959 {
960 // Bottom edge is in-bounds
961 destOriginY = bottom - destClippedHeight;
962 }
963 else
964 {
965 // Region completely spans bounds
966 extraYOffset = (bounds.destRegion.height - destClippedHeight) / 2;
967 destOriginY = bounds.destRegion.y + extraYOffset;
968 }
969
970 // Offsets from the bottom left corner of the original region to
971 // the bottom left corner of the clipped region.
972 // This value (after it is scaled) is the respective offset we will apply
973 // to the src origin.
974
975 CheckedNumeric<GLuint> checkedXOffset(destOriginX - bounds.destRegion.x - extraXOffset / 2);
976 CheckedNumeric<GLuint> checkedYOffset(destOriginY - bounds.destRegion.y - extraYOffset / 2);
977
978 // if X/Y is reversed, use the top/right out-of-bounds region to compute
979 // the origin offset instead of the left/bottom out-of-bounds region
980 if (bounds.xFlipped)
981 {
982 checkedXOffset =
983 (bounds.destRegion.x1() - (destOriginX + destClippedWidth) + extraXOffset / 2);
984 }
985 if (bounds.yFlipped)
986 {
987 checkedYOffset =
988 (bounds.destRegion.y1() - (destOriginY + destClippedHeight) + extraYOffset / 2);
989 }
990
991 // These offsets should never overflow
992 GLuint xOffset, yOffset;
993 if (!checkedXOffset.AssignIfValid(&xOffset) || !checkedYOffset.AssignIfValid(&yOffset))
994 {
995 UNREACHABLE();
996 return angle::Result::Stop;
997 }
998
999 bounds.destRegion =
1000 gl::Rectangle(destOriginX, destOriginY, destClippedWidth, destClippedHeight);
1001
1002 // Adjust the src region by the same factor
1003 bounds.sourceRegion = gl::Rectangle(bounds.sourceRegion.x + (xOffset >> destXHalvings),
1004 bounds.sourceRegion.y + (yOffset >> destYHalvings),
1005 bounds.sourceRegion.width >> destXHalvings,
1006 bounds.sourceRegion.height >> destYHalvings);
1007
1008 // if the src was scaled to 0, set it to 1 so the src is non-empty
1009 if (bounds.sourceRegion.width == 0)
1010 {
1011 bounds.sourceRegion.width = 1;
1012 }
1013 if (bounds.sourceRegion.height == 0)
1014 {
1015 bounds.sourceRegion.height = 1;
1016 }
1017 }
1018
1019 if (!bounds.sourceBounds.encloses(bounds.sourceRegion))
1020 {
1021 // sourceRegion is not within sourceBounds. We want to adjust it to a
1022 // reasonable size. This is done by halving the sourceRegion until it is at
1023 // most twice the size of the framebuffer. We cut it in half instead
1024 // of arbitrarily shrinking it to fit so that we don't end up with
1025 // non-power-of-two scale factors which could mess up pixel interpolation.
1026 // Naively clipping the source rect and then proportionally sizing the
1027 // dest rect yields incorrect results.
1028
1029 GLuint sourceXHalvings = 0;
1030 GLuint sourceYHalvings = 0;
1031 GLint sourceOriginX = bounds.sourceRegion.x;
1032 GLint sourceOriginY = bounds.sourceRegion.y;
1033
1034 GLint sourceClippedWidth = bounds.sourceRegion.width;
1035 while (sourceClippedWidth > 2 * bounds.sourceBounds.width)
1036 {
1037 sourceClippedWidth = sourceClippedWidth / 2;
1038 sourceXHalvings++;
1039 }
1040
1041 GLint sourceClippedHeight = bounds.sourceRegion.height;
1042 while (sourceClippedHeight > 2 * bounds.sourceBounds.height)
1043 {
1044 sourceClippedHeight = sourceClippedHeight / 2;
1045 sourceYHalvings++;
1046 }
1047
1048 // Before this block, we check that the two rectangles intersect.
1049 // Now, compute the location of a new region origin such that we use the
1050 // scaled dimensions but the new region has the same intersection as the
1051 // original region.
1052
1053 GLint left = bounds.sourceRegion.x0();
1054 GLint right = bounds.sourceRegion.x1();
1055 GLint top = bounds.sourceRegion.y0();
1056 GLint bottom = bounds.sourceRegion.y1();
1057
1058 GLint extraXOffset = 0;
1059 if (left >= 0 && left < bounds.sourceBounds.width)
1060 {
1061 // Left edge is in-bounds
1062 sourceOriginX = bounds.sourceRegion.x;
1063 }
1064 else if (right > 0 && right <= bounds.sourceBounds.width)
1065 {
1066 // Right edge is in-bounds
1067 sourceOriginX = right - sourceClippedWidth;
1068 }
1069 else
1070 {
1071 // Region completely spans bounds
1072 extraXOffset = (bounds.sourceRegion.width - sourceClippedWidth) / 2;
1073 sourceOriginX = bounds.sourceRegion.x + extraXOffset;
1074 }
1075
1076 GLint extraYOffset = 0;
1077 if (top >= 0 && top < bounds.sourceBounds.height)
1078 {
1079 // Top edge is in-bounds
1080 sourceOriginY = bounds.sourceRegion.y;
1081 }
1082 else if (bottom > 0 && bottom <= bounds.sourceBounds.height)
1083 {
1084 // Bottom edge is in-bounds
1085 sourceOriginY = bottom - sourceClippedHeight;
1086 }
1087 else
1088 {
1089 // Region completely spans bounds
1090 extraYOffset = (bounds.sourceRegion.height - sourceClippedHeight) / 2;
1091 sourceOriginY = bounds.sourceRegion.y + extraYOffset;
1092 }
1093
1094 // Offsets from the bottom left corner of the original region to
1095 // the bottom left corner of the clipped region.
1096 // This value (after it is scaled) is the respective offset we will apply
1097 // to the dest origin.
1098
1099 CheckedNumeric<GLuint> checkedXOffset(sourceOriginX - bounds.sourceRegion.x -
1100 extraXOffset / 2);
1101 CheckedNumeric<GLuint> checkedYOffset(sourceOriginY - bounds.sourceRegion.y -
1102 extraYOffset / 2);
1103
1104 // if X/Y is reversed, use the top/right out-of-bounds region to compute
1105 // the origin offset instead of the left/bottom out-of-bounds region
1106 if (bounds.xFlipped)
1107 {
1108 checkedXOffset = (bounds.sourceRegion.x1() - (sourceOriginX + sourceClippedWidth) +
1109 extraXOffset / 2);
1110 }
1111 if (bounds.yFlipped)
1112 {
1113 checkedYOffset = (bounds.sourceRegion.y1() - (sourceOriginY + sourceClippedHeight) +
1114 extraYOffset / 2);
1115 }
1116
1117 // These offsets should never overflow
1118 GLuint xOffset, yOffset;
1119 if (!checkedXOffset.AssignIfValid(&xOffset) || !checkedYOffset.AssignIfValid(&yOffset))
1120 {
1121 UNREACHABLE();
1122 return angle::Result::Stop;
1123 }
1124
1125 bounds.sourceRegion =
1126 gl::Rectangle(sourceOriginX, sourceOriginY, sourceClippedWidth, sourceClippedHeight);
1127
1128 // Adjust the dest region by the same factor
1129 bounds.destRegion = gl::Rectangle(bounds.destRegion.x + (xOffset >> sourceXHalvings),
1130 bounds.destRegion.y + (yOffset >> sourceYHalvings),
1131 bounds.destRegion.width >> sourceXHalvings,
1132 bounds.destRegion.height >> sourceYHalvings);
1133 }
1134 // Set the src and dst endpoints. If they were previously flipped,
1135 // set them as flipped.
1136 *newSourceArea = bounds.sourceRegion.flip(sourceArea.isReversedX(), sourceArea.isReversedY());
1137 *newDestArea = bounds.destRegion.flip(destArea.isReversedX(), destArea.isReversedY());
1138
1139 return angle::Result::Continue;
1140 }
1141
clipSrcRegion(const gl::Context * context,const gl::Rectangle & sourceArea,const gl::Rectangle & destArea,gl::Rectangle * newSourceArea,gl::Rectangle * newDestArea)1142 angle::Result FramebufferGL::clipSrcRegion(const gl::Context *context,
1143 const gl::Rectangle &sourceArea,
1144 const gl::Rectangle &destArea,
1145 gl::Rectangle *newSourceArea,
1146 gl::Rectangle *newDestArea)
1147 {
1148 BlitFramebufferBounds bounds = GetBlitFramebufferBounds(context, sourceArea, destArea);
1149
1150 if (bounds.destRegion.width == 0 || bounds.sourceRegion.width == 0 ||
1151 bounds.destRegion.height == 0 || bounds.sourceRegion.height == 0)
1152 {
1153 return angle::Result::Stop;
1154 }
1155 if (!ClipRectangle(bounds.destBounds, bounds.destRegion, nullptr))
1156 {
1157 return angle::Result::Stop;
1158 }
1159
1160 if (!bounds.sourceBounds.encloses(bounds.sourceRegion))
1161 {
1162 // If pixels lying outside the read framebuffer, adjust src region
1163 // and dst region to appropriate in-bounds regions respectively.
1164 gl::Rectangle realSourceRegion;
1165 if (!ClipRectangle(bounds.sourceRegion, bounds.sourceBounds, &realSourceRegion))
1166 {
1167 return angle::Result::Stop;
1168 }
1169 GLuint xOffset = realSourceRegion.x - bounds.sourceRegion.x;
1170 GLuint yOffset = realSourceRegion.y - bounds.sourceRegion.y;
1171
1172 // if X/Y is reversed, use the top/right out-of-bounds region for mapping
1173 // to dst region, instead of left/bottom out-of-bounds region for mapping.
1174 if (bounds.xFlipped)
1175 {
1176 xOffset = bounds.sourceRegion.x1() - realSourceRegion.x1();
1177 }
1178 if (bounds.yFlipped)
1179 {
1180 yOffset = bounds.sourceRegion.y1() - realSourceRegion.y1();
1181 }
1182
1183 GLfloat destMappingWidth = static_cast<GLfloat>(realSourceRegion.width) *
1184 bounds.destRegion.width / bounds.sourceRegion.width;
1185 GLfloat destMappingHeight = static_cast<GLfloat>(realSourceRegion.height) *
1186 bounds.destRegion.height / bounds.sourceRegion.height;
1187 GLfloat destMappingXOffset =
1188 static_cast<GLfloat>(xOffset) * bounds.destRegion.width / bounds.sourceRegion.width;
1189 GLfloat destMappingYOffset =
1190 static_cast<GLfloat>(yOffset) * bounds.destRegion.height / bounds.sourceRegion.height;
1191
1192 GLuint destMappingX0 =
1193 static_cast<GLuint>(std::round(bounds.destRegion.x + destMappingXOffset));
1194 GLuint destMappingY0 =
1195 static_cast<GLuint>(std::round(bounds.destRegion.y + destMappingYOffset));
1196
1197 GLuint destMappingX1 = static_cast<GLuint>(
1198 std::round(bounds.destRegion.x + destMappingXOffset + destMappingWidth));
1199 GLuint destMappingY1 = static_cast<GLuint>(
1200 std::round(bounds.destRegion.y + destMappingYOffset + destMappingHeight));
1201
1202 bounds.destRegion =
1203 gl::Rectangle(destMappingX0, destMappingY0, destMappingX1 - destMappingX0,
1204 destMappingY1 - destMappingY0);
1205
1206 bounds.sourceRegion = realSourceRegion;
1207 }
1208 // Set the src and dst endpoints. If they were previously flipped,
1209 // set them as flipped.
1210 *newSourceArea = bounds.sourceRegion.flip(sourceArea.isReversedX(), sourceArea.isReversedY());
1211 *newDestArea = bounds.destRegion.flip(destArea.isReversedX(), destArea.isReversedY());
1212
1213 return angle::Result::Continue;
1214 }
1215
getSamplePosition(const gl::Context * context,size_t index,GLfloat * xy) const1216 angle::Result FramebufferGL::getSamplePosition(const gl::Context *context,
1217 size_t index,
1218 GLfloat *xy) const
1219 {
1220 const FunctionsGL *functions = GetFunctionsGL(context);
1221 StateManagerGL *stateManager = GetStateManagerGL(context);
1222
1223 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
1224 functions->getMultisamplefv(GL_SAMPLE_POSITION, static_cast<GLuint>(index), xy);
1225 return angle::Result::Continue;
1226 }
1227
shouldSyncStateBeforeCheckStatus() const1228 bool FramebufferGL::shouldSyncStateBeforeCheckStatus() const
1229 {
1230 return true;
1231 }
1232
checkStatus(const gl::Context * context) const1233 gl::FramebufferStatus FramebufferGL::checkStatus(const gl::Context *context) const
1234 {
1235 const FunctionsGL *functions = GetFunctionsGL(context);
1236 StateManagerGL *stateManager = GetStateManagerGL(context);
1237
1238 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
1239 GLenum status = functions->checkFramebufferStatus(GL_FRAMEBUFFER);
1240 if (status != GL_FRAMEBUFFER_COMPLETE)
1241 {
1242 WARN() << "GL framebuffer returned incomplete: " << gl::FmtHex(status);
1243 return gl::FramebufferStatus::Incomplete(GL_FRAMEBUFFER_UNSUPPORTED,
1244 gl::err::kFramebufferIncompleteDriverUnsupported);
1245 }
1246
1247 return gl::FramebufferStatus::Complete();
1248 }
1249
syncState(const gl::Context * context,GLenum binding,const gl::Framebuffer::DirtyBits & dirtyBits,gl::Command command)1250 angle::Result FramebufferGL::syncState(const gl::Context *context,
1251 GLenum binding,
1252 const gl::Framebuffer::DirtyBits &dirtyBits,
1253 gl::Command command)
1254 {
1255 // Don't need to sync state for the default FBO.
1256 if (mIsDefault)
1257 {
1258 return angle::Result::Continue;
1259 }
1260
1261 const FunctionsGL *functions = GetFunctionsGL(context);
1262 StateManagerGL *stateManager = GetStateManagerGL(context);
1263
1264 stateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebufferID);
1265
1266 // A pointer to one of the attachments for which the texture or the render buffer is not zero.
1267 const FramebufferAttachment *attachment = nullptr;
1268
1269 for (auto dirtyBit : dirtyBits)
1270 {
1271 switch (dirtyBit)
1272 {
1273 case Framebuffer::DIRTY_BIT_DEPTH_ATTACHMENT:
1274 {
1275 const FramebufferAttachment *newAttachment = mState.getDepthAttachment();
1276 BindFramebufferAttachment(functions, GL_DEPTH_ATTACHMENT, newAttachment,
1277 GetFeaturesGL(context));
1278 if (newAttachment)
1279 {
1280 attachment = newAttachment;
1281 }
1282 break;
1283 }
1284 case Framebuffer::DIRTY_BIT_STENCIL_ATTACHMENT:
1285 {
1286 const FramebufferAttachment *newAttachment = mState.getStencilAttachment();
1287 BindFramebufferAttachment(functions, GL_STENCIL_ATTACHMENT, newAttachment,
1288 GetFeaturesGL(context));
1289 if (newAttachment)
1290 {
1291 attachment = newAttachment;
1292 }
1293 break;
1294 }
1295 case Framebuffer::DIRTY_BIT_DRAW_BUFFERS:
1296 {
1297 const auto &drawBuffers = mState.getDrawBufferStates();
1298 functions->drawBuffers(static_cast<GLsizei>(drawBuffers.size()),
1299 drawBuffers.data());
1300 mAppliedEnabledDrawBuffers = mState.getEnabledDrawBuffers();
1301 break;
1302 }
1303 case Framebuffer::DIRTY_BIT_READ_BUFFER:
1304 functions->readBuffer(mState.getReadBufferState());
1305 break;
1306 case Framebuffer::DIRTY_BIT_DEFAULT_WIDTH:
1307 functions->framebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH,
1308 mState.getDefaultWidth());
1309 break;
1310 case Framebuffer::DIRTY_BIT_DEFAULT_HEIGHT:
1311 functions->framebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT,
1312 mState.getDefaultHeight());
1313 break;
1314 case Framebuffer::DIRTY_BIT_DEFAULT_SAMPLES:
1315 functions->framebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_SAMPLES,
1316 mState.getDefaultSamples());
1317 break;
1318 case Framebuffer::DIRTY_BIT_DEFAULT_FIXED_SAMPLE_LOCATIONS:
1319 functions->framebufferParameteri(
1320 GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS,
1321 gl::ConvertToGLBoolean(mState.getDefaultFixedSampleLocations()));
1322 break;
1323 case Framebuffer::DIRTY_BIT_DEFAULT_LAYERS:
1324 functions->framebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_LAYERS_EXT,
1325 mState.getDefaultLayers());
1326 break;
1327 case Framebuffer::DIRTY_BIT_FLIP_Y:
1328 functions->framebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_FLIP_Y_MESA,
1329 gl::ConvertToGLBoolean(mState.getFlipY()));
1330 break;
1331 default:
1332 {
1333 static_assert(Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0 == 0, "FB dirty bits");
1334 if (dirtyBit < Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_MAX)
1335 {
1336 size_t index =
1337 static_cast<size_t>(dirtyBit - Framebuffer::DIRTY_BIT_COLOR_ATTACHMENT_0);
1338 const FramebufferAttachment *newAttachment = mState.getColorAttachment(index);
1339 BindFramebufferAttachment(functions,
1340 static_cast<GLenum>(GL_COLOR_ATTACHMENT0 + index),
1341 newAttachment, GetFeaturesGL(context));
1342 if (newAttachment)
1343 {
1344 attachment = newAttachment;
1345 }
1346
1347 // Hiding an alpha channel is only supported when it's the first attachment
1348 // currently. Assert that these emulated textures are not bound to a framebuffer
1349 // using MRT.
1350 if (index == 0)
1351 {
1352 mHasEmulatedAlphaAttachment =
1353 IsEmulatedAlphaChannelTextureAttachment(attachment);
1354 }
1355 ASSERT(index == 0 || !IsEmulatedAlphaChannelTextureAttachment(attachment));
1356 }
1357 break;
1358 }
1359 }
1360 }
1361
1362 if (attachment && mState.id() == context->getState().getDrawFramebuffer()->id())
1363 {
1364 stateManager->updateMultiviewBaseViewLayerIndexUniform(context->getState().getProgram(),
1365 getState());
1366 }
1367
1368 return angle::Result::Continue;
1369 }
1370
getFramebufferID() const1371 GLuint FramebufferGL::getFramebufferID() const
1372 {
1373 return mFramebufferID;
1374 }
1375
updateDefaultFramebufferID(GLuint framebufferID)1376 void FramebufferGL::updateDefaultFramebufferID(GLuint framebufferID)
1377 {
1378 // We only update framebufferID for a default frambuffer, and the framebufferID is created
1379 // externally. ANGLE doesn't owne it.
1380 ASSERT(isDefault());
1381 mFramebufferID = framebufferID;
1382 }
1383
isDefault() const1384 bool FramebufferGL::isDefault() const
1385 {
1386 return mIsDefault;
1387 }
1388
hasEmulatedAlphaChannelTextureAttachment() const1389 bool FramebufferGL::hasEmulatedAlphaChannelTextureAttachment() const
1390 {
1391 return mHasEmulatedAlphaAttachment;
1392 }
1393
syncClearState(const gl::Context * context,GLbitfield mask)1394 void FramebufferGL::syncClearState(const gl::Context *context, GLbitfield mask)
1395 {
1396 StateManagerGL *stateManager = GetStateManagerGL(context);
1397 const angle::FeaturesGL &features = GetFeaturesGL(context);
1398
1399 if (features.doesSRGBClearsOnLinearFramebufferAttachments.enabled &&
1400 (mask & GL_COLOR_BUFFER_BIT) != 0 && !mIsDefault)
1401 {
1402 bool hasSRGBAttachment = false;
1403 for (const auto &attachment : mState.getColorAttachments())
1404 {
1405 if (attachment.isAttached() && attachment.getColorEncoding() == GL_SRGB)
1406 {
1407 hasSRGBAttachment = true;
1408 break;
1409 }
1410 }
1411
1412 stateManager->setFramebufferSRGBEnabled(context, hasSRGBAttachment);
1413 }
1414 else
1415 {
1416 stateManager->setFramebufferSRGBEnabled(context, !mIsDefault);
1417 }
1418 }
1419
syncClearBufferState(const gl::Context * context,GLenum buffer,GLint drawBuffer)1420 void FramebufferGL::syncClearBufferState(const gl::Context *context,
1421 GLenum buffer,
1422 GLint drawBuffer)
1423 {
1424 StateManagerGL *stateManager = GetStateManagerGL(context);
1425 const angle::FeaturesGL &features = GetFeaturesGL(context);
1426
1427 if (features.doesSRGBClearsOnLinearFramebufferAttachments.enabled && buffer == GL_COLOR &&
1428 !mIsDefault)
1429 {
1430 // If doing a clear on a color buffer, set SRGB blend enabled only if the color buffer
1431 // is an SRGB format.
1432 const auto &drawbufferState = mState.getDrawBufferStates();
1433 const auto &colorAttachments = mState.getColorAttachments();
1434
1435 const FramebufferAttachment *attachment = nullptr;
1436 if (drawbufferState[drawBuffer] >= GL_COLOR_ATTACHMENT0 &&
1437 drawbufferState[drawBuffer] < GL_COLOR_ATTACHMENT0 + colorAttachments.size())
1438 {
1439 size_t attachmentIdx =
1440 static_cast<size_t>(drawbufferState[drawBuffer] - GL_COLOR_ATTACHMENT0);
1441 attachment = &colorAttachments[attachmentIdx];
1442 }
1443
1444 if (attachment != nullptr)
1445 {
1446 stateManager->setFramebufferSRGBEnabled(context,
1447 attachment->getColorEncoding() == GL_SRGB);
1448 }
1449 }
1450 else
1451 {
1452 stateManager->setFramebufferSRGBEnabled(context, !mIsDefault);
1453 }
1454 }
1455
modifyInvalidateAttachmentsForEmulatedDefaultFBO(size_t count,const GLenum * attachments,std::vector<GLenum> * modifiedAttachments) const1456 bool FramebufferGL::modifyInvalidateAttachmentsForEmulatedDefaultFBO(
1457 size_t count,
1458 const GLenum *attachments,
1459 std::vector<GLenum> *modifiedAttachments) const
1460 {
1461 bool needsModification = mIsDefault && mFramebufferID != 0;
1462 if (!needsModification)
1463 {
1464 return false;
1465 }
1466
1467 modifiedAttachments->resize(count);
1468 for (size_t i = 0; i < count; i++)
1469 {
1470 switch (attachments[i])
1471 {
1472 case GL_COLOR:
1473 (*modifiedAttachments)[i] = GL_COLOR_ATTACHMENT0;
1474 break;
1475
1476 case GL_DEPTH:
1477 (*modifiedAttachments)[i] = GL_DEPTH_ATTACHMENT;
1478 break;
1479
1480 case GL_STENCIL:
1481 (*modifiedAttachments)[i] = GL_STENCIL_ATTACHMENT;
1482 break;
1483
1484 default:
1485 UNREACHABLE();
1486 break;
1487 }
1488 }
1489
1490 return true;
1491 }
1492
readPixelsRowByRow(const gl::Context * context,const gl::Rectangle & area,GLenum originalReadFormat,GLenum format,GLenum type,const gl::PixelPackState & pack,GLubyte * pixels) const1493 angle::Result FramebufferGL::readPixelsRowByRow(const gl::Context *context,
1494 const gl::Rectangle &area,
1495 GLenum originalReadFormat,
1496 GLenum format,
1497 GLenum type,
1498 const gl::PixelPackState &pack,
1499 GLubyte *pixels) const
1500 {
1501 ContextGL *contextGL = GetImplAs<ContextGL>(context);
1502 const FunctionsGL *functions = GetFunctionsGL(context);
1503 StateManagerGL *stateManager = GetStateManagerGL(context);
1504 GLubyte *originalReadFormatPixels = pixels;
1505
1506 const gl::InternalFormat &glFormat = gl::GetInternalFormatInfo(format, type);
1507
1508 GLuint rowBytes = 0;
1509 ANGLE_CHECK_GL_MATH(contextGL, glFormat.computeRowPitch(type, area.width, pack.alignment,
1510 pack.rowLength, &rowBytes));
1511 GLuint skipBytes = 0;
1512 ANGLE_CHECK_GL_MATH(contextGL,
1513 glFormat.computeSkipBytes(type, rowBytes, 0, pack, false, &skipBytes));
1514
1515 ScopedEXTTextureNorm16ReadbackWorkaround workaround;
1516 angle::Result result =
1517 workaround.Initialize(context, area, originalReadFormat, format, type, skipBytes, rowBytes,
1518 glFormat.computePixelBytes(type), pixels);
1519 if (result != angle::Result::Continue)
1520 {
1521 return result;
1522 }
1523
1524 gl::PixelPackState directPack;
1525 directPack.alignment = 1;
1526 ANGLE_TRY(stateManager->setPixelPackState(context, directPack));
1527
1528 GLubyte *readbackPixels = workaround.Pixels();
1529 readbackPixels += skipBytes;
1530 for (GLint y = area.y; y < area.y + area.height; ++y)
1531 {
1532 ANGLE_GL_TRY(context,
1533 functions->readPixels(area.x, y, area.width, 1, format, type, readbackPixels));
1534 readbackPixels += rowBytes;
1535 }
1536
1537 if (workaround.IsEnabled())
1538 {
1539 return RearrangeEXTTextureNorm16Pixels(
1540 context, area, originalReadFormat, format, type, skipBytes, rowBytes,
1541 glFormat.computePixelBytes(type), pack, originalReadFormatPixels, workaround.Pixels());
1542 }
1543
1544 return angle::Result::Continue;
1545 }
1546
readPixelsAllAtOnce(const gl::Context * context,const gl::Rectangle & area,GLenum originalReadFormat,GLenum format,GLenum type,const gl::PixelPackState & pack,GLubyte * pixels,bool readLastRowSeparately) const1547 angle::Result FramebufferGL::readPixelsAllAtOnce(const gl::Context *context,
1548 const gl::Rectangle &area,
1549 GLenum originalReadFormat,
1550 GLenum format,
1551 GLenum type,
1552 const gl::PixelPackState &pack,
1553 GLubyte *pixels,
1554 bool readLastRowSeparately) const
1555 {
1556 ContextGL *contextGL = GetImplAs<ContextGL>(context);
1557 const FunctionsGL *functions = GetFunctionsGL(context);
1558 StateManagerGL *stateManager = GetStateManagerGL(context);
1559 GLubyte *originalReadFormatPixels = pixels;
1560
1561 const gl::InternalFormat &glFormat = gl::GetInternalFormatInfo(format, type);
1562
1563 GLuint rowBytes = 0;
1564 ANGLE_CHECK_GL_MATH(contextGL, glFormat.computeRowPitch(type, area.width, pack.alignment,
1565 pack.rowLength, &rowBytes));
1566 GLuint skipBytes = 0;
1567 ANGLE_CHECK_GL_MATH(contextGL,
1568 glFormat.computeSkipBytes(type, rowBytes, 0, pack, false, &skipBytes));
1569
1570 ScopedEXTTextureNorm16ReadbackWorkaround workaround;
1571 angle::Result result =
1572 workaround.Initialize(context, area, originalReadFormat, format, type, skipBytes, rowBytes,
1573 glFormat.computePixelBytes(type), pixels);
1574 if (result != angle::Result::Continue)
1575 {
1576 return result;
1577 }
1578
1579 GLint height = area.height - readLastRowSeparately;
1580 if (height > 0)
1581 {
1582 ANGLE_TRY(stateManager->setPixelPackState(context, pack));
1583 ANGLE_GL_TRY(context, functions->readPixels(area.x, area.y, area.width, height, format,
1584 type, workaround.Pixels()));
1585 }
1586
1587 if (readLastRowSeparately)
1588 {
1589 gl::PixelPackState directPack;
1590 directPack.alignment = 1;
1591 ANGLE_TRY(stateManager->setPixelPackState(context, directPack));
1592
1593 GLubyte *readbackPixels = workaround.Pixels();
1594 readbackPixels += skipBytes + (area.height - 1) * rowBytes;
1595 ANGLE_GL_TRY(context, functions->readPixels(area.x, area.y + area.height - 1, area.width, 1,
1596 format, type, readbackPixels));
1597 }
1598
1599 if (workaround.IsEnabled())
1600 {
1601 return RearrangeEXTTextureNorm16Pixels(
1602 context, area, originalReadFormat, format, type, skipBytes, rowBytes,
1603 glFormat.computePixelBytes(type), pack, originalReadFormatPixels, workaround.Pixels());
1604 }
1605
1606 return angle::Result::Continue;
1607 }
1608 } // namespace rx
1609