C++程序  |  971行  |  32.81 KB

/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "src/pixelflinger2/pixelflinger2.h"

#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <map>

#include <llvm/LLVMContext.h>
#include <llvm/Module.h>
#include <bcc/bcc.h>
#include <dlfcn.h>


#include "src/talloc/hieralloc.h"
#include "src/mesa/main/shaderobj.h"
#include "src/mesa/program/prog_parameter.h"
#include "src/mesa/program/prog_uniform.h"
#include "src/glsl/glsl_types.h"
#include "src/glsl/ir_to_llvm.h"
#include "src/glsl/ir_print_visitor.h"

//#undef LOGD
//#define LOGD(...)

static void InitializeGLContext(struct gl_context *ctx)
{
   memset(ctx, 0, sizeof(*ctx));
   ctx->API = API_OPENGLES2;
   ctx->Extensions.ARB_draw_buffers = GL_TRUE;
   ctx->Extensions.ARB_fragment_coord_conventions = GL_TRUE;
   ctx->Extensions.EXT_texture_array = GL_TRUE;
   ctx->Extensions.NV_texture_rectangle = GL_TRUE;

   /* 1.10 minimums. */
   ctx->Const.MaxLights = 8;
   ctx->Const.MaxClipPlanes = 8;
   ctx->Const.MaxTextureUnits = 2;

   /* More than the 1.10 minimum to appease parser tests taken from
    * apps that (hopefully) already checked the number of coords.
    */
   ctx->Const.MaxTextureCoordUnits = 4;

   ctx->Const.VertexProgram.MaxAttribs = 16;
   ctx->Const.VertexProgram.MaxUniformComponents = 512;
   ctx->Const.MaxVarying = 8;
   ctx->Const.MaxVertexTextureImageUnits = 0;
   ctx->Const.MaxCombinedTextureImageUnits = 2;
   ctx->Const.MaxTextureImageUnits = 2;
   ctx->Const.FragmentProgram.MaxUniformComponents = 64;

   ctx->Const.MaxDrawBuffers = 2;
}

void * llvmCtx = NULL;
static const struct GLContext {
   const gl_context * ctx;
   GLContext() {
      ctx = hieralloc_zero(NULL, gl_context);
//      ctx = (const gl_context*)calloc(1,sizeof(gl_context));
      InitializeGLContext(const_cast<gl_context *>(ctx));
      llvmCtx = new llvm::LLVMContext();
   }
   ~GLContext() {
      _mesa_glsl_release_types(); // TODO: find when to release to minize memory
      _mesa_glsl_release_functions(); // the IR has pointers to types
      hieralloc_free(const_cast<gl_context *>(ctx));
//      free(const_cast<gl_context *>(ctx));
      ctx = NULL;
      delete (llvm::LLVMContext *)llvmCtx;
   }
} glContext;

extern "C" void GLContextDctr()
{
   _mesa_glsl_release_types(); // TODO: find when to release to minize memory
   _mesa_glsl_release_functions();
   //glContext.~GLContext();
}

struct ShaderKey {
   struct ScanLineKey {
      GGLStencilState frontStencil, backStencil;
      GGLBufferState bufferState;
      GGLBlendState blendState;
   } scanLineKey;
   GGLPixelFormat textureFormats[GGL_MAXCOMBINEDTEXTUREIMAGEUNITS];
   unsigned char textureParameters[GGL_MAXCOMBINEDTEXTUREIMAGEUNITS]; // wrap and filter
   bool operator <(const ShaderKey & rhs) const {
      return memcmp(this, &rhs, sizeof(*this)) < 0;
   }
};

struct Instance {
   llvm::Module * module;
   struct BCCOpaqueScript * script;
   void (* function)();
   ~Instance() {
      // TODO: check bccDisposeScript, which seems to dispose llvm::Module
      if (script)
         bccDisposeScript(script);
      else if (module)
         delete module;
   }
};

struct Executable { // codegen info
   std::map<ShaderKey, Instance *> instances;
};

bool do_mat_op_to_vec(exec_list *instructions);

extern void link_shaders(const struct gl_context *ctx, struct gl_shader_program *prog);

extern "C" void compile_shader(const struct gl_context *ctx, struct gl_shader *shader);

gl_shader * GGLShaderCreate(GLenum type)
{
   return _mesa_new_shader(NULL, 0, type);
}

static gl_shader * ShaderCreate(const GGLInterface * iface, GLenum type)
{
   GGL_GET_CONST_CONTEXT(ctx, iface);
   if (GL_VERTEX_SHADER != type && GL_FRAGMENT_SHADER != type) {
      gglError(GL_INVALID_ENUM);
      return NULL;
   }
   gl_shader * shader = _mesa_new_shader(NULL, 0, type);
   if (!shader)
      gglError(GL_OUT_OF_MEMORY);
   assert(1 == shader->RefCount);
   return shader;
}

