<commit_before>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "Common.h"
#include "ParserWorker.h"
#include "Parser.h"
#include "parser/Task.h"
#include "File.h"
#include "medialibrary/filesystem/Errors.h"
#include "Folder.h"

namespace medialibrary
{
namespace parser
{

Worker::Worker()
    : m_ml( nullptr )
    , m_parserCb( nullptr )
    , m_stopParser( false )
    , m_paused( false )
    , m_idle( true )
    , m_lastTaskInfo()
{
    assert( m_lastTaskInfo.parentFolderId == 0 && m_lastTaskInfo.fsDir == nullptr );
}

void Worker::start()
{
    // This function is called from a locked context.

    // Ensure we don't start multiple times.
    assert( m_thread.joinable() == false );
    m_thread = compat::Thread{ &Worker::mainloop, this };
}

void Worker::pause()
{
    std::lock_guard<compat::Mutex> lock( m_lock );
    m_paused = true;
}

void Worker::resume()
{
    std::lock_guard<compat::Mutex> lock( m_lock );
    m_paused = false;
    m_cond.notify_all();
}

void Worker::signalStop()
{
    {
        std::lock_guard<compat::Mutex> lock( m_lock );
        m_stopParser = true;
    }
    m_cond.notify_all();
    m_service->stop();
}

void Worker::stop()
{
    if ( m_thread.joinable() == true )
        m_thread.join();
}

void Worker::parse( std::shared_ptr<Task> t )
{
    // Even if no threads appear to be started, we need to lock this in case
    // we're currently doing a stop/start
    {
        std::lock_guard<compat::Mutex> lock( m_lock );

        m_tasks.push( std::move( t ) );
        if ( m_thread.get_id() == compat::Thread::id{} )
        {
            start();
            return;
        }
    }
    m_cond.notify_all();
}

void Worker::parse( std::vector<std::shared_ptr<Task>> tasks )
{
    {
        std::lock_guard<compat::Mutex> lock( m_lock );

        for ( auto& t : tasks )
            m_tasks.push( std::move( t ) );
        if ( m_thread.get_id() == compat::Thread::id{} )
        {
            start();
            return;
        }
    }
    m_cond.notify_all();
}

void Worker::initialize( MediaLibrary* ml, IParserCb* parserCb,
                         std::shared_ptr<IParserService> service )
{
    m_ml = ml;
    m_service = std::move( service );
    m_parserCb = parserCb;
}

bool Worker::isIdle() const
{
    return m_idle.load( std::memory_order_acquire );
}

void Worker::flush()
{
    std::unique_lock<compat::Mutex> lock( m_lock );
    while ( m_tasks.empty() == false )
        m_tasks.pop();
    m_service->onFlushing();
}

void Worker::restart()
{
    m_service->onRestarted();
}

void Worker::mainloop()
{
    // It would be unsafe to call name() at the end of this function, since
    // we might stop the thread during ParserService destruction. This implies
    // that the underlying service has been deleted already.
    std::string serviceName = m_service->name();
    LOG_INFO("Entering ParserService [", serviceName, "] thread");
    setIdle( false );

    // Run the service specific initializer
    m_service->initialize( m_ml );

    while ( true )
    {
        std::shared_ptr<Task> task;
        ML_UNHANDLED_EXCEPTION_INIT
        {
            {
                std::unique_lock<compat::Mutex> lock( m_lock );
                if ( m_stopParser == true )
                    break;
                if ( m_tasks.empty() == true || m_paused == true )
                {
                    LOG_DEBUG( "Halting ParserService [", serviceName, "] mainloop" );
                    setIdle( true );
                    m_cond.wait( lock, [this]() {
                        return ( m_tasks.empty() == false && m_paused == false )
                                || m_stopParser == true;
                    });
                    LOG_DEBUG( "Resuming ParserService [", serviceName, "] mainloop" );
                    // We might have been woken up because the parser is being destroyed
                    if ( m_stopParser  == true )
                        break;
                    setIdle( false );
                }
                // Otherwise it's safe to assume we have at least one element.
                LOG_DEBUG('[', serviceName, "] has ", m_tasks.size(), " tasks remaining" );
                task = std::move( m_tasks.front() );
                m_tasks.pop();
            }
            if ( task->isStepCompleted( m_service->targetedStep() ) == true )
            {
                LOG_DEBUG( "Skipping completed task [", serviceName, "] on ", task->mrl() );
                m_parserCb->done( std::move( task ), Status::Success );
                continue;
            }
            if ( task->needEntityRestoration() == true )
            {
                if ( task->restoreLinkedEntities( m_lastTaskInfo ) == false )
                {
                    m_parserCb->done( std::move( task ), Status::TemporaryUnavailable );
                    continue;
                }
            }
            Status status;
            try
            {
                LOG_DEBUG( "Executing ", serviceName, " task on ", task->mrl() );
                auto chrono = std::chrono::steady_clock::now();
                auto file = std::static_pointer_cast<File>( task->file() );

                if ( file != nullptr && file->isRemovable() )
                {
                    auto folder = Folder::fetch( m_ml, file->folderId() );
                    assert( folder != nullptr );
                    if ( folder == nullptr || folder->isPresent() == false )
                    {
                        LOG_DEBUG( "Postponing parsing of ", file->rawMrl(),
                                  " until the device containing it gets mounted back" );
                        m_parserCb->done( std::move( task ), Status::TemporaryUnavailable );
                        continue;
                    }
                }
                task->startParserStep();
                status = m_service->run( *task );
                auto duration = std::chrono::steady_clock::now() - chrono;
                LOG_DEBUG( "Done executing ", serviceName, " task on ", task->mrl(), " in ",
                           std::chrono::duration_cast<std::chrono::milliseconds>( duration ).count(),
                           "ms. Result: ",
                           static_cast<std::underlying_type_t<parser::Status>>( status ) );
            }
            catch ( const fs::errors::DeviceRemoved& )
            {
                LOG_ERROR( "Parsing of ", task->mrl(), " was interrupted "
                           "due to its containing device being unmounted" );
                status = Status::TemporaryUnavailable;
            }
            catch ( const fs::errors::Exception& ex )
            {
                LOG_ERROR( "Caught an FS exception during ", task->mrl(), " [", serviceName, "] parsing: ", ex.what() );
                status = Status::Fatal;
            }
            if ( handleServiceResult( *task, status ) == false )
                status = Status::Fatal;
            m_parserCb->done( std::move( task ), status );
        }
        ML_UNHANDLED_EXCEPTION_BODY( serviceName.c_str() )
    }
    LOG_INFO("Exiting ParserService [", serviceName, "] thread");
    setIdle( true );
}

void Worker::setIdle(bool isIdle)
{
    // Calling the idleChanged callback will trigger a call to isIdle, so set the value before
    // invoking it, otherwise we have an incoherent state.
    auto expected = !isIdle;
    if ( m_idle.compare_exchange_strong( expected, isIdle, std::memory_order_acq_rel ) == true )
    {
        m_parserCb->onIdleChanged( isIdle );
    }
}

bool Worker::handleServiceResult( Task& task, Status status )
{
    if ( status == Status::Success )
    {
        task.markStepCompleted( m_service->targetedStep() );
        // We don't want to save the extraction step in database, as restarting a
        // task with extraction completed but analysis uncompleted wouldn't run
        // the extraction again, causing the analysis to run with no info.
        if ( m_service->targetedStep() != Step::MetadataExtraction )
            return task.saveParserStep();
        // We don't want to reset the entire retry count, as we would be stuck in
        // a "loop" in case the metadata analysis fails (we'd always reset the retry
        // count to zero, then fail, then run the extraction again, reset the retry,
        // fail the analysis, and so on.
        // We can't not increment the retry count for metadata extraction, since
        // in case a file makes (lib)VLC crash, we would always try again, and
        // therefor we would keep on crashing.
        // However we don't want to just increment the retry count, since it
        // would reach the maximum value too quickly (extraction would set retry
        // count to 1, analysis to 2, and in case of failure, next run would set
        // it over 3, while we only tried 2 times. Instead we just decrement it
        // when the extraction step succeeds
        return task.decrementRetryCount();
    }
    else if ( status == Status::Completed )
    {
        task.markStepCompleted( Step::Completed );
        return task.saveParserStep();
    }
    else if ( status == Status::Discarded )
    {
        return Task::destroy( m_ml, task.id() );
    }
    return true;
}

}
}
<commit_msg>Revert "parser: Don't toggle idle state outside of the worker"<commit_after>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "Common.h"
#include "ParserWorker.h"
#include "Parser.h"
#include "parser/Task.h"
#include "File.h"
#include "medialibrary/filesystem/Errors.h"
#include "Folder.h"

