<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#ifndef INCLUDED_SC_SOURCE_CORE_INC_BCASLOT_HXX
#define INCLUDED_SC_SOURCE_CORE_INC_BCASLOT_HXX

#include <set>
#include <boost/unordered_set.hpp>
#include <functional>
#include <svl/broadcast.hxx>

#include "global.hxx"
#include "brdcst.hxx"

namespace sc {

struct AreaListener
{
    ScRange maArea;
    SvtListener* mpListener;
};

}

/**
    Used in a Unique Associative Container.
 */

class ScBroadcastArea
{
private:
    ScBroadcastArea*    pUpdateChainNext;
    SvtBroadcaster      aBroadcaster;
    ScRange             aRange;
    sal_uLong               nRefCount;
    bool                bInUpdateChain;

public:
            ScBroadcastArea( const ScRange& rRange )
                : pUpdateChainNext( NULL ), aRange( rRange ),
                nRefCount( 0 ), bInUpdateChain( false ) {}
    inline SvtBroadcaster&       GetBroadcaster()       { return aBroadcaster; }
    inline const SvtBroadcaster& GetBroadcaster() const { return aBroadcaster; }
    inline void         UpdateRange( const ScRange& rNewRange )
                            { aRange = rNewRange; }
    inline const ScRange&   GetRange() const { return aRange; }
    inline const ScAddress& GetStart() const { return aRange.aStart; }
    inline const ScAddress& GetEnd() const { return aRange.aEnd; }
    inline void         IncRef() { ++nRefCount; }
    inline sal_uLong        DecRef() { return nRefCount ? --nRefCount : 0; }
    inline sal_uLong        GetRef() { return nRefCount; }
    inline ScBroadcastArea* GetUpdateChainNext() const { return pUpdateChainNext; }
    inline void         SetUpdateChainNext( ScBroadcastArea* p ) { pUpdateChainNext = p; }
    inline bool         IsInUpdateChain() const { return bInUpdateChain; }
    inline void         SetInUpdateChain( bool b ) { bInUpdateChain = b; }

    /** Equalness of this or range. */
    inline  bool        operator==( const ScBroadcastArea & rArea ) const;
};

inline bool ScBroadcastArea::operator==( const ScBroadcastArea & rArea ) const
{
    return aRange == rArea.aRange;
}

struct ScBroadcastAreaEntry
{
    ScBroadcastArea* mpArea;
    mutable bool     mbErasure;     ///< TRUE if marked for erasure in this set

    ScBroadcastAreaEntry( ScBroadcastArea* p ) : mpArea( p), mbErasure( false) {}
};

struct ScBroadcastAreaHash
{
    size_t operator()( const ScBroadcastAreaEntry& rEntry ) const
    {
        return rEntry.mpArea->GetRange().hashArea();
    }
};

struct ScBroadcastAreaEqual
{
    bool operator()( const ScBroadcastAreaEntry& rEntry1, const ScBroadcastAreaEntry& rEntry2) const
    {
        return *rEntry1.mpArea == *rEntry2.mpArea;
    }
};

typedef ::boost::unordered_set< ScBroadcastAreaEntry, ScBroadcastAreaHash, ScBroadcastAreaEqual > ScBroadcastAreas;

struct ScBroadcastAreaBulkHash
{
    size_t operator()( const ScBroadcastArea* p ) const
    {
        return reinterpret_cast<size_t>(p);
    }
};

struct ScBroadcastAreaBulkEqual
{
    bool operator()( const ScBroadcastArea* p1, const ScBroadcastArea* p2) const
    {
        return p1 == p2;
    }
};

typedef ::boost::unordered_set< const ScBroadcastArea*, ScBroadcastAreaBulkHash,
        ScBroadcastAreaBulkEqual > ScBroadcastAreasBulk;

class ScBroadcastAreaSlotMachine;

/// Collection of BroadcastAreas
class ScBroadcastAreaSlot
{
private:
    ScBroadcastAreas    aBroadcastAreaTbl;
    mutable ScBroadcastArea aTmpSeekBroadcastArea;      // for FindBroadcastArea()
    ScDocument*         pDoc;
    ScBroadcastAreaSlotMachine* pBASM;
    bool                mbInBroadcastIteration;

    /**
     * If true, the slot has at least one area broadcaster marked for removal.
     * This flag is used only during broadcast iteration, to speed up
     * iteration.  Using this flag is cheaper than dereferencing each iterator
     * and checking its own flag inside especially when no areas are marked
     * for removal.
     */
    bool mbHasErasedArea;

    ScBroadcastAreas::const_iterator  FindBroadcastArea( const ScRange& rRange ) const;

    /**
        More hypothetical (memory would probably be doomed anyway) check
        whether there would be an overflow when adding an area, setting the
        proper state if so.

        @return true if a HardRecalcState is effective and area is not to be
        added.
      */
    bool                CheckHardRecalcStateCondition() const;

    /** Finally erase all areas pushed as to-be-erased. */
    void                FinallyEraseAreas();

    bool                isMarkedErased( const ScBroadcastAreas::iterator& rIter )
                            {
                                return (*rIter).mbErasure;
                            }

public:
                        ScBroadcastAreaSlot( ScDocument* pDoc,
                                        ScBroadcastAreaSlotMachine* pBASM );
                        ~ScBroadcastAreaSlot();
    const ScBroadcastAreas& GetBroadcastAreas() const
                                            { return aBroadcastAreaTbl; }

    /**
        Only here new ScBroadcastArea objects are created, prevention of dupes.

        @param rpArea
            If NULL, a new ScBroadcastArea is created and assigned ton the
            reference if a matching area wasn't found. If a matching area was
            found, that is assigned. In any case, the SvtListener is added to
            the broadcaster.

            If not NULL then no listeners are startet, only the area is
            inserted and the reference count incremented. Effectively the same
            as InsertListeningArea(), so use that instead.

        @return
            true if rpArea passed was NULL and ScBroadcastArea is newly
            created.
     */
    bool                StartListeningArea( const ScRange& rRange,
                                            SvtListener* pListener,
                                            ScBroadcastArea*& rpArea );

    /**
        Insert a ScBroadcastArea obtained via StartListeningArea() to
        subsequent slots.
     */
    void                InsertListeningArea( ScBroadcastArea* pArea );

    void                EndListeningArea( const ScRange& rRange,
                                            SvtListener* pListener,
                                            ScBroadcastArea*& rpArea );
    bool                AreaBroadcast( const ScHint& rHint );
    /// @return true if at least one broadcast occurred.
    bool                AreaBroadcastInRange( const ScRange& rRange,
                                              const ScHint& rHint );
    void                DelBroadcastAreasInRange( const ScRange& rRange );
    void                UpdateRemove( UpdateRefMode eUpdateRefMode,
                                        const ScRange& rRange,
                                        SCsCOL nDx, SCsROW nDy, SCsTAB nDz );
    void                UpdateRemoveArea( ScBroadcastArea* pArea );
    void                UpdateInsert( ScBroadcastArea* pArea );

    bool                IsInBroadcastIteration() const { return mbInBroadcastIteration; }

    /** Erase an area from set and delete it if last reference, or if
        mbInBroadcastIteration is set push it to the vector of to-be-erased
        areas instead.

        Meant to be used internally and from ScBroadcastAreaSlotMachine only.
     */
    void                EraseArea( ScBroadcastAreas::iterator& rIter );

    void GetAllListeners(
        const ScRange& rRange, std::vector<sc::AreaListener>& rListeners, sc::AreaOverlapType eType );
};

/**
    BroadcastAreaSlots and their management, once per document.
 */

class  ScBroadcastAreaSlotMachine
{
private:

    /**
        Slot offset arrangement of columns and rows, once per sheet.

        +---+---+
        | 0 | 3 |
        +---+---+
        | 1 | 4 |
        +---+---+
        | 2 | 5 |
        +---+---+
     */

    class TableSlots
    {
        public:
                                            TableSlots();
                                            ~TableSlots();
            inline ScBroadcastAreaSlot**    getSlots() { return ppSlots; }

            /**
                Obtain slot pointer, no check on validity! It is assumed that
                all calls are made with the results of ComputeSlotOffset(),
                ComputeAreaPoints() and ComputeNextSlot()
              */
            inline ScBroadcastAreaSlot*     getAreaSlot( SCSIZE nOff ) { return *(ppSlots + nOff); }

        private:
            ScBroadcastAreaSlot**   ppSlots;

            // prevent usage
            TableSlots( const TableSlots& );
            TableSlots& operator=( const TableSlots& );
    };

    typedef ::std::map< SCTAB, TableSlots* > TableSlotsMap;

    typedef ::std::vector< ::std::pair< ScBroadcastAreaSlot*, ScBroadcastAreas::iterator > > AreasToBeErased;

private:
    ScBroadcastAreasBulk  aBulkBroadcastAreas;
    TableSlotsMap         aTableSlotsMap;
    AreasToBeErased       maAreasToBeErased;
    SvtBroadcaster       *pBCAlways;             // for the RC_ALWAYS special range
    ScDocument           *pDoc;
    ScBroadcastArea      *pUpdateChain;
    ScBroadcastArea      *pEOUpdateChain;
    sal_uLong             nInBulkBroadcast;

    inline SCSIZE       ComputeSlotOffset( const ScAddress& rAddress ) const;
    void                ComputeAreaPoints( const ScRange& rRange,
                                            SCSIZE& nStart, SCSIZE& nEnd,
                                            SCSIZE& nRowBreak ) const;

public:
                        ScBroadcastAreaSlotMachine( ScDocument* pDoc );
                        ~ScBroadcastAreaSlotMachine();
    void                StartListeningArea( const ScRange& rRange,
                                            SvtListener* pListener );
    void                EndListeningArea( const ScRange& rRange,
                                            SvtListener* pListener );
    bool                AreaBroadcast( const ScHint& rHint ) const;
        // return: at least one broadcast occurred
    bool                AreaBroadcastInRange( const ScRange& rRange, const ScHint& rHint ) const;
    void                DelBroadcastAreasInRange( const ScRange& rRange );
    void                UpdateBroadcastAreas( UpdateRefMode eUpdateRefMode,
                                            const ScRange& rRange,
                                            SCsCOL nDx, SCsROW nDy, SCsTAB nDz );
    void                EnterBulkBroadcast();
    void                LeaveBulkBroadcast();
    bool                InsertBulkArea( const ScBroadcastArea* p );
    /// @return: how many removed
    size_t              RemoveBulkArea( const ScBroadcastArea* p );
    inline ScBroadcastArea* GetUpdateChain() const { return pUpdateChain; }
    inline void SetUpdateChain( ScBroadcastArea* p ) { pUpdateChain = p; }
    inline ScBroadcastArea* GetEOUpdateChain() const { return pEOUpdateChain; }
    inline void SetEOUpdateChain( ScBroadcastArea* p ) { pEOUpdateChain = p; }
    inline bool IsInBulkBroadcast() const { return nInBulkBroadcast > 0; }

    // only for ScBroadcastAreaSlot
    void                PushAreaToBeErased( ScBroadcastAreaSlot* pSlot,
                                            ScBroadcastAreas::iterator& rIter );
    // only for ScBroadcastAreaSlot
    void                FinallyEraseAreas( ScBroadcastAreaSlot* pSlot );

    std::vector<sc::AreaListener> GetAllListeners(
        const ScRange& rRange, sc::AreaOverlapType eType );
};

class ScBulkBroadcast
{
    ScBroadcastAreaSlotMachine* pBASM;
public:
    explicit ScBulkBroadcast( ScBroadcastAreaSlotMachine* p ) : pBASM(p)
    {
        if (pBASM)
            pBASM->EnterBulkBroadcast();
    }
    ~ScBulkBroadcast()
    {
        if (pBASM)
            pBASM->LeaveBulkBroadcast();
    }
    void LeaveBulkBroadcast()
    {
        if (pBASM)
        {
            pBASM->LeaveBulkBroadcast();
            pBASM = NULL;
        }
    }
};

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>Unindent.<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#ifndef INCLUDED_SC_SOURCE_CORE_INC_BCASLOT_HXX
#define INCLUDED_SC_SOURCE_CORE_INC_BCASLOT_HXX

#include <set>
#include <boost/unordered_set.hpp>
#include <functional>
#include <svl/broadcast.hxx>

#include "global.hxx"
#include "brdcst.hxx"

namespace sc {

struct AreaListener
{
    ScRange maArea;
    SvtListener* mpListener;
};

}

/**
    Used in a Unique Associative Container.
 */