void GGLShaderSource(gl_shader_t * shader, GLsizei count, const char ** string, const int * length)
{
   hieralloc_free(const_cast<GLchar *>(shader->Source));
   for (unsigned i = 0; i < count; i++) {
      int len = strlen(string[i]);
      if (length && length[i] >= 0)
         len = length[i];
      shader->Source = hieralloc_strndup_append(const_cast<GLchar *>(shader->Source), string[i], len);
   }
//   LOGD("pf2: GGLShaderSource: \n '%s' \n", shader->Source);
}

GLboolean GGLShaderCompile(gl_shader * shader, const char * glsl, const char ** infoLog)
{
   if (glsl)
      shader->Source = glsl;
   assert(shader->Source);
   compile_shader(glContext.ctx, shader);
   if (glsl)
      shader->Source = NULL;
   if (infoLog)
      *infoLog = shader->InfoLog;
   return shader->CompileStatus;
}

static GLboolean ShaderCompile(const GGLInterface * iface, gl_shader * shader,
                               const char * glsl, const char ** infoLog)
{
   GGL_GET_CONST_CONTEXT(ctx, iface);
   if (!glsl && !shader->Source) {
      gglError(GL_INVALID_VALUE);
      assert(0);
      return GL_FALSE;
   }
   return GGLShaderCompile(shader, glsl, infoLog);
}

void GGLShaderDelete(gl_shader * shader)
{
   if (shader && shader->executable) {
      for (std::map<ShaderKey, Instance *>::iterator it=shader->executable->instances.begin();
            it != shader->executable->instances.end(); it++)
         (*it).second->~Instance();
      shader->executable->instances.~map();
   }
   _mesa_delete_shader(NULL, shader);
}

static void ShaderDelete(const GGLInterface * iface, gl_shader * shader)
{
   GGLShaderDelete(shader);
}

gl_shader_program * GGLShaderProgramCreate()
{
   gl_shader_program * program = hieralloc_zero(NULL, struct gl_shader_program);
   if (!program)
      return NULL;
   program->Attributes = hieralloc_zero(program, gl_program_parameter_list);
   if (!program->Attributes) {
      hieralloc_free(program);
      return NULL;
   }
   program->Varying = hieralloc_zero(program, gl_program_parameter_list);
   if (!program->Varying) {
      hieralloc_free(program);
      return NULL;
   }
   return program;
}

static gl_shader_program * ShaderProgramCreate(const GGLInterface * iface)
{
   GGL_GET_CONST_CONTEXT(ctx, iface);
   gl_shader_program * program = GGLShaderProgramCreate();
   if (!program)
      gglError(GL_OUT_OF_MEMORY);
   return program;
}

unsigned GGLShaderAttach(gl_shader_program * program, gl_shader * shader)
{
   for (unsigned i = 0; i < program->NumShaders; i++)
      if (program->Shaders[i]->Type == shader->Type || program->Shaders[i] == shader)
         return GL_INVALID_OPERATION;

   program->Shaders = (gl_shader **)hieralloc_realloc
                      (program, program->Shaders, gl_shader *, program->NumShaders + 1);
   if (!program->Shaders) {
      assert(0);
      return GL_OUT_OF_MEMORY;
   }
   program->Shaders[program->NumShaders] = shader;
   program->NumShaders++;
   shader->RefCount++;
   return GL_NO_ERROR;
}

static void ShaderAttach(const GGLInterface * iface, gl_shader_program * program,
                         gl_shader * shader)
{
   unsigned error = GGLShaderAttach(program, shader);
   if (GL_NO_ERROR != error)
      gglError(error);
}

GLboolean GGLShaderProgramLink(gl_shader_program * program, const char ** infoLog)
{
   link_shaders(glContext.ctx, program);
   if (infoLog)
      *infoLog = program->InfoLog;
   if (!program->LinkStatus)
      return program->LinkStatus;
   LOGD("slots: attribute=%d varying=%d uniforms=%d \n", program->AttributeSlots, program->VaryingSlots, program->Uniforms->Slots);
//   for (unsigned i = 0; i < program->Attributes->NumParameters; i++) {
//      const gl_program_parameter & attribute = program->Attributes->Parameters[i];
//      LOGD("attribute '%s': location=%d slots=%d \n", attribute.Name, attribute.Location, attribute.Slots);
//   }
//   for (unsigned i = 0; i < program->Varying->NumParameters; i++) {
//      const gl_program_parameter & varying = program->Varying->Parameters[i];
//      LOGD("varying '%s': vs_location=%d fs_location=%d \n", varying.Name, varying.BindLocation, varying.Location);
//   }
   for (unsigned i = 0; i < program->Uniforms->NumUniforms; i++) {
      const gl_uniform & uniform = program->Uniforms->Uniforms[i];
      LOGD("uniform '%s': location=%d type=%s \n", uniform.Name, uniform.Pos, uniform.Type->name);
   }
   return program->LinkStatus;
}

