/*
 * Mesa 3-D graphics library
 * Version:  6.5
 *
 * Copyright (C) 2005-2006  Brian Paul   All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/**
 * \file shaderobjects_3dlabs.c
 * shader objects definitions for 3dlabs compiler
 * \author Michal Krol
 */

/* Set this to 1 when we are ready to use 3dlabs' front-end */
#define USE_3DLABS_FRONTEND 0

#include "imports.h"
#include "hash.h"
#include "macros.h"
#include "program.h"
#include "shaderobjects.h"
#include "shaderobjects_3dlabs.h"

#if USE_3DLABS_FRONTEND
#include "slang_mesa.h"
#include "Public/ShaderLang.h"
#else
#include "slang_link.h"
#endif

#if FEATURE_ARB_shader_objects

struct gl2_unknown_obj
{
   GLuint reference_count;
   void (*_destructor) (struct gl2_unknown_intf **);
};

struct gl2_unknown_impl
{
   struct gl2_unknown_intf *_vftbl;
   struct gl2_unknown_obj _obj;
};

static void
_unknown_destructor(struct gl2_unknown_intf **intf)
{
}

static void
_unknown_AddRef(struct gl2_unknown_intf **intf)
{
   struct gl2_unknown_impl *impl = (struct gl2_unknown_impl *) intf;

   impl->_obj.reference_count++;
}

static void
_unknown_Release(struct gl2_unknown_intf **intf)
{
   struct gl2_unknown_impl *impl = (struct gl2_unknown_impl *) intf;

   impl->_obj.reference_count--;
   if (impl->_obj.reference_count == 0) {
      impl->_obj._destructor(intf);
      _mesa_free((void *) intf);
   }
}

static struct gl2_unknown_intf **
_unknown_QueryInterface(struct gl2_unknown_intf **intf, enum gl2_uiid uiid)
{
   if (uiid == UIID_UNKNOWN) {
      (**intf).AddRef(intf);
      return intf;
   }
   return NULL;
}

static struct gl2_unknown_intf _unknown_vftbl = {
   _unknown_AddRef,
   _unknown_Release,
   _unknown_QueryInterface
};

static void
_unknown_constructor(struct gl2_unknown_impl *impl)
{
   impl->_vftbl = &_unknown_vftbl;
   impl->_obj.reference_count = 1;
   impl->_obj._destructor = _unknown_destructor;
}

struct gl2_unkinner_obj
{
   struct gl2_unknown_intf **unkouter;
};

struct gl2_unkinner_impl
{
   struct gl2_unknown_intf *_vftbl;
   struct gl2_unkinner_obj _obj;
};

static void
_unkinner_destructor(struct gl2_unknown_intf **intf)
{
}

static void
_unkinner_AddRef(struct gl2_unknown_intf **intf)
{
   struct gl2_unkinner_impl *impl = (struct gl2_unkinner_impl *) intf;

   (**impl->_obj.unkouter).AddRef(impl->_obj.unkouter);
}

static void
_unkinner_Release(struct gl2_unknown_intf **intf)
{
   struct gl2_unkinner_impl *impl = (struct gl2_unkinner_impl *) intf;

   (**impl->_obj.unkouter).Release(impl->_obj.unkouter);
}

static struct gl2_unknown_intf **
_unkinner_QueryInterface(struct gl2_unknown_intf **intf, enum gl2_uiid uiid)
{
   struct gl2_unkinner_impl *impl = (struct gl2_unkinner_impl *) intf;

   return (**impl->_obj.unkouter).QueryInterface(impl->_obj.unkouter, uiid);
}

static struct gl2_unknown_intf _unkinner_vftbl = {
   _unkinner_AddRef,
   _unkinner_Release,
   _unkinner_QueryInterface
};

static void
_unkinner_constructor(struct gl2_unkinner_impl *impl,
                      struct gl2_unknown_intf **outer)
{
   impl->_vftbl = &_unkinner_vftbl;
   impl->_obj.unkouter = outer;
}

struct gl2_generic_obj
{
   struct gl2_unknown_obj _unknown;
   GLhandleARB name;
   GLboolean delete_status;
   GLcharARB *info_log;
};

struct gl2_generic_impl
{
   struct gl2_generic_intf *_vftbl;
   struct gl2_generic_obj _obj;
};

static void
_generic_destructor(struct gl2_unknown_intf **intf)
{
   GET_CURRENT_CONTEXT(ctx);
   struct gl2_generic_impl *impl = (struct gl2_generic_impl *) intf;

   _mesa_free((void *) impl->_obj.info_log);

   _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
   _mesa_HashRemove(ctx->Shared->GL2Objects, impl->_obj.name);
   _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);

   _unknown_destructor(intf);
}

static struct gl2_unknown_intf **
_generic_QueryInterface(struct gl2_unknown_intf **intf, enum gl2_uiid uiid)
{
   if (uiid == UIID_GENERIC) {
      (**intf).AddRef(intf);
      return intf;
   }
   return _unknown_QueryInterface(intf, uiid);
}

static void
_generic_Delete(struct gl2_generic_intf **intf)
{
   struct gl2_generic_impl *impl = (struct gl2_generic_impl *) intf;

   if (impl->_obj.delete_status == GL_FALSE) {
      impl->_obj.delete_status = GL_TRUE;
      (**intf)._unknown.Release((struct gl2_unknown_intf **) intf);
   }
}

static GLhandleARB
_generic_GetName(struct gl2_generic_intf **intf)
{
   struct gl2_generic_impl *impl = (struct gl2_generic_impl *) intf;

   return impl->_obj.name;
}

static GLboolean
_generic_GetDeleteStatus(struct gl2_generic_intf **intf)
{
   struct gl2_generic_impl *impl = (struct gl2_generic_impl *) intf;

   return impl->_obj.delete_status;
}

static GLvoid
_generic_GetInfoLog(struct gl2_generic_intf **intf, GLsizei maxlen,
                    GLcharARB * infolog)
{
   struct gl2_generic_impl *impl = (struct gl2_generic_impl *) (intf);

   if (maxlen > 0) {
      _mesa_strncpy(infolog, impl->_obj.info_log, maxlen - 1);
      infolog[maxlen - 1] = '\0';
   }
}

static GLsizei
_generic_GetInfoLogLength(struct gl2_generic_intf **intf)
{
   struct gl2_generic_impl *impl = (struct gl2_generic_impl *) (intf);

   if (impl->_obj.info_log == NULL)
      return 1;
   return _mesa_strlen(impl->_obj.info_log) + 1;
}

static struct gl2_generic_intf _generic_vftbl = {
   {
    _unknown_AddRef,
    _unknown_Release,
    _generic_QueryInterface},
   _generic_Delete,
   NULL,                        /* abstract GetType */
   _generic_GetName,
   _generic_GetDeleteStatus,
   _generic_GetInfoLog,
   _generic_GetInfoLogLength
};

static void
_generic_constructor(struct gl2_generic_impl *impl)
{
   GET_CURRENT_CONTEXT(ctx);

   _unknown_constructor((struct gl2_unknown_impl *) impl);
   impl->_vftbl = &_generic_vftbl;
   impl->_obj._unknown._destructor = _generic_destructor;
   impl->_obj.delete_status = GL_FALSE;
   impl->_obj.info_log = NULL;

   _glthread_LOCK_MUTEX(ctx->Shared->Mutex);
   impl->_obj.name = _mesa_HashFindFreeKeyBlock(ctx->Shared->GL2Objects, 1);
   _mesa_HashInsert(ctx->Shared->GL2Objects, impl->_obj.name, (void *) impl);
   _glthread_UNLOCK_MUTEX(ctx->Shared->Mutex);
}

struct gl2_container_obj
{
   struct gl2_generic_obj _generic;
   struct gl2_generic_intf ***attached;
   GLuint attached_count;
};

struct gl2_container_impl
{
   struct gl2_container_intf *_vftbl;
   struct gl2_container_obj _obj;
};

static void
_container_destructor(struct gl2_unknown_intf **intf)
{
   struct gl2_container_impl *impl = (struct gl2_container_impl *) intf;
   GLuint i;

   for (i = 0; i < impl->_obj.attached_count; i++) {
      struct gl2_generic_intf **x = impl->_obj.attached[i];
      (**x)._unknown.Release((struct gl2_unknown_intf **) x);
   }

   _generic_destructor(intf);
}

static struct gl2_unknown_intf **
_container_QueryInterface(struct gl2_unknown_intf **intf, enum gl2_uiid uiid)
{
   if (uiid == UIID_CONTAINER) {
      (**intf).AddRef(intf);
      return intf;
   }
   return _generic_QueryInterface(intf, uiid);
}

static GLboolean
_container_Attach(struct gl2_container_intf **intf,
                  struct gl2_generic_intf **att)
{
   GET_CURRENT_CONTEXT(ctx);
   struct gl2_container_impl *impl = (struct gl2_container_impl *) intf;
   GLuint i;

