LOCAL_PATH := $(call my-dir)
###########################
#
# SDL shared library
#
###########################
include $(CLEAR_VARS)
LOCAL_MODULE := SDL2
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES)
LOCAL_SRC_FILES := \
$(subst $(LOCAL_PATH)/,, \
$(wildcard $(LOCAL_PATH)/src/*.c) \
$(wildcard $(LOCAL_PATH)/src/audio/*.c) \
$(wildcard $(LOCAL_PATH)/src/audio/android/*.c) \
$(wildcard $(LOCAL_PATH)/src/audio/dummy/*.c) \
$(LOCAL_PATH)/src/atomic/SDL_atomic.c \
$(LOCAL_PATH)/src/atomic/SDL_spinlock.c.arm \
$(wildcard $(LOCAL_PATH)/src/core/android/*.c) \
$(wildcard $(LOCAL_PATH)/src/cpuinfo/*.c) \
$(wildcard $(LOCAL_PATH)/src/dynapi/*.c) \
$(wildcard $(LOCAL_PATH)/src/events/*.c) \
$(wildcard $(LOCAL_PATH)/src/file/*.c) \
$(wildcard $(LOCAL_PATH)/src/haptic/*.c) \
$(wildcard $(LOCAL_PATH)/src/haptic/dummy/*.c) \
$(wildcard $(LOCAL_PATH)/src/joystick/*.c) \
$(wildcard $(LOCAL_PATH)/src/joystick/android/*.c) \
$(wildcard $(LOCAL_PATH)/src/loadso/dlopen/*.c) \
$(wildcard $(LOCAL_PATH)/src/power/*.c) \
$(wildcardpre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888 } /* Comment */
.highlight .err { color: #A61717; background-color: #E3D2D2 } /* Error */
.highlight .k { color: #080; font-weight: bold } /* Keyword */
.highlight .ch { color: #888 } /* Comment.Hashbang */
.highlight .cm { color: #888 } /* Comment.Multiline */
.highlight .cp { color: #C00; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888 } /* Comment.Single */
.highlight .cs { color: #C00; font-weight: bold; background-color: #FFF0F0 } /* Comment.Special */
.highlight .gd { color: #000; background-color: #FDD } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #A00 } /* Generic.Error */
.highlight .gh { color: #333 } /* Generic.Heading */
.highlight .gi { color: #000; background-color: #DFD } /* Generic.Inserted */
.highlight .go { color: #888 } /* Generic.Output */
.highlight .gp { color: #555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666 } /* Generic.Subheading */
.highlight .gt { color: #A00 } /* Generic.Traceback */
.highlight .kc { color: #080; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #080; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #080; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #080 } /* Keyword.Pseudo */
.highlight .kr { color: #080; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #00D; font-weight: bold } /* Literal.Number */
.highlight .s { color: #D20; background-color: #FFF0F0 } /* Literal.String */
.highlight .na { color: #369 } /* Name.Attribute */
.highlight .nb { color: #038 } /* Name.Builtin */
.highlight .nc { color: #B06; font-weight: bold } /* Name.Class */
.highlight .no { color: #036; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555 } /* Name.Decorator */
.highlight .ne { color: #B06; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #06B; font-weight: bold } /* Name.Function */
.highlight .nl { color: #369; font-style: italic } /* Name.Label */
.highlight .nn { color: #B06; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #369; font-weight: bold } /* Name.Property */
.highlight .nt { color: #B06; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #369 } /* Name.Variable */
.highlight .ow { color: #080 } /* Operator.Word */
.highlight .w { color: #BBB } /* Text.Whitespace */
.highlight .mb { color: #00D; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #00D; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #00D; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #00D; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #00D; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #D20; background-color: #FFF0F0 } /* Literal.String.Affix */
.highlight .sb { color: #D20; background-color: #FFF0F0 } /* Literal.String.Backtick */
.highlight .sc { color: #D20; background-color: #FFF0F0 } /* Literal.String.Char */
.highlight .dl { color: #D20; background-color: #FFF0F0 } /* Literal.String.Delimiter */
.highlight .sd { color: #D20; background-color: #FFF0F0 } /* Literal.String.Doc */
.highlight .s2 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Double */
.highlight .se { color: #04D; background-color: #FFF0F0 } /* Literal.String.Escape */
.highlight .sh { color: #D20; background-color: #FFF0F0 } /* Literal.String.Heredoc */
.highlight .si { color: #33B; background-color: #FFF0F0 } /* Literal.String.Interpol */
.highlight .sx { color: #2B2; background-color: #F0FFF0 } /* Literal.String.Other */
.highlight .sr { color: #080; background-color: #FFF0FF } /* Literal.String.Regex */
.highlight .s1 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Single */
.highlight .ss { color: #A60; background-color: #FFF0F0 } /* Literal.String.Symbol */
.highlight .bp { color: #038 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #06B; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #369 } /* Name.Variable.Class */
.highlight .vg { color: #D70 } /* Name.Variable.Global */
.highlight .vi { color: #33B } /* Name.Variable.Instance */
.highlight .vm { color: #369 } /* Name.Variable.Magic */
.highlight .il { color: #00D; font-weight: bold } /* Literal.Number.Integer.Long *///-----------------------------------------------------------------------------
// This is an implementation of Tom Forsyth's "Linear-Speed Vertex Cache
// Optimization" algorithm as described here:
// http://home.comcast.net/~tom_forsyth/papers/fast_vert_cache_opt.html
//
// This code was authored and released into the public domain by
// Adrian Stone (stone@gameangst.com).
//
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER
// LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <assert.h>
#include <math.h>
#include <vector>
#include <limits>
#include <algorithm>
namespace Forsyth
{
typedef unsigned int uint;
typedef unsigned short uint16;
typedef unsigned char byte;
//-----------------------------------------------------------------------------
// OptimizeFaces
//-----------------------------------------------------------------------------
// Parameters:
// indexList
// input index list
// indexCount
// the number of indices in the list
// vertexCount
// the largest index value in indexList
// newIndexList
// a pointer to a preallocated buffer the same size as indexList to
// hold the optimized index list
// lruCacheSize
// the size of the simulated post-transform cache (max:64)
//-----------------------------------------------------------------------------
void OptimizeFaces(const uint16* indexList, uint indexCount, uint vertexCount, uint16* newIndexList, uint16 lruCacheSize);
namespace
{
// code for computing vertex score was taken, as much as possible
// directly from the original publication.
float ComputeVertexCacheScore(int cachePosition, int vertexCacheSize)
{
const float FindVertexScore_CacheDecayPower = 1.5f;
const float FindVertexScore_LastTriScore = 0.75f;
float score = 0.0f;
if ( cachePosition < 0 )
{
// Vertex is not in FIFO cache - no score.
}
else
{
if ( cachePosition < 3 )
{
// This vertex was used in the last triangle,
// so it has a fixed score, whichever of the three
// it's in. Otherwise, you can get very different
// answers depending on whether you add
// the triangle 1,2,3 or 3,1,2 - which is silly.
score = FindVertexScore_LastTriScore;
}
else
{
assert ( cachePosition < vertexCacheSize );
// Points for being high in the cache.
const float scaler = 1.0f / ( vertexCacheSize - 3 );
score = 1.0f - ( cachePosition - 3 ) * scaler;
score = powf ( score, FindVertexScore_CacheDecayPower );
}
}
return score;
}
float ComputeVertexValenceScore(uint numActiveFaces)
{
const float FindVertexScore_ValenceBoostScale = 2.0f;
const float FindVertexScore_ValenceBoostPower = 0.5f;
float score = 0.f;
// Bonus points for having a low number of tris still to
// use the vert, so we get rid of lone verts quickly.
float valenceBoost = powf ( static_cast<float>(numActiveFaces),
-FindVertexScore_ValenceBoostPower );
score += FindVertexScore_ValenceBoostScale * valenceBoost;
return score;
}
const int kMaxVertexCacheSize = 64;
const uint kMaxPrecomputedVertexValenceScores = 64;
float s_vertexCacheScores[kMaxVertexCacheSize+1][kMaxVertexCacheSize];
float s_vertexValenceScores[kMaxPrecomputedVertexValenceScores];
bool ComputeVertexScores()
{
for (int cacheSize=0; cacheSize<=kMaxVertexCacheSize; ++cacheSize)
{
for (int cachePos=0; cachePos<cacheSize; ++cachePos)
{
s_vertexCacheScores[cacheSize][cachePos] = ComputeVertexCacheScore(cachePos, cacheSize);
}
}
for (uint valence=0; valence<kMaxPrecomputedVertexValenceScores; ++valence)
{
s_vertexValenceScores[valence] = ComputeVertexValenceScore(valence);
}
return true;
}
bool s_vertexScoresComputed = ComputeVertexScores();
// inline float FindVertexCacheScore(uint cachePosition, uint maxSizeVertexCache)
// {
// return s_vertexCacheScores[maxSizeVertexCache][cachePosition];
// }
// inline float FindVertexValenceScore(uint numActiveTris)
// {
// return s_vertexValenceScores[numActiveTris];
// }
float FindVertexScore(uint numActiveFaces, uint cachePosition, uint vertexCacheSize)
{
assert(s_vertexScoresComputed);
if ( numActiveFaces == 0 )
{
// No tri needs this vertex!
return -1.0f;
}
float score = 0.f;
if (cachePosition < vertexCacheSize)
{
score += s_vertexCacheScores[vertexCacheSize][cachePosition];
}
if (numActiveFaces < kMaxPrecomputedVertexValenceScores)
{
score += s_vertexValenceScores[numActiveFaces];
}
else
{
score += ComputeVertexValenceScore(numActiveFaces);
}
return score;
}
struct OptimizeVertexData
{
float score;
uint activeFaceListStart;
uint activeFaceListSize;
uint16 cachePos0;
uint16 cachePos1;
OptimizeVertexData() : score(0.f), activeFaceListStart(0), activeFaceListSize(0), cachePos0(0), cachePos1(0) { }
};
}
void OptimizeFaces(const uint16* indexList, uint indexCount, uint vertexCount, uint16* newIndexList, uint16 lruCacheSize)
{
std::vector<OptimizeVertexData> vertexDataList;
vertexDataList.resize(vertexCount);
// compute face count per vertex
for (uint i=0; i<indexCount; ++i)
{
uint16 index = indexList[i];
assert(index < vertexCount);
OptimizeVertexData& vertexData = vertexDataList[index];
vertexData.activeFaceListSize++;
}
std::vector<uint> activeFaceList;
const uint16 kEvictedCacheIndex = std::numeric_limits<uint16>::max();
{
// allocate face list per vertex
uint curActiveFaceListPos = 0;
for (uint i=0; i<vertexCount; ++i)
{
OptimizeVertexData& vertexData = vertexDataList[i];
vertexData.cachePos0 = kEvictedCacheIndex;
vertexData.cachePos1 = kEvictedCacheIndex;
vertexData.activeFaceListStart = curActiveFaceListPos;
curActiveFaceListPos += vertexData.activeFaceListSize;
vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos0, lruCacheSize);
vertexData.activeFaceListSize = 0;
}
activeFaceList.resize(curActiveFaceListPos);
}
// fill out face list per vertex
for (uint i=0; i<indexCount; i+=3)
{
for (uint j=0; j<3; ++j)
{
uint16 index = indexList[i+j];
OptimizeVertexData& vertexData = vertexDataList[index];
activeFaceList[vertexData.activeFaceListStart + vertexData.activeFaceListSize] = i;
vertexData.activeFaceListSize++;
}
}
std::vector<byte> processedFaceList;
processedFaceList.resize(indexCount);
uint16 vertexCacheBuffer[(kMaxVertexCacheSize+3)*2];
uint16* cache0 = vertexCacheBuffer;
uint16* cache1 = vertexCacheBuffer+(kMaxVertexCacheSize+3);
uint16 entriesInCache0 = 0;
uint bestFace = 0;
float bestScore = -1.f;
const float maxValenceScore = FindVertexScore(1, kEvictedCacheIndex, lruCacheSize) * 3.f;
for (uint i = 0; i < indexCount; i += 3)
{
if (bestScore < 0.f)
{
// no verts in the cache are used by any unprocessed faces so
// search all unprocessed faces for a new starting point
for (uint j = 0; j < indexCount; j += 3)
{
if (processedFaceList[j] == 0)
{
uint face = j;
float faceScore = 0.f;
for (uint k=0; k<3; ++k)
{
uint16 index = indexList[face+k];
OptimizeVertexData& vertexData = vertexDataList[index];
assert(vertexData.activeFaceListSize > 0);
assert(vertexData.cachePos0 >= lruCacheSize);
faceScore += vertexData.score;
}
if (faceScore > bestScore)
{
bestScore = faceScore;
bestFace = face;
assert(bestScore <= maxValenceScore);
if (bestScore >= maxValenceScore)
{
break;
}
}
}
}
assert(bestScore >= 0.f);
}
processedFaceList[bestFace] = 1;
uint16 entriesInCache1 = 0;
// add bestFace to LRU cache and to newIndexList
for (uint v = 0; v < 3; ++v)
{
uint16 index = indexList[bestFace+v];
newIndexList[i+v] = index;
OptimizeVertexData& vertexData = vertexDataList[index];
if (vertexData.cachePos1 >= entriesInCache1)
{
vertexData.cachePos1 = entriesInCache1;
cache1[entriesInCache1++] = index;
if (vertexData.activeFaceListSize == 1)
{
--vertexData.activeFaceListSize;
continue;
}
}
assert(vertexData.activeFaceListSize > 0);
uint* begin = &activeFaceList[vertexData.activeFaceListStart];
uint* end = &activeFaceList[vertexData.activeFaceListStart + vertexData.activeFaceListSize];
uint* it = std::find(begin, end, bestFace);
assert(it != end);
std::swap(*it, *(end-1));
--vertexData.activeFaceListSize;
vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos1, lruCacheSize);
}
// move the rest of the old verts in the cache down and compute their new scores
for (uint c0 = 0; c0 < entriesInCache0; ++c0)
{
uint16 index = cache0[c0];
OptimizeVertexData& vertexData = vertexDataList[index];
if (vertexData.cachePos1 >= entriesInCache1)
{
vertexData.cachePos1 = entriesInCache1;
cache1[entriesInCache1++] = index;
vertexData.score = FindVertexScore(vertexData.activeFaceListSize, vertexData.cachePos1, lruCacheSize);
}
}
// find the best scoring triangle in the current cache (including up to 3 that were just evicted)
bestScore = -1.f;
for (uint c1 = 0; c1 < entriesInCache1; ++c1)
{
uint16 index = cache1[c1];
OptimizeVertexData& vertexData = vertexDataList[index];
vertexData.cachePos0 = vertexData.cachePos1;
vertexData.cachePos1 = kEvictedCacheIndex;
for (uint j=0; j<vertexData.activeFaceListSize; ++j)
{
uint face = activeFaceList[vertexData.activeFaceListStart+j];
float faceScore = 0.f;
for (uint v=0; v<3; v++)
{
uint16 faceIndex = indexList[face+v];
OptimizeVertexData& faceVertexData = vertexDataList[faceIndex];
faceScore += faceVertexData.score;
}
if (faceScore > bestScore)
{
bestScore = faceScore;
bestFace = face;
}
}
}
std::swap(cache0, cache1);
entriesInCache0 = std::min(entriesInCache1, lruCacheSize);
}
}
} // namespace Forsyth