- Home /
JIT exception in Unity iPhone
Hi, I have my project working in the editor, but there is an error when runing on my iPad. This is the exception:
ExecutionEngineException: Attempting to JIT compile method 'TerrainData:.ctor ()' while running with --aot-only.
at TerrainData..cctor () [0x00000] in :0 Rethrow as TypeInitializationException: An exception was thrown by the type initializer for TerrainData
I suspect my use of generics is causing a problem, but since I am new to C#/Unity I am befuddled!
Here is the entire class:
public class TerrainData {
private static readonly TerrainData instance = new TerrainData();
enum roomWallType { exterior, interior };
//a set of slope prefabs indexed by type then variant
public List<List<GameObject>> slopePrefabs;
public List<GameObject> roomWallJoin; //joiner bits for room walls
public List<GameObject>[] roomWall;
public List<GameObject>[] roomWin;
public List<GameObject>[] roomDoor;
public List<GameObject>[] roomLockDoor;
public List<GameObject> moveHighlightPrefabs;
//Defines all possible slopes based on height level of four corners (hence 4 dimensional array)
public MapElement[,,,] slopes;
public MapElement[,,,,,,,] wallJoins;
//define a slope type by the height level of each corner (only one of the four possible orientations)
//this data is based on the template graphics (with first corner at x0,y1 in Blender file)
public int[,] slopeTypes;
//defined by edge types (external=1, internal=2) on each of four spokes
public int[,] wallJoinTypes;
private TerrainData() {
slopePrefabs = new List<List<GameObject>>();
roomWallJoin = new List<GameObject>(); //joiner bits for room walls
roomWall = new List<GameObject>[2];
roomWin = new List<GameObject>[2];
roomDoor = new List<GameObject>[2];
roomLockDoor = new List<GameObject>[2];
moveHighlightPrefabs = new List<GameObject>();
slopes = new MapElement[3,3,3,3];
wallJoins = new MapElement[3,3,3,3,3,3,3,3];
//define a slope type by the height level of each corner (only one of the four possible orientations)
//this data is based on the template graphics (with first corner at x0,y1 in Blender file)
slopeTypes = new int[,] { //represents height level of each corner
{0,0,0,0}, //floor type
{1,1,1,1}, //floor type
{2,2,2,2}, //floor type
{1,1,0,0},
{2,2,1,1},
{1,0,0,0},
{2,1,1,1},
{1,1,0,1},
{2,2,1,2},
{1,1,0,2},
{1,2,0,0},
{1,2,0,1},
{2,0,0,0},
{2,1,0,0},
{2,1,0,1},
{2,1,0,2},
{2,2,0,0},
{2,2,0,1},
{2,2,0,2},
{1,0,1,0},
{2,1,2,1},
{2,0,2,0},
{2,0,1,0},
{2,0,2,1} };
//defined by edge types (external=1, internal=2) on each of four spokes
wallJoinTypes = new int[,] {
{0,0,0,0,0,0,1,2},
{0,0,0,0,0,0,2,1},
{0,0,0,0,0,0,2,2},
{0,0,1,2,0,0,2,1},
{0,0,2,2,0,0,2,2},
{1,1,1,2,0,0,2,1},
{1,2,0,0,0,0,2,1},
{1,2,2,1,1,2,2,1},
{1,2,2,2,0,0,2,1},
{1,2,2,2,2,2,2,1},
{2,1,0,0,0,0,1,2},
{2,1,1,2,0,0,2,2},
{2,2,0,0,0,0,2,2},
{2,2,2,1,0,0,1,2},
{2,2,2,2,0,0,2,2},
{2,2,2,2,2,2,2,2} };
}
public static TerrainData Instance
{
get
{
return instance;
}
}
// Terrain\Floors\Template\Template_slope_0000
public void LoadSlopes() {
string envName = "Template";
string slopePathStub = String.Format("Terrain/Floors/{0}/{0}_slope_", envName);
for (int i=0; i< slopeTypes.GetLength(0); i++) {
string slopeID = String.Format("{0}{1}{2}{3}", slopeTypes[i,0], slopeTypes[i,1], slopeTypes[i,2], slopeTypes[i,3]);
string slopeFolder = slopePathStub + slopeID;
List<GameObject> slopeVariants = new List<GameObject>();
GameObject slope = null;
int variantNum = 0;
while (true) {
string fileName = slopeFolder + String.Format( "/{0}_slope_{1}_{2}" , envName, slopeID, variantNum );
slope = LoadPrefab(fileName);
if (slope == null) break;
slopeVariants.Add(slope);
variantNum++;
}
slopePrefabs.Add(slopeVariants);
}
}
public void LoadMoveHighlights() {
string pathStub = "Interface/MoveHighlights/MH_";
for (int i=0; i< slopeTypes.GetLength(0); i++) {
string slopeID = String.Format("{0}{1}{2}{3}", slopeTypes[i,0], slopeTypes[i,1], slopeTypes[i,2], slopeTypes[i,3]);
string pathName = pathStub + slopeID;
GameObject highlight = null;
highlight = LoadPrefab(pathName);
moveHighlightPrefabs.Add(highlight);
}
}
/* builds four rotations for each slope type in a 4D array, which is then used in the map building
*/
public void BuildSlopeElements() {
MapElement slopeElement;
slopeElement.valid = true;
for (int i=0; i< slopeTypes.GetLength(0); i++) {
int c1 = slopeTypes[i,0];
int c2 = slopeTypes[i,1];
int c3 = slopeTypes[i,2];
int c4 = slopeTypes[i,3];
slopeElement.typeID = i;
slopeElement.orientation = 0;
slopes[c1,c2,c3,c4] = slopeElement;
slopeElement.orientation = 1;
slopes[c4,c1,c2,c3] = slopeElement;
slopeElement.orientation = 2;
slopes[c3,c4,c1,c2] = slopeElement;
slopeElement.orientation = 3;
slopes[c2,c3,c4,c1] = slopeElement;
}
}
public GameObject GetSlopePrefab( int slopeID, int variant ) {
//if (variant >= slopePrefabs[slopeID].Count) variant = 0;
return slopePrefabs[slopeID][variant];
}
public GameObject GetWallJoinPrefab( int joinID ) {
return roomWallJoin[ joinID ];
}
public int GetNumVariants( int slopeID ) {
return slopePrefabs[slopeID].Count;
}
public int GetNumRoomWallVariants( RoomWallCell.BaseType baseType, RoomWallCell.SubType subType ) {
switch (subType) {
case RoomWallCell.SubType.wall:
return roomWall[(int)baseType-1].Count;
case RoomWallCell.SubType.window:
return roomWin[(int)baseType-1].Count;
case RoomWallCell.SubType.door:
return roomDoor[(int)baseType-1].Count;
case RoomWallCell.SubType.lockDoor:
return roomLockDoor[(int)baseType-1].Count;
}
return 1;
}
public int SetValidSlopeVariant( int slopeID, int variant) {
if ( variant >= slopePrefabs[slopeID].Count) variant = 0;
return variant;
}
//will return null if no such object
private GameObject LoadPrefab(string pathName) {
GameObject prefab = Resources.Load(pathName) as GameObject;
//if (prefab == null) {
//Debug.Log( String.Format("Could not load prefab [{0}]", pathName) );
//}
return prefab;
}
public void LoadRoomWalls() {
string envName = "Template";
string stub = String.Format("Terrain/Rooms/RoomWalls/{0}", envName);
GameObject wall = null;
int variantID;
string[] wallTypes = {"ext", "int"};
for (int i=0 ; i<2; i++) {
//walls
variantID = 0;
roomWall[i] = new List<GameObject>();
while (true) {
string fname = String.Format( "{0}/{1}_wall_{2}_{3}", stub, envName, wallTypes[i], variantID );
wall = LoadPrefab( fname );
if (wall == null) break;
roomWall[i].Add( wall );
variantID ++;
}
//windows
variantID = 0;
roomWin[i] = new List<GameObject>();
while (true) {
string fname = String.Format( "{0}/{1}_win_{2}_{3}", stub, envName, wallTypes[i], variantID );
wall = LoadPrefab( fname );
if (wall == null) break;
roomWin[i].Add( wall );
variantID ++;
}
//doors
variantID = 0;
roomDoor[i] = new List<GameObject>();
while (true) {
string fname = String.Format( "{0}/{1}_door_{2}_{3}", stub, envName, wallTypes[i], variantID );
wall = LoadPrefab( fname );
if (wall == null) break;
roomDoor[i].Add( wall );
variantID ++;
}
//locked doors
variantID = 0;
roomLockDoor[i] = new List<GameObject>();
while (true) {
string fname = String.Format( "{0}/{1}_lockdoor_{2}_{3}", stub, envName, wallTypes[i], variantID );
wall = LoadPrefab( fname );
if (wall == null) break;
roomLockDoor[i].Add( wall );
variantID ++;
}
}
}
public GameObject GetRoomWall( RoomWallCell.BaseType baseType, RoomWallCell.SubType subType, int variant ) {
//probably should check if valid variant
switch (subType) {
case RoomWallCell.SubType.wall:
return roomWall[(int)baseType-1][variant];
case RoomWallCell.SubType.window:
return roomWin[(int)baseType-1][variant];
case RoomWallCell.SubType.door:
return roomDoor[(int)baseType-1][variant];
case RoomWallCell.SubType.lockDoor:
return roomLockDoor[(int)baseType-1][variant];
}
return null;
}
public void LoadRoomWallJoins() {
string envName = "Template";
string stub = String.Format("Terrain/Rooms/RoomWalls/{0}/{0}_walljoin_", envName);
for (int i=0; i < wallJoinTypes.GetLength(0); i++ ) {
string ID = "";
for (int j=0; j<8; j++) {
ID += wallJoinTypes[i,j].ToString();
}
roomWallJoin.Add( LoadPrefab( stub + ID ) );
}
}
public void BuildWallJoinElements() {
MapElement slopeElement;
slopeElement.valid = true;
for (int i=0; i< wallJoinTypes.GetLength(0); i++) {
int c1 = wallJoinTypes[i,0];
int c2 = wallJoinTypes[i,1];
int c3 = wallJoinTypes[i,2];
int c4 = wallJoinTypes[i,3];
int c5 = wallJoinTypes[i,4];
int c6 = wallJoinTypes[i,5];
int c7 = wallJoinTypes[i,6];
int c8 = wallJoinTypes[i,7];
slopeElement.typeID = i;
slopeElement.orientation = 0;
wallJoins[c1,c2,c3,c4,c5,c6,c7,c8] = slopeElement;
slopeElement.orientation = 1;
wallJoins[c7,c8,c1,c2,c3,c4,c5,c6] = slopeElement;
slopeElement.orientation = 2;
wallJoins[c5,c6,c7,c8,c1,c2,c3,c4] = slopeElement;
slopeElement.orientation = 3;
wallJoins[c3,c4,c5,c6,c7,c8,c1,c2] = slopeElement;
}
}
/* load all prefabs and prepare data */
public void MakeData() {
LoadSlopes();
BuildSlopeElements();
LoadRoomWalls();
LoadRoomWallJoins();
BuildWallJoinElements();
LoadMoveHighlights();
}
}
I have never seen an eight dimensional array before - what on earth are you storing in wallJoins? Surely that isn't fully populated?
Anyway Generics are fine on the iPhone but I've never used an array of lists (though lists of lists I use all the time). Can you post any initialised variables and the constructor?
Thanks for your reply, whydoidoit!
The wall joins can join in up to four directions with either internal or external wall surfaces on each side. The 8D array is used to access the correct prefab for a particular situation.
I posted the entire class to see if you can spot anything.
What is your API Compatibility Level and Stripping Level in your Build settings?
API compatibility level is .NET 2.0 Stripping level is disabled
$$anonymous$$ake the arrays of Lists into Lists of Lists and see what happens. That's the only thing that looks strange in the constructor to me.
Answer by demonpants · Jan 27, 2013 at 01:01 AM
Yup, the JIT error is for sure caused by those multidimensional arrays. I had the same issue with my game. You could probably fix it by using non-dynamic arrays (int[][][][]) but as a Java guy I was annoyed with how those are treated. Also, when you're doing so many dimensions (like 8), you probably want to flatten everything into a 1D array for better performance. I made a quick class for doing this.
using System.Collections;
//this focuses on speed, not safety, so don't call a function that
//uses a length bigger than what you passed in
public class FlattenedFloatArray
{
private float[] array;
private int[] dimensionMultiples;
private int[] dimensionLengths;
private int totalLength;
public FlattenedFloatArray( int[] dimensions )
{
dimensionLengths = dimensions;
totalLength = 1;
for ( int i = 0; i < dimensions.Length; i++ )
{
totalLength *= dimensions[i];
}
array = new float[ totalLength ];
dimensionMultiples = new int[ dimensions.Length ];
dimensionMultiples[ dimensions.Length - 1 ] = 1;
for ( int i = dimensions.Length - 2; i >= 0; i-- )
{
dimensionMultiples[i] = dimensions[i+1] * dimensionMultiples[i+1];
}
}
public float Get( int i, int j )
{
return array[ i * dimensionMultiples[0] + j * dimensionMultiples[1] ];
}
public float Get( int i, int j, int k )
{
return array[ i * dimensionMultiples[0] + j * dimensionMultiples[1] + k * dimensionMultiples[2] ];
}
public float Get( int i, int j, int k, int l )
{
return array[ i * dimensionMultiples[0] + j * dimensionMultiples[1] + k * dimensionMultiples[2] + l * dimensionMultiples[3] ];
}
public float Get( int i, int j, int k, int l, int m )
{
return array[ i * dimensionMultiples[0] + j * dimensionMultiples[1] + k * dimensionMultiples[2] + l * dimensionMultiples[3] + m * dimensionMultiples[4] ];
}
public float Get( int[] indices )
{
int index = 0;
for ( int i = 0; i < indices.Length; i++ )
{
index += indices[i] * dimensionMultiples[i];
}
return array[ index ];
}
public void Set( float v, int i, int j )
{
array[ i * dimensionMultiples[0] + j * dimensionMultiples[1] ] = v;
}
public void Set( float v, int i, int j, int k )
{
array[ i * dimensionMultiples[0] + j * dimensionMultiples[1] + k * dimensionMultiples[2] ] = v;
}
public void Set( float v, int i, int j, int k, int l )
{
array[ i * dimensionMultiples[0] + j * dimensionMultiples[1] + k * dimensionMultiples[2] + l * dimensionMultiples[3] ] = v;
}
public void Set( float v, int i, int j, int k, int l, int m )
{
array[ i * dimensionMultiples[0] + j * dimensionMultiples[1] + k * dimensionMultiples[2] + l * dimensionMultiples[3] + m * dimensionMultiples[4] ] = v;
}
public void Set( float v, int[] indices )
{
int index = 0;
for ( int i = 0; i < indices.Length; i++ )
{
index += indices[i] * dimensionMultiples[i];
}
array[ index ] = v;
}
public int GetTotalLength()
{
return array.Length;
}
public int GetDimensionCount()
{
return dimensionMultiples.Length;
}
public int GetLength( int dimension )
{
return dimensionLengths[ dimension ];
}
}