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