Skip to content

Instantly share code, notes, and snippets.

@omnp
Created August 3, 2023 19:04
Show Gist options
  • Select an option

  • Save omnp/6ac3385e2b3f6cab987d84e6477e636a to your computer and use it in GitHub Desktop.

Select an option

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.
# 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)
@omnp
Copy link
Copy Markdown
Author

omnp commented Aug 3, 2023

Screenshot image!
screenshot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment