xref: /aosp_15_r20/external/mesa3d/src/gallium/auxiliary/draw/draw_pipe_offset.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /**************************************************************************
2  *
3  * Copyright 2007 VMware, Inc.
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial portions
16  * of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  **************************************************************************/
27 
28 /**
29  * \brief  polygon offset state
30  *
31  * \author  Keith Whitwell <[email protected]>
32  * \author  Brian Paul
33  */
34 
35 #include "util/format/u_format.h"
36 #include "util/u_math.h"
37 #include "util/u_memory.h"
38 #include "draw_pipe.h"
39 
40 
41 struct offset_stage {
42    struct draw_stage stage;
43 
44    float scale;
45    float units;
46    float clamp;
47 };
48 
49 
50 static inline struct offset_stage *
offset_stage(struct draw_stage * stage)51 offset_stage(struct draw_stage *stage)
52 {
53    return (struct offset_stage *) stage;
54 }
55 
56 
57 /**
58  * Offset tri Z.  Some hardware can handle this, but not usually when
59  * doing unfilled rendering.
60  */
61 static void
do_offset_tri(struct draw_stage * stage,struct prim_header * header)62 do_offset_tri(struct draw_stage *stage,
63               struct prim_header *header)
64 {
65    const unsigned pos = draw_current_shader_position_output(stage->draw);
66    struct offset_stage *offset = offset_stage(stage);
67    float inv_det = 1.0f / header->det;
68 
69    /* Window coords:
70     */
71    float *v0 = header->v[0]->data[pos];
72    float *v1 = header->v[1]->data[pos];
73    float *v2 = header->v[2]->data[pos];
74 
75    /* edge vectors e = v0 - v2, f = v1 - v2 */
76    float ex = v0[0] - v2[0];
77    float ey = v0[1] - v2[1];
78    float ez = v0[2] - v2[2];
79    float fx = v1[0] - v2[0];
80    float fy = v1[1] - v2[1];
81    float fz = v1[2] - v2[2];
82 
83    /* (a,b) = cross(e,f).xy */
84    float a = ey*fz - ez*fy;
85    float b = ez*fx - ex*fz;
86 
87    float dzdx = fabsf(a * inv_det);
88    float dzdy = fabsf(b * inv_det);
89 
90    float mult = MAX2(dzdx, dzdy) * offset->scale;
91 
92    float zoffset;
93    if (stage->draw->floating_point_depth) {
94       float bias;
95       union fi maxz;
96       maxz.f = MAX3(fabs(v0[2]), fabs(v1[2]), fabs(v2[2]));
97       /* just do the math directly on shifted number */
98       maxz.ui &= 0xff << 23;
99       maxz.i -= 23 << 23;
100       /* Clamping to zero means mrd will be zero for very small numbers,
101        * but specs do not indicate this should be prevented by clamping
102        * mrd to smallest normal number instead. */
103       maxz.i = MAX2(maxz.i, 0);
104 
105       bias = offset->units * maxz.f;
106       zoffset = bias + mult;
107    } else {
108       zoffset = offset->units + mult;
109    }
110 
111    if (offset->clamp)
112       zoffset = (offset->clamp < 0.0f) ? MAX2(zoffset, offset->clamp) :
113                                          MIN2(zoffset, offset->clamp);
114 
115    /*
116     * Note: we're applying the offset and clamping per-vertex.
117     * Ideally, the offset is applied per-fragment prior to fragment shading.
118     */
119    v0[2] = SATURATE(v0[2] + zoffset);
120    v1[2] = SATURATE(v1[2] + zoffset);
121    v2[2] = SATURATE(v2[2] + zoffset);
122 
123    stage->next->tri(stage->next, header);
124 }
125 
126 
127 static void
offset_tri(struct draw_stage * stage,struct prim_header * header)128 offset_tri(struct draw_stage *stage,
129            struct prim_header *header)
130 {
131    struct prim_header tmp;
132 
133    tmp.det = header->det;
134    tmp.flags = header->flags;
135    tmp.pad = header->pad;
136    tmp.v[0] = dup_vert(stage, header->v[0], 0);
137    tmp.v[1] = dup_vert(stage, header->v[1], 1);
138    tmp.v[2] = dup_vert(stage, header->v[2], 2);
139 
140    do_offset_tri(stage, &tmp);
141 }
142 
143 
144 static void
offset_first_tri(struct draw_stage * stage,struct prim_header * header)145 offset_first_tri(struct draw_stage *stage,
146                  struct prim_header *header)
147 {
148    struct offset_stage *offset = offset_stage(stage);
149    const struct pipe_rasterizer_state *rast = stage->draw->rasterizer;
150    unsigned fill_mode = rast->fill_front;
151    bool do_offset;
152 
153    if (rast->fill_back != rast->fill_front) {
154       /* Need to check for back-facing triangle */
155       bool ccw = header->det < 0.0f;
156       if (ccw != rast->front_ccw)
157          fill_mode = rast->fill_back;
158    }
159 
160    /* Now determine if we need to do offsetting for the point/line/fill mode */
161    switch (fill_mode) {
162    case PIPE_POLYGON_MODE_FILL:
163       do_offset = rast->offset_tri;
164       break;
165    case PIPE_POLYGON_MODE_LINE:
166       do_offset = rast->offset_line;
167       break;
168    case PIPE_POLYGON_MODE_POINT:
169       do_offset = rast->offset_point;
170       break;
171    default:
172       assert(!"invalid fill_mode in offset_first_tri()");
173       do_offset = rast->offset_tri;
174    }
175 
176    if (do_offset) {
177       offset->scale = rast->offset_scale;
178       offset->clamp = rast->offset_clamp;
179 
180       /*
181        * If depth is floating point, depth bias is calculated with respect
182        * to the primitive's maximum Z value. Retain the original depth bias
183        * value until that stage.
184        */
185       if (stage->draw->floating_point_depth) {
186          offset->units = (float) rast->offset_units;
187       } else {
188          offset->units = (float) (rast->offset_units * stage->draw->mrd * 2);
189       }
190    } else {
191       offset->scale = 0.0f;
192       offset->clamp = 0.0f;
193       offset->units = 0.0f;
194    }
195 
196    stage->tri = offset_tri;
197    stage->tri(stage, header);
198 }
199 
200 
201 static void
offset_flush(struct draw_stage * stage,unsigned flags)202 offset_flush(struct draw_stage *stage,
203              unsigned flags)
204 {
205    stage->tri = offset_first_tri;
206    stage->next->flush(stage->next, flags);
207 }
208 
209 
210 static void
offset_reset_stipple_counter(struct draw_stage * stage)211 offset_reset_stipple_counter(struct draw_stage *stage)
212 {
213    stage->next->reset_stipple_counter(stage->next);
214 }
215 
216 
217 static void
offset_destroy(struct draw_stage * stage)218 offset_destroy(struct draw_stage *stage)
219 {
220    draw_free_temp_verts(stage);
221    FREE(stage);
222 }
223 
224 
225 /**
226  * Create polygon offset drawing stage.
227  */
228 struct draw_stage *
draw_offset_stage(struct draw_context * draw)229 draw_offset_stage(struct draw_context *draw)
230 {
231    struct offset_stage *offset = CALLOC_STRUCT(offset_stage);
232    if (!offset)
233       goto fail;
234 
235    offset->stage.draw = draw;
236    offset->stage.name = "offset";
237    offset->stage.next = NULL;
238    offset->stage.point = draw_pipe_passthrough_point;
239    offset->stage.line = draw_pipe_passthrough_line;
240    offset->stage.tri = offset_first_tri;
241    offset->stage.flush = offset_flush;
242    offset->stage.reset_stipple_counter = offset_reset_stipple_counter;
243    offset->stage.destroy = offset_destroy;
244 
245    if (!draw_alloc_temp_verts(&offset->stage, 3))
246       goto fail;
247 
248    return &offset->stage;
249 
250 fail:
251    if (offset)
252       offset->stage.destroy(&offset->stage);
253 
254    return NULL;
255 }
256