   for (i = 0; i < impl->_obj.attached_count; i++)
      if (impl->_obj.attached[i] == att) {
         _mesa_error(ctx, GL_INVALID_OPERATION, "_container_Attach");
         return GL_FALSE;
      }

   impl->_obj.attached = (struct gl2_generic_intf ***)
      _mesa_realloc(impl->_obj.attached,
               impl->_obj.attached_count * sizeof(*impl->_obj.attached),
               (impl->_obj.attached_count + 1) * sizeof(*impl->_obj.attached));
   if (impl->_obj.attached == NULL)
      return GL_FALSE;

   impl->_obj.attached[impl->_obj.attached_count] = att;
   impl->_obj.attached_count++;
   (**att)._unknown.AddRef((struct gl2_unknown_intf **) att);
   return GL_TRUE;
}

static GLboolean
_container_Detach(struct gl2_container_intf **intf,
                  struct gl2_generic_intf **att)
{
   GET_CURRENT_CONTEXT(ctx);
   struct gl2_container_impl *impl = (struct gl2_container_impl *) intf;
   GLuint i, j;

   for (i = 0; i < impl->_obj.attached_count; i++)
      if (impl->_obj.attached[i] == att) {
         for (j = i; j < impl->_obj.attached_count - 1; j++)
            impl->_obj.attached[j] = impl->_obj.attached[j + 1];
         impl->_obj.attached = (struct gl2_generic_intf ***)
            _mesa_realloc(impl->_obj.attached,
               impl->_obj.attached_count * sizeof(*impl->_obj.attached),
               (impl->_obj.attached_count - 1) * sizeof(*impl->_obj.attached));
         impl->_obj.attached_count--;
         (**att)._unknown.Release((struct gl2_unknown_intf **) att);
         return GL_TRUE;
      }

   _mesa_error(ctx, GL_INVALID_OPERATION, "_container_Detach");
   return GL_FALSE;
}

static GLsizei
_container_GetAttachedCount(struct gl2_container_intf **intf)
{
   struct gl2_container_impl *impl = (struct gl2_container_impl *) intf;

   return impl->_obj.attached_count;
}

static struct gl2_generic_intf **
_container_GetAttached(struct gl2_container_intf **intf, GLuint index)
{
   struct gl2_container_impl *impl = (struct gl2_container_impl *) intf;

   (**impl->_obj.attached[index])._unknown.AddRef((struct gl2_unknown_intf **)
                                                  impl->_obj.attached[index]);
   return impl->_obj.attached[index];
}

static struct gl2_container_intf _container_vftbl = {
   {
      {
         _unknown_AddRef,
         _unknown_Release,
         _container_QueryInterface
      },
      _generic_Delete,
      NULL,                       /* abstract GetType */
      _generic_GetName,
      _generic_GetDeleteStatus,
      _generic_GetInfoLog,
      _generic_GetInfoLogLength
   },
   _container_Attach,
   _container_Detach,
   _container_GetAttachedCount,
   _container_GetAttached
};

static void
_container_constructor(struct gl2_container_impl *impl)
{
   _generic_constructor((struct gl2_generic_impl *) impl);
   impl->_vftbl = &_container_vftbl;
   impl->_obj._generic._unknown._destructor = _container_destructor;
   impl->_obj.attached = NULL;
   impl->_obj.attached_count = 0;
}

struct gl2_3dlabs_shhandle_obj
{
   struct gl2_unkinner_obj _unknown;
#if USE_3DLABS_FRONTEND
   ShHandle handle;
#endif
};

struct gl2_3dlabs_shhandle_impl
{
   struct gl2_3dlabs_shhandle_intf *_vftbl;
   struct gl2_3dlabs_shhandle_obj _obj;
};

static void
_3dlabs_shhandle_destructor(struct gl2_unknown_intf **intf)
{
#if USE_3DLABS_FRONTEND
   struct gl2_3dlabs_shhandle_impl *impl =
      (struct gl2_3dlabs_shhandle_impl *) intf;
   ShDestruct(impl->_obj.handle);
#endif
   _unkinner_destructor(intf);
}

static GLvoid *
_3dlabs_shhandle_GetShHandle(struct gl2_3dlabs_shhandle_intf **intf)
{
#if USE_3DLABS_FRONTEND
   struct gl2_3dlabs_shhandle_impl *impl =
      (struct gl2_3dlabs_shhandle_impl *) intf;
   return impl->_obj.handle;
#else
   return NULL;
#endif
}

static struct gl2_3dlabs_shhandle_intf _3dlabs_shhandle_vftbl = {
   {
    _unkinner_AddRef,
    _unkinner_Release,
    _unkinner_QueryInterface},
   _3dlabs_shhandle_GetShHandle
};

static void
_3dlabs_shhandle_constructor(struct gl2_3dlabs_shhandle_impl *impl,
                             struct gl2_unknown_intf **outer)
{
   _unkinner_constructor((struct gl2_unkinner_impl *) impl, outer);
   impl->_vftbl = &_3dlabs_shhandle_vftbl;
#if USE_3DLABS_FRONTEND
   impl->_obj.handle = NULL;
#endif
}

struct gl2_shader_obj
{
   struct gl2_generic_obj _generic;
   struct gl2_3dlabs_shhandle_impl _3dlabs_shhandle;
   GLboolean compile_status;
   GLcharARB *source;
   GLint *offsets;
   GLsizei offset_count;
   slang_code_object code;
};

struct gl2_shader_impl
{
   struct gl2_shader_intf *_vftbl;
   struct gl2_shader_obj _obj;
};

static void
_shader_destructor(struct gl2_unknown_intf **intf)
{
   struct gl2_shader_impl *impl = (struct gl2_shader_impl *) intf;

   _mesa_free((void *) impl->_obj.source);
   _mesa_free((void *) impl->_obj.offsets);
   _slang_code_object_dtr(&impl->_obj.code);
   _3dlabs_shhandle_destructor((struct gl2_unknown_intf **) &impl->_obj.
                               _3dlabs_shhandle._vftbl);
   _generic_destructor(intf);
}

static struct gl2_unknown_intf **
_shader_QueryInterface(struct gl2_unknown_intf **intf, enum gl2_uiid uiid)
{
#if USE_3DLABS_FRONTEND
   struct gl2_shader_impl *impl = (struct gl2_shader_impl *) intf;
#endif

   if (uiid == UIID_SHADER) {
      (**intf).AddRef(intf);
      return intf;
   }
#if USE_3DLABS_FRONTEND
   if (uiid == UIID_3DLABS_SHHANDLE) {
      (**intf).AddRef(intf);
      return (struct gl2_unknown_intf **) &impl->_obj._3dlabs_shhandle._vftbl;
   }
#endif
   return _generic_QueryInterface(intf, uiid);
}

static GLenum
_shader_GetType(struct gl2_generic_intf **intf)
{
   return GL_SHADER_OBJECT_ARB;
}

static GLvoid
_shader_GetInfoLog(struct gl2_generic_intf **intf, GLsizei maxlen,
                   GLcharARB * infolog)
{
   struct gl2_shader_impl *impl = (struct gl2_shader_impl *) (intf);

   if (maxlen > 0) {
      if (impl->_obj._generic.info_log != NULL) {
         GLsizei len = _mesa_strlen(impl->_obj._generic.info_log);
         if (len > maxlen - 1)
            len = maxlen - 1;
         _mesa_memcpy(infolog, impl->_obj._generic.info_log, len);
         infolog += len;
         maxlen -= len;
      }
      if (impl->_obj.code.machine.infolog != NULL &&
          impl->_obj.code.machine.infolog->text != NULL) {
         GLsizei len = _mesa_strlen(impl->_obj.code.machine.infolog->text);
         if (len > maxlen - 1)
            len = maxlen - 1;
         _mesa_memcpy(infolog, impl->_obj.code.machine.infolog->text, len);
      }
      infolog[maxlen - 1] = '\0';
   }
}

static GLsizei
_shader_GetInfoLogLength(struct gl2_generic_intf **intf)
{
   struct gl2_shader_impl *impl = (struct gl2_shader_impl *) (intf);
   GLsizei length = 1;

   if (impl->_obj._generic.info_log != NULL)
      length += _mesa_strlen(impl->_obj._generic.info_log);
   if (impl->_obj.code.machine.infolog != NULL &&
       impl->_obj.code.machine.infolog->text != NULL)
      length += _mesa_strlen(impl->_obj.code.machine.infolog->text);
   return length;
}

static GLboolean
_shader_GetCompileStatus(struct gl2_shader_intf **intf)
{
   struct gl2_shader_impl *impl = (struct gl2_shader_impl *) intf;

   return impl->_obj.compile_status;
}

static GLvoid
_shader_SetSource(struct gl2_shader_intf **intf, GLcharARB * src, GLint * off,
                  GLsizei cnt)
{
   struct gl2_shader_impl *impl = (struct gl2_shader_impl *) intf;

   _mesa_free((void *) impl->_obj.source);
   impl->_obj.source = src;
   _mesa_free((void *) impl->_obj.offsets);
   impl->_obj.offsets = off;
   impl->_obj.offset_count = cnt;
}

static const GLcharARB *
_shader_GetSource(struct gl2_shader_intf **intf)
{
   struct gl2_shader_impl *impl = (struct gl2_shader_impl *) intf;

   return impl->_obj.source;
}

static GLvoid
_shader_Compile(struct gl2_shader_intf **intf)
{
   GET_CURRENT_CONTEXT(ctx);
   struct gl2_shader_impl *impl = (struct gl2_shader_impl *) intf;
#if USE_3DLABS_FRONTEND
   char **strings;
   TBuiltInResource res;
#else
   slang_unit_type type;
   slang_info_log info_log;
#endif

   impl->_obj.compile_status = GL_FALSE;
   _mesa_free((void *) impl->_obj._generic.info_log);
   impl->_obj._generic.info_log = NULL;

#if USE_3DLABS_FRONTEND
   /* 3dlabs compiler expects us to feed it with null-terminated string array,
      we've got only one big string with offsets, so we must split it; but when
      there's only one string to deal with, we pass its address directly */

   if (impl->_obj.offset_count <= 1)
      strings = &impl->_obj.source;
   else {
      GLsizei i, offset = 0;

      strings =
         (char **) _mesa_malloc(impl->_obj.offset_count * sizeof(char *));
      if (strings == NULL)
         return;

      for (i = 0; i < impl->_obj.offset_count; i++) {
         GLsizei size = impl->_obj.offsets[i] - offset;

         strings[i] = (char *) _mesa_malloc((size + 1) * sizeof(char));
         if (strings[i] == NULL) {
            GLsizei j;

            for (j = 0; j < i; j++)
               _mesa_free(strings[j]);
            _mesa_free(strings);
            return;
         }

         _mesa_memcpy(strings[i], impl->_obj.source + offset,
                      size * sizeof(char));
         strings[i][size] = '\0';
         offset = impl->_obj.offsets[i];
      }
   }

   /* TODO set these fields to some REAL numbers */
   res.maxLights = 8;
   res.maxClipPlanes = 6;
   res.maxTextureUnits = 2;
   res.maxTextureCoords = 2;
   res.maxVertexAttribs = 8;
   res.maxVertexUniformComponents = 64;
   res.maxVaryingFloats = 8;
   res.maxVertexTextureImageUnits = 2;
   res.maxCombinedTextureImageUnits = 2;
   res.maxTextureImageUnits = 2;
   res.maxFragmentUniformComponents = 64;
   res.maxDrawBuffers = 1;

   if (ShCompile
       (impl->_obj._3dlabs_shhandle._obj.handle, strings,
        impl->_obj.offset_count, EShOptFull, &res, 0))
      impl->_obj.compile_status = GL_TRUE;
   if (impl->_obj.offset_count > 1) {
      GLsizei i;

      for (i = 0; i < impl->_obj.offset_count; i++)
         _mesa_free(strings[i]);
      _mesa_free(strings);
   }

   impl->_obj._generic.info_log =
      _mesa_strdup(ShGetInfoLog(impl->_obj._3dlabs_shhandle._obj.handle));
#else
   /* NEW_SLANG */
   if (impl->_vftbl->GetSubType(intf) == GL_FRAGMENT_SHADER) {
      type = slang_unit_fragment_shader;
      (*intf)->Program = _mesa_new_program(ctx, GL_FRAGMENT_PROGRAM_ARB, 1);
   }
   else {
      type = slang_unit_vertex_shader;
      (*intf)->Program = _mesa_new_program(ctx, GL_VERTEX_PROGRAM_ARB, 1);
   }
   slang_info_log_construct(&info_log);
   if (_slang_compile(impl->_obj.source, &impl->_obj.code, type, &info_log,
                      (*intf)->Program))
      impl->_obj.compile_status = GL_TRUE;
   if (info_log.text != NULL)
      impl->_obj._generic.info_log = _mesa_strdup(info_log.text);
   else if (impl->_obj.compile_status)
      impl->_obj._generic.info_log = _mesa_strdup("Compile OK.\n");
   else
      impl->_obj._generic.info_log = _mesa_strdup("Compile failed.\n");
   slang_info_log_destruct(&info_log);
#endif
}

static struct gl2_shader_intf _shader_vftbl = {
   {
      {
         _unknown_AddRef,
         _unknown_Release,
         _shader_QueryInterface
      },
      _generic_Delete,
      _shader_GetType,
      _generic_GetName,
      _generic_GetDeleteStatus,
      _shader_GetInfoLog,
      _shader_GetInfoLogLength
   },
   NULL,                        /* abstract GetSubType */
   _shader_GetCompileStatus,
   _shader_SetSource,
   _shader_GetSource,
   _shader_Compile
};

static void
_shader_constructor(struct gl2_shader_impl *impl)
{
   _generic_constructor((struct gl2_generic_impl *) impl);
   _3dlabs_shhandle_constructor(&impl->_obj._3dlabs_shhandle,
                                (struct gl2_unknown_intf **)
                                &impl->_vftbl);
   impl->_vftbl = &_shader_vftbl;
   impl->_obj._generic._unknown._destructor = _shader_destructor;
   impl->_obj.compile_status = GL_FALSE;
   impl->_obj.source = NULL;
   impl->_obj.offsets = NULL;
   impl->_obj.offset_count = 0;
   _slang_code_object_ctr(&impl->_obj.code);
}

struct gl2_program_obj
{
   struct gl2_container_obj _container;
   GLboolean link_status;
   GLboolean validate_status;
#if USE_3DLABS_FRONTEND
   ShHandle linker;
   ShHandle uniforms;
#endif
   slang_program prog;
};

struct gl2_program_impl
{
   struct gl2_program_intf *_vftbl;
   struct gl2_program_obj _obj;
};

static void
_program_destructor(struct gl2_unknown_intf **intf)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) intf;
#if USE_3DLABS_FRONTEND
   ShDestruct(impl->_obj.linker);
   ShDestruct(impl->_obj.uniforms);
#endif
   _container_destructor(intf);
   _slang_program_dtr(&impl->_obj.prog);
}

static struct gl2_unknown_intf **
_program_QueryInterface(struct gl2_unknown_intf **intf, enum gl2_uiid uiid)
{
   if (uiid == UIID_PROGRAM) {
      (**intf).AddRef(intf);
      return intf;
   }
   return _container_QueryInterface(intf, uiid);
}

static GLenum
_program_GetType(struct gl2_generic_intf **intf)
{
   return GL_PROGRAM_OBJECT_ARB;
}

static GLboolean
_program_Attach(struct gl2_container_intf **intf,
                struct gl2_generic_intf **att)
{
   GET_CURRENT_CONTEXT(ctx);
   struct gl2_unknown_intf **sha;

   sha =
      (**att)._unknown.QueryInterface((struct gl2_unknown_intf **) att,
                                      UIID_SHADER);
   if (sha == NULL) {
      _mesa_error(ctx, GL_INVALID_OPERATION, "_program_Attach");
      return GL_FALSE;
   }

   (**sha).Release(sha);
   return _container_Attach(intf, att);
}

static GLboolean
_program_GetLinkStatus(struct gl2_program_intf **intf)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) intf;

   return impl->_obj.link_status;
}

static GLboolean
_program_GetValidateStatus(struct gl2_program_intf **intf)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) intf;

   return impl->_obj.validate_status;
}

static GLvoid
_program_Link(struct gl2_program_intf **intf)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) intf;
#if USE_3DLABS_FRONTEND
   ShHandle *handles;
#endif
   GLuint i, count;
   slang_code_object *code[2];
   GLboolean all_compiled = GL_TRUE;

   impl->_obj.link_status = GL_FALSE;
   _mesa_free((void *) impl->_obj._container._generic.info_log);
   impl->_obj._container._generic.info_log = NULL;
   _slang_program_rst(&impl->_obj.prog);

#if USE_3DLABS_FRONTEND
   handles =
      (ShHandle *) _mesa_malloc(impl->_obj._container.attached_count *
                                sizeof(ShHandle));
   if (handles == NULL)
      return;

   for (i = 0; i < impl->_obj._container.attached_count; i++) {
      struct gl2_generic_intf **gen = impl->_obj._container.attached[i];
      struct gl2_3dlabs_shhandle_intf **sh;

      sh =
         (struct gl2_3dlabs_shhandle_intf **) (**gen)._unknown.
         QueryInterface((struct gl2_unknown_intf **) gen,
                        UIID_3DLABS_SHHANDLE);
      if (sh != NULL) {
         handles[i] = (**sh).GetShHandle(sh);
         (**sh)._unknown.Release((struct gl2_unknown_intf **) sh);
      }
      else {
         _mesa_free(handles);
         return;
      }
   }

   if (ShLink(impl->_obj.linker, handles, impl->_obj._container.attached_count,
              impl->_obj.uniforms, NULL, NULL))
      impl->_obj.link_status = GL_TRUE;

   impl->_obj._container._generic.info_log =
      _mesa_strdup(ShGetInfoLog(impl->_obj.linker));
