1 module des.gl.fbo;
2 
3 import std.exception;
4 import std.conv;
5 
6 import des.gl.general;
7 import des.gl.texture;
8 import des.gl.rbo;
9 
10 ///
11 class GLFBOException : DesGLException
12 {
13     ///
14     this( string msg, string file=__FILE__, size_t line=__LINE__ ) @safe pure nothrow
15     { super( msg, file, line ); }
16 }
17 
18 ///
19 class GLFrameBuffer : GLObject!"Framebuffer"
20 {
21     mixin DES;
22     mixin ClassLogger;
23 
24 protected:
25     static uint[] id_stack;
26 
27     ///
28     enum Attachment
29     {
30         COLOR         = GL_COLOR_ATTACHMENT0,        /// `GL_COLOR_ATTACHMENT0`
31         DEPTH         = GL_DEPTH_ATTACHMENT,         /// `GL_DEPTH_ATTACHMENT`
32         STENCIL       = GL_STENCIL_ATTACHMENT,       /// `GL_STENCIL_ATTACHMENT`
33         DEPTH_STENCIL = GL_DEPTH_STENCIL_ATTACHMENT, /// `GL_DEPTH_STENCIL_ATTACHMENT`
34     }
35 
36 public:
37 
38     ///
39     this()
40     {
41         if( id_stack.length == 0 ) id_stack ~= 0;
42         super( GL_FRAMEBUFFER );
43         logger.Debug( "pass" );
44     }
45 
46     final
47     {
48         /// `glBindFramebuffer` add id to stack
49         override void bind()
50         {
51             if( id_stack[$-1] == id ) return;
52             ntCheckGLCall!glBindFramebuffer( GL_FRAMEBUFFER, id );
53             id_stack ~= id;
54             debug logger.trace( "pass" );
55         }
56 
57         /// pop from stack old frame buffer id and `glBindFramebuffer` with it
58         override void unbind()
59         {
60             if( id_stack.length < 2 && id_stack[$-1] != id ) return;
61             id_stack.length--;
62             ntCheckGLCall!glBindFramebuffer( GL_FRAMEBUFFER, id_stack[$-1] );
63             debug logger.trace( "bind [%d]", id_stack[$-1] );
64         }
65     }
66 
67     ///
68     void drawBuffers( in int[] bufs... )
69     {
70         int max_bufs;
71         checkGLCall!glGetIntegerv( GL_MAX_DRAW_BUFFERS, &max_bufs );
72         enforce( bufs.length < max_bufs,
73             new GLFBOException( format( "count of draw buffers greater what max value (%d>%d)", bufs.length, max_bufs ) ) );
74         bind(); scope(exit) unbind();
75         GLenum[] res;
76         foreach( val; bufs )
77             if( val < 0 ) res ~= GL_NONE;
78             else res ~= cast(GLenum)( Attachment.COLOR + val );
79         checkGLCall!glDrawBuffers( cast(int)res.length, res.ptr );
80     }
81 
82     /// set render buffer as depth attachment
83     void setDepth( GLRenderBuffer rbo )
84     in{ assert( rbo !is null ); } body
85     {
86         bind(); scope(exit) unbind();
87         setRBO( rbo, Attachment.DEPTH );
88         logger.Debug( "[%d]", rbo.id );
89     }
90 
91     /// set render buffer as color attachment
92     void setColor( GLRenderBuffer rbo, uint no )
93     in{ assert( rbo !is null ); } body
94     {
95         bind(); scope(exit) unbind();
96         setRBO( rbo, cast(GLenum)( Attachment.COLOR + no ) );
97         logger.Debug( "[%d] as COLOR%d", rbo.id, no );
98     }
99 
100     /// set texture as depth attachment
101     void setDepth( GLTexture tex )
102     {
103         bind(); scope(exit) unbind();
104         setTex( tex, Attachment.DEPTH );
105         logger.Debug( "[%d]", tex.id );
106     }
107 
108     /// set texture as color attachment
109     void setColor( GLTexture tex, uint no=0 )
110     {
111         bind(); scope(exit) unbind();
112         setTex( tex, cast(GLenum)( Attachment.COLOR + no ) );
113         logger.Debug( "[%d] as COLOR%d", tex.id, no );
114     }
115 
116     /// `glCheckFramebufferStatus`
117     void check()
118     {
119         bind(); scope(exit) unbind();
120         auto status = checkGLCall!glCheckFramebufferStatus( GL_FRAMEBUFFER );
121         import std..string;
122         if( status != GL_FRAMEBUFFER_COMPLETE )
123             throw new GLFBOException( format( "status isn't GL_FRAMEBUFFER_COMPLETE, it's %#x", status ) );
124         logger.Debug( "pass" );
125     }
126 
127 protected:
128 
129     /// warning: no bind
130     void setRBO( GLRenderBuffer rbo, GLenum attachment )
131     in{ assert( rbo !is null ); } body
132     {
133         checkGLCall!glFramebufferRenderbuffer( GL_FRAMEBUFFER,
134                             attachment, GL_RENDERBUFFER, rbo.id );
135     }
136 
137     /// warning: no bind
138     void texture1D( GLTexture tex, GLenum attachment, uint level=0 )
139     in { assert( tex !is null ); } body
140     {
141         checkGLCall!glFramebufferTexture1D( GL_FRAMEBUFFER, attachment,
142                                             GL_TEXTURE_1D, tex.id, level );
143     }
144 
145     /// warning: no bind
146     void texture2D( GLTexture tex, GLenum attachment, uint level=0 )
147     in { assert( tex !is null ); } body
148     {
149         checkGLCall!glFramebufferTexture2D( GL_FRAMEBUFFER, attachment,
150                                             tex.target, tex.id, level );
151     }
152 
153     /// warning: no bind
154     void texture3D( GLTexture tex, GLenum attachment, uint level=0, int layer=0 )
155     in { assert( tex !is null ); } body
156     {
157         checkGLCall!glFramebufferTexture3D( GL_FRAMEBUFFER, attachment,
158                                             tex.target, tex.id, level, layer );
159     }
160 
161     /// warning: no bind
162     void texture( GLTexture tex, GLenum attachment, uint level=0 )
163     { checkGLCall!glFramebufferTexture( GL_FRAMEBUFFER, attachment, tex.id, level ); }
164 
165     /// warning: no bind
166     void textureLayer( GLTexture tex, GLenum attachment, uint layer, uint level=0 )
167     { checkGLCall!glFramebufferTextureLayer( GL_FRAMEBUFFER, attachment, tex.id, level, layer ); }
168 
169 
170     /// warning: no bind
171     void setTex( GLTexture tex, GLenum attachment )
172     {
173         if( tex.target == GL_TEXTURE_1D )
174             texture1D( tex, attachment );
175         else if( tex.target == GL_TEXTURE_3D ||
176                  tex.target == GL_TEXTURE_2D_ARRAY ||
177                  tex.target == GL_TEXTURE_CUBE_MAP_ARRAY )
178             texture3D( tex, attachment );
179         else
180             texture2D( tex, attachment, tex.target );
181     }
182 }