2 * Mesa 3-D graphics library
5 * Copyright (C) 1999-2007 Brian Paul All Rights Reserved.
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:
14 * The above copyright notice and this permission notice shall be included
15 * in all copies or substantial portions of the Software.
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.
27 * Vertex and fragment program support functions.
36 #include "prog_cache.h"
37 #include "prog_parameter.h"
38 #include "prog_instruction.h"
42 * A pointer to this dummy program is put into the hash table when
43 * glGenPrograms is called.
45 struct gl_program _mesa_DummyProgram
;
49 * Init context's vertex/fragment program state
52 _mesa_init_program(GLcontext
*ctx
)
56 ctx
->Program
.ErrorPos
= -1;
57 ctx
->Program
.ErrorString
= _mesa_strdup("");
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
;
70 ctx
->VertexProgram
.Cache
= _mesa_new_program_cache();
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();
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
++;
93 * Free a context's vertex/fragment program state
96 _mesa_free_program_data(GLcontext
*ctx
)
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
));
105 #if FEATURE_NV_fragment_program || FEATURE_ARB_fragment_program
106 if (ctx
->FragmentProgram
.Current
) {
107 ctx
->FragmentProgram
.Current
->Base
.RefCount
--;
108 if (ctx
->FragmentProgram
.Current
->Base
.RefCount
<= 0)
109 ctx
->Driver
.DeleteProgram(ctx
, &(ctx
->FragmentProgram
.Current
->Base
));
112 /* XXX probably move this stuff */
113 #if FEATURE_ATI_fragment_shader
114 if (ctx
->ATIFragmentShader
.Current
) {
115 ctx
->ATIFragmentShader
.Current
->RefCount
--;
116 if (ctx
->ATIFragmentShader
.Current
->RefCount
<= 0) {
117 _mesa_free(ctx
->ATIFragmentShader
.Current
);
121 _mesa_free((void *) ctx
->Program
.ErrorString
);
128 * Set the vertex/fragment program error state (position and error string).
129 * This is generally called from within the parsers.
132 _mesa_set_program_error(GLcontext
*ctx
, GLint pos
, const char *string
)
134 ctx
->Program
.ErrorPos
= pos
;
135 _mesa_free((void *) ctx
->Program
.ErrorString
);
138 ctx
->Program
.ErrorString
= _mesa_strdup(string
);
143 * Find the line number and column for 'pos' within 'string'.
144 * Return a copy of the line which contains 'pos'. Free the line with
146 * \param string the program string
147 * \param pos the position within the string
148 * \param line returns the line number corresponding to 'pos'.
149 * \param col returns the column number corresponding to 'pos'.
150 * \return copy of the line containing 'pos'.
153 _mesa_find_line_column(const GLubyte
*string
, const GLubyte
*pos
,
154 GLint
*line
, GLint
*col
)
156 const GLubyte
*lineStart
= string
;
157 const GLubyte
*p
= string
;
164 if (*p
== (GLubyte
) '\n') {
171 *col
= (pos
- lineStart
) + 1;
173 /* return copy of this line */
174 while (*p
!= 0 && *p
!= '\n')
177 s
= (GLubyte
*) _mesa_malloc(len
+ 1);
178 _mesa_memcpy(s
, lineStart
, len
);
186 * Initialize a new vertex/fragment program object.
188 static struct gl_program
*
189 _mesa_init_program_struct( GLcontext
*ctx
, struct gl_program
*prog
,
190 GLenum target
, GLuint id
)
195 _mesa_bzero(prog
, sizeof(*prog
));
197 prog
->Target
= target
;
198 prog
->Resident
= GL_TRUE
;
200 prog
->Format
= GL_PROGRAM_FORMAT_ASCII_ARB
;
202 /* default mapping from samplers to texture units */
203 for (i
= 0; i
< MAX_SAMPLERS
; i
++)
204 prog
->SamplerUnits
[i
] = i
;
212 * Initialize a new fragment program object.
215 _mesa_init_fragment_program( GLcontext
*ctx
, struct gl_fragment_program
*prog
,
216 GLenum target
, GLuint id
)
219 return _mesa_init_program_struct( ctx
, &prog
->Base
, target
, id
);
226 * Initialize a new vertex program object.
229 _mesa_init_vertex_program( GLcontext
*ctx
, struct gl_vertex_program
*prog
,
230 GLenum target
, GLuint id
)
233 return _mesa_init_program_struct( ctx
, &prog
->Base
, target
, id
);
240 * Allocate and initialize a new fragment/vertex program object but
241 * don't put it into the program hash table. Called via
242 * ctx->Driver.NewProgram. May be overridden (ie. replaced) by a
243 * device driver function to implement OO deriviation with additional
244 * types not understood by this function.
247 * \param id program id/number
248 * \param target program target/type
249 * \return pointer to new program object
252 _mesa_new_program(GLcontext
*ctx
, GLenum target
, GLuint id
)
255 case GL_VERTEX_PROGRAM_ARB
: /* == GL_VERTEX_PROGRAM_NV */
256 return _mesa_init_vertex_program(ctx
, CALLOC_STRUCT(gl_vertex_program
),
258 case GL_FRAGMENT_PROGRAM_NV
:
259 case GL_FRAGMENT_PROGRAM_ARB
:
260 return _mesa_init_fragment_program(ctx
,
261 CALLOC_STRUCT(gl_fragment_program
),
264 _mesa_problem(ctx
, "bad target in _mesa_new_program");
271 * Delete a program and remove it from the hash table, ignoring the
273 * Called via ctx->Driver.DeleteProgram. May be wrapped (OO deriviation)
274 * by a device driver function.
277 _mesa_delete_program(GLcontext
*ctx
, struct gl_program
*prog
)
282 if (prog
== &_mesa_DummyProgram
)
286 _mesa_free(prog
->String
);
288 if (prog
->Instructions
) {
290 for (i
= 0; i
< prog
->NumInstructions
; i
++) {
291 if (prog
->Instructions
[i
].Data
)
292 _mesa_free(prog
->Instructions
[i
].Data
);
293 if (prog
->Instructions
[i
].Comment
)
294 _mesa_free((char *) prog
->Instructions
[i
].Comment
);
296 _mesa_free(prog
->Instructions
);
299 if (prog
->Parameters
) {
300 _mesa_free_parameter_list(prog
->Parameters
);
303 _mesa_free_parameter_list(prog
->Varying
);
305 if (prog
->Attributes
) {
306 _mesa_free_parameter_list(prog
->Attributes
);
309 /* XXX this is a little ugly */
310 if (prog
->Target
== GL_VERTEX_PROGRAM_ARB
) {
311 struct gl_vertex_program
*vprog
= (struct gl_vertex_program
*) prog
;
313 _mesa_free(vprog
->TnlData
);
321 * Return the gl_program object for a given ID.
322 * Basically just a wrapper for _mesa_HashLookup() to avoid a lot of
326 _mesa_lookup_program(GLcontext
*ctx
, GLuint id
)
329 return (struct gl_program
*) _mesa_HashLookup(ctx
->Shared
->Programs
, id
);
336 * Return a copy of a program.
337 * XXX Problem here if the program object is actually OO-derivation
338 * made by a device driver.
341 _mesa_clone_program(GLcontext
*ctx
, const struct gl_program
*prog
)
343 struct gl_program
*clone
;
345 clone
= ctx
->Driver
.NewProgram(ctx
, prog
->Target
, prog
->Id
);
349 assert(clone
->Target
== prog
->Target
);
350 clone
->String
= (GLubyte
*) _mesa_strdup((char *) prog
->String
);
352 clone
->Format
= prog
->Format
;
353 clone
->Instructions
= _mesa_alloc_instructions(prog
->NumInstructions
);
354 if (!clone
->Instructions
) {
355 _mesa_delete_program(ctx
, clone
);
358 _mesa_copy_instructions(clone
->Instructions
, prog
->Instructions
,
359 prog
->NumInstructions
);
360 clone
->InputsRead
= prog
->InputsRead
;
361 clone
->OutputsWritten
= prog
->OutputsWritten
;
362 memcpy(clone
->TexturesUsed
, prog
->TexturesUsed
, sizeof(prog
->TexturesUsed
));
364 if (prog
->Parameters
)
365 clone
->Parameters
= _mesa_clone_parameter_list(prog
->Parameters
);
366 memcpy(clone
->LocalParams
, prog
->LocalParams
, sizeof(clone
->LocalParams
));
368 clone
->Varying
= _mesa_clone_parameter_list(prog
->Varying
);
369 if (prog
->Attributes
)
370 clone
->Attributes
= _mesa_clone_parameter_list(prog
->Attributes
);
371 memcpy(clone
->LocalParams
, prog
->LocalParams
, sizeof(clone
->LocalParams
));
372 clone
->NumInstructions
= prog
->NumInstructions
;
373 clone
->NumTemporaries
= prog
->NumTemporaries
;
374 clone
->NumParameters
= prog
->NumParameters
;
375 clone
->NumAttributes
= prog
->NumAttributes
;
376 clone
->NumAddressRegs
= prog
->NumAddressRegs
;
377 clone
->NumNativeInstructions
= prog
->NumNativeInstructions
;
378 clone
->NumNativeTemporaries
= prog
->NumNativeTemporaries
;
379 clone
->NumNativeParameters
= prog
->NumNativeParameters
;
380 clone
->NumNativeAttributes
= prog
->NumNativeAttributes
;
381 clone
->NumNativeAddressRegs
= prog
->NumNativeAddressRegs
;
382 clone
->NumAluInstructions
= prog
->NumAluInstructions
;
383 clone
->NumTexInstructions
= prog
->NumTexInstructions
;
384 clone
->NumTexIndirections
= prog
->NumTexIndirections
;
385 clone
->NumNativeAluInstructions
= prog
->NumNativeAluInstructions
;
386 clone
->NumNativeTexInstructions
= prog
->NumNativeTexInstructions
;
387 clone
->NumNativeTexIndirections
= prog
->NumNativeTexIndirections
;
389 switch (prog
->Target
) {
390 case GL_VERTEX_PROGRAM_ARB
:
392 const struct gl_vertex_program
*vp
393 = (const struct gl_vertex_program
*) prog
;
394 struct gl_vertex_program
*vpc
= (struct gl_vertex_program
*) clone
;
395 vpc
->IsPositionInvariant
= vp
->IsPositionInvariant
;
398 case GL_FRAGMENT_PROGRAM_ARB
:
400 const struct gl_fragment_program
*fp
401 = (const struct gl_fragment_program
*) prog
;
402 struct gl_fragment_program
*fpc
= (struct gl_fragment_program
*) clone
;
403 fpc
->FogOption
= fp
->FogOption
;
404 fpc
->UsesKill
= fp
->UsesKill
;
408 _mesa_problem(NULL
, "Unexpected target in _mesa_clone_program");
417 * Search instructions for registers that match (oldFile, oldIndex),
418 * replacing them with (newFile, newIndex).
421 replace_registers(struct prog_instruction
*inst
, GLuint numInst
,
422 GLuint oldFile
, GLuint oldIndex
,
423 GLuint newFile
, GLuint newIndex
)
426 for (i
= 0; i
< numInst
; i
++) {
427 for (j
= 0; j
< _mesa_num_inst_src_regs(inst
->Opcode
); j
++) {
428 if (inst
[i
].SrcReg
[j
].File
== oldFile
&&
429 inst
[i
].SrcReg
[j
].Index
== oldIndex
) {
430 inst
[i
].SrcReg
[j
].File
= newFile
;
431 inst
[i
].SrcReg
[j
].Index
= newIndex
;
439 * Search instructions for references to program parameters. When found,
440 * increment the parameter index by 'offset'.
441 * Used when combining programs.
444 adjust_param_indexes(struct prog_instruction
*inst
, GLuint numInst
,
448 for (i
= 0; i
< numInst
; i
++) {
449 for (j
= 0; j
< _mesa_num_inst_src_regs(inst
->Opcode
); j
++) {
450 GLuint f
= inst
[i
].SrcReg
[j
].File
;
451 if (f
== PROGRAM_CONSTANT
||
452 f
== PROGRAM_UNIFORM
||
453 f
== PROGRAM_STATE_VAR
) {
454 inst
[i
].SrcReg
[j
].Index
+= offset
;
462 * Combine two programs into one. Fix instructions so the outputs of
463 * the first program go to the inputs of the second program.
466 _mesa_combine_programs(GLcontext
*ctx
,
467 const struct gl_program
*progA
,
468 const struct gl_program
*progB
)
470 struct prog_instruction
*newInst
;
471 struct gl_program
*newProg
;
472 const GLuint lenA
= progA
->NumInstructions
- 1; /* omit END instr */
473 const GLuint lenB
= progB
->NumInstructions
;
474 const GLuint numParamsA
= _mesa_num_parameters(progA
->Parameters
);
475 const GLuint newLength
= lenA
+ lenB
;
479 ASSERT(progA
->Target
== progB
->Target
);
481 newInst
= _mesa_alloc_instructions(newLength
);
485 _mesa_copy_instructions(newInst
, progA
->Instructions
, lenA
);
486 _mesa_copy_instructions(newInst
+ lenA
, progB
->Instructions
, lenB
);
488 /* adjust branch / instruction addresses for B's instructions */
489 for (i
= 0; i
< lenB
; i
++) {
490 newInst
[lenA
+ i
].BranchTarget
+= lenA
;
493 newProg
= ctx
->Driver
.NewProgram(ctx
, progA
->Target
, 0);
494 newProg
->Instructions
= newInst
;
495 newProg
->NumInstructions
= newLength
;
497 if (newProg
->Target
== GL_FRAGMENT_PROGRAM_ARB
) {
498 /* connect color outputs/inputs */
499 if ((progA
->OutputsWritten
& (1 << FRAG_RESULT_COLR
)) &&
500 (progB
->InputsRead
& (1 << FRAG_ATTRIB_COL0
))) {
501 replace_registers(newInst
+ lenA
, lenB
,
502 PROGRAM_INPUT
, FRAG_ATTRIB_COL0
,
503 PROGRAM_OUTPUT
, FRAG_RESULT_COLR
);
506 inputsB
= progB
->InputsRead
;
507 if (progA
->OutputsWritten
& (1 << FRAG_RESULT_COLR
)) {
508 inputsB
&= ~(1 << FRAG_ATTRIB_COL0
);
510 newProg
->InputsRead
= progA
->InputsRead
| inputsB
;
511 newProg
->OutputsWritten
= progB
->OutputsWritten
;
515 assert(0); /* XXX todo */
519 * Merge parameters (uniforms, constants, etc)
521 newProg
->Parameters
= _mesa_combine_parameter_lists(progA
->Parameters
,
524 adjust_param_indexes(newInst
+ lenA
, lenB
, numParamsA
);
534 * Scan the given program to find a free register of the given type.
535 * \param regFile - PROGRAM_INPUT, PROGRAM_OUTPUT or PROGRAM_TEMPORARY
538 _mesa_find_free_register(const struct gl_program
*prog
, GLuint regFile
)
540 GLboolean used
[MAX_PROGRAM_TEMPS
];
543 assert(regFile
== PROGRAM_INPUT
||
544 regFile
== PROGRAM_OUTPUT
||
545 regFile
== PROGRAM_TEMPORARY
);
547 _mesa_memset(used
, 0, sizeof(used
));
549 for (i
= 0; i
< prog
->NumInstructions
; i
++) {
550 const struct prog_instruction
*inst
= prog
->Instructions
+ i
;
551 const GLuint n
= _mesa_num_inst_src_regs(inst
->Opcode
);
553 for (k
= 0; k
< n
; k
++) {
554 if (inst
->SrcReg
[k
].File
== regFile
) {
555 used
[inst
->SrcReg
[k
].Index
] = GL_TRUE
;
560 for (i
= 0; i
< MAX_PROGRAM_TEMPS
; i
++) {
571 * Mixing ARB and NV vertex/fragment programs can be tricky.
572 * Note: GL_VERTEX_PROGRAM_ARB == GL_VERTEX_PROGRAM_NV
573 * but, GL_FRAGMENT_PROGRAM_ARB != GL_FRAGMENT_PROGRAM_NV
574 * The two different fragment program targets are supposed to be compatible
575 * to some extent (see GL_ARB_fragment_program spec).
576 * This function does the compatibility check.
579 compatible_program_targets(GLenum t1
, GLenum t2
)
583 if (t1
== GL_FRAGMENT_PROGRAM_ARB
&& t2
== GL_FRAGMENT_PROGRAM_NV
)
585 if (t1
== GL_FRAGMENT_PROGRAM_NV
&& t2
== GL_FRAGMENT_PROGRAM_ARB
)
592 /**********************************************************************/
594 /**********************************************************************/
598 * Bind a program (make it current)
599 * \note Called from the GL API dispatcher by both glBindProgramNV
600 * and glBindProgramARB.
603 _mesa_BindProgram(GLenum target
, GLuint id
)
605 struct gl_program
*curProg
, *newProg
;
606 GET_CURRENT_CONTEXT(ctx
);
607 ASSERT_OUTSIDE_BEGIN_END(ctx
);
609 FLUSH_VERTICES(ctx
, _NEW_PROGRAM
);
611 /* Error-check target and get curProg */
612 if ((target
== GL_VERTEX_PROGRAM_ARB
) && /* == GL_VERTEX_PROGRAM_NV */
613 (ctx
->Extensions
.NV_vertex_program
||
614 ctx
->Extensions
.ARB_vertex_program
)) {
615 curProg
= &ctx
->VertexProgram
.Current
->Base
;
617 else if ((target
== GL_FRAGMENT_PROGRAM_NV
618 && ctx
->Extensions
.NV_fragment_program
) ||
619 (target
== GL_FRAGMENT_PROGRAM_ARB
620 && ctx
->Extensions
.ARB_fragment_program
)) {
621 curProg
= &ctx
->FragmentProgram
.Current
->Base
;
624 _mesa_error(ctx
, GL_INVALID_ENUM
, "glBindProgramNV/ARB(target)");
629 * Get pointer to new program to bind.
630 * NOTE: binding to a non-existant program is not an error.
631 * That's supposed to be caught in glBegin.
634 /* Bind a default program */
636 if (target
== GL_VERTEX_PROGRAM_ARB
) /* == GL_VERTEX_PROGRAM_NV */
637 newProg
= ctx
->Shared
->DefaultVertexProgram
;
639 newProg
= ctx
->Shared
->DefaultFragmentProgram
;
642 /* Bind a user program */
643 newProg
= _mesa_lookup_program(ctx
, id
);
644 if (!newProg
|| newProg
== &_mesa_DummyProgram
) {
645 /* allocate a new program now */
646 newProg
= ctx
->Driver
.NewProgram(ctx
, target
, id
);
648 _mesa_error(ctx
, GL_OUT_OF_MEMORY
, "glBindProgramNV/ARB");
651 _mesa_HashInsert(ctx
->Shared
->Programs
, id
, newProg
);
653 else if (!compatible_program_targets(newProg
->Target
, target
)) {
654 _mesa_error(ctx
, GL_INVALID_OPERATION
,
655 "glBindProgramNV/ARB(target mismatch)");
660 /** All error checking is complete now **/
662 if (curProg
->Id
== id
) {
663 /* binding same program - no change */
667 /* unbind/delete oldProg */
668 if (curProg
->Id
!= 0) {
669 /* decrement refcount on previously bound fragment program */
671 /* and delete if refcount goes below one */
672 if (curProg
->RefCount
<= 0) {
673 /* the program ID was already removed from the hash table */
674 ctx
->Driver
.DeleteProgram(ctx
, curProg
);
679 if (target
== GL_VERTEX_PROGRAM_ARB
) { /* == GL_VERTEX_PROGRAM_NV */
680 ctx
->VertexProgram
.Current
= (struct gl_vertex_program
*) newProg
;
682 else if (target
== GL_FRAGMENT_PROGRAM_NV
||
683 target
== GL_FRAGMENT_PROGRAM_ARB
) {
684 ctx
->FragmentProgram
.Current
= (struct gl_fragment_program
*) newProg
;
688 /* Never null pointers */
689 ASSERT(ctx
->VertexProgram
.Current
);
690 ASSERT(ctx
->FragmentProgram
.Current
);
692 if (ctx
->Driver
.BindProgram
)
693 ctx
->Driver
.BindProgram(ctx
, target
, newProg
);
698 * Delete a list of programs.
699 * \note Not compiled into display lists.
700 * \note Called by both glDeleteProgramsNV and glDeleteProgramsARB.
703 _mesa_DeletePrograms(GLsizei n
, const GLuint
*ids
)
706 GET_CURRENT_CONTEXT(ctx
);
707 ASSERT_OUTSIDE_BEGIN_END_AND_FLUSH(ctx
);
710 _mesa_error( ctx
, GL_INVALID_VALUE
, "glDeleteProgramsNV" );
714 for (i
= 0; i
< n
; i
++) {
716 struct gl_program
*prog
= _mesa_lookup_program(ctx
, ids
[i
]);
717 if (prog
== &_mesa_DummyProgram
) {
718 _mesa_HashRemove(ctx
->Shared
->Programs
, ids
[i
]);
721 /* Unbind program if necessary */
722 if (prog
->Target
== GL_VERTEX_PROGRAM_ARB
|| /* == GL_VERTEX_PROGRAM_NV */
723 prog
->Target
== GL_VERTEX_STATE_PROGRAM_NV
) {
724 if (ctx
->VertexProgram
.Current
&&
725 ctx
->VertexProgram
.Current
->Base
.Id
== ids
[i
]) {
726 /* unbind this currently bound program */
727 _mesa_BindProgram(prog
->Target
, 0);
730 else if (prog
->Target
== GL_FRAGMENT_PROGRAM_NV
||
731 prog
->Target
== GL_FRAGMENT_PROGRAM_ARB
) {
732 if (ctx
->FragmentProgram
.Current
&&
733 ctx
->FragmentProgram
.Current
->Base
.Id
== ids
[i
]) {
734 /* unbind this currently bound program */
735 _mesa_BindProgram(prog
->Target
, 0);
739 _mesa_problem(ctx
, "bad target in glDeleteProgramsNV");
742 /* The ID is immediately available for re-use now */
743 _mesa_HashRemove(ctx
->Shared
->Programs
, ids
[i
]);
745 if (prog
->RefCount
<= 0) {
746 ctx
->Driver
.DeleteProgram(ctx
, prog
);
755 * Generate a list of new program identifiers.
756 * \note Not compiled into display lists.
757 * \note Called by both glGenProgramsNV and glGenProgramsARB.
760 _mesa_GenPrograms(GLsizei n
, GLuint
*ids
)
764 GET_CURRENT_CONTEXT(ctx
);
765 ASSERT_OUTSIDE_BEGIN_END(ctx
);
768 _mesa_error(ctx
, GL_INVALID_VALUE
, "glGenPrograms");
775 first
= _mesa_HashFindFreeKeyBlock(ctx
->Shared
->Programs
, n
);
777 /* Insert pointer to dummy program as placeholder */
778 for (i
= 0; i
< (GLuint
) n
; i
++) {
779 _mesa_HashInsert(ctx
->Shared
->Programs
, first
+ i
, &_mesa_DummyProgram
);
782 /* Return the program names */
783 for (i
= 0; i
< (GLuint
) n
; i
++) {