515 lines
13 KiB
Go
515 lines
13 KiB
Go
package instance
|
|
|
|
import (
|
|
"github.com/pkg/errors"
|
|
"github.com/vkngwrapper/core/v2/core1_0"
|
|
"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"
|
|
"os"
|
|
)
|
|
|
|
func (e *FInstance) createSurface() error {
|
|
surfaceLoader := khr_surface.CreateExtensionFromInstance(e.ptr)
|
|
|
|
surface, err := vkng_sdl2.CreateSurface(e.ptr, surfaceLoader, e.win.GetInstance())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
e.surface = surface
|
|
return nil
|
|
}
|
|
func (e *FInstance) pickPhysicalDevice() error {
|
|
physicalDevices, _, err := e.ptr.EnumeratePhysicalDevices()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, device := range physicalDevices {
|
|
if help.isDeviceSuitable(device) {
|
|
e.physicalDevice = device
|
|
break
|
|
}
|
|
}
|
|
|
|
if e.physicalDevice == nil {
|
|
return errors.New("failed to find a suitable GPU!")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
func (e *FInstance) createLogicalDevice() error {
|
|
indices, err := help.findQueueFamilies(e.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 := e.physicalDevice.EnumerateDeviceExtensionProperties()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, supported := extensions[khr_portability_subset.ExtensionName]
|
|
if supported {
|
|
extensionNames = append(extensionNames, khr_portability_subset.ExtensionName)
|
|
}
|
|
|
|
e.device, _, err = e.physicalDevice.CreateDevice(nil, core1_0.DeviceCreateInfo{
|
|
QueueCreateInfos: queueFamilyOptions,
|
|
EnabledFeatures: &core1_0.PhysicalDeviceFeatures{},
|
|
EnabledExtensionNames: extensionNames,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
e.graphicsQueue = e.device.GetQueue(*indices.GraphicsFamily, 0)
|
|
e.presentQueue = e.device.GetQueue(*indices.PresentFamily, 0)
|
|
return nil
|
|
}
|
|
|
|
func (e *FInstance) createSwapchain() error {
|
|
e.swapchainExtension = khr_swapchain.CreateExtensionFromDevice(e.device)
|
|
|
|
swapchainSupport, err := help.querySwapChainSupport(e.physicalDevice)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
surfaceFormat := help.chooseSwapSurfaceFormat(swapchainSupport.Formats)
|
|
presentMode := help.chooseSwapPresentMode(swapchainSupport.PresentModes)
|
|
extent := help.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 := help.findQueueFamilies(e.physicalDevice)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if *indices.GraphicsFamily != *indices.PresentFamily {
|
|
sharingMode = core1_0.SharingModeConcurrent
|
|
queueFamilyIndices = append(queueFamilyIndices, *indices.GraphicsFamily, *indices.PresentFamily)
|
|
}
|
|
|
|
swapchain, _, err := e.swapchainExtension.CreateSwapchain(e.device, nil, khr_swapchain.SwapchainCreateInfo{
|
|
Surface: e.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
|
|
}
|
|
e.swapchainExtent = extent
|
|
e.swapchain = swapchain
|
|
e.swapchainImageFormat = surfaceFormat.Format
|
|
|
|
images, _, err := swapchain.SwapchainImages()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
e.swapchainImages = images
|
|
|
|
var imageViews []core1_0.ImageView
|
|
for _, image := range images {
|
|
view, _, err := e.device.CreateImageView(nil, core1_0.ImageViewCreateInfo{
|
|
ViewType: core1_0.ImageViewType2D,
|
|
Image: image,
|
|
Format: surfaceFormat.Format,
|
|
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)
|
|
}
|
|
e.swapchainImageViews = imageViews
|
|
|
|
return nil
|
|
}
|
|
func (e *FInstance) createRenderPass() error {
|
|
renderPass, _, err := e.device.CreateRenderPass(nil, core1_0.RenderPassCreateInfo{
|
|
Attachments: []core1_0.AttachmentDescription{
|
|
{
|
|
Format: e.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
|
|
}
|
|
|
|
e.renderPass = renderPass
|
|
|
|
return nil
|
|
}
|
|
|
|
func (e *FInstance) createGraphicsPipeline() error {
|
|
// Load vertex shader
|
|
vertShaderBytes, err := os.ReadFile("asset/shaders/vert.spv")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
vertShader, _, err := e.device.CreateShaderModule(nil, core1_0.ShaderModuleCreateInfo{
|
|
Code: bytesToBytecode(vertShaderBytes),
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer vertShader.Destroy(nil)
|
|
|
|
// Load fragment shader
|
|
fragShaderBytes, err := os.ReadFile("asset/shaders/frag.spv")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fragShader, _, err := e.device.CreateShaderModule(nil, core1_0.ShaderModuleCreateInfo{
|
|
Code: bytesToBytecode(fragShaderBytes),
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer fragShader.Destroy(nil)
|
|
|
|
vertexInput := &core1_0.PipelineVertexInputStateCreateInfo{}
|
|
|
|
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(e.swapchainExtent.Width),
|
|
Height: float32(e.swapchainExtent.Height),
|
|
MinDepth: 0,
|
|
MaxDepth: 1,
|
|
},
|
|
},
|
|
Scissors: []core1_0.Rect2D{
|
|
{
|
|
Offset: core1_0.Offset2D{X: 0, Y: 0},
|
|
Extent: e.swapchainExtent,
|
|
},
|
|
},
|
|
}
|
|
|
|
rasterization := &core1_0.PipelineRasterizationStateCreateInfo{
|
|
DepthClampEnable: false,
|
|
RasterizerDiscardEnable: false,
|
|
|
|
PolygonMode: core1_0.PolygonModeFill,
|
|
CullMode: core1_0.CullModeBack,
|
|
FrontFace: core1_0.FrontFaceClockwise,
|
|
|
|
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,
|
|
},
|
|
},
|
|
}
|
|
|
|
e.pipelineLayout, _, err = e.device.CreatePipelineLayout(nil, core1_0.PipelineLayoutCreateInfo{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pipelines, _, err := e.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: e.pipelineLayout,
|
|
RenderPass: e.renderPass,
|
|
Subpass: 0,
|
|
BasePipelineIndex: -1,
|
|
},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
e.graphicsPipeline = pipelines[0]
|
|
|
|
return nil
|
|
}
|
|
|
|
func (e *FInstance) createFramebuffers() error {
|
|
for _, imageView := range e.swapchainImageViews {
|
|
framebuffer, _, err := e.device.CreateFramebuffer(nil, core1_0.FramebufferCreateInfo{
|
|
RenderPass: e.renderPass,
|
|
Layers: 1,
|
|
Attachments: []core1_0.ImageView{
|
|
imageView,
|
|
},
|
|
Width: e.swapchainExtent.Width,
|
|
Height: e.swapchainExtent.Height,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
e.swapchainFramebuffers = append(e.swapchainFramebuffers, framebuffer)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (e *FInstance) createCommandPool() error {
|
|
indices, err := help.findQueueFamilies(e.physicalDevice)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
pool, _, err := e.device.CreateCommandPool(nil, core1_0.CommandPoolCreateInfo{
|
|
QueueFamilyIndex: *indices.GraphicsFamily,
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
e.commandPool = pool
|
|
|
|
return nil
|
|
}
|
|
|
|
func (e *FInstance) createCommandBuffers() error {
|
|
buffers, _, err := e.device.AllocateCommandBuffers(core1_0.CommandBufferAllocateInfo{
|
|
CommandPool: e.commandPool,
|
|
Level: core1_0.CommandBufferLevelPrimary,
|
|
CommandBufferCount: len(e.swapchainImages),
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
e.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: e.renderPass,
|
|
Framebuffer: e.swapchainFramebuffers[bufferIdx],
|
|
RenderArea: core1_0.Rect2D{
|
|
Offset: core1_0.Offset2D{X: 0, Y: 0},
|
|
Extent: e.swapchainExtent,
|
|
},
|
|
ClearValues: []core1_0.ClearValue{
|
|
core1_0.ClearValueFloat{0, 0, 0, 1},
|
|
},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
buffer.CmdBindPipeline(core1_0.PipelineBindPointGraphics, e.graphicsPipeline)
|
|
buffer.CmdDraw(3, 1, 0, 0)
|
|
buffer.CmdEndRenderPass()
|
|
|
|
_, err = buffer.End()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
func (e *FInstance) createSyncObjects() error {
|
|
for i := 0; i < MaxFramesInFlight; i++ {
|
|
semaphore, _, err := e.device.CreateSemaphore(nil, core1_0.SemaphoreCreateInfo{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
e.imageAvailableSemaphore = append(e.imageAvailableSemaphore, semaphore)
|
|
|
|
semaphore, _, err = e.device.CreateSemaphore(nil, core1_0.SemaphoreCreateInfo{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
e.renderFinishedSemaphore = append(e.renderFinishedSemaphore, semaphore)
|
|
|
|
fence, _, err := e.device.CreateFence(nil, core1_0.FenceCreateInfo{
|
|
Flags: core1_0.FenceCreateSignaled,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
e.inFlightFence = append(e.inFlightFence, fence)
|
|
}
|
|
|
|
for i := 0; i < len(e.swapchainImages); i++ {
|
|
e.imagesInFlight = append(e.imagesInFlight, nil)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
func (e *FInstance) Init() error {
|
|
help.vulkan = e
|
|
err := e.createSurface()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = e.pickPhysicalDevice()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = e.createLogicalDevice()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = e.createSwapchain()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = e.createRenderPass()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = e.createGraphicsPipeline()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = e.createFramebuffers()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = e.createCommandPool()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = e.createCommandBuffers()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return e.createSyncObjects()
|
|
}
|