#else
   count = impl->_obj._container.attached_count;
   if (count > 2)
      return;

   for (i = 0; i < count; i++) {
      struct gl2_generic_intf **obj;
      struct gl2_unknown_intf **unk;
      struct gl2_shader_impl *sha;

      obj = impl->_obj._container.attached[i];
      unk =
         (**obj)._unknown.QueryInterface((struct gl2_unknown_intf **) obj,
                                         UIID_SHADER);
      if (unk == NULL)
         return;
      sha = (struct gl2_shader_impl *) unk;
      code[i] = &sha->_obj.code;
      all_compiled = all_compiled && sha->_obj.compile_status;
      (**unk).Release(unk);
   }

   impl->_obj.link_status = all_compiled;
   if (!impl->_obj.link_status) {
      impl->_obj._container._generic.info_log =
         _mesa_strdup
         ("Error: One or more shaders has not successfully compiled.\n");
      return;
   }

   impl->_obj.link_status = _slang_link(&impl->_obj.prog, code, count);
   if (!impl->_obj.link_status) {
      impl->_obj._container._generic.info_log =
         _mesa_strdup("Link failed.\n");
      return;
   }

   impl->_obj._container._generic.info_log = _mesa_strdup("Link OK.\n");
#endif
}

static GLvoid
_program_Validate(struct gl2_program_intf **intf)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) intf;

   impl->_obj.validate_status = GL_FALSE;
   _mesa_free((void *) impl->_obj._container._generic.info_log);
   impl->_obj._container._generic.info_log = NULL;

   /* TODO validate */
}

static GLvoid
write_common_fixed(slang_program * pro, GLuint index, const GLvoid * src,
                   GLuint off, GLuint size)
{
   GLuint i;

   for (i = 0; i < SLANG_SHADER_MAX; i++) {
      GLuint addr;

      addr = pro->common_fixed_entries[i][index];
      if (addr != ~0) {
         GLubyte *dst;

         dst = (GLubyte *) pro->machines[i]->mem + addr + off * size;
         _mesa_memcpy(dst, src, size);
      }
   }
}

static GLvoid
write_common_fixed_mat4(slang_program * pro, GLmatrix * matrix, GLuint off,
                        GLuint i, GLuint ii, GLuint it, GLuint iit)
{
   GLfloat mat[16];

   /* we want inverse matrix */
   if (!matrix->inv) {
      /* allocate inverse matrix and make it dirty */
      _math_matrix_alloc_inv(matrix);
      _math_matrix_loadf(matrix, matrix->m);
   }
   _math_matrix_analyse(matrix);

   write_common_fixed(pro, i, matrix->m, off, 16 * sizeof(GLfloat));

   /* inverse */
   write_common_fixed(pro, ii, matrix->inv, off, 16 * sizeof(GLfloat));

   /* transpose */
   _math_transposef(mat, matrix->m);
   write_common_fixed(pro, it, mat, off, 16 * sizeof(GLfloat));

   /* inverse transpose */
   _math_transposef(mat, matrix->inv);
   write_common_fixed(pro, iit, mat, off, 16 * sizeof(GLfloat));
}

static GLvoid
write_common_fixed_material(GLcontext * ctx, slang_program * pro, GLuint i,
                            GLuint e, GLuint a, GLuint d, GLuint sp,
                            GLuint sh)
{
   GLfloat v[17];

   COPY_4FV(v, ctx->Light.Material.Attrib[e]);
   COPY_4FV((v + 4), ctx->Light.Material.Attrib[a]);
   COPY_4FV((v + 8), ctx->Light.Material.Attrib[d]);
   COPY_4FV((v + 12), ctx->Light.Material.Attrib[sp]);
   v[16] = ctx->Light.Material.Attrib[sh][0];
   write_common_fixed(pro, i, v, 0, 17 * sizeof(GLfloat));
}

static GLvoid
write_common_fixed_light_model_product(GLcontext * ctx, slang_program * pro,
                                       GLuint i, GLuint e, GLuint a)
{
   GLfloat v[4];

   SCALE_4V(v, ctx->Light.Material.Attrib[a], ctx->Light.Model.Ambient);
   ACC_4V(v, ctx->Light.Material.Attrib[e]);
   write_common_fixed(pro, i, v, 0, 4 * sizeof(GLfloat));
}

static GLvoid
write_common_fixed_light_product(GLcontext * ctx, slang_program * pro,
                                 GLuint off, GLuint i, GLuint a, GLuint d,
                                 GLuint s)
{
   GLfloat v[12];

   SCALE_4V(v, ctx->Light.Light[off].Ambient, ctx->Light.Material.Attrib[a]);
   SCALE_4V((v + 4), ctx->Light.Light[off].Diffuse,
            ctx->Light.Material.Attrib[d]);
   SCALE_4V((v + 8), ctx->Light.Light[off].Specular,
            ctx->Light.Material.Attrib[s]);
   write_common_fixed(pro, i, v, off, 12 * sizeof(GLfloat));
}

