diff --git a/Samples/MiniFramework/VulkanApp.cs b/Samples/MiniFramework/VulkanApp.cs index fd39069..613d859 100644 --- a/Samples/MiniFramework/VulkanApp.cs +++ b/Samples/MiniFramework/VulkanApp.cs @@ -5,6 +5,7 @@ using System.Linq; using VulkanCore.Ext; using VulkanCore.Khr; +using VulkanCore.Mvk; namespace VulkanCore.Samples { @@ -49,8 +50,7 @@ public void Initialize(IVulkanAppHost host) { Host = host; #if DEBUG - //At the moment molten-vk doesn't support the debug extension - bool debug = host.Platform != Platform.MacOS; + const bool debug = true; #else const bool debug = false; #endif @@ -64,6 +64,16 @@ public void Initialize(IVulkanAppHost host) ImageAvailableSemaphore = ToDispose(Context.Device.CreateSemaphore()); RenderingFinishedSemaphore = ToDispose(Context.Device.CreateSemaphore()); + if(host.Platform == Platform.MacOS) + { + //Setup MoltenVK specific device configuration. + MVKDeviceConfiguration deviceConfig = Context.Device.GetMVKDeviceConfiguration(); + deviceConfig.DebugMode = debug; + deviceConfig.PerformanceTracking = debug; + deviceConfig.PerformanceLoggingFrameCount = debug ? 300 : 0; + Context.Device.SetMVKDeviceConfiguration(deviceConfig); + } + _initializingPermanent = false; // Calling ToDispose here registers the resource to be automatically disposed on events // such as window resize. @@ -171,7 +181,9 @@ private Instance CreateInstance(bool debug) } var createInfo = new InstanceCreateInfo(); - if (debug) + + //Currently MoltenVK (used for MacOS) doesn't support the debug layer. + if (debug && Host.Platform != Platform.MacOS) { var availableLayers = Instance.EnumerateLayerProperties(); createInfo.EnabledLayerNames = new[] { Constant.InstanceLayer.LunarGStandardValidation } @@ -197,7 +209,8 @@ private Instance CreateInstance(bool debug) private DebugReportCallbackExt CreateDebugReportCallback(bool debug) { - if (!debug) return null; + //Currently MoltenVK (used for MacOS) doesn't support the debug layer. + if (!debug || Host.Platform == Platform.MacOS) return null; // Attach debug callback. var debugReportCreateInfo = new DebugReportCallbackCreateInfoExt( @@ -221,7 +234,7 @@ private SurfaceKhr CreateSurface() case Platform.Win32: return Instance.CreateWin32SurfaceKhr(new Win32SurfaceCreateInfoKhr(Host.InstanceHandle, Host.WindowHandle)); case Platform.MacOS: - return Mvk.InstanceExtensions.CreateMacOSSurfaceMvk(Instance, new Mvk.MacOSSurfaceCreateInfoMvk { View = Host.WindowHandle }); + return Instance.CreateMacOSSurfaceMvk(new MacOSSurfaceCreateInfoMvk(Host.WindowHandle)); default: throw new NotImplementedException(); } diff --git a/Src/Mvk/DeviceExtensions.cs b/Src/Mvk/DeviceExtensions.cs new file mode 100644 index 0000000..d8a1c92 --- /dev/null +++ b/Src/Mvk/DeviceExtensions.cs @@ -0,0 +1,40 @@ +using System; + +namespace VulkanCore.Mvk +{ + /// + /// Provides Brenwill Workshop specific extension methods for the class. + /// + public static unsafe class DeviceExtensions + { + /// + /// Get the current structure for this device + /// + /// The device to get configuration from. + /// The configuration structure + public static MVKDeviceConfiguration GetMVKDeviceConfiguration(this Device device) + { + MVKDeviceConfiguration configuration; + vkGetMoltenVKDeviceConfigurationMVK(device)(device, &configuration); + return configuration; + } + + /// + /// Sets the current structure for this device + /// + /// The device to set the configuration to. + /// Structure containing the configuration parameters. + /// Vulkan returns an error code. + public static void SetMVKDeviceConfiguration(this Device device, MVKDeviceConfiguration configuration) + { + Result result = vkSetMoltenVKDeviceConfigurationMVK(device)(device, &configuration); + VulkanException.ThrowForInvalidResult(result); + } + + private delegate void vkGetMoltenVKDeviceConfigurationMVKDelegate(IntPtr device, MVKDeviceConfiguration* configuration); + private static vkGetMoltenVKDeviceConfigurationMVKDelegate vkGetMoltenVKDeviceConfigurationMVK(Device device) => device.GetProc(nameof(vkGetMoltenVKDeviceConfigurationMVK)); + + private delegate Result vkSetMoltenVKDeviceConfigurationMVKDelegate(IntPtr device, MVKDeviceConfiguration* configuration); + private static vkSetMoltenVKDeviceConfigurationMVKDelegate vkSetMoltenVKDeviceConfigurationMVK(Device device) => device.GetProc(nameof(vkSetMoltenVKDeviceConfigurationMVK)); + } +} diff --git a/Src/Mvk/InstanceExtensions.cs b/Src/Mvk/InstanceExtensions.cs index 33db547..49ff20b 100644 --- a/Src/Mvk/InstanceExtensions.cs +++ b/Src/Mvk/InstanceExtensions.cs @@ -135,6 +135,18 @@ public struct MacOSSurfaceCreateInfoMvk /// public IntPtr View; + /// + /// Initializes a new instance of the structure. + /// + /// Pointer to a NSView that is backed by a CALayer instance of type CAMetalLayer. + public MacOSSurfaceCreateInfoMvk(IntPtr view) + { + View = view; + Type = StructureType.MacOSSurfaceCreateInfoMvk; + Next = IntPtr.Zero; + Flags = 0; + } + internal void Prepare() { Type = StructureType.MacOSSurfaceCreateInfoMvk; diff --git a/Src/Mvk/MVKDeviceConfiguration.cs b/Src/Mvk/MVKDeviceConfiguration.cs new file mode 100644 index 0000000..9ad1e23 --- /dev/null +++ b/Src/Mvk/MVKDeviceConfiguration.cs @@ -0,0 +1,78 @@ +using System; +using System.Runtime.InteropServices; + +namespace VulkanCore.Mvk +{ + /// + /// Structure specifying MoltenVK configuration settings. + /// + [StructLayout(LayoutKind.Sequential)] + public struct MVKDeviceConfiguration + { + /// + /// If enabled, several debugging capabilities will be enabled. Shader code will be logged + /// during Runtime Shader Conversion. Adjusts settings that might trigger Metal validation but + /// are otherwise acceptable to Metal runtime. Improves support for Xcode GPU Frame Capture. + /// Default value is true in the presence of the DEBUG build setting, and false otherwise. + /// + public Bool DebugMode; + /// + /// If enabled, MSL vertex shader code created during Runtime Shader Conversion will flip the + /// Y-axis of each vertex, as Vulkan coordinate system is inverse of OpenGL. Default is true. + /// + public Bool ShaderConversionFlipVertexY; + /// + /// If enabled, queue command submissions (vkQueueSubmit() and vkQueuePresentKHR()) will be + /// processed on the thread that called the submission function. If disabled, processing will + /// be dispatched to a GCD dispatch_queue whose priority is determined by + /// VkDeviceQueueCreateInfo::pQueuePriorities during vkCreateDevice(). This setting affects how + /// much command processing should be performed on the rendering thread, or offloaded to a secondary + /// thread. Default value is false, and command processing will be handled on a prioritizable queue thread. + /// + public Bool SynchronousQueueSubmits; + /// + /// Metal allows only 8192 occlusion queries per MTLBuffer. If enabled, MoltenVK allocates a MTLBuffer + /// for each query pool, allowing each query pool to support 8192 queries, which may slow + /// performance or cause unexpected behaviour if the query pool is not established prior to a + /// Metal renderpass, or if the query pool is changed within a Metal renderpass. If disabled, + /// one MTLBuffer will be shared by all query pools, which improves performance, but limits the total + /// device queries to 8192. Default value is true. + /// + public Bool SupportLargeQueryPools; + /// + /// If enabled, each surface presentation is scheduled using a command buffer. Enabling this may + /// improve rendering frame synchronization, but may result in reduced frame rates. Default value + /// is false if the MVK_PRESENT_WITHOUT_COMMAND_BUFFER build setting is defined when MoltenVK is + /// compiled, and true otherwise. By default the MVK_PRESENT_WITHOUT_COMMAND_BUFFER build setting + /// is not defined and the value of this setting is true. + /// + public Bool PresentWithCommandBuffer; + /// + /// If enabled, a MoltenVK logo watermark will be rendered on top of the scene. This can be + /// enabled for publicity during demos. Default value is true if the MVK_DISPLAY_WATERMARK + /// build setting is defined when MoltenVK is compiled, and false otherwise. By default the + /// MVK_DISPLAY_WATERMARK build setting is not defined. + /// + public Bool DisplayWatermark; + /// + /// If enabled, per-frame performance statistics are tracked, optionally logged, and can be retrieved + /// via the vkGetSwapchainPerformanceMVK() function, and various performance statistics are tracked, + /// logged, and can be retrieved via the vkGetPerformanceStatisticsMVK() function. Default value + /// is true in the presence of the DEBUG build setting, and false otherwise. + /// + public Bool PerformanceTracking; + /// + /// If non-zero, performance statistics will be periodically logged to the console, on a repeating + /// cycle of this many frames per swapchain. The performanceTracking capability must also be enabled. + /// Default value is 300 in the presence of the DEBUG build setting, and zero otherwise. + /// + public UInt32 PerformanceLoggingFrameCount; + /// + /// The maximum amount of time, in nanoseconds, to wait for a Metal library, function or pipeline state + /// object to be compiled and created. If an internal error occurs with the Metal compiler, it can stall + /// the thread for up to 30 seconds. Setting this value limits the delay to that amount of time. + /// Default value is infinite. + /// + public UInt64 MetalCompileTimeout; + } +}