GCC Code Coverage Report


Directory: ./
File: lib/geogram_gfx/basic/frame_buffer_object.cpp
Date: 2026-09-07 02:37:58
Exec Total Coverage
Lines: 0 127 0.0%
Functions: 0 10 0.0%
Branches: 0 42 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #include <geogram_gfx/basic/frame_buffer_object.h>
41 #include <geogram_gfx/basic/GLSL.h>
42 #include <geogram/basic/logger.h>
43 #include <string>
44
45 #ifdef GEO_OS_EMSCRIPTEN
46 #include <emscripten.h>
47 #endif
48
49 namespace {
50 using namespace GEO;
51
52 /**
53 * \brief Allocates the currently bound GL_TEXTURE_2D object.
54 * \param[in] width , height size of the texture.
55 * \param[in] internalformat internal format for the texture.
56 * \details Under WebGL2, if a generic internal format is specified,
57 * then a specific one is chosen (generic internal formats are
58 * not supported under WebGL2).
59 */
60 void allocate_texture_2D(
61 index_t width, index_t height, GLint internalformat
62 ) {
63
64 // - glTexImage2d in WebGL2 is very picky on what combination of
65 // internalformat/format/type is accepted, and it needs a full
66 // specification of internalformat (for instance, GL_RGBA8 instead
67 // of GL_RGBA)
68 // See
69 // https://webgl2fundamentals.org/webgl/lessons/webgl-data-textures.html
70 // for the valid combinations
71 // OpenGL ES 3.0: see:
72 // https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml
73 //
74 // - not all internalformat defines are available in Emscripten,
75 // added the ones that I need in geogram_gfx/basic/GL.h
76 //
77 // TODO: initialize the WebGL extension "floating point render target"
78 // if needed
79
80 GLenum format=0;
81 GLenum type=0;
82
83 switch(internalformat) {
84 case GL_RGBA:
85 format = GL_RGBA;
86 #if defined(GEO_OS_EMSCRIPTEN) && !defined(GEO_WEBGL2)
87 type = GL_FLOAT;
88 #else
89 type = GL_UNSIGNED_BYTE;
90 #endif
91 break;
92 case GL_RGB:
93 format = GL_RGB;
94 type = GL_UNSIGNED_BYTE;
95 break;
96 case GL_RED:
97 format = GL_RED;
98 type = GL_UNSIGNED_BYTE;
99 break;
100 case GL_R16:
101 format = GL_RED;
102 type = GL_SHORT;
103 break;
104 case GL_R32F:
105 format = GL_RED;
106 type = GL_FLOAT;
107 break;
108 case GL_R16F:
109 format = GL_RED;
110 type = GL_FLOAT;
111 break;
112 case GL_DEPTH_COMPONENT:
113 format = GL_DEPTH_COMPONENT;
114 type = GL_UNSIGNED_INT;
115 break;
116 }
117
118 #if (defined(GEO_OS_EMSCRIPTEN) && defined(GEO_WEBGL2)) || defined(GEO_OS_ANDROID)
119 // Replace generic internal format with specific
120 // one, as required by WebGL2 and ES3
121 if(internalformat == GL_RGBA) {
122 internalformat = GL_RGBA8;
123 } else if(internalformat == GL_RGB) {
124 internalformat = GL_RGB8;
125 } else if(internalformat == GL_DEPTH_COMPONENT) {
126 internalformat = GL_DEPTH_COMPONENT24;
127 } else if(internalformat == GL_RED) {
128 internalformat = GL_R8;
129 }
130 #endif
131
132 // If the following assertions fail, then
133 // this means that the internalformat that
134 // was given is not taken into account (just
135 // add it in the previous switch() and cascading
136 // ifs).
137 geo_assert(format != 0);
138 geo_assert(type != 0);
139
140 glTexImage2D(
141 GL_TEXTURE_2D, 0,
142 internalformat,
143 GLsizei(width), GLsizei(height), 0,
144 format,
145 type,
146 nullptr
147 );
148 }
149 }
150
151 namespace GEO {
152
153 FrameBufferObject::FrameBufferObject() :
154 frame_buffer_id(0),
155 depth_buffer_id(0),
156 offscreen_id(0),
157 width(0),
158 height(0)
159 {
160 }
161
162 FrameBufferObject::~FrameBufferObject() {
163 if(offscreen_id != 0) {
164 glDeleteTextures(1, &offscreen_id);
165 offscreen_id = 0;
166 }
167 if(depth_buffer_id != 0) {
168 glDeleteTextures(1, &depth_buffer_id);
169 depth_buffer_id = 0;
170 }
171 if(frame_buffer_id != 0) {
172 glBindFramebuffer(GL_FRAMEBUFFER, 0);
173 glDeleteFramebuffers(1, &frame_buffer_id);
174 }
175 }
176
177 void FrameBufferObject::resize(index_t new_width, index_t new_height) {
178 if(width != new_width || height != new_height) {
179 width = new_width;
180 height = new_height;
181
182 GEO_CHECK_GL();
183 glBindTexture(GL_TEXTURE_2D, offscreen_id);
184 allocate_texture_2D(width, height, internal_storage);
185 GEO_CHECK_GL();
186 if(depth_buffer_id != 0) {
187 glBindTexture(GL_TEXTURE_2D, depth_buffer_id);
188 GEO_CHECK_GL();
189 allocate_texture_2D(width, height, GL_DEPTH_COMPONENT);
190 GEO_CHECK_GL();
191 }
192 glBindTexture(GL_TEXTURE_2D, 0);
193 }
194 }
195
196 bool FrameBufferObject::initialize(
197 index_t width_in, index_t height_in,
198 bool with_depth_buffer, GLint internal_storage_in,
199 bool mipmaps
200 ) {
201
202 GEO_CHECK_GL();
203
204 width = width_in;
205 height = height_in;
206 internal_storage = internal_storage_in;
207
208 // Generate frame buffer object then bind it.
209 glGenFramebuffers(1, &frame_buffer_id);
210 glBindFramebuffer(GL_FRAMEBUFFER, frame_buffer_id);
211
212 GEO_CHECK_GL();
213
214 if(with_depth_buffer) {
215 glGenTextures(1, &depth_buffer_id);
216 }
217
218 GEO_CHECK_GL();
219
220 // Create the texture we will be using to render to.
221 glGenTextures(1, &offscreen_id);
222 glBindTexture(GL_TEXTURE_2D, offscreen_id);
223
224 GEO_CHECK_GL();
225
226 if(!mipmaps) {
227 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
228 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
229 }
230
231 GEO_CHECK_GL();
232
233 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
234 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
235
236 GEO_CHECK_GL();
237 allocate_texture_2D(width, height, internal_storage);
238 GEO_CHECK_GL();
239
240 // Bind the texture to the frame buffer.
241 glFramebufferTexture2D(
242 GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
243 GL_TEXTURE_2D, offscreen_id, 0
244 );
245
246 GEO_CHECK_GL();
247
248 // Initialize the depth buffer.
249 if(with_depth_buffer) {
250
251 glBindTexture(GL_TEXTURE_2D, depth_buffer_id);
252 allocate_texture_2D(width, height, GL_DEPTH_COMPONENT);
253 GEO_CHECK_GL();
254
255 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
256 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
257 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
258 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
259
260 GEO_CHECK_GL();
261
262 glFramebufferTexture2D(
263 GL_FRAMEBUFFER,
264 GL_DEPTH_ATTACHMENT,
265 GL_TEXTURE_2D, depth_buffer_id, 0
266 );
267
268 GEO_CHECK_GL();
269 }
270
271 GEO_CHECK_GL();
272
273 // Make sure we have not errors.
274 if(
275 glCheckFramebufferStatus(GL_FRAMEBUFFER) !=
276 GL_FRAMEBUFFER_COMPLETE
277 ) {
278 return false;
279 }
280
281 GEO_CHECK_GL();
282
283 return true;
284 }
285
286 void FrameBufferObject::bind_as_texture() {
287 glBindTexture(GL_TEXTURE_2D, offscreen_id);
288 }
289
290 void FrameBufferObject::bind_depth_buffer_as_texture() {
291 glBindTexture(GL_TEXTURE_2D, depth_buffer_id);
292 }
293
294 void FrameBufferObject::bind_as_framebuffer() {
295 glBindFramebuffer(GL_FRAMEBUFFER, frame_buffer_id);
296 }
297
298 void FrameBufferObject::unbind() {
299 // No longer do that, it is too misleading ---v
300 // glBindFramebuffer(GL_FRAMEBUFFER, previous_frame_buffer_id);
301 if(is_bound_as_framebuffer()) {
302 glBindFramebuffer(GL_FRAMEBUFFER, 0);
303 }
304 glBindTexture(GL_TEXTURE_2D, 0);
305 }
306
307 bool FrameBufferObject::is_bound_as_framebuffer() const {
308 return (bound_framebuffer_id() == frame_buffer_id);
309 }
310 }
311