Unity 6.3
0 онлайн 30 гостей 3 в системе
Вход

MemoryProfiler.TakeSnapshot

Declaration

public static void TakeSnapshot(string path, Action<string,bool> finishCallback, Unity.Profiling.Memory.CaptureFlags captureFlags);
public static void TakeSnapshot(string path, Action<string,bool> finishCallback, Action<string,bool,DebugScreenCapture> screenshotCallback, Unity.Profiling.Memory.CaptureFlags captureFlags);

Параметры

Параметр Описание
путь Путь назначения для файла снимка памяти.
finishCallback Событие, которое запускается после того, как снимок памяти завершил процесс захвата данных. Первый параметр обозначает, что файл снимка был успешно создан на вызывающем устройстве, второй параметр содержит путь к полученному снимку.
screenshotCallback Событие, которое можно указать для получения скриншота после завершения снима. Первый параметр указывает, был ли файл скриншота успешно создан на вызывающем устройстве, второй содержит путь к полученному скриншоту, а третий — данные снима в виде DebugScreenCapture.
captureFlags Маска флага, определяющая содержимое снимка памяти.

Описание

Вызывает захват снимка памяти, чтобы получить снимок состояния памяти, который можно открыть и разобрать в Memory Profiler.

Не все поля, соответствующие флагам захвата, собираются. Это зависит от целевой сборки, используемой для захвата снимка. В частности, CaptureFlags.NativeAllocationSites и CaptureFlags.NativeStackTraces относятся к данным, которые могут быть собраны только из сборки, поддерживающей сбор информации о собственном стеке вызовов. Сбор информации о собственном стеке вызовов в настоящее время требует доступа к исходному коду. При предоставлении этих флагов для захвата из сборок, которые их не поддерживают, они игнорируются.

Параметр path относится к устройству, на котором выполняется вызов, но именно состояние подключения редактора или плеера, из которого вызывается этот метод, определяет, какой процесс будет захвачен и где будет создан файл снимка. В системе есть два разных варианта поведения при сборе данных:

* При вызове этого метода из проигрывателя, подключенного к редактору через PlayerConnection, файл не хранится локально, а передаётся в подключенный редактор. Указанный путь должен быть пустым, или вместо этого может быть вызван MemoryProfiler.TakeTempSnapshot. Булевое значение finishCallback будет false, даже если снимки были успешно сохранены в редакторе.

* Когда вы вызываете это из проигрывателя, который не подключен к редактору через PlayerConnection, файл создается на хост-устройстве проигрывателя.

* Когда вы вызываете это из редактора, файл создается на хост-устройстве редактора. Если редактор не подключен к проигрывателю через EditorConnection, он захватит проигрыватель, в противном случае он захватит редактор.

Сбор метаданных происходит непосредственно перед тем, как будет сделан снимок, если хотя бы один слушатель был зарегистрирован на событие MemoryProfiler.CreatingMetadata. В конце процесса запускается finishCallback. Если к вызову был предоставлен обратный вызов скриншота, это вызывает в конце текущего кадра.

Примечание: * Если предоставлен абсолютный путь, убедитесь, что приложение может записывать в этот путь. Если предоставлен только имя файла или относительный путь, снимок будет храниться в пути относительно Application.dataPath и API, как File.Open, может использоваться с одной и той же информацией относительного пути. * Слушатели события MemoryProfiler.CreatingMetadata уведомляются в Player или Editor, которые захватываются, что может быть не Editor, который вызвал этот метод, а подключенный Player вместо этого. * Screenshot callbacks вызываются только в автономных проигрывателях или в режиме Play. Если не предоставлены callbacks для screenshotCallback, то screenshot не будет сделан. * Рекомендуется вызывать это API из Coroutine, оставляя до тех пор, пока оба callbacks не будут вызваны, потому что процесс захвата блокирует исполнение кода на хост-устройстве, пока он захватывается и занимает время для выполнения. Вызов API из coroutine предотвращает заблокирование Editor захватов в это время. * Нет способа захватить режим Play в изоляции, только весь Editor. Использование памяти в Editor может резко отличаться от того, что в встроенном Player. Всегда убедитесь, что вы анализируете использование памяти в разработке Players, работающих на ваших целевых устройствах. * Вы можете только сделать следующий снимок после того, как finishCallback был вызван. * Используйте Пакет Memory Profiler для открытия и анализа полученных файлов.

using System;
using System.Collections;
using System.IO;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Collections;
using Unity.Profiling;
using Unity.Profiling.Memory;
using UnityEngine;

#if UNITY_EDITOR using UnityEditor; using UnityEditor.Networking.PlayerConnection; using UnityEngine.Networking.PlayerConnection; // Reguires com.unity.editorcoroutines package to be installed and its assembly referenced using Unity.EditorCoroutines.Editor; #endif

