1 /*
2  * This file is part of the Colobot: Gold Edition source code
3  * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam
4  * http://epsitec.ch; http://colobot.info; http://github.com/colobot
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see http://gnu.org/licenses
18  */
19 
20 #include "graphics/opengl/glframebuffer.h"
21 
22 #include "common/logger.h"
23 
24 namespace Gfx
25 {
26 
27 // CGLFramebuffer
28 
29 GLuint CGLFramebuffer::m_currentFBO = 0;
30 
CGLFramebuffer(const FramebufferParams & params)31 CGLFramebuffer::CGLFramebuffer(const FramebufferParams& params)
32     : m_params(params)
33 {
34     m_fbo = 0;
35     m_colorRenderbuffer = 0;
36     m_colorTexture = 0;
37     m_depthRenderbuffer = 0;
38     m_depthTexture = 0;
39     m_width = 0;
40     m_height = 0;
41     m_depth = 0;
42     m_samples = 0;
43 }
44 
Create()45 bool CGLFramebuffer::Create()
46 {
47     if (m_fbo != 0) return false;
48 
49     m_width = m_params.width;
50     m_height = m_params.height;
51     m_depth = m_params.depth;
52     m_samples = m_params.samples;
53 
54     glGenFramebuffers(1, &m_fbo);
55     glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
56 
57     // create color texture
58     if (m_params.colorAttachment == FramebufferParams::AttachmentType::Texture)
59     {
60         GLint previous;
61         glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous);
62 
63         glGenTextures(1, &m_colorTexture);
64         glBindTexture(GL_TEXTURE_2D, m_colorTexture);
65 
66         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_params.width, m_params.height,
67                 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
68 
69         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
70         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
71         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
72         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
73 
74         glBindTexture(GL_TEXTURE_2D, previous);
75 
76         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorTexture, 0);
77     }
78     // create color renderbuffer
79     else if (m_params.colorAttachment == FramebufferParams::AttachmentType::Renderbuffer)
80     {
81         glGenRenderbuffers(1, &m_colorRenderbuffer);
82         glBindRenderbuffer(GL_RENDERBUFFER, m_colorRenderbuffer);
83 
84         if (m_params.samples > 1)
85             glRenderbufferStorageMultisample(GL_RENDERBUFFER, m_params.samples,
86                     GL_RGBA8, m_params.width, m_params.height);
87         else
88             glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, m_params.width, m_params.height);
89 
90         glBindRenderbuffer(GL_RENDERBUFFER, 0);
91 
92         glFramebufferRenderbuffer(GL_FRAMEBUFFER,
93                 GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_colorRenderbuffer);
94     }
95     else
96     {
97         glDrawBuffer(GL_NONE);
98     }
99 
100     GLuint depthFormat = 0;
101 
102     switch (m_params.depth)
103     {
104     case 16: depthFormat = GL_DEPTH_COMPONENT16; break;
105     case 24: depthFormat = GL_DEPTH_COMPONENT24; break;
106     case 32: depthFormat = GL_DEPTH_COMPONENT32; break;
107     default: depthFormat = GL_DEPTH_COMPONENT16; break;
108     }
109 
110     // create depth texture
111     if (m_params.depthAttachment == FramebufferParams::AttachmentType::Texture)
112     {
113         GLint previous;
114         glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous);
115 
116         glGenTextures(1, &m_depthTexture);
117         glBindTexture(GL_TEXTURE_2D, m_depthTexture);
118 
119         glTexImage2D(GL_TEXTURE_2D, 0, depthFormat, m_params.width, m_params.height, 0,
120                 GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
121 
122         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
123         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
124 
125         float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
126 
127         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
128         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
129         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
130         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
131         glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color);
132 
133         glBindTexture(GL_TEXTURE_2D, previous);
134 
135         glFramebufferTexture2D(GL_FRAMEBUFFER,
136                 GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_depthTexture, 0);
137     }
138     // create depth renderbuffer
139     else if (m_params.depthAttachment == FramebufferParams::AttachmentType::Renderbuffer)
140     {
141         glGenRenderbuffers(1, &m_depthRenderbuffer);
142         glBindRenderbuffer(GL_RENDERBUFFER, m_depthRenderbuffer);
143 
144         if (m_params.samples > 1)
145             glRenderbufferStorageMultisample(GL_RENDERBUFFER, m_params.samples,
146                     depthFormat, m_params.width, m_params.height);
147         else
148             glRenderbufferStorage(GL_RENDERBUFFER,
149                     depthFormat, m_params.width, m_params.height);
150 
151         glBindRenderbuffer(GL_RENDERBUFFER, 0);
152 
153         glFramebufferRenderbuffer(GL_FRAMEBUFFER,
154                 GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthRenderbuffer);
155     }
156 
157     GLuint result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
158     if (result != GL_FRAMEBUFFER_COMPLETE)
159     {
160         GetLogger()->Error("Framebuffer incomplete: ");
161 
162         switch (result)
163         {
164         case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
165             GetLogger()->Error("attachment point incomplete");
166             break;
167         case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
168             GetLogger()->Error("missing attachment");
169             break;
170         case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
171             GetLogger()->Error("draw buffer has missing color attachments");
172             break;
173         case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
174             GetLogger()->Error("read buffer has missing color attachments");
175             break;
176         case GL_FRAMEBUFFER_UNSUPPORTED:
177             GetLogger()->Error("unsupported attachment format");
178             break;
179         }
180 
181         GetLogger()->Error("\n");
182 
183         Destroy();
184 
185         glBindFramebuffer(GL_FRAMEBUFFER, m_currentFBO);
186         return false;
187     }
188 
189     glBindFramebuffer(GL_FRAMEBUFFER, m_currentFBO);
190     return true;
191 }
192 
Destroy()193 void CGLFramebuffer::Destroy()
194 {
195     if (m_fbo == 0) return;
196 
197     if (m_currentFBO == m_fbo)
198         glBindFramebuffer(GL_FRAMEBUFFER, 0);
199 
200     glDeleteFramebuffers(1, &m_fbo);
201     m_fbo = 0;
202 
203     if (m_colorRenderbuffer != 0)
204     {
205         glDeleteRenderbuffers(1, &m_colorRenderbuffer);
206         m_colorRenderbuffer = 0;
207     }
208 
209     if (m_colorTexture != 0)
210     {
211         glDeleteTextures(1, &m_colorTexture);
212         m_colorTexture = 0;
213     }
214 
215     if (m_depthRenderbuffer != 0)
216     {
217         glDeleteRenderbuffers(1, &m_depthRenderbuffer);
218         m_depthRenderbuffer = 0;
219     }
220 
221     if (m_depthTexture != 0)
222     {
223         glDeleteTextures(1, &m_depthTexture);
224         m_depthTexture = 0;
225     }
226 
227     m_width = 0;
228     m_height = 0;
229     m_depth = 0;
230     m_samples = 0;
231 }
232 
IsDefault()233 bool CGLFramebuffer::IsDefault()
234 {
235     return false;
236 }
237 
238 //! Returns width of buffers in this framebuffer
GetWidth()239 int CGLFramebuffer::GetWidth()
240 {
241     return m_width;
242 }
243 
244 //! Returns height of buffers in this framebuffer
GetHeight()245 int CGLFramebuffer::GetHeight()
246 {
247     return m_height;
248 }
249 
250 //! Returns depth size in bits
GetDepth()251 int CGLFramebuffer::GetDepth()
252 {
253     return m_depth;
254 }
255 
256 //! Returns number of samples or 1 if multisampling is not supported
GetSamples()257 int CGLFramebuffer::GetSamples()
258 {
259     return m_samples;
260 }
261 
262 //! Returns texture that contains color buffer or 0 if not available
GetColorTexture()263 int CGLFramebuffer::GetColorTexture()
264 {
265     return m_colorTexture;
266 }
267 
268 //! Returns texture that contains depth buffer or 0 if not available
GetDepthTexture()269 int CGLFramebuffer::GetDepthTexture()
270 {
271     return m_depthTexture;
272 }
273 
274 //! Binds this framebuffer to context
Bind()275 void CGLFramebuffer::Bind()
276 {
277     glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
278     m_currentFBO = m_fbo;
279 }
280 
281 //! Unbinds this framebuffer from context
Unbind()282 void CGLFramebuffer::Unbind()
283 {
284     glBindFramebuffer(GL_FRAMEBUFFER, 0);
285     m_currentFBO = 0;
286 }
287 
CopyToScreen(int fromX,int fromY,int fromWidth,int fromHeight,int toX,int toY,int toWidth,int toHeight)288 void CGLFramebuffer::CopyToScreen(int fromX, int fromY, int fromWidth, int fromHeight,
289         int toX, int toY, int toWidth, int toHeight)
290 {
291     glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo);
292     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
293 
294     glBlitFramebuffer(fromX, fromY, fromX + fromWidth, fromY + fromHeight,
295         toX, toY, toX + toWidth, toY + toHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
296 
297     glBindFramebuffer(GL_FRAMEBUFFER, m_currentFBO);
298 }
299 
300 // CGLFramebufferEXT
301 GLuint CGLFramebufferEXT::m_currentFBO = 0;
302 
CGLFramebufferEXT(const FramebufferParams & params)303 CGLFramebufferEXT::CGLFramebufferEXT(const FramebufferParams& params)
304     : m_params(params)
305 {
306     m_fbo = 0;
307     m_colorRenderbuffer = 0;
308     m_colorTexture = 0;
309     m_depthRenderbuffer = 0;
310     m_depthTexture = 0;
311     m_width = 0;
312     m_height = 0;
313     m_depth = 0;
314     m_samples = 0;
315 }
316 
Create()317 bool CGLFramebufferEXT::Create()
318 {
319     if (m_fbo != 0) return false;
320 
321     m_width = m_params.width;
322     m_height = m_params.height;
323     m_depth = m_params.depth;
324     m_samples = m_params.samples;
325 
326     glGenFramebuffersEXT(1, &m_fbo);
327     glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo);
328 
329     // create color texture
330     if (m_params.colorAttachment == FramebufferParams::AttachmentType::Texture)
331     {
332         GLint previous;
333         glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous);
334 
335         glGenTextures(1, &m_colorTexture);
336         glBindTexture(GL_TEXTURE_2D, m_colorTexture);
337 
338         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,
339                 m_params.width, m_params.height, 0,
340                 GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
341 
342         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
343         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
344         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
345         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
346 
347         glBindTexture(GL_TEXTURE_2D, previous);
348 
349         glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
350                 GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_colorTexture, 0);
351     }
352     // create color renderbuffer
353     else if (m_params.colorAttachment == FramebufferParams::AttachmentType::Renderbuffer)
354     {
355         glGenRenderbuffersEXT(1, &m_colorRenderbuffer);
356         glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_colorRenderbuffer);
357 
358         if (m_params.samples > 1)
359             glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT,
360                     m_params.samples, GL_RGBA8, m_params.width, m_params.height);
361         else
362             glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_RGBA8,
363                     m_params.width, m_params.height);
364 
365         glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
366 
367         glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
368                 GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, m_colorRenderbuffer);
369     }
370     else
371     {
372         glDrawBuffer(GL_NONE);
373     }
374 
375     GLuint depthFormat = 0;
376 
377     switch (m_params.depth)
378     {
379     case 16: depthFormat = GL_DEPTH_COMPONENT16; break;
380     case 24: depthFormat = GL_DEPTH_COMPONENT24; break;
381     case 32: depthFormat = GL_DEPTH_COMPONENT32; break;
382     default: depthFormat = GL_DEPTH_COMPONENT16; break;
383     }
384 
385     // create depth texture
386     if (m_params.depthAttachment == FramebufferParams::AttachmentType::Texture)
387     {
388         GLint previous;
389         glGetIntegerv(GL_TEXTURE_BINDING_2D, &previous);
390 
391         glGenTextures(1, &m_depthTexture);
392         glBindTexture(GL_TEXTURE_2D, m_depthTexture);
393 
394         glTexImage2D(GL_TEXTURE_2D, 0, depthFormat, m_params.width, m_params.height, 0,
395                 GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
396 
397         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
398         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
399 
400         float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
401 
402         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
403         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
404         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
405         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
406         glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color);
407 
408         glBindTexture(GL_TEXTURE_2D, previous);
409 
410         glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
411                 GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, m_depthTexture, 0);
412     }
413     // create depth renderbuffer
414     else if (m_params.depthAttachment == FramebufferParams::AttachmentType::Renderbuffer)
415     {
416         glGenRenderbuffersEXT(1, &m_depthRenderbuffer);
417         glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_depthRenderbuffer);
418 
419         if (m_params.samples > 1)
420             glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT,
421                     m_params.samples, depthFormat, m_params.width, m_params.height);
422         else
423             glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, depthFormat, m_params.width, m_params.height);
424 
425         glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
426 
427         glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
428                 GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_depthRenderbuffer);
429     }
430 
431     GLuint result = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
432     if (result != GL_FRAMEBUFFER_COMPLETE_EXT)
433     {
434         GetLogger()->Error("Framebuffer incomplete: ");
435 
436         switch (result)
437         {
438         case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
439             GetLogger()->Error("attachment point incomplete");
440             break;
441         case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
442             GetLogger()->Error("missing attachment");
443             break;
444         case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
445             GetLogger()->Error("incompatible attachment dimensions");
446             break;
447         case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
448             GetLogger()->Error("draw buffer has missing color attachments");
449             break;
450         case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
451             GetLogger()->Error("read buffer has missing color attachments");
452             break;
453         case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
454             GetLogger()->Error("unsupported attachment format");
455             break;
456         }
457 
458         Destroy();
459 
460         glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_currentFBO);
461         return false;
462     }
463 
464     glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_currentFBO);
465     return true;
466 }
467 
Destroy()468 void CGLFramebufferEXT::Destroy()
469 {
470     if (m_fbo == 0) return;
471 
472     if (m_currentFBO == m_fbo)
473         glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
474 
475     glDeleteFramebuffersEXT(1, &m_fbo);
476     m_fbo = 0;
477 
478     if (m_colorRenderbuffer != 0)
479     {
480         glDeleteRenderbuffersEXT(1, &m_colorRenderbuffer);
481         m_colorRenderbuffer = 0;
482     }
483 
484     if (m_colorTexture != 0)
485     {
486         glDeleteTextures(1, &m_colorTexture);
487         m_colorTexture = 0;
488     }
489 
490     if (m_depthRenderbuffer != 0)
491     {
492         glDeleteRenderbuffersEXT(1, &m_depthRenderbuffer);
493         m_depthRenderbuffer = 0;
494     }
495 
496     if (m_depthTexture != 0)
497     {
498         glDeleteTextures(1, &m_depthTexture);
499         m_depthTexture = 0;
500     }
501 
502     m_width = 0;
503     m_height = 0;
504     m_depth = 0;
505     m_samples = 0;
506 }
507 
IsDefault()508 bool CGLFramebufferEXT::IsDefault()
509 {
510     return false;
511 }
512 
513 //! Returns width of buffers in this framebuffer
GetWidth()514 int CGLFramebufferEXT::GetWidth()
515 {
516     return m_width;
517 }
518 
519 //! Returns height of buffers in this framebuffer
GetHeight()520 int CGLFramebufferEXT::GetHeight()
521 {
522     return m_height;
523 }
524 
525 //! Returns depth size in bits
GetDepth()526 int CGLFramebufferEXT::GetDepth()
527 {
528     return m_depth;
529 }
530 
531 //! Returns number of samples or 1 if multisampling is not supported
GetSamples()532 int CGLFramebufferEXT::GetSamples()
533 {
534     return m_samples;
535 }
536 
537 //! Returns texture that contains color buffer or 0 if not available
GetColorTexture()538 int CGLFramebufferEXT::GetColorTexture()
539 {
540     return m_colorTexture;
541 }
542 
543 //! Returns texture that contains depth buffer or 0 if not available
GetDepthTexture()544 int CGLFramebufferEXT::GetDepthTexture()
545 {
546     return m_depthTexture;
547 }
548 
549 //! Binds this framebuffer to context
Bind()550 void CGLFramebufferEXT::Bind()
551 {
552     glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_fbo);
553     m_currentFBO = m_fbo;
554 }
555 
556 //! Unbinds this framebuffer from context
Unbind()557 void CGLFramebufferEXT::Unbind()
558 {
559     glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
560     m_currentFBO = 0;
561 }
562 
CopyToScreen(int fromX,int fromY,int fromWidth,int fromHeight,int toX,int toY,int toWidth,int toHeight)563 void CGLFramebufferEXT::CopyToScreen(int fromX, int fromY, int fromWidth, int fromHeight,
564         int toX, int toY, int toWidth, int toHeight)
565 {
566     glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, m_fbo);
567     glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
568 
569     glBlitFramebufferEXT(fromX, fromY, fromX + fromWidth, fromY + fromHeight,
570             toX, toY, toX + toWidth, toY + toHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);
571 
572     glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_currentFBO);
573 }
574 
575 } // end of Gfx
576