00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00037 #include "pipe/p_inlines.h"
00038 #include "pipe/p_context.h"
00039 #include "pipe/p_defines.h"
00040 #include "pipe/p_shader_tokens.h"
00041
00042 #include "util/u_math.h"
00043 #include "util/u_memory.h"
00044
00045 #include "tgsi/tgsi_transform.h"
00046 #include "tgsi/tgsi_dump.h"
00047
00048 #include "draw_context.h"
00049 #include "draw_pipe.h"
00050
00051
00052
00056 struct pstip_fragment_shader
00057 {
00058 struct pipe_shader_state state;
00059 void *driver_fs;
00060 void *pstip_fs;
00061 uint sampler_unit;
00062 };
00063
00064
00068 struct pstip_stage
00069 {
00070 struct draw_stage stage;
00071
00072 void *sampler_cso;
00073 struct pipe_texture *texture;
00074 uint num_samplers;
00075 uint num_textures;
00076
00077
00078
00079
00080 struct pstip_fragment_shader *fs;
00081 struct {
00082 void *samplers[PIPE_MAX_SAMPLERS];
00083 struct pipe_texture *textures[PIPE_MAX_SAMPLERS];
00084 const struct pipe_poly_stipple *stipple;
00085 } state;
00086
00087
00088
00089
00090 void * (*driver_create_fs_state)(struct pipe_context *,
00091 const struct pipe_shader_state *);
00092 void (*driver_bind_fs_state)(struct pipe_context *, void *);
00093 void (*driver_delete_fs_state)(struct pipe_context *, void *);
00094
00095 void (*driver_bind_sampler_states)(struct pipe_context *, unsigned, void **);
00096
00097 void (*driver_set_sampler_textures)(struct pipe_context *, unsigned,
00098 struct pipe_texture **);
00099
00100 void (*driver_set_polygon_stipple)(struct pipe_context *,
00101 const struct pipe_poly_stipple *);
00102
00103 struct pipe_context *pipe;
00104 };
00105
00106
00107
00112 struct pstip_transform_context {
00113 struct tgsi_transform_context base;
00114 uint tempsUsed;
00115 int wincoordInput;
00116 int maxInput;
00117 uint samplersUsed;
00118 int freeSampler;
00119 int texTemp;
00120 int numImmed;
00121 boolean firstInstruction;
00122 };
00123
00124
00129 static void
00130 pstip_transform_decl(struct tgsi_transform_context *ctx,
00131 struct tgsi_full_declaration *decl)
00132 {
00133 struct pstip_transform_context *pctx = (struct pstip_transform_context *) ctx;
00134
00135 if (decl->Declaration.File == TGSI_FILE_SAMPLER) {
00136 uint i;
00137 for (i = decl->DeclarationRange.First;
00138 i <= decl->DeclarationRange.Last; i++) {
00139 pctx->samplersUsed |= 1 << i;
00140 }
00141 }
00142 else if (decl->Declaration.File == TGSI_FILE_INPUT) {
00143 pctx->maxInput = MAX2(pctx->maxInput, (int) decl->DeclarationRange.Last);
00144 if (decl->Semantic.SemanticName == TGSI_SEMANTIC_POSITION)
00145 pctx->wincoordInput = (int) decl->DeclarationRange.First;
00146 }
00147 else if (decl->Declaration.File == TGSI_FILE_TEMPORARY) {
00148 uint i;
00149 for (i = decl->DeclarationRange.First;
00150 i <= decl->DeclarationRange.Last; i++) {
00151 pctx->tempsUsed |= (1 << i);
00152 }
00153 }
00154
00155 ctx->emit_declaration(ctx, decl);
00156 }
00157
00158
00159 static void
00160 pstip_transform_immed(struct tgsi_transform_context *ctx,
00161 struct tgsi_full_immediate *immed)
00162 {
00163 struct pstip_transform_context *pctx = (struct pstip_transform_context *) ctx;
00164 pctx->numImmed++;
00165 }
00166
00167
00171 static int
00172 free_bit(uint bitfield)
00173 {
00174 int i;
00175 for (i = 0; i < 32; i++) {
00176 if ((bitfield & (1 << i)) == 0)
00177 return i;
00178 }
00179 return -1;
00180 }
00181
00182
00188 static void
00189 pstip_transform_inst(struct tgsi_transform_context *ctx,
00190 struct tgsi_full_instruction *inst)
00191 {
00192 struct pstip_transform_context *pctx = (struct pstip_transform_context *) ctx;
00193
00194 if (pctx->firstInstruction) {
00195
00196
00197 struct tgsi_full_declaration decl;
00198 struct tgsi_full_instruction newInst;
00199 uint i;
00200 int wincoordInput;
00201
00202
00203 pctx->freeSampler = free_bit(pctx->samplersUsed);
00204 if (pctx->freeSampler >= PIPE_MAX_SAMPLERS)
00205 pctx->freeSampler = PIPE_MAX_SAMPLERS - 1;
00206
00207 if (pctx->wincoordInput < 0)
00208 wincoordInput = pctx->maxInput + 1;
00209 else
00210 wincoordInput = pctx->wincoordInput;
00211
00212
00213 for (i = 0; i < 32; i++) {
00214 if ((pctx->tempsUsed & (1 << i)) == 0) {
00215
00216 if (pctx->texTemp < 0)
00217 pctx->texTemp = i;
00218 else
00219 break;
00220 }
00221 }
00222 assert(pctx->texTemp >= 0);
00223
00224 if (pctx->wincoordInput < 0) {
00225
00226 decl = tgsi_default_full_declaration();
00227 decl.Declaration.File = TGSI_FILE_INPUT;
00228 decl.Declaration.Interpolate = TGSI_INTERPOLATE_LINEAR;
00229 decl.Declaration.Semantic = 1;
00230 decl.Semantic.SemanticName = TGSI_SEMANTIC_POSITION;
00231 decl.Semantic.SemanticIndex = 0;
00232 decl.DeclarationRange.First =
00233 decl.DeclarationRange.Last = wincoordInput;
00234 ctx->emit_declaration(ctx, &decl);
00235 }
00236
00237
00238 decl = tgsi_default_full_declaration();
00239 decl.Declaration.File = TGSI_FILE_SAMPLER;
00240 decl.DeclarationRange.First =
00241 decl.DeclarationRange.Last = pctx->freeSampler;
00242 ctx->emit_declaration(ctx, &decl);
00243
00244
00245 decl = tgsi_default_full_declaration();
00246 decl.Declaration.File = TGSI_FILE_TEMPORARY;
00247 decl.DeclarationRange.First =
00248 decl.DeclarationRange.Last = pctx->texTemp;
00249 ctx->emit_declaration(ctx, &decl);
00250
00251
00252
00253
00254 {
00255 static const float value[4] = { 1.0/32, 1.0/32, 1.0, 1.0 };
00256 struct tgsi_full_immediate immed;
00257 uint size = 4;
00258 immed = tgsi_default_full_immediate();
00259 immed.Immediate.Size = 1 + size;
00260 immed.u.Pointer = (void *) value;
00261 ctx->emit_immediate(ctx, &immed);
00262 }
00263
00264 pctx->firstInstruction = FALSE;
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278 newInst = tgsi_default_full_instruction();
00279 newInst.Instruction.Opcode = TGSI_OPCODE_MUL;
00280 newInst.Instruction.NumDstRegs = 1;
00281 newInst.FullDstRegisters[0].DstRegister.File = TGSI_FILE_TEMPORARY;
00282 newInst.FullDstRegisters[0].DstRegister.Index = pctx->texTemp;
00283 newInst.Instruction.NumSrcRegs = 2;
00284 newInst.FullSrcRegisters[0].SrcRegister.File = TGSI_FILE_INPUT;
00285 newInst.FullSrcRegisters[0].SrcRegister.Index = wincoordInput;
00286 newInst.FullSrcRegisters[1].SrcRegister.File = TGSI_FILE_IMMEDIATE;
00287 newInst.FullSrcRegisters[1].SrcRegister.Index = pctx->numImmed;
00288 ctx->emit_instruction(ctx, &newInst);
00289
00290
00291 newInst = tgsi_default_full_instruction();
00292 newInst.Instruction.Opcode = TGSI_OPCODE_TEX;
00293 newInst.Instruction.NumDstRegs = 1;
00294 newInst.FullDstRegisters[0].DstRegister.File = TGSI_FILE_TEMPORARY;
00295 newInst.FullDstRegisters[0].DstRegister.Index = pctx->texTemp;
00296 newInst.Instruction.NumSrcRegs = 2;
00297 newInst.InstructionExtTexture.Texture = TGSI_TEXTURE_2D;
00298 newInst.FullSrcRegisters[0].SrcRegister.File = TGSI_FILE_TEMPORARY;
00299 newInst.FullSrcRegisters[0].SrcRegister.Index = pctx->texTemp;
00300 newInst.FullSrcRegisters[1].SrcRegister.File = TGSI_FILE_SAMPLER;
00301 newInst.FullSrcRegisters[1].SrcRegister.Index = pctx->freeSampler;
00302 ctx->emit_instruction(ctx, &newInst);
00303
00304
00305 newInst = tgsi_default_full_instruction();
00306 newInst.Instruction.Opcode = TGSI_OPCODE_KIL;
00307 newInst.Instruction.NumDstRegs = 0;
00308 newInst.Instruction.NumSrcRegs = 1;
00309 newInst.FullSrcRegisters[0].SrcRegister.File = TGSI_FILE_TEMPORARY;
00310 newInst.FullSrcRegisters[0].SrcRegister.Index = pctx->texTemp;
00311 newInst.FullSrcRegisters[0].SrcRegister.Negate = 1;
00312 ctx->emit_instruction(ctx, &newInst);
00313 }
00314
00315
00316 ctx->emit_instruction(ctx, inst);
00317 }
00318
00319
00324 static boolean
00325 generate_pstip_fs(struct pstip_stage *pstip)
00326 {
00327 const struct pipe_shader_state *orig_fs = &pstip->fs->state;
00328
00329 struct pipe_shader_state pstip_fs;
00330 struct pstip_transform_context transform;
00331
00332 #define MAX 1000
00333
00334 pstip_fs = *orig_fs;
00335 pstip_fs.tokens = MALLOC(sizeof(struct tgsi_token) * MAX);
00336 if (pstip_fs.tokens == NULL)
00337 return FALSE;
00338
00339 memset(&transform, 0, sizeof(transform));
00340 transform.wincoordInput = -1;
00341 transform.maxInput = -1;
00342 transform.texTemp = -1;
00343 transform.firstInstruction = TRUE;
00344 transform.base.transform_instruction = pstip_transform_inst;
00345 transform.base.transform_declaration = pstip_transform_decl;
00346 transform.base.transform_immediate = pstip_transform_immed;
00347
00348 tgsi_transform_shader(orig_fs->tokens,
00349 (struct tgsi_token *) pstip_fs.tokens,
00350 MAX, &transform.base);
00351
00352 #if 0
00353 tgsi_dump(orig_fs->tokens, 0);
00354 tgsi_dump(pstip_fs.tokens, 0);
00355 #endif
00356
00357 pstip->fs->sampler_unit = transform.freeSampler;
00358 assert(pstip->fs->sampler_unit < PIPE_MAX_SAMPLERS);
00359
00360 pstip->fs->pstip_fs = pstip->driver_create_fs_state(pstip->pipe, &pstip_fs);
00361
00362 return TRUE;
00363 }
00364
00365
00369 static void
00370 pstip_update_texture(struct pstip_stage *pstip)
00371 {
00372 static const uint bit31 = 1 << 31;
00373 struct pipe_context *pipe = pstip->pipe;
00374 struct pipe_screen *screen = pipe->screen;
00375 struct pipe_surface *surface;
00376 const uint *stipple = pstip->state.stipple->stipple;
00377 uint i, j;
00378 ubyte *data;
00379
00380
00381
00382 pipe->flush( pipe, PIPE_FLUSH_TEXTURE_CACHE, NULL );
00383
00384 surface = screen->get_tex_surface(screen, pstip->texture, 0, 0, 0,
00385 PIPE_BUFFER_USAGE_CPU_WRITE);
00386 data = screen->surface_map(screen, surface,
00387 PIPE_BUFFER_USAGE_CPU_WRITE);
00388
00389
00390
00391
00392
00393
00394
00395 for (i = 0; i < 32; i++) {
00396 for (j = 0; j < 32; j++) {
00397 if (stipple[i] & (bit31 >> j)) {
00398
00399 data[i * surface->stride + j] = 0;
00400 }
00401 else {
00402
00403 data[i * surface->stride + j] = 255;
00404 }
00405 }
00406 }
00407
00408
00409 screen->surface_unmap(screen, surface);
00410 screen->tex_surface_release(screen, &surface);
00411 }
00412
00413
00417 static boolean
00418 pstip_create_texture(struct pstip_stage *pstip)
00419 {
00420 struct pipe_context *pipe = pstip->pipe;
00421 struct pipe_screen *screen = pipe->screen;
00422 struct pipe_texture texTemp;
00423
00424 memset(&texTemp, 0, sizeof(texTemp));
00425 texTemp.target = PIPE_TEXTURE_2D;
00426 texTemp.format = PIPE_FORMAT_A8_UNORM;
00427 texTemp.last_level = 0;
00428 texTemp.width[0] = 32;
00429 texTemp.height[0] = 32;
00430 texTemp.depth[0] = 1;
00431 pf_get_block(texTemp.format, &texTemp.block);
00432
00433 pstip->texture = screen->texture_create(screen, &texTemp);
00434 if (pstip->texture == NULL)
00435 return FALSE;
00436
00437 return TRUE;
00438 }
00439
00440
00444 static boolean
00445 pstip_create_sampler(struct pstip_stage *pstip)
00446 {
00447 struct pipe_sampler_state sampler;
00448 struct pipe_context *pipe = pstip->pipe;
00449
00450 memset(&sampler, 0, sizeof(sampler));
00451 sampler.wrap_s = PIPE_TEX_WRAP_REPEAT;
00452 sampler.wrap_t = PIPE_TEX_WRAP_REPEAT;
00453 sampler.wrap_r = PIPE_TEX_WRAP_REPEAT;
00454 sampler.min_mip_filter = PIPE_TEX_MIPFILTER_NONE;
00455 sampler.min_img_filter = PIPE_TEX_FILTER_NEAREST;
00456 sampler.mag_img_filter = PIPE_TEX_FILTER_NEAREST;
00457 sampler.normalized_coords = 1;
00458 sampler.min_lod = 0.0f;
00459 sampler.max_lod = 0.0f;
00460
00461 pstip->sampler_cso = pipe->create_sampler_state(pipe, &sampler);
00462 if (pstip->sampler_cso == NULL)
00463 return FALSE;
00464
00465 return TRUE;
00466 }
00467
00468
00473 static boolean
00474 bind_pstip_fragment_shader(struct pstip_stage *pstip)
00475 {
00476 struct draw_context *draw = pstip->stage.draw;
00477 if (!pstip->fs->pstip_fs &&
00478 !generate_pstip_fs(pstip))
00479 return FALSE;
00480
00481 draw->suspend_flushing = TRUE;
00482 pstip->driver_bind_fs_state(pstip->pipe, pstip->fs->pstip_fs);
00483 draw->suspend_flushing = FALSE;
00484 return TRUE;
00485 }
00486
00487
00488 static INLINE struct pstip_stage *
00489 pstip_stage( struct draw_stage *stage )
00490 {
00491 return (struct pstip_stage *) stage;
00492 }
00493
00494
00495 static void
00496 pstip_first_tri(struct draw_stage *stage, struct prim_header *header)
00497 {
00498 struct pstip_stage *pstip = pstip_stage(stage);
00499 struct pipe_context *pipe = pstip->pipe;
00500 struct draw_context *draw = stage->draw;
00501 uint num_samplers;
00502
00503 assert(stage->draw->rasterizer->poly_stipple_enable);
00504
00505
00506 if (!bind_pstip_fragment_shader(pstip)) {
00507 stage->tri = draw_pipe_passthrough_tri;
00508 stage->tri(stage, header);
00509 return;
00510 }
00511
00512
00513
00514
00515 num_samplers = MAX2(pstip->num_textures, pstip->num_samplers);
00516 num_samplers = MAX2(num_samplers, pstip->fs->sampler_unit + 1);
00517
00518
00519 pstip->state.samplers[pstip->fs->sampler_unit] = pstip->sampler_cso;
00520 pipe_texture_reference(&pstip->state.textures[pstip->fs->sampler_unit],
00521 pstip->texture);
00522
00523 assert(num_samplers <= PIPE_MAX_SAMPLERS);
00524
00525 draw->suspend_flushing = TRUE;
00526 pstip->driver_bind_sampler_states(pipe, num_samplers, pstip->state.samplers);
00527 pstip->driver_set_sampler_textures(pipe, num_samplers, pstip->state.textures);
00528 draw->suspend_flushing = FALSE;
00529
00530
00531 stage->tri = draw_pipe_passthrough_tri;
00532 stage->tri(stage, header);
00533 }
00534
00535
00536 static void
00537 pstip_flush(struct draw_stage *stage, unsigned flags)
00538 {
00539 struct draw_context *draw = stage->draw;
00540 struct pstip_stage *pstip = pstip_stage(stage);
00541 struct pipe_context *pipe = pstip->pipe;
00542
00543 stage->tri = pstip_first_tri;
00544 stage->next->flush( stage->next, flags );
00545
00546
00547 draw->suspend_flushing = TRUE;
00548 pstip->driver_bind_fs_state(pipe, pstip->fs->driver_fs);
00549 pstip->driver_bind_sampler_states(pipe, pstip->num_samplers,
00550 pstip->state.samplers);
00551 pstip->driver_set_sampler_textures(pipe, pstip->num_textures,
00552 pstip->state.textures);
00553 draw->suspend_flushing = FALSE;
00554 }
00555
00556
00557 static void
00558 pstip_reset_stipple_counter(struct draw_stage *stage)
00559 {
00560 stage->next->reset_stipple_counter( stage->next );
00561 }
00562
00563
00564 static void
00565 pstip_destroy(struct draw_stage *stage)
00566 {
00567 struct pstip_stage *pstip = pstip_stage(stage);
00568 uint i;
00569
00570 for (i = 0; i < PIPE_MAX_SAMPLERS; i++) {
00571 pipe_texture_reference(&pstip->state.textures[i], NULL);
00572 }
00573
00574 pstip->pipe->delete_sampler_state(pstip->pipe, pstip->sampler_cso);
00575
00576 pipe_texture_release(&pstip->texture);
00577
00578 draw_free_temp_verts( stage );
00579 FREE( stage );
00580 }
00581
00582
00583 static struct pstip_stage *
00584 draw_pstip_stage(struct draw_context *draw)
00585 {
00586 struct pstip_stage *pstip = CALLOC_STRUCT(pstip_stage);
00587
00588 draw_alloc_temp_verts( &pstip->stage, 8 );
00589
00590 pstip->stage.draw = draw;
00591 pstip->stage.next = NULL;
00592 pstip->stage.point = draw_pipe_passthrough_point;
00593 pstip->stage.line = draw_pipe_passthrough_line;
00594 pstip->stage.tri = pstip_first_tri;
00595 pstip->stage.flush = pstip_flush;
00596 pstip->stage.reset_stipple_counter = pstip_reset_stipple_counter;
00597 pstip->stage.destroy = pstip_destroy;
00598
00599 return pstip;
00600 }
00601
00602
00603 static struct pstip_stage *
00604 pstip_stage_from_pipe(struct pipe_context *pipe)
00605 {
00606 struct draw_context *draw = (struct draw_context *) pipe->draw;
00607 return pstip_stage(draw->pipeline.pstipple);
00608 }
00609
00610
00615 static void *
00616 pstip_create_fs_state(struct pipe_context *pipe,
00617 const struct pipe_shader_state *fs)
00618 {
00619 struct pstip_stage *pstip = pstip_stage_from_pipe(pipe);
00620 struct pstip_fragment_shader *aafs = CALLOC_STRUCT(pstip_fragment_shader);
00621
00622 if (aafs) {
00623 aafs->state = *fs;
00624
00625
00626 aafs->driver_fs = pstip->driver_create_fs_state(pstip->pipe, fs);
00627 }
00628
00629 return aafs;
00630 }
00631
00632
00633 static void
00634 pstip_bind_fs_state(struct pipe_context *pipe, void *fs)
00635 {
00636 struct pstip_stage *pstip = pstip_stage_from_pipe(pipe);
00637 struct pstip_fragment_shader *aafs = (struct pstip_fragment_shader *) fs;
00638
00639 pstip->fs = aafs;
00640
00641 pstip->driver_bind_fs_state(pstip->pipe,
00642 (aafs ? aafs->driver_fs : NULL));
00643 }
00644
00645
00646 static void
00647 pstip_delete_fs_state(struct pipe_context *pipe, void *fs)
00648 {
00649 struct pstip_stage *pstip = pstip_stage_from_pipe(pipe);
00650 struct pstip_fragment_shader *aafs = (struct pstip_fragment_shader *) fs;
00651
00652 pstip->driver_delete_fs_state(pstip->pipe, aafs->driver_fs);
00653 FREE(aafs);
00654 }
00655
00656
00657 static void
00658 pstip_bind_sampler_states(struct pipe_context *pipe,
00659 unsigned num, void **sampler)
00660 {
00661 struct pstip_stage *pstip = pstip_stage_from_pipe(pipe);
00662 uint i;
00663
00664
00665 memcpy(pstip->state.samplers, sampler, num * sizeof(void *));
00666 for (i = num; i < PIPE_MAX_SAMPLERS; i++) {
00667 pstip->state.samplers[i] = NULL;
00668 }
00669
00670 pstip->num_samplers = num;
00671
00672 pstip->driver_bind_sampler_states(pstip->pipe, num, sampler);
00673 }
00674
00675
00676 static void
00677 pstip_set_sampler_textures(struct pipe_context *pipe,
00678 unsigned num, struct pipe_texture **texture)
00679 {
00680 struct pstip_stage *pstip = pstip_stage_from_pipe(pipe);
00681 uint i;
00682
00683
00684 for (i = 0; i < num; i++) {
00685 pipe_texture_reference(&pstip->state.textures[i], texture[i]);
00686 }
00687 for (; i < PIPE_MAX_SAMPLERS; i++) {
00688 pipe_texture_reference(&pstip->state.textures[i], NULL);
00689 }
00690
00691 pstip->num_textures = num;
00692
00693
00694 pstip->driver_set_sampler_textures(pstip->pipe, num, texture);
00695 }
00696
00697
00698 static void
00699 pstip_set_polygon_stipple(struct pipe_context *pipe,
00700 const struct pipe_poly_stipple *stipple)
00701 {
00702 struct pstip_stage *pstip = pstip_stage_from_pipe(pipe);
00703
00704
00705 pstip->state.stipple = stipple;
00706
00707
00708 pstip->driver_set_polygon_stipple(pstip->pipe, stipple);
00709
00710 pstip_update_texture(pstip);
00711 }
00712
00713
00719 boolean
00720 draw_install_pstipple_stage(struct draw_context *draw,
00721 struct pipe_context *pipe)
00722 {
00723 struct pstip_stage *pstip;
00724
00725 pipe->draw = (void *) draw;
00726
00727
00728
00729
00730 pstip = draw_pstip_stage( draw );
00731 if (pstip == NULL)
00732 goto fail;
00733
00734 draw->pipeline.pstipple = &pstip->stage;
00735
00736 pstip->pipe = pipe;
00737
00738
00739 if (!pstip_create_texture(pstip))
00740 goto fail;
00741
00742 if (!pstip_create_sampler(pstip))
00743 goto fail;
00744
00745
00746 pstip->driver_create_fs_state = pipe->create_fs_state;
00747 pstip->driver_bind_fs_state = pipe->bind_fs_state;
00748 pstip->driver_delete_fs_state = pipe->delete_fs_state;
00749
00750 pstip->driver_bind_sampler_states = pipe->bind_sampler_states;
00751 pstip->driver_set_sampler_textures = pipe->set_sampler_textures;
00752 pstip->driver_set_polygon_stipple = pipe->set_polygon_stipple;
00753
00754
00755 pipe->create_fs_state = pstip_create_fs_state;
00756 pipe->bind_fs_state = pstip_bind_fs_state;
00757 pipe->delete_fs_state = pstip_delete_fs_state;
00758
00759 pipe->bind_sampler_states = pstip_bind_sampler_states;
00760 pipe->set_sampler_textures = pstip_set_sampler_textures;
00761 pipe->set_polygon_stipple = pstip_set_polygon_stipple;
00762
00763 return TRUE;
00764
00765 fail:
00766 if (pstip)
00767 pstip->stage.destroy( &pstip->stage );
00768
00769 return FALSE;
00770 }