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

摘要:
Vulkan搭建环境-清空窗口认识VulkanVulkan是新一代3D图形API,它继承了OpenGL的优点,弥补了OpenGL的缺憾。Vulkan对GPU的抽象比OpenGL更加细腻。vulkan-sdk.exe太大,我就不提供下载了。这是本人搜罗整理来的一个Vulkan库,外加一些示例代码。Vulkan使用了大量的struct、enum,这与OpenGL类似。Lesson01Clear文件夹下的是第一个示例,展示了Vulkan清空窗口的代码。清空窗口用Vulkan写个清空窗口的程序,就像是用C写个helloworld。Vulkan需要初始化一些东西,在每次渲染时,渲染一些东西。

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(0)搭建环境-清空窗口第1张

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

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

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

1 namespaceLesson01Clear {
2     unsafe classLessonClear {
3     
4         bool isInitialized = false;
5 
6         public voidInit() {
7             if (this.isInitialized) { return; }
8 
9             this.isInitialized = true;
10 }
11 
12         public voidRender() {
13             if (!isInitialized) return;
14 
15 }
16 }
17 }

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

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

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

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

此时的解决方案如下:

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

初始化

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

VkInstance

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

1     unsafe classLessonClear {
2 VkInstance vkIntance;
3         bool isInitialized = false;
4 
5         public voidInit() {
6             if (this.isInitialized) { return; }
7 
8             this.vkIntance =InitInstance();
9 
10             this.isInitialized = true;
11 }
12 
13         privateVkInstance InitInstance() {
14 VkLayerProperties[] layerProperties;
15             Layer.EnumerateInstanceLayerProperties(outlayerProperties);
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 = newVkApplicationInfo();
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 = newVkInstanceCreateInfo();
30 {
31                 info.SType =VkStructureType.InstanceCreateInfo;
32                 extensions.Set(ref info.EnabledExtensionNames, refinfo.EnabledExtensionCount);
33                 layersToEnable.Set(ref info.EnabledLayerNames, refinfo.EnabledLayerCount);
34                 info.ApplicationInfo = (IntPtr)(&appInfo);
35 }
36 
37 VkInstance result;
38             VkInstance.Create(ref info, null, outresult).Check();
39 
40             returnresult;
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 structVkInstanceCreateInfo {
2         publicVkStructureType SType;
3         publicIntPtr Next;
4         publicUInt32 Flags;
5         publicIntPtr ApplicationInfo;
6         publicUInt32 EnabledLayerCount;
7         publicIntPtr EnabledLayerNames;
8         publicUInt32 EnabledExtensionCount; // 数组元素的数量
9         publicIntPtr 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, refUInt32 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, refinfo.EnabledExtensionCount);
3 var layersToEnable = new[] { "VK_LAYER_LUNARG_standard_validation"};
4 layersToEnable.Set(ref info.EnabledLayerNames, ref info.EnabledLayerCount);

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

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

1 namespaceVulkan {
2     public unsafe partial classVkInstance : IDisposable {
3         public readonlyIntPtr handle;
4         private readonly UnmanagedArray<VkAllocationCallbacks>callbacks;
5 
6         public static VkResult Create(ref VkInstanceCreateInfo createInfo, UnmanagedArray<VkAllocationCallbacks> callbacks, outVkInstance instance) {
7             VkResult result =VkResult.Success;
8             var handle = newIntPtr();
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 = newVkInstance(callbacks, handle);
15 
16             returnresult;
17 }
18 
19         private VkInstance(UnmanagedArray<VkAllocationCallbacks>callbacks, IntPtr handle) {
20             this.callbacks =callbacks;
21             this.handle =handle;
22 }
23 
24         public voidDispose() {
25             VkAllocationCallbacks* pAllocator = callbacks != null ? (VkAllocationCallbacks*)callbacks.header : null;
26             vkAPI.vkDestroyInstance(this.handle, pAllocator);
27 }
28 }
29     
30     classvkAPI {
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 }

publicstatic 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 namespaceLesson01Clear {
2     unsafe classLessonClear {
3 VkInstance vkIntance;
4 VkSurfaceKhr vkSurface;
5         bool isInitialized = false;
6 
7         public voidInit(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         privateVkSurfaceKhr InitSurface(VkInstance instance, IntPtr hwnd, IntPtr processHandle) {
17             var info = newVkWin32SurfaceCreateInfoKhr {
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 namespaceLesson01Clear {
2     unsafe classLessonClear {
3 VkInstance vkIntance;
4 VkSurfaceKhr vkSurface;
5 VkPhysicalDevice vkPhysicalDevice;
6         bool isInitialized = false;
7 
8         public voidInit(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         privateVkPhysicalDevice InitPhysicalDevice() {
19 VkPhysicalDevice[] physicalDevices;
20             this.vkIntance.EnumeratePhysicalDevices(outphysicalDevices);
21             return physicalDevices[0];
22 }
23 }
24 }

创建VkPhysicalDivice对象不需要Callback:

1 namespaceVulkan {
2     public unsafe partial classVkPhysicalDevice {
3         public readonlyIntPtr handle;
4 
5         public static VkResult Enumerate(VkInstance instance, outVkPhysicalDevice[] physicalDevices) {
6             if (instance == null) { physicalDevices = null; returnVkResult.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 = newVkPhysicalDevice[count];
16             for (int i = 0; i < count; i++) {
17                 physicalDevices[i] = newVkPhysicalDevice(handles[i]);
18 }
19 
20             returnresult;
21 }
22 
23         privateVkPhysicalDevice(IntPtr handle) {
24             this.handle =handle;
25 }
26 }
27 }

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

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

VkDevice

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

namespaceLesson01Clear {
    unsafe classLessonClear {
        VkInstance vkIntance;
        VkSurfaceKhr vkSurface;
        VkPhysicalDevice vkPhysicalDevice;
        VkDevice vkDevice;
        bool isInitialized = false;
        public voidInit(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, outsurfaceCapabilities);
            this.vkDevice = InitDevice(this.vkPhysicalDevice, this.vkSurface);
            this.isInitialized = true;
        }
        privateVkDevice InitDevice(VkPhysicalDevice physicalDevice, VkSurfaceKhr surface) {
            VkQueueFamilyProperties[] properties =physicalDevice.GetQueueFamilyProperties();
            uintindex;
            for (index = 0; index < properties.Length; ++index) {
                VkBool32 supported;
                physicalDevice.GetSurfaceSupportKhr(index, surface, outsupported);
                if (!supported) { continue; }
                if (properties[index].QueueFlags.HasFlag(VkQueueFlags.QueueGraphics)) break;
            }
            var queueInfo = newVkDeviceQueueCreateInfo();
            {
                queueInfo.SType =VkStructureType.DeviceQueueCreateInfo;
                new float[] { 1.0f }.Set(ref queueInfo.QueuePriorities, refqueueInfo.QueueCount);
                queueInfo.QueueFamilyIndex =index;
            }
            var deviceInfo = newVkDeviceCreateInfo();
            {
                deviceInfo.SType =VkStructureType.DeviceCreateInfo;
                new string[] { "VK_KHR_swapchain" }.Set(ref deviceInfo.EnabledExtensionNames, refdeviceInfo.EnabledExtensionCount);
                new VkDeviceQueueCreateInfo[] { queueInfo }.Set(ref deviceInfo.QueueCreateInfos, refdeviceInfo.QueueCreateInfoCount);
            }
            VkDevice device;
            physicalDevice.CreateDevice(ref deviceInfo, null, outdevice);
            returndevice;
        }
    }
}

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

最后只介绍一下VkCommandBuffer。

VkCommandBuffer

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

1 namespaceLesson01Clear {
2     unsafe classLessonClear {
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 voidInit(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, outsurfaceCapabilities);
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(reffenceInfo);
38             var semaphoreInfo = new VkSemaphoreCreateInfo() { SType =VkStructureType.SemaphoreCreateInfo };
39             this.vkSemaphore = this.vkDevice.CreateSemaphore(refsemaphoreInfo);
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 = newVkCommandPoolCreateInfo {
48                 SType =VkStructureType.CommandPoolCreateInfo,
49                 Flags =VkCommandPoolCreateFlags.ResetCommandBuffer
50 };
51             var commandPool = device.CreateCommandPool(refcreatePoolInfo);
52             var commandBufferAllocateInfo = newVkCommandBufferAllocateInfo {
53                 SType =VkStructureType.CommandBufferAllocateInfo,
54                 Level =VkCommandBufferLevel.Primary,
55                 CommandPool =commandPool.handle,
56                 CommandBufferCount = (uint)images.Length
57 };
58             VkCommandBuffer[] buffers = device.AllocateCommandBuffers(refcommandBufferAllocateInfo);
59             for (int i = 0; i < images.Length; i++) {
60 
61                 var commandBufferBeginInfo = newVkCommandBufferBeginInfo() {
62                     SType =VkStructureType.CommandBufferBeginInfo
63 };
64                 buffers[i].Begin(refcommandBufferBeginInfo);
65 {
66                     var renderPassBeginInfo = newVkRenderPassBeginInfo();
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, refrenderPassBeginInfo.ClearValueCount);
72                         renderPassBeginInfo.RenderArea = newVkRect2D {
73                             Extent =surfaceCapabilities.CurrentExtent
74 };
75 };
76                     buffers[i].CmdBeginRenderPass(refrenderPassBeginInfo, VkSubpassContents.Inline);
77 {
78                         //nothing to do in this lesson.
79 }
80 buffers[i].CmdEndRenderPass();
81 }
82 buffers[i].End();
83 }
84             returnbuffers;
85 }
86 }
87 }

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

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

总结

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

免责声明:文章转载自《Vulkan(0)搭建环境-清空窗口》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇TortoiseGit用户手册C# EF增删改查下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Vue.set()和this.$set()源码解析

前言 我们在日常项目开发过程中,有时候我们对数组或者对象进行了一些操作后,发现页面数据没有更新到。这个时候就会有疑问,why? 如果我们在看文档有这样一个api,以下内容: Vue.set()和this.$set()实现原理 Vue.set()的源码:... 这里是省略的代码 import { set } from '../observer/index'...

服务器中了蠕虫病毒Wannamine2.0小记

近期用户反馈某台服务器总感觉性能不是很好存在卡顿,于是今天远程上去分析。 打开任务管理器发现CPU使用率非常低,内存使用也在接受范围内(10/64G)。不过我有一个偏好就是不喜欢用系统自带的任务管理器查看资源,顺手把procexp搞上去再看一遍。发现rundll32.exe显示占用了62%左右的CPU资源,加载执行一个名为HalPluginServices...

【转载】s3c2440裸机开发调试环境(MDK4.6,Jlink v8,mini2440)

    用于arm裸机程序开发的IDE基本有 以下3个:MDK,IAR,还有ADS。具体它们的具体情况在这里我就不多说了,百度一下就明白了。由于之前开发c51,stm32时候都使用了MDK开发环境,而且MDK的界面确实看起来舒服多了,所以我选择了MDK作为我的s3c2440裸机开发的IDE。以下主要介绍一下如何使用MDK配合J-link来调试基于s3c24...

Python 并发总结,多线程,多进程,异步IO

1 测量函数运行时间 importtime defprofile(func): def wrapper(*args, **kwargs): importtime start =time.time() func(*args, **kwargs) end =time.time()...

Reflect

1.概述 Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API。Reflect对象的设计目的有这样几个。 (1) 将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只部署在Re...

ubuntu 18.04, 编译运行ORB_SLAM3, 遇到错误Pangolin X11: Unable to retrieve framebuffer options。

当前问题 遇到错误Pangolin X11: Unable to retrieve framebuffer options。操作系统是ubuntu 18.04,物理机,不是虚拟机. 按报错Pangolin X11: Unable to retrieve framebuffer options ,修改components/pango_windowing/sr...