Bug 238788 - ANGLE incorrectly calculates if any frame buffer draw buffers are masked
Summary: ANGLE incorrectly calculates if any frame buffer draw buffers are masked
Status: ASSIGNED
Alias: None
Product: WebKit
Classification: Unclassified
Component: ANGLE (show other bugs)
Version: Other
Hardware: All All
: P2 Normal
Assignee: Dan Glastonbury
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2022-04-04 21:29 PDT by Dan Glastonbury
Modified: 2022-04-12 15:09 PDT (History)
11 users (show)

See Also:


Attachments
Patch (4.39 KB, patch)
2022-04-04 21:41 PDT, Dan Glastonbury
no flags Details | Formatted Diff | Diff
Patch (29.01 KB, patch)
2022-04-04 22:51 PDT, Dan Glastonbury
no flags Details | Formatted Diff | Diff
Patch (8.08 KB, patch)
2022-04-04 23:01 PDT, Dan Glastonbury
no flags Details | Formatted Diff | Diff
Patch (8.34 KB, patch)
2022-04-05 22:15 PDT, Dan Glastonbury
kkinnunen: review+
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Dan Glastonbury 2022-04-04 21:29:54 PDT
It appears that the code in `Framebuffer::partialClearNeedsInit` falsely returns that a partial clear is needed on 64-bit targets because the unused high-nibble of mMaxColorMask is being considered.
Comment 1 Dan Glastonbury 2022-04-04 21:41:58 PDT
Created attachment 456667 [details]
Patch
Comment 2 EWS Watchlist 2022-04-04 21:43:30 PDT
Note that there are important steps to take when updating ANGLE. See https://trac.webkit.org/wiki/UpdatingANGLE
Comment 3 Dan Glastonbury 2022-04-04 22:51:33 PDT
Created attachment 456671 [details]
Patch
Comment 4 Dan Glastonbury 2022-04-04 22:53:17 PDT
Comment on attachment 456671 [details]
Patch

webkit-patch uploaded the wrong patch.
Comment 5 Dan Glastonbury 2022-04-04 23:01:32 PDT
Created attachment 456672 [details]
Patch
Comment 6 Kimmo Kinnunen 2022-04-05 00:54:10 PDT
Comment on attachment 456672 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=456672&action=review

nice find! some things to consider, not an ANGLE reviewer

> Source/ThirdParty/ANGLE/ChangeLog:7
> +        8-bits, so mMaxColorMask can't use used with the bit difference

Nit:
"A is extended to 8-bits" initially throws the non-expert reader like me off, since in c++ nothing can be extended to 8-bits as everything is at least 8-bits. Also "extension" would refer to type promotion. Reading the ANGLE code I see you're talking about using BlendStateExt::StorageType::kBits value being 8 instead of 4 on 32-bit system.

Also the commit message is a bit begging the question for non-expert reader like me (solve A because we solve A).

> Source/ThirdParty/ANGLE/src/libANGLE/angletypes.cpp:444
> +    return compareColorMask(ColorMaskStorage::GetReplicatedValue(

I'm not a ANGLE reviewer, but the way I understand BlendStateExt is that it 
1) contains various mask values based on various lengths 
2) encapsulates the complexities of the various buffer mask calculations based on the mask values and lengths it stores. 

from this perspective it would seem that the bug is in BlendStateExt::compareColorMask() where the function sometimes returns values outside the domain it should.
This would mean that if you change the calculation strategy to fix this particular bug, the next user of compareColorMask() would potentially not know that and run into the same or similar issue elsewhere.

I see most of the BlendStateExt  setters and getters clamp the set and get value with t he mask limit values, in this case mMaxColorMask.

Would it make sense to clamp the values returned by compareColorMask()
E.g. if we consider
  ColorMaskStorage::GetDiffMask() as "calculate the bit difference"
  BendStateExt::compareColorMask() as "calculate the bit difference as it makes sense to this particular blend state"

In practice I believe the code would be above
  return ColorMaskStorage::GetDiffMask(mColorMask, other) & mMaxColorMask;

