1546 lines
38 KiB
Go
1546 lines
38 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"embed"
|
|
"encoding/binary"
|
|
"github.com/loov/hrtime"
|
|
"github.com/pkg/errors"
|
|
"github.com/veandco/go-sdl2/sdl"
|
|
"github.com/vkngwrapper/core/v2"
|
|
"github.com/vkngwrapper/core/v2/common"
|
|
"github.com/vkngwrapper/core/v2/core1_0"
|
|
"github.com/vkngwrapper/extensions/v2/ext_debug_utils"
|
|
"github.com/vkngwrapper/extensions/v2/khr_portability_enumeration"
|
|
"github.com/vkngwrapper/extensions/v2/khr_portability_subset"
|
|
"github.com/vkngwrapper/extensions/v2/khr_surface"
|
|
"github.com/vkngwrapper/extensions/v2/khr_swapchain"
|
|
vkng_sdl2 "github.com/vkngwrapper/integrations/sdl2/v2"
|
|
vkngmath "github.com/vkngwrapper/math"
|
|
"log"
|
|
"math"
|
|
"unsafe"
|
|
)
|
|
|
|
//go:embed shaders
|
|
var shaders embed.FS
|
|
|
|
const MaxFramesInFlight = 2
|
|
|
|
var validationLayers = []string{"VK_LAYER_KHRONOS_validation"}
|
|
var deviceExtensions = []string{khr_swapchain.ExtensionName}
|
|
|
|
const enableValidationLayers = true
|
|
|
|
type QueueFamilyIndices struct {
|
|
GraphicsFamily *int
|
|
PresentFamily *int
|
|
}
|
|
|
|
func (i *QueueFamilyIndices) IsComplete() bool {
|
|
return i.GraphicsFamily != nil && i.PresentFamily != nil
|
|
}
|
|
|
|
type SwapChainSupportDetails struct {
|
|
Capabilities *khr_surface.SurfaceCapabilities
|
|
Formats []khr_surface.SurfaceFormat
|
|
PresentModes []khr_surface.PresentMode
|
|
}
|
|
|
|
type Vertex struct {
|
|
Position vkngmath.Vec2[float32]
|
|
Color vkngmath.Vec3[float32]
|
|
}
|
|
|
|
type UniformBufferObject struct {
|
|
Model vkngmath.Mat4x4[float32]
|
|
View vkngmath.Mat4x4[float32]
|
|
Proj vkngmath.Mat4x4[float32]
|
|
}
|
|
|
|
func getVertexBindingDescription() []core1_0.VertexInputBindingDescription {
|
|
v := Vertex{}
|
|
return []core1_0.VertexInputBindingDescription{
|
|
{
|
|
Binding: 0,
|
|
Stride: int(unsafe.Sizeof(v)),
|
|
InputRate: core1_0.VertexInputRateVertex,
|
|
},
|
|
}
|
|
}
|
|
|
|
func getVertexAttributeDescriptions() []core1_0.VertexInputAttributeDescription {
|
|
v := Vertex{}
|
|
return []core1_0.VertexInputAttributeDescription{
|
|
{
|
|
Binding: 0,
|
|
Location: 0,
|
|
Format: core1_0.FormatR32G32B32SignedFloat,
|
|
Offset: int(unsafe.Offsetof(v.Position)),
|
|
},
|
|
{
|
|
Binding: 0,
|
|
Location: 1,
|
|
Format: core1_0.FormatR32G32B32SignedFloat,
|
|
Offset: int(unsafe.Offsetof(v.Color)),
|
|
},
|
|
}
|
|
}
|
|
|
|
var vertices = []Vertex{
|
|
{Position: vkngmath.Vec2[float32]{X: -0.5, Y: -0.5}, Color: vkngmath.Vec3[float32]{X: 1, Y: 0, Z: 0}},
|
|
{Position: vkngmath.Vec2[float32]{X: 0.5, Y: -0.5}, Color: vkngmath.Vec3[float32]{X: 0, Y: 1, Z: 0}},
|
|
{Position: vkngmath.Vec2[float32]{X: 0.5, Y: 0.5}, Color: vkngmath.Vec3[float32]{X: 0, Y: 0, Z: 1}},
|
|
{Position: vkngmath.Vec2[float32]{X: -0.5, Y: 0.5}, Color: vkngmath.Vec3[float32]{X: 1, Y: 1, Z: 1}},
|
|
}
|
|
|
|
var indices = []uint16{0, 1, 2, 2, 3, 0}
|
|
|
|
type HelloTriangleApplication struct {
|
|
window *sdl.Window
|
|
loader core.Loader
|
|
|
|
instance core1_0.Instance
|
|
debugMessenger ext_debug_utils.DebugUtilsMessenger
|
|
surface khr_surface.Surface
|
|
|
|
physicalDevice core1_0.PhysicalDevice
|
|
device core1_0.Device
|
|
|
|
graphicsQueue core1_0.Queue
|
|
presentQueue core1_0.Queue
|
|
|
|
swapchainExtension khr_swapchain.Extension
|
|
swapchain khr_swapchain.Swapchain
|
|
swapchainImages []core1_0.Image
|
|
swapchainImageFormat core1_0.Format
|
|
swapchainExtent core1_0.Extent2D
|
|
swapchainImageViews []core1_0.ImageView
|
|
swapchainFramebuffers []core1_0.Framebuffer
|
|
|
|
renderPass core1_0.RenderPass
|
|
descriptorPool core1_0.DescriptorPool
|
|
descriptorSets []core1_0.DescriptorSet
|
|
descriptorSetLayout core1_0.DescriptorSetLayout
|
|
pipelineLayout core1_0.PipelineLayout
|
|
graphicsPipeline core1_0.Pipeline
|
|
|
|
commandPool core1_0.CommandPool
|
|
commandBuffers []core1_0.CommandBuffer
|
|
|
|
imageAvailableSemaphore []core1_0.Semaphore
|
|
renderFinishedSemaphore []core1_0.Semaphore
|
|
inFlightFence []core1_0.Fence
|
|
imagesInFlight []core1_0.Fence
|
|
currentFrame int
|
|
frameStart float64
|
|
|
|
vertexBuffer core1_0.Buffer
|
|
vertexBufferMemory core1_0.DeviceMemory
|
|
indexBuffer core1_0.Buffer
|
|
indexBufferMemory core1_0.DeviceMemory
|
|
|
|
uniformBuffers []core1_0.Buffer
|
|
uniformBuffersMemory []core1_0.DeviceMemory
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) Run() error {
|
|
err := app.initWindow()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.initVulkan()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer app.cleanup()
|
|
|
|
return app.mainLoop()
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) initWindow() error {
|
|
if err := sdl.Init(sdl.INIT_VIDEO); err != nil {
|
|
return err
|
|
}
|
|
|
|
window, err := sdl.CreateWindow("Vulkan", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, 800, 600, sdl.WINDOW_SHOWN|sdl.WINDOW_VULKAN|sdl.WINDOW_RESIZABLE)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
app.window = window
|
|
|
|
app.loader, err = core.CreateLoaderFromProcAddr(sdl.VulkanGetVkGetInstanceProcAddr())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) initVulkan() error {
|
|
err := app.createInstance()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.setupDebugMessenger()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createSurface()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.pickPhysicalDevice()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createLogicalDevice()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createSwapchain()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createImageViews()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createRenderPass()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createDescriptorSetLayout()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createGraphicsPipeline()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createFramebuffers()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createCommandPool()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createVertexBuffer()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createIndexBuffer()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createUniformBuffers()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createDescriptorPool()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createDescriptorSets()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createCommandBuffers()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return app.createSyncObjects()
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) mainLoop() error {
|
|
rendering := true
|
|
|
|
appLoop:
|
|
for true {
|
|
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
|
|
switch e := event.(type) {
|
|
case *sdl.QuitEvent:
|
|
break appLoop
|
|
case *sdl.WindowEvent:
|
|
switch e.Event {
|
|
case sdl.WINDOWEVENT_MINIMIZED:
|
|
rendering = false
|
|
case sdl.WINDOWEVENT_RESTORED:
|
|
rendering = true
|
|
case sdl.WINDOWEVENT_RESIZED:
|
|
w, h := app.window.GetSize()
|
|
if w > 0 && h > 0 {
|
|
rendering = true
|
|
app.recreateSwapChain()
|
|
} else {
|
|
rendering = false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if rendering {
|
|
err := app.drawFrame()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
_, err := app.device.WaitIdle()
|
|
return err
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) cleanupSwapChain() {
|
|
for _, framebuffer := range app.swapchainFramebuffers {
|
|
framebuffer.Destroy(nil)
|
|
}
|
|
app.swapchainFramebuffers = []core1_0.Framebuffer{}
|
|
|
|
if len(app.commandBuffers) > 0 {
|
|
app.device.FreeCommandBuffers(app.commandBuffers)
|
|
app.commandBuffers = []core1_0.CommandBuffer{}
|
|
}
|
|
|
|
if app.graphicsPipeline != nil {
|
|
app.graphicsPipeline.Destroy(nil)
|
|
app.graphicsPipeline = nil
|
|
}
|
|
|
|
if app.pipelineLayout != nil {
|
|
app.pipelineLayout.Destroy(nil)
|
|
app.pipelineLayout = nil
|
|
}
|
|
|
|
if app.renderPass != nil {
|
|
app.renderPass.Destroy(nil)
|
|
app.renderPass = nil
|
|
}
|
|
|
|
for _, imageView := range app.swapchainImageViews {
|
|
imageView.Destroy(nil)
|
|
}
|
|
app.swapchainImageViews = []core1_0.ImageView{}
|
|
|
|
if app.swapchain != nil {
|
|
app.swapchain.Destroy(nil)
|
|
app.swapchain = nil
|
|
}
|
|
|
|
for i := 0; i < len(app.uniformBuffers); i++ {
|
|
app.uniformBuffers[i].Destroy(nil)
|
|
}
|
|
app.uniformBuffers = app.uniformBuffers[:0]
|
|
|
|
for i := 0; i < len(app.uniformBuffersMemory); i++ {
|
|
app.uniformBuffersMemory[i].Free(nil)
|
|
}
|
|
app.uniformBuffersMemory = app.uniformBuffersMemory[:0]
|
|
|
|
app.descriptorPool.Destroy(nil)
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) cleanup() {
|
|
app.cleanupSwapChain()
|
|
|
|
if app.descriptorSetLayout != nil {
|
|
app.descriptorSetLayout.Destroy(nil)
|
|
}
|
|
|
|
if app.indexBuffer != nil {
|
|
app.indexBuffer.Destroy(nil)
|
|
}
|
|
|
|
if app.indexBufferMemory != nil {
|
|
app.indexBufferMemory.Free(nil)
|
|
}
|
|
|
|
if app.vertexBuffer != nil {
|
|
app.vertexBuffer.Destroy(nil)
|
|
}
|
|
|
|
if app.vertexBufferMemory != nil {
|
|
app.vertexBufferMemory.Free(nil)
|
|
}
|
|
|
|
for _, fence := range app.inFlightFence {
|
|
fence.Destroy(nil)
|
|
}
|
|
|
|
for _, semaphore := range app.renderFinishedSemaphore {
|
|
semaphore.Destroy(nil)
|
|
}
|
|
|
|
for _, semaphore := range app.imageAvailableSemaphore {
|
|
semaphore.Destroy(nil)
|
|
}
|
|
|
|
if app.commandPool != nil {
|
|
app.commandPool.Destroy(nil)
|
|
}
|
|
|
|
if app.device != nil {
|
|
app.device.Destroy(nil)
|
|
}
|
|
|
|
if app.debugMessenger != nil {
|
|
app.debugMessenger.Destroy(nil)
|
|
}
|
|
|
|
if app.surface != nil {
|
|
app.surface.Destroy(nil)
|
|
}
|
|
|
|
if app.instance != nil {
|
|
app.instance.Destroy(nil)
|
|
}
|
|
|
|
if app.window != nil {
|
|
app.window.Destroy()
|
|
}
|
|
sdl.Quit()
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) recreateSwapChain() error {
|
|
w, h := app.window.VulkanGetDrawableSize()
|
|
if w == 0 || h == 0 {
|
|
return nil
|
|
}
|
|
if (app.window.GetFlags() & sdl.WINDOW_MINIMIZED) != 0 {
|
|
return nil
|
|
}
|
|
|
|
_, err := app.device.WaitIdle()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
app.cleanupSwapChain()
|
|
|
|
err = app.createSwapchain()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createImageViews()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createRenderPass()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createGraphicsPipeline()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createFramebuffers()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createUniformBuffers()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createDescriptorPool()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createDescriptorSets()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.createCommandBuffers()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
app.imagesInFlight = []core1_0.Fence{}
|
|
for i := 0; i < len(app.swapchainImages); i++ {
|
|
app.imagesInFlight = append(app.imagesInFlight, nil)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) createInstance() error {
|
|
instanceOptions := core1_0.InstanceCreateInfo{
|
|
ApplicationName: "Hello Triangle",
|
|
ApplicationVersion: common.CreateVersion(1, 0, 0),
|
|
EngineName: "No Engine",
|
|
EngineVersion: common.CreateVersion(1, 0, 0),
|
|
APIVersion: common.Vulkan1_2,
|
|
}
|
|
|
|
// Add extensions
|
|
sdlExtensions := app.window.VulkanGetInstanceExtensions()
|
|
extensions, _, err := app.loader.AvailableExtensions()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, ext := range sdlExtensions {
|
|
_, hasExt := extensions[ext]
|
|
if !hasExt {
|
|
return errors.Errorf("createinstance: cannot initialize sdl: missing extension %s", ext)
|
|
}
|
|
instanceOptions.EnabledExtensionNames = append(instanceOptions.EnabledExtensionNames, ext)
|
|
}
|
|
|
|
if enableValidationLayers {
|
|
instanceOptions.EnabledExtensionNames = append(instanceOptions.EnabledExtensionNames, ext_debug_utils.ExtensionName)
|
|
}
|
|
|
|
_, enumerationSupported := extensions[khr_portability_enumeration.ExtensionName]
|
|
if enumerationSupported {
|
|
instanceOptions.EnabledExtensionNames = append(instanceOptions.EnabledExtensionNames, khr_portability_enumeration.ExtensionName)
|
|
instanceOptions.Flags |= khr_portability_enumeration.InstanceCreateEnumeratePortability
|
|
}
|
|
|
|
// Add layers
|
|
layers, _, err := app.loader.AvailableLayers()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if enableValidationLayers {
|
|
for _, layer := range validationLayers {
|
|
_, hasValidation := layers[layer]
|
|
if !hasValidation {
|
|
return errors.Errorf("createInstance: cannot add validation- layer %s not available- install LunarG Vulkan SDK", layer)
|
|
}
|
|
instanceOptions.EnabledLayerNames = append(instanceOptions.EnabledLayerNames, layer)
|
|
}
|
|
|
|
// Add debug messenger
|
|
instanceOptions.Next = app.debugMessengerOptions()
|
|
}
|
|
|
|
app.instance, _, err = app.loader.CreateInstance(nil, instanceOptions)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) debugMessengerOptions() ext_debug_utils.DebugUtilsMessengerCreateInfo {
|
|
return ext_debug_utils.DebugUtilsMessengerCreateInfo{
|
|
MessageSeverity: ext_debug_utils.SeverityError | ext_debug_utils.SeverityWarning,
|
|
MessageType: ext_debug_utils.TypeGeneral | ext_debug_utils.TypeValidation | ext_debug_utils.TypePerformance,
|
|
UserCallback: app.logDebug,
|
|
}
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) setupDebugMessenger() error {
|
|
if !enableValidationLayers {
|
|
return nil
|
|
}
|
|
|
|
var err error
|
|
debugLoader := ext_debug_utils.CreateExtensionFromInstance(app.instance)
|
|
app.debugMessenger, _, err = debugLoader.CreateDebugUtilsMessenger(app.instance, nil, app.debugMessengerOptions())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) createSurface() error {
|
|
surfaceLoader := khr_surface.CreateExtensionFromInstance(app.instance)
|
|
|
|
surface, err := vkng_sdl2.CreateSurface(app.instance, surfaceLoader, app.window)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
app.surface = surface
|
|
return nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) pickPhysicalDevice() error {
|
|
physicalDevices, _, err := app.instance.EnumeratePhysicalDevices()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, device := range physicalDevices {
|
|
if app.isDeviceSuitable(device) {
|
|
app.physicalDevice = device
|
|
break
|
|
}
|
|
}
|
|
|
|
if app.physicalDevice == nil {
|
|
return errors.Errorf("failed to find a suitable GPU!")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) createLogicalDevice() error {
|
|
indices, err := app.findQueueFamilies(app.physicalDevice)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
uniqueQueueFamilies := []int{*indices.GraphicsFamily}
|
|
if uniqueQueueFamilies[0] != *indices.PresentFamily {
|
|
uniqueQueueFamilies = append(uniqueQueueFamilies, *indices.PresentFamily)
|
|
}
|
|
|
|
var queueFamilyOptions []core1_0.DeviceQueueCreateInfo
|
|
queuePriority := float32(1.0)
|
|
for _, queueFamily := range uniqueQueueFamilies {
|
|
queueFamilyOptions = append(queueFamilyOptions, core1_0.DeviceQueueCreateInfo{
|
|
QueueFamilyIndex: queueFamily,
|
|
QueuePriorities: []float32{queuePriority},
|
|
})
|
|
}
|
|
|
|
var extensionNames []string
|
|
extensionNames = append(extensionNames, deviceExtensions...)
|
|
|
|
// Makes this example compatible with vulkan portability, necessary to run on mobile & mac
|
|
extensions, _, err := app.physicalDevice.EnumerateDeviceExtensionProperties()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, supported := extensions[khr_portability_subset.ExtensionName]
|
|
if supported {
|
|
extensionNames = append(extensionNames, khr_portability_subset.ExtensionName)
|
|
}
|
|
|
|
app.device, _, err = app.physicalDevice.CreateDevice(nil, core1_0.DeviceCreateInfo{
|
|
QueueCreateInfos: queueFamilyOptions,
|
|
EnabledFeatures: &core1_0.PhysicalDeviceFeatures{},
|
|
EnabledExtensionNames: extensionNames,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
app.graphicsQueue = app.device.GetQueue(*indices.GraphicsFamily, 0)
|
|
app.presentQueue = app.device.GetQueue(*indices.PresentFamily, 0)
|
|
return nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) createSwapchain() error {
|
|
app.swapchainExtension = khr_swapchain.CreateExtensionFromDevice(app.device)
|
|
|
|
swapchainSupport, err := app.querySwapChainSupport(app.physicalDevice)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
surfaceFormat := app.chooseSwapSurfaceFormat(swapchainSupport.Formats)
|
|
presentMode := app.chooseSwapPresentMode(swapchainSupport.PresentModes)
|
|
extent := app.chooseSwapExtent(swapchainSupport.Capabilities)
|
|
|
|
imageCount := swapchainSupport.Capabilities.MinImageCount + 1
|
|
if swapchainSupport.Capabilities.MaxImageCount > 0 && swapchainSupport.Capabilities.MaxImageCount < imageCount {
|
|
imageCount = swapchainSupport.Capabilities.MaxImageCount
|
|
}
|
|
|
|
sharingMode := core1_0.SharingModeExclusive
|
|
var queueFamilyIndices []int
|
|
|
|
indices, err := app.findQueueFamilies(app.physicalDevice)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if *indices.GraphicsFamily != *indices.PresentFamily {
|
|
sharingMode = core1_0.SharingModeConcurrent
|
|
queueFamilyIndices = append(queueFamilyIndices, *indices.GraphicsFamily, *indices.PresentFamily)
|
|
}
|
|
|
|
swapchain, _, err := app.swapchainExtension.CreateSwapchain(app.device, nil, khr_swapchain.SwapchainCreateInfo{
|
|
Surface: app.surface,
|
|
|
|
MinImageCount: imageCount,
|
|
ImageFormat: surfaceFormat.Format,
|
|
ImageColorSpace: surfaceFormat.ColorSpace,
|
|
ImageExtent: extent,
|
|
ImageArrayLayers: 1,
|
|
ImageUsage: core1_0.ImageUsageColorAttachment,
|
|
|
|
ImageSharingMode: sharingMode,
|
|
QueueFamilyIndices: queueFamilyIndices,
|
|
|
|
PreTransform: swapchainSupport.Capabilities.CurrentTransform,
|
|
CompositeAlpha: khr_surface.CompositeAlphaOpaque,
|
|
PresentMode: presentMode,
|
|
Clipped: true,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
app.swapchainExtent = extent
|
|
app.swapchain = swapchain
|
|
app.swapchainImageFormat = surfaceFormat.Format
|
|
|
|
return nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) createImageViews() error {
|
|
images, _, err := app.swapchain.SwapchainImages()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
app.swapchainImages = images
|
|
|
|
var imageViews []core1_0.ImageView
|
|
for _, image := range images {
|
|
view, _, err := app.device.CreateImageView(nil, core1_0.ImageViewCreateInfo{
|
|
ViewType: core1_0.ImageViewType2D,
|
|
Image: image,
|
|
Format: app.swapchainImageFormat,
|
|
Components: core1_0.ComponentMapping{
|
|
R: core1_0.ComponentSwizzleIdentity,
|
|
G: core1_0.ComponentSwizzleIdentity,
|
|
B: core1_0.ComponentSwizzleIdentity,
|
|
A: core1_0.ComponentSwizzleIdentity,
|
|
},
|
|
SubresourceRange: core1_0.ImageSubresourceRange{
|
|
AspectMask: core1_0.ImageAspectColor,
|
|
BaseMipLevel: 0,
|
|
LevelCount: 1,
|
|
BaseArrayLayer: 0,
|
|
LayerCount: 1,
|
|
},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
imageViews = append(imageViews, view)
|
|
}
|
|
app.swapchainImageViews = imageViews
|
|
|
|
return nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) createRenderPass() error {
|
|
renderPass, _, err := app.device.CreateRenderPass(nil, core1_0.RenderPassCreateInfo{
|
|
Attachments: []core1_0.AttachmentDescription{
|
|
{
|
|
Format: app.swapchainImageFormat,
|
|
Samples: core1_0.Samples1,
|
|
LoadOp: core1_0.AttachmentLoadOpClear,
|
|
StoreOp: core1_0.AttachmentStoreOpStore,
|
|
StencilLoadOp: core1_0.AttachmentLoadOpDontCare,
|
|
StencilStoreOp: core1_0.AttachmentStoreOpDontCare,
|
|
InitialLayout: core1_0.ImageLayoutUndefined,
|
|
FinalLayout: khr_swapchain.ImageLayoutPresentSrc,
|
|
},
|
|
},
|
|
Subpasses: []core1_0.SubpassDescription{
|
|
{
|
|
PipelineBindPoint: core1_0.PipelineBindPointGraphics,
|
|
ColorAttachments: []core1_0.AttachmentReference{
|
|
{
|
|
Attachment: 0,
|
|
Layout: core1_0.ImageLayoutColorAttachmentOptimal,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
SubpassDependencies: []core1_0.SubpassDependency{
|
|
{
|
|
SrcSubpass: core1_0.SubpassExternal,
|
|
DstSubpass: 0,
|
|
|
|
SrcStageMask: core1_0.PipelineStageColorAttachmentOutput,
|
|
SrcAccessMask: 0,
|
|
|
|
DstStageMask: core1_0.PipelineStageColorAttachmentOutput,
|
|
DstAccessMask: core1_0.AccessColorAttachmentWrite,
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
app.renderPass = renderPass
|
|
|
|
return nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) createDescriptorSetLayout() error {
|
|
var err error
|
|
app.descriptorSetLayout, _, err = app.device.CreateDescriptorSetLayout(nil, core1_0.DescriptorSetLayoutCreateInfo{
|
|
Bindings: []core1_0.DescriptorSetLayoutBinding{
|
|
{
|
|
Binding: 0,
|
|
DescriptorType: core1_0.DescriptorTypeUniformBuffer,
|
|
DescriptorCount: 1,
|
|
|
|
StageFlags: core1_0.StageVertex,
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func bytesToBytecode(b []byte) []uint32 {
|
|
byteCode := make([]uint32, len(b)/4)
|
|
for i := 0; i < len(byteCode); i++ {
|
|
byteIndex := i * 4
|
|
byteCode[i] = 0
|
|
byteCode[i] |= uint32(b[byteIndex])
|
|
byteCode[i] |= uint32(b[byteIndex+1]) << 8
|
|
byteCode[i] |= uint32(b[byteIndex+2]) << 16
|
|
byteCode[i] |= uint32(b[byteIndex+3]) << 24
|
|
}
|
|
|
|
return byteCode
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) createGraphicsPipeline() error {
|
|
// Load vertex shader
|
|
vertShaderBytes, err := shaders.ReadFile("shaders/vert.spv")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
vertShader, _, err := app.device.CreateShaderModule(nil, core1_0.ShaderModuleCreateInfo{
|
|
Code: bytesToBytecode(vertShaderBytes),
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer vertShader.Destroy(nil)
|
|
|
|
// Load fragment shader
|
|
fragShaderBytes, err := shaders.ReadFile("shaders/frag.spv")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fragShader, _, err := app.device.CreateShaderModule(nil, core1_0.ShaderModuleCreateInfo{
|
|
Code: bytesToBytecode(fragShaderBytes),
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer fragShader.Destroy(nil)
|
|
|
|
vertexInput := &core1_0.PipelineVertexInputStateCreateInfo{
|
|
VertexBindingDescriptions: getVertexBindingDescription(),
|
|
VertexAttributeDescriptions: getVertexAttributeDescriptions(),
|
|
}
|
|
|
|
inputAssembly := &core1_0.PipelineInputAssemblyStateCreateInfo{
|
|
Topology: core1_0.PrimitiveTopologyTriangleList,
|
|
PrimitiveRestartEnable: false,
|
|
}
|
|
|
|
vertStage := core1_0.PipelineShaderStageCreateInfo{
|
|
Stage: core1_0.StageVertex,
|
|
Module: vertShader,
|
|
Name: "main",
|
|
}
|
|
|
|
fragStage := core1_0.PipelineShaderStageCreateInfo{
|
|
Stage: core1_0.StageFragment,
|
|
Module: fragShader,
|
|
Name: "main",
|
|
}
|
|
|
|
viewport := &core1_0.PipelineViewportStateCreateInfo{
|
|
Viewports: []core1_0.Viewport{
|
|
{
|
|
X: 0,
|
|
Y: 0,
|
|
Width: float32(app.swapchainExtent.Width),
|
|
Height: float32(app.swapchainExtent.Height),
|
|
MinDepth: 0,
|
|
MaxDepth: 1,
|
|
},
|
|
},
|
|
Scissors: []core1_0.Rect2D{
|
|
{
|
|
Offset: core1_0.Offset2D{X: 0, Y: 0},
|
|
Extent: app.swapchainExtent,
|
|
},
|
|
},
|
|
}
|
|
|
|
rasterization := &core1_0.PipelineRasterizationStateCreateInfo{
|
|
DepthClampEnable: false,
|
|
RasterizerDiscardEnable: false,
|
|
|
|
PolygonMode: core1_0.PolygonModeFill,
|
|
CullMode: core1_0.CullModeBack,
|
|
FrontFace: core1_0.FrontFaceCounterClockwise,
|
|
|
|
DepthBiasEnable: false,
|
|
|
|
LineWidth: 1.0,
|
|
}
|
|
|
|
multisample := &core1_0.PipelineMultisampleStateCreateInfo{
|
|
SampleShadingEnable: false,
|
|
RasterizationSamples: core1_0.Samples1,
|
|
MinSampleShading: 1.0,
|
|
}
|
|
|
|
colorBlend := &core1_0.PipelineColorBlendStateCreateInfo{
|
|
LogicOpEnabled: false,
|
|
LogicOp: core1_0.LogicOpCopy,
|
|
|
|
BlendConstants: [4]float32{0, 0, 0, 0},
|
|
Attachments: []core1_0.PipelineColorBlendAttachmentState{
|
|
{
|
|
BlendEnabled: false,
|
|
ColorWriteMask: core1_0.ColorComponentRed | core1_0.ColorComponentGreen | core1_0.ColorComponentBlue | core1_0.ColorComponentAlpha,
|
|
},
|
|
},
|
|
}
|
|
|
|
app.pipelineLayout, _, err = app.device.CreatePipelineLayout(nil, core1_0.PipelineLayoutCreateInfo{
|
|
SetLayouts: []core1_0.DescriptorSetLayout{
|
|
app.descriptorSetLayout,
|
|
},
|
|
})
|
|
|
|
pipelines, _, err := app.device.CreateGraphicsPipelines(nil, nil, []core1_0.GraphicsPipelineCreateInfo{
|
|
{
|
|
Stages: []core1_0.PipelineShaderStageCreateInfo{
|
|
vertStage,
|
|
fragStage,
|
|
},
|
|
VertexInputState: vertexInput,
|
|
InputAssemblyState: inputAssembly,
|
|
ViewportState: viewport,
|
|
RasterizationState: rasterization,
|
|
MultisampleState: multisample,
|
|
ColorBlendState: colorBlend,
|
|
Layout: app.pipelineLayout,
|
|
RenderPass: app.renderPass,
|
|
Subpass: 0,
|
|
BasePipelineIndex: -1,
|
|
},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
app.graphicsPipeline = pipelines[0]
|
|
|
|
return nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) createFramebuffers() error {
|
|
for _, imageView := range app.swapchainImageViews {
|
|
framebuffer, _, err := app.device.CreateFramebuffer(nil, core1_0.FramebufferCreateInfo{
|
|
RenderPass: app.renderPass,
|
|
Layers: 1,
|
|
Attachments: []core1_0.ImageView{
|
|
imageView,
|
|
},
|
|
Width: app.swapchainExtent.Width,
|
|
Height: app.swapchainExtent.Height,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
app.swapchainFramebuffers = append(app.swapchainFramebuffers, framebuffer)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) createCommandPool() error {
|
|
indices, err := app.findQueueFamilies(app.physicalDevice)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pool, _, err := app.device.CreateCommandPool(nil, core1_0.CommandPoolCreateInfo{
|
|
QueueFamilyIndex: *indices.GraphicsFamily,
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
app.commandPool = pool
|
|
|
|
return nil
|
|
}
|
|
|
|
func writeData(memory core1_0.DeviceMemory, offset int, data any) error {
|
|
bufferSize := binary.Size(data)
|
|
|
|
memoryPtr, _, err := memory.Map(offset, bufferSize, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer memory.Unmap()
|
|
|
|
dataBuffer := unsafe.Slice((*byte)(memoryPtr), bufferSize)
|
|
|
|
buf := &bytes.Buffer{}
|
|
err = binary.Write(buf, common.ByteOrder, data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
copy(dataBuffer, buf.Bytes())
|
|
return nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) createVertexBuffer() error {
|
|
var err error
|
|
bufferSize := binary.Size(vertices)
|
|
|
|
stagingBuffer, stagingBufferMemory, err := app.createBuffer(bufferSize, core1_0.BufferUsageTransferSrc, core1_0.MemoryPropertyHostVisible|core1_0.MemoryPropertyHostCoherent)
|
|
if stagingBuffer != nil {
|
|
defer stagingBuffer.Destroy(nil)
|
|
}
|
|
if stagingBufferMemory != nil {
|
|
defer stagingBufferMemory.Free(nil)
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = writeData(stagingBufferMemory, 0, vertices)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
app.vertexBuffer, app.vertexBufferMemory, err = app.createBuffer(bufferSize, core1_0.BufferUsageTransferDst|core1_0.BufferUsageVertexBuffer, core1_0.MemoryPropertyDeviceLocal)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return app.copyBuffer(stagingBuffer, app.vertexBuffer, bufferSize)
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) createIndexBuffer() error {
|
|
bufferSize := binary.Size(indices)
|
|
|
|
stagingBuffer, stagingBufferMemory, err := app.createBuffer(bufferSize, core1_0.BufferUsageTransferSrc, core1_0.MemoryPropertyHostVisible|core1_0.MemoryPropertyHostCoherent)
|
|
if stagingBuffer != nil {
|
|
defer stagingBuffer.Destroy(nil)
|
|
}
|
|
if stagingBufferMemory != nil {
|
|
defer stagingBufferMemory.Free(nil)
|
|
}
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = writeData(stagingBufferMemory, 0, indices)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
app.indexBuffer, app.indexBufferMemory, err = app.createBuffer(bufferSize, core1_0.BufferUsageTransferDst|core1_0.BufferUsageIndexBuffer, core1_0.MemoryPropertyDeviceLocal)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return app.copyBuffer(stagingBuffer, app.indexBuffer, bufferSize)
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) createUniformBuffers() error {
|
|
bufferSize := int(unsafe.Sizeof(UniformBufferObject{}))
|
|
|
|
for i := 0; i < len(app.swapchainImages); i++ {
|
|
buffer, memory, err := app.createBuffer(bufferSize, core1_0.BufferUsageUniformBuffer, core1_0.MemoryPropertyHostVisible|core1_0.MemoryPropertyHostCoherent)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
app.uniformBuffers = append(app.uniformBuffers, buffer)
|
|
app.uniformBuffersMemory = append(app.uniformBuffersMemory, memory)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) createDescriptorPool() error {
|
|
var err error
|
|
app.descriptorPool, _, err = app.device.CreateDescriptorPool(nil, core1_0.DescriptorPoolCreateInfo{
|
|
MaxSets: len(app.swapchainImages),
|
|
PoolSizes: []core1_0.DescriptorPoolSize{
|
|
{
|
|
Type: core1_0.DescriptorTypeUniformBuffer,
|
|
DescriptorCount: len(app.swapchainImages),
|
|
},
|
|
},
|
|
})
|
|
return err
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) createDescriptorSets() error {
|
|
var allocLayouts []core1_0.DescriptorSetLayout
|
|
for i := 0; i < len(app.swapchainImages); i++ {
|
|
allocLayouts = append(allocLayouts, app.descriptorSetLayout)
|
|
}
|
|
|
|
var err error
|
|
app.descriptorSets, _, err = app.device.AllocateDescriptorSets(core1_0.DescriptorSetAllocateInfo{
|
|
DescriptorPool: app.descriptorPool,
|
|
SetLayouts: allocLayouts,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for i := 0; i < len(app.swapchainImages); i++ {
|
|
err = app.device.UpdateDescriptorSets([]core1_0.WriteDescriptorSet{
|
|
{
|
|
DstSet: app.descriptorSets[i],
|
|
DstBinding: 0,
|
|
DstArrayElement: 0,
|
|
|
|
DescriptorType: core1_0.DescriptorTypeUniformBuffer,
|
|
|
|
BufferInfo: []core1_0.DescriptorBufferInfo{
|
|
{
|
|
Buffer: app.uniformBuffers[i],
|
|
Offset: 0,
|
|
Range: int(unsafe.Sizeof(UniformBufferObject{})),
|
|
},
|
|
},
|
|
},
|
|
}, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) createBuffer(size int, usage core1_0.BufferUsageFlags, properties core1_0.MemoryPropertyFlags) (core1_0.Buffer, core1_0.DeviceMemory, error) {
|
|
buffer, _, err := app.device.CreateBuffer(nil, core1_0.BufferCreateInfo{
|
|
Size: size,
|
|
Usage: usage,
|
|
SharingMode: core1_0.SharingModeExclusive,
|
|
})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
memRequirements := buffer.MemoryRequirements()
|
|
memoryTypeIndex, err := app.findMemoryType(memRequirements.MemoryTypeBits, properties)
|
|
if err != nil {
|
|
return buffer, nil, err
|
|
}
|
|
|
|
memory, _, err := app.device.AllocateMemory(nil, core1_0.MemoryAllocateInfo{
|
|
AllocationSize: memRequirements.Size,
|
|
MemoryTypeIndex: memoryTypeIndex,
|
|
})
|
|
if err != nil {
|
|
return buffer, nil, err
|
|
}
|
|
|
|
_, err = buffer.BindBufferMemory(memory, 0)
|
|
return buffer, memory, err
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) copyBuffer(srcBuffer core1_0.Buffer, dstBuffer core1_0.Buffer, size int) error {
|
|
buffers, _, err := app.device.AllocateCommandBuffers(core1_0.CommandBufferAllocateInfo{
|
|
CommandPool: app.commandPool,
|
|
Level: core1_0.CommandBufferLevelPrimary,
|
|
CommandBufferCount: 1,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
buffer := buffers[0]
|
|
_, err = buffer.Begin(core1_0.CommandBufferBeginInfo{
|
|
Flags: core1_0.CommandBufferUsageOneTimeSubmit,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer app.device.FreeCommandBuffers(buffers)
|
|
|
|
buffer.CmdCopyBuffer(srcBuffer, dstBuffer, []core1_0.BufferCopy{
|
|
{
|
|
SrcOffset: 0,
|
|
DstOffset: 0,
|
|
Size: size,
|
|
},
|
|
})
|
|
|
|
_, err = buffer.End()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = app.graphicsQueue.Submit(nil, []core1_0.SubmitInfo{
|
|
{
|
|
CommandBuffers: []core1_0.CommandBuffer{buffer},
|
|
},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = app.graphicsQueue.WaitIdle()
|
|
return err
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) findMemoryType(typeFilter uint32, properties core1_0.MemoryPropertyFlags) (int, error) {
|
|
memProperties := app.physicalDevice.MemoryProperties()
|
|
for i, memoryType := range memProperties.MemoryTypes {
|
|
typeBit := uint32(1 << i)
|
|
|
|
if (typeFilter&typeBit) != 0 && (memoryType.PropertyFlags&properties) == properties {
|
|
return i, nil
|
|
}
|
|
}
|
|
|
|
return 0, errors.Errorf("failed to find any suitable memory type!")
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) createCommandBuffers() error {
|
|
|
|
buffers, _, err := app.device.AllocateCommandBuffers(core1_0.CommandBufferAllocateInfo{
|
|
CommandPool: app.commandPool,
|
|
Level: core1_0.CommandBufferLevelPrimary,
|
|
CommandBufferCount: len(app.swapchainImages),
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
app.commandBuffers = buffers
|
|
|
|
for bufferIdx, buffer := range buffers {
|
|
_, err = buffer.Begin(core1_0.CommandBufferBeginInfo{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = buffer.CmdBeginRenderPass(core1_0.SubpassContentsInline,
|
|
core1_0.RenderPassBeginInfo{
|
|
RenderPass: app.renderPass,
|
|
Framebuffer: app.swapchainFramebuffers[bufferIdx],
|
|
RenderArea: core1_0.Rect2D{
|
|
Offset: core1_0.Offset2D{X: 0, Y: 0},
|
|
Extent: app.swapchainExtent,
|
|
},
|
|
ClearValues: []core1_0.ClearValue{
|
|
core1_0.ClearValueFloat{0, 0, 0, 1},
|
|
},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
buffer.CmdBindPipeline(core1_0.PipelineBindPointGraphics, app.graphicsPipeline)
|
|
buffer.CmdBindVertexBuffers(0, []core1_0.Buffer{app.vertexBuffer}, []int{0})
|
|
buffer.CmdBindIndexBuffer(app.indexBuffer, 0, core1_0.IndexTypeUInt16)
|
|
buffer.CmdBindDescriptorSets(core1_0.PipelineBindPointGraphics, app.pipelineLayout, 0, []core1_0.DescriptorSet{
|
|
app.descriptorSets[bufferIdx],
|
|
}, nil)
|
|
buffer.CmdDrawIndexed(len(indices), 1, 0, 0, 0)
|
|
buffer.CmdEndRenderPass()
|
|
|
|
_, err = buffer.End()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) createSyncObjects() error {
|
|
for i := 0; i < MaxFramesInFlight; i++ {
|
|
semaphore, _, err := app.device.CreateSemaphore(nil, core1_0.SemaphoreCreateInfo{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
app.imageAvailableSemaphore = append(app.imageAvailableSemaphore, semaphore)
|
|
|
|
semaphore, _, err = app.device.CreateSemaphore(nil, core1_0.SemaphoreCreateInfo{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
app.renderFinishedSemaphore = append(app.renderFinishedSemaphore, semaphore)
|
|
|
|
fence, _, err := app.device.CreateFence(nil, core1_0.FenceCreateInfo{
|
|
Flags: core1_0.FenceCreateSignaled,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
app.inFlightFence = append(app.inFlightFence, fence)
|
|
}
|
|
|
|
for i := 0; i < len(app.swapchainImages); i++ {
|
|
app.imagesInFlight = append(app.imagesInFlight, nil)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) drawFrame() error {
|
|
fences := []core1_0.Fence{app.inFlightFence[app.currentFrame]}
|
|
|
|
_, err := app.device.WaitForFences(true, common.NoTimeout, fences)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
imageIndex, res, err := app.swapchain.AcquireNextImage(common.NoTimeout, app.imageAvailableSemaphore[app.currentFrame], nil)
|
|
if res == khr_swapchain.VKErrorOutOfDate {
|
|
return app.recreateSwapChain()
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
|
|
if app.imagesInFlight[imageIndex] != nil {
|
|
_, err := app.imagesInFlight[imageIndex].Wait(common.NoTimeout)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
app.imagesInFlight[imageIndex] = app.inFlightFence[app.currentFrame]
|
|
|
|
_, err = app.device.ResetFences(fences)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = app.updateUniformBuffer(imageIndex)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = app.graphicsQueue.Submit(app.inFlightFence[app.currentFrame], []core1_0.SubmitInfo{
|
|
{
|
|
WaitSemaphores: []core1_0.Semaphore{app.imageAvailableSemaphore[app.currentFrame]},
|
|
WaitDstStageMask: []core1_0.PipelineStageFlags{core1_0.PipelineStageColorAttachmentOutput},
|
|
CommandBuffers: []core1_0.CommandBuffer{app.commandBuffers[imageIndex]},
|
|
SignalSemaphores: []core1_0.Semaphore{app.renderFinishedSemaphore[app.currentFrame]},
|
|
},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
res, err = app.swapchainExtension.QueuePresent(app.presentQueue, khr_swapchain.PresentInfo{
|
|
WaitSemaphores: []core1_0.Semaphore{app.renderFinishedSemaphore[app.currentFrame]},
|
|
Swapchains: []khr_swapchain.Swapchain{app.swapchain},
|
|
ImageIndices: []int{imageIndex},
|
|
})
|
|
if res == khr_swapchain.VKErrorOutOfDate || res == khr_swapchain.VKSuboptimal {
|
|
return app.recreateSwapChain()
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
|
|
app.currentFrame = (app.currentFrame + 1) % MaxFramesInFlight
|
|
|
|
return nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) updateUniformBuffer(currentImage int) error {
|
|
currentTime := hrtime.Now().Seconds()
|
|
timePeriod := math.Mod(currentTime, 4.0)
|
|
|
|
ubo := UniformBufferObject{}
|
|
ubo.Model.SetRotationZ(timePeriod * math.Pi / 2.0)
|
|
ubo.View.SetLookAt(
|
|
&vkngmath.Vec3[float32]{X: 2, Y: 2, Z: 2},
|
|
&vkngmath.Vec3[float32]{X: 0, Y: 0, Z: 0},
|
|
&vkngmath.Vec3[float32]{X: 0, Y: 0, Z: 1},
|
|
)
|
|
aspectRatio := float32(app.swapchainExtent.Width) / float32(app.swapchainExtent.Height)
|
|
ubo.Proj.SetPerspective(math.Pi/4.0, aspectRatio, 0.1, 10)
|
|
|
|
err := writeData(app.uniformBuffersMemory[currentImage], 0, &ubo)
|
|
return err
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) chooseSwapSurfaceFormat(availableFormats []khr_surface.SurfaceFormat) khr_surface.SurfaceFormat {
|
|
for _, format := range availableFormats {
|
|
if format.Format == core1_0.FormatB8G8R8A8SRGB && format.ColorSpace == khr_surface.ColorSpaceSRGBNonlinear {
|
|
return format
|
|
}
|
|
}
|
|
|
|
return availableFormats[0]
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) chooseSwapPresentMode(availablePresentModes []khr_surface.PresentMode) khr_surface.PresentMode {
|
|
for _, presentMode := range availablePresentModes {
|
|
if presentMode == khr_surface.PresentModeMailbox {
|
|
return presentMode
|
|
}
|
|
}
|
|
|
|
return khr_surface.PresentModeFIFO
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) chooseSwapExtent(capabilities *khr_surface.SurfaceCapabilities) core1_0.Extent2D {
|
|
if capabilities.CurrentExtent.Width != -1 {
|
|
return capabilities.CurrentExtent
|
|
}
|
|
|
|
widthInt, heightInt := app.window.VulkanGetDrawableSize()
|
|
width := int(widthInt)
|
|
height := int(heightInt)
|
|
|
|
if width < capabilities.MinImageExtent.Width {
|
|
width = capabilities.MinImageExtent.Width
|
|
}
|
|
if width > capabilities.MaxImageExtent.Width {
|
|
width = capabilities.MaxImageExtent.Width
|
|
}
|
|
if height < capabilities.MinImageExtent.Height {
|
|
height = capabilities.MinImageExtent.Height
|
|
}
|
|
if height > capabilities.MaxImageExtent.Height {
|
|
height = capabilities.MaxImageExtent.Height
|
|
}
|
|
|
|
return core1_0.Extent2D{Width: width, Height: height}
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) querySwapChainSupport(device core1_0.PhysicalDevice) (SwapChainSupportDetails, error) {
|
|
var details SwapChainSupportDetails
|
|
var err error
|
|
|
|
details.Capabilities, _, err = app.surface.PhysicalDeviceSurfaceCapabilities(device)
|
|
if err != nil {
|
|
return details, err
|
|
}
|
|
|
|
details.Formats, _, err = app.surface.PhysicalDeviceSurfaceFormats(device)
|
|
if err != nil {
|
|
return details, err
|
|
}
|
|
|
|
details.PresentModes, _, err = app.surface.PhysicalDeviceSurfacePresentModes(device)
|
|
return details, err
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) isDeviceSuitable(device core1_0.PhysicalDevice) bool {
|
|
indices, err := app.findQueueFamilies(device)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
extensionsSupported := app.checkDeviceExtensionSupport(device)
|
|
|
|
var swapChainAdequate bool
|
|
if extensionsSupported {
|
|
swapChainSupport, err := app.querySwapChainSupport(device)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
swapChainAdequate = len(swapChainSupport.Formats) > 0 && len(swapChainSupport.PresentModes) > 0
|
|
}
|
|
|
|
return indices.IsComplete() && extensionsSupported && swapChainAdequate
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) checkDeviceExtensionSupport(device core1_0.PhysicalDevice) bool {
|
|
extensions, _, err := device.EnumerateDeviceExtensionProperties()
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
for _, extension := range deviceExtensions {
|
|
_, hasExtension := extensions[extension]
|
|
if !hasExtension {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) findQueueFamilies(device core1_0.PhysicalDevice) (QueueFamilyIndices, error) {
|
|
indices := QueueFamilyIndices{}
|
|
queueFamilies := device.QueueFamilyProperties()
|
|
|
|
for queueFamilyIdx, queueFamily := range queueFamilies {
|
|
if (queueFamily.QueueFlags & core1_0.QueueGraphics) != 0 {
|
|
indices.GraphicsFamily = new(int)
|
|
*indices.GraphicsFamily = queueFamilyIdx
|
|
}
|
|
|
|
supported, _, err := app.surface.PhysicalDeviceSurfaceSupport(device, queueFamilyIdx)
|
|
if err != nil {
|
|
return indices, err
|
|
}
|
|
|
|
if supported {
|
|
indices.PresentFamily = new(int)
|
|
*indices.PresentFamily = queueFamilyIdx
|
|
}
|
|
|
|
if indices.IsComplete() {
|
|
break
|
|
}
|
|
}
|
|
|
|
return indices, nil
|
|
}
|
|
|
|
func (app *HelloTriangleApplication) logDebug(msgType ext_debug_utils.DebugUtilsMessageTypeFlags, severity ext_debug_utils.DebugUtilsMessageSeverityFlags, data *ext_debug_utils.DebugUtilsMessengerCallbackData) bool {
|
|
log.Printf("[%s %s] - %s", severity, msgType, data.Message)
|
|
return false
|
|
}
|
|
|
|
func main() {
|
|
app := &HelloTriangleApplication{}
|
|
|
|
err := app.Run()
|
|
if err != nil {
|
|
log.Fatalf("%+v\n", err)
|
|
}
|
|
}
|