1 /*
2  * Mesa 3-D graphics library
3  *
4  * Copyright (C) 2004-2008  Brian Paul   All Rights Reserved.
5  * Copyright (C) 2009-2010  VMware, Inc.  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 shaderobj.c
27  * \author Brian Paul
28  *
29  */
30 
31 
32 #include "main/glheader.h"
33 #include "main/context.h"
34 #include "main/hash.h"
35 #include "main/mfeatures.h"
36 #include "main/mtypes.h"
37 #include "main/shaderobj.h"
38 #include "main/uniforms.h"
39 #include "program/program.h"
40 #include "program/prog_parameter.h"
41 #include "program/hash_table.h"
42 #include "ralloc.h"
43 
44 /**********************************************************************/
45 /*** Shader object functions                                        ***/
46 /**********************************************************************/
47 
48 
49 /**
50  * Set ptr to point to sh.
51  * If ptr is pointing to another shader, decrement its refcount (and delete
52  * if refcount hits zero).
53  * Then set ptr to point to sh, incrementing its refcount.
54  */
55 void
_mesa_reference_shader(struct gl_context * ctx,struct gl_shader ** ptr,struct gl_shader * sh)56 _mesa_reference_shader(struct gl_context *ctx, struct gl_shader **ptr,
57                        struct gl_shader *sh)
58 {
59    assert(ptr);
60    if (*ptr == sh) {
61       /* no-op */
62       return;
63    }
64    if (*ptr) {
65       /* Unreference the old shader */
66       GLboolean deleteFlag = GL_FALSE;
67       struct gl_shader *old = *ptr;
68 
69       ASSERT(old->RefCount > 0);
70       old->RefCount--;
71       /*printf("SHADER DECR %p (%d) to %d\n",
72         (void*) old, old->Name, old->RefCount);*/
73       deleteFlag = (old->RefCount == 0);
74 
75       if (deleteFlag) {
76 	 if (old->Name != 0)
77 	    _mesa_HashRemove(ctx->Shared->ShaderObjects, old->Name);
78          ctx->Driver.DeleteShader(ctx, old);
79       }
80 
81       *ptr = NULL;
82    }
83    assert(!*ptr);
84 
85    if (sh) {
86       /* reference new */
87       sh->RefCount++;
88       /*printf("SHADER INCR %p (%d) to %d\n",
89         (void*) sh, sh->Name, sh->RefCount);*/
90       *ptr = sh;
91    }
92 }
93 
94 void
_mesa_init_shader(struct gl_context * ctx,struct gl_shader * shader)95 _mesa_init_shader(struct gl_context *ctx, struct gl_shader *shader)
96 {
97    shader->RefCount = 1;
98 }
99 
100 /**
101  * Allocate a new gl_shader object, initialize it.
102  * Called via ctx->Driver.NewShader()
103  */
104 struct gl_shader *
_mesa_new_shader(struct gl_context * ctx,GLuint name,GLenum type)105 _mesa_new_shader(struct gl_context *ctx, GLuint name, GLenum type)
106 {
107    struct gl_shader *shader;
108    assert(type == GL_FRAGMENT_SHADER || type == GL_VERTEX_SHADER ||
109           type == GL_GEOMETRY_SHADER_ARB);
110    shader = rzalloc(NULL, struct gl_shader);
111    if (shader) {
112       shader->Type = type;
113       shader->Name = name;
114       _mesa_init_shader(ctx, shader);
115    }
116    return shader;
117 }
118 
119 
120 /**
121  * Delete a shader object.
122  * Called via ctx->Driver.DeleteShader().
123  */
124 static void
_mesa_delete_shader(struct gl_context * ctx,struct gl_shader * sh)125 _mesa_delete_shader(struct gl_context *ctx, struct gl_shader *sh)
126 {
127    if (sh->Source)
128       free((void *) sh->Source);
129    _mesa_reference_program(ctx, &sh->Program, NULL);
130    ralloc_free(sh);
131 }
132 
133 
134 /**
135  * Lookup a GLSL shader object.
136  */
137 struct gl_shader *
_mesa_lookup_shader(struct gl_context * ctx,GLuint name)138 _mesa_lookup_shader(struct gl_context *ctx, GLuint name)
139 {
140    if (name) {
141       struct gl_shader *sh = (struct gl_shader *)
142          _mesa_HashLookup(ctx->Shared->ShaderObjects, name);
143       /* Note that both gl_shader and gl_shader_program objects are kept
144        * in the same hash table.  Check the object's type to be sure it's
145        * what we're expecting.
146        */
147       if (sh && sh->Type == GL_SHADER_PROGRAM_MESA) {
148          return NULL;
149       }
150       return sh;
151    }
152    return NULL;
153 }
154 
155 
156 /**
157  * As above, but record an error if shader is not found.
158  */
159 struct gl_shader *
_mesa_lookup_shader_err(struct gl_context * ctx,GLuint name,const char * caller)160 _mesa_lookup_shader_err(struct gl_context *ctx, GLuint name, const char *caller)
161 {
162    if (!name) {
163       _mesa_error(ctx, GL_INVALID_VALUE, "%s", caller);
164       return NULL;
165    }
166    else {
167       struct gl_shader *sh = (struct gl_shader *)
168          _mesa_HashLookup(ctx->Shared->ShaderObjects, name);
169       if (!sh) {
170          _mesa_error(ctx, GL_INVALID_VALUE, "%s", caller);
171          return NULL;
172       }
173       if (sh->Type == GL_SHADER_PROGRAM_MESA) {
174          _mesa_error(ctx, GL_INVALID_OPERATION, "%s", caller);
175          return NULL;
176       }
177       return sh;
178    }
179 }
180 
181 
182 
183 /**********************************************************************/
184 /*** Shader Program object functions                                ***/
185 /**********************************************************************/
186 
187 
188 /**
189  * Set ptr to point to shProg.
190  * If ptr is pointing to another object, decrement its refcount (and delete
191  * if refcount hits zero).
192  * Then set ptr to point to shProg, incrementing its refcount.
193  */
194 void
_mesa_reference_shader_program(struct gl_context * ctx,struct gl_shader_program ** ptr,struct gl_shader_program * shProg)195 _mesa_reference_shader_program(struct gl_context *ctx,
196                                struct gl_shader_program **ptr,
197                                struct gl_shader_program *shProg)
198 {
199    assert(ptr);
200    if (*ptr == shProg) {
201       /* no-op */
202       return;
203    }
204    if (*ptr) {
205       /* Unreference the old shader program */
206       GLboolean deleteFlag = GL_FALSE;
207       struct gl_shader_program *old = *ptr;
208 
209       ASSERT(old->RefCount > 0);
210       old->RefCount--;
211 #if 0
212       printf("ShaderProgram %p ID=%u  RefCount-- to %d\n",
213              (void *) old, old->Name, old->RefCount);
214 #endif
215       deleteFlag = (old->RefCount == 0);
216 
217       if (deleteFlag) {
218 	 if (old->Name != 0)
219 	    _mesa_HashRemove(ctx->Shared->ShaderObjects, old->Name);
220          ctx->Driver.DeleteShaderProgram(ctx, old);
221       }
222 
223       *ptr = NULL;
224    }
225    assert(!*ptr);
226 
227    if (shProg) {
228       shProg->RefCount++;
229 #if 0
230       printf("ShaderProgram %p ID=%u  RefCount++ to %d\n",
231              (void *) shProg, shProg->Name, shProg->RefCount);
232 #endif
233       *ptr = shProg;
234    }
235 }
236 
237 void
_mesa_init_shader_program(struct gl_context * ctx,struct gl_shader_program * prog)238 _mesa_init_shader_program(struct gl_context *ctx, struct gl_shader_program *prog)
239 {
240    prog->Type = GL_SHADER_PROGRAM_MESA;
241    prog->RefCount = 1;
242 
243    prog->AttributeBindings = string_to_uint_map_ctor();
244    prog->FragDataBindings = string_to_uint_map_ctor();
245    prog->FragDataIndexBindings = string_to_uint_map_ctor();
246 
247 #if FEATURE_ARB_geometry_shader4
248    prog->Geom.VerticesOut = 0;
249    prog->Geom.InputType = GL_TRIANGLES;
250    prog->Geom.OutputType = GL_TRIANGLE_STRIP;
251 #endif
252 
253    prog->TransformFeedback.BufferMode = GL_INTERLEAVED_ATTRIBS;
254 
255    prog->InfoLog = ralloc_strdup(prog, "");
256 }
257 
258 /**
259  * Allocate a new gl_shader_program object, initialize it.
260  * Called via ctx->Driver.NewShaderProgram()
261  */
262 static struct gl_shader_program *
_mesa_new_shader_program(struct gl_context * ctx,GLuint name)263 _mesa_new_shader_program(struct gl_context *ctx, GLuint name)
264 {
265    struct gl_shader_program *shProg;
266    shProg = rzalloc(NULL, struct gl_shader_program);
267    if (shProg) {
268       shProg->Name = name;
269       _mesa_init_shader_program(ctx, shProg);
270    }
271    return shProg;
272 }
273 
274 
275 /**
276  * Clear (free) the shader program state that gets produced by linking.
277  */
278 void
_mesa_clear_shader_program_data(struct gl_context * ctx,struct gl_shader_program * shProg)279 _mesa_clear_shader_program_data(struct gl_context *ctx,
280                                 struct gl_shader_program *shProg)
281 {
282    if (shProg->UniformStorage) {
283       unsigned i;
284       for (i = 0; i < shProg->NumUserUniformStorage; ++i)
285          _mesa_uniform_detach_all_driver_storage(&shProg->UniformStorage[i]);
286       ralloc_free(shProg->UniformStorage);
287       shProg->NumUserUniformStorage = 0;
288       shProg->UniformStorage = NULL;
289    }
290 
291    if (shProg->UniformHash) {
292       string_to_uint_map_dtor(shProg->UniformHash);
293       shProg->UniformHash = NULL;
294    }
295 
296    assert(shProg->InfoLog != NULL);
297    ralloc_free(shProg->InfoLog);
298    shProg->InfoLog = ralloc_strdup(shProg, "");
299 }
300 
301 
302 /**
303  * Free all the data that hangs off a shader program object, but not the
304  * object itself.
305  */
306 void
_mesa_free_shader_program_data(struct gl_context * ctx,struct gl_shader_program * shProg)307 _mesa_free_shader_program_data(struct gl_context *ctx,
308                                struct gl_shader_program *shProg)
309 {
310    GLuint i;
311    gl_shader_type sh;
312 
313    assert(shProg->Type == GL_SHADER_PROGRAM_MESA);
314 
315    _mesa_clear_shader_program_data(ctx, shProg);
316 
317    if (shProg->AttributeBindings) {
318       string_to_uint_map_dtor(shProg->AttributeBindings);
319       shProg->AttributeBindings = NULL;
320    }
321 
322    if (shProg->FragDataBindings) {
323       string_to_uint_map_dtor(shProg->FragDataBindings);
324       shProg->FragDataBindings = NULL;
325    }
326 
327    if (shProg->FragDataIndexBindings) {
328       string_to_uint_map_dtor(shProg->FragDataIndexBindings);
329       shProg->FragDataIndexBindings = NULL;
330    }
331 
332    /* detach shaders */
333    for (i = 0; i < shProg->NumShaders; i++) {
334       _mesa_reference_shader(ctx, &shProg->Shaders[i], NULL);
335    }
336    shProg->NumShaders = 0;
337 
338    if (shProg->Shaders) {
339       free(shProg->Shaders);
340       shProg->Shaders = NULL;
341    }
342 
343    /* Transform feedback varying vars */
344    for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) {
345       free(shProg->TransformFeedback.VaryingNames[i]);
346    }
347    free(shProg->TransformFeedback.VaryingNames);
348    shProg->TransformFeedback.VaryingNames = NULL;
349    shProg->TransformFeedback.NumVarying = 0;
350 
351 
352    for (sh = 0; sh < MESA_SHADER_TYPES; sh++) {
353       if (shProg->_LinkedShaders[sh] != NULL) {
354 	 ctx->Driver.DeleteShader(ctx, shProg->_LinkedShaders[sh]);
355 	 shProg->_LinkedShaders[sh] = NULL;
356       }
357    }
358 }
359 
360 
361 /**
362  * Free/delete a shader program object.
363  * Called via ctx->Driver.DeleteShaderProgram().
364  */
365 static void
_mesa_delete_shader_program(struct gl_context * ctx,struct gl_shader_program * shProg)366 _mesa_delete_shader_program(struct gl_context *ctx, struct gl_shader_program *shProg)
367 {
368    _mesa_free_shader_program_data(ctx, shProg);
369 
370    ralloc_free(shProg);
371 }
372 
373 
374 /**
375  * Lookup a GLSL program object.
376  */
377 struct gl_shader_program *
_mesa_lookup_shader_program(struct gl_context * ctx,GLuint name)378 _mesa_lookup_shader_program(struct gl_context *ctx, GLuint name)
379 {
380    struct gl_shader_program *shProg;
381    if (name) {
382       shProg = (struct gl_shader_program *)
383          _mesa_HashLookup(ctx->Shared->ShaderObjects, name);
384       /* Note that both gl_shader and gl_shader_program objects are kept
385        * in the same hash table.  Check the object's type to be sure it's
386        * what we're expecting.
387        */
388       if (shProg && shProg->Type != GL_SHADER_PROGRAM_MESA) {
389          return NULL;
390       }
391       return shProg;
392    }
393    return NULL;
394 }
395 
396 
397 /**
398  * As above, but record an error if program is not found.
399  */
400 struct gl_shader_program *
_mesa_lookup_shader_program_err(struct gl_context * ctx,GLuint name,const char * caller)401 _mesa_lookup_shader_program_err(struct gl_context *ctx, GLuint name,
402                                 const char *caller)
403 {
404    if (!name) {
405       _mesa_error(ctx, GL_INVALID_VALUE, "%s", caller);
406       return NULL;
407    }
408    else {
409       struct gl_shader_program *shProg = (struct gl_shader_program *)
410          _mesa_HashLookup(ctx->Shared->ShaderObjects, name);
411       if (!shProg) {
412          _mesa_error(ctx, GL_INVALID_VALUE, "%s", caller);
413          return NULL;
414       }
415       if (shProg->Type != GL_SHADER_PROGRAM_MESA) {
416          _mesa_error(ctx, GL_INVALID_OPERATION, "%s", caller);
417          return NULL;
418       }
419       return shProg;
420    }
421 }
422 
423 
424 void
_mesa_init_shader_object_functions(struct dd_function_table * driver)425 _mesa_init_shader_object_functions(struct dd_function_table *driver)
426 {
427    driver->NewShader = _mesa_new_shader;
428    driver->DeleteShader = _mesa_delete_shader;
429    driver->NewShaderProgram = _mesa_new_shader_program;
430    driver->DeleteShaderProgram = _mesa_delete_shader_program;
431    driver->LinkShader = _mesa_ir_link_shader;
432 }
433