1 /**************************************************************************
3 * Copyright 2007 VMware, Inc.
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:
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
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.
26 **************************************************************************/
29 * Binning code for lines
32 #include "util/u_math.h"
33 #include "util/u_memory.h"
35 #include "lp_setup_context.h"
37 #include "lp_state_fs.h"
38 #include "lp_state_setup.h"
39 #include "lp_context.h"
40 #include "draw/draw_context.h"
42 #define NUM_CHANNELS 4
61 * Compute a0 for a constant-valued coefficient (GL_FLAT shading).
63 static void constant_coef( struct lp_setup_context
*setup
,
64 struct lp_line_info
*info
,
69 info
->a0
[slot
][i
] = value
;
70 info
->dadx
[slot
][i
] = 0.0f
;
71 info
->dady
[slot
][i
] = 0.0f
;
76 * Compute a0, dadx and dady for a linearly interpolated coefficient,
79 static void linear_coef( struct lp_setup_context
*setup
,
80 struct lp_line_info
*info
,
85 float a1
= info
->v1
[vert_attr
][i
];
86 float a2
= info
->v2
[vert_attr
][i
];
89 float dadx
= da21
* info
->dx
* info
->oneoverarea
;
90 float dady
= da21
* info
->dy
* info
->oneoverarea
;
92 info
->dadx
[slot
][i
] = dadx
;
93 info
->dady
[slot
][i
] = dady
;
95 info
->a0
[slot
][i
] = (a1
-
96 (dadx
* (info
->v1
[0][0] - setup
->pixel_offset
) +
97 dady
* (info
->v1
[0][1] - setup
->pixel_offset
)));
102 * Compute a0, dadx and dady for a perspective-corrected interpolant,
104 * We basically multiply the vertex value by 1/w before computing
105 * the plane coefficients (a0, dadx, dady).
106 * Later, when we compute the value at a particular fragment position we'll
107 * divide the interpolated value by the interpolated W at that fragment.
109 static void perspective_coef( struct lp_setup_context
*setup
,
110 struct lp_line_info
*info
,
115 /* premultiply by 1/w (v[0][3] is always 1/w):
117 float a1
= info
->v1
[vert_attr
][i
] * info
->v1
[0][3];
118 float a2
= info
->v2
[vert_attr
][i
] * info
->v2
[0][3];
120 float da21
= a1
- a2
;
121 float dadx
= da21
* info
->dx
* info
->oneoverarea
;
122 float dady
= da21
* info
->dy
* info
->oneoverarea
;
124 info
->dadx
[slot
][i
] = dadx
;
125 info
->dady
[slot
][i
] = dady
;
127 info
->a0
[slot
][i
] = (a1
-
128 (dadx
* (info
->v1
[0][0] - setup
->pixel_offset
) +
129 dady
* (info
->v1
[0][1] - setup
->pixel_offset
)));
133 setup_fragcoord_coef( struct lp_setup_context
*setup
,
134 struct lp_line_info
*info
,
139 if (usage_mask
& TGSI_WRITEMASK_X
) {
140 info
->a0
[slot
][0] = 0.0;
141 info
->dadx
[slot
][0] = 1.0;
142 info
->dady
[slot
][0] = 0.0;
146 if (usage_mask
& TGSI_WRITEMASK_Y
) {
147 info
->a0
[slot
][1] = 0.0;
148 info
->dadx
[slot
][1] = 0.0;
149 info
->dady
[slot
][1] = 1.0;
153 if (usage_mask
& TGSI_WRITEMASK_Z
) {
154 linear_coef(setup
, info
, slot
, 0, 2);
158 if (usage_mask
& TGSI_WRITEMASK_W
) {
159 linear_coef(setup
, info
, slot
, 0, 3);
164 * Compute the tri->coef[] array dadx, dady, a0 values.
166 static void setup_line_coefficients( struct lp_setup_context
*setup
,
167 struct lp_line_info
*info
)
169 const struct lp_setup_variant_key
*key
= &setup
->setup
.variant
->key
;
170 unsigned fragcoord_usage_mask
= TGSI_WRITEMASK_XYZ
;
173 /* setup interpolation for all the remaining attributes:
175 for (slot
= 0; slot
< key
->num_inputs
; slot
++) {
176 unsigned vert_attr
= key
->inputs
[slot
].src_index
;
177 unsigned usage_mask
= key
->inputs
[slot
].usage_mask
;
180 switch (key
->inputs
[slot
].interp
) {
181 case LP_INTERP_CONSTANT
:
182 if (key
->flatshade_first
) {
183 for (i
= 0; i
< NUM_CHANNELS
; i
++)
184 if (usage_mask
& (1 << i
))
185 constant_coef(setup
, info
, slot
+1, info
->v1
[vert_attr
][i
], i
);
188 for (i
= 0; i
< NUM_CHANNELS
; i
++)
189 if (usage_mask
& (1 << i
))
190 constant_coef(setup
, info
, slot
+1, info
->v2
[vert_attr
][i
], i
);
194 case LP_INTERP_LINEAR
:
195 for (i
= 0; i
< NUM_CHANNELS
; i
++)
196 if (usage_mask
& (1 << i
))
197 linear_coef(setup
, info
, slot
+1, vert_attr
, i
);
200 case LP_INTERP_PERSPECTIVE
:
201 for (i
= 0; i
< NUM_CHANNELS
; i
++)
202 if (usage_mask
& (1 << i
))
203 perspective_coef(setup
, info
, slot
+1, vert_attr
, i
);
204 fragcoord_usage_mask
|= TGSI_WRITEMASK_W
;
207 case LP_INTERP_POSITION
:
209 * The generated pixel interpolators will pick up the coeffs from
210 * slot 0, so all need to ensure that the usage mask is covers all
213 fragcoord_usage_mask
|= usage_mask
;
216 case LP_INTERP_FACING
:
217 for (i
= 0; i
< NUM_CHANNELS
; i
++)
218 if (usage_mask
& (1 << i
))
219 constant_coef(setup
, info
, slot
+1,
220 info
->frontfacing
? 1.0f
: -1.0f
, i
);
228 /* The internal position input is in slot zero:
230 setup_fragcoord_coef(setup
, info
, 0,
231 fragcoord_usage_mask
);
236 static inline int subpixel_snap( float a
)
238 return util_iround(FIXED_ONE
* a
);
243 * Print line vertex attribs (for debug).
246 print_line(struct lp_setup_context
*setup
,
247 const float (*v1
)[4],
248 const float (*v2
)[4])
250 const struct lp_setup_variant_key
*key
= &setup
->setup
.variant
->key
;
253 debug_printf("llvmpipe line\n");
254 for (i
= 0; i
< 1 + key
->num_inputs
; i
++) {
255 debug_printf(" v1[%d]: %f %f %f %f\n", i
,
256 v1
[i
][0], v1
[i
][1], v1
[i
][2], v1
[i
][3]);
258 for (i
= 0; i
< 1 + key
->num_inputs
; i
++) {
259 debug_printf(" v2[%d]: %f %f %f %f\n", i
,
260 v2
[i
][0], v2
[i
][1], v2
[i
][2], v2
[i
][3]);
265 static inline boolean
sign(float x
){
270 /* Used on positive floats only:
272 static inline float fracf(float f
)
274 return f
- floorf(f
);
280 try_setup_line( struct lp_setup_context
*setup
,
281 const float (*v1
)[4],
282 const float (*v2
)[4])
284 struct llvmpipe_context
*lp_context
= (struct llvmpipe_context
*)setup
->pipe
;
285 struct lp_scene
*scene
= setup
->scene
;
286 const struct lp_setup_variant_key
*key
= &setup
->setup
.variant
->key
;
287 struct lp_rast_triangle
*line
;
288 struct lp_rast_plane
*plane
;
289 struct lp_line_info info
;
290 float width
= MAX2(1.0, setup
->line_width
);
291 const struct u_rect
*scissor
;
292 struct u_rect bbox
, bboxpos
;
299 unsigned viewport_index
= 0;
302 /* linewidth should be interpreted as integer */
303 int fixed_width
= util_iround(width
) * FIXED_ONE
;
307 float x_offset_end
=0;
308 float y_offset_end
=0;
316 const float (*pv
)[4];
320 boolean will_draw_start
;
321 boolean will_draw_end
;
323 if (lp_context
->active_statistics_queries
) {
324 lp_context
->pipeline_statistics
.c_primitives
++;
328 print_line(setup
, v1
, v2
);
330 if (setup
->flatshade_first
) {
336 if (setup
->viewport_index_slot
> 0) {
337 unsigned *udata
= (unsigned*)pv
[setup
->viewport_index_slot
];
338 viewport_index
= lp_clamp_viewport_idx(*udata
);
340 if (setup
->layer_slot
> 0) {
341 layer
= *(unsigned*)pv
[setup
->layer_slot
];
342 layer
= MIN2(layer
, scene
->fb_max_layer
);
345 dx
= v1
[0][0] - v2
[0][0];
346 dy
= v1
[0][1] - v2
[0][1];
347 area
= (dx
* dx
+ dy
* dy
);
349 LP_COUNT(nr_culled_tris
);
353 info
.oneoverarea
= 1.0f
/ area
;
361 if (fabsf(dx
) >= fabsf(dy
)) {
362 float dydx
= dy
/ dx
;
364 x1diff
= v1
[0][0] - floorf(v1
[0][0]) - 0.5f
;
365 y1diff
= v1
[0][1] - floorf(v1
[0][1]) - 0.5f
;
366 x2diff
= v2
[0][0] - floorf(v2
[0][0]) - 0.5f
;
367 y2diff
= v2
[0][1] - floorf(v2
[0][1]) - 0.5f
;
369 if (y2diff
==-0.5 && dy
<0){
374 * Diamond exit rule test for starting point
376 if (fabsf(x1diff
) + fabsf(y1diff
) < 0.5) {
379 else if (sign(x1diff
) == sign(-dx
)) {
382 else if (sign(-y1diff
) != sign(dy
)) {
386 /* do intersection test */
387 float yintersect
= fracf(v1
[0][1]) + x1diff
* dydx
;
388 draw_start
= (yintersect
< 1.0 && yintersect
> 0.0);
393 * Diamond exit rule test for ending point
395 if (fabsf(x2diff
) + fabsf(y2diff
) < 0.5) {
398 else if (sign(x2diff
) != sign(-dx
)) {
401 else if (sign(-y2diff
) == sign(dy
)) {
405 /* do intersection test */
406 float yintersect
= fracf(v2
[0][1]) + x2diff
* dydx
;
407 draw_end
= (yintersect
< 1.0 && yintersect
> 0.0);
410 /* Are we already drawing start/end?
412 will_draw_start
= sign(-x1diff
) != sign(dx
);
413 will_draw_end
= (sign(x2diff
) == sign(-dx
)) || x2diff
==0;
416 /* if v2 is to the right of v1, swap pointers */
417 const float (*temp
)[4] = v1
;
422 /* Otherwise shift planes appropriately */
423 if (will_draw_start
!= draw_start
) {
424 x_offset_end
= - x1diff
- 0.5;
425 y_offset_end
= x_offset_end
* dydx
;
428 if (will_draw_end
!= draw_end
) {
429 x_offset
= - x2diff
- 0.5;
430 y_offset
= x_offset
* dydx
;
435 /* Otherwise shift planes appropriately */
436 if (will_draw_start
!= draw_start
) {
437 x_offset
= - x1diff
+ 0.5;
438 y_offset
= x_offset
* dydx
;
440 if (will_draw_end
!= draw_end
) {
441 x_offset_end
= - x2diff
+ 0.5;
442 y_offset_end
= x_offset_end
* dydx
;
446 /* x/y positions in fixed point */
447 x
[0] = subpixel_snap(v1
[0][0] + x_offset
- setup
->pixel_offset
);
448 x
[1] = subpixel_snap(v2
[0][0] + x_offset_end
- setup
->pixel_offset
);
449 x
[2] = subpixel_snap(v2
[0][0] + x_offset_end
- setup
->pixel_offset
);
450 x
[3] = subpixel_snap(v1
[0][0] + x_offset
- setup
->pixel_offset
);
452 y
[0] = subpixel_snap(v1
[0][1] + y_offset
- setup
->pixel_offset
) - fixed_width
/2;
453 y
[1] = subpixel_snap(v2
[0][1] + y_offset_end
- setup
->pixel_offset
) - fixed_width
/2;
454 y
[2] = subpixel_snap(v2
[0][1] + y_offset_end
- setup
->pixel_offset
) + fixed_width
/2;
455 y
[3] = subpixel_snap(v1
[0][1] + y_offset
- setup
->pixel_offset
) + fixed_width
/2;
459 const float dxdy
= dx
/ dy
;
462 x1diff
= v1
[0][0] - floorf(v1
[0][0]) - 0.5f
;
463 y1diff
= v1
[0][1] - floorf(v1
[0][1]) - 0.5f
;
464 x2diff
= v2
[0][0] - floorf(v2
[0][0]) - 0.5f
;
465 y2diff
= v2
[0][1] - floorf(v2
[0][1]) - 0.5f
;
467 if (x2diff
==-0.5 && dx
<0) {
472 * Diamond exit rule test for starting point
474 if (fabsf(x1diff
) + fabsf(y1diff
) < 0.5) {
477 else if (sign(-y1diff
) == sign(dy
)) {
480 else if (sign(x1diff
) != sign(-dx
)) {
484 /* do intersection test */
485 float xintersect
= fracf(v1
[0][0]) + y1diff
* dxdy
;
486 draw_start
= (xintersect
< 1.0 && xintersect
> 0.0);
490 * Diamond exit rule test for ending point
492 if (fabsf(x2diff
) + fabsf(y2diff
) < 0.5) {
495 else if (sign(-y2diff
) != sign(dy
) ) {
498 else if (sign(x2diff
) == sign(-dx
) ) {
502 /* do intersection test */
503 float xintersect
= fracf(v2
[0][0]) + y2diff
* dxdy
;
504 draw_end
= (xintersect
< 1.0 && xintersect
>= 0.0);
507 /* Are we already drawing start/end?
509 will_draw_start
= sign(y1diff
) == sign(dy
);
510 will_draw_end
= (sign(-y2diff
) == sign(dy
)) || y2diff
==0;
513 /* if v2 is on top of v1, swap pointers */
514 const float (*temp
)[4] = v1
;
520 /* Otherwise shift planes appropriately */
521 if (will_draw_start
!= draw_start
) {
522 y_offset_end
= - y1diff
+ 0.5;
523 x_offset_end
= y_offset_end
* dxdy
;
525 if (will_draw_end
!= draw_end
) {
526 y_offset
= - y2diff
+ 0.5;
527 x_offset
= y_offset
* dxdy
;
531 /* Otherwise shift planes appropriately */
532 if (will_draw_start
!= draw_start
) {
533 y_offset
= - y1diff
- 0.5;
534 x_offset
= y_offset
* dxdy
;
537 if (will_draw_end
!= draw_end
) {
538 y_offset_end
= - y2diff
- 0.5;
539 x_offset_end
= y_offset_end
* dxdy
;
543 /* x/y positions in fixed point */
544 x
[0] = subpixel_snap(v1
[0][0] + x_offset
- setup
->pixel_offset
) - fixed_width
/2;
545 x
[1] = subpixel_snap(v2
[0][0] + x_offset_end
- setup
->pixel_offset
) - fixed_width
/2;
546 x
[2] = subpixel_snap(v2
[0][0] + x_offset_end
- setup
->pixel_offset
) + fixed_width
/2;
547 x
[3] = subpixel_snap(v1
[0][0] + x_offset
- setup
->pixel_offset
) + fixed_width
/2;
549 y
[0] = subpixel_snap(v1
[0][1] + y_offset
- setup
->pixel_offset
);
550 y
[1] = subpixel_snap(v2
[0][1] + y_offset_end
- setup
->pixel_offset
);
551 y
[2] = subpixel_snap(v2
[0][1] + y_offset_end
- setup
->pixel_offset
);
552 y
[3] = subpixel_snap(v1
[0][1] + y_offset
- setup
->pixel_offset
);
555 /* Bounding rectangle (in pixels) */
557 /* Yes this is necessary to accurately calculate bounding boxes
558 * with the two fill-conventions we support. GL (normally) ends
559 * up needing a bottom-left fill convention, which requires
560 * slightly different rounding.
562 int adj
= (setup
->bottom_edge_rule
!= 0) ? 1 : 0;
564 bbox
.x0
= (MIN4(x
[0], x
[1], x
[2], x
[3]) + (FIXED_ONE
-1)) >> FIXED_ORDER
;
565 bbox
.x1
= (MAX4(x
[0], x
[1], x
[2], x
[3]) + (FIXED_ONE
-1)) >> FIXED_ORDER
;
566 bbox
.y0
= (MIN4(y
[0], y
[1], y
[2], y
[3]) + (FIXED_ONE
-1) + adj
) >> FIXED_ORDER
;
567 bbox
.y1
= (MAX4(y
[0], y
[1], y
[2], y
[3]) + (FIXED_ONE
-1) + adj
) >> FIXED_ORDER
;
569 /* Inclusive coordinates:
575 if (bbox
.x1
< bbox
.x0
||
577 if (0) debug_printf("empty bounding box\n");
578 LP_COUNT(nr_culled_tris
);
582 if (!u_rect_test_intersection(&setup
->draw_regions
[viewport_index
], &bbox
)) {
583 if (0) debug_printf("offscreen\n");
584 LP_COUNT(nr_culled_tris
);
590 /* Can safely discard negative regions:
592 bboxpos
.x0
= MAX2(bboxpos
.x0
, 0);
593 bboxpos
.y0
= MAX2(bboxpos
.y0
, 0);
597 * Determine how many scissor planes we need, that is drop scissor
598 * edges if the bounding box of the tri is fully inside that edge.
600 if (setup
->scissor_test
) {
601 /* why not just use draw_regions */
602 scissor
= &setup
->scissors
[viewport_index
];
603 scissor_planes_needed(s_planes
, &bboxpos
, scissor
);
604 nr_planes
+= s_planes
[0] + s_planes
[1] + s_planes
[2] + s_planes
[3];
607 line
= lp_setup_alloc_triangle(scene
,
615 line
->v
[0][0] = v1
[0][0];
616 line
->v
[1][0] = v2
[0][0];
617 line
->v
[0][1] = v1
[0][1];
618 line
->v
[1][1] = v2
[0][1];
623 /* calculate the deltas */
624 plane
= GET_PLANES(line
);
625 plane
[0].dcdy
= x
[0] - x
[1];
626 plane
[1].dcdy
= x
[1] - x
[2];
627 plane
[2].dcdy
= x
[2] - x
[3];
628 plane
[3].dcdy
= x
[3] - x
[0];
630 plane
[0].dcdx
= y
[0] - y
[1];
631 plane
[1].dcdx
= y
[1] - y
[2];
632 plane
[2].dcdx
= y
[2] - y
[3];
633 plane
[3].dcdx
= y
[3] - y
[0];
635 if (draw_will_inject_frontface(lp_context
->draw
) &&
636 setup
->face_slot
> 0) {
637 line
->inputs
.frontfacing
= v1
[setup
->face_slot
][0];
639 line
->inputs
.frontfacing
= TRUE
;
642 /* Setup parameter interpolants:
644 info
.a0
= GET_A0(&line
->inputs
);
645 info
.dadx
= GET_DADX(&line
->inputs
);
646 info
.dady
= GET_DADY(&line
->inputs
);
647 info
.frontfacing
= line
->inputs
.frontfacing
;
648 setup_line_coefficients(setup
, &info
);
650 line
->inputs
.disable
= FALSE
;
651 line
->inputs
.opaque
= FALSE
;
652 line
->inputs
.layer
= layer
;
653 line
->inputs
.viewport_index
= viewport_index
;
656 * XXX: this code is mostly identical to the one in lp_setup_tri, except it
657 * uses 4 planes instead of 3. Could share the code (including the sse
658 * assembly, in fact we'd get the 4th plane for free).
659 * The only difference apart from storing the 4th plane would be some
660 * different shuffle for calculating dcdx/dcdy.
662 for (i
= 0; i
< 4; i
++) {
664 /* half-edge constants, will be iterated over the whole render
667 plane
[i
].c
= IMUL64(plane
[i
].dcdx
, x
[i
]) - IMUL64(plane
[i
].dcdy
, y
[i
]);
669 /* correct for top-left vs. bottom-left fill convention.
671 if (plane
[i
].dcdx
< 0) {
672 /* both fill conventions want this - adjust for left edges */
675 else if (plane
[i
].dcdx
== 0) {
676 if (setup
->pixel_offset
== 0) {
677 /* correct for top-left fill convention:
679 if (plane
[i
].dcdy
> 0) plane
[i
].c
++;
682 /* correct for bottom-left fill convention:
684 if (plane
[i
].dcdy
< 0) plane
[i
].c
++;
688 plane
[i
].dcdx
*= FIXED_ONE
;
689 plane
[i
].dcdy
*= FIXED_ONE
;
691 /* find trivial reject offsets for each edge for a single-pixel
692 * sized block. These will be scaled up at each recursive level to
693 * match the active blocksize. Scaling in this way works best if
694 * the blocks are square.
697 if (plane
[i
].dcdx
< 0) plane
[i
].eo
-= plane
[i
].dcdx
;
698 if (plane
[i
].dcdy
> 0) plane
[i
].eo
+= plane
[i
].dcdy
;
703 * When rasterizing scissored tris, use the intersection of the
704 * triangle bounding box and the scissor rect to generate the
707 * This permits us to cut off the triangle "tails" that are present
708 * in the intermediate recursive levels caused when two of the
709 * triangles edges don't diverge quickly enough to trivially reject
710 * exterior blocks from the triangle.
712 * It's not really clear if it's worth worrying about these tails,
713 * but since we generate the planes for each scissored tri, it's
714 * free to trim them in this case.
716 * Note that otherwise, the scissor planes only vary in 'C' value,
717 * and even then only on state-changes. Could alternatively store
718 * these planes elsewhere.
719 * (Or only store the c value together with a bit indicating which
720 * scissor edge this is, so rasterization would treat them differently
721 * (easier to evaluate) to ordinary planes.)
724 struct lp_rast_plane
*plane_s
= &plane
[4];
727 plane_s
->dcdx
= ~0U << 8;
729 plane_s
->c
= (1-scissor
->x0
) << 8;
730 plane_s
->eo
= 1 << 8;
734 plane_s
->dcdx
= 1 << 8;
736 plane_s
->c
= (scissor
->x1
+1) << 8;
737 plane_s
->eo
= 0 << 8;
742 plane_s
->dcdy
= 1 << 8;
743 plane_s
->c
= (1-scissor
->y0
) << 8;
744 plane_s
->eo
= 1 << 8;
749 plane_s
->dcdy
= ~0U << 8;
750 plane_s
->c
= (scissor
->y1
+1) << 8;
754 assert(plane_s
== &plane
[nr_planes
]);
757 return lp_setup_bin_triangle(setup
, line
, &bbox
, &bboxpos
, nr_planes
, viewport_index
);
761 static void lp_setup_line_discard(struct lp_setup_context
*setup
,
762 const float (*v0
)[4],
763 const float (*v1
)[4])
767 static void lp_setup_line(struct lp_setup_context
*setup
,
768 const float (*v0
)[4],
769 const float (*v1
)[4])
771 if (!try_setup_line(setup
, v0
, v1
)) {
772 if (!lp_setup_flush_and_restart(setup
))
775 if (!try_setup_line(setup
, v0
, v1
))
781 void lp_setup_choose_line(struct lp_setup_context
*setup
)
783 if (setup
->rasterizer_discard
) {
784 setup
->line
= lp_setup_line_discard
;
786 setup
->line
= lp_setup_line
;