class ScBroadcastArea
{
private:
    ScBroadcastArea*    pUpdateChainNext;
    SvtBroadcaster      aBroadcaster;
    ScRange             aRange;
    sal_uLong               nRefCount;
    bool                bInUpdateChain;

public:
            ScBroadcastArea( const ScRange& rRange )
                : pUpdateChainNext( NULL ), aRange( rRange ),
                nRefCount( 0 ), bInUpdateChain( false ) {}
    inline SvtBroadcaster&       GetBroadcaster()       { return aBroadcaster; }
    inline const SvtBroadcaster& GetBroadcaster() const { return aBroadcaster; }
    inline void         UpdateRange( const ScRange& rNewRange )
                            { aRange = rNewRange; }
    inline const ScRange&   GetRange() const { return aRange; }
    inline const ScAddress& GetStart() const { return aRange.aStart; }
    inline const ScAddress& GetEnd() const { return aRange.aEnd; }
    inline void         IncRef() { ++nRefCount; }
    inline sal_uLong        DecRef() { return nRefCount ? --nRefCount : 0; }
    inline sal_uLong        GetRef() { return nRefCount; }
    inline ScBroadcastArea* GetUpdateChainNext() const { return pUpdateChainNext; }
    inline void         SetUpdateChainNext( ScBroadcastArea* p ) { pUpdateChainNext = p; }
    inline bool         IsInUpdateChain() const { return bInUpdateChain; }
    inline void         SetInUpdateChain( bool b ) { bInUpdateChain = b; }

    /** Equalness of this or range. */
    inline  bool        operator==( const ScBroadcastArea & rArea ) const;
};

inline bool ScBroadcastArea::operator==( const ScBroadcastArea & rArea ) const
{
    return aRange == rArea.aRange;
}

struct ScBroadcastAreaEntry
{
    ScBroadcastArea* mpArea;
    mutable bool     mbErasure;     ///< TRUE if marked for erasure in this set

    ScBroadcastAreaEntry( ScBroadcastArea* p ) : mpArea( p), mbErasure( false) {}
};

struct ScBroadcastAreaHash
{
    size_t operator()( const ScBroadcastAreaEntry& rEntry ) const
    {
        return rEntry.mpArea->GetRange().hashArea();
    }
};

struct ScBroadcastAreaEqual
{
    bool operator()( const ScBroadcastAreaEntry& rEntry1, const ScBroadcastAreaEntry& rEntry2) const
    {
        return *rEntry1.mpArea == *rEntry2.mpArea;
    }
};

typedef ::boost::unordered_set< ScBroadcastAreaEntry, ScBroadcastAreaHash, ScBroadcastAreaEqual > ScBroadcastAreas;

struct ScBroadcastAreaBulkHash
{
    size_t operator()( const ScBroadcastArea* p ) const
    {
        return reinterpret_cast<size_t>(p);
    }
};

struct ScBroadcastAreaBulkEqual
{
    bool operator()( const ScBroadcastArea* p1, const ScBroadcastArea* p2) const
    {
        return p1 == p2;
    }
};

typedef ::boost::unordered_set< const ScBroadcastArea*, ScBroadcastAreaBulkHash,
        ScBroadcastAreaBulkEqual > ScBroadcastAreasBulk;

class ScBroadcastAreaSlotMachine;

/// Collection of BroadcastAreas
class ScBroadcastAreaSlot
{
private:
    ScBroadcastAreas    aBroadcastAreaTbl;
    mutable ScBroadcastArea aTmpSeekBroadcastArea;      // for FindBroadcastArea()
    ScDocument*         pDoc;
    ScBroadcastAreaSlotMachine* pBASM;
    bool                mbInBroadcastIteration;

    /**
     * If true, the slot has at least one area broadcaster marked for removal.
     * This flag is used only during broadcast iteration, to speed up
     * iteration.  Using this flag is cheaper than dereferencing each iterator
     * and checking its own flag inside especially when no areas are marked
     * for removal.
     */
    bool mbHasErasedArea;

    ScBroadcastAreas::const_iterator  FindBroadcastArea( const ScRange& rRange ) const;

    /**
        More hypothetical (memory would probably be doomed anyway) check
        whether there would be an overflow when adding an area, setting the
        proper state if so.

        @return true if a HardRecalcState is effective and area is not to be
        added.
      */
    bool                CheckHardRecalcStateCondition() const;

    /** Finally erase all areas pushed as to-be-erased. */
    void                FinallyEraseAreas();

    bool                isMarkedErased( const ScBroadcastAreas::iterator& rIter )
                            {
                                return (*rIter).mbErasure;
                            }

public:
                        ScBroadcastAreaSlot( ScDocument* pDoc,
                                        ScBroadcastAreaSlotMachine* pBASM );
                        ~ScBroadcastAreaSlot();
    const ScBroadcastAreas& GetBroadcastAreas() const
                                            { return aBroadcastAreaTbl; }

    /**
        Only here new ScBroadcastArea objects are created, prevention of dupes.

        @param rpArea
            If NULL, a new ScBroadcastArea is created and assigned ton the
            reference if a matching area wasn't found. If a matching area was
            found, that is assigned. In any case, the SvtListener is added to
            the broadcaster.

            If not NULL then no listeners are startet, only the area is
            inserted and the reference count incremented. Effectively the same
            as InsertListeningArea(), so use that instead.

        @return
            true if rpArea passed was NULL and ScBroadcastArea is newly
            created.
     */
    bool                StartListeningArea( const ScRange& rRange,
                                            SvtListener* pListener,
                                            ScBroadcastArea*& rpArea );

    /**
        Insert a ScBroadcastArea obtained via StartListeningArea() to
        subsequent slots.
     */
    void                InsertListeningArea( ScBroadcastArea* pArea );

    void                EndListeningArea( const ScRange& rRange,
                                            SvtListener* pListener,
                                            ScBroadcastArea*& rpArea );
    bool                AreaBroadcast( const ScHint& rHint );
    /// @return true if at least one broadcast occurred.
    bool                AreaBroadcastInRange( const ScRange& rRange,
                                              const ScHint& rHint );
    void                DelBroadcastAreasInRange( const ScRange& rRange );
    void                UpdateRemove( UpdateRefMode eUpdateRefMode,
                                        const ScRange& rRange,
                                        SCsCOL nDx, SCsROW nDy, SCsTAB nDz );
    void                UpdateRemoveArea( ScBroadcastArea* pArea );
    void                UpdateInsert( ScBroadcastArea* pArea );

    bool                IsInBroadcastIteration() const { return mbInBroadcastIteration; }

    /** Erase an area from set and delete it if last reference, or if
        mbInBroadcastIteration is set push it to the vector of to-be-erased
        areas instead.

        Meant to be used internally and from ScBroadcastAreaSlotMachine only.
     */
    void                EraseArea( ScBroadcastAreas::iterator& rIter );

    void GetAllListeners(
        const ScRange& rRange, std::vector<sc::AreaListener>& rListeners, sc::AreaOverlapType eType );
};

/**
    BroadcastAreaSlots and their management, once per document.
 */

class  ScBroadcastAreaSlotMachine
{
private:

    /**
        Slot offset arrangement of columns and rows, once per sheet.

        +---+---+
        | 0 | 3 |
        +---+---+
        | 1 | 4 |
        +---+---+
        | 2 | 5 |
        +---+---+
     */

    class TableSlots
    {
    public:
                                        TableSlots();
                                        ~TableSlots();
        inline ScBroadcastAreaSlot**    getSlots() { return ppSlots; }

        /**
            Obtain slot pointer, no check on validity! It is assumed that
            all calls are made with the results of ComputeSlotOffset(),
            ComputeAreaPoints() and ComputeNextSlot()
          */
        inline ScBroadcastAreaSlot*     getAreaSlot( SCSIZE nOff ) { return *(ppSlots + nOff); }

    private:
        ScBroadcastAreaSlot**   ppSlots;

        // prevent usage
        TableSlots( const TableSlots& );
        TableSlots& operator=( const TableSlots& );
    };

    typedef ::std::map< SCTAB, TableSlots* > TableSlotsMap;

    typedef ::std::vector< ::std::pair< ScBroadcastAreaSlot*, ScBroadcastAreas::iterator > > AreasToBeErased;

private:
    ScBroadcastAreasBulk  aBulkBroadcastAreas;
    TableSlotsMap         aTableSlotsMap;
    AreasToBeErased       maAreasToBeErased;
    SvtBroadcaster       *pBCAlways;             // for the RC_ALWAYS special range
    ScDocument           *pDoc;
    ScBroadcastArea      *pUpdateChain;
    ScBroadcastArea      *pEOUpdateChain;
    sal_uLong             nInBulkBroadcast;

    inline SCSIZE       ComputeSlotOffset( const ScAddress& rAddress ) const;
    void                ComputeAreaPoints( const ScRange& rRange,
                                            SCSIZE& nStart, SCSIZE& nEnd,
                                            SCSIZE& nRowBreak ) const;

public:
                        ScBroadcastAreaSlotMachine( ScDocument* pDoc );
                        ~ScBroadcastAreaSlotMachine();
    void                StartListeningArea( const ScRange& rRange,
                                            SvtListener* pListener );
    void                EndListeningArea( const ScRange& rRange,
                                            SvtListener* pListener );
    bool                AreaBroadcast( const ScHint& rHint ) const;
        // return: at least one broadcast occurred
    bool                AreaBroadcastInRange( const ScRange& rRange, const ScHint& rHint ) const;
    void                DelBroadcastAreasInRange( const ScRange& rRange );
    void                UpdateBroadcastAreas( UpdateRefMode eUpdateRefMode,
                                            const ScRange& rRange,
                                            SCsCOL nDx, SCsROW nDy, SCsTAB nDz );
    void                EnterBulkBroadcast();
    void                LeaveBulkBroadcast();
    bool                InsertBulkArea( const ScBroadcastArea* p );
    /// @return: how many removed
    size_t              RemoveBulkArea( const ScBroadcastArea* p );
    inline ScBroadcastArea* GetUpdateChain() const { return pUpdateChain; }
    inline void SetUpdateChain( ScBroadcastArea* p ) { pUpdateChain = p; }
    inline ScBroadcastArea* GetEOUpdateChain() const { return pEOUpdateChain; }
    inline void SetEOUpdateChain( ScBroadcastArea* p ) { pEOUpdateChain = p; }
    inline bool IsInBulkBroadcast() const { return nInBulkBroadcast > 0; }

    // only for ScBroadcastAreaSlot
    void                PushAreaToBeErased( ScBroadcastAreaSlot* pSlot,
                                            ScBroadcastAreas::iterator& rIter );
    // only for ScBroadcastAreaSlot
    void                FinallyEraseAreas( ScBroadcastAreaSlot* pSlot );

    std::vector<sc::AreaListener> GetAllListeners(
        const ScRange& rRange, sc::AreaOverlapType eType );
};

class ScBulkBroadcast
{
    ScBroadcastAreaSlotMachine* pBASM;
public:
    explicit ScBulkBroadcast( ScBroadcastAreaSlotMachine* p ) : pBASM(p)
    {
        if (pBASM)
            pBASM->EnterBulkBroadcast();
    }
    ~ScBulkBroadcast()
    {
        if (pBASM)
            pBASM->LeaveBulkBroadcast();
    }
    void LeaveBulkBroadcast()
    {
        if (pBASM)
        {
            pBASM->LeaveBulkBroadcast();
            pBASM = NULL;
        }
    }
};

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>/**
 * @file rectangle_tree.hpp
 * @author Andrew Wells
 *
 * Include all the necessary filse to use the Rectangle Type Trees (RTree, RStarTree, XTree,
 * and HilbertRTree.)
 */
#ifndef __MLPACK_CORE_TREE_RECTANGLE_TREE_HPP
#define __MLPACK_CORE_TREE_RECTANGLE_TREE_HPP

/* we include bounds.hpp since it gives us the necessary files.
 * However, we will not use the "ballbounds" option.
 */
#include "bounds.hpp"
#include "rectangle_tree/rectangle_tree.hpp"
#include "rectangle_tree/single_tree_traverser.hpp"
#include "rectangle_tree/single_tree_traverser_impl.hpp"
#include "rectangle_tree/dual_tree_traverser.hpp"
#include "rectangle_tree/dual_tree_traverser_impl.hpp"
#include "rectangle_tree/r_tree_split.hpp"
#include "rectangle_tree/r_star_tree_split.hpp"
#include "rectangle_tree/r_tree_descent_heuristic.hpp"
#include "rectangle_tree/r_star_tree_descent_heuristic.hpp"
#include "rectangle_tree/traits.hpp"
#include "rectangle_tree/x_tree_split.hpp"
#include "rectangle_tree/typedef.hpp"

