Unify rendering of textured and non-textured primitives.
[mesa.git] / src / mesa / drivers / dri / r300 / r300_render.c
1 /**************************************************************************
2
3 Copyright (C) 2004 Nicolai Haehnle.
4
5 All Rights Reserved.
6
7 Permission is hereby granted, free of charge, to any person obtaining a
8 copy of this software and associated documentation files (the "Software"),
9 to deal in the Software without restriction, including without limitation
10 on the rights to use, copy, modify, merge, publish, distribute, sub
11 license, and/or sell copies of the Software, and to permit persons to whom
12 the Software is furnished to do so, subject to the following conditions:
13
14 The above copyright notice and this permission notice (including the next
15 paragraph) shall be included in all copies or substantial portions of the
16 Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21 ATI, VA LINUX SYSTEMS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
22 DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24 USE OR OTHER DEALINGS IN THE SOFTWARE.
25
26 **************************************************************************/
27
28 /*
29 * Authors:
30 * Nicolai Haehnle <prefect_@gmx.net>
31 */
32
33 #include "glheader.h"
34 #include "state.h"
35 #include "imports.h"
36 #include "enums.h"
37 #include "macros.h"
38 #include "context.h"
39 #include "dd.h"
40 #include "simple_list.h"
41
42 #include "api_arrayelt.h"
43 #include "swrast/swrast.h"
44 #include "swrast_setup/swrast_setup.h"
45 #include "array_cache/acache.h"
46 #include "tnl/tnl.h"
47
48 #include "radeon_reg.h"
49 #include "radeon_macros.h"
50 #include "radeon_ioctl.h"
51 #include "radeon_state.h"
52 #include "r300_context.h"
53 #include "r300_ioctl.h"
54 #include "r300_state.h"
55 #include "r300_reg.h"
56 #include "r300_program.h"
57 #include "r300_tex.h"
58
59 #include "r300_lib.h"
60
61
62 /**********************************************************************
63 * Hardware rasterization
64 *
65 * When we fell back to software TCL, we still try to use the
66 * rasterization hardware for rendering.
67 **********************************************************************/
68
69 static int r300_get_primitive_type(r300ContextPtr rmesa,
70 GLcontext *ctx,
71 int start,
72 int end,
73 int prim)
74 {
75 TNLcontext *tnl = TNL_CONTEXT(ctx);
76 struct vertex_buffer *VB = &tnl->vb;
77 GLuint i;
78 int type=-1, min_vertices=0;
79 char *name="UNKNOWN";
80
81 if(end<=start)return -1; /* do we need to watch for this ? */
82
83 switch (prim & PRIM_MODE_MASK) {
84 case GL_POINTS:
85 name="P";
86 type=R300_VAP_VF_CNTL__PRIM_POINTS;
87 min_vertices=1;
88 break;
89 case GL_LINES:
90 name="L";
91 type=R300_VAP_VF_CNTL__PRIM_LINES;
92 min_vertices=2;
93 break;
94 case GL_LINE_STRIP:
95 name="LS";
96 type=R300_VAP_VF_CNTL__PRIM_LINE_STRIP;
97 min_vertices=2;
98 break;
99 case GL_LINE_LOOP:
100 name="LL";
101 min_vertices=2;
102 return -1;
103 break;
104 case GL_TRIANGLES:
105 name="T";
106 type=R300_VAP_VF_CNTL__PRIM_TRIANGLES;
107 min_vertices=3;
108 break;
109 case GL_TRIANGLE_STRIP:
110 name="TS";
111 type=R300_VAP_VF_CNTL__PRIM_TRIANGLE_STRIP;
112 min_vertices=3;
113 break;
114 case GL_TRIANGLE_FAN:
115 name="TF";
116 type=R300_VAP_VF_CNTL__PRIM_TRIANGLE_FAN;
117 min_vertices=3;
118 break;
119 case GL_QUADS:
120 name="Q";
121 type=R300_VAP_VF_CNTL__PRIM_QUADS;
122 min_vertices=4;
123 break;
124 case GL_QUAD_STRIP:
125 name="QS";
126 type=R300_VAP_VF_CNTL__PRIM_QUAD_STRIP;
127 min_vertices=4;
128 break;
129 default:
130 fprintf(stderr, "Cannot handle primitive %02x ", prim & PRIM_MODE_MASK);
131 return -1;
132 break;
133 }
134 #if 0
135 fprintf(stderr, "[%d-%d]%s ", start, end, name);
136 #endif
137 if(start+min_vertices>=end){
138 fprintf(stderr, "Not enough vertices\n");
139 return -1;
140 }
141 return type;
142 }
143
144 /* This function compiles GL context into state registers that
145 describe data routing inside of R300 pipeline.
146
147 In particular, it programs input_route, output_vtx_fmt, texture
148 unit configuration and gb_output_vtx_fmt
149
150 This function encompasses setup_AOS() from r300_lib.c
151 */
152
153
154
155
156 /* Immediate implementation - vertex data is sent via command stream */
157
158 static GLfloat default_vector[4]={0.0, 0.0, 0.0, 1.0};
159
160 #define output_vector(v, i) \
161 { \
162 int _i; \
163 for(_i=0;_i<v->size;_i++){ \
164 efloat(VEC_ELT(v, GLfloat, i)[_i]); \
165 } \
166 for(_i=v->size;_i<4;_i++){ \
167 efloat(default_vector[_i]); \
168 } \
169 }
170
171 /* Immediate implementation - vertex data is sent via command stream */
172
173 static void r300_render_immediate_primitive(r300ContextPtr rmesa,
174 GLcontext *ctx,
175 int start,
176 int end,
177 int prim)
178 {
179 TNLcontext *tnl = TNL_CONTEXT(ctx);
180 struct vertex_buffer *VB = &tnl->vb;
181 GLuint i;
182 int k, type;
183 LOCAL_VARS
184
185 type=r300_get_primitive_type(rmesa, ctx, start, end, prim);
186
187 #if 0
188 fprintf(stderr,"ObjPtr: size=%d stride=%d\n",
189 VB->ObjPtr->size, VB->ObjPtr->stride);
190 fprintf(stderr,"ColorPtr[0]: size=%d stride=%d\n",
191 VB->ColorPtr[0]->size, VB->ColorPtr[0]->stride);
192 fprintf(stderr,"TexCoordPtr[0]: size=%d stride=%d\n",
193 VB->TexCoordPtr[0]->size, VB->TexCoordPtr[0]->stride);
194 #endif
195
196 if(type<0)return;
197
198
199 start_immediate_packet(end-start, type, 8+4*rmesa->state.texture.tc_count);
200
201 for(i=start;i<end;i++){
202 #if 0
203 fprintf(stderr, "* (%f %f %f %f) (%f %f %f %f)\n",
204 VEC_ELT(VB->ObjPtr, GLfloat, i)[0],
205 VEC_ELT(VB->ObjPtr, GLfloat, i)[1],
206 VEC_ELT(VB->ObjPtr, GLfloat, i)[2],
207 VEC_ELT(VB->ObjPtr, GLfloat, i)[3],
208
209 VEC_ELT(VB->ColorPtr[0], GLfloat, i)[0],
210 VEC_ELT(VB->ColorPtr[0], GLfloat, i)[1],
211 VEC_ELT(VB->ColorPtr[0], GLfloat, i)[2],
212 VEC_ELT(VB->ColorPtr[0], GLfloat, i)[3]
213 );
214 #endif
215
216
217 /* coordinates */
218 output_vector(VB->ObjPtr, i);
219
220 /* color components */
221 output_vector(VB->ColorPtr[0], i);
222
223 /* texture coordinates */
224 for(k=0;k < ctx->Const.MaxTextureUnits;k++)
225 if(ctx->Texture.Unit[k].Enabled)
226 output_vector(VB->TexCoordPtr[k], i);
227 }
228
229 }
230
231
232 static void assign_pipeline(r300ContextPtr rmesa, R300_PIPELINE *p)
233 {
234 /* Watch out ! This is buggy .. but will do for now */
235
236 /* At least one sanity check is in order */
237 if(sizeof(rmesa->state.vertex_shader) != sizeof(p->vertex_shader)){
238 fprintf(stderr, "Aieee ! vertex_shader sizes don't match.\n");
239 exit(-1);
240 }
241 if(sizeof(rmesa->state.pixel_shader) != sizeof(p->pixel_shader)){
242 fprintf(stderr, "Aieee ! vertex_shader sizes don't match.\n");
243 exit(-1);
244 }
245
246 memcpy(&rmesa->state.vertex_shader, &(p->vertex_shader), sizeof(rmesa->state.vertex_shader));
247 memcpy(&rmesa->state.pixel_shader, &(p->pixel_shader), sizeof(rmesa->state.pixel_shader));
248
249 }
250
251 static GLboolean r300_run_immediate_render(GLcontext *ctx,
252 struct tnl_pipeline_stage *stage)
253 {
254 r300ContextPtr rmesa = R300_CONTEXT(ctx);
255 TNLcontext *tnl = TNL_CONTEXT(ctx);
256 struct vertex_buffer *VB = &tnl->vb;
257 GLuint i;
258 /* Only do 2d textures */
259 struct gl_texture_object *to=ctx->Texture.Unit[0].Current2D;
260 r300TexObjPtr t=to->DriverData;
261 LOCAL_VARS
262
263
264 /* Update texture state - needs to be done only when actually changed..
265 All the time for now.. */
266 /* Flush state - make sure command buffer is nice and large */
267 r300Flush(ctx);
268
269
270 if (RADEON_DEBUG == DEBUG_PRIMS)
271 fprintf(stderr, "%s\n", __FUNCTION__);
272
273
274 /* needed before starting 3d operation .. */
275 reg_start(R300_RB3D_DSTCACHE_CTLSTAT,0);
276 e32(0x0000000a);
277
278 reg_start(0x4f18,0);
279 e32(0x00000003);
280
281
282 rmesa->hw.vte.cmd[1] = R300_VPORT_X_SCALE_ENA
283 | R300_VPORT_X_OFFSET_ENA
284 | R300_VPORT_Y_SCALE_ENA
285 | R300_VPORT_Y_OFFSET_ENA
286 | R300_VTX_W0_FMT;
287 R300_STATECHANGE(rmesa, vte);
288
289 r300EmitState(rmesa);
290
291 /* Magic register - note it is right after 20b0 */
292
293 if(rmesa->state.texture.tc_count>0){
294 reg_start(0x20b4,0);
295 e32(0x0000000c);
296
297 assign_pipeline(rmesa, &SINGLE_TEXTURE_PIPELINE);
298 } else {
299 assign_pipeline(rmesa, &FLAT_COLOR_PIPELINE);
300 }
301
302 rmesa->state.vertex_shader.matrix[0].length=16;
303 memcpy(rmesa->state.vertex_shader.matrix[0].body.f, ctx->_ModelProjectMatrix.m, 16*4);
304
305 rmesa->state.vertex_shader.unknown2.length=4;
306 rmesa->state.vertex_shader.unknown2.body.f[0]=0.0;
307 rmesa->state.vertex_shader.unknown2.body.f[1]=0.0;
308 rmesa->state.vertex_shader.unknown2.body.f[2]=1.0;
309 rmesa->state.vertex_shader.unknown2.body.f[3]=0.0;
310
311
312 r300EmitVertexShader(rmesa);
313 r300EmitPixelShader(rmesa);
314
315 /* We need LOAD_VBPNTR to setup AOS_ATTR fields.. the offsets are irrelevant */
316 r300EmitLOAD_VBPNTR(rmesa, 0);
317
318 for(i=0; i < VB->PrimitiveCount; i++){
319 GLuint prim = VB->Primitive[i].mode;
320 GLuint start = VB->Primitive[i].start;
321 GLuint length = VB->Primitive[i].count;
322 r300_render_immediate_primitive(rmesa, ctx, start, start + length, prim);
323 }
324
325 /* This sequence is required after any 3d drawing packet
326 I suspect it work arounds a bug (or deficiency) in hardware */
327
328 reg_start(R300_RB3D_DSTCACHE_CTLSTAT,0);
329 e32(0x0000000a);
330
331 reg_start(0x4f18,0);
332 e32(0x00000003);
333
334 return GL_FALSE;
335 }
336
337
338 /* vertex buffer implementation */
339
340 /* We use the start part of GART texture buffer for vertices */
341
342
343 static void upload_vertex_buffer(r300ContextPtr rmesa,
344 GLcontext *ctx, AOS_DATA *array, int *n_arrays)
345 {
346 TNLcontext *tnl = TNL_CONTEXT(ctx);
347 struct vertex_buffer *VB = &tnl->vb;
348 int offset=0, idx=0;
349 int i,j;
350 radeonScreenPtr rsp=rmesa->radeon.radeonScreen;
351 /* Not the most efficient implementation, but, for now, I just want something that
352 works */
353 /* to do - make single memcpy per column (is it possible ?) */
354 /* to do - use dirty flags to avoid redundant copies */
355 #define UPLOAD_VECTOR(v, r, f)\
356 { \
357 /* Is the data dirty ? */ \
358 if (v->flags & ((1<<v->size)-1)) { \
359 fprintf(stderr, "size=%d vs stride=%d\n", v->size, v->stride); \
360 if(v->size*4==v->stride){\
361 /* fast path */ \
362 memcpy(rsp->gartTextures.map+offset, v->data, v->stride*VB->Count); \
363 } else { \
364 for(i=0;i<VB->Count;i++){ \
365 /* copy one vertex at a time*/ \
366 memcpy(rsp->gartTextures.map+offset+i*v->size*4, VEC_ELT(v, GLfloat, i), v->size*4); \
367 } \
368 } \
369 /* v->flags &= ~((1<<v->size)-1);*/ \
370 } \
371 array[idx].element_size=v->size; \
372 array[idx].stride=v->size; \
373 array[idx].format=(f); \
374 array[idx].ncomponents=v->size; \
375 array[idx].offset=rsp->gartTextures.handle+offset; \
376 array[idx].reg=r; \
377 offset+=v->size*4*VB->Count; \
378 idx++; \
379 }
380
381 UPLOAD_VECTOR(VB->ObjPtr, REG_COORDS, AOS_FORMAT_FLOAT);
382 UPLOAD_VECTOR(VB->ColorPtr[0], REG_COLOR0, AOS_FORMAT_FLOAT_COLOR);
383
384 *n_arrays=idx;
385 if(idx>=R300_MAX_AOS_ARRAYS){
386 fprintf(stderr, "Aieee ! Maximum AOS arrays count exceeded.. \n");
387 exit(-1);
388 }
389 }
390
391 static void r300_render_vb_flat_primitive(r300ContextPtr rmesa,
392 GLcontext *ctx,
393 int start,
394 int end,
395 int prim)
396 {
397 TNLcontext *tnl = TNL_CONTEXT(ctx);
398 struct vertex_buffer *VB = &tnl->vb;
399 GLuint i;
400 int k, type, n_arrays;
401 LOCAL_VARS
402
403 if(end<=start)return; /* do we need to watch for this ? */
404
405 type=r300_get_primitive_type(rmesa, ctx, start, end, prim);
406 if(type<0)return;
407
408 fire_AOS(PASS_PREFIX end-start, type);
409 }
410
411 static GLboolean r300_run_vb_flat_render(GLcontext *ctx,
412 struct tnl_pipeline_stage *stage)
413 {
414 r300ContextPtr rmesa = R300_CONTEXT(ctx);
415 TNLcontext *tnl = TNL_CONTEXT(ctx);
416 struct vertex_buffer *VB = &tnl->vb;
417 int i, j, n_arrays;
418 AOS_DATA vb_arrays[R300_MAX_AOS_ARRAYS];
419 AOS_DATA vb_arrays2[R300_MAX_AOS_ARRAYS];
420 LOCAL_VARS
421
422 if (RADEON_DEBUG == DEBUG_PRIMS)
423 fprintf(stderr, "%s\n", __FUNCTION__);
424
425 /* setup array of structures data */
426
427 upload_vertex_buffer(rmesa, ctx, vb_arrays, &n_arrays);
428 fprintf(stderr, "Using %d AOS arrays\n", n_arrays);
429
430 reg_start(R300_RB3D_DSTCACHE_CTLSTAT,0);
431 e32(0x0000000a);
432
433 reg_start(0x4f18,0);
434 e32(0x00000003);
435
436 r300_setup_routing(rmesa, ctx, GL_FALSE);
437
438 r300EmitState(rmesa);
439
440 FLAT_COLOR_PIPELINE.vertex_shader.matrix[0].length=16;
441 memcpy(FLAT_COLOR_PIPELINE.vertex_shader.matrix[0].body.f, ctx->_ModelProjectMatrix.m, 16*4);
442
443 FLAT_COLOR_PIPELINE.vertex_shader.unknown2.length=4;
444 FLAT_COLOR_PIPELINE.vertex_shader.unknown2.body.f[0]=0.0;
445 FLAT_COLOR_PIPELINE.vertex_shader.unknown2.body.f[1]=0.0;
446 FLAT_COLOR_PIPELINE.vertex_shader.unknown2.body.f[2]=1.0;
447 FLAT_COLOR_PIPELINE.vertex_shader.unknown2.body.f[3]=0.0;
448
449 program_pipeline(PASS_PREFIX &FLAT_COLOR_PIPELINE);
450
451 set_quad0(PASS_PREFIX 1.0,1.0,1.0,1.0);
452 set_init21(PASS_PREFIX 0.0,1.0);
453
454 for(i=0; i < VB->PrimitiveCount; i++){
455 GLuint prim = VB->Primitive[i].mode;
456 GLuint start = VB->Primitive[i].start;
457 GLuint length = VB->Primitive[i].count;
458
459 /* copy arrays */
460 memcpy(vb_arrays2, vb_arrays, sizeof(AOS_DATA)*n_arrays);
461 for(j=0;j<n_arrays;j++){
462 vb_arrays2[j].offset+=vb_arrays2[j].stride*start*4;
463 }
464
465 setup_AOS(PASS_PREFIX vb_arrays2, n_arrays);
466
467 r300_render_vb_flat_primitive(rmesa, ctx, start, start + length, prim);
468 }
469
470 /* This sequence is required after any 3d drawing packet
471 I suspect it work arounds a bug (or deficiency) in hardware */
472
473 reg_start(R300_RB3D_DSTCACHE_CTLSTAT,0);
474 e32(0x0000000a);
475
476 reg_start(0x4f18,0);
477 e32(0x00000003);
478
479 end_3d(PASS_PREFIX_VOID);
480
481 /* Flush state - we are done drawing.. */
482 r300Flush(ctx);
483 fprintf(stderr, "\n");
484 return GL_FALSE;
485 }
486
487
488 /**
489 * Called by the pipeline manager to render a batch of primitives.
490 * We can return true to pass on to the next stage (i.e. software
491 * rasterization) or false to indicate that the pipeline has finished
492 * after we render something.
493 */
494 static GLboolean r300_run_render(GLcontext *ctx,
495 struct tnl_pipeline_stage *stage)
496 {
497 r300ContextPtr rmesa = R300_CONTEXT(ctx);
498 TNLcontext *tnl = TNL_CONTEXT(ctx);
499 struct vertex_buffer *VB = &tnl->vb;
500 GLuint i;
501
502 if (RADEON_DEBUG == DEBUG_PRIMS)
503 fprintf(stderr, "%s\n", __FUNCTION__);
504
505
506 #if 1
507
508 return r300_run_immediate_render(ctx, stage);
509 #else
510 return GL_TRUE;
511 #endif
512
513 #if 0
514 mgaContextPtr mmesa = MGA_CONTEXT(ctx);
515 TNLcontext *tnl = TNL_CONTEXT(ctx);
516 struct vertex_buffer *VB = &tnl->vb;
517 GLuint i;
518
519 /* Don't handle clipping or indexed vertices or vertex manipulations.
520 */
521 if (mmesa->RenderIndex != 0 ||
522 !mga_validate_render( ctx, VB )) {
523 return GL_TRUE;
524 }
525
526 tnl->Driver.Render.Start( ctx );
527 mmesa->SetupNewInputs = ~0;
528
529 for (i = 0 ; i < VB->PrimitiveCount ; i++)
530 {
531 GLuint prim = VB->Primitive[i].mode;
532 GLuint start = VB->Primitive[i].start;
533 GLuint length = VB->Primitive[i].count;
534
535 if (!length)
536 continue;
537
538 mga_render_tab_verts[prim & PRIM_MODE_MASK]( ctx, start, start + length,
539 prim);
540 }
541
542 tnl->Driver.Render.Finish( ctx );
543
544 return GL_FALSE; /* finished the pipe */
545 #endif
546 }
547
548
549 /**
550 * Called by the pipeline manager once before rendering.
551 * We check the GL state here to
552 * a) decide whether we can do the current state in hardware and
553 * b) update hardware registers
554 */
555 #define FALLBACK_IF(expr) \
556 do { \
557 if (expr) { \
558 if (RADEON_DEBUG & DEBUG_FALLBACKS) \
559 fprintf(stderr, "%s: fallback:%s\n", \
560 __FUNCTION__, #expr); \
561 stage->active = GL_FALSE; \
562 return; \
563 } \
564 } while(0)
565
566 static void r300_check_render(GLcontext *ctx, struct tnl_pipeline_stage *stage)
567 {
568 r300ContextPtr r300 = R300_CONTEXT(ctx);
569 int i;
570
571 if (RADEON_DEBUG & DEBUG_STATE)
572 fprintf(stderr, "%s\n", __FUNCTION__);
573
574 /* We only support rendering in hardware for now */
575 if (ctx->RenderMode != GL_RENDER) {
576 stage->active = GL_FALSE;
577 return;
578 }
579
580 // I failed to figure out how dither works in hardware,
581 // let's just ignore it for now
582 //FALLBACK_IF(ctx->Color.DitherFlag);
583
584 /* I'm almost certain I forgot something here */
585 #if 0 /* This should work now.. */
586 FALLBACK_IF(ctx->Color.AlphaEnabled); // GL_ALPHA_TEST
587 #endif
588 FALLBACK_IF(ctx->Color.BlendEnabled); // GL_BLEND
589 FALLBACK_IF(ctx->Fog.Enabled); // GL_FOG
590 FALLBACK_IF(ctx->Line.SmoothFlag); // GL_LINE_SMOOTH
591 FALLBACK_IF(ctx->Line.StippleFlag); // GL_LINE_STIPPLE
592 FALLBACK_IF(ctx->Point.SmoothFlag); // GL_POINT_SMOOTH
593 if (ctx->Extensions.NV_point_sprite || ctx->Extensions.ARB_point_sprite)
594 FALLBACK_IF(ctx->Point.PointSprite); // GL_POINT_SPRITE_NV
595 FALLBACK_IF(ctx->Polygon.OffsetPoint); // GL_POLYGON_OFFSET_POINT
596 FALLBACK_IF(ctx->Polygon.OffsetLine); // GL_POLYGON_OFFSET_LINE
597 FALLBACK_IF(ctx->Polygon.OffsetFill); // GL_POLYGON_OFFSET_FILL
598 FALLBACK_IF(ctx->Polygon.SmoothFlag); // GL_POLYGON_SMOOTH
599 FALLBACK_IF(ctx->Polygon.StippleFlag); // GL_POLYGON_STIPPLE
600 FALLBACK_IF(ctx->Stencil.Enabled); // GL_STENCIL_TEST
601 FALLBACK_IF(ctx->Multisample.Enabled); // GL_MULTISAMPLE_ARB
602
603 /* One step at a time - let one texture pass.. */
604 for (i = 1; i < ctx->Const.MaxTextureUnits; i++)
605 FALLBACK_IF(ctx->Texture.Unit[i].Enabled);
606
607
608 /* let r300_run_render do its job */
609 #if 0
610 stage->active = GL_FALSE;
611 #endif
612 }
613
614
615 static void dtr(struct tnl_pipeline_stage *stage)
616 {
617 (void)stage;
618 }
619
620 const struct tnl_pipeline_stage _r300_render_stage = {
621 "r300 hw rasterize",
622 _NEW_ALL, /* re-check (always re-check for now) */
623 0, /* re-run (always runs) */
624 GL_TRUE, /* active */
625 0, 0, /* inputs (set in check_render), outputs */
626 0, 0, /* changed_inputs, private */
627 dtr, /* destructor */
628 r300_check_render, /* check */
629 r300_run_render /* run */
630 };