This commit is contained in:
Tim Felgentreff 2022-06-29 20:07:05 +02:00
commit 1dc013f0b1
14 changed files with 300 additions and 139 deletions

View file

@ -876,6 +876,8 @@ endif()
if(LINUX)
add_definitions(-DUSE_LINUX)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -ggdb")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -ggdb")
endif()
if(BEOS)

View file

@ -1137,7 +1137,13 @@ void AiForce::Update()
void AiForceManager::Update()
{
for (unsigned int f = 0; f < forces.size(); ++f) {
unsigned int fsize = forces.size();
int maxPathing = 2; // reduce load by stopping after issuing a few map searches
for (unsigned int f = 0; f < fsize; ++f) {
if (maxPathing < 0) {
return;
}
AiForce &force = forces[f];
// Look if our defenders still have enemies in range.
@ -1160,6 +1166,7 @@ void AiForceManager::Update()
if (force.Units[i]->MapDistanceTo(force.GoalPos) <= nearDist) {
// Look if still enemies in attack range.
const CUnit *dummy = NULL;
maxPathing--;
if (!AiForceEnemyFinder<AIATTACK_RANGE>(force, &dummy).found()) {
force.ReturnToHome();
}
@ -1175,6 +1182,7 @@ void AiForceManager::Update()
// Don't attack if there aren't our units near goal point
std::vector<CUnit *> nearGoal;
const Vec2i offset(15, 15);
maxPathing--;
Select(force.GoalPos - offset, force.GoalPos + offset, nearGoal,
IsAnAlliedUnitOf(*force.Units[0]->Player));
if (nearGoal.empty()) {
@ -1206,6 +1214,7 @@ void AiForceManager::Update()
}
} else if (force.Attacking) {
force.RemoveDeadUnit();
maxPathing--;
force.Update();
}
}

View file

@ -459,8 +459,8 @@ extern int AiFindWall(AiForce *force);
/// Plan the an attack
/// Send explorers around the map
extern void AiSendExplorers();
/// Enemy units in distance
extern int AiEnemyUnitsInDistance(const CPlayer &player, const CUnitType *type,
/// Check if there are enemy units in a given range (optionally of type)
extern bool AiEnemyUnitsInDistance(const CPlayer &player, const CUnitType *type,
const Vec2i &pos, unsigned range);
//

View file

@ -169,18 +169,20 @@ static int AiCheckUnitTypeCosts(const CUnitType &type)
return AiCheckCosts(type.Stats[AiPlayer->Player->Index].Costs);
}
template <bool ignoreVisibility = false>
class IsAEnemyUnitOf
{
public:
explicit IsAEnemyUnitOf(const CPlayer &_player) : player(&_player) {}
bool operator()(const CUnit *unit) const
{
return unit->IsVisibleAsGoal(*player) && unit->IsEnemy(*player);
return (ignoreVisibility || unit->IsVisibleAsGoal(*player)) && unit->IsEnemy(*player);
}
private:
const CPlayer *player;
};
template <bool ignoreVisibility = false>
class IsAEnemyUnitWhichCanCounterAttackOf
{
public:
@ -189,7 +191,7 @@ public:
{}
bool operator()(const CUnit *unit) const
{
return unit->IsVisibleAsGoal(*player)
return (ignoreVisibility || unit->IsVisibleAsGoal(*player))
&& unit->IsEnemy(*player)
&& CanTarget(*unit->Type, *type);
}
@ -199,42 +201,42 @@ private:
};
/**
** Enemy units in distance.
** Check if there are enemy units in a given range.
**
** @param player Find enemies of this player
** @param type Optional unit type to check if enemy can target this
** @param pos location
** @param range Distance range to look.
**
** @return Number of enemy units.
** @return If there are any enemy units in the range
*/
int AiEnemyUnitsInDistance(const CPlayer &player,
const CUnitType *type, const Vec2i &pos, unsigned range)
bool AiEnemyUnitsInDistance(const CPlayer &player,
const CUnitType *type, const Vec2i &pos, unsigned range)
{
const Vec2i offset(range, range);
std::vector<CUnit *> units;
if (type == NULL) {
Select(pos - offset, pos + offset, units, IsAEnemyUnitOf(player));
Select<1>(pos - offset, pos + offset, units, IsAEnemyUnitOf<true>(player));
return static_cast<int>(units.size());
} else {
const Vec2i typeSize(type->TileWidth - 1, type->TileHeight - 1);
const IsAEnemyUnitWhichCanCounterAttackOf pred(player, *type);
const IsAEnemyUnitWhichCanCounterAttackOf<true> pred(player, *type);
Select(pos - offset, pos + typeSize + offset, units, pred);
Select<1>(pos - offset, pos + typeSize + offset, units, pred);
return static_cast<int>(units.size());
}
}
/**
** Enemy units in distance.
** Check if there are enemy units in a given range.
**
** @param unit Find in distance for this unit.
** @param range Distance range to look.
**
** @return Number of enemy units.
** @return If there are any enemy units in the range
*/
int AiEnemyUnitsInDistance(const CUnit &unit, unsigned range)
bool AiEnemyUnitsInDistance(const CUnit &unit, unsigned range)
{
return AiEnemyUnitsInDistance(*unit.Player, unit.Type, unit.tilePos, range);
}

View file

@ -146,14 +146,8 @@ void StartMap(const std::string &filename, bool clean)
UI.StatusLine.Set(NameLine);
SetMessage("%s", _("Do it! Do it now!"));
long ticks = SDL_GetTicks();
// Play the game.
GameMainLoop();
if (Parameters::Instance.benchmark) {
ticks = SDL_GetTicks() - ticks;
double fps = FrameCounter * 1000.0 / ticks;
fprintf(stderr, "BENCHMARK RESULT: %f fps (%ldms for %ldframes)\n", fps, ticks, FrameCounter);
}
// Clear screen
Video.ClearScreen();

View file

@ -199,6 +199,8 @@ extern int AStarMovingUnitCrossingCost;
extern bool AStarKnowUnseenTerrain;
/// Cost of using a square we haven't seen before.
extern int AStarUnknownTerrainCost;
/// Maximum number of iterations of A* before giving up.
extern int AStarMaxSearchIterations;
//
// Convert heading into direction.

View file

@ -221,12 +221,14 @@ void Select(const Vec2i &ltPos, const Vec2i &rbPos, std::vector<CUnit *> &units)
void SelectFixed(const Vec2i &ltPos, const Vec2i &rbPos, std::vector<CUnit *> &units);
void SelectAroundUnit(const CUnit &unit, int range, std::vector<CUnit *> &around);
template <typename Pred>
template <int selectMax = 0, typename Pred>
void SelectFixed(const Vec2i &ltPos, const Vec2i &rbPos, std::vector<CUnit *> &units, Pred pred)
{
Assert(Map.Info.IsPointOnMap(ltPos));
Assert(Map.Info.IsPointOnMap(rbPos));
Assert(units.empty());
units.reserve(selectMax << 1);
int max = selectMax || INT_MAX;
for (Vec2i posIt = ltPos; posIt.y != rbPos.y + 1; ++posIt.y) {
for (posIt.x = ltPos.x; posIt.x != rbPos.x + 1; ++posIt.x) {
@ -236,9 +238,17 @@ void SelectFixed(const Vec2i &ltPos, const Vec2i &rbPos, std::vector<CUnit *> &u
for (size_t i = 0; i != cache.size(); ++i) {
CUnit &unit = *cache[i];
if (unit.CacheLock == 0 && pred(&unit)) {
unit.CacheLock = 1;
units.push_back(&unit);
if ((selectMax == 1 || unit.CacheLock == 0) && pred(&unit)) {
if (selectMax == 1) {
units.push_back(&unit);
return;
} else {
unit.CacheLock = 1;
units.push_back(&unit);
if (--max == 0) {
break;
}
}
}
}
}
@ -248,25 +258,25 @@ void SelectFixed(const Vec2i &ltPos, const Vec2i &rbPos, std::vector<CUnit *> &u
}
}
template <typename Pred>
void SelectAroundUnit(const CUnit &unit, int range, std::vector<CUnit *> &around, Pred pred)
{
const Vec2i offset(range, range);
const Vec2i typeSize(unit.Type->TileWidth - 1, unit.Type->TileHeight - 1);
Select(unit.tilePos - offset,
unit.tilePos + typeSize + offset, around,
MakeAndPredicate(IsNotTheSameUnitAs(unit), pred));
}
template <typename Pred>
template <int selectMax = 0, typename Pred>
void Select(const Vec2i &ltPos, const Vec2i &rbPos, std::vector<CUnit *> &units, Pred pred)
{
Vec2i minPos = ltPos;
Vec2i maxPos = rbPos;
Map.FixSelectionArea(minPos, maxPos);
SelectFixed(minPos, maxPos, units, pred);
SelectFixed<selectMax>(minPos, maxPos, units, pred);
}
template <int selectMax = 0, typename Pred>
void SelectAroundUnit(const CUnit &unit, int range, std::vector<CUnit *> &around, Pred pred)
{
const Vec2i offset(range, range);
const Vec2i typeSize(unit.Type->TileWidth - 1, unit.Type->TileHeight - 1);
Select<selectMax>(unit.tilePos - offset,
unit.tilePos + typeSize + offset, around,
MakeAndPredicate(IsNotTheSameUnitAs(unit), pred));
}
template <typename Pred>

View file

@ -142,6 +142,14 @@ void SetClipboard(std::string &str);
int UTF8GetNext(const std::string &text, int curpos);
int UTF8GetPrev(const std::string &text, int curpos);
/*----------------------------------------------------------------------------
-- SIMD support
----------------------------------------------------------------------------*/
bool supportsSSE2();
bool supportsAVX();
void *aligned_malloc(size_t alignment, size_t size);
void aligned_free(void *block);
//@}
#endif /* __UTIL_H__ */

View file

@ -51,10 +51,10 @@
----------------------------------------------------------------------------*/
struct Node {
int CostFromStart; /// Real costs to reach this point
short int CostToGoal; /// Estimated cost to goal
char InGoal; /// is this point in the goal
char Direction; /// Direction for trace back
int32_t CostFromStart; /// Real costs to reach this point
int16_t CostToGoal; /// Estimated cost to goal
int8_t InGoal; /// is this point in the goal
int8_t Direction; /// Direction for trace back
};
struct Open {
@ -88,9 +88,6 @@ const int XY2Heading[3][3] = { {7, 6, 5}, {0, 0, 4}, {1, 2, 3}};
static Node *AStarMatrix;
/// a list of close nodes, helps to speed up the matrix cleaning
static int *CloseSet;
static int CloseSetSize;
static int Threshold;
static int OpenSetMaxSize;
static int AStarMatrixSize;
#define MAX_CLOSE_SET_RATIO 4
@ -99,6 +96,7 @@ static int AStarMatrixSize;
/// see pathfinder.h
int AStarFixedUnitCrossingCost;// = MaxMapWidth * MaxMapHeight;
int AStarMovingUnitCrossingCost = 5;
int AStarMaxSearchIterations = INT_MAX;
bool AStarKnowUnseenTerrain = false;
int AStarUnknownTerrainCost = 2;
/// Used to temporary make enemy units unpassable (needs for correct path lenght calculating for automatic targeting alorithm)
@ -106,6 +104,7 @@ static bool AStarFixedEnemyUnitsUnpassable = false;
static int AStarMapWidth;
static int AStarMapHeight;
static int AStarMapMax;
static int AStarGoalX;
static int AStarGoalY;
@ -120,8 +119,9 @@ static Open *OpenSet;
/// The size of the open node set
static int OpenSetSize;
static int *CostMoveToCache;
static const int CacheNotSet = -5;
static int32_t *CostMoveToCache;
static int CostMoveToCacheSize;
static constexpr int CacheNotSet = -1;
/*----------------------------------------------------------------------------
-- Profile
@ -257,19 +257,23 @@ void InitAStar(int mapWidth, int mapHeight)
AStarMapWidth = mapWidth;
AStarMapHeight = mapHeight;
AStarMapMax = AStarMapWidth * AStarMapHeight;
AStarMatrixSize = sizeof(Node) * AStarMapWidth * AStarMapHeight;
AStarMatrix = new Node[AStarMapWidth * AStarMapHeight];
// align the matrix, the open set, and the cost to move cache
// on 64-byte boundary, in case the memset/memmove operations
// of the libc we're using has a 128/256/512bit SIMD vector
// instruction branch, since we might be clearing 8M of
// memory for a 2048x2048 map
AStarMatrixSize = sizeof(Node) * AStarMapMax;
AStarMatrix = (Node *)aligned_malloc(64, AStarMatrixSize);
memset(AStarMatrix, 0, AStarMatrixSize);
Threshold = AStarMapWidth * AStarMapHeight / MAX_CLOSE_SET_RATIO;
CloseSetSize = Threshold;
CloseSet = new int[Threshold];
OpenSetMaxSize = AStarMapMax / MAX_OPEN_SET_RATIO;
OpenSet = (Open *)aligned_malloc(64, OpenSetMaxSize * sizeof(Open));
OpenSetMaxSize = AStarMapWidth * AStarMapHeight / MAX_OPEN_SET_RATIO;
OpenSet = new Open[OpenSetMaxSize];
CostMoveToCache = new int[AStarMapWidth * AStarMapHeight];
CostMoveToCacheSize = sizeof(int32_t) * AStarMapMax;
CostMoveToCache = (int32_t*)aligned_malloc(64, CostMoveToCacheSize);
memset(CostMoveToCache, CacheNotSet, CostMoveToCacheSize);
for (int i = 0; i < 9; ++i) {
Heading2O[i] = Heading2Y[i] * AStarMapWidth;
@ -283,15 +287,12 @@ void InitAStar(int mapWidth, int mapHeight)
*/
void FreeAStar()
{
delete[] AStarMatrix;
aligned_free(AStarMatrix);
AStarMatrix = NULL;
delete[] CloseSet;
CloseSet = NULL;
CloseSetSize = 0;
delete[] OpenSet;
aligned_free(OpenSet);
OpenSet = NULL;
OpenSetSize = 0;
delete[] CostMoveToCache;
aligned_free(CostMoveToCache);
CostMoveToCache = NULL;
ProfilePrint();
@ -312,62 +313,14 @@ static void CostMoveToCacheCleanUp();
static void AStarCleanUp()
{
ProfileBegin("AStarCleanUp");
if (CloseSetSize >= Threshold) {
AStarPrepare();
CostMoveToCacheCleanUp();
} else {
for (int i = 0; i < CloseSetSize; ++i) {
AStarMatrix[CloseSet[i]].CostFromStart = 0;
AStarMatrix[CloseSet[i]].InGoal = 0;
CostMoveToCache[CloseSet[i]] = CacheNotSet;
}
}
AStarPrepare();
CostMoveToCacheCleanUp();
ProfileEnd("AStarCleanUp");
}
static void CostMoveToCacheCleanUp()
{
ProfileBegin("CostMoveToCacheCleanUp");
int AStarMapMax = AStarMapWidth * AStarMapHeight;
#if 1
int *ptr = CostMoveToCache;
#ifdef __x86_64__
union {
intptr_t d;
int i[2];
} conv;
conv.i[0] = CacheNotSet;
conv.i[1] = CacheNotSet;
if (((uintptr_t)ptr) & 4) {
*ptr++ = CacheNotSet;
--AStarMapMax;
}
#endif
while (AStarMapMax > 3) {
#ifdef __x86_64__
*((intptr_t *)ptr) = conv.d;
*((intptr_t *)(ptr + 2)) = conv.d;
ptr += 4;
#else
*ptr++ = CacheNotSet;
*ptr++ = CacheNotSet;
*ptr++ = CacheNotSet;
*ptr++ = CacheNotSet;
#endif
AStarMapMax -= 4;
};
while (AStarMapMax) {
*ptr++ = CacheNotSet;
--AStarMapMax;
}
#else
for (int i = 0; i < AStarMapMax; ++i) {
CostMoveToCache[i] = CacheNotSet;
}
#endif
ProfileEnd("CostMoveToCacheCleanUp");
memset(CostMoveToCache, CacheNotSet, CostMoveToCacheSize);
}
/**
@ -496,16 +449,6 @@ static int AStarFindNode(int eo)
return -1;
}
/**
** Add a node to the closed set
*/
static void AStarAddToClose(int node)
{
if (CloseSetSize < Threshold) {
CloseSet[CloseSetSize++] = node;
}
}
#define GetIndex(x, y) (x) + (y) * AStarMapWidth
/* build-in costmoveto code */
@ -605,12 +548,19 @@ static int CostMoveToCallBack_Default(unsigned int index, const CUnit &unit)
*/
static inline int CostMoveTo(unsigned int index, const CUnit &unit)
{
int *c = &CostMoveToCache[index];
int32_t *c = &CostMoveToCache[index];
if (*c != CacheNotSet) {
return *c;
// for performance reasons, CostMoveToCache uses -1 to
// indicate it is unset, but the algorithm is simpler
// if the range of costs is [-1, INT_MAX]. so we always
// store everything +1
return *c - 1;
}
*c = CostMoveToCallBack_Default(index, unit);
return *c;
*c = CostMoveToCallBack_Default(index, unit) + 1;
#ifdef DEBUG
Assert(c >= 0);
#endif
return *c - 1;
}
class AStarGoalMarker
@ -626,7 +576,6 @@ public:
AStarMatrix[offset].InGoal = 1;
*goal_reachable = true;
}
AStarAddToClose(offset);
}
private:
const CUnit &unit;
@ -957,7 +906,6 @@ int AStarFindPath(const Vec2i &startPos, const Vec2i &goalPos, int gw, int gh,
AStarCleanUp();
OpenSetSize = 0;
CloseSetSize = 0;
if (!AStarMarkGoal(goalPos, gw, gh, tilesizex, tilesizey, minrange, maxrange, unit)) {
// goal is not reachable
@ -981,7 +929,6 @@ int AStarFindPath(const Vec2i &startPos, const Vec2i &goalPos, int gw, int gh,
ProfileEnd("AStarFindPath");
return ret;
}
AStarAddToClose(OpenSet[0].O);
if (AStarMatrix[eo].InGoal) {
ret = PF_REACHED;
ProfileEnd("AStarFindPath");
@ -989,6 +936,8 @@ int AStarFindPath(const Vec2i &startPos, const Vec2i &goalPos, int gw, int gh,
}
Vec2i endPos;
int counter = AStarMaxSearchIterations;
// Begin search
while (1) {
// Find the best node of from the open set
@ -1006,17 +955,13 @@ int AStarFindPath(const Vec2i &startPos, const Vec2i &goalPos, int gw, int gh,
break;
}
#if 0
// If we have looked too long, then exit.
if (!counter--) {
// FIXME: Select a "good" point from the open set.
// Nearest point to goal.
// TODO: Select a "good" point from the open set.
AstarDebugPrint("way too long\n");
ret = PF_FAILED;
ProfileEnd("AStarFindPath");
return ret;
return PF_UNREACHABLE;
}
#endif
// Generate successors of this node.
@ -1067,8 +1012,6 @@ int AStarFindPath(const Vec2i &startPos, const Vec2i &goalPos, int gw, int gh,
ProfileEnd("AStarFindPath");
return ret;
}
// we add the point to the close set
AStarAddToClose(eo);
} else if (new_cost < AStarMatrix[eo].CostFromStart) {
// Already visited node, but we have here a better path
// I know, it's redundant (but simpler like this)

View file

@ -93,6 +93,15 @@ static int CclAStar(lua_State *l)
} else {
AStarUnknownTerrainCost = i;
}
} else if (!strcmp(value, "max-search-iterations")) {
++j;
i = LuaToNumber(l, j + 1);
if (i <= 0) {
PrintFunction();
fprintf(stdout, "Max A* search iterations must be strictly > 0\n");
} else {
AStarMaxSearchIterations = i;
}
} else {
LuaError(l, "Unsupported tag: %s" _C_ value);
}

View file

@ -52,6 +52,7 @@
#include "ui.h"
#include "unit.h"
#include "video.h"
#include "parameters.h"
#include <guichan.h>
void DrawGuichanWidgets();
@ -421,6 +422,8 @@ void GameMainLoop()
CclCommand("if (GameStarting ~= nil) then GameStarting() end");
long ticks = SDL_GetTicks();
MultiPlayerReplayEachCycle();
SingleGameLoop();
@ -445,6 +448,12 @@ void GameMainLoop()
NetworkQuitGame();
EndReplayLog();
if (Parameters::Instance.benchmark) {
ticks = SDL_GetTicks() - ticks;
double fps = FrameCounter * 1000.0 / ticks;
fprintf(stderr, "BENCHMARK RESULT: %f fps, %f cps (%ldms for %ldframes in %ldcycles)\n", fps, GameCycle * 1000.0 / ticks, ticks, FrameCounter, GameCycle);
}
GameCycle = 0;
CParticleManager::exit();
FlagRevealMap = MapRevealModes::cHidden;

View file

@ -41,8 +41,10 @@
#include <stdlib.h>
#include <stdarg.h>
#include <malloc.h>
#ifdef WIN32
#include <windows.h>
#include <intrin.h>
#endif
#ifdef USE_STACKTRACE
@ -500,3 +502,148 @@ void PrintOnStdOut(const char *format, ...)
va_end(valist);
fflush(stdout);
}
/*----------------------------------------------------------------------------
Check SSE/AVX support.
This can detect the instruction support of
SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, SSE4a, SSE5, and AVX.
----------------------------------------------------------------------------*/
#ifdef __x86_64__
#ifdef __GNUC__
static void __cpuid(int* cpuinfo, int info)
{
__asm__ __volatile__(
"xchg %%ebx, %%edi;"
"cpuid;"
"xchg %%ebx, %%edi;"
:"=a" (cpuinfo[0]), "=D" (cpuinfo[1]), "=c" (cpuinfo[2]), "=d" (cpuinfo[3])
:"0" (info)
);
}
static unsigned long long _my_xgetbv(unsigned int index)
{
unsigned int eax, edx;
__asm__ __volatile__(
"xgetbv;"
: "=a" (eax), "=d"(edx)
: "c" (index)
);
return ((unsigned long long)edx << 32) | eax;
}
#else // __GNUC__
#define _my_xgetbv(index) _xgetbv(index)
#endif // __GNUC__
struct SIMDSupport {
bool sseSupportted = false;
bool sse2Supportted = false;
bool sse3Supportted = false;
bool ssse3Supportted = false;
bool sse4_1Supportted = false;
bool sse4_2Supportted = false;
bool sse4aSupportted = false;
bool sse5Supportted = false;
bool avxSupportted = false;
};
static struct SIMDSupport checkSIMDSupport() {
struct SIMDSupport s;
int cpuinfo[4];
__cpuid(cpuinfo, 1);
// Check SSE, SSE2, SSE3, SSSE3, SSE4.1, and SSE4.2 support
s.sseSupportted = cpuinfo[3] & (1 << 25) || false;
s.sse2Supportted = cpuinfo[3] & (1 << 26) || false;
s.sse3Supportted = cpuinfo[2] & (1 << 0) || false;
s.ssse3Supportted = cpuinfo[2] & (1 << 9) || false;
s.sse4_1Supportted = cpuinfo[2] & (1 << 19) || false;
s.sse4_2Supportted = cpuinfo[2] & (1 << 20) || false;
// ----------------------------------------------------------------------
// Check AVX support
// References
// http://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled/
// http://insufficientlycomplicated.wordpress.com/2011/11/07/detecting-intel-advanced-vector-extensions-avx-in-visual-studio/
s.avxSupportted = cpuinfo[2] & (1 << 28) || false;
bool osxsaveSupported = cpuinfo[2] & (1 << 27) || false;
if (osxsaveSupported && s.avxSupportted)
{
// _XCR_XFEATURE_ENABLED_MASK = 0
unsigned long long xcrFeatureMask = _my_xgetbv(0);
s.avxSupportted = (xcrFeatureMask & 0x6) == 0x6;
}
// ----------------------------------------------------------------------
// Check SSE4a and SSE5 support
// Get the number of valid extended IDs
__cpuid(cpuinfo, 0x80000000);
int numExtendedIds = cpuinfo[0];
if (numExtendedIds >= 0x80000001)
{
__cpuid(cpuinfo, 0x80000001);
s.sse4aSupportted = cpuinfo[2] & (1 << 6) || false;
s.sse5Supportted = cpuinfo[2] & (1 << 11) || false;
}
// ----------------------------------------------------------------------
return s;
}
bool supportsSSE2()
{
static struct SIMDSupport s = checkSIMDSupport();
return s.sse2Supportted;
}
bool supportsAVX()
{
static struct SIMDSupport s = checkSIMDSupport();
return s.avxSupportted;
}
#else // __x86_64__
bool supportsSSE2()
{
return false;
}
bool supportsAVX()
{
return false;
}
#endif // __x86_64__
void *aligned_malloc(size_t alignment, size_t size)
{
#ifdef WIN32
return _aligned_malloc(size, alignment);
#elif _ISOC11_SOURCE
return aligned_alloc(alignment, size);
#else
return memalign(alignment, size);
#endif
}
void aligned_free(void *block)
{
#ifdef WIN32
_aligned_free(block);
#else
free(block);
#endif
}

View file

@ -719,6 +719,8 @@ const EventCallback *GetCallbacks()
return Callbacks;
}
static int SkipFrameMask = 0;
/**
** Wait for interactive input event for one frame.
**
@ -738,6 +740,27 @@ void WaitEventsOneFrame()
Uint32 ticks = SDL_GetTicks();
if (ticks > NextFrameTicks) { // We are too slow :(
++SlowFrameCounter;
if (SlowFrameCounter > FRAMES_PER_SECOND) {
unsigned long pct = (SlowFrameCounter * 100) / (FrameCounter ? FrameCounter : 1);
bool warn = false;
if (pct >= 40) {
warn = (SkipFrameMask < 0b101);
SkipFrameMask = 0b101;
} else if (pct >= 20) {
warn = (SkipFrameMask < 0b11);
SkipFrameMask = 0b11;
} else if (pct >= 10) {
warn = (SkipFrameMask < 0b1);
SkipFrameMask = 0b1;
}
if (warn) {
fprintf(stdout, "WARNING WARNING WARNING\n"
"Frames %lu, Slow frames %d = %lu%%, starting to render only every %d%s frame.\n",
FrameCounter, SlowFrameCounter, pct, SkipFrameMask + 1, SkipFrameMask == 1 ? "nd" : "th");
fflush(stdout);
SlowFrameCounter = 0;
}
}
}
InputMouseTimeout(*GetCallbacks(), ticks);
@ -803,6 +826,9 @@ void RealizeVideoMemory()
if (dummyRenderer) {
return;
}
if (FrameCounter & SkipFrameMask) {
return;
}
if (NumRects) {
//SDL_UpdateWindowSurfaceRects(TheWindow, Rects, NumRects);
SDL_UpdateTexture(TheTexture, NULL, TheScreen->pixels, TheScreen->pitch);

View file

@ -201,7 +201,7 @@ void SetClipping(int left, int top, int right, int bottom)
{
Assert(left <= right && top <= bottom && left >= 0 && left < Video.Width
&& top >= 0 && top < Video.Height && right >= 0
&& right < Video.Width && bottom >= 0 && bottom < Video.Height);
&& right <= Video.Width && bottom >= 0 && bottom <= Video.Height);
ClipX1 = left;
ClipY1 = top;