Author: eilemann@gmail.com
State: Implemented in 0.9.1
Overview
This features adds support for a new type of decomposition and recomposition, whereby each contributing channel renders one or multiple subsamples for full-scene anti-aliasing (FSAA) or depth-of-field (DOF).
Applications might already do multi-pass software FSAA/DOF, either during rendering or when the application is idle. Equalizer does not yet have a notion of idle/non-idle rendering.
Design
The application will do the adding of the frustum jitter, since it is the one which knows how many FSAA and/or DOF samples are desired. Therefore it needs to know in Channel::frameDraw which sample out of how many it should render.
The default implementation of Channel::applyFrustum will use the subpixel sample description to compute an FSAA jitter using a pre-defined lookup table. It will add this jitter to the frustum supplied by getFrustum.
Applications which have their own SWAA settings will use the subpixel sample description to calculate how many passes with which samples have to be rendered, e.g., if it desires to render 16 samples on a 4-time decomposition, the application will render 4 passes out of a 16-value jitter lookup table on each channel.
It is the application's responsibility to provide a blended result of the sub-passes on each channel. This should not be an overhead, since the application could already compute the accumulation and averaging before.
Compositing
API
class SubPixel { public: uint32_t index; uint32_t size; } vmml::Vector2f Channel::getJitter() const; vmml::Vector2i Channel::getSubpixel() const;
File Format
compound { subpixel [ index size ] }
Implementation
Idle SWAA in eqPly
class FrameData bool _idleMode // idle AA mode class Config uint32_t _nbFramesAA // number of frames left class Channel Accum* _accum // accumulation buffer uint32_t _jitterStep // current jitter step decrementing uint32_t _totalSteps // total steps to be done uint32_t _subpixelStep // number of ressources (subpixel.size) bool _needsTransfer // combining idle AA with AA compound PixelViewport _currentPVP // current saved pvp ------------------------------------------------------------------- Config::startFrame if view is idle AA assert( nbFramesAA > 0 ) --nbFramesAA set idleMode else nbFrames = 0 clear idleMode commit frame data Config::handleEvent event = get IDLE_AA event nbFramesAA = MAX( nbFramesAA, event->jitter ) Channel::frameStart if view is not in idle AA mode clear accum reset idle AA Channel::frameViewStart save _currentPVP Channel::frameDraw [...] _subpixelStep = MAX( _subpixelStep, getSubPixel().size ) set _needsTransfer Channel::frameAssemble if getPVP != _currentPVP if accum doesn't use an FBO warning, idle AA not implemented stop idle AA do normal assembly set _needsTransfer else if all input frames have SubPixel::ALL do normal assembly set _needsTransfer else for each input frame assemble each frame with subpixel decomp accumulate clear _needsTransfer Channel::frameViewFinish if PVP has changed resize accum buffer clear accum reset idle AA else if idle mode && _needsTransfer if first idle step reset accum buffer accumulate back buffer to accum buffer display average result in back buffer Channel::frameFinish if idle mode && _jitterStep > 0 _jitterStep -= subpixelStep create IDLE_AA event send event to the config Channel::applyFrustum if idle mode && _jitterStep > 0 jitter = _getJitter(); apply jitter vector to frustum else do normal apply frustum Channel::_getJitterStep idx = channelID * _totalSteps; idx += ( _jitterStep * primeNumber[channelID] ) % subset_size return ( idx % sampleSize, idx / sampleSize ) Channel::_getJitter compute pixel size and subpixel size on near plane (frustum) generate random sample position in _getJitterStep() subpixel
AA compound in Equalizer
AA compounds without idle AA: eq::Channel::applyFrustum / applyOrtho -> add jitter eq::Channel::frameAssemble Step 1: Do accumulation using GPU: for each jitter decomp in input frames assemble all input frames with jitter decomp accumulate result return result Step 2: Opt: Do accumulate using CPU (later) AA compounds with idle AA: See also Issue 6. -> extend compositor functions so the accumulation buffer (and state?) can be given as parameter
Server
loader add subpixel token Compound::InheritData add subpixel param Compound::updateInheritData update subpixel CompoundUpdateOutput::_updateOutput update subpixel ChannelUpdateVisitor::_setupRenderContext update subpixel FrameData::setSubPixel
Client
FrameData::Data add subpixel param Channel::getSubPixel Channel::applyFrustum if subpixel decomp lookup on precomputed jitter tables if no table is found compute the sample randomly apply the jitter vector Compositor::obtainAcum get accum object from ObjectManager if accum doesn't exist create new accum object else resize accum Compositor::assembleFramesSorted/Unsorted if input frames have different subpixel decomp if accum doesn't exist obtain accum from compositor clear accum extract similar subpixel decomp assemble frames (recursion on assembleFramesSorted/Unsorted) accumulate frames display result [...]
Issues
1. Do we need a one-dimensional or two-dimensional subpixel description for each source channel?
This depends on Q2. My feeling is a one-dimensional attribute is easier.
2. How do I compute my current jitter area?
The number of jitter areas, i.e, the number of FSAA or DOF samples, is an application parameter. The current jitter area within each Channel::frameDraw is a function of the current idle step and the channel's subpixel description.
Open Issue: the function itself. The jitter values for all channels in the first pass should be evenly distributed over the total subpixel grid.
Idea: Divide the sample grid into the number of channels to get a subset of pixels for each channel. For each pass, compute (pass_number * rnd_prime_number) % size to get kind of a randomized distribution. The size parameter is the size of the pixel subset for the current channel.
3. How do I disable FSAA when it's not supported?
Depending on the OpenGL version, we run either the glAccum method either the FBO's one. On MacOSX, a different implementation of glAccum is needed.
4. How do I compute the jitter steps for FSAA compound?
The destination channel asks for a new frame instead that the config starts a new frame itself. Each destination channel sends an event to the config which takes the maximum number of steps needed and enables to start a new frame.
5. How do I decrement the jitter steps done in the current frame?
The destination channel might render 0..n jitter steps and receive 0..n jitter steps as input frames during compositing.
We could assume that a subpixel decomposition parameter is set for the destination channel. If the destination channel does a frameDraw, he will decrement the jitter step by the subpixel decomposition size in frameDraw, otherwise he will decrement it by the subpixel decomposition size of the input frames in frameAssemble:
Channel::frameStart _subpixelSteps = 0; Channel::frameDraw _subpixelSteps = EQ_MAX( _subpixelSteps, subpixel.size ); Channel::frameAssemble for each input frame _subpixelSteps = EQ_MAX( _subpixelSteps, frame.subpixel.size ); Channel::frameFinish _jitterSteps -= _subpixelSteps; send event
6. How does the accumulation in frameAssemble work together with the one in frameViewFinish?
Without any scalability, each destination channel accumulates in idle mode the results of different frames into an accumulation buffer. With scalability, frameAssemble might accumulate the results of different, jittered input frames.
A simple, but non-optimal solution is to assemble and average all jitter steps in frameAssemble, and to transfer the result into the accumulation buffer n times in frameViewFinish:
Channel::frameAssemble accumulate all input frames in separate accum buffer return accum buffer to frame buffer Channel::frameViewFinish copy frame buffer _subpixelSteps times into accum buffer _stepsDone += _subpixelSteps; return accum buffer
In the optimal case, frameAssemble and frameViewFinish should share the accumulation buffer:
Channel::frameDraw set _needsTransfer Channel::frameViewStart save current pvp Channel::frameAssemble if( current pvp != saved pvp ) use separate accum buffer return accum buffer after assembly set _needsTransfer Q: what if glAccum is used? Disable idle AA for this channel? if( all input frames have Subpixel::ALL ) do normal assembly set _needsTransfer else if for each jitter decomp in input frames assemble all input frames with jitter decomp accumulate result clear _needsTransfer Channel::frameViewFinish if _needsTransfer accumulate FB into accum buffer return accum buffer