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
5 #ifndef __C_OGLCORE_RENDER_TARGET_H_INCLUDED__
\r
6 #define __C_OGLCORE_RENDER_TARGET_H_INCLUDED__
\r
9 #include "IRenderTarget.h"
\r
11 #ifndef GL_FRAMEBUFFER_INCOMPLETE_FORMATS
\r
12 #define GL_FRAMEBUFFER_INCOMPLETE_FORMATS GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT
\r
15 #ifndef GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS
\r
16 #define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT
\r
24 template <class TOpenGLDriver, class TOpenGLTexture>
\r
25 class COpenGLCoreRenderTarget : public IRenderTarget
\r
28 COpenGLCoreRenderTarget(TOpenGLDriver* driver) : AssignedDepth(false), AssignedStencil(false), RequestTextureUpdate(false), RequestDepthStencilUpdate(false),
\r
29 BufferID(0), ColorAttachment(0), MultipleRenderTarget(0), Driver(driver)
\r
32 setDebugName("COpenGLCoreRenderTarget");
\r
35 DriverType = Driver->getDriverType();
\r
37 Size = Driver->getScreenSize();
\r
39 ColorAttachment = Driver->getFeature().ColorAttachment;
\r
40 MultipleRenderTarget = Driver->getFeature().MultipleRenderTarget;
\r
42 if (ColorAttachment > 0)
\r
43 Driver->irrGlGenFramebuffers(1, &BufferID);
\r
45 AssignedTextures.set_used(static_cast<u32>(ColorAttachment));
\r
47 for (u32 i = 0; i < AssignedTextures.size(); ++i)
\r
48 AssignedTextures[i] = GL_NONE;
\r
51 virtual ~COpenGLCoreRenderTarget()
\r
53 if (ColorAttachment > 0 && BufferID != 0)
\r
54 Driver->irrGlDeleteFramebuffers(1, &BufferID);
\r
56 for (u32 i = 0; i < Textures.size(); ++i)
\r
59 Textures[i]->drop();
\r
63 DepthStencil->drop();
\r
66 void setTextures(ITexture* const * textures, u32 numTextures, ITexture* depthStencil, const E_CUBE_SURFACE* cubeSurfaces, u32 numCubeSurfaces) override
\r
68 bool needSizeUpdate = false;
\r
70 // Set color attachments.
\r
71 if (!Textures.equals(textures, numTextures) || !CubeSurfaces.equals(cubeSurfaces, numCubeSurfaces))
\r
73 needSizeUpdate = true;
\r
75 core::array<ITexture*> prevTextures(Textures);
\r
77 if (numTextures > static_cast<u32>(ColorAttachment))
\r
79 core::stringc message = "This GPU supports up to ";
\r
80 message += static_cast<u32>(ColorAttachment);
\r
81 message += " textures per render target.";
\r
83 os::Printer::log(message.c_str(), ELL_WARNING);
\r
86 Textures.set_used(core::min_(numTextures, static_cast<u32>(ColorAttachment)));
\r
88 for (u32 i = 0; i < Textures.size(); ++i)
\r
90 TOpenGLTexture* currentTexture = (textures[i] && textures[i]->getDriverType() == DriverType) ? static_cast<TOpenGLTexture*>(textures[i]) : 0;
\r
92 GLuint textureID = 0;
\r
96 textureID = currentTexture->getOpenGLTextureName();
\r
101 Textures[i] = textures[i];
\r
102 Textures[i]->grab();
\r
110 for (u32 i = 0; i < prevTextures.size(); ++i)
\r
112 if (prevTextures[i])
\r
113 prevTextures[i]->drop();
\r
116 RequestTextureUpdate = true;
\r
119 if (!CubeSurfaces.equals(cubeSurfaces, numCubeSurfaces))
\r
121 CubeSurfaces.set_data(cubeSurfaces, numCubeSurfaces);
\r
122 RequestTextureUpdate = true;
\r
125 // Set depth and stencil attachments.
\r
126 if (DepthStencil != depthStencil)
\r
130 DepthStencil->drop();
\r
134 needSizeUpdate = true;
\r
135 TOpenGLTexture* currentTexture = (depthStencil && depthStencil->getDriverType() == DriverType) ? static_cast<TOpenGLTexture*>(depthStencil) : 0;
\r
137 if (currentTexture)
\r
139 if (currentTexture->getType() == ETT_2D)
\r
141 GLuint textureID = currentTexture->getOpenGLTextureName();
\r
143 const ECOLOR_FORMAT textureFormat = (textureID != 0) ? depthStencil->getColorFormat() : ECF_UNKNOWN;
\r
144 if (IImage::isDepthFormat(textureFormat))
\r
146 DepthStencil = depthStencil;
\r
147 DepthStencil->grab();
\r
151 os::Printer::log("Ignoring depth/stencil texture without depth color format.", ELL_WARNING);
\r
156 os::Printer::log("This driver doesn't support depth/stencil to cubemaps.", ELL_WARNING);
\r
160 RequestDepthStencilUpdate = true;
\r
163 if (needSizeUpdate)
\r
165 // Set size required for a viewport.
\r
167 ITexture* firstTexture = getTexture();
\r
170 Size = firstTexture->getSize();
\r
174 Size = DepthStencil->getSize();
\r
176 Size = Driver->getScreenSize();
\r
183 if (RequestTextureUpdate || RequestDepthStencilUpdate)
\r
185 // Set color attachments.
\r
187 if (RequestTextureUpdate)
\r
189 // Set new color textures.
\r
191 const u32 textureSize = core::min_(Textures.size(), AssignedTextures.size());
\r
193 for (u32 i = 0; i < textureSize; ++i)
\r
195 TOpenGLTexture* currentTexture = static_cast<TOpenGLTexture*>(Textures[i]);
\r
196 GLuint textureID = currentTexture ? currentTexture->getOpenGLTextureName() : 0;
\r
198 if (textureID != 0)
\r
200 AssignedTextures[i] = GL_COLOR_ATTACHMENT0 + i;
\r
201 GLenum textarget = currentTexture->getType() == ETT_2D ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP_POSITIVE_X + (int)CubeSurfaces[i];
\r
202 Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, AssignedTextures[i], textarget, textureID, 0);
\r
204 Driver->testGLError(__LINE__);
\r
207 else if (AssignedTextures[i] != GL_NONE)
\r
209 AssignedTextures[i] = GL_NONE;
\r
210 Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, AssignedTextures[i], GL_TEXTURE_2D, 0, 0);
\r
212 os::Printer::log("Error: Could not set render target.", ELL_ERROR);
\r
216 // Reset other render target channels.
\r
218 for (u32 i = textureSize; i < AssignedTextures.size(); ++i)
\r
220 if (AssignedTextures[i] != GL_NONE)
\r
222 Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, AssignedTextures[i], GL_TEXTURE_2D, 0, 0);
\r
223 AssignedTextures[i] = GL_NONE;
\r
227 RequestTextureUpdate = false;
\r
230 // Set depth and stencil attachments.
\r
232 if (RequestDepthStencilUpdate)
\r
234 const ECOLOR_FORMAT textureFormat = (DepthStencil) ? DepthStencil->getColorFormat() : ECF_UNKNOWN;
\r
236 if (IImage::isDepthFormat(textureFormat))
\r
238 GLuint textureID = static_cast<TOpenGLTexture*>(DepthStencil)->getOpenGLTextureName();
\r
240 #ifdef _IRR_EMSCRIPTEN_PLATFORM_ // The WEBGL_depth_texture extension does not allow attaching stencil+depth separate.
\r
241 if (textureFormat == ECF_D24S8)
\r
243 GLenum attachment = 0x821A; // GL_DEPTH_STENCIL_ATTACHMENT
\r
244 Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, textureID, 0);
\r
245 AssignedStencil = true;
\r
249 Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textureID, 0);
\r
250 AssignedStencil = false;
\r
253 Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textureID, 0);
\r
255 if (textureFormat == ECF_D24S8)
\r
257 Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, textureID, 0);
\r
259 AssignedStencil = true;
\r
263 if (AssignedStencil)
\r
264 Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
\r
266 AssignedStencil = false;
\r
269 AssignedDepth = true;
\r
274 Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
\r
276 if (AssignedStencil)
\r
277 Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
\r
279 AssignedDepth = false;
\r
280 AssignedStencil = false;
\r
283 Driver->testGLError(__LINE__);
\r
286 RequestDepthStencilUpdate = false;
\r
289 // Configure drawing operation.
\r
291 if (ColorAttachment > 0 && BufferID != 0)
\r
293 const u32 textureSize = Textures.size();
\r
295 if (textureSize == 0)
\r
296 Driver->irrGlDrawBuffer(GL_NONE);
\r
297 else if (textureSize == 1 || MultipleRenderTarget == 0)
\r
298 Driver->irrGlDrawBuffer(GL_COLOR_ATTACHMENT0);
\r
301 const u32 bufferCount = core::min_(MultipleRenderTarget, core::min_(textureSize, AssignedTextures.size()));
\r
303 Driver->irrGlDrawBuffers(bufferCount, AssignedTextures.pointer());
\r
307 Driver->testGLError(__LINE__);
\r
318 GLuint getBufferID() const
\r
323 const core::dimension2d<u32>& getSize() const
\r
328 ITexture* getTexture() const
\r
330 for (u32 i = 0; i < Textures.size(); ++i)
\r
333 return Textures[i];
\r
340 bool checkFBO(TOpenGLDriver* driver)
\r
342 if (ColorAttachment == 0)
\r
345 GLenum status = driver->irrGlCheckFramebufferStatus(GL_FRAMEBUFFER);
\r
349 case GL_FRAMEBUFFER_COMPLETE:
\r
351 case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
\r
352 os::Printer::log("FBO has invalid read buffer", ELL_ERROR);
\r
354 case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
\r
355 os::Printer::log("FBO has invalid draw buffer", ELL_ERROR);
\r
357 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
\r
358 os::Printer::log("FBO has one or several incomplete image attachments", ELL_ERROR);
\r
360 case GL_FRAMEBUFFER_INCOMPLETE_FORMATS:
\r
361 os::Printer::log("FBO has one or several image attachments with different internal formats", ELL_ERROR);
\r
363 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
\r
364 os::Printer::log("FBO has one or several image attachments with different dimensions", ELL_ERROR);
\r
366 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
\r
367 os::Printer::log("FBO missing an image attachment", ELL_ERROR);
\r
369 case GL_FRAMEBUFFER_UNSUPPORTED:
\r
370 os::Printer::log("FBO format unsupported", ELL_ERROR);
\r
373 os::Printer::log("FBO error", ELL_ERROR);
\r
380 core::array<GLenum> AssignedTextures;
\r
381 bool AssignedDepth;
\r
382 bool AssignedStencil;
\r
384 bool RequestTextureUpdate;
\r
385 bool RequestDepthStencilUpdate;
\r
389 core::dimension2d<u32> Size;
\r
391 u32 ColorAttachment;
\r
392 u32 MultipleRenderTarget;
\r
394 TOpenGLDriver* Driver;
\r