From Michael Platings and Paul Palumbo, multi-sample FBO support

This commit is contained in:
Robert Osfield
2008-06-18 14:09:11 +00:00
parent 7bef1bd16a
commit 90ea0bd95f
6 changed files with 439 additions and 79 deletions

View File

@@ -258,17 +258,25 @@ void Camera::attach(BufferComponent buffer, GLenum internalFormat)
_bufferAttachmentMap[buffer]._internalFormat = internalFormat;
}
void Camera::attach(BufferComponent buffer, osg::Texture* texture, unsigned int level, unsigned int face, bool mipMapGeneration)
void Camera::attach(BufferComponent buffer, osg::Texture* texture, unsigned int level, unsigned int face, bool mipMapGeneration,
unsigned int multisampleSamples,
unsigned int multisampleColorSamples)
{
_bufferAttachmentMap[buffer]._texture = texture;
_bufferAttachmentMap[buffer]._level = level;
_bufferAttachmentMap[buffer]._face = face;
_bufferAttachmentMap[buffer]._mipMapGeneration = mipMapGeneration;
_bufferAttachmentMap[buffer]._multisampleSamples = multisampleSamples;
_bufferAttachmentMap[buffer]._multisampleColorSamples = multisampleColorSamples;
}
void Camera::attach(BufferComponent buffer, osg::Image* image)
void Camera::attach(BufferComponent buffer, osg::Image* image,
unsigned int multisampleSamples,
unsigned int multisampleColorSamples)
{
_bufferAttachmentMap[buffer]._image = image;
_bufferAttachmentMap[buffer]._multisampleSamples = multisampleSamples;
_bufferAttachmentMap[buffer]._multisampleColorSamples = multisampleColorSamples;
}
void Camera::detach(BufferComponent buffer)

View File