static GLboolean ShaderProgramLink(gl_shader_program * program, const char ** infoLog)
{
   return GGLShaderProgramLink(program, infoLog);
}

static void GetShaderKey(const GGLState * ctx, const gl_shader * shader, ShaderKey * key)
{
   memset(key, 0, sizeof(*key));
   if (GL_FRAGMENT_SHADER == shader->Type) {
      key->scanLineKey.frontStencil = ctx->frontStencil;
      key->scanLineKey.backStencil = ctx->backStencil;
      key->scanLineKey.bufferState = ctx->bufferState;
      key->scanLineKey.blendState = ctx->blendState;
   }

   for (unsigned i = 0; i < GGL_MAXCOMBINEDTEXTUREIMAGEUNITS; i++)
      if (shader->SamplersUsed & (1 << i)) {
         const GGLTexture & texture = ctx->textureState.textures[i];
         key->textureFormats[i] = texture.format;
         assert((1 << 2) > texture.wrapS);
         key->textureParameters[i] |= texture.wrapS;
         assert((1 << 2) > texture.wrapT);
         key->textureParameters[i] |= texture.wrapT << 2;
         assert((1 << 3) > texture.minFilter);
         key->textureParameters[i] |= texture.minFilter << (2 + 2);
         assert((1 << 1) > texture.magFilter);
         key->textureParameters[i] |= texture.magFilter << (2 + 2 + 3);
      }
}

static inline char HexDigit(unsigned char d)
{
   return (d > 9 ? d + 'A' - 10 : d + '0');
}

static const unsigned SHADER_KEY_STRING_LEN = GGL_MAXCOMBINEDTEXTUREIMAGEUNITS * 4 + 2;

static void GetShaderKeyString(const GLenum type, const ShaderKey * key,
                               char * buffer, const unsigned bufferSize)
{
   assert(1 == sizeof(char));
   assert(0xff >= GGL_PIXEL_FORMAT_COUNT);
   assert(SHADER_KEY_STRING_LEN <= bufferSize);
   char * str = buffer;
   if (GL_VERTEX_SHADER == type)
      *str++ = 'v';
   else if (GL_FRAGMENT_SHADER == type)
      *str++ = 'f';
   else
      assert(0);
   for (unsigned i = 0; i < GGL_MAXCOMBINEDTEXTUREIMAGEUNITS; i++) {
      *str++ = HexDigit(key->textureFormats[i] / 16);
      *str++ = HexDigit(key->textureFormats[i] % 16);
      *str++ = HexDigit(key->textureParameters[i] / 16);
      *str++ = HexDigit(key->textureParameters[i] % 16);
   }
   *str++ = '\0';
}

static const unsigned SCANLINE_KEY_STRING_LEN = 2 * sizeof(ShaderKey::ScanLineKey) + 3 + SHADER_KEY_STRING_LEN;

static char * GetScanlineKeyString(const ShaderKey * key, char * buffer,
                                   const unsigned bufferSize)
{
   assert(1 == sizeof(char));
   assert(0xff >= GGL_PIXEL_FORMAT_COUNT);
   assert(SCANLINE_KEY_STRING_LEN <= bufferSize);
   char * str = buffer;
   *str++ = 's';
   const unsigned char * start = (const unsigned char *)&key->scanLineKey;
   const unsigned char * const end = start + sizeof(key->scanLineKey);
   for (; start < end; start++) {
      *str++ = HexDigit(*start / 16);
      *str++ = HexDigit(*start % 16);
   }
   GetShaderKeyString(GL_FRAGMENT_SHADER, key, str, bufferSize - (str - buffer));
   return buffer;
}

struct SymbolLookupContext {
   const GGLState * gglCtx;
   const gl_shader_program * program;
   const gl_shader * shader;
};

static void* SymbolLookup(void* pContext, const char* name)
{
   SymbolLookupContext * ctx = (SymbolLookupContext *)pContext;
   const GGLState * gglCtx = ctx->gglCtx;
   const void * symbol = (void*)dlsym(RTLD_DEFAULT, name);
   if (NULL == symbol) {
      if (!strcmp(_PF2_TEXTURE_DATA_NAME_, name))
         symbol = (void *)gglCtx->textureState.textureData;
      else if (!strcmp(_PF2_TEXTURE_DIMENSIONS_NAME_, name))
         symbol = (void *)gglCtx->textureState.textureDimensions;
      else // attributes, varyings and uniforms are mapped to locations in pointers
      {
         LOGD("pf2: SymbolLookup unknown symbol: '%s'", name);
         assert(0);
      }
   }
//   printf("symbolLookup '%s'=%p \n", name, symbol);
   assert(symbol);
   return (void *)symbol;
}