#endif
<commit_msg>Minor misspelling fix.<commit_after>/**
 * @file rectangle_tree.hpp
 * @author Andrew Wells
 *
 * Include all the necessary files to use the Rectangle Type Trees (RTree,
 * RStarTree, XTree, and HilbertRTree).
 */
#ifndef __MLPACK_CORE_TREE_RECTANGLE_TREE_HPP
#define __MLPACK_CORE_TREE_RECTANGLE_TREE_HPP

/* We include bounds.hpp since it gives us the necessary files.
 * However, we will not use the "ballbounds" option.
 */
#include "bounds.hpp"
#include "rectangle_tree/rectangle_tree.hpp"
#include "rectangle_tree/single_tree_traverser.hpp"
#include "rectangle_tree/single_tree_traverser_impl.hpp"
#include "rectangle_tree/dual_tree_traverser.hpp"
#include "rectangle_tree/dual_tree_traverser_impl.hpp"
#include "rectangle_tree/r_tree_split.hpp"
#include "rectangle_tree/r_star_tree_split.hpp"
#include "rectangle_tree/r_tree_descent_heuristic.hpp"
#include "rectangle_tree/r_star_tree_descent_heuristic.hpp"
#include "rectangle_tree/traits.hpp"
#include "rectangle_tree/x_tree_split.hpp"
#include "rectangle_tree/typedef.hpp"

#endif
<|endoftext|>


<commit_before>void SETUP()
{
   // we assume PWG0base (and thus ESD) already loaded
   CheckLoadLibrary("libMinuit");

   // this package depends on STEER
   CheckLoadLibrary("libVMC");
   CheckLoadLibrary("libMinuit");
   CheckLoadLibrary("libSTEER");

   // more packages to access the alice event header
   CheckLoadLibrary("libEVGEN");
   CheckLoadLibrary("libFASTSIM");
   CheckLoadLibrary("libmicrocern");
   CheckLoadLibrary("libpdf");
   CheckLoadLibrary("libpythia6");
   CheckLoadLibrary("libEGPythia6");
   CheckLoadLibrary("libAliPythia6");

   CheckLoadLibrary("libPWG0dep");

   // Set the Include paths
   gROOT->ProcessLine(".include PWG0dep");
}

Int_t CheckLoadLibrary(const char* library)
{
  // checks if a library is already loaded, if not loads the library

  if (strlen(gSystem->GetLibraries(Form("%s.so", library), "", kFALSE)) > 0)
    return 1;

  return gSystem->Load(library);
}
<commit_msg>added loading of lhapdf lib<commit_after>void SETUP()
{
   // we assume PWG0base (and thus ESD) already loaded
   CheckLoadLibrary("libMinuit");

   // this package depends on STEER
   CheckLoadLibrary("libVMC");
   CheckLoadLibrary("libMinuit");
   CheckLoadLibrary("libSTEER");

   // more packages to access the alice event header
   CheckLoadLibrary("libEVGEN");
   CheckLoadLibrary("libFASTSIM");
   CheckLoadLibrary("libmicrocern");
   CheckLoadLibrary("libpdf");    // both are needed because old aliroot needs pdf, new one lhapdf
   CheckLoadLibrary("liblhapdf"); //
   CheckLoadLibrary("libpythia6");
   CheckLoadLibrary("libEGPythia6");
   CheckLoadLibrary("libAliPythia6");

   CheckLoadLibrary("libPWG0dep");

   // Set the Include paths
   gROOT->ProcessLine(".include PWG0dep");
}

Int_t CheckLoadLibrary(const char* library)
{
  // checks if a library is already loaded, if not loads the library

  if (strlen(gSystem->GetLibraries(Form("%s.so", library), "", kFALSE)) > 0)
    return 1;

  return gSystem->Load(library);
}
<|endoftext|>


<commit_before>// Probabilistic Question-Answering system
// @2017 Sarge Rogatch
// This software is distributed under GNU AGPLv3 license. See file LICENSE in repository root for details.

#include "stdafx.h"

using namespace ProbQA;
using namespace SRPlat;

//////TODO: move these to common routines when needed by something else
const uint64_t gPerfCntFreq = []() {
  LARGE_INTEGER li;
  if (!QueryPerformanceFrequency(&li)) {
    printf("Can't get performance counter frequency: error %u.\n", GetLastError());
  }
  return li.QuadPart;
}();

uint64_t GetPerfCnt() {
  LARGE_INTEGER li;
  if (!QueryPerformanceCounter(&li)) {
    printf("Failed QueryPerformanceCounter(): error %u.\n", GetLastError());
  }
  return li.QuadPart;
}

