-
Notifications
You must be signed in to change notification settings - Fork 435
/
Copy pathVeldridRenderer.cs
345 lines (270 loc) · 15.1 KB
/
VeldridRenderer.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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Rendering;
using osu.Framework.Graphics.Rendering.Vertices;
using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Veldrid.Batches;
using osu.Framework.Platform;
using osu.Framework.Graphics.Veldrid.Buffers;
using osu.Framework.Graphics.Veldrid.Buffers.Staging;
using osu.Framework.Graphics.Veldrid.Pipelines;
using osu.Framework.Graphics.Veldrid.Shaders;
using osu.Framework.Graphics.Veldrid.Textures;
using osu.Framework.Graphics.Veldrid.Vertices;
using osuTK;
using osuTK.Graphics;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using Veldrid;
using PrimitiveTopology = osu.Framework.Graphics.Rendering.PrimitiveTopology;
namespace osu.Framework.Graphics.Veldrid
{
internal class VeldridRenderer : Renderer, IVeldridRenderer
{
protected internal override bool VerticalSync
{
get => veldridDevice.VerticalSync;
set => veldridDevice.VerticalSync = value;
}
protected internal override bool AllowTearing
{
get => veldridDevice.AllowTearing;
set => veldridDevice.AllowTearing = value;
}
public override bool IsDepthRangeZeroToOne
=> veldridDevice.IsDepthRangeZeroToOne;
public override bool IsUvOriginTopLeft
=> veldridDevice.IsUvOriginTopLeft;
public override bool IsClipSpaceYInverted
=> veldridDevice.IsClipSpaceYInverted;
public bool UseStructuredBuffers
=> veldridDevice.UseStructuredBuffers;
public GraphicsDevice Device
=> veldridDevice.Device;
public ResourceFactory Factory
=> veldridDevice.Factory;
public GraphicsSurfaceType SurfaceType
=> veldridDevice.SurfaceType;
private readonly HashSet<IVeldridUniformBuffer> uniformBufferResetList = new HashSet<IVeldridUniformBuffer>();
private VeldridDevice veldridDevice = null!;
private GraphicsPipeline graphicsPipeline = null!;
private BasicPipeline bufferUpdatePipeline = null!;
private BasicPipeline textureUploadPipeline = null!;
private VeldridStagingTexturePool stagingTexturePool = null!;
private bool beganTextureUploadPipeline;
private VeldridIndexBuffer? linearIndexBuffer;
private VeldridIndexBuffer? quadIndexBuffer;
protected override void Initialise(IGraphicsSurface graphicsSurface)
{
veldridDevice = new VeldridDevice(graphicsSurface);
graphicsPipeline = new GraphicsPipeline(veldridDevice);
bufferUpdatePipeline = new BasicPipeline(veldridDevice);
textureUploadPipeline = new BasicPipeline(veldridDevice);
stagingTexturePool = new VeldridStagingTexturePool(graphicsPipeline);
MaxTextureSize = veldridDevice.MaxTextureSize;
}
protected internal override void BeginFrame(Vector2 windowSize)
{
foreach (var ubo in uniformBufferResetList)
ubo.ResetCounters();
uniformBufferResetList.Clear();
veldridDevice.Resize(new Vector2I((int)windowSize.X, (int)windowSize.Y));
graphicsPipeline.Begin();
bufferUpdatePipeline.Begin();
base.BeginFrame(windowSize);
}
protected internal override void FinishFrame()
{
base.FinishFrame();
flushTextureUploadPipeline();
bufferUpdatePipeline.End();
graphicsPipeline.End();
}
protected internal override void SwapBuffers()
=> veldridDevice.SwapBuffers();
protected internal override void WaitUntilIdle()
=> veldridDevice.WaitUntilIdle();
protected internal override void WaitUntilNextFrameReady()
=> veldridDevice.WaitUntilNextFrameReady();
protected internal override void MakeCurrent()
=> veldridDevice.MakeCurrent();
protected internal override void ClearCurrent()
=> veldridDevice.ClearCurrent();
protected override void ClearImplementation(ClearInfo clearInfo)
=> graphicsPipeline.Clear(clearInfo);
protected override void SetScissorStateImplementation(bool enabled)
=> graphicsPipeline.SetScissorState(enabled);
protected override bool SetTextureImplementation(INativeTexture? texture, int unit)
{
if (texture is not VeldridTexture veldridTexture)
return false;
graphicsPipeline.AttachTexture(unit, veldridTexture);
return true;
}
protected override void SetShaderImplementation(IShader shader)
=> graphicsPipeline.SetShader((VeldridShader)shader);
protected override void SetBlendImplementation(BlendingParameters blendingParameters)
=> graphicsPipeline.SetBlend(blendingParameters);
protected override void SetBlendMaskImplementation(BlendingMask blendingMask)
=> graphicsPipeline.SetBlendMask(blendingMask);
protected override void SetViewportImplementation(RectangleI viewport)
=> graphicsPipeline.SetViewport(viewport);
protected override void SetScissorImplementation(RectangleI scissor)
=> graphicsPipeline.SetScissor(scissor);
protected override void SetDepthInfoImplementation(DepthInfo depthInfo)
=> graphicsPipeline.SetDepthInfo(depthInfo);
protected override void SetStencilInfoImplementation(StencilInfo stencilInfo)
=> graphicsPipeline.SetStencilInfo(stencilInfo);
protected override void SetFrameBufferImplementation(IFrameBuffer? frameBuffer)
=> graphicsPipeline.SetFrameBuffer((VeldridFrameBuffer?)frameBuffer);
protected override void DeleteFrameBufferImplementation(IFrameBuffer frameBuffer)
=> ((VeldridFrameBuffer)frameBuffer).DeleteResources(true);
public override void DrawVerticesImplementation(PrimitiveTopology topology, int vertexStart, int verticesCount)
{
// normally we would flush/submit all texture upload commands at the end of the frame, since no actual rendering by the GPU will happen until then,
// but turns out on macOS with non-apple GPU, this results in rendering corruption.
// flushing the texture upload commands here before a draw call fixes the corruption, and there's no explanation as to why that's the case,
// but there is nothing to be lost in flushing here except for a frame that contains many sprites with Texture.BypassTextureUploadQueue = true.
// until that appears to be problem, let's just flush here.
flushTextureUploadPipeline();
graphicsPipeline.DrawVertices(topology.ToPrimitiveTopology(), vertexStart, verticesCount);
}
public void BindVertexBuffer<T>(IVeldridVertexBuffer<T> buffer)
where T : unmanaged, IEquatable<T>, IVertex
=> graphicsPipeline.SetVertexBuffer(buffer.Buffer, VeldridVertexUtils<T>.Layout);
public void BindIndexBuffer(VeldridIndexLayout layout, int verticesCount)
{
ref var indexBuffer = ref layout == VeldridIndexLayout.Quad
? ref quadIndexBuffer
: ref linearIndexBuffer;
if (indexBuffer == null || indexBuffer.VertexCapacity < verticesCount)
{
indexBuffer?.Dispose();
indexBuffer = new VeldridIndexBuffer(bufferUpdatePipeline, layout, verticesCount);
}
graphicsPipeline.SetIndexBuffer(indexBuffer);
}
private void ensureTextureUploadPipelineBegan()
{
if (beganTextureUploadPipeline)
return;
textureUploadPipeline.Begin();
beganTextureUploadPipeline = true;
}
private void flushTextureUploadPipeline()
{
if (!beganTextureUploadPipeline)
return;
textureUploadPipeline.End();
beganTextureUploadPipeline = false;
}
/// <summary>
/// Checks whether the given frame buffer is currently bound.
/// </summary>
/// <param name="frameBuffer">The frame buffer to check.</param>
public bool IsFrameBufferBound(IFrameBuffer frameBuffer)
=> FrameBuffer == frameBuffer;
protected internal override Image<Rgba32> TakeScreenshot()
=> veldridDevice.TakeScreenshot();
/// <summary>
/// Updates a <see cref="global::Veldrid.Texture"/> with a <paramref name="data"/> at the specified coordinates.
/// </summary>
/// <param name="texture">The <see cref="global::Veldrid.Texture"/> to update.</param>
/// <param name="x">The X coordinate of the update region.</param>
/// <param name="y">The Y coordinate of the update region.</param>
/// <param name="width">The width of the update region.</param>
/// <param name="height">The height of the update region.</param>
/// <param name="level">The texture level.</param>
/// <param name="data">The texture data.</param>
/// <typeparam name="T">The pixel type.</typeparam>
public void UpdateTexture<T>(global::Veldrid.Texture texture, int x, int y, int width, int height, int level, ReadOnlySpan<T> data)
where T : unmanaged
{
ensureTextureUploadPipelineBegan();
textureUploadPipeline.UpdateTexture(stagingTexturePool, texture, x, y, width, height, level, data);
}
/// <summary>
/// Updates a <see cref="global::Veldrid.Texture"/> with a <paramref name="data"/> at the specified coordinates.
/// </summary>
/// <param name="texture">The <see cref="global::Veldrid.Texture"/> to update.</param>
/// <param name="x">The X coordinate of the update region.</param>
/// <param name="y">The Y coordinate of the update region.</param>
/// <param name="width">The width of the update region.</param>
/// <param name="height">The height of the update region.</param>
/// <param name="level">The texture level.</param>
/// <param name="data">The texture data.</param>
/// <param name="rowLengthInBytes">The number of bytes per row of the image to read from <paramref name="data"/>.</param>
public void UpdateTexture(global::Veldrid.Texture texture, int x, int y, int width, int height, int level, IntPtr data, int rowLengthInBytes)
=> bufferUpdatePipeline.UpdateTexture(stagingTexturePool, texture, x, y, width, height, level, data, rowLengthInBytes);
protected override void SetUniformImplementation<T>(IUniformWithValue<T> uniform)
{
}
protected override void SetUniformBufferImplementation(string blockName, IUniformBuffer buffer)
=> graphicsPipeline.AttachUniformBuffer(blockName, (IVeldridUniformBuffer)buffer);
public void RegisterUniformBufferForReset(IVeldridUniformBuffer buffer)
=> uniformBufferResetList.Add(buffer);
public void GenerateMipmaps(VeldridTexture texture)
=> graphicsPipeline.GenerateMipmaps(texture);
public CommandList BufferUpdateCommands
=> bufferUpdatePipeline.Commands;
void IVeldridRenderer.BindShader(VeldridShader shader)
=> BindShader(shader);
void IVeldridRenderer.UnbindShader(VeldridShader shader)
=> UnbindShader(shader);
void IVeldridRenderer.EnqueueTextureUpload(VeldridTexture texture)
=> EnqueueTextureUpload(texture);
protected override IShaderPart CreateShaderPart(IShaderStore store, string name, byte[]? rawData, ShaderPartType partType)
=> new VeldridShaderPart(this, rawData, partType, store);
protected override IShader CreateShader(string name, IShaderPart[] parts, ShaderCompilationStore compilationStore)
=> new VeldridShader(this, name, parts.Cast<VeldridShaderPart>().ToArray(), compilationStore);
public override IFrameBuffer CreateFrameBuffer(RenderBufferFormat[]? renderBufferFormats = null, TextureFilteringMode filteringMode = TextureFilteringMode.Linear)
=> new VeldridFrameBuffer(this, renderBufferFormats?.ToPixelFormats(), filteringMode.ToSamplerFilter());
protected override IVertexBatch<TVertex> CreateLinearBatch<TVertex>(int size, int maxBuffers, PrimitiveTopology primitiveType)
// maxBuffers is ignored because batches are not allowed to wrap around in Veldrid.
=> new VeldridLinearBatch<TVertex>(this, size, primitiveType);
protected override IVertexBatch<TVertex> CreateQuadBatch<TVertex>(int size, int maxBuffers)
// maxBuffers is ignored because batches are not allowed to wrap around in Veldrid.
=> new VeldridQuadBatch<TVertex>(this, size);
protected override IUniformBuffer<TData> CreateUniformBuffer<TData>()
=> new VeldridUniformBuffer<TData>(this);
protected override IShaderStorageBufferObject<TData> CreateShaderStorageBufferObject<TData>(int uboSize, int ssboSize)
=> new VeldridShaderStorageBufferObject<TData>(this, uboSize, ssboSize);
protected override INativeTexture CreateNativeTexture(int width, int height, bool manualMipmaps = false, TextureFilteringMode filteringMode = TextureFilteringMode.Linear,
Color4? initialisationColour = null)
=> new VeldridTexture(this, width, height, manualMipmaps, filteringMode.ToSamplerFilter(), initialisationColour);
protected override INativeTexture CreateNativeVideoTexture(int width, int height)
=> new VeldridVideoTexture(this, width, height);
internal IStagingBuffer<T> CreateStagingBuffer<T>(uint count)
where T : unmanaged
{
switch (FrameworkEnvironment.StagingBufferType)
{
case 0:
return new ManagedStagingBuffer<T>(this, count);
case 1:
return new PersistentStagingBuffer<T>(this, count);
case 2:
return new DeferredStagingBuffer<T>(this, count);
default:
switch (Device.BackendType)
{
case GraphicsBackend.Direct3D11:
case GraphicsBackend.Vulkan:
return new PersistentStagingBuffer<T>(this, count);
default:
// Metal uses a more optimal path that elides a Blit Command Encoder.
case GraphicsBackend.Metal:
// OpenGL backends need additional work to support coherency and persistently mapped buffers.
case GraphicsBackend.OpenGL:
case GraphicsBackend.OpenGLES:
return new ManagedStagingBuffer<T>(this, count);
}
}
}
}
}