|
| 1 | +using System; |
| 2 | +using System.Collections; |
| 3 | +using System.Threading; |
1 | 4 | using Sentry.Extensibility; |
2 | | -using Sentry.Unity.Integrations; |
| 5 | +using Sentry.Internal; |
3 | 6 | using UnityEngine; |
4 | 7 |
|
5 | 8 | namespace Sentry.Unity; |
6 | 9 |
|
7 | | -public class ScreenshotEventProcessor : ISentryEventProcessorWithHint |
| 10 | +public class ScreenshotEventProcessor : ISentryEventProcessor |
8 | 11 | { |
9 | 12 | private readonly SentryUnityOptions _options; |
10 | | - private readonly IApplication _application; |
11 | | - public ScreenshotEventProcessor(SentryUnityOptions sentryOptions) : this(sentryOptions, null) { } |
| 13 | + private readonly ISentryMonoBehaviour _sentryMonoBehaviour; |
| 14 | + private volatile int _isCapturingScreenshot; |
12 | 15 |
|
13 | | - internal ScreenshotEventProcessor(SentryUnityOptions sentryOptions, IApplication? application) |
| 16 | + public ScreenshotEventProcessor(SentryUnityOptions sentryOptions) : this(sentryOptions, SentryMonoBehaviour.Instance) { } |
| 17 | + |
| 18 | + internal ScreenshotEventProcessor(SentryUnityOptions sentryOptions, ISentryMonoBehaviour sentryMonoBehaviour) |
14 | 19 | { |
15 | 20 | _options = sentryOptions; |
16 | | - _application = application ?? ApplicationAdapter.Instance; |
| 21 | + _sentryMonoBehaviour = sentryMonoBehaviour; |
17 | 22 | } |
18 | 23 |
|
19 | | - public SentryEvent? Process(SentryEvent @event) |
| 24 | + public SentryEvent Process(SentryEvent @event) |
20 | 25 | { |
| 26 | + // Only ever capture one screenshot per frame |
| 27 | + if (Interlocked.CompareExchange(ref _isCapturingScreenshot, 1, 0) == 0) |
| 28 | + { |
| 29 | + _sentryMonoBehaviour.StartCoroutine(CaptureScreenshotCoroutine(@event.EventId)); |
| 30 | + } |
| 31 | + |
21 | 32 | return @event; |
22 | 33 | } |
23 | 34 |
|
24 | | - public SentryEvent? Process(SentryEvent @event, SentryHint hint) |
| 35 | + internal IEnumerator CaptureScreenshotCoroutine(SentryId eventId) |
25 | 36 | { |
26 | | - // save event id |
27 | | - // wait for end of frame |
28 | | - // check if last id is event it |
29 | | - // send screenshot |
30 | | - |
31 | | - // add workitem: screentshot for ID xxx |
32 | | - // sdk integration checking for work: if ID got sent, follow up with screenshot |
| 37 | + _options.LogDebug("Screenshot capture triggered. Waiting for End of Frame."); |
33 | 38 |
|
34 | | - if (!MainThreadData.IsMainThread()) |
35 | | - { |
36 | | - _options.DiagnosticLogger?.LogDebug("Screenshot capture skipped. Can't capture screenshots on other than the main thread."); |
37 | | - return @event; |
38 | | - } |
| 39 | + // WaitForEndOfFrame does not work in headless mode so we're making it configurable for CI. |
| 40 | + // See https://docs.unity3d.com/6000.1/Documentation/ScriptReference/WaitForEndOfFrame.html |
| 41 | + yield return WaitForEndOfFrame(); |
39 | 42 |
|
40 | | - if (_options.BeforeCaptureScreenshotInternal?.Invoke() is not false) |
| 43 | + try |
41 | 44 | { |
42 | | - if (_application.IsEditor) |
| 45 | + if (_options.BeforeCaptureScreenshotInternal?.Invoke() is false) |
43 | 46 | { |
44 | | - _options.DiagnosticLogger?.LogInfo("Screenshot capture skipped. Capturing screenshots it not supported in the Editor"); |
45 | | - return @event; |
| 47 | + yield break; |
46 | 48 | } |
47 | 49 |
|
48 | | - if (Screen.width == 0 || Screen.height == 0) |
| 50 | + var screenshotBytes = CaptureScreenshot(_options); |
| 51 | + if (screenshotBytes.Length == 0) |
49 | 52 | { |
50 | | - _options.DiagnosticLogger?.LogWarning("Can't capture screenshots on a screen with a resolution of '{0}x{1}'.", Screen.width, Screen.height); |
51 | | - } |
52 | | - else |
53 | | - { |
54 | | - hint.AddAttachment(SentryScreenshot.Capture(_options), "screenshot.jpg", contentType: "image/jpeg"); |
| 53 | + _options.LogWarning("Screenshot capture returned empty data for event {0}", eventId); |
| 54 | + yield break; |
55 | 55 | } |
| 56 | + |
| 57 | + var attachment = new SentryAttachment( |
| 58 | + AttachmentType.Default, |
| 59 | + new ByteAttachmentContent(screenshotBytes), |
| 60 | + "screenshot.jpg", |
| 61 | + "image/jpeg"); |
| 62 | + |
| 63 | + _options.LogDebug("Screenshot captured for event {0}", eventId); |
| 64 | + |
| 65 | + CaptureAttachment(eventId, attachment); |
56 | 66 | } |
57 | | - else |
| 67 | + catch (Exception e) |
58 | 68 | { |
59 | | - _options.DiagnosticLogger?.LogInfo("Screenshot capture skipped by BeforeAttachScreenshot callback."); |
| 69 | + _options.LogError(e, "Failed to capture screenshot."); |
| 70 | + } |
| 71 | + finally |
| 72 | + { |
| 73 | + Interlocked.Exchange(ref _isCapturingScreenshot, 0); |
60 | 74 | } |
61 | | - |
62 | | - return @event; |
63 | 75 | } |
| 76 | + |
| 77 | + internal virtual byte[] CaptureScreenshot(SentryUnityOptions options) |
| 78 | + => SentryScreenshot.Capture(options); |
| 79 | + |
| 80 | + internal virtual void CaptureAttachment(SentryId eventId, SentryAttachment attachment) |
| 81 | + => (Sentry.SentrySdk.CurrentHub as Hub)?.CaptureAttachment(eventId, attachment); |
| 82 | + |
| 83 | + internal virtual YieldInstruction WaitForEndOfFrame() |
| 84 | + => new WaitForEndOfFrame(); |
64 | 85 | } |
0 commit comments