gallium: unused var silence warning
[mesa.git] / src / mesa / shader / program.c
1 /*
2 * Mesa 3-D graphics library
3 * Version: 6.5.3
4 *
5 * Copyright (C) 1999-2007 Brian Paul 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 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25 /**
26 * \file program.c
27 * Vertex and fragment program support functions.
28 * \author Brian Paul
29 */
30
31
32 #include "glheader.h"
33 #include "context.h"
34 #include "hash.h"
35 #include "program.h"
36 #include "prog_cache.h"
37 #include "prog_parameter.h"
38 #include "prog_instruction.h"
39
40
41 /**
42 * A pointer to this dummy program is put into the hash table when
43 * glGenPrograms is called.
44 */
45 struct gl_program _mesa_DummyProgram;
46
47
48 /**
49 * Init context's vertex/fragment program state
50 */
51 void
52 _mesa_init_program(GLcontext *ctx)
53 {
54 GLuint i;
55
56 ctx->Program.ErrorPos = -1;
57 ctx->Program.ErrorString = _mesa_strdup("");
58
59 #if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program
60 ctx->VertexProgram.Enabled = GL_FALSE;
61 ctx->VertexProgram.PointSizeEnabled = GL_FALSE;
62 ctx->VertexProgram.TwoSideEnabled = GL_FALSE;
63 ctx->VertexProgram.Current = (struct gl_vertex_program *) ctx->Shared->DefaultVertexProgram;
64 assert(ctx->VertexProgram.Current);
65 ctx->VertexProgram.Current->Base.RefCount++;
66 for (i = 0; i < MAX_NV_VERTEX_PROGRAM_PARAMS / 4; i++) {
67 ctx->VertexProgram.TrackMatrix[i] = GL_NONE;
68 ctx->VertexProgram.TrackMatrixTransform[i] = GL_IDENTITY_NV;
69 }
70 ctx->VertexProgram.Cache = _mesa_new_program_cache();
71 #endif
72
73 #if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program
74 ctx->FragmentProgram.Enabled = GL_FALSE;
75 ctx->FragmentProgram.Current = (struct gl_fragment_program *) ctx->Shared->DefaultFragmentProgram;
76 assert(ctx->FragmentProgram.Current);
77 ctx->FragmentProgram.Current->Base.RefCount++;
78 ctx->FragmentProgram.Cache = _mesa_new_program_cache();
79 #endif
80
81
82 /* XXX probably move this stuff */
83 #if FEATURE_ATI_fragment_shader
84 ctx->ATIFragmentShader.Enabled = GL_FALSE;
85 ctx->ATIFragmentShader.Current = (struct ati_fragment_shader *) ctx->Shared->DefaultFragmentShader;
86 assert(ctx->ATIFragmentShader.Current);
87 ctx->ATIFragmentShader.Current->RefCount++;
88 #endif
89 }
90
91
92 /**
93 * Free a context's vertex/fragment program state
94 */
95 void
96 _mesa_free_program_data(GLcontext *ctx)
97 {
98 #if FEATURE_NV_vertex_program || FEATURE_ARB_vertex_program
99 if (ctx->VertexProgram.Current) {
100 ctx->VertexProgram.Current->Base.RefCount--;
101 if (ctx->VertexProgram.Current->Base.RefCount <= 0)
102 ctx->Driver.DeleteProgram(ctx, &(ctx->VertexProgram.Current->Base));
103 }
104 _mesa_delete_program_cache(ctx, ctx->VertexProgram.Cache);
105 #endif
106 #if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program
107 if (ctx->FragmentProgram.Current) {
108 ctx->FragmentProgram.Current->Base.RefCount--;
109 if (ctx->FragmentProgram.Current->Base.RefCount <= 0)
110 ctx->Driver.DeleteProgram(ctx, &(ctx->FragmentProgram.Current->Base));
111 }
112 _mesa_delete_program_cache(ctx, ctx->FragmentProgram.Cache);
113 #endif
114 /* XXX probably move this stuff */
115 #if FEATURE_ATI_fragment_shader
116 if (ctx->ATIFragmentShader.Current) {
117 ctx->ATIFragmentShader.Current->RefCount--;
118 if (ctx->ATIFragmentShader.Current->RefCount <= 0) {
119 _mesa_free(ctx->ATIFragmentShader.Current);
120 }
121 }
122 #endif
123 _mesa_free((void *) ctx->Program.ErrorString);
124 }
125
126
127
128
129 /**
130 * Set the vertex/fragment program error state (position and error string).
131 * This is generally called from within the parsers.
132 */
133 void
134 _mesa_set_program_error(GLcontext *ctx, GLint pos, const char *string)
135 {
136 ctx->Program.ErrorPos = pos;
137 _mesa_free((void *) ctx->Program.ErrorString);
138 if (!string)
139 string = "";
140 ctx->Program.ErrorString = _mesa_strdup(string);
141 }
142
143
144 /**
145 * Find the line number and column for 'pos' within 'string'.
146 * Return a copy of the line which contains 'pos'. Free the line with
147 * _mesa_free().
148 * \param string the program string
149 * \param pos the position within the string
150 * \param line returns the line number corresponding to 'pos'.
151 * \param col returns the column number corresponding to 'pos'.
152 * \return copy of the line containing 'pos'.
153 */
154 const GLubyte *
155 _mesa_find_line_column(const GLubyte *string, const GLubyte *pos,
156 GLint *line, GLint *col)
157 {
158 const GLubyte *lineStart = string;
159 const GLubyte *p = string;
160 GLubyte *s;
161 int len;
162
163 *line = 1;
164
165 while (p != pos) {
166 if (*p == (GLubyte) '\n') {
167 (*line)++;
168 lineStart = p + 1;
169 }
170 p++;
171 }
172
173 *col = (pos - lineStart) + 1;
174
175 /* return copy of this line */
176 while (*p != 0 && *p != '\n')
177 p++;
178 len = p - lineStart;
179 s = (GLubyte *) _mesa_malloc(len + 1);
180 _mesa_memcpy(s, lineStart, len);
181 s[len] = 0;
182
183 return s;
184 }
185
186
187 /**
188 * Initialize a new vertex/fragment program object.
189 */
190 static struct gl_program *
191 _mesa_init_program_struct( GLcontext *ctx, struct gl_program *prog,
192 GLenum target, GLuint id)
193 {
194 (void) ctx;
195 if (prog) {
196 GLuint i;
197 _mesa_bzero(prog, sizeof(*prog));
198 prog->Id = id;
199 prog->Target = target;
200 prog->Resident = GL_TRUE;
201 prog->RefCount = 1;
202 prog->Format = GL_PROGRAM_FORMAT_ASCII_ARB;
203
204 /* default mapping from samplers to texture units */
205 for (i = 0; i < MAX_SAMPLERS; i++)
206 prog->SamplerUnits[i] = i;
207 }
208
209 return prog;
210 }
211
212
213 /**
214 * Initialize a new fragment program object.
215 */
216 struct gl_program *
217 _mesa_init_fragment_program( GLcontext *ctx, struct gl_fragment_program *prog,
218 GLenum target, GLuint id)
219 {
220 if (prog)
221 return _mesa_init_program_struct( ctx, &prog->Base, target, id );
222 else
223 return NULL;
224 }
225
226
227 /**
228 * Initialize a new vertex program object.
229 */
230 struct gl_program *
231 _mesa_init_vertex_program( GLcontext *ctx, struct gl_vertex_program *prog,
232 GLenum target, GLuint id)
233 {
234 if (prog)
235 return _mesa_init_program_struct( ctx, &prog->Base, target, id );
236 else
237 return NULL;
238 }
239
240
241 /**
242 * Allocate and initialize a new fragment/vertex program object but
243 * don't put it into the program hash table. Called via
244 * ctx->Driver.NewProgram. May be overridden (ie. replaced) by a
245 * device driver function to implement OO deriviation with additional
246 * types not understood by this function.
247 *
248 * \param ctx context
249 * \param id program id/number
250 * \param target program target/type
251 * \return pointer to new program object
252 */
253 struct gl_program *
254 _mesa_new_program(GLcontext *ctx, GLenum target, GLuint id)
255 {
256 switch (target) {
257 case GL_VERTEX_PROGRAM_ARB: /* == GL_VERTEX_PROGRAM_NV */
258 return _mesa_init_vertex_program(ctx, CALLOC_STRUCT(gl_vertex_program),
259 target, id );
260 case GL_FRAGMENT_PROGRAM_NV:
261 case GL_FRAGMENT_PROGRAM_ARB:
262 return _mesa_init_fragment_program(ctx,
263 CALLOC_STRUCT(gl_fragment_program),
264 target, id );
265 default:
266 _mesa_problem(ctx, "bad target in _mesa_new_program");
267 return NULL;
268 }
269 }
270
271
272 /**
273 * Delete a program and remove it from the hash table, ignoring the
274 * reference count.
275 * Called via ctx->Driver.DeleteProgram. May be wrapped (OO deriviation)
276 * by a device driver function.
277 */
278 void
279 _mesa_delete_program(GLcontext *ctx, struct gl_program *prog)
280 {
281 (void) ctx;
282 ASSERT(prog);
283
284 if (prog == &_mesa_DummyProgram)
285 return;
286
287 if (prog->String)
288 _mesa_free(prog->String);
289
290 if (prog->Instructions) {
291 GLuint i;
292 for (i = 0; i < prog->NumInstructions; i++) {
293 if (prog->Instructions[i].Data)
294 _mesa_free(prog->Instructions[i].Data);
295 if (prog->Instructions[i].Comment)
296 _mesa_free((char *) prog->Instructions[i].Comment);
297 }
298 _mesa_free(prog->Instructions);
299 }
300
301 if (prog->Parameters) {
302 _mesa_free_parameter_list(prog->Parameters);
303 }
304 if (prog->Varying) {
305 _mesa_free_parameter_list(prog->Varying);
306 }
307 if (prog->Attributes) {
308 _mesa_free_parameter_list(prog->Attributes);
309 }
310
311 /* XXX this is a little ugly */
312 if (prog->Target == GL_VERTEX_PROGRAM_ARB) {
313 struct gl_vertex_program *vprog = (struct gl_vertex_program *) prog;
314 if (vprog->TnlData)
315 _mesa_free(vprog->TnlData);
316 }
317
318 _mesa_free(prog);
319 }
320
321
322 /**
323 * Return the gl_program object for a given ID.
324 * Basically just a wrapper for _mesa_HashLookup() to avoid a lot of
325 * casts elsewhere.
326 */
327 struct gl_program *
328 _mesa_lookup_program(GLcontext *ctx, GLuint id)
329 {
330 if (id)
331 return (struct gl_program *) _mesa_HashLookup(ctx->Shared->Programs, id);
332 else
333 return NULL;
334 }
335
336
337 /**
338 * Return a copy of a program.
339 * XXX Problem here if the program object is actually OO-derivation
340 * made by a device driver.
341 */
342 struct gl_program *
343 _mesa_clone_program(GLcontext *ctx, const struct gl_program *prog)
344 {
345 struct gl_program *clone;
346
347 clone = ctx->Driver.NewProgram(ctx, prog->Target, prog->Id);
348 if (!clone)
349 return NULL;
350
351 assert(clone->Target == prog->Target);
352 clone->String = (GLubyte *) _mesa_strdup((char *) prog->String);
353 clone->RefCount = 1;
354 clone->Format = prog->Format;
355 clone->Instructions = _mesa_alloc_instructions(prog->NumInstructions);
356 if (!clone->Instructions) {
357 _mesa_delete_program(ctx, clone);
358 return NULL;
359 }
360 _mesa_copy_instructions(clone->Instructions, prog->Instructions,
361 prog->NumInstructions);
362 clone->InputsRead = prog->InputsRead;
363 clone->OutputsWritten = prog->OutputsWritten;
364 memcpy(clone->TexturesUsed, prog->TexturesUsed, sizeof(prog->TexturesUsed));
365
366 if (prog->Parameters)
367 clone->Parameters = _mesa_clone_parameter_list(prog->Parameters);
368 memcpy(clone->LocalParams, prog->LocalParams, sizeof(clone->LocalParams));
369 if (prog->Varying)
370 clone->Varying = _mesa_clone_parameter_list(prog->Varying);
371 if (prog->Attributes)
372 clone->Attributes = _mesa_clone_parameter_list(prog->Attributes);
373 memcpy(clone->LocalParams, prog->LocalParams, sizeof(clone->LocalParams));
374 clone->NumInstructions = prog->NumInstructions;
375 clone->NumTemporaries = prog->NumTemporaries;
376 clone->NumParameters = prog->NumParameters;
377 clone->NumAttributes = prog->NumAttributes;
378 clone->NumAddressRegs = prog->NumAddressRegs;
379 clone->NumNativeInstructions = prog->NumNativeInstructions;
380 clone->NumNativeTemporaries = prog->NumNativeTemporaries;
381 clone->NumNativeParameters = prog->NumNativeParameters;
382 clone->NumNativeAttributes = prog->NumNativeAttributes;
383 clone->NumNativeAddressRegs = prog->NumNativeAddressRegs;
384 clone->NumAluInstructions = prog->NumAluInstructions;
385 clone->NumTexInstructions = prog->NumTexInstructions;
386 clone->NumTexIndirections = prog->NumTexIndirections;
387 clone->NumNativeAluInstructions = prog->NumNativeAluInstructions;
388 clone->NumNativeTexInstructions = prog->NumNativeTexInstructions;
389 clone->NumNativeTexIndirections = prog->NumNativeTexIndirections;
390
391 switch (prog->Target) {
392 case GL_VERTEX_PROGRAM_ARB:
393 {
394 const struct gl_vertex_program *vp
395 = (const struct gl_vertex_program *) prog;
396 struct gl_vertex_program *vpc = (struct gl_vertex_program *) clone;
397 vpc->IsPositionInvariant = vp->IsPositionInvariant;
398 }
399 break;
400 case GL_FRAGMENT_PROGRAM_ARB:
401 {
402 const struct gl_fragment_program *fp
403 = (const struct gl_fragment_program *) prog;
404 struct gl_fragment_program *fpc = (struct gl_fragment_program *) clone;
405 fpc->FogOption = fp->FogOption;
406 fpc->UsesKill = fp->UsesKill;
407 }
408 break;
409 default:
410 _mesa_problem(NULL, "Unexpected target in _mesa_clone_program");
411 }
412
413 return clone;
414 }
415
416
417
418 /**
419 * Search instructions for registers that match (oldFile, oldIndex),
420 * replacing them with (newFile, newIndex).
421 */
422 static void
423 replace_registers(struct prog_instruction *inst, GLuint numInst,
424 GLuint oldFile, GLuint oldIndex,
425 GLuint newFile, GLuint newIndex)
426 {
427 GLuint i, j;
428 for (i = 0; i < numInst; i++) {
429 for (j = 0; j < _mesa_num_inst_src_regs(inst->Opcode); j++) {
430 if (inst[i].SrcReg[j].File == oldFile &&
431 inst[i].SrcReg[j].Index == oldIndex) {
432 inst[i].SrcReg[j].File = newFile;
433 inst[i].SrcReg[j].Index = newIndex;
434 }
435 }
436 }
437 }
438
439
440 /**
441 * Search instructions for references to program parameters. When found,
442 * increment the parameter index by 'offset'.
443 * Used when combining programs.
444 */
445 static void
446 adjust_param_indexes(struct prog_instruction *inst, GLuint numInst,
447 GLuint offset)
448 {
449 GLuint i, j;
450 for (i = 0; i < numInst; i++) {
451 for (j = 0; j < _mesa_num_inst_src_regs(inst->Opcode); j++) {
452 GLuint f = inst[i].SrcReg[j].File;
453 if (f == PROGRAM_CONSTANT ||
454 f == PROGRAM_UNIFORM ||
455 f == PROGRAM_STATE_VAR) {
456 inst[i].SrcReg[j].Index += offset;
457 }
458 }
459 }
460 }
461
462
463 /**
464 * Combine two programs into one. Fix instructions so the outputs of
465 * the first program go to the inputs of the second program.
466 */
467 struct gl_program *
468 _mesa_combine_programs(GLcontext *ctx,
469 const struct gl_program *progA,
470 const struct gl_program *progB)
471 {
472 struct prog_instruction *newInst;
473 struct gl_program *newProg;
474 const GLuint lenA = progA->NumInstructions - 1; /* omit END instr */
475 const GLuint lenB = progB->NumInstructions;
476 const GLuint numParamsA = _mesa_num_parameters(progA->Parameters);
477 const GLuint newLength = lenA + lenB;
478 GLbitfield inputsB;
479 GLuint i;
480
481 ASSERT(progA->Target == progB->Target);
482
483 newInst = _mesa_alloc_instructions(newLength);
484 if (!newInst)
485 return GL_FALSE;
486
487 _mesa_copy_instructions(newInst, progA->Instructions, lenA);
488 _mesa_copy_instructions(newInst + lenA, progB->Instructions, lenB);
489
490 /* adjust branch / instruction addresses for B's instructions */
491 for (i = 0; i < lenB; i++) {
492 newInst[lenA + i].BranchTarget += lenA;
493 }
494
495 newProg = ctx->Driver.NewProgram(ctx, progA->Target, 0);
496 newProg->Instructions = newInst;
497 newProg->NumInstructions = newLength;
498
499 if (newProg->Target == GL_FRAGMENT_PROGRAM_ARB) {
500 struct gl_fragment_program *fprogA, *fprogB, *newFprog;
501 fprogA = (struct gl_fragment_program *) progA;
502 fprogB = (struct gl_fragment_program *) progB;
503 newFprog = (struct gl_fragment_program *) newProg;
504
505 newFprog->UsesKill = fprogA->UsesKill || fprogB->UsesKill;
506
507 /* connect color outputs/inputs */
508 if ((progA->OutputsWritten & (1 << FRAG_RESULT_COLR)) &&
509 (progB->InputsRead & (1 << FRAG_ATTRIB_COL0))) {
510 replace_registers(newInst + lenA, lenB,
511 PROGRAM_INPUT, FRAG_ATTRIB_COL0,
512 PROGRAM_OUTPUT, FRAG_RESULT_COLR);
513 }
514
515 inputsB = progB->InputsRead;
516 if (progA->OutputsWritten & (1 << FRAG_RESULT_COLR)) {
517 inputsB &= ~(1 << FRAG_ATTRIB_COL0);
518 }
519 newProg->InputsRead = progA->InputsRead | inputsB;
520 newProg->OutputsWritten = progB->OutputsWritten;
521 }
522 else {
523 /* vertex program */
524 assert(0); /* XXX todo */
525 }
526
527 /*
528 * Merge parameters (uniforms, constants, etc)
529 */
530 newProg->Parameters = _mesa_combine_parameter_lists(progA->Parameters,
531 progB->Parameters);
532
533 adjust_param_indexes(newInst + lenA, lenB, numParamsA);
534
535
536 return newProg;
537 }
538
539
540
541
542 /**
543 * Scan the given program to find a free register of the given type.
544 * \param regFile - PROGRAM_INPUT, PROGRAM_OUTPUT or PROGRAM_TEMPORARY
545 */
546 GLint
547 _mesa_find_free_register(const struct gl_program *prog, GLuint regFile)
548 {
549 GLboolean used[MAX_PROGRAM_TEMPS];
550 GLuint i, k;
551
552 assert(regFile == PROGRAM_INPUT ||
553 regFile == PROGRAM_OUTPUT ||
554 regFile == PROGRAM_TEMPORARY);
555
556 _mesa_memset(used, 0, sizeof(used));
557
558 for (i = 0; i < prog->NumInstructions; i++) {
559 const struct prog_instruction *inst = prog->Instructions + i;
560 const GLuint n = _mesa_num_inst_src_regs(inst->Opcode);
561
562 for (k = 0; k < n; k++) {
563 if (inst->SrcReg[k].File == regFile) {
564 used[inst->SrcReg[k].Index] = GL_TRUE;
565 }
566 }
567 }
568
569 for (i = 0; i < MAX_PROGRAM_TEMPS; i++) {
570 if (!used[i])
571 return i;
572 }
573
574 return -1;
575 }
576
577
578
579 /**
580 * Mixing ARB and NV vertex/fragment programs can be tricky.
581 * Note: GL_VERTEX_PROGRAM_ARB == GL_VERTEX_PROGRAM_NV
582 * but, GL_FRAGMENT_PROGRAM_ARB != GL_FRAGMENT_PROGRAM_NV
583 * The two different fragment program targets are supposed to be compatible
584 * to some extent (see GL_ARB_fragment_program spec).
585 * This function does the compatibility check.
586 */
587 static GLboolean
588 compatible_program_targets(GLenum t1, GLenum t2)
589 {
590 if (t1 == t2)
591 return GL_TRUE;
592 if (t1 == GL_FRAGMENT_PROGRAM_ARB && t2 == GL_FRAGMENT_PROGRAM_NV)
593 return GL_TRUE;
594 if (t1 == GL_FRAGMENT_PROGRAM_NV && t2 == GL_FRAGMENT_PROGRAM_ARB)
595 return GL_TRUE;
596 return GL_FALSE;
597 }
598
599
600
601 /**********************************************************************/
602 /* API functions */
603 /**********************************************************************/
604
605
606 /**
607 * Bind a program (make it current)
608 * \note Called from the GL API dispatcher by both glBindProgramNV
609 * and glBindProgramARB.
610 */
611 void GLAPIENTRY
612 _mesa_BindProgram(GLenum target, GLuint id)
613 {
614 struct gl_program *curProg, *newProg;
615 GET_CURRENT_CONTEXT(ctx);
616 ASSERT_OUTSIDE_BEGIN_END(ctx);
617
618 FLUSH_VERTICES(ctx, _NEW_PROGRAM);
619
620 /* Error-check target and get curProg */
621 if ((target == GL_VERTEX_PROGRAM_ARB) && /* == GL_VERTEX_PROGRAM_NV */
622 (ctx->Extensions.NV_vertex_program ||
623 ctx->Extensions.ARB_vertex_program)) {
624 curProg = &ctx->VertexProgram.Current->Base;
625 }
626 else if ((target == GL_FRAGMENT_PROGRAM_NV
627 && ctx->Extensions.NV_fragment_program) ||
628 (target == GL_FRAGMENT_PROGRAM_ARB
629 && ctx->Extensions.ARB_fragment_program)) {
630 curProg = &ctx->FragmentProgram.Current->Base;
631 }
632 else {
633 _mesa_error(ctx, GL_INVALID_ENUM, "glBindProgramNV/ARB(target)");
634 return;
635 }
636
637 /*
638 * Get pointer to new program to bind.
639 * NOTE: binding to a non-existant program is not an error.
640 * That's supposed to be caught in glBegin.
641 */
642 if (id == 0) {
643 /* Bind a default program */
644 newProg = NULL;
645 if (target == GL_VERTEX_PROGRAM_ARB) /* == GL_VERTEX_PROGRAM_NV */
646 newProg = ctx->Shared->DefaultVertexProgram;
647 else
648 newProg = ctx->Shared->DefaultFragmentProgram;
649 }
650 else {
651 /* Bind a user program */
652 newProg = _mesa_lookup_program(ctx, id);
653 if (!newProg || newProg == &_mesa_DummyProgram) {
654 /* allocate a new program now */
655 newProg = ctx->Driver.NewProgram(ctx, target, id);
656 if (!newProg) {
657 _mesa_error(ctx, GL_OUT_OF_MEMORY, "glBindProgramNV/ARB");
658 return;
659 }
660 _mesa_HashInsert(ctx->Shared->Programs, id, newProg);
661 }
662 else if (!compatible_program_targets(newProg->Target, target)) {
663 _mesa_error(ctx, GL_INVALID_OPERATION,
664 "glBindProgramNV/ARB(target mismatch)");
665 return;
666 }
667 }
668
669 /** All error checking is complete now **/
670
671 if (curProg->Id == id) {
672 /* binding same program - no change */
673 return;
674 }
675
676 /* unbind/delete oldProg */
677 if (curProg->Id != 0) {
678 /* decrement refcount on previously bound fragment program */
679 curProg->RefCount--;
680 /* and delete if refcount goes below one */
681 if (curProg->RefCount <= 0) {
682 /* the program ID was already removed from the hash table */
683 ctx->Driver.DeleteProgram(ctx, curProg);
684 }
685 }
686
687 /* bind newProg */
688 if (target == GL_VERTEX_PROGRAM_ARB) { /* == GL_VERTEX_PROGRAM_NV */
689 ctx->VertexProgram.Current = (struct gl_vertex_program *) newProg;
690 }
691 else if (target == GL_FRAGMENT_PROGRAM_NV ||
692 target == GL_FRAGMENT_PROGRAM_ARB) {
693 ctx->FragmentProgram.Current = (struct gl_fragment_program *) newProg;
694 }
695 newProg->RefCount++;
696
697 /* Never null pointers */
698 ASSERT(ctx->VertexProgram.Current);
699 ASSERT(ctx->FragmentProgram.Current);
700
701 if (ctx->Driver.BindProgram)
702 ctx->Driver.BindProgram(ctx, target, newProg);
703 }
704
705
706 /**
707 * Delete a list of programs.
708 * \note Not compiled into display lists.
709 * \note Called by both glDeleteProgramsNV and glDeleteProgramsARB.
710 */
711 void GLAPIENTRY
712 _mesa_DeletePrograms(GLsizei n, const GLuint *ids)
713 {
714 GLint i;
715 GET_CURRENT_CONTEXT(ctx);
716 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx);
717
718 if (n < 0) {
719 _mesa_error( ctx, GL_INVALID_VALUE, "glDeleteProgramsNV" );
720 return;
721 }
722
723 for (i = 0; i < n; i++) {
724 if (ids[i] != 0) {
725 struct gl_program *prog = _mesa_lookup_program(ctx, ids[i]);
726 if (prog == &_mesa_DummyProgram) {
727 _mesa_HashRemove(ctx->Shared->Programs, ids[i]);
728 }
729 else if (prog) {
730 /* Unbind program if necessary */
731 if (prog->Target == GL_VERTEX_PROGRAM_ARB || /* == GL_VERTEX_PROGRAM_NV */
732 prog->Target == GL_VERTEX_STATE_PROGRAM_NV) {
733 if (ctx->VertexProgram.Current &&
734 ctx->VertexProgram.Current->Base.Id == ids[i]) {
735 /* unbind this currently bound program */
736 _mesa_BindProgram(prog->Target, 0);
737 }
738 }
739 else if (prog->Target == GL_FRAGMENT_PROGRAM_NV ||
740 prog->Target == GL_FRAGMENT_PROGRAM_ARB) {
741 if (ctx->FragmentProgram.Current &&
742 ctx->FragmentProgram.Current->Base.Id == ids[i]) {
743 /* unbind this currently bound program */
744 _mesa_BindProgram(prog->Target, 0);
745 }
746 }
747 else {
748 _mesa_problem(ctx, "bad target in glDeleteProgramsNV");
749 return;
750 }
751 /* The ID is immediately available for re-use now */
752 _mesa_HashRemove(ctx->Shared->Programs, ids[i]);
753 prog->RefCount--;
754 if (prog->RefCount <= 0) {
755 ctx->Driver.DeleteProgram(ctx, prog);
756 }
757 }
758 }
759 }
760 }
761
762
763 /**
764 * Generate a list of new program identifiers.
765 * \note Not compiled into display lists.
766 * \note Called by both glGenProgramsNV and glGenProgramsARB.
767 */
768 void GLAPIENTRY
769 _mesa_GenPrograms(GLsizei n, GLuint *ids)
770 {
771 GLuint first;
772 GLuint i;
773 GET_CURRENT_CONTEXT(ctx);
774 ASSERT_OUTSIDE_BEGIN_END(ctx);
775
776 if (n < 0) {
777 _mesa_error(ctx, GL_INVALID_VALUE, "glGenPrograms");
778 return;
779 }
780
781 if (!ids)
782 return;
783
784 first = _mesa_HashFindFreeKeyBlock(ctx->Shared->Programs, n);
785
786 /* Insert pointer to dummy program as placeholder */
787 for (i = 0; i < (GLuint) n; i++) {
788 _mesa_HashInsert(ctx->Shared->Programs, first + i, &_mesa_DummyProgram);
789 }
790
791 /* Return the program names */
792 for (i = 0; i < (GLuint) n; i++) {
793 ids[i] = first + i;
794 }
795 }