# lots of housekeeping/fixes
premake: document extra_params ogl: remove EXT_ symbols, use glext instead adts: move cache into separate file (also remove old cache impl) move SDL files in sysdep/win to libraries/SDL ScopeTimerAccrue: change #ifdef spaghetti into templated policy class app_hooks, define VOID_FUNC to FUNC(.., (void) ) look at stalk walk code; any reason not to work on Win64? replace ERR_TEX_CODEC_CANNOT_HANDLE with INFO_* - not an error. also ERR_SYM_SUPPRESS_OUTPUT ERR_SYM_SINGLE_SYMBOL_LIMIT wdbg_sym: only import RtlCaptureContext once (not every walk_stack) add sys_last_error (!GetLastError on win32); converts to text in display_error, also show sys_last_error and errno app_hooks: move i18n impl out (belongs in pyrogenesis) fixes to string_s test. refs #124 This was SVN commit r4020.
This commit is contained in:
parent
2f065990f6
commit
7cb82ada2f
@ -109,6 +109,11 @@ function package_create(package_name, target_type)
|
||||
end
|
||||
|
||||
|
||||
-- extra_params: table including zero or more of the following:
|
||||
-- * no_default_pch: (any type) prevents adding the PCH include dir.
|
||||
-- see setup_static_lib_package() for explanation of this scheme and rationale.
|
||||
-- * extra_files: table of filenames (relative to source_root) to add to project
|
||||
-- * extra_links: table of library names to add to link step
|
||||
function package_add_contents(source_root, rel_source_dirs, rel_include_dirs, extra_params)
|
||||
|
||||
-- We don't want the VC project to be deeply nested (once for each
|
||||
@ -161,10 +166,12 @@ end
|
||||
-- more to the point, it is necessary to efficiently support a separate
|
||||
-- test executable that also includes much of the game code.
|
||||
|
||||
-- names of all static libs created. added to the main app project later
|
||||
-- (see explanation at end of this file)
|
||||
-- names of all static libs created. automatically added to the
|
||||
-- main app project later (see explanation at end of this file)
|
||||
static_lib_names = {}
|
||||
|
||||
-- set up one of the static libraries into which the main engine code is split.
|
||||
-- extra_params: see package_add_contents().
|
||||
-- note: rel_source_dirs and rel_include_dirs are relative to global source_root.
|
||||
local function setup_static_lib_package (package_name, rel_source_dirs, extern_libs, extra_params)
|
||||
|
||||
@ -223,6 +230,7 @@ function setup_all_libs ()
|
||||
}
|
||||
extern_libs = {
|
||||
"spidermonkey",
|
||||
"sdl", -- key definitions
|
||||
"xerces",
|
||||
"opengl",
|
||||
"zlib",
|
||||
@ -238,6 +246,7 @@ function setup_all_libs ()
|
||||
}
|
||||
extern_libs = {
|
||||
"opengl",
|
||||
"sdl", -- key definitions
|
||||
"spidermonkey", -- for graphics/scripting
|
||||
"boost"
|
||||
}
|
||||
@ -262,6 +271,7 @@ function setup_all_libs ()
|
||||
}
|
||||
extern_libs = {
|
||||
"boost",
|
||||
"sdl", -- key definitions
|
||||
"opengl",
|
||||
"spidermonkey"
|
||||
}
|
||||
@ -274,6 +284,7 @@ function setup_all_libs ()
|
||||
}
|
||||
extern_libs = {
|
||||
"spidermonkey",
|
||||
"sdl", -- key definitions
|
||||
"opengl",
|
||||
"boost"
|
||||
}
|
||||
@ -289,6 +300,7 @@ function setup_all_libs ()
|
||||
"lib/res/sound"
|
||||
}
|
||||
extern_libs = {
|
||||
"sdl",
|
||||
"opengl",
|
||||
"libpng",
|
||||
"zlib",
|
||||
@ -406,15 +418,17 @@ end
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- setup a typical Atlas component package
|
||||
local function setup_atlas_package(package_name, target_type, rel_source_dirs, rel_include_dirs, extern_libs, flags)
|
||||
-- extra_params: as in package_add_contents; also zero or more of the following:
|
||||
-- * pch: (any type) set stdafx.h and .cpp as PCH
|
||||
local function setup_atlas_package(package_name, target_type, rel_source_dirs, rel_include_dirs, extern_libs, extra_params)
|
||||
|
||||
local source_root = "../../../source/tools/atlas/" .. package_name .. "/"
|
||||
package_create(package_name, target_type)
|
||||
|
||||
-- Don't add the default 'sourceroot/pch/projectname' for finding PCH files
|
||||
flags["no_default_pch"] = 1
|
||||
extra_params["no_default_pch"] = 1
|
||||
|
||||
package_add_contents(source_root, rel_source_dirs, rel_include_dirs, flags)
|
||||
package_add_contents(source_root, rel_source_dirs, rel_include_dirs, extra_params)
|
||||
package_add_extern_libs(extern_libs)
|
||||
|
||||
-- Platform Specifics
|
||||
@ -428,15 +442,11 @@ local function setup_atlas_package(package_name, target_type, rel_source_dirs, r
|
||||
-- required to use WinMain() on Windows, otherwise will default to main()
|
||||
tinsert(package.buildflags, "no-main")
|
||||
|
||||
if flags["pch"] then
|
||||
if extra_params["pch"] then
|
||||
package.pchheader = "stdafx.h"
|
||||
package.pchsource = "stdafx.cpp"
|
||||
end
|
||||
|
||||
if flags["depends"] then
|
||||
listconcat(package.links, flags["depends"])
|
||||
end
|
||||
|
||||
else -- Non-Windows, = Unix
|
||||
-- TODO
|
||||
end
|
||||
@ -453,7 +463,7 @@ function setup_atlas_packages()
|
||||
},{ -- include
|
||||
},{ -- extern_libs
|
||||
"xerces"
|
||||
},{ -- flags
|
||||
},{ -- extra_params
|
||||
})
|
||||
|
||||
setup_atlas_package("AtlasUI", "dll",
|
||||
@ -488,9 +498,9 @@ function setup_atlas_packages()
|
||||
"devil",
|
||||
"xerces",
|
||||
"wxwidgets"
|
||||
},{ -- flags
|
||||
},{ -- extra_params
|
||||
pch = 1,
|
||||
depends = { "AtlasObject", "DatafileIO" },
|
||||
extra_links = { "AtlasObject", "DatafileIO" },
|
||||
extra_files = { "Misc/atlas.rc" }
|
||||
})
|
||||
|
||||
@ -507,7 +517,7 @@ function setup_atlas_packages()
|
||||
"devil",
|
||||
"xerces",
|
||||
"zlib"
|
||||
},{ -- flags
|
||||
},{ -- extra_params
|
||||
pch = 1,
|
||||
})
|
||||
|
||||
|
@ -1,25 +0,0 @@
|
||||
/**
|
||||
* =========================================================================
|
||||
* File : adts.cpp
|
||||
* Project : 0 A.D.
|
||||
* Description : useful Abstract Data Types not provided by STL.
|
||||
*
|
||||
* @author Jan.Wassenberg@stud.uni-karlsruhe.de
|
||||
* =========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005 Jan Wassenberg
|
||||
*
|
||||
* Redistribution and/or modification are also permitted under the
|
||||
* terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation (version 2 or later, at your option).
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "adts.h"
|
@ -23,15 +23,9 @@
|
||||
#ifndef ADTS_H__
|
||||
#define ADTS_H__
|
||||
|
||||
#include "lib.h"
|
||||
|
||||
#include <cfloat>
|
||||
|
||||
#pragma warning(push, 3) // VC7.1 STL not /W4 clean :(
|
||||
#include <list>
|
||||
#include <map>
|
||||
#pragma warning(pop)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// dynamic (grow-able) hash table
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template<typename Key, typename T> class DHT_Traits
|
||||
{
|
||||
@ -61,7 +55,6 @@ public:
|
||||
};
|
||||
|
||||
|
||||
|
||||
// intended for pointer types
|
||||
template<typename Key, typename T, typename Traits=DHT_Traits<Key,T> >
|
||||
class DynHashTbl
|
||||
@ -241,712 +234,8 @@ public:
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
Cache for items of variable size and value/"cost".
|
||||
underlying displacement algorithm is pluggable; default is "Landlord".
|
||||
|
||||
template reference:
|
||||
Entry provides size, cost, credit and credit_density().
|
||||
rationale:
|
||||
- made a template instead of exposing Cache::Entry because
|
||||
that would drag a lot of stuff out of Cache.
|
||||
- calculates its own density since that entails a Divider functor,
|
||||
which requires storage inside Entry.
|
||||
Entries is a collection with iterator and begin()/end() and
|
||||
"static Entry& entry_from_it(iterator)".
|
||||
rationale:
|
||||
- STL map has pair<key, item> as its value_type, so this
|
||||
function would return it->second. however, we want to support
|
||||
other container types (where we'd just return *it).
|
||||
Manager is a template parameterized on typename Key and class Entry.
|
||||
its interface is as follows:
|
||||
|
||||
// is the cache empty?
|
||||
bool empty() const;
|
||||
|
||||
// add (key, entry) to cache.
|
||||
void add(Key key, const Entry& entry);
|
||||
|
||||
// if the entry identified by <key> is not in cache, return false;
|
||||
// otherwise return true and pass back a pointer to it.
|
||||
bool find(Key key, const Entry** pentry) const;
|
||||
|
||||
// remove an entry from cache, which is assumed to exist!
|
||||
// this makes sense because callers will typically first use find() to
|
||||
// return info about the entry; this also checks if present.
|
||||
void remove(Key key);
|
||||
|
||||
// mark <entry> as just accessed for purpose of cache management.
|
||||
// it will tend to be kept in cache longer.
|
||||
void on_access(Entry& entry);
|
||||
|
||||
// caller's intent is to remove the least valuable entry.
|
||||
// in implementing this, you have the latitude to "shake loose"
|
||||
// several entries (e.g. because their 'value' is equal).
|
||||
// they must all be push_back-ed into the list; Cache will dole
|
||||
// them out one at a time in FIFO order to callers.
|
||||
//
|
||||
// rationale:
|
||||
// - it is necessary for callers to receive a copy of the
|
||||
// Entry being evicted - e.g. file_cache owns its items and
|
||||
// they must be passed back to allocator when evicted.
|
||||
// - e.g. Landlord can potentially see several entries become
|
||||
// evictable in one call to remove_least_valuable. there are
|
||||
// several ways to deal with this:
|
||||
// 1) generator interface: we return one of { empty, nevermind,
|
||||
// removed, remove-and-call-again }. this greatly complicates
|
||||
// the call site.
|
||||
// 2) return immediately after finding an item to evict.
|
||||
// this changes cache behavior - entries stored at the
|
||||
// beginning would be charged more often (unfair).
|
||||
// resuming charging at the next entry doesn't work - this
|
||||
// would have to be flushed when adding, at which time there
|
||||
// is no provision for returning any items that may be evicted.
|
||||
// 3) return list of all entries "shaken loose". this incurs
|
||||
// frequent mem allocs, which can be alleviated via suballocator.
|
||||
// note: an intrusive linked-list doesn't make sense because
|
||||
// entries to be returned need to be copied anyway (they are
|
||||
// removed from the manager's storage).
|
||||
void remove_least_valuable(std::list<Entry>& entry_list)
|
||||
*/
|
||||
|
||||
|
||||
//
|
||||
// functors to calculate minimum credit density (MCD)
|
||||
//
|
||||
|
||||
// MCD is required for the Landlord algorithm's evict logic.
|
||||
// [Young02] calls it '\delta'.
|
||||
|
||||
// scan over all entries and return MCD.
|
||||
template<class Entries> float ll_calc_min_credit_density(const Entries& entries)
|
||||
{
|
||||
float min_credit_density = FLT_MAX;
|
||||
for(typename Entries::const_iterator it = entries.begin(); it != entries.end(); ++it)
|
||||
{
|
||||
const float credit_density = Entries::entry_from_it(it).credit_density();
|
||||
min_credit_density = fminf(min_credit_density, credit_density);
|
||||
}
|
||||
return min_credit_density;
|
||||
}
|
||||
|
||||
// note: no warning is given that the MCD entry is being removed!
|
||||
// (reduces overhead in remove_least_valuable)
|
||||
// these functors must account for that themselves (e.g. by resetting
|
||||
// their state directly after returning MCD).
|
||||
|
||||
// determine MCD by scanning over all entries.
|
||||
// tradeoff: O(N) time complexity, but all notify* calls are no-ops.
|
||||
template<class Entry, class Entries>
|
||||
class McdCalc_Naive
|
||||
{
|
||||
public:
|
||||
void notify_added(const Entry&) const {}
|
||||
void notify_decreased(const Entry&) const {}
|
||||
void notify_impending_increase_or_remove(const Entry&) const {}
|
||||
void notify_increased_or_removed(const Entry&) const {}
|
||||
float operator()(const Entries& entries) const
|
||||
{
|
||||
const float mcd = ll_calc_min_credit_density(entries);
|
||||
return mcd;
|
||||
}
|
||||
};
|
||||
|
||||
// cache previous MCD and update it incrementally (when possible).
|
||||
// tradeoff: amortized O(1) time complexity, but notify* calls must
|
||||
// perform work whenever something in the cache changes.
|
||||
template<class Entry, class Entries>
|
||||
class McdCalc_Cached
|
||||
{
|
||||
public:
|
||||
McdCalc_Cached() : min_credit_density(FLT_MAX), min_valid(false) {}
|
||||
|
||||
void notify_added(const Entry& entry)
|
||||
{
|
||||
// when adding a new item, the minimum credit density can only
|
||||
// decrease or remain the same; acting as if entry's credit had
|
||||
// been decreased covers both cases.
|
||||
notify_decreased(entry);
|
||||
}
|
||||
|
||||
void notify_decreased(const Entry& entry)
|
||||
{
|
||||
min_credit_density = MIN(min_credit_density, entry.credit_density());
|
||||
}
|
||||
|
||||
void notify_impending_increase_or_remove(const Entry& entry)
|
||||
{
|
||||
// remember if this entry had the smallest density
|
||||
is_min_entry = feq(min_credit_density, entry.credit_density());
|
||||
}
|
||||
|
||||
void notify_increased_or_removed(const Entry& UNUSED(entry))
|
||||
{
|
||||
// .. it did and was increased or removed. we must invalidate
|
||||
// MCD and recalculate it next time.
|
||||
if(is_min_entry)
|
||||
{
|
||||
min_valid = false;
|
||||
min_credit_density = -1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
float operator()(const Entries& entries)
|
||||
{
|
||||
if(min_valid)
|
||||
{
|
||||
// the entry that has MCD will be removed anyway by caller;
|
||||
// we need to invalidate here because they don't call
|
||||
// notify_increased_or_removed.
|
||||
min_valid = false;
|
||||
return min_credit_density;
|
||||
}
|
||||
|
||||
// this is somewhat counterintuitive. since we're calculating
|
||||
// MCD directly, why not mark our cached version of it valid
|
||||
// afterwards? reason is that our caller will remove the entry with
|
||||
// MCD, so it'll be invalidated anyway.
|
||||
// instead, our intent is to calculate MCD for the *next time*.
|
||||
const float ret = ll_calc_min_credit_density(entries);
|
||||
min_valid = true;
|
||||
min_credit_density = FLT_MAX;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
float min_credit_density;
|
||||
bool min_valid;
|
||||
|
||||
// temporary flag set by notify_impending_increase_or_remove
|
||||
bool is_min_entry;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Landlord cache management policy: see [Young02].
|
||||
//
|
||||
|
||||
// in short, each entry has credit initially set to cost. when wanting to
|
||||
// remove an item, all are charged according to MCD and their size;
|
||||
// entries are evicted if their credit is exhausted. accessing an entry
|
||||
// restores "some" of its credit.
|
||||
template<typename Key, typename Entry, template<class Entry, class Entries> class McdCalc = McdCalc_Cached>
|
||||
class Landlord
|
||||
{
|
||||
public:
|
||||
bool empty() const
|
||||
{
|
||||
return map.empty();
|
||||
}
|
||||
|
||||
void add(Key key, const Entry& entry)
|
||||
{
|
||||
// adapter for add_ (which returns an iterator)
|
||||
(void)add_(key, entry);
|
||||
}
|
||||
|
||||
bool find(Key key, const Entry** pentry) const
|
||||
{
|
||||
MapCIt it = map.find(key);
|
||||
if(it == map.end())
|
||||
return false;
|
||||
*pentry = &it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
void remove(Key key)
|
||||
{
|
||||
MapIt it = map.find(key);
|
||||
debug_assert(it != map.end());
|
||||
remove_(it);
|
||||
}
|
||||
|
||||
void on_access(Entry& entry)
|
||||
{
|
||||
mcd_calc.notify_impending_increase_or_remove(entry);
|
||||
|
||||
// Landlord algorithm calls for credit to be reset to anything
|
||||
// between its current value and the cost.
|
||||
const float gain = 0.75f; // restore most credit
|
||||
entry.credit = gain*entry.cost + (1.0f-gain)*entry.credit;
|
||||
|
||||
mcd_calc.notify_increased_or_removed(entry);
|
||||
}
|
||||
|
||||
void remove_least_valuable(std::list<Entry>& entry_list)
|
||||
{
|
||||
// we are required to evict at least one entry. one iteration
|
||||
// ought to suffice, due to definition of min_credit_density and
|
||||
// tolerance; however, we provide for repeating if necessary.
|
||||
again:
|
||||
|
||||
// messing with this (e.g. raising if tiny) would result in
|
||||
// different evictions than Landlord_Lazy, which is unacceptable.
|
||||
// nor is doing so necessary: if mcd is tiny, so is credit.
|
||||
const float min_credit_density = mcd_calc(map);
|
||||
debug_assert(min_credit_density > 0.0f);
|
||||
|
||||
for(MapIt it = map.begin(); it != map.end();) // no ++it
|
||||
{
|
||||
Entry& entry = it->second;
|
||||
|
||||
charge(entry, min_credit_density);
|
||||
if(should_evict(entry))
|
||||
{
|
||||
entry_list.push_back(entry);
|
||||
|
||||
// annoying: we have to increment <it> before erasing
|
||||
MapIt it_to_remove = it++;
|
||||
map.erase(it_to_remove);
|
||||
}
|
||||
else
|
||||
{
|
||||
mcd_calc.notify_decreased(entry);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if(entry_list.empty())
|
||||
goto again;
|
||||
}
|
||||
|
||||
protected:
|
||||
// note: use hash_map instead of map for better locality
|
||||
// (relevant when iterating over all items in remove_least_valuable)
|
||||
class Map : public STL_HASH_MAP<Key, Entry>
|
||||
{
|
||||
public:
|
||||
static Entry& entry_from_it(typename Map::iterator it) { return it->second; }
|
||||
static const Entry& entry_from_it(typename Map::const_iterator it) { return it->second; }
|
||||
};
|
||||
typedef typename Map::iterator MapIt;
|
||||
typedef typename Map::const_iterator MapCIt;
|
||||
Map map;
|
||||
|
||||
// add entry and return iterator pointing to it.
|
||||
MapIt add_(Key key, const Entry& entry)
|
||||
{
|
||||
typedef std::pair<MapIt, bool> PairIB;
|
||||
typename Map::value_type val = std::make_pair(key, entry);
|
||||
PairIB ret = map.insert(val);
|
||||
debug_assert(ret.second); // must not already be in map
|
||||
|
||||
mcd_calc.notify_added(entry);
|
||||
|
||||
return ret.first;
|
||||
}
|
||||
|
||||
// remove entry (given by iterator) directly.
|
||||
void remove_(MapIt it)
|
||||
{
|
||||
const Entry& entry = it->second;
|
||||
mcd_calc.notify_impending_increase_or_remove(entry);
|
||||
mcd_calc.notify_increased_or_removed(entry);
|
||||
map.erase(it);
|
||||
}
|
||||
|
||||
void charge(Entry& entry, float delta)
|
||||
{
|
||||
entry.credit -= delta * entry.size;
|
||||
|
||||
// don't worry about entry.size being 0 - if so, cost
|
||||
// should also be 0, so credit will already be 0 anyway.
|
||||
}
|
||||
|
||||
// for each entry, 'charge' it (i.e. reduce credit by) delta * its size.
|
||||
// delta is typically MCD (see above); however, several such updates
|
||||
// may be lumped together to save time. Landlord_Lazy does this.
|
||||
void charge_all(float delta)
|
||||
{
|
||||
for(MapIt it = map.begin(); it != map.end(); ++it)
|
||||
{
|
||||
Entry& entry = it->second;
|
||||
entry.credit -= delta * entry.size;
|
||||
if(!should_evict(entry))
|
||||
mcd_calc.notify_decreased(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// is entry's credit exhausted? if so, it should be evicted.
|
||||
bool should_evict(const Entry& entry)
|
||||
{
|
||||
// we need a bit of leeway because density calculations may not
|
||||
// be exact. choose value carefully: must not be high enough to
|
||||
// trigger false positives.
|
||||
return entry.credit < 0.0001f;
|
||||
}
|
||||
|
||||
private:
|
||||
McdCalc<Entry, Map> mcd_calc;
|
||||
};
|
||||
|
||||
// Cache manger policies. (these are partial specializations of Landlord,
|
||||
// adapting it to the template params required by Cache)
|
||||
template<class Key, class Entry> class Landlord_Naive : public Landlord<Key, Entry, McdCalc_Naive> {};
|
||||
template<class Key, class Entry> class Landlord_Cached: public Landlord<Key, Entry, McdCalc_Cached> {};
|
||||
|
||||
// variant of Landlord that adds a priority queue to directly determine
|
||||
// which entry to evict. this allows lumping several charge operations
|
||||
// together and thus reduces iteration over all entries.
|
||||
// tradeoff: O(logN) removal (instead of N), but additional O(N) storage.
|
||||
template<typename Key, class Entry>
|
||||
class Landlord_Lazy : public Landlord_Naive<Key, Entry>
|
||||
{
|
||||
typedef typename Landlord_Naive<Key, Entry>::Map Map;
|
||||
typedef typename Landlord_Naive<Key, Entry>::MapIt MapIt;
|
||||
typedef typename Landlord_Naive<Key, Entry>::MapCIt MapCIt;
|
||||
|
||||
public:
|
||||
Landlord_Lazy() { pending_delta = 0.0f; }
|
||||
|
||||
void add(Key key, const Entry& entry)
|
||||
{
|
||||
// we must apply pending_delta now - otherwise, the existing delta
|
||||
// would later be applied to this newly added item (incorrect).
|
||||
commit_pending_delta();
|
||||
|
||||
MapIt it = Parent::add_(key, entry);
|
||||
pri_q.push(it);
|
||||
}
|
||||
|
||||
void remove(Key key)
|
||||
{
|
||||
Parent::remove(key);
|
||||
|
||||
// reconstruct pri_q from current map. this is slow (N*logN) and
|
||||
// could definitely be done better, but we don't bother since
|
||||
// remove is a very rare operation (e.g. invalidating entries).
|
||||
while(!pri_q.empty())
|
||||
pri_q.pop();
|
||||
for(MapCIt it = this->map.begin(); it != this->map.end(); ++it)
|
||||
pri_q.push(it);
|
||||
}
|
||||
|
||||
void on_access(Entry& entry)
|
||||
{
|
||||
Parent::on_access(entry);
|
||||
|
||||
// entry's credit was changed. we now need to reshuffle the
|
||||
// pri queue to reflect this.
|
||||
pri_q.ensure_heap_order();
|
||||
}
|
||||
|
||||
void remove_least_valuable(std::list<Entry>& entry_list)
|
||||
{
|
||||
MapIt least_valuable_it = pri_q.top(); pri_q.pop();
|
||||
Entry& entry = Map::entry_from_it(least_valuable_it);
|
||||
|
||||
entry_list.push_back(entry);
|
||||
|
||||
// add to pending_delta the MCD that would have resulted
|
||||
// if removing least_valuable_it normally.
|
||||
// first, calculate actual credit (i.e. apply pending_delta to
|
||||
// this entry); then add the resulting density to pending_delta.
|
||||
entry.credit -= pending_delta*entry.size;
|
||||
const float credit_density = entry.credit_density();
|
||||
debug_assert(credit_density > 0.0f);
|
||||
pending_delta += credit_density;
|
||||
|
||||
Parent::remove_(least_valuable_it);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef Landlord_Naive<Key, Entry> Parent;
|
||||
|
||||
// sort iterators by credit_density of the Entry they reference.
|
||||
struct CD_greater
|
||||
{
|
||||
bool operator()(MapIt it1, MapIt it2) const
|
||||
{
|
||||
return Map::entry_from_it(it1).credit_density() >
|
||||
Map::entry_from_it(it2).credit_density();
|
||||
}
|
||||
};
|
||||
// wrapper on top of priority_queue that allows 'heap re-sift'
|
||||
// (see on_access).
|
||||
// notes:
|
||||
// - greater comparator makes pri_q.top() the one with
|
||||
// LEAST credit_density, which is what we want.
|
||||
// - deriving from an STL container is a bit dirty, but we need this
|
||||
// to get at the underlying data (priority_queue interface is not
|
||||
// very capable).
|
||||
class PriQ: public std::priority_queue<MapIt, std::vector<MapIt>, CD_greater>
|
||||
{
|
||||
public:
|
||||
void ensure_heap_order()
|
||||
{
|
||||
// TODO: this is actually N*logN - ouch! that explains high
|
||||
// CPU cost in profile. this is called after only 1 item has
|
||||
// changed, so a logN "sift" operation ought to suffice.
|
||||
// that's not supported by the STL heap functions, so we'd
|
||||
// need a better implementation. pending..
|
||||
std::make_heap(this->c.begin(), this->c.end(), this->comp);
|
||||
}
|
||||
};
|
||||
PriQ pri_q;
|
||||
|
||||
// delta values that have accumulated over several
|
||||
// remove_least_valuable() calls. applied during add().
|
||||
float pending_delta;
|
||||
|
||||
void commit_pending_delta()
|
||||
{
|
||||
if(pending_delta > 0.0f)
|
||||
{
|
||||
this->charge_all(pending_delta);
|
||||
pending_delta = 0.0f;
|
||||
|
||||
// we've changed entry credit, so the heap order *may* have been
|
||||
// violated; reorder the pri queue. (I don't think so,
|
||||
// due to definition of delta, but we'll play it safe)
|
||||
pri_q.ensure_heap_order();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// functor that implements division of first arg by second
|
||||
//
|
||||
|
||||
// this is used to calculate credit_density(); performance matters
|
||||
// because this is called for each entry during each remove operation.
|
||||
|
||||
// floating-point division (fairly slow)
|
||||
class Divider_Naive
|
||||
{
|
||||
public:
|
||||
Divider_Naive() {} // needed for default CacheEntry ctor
|
||||
Divider_Naive(float UNUSED(x)) {}
|
||||
float operator()(float val, float divisor) const
|
||||
{
|
||||
return val / divisor;
|
||||
}
|
||||
};
|
||||
|
||||
// caches reciprocal of divisor and multiplies by that.
|
||||
// tradeoff: only 4 clocks (instead of 20), but 4 bytes extra per entry.
|
||||
class Divider_Recip
|
||||
{
|
||||
float recip;
|
||||
public:
|
||||
Divider_Recip() {} // needed for default CacheEntry ctor
|
||||
Divider_Recip(float x) { recip = 1.0f / x; }
|
||||
float operator()(float val, float UNUSED(divisor)) const
|
||||
{
|
||||
return val * recip;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: use SSE/3DNow RCP instruction? not yet, because not all systems
|
||||
// support it and overhead of detecting this support eats into any gains.
|
||||
|
||||
// initial implementation for testing purposes; quite inefficient.
|
||||
template<typename Key, typename Entry>
|
||||
class LRU
|
||||
{
|
||||
public:
|
||||
bool empty() const
|
||||
{
|
||||
return lru.empty();
|
||||
}
|
||||
|
||||
void add(Key key, const Entry& entry)
|
||||
{
|
||||
lru.push_back(KeyAndEntry(key, entry));
|
||||
}
|
||||
|
||||
bool find(Key key, const Entry** pentry) const
|
||||
{
|
||||
CIt it = std::find_if(lru.begin(), lru.end(), KeyEq(key));
|
||||
if(it == lru.end())
|
||||
return false;
|
||||
*pentry = &it->entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
void remove(Key key)
|
||||
{
|
||||
std::remove_if(lru.begin(), lru.end(), KeyEq(key));
|
||||
}
|
||||
|
||||
void on_access(Entry& entry)
|
||||
{
|
||||
for(It it = lru.begin(); it != lru.end(); ++it)
|
||||
{
|
||||
if(&entry == &it->entry)
|
||||
{
|
||||
add(it->key, it->entry);
|
||||
lru.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
debug_warn("entry not found in list");
|
||||
}
|
||||
|
||||
void remove_least_valuable(std::list<Entry>& entry_list)
|
||||
{
|
||||
entry_list.push_back(lru.front().entry);
|
||||
lru.pop_front();
|
||||
}
|
||||
|
||||
private:
|
||||
struct KeyAndEntry
|
||||
{
|
||||
Key key;
|
||||
Entry entry;
|
||||
KeyAndEntry(Key key_, const Entry& entry_)
|
||||
: key(key_), entry(entry_) {}
|
||||
};
|
||||
class KeyEq
|
||||
{
|
||||
Key key;
|
||||
public:
|
||||
KeyEq(Key key_) : key(key_) {}
|
||||
bool operator()(const KeyAndEntry& ke) const
|
||||
{
|
||||
return ke.key == key;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::list<KeyAndEntry> List;
|
||||
typedef typename List::iterator It;
|
||||
typedef typename List::const_iterator CIt;
|
||||
List lru;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Cache
|
||||
//
|
||||
|
||||
template
|
||||
<
|
||||
typename Key, typename Item,
|
||||
// see documentation above for Manager's interface.
|
||||
template<typename Key, class Entry> class Manager = Landlord_Cached,
|
||||
class Divider = Divider_Naive
|
||||
>
|
||||
class Cache
|
||||
{
|
||||
public:
|
||||
Cache() : mgr() {}
|
||||
|
||||
void add(Key key, Item item, size_t size, uint cost)
|
||||
{
|
||||
return mgr.add(key, Entry(item, size, cost));
|
||||
}
|
||||
|
||||
// remove the entry identified by <key>. expected usage is to check
|
||||
// if present and determine size via retrieve(), so no need for
|
||||
// error checking.
|
||||
// useful for invalidating single cache entries.
|
||||
void remove(Key key)
|
||||
{
|
||||
mgr.remove(key);
|
||||
}
|
||||
|
||||
// if there is no entry for <key> in the cache, return false.
|
||||
// otherwise, return true and pass back item and (optionally) size.
|
||||
//
|
||||
// if refill_credit (default), the cache manager 'rewards' this entry,
|
||||
// tending to keep it in cache longer. this parameter is not used in
|
||||
// normal operation - it's only for special cases where we need to
|
||||
// make an end run around the cache accounting (e.g. for cache simulator).
|
||||
bool retrieve(Key key, Item& item, size_t* psize = 0, bool refill_credit = true)
|
||||
{
|
||||
const Entry* entry;
|
||||
if(!mgr.find(key, &entry))
|
||||
return false;
|
||||
|
||||
item = entry->item;
|
||||
if(psize)
|
||||
*psize = entry->size;
|
||||
|
||||
if(refill_credit)
|
||||
mgr.on_access((Entry&)*entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// toss out the least valuable entry. return false if cache is empty,
|
||||
// otherwise true and (optionally) pass back its item and size.
|
||||
bool remove_least_valuable(Item* pItem = 0, size_t* pSize = 0)
|
||||
{
|
||||
// as an artefact of the cache eviction policy, several entries
|
||||
// may be "shaken loose" by one call to remove_least_valuable.
|
||||
// we cache them in a list to disburden callers (they always get
|
||||
// exactly one).
|
||||
if(entries_awaiting_eviction.empty())
|
||||
{
|
||||
if(empty())
|
||||
return false;
|
||||
|
||||
mgr.remove_least_valuable(entries_awaiting_eviction);
|
||||
debug_assert(!entries_awaiting_eviction.empty());
|
||||
}
|
||||
|
||||
const Entry& entry = entries_awaiting_eviction.front();
|
||||
if(pItem)
|
||||
*pItem = entry.item;
|
||||
if(pSize)
|
||||
*pSize = entry.size;
|
||||
entries_awaiting_eviction.pop_front();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return mgr.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
// this is applicable to all cache management policies and stores all
|
||||
// required information. a Divider functor is used to implement
|
||||
// division for credit_density.
|
||||
template<class InnerDivider> struct CacheEntry
|
||||
{
|
||||
Item item;
|
||||
size_t size;
|
||||
uint cost;
|
||||
float credit;
|
||||
|
||||
InnerDivider divider;
|
||||
|
||||
// needed for mgr.remove_least_valuable's entry_copy
|
||||
CacheEntry() {}
|
||||
|
||||
CacheEntry(Item item_, size_t size_, uint cost_)
|
||||
: item(item_), divider((float)size_)
|
||||
{
|
||||
size = size_;
|
||||
cost = cost_;
|
||||
credit = cost;
|
||||
|
||||
// else divider will fail
|
||||
debug_assert(size != 0);
|
||||
}
|
||||
|
||||
float credit_density() const
|
||||
{
|
||||
return divider(credit, (float)size);
|
||||
}
|
||||
};
|
||||
typedef CacheEntry<Divider> Entry;
|
||||
|
||||
// see note in remove_least_valuable().
|
||||
std::list<Entry> entries_awaiting_eviction;
|
||||
|
||||
Manager<Key, Entry> mgr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
// FIFO bit queue
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
struct BitBuf
|
||||
{
|
||||
@ -981,9 +270,9 @@ struct BitBuf
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// ring buffer - static array, accessible modulo n
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
template<class T, size_t n> class RingBuf
|
||||
{
|
||||
@ -1168,143 +457,4 @@ public:
|
||||
};
|
||||
|
||||
|
||||
|
||||
//
|
||||
// cache
|
||||
//
|
||||
|
||||
|
||||
// owns a pool of resources (Entry-s), associated with a 64 bit id.
|
||||
// typical use: add all available resources to the cache via grow();
|
||||
// assign() ids to the resources, and update the resource data if necessary;
|
||||
// retrieve() the resource, given id.
|
||||
template<class Entry> class LRUCache
|
||||
{
|
||||
public:
|
||||
// 'give' Entry to the cache.
|
||||
int grow(Entry& e)
|
||||
{
|
||||
// add to front of LRU list, but not index
|
||||
// (since we don't have an id yet)
|
||||
lru_list.push_front(Line(e));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// find the least-recently used line; associate id with it,
|
||||
// and return its Entry. fails (returns 0) if id is already
|
||||
// associated, or all lines are locked.
|
||||
Entry* assign(u64 id)
|
||||
{
|
||||
if(find_line(id))
|
||||
{
|
||||
debug_warn("assign: id already in cache!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// scan in least->most used order for first non-locked entry
|
||||
List_iterator l = lru_list.end();
|
||||
while(l != lru_list.begin())
|
||||
{
|
||||
--l;
|
||||
if(l->refs == 0)
|
||||
goto have_line;
|
||||
}
|
||||
|
||||
// all are locked and cannot be displaced.
|
||||
// caller should grow() enough lines so that this never happens.
|
||||
debug_warn("assign: all lines locked - grow() more lines");
|
||||
return 0;
|
||||
|
||||
have_line:
|
||||
|
||||
// update mapping (index)
|
||||
idx.erase(id);
|
||||
idx[id] = l;
|
||||
|
||||
l->id = id;
|
||||
return &l->ent;
|
||||
}
|
||||
|
||||
|
||||
// find line identified by id; return its entry or 0 if not in cache.
|
||||
Entry* retrieve(u64 id)
|
||||
{
|
||||
// invalid: id 0 denotes not-yet-associated lines
|
||||
if(id == 0)
|
||||
{
|
||||
debug_warn("retrieve: id 0 not allowed");
|
||||
return 0;
|
||||
}
|
||||
Line* l = find_line(id);
|
||||
return l? &l->ent : 0;
|
||||
}
|
||||
|
||||
|
||||
// add/release a reference to a line, to protect it against
|
||||
// displacement via associate(). we verify refs >= 0.
|
||||
int lock(u64 id, bool locked)
|
||||
{
|
||||
Line* l = find_line(id);
|
||||
if(!l)
|
||||
return -1;
|
||||
|
||||
if(locked)
|
||||
l->refs++;
|
||||
else
|
||||
{
|
||||
debug_assert(l->refs > 0);
|
||||
l->refs--;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
// implementation:
|
||||
// cache lines are stored in a list, most recently used in front.
|
||||
// a map finds the list entry containing a given id in log-time.
|
||||
|
||||
struct Line
|
||||
{
|
||||
u64 id;
|
||||
Entry ent;
|
||||
int refs; // protect from displacement if > 0
|
||||
|
||||
Line(Entry& _ent)
|
||||
{
|
||||
id = 0;
|
||||
ent = _ent;
|
||||
refs = 0;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::list<Line> List;
|
||||
typedef typename List::iterator List_iterator;
|
||||
List lru_list;
|
||||
|
||||
typedef std::map<u64, List_iterator> Map;
|
||||
Map idx;
|
||||
|
||||
|
||||
// return the line identified by id, or 0 if not in cache.
|
||||
// mark it as the most recently used line.
|
||||
Line* find_line(u64 id)
|
||||
{
|
||||
typename Map::const_iterator i = idx.find(id);
|
||||
// not found
|
||||
if(i == idx.end())
|
||||
return 0;
|
||||
|
||||
// index points us to list entry
|
||||
List_iterator l = i->second;
|
||||
|
||||
// mark l as the most recently used line.
|
||||
lru_list.splice(lru_list.begin(), lru_list, l);
|
||||
idx[l->id] = l;
|
||||
|
||||
return &*l;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // #ifndef ADTS_H__
|
||||
|
@ -109,32 +109,9 @@ static void bundle_logs(FILE* f)
|
||||
}
|
||||
|
||||
|
||||
// TODO: leaks memory returned by wcsdup
|
||||
|
||||
static const wchar_t* translate(const wchar_t* text)
|
||||
{
|
||||
#if HAVE_I18N
|
||||
// make sure i18n system is (already|still) initialized.
|
||||
if(g_CurrentLocale)
|
||||
{
|
||||
// be prepared for this to fail, because translation potentially
|
||||
// involves script code and the JS context might be corrupted.
|
||||
#if OS_WIN
|
||||
__try
|
||||
#endif
|
||||
{
|
||||
const wchar_t* text2 = wcsdup(I18n::translate(text).c_str());
|
||||
// only overwrite if wcsdup succeeded, i.e. not out of memory.
|
||||
if(text2)
|
||||
text = text2;
|
||||
}
|
||||
#if OS_WIN
|
||||
__except(EXCEPTION_EXECUTE_HANDLER)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,10 @@ and call set_app_hooks.
|
||||
// a return value.
|
||||
#ifdef FUNC
|
||||
|
||||
// for convenience; less confusing than FUNC(void, [..], (void))
|
||||
#define VOID_FUNC(name, params, param_names)\
|
||||
FUNC(void, name, params, param_names, (void))
|
||||
|
||||
// override default decision on using OpenGL extensions relating to
|
||||
// texture upload. this should call ogl_tex_override to disable/force
|
||||
// their use if the current card/driver combo respectively crashes or
|
||||
@ -80,7 +84,7 @@ and call set_app_hooks.
|
||||
//
|
||||
// default implementation works but is hardwired in code and therefore
|
||||
// not expandable.
|
||||
FUNC(void, override_gl_upload_caps, (void), (), (void))
|
||||
VOID_FUNC(override_gl_upload_caps, (void), ())
|
||||
|
||||
// return full native path of the directory into which crashdumps should be
|
||||
// written. must end with directory separator (e.g. '/').
|
||||
@ -95,7 +99,7 @@ FUNC(const char*, get_log_dir, (void), (), return)
|
||||
// used when writing a crashlog so that all relevant info is in one file.
|
||||
//
|
||||
// default implementation gathers 0ad data but is fail-safe.
|
||||
FUNC(void, bundle_logs, (FILE* f), (f), (void))
|
||||
VOID_FUNC(bundle_logs, (FILE* f), (f))
|
||||
|
||||
// return localized version of <text> if i18n functionality is available.
|
||||
//
|
||||
@ -105,10 +109,12 @@ FUNC(const wchar_t*, translate, (const wchar_t* text), (text), return)
|
||||
// write <text> to the app's log.
|
||||
//
|
||||
// default implementation uses stdout.
|
||||
FUNC(void, log, (const wchar_t* text), (text), (void))
|
||||
VOID_FUNC(log, (const wchar_t* text), (text))
|
||||
|
||||
FUNC(ErrorReaction, display_error, (const wchar_t* text, uint flags), (text, flags), return)
|
||||
|
||||
#undef VOID_FUNC
|
||||
|
||||
#endif // #ifdef FUNC
|
||||
|
||||
|
||||
|
731
source/lib/cache_adt.h
Normal file
731
source/lib/cache_adt.h
Normal file
@ -0,0 +1,731 @@
|
||||
/**
|
||||
* =========================================================================
|
||||
* File : cache_adt.h
|
||||
* Project : 0 A.D.
|
||||
* Description : Customizable cache data structure.
|
||||
*
|
||||
* @author Jan.Wassenberg@stud.uni-karlsruhe.de
|
||||
* =========================================================================
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005-2006 Jan Wassenberg
|
||||
*
|
||||
* Redistribution and/or modification are also permitted under the
|
||||
* terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation (version 2 or later, at your option).
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CACHE_ADT_H__
|
||||
#define CACHE_ADT_H__
|
||||
|
||||
#include <cfloat>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
/*
|
||||
Cache for items of variable size and value/"cost".
|
||||
underlying displacement algorithm is pluggable; default is "Landlord".
|
||||
|
||||
template reference:
|
||||
Entry provides size, cost, credit and credit_density().
|
||||
rationale:
|
||||
- made a template instead of exposing Cache::Entry because
|
||||
that would drag a lot of stuff out of Cache.
|
||||
- calculates its own density since that entails a Divider functor,
|
||||
which requires storage inside Entry.
|
||||
Entries is a collection with iterator and begin()/end() and
|
||||
"static Entry& entry_from_it(iterator)".
|
||||
rationale:
|
||||
- STL map has pair<key, item> as its value_type, so this
|
||||
function would return it->second. however, we want to support
|
||||
other container types (where we'd just return *it).
|
||||
Manager is a template parameterized on typename Key and class Entry.
|
||||
its interface is as follows:
|
||||
|
||||
// is the cache empty?
|
||||
bool empty() const;
|
||||
|
||||
// add (key, entry) to cache.
|
||||
void add(Key key, const Entry& entry);
|
||||
|
||||
// if the entry identified by <key> is not in cache, return false;
|
||||
// otherwise return true and pass back a pointer to it.
|
||||
bool find(Key key, const Entry** pentry) const;
|
||||
|
||||
// remove an entry from cache, which is assumed to exist!
|
||||
// this makes sense because callers will typically first use find() to
|
||||
// return info about the entry; this also checks if present.
|
||||
void remove(Key key);
|
||||
|
||||
// mark <entry> as just accessed for purpose of cache management.
|
||||
// it will tend to be kept in cache longer.
|
||||
void on_access(Entry& entry);
|
||||
|
||||
// caller's intent is to remove the least valuable entry.
|
||||
// in implementing this, you have the latitude to "shake loose"
|
||||
// several entries (e.g. because their 'value' is equal).
|
||||
// they must all be push_back-ed into the list; Cache will dole
|
||||
// them out one at a time in FIFO order to callers.
|
||||
//
|
||||
// rationale:
|
||||
// - it is necessary for callers to receive a copy of the
|
||||
// Entry being evicted - e.g. file_cache owns its items and
|
||||
// they must be passed back to allocator when evicted.
|
||||
// - e.g. Landlord can potentially see several entries become
|
||||
// evictable in one call to remove_least_valuable. there are
|
||||
// several ways to deal with this:
|
||||
// 1) generator interface: we return one of { empty, nevermind,
|
||||
// removed, remove-and-call-again }. this greatly complicates
|
||||
// the call site.
|
||||
// 2) return immediately after finding an item to evict.
|
||||
// this changes cache behavior - entries stored at the
|
||||
// beginning would be charged more often (unfair).
|
||||
// resuming charging at the next entry doesn't work - this
|
||||
// would have to be flushed when adding, at which time there
|
||||
// is no provision for returning any items that may be evicted.
|
||||
// 3) return list of all entries "shaken loose". this incurs
|
||||
// frequent mem allocs, which can be alleviated via suballocator.
|
||||
// note: an intrusive linked-list doesn't make sense because
|
||||
// entries to be returned need to be copied anyway (they are
|
||||
// removed from the manager's storage).
|
||||
void remove_least_valuable(std::list<Entry>& entry_list)
|
||||
*/
|
||||
|
||||
|
||||
//
|
||||
// functors to calculate minimum credit density (MCD)
|
||||
//
|
||||
|
||||
// MCD is required for the Landlord algorithm's evict logic.
|
||||
// [Young02] calls it '\delta'.
|
||||
|
||||
// scan over all entries and return MCD.
|
||||
template<class Entries> float ll_calc_min_credit_density(const Entries& entries)
|
||||
{
|
||||
float min_credit_density = FLT_MAX;
|
||||
for(typename Entries::const_iterator it = entries.begin(); it != entries.end(); ++it)
|
||||
{
|
||||
const float credit_density = Entries::entry_from_it(it).credit_density();
|
||||
min_credit_density = fminf(min_credit_density, credit_density);
|
||||
}
|
||||
return min_credit_density;
|
||||
}
|
||||
|
||||
// note: no warning is given that the MCD entry is being removed!
|
||||
// (reduces overhead in remove_least_valuable)
|
||||
// these functors must account for that themselves (e.g. by resetting
|
||||
// their state directly after returning MCD).
|
||||
|
||||
// determine MCD by scanning over all entries.
|
||||
// tradeoff: O(N) time complexity, but all notify* calls are no-ops.
|
||||
template<class Entry, class Entries>
|
||||
class McdCalc_Naive
|
||||
{
|
||||
public:
|
||||
void notify_added(const Entry&) const {}
|
||||
void notify_decreased(const Entry&) const {}
|
||||
void notify_impending_increase_or_remove(const Entry&) const {}
|
||||
void notify_increased_or_removed(const Entry&) const {}
|
||||
float operator()(const Entries& entries) const
|
||||
{
|
||||
const float mcd = ll_calc_min_credit_density(entries);
|
||||
return mcd;
|
||||
}
|
||||
};
|
||||
|
||||
// cache previous MCD and update it incrementally (when possible).
|
||||
// tradeoff: amortized O(1) time complexity, but notify* calls must
|
||||
// perform work whenever something in the cache changes.
|
||||
template<class Entry, class Entries>
|
||||
class McdCalc_Cached
|
||||
{
|
||||
public:
|
||||
McdCalc_Cached() : min_credit_density(FLT_MAX), min_valid(false) {}
|
||||
|
||||
void notify_added(const Entry& entry)
|
||||
{
|
||||
// when adding a new item, the minimum credit density can only
|
||||
// decrease or remain the same; acting as if entry's credit had
|
||||
// been decreased covers both cases.
|
||||
notify_decreased(entry);
|
||||
}
|
||||
|
||||
void notify_decreased(const Entry& entry)
|
||||
{
|
||||
min_credit_density = MIN(min_credit_density, entry.credit_density());
|
||||
}
|
||||
|
||||
void notify_impending_increase_or_remove(const Entry& entry)
|
||||
{
|
||||
// remember if this entry had the smallest density
|
||||
is_min_entry = feq(min_credit_density, entry.credit_density());
|
||||
}
|
||||
|
||||
void notify_increased_or_removed(const Entry& UNUSED(entry))
|
||||
{
|
||||
// .. it did and was increased or removed. we must invalidate
|
||||
// MCD and recalculate it next time.
|
||||
if(is_min_entry)
|
||||
{
|
||||
min_valid = false;
|
||||
min_credit_density = -1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
float operator()(const Entries& entries)
|
||||
{
|
||||
if(min_valid)
|
||||
{
|
||||
// the entry that has MCD will be removed anyway by caller;
|
||||
// we need to invalidate here because they don't call
|
||||
// notify_increased_or_removed.
|
||||
min_valid = false;
|
||||
return min_credit_density;
|
||||
}
|
||||
|
||||
// this is somewhat counterintuitive. since we're calculating
|
||||
// MCD directly, why not mark our cached version of it valid
|
||||
// afterwards? reason is that our caller will remove the entry with
|
||||
// MCD, so it'll be invalidated anyway.
|
||||
// instead, our intent is to calculate MCD for the *next time*.
|
||||
const float ret = ll_calc_min_credit_density(entries);
|
||||
min_valid = true;
|
||||
min_credit_density = FLT_MAX;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
float min_credit_density;
|
||||
bool min_valid;
|
||||
|
||||
// temporary flag set by notify_impending_increase_or_remove
|
||||
bool is_min_entry;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Landlord cache management policy: see [Young02].
|
||||
//
|
||||
|
||||
// in short, each entry has credit initially set to cost. when wanting to
|
||||
// remove an item, all are charged according to MCD and their size;
|
||||
// entries are evicted if their credit is exhausted. accessing an entry
|
||||
// restores "some" of its credit.
|
||||
template<typename Key, typename Entry, template<class Entry, class Entries> class McdCalc = McdCalc_Cached>
|
||||
class Landlord
|
||||
{
|
||||
public:
|
||||
bool empty() const
|
||||
{
|
||||
return map.empty();
|
||||
}
|
||||
|
||||
void add(Key key, const Entry& entry)
|
||||
{
|
||||
// adapter for add_ (which returns an iterator)
|
||||
(void)add_(key, entry);
|
||||
}
|
||||
|
||||
bool find(Key key, const Entry** pentry) const
|
||||
{
|
||||
MapCIt it = map.find(key);
|
||||
if(it == map.end())
|
||||
return false;
|
||||
*pentry = &it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
void remove(Key key)
|
||||
{
|
||||
MapIt it = map.find(key);
|
||||
debug_assert(it != map.end());
|
||||
remove_(it);
|
||||
}
|
||||
|
||||
void on_access(Entry& entry)
|
||||
{
|
||||
mcd_calc.notify_impending_increase_or_remove(entry);
|
||||
|
||||
// Landlord algorithm calls for credit to be reset to anything
|
||||
// between its current value and the cost.
|
||||
const float gain = 0.75f; // restore most credit
|
||||
entry.credit = gain*entry.cost + (1.0f-gain)*entry.credit;
|
||||
|
||||
mcd_calc.notify_increased_or_removed(entry);
|
||||
}
|
||||
|
||||
void remove_least_valuable(std::list<Entry>& entry_list)
|
||||
{
|
||||
// we are required to evict at least one entry. one iteration
|
||||
// ought to suffice, due to definition of min_credit_density and
|
||||
// tolerance; however, we provide for repeating if necessary.
|
||||
again:
|
||||
|
||||
// messing with this (e.g. raising if tiny) would result in
|
||||
// different evictions than Landlord_Lazy, which is unacceptable.
|
||||
// nor is doing so necessary: if mcd is tiny, so is credit.
|
||||
const float min_credit_density = mcd_calc(map);
|
||||
debug_assert(min_credit_density > 0.0f);
|
||||
|
||||
for(MapIt it = map.begin(); it != map.end();) // no ++it
|
||||
{
|
||||
Entry& entry = it->second;
|
||||
|
||||
charge(entry, min_credit_density);
|
||||
if(should_evict(entry))
|
||||
{
|
||||
entry_list.push_back(entry);
|
||||
|
||||
// annoying: we have to increment <it> before erasing
|
||||
MapIt it_to_remove = it++;
|
||||
map.erase(it_to_remove);
|
||||
}
|
||||
else
|
||||
{
|
||||
mcd_calc.notify_decreased(entry);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if(entry_list.empty())
|
||||
goto again;
|
||||
}
|
||||
|
||||
protected:
|
||||
// note: use hash_map instead of map for better locality
|
||||
// (relevant when iterating over all items in remove_least_valuable)
|
||||
class Map : public STL_HASH_MAP<Key, Entry>
|
||||
{
|
||||
public:
|
||||
static Entry& entry_from_it(typename Map::iterator it) { return it->second; }
|
||||
static const Entry& entry_from_it(typename Map::const_iterator it) { return it->second; }
|
||||
};
|
||||
typedef typename Map::iterator MapIt;
|
||||
typedef typename Map::const_iterator MapCIt;
|
||||
Map map;
|
||||
|
||||
// add entry and return iterator pointing to it.
|
||||
MapIt add_(Key key, const Entry& entry)
|
||||
{
|
||||
typedef std::pair<MapIt, bool> PairIB;
|
||||
typename Map::value_type val = std::make_pair(key, entry);
|
||||
PairIB ret = map.insert(val);
|
||||
debug_assert(ret.second); // must not already be in map
|
||||
|
||||
mcd_calc.notify_added(entry);
|
||||
|
||||
return ret.first;
|
||||
}
|
||||
|
||||
// remove entry (given by iterator) directly.
|
||||
void remove_(MapIt it)
|
||||
{
|
||||
const Entry& entry = it->second;
|
||||
mcd_calc.notify_impending_increase_or_remove(entry);
|
||||
mcd_calc.notify_increased_or_removed(entry);
|
||||
map.erase(it);
|
||||
}
|
||||
|
||||
void charge(Entry& entry, float delta)
|
||||
{
|
||||
entry.credit -= delta * entry.size;
|
||||
|
||||
// don't worry about entry.size being 0 - if so, cost
|
||||
// should also be 0, so credit will already be 0 anyway.
|
||||
}
|
||||
|
||||
// for each entry, 'charge' it (i.e. reduce credit by) delta * its size.
|
||||
// delta is typically MCD (see above); however, several such updates
|
||||
// may be lumped together to save time. Landlord_Lazy does this.
|
||||
void charge_all(float delta)
|
||||
{
|
||||
for(MapIt it = map.begin(); it != map.end(); ++it)
|
||||
{
|
||||
Entry& entry = it->second;
|
||||
entry.credit -= delta * entry.size;
|
||||
if(!should_evict(entry))
|
||||
mcd_calc.notify_decreased(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// is entry's credit exhausted? if so, it should be evicted.
|
||||
bool should_evict(const Entry& entry)
|
||||
{
|
||||
// we need a bit of leeway because density calculations may not
|
||||
// be exact. choose value carefully: must not be high enough to
|
||||
// trigger false positives.
|
||||
return entry.credit < 0.0001f;
|
||||
}
|
||||
|
||||
private:
|
||||
McdCalc<Entry, Map> mcd_calc;
|
||||
};
|
||||
|
||||
// Cache manger policies. (these are partial specializations of Landlord,
|
||||
// adapting it to the template params required by Cache)
|
||||
template<class Key, class Entry> class Landlord_Naive : public Landlord<Key, Entry, McdCalc_Naive> {};
|
||||
template<class Key, class Entry> class Landlord_Cached: public Landlord<Key, Entry, McdCalc_Cached> {};
|
||||
|
||||
// variant of Landlord that adds a priority queue to directly determine
|
||||
// which entry to evict. this allows lumping several charge operations
|
||||
// together and thus reduces iteration over all entries.
|
||||
// tradeoff: O(logN) removal (instead of N), but additional O(N) storage.
|
||||
template<typename Key, class Entry>
|
||||
class Landlord_Lazy : public Landlord_Naive<Key, Entry>
|
||||
{
|
||||
typedef typename Landlord_Naive<Key, Entry>::Map Map;
|
||||
typedef typename Landlord_Naive<Key, Entry>::MapIt MapIt;
|
||||
typedef typename Landlord_Naive<Key, Entry>::MapCIt MapCIt;
|
||||
|
||||
public:
|
||||
Landlord_Lazy() { pending_delta = 0.0f; }
|
||||
|
||||
void add(Key key, const Entry& entry)
|
||||
{
|
||||
// we must apply pending_delta now - otherwise, the existing delta
|
||||
// would later be applied to this newly added item (incorrect).
|
||||
commit_pending_delta();
|
||||
|
||||
MapIt it = Parent::add_(key, entry);
|
||||
pri_q.push(it);
|
||||
}
|
||||
|
||||
void remove(Key key)
|
||||
{
|
||||
Parent::remove(key);
|
||||
|
||||
// reconstruct pri_q from current map. this is slow (N*logN) and
|
||||
// could definitely be done better, but we don't bother since
|
||||
// remove is a very rare operation (e.g. invalidating entries).
|
||||
while(!pri_q.empty())
|
||||
pri_q.pop();
|
||||
for(MapCIt it = this->map.begin(); it != this->map.end(); ++it)
|
||||
pri_q.push(it);
|
||||
}
|
||||
|
||||
void on_access(Entry& entry)
|
||||
{
|
||||
Parent::on_access(entry);
|
||||
|
||||
// entry's credit was changed. we now need to reshuffle the
|
||||
// pri queue to reflect this.
|
||||
pri_q.ensure_heap_order();
|
||||
}
|
||||
|
||||
void remove_least_valuable(std::list<Entry>& entry_list)
|
||||
{
|
||||
MapIt least_valuable_it = pri_q.top(); pri_q.pop();
|
||||
Entry& entry = Map::entry_from_it(least_valuable_it);
|
||||
|
||||
entry_list.push_back(entry);
|
||||
|
||||
// add to pending_delta the MCD that would have resulted
|
||||
// if removing least_valuable_it normally.
|
||||
// first, calculate actual credit (i.e. apply pending_delta to
|
||||
// this entry); then add the resulting density to pending_delta.
|
||||
entry.credit -= pending_delta*entry.size;
|
||||
const float credit_density = entry.credit_density();
|
||||
debug_assert(credit_density > 0.0f);
|
||||
pending_delta += credit_density;
|
||||
|
||||
Parent::remove_(least_valuable_it);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef Landlord_Naive<Key, Entry> Parent;
|
||||
|
||||
// sort iterators by credit_density of the Entry they reference.
|
||||
struct CD_greater
|
||||
{
|
||||
bool operator()(MapIt it1, MapIt it2) const
|
||||
{
|
||||
return Map::entry_from_it(it1).credit_density() >
|
||||
Map::entry_from_it(it2).credit_density();
|
||||
}
|
||||
};
|
||||
// wrapper on top of priority_queue that allows 'heap re-sift'
|
||||
// (see on_access).
|
||||
// notes:
|
||||
// - greater comparator makes pri_q.top() the one with
|
||||
// LEAST credit_density, which is what we want.
|
||||
// - deriving from an STL container is a bit dirty, but we need this
|
||||
// to get at the underlying data (priority_queue interface is not
|
||||
// very capable).
|
||||
class PriQ: public std::priority_queue<MapIt, std::vector<MapIt>, CD_greater>
|
||||
{
|
||||
public:
|
||||
void ensure_heap_order()
|
||||
{
|
||||
// TODO: this is actually N*logN - ouch! that explains high
|
||||
// CPU cost in profile. this is called after only 1 item has
|
||||
// changed, so a logN "sift" operation ought to suffice.
|
||||
// that's not supported by the STL heap functions, so we'd
|
||||
// need a better implementation. pending..
|
||||
std::make_heap(this->c.begin(), this->c.end(), this->comp);
|
||||
}
|
||||
};
|
||||
PriQ pri_q;
|
||||
|
||||
// delta values that have accumulated over several
|
||||
// remove_least_valuable() calls. applied during add().
|
||||
float pending_delta;
|
||||
|
||||
void commit_pending_delta()
|
||||
{
|
||||
if(pending_delta > 0.0f)
|
||||
{
|
||||
this->charge_all(pending_delta);
|
||||
pending_delta = 0.0f;
|
||||
|
||||
// we've changed entry credit, so the heap order *may* have been
|
||||
// violated; reorder the pri queue. (I don't think so,
|
||||
// due to definition of delta, but we'll play it safe)
|
||||
pri_q.ensure_heap_order();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// functor that implements division of first arg by second
|
||||
//
|
||||
|
||||
// this is used to calculate credit_density(); performance matters
|
||||
// because this is called for each entry during each remove operation.
|
||||
|
||||
// floating-point division (fairly slow)
|
||||
class Divider_Naive
|
||||
{
|
||||
public:
|
||||
Divider_Naive() {} // needed for default CacheEntry ctor
|
||||
Divider_Naive(float UNUSED(x)) {}
|
||||
float operator()(float val, float divisor) const
|
||||
{
|
||||
return val / divisor;
|
||||
}
|
||||
};
|
||||
|
||||
// caches reciprocal of divisor and multiplies by that.
|
||||
// tradeoff: only 4 clocks (instead of 20), but 4 bytes extra per entry.
|
||||
class Divider_Recip
|
||||
{
|
||||
float recip;
|
||||
public:
|
||||
Divider_Recip() {} // needed for default CacheEntry ctor
|
||||
Divider_Recip(float x) { recip = 1.0f / x; }
|
||||
float operator()(float val, float UNUSED(divisor)) const
|
||||
{
|
||||
return val * recip;
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: use SSE/3DNow RCP instruction? not yet, because not all systems
|
||||
// support it and overhead of detecting this support eats into any gains.
|
||||
|
||||
// initial implementation for testing purposes; quite inefficient.
|
||||
template<typename Key, typename Entry>
|
||||
class LRU
|
||||
{
|
||||
public:
|
||||
bool empty() const
|
||||
{
|
||||
return lru.empty();
|
||||
}
|
||||
|
||||
void add(Key key, const Entry& entry)
|
||||
{
|
||||
lru.push_back(KeyAndEntry(key, entry));
|
||||
}
|
||||
|
||||
bool find(Key key, const Entry** pentry) const
|
||||
{
|
||||
CIt it = std::find_if(lru.begin(), lru.end(), KeyEq(key));
|
||||
if(it == lru.end())
|
||||
return false;
|
||||
*pentry = &it->entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
void remove(Key key)
|
||||
{
|
||||
std::remove_if(lru.begin(), lru.end(), KeyEq(key));
|
||||
}
|
||||
|
||||
void on_access(Entry& entry)
|
||||
{
|
||||
for(It it = lru.begin(); it != lru.end(); ++it)
|
||||
{
|
||||
if(&entry == &it->entry)
|
||||
{
|
||||
add(it->key, it->entry);
|
||||
lru.erase(it);
|
||||
return;
|
||||
}
|
||||
}
|
||||
debug_warn("entry not found in list");
|
||||
}
|
||||
|
||||
void remove_least_valuable(std::list<Entry>& entry_list)
|
||||
{
|
||||
entry_list.push_back(lru.front().entry);
|
||||
lru.pop_front();
|
||||
}
|
||||
|
||||
private:
|
||||
struct KeyAndEntry
|
||||
{
|
||||
Key key;
|
||||
Entry entry;
|
||||
KeyAndEntry(Key key_, const Entry& entry_)
|
||||
: key(key_), entry(entry_) {}
|
||||
};
|
||||
class KeyEq
|
||||
{
|
||||
Key key;
|
||||
public:
|
||||
KeyEq(Key key_) : key(key_) {}
|
||||
bool operator()(const KeyAndEntry& ke) const
|
||||
{
|
||||
return ke.key == key;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::list<KeyAndEntry> List;
|
||||
typedef typename List::iterator It;
|
||||
typedef typename List::const_iterator CIt;
|
||||
List lru;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Cache
|
||||
//
|
||||
|
||||
template
|
||||
<
|
||||
typename Key, typename Item,
|
||||
// see documentation above for Manager's interface.
|
||||
template<typename Key, class Entry> class Manager = Landlord_Cached,
|
||||
class Divider = Divider_Naive
|
||||
>
|
||||
class Cache
|
||||
{
|
||||
public:
|
||||
Cache() : mgr() {}
|
||||
|
||||
void add(Key key, Item item, size_t size, uint cost)
|
||||
{
|
||||
return mgr.add(key, Entry(item, size, cost));
|
||||
}
|
||||
|
||||
// remove the entry identified by <key>. expected usage is to check
|
||||
// if present and determine size via retrieve(), so no need for
|
||||
// error checking.
|
||||
// useful for invalidating single cache entries.
|
||||
void remove(Key key)
|
||||
{
|
||||
mgr.remove(key);
|
||||
}
|
||||
|
||||
// if there is no entry for <key> in the cache, return false.
|
||||
// otherwise, return true and pass back item and (optionally) size.
|
||||
//
|
||||
// if refill_credit (default), the cache manager 'rewards' this entry,
|
||||
// tending to keep it in cache longer. this parameter is not used in
|
||||
// normal operation - it's only for special cases where we need to
|
||||
// make an end run around the cache accounting (e.g. for cache simulator).
|
||||
bool retrieve(Key key, Item& item, size_t* psize = 0, bool refill_credit = true)
|
||||
{
|
||||
const Entry* entry;
|
||||
if(!mgr.find(key, &entry))
|
||||
return false;
|
||||
|
||||
item = entry->item;
|
||||
if(psize)
|
||||
*psize = entry->size;
|
||||
|
||||
if(refill_credit)
|
||||
mgr.on_access((Entry&)*entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// toss out the least valuable entry. return false if cache is empty,
|
||||
// otherwise true and (optionally) pass back its item and size.
|
||||
bool remove_least_valuable(Item* pItem = 0, size_t* pSize = 0)
|
||||
{
|
||||
// as an artefact of the cache eviction policy, several entries
|
||||
// may be "shaken loose" by one call to remove_least_valuable.
|
||||
// we cache them in a list to disburden callers (they always get
|
||||
// exactly one).
|
||||
if(entries_awaiting_eviction.empty())
|
||||
{
|
||||
if(empty())
|
||||
return false;
|
||||
|
||||
mgr.remove_least_valuable(entries_awaiting_eviction);
|
||||
debug_assert(!entries_awaiting_eviction.empty());
|
||||
}
|
||||
|
||||
const Entry& entry = entries_awaiting_eviction.front();
|
||||
if(pItem)
|
||||
*pItem = entry.item;
|
||||
if(pSize)
|
||||
*pSize = entry.size;
|
||||
entries_awaiting_eviction.pop_front();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return mgr.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
// this is applicable to all cache management policies and stores all
|
||||
// required information. a Divider functor is used to implement
|
||||
// division for credit_density.
|
||||
template<class InnerDivider> struct CacheEntry
|
||||
{
|
||||
Item item;
|
||||
size_t size;
|
||||
uint cost;
|
||||
float credit;
|
||||
|
||||
InnerDivider divider;
|
||||
|
||||
// needed for mgr.remove_least_valuable's entry_copy
|
||||
CacheEntry() {}
|
||||
|
||||
CacheEntry(Item item_, size_t size_, uint cost_)
|
||||
: item(item_), divider((float)size_)
|
||||
{
|
||||
size = size_;
|
||||
cost = cost_;
|
||||
credit = cost;
|
||||
|
||||
// else divider will fail
|
||||
debug_assert(size != 0);
|
||||
}
|
||||
|
||||
float credit_density() const
|
||||
{
|
||||
return divider(credit, (float)size);
|
||||
}
|
||||
};
|
||||
typedef CacheEntry<Divider> Entry;
|
||||
|
||||
// see note in remove_least_valuable().
|
||||
std::list<Entry> entries_awaiting_eviction;
|
||||
|
||||
Manager<Key, Entry> mgr;
|
||||
};
|
||||
|
||||
#endif // #ifndef CACHE_ADT_H__
|
@ -484,13 +484,29 @@ static const wchar_t* build_error_message(wchar_t* buf, size_t max_chars,
|
||||
if(!buf)
|
||||
return L"(insufficient memory to generate error message)";
|
||||
|
||||
char errno_description[100] = {'?'};
|
||||
LibError errno_equiv = LibError_from_errno(false);
|
||||
if(errno_equiv != ERR_FAIL) // meaningful translation
|
||||
error_description_r(errno_equiv, errno_description, ARRAY_SIZE(errno_description));
|
||||
|
||||
char os_error[100];
|
||||
if(sys_error_description_r(0, os_error, ARRAY_SIZE(os_error)) != INFO_OK)
|
||||
strcpy_s(os_error, ARRAY_SIZE(os_error), "?");
|
||||
|
||||
static const wchar_t fmt[] =
|
||||
L"%ls\r\n"
|
||||
L"Location: %hs:%d (%hs)\r\n"
|
||||
L"errno = %d (%hs)\r\n"
|
||||
L"OS error = %hs\r\n"
|
||||
L"\r\n"
|
||||
L"Call stack:\r\n"
|
||||
L"\r\n";
|
||||
int len = swprintf(buf,max_chars,fmt, description, fn_only, line, func);
|
||||
int len = swprintf(buf,max_chars,fmt,
|
||||
description,
|
||||
fn_only, line, func,
|
||||
errno, errno_description,
|
||||
os_error
|
||||
);
|
||||
if(len < 0)
|
||||
return L"(error while formatting error message)";
|
||||
|
||||
|
@ -457,18 +457,18 @@ ERR(-100800, ERR_SYM_NO_STACK_FRAMES_FOUND, "No stack frames found")
|
||||
ERR(-100801, ERR_SYM_UNRETRIEVABLE_STATIC, "Value unretrievable (stored in external module)")
|
||||
ERR(-100802, ERR_SYM_UNRETRIEVABLE_REG, "Value unretrievable (stored in register)")
|
||||
ERR(-100803, ERR_SYM_TYPE_INFO_UNAVAILABLE, "Error getting type_info")
|
||||
ERR(-100804, ERR_SYM_INTERNAL_ERROR, "Exception raised while processing a symbol")
|
||||
ERR(-100805, ERR_SYM_UNSUPPORTED, "Symbol type not (fully) supported")
|
||||
ERR(-100806, ERR_SYM_CHILD_NOT_FOUND, "Symbol does not have the given child")
|
||||
// .. this limit is to prevent infinite recursion.
|
||||
ERR(-100804, ERR_SYM_NESTING_LIMIT, "Symbol nesting too deep or infinite recursion")
|
||||
ERR(-100807, ERR_SYM_NESTING_LIMIT, "Symbol nesting too deep or infinite recursion")
|
||||
// .. this limit is to prevent large symbols (e.g. arrays or linked lists) from
|
||||
// hogging all stack trace buffer space.
|
||||
ERR(-100805, ERR_SYM_SINGLE_SYMBOL_LIMIT, "Symbol has produced too much output")
|
||||
ERR(-100806, ERR_SYM_INTERNAL_ERROR, "Exception raised while processing a symbol")
|
||||
ERR(-100807, ERR_SYM_UNSUPPORTED, "Symbol type not (fully) supported")
|
||||
ERR(-100808, ERR_SYM_SINGLE_SYMBOL_LIMIT, "Symbol has produced too much output")
|
||||
// .. one of the dump_sym* functions decided not to output anything at
|
||||
// all (e.g. for member functions in UDTs - we don't want those).
|
||||
// therefore, skip any post-symbol formatting (e.g. ",") as well.
|
||||
ERR(-100808, ERR_SYM_SUPPRESS_OUTPUT, "Symbol was suppressed")
|
||||
ERR(-100809, ERR_SYM_CHILD_NOT_FOUND, "Symbol does not have the given child")
|
||||
ERR(+100809, INFO_SYM_SUPPRESS_OUTPUT, "Symbol was suppressed")
|
||||
|
||||
// STL debug
|
||||
ERR(-100900, ERR_STL_CNT_UNKNOWN, "Unknown STL container type_name")
|
||||
|
@ -71,61 +71,6 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
// Enums from EXT_framebuffer_object
|
||||
#ifndef GL_FRAMEBUFFER_EXT
|
||||
#define GL_FRAMEBUFFER_EXT 0x8D40
|
||||
#define GL_RENDERBUFFER_EXT 0x8D41
|
||||
#define GL_STENCIL_INDEX1_EXT 0x8D46
|
||||
#define GL_STENCIL_INDEX4_EXT 0x8D47
|
||||
#define GL_STENCIL_INDEX8_EXT 0x8D48
|
||||
#define GL_STENCIL_INDEX16_EXT 0x8D49
|
||||
#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42
|
||||
#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43
|
||||
#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44
|
||||
#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50
|
||||
#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51
|
||||
#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52
|
||||
#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53
|
||||
#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54
|
||||
#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55
|
||||
#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0
|
||||
#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1
|
||||
#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2
|
||||
#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3
|
||||
#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4
|
||||
#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0
|
||||
#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1
|
||||
#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2
|
||||
#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3
|
||||
#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4
|
||||
#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5
|
||||
#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6
|
||||
#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7
|
||||
#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8
|
||||
#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9
|
||||
#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA
|
||||
#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB
|
||||
#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC
|
||||
#define GL_COLOR_ATTACHMENT13_EXT 0x8CED
|
||||
#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE
|
||||
#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF
|
||||
#define GL_DEPTH_ATTACHMENT_EXT 0x8D00
|
||||
#define GL_STENCIL_ATTACHMENT_EXT 0x8D20
|
||||
#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5
|
||||
#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6
|
||||
#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7
|
||||
#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9
|
||||
#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA
|
||||
#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB
|
||||
#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC
|
||||
#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD
|
||||
#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6
|
||||
#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7
|
||||
#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF
|
||||
#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8
|
||||
#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506
|
||||
#endif
|
||||
|
||||
//
|
||||
// extensions
|
||||
//
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
#include "lib/allocators.h"
|
||||
#include "lib/byte_order.h"
|
||||
#include "lib/adts.h"
|
||||
#include "lib/cache_adt.h"
|
||||
#include "file_internal.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -233,9 +233,12 @@ extern LibError sys_cursor_free(void* cursor);
|
||||
// misc
|
||||
//
|
||||
|
||||
// OS-specific backend for error_description_r.
|
||||
// NB: it is expected to be rare that OS return/error codes are actually
|
||||
// seen by user code, but we still translate them for completeness.
|
||||
// describe the current OS error state.
|
||||
//
|
||||
// err: if not 0, use that as the error code to translate; otherwise,
|
||||
// uses GetLastError or similar.
|
||||
// rationale: it is expected to be rare that OS return/error codes are
|
||||
// actually seen by user code, but we leave the possibility open.
|
||||
extern LibError sys_error_description_r(int err, char* buf, size_t max_chars);
|
||||
|
||||
// determine filename of the module to whom the given address belongs.
|
||||
|
@ -1,316 +0,0 @@
|
||||
/*
|
||||
SDL - Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2004 Sam Lantinga
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Sam Lantinga
|
||||
slouken@libsdl.org
|
||||
*/
|
||||
|
||||
#ifdef SAVE_RCSID
|
||||
static char rcsid =
|
||||
"@(#) $Id$";
|
||||
#endif
|
||||
|
||||
#ifndef _SDL_keysym_h
|
||||
#define _SDL_keysym_h
|
||||
|
||||
/* What we really want is a mapping of every raw key on the keyboard.
|
||||
To support international keyboards, we use the range 0xA1 - 0xFF
|
||||
as international virtual keycodes. We'll follow in the footsteps of X11...
|
||||
The names of the keys
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
/* The keyboard syms have been cleverly chosen to map to ASCII */
|
||||
SDLK_UNKNOWN = 0,
|
||||
SDLK_FIRST = 0,
|
||||
SDLK_BACKSPACE = 8,
|
||||
SDLK_TAB = 9,
|
||||
SDLK_CLEAR = 12,
|
||||
SDLK_RETURN = 13,
|
||||
SDLK_PAUSE = 19,
|
||||
SDLK_ESCAPE = 27,
|
||||
SDLK_SPACE = 32,
|
||||
SDLK_EXCLAIM = 33,
|
||||
SDLK_QUOTEDBL = 34,
|
||||
SDLK_HASH = 35,
|
||||
SDLK_DOLLAR = 36,
|
||||
SDLK_AMPERSAND = 38,
|
||||
SDLK_QUOTE = 39,
|
||||
SDLK_LEFTPAREN = 40,
|
||||
SDLK_RIGHTPAREN = 41,
|
||||
SDLK_ASTERISK = 42,
|
||||
SDLK_PLUS = 43,
|
||||
SDLK_COMMA = 44,
|
||||
SDLK_MINUS = 45,
|
||||
SDLK_PERIOD = 46,
|
||||
SDLK_SLASH = 47,
|
||||
SDLK_0 = 48,
|
||||
SDLK_1 = 49,
|
||||
SDLK_2 = 50,
|
||||
SDLK_3 = 51,
|
||||
SDLK_4 = 52,
|
||||
SDLK_5 = 53,
|
||||
SDLK_6 = 54,
|
||||
SDLK_7 = 55,
|
||||
SDLK_8 = 56,
|
||||
SDLK_9 = 57,
|
||||
SDLK_COLON = 58,
|
||||
SDLK_SEMICOLON = 59,
|
||||
SDLK_LESS = 60,
|
||||
SDLK_EQUALS = 61,
|
||||
SDLK_GREATER = 62,
|
||||
SDLK_QUESTION = 63,
|
||||
SDLK_AT = 64,
|
||||
/*
|
||||
Skip uppercase letters
|
||||
*/
|
||||
SDLK_LEFTBRACKET = 91,
|
||||
SDLK_BACKSLASH = 92,
|
||||
SDLK_RIGHTBRACKET = 93,
|
||||
SDLK_CARET = 94,
|
||||
SDLK_UNDERSCORE = 95,
|
||||
SDLK_BACKQUOTE = 96,
|
||||
SDLK_a = 97,
|
||||
SDLK_b = 98,
|
||||
SDLK_c = 99,
|
||||
SDLK_d = 100,
|
||||
SDLK_e = 101,
|
||||
SDLK_f = 102,
|
||||
SDLK_g = 103,
|
||||
SDLK_h = 104,
|
||||
SDLK_i = 105,
|
||||
SDLK_j = 106,
|
||||
SDLK_k = 107,
|
||||
SDLK_l = 108,
|
||||
SDLK_m = 109,
|
||||
SDLK_n = 110,
|
||||
SDLK_o = 111,
|
||||
SDLK_p = 112,
|
||||
SDLK_q = 113,
|
||||
SDLK_r = 114,
|
||||
SDLK_s = 115,
|
||||
SDLK_t = 116,
|
||||
SDLK_u = 117,
|
||||
SDLK_v = 118,
|
||||
SDLK_w = 119,
|
||||
SDLK_x = 120,
|
||||
SDLK_y = 121,
|
||||
SDLK_z = 122,
|
||||
SDLK_DELETE = 127,
|
||||
/* End of ASCII mapped keysyms */
|
||||
|
||||
/* International keyboard syms */
|
||||
SDLK_WORLD_0 = 160, /* 0xA0 */
|
||||
SDLK_WORLD_1 = 161,
|
||||
SDLK_WORLD_2 = 162,
|
||||
SDLK_WORLD_3 = 163,
|
||||
SDLK_WORLD_4 = 164,
|
||||
SDLK_WORLD_5 = 165,
|
||||
SDLK_WORLD_6 = 166,
|
||||
SDLK_WORLD_7 = 167,
|
||||
SDLK_WORLD_8 = 168,
|
||||
SDLK_WORLD_9 = 169,
|
||||
SDLK_WORLD_10 = 170,
|
||||
SDLK_WORLD_11 = 171,
|
||||
SDLK_WORLD_12 = 172,
|
||||
SDLK_WORLD_13 = 173,
|
||||
SDLK_WORLD_14 = 174,
|
||||
SDLK_WORLD_15 = 175,
|
||||
SDLK_WORLD_16 = 176,
|
||||
SDLK_WORLD_17 = 177,
|
||||
SDLK_WORLD_18 = 178,
|
||||
SDLK_WORLD_19 = 179,
|
||||
SDLK_WORLD_20 = 180,
|
||||
SDLK_WORLD_21 = 181,
|
||||
SDLK_WORLD_22 = 182,
|
||||
SDLK_WORLD_23 = 183,
|
||||
SDLK_WORLD_24 = 184,
|
||||
SDLK_WORLD_25 = 185,
|
||||
SDLK_WORLD_26 = 186,
|
||||
SDLK_WORLD_27 = 187,
|
||||
SDLK_WORLD_28 = 188,
|
||||
SDLK_WORLD_29 = 189,
|
||||
SDLK_WORLD_30 = 190,
|
||||
SDLK_WORLD_31 = 191,
|
||||
SDLK_WORLD_32 = 192,
|
||||
SDLK_WORLD_33 = 193,
|
||||
SDLK_WORLD_34 = 194,
|
||||
SDLK_WORLD_35 = 195,
|
||||
SDLK_WORLD_36 = 196,
|
||||
SDLK_WORLD_37 = 197,
|
||||
SDLK_WORLD_38 = 198,
|
||||
SDLK_WORLD_39 = 199,
|
||||
SDLK_WORLD_40 = 200,
|
||||
SDLK_WORLD_41 = 201,
|
||||
SDLK_WORLD_42 = 202,
|
||||
SDLK_WORLD_43 = 203,
|
||||
SDLK_WORLD_44 = 204,
|
||||
SDLK_WORLD_45 = 205,
|
||||
SDLK_WORLD_46 = 206,
|
||||
SDLK_WORLD_47 = 207,
|
||||
SDLK_WORLD_48 = 208,
|
||||
SDLK_WORLD_49 = 209,
|
||||
SDLK_WORLD_50 = 210,
|
||||
SDLK_WORLD_51 = 211,
|
||||
SDLK_WORLD_52 = 212,
|
||||
SDLK_WORLD_53 = 213,
|
||||
SDLK_WORLD_54 = 214,
|
||||
SDLK_WORLD_55 = 215,
|
||||
SDLK_WORLD_56 = 216,
|
||||
SDLK_WORLD_57 = 217,
|
||||
SDLK_WORLD_58 = 218,
|
||||
SDLK_WORLD_59 = 219,
|
||||
SDLK_WORLD_60 = 220,
|
||||
SDLK_WORLD_61 = 221,
|
||||
SDLK_WORLD_62 = 222,
|
||||
SDLK_WORLD_63 = 223,
|
||||
SDLK_WORLD_64 = 224,
|
||||
SDLK_WORLD_65 = 225,
|
||||
SDLK_WORLD_66 = 226,
|
||||
SDLK_WORLD_67 = 227,
|
||||
SDLK_WORLD_68 = 228,
|
||||
SDLK_WORLD_69 = 229,
|
||||
SDLK_WORLD_70 = 230,
|
||||
SDLK_WORLD_71 = 231,
|
||||
SDLK_WORLD_72 = 232,
|
||||
SDLK_WORLD_73 = 233,
|
||||
SDLK_WORLD_74 = 234,
|
||||
SDLK_WORLD_75 = 235,
|
||||
SDLK_WORLD_76 = 236,
|
||||
SDLK_WORLD_77 = 237,
|
||||
SDLK_WORLD_78 = 238,
|
||||
SDLK_WORLD_79 = 239,
|
||||
SDLK_WORLD_80 = 240,
|
||||
SDLK_WORLD_81 = 241,
|
||||
SDLK_WORLD_82 = 242,
|
||||
SDLK_WORLD_83 = 243,
|
||||
SDLK_WORLD_84 = 244,
|
||||
SDLK_WORLD_85 = 245,
|
||||
SDLK_WORLD_86 = 246,
|
||||
SDLK_WORLD_87 = 247,
|
||||
SDLK_WORLD_88 = 248,
|
||||
SDLK_WORLD_89 = 249,
|
||||
SDLK_WORLD_90 = 250,
|
||||
SDLK_WORLD_91 = 251,
|
||||
SDLK_WORLD_92 = 252,
|
||||
SDLK_WORLD_93 = 253,
|
||||
SDLK_WORLD_94 = 254,
|
||||
SDLK_WORLD_95 = 255, /* 0xFF */
|
||||
|
||||
/* Numeric keypad */
|
||||
SDLK_KP0 = 256,
|
||||
SDLK_KP1 = 257,
|
||||
SDLK_KP2 = 258,
|
||||
SDLK_KP3 = 259,
|
||||
SDLK_KP4 = 260,
|
||||
SDLK_KP5 = 261,
|
||||
SDLK_KP6 = 262,
|
||||
SDLK_KP7 = 263,
|
||||
SDLK_KP8 = 264,
|
||||
SDLK_KP9 = 265,
|
||||
SDLK_KP_PERIOD = 266,
|
||||
SDLK_KP_DIVIDE = 267,
|
||||
SDLK_KP_MULTIPLY = 268,
|
||||
SDLK_KP_MINUS = 269,
|
||||
SDLK_KP_PLUS = 270,
|
||||
SDLK_KP_ENTER = 271,
|
||||
SDLK_KP_EQUALS = 272,
|
||||
|
||||
/* Arrows + Home/End pad */
|
||||
SDLK_UP = 273,
|
||||
SDLK_DOWN = 274,
|
||||
SDLK_RIGHT = 275,
|
||||
SDLK_LEFT = 276,
|
||||
SDLK_INSERT = 277,
|
||||
SDLK_HOME = 278,
|
||||
SDLK_END = 279,
|
||||
SDLK_PAGEUP = 280,
|
||||
SDLK_PAGEDOWN = 281,
|
||||
|
||||
/* Function keys */
|
||||
SDLK_F1 = 282,
|
||||
SDLK_F2 = 283,
|
||||
SDLK_F3 = 284,
|
||||
SDLK_F4 = 285,
|
||||
SDLK_F5 = 286,
|
||||
SDLK_F6 = 287,
|
||||
SDLK_F7 = 288,
|
||||
SDLK_F8 = 289,
|
||||
SDLK_F9 = 290,
|
||||
SDLK_F10 = 291,
|
||||
SDLK_F11 = 292,
|
||||
SDLK_F12 = 293,
|
||||
SDLK_F13 = 294,
|
||||
SDLK_F14 = 295,
|
||||
SDLK_F15 = 296,
|
||||
|
||||
/* Key state modifier keys */
|
||||
SDLK_NUMLOCK = 300,
|
||||
SDLK_CAPSLOCK = 301,
|
||||
SDLK_SCROLLOCK = 302,
|
||||
SDLK_RSHIFT = 303,
|
||||
SDLK_LSHIFT = 304,
|
||||
SDLK_RCTRL = 305,
|
||||
SDLK_LCTRL = 306,
|
||||
SDLK_RALT = 307,
|
||||
SDLK_LALT = 308,
|
||||
SDLK_RMETA = 309,
|
||||
SDLK_LMETA = 310,
|
||||
SDLK_LSUPER = 311, /* Left "Windows" key */
|
||||
SDLK_RSUPER = 312, /* Right "Windows" key */
|
||||
SDLK_MODE = 313, /* "Alt Gr" key */
|
||||
SDLK_COMPOSE = 314, /* Multi-key compose key */
|
||||
|
||||
/* Miscellaneous function keys */
|
||||
SDLK_HELP = 315,
|
||||
SDLK_PRINT = 316,
|
||||
SDLK_SYSREQ = 317,
|
||||
SDLK_BREAK = 318,
|
||||
SDLK_MENU = 319,
|
||||
SDLK_POWER = 320, /* Power Macintosh power key */
|
||||
SDLK_EURO = 321, /* Some european keyboards */
|
||||
SDLK_UNDO = 322, /* Atari keyboard has Undo */
|
||||
|
||||
/* Add any other keys here */
|
||||
|
||||
SDLK_LAST
|
||||
} SDLKey;
|
||||
|
||||
/* Enumeration of valid key mods (possibly OR'd together) */
|
||||
typedef enum {
|
||||
KMOD_NONE = 0x0000,
|
||||
KMOD_LSHIFT= 0x0001,
|
||||
KMOD_RSHIFT= 0x0002,
|
||||
KMOD_LCTRL = 0x0040,
|
||||
KMOD_RCTRL = 0x0080,
|
||||
KMOD_LALT = 0x0100,
|
||||
KMOD_RALT = 0x0200,
|
||||
KMOD_LMETA = 0x0400,
|
||||
KMOD_RMETA = 0x0800,
|
||||
KMOD_NUM = 0x1000,
|
||||
KMOD_CAPS = 0x2000,
|
||||
KMOD_MODE = 0x4000,
|
||||
KMOD_RESERVED = 0x8000
|
||||
} SDLMod;
|
||||
|
||||
#define KMOD_CTRL (KMOD_LCTRL|KMOD_RCTRL)
|
||||
#define KMOD_SHIFT (KMOD_LSHIFT|KMOD_RSHIFT)
|
||||
#define KMOD_ALT (KMOD_LALT|KMOD_RALT)
|
||||
#define KMOD_META (KMOD_LMETA|KMOD_RMETA)
|
||||
|
||||
#endif /* _SDL_keysym_h */
|
@ -1,81 +0,0 @@
|
||||
/*
|
||||
SDL - Simple DirectMedia Layer
|
||||
Copyright (C) 1997-2004 Sam Lantinga
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Sam Lantinga
|
||||
slouken@libsdl.org
|
||||
*/
|
||||
|
||||
#ifdef SAVE_RCSID
|
||||
static char rcsid =
|
||||
"@(#) $Id$";
|
||||
#endif
|
||||
|
||||
#ifndef VK_0
|
||||
#define VK_0 '0'
|
||||
#define VK_1 '1'
|
||||
#define VK_2 '2'
|
||||
#define VK_3 '3'
|
||||
#define VK_4 '4'
|
||||
#define VK_5 '5'
|
||||
#define VK_6 '6'
|
||||
#define VK_7 '7'
|
||||
#define VK_8 '8'
|
||||
#define VK_9 '9'
|
||||
#define VK_A 'A'
|
||||
#define VK_B 'B'
|
||||
#define VK_C 'C'
|
||||
#define VK_D 'D'
|
||||
#define VK_E 'E'
|
||||
#define VK_F 'F'
|
||||
#define VK_G 'G'
|
||||
#define VK_H 'H'
|
||||
#define VK_I 'I'
|
||||
#define VK_J 'J'
|
||||
#define VK_K 'K'
|
||||
#define VK_L 'L'
|
||||
#define VK_M 'M'
|
||||
#define VK_N 'N'
|
||||
#define VK_O 'O'
|
||||
#define VK_P 'P'
|
||||
#define VK_Q 'Q'
|
||||
#define VK_R 'R'
|
||||
#define VK_S 'S'
|
||||
#define VK_T 'T'
|
||||
#define VK_U 'U'
|
||||
#define VK_V 'V'
|
||||
#define VK_W 'W'
|
||||
#define VK_X 'X'
|
||||
#define VK_Y 'Y'
|
||||
#define VK_Z 'Z'
|
||||
#endif /* VK_0 */
|
||||
|
||||
#ifndef VK_OEM_1
|
||||
#define VK_OEM_1 0xba
|
||||
#define VK_OEM_2 0xbf
|
||||
#define VK_OEM_3 0xc0
|
||||
#define VK_OEM_4 0xdb
|
||||
#define VK_OEM_5 0xdc
|
||||
#define VK_OEM_6 0xdd
|
||||
#define VK_OEM_7 0xde
|
||||
#define VK_OEM_8 0xdf
|
||||
#define VK_OEM_PLUS 0xbb
|
||||
#define VK_OEM_COMMA 0xbc
|
||||
#define VK_OEM_MINUS 0xbd
|
||||
#define VK_OEM_PERIOD 0xbe
|
||||
#endif
|
||||
|
@ -47,7 +47,8 @@
|
||||
#endif
|
||||
|
||||
|
||||
// automatic module shutdown (before process termination)
|
||||
#pragma data_seg(WIN_CALLBACK_PRE_LIBC(b))
|
||||
WIN_REGISTER_FUNC(wdbg_sym_init);
|
||||
#pragma data_seg(WIN_CALLBACK_POST_ATEXIT(b))
|
||||
WIN_REGISTER_FUNC(wdbg_sym_shutdown);
|
||||
#pragma data_seg()
|
||||
@ -225,6 +226,7 @@ LibError debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char* file,
|
||||
// stack walk
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
static VOID (*pRtlCaptureContext)(PCONTEXT*);
|
||||
|
||||
/*
|
||||
Subroutine linkage example code:
|
||||
@ -354,12 +356,7 @@ static LibError walk_stack(StackFrameCallback cb, void* user_arg = 0, uint skip
|
||||
#if CPU_IA32
|
||||
ia32_get_current_context(&context);
|
||||
#else
|
||||
// try to import RtlCaptureContext (available on WinXP and later)
|
||||
// .. note: kernel32 is always loaded into every process, so we
|
||||
// don't need LoadLibrary/FreeLibrary.
|
||||
HMODULE hKernel32Dll = GetModuleHandle("kernel32.dll");
|
||||
VOID (*pRtlCaptureContext)(PCONTEXT*);
|
||||
*(void**)&pRtlCaptureContext = GetProcAddress(hKernel32Dll, "RtlCaptureContext");
|
||||
// preferred implementation (was imported during module init)
|
||||
if(pRtlCaptureContext)
|
||||
pRtlCaptureContext(&context);
|
||||
// not available: raise+handle an exception; grab the reported context.
|
||||
@ -524,7 +521,7 @@ static wchar_t* out_pos;
|
||||
// new position exceeds the limit and aborts if so.
|
||||
// slight wrinkle: since we don't want each level of UDTs to successively
|
||||
// realize the limit has been hit and display the error message, we
|
||||
// return ERR_SYM_SINGLE_SYMBOL_LIMIT once and thereafter ERR_SYM_SUPPRESS_OUTPUT.
|
||||
// return ERR_SYM_SINGLE_SYMBOL_LIMIT once and thereafter INFO_SYM_SUPPRESS_OUTPUT.
|
||||
//
|
||||
// * example: local variables, as opposed to child symbols in a UDT.
|
||||
static wchar_t* out_latched_pos;
|
||||
@ -609,7 +606,7 @@ static void out_latch_pos()
|
||||
static LibError out_check_limit()
|
||||
{
|
||||
if(out_have_warned_of_limit)
|
||||
return ERR_SYM_SUPPRESS_OUTPUT; // NOWARN
|
||||
return INFO_SYM_SUPPRESS_OUTPUT;
|
||||
if(out_pos - out_latched_pos > 3000) // ~30 lines
|
||||
{
|
||||
out_have_warned_of_limit = true;
|
||||
@ -736,7 +733,7 @@ static void dump_error(LibError err, const u8* p)
|
||||
case ERR_SYM_INTERNAL_ERROR:
|
||||
out(L"(unavailable - internal error)\r\n");
|
||||
break;
|
||||
case ERR_SYM_SUPPRESS_OUTPUT:
|
||||
case INFO_SYM_SUPPRESS_OUTPUT:
|
||||
// not an error; do not output anything. handled by caller.
|
||||
break;
|
||||
default:
|
||||
@ -840,7 +837,7 @@ static LibError dump_sequence(DebugIterator el_iterator, void* internal,
|
||||
|
||||
// there was no output for this child; undo its indentation (if any),
|
||||
// skip everything below and proceed with the next child.
|
||||
if(err == ERR_SYM_SUPPRESS_OUTPUT)
|
||||
if(err == INFO_SYM_SUPPRESS_OUTPUT)
|
||||
{
|
||||
if(!fits_on_one_line)
|
||||
UNINDENT;
|
||||
@ -1276,7 +1273,7 @@ static LibError dump_sym_enum(DWORD type_id, const u8* p, DumpState UNUSED(state
|
||||
static LibError dump_sym_function(DWORD UNUSED(type_id), const u8* UNUSED(p),
|
||||
DumpState UNUSED(state))
|
||||
{
|
||||
return ERR_SYM_SUPPRESS_OUTPUT; // NOWARN
|
||||
return INFO_SYM_SUPPRESS_OUTPUT;
|
||||
}
|
||||
|
||||
|
||||
@ -1624,7 +1621,7 @@ static LibError udt_dump_normal(const wchar_t* type_name, const u8* p, size_t si
|
||||
|
||||
// there was no output for this child; undo its indentation (if any),
|
||||
// skip everything below and proceed with the next child.
|
||||
if(err == ERR_SYM_SUPPRESS_OUTPUT)
|
||||
if(err == INFO_SYM_SUPPRESS_OUTPUT)
|
||||
{
|
||||
if(!fits_on_one_line)
|
||||
UNINDENT;
|
||||
@ -1710,7 +1707,7 @@ done:
|
||||
static LibError dump_sym_vtable(DWORD UNUSED(type_id), const u8* UNUSED(p), DumpState UNUSED(state))
|
||||
{
|
||||
// unsupported (vtable internals are undocumented; too much work).
|
||||
return ERR_SYM_SUPPRESS_OUTPUT; // NOWARN
|
||||
return INFO_SYM_SUPPRESS_OUTPUT;
|
||||
}
|
||||
|
||||
|
||||
@ -1790,7 +1787,7 @@ static BOOL CALLBACK dump_sym_cb(SYMBOL_INFO* sym, ULONG UNUSED(size), void* UNU
|
||||
INDENT;
|
||||
LibError err = dump_sym(sym->Index, p, state);
|
||||
dump_error(err, p);
|
||||
if(err == ERR_SYM_SUPPRESS_OUTPUT)
|
||||
if(err == INFO_SYM_SUPPRESS_OUTPUT)
|
||||
UNINDENT;
|
||||
else
|
||||
out(L"\r\n");
|
||||
@ -1932,6 +1929,20 @@ fail:
|
||||
|
||||
|
||||
|
||||
static LibError wdbg_sym_init()
|
||||
{
|
||||
// try to import RtlCaptureContext (available on WinXP and later).
|
||||
// it's used in walk_stack; import here to avoid overhead of doing so
|
||||
// on every call. if not available, walk_stack emulates it.
|
||||
//
|
||||
// note: kernel32 is always loaded into every process, so we
|
||||
// don't need LoadLibrary/FreeLibrary.
|
||||
HMODULE hKernel32Dll = GetModuleHandle("kernel32.dll");
|
||||
*(void**)&pRtlCaptureContext = GetProcAddress(hKernel32Dll, "RtlCaptureContext");
|
||||
|
||||
return INFO_OK;
|
||||
}
|
||||
|
||||
|
||||
static LibError wdbg_sym_shutdown()
|
||||
{
|
||||
|
@ -35,7 +35,6 @@
|
||||
#include "lib/lib.h"
|
||||
#include "lib/posix.h"
|
||||
#include "lib/ogl.h" // needed to pull in the delay-loaded opengl32.dll
|
||||
#include "SDL_vkeys.h"
|
||||
|
||||
|
||||
// for easy removal of DirectDraw dependency (used to query total video mem)
|
||||
@ -574,6 +573,10 @@ static void queue_key_event(uint type, uint sdlk, WCHAR unicode_char)
|
||||
|
||||
static Uint8 keys[SDLK_LAST];
|
||||
|
||||
// winuser.h promises VK_0 and VK_A etc. match ASCII value.
|
||||
#define VK_0 '0'
|
||||
#define VK_A 'A'
|
||||
|
||||
static void init_vkmap(SDLKey (&VK_keymap)[256])
|
||||
{
|
||||
int i;
|
||||
|
@ -24,7 +24,7 @@
|
||||
#define WSDL_H__
|
||||
|
||||
#include "lib/types.h"
|
||||
#include "SDL_keysym.h"
|
||||
#include "SDL/SDL_keysym.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -547,19 +547,20 @@ LibError sys_cursor_free(void* cursor)
|
||||
// misc
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// OS-specific backend for error_description_r.
|
||||
// NB: it is expected to be rare that OS return/error codes are actually
|
||||
// seen by user code, but we still translate them for completeness.
|
||||
LibError sys_error_description_r(int err, char* buf, size_t max_chars)
|
||||
LibError sys_error_description_r(int user_err, char* buf, size_t max_chars)
|
||||
{
|
||||
DWORD err = (DWORD)user_err;
|
||||
// not in our range (Win32 error numbers are positive)
|
||||
if(err < 0)
|
||||
if(user_err < 0)
|
||||
return ERR_FAIL; // NOWARN
|
||||
// user doesn't know error code; get current error state
|
||||
if(!user_err)
|
||||
err = GetLastError();
|
||||
|
||||
const LPCVOID source = 0; // ignored (we're not using FROM_HMODULE etc.)
|
||||
const DWORD lang_id = 0; // look for neutral, then current locale
|
||||
va_list* args = 0; // we don't care about "inserts"
|
||||
DWORD chars_output = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, source, (DWORD)err,
|
||||
DWORD chars_output = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, source, err,
|
||||
lang_id, buf, (DWORD)max_chars, args);
|
||||
if(!chars_output)
|
||||
WARN_RETURN(ERR_FAIL);
|
||||
|
@ -73,108 +73,3 @@ public:
|
||||
TS_ASSERT(equal(begin, end, deq.begin()));
|
||||
}
|
||||
};
|
||||
|
||||
class TestCache: public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_cache_perf()
|
||||
{
|
||||
Cache<int, int, Landlord_Naive> c1;
|
||||
Cache<int, int, Landlord_Naive, Divider_Recip> c1r;
|
||||
Cache<int, int, Landlord_Cached> c2;
|
||||
Cache<int, int, Landlord_Cached, Divider_Recip> c2r;
|
||||
Cache<int, int, Landlord_Lazy> c3;
|
||||
Cache<int, int, Landlord_Lazy, Divider_Recip> c3r;
|
||||
|
||||
#if defined(ENABLE_CACHE_POLICY_BENCHMARK) || 0
|
||||
// set max priority, to reduce interference while measuring.
|
||||
int old_policy; static sched_param old_param; // (static => 0-init)
|
||||
pthread_getschedparam(pthread_self(), &old_policy, &old_param);
|
||||
static sched_param max_param;
|
||||
max_param.sched_priority = sched_get_priority_max(SCHED_FIFO);
|
||||
pthread_setschedparam(pthread_self(), SCHED_FIFO, &max_param);
|
||||
|
||||
#define MEASURE(c, desc)\
|
||||
{\
|
||||
srand(1);\
|
||||
int cnt = 1;\
|
||||
TIMER_BEGIN(desc);\
|
||||
for(int i = 0; i < 30000; i++)\
|
||||
{\
|
||||
/* 70% add (random objects) */\
|
||||
bool add = rand(1,10) < 7;\
|
||||
if(add)\
|
||||
{\
|
||||
int key = cnt++;\
|
||||
int val = cnt++;\
|
||||
size_t size = (size_t)rand(1,100);\
|
||||
uint cost = (uint)rand(1,100);\
|
||||
c.add(key, val, size, cost);\
|
||||
}\
|
||||
else\
|
||||
{\
|
||||
size_t size;\
|
||||
int value;\
|
||||
c.remove_least_valuable(&value, &size);\
|
||||
}\
|
||||
}\
|
||||
TIMER_END(desc);\
|
||||
}
|
||||
MEASURE(c1, "naive")
|
||||
MEASURE(c1r, "naiverecip")
|
||||
MEASURE(c2, "cached")
|
||||
MEASURE(c2r, "cachedrecip")
|
||||
MEASURE(c3, "lazy")
|
||||
MEASURE(c3r, "lazyrecip")
|
||||
|
||||
// restore previous policy and priority.
|
||||
pthread_setschedparam(pthread_self(), old_policy, &old_param);
|
||||
exit(1134);
|
||||
#endif
|
||||
}
|
||||
|
||||
// ensures all 3 variants of Landlord<> behave the same
|
||||
void test_cache_policies()
|
||||
{
|
||||
Cache<int, int, Landlord_Naive > c1;
|
||||
Cache<int, int, Landlord_Cached> c2;
|
||||
Cache<int, int, Landlord_Lazy > c3;
|
||||
|
||||
srand(1);
|
||||
int cnt = 1;
|
||||
for(int i = 0; i < 1000; i++)
|
||||
{
|
||||
// 70% add (random objects)
|
||||
bool add = rand(1,10) < 7;
|
||||
if(add)
|
||||
{
|
||||
int key = cnt++;
|
||||
int val = cnt++;
|
||||
size_t size = (size_t)rand(1,100);
|
||||
uint cost = (uint)rand(1,100);
|
||||
c1.add(key, val, size, cost);
|
||||
c2.add(key, val, size, cost);
|
||||
c3.add(key, val, size, cost);
|
||||
}
|
||||
// 30% delete - make sure "least valuable" was same for all
|
||||
else
|
||||
{
|
||||
size_t size1, size2, size3;
|
||||
int value1, value2, value3;
|
||||
bool removed1, removed2, removed3;
|
||||
removed1 = c1.remove_least_valuable(&value1, &size1);
|
||||
removed2 = c2.remove_least_valuable(&value2, &size2);
|
||||
removed3 = c3.remove_least_valuable(&value3, &size3);
|
||||
TS_ASSERT_EQUALS(removed1, removed2);
|
||||
TS_ASSERT_EQUALS(removed2, removed3);
|
||||
if (removed1)
|
||||
{
|
||||
TS_ASSERT_EQUALS(size1, size2);
|
||||
TS_ASSERT_EQUALS(value1, value2);
|
||||
TS_ASSERT_EQUALS(size2, size3);
|
||||
TS_ASSERT_EQUALS(value2, value3);
|
||||
}
|
||||
} // else
|
||||
} // for i
|
||||
}
|
||||
};
|
||||
|
108
source/lib/tests/test_cache_adt.h
Normal file
108
source/lib/tests/test_cache_adt.h
Normal file
@ -0,0 +1,108 @@
|
||||
#include "lib/self_test.h"
|
||||
|
||||
#include "lib/cache_adt.h"
|
||||
|
||||
class TestCache: public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_cache_perf()
|
||||
{
|
||||
Cache<int, int, Landlord_Naive> c1;
|
||||
Cache<int, int, Landlord_Naive, Divider_Recip> c1r;
|
||||
Cache<int, int, Landlord_Cached> c2;
|
||||
Cache<int, int, Landlord_Cached, Divider_Recip> c2r;
|
||||
Cache<int, int, Landlord_Lazy> c3;
|
||||
Cache<int, int, Landlord_Lazy, Divider_Recip> c3r;
|
||||
|
||||
#if defined(ENABLE_CACHE_POLICY_BENCHMARK) || 0
|
||||
// set max priority, to reduce interference while measuring.
|
||||
int old_policy; static sched_param old_param; // (static => 0-init)
|
||||
pthread_getschedparam(pthread_self(), &old_policy, &old_param);
|
||||
static sched_param max_param;
|
||||
max_param.sched_priority = sched_get_priority_max(SCHED_FIFO);
|
||||
pthread_setschedparam(pthread_self(), SCHED_FIFO, &max_param);
|
||||
|
||||
#define MEASURE(c, desc)\
|
||||
{\
|
||||
srand(1);\
|
||||
int cnt = 1;\
|
||||
TIMER_BEGIN(desc);\
|
||||
for(int i = 0; i < 30000; i++)\
|
||||
{\
|
||||
/* 70% add (random objects) */\
|
||||
bool add = rand(1,10) < 7;\
|
||||
if(add)\
|
||||
{\
|
||||
int key = cnt++;\
|
||||
int val = cnt++;\
|
||||
size_t size = (size_t)rand(1,100);\
|
||||
uint cost = (uint)rand(1,100);\
|
||||
c.add(key, val, size, cost);\
|
||||
}\
|
||||
else\
|
||||
{\
|
||||
size_t size;\
|
||||
int value;\
|
||||
c.remove_least_valuable(&value, &size);\
|
||||
}\
|
||||
}\
|
||||
TIMER_END(desc);\
|
||||
}
|
||||
MEASURE(c1, "naive")
|
||||
MEASURE(c1r, "naiverecip")
|
||||
MEASURE(c2, "cached")
|
||||
MEASURE(c2r, "cachedrecip")
|
||||
MEASURE(c3, "lazy")
|
||||
MEASURE(c3r, "lazyrecip")
|
||||
|
||||
// restore previous policy and priority.
|
||||
pthread_setschedparam(pthread_self(), old_policy, &old_param);
|
||||
exit(1134);
|
||||
#endif
|
||||
}
|
||||
|
||||
// ensures all 3 variants of Landlord<> behave the same
|
||||
void test_cache_policies()
|
||||
{
|
||||
Cache<int, int, Landlord_Naive > c1;
|
||||
Cache<int, int, Landlord_Cached> c2;
|
||||
Cache<int, int, Landlord_Lazy > c3;
|
||||
|
||||
srand(1);
|
||||
int cnt = 1;
|
||||
for(int i = 0; i < 1000; i++)
|
||||
{
|
||||
// 70% add (random objects)
|
||||
bool add = rand(1,10) < 7;
|
||||
if(add)
|
||||
{
|
||||
int key = cnt++;
|
||||
int val = cnt++;
|
||||
size_t size = (size_t)rand(1,100);
|
||||
uint cost = (uint)rand(1,100);
|
||||
c1.add(key, val, size, cost);
|
||||
c2.add(key, val, size, cost);
|
||||
c3.add(key, val, size, cost);
|
||||
}
|
||||
// 30% delete - make sure "least valuable" was same for all
|
||||
else
|
||||
{
|
||||
size_t size1, size2, size3;
|
||||
int value1, value2, value3;
|
||||
bool removed1, removed2, removed3;
|
||||
removed1 = c1.remove_least_valuable(&value1, &size1);
|
||||
removed2 = c2.remove_least_valuable(&value2, &size2);
|
||||
removed3 = c3.remove_least_valuable(&value3, &size3);
|
||||
TS_ASSERT_EQUALS(removed1, removed2);
|
||||
TS_ASSERT_EQUALS(removed2, removed3);
|
||||
if (removed1)
|
||||
{
|
||||
TS_ASSERT_EQUALS(size1, size2);
|
||||
TS_ASSERT_EQUALS(value1, value2);
|
||||
TS_ASSERT_EQUALS(size2, size3);
|
||||
TS_ASSERT_EQUALS(value2, value3);
|
||||
}
|
||||
} // else
|
||||
} // for i
|
||||
}
|
||||
};
|
@ -30,8 +30,9 @@ class TestString_s : public CxxTest::TestSuite
|
||||
|
||||
|
||||
static void TEST_LEN(const char* string, size_t limit,
|
||||
size_t expected_len)
|
||||
{
|
||||
TS_ASSERT_EQUALS(strnlen((string), (limit)), (expected));
|
||||
TS_ASSERT_EQUALS(strnlen((string), (limit)), (expected_len));
|
||||
}
|
||||
|
||||
static void TEST_CPY(char* dst, size_t dst_max, const char* src,
|
||||
@ -46,7 +47,7 @@ class TestString_s : public CxxTest::TestSuite
|
||||
static void TEST_CPY2(char* dst, size_t max_dst_chars, const char* src,
|
||||
int expected_ret, const char* expected_dst)
|
||||
{
|
||||
int ret = strcpy_s((dst), ARRAY_SIZE(dst), (src));
|
||||
int ret = strcpy_s((dst), max_dst_chars, (src));
|
||||
TS_ASSERT_EQUALS(ret, expected_ret);
|
||||
if(dst != 0)
|
||||
TS_ASSERT(!strcmp(dst, expected_dst));
|
||||
@ -62,7 +63,7 @@ class TestString_s : public CxxTest::TestSuite
|
||||
}
|
||||
|
||||
static void TEST_CAT(char* dst, size_t max_dst_chars, const char* src,
|
||||
int expected_ret, const char expected_dst)
|
||||
int expected_ret, const char* expected_dst)
|
||||
{
|
||||
int ret = strcat_s(dst, max_dst_chars, src);
|
||||
TS_ASSERT_EQUALS(ret, expected_ret);
|
||||
@ -186,7 +187,7 @@ public:
|
||||
|
||||
void test_concatenate()
|
||||
{
|
||||
TEST_CAT2(d3,3, s1, ,"1",0,"1a");
|
||||
TEST_CAT2(d3,3, s1, "1",0,"1a");
|
||||
TEST_CAT2(d5,5, s1, "1",0,"1a");
|
||||
TEST_CAT2(d6,6, s5, "",0,"abcde");
|
||||
TEST_CAT2(d10,10, s5, "",0,"abcde");
|
||||
|
@ -49,6 +49,13 @@ extern float spf; // for time-since-last-frame use
|
||||
extern void calc_fps(void);
|
||||
|
||||
|
||||
//
|
||||
// cumulative timer API
|
||||
//
|
||||
|
||||
// this supplements in-game profiling by providing low-overhead,
|
||||
// high resolution time accounting.
|
||||
|
||||
// since TIMER_ACCRUE et al. are called so often, we try to keep
|
||||
// overhead to an absolute minimum. this flag allows storing
|
||||
// raw tick counts (e.g. CPU cycles returned by ia32_rdtsc) instead of
|
||||
@ -73,15 +80,6 @@ typedef i64 TimerUnit;
|
||||
typedef double TimerUnit;
|
||||
#endif
|
||||
|
||||
|
||||
//
|
||||
// cumulative timer API
|
||||
//
|
||||
|
||||
// this supplements in-game profiling by providing low-overhead,
|
||||
// high resolution time accounting.
|
||||
|
||||
|
||||
// opaque - do not access its fields!
|
||||
// note: must be defined here because clients instantiate them;
|
||||
// fields cannot be made private due to C compatibility requirement.
|
||||
@ -202,44 +200,60 @@ Example usage:
|
||||
#define TIMER_END(description) }
|
||||
|
||||
|
||||
// used via TIMER_ACCRUE
|
||||
class ScopeTimerAccrue
|
||||
#if TIMER_USE_RAW_TICKS
|
||||
|
||||
#if CPU_IA32
|
||||
// fast, not usable as wall-clock (http://www.gamedev.net/reference/programming/features/timing)
|
||||
class TimerRdtsc
|
||||
{
|
||||
TimerUnit t0;
|
||||
public:
|
||||
typedef i64 unit;
|
||||
unit get_timestamp() const
|
||||
{
|
||||
return ia32_rdtsc();
|
||||
}
|
||||
};
|
||||
#else
|
||||
# error "port"
|
||||
#endif // CPU_IA32
|
||||
|
||||
#else
|
||||
|
||||
class TimerNormal
|
||||
{
|
||||
public:
|
||||
typedef double unit;
|
||||
unit get_timestamp() const
|
||||
{
|
||||
return get_time();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // TIMER_USE_RAW_TICKS
|
||||
|
||||
|
||||
// used via TIMER_ACCRUE
|
||||
template<class TimerImpl = TimerRdtsc> class ScopeTimerAccrue
|
||||
{
|
||||
TimerImpl impl;
|
||||
typename TimerImpl::unit t0;
|
||||
TimerClient* tc;
|
||||
|
||||
public:
|
||||
ScopeTimerAccrue(TimerClient* tc_)
|
||||
ScopeTimerAccrue<TimerImpl>(TimerClient* tc_)
|
||||
{
|
||||
#if TIMER_USE_RAW_TICKS
|
||||
# if CPU_IA32
|
||||
t0 = ia32_rdtsc();
|
||||
# else
|
||||
# error "port"
|
||||
# endif
|
||||
#else
|
||||
t0 = get_time();
|
||||
#endif
|
||||
t0 = impl.get_timestamp();
|
||||
tc = tc_;
|
||||
}
|
||||
~ScopeTimerAccrue()
|
||||
~ScopeTimerAccrue<TimerImpl>()
|
||||
{
|
||||
#if TIMER_USE_RAW_TICKS
|
||||
# if CPU_IA32
|
||||
TimerUnit t1 = ia32_rdtsc();
|
||||
# else
|
||||
# error "port"
|
||||
# endif
|
||||
#else
|
||||
TimerUnit t1 = get_time();
|
||||
#endif
|
||||
TimerUnit dt = t1-t0;
|
||||
TimerImpl::unit dt = impl.get_timestamp() - t0;
|
||||
timer_bill_client(tc, dt);
|
||||
}
|
||||
|
||||
// disallow copying (makes no sense)
|
||||
private:
|
||||
ScopeTimerAccrue& operator=(const ScopeTimerAccrue&);
|
||||
ScopeTimerAccrue<TimerImpl>& operator=(const ScopeTimerAccrue<TimerImpl>&);
|
||||
};
|
||||
|
||||
|
||||
@ -272,6 +286,6 @@ Example usage:
|
||||
[at exit]
|
||||
timer_display_client_totals();
|
||||
*/
|
||||
#define TIMER_ACCRUE(client) ScopeTimerAccrue UID__(client)
|
||||
#define TIMER_ACCRUE(client) ScopeTimerAccrue<> UID__(client)
|
||||
|
||||
#endif // #ifndef TIMER_H
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "lib/ogl.h"
|
||||
#include "lib/timer.h"
|
||||
#include "lib/input.h"
|
||||
#include "lib/app_hooks.h"
|
||||
#include "lib/sysdep/cpu.h"
|
||||
#include "lib/sysdep/gfx.h"
|
||||
#include "lib/res/res.h"
|
||||
@ -915,6 +916,11 @@ void Init(int argc, char* argv[], uint flags)
|
||||
MICROLOG(L"init i18n");
|
||||
I18n::LoadLanguage(NULL);
|
||||
|
||||
// override ah_translate with our i18n code.
|
||||
AppHooks hooks = {0};
|
||||
hooks.translate = psTranslate;
|
||||
set_app_hooks(&hooks);
|
||||
|
||||
// Set up the console early, so that debugging
|
||||
// messages can be logged to it. (The console's size
|
||||
// and fonts are set later in InitPs())
|
||||
|
@ -1,6 +1,33 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "Pyrogenesis.h"
|
||||
#include "ps/i18n.h"
|
||||
|
||||
DEFINE_ERROR(PS_OK, "OK");
|
||||
DEFINE_ERROR(PS_FAIL, "Fail");
|
||||
|
||||
// overrides ah_translate. registered in GameSetup.cpp
|
||||
const wchar_t* psTranslate(const wchar_t* text)
|
||||
{
|
||||
// TODO: leaks memory returned by wcsdup
|
||||
|
||||
// make sure i18n system is (already|still) initialized.
|
||||
if(g_CurrentLocale)
|
||||
{
|
||||
// be prepared for this to fail, because translation potentially
|
||||
// involves script code and the JS context might be corrupted.
|
||||
try
|
||||
{
|
||||
CStrW ret = I18n::translate(text);
|
||||
const wchar_t* text2 = wcsdup(ret.c_str());
|
||||
// only overwrite if wcsdup succeeded, i.e. not out of memory.
|
||||
if(text2)
|
||||
text = text2;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
@ -22,4 +22,8 @@ DECLARE_ERROR(PS_FAIL);
|
||||
|
||||
#define MICROLOG debug_wprintf_mem
|
||||
|
||||
|
||||
// overrides ah_translate. registered in GameSetup.cpp
|
||||
extern const wchar_t* psTranslate(const wchar_t* text);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user