static void CodeGen(Instance * instance, const char * mainName, gl_shader * shader,
                    gl_shader_program * program, const GGLState * gglCtx)
{
   SymbolLookupContext ctx = {gglCtx, program, shader};
   int result = 0;

//   instance->module->dump();

   BCCScriptRef & script = instance->script;
   script = bccCreateScript();
   result = bccReadModule(script, "glsl", (LLVMModuleRef)instance->module, 0);
   assert(0 == result);
   result = bccRegisterSymbolCallback(script, SymbolLookup, &ctx);
   assert(0 == result);
   result = bccPrepareExecutable(script, NULL, NULL, 0);

   result = bccGetError(script);
   if (result != 0) {
      LOGD("failed bcc_compile");
      assert(0);
      return;
   }

   instance->function = (void (*)())bccGetFuncAddr(script, mainName);
   assert(instance->function);
   result = bccGetError(script);
   if (result != BCC_NO_ERROR)
      LOGD("Could not find '%s': %d\n", mainName, result);
//   else
//      printf("bcc_compile %s=%p \n", mainName, instance->function);

//   assert(0);
}

void GenerateScanLine(const GGLState * gglCtx, const gl_shader_program * program, llvm::Module * mod,
                      const char * shaderName, const char * scanlineName);

void GGLShaderUse(void * llvmCtx, const GGLState * gglState, gl_shader_program * program)
{
//   LOGD("%s", program->Shaders[MESA_SHADER_FRAGMENT]->Source);
   for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
      if (!program->_LinkedShaders[i])
         continue;
      gl_shader * shader = program->_LinkedShaders[i];
      shader->function = NULL;
      if (!shader->executable) {
         shader->executable = hieralloc_zero(shader, Executable);
         shader->executable->instances = std::map<ShaderKey, Instance *>();
      }

      ShaderKey shaderKey;
      GetShaderKey(gglState, shader, &shaderKey);
      Instance * instance = shader->executable->instances[shaderKey];
      if (!instance) {
//         puts("begin jit new shader");
         instance = hieralloc_zero(shader->executable, Instance);
         instance->module = new llvm::Module("glsl", *(llvm::LLVMContext *)llvmCtx);

         char shaderName [SHADER_KEY_STRING_LEN] = {0};
         GetShaderKeyString(shader->Type, &shaderKey, shaderName, sizeof shaderName / sizeof *shaderName);

         char mainName [SHADER_KEY_STRING_LEN + 6] = {"main"};
         strcat(mainName, shaderName);

         do_mat_op_to_vec(shader->ir); // TODO: move these passes to link?
//#ifdef __arm__
//         static const char fileName[] = "/data/pf2.txt";
//         FILE * file = freopen(fileName, "w", stdout);
//         assert(file);
//         *stdout = *file;
//         std::ios_base::sync_with_stdio(true);
//#endif
//         _mesa_print_ir(shader->ir, NULL);
//#ifdef __arm__
//         fclose(file);
//         file = fopen(fileName, "r");
//         assert(file);
//         static char str[256];
//         while (!feof(file)) {
//            fgets(str, sizeof(str) - 1, file);
//            str[sizeof(str) - 1] = 0;
//            LOGD("%s", str);
//         }
//         fclose(file);
//#endif
         llvm::Module * module = glsl_ir_to_llvm_module(shader->ir, instance->module, gglState, shaderName);
         if (!module)
            assert(0);
//#ifdef __arm__
//         static const char fileName[] = "/data/pf2.txt";
//         FILE * file = freopen(fileName, "w", stderr);
//         assert(file);
//         *stderr = *file;
//         std::ios_base::sync_with_stdio(true);
//#endif

//         if (strstr(program->Shaders[MESA_SHADER_FRAGMENT]->Source,
//                    "gl_FragColor = color * texture2D(sampler, outTexCoords).a;")) {
//            if (i == MESA_SHADER_VERTEX) {
//               for (unsigned i = 0; i < program->Attributes->NumParameters; i++) {
//                  const gl_program_parameter & attribute = program->Attributes->Parameters[i];
//                  LOGD("attribute '%s': location=%d slots=%d \n", attribute.Name, attribute.Location, attribute.Slots);
//               }
//               for (unsigned i = 0; i < program->Varying->NumParameters; i++) {
//                  const gl_program_parameter & varying = program->Varying->Parameters[i];
//                  LOGD("varying '%s': vs_location=%d fs_location=%d \n", varying.Name, varying.BindLocation, varying.Location);
//               }
//               LOGD("%s", program->Shaders[MESA_SHADER_VERTEX]->Source);
//               module->dump();
//            }
//         }

//#ifdef __arm__
//         fputs("end of bcc disassembly", stderr);
//         fclose(stderr);
//
//         file = fopen(fileName, "r");
//         assert(file);
//         fseek(file , 0 , SEEK_END);
//         long lSize = ftell(file);
//         rewind(file);
//         assert(0 <= lSize);
//         static char str[256];
//         while (!feof(file)) {
//            fgets(str, sizeof(str) - 1, file);
//            str[sizeof(str) - 1] = 0;
//            LOGD("%s", str);
//         }
//         fclose(file);
//#endif

#if USE_LLVM_SCANLINE
         if (GL_FRAGMENT_SHADER == shader->Type) {
            char scanlineName [SCANLINE_KEY_STRING_LEN] = {0};
            GetScanlineKeyString(&shaderKey, scanlineName, sizeof scanlineName / sizeof *scanlineName);
            GenerateScanLine(gglState, program, module, mainName, scanlineName);
            CodeGen(instance, scanlineName, shader, program, gglState);
         } else
#endif
            CodeGen(instance, mainName, shader, program, gglState);

         shader->executable->instances[shaderKey] = instance;
//         debug_printf("jit new shader '%s'(%p) \n", mainName, instance->function);
      } else
//         debug_printf("use cached shader %p \n", instance->function);
         ;

      shader->function  = instance->function;
   }