Alternatively we could maybe consider that the bug is that mColorMask stores a value outside its domain? So then tracking the setter that doesn't clamp would fix all bugs related to assuming that mColorMask is never outside the domain.
Comment 7 Dan Glastonbury 2022-04-05 19:06:11 PDT
Thanks for reviewing, :kimmo. I'm not an ANGLE coder, let alone ANGLE reviewer. :-)

My assessment of the issue is:

4 bits per draw buffer are used to represent if a channel is masked. (0 - not drawn to, 1 - drawn). So unmasked, draw to all channels is 0xF.

Considering 4 draw buffers, in the 32-bit case this is stored as 0x0000_FFFF and in the 64-bit case as 0x00000000_0F0F0F0F (because the elements are widened to 8-bits each). mMaxColorMask appears to be a mask uses to discard parts of the storage that aren't valid elements, ie. the high 16-bits and high 32-bits in the 32-bit and 64-bit cases, respectively.

I believe that the mMaxColorMask of 0xFFFF was being used in the 32-bit case because it also happened to match what the correct unmasked write bit pattern. In moving to 64-bit, the mask becomes 0xFFFFFFFF, which doesn't match 0x0F0F0F0F and leads to the error.

> In practice I believe the code would be above
>   return ColorMaskStorage::GetDiffMask(mColorMask, other) & mMaxColorMask;

This will still be wrong. `other` needs to be masked first by either 0xF or 0x0F depending on kBits. eg:

```
  return ColorMaskStorage::GetDiffMask(mColorMask, 0x0F0F0F0FULL);
```

I'm relying on `ColorMaskStorage::GetReplicatedValue(BlendStateExt::PackColorMask(true, true, true, true))` being constexpr to generate the correct value of 0xFFFF or 0x0F0F0F0F.
Comment 8 Dan Glastonbury 2022-04-05 19:09:51 PDT
(In reply to Dan Glastonbury from comment #7)
> ```
>   return ColorMaskStorage::GetDiffMask(mColorMask, 0x0F0F0F0FULL);
> ```

Sorry, I mean:

```
  return ColorMaskStorage::GetDiffMask(mColorMask, other & 0x0F0F0F0FULL);
 ```
Comment 9 Dan Glastonbury 2022-04-05 22:15:16 PDT
Created attachment 456784 [details]
Patch
Comment 10 Radar WebKit Bug Importer 2022-04-11 21:30:16 PDT
<rdar://problem/91606700>
Comment 11 Kenneth Russell 2022-04-12 11:39:26 PDT
CC'ing some other ANGLE/Chromium folks. It'd be ideal if this patch were uploaded to ANGLE's code review tool for more in-depth review by the ANGLE experts and then rolled into WebKit, rather than landing this here and requiring later upstreaming.
Comment 12 Jonah RD 2022-04-12 13:21:24 PDT
Thanks for the patch. I'm worried that the new test might not pass on all of the platforms we test. Can you upload this CL through Gerrit so we can land safely upstream and then cherry-pick it into WebKit? https://chromium-review.googlesource.com/q/project:angle
Comment 13 Kenneth Russell 2022-04-12 13:23:17 PDT
Also please talk with Dean or Kimmo about getting added to the Apple-side list for permission to upload CLs to ANGLE's code review tool. Thanks.
Comment 14 Alexey Knyazev 2022-04-12 14:23:10 PDT
Looks like I wrote that code some time ago. The color mask comparison does indeed produce incorrect results sometimes.

I think the simplest fix would be to adjust mMaxColorMask initialization to keep the unused bits unset on 64-bit systems. Also this would better align the value with the variable name.
Comment 15 Alexey Knyazev 2022-04-12 15:09:30 PDT
Specifically, could you please try updating the BlendStateExt ctor with these expressions?

```
mMaxColorMask(ColorMaskStorage::GetReplicatedValue(PackColorMask(true, true, true, true),
                                                   ColorMaskStorage::GetMask(drawBuffers))),
mColorMask(mMaxColorMask),
```