mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-06 01:45:36 +00:00
app,gpu,internal/vk: add Vulkan port for Wayland, X11, Android
This change implements a Vulkan port for the two renderers, old and compute. Run with GIORENDERER=forcecompute to test the compute renderer. To shake out bugs faster, it is also made the default on systems that support it. To disable Vulkan and force the use of OpenGL, use the `novulkan` tag: $ go run -tags novulkan gioui.org/example/kitchen Don't forget to file an issue describing the issue that prompted the use of the tag. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+10
-8
@@ -21,12 +21,14 @@ type androidContext struct {
|
||||
*egl.Context
|
||||
}
|
||||
|
||||
func (w *window) NewContext() (context, error) {
|
||||
ctx, err := egl.NewContext(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func init() {
|
||||
newAndroidGLESContext = func(w *window) (context, error) {
|
||||
ctx, err := egl.NewContext(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &androidContext{win: w, Context: ctx}, nil
|
||||
}
|
||||
return &androidContext{win: w, Context: ctx}, nil
|
||||
}
|
||||
|
||||
func (c *androidContext) Release() {
|
||||
@@ -38,10 +40,10 @@ func (c *androidContext) Release() {
|
||||
|
||||
func (c *androidContext) Refresh() error {
|
||||
c.Context.ReleaseSurface()
|
||||
win, width, height := c.win.nativeWindow(c.Context.VisualID())
|
||||
if win == nil {
|
||||
return nil
|
||||
if err := c.win.setVisual(c.Context.VisualID()); err != nil {
|
||||
return err
|
||||
}
|
||||
win, width, height := c.win.nativeWindow()
|
||||
c.eglSurf = egl.NativeWindowType(unsafe.Pointer(win))
|
||||
c.width, c.height = width, height
|
||||
return nil
|
||||
|
||||
+8
-6
@@ -30,13 +30,15 @@ type wlContext struct {
|
||||
eglWin *C.struct_wl_egl_window
|
||||
}
|
||||
|
||||
func (w *window) NewContext() (context, error) {
|
||||
disp := egl.NativeDisplayType(unsafe.Pointer(w.display()))
|
||||
ctx, err := egl.NewContext(disp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func init() {
|
||||
newWaylandEGLContext = func(w *window) (context, error) {
|
||||
disp := egl.NativeDisplayType(unsafe.Pointer(w.display()))
|
||||
ctx, err := egl.NewContext(disp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &wlContext{Context: ctx, win: w}, nil
|
||||
}
|
||||
return &wlContext{Context: ctx, win: w}, nil
|
||||
}
|
||||
|
||||
func (c *wlContext) Release() {
|
||||
|
||||
+8
-6
@@ -17,13 +17,15 @@ type x11Context struct {
|
||||
*egl.Context
|
||||
}
|
||||
|
||||
func (w *x11Window) NewContext() (context, error) {
|
||||
disp := egl.NativeDisplayType(unsafe.Pointer(w.display()))
|
||||
ctx, err := egl.NewContext(disp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func init() {
|
||||
newX11EGLContext = func(w *x11Window) (context, error) {
|
||||
disp := egl.NativeDisplayType(unsafe.Pointer(w.display()))
|
||||
ctx, err := egl.NewContext(disp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &x11Context{win: w, Context: ctx}, nil
|
||||
}
|
||||
return &x11Context{win: w, Context: ctx}, nil
|
||||
}
|
||||
|
||||
func (c *x11Context) Release() {
|
||||
|
||||
+36
-9
@@ -211,6 +211,33 @@ var (
|
||||
dataPath string
|
||||
)
|
||||
|
||||
var (
|
||||
newAndroidVulkanContext func(w *window) (context, error)
|
||||
newAndroidGLESContext func(w *window) (context, error)
|
||||
)
|
||||
|
||||
func (w *window) NewContext() (context, error) {
|
||||
funcs := []func(w *window) (context, error){newAndroidVulkanContext, newAndroidGLESContext}
|
||||
var firstErr error
|
||||
for _, f := range funcs {
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
c, err := f(w)
|
||||
if err != nil {
|
||||
if firstErr == nil {
|
||||
firstErr = err
|
||||
}
|
||||
continue
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
if firstErr != nil {
|
||||
return nil, firstErr
|
||||
}
|
||||
return nil, errors.New("x11: no available GPU backends")
|
||||
}
|
||||
|
||||
func dataDir() (string, error) {
|
||||
dataDirOnce.Do(func() {
|
||||
dataPath = <-dataDirChan
|
||||
@@ -471,16 +498,16 @@ func (w *window) setStage(stage system.Stage) {
|
||||
w.callbacks.Event(system.StageEvent{stage})
|
||||
}
|
||||
|
||||
func (w *window) nativeWindow(visID int) (*C.ANativeWindow, int, int) {
|
||||
var width, height int
|
||||
if w.win != nil {
|
||||
if C.ANativeWindow_setBuffersGeometry(w.win, 0, 0, C.int32_t(visID)) != 0 {
|
||||
panic(errors.New("ANativeWindow_setBuffersGeometry failed"))
|
||||
}
|
||||
w, h := C.ANativeWindow_getWidth(w.win), C.ANativeWindow_getHeight(w.win)
|
||||
width, height = int(w), int(h)
|
||||
func (w *window) setVisual(visID int) error {
|
||||
if C.ANativeWindow_setBuffersGeometry(w.win, 0, 0, C.int32_t(visID)) != 0 {
|
||||
return errors.New("ANativeWindow_setBuffersGeometry failed")
|
||||
}
|
||||
return w.win, width, height
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *window) nativeWindow() (*C.ANativeWindow, int, int) {
|
||||
width, height := C.ANativeWindow_getWidth(w.win), C.ANativeWindow_getHeight(w.win)
|
||||
return w.win, int(width), int(height)
|
||||
}
|
||||
|
||||
func (w *window) loadConfig(env *C.JNIEnv, class C.jclass) {
|
||||
|
||||
@@ -220,6 +220,11 @@ var callbackMap sync.Map
|
||||
// order of preference.
|
||||
var clipboardMimeTypes = []string{"text/plain;charset=utf8", "UTF8_STRING", "text/plain", "TEXT", "STRING"}
|
||||
|
||||
var (
|
||||
newWaylandEGLContext func(w *window) (context, error)
|
||||
newWaylandVulkanContext func(w *window) (context, error)
|
||||
)
|
||||
|
||||
func init() {
|
||||
wlDriver = newWLWindow
|
||||
}
|
||||
@@ -1476,6 +1481,28 @@ func (w *window) SetInputHint(_ key.InputHint) {}
|
||||
// Close the window. Not implemented for Wayland.
|
||||
func (w *window) Close() {}
|
||||
|
||||
func (w *window) NewContext() (context, error) {
|
||||
var firstErr error
|
||||
if f := newWaylandVulkanContext; f != nil {
|
||||
c, err := f(w)
|
||||
if err == nil {
|
||||
return c, nil
|
||||
}
|
||||
firstErr = err
|
||||
}
|
||||
if f := newWaylandEGLContext; f != nil {
|
||||
c, err := f(w)
|
||||
if err == nil {
|
||||
return c, nil
|
||||
}
|
||||
firstErr = err
|
||||
}
|
||||
if firstErr != nil {
|
||||
return nil, firstErr
|
||||
}
|
||||
return nil, errors.New("wayland: no available GPU backends")
|
||||
}
|
||||
|
||||
// detectUIScale reports the system UI scale, or 1.0 if it fails.
|
||||
func detectUIScale() float32 {
|
||||
// TODO: What about other window environments?
|
||||
|
||||
@@ -102,6 +102,33 @@ type x11Window struct {
|
||||
wakeups chan struct{}
|
||||
}
|
||||
|
||||
var (
|
||||
newX11EGLContext func(w *x11Window) (context, error)
|
||||
newX11VulkanContext func(w *x11Window) (context, error)
|
||||
)
|
||||
|
||||
func (w *x11Window) NewContext() (context, error) {
|
||||
var firstErr error
|
||||
if f := newX11VulkanContext; f != nil {
|
||||
c, err := f(w)
|
||||
if err == nil {
|
||||
return c, nil
|
||||
}
|
||||
firstErr = err
|
||||
}
|
||||
if f := newX11EGLContext; f != nil {
|
||||
c, err := f(w)
|
||||
if err == nil {
|
||||
return c, nil
|
||||
}
|
||||
firstErr = err
|
||||
}
|
||||
if firstErr != nil {
|
||||
return nil, firstErr
|
||||
}
|
||||
return nil, errors.New("x11: no available GPU backends")
|
||||
}
|
||||
|
||||
func (w *x11Window) SetAnimating(anim bool) {
|
||||
w.animating = anim
|
||||
}
|
||||
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
// 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
|
||||
surf vk.Surface
|
||||
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 {
|
||||
vk.DestroySurface(inst, surf)
|
||||
return nil, err
|
||||
}
|
||||
dev, err := vk.CreateDeviceAndQueue(physDev, qFam, "VK_KHR_swapchain")
|
||||
if err != nil {
|
||||
vk.DestroySurface(inst, surf)
|
||||
return nil, err
|
||||
}
|
||||
if err != nil {
|
||||
vk.DestroySurface(inst, surf)
|
||||
vk.DestroyDevice(dev)
|
||||
return nil, err
|
||||
}
|
||||
acquireSem, err := vk.CreateSemaphore(dev)
|
||||
if err != nil {
|
||||
vk.DestroySurface(inst, surf)
|
||||
vk.DestroyDevice(dev)
|
||||
return nil, err
|
||||
}
|
||||
presentSem, err := vk.CreateSemaphore(dev)
|
||||
if err != nil {
|
||||
vk.DestroySemaphore(dev, acquireSem)
|
||||
vk.DestroySurface(inst, surf)
|
||||
vk.DestroyDevice(dev)
|
||||
return nil, err
|
||||
}
|
||||
c := &vkContext{
|
||||
physDev: physDev,
|
||||
inst: inst,
|
||||
surf: surf,
|
||||
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.destroySurface()
|
||||
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) destroySurface() {
|
||||
vk.DeviceWaitIdle(c.dev)
|
||||
|
||||
c.destroyImageViews()
|
||||
if c.swchain != 0 {
|
||||
vk.DestroySwapchain(c.dev, c.swchain)
|
||||
c.swchain = 0
|
||||
}
|
||||
if c.surf != 0 {
|
||||
vk.DestroySurface(c.inst, c.surf)
|
||||
c.surf = 0
|
||||
}
|
||||
}
|
||||
|
||||
func (c *vkContext) setSurface(surf vk.Surface) {
|
||||
if c.surf != 0 {
|
||||
panic("another surface is active")
|
||||
}
|
||||
c.surf = surf
|
||||
}
|
||||
|
||||
func (c *vkContext) refresh(width, height int) error {
|
||||
vk.DeviceWaitIdle(c.dev)
|
||||
|
||||
c.destroyImageViews()
|
||||
swchain, imgs, format, err := vk.CreateSwapchain(c.physDev, c.dev, c.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
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
//go:build !novulkan
|
||||
// +build !novulkan
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"gioui.org/gpu"
|
||||
"gioui.org/internal/vk"
|
||||
)
|
||||
|
||||
type wlVkContext struct {
|
||||
win *window
|
||||
inst vk.Instance
|
||||
ctx *vkContext
|
||||
}
|
||||
|
||||
func init() {
|
||||
newAndroidVulkanContext = func(w *window) (context, error) {
|
||||
inst, err := vk.CreateInstance("VK_KHR_surface", "VK_KHR_android_surface")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
window, _, _ := w.nativeWindow()
|
||||
surf, err := vk.CreateAndroidSurface(inst, unsafe.Pointer(window))
|
||||
if err != nil {
|
||||
vk.DestroyInstance(inst)
|
||||
return nil, err
|
||||
}
|
||||
ctx, err := newVulkanContext(inst, surf)
|
||||
if err != nil {
|
||||
vk.DestroySurface(inst, surf)
|
||||
vk.DestroyInstance(inst)
|
||||
return nil, err
|
||||
}
|
||||
c := &wlVkContext{
|
||||
win: w,
|
||||
inst: inst,
|
||||
ctx: ctx,
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *wlVkContext) RenderTarget() (gpu.RenderTarget, error) {
|
||||
return c.ctx.RenderTarget()
|
||||
}
|
||||
|
||||
func (c *wlVkContext) API() gpu.API {
|
||||
return c.ctx.api()
|
||||
}
|
||||
|
||||
func (c *wlVkContext) Release() {
|
||||
c.ctx.release()
|
||||
vk.DestroyInstance(c.inst)
|
||||
*c = wlVkContext{}
|
||||
}
|
||||
|
||||
func (c *wlVkContext) Present() error {
|
||||
return c.ctx.present()
|
||||
}
|
||||
|
||||
func (c *wlVkContext) Lock() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *wlVkContext) Unlock() {}
|
||||
|
||||
func (c *wlVkContext) Refresh() error {
|
||||
win, w, h := c.win.nativeWindow()
|
||||
c.ctx.destroySurface()
|
||||
surf, err := vk.CreateAndroidSurface(c.inst, unsafe.Pointer(win))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.ctx.setSurface(surf)
|
||||
return c.ctx.refresh(w, h)
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
//go:build ((linux && !android) || freebsd) && !nowayland && !novulkan
|
||||
// +build linux,!android freebsd
|
||||
// +build !nowayland
|
||||
// +build !novulkan
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"gioui.org/gpu"
|
||||
"gioui.org/internal/vk"
|
||||
)
|
||||
|
||||
type wlVkContext struct {
|
||||
win *window
|
||||
inst vk.Instance
|
||||
ctx *vkContext
|
||||
}
|
||||
|
||||
func init() {
|
||||
newWaylandVulkanContext = func(w *window) (context, error) {
|
||||
inst, err := vk.CreateInstance("VK_KHR_surface", "VK_KHR_wayland_surface")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disp := w.display()
|
||||
wlSurf, _, _ := w.surface()
|
||||
surf, err := vk.CreateWaylandSurface(inst, unsafe.Pointer(disp), unsafe.Pointer(wlSurf))
|
||||
if err != nil {
|
||||
vk.DestroyInstance(inst)
|
||||
return nil, err
|
||||
}
|
||||
ctx, err := newVulkanContext(inst, surf)
|
||||
if err != nil {
|
||||
vk.DestroySurface(inst, surf)
|
||||
vk.DestroyInstance(inst)
|
||||
return nil, err
|
||||
}
|
||||
c := &wlVkContext{
|
||||
win: w,
|
||||
inst: inst,
|
||||
ctx: ctx,
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *wlVkContext) RenderTarget() (gpu.RenderTarget, error) {
|
||||
return c.ctx.RenderTarget()
|
||||
}
|
||||
|
||||
func (c *wlVkContext) API() gpu.API {
|
||||
return c.ctx.api()
|
||||
}
|
||||
|
||||
func (c *wlVkContext) Release() {
|
||||
c.ctx.release()
|
||||
vk.DestroyInstance(c.inst)
|
||||
*c = wlVkContext{}
|
||||
}
|
||||
|
||||
func (c *wlVkContext) Present() error {
|
||||
return c.ctx.present()
|
||||
}
|
||||
|
||||
func (c *wlVkContext) Lock() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *wlVkContext) Unlock() {}
|
||||
|
||||
func (c *wlVkContext) Refresh() error {
|
||||
_, w, h := c.win.surface()
|
||||
return c.ctx.refresh(w, h)
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
// SPDX-License-Identifier: Unlicense OR MIT
|
||||
|
||||
//go:build ((linux && !android) || freebsd) && !nox11 && !novulkan
|
||||
// +build linux,!android freebsd
|
||||
// +build !nox11
|
||||
// +build !novulkan
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"gioui.org/gpu"
|
||||
"gioui.org/internal/vk"
|
||||
)
|
||||
|
||||
type x11VkContext struct {
|
||||
win *x11Window
|
||||
inst vk.Instance
|
||||
ctx *vkContext
|
||||
}
|
||||
|
||||
func init() {
|
||||
newX11VulkanContext = func(w *x11Window) (context, error) {
|
||||
inst, err := vk.CreateInstance("VK_KHR_surface", "VK_KHR_xlib_surface")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disp := w.display()
|
||||
window, _, _ := w.window()
|
||||
surf, err := vk.CreateXlibSurface(inst, unsafe.Pointer(disp), uintptr(window))
|
||||
if err != nil {
|
||||
vk.DestroyInstance(inst)
|
||||
return nil, err
|
||||
}
|
||||
ctx, err := newVulkanContext(inst, surf)
|
||||
if err != nil {
|
||||
vk.DestroySurface(inst, surf)
|
||||
vk.DestroyInstance(inst)
|
||||
return nil, err
|
||||
}
|
||||
c := &x11VkContext{
|
||||
win: w,
|
||||
inst: inst,
|
||||
ctx: ctx,
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *x11VkContext) RenderTarget() (gpu.RenderTarget, error) {
|
||||
return c.ctx.RenderTarget()
|
||||
}
|
||||
|
||||
func (c *x11VkContext) API() gpu.API {
|
||||
return c.ctx.api()
|
||||
}
|
||||
|
||||
func (c *x11VkContext) Release() {
|
||||
c.ctx.release()
|
||||
vk.DestroyInstance(c.inst)
|
||||
*c = x11VkContext{}
|
||||
}
|
||||
|
||||
func (c *x11VkContext) Present() error {
|
||||
return c.ctx.present()
|
||||
}
|
||||
|
||||
func (c *x11VkContext) Lock() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *x11VkContext) Unlock() {}
|
||||
|
||||
func (c *x11VkContext) Refresh() error {
|
||||
_, w, h := c.win.window()
|
||||
return c.ctx.refresh(w, h)
|
||||
}
|
||||
Reference in New Issue
Block a user