zworld-demo/engine/render/vulkan/instance/instance_init.go
2024-01-09 21:43:28 +08:00

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()
}