• Vulkan(0)搭建环境-清空窗口


    Vulkan(0)搭建环境-清空窗口

    认识Vulkan

    Vulkan是新一代3D图形API,它继承了OpenGL的优点,弥补了OpenGL的缺憾。有点像科创板之于主板,歼20之于歼10,微信之于QQ,网店之于实体店,今日之于昨日。

    使用OpenGL时,每次drawcall都需要向OpenGL提交很多数据。而Vulkan可以提前将这些drawcall指令保存到一个buffer(像保存顶点数据到buffer一样),这样就减少了很多开销。

    使用OpenGL时,OpenGL的Context会包含很多你并不打算使用的东西,例如线的宽度、混合等。而Vulkan不会提供这些你用不到的东西,你需要什么,你来指定。(当然,你不指定,Vulkan不会自动地提供)

    Vulkan还支持多线程,OpenGL这方面就不行了。

    Vulkan对GPU的抽象比OpenGL更加细腻。

    搭建环境

    本文和本系列都将使用C#和Visual Studio 2017来学习使用Vulkan。

    首先,在官网(https://vulkan.lunarg.com)下载vulkan-sdk.exe和vulkan-runtime.exe。完后安装。vulkan-runtime.exe也可以在(https://files.cnblogs.com/files/bitzhuwei/vulkan-runtime.rar)下载。vulkan-sdk.exe太大,我就不提供下载了。

    然后,下载Vulkan.net库(https://github.com/bitzhuwei/Vulkan.net)。这是本人搜罗整理来的一个Vulkan库,外加一些示例代码。用VS2017打开Vulkan.sln,在这个解决方案下就可以学习使用Vulkan了。

    如果读者在Github上的下载速度太慢,可以试试将各个文件单独点开下载。这很笨,但也是个办法。

    简单介绍下此解决方案。

    Vulkan文件夹下的Vulkan.csproj是对Vulkan API的封装。Vulkan使用了大量的struct、enum,这与OpenGL类似。

    Vulkan.Platforms文件夹下的Vulkan.Platforms.csproj是平台相关的一些API。

    Lesson01Clear文件夹下的是第一个示例,展示了Vulkan清空窗口的代码。以后会逐步添加更多的示例。

    有了这个库,读者就可以运行示例程序,一点点地读代码,慢慢理解Vulkan了。这也是本人用的最多的学习方法。遇到不懂的就上网搜索,毕竟我没有别人可以问。

    这个库还很不成熟,以后会有大的改动。但这不妨碍学习,反而是学习的好资料,在变动的过程中方能体会软件工程的精髓。

    清空窗口

    用Vulkan写个清空窗口的程序,就像是用C写个hello world。

    外壳

    新建Windows窗体应用程序。

     

     添加对类库Vulkan和Vulkan.Platforms的引用:

     

     添加此项目的核心类型LessonClear。Vulkan需要初始化(Init)一些东西,在每次渲染时,渲染(Render)一些东西。

     1 namespace Lesson01Clear {
     2     unsafe class LessonClear {
     3     
     4         bool isInitialized = false;
     5 
     6         public void Init() {
     7             if (this.isInitialized) { return; }
     8 
     9             this.isInitialized = true;
    10         }
    11 
    12         public void Render() {
    13             if (!isInitialized) return;
    14 
    15         }
    16     }
    17 }

    添加一个User Control,用以调用LessonClear。

     1 namespace Lesson01Clear {
     2     public partial class UCClear : UserControl {
     3 
     4         LessonClear lesson;
     5 
     6         public UCClear() {
     7             InitializeComponent();
     8         }
     9 
    10         protected override void OnLoad(EventArgs e) {
    11             base.OnLoad(e);
    12 
    13             this.lesson = new LessonClear();
    14             this.lesson.Init();
    15         }
    16 
    17         protected override void OnPaintBackground(PaintEventArgs e) {
    18             var lesson = this.lesson;
    19             if (lesson != null) {
    20                 lesson.Render();
    21             }
    22         }
    23     }
    24 }

    在主窗口中添加一个自定义控件UCClear。这样,在窗口启动时,就会自动执行LessonClear的初始化和渲染功能了。

     

     此时的解决方案如下:

     

    初始化

    要初始化的东西比较多,我们一项一项来看。

    VkInstance

    在LessonClear中添加成员变量VkInstance vkIntance,在InitInstance()函数中初始化它。

     1     unsafe class LessonClear {
     2         VkInstance vkIntance;
     3         bool isInitialized = false;
     4 
     5         public void Init() {
     6             if (this.isInitialized) { return; }
     7 
     8             this.vkIntance = InitInstance();
     9 
    10             this.isInitialized = true;
    11         }
    12 
    13         private VkInstance InitInstance() {
    14             VkLayerProperties[] layerProperties;
    15             Layer.EnumerateInstanceLayerProperties(out layerProperties);
    16             string[] layersToEnable = layerProperties.Any(l => StringHelper.ToStringAnsi(l.LayerName) == "VK_LAYER_LUNARG_standard_validation")
    17                 ? new[] { "VK_LAYER_LUNARG_standard_validation" }
    18                 : new string[0];
    19 
    20             var appInfo = new VkApplicationInfo();
    21             {
    22                 appInfo.SType = VkStructureType.ApplicationInfo;
    23                 uint version = Vulkan.Version.Make(1, 0, 0);
    24                 appInfo.ApiVersion = version;
    25             }
    26 
    27             var extensions = new string[] { "VK_KHR_surface", "VK_KHR_win32_surface", "VK_EXT_debug_report" };
    28 
    29             var info = new VkInstanceCreateInfo();
    30             {
    31                 info.SType = VkStructureType.InstanceCreateInfo;
    32                 extensions.Set(ref info.EnabledExtensionNames, ref info.EnabledExtensionCount);
    33                 layersToEnable.Set(ref info.EnabledLayerNames, ref info.EnabledLayerCount);
    34                 info.ApplicationInfo = (IntPtr)(&appInfo);
    35             }
    36 
    37             VkInstance result;
    38             VkInstance.Create(ref info, null, out result).Check();
    39 
    40             return result;
    41         }
    42     }

     VkInstance的extension和layer是什么,一时难以说清,先不管。VkInstance像是一个缓存,它根据用户提供的参数,准备好了用户可能要用的东西。在创建VkInstance时,我明显感到程序卡顿了1秒。如果用户稍后请求的东西在缓存中,VkInstance就立即提供给他;如果不在,VkInstance就不给,并抛出VkResult。

    以“Vk”开头的一般是Vulkan的结构体,或者对某种Vulkan对象的封装。

    VkInstance就是一个对Vulkan对象的封装。创建一个VkInstance对象时,Vulkan的API只会返回一个 IntPtr 指针。在本库中,用一个class VkInstance将其封装起来,以便使用。

    创建一个VkInstance对象时,需要我们提供给Vulkan API一个对应的 VkInstanceCreateInfo 结构体。这个结构体包含了创建VkInstance所需的各种信息,例如我们想让这个VkInstance支持哪些extension、哪些layer等。对于extension,显然,这必须用一个数组指针IntPtr和extension的总数来描述。

     1     public struct VkInstanceCreateInfo {
     2         public VkStructureType SType;
     3         public IntPtr Next;
     4         public UInt32 Flags;
     5         public IntPtr ApplicationInfo;
     6         public UInt32 EnabledLayerCount;
     7         public IntPtr EnabledLayerNames;
     8         public UInt32 EnabledExtensionCount; // 数组元素的数量
     9         public IntPtr EnabledExtensionNames; // 数组指针
    10     }

    这样的情况在Vulkan十分普遍,所以本库提供一个扩展方法来执行这一操作:

     1     /// <summary>
     2     /// Set an array of structs to specified <paramref name="target"/> and <paramref name="count"/>.
     3     /// <para>Enumeration types are not allowed to use this method.
     4     /// If you have to, convert them to byte/short/ushort/int/uint according to their underlying types first.</para>
     5     /// </summary>
     6     /// <param name="value"></param>
     7     /// <param name="target">address of first element/array.</param>
     8     /// <param name="count">How many elements?</param>
     9     public static void Set<T>(this T[] value, ref IntPtr target, ref UInt32 count) where T : struct {
    10         {   // free unmanaged memory.
    11             if (target != IntPtr.Zero) {
    12                 Marshal.FreeHGlobal(target);
    13                 target = IntPtr.Zero;
    14                 count = 0;
    15             }
    16         }
    17         {
    18             count = (UInt32)value.Length;
    19 
    20             int elementSize = Marshal.SizeOf<T>();
    21             int byteLength = (int)(count * elementSize);
    22             IntPtr array = Marshal.AllocHGlobal(byteLength);
    23             var dst = (byte*)array;
    24             GCHandle pin = GCHandle.Alloc(value, GCHandleType.Pinned);
    25             IntPtr address = Marshal.UnsafeAddrOfPinnedArrayElement(value, 0);
    26             var src = (byte*)address;
    27             for (int i = 0; i < byteLength; i++) {
    28                 dst[i] = src[i];
    29             }
    30             pin.Free();
    31 
    32             target = array;
    33         }
    34     }

     这个Set<T>()函数的核心作用是:在非托管内存上创建一个数组,将托管内存中的数组T[] value中的数据复制过去,然后,记录非托管内存中的数组的首地址(target)和元素数量(count)。当然,如果这不是第一次让target记录非托管内存中的某个数组,那就意味着首先应当将target指向的数组释放掉。

    如果这里的T是枚举类型, Marshal.SizeOf() 会抛出异常,所以,必须先将枚举数组转换为 byte/short/ushort/int/uint 类型的数组。至于Marshal.SizeOf为什么会抛异常,我也不知道。

    如果这里的T是string,那么必须用另一个变种函数代替:

     1     /// <summary>
     2     /// Set an array of strings to specified <paramref name="target"/> and <paramref name="count"/>.
     3     /// </summary>
     4     /// <param name="value"></param>
     5     /// <param name="target">address of first element/array.</param>
     6     /// <param name="count">How many elements?</param>
     7     public static void Set(this string[] value, ref IntPtr target, ref UInt32 count) {
     8         {   // free unmanaged memory.
     9             var pointer = (IntPtr*)(target.ToPointer());
    10             if (pointer != null) {
    11                 for (int i = 0; i < count; i++) {
    12                     Marshal.FreeHGlobal(pointer[i]);
    13                 }
    14             }
    15         }
    16         {
    17             int length = value.Length;
    18             if (length > 0) {
    19                 int elementSize = Marshal.SizeOf(typeof(IntPtr));
    20                 int byteLength = (int)(length * elementSize);
    21                 IntPtr array = Marshal.AllocHGlobal(byteLength);
    22                 IntPtr* pointer = (IntPtr*)array.ToPointer();
    23                 for (int i = 0; i < length; i++) {
    24                     IntPtr str = Marshal.StringToHGlobalAnsi(value[i]);
    25                     pointer[i] = str;
    26                 }
    27                 target = array;
    28             }
    29             count = (UInt32)length;
    30         }
    31     }
    public static void Set(this string[] value, ref IntPtr target, ref UInt32 count)

    实现和解释起来略显复杂,但使用起来十分简单:

    1 var extensions = new string[] { "VK_KHR_surface", "VK_KHR_win32_surface", "VK_EXT_debug_report" };
    2 extensions.Set(ref info.EnabledExtensionNames, ref info.EnabledExtensionCount);
    3 var layersToEnable = new[] { "VK_LAYER_LUNARG_standard_validation" };
    4 layersToEnable.Set(ref info.EnabledLayerNames, ref info.EnabledLayerCount);

     在后续创建其他Vulkan对象时,我们将多次使用这一方法。

    创建VkInstance的内部过程,就是调用Vulkan API的问题:

     1 namespace Vulkan {
     2     public unsafe partial class VkInstance : IDisposable {
     3         public readonly IntPtr handle;
     4         private readonly UnmanagedArray<VkAllocationCallbacks> callbacks;
     5 
     6         public static VkResult Create(ref VkInstanceCreateInfo createInfo, UnmanagedArray<VkAllocationCallbacks> callbacks, out VkInstance instance) {
     7             VkResult result = VkResult.Success;
     8             var handle = new IntPtr();
     9             VkAllocationCallbacks* pAllocator = callbacks != null ? (VkAllocationCallbacks*)callbacks.header : null;
    10             fixed (VkInstanceCreateInfo* pCreateInfo = &createInfo) {
    11                 vkAPI.vkCreateInstance(pCreateInfo, pAllocator, &handle).Check();
    12             }
    13 
    14             instance = new VkInstance(callbacks, handle);
    15 
    16             return result;
    17         }
    18 
    19         private VkInstance(UnmanagedArray<VkAllocationCallbacks> callbacks, IntPtr handle) {
    20             this.callbacks = callbacks;
    21             this.handle = handle;
    22         }
    23 
    24         public void Dispose() {
    25             VkAllocationCallbacks* pAllocator = callbacks != null ? (VkAllocationCallbacks*)callbacks.header : null;
    26             vkAPI.vkDestroyInstance(this.handle, pAllocator);
    27         }
    28     }
    29     
    30     class vkAPI {
    31         const string VulkanLibrary = "vulkan-1";
    32 
    33         [DllImport(VulkanLibrary, CallingConvention = CallingConvention.Winapi)]
    34         internal static unsafe extern VkResult vkCreateInstance(VkInstanceCreateInfo* pCreateInfo, VkAllocationCallbacks* pAllocator, IntPtr* pInstance);
    35 
    36         [DllImport(VulkanLibrary, CallingConvention = CallingConvention.Winapi)]
    37         internal static unsafe extern void vkDestroyInstance(IntPtr instance, VkAllocationCallbacks* pAllocator);
    38     }
    39 }

    在 public static VkResult Create(ref VkInstanceCreateInfo createInfo, UnmanagedArray<VkAllocationCallbacks> callbacks, out VkInstance instance); 函数中:

    第一个参数用ref标记,是因为这样就会强制程序员提供一个 VkInstanceCreateInfo 结构体。如果改用 VkInstanceCreateInfo* ,那么程序员就有可能提供一个null指针,这对于Vulkan API的 vkCreateInstance() 是没有应用意义的。

    对第二个参数提供null指针是有应用意义的,但是,如果用 VkAllocationCallbacks* ,那么此参数指向的对象仍旧可能位于托管内存中(从而,在后续阶段,其位置有可能被GC改变)。用 UnmanagedArray<VkAllocationCallbacks> 就可以保证它位于非托管内存

    对于第三个参数,之所以让它用out标记(而不是放到返回值上),是因为 vkCreateInstance() 的返回值是 VkResult 。这样写,可以保持代码的风格与Vulkan一致。如果以后需要用切面编程之类的的方式添加log等功能,这样的一致性就会带来便利。

    在函数中声明的结构体变量(例如这里的 var handle = new IntPtr(); ),可以直接取其地址( &handle )。

    创建VkInstance的方式方法流程,与创建其他Vulkan对象的方式方法流程是极其相似的。读者可以触类旁通。

    VkSurfaceKhr

    在LessonClear中添加成员变量VkSurfaceKhr vkSurface,在InitSurface()函数中初始化它。

     1 namespace Lesson01Clear {
     2     unsafe class LessonClear {
     3         VkInstance vkIntance;
     4         VkSurfaceKhr vkSurface;
     5         bool isInitialized = false;
     6 
     7         public void Init(IntPtr hwnd, IntPtr processHandle) {
     8             if (this.isInitialized) { return; }
     9 
    10             this.vkIntance = InitInstance();
    11             this.vkSurface = InitSurface(this.vkIntance, hwnd, processHandle);
    12             
    13             this.isInitialized = true;
    14         }
    15 
    16         private VkSurfaceKhr InitSurface(VkInstance instance, IntPtr hwnd, IntPtr processHandle) {
    17             var info = new VkWin32SurfaceCreateInfoKhr {
    18                 SType = VkStructureType.Win32SurfaceCreateInfoKhr,
    19                 Hwnd = hwnd, // handle of User Control.
    20                 Hinstance = processHandle, //Process.GetCurrentProcess().Handle
    21             };
    22             return instance.CreateWin32SurfaceKHR(ref info, null);
    23         }
    24     }
    25 }

     可见,VkSurfaceKhr的创建与VkInstance遵循同样的模式,只是CreateInfo内容比较少。VkSurfaceKhr需要知道窗口句柄和进程句柄,这样它才能渲染到相应的窗口/控件上。

    VkPhysicalDevice

    这里的物理设备指的就是我们的计算机上的GPU了。

     1 namespace Lesson01Clear {
     2     unsafe class LessonClear {
     3         VkInstance vkIntance;
     4         VkSurfaceKhr vkSurface;
     5         VkPhysicalDevice vkPhysicalDevice;
     6         bool isInitialized = false;
     7 
     8         public void Init(IntPtr hwnd, IntPtr processHandle) {
     9             if (this.isInitialized) { return; }
    10 
    11             this.vkIntance = InitInstance();
    12             this.vkSurface = InitSurface(this.vkIntance, hwnd, processHandle);
    13             this.vkPhysicalDevice = InitPhysicalDevice();
    14 
    15             this.isInitialized = true;
    16         }
    17 
    18         private VkPhysicalDevice InitPhysicalDevice() {
    19             VkPhysicalDevice[] physicalDevices;
    20             this.vkIntance.EnumeratePhysicalDevices(out physicalDevices);
    21             return physicalDevices[0];
    22         }
    23     }
    24 }

    创建VkPhysicalDivice对象不需要Callback:

     1 namespace Vulkan {
     2     public unsafe partial class VkPhysicalDevice {
     3         public readonly IntPtr handle;
     4 
     5         public static VkResult Enumerate(VkInstance instance, out VkPhysicalDevice[] physicalDevices) {
     6             if (instance == null) { physicalDevices = null; return VkResult.Incomplete; }
     7 
     8             UInt32 count;
     9             VkResult result = vkAPI.vkEnumeratePhysicalDevices(instance.handle, &count, null).Check();
    10             var handles = stackalloc IntPtr[(int)count];
    11             if (count > 0) {
    12                 result = vkAPI.vkEnumeratePhysicalDevices(instance.handle, &count, handles).Check();
    13             }
    14 
    15             physicalDevices = new VkPhysicalDevice[count];
    16             for (int i = 0; i < count; i++) {
    17                 physicalDevices[i] = new VkPhysicalDevice(handles[i]);
    18             }
    19 
    20             return result;
    21         }
    22 
    23         private VkPhysicalDevice(IntPtr handle) {
    24             this.handle = handle;
    25         }
    26     }
    27 }

    在函数中声明的变量(例如这里的 var handle = new IntPtr(); ),可以直接取其地址( &handle )。

    但是在函数中声明的数组,数组本身是在中的,不能直接取其地址。为了能够取其地址,可以用( var handles = stackalloc IntPtr[(int)count]; )这样的方式,这会将数组本身创建到函数自己的空间,从而可以直接取其地址了。

    VkDevice

    这个设备是对物理设备的缓存抽象接口,我们想使用物理设备的哪些功能,就在CreateInfo中指定,然后创建VkDevice。(不指定的功能,以后就无法使用。)后续各种对象,都是用VkDevice创建的。

    namespace Lesson01Clear {
        unsafe class LessonClear {
            VkInstance vkIntance;
            VkSurfaceKhr vkSurface;
            VkPhysicalDevice vkPhysicalDevice;
            VkDevice vkDevice;
            
            bool isInitialized = false;
    
            public void Init(IntPtr hwnd, IntPtr processHandle) {
                if (this.isInitialized) { return; }
    
                this.vkIntance = InitInstance();
                this.vkSurface = InitSurface(this.vkIntance, hwnd, processHandle);
                this.vkPhysicalDevice = InitPhysicalDevice();
                VkSurfaceFormatKhr surfaceFormat = SelectFormat(this.vkPhysicalDevice, this.vkSurface);
                VkSurfaceCapabilitiesKhr surfaceCapabilities;
                this.vkPhysicalDevice.GetSurfaceCapabilitiesKhr(this.vkSurface, out surfaceCapabilities);
    
                this.vkDevice = InitDevice(this.vkPhysicalDevice, this.vkSurface);
    
                this.isInitialized = true;
            }
    
            private VkDevice InitDevice(VkPhysicalDevice physicalDevice, VkSurfaceKhr surface) {
                VkQueueFamilyProperties[] properties = physicalDevice.GetQueueFamilyProperties();
                uint index;
                for (index = 0; index < properties.Length; ++index) {
                    VkBool32 supported;
                    physicalDevice.GetSurfaceSupportKhr(index, surface, out supported);
                    if (!supported) { continue; }
    
                    if (properties[index].QueueFlags.HasFlag(VkQueueFlags.QueueGraphics)) break;
                }
    
                var queueInfo = new VkDeviceQueueCreateInfo();
                {
                    queueInfo.SType = VkStructureType.DeviceQueueCreateInfo;
                    new float[] { 1.0f }.Set(ref queueInfo.QueuePriorities, ref queueInfo.QueueCount);
                    queueInfo.QueueFamilyIndex = index;
                }
    
                var deviceInfo = new VkDeviceCreateInfo();
                {
                    deviceInfo.SType = VkStructureType.DeviceCreateInfo;
                    new string[] { "VK_KHR_swapchain" }.Set(ref deviceInfo.EnabledExtensionNames, ref deviceInfo.EnabledExtensionCount);
                    new VkDeviceQueueCreateInfo[] { queueInfo }.Set(ref deviceInfo.QueueCreateInfos, ref deviceInfo.QueueCreateInfoCount);
                }
    
                VkDevice device;
                physicalDevice.CreateDevice(ref deviceInfo, null, out device);
    
                return device;
            }
        }
    }

     后续的Queue、Swapchain、Image、RenderPass、Framebuffer、Fence和Semaphore等都不再一一介绍,毕竟都是十分类似的创建过程。

    最后只介绍一下VkCommandBuffer。

    VkCommandBuffer

    Vulkan可以将很多渲染指令保存到buffer,将buffer一次性上传到GPU内存,这样以后每次调用它即可,不必重复提交这些数据了。

     1 namespace Lesson01Clear {
     2     unsafe class LessonClear {
     3         VkInstance vkIntance;
     4         VkSurfaceKhr vkSurface;
     5         VkPhysicalDevice vkPhysicalDevice;
     6 
     7         VkDevice vkDevice;
     8         VkQueue vkQueue;
     9         VkSwapchainKhr vkSwapchain;
    10         VkImage[] vkImages;
    11         VkRenderPass vkRenderPass;
    12         VkFramebuffer[] vkFramebuffers;
    13         VkFence vkFence;
    14         VkSemaphore vkSemaphore;
    15         VkCommandBuffer[] vkCommandBuffers;
    16         bool isInitialized = false;
    17 
    18         public void Init(IntPtr hwnd, IntPtr processHandle) {
    19             if (this.isInitialized) { return; }
    20 
    21             this.vkIntance = InitInstance();
    22             this.vkSurface = InitSurface(this.vkIntance, hwnd, processHandle);
    23             this.vkPhysicalDevice = InitPhysicalDevice();
    24             VkSurfaceFormatKhr surfaceFormat = SelectFormat(this.vkPhysicalDevice, this.vkSurface);
    25             VkSurfaceCapabilitiesKhr surfaceCapabilities;
    26             this.vkPhysicalDevice.GetSurfaceCapabilitiesKhr(this.vkSurface, out surfaceCapabilities);
    27 
    28             this.vkDevice = InitDevice(this.vkPhysicalDevice, this.vkSurface);
    29 
    30             this.vkQueue = this.vkDevice.GetDeviceQueue(0, 0);
    31             this.vkSwapchain = CreateSwapchain(this.vkDevice, this.vkSurface, surfaceFormat, surfaceCapabilities);
    32             this.vkImages = this.vkDevice.GetSwapchainImagesKHR(this.vkSwapchain);
    33             this.vkRenderPass = CreateRenderPass(this.vkDevice, surfaceFormat);
    34             this.vkFramebuffers = CreateFramebuffers(this.vkDevice, this.vkImages, surfaceFormat, this.vkRenderPass, surfaceCapabilities);
    35 
    36             var fenceInfo = new VkFenceCreateInfo() { SType = VkStructureType.FenceCreateInfo };
    37             this.vkFence = this.vkDevice.CreateFence(ref fenceInfo);
    38             var semaphoreInfo = new VkSemaphoreCreateInfo() { SType = VkStructureType.SemaphoreCreateInfo };
    39             this.vkSemaphore = this.vkDevice.CreateSemaphore(ref semaphoreInfo);
    40 
    41             this.vkCommandBuffers = CreateCommandBuffers(this.vkDevice, this.vkImages, this.vkFramebuffers, this.vkRenderPass, surfaceCapabilities);
    42 
    43             this.isInitialized = true;
    44         }
    45 
    46         VkCommandBuffer[] CreateCommandBuffers(VkDevice device, VkImage[] images, VkFramebuffer[] framebuffers, VkRenderPass renderPass, VkSurfaceCapabilitiesKhr surfaceCapabilities) {
    47             var createPoolInfo = new VkCommandPoolCreateInfo {
    48                 SType = VkStructureType.CommandPoolCreateInfo,
    49                 Flags = VkCommandPoolCreateFlags.ResetCommandBuffer
    50             };
    51             var commandPool = device.CreateCommandPool(ref createPoolInfo);
    52             var commandBufferAllocateInfo = new VkCommandBufferAllocateInfo {
    53                 SType = VkStructureType.CommandBufferAllocateInfo,
    54                 Level = VkCommandBufferLevel.Primary,
    55                 CommandPool = commandPool.handle,
    56                 CommandBufferCount = (uint)images.Length
    57             };
    58             VkCommandBuffer[] buffers = device.AllocateCommandBuffers(ref commandBufferAllocateInfo);
    59             for (int i = 0; i < images.Length; i++) {
    60 
    61                 var commandBufferBeginInfo = new VkCommandBufferBeginInfo() {
    62                     SType = VkStructureType.CommandBufferBeginInfo
    63                 };
    64                 buffers[i].Begin(ref commandBufferBeginInfo);
    65                 {
    66                     var renderPassBeginInfo = new VkRenderPassBeginInfo();
    67                     {
    68                         renderPassBeginInfo.SType = VkStructureType.RenderPassBeginInfo;
    69                         renderPassBeginInfo.Framebuffer = framebuffers[i].handle;
    70                         renderPassBeginInfo.RenderPass = renderPass.handle;
    71                         new VkClearValue[] { new VkClearValue { Color = new VkClearColorValue(0.9f, 0.7f, 0.0f, 1.0f) } }.Set(ref renderPassBeginInfo.ClearValues, ref renderPassBeginInfo.ClearValueCount);
    72                         renderPassBeginInfo.RenderArea = new VkRect2D {
    73                             Extent = surfaceCapabilities.CurrentExtent
    74                         };
    75                     };
    76                     buffers[i].CmdBeginRenderPass(ref renderPassBeginInfo, VkSubpassContents.Inline);
    77                     {
    78                         // nothing to do in this lesson.
    79                     }
    80                     buffers[i].CmdEndRenderPass();
    81                 }
    82                 buffers[i].End();
    83             }
    84             return buffers;
    85         }
    86     }
    87 }

     本例中的VkClearValue用于指定背景色,这里指定了黄色,运行效果如下:

    总结

    如果看不懂本文,就去看代码,运行代码,再来看本文。反反复复看,总会懂。

  • 相关阅读:
    iOS:图片相关(19-05-09更)
    iOS:文字相关(19-01-08更)
    iOS:通信录(完成)(18-01-18更)
    Swift 中的Closures(闭包)详解
    Swift 中的高阶函数和函数嵌套
    Swift API设计原则
    Swift下CoreData的使用
    iOS 使用贝塞尔曲线绘制路径
    iOS CALayer使用
    iOS 多线程的使用
  • 原文地址:https://www.cnblogs.com/bitzhuwei/p/Vulkan-0-Environment-Clear-Window.html
Copyright © 2020-2023  润新知