#if UNITY_EDITOR public class MemoryProfilerExampleWindow : EditorWindow { IConnectionState m_PlayerConnectionState; [MenuItem("Windows/Analysis/MemoryProfilerAPIExample")] static void InitializeOnLoad() { var window = EditorWindow.GetWindow<MemoryProfilerExampleWindow>(); window.m_PlayerConnectionState = PlayerConnectionGUIUtility.GetConnectionState(window); window.Show(); }

void TakeSnapshot() { // In the Unity Editor, the screenshot function only gets called when the not profiling Playmode or if Playmode is active. // In otherwords, capturing an Editor outside of Playmode does not create a screenshot and waiting for the callback would create and endless loop. var takeScreenshot = m_PlayerConnectionState.connectedToTarget == ConnectionTarget.Player || Application.isPlaying; EditorCoroutineUtility.StartCoroutine(MemoryProfilerExample.TakeSnapshot(takeScreenshot), this); } } #endif

public static class MemoryProfilerExample { public static IEnumerator TakeSnapshot(bool takeScreenshot) { var snapshotFileName = "SnapshotName.tmpsnap"; // Make sure the file does not exist, e.g. as a left over of a failed previous attempt to take a snapshot. if (File.Exists(snapshotFileName)) File.Delete(snapshotFileName);

var snapshotFinished = false; var screenshoFinished = false; string resultingSnapshotPath = null; string resultingScreenshotPath = null; var captureFlags = CaptureFlags.ManagedObjects | CaptureFlags.NativeObjects | CaptureFlags.NativeAllocations; Action<string, bool> snapshotCaptureFunction = (snapshotFilePath, success) => { snapshotFinished = true; if (success) { resultingSnapshotPath = Path.GetFullPath(snapshotFilePath); Debug.Log($"Snapshot captured and stored at {resultingSnapshotPath}."); } else { Debug.LogError("Failed to take a snapshot."); } }; Action<string, bool, DebugScreenCapture> screenshotCaptureFunction = (screenshotFilePath, success, screenshotData) => { screenshoFinished = true; if (!success || screenshotData.RawImageDataReference.Length == 0) return;

// Note: for the Memory Profiler to be able to pick up the screenshot, the name and path needs to match that of the snapshot file, safe for the extension. // The path provided by the callback is based on the path provided to TakeSnapshot. if (Path.HasExtension(screenshotFilePath)) { screenshotFilePath = Path.ChangeExtension(screenshotFilePath, ".tmppng"); }

var texture = new Texture2D(screenshotData.Width, screenshotData.Height, screenshotData.ImageFormat, false, false); CopyDataToTexture(texture, screenshotData.RawImageDataReference); File.WriteAllBytes(screenshotFilePath, texture.EncodeToPNG()); if (Application.isPlaying) UnityEngine.Object.Destroy(texture); else UnityEngine.Object.DestroyImmediate(texture); resultingScreenshotPath = screenshotFilePath; };

if (takeScreenshot) MemoryProfiler.TakeSnapshot(snapshotFileName, snapshotCaptureFunction, screenshotCaptureFunction, captureFlags); else MemoryProfiler.TakeSnapshot(snapshotFileName, snapshotCaptureFunction, captureFlags);

// The finishCallback is called first. while (!snapshotFinished) { yield return null; } // The screenshotCallback is called second and can take a moment longer, // but don't wait for it if it is not being taken. while (takeScreenshot && !screenshoFinished) { yield return null; }

if (resultingSnapshotPath != null && File.Exists(resultingSnapshotPath)) { var finalSnapshotPath = Path.ChangeExtension(resultingSnapshotPath, ".snap"); var finalScreenshotPath = resultingScreenshotPath != null ? Path.ChangeExtension(resultingScreenshotPath, ".png") : null;

// Remove any pre-existing files first. if (File.Exists(finalSnapshotPath)) File.Delete(finalSnapshotPath); if (finalScreenshotPath != null && File.Exists(finalScreenshotPath)) File.Delete(finalScreenshotPath);

// Now that writing to the file has succesfully completed, rename the file to the .snap extension to denote that the Memory Profiler can open it. File.Move(resultingSnapshotPath, finalSnapshotPath); if (finalScreenshotPath != null) File.Move(resultingScreenshotPath, finalScreenshotPath);

// If you don't have access to the Player's file system you could also upload the file to an end-point that is accessible to you here. } }

static void CopyDataToTexture(Texture2D tex, NativeArray<byte> byteArray) { unsafe { void* srcPtr = NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(byteArray); void* dstPtr = tex.GetRawTextureData<byte>().GetUnsafeReadOnlyPtr(); UnsafeUtility.MemCpy(dstPtr, srcPtr, byteArray.Length * sizeof(byte)); } } }

Дополнительные ресурсы: Память Profiler пакет, Application.dataPath, DebugScreenCapture, MemoryProfiler.CreatingMetadata, PlayerConnectionGUIUtility.GetConnectionState, EditorCoroutineUtility.