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