forked from joejulian/gio
94f7fa3218
Before this change, it was unclear who owned the platform specific VkSurfaceKHR object, leading to a double-free in the error path for devices with no Vulkan support. This change moves the ownership to the platform specific code. Add vk.EnumeratePhysicalDevices while here (refactor was part of debugging of the double-free). Signed-off-by: Elias Naur <mail@eliasnaur.com>
189 lines
4.0 KiB
Go
189 lines
4.0 KiB
Go
// 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 != nil {
|
|
return nil, mapErr(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) {
|
|
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.IsDeviceLost():
|
|
return gpu.ErrDeviceLost
|
|
}
|
|
return 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 mapErr(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()
|
|
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 != nil {
|
|
return mapErr(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 != nil {
|
|
return mapErr(err)
|
|
}
|
|
defer vk.DestroyRenderPass(c.dev, pass)
|
|
for _, img := range imgs {
|
|
view, err := vk.CreateImageView(c.dev, img, format)
|
|
if err != nil {
|
|
return mapErr(err)
|
|
}
|
|
c.views = append(c.views, view)
|
|
fbo, err := vk.CreateFramebuffer(c.dev, pass, view, width, height)
|
|
if err != nil {
|
|
return mapErr(err)
|
|
}
|
|
c.fbos = append(c.fbos, fbo)
|
|
}
|
|
return nil
|
|
}
|