void Channel::_drawHelp() { const FrameData& frameData = _getFrameData(); std::string message = frameData.getMessage(); if( !frameData.showHelp() && message.empty( )) return; applyBuffer(); applyViewport(); setupAssemblyState(); glLogicOp( GL_XOR ); glEnable( GL_COLOR_LOGIC_OP ); glDisable( GL_LIGHTING ); glDisable( GL_DEPTH_TEST ); glColor3f( 1.f, 1.f, 1.f ); const eq::PixelViewport& pvp = getPixelViewport(); const eq::Viewport& vp = getViewport(); const float height = pvp.h / vp.h; if( !message.empty( )) { const eq::util::BitmapFont* font = getWindow()->getMediumFont(); const float width = pvp.w / vp.w; const float xOffset = vp.x * width; const float yOffset = vp.y * height; const float yPos = 0.618f * height; float y = yPos - yOffset; for( size_t pos = message.find( '\n' ); pos != std::string::npos; pos = message.find( '\n' )) { glRasterPos3f( 10.f - xOffset, y, 0.99f ); font->draw( message.substr( 0, pos )); message = message.substr( pos + 1 ); y -= 22.f; } // last line glRasterPos3f( 10.f - xOffset, y, 0.99f ); font->draw( message ); } glMatrixMode( GL_PROJECTION ); glLoadIdentity(); applyScreenFrustum(); glMatrixMode( GL_MODELVIEW ); if( frameData.showHelp( )) { const eq::util::BitmapFont* font = getWindow()->getSmallFont(); std::string help = EqPly::getHelp(); float y = height - 16.f; for( size_t pos = help.find( '\n' ); pos != std::string::npos; pos = help.find( '\n' )) { glRasterPos3f( 10.f, y, 0.99f ); font->draw( help.substr( 0, pos )); help = help.substr( pos + 1 ); y -= 16.f; } // last line glRasterPos3f( 10.f, y, 0.99f ); font->draw( help ); } resetAssemblyState(); }
bool Window::processEvent( const Event& event ) { switch( event.type ) { case Event::WINDOW_HIDE: setPixelViewport( PixelViewport( 0, 0, 0, 0 )); break; case Event::WINDOW_SHOW: case Event::WINDOW_RESIZE: setPixelViewport( PixelViewport( event.resize.x, event.resize.y, event.resize.w, event.resize.h )); break; case Event::KEY_PRESS: case Event::KEY_RELEASE: if( event.key.key == KC_VOID ) return true; // ignore // else fall through case Event::WINDOW_EXPOSE: case Event::WINDOW_CLOSE: case Event::STATISTIC: case Event::MAGELLAN_AXIS: case Event::MAGELLAN_BUTTON: break; case Event::WINDOW_POINTER_GRAB: _grabbedChannels = _getEventChannels( event.pointer ); break; case Event::WINDOW_POINTER_UNGRAB: _grabbedChannels.clear(); break; case Event::WINDOW_POINTER_MOTION: case Event::WINDOW_POINTER_BUTTON_PRESS: case Event::WINDOW_POINTER_BUTTON_RELEASE: case Event::WINDOW_POINTER_WHEEL: { const Channels& channels = _getEventChannels( event.pointer ); for( Channels::const_iterator i = channels.begin(); i != channels.end(); ++i ) { Channel* channel = *i; Event channelEvent = event; switch( event.type ) { case Event::WINDOW_POINTER_MOTION: channelEvent.type = Event::CHANNEL_POINTER_MOTION; break; case Event::WINDOW_POINTER_BUTTON_PRESS: channelEvent.type = Event::CHANNEL_POINTER_BUTTON_PRESS; break; case Event::WINDOW_POINTER_BUTTON_RELEASE: channelEvent.type = Event::CHANNEL_POINTER_BUTTON_RELEASE; break; case Event::WINDOW_POINTER_WHEEL: channelEvent.type = Event::CHANNEL_POINTER_WHEEL; break; default: LBWARN << "Unhandled window event of type " << event.type << std::endl; LBUNIMPLEMENTED; } // convert y to GL notation (Channel PVP uses GL coordinates) const PixelViewport& pvp = getPixelViewport(); const int32_t y = pvp.h - event.pointer.y; const PixelViewport& channelPVP = channel->getNativePixelViewport(); channelEvent.originator = channel->getID(); channelEvent.serial = channel->getSerial(); channelEvent.pointer.x -= channelPVP.x; channelEvent.pointer.y = channelPVP.h - y + channelPVP.y; channel->processEvent( channelEvent ); } break; } case Event::WINDOW_SCREENSAVER: switch( getIAttribute( IATTR_HINT_SCREENSAVER )) { case OFF: return true; // screen saver stays inactive case ON: return false; // screen saver becomes active default: // AUTO if( getDrawableConfig().doublebuffered && getIAttribute( IATTR_HINT_DRAWABLE ) == WINDOW ) { return true; // screen saver stays inactive } return false; } case Event::UNKNOWN: // unknown window-system native event, which was not handled return false; default: LBWARN << "Unhandled window event of type " << event.type << std::endl; LBUNIMPLEMENTED; } Config* config = getConfig(); ConfigEvent configEvent; configEvent.data = event; config->sendEvent( configEvent ); return true; }
void Channel::_drawHelp() { const FrameData& frameData = _getFrameData(); std::string message = frameData.getMessage(); if( !frameData.showHelp() && message.empty( )) return; applyBuffer(); applyViewport(); setupAssemblyState(); glDisable( GL_LIGHTING ); glDisable( GL_DEPTH_TEST ); glColor3f( 1.f, 1.f, 1.f ); if( frameData.showHelp( )) { const eq::Window::Font* font = getWindow()->getSmallFont(); std::string help = EVolve::getHelp(); float y = 340.f; for( size_t pos = help.find( '\n' ); pos != std::string::npos; pos = help.find( '\n' )) { glRasterPos3f( 10.f, y, 0.99f ); font->draw( help.substr( 0, pos )); help = help.substr( pos + 1 ); y -= 16.f; } // last line glRasterPos3f( 10.f, y, 0.99f ); font->draw( help ); } if( !message.empty( )) { const eq::Window::Font* font = getWindow()->getMediumFont(); const eq::Viewport& vp = getViewport(); const eq::PixelViewport& pvp = getPixelViewport(); const float width = pvp.w / vp.w; const float xOffset = vp.x * width; const float height = pvp.h / vp.h; const float yOffset = vp.y * height; const float yMiddle = 0.5f * height; float y = yMiddle - yOffset; for( size_t pos = message.find( '\n' ); pos != std::string::npos; pos = message.find( '\n' )) { glRasterPos3f( 10.f - xOffset, y, 0.99f ); font->draw( message.substr( 0, pos )); message = message.substr( pos + 1 ); y -= 22.f; } // last line glRasterPos3f( 10.f - xOffset, y, 0.99f ); font->draw( message ); } EQ_GL_CALL( resetAssemblyState( )); }
bool Channel::_initAccum() { View* view = static_cast< View* >( getNativeView( )); if( !view ) // Only alloc accum for dest return true; const eq::Eye eye = getEye(); Accum& accum = _accum[ lunchbox::getIndexOfLastBit( eye ) ]; if( accum.buffer ) // already done return true; if( accum.step == -1 ) // accum init failed last time return false; // Check unsupported cases if( !eq::util::Accum::usesFBO( glewGetContext( ))) { for( size_t i = 0; i < eq::NUM_EYES; ++i ) { if( _accum[ i ].buffer ) { LBWARN << "glAccum-based accumulation does not support " << "stereo, disabling idle anti-aliasing." << std::endl; for( size_t j = 0; j < eq::NUM_EYES; ++j ) { delete _accum[ j ].buffer; _accum[ j ].buffer = 0; _accum[ j ].step = -1; } view->setIdleSteps( 0 ); return false; } } } // set up accumulation buffer accum.buffer = new eq::util::Accum( glewGetContext( )); const eq::PixelViewport& pvp = getPixelViewport(); LBASSERT( pvp.isValid( )); if( !accum.buffer->init( pvp, getWindow()->getColorFormat( )) || accum.buffer->getMaxSteps() < 256 ) { LBWARN <<"Accumulation buffer initialization failed, " << "idle AA not available." << std::endl; delete accum.buffer; accum.buffer = 0; accum.step = -1; return false; } // else LBVERB << "Initialized " << (accum.buffer->usesFBO() ? "FBO accum" : "glAccum") << " buffer for " << getName() << " " << getEye() << std::endl; view->setIdleSteps( accum.buffer ? 256 : 0 ); return true; }
void Window::setXDrawable( XID drawable ) { LBASSERT( _impl->xDisplay ); if( _impl->xDrawable == drawable ) return; if( _impl->xDrawable ) exitEventHandler(); _impl->xDrawable = drawable; if( !drawable ) return; const int32_t drawableType = getIAttribute( IATTR_HINT_DRAWABLE ); if( drawableType != OFF ) initEventHandler(); // query pixel viewport of window switch( drawableType ) { case PBUFFER: { unsigned width = 0; unsigned height = 0; glXQueryDrawable( _impl->xDisplay, drawable, GLX_WIDTH, &width ); glXQueryDrawable( _impl->xDisplay, drawable, GLX_HEIGHT, &height ); setPixelViewport( PixelViewport( 0, 0, int32_t( width ), int32_t( height ))); break; } case WINDOW: case AUTO: case UNDEFINED: { XWindowAttributes wa; XGetWindowAttributes( _impl->xDisplay, drawable, &wa ); // position is relative to parent: translate to absolute coords ::Window root, parent, *children; unsigned nChildren; XQueryTree( _impl->xDisplay, drawable, &root, &parent, &children, &nChildren ); if( children != 0 ) XFree( children ); int x,y; ::Window childReturn; XTranslateCoordinates( _impl->xDisplay, parent, root, wa.x, wa.y, &x, &y, &childReturn ); setPixelViewport( PixelViewport( x, y, wa.width, wa.height )); break; } default: LBERROR << "Unknown drawable type " << drawableType << std::endl; LBUNIMPLEMENTED; case OFF: case FBO: LBASSERT( getPixelViewport().hasArea( )); } }
void Channel::frameAssemble( const eq::uint128_t& frameID ) { const bool composeOnly = (_drawRange == eq::Range::ALL); eq::FrameData* data = _frame.getData(); _startAssemble(); const eq::Frames& frames = getInputFrames(); eq::PixelViewport coveredPVP; eq::Frames dbFrames; eq::Zoom zoom( eq::Zoom::NONE ); // Make sure all frames are ready and gather some information on them for( eq::Frames::const_iterator i = frames.begin(); i != frames.end(); ++i ) { eq::Frame* frame = *i; { eq::ChannelStatistics stat( eq::Statistic::CHANNEL_FRAME_WAIT_READY, this ); frame->waitReady( ); } const eq::Range& range = frame->getRange(); if( range == eq::Range::ALL ) // 2D frame, assemble directly eq::Compositor::assembleFrame( frame, this ); else { dbFrames.push_back( frame ); zoom = frame->getZoom(); _expandPVP( coveredPVP, frame->getImages(), frame->getOffset() ); } } coveredPVP.intersect( getPixelViewport( )); if( dbFrames.empty( )) { resetAssemblyState(); return; } // calculate correct frames sequence if( !composeOnly && coveredPVP.hasArea( )) { _frame.clear(); data->setRange( _drawRange ); dbFrames.push_back( &_frame ); } _orderFrames( dbFrames ); // check if current frame is in proper position, read back if not if( !composeOnly ) { if( _bgColor == eq::Vector3f::ZERO && dbFrames.front() == &_frame ) dbFrames.erase( dbFrames.begin( )); else if( coveredPVP.hasArea()) { eq::Window::ObjectManager* glObjects = getObjectManager(); _frame.setOffset( eq::Vector2i( 0, 0 )); _frame.setZoom( zoom ); data->setPixelViewport( coveredPVP ); _frame.readback( glObjects, getDrawableConfig( )); clearViewport( coveredPVP ); // offset for assembly _frame.setOffset( eq::Vector2i( coveredPVP.x, coveredPVP.y )); } } // blend DB frames in order try { eq::Compositor::assembleFramesSorted( dbFrames, this, 0, true /*blendAlpha*/ ); } catch( eq::Exception e ) { EQWARN << e << std::endl; } resetAssemblyState(); // Update range _drawRange = getRange(); }
void Channel::_drawModel( const Model* scene ) { Window* window = static_cast< Window* >( getWindow( )); VertexBufferState& state = window->getState(); const FrameData& frameData = _getFrameData(); if( frameData.getColorMode() == COLOR_MODEL && scene->hasColors( )) state.setColors( true ); else state.setColors( false ); state.setChannel( this ); // Compute cull matrix const eq::Matrix4f& rotation = frameData.getCameraRotation(); const eq::Matrix4f& modelRotation = frameData.getModelRotation(); eq::Matrix4f position = eq::Matrix4f::IDENTITY; position.set_translation( frameData.getCameraPosition()); const eq::Frustumf& frustum = getFrustum(); const eq::Matrix4f projection = useOrtho() ? frustum.compute_ortho_matrix(): frustum.compute_matrix(); const eq::Matrix4f& view = getHeadTransform(); const eq::Matrix4f model = rotation * position * modelRotation; state.setProjectionModelViewMatrix( projection * view * model ); state.setRange( &getRange().start); const eq::Pipe* pipe = getPipe(); const GLuint program = state.getProgram( pipe ); if( program != VertexBufferState::INVALID ) glUseProgram( program ); scene->cullDraw( state ); state.setChannel( 0 ); if( program != VertexBufferState::INVALID ) glUseProgram( 0 ); const InitData& initData = static_cast<Config*>( getConfig( ))->getInitData(); if( !initData.useROI( )) { declareRegion( getPixelViewport( )); return; } #ifndef NDEBUG // region border const eq::PixelViewport& pvp = getPixelViewport(); const eq::PixelViewport& region = getRegion(); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glOrtho( 0.f, pvp.w, 0.f, pvp.h, -1.f, 1.f ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); const eq::View* currentView = getView(); if( currentView && frameData.getCurrentViewID() == currentView->getID( )) glColor3f( 0.f, 0.f, 0.f ); else glColor3f( 1.f, 1.f, 1.f ); glNormal3f( 0.f, 0.f, 1.f ); const eq::Vector4f rect( float( region.x ) + .5f, float( region.y ) + .5f, float( region.getXEnd( )) - .5f, float( region.getYEnd( )) - .5f ); glBegin( GL_LINE_LOOP ); { glVertex3f( rect[0], rect[1], -.99f ); glVertex3f( rect[2], rect[1], -.99f ); glVertex3f( rect[2], rect[3], -.99f ); glVertex3f( rect[0], rect[3], -.99f ); } glEnd(); #endif }
void Channel::_testTiledOperations() { //----- setup constant data const eq::Images& images = _frame.getImages(); LBASSERT( images[0] ); eq::Config* config = getConfig(); const eq::PixelViewport& pvp = getPixelViewport(); const eq::Vector2i offset( pvp.x, pvp.y ); ConfigEvent event = _createConfigEvent(); event.area.x() = pvp.w; lunchbox::Clock clock; eq::Window::ObjectManager* glObjects = getObjectManager(); const GLEWContext* glewContext = glewGetContext(); //----- test tiled assembly algorithms eq::PixelViewport subPVP = pvp; subPVP.h /= NUM_IMAGES; for( unsigned i = 0; i < NUM_IMAGES; ++i ) { LBASSERT( images[ i ] ); images[ i ]->setPixelViewport( subPVP ); } for( unsigned tiles = 0; tiles < NUM_IMAGES; ++tiles ) { _draw( 0 ); event.area.y() = subPVP.h * (tiles+1); //---- readback of 'tiles' depth images event.data.type = ConfigEvent::READBACK; snprintf( event.formatType, 32, "%d depth tiles", tiles+1 ); event.msec = 0; for( unsigned j = 0; j <= tiles; ++j ) { subPVP.y = pvp.y + j * subPVP.h; eq::Image* image = images[ j ]; LBCHECK( image->allocDownloader( eq::Frame::BUFFER_DEPTH, EQ_COMPRESSOR_TRANSFER_DEPTH_TO_DEPTH_UNSIGNED_INT, glewContext )); image->clearPixelData( eq::Frame::BUFFER_DEPTH ); clock.reset(); image->startReadback( eq::Frame::BUFFER_DEPTH, subPVP, eq::Zoom::NONE, glObjects ); image->finishReadback( eq::Zoom::NONE, glObjects->glewGetContext( )); event.msec += clock.getTimef(); } config->sendEvent( event ); if( tiles == NUM_IMAGES-1 ) for( unsigned j = 0; j <= tiles; ++j ) _saveImage( images[j], "EQ_COMPRESSOR_DATATYPE_DEPTH_UNSIGNED_INT", "tiles" ); //---- readback of 'tiles' color images event.data.type = ConfigEvent::READBACK; snprintf( event.formatType, 32, "%d color tiles", tiles+1 ); event.msec = 0; for( unsigned j = 0; j <= tiles; ++j ) { subPVP.y = pvp.y + j * subPVP.h; eq::Image* image = images[ j ]; LBCHECK( image->allocDownloader( eq::Frame::BUFFER_COLOR, EQ_COMPRESSOR_TRANSFER_RGBA_TO_BGRA, glewContext )); image->clearPixelData( eq::Frame::BUFFER_COLOR ); clock.reset(); image->startReadback( eq::Frame::BUFFER_COLOR, subPVP, eq::Zoom::NONE, glObjects ); image->finishReadback( eq::Zoom::NONE, glObjects->glewGetContext( )); event.msec += clock.getTimef(); } config->sendEvent( event ); if( tiles == NUM_IMAGES-1 ) for( unsigned j = 0; j <= tiles; ++j ) _saveImage( images[j],"EQ_COMPRESSOR_DATATYPE_BGRA","tiles" ); //---- benchmark assembly operations subPVP.y = pvp.y + tiles * subPVP.h; eq::Compositor::ImageOp op; op.channel = this; op.buffers = eq::Frame::BUFFER_COLOR | eq::Frame::BUFFER_DEPTH; op.offset = offset; // fixed-function event.data.type = ConfigEvent::ASSEMBLE; snprintf( event.formatType, 32, "tiles, GL1.1, %d images", tiles+1 ); clock.reset(); for( unsigned j = 0; j <= tiles; ++j ) eq::Compositor::assembleImage( images[j], op ); event.msec = clock.getTimef(); config->sendEvent( event ); // CPU snprintf( event.formatType, 32, "tiles, CPU, %d images", tiles+1 ); std::vector< eq::Frame* > frames; frames.push_back( &_frame ); clock.reset(); eq::Compositor::assembleFramesCPU( frames, this ); event.msec = clock.getTimef(); config->sendEvent( event ); } }
void Channel::_testDepthAssemble() { glGetError(); // reset //----- setup constant data const eq::Images& images = _frame.getImages(); eq::Image* image = images[ 0 ]; LBASSERT( image ); const eq::PixelViewport& pvp = getPixelViewport(); const eq::Vector2i offset( pvp.x, pvp.y ); eq::Vector2i area; area.x() = pvp.w; lunchbox::Clock clock; eq::util::ObjectManager& glObjects = getObjectManager(); const GLEWContext* glewContext = glewGetContext(); //----- test depth-based assembly algorithms for( unsigned i = 0; i < NUM_IMAGES; ++i ) { image = images[ i ]; LBASSERT( image ); image->setPixelViewport( pvp ); } area.y() = pvp.h; for( uint64_t i = 0; i < NUM_IMAGES; ++i ) { _draw( co::uint128_t( i ) ); // fill depth & color image image = images[ i ]; LBCHECK( image->allocDownloader( eq::Frame::BUFFER_COLOR, EQ_COMPRESSOR_TRANSFER_RGBA_TO_BGRA, glewContext )); LBCHECK( image->allocDownloader( eq::Frame::BUFFER_DEPTH, EQ_COMPRESSOR_TRANSFER_DEPTH_TO_DEPTH_UNSIGNED_INT, glewContext )); image->clearPixelData( eq::Frame::BUFFER_COLOR ); image->clearPixelData( eq::Frame::BUFFER_DEPTH ); image->startReadback( eq::Frame::BUFFER_COLOR | eq::Frame::BUFFER_DEPTH, pvp, eq::Zoom::NONE, glObjects ); image->finishReadback( glObjects.glewGetContext( )); LBASSERT( image->hasPixelData( eq::Frame::BUFFER_COLOR )); LBASSERT( image->hasPixelData( eq::Frame::BUFFER_DEPTH )); if( i == NUM_IMAGES-1 ) _saveImage( image,"EQ_COMPRESSOR_DATATYPE_DEPTH_UNSIGNED_INT", "depthAssemble" ); // benchmark eq::Compositor::ImageOp op; op.channel = this; op.buffers = eq::Frame::BUFFER_COLOR | eq::Frame::BUFFER_DEPTH; op.offset = offset; // fixed-function std::stringstream formatType; formatType << "depth, GL1.1, " << i+1 << " images"; clock.reset(); for( unsigned j = 0; j <= i; ++j ) eq::Compositor::assembleImageDB_FF( images[j], op ); float msec = clock.getTimef(); _sendEvent( ASSEMBLE, msec, area, formatType.str(), 0, 0 ); // GLSL if( GLEW_VERSION_2_0 ) { formatType.str(""); formatType << "depth, GLSL, " << i+1 << " images"; clock.reset(); for( unsigned j = 0; j <= i; ++j ) eq::Compositor::assembleImageDB_GLSL( images[j], op ); msec = clock.getTimef(); _sendEvent( ASSEMBLE, msec, area, formatType.str(), 0, 0 ); } // CPU formatType.str(""); formatType << "depth, CPU, " << i+1 << " images"; std::vector< eq::Frame* > frames; frames.push_back( &_frame ); clock.reset(); eq::Compositor::assembleFramesCPU( frames, this ); msec = clock.getTimef(); _sendEvent( ASSEMBLE, msec, area, formatType.str(), 0, 0 ); } }
void Channel::frameViewFinish( const eq::uint128_t& frameID ) { if( stopRendering( )) return; applyBuffer(); const FrameData& frameData = _getFrameData(); Accum& accum = _accum[ lunchbox::getIndexOfLastBit( getEye()) ]; if( accum.buffer ) { const eq::PixelViewport& pvp = getPixelViewport(); const bool isResized = accum.buffer->resize( pvp.w, pvp.h ); if( isResized ) { const View* view = static_cast< const View* >( getView( )); accum.buffer->clear(); accum.step = view->getIdleSteps(); accum.stepsDone = 0; } else if( frameData.isIdle( )) { setupAssemblyState(); if( !_isDone() && accum.transfer ) accum.buffer->accum(); accum.buffer->display(); resetAssemblyState(); } } applyViewport(); _drawOverlay(); _drawHelp(); if( frameData.useStatistics()) drawStatistics(); ConfigEvent event; event.data.originator = getID(); event.data.type = ConfigEvent::IDLE_AA_LEFT; if( frameData.isIdle( )) { event.steps = 0; for( size_t i = 0; i < eq::NUM_EYES; ++i ) event.steps = LB_MAX( event.steps, _accum[i].step ); } else { const View* view = static_cast< const View* >( getView( )); event.steps = view ? view->getIdleSteps() : 0; } // if _jitterStep == 0 and no user redraw event happened, the app will exit // FSAA idle mode and block on the next redraw event. eq::Config* config = getConfig(); config->sendEvent( event ); }
void Channel::_testTiledOperations() { glGetError(); // reset //----- setup constant data const eq::Images& images = _frame.getImages(); LBASSERT( images[0] ); const eq::PixelViewport& pvp = getPixelViewport(); const eq::Vector2i offset( pvp.x, pvp.y ); eq::Vector2i area; area.x() = pvp.w; lunchbox::Clock clock; eq::util::ObjectManager& glObjects = getObjectManager(); const GLEWContext* glewContext = glewGetContext(); //----- test tiled assembly algorithms eq::PixelViewport subPVP = pvp; subPVP.h /= NUM_IMAGES; for( unsigned i = 0; i < NUM_IMAGES; ++i ) { LBASSERT( images[ i ] ); images[ i ]->setPixelViewport( subPVP ); } for( unsigned tiles = 0; tiles < NUM_IMAGES; ++tiles ) { EQ_GL_CALL( _draw( co::uint128_t( ))); area.y() = subPVP.h * (tiles+1); //---- readback of 'tiles' depth images std::stringstream formatType; formatType << tiles+1 << " depth tiles"; float msec = 0; for( unsigned j = 0; j <= tiles; ++j ) { subPVP.y = pvp.y + j * subPVP.h; eq::Image* image = images[ j ]; LBCHECK( image->allocDownloader( eq::Frame::BUFFER_DEPTH, EQ_COMPRESSOR_TRANSFER_DEPTH_TO_DEPTH_UNSIGNED_INT, glewContext )); image->clearPixelData( eq::Frame::BUFFER_DEPTH ); clock.reset(); image->startReadback( eq::Frame::BUFFER_DEPTH, subPVP, eq::Zoom::NONE, glObjects ); image->finishReadback( glObjects.glewGetContext( )); msec += clock.getTimef(); } _sendEvent( READBACK, msec, area, formatType.str(), 0, 0 ); if( tiles == NUM_IMAGES-1 ) for( unsigned j = 0; j <= tiles; ++j ) _saveImage( images[j], "EQ_COMPRESSOR_DATATYPE_DEPTH_UNSIGNED_INT", "tiles" ); //---- readback of 'tiles' color images formatType.str(""); formatType << tiles+1 << " color tiles"; msec = 0; for( unsigned j = 0; j <= tiles; ++j ) { subPVP.y = pvp.y + j * subPVP.h; eq::Image* image = images[ j ]; LBCHECK( image->allocDownloader( eq::Frame::BUFFER_COLOR, EQ_COMPRESSOR_TRANSFER_RGBA_TO_BGRA, glewContext )); image->clearPixelData( eq::Frame::BUFFER_COLOR ); clock.reset(); image->startReadback( eq::Frame::BUFFER_COLOR, subPVP, eq::Zoom::NONE, glObjects ); image->finishReadback( glObjects.glewGetContext( )); msec += clock.getTimef(); } _sendEvent( READBACK, msec, area, formatType.str(), 0, 0 ); if( tiles == NUM_IMAGES-1 ) for( unsigned j = 0; j <= tiles; ++j ) _saveImage( images[j],"EQ_COMPRESSOR_DATATYPE_BGRA","tiles" ); //---- benchmark assembly operations subPVP.y = pvp.y + tiles * subPVP.h; eq::Compositor::ImageOp op; op.channel = this; op.buffers = eq::Frame::BUFFER_COLOR | eq::Frame::BUFFER_DEPTH; op.offset = offset; // fixed-function formatType.str(""); formatType << "tiles, GL1.1, " << tiles+1 << " images"; clock.reset(); for( unsigned j = 0; j <= tiles; ++j ) eq::Compositor::assembleImage( images[j], op ); msec = clock.getTimef(); _sendEvent( ASSEMBLE, msec, area, formatType.str(), 0, 0 ); // CPU formatType.str(""); formatType << "tiles, CPU, " << tiles+1 << " images"; std::vector< eq::Frame* > frames; frames.push_back( &_frame ); clock.reset(); eq::Compositor::assembleFramesCPU( frames, this ); msec = clock.getTimef(); _sendEvent( ASSEMBLE, msec, area, formatType.str(), 0, 0 ); } }
void Channel::_testFormats( float applyZoom ) { glGetError(); // reset //----- setup constant data const eq::Images& images = _frame.getImages(); eq::Image* image = images[ 0 ]; LBASSERT( image ); const eq::PixelViewport& pvp = getPixelViewport(); const eq::Vector2i offset( pvp.x, pvp.y ); const eq::Zoom zoom( applyZoom, applyZoom ); lunchbox::Clock clock; eq::util::ObjectManager& glObjects = getObjectManager(); //----- test all default format/type combinations for( uint32_t i=0; _enums[i].internalFormatString; ++i ) { const uint32_t internalFormat = _enums[i].internalFormat; image->flush(); image->setInternalFormat( eq::Frame::BUFFER_COLOR, internalFormat ); image->setQuality( eq::Frame::BUFFER_COLOR, 0.f ); image->setAlphaUsage( false ); const GLEWContext* glewContext = glewGetContext(); const std::vector< uint32_t >& names = image->findTransferers( eq::Frame::BUFFER_COLOR, glewContext ); for( std::vector< uint32_t >::const_iterator j = names.begin(); j != names.end(); ++j ) { _draw( co::uint128_t( )); image->allocDownloader( eq::Frame::BUFFER_COLOR, *j, glewContext ); image->setPixelViewport( pvp ); const uint32_t outputToken = image->getExternalFormat( eq::Frame::BUFFER_COLOR ); std::stringstream formatType; formatType << std::hex << *j << ':' << _enums[i].internalFormatString << '/' << outputToken << std::dec; // read glFinish(); size_t nLoops = 0; try { clock.reset(); while( clock.getTime64() < 100 /*ms*/ ) { image->startReadback( eq::Frame::BUFFER_COLOR, pvp, zoom, glObjects ); image->finishReadback( glObjects.glewGetContext( )); ++nLoops; } glFinish(); const float msec = clock.getTimef() / float( nLoops ); const GLenum error = glGetError(); // release mode if( error != GL_NO_ERROR ) throw eq::GLException( error ); const eq::PixelData& pixels = image->getPixelData( eq::Frame::BUFFER_COLOR ); const eq::Vector2i area( pixels.pvp.w, pixels.pvp.h ); const uint64_t dataSizeGPU = area.x() * area.y() * _enums[i].pixelSize; const uint64_t dataSizeCPU = image->getPixelDataSize( eq::Frame::BUFFER_COLOR ); _sendEvent( READBACK, msec, area, formatType.str(), dataSizeGPU, dataSizeCPU ); } catch( const eq::GLException& e ) { _sendEvent( READBACK, -static_cast<float>( e.glError ), eq::Vector2i(), formatType.str(), 0, 0 ); continue; } // write eq::Compositor::ImageOp op; op.channel = this; op.buffers = eq::Frame::BUFFER_COLOR; op.offset = offset; op.zoom = zoom; const uint64_t dataSizeCPU = image->getPixelDataSize( eq::Frame::BUFFER_COLOR ); try { clock.reset(); eq::Compositor::assembleImage( image, op ); glFinish(); const float msec = clock.getTimef() / float( nLoops ); const GLenum error = glGetError(); // release mode if( error != GL_NO_ERROR ) throw eq::Exception( error ); const eq::PixelData& pixels = image->getPixelData( eq::Frame::BUFFER_COLOR ); const eq::Vector2i area( pixels.pvp.w, pixels.pvp.h ); const uint64_t dataSizeGPU = image->getPixelDataSize( eq::Frame::BUFFER_COLOR ); _sendEvent( ASSEMBLE, msec, area, formatType.str(), dataSizeGPU, dataSizeCPU ); } catch( const eq::GLException& e ) // debug mode { _sendEvent( ASSEMBLE, -static_cast<float>( e.glError ), eq::Vector2i(), formatType.str(), 0, 0 ); } } } }
Images FrameData::startReadback( const Frame& frame, util::ObjectManager& glObjects, const DrawableConfig& config, const PixelViewports& regions ) { if( _impl->data.buffers == Frame::BUFFER_NONE ) return Images(); const Zoom& zoom = frame.getZoom(); if( !zoom.isValid( )) { LBWARN << "Invalid zoom factor, skipping frame" << std::endl; return Images(); } const eq::PixelViewport& framePVP = getPixelViewport(); const PixelViewport absPVP = framePVP + frame.getOffset(); if( !absPVP.isValid( )) return Images(); Images images; // readback the whole screen when using textures if( getType() == eq::Frame::TYPE_TEXTURE ) { Image* image = newImage( getType(), config ); if( image->startReadback( getBuffers(), absPVP, zoom, glObjects )) images.push_back( image ); image->setOffset( 0, 0 ); return images; } //else read only required regions #if 0 // TODO: issue #85: move automatic ROI detection to eq::Channel PixelViewports regions; if( _impl->data.buffers & Frame::BUFFER_DEPTH && zoom == Zoom::NONE ) regions = _impl->roiFinder->findRegions( _impl->data.buffers, absPVP, zoom, frame.getAssemblyStage(), frame.getFrameID(), glObjects); else regions.push_back( absPVP ); #endif LBASSERT( getType() == eq::Frame::TYPE_MEMORY ); const eq::Pixel& pixel = getPixel(); for( uint32_t i = 0; i < regions.size(); ++i ) { PixelViewport pvp = regions[ i ] + frame.getOffset(); pvp.intersect( absPVP ); if( !pvp.hasArea( )) continue; Image* image = newImage( getType(), config ); if( image->startReadback( getBuffers(), pvp, zoom, glObjects )) images.push_back( image ); pvp -= frame.getOffset(); pvp.apply( zoom ); image->setOffset( (pvp.x - framePVP.x) * pixel.w, (pvp.y - framePVP.y) * pixel.h ); } return images; }
bool Window::configInitAGLWindow() { AGLContext context = getAGLContext(); if( !context ) { sendError( ERROR_AGLWINDOW_NO_CONTEXT ); return false; } // window const bool decoration = getIAttribute(WindowSettings::IATTR_HINT_DECORATION) != OFF; WindowAttributes winAttributes = ( decoration ? kWindowStandardDocumentAttributes : kWindowNoTitleBarAttribute | kWindowNoShadowAttribute | kWindowResizableAttribute ) | kWindowStandardHandlerAttribute | kWindowInWindowMenuAttribute; // top, left, bottom, right const PixelViewport& pvp = getPixelViewport(); const int32_t menuHeight = decoration ? EQ_AGL_MENUBARHEIGHT : 0 ; Rect windowRect = { short( pvp.y + menuHeight ), short( pvp.x ), short( pvp.getYEnd() + menuHeight ), short( pvp.getXEnd( )) }; WindowRef windowRef; Global::enterCarbon(); const OSStatus status = CreateNewWindow( kDocumentWindowClass, winAttributes, &windowRect, &windowRef ); if( status != noErr ) { sendError( ERROR_AGLWINDOW_CREATEWINDOW_FAILED ) << lexical_cast< std::string >( status ); Global::leaveCarbon(); return false; } // window title const std::string& name = getName(); std::stringstream windowTitle; if( name.empty( )) { windowTitle << "Equalizer"; #ifndef NDEBUG windowTitle << " (" << getpid() << ")"; #endif } else windowTitle << name; CFStringRef title = CFStringCreateWithCString( kCFAllocatorDefault, windowTitle.str().c_str(), kCFStringEncodingMacRoman ); SetWindowTitleWithCFString( windowRef, title ); CFRelease( title ); if( !aglSetWindowRef( context, windowRef )) { sendError( ERROR_AGLWINDOW_SETWINDOW_FAILED ) << aglError(); Global::leaveCarbon(); return false; } // show ShowWindow( windowRef ); // Do focus hell ProcessSerialNumber selfProcess = { 0, kCurrentProcess }; SetFrontProcess( &selfProcess ); Global::leaveCarbon(); setCarbonWindow( windowRef ); return true; }
void Channel::frameAssemble( const eq::uint128_t&, const eq::Frames& frames ) { _startAssemble(); eq::PixelViewport coveredPVP; eq::ImageOps dbImages; eq::Zoom zoom( eq::Zoom::NONE ); // Make sure all frames are ready and gather some information on them for( eq::Frame* frame : frames ) { { eq::ChannelStatistics stat( eq::Statistic::CHANNEL_FRAME_WAIT_READY, this ); frame->waitReady( ); } for( eq::Image* image : frame->getImages( )) { eq::ImageOp op( frame, image ); op.offset = frame->getOffset(); const eq::Range& range = image->getContext().range; if( range == eq::Range::ALL ) // 2D frame, assemble directly eq::Compositor::assembleImage( op, this ); else { dbImages.emplace_back( op ); zoom = frame->getZoom(); coveredPVP.merge( image->getPixelViewport() + frame->getOffset()); } } } if( dbImages.empty( )) { resetAssemblyState(); return; } // calculate correct image sequence const bool dbCompose = _image.getContext().range != eq::Range::ALL; coveredPVP.intersect( getPixelViewport( )); if( dbCompose && coveredPVP.hasArea( )) { eq::ImageOp op; op.image = &_image; op.buffers = eq::Frame::BUFFER_COLOR; op.zoom = zoom; op.offset = eq::Vector2i( coveredPVP.x, coveredPVP.y ); dbImages.emplace_back( op ); } _orderImages( dbImages ); // check if current image is in proper position, read back if not if( dbCompose ) { if( _bgColor == eq::Vector3f() && dbImages.front().image == &_image) dbImages.erase( dbImages.begin( )); else if( coveredPVP.hasArea()) { eq::util::ObjectManager& glObjects = getObjectManager(); eq::PixelViewport pvp = getRegion(); pvp.intersect( coveredPVP ); // Update range eq::Range range( 1.f, 0.f ); for( const eq::ImageOp& op : dbImages ) { const eq::Range& r = op.image->getContext().range; range.start = std::min( range.start, r.start ); range.end = std::max( range.end, r.end ); } eq::RenderContext context = _image.getContext(); context.range = range; if( _image.startReadback( eq::Frame::BUFFER_COLOR, pvp, context, zoom, glObjects )) { _image.finishReadback( glewGetContext( )); } clearViewport( coveredPVP ); } } glEnable( GL_BLEND ); LBASSERT( GLEW_EXT_blend_func_separate ); glBlendFuncSeparate( GL_ONE, GL_SRC_ALPHA, GL_ZERO, GL_SRC_ALPHA ); eq::Compositor::blendImages( dbImages, this, 0 ); resetAssemblyState(); }
void Channel::_testFormats( float applyZoom ) { //----- setup constant data const eq::Images& images = _frame.getImages(); eq::Image* image = images[ 0 ]; LBASSERT( image ); Config* config = static_cast< Config* >( getConfig( )); const eq::PixelViewport& pvp = getPixelViewport(); const eq::Vector2i offset( pvp.x, pvp.y ); const eq::Zoom zoom( applyZoom, applyZoom ); lunchbox::Clock clock; eq::Window::ObjectManager* glObjects = getObjectManager(); //----- test all default format/type combinations ConfigEvent event = _createConfigEvent(); glGetError(); for( uint32_t i=0; _enums[i].internalFormatString; ++i ) { const uint32_t internalFormat = _enums[i].internalFormat; image->flush(); image->setInternalFormat( eq::Frame::BUFFER_COLOR, internalFormat ); image->setQuality( eq::Frame::BUFFER_COLOR, 0.f ); image->setAlphaUsage( false ); const GLEWContext* glewContext = glewGetContext(); std::vector< uint32_t > names; image->findTransferers( eq::Frame::BUFFER_COLOR, glewContext, names ); for( std::vector< uint32_t >::const_iterator j = names.begin(); j != names.end(); ++j ) { _draw( 0 ); // setup event.formatType[31] = '\0'; event.data.type = ConfigEvent::READBACK; image->allocDownloader( eq::Frame::BUFFER_COLOR, *j, glewContext ); image->setPixelViewport( pvp ); const uint32_t outputToken = image->getExternalFormat( eq::Frame::BUFFER_COLOR ); snprintf( event.formatType, 32, "%s/%x/%x", _enums[i].internalFormatString, outputToken, *j ); // read glFinish(); size_t nLoops = 0; clock.reset(); while( clock.getTime64() < 100 /*ms*/ ) { image->startReadback( eq::Frame::BUFFER_COLOR, pvp, zoom, glObjects ); image->finishReadback( zoom, glObjects->glewGetContext( )); ++nLoops; } glFinish(); event.msec = clock.getTimef() / float( nLoops ); const eq::PixelData& pixels = image->getPixelData( eq::Frame::BUFFER_COLOR ); event.area.x() = pixels.pvp.w; event.area.y() = pixels.pvp.h; event.dataSizeGPU = pixels.pvp.getArea() * _enums[i].pixelSize; event.dataSizeCPU = image->getPixelDataSize( eq::Frame::BUFFER_COLOR ); GLenum error = glGetError(); if( error != GL_NO_ERROR ) event.msec = -static_cast<float>( error ); config->sendEvent( event ); // write eq::Compositor::ImageOp op; op.channel = this; op.buffers = eq::Frame::BUFFER_COLOR; op.offset = offset; op.zoom = zoom; event.data.type = ConfigEvent::ASSEMBLE; event.dataSizeCPU = image->getPixelDataSize( eq::Frame::BUFFER_COLOR ); clock.reset(); eq::Compositor::assembleImage( image, op ); event.msec = clock.getTimef(); const eq::PixelData& pixelA = image->getPixelData( eq::Frame::BUFFER_COLOR ); event.area.x() = pixelA.pvp.w; event.area.y() = pixelA.pvp.h; event.dataSizeGPU = image->getPixelDataSize( eq::Frame::BUFFER_COLOR ); error = glGetError(); if( error != GL_NO_ERROR ) event.msec = -static_cast<float>( error ); config->sendEvent( event ); } } }
void Channel::_drawHelp() { const FrameData& frameData = _getFrameData(); std::string message = frameData.getMessage(); if( !frameData.showHelp() && message.empty( )) return; applyOverlayState(); if( frameData.showHelp( )) { const eq::util::BitmapFont* font = getWindow()->getSmallFont(); std::string help = EVolve::getHelp(); float y = 340.f; for( size_t pos = help.find( '\n' ); pos != std::string::npos; pos = help.find( '\n' )) { glRasterPos3f( 10.f, y, 0.99f ); font->draw( help.substr( 0, pos )); help = help.substr( pos + 1 ); y -= 16.f; } // last line glRasterPos3f( 10.f, y, 0.99f ); font->draw( help ); } if( !message.empty( )) { const eq::util::BitmapFont* font = getWindow()->getMediumFont(); const eq::Viewport& vp = getViewport(); const eq::PixelViewport& pvp = getPixelViewport(); const float width = pvp.w / vp.w; const float xOffset = vp.x * width; const float height = pvp.h / vp.h; const float yOffset = vp.y * height; const float yMiddle = 0.5f * height; float y = yMiddle - yOffset; for( size_t pos = message.find( '\n' ); pos != std::string::npos; pos = message.find( '\n' )) { glRasterPos3f( 10.f - xOffset, y, 0.99f ); font->draw( message.substr( 0, pos )); message = message.substr( pos + 1 ); y -= 22.f; } // last line glRasterPos3f( 10.f - xOffset, y, 0.99f ); font->draw( message ); } resetOverlayState(); }
void Channel::_testDepthAssemble() { //----- setup constant data const eq::Images& images = _frame.getImages(); eq::Image* image = images[ 0 ]; LBASSERT( image ); eq::Config* config = getConfig(); const eq::PixelViewport& pvp = getPixelViewport(); const eq::Vector2i offset( pvp.x, pvp.y ); ConfigEvent event = _createConfigEvent(); event.area.x() = pvp.w; lunchbox::Clock clock; eq::Window::ObjectManager* glObjects = getObjectManager(); const GLEWContext* glewContext = glewGetContext(); //----- test depth-based assembly algorithms for( unsigned i = 0; i < NUM_IMAGES; ++i ) { image = images[ i ]; LBASSERT( image ); image->setPixelViewport( pvp ); } event.area.y() = pvp.h; for( unsigned i = 0; i < NUM_IMAGES; ++i ) { _draw( i ); // fill depth & color image image = images[ i ]; LBCHECK( image->allocDownloader( eq::Frame::BUFFER_COLOR, EQ_COMPRESSOR_TRANSFER_RGBA_TO_BGRA, glewContext )); LBCHECK( image->allocDownloader( eq::Frame::BUFFER_DEPTH, EQ_COMPRESSOR_TRANSFER_DEPTH_TO_DEPTH_UNSIGNED_INT, glewContext )); image->clearPixelData( eq::Frame::BUFFER_COLOR ); image->clearPixelData( eq::Frame::BUFFER_DEPTH ); image->startReadback( eq::Frame::BUFFER_COLOR | eq::Frame::BUFFER_DEPTH, pvp, eq::Zoom::NONE, glObjects ); image->finishReadback( eq::Zoom::NONE, glObjects->glewGetContext( )); if( i == NUM_IMAGES-1 ) _saveImage( image,"EQ_COMPRESSOR_DATATYPE_DEPTH_UNSIGNED_INT", "depthAssemble" ); // benchmark eq::Compositor::ImageOp op; op.channel = this; op.buffers = eq::Frame::BUFFER_COLOR | eq::Frame::BUFFER_DEPTH; op.offset = offset; // fixed-function event.data.type = ConfigEvent::ASSEMBLE; snprintf( event.formatType, 32, "depth, GL1.1, %d images", i+1 ); clock.reset(); for( unsigned j = 0; j <= i; ++j ) eq::Compositor::assembleImageDB_FF( images[j], op ); event.msec = clock.getTimef(); config->sendEvent( event ); // GLSL if( GLEW_VERSION_2_0 ) { snprintf( event.formatType, 32, "depth, GLSL, %d images", i+1 ); clock.reset(); for( unsigned j = 0; j <= i; ++j ) eq::Compositor::assembleImageDB_GLSL( images[j], op ); event.msec = clock.getTimef(); config->sendEvent( event ); } // CPU snprintf( event.formatType, 32, "depth, CPU, %d images", i+1 ); std::vector< eq::Frame* > frames; frames.push_back( &_frame ); clock.reset(); eq::Compositor::assembleFramesCPU( frames, this ); event.msec = clock.getTimef(); config->sendEvent( event ); } }
bool Window::configInitGL( const eq::uint128_t& initID ) { LBINFO << "-----> Window::configInitGL(" << initID << ", " << getPixelViewport( ) << ", " << getViewport( ) << ")" << std::endl; bool init = false; LBASSERT( !_window.valid( )); if( !eq::Window::configInitGL( initID )) goto out; { const eq::PixelViewport& pvp = getPixelViewport( ); const eq::DrawableConfig& dc = getDrawableConfig( ); osg::ref_ptr< osg::GraphicsContext::Traits > traits = new osg::GraphicsContext::Traits( ); traits->x = pvp.x; traits->y = pvp.y; traits->width = pvp.w; traits->height = pvp.h; traits->red = traits->blue = traits->green = dc.colorBits; traits->alpha = dc.alphaBits; traits->stencil = dc.stencilBits; traits->doubleBuffer = dc.doublebuffered; #if 0 traits->pbuffer = ( getIAttribute( IATTR_HINT_DRAWABLE ) == eq::PBUFFER ); #endif std::ostringstream version; version << dc.glVersion; traits->glContextVersion = version.str( ); #if 0 Window* sharedWindow = static_cast< Window* >( getSharedContextWindow( )); if( sharedWindow && ( sharedWindow != this )) traits->sharedContext = sharedWindow->getGraphicsContext( ); #endif _window = new osgViewer::GraphicsWindowEmbedded( traits ); static_cast< Node* >( getNode( ))->addGraphicsContext( _window ); const unsigned int maxTexturePoolSize = osg::DisplaySettings::instance( )->getMaxTexturePoolSize( ); const unsigned int maxBufferObjectPoolSize = osg::DisplaySettings::instance( )->getMaxBufferObjectPoolSize( ); if( maxTexturePoolSize > 0 ) getState( )->setMaxTexturePoolSize( maxTexturePoolSize ); if( maxBufferObjectPoolSize > 0 ) getState( )->setMaxBufferObjectPoolSize( maxBufferObjectPoolSize ); initCapabilities( _window ); } init = true; out: if( !init ) cleanup( ); LBINFO << "<----- Window::configInitGL(" << initID << ")" << std::endl; return init; }