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