1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 
9 #include "src/gpu/GrStencilSettings.h"
10 
11 #include "src/gpu/GrProcessor.h"
12 
13 constexpr const GrUserStencilSettings gUnused(
14     GrUserStencilSettings::StaticInit<
15         0x0000,
16         GrUserStencilTest::kAlwaysIfInClip,
17         0xffff,
18         GrUserStencilOp::kKeep,
19         GrUserStencilOp::kKeep,
20         0x0000>()
21 );
22 
23 GR_STATIC_ASSERT(kAll_StencilFlags == (gUnused.fFrontFlags[0] & gUnused.fBackFlags[0]));
24 
25 const GrUserStencilSettings& GrUserStencilSettings::kUnused = gUnused;
26 
reset(const GrUserStencilSettings & user,bool hasStencilClip,int numStencilBits)27 void GrStencilSettings::reset(const GrUserStencilSettings& user, bool hasStencilClip,
28                               int numStencilBits) {
29     uint16_t frontFlags = user.fFrontFlags[hasStencilClip];
30     if (frontFlags & kSingleSided_StencilFlag) {
31         SkASSERT(frontFlags == user.fBackFlags[hasStencilClip]);
32         fFlags = frontFlags;
33         if (!this->isDisabled()) {
34             fFront.reset(user.fFront, hasStencilClip, numStencilBits);
35         }
36         return;
37     }
38 
39     uint16_t backFlags = user.fBackFlags[hasStencilClip];
40     fFlags = frontFlags & backFlags;
41     if (this->isDisabled()) {
42         return;
43     }
44     if (!(frontFlags & kDisabled_StencilFlag)) {
45         fFront.reset(user.fFront, hasStencilClip, numStencilBits);
46     } else {
47         fFront.setDisabled();
48     }
49     if (!(backFlags & kDisabled_StencilFlag)) {
50         fBack.reset(user.fBack, hasStencilClip, numStencilBits);
51     } else {
52         fBack.setDisabled();
53     }
54 }
55 
reset(const GrStencilSettings & that)56 void GrStencilSettings::reset(const GrStencilSettings& that) {
57     fFlags = that.fFlags;
58     if ((kInvalid_PrivateFlag | kDisabled_StencilFlag) & fFlags) {
59         return;
60     }
61     if (!this->isTwoSided()) {
62         memcpy(&fFront, &that.fFront, sizeof(Face));
63     } else {
64         memcpy(&fFront, &that.fFront, 2 * sizeof(Face));
65         GR_STATIC_ASSERT(sizeof(Face) ==
66                          offsetof(GrStencilSettings, fBack) - offsetof(GrStencilSettings, fFront));
67     }
68 }
69 
operator ==(const GrStencilSettings & that) const70 bool GrStencilSettings::operator==(const GrStencilSettings& that) const {
71     if ((kInvalid_PrivateFlag | kDisabled_StencilFlag) & (fFlags | that.fFlags)) {
72         // At least one is invalid and/or disabled.
73         if (kInvalid_PrivateFlag & (fFlags | that.fFlags)) {
74             return false; // We never allow invalid stencils to be equal.
75         }
76         // They're only equal if both are disabled.
77         return kDisabled_StencilFlag & (fFlags & that.fFlags);
78     }
79     if (kSingleSided_StencilFlag & (fFlags & that.fFlags)) {
80         return 0 == memcmp(&fFront, &that.fFront, sizeof(Face)); // Both are single sided.
81     } else if (kSingleSided_StencilFlag & (fFlags | that.fFlags)) {
82         return false;
83     } else {
84         return 0 == memcmp(&fFront, &that.fFront, 2 * sizeof(Face));
85         GR_STATIC_ASSERT(sizeof(Face) ==
86                          offsetof(GrStencilSettings, fBack) - offsetof(GrStencilSettings, fFront));
87     }
88     // memcmp relies on GrStencilSettings::Face being tightly packed.
89     GR_STATIC_ASSERT(0 == offsetof(Face, fRef));
90     GR_STATIC_ASSERT(2 == sizeof(Face::fRef));
91     GR_STATIC_ASSERT(2 == offsetof(Face, fTest));
92     GR_STATIC_ASSERT(2 == sizeof(Face::fTest));
93     GR_STATIC_ASSERT(4 == offsetof(Face, fTestMask));
94     GR_STATIC_ASSERT(2 == sizeof(Face::fTestMask));
95     GR_STATIC_ASSERT(6 == offsetof(Face, fPassOp));
96     GR_STATIC_ASSERT(1 == sizeof(Face::fPassOp));
97     GR_STATIC_ASSERT(7 == offsetof(Face, fFailOp));
98     GR_STATIC_ASSERT(1 == sizeof(Face::fFailOp));
99     GR_STATIC_ASSERT(8 == offsetof(Face, fWriteMask));
100     GR_STATIC_ASSERT(2 == sizeof(Face::fWriteMask));
101     GR_STATIC_ASSERT(10 == sizeof(Face));
102 }
103 
104 static constexpr GrStencilTest gUserStencilTestToRaw[kGrUserStencilTestCount] = {
105     // Tests that respect the clip.
106     GrStencilTest::kAlways,  // kAlwaysIfInClip (This is only for when there is not a stencil clip).
107     GrStencilTest::kEqual,   // kEqualIfInClip.
108     GrStencilTest::kLess,    // kLessIfInClip.
109     GrStencilTest::kLEqual,  // kLEqualIfInClip.
110 
111     // Tests that ignore the clip.
112     GrStencilTest::kAlways,
113     GrStencilTest::kNever,
114     GrStencilTest::kGreater,
115     GrStencilTest::kGEqual,
116     GrStencilTest::kLess,
117     GrStencilTest::kLEqual,
118     GrStencilTest::kEqual,
119     GrStencilTest::kNotEqual
120 };
121 
122 GR_STATIC_ASSERT(0 == (int)GrUserStencilTest::kAlwaysIfInClip);
123 GR_STATIC_ASSERT(1 == (int)GrUserStencilTest::kEqualIfInClip);
124 GR_STATIC_ASSERT(2 == (int)GrUserStencilTest::kLessIfInClip);
125 GR_STATIC_ASSERT(3 == (int)GrUserStencilTest::kLEqualIfInClip);
126 GR_STATIC_ASSERT(4 == (int)GrUserStencilTest::kAlways);
127 GR_STATIC_ASSERT(5 == (int)GrUserStencilTest::kNever);
128 GR_STATIC_ASSERT(6 == (int)GrUserStencilTest::kGreater);
129 GR_STATIC_ASSERT(7 == (int)GrUserStencilTest::kGEqual);
130 GR_STATIC_ASSERT(8 == (int)GrUserStencilTest::kLess);
131 GR_STATIC_ASSERT(9 == (int)GrUserStencilTest::kLEqual);
132 GR_STATIC_ASSERT(10 == (int)GrUserStencilTest::kEqual);
133 GR_STATIC_ASSERT(11 == (int)GrUserStencilTest::kNotEqual);
134 
135 static constexpr GrStencilOp gUserStencilOpToRaw[kGrUserStencilOpCount] = {
136     GrStencilOp::kKeep,
137 
138     // Ops that only modify user bits.
139     GrStencilOp::kZero,
140     GrStencilOp::kReplace,
141     GrStencilOp::kInvert,
142     GrStencilOp::kIncWrap,
143     GrStencilOp::kDecWrap,
144     GrStencilOp::kIncClamp,  // kIncMaybeClamp.
145     GrStencilOp::kDecClamp,  // kDecMaybeClamp.
146 
147     // Ops that only modify the clip bit.
148     GrStencilOp::kZero,      // kZeroClipBit.
149     GrStencilOp::kReplace,   // kSetClipBit.
150     GrStencilOp::kInvert,    // kInvertClipBit.
151 
152     // Ops that modify clip and user bits.
153     GrStencilOp::kReplace,   // kSetClipAndReplaceUserBits.
154     GrStencilOp::kZero       // kZeroClipAndUserBits.
155 };
156 
157 GR_STATIC_ASSERT(0 == (int)GrUserStencilOp::kKeep);
158 GR_STATIC_ASSERT(1 == (int)GrUserStencilOp::kZero);
159 GR_STATIC_ASSERT(2 == (int)GrUserStencilOp::kReplace);
160 GR_STATIC_ASSERT(3 == (int)GrUserStencilOp::kInvert);
161 GR_STATIC_ASSERT(4 == (int)GrUserStencilOp::kIncWrap);
162 GR_STATIC_ASSERT(5 == (int)GrUserStencilOp::kDecWrap);
163 GR_STATIC_ASSERT(6 == (int)GrUserStencilOp::kIncMaybeClamp);
164 GR_STATIC_ASSERT(7 == (int)GrUserStencilOp::kDecMaybeClamp);
165 GR_STATIC_ASSERT(8 == (int)GrUserStencilOp::kZeroClipBit);
166 GR_STATIC_ASSERT(9 == (int)GrUserStencilOp::kSetClipBit);
167 GR_STATIC_ASSERT(10 == (int)GrUserStencilOp::kInvertClipBit);
168 GR_STATIC_ASSERT(11 == (int)GrUserStencilOp::kSetClipAndReplaceUserBits);
169 GR_STATIC_ASSERT(12 == (int)GrUserStencilOp::kZeroClipAndUserBits);
170 
reset(const GrUserStencilSettings::Face & user,bool hasStencilClip,int numStencilBits)171 void GrStencilSettings::Face::reset(const GrUserStencilSettings::Face& user, bool hasStencilClip,
172                                     int numStencilBits) {
173     SkASSERT(user.fTest < (GrUserStencilTest)kGrUserStencilTestCount);
174     SkASSERT(user.fPassOp < (GrUserStencilOp)kGrUserStencilOpCount);
175     SkASSERT(user.fFailOp < (GrUserStencilOp)kGrUserStencilOpCount);
176     SkASSERT(numStencilBits > 0 && numStencilBits <= 16);
177     int clipBit = 1 << (numStencilBits - 1);
178     int userMask = clipBit - 1;
179 
180     GrUserStencilOp maxOp = SkTMax(user.fPassOp, user.fFailOp);
181     SkDEBUGCODE(GrUserStencilOp otherOp = SkTMin(user.fPassOp, user.fFailOp);)
182     if (maxOp <= kLastUserOnlyStencilOp) {
183         // Ops that only modify user bits.
184         fWriteMask = user.fWriteMask & userMask;
185         SkASSERT(otherOp <= kLastUserOnlyStencilOp);
186     } else if (maxOp <= kLastClipOnlyStencilOp) {
187         // Ops that only modify the clip bit.
188         fWriteMask = clipBit;
189         SkASSERT(GrUserStencilOp::kKeep == otherOp ||
190                  (otherOp > kLastUserOnlyStencilOp && otherOp <= kLastClipOnlyStencilOp));
191     } else {
192         // Ops that modify both clip and user bits.
193         fWriteMask = clipBit | (user.fWriteMask & userMask);
194         SkASSERT(GrUserStencilOp::kKeep == otherOp || otherOp > kLastClipOnlyStencilOp);
195     }
196 
197     fFailOp = gUserStencilOpToRaw[(int)user.fFailOp];
198     fPassOp = gUserStencilOpToRaw[(int)user.fPassOp];
199 
200     if (!hasStencilClip || user.fTest > kLastClippedStencilTest) {
201         // Ignore the clip.
202         fTestMask = user.fTestMask & userMask;
203         fTest = gUserStencilTestToRaw[(int)user.fTest];
204     } else if (GrUserStencilTest::kAlwaysIfInClip != user.fTest) {
205         // Respect the clip.
206         fTestMask = clipBit | (user.fTestMask & userMask);
207         fTest = gUserStencilTestToRaw[(int)user.fTest];
208     } else {
209         // Test only for clip.
210         fTestMask = clipBit;
211         fTest = GrStencilTest::kEqual;
212     }
213 
214     fRef = (clipBit | user.fRef) & (fTestMask | fWriteMask);
215 }
216 
setDisabled()217 void GrStencilSettings::Face::setDisabled() {
218     memset(this, 0, sizeof(*this));
219     GR_STATIC_ASSERT(0 == (int)GrStencilTest::kAlways);
220     GR_STATIC_ASSERT(0 == (int)GrStencilOp::kKeep);
221 }
222 
223 ////////////////////////////////////////////////////////////////////////////////
224 // Stencil Rules for Merging user stencil space into clip
225 //
226 
227 ///////
228 // Replace
229 static constexpr GrUserStencilSettings gUserToClipReplace(
230     GrUserStencilSettings::StaticInit<
231         0x0000,
232         GrUserStencilTest::kNotEqual,
233         0xffff,
234         GrUserStencilOp::kSetClipAndReplaceUserBits,
235         GrUserStencilOp::kZeroClipAndUserBits,
236         0xffff>()
237 );
238 
239 static constexpr GrUserStencilSettings gInvUserToClipReplace(
240     GrUserStencilSettings::StaticInit<
241         0x0000,
242         GrUserStencilTest::kEqual,
243         0xffff,
244         GrUserStencilOp::kSetClipAndReplaceUserBits,
245         GrUserStencilOp::kZeroClipAndUserBits,
246         0xffff>()
247 );
248 
249 ///////
250 // Intersect
251 static constexpr GrUserStencilSettings gUserToClipIsect(
252     GrUserStencilSettings::StaticInit<
253         0x0000,
254         GrUserStencilTest::kLessIfInClip, // "0 < userBits" is equivalent to "0 != userBits".
255         0xffff,
256         GrUserStencilOp::kSetClipAndReplaceUserBits,
257         GrUserStencilOp::kZeroClipAndUserBits,
258         0xffff>()
259 );
260 
261 ///////
262 // Difference
263 static constexpr GrUserStencilSettings gUserToClipDiff(
264     GrUserStencilSettings::StaticInit<
265         0x0000,
266         GrUserStencilTest::kEqualIfInClip,
267         0xffff,
268         GrUserStencilOp::kSetClipAndReplaceUserBits,
269         GrUserStencilOp::kZeroClipAndUserBits,
270         0xffff>()
271 );
272 
273 ///////
274 // Union
275 static constexpr GrUserStencilSettings gUserToClipUnion(
276     GrUserStencilSettings::StaticInit<
277         0x0000,
278         GrUserStencilTest::kNotEqual,
279         0xffff,
280         GrUserStencilOp::kSetClipAndReplaceUserBits,
281         GrUserStencilOp::kKeep,
282         0xffff>()
283 );
284 
285 static constexpr GrUserStencilSettings gInvUserToClipUnionPass0( // Does not zero user bits.
286     GrUserStencilSettings::StaticInit<
287         0x0000,
288         GrUserStencilTest::kEqual,
289         0xffff,
290         GrUserStencilOp::kSetClipBit,
291         GrUserStencilOp::kKeep,
292         0x0000>()
293 );
294 
295 ///////
296 // Xor
297 static constexpr GrUserStencilSettings gUserToClipXorPass0( // Does not zero user bits.
298     GrUserStencilSettings::StaticInit<
299         0x0000,
300         GrUserStencilTest::kNotEqual,
301         0xffff,
302         GrUserStencilOp::kInvertClipBit,
303         GrUserStencilOp::kKeep,
304         0x0000>()
305 );
306 
307 static constexpr GrUserStencilSettings gInvUserToClipXorPass0( // Does not zero user bits.
308     GrUserStencilSettings::StaticInit<
309         0x0000,
310         GrUserStencilTest::kEqual,
311         0xffff,
312         GrUserStencilOp::kInvertClipBit,
313         GrUserStencilOp::kKeep,
314         0x0000>()
315 );
316 
317 ///////
318 // Reverse Diff
319 static constexpr GrUserStencilSettings gUserToClipRDiffPass0( // Does not zero user bits.
320     GrUserStencilSettings::StaticInit<
321         0x0000,
322         GrUserStencilTest::kNotEqual,
323         0xffff,
324         GrUserStencilOp::kInvertClipBit,
325         GrUserStencilOp::kZeroClipBit,
326         0x0000>()
327 );
328 
329 static constexpr GrUserStencilSettings gInvUserToClipRDiffPass0( // Does not zero user bits.
330     GrUserStencilSettings::StaticInit<
331         0x0000,
332         GrUserStencilTest::kEqual,
333         0xffff,
334         GrUserStencilOp::kInvertClipBit,
335         GrUserStencilOp::kZeroClipBit,
336         0x0000>()
337 );
338 
339 ///////
340 // Second pass to clear user bits (only needed sometimes)
341 static constexpr GrUserStencilSettings gZeroUserBits(
342     GrUserStencilSettings::StaticInit<
343         0x0000,
344         GrUserStencilTest::kNotEqual,
345         0xffff,
346         GrUserStencilOp::kZero,
347         GrUserStencilOp::kKeep,
348         0xffff>()
349 );
350 
351 static constexpr const GrUserStencilSettings* gUserToClipTable[2][1 + SkRegion::kLastOp][3] = {
352     {  /* Normal fill. */
353         {&gUserToClipDiff,           nullptr,         nullptr},  // kDifference_Op.
354         {&gUserToClipIsect,          nullptr,         nullptr},  // kIntersect_Op.
355         {&gUserToClipUnion,          nullptr,         nullptr},  // kUnion_Op.
356         {&gUserToClipXorPass0,       &gZeroUserBits,  nullptr},  // kXOR_Op.
357         {&gUserToClipRDiffPass0,     &gZeroUserBits,  nullptr},  // kReverseDifference_Op.
358         {&gUserToClipReplace,        nullptr,         nullptr}   // kReplace_Op.
359 
360     }, /* Inverse fill. */ {
361         {&gUserToClipIsect,          nullptr,         nullptr},  // ~diff (aka isect).
362         {&gUserToClipDiff,           nullptr,         nullptr},  // ~isect (aka diff).
363         {&gInvUserToClipUnionPass0,  &gZeroUserBits,  nullptr},  // ~union.
364         {&gInvUserToClipXorPass0,    &gZeroUserBits,  nullptr},  // ~xor.
365         {&gInvUserToClipRDiffPass0,  &gZeroUserBits,  nullptr},  // ~reverse diff.
366         {&gInvUserToClipReplace,     nullptr,         nullptr}   // ~replace.
367     }
368 };
369 
370 GR_STATIC_ASSERT(0 == SkRegion::kDifference_Op);
371 GR_STATIC_ASSERT(1 == SkRegion::kIntersect_Op);
372 GR_STATIC_ASSERT(2 == SkRegion::kUnion_Op);
373 GR_STATIC_ASSERT(3 == SkRegion::kXOR_Op);
374 GR_STATIC_ASSERT(4 == SkRegion::kReverseDifference_Op);
375 GR_STATIC_ASSERT(5 == SkRegion::kReplace_Op);
376 
377 ///////
378 // Direct to Stencil
379 
380 // We can render a clip element directly without first writing to the client
381 // portion of the clip when the fill is not inverse and the set operation will
382 // only modify the in/out status of samples covered by the clip element.
383 
384 // this one only works if used right after stencil clip was cleared.
385 // Our clip mask creation code doesn't allow midstream replace ops.
386 static constexpr GrUserStencilSettings gReplaceClip(
387     GrUserStencilSettings::StaticInit<
388         0x0000,
389         GrUserStencilTest::kAlways,
390         0xffff,
391         GrUserStencilOp::kSetClipBit,
392         GrUserStencilOp::kSetClipBit,
393         0x0000>()
394 );
395 
396 static constexpr GrUserStencilSettings gUnionClip(
397     GrUserStencilSettings::StaticInit<
398         0x0000,
399         GrUserStencilTest::kAlwaysIfInClip,
400         0xffff,
401         GrUserStencilOp::kKeep,
402         GrUserStencilOp::kSetClipBit,
403         0x0000>()
404 );
405 
406 static constexpr GrUserStencilSettings gXorClip(
407     GrUserStencilSettings::StaticInit<
408         0x0000,
409         GrUserStencilTest::kAlways,
410         0xffff,
411         GrUserStencilOp::kInvertClipBit,
412         GrUserStencilOp::kInvertClipBit,
413         0x0000>()
414 );
415 
416 static constexpr GrUserStencilSettings gDiffClip(
417     GrUserStencilSettings::StaticInit<
418         0x0000,
419         GrUserStencilTest::kAlwaysIfInClip,
420         0xffff,
421         GrUserStencilOp::kZeroClipBit,
422         GrUserStencilOp::kKeep,
423         0x0000>()
424 );
425 
426 static constexpr const GrUserStencilSettings* gDirectDrawTable[1 + SkRegion::kLastOp][2] = {
427     {&gDiffClip,     nullptr},  // kDifference_Op.
428     {nullptr,        nullptr},  // kIntersect_Op.
429     {&gUnionClip,    nullptr},  // kUnion_Op.
430     {&gXorClip,      nullptr},  // kXOR_Op.
431     {nullptr,        nullptr},  // kReverseDifference_Op.
432     {&gReplaceClip,  nullptr}   // kReplace_Op.
433 };
434 
435 GR_STATIC_ASSERT(0 == SkRegion::kDifference_Op);
436 GR_STATIC_ASSERT(1 == SkRegion::kIntersect_Op);
437 GR_STATIC_ASSERT(2 == SkRegion::kUnion_Op);
438 GR_STATIC_ASSERT(3 == SkRegion::kXOR_Op);
439 GR_STATIC_ASSERT(4 == SkRegion::kReverseDifference_Op);
440 GR_STATIC_ASSERT(5 == SkRegion::kReplace_Op);
441 
GetClipPasses(SkRegion::Op op,bool canBeDirect,bool invertedFill,bool * drawDirectToClip)442 GrUserStencilSettings const* const* GrStencilSettings::GetClipPasses(SkRegion::Op op,
443                                                                      bool canBeDirect,
444                                                                      bool invertedFill,
445                                                                      bool* drawDirectToClip) {
446     SkASSERT((unsigned)op <= SkRegion::kLastOp);
447     if (canBeDirect && !invertedFill) { // TODO: inverse fill + intersect op can be direct.
448         GrUserStencilSettings const* const* directPass = gDirectDrawTable[op];
449         if (directPass[0]) {
450             *drawDirectToClip = true;
451             return directPass;
452         }
453     }
454     *drawDirectToClip = false;
455     return gUserToClipTable[invertedFill][op];
456 }
457 
458 static constexpr GrUserStencilSettings gZeroStencilClipBit(
459     GrUserStencilSettings::StaticInit<
460         0x0000,
461         GrUserStencilTest::kAlways,
462         0xffff,
463         GrUserStencilOp::kZeroClipBit,
464         GrUserStencilOp::kZeroClipBit,
465         0x0000>()
466 );
467 
SetClipBitSettings(bool setToInside)468 const GrUserStencilSettings* GrStencilSettings::SetClipBitSettings(bool setToInside) {
469     return setToInside ? &gReplaceClip : &gZeroStencilClipBit;
470 }
471 
genKey(GrProcessorKeyBuilder * b) const472 void GrStencilSettings::genKey(GrProcessorKeyBuilder* b) const {
473     b->add32(fFlags);
474     if (this->isDisabled()) {
475         return;
476     }
477     if (!this->isTwoSided()) {
478         constexpr int kCount16 = sizeof(Face) / sizeof(uint16_t);
479         GR_STATIC_ASSERT(0 == sizeof(Face) % sizeof(uint16_t));
480         uint16_t* key = reinterpret_cast<uint16_t*>(b->add32n((kCount16 + 1) / 2));
481         memcpy(key, &fFront, sizeof(Face));
482         key[kCount16] = 0;
483         GR_STATIC_ASSERT(1 == kCount16 % 2);
484     } else {
485         constexpr int kCount32 = (2 * sizeof(Face)) / sizeof(uint32_t);
486         GR_STATIC_ASSERT(0 == (2 * sizeof(Face)) % sizeof(uint32_t));
487         uint32_t* key = b->add32n(kCount32);
488         memcpy(key, &fFront, 2 * sizeof(Face));
489         GR_STATIC_ASSERT(sizeof(Face) ==
490                          offsetof(GrStencilSettings, fBack) - offsetof(GrStencilSettings, fFront));
491     }
492     // We rely on GrStencilSettings::Face being tightly packed for the key to be reliable.
493     GR_STATIC_ASSERT(0 == offsetof(Face, fRef));
494     GR_STATIC_ASSERT(2 == sizeof(Face::fRef));
495     GR_STATIC_ASSERT(2 == offsetof(Face, fTest));
496     GR_STATIC_ASSERT(2 == sizeof(Face::fTest));
497     GR_STATIC_ASSERT(4 == offsetof(Face, fTestMask));
498     GR_STATIC_ASSERT(2 == sizeof(Face::fTestMask));
499     GR_STATIC_ASSERT(6 == offsetof(Face, fPassOp));
500     GR_STATIC_ASSERT(1 == sizeof(Face::fPassOp));
501     GR_STATIC_ASSERT(7 == offsetof(Face, fFailOp));
502     GR_STATIC_ASSERT(1 == sizeof(Face::fFailOp));
503     GR_STATIC_ASSERT(8 == offsetof(Face, fWriteMask));
504     GR_STATIC_ASSERT(2 == sizeof(Face::fWriteMask));
505     GR_STATIC_ASSERT(10 == sizeof(Face));
506 }
507