static GLvoid
_program_UpdateFixedUniforms(struct gl2_program_intf **intf)
{
   GET_CURRENT_CONTEXT(ctx);
   struct gl2_program_impl *impl = (struct gl2_program_impl *) intf;
   slang_program *pro = &impl->_obj.prog;
   GLuint i;
   GLfloat v[29];
   GLfloat *p;

   /* MODELVIEW matrix */
   write_common_fixed_mat4(pro, ctx->ModelviewMatrixStack.Top, 0,
                           SLANG_COMMON_FIXED_MODELVIEWMATRIX,
                           SLANG_COMMON_FIXED_MODELVIEWMATRIXINVERSE,
                           SLANG_COMMON_FIXED_MODELVIEWMATRIXTRANSPOSE,
                           SLANG_COMMON_FIXED_MODELVIEWMATRIXINVERSETRANSPOSE);

   /* PROJECTION matrix */
   write_common_fixed_mat4(pro, ctx->ProjectionMatrixStack.Top, 0,
                           SLANG_COMMON_FIXED_PROJECTIONMATRIX,
                           SLANG_COMMON_FIXED_PROJECTIONMATRIXINVERSE,
                           SLANG_COMMON_FIXED_PROJECTIONMATRIXTRANSPOSE,
                           SLANG_COMMON_FIXED_PROJECTIONMATRIXINVERSETRANSPOSE);

   /* MVP matrix */
   write_common_fixed_mat4(pro, &ctx->_ModelProjectMatrix, 0,
                           SLANG_COMMON_FIXED_MODELVIEWPROJECTIONMATRIX,
                           SLANG_COMMON_FIXED_MODELVIEWPROJECTIONMATRIXINVERSE,
                           SLANG_COMMON_FIXED_MODELVIEWPROJECTIONMATRIXTRANSPOSE,
                           SLANG_COMMON_FIXED_MODELVIEWPROJECTIONMATRIXINVERSETRANSPOSE);

   for (i = 0; i < ctx->Const.MaxTextureCoordUnits; i++) {
      /* TEXTURE matrix */
      write_common_fixed_mat4(pro, ctx->TextureMatrixStack[i].Top, i,
                              SLANG_COMMON_FIXED_TEXTUREMATRIX,
                              SLANG_COMMON_FIXED_TEXTUREMATRIXINVERSE,
                              SLANG_COMMON_FIXED_TEXTUREMATRIXTRANSPOSE,
                              SLANG_COMMON_FIXED_TEXTUREMATRIXINVERSETRANSPOSE);

      /* EYE_PLANE texture-coordinate generation */
      write_common_fixed(pro, SLANG_COMMON_FIXED_EYEPLANES,
                         ctx->Texture.Unit[i].EyePlaneS, i,
                         4 * sizeof(GLfloat));
      write_common_fixed(pro, SLANG_COMMON_FIXED_EYEPLANET,
                         ctx->Texture.Unit[i].EyePlaneT, i,
                         4 * sizeof(GLfloat));
      write_common_fixed(pro, SLANG_COMMON_FIXED_EYEPLANER,
                         ctx->Texture.Unit[i].EyePlaneR, i,
                         4 * sizeof(GLfloat));
      write_common_fixed(pro, SLANG_COMMON_FIXED_EYEPLANEQ,
                         ctx->Texture.Unit[i].EyePlaneQ, i,
                         4 * sizeof(GLfloat));

      /* OBJECT_PLANE texture-coordinate generation */
      write_common_fixed(pro, SLANG_COMMON_FIXED_OBJECTPLANES,
                         ctx->Texture.Unit[i].ObjectPlaneS, i,
                         4 * sizeof(GLfloat));
      write_common_fixed(pro, SLANG_COMMON_FIXED_OBJECTPLANET,
                         ctx->Texture.Unit[i].ObjectPlaneT, i,
                         4 * sizeof(GLfloat));
      write_common_fixed(pro, SLANG_COMMON_FIXED_OBJECTPLANER,
                         ctx->Texture.Unit[i].ObjectPlaneR, i,
                         4 * sizeof(GLfloat));
      write_common_fixed(pro, SLANG_COMMON_FIXED_OBJECTPLANEQ,
                         ctx->Texture.Unit[i].ObjectPlaneQ, i,
                         4 * sizeof(GLfloat));
   }

   /* NORMAL matrix - upper 3x3 inverse transpose of MODELVIEW matrix */
   p = ctx->ModelviewMatrixStack.Top->inv;
   v[0] = p[0];
   v[1] = p[4];
   v[2] = p[8];
   v[3] = p[1];
   v[4] = p[5];
   v[5] = p[9];
   v[6] = p[2];
   v[7] = p[6];
   v[8] = p[10];
   write_common_fixed(pro, SLANG_COMMON_FIXED_NORMALMATRIX, v, 0,
                      9 * sizeof(GLfloat));

   /* normal scale */
   write_common_fixed(pro, SLANG_COMMON_FIXED_NORMALSCALE,
                      &ctx->_ModelViewInvScale, 0, sizeof(GLfloat));

   /* depth range parameters */
   v[0] = ctx->Viewport.Near;
   v[1] = ctx->Viewport.Far;
   v[2] = ctx->Viewport.Far - ctx->Viewport.Near;
   write_common_fixed(pro, SLANG_COMMON_FIXED_DEPTHRANGE, v, 0,
                      3 * sizeof(GLfloat));

   /* CLIP_PLANEi */
   for (i = 0; i < ctx->Const.MaxClipPlanes; i++) {
      write_common_fixed(pro, SLANG_COMMON_FIXED_CLIPPLANE,
                         ctx->Transform.EyeUserPlane[i], i,
                         4 * sizeof(GLfloat));
   }

   /* point parameters */
   v[0] = ctx->Point.Size;
   v[1] = ctx->Point.MinSize;
   v[2] = ctx->Point.MaxSize;
   v[3] = ctx->Point.Threshold;
   COPY_3FV((v + 4), ctx->Point.Params);
   write_common_fixed(pro, SLANG_COMMON_FIXED_POINT, v, 0,
                      7 * sizeof(GLfloat));

   /* material parameters */
   write_common_fixed_material(ctx, pro, SLANG_COMMON_FIXED_FRONTMATERIAL,
                               MAT_ATTRIB_FRONT_EMISSION,
                               MAT_ATTRIB_FRONT_AMBIENT,
                               MAT_ATTRIB_FRONT_DIFFUSE,
                               MAT_ATTRIB_FRONT_SPECULAR,
                               MAT_ATTRIB_FRONT_SHININESS);
   write_common_fixed_material(ctx, pro, SLANG_COMMON_FIXED_BACKMATERIAL,
                               MAT_ATTRIB_BACK_EMISSION,
                               MAT_ATTRIB_BACK_AMBIENT,
                               MAT_ATTRIB_BACK_DIFFUSE,
                               MAT_ATTRIB_BACK_SPECULAR,
                               MAT_ATTRIB_BACK_SHININESS);

   for (i = 0; i < ctx->Const.MaxLights; i++) {
      /* light source parameters */
      COPY_4FV(v, ctx->Light.Light[i].Ambient);
      COPY_4FV((v + 4), ctx->Light.Light[i].Diffuse);
      COPY_4FV((v + 8), ctx->Light.Light[i].Specular);
      COPY_4FV((v + 12), ctx->Light.Light[i].EyePosition);
      COPY_2FV((v + 16), ctx->Light.Light[i].EyePosition);
      v[18] = ctx->Light.Light[i].EyePosition[2] + 1.0f;
      NORMALIZE_3FV((v + 16));
      v[19] = 0.0f;
      COPY_3V((v + 20), ctx->Light.Light[i].EyeDirection);
      v[23] = ctx->Light.Light[i].SpotExponent;
      v[24] = ctx->Light.Light[i].SpotCutoff;
      v[25] = ctx->Light.Light[i]._CosCutoffNeg;
      v[26] = ctx->Light.Light[i].ConstantAttenuation;
      v[27] = ctx->Light.Light[i].LinearAttenuation;
      v[28] = ctx->Light.Light[i].QuadraticAttenuation;
      write_common_fixed(pro, SLANG_COMMON_FIXED_LIGHTSOURCE, v, i,
                         29 * sizeof(GLfloat));

      /* light product */
      write_common_fixed_light_product(ctx, pro, i,
                                       SLANG_COMMON_FIXED_FRONTLIGHTPRODUCT,
                                       MAT_ATTRIB_FRONT_AMBIENT,
                                       MAT_ATTRIB_FRONT_DIFFUSE,
                                       MAT_ATTRIB_FRONT_SPECULAR);
      write_common_fixed_light_product(ctx, pro, i,
                                       SLANG_COMMON_FIXED_BACKLIGHTPRODUCT,
                                       MAT_ATTRIB_BACK_AMBIENT,
                                       MAT_ATTRIB_BACK_DIFFUSE,
                                       MAT_ATTRIB_BACK_SPECULAR);
   }

   /* light model parameters */
   write_common_fixed(pro, SLANG_COMMON_FIXED_LIGHTMODEL,
                      ctx->Light.Model.Ambient, 0, 4 * sizeof(GLfloat));

   /* light model product */
   write_common_fixed_light_model_product(ctx, pro,
                                          SLANG_COMMON_FIXED_FRONTLIGHTMODELPRODUCT,
                                          MAT_ATTRIB_FRONT_EMISSION,
                                          MAT_ATTRIB_FRONT_AMBIENT);
   write_common_fixed_light_model_product(ctx, pro,
                                          SLANG_COMMON_FIXED_BACKLIGHTMODELPRODUCT,
                                          MAT_ATTRIB_BACK_EMISSION,
                                          MAT_ATTRIB_BACK_AMBIENT);

   /* TEXTURE_ENV_COLOR */
   for (i = 0; i < ctx->Const.MaxTextureImageUnits; i++) {
      write_common_fixed(pro, SLANG_COMMON_FIXED_TEXTUREENVCOLOR,
                         ctx->Texture.Unit[i].EnvColor, i,
                         4 * sizeof(GLfloat));
   }

   /* fog parameters */
   COPY_4FV(v, ctx->Fog.Color);
   v[4] = ctx->Fog.Density;
   v[5] = ctx->Fog.Start;
   v[6] = ctx->Fog.End;
   v[7] = ctx->Fog._Scale;
   write_common_fixed(pro, SLANG_COMMON_FIXED_FOG, v, 0, 8 * sizeof(GLfloat));
}

static GLvoid
_program_UpdateFixedAttrib(struct gl2_program_intf **intf, GLuint index,
                           GLvoid * data, GLuint offset, GLuint size,
                           GLboolean write)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) intf;
   slang_program *pro = &impl->_obj.prog;
   GLuint addr;

   addr = pro->vertex_fixed_entries[index];
   if (addr != ~0) {
      GLubyte *mem;

      mem =
         (GLubyte *) pro->machines[SLANG_SHADER_VERTEX]->mem + addr +
         offset * size;
      if (write)
         _mesa_memcpy(mem, data, size);
      else
         _mesa_memcpy(data, mem, size);
   }
}


/**
 * Called during fragment shader execution to either load a varying
 * register with values, or fetch values from a varying register.
 * \param intf  the internal program?
 * \param index  which varying register, one of the SLANG_FRAGMENT_FIXED_*
 *               values for example.
 * \param data  source values to load (or dest to write to)
 * \param offset  indicates a texture unit or generic varying attribute
 * \param size  number of bytes to copy
 * \param write  if true, write to the varying register, else store values
 *               in 'data'
 */
static GLvoid
_program_UpdateFixedVarying(struct gl2_program_intf **intf, GLuint index,
                            GLvoid * data,
                            GLuint offset, GLuint size, GLboolean write)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) intf;
   slang_program *pro = &impl->_obj.prog;
   GLuint addr;

   addr = pro->fragment_fixed_entries[index];
   if (addr != ~0) {
      GLubyte *mem;

      mem =
         (GLubyte *) pro->machines[SLANG_SHADER_FRAGMENT]->mem + addr +
         offset * size;
      if (write)
         _mesa_memcpy(mem, data, size);
      else
         _mesa_memcpy(data, mem, size);
   }
}

static GLvoid
_program_GetTextureImageUsage(struct gl2_program_intf **intf,
                              GLbitfield * teximageusage)
{
   GET_CURRENT_CONTEXT(ctx);
   struct gl2_program_impl *impl = (struct gl2_program_impl *) intf;
   slang_program *pro = &impl->_obj.prog;
   GLuint i;

   for (i = 0; i < ctx->Const.MaxTextureImageUnits; i++)
      teximageusage[i] = 0;

   for (i = 0; i < pro->texture_usage.count; i++) {
      GLuint n, addr, j;

      n = slang_export_data_quant_elements(pro->texture_usage.table[i].quant);
      addr = pro->texture_usage.table[i].frag_address;
      for (j = 0; j < n; j++) {
         GLubyte *mem;
         GLuint image;

         mem =
            (GLubyte *) pro->machines[SLANG_SHADER_FRAGMENT]->mem + addr +
            j * 4;
         image = (GLuint) * ((GLfloat *) mem);
         if (image >= 0 && image < ctx->Const.MaxTextureImageUnits) {
            switch (slang_export_data_quant_type
                    (pro->texture_usage.table[i].quant)) {
            case GL_SAMPLER_1D_ARB:
            case GL_SAMPLER_1D_SHADOW_ARB:
               teximageusage[image] |= TEXTURE_1D_BIT;
               break;
            case GL_SAMPLER_2D_ARB:
            case GL_SAMPLER_2D_SHADOW_ARB:
               teximageusage[image] |= TEXTURE_2D_BIT;
               break;
            case GL_SAMPLER_3D_ARB:
               teximageusage[image] |= TEXTURE_3D_BIT;
               break;
            case GL_SAMPLER_CUBE_ARB:
               teximageusage[image] |= TEXTURE_CUBE_BIT;
               break;
            }
         }
      }
   }

   /* TODO: make sure that for 0<=i<=MaxTextureImageUint bitcount(teximageuint[i])<=0 */
}