//   puts("pf2: GGLShaderUse end");

//   assert(0);
}

static void ShaderUse(GGLInterface * iface, gl_shader_program * program)
{
   GGL_GET_CONTEXT(ctx, iface);
   // so drawing calls will do nothing until ShaderUse with a program
   SetShaderVerifyFunctions(iface);
   if (!program) {
      ctx->CurrentProgram = NULL;
      return;
   }

   GGLShaderUse(ctx->llvmCtx, &ctx->state, program);
   for (unsigned i = 0; i < MESA_SHADER_TYPES; i++) {
      if (!program->_LinkedShaders[i])
         continue;
      if (!program->_LinkedShaders[i]->function)
         continue;
      if (GL_VERTEX_SHADER == program->_LinkedShaders[i]->Type)
         ctx->PickRaster(iface);
      else if (GL_FRAGMENT_SHADER == program->_LinkedShaders[i]->Type)
         ctx->PickScanLine(iface);
      else
         assert(0);
   }
   ctx->CurrentProgram = program;
}

unsigned GGLShaderDetach(gl_shader_program * program, gl_shader * shader)
{
   for (unsigned i = 0; i < program->NumShaders; i++)
      if (program->Shaders[i] == shader) {
         program->NumShaders--;
         // just swap end to deleted shader
         program->Shaders[i] = program->Shaders[program->NumShaders];
         shader->RefCount--;
         if (1 == shader->RefCount && shader->DeletePending)
            GGLShaderDelete(shader);
         return GL_NO_ERROR;
      }
   return (GL_INVALID_OPERATION);
}

static void ShaderDetach(const GGLInterface * iface, gl_shader_program * program,
                         gl_shader * shader)
{
   unsigned error = GGLShaderDetach(program, shader);
   if (GL_NO_ERROR != error)
      gglError(error);
}

void GGLShaderProgramDelete(gl_shader_program * program)
{
   for (unsigned i = 0; i < program->NumShaders; i++) {
      GGLShaderDelete(program->Shaders[i]); // actually just mark for delete
      GGLShaderDetach(program, program->Shaders[i]); // detach will delete if ref == 1
      i--; // GGLShaderDetach just swaps end to detached shader
   }

   for (unsigned i = 0; i < MESA_SHADER_TYPES; i++)
      GGLShaderDelete(program->_LinkedShaders[i]);

   hieralloc_free(program);
}

static void ShaderProgramDelete(GGLInterface * iface, gl_shader_program * program)
{
   GGL_GET_CONTEXT(ctx, iface);
   if (ctx->CurrentProgram == program) {
      ctx->CurrentProgram = NULL;
      SetShaderVerifyFunctions(iface);
   }
   GGLShaderProgramDelete(program);
}

void GGLShaderGetiv(const gl_shader_t * shader, const GLenum pname, GLint * params)
{
   switch (pname) {
   case GL_SHADER_TYPE:
      *params = shader->Type;
      break;
   case GL_DELETE_STATUS:
      *params = shader->DeletePending;
      break;
   case GL_COMPILE_STATUS:
      *params = shader->CompileStatus;
      break;
   case GL_INFO_LOG_LENGTH:
      *params = shader->InfoLog ? strlen(shader->InfoLog) + 1 : 0;
      break;
   case GL_SHADER_SOURCE_LENGTH:
      *params = shader->Source ? strlen(shader->Source) + 1 : 0;
      break;
   default:
      assert(0);
      break;
   }
}

