GLSLEffect.cs 2.72 KB
Newer Older
pcy's avatar
initial  
pcy committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

/* TODO:
 * * apply
 *   * HOW????
 *   * OnApply: protecteed internal :(
 *   * INTERNAL_applyEffect: internal...
 *     + calls mojoshader! --> hijack?!!
 *       -> glEffect{Begin,End}: state mgmt | unbind shader!
 *       -> glEffect{Begin,End}Pass: bind shader! | state mgmt
 *   * GD draw calls: ok
 *     * EXCEPT mojoshader: glProgramReady, glProgramViewportInfo
 *       -> set up uniforms!
 * * uniforms
 * * samplers, textures
 */

namespace PoroCYon.FNAGLSL {
	public class GLSLEffect : Effect {
		uint[] glshdrs;
		uint glprgm;

		public unsafe GLSLEffect(GraphicsDevice gd,
					IDictionary<GLSLPurpose, string> shaders)
				: base(/*UGLY hack*/new BasicEffect(gd)) {
			GL.Init(gd);

			// TODO: gd.GLDevice.DeleteEffect(base.glEffect),
			//       set base.glEffect.{GL,}EffectData to IntPtr.Zero

			var shdrs = new uint[shaders.Count];
			bool ok = false;
			uint prgm = 0;

			try {
				prgm = GL.CreateProgram();
				if (prgm == 0) GL.Throw();

				int i = 0;
				foreach (var kvp in shaders) {
					uint sh = GL.CreateShader(kvp.Key);
					if (sh == 0) GL.Throw();

					byte[] utf8src = Encoding.UTF8.GetBytes(kvp.Value);
					fixed (byte* sbuf = utf8src) {
						sbyte** uurgh = stackalloc sbyte*[1];
						*uurgh = (sbyte*)sbuf;
						int* uurgh2 = stackalloc int[1];
						*uurgh2 = utf8src.Length;
						GL.ShaderSource(sh, 1, uurgh, uurgh2);
					}
					GL.Throw();

					GL.CompileShader(sh);

					if (GL.GetError() != 0 || GL.GetShaderiv(sh, GL.COMPILE_STATUS) == 0) {
						int len = GL.GetShaderiv(sh, GL.INFO_LOG_LENGTH);
						var msg = new sbyte[len];
						fixed (sbyte* buf = msg) {
							GL.GetShaderInfoLog(sh, len, null, buf);
							throw new GLSLCompileException(new String(buf, 0, len-1));
						}
					}

					GL.AttachShader(prgm, sh);
					GL.Throw();

					shdrs[i++] = sh;
				}

				GL.LinkProgram(prgm);
				if (GL.GetError() != 0 || GL.GetProgramiv(prgm, GL.LINK_STATUS) == 0) {
					int len = GL.GetProgramiv(prgm, GL.INFO_LOG_LENGTH);
					var msg = new sbyte[len];
					fixed (sbyte* buf = msg) {
						GL.GetProgramInfoLog(prgm, len, null, buf);
						throw new GLSLLinkException(new String(buf, 0, len-1));
					}
				}

				ok = true;
			}
			finally {
				if (!ok) {
					DelShdrPrgm();
				} else {
					glshdrs = shdrs;
					glprgm  = prgm ;
				}
			}
		}

		void DelShdrPrgm() {
			for (int i = 0; i < glshdrs.Length; ++i)
				if (shdrs[i] != 0)
					GL.DeleteShader(shdrs[i]);

			if (prgm != 0)
				GL.DeleteProgram(prgm);
		}

		protected override void Dispose(bool disposing) {
			if (!IsDisposed) {
				DelShdrPrgm();
			}

			base.Dispose(disposing);
		}
	}
}