static GLboolean
_program_IsShaderPresent(struct gl2_program_intf **intf, GLenum subtype)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) intf;
   slang_program *pro = &impl->_obj.prog;

   switch (subtype) {
   case GL_VERTEX_SHADER_ARB:
      return pro->machines[SLANG_SHADER_VERTEX] != NULL;
   case GL_FRAGMENT_SHADER_ARB:
      return pro->machines[SLANG_SHADER_FRAGMENT] != NULL;
   default:
      return GL_FALSE;
   }
}

static GLvoid
get_active_variable(slang_active_variable * var, GLsizei maxLength,
                    GLsizei * length, GLint * size, GLenum * type,
                    GLchar * name)
{
   GLsizei len;

   len = _mesa_strlen(var->name);
   if (len >= maxLength)
      len = maxLength - 1;
   if (length != NULL)
      *length = len;
   *size = slang_export_data_quant_elements(var->quant);
   *type = slang_export_data_quant_type(var->quant);
   _mesa_memcpy(name, var->name, len);
   name[len] = '\0';
}

static GLuint
get_active_variable_max_length(slang_active_variables * vars)
{
   GLuint i, len = 0;

   for (i = 0; i < vars->count; i++) {
      GLuint n = _mesa_strlen(vars->table[i].name);
      if (n > len)
         len = n;
   }
   return len;
}

static GLvoid
_program_GetActiveUniform(struct gl2_program_intf **intf, GLuint index,
                          GLsizei maxLength, GLsizei * length, GLint * size,
                          GLenum * type, GLchar * name)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) (intf);
   slang_active_variable *u = &impl->_obj.prog.active_uniforms.table[index];

   get_active_variable(u, maxLength, length, size, type, name);
}

static GLuint
_program_GetActiveUniformMaxLength(struct gl2_program_intf **intf)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) (intf);

   return get_active_variable_max_length(&impl->_obj.prog.active_uniforms);
}

static GLuint
_program_GetActiveUniformCount(struct gl2_program_intf **intf)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) (intf);

   return impl->_obj.prog.active_uniforms.count;
}

static GLint
_program_GetUniformLocation(struct gl2_program_intf **intf,
                            const GLchar * name)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) (intf);
   slang_uniform_bindings *bind = &impl->_obj.prog.uniforms;
   GLuint i;

   for (i = 0; i < bind->count; i++)
      if (_mesa_strcmp(bind->table[i].name, name) == 0)
         return i;
   return -1;
}

/**
 * Write a uniform variable into program's memory.
 * \return GL_TRUE for success, GL_FALSE if error
 */
static GLboolean
_program_WriteUniform(struct gl2_program_intf **intf, GLint loc,
                      GLsizei count, const GLvoid * data, GLenum type)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) (intf);
   slang_uniform_bindings *uniforms = &impl->_obj.prog.uniforms;
   slang_uniform_binding *uniform;
   GLuint i;
   GLboolean convert_float_to_bool = GL_FALSE;
   GLboolean convert_int_to_bool = GL_FALSE;
   GLboolean convert_int_to_float = GL_FALSE;
   GLboolean types_match = GL_FALSE;

   if (loc < 0 || loc >= uniforms->count)
      return GL_FALSE;

   uniform = &uniforms->table[loc];
   /* TODO: check sizes */
   if (slang_export_data_quant_struct(uniform->quant))
      return GL_FALSE;

   switch (slang_export_data_quant_type(uniform->quant)) {
   case GL_BOOL_ARB:
      types_match = (type == GL_FLOAT) || (type == GL_INT);
      if (type == GL_FLOAT)
         convert_float_to_bool = GL_TRUE;
      else
         convert_int_to_bool = GL_TRUE;
      break;
   case GL_BOOL_VEC2_ARB:
      types_match = (type == GL_FLOAT_VEC2_ARB) || (type == GL_INT_VEC2_ARB);
      if (type == GL_FLOAT_VEC2_ARB)
         convert_float_to_bool = GL_TRUE;
      else
         convert_int_to_bool = GL_TRUE;
      break;
   case GL_BOOL_VEC3_ARB:
      types_match = (type == GL_FLOAT_VEC3_ARB) || (type == GL_INT_VEC3_ARB);
      if (type == GL_FLOAT_VEC3_ARB)
         convert_float_to_bool = GL_TRUE;
      else
         convert_int_to_bool = GL_TRUE;
      break;
   case GL_BOOL_VEC4_ARB:
      types_match = (type == GL_FLOAT_VEC4_ARB) || (type == GL_INT_VEC4_ARB);
      if (type == GL_FLOAT_VEC4_ARB)
         convert_float_to_bool = GL_TRUE;
      else
         convert_int_to_bool = GL_TRUE;
      break;
   case GL_SAMPLER_1D_ARB:
   case GL_SAMPLER_2D_ARB:
   case GL_SAMPLER_3D_ARB:
   case GL_SAMPLER_CUBE_ARB:
   case GL_SAMPLER_1D_SHADOW_ARB:
   case GL_SAMPLER_2D_SHADOW_ARB:
      types_match = (type == GL_INT);
      break;
   default:
      types_match = (type == slang_export_data_quant_type(uniform->quant));
      break;
   }

   if (!types_match)
      return GL_FALSE;

   switch (type) {
   case GL_INT:
   case GL_INT_VEC2_ARB:
   case GL_INT_VEC3_ARB:
   case GL_INT_VEC4_ARB:
      convert_int_to_float = GL_TRUE;
      break;
   }

   for (i = 0; i < SLANG_SHADER_MAX; i++) {
      if (uniform->address[i] != ~0) {
         void *dest
            = &impl->_obj.prog.machines[i]->mem[uniform->address[i] / 4];
         /* total number of values to copy */
         GLuint total
            = count * slang_export_data_quant_components(uniform->quant);
         GLuint j;
         if (convert_float_to_bool) {
            const GLfloat *src = (GLfloat *) (data);
            GLfloat *dst = (GLfloat *) dest;
            for (j = 0; j < total; j++)
               dst[j] = src[j] != 0.0f ? 1.0f : 0.0f;
            break;
         }
         else if (convert_int_to_bool) {
            const GLint *src = (GLint *) (data);
            GLfloat *dst = (GLfloat *) dest;
            for (j = 0; j < total; j++)
               dst[j] = src[j] ? 1.0f : 0.0f;
            break;
         }
         else if (convert_int_to_float) {
            const GLint *src = (GLint *) (data);
            GLfloat *dst = (GLfloat *) dest;
            for (j = 0; j < total; j++)
               dst[j] = (GLfloat) src[j];
            break;
         }
         else {
            _mesa_memcpy(dest, data, total * sizeof(GLfloat));
            break;
         }
         break;
      }
   }
   return GL_TRUE;
}

/**
 * Read a uniform variable from program's memory.
 * \return GL_TRUE for success, GL_FALSE if error
 */
