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