[Mesa-dev] [PATCH] draw: fix clip test with NaNs
Matt Turner
mattst88 at gmail.com
Thu Dec 17 11:22:53 PST 2015
On Thu, Dec 17, 2015 at 11:19 AM, Roland Scheidegger <sroland at vmware.com> wrote:
> Am 17.12.2015 um 19:13 schrieb Brian Paul:
>> On 12/17/2015 09:58 AM, sroland at vmware.com wrote:
>>> From: Roland Scheidegger <sroland at vmware.com>
>>>
>>> NaNs mean it should be clipped, otherwise the NaNs might get passed to
>>> the
>>> next stages (if clipping didn't happen for another reason already), which
>>> might cause all kind of problems.
>>> The llvm path got this right already (possibly by luck), but this
>>> isn't used
>>> when there's a gs active.
>>> Found by code inspection, verified with some hacked piglit test and
>>> some more
>>> hacked debug output.
>>> (Note the clipper can still itself incorrectly generate NaN and INF
>>> position
>>> values in its output prims (at least after w divide / viewport
>>> transform) even
>>> if the inputs weren't NaNs, if the position data of the vertices is
>>> "sufficiently bad".)
>>> ---
>>> src/gallium/auxiliary/draw/draw_cliptest_tmp.h | 28
>>> +++++++++++++-------------
>>> src/gallium/auxiliary/draw/draw_llvm.c | 4 ++++
>>> 2 files changed, 18 insertions(+), 14 deletions(-)
>>>
>>> diff --git a/src/gallium/auxiliary/draw/draw_cliptest_tmp.h
>>> b/src/gallium/auxiliary/draw/draw_cliptest_tmp.h
>>> index 779b237..d8866cd 100644
>>> --- a/src/gallium/auxiliary/draw/draw_cliptest_tmp.h
>>> +++ b/src/gallium/auxiliary/draw/draw_cliptest_tmp.h
>>> @@ -95,30 +95,31 @@ static boolean TAG(do_cliptest)( struct pt_post_vs
>>> *pvs,
>>> out->pre_clip_pos[i] = position[i];
>>> }
>>>
>>> + /* Be careful with NaNs. Comparisons must be true for them. */
>>> /* Do the hardwired planes first:
>>> */
>>> if (flags & DO_CLIP_XY_GUARD_BAND) {
>>> - if (-0.50 * position[0] + position[3] < 0) mask |= (1<<0);
>>> - if ( 0.50 * position[0] + position[3] < 0) mask |= (1<<1);
>>> - if (-0.50 * position[1] + position[3] < 0) mask |= (1<<2);
>>> - if ( 0.50 * position[1] + position[3] < 0) mask |= (1<<3);
>>> + if (!(-0.50 * position[0] + position[3] >= 0)) mask |=
>>> (1<<0);
>>> + if (!( 0.50 * position[0] + position[3] >= 0)) mask |=
>>> (1<<1);
>>> + if (!(-0.50 * position[1] + position[3] >= 0)) mask |=
>>> (1<<2);
>>> + if (!( 0.50 * position[1] + position[3] >= 0)) mask |=
>>> (1<<3);
>>> }
>>> else if (flags & DO_CLIP_XY) {
>>> - if (-position[0] + position[3] < 0) mask |= (1<<0);
>>> - if ( position[0] + position[3] < 0) mask |= (1<<1);
>>> - if (-position[1] + position[3] < 0) mask |= (1<<2);
>>> - if ( position[1] + position[3] < 0) mask |= (1<<3);
>>> + if (!(-position[0] + position[3] >= 0)) mask |= (1<<0);
>>> + if (!( position[0] + position[3] >= 0)) mask |= (1<<1);
>>> + if (!(-position[1] + position[3] >= 0)) mask |= (1<<2);
>>> + if (!( position[1] + position[3] >= 0)) mask |= (1<<3);
>>> }
>>>
>>> /* Clip Z planes according to full cube, half cube or none.
>>> */
>>> if (flags & DO_CLIP_FULL_Z) {
>>> - if ( position[2] + position[3] < 0) mask |= (1<<4);
>>> - if (-position[2] + position[3] < 0) mask |= (1<<5);
>>> + if (!( position[2] + position[3] >= 0)) mask |= (1<<4);
>>> + if (!(-position[2] + position[3] >= 0)) mask |= (1<<5);
>>> }
>>> else if (flags & DO_CLIP_HALF_Z) {
>>> - if ( position[2] < 0) mask |= (1<<4);
>>> - if (-position[2] + position[3] < 0) mask |= (1<<5);
>>> + if (!( position[2] >= 0)) mask |= (1<<4);
>>> + if (!(-position[2] + position[3] >= 0)) mask |= (1<<5);
>>> }
>>>
>>> if (flags & DO_CLIP_USER) {
>>> @@ -146,7 +147,7 @@ static boolean TAG(do_cliptest)( struct pt_post_vs
>>> *pvs,
>>> if (clipdist < 0 || util_is_inf_or_nan(clipdist))
>>> mask |= 1 << plane_idx;
>>> } else {
>>> - if (dot4(clipvertex, plane[plane_idx]) < 0)
>>> + if (!(dot4(clipvertex, plane[plane_idx]) >= 0))
>>> mask |= 1 << plane_idx;
>>> }
>>> }
>>> @@ -192,7 +193,6 @@ static boolean TAG(do_cliptest)( struct pt_post_vs
>>> *pvs,
>>>
>>> out = (struct vertex_header *)( (char *)out + info->stride );
>>> }
>>> -
>>> return need_pipeline != 0;
>>> }
>>>
>>> diff --git a/src/gallium/auxiliary/draw/draw_llvm.c
>>> b/src/gallium/auxiliary/draw/draw_llvm.c
>>> index 8435991..dad523a 100644
>>> --- a/src/gallium/auxiliary/draw/draw_llvm.c
>>> +++ b/src/gallium/auxiliary/draw/draw_llvm.c
>>> @@ -1200,6 +1200,10 @@ generate_clipmask(struct draw_llvm *llvm,
>>> cv_w = pos_w;
>>> }
>>>
>>> + /*
>>> + * Be careful with the comparisons and NaNs (using llvm's unordered
>>> + * comparisons here).
>>> + */
>>> /* Cliptest, for hardwired planes */
>>> if (clip_xy) {
>>> /* plane 1 */
>>>
>>
>> Looks OK to me, but I'm not sure I understand what'll happen when we
>> find a vertex with a NaN coordinate. Does the whole triangle get
>> culled? Otherwise, what would be the result of computing the
>> intersection point on the clip plane?
>>
>> Reviewed-by: Brian Paul <brianp at vmware.com>
>>
>
> If we set the clip mask bit when there's a NaN, this simply ensures
> we're entering the clip stage (and the clip stage will also actually
> perform clipping against the affected clip plane).
> We won't actually cull such a tri directly, but the math in the clip
> stage should ensure it will eventually be discarded. Because
> getclipdist() then should return a NaN too, which will cause the code to
> immediately return without emitting the tri (or any already generated
> tri due to clipping with other planes) immediately. As far as I can see,
> this logic should work reliably (the nans cannot disappear even if we
> performed some math on them due to clipping with other planes).
> There's more bugs in the clip stage but I don't quite understand it.
> Though I spotted some issue which could be problematic, there's some
> DIFFERENT_SIGNS logic (which is also used to avoid division by zero)
> which doesn't look bullet-proof. This is defined as:
> DIFFERENT_SIGNS(x, y) ((x) * (y) <= 0.0F && (x) - (y) != 0.0F)
> But for sufficiently small x and y (in the order of 2^-63) this will be
> true even if both x and y have the same sign (as the product would be
> zero). But there's probably more to it which causes it to generate bogus
> tris in the end...
I changed the implementation of DIFFERENT_SIGNS in src/mesa/main/macros.h to be
return signbit(x) != signbit(y);
a while ago. Perhaps you could just pull that into draw_pipe_clip.c?
More information about the mesa-dev
mailing list