/* * Mesa 3-D graphics library * Version: 6.5.3 * * Copyright (C) 1999-2007 Brian Paul, Tungsten Graphics, Inc. * 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. */ /** * \mainpage * * Stand-alone Shading Language compiler. * Basically, a command-line program which accepts GLSL shaders and emits * vertex/fragment programs (GPU instructions). * * This file is basically just a Mesa device driver but instead of building * a shared library we build an executable. * * We can emit programs in three different formats: * 1. ARB-style (GL_ARB_vertex/fragment_program) * 2. NV-style (GL_NV_vertex/fragment_program) * 3. debug-style (a slightly more sophisticated, internal format) * * Note that the ARB and NV program languages can't express all the * features that might be used by a fragment program (examples being * uniform and varying vars). So, the ARB/NV programs that are * emitted aren't always legal programs in those languages. */ #include "main/imports.h" #include "main/context.h" #include "main/extensions.h" #include "main/framebuffer.h" #include "main/shaderapi.h" #include "main/shaderobj.h" #include "program/prog_print.h" #include "drivers/common/driverfuncs.h" #include "tnl/tnl.h" #include "tnl/t_context.h" #include "tnl/t_pipeline.h" #include "swrast/swrast.h" #include "swrast_setup/swrast_setup.h" #include "vbo/vbo.h" static const char *Prog = "glslcompiler"; struct options { GLboolean LineNumbers; GLboolean Link; gl_prog_print_mode Mode; const char *VertFile; const char *FragFile; const char *GeoFile; const char *OutputFile; GLboolean Params; struct gl_sl_pragmas Pragmas; }; static struct options Options; /** * GLSL compiler driver context. (kind of an artificial thing for now) */ struct compiler_context { GLcontext MesaContext; int foo; }; typedef struct compiler_context CompilerContext; static void UpdateState(GLcontext *ctx, GLuint new_state) { /* easy - just propogate */ _swrast_InvalidateState( ctx, new_state ); _swsetup_InvalidateState( ctx, new_state ); _tnl_InvalidateState( ctx, new_state ); _vbo_InvalidateState( ctx, new_state ); } static GLboolean CreateContext(void) { struct dd_function_table ddFuncs; GLvisual *vis; GLframebuffer *buf; GLcontext *ctx; CompilerContext *cc; vis = _mesa_create_visual(GL_FALSE, GL_FALSE, /* RGB */ 8, 8, 8, 8, /* color */ 0, 0, /* z, stencil */ 0, 0, 0, 0, 1); /* accum */ buf = _mesa_create_framebuffer(vis); cc = calloc(1, sizeof(*cc)); if (!vis || !buf || !cc) { if (vis) _mesa_destroy_visual(vis); if (buf) _mesa_destroy_framebuffer(buf); return GL_FALSE; } _mesa_init_driver_functions(&ddFuncs); ddFuncs.GetString = NULL;/*get_string;*/ ddFuncs.UpdateState = UpdateState; ddFuncs.GetBufferSize = NULL; ctx = &cc->MesaContext; _mesa_initialize_context(ctx, vis, NULL, &ddFuncs, cc); _mesa_enable_sw_extensions(ctx); if (!_swrast_CreateContext( ctx ) || !_vbo_CreateContext( ctx ) || !_tnl_CreateContext( ctx ) || !_swsetup_CreateContext( ctx )) { _mesa_destroy_visual(vis); _mesa_free_context_data(ctx); free(cc); return GL_FALSE; } TNL_CONTEXT(ctx)->Driver.RunPipeline = _tnl_run_pipeline; _swsetup_Wakeup( ctx ); /* Override the context's default pragma settings */ ctx->Shader.DefaultPragmas = Options.Pragmas; _mesa_make_current(ctx, buf, buf); return GL_TRUE; } static void LoadAndCompileShader(GLuint shader, const char *text) { GLint stat; _mesa_ShaderSourceARB(shader, 1, (const GLchar **) &text, NULL); _mesa_CompileShaderARB(shader); _mesa_GetShaderiv(shader, GL_COMPILE_STATUS, &stat); if (!stat) { GLchar log[1000]; GLsizei len; _mesa_GetShaderInfoLog(shader, 1000, &len, log); fprintf(stderr, "%s: problem compiling shader: %s\n", Prog, log); exit(1); } else { printf("Shader compiled OK\n"); } } /** * Read a shader from a file. */ static void ReadShader(GLuint shader, const char *filename) { const int max = 100*1000; int n; char *buffer = (char*) malloc(max); FILE *f = fopen(filename, "r"); if (!f) { fprintf(stderr, "%s: Unable to open shader file %s\n", Prog, filename); exit(1); } n = fread(buffer, 1, max, f); /* printf("%s: read %d bytes from shader file %s\n", Prog, n, filename); */ if (n > 0) { buffer[n] = 0; LoadAndCompileShader(shader, buffer); } fclose(f); free(buffer); } static void CheckLink(GLuint v_shader, GLuint f_shader) { GLuint prog; GLint stat; prog = _mesa_CreateProgram(); _mesa_AttachShader(prog, v_shader); _mesa_AttachShader(prog, f_shader); _mesa_LinkProgramARB(prog); _mesa_GetProgramiv(prog, GL_LINK_STATUS, &stat); if (!stat) { GLchar log[1000]; GLsizei len; _mesa_GetProgramInfoLog(prog, 1000, &len, log); fprintf(stderr, "Linker error:\n%s\n", log); } else { fprintf(stderr, "Link success!\n"); } } static void PrintShaderInstructions(GLuint shader, FILE *f) { GET_CURRENT_CONTEXT(ctx); struct gl_shader *sh = _mesa_lookup_shader(ctx, shader); struct gl_program *prog = sh->Program; _mesa_fprint_program_opt(stdout, prog, Options.Mode, Options.LineNumbers); if (Options.Params) _mesa_print_program_parameters(ctx, prog); } static GLuint CompileShader(const char *filename, GLenum type) { GLuint shader; assert(type == GL_FRAGMENT_SHADER || type == GL_VERTEX_SHADER || type == GL_GEOMETRY_SHADER_ARB); shader = _mesa_CreateShader(type); ReadShader(shader, filename); return shader; } static void Usage(void) { printf("Mesa GLSL stand-alone compiler\n"); printf("Usage:\n"); printf(" --vs FILE vertex shader input filename\n"); printf(" --fs FILE fragment shader input filename\n"); printf(" --gs FILE geometry shader input filename\n"); printf(" --arb emit ARB-style instructions\n"); printf(" --nv emit NV-style instructions\n"); printf(" --link run linker\n"); printf(" --debug force #pragma debug(on)\n"); printf(" --nodebug force #pragma debug(off)\n"); printf(" --opt force #pragma optimize(on)\n"); printf(" --noopt force #pragma optimize(off)\n"); printf(" --number, -n emit line numbers (if --arb or --nv)\n"); printf(" --output, -o FILE output filename\n"); printf(" --params also emit program parameter info\n"); printf(" --help display this information\n"); } static void ParseOptions(int argc, char *argv[]) { int i; Options.LineNumbers = GL_FALSE; Options.Mode = PROG_PRINT_DEBUG; Options.VertFile = NULL; Options.FragFile = NULL; Options.GeoFile = NULL; Options.OutputFile = NULL; Options.Params = GL_FALSE; Options.Pragmas.IgnoreOptimize = GL_FALSE; Options.Pragmas.IgnoreDebug = GL_FALSE; Options.Pragmas.Debug = GL_FALSE; Options.Pragmas.Optimize = GL_TRUE; if (argc == 1) { Usage(); exit(0); } for (i = 1; i < argc; i++) { if (strcmp(argv[i], "--vs") == 0) { Options.VertFile = argv[i + 1]; i++; } else if (strcmp(argv[i], "--fs") == 0) { Options.FragFile = argv[i + 1]; i++; } else if (strcmp(argv[i], "--gs") == 0) { Options.GeoFile = argv[i + 1]; i++; } else if (strcmp(argv[i], "--arb") == 0) { Options.Mode = PROG_PRINT_ARB; } else if (strcmp(argv[i], "--nv") == 0) { Options.Mode = PROG_PRINT_NV; } else if (strcmp(argv[i], "--link") == 0) { Options.Link = GL_TRUE; } else if (strcmp(argv[i], "--debug") == 0) { Options.Pragmas.IgnoreDebug = GL_TRUE; Options.Pragmas.Debug = GL_TRUE; } else if (strcmp(argv[i], "--nodebug") == 0) { Options.Pragmas.IgnoreDebug = GL_TRUE; Options.Pragmas.Debug = GL_FALSE; } else if (strcmp(argv[i], "--opt") == 0) { Options.Pragmas.IgnoreOptimize = GL_TRUE; Options.Pragmas.Optimize = GL_TRUE; } else if (strcmp(argv[i], "--noopt") == 0) { Options.Pragmas.IgnoreOptimize = GL_TRUE; Options.Pragmas.Optimize = GL_FALSE; } else if (strcmp(argv[i], "--number") == 0 || strcmp(argv[i], "-n") == 0) { Options.LineNumbers = GL_TRUE; } else if (strcmp(argv[i], "--output") == 0 || strcmp(argv[i], "-o") == 0) { Options.OutputFile = argv[i + 1]; i++; } else if (strcmp(argv[i], "--params") == 0) { Options.Params = GL_TRUE; } else if (strcmp(argv[i], "--help") == 0) { Usage(); exit(0); } else { printf("Unknown option: %s\n", argv[i]); Usage(); exit(1); } } if (Options.Mode == PROG_PRINT_DEBUG) { /* always print line numbers when emitting debug-style output */ Options.LineNumbers = GL_TRUE; } } int main(int argc, char *argv[]) { GLuint v_shader = 0, f_shader = 0, g_shader = 0; ParseOptions(argc, argv); if (!CreateContext()) { fprintf(stderr, "%s: Failed to create compiler context\n", Prog); exit(1); } if (Options.VertFile) { v_shader = CompileShader(Options.VertFile, GL_VERTEX_SHADER); } if (Options.FragFile) { f_shader = CompileShader(Options.FragFile, GL_FRAGMENT_SHADER); } if (Options.GeoFile) { g_shader = CompileShader(Options.GeoFile, GL_GEOMETRY_SHADER_ARB); } if (v_shader || f_shader || g_shader) { if (Options.OutputFile) { fclose(stdout); /*stdout =*/ freopen(Options.OutputFile, "w", stdout); } if (stdout && v_shader) { PrintShaderInstructions(v_shader, stdout); } if (stdout && f_shader) { PrintShaderInstructions(f_shader, stdout); } if (stdout && g_shader) { PrintShaderInstructions(g_shader, stdout); } if (Options.OutputFile) { fclose(stdout); } } if (Options.Link) { if (!v_shader || !f_shader) { fprintf(stderr, "--link option requires both a vertex and fragment shader.\n"); exit(1); } CheckLink(v_shader, f_shader); } return 0; }