namespace medialibrary
{
namespace parser
{

Worker::Worker()
    : m_ml( nullptr )
    , m_parserCb( nullptr )
    , m_stopParser( false )
    , m_paused( false )
    , m_idle( true )
    , m_lastTaskInfo()
{
    assert( m_lastTaskInfo.parentFolderId == 0 && m_lastTaskInfo.fsDir == nullptr );
}

void Worker::start()
{
    // This function is called from a locked context.

    // Ensure we don't start multiple times.
    assert( m_thread.joinable() == false );
    m_thread = compat::Thread{ &Worker::mainloop, this };
}

void Worker::pause()
{
    std::lock_guard<compat::Mutex> lock( m_lock );
    m_paused = true;
}

void Worker::resume()
{
    std::lock_guard<compat::Mutex> lock( m_lock );
    m_paused = false;
    m_cond.notify_all();
}

void Worker::signalStop()
{
    {
        std::lock_guard<compat::Mutex> lock( m_lock );
        m_stopParser = true;
    }
    m_cond.notify_all();
    m_service->stop();
}

void Worker::stop()
{
    if ( m_thread.joinable() == true )
        m_thread.join();
}

void Worker::parse( std::shared_ptr<Task> t )
{
    // Even if no threads appear to be started, we need to lock this in case
    // we're currently doing a stop/start
    {
        std::lock_guard<compat::Mutex> lock( m_lock );

        // Avoid flickering from idle/not idle when not many tasks are running.
        // The thread calling parse for the next parser step might not have
        // something left to do and would turn idle, potentially causing all
        // services to be idle for a very short time, until this parser
        // thread awakes/starts, causing the global parser idle state to be
        // restored back to false.
        // Since we are queuing a task, we already know that this thread is
        // not idle
        // However, we must not override the idle state if the worker was paused
        // since the pausing thread is waiting for the worker to go idle.
        if ( m_paused == false )
            setIdle( false );

        m_tasks.push( std::move( t ) );
        if ( m_thread.get_id() == compat::Thread::id{} )
        {
            start();
            return;
        }
    }
    m_cond.notify_all();
}

void Worker::parse( std::vector<std::shared_ptr<Task>> tasks )
{
    {
        std::lock_guard<compat::Mutex> lock( m_lock );

        if ( m_paused == false )
            setIdle( false );

        for ( auto& t : tasks )
            m_tasks.push( std::move( t ) );
        if ( m_thread.get_id() == compat::Thread::id{} )
        {
            start();
            return;
        }
    }
    m_cond.notify_all();
}

void Worker::initialize( MediaLibrary* ml, IParserCb* parserCb,
                         std::shared_ptr<IParserService> service )
{
    m_ml = ml;
    m_service = std::move( service );
    m_parserCb = parserCb;
}

bool Worker::isIdle() const
{
    return m_idle.load( std::memory_order_acquire );
}

void Worker::flush()
{
    std::unique_lock<compat::Mutex> lock( m_lock );
    while ( m_tasks.empty() == false )
        m_tasks.pop();
    m_service->onFlushing();
}

void Worker::restart()
{
    m_service->onRestarted();
}

void Worker::mainloop()
{
    // It would be unsafe to call name() at the end of this function, since
    // we might stop the thread during ParserService destruction. This implies
    // that the underlying service has been deleted already.
    std::string serviceName = m_service->name();
    LOG_INFO("Entering ParserService [", serviceName, "] thread");
    m_parserCb->onIdleChanged( false );

    // Run the service specific initializer
    m_service->initialize( m_ml );

    while ( true )
    {
        std::shared_ptr<Task> task;
        ML_UNHANDLED_EXCEPTION_INIT
        {
            {
                std::unique_lock<compat::Mutex> lock( m_lock );
                if ( m_stopParser == true )
                    break;
                if ( m_tasks.empty() == true || m_paused == true )
                {
                    LOG_DEBUG( "Halting ParserService [", serviceName, "] mainloop" );
                    setIdle( true );
                    m_cond.wait( lock, [this]() {
                        return ( m_tasks.empty() == false && m_paused == false )
                                || m_stopParser == true;
                    });
                    LOG_DEBUG( "Resuming ParserService [", serviceName, "] mainloop" );
                    // We might have been woken up because the parser is being destroyed
                    if ( m_stopParser  == true )
                        break;
                    setIdle( false );
                }
                // Otherwise it's safe to assume we have at least one element.
                LOG_DEBUG('[', serviceName, "] has ", m_tasks.size(), " tasks remaining" );
                task = std::move( m_tasks.front() );
                m_tasks.pop();
            }
            if ( task->isStepCompleted( m_service->targetedStep() ) == true )
            {
                LOG_DEBUG( "Skipping completed task [", serviceName, "] on ", task->mrl() );
                m_parserCb->done( std::move( task ), Status::Success );
                continue;
            }
            if ( task->needEntityRestoration() == true )
            {
                if ( task->restoreLinkedEntities( m_lastTaskInfo ) == false )
                {
                    m_parserCb->done( std::move( task ), Status::TemporaryUnavailable );
                    continue;
                }
            }
            Status status;
            try
            {
                LOG_DEBUG( "Executing ", serviceName, " task on ", task->mrl() );
                auto chrono = std::chrono::steady_clock::now();
                auto file = std::static_pointer_cast<File>( task->file() );

                if ( file != nullptr && file->isRemovable() )
                {
                    auto folder = Folder::fetch( m_ml, file->folderId() );
                    assert( folder != nullptr );
                    if ( folder == nullptr || folder->isPresent() == false )
                    {
                        LOG_DEBUG( "Postponing parsing of ", file->rawMrl(),
                                  " until the device containing it gets mounted back" );
                        m_parserCb->done( std::move( task ), Status::TemporaryUnavailable );
                        continue;
                    }
                }
                task->startParserStep();
                status = m_service->run( *task );
                auto duration = std::chrono::steady_clock::now() - chrono;
                LOG_DEBUG( "Done executing ", serviceName, " task on ", task->mrl(), " in ",
                           std::chrono::duration_cast<std::chrono::milliseconds>( duration ).count(),
                           "ms. Result: ",
                           static_cast<std::underlying_type_t<parser::Status>>( status ) );
            }
            catch ( const fs::errors::DeviceRemoved& )
            {
                LOG_ERROR( "Parsing of ", task->mrl(), " was interrupted "
                           "due to its containing device being unmounted" );
                status = Status::TemporaryUnavailable;
            }
            catch ( const fs::errors::Exception& ex )
            {
                LOG_ERROR( "Caught an FS exception during ", task->mrl(), " [", serviceName, "] parsing: ", ex.what() );
                status = Status::Fatal;
            }
            if ( handleServiceResult( *task, status ) == false )
                status = Status::Fatal;
            m_parserCb->done( std::move( task ), status );
        }
        ML_UNHANDLED_EXCEPTION_BODY( serviceName.c_str() )
    }
    LOG_INFO("Exiting ParserService [", serviceName, "] thread");
    setIdle( true );
}

void Worker::setIdle(bool isIdle)
{
    // Calling the idleChanged callback will trigger a call to isIdle, so set the value before
    // invoking it, otherwise we have an incoherent state.
    auto expected = !isIdle;
    if ( m_idle.compare_exchange_strong( expected, isIdle, std::memory_order_acq_rel ) == true )
    {
        m_parserCb->onIdleChanged( isIdle );
    }
}

bool Worker::handleServiceResult( Task& task, Status status )
{
    if ( status == Status::Success )
    {
        task.markStepCompleted( m_service->targetedStep() );
        // We don't want to save the extraction step in database, as restarting a
        // task with extraction completed but analysis uncompleted wouldn't run
        // the extraction again, causing the analysis to run with no info.
        if ( m_service->targetedStep() != Step::MetadataExtraction )
            return task.saveParserStep();
        // We don't want to reset the entire retry count, as we would be stuck in
        // a "loop" in case the metadata analysis fails (we'd always reset the retry
        // count to zero, then fail, then run the extraction again, reset the retry,
        // fail the analysis, and so on.
        // We can't not increment the retry count for metadata extraction, since
        // in case a file makes (lib)VLC crash, we would always try again, and
        // therefor we would keep on crashing.
        // However we don't want to just increment the retry count, since it
        // would reach the maximum value too quickly (extraction would set retry
        // count to 1, analysis to 2, and in case of failure, next run would set
        // it over 3, while we only tried 2 times. Instead we just decrement it
        // when the extraction step succeeds
        return task.decrementRetryCount();
    }
    else if ( status == Status::Completed )
    {
        task.markStepCompleted( Step::Completed );
        return task.saveParserStep();
    }
    else if ( status == Status::Discarded )
    {
        return Task::destroy( m_ml, task.id() );
    }
    return true;
}

}
}
<|endoftext|>


<commit_before>//
// Created by david on 2019-03-18.
//
#include <algorithms/class_algorithm_status.h>
#include <glog/logging.h>
#include <string>
#include <tensors/class_tensors_finite.h>
#include <tensors/model/class_model_finite.h>
#include <tools/common/log.h>
#include <tools/common/prof.h>
#include <tools/finite/measure.h>
#include <tools/finite/opt-internal/ceres_base.h>
#include <tools/finite/opt-internal/ceres_direct_functor.h>
#include <tools/finite/opt-internal/ceres_subspace_functor.h>
#include <tools/finite/opt-internal/opt-internal.h>
#include <tools/finite/opt-internal/report.h>
#include <tools/finite/opt_mps.h>

tools::finite::opt::opt_mps tools::finite::opt::find_excited_state(const class_tensors_finite &tensors, const class_algorithm_status &status, OptMode optMode,
                                                                   OptSpace optSpace, OptType optType) {
    double  energy_reduced = tools::finite::measure::energy_reduced(tensors);
    opt_mps initial_mps("current state", tensors.get_multisite_mps(), tensors.active_sites,
                        tools::finite::measure::energy(tensors) - energy_reduced, // Eigval
                        energy_reduced,                                           // Energy reduced for full system
                        tools::finite::measure::energy_variance(tensors),
                        1.0, // Overlap
                        tensors.get_length());

    return find_excited_state(tensors, initial_mps, status, optMode, optSpace, optType);
}

