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