1 module des.gl.buffer; 2 3 import des.gl.general; 4 5 /// 6 class GLBufferException : DesGLException 7 { 8 /// 9 this( string msg, string file=__FILE__, size_t line=__LINE__ ) @safe pure nothrow 10 { super( msg, file, line ); } 11 } 12 13 /// 14 abstract class GLBuffer : GLObject!"Buffer" 15 { 16 protected: 17 18 /// 19 size_t data_size; 20 /// 21 uint element_count; 22 23 public: 24 25 /// 26 enum Usage 27 { 28 STREAM_DRAW = GL_STREAM_DRAW, /// `GL_STREAM_DRAW` 29 STREAM_READ = GL_STREAM_READ, /// `GL_STREAM_READ` 30 STREAM_COPY = GL_STREAM_COPY, /// `GL_STREAM_COPY` 31 STATIC_DRAW = GL_STATIC_DRAW, /// `GL_STATIC_DRAW` 32 STATIC_READ = GL_STATIC_READ, /// `GL_STATIC_READ` 33 STATIC_COPY = GL_STATIC_COPY, /// `GL_STATIC_COPY` 34 DYNAMIC_DRAW = GL_DYNAMIC_DRAW, /// `GL_DYNAMIC_DRAW` 35 DYNAMIC_READ = GL_DYNAMIC_READ, /// `GL_DYNAMIC_READ` 36 DYNAMIC_COPY = GL_DYNAMIC_COPY /// `GL_DYNAMIC_COPY` 37 } 38 39 /// 40 enum MapBits 41 { 42 READ = GL_MAP_READ_BIT, /// `GL_MAP_READ_BIT` 43 WRITE = GL_MAP_WRITE_BIT, /// `GL_MAP_WRITE_BIT` 44 45 PERSISTENT = GL_MAP_PERSISTENT_BIT, /// `GL_MAP_PERSISTENT_BIT` 46 COHERENT = GL_MAP_COHERENT_BIT, /// `GL_MAP_COHERENT_BIT` 47 48 INVALIDATE_RANGE = GL_MAP_INVALIDATE_RANGE_BIT, /// `GL_MAP_INVALIDATE_RANGE_BIT` 49 INVALIDATE_BUFFER = GL_MAP_INVALIDATE_BUFFER_BIT, /// `GL_MAP_INVALIDATE_BUFFER_BIT` 50 FLUSH_EXPLICIT = GL_MAP_FLUSH_EXPLICIT_BIT, /// `GL_MAP_FLUSH_EXPLICIT_BIT` 51 UNSYNCHRONIZED = GL_MAP_UNSYNCHRONIZED_BIT /// `GL_MAP_UNSYNCHRONIZED_BIT` 52 } 53 54 /// 55 enum StorageBits 56 { 57 READ = GL_MAP_READ_BIT, /// `GL_MAP_READ_BIT` 58 WRITE = GL_MAP_WRITE_BIT, /// `GL_MAP_WRITE_BIT` 59 60 PERSISTENT = GL_MAP_PERSISTENT_BIT, /// `GL_MAP_PERSISTENT_BIT` 61 COHERENT = GL_MAP_COHERENT_BIT, /// `GL_MAP_COHERENT_BIT` 62 63 DYNAMIC = GL_DYNAMIC_STORAGE_BIT, /// `GL_DYNAMIC_STORAGE_BIT` 64 CLIENT = GL_CLIENT_STORAGE_BIT, /// `GL_CLIENT_STORAGE_BIT` 65 } 66 67 /// 68 this( GLenum trg ) 69 { 70 super( trg ); 71 logger.Debug( "pass" ); 72 } 73 74 @property 75 { 76 const final 77 { 78 /// 79 uint elementCount() { return element_count; } 80 /// 81 size_t dataSize() { return data_size; } 82 83 /// calculated 84 uint elementSize() 85 { return cast(uint)( element_count ? data_size / element_count : 0 ); } 86 87 bool isMapped() { return is_mapped; } 88 } 89 } 90 91 /// `bind`, `glUnmapBuffer`, `unbind` 92 void unmap() 93 { 94 bind(); 95 checkGLCall!glUnmapBuffer( target ); 96 debug logger.trace( "pass" ); 97 unbind(); 98 is_mapped = false; 99 } 100 101 /++ provides: 102 + * `void setRaw( in void[] arr, size_t esize, Usage mem=Usage.STATIC_DRAW )` 103 + * `void setSubRaw( size_t offset, in void[] arr )` 104 + * `void storageRaw( in void[] arr, size_t esize, StorageBits[] bits )` 105 +/ 106 mixin template RawSet() 107 { 108 public 109 { 110 void setRaw( in void[] arr, size_t esize, Usage mem=Usage.STATIC_DRAW ) 111 { setRawData( arr, esize, mem ); } 112 113 void setSubRaw( size_t offset, in void[] arr ) 114 { setRawSubData( offset, arr ); } 115 116 void storageRaw( in void[] arr, size_t esize, StorageBits[] bits ) 117 { setStorage( arr, esize, bits ); } 118 119 void allocRaw( size_t count, size_t esize, StorageBits[] bits ) 120 { allocStorage( count, esize, bits ); } 121 } 122 } 123 124 mixin template RawGet() 125 { 126 public void[] getRaw( size_t offset=0, size_t size=0 ) 127 { return getRawSubData( offset, size ); } 128 } 129 130 mixin template RawMap() 131 { 132 public 133 { 134 void[] mapRaw( in MapBits[] bits ) 135 { return mapRawDataRange( 0, data_size, bits ); } 136 137 void[] mapRangeRaw( size_t offset, size_t length, in MapBits[] bits ) 138 { return mapRawDataRange( offset, length, bits ); } 139 140 void flushRangeRaw( size_t offset, size_t length, size_t esize=1 ) 141 { flushRawMappedRange( offset, length, esize ); } 142 } 143 } 144 145 mixin template RawAccess() 146 { 147 mixin RawSet; 148 mixin RawGet; 149 mixin RawMap; 150 } 151 152 mixin template TemplateSet() 153 { 154 public 155 { 156 void setData(E)( in E[] arr, Usage mem=Usage.STATIC_DRAW ) 157 { setRawData( arr, E.sizeof, mem ); } 158 159 void setSubData(E)( size_t offset, in E[] arr ) 160 { 161 auto es = E.sizeof; 162 if( es != elementSize ) 163 logger.error( "mismatch element size" ); 164 setRawSubData( offset * es, arr ); 165 } 166 167 void storageData(E)( in E[] arr, StorageBits[] bits ) 168 { setStorage( arr, E.sizeof, bits ); } 169 170 void allocData(E)( size_t count, StorageBits[] bits ) 171 { allocStorage( count, E.sizeof, bits ); } 172 } 173 } 174 175 mixin template TemplateGet() 176 { 177 public E[] getData(E)( size_t offset=0, size_t count=0 ) 178 { return cast(E[])getRawSubData( offset * E.sizeof, count * E.sizeof ); } 179 } 180 181 mixin template TemplateMap() 182 { 183 public 184 { 185 E[] mapData(E)( in MapBits[] bits ) 186 { return cast(E[])mapRawDataRange( 0, data_size, bits ); } 187 188 E[] mapRangeData(E)( size_t offset, size_t length, in MapBits[] bits ) 189 { 190 auto es = E.sizeof; 191 return cast(E[])mapRawDataRange( offset*es, length*es, bits ); 192 } 193 194 void flushRangeData(E)( size_t offset, size_t length ) 195 { 196 auto es = E.sizeof; 197 flushRawMappedRange( offset*es, length*es, es ); 198 } 199 } 200 } 201 202 mixin template TemplateAccess() 203 { 204 mixin TemplateSet; 205 mixin TemplateGet; 206 mixin TemplateMap; 207 } 208 209 mixin template TypeSet(T,string postFix="") 210 { 211 mixin( format(q{ 212 public 213 { 214 static if( !is(typeof(setType%1$s)) ) 215 protected void setType%1$s() {} 216 217 void set( in T[] arr, Usage mem=Usage.STATIC_DRAW ) 218 { 219 setRawData( arr, T.sizeof, mem ); 220 setType%1$s(); 221 } 222 223 void setSub( size_t offset, in T[] arr ) 224 { 225 auto es = T.sizeof; 226 if( es != elementSize ) 227 logger.error( "mismatch element size" ); 228 setRawSubData( offset * es, arr ); 229 setType%1$s(); 230 } 231 232 void storage( in T[] arr, in StorageBits[] bits ) 233 { 234 setStorage( arr, T.sizeof, bits ); 235 setType%1$s(); 236 } 237 238 void alloc%1$s( size_t count, in StorageBits[] bits ) 239 { 240 allocStorage( count * T.sizeof, T.sizeof, bits ); 241 setType%1$s(); 242 } 243 } 244 }, postFix )); 245 } 246 247 mixin template TypeGet(T,string postFix="") 248 { 249 mixin( format(q{ 250 public T[] get%1$s( size_t offset=0, size_t count=0 ) 251 { return cast(T[])getRawSubData( offset * T.sizeof, count * T.sizeof ); } 252 }, postFix )); 253 } 254 255 mixin template TypeMap(T,string postFix="") 256 { 257 mixin( format(q{ 258 public 259 { 260 T[] mapRange%1$s( size_t offset, size_t length, in MapBits[] bits ) 261 { 262 auto es = T.sizeof; 263 return cast(T[])mapRawDataRange( offset * es, length*es, bits ); 264 } 265 266 T[] map%1$s( in MapBits[] bits ) 267 { return cast(T[])mapRawDataRange( 0, data_size, bits ); } 268 269 void flushRange%1$s( size_t offset, size_t length ) 270 { 271 auto es = T.sizeof; 272 flushRawMappedRange( offset*es, length*es, es ); 273 } 274 } 275 }, postFix )); 276 } 277 278 mixin template TypeAccess(T,string postFix="") 279 { 280 mixin TypeSet!(T,postFix); 281 mixin TypeGet!(T,postFix); 282 mixin TypeMap!(T,postFix); 283 } 284 285 protected: 286 287 //===== sets ===== 288 289 /// `bind`, `glBufferData`, `unbind` 290 void setRawData( in void[] data_arr, size_t element_size, Usage mem ) 291 { 292 auto size = data_arr.length; 293 if( !size ) throw new GLBufferException( "set buffer data size is 0" ); 294 295 bind(); scope(exit) unbind(); 296 checkGLCall!glBufferData( target, size, data_arr.ptr, mem ); 297 298 element_count = cast(uint)( data_arr.length / element_size ); 299 data_size = size; 300 301 debug logger.trace( "size [%db : %d by %db], usage [%s]", size, element_count, element_size, mem ); 302 } 303 304 /// `bind`, `glBufferSubData`, `unbind` 305 void setRawSubData( size_t offset, in void[] data_arr ) 306 { 307 auto size = data_arr.length; 308 309 if( !size ) throw new GLBufferException( "set sub buffer data size is 0" ); 310 if( offset + size > data_size ) 311 throw new GLBufferException( "set sub buffer data: offset+size > data_size" ); 312 313 bind(); scope(exit) unbind(); 314 checkGLCall!glBufferSubData( target, offset, size, data_arr.ptr ); 315 316 debug logger.trace( "offset [%d], size [%d]", offset, size ); 317 } 318 319 /// 320 void setStorage( in void[] data, size_t elem_size, in StorageBits[] bits ) 321 { 322 bind(); scope(exit) unbind(); 323 element_count = cast(uint)( data.length / elem_size ); 324 data_size = data.length; 325 checkGLCall!glBufferStorage( target, data.length, data.ptr, packBitMask(bits) ); 326 debug logger.trace( "by bits %s: size [%d : %d x %d]", bits, data_size, element_count, elementSize ); 327 } 328 329 /// 330 void allocStorage( size_t elem_count, size_t elem_size, in StorageBits[] bits ) 331 { 332 bind(); scope(exit) unbind(); 333 element_count = cast(uint)elem_count; 334 data_size = elem_count * elem_size; 335 checkGLCall!glBufferStorage( target, data_size, null, packBitMask(bits) ); 336 debug logger.trace( "by bits %s: size [%d : %d x %d]", bits, data_size, element_count, elementSize ); 337 } 338 339 //===== gets ===== 340 341 /++ return untyped copy of buffer data 342 + 343 + if `size == 0` returns data with `dataSize - offset` length 344 +/ 345 void[] getRawSubData( size_t offset=0, size_t size=0 ) 346 { 347 ptrdiff_t sz = size ? size : cast(ptrdiff_t)dataSize - offset; 348 349 auto buf = new void[]( sz ); 350 bind(); 351 checkGLCall!glGetBufferSubData( target, offset, sz, buf.ptr ); 352 unbind(); 353 354 debug 355 { 356 auto es = elementSize; 357 358 if( offset % es ) logger.warn( "offset is not a multiple of element size" ); 359 if( size % es ) logger.warn( "size is not a multiple of element size" ); 360 361 logger.trace( "[%s]: offset [%d], size [%d], offset in elements [%d], size in elements [%d]", 362 target, offset, sz, offset / es, sz / es ); 363 } 364 365 return buf; 366 } 367 368 //===== maps ===== 369 370 bool is_mapped = false; 371 372 /// `bind`, `glMapBufferRange`, `unbind` 373 void[] mapRawDataRange( size_t offset, size_t length, in MapBits[] bits ) 374 { 375 if( offset + length > data_size ) 376 throw new GLBufferException( "map buffer range: offset + length > data_size" ); 377 if( bits.length == 0 ) 378 throw new GLBufferException( "map buffer range must accept bits" ); 379 380 debug logger.trace( "by bits %s: offset [%d], length [%d]", bits, offset, length ); 381 382 bind(); 383 scope(exit) 384 { 385 unbind(); 386 is_mapped = true; 387 } 388 389 return getTypedArray!void( length, checkGLCall!glMapBufferRange( target, offset, length, packBitMask(bits) ) ); 390 } 391 392 /// 393 void flushRawMappedRange( size_t offset, size_t length, size_t elem_size=1 ) 394 { 395 bind(); scope(exit) unbind(); 396 checkGLCall!glFlushMappedBufferRange( target, offset * elem_size, length * elem_size ); 397 debug logger.trace( "offset [%d], length [%d], elem_size [%d]", offset, length, elem_size ); 398 } 399 } 400 401 /// 402 void bindCopyReadBuffer( GLBuffer buf ) 403 { checkGLCall!glBindBuffer( GL_COPY_READ_BUFFER, buf.id ); } 404 405 /// 406 void unbindCopyReadBuffer() 407 { checkGLCall!glBindBuffer( GL_COPY_READ_BUFFER, 0 ); } 408 409 /// 410 void bindCopyWriteBuffer( GLBuffer buf ) 411 { checkGLCall!glBindBuffer( GL_COPY_WRITE_BUFFER, buf.id ); } 412 413 /// 414 void unbindCopyWriteBuffer() 415 { checkGLCall!glBindBuffer( GL_COPY_WRITE_BUFFER, 0 ); } 416 417 /// 418 void copyBufferSubData( GLBuffer readbuf, GLBuffer writebuf, uint readoffset, uint writeoffset, uint size ) 419 { 420 bindCopyReadBuffer( readbuf ); 421 bindCopyWriteBuffer( writebuf ); 422 checkGLCall!glCopyBufferSubData( GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 423 readoffset, writeoffset, size ); 424 unbindCopyReadBuffer(); 425 unbindCopyWriteBuffer(); 426 } 427 428 /// 429 class GLArrayBuffer : GLBuffer 430 { 431 /// 432 this() { super( GL_ARRAY_BUFFER ); } 433 434 mixin RawAccess; 435 mixin TemplateAccess; 436 } 437 438 // TODO 439 //class GLDispatchIndirectBuffer : GLBuffer 440 //{ 441 // /// 442 // this() { super( GL_DISPATCH_INDIRECT_BUFFER ); } 443 //} 444 445 /// 446 class GLDrawIndirectBuffer : GLBuffer 447 { 448 /// 449 this() { super( GL_DRAW_INDIRECT_BUFFER ); } 450 451 /// 452 struct ArrayCmd 453 { 454 /// 455 uint count; 456 /// 457 uint instanceCount; 458 /// 459 uint first; 460 /// 461 uint baseInstance; 462 } 463 464 /// 465 struct ElementCmd 466 { 467 /// indices count 468 uint count; 469 /// 470 uint instanceCount; 471 /// offset in index array 472 uint firstIndex; 473 /// 474 uint baseVertex; 475 /// not affect to glsl `gl_InstanceID`, only attrib divisor 476 uint baseInstance; 477 } 478 479 mixin TypeAccess!(ArrayCmd,"Array"); 480 mixin TypeAccess!(ElementCmd,"Element"); 481 } 482 483 /// 484 class GLElementArrayBuffer : GLBuffer 485 { 486 protected: 487 488 Type _type; 489 490 void setTypeByte() { _type = Type.UBYTE; } 491 void setTypeShort() { _type = Type.USHORT; } 492 void setTypeInt() { _type = Type.UINT; } 493 494 public: 495 496 /// 497 enum Type : GLType 498 { 499 UBYTE = GLType.UBYTE, /// `GL_UNSIGNED_BYTE` 500 USHORT = GLType.USHORT, /// `GL_UNSIGNED_SHORT` 501 UINT = GLType.UINT, /// `GL_UNSIGNED_INT` 502 } 503 504 final nothrow pure const @nogc @property 505 { 506 GLType type() { return _type; } 507 } 508 509 /// 510 this() { super( GL_ELEMENT_ARRAY_BUFFER ); } 511 512 mixin TypeAccess!(ubyte,"Byte"); 513 mixin TypeAccess!(ushort,"Short"); 514 mixin TypeAccess!(uint,"Int"); 515 } 516 517 // TODO 518 //class GLPixelPackBuffer : GLBuffer 519 //{ 520 // /// 521 // this() { super( GL_PIXEL_PACK_BUFFER ); } 522 //} 523 524 // TODO 525 //class GLPixelUnpackBuffer : GLBuffer 526 //{ 527 // /// 528 // this() { super( GL_PIXEL_UNPACK_BUFFER ); } 529 //} 530 531 // TODO 532 //class GLQueryBuffer : GLBuffer 533 //{ 534 // /// 535 // this() { super( GL_QUERY_BUFFER ); } 536 //} 537 538 // TODO 539 //class GLTextureBuffer : GLBuffer 540 //{ 541 // /// 542 // this() { super( GL_TEXTURE_BUFFER ); } 543 //} 544 545 /// 546 abstract class GLIndexedBuffer : GLBuffer 547 { 548 private: 549 bool target_setted = false; 550 551 public: 552 553 this( GLenum trg ) 554 { 555 super(trg); 556 target_setted = true; 557 } 558 559 invariant() 560 { 561 if( target_setted ) 562 assert( isValidIndexedTarget( _target ) ); 563 } 564 565 /// calls `glBindBufferBase` 566 void bindBase( uint index ) 567 { 568 checkGLCall!glBindBufferBase( target, index, id ); 569 debug logger.trace( "index [%d]", index ); 570 } 571 572 /// calls `glBindBufferRange` 573 void bindRange( uint index, size_t offset, size_t size ) 574 { 575 checkGLCall!glBindBufferRange( target, index, id, offset, size ); 576 debug logger.trace( "index [%d], offset [%d], size [%d]", index, offset, size ); 577 } 578 579 private: 580 581 /// 582 bool isValidIndexedTarget( GLenum trg ) const nothrow 583 { 584 switch( trg ) 585 { 586 case GL_ATOMIC_COUNTER_BUFFER: 587 case GL_TRANSFORM_FEEDBACK_BUFFER: 588 case GL_UNIFORM_BUFFER: 589 case GL_SHADER_STORAGE_BUFFER: 590 return true; 591 default: 592 return false; 593 } 594 } 595 } 596 597 // TODO 598 //class GLAtomicCounterBuffer : GLIndexedBuffer 599 //{ 600 // /// 601 // this() { super( GL_ATOMIC_COUNTER_BUFFER ); } 602 //} 603 604 // TODO 605 //class GLTransformFeedbackBuffer : GLIndexedBuffer 606 //{ 607 // /// 608 // this() { super( GL_TRANSFORM_FEEDBACK_BUFFER ); } 609 //} 610 611 /// 612 class GLUniformBuffer : GLIndexedBuffer 613 { 614 /// 615 this() { super( GL_UNIFORM_BUFFER ); } 616 617 mixin RawAccess; 618 mixin TemplateAccess; 619 } 620 621 /// 622 class GLShaderStorageBuffer : GLIndexedBuffer 623 { 624 /// 625 this() { super( GL_SHADER_STORAGE_BUFFER ); } 626 627 mixin RawAccess; 628 mixin TemplateAccess; 629 }