tools::finite::opt::opt_mps tools::finite::opt::find_excited_state(const class_tensors_finite &tensors, const opt_mps &initial_mps,
                                                                   const class_algorithm_status &status, OptMode optMode, OptSpace optSpace, OptType optType) {
    auto t_opt = tools::common::profile::prof[AlgorithmType::xDMRG]["t_opt"]->tic_token();
    tools::log->trace("Starting optimization: mode [{}] | space [{}] | type [{}] | position [{}] | sites {} | shape {} = {}", enum2str(optMode),
                      enum2str(optSpace), enum2str(optType), status.position, tensors.active_sites, tensors.active_problem_dims(),
                      tensors.active_problem_size());

    using namespace opt::internal;
    static bool googleLogginghasInitialized = false;
    if(not googleLogginghasInitialized) {
        googleLogginghasInitialized = true;
#if defined(CERCES_INTERNAL_MINIGLOG_GLOG_LOGGING_H_)
        google::InitGoogleLogging(const_cast<char *>(tools::log->name().c_str()));
#else
        google::InitGoogleLogging(tools::log->name().c_str());
        google::SetStderrLogging(3);
//#pragma message "Using verbose glog"
//        google::SetStderrLogging(0);
#endif
    }

    /* clang-format off */

    /* Alglib has a good read comparing NCG and LBFGS https://www.alglib.net/optimization/lbfgsandcg.php
     * Some noteworthy points:
     *      "L-BFGS algorithm is quite easy to tune.
     *       The only parameter to tune is number of correction pairs M - number of function/gradient pairs
     *       used to build quadratic model (i.e. rank). This parameter is passed to the function which is used
     *       to create optimizer object. Increase of M decreases number of function evaluations and iterations
     *       needed to converge, but increases computational overhead associated with iteration. On well-conditioned
     *       problems M can be as small as 3-10. If function changes rapidly in some directions and slowly in
     *       other ones, then you can try increasing M in order to increase convergence."
     *
     *  Testing here shows that lbfgs is much better than NCG.
     *  Testing also shows that increasing lbfgs rank can give better results at some cost in some cases,
     *  in particular when a problem is ill-conditioned.
     *
     */

    /*
     * When a problem is ill-conditioned, for instance when the difference in eigenvalues is small,
     * we need to increase the lbfgs rank.
     * Read more here:
     *   kth.diva-portal.org/smash/get/diva2:1438308/FULLTEXT01.pdf (page 22)
     *   https://www.deeplearningbook.org/contents/optimization.html (page 307)
     *
     */

    ceres_default_options.line_search_type                           = ceres::LineSearchType::WOLFE;
    ceres_default_options.line_search_interpolation_type             = ceres::LineSearchInterpolationType::CUBIC;
    ceres_default_options.line_search_direction_type                 = ceres::LineSearchDirectionType::LBFGS;
    ceres_default_options.max_num_iterations                         = 20000;
    ceres_default_options.max_lbfgs_rank                             = 128; // Tested: around 8-32 seems to be a good compromise, anything larger incurs a large overhead. The overhead means 2x computation time at ~64
    ceres_default_options.use_approximate_eigenvalue_bfgs_scaling    = true;  // Tested: True makes a huge difference, takes longer steps at each iteration and generally converges faster/to better variance
    ceres_default_options.min_line_search_step_size                  = 1e-20;//std::numeric_limits<double>::epsilon();
    ceres_default_options.max_line_search_step_contraction           = 1e-3; // 1e-3
    ceres_default_options.min_line_search_step_contraction           = 0.6; // 0.6
    ceres_default_options.max_line_search_step_expansion             = 10; // 10
    ceres_default_options.max_num_line_search_step_size_iterations   = 200;
    ceres_default_options.max_num_line_search_direction_restarts     = 80; //5
    ceres_default_options.line_search_sufficient_function_decrease   = 1e-4; //Tested, doesn't seem to matter between [1e-1 to 1e-4]. Default is fine: 1e-4
    ceres_default_options.line_search_sufficient_curvature_decrease  = 0.9;//0.9 // This one should be above 0.5. Below, it makes retries at every step and starts taking twice as long for no added benefit. Tested 0.9 to be sweetspot
    ceres_default_options.max_solver_time_in_seconds                 = 60*60;//60*2;
    ceres_default_options.function_tolerance                         = 1e-14; // Tested, 1e-6 seems to be a sweetspot
    ceres_default_options.gradient_tolerance                         = 1e-14;
    ceres_default_options.parameter_tolerance                        = 1e-20;
    ceres_default_options.minimizer_progress_to_stdout               = false; //tools::log->level() <= spdlog::level::trace;
    ceres_default_options.logging_type                               = ceres::LoggingType::PER_MINIMIZER_ITERATION;

    if(status.algorithm_has_stuck_for > 0){
        ceres_default_options.max_lbfgs_rank                            = 128; // Tested: around 8-32 seems to be a good compromise,but larger is more precise sometimes. Overhead goes from 1.2x to 2x computation time at in 8 -> 64
    }

    //    Progress log definitions:
    //    f is the value of the objective function.
    //    d is the change in the value of the objective function if the step computed in this iteration is accepted.
    //    g is the inf norm of the gradient (i.e. the largest element of |grad f| )
    //    h is the change in the parameter vector.
    //    s is the optimal step length computed by the line search.
    //    it is the time take by the current iteration.
    //    tt is the total time taken by the minimizer.

    /* clang-format on */
    if(initial_mps.get_sites() != tensors.active_sites)
        throw std::runtime_error(fmt::format("mismatch in active sites: initial_mps {} | active {}", initial_mps.get_sites(), tensors.active_sites));

    initial_mps.validate_candidate();
    opt_mps result;
    switch(optSpace) {
        /* clang-format off */
        case OptSpace::SUBSPACE_ONLY:       result = internal::ceres_subspace_optimization(tensors,initial_mps,status, optType, optMode,optSpace); break;
        case OptSpace::SUBSPACE_AND_DIRECT: result = internal::ceres_subspace_optimization(tensors,initial_mps,status, optType, optMode,optSpace); break;
        case OptSpace::DIRECT:              result = internal::ceres_direct_optimization(tensors,initial_mps,status, optType,optMode,optSpace); break;
        case OptSpace::KRYLOV_ENERGY:        result = internal::arpack_energy_optimization(tensors,initial_mps,status, optType,optMode,optSpace); break;
        case OptSpace::KRYLOV_VARIANCE:      result = internal::arpack_variance_optimization(tensors,initial_mps,status, optType,optMode,optSpace); break;
            /* clang-format on */
    }
    // Finish up and print reports
    reports::print_bfgs_report();
    reports::print_time_report();
    result.set_optspace(optSpace);
    result.set_optmode(optMode);
    result.validate_result();
    return result;
}

Eigen::Tensor<std::complex<double>, 3> tools::finite::opt::find_ground_state(const class_tensors_finite &tensors, StateRitz ritz) {
    return internal::ground_state_optimization(tensors, ritz);
}

double tools::finite::opt::internal::windowed_func_abs(double x, double window) {
    if(std::abs(x) >= window)
        return std::abs(x) - window;
    else
        return 0;
}
double tools::finite::opt::internal::windowed_grad_abs(double x, double window) {
    if(std::abs(x) >= window)
        return sgn(x);
    else
        return 0.0;
}

double tools::finite::opt::internal::windowed_func_pow(double x, double window) {
    if(std::abs(x) >= window)
        return x * x - window * window;
    else
        return 0.0;
}
double tools::finite::opt::internal::windowed_grad_pow(double x, double window) {
    if(std::abs(x) >= window)
        return 2.0 * x;
    else
        return 0.0;
}

std::pair<double, double> tools::finite::opt::internal::windowed_func_grad(double x, double window) {
    double func = 0;
    double grad = 0;
    if(std::abs(x) >= window) {
        if(x > 0) {
            func = (x - window) * (x - window);
            grad = 2 * (x - window);
        } else {
            func = (x + window) * (x + window);
            grad = 2 * (x + window);
        }
    }

    return std::make_pair(func, grad);
}

long tools::finite::opt::internal::get_ops(long d, long chiL, long chiR, long m) {
    if(chiR > chiL) return get_ops(d, chiR, chiL, m);
    if(d > m) {
        // d first
        long step1 = chiL * chiL * chiR * m * m * d;
        long step2 = chiL * chiR * d * m * m * m * (d * m + 1);
        long step3 = chiL * chiR * d * m * (chiR * m * m * m + m * m + 1);
        return step1 + step2 + step3;
    } else {
        // m first
        long step1 = chiL * chiL * chiR * m * d;
        long step2 = chiL * chiR * d * m * m * (d * m + 1);
        long step3 = chiL * chiR * chiR * d * m * (m + 1);
        return step1 + step2 + step3;
    }
}

template<typename FunctorType>
tools::finite::opt::internal::CustomLogCallback<FunctorType>::CustomLogCallback(const FunctorType &functor_) : functor(functor_) {
    if(not log) log = tools::Logger::setLogger("xDMRG");
    log->set_level(tools::log->level());
    if(log->level() == spdlog::level::debug) {
        freq_log_iter = 10;
        freq_log_time = 10;
        init_log_time = 1;
    }
}

template<typename FunctorType>
ceres::CallbackReturnType tools::finite::opt::internal::CustomLogCallback<FunctorType>::operator()(const ceres::IterationSummary &summary) {
    //    if(summary.iteration > 100 and summary.cost_change < 1e-5 and summary.gradient_max_norm < 10.0 and summary.step_norm < 1e-6) return
    //    ceres::SOLVER_ABORT;
    functor.set_delta_f(std::abs(summary.cost_change/summary.cost));
    functor.set_grad_max_norm(summary.gradient_max_norm);
    if(not log) return ceres::SOLVER_CONTINUE;
    if(log->level() >= spdlog::level::debug) return ceres::SOLVER_CONTINUE;
    if(summary.iteration - last_log_iter < freq_log_iter and summary.iteration > init_log_iter) return ceres::SOLVER_CONTINUE;
    if(summary.cumulative_time_in_seconds - last_log_time < freq_log_time and summary.cumulative_time_in_seconds > init_log_time) return ceres::SOLVER_CONTINUE;
    last_log_time = summary.cumulative_time_in_seconds;
    last_log_iter = summary.iteration;
    /* clang-format off */
    log->trace("LBFGS: iter {:>5} f {:>8.5f} |Δf| {:>3.2e} |∇f|∞ {:>3.2e} "
               "|ΔΨ| {:3.2e} |Ψ|-1 {:3.2e} ls {:3.2e} evals {:>4}/{:<4} "
               "t_step {:<} t_iter {:<} t_tot {:<} GOp/s {:<4.2f} | energy {:<18.15f} log₁₀var {:<6.6f}",
               summary.iteration,
               summary.cost,
               summary.cost_change,
               summary.gradient_max_norm,
               summary.step_norm, // By lbfgs
               functor.get_norm_offset(),
               std::abs(summary.step_size), // By line search
               functor.get_count() - last_count,
               functor.get_count(),
               fmt::format("{:>6.0f} ms",summary.step_solver_time_in_seconds * 1000),
               fmt::format("{:>6.0f} ms",summary.iteration_time_in_seconds * 1000),
               fmt::format("{:>5.3f} s",summary.cumulative_time_in_seconds),
               static_cast<double>(functor.get_ops()) / functor.t_vH2->get_last_interval()/1e9,
               functor.get_energy_per_site(),
               std::log10(functor.get_variance())
    );
    last_count = functor.get_count();
    /* clang-format on */
    return ceres::SOLVER_CONTINUE;
}