bool ExistsDirectory(const char* const szPath) {
  DWORD dwAttrib = GetFileAttributesA(szPath);
  return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
    (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}

//////End of common routines

void InfLoopThread() {
  for (;;) {}
}

void InfLoopHead() {
  std::vector<std::thread> testLoad;
  for (int i = 0; i < 16; i++) {
    testLoad.emplace_back(&InfLoopThread);
  }
  for (int i = 0; i < 16; i++) {
    testLoad[i].join();
  }
}

namespace {

std::string gKbsDir;

} // anonymous namespace

int LearnBinarySearch(const char* const initKbFp) {
  FILE *fpProgress = fopen("progress.txt", "wt");

  PqaError err;
  IPqaEngine *pEngine;
  if (initKbFp == nullptr) {
    EngineDefinition ed;
    ed._dims._nAnswers = 5;
    ed._dims._nQuestions = 1000;
    ed._dims._nTargets = 1000;
    ed._initAmount = 0.1;
    ed._prec._type = TPqaPrecisionType::Double;
    pEngine = PqaGetEngineFactory().CreateCpuEngine(err, ed);
    if (!err.IsOk() || pEngine == nullptr) {
      fprintf(stderr, "Failed to instantiate a ProbQA engine: %s\n", err.ToString(true).ToStd().c_str());
      return int(SRExitCode::UnspecifiedError);
    }
  }
  else {
    pEngine = PqaGetEngineFactory().LoadCpuEngine(err, initKbFp);
    if (!err.IsOk() || pEngine == nullptr) {
      fprintf(stderr, "Failed to load a ProbQA engine: %s\n", err.ToString(true).ToStd().c_str());
      return int(SRExitCode::UnspecifiedError);
    }
  }

  SRFastRandom fr;
  SREntropyAdapter ea(fr);

  constexpr int64_t cnTrainings = 1000 * 1000;
  constexpr int64_t cMaxQuizLen = 100;
  constexpr int64_t cMaxTrialLen = 30;
  constexpr int64_t cnTopRated = 1;

  int64_t nCorrect = 0;
  int64_t sumQuizLens = 0;
  double totCertainty = 0;
  uint64_t pcStart = GetPerfCnt();
  uint64_t prevQAsked = pEngine->GetTotalQuestionsAsked(err);
  if (!err.IsOk()) {
    fprintf(stderr, SR_FILE_LINE "Failed to query the total number of questions asked.\n");
    return int(SRExitCode::UnspecifiedError);
  }
  for (int64_t i = 0; i < cnTrainings; i++) {
    if (((i & 255) == 0) && (i != 0)) {
      const uint64_t totQAsked = pEngine->GetTotalQuestionsAsked(err);
      if (!err.IsOk()) {
        fprintf(stderr, SR_FILE_LINE "Failed to query the total number of questions asked.\n");
        return int(SRExitCode::UnspecifiedError);
      }
      const double precision = nCorrect * 100.0 / 256;
      const double elapsedSec = double(GetPerfCnt() - pcStart) / gPerfCntFreq;
      printf("\n*%" PRIu64 ";%.2lf%%*", totQAsked, precision);
      fprintf(fpProgress, "%" PRId64 "\t%" PRIu64 "\t%lf\t%lf\t%lf\t%lf\n", i, totQAsked, precision,
        double(sumQuizLens) / nCorrect, totCertainty / nCorrect, (totQAsked - prevQAsked) / elapsedSec);
      fflush(fpProgress);

      char kbFile[128];
      sprintf(kbFile, "%sdichotomy%.6" PRId64 ".kb", gKbsDir.c_str(), i);
      err = pEngine->SaveKB(kbFile, false);
      if (!err.IsOk()) {
        fprintf(stderr, SR_FILE_LINE "Failed to save the KB.\n");
        return int(SRExitCode::UnspecifiedError);
      }

      nCorrect = 0;
      sumQuizLens = 0;
      totCertainty = 0;
      prevQAsked = totQAsked;
      pcStart = GetPerfCnt();
    }
    const TPqaId guess = ea.Generate<TPqaId>(pEngine->CopyDims()._nTargets);
    volatile TPqaId dbgGuess = guess;
    const TPqaId iQuiz = pEngine->StartQuiz(err);
    if (!err.IsOk() || iQuiz == cInvalidPqaId) {
      fprintf(stderr, "Failed to create a quiz.\n");
      return int(SRExitCode::UnspecifiedError);
    }
    int64_t j = 0;
    for (; j < cMaxQuizLen; j++) {
      const TPqaId iQuestion = pEngine->NextQuestion(err, iQuiz);
      if (!err.IsOk() || iQuestion == cInvalidPqaId) {
        fprintf(stderr, "Failed to query a next question.\n");
        return int(SRExitCode::UnspecifiedError);
      }
      TPqaId iAnswer;
      if (guess < iQuestion - 32) {
        iAnswer = 0;
      }
      else if (iQuestion - 32 <= guess && guess < iQuestion) {
        iAnswer = 1;
      }
      else if (iQuestion == guess) {
        iAnswer = 2;
      }
      else if (iQuestion < guess && guess <= iQuestion + 32) {
        iAnswer = 3;
      }
      else if (guess > iQuestion + 32) {
        iAnswer = 4;
      }
      else {
        fprintf(stderr, "Answering logic error.\n");
        return int(SRExitCode::UnspecifiedError);
      }
      err = pEngine->RecordAnswer(iQuiz, iAnswer);
      if (!err.IsOk()) {
        fprintf(stderr, "Failed to record answer: %s\n", err.ToString(true).ToStd().c_str());
        return int(SRExitCode::UnspecifiedError);
      }

      RatedTarget rts[cnTopRated];
      const TPqaId nListed = pEngine->ListTopTargets(err, iQuiz, cnTopRated, rts);
      if (!err.IsOk() || nListed != cnTopRated) {
        fprintf(stderr, "Failed to list top targets.\n");
        return int(SRExitCode::UnspecifiedError);
      }

      //for (TPqaId k = 0; k < cnTopRated; k++) {
      //  printf(" [%g; %" PRId64 "] ", rts[k]._prob, rts[k]._iTarget);
      //}
      //printf("\n");

      TPqaId posInTop = cInvalidPqaId;
      for (TPqaId k = 0; k < cnTopRated; k++) {
        if (rts[k]._iTarget == guess) {
          posInTop = k;
          break;
        }
      }
      if (posInTop != cInvalidPqaId) {
        const double certainty = rts[posInTop]._prob * 100;
        nCorrect++;
        sumQuizLens += j + 1;
        totCertainty += certainty;
        //printf("[guess=%" PRId64 ",top=%" PRId64 ",after=%" PRId64 "]", int64_t(guess), int64_t(posInTop),
        //  int64_t(j+1));
        printf("[G=%" PRId64 ",A=%" PRId64 ",P=%.2lf%%]", int64_t(guess), int64_t(j + 1), certainty);
        break;
      }
    }
    if (j >= cMaxQuizLen) {
      printf("-");
    }
    err = pEngine->RecordQuizTarget(iQuiz, guess);
    if (!err.IsOk()) {
      fprintf(stderr, "Failed to record quiz target: %s\n", err.ToString(true).ToStd().c_str());
      return int(SRExitCode::UnspecifiedError);
    }
    err = pEngine->ReleaseQuiz(iQuiz);
    if (!err.IsOk()) {
      fprintf(stderr, "Failed to release a quiz: %s\n", err.ToString(true).ToStd().c_str());
      return int(SRExitCode::UnspecifiedError);
    }
  }
  delete pEngine;
  fclose(fpProgress);
  return 0;
}

int __cdecl main() {
  const char* baseName = "Logs\\PqaClient";
  if (!CreateDirectoryA("Logs", nullptr)) {
    uint32_t le = GetLastError();
    if (le != ERROR_ALREADY_EXISTS) {
      baseName = "PqaClient";
    }
  }
  SRDefaultLogger::Init(SRString::MakeUnowned(baseName));

  gKbsDir = "E:\\Data\\Dev\\Engines\\ProbQA\\KBs\\";
  if (!ExistsDirectory(gKbsDir.c_str())) {
    const char* const sKBs = "KBs";
    gKbsDir = std::string(sKBs) + "\\";
    if (!CreateDirectoryA(sKBs, nullptr)) {
      uint32_t le = GetLastError();
      if (le != ERROR_ALREADY_EXISTS) {
        fprintf(stderr, "Failed to ensure that a directory for KBs exists.\n");
        return int(SRExitCode::UnspecifiedError);
      }
    }
  }

  //return LearnBinarySearch("KBs\\initial.kb"); // To load a saved KB
  return LearnBinarySearch(nullptr); // To create a KB from scratch by training
}
<commit_msg>Changed max quiz length to 25 questions.<commit_after>// Probabilistic Question-Answering system
// @2017 Sarge Rogatch
// This software is distributed under GNU AGPLv3 license. See file LICENSE in repository root for details.

#include "stdafx.h"

using namespace ProbQA;
using namespace SRPlat;

//////TODO: move these to common routines when needed by something else
const uint64_t gPerfCntFreq = []() {
  LARGE_INTEGER li;
  if (!QueryPerformanceFrequency(&li)) {
    printf("Can't get performance counter frequency: error %u.\n", GetLastError());
  }
  return li.QuadPart;
}();

uint64_t GetPerfCnt() {
  LARGE_INTEGER li;
  if (!QueryPerformanceCounter(&li)) {
    printf("Failed QueryPerformanceCounter(): error %u.\n", GetLastError());
  }
  return li.QuadPart;
}

bool ExistsDirectory(const char* const szPath) {
  DWORD dwAttrib = GetFileAttributesA(szPath);
  return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
    (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}

//////End of common routines

void InfLoopThread() {
  for (;;) {}
}

void InfLoopHead() {
  std::vector<std::thread> testLoad;
  for (int i = 0; i < 16; i++) {
    testLoad.emplace_back(&InfLoopThread);
  }
  for (int i = 0; i < 16; i++) {
    testLoad[i].join();
  }
}

namespace {

std::string gKbsDir;

} // anonymous namespace

int LearnBinarySearch(const char* const initKbFp) {
  FILE *fpProgress = fopen("progress.txt", "wt");

  PqaError err;
  IPqaEngine *pEngine;
  if (initKbFp == nullptr) {
    EngineDefinition ed;
    ed._dims._nAnswers = 5;
    ed._dims._nQuestions = 1000;
    ed._dims._nTargets = 1000;
    ed._initAmount = 0.1;
    ed._prec._type = TPqaPrecisionType::Double;
    pEngine = PqaGetEngineFactory().CreateCpuEngine(err, ed);
    if (!err.IsOk() || pEngine == nullptr) {
      fprintf(stderr, "Failed to instantiate a ProbQA engine: %s\n", err.ToString(true).ToStd().c_str());
      return int(SRExitCode::UnspecifiedError);
    }
  }
  else {
    pEngine = PqaGetEngineFactory().LoadCpuEngine(err, initKbFp);
    if (!err.IsOk() || pEngine == nullptr) {
      fprintf(stderr, "Failed to load a ProbQA engine: %s\n", err.ToString(true).ToStd().c_str());
      return int(SRExitCode::UnspecifiedError);
    }
  }

  SRFastRandom fr;
  SREntropyAdapter ea(fr);

  constexpr int64_t cnTrainings = 4 * 1000 * 1000;
  constexpr int64_t cMaxQuizLen = 25;
  constexpr int64_t cnTopRated = 1;

  int64_t nCorrect = 0;
  int64_t sumQuizLens = 0;
  double totCertainty = 0;
  uint64_t pcStart = GetPerfCnt();
  uint64_t prevQAsked = pEngine->GetTotalQuestionsAsked(err);
  if (!err.IsOk()) {
    fprintf(stderr, SR_FILE_LINE "Failed to query the total number of questions asked.\n");
    return int(SRExitCode::UnspecifiedError);
  }
  for (int64_t i = 0; i < cnTrainings; i++) {
    if (((i & 1023) == 0) && (i != 0)) {
      const uint64_t totQAsked = pEngine->GetTotalQuestionsAsked(err);
      if (!err.IsOk()) {
        fprintf(stderr, SR_FILE_LINE "Failed to query the total number of questions asked.\n");
        return int(SRExitCode::UnspecifiedError);
      }
      const double precision = nCorrect * 100.0 / 1024;
      const double elapsedSec = double(GetPerfCnt() - pcStart) / gPerfCntFreq;
      printf("\n*%" PRIu64 ";%.2lf%%*", totQAsked, precision);
      fprintf(fpProgress, "%" PRId64 "\t%" PRIu64 "\t%lf\t%lf\t%lf\t%lf\n", i, totQAsked, precision,
        double(sumQuizLens) / nCorrect, totCertainty / nCorrect, (totQAsked - prevQAsked) / elapsedSec);
      fflush(fpProgress);

      char kbFile[128];
      sprintf(kbFile, "%sdichotomy%.6" PRId64 ".kb", gKbsDir.c_str(), i);
      err = pEngine->SaveKB(kbFile, false);
      if (!err.IsOk()) {
        fprintf(stderr, SR_FILE_LINE "Failed to save the KB.\n");
        return int(SRExitCode::UnspecifiedError);
      }

      nCorrect = 0;
      sumQuizLens = 0;
      totCertainty = 0;
      prevQAsked = totQAsked;
      pcStart = GetPerfCnt();
    }
    const TPqaId guess = ea.Generate<TPqaId>(pEngine->CopyDims()._nTargets);
    volatile TPqaId dbgGuess = guess;
    const TPqaId iQuiz = pEngine->StartQuiz(err);
    if (!err.IsOk() || iQuiz == cInvalidPqaId) {
      fprintf(stderr, "Failed to create a quiz.\n");
      return int(SRExitCode::UnspecifiedError);
    }
    int64_t j = 0;
    for (; j < cMaxQuizLen; j++) {
      const TPqaId iQuestion = pEngine->NextQuestion(err, iQuiz);
      if (!err.IsOk() || iQuestion == cInvalidPqaId) {
        fprintf(stderr, "Failed to query a next question.\n");
        return int(SRExitCode::UnspecifiedError);
      }
      TPqaId iAnswer;
      if (guess < iQuestion - 32) {
        iAnswer = 0;
      }
      else if (iQuestion - 32 <= guess && guess < iQuestion) {
        iAnswer = 1;
      }
      else if (iQuestion == guess) {
        iAnswer = 2;
      }
      else if (iQuestion < guess && guess <= iQuestion + 32) {
        iAnswer = 3;
      }
      else if (guess > iQuestion + 32) {
        iAnswer = 4;
      }
      else {
        fprintf(stderr, "Answering logic error.\n");
        return int(SRExitCode::UnspecifiedError);
      }
      err = pEngine->RecordAnswer(iQuiz, iAnswer);
      if (!err.IsOk()) {
        fprintf(stderr, "Failed to record answer: %s\n", err.ToString(true).ToStd().c_str());
        return int(SRExitCode::UnspecifiedError);
      }

      RatedTarget rts[cnTopRated];
      const TPqaId nListed = pEngine->ListTopTargets(err, iQuiz, cnTopRated, rts);
      if (!err.IsOk() || nListed != cnTopRated) {
        fprintf(stderr, "Failed to list top targets.\n");
        return int(SRExitCode::UnspecifiedError);
      }

      //for (TPqaId k = 0; k < cnTopRated; k++) {
      //  printf(" [%g; %" PRId64 "] ", rts[k]._prob, rts[k]._iTarget);
      //}
      //printf("\n");

      TPqaId posInTop = cInvalidPqaId;
      for (TPqaId k = 0; k < cnTopRated; k++) {
        if (rts[k]._iTarget == guess) {
          posInTop = k;
          break;
        }
      }
      if (posInTop != cInvalidPqaId) {
        const double certainty = rts[posInTop]._prob * 100;
        nCorrect++;
        sumQuizLens += j + 1;
        totCertainty += certainty;
        //printf("[guess=%" PRId64 ",top=%" PRId64 ",after=%" PRId64 "]", int64_t(guess), int64_t(posInTop),
        //  int64_t(j+1));
        printf("[G=%" PRId64 ",A=%" PRId64 ",P=%.2lf%%]", int64_t(guess), int64_t(j + 1), certainty);
        break;
      }
    }
    if (j >= cMaxQuizLen) {
      printf("-");
    }
    err = pEngine->RecordQuizTarget(iQuiz, guess);
    if (!err.IsOk()) {
      fprintf(stderr, "Failed to record quiz target: %s\n", err.ToString(true).ToStd().c_str());
      return int(SRExitCode::UnspecifiedError);
    }
    err = pEngine->ReleaseQuiz(iQuiz);
    if (!err.IsOk()) {
      fprintf(stderr, "Failed to release a quiz: %s\n", err.ToString(true).ToStd().c_str());
      return int(SRExitCode::UnspecifiedError);
    }
  }
  delete pEngine;
  fclose(fpProgress);
  return 0;
}

int __cdecl main() {
  const char* baseName = "Logs\\PqaClient";
  if (!CreateDirectoryA("Logs", nullptr)) {
    uint32_t le = GetLastError();
    if (le != ERROR_ALREADY_EXISTS) {
      baseName = "PqaClient";
    }
  }
  SRDefaultLogger::Init(SRString::MakeUnowned(baseName));

  gKbsDir = "E:\\Data\\Dev\\Engines\\ProbQA\\KBs\\";
  if (!ExistsDirectory(gKbsDir.c_str())) {
    const char* const sKBs = "KBs";
    gKbsDir = std::string(sKBs) + "\\";
    if (!CreateDirectoryA(sKBs, nullptr)) {
      uint32_t le = GetLastError();
      if (le != ERROR_ALREADY_EXISTS) {
        fprintf(stderr, "Failed to ensure that a directory for KBs exists.\n");
        return int(SRExitCode::UnspecifiedError);
      }
    }
  }

  //return LearnBinarySearch("KBs\\initial.kb"); // To load a saved KB
  return LearnBinarySearch(nullptr); // To create a KB from scratch by training
}
<|endoftext|>


<commit_before>#ifndef VXL_QUAT_HPP
# define VXL_QUAT_HPP
# pragma once

#include "matrix.hpp"

namespace vxl
{

template <typename T>
struct quat
{
  typename vector_traits<T, 4>::vector_type data_;

  // element access
  constexpr auto operator()(unsigned const i) const noexcept
  {
    return data_[i];
  }

  constexpr void set_element(unsigned const i, T const v) noexcept
  {
    data_[i] = v;
  }

  // conversion
  constexpr auto& ref() noexcept { return data_; }
  constexpr auto& ref() const noexcept { return data_; }
};

template <typename T>
inline constexpr quat<T> operator+(quat<T> const& a,
  quat<T> const& b) noexcept
{
  return { a.data_ + b.data_ };
}

template <typename T>
inline constexpr quat<T> operator-(quat<T> const& a,
  quat<T> const& b) noexcept
{
  return { a.data_ - b.data_ };
}

template <typename T>
//__attribute__ ((noinline))
inline constexpr quat<T> operator*(
  quat<T> const& l, quat<T> const& r) noexcept
{
//  l(0)r(3) + l(1)r(2) - l(2)r(1) + l(3)r(0)
// -l(0)r(2) + l(1)r(3) + l(2)r(0) + l(3)r(1)
//  l(0)r(1) - l(1)r(0) + l(2)r(3) + l(3)r(2)
// -l(0)r(0) - l(1)r(1) - l(2)r(2) + l(3)r(3)
  using int_value_type = typename vector_traits<T, 4>::int_value_type;
  using int_vector_type = typename vector_traits<T, 4>::int_vector_type;

#if defined(__clang__)
  auto const t1(
    l.data_ *
    __builtin_shufflevector(r.data_, r.data_, 3, 3, 3, 3)
  );
  auto const t2(
    __builtin_shufflevector(l.data_, l.data_, 1, 2, 0, 2) *
    __builtin_shufflevector(r.data_, r.data_, 2, 0, 1, 2)
  );
  auto const t3(
    __builtin_shufflevector(l.data_, l.data_, 3, 3, 3, 1) *
    __builtin_shufflevector(r.data_, r.data_, 0, 1, 2, 1)
  );
  auto const t4(
    __builtin_shufflevector(l.data_, l.data_, 2, 0, 1, 0) *
    __builtin_shufflevector(r.data_, r.data_, 1, 2, 0, 0)
  );
#else
  auto const t1(
    l.data_ *
    __builtin_shuffle(r.data_, int_vector_type{3, 3, 3, 3})
  );
  auto const t2(
    __builtin_shuffle(l.data_, int_vector_type{1, 2, 0, 2}) *
    __builtin_shuffle(r.data_, int_vector_type{2, 0, 1, 2})
  );
  auto const t3(
    __builtin_shuffle(l.data_, int_vector_type{3, 3, 3, 1}) *
    __builtin_shuffle(r.data_, int_vector_type{0, 1, 2, 1})
  );
  auto const t4(
    __builtin_shuffle(l.data_, int_vector_type{2, 0, 1, 0}) *
    __builtin_shuffle(r.data_, int_vector_type{1, 2, 0, 0})
  );
#endif

  // negate the sign bit
  constexpr int_vector_type mask{
    0, 0, 0, 1 << (8 * sizeof(int_value_type) - 1)
  };

  return {
    t1 +
    decltype(t2)(int_vector_type(t2 + t3) ^ mask) -
    t4
  };
}

template <typename T>
inline constexpr auto& operator+=(quat<T>& a, quat<T> const& b) noexcept
{
  return a.data_ += b.data_, a;
}

template <typename T>
inline constexpr auto& operator-=(quat<T>& a, quat<T> const& b) noexcept
{
  return a.data_ -= b.data_, a;
}

// comparison
template <typename T>
inline bool operator==(quat<T> const& l, quat<T> const& r) noexcept
{
  return detail::vector::all_zeros<T, 4>(l.data_ != r.data_,
    std::make_index_sequence<detail::vector::log2(4)>()
  );
}

template <typename T>
inline constexpr bool operator!=(quat<T> const& l, quat<T> const& r) noexcept
{
  return !operator==(l, r);
}

// scalar part
template <typename T>
inline constexpr auto scalar(quat<T> const& x) noexcept
{
  return x.data_[3];
}

namespace detail
{

namespace quat
{

template <typename T, unsigned N, std::size_t ...Is>
constexpr inline auto scalar_vector(vxl::quat<T> const& x,
  std::index_sequence<Is...>) noexcept
{
#if defined(__clang__)
  return __builtin_shufflevector(x.data_, x.data_, (Is, 3)...);
#else
  using int_vector_type = typename vector_traits<T, N>::int_vector_type;

  return __builtin_shuffle(x.data_, int_vector_type{(Is, 3)...});
#endif
}

}

}

template <typename T, unsigned N>
constexpr inline auto scalar_vector(quat<T> const& x) noexcept
{
  return detail::quat::scalar_vector<T, N>(x,
    std::make_index_sequence<4>()
  );
}

// vector part
template <typename T>
constexpr inline auto vec(quat<T> const& x) noexcept
{
  return vector<T, 3>{x.data_};
}

// conjugation
template <typename T>
inline constexpr void conjugate(quat<T>& x) noexcept
{
  using int_vector_type = typename vector_traits<T, 4>::int_vector_type;
  using vector_type = typename vector_traits<T, 4>::vector_type;

  x.data_ = vector_type(
    int_vector_type(x.data_) ^
    int_vector_type(vector_type{T(-.0), T(-.0), T(-.0), T(.0)})
  );
}

template <typename T>
inline constexpr quat<T> conjugated(quat<T> const& x) noexcept
{
  using int_vector_type = typename vector_traits<T, 4>::int_vector_type;
  using vector_type = typename vector_traits<T, 4>::vector_type;

  return {
    vector_type(
      int_vector_type(x.data_) ^
      int_vector_type(vector_type{T(-.0), T(-.0), T(-.0), T(.0)})
    )
  };
}

template <typename T>
inline constexpr auto norm2(quat<T> const& x) noexcept
{
  vector<T, 4> const q{x.data_};

  return dot(q, q);
}

}

// stream operators
template <typename T>
std::ostream& operator<<(std::ostream& os, vxl::quat<T> const& v)
{
  os << '(';

  for (unsigned i{}; i != 3; ++i)
  {
    os << v.data_[i] << ", ";
  }

  return os << v.data_[3] << ')';
}

#endif // VXL_QUAT_HPP
<commit_msg>some fixes<commit_after>#ifndef VXL_QUAT_HPP
# define VXL_QUAT_HPP
# pragma once

#include "matrix.hpp"

namespace vxl
{

template <typename T>
struct quat
{
  typename vector_traits<T, 4>::vector_type data_;

  // element access
  constexpr auto operator()(unsigned const i) const noexcept
  {
    return data_[i];
  }

  constexpr void set_element(unsigned const i, T const v) noexcept
  {
    data_[i] = v;
  }

  // conversion
  constexpr auto& ref() noexcept { return data_; }
  constexpr auto& ref() const noexcept { return data_; }
};

template <typename T>
inline constexpr quat<T> operator+(quat<T> const& a,
  quat<T> const& b) noexcept
{
  return { a.data_ + b.data_ };
}

template <typename T>
inline constexpr quat<T> operator-(quat<T> const& a,
  quat<T> const& b) noexcept
{
  return { a.data_ - b.data_ };
}

template <typename T>
//__attribute__ ((noinline))
inline constexpr quat<T> operator*(
  quat<T> const& l, quat<T> const& r) noexcept
{
//  l(0)r(3) + l(1)r(2) - l(2)r(1) + l(3)r(0)
// -l(0)r(2) + l(1)r(3) + l(2)r(0) + l(3)r(1)
//  l(0)r(1) - l(1)r(0) + l(2)r(3) + l(3)r(2)
// -l(0)r(0) - l(1)r(1) - l(2)r(2) + l(3)r(3)
  using int_value_type = typename vector_traits<T, 4>::int_value_type;
  using int_vector_type = typename vector_traits<T, 4>::int_vector_type;

#if defined(__clang__)
  auto const t1(
    l.data_ *
    __builtin_shufflevector(r.data_, r.data_, 3, 3, 3, 3)
  );
  auto const t2(
    __builtin_shufflevector(l.data_, l.data_, 1, 2, 0, 2) *
    __builtin_shufflevector(r.data_, r.data_, 2, 0, 1, 2)
  );
  auto const t3(
    __builtin_shufflevector(l.data_, l.data_, 3, 3, 3, 1) *
    __builtin_shufflevector(r.data_, r.data_, 0, 1, 2, 1)
  );
  auto const t4(
    __builtin_shufflevector(l.data_, l.data_, 2, 0, 1, 0) *
    __builtin_shufflevector(r.data_, r.data_, 1, 2, 0, 0)
  );
#else
  auto const t1(
    l.data_ *
    __builtin_shuffle(r.data_, int_vector_type{3, 3, 3, 3})
  );
  auto const t2(
    __builtin_shuffle(l.data_, int_vector_type{1, 2, 0, 2}) *
    __builtin_shuffle(r.data_, int_vector_type{2, 0, 1, 2})
  );
  auto const t3(
    __builtin_shuffle(l.data_, int_vector_type{3, 3, 3, 1}) *
    __builtin_shuffle(r.data_, int_vector_type{0, 1, 2, 1})
  );
  auto const t4(
    __builtin_shuffle(l.data_, int_vector_type{2, 0, 1, 0}) *
    __builtin_shuffle(r.data_, int_vector_type{1, 2, 0, 0})
  );
#endif

  return {
    t1 +
    decltype(t2)(int_vector_type(t2 + t3) ^
      int_vector_type{
        0, 0, 0, 1 << (8 * sizeof(int_value_type) - 1)
      }
    ) -
    t4
  };
}

template <typename T>
inline constexpr auto& operator+=(quat<T>& a, quat<T> const& b) noexcept
{
  return a.data_ += b.data_, a;
}

template <typename T>
inline constexpr auto& operator-=(quat<T>& a, quat<T> const& b) noexcept
{
  return a.data_ -= b.data_, a;
}

// comparison
template <typename T>
inline bool operator==(quat<T> const& l, quat<T> const& r) noexcept
{
  return detail::vector::all_zeros<T, 4>(l.data_ != r.data_,
    std::make_index_sequence<detail::vector::log2(4)>()
  );
}

template <typename T>
inline constexpr bool operator!=(quat<T> const& l, quat<T> const& r) noexcept
{
  return !operator==(l, r);
}

// scalar part
template <typename T>
inline constexpr auto scalar(quat<T> const& x) noexcept
{
  return x.data_[3];
}

namespace detail
{

namespace quat
{

template <typename T, unsigned N, std::size_t ...Is>
constexpr inline auto scalar_vector(vxl::quat<T> const& x,
  std::index_sequence<Is...>) noexcept
{
#if defined(__clang__)
  return __builtin_shufflevector(x.data_, x.data_, (3 + Is - Is)...);
#else
  using int_vector_type = typename vector_traits<T, N>::int_vector_type;

  return __builtin_shuffle(x.data_, int_vector_type{(3 + Is - Is)...});
#endif
}

}

}

template <typename T, unsigned N>
constexpr inline auto scalar_vector(quat<T> const& x) noexcept
{
  return detail::quat::scalar_vector<T, N>(x,
    std::make_index_sequence<4>()
  );
}

// vector part
template <typename T>
constexpr inline auto vec(quat<T> const& x) noexcept
{
  return vector<T, 3>{x.data_};
}

// conjugation
template <typename T>
inline constexpr void conjugate(quat<T>& x) noexcept
{
  using int_vector_type = typename vector_traits<T, 4>::int_vector_type;
  using vector_type = typename vector_traits<T, 4>::vector_type;

  x.data_ = vector_type(
    int_vector_type(x.data_) ^
    int_vector_type(vector_type{T(-.0), T(-.0), T(-.0), T(.0)})
  );
}

template <typename T>
inline constexpr quat<T> conjugated(quat<T> const& x) noexcept
{
  using int_vector_type = typename vector_traits<T, 4>::int_vector_type;
  using vector_type = typename vector_traits<T, 4>::vector_type;

  return {
    vector_type(
      int_vector_type(x.data_) ^
      int_vector_type(vector_type{T(-.0), T(-.0), T(-.0), T(.0)})
    )
  };
}

template <typename T>
inline constexpr auto norm2(quat<T> const& x) noexcept
{
  vector<T, 4> const q{x.data_};

  return dot(q, q);
}

}

// stream operators
template <typename T>
std::ostream& operator<<(std::ostream& os, vxl::quat<T> const& v)
{
  os << '(';

  for (unsigned i{}; i != 3; ++i)
  {
    os << v.data_[i] << ", ";
  }

  return os << v.data_[3] << ')';
}

#endif // VXL_QUAT_HPP
<|endoftext|>


<commit_before>#if defined(TEMPEST_BUILD_DIRECTX12)

#include <Tempest/Log>
#include <iostream>

#include "guid.h"
#include "dxdevice.h"

#include "dxbuffer.h"
#include "dxtexture.h"
#include "dxshader.h"
#include "dxpipeline.h"
#include "builtin_shader.h"

using namespace Tempest;
using namespace Tempest::Detail;

DxDevice::DxDevice(IDXGIAdapter1& adapter, const ApiEntry& dllApi)
  :dllApi(dllApi) {
  dxAssert(dllApi.D3D12CreateDevice(&adapter, D3D_FEATURE_LEVEL_11_0, uuid<ID3D12Device>(), reinterpret_cast<void**>(&device)));

  ComPtr<ID3D12InfoQueue> pInfoQueue;
  if(SUCCEEDED(device->QueryInterface(uuid<ID3D12InfoQueue>(),reinterpret_cast<void**>(&pInfoQueue)))) {
    // Suppress messages based on their severity level
    D3D12_MESSAGE_SEVERITY severities[] = {
      D3D12_MESSAGE_SEVERITY_INFO
      };
    D3D12_MESSAGE_ID denyIds[] = {
      // I'm really not sure how to avoid this message.
      D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE,
      D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE,
      // blit shader uses no vbo
      D3D12_MESSAGE_ID_CREATEINPUTLAYOUT_EMPTY_LAYOUT,
      };
    D3D12_INFO_QUEUE_FILTER filter = {};
    filter.DenyList.NumSeverities = _countof(severities);
    filter.DenyList.pSeverityList = severities;
    filter.DenyList.NumIDs        = _countof(denyIds);
    filter.DenyList.pIDList       = denyIds;

    dxAssert(pInfoQueue->PushStorageFilter(&filter));

    pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true);
    pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
    pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, false);
    }

  DXGI_ADAPTER_DESC desc={};
  adapter.GetDesc(&desc);
  getProp(adapter,*device,props);

  D3D12_COMMAND_QUEUE_DESC queueDesc = {};
  queueDesc.Type  = D3D12_COMMAND_LIST_TYPE_DIRECT;
  queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
  dxAssert(device->CreateCommandQueue(&queueDesc, uuid<ID3D12CommandQueue>(), reinterpret_cast<void**>(&cmdQueue)));

  allocator.setDevice(*this);

  dxAssert(device->CreateFence(DxFence::Waiting, D3D12_FENCE_FLAG_NONE,
                               uuid<ID3D12Fence>(),
                               reinterpret_cast<void**>(&idleFence)));
  idleEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);

  static bool internalShaders = true;

  if(internalShaders) {
    {
    RenderState st;
    st.setZTestMode   (RenderState::ZTestMode::Always);
    st.setCullFaceMode(RenderState::CullMode::NoCull);

    auto blitVs = DSharedPtr<DxShader*>(new DxShader(blit_vert_sprv,sizeof(blit_vert_sprv)));
    auto blitFs = DSharedPtr<DxShader*>(new DxShader(blit_frag_sprv,sizeof(blit_frag_sprv)));

    DxShader* blitSh[2] = {blitVs.handler, blitFs.handler};
    blitLayout  = DSharedPtr<DxPipelineLay*>(new DxPipelineLay(*this,&blitFs.handler->lay));
    blit        = DSharedPtr<DxPipeline*>   (new DxPipeline   (*this,st,0,Triangles,*blitLayout.handler,blitSh,2));
    }

    {
    auto copyCs = DSharedPtr<DxShader*>(new DxShader(copy_comp_sprv,sizeof(copy_comp_sprv)));
    copyLayout  = DSharedPtr<DxPipelineLay*> (new DxPipelineLay(*this,&copyCs.handler->lay));
    copy        = DSharedPtr<DxCompPipeline*>(new DxCompPipeline(*this,*copyLayout.handler,*copyCs.handler));
    }
    {
    auto copyCs = DSharedPtr<DxShader*>(new DxShader(copy_s_comp_sprv,sizeof(copy_s_comp_sprv)));
    copyS       = DSharedPtr<DxCompPipeline*>(new DxCompPipeline(*this,*copyLayout.handler,*copyCs.handler));
    }
    }

  data.reset(new DataMgr(*this));
  }

