1 module armos.graphics.buffer;
2 
3 import derelict.opengl3.gl;
4 import armos.graphics.vao;
5 import armos.math;
6 import std.variant;
7 
8 
9 /++
10 +/
11 class Buffer {
12     public{
13 
14         /++
15             BufferTypeを指定して初期化します.
16         +/
17         this(in BufferType bufferType){
18             glGenBuffers(1, cast(uint*)&_id);
19             _type = bufferType;
20         }
21 
22         ///
23         ~this(){
24             glDeleteBuffers(1, cast(uint*)&_id);
25         }
26 
27         /++
28         +/
29         void begin(){
30             int savedID;
31             glGetIntegerv(bindingEnum(_type), &savedID);
32             _savedIDs ~= savedID;
33             glBindBuffer(_type, _id);
34         }
35 
36         /++
37         +/
38         void end(){
39             import std.range;
40             glBindBuffer(_type, _savedIDs[$-1]);
41             if (_savedIDs.length == 0) {
42                 assert(0, "stack is empty");
43             }else{
44                 _savedIDs.popBack;
45             }
46         }
47 
48         ///
49         Buffer array(T)(in T[] array, in size_t dimention = 1,
50                         in BufferUsageFrequency freq   = BufferUsageFrequency.Dynamic,
51                         in BufferUsageNature    nature = BufferUsageNature.Draw)
52         if(__traits(isArithmetic, T)){
53             if(array.length == 0)return this;
54 
55             _array     = Algebraic!(const(int)[], const(float)[], const(double)[])(array);
56             _dimention = dimention;
57             _freq      = freq;
58             _nature    = nature;
59 
60             updateGlBuffer;
61             return this;
62         }
63         
64         ///
65         Buffer array(V)(in V[] array,
66                         in BufferUsageFrequency freq   = BufferUsageFrequency.Dynamic,
67                         in BufferUsageNature    nature = BufferUsageNature.Draw)
68         if(isVector!V){
69             if(array.length == 0)return this;
70             V.elementType[] raw = new V.elementType[array.length*V.dimention];
71             for (size_t i = 0, pos = 0, len = array.length; i < len; ++i, pos += V.dimention) {
72                 raw[pos .. pos + V.dimention] = array[i].elements;
73             }
74             this.array(raw, V.dimention, freq, nature);
75             return this;
76         }
77 
78         ///
79         Buffer updateGlBuffer(){
80             if(_array.type == typeid(const(float)[])){
81                 updateGlBuffer!(const(float));
82             }else if(_array.type == typeid(const(double)[])){
83                 updateGlBuffer!(const(double));
84             }else if(_array.type == typeid(const(int)[])){
85                 updateGlBuffer!(const(int));
86             }
87             return this;
88         }
89 
90         ///
91         size_t size()const{return _size;}
92 
93         ///
94         BufferType type()const{return _type;}
95     }//public
96 
97     private{
98         int _id;
99         int[] _savedIDs;
100         BufferType _type;
101         size_t _size;
102         size_t _dimention;
103         BufferUsageFrequency _freq;
104         BufferUsageNature _nature;
105         Algebraic!(const(int)[], const(float)[], const(double)[]) _array;
106 
107         void updateGlBuffer(T)(){
108             auto arr = _array.get!(T[]);
109             begin;
110             immutable currentSize = arr.length * arr[0].sizeof;
111              
112             if(_size != currentSize){
113                 _size = currentSize;
114                 glBufferData(_type, _size, arr.ptr, usageEnum(_freq, _nature));
115             }else{
116                 glBufferSubData(_type, 0, _size, arr.ptr);
117             }
118             
119             import std.conv;
120             if(_type != BufferType.ElementArray){
121                 static if(is(T == const(float))){
122                     GLenum glDataType = GL_FLOAT;
123                 }else static if(is(T == const(double))){
124                     GLenum glDataType = GL_DOUBLE;
125                 }else static if(is(T == const(int))){
126                     GLenum glDataType = GL_INT;
127                 }
128                 glVertexAttribPointer(0,
129                                       _dimention.to!int,
130                                       glDataType,
131                                       GL_FALSE,
132                                       0,
133                                       null);
134             }
135             end;
136         }
137     }//private
138 }//class Vbo
139 
140 /++
141 +/
142 enum BufferType{
143     Array             = GL_ARRAY_BUFFER,              /// Vertex attributes
144     AtomicCounter     = GL_ATOMIC_COUNTER_BUFFER,     /// Atomic counter storage
145     CopyRead          = GL_COPY_READ_BUFFER,          /// Buffer copy source
146     CopyWrite         = GL_COPY_WRITE_BUFFER,         /// Buffer copy destination
147     DispatchIndirect  = GL_DISPATCH_INDIRECT_BUFFER,  /// Indirect compute dispatch commands
148     DrawIndirect      = GL_DRAW_INDIRECT_BUFFER,      /// Indirect command arguments
149     ElementArray      = GL_ELEMENT_ARRAY_BUFFER,      /// Vertex array indices
150     PixelPack         = GL_PIXEL_PACK_BUFFER,         /// Pixel read target
151     PixelUnpack       = GL_PIXEL_UNPACK_BUFFER,       /// Texture data source
152     Query             = GL_QUERY_BUFFER,              /// Query result buffer
153     ShaderStorage     = GL_SHADER_STORAGE_BUFFER,     /// Read-write storage for shaders
154     Texture           = GL_TEXTURE_BUFFER,            /// Texture data buffer
155     TransformFeedback = GL_TRANSFORM_FEEDBACK_BUFFER, /// Transform feedback buffer
156     Uniform           = GL_UNIFORM_BUFFER,            /// Uniform block storage
157 }
158 
159 private GLenum bindingEnum(in BufferType bufferType){
160     GLenum bindingType;
161     switch (bufferType) {
162         case BufferType.Array:
163             bindingType =  GL_ARRAY_BUFFER_BINDING;
164             break;
165         case BufferType.AtomicCounter:
166             bindingType = GL_ATOMIC_COUNTER_BUFFER_BINDING;
167             break;
168         case BufferType.CopyRead:
169             bindingType = GL_COPY_READ_BUFFER_BINDING;
170             break;
171         case BufferType.CopyWrite:
172             bindingType = GL_COPY_WRITE_BUFFER_BINDING;
173             break;
174         case BufferType.DispatchIndirect:
175             bindingType = GL_DISPATCH_INDIRECT_BUFFER_BINDING;
176             break;
177         case BufferType.DrawIndirect:
178             bindingType = GL_DRAW_INDIRECT_BUFFER_BINDING;
179             break;
180         case BufferType.ElementArray:
181             bindingType = GL_ELEMENT_ARRAY_BUFFER_BINDING;
182             break;
183         case BufferType.PixelPack:
184             bindingType = GL_PIXEL_PACK_BUFFER_BINDING;
185             break;
186         case BufferType.PixelUnpack:
187             bindingType = GL_PIXEL_UNPACK_BUFFER_BINDING;
188             break;
189         case BufferType.Query:
190             bindingType = GL_QUERY_BUFFER_BINDING;
191             break;
192         case BufferType.ShaderStorage:
193             bindingType = GL_SHADER_STORAGE_BUFFER_BINDING;
194             break;
195         case BufferType.Texture:
196             bindingType = GL_TEXTURE_BUFFER_BINDING;
197             break;
198         case BufferType.TransformFeedback:
199             bindingType = GL_TRANSFORM_FEEDBACK_BUFFER_BINDING;
200             break;
201         case BufferType.Uniform:
202             bindingType = GL_UNIFORM_BUFFER_BINDING;
203             break;
204         default : assert(0, "invalid value");
205     }
206     return bindingType;
207 }
208 
209 /++
210     Bufferの更新頻度を表すenumです.
211 +/
212 enum BufferUsageFrequency{
213     Stream,  /// 読み込みも書き込みも一度のみ
214     Static,  /// 書き込みが一度だけで,読み込みは何度も行われる
215     Dynamic, /// 何度も読み書きが行われる
216 }
217 
218 /++
219     Bufferの読み書きの方法を表すenumです.
220 +/
221 enum BufferUsageNature{
222     Draw, /// アプリケーション側から書き込まれ,OpenGLが描画のために読み込む
223     Read, /// OpenGL側で書き込まれ,アプリケーション側で読み込む
224     Copy, /// OpenGL側で書き込まれ,描画に用いられる
225 }
226 
227 private static GLenum[BufferUsageNature][BufferUsageFrequency] usageEnumsTable(){
228     GLenum[BufferUsageNature][BufferUsageFrequency] enums;
229     enums[BufferUsageFrequency.Stream][BufferUsageNature.Draw] = GL_STREAM_DRAW;
230     enums[BufferUsageFrequency.Stream][BufferUsageNature.Read] = GL_STREAM_READ;
231     enums[BufferUsageFrequency.Stream][BufferUsageNature.Copy] = GL_STREAM_COPY;
232 
233     enums[BufferUsageFrequency.Static][BufferUsageNature.Draw] = GL_STATIC_DRAW;
234     enums[BufferUsageFrequency.Static][BufferUsageNature.Read] = GL_STATIC_READ;
235     enums[BufferUsageFrequency.Static][BufferUsageNature.Copy] = GL_STATIC_COPY;
236 
237     enums[BufferUsageFrequency.Dynamic][BufferUsageNature.Draw] = GL_DYNAMIC_DRAW;
238     enums[BufferUsageFrequency.Dynamic][BufferUsageNature.Read] = GL_DYNAMIC_READ;
239     enums[BufferUsageFrequency.Dynamic][BufferUsageNature.Copy] = GL_DYNAMIC_COPY;
240     return enums;
241 };
242 
243 static unittest{
244     static assert(usageEnumsTable[BufferUsageFrequency.Static][BufferUsageNature.Draw] == GL_STATIC_DRAW);
245 }
246 
247 private GLenum usageEnum(in BufferUsageFrequency freq, in BufferUsageNature nature){
248     return usageEnumsTable[freq][nature];
249 }