/
VeldridSurfaceView.cs
172 lines (151 loc) · 5.26 KB
/
VeldridSurfaceView.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
using Android.Content;
using Android.Graphics;
using Android.Runtime;
using Android.Views;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Veldrid;
namespace SampleBase.Android
{
public class VeldridSurfaceView : SurfaceView, ISurfaceHolderCallback
{
private readonly GraphicsBackend _backend;
protected GraphicsDeviceOptions DeviceOptions { get; }
private bool _surfaceDestroyed;
private bool _paused;
private bool _enabled;
private bool _needsResize;
private bool _surfaceCreated;
public GraphicsDevice GraphicsDevice { get; protected set; }
public Swapchain MainSwapchain { get; protected set; }
public event Action Rendering;
public event Action DeviceCreated;
public event Action DeviceDisposed;
public event Action Resized;
public VeldridSurfaceView(Context context, GraphicsBackend backend)
: this(context, backend, new GraphicsDeviceOptions())
{
}
public VeldridSurfaceView(Context context, GraphicsBackend backend, GraphicsDeviceOptions deviceOptions) : base(context)
{
if (!(backend == GraphicsBackend.Vulkan || backend == GraphicsBackend.OpenGLES))
{
throw new NotSupportedException($"{backend} is not supported on Android.");
}
_backend = backend;
DeviceOptions = deviceOptions;
Holder.AddCallback(this);
}
public void Disable()
{
_enabled = false;
}
public void SurfaceCreated(ISurfaceHolder holder)
{
bool deviceCreated = false;
if (_backend == GraphicsBackend.Vulkan)
{
if (GraphicsDevice == null)
{
GraphicsDevice = GraphicsDevice.CreateVulkan(DeviceOptions);
deviceCreated = true;
}
Debug.Assert(MainSwapchain == null);
SwapchainSource ss = SwapchainSource.CreateAndroidSurface(holder.Surface.Handle, JNIEnv.Handle);
SwapchainDescription sd = new SwapchainDescription(
ss,
(uint)Width,
(uint)Height,
DeviceOptions.SwapchainDepthFormat,
DeviceOptions.SyncToVerticalBlank);
MainSwapchain = GraphicsDevice.ResourceFactory.CreateSwapchain(sd);
}
else
{
Debug.Assert(GraphicsDevice == null && MainSwapchain == null);
SwapchainSource ss = SwapchainSource.CreateAndroidSurface(holder.Surface.Handle, JNIEnv.Handle);
SwapchainDescription sd = new SwapchainDescription(
ss,
(uint)Width,
(uint)Height,
DeviceOptions.SwapchainDepthFormat,
DeviceOptions.SyncToVerticalBlank);
GraphicsDevice = GraphicsDevice.CreateOpenGLES(DeviceOptions, sd);
MainSwapchain = GraphicsDevice.MainSwapchain;
deviceCreated = true;
}
if (deviceCreated)
{
DeviceCreated?.Invoke();
}
_surfaceCreated = true;
}
public void RunContinuousRenderLoop()
{
Task.Factory.StartNew(() => RenderLoop(), TaskCreationOptions.LongRunning);
}
public void SurfaceDestroyed(ISurfaceHolder holder)
{
_surfaceDestroyed = true;
}
public void SurfaceChanged(ISurfaceHolder holder, [GeneratedEnum] Format format, int width, int height)
{
_needsResize = true;
}
private void RenderLoop()
{
_enabled = true;
while (_enabled)
{
try
{
if (_paused || !_surfaceCreated) { continue; }
if (_surfaceDestroyed)
{
HandleSurfaceDestroyed();
continue;
}
if (_needsResize)
{
_needsResize = false;
MainSwapchain.Resize((uint)Width, (uint)Height);
Resized?.Invoke();
}
if (GraphicsDevice != null)
{
Rendering?.Invoke();
}
}
catch (Exception e)
{
Debug.WriteLine("Encountered an error while rendering: " + e);
throw;
}
}
}
private void HandleSurfaceDestroyed()
{
if (_backend == GraphicsBackend.Vulkan)
{
MainSwapchain.Dispose();
MainSwapchain = null;
}
else
{
GraphicsDevice.Dispose();
GraphicsDevice = null;
MainSwapchain = null;
DeviceDisposed?.Invoke();
}
}
public void OnPause()
{
_paused = true;
}
public void OnResume()
{
_paused = false;
}
}
}