1 module glamour.fbo;
2 
3 private {
4     import glamour.gl : GLenum, GLint, GLuint, GLsizei, glDeleteBuffers,
5                         glGenFramebuffers, glBindFramebuffer, glRenderbufferStorage,
6                         glFramebufferRenderbuffer, glFramebufferTexture1D,
7                         glFramebufferTexture2D, glGenRenderbuffers, glGetRenderbufferParameteriv,
8                         glBindRenderbuffer, glCheckFramebufferStatus, GL_FRAMEBUFFER_COMPLETE,
9                         GL_FRAMEBUFFER, GL_RENDERBUFFER, GL_TEXTURE_1D, GL_TEXTURE_2D;
10     import glamour.texture : Texture1D, Texture2D;
11     import glamour.util : checkgl;
12 
13     import std..string : format;
14 
15     debug import std.stdio : stderr;
16 }
17 
18 
19 class FrameBufferException : Exception {
20     this(GLenum err) {
21         super("FrameBuffer error: 0x%x".format(err));
22     }
23 }
24 
25 
26 /// Represents an Opengl FBO (FrameBufferObject)
27 class FrameBuffer {
28     static const GLenum target = GL_FRAMEBUFFER;
29 
30     /// The OpenGL FBO name
31     GLuint fbo;
32     /// Alias this to fbo
33     alias fbo this;
34 
35     /// Creates an OpenGL FBO
36     this() {
37         checkgl!glGenFramebuffers(1, &fbo);
38     }
39 
40     ~this() {
41         debug if(fbo != 0) stderr.writefln("OpenGL: FBO resource not released.");
42     }
43 
44     /// Deletes the FrameBuffer
45     void remove() {
46         checkgl!glDeleteBuffers(1, &fbo);
47         fbo = 0;
48     }
49 
50     /// Binds the FrameBuffer
51     void bind() {
52         checkgl!glBindFramebuffer(target, fbo);
53     }
54 
55     /// Unbinds the FrameBuffer
56     void unbind() {
57         checkgl!glBindFramebuffer(target, 0);
58     }
59 
60     /// Attaches a 1D-texture to the FrameBuffer
61     /// Calls validate
62     void attach(Texture1D texture, GLenum attachment_point, GLint level=0) {
63         bind();
64         checkgl!glFramebufferTexture1D(target, attachment_point, GL_TEXTURE_1D, texture, level);
65         validate();
66     }
67 
68     /// Attaches a new and empty 2D texture to the FrameBuffer
69     /// Calls validate, and binds the returned texture
70     Texture1D attach_new_texture(GLenum attachment_point, GLint internal_format,
71                                  int width, GLenum format, GLenum type) {
72         auto texture = new Texture1D();
73         texture.set_data(cast(void*)null, internal_format, width, format, type);
74         attach(texture, attachment_point);
75 
76         return texture;
77     }
78 
79     /// Attaches a 2D-texture to the FrameBuffer
80     /// Calls validate
81     void attach(Texture2D texture, GLenum attachment_point, GLint level=0) {
82         bind();
83         checkgl!glFramebufferTexture2D(target, attachment_point, GL_TEXTURE_2D, texture, level);
84         validate();
85     }
86 
87     /// Attaches a new and empty 2D texture to the FrameBuffer
88     /// Calls validate, and binds the returned texture
89     Texture2D attach_new_texture(GLenum attachment_point, GLint internal_format,
90                                  int width, int height, GLenum format, GLenum type) {
91         auto texture = new Texture2D();
92         texture.set_data(cast(void*)null, internal_format, width, height,
93                         format, type, true, 0);
94         attach(texture, attachment_point);
95 
96         return texture;
97     }
98 
99     /// Attaches a RenderBuffer to the FrameBuffer
100     /// Calls validate
101     void attach(RenderBuffer rbuffer, GLenum attachment_point) {
102         bind();
103         checkgl!glFramebufferRenderbuffer(target, attachment_point, rbuffer.target, rbuffer);
104         validate();
105     }
106 
107     /// Attaches a new renderbuffer to the FrameBuffer
108     /// Calls validate, also binds the returned RenderBuffer
109     RenderBuffer attach_new_renderbuffer(GLenum attachment_point, GLenum internal_format,
110                                          int width, int height) {
111         auto rb = new RenderBuffer();
112         rb.set_storage(internal_format, width, height);
113         attach(rb, attachment_point);
114         return rb;
115     }
116 
117     /// Calls glCheckFramebufferStatus and throws a FrameBufferException if the return value
118     /// is not GL_FRAMEBUFFER_COMPLETE
119     static void validate() {
120         auto ret = glCheckFramebufferStatus(target);
121         if(ret != GL_FRAMEBUFFER_COMPLETE) {
122             throw new FrameBufferException(ret);
123         }
124     }
125 
126 //     /// Sets a framebuffer parameter, mostly used for simulating empty buffers
127 //     void set_parameter(GLenum param_name, GLint param) {
128 //         checkgl!glFramebufferParameteri(target, param_name, param);
129 //     }
130 }
131 
132 /// Represents an OpenGL RenderBuffer
133 class RenderBuffer {
134     static const GLenum target = GL_RENDERBUFFER;
135 
136     /// The OpenGL RenderBuffer name
137     GLuint renderbuffer;
138     /// Alias this to renderbuffer
139     alias renderbuffer this;
140 
141     /// Creates an OpenGL RenderBuffer
142     this() {
143         checkgl!glGenRenderbuffers(1, &renderbuffer);
144     }
145 
146     ~this() {
147         debug if(renderbuffer != 0) stderr.writefln("OpenGL: RenderBuffer not released.");
148     }
149 
150     /// Deletes the FrameBuffer
151     void remove() {
152         checkgl!glDeleteBuffers(1, &renderbuffer);
153         renderbuffer = 0;
154     }
155 
156     /// Binds the RenderBuffer
157     void bind() {
158         checkgl!glBindRenderbuffer(target, renderbuffer);
159     }
160 
161     /// Unbinds the RenderBuffer
162     void unbind() {
163         checkgl!glBindRenderbuffer(target, 0);
164     }
165 
166     /// Allocates storage for the RenderBuffer
167     void set_storage(GLenum internal_format, GLsizei width, GLsizei height) {
168         bind();
169         checkgl!glRenderbufferStorage(target, internal_format, width, height);
170     }
171 
172     /// Returns a RenderBuffer parameter
173     int get_parameter(GLenum param_name) {
174         int store;
175         checkgl!glGetRenderbufferParameteriv(target, param_name, &store);
176         return store;
177     }
178 }