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 ~this() 70 { 71 close(); 72 } 73 74 /// Releases the OpenGL FBO resource. 75 void close() 76 { 77 if (_initialized) 78 { 79 glBindFramebuffer(_target, _handle); 80 81 // detach all 82 for(int i = 0; i < _colors.length; ++i) 83 _colors[i].close(); 84 85 _depth.close(); 86 _stencil.close(); 87 88 glDeleteFramebuffers(1, &_handle); 89 _initialized = false; 90 } 91 } 92 93 /// Binds this FBO. 94 /// Throws: $(D OpenGLException) on error. 95 void use() 96 { 97 glBindFramebuffer(_target, _handle); 98 99 _gl.runtimeCheck(); 100 _isBound = true; 101 102 for(int i = 0; i < _colors.length; ++i) 103 _colors[i].updateAttachment(); 104 } 105 106 /// Unbinds this FBO. 107 /// Throws: $(D OpenGLException) on error. 108 void unuse() 109 { 110 _isBound = false; 111 glBindFramebuffer(_target, 0); 112 113 _gl.runtimeCheck(); 114 } 115 116 /// Returns: A FBO color attachment. 117 /// Params: 118 /// i = Index of color attachment. 119 GLFBOAttachment color(int i) 120 { 121 return _colors[i]; 122 } 123 124 /// Returns: FBO depth attachment. 125 GLFBOAttachment depth() 126 { 127 return _depth; 128 } 129 130 /// Returns: FBO stencil attachment. 131 GLFBOAttachment stencil() 132 { 133 return _stencil; 134 } 135 } 136 137 private 138 { 139 OpenGL _gl; 140 GLuint _handle; 141 bool _initialized, _isBound; 142 143 // attachements 144 GLFBOAttachment[] _colors; 145 GLFBOAttachment _depth, _stencil, _depthStencil; 146 147 Usage _usage; 148 GLenum _target; // redundant 149 150 void checkStatus() 151 { 152 GLenum status = void; 153 status = glCheckFramebufferStatus(_target); 154 155 switch(status) 156 { 157 case GL_FRAMEBUFFER_COMPLETE: 158 return; 159 160 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 161 throw new OpenGLException("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); 162 163 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 164 throw new OpenGLException("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"); 165 166 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: 167 throw new OpenGLException("GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT"); 168 169 case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: 170 throw new OpenGLException("GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT"); 171 172 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: 173 throw new OpenGLException("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER"); 174 175 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: 176 throw new OpenGLException("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER"); 177 178 case GL_FRAMEBUFFER_UNSUPPORTED: 179 throw new OpenGLException("GL_FRAMEBUFFER_UNSUPPORTED"); 180 181 default: throw new OpenGLException("Unknown FBO error"); 182 } 183 } 184 } 185 } 186 187 /// Defines one FBO attachment. 188 final class GLFBOAttachment 189 { 190 public 191 { 192 /// Attaches a 1D texture to the FBO. 193 /// Throws: $(D OpenGLException) on error. 194 void attach(GLTexture1D tex, int level = 0) 195 { 196 _newCall = Call(this, Call.Type.TEXTURE_1D, tex, null, level, 0); 197 updateAttachment(); 198 } 199 200 /// Attaches a 2D texture to the FBO. 201 /// Throws: $(D OpenGLException) on error. 202 void attach(GLTexture2D tex, int level = 0) 203 { 204 _newCall = Call(this, Call.Type.TEXTURE_2D, tex, null, level, 0); 205 updateAttachment(); 206 } 207 208 /// Attaches a 3D texture to the FBO. 209 /// Throws: $(D OpenGLException) on error. 210 void attach(GLTexture3D tex, int layer, int level) 211 { 212 _newCall = Call(this, Call.Type.TEXTURE_3D, tex, null, level, layer); 213 updateAttachment(); 214 } 215 216 /// Attaches a 1D texture array to the FBO. 217 /// Throws: $(D OpenGLException) on error. 218 void attach(GLTexture1DArray tex, int layer) 219 { 220 _newCall = Call(this, Call.Type.TEXTURE_3D, tex, null, 0, layer); 221 updateAttachment(); 222 } 223 224 /// Attaches a 2D texture array to the FBO. 225 /// Throws: $(D OpenGLException) on error. 226 void attach(GLTexture2DArray tex, int layer) 227 { 228 _newCall = Call(this, Call.Type.TEXTURE_3D, tex, null, 0, layer); 229 updateAttachment(); 230 } 231 232 /// Attaches a rectangle texture to the FBO. 233 /// Throws: $(D OpenGLException) on error. 234 void attach(GLTextureRectangle tex) 235 { 236 _newCall = Call(this, Call.Type.TEXTURE_2D, tex, null, 0, 0); 237 updateAttachment(); 238 } 239 240 /// Attaches a multisampled 2D texture to the FBO. 241 /// Throws: $(D OpenGLException) on error. 242 void attach(GLTexture2DMultisample tex) 243 { 244 _newCall = Call(this, Call.Type.TEXTURE_2D, tex, null, 0, 0); 245 updateAttachment(); 246 } 247 248 /// Attaches a multisampled 2D texture array to the FBO. 249 /// Throws: $(D OpenGLException) on error. 250 void attach(GLTexture2DMultisampleArray tex, int layer) 251 { 252 _newCall = Call(this, Call.Type.TEXTURE_3D, tex, null, 0, layer); 253 updateAttachment(); 254 } 255 256 /// Attaches a renderbuffer to the FBO. 257 /// Throws: $(D OpenGLException) on error. 258 void attach(GLRenderBuffer buffer) 259 { 260 _newCall = Call(this, Call.Type.RENDERBUFFER, null, buffer, 0, 0); 261 updateAttachment(); 262 } 263 } 264 265 private 266 { 267 this(GLFBO fbo, GLenum attachment) 268 { 269 _fbo = fbo; 270 _gl = fbo._gl; 271 _attachment = attachment; 272 _lastCall = _newCall = Call(this, Call.Type.DISABLED, null, null, 0, 0); 273 } 274 275 // guaranteed to be called once 276 void close() 277 { 278 _lastCall.detach(); 279 } 280 281 OpenGL _gl; 282 GLFBO _fbo; 283 GLenum _attachment; 284 Call _lastCall; 285 Call _newCall; 286 287 void updateAttachment() 288 { 289 if (_newCall != _lastCall && _fbo._isBound) 290 { 291 try 292 { 293 // trying to detach existing attachment 294 // would that help? 295 _lastCall.detach(); 296 } 297 catch(OpenGLException e) 298 { 299 // ignoring errors here 300 } 301 302 _newCall.attach(); 303 _lastCall = _newCall; 304 } 305 } 306 307 struct Call 308 { 309 public 310 { 311 enum Type 312 { 313 DISABLED, 314 TEXTURE_1D, 315 TEXTURE_2D, 316 TEXTURE_3D, 317 RENDERBUFFER 318 } 319 320 GLFBOAttachment _outer; 321 Type _type; 322 GLTexture _texture; 323 GLRenderBuffer _renderbuffer; 324 GLint _level; 325 GLint _layer; 326 327 void attach() 328 { 329 GLuint textureHandle = _texture !is null ? _texture.handle() : 0; 330 GLuint renderBufferHandle = _renderbuffer !is null ? _renderbuffer.handle() : 0; 331 attachOrDetach(textureHandle, renderBufferHandle); 332 } 333 334 void detach() 335 { 336 attachOrDetach(0, 0); 337 } 338 339 void attachOrDetach(GLuint textureHandle, GLuint renderBufferHandle) 340 { 341 final switch(_type) 342 { 343 case Type.DISABLED: 344 return; // do nothing 345 346 case Type.TEXTURE_1D: 347 glFramebufferTexture1D(_outer._fbo._target, _outer._attachment, _texture._target, textureHandle, _level); 348 break; 349 350 case Type.TEXTURE_2D: 351 glFramebufferTexture2D(_outer._fbo._target, _outer._attachment, _texture._target, textureHandle, _level); 352 break; 353 354 case Type.TEXTURE_3D: 355 glFramebufferTexture3D(_outer._fbo._target, _outer._attachment, _texture._target, textureHandle, _level, _layer); 356 break; 357 358 case Type.RENDERBUFFER: 359 glFramebufferRenderbuffer(_outer._fbo._target, _outer._attachment, GL_RENDERBUFFER, renderBufferHandle); 360 break; 361 } 362 _outer._gl.runtimeCheck(); 363 } 364 } 365 } 366 } 367 }