DxDevice::~DxDevice() {
  blit       = DSharedPtr<DxPipeline*>();
  blitLayout = DSharedPtr<DxPipelineLay*>();
  data.reset();
  CloseHandle(idleEvent);
  }

void DxDevice::getProp(IDXGIAdapter1& adapter, ID3D12Device& dev, AbstractGraphicsApi::Props& prop) {
  DXGI_ADAPTER_DESC1 desc={};
  adapter.GetDesc1(&desc);
  return getProp(desc,dev,prop);
  }

void DxDevice::getProp(DXGI_ADAPTER_DESC1& desc, ID3D12Device& dev, AbstractGraphicsApi::Props& prop) {
  for(size_t i=0;i<sizeof(prop.name);++i)  {
    WCHAR c = desc.Description[i];
    if(c==0)
      break;
    if(('0'<=c && c<='9') || ('a'<=c && c<='z') || ('A'<=c && c<='Z') ||
       c=='(' || c==')' || c=='_' || c=='[' || c==']' || c=='{' || c=='}' || c==' ')
      prop.name[i] = char(c); else
      prop.name[i] = '?';
    }
  prop.name[sizeof(prop.name)-1]='\0';

  // https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/hardware-support-for-direct3d-12-0-formats
  // NOTE: TextureFormat::RGB32F is not supported, because of mip-maps
  static const TextureFormat smp[] = {TextureFormat::R8,   TextureFormat::RG8,   TextureFormat::RGBA8,
                                      TextureFormat::R16,  TextureFormat::RG16,  TextureFormat::RGBA16,
                                      TextureFormat::R32F, TextureFormat::RG32F, /*TextureFormat::RGB32F,*/ TextureFormat::RGBA32F,
                                      TextureFormat::DXT1, TextureFormat::DXT3,  TextureFormat::DXT5
                                     };

  static const TextureFormat att[] = {TextureFormat::R8,   TextureFormat::RG8,   TextureFormat::RGBA8,
                                      TextureFormat::R16,  TextureFormat::RG16,  TextureFormat::RGBA16,
                                      TextureFormat::R32F, TextureFormat::RG32F, TextureFormat::RGBA32F
                                     };

  static const TextureFormat sso[] = {TextureFormat::R8,   TextureFormat::RG8,   TextureFormat::RGBA8,
                                      TextureFormat::R16,  TextureFormat::RG16,  TextureFormat::RGBA16,
                                      TextureFormat::R32F, TextureFormat::RGBA32F
                                     };

  static const TextureFormat ds[]  = {TextureFormat::Depth16, TextureFormat::Depth24x8, TextureFormat::Depth24S8};

  uint64_t smpBit = 0, attBit = 0, dsBit = 0, storBit = 0;
  for(auto& i:smp)
    smpBit |= uint64_t(1) << uint64_t(i);
  for(auto& i:att)
    attBit |= uint64_t(1) << uint64_t(i);
  for(auto& i:ds) {
    dsBit  |= uint64_t(1) << uint64_t(i);
    smpBit |= uint64_t(1) << uint64_t(i);
    }
  for(auto& i:sso)
    storBit  |= uint64_t(1) << uint64_t(i);
  prop.setSamplerFormats(smpBit);
  prop.setAttachFormats (attBit);
  prop.setDepthFormats  (dsBit);
  prop.setStorageFormats(storBit);

  // TODO: buffer limits
  //prop.vbo.maxRange    = ;

  prop.ssbo.offsetAlign = 256;
  //prop.ssbo.maxRange    = size_t(prop.limits.maxStorageBufferRange);

  prop.ubo.maxRange      = D3D12_REQ_CONSTANT_BUFFER_ELEMENT_COUNT*4;
  prop.ubo.offsetAlign   = 256;

  prop.push.maxRange     = 256;

  prop.anisotropy        = true;
  prop.maxAnisotropy     = 16;
  prop.tesselationShader = false; // TODO: dxil compiller crashes

  prop.storeAndAtomicVs  = true;
  prop.storeAndAtomicFs  = true;

  prop.mrt.maxColorAttachments = D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT;

  prop.compute.maxGroups.x    = D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION;
  prop.compute.maxGroups.y    = D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION;
  prop.compute.maxGroups.z    = D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION;

  prop.compute.maxGroupSize.x = D3D12_CS_THREAD_GROUP_MAX_X;
  prop.compute.maxGroupSize.y = D3D12_CS_THREAD_GROUP_MAX_Y;
  prop.compute.maxGroupSize.z = D3D12_CS_THREAD_GROUP_MAX_Z;

  if(desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
    prop.type = DeviceType::Cpu;
    } else {
    D3D12_FEATURE_DATA_ARCHITECTURE arch = {};
    if(SUCCEEDED(dev.CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE, &arch, sizeof(arch))))
      prop.type = arch.UMA ? DeviceType::Integrated : DeviceType::Discrete;
    }

  D3D12_FEATURE_DATA_D3D12_OPTIONS5 feature5 = {};
  if(SUCCEEDED(dev.CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &feature5, sizeof(feature5)))) {
    prop.raytracing.rayQuery = (feature5.RaytracingTier != D3D12_RAYTRACING_TIER_NOT_SUPPORTED);
    //prop.raytracing.rayQuery = false; // TODO: dxil compiller
    }

  D3D12_FEATURE_DATA_D3D12_OPTIONS7 feature7 = {};
  if(SUCCEEDED(dev.CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &feature7, sizeof(feature7)))) {
    // prop.meshlets.taskShader = feature7.MeshShaderTier!=D3D12_MESH_SHADER_TIER_NOT_SUPPORTED;
    // prop.meshlets.meshShader = feature7.MeshShaderTier!=D3D12_MESH_SHADER_TIER_NOT_SUPPORTED;
    // props.meshlets.maxMeshGroups    = meshProperties.maxDrawMeshTasksCount;
    // props.meshlets.maxMeshGroupSize = meshProperties.maxMeshWorkGroupSize[0];
    }
  prop.bindless.nonUniformIndexing = true; // SM5.1
  }

