irrlicht/source/Irrlicht/COpenGLCoreRenderTarget.h
cutealien d03881b83a Reworking IRenderTarget interface to avoid constant memory allocations.
setTexture functions for single textures (more or less the usual case) IRenderTarget no longer need memory allocations 
on each call.
Also calling IRenderTarget::setTexture with a nullpointer no longer sets a rendertarget with an array which contains a single nullpointer but clears the array instead.


git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6243 dfc29bdd-3216-0410-991c-e03cc46cb475
2021-08-26 16:45:20 +00:00

384 lines
9.8 KiB
C++

// Copyright (C) 2015 Patryk Nadrowski
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#ifndef __C_OGLCORE_RENDER_TARGET_H_INCLUDED__
#define __C_OGLCORE_RENDER_TARGET_H_INCLUDED__
#include "IrrCompileConfig.h"
#if defined(_IRR_COMPILE_WITH_OPENGL_) || defined(_IRR_COMPILE_WITH_OGLES1_) || defined(_IRR_COMPILE_WITH_OGLES2_)
#include "IRenderTarget.h"
namespace irr
{
namespace video
{
template <class TOpenGLDriver, class TOpenGLTexture>
class COpenGLCoreRenderTarget : public IRenderTarget
{
public:
COpenGLCoreRenderTarget(TOpenGLDriver* driver) : AssignedDepth(false), AssignedStencil(false), RequestTextureUpdate(false), RequestDepthStencilUpdate(false),
BufferID(0), ColorAttachment(0), MultipleRenderTarget(0), Driver(driver)
{
#ifdef _DEBUG
setDebugName("COpenGLCoreRenderTarget");
#endif
DriverType = Driver->getDriverType();
Size = Driver->getScreenSize();
ColorAttachment = Driver->getFeature().ColorAttachment;
MultipleRenderTarget = Driver->getFeature().MultipleRenderTarget;
if (ColorAttachment > 0)
Driver->irrGlGenFramebuffers(1, &BufferID);
AssignedTextures.set_used(static_cast<u32>(ColorAttachment));
for (u32 i = 0; i < AssignedTextures.size(); ++i)
AssignedTextures[i] = GL_NONE;
}
virtual ~COpenGLCoreRenderTarget()
{
if (ColorAttachment > 0 && BufferID != 0)
Driver->irrGlDeleteFramebuffers(1, &BufferID);
for (u32 i = 0; i < Textures.size(); ++i)
{
if (Textures[i])
Textures[i]->drop();
}
if (DepthStencil)
DepthStencil->drop();
}
virtual void setTextures(ITexture* const * textures, u32 numTextures, ITexture* depthStencil, const E_CUBE_SURFACE* cubeSurfaces, u32 numCubeSurfaces) _IRR_OVERRIDE_
{
bool needSizeUpdate = false;
// Set color attachments.
if (!Textures.equals(textures, numTextures) || !CubeSurfaces.equals(cubeSurfaces, numCubeSurfaces))
{
needSizeUpdate = true;
core::array<ITexture*> prevTextures(Textures);
if (numTextures > static_cast<u32>(ColorAttachment))
{
core::stringc message = "This GPU supports up to ";
message += static_cast<u32>(ColorAttachment);
message += " textures per render target.";
os::Printer::log(message.c_str(), ELL_WARNING);
}
Textures.set_used(core::min_(numTextures, static_cast<u32>(ColorAttachment)));
for (u32 i = 0; i < Textures.size(); ++i)
{
TOpenGLTexture* currentTexture = (textures[i] && textures[i]->getDriverType() == DriverType) ? static_cast<TOpenGLTexture*>(textures[i]) : 0;
GLuint textureID = 0;
if (currentTexture)
{
textureID = currentTexture->getOpenGLTextureName();
}
if (textureID != 0)
{
Textures[i] = textures[i];
Textures[i]->grab();
}
else
{
Textures[i] = 0;
}
}
for (u32 i = 0; i < prevTextures.size(); ++i)
{
if (prevTextures[i])
prevTextures[i]->drop();
}
RequestTextureUpdate = true;
}
if (!CubeSurfaces.equals(cubeSurfaces, numCubeSurfaces))
{
CubeSurfaces.set_data(cubeSurfaces, numCubeSurfaces);
RequestTextureUpdate = true;
}
// Set depth and stencil attachments.
if (DepthStencil != depthStencil)
{
if (DepthStencil)
{
DepthStencil->drop();
DepthStencil = 0;
}
needSizeUpdate = true;
TOpenGLTexture* currentTexture = (depthStencil && depthStencil->getDriverType() == DriverType) ? static_cast<TOpenGLTexture*>(depthStencil) : 0;
if (currentTexture)
{
if (currentTexture->getType() == ETT_2D)
{
GLuint textureID = currentTexture->getOpenGLTextureName();
const ECOLOR_FORMAT textureFormat = (textureID != 0) ? depthStencil->getColorFormat() : ECF_UNKNOWN;
if (IImage::isDepthFormat(textureFormat))
{
DepthStencil = depthStencil;
DepthStencil->grab();
}
else
{
os::Printer::log("Ignoring depth/stencil texture without depth color format.", ELL_WARNING);
}
}
else
{
os::Printer::log("This driver doesn't support depth/stencil to cubemaps.", ELL_WARNING);
}
}
RequestDepthStencilUpdate = true;
}
if (needSizeUpdate)
{
// Set size required for a viewport.
ITexture* firstTexture = getTexture();
if (firstTexture)
Size = firstTexture->getSize();
else
{
if (DepthStencil)
Size = DepthStencil->getSize();
else
Size = Driver->getScreenSize();
}
}
}
void update()
{
if (RequestTextureUpdate || RequestDepthStencilUpdate)
{
// Set color attachments.
if (RequestTextureUpdate)
{
// Set new color textures.
const u32 textureSize = core::min_(Textures.size(), AssignedTextures.size());
for (u32 i = 0; i < textureSize; ++i)
{
TOpenGLTexture* currentTexture = static_cast<TOpenGLTexture*>(Textures[i]);
GLuint textureID = currentTexture ? currentTexture->getOpenGLTextureName() : 0;
if (textureID != 0)
{
AssignedTextures[i] = GL_COLOR_ATTACHMENT0 + i;
GLenum textarget = currentTexture->getType() == ETT_2D ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP_POSITIVE_X + (int)CubeSurfaces[i];
Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, AssignedTextures[i], textarget, textureID, 0);
#ifdef _DEBUG
Driver->testGLError(__LINE__);
#endif
}
else if (AssignedTextures[i] != GL_NONE)
{
AssignedTextures[i] = GL_NONE;
Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, AssignedTextures[i], GL_TEXTURE_2D, 0, 0);
os::Printer::log("Error: Could not set render target.", ELL_ERROR);
}
}
// Reset other render target channels.
for (u32 i = textureSize; i < AssignedTextures.size(); ++i)
{
if (AssignedTextures[i] != GL_NONE)
{
Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, AssignedTextures[i], GL_TEXTURE_2D, 0, 0);
AssignedTextures[i] = GL_NONE;
}
}
RequestTextureUpdate = false;
}
// Set depth and stencil attachments.
if (RequestDepthStencilUpdate)
{
const ECOLOR_FORMAT textureFormat = (DepthStencil) ? DepthStencil->getColorFormat() : ECF_UNKNOWN;
if (IImage::isDepthFormat(textureFormat))
{
GLuint textureID = static_cast<TOpenGLTexture*>(DepthStencil)->getOpenGLTextureName();
Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textureID, 0);
if (textureFormat == ECF_D24S8)
{
Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, textureID, 0);
AssignedStencil = true;
}
else
{
if (AssignedStencil)
Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
AssignedStencil = false;
}
AssignedDepth = true;
}
else
{
if (AssignedDepth)
Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
if (AssignedStencil)
Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
AssignedDepth = false;
AssignedStencil = false;
}
#ifdef _DEBUG
Driver->testGLError(__LINE__);
#endif
RequestDepthStencilUpdate = false;
}
// Configure drawing operation.
if (ColorAttachment > 0 && BufferID != 0)
{
const u32 textureSize = Textures.size();
if (textureSize == 0)
Driver->irrGlDrawBuffer(GL_NONE);
else if (textureSize == 1 || MultipleRenderTarget == 0)
Driver->irrGlDrawBuffer(GL_COLOR_ATTACHMENT0);
else
{
const u32 bufferCount = core::min_(MultipleRenderTarget, core::min_(textureSize, AssignedTextures.size()));
Driver->irrGlDrawBuffers(bufferCount, AssignedTextures.pointer());
}
#ifdef _DEBUG
Driver->testGLError(__LINE__);
#endif
}
#ifdef _DEBUG
checkFBO(Driver);
#endif
}
}
GLuint getBufferID() const
{
return BufferID;
}
const core::dimension2d<u32>& getSize() const
{
return Size;
}
ITexture* getTexture() const
{
for (u32 i = 0; i < Textures.size(); ++i)
{
if (Textures[i])
return Textures[i];
}
return 0;
}
protected:
bool checkFBO(TOpenGLDriver* driver)
{
if (ColorAttachment == 0)
return true;
GLenum status = driver->irrGlCheckFramebufferStatus(GL_FRAMEBUFFER);
switch (status)
{
case GL_FRAMEBUFFER_COMPLETE:
return true;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
os::Printer::log("FBO has invalid read buffer", ELL_ERROR);
break;
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
os::Printer::log("FBO has invalid draw buffer", ELL_ERROR);
break;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
os::Printer::log("FBO has one or several incomplete image attachments", ELL_ERROR);
break;
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS:
os::Printer::log("FBO has one or several image attachments with different internal formats", ELL_ERROR);
break;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
os::Printer::log("FBO has one or several image attachments with different dimensions", ELL_ERROR);
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
os::Printer::log("FBO missing an image attachment", ELL_ERROR);
break;
case GL_FRAMEBUFFER_UNSUPPORTED:
os::Printer::log("FBO format unsupported", ELL_ERROR);
break;
default:
os::Printer::log("FBO error", ELL_ERROR);
break;
}
return false;
}
core::array<GLenum> AssignedTextures;
bool AssignedDepth;
bool AssignedStencil;
bool RequestTextureUpdate;
bool RequestDepthStencilUpdate;
GLuint BufferID;
core::dimension2d<u32> Size;
u32 ColorAttachment;
u32 MultipleRenderTarget;
TOpenGLDriver* Driver;
};
}
}
#endif
#endif