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