static GLboolean
_program_ReadUniform(struct gl2_program_intf **intf, GLint loc,
                     GLsizei count, GLvoid *data, GLenum type)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) (intf);
   const slang_uniform_bindings *uniforms = &impl->_obj.prog.uniforms;
   const slang_uniform_binding *uniform;
   GLuint i;
   GLboolean convert_bool_to_float = GL_FALSE;
   GLboolean convert_bool_to_int = GL_FALSE;
   GLboolean convert_float_to_int = GL_FALSE;
   GLboolean types_match = GL_FALSE;

   if (loc < 0 || loc >= uniforms->count)
      return GL_FALSE;

   uniform = &uniforms->table[loc];

   if (slang_export_data_quant_struct(uniform->quant))
      return GL_FALSE;

   switch (slang_export_data_quant_type(uniform->quant)) {
   case GL_BOOL_ARB:
      types_match = (type == GL_FLOAT) || (type == GL_INT);
      if (type == GL_FLOAT)
         convert_bool_to_float = GL_TRUE;
      else
         convert_bool_to_int = GL_TRUE;
      break;
   case GL_BOOL_VEC2_ARB:
      types_match = (type == GL_FLOAT_VEC2_ARB) || (type == GL_INT_VEC2_ARB);
      if (type == GL_FLOAT_VEC2_ARB)
         convert_bool_to_float = GL_TRUE;
      else
         convert_bool_to_int = GL_TRUE;
      break;
   case GL_BOOL_VEC3_ARB:
      types_match = (type == GL_FLOAT_VEC3_ARB) || (type == GL_INT_VEC3_ARB);
      if (type == GL_FLOAT_VEC3_ARB)
         convert_bool_to_float = GL_TRUE;
      else
         convert_bool_to_int = GL_TRUE;
      break;
   case GL_BOOL_VEC4_ARB:
      types_match = (type == GL_FLOAT_VEC4_ARB) || (type == GL_INT_VEC4_ARB);
      if (type == GL_FLOAT_VEC4_ARB)
         convert_bool_to_float = GL_TRUE;
      else
         convert_bool_to_int = GL_TRUE;
      break;
   case GL_SAMPLER_1D_ARB:
   case GL_SAMPLER_2D_ARB:
   case GL_SAMPLER_3D_ARB:
   case GL_SAMPLER_CUBE_ARB:
   case GL_SAMPLER_1D_SHADOW_ARB:
   case GL_SAMPLER_2D_SHADOW_ARB:
      types_match = (type == GL_INT);
      break;
   default:
      /* uniform is a float type */
      types_match = (type == GL_FLOAT);
      break;
   }

   if (!types_match)
      return GL_FALSE;

   switch (type) {
   case GL_INT:
   case GL_INT_VEC2_ARB:
   case GL_INT_VEC3_ARB:
   case GL_INT_VEC4_ARB:
      convert_float_to_int = GL_TRUE;
      break;
   }

   for (i = 0; i < SLANG_SHADER_MAX; i++) {
      if (uniform->address[i] != ~0) {
         /* XXX if bools are really implemented as floats, some of this
          * could probably be culled out.
          */
         const void *source
            = &impl->_obj.prog.machines[i]->mem[uniform->address[i] / 4];
         /* total number of values to copy */
         const GLuint total
            = count * slang_export_data_quant_components(uniform->quant);
         GLuint j;
         if (convert_bool_to_float) {
            GLfloat *dst = (GLfloat *) (data);
            const GLfloat *src = (GLfloat *) source;
            for (j = 0; j < total; j++)
               dst[j] = src[j] == 0.0 ? 0.0 : 1.0;
         }
         else if (convert_bool_to_int) {
            GLint *dst = (GLint *) (data);
            const GLfloat *src = (GLfloat *) source;
            for (j = 0; j < total; j++)
               dst[j] = src[j] == 0.0 ? 0 : 1;
         }
         else if (convert_float_to_int) {
            GLint *dst = (GLint *) (data);
            const GLfloat *src = (GLfloat *) source;
            for (j = 0; j < total; j++)
               dst[j] = (GLint) src[j];
         }
         else {
            /* no type conversion needed */
            _mesa_memcpy(data, source, total * sizeof(GLfloat));
         }
         break;
      } /* if */
   } /* for */

   return GL_TRUE;
}


static GLvoid
_program_GetActiveAttrib(struct gl2_program_intf **intf, GLuint index,
                         GLsizei maxLength, GLsizei * length, GLint * size,
                         GLenum * type, GLchar * name)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) (intf);
   slang_active_variable *a = &impl->_obj.prog.active_attribs.table[index];

   get_active_variable(a, maxLength, length, size, type, name);
}

static GLuint
_program_GetActiveAttribMaxLength(struct gl2_program_intf **intf)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) (intf);

   return get_active_variable_max_length(&impl->_obj.prog.active_attribs);
}

static GLuint
_program_GetActiveAttribCount(struct gl2_program_intf **intf)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) (intf);

   return impl->_obj.prog.active_attribs.count;
}

static GLint
_program_GetAttribLocation(struct gl2_program_intf **intf,
                           const GLchar * name)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) (intf);
   slang_attrib_bindings *attribs = &impl->_obj.prog.attribs;
   GLuint i;

   for (i = 0; i < attribs->binding_count; i++)
      if (_mesa_strcmp(attribs->bindings[i].name, name) == 0)
         return attribs->bindings[i].first_slot_index;
   return -1;
}

static GLvoid
_program_OverrideAttribBinding(struct gl2_program_intf **intf, GLuint index,
                               const GLchar * name)
{
   GET_CURRENT_CONTEXT(ctx);
   struct gl2_program_impl *impl = (struct gl2_program_impl *) (intf);
   slang_program *pro = &impl->_obj.prog;

   if (!_slang_attrib_overrides_add(&pro->attrib_overrides, index, name))
      _mesa_error(ctx, GL_OUT_OF_MEMORY, "_program_OverrideAttribBinding");
}

static GLvoid
_program_WriteAttrib(struct gl2_program_intf **intf, GLuint index,
                     const GLfloat * value)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) (intf);
   slang_program *pro = &impl->_obj.prog;
   slang_attrib_slot *slot = &pro->attribs.slots[index];

   /*
    * Generic attributes can be allocated in a shader with scalar, vec
    * or mat type.  For scalar and vec types (specifically float, vec2
    * and vec3) this is simple - just ignore the extra components. For
    * mat type this is more complicated - the vertex_shader spec
    * requires to store every column of a matrix in a separate attrib
    * slot.  To prvent from overwriting data from neighbouring matrix
    * columns, the "fill" information is kept to know how many
    * components to copy.
    */

   if (slot->addr != ~0)
      _mesa_memcpy(&pro->machines[SLANG_SHADER_VERTEX]->mem[slot->addr / 4].
                   _float, value, slot->fill * sizeof(GLfloat));
}

static GLvoid
_program_UpdateVarying(struct gl2_program_intf **intf, GLuint index,
                       GLfloat * value, GLboolean vert)
{
   struct gl2_program_impl *impl = (struct gl2_program_impl *) intf;
   slang_program *pro = &impl->_obj.prog;
   GLuint addr;

   if (index >= pro->varyings.slot_count)
      return;
   if (vert)
      addr = pro->varyings.slots[index].vert_addr / 4;
   else
      addr = pro->varyings.slots[index].frag_addr / 4;
   if (addr != ~0) {
      if (vert)
         *value = pro->machines[SLANG_SHADER_VERTEX]->mem[addr]._float;
      else
         pro->machines[SLANG_SHADER_FRAGMENT]->mem[addr]._float = *value;
   }
}

static struct gl2_program_intf _program_vftbl = {
   {
      {
         {
            _unknown_AddRef,
            _unknown_Release,
            _program_QueryInterface
         },
         _generic_Delete,
         _program_GetType,
         _generic_GetName,
         _generic_GetDeleteStatus,
         _generic_GetInfoLog,
         _generic_GetInfoLogLength
      },
      _program_Attach,
      _container_Detach,
      _container_GetAttachedCount,
      _container_GetAttached
   },
   _program_GetLinkStatus,
   _program_GetValidateStatus,
   _program_Link,
   _program_Validate,
   _program_UpdateFixedUniforms,
   _program_UpdateFixedAttrib,
   _program_UpdateFixedVarying,
   _program_GetTextureImageUsage,
   _program_IsShaderPresent,
   _program_GetActiveUniform,
   _program_GetActiveUniformMaxLength,
   _program_GetActiveUniformCount,
   _program_GetUniformLocation,
   _program_WriteUniform,
   _program_ReadUniform,
   _program_GetActiveAttrib,
   _program_GetActiveAttribMaxLength,
   _program_GetActiveAttribCount,
   _program_GetAttribLocation,
   _program_OverrideAttribBinding,
   _program_WriteAttrib,
   _program_UpdateVarying
};

static void
_program_constructor(struct gl2_program_impl *impl)
{
   _container_constructor((struct gl2_container_impl *) impl);
   impl->_vftbl = &_program_vftbl;
   impl->_obj._container._generic._unknown._destructor = _program_destructor;
   impl->_obj.link_status = GL_FALSE;
   impl->_obj.validate_status = GL_FALSE;
#if USE_3DLABS_FRONTEND
   impl->_obj.linker = ShConstructLinker(EShExVertexFragment, 0);
   impl->_obj.uniforms = ShConstructUniformMap();
#endif
   _slang_program_ctr(&impl->_obj.prog);
}

struct gl2_fragment_shader_obj
{
   struct gl2_shader_obj _shader;
};

struct gl2_fragment_shader_impl
{
   struct gl2_fragment_shader_intf *_vftbl;
   struct gl2_fragment_shader_obj _obj;
};

static void
_fragment_shader_destructor(struct gl2_unknown_intf **intf)
{
   struct gl2_fragment_shader_impl *impl =
      (struct gl2_fragment_shader_impl *) intf;

   (void) impl;
   /* TODO free fragment shader data */

   _shader_destructor(intf);
}

static struct gl2_unknown_intf **
_fragment_shader_QueryInterface(struct gl2_unknown_intf **intf,
                                enum gl2_uiid uiid)
{
   if (uiid == UIID_FRAGMENT_SHADER) {
      (**intf).AddRef(intf);
      return intf;
   }
   return _shader_QueryInterface(intf, uiid);
}

