Created
August 3, 2023 19:04
-
-
Save omnp/6ac3385e2b3f6cab987d84e6477e636a to your computer and use it in GitHub Desktop.
Example showing how to create a minimal GTK 4 app with a main window and on it a Wayland subsurface fed by Vulkan.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Example showing how to create a minimal GTK 4 app with a main window and on it a Wayland subsurface fed by Vulkan. | |
# The FPS counter printed on the console should show about 60 fps on Gnome/Mutter. | |
# One may need to install vulkan (the python package) from source. | |
# Maybe same for pywayland. Mileage may vary. | |
# I happen to have two discrete GPUs installed in the test computer, one AMD, one NVIDIA, and Intel integrated to top. | |
# The Radeon is the main one running Gnome/Mutter (after some /usr/share/glvnd/egl_vendor.d/ symlinking happened, that is) | |
# "Works for me" on the Radeon, when started from Gnome Terminal as python example4.py | |
# Playing around with switcherooctl style env variables trying to get the Nvidia card to render vulkan while being composited to | |
# the main window does not work though. | |
# A similar error message can be found here: | |
# https://github.com/NVIDIA/egl-wayland/issues/41 | |
# Don't know if it's related. | |
# There are some additional comments in the code below (from myself and the original comments by the author of the vulkan example). | |
# Get GTK 4 | |
import gi | |
gi.require_version("Gtk", "4.0") | |
from gi.repository import Gtk | |
import ctypes | |
from cffi import FFI | |
ffi = FFI() | |
# These functions can't be accessed from the language bindings directly. | |
ffi.cdef(""" | |
void * gdk_wayland_surface_get_wl_surface (void * surface); | |
void * gdk_wayland_display_get_wl_display (void * display); | |
void * gdk_wayland_display_get_wl_compositor (void * display); | |
""") | |
# GTK 4, again | |
gtk = ffi.dlopen('libgtk-4.so.1') | |
# https://github.com/realitix/vulkan | |
import vulkan | |
# https://github.com/flacjacket/pywayland | |
import pywayland.client | |
import pywayland.protocol.wayland | |
import os | |
import sys | |
import time | |
def on_activate(app): | |
X=100 | |
Y=100 | |
WIDTH = 400 | |
HEIGHT = 400 | |
win = Gtk.ApplicationWindow(application=app) | |
win.set_default_size(600, 600) | |
win.set_title("Example 4") | |
win.present() | |
win.set_size_request(600, 600) | |
win.realize() | |
native = win.get_native() | |
print(native) | |
display = native.get_display() | |
print(display) | |
print(display.get_name()) | |
surface = native.get_surface() | |
print(surface) | |
# Some pointer magic. Accessing the internals. | |
ctypes.pythonapi.PyCapsule_GetPointer.restype = ctypes.c_void_p | |
ctypes.pythonapi.PyCapsule_GetPointer.argtypes = (ctypes.py_object,) | |
wl_surface = gtk.gdk_wayland_surface_get_wl_surface(ffi.cast("void *", ctypes.pythonapi.PyCapsule_GetPointer(surface.__gpointer__, None))) | |
print(wl_surface) | |
wl_display = gtk.gdk_wayland_display_get_wl_display(ffi.cast("void *", ctypes.pythonapi.PyCapsule_GetPointer(display.__gpointer__, None))) | |
print(wl_display) | |
wl_compositor = gtk.gdk_wayland_display_get_wl_compositor(ffi.cast("void *", ctypes.pythonapi.PyCapsule_GetPointer(display.__gpointer__, None))) | |
print(wl_compositor) | |
# We have a display! | |
d = pywayland.client.Display() | |
d._ptr = wl_display | |
# No need to d.connect() here (one should not) | |
compositor = None | |
subcompositor = None | |
def handler(registry, id_, interface, version): | |
nonlocal compositor, subcompositor | |
if interface == 'wl_compositor': | |
compositor = registry.bind(id_, pywayland.protocol.wayland.WlCompositor, version) | |
elif interface == 'wl_subcompositor': | |
subcompositor = registry.bind(id_, pywayland.protocol.wayland.WlSubcompositor, version) | |
registry = d.get_registry() | |
registry.dispatcher['global'] = handler | |
d.dispatch() | |
d.roundtrip() | |
assert(compositor) | |
assert(subcompositor) | |
s = compositor.create_surface() | |
r = compositor.create_region() | |
r.add(0, 0, WIDTH, HEIGHT) | |
s.set_opaque_region(r) | |
t = pywayland.protocol.wayland.WlSurface() | |
t._ptr = wl_surface | |
# The order is surface, parent. | |
subsurface = subcompositor.get_subsurface(s, t) | |
subsurface.set_position(X, Y) | |
subsurface.set_sync() | |
# vulkan starts here | |
# The vulkan setup and rendering part is adapted from here: | |
# https://github.com/realitix/vulkan/blob/master/example/contribs/example_wayland.py | |
# I've changed some of the print calls to use .format() and such. | |
appinfo = vulkan.VkApplicationInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_APPLICATION_INFO, | |
pApplicationName="Hello Triangle", | |
applicationVersion=vulkan.VK_MAKE_VERSION(1, 0, 0), | |
pEngineName="No Engine", | |
engineVersion=vulkan.VK_MAKE_VERSION(1, 0, 0), | |
apiVersion=vulkan.VK_API_VERSION_1_0) | |
extensions = vulkan.vkEnumerateInstanceExtensionProperties(None) | |
extensions = [e.extensionName for e in extensions] | |
print("available extensions:", *extensions) | |
layers = vulkan.vkEnumerateInstanceLayerProperties() | |
layers = [l.layerName for l in layers] | |
print("available layers:", *layers) | |
layers = []#'VK_LAYER_LUNARG_standard_validation'] # I don't have this one. | |
extensions = ['VK_KHR_surface', 'VK_EXT_debug_report', 'VK_KHR_wayland_surface'] | |
createinfo = vulkan.VkInstanceCreateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, | |
flags=0, | |
pApplicationInfo=appinfo, | |
enabledExtensionCount=len(extensions), | |
ppEnabledExtensionNames=extensions, | |
enabledLayerCount=len(layers), | |
ppEnabledLayerNames=layers) | |
instance = vulkan.vkCreateInstance(createinfo, None) | |
vkDestroySurfaceKHR = vulkan.vkGetInstanceProcAddr(instance, "vkDestroySurfaceKHR") | |
vkCreateWaylandSurfaceKHR = vulkan.vkGetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR") | |
surface_create = vulkan.VkWaylandSurfaceCreateInfoKHR( | |
sType=vulkan.VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR, | |
display=ffi.cast('void *', wl_display), | |
surface=ffi.cast('void *', s._ptr), | |
flags=0) | |
surface_vk_wayland = vkCreateWaylandSurfaceKHR(instance, surface_create, None) | |
physical_devices = vulkan.vkEnumeratePhysicalDevices(instance) | |
physical_devices_features = {physical_device: vulkan.vkGetPhysicalDeviceFeatures(physical_device) for physical_device in physical_devices} | |
physical_devices_properties = {physical_device: vulkan.vkGetPhysicalDeviceProperties(physical_device) for physical_device in physical_devices} | |
physical_device = physical_devices[0] | |
print("available devices:", *(p.deviceName for p in physical_devices_properties.values())) | |
print("selected device:", physical_devices_properties[physical_device].deviceName) | |
vkGetPhysicalDeviceSurfaceSupportKHR = vulkan.vkGetInstanceProcAddr(instance, 'vkGetPhysicalDeviceSurfaceSupportKHR') | |
queue_families = vulkan.vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice=physical_device) | |
print("{} available queue families".format(len(queue_families))) | |
queue_family_graphic_index = -1 | |
queue_family_present_index = -1 | |
for i, queue_family in enumerate(queue_families): | |
support_present = vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice=physical_device, | |
queueFamilyIndex=i, | |
surface=surface_vk_wayland) | |
if (queue_family.queueCount > 0 and queue_family.queueFlags & vulkan.VK_QUEUE_GRAPHICS_BIT): | |
queue_family_graphic_index = i | |
queue_family_present_index = i | |
break | |
print("index of selected queue families, graphics: {}, presentation: {}".format(queue_family_graphic_index, queue_family_present_index)) | |
extensions = vulkan.vkEnumerateDeviceExtensionProperties(physicalDevice=physical_device, pLayerName=None) | |
extensions = [e.extensionName for e in extensions] | |
print("available device extensions:", *extensions) | |
extensions = [vulkan.VK_KHR_SWAPCHAIN_EXTENSION_NAME] | |
queues_create = [vulkan.VkDeviceQueueCreateInfo(sType=vulkan.VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, | |
queueFamilyIndex=i, | |
queueCount=1, | |
pQueuePriorities=[1], | |
flags=0) | |
for i in {queue_family_graphic_index, | |
queue_family_present_index}] | |
device_create = vulkan.VkDeviceCreateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, | |
pQueueCreateInfos=queues_create, | |
queueCreateInfoCount=len(queues_create), | |
pEnabledFeatures=physical_devices_features[physical_device], | |
flags=0, | |
enabledLayerCount=len(layers), | |
ppEnabledLayerNames=layers, | |
enabledExtensionCount=len(extensions), | |
ppEnabledExtensionNames=extensions) | |
logical_device = vulkan.vkCreateDevice(physical_device, device_create, None) | |
graphic_queue = vulkan.vkGetDeviceQueue( | |
device=logical_device, | |
queueFamilyIndex=queue_family_graphic_index, | |
queueIndex=0) | |
presentation_queue = vulkan.vkGetDeviceQueue( | |
device=logical_device, | |
queueFamilyIndex=queue_family_present_index, | |
queueIndex=0) | |
print("Logical device and graphic queue successfully created") | |
# ---------- | |
# Create swapchain | |
vkGetPhysicalDeviceSurfaceCapabilitiesKHR = vulkan.vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR") | |
vkGetPhysicalDeviceSurfaceFormatsKHR = vulkan.vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfaceFormatsKHR") | |
vkGetPhysicalDeviceSurfacePresentModesKHR = vulkan.vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceSurfacePresentModesKHR") | |
surface_capabilities = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice=physical_device, surface=surface_vk_wayland) | |
surface_formats = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice=physical_device, surface=surface_vk_wayland) | |
surface_present_modes = vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice=physical_device, surface=surface_vk_wayland) | |
if not surface_formats or not surface_present_modes: | |
raise Exception('No available swapchain') | |
def get_surface_format(formats): | |
for f in formats: | |
if f.format == vulkan.VK_FORMAT_UNDEFINED: | |
return f | |
if (f.format == vulkan.VK_FORMAT_B8G8R8A8_UNORM and | |
f.colorSpace == vulkan.VK_COLOR_SPACE_SRGB_NONLINEAR_KHR): | |
return f | |
return formats[0] | |
def get_surface_present_mode(present_modes): | |
for p in present_modes: | |
if p == vulkan.VK_PRESENT_MODE_MAILBOX_KHR: | |
return p | |
return vulkan.VK_PRESENT_MODE_FIFO_KHR | |
def get_swap_extent(capabilities): | |
#uint32_max = 4294967295 | |
#if capabilities.currentExtent.width != uint32_max: | |
# return vulkan.VkExtent2D(width=capabilities.currentExtent.width, | |
# height=capabilities.currentExtent.height) | |
# | |
width = max( | |
capabilities.minImageExtent.width, | |
min(capabilities.maxImageExtent.width, WIDTH)) | |
height = max( | |
capabilities.minImageExtent.height, | |
min(capabilities.maxImageExtent.height, HEIGHT)) | |
actualExtent = vulkan.VkExtent2D(width=width, height=height); | |
return actualExtent | |
surface_format = get_surface_format(surface_formats) | |
present_mode = get_surface_present_mode(surface_present_modes) | |
extent = get_swap_extent(surface_capabilities) | |
imageCount = surface_capabilities.minImageCount + 1; | |
if surface_capabilities.maxImageCount > 0 and imageCount > surface_capabilities.maxImageCount: | |
imageCount = surface_capabilities.maxImageCount | |
print('selected format: {}'.format(surface_format.format)) | |
print('{} available swapchain present modes'.format(len(surface_present_modes))) | |
imageSharingMode = vulkan.VK_SHARING_MODE_EXCLUSIVE | |
queueFamilyIndexCount = 0 | |
pQueueFamilyIndices = None | |
if queue_family_graphic_index != queue_family_present_index: | |
imageSharingMode = vulkan.VK_SHARING_MODE_CONCURRENT | |
queueFamilyIndexCount = 2 | |
pQueueFamilyIndices = [queue_family_graphic_index, queue_family_present_index] | |
vkCreateSwapchainKHR = vulkan.vkGetInstanceProcAddr(instance, 'vkCreateSwapchainKHR') | |
vkDestroySwapchainKHR = vulkan.vkGetInstanceProcAddr(instance, 'vkDestroySwapchainKHR') | |
vkGetSwapchainImagesKHR = vulkan.vkGetInstanceProcAddr(instance, 'vkGetSwapchainImagesKHR') | |
swapchain_create = vulkan.VkSwapchainCreateInfoKHR( | |
sType=vulkan.VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, | |
flags=0, | |
surface=surface_vk_wayland, | |
minImageCount=imageCount, | |
imageFormat=surface_format.format, | |
imageColorSpace=surface_format.colorSpace, | |
imageExtent=extent, | |
imageArrayLayers=1, | |
imageUsage=vulkan.VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, | |
imageSharingMode=imageSharingMode, | |
queueFamilyIndexCount=queueFamilyIndexCount, | |
pQueueFamilyIndices=pQueueFamilyIndices, | |
compositeAlpha=vulkan.VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, | |
presentMode=present_mode, | |
clipped=vulkan.VK_TRUE, | |
oldSwapchain=None, | |
preTransform=surface_capabilities.currentTransform) | |
swapchain = vkCreateSwapchainKHR(logical_device, swapchain_create, None) | |
swapchain_images = vkGetSwapchainImagesKHR(logical_device, swapchain) | |
# Create image view for each image in swapchain | |
image_views = [] | |
for image in swapchain_images: | |
subresourceRange = vulkan.VkImageSubresourceRange( | |
aspectMask=vulkan.VK_IMAGE_ASPECT_COLOR_BIT, | |
baseMipLevel=0, | |
levelCount=1, | |
baseArrayLayer=0, | |
layerCount=1) | |
components = vulkan.VkComponentMapping( | |
r=vulkan.VK_COMPONENT_SWIZZLE_IDENTITY, | |
g=vulkan.VK_COMPONENT_SWIZZLE_IDENTITY, | |
b=vulkan.VK_COMPONENT_SWIZZLE_IDENTITY, | |
a=vulkan.VK_COMPONENT_SWIZZLE_IDENTITY) | |
imageview_create = vulkan.VkImageViewCreateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | |
image=image, | |
flags=0, | |
viewType=vulkan.VK_IMAGE_VIEW_TYPE_2D, | |
format=surface_format.format, | |
components=components, | |
subresourceRange=subresourceRange) | |
image_views.append(vulkan.vkCreateImageView(logical_device, imageview_create, None)) | |
print("{} image views created".format(len(image_views))) | |
# Load spirv shader | |
path = os.path.dirname(os.path.abspath(__file__)) | |
with open(os.path.join(path, "vert.spv"), 'rb') as f: | |
vert_shader_spirv = f.read() | |
with open(os.path.join(path, "frag.spv"), 'rb') as f: | |
frag_shader_spirv = f.read() | |
# Added note: get the spirv files from here: | |
# https://github.com/realitix/vulkan/tree/master/example/contribs | |
# Create shader | |
vert_shader_create = vulkan.VkShaderModuleCreateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, | |
flags=0, | |
codeSize=len(vert_shader_spirv), | |
pCode=vert_shader_spirv) | |
vert_shader_module = vulkan.vkCreateShaderModule(logical_device, vert_shader_create, None) | |
frag_shader_create = vulkan.VkShaderModuleCreateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, | |
flags=0, | |
codeSize=len(frag_shader_spirv), | |
pCode=frag_shader_spirv) | |
frag_shader_module = vulkan.vkCreateShaderModule(logical_device, frag_shader_create, None) | |
# Create shader stage | |
vert_stage_create = vulkan.VkPipelineShaderStageCreateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | |
stage=vulkan.VK_SHADER_STAGE_VERTEX_BIT, | |
module=vert_shader_module, | |
flags=0, | |
pSpecializationInfo=None, | |
pName='main') | |
frag_stage_create = vulkan.VkPipelineShaderStageCreateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | |
stage=vulkan.VK_SHADER_STAGE_FRAGMENT_BIT, | |
module=frag_shader_module, | |
flags=0, | |
pSpecializationInfo=None, | |
pName='main') | |
# Create render pass | |
color_attachment = vulkan.VkAttachmentDescription( | |
flags=0, | |
format=surface_format.format, | |
samples=vulkan.VK_SAMPLE_COUNT_1_BIT, | |
loadOp=vulkan.VK_ATTACHMENT_LOAD_OP_CLEAR, | |
storeOp=vulkan.VK_ATTACHMENT_STORE_OP_STORE, | |
stencilLoadOp=vulkan.VK_ATTACHMENT_LOAD_OP_DONT_CARE, | |
stencilStoreOp=vulkan.VK_ATTACHMENT_STORE_OP_DONT_CARE, | |
initialLayout=vulkan.VK_IMAGE_LAYOUT_UNDEFINED, | |
finalLayout=vulkan.VK_IMAGE_LAYOUT_PRESENT_SRC_KHR) | |
color_attachment_reference = vulkan.VkAttachmentReference( | |
attachment=0, | |
layout=vulkan.VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) | |
sub_pass = vulkan.VkSubpassDescription( | |
flags=0, | |
pipelineBindPoint=vulkan.VK_PIPELINE_BIND_POINT_GRAPHICS, | |
inputAttachmentCount=0, | |
pInputAttachments=None, | |
pResolveAttachments=None, | |
pDepthStencilAttachment=None, | |
preserveAttachmentCount=0, | |
pPreserveAttachments=None, | |
colorAttachmentCount=1, | |
pColorAttachments=[color_attachment_reference]) | |
dependency = vulkan.VkSubpassDependency( | |
dependencyFlags=0, | |
srcSubpass=vulkan.VK_SUBPASS_EXTERNAL, | |
dstSubpass=0, | |
srcStageMask=vulkan.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, | |
srcAccessMask=0, | |
dstStageMask=vulkan.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, | |
dstAccessMask=vulkan.VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | vulkan.VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT) | |
render_pass_create = vulkan.VkRenderPassCreateInfo( | |
flags=0, | |
sType=vulkan.VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, | |
attachmentCount=1, | |
pAttachments=[color_attachment], | |
subpassCount=1, | |
pSubpasses=[sub_pass], | |
dependencyCount=1, | |
pDependencies=[dependency]) | |
render_pass = vulkan.vkCreateRenderPass(logical_device, render_pass_create, None) | |
# Create graphic pipeline | |
vertex_input_create = vulkan.VkPipelineVertexInputStateCreateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, | |
flags=0, | |
vertexBindingDescriptionCount=0, | |
pVertexBindingDescriptions=None, | |
vertexAttributeDescriptionCount=0, | |
pVertexAttributeDescriptions=None) | |
input_assembly_create = vulkan.VkPipelineInputAssemblyStateCreateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, | |
flags=0, | |
topology=vulkan.VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, | |
primitiveRestartEnable=vulkan.VK_FALSE) | |
viewport = vulkan.VkViewport( | |
x=0, y=0, width=float(extent.width), height=float(extent.height), | |
minDepth=0., maxDepth=1.) | |
scissor_offset = vulkan.VkOffset2D(x=0, y=0) | |
scissor = vulkan.VkRect2D(offset=scissor_offset, extent=extent) | |
viewport_state_create = vulkan.VkPipelineViewportStateCreateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, | |
flags=0, | |
viewportCount=1, | |
pViewports=[viewport], | |
scissorCount=1, | |
pScissors=[scissor]) | |
rasterizer_create = vulkan.VkPipelineRasterizationStateCreateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, | |
flags=0, | |
depthClampEnable=vulkan.VK_FALSE, | |
rasterizerDiscardEnable=vulkan.VK_FALSE, | |
polygonMode=vulkan.VK_POLYGON_MODE_FILL, | |
lineWidth=1, | |
cullMode=vulkan.VK_CULL_MODE_BACK_BIT, | |
frontFace=vulkan.VK_FRONT_FACE_CLOCKWISE, | |
depthBiasEnable=vulkan.VK_FALSE, | |
depthBiasConstantFactor=0., | |
depthBiasClamp=0., | |
depthBiasSlopeFactor=0.) | |
multisample_create = vulkan.VkPipelineMultisampleStateCreateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, | |
flags=0, | |
sampleShadingEnable=vulkan.VK_FALSE, | |
rasterizationSamples=vulkan.VK_SAMPLE_COUNT_1_BIT, | |
minSampleShading=1, | |
pSampleMask=None, | |
alphaToCoverageEnable=vulkan.VK_FALSE, | |
alphaToOneEnable=vulkan.VK_FALSE) | |
color_blend_attachment = vulkan.VkPipelineColorBlendAttachmentState( | |
colorWriteMask=vulkan.VK_COLOR_COMPONENT_R_BIT | vulkan.VK_COLOR_COMPONENT_G_BIT | vulkan.VK_COLOR_COMPONENT_B_BIT | vulkan.VK_COLOR_COMPONENT_A_BIT, | |
blendEnable=vulkan.VK_FALSE, | |
srcColorBlendFactor=vulkan.VK_BLEND_FACTOR_ONE, | |
dstColorBlendFactor=vulkan.VK_BLEND_FACTOR_ZERO, | |
colorBlendOp=vulkan.VK_BLEND_OP_ADD, | |
srcAlphaBlendFactor=vulkan.VK_BLEND_FACTOR_ONE, | |
dstAlphaBlendFactor=vulkan.VK_BLEND_FACTOR_ZERO, | |
alphaBlendOp=vulkan.VK_BLEND_OP_ADD) | |
color_blend_create = vulkan.VkPipelineColorBlendStateCreateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, | |
flags=0, | |
logicOpEnable=vulkan.VK_FALSE, | |
logicOp=vulkan.VK_LOGIC_OP_COPY, | |
attachmentCount=1, | |
pAttachments=[color_blend_attachment], | |
blendConstants=[0, 0, 0, 0]) | |
push_constant_ranges = vulkan.VkPushConstantRange( | |
stageFlags=0, | |
offset=0, | |
size=0) | |
pipeline_layout_create = vulkan.VkPipelineLayoutCreateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | |
flags=0, | |
setLayoutCount=0, | |
pSetLayouts=None, | |
pushConstantRangeCount=0, | |
pPushConstantRanges=[push_constant_ranges]) | |
pipeline_layout = vulkan.vkCreatePipelineLayout(logical_device, pipeline_layout_create, None) | |
# Finally create graphic pipeline | |
pipeline_create = vulkan.VkGraphicsPipelineCreateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | |
flags=0, | |
stageCount=2, | |
pStages=[vert_stage_create, frag_stage_create], | |
pVertexInputState=vertex_input_create, | |
pInputAssemblyState=input_assembly_create, | |
pTessellationState=None, | |
pViewportState=viewport_state_create, | |
pRasterizationState=rasterizer_create, | |
pMultisampleState=multisample_create, | |
pDepthStencilState=None, | |
pColorBlendState=color_blend_create, | |
pDynamicState=None, | |
layout=pipeline_layout, | |
renderPass=render_pass, | |
subpass=0, | |
basePipelineHandle=None, | |
basePipelineIndex=-1) | |
pipeline = vulkan.vkCreateGraphicsPipelines(logical_device, None, 1, [pipeline_create], None) | |
# Framebuffers creation | |
framebuffers = [] | |
for image in image_views: | |
attachments = [image] | |
framebuffer_create = vulkan.VkFramebufferCreateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, | |
flags=0, | |
renderPass=render_pass, | |
attachmentCount=len(attachments), | |
pAttachments=attachments, | |
width=extent.width, | |
height=extent.height, | |
layers=1) | |
framebuffers.append( | |
vulkan.vkCreateFramebuffer(logical_device, framebuffer_create, None)) | |
# Create command pools | |
command_pool_create = vulkan.VkCommandPoolCreateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, | |
queueFamilyIndex=queue_family_graphic_index, | |
flags=0) | |
command_pool = vulkan.vkCreateCommandPool(logical_device, command_pool_create, None) | |
# Create command buffers | |
command_buffers_create = vulkan.VkCommandBufferAllocateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, | |
commandPool=command_pool, | |
level=vulkan.VK_COMMAND_BUFFER_LEVEL_PRIMARY, | |
commandBufferCount=len(framebuffers)) | |
command_buffers = vulkan.vkAllocateCommandBuffers(logical_device, command_buffers_create) | |
# Record command buffer | |
for i, command_buffer in enumerate(command_buffers): | |
command_buffer_begin_create = vulkan.VkCommandBufferBeginInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | |
flags=vulkan.VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, | |
pInheritanceInfo=None) | |
vulkan.vkBeginCommandBuffer(command_buffer, command_buffer_begin_create) | |
# Create render pass | |
render_area = vulkan.VkRect2D(offset=vulkan.VkOffset2D(x=0, y=0), | |
extent=extent) | |
color = vulkan.VkClearColorValue(float32=[0, 1, 0, 1]) | |
clear_value = vulkan.VkClearValue(color=color) | |
render_pass_begin_create = vulkan.VkRenderPassBeginInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, | |
renderPass=render_pass, | |
framebuffer=framebuffers[i], | |
renderArea=render_area, | |
clearValueCount=1, | |
pClearValues=[clear_value]) | |
vulkan.vkCmdBeginRenderPass(command_buffer, render_pass_begin_create, vulkan.VK_SUBPASS_CONTENTS_INLINE) | |
# Bind pipeline | |
vulkan.vkCmdBindPipeline(command_buffer, vulkan.VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline[0]) | |
vulkan.vkCmdSetViewport(command_buffer, 0, 1, [viewport]) | |
# Draw | |
vulkan.vkCmdDraw(command_buffer, 3, 1, 0, 0) | |
# End | |
vulkan.vkCmdEndRenderPass(command_buffer) | |
vulkan.vkEndCommandBuffer(command_buffer) | |
# Create semaphore | |
semaphore_create = vulkan.VkSemaphoreCreateInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, | |
flags=0) | |
semaphore_image_available = vulkan.vkCreateSemaphore(logical_device, semaphore_create, None) | |
semaphore_render_finished = vulkan.vkCreateSemaphore(logical_device, semaphore_create, None) | |
vkAcquireNextImageKHR = vulkan.vkGetInstanceProcAddr(instance, "vkAcquireNextImageKHR") | |
vkQueuePresentKHR = vulkan.vkGetInstanceProcAddr(instance, "vkQueuePresentKHR") | |
wait_semaphores = [semaphore_image_available] | |
wait_stages = [vulkan.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT] | |
signal_semaphores = [semaphore_render_finished] | |
submit_create = vulkan.VkSubmitInfo( | |
sType=vulkan.VK_STRUCTURE_TYPE_SUBMIT_INFO, | |
waitSemaphoreCount=len(wait_semaphores), | |
pWaitSemaphores=wait_semaphores, | |
pWaitDstStageMask=wait_stages, | |
commandBufferCount=1, | |
pCommandBuffers=[command_buffers[0]], | |
signalSemaphoreCount=len(signal_semaphores), | |
pSignalSemaphores=signal_semaphores) | |
present_create = vulkan.VkPresentInfoKHR( | |
sType=vulkan.VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, | |
waitSemaphoreCount=1, | |
pWaitSemaphores=signal_semaphores, | |
swapchainCount=1, | |
pSwapchains=[swapchain], | |
pImageIndices=[0], | |
pResults=None) | |
# optimization to avoid creating a new array each time | |
submit_list = vulkan.ffi.new('VkSubmitInfo[1]', [submit_create]) | |
# Note: above we are calling new from *vulkan.ffi*. Lots of ffi's around in this file. Count them. | |
def draw_frame(): | |
try: | |
image_index = vkAcquireNextImageKHR(logical_device, swapchain, vulkan.UINT64_MAX, semaphore_image_available, None) | |
except vulkan.VkNotReady: | |
print('not ready') | |
return | |
submit_create.pCommandBuffers[0] = command_buffers[image_index] | |
vulkan.vkQueueSubmit(graphic_queue, 1, submit_list, None) | |
present_create.pImageIndices[0] = image_index | |
vkQueuePresentKHR(presentation_queue, present_create) | |
# Moved the clean up stuff to be done here in this callback. | |
def on_close_request(x): | |
# ---------- | |
# Clean everything | |
vulkan.vkDestroySemaphore(logical_device, semaphore_image_available, None) | |
vulkan.vkDestroySemaphore(logical_device, semaphore_render_finished, None) | |
vulkan.vkDestroyCommandPool(logical_device, command_pool, None) | |
for f in framebuffers: | |
vulkan.vkDestroyFramebuffer(logical_device, f, None) | |
vulkan.vkDestroyPipeline(logical_device, pipeline[0], None) | |
vulkan.vkDestroyPipelineLayout(logical_device, pipeline_layout, None) | |
vulkan.vkDestroyRenderPass(logical_device, render_pass, None) | |
vulkan.vkDestroyShaderModule(logical_device, frag_shader_module, None) | |
vulkan.vkDestroyShaderModule(logical_device, vert_shader_module, None) | |
for i in image_views: | |
vulkan.vkDestroyImageView(logical_device, i, None) | |
vkDestroySwapchainKHR(logical_device, swapchain, None) | |
vulkan.vkDestroyDevice(logical_device, None) | |
vkDestroySurfaceKHR(instance, surface_vk_wayland, None) | |
#vulkan.vkDestroyDebugReportCallbackEXT(instance, callback, None) | |
vulkan.vkDestroyInstance(instance, None) | |
win.connect('close-request', on_close_request) | |
# Main loop | |
if sys.version_info >= (3, 3): | |
clock = time.perf_counter | |
else: | |
clock = time.clock | |
running = True | |
last_time = clock() * 1000 | |
fps = 0 | |
# I modified the main loop into a draw callback. | |
# Still printing fps though, but instead of 30000 it's 60! | |
def draw(*args): | |
nonlocal running, last_time, clock, fps | |
if running: | |
fps += 1 | |
if clock() * 1000 - last_time >= 1000: | |
last_time = clock() * 1000 | |
print("FPS: %s" % fps) | |
fps = 0 | |
draw_frame() | |
return True | |
return False | |
win.add_tick_callback(draw) | |
# Here we create our application and run it! | |
app = Gtk.Application(application_id='com.github.omnp.Example4') | |
app.connect('activate', on_activate) | |
app.run(None) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Screenshot image!
