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[];
  19.  
  20. var appInfo = new VkApplicationInfo();
  21. {
  22. appInfo.SType = VkStructureType.ApplicationInfo;
  23. uint version = Vulkan.Version.Make(, , );
  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 = ;
  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, );
  26. var src = (byte*)address;
  27. for (int i = ; 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 = ; i < count; i++) {
  12. Marshal.FreeHGlobal(pointer[i]);
  13. }
  14. }
  15. }
  16. {
  17. int length = value.Length;
  18. if (length > ) {
  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 = ; 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[];
  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 > ) {
  12. result = vkAPI.vkEnumeratePhysicalDevices(instance.handle, &count, handles).Check();
  13. }
  14.  
  15. physicalDevices = new VkPhysicalDevice[count];
  16. for (int i = ; 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创建的。

  1. namespace Lesson01Clear {
  2. unsafe class LessonClear {
  3. VkInstance vkIntance;
  4. VkSurfaceKhr vkSurface;
  5. VkPhysicalDevice vkPhysicalDevice;
  6. VkDevice vkDevice;
  7.  
  8. bool isInitialized = false;
  9.  
  10. public void Init(IntPtr hwnd, IntPtr processHandle) {
  11. if (this.isInitialized) { return; }
  12.  
  13. this.vkIntance = InitInstance();
  14. this.vkSurface = InitSurface(this.vkIntance, hwnd, processHandle);
  15. this.vkPhysicalDevice = InitPhysicalDevice();
  16. VkSurfaceFormatKhr surfaceFormat = SelectFormat(this.vkPhysicalDevice, this.vkSurface);
  17. VkSurfaceCapabilitiesKhr surfaceCapabilities;
  18. this.vkPhysicalDevice.GetSurfaceCapabilitiesKhr(this.vkSurface, out surfaceCapabilities);
  19.  
  20. this.vkDevice = InitDevice(this.vkPhysicalDevice, this.vkSurface);
  21.  
  22. this.isInitialized = true;
  23. }
  24.  
  25. private VkDevice InitDevice(VkPhysicalDevice physicalDevice, VkSurfaceKhr surface) {
  26. VkQueueFamilyProperties[] properties = physicalDevice.GetQueueFamilyProperties();
  27. uint index;
  28. for (index = ; index < properties.Length; ++index) {
  29. VkBool32 supported;
  30. physicalDevice.GetSurfaceSupportKhr(index, surface, out supported);
  31. if (!supported) { continue; }
  32.  
  33. if (properties[index].QueueFlags.HasFlag(VkQueueFlags.QueueGraphics)) break;
  34. }
  35.  
  36. var queueInfo = new VkDeviceQueueCreateInfo();
  37. {
  38. queueInfo.SType = VkStructureType.DeviceQueueCreateInfo;
  39. new float[] { 1.0f }.Set(ref queueInfo.QueuePriorities, ref queueInfo.QueueCount);
  40. queueInfo.QueueFamilyIndex = index;
  41. }
  42.  
  43. var deviceInfo = new VkDeviceCreateInfo();
  44. {
  45. deviceInfo.SType = VkStructureType.DeviceCreateInfo;
  46. new string[] { "VK_KHR_swapchain" }.Set(ref deviceInfo.EnabledExtensionNames, ref deviceInfo.EnabledExtensionCount);
  47. new VkDeviceQueueCreateInfo[] { queueInfo }.Set(ref deviceInfo.QueueCreateInfos, ref deviceInfo.QueueCreateInfoCount);
  48. }
  49.  
  50. VkDevice device;
  51. physicalDevice.CreateDevice(ref deviceInfo, null, out device);
  52.  
  53. return device;
  54. }
  55. }
  56. }

后续的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(, );
  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 = ; 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用于指定背景色,这里指定了黄色,运行效果如下:

总结

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

Vulkan(0)搭建环境-清空窗口的更多相关文章

  1. Windows 7旗舰版搭建andriod 4.0开发环境记录

    搭建Android环境步骤(仅供参考): 官方搭建步骤: http://developer.android.com/index.html 搭建环境之前需要下载下面几个文件包: 一.安装Java运行环境 ...

  2. android 5.0开发环境搭建

    Android 5.0 是 Google 于 2014 年 10 月 15 日发布的全新 Android 操作系统.本文将就最新的Android 5.0 开发环境搭建做详细介绍. 工具/原料 jdk- ...

  3. selenium win7+selenium2.0+python环境搭建

    win7+selenium2.0+python环境搭建 by:授客 QQ:1033553122 步骤1:下载python 担心最新版的支持不太好,这里我下载的是python 2.7(selenium之 ...

  4. EJB3.0开发环境的搭建

    EJB Container的介绍SUN公司正式推出了EJB的规范之后,在众多的公司和开发者中引起了非常大的反响.标志着用Java开发企业级应用系统将变的非常easy.很多公司都已经推出了或正打算EJB ...

  5. windows下cocos2dx3.0开发环境及Android编译环境搭建

    cocos2dx更新到了3.x版本号,自己一直没有换,如今开发组要求统一换版本号,我就把搭建好开发环境的过程记录下来. 一.Windowns下开发环境搭建 1.  所需工具         1)coc ...

  6. Jira 6.0.5环境搭建

    敏捷开发-Jira 6.0.5环境搭建[1] 我的环境 Win7 64位,MSSql2008 R2,已经安装tomcat了 拓展环境 jira  6.0.5     百度网盘下载           ...

  7. Vulkan Tutorial 开发环境搭建之Windows

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 相信很多人在开始学习Vulkan开发的起始阶段都会在开发环境的配置上下一些功夫,那么 ...

  8. Hadoop-2.8.0 开发环境搭建(Mac)

    Hadoop是一个由Apache基金会开发的分布式系统架构,简称HDFS,具有高容错性.可伸缩性等特点,并且可以部署在低配置的硬件上:同时,提供了高吞吐量的数据访问性能,适用于超大数据集的应用程序,以 ...

  9. odoo10.0在odoo12.0环境的基础上搭建环境

    在前边的文章中,讲述了如何搭建12.0的环境,现由业务的需要需要在此基础上搭建基于python2.7的10.0版本. 第一步,安装python2.7 sudo apt- 第二步,安装python-de ...

随机推荐

  1. 高级MySQL

    一.MySQL的架构介绍 1.高级MySQL MySQL内核 SQL优化 MySQL服务器的优化 各种参数常亮设定 查询语句优化 主从复制 软硬件升级 容灾备份 SQL编程 2.MySQL的Linux ...

  2. Python解释器安装教程和环境变量配置

    Python解释器安装教程和环境变量配置 Python解释器安装 登录Python的官方网站   https://www.python.org/  进行相应版本的下载. 第一步:根据电脑系统选择软件适 ...

  3. 10-Helm

    Helm Chart仓库 helm 架构 https://helm.sh/docs/architecture/ 主要概念 chart 创建Kubernetes应用程序实例所必需的一组信息 config ...

  4. T-SQL 镜像测试

    --====================================================== ----镜像计划建立 2016-05-10 17:05:16.463 hubiyun ...

  5. 警惕!CAF效应导致PCB漏电

    最近碰到一个PCB漏电的问题,起因是一款低功耗产品,本来整机uA级别的电流,常温老化使用了一段时间后发现其功耗上升,个别样机功耗甚至达到了mA级别.仔细排除了元器件问题,最终发现了一个5V电压点,在产 ...

  6. 分布式ID系列之为什么需要分布式ID以及生成分布式ID的业务需求

    为什么需要分布式id生成系统 在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识.如在美团点评的金融.支付.餐饮.酒店.猫眼电影等产品的系统中,数据日渐增长,对数据分库分表后需要有一个唯一ID ...

  7. solidity智能合约字节数最大值及缩减字节数

    智能合约最大字节数 在Solidity中,EIP 170将contract的最大大小限制为24 KB .因此,如果智能合约内容过多,会导致无法进行发布操作. 减少压缩字节数方法 方法及变量命名 在一定 ...

  8. Lua语言学习

    1,语法 语句不用分号结尾 function ... end if .. else .. end 2, io库, string库, table库, OS库, 算术库, debug库 3, dofile ...

  9. Oracle创建设置查询权限用户

    用户创建的可以参考博客: https://blog.csdn.net/u014427391/article/details/84889023 Oracle授权表权限给用户: 语法:grant [权限名 ...

  10. Linux curl 表单登录或提交与cookie使用

    本文主要讲解通过curl 实现表单提交登录.单独的表单提交与表单登录都差不多,因此就不单独说了. 说明:针对curl表单提交实现登录,不是所有网站都适用,原因是有些网站后台做了限制或有其他校验.我们不 ...