shaderobj.c revision ce220540744c0dde30e5670441bbc7eab915cf89
147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/* 247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Mesa 3-D graphics library 347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * 447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Copyright (C) 2004-2008 Brian Paul All Rights Reserved. 547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Copyright (C) 2009-2010 VMware, Inc. All Rights Reserved. 647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * 747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Permission is hereby granted, free of charge, to any person obtaining a 847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * copy of this software and associated documentation files (the "Software"), 947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * to deal in the Software without restriction, including without limitation 1047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * the rights to use, copy, modify, merge, publish, distribute, sublicense, 1147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * and/or sell copies of the Software, and to permit persons to whom the 1247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Software is furnished to do so, subject to the following conditions: 1347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * 1447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * The above copyright notice and this permission notice shall be included 1547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * in all copies or substantial portions of the Software. 1647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * 1747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 1847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 2047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 2147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 2247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 2347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt */ 2447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 2547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/** 2647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * \file shaderobj.c 2747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * \author Brian Paul 2847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * 2947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt */ 3047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 3147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 3247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include "main/glheader.h" 3347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include "main/context.h" 3447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include "main/hash.h" 3547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include "main/mfeatures.h" 3647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include "main/mtypes.h" 3747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include "main/shaderobj.h" 3847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include "main/uniforms.h" 3947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include "program/program.h" 4047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include "program/prog_parameter.h" 4147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include "program/hash_table.h" 4247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#include "ralloc.h" 4347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 4447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/**********************************************************************/ 4547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/*** Shader object functions ***/ 4647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/**********************************************************************/ 4747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 4847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 4947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/** 5047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Set ptr to point to sh. 5147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * If ptr is pointing to another shader, decrement its refcount (and delete 5247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * if refcount hits zero). 5347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Then set ptr to point to sh, incrementing its refcount. 5447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt */ 5547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltvoid 5647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt_mesa_reference_shader(struct gl_context *ctx, struct gl_shader **ptr, 5747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt struct gl_shader *sh) 5847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt{ 5947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt assert(ptr); 6047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (*ptr == sh) { 6147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt /* no-op */ 6247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt return; 6347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 6447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (*ptr) { 6547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt /* Unreference the old shader */ 6647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt GLboolean deleteFlag = GL_FALSE; 6747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt struct gl_shader *old = *ptr; 6847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 6947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt ASSERT(old->RefCount > 0); 7047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt old->RefCount--; 7147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt /*printf("SHADER DECR %p (%d) to %d\n", 7247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt (void*) old, old->Name, old->RefCount);*/ 7347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt deleteFlag = (old->RefCount == 0); 7447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 7547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (deleteFlag) { 7647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (old->Name != 0) 7747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_HashRemove(ctx->Shared->ShaderObjects, old->Name); 7847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt ctx->Driver.DeleteShader(ctx, old); 7947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 8047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 8147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *ptr = NULL; 8247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 8347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt assert(!*ptr); 8447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 8547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (sh) { 8647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt /* reference new */ 8747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt sh->RefCount++; 8847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt /*printf("SHADER INCR %p (%d) to %d\n", 8947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt (void*) sh, sh->Name, sh->RefCount);*/ 9047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *ptr = sh; 9147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 9247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt} 9347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 9447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltvoid 9547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt_mesa_init_shader(struct gl_context *ctx, struct gl_shader *shader) 9647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt{ 9747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shader->RefCount = 1; 9847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt} 9947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 10047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/** 10147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Allocate a new gl_shader object, initialize it. 10247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Called via ctx->Driver.NewShader() 10347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt */ 10447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstruct gl_shader * 10547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt_mesa_new_shader(struct gl_context *ctx, GLuint name, GLenum type) 10647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt{ 10747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt struct gl_shader *shader; 10847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt assert(type == GL_FRAGMENT_SHADER || type == GL_VERTEX_SHADER || 10947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt type == GL_GEOMETRY_SHADER_ARB); 11047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shader = rzalloc(NULL, struct gl_shader); 11147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (shader) { 11247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shader->Type = type; 11347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shader->Name = name; 11447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_init_shader(ctx, shader); 11547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 11647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt return shader; 11747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt} 11847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 11947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 12047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/** 12147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Delete a shader object. 12247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Called via ctx->Driver.DeleteShader(). 12347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt */ 12447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstatic void 12547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt_mesa_delete_shader(struct gl_context *ctx, struct gl_shader *sh) 12647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt{ 12747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (sh->Source) 12847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt free((void *) sh->Source); 12947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_reference_program(ctx, &sh->Program, NULL); 13047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt ralloc_free(sh); 13147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt} 13247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 13347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 13447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/** 13547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Lookup a GLSL shader object. 13647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt */ 13747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstruct gl_shader * 13847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt_mesa_lookup_shader(struct gl_context *ctx, GLuint name) 13947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt{ 14047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (name) { 14147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt struct gl_shader *sh = (struct gl_shader *) 14247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_HashLookup(ctx->Shared->ShaderObjects, name); 14347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt /* Note that both gl_shader and gl_shader_program objects are kept 14447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * in the same hash table. Check the object's type to be sure it's 14547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * what we're expecting. 14647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt */ 14747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (sh && sh->Type == GL_SHADER_PROGRAM_MESA) { 14847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt return NULL; 14947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 15047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt return sh; 15147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 15247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt return NULL; 15347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt} 15447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 15547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 15647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/** 15747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * As above, but record an error if shader is not found. 15847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt */ 15947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstruct gl_shader * 16047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt_mesa_lookup_shader_err(struct gl_context *ctx, GLuint name, const char *caller) 16147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt{ 16247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (!name) { 16347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_error(ctx, GL_INVALID_VALUE, "%s", caller); 16447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt return NULL; 16547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 16647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt else { 16747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt struct gl_shader *sh = (struct gl_shader *) 16847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_HashLookup(ctx->Shared->ShaderObjects, name); 16947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (!sh) { 17047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_error(ctx, GL_INVALID_VALUE, "%s", caller); 17147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt return NULL; 17247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 17347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (sh->Type == GL_SHADER_PROGRAM_MESA) { 17447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_error(ctx, GL_INVALID_OPERATION, "%s", caller); 17547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt return NULL; 17647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 17747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt return sh; 17847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 17947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt} 18047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 18147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 18247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 18347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/**********************************************************************/ 18447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/*** Shader Program object functions ***/ 18547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/**********************************************************************/ 18647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 18747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 18847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/** 18947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Set ptr to point to shProg. 19047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * If ptr is pointing to another object, decrement its refcount (and delete 19147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * if refcount hits zero). 19247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Then set ptr to point to shProg, incrementing its refcount. 19347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt */ 19447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltvoid 19547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt_mesa_reference_shader_program(struct gl_context *ctx, 19647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt struct gl_shader_program **ptr, 19747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt struct gl_shader_program *shProg) 19847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt{ 19947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt assert(ptr); 20047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (*ptr == shProg) { 20147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt /* no-op */ 20247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt return; 20347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 20447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (*ptr) { 20547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt /* Unreference the old shader program */ 20647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt GLboolean deleteFlag = GL_FALSE; 20747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt struct gl_shader_program *old = *ptr; 20847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 20947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt ASSERT(old->RefCount > 0); 21047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt old->RefCount--; 21147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#if 0 21247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt printf("ShaderProgram %p ID=%u RefCount-- to %d\n", 21347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt (void *) old, old->Name, old->RefCount); 21447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#endif 21547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt deleteFlag = (old->RefCount == 0); 21647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 21747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (deleteFlag) { 21847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (old->Name != 0) 21947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_HashRemove(ctx->Shared->ShaderObjects, old->Name); 22047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt ctx->Driver.DeleteShaderProgram(ctx, old); 22147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 22247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 22347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *ptr = NULL; 22447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 22547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt assert(!*ptr); 22647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 22747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (shProg) { 22847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shProg->RefCount++; 22947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#if 0 23047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt printf("ShaderProgram %p ID=%u RefCount++ to %d\n", 23147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt (void *) shProg, shProg->Name, shProg->RefCount); 23247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#endif 23347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt *ptr = shProg; 23447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 23547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt} 23647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 23747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltvoid 23847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt_mesa_init_shader_program(struct gl_context *ctx, struct gl_shader_program *prog) 23947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt{ 24047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt prog->Type = GL_SHADER_PROGRAM_MESA; 24147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt prog->RefCount = 1; 24247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 24347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt prog->AttributeBindings = string_to_uint_map_ctor(); 24447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt prog->FragDataBindings = string_to_uint_map_ctor(); 24547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt prog->FragDataIndexBindings = string_to_uint_map_ctor(); 24647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 24747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#if FEATURE_ARB_geometry_shader4 24847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt prog->Geom.VerticesOut = 0; 24947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt prog->Geom.InputType = GL_TRIANGLES; 25047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt prog->Geom.OutputType = GL_TRIANGLE_STRIP; 25147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt#endif 25247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 25347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt prog->TransformFeedback.BufferMode = GL_INTERLEAVED_ATTRIBS; 25447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 25547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt prog->InfoLog = ralloc_strdup(prog, ""); 25647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt} 25747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 25847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/** 25947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Allocate a new gl_shader_program object, initialize it. 26047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Called via ctx->Driver.NewShaderProgram() 26147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt */ 26247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstatic struct gl_shader_program * 26347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt_mesa_new_shader_program(struct gl_context *ctx, GLuint name) 26447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt{ 26547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt struct gl_shader_program *shProg; 26647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shProg = rzalloc(NULL, struct gl_shader_program); 26747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (shProg) { 26847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shProg->Name = name; 26947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_init_shader_program(ctx, shProg); 27047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 27147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt return shProg; 27247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt} 27347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 27447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 27547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/** 27647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Clear (free) the shader program state that gets produced by linking. 27747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt */ 27847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltvoid 27947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt_mesa_clear_shader_program_data(struct gl_context *ctx, 28047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt struct gl_shader_program *shProg) 28147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt{ 28247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (shProg->UniformStorage) { 28347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt unsigned i; 28447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt for (i = 0; i < shProg->NumUserUniformStorage; ++i) 28547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_uniform_detach_all_driver_storage(&shProg->UniformStorage[i]); 28647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt ralloc_free(shProg->UniformStorage); 28747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shProg->NumUserUniformStorage = 0; 28847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shProg->UniformStorage = NULL; 28947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 29047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 29147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (shProg->UniformHash) { 29247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt string_to_uint_map_dtor(shProg->UniformHash); 29347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shProg->UniformHash = NULL; 29447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 29547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 29647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt assert(shProg->InfoLog != NULL); 29747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt ralloc_free(shProg->InfoLog); 29847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shProg->InfoLog = ralloc_strdup(shProg, ""); 29947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt} 30047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 30147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 30247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/** 30347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Free all the data that hangs off a shader program object, but not the 30447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * object itself. 30547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt */ 30647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltvoid 30747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt_mesa_free_shader_program_data(struct gl_context *ctx, 30847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt struct gl_shader_program *shProg) 30947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt{ 31047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt GLuint i; 31147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt gl_shader_type sh; 31247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 31347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt assert(shProg->Type == GL_SHADER_PROGRAM_MESA); 31447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 31547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_clear_shader_program_data(ctx, shProg); 31647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 31747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (shProg->AttributeBindings) { 31847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt string_to_uint_map_dtor(shProg->AttributeBindings); 31947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shProg->AttributeBindings = NULL; 32047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 32147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 32247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (shProg->FragDataBindings) { 32347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt string_to_uint_map_dtor(shProg->FragDataBindings); 32447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shProg->FragDataBindings = NULL; 32547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 32647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 32747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (shProg->FragDataIndexBindings) { 32847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt string_to_uint_map_dtor(shProg->FragDataIndexBindings); 32947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shProg->FragDataIndexBindings = NULL; 33047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 33147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 33247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt /* detach shaders */ 33347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt for (i = 0; i < shProg->NumShaders; i++) { 33447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_reference_shader(ctx, &shProg->Shaders[i], NULL); 33547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 33647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shProg->NumShaders = 0; 33747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 33847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (shProg->Shaders) { 33947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt free(shProg->Shaders); 34047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shProg->Shaders = NULL; 34147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 34247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 34347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt /* Transform feedback varying vars */ 34447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt for (i = 0; i < shProg->TransformFeedback.NumVarying; i++) { 34547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt free(shProg->TransformFeedback.VaryingNames[i]); 34647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 34747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt free(shProg->TransformFeedback.VaryingNames); 34847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shProg->TransformFeedback.VaryingNames = NULL; 34947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shProg->TransformFeedback.NumVarying = 0; 35047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 35147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 35247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt for (sh = 0; sh < MESA_SHADER_TYPES; sh++) { 35347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (shProg->_LinkedShaders[sh] != NULL) { 35447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt ctx->Driver.DeleteShader(ctx, shProg->_LinkedShaders[sh]); 35547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shProg->_LinkedShaders[sh] = NULL; 35647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 35747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 35847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt} 35947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 36047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 36147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/** 36247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Free/delete a shader program object. 36347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Called via ctx->Driver.DeleteShaderProgram(). 36447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt */ 36547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstatic void 36647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt_mesa_delete_shader_program(struct gl_context *ctx, struct gl_shader_program *shProg) 36747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt{ 36847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_free_shader_program_data(ctx, shProg); 36947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 37047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt ralloc_free(shProg); 37147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt} 37247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 37347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 37447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/** 37547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * Lookup a GLSL program object. 37647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt */ 37747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstruct gl_shader_program * 37847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt_mesa_lookup_shader_program(struct gl_context *ctx, GLuint name) 37947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt{ 38047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt struct gl_shader_program *shProg; 38147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (name) { 38247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt shProg = (struct gl_shader_program *) 38347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_HashLookup(ctx->Shared->ShaderObjects, name); 38447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt /* Note that both gl_shader and gl_shader_program objects are kept 38547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * in the same hash table. Check the object's type to be sure it's 38647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * what we're expecting. 38747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt */ 38847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (shProg && shProg->Type != GL_SHADER_PROGRAM_MESA) { 38947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt return NULL; 39047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 39147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt return shProg; 39247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 39347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt return NULL; 39447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt} 39547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 39647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 39747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt/** 39847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt * As above, but record an error if program is not found. 39947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt */ 40047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltstruct gl_shader_program * 40147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt_mesa_lookup_shader_program_err(struct gl_context *ctx, GLuint name, 40247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt const char *caller) 40347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt{ 40447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (!name) { 40547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_error(ctx, GL_INVALID_VALUE, "%s", caller); 40647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt return NULL; 40747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 40847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt else { 40947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt struct gl_shader_program *shProg = (struct gl_shader_program *) 41047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_HashLookup(ctx->Shared->ShaderObjects, name); 41147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (!shProg) { 41247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_error(ctx, GL_INVALID_VALUE, "%s", caller); 41347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt return NULL; 41447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 41547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt if (shProg->Type != GL_SHADER_PROGRAM_MESA) { 41647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt _mesa_error(ctx, GL_INVALID_OPERATION, "%s", caller); 41747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt return NULL; 41847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 41947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt return shProg; 42047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt } 42147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt} 42247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 42347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt 42447e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwaltvoid 42547e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt_mesa_init_shader_object_functions(struct dd_function_table *driver) 42647e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt{ 42747e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt driver->NewShader = _mesa_new_shader; 42847e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt driver->DeleteShader = _mesa_delete_shader; 42947e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt driver->NewShaderProgram = _mesa_new_shader_program; 43047e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt driver->DeleteShaderProgram = _mesa_delete_shader_program; 43147e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt driver->LinkShader = _mesa_ir_link_shader; 43247e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt} 43347e4cebad7397422144bb03a21f3f7682c062c4aRobert Greenwalt