123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- // 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
- fence vk.Fence
- 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
- }
- 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
- }
- fence, err := vk.CreateFence(dev, vk.FENCE_CREATE_SIGNALED_BIT)
- if err != nil {
- vk.DestroySemaphore(dev, presentSem)
- 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,
- fence: fence,
- }
- return c, nil
- }
- func (c *vkContext) RenderTarget() (gpu.RenderTarget, error) {
- vk.WaitForFences(c.dev, c.fence)
- vk.ResetFences(c.dev, c.fence)
- 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),
- Fence: uint64(c.fence),
- 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.DestroyFence(c.dev, c.fence)
- 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 := vk.SurfaceCapabilitiesMinExtent(caps), vk.SurfaceCapabilitiesMaxExtent(caps)
- 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
- }
|