blob: 9cfee8287b9db7b84a0be93fa46cd7199dd2ee8a [file] [log] [blame]
/*
* 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/shaders.h"
#include "program/shader_api.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/s_context.h"
#include "swrast/s_triangle.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;
}