├── .gitignore ├── 00_base_code.py ├── 01_instance_creation.py ├── 02_validation_layers.py ├── 03_physical_device_selection.py ├── 04_logical_device.py ├── 05_window_surface.py ├── 06_swap_chain_creation.py ├── 07_image_views.py ├── 08_graphics_pipeline.py ├── 09_shader_base.frag ├── 09_shader_base.vert ├── 09_shader_modules.py ├── 10_fixed_functions.c.py ├── 11_render_passes.py ├── 12_graphics_pipeline_complete.py ├── 13_framebuffers.py ├── 14_command_buffers.py ├── 15_hello_triangle.py ├── 16_swap_chain_recreation.py ├── 17_shader_vertexbuffer.frag ├── 17_shader_vertexbuffer.vert ├── 17_vertex_input.py ├── 18_vertex_buffer.py ├── 19_staging_buffer.py ├── 20_index_buffer.py ├── 21_descriptor_layout.py ├── 21_shader_ubo.frag ├── 21_shader_ubo.vert ├── 22_descriptor_set.py ├── 23_texture_image.py ├── 24_sampler.py ├── 25_shader_textures.frag ├── 25_shader_textures.vert ├── 25_texture_mapping.py ├── 26_depth_buffering.py ├── 26_shader_depth.frag ├── 26_shader_depth.vert ├── 27_model_loading.py ├── 27_model_loading_2.py ├── 28_mipmapping.py ├── LICENSE ├── README.md ├── glm.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask stuff: 57 | instance/ 58 | .webassets-cache 59 | 60 | # Scrapy stuff: 61 | .scrapy 62 | 63 | # Sphinx documentation 64 | docs/_build/ 65 | 66 | # PyBuilder 67 | target/ 68 | 69 | # IPython Notebook 70 | .ipynb_checkpoints 71 | 72 | # pyenv 73 | .python-version 74 | 75 | # celery beat schedule file 76 | celerybeat-schedule 77 | 78 | # dotenv 79 | .env 80 | 81 | # virtualenv 82 | venv/ 83 | ENV/ 84 | 85 | # Spyder project settings 86 | .spyderproject 87 | 88 | # Rope project settings 89 | .ropeproject 90 | 91 | # ide 92 | .idea/ 93 | 94 | 95 | shader/ 96 | textures/ 97 | models/ -------------------------------------------------------------------------------- /00_base_code.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | from PySide2 import (QtGui, QtCore) 4 | 5 | 6 | class HelloTriangleApplication(QtGui.QWindow): 7 | 8 | def __init__(self): 9 | super(HelloTriangleApplication, self).__init__() 10 | 11 | self.setWidth(1280) 12 | self.setHeight(720) 13 | 14 | self.setTitle("Vulkan Python - PySide2") 15 | 16 | #self.setSurfaceType(self.OpenGLSurface) 17 | 18 | def __del__(self): 19 | print('closed') 20 | pass 21 | 22 | 23 | if __name__ == '__main__': 24 | import sys 25 | 26 | app = QtGui.QGuiApplication(sys.argv) 27 | 28 | win = HelloTriangleApplication() 29 | win.show() 30 | 31 | # def clenaup(): 32 | # global win 33 | # del win 34 | # 35 | # app.aboutToQuit.connect(clenaup) 36 | 37 | sys.exit(app.exec_()) 38 | -------------------------------------------------------------------------------- /01_instance_creation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | from vulkan import * 4 | from PySide2 import (QtGui, QtCore) 5 | 6 | 7 | class HelloTriangleApplication(QtGui.QWindow): 8 | 9 | def __init__(self): 10 | super(HelloTriangleApplication, self).__init__() 11 | 12 | self.setWidth(1280) 13 | self.setHeight(720) 14 | 15 | self.setTitle("Vulkan Python - PySide2") 16 | 17 | #self.setSurfaceType(self.OpenGLSurface) 18 | 19 | self.__instance = None 20 | 21 | self.initVulkan() 22 | 23 | def __del__(self): 24 | if self.__instance: 25 | vkDestroyInstance(self.__instance, None) 26 | print('instance destroyed') 27 | 28 | def initVulkan(self): 29 | self.__cretaeInstance() 30 | 31 | def __cretaeInstance(self): 32 | appInfo = VkApplicationInfo( 33 | # sType=VK_STRUCTURE_TYPE_APPLICATION_INFO, 34 | pApplicationName='Python VK', 35 | applicationVersion=VK_MAKE_VERSION(1, 0, 0), 36 | pEngineName='pyvulkan', 37 | engineVersion=VK_MAKE_VERSION(1, 0, 0), 38 | apiVersion=VK_API_VERSION 39 | ) 40 | 41 | extenstions = [e.extensionName for e in vkEnumerateInstanceExtensionProperties(None)] 42 | instanceInfo = VkInstanceCreateInfo( 43 | pApplicationInfo=appInfo, 44 | enabledLayerCount=0, 45 | enabledExtensionCount=len(extenstions), 46 | ppEnabledExtensionNames=extenstions 47 | ) 48 | 49 | self.__instance = vkCreateInstance(instanceInfo, None) 50 | 51 | 52 | if __name__ == '__main__': 53 | import sys 54 | 55 | app = QtGui.QGuiApplication(sys.argv) 56 | 57 | win = HelloTriangleApplication() 58 | win.show() 59 | 60 | def clenaup(): 61 | global win 62 | del win 63 | 64 | app.aboutToQuit.connect(clenaup) 65 | 66 | sys.exit(app.exec_()) 67 | -------------------------------------------------------------------------------- /02_validation_layers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | from vulkan import * 4 | from PySide2 import (QtGui, QtCore) 5 | 6 | 7 | validationLayers = [ 8 | 'VK_LAYER_LUNARG_standard_validation' 9 | ] 10 | 11 | enableValidationLayers = True 12 | 13 | 14 | class InstanceProcAddr(object): 15 | 16 | def __init__(self, func): 17 | self.__func = func 18 | 19 | def __call__(self, *args, **kwargs): 20 | funcName = self.__func.__name__ 21 | func = vkGetInstanceProcAddr(args[0], funcName) 22 | if func: 23 | return func(*args, **kwargs) 24 | else: 25 | return VK_ERROR_EXTENSION_NOT_PRESENT 26 | 27 | @InstanceProcAddr 28 | def vkCreateDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 29 | pass 30 | 31 | @InstanceProcAddr 32 | def vkDestroyDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 33 | pass 34 | 35 | 36 | def debugCallback(*args): 37 | print('DEBUG: {} {}'.format(args[5], args[6])) 38 | return 0 39 | 40 | 41 | class HelloTriangleApplication(QtGui.QWindow): 42 | 43 | def __init__(self): 44 | super(HelloTriangleApplication, self).__init__() 45 | 46 | self.setWidth(1280) 47 | self.setHeight(720) 48 | 49 | self.setTitle("Vulkan Python - PySide2") 50 | 51 | #self.setSurfaceType(self.OpenGLSurface) 52 | 53 | self.__instance = None 54 | self.__callbcak = None 55 | 56 | self.initVulkan() 57 | 58 | def __del__(self): 59 | if self.__callbcak: 60 | vkDestroyDebugReportCallbackEXT(self.__instance, self.__callbcak, None) 61 | 62 | if self.__instance: 63 | vkDestroyInstance(self.__instance, None) 64 | print('instance destroyed') 65 | 66 | def initVulkan(self): 67 | self.__cretaeInstance() 68 | self.__setupDebugCallback() 69 | 70 | def __cretaeInstance(self): 71 | appInfo = VkApplicationInfo( 72 | # sType=VK_STRUCTURE_TYPE_APPLICATION_INFO, 73 | pApplicationName='Python VK', 74 | applicationVersion=VK_MAKE_VERSION(1, 0, 0), 75 | pEngineName='pyvulkan', 76 | engineVersion=VK_MAKE_VERSION(1, 0, 0), 77 | apiVersion=VK_API_VERSION 78 | ) 79 | 80 | extenstions = [e.extensionName for e in vkEnumerateInstanceExtensionProperties(None)] 81 | if enableValidationLayers: 82 | instanceInfo = VkInstanceCreateInfo( 83 | pApplicationInfo=appInfo, 84 | enabledLayerCount=len(validationLayers), 85 | ppEnabledLayerNames=validationLayers, 86 | enabledExtensionCount=len(extenstions), 87 | ppEnabledExtensionNames=extenstions 88 | ) 89 | else: 90 | instanceInfo = VkInstanceCreateInfo( 91 | pApplicationInfo=appInfo, 92 | enabledLayerCount=0, 93 | enabledExtensionCount=len(extenstions), 94 | ppEnabledExtensionNames=extenstions 95 | ) 96 | 97 | self.__instance = vkCreateInstance(instanceInfo, None) 98 | 99 | def __setupDebugCallback(self): 100 | if not enableValidationLayers: 101 | return 102 | 103 | createInfo = VkDebugReportCallbackCreateInfoEXT( 104 | flags=VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT, 105 | pfnCallback=debugCallback 106 | ) 107 | 108 | self.__callbcak = vkCreateDebugReportCallbackEXT(self.__instance, createInfo, None) 109 | 110 | 111 | if __name__ == '__main__': 112 | import sys 113 | 114 | app = QtGui.QGuiApplication(sys.argv) 115 | 116 | win = HelloTriangleApplication() 117 | win.show() 118 | 119 | def clenaup(): 120 | global win 121 | del win 122 | 123 | app.aboutToQuit.connect(clenaup) 124 | 125 | sys.exit(app.exec_()) -------------------------------------------------------------------------------- /03_physical_device_selection.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | from vulkan import * 4 | from PySide2 import (QtGui, QtCore) 5 | 6 | 7 | validationLayers = [ 8 | 'VK_LAYER_LUNARG_standard_validation' 9 | ] 10 | 11 | enableValidationLayers = True 12 | 13 | 14 | class InstanceProcAddr(object): 15 | 16 | def __init__(self, func): 17 | self.__func = func 18 | 19 | def __call__(self, *args, **kwargs): 20 | funcName = self.__func.__name__ 21 | func = vkGetInstanceProcAddr(args[0], funcName) 22 | if func: 23 | return func(*args, **kwargs) 24 | else: 25 | return VK_ERROR_EXTENSION_NOT_PRESENT 26 | 27 | @InstanceProcAddr 28 | def vkCreateDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 29 | pass 30 | 31 | @InstanceProcAddr 32 | def vkDestroyDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 33 | pass 34 | 35 | 36 | def debugCallback(*args): 37 | print('DEBUG: {} {}'.format(args[5], args[6])) 38 | return 0 39 | 40 | 41 | class QueueFamilyIndices(object): 42 | 43 | def __init__(self): 44 | self.graphicsFamily = -1 45 | 46 | @property 47 | def isComplete(self): 48 | return self.graphicsFamily >= 0 49 | 50 | 51 | class HelloTriangleApplication(QtGui.QWindow): 52 | 53 | def __init__(self): 54 | super(HelloTriangleApplication, self).__init__() 55 | 56 | self.setWidth(1280) 57 | self.setHeight(720) 58 | 59 | self.setTitle("Vulkan Python - PySide2") 60 | 61 | #self.setSurfaceType(self.OpenGLSurface) 62 | 63 | self.__instance = None 64 | self.__callbcak = None 65 | self.__physicalDevice = None 66 | 67 | self.initVulkan() 68 | 69 | def __del__(self): 70 | if self.__callbcak: 71 | vkDestroyDebugReportCallbackEXT(self.__instance, self.__callbcak, None) 72 | 73 | if self.__instance: 74 | vkDestroyInstance(self.__instance, None) 75 | print('instance destroyed') 76 | 77 | def initVulkan(self): 78 | self.__cretaeInstance() 79 | self.__setupDebugCallback() 80 | self.__pickPhysicalDevice() 81 | 82 | def __cretaeInstance(self): 83 | appInfo = VkApplicationInfo( 84 | # sType=VK_STRUCTURE_TYPE_APPLICATION_INFO, 85 | pApplicationName='Python VK', 86 | applicationVersion=VK_MAKE_VERSION(1, 0, 0), 87 | pEngineName='pyvulkan', 88 | engineVersion=VK_MAKE_VERSION(1, 0, 0), 89 | apiVersion=VK_API_VERSION 90 | ) 91 | 92 | extenstions = [e.extensionName for e in vkEnumerateInstanceExtensionProperties(None)] 93 | if enableValidationLayers: 94 | instanceInfo = VkInstanceCreateInfo( 95 | pApplicationInfo=appInfo, 96 | enabledLayerCount=len(validationLayers), 97 | ppEnabledLayerNames=validationLayers, 98 | enabledExtensionCount=len(extenstions), 99 | ppEnabledExtensionNames=extenstions 100 | ) 101 | else: 102 | instanceInfo = VkInstanceCreateInfo( 103 | pApplicationInfo=appInfo, 104 | enabledLayerCount=0, 105 | enabledExtensionCount=len(extenstions), 106 | ppEnabledExtensionNames=extenstions 107 | ) 108 | 109 | self.__instance = vkCreateInstance(instanceInfo, None) 110 | 111 | def __setupDebugCallback(self): 112 | if not enableValidationLayers: 113 | return 114 | 115 | createInfo = VkDebugReportCallbackCreateInfoEXT( 116 | flags=VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT, 117 | pfnCallback=debugCallback 118 | ) 119 | 120 | self.__callbcak = vkCreateDebugReportCallbackEXT(self.__instance, createInfo, None) 121 | 122 | def __pickPhysicalDevice(self): 123 | physicalDevices = vkEnumeratePhysicalDevices(self.__instance) 124 | 125 | for device in physicalDevices: 126 | if self.__isDeviceSuitable(device): 127 | self.__physicalDevice = device 128 | break 129 | 130 | assert self.__physicalDevice != None 131 | 132 | def __isDeviceSuitable(self, device): 133 | indices = self.__findQueueFamilies(device) 134 | 135 | return indices.isComplete 136 | 137 | def __findQueueFamilies(self, device): 138 | indices = QueueFamilyIndices() 139 | 140 | familyProperties = vkGetPhysicalDeviceQueueFamilyProperties(device) 141 | for i, prop in enumerate(familyProperties): 142 | if prop.queueCount > 0 and prop.queueFlags & VK_QUEUE_GRAPHICS_BIT: 143 | indices.graphicsFamily = i 144 | 145 | if indices.isComplete: 146 | break 147 | 148 | return indices 149 | 150 | 151 | 152 | if __name__ == '__main__': 153 | import sys 154 | 155 | app = QtGui.QGuiApplication(sys.argv) 156 | 157 | win = HelloTriangleApplication() 158 | win.show() 159 | 160 | def clenaup(): 161 | global win 162 | del win 163 | 164 | app.aboutToQuit.connect(clenaup) 165 | 166 | sys.exit(app.exec_()) 167 | -------------------------------------------------------------------------------- /04_logical_device.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | from vulkan import * 4 | from PySide2 import (QtGui, QtCore) 5 | 6 | 7 | validationLayers = [ 8 | 'VK_LAYER_LUNARG_standard_validation' 9 | ] 10 | 11 | enableValidationLayers = True 12 | 13 | 14 | class InstanceProcAddr(object): 15 | 16 | def __init__(self, func): 17 | self.__func = func 18 | 19 | def __call__(self, *args, **kwargs): 20 | funcName = self.__func.__name__ 21 | func = vkGetInstanceProcAddr(args[0], funcName) 22 | if func: 23 | return func(*args, **kwargs) 24 | else: 25 | return VK_ERROR_EXTENSION_NOT_PRESENT 26 | 27 | @InstanceProcAddr 28 | def vkCreateDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 29 | pass 30 | 31 | @InstanceProcAddr 32 | def vkDestroyDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 33 | pass 34 | 35 | 36 | def debugCallback(*args): 37 | print('DEBUG: {} {}'.format(args[5], args[6])) 38 | return 0 39 | 40 | 41 | class QueueFamilyIndices(object): 42 | 43 | def __init__(self): 44 | self.graphicsFamily = -1 45 | 46 | @property 47 | def isComplete(self): 48 | return self.graphicsFamily >= 0 49 | 50 | 51 | class HelloTriangleApplication(QtGui.QWindow): 52 | 53 | def __init__(self): 54 | super(HelloTriangleApplication, self).__init__() 55 | 56 | self.setWidth(1280) 57 | self.setHeight(720) 58 | 59 | self.setTitle("Vulkan Python - PySide2") 60 | 61 | #self.setSurfaceType(self.OpenGLSurface) 62 | 63 | self.__instance = None 64 | self.__callbcak = None 65 | self.__physicalDevice = None 66 | self.__device = None 67 | self.__graphicQueue = None 68 | 69 | self.initVulkan() 70 | 71 | def __del__(self): 72 | if self.__device: 73 | vkDestroyDevice(self.__device, None) 74 | 75 | if self.__callbcak: 76 | vkDestroyDebugReportCallbackEXT(self.__instance, self.__callbcak, None) 77 | 78 | if self.__instance: 79 | vkDestroyInstance(self.__instance, None) 80 | print('instance destroyed') 81 | 82 | def initVulkan(self): 83 | self.__cretaeInstance() 84 | self.__setupDebugCallback() 85 | self.__pickPhysicalDevice() 86 | self.__createLogicalDevice() 87 | 88 | def __cretaeInstance(self): 89 | if enableValidationLayers and not self.__checkValidationLayerSupport(): 90 | raise Exception("validation layers requested, but not available!") 91 | 92 | appInfo = VkApplicationInfo( 93 | # sType=VK_STRUCTURE_TYPE_APPLICATION_INFO, 94 | pApplicationName='Python VK', 95 | applicationVersion=VK_MAKE_VERSION(1, 0, 0), 96 | pEngineName='pyvulkan', 97 | engineVersion=VK_MAKE_VERSION(1, 0, 0), 98 | apiVersion=VK_API_VERSION 99 | ) 100 | 101 | extenstions = self.__getRequiredExtensions() 102 | if enableValidationLayers: 103 | instanceInfo = VkInstanceCreateInfo( 104 | pApplicationInfo=appInfo, 105 | enabledLayerCount=len(validationLayers), 106 | ppEnabledLayerNames=validationLayers, 107 | enabledExtensionCount=len(extenstions), 108 | ppEnabledExtensionNames=extenstions 109 | ) 110 | else: 111 | instanceInfo = VkInstanceCreateInfo( 112 | pApplicationInfo=appInfo, 113 | enabledLayerCount=0, 114 | enabledExtensionCount=len(extenstions), 115 | ppEnabledExtensionNames=extenstions 116 | ) 117 | 118 | self.__instance = vkCreateInstance(instanceInfo, None) 119 | 120 | def __setupDebugCallback(self): 121 | if not enableValidationLayers: 122 | return 123 | 124 | createInfo = VkDebugReportCallbackCreateInfoEXT( 125 | flags=VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT, 126 | pfnCallback=debugCallback 127 | ) 128 | 129 | self.__callbcak = vkCreateDebugReportCallbackEXT(self.__instance, createInfo, None) 130 | 131 | def __pickPhysicalDevice(self): 132 | physicalDevices = vkEnumeratePhysicalDevices(self.__instance) 133 | 134 | for device in physicalDevices: 135 | if self.__isDeviceSuitable(device): 136 | self.__physicalDevice = device 137 | break 138 | 139 | assert self.__physicalDevice != None 140 | 141 | def __createLogicalDevice(self): 142 | indices = self.__findQueueFamilies(self.__physicalDevice) 143 | queueCreateInfo = VkDeviceQueueCreateInfo( 144 | queueFamilyIndex=indices.graphicsFamily, 145 | queueCount=1, 146 | pQueuePriorities=[1.0] 147 | ) 148 | 149 | deviceFeatures = VkPhysicalDeviceFeatures() 150 | if enableValidationLayers: 151 | createInfo = VkDeviceCreateInfo( 152 | queueCreateInfoCount=1, 153 | pQueueCreateInfos=queueCreateInfo, 154 | enabledExtensionCount=0, 155 | enabledLayerCount=len(validationLayers), 156 | ppEnabledLayerNames=validationLayers, 157 | pEnabledFeatures=deviceFeatures 158 | ) 159 | else: 160 | createInfo = VkDeviceCreateInfo( 161 | queueCreateInfoCount=1, 162 | pQueueCreateInfos=queueCreateInfo, 163 | enabledExtensionCount=0, 164 | enabledLayerCount=0, 165 | pEnabledFeatures=deviceFeatures 166 | ) 167 | 168 | self.__device = vkCreateDevice(self.__physicalDevice, createInfo, None) 169 | 170 | def __isDeviceSuitable(self, device): 171 | indices = self.__findQueueFamilies(device) 172 | 173 | return indices.isComplete 174 | 175 | def __findQueueFamilies(self, device): 176 | indices = QueueFamilyIndices() 177 | 178 | familyProperties = vkGetPhysicalDeviceQueueFamilyProperties(device) 179 | for i, prop in enumerate(familyProperties): 180 | if prop.queueCount > 0 and prop.queueFlags & VK_QUEUE_GRAPHICS_BIT: 181 | indices.graphicsFamily = i 182 | 183 | if indices.isComplete: 184 | break 185 | 186 | return indices 187 | 188 | def __getRequiredExtensions(self): 189 | extenstions = [e.extensionName for e in vkEnumerateInstanceExtensionProperties(None)] 190 | 191 | if enableValidationLayers: 192 | extenstions.append(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) 193 | 194 | return extenstions 195 | 196 | def __checkValidationLayerSupport(self): 197 | availableLayers = vkEnumerateInstanceLayerProperties() 198 | 199 | for layer in validationLayers: 200 | layerfound = False 201 | 202 | for layerProp in availableLayers: 203 | if layer == layerProp.layerName: 204 | layerfound = True 205 | break 206 | return layerfound 207 | 208 | return False 209 | 210 | if __name__ == '__main__': 211 | import sys 212 | 213 | app = QtGui.QGuiApplication(sys.argv) 214 | 215 | win = HelloTriangleApplication() 216 | win.show() 217 | 218 | def clenaup(): 219 | global win 220 | del win 221 | 222 | app.aboutToQuit.connect(clenaup) 223 | 224 | sys.exit(app.exec_()) 225 | -------------------------------------------------------------------------------- /05_window_surface.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | import sys 4 | 5 | from vulkan import * 6 | from PySide2 import (QtGui, QtCore) 7 | 8 | 9 | validationLayers = [ 10 | 'VK_LAYER_LUNARG_standard_validation' 11 | ] 12 | 13 | enableValidationLayers = True 14 | 15 | 16 | class InstanceProcAddr(object): 17 | 18 | T = None 19 | 20 | def __init__(self, func): 21 | self.__func = func 22 | 23 | def __call__(self, *args, **kwargs): 24 | funcName = self.__func.__name__ 25 | func = InstanceProcAddr.procfunc(funcName) 26 | if func: 27 | return func(*args, **kwargs) 28 | else: 29 | return VK_ERROR_EXTENSION_NOT_PRESENT 30 | 31 | @staticmethod 32 | def procfunc(funcName): 33 | return vkGetInstanceProcAddr(InstanceProcAddr.T, funcName) 34 | 35 | 36 | @InstanceProcAddr 37 | def vkCreateDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 38 | pass 39 | 40 | @InstanceProcAddr 41 | def vkDestroyDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 42 | pass 43 | 44 | @InstanceProcAddr 45 | def vkCreateWin32SurfaceKHR(instance, pCreateInfo, pAllocator): 46 | pass 47 | 48 | @InstanceProcAddr 49 | def vkDestroySurfaceKHR(instance, surface, pAllocator): 50 | pass 51 | 52 | @InstanceProcAddr 53 | def vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface): 54 | pass 55 | 56 | # device ext functions 57 | 58 | def debugCallback(*args): 59 | print('DEBUG: {} {}'.format(args[5], args[6])) 60 | return 0 61 | 62 | 63 | class Win32misc(object): 64 | @staticmethod 65 | def getInstance(hWnd): 66 | from cffi import FFI as _FFI 67 | _ffi = _FFI() 68 | _ffi.cdef('long __stdcall GetWindowLongA(void* hWnd, int nIndex);') 69 | _lib = _ffi.dlopen('User32.dll') 70 | return _lib.GetWindowLongA(_ffi.cast('void*', hWnd), -6) # GWL_HINSTANCE 71 | 72 | 73 | class QueueFamilyIndices(object): 74 | 75 | def __init__(self): 76 | self.graphicsFamily = -1 77 | self.presentFamily = -1 78 | 79 | @property 80 | def isComplete(self): 81 | return self.graphicsFamily >= 0 and self.presentFamily >= 0 82 | 83 | 84 | class HelloTriangleApplication(QtGui.QWindow): 85 | 86 | def __init__(self): 87 | super(HelloTriangleApplication, self).__init__() 88 | 89 | self.setWidth(1280) 90 | self.setHeight(720) 91 | 92 | self.setTitle("Vulkan Python - PySide2") 93 | 94 | #self.setSurfaceType(self.OpenGLSurface) 95 | 96 | self.__instance = None 97 | self.__callbcak = None 98 | self.__surface = None 99 | 100 | self.__physicalDevice = None 101 | self.__device = None 102 | self.__graphicQueue = None 103 | self.__presentQueue = None 104 | 105 | self.__indices = QueueFamilyIndices() 106 | 107 | self.initVulkan() 108 | 109 | def __del__(self): 110 | if self.__surface: 111 | vkDestroySurfaceKHR(self.__instance, self.__surface, None) 112 | 113 | if self.__device: 114 | vkDestroyDevice(self.__device, None) 115 | 116 | if self.__callbcak: 117 | vkDestroyDebugReportCallbackEXT(self.__instance, self.__callbcak, None) 118 | 119 | if self.__instance: 120 | vkDestroyInstance(self.__instance, None) 121 | print('instance destroyed') 122 | 123 | def initVulkan(self): 124 | self.__cretaeInstance() 125 | self.__setupDebugCallback() 126 | self.__createSurface() 127 | self.__pickPhysicalDevice() 128 | self.__createLogicalDevice() 129 | 130 | def __cretaeInstance(self): 131 | if enableValidationLayers and not self.__checkValidationLayerSupport(): 132 | raise Exception("validation layers requested, but not available!") 133 | 134 | appInfo = VkApplicationInfo( 135 | # sType=VK_STRUCTURE_TYPE_APPLICATION_INFO, 136 | pApplicationName='Python VK', 137 | applicationVersion=VK_MAKE_VERSION(1, 0, 0), 138 | pEngineName='pyvulkan', 139 | engineVersion=VK_MAKE_VERSION(1, 0, 0), 140 | apiVersion=VK_API_VERSION 141 | ) 142 | 143 | extenstions = self.__getRequiredExtensions() 144 | if enableValidationLayers: 145 | instanceInfo = VkInstanceCreateInfo( 146 | pApplicationInfo=appInfo, 147 | enabledLayerCount=len(validationLayers), 148 | ppEnabledLayerNames=validationLayers, 149 | enabledExtensionCount=len(extenstions), 150 | ppEnabledExtensionNames=extenstions 151 | ) 152 | else: 153 | instanceInfo = VkInstanceCreateInfo( 154 | pApplicationInfo=appInfo, 155 | enabledLayerCount=0, 156 | enabledExtensionCount=len(extenstions), 157 | ppEnabledExtensionNames=extenstions 158 | ) 159 | 160 | self.__instance = vkCreateInstance(instanceInfo, None) 161 | 162 | InstanceProcAddr.T = self.__instance 163 | 164 | def __setupDebugCallback(self): 165 | if not enableValidationLayers: 166 | return 167 | 168 | createInfo = VkDebugReportCallbackCreateInfoEXT( 169 | flags=VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT, 170 | pfnCallback=debugCallback 171 | ) 172 | 173 | self.__callbcak = vkCreateDebugReportCallbackEXT(self.__instance, createInfo, None) 174 | 175 | def __createSurface(self): 176 | if sys.platform == 'win32': 177 | hwnd = self.winId() 178 | hinstance = Win32misc.getInstance(hwnd) 179 | createInfo = VkWin32SurfaceCreateInfoKHR( 180 | hinstance=hinstance, 181 | hwnd=hwnd 182 | ) 183 | 184 | self.__surface = vkCreateWin32SurfaceKHR(self.__instance, createInfo, None) 185 | # elif sys.platform == 'linux': 186 | # pass 187 | 188 | def __pickPhysicalDevice(self): 189 | physicalDevices = vkEnumeratePhysicalDevices(self.__instance) 190 | 191 | for device in physicalDevices: 192 | if self.__isDeviceSuitable(device): 193 | self.__physicalDevice = device 194 | break 195 | 196 | assert self.__physicalDevice != None 197 | 198 | def __createLogicalDevice(self): 199 | self.__indices = self.__findQueueFamilies(self.__physicalDevice) 200 | 201 | uniqueQueueFamilies = {}.fromkeys([self.__indices.graphicsFamily, self.__indices.presentFamily]) 202 | queueCreateInfos = [] 203 | for i in uniqueQueueFamilies: 204 | queueCreateInfo = VkDeviceQueueCreateInfo( 205 | queueFamilyIndex=i, 206 | queueCount=1, 207 | pQueuePriorities=[1.0] 208 | ) 209 | queueCreateInfos.append(queueCreateInfo) 210 | 211 | deviceFeatures = VkPhysicalDeviceFeatures() 212 | if enableValidationLayers: 213 | createInfo = VkDeviceCreateInfo( 214 | queueCreateInfoCount=len(queueCreateInfos), 215 | pQueueCreateInfos=queueCreateInfos, 216 | enabledExtensionCount=0, 217 | enabledLayerCount=len(validationLayers), 218 | ppEnabledLayerNames=validationLayers, 219 | pEnabledFeatures=deviceFeatures 220 | ) 221 | else: 222 | createInfo = VkDeviceCreateInfo( 223 | queueCreateInfoCount=1, 224 | pQueueCreateInfos=queueCreateInfo, 225 | enabledExtensionCount=0, 226 | enabledLayerCount=0, 227 | pEnabledFeatures=deviceFeatures 228 | ) 229 | 230 | self.__device = vkCreateDevice(self.__physicalDevice, createInfo, None) 231 | 232 | self.__graphicQueue = vkGetDeviceQueue(self.__device, self.__indices.graphicsFamily, 0) 233 | self.__presentQueue = vkGetDeviceQueue(self.__device, self.__indices.presentFamily, 0) 234 | 235 | def __isDeviceSuitable(self, device): 236 | indices = self.__findQueueFamilies(device) 237 | 238 | return indices.isComplete 239 | 240 | def __findQueueFamilies(self, device): 241 | indices = QueueFamilyIndices() 242 | 243 | familyProperties = vkGetPhysicalDeviceQueueFamilyProperties(device) 244 | for i, prop in enumerate(familyProperties): 245 | if prop.queueCount > 0 and prop.queueFlags & VK_QUEUE_GRAPHICS_BIT: 246 | indices.graphicsFamily = i 247 | 248 | presentSupport = vkGetPhysicalDeviceSurfaceSupportKHR(device, i, self.__surface) 249 | 250 | if prop.queueCount > 0 and presentSupport: 251 | indices.presentFamily = i 252 | 253 | if indices.isComplete: 254 | break 255 | 256 | return indices 257 | 258 | def __getRequiredExtensions(self): 259 | extenstions = [e.extensionName for e in vkEnumerateInstanceExtensionProperties(None)] 260 | 261 | if enableValidationLayers: 262 | extenstions.append(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) 263 | 264 | return extenstions 265 | 266 | def __checkValidationLayerSupport(self): 267 | availableLayers = vkEnumerateInstanceLayerProperties() 268 | 269 | for layer in validationLayers: 270 | layerfound = False 271 | 272 | for layerProp in availableLayers: 273 | if layer == layerProp.layerName: 274 | layerfound = True 275 | break 276 | return layerfound 277 | 278 | return False 279 | 280 | if __name__ == '__main__': 281 | import sys 282 | 283 | app = QtGui.QGuiApplication(sys.argv) 284 | 285 | win = HelloTriangleApplication() 286 | win.show() 287 | 288 | def clenaup(): 289 | global win 290 | del win 291 | 292 | app.aboutToQuit.connect(clenaup) 293 | 294 | sys.exit(app.exec_()) 295 | -------------------------------------------------------------------------------- /06_swap_chain_creation.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | import sys 4 | 5 | from vulkan import * 6 | from PySide2 import (QtGui, QtCore) 7 | 8 | validationLayers = [ 9 | 'VK_LAYER_LUNARG_standard_validation' 10 | ] 11 | 12 | deviceExtensions = [ 13 | VK_KHR_SWAPCHAIN_EXTENSION_NAME 14 | ] 15 | 16 | enableValidationLayers = True 17 | 18 | 19 | class InstanceProcAddr(object): 20 | T = None 21 | 22 | def __init__(self, func): 23 | self.__func = func 24 | 25 | def __call__(self, *args, **kwargs): 26 | funcName = self.__func.__name__ 27 | func = InstanceProcAddr.procfunc(funcName) 28 | if func: 29 | return func(*args, **kwargs) 30 | else: 31 | return VK_ERROR_EXTENSION_NOT_PRESENT 32 | 33 | @staticmethod 34 | def procfunc(funcName): 35 | return vkGetInstanceProcAddr(InstanceProcAddr.T, funcName) 36 | 37 | 38 | class DeviceProcAddr(InstanceProcAddr): 39 | 40 | @staticmethod 41 | def procfunc(funcName): 42 | return vkGetDeviceProcAddr(InstanceProcAddr.T, funcName) 43 | 44 | # instance ext functions 45 | @InstanceProcAddr 46 | def vkCreateDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 47 | pass 48 | 49 | 50 | @InstanceProcAddr 51 | def vkDestroyDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 52 | pass 53 | 54 | 55 | @InstanceProcAddr 56 | def vkCreateWin32SurfaceKHR(instance, pCreateInfo, pAllocator): 57 | pass 58 | 59 | 60 | @InstanceProcAddr 61 | def vkDestroySurfaceKHR(instance, surface, pAllocator): 62 | pass 63 | 64 | 65 | @InstanceProcAddr 66 | def vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface): 67 | pass 68 | 69 | 70 | @InstanceProcAddr 71 | def vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface): 72 | pass 73 | 74 | 75 | @InstanceProcAddr 76 | def vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface): 77 | pass 78 | 79 | 80 | @InstanceProcAddr 81 | def vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface): 82 | pass 83 | 84 | 85 | # device ext functions 86 | @DeviceProcAddr 87 | def vkCreateSwapchainKHR(device, pCreateInfo, pAllocator): 88 | pass 89 | 90 | @DeviceProcAddr 91 | def vkDestroySwapchainKHR(device, swapchain, pAllocator): 92 | pass 93 | 94 | @DeviceProcAddr 95 | def vkGetSwapchainImagesKHR(device, swapchain): 96 | pass 97 | 98 | 99 | 100 | 101 | def debugCallback(*args): 102 | print('DEBUG: {} {}'.format(args[5], args[6])) 103 | return 0 104 | 105 | 106 | class Win32misc(object): 107 | @staticmethod 108 | def getInstance(hWnd): 109 | from cffi import FFI as _FFI 110 | _ffi = _FFI() 111 | _ffi.cdef('long __stdcall GetWindowLongA(void* hWnd, int nIndex);') 112 | _lib = _ffi.dlopen('User32.dll') 113 | return _lib.GetWindowLongA(_ffi.cast('void*', hWnd), -6) # GWL_HINSTANCE 114 | 115 | 116 | class QueueFamilyIndices(object): 117 | 118 | def __init__(self): 119 | self.graphicsFamily = -1 120 | self.presentFamily = -1 121 | 122 | @property 123 | def isComplete(self): 124 | return self.graphicsFamily >= 0 and self.presentFamily >= 0 125 | 126 | 127 | class SwapChainSupportDetails(object): 128 | 129 | def __init__(self): 130 | self.capabilities = None 131 | self.formats = None 132 | self.presentModes = None 133 | 134 | 135 | class HelloTriangleApplication(QtGui.QWindow): 136 | 137 | def __init__(self): 138 | super(HelloTriangleApplication, self).__init__() 139 | 140 | self.setWidth(1280) 141 | self.setHeight(720) 142 | 143 | self.setTitle("Vulkan Python - PySide2") 144 | 145 | # self.setSurfaceType(self.OpenGLSurface) 146 | 147 | self.__instance = None 148 | self.__callbcak = None 149 | self.__surface = None 150 | 151 | self.__physicalDevice = None 152 | self.__device = None 153 | self.__graphicQueue = None 154 | self.__presentQueue = None 155 | 156 | self.__swapChain = None 157 | self.__swapChainImages = [] 158 | self.__swapChainImageFormat = None 159 | self.__swapChainExtent = None 160 | 161 | self.__indices = QueueFamilyIndices() 162 | 163 | self.initVulkan() 164 | 165 | def __del__(self): 166 | if self.__swapChain: 167 | vkDestroySwapchainKHR(self.__device, self.__swapChain, None) 168 | 169 | if self.__device: 170 | vkDestroyDevice(self.__device, None) 171 | 172 | if self.__callbcak: 173 | vkDestroyDebugReportCallbackEXT(self.__instance, self.__callbcak, None) 174 | 175 | if self.__surface: 176 | vkDestroySurfaceKHR(self.__instance, self.__surface, None) 177 | 178 | if self.__instance: 179 | vkDestroyInstance(self.__instance, None) 180 | print('instance destroyed') 181 | 182 | def initVulkan(self): 183 | self.__cretaeInstance() 184 | self.__setupDebugCallback() 185 | self.__createSurface() 186 | self.__pickPhysicalDevice() 187 | self.__createLogicalDevice() 188 | self.__createSwapChain() 189 | 190 | def __cretaeInstance(self): 191 | if enableValidationLayers and not self.__checkValidationLayerSupport(): 192 | raise Exception("validation layers requested, but not available!") 193 | 194 | appInfo = VkApplicationInfo( 195 | # sType=VK_STRUCTURE_TYPE_APPLICATION_INFO, 196 | pApplicationName='Python VK', 197 | applicationVersion=VK_MAKE_VERSION(1, 0, 0), 198 | pEngineName='pyvulkan', 199 | engineVersion=VK_MAKE_VERSION(1, 0, 0), 200 | apiVersion=VK_API_VERSION 201 | ) 202 | 203 | extenstions = self.__getRequiredExtensions() 204 | if enableValidationLayers: 205 | instanceInfo = VkInstanceCreateInfo( 206 | pApplicationInfo=appInfo, 207 | enabledLayerCount=len(validationLayers), 208 | ppEnabledLayerNames=validationLayers, 209 | enabledExtensionCount=len(extenstions), 210 | ppEnabledExtensionNames=extenstions 211 | ) 212 | else: 213 | instanceInfo = VkInstanceCreateInfo( 214 | pApplicationInfo=appInfo, 215 | enabledLayerCount=0, 216 | enabledExtensionCount=len(extenstions), 217 | ppEnabledExtensionNames=extenstions 218 | ) 219 | 220 | self.__instance = vkCreateInstance(instanceInfo, None) 221 | 222 | InstanceProcAddr.T = self.__instance 223 | 224 | def __setupDebugCallback(self): 225 | if not enableValidationLayers: 226 | return 227 | 228 | createInfo = VkDebugReportCallbackCreateInfoEXT( 229 | flags=VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT, 230 | pfnCallback=debugCallback 231 | ) 232 | 233 | self.__callbcak = vkCreateDebugReportCallbackEXT(self.__instance, createInfo, None) 234 | 235 | def __createSurface(self): 236 | if sys.platform == 'win32': 237 | hwnd = self.winId() 238 | hinstance = Win32misc.getInstance(hwnd) 239 | createInfo = VkWin32SurfaceCreateInfoKHR( 240 | hinstance=hinstance, 241 | hwnd=hwnd 242 | ) 243 | 244 | self.__surface = vkCreateWin32SurfaceKHR(self.__instance, createInfo, None) 245 | # elif sys.platform == 'linux': 246 | # pass 247 | 248 | def __pickPhysicalDevice(self): 249 | physicalDevices = vkEnumeratePhysicalDevices(self.__instance) 250 | 251 | for device in physicalDevices: 252 | if self.__isDeviceSuitable(device): 253 | self.__physicalDevice = device 254 | break 255 | 256 | assert self.__physicalDevice != None 257 | 258 | def __createLogicalDevice(self): 259 | self.__indices = self.__findQueueFamilies(self.__physicalDevice) 260 | 261 | uniqueQueueFamilies = {}.fromkeys([self.__indices.graphicsFamily, self.__indices.presentFamily]) 262 | queueCreateInfos = [] 263 | for i in uniqueQueueFamilies: 264 | queueCreateInfo = VkDeviceQueueCreateInfo( 265 | queueFamilyIndex=i, 266 | queueCount=1, 267 | pQueuePriorities=[1.0] 268 | ) 269 | queueCreateInfos.append(queueCreateInfo) 270 | 271 | deviceFeatures = VkPhysicalDeviceFeatures() 272 | if enableValidationLayers: 273 | createInfo = VkDeviceCreateInfo( 274 | queueCreateInfoCount=len(queueCreateInfos), 275 | pQueueCreateInfos=queueCreateInfos, 276 | enabledExtensionCount=len(deviceExtensions), 277 | ppEnabledExtensionNames=deviceExtensions, 278 | enabledLayerCount=len(validationLayers), 279 | ppEnabledLayerNames=validationLayers, 280 | pEnabledFeatures=deviceFeatures 281 | ) 282 | else: 283 | createInfo = VkDeviceCreateInfo( 284 | queueCreateInfoCount=1, 285 | pQueueCreateInfos=queueCreateInfo, 286 | enabledExtensionCount=len(deviceExtensions), 287 | ppEnabledExtensionNames=deviceExtensions, 288 | enabledLayerCount=0, 289 | pEnabledFeatures=deviceFeatures 290 | ) 291 | 292 | self.__device = vkCreateDevice(self.__physicalDevice, createInfo, None) 293 | 294 | DeviceProcAddr.T = self.__device 295 | 296 | self.__graphicQueue = vkGetDeviceQueue(self.__device, self.__indices.graphicsFamily, 0) 297 | self.__presentQueue = vkGetDeviceQueue(self.__device, self.__indices.presentFamily, 0) 298 | 299 | def __createSwapChain(self): 300 | swapChainSupport = self.__querySwapChainSupport(self.__physicalDevice) 301 | 302 | surfaceFormat = self.__chooseSwapSurfaceFormat(swapChainSupport.formats) 303 | presentMode = self.__chooseSwapPresentMode(swapChainSupport.presentModes) 304 | extent = self.__chooseSwapExtent(swapChainSupport.capabilities) 305 | 306 | imageCount = swapChainSupport.capabilities.minImageCount + 1 307 | if swapChainSupport.capabilities.maxImageCount > 0 and imageCount > swapChainSupport.capabilities.maxImageCount: 308 | imageCount = swapChainSupport.capabilities.maxImageCount 309 | 310 | indices = self.__findQueueFamilies(self.__physicalDevice) 311 | queueFamily = {}.fromkeys([indices.graphicsFamily, indices.presentFamily]) 312 | queueFamilies = list(queueFamily.keys()) 313 | if len(queueFamilies) > 1: 314 | createInfo = VkSwapchainCreateInfoKHR( 315 | surface=self.__surface, 316 | minImageCount=imageCount, 317 | imageFormat=surfaceFormat.format, 318 | imageColorSpace=surfaceFormat.colorSpace, 319 | imageExtent=extent, 320 | imageArrayLayers=1, 321 | imageUsage=VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 322 | queueFamilyIndexCount=len(queueFamilies), 323 | pQueueFamilyIndices=queueFamilies, 324 | imageSharingMode=VK_SHARING_MODE_CONCURRENT, 325 | preTransform=swapChainSupport.capabilities.currentTransform, 326 | compositeAlpha=VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 327 | presentMode=presentMode, 328 | clipped=True 329 | ) 330 | else: 331 | createInfo = VkSwapchainCreateInfoKHR( 332 | surface=self.__surface, 333 | minImageCount=imageCount, 334 | imageFormat=surfaceFormat.format, 335 | imageColorSpace=surfaceFormat.colorSpace, 336 | imageExtent=extent, 337 | imageArrayLayers=1, 338 | imageUsage=VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 339 | queueFamilyIndexCount=len(queueFamilies), 340 | pQueueFamilyIndices=queueFamilies, 341 | imageSharingMode=VK_SHARING_MODE_EXCLUSIVE, 342 | preTransform=swapChainSupport.capabilities.currentTransform, 343 | compositeAlpha=VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 344 | presentMode=presentMode, 345 | clipped=True 346 | ) 347 | 348 | self.__swapChain = vkCreateSwapchainKHR(self.__device, createInfo, None) 349 | assert self.__swapChain != None 350 | 351 | self.__swapChainImages = vkGetSwapchainImagesKHR(self.__device, self.__swapChain) 352 | 353 | self.__swapChainImageFormat = surfaceFormat.format 354 | self.__swapChainExtent = extent 355 | 356 | def __chooseSwapSurfaceFormat(self, formats): 357 | if len(formats) == 1 and formats[0].format == VK_FORMAT_UNDEFINED: 358 | return [VK_FORMAT_B8G8R8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR] 359 | 360 | for i in formats: 361 | if i.format == VK_FORMAT_B8G8R8_UNORM and i.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: 362 | return i 363 | 364 | return formats[0] 365 | 366 | def __chooseSwapPresentMode(self, presentModes): 367 | bestMode = VK_PRESENT_MODE_FIFO_KHR 368 | 369 | for i in presentModes: 370 | if i == VK_PRESENT_MODE_FIFO_KHR: 371 | return i 372 | elif i == VK_PRESENT_MODE_MAILBOX_KHR: 373 | return i 374 | elif i == VK_PRESENT_MODE_IMMEDIATE_KHR: 375 | return i 376 | 377 | return bestMode 378 | 379 | def __chooseSwapExtent(self, capabilities): 380 | width = max(capabilities.minImageExtent.width, min(capabilities.maxImageExtent.width, self.width())) 381 | height = max(capabilities.minImageExtent.height, min(capabilities.maxImageExtent.height, self.height())) 382 | return VkExtent2D(width, height) 383 | 384 | def __querySwapChainSupport(self, device): 385 | detail = SwapChainSupportDetails() 386 | 387 | detail.capabilities = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, self.__surface) 388 | detail.formats = vkGetPhysicalDeviceSurfaceFormatsKHR(device, self.__surface) 389 | detail.presentModes = vkGetPhysicalDeviceSurfacePresentModesKHR(device, self.__surface) 390 | return detail 391 | 392 | def __isDeviceSuitable(self, device): 393 | indices = self.__findQueueFamilies(device) 394 | 395 | extensionsSupported = self.__checkDeviceExtensionSupport(device) 396 | 397 | swapChainAdequate = False 398 | if extensionsSupported: 399 | swapChainSupport = self.__querySwapChainSupport(device) 400 | swapChainAdequate = (swapChainSupport.formats is not None) and (swapChainSupport.presentModes is not None) 401 | 402 | return indices.isComplete and extensionsSupported and swapChainAdequate 403 | 404 | def __checkDeviceExtensionSupport(self, device): 405 | availableExtensions = vkEnumerateDeviceExtensionProperties(device, None) 406 | 407 | aen = [i.extensionName for i in availableExtensions] 408 | for i in deviceExtensions: 409 | if i not in aen: 410 | return False 411 | 412 | return True 413 | 414 | def __findQueueFamilies(self, device): 415 | indices = QueueFamilyIndices() 416 | 417 | familyProperties = vkGetPhysicalDeviceQueueFamilyProperties(device) 418 | for i, prop in enumerate(familyProperties): 419 | if prop.queueCount > 0 and prop.queueFlags & VK_QUEUE_GRAPHICS_BIT: 420 | indices.graphicsFamily = i 421 | 422 | presentSupport = vkGetPhysicalDeviceSurfaceSupportKHR(device, i, self.__surface) 423 | 424 | if prop.queueCount > 0 and presentSupport: 425 | indices.presentFamily = i 426 | 427 | if indices.isComplete: 428 | break 429 | 430 | return indices 431 | 432 | def __getRequiredExtensions(self): 433 | extenstions = [e.extensionName for e in vkEnumerateInstanceExtensionProperties(None)] 434 | 435 | if enableValidationLayers: 436 | extenstions.append(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) 437 | 438 | return extenstions 439 | 440 | def __checkValidationLayerSupport(self): 441 | availableLayers = vkEnumerateInstanceLayerProperties() 442 | 443 | for layer in validationLayers: 444 | layerfound = False 445 | 446 | for layerProp in availableLayers: 447 | if layer == layerProp.layerName: 448 | layerfound = True 449 | break 450 | return layerfound 451 | 452 | return False 453 | 454 | 455 | if __name__ == '__main__': 456 | import sys 457 | 458 | app = QtGui.QGuiApplication(sys.argv) 459 | 460 | win = HelloTriangleApplication() 461 | win.show() 462 | 463 | 464 | def clenaup(): 465 | global win 466 | del win 467 | 468 | 469 | app.aboutToQuit.connect(clenaup) 470 | 471 | sys.exit(app.exec_()) 472 | -------------------------------------------------------------------------------- /07_image_views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | import sys 4 | 5 | from vulkan import * 6 | from PySide2 import (QtGui, QtCore) 7 | 8 | validationLayers = [ 9 | 'VK_LAYER_LUNARG_standard_validation' 10 | ] 11 | 12 | deviceExtensions = [ 13 | VK_KHR_SWAPCHAIN_EXTENSION_NAME 14 | ] 15 | 16 | enableValidationLayers = True 17 | 18 | 19 | class InstanceProcAddr(object): 20 | T = None 21 | 22 | def __init__(self, func): 23 | self.__func = func 24 | 25 | def __call__(self, *args, **kwargs): 26 | funcName = self.__func.__name__ 27 | func = InstanceProcAddr.procfunc(funcName) 28 | if func: 29 | return func(*args, **kwargs) 30 | else: 31 | return VK_ERROR_EXTENSION_NOT_PRESENT 32 | 33 | @staticmethod 34 | def procfunc(funcName): 35 | return vkGetInstanceProcAddr(InstanceProcAddr.T, funcName) 36 | 37 | 38 | class DeviceProcAddr(InstanceProcAddr): 39 | 40 | @staticmethod 41 | def procfunc(funcName): 42 | return vkGetDeviceProcAddr(InstanceProcAddr.T, funcName) 43 | 44 | # instance ext functions 45 | @InstanceProcAddr 46 | def vkCreateDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 47 | pass 48 | 49 | 50 | @InstanceProcAddr 51 | def vkDestroyDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 52 | pass 53 | 54 | 55 | @InstanceProcAddr 56 | def vkCreateWin32SurfaceKHR(instance, pCreateInfo, pAllocator): 57 | pass 58 | 59 | 60 | @InstanceProcAddr 61 | def vkDestroySurfaceKHR(instance, surface, pAllocator): 62 | pass 63 | 64 | 65 | @InstanceProcAddr 66 | def vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface): 67 | pass 68 | 69 | 70 | @InstanceProcAddr 71 | def vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface): 72 | pass 73 | 74 | 75 | @InstanceProcAddr 76 | def vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface): 77 | pass 78 | 79 | 80 | @InstanceProcAddr 81 | def vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface): 82 | pass 83 | 84 | 85 | # device ext functions 86 | @DeviceProcAddr 87 | def vkCreateSwapchainKHR(device, pCreateInfo, pAllocator): 88 | pass 89 | 90 | @DeviceProcAddr 91 | def vkDestroySwapchainKHR(device, swapchain, pAllocator): 92 | pass 93 | 94 | @DeviceProcAddr 95 | def vkGetSwapchainImagesKHR(device, swapchain): 96 | pass 97 | 98 | 99 | 100 | 101 | def debugCallback(*args): 102 | print('DEBUG: {} {}'.format(args[5], args[6])) 103 | return 0 104 | 105 | 106 | class Win32misc(object): 107 | @staticmethod 108 | def getInstance(hWnd): 109 | from cffi import FFI as _FFI 110 | _ffi = _FFI() 111 | _ffi.cdef('long __stdcall GetWindowLongA(void* hWnd, int nIndex);') 112 | _lib = _ffi.dlopen('User32.dll') 113 | return _lib.GetWindowLongA(_ffi.cast('void*', hWnd), -6) # GWL_HINSTANCE 114 | 115 | 116 | class QueueFamilyIndices(object): 117 | 118 | def __init__(self): 119 | self.graphicsFamily = -1 120 | self.presentFamily = -1 121 | 122 | @property 123 | def isComplete(self): 124 | return self.graphicsFamily >= 0 and self.presentFamily >= 0 125 | 126 | 127 | class SwapChainSupportDetails(object): 128 | 129 | def __init__(self): 130 | self.capabilities = None 131 | self.formats = None 132 | self.presentModes = None 133 | 134 | 135 | class HelloTriangleApplication(QtGui.QWindow): 136 | 137 | def __init__(self): 138 | super(HelloTriangleApplication, self).__init__() 139 | 140 | self.setWidth(1280) 141 | self.setHeight(720) 142 | 143 | self.setTitle("Vulkan Python - PySide2") 144 | 145 | # self.setSurfaceType(self.OpenGLSurface) 146 | 147 | self.__instance = None 148 | self.__callbcak = None 149 | self.__surface = None 150 | 151 | self.__physicalDevice = None 152 | self.__device = None 153 | self.__graphicQueue = None 154 | self.__presentQueue = None 155 | 156 | self.__swapChain = None 157 | self.__swapChainImages = [] 158 | self.__swapChainImageFormat = None 159 | self.__swapChainExtent = None 160 | self.__swapChainImageViews = [] 161 | 162 | self.__indices = QueueFamilyIndices() 163 | 164 | self.initVulkan() 165 | 166 | def __del__(self): 167 | if self.__swapChainImageViews: 168 | [vkDestroyImageView(self.__device, imv, None) for imv in self.__swapChainImageViews] 169 | 170 | if self.__swapChain: 171 | vkDestroySwapchainKHR(self.__device, self.__swapChain, None) 172 | 173 | if self.__device: 174 | vkDestroyDevice(self.__device, None) 175 | 176 | if self.__callbcak: 177 | vkDestroyDebugReportCallbackEXT(self.__instance, self.__callbcak, None) 178 | 179 | if self.__surface: 180 | vkDestroySurfaceKHR(self.__instance, self.__surface, None) 181 | 182 | if self.__instance: 183 | vkDestroyInstance(self.__instance, None) 184 | print('instance destroyed') 185 | 186 | def initVulkan(self): 187 | self.__cretaeInstance() 188 | self.__setupDebugCallback() 189 | self.__createSurface() 190 | self.__pickPhysicalDevice() 191 | self.__createLogicalDevice() 192 | self.__createSwapChain() 193 | self.__createImageViews() 194 | 195 | def __cretaeInstance(self): 196 | if enableValidationLayers and not self.__checkValidationLayerSupport(): 197 | raise Exception("validation layers requested, but not available!") 198 | 199 | appInfo = VkApplicationInfo( 200 | # sType=VK_STRUCTURE_TYPE_APPLICATION_INFO, 201 | pApplicationName='Python VK', 202 | applicationVersion=VK_MAKE_VERSION(1, 0, 0), 203 | pEngineName='pyvulkan', 204 | engineVersion=VK_MAKE_VERSION(1, 0, 0), 205 | apiVersion=VK_API_VERSION 206 | ) 207 | 208 | extenstions = self.__getRequiredExtensions() 209 | if enableValidationLayers: 210 | instanceInfo = VkInstanceCreateInfo( 211 | pApplicationInfo=appInfo, 212 | enabledLayerCount=len(validationLayers), 213 | ppEnabledLayerNames=validationLayers, 214 | enabledExtensionCount=len(extenstions), 215 | ppEnabledExtensionNames=extenstions 216 | ) 217 | else: 218 | instanceInfo = VkInstanceCreateInfo( 219 | pApplicationInfo=appInfo, 220 | enabledLayerCount=0, 221 | enabledExtensionCount=len(extenstions), 222 | ppEnabledExtensionNames=extenstions 223 | ) 224 | 225 | self.__instance = vkCreateInstance(instanceInfo, None) 226 | 227 | InstanceProcAddr.T = self.__instance 228 | 229 | def __setupDebugCallback(self): 230 | if not enableValidationLayers: 231 | return 232 | 233 | createInfo = VkDebugReportCallbackCreateInfoEXT( 234 | flags=VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT, 235 | pfnCallback=debugCallback 236 | ) 237 | 238 | self.__callbcak = vkCreateDebugReportCallbackEXT(self.__instance, createInfo, None) 239 | 240 | def __createSurface(self): 241 | if sys.platform == 'win32': 242 | hwnd = self.winId() 243 | hinstance = Win32misc.getInstance(hwnd) 244 | createInfo = VkWin32SurfaceCreateInfoKHR( 245 | hinstance=hinstance, 246 | hwnd=hwnd 247 | ) 248 | 249 | self.__surface = vkCreateWin32SurfaceKHR(self.__instance, createInfo, None) 250 | # elif sys.platform == 'linux': 251 | # pass 252 | 253 | def __pickPhysicalDevice(self): 254 | physicalDevices = vkEnumeratePhysicalDevices(self.__instance) 255 | 256 | for device in physicalDevices: 257 | if self.__isDeviceSuitable(device): 258 | self.__physicalDevice = device 259 | break 260 | 261 | assert self.__physicalDevice != None 262 | 263 | def __createLogicalDevice(self): 264 | self.__indices = self.__findQueueFamilies(self.__physicalDevice) 265 | 266 | uniqueQueueFamilies = {}.fromkeys([self.__indices.graphicsFamily, self.__indices.presentFamily]) 267 | queueCreateInfos = [] 268 | for i in uniqueQueueFamilies: 269 | queueCreateInfo = VkDeviceQueueCreateInfo( 270 | queueFamilyIndex=i, 271 | queueCount=1, 272 | pQueuePriorities=[1.0] 273 | ) 274 | queueCreateInfos.append(queueCreateInfo) 275 | 276 | deviceFeatures = VkPhysicalDeviceFeatures() 277 | if enableValidationLayers: 278 | createInfo = VkDeviceCreateInfo( 279 | queueCreateInfoCount=len(queueCreateInfos), 280 | pQueueCreateInfos=queueCreateInfos, 281 | enabledExtensionCount=len(deviceExtensions), 282 | ppEnabledExtensionNames=deviceExtensions, 283 | enabledLayerCount=len(validationLayers), 284 | ppEnabledLayerNames=validationLayers, 285 | pEnabledFeatures=deviceFeatures 286 | ) 287 | else: 288 | createInfo = VkDeviceCreateInfo( 289 | queueCreateInfoCount=1, 290 | pQueueCreateInfos=queueCreateInfo, 291 | enabledExtensionCount=len(deviceExtensions), 292 | ppEnabledExtensionNames=deviceExtensions, 293 | enabledLayerCount=0, 294 | pEnabledFeatures=deviceFeatures 295 | ) 296 | 297 | self.__device = vkCreateDevice(self.__physicalDevice, createInfo, None) 298 | 299 | DeviceProcAddr.T = self.__device 300 | 301 | self.__graphicQueue = vkGetDeviceQueue(self.__device, self.__indices.graphicsFamily, 0) 302 | self.__presentQueue = vkGetDeviceQueue(self.__device, self.__indices.presentFamily, 0) 303 | 304 | def __createSwapChain(self): 305 | swapChainSupport = self.__querySwapChainSupport(self.__physicalDevice) 306 | 307 | surfaceFormat = self.__chooseSwapSurfaceFormat(swapChainSupport.formats) 308 | presentMode = self.__chooseSwapPresentMode(swapChainSupport.presentModes) 309 | extent = self.__chooseSwapExtent(swapChainSupport.capabilities) 310 | 311 | imageCount = swapChainSupport.capabilities.minImageCount + 1 312 | if swapChainSupport.capabilities.maxImageCount > 0 and imageCount > swapChainSupport.capabilities.maxImageCount: 313 | imageCount = swapChainSupport.capabilities.maxImageCount 314 | 315 | indices = self.__findQueueFamilies(self.__physicalDevice) 316 | queueFamily = {}.fromkeys([indices.graphicsFamily, indices.presentFamily]) 317 | queueFamilies = list(queueFamily.keys()) 318 | if len(queueFamilies) > 1: 319 | createInfo = VkSwapchainCreateInfoKHR( 320 | surface=self.__surface, 321 | minImageCount=imageCount, 322 | imageFormat=surfaceFormat.format, 323 | imageColorSpace=surfaceFormat.colorSpace, 324 | imageExtent=extent, 325 | imageArrayLayers=1, 326 | imageUsage=VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 327 | queueFamilyIndexCount=len(queueFamilies), 328 | pQueueFamilyIndices=queueFamilies, 329 | imageSharingMode=VK_SHARING_MODE_CONCURRENT, 330 | preTransform=swapChainSupport.capabilities.currentTransform, 331 | compositeAlpha=VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 332 | presentMode=presentMode, 333 | clipped=True 334 | ) 335 | else: 336 | createInfo = VkSwapchainCreateInfoKHR( 337 | surface=self.__surface, 338 | minImageCount=imageCount, 339 | imageFormat=surfaceFormat.format, 340 | imageColorSpace=surfaceFormat.colorSpace, 341 | imageExtent=extent, 342 | imageArrayLayers=1, 343 | imageUsage=VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 344 | queueFamilyIndexCount=len(queueFamilies), 345 | pQueueFamilyIndices=queueFamilies, 346 | imageSharingMode=VK_SHARING_MODE_EXCLUSIVE, 347 | preTransform=swapChainSupport.capabilities.currentTransform, 348 | compositeAlpha=VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 349 | presentMode=presentMode, 350 | clipped=True 351 | ) 352 | 353 | self.__swapChain = vkCreateSwapchainKHR(self.__device, createInfo, None) 354 | assert self.__swapChain != None 355 | 356 | self.__swapChainImages = vkGetSwapchainImagesKHR(self.__device, self.__swapChain) 357 | 358 | self.__swapChainImageFormat = surfaceFormat.format 359 | self.__swapChainExtent = extent 360 | 361 | def __createImageViews(self): 362 | self.__swapChainImageViews = [] 363 | 364 | for i, image in enumerate(self.__swapChainImages): 365 | ssr = VkImageSubresourceRange( 366 | VK_IMAGE_ASPECT_COLOR_BIT, 367 | 0, 1, 0, 1 368 | ) 369 | 370 | createInfo = VkImageViewCreateInfo( 371 | image=image, 372 | viewType=VK_IMAGE_VIEW_TYPE_2D, 373 | format=self.__swapChainImageFormat, 374 | components=[VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, 375 | VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY], 376 | subresourceRange=ssr 377 | ) 378 | 379 | self.__swapChainImageViews.append(vkCreateImageView(self.__device, createInfo, None)) 380 | 381 | def __chooseSwapSurfaceFormat(self, formats): 382 | if len(formats) == 1 and formats[0].format == VK_FORMAT_UNDEFINED: 383 | return [VK_FORMAT_B8G8R8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR] 384 | 385 | for i in formats: 386 | if i.format == VK_FORMAT_B8G8R8_UNORM and i.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: 387 | return i 388 | 389 | return formats[0] 390 | 391 | def __chooseSwapPresentMode(self, presentModes): 392 | bestMode = VK_PRESENT_MODE_FIFO_KHR 393 | 394 | for i in presentModes: 395 | if i == VK_PRESENT_MODE_FIFO_KHR: 396 | return i 397 | elif i == VK_PRESENT_MODE_MAILBOX_KHR: 398 | return i 399 | elif i == VK_PRESENT_MODE_IMMEDIATE_KHR: 400 | return i 401 | 402 | return bestMode 403 | 404 | def __chooseSwapExtent(self, capabilities): 405 | width = max(capabilities.minImageExtent.width, min(capabilities.maxImageExtent.width, self.width())) 406 | height = max(capabilities.minImageExtent.height, min(capabilities.maxImageExtent.height, self.height())) 407 | return VkExtent2D(width, height) 408 | 409 | def __querySwapChainSupport(self, device): 410 | detail = SwapChainSupportDetails() 411 | 412 | detail.capabilities = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, self.__surface) 413 | detail.formats = vkGetPhysicalDeviceSurfaceFormatsKHR(device, self.__surface) 414 | detail.presentModes = vkGetPhysicalDeviceSurfacePresentModesKHR(device, self.__surface) 415 | return detail 416 | 417 | def __isDeviceSuitable(self, device): 418 | indices = self.__findQueueFamilies(device) 419 | 420 | extensionsSupported = self.__checkDeviceExtensionSupport(device) 421 | 422 | swapChainAdequate = False 423 | if extensionsSupported: 424 | swapChainSupport = self.__querySwapChainSupport(device) 425 | swapChainAdequate = (swapChainSupport.formats is not None) and (swapChainSupport.presentModes is not None) 426 | 427 | return indices.isComplete and extensionsSupported and swapChainAdequate 428 | 429 | def __checkDeviceExtensionSupport(self, device): 430 | availableExtensions = vkEnumerateDeviceExtensionProperties(device, None) 431 | 432 | aen = [i.extensionName for i in availableExtensions] 433 | for i in deviceExtensions: 434 | if i not in aen: 435 | return False 436 | 437 | return True 438 | 439 | def __findQueueFamilies(self, device): 440 | indices = QueueFamilyIndices() 441 | 442 | familyProperties = vkGetPhysicalDeviceQueueFamilyProperties(device) 443 | for i, prop in enumerate(familyProperties): 444 | if prop.queueCount > 0 and prop.queueFlags & VK_QUEUE_GRAPHICS_BIT: 445 | indices.graphicsFamily = i 446 | 447 | presentSupport = vkGetPhysicalDeviceSurfaceSupportKHR(device, i, self.__surface) 448 | 449 | if prop.queueCount > 0 and presentSupport: 450 | indices.presentFamily = i 451 | 452 | if indices.isComplete: 453 | break 454 | 455 | return indices 456 | 457 | def __getRequiredExtensions(self): 458 | extenstions = [e.extensionName for e in vkEnumerateInstanceExtensionProperties(None)] 459 | 460 | if enableValidationLayers: 461 | extenstions.append(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) 462 | 463 | return extenstions 464 | 465 | def __checkValidationLayerSupport(self): 466 | availableLayers = vkEnumerateInstanceLayerProperties() 467 | 468 | for layer in validationLayers: 469 | layerfound = False 470 | 471 | for layerProp in availableLayers: 472 | if layer == layerProp.layerName: 473 | layerfound = True 474 | break 475 | return layerfound 476 | 477 | return False 478 | 479 | 480 | if __name__ == '__main__': 481 | import sys 482 | 483 | app = QtGui.QGuiApplication(sys.argv) 484 | 485 | win = HelloTriangleApplication() 486 | win.show() 487 | 488 | 489 | def clenaup(): 490 | global win 491 | del win 492 | 493 | 494 | app.aboutToQuit.connect(clenaup) 495 | 496 | sys.exit(app.exec_()) 497 | -------------------------------------------------------------------------------- /08_graphics_pipeline.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | import sys 4 | 5 | from vulkan import * 6 | from PySide2 import (QtGui, QtCore) 7 | 8 | validationLayers = [ 9 | 'VK_LAYER_LUNARG_standard_validation' 10 | ] 11 | 12 | deviceExtensions = [ 13 | VK_KHR_SWAPCHAIN_EXTENSION_NAME 14 | ] 15 | 16 | enableValidationLayers = True 17 | 18 | 19 | class InstanceProcAddr(object): 20 | T = None 21 | 22 | def __init__(self, func): 23 | self.__func = func 24 | 25 | def __call__(self, *args, **kwargs): 26 | funcName = self.__func.__name__ 27 | func = InstanceProcAddr.procfunc(funcName) 28 | if func: 29 | return func(*args, **kwargs) 30 | else: 31 | return VK_ERROR_EXTENSION_NOT_PRESENT 32 | 33 | @staticmethod 34 | def procfunc(funcName): 35 | return vkGetInstanceProcAddr(InstanceProcAddr.T, funcName) 36 | 37 | 38 | class DeviceProcAddr(InstanceProcAddr): 39 | 40 | @staticmethod 41 | def procfunc(funcName): 42 | return vkGetDeviceProcAddr(InstanceProcAddr.T, funcName) 43 | 44 | # instance ext functions 45 | @InstanceProcAddr 46 | def vkCreateDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 47 | pass 48 | 49 | 50 | @InstanceProcAddr 51 | def vkDestroyDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 52 | pass 53 | 54 | 55 | @InstanceProcAddr 56 | def vkCreateWin32SurfaceKHR(instance, pCreateInfo, pAllocator): 57 | pass 58 | 59 | 60 | @InstanceProcAddr 61 | def vkDestroySurfaceKHR(instance, surface, pAllocator): 62 | pass 63 | 64 | 65 | @InstanceProcAddr 66 | def vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface): 67 | pass 68 | 69 | 70 | @InstanceProcAddr 71 | def vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface): 72 | pass 73 | 74 | 75 | @InstanceProcAddr 76 | def vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface): 77 | pass 78 | 79 | 80 | @InstanceProcAddr 81 | def vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface): 82 | pass 83 | 84 | 85 | # device ext functions 86 | @DeviceProcAddr 87 | def vkCreateSwapchainKHR(device, pCreateInfo, pAllocator): 88 | pass 89 | 90 | @DeviceProcAddr 91 | def vkDestroySwapchainKHR(device, swapchain, pAllocator): 92 | pass 93 | 94 | @DeviceProcAddr 95 | def vkGetSwapchainImagesKHR(device, swapchain): 96 | pass 97 | 98 | 99 | 100 | 101 | def debugCallback(*args): 102 | print('DEBUG: {} {}'.format(args[5], args[6])) 103 | return 0 104 | 105 | 106 | class Win32misc(object): 107 | @staticmethod 108 | def getInstance(hWnd): 109 | from cffi import FFI as _FFI 110 | _ffi = _FFI() 111 | _ffi.cdef('long __stdcall GetWindowLongA(void* hWnd, int nIndex);') 112 | _lib = _ffi.dlopen('User32.dll') 113 | return _lib.GetWindowLongA(_ffi.cast('void*', hWnd), -6) # GWL_HINSTANCE 114 | 115 | 116 | class QueueFamilyIndices(object): 117 | 118 | def __init__(self): 119 | self.graphicsFamily = -1 120 | self.presentFamily = -1 121 | 122 | @property 123 | def isComplete(self): 124 | return self.graphicsFamily >= 0 and self.presentFamily >= 0 125 | 126 | 127 | class SwapChainSupportDetails(object): 128 | 129 | def __init__(self): 130 | self.capabilities = None 131 | self.formats = None 132 | self.presentModes = None 133 | 134 | 135 | class HelloTriangleApplication(QtGui.QWindow): 136 | 137 | def __init__(self): 138 | super(HelloTriangleApplication, self).__init__() 139 | 140 | self.setWidth(1280) 141 | self.setHeight(720) 142 | 143 | self.setTitle("Vulkan Python - PySide2") 144 | 145 | # self.setSurfaceType(self.OpenGLSurface) 146 | 147 | self.__instance = None 148 | self.__callbcak = None 149 | self.__surface = None 150 | 151 | self.__physicalDevice = None 152 | self.__device = None 153 | self.__graphicQueue = None 154 | self.__presentQueue = None 155 | 156 | self.__swapChain = None 157 | self.__swapChainImages = [] 158 | self.__swapChainImageFormat = None 159 | self.__swapChainExtent = None 160 | self.__swapChainImageViews = [] 161 | 162 | self.__indices = QueueFamilyIndices() 163 | 164 | self.initVulkan() 165 | 166 | def __del__(self): 167 | if self.__swapChainImageViews: 168 | [vkDestroyImageView(self.__device, imv, None) for imv in self.__swapChainImageViews] 169 | 170 | if self.__swapChain: 171 | vkDestroySwapchainKHR(self.__device, self.__swapChain, None) 172 | 173 | if self.__device: 174 | vkDestroyDevice(self.__device, None) 175 | 176 | if self.__callbcak: 177 | vkDestroyDebugReportCallbackEXT(self.__instance, self.__callbcak, None) 178 | 179 | if self.__surface: 180 | vkDestroySurfaceKHR(self.__instance, self.__surface, None) 181 | 182 | if self.__instance: 183 | vkDestroyInstance(self.__instance, None) 184 | print('instance destroyed') 185 | 186 | def initVulkan(self): 187 | self.__cretaeInstance() 188 | self.__setupDebugCallback() 189 | self.__createSurface() 190 | self.__pickPhysicalDevice() 191 | self.__createLogicalDevice() 192 | self.__createSwapChain() 193 | self.__createImageViews() 194 | 195 | def __cretaeInstance(self): 196 | if enableValidationLayers and not self.__checkValidationLayerSupport(): 197 | raise Exception("validation layers requested, but not available!") 198 | 199 | appInfo = VkApplicationInfo( 200 | # sType=VK_STRUCTURE_TYPE_APPLICATION_INFO, 201 | pApplicationName='Python VK', 202 | applicationVersion=VK_MAKE_VERSION(1, 0, 0), 203 | pEngineName='pyvulkan', 204 | engineVersion=VK_MAKE_VERSION(1, 0, 0), 205 | apiVersion=VK_API_VERSION 206 | ) 207 | 208 | extenstions = self.__getRequiredExtensions() 209 | if enableValidationLayers: 210 | instanceInfo = VkInstanceCreateInfo( 211 | pApplicationInfo=appInfo, 212 | enabledLayerCount=len(validationLayers), 213 | ppEnabledLayerNames=validationLayers, 214 | enabledExtensionCount=len(extenstions), 215 | ppEnabledExtensionNames=extenstions 216 | ) 217 | else: 218 | instanceInfo = VkInstanceCreateInfo( 219 | pApplicationInfo=appInfo, 220 | enabledLayerCount=0, 221 | enabledExtensionCount=len(extenstions), 222 | ppEnabledExtensionNames=extenstions 223 | ) 224 | 225 | self.__instance = vkCreateInstance(instanceInfo, None) 226 | 227 | InstanceProcAddr.T = self.__instance 228 | 229 | def __setupDebugCallback(self): 230 | if not enableValidationLayers: 231 | return 232 | 233 | createInfo = VkDebugReportCallbackCreateInfoEXT( 234 | flags=VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT, 235 | pfnCallback=debugCallback 236 | ) 237 | 238 | self.__callbcak = vkCreateDebugReportCallbackEXT(self.__instance, createInfo, None) 239 | 240 | def __createSurface(self): 241 | if sys.platform == 'win32': 242 | hwnd = self.winId() 243 | hinstance = Win32misc.getInstance(hwnd) 244 | createInfo = VkWin32SurfaceCreateInfoKHR( 245 | hinstance=hinstance, 246 | hwnd=hwnd 247 | ) 248 | 249 | self.__surface = vkCreateWin32SurfaceKHR(self.__instance, createInfo, None) 250 | # elif sys.platform == 'linux': 251 | # pass 252 | 253 | def __pickPhysicalDevice(self): 254 | physicalDevices = vkEnumeratePhysicalDevices(self.__instance) 255 | 256 | for device in physicalDevices: 257 | if self.__isDeviceSuitable(device): 258 | self.__physicalDevice = device 259 | break 260 | 261 | assert self.__physicalDevice != None 262 | 263 | def __createLogicalDevice(self): 264 | self.__indices = self.__findQueueFamilies(self.__physicalDevice) 265 | 266 | uniqueQueueFamilies = {}.fromkeys([self.__indices.graphicsFamily, self.__indices.presentFamily]) 267 | queueCreateInfos = [] 268 | for i in uniqueQueueFamilies: 269 | queueCreateInfo = VkDeviceQueueCreateInfo( 270 | queueFamilyIndex=i, 271 | queueCount=1, 272 | pQueuePriorities=[1.0] 273 | ) 274 | queueCreateInfos.append(queueCreateInfo) 275 | 276 | deviceFeatures = VkPhysicalDeviceFeatures() 277 | if enableValidationLayers: 278 | createInfo = VkDeviceCreateInfo( 279 | queueCreateInfoCount=len(queueCreateInfos), 280 | pQueueCreateInfos=queueCreateInfos, 281 | enabledExtensionCount=len(deviceExtensions), 282 | ppEnabledExtensionNames=deviceExtensions, 283 | enabledLayerCount=len(validationLayers), 284 | ppEnabledLayerNames=validationLayers, 285 | pEnabledFeatures=deviceFeatures 286 | ) 287 | else: 288 | createInfo = VkDeviceCreateInfo( 289 | queueCreateInfoCount=1, 290 | pQueueCreateInfos=queueCreateInfo, 291 | enabledExtensionCount=len(deviceExtensions), 292 | ppEnabledExtensionNames=deviceExtensions, 293 | enabledLayerCount=0, 294 | pEnabledFeatures=deviceFeatures 295 | ) 296 | 297 | self.__device = vkCreateDevice(self.__physicalDevice, createInfo, None) 298 | 299 | DeviceProcAddr.T = self.__device 300 | 301 | self.__graphicQueue = vkGetDeviceQueue(self.__device, self.__indices.graphicsFamily, 0) 302 | self.__presentQueue = vkGetDeviceQueue(self.__device, self.__indices.presentFamily, 0) 303 | 304 | def __createSwapChain(self): 305 | swapChainSupport = self.__querySwapChainSupport(self.__physicalDevice) 306 | 307 | surfaceFormat = self.__chooseSwapSurfaceFormat(swapChainSupport.formats) 308 | presentMode = self.__chooseSwapPresentMode(swapChainSupport.presentModes) 309 | extent = self.__chooseSwapExtent(swapChainSupport.capabilities) 310 | 311 | imageCount = swapChainSupport.capabilities.minImageCount + 1 312 | if swapChainSupport.capabilities.maxImageCount > 0 and imageCount > swapChainSupport.capabilities.maxImageCount: 313 | imageCount = swapChainSupport.capabilities.maxImageCount 314 | 315 | indices = self.__findQueueFamilies(self.__physicalDevice) 316 | queueFamily = {}.fromkeys([indices.graphicsFamily, indices.presentFamily]) 317 | queueFamilies = list(queueFamily.keys()) 318 | if len(queueFamilies) > 1: 319 | createInfo = VkSwapchainCreateInfoKHR( 320 | surface=self.__surface, 321 | minImageCount=imageCount, 322 | imageFormat=surfaceFormat.format, 323 | imageColorSpace=surfaceFormat.colorSpace, 324 | imageExtent=extent, 325 | imageArrayLayers=1, 326 | imageUsage=VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 327 | queueFamilyIndexCount=len(queueFamilies), 328 | pQueueFamilyIndices=queueFamilies, 329 | imageSharingMode=VK_SHARING_MODE_CONCURRENT, 330 | preTransform=swapChainSupport.capabilities.currentTransform, 331 | compositeAlpha=VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 332 | presentMode=presentMode, 333 | clipped=True 334 | ) 335 | else: 336 | createInfo = VkSwapchainCreateInfoKHR( 337 | surface=self.__surface, 338 | minImageCount=imageCount, 339 | imageFormat=surfaceFormat.format, 340 | imageColorSpace=surfaceFormat.colorSpace, 341 | imageExtent=extent, 342 | imageArrayLayers=1, 343 | imageUsage=VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 344 | queueFamilyIndexCount=len(queueFamilies), 345 | pQueueFamilyIndices=queueFamilies, 346 | imageSharingMode=VK_SHARING_MODE_EXCLUSIVE, 347 | preTransform=swapChainSupport.capabilities.currentTransform, 348 | compositeAlpha=VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 349 | presentMode=presentMode, 350 | clipped=True 351 | ) 352 | 353 | self.__swapChain = vkCreateSwapchainKHR(self.__device, createInfo, None) 354 | assert self.__swapChain != None 355 | 356 | self.__swapChainImages = vkGetSwapchainImagesKHR(self.__device, self.__swapChain) 357 | 358 | self.__swapChainImageFormat = surfaceFormat.format 359 | self.__swapChainExtent = extent 360 | 361 | def __createImageViews(self): 362 | self.__swapChainImageViews = [] 363 | 364 | for i, image in enumerate(self.__swapChainImages): 365 | ssr = VkImageSubresourceRange( 366 | VK_IMAGE_ASPECT_COLOR_BIT, 367 | 0, 1, 0, 1 368 | ) 369 | 370 | createInfo = VkImageViewCreateInfo( 371 | image=image, 372 | viewType=VK_IMAGE_VIEW_TYPE_2D, 373 | format=self.__swapChainImageFormat, 374 | components=[VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, 375 | VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY], 376 | subresourceRange=ssr 377 | ) 378 | 379 | self.__swapChainImageViews.append(vkCreateImageView(self.__device, createInfo, None)) 380 | 381 | def __createGraphicsPipeline(self): 382 | pass 383 | 384 | def __chooseSwapSurfaceFormat(self, formats): 385 | if len(formats) == 1 and formats[0].format == VK_FORMAT_UNDEFINED: 386 | return [VK_FORMAT_B8G8R8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR] 387 | 388 | for i in formats: 389 | if i.format == VK_FORMAT_B8G8R8_UNORM and i.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: 390 | return i 391 | 392 | return formats[0] 393 | 394 | def __chooseSwapPresentMode(self, presentModes): 395 | bestMode = VK_PRESENT_MODE_FIFO_KHR 396 | 397 | for i in presentModes: 398 | if i == VK_PRESENT_MODE_FIFO_KHR: 399 | return i 400 | elif i == VK_PRESENT_MODE_MAILBOX_KHR: 401 | return i 402 | elif i == VK_PRESENT_MODE_IMMEDIATE_KHR: 403 | return i 404 | 405 | return bestMode 406 | 407 | def __chooseSwapExtent(self, capabilities): 408 | width = max(capabilities.minImageExtent.width, min(capabilities.maxImageExtent.width, self.width())) 409 | height = max(capabilities.minImageExtent.height, min(capabilities.maxImageExtent.height, self.height())) 410 | return VkExtent2D(width, height) 411 | 412 | def __querySwapChainSupport(self, device): 413 | detail = SwapChainSupportDetails() 414 | 415 | detail.capabilities = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, self.__surface) 416 | detail.formats = vkGetPhysicalDeviceSurfaceFormatsKHR(device, self.__surface) 417 | detail.presentModes = vkGetPhysicalDeviceSurfacePresentModesKHR(device, self.__surface) 418 | return detail 419 | 420 | def __isDeviceSuitable(self, device): 421 | indices = self.__findQueueFamilies(device) 422 | 423 | extensionsSupported = self.__checkDeviceExtensionSupport(device) 424 | 425 | swapChainAdequate = False 426 | if extensionsSupported: 427 | swapChainSupport = self.__querySwapChainSupport(device) 428 | swapChainAdequate = (swapChainSupport.formats is not None) and (swapChainSupport.presentModes is not None) 429 | 430 | return indices.isComplete and extensionsSupported and swapChainAdequate 431 | 432 | def __checkDeviceExtensionSupport(self, device): 433 | availableExtensions = vkEnumerateDeviceExtensionProperties(device, None) 434 | 435 | aen = [i.extensionName for i in availableExtensions] 436 | for i in deviceExtensions: 437 | if i not in aen: 438 | return False 439 | 440 | return True 441 | 442 | def __findQueueFamilies(self, device): 443 | indices = QueueFamilyIndices() 444 | 445 | familyProperties = vkGetPhysicalDeviceQueueFamilyProperties(device) 446 | for i, prop in enumerate(familyProperties): 447 | if prop.queueCount > 0 and prop.queueFlags & VK_QUEUE_GRAPHICS_BIT: 448 | indices.graphicsFamily = i 449 | 450 | presentSupport = vkGetPhysicalDeviceSurfaceSupportKHR(device, i, self.__surface) 451 | 452 | if prop.queueCount > 0 and presentSupport: 453 | indices.presentFamily = i 454 | 455 | if indices.isComplete: 456 | break 457 | 458 | return indices 459 | 460 | def __getRequiredExtensions(self): 461 | extenstions = [e.extensionName for e in vkEnumerateInstanceExtensionProperties(None)] 462 | 463 | if enableValidationLayers: 464 | extenstions.append(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) 465 | 466 | return extenstions 467 | 468 | def __checkValidationLayerSupport(self): 469 | availableLayers = vkEnumerateInstanceLayerProperties() 470 | 471 | for layer in validationLayers: 472 | layerfound = False 473 | 474 | for layerProp in availableLayers: 475 | if layer == layerProp.layerName: 476 | layerfound = True 477 | break 478 | return layerfound 479 | 480 | return False 481 | 482 | 483 | if __name__ == '__main__': 484 | import sys 485 | 486 | app = QtGui.QGuiApplication(sys.argv) 487 | 488 | win = HelloTriangleApplication() 489 | win.show() 490 | 491 | 492 | def clenaup(): 493 | global win 494 | del win 495 | 496 | 497 | app.aboutToQuit.connect(clenaup) 498 | 499 | sys.exit(app.exec_()) 500 | -------------------------------------------------------------------------------- /09_shader_base.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec3 fragColor; 5 | 6 | layout(location = 0) out vec4 outColor; 7 | 8 | void main() { 9 | outColor = vec4(fragColor, 1.0); 10 | } -------------------------------------------------------------------------------- /09_shader_base.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | out gl_PerVertex { 5 | vec4 gl_Position; 6 | }; 7 | 8 | layout(location = 0) out vec3 fragColor; 9 | 10 | vec2 positions[3] = vec2[]( 11 | vec2(0.0, -0.5), 12 | vec2(0.5, 0.5), 13 | vec2(-0.5, 0.5) 14 | ); 15 | 16 | vec3 colors[3] = vec3[]( 17 | vec3(1.0, 0.0, 0.0), 18 | vec3(0.0, 1.0, 0.0), 19 | vec3(0.0, 0.0, 1.0) 20 | ); 21 | 22 | void main() { 23 | gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); 24 | fragColor = colors[gl_VertexIndex]; 25 | } -------------------------------------------------------------------------------- /09_shader_modules.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | import sys 4 | 5 | from vulkan import * 6 | from PySide2 import (QtGui, QtCore) 7 | 8 | validationLayers = [ 9 | 'VK_LAYER_LUNARG_standard_validation' 10 | ] 11 | 12 | deviceExtensions = [ 13 | VK_KHR_SWAPCHAIN_EXTENSION_NAME 14 | ] 15 | 16 | enableValidationLayers = True 17 | 18 | 19 | class InstanceProcAddr(object): 20 | T = None 21 | 22 | def __init__(self, func): 23 | self.__func = func 24 | 25 | def __call__(self, *args, **kwargs): 26 | funcName = self.__func.__name__ 27 | func = InstanceProcAddr.procfunc(funcName) 28 | if func: 29 | return func(*args, **kwargs) 30 | else: 31 | return VK_ERROR_EXTENSION_NOT_PRESENT 32 | 33 | @staticmethod 34 | def procfunc(funcName): 35 | return vkGetInstanceProcAddr(InstanceProcAddr.T, funcName) 36 | 37 | 38 | class DeviceProcAddr(InstanceProcAddr): 39 | 40 | @staticmethod 41 | def procfunc(funcName): 42 | return vkGetDeviceProcAddr(InstanceProcAddr.T, funcName) 43 | 44 | # instance ext functions 45 | @InstanceProcAddr 46 | def vkCreateDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 47 | pass 48 | 49 | 50 | @InstanceProcAddr 51 | def vkDestroyDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 52 | pass 53 | 54 | 55 | @InstanceProcAddr 56 | def vkCreateWin32SurfaceKHR(instance, pCreateInfo, pAllocator): 57 | pass 58 | 59 | 60 | @InstanceProcAddr 61 | def vkDestroySurfaceKHR(instance, surface, pAllocator): 62 | pass 63 | 64 | 65 | @InstanceProcAddr 66 | def vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface): 67 | pass 68 | 69 | 70 | @InstanceProcAddr 71 | def vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface): 72 | pass 73 | 74 | 75 | @InstanceProcAddr 76 | def vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface): 77 | pass 78 | 79 | 80 | @InstanceProcAddr 81 | def vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface): 82 | pass 83 | 84 | 85 | # device ext functions 86 | @DeviceProcAddr 87 | def vkCreateSwapchainKHR(device, pCreateInfo, pAllocator): 88 | pass 89 | 90 | @DeviceProcAddr 91 | def vkDestroySwapchainKHR(device, swapchain, pAllocator): 92 | pass 93 | 94 | @DeviceProcAddr 95 | def vkGetSwapchainImagesKHR(device, swapchain): 96 | pass 97 | 98 | 99 | 100 | 101 | def debugCallback(*args): 102 | print('DEBUG: {} {}'.format(args[5], args[6])) 103 | return 0 104 | 105 | 106 | class Win32misc(object): 107 | @staticmethod 108 | def getInstance(hWnd): 109 | from cffi import FFI as _FFI 110 | _ffi = _FFI() 111 | _ffi.cdef('long __stdcall GetWindowLongA(void* hWnd, int nIndex);') 112 | _lib = _ffi.dlopen('User32.dll') 113 | return _lib.GetWindowLongA(_ffi.cast('void*', hWnd), -6) # GWL_HINSTANCE 114 | 115 | 116 | class QueueFamilyIndices(object): 117 | 118 | def __init__(self): 119 | self.graphicsFamily = -1 120 | self.presentFamily = -1 121 | 122 | @property 123 | def isComplete(self): 124 | return self.graphicsFamily >= 0 and self.presentFamily >= 0 125 | 126 | 127 | class SwapChainSupportDetails(object): 128 | 129 | def __init__(self): 130 | self.capabilities = None 131 | self.formats = None 132 | self.presentModes = None 133 | 134 | 135 | class HelloTriangleApplication(QtGui.QWindow): 136 | 137 | def __init__(self): 138 | super(HelloTriangleApplication, self).__init__() 139 | 140 | self.setWidth(1280) 141 | self.setHeight(720) 142 | 143 | self.setTitle("Vulkan Python - PySide2") 144 | 145 | # self.setSurfaceType(self.OpenGLSurface) 146 | 147 | self.__instance = None 148 | self.__callbcak = None 149 | self.__surface = None 150 | 151 | self.__physicalDevice = None 152 | self.__device = None 153 | self.__graphicQueue = None 154 | self.__presentQueue = None 155 | 156 | self.__swapChain = None 157 | self.__swapChainImages = [] 158 | self.__swapChainImageFormat = None 159 | self.__swapChainExtent = None 160 | self.__swapChainImageViews = [] 161 | 162 | self.__indices = QueueFamilyIndices() 163 | 164 | self.initVulkan() 165 | 166 | def __del__(self): 167 | if self.__swapChainImageViews: 168 | [vkDestroyImageView(self.__device, imv, None) for imv in self.__swapChainImageViews] 169 | 170 | if self.__swapChain: 171 | vkDestroySwapchainKHR(self.__device, self.__swapChain, None) 172 | 173 | if self.__device: 174 | vkDestroyDevice(self.__device, None) 175 | 176 | if self.__callbcak: 177 | vkDestroyDebugReportCallbackEXT(self.__instance, self.__callbcak, None) 178 | 179 | if self.__surface: 180 | vkDestroySurfaceKHR(self.__instance, self.__surface, None) 181 | 182 | if self.__instance: 183 | vkDestroyInstance(self.__instance, None) 184 | print('instance destroyed') 185 | 186 | def initVulkan(self): 187 | self.__cretaeInstance() 188 | self.__setupDebugCallback() 189 | self.__createSurface() 190 | self.__pickPhysicalDevice() 191 | self.__createLogicalDevice() 192 | self.__createSwapChain() 193 | self.__createImageViews() 194 | self.__createGraphicsPipeline() 195 | 196 | def __cretaeInstance(self): 197 | if enableValidationLayers and not self.__checkValidationLayerSupport(): 198 | raise Exception("validation layers requested, but not available!") 199 | 200 | appInfo = VkApplicationInfo( 201 | # sType=VK_STRUCTURE_TYPE_APPLICATION_INFO, 202 | pApplicationName='Python VK', 203 | applicationVersion=VK_MAKE_VERSION(1, 0, 0), 204 | pEngineName='pyvulkan', 205 | engineVersion=VK_MAKE_VERSION(1, 0, 0), 206 | apiVersion=VK_API_VERSION 207 | ) 208 | 209 | extenstions = self.__getRequiredExtensions() 210 | if enableValidationLayers: 211 | instanceInfo = VkInstanceCreateInfo( 212 | pApplicationInfo=appInfo, 213 | enabledLayerCount=len(validationLayers), 214 | ppEnabledLayerNames=validationLayers, 215 | enabledExtensionCount=len(extenstions), 216 | ppEnabledExtensionNames=extenstions 217 | ) 218 | else: 219 | instanceInfo = VkInstanceCreateInfo( 220 | pApplicationInfo=appInfo, 221 | enabledLayerCount=0, 222 | enabledExtensionCount=len(extenstions), 223 | ppEnabledExtensionNames=extenstions 224 | ) 225 | 226 | self.__instance = vkCreateInstance(instanceInfo, None) 227 | 228 | InstanceProcAddr.T = self.__instance 229 | 230 | def __setupDebugCallback(self): 231 | if not enableValidationLayers: 232 | return 233 | 234 | createInfo = VkDebugReportCallbackCreateInfoEXT( 235 | flags=VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT, 236 | pfnCallback=debugCallback 237 | ) 238 | 239 | self.__callbcak = vkCreateDebugReportCallbackEXT(self.__instance, createInfo, None) 240 | 241 | def __createSurface(self): 242 | if sys.platform == 'win32': 243 | hwnd = self.winId() 244 | hinstance = Win32misc.getInstance(hwnd) 245 | createInfo = VkWin32SurfaceCreateInfoKHR( 246 | hinstance=hinstance, 247 | hwnd=hwnd 248 | ) 249 | 250 | self.__surface = vkCreateWin32SurfaceKHR(self.__instance, createInfo, None) 251 | # elif sys.platform == 'linux': 252 | # pass 253 | 254 | def __pickPhysicalDevice(self): 255 | physicalDevices = vkEnumeratePhysicalDevices(self.__instance) 256 | 257 | for device in physicalDevices: 258 | if self.__isDeviceSuitable(device): 259 | self.__physicalDevice = device 260 | break 261 | 262 | assert self.__physicalDevice != None 263 | 264 | def __createLogicalDevice(self): 265 | self.__indices = self.__findQueueFamilies(self.__physicalDevice) 266 | 267 | uniqueQueueFamilies = {}.fromkeys([self.__indices.graphicsFamily, self.__indices.presentFamily]) 268 | queueCreateInfos = [] 269 | for i in uniqueQueueFamilies: 270 | queueCreateInfo = VkDeviceQueueCreateInfo( 271 | queueFamilyIndex=i, 272 | queueCount=1, 273 | pQueuePriorities=[1.0] 274 | ) 275 | queueCreateInfos.append(queueCreateInfo) 276 | 277 | deviceFeatures = VkPhysicalDeviceFeatures() 278 | if enableValidationLayers: 279 | createInfo = VkDeviceCreateInfo( 280 | queueCreateInfoCount=len(queueCreateInfos), 281 | pQueueCreateInfos=queueCreateInfos, 282 | enabledExtensionCount=len(deviceExtensions), 283 | ppEnabledExtensionNames=deviceExtensions, 284 | enabledLayerCount=len(validationLayers), 285 | ppEnabledLayerNames=validationLayers, 286 | pEnabledFeatures=deviceFeatures 287 | ) 288 | else: 289 | createInfo = VkDeviceCreateInfo( 290 | queueCreateInfoCount=1, 291 | pQueueCreateInfos=queueCreateInfo, 292 | enabledExtensionCount=len(deviceExtensions), 293 | ppEnabledExtensionNames=deviceExtensions, 294 | enabledLayerCount=0, 295 | pEnabledFeatures=deviceFeatures 296 | ) 297 | 298 | self.__device = vkCreateDevice(self.__physicalDevice, createInfo, None) 299 | 300 | DeviceProcAddr.T = self.__device 301 | 302 | self.__graphicQueue = vkGetDeviceQueue(self.__device, self.__indices.graphicsFamily, 0) 303 | self.__presentQueue = vkGetDeviceQueue(self.__device, self.__indices.presentFamily, 0) 304 | 305 | def __createSwapChain(self): 306 | swapChainSupport = self.__querySwapChainSupport(self.__physicalDevice) 307 | 308 | surfaceFormat = self.__chooseSwapSurfaceFormat(swapChainSupport.formats) 309 | presentMode = self.__chooseSwapPresentMode(swapChainSupport.presentModes) 310 | extent = self.__chooseSwapExtent(swapChainSupport.capabilities) 311 | 312 | imageCount = swapChainSupport.capabilities.minImageCount + 1 313 | if swapChainSupport.capabilities.maxImageCount > 0 and imageCount > swapChainSupport.capabilities.maxImageCount: 314 | imageCount = swapChainSupport.capabilities.maxImageCount 315 | 316 | indices = self.__findQueueFamilies(self.__physicalDevice) 317 | queueFamily = {}.fromkeys([indices.graphicsFamily, indices.presentFamily]) 318 | queueFamilies = list(queueFamily.keys()) 319 | if len(queueFamilies) > 1: 320 | createInfo = VkSwapchainCreateInfoKHR( 321 | surface=self.__surface, 322 | minImageCount=imageCount, 323 | imageFormat=surfaceFormat.format, 324 | imageColorSpace=surfaceFormat.colorSpace, 325 | imageExtent=extent, 326 | imageArrayLayers=1, 327 | imageUsage=VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 328 | queueFamilyIndexCount=len(queueFamilies), 329 | pQueueFamilyIndices=queueFamilies, 330 | imageSharingMode=VK_SHARING_MODE_CONCURRENT, 331 | preTransform=swapChainSupport.capabilities.currentTransform, 332 | compositeAlpha=VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 333 | presentMode=presentMode, 334 | clipped=True 335 | ) 336 | else: 337 | createInfo = VkSwapchainCreateInfoKHR( 338 | surface=self.__surface, 339 | minImageCount=imageCount, 340 | imageFormat=surfaceFormat.format, 341 | imageColorSpace=surfaceFormat.colorSpace, 342 | imageExtent=extent, 343 | imageArrayLayers=1, 344 | imageUsage=VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 345 | queueFamilyIndexCount=len(queueFamilies), 346 | pQueueFamilyIndices=queueFamilies, 347 | imageSharingMode=VK_SHARING_MODE_EXCLUSIVE, 348 | preTransform=swapChainSupport.capabilities.currentTransform, 349 | compositeAlpha=VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 350 | presentMode=presentMode, 351 | clipped=True 352 | ) 353 | 354 | self.__swapChain = vkCreateSwapchainKHR(self.__device, createInfo, None) 355 | assert self.__swapChain != None 356 | 357 | self.__swapChainImages = vkGetSwapchainImagesKHR(self.__device, self.__swapChain) 358 | 359 | self.__swapChainImageFormat = surfaceFormat.format 360 | self.__swapChainExtent = extent 361 | 362 | def __createImageViews(self): 363 | self.__swapChainImageViews = [] 364 | 365 | for i, image in enumerate(self.__swapChainImages): 366 | ssr = VkImageSubresourceRange( 367 | VK_IMAGE_ASPECT_COLOR_BIT, 368 | 0, 1, 0, 1 369 | ) 370 | 371 | createInfo = VkImageViewCreateInfo( 372 | image=image, 373 | viewType=VK_IMAGE_VIEW_TYPE_2D, 374 | format=self.__swapChainImageFormat, 375 | components=[VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, 376 | VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY], 377 | subresourceRange=ssr 378 | ) 379 | 380 | self.__swapChainImageViews.append(vkCreateImageView(self.__device, createInfo, None)) 381 | 382 | def __createGraphicsPipeline(self): 383 | vertexShaderMode = self.__createShaderModule('shader/vert.spv') 384 | fragmentShaderMode = self.__createShaderModule('shader/frag.spv') 385 | 386 | vertexShaderStageInfo = VkPipelineShaderStageCreateInfo( 387 | stage=VK_SHADER_STAGE_VERTEX_BIT, 388 | module=vertexShaderMode, 389 | pName='main' 390 | ) 391 | fragmentShaderStageInfo = VkPipelineShaderStageCreateInfo( 392 | stage=VK_SHADER_STAGE_FRAGMENT_BIT, 393 | module=fragmentShaderMode, 394 | pName='main' 395 | ) 396 | 397 | shaderStageInfos = [vertexShaderStageInfo, fragmentShaderStageInfo] 398 | 399 | vkDestroyShaderModule(self.__device, vertexShaderMode, None) 400 | vkDestroyShaderModule(self.__device, fragmentShaderMode, None) 401 | 402 | def __createShaderModule(self, shaderFile): 403 | with open(shaderFile, 'rb') as sf: 404 | code = sf.read() 405 | 406 | createInfo = VkShaderModuleCreateInfo( 407 | codeSize=len(code), 408 | pCode=code 409 | ) 410 | 411 | return vkCreateShaderModule(self.__device, createInfo, None) 412 | 413 | def __chooseSwapSurfaceFormat(self, formats): 414 | if len(formats) == 1 and formats[0].format == VK_FORMAT_UNDEFINED: 415 | return [VK_FORMAT_B8G8R8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR] 416 | 417 | for i in formats: 418 | if i.format == VK_FORMAT_B8G8R8_UNORM and i.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: 419 | return i 420 | 421 | return formats[0] 422 | 423 | def __chooseSwapPresentMode(self, presentModes): 424 | bestMode = VK_PRESENT_MODE_FIFO_KHR 425 | 426 | for i in presentModes: 427 | if i == VK_PRESENT_MODE_FIFO_KHR: 428 | return i 429 | elif i == VK_PRESENT_MODE_MAILBOX_KHR: 430 | return i 431 | elif i == VK_PRESENT_MODE_IMMEDIATE_KHR: 432 | return i 433 | 434 | return bestMode 435 | 436 | def __chooseSwapExtent(self, capabilities): 437 | width = max(capabilities.minImageExtent.width, min(capabilities.maxImageExtent.width, self.width())) 438 | height = max(capabilities.minImageExtent.height, min(capabilities.maxImageExtent.height, self.height())) 439 | return VkExtent2D(width, height) 440 | 441 | def __querySwapChainSupport(self, device): 442 | detail = SwapChainSupportDetails() 443 | 444 | detail.capabilities = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, self.__surface) 445 | detail.formats = vkGetPhysicalDeviceSurfaceFormatsKHR(device, self.__surface) 446 | detail.presentModes = vkGetPhysicalDeviceSurfacePresentModesKHR(device, self.__surface) 447 | return detail 448 | 449 | def __isDeviceSuitable(self, device): 450 | indices = self.__findQueueFamilies(device) 451 | 452 | extensionsSupported = self.__checkDeviceExtensionSupport(device) 453 | 454 | swapChainAdequate = False 455 | if extensionsSupported: 456 | swapChainSupport = self.__querySwapChainSupport(device) 457 | swapChainAdequate = (swapChainSupport.formats is not None) and (swapChainSupport.presentModes is not None) 458 | 459 | return indices.isComplete and extensionsSupported and swapChainAdequate 460 | 461 | def __checkDeviceExtensionSupport(self, device): 462 | availableExtensions = vkEnumerateDeviceExtensionProperties(device, None) 463 | 464 | aen = [i.extensionName for i in availableExtensions] 465 | for i in deviceExtensions: 466 | if i not in aen: 467 | return False 468 | 469 | return True 470 | 471 | def __findQueueFamilies(self, device): 472 | indices = QueueFamilyIndices() 473 | 474 | familyProperties = vkGetPhysicalDeviceQueueFamilyProperties(device) 475 | for i, prop in enumerate(familyProperties): 476 | if prop.queueCount > 0 and prop.queueFlags & VK_QUEUE_GRAPHICS_BIT: 477 | indices.graphicsFamily = i 478 | 479 | presentSupport = vkGetPhysicalDeviceSurfaceSupportKHR(device, i, self.__surface) 480 | 481 | if prop.queueCount > 0 and presentSupport: 482 | indices.presentFamily = i 483 | 484 | if indices.isComplete: 485 | break 486 | 487 | return indices 488 | 489 | def __getRequiredExtensions(self): 490 | extenstions = [e.extensionName for e in vkEnumerateInstanceExtensionProperties(None)] 491 | 492 | if enableValidationLayers: 493 | extenstions.append(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) 494 | 495 | return extenstions 496 | 497 | def __checkValidationLayerSupport(self): 498 | availableLayers = vkEnumerateInstanceLayerProperties() 499 | 500 | for layer in validationLayers: 501 | layerfound = False 502 | 503 | for layerProp in availableLayers: 504 | if layer == layerProp.layerName: 505 | layerfound = True 506 | break 507 | return layerfound 508 | 509 | return False 510 | 511 | 512 | if __name__ == '__main__': 513 | import sys 514 | 515 | app = QtGui.QGuiApplication(sys.argv) 516 | 517 | win = HelloTriangleApplication() 518 | win.show() 519 | 520 | 521 | def clenaup(): 522 | global win 523 | del win 524 | 525 | 526 | app.aboutToQuit.connect(clenaup) 527 | 528 | sys.exit(app.exec_()) 529 | -------------------------------------------------------------------------------- /10_fixed_functions.c.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | import sys 4 | 5 | from vulkan import * 6 | from PySide2 import (QtGui, QtCore) 7 | 8 | validationLayers = [ 9 | 'VK_LAYER_LUNARG_standard_validation' 10 | ] 11 | 12 | deviceExtensions = [ 13 | VK_KHR_SWAPCHAIN_EXTENSION_NAME 14 | ] 15 | 16 | enableValidationLayers = True 17 | 18 | 19 | class InstanceProcAddr(object): 20 | T = None 21 | 22 | def __init__(self, func): 23 | self.__func = func 24 | 25 | def __call__(self, *args, **kwargs): 26 | funcName = self.__func.__name__ 27 | func = InstanceProcAddr.procfunc(funcName) 28 | if func: 29 | return func(*args, **kwargs) 30 | else: 31 | return VK_ERROR_EXTENSION_NOT_PRESENT 32 | 33 | @staticmethod 34 | def procfunc(funcName): 35 | return vkGetInstanceProcAddr(InstanceProcAddr.T, funcName) 36 | 37 | 38 | class DeviceProcAddr(InstanceProcAddr): 39 | 40 | @staticmethod 41 | def procfunc(funcName): 42 | return vkGetDeviceProcAddr(InstanceProcAddr.T, funcName) 43 | 44 | # instance ext functions 45 | @InstanceProcAddr 46 | def vkCreateDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 47 | pass 48 | 49 | 50 | @InstanceProcAddr 51 | def vkDestroyDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 52 | pass 53 | 54 | 55 | @InstanceProcAddr 56 | def vkCreateWin32SurfaceKHR(instance, pCreateInfo, pAllocator): 57 | pass 58 | 59 | 60 | @InstanceProcAddr 61 | def vkDestroySurfaceKHR(instance, surface, pAllocator): 62 | pass 63 | 64 | 65 | @InstanceProcAddr 66 | def vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface): 67 | pass 68 | 69 | 70 | @InstanceProcAddr 71 | def vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface): 72 | pass 73 | 74 | 75 | @InstanceProcAddr 76 | def vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface): 77 | pass 78 | 79 | 80 | @InstanceProcAddr 81 | def vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface): 82 | pass 83 | 84 | 85 | # device ext functions 86 | @DeviceProcAddr 87 | def vkCreateSwapchainKHR(device, pCreateInfo, pAllocator): 88 | pass 89 | 90 | @DeviceProcAddr 91 | def vkDestroySwapchainKHR(device, swapchain, pAllocator): 92 | pass 93 | 94 | @DeviceProcAddr 95 | def vkGetSwapchainImagesKHR(device, swapchain): 96 | pass 97 | 98 | 99 | 100 | 101 | def debugCallback(*args): 102 | print('DEBUG: {} {}'.format(args[5], args[6])) 103 | return 0 104 | 105 | 106 | class Win32misc(object): 107 | @staticmethod 108 | def getInstance(hWnd): 109 | from cffi import FFI as _FFI 110 | _ffi = _FFI() 111 | _ffi.cdef('long __stdcall GetWindowLongA(void* hWnd, int nIndex);') 112 | _lib = _ffi.dlopen('User32.dll') 113 | return _lib.GetWindowLongA(_ffi.cast('void*', hWnd), -6) # GWL_HINSTANCE 114 | 115 | 116 | class QueueFamilyIndices(object): 117 | 118 | def __init__(self): 119 | self.graphicsFamily = -1 120 | self.presentFamily = -1 121 | 122 | @property 123 | def isComplete(self): 124 | return self.graphicsFamily >= 0 and self.presentFamily >= 0 125 | 126 | 127 | class SwapChainSupportDetails(object): 128 | 129 | def __init__(self): 130 | self.capabilities = None 131 | self.formats = None 132 | self.presentModes = None 133 | 134 | 135 | class HelloTriangleApplication(QtGui.QWindow): 136 | 137 | def __init__(self): 138 | super(HelloTriangleApplication, self).__init__() 139 | 140 | self.setWidth(1280) 141 | self.setHeight(720) 142 | 143 | self.setTitle("Vulkan Python - PySide2") 144 | 145 | # self.setSurfaceType(self.OpenGLSurface) 146 | 147 | self.__instance = None 148 | self.__callbcak = None 149 | self.__surface = None 150 | 151 | self.__physicalDevice = None 152 | self.__device = None 153 | self.__graphicQueue = None 154 | self.__presentQueue = None 155 | 156 | self.__swapChain = None 157 | self.__swapChainImages = [] 158 | self.__swapChainImageFormat = None 159 | self.__swapChainExtent = None 160 | self.__swapChainImageViews = [] 161 | 162 | self.__pipelineLayout = None 163 | 164 | self.__indices = QueueFamilyIndices() 165 | 166 | self.initVulkan() 167 | 168 | def __del__(self): 169 | if self.__pipelineLayout: 170 | vkDestroyPipelineLayout(self.__device, self.__pipelineLayout, None) 171 | 172 | if self.__swapChainImageViews: 173 | [vkDestroyImageView(self.__device, imv, None) for imv in self.__swapChainImageViews] 174 | 175 | if self.__swapChain: 176 | vkDestroySwapchainKHR(self.__device, self.__swapChain, None) 177 | 178 | if self.__device: 179 | vkDestroyDevice(self.__device, None) 180 | 181 | if self.__callbcak: 182 | vkDestroyDebugReportCallbackEXT(self.__instance, self.__callbcak, None) 183 | 184 | if self.__surface: 185 | vkDestroySurfaceKHR(self.__instance, self.__surface, None) 186 | 187 | if self.__instance: 188 | vkDestroyInstance(self.__instance, None) 189 | print('instance destroyed') 190 | 191 | def initVulkan(self): 192 | self.__cretaeInstance() 193 | self.__setupDebugCallback() 194 | self.__createSurface() 195 | self.__pickPhysicalDevice() 196 | self.__createLogicalDevice() 197 | self.__createSwapChain() 198 | self.__createImageViews() 199 | self.__createGraphicsPipeline() 200 | 201 | def __cretaeInstance(self): 202 | if enableValidationLayers and not self.__checkValidationLayerSupport(): 203 | raise Exception("validation layers requested, but not available!") 204 | 205 | appInfo = VkApplicationInfo( 206 | # sType=VK_STRUCTURE_TYPE_APPLICATION_INFO, 207 | pApplicationName='Python VK', 208 | applicationVersion=VK_MAKE_VERSION(1, 0, 0), 209 | pEngineName='pyvulkan', 210 | engineVersion=VK_MAKE_VERSION(1, 0, 0), 211 | apiVersion=VK_API_VERSION 212 | ) 213 | 214 | extenstions = self.__getRequiredExtensions() 215 | if enableValidationLayers: 216 | instanceInfo = VkInstanceCreateInfo( 217 | pApplicationInfo=appInfo, 218 | enabledLayerCount=len(validationLayers), 219 | ppEnabledLayerNames=validationLayers, 220 | enabledExtensionCount=len(extenstions), 221 | ppEnabledExtensionNames=extenstions 222 | ) 223 | else: 224 | instanceInfo = VkInstanceCreateInfo( 225 | pApplicationInfo=appInfo, 226 | enabledLayerCount=0, 227 | enabledExtensionCount=len(extenstions), 228 | ppEnabledExtensionNames=extenstions 229 | ) 230 | 231 | self.__instance = vkCreateInstance(instanceInfo, None) 232 | 233 | InstanceProcAddr.T = self.__instance 234 | 235 | def __setupDebugCallback(self): 236 | if not enableValidationLayers: 237 | return 238 | 239 | createInfo = VkDebugReportCallbackCreateInfoEXT( 240 | flags=VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT, 241 | pfnCallback=debugCallback 242 | ) 243 | 244 | self.__callbcak = vkCreateDebugReportCallbackEXT(self.__instance, createInfo, None) 245 | 246 | def __createSurface(self): 247 | if sys.platform == 'win32': 248 | hwnd = self.winId() 249 | hinstance = Win32misc.getInstance(hwnd) 250 | createInfo = VkWin32SurfaceCreateInfoKHR( 251 | hinstance=hinstance, 252 | hwnd=hwnd 253 | ) 254 | 255 | self.__surface = vkCreateWin32SurfaceKHR(self.__instance, createInfo, None) 256 | # elif sys.platform == 'linux': 257 | # pass 258 | 259 | def __pickPhysicalDevice(self): 260 | physicalDevices = vkEnumeratePhysicalDevices(self.__instance) 261 | 262 | for device in physicalDevices: 263 | if self.__isDeviceSuitable(device): 264 | self.__physicalDevice = device 265 | break 266 | 267 | assert self.__physicalDevice != None 268 | 269 | def __createLogicalDevice(self): 270 | self.__indices = self.__findQueueFamilies(self.__physicalDevice) 271 | 272 | uniqueQueueFamilies = {}.fromkeys([self.__indices.graphicsFamily, self.__indices.presentFamily]) 273 | queueCreateInfos = [] 274 | for i in uniqueQueueFamilies: 275 | queueCreateInfo = VkDeviceQueueCreateInfo( 276 | queueFamilyIndex=i, 277 | queueCount=1, 278 | pQueuePriorities=[1.0] 279 | ) 280 | queueCreateInfos.append(queueCreateInfo) 281 | 282 | deviceFeatures = VkPhysicalDeviceFeatures() 283 | if enableValidationLayers: 284 | createInfo = VkDeviceCreateInfo( 285 | queueCreateInfoCount=len(queueCreateInfos), 286 | pQueueCreateInfos=queueCreateInfos, 287 | enabledExtensionCount=len(deviceExtensions), 288 | ppEnabledExtensionNames=deviceExtensions, 289 | enabledLayerCount=len(validationLayers), 290 | ppEnabledLayerNames=validationLayers, 291 | pEnabledFeatures=deviceFeatures 292 | ) 293 | else: 294 | createInfo = VkDeviceCreateInfo( 295 | queueCreateInfoCount=1, 296 | pQueueCreateInfos=queueCreateInfo, 297 | enabledExtensionCount=len(deviceExtensions), 298 | ppEnabledExtensionNames=deviceExtensions, 299 | enabledLayerCount=0, 300 | pEnabledFeatures=deviceFeatures 301 | ) 302 | 303 | self.__device = vkCreateDevice(self.__physicalDevice, createInfo, None) 304 | 305 | DeviceProcAddr.T = self.__device 306 | 307 | self.__graphicQueue = vkGetDeviceQueue(self.__device, self.__indices.graphicsFamily, 0) 308 | self.__presentQueue = vkGetDeviceQueue(self.__device, self.__indices.presentFamily, 0) 309 | 310 | def __createSwapChain(self): 311 | swapChainSupport = self.__querySwapChainSupport(self.__physicalDevice) 312 | 313 | surfaceFormat = self.__chooseSwapSurfaceFormat(swapChainSupport.formats) 314 | presentMode = self.__chooseSwapPresentMode(swapChainSupport.presentModes) 315 | extent = self.__chooseSwapExtent(swapChainSupport.capabilities) 316 | 317 | imageCount = swapChainSupport.capabilities.minImageCount + 1 318 | if swapChainSupport.capabilities.maxImageCount > 0 and imageCount > swapChainSupport.capabilities.maxImageCount: 319 | imageCount = swapChainSupport.capabilities.maxImageCount 320 | 321 | indices = self.__findQueueFamilies(self.__physicalDevice) 322 | queueFamily = {}.fromkeys([indices.graphicsFamily, indices.presentFamily]) 323 | queueFamilies = list(queueFamily.keys()) 324 | if len(queueFamilies) > 1: 325 | createInfo = VkSwapchainCreateInfoKHR( 326 | surface=self.__surface, 327 | minImageCount=imageCount, 328 | imageFormat=surfaceFormat.format, 329 | imageColorSpace=surfaceFormat.colorSpace, 330 | imageExtent=extent, 331 | imageArrayLayers=1, 332 | imageUsage=VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 333 | queueFamilyIndexCount=len(queueFamilies), 334 | pQueueFamilyIndices=queueFamilies, 335 | imageSharingMode=VK_SHARING_MODE_CONCURRENT, 336 | preTransform=swapChainSupport.capabilities.currentTransform, 337 | compositeAlpha=VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 338 | presentMode=presentMode, 339 | clipped=True 340 | ) 341 | else: 342 | createInfo = VkSwapchainCreateInfoKHR( 343 | surface=self.__surface, 344 | minImageCount=imageCount, 345 | imageFormat=surfaceFormat.format, 346 | imageColorSpace=surfaceFormat.colorSpace, 347 | imageExtent=extent, 348 | imageArrayLayers=1, 349 | imageUsage=VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 350 | queueFamilyIndexCount=len(queueFamilies), 351 | pQueueFamilyIndices=queueFamilies, 352 | imageSharingMode=VK_SHARING_MODE_EXCLUSIVE, 353 | preTransform=swapChainSupport.capabilities.currentTransform, 354 | compositeAlpha=VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 355 | presentMode=presentMode, 356 | clipped=True 357 | ) 358 | 359 | self.__swapChain = vkCreateSwapchainKHR(self.__device, createInfo, None) 360 | assert self.__swapChain != None 361 | 362 | self.__swapChainImages = vkGetSwapchainImagesKHR(self.__device, self.__swapChain) 363 | 364 | self.__swapChainImageFormat = surfaceFormat.format 365 | self.__swapChainExtent = extent 366 | 367 | def __createImageViews(self): 368 | self.__swapChainImageViews = [] 369 | 370 | for i, image in enumerate(self.__swapChainImages): 371 | ssr = VkImageSubresourceRange( 372 | VK_IMAGE_ASPECT_COLOR_BIT, 373 | 0, 1, 0, 1 374 | ) 375 | 376 | createInfo = VkImageViewCreateInfo( 377 | image=image, 378 | viewType=VK_IMAGE_VIEW_TYPE_2D, 379 | format=self.__swapChainImageFormat, 380 | components=[VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, 381 | VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY], 382 | subresourceRange=ssr 383 | ) 384 | 385 | self.__swapChainImageViews.append(vkCreateImageView(self.__device, createInfo, None)) 386 | 387 | def __createGraphicsPipeline(self): 388 | vertexShaderMode = self.__createShaderModule('shader/vert.spv') 389 | fragmentShaderMode = self.__createShaderModule('shader/frag.spv') 390 | 391 | vertexShaderStageInfo = VkPipelineShaderStageCreateInfo( 392 | stage=VK_SHADER_STAGE_VERTEX_BIT, 393 | module=vertexShaderMode, 394 | pName='main' 395 | ) 396 | fragmentShaderStageInfo = VkPipelineShaderStageCreateInfo( 397 | stage=VK_SHADER_STAGE_FRAGMENT_BIT, 398 | module=fragmentShaderMode, 399 | pName='main' 400 | ) 401 | 402 | shaderStageInfos = [vertexShaderStageInfo, fragmentShaderStageInfo] 403 | 404 | vertexInputInfo = VkPipelineVertexInputStateCreateInfo( 405 | vertexBindingDescriptionCount=0, 406 | vertexAttributeDescriptionCount=0 407 | ) 408 | 409 | inputAssembly = VkPipelineInputAssemblyStateCreateInfo( 410 | topology=VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 411 | primitiveRestartEnable=False 412 | ) 413 | 414 | viewport = VkViewport(0.0, 0.0, 415 | float(self.__swapChainExtent.width), 416 | float(self.__swapChainExtent.height), 417 | 0.0, 1.0) 418 | 419 | scissor = VkRect2D([0, 0], self.__swapChainExtent) 420 | viewportStage = VkPipelineViewportStateCreateInfo( 421 | viewportCount=1, 422 | pViewports=viewport, 423 | scissorCount=1, 424 | pScissors=scissor 425 | ) 426 | 427 | rasterizer = VkPipelineRasterizationStateCreateInfo( 428 | depthClampEnable=False, 429 | rasterizerDiscardEnable=False, 430 | polygonMode=VK_POLYGON_MODE_FILL, 431 | lineWidth=1.0, 432 | cullMode=VK_CULL_MODE_BACK_BIT, 433 | frontFace=VK_FRONT_FACE_CLOCKWISE, 434 | depthBiasEnable=False 435 | ) 436 | 437 | multisampling = VkPipelineMultisampleStateCreateInfo( 438 | sampleShadingEnable=False, 439 | rasterizationSamples=VK_SAMPLE_COUNT_1_BIT 440 | ) 441 | 442 | colorBlendAttachment = VkPipelineColorBlendAttachmentState( 443 | colorWriteMask=VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, 444 | blendEnable=False 445 | ) 446 | 447 | colorBending = VkPipelineColorBlendStateCreateInfo( 448 | logicOpEnable=False, 449 | logicOp=VK_LOGIC_OP_COPY, 450 | attachmentCount=1, 451 | pAttachments=colorBlendAttachment, 452 | blendConstants=[0.0, 0.0, 0.0, 0.0] 453 | ) 454 | 455 | pipelineLayoutInfo = VkPipelineLayoutCreateInfo( 456 | setLayoutCount=0, 457 | pushConstantRangeCount=0 458 | ) 459 | 460 | self.__pipelineLayout = vkCreatePipelineLayout(self.__device, pipelineLayoutInfo, None) 461 | 462 | vkDestroyShaderModule(self.__device, vertexShaderMode, None) 463 | vkDestroyShaderModule(self.__device, fragmentShaderMode, None) 464 | 465 | def __createShaderModule(self, shaderFile): 466 | with open(shaderFile, 'rb') as sf: 467 | code = sf.read() 468 | 469 | createInfo = VkShaderModuleCreateInfo( 470 | codeSize=len(code), 471 | pCode=code 472 | ) 473 | 474 | return vkCreateShaderModule(self.__device, createInfo, None) 475 | 476 | def __chooseSwapSurfaceFormat(self, formats): 477 | if len(formats) == 1 and formats[0].format == VK_FORMAT_UNDEFINED: 478 | return [VK_FORMAT_B8G8R8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR] 479 | 480 | for i in formats: 481 | if i.format == VK_FORMAT_B8G8R8_UNORM and i.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: 482 | return i 483 | 484 | return formats[0] 485 | 486 | def __chooseSwapPresentMode(self, presentModes): 487 | bestMode = VK_PRESENT_MODE_FIFO_KHR 488 | 489 | for i in presentModes: 490 | if i == VK_PRESENT_MODE_FIFO_KHR: 491 | return i 492 | elif i == VK_PRESENT_MODE_MAILBOX_KHR: 493 | return i 494 | elif i == VK_PRESENT_MODE_IMMEDIATE_KHR: 495 | return i 496 | 497 | return bestMode 498 | 499 | def __chooseSwapExtent(self, capabilities): 500 | width = max(capabilities.minImageExtent.width, min(capabilities.maxImageExtent.width, self.width())) 501 | height = max(capabilities.minImageExtent.height, min(capabilities.maxImageExtent.height, self.height())) 502 | return VkExtent2D(width, height) 503 | 504 | def __querySwapChainSupport(self, device): 505 | detail = SwapChainSupportDetails() 506 | 507 | detail.capabilities = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, self.__surface) 508 | detail.formats = vkGetPhysicalDeviceSurfaceFormatsKHR(device, self.__surface) 509 | detail.presentModes = vkGetPhysicalDeviceSurfacePresentModesKHR(device, self.__surface) 510 | return detail 511 | 512 | def __isDeviceSuitable(self, device): 513 | indices = self.__findQueueFamilies(device) 514 | 515 | extensionsSupported = self.__checkDeviceExtensionSupport(device) 516 | 517 | swapChainAdequate = False 518 | if extensionsSupported: 519 | swapChainSupport = self.__querySwapChainSupport(device) 520 | swapChainAdequate = (swapChainSupport.formats is not None) and (swapChainSupport.presentModes is not None) 521 | 522 | return indices.isComplete and extensionsSupported and swapChainAdequate 523 | 524 | def __checkDeviceExtensionSupport(self, device): 525 | availableExtensions = vkEnumerateDeviceExtensionProperties(device, None) 526 | 527 | aen = [i.extensionName for i in availableExtensions] 528 | for i in deviceExtensions: 529 | if i not in aen: 530 | return False 531 | 532 | return True 533 | 534 | def __findQueueFamilies(self, device): 535 | indices = QueueFamilyIndices() 536 | 537 | familyProperties = vkGetPhysicalDeviceQueueFamilyProperties(device) 538 | for i, prop in enumerate(familyProperties): 539 | if prop.queueCount > 0 and prop.queueFlags & VK_QUEUE_GRAPHICS_BIT: 540 | indices.graphicsFamily = i 541 | 542 | presentSupport = vkGetPhysicalDeviceSurfaceSupportKHR(device, i, self.__surface) 543 | 544 | if prop.queueCount > 0 and presentSupport: 545 | indices.presentFamily = i 546 | 547 | if indices.isComplete: 548 | break 549 | 550 | return indices 551 | 552 | def __getRequiredExtensions(self): 553 | extenstions = [e.extensionName for e in vkEnumerateInstanceExtensionProperties(None)] 554 | 555 | if enableValidationLayers: 556 | extenstions.append(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) 557 | 558 | return extenstions 559 | 560 | def __checkValidationLayerSupport(self): 561 | availableLayers = vkEnumerateInstanceLayerProperties() 562 | 563 | for layer in validationLayers: 564 | layerfound = False 565 | 566 | for layerProp in availableLayers: 567 | if layer == layerProp.layerName: 568 | layerfound = True 569 | break 570 | return layerfound 571 | 572 | return False 573 | 574 | 575 | if __name__ == '__main__': 576 | import sys 577 | 578 | app = QtGui.QGuiApplication(sys.argv) 579 | 580 | win = HelloTriangleApplication() 581 | win.show() 582 | 583 | 584 | def clenaup(): 585 | global win 586 | del win 587 | 588 | 589 | app.aboutToQuit.connect(clenaup) 590 | 591 | sys.exit(app.exec_()) 592 | -------------------------------------------------------------------------------- /11_render_passes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | import sys 4 | 5 | from vulkan import * 6 | from PySide2 import (QtGui, QtCore) 7 | 8 | validationLayers = [ 9 | 'VK_LAYER_LUNARG_standard_validation' 10 | ] 11 | 12 | deviceExtensions = [ 13 | VK_KHR_SWAPCHAIN_EXTENSION_NAME 14 | ] 15 | 16 | enableValidationLayers = True 17 | 18 | 19 | class InstanceProcAddr(object): 20 | T = None 21 | 22 | def __init__(self, func): 23 | self.__func = func 24 | 25 | def __call__(self, *args, **kwargs): 26 | funcName = self.__func.__name__ 27 | func = InstanceProcAddr.procfunc(funcName) 28 | if func: 29 | return func(*args, **kwargs) 30 | else: 31 | return VK_ERROR_EXTENSION_NOT_PRESENT 32 | 33 | @staticmethod 34 | def procfunc(funcName): 35 | return vkGetInstanceProcAddr(InstanceProcAddr.T, funcName) 36 | 37 | 38 | class DeviceProcAddr(InstanceProcAddr): 39 | 40 | @staticmethod 41 | def procfunc(funcName): 42 | return vkGetDeviceProcAddr(InstanceProcAddr.T, funcName) 43 | 44 | # instance ext functions 45 | @InstanceProcAddr 46 | def vkCreateDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 47 | pass 48 | 49 | 50 | @InstanceProcAddr 51 | def vkDestroyDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 52 | pass 53 | 54 | 55 | @InstanceProcAddr 56 | def vkCreateWin32SurfaceKHR(instance, pCreateInfo, pAllocator): 57 | pass 58 | 59 | 60 | @InstanceProcAddr 61 | def vkDestroySurfaceKHR(instance, surface, pAllocator): 62 | pass 63 | 64 | 65 | @InstanceProcAddr 66 | def vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface): 67 | pass 68 | 69 | 70 | @InstanceProcAddr 71 | def vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface): 72 | pass 73 | 74 | 75 | @InstanceProcAddr 76 | def vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface): 77 | pass 78 | 79 | 80 | @InstanceProcAddr 81 | def vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface): 82 | pass 83 | 84 | 85 | # device ext functions 86 | @DeviceProcAddr 87 | def vkCreateSwapchainKHR(device, pCreateInfo, pAllocator): 88 | pass 89 | 90 | @DeviceProcAddr 91 | def vkDestroySwapchainKHR(device, swapchain, pAllocator): 92 | pass 93 | 94 | @DeviceProcAddr 95 | def vkGetSwapchainImagesKHR(device, swapchain): 96 | pass 97 | 98 | 99 | 100 | 101 | def debugCallback(*args): 102 | print('DEBUG: {} {}'.format(args[5], args[6])) 103 | return 0 104 | 105 | 106 | class Win32misc(object): 107 | @staticmethod 108 | def getInstance(hWnd): 109 | from cffi import FFI as _FFI 110 | _ffi = _FFI() 111 | _ffi.cdef('long __stdcall GetWindowLongA(void* hWnd, int nIndex);') 112 | _lib = _ffi.dlopen('User32.dll') 113 | return _lib.GetWindowLongA(_ffi.cast('void*', hWnd), -6) # GWL_HINSTANCE 114 | 115 | 116 | class QueueFamilyIndices(object): 117 | 118 | def __init__(self): 119 | self.graphicsFamily = -1 120 | self.presentFamily = -1 121 | 122 | @property 123 | def isComplete(self): 124 | return self.graphicsFamily >= 0 and self.presentFamily >= 0 125 | 126 | 127 | class SwapChainSupportDetails(object): 128 | 129 | def __init__(self): 130 | self.capabilities = None 131 | self.formats = None 132 | self.presentModes = None 133 | 134 | 135 | class HelloTriangleApplication(QtGui.QWindow): 136 | 137 | def __init__(self): 138 | super(HelloTriangleApplication, self).__init__() 139 | 140 | self.setWidth(1280) 141 | self.setHeight(720) 142 | 143 | self.setTitle("Vulkan Python - PySide2") 144 | 145 | # self.setSurfaceType(self.OpenGLSurface) 146 | 147 | self.__instance = None 148 | self.__callbcak = None 149 | self.__surface = None 150 | 151 | self.__physicalDevice = None 152 | self.__device = None 153 | self.__graphicQueue = None 154 | self.__presentQueue = None 155 | 156 | self.__swapChain = None 157 | self.__swapChainImages = [] 158 | self.__swapChainImageFormat = None 159 | self.__swapChainExtent = None 160 | self.__swapChainImageViews = [] 161 | 162 | self.__renderpass = None 163 | self.__pipelineLayout = None 164 | 165 | self.__indices = QueueFamilyIndices() 166 | 167 | self.initVulkan() 168 | 169 | def __del__(self): 170 | if self.__renderpass: 171 | vkDestroyRenderPass(self.__device, self.__renderpass, None) 172 | 173 | if self.__pipelineLayout: 174 | vkDestroyPipelineLayout(self.__device, self.__pipelineLayout, None) 175 | 176 | if self.__swapChainImageViews: 177 | [vkDestroyImageView(self.__device, imv, None) for imv in self.__swapChainImageViews] 178 | 179 | if self.__swapChain: 180 | vkDestroySwapchainKHR(self.__device, self.__swapChain, None) 181 | 182 | if self.__device: 183 | vkDestroyDevice(self.__device, None) 184 | 185 | if self.__callbcak: 186 | vkDestroyDebugReportCallbackEXT(self.__instance, self.__callbcak, None) 187 | 188 | if self.__surface: 189 | vkDestroySurfaceKHR(self.__instance, self.__surface, None) 190 | 191 | if self.__instance: 192 | vkDestroyInstance(self.__instance, None) 193 | print('instance destroyed') 194 | 195 | def initVulkan(self): 196 | self.__cretaeInstance() 197 | self.__setupDebugCallback() 198 | self.__createSurface() 199 | self.__pickPhysicalDevice() 200 | self.__createLogicalDevice() 201 | self.__createSwapChain() 202 | self.__createImageViews() 203 | self.__createRenderPass() 204 | self.__createGraphicsPipeline() 205 | 206 | def __cretaeInstance(self): 207 | if enableValidationLayers and not self.__checkValidationLayerSupport(): 208 | raise Exception("validation layers requested, but not available!") 209 | 210 | appInfo = VkApplicationInfo( 211 | # sType=VK_STRUCTURE_TYPE_APPLICATION_INFO, 212 | pApplicationName='Python VK', 213 | applicationVersion=VK_MAKE_VERSION(1, 0, 0), 214 | pEngineName='pyvulkan', 215 | engineVersion=VK_MAKE_VERSION(1, 0, 0), 216 | apiVersion=VK_API_VERSION 217 | ) 218 | 219 | extenstions = self.__getRequiredExtensions() 220 | if enableValidationLayers: 221 | instanceInfo = VkInstanceCreateInfo( 222 | pApplicationInfo=appInfo, 223 | # enabledLayerCount=len(validationLayers), 224 | ppEnabledLayerNames=validationLayers, 225 | # enabledExtensionCount=len(extenstions), 226 | ppEnabledExtensionNames=extenstions 227 | ) 228 | else: 229 | instanceInfo = VkInstanceCreateInfo( 230 | pApplicationInfo=appInfo, 231 | enabledLayerCount=0, 232 | # enabledExtensionCount=len(extenstions), 233 | ppEnabledExtensionNames=extenstions 234 | ) 235 | 236 | self.__instance = vkCreateInstance(instanceInfo, None) 237 | 238 | InstanceProcAddr.T = self.__instance 239 | 240 | def __setupDebugCallback(self): 241 | if not enableValidationLayers: 242 | return 243 | 244 | createInfo = VkDebugReportCallbackCreateInfoEXT( 245 | flags=VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT, 246 | pfnCallback=debugCallback 247 | ) 248 | 249 | self.__callbcak = vkCreateDebugReportCallbackEXT(self.__instance, createInfo, None) 250 | 251 | def __createSurface(self): 252 | if sys.platform == 'win32': 253 | hwnd = self.winId() 254 | hinstance = Win32misc.getInstance(hwnd) 255 | createInfo = VkWin32SurfaceCreateInfoKHR( 256 | hinstance=hinstance, 257 | hwnd=hwnd 258 | ) 259 | 260 | self.__surface = vkCreateWin32SurfaceKHR(self.__instance, createInfo, None) 261 | # elif sys.platform == 'linux': 262 | # pass 263 | 264 | def __pickPhysicalDevice(self): 265 | physicalDevices = vkEnumeratePhysicalDevices(self.__instance) 266 | 267 | for device in physicalDevices: 268 | if self.__isDeviceSuitable(device): 269 | self.__physicalDevice = device 270 | break 271 | 272 | assert self.__physicalDevice != None 273 | 274 | def __createLogicalDevice(self): 275 | self.__indices = self.__findQueueFamilies(self.__physicalDevice) 276 | 277 | uniqueQueueFamilies = {}.fromkeys([self.__indices.graphicsFamily, self.__indices.presentFamily]) 278 | queueCreateInfos = [] 279 | for i in uniqueQueueFamilies: 280 | queueCreateInfo = VkDeviceQueueCreateInfo( 281 | queueFamilyIndex=i, 282 | queueCount=1, 283 | pQueuePriorities=[1.0] 284 | ) 285 | queueCreateInfos.append(queueCreateInfo) 286 | 287 | deviceFeatures = VkPhysicalDeviceFeatures() 288 | if enableValidationLayers: 289 | createInfo = VkDeviceCreateInfo( 290 | # queueCreateInfoCount=len(queueCreateInfos), 291 | pQueueCreateInfos=queueCreateInfos, 292 | # enabledExtensionCount=len(deviceExtensions), 293 | ppEnabledExtensionNames=deviceExtensions, 294 | # enabledLayerCount=len(validationLayers), 295 | ppEnabledLayerNames=validationLayers, 296 | pEnabledFeatures=deviceFeatures 297 | ) 298 | else: 299 | createInfo = VkDeviceCreateInfo( 300 | queueCreateInfoCount=1, 301 | pQueueCreateInfos=queueCreateInfo, 302 | # enabledExtensionCount=len(deviceExtensions), 303 | ppEnabledExtensionNames=deviceExtensions, 304 | enabledLayerCount=0, 305 | pEnabledFeatures=deviceFeatures 306 | ) 307 | 308 | self.__device = vkCreateDevice(self.__physicalDevice, createInfo, None) 309 | 310 | DeviceProcAddr.T = self.__device 311 | 312 | self.__graphicQueue = vkGetDeviceQueue(self.__device, self.__indices.graphicsFamily, 0) 313 | self.__presentQueue = vkGetDeviceQueue(self.__device, self.__indices.presentFamily, 0) 314 | 315 | def __createSwapChain(self): 316 | swapChainSupport = self.__querySwapChainSupport(self.__physicalDevice) 317 | 318 | surfaceFormat = self.__chooseSwapSurfaceFormat(swapChainSupport.formats) 319 | presentMode = self.__chooseSwapPresentMode(swapChainSupport.presentModes) 320 | extent = self.__chooseSwapExtent(swapChainSupport.capabilities) 321 | 322 | imageCount = swapChainSupport.capabilities.minImageCount + 1 323 | if swapChainSupport.capabilities.maxImageCount > 0 and imageCount > swapChainSupport.capabilities.maxImageCount: 324 | imageCount = swapChainSupport.capabilities.maxImageCount 325 | 326 | indices = self.__findQueueFamilies(self.__physicalDevice) 327 | queueFamily = {}.fromkeys([indices.graphicsFamily, indices.presentFamily]) 328 | queueFamilies = list(queueFamily.keys()) 329 | if len(queueFamilies) > 1: 330 | createInfo = VkSwapchainCreateInfoKHR( 331 | surface=self.__surface, 332 | minImageCount=imageCount, 333 | imageFormat=surfaceFormat.format, 334 | imageColorSpace=surfaceFormat.colorSpace, 335 | imageExtent=extent, 336 | imageArrayLayers=1, 337 | imageUsage=VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 338 | # queueFamilyIndexCount=len(queueFamilies), 339 | pQueueFamilyIndices=queueFamilies, 340 | imageSharingMode=VK_SHARING_MODE_CONCURRENT, 341 | preTransform=swapChainSupport.capabilities.currentTransform, 342 | compositeAlpha=VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 343 | presentMode=presentMode, 344 | clipped=True 345 | ) 346 | else: 347 | createInfo = VkSwapchainCreateInfoKHR( 348 | surface=self.__surface, 349 | minImageCount=imageCount, 350 | imageFormat=surfaceFormat.format, 351 | imageColorSpace=surfaceFormat.colorSpace, 352 | imageExtent=extent, 353 | imageArrayLayers=1, 354 | imageUsage=VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 355 | # queueFamilyIndexCount=len(queueFamilies), 356 | pQueueFamilyIndices=queueFamilies, 357 | imageSharingMode=VK_SHARING_MODE_EXCLUSIVE, 358 | preTransform=swapChainSupport.capabilities.currentTransform, 359 | compositeAlpha=VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 360 | presentMode=presentMode, 361 | clipped=True 362 | ) 363 | 364 | self.__swapChain = vkCreateSwapchainKHR(self.__device, createInfo, None) 365 | assert self.__swapChain != None 366 | 367 | self.__swapChainImages = vkGetSwapchainImagesKHR(self.__device, self.__swapChain) 368 | 369 | self.__swapChainImageFormat = surfaceFormat.format 370 | self.__swapChainExtent = extent 371 | 372 | def __createImageViews(self): 373 | self.__swapChainImageViews = [] 374 | 375 | for i, image in enumerate(self.__swapChainImages): 376 | ssr = VkImageSubresourceRange( 377 | VK_IMAGE_ASPECT_COLOR_BIT, 378 | 0, 1, 0, 1 379 | ) 380 | 381 | createInfo = VkImageViewCreateInfo( 382 | image=image, 383 | viewType=VK_IMAGE_VIEW_TYPE_2D, 384 | format=self.__swapChainImageFormat, 385 | components=[VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, 386 | VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY], 387 | subresourceRange=ssr 388 | ) 389 | 390 | self.__swapChainImageViews.append(vkCreateImageView(self.__device, createInfo, None)) 391 | 392 | def __createRenderPass(self): 393 | colorAttachment = VkAttachmentDescription( 394 | format=self.__swapChainImageFormat, 395 | samples=VK_SAMPLE_COUNT_1_BIT, 396 | loadOp=VK_ATTACHMENT_LOAD_OP_CLEAR, 397 | storeOp=VK_ATTACHMENT_STORE_OP_STORE, 398 | stencilLoadOp=VK_ATTACHMENT_LOAD_OP_DONT_CARE, 399 | stencilStoreOp=VK_ATTACHMENT_STORE_OP_DONT_CARE, 400 | initialLayout=VK_IMAGE_LAYOUT_UNDEFINED, 401 | finalLayout=VK_IMAGE_LAYOUT_PRESENT_SRC_KHR 402 | ) 403 | 404 | colorAttachmentRef = VkAttachmentReference( 405 | 0, 406 | VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL 407 | ) 408 | 409 | subpass = VkSubpassDescription( 410 | pipelineBindPoint=VK_PIPELINE_BIND_POINT_GRAPHICS, 411 | pColorAttachments=[colorAttachmentRef] 412 | ) 413 | 414 | renderPassInfo = VkRenderPassCreateInfo( 415 | pAttachments=[colorAttachment], 416 | pSubpasses=[subpass] 417 | ) 418 | 419 | self.__renderpass = vkCreateRenderPass(self.__device, renderPassInfo, None) 420 | 421 | def __createGraphicsPipeline(self): 422 | vertexShaderMode = self.__createShaderModule('shader/vert.spv') 423 | fragmentShaderMode = self.__createShaderModule('shader/frag.spv') 424 | 425 | vertexShaderStageInfo = VkPipelineShaderStageCreateInfo( 426 | stage=VK_SHADER_STAGE_VERTEX_BIT, 427 | module=vertexShaderMode, 428 | pName='main' 429 | ) 430 | fragmentShaderStageInfo = VkPipelineShaderStageCreateInfo( 431 | stage=VK_SHADER_STAGE_FRAGMENT_BIT, 432 | module=fragmentShaderMode, 433 | pName='main' 434 | ) 435 | 436 | shaderStageInfos = [vertexShaderStageInfo, fragmentShaderStageInfo] 437 | 438 | vertexInputInfo = VkPipelineVertexInputStateCreateInfo( 439 | vertexBindingDescriptionCount=0, 440 | vertexAttributeDescriptionCount=0 441 | ) 442 | 443 | inputAssembly = VkPipelineInputAssemblyStateCreateInfo( 444 | topology=VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 445 | primitiveRestartEnable=False 446 | ) 447 | 448 | viewport = VkViewport(0.0, 0.0, 449 | float(self.__swapChainExtent.width), 450 | float(self.__swapChainExtent.height), 451 | 0.0, 1.0) 452 | 453 | scissor = VkRect2D([0, 0], self.__swapChainExtent) 454 | viewportStage = VkPipelineViewportStateCreateInfo( 455 | viewportCount=1, 456 | pViewports=viewport, 457 | scissorCount=1, 458 | pScissors=scissor 459 | ) 460 | 461 | rasterizer = VkPipelineRasterizationStateCreateInfo( 462 | depthClampEnable=False, 463 | rasterizerDiscardEnable=False, 464 | polygonMode=VK_POLYGON_MODE_FILL, 465 | lineWidth=1.0, 466 | cullMode=VK_CULL_MODE_BACK_BIT, 467 | frontFace=VK_FRONT_FACE_CLOCKWISE, 468 | depthBiasEnable=False 469 | ) 470 | 471 | multisampling = VkPipelineMultisampleStateCreateInfo( 472 | sampleShadingEnable=False, 473 | rasterizationSamples=VK_SAMPLE_COUNT_1_BIT 474 | ) 475 | 476 | colorBlendAttachment = VkPipelineColorBlendAttachmentState( 477 | colorWriteMask=VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, 478 | blendEnable=False 479 | ) 480 | 481 | colorBending = VkPipelineColorBlendStateCreateInfo( 482 | logicOpEnable=False, 483 | logicOp=VK_LOGIC_OP_COPY, 484 | attachmentCount=1, 485 | pAttachments=colorBlendAttachment, 486 | blendConstants=[0.0, 0.0, 0.0, 0.0] 487 | ) 488 | 489 | pipelineLayoutInfo = VkPipelineLayoutCreateInfo( 490 | setLayoutCount=0, 491 | pushConstantRangeCount=0 492 | ) 493 | 494 | self.__pipelineLayout = vkCreatePipelineLayout(self.__device, pipelineLayoutInfo, None) 495 | 496 | vkDestroyShaderModule(self.__device, vertexShaderMode, None) 497 | vkDestroyShaderModule(self.__device, fragmentShaderMode, None) 498 | 499 | def __createShaderModule(self, shaderFile): 500 | with open(shaderFile, 'rb') as sf: 501 | code = sf.read() 502 | 503 | createInfo = VkShaderModuleCreateInfo( 504 | codeSize=len(code), 505 | pCode=code 506 | ) 507 | 508 | return vkCreateShaderModule(self.__device, createInfo, None) 509 | 510 | def __chooseSwapSurfaceFormat(self, formats): 511 | if len(formats) == 1 and formats[0].format == VK_FORMAT_UNDEFINED: 512 | return [VK_FORMAT_B8G8R8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR] 513 | 514 | for i in formats: 515 | if i.format == VK_FORMAT_B8G8R8_UNORM and i.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: 516 | return i 517 | 518 | return formats[0] 519 | 520 | def __chooseSwapPresentMode(self, presentModes): 521 | bestMode = VK_PRESENT_MODE_FIFO_KHR 522 | 523 | for i in presentModes: 524 | if i == VK_PRESENT_MODE_FIFO_KHR: 525 | return i 526 | elif i == VK_PRESENT_MODE_MAILBOX_KHR: 527 | return i 528 | elif i == VK_PRESENT_MODE_IMMEDIATE_KHR: 529 | return i 530 | 531 | return bestMode 532 | 533 | def __chooseSwapExtent(self, capabilities): 534 | width = max(capabilities.minImageExtent.width, min(capabilities.maxImageExtent.width, self.width())) 535 | height = max(capabilities.minImageExtent.height, min(capabilities.maxImageExtent.height, self.height())) 536 | return VkExtent2D(width, height) 537 | 538 | def __querySwapChainSupport(self, device): 539 | detail = SwapChainSupportDetails() 540 | 541 | detail.capabilities = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, self.__surface) 542 | detail.formats = vkGetPhysicalDeviceSurfaceFormatsKHR(device, self.__surface) 543 | detail.presentModes = vkGetPhysicalDeviceSurfacePresentModesKHR(device, self.__surface) 544 | return detail 545 | 546 | def __isDeviceSuitable(self, device): 547 | indices = self.__findQueueFamilies(device) 548 | 549 | extensionsSupported = self.__checkDeviceExtensionSupport(device) 550 | 551 | swapChainAdequate = False 552 | if extensionsSupported: 553 | swapChainSupport = self.__querySwapChainSupport(device) 554 | swapChainAdequate = (swapChainSupport.formats is not None) and (swapChainSupport.presentModes is not None) 555 | 556 | return indices.isComplete and extensionsSupported and swapChainAdequate 557 | 558 | def __checkDeviceExtensionSupport(self, device): 559 | availableExtensions = vkEnumerateDeviceExtensionProperties(device, None) 560 | 561 | aen = [i.extensionName for i in availableExtensions] 562 | for i in deviceExtensions: 563 | if i not in aen: 564 | return False 565 | 566 | return True 567 | 568 | def __findQueueFamilies(self, device): 569 | indices = QueueFamilyIndices() 570 | 571 | familyProperties = vkGetPhysicalDeviceQueueFamilyProperties(device) 572 | for i, prop in enumerate(familyProperties): 573 | if prop.queueCount > 0 and prop.queueFlags & VK_QUEUE_GRAPHICS_BIT: 574 | indices.graphicsFamily = i 575 | 576 | presentSupport = vkGetPhysicalDeviceSurfaceSupportKHR(device, i, self.__surface) 577 | 578 | if prop.queueCount > 0 and presentSupport: 579 | indices.presentFamily = i 580 | 581 | if indices.isComplete: 582 | break 583 | 584 | return indices 585 | 586 | def __getRequiredExtensions(self): 587 | extenstions = [e.extensionName for e in vkEnumerateInstanceExtensionProperties(None)] 588 | 589 | if enableValidationLayers: 590 | extenstions.append(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) 591 | 592 | return extenstions 593 | 594 | def __checkValidationLayerSupport(self): 595 | availableLayers = vkEnumerateInstanceLayerProperties() 596 | 597 | for layer in validationLayers: 598 | layerfound = False 599 | 600 | for layerProp in availableLayers: 601 | if layer == layerProp.layerName: 602 | layerfound = True 603 | break 604 | return layerfound 605 | 606 | return False 607 | 608 | 609 | if __name__ == '__main__': 610 | import sys 611 | 612 | app = QtGui.QGuiApplication(sys.argv) 613 | 614 | win = HelloTriangleApplication() 615 | win.show() 616 | 617 | 618 | def clenaup(): 619 | global win 620 | del win 621 | 622 | 623 | app.aboutToQuit.connect(clenaup) 624 | 625 | sys.exit(app.exec_()) 626 | -------------------------------------------------------------------------------- /12_graphics_pipeline_complete.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | import sys 4 | 5 | from vulkan import * 6 | from PySide2 import (QtGui, QtCore) 7 | 8 | validationLayers = [ 9 | 'VK_LAYER_LUNARG_standard_validation' 10 | ] 11 | 12 | deviceExtensions = [ 13 | VK_KHR_SWAPCHAIN_EXTENSION_NAME 14 | ] 15 | 16 | enableValidationLayers = True 17 | 18 | 19 | class InstanceProcAddr(object): 20 | T = None 21 | 22 | def __init__(self, func): 23 | self.__func = func 24 | 25 | def __call__(self, *args, **kwargs): 26 | funcName = self.__func.__name__ 27 | func = InstanceProcAddr.procfunc(funcName) 28 | if func: 29 | return func(*args, **kwargs) 30 | else: 31 | return VK_ERROR_EXTENSION_NOT_PRESENT 32 | 33 | @staticmethod 34 | def procfunc(funcName): 35 | return vkGetInstanceProcAddr(InstanceProcAddr.T, funcName) 36 | 37 | 38 | class DeviceProcAddr(InstanceProcAddr): 39 | 40 | @staticmethod 41 | def procfunc(funcName): 42 | return vkGetDeviceProcAddr(InstanceProcAddr.T, funcName) 43 | 44 | # instance ext functions 45 | @InstanceProcAddr 46 | def vkCreateDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 47 | pass 48 | 49 | 50 | @InstanceProcAddr 51 | def vkDestroyDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 52 | pass 53 | 54 | 55 | @InstanceProcAddr 56 | def vkCreateWin32SurfaceKHR(instance, pCreateInfo, pAllocator): 57 | pass 58 | 59 | 60 | @InstanceProcAddr 61 | def vkDestroySurfaceKHR(instance, surface, pAllocator): 62 | pass 63 | 64 | 65 | @InstanceProcAddr 66 | def vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface): 67 | pass 68 | 69 | 70 | @InstanceProcAddr 71 | def vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface): 72 | pass 73 | 74 | 75 | @InstanceProcAddr 76 | def vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface): 77 | pass 78 | 79 | 80 | @InstanceProcAddr 81 | def vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface): 82 | pass 83 | 84 | 85 | # device ext functions 86 | @DeviceProcAddr 87 | def vkCreateSwapchainKHR(device, pCreateInfo, pAllocator): 88 | pass 89 | 90 | @DeviceProcAddr 91 | def vkDestroySwapchainKHR(device, swapchain, pAllocator): 92 | pass 93 | 94 | @DeviceProcAddr 95 | def vkGetSwapchainImagesKHR(device, swapchain): 96 | pass 97 | 98 | 99 | 100 | 101 | def debugCallback(*args): 102 | print('DEBUG: {} {}'.format(args[5], args[6])) 103 | return 0 104 | 105 | 106 | class Win32misc(object): 107 | @staticmethod 108 | def getInstance(hWnd): 109 | from cffi import FFI as _FFI 110 | _ffi = _FFI() 111 | _ffi.cdef('long __stdcall GetWindowLongA(void* hWnd, int nIndex);') 112 | _lib = _ffi.dlopen('User32.dll') 113 | return _lib.GetWindowLongA(_ffi.cast('void*', hWnd), -6) # GWL_HINSTANCE 114 | 115 | 116 | class QueueFamilyIndices(object): 117 | 118 | def __init__(self): 119 | self.graphicsFamily = -1 120 | self.presentFamily = -1 121 | 122 | @property 123 | def isComplete(self): 124 | return self.graphicsFamily >= 0 and self.presentFamily >= 0 125 | 126 | 127 | class SwapChainSupportDetails(object): 128 | 129 | def __init__(self): 130 | self.capabilities = None 131 | self.formats = None 132 | self.presentModes = None 133 | 134 | 135 | class HelloTriangleApplication(QtGui.QWindow): 136 | 137 | def __init__(self): 138 | super(HelloTriangleApplication, self).__init__() 139 | 140 | self.setWidth(1280) 141 | self.setHeight(720) 142 | 143 | self.setTitle("Vulkan Python - PySide2") 144 | 145 | # self.setSurfaceType(self.OpenGLSurface) 146 | 147 | self.__instance = None 148 | self.__callbcak = None 149 | self.__surface = None 150 | 151 | self.__physicalDevice = None 152 | self.__device = None 153 | self.__graphicQueue = None 154 | self.__presentQueue = None 155 | 156 | self.__swapChain = None 157 | self.__swapChainImages = [] 158 | self.__swapChainImageFormat = None 159 | self.__swapChainExtent = None 160 | self.__swapChainImageViews = [] 161 | 162 | self.__renderpass = None 163 | self.__pipeline = None 164 | self.__pipelineLayout = None 165 | 166 | self.__indices = QueueFamilyIndices() 167 | 168 | self.initVulkan() 169 | 170 | def __del__(self): 171 | if self.__renderpass: 172 | vkDestroyRenderPass(self.__device, self.__renderpass, None) 173 | 174 | if self.__pipelineLayout: 175 | vkDestroyPipelineLayout(self.__device, self.__pipelineLayout, None) 176 | 177 | if self.__pipeline: 178 | vkDestroyPipeline(self.__device, self.__pipeline, None) 179 | 180 | if self.__swapChainImageViews: 181 | [vkDestroyImageView(self.__device, imv, None) for imv in self.__swapChainImageViews] 182 | 183 | if self.__swapChain: 184 | vkDestroySwapchainKHR(self.__device, self.__swapChain, None) 185 | 186 | if self.__device: 187 | vkDestroyDevice(self.__device, None) 188 | 189 | if self.__callbcak: 190 | vkDestroyDebugReportCallbackEXT(self.__instance, self.__callbcak, None) 191 | 192 | if self.__surface: 193 | vkDestroySurfaceKHR(self.__instance, self.__surface, None) 194 | 195 | if self.__instance: 196 | vkDestroyInstance(self.__instance, None) 197 | print('instance destroyed') 198 | 199 | def initVulkan(self): 200 | self.__cretaeInstance() 201 | self.__setupDebugCallback() 202 | self.__createSurface() 203 | self.__pickPhysicalDevice() 204 | self.__createLogicalDevice() 205 | self.__createSwapChain() 206 | self.__createImageViews() 207 | self.__createRenderPass() 208 | self.__createGraphicsPipeline() 209 | 210 | def __cretaeInstance(self): 211 | if enableValidationLayers and not self.__checkValidationLayerSupport(): 212 | raise Exception("validation layers requested, but not available!") 213 | 214 | appInfo = VkApplicationInfo( 215 | # sType=VK_STRUCTURE_TYPE_APPLICATION_INFO, 216 | pApplicationName='Python VK', 217 | applicationVersion=VK_MAKE_VERSION(1, 0, 0), 218 | pEngineName='pyvulkan', 219 | engineVersion=VK_MAKE_VERSION(1, 0, 0), 220 | apiVersion=VK_API_VERSION 221 | ) 222 | 223 | extenstions = self.__getRequiredExtensions() 224 | if enableValidationLayers: 225 | instanceInfo = VkInstanceCreateInfo( 226 | pApplicationInfo=appInfo, 227 | # enabledLayerCount=len(validationLayers), 228 | ppEnabledLayerNames=validationLayers, 229 | # enabledExtensionCount=len(extenstions), 230 | ppEnabledExtensionNames=extenstions 231 | ) 232 | else: 233 | instanceInfo = VkInstanceCreateInfo( 234 | pApplicationInfo=appInfo, 235 | enabledLayerCount=0, 236 | # enabledExtensionCount=len(extenstions), 237 | ppEnabledExtensionNames=extenstions 238 | ) 239 | 240 | self.__instance = vkCreateInstance(instanceInfo, None) 241 | 242 | InstanceProcAddr.T = self.__instance 243 | 244 | def __setupDebugCallback(self): 245 | if not enableValidationLayers: 246 | return 247 | 248 | createInfo = VkDebugReportCallbackCreateInfoEXT( 249 | flags=VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT, 250 | pfnCallback=debugCallback 251 | ) 252 | 253 | self.__callbcak = vkCreateDebugReportCallbackEXT(self.__instance, createInfo, None) 254 | 255 | def __createSurface(self): 256 | if sys.platform == 'win32': 257 | hwnd = self.winId() 258 | hinstance = Win32misc.getInstance(hwnd) 259 | createInfo = VkWin32SurfaceCreateInfoKHR( 260 | hinstance=hinstance, 261 | hwnd=hwnd 262 | ) 263 | 264 | self.__surface = vkCreateWin32SurfaceKHR(self.__instance, createInfo, None) 265 | # elif sys.platform == 'linux': 266 | # pass 267 | 268 | def __pickPhysicalDevice(self): 269 | physicalDevices = vkEnumeratePhysicalDevices(self.__instance) 270 | 271 | for device in physicalDevices: 272 | if self.__isDeviceSuitable(device): 273 | self.__physicalDevice = device 274 | break 275 | 276 | assert self.__physicalDevice != None 277 | 278 | def __createLogicalDevice(self): 279 | self.__indices = self.__findQueueFamilies(self.__physicalDevice) 280 | 281 | uniqueQueueFamilies = {}.fromkeys([self.__indices.graphicsFamily, self.__indices.presentFamily]) 282 | queueCreateInfos = [] 283 | for i in uniqueQueueFamilies: 284 | queueCreateInfo = VkDeviceQueueCreateInfo( 285 | queueFamilyIndex=i, 286 | queueCount=1, 287 | pQueuePriorities=[1.0] 288 | ) 289 | queueCreateInfos.append(queueCreateInfo) 290 | 291 | deviceFeatures = VkPhysicalDeviceFeatures() 292 | if enableValidationLayers: 293 | createInfo = VkDeviceCreateInfo( 294 | # queueCreateInfoCount=len(queueCreateInfos), 295 | pQueueCreateInfos=queueCreateInfos, 296 | # enabledExtensionCount=len(deviceExtensions), 297 | ppEnabledExtensionNames=deviceExtensions, 298 | # enabledLayerCount=len(validationLayers), 299 | ppEnabledLayerNames=validationLayers, 300 | pEnabledFeatures=deviceFeatures 301 | ) 302 | else: 303 | createInfo = VkDeviceCreateInfo( 304 | queueCreateInfoCount=1, 305 | pQueueCreateInfos=queueCreateInfo, 306 | # enabledExtensionCount=len(deviceExtensions), 307 | ppEnabledExtensionNames=deviceExtensions, 308 | enabledLayerCount=0, 309 | pEnabledFeatures=deviceFeatures 310 | ) 311 | 312 | self.__device = vkCreateDevice(self.__physicalDevice, createInfo, None) 313 | 314 | DeviceProcAddr.T = self.__device 315 | 316 | self.__graphicQueue = vkGetDeviceQueue(self.__device, self.__indices.graphicsFamily, 0) 317 | self.__presentQueue = vkGetDeviceQueue(self.__device, self.__indices.presentFamily, 0) 318 | 319 | def __createSwapChain(self): 320 | swapChainSupport = self.__querySwapChainSupport(self.__physicalDevice) 321 | 322 | surfaceFormat = self.__chooseSwapSurfaceFormat(swapChainSupport.formats) 323 | presentMode = self.__chooseSwapPresentMode(swapChainSupport.presentModes) 324 | extent = self.__chooseSwapExtent(swapChainSupport.capabilities) 325 | 326 | imageCount = swapChainSupport.capabilities.minImageCount + 1 327 | if swapChainSupport.capabilities.maxImageCount > 0 and imageCount > swapChainSupport.capabilities.maxImageCount: 328 | imageCount = swapChainSupport.capabilities.maxImageCount 329 | 330 | indices = self.__findQueueFamilies(self.__physicalDevice) 331 | queueFamily = {}.fromkeys([indices.graphicsFamily, indices.presentFamily]) 332 | queueFamilies = list(queueFamily.keys()) 333 | if len(queueFamilies) > 1: 334 | createInfo = VkSwapchainCreateInfoKHR( 335 | surface=self.__surface, 336 | minImageCount=imageCount, 337 | imageFormat=surfaceFormat.format, 338 | imageColorSpace=surfaceFormat.colorSpace, 339 | imageExtent=extent, 340 | imageArrayLayers=1, 341 | imageUsage=VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 342 | # queueFamilyIndexCount=len(queueFamilies), 343 | pQueueFamilyIndices=queueFamilies, 344 | imageSharingMode=VK_SHARING_MODE_CONCURRENT, 345 | preTransform=swapChainSupport.capabilities.currentTransform, 346 | compositeAlpha=VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 347 | presentMode=presentMode, 348 | clipped=True 349 | ) 350 | else: 351 | createInfo = VkSwapchainCreateInfoKHR( 352 | surface=self.__surface, 353 | minImageCount=imageCount, 354 | imageFormat=surfaceFormat.format, 355 | imageColorSpace=surfaceFormat.colorSpace, 356 | imageExtent=extent, 357 | imageArrayLayers=1, 358 | imageUsage=VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 359 | # queueFamilyIndexCount=len(queueFamilies), 360 | pQueueFamilyIndices=queueFamilies, 361 | imageSharingMode=VK_SHARING_MODE_EXCLUSIVE, 362 | preTransform=swapChainSupport.capabilities.currentTransform, 363 | compositeAlpha=VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 364 | presentMode=presentMode, 365 | clipped=True 366 | ) 367 | 368 | self.__swapChain = vkCreateSwapchainKHR(self.__device, createInfo, None) 369 | assert self.__swapChain != None 370 | 371 | self.__swapChainImages = vkGetSwapchainImagesKHR(self.__device, self.__swapChain) 372 | 373 | self.__swapChainImageFormat = surfaceFormat.format 374 | self.__swapChainExtent = extent 375 | 376 | def __createImageViews(self): 377 | self.__swapChainImageViews = [] 378 | 379 | for i, image in enumerate(self.__swapChainImages): 380 | ssr = VkImageSubresourceRange( 381 | VK_IMAGE_ASPECT_COLOR_BIT, 382 | 0, 1, 0, 1 383 | ) 384 | 385 | createInfo = VkImageViewCreateInfo( 386 | image=image, 387 | viewType=VK_IMAGE_VIEW_TYPE_2D, 388 | format=self.__swapChainImageFormat, 389 | components=[VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, 390 | VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY], 391 | subresourceRange=ssr 392 | ) 393 | 394 | self.__swapChainImageViews.append(vkCreateImageView(self.__device, createInfo, None)) 395 | 396 | def __createRenderPass(self): 397 | colorAttachment = VkAttachmentDescription( 398 | format=self.__swapChainImageFormat, 399 | samples=VK_SAMPLE_COUNT_1_BIT, 400 | loadOp=VK_ATTACHMENT_LOAD_OP_CLEAR, 401 | storeOp=VK_ATTACHMENT_STORE_OP_STORE, 402 | stencilLoadOp=VK_ATTACHMENT_LOAD_OP_DONT_CARE, 403 | stencilStoreOp=VK_ATTACHMENT_STORE_OP_DONT_CARE, 404 | initialLayout=VK_IMAGE_LAYOUT_UNDEFINED, 405 | finalLayout=VK_IMAGE_LAYOUT_PRESENT_SRC_KHR 406 | ) 407 | 408 | colorAttachmentRef = VkAttachmentReference( 409 | 0, 410 | VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL 411 | ) 412 | 413 | subpass = VkSubpassDescription( 414 | pipelineBindPoint=VK_PIPELINE_BIND_POINT_GRAPHICS, 415 | pColorAttachments=[colorAttachmentRef] 416 | ) 417 | 418 | renderPassInfo = VkRenderPassCreateInfo( 419 | pAttachments=[colorAttachment], 420 | pSubpasses=[subpass] 421 | ) 422 | 423 | self.__renderpass = vkCreateRenderPass(self.__device, renderPassInfo, None) 424 | 425 | def __createGraphicsPipeline(self): 426 | vertexShaderMode = self.__createShaderModule('shader/vert.spv') 427 | fragmentShaderMode = self.__createShaderModule('shader/frag.spv') 428 | 429 | vertexShaderStageInfo = VkPipelineShaderStageCreateInfo( 430 | stage=VK_SHADER_STAGE_VERTEX_BIT, 431 | module=vertexShaderMode, 432 | pName='main' 433 | ) 434 | fragmentShaderStageInfo = VkPipelineShaderStageCreateInfo( 435 | stage=VK_SHADER_STAGE_FRAGMENT_BIT, 436 | module=fragmentShaderMode, 437 | pName='main' 438 | ) 439 | 440 | shaderStageInfos = [vertexShaderStageInfo, fragmentShaderStageInfo] 441 | 442 | vertexInputInfo = VkPipelineVertexInputStateCreateInfo( 443 | vertexBindingDescriptionCount=0, 444 | vertexAttributeDescriptionCount=0 445 | ) 446 | 447 | inputAssembly = VkPipelineInputAssemblyStateCreateInfo( 448 | topology=VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 449 | primitiveRestartEnable=False 450 | ) 451 | 452 | viewport = VkViewport(0.0, 0.0, 453 | float(self.__swapChainExtent.width), 454 | float(self.__swapChainExtent.height), 455 | 0.0, 1.0) 456 | 457 | scissor = VkRect2D([0, 0], self.__swapChainExtent) 458 | viewportStage = VkPipelineViewportStateCreateInfo( 459 | viewportCount=1, 460 | pViewports=viewport, 461 | scissorCount=1, 462 | pScissors=scissor 463 | ) 464 | 465 | rasterizer = VkPipelineRasterizationStateCreateInfo( 466 | depthClampEnable=False, 467 | rasterizerDiscardEnable=False, 468 | polygonMode=VK_POLYGON_MODE_FILL, 469 | lineWidth=1.0, 470 | cullMode=VK_CULL_MODE_BACK_BIT, 471 | frontFace=VK_FRONT_FACE_CLOCKWISE, 472 | depthBiasEnable=False 473 | ) 474 | 475 | multisampling = VkPipelineMultisampleStateCreateInfo( 476 | sampleShadingEnable=False, 477 | rasterizationSamples=VK_SAMPLE_COUNT_1_BIT 478 | ) 479 | 480 | colorBlendAttachment = VkPipelineColorBlendAttachmentState( 481 | colorWriteMask=VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, 482 | blendEnable=False 483 | ) 484 | 485 | colorBending = VkPipelineColorBlendStateCreateInfo( 486 | logicOpEnable=False, 487 | logicOp=VK_LOGIC_OP_COPY, 488 | attachmentCount=1, 489 | pAttachments=colorBlendAttachment, 490 | blendConstants=[0.0, 0.0, 0.0, 0.0] 491 | ) 492 | 493 | pipelineLayoutInfo = VkPipelineLayoutCreateInfo( 494 | setLayoutCount=0, 495 | pushConstantRangeCount=0 496 | ) 497 | 498 | self.__pipelineLayout = vkCreatePipelineLayout(self.__device, pipelineLayoutInfo, None) 499 | 500 | pipelineInfo = VkGraphicsPipelineCreateInfo( 501 | # stageCount=len(shaderStageInfos), 502 | pStages=shaderStageInfos, 503 | pVertexInputState=vertexInputInfo, 504 | pInputAssemblyState=inputAssembly, 505 | pViewportState=viewportStage, 506 | pRasterizationState=rasterizer, 507 | pMultisampleState=multisampling, 508 | pColorBlendState=colorBending, 509 | layout=self.__pipelineLayout, 510 | renderPass=self.__renderpass, 511 | ) 512 | 513 | self.__pipeline = vkCreateGraphicsPipelines(self.__device, VK_NULL_HANDLE, 1, pipelineInfo, None)#[0] 514 | 515 | vkDestroyShaderModule(self.__device, vertexShaderMode, None) 516 | vkDestroyShaderModule(self.__device, fragmentShaderMode, None) 517 | 518 | def __createShaderModule(self, shaderFile): 519 | with open(shaderFile, 'rb') as sf: 520 | code = sf.read() 521 | 522 | createInfo = VkShaderModuleCreateInfo( 523 | codeSize=len(code), 524 | pCode=code 525 | ) 526 | 527 | return vkCreateShaderModule(self.__device, createInfo, None) 528 | 529 | def __chooseSwapSurfaceFormat(self, formats): 530 | if len(formats) == 1 and formats[0].format == VK_FORMAT_UNDEFINED: 531 | return [VK_FORMAT_B8G8R8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR] 532 | 533 | for i in formats: 534 | if i.format == VK_FORMAT_B8G8R8_UNORM and i.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: 535 | return i 536 | 537 | return formats[0] 538 | 539 | def __chooseSwapPresentMode(self, presentModes): 540 | bestMode = VK_PRESENT_MODE_FIFO_KHR 541 | 542 | for i in presentModes: 543 | if i == VK_PRESENT_MODE_FIFO_KHR: 544 | return i 545 | elif i == VK_PRESENT_MODE_MAILBOX_KHR: 546 | return i 547 | elif i == VK_PRESENT_MODE_IMMEDIATE_KHR: 548 | return i 549 | 550 | return bestMode 551 | 552 | def __chooseSwapExtent(self, capabilities): 553 | width = max(capabilities.minImageExtent.width, min(capabilities.maxImageExtent.width, self.width())) 554 | height = max(capabilities.minImageExtent.height, min(capabilities.maxImageExtent.height, self.height())) 555 | return VkExtent2D(width, height) 556 | 557 | def __querySwapChainSupport(self, device): 558 | detail = SwapChainSupportDetails() 559 | 560 | detail.capabilities = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, self.__surface) 561 | detail.formats = vkGetPhysicalDeviceSurfaceFormatsKHR(device, self.__surface) 562 | detail.presentModes = vkGetPhysicalDeviceSurfacePresentModesKHR(device, self.__surface) 563 | return detail 564 | 565 | def __isDeviceSuitable(self, device): 566 | indices = self.__findQueueFamilies(device) 567 | 568 | extensionsSupported = self.__checkDeviceExtensionSupport(device) 569 | 570 | swapChainAdequate = False 571 | if extensionsSupported: 572 | swapChainSupport = self.__querySwapChainSupport(device) 573 | swapChainAdequate = (swapChainSupport.formats is not None) and (swapChainSupport.presentModes is not None) 574 | 575 | return indices.isComplete and extensionsSupported and swapChainAdequate 576 | 577 | def __checkDeviceExtensionSupport(self, device): 578 | availableExtensions = vkEnumerateDeviceExtensionProperties(device, None) 579 | 580 | aen = [i.extensionName for i in availableExtensions] 581 | for i in deviceExtensions: 582 | if i not in aen: 583 | return False 584 | 585 | return True 586 | 587 | def __findQueueFamilies(self, device): 588 | indices = QueueFamilyIndices() 589 | 590 | familyProperties = vkGetPhysicalDeviceQueueFamilyProperties(device) 591 | for i, prop in enumerate(familyProperties): 592 | if prop.queueCount > 0 and prop.queueFlags & VK_QUEUE_GRAPHICS_BIT: 593 | indices.graphicsFamily = i 594 | 595 | presentSupport = vkGetPhysicalDeviceSurfaceSupportKHR(device, i, self.__surface) 596 | 597 | if prop.queueCount > 0 and presentSupport: 598 | indices.presentFamily = i 599 | 600 | if indices.isComplete: 601 | break 602 | 603 | return indices 604 | 605 | def __getRequiredExtensions(self): 606 | extenstions = [e.extensionName for e in vkEnumerateInstanceExtensionProperties(None)] 607 | 608 | if enableValidationLayers: 609 | extenstions.append(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) 610 | 611 | return extenstions 612 | 613 | def __checkValidationLayerSupport(self): 614 | availableLayers = vkEnumerateInstanceLayerProperties() 615 | 616 | for layer in validationLayers: 617 | layerfound = False 618 | 619 | for layerProp in availableLayers: 620 | if layer == layerProp.layerName: 621 | layerfound = True 622 | break 623 | return layerfound 624 | 625 | return False 626 | 627 | 628 | if __name__ == '__main__': 629 | import sys 630 | 631 | app = QtGui.QGuiApplication(sys.argv) 632 | 633 | win = HelloTriangleApplication() 634 | win.show() 635 | 636 | 637 | def clenaup(): 638 | global win 639 | del win 640 | 641 | 642 | app.aboutToQuit.connect(clenaup) 643 | 644 | sys.exit(app.exec_()) 645 | -------------------------------------------------------------------------------- /13_framebuffers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | import sys 4 | 5 | from vulkan import * 6 | from PySide2 import (QtGui, QtCore) 7 | 8 | validationLayers = [ 9 | 'VK_LAYER_LUNARG_standard_validation' 10 | ] 11 | 12 | deviceExtensions = [ 13 | VK_KHR_SWAPCHAIN_EXTENSION_NAME 14 | ] 15 | 16 | enableValidationLayers = True 17 | 18 | 19 | class InstanceProcAddr(object): 20 | T = None 21 | 22 | def __init__(self, func): 23 | self.__func = func 24 | 25 | def __call__(self, *args, **kwargs): 26 | funcName = self.__func.__name__ 27 | func = InstanceProcAddr.procfunc(funcName) 28 | if func: 29 | return func(*args, **kwargs) 30 | else: 31 | return VK_ERROR_EXTENSION_NOT_PRESENT 32 | 33 | @staticmethod 34 | def procfunc(funcName): 35 | return vkGetInstanceProcAddr(InstanceProcAddr.T, funcName) 36 | 37 | 38 | class DeviceProcAddr(InstanceProcAddr): 39 | 40 | @staticmethod 41 | def procfunc(funcName): 42 | return vkGetDeviceProcAddr(InstanceProcAddr.T, funcName) 43 | 44 | # instance ext functions 45 | @InstanceProcAddr 46 | def vkCreateDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 47 | pass 48 | 49 | 50 | @InstanceProcAddr 51 | def vkDestroyDebugReportCallbackEXT(instance, pCreateInfo, pAllocator): 52 | pass 53 | 54 | 55 | @InstanceProcAddr 56 | def vkCreateWin32SurfaceKHR(instance, pCreateInfo, pAllocator): 57 | pass 58 | 59 | 60 | @InstanceProcAddr 61 | def vkDestroySurfaceKHR(instance, surface, pAllocator): 62 | pass 63 | 64 | 65 | @InstanceProcAddr 66 | def vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, queueFamilyIndex, surface): 67 | pass 68 | 69 | 70 | @InstanceProcAddr 71 | def vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface): 72 | pass 73 | 74 | 75 | @InstanceProcAddr 76 | def vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface): 77 | pass 78 | 79 | 80 | @InstanceProcAddr 81 | def vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface): 82 | pass 83 | 84 | 85 | # device ext functions 86 | @DeviceProcAddr 87 | def vkCreateSwapchainKHR(device, pCreateInfo, pAllocator): 88 | pass 89 | 90 | @DeviceProcAddr 91 | def vkDestroySwapchainKHR(device, swapchain, pAllocator): 92 | pass 93 | 94 | @DeviceProcAddr 95 | def vkGetSwapchainImagesKHR(device, swapchain): 96 | pass 97 | 98 | 99 | 100 | 101 | def debugCallback(*args): 102 | print('DEBUG: {} {}'.format(args[5], args[6])) 103 | return 0 104 | 105 | 106 | class Win32misc(object): 107 | @staticmethod 108 | def getInstance(hWnd): 109 | from cffi import FFI as _FFI 110 | _ffi = _FFI() 111 | _ffi.cdef('long __stdcall GetWindowLongA(void* hWnd, int nIndex);') 112 | _lib = _ffi.dlopen('User32.dll') 113 | return _lib.GetWindowLongA(_ffi.cast('void*', hWnd), -6) # GWL_HINSTANCE 114 | 115 | 116 | class QueueFamilyIndices(object): 117 | 118 | def __init__(self): 119 | self.graphicsFamily = -1 120 | self.presentFamily = -1 121 | 122 | @property 123 | def isComplete(self): 124 | return self.graphicsFamily >= 0 and self.presentFamily >= 0 125 | 126 | 127 | class SwapChainSupportDetails(object): 128 | 129 | def __init__(self): 130 | self.capabilities = None 131 | self.formats = None 132 | self.presentModes = None 133 | 134 | 135 | class HelloTriangleApplication(QtGui.QWindow): 136 | 137 | def __init__(self): 138 | super(HelloTriangleApplication, self).__init__() 139 | 140 | self.setWidth(1280) 141 | self.setHeight(720) 142 | 143 | self.setTitle("Vulkan Python - PySide2") 144 | 145 | # self.setSurfaceType(self.OpenGLSurface) 146 | 147 | self.__instance = None 148 | self.__callbcak = None 149 | self.__surface = None 150 | 151 | self.__physicalDevice = None 152 | self.__device = None 153 | self.__graphicQueue = None 154 | self.__presentQueue = None 155 | 156 | self.__swapChain = None 157 | self.__swapChainImages = [] 158 | self.__swapChainImageFormat = None 159 | self.__swapChainExtent = None 160 | self.__swapChainImageViews = [] 161 | self.__swapChainFramebuffers = [] 162 | 163 | self.__renderpass = None 164 | self.__pipeline = None 165 | self.__pipelineLayout = None 166 | 167 | self.__indices = QueueFamilyIndices() 168 | 169 | self.initVulkan() 170 | 171 | def __del__(self): 172 | if self.__swapChainFramebuffers: 173 | [vkDestroyFramebuffer(self.__device, i, None) for i in self.__swapChainFramebuffers] 174 | 175 | if self.__renderpass: 176 | vkDestroyRenderPass(self.__device, self.__renderpass, None) 177 | 178 | if self.__pipelineLayout: 179 | vkDestroyPipelineLayout(self.__device, self.__pipelineLayout, None) 180 | 181 | if self.__pipeline: 182 | vkDestroyPipeline(self.__device, self.__pipeline, None) 183 | 184 | if self.__swapChainImageViews: 185 | [vkDestroyImageView(self.__device, imv, None) for imv in self.__swapChainImageViews] 186 | 187 | if self.__swapChain: 188 | vkDestroySwapchainKHR(self.__device, self.__swapChain, None) 189 | 190 | if self.__device: 191 | vkDestroyDevice(self.__device, None) 192 | 193 | if self.__callbcak: 194 | vkDestroyDebugReportCallbackEXT(self.__instance, self.__callbcak, None) 195 | 196 | if self.__surface: 197 | vkDestroySurfaceKHR(self.__instance, self.__surface, None) 198 | 199 | if self.__instance: 200 | vkDestroyInstance(self.__instance, None) 201 | print('instance destroyed') 202 | 203 | def initVulkan(self): 204 | self.__cretaeInstance() 205 | self.__setupDebugCallback() 206 | self.__createSurface() 207 | self.__pickPhysicalDevice() 208 | self.__createLogicalDevice() 209 | self.__createSwapChain() 210 | self.__createImageViews() 211 | self.__createRenderPass() 212 | self.__createGraphicsPipeline() 213 | self.__createFrambuffers() 214 | 215 | def __cretaeInstance(self): 216 | if enableValidationLayers and not self.__checkValidationLayerSupport(): 217 | raise Exception("validation layers requested, but not available!") 218 | 219 | appInfo = VkApplicationInfo( 220 | # sType=VK_STRUCTURE_TYPE_APPLICATION_INFO, 221 | pApplicationName='Python VK', 222 | applicationVersion=VK_MAKE_VERSION(1, 0, 0), 223 | pEngineName='pyvulkan', 224 | engineVersion=VK_MAKE_VERSION(1, 0, 0), 225 | apiVersion=VK_API_VERSION 226 | ) 227 | 228 | extenstions = self.__getRequiredExtensions() 229 | if enableValidationLayers: 230 | instanceInfo = VkInstanceCreateInfo( 231 | pApplicationInfo=appInfo, 232 | # enabledLayerCount=len(validationLayers), 233 | ppEnabledLayerNames=validationLayers, 234 | # enabledExtensionCount=len(extenstions), 235 | ppEnabledExtensionNames=extenstions 236 | ) 237 | else: 238 | instanceInfo = VkInstanceCreateInfo( 239 | pApplicationInfo=appInfo, 240 | enabledLayerCount=0, 241 | # enabledExtensionCount=len(extenstions), 242 | ppEnabledExtensionNames=extenstions 243 | ) 244 | 245 | self.__instance = vkCreateInstance(instanceInfo, None) 246 | 247 | InstanceProcAddr.T = self.__instance 248 | 249 | def __setupDebugCallback(self): 250 | if not enableValidationLayers: 251 | return 252 | 253 | createInfo = VkDebugReportCallbackCreateInfoEXT( 254 | flags=VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT, 255 | pfnCallback=debugCallback 256 | ) 257 | 258 | self.__callbcak = vkCreateDebugReportCallbackEXT(self.__instance, createInfo, None) 259 | 260 | def __createSurface(self): 261 | if sys.platform == 'win32': 262 | hwnd = self.winId() 263 | hinstance = Win32misc.getInstance(hwnd) 264 | createInfo = VkWin32SurfaceCreateInfoKHR( 265 | hinstance=hinstance, 266 | hwnd=hwnd 267 | ) 268 | 269 | self.__surface = vkCreateWin32SurfaceKHR(self.__instance, createInfo, None) 270 | # elif sys.platform == 'linux': 271 | # pass 272 | 273 | def __pickPhysicalDevice(self): 274 | physicalDevices = vkEnumeratePhysicalDevices(self.__instance) 275 | 276 | for device in physicalDevices: 277 | if self.__isDeviceSuitable(device): 278 | self.__physicalDevice = device 279 | break 280 | 281 | assert self.__physicalDevice != None 282 | 283 | def __createLogicalDevice(self): 284 | self.__indices = self.__findQueueFamilies(self.__physicalDevice) 285 | 286 | uniqueQueueFamilies = {}.fromkeys([self.__indices.graphicsFamily, self.__indices.presentFamily]) 287 | queueCreateInfos = [] 288 | for i in uniqueQueueFamilies: 289 | queueCreateInfo = VkDeviceQueueCreateInfo( 290 | queueFamilyIndex=i, 291 | queueCount=1, 292 | pQueuePriorities=[1.0] 293 | ) 294 | queueCreateInfos.append(queueCreateInfo) 295 | 296 | deviceFeatures = VkPhysicalDeviceFeatures() 297 | if enableValidationLayers: 298 | createInfo = VkDeviceCreateInfo( 299 | # queueCreateInfoCount=len(queueCreateInfos), 300 | pQueueCreateInfos=queueCreateInfos, 301 | # enabledExtensionCount=len(deviceExtensions), 302 | ppEnabledExtensionNames=deviceExtensions, 303 | # enabledLayerCount=len(validationLayers), 304 | ppEnabledLayerNames=validationLayers, 305 | pEnabledFeatures=deviceFeatures 306 | ) 307 | else: 308 | createInfo = VkDeviceCreateInfo( 309 | queueCreateInfoCount=1, 310 | pQueueCreateInfos=queueCreateInfo, 311 | # enabledExtensionCount=len(deviceExtensions), 312 | ppEnabledExtensionNames=deviceExtensions, 313 | enabledLayerCount=0, 314 | pEnabledFeatures=deviceFeatures 315 | ) 316 | 317 | self.__device = vkCreateDevice(self.__physicalDevice, createInfo, None) 318 | 319 | DeviceProcAddr.T = self.__device 320 | 321 | self.__graphicQueue = vkGetDeviceQueue(self.__device, self.__indices.graphicsFamily, 0) 322 | self.__presentQueue = vkGetDeviceQueue(self.__device, self.__indices.presentFamily, 0) 323 | 324 | def __createSwapChain(self): 325 | swapChainSupport = self.__querySwapChainSupport(self.__physicalDevice) 326 | 327 | surfaceFormat = self.__chooseSwapSurfaceFormat(swapChainSupport.formats) 328 | presentMode = self.__chooseSwapPresentMode(swapChainSupport.presentModes) 329 | extent = self.__chooseSwapExtent(swapChainSupport.capabilities) 330 | 331 | imageCount = swapChainSupport.capabilities.minImageCount + 1 332 | if swapChainSupport.capabilities.maxImageCount > 0 and imageCount > swapChainSupport.capabilities.maxImageCount: 333 | imageCount = swapChainSupport.capabilities.maxImageCount 334 | 335 | indices = self.__findQueueFamilies(self.__physicalDevice) 336 | queueFamily = {}.fromkeys([indices.graphicsFamily, indices.presentFamily]) 337 | queueFamilies = list(queueFamily.keys()) 338 | if len(queueFamilies) > 1: 339 | createInfo = VkSwapchainCreateInfoKHR( 340 | surface=self.__surface, 341 | minImageCount=imageCount, 342 | imageFormat=surfaceFormat.format, 343 | imageColorSpace=surfaceFormat.colorSpace, 344 | imageExtent=extent, 345 | imageArrayLayers=1, 346 | imageUsage=VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 347 | # queueFamilyIndexCount=len(queueFamilies), 348 | pQueueFamilyIndices=queueFamilies, 349 | imageSharingMode=VK_SHARING_MODE_CONCURRENT, 350 | preTransform=swapChainSupport.capabilities.currentTransform, 351 | compositeAlpha=VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 352 | presentMode=presentMode, 353 | clipped=True 354 | ) 355 | else: 356 | createInfo = VkSwapchainCreateInfoKHR( 357 | surface=self.__surface, 358 | minImageCount=imageCount, 359 | imageFormat=surfaceFormat.format, 360 | imageColorSpace=surfaceFormat.colorSpace, 361 | imageExtent=extent, 362 | imageArrayLayers=1, 363 | imageUsage=VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, 364 | # queueFamilyIndexCount=len(queueFamilies), 365 | pQueueFamilyIndices=queueFamilies, 366 | imageSharingMode=VK_SHARING_MODE_EXCLUSIVE, 367 | preTransform=swapChainSupport.capabilities.currentTransform, 368 | compositeAlpha=VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, 369 | presentMode=presentMode, 370 | clipped=True 371 | ) 372 | 373 | self.__swapChain = vkCreateSwapchainKHR(self.__device, createInfo, None) 374 | assert self.__swapChain != None 375 | 376 | self.__swapChainImages = vkGetSwapchainImagesKHR(self.__device, self.__swapChain) 377 | 378 | self.__swapChainImageFormat = surfaceFormat.format 379 | self.__swapChainExtent = extent 380 | 381 | def __createImageViews(self): 382 | self.__swapChainImageViews = [] 383 | 384 | for i, image in enumerate(self.__swapChainImages): 385 | ssr = VkImageSubresourceRange( 386 | VK_IMAGE_ASPECT_COLOR_BIT, 387 | 0, 1, 0, 1 388 | ) 389 | 390 | createInfo = VkImageViewCreateInfo( 391 | image=image, 392 | viewType=VK_IMAGE_VIEW_TYPE_2D, 393 | format=self.__swapChainImageFormat, 394 | components=[VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, 395 | VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY], 396 | subresourceRange=ssr 397 | ) 398 | 399 | self.__swapChainImageViews.append(vkCreateImageView(self.__device, createInfo, None)) 400 | 401 | def __createRenderPass(self): 402 | colorAttachment = VkAttachmentDescription( 403 | format=self.__swapChainImageFormat, 404 | samples=VK_SAMPLE_COUNT_1_BIT, 405 | loadOp=VK_ATTACHMENT_LOAD_OP_CLEAR, 406 | storeOp=VK_ATTACHMENT_STORE_OP_STORE, 407 | stencilLoadOp=VK_ATTACHMENT_LOAD_OP_DONT_CARE, 408 | stencilStoreOp=VK_ATTACHMENT_STORE_OP_DONT_CARE, 409 | initialLayout=VK_IMAGE_LAYOUT_UNDEFINED, 410 | finalLayout=VK_IMAGE_LAYOUT_PRESENT_SRC_KHR 411 | ) 412 | 413 | colorAttachmentRef = VkAttachmentReference( 414 | 0, 415 | VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL 416 | ) 417 | 418 | subpass = VkSubpassDescription( 419 | pipelineBindPoint=VK_PIPELINE_BIND_POINT_GRAPHICS, 420 | pColorAttachments=[colorAttachmentRef] 421 | ) 422 | 423 | renderPassInfo = VkRenderPassCreateInfo( 424 | pAttachments=[colorAttachment], 425 | pSubpasses=[subpass] 426 | ) 427 | 428 | self.__renderpass = vkCreateRenderPass(self.__device, renderPassInfo, None) 429 | 430 | def __createGraphicsPipeline(self): 431 | vertexShaderMode = self.__createShaderModule('shader/vert.spv') 432 | fragmentShaderMode = self.__createShaderModule('shader/frag.spv') 433 | 434 | vertexShaderStageInfo = VkPipelineShaderStageCreateInfo( 435 | stage=VK_SHADER_STAGE_VERTEX_BIT, 436 | module=vertexShaderMode, 437 | pName='main' 438 | ) 439 | fragmentShaderStageInfo = VkPipelineShaderStageCreateInfo( 440 | stage=VK_SHADER_STAGE_FRAGMENT_BIT, 441 | module=fragmentShaderMode, 442 | pName='main' 443 | ) 444 | 445 | shaderStageInfos = [vertexShaderStageInfo, fragmentShaderStageInfo] 446 | 447 | vertexInputInfo = VkPipelineVertexInputStateCreateInfo( 448 | vertexBindingDescriptionCount=0, 449 | vertexAttributeDescriptionCount=0 450 | ) 451 | 452 | inputAssembly = VkPipelineInputAssemblyStateCreateInfo( 453 | topology=VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 454 | primitiveRestartEnable=False 455 | ) 456 | 457 | viewport = VkViewport(0.0, 0.0, 458 | float(self.__swapChainExtent.width), 459 | float(self.__swapChainExtent.height), 460 | 0.0, 1.0) 461 | 462 | scissor = VkRect2D([0, 0], self.__swapChainExtent) 463 | viewportStage = VkPipelineViewportStateCreateInfo( 464 | viewportCount=1, 465 | pViewports=viewport, 466 | scissorCount=1, 467 | pScissors=scissor 468 | ) 469 | 470 | rasterizer = VkPipelineRasterizationStateCreateInfo( 471 | depthClampEnable=False, 472 | rasterizerDiscardEnable=False, 473 | polygonMode=VK_POLYGON_MODE_FILL, 474 | lineWidth=1.0, 475 | cullMode=VK_CULL_MODE_BACK_BIT, 476 | frontFace=VK_FRONT_FACE_CLOCKWISE, 477 | depthBiasEnable=False 478 | ) 479 | 480 | multisampling = VkPipelineMultisampleStateCreateInfo( 481 | sampleShadingEnable=False, 482 | rasterizationSamples=VK_SAMPLE_COUNT_1_BIT 483 | ) 484 | 485 | colorBlendAttachment = VkPipelineColorBlendAttachmentState( 486 | colorWriteMask=VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, 487 | blendEnable=False 488 | ) 489 | 490 | colorBending = VkPipelineColorBlendStateCreateInfo( 491 | logicOpEnable=False, 492 | logicOp=VK_LOGIC_OP_COPY, 493 | attachmentCount=1, 494 | pAttachments=colorBlendAttachment, 495 | blendConstants=[0.0, 0.0, 0.0, 0.0] 496 | ) 497 | 498 | pipelineLayoutInfo = VkPipelineLayoutCreateInfo( 499 | setLayoutCount=0, 500 | pushConstantRangeCount=0 501 | ) 502 | 503 | self.__pipelineLayout = vkCreatePipelineLayout(self.__device, pipelineLayoutInfo, None) 504 | 505 | pipelineInfo = VkGraphicsPipelineCreateInfo( 506 | # stageCount=len(shaderStageInfos), 507 | pStages=shaderStageInfos, 508 | pVertexInputState=vertexInputInfo, 509 | pInputAssemblyState=inputAssembly, 510 | pViewportState=viewportStage, 511 | pRasterizationState=rasterizer, 512 | pMultisampleState=multisampling, 513 | pColorBlendState=colorBending, 514 | layout=self.__pipelineLayout, 515 | renderPass=self.__renderpass, 516 | ) 517 | 518 | self.__pipeline = vkCreateGraphicsPipelines(self.__device, VK_NULL_HANDLE, 1, pipelineInfo, None)#[0] 519 | 520 | vkDestroyShaderModule(self.__device, vertexShaderMode, None) 521 | vkDestroyShaderModule(self.__device, fragmentShaderMode, None) 522 | 523 | def __createFrambuffers(self): 524 | self.__swapChainFramebuffers = [] 525 | for i, iv in enumerate(self.__swapChainImageViews): 526 | framebufferInfo = VkFramebufferCreateInfo( 527 | renderPass=self.__renderpass, 528 | pAttachments=[iv], 529 | width=self.__swapChainExtent.width, 530 | height=self.__swapChainExtent.height, 531 | layers=1 532 | ) 533 | 534 | self.__swapChainFramebuffers.append(vkCreateFramebuffer(self.__device, framebufferInfo, None)) 535 | 536 | def __createShaderModule(self, shaderFile): 537 | with open(shaderFile, 'rb') as sf: 538 | code = sf.read() 539 | 540 | createInfo = VkShaderModuleCreateInfo( 541 | codeSize=len(code), 542 | pCode=code 543 | ) 544 | 545 | return vkCreateShaderModule(self.__device, createInfo, None) 546 | 547 | def __chooseSwapSurfaceFormat(self, formats): 548 | if len(formats) == 1 and formats[0].format == VK_FORMAT_UNDEFINED: 549 | return [VK_FORMAT_B8G8R8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR] 550 | 551 | for i in formats: 552 | if i.format == VK_FORMAT_B8G8R8_UNORM and i.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR: 553 | return i 554 | 555 | return formats[0] 556 | 557 | def __chooseSwapPresentMode(self, presentModes): 558 | bestMode = VK_PRESENT_MODE_FIFO_KHR 559 | 560 | for i in presentModes: 561 | if i == VK_PRESENT_MODE_FIFO_KHR: 562 | return i 563 | elif i == VK_PRESENT_MODE_MAILBOX_KHR: 564 | return i 565 | elif i == VK_PRESENT_MODE_IMMEDIATE_KHR: 566 | return i 567 | 568 | return bestMode 569 | 570 | def __chooseSwapExtent(self, capabilities): 571 | width = max(capabilities.minImageExtent.width, min(capabilities.maxImageExtent.width, self.width())) 572 | height = max(capabilities.minImageExtent.height, min(capabilities.maxImageExtent.height, self.height())) 573 | return VkExtent2D(width, height) 574 | 575 | def __querySwapChainSupport(self, device): 576 | detail = SwapChainSupportDetails() 577 | 578 | detail.capabilities = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, self.__surface) 579 | detail.formats = vkGetPhysicalDeviceSurfaceFormatsKHR(device, self.__surface) 580 | detail.presentModes = vkGetPhysicalDeviceSurfacePresentModesKHR(device, self.__surface) 581 | return detail 582 | 583 | def __isDeviceSuitable(self, device): 584 | indices = self.__findQueueFamilies(device) 585 | 586 | extensionsSupported = self.__checkDeviceExtensionSupport(device) 587 | 588 | swapChainAdequate = False 589 | if extensionsSupported: 590 | swapChainSupport = self.__querySwapChainSupport(device) 591 | swapChainAdequate = (swapChainSupport.formats is not None) and (swapChainSupport.presentModes is not None) 592 | 593 | return indices.isComplete and extensionsSupported and swapChainAdequate 594 | 595 | def __checkDeviceExtensionSupport(self, device): 596 | availableExtensions = vkEnumerateDeviceExtensionProperties(device, None) 597 | 598 | aen = [i.extensionName for i in availableExtensions] 599 | for i in deviceExtensions: 600 | if i not in aen: 601 | return False 602 | 603 | return True 604 | 605 | def __findQueueFamilies(self, device): 606 | indices = QueueFamilyIndices() 607 | 608 | familyProperties = vkGetPhysicalDeviceQueueFamilyProperties(device) 609 | for i, prop in enumerate(familyProperties): 610 | if prop.queueCount > 0 and prop.queueFlags & VK_QUEUE_GRAPHICS_BIT: 611 | indices.graphicsFamily = i 612 | 613 | presentSupport = vkGetPhysicalDeviceSurfaceSupportKHR(device, i, self.__surface) 614 | 615 | if prop.queueCount > 0 and presentSupport: 616 | indices.presentFamily = i 617 | 618 | if indices.isComplete: 619 | break 620 | 621 | return indices 622 | 623 | def __getRequiredExtensions(self): 624 | extenstions = [e.extensionName for e in vkEnumerateInstanceExtensionProperties(None)] 625 | 626 | if enableValidationLayers: 627 | extenstions.append(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) 628 | 629 | return extenstions 630 | 631 | def __checkValidationLayerSupport(self): 632 | availableLayers = vkEnumerateInstanceLayerProperties() 633 | 634 | for layer in validationLayers: 635 | layerfound = False 636 | 637 | for layerProp in availableLayers: 638 | if layer == layerProp.layerName: 639 | layerfound = True 640 | break 641 | return layerfound 642 | 643 | return False 644 | 645 | 646 | if __name__ == '__main__': 647 | import sys 648 | 649 | app = QtGui.QGuiApplication(sys.argv) 650 | 651 | win = HelloTriangleApplication() 652 | win.show() 653 | 654 | 655 | def clenaup(): 656 | global win 657 | del win 658 | 659 | 660 | app.aboutToQuit.connect(clenaup) 661 | 662 | sys.exit(app.exec_()) 663 | -------------------------------------------------------------------------------- /17_shader_vertexbuffer.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec3 fragColor; 5 | 6 | layout(location = 0) out vec4 outColor; 7 | 8 | void main() { 9 | outColor = vec4(fragColor, 1.0); 10 | } -------------------------------------------------------------------------------- /17_shader_vertexbuffer.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec2 inPosition; 5 | layout(location = 1) in vec3 inColor; 6 | 7 | layout(location = 0) out vec3 fragColor; 8 | 9 | out gl_PerVertex { 10 | vec4 gl_Position; 11 | }; 12 | 13 | void main() { 14 | gl_Position = vec4(inPosition, 0.0, 1.0); 15 | fragColor = inColor; 16 | } -------------------------------------------------------------------------------- /21_shader_ubo.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(location = 0) in vec3 fragColor; 5 | 6 | layout(location = 0) out vec4 outColor; 7 | 8 | void main() { 9 | outColor = vec4(fragColor, 1.0); 10 | } -------------------------------------------------------------------------------- /21_shader_ubo.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(binding = 0) uniform UniformBufferObject { 5 | mat4 model; 6 | mat4 view; 7 | mat4 proj; 8 | } ubo; 9 | 10 | layout(location = 0) in vec2 inPosition; 11 | layout(location = 1) in vec3 inColor; 12 | 13 | layout(location = 0) out vec3 fragColor; 14 | 15 | out gl_PerVertex { 16 | vec4 gl_Position; 17 | }; 18 | 19 | void main() { 20 | gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); 21 | fragColor = inColor; 22 | } -------------------------------------------------------------------------------- /25_shader_textures.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(binding = 1) uniform sampler2D texSampler; 5 | 6 | layout(location = 0) in vec3 fragColor; 7 | layout(location = 1) in vec2 fragTexCoord; 8 | 9 | layout(location = 0) out vec4 outColor; 10 | 11 | void main() { 12 | outColor = texture(texSampler, fragTexCoord); 13 | } -------------------------------------------------------------------------------- /25_shader_textures.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(binding = 0) uniform UniformBufferObject { 5 | mat4 model; 6 | mat4 view; 7 | mat4 proj; 8 | } ubo; 9 | 10 | layout(location = 0) in vec2 inPosition; 11 | layout(location = 1) in vec3 inColor; 12 | layout(location = 2) in vec2 inTexCoord; 13 | 14 | layout(location = 0) out vec3 fragColor; 15 | layout(location = 1) out vec2 fragTexCoord; 16 | 17 | out gl_PerVertex { 18 | vec4 gl_Position; 19 | }; 20 | 21 | void main() { 22 | gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 0.0, 1.0); 23 | fragColor = inColor; 24 | fragTexCoord = inTexCoord; 25 | } -------------------------------------------------------------------------------- /26_shader_depth.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(binding = 1) uniform sampler2D texSampler; 5 | 6 | layout(location = 0) in vec3 fragColor; 7 | layout(location = 1) in vec2 fragTexCoord; 8 | 9 | layout(location = 0) out vec4 outColor; 10 | 11 | void main() { 12 | outColor = texture(texSampler, fragTexCoord); 13 | } -------------------------------------------------------------------------------- /26_shader_depth.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #extension GL_ARB_separate_shader_objects : enable 3 | 4 | layout(binding = 0) uniform UniformBufferObject { 5 | mat4 model; 6 | mat4 view; 7 | mat4 proj; 8 | } ubo; 9 | 10 | layout(location = 0) in vec3 inPosition; 11 | layout(location = 1) in vec3 inColor; 12 | layout(location = 2) in vec2 inTexCoord; 13 | 14 | layout(location = 0) out vec3 fragColor; 15 | layout(location = 1) out vec2 fragTexCoord; 16 | 17 | out gl_PerVertex { 18 | vec4 gl_Position; 19 | }; 20 | 21 | void main() { 22 | gl_Position = ubo.proj * ubo.view * ubo.model * vec4(inPosition, 1.0); 23 | fragColor = inColor; 24 | fragTexCoord = inTexCoord; 25 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 mack stone 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vulkan-tutorial 2 | vulkan tutorial with python live steam 3 | 4 | [PySide 2](http://wiki.qt.io/PySide2_GettingStarted) 5 | 6 | [Vulkan SDK](https://vulkan.lunarg.com/sdk/home) 7 | 8 | [Vulkan API spec](https://www.khronos.org/registry/vulkan/specs/1.1-khr-extensions/html/vkspec.html) 9 | 10 | [Live steam](https://panda.tv/1899029) -------------------------------------------------------------------------------- /glm.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # ----------------------------------------------------------------------------- 3 | # Copyright (c) 2014, Nicolas P. Rougier. All rights reserved. 4 | # Distributed under the terms of the new BSD License. 5 | # ----------------------------------------------------------------------------- 6 | """ 7 | Very simple transformation library that is needed for some examples. 8 | 9 | Notes 10 | ----- 11 | 12 | Functions that take a matrix as input generally operate on that matrix in 13 | place. 14 | """ 15 | 16 | # This file is copied from glumpy[https://github.com/glumpy/glumpy] 17 | # some functions add by myself 18 | 19 | # Note: we use functions from math module because they're faster on scalars 20 | 21 | import math 22 | import numpy as np 23 | 24 | def normalize(x): 25 | if isinstance(x, float): 26 | return -1 if x < 0 else 1 27 | elif len(x) > 1: 28 | sqr = math.sqrt(np.sum(x*x)) 29 | return x / sqr 30 | 31 | def translate(M, x, y=None, z=None): 32 | """Translate by an offset (x, y, z) . 33 | 34 | Parameters 35 | ---------- 36 | M : array 37 | Original transformation (4x4). 38 | x : float 39 | X coordinate of a translation vector. 40 | y : float | None 41 | Y coordinate of translation vector. If None, `x` will be used. 42 | z : float | None 43 | Z coordinate of translation vector. If None, `x` will be used. 44 | 45 | Returns 46 | ------- 47 | M : array 48 | Updated transformation (4x4). Note that this function operates 49 | in-place. 50 | """ 51 | y = x if y is None else y 52 | z = x if z is None else z 53 | T = np.array([[1.0, 0.0, 0.0, x], 54 | [0.0, 1.0, 0.0, y], 55 | [0.0, 0.0, 1.0, z], 56 | [0.0, 0.0, 0.0, 1.0]], dtype=M.dtype).T 57 | M[...] = np.dot(M, T) 58 | return M 59 | 60 | 61 | def translation(x, y=None, z=None): 62 | """Translate by an offset (x, y, z) . 63 | 64 | Parameters 65 | ---------- 66 | x : float 67 | X coordinate of a translation vector. 68 | y : float | None 69 | Y coordinate of translation vector. If None, `x` will be used. 70 | z : float | None 71 | Z coordinate of translation vector. If None, `x` will be used. 72 | 73 | Returns 74 | ------- 75 | M : array 76 | Translation matrix 77 | """ 78 | 79 | M = np.eye(4, dtype=np.float32) 80 | return translate(M,x,y,z) 81 | 82 | 83 | 84 | 85 | def scale(M, x, y=None, z=None): 86 | """Non-uniform scaling along the x, y, and z axes 87 | 88 | Parameters 89 | ---------- 90 | M : array 91 | Original transformation (4x4). 92 | x : float 93 | X coordinate of the translation vector. 94 | y : float | None 95 | Y coordinate of the translation vector. If None, `x` will be used. 96 | z : float | None 97 | Z coordinate of the translation vector. If None, `x` will be used. 98 | 99 | Returns 100 | ------- 101 | M : array 102 | Updated transformation (4x4). Note that this function operates 103 | in-place. 104 | """ 105 | y = x if y is None else y 106 | z = x if z is None else z 107 | S = np.array([[x, 0.0, 0.0, 0.0], 108 | [0.0, y, 0.0, 0.0], 109 | [0.0, 0.0, z, 0.0], 110 | [0.0, 0.0, 0.0, 1.0]], dtype=M.dtype).T 111 | M[...] = np.dot(M, S) 112 | return M 113 | 114 | 115 | def xrotate(M, theta): 116 | """Rotate about the X axis 117 | 118 | Parameters 119 | ---------- 120 | M : array 121 | Original transformation (4x4). 122 | theta : float 123 | Specifies the angle of rotation, in degrees. 124 | 125 | Returns 126 | ------- 127 | M : array 128 | Updated transformation (4x4). Note that this function operates 129 | in-place. 130 | """ 131 | t = math.pi * theta / 180. 132 | cosT = math.cos(t) 133 | sinT = math.sin(t) 134 | R = np.array([[1.0, 0.0, 0.0, 0.0], 135 | [0.0, cosT, -sinT, 0.0], 136 | [0.0, sinT, cosT, 0.0], 137 | [0.0, 0.0, 0.0, 1.0]], dtype=M.dtype) 138 | M[...] = np.dot(M, R) 139 | return M 140 | 141 | 142 | def yrotate(M, theta): 143 | """Rotate about the Y axis 144 | 145 | Parameters 146 | ---------- 147 | M : array 148 | Original transformation (4x4). 149 | theta : float 150 | Specifies the angle of rotation, in degrees. 151 | 152 | Returns 153 | ------- 154 | M : array 155 | Updated transformation (4x4). Note that this function operates 156 | in-place. 157 | """ 158 | t = math.pi * theta / 180 159 | cosT = math.cos(t) 160 | sinT = math.sin(t) 161 | R = np.array( 162 | [[cosT, 0.0, sinT, 0.0], 163 | [0.0, 1.0, 0.0, 0.0], 164 | [-sinT, 0.0, cosT, 0.0], 165 | [0.0, 0.0, 0.0, 1.0]], dtype=M.dtype) 166 | M[...] = np.dot(M, R) 167 | return M 168 | 169 | 170 | def zrotate(M, theta): 171 | """Rotate about the Z axis 172 | 173 | Parameters 174 | ---------- 175 | M : array 176 | Original transformation (4x4). 177 | theta : float 178 | Specifies the angle of rotation, in degrees. 179 | 180 | Returns 181 | ------- 182 | M : array 183 | Updated transformation (4x4). Note that this function operates 184 | in-place. 185 | """ 186 | t = math.pi * theta / 180 187 | cosT = math.cos(t) 188 | sinT = math.sin(t) 189 | R = np.array( 190 | [[cosT, -sinT, 0.0, 0.0], 191 | [sinT, cosT, 0.0, 0.0], 192 | [0.0, 0.0, 1.0, 0.0], 193 | [0.0, 0.0, 0.0, 1.0]], dtype=M.dtype) 194 | M[...] = np.dot(M, R) 195 | return M 196 | 197 | 198 | def rotate(M, angle, x, y, z, point=None): 199 | """Rotation about a vector 200 | 201 | Parameters 202 | ---------- 203 | M : array 204 | Original transformation (4x4). 205 | angle : float 206 | Specifies the angle of rotation, in degrees. 207 | x : float 208 | X coordinate of the angle of rotation vector. 209 | y : float | None 210 | Y coordinate of the angle of rotation vector. 211 | z : float | None 212 | Z coordinate of the angle of rotation vector. 213 | 214 | Returns 215 | ------- 216 | M : array 217 | Updated transformation (4x4). Note that this function operates 218 | in-place. 219 | """ 220 | angle = math.pi * angle / 180 221 | c, s = math.cos(angle), math.sin(angle) 222 | n = math.sqrt(x * x + y * y + z * z) 223 | x /= n 224 | y /= n 225 | z /= n 226 | cx, cy, cz = (1 - c) * x, (1 - c) * y, (1 - c) * z 227 | R = np.array([[cx * x + c, cy * x - z * s, cz * x + y * s, 0], 228 | [cx * y + z * s, cy * y + c, cz * y - x * s, 0], 229 | [cx * z - y * s, cy * z + x * s, cz * z + c, 0], 230 | [0, 0, 0, 1]], dtype=M.dtype).T 231 | M[...] = np.dot(M, R) 232 | return M 233 | 234 | 235 | def ortho(left, right, bottom, top, znear, zfar): 236 | """Create orthographic projection matrix 237 | 238 | Parameters 239 | ---------- 240 | left : float 241 | Left coordinate of the field of view. 242 | right : float 243 | Right coordinate of the field of view. 244 | bottom : float 245 | Bottom coordinate of the field of view. 246 | top : float 247 | Top coordinate of the field of view. 248 | znear : float 249 | Near coordinate of the field of view. 250 | zfar : float 251 | Far coordinate of the field of view. 252 | 253 | Returns 254 | ------- 255 | M : array 256 | Orthographic projection matrix (4x4). 257 | """ 258 | assert(right != left) 259 | assert(bottom != top) 260 | assert(znear != zfar) 261 | 262 | M = np.zeros((4, 4), dtype=np.float32) 263 | M[0, 0] = +2.0 / (right - left) 264 | M[3, 0] = -(right + left) / float(right - left) 265 | M[1, 1] = +2.0 / (top - bottom) 266 | M[3, 1] = -(top + bottom) / float(top - bottom) 267 | M[2, 2] = -2.0 / (zfar - znear) 268 | M[3, 2] = -(zfar + znear) / float(zfar - znear) 269 | M[3, 3] = 1.0 270 | return M 271 | 272 | 273 | def frustum(left, right, bottom, top, znear, zfar): 274 | """Create view frustum 275 | 276 | Parameters 277 | ---------- 278 | left : float 279 | Left coordinate of the field of view. 280 | right : float 281 | Right coordinate of the field of view. 282 | bottom : float 283 | Bottom coordinate of the field of view. 284 | top : float 285 | Top coordinate of the field of view. 286 | znear : float 287 | Near coordinate of the field of view. 288 | zfar : float 289 | Far coordinate of the field of view. 290 | 291 | Returns 292 | ------- 293 | M : array 294 | View frustum matrix (4x4). 295 | """ 296 | assert(right != left) 297 | assert(bottom != top) 298 | assert(znear != zfar) 299 | 300 | M = np.zeros((4, 4), dtype=np.float32) 301 | M[0, 0] = +2.0 * znear / (right - left) 302 | M[2, 0] = (right + left) / (right - left) 303 | M[1, 1] = +2.0 * znear / (top - bottom) 304 | M[3, 1] = (top + bottom) / (top - bottom) 305 | M[2, 2] = -(zfar + znear) / (zfar - znear) 306 | M[3, 2] = -2.0 * znear * zfar / (zfar - znear) 307 | M[2, 3] = -1.0 308 | return M 309 | 310 | 311 | def perspective(fovy, aspect, znear, zfar): 312 | """Create perspective projection matrix 313 | 314 | Parameters 315 | ---------- 316 | fovy : float 317 | The field of view along the y axis. 318 | aspect : float 319 | Aspect ratio of the view. 320 | znear : float 321 | Near coordinate of the field of view. 322 | zfar : float 323 | Far coordinate of the field of view. 324 | 325 | Returns 326 | ------- 327 | M : array 328 | Perspective projection matrix (4x4). 329 | """ 330 | assert(znear != zfar) 331 | h = math.tan(fovy / 360.0 * math.pi) * znear 332 | w = h * aspect 333 | return frustum(-w, w, -h, h, znear, zfar) 334 | 335 | 336 | def lookAt(eye, center, up): 337 | """ 338 | """ 339 | f = normalize(center - eye) 340 | s = normalize(np.cross(f, up)) 341 | u = np.cross(s, f) 342 | 343 | result = np.identity(4, np.float32) 344 | result[:,0][:3] = s 345 | result[:,1][:3] = u 346 | result[:,2][:3] = -f 347 | result[3][0] = -np.dot(s, eye) 348 | result[3][1] = -np.dot(u, eye) 349 | result[3][2] = np.dot(f, eye) 350 | return result -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | vulkan 2 | numpy 3 | Pillow --------------------------------------------------------------------------------