1 module Engine.Buffer;
2 
3 import derelict.opengl3.gl;
4 import gl3n.linalg;
5 import std.stdio;
6 import Engine.Options;
7 
8 class Buffer {
9     GLenum type;
10 	GLenum memtype;
11 	GLenum buffer;
12 
13 	int size;
14 
15 	alias buffer this;
16 
17 	this(T)(T[] arr, GLenum type, GLenum memtype) {
18 		glGenBuffers(1, &buffer);
19 		glBindBuffer(type, buffer);
20 		glBufferData(type, T.sizeof * arr.length, &arr[0], memtype);
21 
22 		this.type = type;
23 		this.memtype = memtype;
24 		this.size = T.sizeof * arr.length;
25 	}
26 
27 	this()(int size, GLenum type, GLenum memtype) {
28 		glGenBuffers(1, &buffer);
29 		glBindBuffer(type, buffer);	
30 		glBufferData(type, size, null, memtype);
31 	
32 		this.type = type;
33 		this.memtype = memtype;
34 		this.size = size;
35 	}
36 
37 	this()(long size, GLenum type, GLenum memtype) {
38 		this(cast(int)size, type, memtype);
39 	}
40 
41 	final void Resize(long size) {
42 		Resize(cast(int)size);
43 	}
44 
45 	final void Resize(int size) {
46 		this.size = size;
47 		glBindBuffer(type, buffer);	
48 		glBufferData(type, size, null, memtype);
49 	}
50 
51 	final void Bind()() {
52 		glBindBuffer(type, buffer);
53 	}
54 
55 	final void Unbind()() {
56 		glBindBuffer(type, 0);
57 	}
58 
59 	void Update(T)(T[] arr, int offset = 0)
60 	{
61 		if (T.sizeof * arr.length + offset * T.sizeof > size) {
62 			throw new Exception("array type is too big for this buffer");
63 		}
64 
65 		Bind();
66 		glBufferSubData(type, offset * T.sizeof, T.sizeof * arr.length, &arr[0]);
67 	}
68 
69 	void Update(T)(void delegate(T[] arr) update) {
70 		auto arr = Map!T();
71 		scope( exit ) Unmap();
72 		update(arr);
73 	}
74 
75 	T[] Map(T)(size_t offset, size_t len) {
76 		Bind();
77 		// map the buffer object into client's memory
78         // Note that glMapBufferARB() causes sync issue.
79         // If GPU is working with this buffer, glMapBufferARB() will wait(stall)
80         // for GPU to finish its job. To avoid waiting (stall), you can call
81         // first glBufferDataARB() with NULL pointer before glMapBufferARB().
82         // If you do that, the previous data in PBO will be discarded and
83         // glMapBufferARB() returns a new allocated pointer immediately
84         // even if GPU is still working with the previous data.
85 		//glBufferData(type, size, null, memtype);
86 		void* arrPTR;
87 		if (Options.useMapBufferRange) {
88 			arrPTR = glMapBufferRange(type,offset,len,GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_WRITE_BIT );
89 		} else {	
90 			arrPTR = glMapBuffer(type,GL_WRITE_ONLY);
91 		}
92 		if (arrPTR !is null) {
93 			return (cast(T*)arrPTR)[0..size/T.sizeof];
94 		} else {
95 			throw new Exception("cannot map buffer." );
96 		}
97 	}
98 
99 	T[] Map(T)() {
100 		Bind();
101 		// map the buffer object into client's memory
102         // Note that glMapBufferARB() causes sync issue.
103         // If GPU is working with this buffer, glMapBufferARB() will wait(stall)
104         // for GPU to finish its job. To avoid waiting (stall), you can call
105         // first glBufferDataARB() with NULL pointer before glMapBufferARB().
106         // If you do that, the previous data in PBO will be discarded and
107         // glMapBufferARB() returns a new allocated pointer immediately
108         // even if GPU is still working with the previous data.
109 		//glBufferData(type, size, null, memtype);
110 		void* arrPTR;
111 		if (Options.useMapBufferRange) {
112 			arrPTR = glMapBufferRange(type,0,size,GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_WRITE_BIT );
113 		} else {
114 			arrPTR = glMapBuffer(type,GL_WRITE_ONLY);
115 		}
116 		if (arrPTR !is null) {
117 			return (cast(T*)arrPTR)[0..size/T.sizeof];
118 		} else {
119 			throw new Exception("cannot map buffer." );
120 		}
121 	}
122 
123 	void Unmap() {
124 		Bind();
125 		glUnmapBuffer(type);
126 	}
127 }