template class tools::finite::opt::internal::CustomLogCallback<tools::finite::opt::internal::ceres_subspace_functor<double>>;
template class tools::finite::opt::internal::CustomLogCallback<tools::finite::opt::internal::ceres_direct_functor<double>>;
template class tools::finite::opt::internal::CustomLogCallback<tools::finite::opt::internal::ceres_subspace_functor<std::complex<double>>>;
template class tools::finite::opt::internal::CustomLogCallback<tools::finite::opt::internal::ceres_direct_functor<std::complex<double>>>;<commit_msg>Refactoring: uptaded old ground state code to use the new opt_mps struct to return results with metadata<commit_after>//
// Created by david on 2019-03-18.
//
#include <algorithms/class_algorithm_status.h>
#include <glog/logging.h>
#include <string>
#include <tensors/class_tensors_finite.h>
#include <tensors/model/class_model_finite.h>
#include <tools/common/log.h>
#include <tools/common/prof.h>
#include <tools/finite/measure.h>
#include <tools/finite/opt-internal/ceres_base.h>
#include <tools/finite/opt-internal/ceres_direct_functor.h>
#include <tools/finite/opt-internal/ceres_subspace_functor.h>
#include <tools/finite/opt-internal/opt-internal.h>
#include <tools/finite/opt-internal/report.h>
#include <tools/finite/opt_mps.h>

tools::finite::opt::opt_mps tools::finite::opt::find_excited_state(const class_tensors_finite &tensors, const class_algorithm_status &status, OptMode optMode,
                                                                   OptSpace optSpace, OptType optType) {
    double  energy_reduced = tools::finite::measure::energy_reduced(tensors);
    opt_mps initial_mps("current state", tensors.get_multisite_mps(), tensors.active_sites,
                        tools::finite::measure::energy(tensors) - energy_reduced, // Eigval
                        energy_reduced,                                           // Energy reduced for full system
                        tools::finite::measure::energy_variance(tensors),
                        1.0, // Overlap
                        tensors.get_length());

    return find_excited_state(tensors, initial_mps, status, optMode, optSpace, optType);
}

tools::finite::opt::opt_mps tools::finite::opt::find_excited_state(const class_tensors_finite &tensors, const opt_mps &initial_mps,
                                                                   const class_algorithm_status &status, OptMode optMode, OptSpace optSpace, OptType optType) {
    auto t_opt = tools::common::profile::prof[AlgorithmType::xDMRG]["t_opt"]->tic_token();
    tools::log->trace("Starting optimization: mode [{}] | space [{}] | type [{}] | position [{}] | sites {} | shape {} = {}", enum2str(optMode),
                      enum2str(optSpace), enum2str(optType), status.position, tensors.active_sites, tensors.active_problem_dims(),
                      tensors.active_problem_size());

    using namespace opt::internal;
    static bool googleLogginghasInitialized = false;
    if(not googleLogginghasInitialized) {
        googleLogginghasInitialized = true;
#if defined(CERCES_INTERNAL_MINIGLOG_GLOG_LOGGING_H_)
        google::InitGoogleLogging(const_cast<char *>(tools::log->name().c_str()));
#else
        google::InitGoogleLogging(tools::log->name().c_str());
        google::SetStderrLogging(3);
//#pragma message "Using verbose glog"
//        google::SetStderrLogging(0);
#endif
    }

    /* clang-format off */

    /* Alglib has a good read comparing NCG and LBFGS https://www.alglib.net/optimization/lbfgsandcg.php
     * Some noteworthy points:
     *      "L-BFGS algorithm is quite easy to tune.
     *       The only parameter to tune is number of correction pairs M - number of function/gradient pairs
     *       used to build quadratic model (i.e. rank). This parameter is passed to the function which is used
     *       to create optimizer object. Increase of M decreases number of function evaluations and iterations
     *       needed to converge, but increases computational overhead associated with iteration. On well-conditioned
     *       problems M can be as small as 3-10. If function changes rapidly in some directions and slowly in
     *       other ones, then you can try increasing M in order to increase convergence."
     *
     *  Testing here shows that lbfgs is much better than NCG.
     *  Testing also shows that increasing lbfgs rank can give better results at some cost in some cases,
     *  in particular when a problem is ill-conditioned.
     *
     */

    /*
     * When a problem is ill-conditioned, for instance when the difference in eigenvalues is small,
     * we need to increase the lbfgs rank.
     * Read more here:
     *   kth.diva-portal.org/smash/get/diva2:1438308/FULLTEXT01.pdf (page 22)
     *   https://www.deeplearningbook.org/contents/optimization.html (page 307)
     *
     */

    ceres_default_options.line_search_type                           = ceres::LineSearchType::WOLFE;
    ceres_default_options.line_search_interpolation_type             = ceres::LineSearchInterpolationType::CUBIC;
    ceres_default_options.line_search_direction_type                 = ceres::LineSearchDirectionType::LBFGS;
    ceres_default_options.max_num_iterations                         = 20000;
    ceres_default_options.max_lbfgs_rank                             = 128; // Tested: around 8-32 seems to be a good compromise, anything larger incurs a large overhead. The overhead means 2x computation time at ~64
    ceres_default_options.use_approximate_eigenvalue_bfgs_scaling    = true;  // Tested: True makes a huge difference, takes longer steps at each iteration and generally converges faster/to better variance
    ceres_default_options.min_line_search_step_size                  = 1e-20;//std::numeric_limits<double>::epsilon();
    ceres_default_options.max_line_search_step_contraction           = 1e-3; // 1e-3
    ceres_default_options.min_line_search_step_contraction           = 0.6; // 0.6
    ceres_default_options.max_line_search_step_expansion             = 10; // 10
    ceres_default_options.max_num_line_search_step_size_iterations   = 200;
    ceres_default_options.max_num_line_search_direction_restarts     = 80; //5
    ceres_default_options.line_search_sufficient_function_decrease   = 1e-4; //Tested, doesn't seem to matter between [1e-1 to 1e-4]. Default is fine: 1e-4
    ceres_default_options.line_search_sufficient_curvature_decrease  = 0.9;//0.9 // This one should be above 0.5. Below, it makes retries at every step and starts taking twice as long for no added benefit. Tested 0.9 to be sweetspot
    ceres_default_options.max_solver_time_in_seconds                 = 60*60;//60*2;
    ceres_default_options.function_tolerance                         = 1e-14; // Tested, 1e-6 seems to be a sweetspot
    ceres_default_options.gradient_tolerance                         = 1e-14;
    ceres_default_options.parameter_tolerance                        = 1e-20;
    ceres_default_options.minimizer_progress_to_stdout               = false; //tools::log->level() <= spdlog::level::trace;
    ceres_default_options.logging_type                               = ceres::LoggingType::PER_MINIMIZER_ITERATION;

    if(status.algorithm_has_stuck_for > 0){
        ceres_default_options.max_lbfgs_rank                            = 128; // Tested: around 8-32 seems to be a good compromise,but larger is more precise sometimes. Overhead goes from 1.2x to 2x computation time at in 8 -> 64
    }

    //    Progress log definitions:
    //    f is the value of the objective function.
    //    d is the change in the value of the objective function if the step computed in this iteration is accepted.
    //    g is the inf norm of the gradient (i.e. the largest element of |grad f| )
    //    h is the change in the parameter vector.
    //    s is the optimal step length computed by the line search.
    //    it is the time take by the current iteration.
    //    tt is the total time taken by the minimizer.

    /* clang-format on */
    if(initial_mps.get_sites() != tensors.active_sites)
        throw std::runtime_error(fmt::format("mismatch in active sites: initial_mps {} | active {}", initial_mps.get_sites(), tensors.active_sites));

    initial_mps.validate_candidate();
    opt_mps result;
    switch(optSpace) {
        /* clang-format off */
        case OptSpace::SUBSPACE_ONLY:       result = internal::ceres_subspace_optimization(tensors,initial_mps,status, optType, optMode,optSpace); break;
        case OptSpace::SUBSPACE_AND_DIRECT: result = internal::ceres_subspace_optimization(tensors,initial_mps,status, optType, optMode,optSpace); break;
        case OptSpace::DIRECT:              result = internal::ceres_direct_optimization(tensors,initial_mps,status, optType,optMode,optSpace); break;
        case OptSpace::KRYLOV_ENERGY:        result = internal::krylov_energy_optimization(tensors,initial_mps,status, optType,optMode,optSpace); break;
        case OptSpace::KRYLOV_VARIANCE:      result = internal::krylov_variance_optimization(tensors,initial_mps,status, optType,optMode,optSpace); break;
            /* clang-format on */
    }
    // Finish up and print reports
    reports::print_bfgs_report();
    reports::print_time_report();
    result.set_optspace(optSpace);
    result.set_optmode(optMode);
    result.validate_result();
    return result;
}

tools::finite::opt::opt_mps tools::finite::opt::find_ground_state(const class_tensors_finite &tensors, const class_algorithm_status & status,  StateRitz ritz) {
    return internal::ground_state_optimization(tensors,status, ritz);
}

double tools::finite::opt::internal::windowed_func_abs(double x, double window) {
    if(std::abs(x) >= window)
        return std::abs(x) - window;
    else
        return 0;
}
double tools::finite::opt::internal::windowed_grad_abs(double x, double window) {
    if(std::abs(x) >= window)
        return sgn(x);
    else
        return 0.0;
}

double tools::finite::opt::internal::windowed_func_pow(double x, double window) {
    if(std::abs(x) >= window)
        return x * x - window * window;
    else
        return 0.0;
}
double tools::finite::opt::internal::windowed_grad_pow(double x, double window) {
    if(std::abs(x) >= window)
        return 2.0 * x;
    else
        return 0.0;
}

std::pair<double, double> tools::finite::opt::internal::windowed_func_grad(double x, double window) {
    double func = 0;
    double grad = 0;
    if(std::abs(x) >= window) {
        if(x > 0) {
            func = (x - window) * (x - window);
            grad = 2 * (x - window);
        } else {
            func = (x + window) * (x + window);
            grad = 2 * (x + window);
        }
    }

    return std::make_pair(func, grad);
}

long tools::finite::opt::internal::get_ops(long d, long chiL, long chiR, long m) {
    if(chiR > chiL) return get_ops(d, chiR, chiL, m);
    if(d > m) {
        // d first
        long step1 = chiL * chiL * chiR * m * m * d;
        long step2 = chiL * chiR * d * m * m * m * (d * m + 1);
        long step3 = chiL * chiR * d * m * (chiR * m * m * m + m * m + 1);
        return step1 + step2 + step3;
    } else {
        // m first
        long step1 = chiL * chiL * chiR * m * d;
        long step2 = chiL * chiR * d * m * m * (d * m + 1);
        long step3 = chiL * chiR * chiR * d * m * (m + 1);
        return step1 + step2 + step3;
    }
}

