1 module gfm.opengl.fbo; 2 3 import std..string; 4 5 import derelict.opengl3.gl3; 6 7 static if( __VERSION__ >= 2067 ) 8 import std.experimental.logger; 9 else 10 import std.historical.logger; 11 12 import gfm.opengl.opengl, 13 gfm.opengl.texture, 14 gfm.opengl.renderbuffer; 15 16 /// OpenGL FrameBuffer Object wrapper. 17 final class GLFBO 18 { 19 public 20 { 21 /// FBO usage. 22 enum Usage 23 { 24 DRAW, /// This FBO will be used for drawing. 25 READ /// This FBO will be used for reading. 26 } 27 28 /// Creates one FBO, with specified usage. OpenGL must have been loaded. 29 /// $(D ARB_framebuffer_object) must be supported. 30 /// Throws: $(D OpenGLException) on error. 31 this(OpenGL gl, Usage usage = Usage.DRAW) 32 { 33 _gl = gl; 34 glGenFramebuffers(1, &_handle); 35 _gl.runtimeCheck(); 36 37 _colors.length = _gl.maxColorAttachments(); 38 for(int i = 0; i < _colors.length; ++i) 39 _colors[i] = new GLFBOAttachment(this, GL_COLOR_ATTACHMENT0 + i); 40 41 _depth = new GLFBOAttachment(this, GL_DEPTH_ATTACHMENT); 42 _stencil = new GLFBOAttachment(this, GL_STENCIL_ATTACHMENT); 43 _depthStencil = new GLFBOAttachment(this, GL_DEPTH_STENCIL_ATTACHMENT); 44 45 setUsage(usage); 46 47 _initialized = true; 48 _isBound = false; 49 } 50 51 auto usage() pure const nothrow @nogc 52 { 53 return _usage; 54 } 55 56 void setUsage(Usage usage) nothrow @nogc 57 { 58 _usage = usage; 59 final switch(usage) 60 { 61 case Usage.DRAW: 62 _target = GL_DRAW_FRAMEBUFFER; 63 break; 64 case Usage.READ: 65 _target = GL_READ_FRAMEBUFFER; 66 } 67 } 68 69 /// Releases the OpenGL FBO resource. 70 ~this() 71 { 72 if (_initialized) 73 { 74 ensureNotInGC("GLFBO"); 75 glBindFramebuffer(_target, _handle); 76 77 // detach all 78 for(int i = 0; i < _colors.length; ++i) 79 _colors[i].close(); 80 81 _depth.close(); 82 _stencil.close(); 83 84 glDeleteFramebuffers(1, &_handle); 85 _initialized = false; 86 } 87 } 88 deprecated("Use .destroy instead") void close(){} 89 90 /// Binds this FBO. 91 /// Throws: $(D OpenGLException) on error. 92 void use() 93 { 94 glBindFramebuffer(_target, _handle); 95 96 _gl.runtimeCheck(); 97 _isBound = true; 98 99 for(int i = 0; i < _colors.length; ++i) 100 _colors[i].updateAttachment(); 101 } 102 103 /// Unbinds this FBO. 104 /// Throws: $(D OpenGLException) on error. 105 void unuse() 106 { 107 _isBound = false; 108 glBindFramebuffer(_target, 0); 109 110 _gl.runtimeCheck(); 111 } 112 113 /// Returns: A FBO color attachment. 114 /// Params: 115 /// i = Index of color attachment. 116 GLFBOAttachment color(int i) 117 { 118 return _colors[i]; 119 } 120 121 /// Returns: FBO depth attachment. 122 GLFBOAttachment depth() 123 { 124 return _depth; 125 } 126 127 /// Returns: FBO stencil attachment. 128 GLFBOAttachment stencil() 129 { 130 return _stencil; 131 } 132 } 133 134 private 135 { 136 OpenGL _gl; 137 GLuint _handle; 138 bool _initialized, _isBound; 139 140 // attachements 141 GLFBOAttachment[] _colors; 142 GLFBOAttachment _depth, _stencil, _depthStencil; 143 144 Usage _usage; 145 GLenum _target; // redundant 146 147 void checkStatus() 148 { 149 GLenum status = void; 150 status = glCheckFramebufferStatus(_target); 151 152 switch(status) 153 { 154 case GL_FRAMEBUFFER_COMPLETE: 155 return; 156 157 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 158 throw new OpenGLException("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); 159 160 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 161 throw new OpenGLException("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"); 162 163 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: 164 throw new OpenGLException("GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT"); 165 166 case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: 167 throw new OpenGLException("GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT"); 168 169 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: 170 throw new OpenGLException("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"); 171 172 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: 173 throw new OpenGLException("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"); 174 175 case GL_FRAMEBUFFER_UNSUPPORTED: 176 throw new OpenGLException("GL_FRAMEBUFFER_UNSUPPORTED"); 177 178 default: throw new OpenGLException("Unknown FBO error"); 179 } 180 } 181 } 182 } 183 184 /// Defines one FBO attachment. 185 final class GLFBOAttachment 186 { 187 public 188 { 189 /// Attaches a 1D texture to the FBO. 190 /// Throws: $(D OpenGLException) on error. 191 void attach(GLTexture1D tex, int level = 0) 192 { 193 _newCall = Call(this, Call.Type.TEXTURE_1D, tex, null, level, 0); 194 updateAttachment(); 195 } 196 197 /// Attaches a 2D texture to the FBO. 198 /// Throws: $(D OpenGLException) on error. 199 void attach(GLTexture2D tex, int level = 0) 200 { 201 _newCall = Call(this, Call.Type.TEXTURE_2D, tex, null, level, 0); 202 updateAttachment(); 203 } 204 205 /// Attaches a 3D texture to the FBO. 206 /// Throws: $(D OpenGLException) on error. 207 void attach(GLTexture3D tex, int layer, int level) 208 { 209 _newCall = Call(this, Call.Type.TEXTURE_3D, tex, null, level, layer); 210 updateAttachment(); 211 } 212 213 /// Attaches a 1D texture array to the FBO. 214 /// Throws: $(D OpenGLException) on error. 215 void attach(GLTexture1DArray tex, int layer) 216 { 217 _newCall = Call(this, Call.Type.TEXTURE_3D, tex, null, 0, layer); 218 updateAttachment(); 219 } 220 221 /// Attaches a 2D texture array to the FBO. 222 /// Throws: $(D OpenGLException) on error. 223 void attach(GLTexture2DArray tex, int layer) 224 { 225 _newCall = Call(this, Call.Type.TEXTURE_3D, tex, null, 0, layer); 226 updateAttachment(); 227 } 228 229 /// Attaches a rectangle texture to the FBO. 230 /// Throws: $(D OpenGLException) on error. 231 void attach(GLTextureRectangle tex) 232 { 233 _newCall = Call(this, Call.Type.TEXTURE_2D, tex, null, 0, 0); 234 updateAttachment(); 235 } 236 237 /// Attaches a multisampled 2D texture to the FBO. 238 /// Throws: $(D OpenGLException) on error. 239 void attach(GLTexture2DMultisample tex) 240 { 241 _newCall = Call(this, Call.Type.TEXTURE_2D, tex, null, 0, 0); 242 updateAttachment(); 243 } 244 245 /// Attaches a multisampled 2D texture array to the FBO. 246 /// Throws: $(D OpenGLException) on error. 247 void attach(GLTexture2DMultisampleArray tex, int layer) 248 { 249 _newCall = Call(this, Call.Type.TEXTURE_3D, tex, null, 0, layer); 250 updateAttachment(); 251 } 252 253 /// Attaches a renderbuffer to the FBO. 254 /// Throws: $(D OpenGLException) on error. 255 void attach(GLRenderBuffer buffer) 256 { 257 _newCall = Call(this, Call.Type.RENDERBUFFER, null, buffer, 0, 0); 258 updateAttachment(); 259 } 260 } 261 262 private 263 { 264 this(GLFBO fbo, GLenum attachment) 265 { 266 _fbo = fbo; 267 _gl = fbo._gl; 268 _attachment = attachment; 269 _lastCall = _newCall = Call(this, Call.Type.DISABLED, null, null, 0, 0); 270 } 271 272 // guaranteed to be called once 273 void close() 274 { 275 _lastCall.detach(); 276 } 277 278 OpenGL _gl; 279 GLFBO _fbo; 280 GLenum _attachment; 281 Call _lastCall; 282 Call _newCall; 283 284 void updateAttachment() 285 { 286 if (_newCall != _lastCall && _fbo._isBound) 287 { 288 try 289 { 290 // trying to detach existing attachment 291 // would that help? 292 _lastCall.detach(); 293 } 294 catch(OpenGLException e) 295 { 296 // ignoring errors here 297 } 298 299 _newCall.attach(); 300 _lastCall = _newCall; 301 } 302 } 303 304 struct Call 305 { 306 public 307 { 308 enum Type 309 { 310 DISABLED, 311 TEXTURE_1D, 312 TEXTURE_2D, 313 TEXTURE_3D, 314 RENDERBUFFER 315 } 316 317 GLFBOAttachment _outer; 318 Type _type; 319 GLTexture _texture; 320 GLRenderBuffer _renderbuffer; 321 GLint _level; 322 GLint _layer; 323 324 void attach() 325 { 326 GLuint textureHandle = _texture !is null ? _texture.handle() : 0; 327 GLuint renderBufferHandle = _renderbuffer !is null ? _renderbuffer.handle() : 0; 328 attachOrDetach(textureHandle, renderBufferHandle); 329 } 330 331 void detach() 332 { 333 attachOrDetach(0, 0); 334 } 335 336 void attachOrDetach(GLuint textureHandle, GLuint renderBufferHandle) 337 { 338 final switch(_type) 339 { 340 case Type.DISABLED: 341 return; // do nothing 342 343 case Type.TEXTURE_1D: 344 glFramebufferTexture1D(_outer._fbo._target, _outer._attachment, _texture._target, textureHandle, _level); 345 break; 346 347 case Type.TEXTURE_2D: 348 glFramebufferTexture2D(_outer._fbo._target, _outer._attachment, _texture._target, textureHandle, _level); 349 break; 350 351 case Type.TEXTURE_3D: 352 glFramebufferTexture3D(_outer._fbo._target, _outer._attachment, _texture._target, textureHandle, _level, _layer); 353 break; 354 355 case Type.RENDERBUFFER: 356 glFramebufferRenderbuffer(_outer._fbo._target, _outer._attachment, GL_RENDERBUFFER, renderBufferHandle); 357 break; 358 } 359 _outer._gl.runtimeCheck(); 360 } 361 } 362 } 363 } 364 }