@@ -41,7 +41,24 @@ FBOExtensions* FBOExtensions::instance(unsigned contextID, bool createIfNotInita
#define LOAD_FBO_EXT(name) setGLExtensionFuncPtr(name, (#name))
FBOExtensions::FBOExtensions(unsigned int contextID)
: _supported(false)
: _supported(false),
glBindRenderbufferEXT(0),
glGenRenderbuffersEXT(0),
glDeleteRenderbuffersEXT(0),
glRenderbufferStorageEXT(0),
glRenderbufferStorageMultisampleEXT(0),
glRenderbufferStorageMultisampleCoverageNV(0),
glBindFramebufferEXT(0),
glDeleteFramebuffersEXT(0),
glGenFramebuffersEXT(0),
glCheckFramebufferStatusEXT(0),
glFramebufferTexture1DEXT(0),
glFramebufferTexture2DEXT(0),
glFramebufferTexture3DEXT(0),
glFramebufferTextureLayerEXT(0),
glFramebufferRenderbufferEXT(0),
glGenerateMipmapEXT(0),
glBlitFramebufferEXT(0)
{
if (!isGLExtensionSupported(contextID, "GL_EXT_framebuffer_object"))
return;
@@ -75,6 +92,21 @@ FBOExtensions::FBOExtensions(unsigned int contextID)
glFramebufferTexture3DEXT != 0 &&
glFramebufferRenderbufferEXT != 0 &&
glGenerateMipmapEXT != 0;
if (!isGLExtensionSupported(contextID, "GL_EXT_framebuffer_blit"))
return;
LOAD_FBO_EXT(glBlitFramebufferEXT);
if (isGLExtensionSupported(contextID, "GL_EXT_framebuffer_multisample"))
{
LOAD_FBO_EXT(glRenderbufferStorageMultisampleEXT);
}
if (isGLExtensionSupported(contextID, "GL_NV_framebuffer_multisample_coverage"))
{
LOAD_FBO_EXT(glRenderbufferStorageMultisampleCoverageNV);
}
}
@@ -144,15 +176,19 @@ RenderBuffer::RenderBuffer()
: Object(),
_internalFormat(GL_DEPTH_COMPONENT24),
_width(512),
_height(512)
_height(512),
_samples(0),
_colorSamples(0)
{
}
RenderBuffer::RenderBuffer(int width, int height, GLenum internalFormat)
RenderBuffer::RenderBuffer(int width, int height, GLenum internalFormat, int samples, int colorSamples)
: Object(),
_internalFormat(internalFormat),
_width(width),
_height(height)
_height(height),
_samples(samples),
_colorSamples(colorSamples)
{
}
@@ -160,7 +196,9 @@ RenderBuffer::RenderBuffer(const RenderBuffer &copy, const CopyOp &copyop)
: Object(copy, copyop),
_internalFormat(copy._internalFormat),
_width(copy._width),
_height(copy._height)
_height(copy._height),
_samples(copy._samples),
_colorSamples(copy._colorSamples)
{
}
@@ -190,7 +228,30 @@ GLuint RenderBuffer::getObjectID(unsigned int contextID, const FBOExtensions *ex
{
// bind and configure
ext->glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, objectID);
ext->glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, _internalFormat, _width, _height);
// framebuffer_multisample_coverage specification requires that coverage
// samples must be >= color samples.
if (_samples < _colorSamples)
{
notify(WARN) << "Coverage samples must be greater than or equal to color samples."
" Setting coverage samples equal to color samples." << std::endl;
const_cast<RenderBuffer*>(this)->setSamples(_colorSamples);
}
if (_samples > 0 && ext->isMultisampleCoverageSupported())
{
ext->glRenderbufferStorageMultisampleCoverageNV(GL_RENDERBUFFER_EXT,
_samples, _colorSamples, _internalFormat, _width, _height);
}
else if (_samples > 0 && ext->isMultisampleSupported())
{
ext->glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT,
_samples, _internalFormat, _width, _height);
}
else
{
ext->glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, _internalFormat, _width, _height);
}
dirty = 0;
}
@@ -391,6 +452,16 @@ FrameBufferAttachment &FrameBufferAttachment::operator = (const FrameBufferAttac
return *this;
}
bool FrameBufferAttachment::isMultisample() const
{
if (_ximpl->renderbufferTarget.valid())
{
return _ximpl->renderbufferTarget->getSamples() > 0;
}
return false;
}
void FrameBufferAttachment::createRequiredTexturesAndApplyGenerateMipMap(State &state, const FBOExtensions* ext) const
{
unsigned int contextID = state.getContextID();
@@ -422,7 +493,7 @@ void FrameBufferAttachment::createRequiredTexturesAndApplyGenerateMipMap(State &
}
}
void FrameBufferAttachment::attach(State &state, GLenum attachment_point, const FBOExtensions* ext) const
void FrameBufferAttachment::attach(State &state, GLenum target, GLenum attachment_point, const FBOExtensions* ext) const
{
unsigned int contextID = state.getContextID();
@@ -444,25 +515,25 @@ void FrameBufferAttachment::attach(State &state, GLenum attachment_point, const
{
default:
case Pimpl::RENDERBUFFER:
ext->glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_RENDERBUFFER_EXT, _ximpl->renderbufferTarget->getObjectID(contextID, ext));
ext->glFramebufferRenderbufferEXT(target, attachment_point, GL_RENDERBUFFER_EXT, _ximpl->renderbufferTarget->getObjectID(contextID, ext));
break;
case Pimpl::TEXTURE1D:
ext->glFramebufferTexture1DEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_TEXTURE_1D, tobj->_id, _ximpl->level);
ext->glFramebufferTexture1DEXT(target, attachment_point, GL_TEXTURE_1D, tobj->_id, _ximpl->level);
break;
case Pimpl::TEXTURE2D:
ext->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_TEXTURE_2D, tobj->_id, _ximpl->level);
ext->glFramebufferTexture2DEXT(target, attachment_point, GL_TEXTURE_2D, tobj->_id, _ximpl->level);
break;
case Pimpl::TEXTURE3D:
ext->glFramebufferTexture3DEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_TEXTURE_3D, tobj->_id, _ximpl->level, _ximpl->zoffset);
ext->glFramebufferTexture3DEXT(target, attachment_point, GL_TEXTURE_3D, tobj->_id, _ximpl->level, _ximpl->zoffset);
break;
case Pimpl::TEXTURE2DARRAY:
ext->glFramebufferTextureLayerEXT(GL_FRAMEBUFFER_EXT, attachment_point, tobj->_id, _ximpl->level, _ximpl->zoffset);
ext->glFramebufferTextureLayerEXT(target, attachment_point, tobj->_id, _ximpl->level, _ximpl->zoffset);
break;
case Pimpl::TEXTURERECT:
ext->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_TEXTURE_RECTANGLE, tobj->_id, 0);
ext->glFramebufferTexture2DEXT(target, attachment_point, GL_TEXTURE_RECTANGLE, tobj->_id, 0);
break;
case Pimpl::TEXTURECUBE:
ext->glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment_point, GL_TEXTURE_CUBE_MAP_POSITIVE_X + _ximpl->cubeMapFace, tobj->_id, _ximpl->level);
ext->glFramebufferTexture2DEXT(target, attachment_point, GL_TEXTURE_CUBE_MAP_POSITIVE_X + _ximpl->cubeMapFace, tobj->_id, _ximpl->level);
break;
}
}
@@ -622,6 +693,11 @@ void FrameBufferObject::updateDrawBuffers()
}
void FrameBufferObject::apply(State &state) const
{
apply(state, READ_DRAW_FRAMEBUFFER);
}
void FrameBufferObject::apply(State &state, BindTarget target) const
{
unsigned int contextID = state.getContextID();
@@ -639,7 +715,7 @@ void FrameBufferObject::apply(State &state) const
if (_attachments.empty())
{
ext->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
ext->glBindFramebufferEXT(target, 0);
return;
}
@@ -678,7 +754,7 @@ void FrameBufferObject::apply(State &state) const
}
ext->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboID);
ext->glBindFramebufferEXT(target, fboID);
// enable drawing buffers to render the result to fbo
if (_drawBuffers.size() > 0)
@@ -695,13 +771,26 @@ void FrameBufferObject::apply(State &state) const
for (AttachmentMap::const_iterator i=_attachments.begin(); i!=_attachments.end(); ++i)
{
const FrameBufferAttachment &fa = i->second;
fa.attach(state, convertBufferComponentToGLenum(i->first), ext);
fa.attach(state, target, convertBufferComponentToGLenum(i->first), ext);
}
dirtyAttachmentList = 0;
}
}
bool FrameBufferObject::isMultisample() const
{
if (_attachments.size())
{
// If the FBO is correctly set up then all attachments will be either
// multisampled or single sampled. Therefore we can just return the
// result of the first attachment.
return _attachments.begin()->second.isMultisample();
}
return false;
}
int FrameBufferObject::compare(const StateAttribute &sa) const
{
COMPARE_StateAttribute_Types(FrameBufferObject, sa);

View File

@@ -33,7 +33,8 @@ using namespace osgUtil;
//RegisterRenderBinProxy<RenderStage> s_registerRenderStageProxy;
RenderStage::RenderStage():
RenderBin(getDefaultRenderBinSortMode())
RenderBin(getDefaultRenderBinSortMode()),
_disableFboAfterRender(true)
{
// point RenderBin's _stage to this to ensure that references to
// stage don't go tempted away to any other stage.
@@ -59,7 +60,8 @@ RenderStage::RenderStage():
}
RenderStage::RenderStage(SortMode mode):
RenderBin(mode)
RenderBin(mode),
_disableFboAfterRender(true)
{
// point RenderBin's _stage to this to ensure that references to
// stage don't go tempted away to any other stage.
@@ -104,7 +106,8 @@ RenderStage::RenderStage(const RenderStage& rhs,const osg::CopyOp& copyop):
_face(rhs._face),
_imageReadPixelFormat(rhs._imageReadPixelFormat),
_imageReadPixelDataType(rhs._imageReadPixelDataType),
_renderStageLighting(rhs._renderStageLighting)
_renderStageLighting(rhs._renderStageLighting),
_disableFboAfterRender(rhs._disableFboAfterRender)
{
_stage = this;
}
@@ -327,10 +330,37 @@ void RenderStage::runCameraSetUp(osg::RenderInfo& renderInfo)
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(*(_camera->getDataChangeMutex()));
osg::ref_ptr<osg::FrameBufferObject> fbo = new osg::FrameBufferObject;
osg::ref_ptr<osg::FrameBufferObject> fbo_multisample;
bool colorAttached = false;
bool depthAttached = false;
bool stencilAttached = false;
unsigned samples = 0;
unsigned colorSamples = 0;
if (fbo_ext->isMultisampleSupported())
{
for(osg::Camera::BufferAttachmentMap::iterator itr = bufferAttachments.begin();
itr != bufferAttachments.end();
++itr)
{
osg::Camera::Attachment& attachment = itr->second;
samples = maximum(samples, attachment._multisampleSamples);
colorSamples = maximum(samples, attachment._multisampleColorSamples);
}
if (colorSamples > samples)
{
osg::notify(WARN) << "Multisample color samples must be less than or "
"equal to samples. Setting color samples equal to samples." << std::endl;
colorSamples = samples;
}
if (samples)
{
fbo_multisample = new osg::FrameBufferObject;
}
}
for(osg::Camera::BufferAttachmentMap::iterator itr = bufferAttachments.begin();
itr != bufferAttachments.end();
@@ -344,6 +374,30 @@ void RenderStage::runCameraSetUp(osg::RenderInfo& renderInfo)
fbo->setAttachment(buffer, osg::FrameBufferAttachment(attachment));
else
fbo->setAttachment(buffer, osg::FrameBufferAttachment(new osg::RenderBuffer(width, height, attachment._internalFormat)));
if (fbo_multisample.valid())
{
GLenum internalFormat = attachment._internalFormat;
if (!internalFormat)
{
switch (buffer)
{
case Camera::DEPTH_BUFFER:
internalFormat = GL_DEPTH_COMPONENT24;
break;
case Camera::STENCIL_BUFFER:
internalFormat = GL_STENCIL_INDEX8_EXT;
break;
default:
internalFormat = GL_RGBA;
break;
}
}
fbo_multisample->setAttachment(buffer,
osg::FrameBufferAttachment(new osg::RenderBuffer(
width, height, internalFormat,
samples, colorSamples)));
}
if (buffer==osg::Camera::DEPTH_BUFFER) depthAttached = true;
else if (buffer==osg::Camera::STENCIL_BUFFER) stencilAttached = true;
@@ -353,11 +407,23 @@ void RenderStage::runCameraSetUp(osg::RenderInfo& renderInfo)
if (!depthAttached)
{
fbo->setAttachment(osg::Camera::DEPTH_BUFFER, osg::FrameBufferAttachment(new osg::RenderBuffer(width, height, GL_DEPTH_COMPONENT24)));
if (fbo_multisample.valid())
{
fbo_multisample->setAttachment(osg::Camera::DEPTH_BUFFER,
osg::FrameBufferAttachment(new osg::RenderBuffer(width,
height, GL_DEPTH_COMPONENT24, samples, colorSamples)));
}
}
if (!colorAttached)
{
fbo->setAttachment(osg::Camera::COLOR_BUFFER, osg::FrameBufferAttachment(new osg::RenderBuffer(width, height, GL_RGB)));
if (fbo_multisample.valid())
{
fbo_multisample->setAttachment(osg::Camera::COLOR_BUFFER,
osg::FrameBufferAttachment(new osg::RenderBuffer(width,
height, GL_RGB, samples, colorSamples)));
}
}
fbo->apply(state);
@@ -386,6 +452,39 @@ void RenderStage::runCameraSetUp(osg::RenderInfo& renderInfo)
setReadBuffer(GL_NONE);
_fbo = fbo;
if (fbo_multisample.valid())
{
fbo_multisample->apply(state);
GLenum status = fbo_ext->glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
if (status != GL_FRAMEBUFFER_COMPLETE_EXT)
{
notify(NOTICE) << "RenderStage::runCameraSetUp(), "
"multisample FBO setup failed, FBO status = 0x"
<< std::hex << status << std::endl;
fbo->apply(state);
fbo_multisample = 0;
_resolveFbo = 0;
// clean up.
double availableTime = 100.0f;
double currentTime = state.getFrameStamp()?state.getFrameStamp()->getReferenceTime():0.0;
osg::RenderBuffer::flushDeletedRenderBuffers(state.getContextID(),currentTime,availableTime);
osg::FrameBufferObject::flushDeletedFrameBufferObjects(state.getContextID(),currentTime,availableTime);
}
else
{
_resolveFbo.swap(_fbo);
_fbo = fbo_multisample;
}
}
else
{
_resolveFbo = 0;
}
}
}
@@ -680,6 +779,27 @@ void RenderStage::copyTexture(osg::RenderInfo& renderInfo)
void RenderStage::drawInner(osg::RenderInfo& renderInfo,RenderLeaf*& previous, bool& doCopyTexture)
{
struct SubFunc
{
static void applyReadFBO(bool& apply_read_fbo,
const FrameBufferObject* read_fbo, osg::State& state)
{
if (read_fbo->isMultisample())
{
osg::notify(osg::WARN) << "Attempting to read from a"
" multisampled framebuffer object. Set a resolve"
" framebuffer on the RenderStage to fix this." << std::endl;
}
if (apply_read_fbo)
{
// Bind the monosampled FBO to read from
read_fbo->apply(state, FrameBufferObject::READ_FRAMEBUFFER);
apply_read_fbo = false;
}
}
};
osg::State& state = *renderInfo.getState();
osg::FBOExtensions* fbo_ext = _fbo.valid() ? osg::FBOExtensions::instance(state.getContextID(),true) : 0;
@@ -722,67 +842,115 @@ void RenderStage::drawInner(osg::RenderInfo& renderInfo,RenderLeaf*& previous, b
}
}
const FrameBufferObject* read_fbo = _fbo.get();
bool apply_read_fbo = false;
if (fbo_supported && _resolveFbo.valid() && fbo_ext->glBlitFramebufferEXT)
{
GLbitfield blitMask = 0;
//find which buffer types should be copied
for (FrameBufferObject::AttachmentMap::const_iterator
it = _resolveFbo->getAttachmentMap().begin(),
end =_resolveFbo->getAttachmentMap().end(); it != end; ++it)
{
switch (it->first)
{
case Camera::DEPTH_BUFFER:
blitMask |= GL_DEPTH_BUFFER_BIT;
break;
case Camera::STENCIL_BUFFER:
blitMask |= GL_STENCIL_BUFFER_BIT;
break;
default:
blitMask |= GL_COLOR_BUFFER_BIT;
break;
}
}
// Bind the resolve framebuffer to blit into.
_resolveFbo->apply(state, FrameBufferObject::DRAW_FRAMEBUFFER);
// Blit to the resolve framebuffer.
// Note that (with nvidia 175.16 windows drivers at least) if the read
// framebuffer is multisampled then the dimension arguments are ignored
// and the whole framebuffer is always copied.
fbo_ext->glBlitFramebufferEXT(
0, 0, static_cast<GLint>(_viewport->width()), static_cast<GLint>(_viewport->height()),
0, 0, static_cast<GLint>(_viewport->width()), static_cast<GLint>(_viewport->height()),
blitMask, GL_NEAREST);
apply_read_fbo = true;
read_fbo = _resolveFbo.get();
using_multiple_render_targets = read_fbo->hasMultipleRenderingTargets();
}
// now copy the rendered image to attached texture.
if (doCopyTexture)
{
SubFunc::applyReadFBO(apply_read_fbo, read_fbo, state);
copyTexture(renderInfo);
}
std::map< osg::Camera::BufferComponent, Attachment>::const_iterator itr;
for(itr = _bufferAttachmentMap.begin();
itr != _bufferAttachmentMap.end();
++itr)
{
if (itr->second._image.valid())
{
if (using_multiple_render_targets)
{
int attachment=itr->first;
if (attachment==osg::Camera::DEPTH_BUFFER || attachment==osg::Camera::STENCIL_BUFFER) {
// assume first buffer rendered to is the one we want
glReadBuffer(_fbo->getMultipleRenderingTargets()[0]);
} else {
glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + (attachment - osg::Camera::COLOR_BUFFER0));
}
} else {
if (_readBuffer != GL_NONE)
{
glReadBuffer(_readBuffer);
}
}
if (itr->second._image.valid())
{
SubFunc::applyReadFBO(apply_read_fbo, read_fbo, state);
GLenum pixelFormat = itr->second._image->getPixelFormat();
if (pixelFormat==0) pixelFormat = _imageReadPixelFormat;
if (pixelFormat==0) pixelFormat = GL_RGB;
if (using_multiple_render_targets)
{
int attachment=itr->first;
if (attachment==osg::Camera::DEPTH_BUFFER || attachment==osg::Camera::STENCIL_BUFFER) {
// assume first buffer rendered to is the one we want
glReadBuffer(read_fbo->getMultipleRenderingTargets()[0]);
} else {
glReadBuffer(GL_COLOR_ATTACHMENT0_EXT + (attachment - osg::Camera::COLOR_BUFFER0));
}
} else {
if (_readBuffer != GL_NONE)
{
glReadBuffer(_readBuffer);
}
}
GLenum dataType = itr->second._image->getDataType();
if (dataType==0) dataType = _imageReadPixelDataType;
if (dataType==0) dataType = GL_UNSIGNED_BYTE;
GLenum pixelFormat = itr->second._image->getPixelFormat();
if (pixelFormat==0) pixelFormat = _imageReadPixelFormat;
if (pixelFormat==0) pixelFormat = GL_RGB;
itr->second._image->readPixels(static_cast<int>(_viewport->x()),
static_cast<int>(_viewport->y()),
static_cast<int>(_viewport->width()),
static_cast<int>(_viewport->height()),
pixelFormat, dataType);
GLenum dataType = itr->second._image->getDataType();
if (dataType==0) dataType = _imageReadPixelDataType;
if (dataType==0) dataType = GL_UNSIGNED_BYTE;
itr->second._image->readPixels(static_cast<int>(_viewport->x()),
static_cast<int>(_viewport->y()),
static_cast<int>(_viewport->width()),
static_cast<int>(_viewport->height()),
pixelFormat, dataType);
}
}
}
}
if (fbo_supported)
{
// switch of the frame buffer object
fbo_ext->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
if (getDisableFboAfterRender())
{
// switch off the frame buffer object
fbo_ext->glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
doCopyTexture = true;
}
if (fbo_supported && _camera)
{
// now generate mipmaps if they are required.
const osg::Camera::BufferAttachmentMap& bufferAttachements = _camera->getBufferAttachmentMap();
for(osg::Camera::BufferAttachmentMap::const_iterator itr = bufferAttachements.begin();
itr != bufferAttachements.end();
const osg::Camera::BufferAttachmentMap& bufferAttachments = _camera->getBufferAttachmentMap();
for(osg::Camera::BufferAttachmentMap::const_iterator itr = bufferAttachments.begin();
itr != bufferAttachments.end();
++itr)
{
if (itr->second._texture.valid() && itr->second._mipMapGeneration)
@@ -1142,3 +1310,13 @@ unsigned int RenderStage::computeNumberOfDynamicRenderLeaves() const
return count;
}
void osgUtil::RenderStage::setMultisampleResolveFramebufferObject(osg::FrameBufferObject* fbo)
{
if (fbo && fbo->isMultisample())
{
osg::notify(osg::WARN) << "Resolve framebuffer must not be"
" multisampled." << std::endl;
}
_resolveFbo = fbo;
}