diff options
Diffstat (limited to 'vendor/gioui.org/app/vulkan.go')
-rw-r--r-- | vendor/gioui.org/app/vulkan.go | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/vendor/gioui.org/app/vulkan.go b/vendor/gioui.org/app/vulkan.go new file mode 100644 index 0000000..630c254 --- /dev/null +++ b/vendor/gioui.org/app/vulkan.go @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: Unlicense OR MIT + +//go:build (linux || freebsd) && !novulkan +// +build linux freebsd +// +build !novulkan + +package app + +import ( + "errors" + "unsafe" + + "gioui.org/gpu" + "gioui.org/internal/vk" +) + +type vkContext struct { + physDev vk.PhysicalDevice + inst vk.Instance + dev vk.Device + queueFam int + queue vk.Queue + acquireSem vk.Semaphore + presentSem vk.Semaphore + + swchain vk.Swapchain + imgs []vk.Image + views []vk.ImageView + fbos []vk.Framebuffer + format vk.Format + presentIdx int +} + +func newVulkanContext(inst vk.Instance, surf vk.Surface) (*vkContext, error) { + physDev, qFam, err := vk.ChoosePhysicalDevice(inst, surf) + if err != nil { + return nil, err + } + dev, err := vk.CreateDeviceAndQueue(physDev, qFam, "VK_KHR_swapchain") + if err != nil { + return nil, err + } + if err != nil { + vk.DestroyDevice(dev) + return nil, err + } + acquireSem, err := vk.CreateSemaphore(dev) + if err != nil { + vk.DestroyDevice(dev) + return nil, err + } + presentSem, err := vk.CreateSemaphore(dev) + if err != nil { + vk.DestroySemaphore(dev, acquireSem) + vk.DestroyDevice(dev) + return nil, err + } + c := &vkContext{ + physDev: physDev, + inst: inst, + dev: dev, + queueFam: qFam, + queue: vk.GetDeviceQueue(dev, qFam, 0), + acquireSem: acquireSem, + presentSem: presentSem, + } + return c, nil +} + +func (c *vkContext) RenderTarget() (gpu.RenderTarget, error) { + vk.DeviceWaitIdle(c.dev) + + imgIdx, err := vk.AcquireNextImage(c.dev, c.swchain, c.acquireSem, 0) + if err := mapSurfaceErr(err); err != nil { + return nil, err + } + c.presentIdx = imgIdx + return gpu.VulkanRenderTarget{ + WaitSem: uint64(c.acquireSem), + SignalSem: uint64(c.presentSem), + Framebuffer: uint64(c.fbos[imgIdx]), + Image: uint64(c.imgs[imgIdx]), + }, nil +} + +func (c *vkContext) api() gpu.API { + return gpu.Vulkan{ + PhysDevice: unsafe.Pointer(c.physDev), + Device: unsafe.Pointer(c.dev), + Format: int(c.format), + QueueFamily: c.queueFam, + QueueIndex: 0, + } +} + +func mapErr(err error) error { + var vkErr vk.Error + if errors.As(err, &vkErr) && vkErr == vk.ERROR_DEVICE_LOST { + return gpu.ErrDeviceLost + } + return err +} + +func mapSurfaceErr(err error) error { + var vkErr vk.Error + if !errors.As(err, &vkErr) { + return err + } + switch { + case vkErr == vk.SUBOPTIMAL_KHR: + // Android reports VK_SUBOPTIMAL_KHR when presenting to a rotated + // swapchain (preTransform != currentTransform). However, we don't + // support transforming the output ourselves, so we'll live with it. + return nil + case vkErr == vk.ERROR_OUT_OF_DATE_KHR: + return errOutOfDate + case vkErr == vk.ERROR_SURFACE_LOST_KHR: + // Treating a lost surface as a lost device isn't accurate, but + // probably not worth optimizing. + return gpu.ErrDeviceLost + } + return mapErr(err) +} + +func (c *vkContext) release() { + vk.DeviceWaitIdle(c.dev) + + c.destroySwapchain() + vk.DestroySemaphore(c.dev, c.acquireSem) + vk.DestroySemaphore(c.dev, c.presentSem) + vk.DestroyDevice(c.dev) + *c = vkContext{} +} + +func (c *vkContext) present() error { + return mapSurfaceErr(vk.PresentQueue(c.queue, c.swchain, c.presentSem, c.presentIdx)) +} + +func (c *vkContext) destroyImageViews() { + for _, f := range c.fbos { + vk.DestroyFramebuffer(c.dev, f) + } + c.fbos = nil + for _, view := range c.views { + vk.DestroyImageView(c.dev, view) + } + c.views = nil +} + +func (c *vkContext) destroySwapchain() { + vk.DeviceWaitIdle(c.dev) + + c.destroyImageViews() + if c.swchain != 0 { + vk.DestroySwapchain(c.dev, c.swchain) + c.swchain = 0 + } +} + +func (c *vkContext) refresh(surf vk.Surface, width, height int) error { + vk.DeviceWaitIdle(c.dev) + + c.destroyImageViews() + // Check whether size is valid. That's needed on X11, where ConfigureNotify + // is not always synchronized with the window extent. + caps, err := vk.GetPhysicalDeviceSurfaceCapabilities(c.physDev, surf) + if err != nil { + return err + } + minExt, maxExt := caps.MinExtent(), caps.MaxExtent() + if width < minExt.X || maxExt.X < width || height < minExt.Y || maxExt.Y < height { + return errOutOfDate + } + swchain, imgs, format, err := vk.CreateSwapchain(c.physDev, c.dev, surf, width, height, c.swchain) + if c.swchain != 0 { + vk.DestroySwapchain(c.dev, c.swchain) + c.swchain = 0 + } + if err := mapSurfaceErr(err); err != nil { + return err + } + c.swchain = swchain + c.imgs = imgs + c.format = format + pass, err := vk.CreateRenderPass( + c.dev, + format, + vk.ATTACHMENT_LOAD_OP_CLEAR, + vk.IMAGE_LAYOUT_UNDEFINED, + vk.IMAGE_LAYOUT_PRESENT_SRC_KHR, + nil, + ) + if err := mapErr(err); err != nil { + return err + } + defer vk.DestroyRenderPass(c.dev, pass) + for _, img := range imgs { + view, err := vk.CreateImageView(c.dev, img, format) + if err := mapErr(err); err != nil { + return err + } + c.views = append(c.views, view) + fbo, err := vk.CreateFramebuffer(c.dev, pass, view, width, height) + if err := mapErr(err); err != nil { + return err + } + c.fbos = append(c.fbos, fbo) + } + return nil +} |