void DxDevice::waitData() {
  data->wait();
  }

void Detail::DxDevice::waitIdle() {
  std::lock_guard<SpinLock> guard(syncCmdQueue);
  dxAssert(cmdQueue->Signal(idleFence.get(),DxFence::Ready));
  dxAssert(idleFence->SetEventOnCompletion(DxFence::Ready,idleEvent));
  WaitForSingleObjectEx(idleEvent, INFINITE, FALSE);
  dxAssert(idleFence->Signal(DxFence::Waiting));
  }

void DxDevice::submit(DxCommandBuffer& cmdBuffer, DxFence& sync) {
  sync.reset();

  std::lock_guard<SpinLock> guard(syncCmdQueue);
  ID3D12CommandList* cmd[] = {cmdBuffer.get()};
  cmdQueue->ExecuteCommandLists(1, cmd);
  sync.signal(*cmdQueue);
  }

#endif
<commit_msg>DX12: disable bindless for now<commit_after>#if defined(TEMPEST_BUILD_DIRECTX12)

#include <Tempest/Log>
#include <iostream>

#include "guid.h"
#include "dxdevice.h"

#include "dxbuffer.h"
#include "dxtexture.h"
#include "dxshader.h"
#include "dxpipeline.h"
#include "builtin_shader.h"

using namespace Tempest;
using namespace Tempest::Detail;

DxDevice::DxDevice(IDXGIAdapter1& adapter, const ApiEntry& dllApi)
  :dllApi(dllApi) {
  dxAssert(dllApi.D3D12CreateDevice(&adapter, D3D_FEATURE_LEVEL_11_0, uuid<ID3D12Device>(), reinterpret_cast<void**>(&device)));

  ComPtr<ID3D12InfoQueue> pInfoQueue;
  if(SUCCEEDED(device->QueryInterface(uuid<ID3D12InfoQueue>(),reinterpret_cast<void**>(&pInfoQueue)))) {
    // Suppress messages based on their severity level
    D3D12_MESSAGE_SEVERITY severities[] = {
      D3D12_MESSAGE_SEVERITY_INFO
      };
    D3D12_MESSAGE_ID denyIds[] = {
      // I'm really not sure how to avoid this message.
      D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE,
      D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE,
      // blit shader uses no vbo
      D3D12_MESSAGE_ID_CREATEINPUTLAYOUT_EMPTY_LAYOUT,
      };
    D3D12_INFO_QUEUE_FILTER filter = {};
    filter.DenyList.NumSeverities = _countof(severities);
    filter.DenyList.pSeverityList = severities;
    filter.DenyList.NumIDs        = _countof(denyIds);
    filter.DenyList.pIDList       = denyIds;

    dxAssert(pInfoQueue->PushStorageFilter(&filter));

    pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true);
    pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_CORRUPTION, true);
    pInfoQueue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_WARNING, false);
    }

  DXGI_ADAPTER_DESC desc={};
  adapter.GetDesc(&desc);
  getProp(adapter,*device,props);

  D3D12_COMMAND_QUEUE_DESC queueDesc = {};
  queueDesc.Type  = D3D12_COMMAND_LIST_TYPE_DIRECT;
  queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
  dxAssert(device->CreateCommandQueue(&queueDesc, uuid<ID3D12CommandQueue>(), reinterpret_cast<void**>(&cmdQueue)));

  allocator.setDevice(*this);

  dxAssert(device->CreateFence(DxFence::Waiting, D3D12_FENCE_FLAG_NONE,
                               uuid<ID3D12Fence>(),
                               reinterpret_cast<void**>(&idleFence)));
  idleEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);

  static bool internalShaders = true;

  if(internalShaders) {
    {
    RenderState st;
    st.setZTestMode   (RenderState::ZTestMode::Always);
    st.setCullFaceMode(RenderState::CullMode::NoCull);

    auto blitVs = DSharedPtr<DxShader*>(new DxShader(blit_vert_sprv,sizeof(blit_vert_sprv)));
    auto blitFs = DSharedPtr<DxShader*>(new DxShader(blit_frag_sprv,sizeof(blit_frag_sprv)));

    DxShader* blitSh[2] = {blitVs.handler, blitFs.handler};
    blitLayout  = DSharedPtr<DxPipelineLay*>(new DxPipelineLay(*this,&blitFs.handler->lay));
    blit        = DSharedPtr<DxPipeline*>   (new DxPipeline   (*this,st,0,Triangles,*blitLayout.handler,blitSh,2));
    }

    {
    auto copyCs = DSharedPtr<DxShader*>(new DxShader(copy_comp_sprv,sizeof(copy_comp_sprv)));
    copyLayout  = DSharedPtr<DxPipelineLay*> (new DxPipelineLay(*this,&copyCs.handler->lay));
    copy        = DSharedPtr<DxCompPipeline*>(new DxCompPipeline(*this,*copyLayout.handler,*copyCs.handler));
    }
    {
    auto copyCs = DSharedPtr<DxShader*>(new DxShader(copy_s_comp_sprv,sizeof(copy_s_comp_sprv)));
    copyS       = DSharedPtr<DxCompPipeline*>(new DxCompPipeline(*this,*copyLayout.handler,*copyCs.handler));
    }
    }

  data.reset(new DataMgr(*this));
  }

