237 lines
6.0 KiB
Go
237 lines
6.0 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
"runtime"
|
|
"unsafe"
|
|
|
|
vk "github.com/christerso/vulkan-go/vk"
|
|
"github.com/ebitengine/purego"
|
|
sdl "github.com/jupiterrider/purego-sdl3/sdl"
|
|
)
|
|
|
|
const (
|
|
APP_NAME = "MXL Player"
|
|
APP_VER = "0.0.1"
|
|
WIN_WIDTH int32 = 1280
|
|
WIN_HEIGHT int32 = 720
|
|
)
|
|
|
|
const (
|
|
initVideo uint32 = 0x00000020
|
|
windowVulkan uint64 = 0x0000000010000000
|
|
windowResizable uint64 = 0x0000000000000020
|
|
|
|
eventQuit uint32 = 0x100
|
|
eventKeyDown uint32 = 0x300
|
|
keyEscape uint32 = 0x1B
|
|
)
|
|
|
|
var (
|
|
sdlInit func(flags uint32) bool
|
|
sdlQuit func()
|
|
sdlGetError func() uintptr
|
|
sdlCreateWindow func(title *byte, w, h int32, flags uint64) uintptr
|
|
sdlDestroyWindow func(window uintptr)
|
|
sdlVulkanGetInstanceExtensions func(count *uint32) uintptr
|
|
sdlVulkanCreateSurface func(window, instance, allocator uintptr, surface *uint64) bool
|
|
sdlPollEvent func(event unsafe.Pointer) bool
|
|
sdlGetWindowSizeInPixels func(window uintptr, w, h *int32) bool
|
|
)
|
|
|
|
func sdlError() string { return cstr(sdlGetError()) }
|
|
|
|
var loaded = false
|
|
|
|
func loadSDLMissing() error {
|
|
if loaded {
|
|
return nil
|
|
}
|
|
h, err := purego.Dlopen("libSDL3.so.0", purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
|
if err != nil {
|
|
return fmt.Errorf("win: load SDL3: %w", err)
|
|
}
|
|
purego.RegisterLibFunc(&sdlInit, h, "SDL_Init")
|
|
purego.RegisterLibFunc(&sdlQuit, h, "SDL_Quit")
|
|
purego.RegisterLibFunc(&sdlGetError, h, "SDL_GetError")
|
|
purego.RegisterLibFunc(&sdlCreateWindow, h, "SDL_CreateWindow")
|
|
purego.RegisterLibFunc(&sdlDestroyWindow, h, "SDL_DestroyWindow")
|
|
purego.RegisterLibFunc(&sdlVulkanGetInstanceExtensions, h, "SDL_Vulkan_GetInstanceExtensions")
|
|
purego.RegisterLibFunc(&sdlVulkanCreateSurface, h, "SDL_Vulkan_CreateSurface")
|
|
purego.RegisterLibFunc(&sdlPollEvent, h, "SDL_PollEvent")
|
|
purego.RegisterLibFunc(&sdlGetWindowSizeInPixels, h, "SDL_GetWindowSizeInPixels")
|
|
loaded = true
|
|
return nil
|
|
}
|
|
|
|
// cstr reads a NUL-terminated C string at p.
|
|
func cstr(p uintptr) string {
|
|
if p == 0 {
|
|
return ""
|
|
}
|
|
var n int
|
|
for *(*byte)(unsafe.Pointer(p + uintptr(n))) != 0 {
|
|
n++
|
|
}
|
|
return string(unsafe.Slice((*byte)(unsafe.Pointer(p)), n))
|
|
}
|
|
|
|
// cbytes returns a NUL-terminated copy of s as *byte, kept alive by the caller.
|
|
func cbytes(s string) *byte {
|
|
b := make([]byte, len(s)+1)
|
|
copy(b, s)
|
|
runtime.KeepAlive(b)
|
|
return &b[0]
|
|
}
|
|
|
|
func main() {
|
|
runtime.LockOSThread()
|
|
// SDL init
|
|
// if !sdl.Init(sdl.InitVideo) {
|
|
// log.Fatal(sdl.GetError())
|
|
// }
|
|
// defer sdl.Quit()
|
|
|
|
// window := sdl.CreateWindow(
|
|
// fmt.Sprintf("%s ver. %s", APP_NAME, APP_VER),
|
|
// 1280,
|
|
// 720,
|
|
// sdl.WindowVulkan|sdl.WindowResizable,
|
|
// )
|
|
// if window == nil {
|
|
// log.Fatal(sdl.GetError())
|
|
// }
|
|
// defer sdl.DestroyWindow(window)
|
|
if err := loadSDLMissing(); err != nil {
|
|
panic(err)
|
|
}
|
|
if !sdlInit(initVideo) {
|
|
log.Fatalf("SDL_Init: %s", sdlError())
|
|
return
|
|
}
|
|
|
|
windowHandler := sdlCreateWindow(cbytes(APP_NAME), WIN_WIDTH, WIN_HEIGHT, windowVulkan|windowResizable)
|
|
if windowHandler == 0 {
|
|
sdlQuit()
|
|
log.Fatalf("SDL_CreateWindow: %s", sdlError())
|
|
return
|
|
}
|
|
|
|
// Vulkan init
|
|
if err := vk.Load(); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Vulkan instance extensions SDL needs.
|
|
var sdlExtCount uint32
|
|
arr := sdlVulkanGetInstanceExtensions(&sdlExtCount)
|
|
if arr == 0 {
|
|
log.Fatal("sdlVulkanGetInstanceExtensions is 0")
|
|
return
|
|
}
|
|
sdlExtensions := make([]string, sdlExtCount)
|
|
for i := uint32(0); i < sdlExtCount; i++ {
|
|
p := *(*uintptr)(unsafe.Pointer(arr + uintptr(i)*unsafe.Sizeof(uintptr(0))))
|
|
sdlExtensions[i] = cstr(p)
|
|
}
|
|
// Vulkan Instance creation
|
|
var vkLayers []string
|
|
vkExtensions := append(sdlExtensions, vk.ExtDebugUtils)
|
|
vkInstance, err := vk.CreateInstance(vk.InstanceConfig{
|
|
ApplicationName: APP_NAME,
|
|
EngineName: "no engine",
|
|
Extensions: vkExtensions,
|
|
Layers: vkLayers,
|
|
})
|
|
if err != nil {
|
|
log.Fatalf(err.Error())
|
|
panic(err)
|
|
}
|
|
defer vkInstance.Destroy()
|
|
// Vulkan surface
|
|
var vkSurface uint64
|
|
if !sdlVulkanCreateSurface(windowHandler, uintptr(vkInstance), 0, &vkSurface) {
|
|
log.Fatalf("SDL_Vulkan_CreateSurface: %s", sdlError())
|
|
return
|
|
}
|
|
vkSurf := vk.SurfaceKHR(vkSurface)
|
|
defer vkInstance.DestroySurface(vkSurf)
|
|
|
|
// Vulkan Physical Device
|
|
devices, err := vkInstance.EnumeratePhysicalDevices()
|
|
if err != nil || len(devices) == 0 {
|
|
panic("No Vulkan devices")
|
|
}
|
|
for _, pd := range devices {
|
|
info := pd.Info()
|
|
fmt.Printf("%s (%s)\n", info.Name, info.Type)
|
|
}
|
|
vkPhysDevice := devices[0]
|
|
// Vulkan Surface & Graphics Queue
|
|
gfx, err := vkPhysDevice.GraphicsFamily()
|
|
if err != nil {
|
|
panic(err.Error())
|
|
}
|
|
if !vkPhysDevice.SurfaceSupport(gfx, vkSurf) {
|
|
log.Fatalf("graphics queue cannot present")
|
|
return
|
|
}
|
|
vkDevice, vkQueue, err := vkPhysDevice.CreateDevice(vk.DeviceConfig{
|
|
GraphicsFamily: gfx,
|
|
Extensions: []string{"VK_KHR_swapchain"},
|
|
})
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer vkDevice.Destroy()
|
|
defer vkDevice.WaitIdle()
|
|
// TODO: colorspace & render pass
|
|
var vkFormat vk.Format
|
|
var vkColorSpace uint32
|
|
formats, _ := vkPhysDevice.SurfaceFormats(vkSurf)
|
|
for _, f := range formats {
|
|
if f.Format == vk.FormatB8G8R8A8Srgb && f.ColorSpace == vk.ColorSpaceSRGBNonlinear {
|
|
vkFormat = f.Format
|
|
vkColorSpace = f.ColorSpace
|
|
}
|
|
}
|
|
var vkPresentMode vk.PresentMode
|
|
modes, _ := vkPhysDevice.SurfacePresentModes(vkSurf)
|
|
for _, m := range modes {
|
|
if m == vk.PresentModeMailbox {
|
|
vkPresentMode = m
|
|
}
|
|
}
|
|
|
|
vkRenderPass, err := vkDevice.CreateColorDepthRenderPass(vkFormat, vk.FormatD32Sfloat)
|
|
if err != nil {
|
|
panic(err.Error())
|
|
}
|
|
defer vkDevice.DestroyRenderPass(vkRenderPass)
|
|
|
|
// Vulkan Command Pool
|
|
vkCommandPool, err := vkDevice.CreateCommandPool(gfx)
|
|
if err != nil {
|
|
panic(err.Error())
|
|
}
|
|
defer vkDevice.DestroyCommandPool(vkCommandPool)
|
|
vkCommands, err := vkDevice.AllocateCommandBuffers(vkCommandPool, 1)
|
|
if err != nil {
|
|
panic(err.Error())
|
|
}
|
|
|
|
// Core Loop
|
|
running := true
|
|
for running {
|
|
var event sdl.Event
|
|
|
|
for sdl.PollEvent(&event) {
|
|
switch event.Type() {
|
|
case sdl.EventQuit:
|
|
running = false
|
|
}
|
|
}
|
|
}
|
|
}
|