2009-04-18 19:00:33 +02:00
|
|
|
/* Copyright (C) 2009 Wildfire Games.
|
|
|
|
* This file is part of 0 A.D.
|
|
|
|
*
|
|
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2009-04-18 19:51:05 +02:00
|
|
|
/*
|
|
|
|
* load and link together shaders; provides hotload support.
|
2006-04-12 01:59:08 +02:00
|
|
|
*/
|
|
|
|
|
2005-10-05 18:42:09 +02:00
|
|
|
#include "precompiled.h"
|
2007-01-01 22:25:47 +01:00
|
|
|
#include "ogl_shader.h"
|
2005-10-05 18:42:09 +02:00
|
|
|
|
|
|
|
#include "lib/ogl.h"
|
|
|
|
|
2007-12-20 21:14:21 +01:00
|
|
|
#include "ps/CStr.h"
|
2005-10-05 18:42:09 +02:00
|
|
|
#include "ps/CLogger.h"
|
|
|
|
#include "ps/XML/Xeromyces.h"
|
|
|
|
|
2007-12-20 21:14:21 +01:00
|
|
|
#include "lib/res/h_mgr.h"
|
|
|
|
#include "lib/file/vfs/vfs.h"
|
|
|
|
extern PIVFS g_VFS;
|
|
|
|
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2009-11-03 22:46:35 +01:00
|
|
|
#define LOG_CATEGORY L"shaders"
|
2005-10-05 18:42:09 +02:00
|
|
|
|
|
|
|
|
2009-11-03 22:46:35 +01:00
|
|
|
ERROR_ASSOCIATE(ERR::SHDR_CREATE, L"Shader creation failed", -1);
|
|
|
|
ERROR_ASSOCIATE(ERR::SHDR_COMPILE, L"Shader compile failed", -1);
|
|
|
|
ERROR_ASSOCIATE(ERR::SHDR_NO_SHADER, L"Invalid shader reference", -1);
|
|
|
|
ERROR_ASSOCIATE(ERR::SHDR_LINK, L"Shader linking failed", -1);
|
|
|
|
ERROR_ASSOCIATE(ERR::SHDR_NO_PROGRAM, L"Invalid shader program reference", -1);
|
2006-09-22 15:19:40 +02:00
|
|
|
|
|
|
|
|
2005-10-05 18:42:09 +02:00
|
|
|
// Convert a shader object type into a descriptive string.
|
|
|
|
// If the type enum is not known, the given buffer is used as scratch space
|
|
|
|
// to format the type number. If buf is null, a generic string is returned.
|
2009-11-06 11:59:10 +01:00
|
|
|
static const wchar_t* shader_type_to_string(GLenum type, wchar_t* buf, size_t buflen)
|
2005-10-05 18:42:09 +02:00
|
|
|
{
|
|
|
|
switch(type)
|
|
|
|
{
|
2009-11-06 11:59:10 +01:00
|
|
|
case GL_VERTEX_SHADER_ARB: return L"VERTEX_SHADER";
|
|
|
|
case GL_FRAGMENT_SHADER_ARB: return L"FRAGMENT_SHADER";
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!buf)
|
2009-11-06 11:59:10 +01:00
|
|
|
return L"unknown type enum";
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2009-11-06 11:59:10 +01:00
|
|
|
swprintf_s(buf, buflen, L"%u", type);
|
2005-10-05 18:42:09 +02:00
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the OpenGL shader type enum for the given string,
|
|
|
|
// or 0 if the shader type is not known.
|
|
|
|
static GLenum string_to_shader_type(const char* name)
|
|
|
|
{
|
2007-04-30 21:58:04 +02:00
|
|
|
if (!strcasecmp(name, "VERTEX_SHADER"))
|
2005-10-05 18:42:09 +02:00
|
|
|
return GL_VERTEX_SHADER_ARB;
|
2007-04-30 21:58:04 +02:00
|
|
|
if (!strcasecmp(name, "FRAGMENT_SHADER"))
|
2005-10-05 18:42:09 +02:00
|
|
|
return GL_FRAGMENT_SHADER_ARB;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// Handle type implementation
|
|
|
|
|
|
|
|
// Data for an Ogl_Shader object
|
|
|
|
struct Ogl_Shader {
|
|
|
|
// Type of shader (e.g. GL_VERTEX_SHADER_ARB)
|
|
|
|
GLenum type;
|
|
|
|
|
|
|
|
// ID of the OpenGL shader object
|
|
|
|
GLhandleARB id;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
H_TYPE_DEFINE(Ogl_Shader);
|
|
|
|
|
|
|
|
|
|
|
|
// One-time initialization, called once by h_alloc, which is
|
|
|
|
// in turn called by ogl_shader_load
|
|
|
|
static void Ogl_Shader_init(Ogl_Shader* shdr, va_list args)
|
|
|
|
{
|
|
|
|
shdr->type = va_arg(args, GLenum);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Reload the shader object from the source file.
|
|
|
|
//
|
|
|
|
// TODO: The OpenGL specification says that all changes to shader objects
|
|
|
|
// have absolutely no effect on a program object that contains these shaders
|
|
|
|
// when the program object is already linked.
|
|
|
|
// So, how can we inform the "parent object" (i.e. the program object) of our change?
|
2007-12-22 19:15:52 +01:00
|
|
|
static LibError Ogl_Shader_reload(Ogl_Shader* shdr, const VfsPath& pathname, Handle UNUSED(h))
|
2005-10-05 18:42:09 +02:00
|
|
|
{
|
2006-09-22 15:19:40 +02:00
|
|
|
LibError err = ERR::FAIL;
|
2005-10-12 06:35:01 +02:00
|
|
|
|
2005-10-05 18:42:09 +02:00
|
|
|
if (shdr->id)
|
2006-09-22 15:19:40 +02:00
|
|
|
return INFO::OK;
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2007-12-20 21:14:21 +01:00
|
|
|
shared_ptr<u8> file; size_t file_size;
|
2007-12-22 19:15:52 +01:00
|
|
|
RETURN_ERR(g_VFS->LoadFile(pathname, file, file_size));
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2005-10-30 02:18:15 +01:00
|
|
|
shdr->id = pglCreateShaderObjectARB(shdr->type);
|
2005-10-05 18:42:09 +02:00
|
|
|
if (!shdr->id)
|
|
|
|
{
|
|
|
|
// May be out of memory, but bad shdr->type is also possible.
|
|
|
|
// In any case, checking OpenGL error state will help spot
|
|
|
|
// bad code.
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2007-12-20 21:14:21 +01:00
|
|
|
WARN_RETURN(ERR::SHDR_CREATE);
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
|
2007-05-29 21:01:21 +02:00
|
|
|
{
|
2007-12-20 21:14:21 +01:00
|
|
|
const GLchar* strings[] = { (const GLchar*)file.get() };
|
2008-07-05 20:46:12 +02:00
|
|
|
const GLint tmp = (GLint)file_size;
|
2008-06-29 11:59:26 +02:00
|
|
|
pglShaderSourceARB(shdr->id, 1, strings, &tmp);
|
2009-03-24 22:00:41 +01:00
|
|
|
// Some drivers (Mesa i915 on 945GM) give GL_INVALID_ENUM after calling
|
|
|
|
// CompileShader on a GL_FRAGMENT_PROGRAM, because the hardware doesn't support
|
|
|
|
// fragment programs. I can't find a better way to detect that situation in advance,
|
|
|
|
// so detect the error afterwards and return failure.
|
|
|
|
ogl_WarnIfError();
|
2007-05-29 21:01:21 +02:00
|
|
|
pglCompileShaderARB(shdr->id);
|
2009-03-24 22:00:41 +01:00
|
|
|
if(ogl_SquelchError(GL_INVALID_ENUM))
|
|
|
|
goto fail_shadercreated;
|
2007-05-29 21:01:21 +02:00
|
|
|
}
|
2009-11-03 22:46:35 +01:00
|
|
|
|
|
|
|
{
|
2009-11-06 11:59:10 +01:00
|
|
|
wchar_t typenamebuf[32];
|
2009-11-03 22:46:35 +01:00
|
|
|
CStrW type(shader_type_to_string(shdr->type, typenamebuf, ARRAY_SIZE(typenamebuf)));
|
|
|
|
|
2007-12-20 21:14:21 +01:00
|
|
|
GLint log_length;
|
|
|
|
GLint compile_success;
|
2009-07-14 15:34:31 +02:00
|
|
|
pglGetShaderiv(shdr->id, GL_OBJECT_COMPILE_STATUS_ARB, &compile_success);
|
|
|
|
pglGetShaderiv(shdr->id, GL_OBJECT_INFO_LOG_LENGTH_ARB, &log_length);
|
2005-10-05 18:42:09 +02:00
|
|
|
if (log_length > 1)
|
|
|
|
{
|
|
|
|
char* infolog = new char[log_length];
|
2009-07-14 15:34:31 +02:00
|
|
|
pglGetShaderInfoLog(shdr->id, log_length, 0, infolog);
|
2009-11-03 22:46:35 +01:00
|
|
|
CStrW infologw(infolog);
|
|
|
|
debug_printf(L"Compile log for shader %ls (type %ls):\n%ls", pathname.string().c_str(), type.c_str(), infologw.c_str());
|
2005-10-05 18:42:09 +02:00
|
|
|
delete[] infolog;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!compile_success)
|
|
|
|
{
|
|
|
|
// Compilation failure caused by syntax errors and similar
|
|
|
|
// errors at the GLSL level does not set OpenGL error state
|
|
|
|
// according to the spec, but this might still prove to be
|
|
|
|
// useful some time.
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2009-11-03 22:46:35 +01:00
|
|
|
debug_printf(L"Failed to compile shader %ls (type %ls)\n", pathname.string().c_str(), type.c_str());
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2009-03-24 22:00:41 +01:00
|
|
|
err = ERR::SHDR_COMPILE;
|
2005-10-05 18:42:09 +02:00
|
|
|
goto fail_shadercreated;
|
|
|
|
}
|
2009-11-03 22:46:35 +01:00
|
|
|
}
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2006-09-22 15:19:40 +02:00
|
|
|
return INFO::OK;
|
2005-10-05 18:42:09 +02:00
|
|
|
|
|
|
|
fail_shadercreated:
|
2005-10-30 02:18:15 +01:00
|
|
|
pglDeleteObjectARB(shdr->id);
|
2005-10-05 18:42:09 +02:00
|
|
|
shdr->id = 0;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Free associated resources
|
|
|
|
static void Ogl_Shader_dtor(Ogl_Shader* shdr)
|
|
|
|
{
|
|
|
|
// shdr->id is 0 when reload has failed
|
|
|
|
if (shdr->id)
|
|
|
|
{
|
2005-10-30 02:18:15 +01:00
|
|
|
pglDeleteObjectARB(shdr->id);
|
2005-10-05 18:42:09 +02:00
|
|
|
shdr->id = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
static LibError Ogl_Shader_validate(const Ogl_Shader* UNUSED(shdr))
|
2005-10-12 06:35:01 +02:00
|
|
|
{
|
|
|
|
// TODO
|
2006-09-22 15:19:40 +02:00
|
|
|
return INFO::OK;
|
2005-10-12 06:35:01 +02:00
|
|
|
}
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2009-11-06 11:59:10 +01:00
|
|
|
static LibError Ogl_Shader_to_string(const Ogl_Shader* shdr, wchar_t* buf)
|
2005-10-21 09:47:38 +02:00
|
|
|
{
|
2009-11-06 11:59:10 +01:00
|
|
|
swprintf_s(buf, H_STRING_LEN, L"Ogl_Shader %p", shdr);
|
2006-09-22 15:19:40 +02:00
|
|
|
return INFO::OK;
|
2005-10-21 09:47:38 +02:00
|
|
|
}
|
|
|
|
|
2005-10-05 18:42:09 +02:00
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// Public API
|
|
|
|
|
|
|
|
// Create, load and compile a shader object of the given type
|
|
|
|
// (e.g. GL_VERTEX_SHADER_ARB). The given file will be used as
|
|
|
|
// source code for the shader.
|
2007-12-22 19:15:52 +01:00
|
|
|
Handle ogl_shader_load(const VfsPath& pathname, GLenum type)
|
2005-10-05 18:42:09 +02:00
|
|
|
{
|
2007-12-22 19:15:52 +01:00
|
|
|
return h_alloc(H_Ogl_Shader, pathname, 0, type);
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Free all resources associated with the given handle (subject
|
|
|
|
// to refcounting).
|
|
|
|
void ogl_shader_free(Handle& h)
|
|
|
|
{
|
|
|
|
h_free(h, H_Ogl_Shader);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attach a shader to the given OpenGL program.
|
2005-12-11 23:23:55 +01:00
|
|
|
LibError ogl_shader_attach(GLhandleARB program, Handle& h)
|
2005-10-05 18:42:09 +02:00
|
|
|
{
|
|
|
|
H_DEREF(h, Ogl_Shader, shdr);
|
|
|
|
|
|
|
|
if (!shdr->id)
|
2006-09-22 15:19:40 +02:00
|
|
|
WARN_RETURN(ERR::SHDR_NO_SHADER);
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2005-10-30 02:18:15 +01:00
|
|
|
pglAttachObjectARB(program, shdr->id);
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2006-09-22 15:19:40 +02:00
|
|
|
return INFO::OK;
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// Program type implementation
|
|
|
|
|
|
|
|
struct Ogl_Program {
|
|
|
|
// ID of the OpenGL program object
|
|
|
|
GLhandleARB id;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
H_TYPE_DEFINE(Ogl_Program);
|
|
|
|
|
|
|
|
|
|
|
|
// One-time initialization, called once by h_alloc, which is
|
|
|
|
// in turn called by ogl_program_load
|
2005-10-12 06:35:01 +02:00
|
|
|
static void Ogl_Program_init(Ogl_Program* UNUSED(p), va_list UNUSED(args))
|
2005-10-05 18:42:09 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Load the shader associated with one Shader element,
|
|
|
|
// and attach it to our program object.
|
2005-12-11 23:23:55 +01:00
|
|
|
static LibError do_load_shader(
|
2007-12-22 19:15:52 +01:00
|
|
|
Ogl_Program* p, const VfsPath& pathname, Handle UNUSED(h),
|
2005-10-05 18:42:09 +02:00
|
|
|
const CXeromyces& XeroFile, const XMBElement& Shader)
|
|
|
|
{
|
2007-05-02 14:07:08 +02:00
|
|
|
#define AT(x) int at_##x = XeroFile.GetAttributeID(#x)
|
2005-10-05 18:42:09 +02:00
|
|
|
AT(type);
|
|
|
|
#undef AT
|
|
|
|
|
2007-05-02 14:07:08 +02:00
|
|
|
CStr Type = Shader.GetAttributes().GetNamedItem(at_type);
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2007-02-01 15:46:14 +01:00
|
|
|
if (Type.empty())
|
2005-10-05 18:42:09 +02:00
|
|
|
{
|
2009-11-03 22:46:35 +01:00
|
|
|
LOG(CLogger::Error, LOG_CATEGORY, L"%ls: Missing attribute \"type\" in element \"Shader\".", pathname.string().c_str());
|
2006-09-22 15:19:40 +02:00
|
|
|
WARN_RETURN(ERR::CORRUPTED);
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
GLenum shadertype = string_to_shader_type(Type.c_str());
|
|
|
|
|
|
|
|
if (!shadertype)
|
|
|
|
{
|
2009-11-03 22:46:35 +01:00
|
|
|
LOG(CLogger::Error, LOG_CATEGORY, L"%ls: Unknown shader type \"%hs\" (valid are: VERTEX_SHADER, FRAGMENT_SHADER).", pathname.string().c_str(), Type.c_str());
|
2006-09-22 15:19:40 +02:00
|
|
|
WARN_RETURN(ERR::CORRUPTED);
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
|
2009-11-03 22:46:35 +01:00
|
|
|
CStr pathnameShader = Shader.GetText();
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2009-11-03 22:46:35 +01:00
|
|
|
if (pathnameShader.empty())
|
2005-10-05 18:42:09 +02:00
|
|
|
{
|
2009-11-03 22:46:35 +01:00
|
|
|
LOG(CLogger::Error, LOG_CATEGORY, L"%ls: Missing shader name.", pathname.string().c_str());
|
2006-09-22 15:19:40 +02:00
|
|
|
WARN_RETURN(ERR::CORRUPTED);
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
|
2009-11-03 22:46:35 +01:00
|
|
|
Handle hshader = ogl_shader_load(CStrW(pathnameShader), shadertype);
|
2005-10-05 18:42:09 +02:00
|
|
|
RETURN_ERR(hshader);
|
|
|
|
|
|
|
|
ogl_shader_attach(p->id, hshader);
|
|
|
|
|
|
|
|
// According to the OpenGL specification, a shader object's deletion
|
|
|
|
// will not be final as long as the shader object is attached to a
|
|
|
|
// container object.
|
|
|
|
// TODO: How will this work with automatic reload?
|
|
|
|
ogl_shader_free(hshader);
|
|
|
|
|
2006-09-22 15:19:40 +02:00
|
|
|
return INFO::OK;
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Reload the program object from the source file.
|
2009-11-03 22:46:35 +01:00
|
|
|
static LibError Ogl_Program_reload(Ogl_Program* p, const VfsPath& pathname, Handle h)
|
2005-10-05 18:42:09 +02:00
|
|
|
{
|
|
|
|
if (p->id)
|
2006-09-22 15:19:40 +02:00
|
|
|
return INFO::OK;
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2005-10-30 02:18:15 +01:00
|
|
|
p->id = pglCreateProgramObjectARB();
|
2005-10-05 18:42:09 +02:00
|
|
|
if (!p->id)
|
|
|
|
{
|
|
|
|
// The spec doesn't mention any error state that can be set
|
|
|
|
// here, but it may still help spot bad code.
|
2007-05-02 14:07:08 +02:00
|
|
|
ogl_WarnIfError();
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2006-09-22 15:19:40 +02:00
|
|
|
WARN_RETURN(ERR::SHDR_CREATE);
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CXeromyces XeroFile;
|
2007-12-22 19:15:52 +01:00
|
|
|
if (XeroFile.Load(pathname) != PSRETURN_OK)
|
2006-09-22 15:19:40 +02:00
|
|
|
WARN_RETURN(ERR::CORRUPTED); // more informative error message?
|
2005-10-05 18:42:09 +02:00
|
|
|
|
|
|
|
// Define all the elements and attributes used in the XML file
|
2007-05-02 14:07:08 +02:00
|
|
|
#define EL(x) int el_##x = XeroFile.GetElementID(#x)
|
2005-10-05 18:42:09 +02:00
|
|
|
EL(program);
|
|
|
|
EL(shaders);
|
|
|
|
EL(shader);
|
|
|
|
#undef EL
|
|
|
|
|
2007-05-02 14:07:08 +02:00
|
|
|
XMBElement Root = XeroFile.GetRoot();
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2007-05-02 14:07:08 +02:00
|
|
|
if (Root.GetNodeName() != el_program)
|
2005-10-05 18:42:09 +02:00
|
|
|
{
|
2009-11-03 22:46:35 +01:00
|
|
|
LOG(CLogger::Error, LOG_CATEGORY, L"%ls: XML root was not \"Program\".", pathname.string().c_str());
|
2006-09-22 15:19:40 +02:00
|
|
|
WARN_RETURN(ERR::CORRUPTED);
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
|
2007-05-02 14:07:08 +02:00
|
|
|
XMBElementList RootChildren = Root.GetChildNodes();
|
2005-10-05 18:42:09 +02:00
|
|
|
|
|
|
|
for(int i = 0; i < RootChildren.Count; ++i)
|
|
|
|
{
|
2007-05-02 14:07:08 +02:00
|
|
|
XMBElement Child = RootChildren.Item(i);
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2007-05-02 14:07:08 +02:00
|
|
|
int ChildName = Child.GetNodeName();
|
2005-10-05 18:42:09 +02:00
|
|
|
if (ChildName == el_shaders)
|
|
|
|
{
|
2007-05-02 14:07:08 +02:00
|
|
|
XMBElementList Shaders = Child.GetChildNodes();
|
2005-10-05 18:42:09 +02:00
|
|
|
|
|
|
|
for(int j = 0; j < Shaders.Count; ++j)
|
|
|
|
{
|
2007-05-02 14:07:08 +02:00
|
|
|
XMBElement Shader = Shaders.Item(j);
|
2005-10-05 18:42:09 +02:00
|
|
|
|
2007-05-02 14:07:08 +02:00
|
|
|
if (Shader.GetNodeName() != el_shader)
|
2005-10-05 18:42:09 +02:00
|
|
|
{
|
2009-11-03 22:46:35 +01:00
|
|
|
LOG(CLogger::Error, LOG_CATEGORY, L"%ls: Only \"Shader\" may be child of \"Shaders\".", pathname.string().c_str());
|
2006-09-22 15:19:40 +02:00
|
|
|
WARN_RETURN(ERR::CORRUPTED);
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
|
2007-12-22 19:15:52 +01:00
|
|
|
RETURN_ERR(do_load_shader(p, pathname, h, XeroFile, Shader));
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-11-03 22:46:35 +01:00
|
|
|
LOG(CLogger::Warning, LOG_CATEGORY, L"%ls: Unknown child of \"Program\".", pathname.string().c_str());
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-10-30 02:18:15 +01:00
|
|
|
pglLinkProgramARB(p->id);
|
2005-10-05 18:42:09 +02:00
|
|
|
|
|
|
|
GLint log_length;
|
|
|
|
GLint linked;
|
|
|
|
|
2009-07-14 15:34:31 +02:00
|
|
|
pglGetProgramiv(p->id, GL_OBJECT_LINK_STATUS_ARB, &linked);
|
|
|
|
pglGetProgramiv(p->id, GL_OBJECT_INFO_LOG_LENGTH_ARB, &log_length);
|
2005-10-05 18:42:09 +02:00
|
|
|
if (log_length > 1)
|
|
|
|
{
|
|
|
|
char* infolog = new char[log_length];
|
2009-07-14 15:34:31 +02:00
|
|
|
pglGetProgramInfoLog(p->id, log_length, 0, infolog);
|
2009-11-03 22:46:35 +01:00
|
|
|
CStrW infologw(infolog);
|
|
|
|
debug_printf(L"Linker log for %ls:\n%ls\n", pathname.string().c_str(), infologw.c_str());
|
2005-10-05 18:42:09 +02:00
|
|
|
delete[] infolog;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!linked)
|
|
|
|
{
|
2009-11-03 22:46:35 +01:00
|
|
|
debug_printf(L"Link failed for %ls\n", pathname.string().c_str());
|
2006-09-22 15:19:40 +02:00
|
|
|
WARN_RETURN(ERR::SHDR_LINK);
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
|
2006-09-22 15:19:40 +02:00
|
|
|
return INFO::OK;
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Free associated resources
|
|
|
|
static void Ogl_Program_dtor(Ogl_Program* p)
|
|
|
|
{
|
|
|
|
if (p->id)
|
|
|
|
{
|
2005-10-30 02:18:15 +01:00
|
|
|
pglDeleteObjectARB(p->id);
|
2005-10-05 18:42:09 +02:00
|
|
|
p->id = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
static LibError Ogl_Program_validate(const Ogl_Program* UNUSED(p))
|
2005-10-12 06:35:01 +02:00
|
|
|
{
|
|
|
|
// TODO
|
2006-09-22 15:19:40 +02:00
|
|
|
return INFO::OK;
|
2005-10-12 06:35:01 +02:00
|
|
|
}
|
|
|
|
|
2009-11-06 11:59:10 +01:00
|
|
|
static LibError Ogl_Program_to_string(const Ogl_Program* p, wchar_t* buf)
|
2005-10-21 09:47:38 +02:00
|
|
|
{
|
2009-11-06 11:59:10 +01:00
|
|
|
swprintf_s(buf, H_STRING_LEN, L"Ogl_Program %p", p);
|
2006-09-22 15:19:40 +02:00
|
|
|
return INFO::OK;
|
2005-10-21 09:47:38 +02:00
|
|
|
}
|
2005-10-05 18:42:09 +02:00
|
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
// Public API
|
|
|
|
|
|
|
|
// Load a program object based on the given XML file description.
|
|
|
|
// Shader objects are loaded and attached automatically.
|
2009-11-03 22:46:35 +01:00
|
|
|
Handle ogl_program_load(const VfsPath& pathname)
|
2005-10-05 18:42:09 +02:00
|
|
|
{
|
2009-11-03 22:46:35 +01:00
|
|
|
return h_alloc(H_Ogl_Program, pathname, 0);
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Free all resources associated with the given program handle.
|
|
|
|
void ogl_program_free(Handle& h)
|
|
|
|
{
|
|
|
|
h_free(h, H_Ogl_Program);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Activate the program (glUseProgramObjectARB).
|
|
|
|
// h may be 0, in which case program objects are disabled.
|
2005-12-11 23:23:55 +01:00
|
|
|
LibError ogl_program_use(Handle h)
|
2005-10-05 18:42:09 +02:00
|
|
|
{
|
|
|
|
if (!h)
|
|
|
|
{
|
2005-10-30 02:18:15 +01:00
|
|
|
pglUseProgramObjectARB(0);
|
2006-09-22 15:19:40 +02:00
|
|
|
return INFO::OK;
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Ogl_Program* p = H_USER_DATA(h, Ogl_Program);
|
|
|
|
if (!p || !p->id)
|
|
|
|
{
|
2005-10-30 02:18:15 +01:00
|
|
|
pglUseProgramObjectARB(0);
|
2006-09-22 15:19:40 +02:00
|
|
|
WARN_RETURN(ERR::INVALID_HANDLE);
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
|
2005-10-30 02:18:15 +01:00
|
|
|
pglUseProgramObjectARB(p->id);
|
2006-09-22 15:19:40 +02:00
|
|
|
return INFO::OK;
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Query uniform information
|
|
|
|
GLint ogl_program_get_uniform_location(Handle h, const char* name)
|
|
|
|
{
|
|
|
|
H_DEREF(h, Ogl_Program, p);
|
|
|
|
|
2005-10-30 02:18:15 +01:00
|
|
|
return pglGetUniformLocationARB(p->id, name);
|
2005-10-05 18:42:09 +02:00
|
|
|
}
|
2005-10-30 02:22:22 +02:00
|
|
|
|
|
|
|
|
|
|
|
// Query vertex attribute information
|
|
|
|
GLint ogl_program_get_attrib_location(Handle h, const char* name)
|
|
|
|
{
|
|
|
|
H_DEREF(h, Ogl_Program, p);
|
|
|
|
|
2005-10-30 02:18:15 +01:00
|
|
|
return pglGetAttribLocationARB(p->id, name);
|
2005-10-30 02:22:22 +02:00
|
|
|
}
|