1 /+
2 The MIT License (MIT)
3 
4     Copyright (c) <2013> <Oleg Butko (deviator), Anton Akzhigitov (Akzwar)>
5 
6     Permission is hereby granted, free of charge, to any person obtaining a copy
7     of this software and associated documentation files (the "Software"), to deal
8     in the Software without restriction, including without limitation the rights
9     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10     copies of the Software, and to permit persons to whom the Software is
11     furnished to do so, subject to the following conditions:
12 
13     The above copyright notice and this permission notice shall be included in
14     all copies or substantial portions of the Software.
15 
16     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22     THE SOFTWARE.
23 +/
24 
25 module des.gl.base.frame;
26 
27 import derelict.opengl3.gl3;
28 
29 import des.math.linear;
30 
31 import des.gl.base.texture;
32 import des.gl.base.type;
33 
34 import des.il.image;
35 
36 ///
37 class GLFBOException : DesGLException 
38 { 
39     ///
40     this( string msg, string file=__FILE__, size_t line=__LINE__ ) @safe pure nothrow
41     { super( msg, file, line ); }
42 }
43 
44 ///
45 class GLRenderBuffer : DesObject
46 {
47     mixin DES;
48     mixin ClassLogger;
49 
50 protected:
51     uint _id;
52     Format _format;
53 
54 public:
55 
56     ///
57     enum Format
58     {
59         R8                 = GL_R8,                 /// `GL_R8`
60         R8UI               = GL_R8UI,               /// `GL_R8UI`
61         R8I                = GL_R8I,                /// `GL_R8I`
62         R16UI              = GL_R16UI,              /// `GL_R16UI`
63         R16I               = GL_R16I,               /// `GL_R16I`
64         R32UI              = GL_R32UI,              /// `GL_R32UI`
65         R32I               = GL_R32I,               /// `GL_R32I`
66         RG8                = GL_RG8,                /// `GL_RG8`
67         RG8UI              = GL_RG8UI,              /// `GL_RG8UI`
68         RG8I               = GL_RG8I,               /// `GL_RG8I`
69         RG16UI             = GL_RG16UI,             /// `GL_RG16UI`
70         RG16I              = GL_RG16I,              /// `GL_RG16I`
71         RG32UI             = GL_RG32UI,             /// `GL_RG32UI`
72         RG32I              = GL_RG32I,              /// `GL_RG32I`
73         RGB8               = GL_RGB8,               /// `GL_RGB8`
74         RGBA8              = GL_RGBA8,              /// `GL_RGBA8`
75         SRGB8_ALPHA8       = GL_SRGB8_ALPHA8,       /// `GL_SRGB8_ALPHA8`
76         RGB5_A1            = GL_RGB5_A1,            /// `GL_RGB5_A1`
77         RGBA4              = GL_RGBA4,              /// `GL_RGBA4`
78         RGB10_A2           = GL_RGB10_A2,           /// `GL_RGB10_A2`
79         RGBA8UI            = GL_RGBA8UI,            /// `GL_RGBA8UI`
80         RGBA8I             = GL_RGBA8I,             /// `GL_RGBA8I`
81         RGB10_A2UI         = GL_RGB10_A2UI,         /// `GL_RGB10_A2UI`
82         RGBA16UI           = GL_RGBA16UI,           /// `GL_RGBA16UI`
83         RGBA16I            = GL_RGBA16I,            /// `GL_RGBA16I`
84         RGBA32I            = GL_RGBA32I,            /// `GL_RGBA32I`
85         RGBA32UI           = GL_RGBA32UI,           /// `GL_RGBA32UI`
86         DEPTH_COMPONENT16  = GL_DEPTH_COMPONENT16,  /// `GL_DEPTH_COMPONENT16`
87         DEPTH_COMPONENT24  = GL_DEPTH_COMPONENT24,  /// `GL_DEPTH_COMPONENT24`
88         DEPTH_COMPONENT32F = GL_DEPTH_COMPONENT32F, /// `GL_DEPTH_COMPONENT32F`
89         DEPTH24_STENCIL8   = GL_DEPTH24_STENCIL8,   /// `GL_DEPTH24_STENCIL8`
90         DEPTH32F_STENCIL8  = GL_DEPTH32F_STENCIL8,  /// `GL_DEPTH32F_STENCIL8`
91         STENCIL_INDEX8     = GL_STENCIL_INDEX8      /// `GL_STENCIL_INDEX8`
92     }
93 
94     /// glGenRenderbuffers
95     this()
96     {
97         checkGLCall!glGenRenderbuffers( 1, &_id );
98         logger = new InstanceLogger( this, std..string.format( "%d", _id ) );
99         logger.Debug( "pass" );
100     }
101 
102     final pure const @property
103     {
104         ///
105         uint id() { return _id; }
106 
107         ///
108         Format format() { return _format; }
109     }
110 
111     /// `glBindRenderbuffer( GL_RENDERBUFFER, id )`
112     void bind()
113     {
114         checkGLCall!glBindRenderbuffer( GL_RENDERBUFFER, _id );
115         debug logger.trace( "[%d]", id );
116     }
117 
118     /// `glBindRenderbuffer( GL_RENDERBUFFER, 0 )`
119     void unbind()
120     {
121         checkGLCall!glBindRenderbuffer( GL_RENDERBUFFER, 0 );
122         debug logger.trace( "call from [%d]", _id );
123     }
124 
125     /// glRenderbufferStorage
126     void storage( in uivec2 sz, Format fmt )
127     in
128     {
129         assert( sz[0] < GL_MAX_RENDERBUFFER_SIZE );
130         assert( sz[1] < GL_MAX_RENDERBUFFER_SIZE );
131     }
132     body
133     {
134         bind();
135         _format = fmt;
136         checkGLCall!glRenderbufferStorage( GL_RENDERBUFFER, cast(GLenum)fmt, sz[0], sz[1] );
137         unbind();
138         debug logger.Debug( "size [%d,%d], format [%s]", sz[0], sz[1], fmt );
139     }
140 
141     /// ditto
142     void storage(T)( in T sz, Format fmt )
143     if( isCompatibleVector!(2,uint,T) )
144     in
145     {
146         assert( sz[0] >= 0 );
147         assert( sz[1] >= 0 );
148     }
149     body { storage( uivec2(sz), fmt ); }
150 
151     /// set storage with new size and old format
152     void resize( in uivec2 sz ) { storage( sz, _format ); }
153 
154     /// ditto
155     void resize(T)( in T sz )
156     if( isCompatibleVector!(2,uint,T) )
157     { resize( uivec2(sz) ); }
158 
159 protected:
160 
161     override void selfDestroy()
162     {
163         unbind();
164         checkGLCall!glDeleteRenderbuffers( 1, &_id );
165         logger.Debug( "pass" );
166     }
167 }
168 
169 ///
170 class GLFrameBuffer : DesObject
171 {
172     mixin DES;
173     mixin ClassLogger;
174 
175 protected:
176     uint _id;
177     static uint[] id_stack;
178 
179 public:
180 
181     // TODO: not work with gl constant
182     //mixin( getAttachmentEnumString!GL_MAX_COLOR_ATTACHMENTS );
183     mixin( getAttachmentEnumString!1 );
184 
185     /// glGenFramebuffers
186     this()
187     {
188         if( id_stack.length == 0 ) id_stack ~= 0;
189 
190         checkGLCall!glGenFramebuffers( 1, &_id );
191         logger = new InstanceLogger( this, format( "%d", _id ) );
192         logger.Debug( "pass" );
193     }
194 
195     final pure const @property
196     {
197         ///
198         uint id() { return _id; }
199     }
200 
201     final nothrow
202     {
203         /// glBindFramebuffer add id to stack 
204         void bind()
205         {
206             if( id_stack[$-1] == _id ) return;
207             ntCheckGLCall!glBindFramebuffer( GL_FRAMEBUFFER, _id );
208             id_stack ~= _id;
209             debug logger.trace( "pass" );
210         }
211 
212         /// pop from stack old frame buffer id and glBindFramebuffer with it
213         void unbind()
214         {
215             if( id_stack.length < 2 && id_stack[$-1] != _id ) return;
216             id_stack.length--;
217             ntCheckGLCall!glBindFramebuffer( GL_FRAMEBUFFER, id_stack[$-1] );
218             debug logger.trace( "bind [%d]", _id, id_stack[$-1] );
219         }
220     }
221 
222     ///
223     void setAttachment(T)( T obj, Attachment att )
224         if( is( T : GLTexture ) || is( T : GLRenderBuffer ) )
225     {
226         static if( is( T : GLTexture ) )
227             texture( obj, att );
228         else static if( is( T : GLRenderBuffer ) )
229             renderBuffer( obj, att );
230     }
231 
232     /// set texture attachment
233     void texture( GLTexture tex, Attachment att )
234     in { assert( isValidTextureTarget(tex.target) ); }
235     body { texture( tex, att, tex.target ); }
236 
237     /// ditto
238     void texture( GLTexture tex, Attachment att, GLTexture.Target trg )
239     in { assert( isValidTextureTarget(trg) ); } body
240     {
241         bind(); scope(exit) unbind();
242 
243         if( trg == tex.Target.T1D )
244             checkGLCall!glFramebufferTexture1D( GL_FRAMEBUFFER, cast(GLenum)att,
245                                         cast(GLenum)trg, tex.id, 0 );
246         else if( tex.target == tex.Target.T3D )
247             checkGLCall!glFramebufferTexture3D( GL_FRAMEBUFFER, cast(GLenum)att,
248                                     cast(GLenum)trg, tex.id, 0, 0 );
249         else
250             checkGLCall!glFramebufferTexture2D( GL_FRAMEBUFFER, cast(GLenum)att,
251                                     cast(GLenum)trg, tex.id, 0 );
252 
253         logger.Debug( "[%s] as [%s]", tex.id, att );
254     }
255 
256     /// set render buffer attachment
257     void renderBuffer( GLRenderBuffer rbo, Attachment att )
258     {
259         bind(); scope(exit) unbind();
260 
261         checkGLCall!glFramebufferRenderbuffer( GL_FRAMEBUFFER, cast(GLenum)att, 
262                                    GL_RENDERBUFFER, rbo.id );
263 
264         logger.Debug( "[%d] as [%s]", rbo.id, att );
265     }
266 
267     /// glCheckFramebufferStatus
268     void check()
269     {
270         bind(); scope(exit) unbind();
271         auto status = checkGLCall!glCheckFramebufferStatus( GL_FRAMEBUFFER );
272         import std.string;
273         if( status != GL_FRAMEBUFFER_COMPLETE )
274             throw new GLFBOException( format( "status isn't GL_FRAMEBUFFER_COMPLETE, it's %#x", status ) );
275     }
276 
277 protected:
278     override void selfDestroy()
279     {
280         unbind();
281         checkGLCall!glDeleteFramebuffers( 1, &_id );
282         logger.Debug( "pass" );
283     }
284 
285     static string getAttachmentEnumString(size_t COLOR_ATTACHMENT_COUNT)() @property
286     {
287         import std.string;
288         string[] ret;
289 
290         ret ~= `
291         enum Attachment
292         {
293             `;
294 
295         foreach( i; 0 .. COLOR_ATTACHMENT_COUNT )
296             ret ~= format( "COLOR%1d = GL_COLOR_ATTACHMENT%1d,", i, i );
297 
298         ret ~= "DEPTH         = GL_DEPTH_ATTACHMENT,";
299         ret ~= "STENCIL       = GL_STENCIL_ATTACHMENT,";
300         ret ~= "DEPTH_STENCIL = GL_DEPTH_STENCIL_ATTACHMENT,";
301 
302         ret ~= `}`;
303 
304         return ret.join("\n");
305     }
306 
307     bool isValidTextureTarget( GLTexture.Target trg )
308     {
309         switch(trg)
310         {
311         case GLTexture.Target.T1D:
312         case GLTexture.Target.T2D:
313         case GLTexture.Target.RECTANGLE:
314         case GLTexture.Target.T3D:
315         case GLTexture.Target.CUBE_MAP_POSITIVE_X:
316         case GLTexture.Target.CUBE_MAP_NEGATIVE_X:
317         case GLTexture.Target.CUBE_MAP_POSITIVE_Y:
318         case GLTexture.Target.CUBE_MAP_NEGATIVE_Y:
319         case GLTexture.Target.CUBE_MAP_POSITIVE_Z:
320         case GLTexture.Target.CUBE_MAP_NEGATIVE_Z:
321             return true;
322         default: return false;
323         }
324     }
325 }