void GGLShaderGetInfoLog(const gl_shader_t * shader, GLsizei bufsize, GLsizei* length, GLchar* infolog)
{
   unsigned len = 0;
   infolog[0] = 0;
   if (shader->InfoLog)
   {
      len = strlen(shader->InfoLog);
      strncpy(infolog, shader->InfoLog, bufsize);
      infolog[bufsize] = 0;
   }
   if (length)
      *length = strlen(infolog);
}

void GGLShaderProgramGetiv(const gl_shader_program_t * program, const GLenum pname, GLint * params)
{
   switch (pname) {
   case GL_DELETE_STATUS:
      *params = program->DeletePending;
      break;
   case GL_LINK_STATUS:
      *params = program->LinkStatus;
      break;
   case GL_VALIDATE_STATUS:
      *params = program->LinkStatus;
      break;
   case GL_INFO_LOG_LENGTH:
      *params = program->InfoLog ? strlen(program->InfoLog) + 1 : 0;
      break;
   case GL_ATTACHED_SHADERS:
      *params = program->NumShaders;
      break;
   case GL_ACTIVE_ATTRIBUTES:
      *params = program->AttributeSlots;
      break;
   case GL_ACTIVE_UNIFORMS:
      *params = program->Uniforms->Slots;
      break;
   case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH:
   case GL_ACTIVE_UNIFORM_MAX_LENGTH:
      printf("pf2:GGLShaderProgramGetiv not implemented: %d \n", pname);
   default:
      assert(0);
      break;
   }
}

void GGLShaderProgramGetInfoLog(const gl_shader_program_t * program, GLsizei bufsize, GLsizei* length, GLchar* infolog)
{
   unsigned len = 0;
   infolog[0] = 0;
   if (program->InfoLog)
   {
      len = strlen(program->InfoLog);
      strncpy(infolog, program->InfoLog, bufsize);
      infolog[bufsize] = 0;
   }
   if (length)
      *length = strlen(infolog);
}

void GGLShaderAttributeBind(const gl_shader_program * program, GLuint index, const GLchar * name)
{
   int i = _mesa_add_parameter(program->Attributes, name);
   program->Attributes->Parameters[i].BindLocation = index;
}

GLint GGLShaderAttributeLocation(const gl_shader_program * program, const char * name)
{
   int i = _mesa_get_parameter(program->Attributes, name);
   if (i >= 0)
      return program->Attributes->Parameters[i].Location;
   return -1;
}

GLint GGLShaderVaryingLocation(const gl_shader_program_t * program,
                               const char * name, GLint * vertexOutputLocation)
{
   for (unsigned int i = 0; i < program->Varying->NumParameters; i++)
      if (!strcmp(program->Varying->Parameters[i].Name, name)) {
         if (vertexOutputLocation)
            *vertexOutputLocation = program->Varying->Parameters[i].BindLocation;
         return program->Varying->Parameters[i].Location;
      }
   return -1;
}

GLint GGLShaderUniformLocation(const gl_shader_program * program,
                               const char * name)
{
   for (unsigned i = 0; i < program->Uniforms->NumUniforms; i++)
      if (!strcmp(program->Uniforms->Uniforms[i].Name, name))
         return i;
   return -1;
}

void GGLShaderUniformGetfv(gl_shader_program * program, GLint location, GLfloat * params)
{
   assert(0 <= location && program->Uniforms->NumUniforms > location);
   int index = program->Uniforms->Uniforms[location].Pos;
   assert(0 <= index && program->Uniforms->Slots > index);
   memcpy(params, program->ValuesUniform + index, sizeof(*program->ValuesUniform));
}

void GGLShaderUniformGetiv(gl_shader_program * program, GLint location, GLint * params)
{
   assert(0 <= location && program->Uniforms->NumUniforms > location);
   int index = program->Uniforms->Uniforms[location].Pos;
   assert(0 <= index && program->Uniforms->Slots > index);
   const float * uniform = program->ValuesUniform[index];
   params[0] = uniform[0];
   params[1] = uniform[1];
   params[2] = uniform[2];
   params[3] = uniform[3];
}

