1 /**************************************************************************
3 * Copyright 2007-2018 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 * AA line stage: AA lines are converted triangles (with extra generic)
35 #include "pipe/p_context.h"
36 #include "pipe/p_defines.h"
37 #include "pipe/p_shader_tokens.h"
38 #include "util/u_inlines.h"
40 #include "util/format/u_format.h"
41 #include "util/u_math.h"
42 #include "util/u_memory.h"
44 #include "tgsi/tgsi_transform.h"
45 #include "tgsi/tgsi_dump.h"
47 #include "draw_context.h"
48 #include "draw_private.h"
49 #include "draw_pipe.h"
52 #include "nir/nir_draw_helpers.h"
54 /** Approx number of new tokens for instructions in aa_transform_inst() */
55 #define NUM_NEW_TOKENS 53
59 * Subclass of pipe_shader_state to carry extra fragment shader info.
61 struct aaline_fragment_shader
63 struct pipe_shader_state state
;
66 int generic_attrib
; /**< generic used for distance */
71 * Subclass of draw_stage
75 struct draw_stage stage
;
77 float half_line_width
;
79 /** For AA lines, this is the vertex attrib slot for new generic */
81 /** position, not necessarily output zero */
86 * Currently bound state
88 struct aaline_fragment_shader
*fs
;
91 * Driver interface/override functions
93 void * (*driver_create_fs_state
)(struct pipe_context
*,
94 const struct pipe_shader_state
*);
95 void (*driver_bind_fs_state
)(struct pipe_context
*, void *);
96 void (*driver_delete_fs_state
)(struct pipe_context
*, void *);
102 * Subclass of tgsi_transform_context, used for transforming the
103 * user's fragment shader to add the special AA instructions.
105 struct aa_transform_context
{
106 struct tgsi_transform_context base
;
107 uint64_t tempsUsed
; /**< bitmask */
108 int colorOutput
; /**< which output is the primary color */
109 int maxInput
, maxGeneric
; /**< max input index found */
110 int colorTemp
, aaTemp
; /**< temp registers */
114 * TGSI declaration transform callback.
115 * Look for a free input attrib, and two free temp regs.
118 aa_transform_decl(struct tgsi_transform_context
*ctx
,
119 struct tgsi_full_declaration
*decl
)
121 struct aa_transform_context
*aactx
= (struct aa_transform_context
*)ctx
;
123 if (decl
->Declaration
.File
== TGSI_FILE_OUTPUT
&&
124 decl
->Semantic
.Name
== TGSI_SEMANTIC_COLOR
&&
125 decl
->Semantic
.Index
== 0) {
126 aactx
->colorOutput
= decl
->Range
.First
;
128 else if (decl
->Declaration
.File
== TGSI_FILE_INPUT
) {
129 if ((int) decl
->Range
.Last
> aactx
->maxInput
)
130 aactx
->maxInput
= decl
->Range
.Last
;
131 if (decl
->Semantic
.Name
== TGSI_SEMANTIC_GENERIC
&&
132 (int) decl
->Semantic
.Index
> aactx
->maxGeneric
) {
133 aactx
->maxGeneric
= decl
->Semantic
.Index
;
136 else if (decl
->Declaration
.File
== TGSI_FILE_TEMPORARY
) {
138 for (i
= decl
->Range
.First
;
139 i
<= decl
->Range
.Last
; i
++) {
141 * XXX this bitfield doesn't really cut it...
143 aactx
->tempsUsed
|= UINT64_C(1) << i
;
147 ctx
->emit_declaration(ctx
, decl
);
152 * Find the lowest zero bit, or -1 if bitfield is all ones.
155 free_bit(uint64_t bitfield
)
157 return ffsll(~bitfield
) - 1;
162 * TGSI transform prolog callback.
165 aa_transform_prolog(struct tgsi_transform_context
*ctx
)
167 struct aa_transform_context
*aactx
= (struct aa_transform_context
*) ctx
;
168 uint64_t usedTemps
= aactx
->tempsUsed
;
170 /* find two free temp regs */
171 aactx
->colorTemp
= free_bit(usedTemps
);
172 usedTemps
|= UINT64_C(1) << aactx
->colorTemp
;
173 aactx
->aaTemp
= free_bit(usedTemps
);
174 assert(aactx
->colorTemp
>= 0);
175 assert(aactx
->aaTemp
>= 0);
177 /* declare new generic input/texcoord */
178 tgsi_transform_input_decl(ctx
, aactx
->maxInput
+ 1,
179 TGSI_SEMANTIC_GENERIC
, aactx
->maxGeneric
+ 1,
180 TGSI_INTERPOLATE_LINEAR
);
182 /* declare new temp regs */
183 tgsi_transform_temp_decl(ctx
, aactx
->aaTemp
);
184 tgsi_transform_temp_decl(ctx
, aactx
->colorTemp
);
189 * TGSI transform epilog callback.
192 aa_transform_epilog(struct tgsi_transform_context
*ctx
)
194 struct aa_transform_context
*aactx
= (struct aa_transform_context
*) ctx
;
196 if (aactx
->colorOutput
!= -1) {
197 struct tgsi_full_instruction inst
;
198 /* insert distance-based coverage code for antialiasing. */
200 /* saturate(linewidth - fabs(interpx), linelength - fabs(interpz) */
201 inst
= tgsi_default_full_instruction();
202 inst
.Instruction
.Saturate
= true;
203 inst
.Instruction
.Opcode
= TGSI_OPCODE_ADD
;
204 inst
.Instruction
.NumDstRegs
= 1;
205 tgsi_transform_dst_reg(&inst
.Dst
[0], TGSI_FILE_TEMPORARY
,
206 aactx
->aaTemp
, TGSI_WRITEMASK_XZ
);
207 inst
.Instruction
.NumSrcRegs
= 2;
208 tgsi_transform_src_reg(&inst
.Src
[1], TGSI_FILE_INPUT
, aactx
->maxInput
+ 1,
209 TGSI_SWIZZLE_X
, TGSI_SWIZZLE_X
,
210 TGSI_SWIZZLE_Z
, TGSI_SWIZZLE_Z
);
211 tgsi_transform_src_reg(&inst
.Src
[0], TGSI_FILE_INPUT
, aactx
->maxInput
+ 1,
212 TGSI_SWIZZLE_Y
, TGSI_SWIZZLE_Y
,
213 TGSI_SWIZZLE_W
, TGSI_SWIZZLE_W
);
214 inst
.Src
[1].Register
.Absolute
= true;
215 inst
.Src
[1].Register
.Negate
= true;
216 ctx
->emit_instruction(ctx
, &inst
);
218 /* MUL width / height alpha */
219 tgsi_transform_op2_swz_inst(ctx
, TGSI_OPCODE_MUL
,
220 TGSI_FILE_TEMPORARY
, aactx
->aaTemp
,
222 TGSI_FILE_TEMPORARY
, aactx
->aaTemp
,
224 TGSI_FILE_TEMPORARY
, aactx
->aaTemp
,
225 TGSI_SWIZZLE_Z
, false);
228 tgsi_transform_op1_inst(ctx
, TGSI_OPCODE_MOV
,
229 TGSI_FILE_OUTPUT
, aactx
->colorOutput
,
231 TGSI_FILE_TEMPORARY
, aactx
->colorTemp
);
234 tgsi_transform_op2_inst(ctx
, TGSI_OPCODE_MUL
,
235 TGSI_FILE_OUTPUT
, aactx
->colorOutput
,
237 TGSI_FILE_TEMPORARY
, aactx
->colorTemp
,
238 TGSI_FILE_TEMPORARY
, aactx
->aaTemp
, false);
244 * TGSI instruction transform callback.
245 * Replace writes to result.color w/ a temp reg.
248 aa_transform_inst(struct tgsi_transform_context
*ctx
,
249 struct tgsi_full_instruction
*inst
)
251 struct aa_transform_context
*aactx
= (struct aa_transform_context
*) ctx
;
255 * Look for writes to result.color and replace with colorTemp reg.
257 for (i
= 0; i
< inst
->Instruction
.NumDstRegs
; i
++) {
258 struct tgsi_full_dst_register
*dst
= &inst
->Dst
[i
];
259 if (dst
->Register
.File
== TGSI_FILE_OUTPUT
&&
260 dst
->Register
.Index
== aactx
->colorOutput
) {
261 dst
->Register
.File
= TGSI_FILE_TEMPORARY
;
262 dst
->Register
.Index
= aactx
->colorTemp
;
266 ctx
->emit_instruction(ctx
, inst
);
271 * Generate the frag shader we'll use for drawing AA lines.
272 * This will be the user's shader plus some arithmetic instructions.
275 generate_aaline_fs(struct aaline_stage
*aaline
)
277 struct pipe_context
*pipe
= aaline
->stage
.draw
->pipe
;
278 const struct pipe_shader_state
*orig_fs
= &aaline
->fs
->state
;
279 struct pipe_shader_state aaline_fs
;
280 struct aa_transform_context transform
;
281 const uint newLen
= tgsi_num_tokens(orig_fs
->tokens
) + NUM_NEW_TOKENS
;
283 aaline_fs
= *orig_fs
; /* copy to init */
284 aaline_fs
.tokens
= tgsi_alloc_tokens(newLen
);
285 if (aaline_fs
.tokens
== NULL
)
288 memset(&transform
, 0, sizeof(transform
));
289 transform
.colorOutput
= -1;
290 transform
.maxInput
= -1;
291 transform
.maxGeneric
= -1;
292 transform
.colorTemp
= -1;
293 transform
.aaTemp
= -1;
294 transform
.base
.prolog
= aa_transform_prolog
;
295 transform
.base
.epilog
= aa_transform_epilog
;
296 transform
.base
.transform_instruction
= aa_transform_inst
;
297 transform
.base
.transform_declaration
= aa_transform_decl
;
299 tgsi_transform_shader(orig_fs
->tokens
,
300 (struct tgsi_token
*) aaline_fs
.tokens
,
301 newLen
, &transform
.base
);
304 debug_printf("draw_aaline, orig shader:\n");
305 tgsi_dump(orig_fs
->tokens
, 0);
306 debug_printf("draw_aaline, new shader:\n");
307 tgsi_dump(aaline_fs
.tokens
, 0);
310 aaline
->fs
->aaline_fs
= aaline
->driver_create_fs_state(pipe
, &aaline_fs
);
311 if (aaline
->fs
->aaline_fs
== NULL
)
314 aaline
->fs
->generic_attrib
= transform
.maxGeneric
+ 1;
315 FREE((void *)aaline_fs
.tokens
);
319 FREE((void *)aaline_fs
.tokens
);
324 generate_aaline_fs_nir(struct aaline_stage
*aaline
)
326 #ifdef LLVM_AVAILABLE
327 struct pipe_context
*pipe
= aaline
->stage
.draw
->pipe
;
328 const struct pipe_shader_state
*orig_fs
= &aaline
->fs
->state
;
329 struct pipe_shader_state aaline_fs
;
331 aaline_fs
= *orig_fs
; /* copy to init */
332 aaline_fs
.ir
.nir
= nir_shader_clone(NULL
, orig_fs
->ir
.nir
);
333 if (!aaline_fs
.ir
.nir
)
336 nir_lower_aaline_fs(aaline_fs
.ir
.nir
, &aaline
->fs
->generic_attrib
);
337 aaline
->fs
->aaline_fs
= aaline
->driver_create_fs_state(pipe
, &aaline_fs
);
338 if (aaline
->fs
->aaline_fs
== NULL
)
349 * When we're about to draw our first AA line in a batch, this function is
350 * called to tell the driver to bind our modified fragment shader.
353 bind_aaline_fragment_shader(struct aaline_stage
*aaline
)
355 struct draw_context
*draw
= aaline
->stage
.draw
;
356 struct pipe_context
*pipe
= draw
->pipe
;
358 if (!aaline
->fs
->aaline_fs
) {
359 if (aaline
->fs
->state
.type
== PIPE_SHADER_IR_NIR
) {
360 if (!generate_aaline_fs_nir(aaline
))
363 if (!generate_aaline_fs(aaline
))
367 draw
->suspend_flushing
= TRUE
;
368 aaline
->driver_bind_fs_state(pipe
, aaline
->fs
->aaline_fs
);
369 draw
->suspend_flushing
= FALSE
;
376 static inline struct aaline_stage
*
377 aaline_stage(struct draw_stage
*stage
)
379 return (struct aaline_stage
*) stage
;
384 * Draw a wide line by drawing a quad, using geometry which will
385 * fullfill GL's antialiased line requirements.
388 aaline_line(struct draw_stage
*stage
, struct prim_header
*header
)
390 const struct aaline_stage
*aaline
= aaline_stage(stage
);
391 const float half_width
= aaline
->half_line_width
;
392 struct prim_header tri
;
393 struct vertex_header
*v
[8];
394 uint coordPos
= aaline
->coord_slot
;
395 uint posPos
= aaline
->pos_slot
;
397 float dx
= header
->v
[1]->data
[posPos
][0] - header
->v
[0]->data
[posPos
][0];
398 float dy
= header
->v
[1]->data
[posPos
][1] - header
->v
[0]->data
[posPos
][1];
399 float a
= atan2f(dy
, dx
);
400 float c_a
= cosf(a
), s_a
= sinf(a
);
405 half_length
= 0.5f
* sqrtf(dx
* dx
+ dy
* dy
);
407 if (half_length
< 0.5f
) {
409 * The logic we use for "normal" sized segments is incorrect
410 * for very short segments (basically because we only have
411 * one value to interpolate, not a distance to each endpoint).
412 * Therefore, we calculate half_length differently, so that for
413 * original line length (near) 0, we get alpha 0 - otherwise
414 * max alpha would still be 0.5. This also prevents us from
415 * artifacts due to degenerated lines (the endpoints being
416 * identical, which would still receive anywhere from alpha
417 * 0-0.5 otherwise) (at least the pstipple stage may generate
418 * such lines due to float inaccuracies if line length is very
419 * close to a integer).
420 * Might not be fully accurate neither (because the "strength" of
421 * the line is going to be determined by how close to the pixel
422 * center those 1 or 2 fragments are) but it's probably the best
425 half_length
= 2.0f
* half_length
;
427 half_length
= half_length
+ 0.5f
;
433 /* allocate/dup new verts */
434 for (i
= 0; i
< 4; i
++) {
435 v
[i
] = dup_vert(stage
, header
->v
[i
/2], i
);
439 * Quad strip for line from v0 to v1 (*=endpoints):
442 * +-----------------------------+
446 * +-----------------------------+
451 * We increase line length by 0.5 pixels (at each endpoint),
452 * and calculate the tri endpoints by moving them half-width
453 * distance away perpendicular to the line.
454 * XXX: since we change line endpoints (by 0.5 pixel), should
455 * actually re-interpolate all other values?
459 pos
= v
[0]->data
[posPos
];
460 pos
[0] += (-t_l
* c_a
- t_w
* s_a
);
461 pos
[1] += (-t_l
* s_a
+ t_w
* c_a
);
463 pos
= v
[1]->data
[posPos
];
464 pos
[0] += (-t_l
* c_a
- -t_w
* s_a
);
465 pos
[1] += (-t_l
* s_a
+ -t_w
* c_a
);
467 pos
= v
[2]->data
[posPos
];
468 pos
[0] += (t_l
* c_a
- t_w
* s_a
);
469 pos
[1] += (t_l
* s_a
+ t_w
* c_a
);
471 pos
= v
[3]->data
[posPos
];
472 pos
[0] += (t_l
* c_a
- -t_w
* s_a
);
473 pos
[1] += (t_l
* s_a
+ -t_w
* c_a
);
476 tex
= v
[0]->data
[coordPos
];
477 ASSIGN_4V(tex
, -half_width
, half_width
, -half_length
, half_length
);
479 tex
= v
[1]->data
[coordPos
];
480 ASSIGN_4V(tex
, half_width
, half_width
, -half_length
, half_length
);
482 tex
= v
[2]->data
[coordPos
];
483 ASSIGN_4V(tex
, -half_width
, half_width
, half_length
, half_length
);
485 tex
= v
[3]->data
[coordPos
];
486 ASSIGN_4V(tex
, half_width
, half_width
, half_length
, half_length
);
488 tri
.v
[0] = v
[2]; tri
.v
[1] = v
[1]; tri
.v
[2] = v
[0];
489 stage
->next
->tri(stage
->next
, &tri
);
491 tri
.v
[0] = v
[3]; tri
.v
[1] = v
[1]; tri
.v
[2] = v
[2];
492 stage
->next
->tri(stage
->next
, &tri
);
497 aaline_first_line(struct draw_stage
*stage
, struct prim_header
*header
)
499 auto struct aaline_stage
*aaline
= aaline_stage(stage
);
500 struct draw_context
*draw
= stage
->draw
;
501 struct pipe_context
*pipe
= draw
->pipe
;
502 const struct pipe_rasterizer_state
*rast
= draw
->rasterizer
;
505 assert(draw
->rasterizer
->line_smooth
&& !draw
->rasterizer
->multisample
);
507 if (draw
->rasterizer
->line_width
<= 1.0)
508 aaline
->half_line_width
= 1.0;
510 aaline
->half_line_width
= 0.5f
* draw
->rasterizer
->line_width
+ 0.5f
;
512 if (!draw
->rasterizer
->half_pixel_center
)
514 * The tex coords probably would need adjustments?
516 debug_printf("aa lines without half pixel center may be wrong\n");
519 * Bind (generate) our fragprog
521 if (!bind_aaline_fragment_shader(aaline
)) {
522 stage
->line
= draw_pipe_passthrough_line
;
523 stage
->line(stage
, header
);
527 draw_aaline_prepare_outputs(draw
, draw
->pipeline
.aaline
);
529 draw
->suspend_flushing
= TRUE
;
531 /* Disable triangle culling, stippling, unfilled mode etc. */
532 r
= draw_get_rasterizer_no_cull(draw
, rast
->scissor
, rast
->flatshade
);
533 pipe
->bind_rasterizer_state(pipe
, r
);
535 draw
->suspend_flushing
= FALSE
;
537 /* now really draw first line */
538 stage
->line
= aaline_line
;
539 stage
->line(stage
, header
);
544 aaline_flush(struct draw_stage
*stage
, unsigned flags
)
546 struct draw_context
*draw
= stage
->draw
;
547 struct aaline_stage
*aaline
= aaline_stage(stage
);
548 struct pipe_context
*pipe
= draw
->pipe
;
550 stage
->line
= aaline_first_line
;
551 stage
->next
->flush(stage
->next
, flags
);
553 /* restore original frag shader */
554 draw
->suspend_flushing
= TRUE
;
555 aaline
->driver_bind_fs_state(pipe
, aaline
->fs
? aaline
->fs
->driver_fs
: NULL
);
557 /* restore original rasterizer state */
558 if (draw
->rast_handle
) {
559 pipe
->bind_rasterizer_state(pipe
, draw
->rast_handle
);
562 draw
->suspend_flushing
= FALSE
;
564 draw_remove_extra_vertex_attribs(draw
);
569 aaline_reset_stipple_counter(struct draw_stage
*stage
)
571 stage
->next
->reset_stipple_counter(stage
->next
);
576 aaline_destroy(struct draw_stage
*stage
)
578 struct aaline_stage
*aaline
= aaline_stage(stage
);
579 struct pipe_context
*pipe
= stage
->draw
->pipe
;
581 draw_free_temp_verts(stage
);
583 /* restore the old entry points */
584 pipe
->create_fs_state
= aaline
->driver_create_fs_state
;
585 pipe
->bind_fs_state
= aaline
->driver_bind_fs_state
;
586 pipe
->delete_fs_state
= aaline
->driver_delete_fs_state
;
592 static struct aaline_stage
*
593 draw_aaline_stage(struct draw_context
*draw
)
595 struct aaline_stage
*aaline
= CALLOC_STRUCT(aaline_stage
);
599 aaline
->stage
.draw
= draw
;
600 aaline
->stage
.name
= "aaline";
601 aaline
->stage
.next
= NULL
;
602 aaline
->stage
.point
= draw_pipe_passthrough_point
;
603 aaline
->stage
.line
= aaline_first_line
;
604 aaline
->stage
.tri
= draw_pipe_passthrough_tri
;
605 aaline
->stage
.flush
= aaline_flush
;
606 aaline
->stage
.reset_stipple_counter
= aaline_reset_stipple_counter
;
607 aaline
->stage
.destroy
= aaline_destroy
;
609 if (!draw_alloc_temp_verts(&aaline
->stage
, 8))
615 aaline
->stage
.destroy(&aaline
->stage
);
621 static struct aaline_stage
*
622 aaline_stage_from_pipe(struct pipe_context
*pipe
)
624 struct draw_context
*draw
= (struct draw_context
*) pipe
->draw
;
627 return aaline_stage(draw
->pipeline
.aaline
);
635 * This function overrides the driver's create_fs_state() function and
636 * will typically be called by the gallium frontend.
639 aaline_create_fs_state(struct pipe_context
*pipe
,
640 const struct pipe_shader_state
*fs
)
642 struct aaline_stage
*aaline
= aaline_stage_from_pipe(pipe
);
643 struct aaline_fragment_shader
*aafs
= NULL
;
648 aafs
= CALLOC_STRUCT(aaline_fragment_shader
);
653 aafs
->state
.type
= fs
->type
;
654 if (fs
->type
== PIPE_SHADER_IR_TGSI
)
655 aafs
->state
.tokens
= tgsi_dup_tokens(fs
->tokens
);
656 #ifdef LLVM_AVAILABLE
658 aafs
->state
.ir
.nir
= nir_shader_clone(NULL
, fs
->ir
.nir
);
662 aafs
->driver_fs
= aaline
->driver_create_fs_state(pipe
, fs
);
669 aaline_bind_fs_state(struct pipe_context
*pipe
, void *fs
)
671 struct aaline_stage
*aaline
= aaline_stage_from_pipe(pipe
);
672 struct aaline_fragment_shader
*aafs
= (struct aaline_fragment_shader
*) fs
;
681 aaline
->driver_bind_fs_state(pipe
, (aafs
? aafs
->driver_fs
: NULL
));
686 aaline_delete_fs_state(struct pipe_context
*pipe
, void *fs
)
688 struct aaline_stage
*aaline
= aaline_stage_from_pipe(pipe
);
689 struct aaline_fragment_shader
*aafs
= (struct aaline_fragment_shader
*) fs
;
697 aaline
->driver_delete_fs_state(pipe
, aafs
->driver_fs
);
700 aaline
->driver_delete_fs_state(pipe
, aafs
->aaline_fs
);
703 if (aafs
->state
.type
== PIPE_SHADER_IR_TGSI
)
704 FREE((void*)aafs
->state
.tokens
);
706 ralloc_free(aafs
->state
.ir
.nir
);
712 draw_aaline_prepare_outputs(struct draw_context
*draw
,
713 struct draw_stage
*stage
)
715 struct aaline_stage
*aaline
= aaline_stage(stage
);
716 const struct pipe_rasterizer_state
*rast
= draw
->rasterizer
;
718 /* update vertex attrib info */
719 aaline
->pos_slot
= draw_current_shader_position_output(draw
);
721 if (!rast
->line_smooth
|| rast
->multisample
)
724 /* allocate the extra post-transformed vertex attribute */
725 if (aaline
->fs
&& aaline
->fs
->aaline_fs
)
726 aaline
->coord_slot
= draw_alloc_extra_vertex_attrib(draw
,
727 TGSI_SEMANTIC_GENERIC
,
728 aaline
->fs
->generic_attrib
);
730 aaline
->coord_slot
= -1;
734 * Called by drivers that want to install this AA line prim stage
735 * into the draw module's pipeline. This will not be used if the
736 * hardware has native support for AA lines.
739 draw_install_aaline_stage(struct draw_context
*draw
, struct pipe_context
*pipe
)
741 struct aaline_stage
*aaline
;
743 pipe
->draw
= (void *) draw
;
746 * Create / install AA line drawing / prim stage
748 aaline
= draw_aaline_stage(draw
);
752 /* save original driver functions */
753 aaline
->driver_create_fs_state
= pipe
->create_fs_state
;
754 aaline
->driver_bind_fs_state
= pipe
->bind_fs_state
;
755 aaline
->driver_delete_fs_state
= pipe
->delete_fs_state
;
757 /* override the driver's functions */
758 pipe
->create_fs_state
= aaline_create_fs_state
;
759 pipe
->bind_fs_state
= aaline_bind_fs_state
;
760 pipe
->delete_fs_state
= aaline_delete_fs_state
;
762 /* Install once everything is known to be OK:
764 draw
->pipeline
.aaline
= &aaline
->stage
;
770 aaline
->stage
.destroy(&aaline
->stage
);