static GLenum
_fragment_shader_GetSubType(struct gl2_shader_intf **intf)
{
   return GL_FRAGMENT_SHADER_ARB;
}

static struct gl2_fragment_shader_intf _fragment_shader_vftbl = {
   {
      {
         {
            _unknown_AddRef,
            _unknown_Release,
            _fragment_shader_QueryInterface
         },
         _generic_Delete,
         _shader_GetType,
         _generic_GetName,
         _generic_GetDeleteStatus,
         _shader_GetInfoLog,
         _shader_GetInfoLogLength
      },
      _fragment_shader_GetSubType,
      _shader_GetCompileStatus,
      _shader_SetSource,
      _shader_GetSource,
      _shader_Compile
   }
};

static void
_fragment_shader_constructor(struct gl2_fragment_shader_impl *impl)
{
   _shader_constructor((struct gl2_shader_impl *) impl);
   impl->_vftbl = &_fragment_shader_vftbl;
   impl->_obj._shader._generic._unknown._destructor =
      _fragment_shader_destructor;
#if USE_3DLABS_FRONTEND
   impl->_obj._shader._3dlabs_shhandle._obj.handle =
      ShConstructCompiler(EShLangFragment, 0);
#endif
}

struct gl2_vertex_shader_obj
{
   struct gl2_shader_obj _shader;
};

struct gl2_vertex_shader_impl
{
   struct gl2_vertex_shader_intf *_vftbl;
   struct gl2_vertex_shader_obj _obj;
};

static void
_vertex_shader_destructor(struct gl2_unknown_intf **intf)
{
   struct gl2_vertex_shader_impl *impl =
      (struct gl2_vertex_shader_impl *) intf;

   (void) impl;
   /* TODO free vertex shader data */

   _shader_destructor(intf);
}

static struct gl2_unknown_intf **
_vertex_shader_QueryInterface(struct gl2_unknown_intf **intf,
                              enum gl2_uiid uiid)
{
   if (uiid == UIID_VERTEX_SHADER) {
      (**intf).AddRef(intf);
      return intf;
   }
   return _shader_QueryInterface(intf, uiid);
}

static GLenum
_vertex_shader_GetSubType(struct gl2_shader_intf **intf)
{
   return GL_VERTEX_SHADER_ARB;
}

static struct gl2_vertex_shader_intf _vertex_shader_vftbl = {
   {
      {
         {
            _unknown_AddRef,
            _unknown_Release,
            _vertex_shader_QueryInterface
         },
         _generic_Delete,
         _shader_GetType,
         _generic_GetName,
         _generic_GetDeleteStatus,
         _shader_GetInfoLog,
         _shader_GetInfoLogLength
      },
      _vertex_shader_GetSubType,
      _shader_GetCompileStatus,
      _shader_SetSource,
      _shader_GetSource,
      _shader_Compile
   }
};

static void
_vertex_shader_constructor(struct gl2_vertex_shader_impl *impl)
{
   _shader_constructor((struct gl2_shader_impl *) impl);
   impl->_vftbl = &_vertex_shader_vftbl;
   impl->_obj._shader._generic._unknown._destructor =
      _vertex_shader_destructor;
#if USE_3DLABS_FRONTEND
   impl->_obj._shader._3dlabs_shhandle._obj.handle =
      ShConstructCompiler(EShLangVertex, 0);
#endif
}

struct gl2_debug_obj
{
   struct gl2_generic_obj _generic;
};

struct gl2_debug_impl
{
   struct gl2_debug_intf *_vftbl;
   struct gl2_debug_obj _obj;
};

static GLvoid
_debug_destructor(struct gl2_unknown_intf **intf)
{
   struct gl2_debug_impl *impl = (struct gl2_debug_impl *) (intf);

   (void) (impl);
   /* TODO */

   _generic_destructor(intf);
}

static struct gl2_unknown_intf **
_debug_QueryInterface(struct gl2_unknown_intf **intf, enum gl2_uiid uiid)
{
   if (uiid == UIID_DEBUG) {
      (**intf).AddRef(intf);
      return intf;
   }
   return _generic_QueryInterface(intf, uiid);
}

static GLenum
_debug_GetType(struct gl2_generic_intf **intf)
{
   return /*GL_DEBUG_OBJECT_MESA */ 0;
}

static GLvoid
_debug_ClearDebugLog(struct gl2_debug_intf **intf, GLenum logType,
                     GLenum shaderType)
{
   struct gl2_debug_impl *impl = (struct gl2_debug_impl *) (intf);

   (void) (impl);
   /* TODO */
}

static GLvoid
_debug_GetDebugLog(struct gl2_debug_intf **intf, GLenum logType,
                   GLenum shaderType, GLsizei maxLength, GLsizei * length,
                   GLcharARB * infoLog)
{
   struct gl2_debug_impl *impl = (struct gl2_debug_impl *) (intf);

   (void) (impl);
   /* TODO */
}

static GLsizei
_debug_GetDebugLogLength(struct gl2_debug_intf **intf, GLenum logType,
                         GLenum shaderType)
{
   struct gl2_debug_impl *impl = (struct gl2_debug_impl *) (intf);

   (void) (impl);
   /* TODO */

   return 0;
}

static struct gl2_debug_intf _debug_vftbl = {
   {
      {
         _unknown_AddRef,
         _unknown_Release,
         _debug_QueryInterface
      },
      _generic_Delete,
      _debug_GetType,
      _generic_GetName,
      _generic_GetDeleteStatus,
      _generic_GetInfoLog,
      _generic_GetInfoLogLength
   },
   _debug_ClearDebugLog,
   _debug_GetDebugLog,
   _debug_GetDebugLogLength
};

static GLvoid
_debug_constructor(struct gl2_debug_impl *impl)
{
   _generic_constructor((struct gl2_generic_impl *) (impl));
   impl->_vftbl = &_debug_vftbl;
   impl->_obj._generic._unknown._destructor = _debug_destructor;
}

GLhandleARB
_mesa_3dlabs_create_shader_object(GLenum shaderType)
{
   switch (shaderType) {
   case GL_FRAGMENT_SHADER_ARB:
      {
         struct gl2_fragment_shader_impl *x =
            (struct gl2_fragment_shader_impl *)
            _mesa_malloc(sizeof(struct gl2_fragment_shader_impl));

         if (x != NULL) {
            _fragment_shader_constructor(x);
            return x->_obj._shader._generic.name;
         }
      }
      break;
   case GL_VERTEX_SHADER_ARB:
      {
         struct gl2_vertex_shader_impl *x = (struct gl2_vertex_shader_impl *)
            _mesa_malloc(sizeof(struct gl2_vertex_shader_impl));

         if (x != NULL) {
            _vertex_shader_constructor(x);
            return x->_obj._shader._generic.name;
         }
      }
      break;
   }

   return 0;
}

GLhandleARB
_mesa_3dlabs_create_program_object(void)
{
   struct gl2_program_impl *x = (struct gl2_program_impl *)
      _mesa_malloc(sizeof(struct gl2_program_impl));

   if (x != NULL) {
      _program_constructor(x);
      return x->_obj._container._generic.name;
   }

   return 0;
}

GLhandleARB
_mesa_3dlabs_create_debug_object(GLvoid)
{
   struct gl2_debug_impl *obj;

   obj =
      (struct gl2_debug_impl *) (_mesa_malloc(sizeof(struct gl2_debug_impl)));
   if (obj != NULL) {
      _debug_constructor(obj);
      return obj->_obj._generic.name;
   }
   return 0;
}

#include "slang_assemble.h"
#include "slang_execute.h"

int
_slang_fetch_discard(struct gl2_program_intf **pro, GLboolean * val)
{
   struct gl2_program_impl *impl;

   impl = (struct gl2_program_impl *) pro;
   *val =
      impl->_obj.prog.machines[SLANG_SHADER_FRAGMENT]->
      kill ? GL_TRUE : GL_FALSE;
   return 1;
}

static GLvoid
exec_shader(struct gl2_program_intf **pro, GLuint i)
{
   struct gl2_program_impl *impl;
   slang_program *p;

   impl = (struct gl2_program_impl *) pro;
   p = &impl->_obj.prog;

   slang_machine_init(p->machines[i]);
   p->machines[i]->ip = p->code[i][SLANG_COMMON_CODE_MAIN];

   _slang_execute2(p->assemblies[i], p->machines[i]);
}

GLvoid
_slang_exec_fragment_shader(struct gl2_program_intf **pro)
{
   exec_shader(pro, SLANG_SHADER_FRAGMENT);
}

GLvoid
_slang_exec_vertex_shader(struct gl2_program_intf **pro)
{
   exec_shader(pro, SLANG_SHADER_VERTEX);
}

#endif

void
_mesa_init_shaderobjects_3dlabs(GLcontext * ctx)
{
#if USE_3DLABS_FRONTEND
   _glslang_3dlabs_InitProcess();
   _glslang_3dlabs_ShInitialize();
#endif
}