template<typename FunctorType>
tools::finite::opt::internal::CustomLogCallback<FunctorType>::CustomLogCallback(const FunctorType &functor_) : functor(functor_) {
    if(not log) log = tools::Logger::setLogger("xDMRG");
    log->set_level(tools::log->level());
    if(log->level() == spdlog::level::debug) {
        freq_log_iter = 10;
        freq_log_time = 10;
        init_log_time = 1;
    }
}

template<typename FunctorType>
ceres::CallbackReturnType tools::finite::opt::internal::CustomLogCallback<FunctorType>::operator()(const ceres::IterationSummary &summary) {
    //    if(summary.iteration > 100 and summary.cost_change < 1e-5 and summary.gradient_max_norm < 10.0 and summary.step_norm < 1e-6) return
    //    ceres::SOLVER_ABORT;
    functor.set_delta_f(std::abs(summary.cost_change/summary.cost));
    if(not log) return ceres::SOLVER_CONTINUE;
    if(log->level() >= spdlog::level::debug) return ceres::SOLVER_CONTINUE;
    if(summary.iteration - last_log_iter < freq_log_iter and summary.iteration > init_log_iter) return ceres::SOLVER_CONTINUE;
    if(summary.cumulative_time_in_seconds - last_log_time < freq_log_time and summary.cumulative_time_in_seconds > init_log_time) return ceres::SOLVER_CONTINUE;
    last_log_time = summary.cumulative_time_in_seconds;
    last_log_iter = summary.iteration;
    /* clang-format off */
    log->trace("LBFGS: iter {:>5} f {:>8.5f} |Δf| {:>3.2e} |∇f|∞ {:>3.2e} "
               "|ΔΨ| {:3.2e} |Ψ|-1 {:3.2e} ls {:3.2e} evals {:>4}/{:<4} "
               "t_step {:<} t_iter {:<} t_tot {:<} GOp/s {:<4.2f} | energy {:<18.15f} log₁₀var {:<6.6f}",
               summary.iteration,
               summary.cost,
               summary.cost_change,
               summary.gradient_max_norm,
               summary.step_norm, // By lbfgs
               functor.get_norm_offset(),
               std::abs(summary.step_size), // By line search
               functor.get_count() - last_count,
               functor.get_count(),
               fmt::format("{:>6.0f} ms",summary.step_solver_time_in_seconds * 1000),
               fmt::format("{:>6.0f} ms",summary.iteration_time_in_seconds * 1000),
               fmt::format("{:>5.3f} s",summary.cumulative_time_in_seconds),
               static_cast<double>(functor.get_ops()) / functor.t_vH2->get_last_interval()/1e9,
               functor.get_energy_per_site(),
               std::log10(functor.get_variance())
    );
    last_count = functor.get_count();
    /* clang-format on */
    return ceres::SOLVER_CONTINUE;
}

template class tools::finite::opt::internal::CustomLogCallback<tools::finite::opt::internal::ceres_subspace_functor<double>>;
template class tools::finite::opt::internal::CustomLogCallback<tools::finite::opt::internal::ceres_direct_functor<double>>;
template class tools::finite::opt::internal::CustomLogCallback<tools::finite::opt::internal::ceres_subspace_functor<std::complex<double>>>;
template class tools::finite::opt::internal::CustomLogCallback<tools::finite::opt::internal::ceres_direct_functor<std::complex<double>>>;<|endoftext|>


<commit_before>#include "collision.h"

Collision::Collision() { }

bool Collision::Check(std::vector<std::shared_ptr<Entity> > shapes, std::string move)
{
    auto current = shapes.front();

    if (move == "down" && (
            current->get<CPosition>()->getY() + current->get<CVelocity>()->getVelY() > 448 || (
            current->get<CPosition>()->getX() == shapes[1]->get<CPosition>()->getX() &&
            current->get<CPosition>()->getY() + current->get<CSprite>()->getSize() == shapes[1]->get<CPosition>()->getY())))
    {
        return 0;
    }

    if (move == "left" && (
            current->get<CPosition>()->getX() - current->get<CVelocity>()->getVelX() < 0 || (
            current->get<CPosition>()->getX() == shapes[1]->get<CPosition>()->getX() + shapes[1]->get<CSprite>()->getSize() &&
            current->get<CPosition>()->getY() == shapes[1]->get<CPosition>()->getY())))
    {
        return 0;
    }

    if (move == "right" && (
            current->get<CPosition>()->getX() + current->get<CVelocity>()->getVelX() > 448 || (
            current->get<CPosition>()->getX() + current->get<CSprite>()->getSize() == shapes[1]->get<CPosition>()->getX() &&
            current->get<CPosition>()->getY() == shapes[1]->get<CPosition>()->getY())))
    {
        return 0;
    }

    if (move == "up" && (
            current->get<CPosition>()->getY() - current->get<CVelocity>()->getVelY() < 0 || (
            current->get<CPosition>()->getX() == shapes[1]->get<CPosition>()->getX() &&
            current->get<CPosition>()->getY() == shapes[1]->get<CPosition>()->getY() + shapes[1]->get<CSprite>()->getSize())))
    {
        return 0;
    }

    return 1;
}
<commit_msg>collision stuff<commit_after>#include "collision.h"

Collision::Collision() { }

bool Collision::Check(std::vector<std::shared_ptr<Entity> > shapes, std::string move)
{
    auto current = shapes.front();

    for (unsigned int i {1}; i < shapes.size(); ++i)
    {
        if (move == "down" && (
                current->get<CPosition>()->getY() + current->get<CVelocity>()->getVelY() > 448 || (
                current->get<CPosition>()->getX() == shapes[i]->get<CPosition>()->getX() &&
                current->get<CPosition>()->getY() + current->get<CSprite>()->getSize() == shapes[i]->get<CPosition>()->getY())))
        {
            return 0;
        }

        if (move == "left" && (
                current->get<CPosition>()->getX() - current->get<CVelocity>()->getVelX() < 0 || (
                current->get<CPosition>()->getX() == shapes[i]->get<CPosition>()->getX() + shapes[i]->get<CSprite>()->getSize() &&
                current->get<CPosition>()->getY() == shapes[i]->get<CPosition>()->getY())))
        {
            return 0;
        }

        if (move == "right" && (
                current->get<CPosition>()->getX() + current->get<CVelocity>()->getVelX() > 448 || (
                current->get<CPosition>()->getX() + current->get<CSprite>()->getSize() == shapes[i]->get<CPosition>()->getX() &&
                current->get<CPosition>()->getY() == shapes[i]->get<CPosition>()->getY())))
        {
            return 0;
        }

        if (move == "up" && (
                current->get<CPosition>()->getY() - current->get<CVelocity>()->getVelY() < 0 || (
                current->get<CPosition>()->getX() == shapes[i]->get<CPosition>()->getX() &&
                current->get<CPosition>()->getY() == shapes[i]->get<CPosition>()->getY() + shapes[i]->get<CSprite>()->getSize())))
        {
            return 0;
        }
    }

    return 1;
}
<|endoftext|>