DxDevice::~DxDevice() {
  blit       = DSharedPtr<DxPipeline*>();
  blitLayout = DSharedPtr<DxPipelineLay*>();
  data.reset();
  CloseHandle(idleEvent);
  }

void DxDevice::getProp(IDXGIAdapter1& adapter, ID3D12Device& dev, AbstractGraphicsApi::Props& prop) {
  DXGI_ADAPTER_DESC1 desc={};
  adapter.GetDesc1(&desc);
  return getProp(desc,dev,prop);
  }

void DxDevice::getProp(DXGI_ADAPTER_DESC1& desc, ID3D12Device& dev, AbstractGraphicsApi::Props& prop) {
  for(size_t i=0;i<sizeof(prop.name);++i)  {
    WCHAR c = desc.Description[i];
    if(c==0)
      break;
    if(('0'<=c && c<='9') || ('a'<=c && c<='z') || ('A'<=c && c<='Z') ||
       c=='(' || c==')' || c=='_' || c=='[' || c==']' || c=='{' || c=='}' || c==' ')
      prop.name[i] = char(c); else
      prop.name[i] = '?';
    }
  prop.name[sizeof(prop.name)-1]='\0';

  // https://docs.microsoft.com/en-us/windows/win32/direct3ddxgi/hardware-support-for-direct3d-12-0-formats
  // NOTE: TextureFormat::RGB32F is not supported, because of mip-maps
  static const TextureFormat smp[] = {TextureFormat::R8,   TextureFormat::RG8,   TextureFormat::RGBA8,
                                      TextureFormat::R16,  TextureFormat::RG16,  TextureFormat::RGBA16,
                                      TextureFormat::R32F, TextureFormat::RG32F, /*TextureFormat::RGB32F,*/ TextureFormat::RGBA32F,
                                      TextureFormat::DXT1, TextureFormat::DXT3,  TextureFormat::DXT5
                                     };

  static const TextureFormat att[] = {TextureFormat::R8,   TextureFormat::RG8,   TextureFormat::RGBA8,
                                      TextureFormat::R16,  TextureFormat::RG16,  TextureFormat::RGBA16,
                                      TextureFormat::R32F, TextureFormat::RG32F, TextureFormat::RGBA32F
                                     };

  static const TextureFormat sso[] = {TextureFormat::R8,   TextureFormat::RG8,   TextureFormat::RGBA8,
                                      TextureFormat::R16,  TextureFormat::RG16,  TextureFormat::RGBA16,
                                      TextureFormat::R32F, TextureFormat::RGBA32F
                                     };

  static const TextureFormat ds[]  = {TextureFormat::Depth16, TextureFormat::Depth24x8, TextureFormat::Depth24S8};

  uint64_t smpBit = 0, attBit = 0, dsBit = 0, storBit = 0;
  for(auto& i:smp)
    smpBit |= uint64_t(1) << uint64_t(i);
  for(auto& i:att)
    attBit |= uint64_t(1) << uint64_t(i);
  for(auto& i:ds) {
    dsBit  |= uint64_t(1) << uint64_t(i);
    smpBit |= uint64_t(1) << uint64_t(i);
    }
  for(auto& i:sso)
    storBit  |= uint64_t(1) << uint64_t(i);
  prop.setSamplerFormats(smpBit);
  prop.setAttachFormats (attBit);
  prop.setDepthFormats  (dsBit);
  prop.setStorageFormats(storBit);

  // TODO: buffer limits
  //prop.vbo.maxRange    = ;

  prop.ssbo.offsetAlign = 256;
  //prop.ssbo.maxRange    = size_t(prop.limits.maxStorageBufferRange);

  prop.ubo.maxRange      = D3D12_REQ_CONSTANT_BUFFER_ELEMENT_COUNT*4;
  prop.ubo.offsetAlign   = 256;

  prop.push.maxRange     = 256;

  prop.anisotropy        = true;
  prop.maxAnisotropy     = 16;
  prop.tesselationShader = false; // TODO: dxil compiller crashes

  prop.storeAndAtomicVs  = true;
  prop.storeAndAtomicFs  = true;

  prop.mrt.maxColorAttachments = D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT;

  prop.compute.maxGroups.x    = D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION;
  prop.compute.maxGroups.y    = D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION;
  prop.compute.maxGroups.z    = D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION;

  prop.compute.maxGroupSize.x = D3D12_CS_THREAD_GROUP_MAX_X;
  prop.compute.maxGroupSize.y = D3D12_CS_THREAD_GROUP_MAX_Y;
  prop.compute.maxGroupSize.z = D3D12_CS_THREAD_GROUP_MAX_Z;

  if(desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) {
    prop.type = DeviceType::Cpu;
    } else {
    D3D12_FEATURE_DATA_ARCHITECTURE arch = {};
    if(SUCCEEDED(dev.CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE, &arch, sizeof(arch))))
      prop.type = arch.UMA ? DeviceType::Integrated : DeviceType::Discrete;
    }

  D3D12_FEATURE_DATA_D3D12_OPTIONS5 feature5 = {};
  if(SUCCEEDED(dev.CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &feature5, sizeof(feature5)))) {
    prop.raytracing.rayQuery = (feature5.RaytracingTier != D3D12_RAYTRACING_TIER_NOT_SUPPORTED);
    //prop.raytracing.rayQuery = false; // TODO: dxil compiller
    }

  D3D12_FEATURE_DATA_D3D12_OPTIONS7 feature7 = {};
  if(SUCCEEDED(dev.CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &feature7, sizeof(feature7)))) {
    // prop.meshlets.taskShader = feature7.MeshShaderTier!=D3D12_MESH_SHADER_TIER_NOT_SUPPORTED;
    // prop.meshlets.meshShader = feature7.MeshShaderTier!=D3D12_MESH_SHADER_TIER_NOT_SUPPORTED;
    // props.meshlets.maxMeshGroups    = meshProperties.maxDrawMeshTasksCount;
    // props.meshlets.maxMeshGroupSize = meshProperties.maxMeshWorkGroupSize[0];
    }
  prop.bindless.nonUniformIndexing = true;  // SM5.1
  prop.bindless.nonUniformIndexing = false; // TEST: DirectX12.Bindless2
  }

void DxDevice::waitData() {
  data->wait();
  }

void Detail::DxDevice::waitIdle() {
  std::lock_guard<SpinLock> guard(syncCmdQueue);
  dxAssert(cmdQueue->Signal(idleFence.get(),DxFence::Ready));
  dxAssert(idleFence->SetEventOnCompletion(DxFence::Ready,idleEvent));
  WaitForSingleObjectEx(idleEvent, INFINITE, FALSE);
  dxAssert(idleFence->Signal(DxFence::Waiting));
  }

void DxDevice::submit(DxCommandBuffer& cmdBuffer, DxFence& sync) {
  sync.reset();

  std::lock_guard<SpinLock> guard(syncCmdQueue);
  ID3D12CommandList* cmd[] = {cmdBuffer.get()};
  cmdQueue->ExecuteCommandLists(1, cmd);
  sync.signal(*cmdQueue);
  }

#endif
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/test/ui/ui_test.h"

#include "build/build_config.h"

class LocaleTestsBase : public UITest {
 public:
  LocaleTestsBase() : UITest(), old_lc_all_(NULL) {
  }

 protected:
  void RestoreLcAllEnvironment() {
#if defined(OS_LINUX)
    if (old_lc_all_) {
      setenv("LC_ALL", old_lc_all_, 1);
    } else {
      unsetenv("LC_ALL");
    }
#endif
  };

  const char* old_lc_all_;
};

class LocaleTestsDa : public LocaleTestsBase {
 public:
  LocaleTestsDa() : LocaleTestsBase() {
    launch_arguments_.AppendSwitchWithValue("lang", "da");

    // Linux doesn't use --lang, it only uses environment variables to set the
    // language.
#if defined(OS_LINUX)
    old_lc_all_ = getenv("LC_ALL");
    setenv("LC_ALL", "da_DK.UTF-8", 1);
#endif
  }
};

class LocaleTestsHe : public LocaleTestsBase {
 public:
  LocaleTestsHe() : LocaleTestsBase() {
    launch_arguments_.AppendSwitchWithValue("lang", "he");
#if defined(OS_LINUX)
    old_lc_all_ = getenv("LC_ALL");
    setenv("LC_ALL", "he_IL.UTF-8", 1);
#endif
  }
};

class LocaleTestsZhTw : public LocaleTestsBase {
 public:
  LocaleTestsZhTw() : LocaleTestsBase() {
    launch_arguments_.AppendSwitchWithValue("lang", "zh-TW");
#if defined(OS_LINUX)
    old_lc_all_ = getenv("LC_ALL");
    setenv("LC_ALL", "zh_TW.UTF-8", 1);
#endif
  }
};

#if defined(OS_WIN) || defined(OS_LINUX)
// These 3 tests started failing between revisions 13115 and 13120.
// See bug 9758.
TEST_F(LocaleTestsDa, TestStart) {
  // Just making sure we can start/shutdown cleanly.
  RestoreLcAllEnvironment();
}

TEST_F(LocaleTestsHe, TestStart) {
  // Just making sure we can start/shutdown cleanly.
  RestoreLcAllEnvironment();
}

TEST_F(LocaleTestsZhTw, TestStart) {
  // Just making sure we can start/shutdown cleanly.
  RestoreLcAllEnvironment();
}
#endif
<commit_msg>Re-enable locale tests on Mac. They work fine now.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/test/ui/ui_test.h"

#include "build/build_config.h"

class LocaleTestsBase : public UITest {
 public:
  LocaleTestsBase() : UITest(), old_lc_all_(NULL) {
  }

 protected:
  void RestoreLcAllEnvironment() {
#if defined(OS_LINUX)
    if (old_lc_all_) {
      setenv("LC_ALL", old_lc_all_, 1);
    } else {
      unsetenv("LC_ALL");
    }
#endif
  };

  const char* old_lc_all_;
};

class LocaleTestsDa : public LocaleTestsBase {
 public:
  LocaleTestsDa() : LocaleTestsBase() {
    launch_arguments_.AppendSwitchWithValue("lang", "da");

    // Linux doesn't use --lang, it only uses environment variables to set the
    // language.
#if defined(OS_LINUX)
    old_lc_all_ = getenv("LC_ALL");
    setenv("LC_ALL", "da_DK.UTF-8", 1);
#endif
  }
};

class LocaleTestsHe : public LocaleTestsBase {
 public:
  LocaleTestsHe() : LocaleTestsBase() {
    launch_arguments_.AppendSwitchWithValue("lang", "he");
#if defined(OS_LINUX)
    old_lc_all_ = getenv("LC_ALL");
    setenv("LC_ALL", "he_IL.UTF-8", 1);
#endif
  }
};

class LocaleTestsZhTw : public LocaleTestsBase {
 public:
  LocaleTestsZhTw() : LocaleTestsBase() {
    launch_arguments_.AppendSwitchWithValue("lang", "zh-TW");
#if defined(OS_LINUX)
    old_lc_all_ = getenv("LC_ALL");
    setenv("LC_ALL", "zh_TW.UTF-8", 1);
#endif
  }
};

TEST_F(LocaleTestsDa, TestStart) {
  // Just making sure we can start/shutdown cleanly.
  RestoreLcAllEnvironment();
}

