1
1
using System ;
2
2
using System . Collections . Generic ;
3
+ using System . Diagnostics ;
3
4
using System . Linq ;
4
5
using System . Runtime . CompilerServices ;
5
6
using System . Runtime . InteropServices ;
7
+ using System . Threading ;
6
8
using Nanoforge . Render . Misc ;
7
9
using Nanoforge . Render . Resources ;
8
10
using Serilog ;
@@ -23,7 +25,9 @@ public unsafe class RenderContext : IDisposable
23
25
public Instance Instance ;
24
26
public DebugUtilsMessengerEXT DebugUtilsMessenger ;
25
27
public CommandPool CommandPool ;
28
+ public CommandPool TransferCommandPool ;
26
29
public Queue GraphicsQueue ;
30
+ public Queue TransferQueue ;
27
31
private ExtDebugUtils ? _debugUtils ;
28
32
29
33
public VkBuffer StagingBuffer = null ! ;
@@ -47,8 +51,6 @@ public unsafe class RenderContext : IDisposable
47
51
48
52
} ;
49
53
50
- //TODO: Will have to change this to not use IWindow when porting this to Avalonia
51
- //TODO: Should some of this init code be stuck in a bootstrap class or something?
52
54
public RenderContext ( )
53
55
{
54
56
#region Create Instance
@@ -148,25 +150,53 @@ public RenderContext()
148
150
#endregion
149
151
150
152
#region Create logical device
151
- var indices = FindQueueFamilies ( PhysicalDevice ) ;
153
+ QueueFamilyIndices queueFamilyIndices = FindQueueFamilies ( PhysicalDevice ) ;
154
+ Debug . Assert ( queueFamilyIndices . GraphicsFamily != null , "queueFamilyIndices.GraphicsFamily != null" ) ;
155
+ Debug . Assert ( queueFamilyIndices . TransferFamily != null , "queueFamilyIndices.TransferFamily != null" ) ;
156
+ Log . Information ( $ "Selected vulkan queues. Graphics queue: { queueFamilyIndices . GraphicsFamily . Value } , Transfer queue: { queueFamilyIndices . TransferFamily . Value } ") ;
152
157
153
- var uniqueQueueFamilies = new [ ] { indices . GraphicsFamily ! . Value } ;
158
+ uint [ ] uniqueQueueFamilies = [ queueFamilyIndices . GraphicsFamily ! . Value , queueFamilyIndices . TransferFamily ! . Value ] ;
154
159
uniqueQueueFamilies = uniqueQueueFamilies . Distinct ( ) . ToArray ( ) ;
155
160
156
161
using var mem = GlobalMemory . Allocate ( uniqueQueueFamilies . Length * sizeof ( DeviceQueueCreateInfo ) ) ;
157
162
var queueCreateInfos = ( DeviceQueueCreateInfo * ) Unsafe . AsPointer ( ref mem . GetPinnableReference ( ) ) ;
158
163
159
164
float queuePriority = 1.0f ;
160
- for ( int i = 0 ; i < uniqueQueueFamilies . Length ; i ++ )
161
- {
162
- queueCreateInfos [ i ] = new ( )
165
+ uint graphicsQueueIndex ;
166
+ uint trafficsQueueIndex ;
167
+ if ( uniqueQueueFamilies . Length == 1 )
168
+ {
169
+ //The graphics and transfer queue are in the same family, so we want two of this queue type with each queue having a different index.
170
+ graphicsQueueIndex = 0 ;
171
+ trafficsQueueIndex = 1 ;
172
+ queueCreateInfos [ 0 ] = new ( )
163
173
{
164
174
SType = StructureType . DeviceQueueCreateInfo ,
165
- QueueFamilyIndex = uniqueQueueFamilies [ i ] ,
166
- QueueCount = 1 ,
175
+ QueueFamilyIndex = uniqueQueueFamilies [ 0 ] ,
176
+ QueueCount = 2 ,
167
177
PQueuePriorities = & queuePriority
168
178
} ;
169
179
}
180
+ else if ( uniqueQueueFamilies . Length >= 2 )
181
+ {
182
+ //The graphics and transfer queue are in different families. So we want 1 of each with each being at index 0.
183
+ graphicsQueueIndex = 0 ;
184
+ trafficsQueueIndex = 0 ;
185
+ for ( int i = 0 ; i < uniqueQueueFamilies . Length ; i ++ )
186
+ {
187
+ queueCreateInfos [ i ] = new ( )
188
+ {
189
+ SType = StructureType . DeviceQueueCreateInfo ,
190
+ QueueFamilyIndex = uniqueQueueFamilies [ i ] ,
191
+ QueueCount = 1 ,
192
+ PQueuePriorities = & queuePriority
193
+ } ;
194
+ }
195
+ }
196
+ else
197
+ {
198
+ throw new Exception ( $ "Unexpected unique vulkan queue family count. Expected either 1 or 2. Found { uniqueQueueFamilies . Length } .") ;
199
+ }
170
200
171
201
PhysicalDeviceFeatures deviceFeatures = new ( )
172
202
{
@@ -200,7 +230,8 @@ public RenderContext()
200
230
throw new Exception ( "failed to create logical device!" ) ;
201
231
}
202
232
203
- Vk . GetDeviceQueue ( Device , indices . GraphicsFamily ! . Value , 0 , out GraphicsQueue ) ;
233
+ Vk . GetDeviceQueue ( Device , queueFamilyIndices . GraphicsFamily ! . Value , graphicsQueueIndex , out GraphicsQueue ) ;
234
+ Vk . GetDeviceQueue ( Device , queueFamilyIndices . TransferFamily ! . Value , trafficsQueueIndex , out TransferQueue ) ;
204
235
205
236
if ( EnableValidationLayers )
206
237
{
@@ -211,7 +242,6 @@ public RenderContext()
211
242
#endregion
212
243
213
244
#region Create main command pool
214
- var queueFamilyIndices = FindQueueFamilies ( PhysicalDevice ) ;
215
245
216
246
CommandPoolCreateInfo poolInfo = new ( )
217
247
{
@@ -222,7 +252,21 @@ public RenderContext()
222
252
223
253
if ( Vk . CreateCommandPool ( Device , in poolInfo , null , out CommandPool ) != Result . Success )
224
254
{
225
- throw new Exception ( "failed to create command pool!" ) ;
255
+ throw new Exception ( "Failed to create primary command pool!" ) ;
256
+ }
257
+ #endregion
258
+
259
+ #region Create command pool for background thread data transfers
260
+ CommandPoolCreateInfo transferPoolInfo = new ( )
261
+ {
262
+ SType = StructureType . CommandPoolCreateInfo ,
263
+ QueueFamilyIndex = queueFamilyIndices . TransferFamily ! . Value ,
264
+ Flags = CommandPoolCreateFlags . ResetCommandBufferBit
265
+ } ;
266
+
267
+ if ( Vk . CreateCommandPool ( Device , in transferPoolInfo , null , out TransferCommandPool ) != Result . Success )
268
+ {
269
+ throw new Exception ( "Failed to create transfer command pool!" ) ;
226
270
}
227
271
#endregion
228
272
@@ -272,21 +316,62 @@ private QueueFamilyIndices FindQueueFamilies(PhysicalDevice device)
272
316
Vk . GetPhysicalDeviceQueueFamilyProperties ( device , ref queueFamilityCount , queueFamiliesPtr ) ;
273
317
}
274
318
275
-
276
- uint i = 0 ;
277
- foreach ( var queueFamily in queueFamilies )
319
+ //First find any graphics capable queue
320
+ for ( uint i = 0 ; i < queueFamilies . Length ; i ++ )
278
321
{
322
+ QueueFamilyProperties queueFamily = queueFamilies [ i ] ;
279
323
if ( queueFamily . QueueFlags . HasFlag ( QueueFlags . GraphicsBit ) )
280
324
{
281
325
indices . GraphicsFamily = i ;
326
+ indices . GraphicsFamilyQueueCount = queueFamily . QueueCount ;
327
+ break ;
282
328
}
329
+ }
283
330
284
- if ( indices . IsComplete ( ) )
331
+ //TODO: Figure out how to do this. The transfer only queue on my device apparently couldn't support the pipeline barrier in the Texture2D layout transition code
332
+ //Next try to find a queue that only has transfer and not graphics
333
+ // for (uint i = 0; i < queueFamilies.Length; i++)
334
+ // {
335
+ // QueueFamilyProperties queueFamily = queueFamilies[i];
336
+ // if (queueFamily.QueueFlags.HasFlag(QueueFlags.TransferBit) && !queueFamily.QueueFlags.HasFlag(QueueFlags.GraphicsBit))
337
+ // {
338
+ // indices.TransferFamily = i;
339
+ // indices.TransferFamilyQueueCount = queueFamily.QueueCount;
340
+ // break;
341
+ // }
342
+ // }
343
+
344
+ //If we failed to find a transfer only queue then just use whatever queue that's available for transfer
345
+ if ( ! indices . TransferFamily . HasValue )
346
+ {
347
+ for ( uint i = 0 ; i < queueFamilies . Length ; i ++ )
285
348
{
286
- break ;
349
+ QueueFamilyProperties queueFamily = queueFamilies [ i ] ;
350
+ if ( queueFamily . QueueFlags . HasFlag ( QueueFlags . TransferBit ) )
351
+ {
352
+ indices . TransferFamily = i ;
353
+ indices . TransferFamilyQueueCount = queueFamily . QueueCount ;
354
+ break ;
355
+ }
287
356
}
288
-
289
- i ++ ;
357
+ }
358
+
359
+ //Make sure there's enough queues available if the graphics and transfer queue are in the same family
360
+ if ( indices is { GraphicsFamily : not null , TransferFamily : not null } && indices . GraphicsFamily . Value == indices . TransferFamily . Value )
361
+ {
362
+ if ( indices . GraphicsFamilyQueueCount < 2 )
363
+ {
364
+ string err = $ "Graphics and transfer queue families are the same and there isn't enough queues available. Require 2, only { indices . GraphicsFamilyQueueCount } are available.";
365
+ Log . Error ( err ) ;
366
+ throw new Exception ( err ) ;
367
+ }
368
+ }
369
+
370
+ if ( ! indices . IsComplete ( ) )
371
+ {
372
+ string err = "Failed to find valid vulkan queue families for graphics and transfer." ;
373
+ Log . Error ( err ) ;
374
+ throw new Exception ( err ) ;
290
375
}
291
376
292
377
return indices ;
@@ -354,13 +439,13 @@ public uint FindMemoryType(uint typeFilter, MemoryPropertyFlags properties)
354
439
throw new Exception ( "failed to find suitable memory type!" ) ;
355
440
}
356
441
357
- public CommandBuffer BeginSingleTimeCommands ( )
442
+ public CommandBuffer BeginSingleTimeCommands ( CommandPool pool )
358
443
{
359
444
CommandBufferAllocateInfo allocateInfo = new ( )
360
445
{
361
446
SType = StructureType . CommandBufferAllocateInfo ,
362
447
Level = CommandBufferLevel . Primary ,
363
- CommandPool = CommandPool ,
448
+ CommandPool = pool ,
364
449
CommandBufferCount = 1 ,
365
450
} ;
366
451
@@ -377,7 +462,7 @@ public CommandBuffer BeginSingleTimeCommands()
377
462
return commandBuffer ;
378
463
}
379
464
380
- public void EndSingleTimeCommands ( CommandBuffer commandBuffer )
465
+ public void EndSingleTimeCommands ( CommandBuffer commandBuffer , CommandPool pool , Queue queue )
381
466
{
382
467
Vk . EndCommandBuffer ( commandBuffer ) ;
383
468
@@ -388,23 +473,23 @@ public void EndSingleTimeCommands(CommandBuffer commandBuffer)
388
473
PCommandBuffers = & commandBuffer ,
389
474
} ;
390
475
391
- Vk . QueueSubmit ( GraphicsQueue , 1 , in submitInfo , default ) ;
392
- Vk . QueueWaitIdle ( GraphicsQueue ) ;
476
+ Vk . QueueSubmit ( queue , 1 , in submitInfo , default ) ;
477
+ Vk . QueueWaitIdle ( queue ) ;
393
478
394
- Vk . FreeCommandBuffers ( Device , CommandPool , 1 , in commandBuffer ) ;
479
+ Vk . FreeCommandBuffers ( Device , pool , 1 , in commandBuffer ) ;
395
480
}
396
481
397
482
private void CreateStagingBuffer ( )
398
483
{
399
484
StagingBuffer = new VkBuffer ( this , StagingBufferSize , BufferUsageFlags . TransferSrcBit , MemoryPropertyFlags . HostVisibleBit | MemoryPropertyFlags . HostCoherentBit , canGrow : true ) ;
400
485
}
401
486
402
- public CommandBuffer AllocateCommandBuffer ( )
487
+ public CommandBuffer AllocateCommandBuffer ( CommandPool pool )
403
488
{
404
489
var allocInfo = new CommandBufferAllocateInfo
405
490
{
406
491
SType = StructureType . CommandBufferAllocateInfo ,
407
- CommandPool = CommandPool ,
492
+ CommandPool = pool ,
408
493
CommandBufferCount = 1 ,
409
494
Level = CommandBufferLevel . Primary
410
495
} ;
@@ -416,6 +501,7 @@ public void Dispose()
416
501
{
417
502
StagingBuffer . Destroy ( ) ;
418
503
Vk . DestroyCommandPool ( Device , CommandPool , null ) ;
504
+ Vk . DestroyCommandPool ( Device , TransferCommandPool , null ) ;
419
505
Vk . DestroyDevice ( Device , null ) ;
420
506
_debugUtils ? . DestroyDebugUtilsMessenger ( Instance , DebugUtilsMessenger , null ) ;
421
507
Vk . DestroyInstance ( Instance , null ) ;
0 commit comments