void ScriptDebuggerPrivate::positionChange(qint64 scriptId,
                                           int lineNumber, int /*columnNumber*/)
{
    ScriptInfo *info = 0;
    bool enterInteractiveMode = false;

    if (m_bpManager->hasBreakpoints()) {
        // check if we hit a breakpoint
        info = m_scripts.value(scriptId);
        QScriptContext *ctx = engine()->currentContext();
        QScriptContextInfo ctxInfo(ctx);
        QScriptValue callee = ctx->callee();

        // try fileName:lineNumber
        int bpid = m_bpManager->findBreakpoint(info->fileName(), lineNumber);
        if ((bpid != -1) && m_bpManager->isBreakpointEnabled(bpid)) {
            message(QString::fromLatin1("Breakpoint %0 at %1:%2")
                    .arg(bpid + 1).arg(info->fileName()).arg(lineNumber));
            if (m_bpManager->isBreakpointSingleShot(bpid))
                m_bpManager->removeBreakpoint(bpid);
        }
        if (bpid == -1) {
            // try function
            bpid = m_bpManager->findBreakpoint(callee);
            if ((bpid != -1) && m_bpManager->isBreakpointEnabled(bpid)) {
                message(QString::fromLatin1("Breakpoint %0, %1()")
                        .arg(bpid + 1).arg(ctxInfo.functionName()));
                if (m_bpManager->isBreakpointSingleShot(bpid))
                    m_bpManager->removeBreakpoint(bpid);
            }
        }
        if ((bpid == -1) && !ctxInfo.functionName().isEmpty()) {
            // try functionName:fileName
            bpid = m_bpManager->findBreakpoint(ctxInfo.functionName(), ctxInfo.fileName());
            if ((bpid != -1) && m_bpManager->isBreakpointEnabled(bpid)) {
                message(QString::fromLatin1("Breakpoint %0, %1():%2").arg(bpid + 1)
                        .arg(ctxInfo.functionName()).arg(ctxInfo.fileName()));
                if (m_bpManager->isBreakpointSingleShot(bpid))
                    m_bpManager->removeBreakpoint(bpid);
            }
        }

        enterInteractiveMode = (bpid != -1);
    }

    switch (mode()) {
    case Run:
        break;

    case StepInto:
        enterInteractiveMode = true;
        break;

    case StepOver:
        enterInteractiveMode = enterInteractiveMode || (m_stepDepth <= 0);
        break;
    }

    if (enterInteractiveMode) {
        if (!info)
            info = m_scripts.value(scriptId);
        Q_ASSERT(info);        
        message(QString::fromLatin1("%0\t%1").arg(lineNumber).arg(info->lineText(lineNumber)));
        interactive();
    }
}
/*!
  Applies the given \a command to the given \a backend.
*/
QScriptDebuggerResponse QScriptDebuggerCommandExecutor::execute(
    QScriptDebuggerBackend *backend,
    const QScriptDebuggerCommand &command)
{
    QScriptDebuggerResponse response;
    switch (command.type()) {
    case QScriptDebuggerCommand::None:
        break;

    case QScriptDebuggerCommand::Interrupt:
        backend->interruptEvaluation();
        break;

    case QScriptDebuggerCommand::Continue:
        if (backend->engine()->isEvaluating()) {
            backend->continueEvalution();
            response.setAsync(true);
        }
        break;

    case QScriptDebuggerCommand::StepInto: {
        QVariant attr = command.attribute(QScriptDebuggerCommand::StepCount);
        int count = attr.isValid() ? attr.toInt() : 1;
        backend->stepInto(count);
        response.setAsync(true);
    }   break;

    case QScriptDebuggerCommand::StepOver: {
        QVariant attr = command.attribute(QScriptDebuggerCommand::StepCount);
        int count = attr.isValid() ? attr.toInt() : 1;
        backend->stepOver(count);
        response.setAsync(true);
    }   break;

    case QScriptDebuggerCommand::StepOut:
        backend->stepOut();
        response.setAsync(true);
        break;

    case QScriptDebuggerCommand::RunToLocation:
        backend->runToLocation(command.fileName(), command.lineNumber());
        response.setAsync(true);
        break;

    case QScriptDebuggerCommand::RunToLocationByID:
        backend->runToLocation(command.scriptId(), command.lineNumber());
        response.setAsync(true);
        break;

    case QScriptDebuggerCommand::ForceReturn: {
        int contextIndex = command.contextIndex();
        QScriptDebuggerValue value = command.scriptValue();
        QScriptEngine *engine = backend->engine();
        QScriptValue realValue = value.toScriptValue(engine);
        backend->returnToCaller(contextIndex, realValue);
        response.setAsync(true);
    }   break;

    case QScriptDebuggerCommand::Resume:
        backend->resume();
        response.setAsync(true);
        break;

    case QScriptDebuggerCommand::SetBreakpoint: {
        QScriptBreakpointData data = command.breakpointData();
        if (!data.isValid())
            data = QScriptBreakpointData(command.fileName(), command.lineNumber());
        int id = backend->setBreakpoint(data);
        response.setResult(id);
    }   break;

    case QScriptDebuggerCommand::DeleteBreakpoint: {
        int id = command.breakpointId();
        if (!backend->deleteBreakpoint(id))
            response.setError(QScriptDebuggerResponse::InvalidBreakpointID);
    }   break;

    case QScriptDebuggerCommand::DeleteAllBreakpoints:
        backend->deleteAllBreakpoints();
        break;

    case QScriptDebuggerCommand::GetBreakpoints: {
        QScriptBreakpointMap bps = backend->breakpoints();
        if (!bps.isEmpty())
            response.setResult(bps);
    }   break;

    case QScriptDebuggerCommand::GetBreakpointData: {
        int id = command.breakpointId();
        QScriptBreakpointData data = backend->breakpointData(id);
        if (data.isValid())
            response.setResult(data);
        else
            response.setError(QScriptDebuggerResponse::InvalidBreakpointID);
    }   break;

    case QScriptDebuggerCommand::SetBreakpointData: {
        int id = command.breakpointId();
        QScriptBreakpointData data = command.breakpointData();
        if (!backend->setBreakpointData(id, data))
            response.setError(QScriptDebuggerResponse::InvalidBreakpointID);
    }   break;

    case QScriptDebuggerCommand::GetScripts: {
        QScriptScriptMap scripts = backend->scripts();
        if (!scripts.isEmpty())
            response.setResult(scripts);
    }   break;

    case QScriptDebuggerCommand::GetScriptData: {
        qint64 id = command.scriptId();
        QScriptScriptData data = backend->scriptData(id);
        if (data.isValid())
            response.setResult(data);
        else
            response.setError(QScriptDebuggerResponse::InvalidScriptID);
    }   break;

    case QScriptDebuggerCommand::ScriptsCheckpoint:
        backend->scriptsCheckpoint();
        response.setResult(QVariant::fromValue(backend->scriptsDelta()));
        break;

    case QScriptDebuggerCommand::GetScriptsDelta:
        response.setResult(QVariant::fromValue(backend->scriptsDelta()));
        break;

    case QScriptDebuggerCommand::ResolveScript:
        response.setResult(backend->resolveScript(command.fileName()));
        break;

    case QScriptDebuggerCommand::GetBacktrace:
        response.setResult(backend->backtrace());
        break;

    case QScriptDebuggerCommand::GetContextCount:
        response.setResult(backend->contextCount());
        break;

    case QScriptDebuggerCommand::GetContextState: {
        QScriptContext *ctx = backend->context(command.contextIndex());
        if (ctx)
            response.setResult(static_cast<int>(ctx->state()));
        else
            response.setError(QScriptDebuggerResponse::InvalidContextIndex);
    }   break;

    case QScriptDebuggerCommand::GetContextID: {
        int idx = command.contextIndex();
        if ((idx >= 0) && (idx < backend->contextCount()))
            response.setResult(backend->contextIds()[idx]);
        else
            response.setError(QScriptDebuggerResponse::InvalidContextIndex);
    }   break;

    case QScriptDebuggerCommand::GetContextInfo: {
        QScriptContext *ctx = backend->context(command.contextIndex());
        if (ctx)
            response.setResult(QScriptContextInfo(ctx));
        else
            response.setError(QScriptDebuggerResponse::InvalidContextIndex);
    }   break;

    case QScriptDebuggerCommand::GetThisObject: {
        QScriptContext *ctx = backend->context(command.contextIndex());
        if (ctx)
            response.setResult(ctx->thisObject());
        else
            response.setError(QScriptDebuggerResponse::InvalidContextIndex);
    }   break;

    case QScriptDebuggerCommand::GetActivationObject: {
        QScriptContext *ctx = backend->context(command.contextIndex());
        if (ctx)
            response.setResult(ctx->activationObject());
        else
            response.setError(QScriptDebuggerResponse::InvalidContextIndex);
    }   break;

    case QScriptDebuggerCommand::GetScopeChain: {
        QScriptContext *ctx = backend->context(command.contextIndex());
        if (ctx) {
            QScriptDebuggerValueList dest;
            QScriptValueList src = ctx->scopeChain();
            for (int i = 0; i < src.size(); ++i)
                dest.append(src.at(i));
            response.setResult(dest);
        } else {
            response.setError(QScriptDebuggerResponse::InvalidContextIndex);
        }
    }   break;

    case QScriptDebuggerCommand::ContextsCheckpoint: {
        response.setResult(QVariant::fromValue(backend->contextsCheckpoint()));
    }   break;

    case QScriptDebuggerCommand::GetPropertyExpressionValue: {
        QScriptContext *ctx = backend->context(command.contextIndex());
        int lineNumber = command.lineNumber();
        QVariant attr = command.attribute(QScriptDebuggerCommand::UserAttribute);
        QStringList path = attr.toStringList();
        if (!ctx || path.isEmpty())
            break;
        QScriptContextInfo ctxInfo(ctx);
        if (ctx->callee().isValid()
            && ((lineNumber < ctxInfo.functionStartLineNumber())
                || (lineNumber > ctxInfo.functionEndLineNumber()))) {
            break;
        }
        QScriptValueList objects;
        int pathIndex = 0;
        if (path.at(0) == QLatin1String("this")) {
            objects.append(ctx->thisObject());
            ++pathIndex;
        } else {
            objects << ctx->scopeChain();
        }
        for (int i = 0; i < objects.size(); ++i) {
            QScriptValue val = objects.at(i);
            for (int j = pathIndex; val.isValid() && (j < path.size()); ++j) {
                val = val.property(path.at(j));
            }
            if (val.isValid()) {
                bool hadException = (ctx->state() == QScriptContext::ExceptionState);
                QString str = val.toString();
                if (!hadException && backend->engine()->hasUncaughtException())
                    backend->engine()->clearExceptions();
                response.setResult(str);
                break;
            }
        }
    }   break;

    case QScriptDebuggerCommand::GetCompletions: {
        QScriptContext *ctx = backend->context(command.contextIndex());
        QVariant attr = command.attribute(QScriptDebuggerCommand::UserAttribute);
        QStringList path = attr.toStringList();
        if (!ctx || path.isEmpty())
            break;
        QScriptValueList objects;
        QString prefix = path.last();
        QSet<QString> matches;
        if (path.size() > 1) {
            const QString &topLevelIdent = path.at(0);
            QScriptValue obj;
            if (topLevelIdent == QLatin1String("this")) {
                obj = ctx->thisObject();
            } else {
                QScriptValueList scopeChain;
                scopeChain = ctx->scopeChain();
                for (int i = 0; i < scopeChain.size(); ++i) {
                    QScriptValue oo = scopeChain.at(i).property(topLevelIdent);
                    if (oo.isObject()) {
                        obj = oo;
                        break;
                    }
                }
            }
            for (int i = 1; obj.isObject() && (i < path.size()-1); ++i)
                obj = obj.property(path.at(i));
            if (obj.isValid())
                objects.append(obj);
        } else {
            objects << ctx->scopeChain();
            QStringList keywords;
            keywords.append(QString::fromLatin1("this"));
            keywords.append(QString::fromLatin1("true"));
            keywords.append(QString::fromLatin1("false"));
            keywords.append(QString::fromLatin1("null"));
            for (int i = 0; i < keywords.size(); ++i) {
                const QString &kwd = keywords.at(i);
                if (isPrefixOf(prefix, kwd))
                    matches.insert(kwd);
            }
        }

        for (int i = 0; i < objects.size(); ++i) {
            QScriptValue obj = objects.at(i);
            while (obj.isObject()) {
                QScriptValueIterator it(obj);
                while (it.hasNext()) {
                    it.next();
                    QString propertyName = it.name();
                    if (isPrefixOf(prefix, propertyName))
                        matches.insert(propertyName);
                }
                obj = obj.prototype();
            }
        }
        QStringList matchesList = matches.toList();
        qStableSort(matchesList);
        response.setResult(matchesList);
    }   break;

    case QScriptDebuggerCommand::NewScriptObjectSnapshot: {
        int id = backend->newScriptObjectSnapshot();
        response.setResult(id);
    }   break;

    case QScriptDebuggerCommand::ScriptObjectSnapshotCapture: {
        int id = command.snapshotId();
        QScriptObjectSnapshot *snap = backend->scriptObjectSnapshot(id);
        Q_ASSERT(snap != 0);
        QScriptDebuggerValue object = command.scriptValue();
        Q_ASSERT(object.type() == QScriptDebuggerValue::ObjectValue);
        QScriptEngine *engine = backend->engine();
        QScriptValue realObject = object.toScriptValue(engine);
        Q_ASSERT(realObject.isObject());
        QScriptObjectSnapshot::Delta delta = snap->capture(realObject);
        QScriptDebuggerObjectSnapshotDelta result;
        result.removedProperties = delta.removedProperties;
        bool didIgnoreExceptions = backend->ignoreExceptions();
        backend->setIgnoreExceptions(true);
        for (int i = 0; i < delta.changedProperties.size(); ++i) {
            const QScriptValueProperty &src = delta.changedProperties.at(i);
            bool hadException = engine->hasUncaughtException();
            QString str = src.value().toString();
            if (!hadException && engine->hasUncaughtException())
                engine->clearExceptions();
            QScriptDebuggerValueProperty dest(src.name(), src.value(), str, src.flags());
            result.changedProperties.append(dest);
        }
        for (int j = 0; j < delta.addedProperties.size(); ++j) {
            const QScriptValueProperty &src = delta.addedProperties.at(j);
            bool hadException = engine->hasUncaughtException();
            QString str = src.value().toString();
            if (!hadException && engine->hasUncaughtException())
                engine->clearExceptions();
            QScriptDebuggerValueProperty dest(src.name(), src.value(), str, src.flags());
            result.addedProperties.append(dest);
        }
        backend->setIgnoreExceptions(didIgnoreExceptions);
        response.setResult(QVariant::fromValue(result));
    }   break;

    case QScriptDebuggerCommand::DeleteScriptObjectSnapshot: {
        int id = command.snapshotId();
        backend->deleteScriptObjectSnapshot(id);
    }   break;

    case QScriptDebuggerCommand::NewScriptValueIterator: {
        QScriptDebuggerValue object = command.scriptValue();
        Q_ASSERT(object.type() == QScriptDebuggerValue::ObjectValue);
        QScriptEngine *engine = backend->engine();
        QScriptValue realObject = object.toScriptValue(engine);
        Q_ASSERT(realObject.isObject());
        int id = backend->newScriptValueIterator(realObject);
        response.setResult(id);
    }   break;

    case QScriptDebuggerCommand::GetPropertiesByIterator: {
        int id = command.iteratorId();
        int count = 1000;
        QScriptValueIterator *it = backend->scriptValueIterator(id);
        Q_ASSERT(it != 0);
        QScriptDebuggerValuePropertyList props;
        for (int i = 0; (i < count) && it->hasNext(); ++i) {
            it->next();
            QString name = it->name();
            QScriptValue value = it->value();
            QString valueAsString = value.toString();
            QScriptValue::PropertyFlags flags = it->flags();
            QScriptDebuggerValueProperty prp(name, value, valueAsString, flags);
            props.append(prp);
        }
        response.setResult(props);
    }   break;

    case QScriptDebuggerCommand::DeleteScriptValueIterator: {
        int id = command.iteratorId();
        backend->deleteScriptValueIterator(id);
    }   break;

    case QScriptDebuggerCommand::Evaluate: {
        int contextIndex = command.contextIndex();
        QString program = command.program();
        QString fileName = command.fileName();
        int lineNumber = command.lineNumber();
        backend->evaluate(contextIndex, program, fileName, lineNumber);
        response.setAsync(true);
    }   break;

    case QScriptDebuggerCommand::ScriptValueToString: {
        QScriptDebuggerValue value = command.scriptValue();
        QScriptEngine *engine = backend->engine();
        QScriptValue realValue = value.toScriptValue(engine);
        response.setResult(realValue.toString());
    }   break;

    case QScriptDebuggerCommand::SetScriptValueProperty: {
        QScriptDebuggerValue object = command.scriptValue();
        QScriptEngine *engine = backend->engine();
        QScriptValue realObject = object.toScriptValue(engine);
        QScriptDebuggerValue value = command.subordinateScriptValue();
        QScriptValue realValue = value.toScriptValue(engine);
        QString name = command.name();
        realObject.setProperty(name, realValue);
    }   break;

    case QScriptDebuggerCommand::ClearExceptions:
        backend->engine()->clearExceptions();
        break;

    case QScriptDebuggerCommand::UserCommand:
    case QScriptDebuggerCommand::MaxUserCommand:
        break;
    }
    return response;
}
bool ScriptDebuggerPrivate::executeCommand(const QString &command, const QStringList &args)
{
    if (command == QLatin1String("c")
        || command == QLatin1String("continue")) {
        setMode(Run);
        return true;
    } else if (command == QLatin1String("s")
               || command == QLatin1String("step")) {
        setMode(StepInto);
        return true;
    } else if (command == QLatin1String("n")
               || command == QLatin1String("next")) {
        setMode(StepOver);
        m_stepDepth = 0;
        return true;
    } else if (command == QLatin1String("f")
               || command == QLatin1String("frame")) {
        bool ok = false;
        int index = args.value(0).toInt(&ok);
        if (ok) {
            if (index < 0 || index >= frameCount()) {
                errorMessage("No such frame.");
            } else {
                setCurrentFrameIndex(index);
                QScriptContext *ctx = currentFrameContext();
                message(QString::fromLatin1("#%0  %1").arg(index).arg(ctx->toString()));
            }
        }
    } else if (command == QLatin1String("bt")
               || command == QLatin1String("backtrace")) {
        QScriptContext *ctx = engine()->currentContext();
        int index = -1;
        while (ctx) {
            ++index;
            QString line = ctx->toString();
            message(QString::fromLatin1("#%0  %1").arg(index).arg(line));
            ctx = ctx->parentContext();
        }
    } else if (command == QLatin1String("up")) {
        int index = currentFrameIndex() + 1;
        if (index == frameCount()) {
            errorMessage(QString::fromLatin1("Initial frame selected; you cannot go up."));
        } else {
            setCurrentFrameIndex(index);
            QScriptContext *ctx = currentFrameContext();
            message(QString::fromLatin1("#%0  %1").arg(index).arg(ctx->toString()));
        }
    } else if (command == QLatin1String("down")) {
        int index = currentFrameIndex() - 1;
        if (index < 0) {
            errorMessage(QString::fromLatin1("Bottom (innermost) frame selected; you cannot go down."));
        } else {
            setCurrentFrameIndex(index);
            QScriptContext *ctx = currentFrameContext();
            message(QString::fromLatin1("#%0  %1").arg(index).arg(ctx->toString()));
        }
    } else if (command == QLatin1String("b")
               || command == QLatin1String("break")) {
        QString str = args.value(0);
        int colonIndex = str.indexOf(QLatin1Char(':'));
        if (colonIndex != -1) {
            // filename:line form
            QString fileName = str.left(colonIndex);
            int lineNumber = str.mid(colonIndex+1).toInt();
            int id = m_bpManager->setBreakpoint(fileName, lineNumber);
            message(QString::fromLatin1("Breakpoint %0 at %1, line %2.").arg(id+1).arg(fileName).arg(lineNumber));
        } else {
            // function
            QScriptValue fun = engine()->globalObject().property(str);
            if (fun.isFunction()) {
                int id = m_bpManager->setBreakpoint(fun);
                message(QString::fromLatin1("Breakpoint %0 at %1().").arg(id+1).arg(str));
            }
        }
    } else if (command == QLatin1String("d")
               || command == QLatin1String("delete")) {
        int id = args.value(0).toInt() - 1;
        m_bpManager->removeBreakpoint(id);
    } else if (command == QLatin1String("disable")) {
        int id = args.value(0).toInt() - 1;
        m_bpManager->setBreakpointEnabled(id, false);
    } else if (command == QLatin1String("enable")) {
        int id = args.value(0).toInt() - 1;
        m_bpManager->setBreakpointEnabled(id, true);
    } else if (command == QLatin1String("list")) {
        QScriptContext *ctx = currentFrameContext();
        ScriptInfo *progInfo = scriptInfo(ctx);
        if (!progInfo) {
            errorMessage("No source text available for this frame.");
        } else {
            QScriptContextInfo ctxInfo(ctx);
            bool ok;
            int line = args.value(0).toInt(&ok);
            if (ok) {
                line = qMax(1, line - 5);
            } else {
                line = listLineNumber();
                if (line == -1)
                    line = qMax(progInfo->lineNumber(), ctxInfo.lineNumber() - 5);
            }
            for (int i = line; i < line + 10; ++i) {
                message(QString::fromLatin1("%0\t%1").arg(i).arg(progInfo->lineText(i)));
            }
            setListLineNumber(line + 10);
        }
    } else if (command == QLatin1String("info")) {
        if (args.size() < 1) {
        } else {
            QString what = args.value(0);
            if (what == QLatin1String("locals")) {
                QScriptValueIterator it(currentFrameContext()->activationObject());
                while (it.hasNext()) {
                    it.next();
                    QString line;
                    line.append(it.name());
                    line.append(QLatin1String(" = "));
                    line.append(safeValueToString(it.value()));
                    message(line);
                }
            }
        }
    } else if (command == QLatin1String("help")) {
        message("continue - continue execution\n"
                "step     - step into statement\n"
                "next     - step over statement\n"
                "list     - show where you are\n"
                "\n"
                "break    - set breakpoint\n"
                "delete   - remove breakpoint\n"
                "disable  - disable breakpoint\n"
                "enable   - enable breakpoint\n"
                "\n"
                "backtrace - show backtrace\n"
                "up       - one frame up\n"
                "down     - one frame down\n"
                "frame    - set frame\n"
                "\n"
                "info locals - show local variables");
    } else {
        errorMessage(QString::fromLatin1("Undefined command \"%0\". Try \"help\".")
                     .arg(command));
    }

    return false;
}
Beispiel #4
0
    virtual void operator()( osg::RenderInfo& renderInfo ) const
    {
        osg::State& state = *renderInfo.getState();
        const unsigned int ctx = state.getContextID();
        osg::FBOExtensions* fboExt = osg::FBOExtensions::instance( ctx, true );

        PerContextInfo& ctxInfo( _contextInfo[ ctx ] );
        if( ctxInfo.__glGetFramebufferAttachmentParameteriv == NULL )
        {
            // Initialize function pointer for FBO query.
            osg::setGLExtensionFuncPtr( ctxInfo.__glGetFramebufferAttachmentParameteriv, "glGetFramebufferAttachmentParameteriv" );
            if( ctxInfo.__glGetFramebufferAttachmentParameteriv == NULL )
                osg::setGLExtensionFuncPtr( ctxInfo.__glGetFramebufferAttachmentParameteriv, "glGetFramebufferAttachmentParameterivEXT" );
            if( ctxInfo.__glGetFramebufferAttachmentParameteriv == NULL )
            {
                osg::notify( osg::ALWAYS ) << "Can't get function pointer glGetFramebufferAttachmentParameteriv" << std::endl;
                return;
            }
        }

        const GLint width = _cam->getViewport()->width();
        const GLint height = _cam->getViewport()->height();

#if 0
        // Make sure something is actually bound.
        GLint drawFBO( -1 );
        glGetIntegerv( GL_DRAW_FRAMEBUFFER_BINDING, &drawFBO );
#endif

        // BlitFramebuffer blits to all attached color buffers in the
        // draw FBO. We only want to blit to attachment1, so aave
        // attachment0 and then unbind it.
        GLint destColorTex0( -1 );
        ctxInfo.__glGetFramebufferAttachmentParameteriv(
            GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
            GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &destColorTex0 );
        osgwTools::glFramebufferTexture2D( fboExt,
            GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
            GL_TEXTURE_2D, 0, 0 );

        // Verification
        //osg::notify( osg::ALWAYS ) << "Dest " << std::hex << destColorTex0 << std::endl;

        // Set draw and read buffers to attachment1 to read from correct
        // buffer and avoid INVALID_FRAMEBUFFER_OPERATION error.
        glDrawBuffer( GL_COLOR_ATTACHMENT1 );
        glReadBuffer( GL_COLOR_ATTACHMENT1 );

        // Blit, from (multisampled read FBO) attachment1 to
        // (non-multisampled draw FBO) attachment1.
        osgwTools::glBlitFramebuffer( fboExt, 0, 0, width, height, 0, 0, width, height,
            GL_COLOR_BUFFER_BIT, GL_NEAREST );

        // Restore draw and read buffers
        glDrawBuffer( GL_COLOR_ATTACHMENT0 );
        glReadBuffer( GL_COLOR_ATTACHMENT0 );

        // Restore the draw FBO's attachment0.
        osgwTools::glFramebufferTexture2D( fboExt,
            GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
            GL_TEXTURE_2D, destColorTex0, 0 );

        // We disabled FBO unbinding in the RenderStage,
        // so do it ourself here.
        /* TBD update to use FBOUtils.h */
        osgwTools::glBindFramebuffer( fboExt, GL_FRAMEBUFFER, 0 );
    }