1 /+ 2 The MIT License (MIT) 3 4 Copyright (c) <2013> <Oleg Butko (deviator), Anton Akzhigitov (Akzwar)> 5 6 Permission is hereby granted, free of charge, to any person obtaining a copy 7 of this software and associated documentation files (the "Software"), to deal 8 in the Software without restriction, including without limitation the rights 9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 copies of the Software, and to permit persons to whom the Software is 11 furnished to do so, subject to the following conditions: 12 13 The above copyright notice and this permission notice shall be included in 14 all copies or substantial portions of the Software. 15 16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 THE SOFTWARE. 23 +/ 24 25 module des.gl.base.object; 26 27 import std.c..string; 28 29 import derelict.opengl3.gl3; 30 31 import des.gl.base.type; 32 33 import des.util.data.type; 34 35 /// 36 class GLObjException : DesGLException 37 { 38 /// 39 this( string msg, string file=__FILE__, size_t line=__LINE__ ) @safe pure nothrow 40 { super( msg, file, line ); } 41 } 42 43 /// 44 class GLBuffer : DesObject 45 { 46 mixin DES; 47 mixin ClassLogger; 48 49 protected: 50 uint _id; 51 Target type; 52 53 /// 54 size_t data_size; 55 /// 56 uint element_count; 57 58 /// 59 GLenum gltype() const nothrow @property { return cast(GLenum)type; } 60 61 public: 62 63 /// 64 enum Usage 65 { 66 STREAM_DRAW = GL_STREAM_DRAW, /// `GL_STREAM_DRAW` 67 STREAM_READ = GL_STREAM_READ, /// `GL_STREAM_READ` 68 STREAM_COPY = GL_STREAM_COPY, /// `GL_STREAM_COPY` 69 STATIC_DRAW = GL_STATIC_DRAW, /// `GL_STATIC_DRAW` 70 STATIC_READ = GL_STATIC_READ, /// `GL_STATIC_READ` 71 STATIC_COPY = GL_STATIC_COPY, /// `GL_STATIC_COPY` 72 DYNAMIC_DRAW = GL_DYNAMIC_DRAW, /// `GL_DYNAMIC_DRAW` 73 DYNAMIC_READ = GL_DYNAMIC_READ, /// `GL_DYNAMIC_READ` 74 DYNAMIC_COPY = GL_DYNAMIC_COPY /// `GL_DYNAMIC_COPY` 75 } 76 77 /// 78 enum Access 79 { 80 READ_ONLY = GL_READ_ONLY, /// `GL_READ_ONLY` 81 WRITE_ONLY = GL_WRITE_ONLY, /// `GL_WRITE_ONLY` 82 READ_WRITE = GL_READ_WRITE /// `GL_READ_WRITE` 83 } 84 85 /// 86 enum Target 87 { 88 ARRAY_BUFFER = GL_ARRAY_BUFFER, /// `GL_ARRAY_BUFFER` 89 ATOMIC_COUNTER_BUFFER = GL_ATOMIC_COUNTER_BUFFER, /// `GL_ATOMIC_COUNTER_BUFFER` 90 COPY_READ_BUFFER = GL_COPY_READ_BUFFER, /// `GL_COPY_READ_BUFFER` 91 COPY_WRITE_BUFFER = GL_COPY_WRITE_BUFFER, /// `GL_COPY_WRITE_BUFFER` 92 DRAW_INDIRECT_BUFFER = GL_DRAW_INDIRECT_BUFFER, /// `GL_DRAW_INDIRECT_BUFFER` 93 DISPATCH_INDIRECT_BUFFER = GL_DISPATCH_INDIRECT_BUFFER, /// `GL_DISPATCH_INDIRECT_BUFFER` 94 ELEMENT_ARRAY_BUFFER = GL_ELEMENT_ARRAY_BUFFER, /// `GL_ELEMENT_ARRAY_BUFFER` 95 PIXEL_PACK_BUFFER = GL_PIXEL_PACK_BUFFER, /// `GL_PIXEL_PACK_BUFFER` 96 PIXEL_UNPACK_BUFFER = GL_PIXEL_UNPACK_BUFFER, /// `GL_PIXEL_UNPACK_BUFFER` 97 SHADER_STORAGE_BUFFER = GL_SHADER_STORAGE_BUFFER, /// `GL_SHADER_STORAGE_BUFFER` 98 TEXTURE_BUFFER = GL_TEXTURE_BUFFER, /// `GL_TEXTURE_BUFFER` 99 TRANSFORM_FEEDBACK_BUFFER = GL_TRANSFORM_FEEDBACK_BUFFER, /// `GL_TRANSFORM_FEEDBACK_BUFFER` 100 UNIFORM_BUFFER = GL_UNIFORM_BUFFER /// `GL_UNIFORM_BUFFER` 101 } 102 103 /// `glBindBuffer( trg, 0 )` 104 static nothrow void unbind( Target trg ) 105 { glBindBuffer( cast(GLenum)trg, 0 ); } 106 107 /// 108 this( Target tp=Target.ARRAY_BUFFER ) 109 { 110 checkGLCall!glGenBuffers( 1, &_id ); 111 type = tp; 112 113 logger = new InstanceLogger( this, format( "%d", _id ) ); 114 115 logger.Debug( "type [%s]", type ); 116 } 117 118 final 119 { 120 /// `glBindBuffer` 121 void bind() 122 { 123 checkGLCall!glBindBuffer( gltype, _id ); 124 debug logger.trace( "type [%s]", type ); 125 } 126 127 /// `glBindBuffer` with self target to 0 128 void unbind() 129 { 130 checkGLCall!glBindBuffer( gltype, 0 ); 131 debug logger.trace( "type [%s]", type ); 132 } 133 134 /// 135 uint id() @property const { return _id; } 136 } 137 138 /++ calls when element count changed (in setUntypedData) 139 + 140 + See_Also: `setUntypedData` 141 +/ 142 Signal!uint elementCountCB; 143 144 /++ calls when element size changed (in setUntypedData) 145 + 146 + See_Also: `setUntypedData` 147 +/ 148 Signal!uint elementSizeCB; 149 150 /++ calls when data size changed (in setUntypedData) 151 + 152 + See_Also: `setUntypedData` 153 +/ 154 Signal!size_t dataSizeCB; 155 156 /++ `bind`, `glBufferData`, `unbind` 157 + call signals: 158 + `elementCountCB` 159 + `elementSizeCB` 160 + `dataSizeCB` 161 +/ 162 void setUntypedData( in void[] data_arr, size_t element_size, Usage mem=Usage.DYNAMIC_DRAW ) 163 { 164 auto size = data_arr.length; 165 if( !size ) throw new GLObjException( "set buffer data size is 0" ); 166 167 bind(); 168 checkGLCall!glBufferData( gltype, size, data_arr.ptr, cast(GLenum)mem ); 169 170 element_count = cast(uint)( data_arr.length / element_size ); 171 data_size = size; 172 173 elementCountCB( cast(uint)element_count ); 174 dataSizeCB( data_size ); 175 elementSizeCB( cast(uint)element_size ); 176 177 debug logger.trace( "[%s]: size [%d], element size [%d], usage [%s]", 178 type, size, element_size, mem ); 179 180 unbind(); 181 } 182 183 /// `setUntypedData( data_arr, E.sizeof, mem )` 184 void setData(E)( in E[] data_arr, Usage mem=Usage.DYNAMIC_DRAW ) 185 { setUntypedData( data_arr, E.sizeof, mem ); } 186 187 /// `bind`, `glBufferSubData`, `unbind` 188 void setSubUntypedData( size_t offset, in void[] data_arr, size_t element_size ) 189 { 190 auto size = data_arr.length; 191 192 if( !size ) throw new GLObjException( "set sub buffer data size is 0" ); 193 if( offset + size > data_size ) 194 throw new GLObjException( "set sub buffer data: offset+size > data_size" ); 195 196 bind(); 197 checkGLCall!glBufferSubData( gltype, offset, size, data_arr.ptr ); 198 199 debug logger.trace( "[%s]: offset [%d], size [%d], element size [%d]", 200 type, offset, size, element_size ); 201 202 unbind(); 203 } 204 205 /// `setSubUntypedData( offset * E.sizeof, data_arr, E.sizeof )` 206 void setSubData(E)( size_t offset, in E[] data_arr ) 207 { setSubUntypedData( offset * E.sizeof, data_arr, E.sizeof ); } 208 209 /// return ubtyped copy of buffer data 210 void[] getUntypedData() 211 { 212 auto mp = mapUntypedData( Access.READ_ONLY ); 213 auto buf = new void[]( data_size ); 214 memcpy( buf.ptr, mp.ptr, data_size ); 215 unmap(); 216 return buf; 217 } 218 219 /// cast untyped copy of buffer data to `E[]` 220 E[] getData(E)() { return cast(E[])getUntypedData(); } 221 222 /// return untyped copy of buffer sub data 223 void[] getSubUntypedData( size_t offset, size_t length ) 224 { 225 auto mp = mapUntypedDataRange( offset, length, Access.READ_ONLY ); 226 auto buf = new void[]( length ); 227 memcpy( buf.ptr, mp.ptr, length ); 228 unmap(); 229 return buf; 230 } 231 232 /// cast untyped copy of buffer sub data to `E[]` 233 E[] getSubData(E)( size_t offset, size_t count ) 234 { return cast(E[])getSubUntypedData( E.sizeof * offset, E.sizeof * count ); } 235 236 @property 237 { 238 const final 239 { 240 /// 241 uint elementCount() { return element_count; } 242 /// 243 size_t dataSize() { return data_size; } 244 245 /// calculated 246 uint elementSize() 247 { return cast(uint)( element_count ? data_size / element_count : 0 ); } 248 } 249 } 250 251 /// `bind`, `glMapBuffer`, `unbind` 252 ArrayData mapUntypedData( Access access=Access.READ_ONLY ) 253 { 254 debug logger.trace( "by access [%s]", access ); 255 bind(); 256 scope(exit) unbind(); 257 return ArrayData( data_size, checkGLCall!glMapBuffer( gltype, cast(GLenum)access ) ); 258 } 259 260 /// `bind`, `glMapBufferRange`, `unbind` 261 ArrayData mapUntypedDataRange( size_t offset, size_t length, Access access=Access.READ_ONLY ) 262 { 263 if( offset + length > data_size ) 264 throw new GLObjException( "map buffer range: offset + length > data_size" ); 265 debug logger.trace( "by access [%s]: offset [%d], length [%d]", access, offset, length ); 266 bind(); 267 scope(exit) unbind(); 268 return ArrayData( length, checkGLCall!glMapBufferRange( gltype, offset, length, cast(GLenum)access ) ); 269 } 270 271 /// 272 AlienArray!E mapData(E)( Access access ) 273 { return getTypedArray!E( mapUntypedData( access ) ); } 274 275 /// 276 AlienArray!E mapDataRange(E)( size_t offset, size_t length, Access access ) 277 { return getTypedArray!E( mapUntypedDataRange( offset * E.sizeof, length * E.sizeof, access ) ); } 278 279 /// `bind`, `glUnmapBuffer`, `unbind` 280 void unmap() 281 { 282 bind(); 283 checkGLCall!glUnmapBuffer( gltype ); 284 debug logger.trace( "pass" ); 285 unbind(); 286 } 287 288 protected: 289 290 override void selfDestroy() 291 { 292 checkGLCall!glDeleteBuffers( 1, &_id ); 293 debug logger.Debug( "pass" ); 294 } 295 } 296 297 /// Vertex Array Object 298 final class GLVAO : DesObject 299 { 300 mixin DES; 301 mixin ClassLogger; 302 303 protected: 304 uint _id; 305 306 int[int] aaset; 307 308 public: 309 /// 310 static nothrow void unbind(){ glBindVertexArray(0); } 311 312 /// `glGenVertexArrays` 313 this() 314 { 315 checkGLCall!glGenVertexArrays( 1, &_id ); 316 logger = new InstanceLogger( this, format( "%d", _id ) ); 317 logger.Debug( "pass" ); 318 } 319 320 /// 321 int[] enabled() const @property { return aaset.keys; } 322 323 nothrow 324 { 325 /// `glBindVertexArray` 326 void bind() 327 { 328 ntCheckGLCall!glBindVertexArray( _id ); 329 debug logger.trace( "pass" ); 330 } 331 332 /// `glEnableVertexAttribArray` 333 void enable( int n ) 334 { 335 debug scope(exit) logger.Debug( "[%d]", n ); 336 if( n < 0 ) return; 337 bind(); 338 ntCheckGLCall!glEnableVertexAttribArray( n ); 339 aaset[n] = n; 340 } 341 342 /// `glDisableVertexAttribArray` 343 void disable( int n ) 344 { 345 debug scope(exit) logger.Debug( "[%d]", n ); 346 if( n < 0 ) return; 347 bind(); 348 ntCheckGLCall!glDisableVertexAttribArray( n ); 349 aaset.remove(n); 350 } 351 } 352 353 protected: 354 355 /// `glDeleteVertexArrays` 356 override void selfDestroy() { glDeleteVertexArrays( 1, &_id ); } 357 358 } 359 360 /// 361 class GLObject : DesObject 362 { 363 mixin DES; 364 mixin ClassLogger; 365 366 protected: 367 /// 368 GLVAO vao; 369 370 final 371 { 372 /// `glVertexAttribPointer` 373 void setAttribPointer( GLBuffer buffer, int index, uint per_element, 374 GLType attype, size_t stride, size_t offset, bool norm=false ) 375 { 376 vao.enable( index ); 377 378 buffer.bind(); 379 checkGLCall!glVertexAttribPointer( index, cast(int)per_element, 380 cast(GLenum)attype, norm, cast(int)stride, cast(void*)offset ); 381 382 logger.Debug( "VAO [%d], buffer [%d], "~ 383 "index [%d], per element [%d][%s]"~ 384 "%s%s", 385 vao._id, buffer.id, 386 index, per_element, attype, 387 stride != 0 ? ntFormat(", stride [%d], offset [%d]", stride, offset ) : "", 388 norm ? ntFormat( ", norm [%s]", norm ) : "" ); 389 390 buffer.unbind(); 391 } 392 393 /// ditto 394 void setAttribPointer( GLBuffer buffer, int index, uint per_element, 395 GLType attype, bool norm=false ) 396 { setAttribPointer( buffer, index, per_element, attype, 0, 0, norm ); } 397 } 398 399 /// override this for any action before draw 400 void preDraw() {} 401 402 /// 403 void drawArrays( DrawMode mode, uint count, uint start=0 ) 404 { 405 vao.bind(); 406 preDraw(); 407 checkGLCall!glDrawArrays( mode, start, count ); 408 debug logger.trace( "mode [%s], count [%d]", mode, count ); 409 } 410 411 /// 412 void drawElements( DrawMode mode, uint count ) 413 { 414 vao.bind(); 415 preDraw(); 416 checkGLCall!glDrawElements( mode, count, GL_UNSIGNED_INT, null ); 417 debug logger.trace( "mode [%s], count [%d]", mode, count ); 418 } 419 420 public: 421 422 /// 423 this() 424 { 425 vao = newEMM!GLVAO; 426 debug checkGL; 427 } 428 429 /// 430 enum DrawMode 431 { 432 POINTS = GL_POINTS, /// `GL_POINTS` 433 LINES = GL_LINES, /// `GL_LINES` 434 LINE_STRIP = GL_LINE_STRIP, /// `GL_LINE_STRIP` 435 LINE_LOOP = GL_LINE_LOOP, /// `GL_LINE_LOOP` 436 TRIANGLES = GL_TRIANGLES, /// `GL_TRIANGLES` 437 TRIANGLE_STRIP = GL_TRIANGLE_STRIP, /// `GL_TRIANGLE_STRIP` 438 TRIANGLE_FAN = GL_TRIANGLE_FAN, /// `GL_TRIANGLE_FAN` 439 LINES_ADJACENCY = GL_LINES_ADJACENCY, /// `GL_LINES_ADJACENCY` 440 LINE_STRIP_ADJACENCY = GL_LINE_STRIP_ADJACENCY, /// `GL_LINE_STRIP_ADJACENCY` 441 TRIANGLES_ADJACENCY = GL_TRIANGLES_ADJACENCY, /// `GL_TRIANGLES_ADJACENCY` 442 TRIANGLE_STRIP_ADJACENCY = GL_TRIANGLE_STRIP_ADJACENCY /// `GL_TRIANGLE_STRIP_ADJACENCY` 443 } 444 }