<commit_before>/* bzflag
 * Copyright (c) 1993 - 2004 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#include "common.h"

#ifdef HAVE_SDL
#include <stdio.h>
#include "SDLDisplay.h"

static int mx = 0;
static int my = 0;

SDLDisplay::SDLDisplay() : fullScreen(false), base_width(640),
			   base_height(480)
{
  if (SDL_InitSubSystem(SDL_INIT_VIDEO) == -1) {
    printf("Could not initialize SDL Video subsystem: %s.\n", SDL_GetError());
    exit (-1);
  };
  SDL_Rect **modeList
    = SDL_ListModes(NULL, SDL_HWSURFACE | SDL_OPENGL | SDL_FULLSCREEN
		    | SDL_HWPALETTE);
  if (!modeList)
    printf("Could not Get available video modes: %s.\n", SDL_GetError());

  int defaultResolutionIndex = 0;
  ResInfo** resolutions;
  int numResolutions = 1;
  // No modes available or All resolutions available
  if ((modeList != (SDL_Rect **) 0) && (modeList != (SDL_Rect **) -1)) {
      for (int i = 1; modeList[i]; i++)
	if ((modeList[i - 1]->w != modeList[i]->w)
	    || (modeList[i - 1]->h != modeList[i]->h))
	  numResolutions++;
  };
  resolutions = new ResInfo*[numResolutions];

  if ((modeList != (SDL_Rect **) 0) && (modeList != (SDL_Rect **) -1)) {
    char name[80];
    int  h;
    int  w;
    int  j = 0;

    for (int i = 0; modeList[i]; i++) {
      h = modeList[i]->h;
      w = modeList[i]->w;
      if (i != 0)
	if ((modeList[i - 1]->w == w) && (modeList[i - 1]->h == h))
	  continue;
      sprintf(name, "%dx%d    ", w, h);
      resolutions[j] = new ResInfo(name, w, h, 0);
      if (w == 640 && h == 480)
	defaultResolutionIndex = j;
      j++;
    }
  } else {
    // if no modes then make default
    resolutions[0] = new ResInfo ("default", 640, 480, 0);
  }

  // register modes
  initResolutions(resolutions, numResolutions, defaultResolutionIndex);

  SDL_EnableUNICODE(1);

  if (SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,
			  SDL_DEFAULT_REPEAT_INTERVAL) == -1) {
    printf("Could not EnableKeyRepeat: %s.\n", SDL_GetError());
    exit (-1);
  };
}

SDLDisplay::~SDLDisplay()
{
  SDL_QuitSubSystem(SDL_INIT_VIDEO);
};

bool SDLDisplay::isEventPending() const
{
  return SDL_PollEvent(NULL) == 1;
};

bool SDLDisplay::getEvent(BzfEvent& _event) const
{
  SDL_Event event;
  if (SDL_PollEvent(&event) == 0)
    return false;

  switch (event.type) {

  case SDL_MOUSEMOTION:
    _event.type        = BzfEvent::MouseMove;
    mx                 = event.motion.x;
    my                 = event.motion.y;
    _event.mouseMove.x = event.motion.x;
    _event.mouseMove.y = event.motion.y;
    break;

  case SDL_MOUSEBUTTONDOWN:
    _event.type          = BzfEvent::KeyDown;
    _event.keyDown.ascii = 0;
    _event.keyDown.shift = 0;
    switch (event.button.button) {
    case SDL_BUTTON_LEFT:
      _event.keyDown.button = BzfKeyEvent::LeftMouse;
      break;
    case SDL_BUTTON_MIDDLE:
      _event.keyDown.button = BzfKeyEvent::MiddleMouse;
      break;
    case SDL_BUTTON_RIGHT:
      _event.keyDown.button = BzfKeyEvent::RightMouse;
      break;
    default:
      return false;
    }
    break;

  case SDL_MOUSEBUTTONUP:
    _event.type = BzfEvent::KeyUp;
    _event.keyUp.ascii = 0;
    _event.keyUp.shift = 0;
    switch (event.button.button) {
    case SDL_BUTTON_LEFT:
      _event.keyDown.button = BzfKeyEvent::LeftMouse;
      break;
    case SDL_BUTTON_MIDDLE:
      _event.keyDown.button = BzfKeyEvent::MiddleMouse;
      break;
    case SDL_BUTTON_RIGHT:
      _event.keyDown.button = BzfKeyEvent::RightMouse;
      break;
    default:
      return false;
    }
    break;

  case SDL_KEYDOWN:
    _event.type = BzfEvent::KeyDown;
    if (!getKey(event, _event.keyDown))
      return false;
    break;

  case SDL_KEYUP:
    _event.type = BzfEvent::KeyUp;
    if (!getKey(event, _event.keyUp))
      return false;
    break;

  case SDL_QUIT:
    _event.type = BzfEvent::Quit;
    break;

  default:
    return false;
  }
  return true;
};

bool SDLDisplay::getKey(const SDL_Event& sdlEvent, BzfKeyEvent& key) const
{
  Uint16 unicode = sdlEvent.key.keysym.unicode;
  SDLKey sym     = sdlEvent.key.keysym.sym;
  SDLMod mod     = sdlEvent.key.keysym.mod;

  if (unicode != 0) {
    char ch;
    if ((unicode & 0xFF80) == 0) {
      ch = unicode & 0x7F;
    }
    else {
      return false;
    }
    key.ascii = ch;
    key.button = BzfKeyEvent::NoButton;
    if (sym == SDLK_DELETE) {
      key.ascii  = 0;
      key.button = BzfKeyEvent::Delete;
    }
  } else {
    key.ascii = 0;
    switch (sym) {
    case SDLK_PAUSE:
      key.button = BzfKeyEvent::Pause;
      break;
    case SDLK_HOME:
      key.button = BzfKeyEvent::Home;
      break;
    case SDLK_END:
      key.button = BzfKeyEvent::End;
      break;
    case SDLK_LEFT:
      key.button = BzfKeyEvent::Left;
      break;
    case SDLK_RIGHT:
      key.button = BzfKeyEvent::Right;
      break;
    case SDLK_UP:
      key.button = BzfKeyEvent::Up;
      break;
    case SDLK_DOWN:
      key.button = BzfKeyEvent::Down;
      break;
    case SDLK_PAGEUP:
      key.button = BzfKeyEvent::PageUp;
      break;
    case SDLK_PAGEDOWN:
      key.button = BzfKeyEvent::PageDown;
      break;
    case SDLK_INSERT:
      key.button = BzfKeyEvent::Insert;
      break;
    case SDLK_DELETE:
      key.button = BzfKeyEvent::Delete;
      break;
    case SDLK_F1:
      key.button = BzfKeyEvent::F1;
      break;
    case SDLK_F2:
      key.button = BzfKeyEvent::F2;
      break;
    case SDLK_F3:
      key.button = BzfKeyEvent::F3;
      break;
    case SDLK_F4:
      key.button = BzfKeyEvent::F4;
      break;
    case SDLK_F5:
      key.button = BzfKeyEvent::F5;
      break;
    case SDLK_F6:
      key.button = BzfKeyEvent::F6;
      break;
    case SDLK_F7:
      key.button = BzfKeyEvent::F7;
      break;
    case SDLK_F8:
      key.button = BzfKeyEvent::F8;
      break;
    case SDLK_F9:
      key.button = BzfKeyEvent::F9;
      break;
    case SDLK_F10:
      key.button = BzfKeyEvent::F10;
      break;
    case SDLK_F11:
      key.button = BzfKeyEvent::F11;
      break;
    case SDLK_F12:
      key.button = BzfKeyEvent::F12;
      break;
    default:
      return false;
    }
  }

  key.shift = 0;
  if (mod & KMOD_SHIFT)
    key.shift |= BzfKeyEvent::ShiftKey;
  if (mod & KMOD_CTRL)
    key.shift |= BzfKeyEvent::ControlKey;
  if (mod & KMOD_ALT)
    key.shift |= BzfKeyEvent::AltKey;
  return true;
}

void SDLDisplay::createWindow() {
  SDL_Surface *surface;
  if (fullScreen)
    surface = SDL_SetVideoMode(getWidth(), getHeight(), 0,
			       SDL_OPENGL  | SDL_FULLSCREEN);
  else
    surface = SDL_SetVideoMode(base_width, base_height, 0, SDL_OPENGL);
  if (!surface)
    printf("Could not set Video Mode: %s.\n", SDL_GetError());
};

void SDLDisplay::setFullscreen() {
  fullScreen = true;
}

void SDLDisplay::setWindowSize(int _width, int _height) {
  base_width  = _width;
  base_height = _height;
}

void SDLDisplay::getWindowSize(int& width, int& height) const {
  if (fullScreen) {
    width  = getWidth();
    height = getHeight();
  } else {
    width  = base_width;
    height = base_height;
  }
};

void SDLVisual::setDoubleBuffer(bool on) {
  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, on ? 1 : 0);
};

void SDLVisual::setRGBA(int minRed, int minGreen,
			int minBlue, int minAlpha) {
  SDL_GL_SetAttribute(SDL_GL_RED_SIZE,   minRed);
  SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, minGreen);
  SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,  minBlue);
  SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, minAlpha);
};

void SDLVisual::setDepth(int minDepth) {
  SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, minDepth);
};

void SDLVisual::setStencil(int minDepth) {
  SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, minDepth);
};

void SDLVisual::setStereo(bool on) {
  SDL_GL_SetAttribute(SDL_GL_STEREO, on ? 1 : 0);
};

SDLWindow::SDLWindow(const SDLDisplay* _display, SDLVisual*)
  : BzfWindow(_display), x(-1), y(-1), hasGamma(true)
{
};

void SDLWindow::setTitle(const char * title) {
  SDL_WM_SetCaption(title, title);
};

void SDLWindow::setFullscreen() {
  ((SDLDisplay *)getDisplay())->setFullscreen();
};

void SDLWindow::warpMouse(int x, int y) {
  SDL_WarpMouse(x, y);
};

void SDLWindow::getMouse(int& x, int& y) const {
  x = mx;
  y = my;
};

void SDLWindow::grabMouse() {
  SDL_WM_GrabInput(SDL_GRAB_ON);
};

void SDLWindow::ungrabMouse() {
  SDL_WM_GrabInput(SDL_GRAB_OFF);
};

void SDLWindow::setSize(int width, int height) {
  ((SDLDisplay *)getDisplay())->setWindowSize(width, height);
};

void SDLWindow::getSize(int& width, int& height) const {
  ((SDLDisplay *)getDisplay())->getWindowSize(width, height);
};

void SDLWindow::setGamma(float newGamma) {
  gamma = newGamma;
  int result = SDL_SetGamma(gamma, gamma, gamma);
  if (result == -1) {
    printf("Could not set Gamma: %s.\n", SDL_GetError());
    hasGamma = false;
  }
};

float SDLWindow::getGamma() const {
  return gamma;
};

bool SDLWindow::hasGammaControl() const {
  return hasGamma;
};

void SDLWindow::swapBuffers() {
  SDL_GL_SwapBuffers();
};

void SDLWindow::create(void) {
  ((SDLDisplay *)getDisplay())->createWindow();
};

#endif

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>GLstate was borked after creating a new window. do an InitContext then<commit_after>/* bzflag
 * Copyright (c) 1993 - 2004 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#include "common.h"

#ifdef HAVE_SDL
#include <stdio.h>
#include "SDLDisplay.h"
#include "OpenGLGState.h"

static int mx = 0;
static int my = 0;

SDLDisplay::SDLDisplay() : fullScreen(false), base_width(640),
			   base_height(480)
{
  if (SDL_InitSubSystem(SDL_INIT_VIDEO) == -1) {
    printf("Could not initialize SDL Video subsystem: %s.\n", SDL_GetError());
    exit (-1);
  };
  SDL_Rect **modeList
    = SDL_ListModes(NULL, SDL_HWSURFACE | SDL_OPENGL | SDL_FULLSCREEN
		    | SDL_HWPALETTE);
  if (!modeList)
    printf("Could not Get available video modes: %s.\n", SDL_GetError());

  int defaultResolutionIndex = 0;
  ResInfo** resolutions;
  int numResolutions = 1;
  // No modes available or All resolutions available
  if ((modeList != (SDL_Rect **) 0) && (modeList != (SDL_Rect **) -1)) {
      for (int i = 1; modeList[i]; i++)
	if ((modeList[i - 1]->w != modeList[i]->w)
	    || (modeList[i - 1]->h != modeList[i]->h))
	  numResolutions++;
  };
  resolutions = new ResInfo*[numResolutions];

  if ((modeList != (SDL_Rect **) 0) && (modeList != (SDL_Rect **) -1)) {
    char name[80];
    int  h;
    int  w;
    int  j = 0;

    for (int i = 0; modeList[i]; i++) {
      h = modeList[i]->h;
      w = modeList[i]->w;
      if (i != 0)
	if ((modeList[i - 1]->w == w) && (modeList[i - 1]->h == h))
	  continue;
      sprintf(name, "%dx%d    ", w, h);
      resolutions[j] = new ResInfo(name, w, h, 0);
      if (w == 640 && h == 480)
	defaultResolutionIndex = j;
      j++;
    }
  } else {
    // if no modes then make default
    resolutions[0] = new ResInfo ("default", 640, 480, 0);
  }

  // register modes
  initResolutions(resolutions, numResolutions, defaultResolutionIndex);

  SDL_EnableUNICODE(1);

  if (SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,
			  SDL_DEFAULT_REPEAT_INTERVAL) == -1) {
    printf("Could not EnableKeyRepeat: %s.\n", SDL_GetError());
    exit (-1);
  };
}

SDLDisplay::~SDLDisplay()
{
  SDL_QuitSubSystem(SDL_INIT_VIDEO);
};

bool SDLDisplay::isEventPending() const
{
  return SDL_PollEvent(NULL) == 1;
};

bool SDLDisplay::getEvent(BzfEvent& _event) const
{
  SDL_Event event;
  if (SDL_PollEvent(&event) == 0)
    return false;

  switch (event.type) {

  case SDL_MOUSEMOTION:
    _event.type        = BzfEvent::MouseMove;
    mx                 = event.motion.x;
    my                 = event.motion.y;
    _event.mouseMove.x = event.motion.x;
    _event.mouseMove.y = event.motion.y;
    break;

  case SDL_MOUSEBUTTONDOWN:
    _event.type          = BzfEvent::KeyDown;
    _event.keyDown.ascii = 0;
    _event.keyDown.shift = 0;
    switch (event.button.button) {
    case SDL_BUTTON_LEFT:
      _event.keyDown.button = BzfKeyEvent::LeftMouse;
      break;
    case SDL_BUTTON_MIDDLE:
      _event.keyDown.button = BzfKeyEvent::MiddleMouse;
      break;
    case SDL_BUTTON_RIGHT:
      _event.keyDown.button = BzfKeyEvent::RightMouse;
      break;
    default:
      return false;
    }
    break;

  case SDL_MOUSEBUTTONUP:
    _event.type = BzfEvent::KeyUp;
    _event.keyUp.ascii = 0;
    _event.keyUp.shift = 0;
    switch (event.button.button) {
    case SDL_BUTTON_LEFT:
      _event.keyDown.button = BzfKeyEvent::LeftMouse;
      break;
    case SDL_BUTTON_MIDDLE:
      _event.keyDown.button = BzfKeyEvent::MiddleMouse;
      break;
    case SDL_BUTTON_RIGHT:
      _event.keyDown.button = BzfKeyEvent::RightMouse;
      break;
    default:
      return false;
    }
    break;

  case SDL_KEYDOWN:
    _event.type = BzfEvent::KeyDown;
    if (!getKey(event, _event.keyDown))
      return false;
    break;

  case SDL_KEYUP:
    _event.type = BzfEvent::KeyUp;
    if (!getKey(event, _event.keyUp))
      return false;
    break;

  case SDL_QUIT:
    _event.type = BzfEvent::Quit;
    break;

  default:
    return false;
  }
  return true;
};

bool SDLDisplay::getKey(const SDL_Event& sdlEvent, BzfKeyEvent& key) const
{
  Uint16 unicode = sdlEvent.key.keysym.unicode;
  SDLKey sym     = sdlEvent.key.keysym.sym;
  SDLMod mod     = sdlEvent.key.keysym.mod;

  if (unicode != 0) {
    char ch;
    if ((unicode & 0xFF80) == 0) {
      ch = unicode & 0x7F;
    }
    else {
      return false;
    }
    key.ascii = ch;
    key.button = BzfKeyEvent::NoButton;
    if (sym == SDLK_DELETE) {
      key.ascii  = 0;
      key.button = BzfKeyEvent::Delete;
    }
  } else {
    key.ascii = 0;
    switch (sym) {
    case SDLK_PAUSE:
      key.button = BzfKeyEvent::Pause;
      break;
    case SDLK_HOME:
      key.button = BzfKeyEvent::Home;
      break;
    case SDLK_END:
      key.button = BzfKeyEvent::End;
      break;
    case SDLK_LEFT:
      key.button = BzfKeyEvent::Left;
      break;
    case SDLK_RIGHT:
      key.button = BzfKeyEvent::Right;
      break;
    case SDLK_UP:
      key.button = BzfKeyEvent::Up;
      break;
    case SDLK_DOWN:
      key.button = BzfKeyEvent::Down;
      break;
    case SDLK_PAGEUP:
      key.button = BzfKeyEvent::PageUp;
      break;
    case SDLK_PAGEDOWN:
      key.button = BzfKeyEvent::PageDown;
      break;
    case SDLK_INSERT:
      key.button = BzfKeyEvent::Insert;
      break;
    case SDLK_DELETE:
      key.button = BzfKeyEvent::Delete;
      break;
    case SDLK_F1:
      key.button = BzfKeyEvent::F1;
      break;
    case SDLK_F2:
      key.button = BzfKeyEvent::F2;
      break;
    case SDLK_F3:
      key.button = BzfKeyEvent::F3;
      break;
    case SDLK_F4:
      key.button = BzfKeyEvent::F4;
      break;
    case SDLK_F5:
      key.button = BzfKeyEvent::F5;
      break;
    case SDLK_F6:
      key.button = BzfKeyEvent::F6;
      break;
    case SDLK_F7:
      key.button = BzfKeyEvent::F7;
      break;
    case SDLK_F8:
      key.button = BzfKeyEvent::F8;
      break;
    case SDLK_F9:
      key.button = BzfKeyEvent::F9;
      break;
    case SDLK_F10:
      key.button = BzfKeyEvent::F10;
      break;
    case SDLK_F11:
      key.button = BzfKeyEvent::F11;
      break;
    case SDLK_F12:
      key.button = BzfKeyEvent::F12;
      break;
    default:
      return false;
    }
  }

  key.shift = 0;
  if (mod & KMOD_SHIFT)
    key.shift |= BzfKeyEvent::ShiftKey;
  if (mod & KMOD_CTRL)
    key.shift |= BzfKeyEvent::ControlKey;
  if (mod & KMOD_ALT)
    key.shift |= BzfKeyEvent::AltKey;
  return true;
}

void SDLDisplay::createWindow() {
  SDL_Surface *surface;
  if (fullScreen)
    surface = SDL_SetVideoMode(getWidth(), getHeight(), 0,
			       SDL_OPENGL  | SDL_FULLSCREEN);
  else
    surface = SDL_SetVideoMode(base_width, base_height, 0, SDL_OPENGL);
  if (!surface)
    printf("Could not set Video Mode: %s.\n", SDL_GetError());
};

void SDLDisplay::setFullscreen() {
  fullScreen = true;
}

void SDLDisplay::setWindowSize(int _width, int _height) {
  base_width  = _width;
  base_height = _height;
}

void SDLDisplay::getWindowSize(int& width, int& height) const {
  if (fullScreen) {
    width  = getWidth();
    height = getHeight();
  } else {
    width  = base_width;
    height = base_height;
  }
};

void SDLVisual::setDoubleBuffer(bool on) {
  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, on ? 1 : 0);
};

void SDLVisual::setRGBA(int minRed, int minGreen,
			int minBlue, int minAlpha) {
  SDL_GL_SetAttribute(SDL_GL_RED_SIZE,   minRed);
  SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, minGreen);
  SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE,  minBlue);
  SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, minAlpha);
};

void SDLVisual::setDepth(int minDepth) {
  SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, minDepth);
};

void SDLVisual::setStencil(int minDepth) {
  SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, minDepth);
};

void SDLVisual::setStereo(bool on) {
  SDL_GL_SetAttribute(SDL_GL_STEREO, on ? 1 : 0);
};

SDLWindow::SDLWindow(const SDLDisplay* _display, SDLVisual*)
  : BzfWindow(_display), x(-1), y(-1), hasGamma(true)
{
};

void SDLWindow::setTitle(const char * title) {
  SDL_WM_SetCaption(title, title);
};

void SDLWindow::setFullscreen() {
  ((SDLDisplay *)getDisplay())->setFullscreen();
};

void SDLWindow::warpMouse(int x, int y) {
  SDL_WarpMouse(x, y);
};

void SDLWindow::getMouse(int& x, int& y) const {
  x = mx;
  y = my;
};

void SDLWindow::grabMouse() {
  SDL_WM_GrabInput(SDL_GRAB_ON);
};

void SDLWindow::ungrabMouse() {
  SDL_WM_GrabInput(SDL_GRAB_OFF);
};

void SDLWindow::setSize(int width, int height) {
  ((SDLDisplay *)getDisplay())->setWindowSize(width, height);
};

void SDLWindow::getSize(int& width, int& height) const {
  ((SDLDisplay *)getDisplay())->getWindowSize(width, height);
};

void SDLWindow::setGamma(float newGamma) {
  gamma = newGamma;
  int result = SDL_SetGamma(gamma, gamma, gamma);
  if (result == -1) {
    printf("Could not set Gamma: %s.\n", SDL_GetError());
    hasGamma = false;
  }
};

float SDLWindow::getGamma() const {
  return gamma;
};

bool SDLWindow::hasGammaControl() const {
  return hasGamma;
};

void SDLWindow::swapBuffers() {
  SDL_GL_SwapBuffers();
};

void SDLWindow::create(void) {
  ((SDLDisplay *)getDisplay())->createWindow();
  // reload context data
  OpenGLGState::initContext();
};

#endif

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>/***********************************************************************
    filename:   StateChangeWrapper.cpp
    created:    Wed, 8th Feb 2012
    author:     Lukas E Meindl
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2012 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/

#include <GL/glew.h>

#include "CEGUI/RendererModules/OpenGL/StateChangeWrapper.h"

namespace CEGUI
{

OpenGL3StateChangeWrapper::BlendFuncParams::BlendFuncParams()
{
    reset();
}

void OpenGL3StateChangeWrapper::BlendFuncParams::reset()
{
    d_dFactor = -1;
    d_sFactor = -1;
}

bool OpenGL3StateChangeWrapper::BlendFuncParams::equal(GLenum sFactor, GLenum dFactor)
{
    bool equal = (d_sFactor == sFactor) && (d_dFactor == dFactor);
    if(!equal)
    {
        d_sFactor = sFactor;
        d_dFactor = dFactor;
    }
    return equal;
}

OpenGL3StateChangeWrapper::BlendFuncSeperateParams::BlendFuncSeperateParams()
{
    reset();
}

void OpenGL3StateChangeWrapper::BlendFuncSeperateParams::reset()
{
    d_sfactorRGB = -1;
    d_dfactorRGB = -1;
    d_sfactorAlpha = -1;
    d_dfactorAlpha = -1;
}

bool OpenGL3StateChangeWrapper::BlendFuncSeperateParams::equal(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha)
{
    bool equal = (d_sfactorRGB == sfactorRGB) && (d_dfactorRGB == dfactorRGB) && (d_sfactorAlpha == sfactorAlpha) && (d_dfactorAlpha == dfactorAlpha);
    if(!equal)
    {
        d_sfactorRGB = sfactorRGB;
        d_dfactorRGB = dfactorRGB;
        d_sfactorAlpha = sfactorAlpha;
        d_dfactorAlpha = dfactorAlpha;
    }
    return equal;
}

OpenGL3StateChangeWrapper::PortParams::PortParams()
{
    reset();
}
void OpenGL3StateChangeWrapper::PortParams::reset()
{
    d_x = -1;
    d_y = -1;
    d_width = -1;
    d_height = -1;
}

bool OpenGL3StateChangeWrapper::PortParams::equal(GLint x, GLint y, GLsizei width, GLsizei height)
{
    bool equal = (d_x == x) && (d_y == y) && (d_width == width) && (d_height == height);
    if(!equal)
    {
        d_x = x;
        d_y = y;
        d_width = width;
        d_height = height;
    }
    return equal;
}

OpenGL3StateChangeWrapper::BindBufferParams::BindBufferParams()
{
    reset();
}
void OpenGL3StateChangeWrapper::BindBufferParams::reset()
{
    d_target = -1;
    d_buffer = -1;
}

bool OpenGL3StateChangeWrapper::BindBufferParams::equal(GLenum target, GLuint buffer)
{
    bool equal = (d_target == target) && (d_buffer == buffer);
    if(!equal)
    {
        d_target = target;
        d_buffer = buffer;
    }
    return equal;
}

//! constructor.
OpenGL3StateChangeWrapper::OpenGL3StateChangeWrapper(OpenGL3Renderer& owner)
{
    reset();
}
//! destructor
OpenGL3StateChangeWrapper::~OpenGL3StateChangeWrapper()
{
}

void OpenGL3StateChangeWrapper::reset()
{
    d_vertexArrayObject = -1;
    d_shaderProgram = -1;
    d_blendFuncParams.reset();
    d_blendFuncSeperateParams.reset();
    d_viewPortParams.reset();
    d_scissorParams.reset();
    d_bindBufferParams.reset();
}

void OpenGL3StateChangeWrapper::bindVertexArray(GLuint vertexArray)
{
    if(vertexArray != d_vertexArrayObject)
    {
        glBindVertexArray(vertexArray);
        d_vertexArrayObject = vertexArray;
    }

}

void OpenGL3StateChangeWrapper::useProgram(GLuint program)
{
    if(program != d_shaderProgram)
    {
        glUseProgram(program);
        d_shaderProgram = program;
    }
}

void OpenGL3StateChangeWrapper::blendFunc(GLenum sfactor, GLenum dfactor)
{
    bool callIsRedundant = d_blendFuncParams.equal(sfactor, dfactor);
    if(!callIsRedundant)
    {
        glBlendFunc(sfactor, dfactor);
    }
}

void OpenGL3StateChangeWrapper::blendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha)
{
    bool callIsRedundant = d_blendFuncSeperateParams.equal(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
    if(!callIsRedundant)
        glBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
}

void OpenGL3StateChangeWrapper::viewport(GLint x, GLint y, GLsizei width, GLsizei height)
{
    bool callIsRedundant = d_viewPortParams.equal(x, y, width, height);
    if(!callIsRedundant)
        glViewport(x, y, width, height);
}

void OpenGL3StateChangeWrapper::scissor(GLint x, GLint y, GLsizei width, GLsizei height)
{
    bool callIsRedundant = d_scissorParams.equal(x, y, width, height);
    if(!callIsRedundant)
        glScissor(x, y, width, height);
}
void OpenGL3StateChangeWrapper::bindBuffer(GLenum target, GLuint buffer)
{
    bool callIsRedundant = d_bindBufferParams.equal(target, buffer);
    if(!callIsRedundant)
        glBindBuffer(target, buffer);
}

}
<commit_msg>MOD: StateChangeWrapper implementation for texture calls<commit_after>/***********************************************************************
    filename:   StateChangeWrapper.cpp
    created:    Wed, 8th Feb 2012
    author:     Lukas E Meindl
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2012 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/

#include <GL/glew.h>

#include "CEGUI/RendererModules/OpenGL/StateChangeWrapper.h"

namespace CEGUI
{

OpenGL3StateChangeWrapper::BlendFuncParams::BlendFuncParams()
{
    reset();
}

void OpenGL3StateChangeWrapper::BlendFuncParams::reset()
{
    d_dFactor = -1;
    d_sFactor = -1;
}

bool OpenGL3StateChangeWrapper::BlendFuncParams::equal(GLenum sFactor, GLenum dFactor)
{
    bool equal = (d_sFactor == sFactor) && (d_dFactor == dFactor);
    if (!equal)
    {
        d_sFactor = sFactor;
        d_dFactor = dFactor;
    }
    return equal;
}

OpenGL3StateChangeWrapper::BlendFuncSeperateParams::BlendFuncSeperateParams()
{
    reset();
}

void OpenGL3StateChangeWrapper::BlendFuncSeperateParams::reset()
{
    d_sfactorRGB = -1;
    d_dfactorRGB = -1;
    d_sfactorAlpha = -1;
    d_dfactorAlpha = -1;
}

bool OpenGL3StateChangeWrapper::BlendFuncSeperateParams::equal(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha)
{
    bool equal = (d_sfactorRGB == sfactorRGB) && (d_dfactorRGB == dfactorRGB) && (d_sfactorAlpha == sfactorAlpha) && (d_dfactorAlpha == dfactorAlpha);
    if (!equal)
    {
        d_sfactorRGB = sfactorRGB;
        d_dfactorRGB = dfactorRGB;
        d_sfactorAlpha = sfactorAlpha;
        d_dfactorAlpha = dfactorAlpha;
    }
    return equal;
}

OpenGL3StateChangeWrapper::PortParams::PortParams()
{
    reset();
}
void OpenGL3StateChangeWrapper::PortParams::reset()
{
    d_x = -1;
    d_y = -1;
    d_width = -1;
    d_height = -1;
}

bool OpenGL3StateChangeWrapper::PortParams::equal(GLint x, GLint y, GLsizei width, GLsizei height)
{
    bool equal = (d_x == x) && (d_y == y) && (d_width == width) && (d_height == height);
    if (!equal)
    {
        d_x = x;
        d_y = y;
        d_width = width;
        d_height = height;
    }
    return equal;
}

OpenGL3StateChangeWrapper::BindBufferParams::BindBufferParams()
{
    reset();
}
void OpenGL3StateChangeWrapper::BindBufferParams::reset()
{
    d_target = -1;
    d_buffer = -1;
}

bool OpenGL3StateChangeWrapper::BindBufferParams::equal(GLenum target, GLuint buffer)
{
    bool equal = (d_target == target) && (d_buffer == buffer);
    if (!equal)
    {
        d_target = target;
        d_buffer = buffer;
    }
    return equal;
}

OpenGL3StateChangeWrapper::BoundTexture::BoundTexture()
{
    d_target = -1;
    d_texture = -1;
}

void OpenGL3StateChangeWrapper::BoundTexture::bindTexture(GLenum target, GLuint texture)
{
    d_target = target;
    d_texture = texture;
}

//! constructor.
OpenGL3StateChangeWrapper::OpenGL3StateChangeWrapper(OpenGL3Renderer& owner)
{
    reset();
}
//! destructor
OpenGL3StateChangeWrapper::~OpenGL3StateChangeWrapper()
{
}

void OpenGL3StateChangeWrapper::reset()
{
    d_vertexArrayObject = -1;
    d_shaderProgram = -1;
    d_blendFuncParams.reset();
    d_blendFuncSeperateParams.reset();
    d_viewPortParams.reset();
    d_scissorParams.reset();
    d_bindBufferParams.reset();
    d_activeTexturePosition = -1;
    d_boundTextures.clear();
}

void OpenGL3StateChangeWrapper::bindVertexArray(GLuint vertexArray)
{
    if (vertexArray != d_vertexArrayObject)
    {
        glBindVertexArray(vertexArray);
        d_vertexArrayObject = vertexArray;
    }

}

void OpenGL3StateChangeWrapper::useProgram(GLuint program)
{
    if (program != d_shaderProgram)
    {
        glUseProgram(program);
        d_shaderProgram = program;
    }
}

void OpenGL3StateChangeWrapper::blendFunc(GLenum sfactor, GLenum dfactor)
{
    bool callIsRedundant = d_blendFuncParams.equal(sfactor, dfactor);
    if (!callIsRedundant)
    {
        glBlendFunc(sfactor, dfactor);
    }
}

void OpenGL3StateChangeWrapper::blendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha)
{
    bool callIsRedundant = d_blendFuncSeperateParams.equal(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
    if (!callIsRedundant)
        glBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
}

void OpenGL3StateChangeWrapper::viewport(GLint x, GLint y, GLsizei width, GLsizei height)
{
    bool callIsRedundant = d_viewPortParams.equal(x, y, width, height);
    if(!callIsRedundant)
        glViewport(x, y, width, height);
}

void OpenGL3StateChangeWrapper::scissor(GLint x, GLint y, GLsizei width, GLsizei height)
{
    bool callIsRedundant = d_scissorParams.equal(x, y, width, height);
    if (!callIsRedundant)
        glScissor(x, y, width, height);
}

void OpenGL3StateChangeWrapper::bindBuffer(GLenum target, GLuint buffer)
{
    bool callIsRedundant = d_bindBufferParams.equal(target, buffer);
    if (!callIsRedundant)
        glBindBuffer(target, buffer);
}

void OpenGL3StateChangeWrapper::activeTexture(unsigned int texture_position)
{
    if (d_activeTexturePosition != texture_position)
    {
        glActiveTexture(GL_TEXTURE0 + texture_position);
        d_activeTexturePosition = texture_position;
    }
}

void OpenGL3StateChangeWrapper::bindTexture(GLenum target, GLuint texture)
{
    if (d_activeTexturePosition == -1)
        return;

    while (d_activeTexturePosition >= d_boundTextures.size())
        d_boundTextures.push_back(BoundTexture());

    BoundTexture& boundTexture = d_boundTextures[d_activeTexturePosition];
    if (boundTexture.d_target != target || boundTexture.d_texture != texture)
    {
        glBindTexture(target, texture);
        boundTexture.bindTexture(target, texture);
    }
}
}
<|endoftext|>