1 /*
2 * Copyright (c) 2011 Collabora, Ltd.
3 *
4 * Permission to use, copy, modify, distribute, and sell this software
5 * and its documentation for any purpose is hereby granted without
6 * fee, provided that the above copyright notice appear in all copies
7 * and that both that copyright notice and this permission notice
8 * appear in supporting documentation, and that the name of
9 * Collabora Ltd. not be used in advertising or publicity pertaining to
10 * distribution of the software without specific, written prior permission.
11 * Collabora Ltd. makes no representations about the suitability of this
12 * software for any purpose. It is provided "as is" without express or
13 * implied warranty.
14 *
15 * COLLABORA LTD. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
17 * NO EVENT SHALL COLLABORA LTD. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
19 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
20 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
21 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 *
23 * Authors: Pekka Paalanen <ppaalanen@gmail.com>
24 */
25
26 #include <map>
27 #include <opengl/framebufferobject.h>
28 #include <opengl/texture.h>
29
30 struct PrivateGLFramebufferObject
31 {
32 PrivateGLFramebufferObject () :
33 fboId (0),
34 pushedId (0),
35 glTex (NULL),
36 status (-1)
37 {
CID 12577 - UNINIT_CTOR
Non-static class member "rbStencilId" is not initialized in this constructor nor in any functions that it calls.
38 }
39
40 void pushFBO ();
41 void popFBO ();
42
43 GLuint fboId;
44 GLuint pushedId;
CID 12577 - UNINIT_CTOR
Class member declaration for "rbStencilId".
45 GLuint rbStencilId;
46 GLTexture *glTex;
47
48 GLint status;
49
50 static GLuint boundId;
51 static std::map<GLuint, GLFramebufferObject *> idMap;
52 };
53
54 GLuint PrivateGLFramebufferObject::boundId = 0;
55 std::map<GLuint, GLFramebufferObject *> PrivateGLFramebufferObject::idMap;
56
57 void
58 PrivateGLFramebufferObject::pushFBO ()
59 {
60 pushedId = boundId;
61 if (boundId != fboId)
62 {
63 (*GL::bindFramebuffer) (GL::FRAMEBUFFER, fboId);
64 boundId = fboId;
65 }
66 }
67
68 void
69 PrivateGLFramebufferObject::popFBO ()
70 {
71 if (boundId != pushedId)
72 {
73 (*GL::bindFramebuffer) (GL::FRAMEBUFFER, pushedId);
74 boundId = pushedId;
75 }
76 }
77
78 GLFramebufferObject::GLFramebufferObject () :
79 priv (new PrivateGLFramebufferObject)
80 {
81 (*GL::genFramebuffers) (1, &priv->fboId);
82 (*GL::genRenderbuffers) (1, &priv->rbStencilId);
83
84 if (priv->fboId != 0)
85 PrivateGLFramebufferObject::idMap[priv->fboId] = this;
86 }
87
88 GLFramebufferObject::~GLFramebufferObject ()
89 {
90 if (priv->glTex)
91 GLTexture::decRef (priv->glTex);
92
93 PrivateGLFramebufferObject::idMap.erase (priv->fboId);
94 (*GL::deleteFramebuffers) (1, &priv->fboId);
95 (*GL::deleteRenderbuffers) (1, &priv->rbStencilId);
96
97
98 delete priv;
99 }
100
101 bool
102 GLFramebufferObject::allocate (const CompSize &size, const char *image,
103 GLenum format, GLenum type)
104 {
105 priv->status = -1;
106
107 if (!priv->glTex ||
108 size.width () != priv->glTex->width () ||
109 size.height () != priv->glTex->height ())
110 {
111 if (priv->glTex)
112 GLTexture::decRef (priv->glTex);
113 priv->glTex = NULL;
114
115 GLTexture::List list = GLTexture::imageDataToTexture (image, size,
116 format, type);
117 if (list.size () != 1 || list[0] == NULL)
118 return false;
119
120 priv->glTex = list[0];
121 GLTexture::incRef (priv->glTex);
122
123 if (GL::fboStencilSupported)
124 {
125 (*GL::bindRenderbuffer) (GL::RENDERBUFFER, priv->rbStencilId);
126 (*GL::renderbufferStorage) (GL::RENDERBUFFER, GL::DEPTH24_STENCIL8, size.width (), size.height ());
127 }
128 }
129
130 priv->pushFBO ();
131
132 (*GL::framebufferTexture2D) (GL::FRAMEBUFFER, GL::COLOR_ATTACHMENT0,
133 priv->glTex->target (),
134 priv->glTex->name (), 0);
135
136 priv->status = (*GL::checkFramebufferStatus) (GL::DRAW_FRAMEBUFFER);
137
138 priv->popFBO ();
139 return true;
140 }
141
142 GLFramebufferObject *
143 GLFramebufferObject::bind ()
144 {
145 GLFramebufferObject *old = NULL;
146
147 if (priv->boundId != 0)
148 {
149 std::map<GLuint, GLFramebufferObject *>::iterator it;
150 it = PrivateGLFramebufferObject::idMap.find (priv->boundId);
151
152 if (it != PrivateGLFramebufferObject::idMap.end ())
153 old = it->second;
154 else
155 compLogMessage ("opengl", CompLogLevelError,
156 "An FBO without GLFramebufferObject cannot be restored");
157 }
158
159 (*GL::bindFramebuffer) (GL::FRAMEBUFFER, priv->fboId);
160 priv->boundId = priv->fboId;
161
162 (*GL::framebufferRenderbuffer) (GL::FRAMEBUFFER, GL::DEPTH_ATTACHMENT, GL::RENDERBUFFER, priv->rbStencilId);
163 (*GL::framebufferRenderbuffer) (GL::FRAMEBUFFER, GL::STENCIL_ATTACHMENT, GL::RENDERBUFFER, priv->rbStencilId);
164
165 return old;
166 }
167
168 // static
169 void
170 GLFramebufferObject::rebind (GLFramebufferObject *fbo)
171 {
172 GLuint id = fbo ? fbo->priv->fboId : 0;
173
174 if (id != fbo->priv->boundId)
175 {
176 (*GL::bindFramebuffer) (GL::FRAMEBUFFER, id);
177 fbo->priv->boundId = id;
178 }
179 }
180
181 static const char *
182 getFboErrorString (GLint status)
183 {
184 switch (status)
185 {
186 case GL::FRAMEBUFFER_COMPLETE:
187 return "GL::FRAMEBUFFER_COMPLETE";
188 case GL::FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
189 return "GL::FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
190 case GL::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
191 return "GL::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
192 case GL::FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
193 return "GL::FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
194 case GL::FRAMEBUFFER_UNSUPPORTED:
195 return "GL::FRAMEBUFFER_UNSUPPORTED";
196 default:
197 return "unexpected status";
198 }
199 }
200
201 bool
202 GLFramebufferObject::checkStatus ()
203 {
204 priv->pushFBO ();
205 priv-> status = (*GL::checkFramebufferStatus) (GL_FRAMEBUFFER);
206 priv->popFBO ();
207
208 if (priv->status == static_cast <GLint> (GL::FRAMEBUFFER_COMPLETE))
209 return true;
210
211 compLogMessage ("opengl", CompLogLevelError,
212 "FBO is incomplete: %s (0x%04x)",
213 getFboErrorString (priv->status), priv->status);
214 return false;
215 }
216
217 GLTexture *
218 GLFramebufferObject::tex ()
219 {
220 return priv->glTex;
221 }