module renderer.api.opengl.CgKernel;
template MCgKernel() {
private import renderer.cg.cgGL;
private import utils.StructClass : simpleStructCtor;
private import std.file : read, exists;
private template MCgProgram() {
/+void setParam(ShaderParam param, mat4 val) {
cgGLSetMatrixParameterfr(cast(CGparameter)param, &val.data[0]);
}
void setStateMatrixParam(ShaderParam param, StateMatrix sm, MatrixTransform mt)
{
cgGLSetStateMatrixParameter(cast(CGparameter)param, cast(CGGLenum)sm, cast(CGGLenum)mt);
}+/
final CGparameter getParam(char[] name) {
{
CGparameter* p = name in namedParams;
if (p) return *p;
}
CGparameter p = cgGetNamedParameter(cgProgram, toStringz(name));
if (p is null) throw new Exception(`Unknown shader param: '` ~ name ~ `'`);
namedParams[name.dup] = p;
namedParams.rehash;
return p;
}
override void opIndexAssign(float val, char[] name) {
cgGLSetParameter1f(getParam(name), val);
}
override void opIndexAssign(vec2 val, char[] name) {
cgGLSetParameter2fv(getParam(name), &val.x);
}
override void opIndexAssign(vec3 val, char[] name) {
cgGLSetParameter3fv(getParam(name), &val.x);
}
override void opIndexAssign(vec4 val, char[] name) {
cgGLSetParameter4fv(getParam(name), &val.x);
}
override void opIndexAssign(StateMatrix val, char[] name) {
static CGGLenum[] typeMap = [
CGGLenum.CG_GL_MODELVIEW_MATRIX,
CGGLenum.CG_GL_PROJECTION_MATRIX,
CGGLenum.CG_GL_TEXTURE_MATRIX,
CGGLenum.CG_GL_MODELVIEW_PROJECTION_MATRIX
];
static CGGLenum[] xformMap = [
CGGLenum.CG_GL_MATRIX_IDENTITY,
CGGLenum.CG_GL_MATRIX_TRANSPOSE,
CGGLenum.CG_GL_MATRIX_INVERSE,
CGGLenum.CG_GL_MATRIX_INVERSE_TRANSPOSE
];
cgGLSetStateMatrixParameter(getParam(name), typeMap[cast(int)val.type], xformMap[cast(int)val.transform]);
}
protected {
CGprofile cgProfile;
CGprogram cgProgram;
CGparameter[char[]] namedParams;
}
}
class CgVertexShader : HwVertexShader {
mixin MCgProgram;
private void extractVaryingParams() {
CGparameter leaf = cgGetFirstLeafParameter(cgProgram, CGenum.CG_PROGRAM);
while (leaf) {
CGenum var = cgGetParameterVariability(leaf);
CGenum dir = cgGetParameterDirection(leaf);
if (var == CGenum.CG_VARYING && dir != CGenum.CG_OUT && dir != CGenum.CG_ERROR) {
CGresource res = cgGetParameterResource(leaf);
if (res >= VertexDataType.min && res <= VertexDataType.max) {
VertexDataType vdt = cast(VertexDataType)res;
varyingParams ~= VaryingParam(leaf, vdt);
writefln("CgVertexShader: extracted a param: ", cast(uint)vdt);
}
}
leaf = cgGetNextLeafParameter(leaf);
}
}
void bind() {
foreach (inout VaryingParam vpar; varyingParams) {
CgKernel.vdtCgParams[vpar.vdt - CgKernel.vdtArrayOffset] = vpar.param;
}
//writefln(`enabling vertex profile: `, cast(int)this.cgProfile);
cgGLEnableProfile(this.cgProfile);
cgGLBindProgram(this.cgProgram);
}
void unbind() {
foreach (inout VaryingParam vpar; varyingParams) {
CgKernel.vdtCgParams[vpar.vdt - CgKernel.vdtArrayOffset] = null;
}
cgGLUnbindProgram(this.cgProfile);
cgGLDisableProfile(this.cgProfile);
}
this (CGprogram prog, CGprofile prof) {
this.cgProgram = prog;
this.cgProfile = prof;
extractVaryingParams();
}
override bool isVertexDataNeeded(VertexDataType vdt) {
foreach (inout VaryingParam vp; varyingParams) {
if (vp.vdt is vdt) {
return true;
}
}
return false;
}
/+void setTexture(ShaderParam param, Texture tex) {
assert(false); // not implemented
}+/
package {
struct VaryingParam {
CGparameter param;
VertexDataType vdt;
mixin simpleStructCtor;
}
VaryingParam[] varyingParams;
}
}
class CgFragmentShader : HwFragmentShader {
mixin MCgProgram;
private void extractTexBindings() {
CGparameter leaf = cgGetFirstLeafParameter(cgProgram, CGenum.CG_PROGRAM);
while (leaf) {
CGenum var = cgGetParameterVariability(leaf);
CGenum dir = cgGetParameterDirection(leaf);
if (var != CGenum.CG_VARYING && dir != CGenum.CG_OUT && dir != CGenum.CG_ERROR) {
CGresource res = cgGetParameterResource(leaf);
if (res >= CGresource.CG_TEXUNIT0 && res <= CGresource.CG_TEXUNIT15) {
texBindings ~= TexBinding(leaf, res - CGresource.CG_TEXUNIT0);
}
}
leaf = cgGetNextLeafParameter(leaf);
}
}
this (CGprogram prog, CGprofile prof) {
this.cgProgram = prog;
this.cgProfile = prof;
extractTexBindings();
}
/+void setTexture(ShaderParam param, Texture tex) { TODO
if (tex !is null && tex.id != -1) {
foreach (inout TexBinding tb; texBindings) {
if (tb.pred == cast(CGparameter)param) {
CgTextureKernel.texUnitTextures[tb.succ] = tex;
return;
}
}
}
}+/
void bind() {
//writefln(`enabling fragment profile: `, cast(int)this.cgProfile);
cgGLEnableProfile(this.cgProfile);
cgGLBindProgram(this.cgProgram);
}
void unbind() {
cgGLUnbindProgram(this.cgProfile);
cgGLDisableProfile(this.cgProfile);
}
package {
struct TexBinding {
CGparameter param;
uint unit;
mixin simpleStructCtor;
}
TexBinding[] texBindings;
}
}
static class CgException : Exception {
this (char[] str) {
super(str);
}
}
class CgKernel : HwShaderKernel {
private static void cgErrorCallback() {
CGerror error = cgGetError();
char* errStr;
if (error == CGerror.CG_COMPILER_ERROR) {
errStr = cgGetLastListing(cgContext);
} else {
errStr = cgGetErrorString(error);
}
throw new CgException("cgError: " ~ .toString(errStr));
}
override bool canLoadVertexShader(char[] filename, char[] profile) {
return
cgContext !is null &&
cgVertexProfile !is CGprofile.CG_PROFILE_UNKNOWN &&
filename.length > 3 && tolower(filename[$-3..$]) == `.cg` &&
std.file.exists(filename);
}
override bool canLoadFragmentShader(char[] filename, char[] profile) {
return
cgContext !is null &&
cgFragmentProfile !is CGprofile.CG_PROFILE_UNKNOWN &&
filename.length > 3 && tolower(filename[$-3..$]) == `.cg` &&
std.file.exists(filename);
}
private bool loadCgProgramFile(char[] programString, CGprofile profile, out CGprogram cgProg) {
CGprogram prog;
bool res = loadCgProgram(cast(char[])std.file.read(programString), profile, prog);
if (res) cgProg = prog;
return res;
}
private bool loadCgProgram(char[] programString, CGprofile profile, out CGprogram cgProg) {
if (!cgGLIsProfileSupported(profile)) {
return false;
}
char[] progStr = programString;//programString.replace("/+", "/*").replace("+/", "*/") ~ '\0';
CGprogram prog;
try {
cgGLSetOptimalOptions(profile);
prog = cgCreateProgram(cgContext, CGenum.CG_SOURCE, .toStringz(progStr), profile, "main", null);
cgGLEnableProfile(profile);
cgGLLoadProgram(prog);
cgGLDisableProfile(profile);
}
catch (CgException err) {
writefln(err);
return false;
}
cgProg = prog;
return true;
}
override HwVertexShader loadVertexShader(char[] filename, char[] profileName) {
writefln(`CgKernel.loadVertexShader (%s)`, filename);
CGprofile profile = this.cgVertexProfile;
if (profileName !is null) profile = cgGetProfile(.toStringz(profileName));
assert (profile != CGprofile.CG_PROFILE_UNKNOWN);
CGprogram prog;
if (canLoadVertexShader(filename, profileName) && loadCgProgramFile(filename, profile, prog)) {
return new CgVertexShader(prog, profile);
} else {
return null;
}
}
override HwFragmentShader loadFragmentShader(char[] filename, char[] profileName) {
writefln(`CgKernel.loadFragmentShader (%s)`, filename);
CGprofile profile = this.cgFragmentProfile;
if (profileName !is null) profile = cgGetProfile(.toStringz(profileName));
assert (profile != CGprofile.CG_PROFILE_UNKNOWN);
CGprogram prog;
if (canLoadFragmentShader(filename, profileName) && loadCgProgramFile(filename, profile, prog)) {
return new CgFragmentShader(prog, profile);
} else {
return null;
}
}
protected void enableVertexArrayCallback(VertexDataType vdt, uint components, uint glType, uint stride, void* ptr) {
CGparameter cgParam = vdtCgParams[vdt - vdtArrayOffset];
if (cgParam) {
//writefln(`enabling cg array: `, cast(int)vdt);
cgGLEnableClientState(cgParam);
cgGLSetParameterPointer(cgParam, components, glType, stride, ptr);
}
}
protected void disableVertexArrayCallback(VertexDataType vdt) {
CGparameter cgParam = vdtCgParams[vdt - vdtArrayOffset];
if (cgParam) {
//writefln(`disabling cg array: `, cast(int)vdt);
cgGLDisableClientState(cgParam);
}
}
this() {
renderer.cg.cgGL.initCg();
renderer.cg.cgGL.initCgGl();
writefln(`Loaded the CgGL shared library`);
if (cgCreateContext !is null) {
cgContext = cgCreateContext();
assert (cgGLGetLatestProfile !is null);
cgVertexProfile = cgGLGetLatestProfile(CGGLenum.CG_GL_VERTEX);
if (cgVertexProfile !is CGprofile.CG_PROFILE_UNKNOWN) {
assert (cgGLSetOptimalOptions !is null);
cgGLSetOptimalOptions(cgVertexProfile);
//checkForCgError("selecting vertex profile");
assert (cgGetProfileString !is null);
writefln("Cg fragment profile: ", .toString(cgGetProfileString(cgVertexProfile)));
} else writefln("No Cg fragment shader support");
cgFragmentProfile = cgGLGetLatestProfile(CGGLenum.CG_GL_FRAGMENT);
if (cgFragmentProfile !is CGprofile.CG_PROFILE_UNKNOWN) {
assert (cgGLSetOptimalOptions !is null);
cgGLSetOptimalOptions(cgFragmentProfile);
//checkForCgError("selecting vertex profile");
assert (cgGetProfileString !is null);
writefln("Cg vertex profile: ", .toString(cgGetProfileString(cgFragmentProfile)));
} else writefln("No Cg vertex shader support");
if (cgSetErrorCallback !is null) cgSetErrorCallback(&cgErrorCallback);
addEnableArrayCallback(&enableVertexArrayCallback);
addDisableArrayCallback(&disableVertexArrayCallback);
}
}
static {
CGcontext cgContext;
CGprofile cgVertexProfile;
CGprofile cgFragmentProfile;
static const uint vdtArraySize = cast(uint)(VertexDataType.max - VertexDataType.min + 1);
static const uint vdtArrayOffset = cast(uint)VertexDataType.min;
static CGparameter[vdtArraySize] vdtCgParams;
}
}
protected final void initCgKernel() {
writefln(`Initializing the Cg kernel`);
addHwShaderKernel(new CgKernel);
}
}