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 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26/**
27 * \file shaderobj.c
28 * \author Brian Paul
29 *
30 */
31
32
33#include "main/glheader.h"
34#include "main/context.h"
35#include "main/hash.h"
36#include "main/mtypes.h"
37#include "main/shaderapi.h"
38#include "main/shaderobj.h"
39#include "main/uniforms.h"
40#include "program/program.h"
41#include "program/prog_parameter.h"
42#include "util/ralloc.h"
43#include "util/string_to_uint_map.h"
44#include "util/u_atomic.h"
45
46/**********************************************************************/
47/*** Shader object functions                                        ***/
48/**********************************************************************/
49
50
51/**
52 * Set ptr to point to sh.
53 * If ptr is pointing to another shader, decrement its refcount (and delete
54 * if refcount hits zero).
55 * Then set ptr to point to sh, incrementing its refcount.
56 */
57void
58_mesa_reference_shader(struct gl_context *ctx, struct gl_shader **ptr,
59                       struct gl_shader *sh)
60{
61   assert(ptr);
62   if (*ptr == sh) {
63      /* no-op */
64      return;
65   }
66   if (*ptr) {
67      /* Unreference the old shader */
68      struct gl_shader *old = *ptr;
69
70      assert(old->RefCount > 0);
71
72      if (p_atomic_dec_zero(&old->RefCount)) {
73	 if (old->Name != 0)
74	    _mesa_HashRemove(ctx->Shared->ShaderObjects, old->Name);
75         _mesa_delete_shader(ctx, old);
76      }
77
78      *ptr = NULL;
79   }
80   assert(!*ptr);
81
82   if (sh) {
83      /* reference new */
84      p_atomic_inc(&sh->RefCount);
85      *ptr = sh;
86   }
87}
88
89static void
90_mesa_init_shader(struct gl_shader *shader)
91{
92   shader->RefCount = 1;
93   shader->info.Geom.VerticesOut = -1;
94   shader->info.Geom.InputType = GL_TRIANGLES;
95   shader->info.Geom.OutputType = GL_TRIANGLE_STRIP;
96}
97
98/**
99 * Allocate a new gl_shader object, initialize it.
100 */
101struct gl_shader *
102_mesa_new_shader(GLuint name, gl_shader_stage stage)
103{
104   struct gl_shader *shader;
105   shader = rzalloc(NULL, struct gl_shader);
106   if (shader) {
107      shader->Stage = stage;
108      shader->Name = name;
109#ifdef DEBUG
110      shader->SourceChecksum = 0xa110c; /* alloc */
111#endif
112      _mesa_init_shader(shader);
113   }
114   return shader;
115}
116
117
118/**
119 * Delete a shader object.
120 */
121void
122_mesa_delete_shader(struct gl_context *ctx, struct gl_shader *sh)
123{
124   free((void *)sh->Source);
125   free(sh->Label);
126   ralloc_free(sh);
127}
128
129
130/**
131 * Delete a shader object.
132 */
133void
134_mesa_delete_linked_shader(struct gl_context *ctx,
135                           struct gl_linked_shader *sh)
136{
137   _mesa_reference_program(ctx, &sh->Program, NULL);
138   ralloc_free(sh);
139}
140
141
142/**
143 * Lookup a GLSL shader object.
144 */
145struct gl_shader *
146_mesa_lookup_shader(struct gl_context *ctx, GLuint name)
147{
148   if (name) {
149      struct gl_shader *sh = (struct gl_shader *)
150         _mesa_HashLookup(ctx->Shared->ShaderObjects, name);
151      /* Note that both gl_shader and gl_shader_program objects are kept
152       * in the same hash table.  Check the object's type to be sure it's
153       * what we're expecting.
154       */
155      if (sh && sh->Type == GL_SHADER_PROGRAM_MESA) {
156         return NULL;
157      }
158      return sh;
159   }
160   return NULL;
161}
162
163
164/**
165 * As above, but record an error if shader is not found.
166 */
167struct gl_shader *
168_mesa_lookup_shader_err(struct gl_context *ctx, GLuint name, const char *caller)
169{
170   if (!name) {
171      _mesa_error(ctx, GL_INVALID_VALUE, "%s", caller);
172      return NULL;
173   }
174   else {
175      struct gl_shader *sh = (struct gl_shader *)
176         _mesa_HashLookup(ctx->Shared->ShaderObjects, name);
177      if (!sh) {
178         _mesa_error(ctx, GL_INVALID_VALUE, "%s", caller);
179         return NULL;
180      }
181      if (sh->Type == GL_SHADER_PROGRAM_MESA) {
182         _mesa_error(ctx, GL_INVALID_OPERATION, "%s", caller);
183         return NULL;
184      }
185      return sh;
186   }
187}
188
189
190
191/**********************************************************************/
192/*** Shader Program object functions                                ***/
193/**********************************************************************/
194
195
196void
197_mesa_reference_shader_program_data(struct gl_context *ctx,
198                                    struct gl_shader_program_data **ptr,
199                                    struct gl_shader_program_data *data)
200{
201   if (*ptr == data)
202      return;
203
204   if (*ptr) {
205      struct gl_shader_program_data *oldData = *ptr;
206
207      assert(oldData->RefCount > 0);
208
209      if (p_atomic_dec_zero(&oldData->RefCount)) {
210         assert(ctx);
211         ralloc_free(oldData);
212      }
213
214      *ptr = NULL;
215   }
216
217   if (data)
218      p_atomic_inc(&data->RefCount);
219
220   *ptr = data;
221}
222
223/**
224 * Set ptr to point to shProg.
225 * If ptr is pointing to another object, decrement its refcount (and delete
226 * if refcount hits zero).
227 * Then set ptr to point to shProg, incrementing its refcount.
228 */
229void
230_mesa_reference_shader_program_(struct gl_context *ctx,
231                                struct gl_shader_program **ptr,
232                                struct gl_shader_program *shProg)
233{
234   assert(ptr);
235   if (*ptr == shProg) {
236      /* no-op */
237      return;
238   }
239   if (*ptr) {
240      /* Unreference the old shader program */
241      struct gl_shader_program *old = *ptr;
242
243      assert(old->RefCount > 0);
244
245      if (p_atomic_dec_zero(&old->RefCount)) {
246	 if (old->Name != 0)
247	    _mesa_HashRemove(ctx->Shared->ShaderObjects, old->Name);
248         _mesa_delete_shader_program(ctx, old);
249      }
250
251      *ptr = NULL;
252   }
253   assert(!*ptr);
254
255   if (shProg) {
256      p_atomic_inc(&shProg->RefCount);
257      *ptr = shProg;
258   }
259}
260
261static struct gl_shader_program_data *
262create_shader_program_data()
263{
264   struct gl_shader_program_data *data;
265   data = rzalloc(NULL, struct gl_shader_program_data);
266   if (data)
267      data->RefCount = 1;
268
269   return data;
270}
271
272static void
273init_shader_program(struct gl_shader_program *prog)
274{
275   prog->Type = GL_SHADER_PROGRAM_MESA;
276   prog->RefCount = 1;
277
278   prog->AttributeBindings = string_to_uint_map_ctor();
279   prog->FragDataBindings = string_to_uint_map_ctor();
280   prog->FragDataIndexBindings = string_to_uint_map_ctor();
281
282   prog->Geom.UsesEndPrimitive = false;
283   prog->Geom.UsesStreams = false;
284
285   prog->Comp.LocalSizeVariable = false;
286
287   prog->TransformFeedback.BufferMode = GL_INTERLEAVED_ATTRIBS;
288
289   exec_list_make_empty(&prog->EmptyUniformLocations);
290
291   prog->data->InfoLog = ralloc_strdup(prog->data, "");
292}
293
294/**
295 * Allocate a new gl_shader_program object, initialize it.
296 */
297struct gl_shader_program *
298_mesa_new_shader_program(GLuint name)
299{
300   struct gl_shader_program *shProg;
301   shProg = rzalloc(NULL, struct gl_shader_program);
302   if (shProg) {
303      shProg->Name = name;
304      shProg->data = create_shader_program_data();
305      if (!shProg->data) {
306         ralloc_free(shProg);
307         return NULL;
308      }
309      init_shader_program(shProg);
310   }
311   return shProg;
312}
313
314
315/**
316 * Clear (free) the shader program state that gets produced by linking.
317 */
318void
319_mesa_clear_shader_program_data(struct gl_context *ctx,
320                                struct gl_shader_program *shProg)
321{
322   for (gl_shader_stage sh = 0; sh < MESA_SHADER_STAGES; sh++) {
323      if (shProg->_LinkedShaders[sh] != NULL) {
324         _mesa_delete_linked_shader(ctx, shProg->_LinkedShaders[sh]);
325         shProg->_LinkedShaders[sh] = NULL;
326      }
327   }
328
329   shProg->data->linked_stages = 0;
330
331   if (shProg->data->UniformStorage) {
332      for (unsigned i = 0; i < shProg->data->NumUniformStorage; ++i)
333         _mesa_uniform_detach_all_driver_storage(&shProg->data->
334                                                    UniformStorage[i]);
335      ralloc_free(shProg->data->UniformStorage);
336      shProg->data->NumUniformStorage = 0;
337      shProg->data->UniformStorage = NULL;
338   }
339
340   if (shProg->UniformRemapTable) {
341      ralloc_free(shProg->UniformRemapTable);
342      shProg->NumUniformRemapTable = 0;
343      shProg->UniformRemapTable = NULL;
344   }
345
346   if (shProg->UniformHash) {
347      string_to_uint_map_dtor(shProg->UniformHash);
348      shProg->UniformHash = NULL;
349   }
350
351   assert(shProg->data->InfoLog != NULL);
352   ralloc_free(shProg->data->InfoLog);
353   shProg->data->InfoLog = ralloc_strdup(shProg->data, "");
354
355   ralloc_free(shProg->data->UniformBlocks);
356   shProg->data->UniformBlocks = NULL;
357   shProg->data->NumUniformBlocks = 0;
358
359   ralloc_free(shProg->data->ShaderStorageBlocks);
360   shProg->data->ShaderStorageBlocks = NULL;
361   shProg->data->NumShaderStorageBlocks = 0;
362
363   ralloc_free(shProg->data->AtomicBuffers);
364   shProg->data->AtomicBuffers = NULL;
365   shProg->data->NumAtomicBuffers = 0;
366
367   if (shProg->ProgramResourceList) {
368      ralloc_free(shProg->ProgramResourceList);
369      shProg->ProgramResourceList = NULL;
370      shProg->NumProgramResourceList = 0;
371   }
372}
373
374
375/**
376 * Free all the data that hangs off a shader program object, but not the
377 * object itself.
378 */
379void
380_mesa_free_shader_program_data(struct gl_context *ctx,
381                               struct gl_shader_program *shProg)
382{
383   GLuint i;
384
385   assert(shProg->Type == GL_SHADER_PROGRAM_MESA);
386
387   _mesa_clear_shader_program_data(ctx, shProg);
388
389   if (shProg->AttributeBindings) {
390      string_to_uint_map_dtor(shProg->AttributeBindings);
391      shProg->AttributeBindings = NULL;
392   }
393
394   if (shProg->FragDataBindings) {
395      string_to_uint_map_dtor(shProg->FragDataBindings);
396      shProg->FragDataBindings = NULL;
397   }
398
399   if (shProg->FragDataIndexBindings) {
400      string_to_uint_map_dtor(shProg->FragDataIndexBindings);
401      shProg->FragDataIndexBindings = NULL;
402   }
403
404   /* detach shaders */
405   for (i = 0; i < shProg->NumShaders; i++) {
406      _mesa_reference_shader(ctx, &shProg->Shaders[i], NULL);
407   }
408   shProg->NumShaders = 0;
409
410   free(shProg->Shaders);
411   shProg->Shaders = NULL;
412
413   /* Transform feedback varying vars */
414   for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) {
415      free(shProg->TransformFeedback.VaryingNames[i]);
416   }
417   free(shProg->TransformFeedback.VaryingNames);
418   shProg->TransformFeedback.VaryingNames = NULL;
419   shProg->TransformFeedback.NumVarying = 0;
420
421   free(shProg->Label);
422   shProg->Label = NULL;
423}
424
425
426/**
427 * Free/delete a shader program object.
428 */
429void
430_mesa_delete_shader_program(struct gl_context *ctx,
431                            struct gl_shader_program *shProg)
432{
433   _mesa_free_shader_program_data(ctx, shProg);
434   _mesa_reference_shader_program_data(ctx, &shProg->data, NULL);
435   ralloc_free(shProg);
436}
437
438
439/**
440 * Lookup a GLSL program object.
441 */
442struct gl_shader_program *
443_mesa_lookup_shader_program(struct gl_context *ctx, GLuint name)
444{
445   struct gl_shader_program *shProg;
446   if (name) {
447      shProg = (struct gl_shader_program *)
448         _mesa_HashLookup(ctx->Shared->ShaderObjects, name);
449      /* Note that both gl_shader and gl_shader_program objects are kept
450       * in the same hash table.  Check the object's type to be sure it's
451       * what we're expecting.
452       */
453      if (shProg && shProg->Type != GL_SHADER_PROGRAM_MESA) {
454         return NULL;
455      }
456      return shProg;
457   }
458   return NULL;
459}
460
461
462/**
463 * As above, but record an error if program is not found.
464 */
465struct gl_shader_program *
466_mesa_lookup_shader_program_err(struct gl_context *ctx, GLuint name,
467                                const char *caller)
468{
469   if (!name) {
470      _mesa_error(ctx, GL_INVALID_VALUE, "%s", caller);
471      return NULL;
472   }
473   else {
474      struct gl_shader_program *shProg = (struct gl_shader_program *)
475         _mesa_HashLookup(ctx->Shared->ShaderObjects, name);
476      if (!shProg) {
477         _mesa_error(ctx, GL_INVALID_VALUE, "%s", caller);
478         return NULL;
479      }
480      if (shProg->Type != GL_SHADER_PROGRAM_MESA) {
481         _mesa_error(ctx, GL_INVALID_OPERATION, "%s", caller);
482         return NULL;
483      }
484      return shProg;
485   }
486}
487
488
489void
490_mesa_init_shader_object_functions(struct dd_function_table *driver)
491{
492   driver->LinkShader = _mesa_ir_link_shader;
493}
494