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