#Use EXT_framebuffer_object when available

* noframebufferobject configuration in system.cfg can be used to
  disable EXT_fbo (in case drivers are flaky etc.)
* shadow texture size now honours the OpenGL maximum texture size as
reported
  by the implementation

This was SVN commit r3693.
This commit is contained in:
prefect 2006-03-26 17:36:33 +00:00
parent 12b214bf27
commit 8fd256c458
9 changed files with 220 additions and 97 deletions

View File

@ -24,6 +24,7 @@
; System settings:
novbo=false
noframebufferobject=false
shadows=true
vsync=false

View File

@ -57,6 +57,25 @@ FUNC2(void, glCompressedTexSubImage2DARB, glCompressedTexSubImage2D, "1.3", (GLe
FUNC2(void, glCompressedTexSubImage1DARB, glCompressedTexSubImage1D, "1.3", (GLenum, GLint, GLint, GLsizei, GLenum, GLsizei, const GLvoid*))
FUNC2(void, glGetCompressedTexImageARB, glGetCompressedTexImage, "1.3", (GLenum, GLint, GLvoid*))
// GL_EXT_framebuffer_object
FUNC(GLboolean, glIsRenderbufferEXT, (GLuint renderbuffer))
FUNC(void, glBindRenderbufferEXT, (GLenum target, GLuint renderbuffer))
FUNC(void, glDeleteRenderbuffersEXT, (GLsizei n, const GLuint *renderbuffers))
FUNC(void, glGenRenderbuffersEXT, (GLsizei n, GLuint *renderbuffers))
FUNC(void, glRenderbufferStorageEXT, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height))
FUNC(void, glGetRenderbufferParameterivEXT, (GLenum target, GLenum pname, GLint *params))
FUNC(GLboolean, glIsFramebufferEXT, (GLuint framebuffer))
FUNC(void, glBindFramebufferEXT, (GLenum target, GLuint framebuffer))
FUNC(void, glDeleteFramebuffersEXT, (GLsizei n, const GLuint *framebuffers))
FUNC(void, glGenFramebuffersEXT, (GLsizei n, GLuint *framebuffers))
FUNC(GLenum, glCheckFramebufferStatusEXT, (GLenum target))
FUNC(void, glFramebufferTexture1DEXT, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level))
FUNC(void, glFramebufferTexture2DEXT, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level))
FUNC(void, glFramebufferTexture3DEXT, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset))
FUNC(void, glFramebufferRenderbufferEXT, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer))
FUNC(void, glGetFramebufferAttachmentParameterivEXT, (GLenum target, GLenum attachment, GLenum pname, GLint *params))
FUNC(void, glGenerateMipmapEXT, (GLenum target))
// GL_ARB_shader_objects
FUNC2(void, glDeleteObjectARB, glDeleteObject, "2.0", (GLhandleARB obj))
FUNC2(GLhandleARB, glGetHandleARB, glGetHandle, "2.0", (GLenum pname))

View File

@ -264,7 +264,8 @@ static void dump_gl_error(GLenum err)
E(GL_STACK_OVERFLOW)
E(GL_STACK_UNDERFLOW)
E(GL_OUT_OF_MEMORY)
default:;
E(GL_INVALID_FRAMEBUFFER_OPERATION_EXT)
default: debug_printf("GL error: %04f\n", err); break;
}
#undef E
}

View File

@ -17,6 +17,8 @@ CStr g_ActiveProfile = "default";
// flag to disable extended GL extensions until fix found - specifically, crashes
// using VBOs on laptop Radeon cards
bool g_NoGLVBO=false;
// disable FBO extension in case the driver is flaky
bool g_NoGLFramebufferObject = false;
// flag to switch on shadows
bool g_Shadows=false;
// flag to switch off pbuffers
@ -69,6 +71,7 @@ static void LoadGlobals()
CFG_GET_USER_VAL("vsync", Bool, g_VSync);
CFG_GET_USER_VAL("novbo", Bool, g_NoGLVBO);
CFG_GET_USER_VAL("noframebufferobject", Bool, g_NoGLFramebufferObject);
CFG_GET_USER_VAL("shadows", Bool, g_Shadows);
CFG_GET_USER_VAL("renderpath", String, g_RenderPath);

