• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2 * Copyright (C) 2014-2015 Intel Corporation.   All Rights Reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 * @file clip.h
24 *
25 * @brief Definitions for clipping
26 *
27 ******************************************************************************/
28 #pragma once
29 
30 #include "common/simdintrin.h"
31 #include "core/context.h"
32 #include "core/pa.h"
33 #include "rdtsc_core.h"
34 
35 // Temp storage used by the clipper
36 extern THREAD SIMDVERTEX_T<SIMD256> tlsTempVertices[7];
37 #if USE_SIMD16_FRONTEND
38 extern THREAD SIMDVERTEX_T<SIMD512> tlsTempVertices_simd16[7];
39 #endif
40 
41 enum SWR_CLIPCODES
42 {
43     // Shift clip codes out of the mantissa to prevent denormalized values when used in float compare.
44     // Guardband is able to use a single high-bit with 4 separate LSBs, because it computes a union, rather than intersection, of clipcodes.
45 #define CLIPCODE_SHIFT 23
46     FRUSTUM_LEFT    = (0x01 << CLIPCODE_SHIFT),
47     FRUSTUM_TOP     = (0x02 << CLIPCODE_SHIFT),
48     FRUSTUM_RIGHT   = (0x04 << CLIPCODE_SHIFT),
49     FRUSTUM_BOTTOM  = (0x08 << CLIPCODE_SHIFT),
50 
51     FRUSTUM_NEAR    = (0x10 << CLIPCODE_SHIFT),
52     FRUSTUM_FAR     = (0x20 << CLIPCODE_SHIFT),
53 
54     NEGW            = (0x40 << CLIPCODE_SHIFT),
55 
56     GUARDBAND_LEFT   = (0x80 << CLIPCODE_SHIFT | 0x1),
57     GUARDBAND_TOP    = (0x80 << CLIPCODE_SHIFT | 0x2),
58     GUARDBAND_RIGHT  = (0x80 << CLIPCODE_SHIFT | 0x4),
59     GUARDBAND_BOTTOM = (0x80 << CLIPCODE_SHIFT | 0x8)
60 };
61 
62 #define GUARDBAND_CLIP_MASK (FRUSTUM_NEAR|FRUSTUM_FAR|GUARDBAND_LEFT|GUARDBAND_TOP|GUARDBAND_RIGHT|GUARDBAND_BOTTOM|NEGW)
63 
64 template<typename SIMD_T>
ComputeClipCodes(const API_STATE & state,const typename SIMD_T::Vec4 & vertex,typename SIMD_T::Float & clipCodes,typename SIMD_T::Integer const & viewportIndexes)65 void ComputeClipCodes(const API_STATE &state, const typename SIMD_T::Vec4 &vertex, typename SIMD_T::Float &clipCodes, typename SIMD_T::Integer const &viewportIndexes)
66 {
67     clipCodes = SIMD_T::setzero_ps();
68 
69     // -w
70     typename SIMD_T::Float vNegW = SIMD_T::mul_ps(vertex.w,SIMD_T::set1_ps(-1.0f));
71 
72     // FRUSTUM_LEFT
73     typename SIMD_T::Float vRes = SIMD_T::cmplt_ps(vertex.x, vNegW);
74     clipCodes = SIMD_T::and_ps(vRes, SIMD_T::castsi_ps(SIMD_T::set1_epi32(FRUSTUM_LEFT)));
75 
76     // FRUSTUM_TOP
77     vRes = SIMD_T::cmplt_ps(vertex.y, vNegW);
78     clipCodes = SIMD_T::or_ps(clipCodes, SIMD_T::and_ps(vRes, SIMD_T::castsi_ps(SIMD_T::set1_epi32(FRUSTUM_TOP))));
79 
80     // FRUSTUM_RIGHT
81     vRes = SIMD_T::cmpgt_ps(vertex.x, vertex.w);
82     clipCodes = SIMD_T::or_ps(clipCodes, SIMD_T::and_ps(vRes, SIMD_T::castsi_ps(SIMD_T::set1_epi32(FRUSTUM_RIGHT))));
83 
84     // FRUSTUM_BOTTOM
85     vRes = SIMD_T::cmpgt_ps(vertex.y, vertex.w);
86     clipCodes = SIMD_T::or_ps(clipCodes, SIMD_T::and_ps(vRes, SIMD_T::castsi_ps(SIMD_T::set1_epi32(FRUSTUM_BOTTOM))));
87 
88     if (state.rastState.depthClipEnable)
89     {
90         // FRUSTUM_NEAR
91         // DX clips depth [0..w], GL clips [-w..w]
92         if (state.rastState.clipHalfZ)
93         {
94             vRes = SIMD_T::cmplt_ps(vertex.z, SIMD_T::setzero_ps());
95         }
96         else
97         {
98             vRes = SIMD_T::cmplt_ps(vertex.z, vNegW);
99         }
100         clipCodes = SIMD_T::or_ps(clipCodes, SIMD_T::and_ps(vRes, SIMD_T::castsi_ps(SIMD_T::set1_epi32(FRUSTUM_NEAR))));
101 
102         // FRUSTUM_FAR
103         vRes = SIMD_T::cmpgt_ps(vertex.z, vertex.w);
104         clipCodes = SIMD_T::or_ps(clipCodes, SIMD_T::and_ps(vRes, SIMD_T::castsi_ps(SIMD_T::set1_epi32(FRUSTUM_FAR))));
105     }
106 
107     // NEGW
108     vRes = SIMD_T::cmple_ps(vertex.w, SIMD_T::setzero_ps());
109     clipCodes = SIMD_T::or_ps(clipCodes, SIMD_T::and_ps(vRes, SIMD_T::castsi_ps(SIMD_T::set1_epi32(NEGW))));
110 
111     // GUARDBAND_LEFT
112     typename SIMD_T::Float gbMult = SIMD_T::mul_ps(vNegW, SIMD_T::template i32gather_ps<typename SIMD_T::ScaleFactor(4)>(&state.gbState.left[0], viewportIndexes));
113     vRes = SIMD_T::cmplt_ps(vertex.x, gbMult);
114     clipCodes = SIMD_T::or_ps(clipCodes, SIMD_T::and_ps(vRes, SIMD_T::castsi_ps(SIMD_T::set1_epi32(GUARDBAND_LEFT))));
115 
116     // GUARDBAND_TOP
117     gbMult = SIMD_T::mul_ps(vNegW, SIMD_T::template i32gather_ps<typename SIMD_T::ScaleFactor(4)>(&state.gbState.top[0], viewportIndexes));
118     vRes = SIMD_T::cmplt_ps(vertex.y, gbMult);
119     clipCodes = SIMD_T::or_ps(clipCodes, SIMD_T::and_ps(vRes, SIMD_T::castsi_ps(SIMD_T::set1_epi32(GUARDBAND_TOP))));
120 
121     // GUARDBAND_RIGHT
122     gbMult = SIMD_T::mul_ps(vertex.w, SIMD_T::template i32gather_ps<typename SIMD_T::ScaleFactor(4)>(&state.gbState.right[0], viewportIndexes));
123     vRes = SIMD_T::cmpgt_ps(vertex.x, gbMult);
124     clipCodes = SIMD_T::or_ps(clipCodes, SIMD_T::and_ps(vRes, SIMD_T::castsi_ps(SIMD_T::set1_epi32(GUARDBAND_RIGHT))));
125 
126     // GUARDBAND_BOTTOM
127     gbMult = SIMD_T::mul_ps(vertex.w, SIMD_T::template i32gather_ps<typename SIMD_T::ScaleFactor(4)>(&state.gbState.bottom[0], viewportIndexes));
128     vRes = SIMD_T::cmpgt_ps(vertex.y, gbMult);
129     clipCodes = SIMD_T::or_ps(clipCodes, SIMD_T::and_ps(vRes, SIMD_T::castsi_ps(SIMD_T::set1_epi32(GUARDBAND_BOTTOM))));
130 }
131 
132 template<typename SIMD_T>
133 struct BinnerChooser
134 {
135 };
136 
137 template<>
138 struct BinnerChooser<SIMD256>
139 {
140     PFN_PROCESS_PRIMS pfnBinFunc;
141 
142     BinnerChooser(uint32_t numVertsPerPrim, uint32_t conservativeRast)
143         :pfnBinFunc(nullptr)
144     {
145         if (numVertsPerPrim == 3)
146         {
147             pfnBinFunc = GetBinTrianglesFunc(conservativeRast > 0);
148 
149         }
150         else if (numVertsPerPrim == 2)
151         {
152             pfnBinFunc = BinLines;
153         }
154         else
155         {
156             SWR_ASSERT(0 && "Unexpected points in clipper.");
157         }
158     }
159 
160     BinnerChooser(PRIMITIVE_TOPOLOGY topology, uint32_t conservativeRast)
161         :pfnBinFunc(nullptr)
162     {
163         switch (topology)
164         {
165         case TOP_POINT_LIST:
166             pfnBinFunc = BinPoints;
167             break;
168         case TOP_LINE_LIST:
169         case TOP_LINE_STRIP:
170         case TOP_LINE_LOOP:
171         case TOP_LINE_LIST_ADJ:
172         case TOP_LISTSTRIP_ADJ:
173             pfnBinFunc = BinLines;
174             break;
175         default:
176             pfnBinFunc = GetBinTrianglesFunc(conservativeRast > 0);
177             break;
178         };
179     }
180 
181     void BinFunc(DRAW_CONTEXT *pDC, PA_STATE &pa, uint32_t workerId, SIMD256::Vec4 prims[], uint32_t primMask, SIMD256::Integer const &primID, SIMD256::Integer &viewportIdx, SIMD256::Integer &rtIdx)
182     {
183         SWR_ASSERT(pfnBinFunc != nullptr);
184 
185         pfnBinFunc(pDC, pa, workerId, prims, primMask, primID, viewportIdx, rtIdx);
186     }
187 };
188 
189 #if USE_SIMD16_FRONTEND
190 template<>
191 struct BinnerChooser<SIMD512>
192 {
193     PFN_PROCESS_PRIMS_SIMD16 pfnBinFunc;
194 
195     BinnerChooser(uint32_t numVertsPerPrim, uint32_t conservativeRast)
196         :pfnBinFunc(nullptr)
197     {
198         if (numVertsPerPrim == 3)
199         {
200             pfnBinFunc = GetBinTrianglesFunc_simd16(conservativeRast > 0);
201 
202         }
203         else if (numVertsPerPrim == 2)
204         {
205             pfnBinFunc = BinLines_simd16;
206         }
207         else
208         {
209             SWR_ASSERT(0 && "Unexpected points in clipper.");
210         }
211     }
212 
213     BinnerChooser(PRIMITIVE_TOPOLOGY topology, uint32_t conservativeRast)
214         :pfnBinFunc(nullptr)
215     {
216         switch (topology)
217         {
218         case TOP_POINT_LIST:
219             pfnBinFunc = BinPoints_simd16;
220             break;
221         case TOP_LINE_LIST:
222         case TOP_LINE_STRIP:
223         case TOP_LINE_LOOP:
224         case TOP_LINE_LIST_ADJ:
225         case TOP_LISTSTRIP_ADJ:
226             pfnBinFunc = BinLines_simd16;
227             break;
228         default:
229             pfnBinFunc = GetBinTrianglesFunc_simd16(conservativeRast > 0);
230             break;
231         };
232     }
233 
234     void BinFunc(DRAW_CONTEXT *pDC, PA_STATE &pa, uint32_t workerId, SIMD512::Vec4 prims[], uint32_t primMask, SIMD512::Integer const &primID, SIMD512::Integer &viewportIdx, SIMD512::Integer &rtIdx)
235     {
236         SWR_ASSERT(pfnBinFunc != nullptr);
237 
238         pfnBinFunc(pDC, pa, workerId, prims, primMask, primID, viewportIdx, rtIdx);
239     }
240 };
241 
242 #endif
243 template<typename SIMD_T>
244 struct SimdHelper
245 {
246 };
247 
248 template<>
249 struct SimdHelper<SIMD256>
250 {
251     static SIMD256::Float insert_lo_ps(SIMD256::Float a)
252     {
253         return a;
254     }
255 
256     static SIMD256::Mask cmpeq_ps_mask(SIMD256::Float a, SIMD256::Float b)
257     {
258         return SIMD256::movemask_ps(SIMD256::cmpeq_ps(a, b));
259     }
260 };
261 
262 #if USE_SIMD16_FRONTEND
263 template<>
264 struct SimdHelper<SIMD512>
265 {
266     static SIMD512::Float insert_lo_ps(SIMD256::Float a)
267     {
268         return SIMD512::insert_ps<0>(SIMD512::setzero_ps(), a);
269     }
270 
271     static SIMD512::Mask cmpeq_ps_mask(SIMD512::Float a, SIMD512::Float b)
272     {
273         return SIMD512::cmp_ps_mask<SIMD16::CompareType::EQ_OQ>(a, b);
274     }
275 };
276 
277 #endif
278 // Temp storage used by the clipper
279 template<typename SIMD_T>
280 struct ClipHelper
281 {
282 };
283 
284 template<>
285 struct ClipHelper<SIMD256>
286 {
287     static SIMDVERTEX_T<SIMD256> *GetTempVertices()
288     {
289         return tlsTempVertices;
290     }
291 };
292 
293 #if USE_SIMD16_FRONTEND
294 template<>
295 struct ClipHelper<SIMD512>
296 {
297     static SIMDVERTEX_T<SIMD512> *GetTempVertices()
298     {
299         return tlsTempVertices_simd16;
300     }
301 };
302 
303 #endif
304 template<typename SIMD_T, uint32_t NumVertsPerPrim>
305 class Clipper
306 {
307 public:
308     INLINE Clipper(uint32_t in_workerId, DRAW_CONTEXT* in_pDC) :
309         workerId(in_workerId), pDC(in_pDC), state(GetApiState(in_pDC))
310     {
311         static_assert(NumVertsPerPrim >= 1 && NumVertsPerPrim <= 3, "Invalid NumVertsPerPrim");
312     }
313 
314     void ComputeClipCodes(typename SIMD_T::Vec4 vertex[], const typename SIMD_T::Integer &viewportIndexes)
315     {
316         for (uint32_t i = 0; i < NumVertsPerPrim; ++i)
317         {
318             ::ComputeClipCodes<SIMD_T>(state, vertex[i], clipCodes[i], viewportIndexes);
319         }
320     }
321 
322     typename SIMD_T::Float ComputeClipCodeIntersection()
323     {
324         typename SIMD_T::Float result = clipCodes[0];
325 
326         for (uint32_t i = 1; i < NumVertsPerPrim; ++i)
327         {
328             result = SIMD_T::and_ps(result, clipCodes[i]);
329         }
330 
331         return result;
332     }
333 
334     typename SIMD_T::Float ComputeClipCodeUnion()
335     {
336         typename SIMD_T::Float result = clipCodes[0];
337 
338         for (uint32_t i = 1; i < NumVertsPerPrim; ++i)
339         {
340             result = SIMD_T::or_ps(result, clipCodes[i]);
341         }
342 
343         return result;
344     }
345 
346     int ComputeClipMask()
347     {
348         typename SIMD_T::Float clipUnion = ComputeClipCodeUnion();
349 
350         clipUnion = SIMD_T::and_ps(clipUnion, SIMD_T::castsi_ps(SIMD_T::set1_epi32(GUARDBAND_CLIP_MASK)));
351 
352         return SIMD_T::movemask_ps(SIMD_T::cmpneq_ps(clipUnion, SIMD_T::setzero_ps()));
353     }
354 
355     // clipper is responsible for culling any prims with NAN coordinates
356     int ComputeNaNMask(typename SIMD_T::Vec4 prim[])
357     {
358         typename SIMD_T::Float vNanMask = SIMD_T::setzero_ps();
359 
360         for (uint32_t e = 0; e < NumVertsPerPrim; ++e)
361         {
362             typename SIMD_T::Float vNan01 = SIMD_T::template cmp_ps<SIMD_T::CompareType::UNORD_Q>(prim[e].v[0], prim[e].v[1]);
363             vNanMask = SIMD_T::or_ps(vNanMask, vNan01);
364 
365             typename SIMD_T::Float vNan23 = SIMD_T::template cmp_ps<SIMD_T::CompareType::UNORD_Q>(prim[e].v[2], prim[e].v[3]);
366             vNanMask = SIMD_T::or_ps(vNanMask, vNan23);
367         }
368 
369         return SIMD_T::movemask_ps(vNanMask);
370     }
371 
372     int ComputeUserClipCullMask(PA_STATE &pa, typename SIMD_T::Vec4 prim[])
373     {
374         uint8_t cullMask = state.backendState.cullDistanceMask;
375         uint32_t vertexClipCullOffset = state.backendState.vertexClipCullOffset;
376 
377         typename SIMD_T::Float vClipCullMask = SIMD_T::setzero_ps();
378 
379         typename SIMD_T::Vec4 vClipCullDistLo[3];
380         typename SIMD_T::Vec4 vClipCullDistHi[3];
381 
382         pa.Assemble(vertexClipCullOffset, vClipCullDistLo);
383         pa.Assemble(vertexClipCullOffset + 1, vClipCullDistHi);
384 
385         DWORD index;
386         while (_BitScanForward(&index, cullMask))
387         {
388             cullMask &= ~(1 << index);
389             uint32_t slot = index >> 2;
390             uint32_t component = index & 0x3;
391 
392             typename SIMD_T::Float vCullMaskElem = SIMD_T::set1_ps(-1.0f);
393             for (uint32_t e = 0; e < NumVertsPerPrim; ++e)
394             {
395                 typename SIMD_T::Float vCullComp;
396                 if (slot == 0)
397                 {
398                     vCullComp = vClipCullDistLo[e][component];
399                 }
400                 else
401                 {
402                     vCullComp = vClipCullDistHi[e][component];
403                 }
404 
405                 // cull if cull distance < 0 || NAN
406                 typename SIMD_T::Float vCull = SIMD_T::template cmp_ps<SIMD_T::CompareType::NLE_UQ>(SIMD_T::setzero_ps(), vCullComp);
407                 vCullMaskElem = SIMD_T::and_ps(vCullMaskElem, vCull);
408             }
409             vClipCullMask = SIMD_T::or_ps(vClipCullMask, vCullMaskElem);
410         }
411 
412         // clipper should also discard any primitive with NAN clip distance
413         uint8_t clipMask = state.backendState.clipDistanceMask;
414         while (_BitScanForward(&index, clipMask))
415         {
416             clipMask &= ~(1 << index);
417             uint32_t slot = index >> 2;
418             uint32_t component = index & 0x3;
419 
420             for (uint32_t e = 0; e < NumVertsPerPrim; ++e)
421             {
422                 typename SIMD_T::Float vClipComp;
423                 if (slot == 0)
424                 {
425                     vClipComp = vClipCullDistLo[e][component];
426                 }
427                 else
428                 {
429                     vClipComp = vClipCullDistHi[e][component];
430                 }
431 
432                 typename SIMD_T::Float vClip = SIMD_T::template cmp_ps<SIMD_T::CompareType::UNORD_Q>(vClipComp, vClipComp);
433                 vClipCullMask = SIMD_T::or_ps(vClipCullMask, vClip);
434             }
435         }
436 
437         return SIMD_T::movemask_ps(vClipCullMask);
438     }
439 
440     void ClipSimd(const typename SIMD_T::Vec4 prim[], const typename SIMD_T::Float &vPrimMask, const typename SIMD_T::Float &vClipMask, PA_STATE &pa,
441                   const typename SIMD_T::Integer &vPrimId, const typename SIMD_T::Integer &vViewportIdx, const typename SIMD_T::Integer &vRtIdx)
442     {
443         // input/output vertex store for clipper
444         SIMDVERTEX_T<SIMD_T> vertices[7]; // maximum 7 verts generated per triangle
445 
446         uint32_t constantInterpMask = state.backendState.constantInterpolationMask;
447         uint32_t provokingVertex = 0;
448         if (pa.binTopology == TOP_TRIANGLE_FAN)
449         {
450             provokingVertex = state.frontendState.provokingVertex.triFan;
451         }
452         ///@todo: line topology for wireframe?
453 
454         // assemble pos
455         typename SIMD_T::Vec4 tmpVector[NumVertsPerPrim];
456         for (uint32_t i = 0; i < NumVertsPerPrim; ++i)
457         {
458             vertices[i].attrib[VERTEX_POSITION_SLOT] = prim[i];
459         }
460 
461         // assemble attribs
462         const SWR_BACKEND_STATE& backendState = state.backendState;
463 
464         int32_t maxSlot = -1;
465         for (uint32_t slot = 0; slot < backendState.numAttributes; ++slot)
466         {
467             // Compute absolute attrib slot in vertex array
468             uint32_t mapSlot = backendState.swizzleEnable ? backendState.swizzleMap[slot].sourceAttrib : slot;
469             maxSlot = std::max<int32_t>(maxSlot, mapSlot);
470             uint32_t inputSlot = backendState.vertexAttribOffset + mapSlot;
471 
472             pa.Assemble(inputSlot, tmpVector);
473 
474             // if constant interpolation enabled for this attribute, assign the provoking
475             // vertex values to all edges
476             if (CheckBit(constantInterpMask, slot))
477             {
478                 for (uint32_t i = 0; i < NumVertsPerPrim; ++i)
479                 {
480                     vertices[i].attrib[inputSlot] = tmpVector[provokingVertex];
481                 }
482             }
483             else
484             {
485                 for (uint32_t i = 0; i < NumVertsPerPrim; ++i)
486                 {
487                     vertices[i].attrib[inputSlot] = tmpVector[i];
488                 }
489             }
490         }
491 
492         // assemble user clip distances if enabled
493         uint32_t vertexClipCullSlot = state.backendState.vertexClipCullOffset;
494         if (state.backendState.clipDistanceMask & 0xf)
495         {
496             pa.Assemble(vertexClipCullSlot, tmpVector);
497             for (uint32_t i = 0; i < NumVertsPerPrim; ++i)
498             {
499                 vertices[i].attrib[vertexClipCullSlot] = tmpVector[i];
500             }
501         }
502 
503         if (state.backendState.clipDistanceMask & 0xf0)
504         {
505             pa.Assemble(vertexClipCullSlot + 1, tmpVector);
506             for (uint32_t i = 0; i < NumVertsPerPrim; ++i)
507             {
508                 vertices[i].attrib[vertexClipCullSlot + 1] = tmpVector[i];
509             }
510         }
511 
512         uint32_t numAttribs = maxSlot + 1;
513 
514         typename SIMD_T::Integer vNumClippedVerts = ClipPrims((float*)&vertices[0], vPrimMask, vClipMask, numAttribs);
515 
516         BinnerChooser<SIMD_T> binner(NumVertsPerPrim, pa.pDC->pState->state.rastState.conservativeRast);
517 
518         // set up new PA for binning clipped primitives
519         PRIMITIVE_TOPOLOGY clipTopology = TOP_UNKNOWN;
520         if (NumVertsPerPrim == 3)
521         {
522             clipTopology = TOP_TRIANGLE_FAN;
523 
524             // so that the binner knows to bloat wide points later
525             if (pa.binTopology == TOP_POINT_LIST)
526             {
527                 clipTopology = TOP_POINT_LIST;
528             }
529         }
530         else if (NumVertsPerPrim == 2)
531         {
532             clipTopology = TOP_LINE_LIST;
533         }
534         else
535         {
536             SWR_ASSERT(0 && "Unexpected points in clipper.");
537         }
538 
539         const uint32_t *pVertexCount = reinterpret_cast<const uint32_t *>(&vNumClippedVerts);
540         const uint32_t *pPrimitiveId = reinterpret_cast<const uint32_t *>(&vPrimId);
541         const uint32_t *pViewportIdx = reinterpret_cast<const uint32_t *>(&vViewportIdx);
542         const uint32_t *pRtIdx = reinterpret_cast<const uint32_t *>(&vRtIdx);
543 
544         const SIMD256::Integer vOffsets = SIMD256::set_epi32(
545             0 * sizeof(SIMDVERTEX_T<SIMD_T>), // unused lane
546             6 * sizeof(SIMDVERTEX_T<SIMD_T>),
547             5 * sizeof(SIMDVERTEX_T<SIMD_T>),
548             4 * sizeof(SIMDVERTEX_T<SIMD_T>),
549             3 * sizeof(SIMDVERTEX_T<SIMD_T>),
550             2 * sizeof(SIMDVERTEX_T<SIMD_T>),
551             1 * sizeof(SIMDVERTEX_T<SIMD_T>),
552             0 * sizeof(SIMDVERTEX_T<SIMD_T>));
553 
554         // only need to gather 7 verts
555         // @todo dynamic mask based on actual # of verts generated per lane
556         const SIMD256::Float vMask = SIMD256::set_ps(0, -1, -1, -1, -1, -1, -1, -1);
557 
558         uint32_t numClippedPrims = 0;
559 
560         // tranpose clipper output so that each lane's vertices are in SIMD order
561         // set aside space for 2 vertices, as the PA will try to read up to 16 verts
562         // for triangle fan
563 
564 #if defined(_DEBUG)
565         // TODO: need to increase stack size, allocating SIMD16-widened transposedPrims causes stack overflow in debug builds
566         SIMDVERTEX_T<SIMD_T> *transposedPrims = reinterpret_cast<SIMDVERTEX_T<SIMD_T> *>(AlignedMalloc(sizeof(SIMDVERTEX_T<SIMD_T>) * 2, 64));
567 
568 #else
569         SIMDVERTEX_T<SIMD_T> transposedPrims[2];
570 
571 #endif
572         uint32_t numInputPrims = pa.NumPrims();
573         for (uint32_t inputPrim = 0; inputPrim < numInputPrims; ++inputPrim)
574         {
575             uint32_t numEmittedVerts = pVertexCount[inputPrim];
576             if (numEmittedVerts < NumVertsPerPrim)
577             {
578                 continue;
579             }
580             SWR_ASSERT(numEmittedVerts <= 7, "Unexpected vertex count from clipper.");
581 
582             uint32_t numEmittedPrims = GetNumPrims(clipTopology, numEmittedVerts);
583             SWR_ASSERT(numEmittedPrims <= 7, "Unexpected primitive count from clipper.");
584 
585             numClippedPrims += numEmittedPrims;
586 
587             // tranpose clipper output so that each lane's vertices are in SIMD order
588             // set aside space for 2 vertices, as the PA will try to read up to 16 verts
589             // for triangle fan
590 
591             // transpose pos
592             uint8_t *pBase = reinterpret_cast<uint8_t *>(&vertices[0].attrib[VERTEX_POSITION_SLOT]) + sizeof(float) * inputPrim;
593 
594 #if 0
595             // TEMPORARY WORKAROUND for bizarre VS2015 code-gen bug
596             static const float *dummy = reinterpret_cast<const float *>(pBase);
597 
598 #endif
599             for (uint32_t c = 0; c < 4; ++c)
600             {
601                 SIMD256::Float temp = SIMD256::template mask_i32gather_ps<typename SIMD_T::ScaleFactor(1)>(SIMD256::setzero_ps(), reinterpret_cast<const float *>(pBase), vOffsets, vMask);
602                 transposedPrims[0].attrib[VERTEX_POSITION_SLOT][c] = SimdHelper<SIMD_T>::insert_lo_ps(temp);
603                 pBase += sizeof(typename SIMD_T::Float);
604             }
605 
606             // transpose attribs
607             pBase = reinterpret_cast<uint8_t *>(&vertices[0].attrib[backendState.vertexAttribOffset]) + sizeof(float) * inputPrim;
608 
609             for (uint32_t attrib = 0; attrib < numAttribs; ++attrib)
610             {
611                 uint32_t attribSlot = backendState.vertexAttribOffset + attrib;
612 
613                 for (uint32_t c = 0; c < 4; ++c)
614                 {
615                     SIMD256::Float temp = SIMD256::template mask_i32gather_ps<typename SIMD_T::ScaleFactor(1)>(SIMD256::setzero_ps(), reinterpret_cast<const float *>(pBase), vOffsets, vMask);
616                     transposedPrims[0].attrib[attribSlot][c] = SimdHelper<SIMD_T>::insert_lo_ps(temp);
617                     pBase += sizeof(typename SIMD_T::Float);
618                 }
619             }
620 
621             // transpose user clip distances if enabled
622             uint32_t vertexClipCullSlot = backendState.vertexClipCullOffset;
623             if (state.backendState.clipDistanceMask & 0x0f)
624             {
625                 pBase = reinterpret_cast<uint8_t *>(&vertices[0].attrib[vertexClipCullSlot]) + sizeof(float) * inputPrim;
626 
627                 for (uint32_t c = 0; c < 4; ++c)
628                 {
629                     SIMD256::Float temp = SIMD256::template mask_i32gather_ps<typename SIMD_T::ScaleFactor(1)>(SIMD256::setzero_ps(), reinterpret_cast<const float *>(pBase), vOffsets, vMask);
630                     transposedPrims[0].attrib[vertexClipCullSlot][c] = SimdHelper<SIMD_T>::insert_lo_ps(temp);
631                     pBase += sizeof(typename SIMD_T::Float);
632                 }
633             }
634 
635             if (state.backendState.clipDistanceMask & 0xf0)
636             {
637                 pBase = reinterpret_cast<uint8_t *>(&vertices[0].attrib[vertexClipCullSlot + 1]) + sizeof(float) * inputPrim;
638 
639                 for (uint32_t c = 0; c < 4; ++c)
640                 {
641                     SIMD256::Float temp = SIMD256::template mask_i32gather_ps<typename SIMD_T::ScaleFactor(1)>(SIMD256::setzero_ps(), reinterpret_cast<const float *>(pBase), vOffsets, vMask);
642                     transposedPrims[0].attrib[vertexClipCullSlot + 1][c] = SimdHelper<SIMD_T>::insert_lo_ps(temp);
643                     pBase += sizeof(typename SIMD_T::Float);
644                 }
645             }
646 
647             PA_STATE_OPT clipPA(pDC, numEmittedPrims, reinterpret_cast<uint8_t *>(&transposedPrims[0]), numEmittedVerts, SWR_VTX_NUM_SLOTS, true, NumVertsPerPrim, clipTopology);
648             clipPA.viewportArrayActive = pa.viewportArrayActive;
649             clipPA.rtArrayActive = pa.rtArrayActive;
650 
651             static const uint32_t primMaskMap[] = { 0x0, 0x1, 0x3, 0x7, 0xf, 0x1f, 0x3f, 0x7f };
652 
653             const uint32_t primMask = primMaskMap[numEmittedPrims];
654 
655             const typename SIMD_T::Integer primID = SIMD_T::set1_epi32(pPrimitiveId[inputPrim]);
656             const typename SIMD_T::Integer viewportIdx = SIMD_T::set1_epi32(pViewportIdx[inputPrim]);
657             const typename SIMD_T::Integer rtIdx = SIMD_T::set1_epi32(pRtIdx[inputPrim]);
658 
659 
660             while (clipPA.GetNextStreamOutput())
661             {
662                 do
663                 {
664                     typename SIMD_T::Vec4 attrib[NumVertsPerPrim];
665 
666                     bool assemble = clipPA.Assemble(VERTEX_POSITION_SLOT, attrib);
667 
668                     if (assemble)
669                     {
670                         binner.pfnBinFunc(pDC, clipPA, workerId, attrib, primMask, primID, viewportIdx, rtIdx);
671                     }
672 
673                 } while (clipPA.NextPrim());
674             }
675         }
676 
677 #if defined(_DEBUG)
678         AlignedFree(transposedPrims);
679 
680 #endif
681         // update global pipeline stat
682         UPDATE_STAT_FE(CPrimitives, numClippedPrims);
683     }
684 
685     void ExecuteStage(PA_STATE &pa, typename SIMD_T::Vec4 prim[], uint32_t primMask,
686                       typename SIMD_T::Integer const &primId, typename SIMD_T::Integer const &viewportIdx, typename SIMD_T::Integer const &rtIdx)
687     {
688         SWR_ASSERT(pa.pDC != nullptr);
689 
690         SWR_CONTEXT *pContext = pa.pDC->pContext;
691 
692         BinnerChooser<SIMD_T> binner(pa.binTopology, pa.pDC->pState->state.rastState.conservativeRast);
693 
694         // update clipper invocations pipeline stat
695         uint32_t numInvoc = _mm_popcnt_u32(primMask);
696         UPDATE_STAT_FE(CInvocations, numInvoc);
697 
698         ComputeClipCodes(prim, viewportIdx);
699 
700         // cull prims with NAN coords
701         primMask &= ~ComputeNaNMask(prim);
702 
703         // user cull distance cull
704         if (state.backendState.cullDistanceMask)
705         {
706             primMask &= ~ComputeUserClipCullMask(pa, prim);
707         }
708 
709         // cull prims outside view frustum
710         typename SIMD_T::Float clipIntersection = ComputeClipCodeIntersection();
711         int validMask = primMask & SimdHelper<SIMD_T>::cmpeq_ps_mask(clipIntersection, SIMD_T::setzero_ps());
712 
713         // skip clipping for points
714         uint32_t clipMask = 0;
715         if (NumVertsPerPrim != 1)
716         {
717             clipMask = primMask & ComputeClipMask();
718         }
719 
720         if (clipMask)
721         {
722             AR_BEGIN(FEGuardbandClip, pa.pDC->drawId);
723             // we have to clip tris, execute the clipper, which will also
724             // call the binner
725             ClipSimd(prim, SIMD_T::vmask_ps(primMask), SIMD_T::vmask_ps(clipMask), pa, primId, viewportIdx, rtIdx);
726             AR_END(FEGuardbandClip, 1);
727         }
728         else if (validMask)
729         {
730             // update CPrimitives pipeline state
731             UPDATE_STAT_FE(CPrimitives, _mm_popcnt_u32(validMask));
732 
733             // forward valid prims directly to binner
734             binner.pfnBinFunc(this->pDC, pa, this->workerId, prim, validMask, primId, viewportIdx, rtIdx);
735         }
736     }
737 
738 private:
739     typename SIMD_T::Float ComputeInterpFactor(typename SIMD_T::Float const &boundaryCoord0, typename SIMD_T::Float const &boundaryCoord1)
740     {
741         return SIMD_T::div_ps(boundaryCoord0, SIMD_T::sub_ps(boundaryCoord0, boundaryCoord1));
742     }
743 
744     typename SIMD_T::Integer ComputeOffsets(uint32_t attrib, typename SIMD_T::Integer const &vIndices, uint32_t component)
745     {
746         const uint32_t simdVertexStride = sizeof(SIMDVERTEX_T<SIMD_T>);
747         const uint32_t componentStride  = sizeof(typename SIMD_T::Float);
748         const uint32_t attribStride     = sizeof(typename SIMD_T::Vec4);
749 
750         static const OSALIGNSIMD16(uint32_t) elemOffset[16] =
751         {
752             0 * sizeof(float),
753             1 * sizeof(float),
754             2 * sizeof(float),
755             3 * sizeof(float),
756             4 * sizeof(float),
757             5 * sizeof(float),
758             6 * sizeof(float),
759             7 * sizeof(float),
760             8 * sizeof(float),
761             9 * sizeof(float),
762             10 * sizeof(float),
763             11 * sizeof(float),
764             12 * sizeof(float),
765             13 * sizeof(float),
766             14 * sizeof(float),
767             15 * sizeof(float),
768         };
769 
770         static_assert(sizeof(typename SIMD_T::Integer) <= sizeof(elemOffset), "Clipper::ComputeOffsets, Increase number of element offsets.");
771 
772         typename SIMD_T::Integer vElemOffset = SIMD_T::loadu_si(reinterpret_cast<const typename SIMD_T::Integer *>(elemOffset));
773 
774         // step to the simdvertex
775         typename SIMD_T::Integer vOffsets = SIMD_T::mullo_epi32(vIndices, SIMD_T::set1_epi32(simdVertexStride));
776 
777         // step to the attribute and component
778         vOffsets = SIMD_T::add_epi32(vOffsets, SIMD_T::set1_epi32(attribStride * attrib + componentStride * component));
779 
780         // step to the lane
781         vOffsets = SIMD_T::add_epi32(vOffsets, vElemOffset);
782 
783         return vOffsets;
784     }
785 
786     typename SIMD_T::Float GatherComponent(const float* pBuffer, uint32_t attrib, typename SIMD_T::Float const &vMask, typename SIMD_T::Integer const &vIndices, uint32_t component)
787     {
788         typename SIMD_T::Integer vOffsets = ComputeOffsets(attrib, vIndices, component);
789         typename SIMD_T::Float vSrc = SIMD_T::setzero_ps();
790 
791         return SIMD_T::template mask_i32gather_ps<typename SIMD_T::ScaleFactor(1)>(vSrc, pBuffer, vOffsets, vMask);
792     }
793 
794     void ScatterComponent(const float* pBuffer, uint32_t attrib, typename SIMD_T::Float const &vMask, typename SIMD_T::Integer const &vIndices, uint32_t component, typename SIMD_T::Float const &vSrc)
795     {
796         typename SIMD_T::Integer vOffsets = ComputeOffsets(attrib, vIndices, component);
797 
798         const uint32_t *pOffsets = reinterpret_cast<const uint32_t *>(&vOffsets);
799         const float *pSrc = reinterpret_cast<const float *>(&vSrc);
800         uint32_t mask = SIMD_T::movemask_ps(vMask);
801         DWORD lane;
802         while (_BitScanForward(&lane, mask))
803         {
804             mask &= ~(1 << lane);
805             const uint8_t *pBuf = reinterpret_cast<const uint8_t *>(pBuffer) + pOffsets[lane];
806             *(float *)pBuf = pSrc[lane];
807         }
808     }
809 
810     template<SWR_CLIPCODES ClippingPlane>
811     void intersect(
812         const typename SIMD_T::Float &vActiveMask,  // active lanes to operate on
813         const typename SIMD_T::Integer &s,          // index to first edge vertex v0 in pInPts.
814         const typename SIMD_T::Integer &p,          // index to second edge vertex v1 in pInPts.
815         const typename SIMD_T::Vec4 &v1,            // vertex 0 position
816         const typename SIMD_T::Vec4 &v2,            // vertex 1 position
817         typename SIMD_T::Integer &outIndex,         // output index.
818         const float *pInVerts,                      // array of all the input positions.
819         uint32_t numInAttribs,                      // number of attributes per vertex.
820         float *pOutVerts)                           // array of output positions. We'll write our new intersection point at i*4.
821     {
822         uint32_t vertexAttribOffset = this->state.backendState.vertexAttribOffset;
823         uint32_t vertexClipCullOffset = this->state.backendState.vertexClipCullOffset;
824 
825         // compute interpolation factor
826         typename SIMD_T::Float t;
827         switch (ClippingPlane)
828         {
829         case FRUSTUM_LEFT:      t = ComputeInterpFactor(SIMD_T::add_ps(v1[3], v1[0]), SIMD_T::add_ps(v2[3], v2[0])); break;
830         case FRUSTUM_RIGHT:     t = ComputeInterpFactor(SIMD_T::sub_ps(v1[3], v1[0]), SIMD_T::sub_ps(v2[3], v2[0])); break;
831         case FRUSTUM_TOP:       t = ComputeInterpFactor(SIMD_T::add_ps(v1[3], v1[1]), SIMD_T::add_ps(v2[3], v2[1])); break;
832         case FRUSTUM_BOTTOM:    t = ComputeInterpFactor(SIMD_T::sub_ps(v1[3], v1[1]), SIMD_T::sub_ps(v2[3], v2[1])); break;
833         case FRUSTUM_NEAR:
834             // DX Znear plane is 0, GL is -w
835             if (this->state.rastState.clipHalfZ)
836             {
837                 t = ComputeInterpFactor(v1[2], v2[2]);
838             }
839             else
840             {
841                 t = ComputeInterpFactor(SIMD_T::add_ps(v1[3], v1[2]), SIMD_T::add_ps(v2[3], v2[2]));
842             }
843             break;
844         case FRUSTUM_FAR:       t = ComputeInterpFactor(SIMD_T::sub_ps(v1[3], v1[2]), SIMD_T::sub_ps(v2[3], v2[2])); break;
845         default: SWR_INVALID("invalid clipping plane: %d", ClippingPlane);
846         };
847 
848         // interpolate position and store
849         for (uint32_t c = 0; c < 4; ++c)
850         {
851             typename SIMD_T::Float vOutPos = SIMD_T::fmadd_ps(SIMD_T::sub_ps(v2[c], v1[c]), t, v1[c]);
852             ScatterComponent(pOutVerts, VERTEX_POSITION_SLOT, vActiveMask, outIndex, c, vOutPos);
853         }
854 
855         // interpolate attributes and store
856         for (uint32_t a = 0; a < numInAttribs; ++a)
857         {
858             uint32_t attribSlot = vertexAttribOffset + a;
859             for (uint32_t c = 0; c < 4; ++c)
860             {
861                 typename SIMD_T::Float vAttrib0 = GatherComponent(pInVerts, attribSlot, vActiveMask, s, c);
862                 typename SIMD_T::Float vAttrib1 = GatherComponent(pInVerts, attribSlot, vActiveMask, p, c);
863                 typename SIMD_T::Float vOutAttrib = SIMD_T::fmadd_ps(SIMD_T::sub_ps(vAttrib1, vAttrib0), t, vAttrib0);
864                 ScatterComponent(pOutVerts, attribSlot, vActiveMask, outIndex, c, vOutAttrib);
865             }
866         }
867 
868         // interpolate clip distance if enabled
869         if (this->state.backendState.clipDistanceMask & 0xf)
870         {
871             uint32_t attribSlot = vertexClipCullOffset;
872             for (uint32_t c = 0; c < 4; ++c)
873             {
874                 typename SIMD_T::Float vAttrib0 = GatherComponent(pInVerts, attribSlot, vActiveMask, s, c);
875                 typename SIMD_T::Float vAttrib1 = GatherComponent(pInVerts, attribSlot, vActiveMask, p, c);
876                 typename SIMD_T::Float vOutAttrib = SIMD_T::fmadd_ps(SIMD_T::sub_ps(vAttrib1, vAttrib0), t, vAttrib0);
877                 ScatterComponent(pOutVerts, attribSlot, vActiveMask, outIndex, c, vOutAttrib);
878             }
879         }
880 
881         if (this->state.backendState.clipDistanceMask & 0xf0)
882         {
883             uint32_t attribSlot = vertexClipCullOffset + 1;
884             for (uint32_t c = 0; c < 4; ++c)
885             {
886                 typename SIMD_T::Float vAttrib0 = GatherComponent(pInVerts, attribSlot, vActiveMask, s, c);
887                 typename SIMD_T::Float vAttrib1 = GatherComponent(pInVerts, attribSlot, vActiveMask, p, c);
888                 typename SIMD_T::Float vOutAttrib = SIMD_T::fmadd_ps(SIMD_T::sub_ps(vAttrib1, vAttrib0), t, vAttrib0);
889                 ScatterComponent(pOutVerts, attribSlot, vActiveMask, outIndex, c, vOutAttrib);
890             }
891         }
892     }
893 
894     template<SWR_CLIPCODES ClippingPlane>
895     typename SIMD_T::Float inside(const typename SIMD_T::Vec4 &v)
896     {
897         switch (ClippingPlane)
898         {
899         case FRUSTUM_LEFT:      return SIMD_T::cmpge_ps(v[0], SIMD_T::mul_ps(v[3], SIMD_T::set1_ps(-1.0f)));
900         case FRUSTUM_RIGHT:     return SIMD_T::cmple_ps(v[0], v[3]);
901         case FRUSTUM_TOP:       return SIMD_T::cmpge_ps(v[1], SIMD_T::mul_ps(v[3], SIMD_T::set1_ps(-1.0f)));
902         case FRUSTUM_BOTTOM:    return SIMD_T::cmple_ps(v[1], v[3]);
903         case FRUSTUM_NEAR:      return SIMD_T::cmpge_ps(v[2], this->state.rastState.clipHalfZ ? SIMD_T::setzero_ps() : SIMD_T::mul_ps(v[3], SIMD_T::set1_ps(-1.0f)));
904         case FRUSTUM_FAR:       return SIMD_T::cmple_ps(v[2], v[3]);
905         default:
906             SWR_INVALID("invalid clipping plane: %d", ClippingPlane);
907             return SIMD_T::setzero_ps();
908         }
909     }
910 
911     template<SWR_CLIPCODES ClippingPlane>
912     typename SIMD_T::Integer ClipTriToPlane(const float *pInVerts, const typename SIMD_T::Integer &vNumInPts, uint32_t numInAttribs, float *pOutVerts)
913     {
914         uint32_t vertexAttribOffset = this->state.backendState.vertexAttribOffset;
915 
916         typename SIMD_T::Integer vCurIndex = SIMD_T::setzero_si();
917         typename SIMD_T::Integer vOutIndex = SIMD_T::setzero_si();
918         typename SIMD_T::Float vActiveMask = SIMD_T::castsi_ps(SIMD_T::cmplt_epi32(vCurIndex, vNumInPts));
919 
920         while (!SIMD_T::testz_ps(vActiveMask, vActiveMask)) // loop until activeMask is empty
921         {
922             typename SIMD_T::Integer s = vCurIndex;
923             typename SIMD_T::Integer p = SIMD_T::add_epi32(s, SIMD_T::set1_epi32(1));
924             typename SIMD_T::Integer underFlowMask = SIMD_T::cmpgt_epi32(vNumInPts, p);
925             p = SIMD_T::castps_si(SIMD_T::blendv_ps(SIMD_T::setzero_ps(), SIMD_T::castsi_ps(p), SIMD_T::castsi_ps(underFlowMask)));
926 
927             // gather position
928             typename SIMD_T::Vec4 vInPos0, vInPos1;
929             for (uint32_t c = 0; c < 4; ++c)
930             {
931                 vInPos0[c] = GatherComponent(pInVerts, VERTEX_POSITION_SLOT, vActiveMask, s, c);
932                 vInPos1[c] = GatherComponent(pInVerts, VERTEX_POSITION_SLOT, vActiveMask, p, c);
933             }
934 
935             // compute inside mask
936             typename SIMD_T::Float s_in = inside<ClippingPlane>(vInPos0);
937             typename SIMD_T::Float p_in = inside<ClippingPlane>(vInPos1);
938 
939             // compute intersection mask (s_in != p_in)
940             typename SIMD_T::Float intersectMask = SIMD_T::xor_ps(s_in, p_in);
941             intersectMask = SIMD_T::and_ps(intersectMask, vActiveMask);
942 
943             // store s if inside
944             s_in = SIMD_T::and_ps(s_in, vActiveMask);
945             if (!SIMD_T::testz_ps(s_in, s_in))
946             {
947                 // store position
948                 for (uint32_t c = 0; c < 4; ++c)
949                 {
950                     ScatterComponent(pOutVerts, VERTEX_POSITION_SLOT, s_in, vOutIndex, c, vInPos0[c]);
951                 }
952 
953                 // store attribs
954                 for (uint32_t a = 0; a < numInAttribs; ++a)
955                 {
956                     uint32_t attribSlot = vertexAttribOffset + a;
957                     for (uint32_t c = 0; c < 4; ++c)
958                     {
959                         typename SIMD_T::Float vAttrib = GatherComponent(pInVerts, attribSlot, s_in, s, c);
960                         ScatterComponent(pOutVerts, attribSlot, s_in, vOutIndex, c, vAttrib);
961                     }
962                 }
963 
964                 // store clip distance if enabled
965                 uint32_t vertexClipCullSlot = this->state.backendState.vertexClipCullOffset;
966                 if (this->state.backendState.clipDistanceMask & 0xf)
967                 {
968                     uint32_t attribSlot = vertexClipCullSlot;
969                     for (uint32_t c = 0; c < 4; ++c)
970                     {
971                         typename SIMD_T::Float vAttrib = GatherComponent(pInVerts, attribSlot, s_in, s, c);
972                         ScatterComponent(pOutVerts, attribSlot, s_in, vOutIndex, c, vAttrib);
973                     }
974                 }
975 
976                 if (this->state.backendState.clipDistanceMask & 0xf0)
977                 {
978                     uint32_t attribSlot = vertexClipCullSlot + 1;
979                     for (uint32_t c = 0; c < 4; ++c)
980                     {
981                         typename SIMD_T::Float vAttrib = GatherComponent(pInVerts, attribSlot, s_in, s, c);
982                         ScatterComponent(pOutVerts, attribSlot, s_in, vOutIndex, c, vAttrib);
983                     }
984                 }
985 
986                 // increment outIndex
987                 vOutIndex = SIMD_T::blendv_epi32(vOutIndex, SIMD_T::add_epi32(vOutIndex, SIMD_T::set1_epi32(1)), s_in);
988             }
989 
990             // compute and store intersection
991             if (!SIMD_T::testz_ps(intersectMask, intersectMask))
992             {
993                 intersect<ClippingPlane>(intersectMask, s, p, vInPos0, vInPos1, vOutIndex, pInVerts, numInAttribs, pOutVerts);
994 
995                 // increment outIndex for active lanes
996                 vOutIndex = SIMD_T::blendv_epi32(vOutIndex, SIMD_T::add_epi32(vOutIndex, SIMD_T::set1_epi32(1)), intersectMask);
997             }
998 
999             // increment loop index and update active mask
1000             vCurIndex = SIMD_T::add_epi32(vCurIndex, SIMD_T::set1_epi32(1));
1001             vActiveMask = SIMD_T::castsi_ps(SIMD_T::cmplt_epi32(vCurIndex, vNumInPts));
1002         }
1003 
1004         return vOutIndex;
1005     }
1006 
1007     template<SWR_CLIPCODES ClippingPlane>
1008     typename SIMD_T::Integer ClipLineToPlane(const float *pInVerts, const typename SIMD_T::Integer &vNumInPts, uint32_t numInAttribs, float *pOutVerts)
1009     {
1010         uint32_t vertexAttribOffset = this->state.backendState.vertexAttribOffset;
1011 
1012         typename SIMD_T::Integer vCurIndex = SIMD_T::setzero_si();
1013         typename SIMD_T::Integer vOutIndex = SIMD_T::setzero_si();
1014         typename SIMD_T::Float vActiveMask = SIMD_T::castsi_ps(SIMD_T::cmplt_epi32(vCurIndex, vNumInPts));
1015 
1016         if (!SIMD_T::testz_ps(vActiveMask, vActiveMask))
1017         {
1018             typename SIMD_T::Integer s = vCurIndex;
1019             typename SIMD_T::Integer p = SIMD_T::add_epi32(s, SIMD_T::set1_epi32(1));
1020 
1021             // gather position
1022             typename SIMD_T::Vec4 vInPos0, vInPos1;
1023             for (uint32_t c = 0; c < 4; ++c)
1024             {
1025                 vInPos0[c] = GatherComponent(pInVerts, VERTEX_POSITION_SLOT, vActiveMask, s, c);
1026                 vInPos1[c] = GatherComponent(pInVerts, VERTEX_POSITION_SLOT, vActiveMask, p, c);
1027             }
1028 
1029             // compute inside mask
1030             typename SIMD_T::Float s_in = inside<ClippingPlane>(vInPos0);
1031             typename SIMD_T::Float p_in = inside<ClippingPlane>(vInPos1);
1032 
1033             // compute intersection mask (s_in != p_in)
1034             typename SIMD_T::Float intersectMask = SIMD_T::xor_ps(s_in, p_in);
1035             intersectMask = SIMD_T::and_ps(intersectMask, vActiveMask);
1036 
1037             // store s if inside
1038             s_in = SIMD_T::and_ps(s_in, vActiveMask);
1039             if (!SIMD_T::testz_ps(s_in, s_in))
1040             {
1041                 for (uint32_t c = 0; c < 4; ++c)
1042                 {
1043                     ScatterComponent(pOutVerts, VERTEX_POSITION_SLOT, s_in, vOutIndex, c, vInPos0[c]);
1044                 }
1045 
1046                 // interpolate attributes and store
1047                 for (uint32_t a = 0; a < numInAttribs; ++a)
1048                 {
1049                     uint32_t attribSlot = vertexAttribOffset + a;
1050                     for (uint32_t c = 0; c < 4; ++c)
1051                     {
1052                         typename SIMD_T::Float vAttrib = GatherComponent(pInVerts, attribSlot, s_in, s, c);
1053                         ScatterComponent(pOutVerts, attribSlot, s_in, vOutIndex, c, vAttrib);
1054                     }
1055                 }
1056 
1057                 // increment outIndex
1058                 vOutIndex = SIMD_T::blendv_epi32(vOutIndex, SIMD_T::add_epi32(vOutIndex, SIMD_T::set1_epi32(1)), s_in);
1059             }
1060 
1061             // compute and store intersection
1062             if (!SIMD_T::testz_ps(intersectMask, intersectMask))
1063             {
1064                 intersect<ClippingPlane>(intersectMask, s, p, vInPos0, vInPos1, vOutIndex, pInVerts, numInAttribs, pOutVerts);
1065 
1066                 // increment outIndex for active lanes
1067                 vOutIndex = SIMD_T::blendv_epi32(vOutIndex, SIMD_T::add_epi32(vOutIndex, SIMD_T::set1_epi32(1)), intersectMask);
1068             }
1069 
1070             // store p if inside
1071             p_in = SIMD_T::and_ps(p_in, vActiveMask);
1072             if (!SIMD_T::testz_ps(p_in, p_in))
1073             {
1074                 for (uint32_t c = 0; c < 4; ++c)
1075                 {
1076                     ScatterComponent(pOutVerts, VERTEX_POSITION_SLOT, p_in, vOutIndex, c, vInPos1[c]);
1077                 }
1078 
1079                 // interpolate attributes and store
1080                 for (uint32_t a = 0; a < numInAttribs; ++a)
1081                 {
1082                     uint32_t attribSlot = vertexAttribOffset + a;
1083                     for (uint32_t c = 0; c < 4; ++c)
1084                     {
1085                         typename SIMD_T::Float vAttrib = GatherComponent(pInVerts, attribSlot, p_in, p, c);
1086                         ScatterComponent(pOutVerts, attribSlot, p_in, vOutIndex, c, vAttrib);
1087                     }
1088                 }
1089 
1090                 // increment outIndex
1091                 vOutIndex = SIMD_T::blendv_epi32(vOutIndex, SIMD_T::add_epi32(vOutIndex, SIMD_T::set1_epi32(1)), p_in);
1092             }
1093         }
1094 
1095         return vOutIndex;
1096     }
1097 
1098     typename SIMD_T::Integer ClipPrims(float *pVertices, const typename SIMD_T::Float &vPrimMask, const typename SIMD_T::Float &vClipMask, int numAttribs)
1099     {
1100         // temp storage
1101         float *pTempVerts = reinterpret_cast<float *>(ClipHelper<SIMD_T>::GetTempVertices());
1102 
1103         // zero out num input verts for non-active lanes
1104         typename SIMD_T::Integer vNumInPts = SIMD_T::set1_epi32(NumVertsPerPrim);
1105         vNumInPts = SIMD_T::blendv_epi32(SIMD_T::setzero_si(), vNumInPts, vClipMask);
1106 
1107         // clip prims to frustum
1108         typename SIMD_T::Integer vNumOutPts;
1109         if (NumVertsPerPrim == 3)
1110         {
1111             vNumOutPts = ClipTriToPlane<FRUSTUM_NEAR>(pVertices, vNumInPts, numAttribs, pTempVerts);
1112             vNumOutPts = ClipTriToPlane<FRUSTUM_FAR>(pTempVerts, vNumOutPts, numAttribs, pVertices);
1113             vNumOutPts = ClipTriToPlane<FRUSTUM_LEFT>(pVertices, vNumOutPts, numAttribs, pTempVerts);
1114             vNumOutPts = ClipTriToPlane<FRUSTUM_RIGHT>(pTempVerts, vNumOutPts, numAttribs, pVertices);
1115             vNumOutPts = ClipTriToPlane<FRUSTUM_BOTTOM>(pVertices, vNumOutPts, numAttribs, pTempVerts);
1116             vNumOutPts = ClipTriToPlane<FRUSTUM_TOP>(pTempVerts, vNumOutPts, numAttribs, pVertices);
1117         }
1118         else
1119         {
1120             SWR_ASSERT(NumVertsPerPrim == 2);
1121             vNumOutPts = ClipLineToPlane<FRUSTUM_NEAR>(pVertices, vNumInPts, numAttribs, pTempVerts);
1122             vNumOutPts = ClipLineToPlane<FRUSTUM_FAR>(pTempVerts, vNumOutPts, numAttribs, pVertices);
1123             vNumOutPts = ClipLineToPlane<FRUSTUM_LEFT>(pVertices, vNumOutPts, numAttribs, pTempVerts);
1124             vNumOutPts = ClipLineToPlane<FRUSTUM_RIGHT>(pTempVerts, vNumOutPts, numAttribs, pVertices);
1125             vNumOutPts = ClipLineToPlane<FRUSTUM_BOTTOM>(pVertices, vNumOutPts, numAttribs, pTempVerts);
1126             vNumOutPts = ClipLineToPlane<FRUSTUM_TOP>(pTempVerts, vNumOutPts, numAttribs, pVertices);
1127         }
1128 
1129         // restore num verts for non-clipped, active lanes
1130         typename SIMD_T::Float vNonClippedMask = SIMD_T::andnot_ps(vClipMask, vPrimMask);
1131         vNumOutPts = SIMD_T::blendv_epi32(vNumOutPts, SIMD_T::set1_epi32(NumVertsPerPrim), vNonClippedMask);
1132 
1133         return vNumOutPts;
1134     }
1135 
1136     const uint32_t workerId{ 0 };
1137     DRAW_CONTEXT *pDC{ nullptr };
1138     const API_STATE &state;
1139     typename SIMD_T::Float clipCodes[NumVertsPerPrim];
1140 };
1141 
1142 
1143 // pipeline stage functions
1144 void ClipTriangles(DRAW_CONTEXT *pDC, PA_STATE& pa, uint32_t workerId, simdvector prims[], uint32_t primMask, simdscalari const &primId, simdscalari const &viewportIdx, simdscalari const &rtIdx);
1145 void ClipLines(DRAW_CONTEXT *pDC, PA_STATE& pa, uint32_t workerId, simdvector prims[], uint32_t primMask, simdscalari const &primId, simdscalari const &viewportIdx, simdscalari const &rtIdx);
1146 void ClipPoints(DRAW_CONTEXT *pDC, PA_STATE& pa, uint32_t workerId, simdvector prims[], uint32_t primMask, simdscalari const &primId, simdscalari const &viewportIdx, simdscalari const &rtIdx);
1147 #if USE_SIMD16_FRONTEND
1148 void SIMDCALL ClipTriangles_simd16(DRAW_CONTEXT *pDC, PA_STATE& pa, uint32_t workerId, simd16vector prims[], uint32_t primMask, simd16scalari const &primId, simd16scalari const &viewportIdx, simd16scalari const &rtIdx);
1149 void SIMDCALL ClipLines_simd16(DRAW_CONTEXT *pDC, PA_STATE& pa, uint32_t workerId, simd16vector prims[], uint32_t primMask, simd16scalari const &primId, simd16scalari const &viewportIdx, simd16scalari const &rtIdx);
1150 void SIMDCALL ClipPoints_simd16(DRAW_CONTEXT *pDC, PA_STATE& pa, uint32_t workerId, simd16vector prims[], uint32_t primMask, simd16scalari const &primId, simd16scalari const &viewportIdx, simd16scalari const &rtIdx);
1151 #endif
1152 
1153