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