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.frame; 26 27 import derelict.opengl3.gl3; 28 29 import des.math.linear; 30 31 import des.gl.base.texture; 32 import des.gl.base.type; 33 34 import des.il.image; 35 36 /// 37 class GLFBOException : DesGLException 38 { 39 /// 40 this( string msg, string file=__FILE__, size_t line=__LINE__ ) @safe pure nothrow 41 { super( msg, file, line ); } 42 } 43 44 /// 45 class GLRenderBuffer : DesObject 46 { 47 mixin DES; 48 mixin ClassLogger; 49 50 protected: 51 uint _id; 52 Format _format; 53 54 public: 55 56 /// 57 enum Format 58 { 59 R8 = GL_R8, /// `GL_R8` 60 R8UI = GL_R8UI, /// `GL_R8UI` 61 R8I = GL_R8I, /// `GL_R8I` 62 R16UI = GL_R16UI, /// `GL_R16UI` 63 R16I = GL_R16I, /// `GL_R16I` 64 R32UI = GL_R32UI, /// `GL_R32UI` 65 R32I = GL_R32I, /// `GL_R32I` 66 RG8 = GL_RG8, /// `GL_RG8` 67 RG8UI = GL_RG8UI, /// `GL_RG8UI` 68 RG8I = GL_RG8I, /// `GL_RG8I` 69 RG16UI = GL_RG16UI, /// `GL_RG16UI` 70 RG16I = GL_RG16I, /// `GL_RG16I` 71 RG32UI = GL_RG32UI, /// `GL_RG32UI` 72 RG32I = GL_RG32I, /// `GL_RG32I` 73 RGB8 = GL_RGB8, /// `GL_RGB8` 74 RGBA8 = GL_RGBA8, /// `GL_RGBA8` 75 SRGB8_ALPHA8 = GL_SRGB8_ALPHA8, /// `GL_SRGB8_ALPHA8` 76 RGB5_A1 = GL_RGB5_A1, /// `GL_RGB5_A1` 77 RGBA4 = GL_RGBA4, /// `GL_RGBA4` 78 RGB10_A2 = GL_RGB10_A2, /// `GL_RGB10_A2` 79 RGBA8UI = GL_RGBA8UI, /// `GL_RGBA8UI` 80 RGBA8I = GL_RGBA8I, /// `GL_RGBA8I` 81 RGB10_A2UI = GL_RGB10_A2UI, /// `GL_RGB10_A2UI` 82 RGBA16UI = GL_RGBA16UI, /// `GL_RGBA16UI` 83 RGBA16I = GL_RGBA16I, /// `GL_RGBA16I` 84 RGBA32I = GL_RGBA32I, /// `GL_RGBA32I` 85 RGBA32UI = GL_RGBA32UI, /// `GL_RGBA32UI` 86 DEPTH_COMPONENT16 = GL_DEPTH_COMPONENT16, /// `GL_DEPTH_COMPONENT16` 87 DEPTH_COMPONENT24 = GL_DEPTH_COMPONENT24, /// `GL_DEPTH_COMPONENT24` 88 DEPTH_COMPONENT32F = GL_DEPTH_COMPONENT32F, /// `GL_DEPTH_COMPONENT32F` 89 DEPTH24_STENCIL8 = GL_DEPTH24_STENCIL8, /// `GL_DEPTH24_STENCIL8` 90 DEPTH32F_STENCIL8 = GL_DEPTH32F_STENCIL8, /// `GL_DEPTH32F_STENCIL8` 91 STENCIL_INDEX8 = GL_STENCIL_INDEX8 /// `GL_STENCIL_INDEX8` 92 } 93 94 /// glGenRenderbuffers 95 this() 96 { 97 checkGLCall!glGenRenderbuffers( 1, &_id ); 98 logger = new InstanceLogger( this, std..string.format( "%d", _id ) ); 99 logger.Debug( "pass" ); 100 } 101 102 final pure const @property 103 { 104 /// 105 uint id() { return _id; } 106 107 /// 108 Format format() { return _format; } 109 } 110 111 /// `glBindRenderbuffer( GL_RENDERBUFFER, id )` 112 void bind() 113 { 114 checkGLCall!glBindRenderbuffer( GL_RENDERBUFFER, _id ); 115 debug logger.trace( "[%d]", id ); 116 } 117 118 /// `glBindRenderbuffer( GL_RENDERBUFFER, 0 )` 119 void unbind() 120 { 121 checkGLCall!glBindRenderbuffer( GL_RENDERBUFFER, 0 ); 122 debug logger.trace( "call from [%d]", _id ); 123 } 124 125 /// glRenderbufferStorage 126 void storage( in uivec2 sz, Format fmt ) 127 in 128 { 129 assert( sz[0] < GL_MAX_RENDERBUFFER_SIZE ); 130 assert( sz[1] < GL_MAX_RENDERBUFFER_SIZE ); 131 } 132 body 133 { 134 bind(); 135 _format = fmt; 136 checkGLCall!glRenderbufferStorage( GL_RENDERBUFFER, cast(GLenum)fmt, sz[0], sz[1] ); 137 unbind(); 138 debug logger.Debug( "size [%d,%d], format [%s]", sz[0], sz[1], fmt ); 139 } 140 141 /// ditto 142 void storage(T)( in T sz, Format fmt ) 143 if( isCompatibleVector!(2,uint,T) ) 144 in 145 { 146 assert( sz[0] >= 0 ); 147 assert( sz[1] >= 0 ); 148 } 149 body { storage( uivec2(sz), fmt ); } 150 151 /// set storage with new size and old format 152 void resize( in uivec2 sz ) { storage( sz, _format ); } 153 154 /// ditto 155 void resize(T)( in T sz ) 156 if( isCompatibleVector!(2,uint,T) ) 157 { resize( uivec2(sz) ); } 158 159 protected: 160 161 override void selfDestroy() 162 { 163 unbind(); 164 checkGLCall!glDeleteRenderbuffers( 1, &_id ); 165 logger.Debug( "pass" ); 166 } 167 } 168 169 /// 170 class GLFrameBuffer : DesObject 171 { 172 mixin DES; 173 mixin ClassLogger; 174 175 protected: 176 uint _id; 177 static uint[] id_stack; 178 179 public: 180 181 // TODO: not work with gl constant 182 //mixin( getAttachmentEnumString!GL_MAX_COLOR_ATTACHMENTS ); 183 mixin( getAttachmentEnumString!1 ); 184 185 /// glGenFramebuffers 186 this() 187 { 188 if( id_stack.length == 0 ) id_stack ~= 0; 189 190 checkGLCall!glGenFramebuffers( 1, &_id ); 191 logger = new InstanceLogger( this, format( "%d", _id ) ); 192 logger.Debug( "pass" ); 193 } 194 195 final pure const @property 196 { 197 /// 198 uint id() { return _id; } 199 } 200 201 final nothrow 202 { 203 /// glBindFramebuffer add id to stack 204 void bind() 205 { 206 if( id_stack[$-1] == _id ) return; 207 ntCheckGLCall!glBindFramebuffer( GL_FRAMEBUFFER, _id ); 208 id_stack ~= _id; 209 debug logger.trace( "pass" ); 210 } 211 212 /// pop from stack old frame buffer id and glBindFramebuffer with it 213 void unbind() 214 { 215 if( id_stack.length < 2 && id_stack[$-1] != _id ) return; 216 id_stack.length--; 217 ntCheckGLCall!glBindFramebuffer( GL_FRAMEBUFFER, id_stack[$-1] ); 218 debug logger.trace( "bind [%d]", _id, id_stack[$-1] ); 219 } 220 } 221 222 /// 223 void setAttachment(T)( T obj, Attachment att ) 224 if( is( T : GLTexture ) || is( T : GLRenderBuffer ) ) 225 { 226 static if( is( T : GLTexture ) ) 227 texture( obj, att ); 228 else static if( is( T : GLRenderBuffer ) ) 229 renderBuffer( obj, att ); 230 } 231 232 /// set texture attachment 233 void texture( GLTexture tex, Attachment att ) 234 in { assert( isValidTextureTarget(tex.target) ); } 235 body { texture( tex, att, tex.target ); } 236 237 /// ditto 238 void texture( GLTexture tex, Attachment att, GLTexture.Target trg ) 239 in { assert( isValidTextureTarget(trg) ); } body 240 { 241 bind(); scope(exit) unbind(); 242 243 if( trg == tex.Target.T1D ) 244 checkGLCall!glFramebufferTexture1D( GL_FRAMEBUFFER, cast(GLenum)att, 245 cast(GLenum)trg, tex.id, 0 ); 246 else if( tex.target == tex.Target.T3D ) 247 checkGLCall!glFramebufferTexture3D( GL_FRAMEBUFFER, cast(GLenum)att, 248 cast(GLenum)trg, tex.id, 0, 0 ); 249 else 250 checkGLCall!glFramebufferTexture2D( GL_FRAMEBUFFER, cast(GLenum)att, 251 cast(GLenum)trg, tex.id, 0 ); 252 253 logger.Debug( "[%s] as [%s]", tex.id, att ); 254 } 255 256 /// set render buffer attachment 257 void renderBuffer( GLRenderBuffer rbo, Attachment att ) 258 { 259 bind(); scope(exit) unbind(); 260 261 checkGLCall!glFramebufferRenderbuffer( GL_FRAMEBUFFER, cast(GLenum)att, 262 GL_RENDERBUFFER, rbo.id ); 263 264 logger.Debug( "[%d] as [%s]", rbo.id, att ); 265 } 266 267 /// glCheckFramebufferStatus 268 void check() 269 { 270 bind(); scope(exit) unbind(); 271 auto status = checkGLCall!glCheckFramebufferStatus( GL_FRAMEBUFFER ); 272 import std.string; 273 if( status != GL_FRAMEBUFFER_COMPLETE ) 274 throw new GLFBOException( format( "status isn't GL_FRAMEBUFFER_COMPLETE, it's %#x", status ) ); 275 } 276 277 protected: 278 override void selfDestroy() 279 { 280 unbind(); 281 checkGLCall!glDeleteFramebuffers( 1, &_id ); 282 logger.Debug( "pass" ); 283 } 284 285 static string getAttachmentEnumString(size_t COLOR_ATTACHMENT_COUNT)() @property 286 { 287 import std.string; 288 string[] ret; 289 290 ret ~= ` 291 enum Attachment 292 { 293 `; 294 295 foreach( i; 0 .. COLOR_ATTACHMENT_COUNT ) 296 ret ~= format( "COLOR%1d = GL_COLOR_ATTACHMENT%1d,", i, i ); 297 298 ret ~= "DEPTH = GL_DEPTH_ATTACHMENT,"; 299 ret ~= "STENCIL = GL_STENCIL_ATTACHMENT,"; 300 ret ~= "DEPTH_STENCIL = GL_DEPTH_STENCIL_ATTACHMENT,"; 301 302 ret ~= `}`; 303 304 return ret.join("\n"); 305 } 306 307 bool isValidTextureTarget( GLTexture.Target trg ) 308 { 309 switch(trg) 310 { 311 case GLTexture.Target.T1D: 312 case GLTexture.Target.T2D: 313 case GLTexture.Target.RECTANGLE: 314 case GLTexture.Target.T3D: 315 case GLTexture.Target.CUBE_MAP_POSITIVE_X: 316 case GLTexture.Target.CUBE_MAP_NEGATIVE_X: 317 case GLTexture.Target.CUBE_MAP_POSITIVE_Y: 318 case GLTexture.Target.CUBE_MAP_NEGATIVE_Y: 319 case GLTexture.Target.CUBE_MAP_POSITIVE_Z: 320 case GLTexture.Target.CUBE_MAP_NEGATIVE_Z: 321 return true; 322 default: return false; 323 } 324 } 325 }