TEST_F(LocaleTestsHe, TestStart) {
  // Just making sure we can start/shutdown cleanly.
  RestoreLcAllEnvironment();
}

TEST_F(LocaleTestsZhTw, TestStart) {
  // Just making sure we can start/shutdown cleanly.
  RestoreLcAllEnvironment();
}
<|endoftext|>


<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/low_memory_observer.h"

#include <fcntl.h>

#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/time.h"
#include "base/timer.h"
#include "chrome/browser/oom_priority_manager.h"
#include "content/public/browser/browser_thread.h"
#include "chrome/browser/browser_process.h"

using content::BrowserThread;

namespace browser {

namespace {
// This is the file that will exist if low memory notification is available
// on the device.  Whenever it becomes readable, it signals a low memory
// condition.
const char kLowMemFile[] = "/dev/chromeos-low-mem";

// This is the minimum amount of time in milliseconds between checks for
// low memory.
const int kLowMemoryCheckTimeoutMs = 750;
}  // namespace

////////////////////////////////////////////////////////////////////////////////
// LowMemoryObserverImpl
//
// Does the actual work of observing.  The observation work happens on the FILE
// thread, and the discarding of tabs happens on the UI thread.
// If low memory is detected, then we discard a tab, wait
// kLowMemoryCheckTimeoutMs milliseconds and then start watching again to see
// if we're still in a low memory state.  This is to keep from discarding all
// tabs the first time we enter the state, because it takes time for the
// tabs to deallocate their memory.  A timer isn't the perfect solution, but
// without any reliable indicator that a tab has had all its parts deallocated,
// it's the next best thing.
class LowMemoryObserverImpl
    : public base::RefCountedThreadSafe<LowMemoryObserverImpl> {
 public:
  LowMemoryObserverImpl() : watcher_delegate_(this), file_descriptor_(-1) {}
  ~LowMemoryObserverImpl() {
    StopObservingOnFileThread();
  }

  // Start watching the low memory file for readability.
  // Calls to StartObserving should always be matched with calls to
  // StopObserving.  This method should only be called from the FILE thread.
  void StartObservingOnFileThread();

  // Stop watching the low memory file for readability.
  // May be safely called if StartObserving has not been called.
  // This method should only be called from the FILE thread.
  void StopObservingOnFileThread();

 private:
  // Start a timer to resume watching the low memory file descriptor.
  void ScheduleNextObservation();

  // Actually start watching the file descriptor.
  void StartWatchingDescriptor();

  // Delegate to receive events from WatchFileDescriptor.
  class FileWatcherDelegate : public MessageLoopForIO::Watcher {
   public:
    explicit FileWatcherDelegate(LowMemoryObserverImpl* owner)
        : owner_(owner) {}
    virtual ~FileWatcherDelegate() {}

    // Overrides for MessageLoopForIO::Watcher
    virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {}
    virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {
      LOG(WARNING) << "Low memory condition detected.  Discarding a tab.";
      // We can only discard tabs on the UI thread.
      base::Callback<void(void)> callback = base::Bind(&DiscardTab);
      BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
      owner_->ScheduleNextObservation();
    }

    // Sends off a discard request to the OomPriorityManager.  Must be run on
    // the UI thread.
    static void DiscardTab() {
      CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
      if (g_browser_process && g_browser_process->oom_priority_manager())
        g_browser_process->oom_priority_manager()->DiscardTab();
    }
   private:
    LowMemoryObserverImpl* owner_;
    DISALLOW_COPY_AND_ASSIGN(FileWatcherDelegate);
  };

  scoped_ptr<MessageLoopForIO::FileDescriptorWatcher> watcher_;
  FileWatcherDelegate watcher_delegate_;
  int file_descriptor_;
  base::OneShotTimer<LowMemoryObserverImpl> timer_;

  DISALLOW_COPY_AND_ASSIGN(LowMemoryObserverImpl);
};

void LowMemoryObserverImpl::StartObservingOnFileThread() {
  DCHECK_LE(file_descriptor_, 0)
      << "Attempted to start observation when it was already started.";
  DCHECK(watcher_.get() == NULL);
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  DCHECK(MessageLoopForIO::current());

  file_descriptor_ = ::open(kLowMemFile, O_RDONLY);
  if (file_descriptor_ < 0) {
    PLOG(ERROR) << "Unable to open " << kLowMemFile;
    return;
  }
  watcher_.reset(new MessageLoopForIO::FileDescriptorWatcher);
  StartWatchingDescriptor();
}

void LowMemoryObserverImpl::StopObservingOnFileThread() {
  // If StartObserving failed, StopObserving will still get called.
  timer_.Stop();
  if (file_descriptor_ >= 0) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    watcher_.reset(NULL);
    ::close(file_descriptor_);
    file_descriptor_ = -1;
  }
}

void LowMemoryObserverImpl::ScheduleNextObservation() {
   timer_.Start(FROM_HERE,
                base::TimeDelta::FromMilliseconds(kLowMemoryCheckTimeoutMs),
                this,
                &LowMemoryObserverImpl::StartWatchingDescriptor);
}

void LowMemoryObserverImpl::StartWatchingDescriptor() {
  DCHECK(watcher_.get());
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  DCHECK(MessageLoopForIO::current());
  if (file_descriptor_ < 0)
    return;
  if (!MessageLoopForIO::current()->WatchFileDescriptor(
          file_descriptor_,
          false,  // persistent=false: We want it to fire once and reschedule.
          MessageLoopForIO::WATCH_READ,
          watcher_.get(),
          &watcher_delegate_)) {
    LOG(ERROR) << "Unable to watch " << kLowMemFile;
  }
}

////////////////////////////////////////////////////////////////////////////////
// LowMemoryObserver

LowMemoryObserver::LowMemoryObserver() : observer_(new LowMemoryObserverImpl) {}

LowMemoryObserver::~LowMemoryObserver() { Stop(); }

void LowMemoryObserver::Start() {
  BrowserThread::PostTask(
      BrowserThread::FILE,
      FROM_HERE,
      base::Bind(&LowMemoryObserverImpl::StartObservingOnFileThread,
                 observer_.get()));
}

void LowMemoryObserver::Stop() {
  BrowserThread::PostTask(
      BrowserThread::FILE,
      FROM_HERE,
      base::Bind(&LowMemoryObserverImpl::StopObservingOnFileThread,
                 observer_.get()));
}

}  // namespace browser
<commit_msg>Removing some log spam from the low memory observer.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/low_memory_observer.h"

#include <fcntl.h>

#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/time.h"
#include "base/timer.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/system/runtime_environment.h"
#include "chrome/browser/oom_priority_manager.h"
#include "content/public/browser/browser_thread.h"

using content::BrowserThread;

namespace browser {

namespace {
// This is the file that will exist if low memory notification is available
// on the device.  Whenever it becomes readable, it signals a low memory
// condition.
const char kLowMemFile[] = "/dev/chromeos-low-mem";

// This is the minimum amount of time in milliseconds between checks for
// low memory.
const int kLowMemoryCheckTimeoutMs = 750;
}  // namespace

////////////////////////////////////////////////////////////////////////////////
// LowMemoryObserverImpl
//
// Does the actual work of observing.  The observation work happens on the FILE
// thread, and the discarding of tabs happens on the UI thread.
// If low memory is detected, then we discard a tab, wait
// kLowMemoryCheckTimeoutMs milliseconds and then start watching again to see
// if we're still in a low memory state.  This is to keep from discarding all
// tabs the first time we enter the state, because it takes time for the
// tabs to deallocate their memory.  A timer isn't the perfect solution, but
// without any reliable indicator that a tab has had all its parts deallocated,
// it's the next best thing.
class LowMemoryObserverImpl
    : public base::RefCountedThreadSafe<LowMemoryObserverImpl> {
 public:
  LowMemoryObserverImpl() : watcher_delegate_(this), file_descriptor_(-1) {}
  ~LowMemoryObserverImpl() {
    StopObservingOnFileThread();
  }

  // Start watching the low memory file for readability.
  // Calls to StartObserving should always be matched with calls to
  // StopObserving.  This method should only be called from the FILE thread.
  void StartObservingOnFileThread();

  // Stop watching the low memory file for readability.
  // May be safely called if StartObserving has not been called.
  // This method should only be called from the FILE thread.
  void StopObservingOnFileThread();

 private:
  // Start a timer to resume watching the low memory file descriptor.
  void ScheduleNextObservation();

  // Actually start watching the file descriptor.
  void StartWatchingDescriptor();

  // Delegate to receive events from WatchFileDescriptor.
  class FileWatcherDelegate : public MessageLoopForIO::Watcher {
   public:
    explicit FileWatcherDelegate(LowMemoryObserverImpl* owner)
        : owner_(owner) {}
    virtual ~FileWatcherDelegate() {}

    // Overrides for MessageLoopForIO::Watcher
    virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {}
    virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {
      LOG(WARNING) << "Low memory condition detected.  Discarding a tab.";
      // We can only discard tabs on the UI thread.
      base::Callback<void(void)> callback = base::Bind(&DiscardTab);
      BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
      owner_->ScheduleNextObservation();
    }

    // Sends off a discard request to the OomPriorityManager.  Must be run on
    // the UI thread.
    static void DiscardTab() {
      CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
      if (g_browser_process && g_browser_process->oom_priority_manager())
        g_browser_process->oom_priority_manager()->DiscardTab();
    }
   private:
    LowMemoryObserverImpl* owner_;
    DISALLOW_COPY_AND_ASSIGN(FileWatcherDelegate);
  };

  scoped_ptr<MessageLoopForIO::FileDescriptorWatcher> watcher_;
  FileWatcherDelegate watcher_delegate_;
  int file_descriptor_;
  base::OneShotTimer<LowMemoryObserverImpl> timer_;

  DISALLOW_COPY_AND_ASSIGN(LowMemoryObserverImpl);
};

void LowMemoryObserverImpl::StartObservingOnFileThread() {
  DCHECK_LE(file_descriptor_, 0)
      << "Attempted to start observation when it was already started.";
  DCHECK(watcher_.get() == NULL);
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  DCHECK(MessageLoopForIO::current());

  file_descriptor_ = ::open(kLowMemFile, O_RDONLY);
  // Don't report this error unless we're really running on ChromeOS
  // to avoid testing spam.
  if (file_descriptor_ < 0 &&
      chromeos::system::runtime_environment::IsRunningOnChromeOS()) {
    PLOG(ERROR) << "Unable to open " << kLowMemFile;
    return;
  }
  watcher_.reset(new MessageLoopForIO::FileDescriptorWatcher);
  StartWatchingDescriptor();
}

void LowMemoryObserverImpl::StopObservingOnFileThread() {
  // If StartObserving failed, StopObserving will still get called.
  timer_.Stop();
  if (file_descriptor_ >= 0) {
    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    watcher_.reset(NULL);
    ::close(file_descriptor_);
    file_descriptor_ = -1;
  }
}

void LowMemoryObserverImpl::ScheduleNextObservation() {
   timer_.Start(FROM_HERE,
                base::TimeDelta::FromMilliseconds(kLowMemoryCheckTimeoutMs),
                this,
                &LowMemoryObserverImpl::StartWatchingDescriptor);
}

void LowMemoryObserverImpl::StartWatchingDescriptor() {
  DCHECK(watcher_.get());
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
  DCHECK(MessageLoopForIO::current());
  if (file_descriptor_ < 0)
    return;
  if (!MessageLoopForIO::current()->WatchFileDescriptor(
          file_descriptor_,
          false,  // persistent=false: We want it to fire once and reschedule.
          MessageLoopForIO::WATCH_READ,
          watcher_.get(),
          &watcher_delegate_)) {
    LOG(ERROR) << "Unable to watch " << kLowMemFile;
  }
}

////////////////////////////////////////////////////////////////////////////////
// LowMemoryObserver

LowMemoryObserver::LowMemoryObserver() : observer_(new LowMemoryObserverImpl) {}

LowMemoryObserver::~LowMemoryObserver() { Stop(); }

void LowMemoryObserver::Start() {
  BrowserThread::PostTask(
      BrowserThread::FILE,
      FROM_HERE,
      base::Bind(&LowMemoryObserverImpl::StartObservingOnFileThread,
                 observer_.get()));
}

void LowMemoryObserver::Stop() {
  BrowserThread::PostTask(
      BrowserThread::FILE,
      FROM_HERE,
      base::Bind(&LowMemoryObserverImpl::StopObservingOnFileThread,
                 observer_.get()));
}

}  // namespace browser
<|endoftext|>