461
|
1 import std/enumerate
|
|
2
|
|
3 import ./vulkan
|
|
4 import ./vulkan_helpers
|
|
5 import ./xlib_helpers
|
|
6
|
|
7 import ./glslang/glslang
|
|
8
|
462
|
9 var vertexShaderCode: string = """#version 450
|
|
10 layout(location = 0) out vec3 fragColor;
|
|
11 vec3 colors[3] = vec3[](
|
|
12 vec3(1.0, 0.0, 0.0),
|
|
13 vec3(0.0, 1.0, 0.0),
|
|
14 vec3(0.0, 0.0, 1.0)
|
|
15 );
|
|
16 vec2 positions[3] = vec2[](
|
|
17 vec2(0.0, -0.5),
|
|
18 vec2(0.5, 0.5),
|
|
19 vec2(-0.5, 0.5)
|
|
20 );
|
|
21 void main() {
|
|
22 gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
|
|
23 fragColor = colors[gl_VertexIndex];
|
|
24 }"""
|
|
25
|
|
26 var fragmentShaderCode: string = """#version 450
|
|
27 layout(location = 0) out vec4 outColor;
|
|
28 layout(location = 0) in vec3 fragColor;
|
|
29 void main() {
|
|
30 outColor = vec4(fragColor, 1.0);
|
|
31 }"""
|
461
|
32
|
|
33 import
|
|
34 x11/xlib,
|
|
35 x11/x
|
|
36
|
|
37 const VULKAN_VERSION = VK_MAKE_API_VERSION(0'u32, 1'u32, 2'u32, 0'u32)
|
|
38
|
|
39 type
|
462
|
40 GraphicsPipeline = object
|
|
41 shaderStages*: seq[VkPipelineShaderStageCreateInfo]
|
461
|
42 QueueFamily = object
|
|
43 properties*: VkQueueFamilyProperties
|
|
44 hasSurfaceSupport*: bool
|
|
45 PhyscialDevice = object
|
|
46 device*: VkPhysicalDevice
|
|
47 extensions*: seq[string]
|
|
48 properties*: VkPhysicalDeviceProperties
|
|
49 features*: VkPhysicalDeviceFeatures
|
|
50 queueFamilies*: seq[QueueFamily]
|
|
51 surfaceCapabilities*: VkSurfaceCapabilitiesKHR
|
|
52 surfaceFormats: seq[VkSurfaceFormatKHR]
|
|
53 presentModes: seq[VkPresentModeKHR]
|
|
54 Vulkan* = object
|
|
55 instance*: VkInstance
|
|
56 deviceList*: seq[PhyscialDevice]
|
|
57 activePhysicalDevice*: PhyscialDevice
|
|
58 activeQueueFamily*: uint32
|
|
59 device*: VkDevice
|
|
60 presentationQueue*: VkQueue
|
|
61 surface*: VkSurfaceKHR
|
|
62 selectedSurfaceFormat: VkSurfaceFormatKHR
|
|
63 selectedPresentationMode: VkPresentModeKHR
|
|
64 selectedExtent: VkExtent2D
|
|
65 swapChain: VkSwapchainKHR
|
|
66 swapImages: seq[VkImage]
|
|
67 swapImageViews: seq[VkImageView]
|
|
68 Engine* = object
|
|
69 display*: PDisplay
|
|
70 window*: x.Window
|
|
71 vulkan*: Vulkan
|
462
|
72 pipeline*: GraphicsPipeline
|
461
|
73
|
|
74
|
|
75 proc getAllPhysicalDevices(instance: VkInstance, surface: VkSurfaceKHR): seq[PhyscialDevice] =
|
|
76 for vulkanPhysicalDevice in getVulkanPhysicalDevices(instance):
|
|
77 var device = PhyscialDevice(device: vulkanPhysicalDevice, extensions: getDeviceExtensions(vulkanPhysicalDevice))
|
|
78 vkGetPhysicalDeviceProperties(vulkanPhysicalDevice, addr(device.properties))
|
|
79 vkGetPhysicalDeviceFeatures(vulkanPhysicalDevice, addr(device.features))
|
|
80 checkVkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vulkanPhysicalDevice, surface, addr(device.surfaceCapabilities))
|
|
81 device.surfaceFormats = getDeviceSurfaceFormats(vulkanPhysicalDevice, surface)
|
|
82 device.presentModes = getDeviceSurfacePresentModes(vulkanPhysicalDevice, surface)
|
|
83
|
|
84 for i, queueFamilyProperty in enumerate(getQueueFamilies(vulkanPhysicalDevice)):
|
462
|
85 var hasSurfaceSupport: VkBool32 = VK_FALSE
|
461
|
86 checkVkResult vkGetPhysicalDeviceSurfaceSupportKHR(vulkanPhysicalDevice, uint32(i), surface, addr(hasSurfaceSupport))
|
|
87 device.queueFamilies.add(QueueFamily(properties: queueFamilyProperty, hasSurfaceSupport: bool(hasSurfaceSupport)))
|
|
88
|
|
89 result.add(device)
|
|
90
|
|
91 proc filterForDevice(devices: seq[PhyscialDevice]): seq[(PhyscialDevice, uint32)] =
|
|
92 for device in devices:
|
|
93 if "VK_KHR_swapchain" in device.extensions:
|
|
94 for i, queueFamily in enumerate(device.queueFamilies):
|
|
95 let hasGraphics = bool(uint32(queueFamily.properties.queueFlags) and ord(VK_QUEUE_GRAPHICS_BIT))
|
|
96 if (
|
|
97 queueFamily.hasSurfaceSupport and
|
|
98 hasGraphics and
|
|
99 device.surfaceFormats.len > 0 and
|
|
100 device.presentModes.len > 0
|
|
101 ):
|
|
102 result.add((device, uint32(i)))
|
|
103
|
|
104 proc filterForSurfaceFormat(formats: seq[VkSurfaceFormatKHR]): seq[VkSurfaceFormatKHR] =
|
|
105 for format in formats:
|
|
106 if format.format == VK_FORMAT_B8G8R8A8_SRGB and format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR:
|
|
107 result.add(format)
|
|
108
|
|
109 proc getSwapExtent(display: PDisplay, window: Window, capabilities: VkSurfaceCapabilitiesKHR): VkExtent2D =
|
|
110 if capabilities.currentExtent.width != high(uint32):
|
|
111 return capabilities.currentExtent
|
|
112 else:
|
|
113 let (width, height) = xlibFramebufferSize(display, window)
|
|
114 return VkExtent2D(
|
|
115 width: min(max(uint32(width), capabilities.minImageExtent.width), capabilities.maxImageExtent.width),
|
|
116 height: min(max(uint32(height), capabilities.minImageExtent.height), capabilities.maxImageExtent.height),
|
|
117 )
|
|
118
|
|
119 proc igniteEngine*(): Engine =
|
|
120 vkLoad1_0()
|
|
121 vkLoad1_1()
|
|
122 vkLoad1_2()
|
|
123
|
|
124 # init X11 window
|
|
125 (result.display, result.window) = xlibInit()
|
|
126
|
|
127 # create vulkan instance
|
|
128 result.vulkan.instance = createVulkanInstance(VULKAN_VERSION)
|
|
129
|
|
130 # create vulkan-X11 surface
|
|
131 var surfaceCreateInfo = VkXlibSurfaceCreateInfoKHR(
|
|
132 sType: VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
|
|
133 dpy: result.display,
|
|
134 window: result.window,
|
|
135 )
|
|
136 checkVkResult vkCreateXlibSurfaceKHR(result.vulkan.instance, addr(surfaceCreateInfo), nil, addr(result.vulkan.surface))
|
|
137
|
|
138 # determine device and queue to use and instantiate
|
|
139 result.vulkan.deviceList = result.vulkan.instance.getAllPhysicalDevices(result.vulkan.surface)
|
|
140 let usableDevices = result.vulkan.deviceList.filterForDevice()
|
|
141 if len(usableDevices) == 0:
|
|
142 raise newException(Exception, "No suitable graphics device found")
|
|
143 (result.vulkan.activePhysicalDevice, result.vulkan.activeQueueFamily) = usableDevices[0]
|
|
144
|
|
145 (result.vulkan.device, result.vulkan.presentationQueue) = getVulcanDevice(
|
|
146 result.vulkan.activePhysicalDevice.device,
|
|
147 result.vulkan.activePhysicalDevice.features,
|
|
148 result.vulkan.activeQueueFamily
|
|
149 )
|
|
150
|
|
151 # determine surface format for swapchain
|
|
152 let usableSurfaceFormats = filterForSurfaceFormat(result.vulkan.activePhysicalDevice.surfaceFormats)
|
|
153 if len(usableSurfaceFormats) == 0:
|
|
154 raise newException(Exception, "No suitable surface formats found")
|
|
155 result.vulkan.selectedSurfaceFormat = usableSurfaceFormats[0]
|
|
156 result.vulkan.selectedPresentationMode = getPresentMode(result.vulkan.activePhysicalDevice.presentModes)
|
|
157 result.vulkan.selectedExtent = getSwapExtent(result.display, result.window, result.vulkan.activePhysicalDevice.surfaceCapabilities)
|
|
158
|
|
159 # setup swapchain
|
|
160 var swapchainCreateInfo = VkSwapchainCreateInfoKHR(
|
|
161 sType: VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
|
162 surface: result.vulkan.surface,
|
|
163 minImageCount: max(result.vulkan.activePhysicalDevice.surfaceCapabilities.minImageCount + 1, result.vulkan.activePhysicalDevice.surfaceCapabilities.maxImageCount),
|
|
164 imageFormat: result.vulkan.selectedSurfaceFormat.format,
|
|
165 imageColorSpace: result.vulkan.selectedSurfaceFormat.colorSpace,
|
|
166 imageExtent: result.vulkan.selectedExtent,
|
|
167 imageArrayLayers: 1,
|
|
168 imageUsage: VkImageUsageFlags(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT),
|
|
169 # VK_SHARING_MODE_CONCURRENT no supported (i.e cannot use different queue families for drawing to swap surface?)
|
|
170 imageSharingMode: VK_SHARING_MODE_EXCLUSIVE,
|
|
171 preTransform: result.vulkan.activePhysicalDevice.surfaceCapabilities.currentTransform,
|
|
172 compositeAlpha: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
|
173 presentMode: result.vulkan.selectedPresentationMode,
|
462
|
174 clipped: VK_TRUE,
|
461
|
175 oldSwapchain: VkSwapchainKHR(0),
|
|
176 )
|
|
177 checkVkResult vkCreateSwapchainKHR(result.vulkan.device, addr(swapchainCreateInfo), nil, addr(result.vulkan.swapChain))
|
|
178 result.vulkan.swapImages = getSwapChainImages(result.vulkan.device, result.vulkan.swapChain)
|
|
179
|
|
180 # setup swapchian image views
|
|
181 result.vulkan.swapImageViews = newSeq[VkImageView](result.vulkan.swapImages.len)
|
|
182 for i, image in enumerate(result.vulkan.swapImages):
|
|
183 var imageViewCreateInfo = VkImageViewCreateInfo(
|
|
184 sType: VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
|
185 image: image,
|
|
186 viewType: VK_IMAGE_VIEW_TYPE_2D,
|
|
187 format: result.vulkan.selectedSurfaceFormat.format,
|
|
188 components: VkComponentMapping(
|
|
189 r: VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
190 g: VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
191 b: VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
192 a: VK_COMPONENT_SWIZZLE_IDENTITY,
|
|
193 ),
|
|
194 subresourceRange: VkImageSubresourceRange(
|
|
195 aspectMask: VkImageAspectFlags(VK_IMAGE_ASPECT_COLOR_BIT),
|
|
196 baseMipLevel: 0,
|
|
197 levelCount: 1,
|
|
198 baseArrayLayer: 0,
|
|
199 layerCount: 1,
|
|
200 ),
|
|
201 )
|
|
202 checkVkResult vkCreateImageView(result.vulkan.device, addr(imageViewCreateInfo), nil, addr(result.vulkan.swapImageViews[i]))
|
462
|
203
|
|
204 # init shader system
|
|
205 checkGlslangResult glslang_initialize_process()
|
|
206
|
|
207 # load shaders
|
|
208 result.pipeline.shaderStages.add(createShaderStage(result.vulkan.device, VK_SHADER_STAGE_VERTEX_BIT, vertexShaderCode))
|
|
209 result.pipeline.shaderStages.add(createShaderStage(result.vulkan.device, VK_SHADER_STAGE_FRAGMENT_BIT, fragmentShaderCode))
|
|
210
|
|
211 # create graphis pipeline
|
|
212 var dynamicStates = [VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR]
|
|
213 var dynamicState = VkPipelineDynamicStateCreateInfo(
|
|
214 sType: VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
|
215 dynamicStateCount: uint32(dynamicStates.len),
|
|
216 pDynamicStates: addr(dynamicStates[0]),
|
|
217 )
|
|
218 var vertexInputInfo = VkPipelineVertexInputStateCreateInfo(
|
|
219 sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
|
220 vertexBindingDescriptionCount: 0,
|
|
221 pVertexBindingDescriptions: nil,
|
|
222 vertexAttributeDescriptionCount: 0,
|
|
223 pVertexAttributeDescriptions: nil,
|
|
224 )
|
|
225 var inputAssembly = VkPipelineInputAssemblyStateCreateInfo(
|
|
226 sType: VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
|
227 topology: VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
|
228 primitiveRestartEnable: VK_FALSE,
|
|
229 )
|
|
230
|
|
231 # setup viewport
|
|
232 var viewport = VkViewport(
|
|
233 x: 0.0,
|
|
234 y: 0.0,
|
|
235 width: (float) result.vulkan.selectedExtent.width,
|
|
236 height: (float) result.vulkan.selectedExtent.height,
|
|
237 minDepth: 0.0,
|
|
238 maxDepth: 1.0,
|
|
239 )
|
|
240 var scissor = VkRect2D(
|
|
241 offset: VkOffset2D(x: 0, y: 0),
|
|
242 extent: result.vulkan.selectedExtent
|
|
243 )
|
461
|
244
|
|
245
|
|
246 proc fullThrottle*(engine: Engine) =
|
|
247 var event: XEvent
|
|
248 while true:
|
|
249 discard XNextEvent(engine.display, addr(event))
|
|
250 case event.theType
|
|
251 of Expose:
|
|
252 discard
|
|
253 of ClientMessage:
|
|
254 if cast[Atom](event.xclient.data.l[0]) == deleteMessage:
|
|
255 break
|
|
256 of KeyPress:
|
|
257 let key = XLookupKeysym(cast[PXKeyEvent](addr(event)), 0)
|
|
258 if key != 0:
|
|
259 echo "Key ", key, " pressed"
|
|
260 of ButtonPressMask:
|
|
261 echo "Mouse button ", event.xbutton.button, " pressed at ",
|
|
262 event.xbutton.x, ",", event.xbutton.y
|
|
263 else:
|
|
264 discard
|
|
265
|
|
266 proc trash*(engine: Engine) =
|
462
|
267 for shaderStage in engine.pipeline.shaderStages:
|
|
268 vkDestroyShaderModule(engine.vulkan.device, shaderStage.module, nil);
|
|
269 glslang_finalize_process()
|
461
|
270 vkDestroySwapchainKHR(engine.vulkan.device, engine.vulkan.swapChain, nil);
|
|
271 vkDestroySurfaceKHR(engine.vulkan.instance, engine.vulkan.surface, nil);
|
|
272 vkDestroyDevice(engine.vulkan.device, nil)
|
|
273 vkDestroyInstance(engine.vulkan.instance, nil)
|
|
274 checkXlibResult engine.display.XDestroyWindow(engine.window)
|
|
275 discard engine.display.XCloseDisplay() # always returns 0
|