void GGLShaderUniformGetSamplers(const gl_shader_program_t * program,
                                 int sampler2tmu[GGL_MAXCOMBINEDTEXTUREIMAGEUNITS])
{
//   LOGD("%s", program->Shaders[MESA_SHADER_FRAGMENT]->Source);
//   for (unsigned i = 0; i < program->Uniforms->Slots + program->Uniforms->SamplerSlots; i++)
//      LOGD("%d: %.2f \t %.2f \t %.2f \t %.2f", i, program->ValuesUniform[i][0], program->ValuesUniform[i][1],
//           program->ValuesUniform[i][2], program->ValuesUniform[i][3]);
   for (unsigned i = 0; i < GGL_MAXCOMBINEDTEXTUREIMAGEUNITS; i++)
      sampler2tmu[i] = -1;
   for (unsigned i = 0; i < program->Uniforms->NumUniforms; i++) {
      const gl_uniform & uniform = program->Uniforms->Uniforms[i];
      if (uniform.Type->is_sampler()) {
//         LOGD("%d uniform.Pos=%d tmu=%d", program->Uniforms->Slots, uniform.Pos, (int)program->ValuesUniform[program->Uniforms->Slots + uniform.Pos][0]);
         sampler2tmu[uniform.Pos] = program->ValuesUniform[program->Uniforms->Slots + uniform.Pos][0];
      } else if (uniform.Type->is_array() && uniform.Type->fields.array->is_sampler())
         assert(0);
   }
}

GLint GGLShaderUniform(gl_shader_program * program, GLint location, GLsizei count,
                       const GLvoid *values, GLenum type)
{
//   LOGD("pf2: GGLShaderUniform location=%d count=%d type=0x%.4X", location, count, type);
   // TODO: sampler uniform and type checking
   if (!program) {
      //gglError(GL_INVALID_OPERATION);
      return -2;
   }
   if (-1 == location)
      return -1;
   assert(0 <= location && program->Uniforms->NumUniforms > location);
   const gl_uniform & uniform = program->Uniforms->Uniforms[location];
   int start = -1;
   if (uniform.Type->is_sampler())
   {
      start = uniform.Pos + program->Uniforms->Slots;
      assert(GL_INT == type && 1 == count);
      program->ValuesUniform[start][0] = *(float *)values;
      return uniform.Pos;
   }
   else if (uniform.Type->is_array() && uniform.Type->fields.array->is_sampler()) {
      assert(0); // not implemented
   } else
      start = uniform.Pos;
   int slots = 0, elems = 0;
   switch (type) {
   case GL_INT:
   case GL_FLOAT:
   case  GL_BOOL:
      slots = count;
      elems = 1;
      break;
   case GL_FLOAT_VEC2: // fall through
   case  GL_INT_VEC2: // fall through
   case  GL_BOOL_VEC2:
      slots = count;
      elems = 2;
      break;
   case  GL_INT_VEC3: // fall through
   case  GL_BOOL_VEC3: // fall through
   case  GL_FLOAT_VEC3: // fall through
      slots = count;
      elems = 3;
      break;
   case  GL_INT_VEC4: // fall through
   case  GL_FLOAT_VEC4: // fall through
   case  GL_BOOL_VEC4: // fall through
      slots = count;
      elems = 4;
      break;
   default:
      assert(0);
   }
//   LOGD("pf2: GGLShaderUniform start=%d slots=%d elems=%d", start, slots, elems);
   if (0 > start)
      assert(0);
   if (start + slots > program->Uniforms->Slots)
      assert(0);
   for (int i = 0; i < slots; i++)
      memcpy(program->ValuesUniform + start + i, values, elems * sizeof(float));
//   LOGD("pf2: GGLShaderUniform copied");
   return -2;
}

void GGLShaderUniformMatrix(gl_shader_program * program, GLint cols, GLint rows,
                            GLint location, GLsizei count, GLboolean transpose, const GLfloat *values)
{
   if (location == -1)
      return;
   assert(!transpose);
   assert(cols == rows);
   assert(0 <= location && program->Uniforms->NumUniforms > location);
   int start = program->Uniforms->Uniforms[location].Pos;
   unsigned slots = cols * count;
   if (start < 0 || start + slots > program->Uniforms->Slots)
      return gglError(GL_INVALID_OPERATION);
   for (unsigned i = 0; i < slots; i++) {
      float * column = program->ValuesUniform[start + i];
      for (unsigned j = 0; j < rows; j++)
         column[j] = values[i * 4 + j];
   }

//   if (!strstr(program->Shaders[MESA_SHADER_FRAGMENT]->Source,
//               "gl_FragColor = color * texture2D(sampler, outTexCoords).a;"))
//      return;
//
//   LOGD("pf2: GGLShaderUniformMatrix location=%d cols=%d count=%d", location, cols, count);
//
//   for (unsigned i = 0; i < 4; i++)
//      LOGD("pf2: GGLShaderUniformMatrix %.2f \t %.2f \t %.2f \t %.2f \n", values[i * 4 + 0],
//           values[i * 4 + 1], values[i * 4 + 2], values[i * 4 + 3]);

}

static void ShaderVerifyProcessVertex(const GGLInterface * iface, const VertexInput * input,
                                      VertexOutput * output)
{
   GGL_GET_CONST_CONTEXT(ctx, iface);
   if (ctx->CurrentProgram) {
      ShaderUse(const_cast<GGLInterface *>(iface), ctx->CurrentProgram);
      if (ShaderVerifyProcessVertex != iface->ProcessVertex)
         iface->ProcessVertex(iface, input, output);
   }
}

