Skip to content

Instantly share code, notes, and snippets.

@Noofbiz
Created October 26, 2018 16:11
Show Gist options
  • Save Noofbiz/de83517b848f153750a43346dbdcd3e9 to your computer and use it in GitHub Desktop.
Save Noofbiz/de83517b848f153750a43346dbdcd3e9 to your computer and use it in GitHub Desktop.
Drew a triangle with Vulkan!
package main
import (
"errors"
"fmt"
"io/ioutil"
"math"
"os"
"runtime"
"time"
"unsafe"
"github.com/veandco/go-sdl2/sdl"
vk "github.com/vulkan-go/vulkan"
)
type swapChainSupportDetails struct {
capabilities vk.SurfaceCapabilities
formats []vk.SurfaceFormat
presentModes []vk.PresentMode
}
type vulkanApp struct {
w *sdl.Window
i vk.Instance
pd vk.PhysicalDevice
graphicsIdx uint32
graphicsQueue vk.Queue
presentIdx uint32
presentQueue vk.Queue
d vk.Device
s vk.Surface
enabledExtensions []string
details swapChainSupportDetails
swapChain vk.Swapchain
images []vk.Image
swapChainImageFormat vk.Format
swapChainExtent vk.Extent2D
swapChainImageViews []vk.ImageView
renderPass vk.RenderPass
pipelineLayout vk.PipelineLayout
graphicsPipelines []vk.Pipeline
swapChainFramebuffers []vk.Framebuffer
commandPool vk.CommandPool
commandBuffers []vk.CommandBuffer
imageAvailableSemaphore vk.Semaphore
renderFinishedSemaphore vk.Semaphore
}
func (v *vulkanApp) run() error {
defer v.cleanup()
if err := v.initWindow(); err != nil {
return err
}
if err := v.initVulkan(); err != nil {
return err
}
if err := v.mainLoop(); err != nil {
return err
}
return nil
}
func (v *vulkanApp) initWindow() error {
var err error
if err = sdl.Init(sdl.INIT_VIDEO | sdl.INIT_EVENTS); err != nil {
return err
}
if err = sdl.VulkanLoadLibrary(""); err != nil {
return err
}
vk.SetGetInstanceProcAddr(sdl.VulkanGetVkGetInstanceProcAddr())
if err = vk.Init(); err != nil {
return err
}
v.w, err = sdl.CreateWindow("Vulkan Play",
sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, 800, 600, sdl.WINDOW_VULKAN)
if err != nil {
return err
}
return nil
}
func (v *vulkanApp) initVulkan() error {
if err := v.createInstance(); err != nil {
return err
}
if err := v.createSurface(); err != nil {
return err
}
if err := v.pickPhysicalDevice(); err != nil {
return err
}
if err := v.createLogicalDevice(); err != nil {
return err
}
if err := v.createSwapChain(); err != nil {
return err
}
if err := v.createImageViews(); err != nil {
return err
}
if err := v.createRenderPass(); err != nil {
return err
}
if err := v.createGraphicsPipeline(); err != nil {
return err
}
if err := v.createFrameBuffers(); err != nil {
return err
}
if err := v.createCommandPool(); err != nil {
return err
}
if err := v.createCommandBuffers(); err != nil {
return err
}
if err := v.createSemaphores(); err != nil {
return err
}
return nil
}
func (v *vulkanApp) createSurface() error {
surfPtr, err := v.w.VulkanCreateSurface(v.i)
v.s = vk.SurfaceFromPointer(surfPtr)
return err
}
func (v *vulkanApp) createLogicalDevice() error {
dqci := []vk.DeviceQueueCreateInfo{{
SType: vk.StructureTypeDeviceQueueCreateInfo,
QueueFamilyIndex: v.graphicsIdx,
QueueCount: 1,
PQueuePriorities: []float32{1.0},
}}
pdf := vk.PhysicalDeviceFeatures{}
ci := vk.DeviceCreateInfo{
SType: vk.StructureTypeDeviceCreateInfo,
PQueueCreateInfos: dqci,
QueueCreateInfoCount: 1,
PEnabledFeatures: []vk.PhysicalDeviceFeatures{pdf},
EnabledExtensionCount: uint32(len(v.enabledExtensions)),
PpEnabledExtensionNames: safeStrings(v.enabledExtensions),
EnabledLayerCount: 0,
}
if res := vk.CreateDevice(v.pd, &ci, nil, &v.d); res != vk.Success {
return errors.New("unable to create device")
}
vk.GetDeviceQueue(v.d, v.graphicsIdx, 0, &v.graphicsQueue)
vk.GetDeviceQueue(v.d, v.presentIdx, 0, &v.presentQueue)
return nil
}
func (v *vulkanApp) createInstance() error {
appInfo := vk.ApplicationInfo{
SType: vk.StructureTypeApplicationInfo,
PApplicationName: "Hello Triangle",
ApplicationVersion: vk.MakeVersion(1, 0, 0),
PEngineName: "No Engine",
EngineVersion: vk.MakeVersion(1, 0, 0),
ApiVersion: vk.ApiVersion10,
}
createInfo := vk.InstanceCreateInfo{}
createInfo.SType = vk.StructureTypeInstanceCreateInfo
createInfo.PApplicationInfo = &appInfo
exts := v.w.VulkanGetInstanceExtensions()
createInfo.EnabledExtensionCount = uint32(len(exts))
createInfo.PpEnabledExtensionNames = exts
if res := vk.CreateInstance(&createInfo, nil, &v.i); res != vk.Success {
return errors.New("unable to create vulkan instance")
}
if err := vk.InitInstance(v.i); err != nil {
return err
}
return nil
}
func (v *vulkanApp) pickPhysicalDevice() error {
var deviceCount uint32
if res := vk.EnumeratePhysicalDevices(v.i, &deviceCount, nil); res != vk.Success {
return errors.New("unable to get physical devices")
}
devices := make([]vk.PhysicalDevice, deviceCount)
if res := vk.EnumeratePhysicalDevices(v.i, &deviceCount, devices); res != vk.Success {
return errors.New("unable to get physical devices")
}
var deviceSelected bool
for _, device := range devices {
if v.isDeviceSuitable(device) {
deviceSelected = true
v.pd = device
break
}
}
if !deviceSelected {
return errors.New("failed to find a sutible GPU")
}
return nil
}
func (v *vulkanApp) isDeviceSuitable(d vk.PhysicalDevice) bool {
var graphicsSupport, presentSupport bool
var queueFamilyPropertyCount uint32
vk.GetPhysicalDeviceQueueFamilyProperties(d, &queueFamilyPropertyCount, nil)
if queueFamilyPropertyCount == 0 {
return false
}
queueFamilyProperties := make([]vk.QueueFamilyProperties, queueFamilyPropertyCount)
vk.GetPhysicalDeviceQueueFamilyProperties(d, &queueFamilyPropertyCount, queueFamilyProperties)
for i, q := range queueFamilyProperties {
q.Deref()
if q.QueueFlags&vk.QueueFlags(vk.QueueGraphicsBit) != 0 {
v.graphicsIdx = uint32(i)
graphicsSupport = true
}
var b32PresentSupport vk.Bool32
vk.GetPhysicalDeviceSurfaceSupport(d, uint32(i), v.s, &b32PresentSupport)
if b32PresentSupport.B() {
presentSupport = true
v.presentIdx = uint32(i)
}
}
extensionSupport := v.checkDeviceExtensionSupport(d)
swapChainSupport := v.isSwapChainSupported(d)
return graphicsSupport && presentSupport && extensionSupport && swapChainSupport
}
func (v *vulkanApp) checkDeviceExtensionSupport(d vk.PhysicalDevice) bool {
v.enabledExtensions = []string{
vk.KhrSwapchainExtensionName,
}
var extensionCount uint32
vk.EnumerateDeviceExtensionProperties(d, "", &extensionCount, nil)
if extensionCount == 0 {
return false
}
availableExtensions := make([]vk.ExtensionProperties, extensionCount)
vk.EnumerateDeviceExtensionProperties(d, "", &extensionCount, availableExtensions)
for _, req := range v.enabledExtensions {
extensionFound := false
for _, ext := range availableExtensions {
ext.Deref()
if vk.ToString(ext.ExtensionName[:]) == req {
extensionFound = true
break
}
}
if !extensionFound {
return false
}
}
return true
}
func (v *vulkanApp) isSwapChainSupported(d vk.PhysicalDevice) bool {
if res := vk.GetPhysicalDeviceSurfaceCapabilities(d, v.s, &v.details.capabilities); res != vk.Success {
return false
}
var formatCount uint32
vk.GetPhysicalDeviceSurfaceFormats(d, v.s, &formatCount, nil)
if formatCount == 0 {
return false
}
v.details.formats = make([]vk.SurfaceFormat, formatCount)
vk.GetPhysicalDeviceSurfaceFormats(d, v.s, &formatCount, v.details.formats)
var presentModeCount uint32
vk.GetPhysicalDeviceSurfacePresentModes(d, v.s, &presentModeCount, nil)
if presentModeCount == 0 {
return false
}
v.details.presentModes = make([]vk.PresentMode, presentModeCount)
vk.GetPhysicalDeviceSurfacePresentModes(d, v.s, &presentModeCount, v.details.presentModes)
return true
}
func (v *vulkanApp) chooseSwapSurfaceFormat() vk.SurfaceFormat {
if len(v.details.formats) == 1 {
v.details.formats[0].Deref()
if v.details.formats[0].Format == vk.FormatUndefined {
return vk.SurfaceFormat{
Format: vk.FormatB8g8r8Unorm,
ColorSpace: vk.ColorSpaceSrgbNonlinear,
}
}
}
for _, f := range v.details.formats {
f.Deref()
if f.Format == vk.FormatB8g8r8Unorm && f.ColorSpace == vk.ColorSpaceSrgbNonlinear {
return f
}
}
return v.details.formats[0]
}
func (v *vulkanApp) chooseSwapPresentMode() vk.PresentMode {
bestMode := vk.PresentModeFifo
for _, p := range v.details.presentModes {
if p == vk.PresentModeMailbox {
return p
}
if p == vk.PresentModeImmediate {
bestMode = p
}
}
return bestMode
}
func (v *vulkanApp) chooseSwapExtent() vk.Extent2D {
v.details.capabilities.Deref()
if v.details.capabilities.CurrentExtent.Width != math.MaxUint32 {
return v.details.capabilities.CurrentExtent
}
actualExtent := vk.Extent2D{
Width: 800,
Height: 600,
}
actualExtent.Width = clamp(v.details.capabilities.MaxImageExtent.Width,
v.details.capabilities.MinImageExtent.Width, actualExtent.Width)
actualExtent.Height = clamp(v.details.capabilities.MaxImageExtent.Height,
v.details.capabilities.MinImageExtent.Height, actualExtent.Height)
return actualExtent
}
func clamp(high, low, value uint32) uint32 {
var ret uint32
if value > high {
ret = high
} else {
ret = value
}
if ret < low {
return low
}
return ret
}
func (v *vulkanApp) createRenderPass() error {
colorAttachment := vk.AttachmentDescription{
Format: v.swapChainImageFormat,
Samples: vk.SampleCount1Bit,
LoadOp: vk.AttachmentLoadOpClear,
StoreOp: vk.AttachmentStoreOpStore,
StencilLoadOp: vk.AttachmentLoadOpDontCare,
StencilStoreOp: vk.AttachmentStoreOpDontCare,
InitialLayout: vk.ImageLayoutUndefined,
FinalLayout: vk.ImageLayoutPresentSrc,
}
colorAttachmentRef := vk.AttachmentReference{
Attachment: 0,
Layout: vk.ImageLayoutColorAttachmentOptimal,
}
subpass := vk.SubpassDescription{
PipelineBindPoint: vk.PipelineBindPointGraphics,
ColorAttachmentCount: 1,
PColorAttachments: []vk.AttachmentReference{colorAttachmentRef},
}
dependency := vk.SubpassDependency{
SrcSubpass: vk.SubpassExternal,
DstSubpass: 0,
SrcStageMask: vk.PipelineStageFlags(vk.PipelineStageColorAttachmentOutputBit),
SrcAccessMask: 0,
DstStageMask: vk.PipelineStageFlags(vk.AccessColorAttachmentReadBit | vk.AccessColorAttachmentWriteBit),
}
renderPassInfo := vk.RenderPassCreateInfo{
SType: vk.StructureTypeRenderPassCreateInfo,
AttachmentCount: 1,
PAttachments: []vk.AttachmentDescription{colorAttachment},
SubpassCount: 1,
PSubpasses: []vk.SubpassDescription{subpass},
DependencyCount: 1,
PDependencies: []vk.SubpassDependency{dependency},
}
if res := vk.CreateRenderPass(v.d, &renderPassInfo, nil, &v.renderPass); res != vk.Success {
return errors.New("failed to create render pass")
}
return nil
}
func (v *vulkanApp) createSwapChain() error {
surfaceFormat := v.chooseSwapSurfaceFormat()
surfaceFormat.Deref()
presentMode := v.chooseSwapPresentMode()
extent := v.chooseSwapExtent()
extent.Deref()
v.details.capabilities.Deref()
imageCount := v.details.capabilities.MinImageCount + 1
if v.details.capabilities.MaxImageCount > 0 {
if imageCount > v.details.capabilities.MaxImageCount {
imageCount = v.details.capabilities.MaxImageCount
}
}
createInfo := vk.SwapchainCreateInfo{
SType: vk.StructureTypeSwapchainCreateInfo,
Surface: v.s,
MinImageCount: imageCount,
ImageFormat: surfaceFormat.Format,
ImageColorSpace: surfaceFormat.ColorSpace,
ImageExtent: extent,
ImageArrayLayers: 1,
ImageUsage: vk.ImageUsageFlags(vk.ImageUsageColorAttachmentBit),
}
if v.graphicsIdx != v.presentIdx {
createInfo.ImageSharingMode = vk.SharingModeConcurrent
createInfo.QueueFamilyIndexCount = 2
createInfo.PQueueFamilyIndices = []uint32{v.graphicsIdx, v.presentIdx}
} else {
createInfo.ImageSharingMode = vk.SharingModeExclusive
createInfo.QueueFamilyIndexCount = 0
createInfo.PQueueFamilyIndices = []uint32{}
}
createInfo.PreTransform = v.details.capabilities.CurrentTransform
createInfo.CompositeAlpha = vk.CompositeAlphaOpaqueBit
createInfo.PresentMode = presentMode
createInfo.Clipped = vk.True
createInfo.OldSwapchain = vk.Swapchain(vk.NullHandle)
if res := vk.CreateSwapchain(v.d, &createInfo, nil, &v.swapChain); res != vk.Success {
return errors.New("failed to create swap chain")
}
var numImgs uint32
vk.GetSwapchainImages(v.d, v.swapChain, &numImgs, nil)
v.images = make([]vk.Image, numImgs)
if res := vk.GetSwapchainImages(v.d, v.swapChain, &numImgs, v.images); res != vk.Success {
return errors.New("failed to get swap chain images")
}
v.swapChainImageFormat = surfaceFormat.Format
v.swapChainExtent = extent
return nil
}
func (v *vulkanApp) createImageViews() error {
v.swapChainImageViews = make([]vk.ImageView, len(v.images))
for i, image := range v.images {
createInfo := vk.ImageViewCreateInfo{
SType: vk.StructureTypeImageViewCreateInfo,
Image: image,
ViewType: vk.ImageViewType2d,
Format: v.swapChainImageFormat,
}
createInfo.Components.R = vk.ComponentSwizzleIdentity
createInfo.Components.G = vk.ComponentSwizzleIdentity
createInfo.Components.B = vk.ComponentSwizzleIdentity
createInfo.Components.A = vk.ComponentSwizzleIdentity
createInfo.SubresourceRange.BaseMipLevel = 0
createInfo.SubresourceRange.LevelCount = 1
createInfo.SubresourceRange.BaseArrayLayer = 0
createInfo.SubresourceRange.LayerCount = 1
if res := vk.CreateImageView(v.d, &createInfo, nil, &v.swapChainImageViews[i]); res != vk.Success {
return errors.New("unable to create image view from swap chain images")
}
}
return nil
}
func (v *vulkanApp) createGraphicsPipeline() error {
vertShaderData, err := readFile("shaders/vert.spv")
if err != nil {
return err
}
fragShaderData, err := readFile("shaders/frag.spv")
if err != nil {
return err
}
vertShaderModule, err := v.loadShaderModule(vertShaderData)
if err != nil {
return err
}
fragShaderModule, err := v.loadShaderModule(fragShaderData)
if err != nil {
return err
}
vertShaderStageInfo := vk.PipelineShaderStageCreateInfo{
SType: vk.StructureTypePipelineShaderStageCreateInfo,
Stage: vk.ShaderStageVertexBit,
Module: vertShaderModule,
PName: safeString("main"),
}
fragShaderStageInfo := vk.PipelineShaderStageCreateInfo{
SType: vk.StructureTypePipelineShaderStageCreateInfo,
Stage: vk.ShaderStageFragmentBit,
Module: fragShaderModule,
PName: safeString("main"),
}
shaderStages := []vk.PipelineShaderStageCreateInfo{
vertShaderStageInfo,
fragShaderStageInfo,
}
vertexInputInfo := vk.PipelineVertexInputStateCreateInfo{
SType: vk.StructureTypePipelineVertexInputStateCreateInfo,
}
inputAssembly := vk.PipelineInputAssemblyStateCreateInfo{
SType: vk.StructureTypePipelineInputAssemblyStateCreateInfo,
Topology: vk.PrimitiveTopologyTriangleList,
PrimitiveRestartEnable: vk.False,
}
viewport := vk.Viewport{
X: 0,
Y: 0,
Width: float32(v.swapChainExtent.Width),
Height: float32(v.swapChainExtent.Height),
MinDepth: 0,
MaxDepth: 1,
}
scissor := vk.Rect2D{
Offset: vk.Offset2D{
X: 0,
Y: 0,
},
Extent: v.swapChainExtent,
}
viewportState := vk.PipelineViewportStateCreateInfo{
SType: vk.StructureTypePipelineViewportStateCreateInfo,
ViewportCount: 1,
PViewports: []vk.Viewport{viewport},
ScissorCount: 1,
PScissors: []vk.Rect2D{scissor},
}
rasterizer := vk.PipelineRasterizationStateCreateInfo{
SType: vk.StructureTypePipelineRasterizationStateCreateInfo,
DepthClampEnable: vk.False,
RasterizerDiscardEnable: vk.False,
PolygonMode: vk.PolygonModeFill,
LineWidth: 1,
CullMode: vk.CullModeFlags(vk.CullModeBackBit),
FrontFace: vk.FrontFaceClockwise,
DepthBiasEnable: vk.False,
}
multisampling := vk.PipelineMultisampleStateCreateInfo{
SType: vk.StructureTypePipelineMultisampleStateCreateInfo,
SampleShadingEnable: vk.False,
RasterizationSamples: vk.SampleCount1Bit,
MinSampleShading: 1,
AlphaToCoverageEnable: vk.False,
AlphaToOneEnable: vk.False,
}
colorBlendAttachment := vk.PipelineColorBlendAttachmentState{
ColorWriteMask: vk.ColorComponentFlags(vk.ColorComponentRBit | vk.ColorComponentGBit | vk.ColorComponentBBit | vk.ColorComponentABit),
BlendEnable: vk.False,
SrcColorBlendFactor: vk.BlendFactorOne,
DstColorBlendFactor: vk.BlendFactorZero,
ColorBlendOp: vk.BlendOpAdd,
SrcAlphaBlendFactor: vk.BlendFactorOne,
DstAlphaBlendFactor: vk.BlendFactorZero,
AlphaBlendOp: vk.BlendOpAdd,
}
colorBlending := vk.PipelineColorBlendStateCreateInfo{
SType: vk.StructureTypePipelineColorBlendStateCreateInfo,
LogicOpEnable: vk.False,
AttachmentCount: 1,
PAttachments: []vk.PipelineColorBlendAttachmentState{colorBlendAttachment},
}
pipelineLayoutInfo := vk.PipelineLayoutCreateInfo{
SType: vk.StructureTypePipelineLayoutCreateInfo,
}
if res := vk.CreatePipelineLayout(v.d, &pipelineLayoutInfo, nil, &v.pipelineLayout); res != vk.Success {
return errors.New("failed to create pipeline layout")
}
pipelineInfo := vk.GraphicsPipelineCreateInfo{
SType: vk.StructureTypeGraphicsPipelineCreateInfo,
StageCount: 2,
PStages: shaderStages,
PVertexInputState: &vertexInputInfo,
PInputAssemblyState: &inputAssembly,
PViewportState: &viewportState,
PRasterizationState: &rasterizer,
PMultisampleState: &multisampling,
PColorBlendState: &colorBlending,
Layout: v.pipelineLayout,
RenderPass: v.renderPass,
Subpass: 0,
}
v.graphicsPipelines = make([]vk.Pipeline, 1)
if res := vk.CreateGraphicsPipelines(v.d, nil, 1, []vk.GraphicsPipelineCreateInfo{pipelineInfo}, nil, v.graphicsPipelines); res != vk.Success {
errors.New("failed to create graphics pipeline")
}
vk.DestroyShaderModule(v.d, vertShaderModule, nil)
vk.DestroyShaderModule(v.d, fragShaderModule, nil)
return nil
}
func readFile(url string) ([]byte, error) {
buf := make([]byte, 0)
f, err := os.Open(url)
if err != nil {
return buf, err
}
defer f.Close()
return ioutil.ReadAll(f)
}
func (v *vulkanApp) loadShaderModule(data []byte) (vk.ShaderModule, error) {
var module vk.ShaderModule
if res := vk.CreateShaderModule(v.d, &vk.ShaderModuleCreateInfo{
SType: vk.StructureTypeShaderModuleCreateInfo,
CodeSize: uint(len(data)),
PCode: sliceUint32(data),
}, nil, &module); res != vk.Success {
return vk.NullShaderModule, errors.New("unable to create shader module")
}
return module, nil
}
func sliceUint32(data []byte) []uint32 {
const m = 0x7fffffff
return (*[m / 4]uint32)(unsafe.Pointer((*sliceHeader)(unsafe.Pointer(&data)).Data))[:len(data)/4]
}
type sliceHeader struct {
Data uintptr
Len int
Cap int
}
func (v *vulkanApp) createFrameBuffers() error {
v.swapChainFramebuffers = make([]vk.Framebuffer, len(v.swapChainImageViews))
for idx, view := range v.swapChainImageViews {
attachments := []vk.ImageView{view}
framebufferInfo := vk.FramebufferCreateInfo{
SType: vk.StructureTypeFramebufferCreateInfo,
RenderPass: v.renderPass,
AttachmentCount: 1,
PAttachments: attachments,
Width: v.swapChainExtent.Width,
Height: v.swapChainExtent.Height,
Layers: 1,
}
if res := vk.CreateFramebuffer(v.d, &framebufferInfo, nil, &v.swapChainFramebuffers[idx]); res != vk.Success {
return errors.New("failed to create framebuffer")
}
}
return nil
}
func (v *vulkanApp) createCommandPool() error {
poolInfo := vk.CommandPoolCreateInfo{
SType: vk.StructureTypeCommandPoolCreateInfo,
QueueFamilyIndex: v.graphicsIdx,
}
if res := vk.CreateCommandPool(v.d, &poolInfo, nil, &v.commandPool); res != vk.Success {
return errors.New("failed to create command pool")
}
return nil
}
func (v *vulkanApp) createCommandBuffers() error {
v.commandBuffers = make([]vk.CommandBuffer, len(v.swapChainFramebuffers))
allocInfo := vk.CommandBufferAllocateInfo{
SType: vk.StructureTypeCommandBufferAllocateInfo,
CommandPool: v.commandPool,
Level: vk.CommandBufferLevelPrimary,
CommandBufferCount: uint32(len(v.commandBuffers)),
}
if res := vk.AllocateCommandBuffers(v.d, &allocInfo, v.commandBuffers); res != vk.Success {
errors.New("failed to allocate command buffers")
}
for idx, buffer := range v.commandBuffers {
beginInfo := vk.CommandBufferBeginInfo{
SType: vk.StructureTypeCommandBufferBeginInfo,
Flags: vk.CommandBufferUsageFlags(vk.CommandBufferUsageSimultaneousUseBit),
}
if res := vk.BeginCommandBuffer(buffer, &beginInfo); res != vk.Success {
return errors.New("failed to begin recording command buffers")
}
clearValue := vk.NewClearValue([]float32{0, 0, 0, 1})
renderPassInfo := vk.RenderPassBeginInfo{
SType: vk.StructureTypeRenderPassBeginInfo,
RenderPass: v.renderPass,
Framebuffer: v.swapChainFramebuffers[idx],
ClearValueCount: 1,
PClearValues: []vk.ClearValue{clearValue},
}
renderPassInfo.RenderArea.Offset = vk.Offset2D{X: 0, Y: 0}
renderPassInfo.RenderArea.Extent = v.swapChainExtent
vk.CmdBeginRenderPass(buffer, &renderPassInfo, vk.SubpassContentsInline)
vk.CmdBindPipeline(buffer, vk.PipelineBindPointGraphics, v.graphicsPipelines[0])
vk.CmdDraw(buffer, 3, 1, 0, 0)
vk.CmdEndRenderPass(buffer)
if vk.EndCommandBuffer(buffer) != vk.Success {
return errors.New("failed to record command buffer!")
}
}
return nil
}
func (v *vulkanApp) createSemaphores() error {
semaphoreInfo := vk.SemaphoreCreateInfo{
SType: vk.StructureTypeSemaphoreCreateInfo,
}
if vk.CreateSemaphore(v.d, &semaphoreInfo, nil, &v.imageAvailableSemaphore) != vk.Success {
return errors.New("failed to create image semaphore")
}
if vk.CreateSemaphore(v.d, &semaphoreInfo, nil, &v.renderFinishedSemaphore) != vk.Success {
return errors.New("failed to create render finished semaphore")
}
return nil
}
func (v *vulkanApp) mainLoop() error {
fpsDelay := time.Second / 60
fpsTicker := time.NewTicker(fpsDelay)
MainLoop:
for {
select {
case <-fpsTicker.C:
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
switch t := event.(type) {
case *sdl.KeyboardEvent:
if t.Keysym.Sym == sdl.K_ESCAPE {
break MainLoop
}
case *sdl.QuitEvent:
break MainLoop
}
}
if err := v.drawFrame(); err != nil {
panic(err)
}
}
}
vk.DeviceWaitIdle(v.d)
return nil
}
func (v *vulkanApp) drawFrame() error {
var imageIndex uint32
vk.AcquireNextImage(v.d, v.swapChain, math.MaxUint64, v.imageAvailableSemaphore, vk.NullFence, &imageIndex)
waitSemaphores := []vk.Semaphore{v.imageAvailableSemaphore}
waitStages := []vk.PipelineStageFlags{vk.PipelineStageFlags(vk.PipelineStageColorAttachmentOutputBit)}
signalSemaphores := []vk.Semaphore{v.renderFinishedSemaphore}
submitInfo := []vk.SubmitInfo{vk.SubmitInfo{
SType: vk.StructureTypeSubmitInfo,
WaitSemaphoreCount: 1,
PWaitSemaphores: waitSemaphores,
PWaitDstStageMask: waitStages,
CommandBufferCount: 1,
PCommandBuffers: []vk.CommandBuffer{v.commandBuffers[imageIndex]},
SignalSemaphoreCount: 1,
PSignalSemaphores: signalSemaphores,
}}
if vk.QueueSubmit(v.graphicsQueue, 1, submitInfo, vk.NullFence) != vk.Success {
return errors.New("failed to submit draw command buffer!")
}
presentInfo := vk.PresentInfo{
SType: vk.StructureTypePresentInfo,
WaitSemaphoreCount: 1,
PWaitSemaphores: signalSemaphores,
SwapchainCount: 1,
PSwapchains: []vk.Swapchain{v.swapChain},
PImageIndices: []uint32{imageIndex},
}
if vk.QueuePresent(v.presentQueue, &presentInfo) != vk.Success {
return errors.New("failed to present draw")
}
return nil
}
func (v *vulkanApp) cleanup() {
vk.DestroySemaphore(v.d, v.imageAvailableSemaphore, nil)
vk.DestroySemaphore(v.d, v.renderFinishedSemaphore, nil)
vk.DestroyCommandPool(v.d, v.commandPool, nil)
for _, framebuffer := range v.swapChainFramebuffers {
vk.DestroyFramebuffer(v.d, framebuffer, nil)
}
for _, pipeline := range v.graphicsPipelines {
vk.DestroyPipeline(v.d, pipeline, nil)
}
vk.DestroyPipelineLayout(v.d, v.pipelineLayout, nil)
vk.DestroyRenderPass(v.d, v.renderPass, nil)
for _, view := range v.swapChainImageViews {
vk.DestroyImageView(v.d, view, nil)
}
vk.DestroySwapchain(v.d, v.swapChain, nil)
vk.DestroySurface(v.i, v.s, nil)
vk.DestroyDevice(v.d, nil)
vk.DestroyInstance(v.i, nil)
v.w.Destroy()
sdl.VulkanUnloadLibrary()
sdl.Quit()
}
var end = "\x00"
var endChar byte = '\x00'
func safeString(s string) string {
if len(s) == 0 {
return end
}
if s[len(s)-1] != endChar {
return s + end
}
return s
}
func safeStrings(list []string) []string {
for i := range list {
list[i] = safeString(list[i])
}
return list
}
func init() {
runtime.LockOSThread()
}
func main() {
v := new(vulkanApp)
if err := v.run(); err != nil {
fmt.Println(err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment