Arma Reforger Explorer  1.1.0.42
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
SCR_FPS_Autotest.c
Go to the documentation of this file.
1 // TODO(koudelkaluk): clean up this mess, get rid of unnecessary files saved for Confluence upload
2 [EntityEditorProps(category: "GameScripted/Utility", description: "Modified Heatmap autotest", dynamicBox: true)]
3 class SCR_HeatMap_AutotestClass: GenericEntityClass
4 {
5 };
6 
8 {
9  float m_fps;
10  float m_x;
11  float m_y;
12  float m_z;
13 
14  void HeatmapData(float fps, float x, float y, float z)
15  {
16  m_fps = fps;
17  m_x = x;
18  m_y = y;
19  m_z = z;
20  }
21 };
22 
23 //------------------------------------------------------------------------------------------------
24 // Handles closing the game after the data is sent to the heatmap database
25 class SCR_RestCallbackExample: RestCallback
26 {
27  //------------------------------------------------------------------------------------------------
28  override void OnError(int errorCode)
29  {
30  Print("Sending data has failed", LogLevel.WARNING);
31  GetGame().RequestClose();
32  };
33 
34  //------------------------------------------------------------------------------------------------
35  override void OnTimeout()
36  {
37  Print("Sending data has timed out", LogLevel.WARNING);
38  GetGame().RequestClose();
39  };
40 
41  //------------------------------------------------------------------------------------------------
42  override void OnSuccess(string data, int dataSize)
43  {
44  Print("Data sent successfully. Size: " + dataSize);
45  GetGame().RequestClose();
46  };
47 };
48 
50 {
51  [Attribute("ScriptCamera1", UIWidgets.EditBox, "Name of camera entity", "")]
52  private string m_cameraEntityName;
53 
54  [Attribute("", UIWidgets.EditBox, "Name of Confluence page", "")]
55  private string m_pageName;
56 
57  [Attribute("Unnamed test", UIWidgets.EditBox, "Title of particular test", "")]
58  private string m_testName;
59 
60  [Attribute("Heatmap title", UIWidgets.EditBox, "Title of heatmap", "")]
61  private string m_heatmapName;
62 
63  [Attribute("300", UIWidgets.Slider, "Measurements step", "10 2000 10")]
64  private int m_step;
65 
66  [Attribute("1", UIWidgets.Slider, "Wait time", "0 20 0.1")]
67  private float m_stepWaitTime;
68 
69  [Attribute("80", UIWidgets.Slider, "Camera height", "1 300 10")]
70  private float m_cameraHeight;
71 
72  [Attribute("10", UIWidgets.Slider, "Number of slowest points for lowest FPS graph", "10 50 1")]
73  private int m_lowFrameCount;
74 
75  [Attribute("", UIWidgets.EditBox)]
76  private string mapName;
77 
78  [Attribute("0", UIWidgets.Auto)]
79  private int startX;
80 
81  [Attribute("0", UIWidgets.Auto)]
82  private int startZ;
83 
84  [Attribute("0", UIWidgets.Auto)]
85  private int endX;
86 
87  [Attribute("0", UIWidgets.Auto)]
88  private int endZ;
89 
90  [Attribute("120", UIWidgets.EditBox)]
91  private int fpsRange;
92 
93  [Attribute("14", UIWidgets.EditBox)]
94  private float timeOfTheDay;
95 
96  [Attribute("", UIWidgets.Auto)]
97  private string mapID;
98 
99  private ref map<EPlatform, string> platforms = new map<EPlatform, string>();
100 
101  private ref SCR_RestCallbackExample m_jsonCallback = new SCR_RestCallbackExample();
102 
103  private const string HEATMAP_URL = "https://ar-heatmap.bistudio.com/";
104 
105  protected bool m_bDoneGenerating = false; //< Used to stop the benchmark after processing and sending data
106 
107  protected float m_timer; //< Used for counting how long camera stays on one place
108 
109  private IEntity m_camera; //< Entity used as camera, this one is auto moved by autotest
110 
111  private int m_x = startX; //< Next camera x position
112  private int m_y; //< Camera y position
113  private int m_z = startZ; //< Next camera y position
114 
115  private int m_worldSize; //< How big is world we are scanning
116  private int m_worldSizeX;
117  private int m_worldSizeZ;
118 
119  protected ref MeasurementFile m_heatmapfile; //< Heatmap output data
120 
121  private ref array<ref HeatmapData> m_geojson_data = new array<ref HeatmapData>();
122  private ref array<int> image_file = new array<int>();
123 
124  private float min_fps = 1000;
125  private float max_fps = 0;
126  private float avg_fps = 0;
127 
128  protected ref AutotestRegister m_register;
129 
130  private bool area_only; // do we want to measure only specified area?
131 
132  private float fps_four_directions[4];
133  private int prev_dir = -1;
134 
135  private int colors[6] = {
136  0x0000ff,
137  0x00ffff,
138  0x00ff00,
139  0xffff00,
140  0xff0000,
141  0xff0000
142  };
143 
144  void SCR_HeatMap_Autotest(IEntitySource src, IEntity parent)
145  {
146  m_timer = -5;
147  SetEventMask(EntityEvent.INIT | EntityEvent.FRAME);
148  SetFlags(EntityFlags.ACTIVE, true);
149  }
150 
151  override void EOnInit(IEntity owner)
152  {
153  ChimeraWorld world = GetGame().GetWorld();
154  if (world.GetTimeAndWeatherManager())
155  {
156  world.GetTimeAndWeatherManager().SetTimeOfTheDay(timeOfTheDay);
157  world.GetTimeAndWeatherManager().SetIsDayAutoAdvanced(false);
158  }
159 
160  area_only = (endX > 0 || endZ > 0 || startX > 0 || startZ > 0);
162  m_camera = GetGame().FindEntity(m_cameraEntityName);
163 
164  if (m_camera)
165  {
166  float groundY = m_camera.GetWorld().GetSurfaceY(m_x, m_z);
167  m_camera.SetOrigin(Vector(m_x, groundY + m_cameraHeight, m_z));
168  }
169 
171  if (GetGame().GetWorldEntity())
172  {
173  vector min, max;
174  GetGame().GetWorldEntity().GetWorldBounds(min, max);
175  m_worldSize = Math.Max(max[0] - min[0], max[2] - min[2]);
176 
177  if (area_only)
178  {
179  m_worldSizeX = endX;
180  m_worldSizeZ = endZ;
181  }
182  else
183  {
184  m_worldSizeX = m_worldSize;
185  m_worldSizeZ = m_worldSize;
186 
187  endX = m_worldSizeX;
188  endZ = m_worldSizeZ;
189  }
190  }
191 
192  platforms.Insert(EPlatform.WINDOWS, "62e0e57519098fc05e1b28fe");
193  platforms.Insert(EPlatform.XBOX_ONE, "62e0e58f19098fc05e1b28ff");
194  platforms.Insert(EPlatform.XBOX_SERIES_X, "632863b8211b59093a5afb79");
195  platforms.Insert(EPlatform.XBOX_SERIES_S, "632863cb211b59093a5afb7a");
196  }
197 
198  override void EOnFrame(IEntity owner, float timeSlice)
199  {
200  m_timer += timeSlice;
201  if (m_timer < 0)
202  {
203  return;
204  }
205 
206  if (m_bDoneGenerating)
207  {
208  return;
209  }
210 
211  if (!m_camera)
212  {
213  Print("Camera entity cannot be found!", LogLevel.ERROR);
214  GetGame().RequestClose();
215  return;
216  }
217 
218  int cur_fps = System.GetFPS();
219  int cur_dir = ((m_timer / m_stepWaitTime - Math.Floor(m_timer / m_stepWaitTime)) * 4);
220 
221  if (cur_dir != prev_dir)
222  {
223  vector new_angles = m_camera.GetAngles(); // rotate the camera in 4 directions
224  new_angles[1] = new_angles[1] + 90;
225  m_camera.SetAngles(new_angles);
226  prev_dir = cur_dir;
227  }
228 
229  fps_four_directions[cur_dir] = cur_fps;
230 
231  if (m_timer > m_stepWaitTime) //< When camera was still long enough
232  {
233  m_timer -= m_stepWaitTime;
234  if (m_camera)
235  {
237  float groundY = m_camera.GetWorld().GetSurfaceY(m_x, m_z);
238  m_y = groundY + m_cameraHeight;
239  m_camera.SetOrigin(Vector(m_x, groundY + m_cameraHeight, m_z));
240  }
241 
242  if (!m_register)
243  {
244  m_register = new AutotestRegister();
245  m_register.Init(m_pageName);
246  }
247 
248  if (!m_heatmapfile)
249  {
250  ref MeasurementFile descrFile = m_register.OpenMeasurementFile("0_fps_test_descr", "", MeasurementType.HTML);
251  descrFile.AddData(string.Format("<h1>" + m_heatmapName + "</h1><p>Camera step: %1m<br/>Step wait time: %2s<br/>Camera height above ground: %3m<br/>Number of worst FPS places: %4</p>", m_step, m_stepWaitTime, m_cameraHeight, m_lowFrameCount));
252 
253  m_heatmapfile = m_register.OpenMeasurementFile("1_fps_heatmap", m_testName, MeasurementType.HeatMap);
254 
255  }
256 
257  int fps = (fps_four_directions[0] + fps_four_directions[1] + fps_four_directions[2] + fps_four_directions[3]) / 4;
258 
259  image_file.Insert(fps);
260  m_heatmapfile.AddData(string.Format("%1,%2,%3", m_x, m_z, fps)); //< output new data to heatmap
261 
262  if (m_x + m_step <= m_worldSizeX)
263  {
264  HeatmapData new_geojson_value = new HeatmapData(fps, m_x, m_y, m_z);
265 
266  m_geojson_data.Insert(new_geojson_value);
267  if (fps > max_fps)
268  max_fps = fps;
269  if (fps < min_fps)
270  min_fps = fps;
272  m_x = m_x + m_step;
273  }
274  else
275  {
276  m_x = startX;
277  if (m_z + m_step <= m_worldSizeZ - m_step)
278  {
279  m_z = m_z + m_step;
280  }
281  else
282  {
283  CalculateAvgFPS();
284  GenerateDDS();
285  GenerateGeoJSON();
286  GenerateCSV();
287  m_bDoneGenerating = true;
288  }
289  }
290  }
291  }
292 
293  private void CalculateAvgFPS()
294  {
295  for (int i = 0; i < m_geojson_data.Count(); ++i)
296  {
297  avg_fps += m_geojson_data[i].m_fps;
298  }
299 
300  if (m_geojson_data.Count() == 0)
301  return;
302 
303  avg_fps /= m_geojson_data.Count();
304  Print("Avg FPS: " + avg_fps);
305  }
306 
307  private void ReverseArray(inout array<int> arr)
308  {
309  array<int> temp = new array<int>();
310  for (int i = arr.Count()-1; i >= 0; --i)
311  {
312  temp.Insert(arr[i]);
313  }
314  arr = temp;
315  }
316 
317  private void GenerateDDS()
318  {
319 #ifdef WORKBENCH
320  int width = (endX - startX) / m_step + 1;//2;
321  int height = (endZ - startZ) / m_step;//2;
322 
323  int offset_x = startX / m_step;
324  int offset_z = startZ / m_step;
325 
326  int image_size = m_worldSize / m_step;
327 
328  int id = 0;
329  int id_z = 0;
330 
331  array <int> image = new array <int>;
332 
333  for (int i = 0; i < image_size * image_size; ++i)
334  {
335  image.Insert(ARGB(255, 0, 0, 0));
336  }
337 
338  for (int y = 0; y < height; ++y)
339  {
340  array <int> row = new array <int>;
341  for (int i = 0; i < image_size; ++i)
342  {
343  row.Insert(ARGB(255, 0, 0, 0));
344  }
345 
346  int id_x = 0;
347 
348  for (int x = 0; x < width; ++x)
349  {
350  float range;
351  int col;
352 
353  int fps = image_file[id];
354 
355  float level = Math.Clamp(fps / fpsRange, 0, 1);
356  int phase = Math.Floor(level / 0.25);
357 
358  range = Math.Clamp((level - phase * 0.25) / 0.25, 0, 1);
359 
360  int from = colors[phase];
361  int to = colors[phase+1];
362 
363  Color col1 = Color.FromInt(from);
364  Color col2 = Color.FromInt(to);
365 
366  col1.Lerp(col2, range);
367  col = col1.PackToInt();
368 
369  int index = offset_x + x;
370 
371  if (index < row.Count())
372  row[index] = col;
373 
374  id_x++;
375  id++;
376  }
377 
378  ReverseArray(row);
379  for (int i = 0; i < row.Count(); ++i)
380  {
381  int index = (offset_z + id_z) * image_size + i;
382  if (index < image.Count())
383  image[index] = row[i];
384  }
385  id_z++;
386  }
387 
388  ReverseArray(image);
389  TexTools.SaveImageData("$logs:heatmap.dds", image_size, image_size, image);
390 #endif
391  }
392 
393  private string GetGeoJSONLine(HeatmapData val) //vector val)
394  {
395  string new_value = string.Format(
396  "{ \"properties\": { \"FPS\": \"%1\" }, \"geometry\": { \"coordinates\": [ %2, %3, %4] } }",
397  Math.Floor(val.m_fps), val.m_x, val.m_z, val.m_y);
398 
399  return new_value;
400  }
401 
402  private string GetCSVLine(HeatmapData val) //vector val)
403  {
404  string new_value = string.Format(
405  "%1,%2,%3",
406  val.m_x, val.m_z, Math.Floor(val.m_fps) );
407 
408  return new_value;
409  }
410 
411  private void GenerateGeoJSON()
412  {
413  FileHandle file;
414 
415  Print("Creating a geojson file");
416 
417  FileIO.MakeDirectory("$logs:/" + mapName);
418  string filename = FormatTimestamp() + ".geojson";
419  file = FileIO.OpenFile("$logs:/" + mapName + "/" + filename, FileMode.WRITE);
420 
421  WriteDataGJSON(file);
422  file.Close();
423  file = FileIO.OpenFile("$logs:/" + mapName + "/" + filename, FileMode.READ);
424 
425  string data;
426  file.Read(data, file.GetLength());
427  file.Close();
428 
429  RestContext restContext = GetGame().GetRestApi().GetContext(HEATMAP_URL);
430 
431  if (!restContext)
432  {
433  Print("Failed to get RestAPI context. Unable to send data", LogLevel.ERROR);
434  return;
435  }
436 
437  restContext.SetHeaders("Content-Type,application/json");
438  restContext.POST(m_jsonCallback, "api/fps-statistics", data);
439 
440  restContext = null;
441 
442  Print("Creating a json done");
443  }
444 
445  private void GenerateCSV()
446  {
447  FileHandle file;
448 
449  Print("Creating a CSV file");
450 
451  FileIO.MakeDirectory("$logs:/" + mapName);
452  string filename = FormatTimestamp() + ".csv";
453  file = FileIO.OpenFile("$logs:/" + mapName + "/" + filename, FileMode.WRITE);
454 
455  WriteDataCSV(file);
456  file.Close();
457 
458  Print("Creating a CSV done");
459  }
460 
461  private void WriteDataGJSON(FileHandle file)
462  {
463  Print("Adding data to geojson");
464 
465  string platform;
466  platforms.Find(System.GetPlatform(), platform);
467 
468  if (!platform)
469  {
470  platform = "Invalid";
471 
472  Print("SCR_FPS_Autotest::Invalid platform, GJSON file will fail to post to database", LogLevel.ERROR);
473  }
474 
475  file.WriteLine("{");
476  file.WriteLine(string.Format("\"date\": \"%1\",", SCR_DateTimeHelper.GetDateTimeLocal()));
477  file.WriteLine(string.Format("\"platform\": \"%1\",", platform));
478  file.WriteLine(string.Format("\"map\": \"%1\",", mapID));
479  file.WriteLine("\"featurecollection\": {");
480  file.WriteLine("\"type\": \"FeatureCollection\",");
481  file.WriteLine("\"name\": \"fps\",");
482  file.WriteLine(string.Format("\"minfps\": %1,", Math.Floor(min_fps)));
483  file.WriteLine(string.Format("\"maxfps\": %1,", Math.Floor(max_fps)));
484  file.WriteLine(string.Format("\"avgfps\": %1,", Math.Floor(avg_fps)));
485  file.WriteLine("\"crs\": { \"type\": \"name\", \"properties\": { \"name\": \"urn:ogc:def:crs:EPSG::32632\" } },");
486  file.WriteLine("\"features\": [");
487 
488  for (int i = 0; i < m_geojson_data.Count(); ++i)
489  {
490  string line_end = ",";
491  if (i == m_geojson_data.Count()-1)
492  line_end = string.Empty;
493 
494  file.WriteLine(GetGeoJSONLine(m_geojson_data[i]) + line_end);
495  }
496 
497  file.WriteLine("]\n}\n}");
498  }
499 
500  private void WriteDataCSV(FileHandle file)
501  {
502  Print("Adding data to CSV");
503 
504  file.WriteLine("x,z,fps");
505 
506  for (int i = 0; i < m_geojson_data.Count(); ++i)
507  file.WriteLine(GetCSVLine(m_geojson_data[i]));
508 
509  }
510  private string FormatTimestamp()
511  {
512  int year, month, day;
513  System.GetYearMonthDay(year, month, day);
514  string smonth, sday;
515  if (month < 10)
516  smonth = string.Format("0%1", month);
517  else
518  smonth = string.Format("%1", month);
519 
520  if (day < 10)
521  sday = string.Format("0%1", day);
522  else
523  sday = string.Format("%1", day);
524 
525  return string.Format("%1%2%3", year, smonth, sday);
526  }
527 };
ChimeraWorld
Definition: ChimeraWorld.c:12
EntityEditorProps
enum EQueryType EntityEditorProps(category:"GameScripted/Sound", description:"THIS IS THE SCRIPT DESCRIPTION.", color:"0 0 255 255")
Definition: SCR_AmbientSoundsComponent.c:12
SCR_HeatMap_Autotest
Definition: SCR_FPS_Autotest.c:49
GetGame
ArmaReforgerScripted GetGame()
Definition: game.c:1424
GenericEntity
SCR_GenericBoxEntityClass GenericEntity
SCR_RestCallbackExample
Definition: SCR_FPS_Autotest.c:25
SCR_DateTimeHelper
Definition: SCR_DateTimeHelper.c:1
Attribute
typedef Attribute
Post-process effect of scripted camera.
SCR_HeatMap_AutotestClass
Definition: SCR_FPS_Autotest.c:3
HeatmapData
Definition: SCR_FPS_Autotest.c:7
index
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
Definition: SCR_DestructionSynchronizationComponent.c:17
data
Get all prefabs that have the spawner data
Definition: SCR_EntityCatalogManagerComponent.c:305
category
params category
Definition: SCR_VehicleDamageManagerComponent.c:180