static void ShaderVerifyDrawTriangle(const GGLInterface * iface, const VertexInput * v0,
                                     const VertexInput * v1, const VertexInput * v2)
{
   GGL_GET_CONST_CONTEXT(ctx, iface);
   if (ctx->CurrentProgram) {
      ShaderUse(const_cast<GGLInterface *>(iface), ctx->CurrentProgram);
      if (ShaderVerifyDrawTriangle != iface->DrawTriangle)
         iface->DrawTriangle(iface, v0, v1, v2);
   }
}

static void ShaderVerifyRasterTriangle(const GGLInterface * iface, const VertexOutput * v1,
                                       const VertexOutput * v2, const VertexOutput * v3)
{
   GGL_GET_CONST_CONTEXT(ctx, iface);
   if (ctx->CurrentProgram) {
      ShaderUse(const_cast<GGLInterface *>(iface), ctx->CurrentProgram);
      if (ShaderVerifyRasterTriangle != iface->RasterTriangle)
         iface->RasterTriangle(iface, v1, v2, v3);
   }
}

static void ShaderVerifyRasterTrapezoid(const GGLInterface * iface, const VertexOutput * tl,
                                        const VertexOutput * tr, const VertexOutput * bl,
                                        const VertexOutput * br)
{
   GGL_GET_CONST_CONTEXT(ctx, iface);
   if (ctx->CurrentProgram) {
      ShaderUse(const_cast<GGLInterface *>(iface), ctx->CurrentProgram);
      if (ShaderVerifyRasterTrapezoid != iface->RasterTrapezoid)
         iface->RasterTrapezoid(iface, tl, tr, bl, br);
   }
}

static void ShaderVerifyScanLine(const GGLInterface * iface, const VertexOutput * v1,
                                 const VertexOutput * v2)
{
   GGL_GET_CONST_CONTEXT(ctx, iface);
   if (ctx->CurrentProgram) {
      ShaderUse(const_cast<GGLInterface *>(iface), ctx->CurrentProgram);
      if (ShaderVerifyScanLine != iface->ScanLine)
         iface->ScanLine(iface, v1, v2);
   }
}

// called after state changes so that drawing calls will trigger JIT
void SetShaderVerifyFunctions(struct GGLInterface * iface)
{
   iface->ProcessVertex = ShaderVerifyProcessVertex;
   iface->DrawTriangle = ShaderVerifyDrawTriangle;
   iface->RasterTriangle = ShaderVerifyRasterTriangle;
   iface->RasterTrapezoid = ShaderVerifyRasterTrapezoid;
   iface->ScanLine = ShaderVerifyScanLine;
}

void InitializeShaderFunctions(struct GGLInterface * iface)
{
   GGL_GET_CONTEXT(ctx, iface);
   ctx->llvmCtx = new llvm::LLVMContext();

   iface->ShaderCreate = ShaderCreate;
   iface->ShaderSource = GGLShaderSource;
   iface->ShaderCompile = ShaderCompile;
   iface->ShaderDelete = ShaderDelete;
   iface->ShaderProgramCreate = ShaderProgramCreate;
   iface->ShaderAttach = ShaderAttach;
   iface->ShaderDetach = ShaderDetach;
   iface->ShaderProgramLink = ShaderProgramLink;
   iface->ShaderUse = ShaderUse;
   iface->ShaderProgramDelete = ShaderProgramDelete;
   iface->ShaderGetiv = GGLShaderGetiv;
   iface->ShaderGetInfoLog = GGLShaderGetInfoLog;
   iface->ShaderProgramGetiv = GGLShaderProgramGetiv;
   iface->ShaderProgramGetInfoLog = GGLShaderProgramGetInfoLog;
   iface->ShaderAttributeBind = GGLShaderAttributeBind;
   iface->ShaderAttributeLocation = GGLShaderAttributeLocation;
   iface->ShaderVaryingLocation = GGLShaderVaryingLocation;
   iface->ShaderUniformLocation = GGLShaderUniformLocation;
   iface->ShaderUniformGetfv = GGLShaderUniformGetfv;
   iface->ShaderUniformGetiv = GGLShaderUniformGetiv;
   iface->ShaderUniformGetSamplers = GGLShaderUniformGetSamplers;
   iface->ShaderUniform = GGLShaderUniform;
   iface->ShaderUniformMatrix = GGLShaderUniformMatrix;
}

void DestroyShaderFunctions(GGLInterface * iface)
{
   GGL_GET_CONTEXT(ctx, iface);
   _mesa_glsl_release_types();
   _mesa_glsl_release_functions();
   delete ctx->llvmCtx;
   ctx->llvmCtx = NULL;
}