]> git.lizzy.rs Git - irrlicht.git/blob - source/Irrlicht/COpenGLCoreRenderTarget.h
f893c17416a64e90c062547db17eeda55ba69c72
[irrlicht.git] / source / Irrlicht / COpenGLCoreRenderTarget.h
1 // Copyright (C) 2015 Patryk Nadrowski\r
2 // This file is part of the "Irrlicht Engine".\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h\r
4 \r
5 #ifndef __C_OGLCORE_RENDER_TARGET_H_INCLUDED__\r
6 #define __C_OGLCORE_RENDER_TARGET_H_INCLUDED__\r
7 \r
8 \r
9 #if defined(_IRR_COMPILE_WITH_OPENGL_) || defined(_IRR_COMPILE_WITH_OGLES1_) || defined(_IRR_COMPILE_WITH_OGLES2_)\r
10 \r
11 #include "IRenderTarget.h"\r
12 \r
13 #ifndef GL_FRAMEBUFFER_INCOMPLETE_FORMATS\r
14 #define GL_FRAMEBUFFER_INCOMPLETE_FORMATS GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT\r
15 #endif\r
16 \r
17 #ifndef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS\r
18 #define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT\r
19 #endif\r
20 \r
21 namespace irr\r
22 {\r
23 namespace video\r
24 {\r
25 \r
26 template <class TOpenGLDriver, class TOpenGLTexture>\r
27 class COpenGLCoreRenderTarget : public IRenderTarget\r
28 {\r
29 public:\r
30         COpenGLCoreRenderTarget(TOpenGLDriver* driver) : AssignedDepth(false), AssignedStencil(false), RequestTextureUpdate(false), RequestDepthStencilUpdate(false),\r
31                 BufferID(0), ColorAttachment(0), MultipleRenderTarget(0), Driver(driver)\r
32         {\r
33 #ifdef _DEBUG\r
34                 setDebugName("COpenGLCoreRenderTarget");\r
35 #endif\r
36 \r
37                 DriverType = Driver->getDriverType();\r
38 \r
39                 Size = Driver->getScreenSize();\r
40 \r
41                 ColorAttachment = Driver->getFeature().ColorAttachment;\r
42                 MultipleRenderTarget = Driver->getFeature().MultipleRenderTarget;\r
43 \r
44                 if (ColorAttachment > 0)\r
45                         Driver->irrGlGenFramebuffers(1, &BufferID);\r
46 \r
47                 AssignedTextures.set_used(static_cast<u32>(ColorAttachment));\r
48 \r
49                 for (u32 i = 0; i < AssignedTextures.size(); ++i)\r
50                         AssignedTextures[i] = GL_NONE;\r
51         }\r
52 \r
53         virtual ~COpenGLCoreRenderTarget()\r
54         {\r
55                 if (ColorAttachment > 0 && BufferID != 0)\r
56                         Driver->irrGlDeleteFramebuffers(1, &BufferID);\r
57 \r
58                 for (u32 i = 0; i < Textures.size(); ++i)\r
59                 {\r
60                         if (Textures[i])\r
61                                 Textures[i]->drop();\r
62                 }\r
63 \r
64                 if (DepthStencil)\r
65                         DepthStencil->drop();\r
66         }\r
67 \r
68         void setTextures(ITexture* const * textures, u32 numTextures, ITexture* depthStencil, const E_CUBE_SURFACE* cubeSurfaces, u32 numCubeSurfaces) override\r
69         {\r
70                 bool needSizeUpdate = false;\r
71 \r
72                 // Set color attachments.\r
73                 if (!Textures.equals(textures, numTextures) || !CubeSurfaces.equals(cubeSurfaces, numCubeSurfaces))\r
74                 {\r
75                         needSizeUpdate = true;\r
76 \r
77                         core::array<ITexture*> prevTextures(Textures);\r
78 \r
79                         if (numTextures > static_cast<u32>(ColorAttachment))\r
80                         {\r
81                                 core::stringc message = "This GPU supports up to ";\r
82                                 message += static_cast<u32>(ColorAttachment);\r
83                                 message += " textures per render target.";\r
84 \r
85                                 os::Printer::log(message.c_str(), ELL_WARNING);\r
86                         }\r
87 \r
88                         Textures.set_used(core::min_(numTextures, static_cast<u32>(ColorAttachment)));\r
89 \r
90                         for (u32 i = 0; i < Textures.size(); ++i)\r
91                         {\r
92                                 TOpenGLTexture* currentTexture = (textures[i] && textures[i]->getDriverType() == DriverType) ? static_cast<TOpenGLTexture*>(textures[i]) : 0;\r
93 \r
94                                 GLuint textureID = 0;\r
95 \r
96                                 if (currentTexture)\r
97                                 {\r
98                                         textureID = currentTexture->getOpenGLTextureName();\r
99                                 }\r
100 \r
101                                 if (textureID != 0)\r
102                                 {\r
103                                         Textures[i] = textures[i];\r
104                                         Textures[i]->grab();\r
105                                 }\r
106                                 else\r
107                                 {\r
108                                         Textures[i] = 0;\r
109                                 }\r
110                         }\r
111 \r
112                         for (u32 i = 0; i < prevTextures.size(); ++i)\r
113                         {\r
114                                 if (prevTextures[i])\r
115                                         prevTextures[i]->drop();\r
116                         }\r
117 \r
118                         RequestTextureUpdate = true;\r
119                 }\r
120 \r
121                 if (!CubeSurfaces.equals(cubeSurfaces, numCubeSurfaces))\r
122                 {\r
123                         CubeSurfaces.set_data(cubeSurfaces, numCubeSurfaces);\r
124                         RequestTextureUpdate = true;\r
125                 }\r
126 \r
127                 // Set depth and stencil attachments.\r
128                 if (DepthStencil != depthStencil)\r
129                 {\r
130                         if (DepthStencil)\r
131                         {\r
132                                 DepthStencil->drop();\r
133                                 DepthStencil = 0;\r
134                         }\r
135 \r
136                         needSizeUpdate = true;\r
137                         TOpenGLTexture* currentTexture = (depthStencil && depthStencil->getDriverType() == DriverType) ? static_cast<TOpenGLTexture*>(depthStencil) : 0;\r
138 \r
139                         if (currentTexture)\r
140                         {       \r
141                                 if (currentTexture->getType() == ETT_2D)\r
142                                 {\r
143                                         GLuint textureID = currentTexture->getOpenGLTextureName();\r
144 \r
145                                         const ECOLOR_FORMAT textureFormat = (textureID != 0) ? depthStencil->getColorFormat() : ECF_UNKNOWN;\r
146                                         if (IImage::isDepthFormat(textureFormat))\r
147                                         {\r
148                                                 DepthStencil = depthStencil;\r
149                                                 DepthStencil->grab();\r
150                                         }\r
151                                         else\r
152                                         {\r
153                                                 os::Printer::log("Ignoring depth/stencil texture without depth color format.", ELL_WARNING);\r
154                                         }\r
155                                 }\r
156                                 else\r
157                                 {\r
158                                         os::Printer::log("This driver doesn't support depth/stencil to cubemaps.", ELL_WARNING);\r
159                                 }\r
160                         }\r
161 \r
162                         RequestDepthStencilUpdate = true;\r
163                 }\r
164 \r
165                 if (needSizeUpdate)\r
166                 {\r
167                         // Set size required for a viewport.\r
168 \r
169                         ITexture* firstTexture = getTexture();\r
170 \r
171                         if (firstTexture)\r
172                                 Size = firstTexture->getSize();\r
173                         else\r
174                         {\r
175                                 if (DepthStencil)\r
176                                         Size = DepthStencil->getSize();\r
177                                 else\r
178                                         Size = Driver->getScreenSize();\r
179                         }\r
180                 }\r
181         }\r
182 \r
183         void update()\r
184         {\r
185                 if (RequestTextureUpdate || RequestDepthStencilUpdate)\r
186                 {\r
187                         // Set color attachments.\r
188 \r
189                         if (RequestTextureUpdate)\r
190                         {\r
191                                 // Set new color textures.\r
192 \r
193                                 const u32 textureSize = core::min_(Textures.size(), AssignedTextures.size());\r
194 \r
195                                 for (u32 i = 0; i < textureSize; ++i)\r
196                                 {\r
197                                         TOpenGLTexture* currentTexture = static_cast<TOpenGLTexture*>(Textures[i]);\r
198                                         GLuint textureID = currentTexture ? currentTexture->getOpenGLTextureName() : 0;\r
199 \r
200                                         if (textureID != 0)\r
201                                         {\r
202                                                 AssignedTextures[i] = GL_COLOR_ATTACHMENT0 + i;\r
203                                                 GLenum textarget = currentTexture->getType() == ETT_2D ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP_POSITIVE_X + (int)CubeSurfaces[i];\r
204                                                 Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, AssignedTextures[i], textarget, textureID, 0);\r
205 #ifdef _DEBUG\r
206                                                 Driver->testGLError(__LINE__);\r
207 #endif\r
208                                         }\r
209                                         else if (AssignedTextures[i] != GL_NONE)\r
210                                         {\r
211                                                 AssignedTextures[i] = GL_NONE;\r
212                                                 Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, AssignedTextures[i], GL_TEXTURE_2D, 0, 0);\r
213 \r
214                                                 os::Printer::log("Error: Could not set render target.", ELL_ERROR);\r
215                                         }\r
216                                 }\r
217 \r
218                                 // Reset other render target channels.\r
219 \r
220                                 for (u32 i = textureSize; i < AssignedTextures.size(); ++i)\r
221                                 {\r
222                                         if (AssignedTextures[i] != GL_NONE)\r
223                                         {\r
224                                                 Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, AssignedTextures[i], GL_TEXTURE_2D, 0, 0);\r
225                                                 AssignedTextures[i] = GL_NONE;\r
226                                         }\r
227                                 }\r
228 \r
229                                 RequestTextureUpdate = false;\r
230                         }\r
231 \r
232                         // Set depth and stencil attachments.\r
233 \r
234                         if (RequestDepthStencilUpdate)\r
235                         {\r
236                                 const ECOLOR_FORMAT textureFormat = (DepthStencil) ? DepthStencil->getColorFormat() : ECF_UNKNOWN;\r
237 \r
238                                 if (IImage::isDepthFormat(textureFormat))\r
239                                 {\r
240                                         GLuint textureID = static_cast<TOpenGLTexture*>(DepthStencil)->getOpenGLTextureName();\r
241 \r
242 #ifdef _IRR_EMSCRIPTEN_PLATFORM_        // The WEBGL_depth_texture extension does not allow attaching stencil+depth separate.\r
243                                         if (textureFormat == ECF_D24S8)\r
244                                         {\r
245                                                 GLenum attachment = 0x821A; // GL_DEPTH_STENCIL_ATTACHMENT\r
246                                                 Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, textureID, 0);\r
247                                                 AssignedStencil = true;\r
248                                         }\r
249                                         else\r
250                                         {\r
251                                                 Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textureID, 0);\r
252                                                 AssignedStencil = false;\r
253                                         }\r
254 #else\r
255                                         Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textureID, 0);\r
256 \r
257                                         if (textureFormat == ECF_D24S8)\r
258                                         {\r
259                                                 Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, textureID, 0);\r
260 \r
261                                                 AssignedStencil = true;\r
262                                         }\r
263                                         else\r
264                                         {\r
265                                                 if (AssignedStencil)\r
266                                                         Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);\r
267 \r
268                                                 AssignedStencil = false;\r
269                                         }\r
270 #endif\r
271                                         AssignedDepth = true;\r
272                                 }\r
273                                 else\r
274                                 {\r
275                                         if (AssignedDepth)\r
276                                                 Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);\r
277 \r
278                                         if (AssignedStencil)\r
279                                                 Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);\r
280 \r
281                                         AssignedDepth = false;\r
282                                         AssignedStencil = false;\r
283                                 }\r
284 #ifdef _DEBUG\r
285                                 Driver->testGLError(__LINE__);\r
286 #endif\r
287 \r
288                                 RequestDepthStencilUpdate = false;\r
289                         }\r
290 \r
291                         // Configure drawing operation.\r
292 \r
293                         if (ColorAttachment > 0 && BufferID != 0)\r
294                         {\r
295                                 const u32 textureSize = Textures.size();\r
296 \r
297                                 if (textureSize == 0)\r
298                                         Driver->irrGlDrawBuffer(GL_NONE);\r
299                                 else if (textureSize == 1 || MultipleRenderTarget == 0)\r
300                                         Driver->irrGlDrawBuffer(GL_COLOR_ATTACHMENT0);\r
301                                 else\r
302                                 {\r
303                                         const u32 bufferCount = core::min_(MultipleRenderTarget, core::min_(textureSize, AssignedTextures.size()));\r
304 \r
305                                         Driver->irrGlDrawBuffers(bufferCount, AssignedTextures.pointer());\r
306                                 }\r
307 \r
308 #ifdef _DEBUG\r
309                                 Driver->testGLError(__LINE__);\r
310 #endif\r
311 \r
312                         }\r
313 \r
314 #ifdef _DEBUG\r
315                         checkFBO(Driver);\r
316 #endif\r
317                 }\r
318         }\r
319 \r
320         GLuint getBufferID() const\r
321         {\r
322                 return BufferID;\r
323         }\r
324 \r
325         const core::dimension2d<u32>& getSize() const\r
326         {\r
327                 return Size;\r
328         }\r
329 \r
330         ITexture* getTexture() const\r
331         {\r
332                 for (u32 i = 0; i < Textures.size(); ++i)\r
333                 {\r
334                         if (Textures[i])\r
335                                 return Textures[i];\r
336                 }\r
337 \r
338                 return 0;\r
339         }\r
340 \r
341 protected:\r
342         bool checkFBO(TOpenGLDriver* driver)\r
343         {\r
344                 if (ColorAttachment == 0)\r
345                         return true;\r
346 \r
347                 GLenum status = driver->irrGlCheckFramebufferStatus(GL_FRAMEBUFFER);\r
348 \r
349                 switch (status)\r
350                 {\r
351                         case GL_FRAMEBUFFER_COMPLETE:\r
352                                 return true;\r
353                         case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:\r
354                                 os::Printer::log("FBO has invalid read buffer", ELL_ERROR);\r
355                                 break;\r
356                         case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:\r
357                                 os::Printer::log("FBO has invalid draw buffer", ELL_ERROR);\r
358                                 break;\r
359                         case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:\r
360                                 os::Printer::log("FBO has one or several incomplete image attachments", ELL_ERROR);\r
361                                 break;\r
362                         case GL_FRAMEBUFFER_INCOMPLETE_FORMATS:\r
363                                 os::Printer::log("FBO has one or several image attachments with different internal formats", ELL_ERROR);\r
364                                 break;\r
365                         case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:\r
366                                 os::Printer::log("FBO has one or several image attachments with different dimensions", ELL_ERROR);\r
367                                 break;\r
368                         case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:\r
369                                 os::Printer::log("FBO missing an image attachment", ELL_ERROR);\r
370                                 break;\r
371                         case GL_FRAMEBUFFER_UNSUPPORTED:\r
372                                 os::Printer::log("FBO format unsupported", ELL_ERROR);\r
373                                 break;\r
374                         default:\r
375                                 os::Printer::log("FBO error", ELL_ERROR);\r
376                                 break;\r
377                 }\r
378 \r
379                 return false;\r
380         }\r
381 \r
382         core::array<GLenum> AssignedTextures;\r
383         bool AssignedDepth;\r
384         bool AssignedStencil;\r
385 \r
386         bool RequestTextureUpdate;\r
387         bool RequestDepthStencilUpdate;\r
388 \r
389         GLuint BufferID;\r
390 \r
391         core::dimension2d<u32> Size;\r
392 \r
393         u32 ColorAttachment;\r
394         u32 MultipleRenderTarget;\r
395 \r
396         TOpenGLDriver* Driver;\r
397 };\r
398 \r
399 }\r
400 }\r
401 \r
402 #endif\r
403 #endif\r