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 }