View File

@ -3,6 +3,8 @@
// flag to disable extended GL extensions until fix found - specifically, crashes
// using VBOs on laptop Radeon cards
extern bool g_NoGLVBO;
// disable FBO extension in case the driver is flaky
extern bool g_NoGLFramebufferObject;
// flag to switch on shadows
extern bool g_Shadows;
// flag to switch off pbuffers

View File

@ -643,6 +643,7 @@ static void InitRenderer()
// set renderer options from command line options - NOVBO must be set before opening the renderer
g_Renderer.SetOptionBool(CRenderer::OPT_NOVBO,g_NoGLVBO);
g_Renderer.SetOptionBool(CRenderer::OPT_NOFRAMEBUFFEROBJECT,g_NoGLFramebufferObject);
g_Renderer.SetOptionBool(CRenderer::OPT_SHADOWS,g_Shadows);
g_Renderer.SetOptionBool(CRenderer::OPT_NOPBUFFER,g_NoPBuffer);
g_Renderer.SetRenderPath(CRenderer::GetRenderPathByName(g_RenderPath));

View File

@ -294,18 +294,104 @@ CRenderer::CRenderer()
m_FastNormals = true;
m_DisplayFrustum = false;
m_DisableCopyShadow = false;
m_FastPlayerColor = true;
m_VertexShader = 0;
m_Options.m_NoVBO=false;
m_Options.m_NoFramebufferObject = false;
m_Options.m_Shadows=true;
m_Options.m_ShadowColor=RGBAColor(0.4f,0.4f,0.4f,1.0f);
m_Options.m_RenderPath = RP_DEFAULT;
m_ShadowZBias = 0.001f;
for (uint i=0;i<MaxTextureUnits;i++) {
m_ActiveTextures[i]=0;
}
ONCE( ScriptingInit(); );
}
///////////////////////////////////////////////////////////////////////////////////
// CRenderer destructor
CRenderer::~CRenderer()
{
// model rendering
for(int vertexType = 0; vertexType < NumVertexTypes; ++vertexType)
{
delete m->Model.pal_NormalFF[vertexType];
delete m->Model.pal_PlayerFF[vertexType];
delete m->Model.pal_TranspFF[vertexType];
delete m->Model.pal_NormalHWLit[vertexType];
delete m->Model.pal_PlayerHWLit[vertexType];
delete m->Model.pal_TranspHWLit[vertexType];
delete m->Model.pal_NormalInstancing[vertexType];
delete m->Model.pal_PlayerInstancing[vertexType];
}
delete m->Model.pal_TranspSortAll;
// general
delete m_VertexShader;
m_VertexShader = 0;
CParticleEngine::GetInstance()->cleanup();
// we no longer UnloadAlphaMaps / UnloadWaterTextures here -
// that is the responsibility of the module that asked for
// them to be loaded (i.e. CGameView).
delete m;
}
///////////////////////////////////////////////////////////////////////////////////
// EnumCaps: build card cap bits
void CRenderer::EnumCaps()
{
// assume support for nothing
m_Caps.m_VBO=false;
m_Caps.m_TextureBorderClamp=false;
m_Caps.m_GenerateMipmaps=false;
m_Caps.m_VertexShader=false;
m_Caps.m_DepthTextureShadows = false;
m_Caps.m_FramebufferObject = false;
// now start querying extensions
if (!m_Options.m_NoVBO) {
if (oglHaveExtension("GL_ARB_vertex_buffer_object")) {
m_Caps.m_VBO=true;
}
}
if (oglHaveExtension("GL_ARB_texture_border_clamp")) {
m_Caps.m_TextureBorderClamp=true;
}
if (oglHaveExtension("GL_SGIS_generate_mipmap")) {
m_Caps.m_GenerateMipmaps=true;
}
if (0 == oglHaveExtensions(0, "GL_ARB_shader_objects", "GL_ARB_shading_language_100", 0))
{
if (oglHaveExtension("GL_ARB_vertex_shader"))
m_Caps.m_VertexShader=true;
}
if (0 == oglHaveExtensions(0, "GL_ARB_shadow", "GL_ARB_depth_texture", 0)) {
// According to Delphi3d.net, all relevant graphics chips that support depth textures
// (i.e. Geforce3+, Radeon9500+, even i915) also have >= 4 TMUs, so this restriction
// isn't actually a restriction, and it helps with integrating depth texture
// shadows into rendering paths.
if (ogl_max_tex_units >= 4)
m_Caps.m_DepthTextureShadows = true;
}
if (!m_Options.m_NoFramebufferObject)
{
if (oglHaveExtension("GL_EXT_framebuffer_object"))
m_Caps.m_FramebufferObject = true;
}
}
bool CRenderer::Open(int width, int height, int depth)
{
// Must query card capabilities before creating renderers that depend
// on card capabilities.
EnumCaps();
@ -375,88 +461,12 @@ CRenderer::CRenderer()
m->Model.ModTransparentShadow = RenderModifierPtr(new TransparentShadowRenderModifier);
m->Model.ModTransparentDepthShadow = RenderModifierPtr(new TransparentDepthShadowModifier);
m_ShadowZBias = 0.001f;
// Particle engine
CParticleEngine::GetInstance()->initParticleSystem();
CEmitter *pEmitter = new CDefaultEmitter(1000, -1);
CParticleEngine::GetInstance()->addEmitter(pEmitter);
ONCE( ScriptingInit(); );
}
///////////////////////////////////////////////////////////////////////////////////
// CRenderer destructor
CRenderer::~CRenderer()
{
// model rendering
for(int vertexType = 0; vertexType < NumVertexTypes; ++vertexType)
{
delete m->Model.pal_NormalFF[vertexType];
delete m->Model.pal_PlayerFF[vertexType];
delete m->Model.pal_TranspFF[vertexType];
delete m->Model.pal_NormalHWLit[vertexType];
delete m->Model.pal_PlayerHWLit[vertexType];
delete m->Model.pal_TranspHWLit[vertexType];
delete m->Model.pal_NormalInstancing[vertexType];
delete m->Model.pal_PlayerInstancing[vertexType];
}
delete m->Model.pal_TranspSortAll;
// general
delete m_VertexShader;
m_VertexShader = 0;
CParticleEngine::GetInstance()->cleanup();
// we no longer UnloadAlphaMaps / UnloadWaterTextures here -
// that is the responsibility of the module that asked for
// them to be loaded (i.e. CGameView).
delete m;
}
///////////////////////////////////////////////////////////////////////////////////
// EnumCaps: build card cap bits
void CRenderer::EnumCaps()
{
// assume support for nothing
m_Caps.m_VBO=false;
m_Caps.m_TextureBorderClamp=false;
m_Caps.m_GenerateMipmaps=false;
m_Caps.m_VertexShader=false;
m_Caps.m_DepthTextureShadows = false;
// now start querying extensions
if (!m_Options.m_NoVBO) {
if (oglHaveExtension("GL_ARB_vertex_buffer_object")) {
m_Caps.m_VBO=true;
}
}
if (oglHaveExtension("GL_ARB_texture_border_clamp")) {
m_Caps.m_TextureBorderClamp=true;
}
if (oglHaveExtension("GL_SGIS_generate_mipmap")) {
m_Caps.m_GenerateMipmaps=true;
}
if (0 == oglHaveExtensions(0, "GL_ARB_shader_objects", "GL_ARB_shading_language_100", 0))
{
if (oglHaveExtension("GL_ARB_vertex_shader"))
m_Caps.m_VertexShader=true;
}
if (0 == oglHaveExtensions(0, "GL_ARB_shadow", "GL_ARB_depth_texture", 0)) {
// According to Delphi3d.net, all relevant graphics chips that support depth textures
// (i.e. Geforce3+, Radeon9500+, even i915) also have >= 4 TMUs, so this restriction
// isn't actually a restriction, and it helps with integrating depth texture
// shadows into rendering paths.
if (ogl_max_tex_units >= 4)
m_Caps.m_DepthTextureShadows = true;
}
}
bool CRenderer::Open(int width, int height, int depth)
{
// Dimensions
m_Width = width;
m_Height = height;
m_Depth = depth;
@ -505,6 +515,9 @@ void CRenderer::SetOptionBool(enum Option opt,bool value)
case OPT_NOVBO:
m_Options.m_NoVBO=value;
break;
case OPT_NOFRAMEBUFFEROBJECT:
m_Options.m_NoFramebufferObject=value;
break;
case OPT_SHADOWS:
m_Options.m_Shadows=value;
break;
@ -524,6 +537,8 @@ bool CRenderer::GetOptionBool(enum Option opt) const
switch (opt) {
case OPT_NOVBO:
return m_Options.m_NoVBO;
case OPT_NOFRAMEBUFFEROBJECT:
return m_Options.m_NoFramebufferObject;
case OPT_SHADOWS:
return m_Options.m_Shadows;
default:

View File

@ -88,6 +88,7 @@ public:
enum { MaxTextureUnits=16 };
enum Option {
OPT_NOVBO,
OPT_NOFRAMEBUFFEROBJECT,
OPT_NOPBUFFER,
OPT_SHADOWS,
OPT_SHADOWCOLOR,
@ -134,6 +135,7 @@ public:
// renderer options
struct Options {
bool m_NoVBO;
bool m_NoFramebufferObject;
bool m_Shadows;
RGBAColor m_ShadowColor;
float m_LodBias;
@ -146,6 +148,7 @@ public:
bool m_GenerateMipmaps;
bool m_VertexShader;
bool m_DepthTextureShadows;
bool m_FramebufferObject;
};
public:

View File

@ -37,10 +37,15 @@ struct ShadowMapInternals
bool UseDepthTexture;
// bit depth for the depth texture, if used
int DepthTextureBits;
// if non-zero, we're using EXT_framebuffer_object for shadow rendering,
// and this is the framebuffer
GLuint Framebuffer;
// handle of shadow map
GLuint Texture;
// width, height of shadow map
u32 Width, Height;
// used width, height of shadow map
u32 EffectiveWidth, EffectiveHeight;
// transform light space into projected light space
// in projected light space, the shadowbound box occupies the [-1..1] cube
// calculated on BeginRender, after the final shadow bounds are known
@ -67,9 +72,12 @@ struct ShadowMapInternals
ShadowMap::ShadowMap()
{
m = new ShadowMapInternals;
m->Framebuffer = 0;
m->Texture = 0;
m->Width = 0;
m->Height = 0;
m->EffectiveWidth = 0;
m->EffectiveHeight = 0;
m->UseDepthTexture = false;
m->DepthTextureBits = 16;
}
@ -79,12 +87,14 @@ ShadowMap::~ShadowMap()
{
if (m->Texture)
glDeleteTextures(1, &m->Texture);
if (m->Framebuffer)
pglDeleteFramebuffersEXT(1, &m->Framebuffer);
delete m;
}
//////////////////////////////////////////////////////////////////////////////
// SetCameraAndLight: camera and light direction for this frame
// SetupFrame: camera and light direction for this frame
void ShadowMap::SetupFrame(const CCamera& camera, const CVector3D& lightdir)
{
if (!m->Texture)
@ -184,8 +194,8 @@ void ShadowMapInternals::CalcShadowMatrices()
// Calculate texture matrix by creating the clip space to texture coordinate matrix
// and then concatenating all matrices that have been calculated so far
CMatrix3D lightToTex;
float texscalex = (float)renderer.GetWidth() / (float)Width;
float texscaley = (float)renderer.GetHeight() / (float)Height;
float texscalex = (float)EffectiveWidth / (float)Width;
float texscaley = (float)EffectiveHeight / (float)Height;
float texscalez = 1.0;
texscalex = texscalex / (ShadowBound[1].X - ShadowBound[0].X);
@ -210,14 +220,42 @@ void ShadowMapInternals::CalcShadowMatrices()
// Create the shadow map
void ShadowMapInternals::CreateTexture()
{
// Cleanup
if (Texture)
{
glDeleteTextures(1, &Texture);
Texture = 0;
}
if (Framebuffer)
{
pglDeleteFramebuffersEXT(1, &Framebuffer);
Framebuffer = 0;
}
// Prepare FBO if available
// Note: luminance is not an RGB format, so a luminance texture cannot be used
// as a color buffer
if (UseDepthTexture && g_Renderer.GetCapabilities().m_FramebufferObject)
{
pglGenFramebuffersEXT(1, &Framebuffer);
}
// get shadow map size as next power of two up from view width and height
Width = g_Renderer.GetWidth();
Width = RoundUpToPowerOf2(Width);
Width = std::min(RoundUpToPowerOf2(Width), ogl_max_tex_size);
Height = g_Renderer.GetHeight();
Height = RoundUpToPowerOf2(Height);
Height = std::min(RoundUpToPowerOf2(Height), ogl_max_tex_size);
if (Framebuffer)
{
EffectiveWidth = Width;
EffectiveHeight = Height;
}
else
{
EffectiveWidth = std::min(Width, (u32)g_Renderer.GetWidth());
EffectiveHeight = std::min(Height, (u32)g_Renderer.GetHeight());
}
const char* formatname = "LUMINANCE";
@ -231,8 +269,8 @@ void ShadowMapInternals::CreateTexture()
}
}
LOG(NORMAL, LOG_CATEGORY, "Creating shadow texture (size %ix%i) (format = %s)",
Width, Height, formatname);
LOG(NORMAL, LOG_CATEGORY, "Creating shadow texture (size %ix%i) (format = %s)%s",
Width, Height, formatname, Framebuffer ? " (using EXT_framebuffer_object)" : "");
// create texture object
glGenTextures(1, &Texture);
@ -274,6 +312,35 @@ void ShadowMapInternals::CreateTexture()
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// bind to framebuffer object
if (Framebuffer)
{
debug_assert(UseDepthTexture);
glBindTexture(GL_TEXTURE_2D, 0);
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, Framebuffer);
pglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,
GL_TEXTURE_2D, Texture, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
GLenum status = pglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
{
LOG(WARNING, LOG_CATEGORY, "Framebuffer object incomplete: %04f", status);
pglDeleteFramebuffersEXT(1, &Framebuffer);
Framebuffer = 0;
EffectiveWidth = std::min(Width, (u32)g_Renderer.GetWidth());
EffectiveHeight = std::min(Height, (u32)g_Renderer.GetHeight());
}
}
}
@ -283,27 +350,29 @@ void ShadowMap::BeginRender()
{
// HACK HACK: this depends in non-obvious ways on the behaviour of the caller
CRenderer& renderer = g_Renderer;
int renderWidth = renderer.GetWidth();
int renderHeight = renderer.GetHeight();
// Calc remaining shadow matrices
m->CalcShadowMatrices();
if (m->Framebuffer)
{
glBindTexture(GL_TEXTURE_2D, 0);
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m->Framebuffer);
}
// clear buffers
if (m->UseDepthTexture)
{
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glClear(GL_DEPTH_BUFFER_BIT);
glColorMask(0,0,0,0);
}
else
{
glClearColor(1,1,1,0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
// setup viewport
glViewport(0, 0, renderWidth, renderHeight);
glViewport(0, 0, m->EffectiveWidth, m->EffectiveHeight);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
@ -314,7 +383,7 @@ void ShadowMap::BeginRender()
glLoadMatrixf(&m->LightTransform._11);
glEnable(GL_SCISSOR_TEST);
glScissor(1,1, renderWidth-2, renderHeight-2);
glScissor(1,1, m->EffectiveWidth-2, m->EffectiveHeight-2);
}
@ -325,11 +394,20 @@ void ShadowMap::EndRender()
glDisable(GL_SCISSOR_TEST);
// copy result into shadow map texture
if (!g_Renderer.GetDisableCopyShadow())
if (m->Framebuffer)
{
g_Renderer.BindTexture(0, m->Texture);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, g_Renderer.GetWidth(), g_Renderer.GetHeight());
pglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
else
{
if (!g_Renderer.GetDisableCopyShadow())
{
g_Renderer.BindTexture(0, m->Texture);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, g_Renderer.GetWidth(), g_Renderer.GetHeight());
}
}
glViewport(0, 0, g_Renderer.GetWidth(), g_Renderer.GetHeight());
if (m->UseDepthTexture)
{