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 }