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