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 /// 34 class GLObjException : DesGLException 35 { 36 /// 37 this( string msg, string file=__FILE__, size_t line=__LINE__ ) @safe pure nothrow 38 { super( msg, file, line ); } 39 } 40 41 /// 42 class GLBuffer : DesObject 43 { 44 mixin DES; 45 mixin ClassLogger; 46 47 protected: 48 uint _id; 49 Target type; 50 51 /// 52 size_t data_size; 53 /// 54 size_t element_count; 55 56 /// 57 GLenum gltype() const nothrow @property { return cast(GLenum)type; } 58 59 public: 60 61 /// 62 enum Usage 63 { 64 STREAM_DRAW = GL_STREAM_DRAW, /// `GL_STREAM_DRAW` 65 STREAM_READ = GL_STREAM_READ, /// `GL_STREAM_READ` 66 STREAM_COPY = GL_STREAM_COPY, /// `GL_STREAM_COPY` 67 STATIC_DRAW = GL_STATIC_DRAW, /// `GL_STATIC_DRAW` 68 STATIC_READ = GL_STATIC_READ, /// `GL_STATIC_READ` 69 STATIC_COPY = GL_STATIC_COPY, /// `GL_STATIC_COPY` 70 DYNAMIC_DRAW = GL_DYNAMIC_DRAW, /// `GL_DYNAMIC_DRAW` 71 DYNAMIC_READ = GL_DYNAMIC_READ, /// `GL_DYNAMIC_READ` 72 DYNAMIC_COPY = GL_DYNAMIC_COPY /// `GL_DYNAMIC_COPY` 73 } 74 75 /// 76 enum Access 77 { 78 READ_ONLY = GL_READ_ONLY, /// `GL_READ_ONLY` 79 WRITE_ONLY = GL_WRITE_ONLY, /// `GL_WRITE_ONLY` 80 READ_WRITE = GL_READ_WRITE /// `GL_READ_WRITE` 81 } 82 83 /// 84 enum Target 85 { 86 ARRAY_BUFFER = GL_ARRAY_BUFFER, /// `GL_ARRAY_BUFFER` 87 ATOMIC_COUNTER_BUFFER = GL_ATOMIC_COUNTER_BUFFER, /// `GL_ATOMIC_COUNTER_BUFFER` 88 COPY_READ_BUFFER = GL_COPY_READ_BUFFER, /// `GL_COPY_READ_BUFFER` 89 COPY_WRITE_BUFFER = GL_COPY_WRITE_BUFFER, /// `GL_COPY_WRITE_BUFFER` 90 DRAW_INDIRECT_BUFFER = GL_DRAW_INDIRECT_BUFFER, /// `GL_DRAW_INDIRECT_BUFFER` 91 DISPATCH_INDIRECT_BUFFER = GL_DISPATCH_INDIRECT_BUFFER, /// `GL_DISPATCH_INDIRECT_BUFFER` 92 ELEMENT_ARRAY_BUFFER = GL_ELEMENT_ARRAY_BUFFER, /// `GL_ELEMENT_ARRAY_BUFFER` 93 PIXEL_PACK_BUFFER = GL_PIXEL_PACK_BUFFER, /// `GL_PIXEL_PACK_BUFFER` 94 PIXEL_UNPACK_BUFFER = GL_PIXEL_UNPACK_BUFFER, /// `GL_PIXEL_UNPACK_BUFFER` 95 SHADER_STORAGE_BUFFER = GL_SHADER_STORAGE_BUFFER, /// `GL_SHADER_STORAGE_BUFFER` 96 TEXTURE_BUFFER = GL_TEXTURE_BUFFER, /// `GL_TEXTURE_BUFFER` 97 TRANSFORM_FEEDBACK_BUFFER = GL_TRANSFORM_FEEDBACK_BUFFER, /// `GL_TRANSFORM_FEEDBACK_BUFFER` 98 UNIFORM_BUFFER = GL_UNIFORM_BUFFER /// `GL_UNIFORM_BUFFER` 99 } 100 101 /// glBindBuffer( trg, 0 ) 102 static nothrow void unbind( Target trg ) 103 { glBindBuffer( cast(GLenum)trg, 0 ); } 104 105 /// 106 this( Target tp=Target.ARRAY_BUFFER ) 107 { 108 checkGLCall!glGenBuffers( 1, &_id ); 109 type = tp; 110 111 logger = new InstanceLogger( this, format( "%d", _id ) ); 112 113 logger.Debug( "with type [%s]", type ); 114 } 115 116 final 117 { 118 /// glBindBuffer 119 void bind() 120 { 121 checkGLCall!glBindBuffer( gltype, _id ); 122 debug logger.trace( "with type [%s]", type ); 123 } 124 125 /// glBindBuffer with self target to 0 126 void unbind() 127 { 128 checkGLCall!glBindBuffer( gltype, 0 ); 129 debug logger.trace( "type [%s]", type ); 130 } 131 @property uint id() const { return _id; } 132 } 133 134 /++ calls when element count changed (in setUntypedData) 135 + 136 + See_Also: [setUntypedData](des/gl/base/object/GLBuffer.setUntypedData.html) 137 +/ 138 Signal!size_t elementCountCB; 139 140 /++ calls when element size changed (in setUntypedData) 141 + 142 + See_Also: [setUntypedData](des/gl/base/object/GLBuffer.setUntypedData.html) 143 +/ 144 Signal!size_t elementSizeCB; 145 146 /++ calls when data size changed (in setUntypedData) 147 + 148 + See_Also: [setUntypedData](des/gl/base/object/GLBuffer.setUntypedData.html) 149 +/ 150 Signal!size_t dataSizeCB; 151 152 /++ glBufferData, call signals 153 + 154 + See_Also: 155 + * [Signal!size_t elementCountCB](des/gl/base/object/GLBuffer.elementCountCB.html) 156 + * [Signal!size_t elementSizeCB](des/gl/base/object/GLBuffer.elementSizeCB.html) 157 + * [Signal!size_t dataSizeCB](des/gl/base/object/GLBuffer.dataSizeCB.html) 158 +/ 159 void setUntypedData( in void[] data_arr, size_t element_size, Usage mem=Usage.DYNAMIC_DRAW ) 160 { 161 auto size = data_arr.length; 162 if( !size ) throw new GLObjException( "set buffer data size is 0" ); 163 164 bind(); 165 checkGLCall!glBufferData( gltype, size, data_arr.ptr, cast(GLenum)mem ); 166 unbind(); 167 168 element_count = data_arr.length / element_size; 169 data_size = size; 170 171 elementCountCB( element_count ); 172 dataSizeCB( data_size ); 173 elementSizeCB( element_size ); 174 175 debug logger.trace( "[%s]: size [%d], element size [%d], usage [%s]", 176 type, size, element_size, mem ); 177 } 178 179 /// `setUntypedData( data_arr, E.sizeof, mem )` 180 void setData(E)( in E[] data_arr, Usage mem=Usage.DYNAMIC_DRAW ) 181 { setUntypedData( data_arr, E.sizeof, mem ); } 182 183 /// `glBufferSubData` 184 void setSubUntypedData( size_t offset, in void[] data_arr, size_t element_size ) 185 { 186 auto size = data_arr.length; 187 188 if( !size ) throw new GLObjException( "set sub buffer data size is 0" ); 189 if( offset + size > data_size ) 190 throw new GLObjException( "set sub buffer data: offset+size > data_size" ); 191 192 bind(); 193 checkGLCall!glBufferSubData( gltype, offset, size, data_arr.ptr ); 194 unbind(); 195 196 debug logger.trace( "[%s]: offset [%d], size [%d], element size [%d]", 197 type, offset, size, element_size ); 198 } 199 200 /// `setSubUntypedData( offset * E.sizeof, data_arr, E.sizeof )` 201 void setSubData(E)( size_t offset, in E[] data_arr ) 202 { setSubUntypedData( offset * E.sizeof, data_arr, E.sizeof ); } 203 204 /// return ubtyped copy of buffer data 205 void[] getUntypedData() 206 { 207 bind(); 208 auto mp = map( Access.READ_ONLY ); 209 auto buf = new void[]( data_size ); 210 memcpy( buf.ptr, mp, data_size ); 211 unmap(); 212 unbind(); 213 return buf; 214 } 215 216 /// cast untyped copy of buffer data to `E[]` 217 E[] getData(E)() { return cast(E[])getUntypedData(); } 218 219 /// return untyped copy of buffer sub data 220 void[] getSubUntypedData( size_t offset, size_t length ) 221 { 222 bind(); 223 auto mp = mapRange( offset, length, Access.READ_ONLY ); 224 auto buf = new void[]( length ); 225 memcpy( buf.ptr, mp, length ); 226 unmap(); 227 unbind(); 228 return buf; 229 } 230 231 /// cast untyped copy of buffer sub data to `E[]` 232 E[] getSubData(E)( size_t offset, size_t count ) 233 { return cast(E[])getSubUntypedData( E.sizeof * offset, E.sizeof * count ); } 234 235 @property 236 { 237 const final 238 { 239 /// 240 size_t elementCount() { return element_count; } 241 /// 242 size_t dataSize() { return data_size; } 243 244 /// calculated 245 size_t elementSize() 246 { return element_count ? data_size / element_count : 0; } 247 } 248 } 249 250 /// glMapBuffer 251 void* map( Access access=Access.READ_ONLY ) 252 { 253 debug logger.trace( "by access [%s]", access ); 254 return checkGLCall!glMapBuffer( gltype, cast(GLenum)access ); 255 } 256 257 /// glMapBufferRange 258 void* mapRange( size_t offset, size_t length, Access access=Access.READ_ONLY ) 259 { 260 if( offset + length > data_size ) 261 throw new GLObjException( "map buffer range: offset + length > data_size" ); 262 debug logger.trace( "by access [%s]: offset [%d], length [%d]", access, offset, length ); 263 return checkGLCall!glMapBufferRange( gltype, offset, length, cast(GLenum)access ); 264 } 265 266 /// glUnmapBuffer 267 void unmap() 268 { 269 checkGLCall!glUnmapBuffer( gltype ); 270 debug logger.trace( "pass" ); 271 } 272 273 protected: 274 275 override void selfDestroy() 276 { 277 checkGLCall!glDeleteBuffers( 1, &_id ); 278 debug logger.Debug( "pass" ); 279 } 280 } 281 282 /// Vertex Array Object 283 final class GLVAO : DesObject 284 { 285 mixin DES; 286 mixin ClassLogger; 287 288 protected: 289 uint _id; 290 291 public: 292 /// 293 static nothrow void unbind(){ glBindVertexArray(0); } 294 295 /// glGenVertexArrays 296 this() 297 { 298 checkGLCall!glGenVertexArrays( 1, &_id ); 299 logger = new InstanceLogger( this, format( "%d", _id ) ); 300 logger.Debug( "pass" ); 301 } 302 303 nothrow 304 { 305 /// glBindVertexArray 306 void bind() 307 { 308 ntCheckGLCall!glBindVertexArray( _id ); 309 debug logger.trace( "pass" ); 310 } 311 312 /// glEnableVertexAttribArray 313 void enable( int n ) 314 { 315 debug scope(exit) logger.Debug( "[%d]", n ); 316 if( n < 0 ) return; 317 bind(); 318 ntCheckGLCall!glEnableVertexAttribArray( n ); 319 } 320 321 /// glDisableVertexAttribArray 322 void disable( int n ) 323 { 324 debug scope(exit) logger.Debug( "[%d]", n ); 325 if( n < 0 ) return; 326 bind(); 327 ntCheckGLCall!glDisableVertexAttribArray( n ); 328 } 329 } 330 331 protected: 332 333 /// glDeleteVertexArrays 334 override void selfDestroy() { glDeleteVertexArrays( 1, &_id ); } 335 336 } 337 338 /// 339 class GLObject : DesObject 340 { 341 mixin DES; 342 mixin ClassLogger; 343 344 protected: 345 /// 346 GLVAO vao; 347 348 final 349 { 350 351 /// glVertexAttribPointer 352 void setAttribPointer( GLBuffer buffer, int index, uint per_element, 353 GLType attype, size_t stride, size_t offset, bool norm=false ) 354 { 355 vao.enable( index ); 356 357 buffer.bind(); 358 checkGLCall!glVertexAttribPointer( index, cast(int)per_element, 359 cast(GLenum)attype, norm, cast(int)stride, cast(void*)offset ); 360 buffer.unbind(); 361 362 logger.Debug( "VAO [%d], buffer [%d], "~ 363 "index [%d], per element [%d][%s]"~ 364 "%s%s", 365 vao._id, buffer.id, 366 index, per_element, attype, 367 stride != 0 ? ntFormat(", stride [%d], offset [%d]", stride, offset ) : "", 368 norm ? ntFormat( ", norm [%s]", norm ) : "" ); 369 } 370 371 /// ditto 372 void setAttribPointer( GLBuffer buffer, int index, uint per_element, 373 GLType attype, bool norm=false ) 374 { setAttribPointer( buffer, index, per_element, attype, 0, 0, norm ); } 375 } 376 377 public: 378 379 /// 380 this() 381 { 382 vao = newEMM!GLVAO; 383 debug checkGL; 384 } 385 }