Compare commits

..

80 commits

Author SHA1 Message Date
bab538a0e4 flake update 2023-11-05 12:02:16 +01:00
2d3fc457be a little cleaner 2023-10-22 12:46:16 +02:00
4feac51f26 weed out stuff 2023-10-22 07:14:22 +02:00
def6e56d1f implement FPS meter in Console output 2023-10-22 06:08:43 +02:00
1b6a6392ba fix rendering! 2023-10-22 02:27:33 +02:00
8f6f5e428d flake update 2023-10-22 01:32:38 +02:00
676e02ed8d more twiddling 2023-10-22 01:30:52 +02:00
167599df74 try fixing 2023-10-22 01:30:52 +02:00
9350198e35 explicitly enable multiIndirectDraw 2023-10-22 01:30:52 +02:00
9d34dacf76 condense flake 2023-10-22 01:30:52 +02:00
a6af76f3b3 fix ambiguous record updates 2023-10-22 01:30:51 +02:00
934085906c final flake fix 2023-10-22 01:30:18 +02:00
c55bfc5416 make shell 2023-10-22 01:30:18 +02:00
f873fa7291 move package 2023-10-22 01:30:18 +02:00
ccd18f803b flake update 2023-10-22 01:30:16 +02:00
fe4155d26f fix flake 2023-10-22 01:29:49 +02:00
2ae3daf0f9 fix flake 2023-10-22 01:29:49 +02:00
d2dac89ce3 small-ish fixes 2023-10-22 01:29:47 +02:00
0cb8bd8363 add test/debug VM capability 2023-10-22 01:28:21 +02:00
d9b7c48953 using indirect draw, not working yet 2023-10-22 01:27:18 +02:00
9084be8bba first little optimisation 2023-10-22 01:27:13 +02:00
3928655bb0 fix changing rules 2023-10-22 01:15:03 +02:00
ea87f58b26 flake update 2023-10-22 00:33:07 +02:00
272db907a0 fix double spaceships 2023-06-11 08:13:53 +02:00
6caf99a13c remove lambda 2023-06-11 08:13:53 +02:00
ef135731a4 first little optimisation 2023-06-11 08:13:49 +02:00
b3a7670c97 fix UVs in Code, not in shader 2023-05-30 01:44:20 +01:00
c0f8e10f44 hunt validation errors 2023-05-17 10:31:01 +02:00
89464b769f add comment 2023-05-17 06:05:05 +02:00
6f6716e69a flip uv coordintes right 2023-05-17 06:02:56 +02:00
61f800cdd9 flip y axis 2023-05-17 05:52:41 +02:00
e91afc8ada draw textured object 2023-05-16 11:14:50 +02:00
32bc3fc4b4 fix initialization 2023-05-15 14:20:18 +02:00
488c32e084 change texture dimensions 2023-05-15 12:29:04 +02:00
f06ce62cff implement textures which crash initialization 2023-05-15 12:10:02 +02:00
ca2d3cc9b1 move meshes 2023-05-15 06:45:12 +02:00
1f9434b06c init textures 2023-05-14 18:50:02 +02:00
fa4f2d6838 optimisation 2023-05-14 13:28:19 +02:00
82962d3350 move matrix transposition into poke definition 2023-05-14 10:18:09 +02:00
455124021f fix it by transposing the model matrix 2023-05-12 14:56:02 +02:00
526b9ba281 next chapter, but broken 2023-05-12 13:30:00 +02:00
fdc86a3c03 new shader 2023-05-12 13:29:44 +02:00
6f369cbec1 further clean allocations 2023-05-01 19:13:21 +02:00
bd578e1d98 some more cleaning 2023-04-29 19:54:52 +02:00
1844775cd0 flake update 2023-04-29 19:13:38 +02:00
1643d8b3db more cleanup 2023-04-29 18:56:07 +02:00
cec99c0b6c more restructuring 2023-04-29 18:52:49 +02:00
b3a1545fd2 split up and clean 2023-04-29 18:41:59 +02:00
cde6d15245 optimize allocations 2023-04-29 02:10:27 +02:00
744a8ddc55 fix the GC 2023-04-29 00:58:41 +02:00
064f31197e make code a little more reslilient 2023-04-23 02:12:04 +02:00
95928de413 remove .envrc from .gitignore 2023-04-20 06:33:31 +02:00
edc6848878 add .envrc 2023-04-20 06:26:44 +02:00
a2f986e852 cleanup 2023-04-20 06:21:43 +02:00
e3bed9a2eb make it work! 2023-04-19 17:26:33 +02:00
b8fbbc8050 make runnable on non-discrete GPUs 2023-04-19 16:07:07 +02:00
728d21a340 make runnable on non-discrete GPUs 2023-04-19 16:05:52 +02:00
153a2764a2 flake update 2023-04-19 15:40:10 +02:00
a655a8be06 first little steps 2023-04-19 15:20:44 +02:00
b5ea2b1359 half-working state… 2023-04-12 13:41:00 +02:00
b71eb31f19 update flake 2023-04-08 01:55:11 +02:00
1c6ccf0b1e update flake 2023-04-02 15:47:26 +02:00
a2d1afe8cd disable validatin temporarily 2023-04-02 15:47:26 +02:00
f735077faa invisible ambient color 2023-04-02 15:47:26 +02:00
2516d12eea updated sources 2023-03-06 18:27:48 +01:00
dce40ecf08 tinker around to hopefully get all layers needed 2023-02-03 08:54:52 +01:00
79a4fea655 start new chapter 2023-02-03 06:46:42 +01:00
f2af2a8ccf make it work 2023-01-17 05:35:57 +01:00
e8e37f1ced mew 2023-01-16 01:10:23 +01:00
341260b2cd meowy 2023-01-07 00:14:39 +01:00
206b331d19 blah 2023-01-06 20:02:17 +01:00
66455ae0d2 enable double buffering 2023-01-06 16:54:30 +01:00
be689da9bf add utility module 2023-01-06 16:03:01 +01:00
925359acc3 add triangles 2023-01-06 05:04:17 +01:00
0ebe818007 fill ReadState 2023-01-05 23:07:21 +01:00
96bc03f07b update 2023-01-02 06:00:08 +01:00
a1e0211b0f start filling the state 2023-01-02 05:59:59 +01:00
1d4372a6b5 get reader monad in place 2023-01-02 02:18:06 +01:00
5601e6fcab clean the states out 2023-01-02 02:11:12 +01:00
f7952bf6f5 clear out errors 2022-12-28 04:31:18 +01:00
32 changed files with 4938 additions and 483 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

1
.gitignore vendored
View file

@ -1,4 +1,3 @@
.direnv/ .direnv/
.envrc
dist-newstyle/ dist-newstyle/
extern/ extern/

13
assets/models/jaeger.mtl Executable file
View file

@ -0,0 +1,13 @@
# Blender MTL File: 'jaeger.blend'
# Material Count: 1
newmtl Material_jaeger.texture.tga
Ns 92.156863
Ka 1.000000 1.000000 1.000000
Kd 0.512000 0.512000 0.512000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
map_Kd jaeger.texture.tga

1237
assets/models/jaeger.obj Executable file

File diff suppressed because it is too large Load diff

1960
assets/models/marksman.obj Executable file

File diff suppressed because it is too large Load diff

BIN
assets/textures/bricks.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 MiB

View file

@ -1,12 +1,15 @@
{ {
"nodes": { "nodes": {
"flake-utils": { "flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": { "locked": {
"lastModified": 1667395993, "lastModified": 1694529238,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", "rev": "ff7b65b44d01cf9ba6a71320833626af21126384",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -17,11 +20,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1671755972, "lastModified": 1699181587,
"narHash": "sha256-X977apvpqBqqRf2XBNorfunZmQNn3cQYGEnQE4L90Fo=", "narHash": "sha256-v35mUUgVU2ituIHPNHifpjl5uiYQYyLrpaMtz8X8Ums=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "e8ee153b1717dca9c6aa38d5cf198329480d5b41", "rev": "197629b470f184a47b2a5716312d25b09454bbf8",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -35,6 +38,21 @@
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs"
} }
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
} }
}, },
"root": "root", "root": "root",

View file

@ -11,47 +11,45 @@
let let
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
haskellPackages = pkgs.haskellPackages; nixos-lib = import (nixpkgs + "/nixos/lib") {};
haskellPackages = pkgs.haskellPackages.override (old: {
overrides = final: prev: {
wavefront = jailbreakUnbreak prev.wavefront;
vulkan-tutorial = vulkan-tutorial;
};
});
vulkan-tutorial = # (ref:haskell-package-def)
haskellPackages.callCabal2nix packageName self {
# Dependency overrides go here
#wavefront = jailbreakUnbreak haskellPackages.wavefront;
};
jailbreakUnbreak = pkg: jailbreakUnbreak = pkg:
pkgs.haskell.lib.doJailbreak (pkg.overrideAttrs (_: { meta = { }; })); pkgs.haskell.lib.doJailbreak (pkgs.haskell.lib.markUnbroken pkg);
packageName = "vulkan-tutorial"; packageName = "vulkan-tutorial";
in rec { in rec {
packages.${packageName} = # (ref:haskell-package-def) packages.default = haskellPackages.vulkan-tutorial;
haskellPackages.callCabal2nix packageName self rec {
# Dependency overrides go here
wavefront = jailbreakUnbreak haskellPackages.wavefront;
};
defaultPackage = self.packages.${system}.${packageName}; devShells.default = haskellPackages.shellFor {
packages = p: [ p.vulkan-tutorial ];
devShell = haskellPackages.shellFor {
packages = p: [ defaultPackage ];
withHoogle = true; withHoogle = true;
buildInputs = (with haskellPackages; [ buildInputs = (with haskellPackages; [
haskell-language-server haskell-language-server
ghcid ghcid
cabal-install cabal-install
(jailbreakUnbreak wavefront)
]) ++ (with pkgs; [ ]) ++ (with pkgs; [
shaderc shaderc
vulkan-loader
vulkan-validation-layers
renderdoc
]); ]);
VK_LAYER_PATH="${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d";
SDL_VULKAN_LIBRARY="${pkgs.vulkan-loader}/lib/libvulkan.so";
}; };
#devShell = pkgs.mkShell {
# buildInputs = with haskellPackages; [
# haskell-language-server
# ghcid
# cabal-install
# ];
# nativeBuildInputs = with pkgs; [
# pkg-config
# SDL2
# vulkan-validation-layers
# vulkan-headers
# vulkan-loader
# ];
# inputsFrom = builtins.attrValues self.packages.${system};
#};
}); });
} }

View file

@ -1,11 +1,16 @@
#version 450 #version 450
layout (location = 0) in vec3 vPosition; layout (location = 0) in vec3 vPosition;
layout (location = 1) in vec3 vNormal; layout (location = 1) in vec3 vNormal;
layout (location = 2) in vec4 vColor; layout (location = 2) in vec4 vColor;
layout (location = 0) out vec4 outColor; layout (location = 0) out vec4 outColor;
layout(set = 0, binding = 0) uniform CameraBuffer{
mat4 view;
mat4 proj;
mat4 viewproj;
} cameraData;
//push constants block //push constants block
layout( push_constant ) uniform constants layout( push_constant ) uniform constants
{ {
@ -15,6 +20,7 @@ layout( push_constant ) uniform constants
void main() void main()
{ {
gl_Position = PushConstants.render_matrix * vec4(vPosition, 1.0f); mat4 transformMatrix = (cameraData.viewproj * PushConstants.render_matrix);
gl_Position = transformMatrix * vec4(vPosition, 1.0f);
outColor = vColor; outColor = vColor;
} }

View file

@ -0,0 +1,21 @@
#version 450
// shader input
layout(location = 0) in vec4 fragColor;
// output write
layout(location = 0) out vec4 outColor;
layout(set = 0, binding = 1) uniform SceneData{
vec4 fogColor; // w is for exponent
vec4 fogDistances; //x for min, y for max, zw unused.
vec4 ambientColor;
vec4 sunlightDirection; //w for sun power
vec4 sunlightColor;
} sceneData;
void main() {
outColor = vec4(fragColor.xyz + sceneData.ambientColor.xyz, 1.0f);
//outColor = fragColor;
//outColor = sceneData.ambientColor;
}

View file

@ -0,0 +1,24 @@
//glsl version 4.5
#version 450
//shader input
layout (location = 0) in vec4 inColor;
layout (location = 1) in vec2 texCoord;
//output write
layout (location = 0) out vec4 outFragColor;
layout(set = 0, binding = 1) uniform SceneData{
vec4 fogColor; // w is for exponent
vec4 fogDistances; //x for min, y for max, zw unused.
vec4 ambientColor;
vec4 sunlightDirection; //w for sun power
vec4 sunlightColor;
} sceneData;
layout(set = 2, binding = 0) uniform sampler2D tex1;
void main()
{
vec3 color = texture(tex1, texCoord).xyz;
outFragColor = vec4(color, inColor.w);
}

40
shadersrc/tri_mesh.vert Normal file
View file

@ -0,0 +1,40 @@
#version 460
layout (location = 0) in vec3 vPosition;
layout (location = 1) in vec3 vNormal;
layout (location = 2) in vec4 vColor;
layout (location = 3) in vec2 vTexCoord;
layout (location = 0) out vec4 outColor;
layout (location = 1) out vec2 texCoord;
layout(set = 0, binding = 0) uniform CameraBuffer{
mat4 view;
mat4 proj;
mat4 viewproj;
} cameraData;
struct ObjectData{
mat4 model;
};
//all object matrices
layout(std140,set = 1, binding = 0) readonly buffer ObjectBuffer{
ObjectData objects[];
} objectBuffer;
//push constants block
layout( push_constant ) uniform constants
{
vec4 data;
mat4 render_matrix;
} PushConstants;
void main()
{
mat4 modelMatrix = objectBuffer.objects[gl_BaseInstance].model;
mat4 transformMatrix = (cameraData.viewproj * modelMatrix);
gl_Position = transformMatrix * vec4(vPosition, 1.0f);
outColor = vColor;
texCoord = vTexCoord;
}

View file

@ -1,31 +1,52 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
module CommandBuffer where module CommandBuffer where
import qualified Control.Concurrent.STM as STM
import Control.Monad import Control.Monad
import Control.Monad.IO.Class import Control.Monad.IO.Class
import Control.Monad.Reader
import Control.Monad.Trans.Resource import Control.Monad.Trans.Resource
import qualified Data.Map.Strict as M
import qualified Data.Vector as V import qualified Data.Vector as V
import Foreign import Foreign hiding (void)
import Foreign.C.Types (CInt) import Foreign.C.Types (CInt)
import Linear import Linear
import qualified Vulkan as Vk import qualified Vulkan as Vk
import qualified Vulkan.Zero as Vk import qualified Vulkan.Zero as Vk
import qualified VulkanMemoryAllocator as VMA
-- internal imports -- internal imports
import Devices
import Types import Types
import Memory
import Util
import Mesh (compactDraws)
createCommandPool frameOverlap :: Int
:: (MonadResource m) frameOverlap = 2
=> Vk.PhysicalDevice
-> Vk.Device
-> m (Vk.CommandPool, Vk.Queue)
createCommandPool physicalDevice logicalDevice = do
queueFamilyIndex <- getQueueFamily physicalDevice Vk.QUEUE_GRAPHICS_BIT maxObjects :: Int
maxObjects = 10000
createFrames
:: (MonadResource m, MonadFail m)
=> Vk.Device
-> VMA.Allocator
-> Vk.DescriptorPool
-> Vk.DescriptorSetLayout
-> Vk.DescriptorSetLayout
-> Word32
-> m (V.Vector FrameData, Vk.Queue)
createFrames
logicalDevice
allocator
descriptorPool
setLayout
objectSetLayout
queueFamilyIndex
= do
let poolCreateInfo = Vk.zero let poolCreateInfo = Vk.zero
{ Vk.flags = Vk.COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT { Vk.flags = Vk.COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
@ -34,22 +55,11 @@ createCommandPool physicalDevice logicalDevice = do
graphicsQueue <- Vk.getDeviceQueue logicalDevice queueFamilyIndex 0 graphicsQueue <- Vk.getDeviceQueue logicalDevice queueFamilyIndex 0
commandPool <- snd <$> allocate frames <- V.mapM
(Vk.createCommandPool logicalDevice poolCreateInfo Nothing) (\_ -> do
(\commandPool -> do
putStrLn "destroying command pool"
Vk.destroyCommandPool logicalDevice commandPool Nothing
)
return (commandPool, graphicsQueue) commandPool <- snd <$>
Vk.withCommandPool logicalDevice poolCreateInfo Nothing allocate
createCommandBuffer
:: (MonadResource m)
=> Vk.Device
-> Vk.CommandPool
-> Int
-> m (V.Vector Vk.CommandBuffer)
createCommandBuffer logicalDevice commandPool number = do
let commandBufferAllocationInfo = Vk.zero let commandBufferAllocationInfo = Vk.zero
{ Vk.commandPool = commandPool { Vk.commandPool = commandPool
@ -57,29 +67,136 @@ createCommandBuffer logicalDevice commandPool number = do
, Vk.commandBufferCount = 1 , Vk.commandBufferCount = 1
} }
V.concat <$> (_, commandBuffer) <- Vk.withCommandBuffers logicalDevice commandBufferAllocationInfo allocate
replicateM number (Vk.allocateCommandBuffers logicalDevice commandBufferAllocationInfo)
let semaphoreCreateInfo = Vk.zero
fenceCreateInfo = Vk.zero
{ Vk.flags = Vk.FENCE_CREATE_SIGNALED_BIT
} :: Vk.FenceCreateInfo '[]
[presentSemaphore, renderSemaphore] <- replicateM 2
(snd <$>
Vk.withSemaphore logicalDevice semaphoreCreateInfo Nothing allocate
)
renderFence <- snd <$>
Vk.withFence logicalDevice fenceCreateInfo Nothing allocate
cameraBuffer <- createAllocatedBuffer
allocator
(sizeOf (GPUCameraData identity identity identity))
Vk.BUFFER_USAGE_UNIFORM_BUFFER_BIT
VMA.MEMORY_USAGE_CPU_TO_GPU
sceneBuffer <- createAllocatedBuffer
allocator
(sizeOf undefinedGPUSceneData * frameOverlap)
Vk.BUFFER_USAGE_UNIFORM_BUFFER_BIT
VMA.MEMORY_USAGE_CPU_TO_GPU
objectBuffer <- createAllocatedBuffer
allocator
(sizeOf undefinedGPUObjectData * maxObjects)
Vk.BUFFER_USAGE_STORAGE_BUFFER_BIT
VMA.MEMORY_USAGE_CPU_TO_GPU
indirectBuffer <- createAllocatedBuffer
allocator
(maxObjects * sizeOf (undefined :: Vk.DrawIndirectCommand))
(Vk.BUFFER_USAGE_TRANSFER_DST_BIT .|.
Vk.BUFFER_USAGE_STORAGE_BUFFER_BIT .|.
Vk.BUFFER_USAGE_INDIRECT_BUFFER_BIT
)
VMA.MEMORY_USAGE_CPU_TO_GPU
let allocationInfo = Vk.zero
{ Vk.descriptorPool = descriptorPool
, Vk.setLayouts = V.singleton setLayout
}
objectSetAllocInfo = Vk.zero
{ Vk.descriptorPool = descriptorPool
, Vk.setLayouts = V.singleton objectSetLayout
}
globalDescriptor <- Vk.allocateDescriptorSets logicalDevice allocationInfo
objectDescriptor <- Vk.allocateDescriptorSets logicalDevice objectSetAllocInfo
let cameraInfo = Vk.zero
{ Vk.buffer = allocatedBuffer cameraBuffer
, Vk.offset = 0
, Vk.range = fromIntegral $ sizeOf (undefined :: GPUCameraData)
} :: Vk.DescriptorBufferInfo
sceneInfo = Vk.zero
{ Vk.buffer = allocatedBuffer sceneBuffer
, Vk.offset = 0
-- , Vk.offset = padUniformBufferSize (fromIntegral $ sizeOf undefinedGPUSceneData) deviceProperties * fromIntegral index
, Vk.range = fromIntegral $ sizeOf undefinedGPUSceneData
} :: Vk.DescriptorBufferInfo
objectInfo = Vk.zero
{ Vk.buffer = allocatedBuffer objectBuffer
, Vk.offset = 0
, Vk.range = fromIntegral $ sizeOf undefinedGPUObjectData * maxObjects
} :: Vk.DescriptorBufferInfo
cameraWrite = writeDescriptorBuffer
Vk.DESCRIPTOR_TYPE_UNIFORM_BUFFER
(V.head globalDescriptor)
cameraInfo
0
sceneWrite = writeDescriptorBuffer
Vk.DESCRIPTOR_TYPE_UNIFORM_BUFFER
(V.head globalDescriptor)
sceneInfo
1
objectWrite = writeDescriptorBuffer
Vk.DESCRIPTOR_TYPE_STORAGE_BUFFER
(V.head objectDescriptor)
objectInfo
0
setWrites = V.fromList
[ cameraWrite
, sceneWrite
, objectWrite
]
Vk.updateDescriptorSets logicalDevice setWrites V.empty
return FrameData
{ framePresentSemaphore = presentSemaphore
, frameRenderSemaphore = renderSemaphore
, frameRenderFence = renderFence
, frameCommandPool = commandPool
, frameMainCommandBuffer = V.head commandBuffer
, frameCameraBuffer = cameraBuffer
, frameSceneBuffer = sceneBuffer
, frameObjectBuffer = objectBuffer
, frameIndirectBuffer = indirectBuffer
, frameGlobalDescriptor = V.head globalDescriptor
, frameObjectDescriptor = V.head objectDescriptor
}
)
(V.fromList [1 .. frameOverlap])
return (frames, graphicsQueue)
recordCommandBuffer recordCommandBuffer
:: (MonadResource m) :: (MonadResource m, MonadReader ReadState m, MonadIO m)
=> Vk.CommandBuffer => VMA.Allocator
-> Vk.CommandBuffer
-> Vk.RenderPass -> Vk.RenderPass
-> Vk.Framebuffer -> Vk.Framebuffer
-> V2 CInt -> V2 CInt
-> Vk.Pipeline -> Vk.Pipeline
-> Vk.PipelineLayout -> FrameData
-> Maybe Mesh
-> Int
-> m () -> m ()
recordCommandBuffer recordCommandBuffer
allocator
commandBuffer commandBuffer
renderPass renderPass
frameBuffer frameBuffer
(V2 width height) dimensions@(V2 width height)
graphicsPipeline graphicsPipeline
meshLayout frame
mesh
frameNumber
= do = do
let commandBufferBeginInfo = Vk.zero let commandBufferBeginInfo = Vk.zero
@ -87,7 +204,7 @@ recordCommandBuffer
, Vk.inheritanceInfo = Nothing , Vk.inheritanceInfo = Nothing
} }
Vk.beginCommandBuffer commandBuffer commandBufferBeginInfo Vk.useCommandBuffer commandBuffer commandBufferBeginInfo $ do
let renderPassInfo = Vk.zero let renderPassInfo = Vk.zero
{ Vk.renderPass = renderPass { Vk.renderPass = renderPass
@ -102,18 +219,119 @@ recordCommandBuffer
, Vk.height = fromIntegral height , Vk.height = fromIntegral height
} }
} }
, Vk.clearValues = V.fromList [ Vk.Color (Vk.Float32 0 0 0 1), Vk.DepthStencil (Vk.ClearDepthStencilValue 1 0)] , Vk.clearValues = V.fromList
[ Vk.Color (Vk.Float32 0 0 0 1)
, Vk.DepthStencil (Vk.ClearDepthStencilValue 1 0)
]
} }
Vk.cmdBeginRenderPass commandBuffer renderPassInfo Vk.SUBPASS_CONTENTS_INLINE Vk.cmdUseRenderPass commandBuffer renderPassInfo Vk.SUBPASS_CONTENTS_INLINE $ do
liftIO $
prepareRecording dimensions commandBuffer graphicsPipeline
renderObjects <- (liftIO . STM.atomically . STM.readTMVar) =<< asks renderables
meshMap <- (liftIO . STM.atomically . STM.readTMVar) =<< asks meshLibrary
materialMap <- (liftIO . STM.atomically . STM.readTMVar) =<< asks materialLibrary
let draws = compactDraws meshMap materialMap renderObjects
drawCommands = V.map
(\(i, RenderObject mesh _ _) ->
Vk.DrawIndirectCommand
{ vertexCount = fromIntegral (V.length $ meshVertices $ meshMap M.! mesh)
, firstVertex = 0
, instanceCount = 1
, firstInstance = i
}
)
(V.zip
(V.fromList [0..])
renderObjects
)
(memRelease, memPtr) <- VMA.withMappedMemory
allocator
(bufferAllocation $ frameIndirectBuffer frame)
allocate
V.mapM_
(\(idx, command) -> liftIO $
poke
(castPtr memPtr `plusPtr` (idx * sizeOf command))
command
)
(V.zip
(V.fromList [0 ..])
drawCommands
)
release memRelease
V.mapM_
(\(IndirectBatch mesh material first count) -> do
when
(materialTextureSet material /= Vk.NULL_HANDLE)
(Vk.cmdBindDescriptorSets
commandBuffer
Vk.PIPELINE_BIND_POINT_GRAPHICS
(materialPipelineLayout material)
2
(V.singleton $ materialTextureSet material)
V.empty
)
Vk.cmdBindPipeline commandBuffer Vk.PIPELINE_BIND_POINT_GRAPHICS (materialPipeline material)
Vk.cmdBindDescriptorSets
commandBuffer
Vk.PIPELINE_BIND_POINT_GRAPHICS
(materialPipelineLayout material)
0
(V.singleton $ frameGlobalDescriptor frame)
V.empty
Vk.cmdBindDescriptorSets
commandBuffer
Vk.PIPELINE_BIND_POINT_GRAPHICS
(materialPipelineLayout material)
1
(V.singleton $ frameObjectDescriptor frame)
V.empty
Vk.cmdBindVertexBuffers
commandBuffer
0
(V.fromList [ allocatedBuffer $ meshBuffer mesh ])
(V.fromList [ 0 ])
let indirectOffset = fromIntegral $
first * fromIntegral ( sizeOf (undefined :: Vk.DrawIndirectCommand))
drawStride = fromIntegral $ sizeOf (undefined :: Vk.DrawIndirectCommand)
Vk.cmdDrawIndirect
commandBuffer
(allocatedBuffer $ frameIndirectBuffer frame)
indirectOffset
count
drawStride
)
draws
prepareRecording
:: V2 CInt
-> Vk.CommandBuffer
-> Vk.Pipeline
-> IO ()
prepareRecording (V2 width height) commandBuffer graphicsPipeline = do
Vk.cmdBindPipeline commandBuffer Vk.PIPELINE_BIND_POINT_GRAPHICS graphicsPipeline Vk.cmdBindPipeline commandBuffer Vk.PIPELINE_BIND_POINT_GRAPHICS graphicsPipeline
-- Passing negative height and nonzero y origin to the viewport flips the models back in order
let viewport = Vk.zero let viewport = Vk.zero
{ Vk.x = 0 { Vk.x = 0
, Vk.y = 0 , Vk.y = fromIntegral height
, Vk.width = fromIntegral width , Vk.width = fromIntegral width
, Vk.height = fromIntegral height , Vk.height = - fromIntegral height
, Vk.minDepth = 0 , Vk.minDepth = 0
, Vk.maxDepth = 1 , Vk.maxDepth = 1
} }
@ -131,66 +349,45 @@ recordCommandBuffer
Vk.cmdSetViewport commandBuffer 0 (V.singleton viewport) Vk.cmdSetViewport commandBuffer 0 (V.singleton viewport)
Vk.cmdSetScissor commandBuffer 0 (V.singleton scissor) Vk.cmdSetScissor commandBuffer 0 (V.singleton scissor)
liftIO $ maybe pokeData
(Vk.cmdDraw commandBuffer 3 1 0 0) :: (MonadResource m, MonadReader ReadState m, MonadIO m)
(\jmesh -> do => FrameData
let camPosition = V3 0 0 (-5) -> Int
camCenter = V3 0 0 0 -> GPUSceneData
camUp = V3 0 1 0 -> GPUCameraData
view = lookAt camPosition camCenter camUp -> m ()
projection = perspective (pi/4) (800 / 600) 0.1 200 pokeData frame frameNumber sceneParameters cameraParameters = do
modelRot = axisAngle (V3 0 1 0) (fromIntegral frameNumber / (10 * pi)) renderObjects <- (liftIO . STM.atomically . STM.readTMVar) =<< asks renderables
model = mkTransformation modelRot (V3 0 0 0)
pvm = projection !*! view !*! model
constants = MeshPushConstants (V4 0 0 0 0) (transpose pvm)
pointer <- castPtr <$> new constants let scenePointer = VMA.mappedData (bufferInfo $ frameSceneBuffer frame)
Vk.cmdPushConstants memoryPointer = VMA.mappedData (bufferInfo $ frameCameraBuffer frame)
commandBuffer objectPointer = VMA.mappedData (bufferInfo $ frameObjectBuffer frame)
meshLayout framed = fromIntegral frameNumber / 120
Vk.SHADER_STAGE_VERTEX_BIT ambient = normalize $ V4 (sin framed) 0 (cos framed) 1
0 -- ambient = V4 1 0 1 1
(fromIntegral $ sizeOf constants) ambientParams = sceneParameters
pointer { ambientColor = ambient
}
Vk.cmdBindVertexBuffers V.mapM_
commandBuffer (\(index, obj) -> do
0
(V.fromList [ allocatedBuffer $ meshBuffer jmesh ])
(V.fromList [ 0 ])
Vk.cmdDraw commandBuffer (fromIntegral $ V.length $ meshVertices jmesh) 1 0 0 let modelMatrix = GPUObjectData (objectMatrix obj)
liftIO $ poke
(castPtr objectPointer `plusPtr` (index * sizeOf (undefined :: GPUObjectData)))
modelMatrix
) )
mesh (V.zip
(V.fromList [0 ..])
Vk.cmdEndRenderPass commandBuffer renderObjects
Vk.endCommandBuffer commandBuffer
createSyncObjects
:: (MonadResource m, MonadFail m)
=> Vk.Device
-> m (Vk.Semaphore, Vk.Semaphore, Vk.Fence)
createSyncObjects logicalDevice = do
let semaphoreCreateInfo = Vk.zero
fenceCreateInfo = Vk.zero
{ Vk.flags = Vk.FENCE_CREATE_SIGNALED_BIT
} :: Vk.FenceCreateInfo '[]
[imageAvailableSemaphore, renderFinishedSemaphore] <- replicateM 2
(snd <$> allocate
(Vk.createSemaphore logicalDevice semaphoreCreateInfo Nothing)
(\semaphore -> do
putStrLn "destroying semaphore"
Vk.destroySemaphore logicalDevice semaphore Nothing
)
)
inFlightFence <- snd <$> allocate
(Vk.createFence logicalDevice fenceCreateInfo Nothing)
(\fence -> do
putStrLn "destroying fence"
Vk.destroyFence logicalDevice fence Nothing
) )
return (imageAvailableSemaphore, renderFinishedSemaphore, inFlightFence) liftIO $ do
poke
(castPtr scenePointer)
ambientParams
poke
(castPtr memoryPointer)
cameraParameters

View file

@ -1,6 +1,7 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedRecordDot #-}
module Devices where module Devices where
import Control.Monad import Control.Monad
@ -9,7 +10,7 @@ import Control.Monad.Trans.Resource
import qualified Data.Vector as V import qualified Data.Vector as V
import Data.Maybe (fromMaybe, isNothing) import Data.Maybe (fromMaybe, isNothing)
import Data.Bits import Data.Bits
import Data.Word (Word32) import Foreign
import Foreign.C.Types (CInt) import Foreign.C.Types (CInt)
import Linear import Linear
import qualified Vulkan as Vk import qualified Vulkan as Vk
@ -39,7 +40,7 @@ pickPhysicalDevice vkInstance = do
let discretePhysDevices = V.filter let discretePhysDevices = V.filter
(\(_, _, devFeatures) -> (\(_, _, devFeatures) ->
-- Vk.deviceType devProp == Vk.PHYSICAL_DEVICE_TYPE_DISCRETE_GPU && -- Vk.deviceType devProps == Vk.PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
Vk.geometryShader devFeatures Vk.geometryShader devFeatures
) )
devicesWithPropsAndFeatures devicesWithPropsAndFeatures
@ -90,7 +91,7 @@ createLogicalDevice
:: (MonadResource m) :: (MonadResource m)
=> Vk.PhysicalDevice => Vk.PhysicalDevice
-> Vk.SurfaceKHR -> Vk.SurfaceKHR
-> m (Vk.Device, V.Vector Vk.SurfaceFormatKHR) -> m (Vk.Device, V.Vector Vk.SurfaceFormatKHR, Word32)
createLogicalDevice physDevice surface = do createLogicalDevice physDevice surface = do
graphicsQueueFamily <- getQueueFamily physDevice Vk.QUEUE_GRAPHICS_BIT graphicsQueueFamily <- getQueueFamily physDevice Vk.QUEUE_GRAPHICS_BIT
@ -104,20 +105,26 @@ createLogicalDevice physDevice surface = do
deviceCreateInfo = Vk.zero deviceCreateInfo = Vk.zero
{ Vk.queueCreateInfos = V.singleton (Vk.SomeStruct queueCreateInfo) { Vk.queueCreateInfos = V.singleton (Vk.SomeStruct queueCreateInfo)
, Vk.enabledExtensionNames = extensionNames , Vk.enabledExtensionNames = extensionNames
, Vk.enabledFeatures = Just (Vk.zero
{ Vk.multiDrawIndirect = True
} :: Vk.PhysicalDeviceFeatures)
} }
shaderDrawParametersFeatures = Vk.zero
{ Vk.shaderDrawParameters = True
} :: Vk.PhysicalDeviceShaderDrawParametersFeatures
(_, logDevice) <- allocate (_, logDevice) <-
(Vk.createDevice physDevice deviceCreateInfo Nothing) Vk.withDevice
(\logDevice -> do physDevice
putStrLn "destroying logical device" (deviceCreateInfo Vk.::& shaderDrawParametersFeatures Vk.:& ())
Vk.destroyDevice logDevice Nothing Nothing
) allocate
(result, formats) <- Vk.getPhysicalDeviceSurfaceFormatsKHR physDevice surface (result, formats) <- Vk.getPhysicalDeviceSurfaceFormatsKHR physDevice surface
unless (result == Vk.SUCCESS) $ unless (result == Vk.SUCCESS) $
error "createLogicalDevice: Failed retrieving surface image formats" error "createLogicalDevice: Failed retrieving surface image formats"
return (logDevice, formats) return (logDevice, formats, graphicsQueueFamily)
createSwapchain createSwapchain
:: (MonadResource m) :: (MonadResource m)
@ -137,9 +144,9 @@ createSwapchain surface surfaceFormats windowDimension logicalDevice allocator =
if if
V.any V.any
(\a -> (\a ->
(Vk.format :: Vk.SurfaceFormatKHR -> Vk.Format) a == a.format ==
Vk.FORMAT_B8G8R8A8_SRGB Vk.FORMAT_B8G8R8A8_SRGB
&& (Vk.colorSpace :: Vk.SurfaceFormatKHR -> Vk.ColorSpaceKHR) a == && a.colorSpace ==
Vk.COLOR_SPACE_SRGB_NONLINEAR_KHR) Vk.COLOR_SPACE_SRGB_NONLINEAR_KHR)
surfaceFormats surfaceFormats
then then
@ -166,12 +173,8 @@ createSwapchain surface surfaceFormats windowDimension logicalDevice allocator =
, Vk.clipped = True , Vk.clipped = True
} }
swapchain <- snd <$> allocate swapchain <- snd <$>
(Vk.createSwapchainKHR logicalDevice createInfo Nothing) Vk.withSwapchainKHR logicalDevice createInfo Nothing allocate
(\swapchain -> do
putStrLn "destroying swapchain"
Vk.destroySwapchainKHR logicalDevice swapchain Nothing
)
let depthImageExtent = let depthImageExtent =
(\(V2 w h) -> Vk.Extent3D (fromIntegral w) (fromIntegral h) 1) (\(V2 w h) -> Vk.Extent3D (fromIntegral w) (fromIntegral h) 1)
@ -187,12 +190,8 @@ createSwapchain surface surfaceFormats windowDimension logicalDevice allocator =
, VMA.requiredFlags = Vk.MEMORY_PROPERTY_DEVICE_LOCAL_BIT , VMA.requiredFlags = Vk.MEMORY_PROPERTY_DEVICE_LOCAL_BIT
} }
(depthImage, depthAllocation, _) <- snd <$> allocate (depthImage, depthAllocation, _) <- snd <$>
(VMA.createImage allocator depthImageInfo depthImageAllocationInfo) VMA.withImage allocator depthImageInfo depthImageAllocationInfo allocate
(\(depthImage, depthAllocation, _) -> do
putStrLn "destroying image"
VMA.destroyImage allocator depthImage depthAllocation
)
let allocatedImage = AllocatedImage depthImage depthAllocation let allocatedImage = AllocatedImage depthImage depthAllocation
depthImageView = imageviewCreate depthImageView = imageviewCreate
@ -200,12 +199,8 @@ createSwapchain surface surfaceFormats windowDimension logicalDevice allocator =
Vk.FORMAT_D32_SFLOAT Vk.FORMAT_D32_SFLOAT
Vk.IMAGE_ASPECT_DEPTH_BIT Vk.IMAGE_ASPECT_DEPTH_BIT
depthImageview <- snd <$> allocate depthImageview <- snd <$>
(Vk.createImageView logicalDevice depthImageView Nothing) Vk.withImageView logicalDevice depthImageView Nothing allocate
(\depthImageview -> do
putStrLn "destroying image view"
Vk.destroyImageView logicalDevice depthImageview Nothing
)
return (swapchain, Vk.SurfaceFormatKHR surfaceFormat colorSpace, depthImageview, allocatedImage) return (swapchain, Vk.SurfaceFormatKHR surfaceFormat colorSpace, depthImageview, allocatedImage)
@ -228,7 +223,7 @@ getImageViewHandles swapchain surfaceFormat logicalDevice = do
let createInfo = Vk.zero let createInfo = Vk.zero
{ Vk.image = tmpImage { Vk.image = tmpImage
, Vk.viewType = Vk.IMAGE_VIEW_TYPE_2D , Vk.viewType = Vk.IMAGE_VIEW_TYPE_2D
, Vk.format = (Vk.format :: Vk.SurfaceFormatKHR -> Vk.Format) surfaceFormat , Vk.format = surfaceFormat.format
, Vk.components = Vk.ComponentMapping , Vk.components = Vk.ComponentMapping
{ Vk.r = Vk.COMPONENT_SWIZZLE_IDENTITY { Vk.r = Vk.COMPONENT_SWIZZLE_IDENTITY
, Vk.g = Vk.COMPONENT_SWIZZLE_IDENTITY , Vk.g = Vk.COMPONENT_SWIZZLE_IDENTITY
@ -243,11 +238,7 @@ getImageViewHandles swapchain surfaceFormat logicalDevice = do
, layerCount = 1 , layerCount = 1
} }
} }
snd <$> allocate snd <$>
(Vk.createImageView logicalDevice createInfo Nothing) Vk.withImageView logicalDevice createInfo Nothing allocate
(\imageView -> do
putStrLn "destroying image view"
Vk.destroyImageView logicalDevice imageView Nothing
)
) )
handles handles

View file

@ -1,11 +1,17 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedRecordDot #-}
module Draw where module Draw where
import qualified Control.Concurrent.STM as STM import qualified Control.Concurrent.STM as STM
import Control.Monad import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Reader
import Control.Monad.Trans.Resource import Control.Monad.Trans.Resource
import qualified Data.Map.Strict as M
import qualified Data.Vector as V import qualified Data.Vector as V
import Linear
import qualified Vulkan.Core10 as Vk import qualified Vulkan.Core10 as Vk
import qualified Vulkan.Extensions.VK_KHR_swapchain as Khr import qualified Vulkan.Extensions.VK_KHR_swapchain as Khr
import qualified Vulkan.CStruct.Extends as Vk import qualified Vulkan.CStruct.Extends as Vk
@ -15,87 +21,96 @@ import qualified Vulkan.Zero as Vk
import CommandBuffer import CommandBuffer
import Types import Types
import Control.Monad.IO.Class (MonadIO(liftIO)) import Util (getFrame)
drawFrame drawFrame
:: (MonadResource m, MonadFail m) :: (MonadResource m, MonadFail m, MonadReader ReadState m)
=> EngineData => EngineData
-> STM.TMVar Pipelines
-> Int -> Int
-> m () -> m ()
drawFrame engineData switch frameNumber = do drawFrame engineData frameNumber = do
unless (all (V.length (engineCommandBuffers engineData) ==) let frame = getFrame engineData frameNumber
[ V.length (engineCommandBuffers engineData)
, V.length (engineFramebuffers engineData)
]) $
error ("Numbers of elements mismatch: command buffers " <>
show (V.length $ engineCommandBuffers engineData) <>
", frame buffers " <> show (V.length $ engineFramebuffers engineData)
)
let fences = V.singleton $ engineInFlightFence engineData void $ Vk.waitForFences
(engineLogicalDevice engineData)
void $ Vk.waitForFences (engineLogicalDevice engineData) fences True maxBound (V.singleton $ frameRenderFence frame)
Vk.resetFences (engineLogicalDevice engineData) fences True
maxBound
Vk.resetFences (engineLogicalDevice engineData) (V.singleton $ frameRenderFence frame)
(imageResult, index) <- (imageResult, index) <-
Khr.acquireNextImageKHR Khr.acquireNextImageKHR
(engineLogicalDevice engineData) (engineLogicalDevice engineData)
(engineSwapchain engineData) (engineSwapchain engineData)
maxBound maxBound
(engineImageAvailableSemaphore engineData) (frameRenderSemaphore frame)
Vk.NULL_HANDLE Vk.NULL_HANDLE
unless (imageResult == Vk.SUCCESS) $ unless (imageResult == Vk.SUCCESS) $
error "drawFrame: Failed acquiring next image from swapchain" error "drawFrame: Failed acquiring next image from swapchain"
-- liftIO $ putStrLn "resetting command buffer"
Vk.resetCommandBuffer Vk.resetCommandBuffer
(engineCommandBuffers engineData V.! fromIntegral index) (Vk.CommandBufferResetFlagBits 0) (frameMainCommandBuffer frame) (Vk.CommandBufferResetFlagBits 0)
switchState <- liftIO $ STM.atomically $ STM.readTMVar switch matLibrary <- (liftIO . STM.atomically. STM.readTMVar) =<< asks materialLibrary
let (V2 width height) = engineWindowDimensions engineData
camPosition = V3
(-10 * sin (fromIntegral frameNumber / 90))
2
(-10 * cos (fromIntegral frameNumber / 90))
camCenter = V3 0 0 0
camUp = V3 0 1 0
camView = lookAt camPosition camCenter camUp
camProjection = perspective
(pi/4)
(fromIntegral width / fromIntegral height)
0.1
200
cameraData = GPUCameraData
{ view = camView
, projection = camProjection
, viewProjection = camProjection !*! camView
}
pokeData
frame
frameNumber
(engineSceneParameters engineData)
cameraData
-- liftIO $ putStrLn "recording command buffer"
recordCommandBuffer recordCommandBuffer
(engineCommandBuffers engineData V.! fromIntegral index) (engineData.engineAllocator)
(frameMainCommandBuffer frame)
(engineRenderPass engineData) (engineRenderPass engineData)
(engineFramebuffers engineData V.! fromIntegral index) (engineFramebuffers engineData V.! fromIntegral index)
(engineWindowDimensions engineData) (engineWindowDimensions engineData)
(meshPipeline engineData V.! fromIntegral index) (materialPipeline $ matLibrary M.! "defaultMesh")
(meshPipelineLayout engineData) frame
(case switchState of
Green ->
Just $ engineMesh engineData
_ ->
Nothing
)
frameNumber
let submitInfo = Vk.zero let submitInfo = Vk.zero
{ Vk.waitSemaphores = { Vk.waitSemaphores =
V.singleton $ engineImageAvailableSemaphore engineData V.singleton $ frameRenderSemaphore frame
, Vk.waitDstStageMask = , Vk.waitDstStageMask =
V.singleton Vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT V.singleton Vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
, Vk.commandBuffers = , Vk.commandBuffers =
V.singleton (Vk.commandBufferHandle $ engineCommandBuffers engineData V.! fromIntegral index) V.singleton (Vk.commandBufferHandle $ frameMainCommandBuffer frame)
, Vk.signalSemaphores = , Vk.signalSemaphores =
V.singleton $ engineRenderFinishedSemaphore engineData V.singleton $ framePresentSemaphore frame
} }
-- liftIO $ putStrLn "submitting to queue"
Vk.queueSubmit Vk.queueSubmit
(engineQueue engineData) (engineQueue engineData)
(V.singleton (Vk.SomeStruct submitInfo)) (V.singleton (Vk.SomeStruct submitInfo))
(engineInFlightFence engineData) (frameRenderFence frame)
let presentInfo = Vk.zero let presentInfo = Vk.zero
{ Khr.waitSemaphores = V.singleton $ engineRenderFinishedSemaphore engineData { Khr.waitSemaphores = V.singleton $ framePresentSemaphore frame
, Khr.swapchains = V.singleton $ engineSwapchain engineData , Khr.swapchains = V.singleton $ engineSwapchain engineData
, Khr.imageIndices = V.singleton index , Khr.imageIndices = V.singleton index
} }
-- liftIO $ putStrLn "presenting queue"
presentResult <- Khr.queuePresentKHR (engineQueue engineData) presentInfo presentResult <- Khr.queuePresentKHR (engineQueue engineData) presentInfo
unless (presentResult == Vk.SUCCESS) $ unless (presentResult == Vk.SUCCESS) $

View file

@ -39,11 +39,6 @@ createFramebuffer
let createInfo = framebufferCreateInfo let createInfo = framebufferCreateInfo
{ Vk.attachments = V.fromList [ imageView, depthImageView ] { Vk.attachments = V.fromList [ imageView, depthImageView ]
} :: Vk.FramebufferCreateInfo '[] } :: Vk.FramebufferCreateInfo '[]
snd <$> allocate snd <$> Vk.withFramebuffer logicalDevice createInfo Nothing allocate
(Vk.createFramebuffer logicalDevice createInfo Nothing)
(\framebuffer -> do
putStrLn "destroying framebuffer"
Vk.destroyFramebuffer logicalDevice framebuffer Nothing
)
) )
swapchainImageViews swapchainImageViews

View file

@ -1,15 +1,17 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
module GraphicsPipeline where module GraphicsPipeline where
import Linear import Linear
import Control.Monad (unless) import qualified Control.Concurrent.STM as STM
import Control.Monad.IO.Class (liftIO) import Control.Monad.Reader
import Control.Monad.Trans.Resource import Control.Monad.Trans.Resource
import Data.Bits import Data.Bits
-- import qualified Data.ByteString as BS -- import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Char8 as BS
import qualified Data.Map.Strict as M
import qualified Data.Vector as V import qualified Data.Vector as V
import Foreign (sizeOf) import Foreign (sizeOf)
import Foreign.C.Types (CInt) import Foreign.C.Types (CInt)
@ -21,7 +23,6 @@ import qualified Vulkan.Utils.ShaderQQ.GLSL.Shaderc as Vk
-- internal imports -- internal imports
import Types import Types
import Mesh
loadShader loadShader
:: (MonadResource m) :: (MonadResource m)
@ -48,12 +49,8 @@ loadShader logicalDevice shaderPath stage = do
{ Vk.code = shader { Vk.code = shader
} }
snd <$> allocate snd <$>
(Vk.createShaderModule logicalDevice createInfo Nothing) Vk.withShaderModule logicalDevice createInfo Nothing allocate
(\shaderModule -> do
putStrLn "destroying shader module"
Vk.destroyShaderModule logicalDevice shaderModule Nothing
)
createRenderPass createRenderPass
:: (MonadResource m) :: (MonadResource m)
@ -106,7 +103,7 @@ createRenderPass
, Vk.dstSubpass = 0 , Vk.dstSubpass = 0
, Vk.srcStageMask = Vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT , Vk.srcStageMask = Vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
, Vk.dstStageMask = Vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT , Vk.dstStageMask = Vk.PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
, Vk.srcAccessMask = bit 0 , Vk.srcAccessMask = Vk.ACCESS_MEMORY_READ_BIT
, Vk.dstAccessMask = Vk.ACCESS_COLOR_ATTACHMENT_WRITE_BIT , Vk.dstAccessMask = Vk.ACCESS_COLOR_ATTACHMENT_WRITE_BIT
} }
subpassDepthDependency = Vk.zero subpassDepthDependency = Vk.zero
@ -114,7 +111,7 @@ createRenderPass
, Vk.dstSubpass = 0 , Vk.dstSubpass = 0
, Vk.srcStageMask = , Vk.srcStageMask =
Vk.PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT .|. Vk.PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT Vk.PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT .|. Vk.PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT
, Vk.srcAccessMask = bit 0 , Vk.srcAccessMask = Vk.ACCESS_MEMORY_READ_BIT
, Vk.dstStageMask = , Vk.dstStageMask =
Vk.PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT .|. Vk.PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT Vk.PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT .|. Vk.PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT
, Vk.dstAccessMask = Vk.ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT , Vk.dstAccessMask = Vk.ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT
@ -125,12 +122,8 @@ createRenderPass
, Vk.dependencies = V.fromList [ subpassDependency, subpassDepthDependency ] , Vk.dependencies = V.fromList [ subpassDependency, subpassDepthDependency ]
} :: Vk.RenderPassCreateInfo '[] } :: Vk.RenderPassCreateInfo '[]
snd <$> allocate snd <$>
(Vk.createRenderPass logicalDevice renderPassCreateInfo Nothing) Vk.withRenderPass logicalDevice renderPassCreateInfo Nothing allocate
(\renderPass -> do
putStrLn "destroying render pass"
Vk.destroyRenderPass logicalDevice renderPass Nothing
)
createShaderStageCreateInfo createShaderStageCreateInfo
:: Vk.ShaderStageFlagBits :: Vk.ShaderStageFlagBits
@ -194,24 +187,25 @@ createMultisampleStateCreateInfo =
} }
createGraphicsPipelines createGraphicsPipelines
:: (MonadResource m) :: (MonadResource m, MonadReader ReadState m)
=> Vk.Device => Vk.Device
-> Vk.RenderPass -> Vk.RenderPass
-> ShaderContainer -> ShaderContainer
-> V2 CInt -> V2 CInt
-> Int
-> Vk.PipelineLayout -> Vk.PipelineLayout
-> Maybe Vk.PipelineDepthStencilStateCreateInfo -> Maybe Vk.PipelineDepthStencilStateCreateInfo
-> m (V.Vector Vk.Pipeline) -> String
-> m ()
createGraphicsPipelines createGraphicsPipelines
logicalDevice logicalDevice
renderPass renderPass
shaderContainer shaderContainer
(V2 width height) (V2 width height)
number
pipelineLayout pipelineLayout
depthState depthState
materialName
= do = do
meshLib <- (liftIO . STM.atomically . STM.readTMVar) =<< asks meshLibrary
let pipelineStagesCreateInfos = let pipelineStagesCreateInfos =
V.fromList $ map Vk.SomeStruct V.fromList $ map Vk.SomeStruct
( maybe ( maybe
@ -242,7 +236,7 @@ createGraphicsPipelines
pipelineDynamicStateCreateInfo = Vk.zero pipelineDynamicStateCreateInfo = Vk.zero
{ Vk.dynamicStates = dynamicStates { Vk.dynamicStates = dynamicStates
} }
vertexDescription = getVertexDescription (V.head loadMeshes) vertexDescription = getVertexDescription (V.head $ meshVertices $ meshLib M.! "mask")
pipelineVertexInputCreateInfo = Vk.zero pipelineVertexInputCreateInfo = Vk.zero
{ Vk.vertexBindingDescriptions = vidBindings vertexDescription { Vk.vertexBindingDescriptions = vidBindings vertexDescription
, Vk.vertexAttributeDescriptions = vidAttributes vertexDescription , Vk.vertexAttributeDescriptions = vidAttributes vertexDescription
@ -300,57 +294,39 @@ createGraphicsPipelines
, Vk.basePipelineIndex = -1 , Vk.basePipelineIndex = -1
} }
snd <$> allocate pipeline <-
(do (do
(result, pipelines) <- Vk.createGraphicsPipelines (result, pipelines) <- snd <$> Vk.withGraphicsPipelines
logicalDevice logicalDevice
Vk.NULL_HANDLE Vk.NULL_HANDLE
(V.replicate number (Vk.SomeStruct pipelineCreateInfo)) (V.singleton (Vk.SomeStruct pipelineCreateInfo))
Nothing Nothing
allocate
unless (result == Vk.SUCCESS) $ unless (result == Vk.SUCCESS) $
error "createGraphicsPipelines: Failed creating pipelines" error "createGraphicsPipelines: Failed creating pipelines"
return pipelines return pipelines
) )
(\pipelines -> do let material = Material Vk.NULL_HANDLE (V.head pipeline) pipelineLayout
V.mapM_ matLibraryTMVar <- asks materialLibrary
(\pipeline -> do matLibrary <- liftIO $ STM.atomically $ STM.readTMVar matLibraryTMVar
putStrLn "destroying pipeline" let newMatLibrary = M.insert materialName material matLibrary
Vk.destroyPipeline logicalDevice pipeline Nothing void $ liftIO $ STM.atomically $ STM.swapTMVar matLibraryTMVar newMatLibrary
)
pipelines
)
createPipelineLayout
:: MonadResource m
=> Vk.Device
-> m Vk.PipelineLayout
createPipelineLayout logicalDevice = do
let pipelineLayoutCreateInfo = Vk.zero
snd <$> allocate
(Vk.createPipelineLayout logicalDevice pipelineLayoutCreateInfo Nothing)
(\pipelineLayout -> do
putStrLn "destroying pipeline layout"
Vk.destroyPipelineLayout logicalDevice pipelineLayout Nothing
)
createMeshPipelineLayout createMeshPipelineLayout
:: MonadResource m :: MonadResource m
=> Vk.Device => Vk.Device
-> V.Vector Vk.DescriptorSetLayout
-> m Vk.PipelineLayout -> m Vk.PipelineLayout
createMeshPipelineLayout logicalDevice = do createMeshPipelineLayout logicalDevice layouts = do
let pushConstantRange = Vk.zero let pushConstantRange = Vk.zero
{ Vk.offset = 0 { Vk.offset = 0
, Vk.size = fromIntegral (sizeOf (undefined :: MeshPushConstants)) , Vk.size = fromIntegral (sizeOf undefinedMeshPushConstants)
, Vk.stageFlags = Vk.SHADER_STAGE_VERTEX_BIT , Vk.stageFlags = Vk.SHADER_STAGE_VERTEX_BIT
} }
pipelineLayoutCreateInfo = Vk.zero pipelineLayoutCreateInfo = Vk.zero
{ Vk.pushConstantRanges = V.singleton pushConstantRange { Vk.pushConstantRanges = V.singleton pushConstantRange
, Vk.setLayouts = layouts
} }
snd <$> allocate snd <$>
(Vk.createPipelineLayout logicalDevice pipelineLayoutCreateInfo Nothing) Vk.withPipelineLayout logicalDevice pipelineLayoutCreateInfo Nothing allocate
(\pipelineLayout -> do
putStrLn "destroying pipeline layout"
Vk.destroyPipelineLayout logicalDevice pipelineLayout Nothing
)

View file

@ -1,17 +1,28 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
module Init where module Init where
import qualified Control.Concurrent.STM as STM
import Control.Exception
import Control.Monad import Control.Monad
import Control.Monad.IO.Class import Control.Monad.IO.Class
import Control.Monad.Reader
import Control.Monad.Trans.Resource import Control.Monad.Trans.Resource
import Data.Bits
import qualified Data.Map.Strict as M
import Data.Maybe
import qualified Data.Vector as V
import qualified SDL hiding (V2) import qualified SDL hiding (V2)
import qualified SDL.Video.Vulkan as SDL import qualified SDL.Video.Vulkan as SDL
import Foreign.Ptr import Foreign.Ptr
import qualified Vulkan.Core10 as Vk import Linear as L
import qualified Vulkan as Vk
import qualified Vulkan.CStruct.Extends as Vk
import qualified Vulkan.Extensions.VK_KHR_swapchain as Khr import qualified Vulkan.Extensions.VK_KHR_swapchain as Khr
import qualified Vulkan.Extensions.VK_KHR_surface as Khr import qualified Vulkan.Extensions.VK_KHR_surface as Khr
import qualified Vulkan.Zero as Vk
-- internal imports -- internal imports
@ -19,14 +30,16 @@ import Instance
import Devices import Devices
import GraphicsPipeline import GraphicsPipeline
import Framebuffers import Framebuffers
import CommandBuffer
import Memory import Memory
import Types import Types
import Mesh import Mesh
import CommandBuffer
import Sync
import Textures
import Util
initEngine initEngine
:: (MonadResource m, MonadFail m) :: Render EngineData
=> m EngineData
initEngine = do initEngine = do
-- initialize SDL2 with all subsystems -- initialize SDL2 with all subsystems
void $ allocate_ void $ allocate_
@ -39,6 +52,7 @@ initEngine = do
-- create a window configuration -- create a window configuration
let windowConfig = SDL.defaultWindow let windowConfig = SDL.defaultWindow
{ SDL.windowGraphicsContext = SDL.VulkanContext -- enable Vulkan support { SDL.windowGraphicsContext = SDL.VulkanContext -- enable Vulkan support
, SDL.windowInitialSize = V2 1280 1024
} }
-- load vulkan library -- load vulkan library
@ -47,7 +61,12 @@ initEngine = do
-- create the window -- create the window
(_, window) <- (_, window) <-
allocate allocate
(SDL.createWindow "Haskell ❤️ Vulkan" windowConfig) (do
eexcept <- try (SDL.createWindow "Haskell ❤️ Vulkan" windowConfig)
case eexcept of
Left err -> error $ "here's your exception: " <> show (err :: SDL.SDLException)
Right a -> return a
)
(\window -> do (\window -> do
liftIO $ putStrLn "destroying window" liftIO $ putStrLn "destroying window"
SDL.destroyWindow window SDL.destroyWindow window
@ -58,9 +77,8 @@ initEngine = do
initVulkan initVulkan
:: (MonadResource m, MonadFail m) :: SDL.Window
=> SDL.Window -> Render EngineData
-> m EngineData
initVulkan window = do initVulkan window = do
vulkanInstance <- createInstance window vulkanInstance <- createInstance window
@ -75,38 +93,75 @@ initVulkan window = do
) )
vulkanPhysicalDevice <- pickPhysicalDevice vulkanInstance vulkanPhysicalDevice <- pickPhysicalDevice vulkanInstance
(vulkanLogicalDevice, surfaceFormats) <- createLogicalDevice vulkanPhysicalDevice vulkanSurface (vulkanLogicalDevice, surfaceFormats, queueIndex) <-
createLogicalDevice vulkanPhysicalDevice vulkanSurface
uploadContext <- initSyncStructures vulkanLogicalDevice queueIndex
(descriptorSetLayout1, descriptorSetLayout2, textureDescriptorSetLayout, descriptorPool) <-
initDescriptors vulkanLogicalDevice
allocator <- initAllocator vulkanPhysicalDevice vulkanLogicalDevice vulkanInstance allocator <- initAllocator vulkanPhysicalDevice vulkanLogicalDevice vulkanInstance
maskMesh <- loadFromObj "./assets/cat_mask_cyberpunk.obj" allocator
dimensions <- liftIO $ SDL.windowInitialSize <$> SDL.getWindowConfig window dimensions <- liftIO $ SDL.windowInitialSize <$> SDL.getWindowConfig window
(swapchain, surfaceFormat, depthImageView, depthAllocatedImage) <- (swapchain, surfaceFormat, depthImageView, depthAllocatedImage) <-
createSwapchain vulkanSurface surfaceFormats dimensions vulkanLogicalDevice allocator createSwapchain vulkanSurface surfaceFormats dimensions vulkanLogicalDevice allocator
imageViews <- getImageViewHandles swapchain surfaceFormat vulkanLogicalDevice imageViews <- getImageViewHandles swapchain surfaceFormat vulkanLogicalDevice
meshVertexShader <- loadShader vulkanLogicalDevice "shadersrc/mesh.vert" "vert" meshVertexShader <- loadShader vulkanLogicalDevice "shadersrc/tri_mesh.vert" "vert"
meshFragmentShader <- loadShader vulkanLogicalDevice "shadersrc/rainbow.frag" "frag" meshFragmentShader <- loadShader vulkanLogicalDevice "shadersrc/rainbow_lit.frag" "frag"
textureFragmentShader <- loadShader vulkanLogicalDevice "shadersrc/textured_lit.frag" "frag"
renderPass <- createRenderPass vulkanLogicalDevice (Khr.format surfaceFormat) Vk.FORMAT_D32_SFLOAT renderPass <- createRenderPass vulkanLogicalDevice (Khr.format surfaceFormat) Vk.FORMAT_D32_SFLOAT
meshLayout <- createMeshPipelineLayout vulkanLogicalDevice meshLayout <- createMeshPipelineLayout
vulkanLogicalDevice
(V.fromList
[ descriptorSetLayout1
, descriptorSetLayout2
, textureDescriptorSetLayout
]
)
textureLayout <- createMeshPipelineLayout
vulkanLogicalDevice
(V.fromList
[ descriptorSetLayout1
, descriptorSetLayout2
, textureDescriptorSetLayout
]
)
let meshContainer = ShaderContainer (Just meshVertexShader) (Just meshFragmentShader) let meshContainer = ShaderContainer (Just meshVertexShader) (Just meshFragmentShader)
meshPipelines <- textureContainer = ShaderContainer (Just meshVertexShader) (Just textureFragmentShader)
createGraphicsPipelines createGraphicsPipelines
vulkanLogicalDevice vulkanLogicalDevice
renderPass renderPass
meshContainer meshContainer
dimensions dimensions
(length imageViews)
meshLayout meshLayout
(Just (createDepthStencilStateCreateInfo True True Vk.COMPARE_OP_LESS_OR_EQUAL)) (Just (createDepthStencilStateCreateInfo True True Vk.COMPARE_OP_LESS_OR_EQUAL))
"defaultMesh"
createGraphicsPipelines
vulkanLogicalDevice
renderPass
textureContainer
dimensions
textureLayout
(Just (createDepthStencilStateCreateInfo True True Vk.COMPARE_OP_LESS_OR_EQUAL))
"texturedMesh"
frameBuffers <- createFramebuffer vulkanLogicalDevice renderPass imageViews depthImageView dimensions frameBuffers <- createFramebuffer vulkanLogicalDevice renderPass imageViews depthImageView dimensions
(commandPool, graphicsQueue) <- createCommandPool vulkanPhysicalDevice vulkanLogicalDevice (frames, queue) <-
commandBuffer <- createCommandBuffer vulkanLogicalDevice commandPool (length frameBuffers) createFrames
vulkanLogicalDevice
allocator
descriptorPool
descriptorSetLayout1
descriptorSetLayout2
queueIndex
(imageAvailableSemaphore, renderFinishedSemaphore, inFlightFence) <- loadMeshes allocator uploadContext queue vulkanLogicalDevice
createSyncObjects vulkanLogicalDevice
loadImages allocator uploadContext queue vulkanLogicalDevice
initScene vulkanLogicalDevice descriptorPool textureDescriptorSetLayout
return $ EngineData return $ EngineData
window window
@ -115,18 +170,176 @@ initVulkan window = do
vulkanLogicalDevice vulkanLogicalDevice
vulkanInstance vulkanInstance
swapchain swapchain
graphicsQueue queue
commandBuffer
frameBuffers frameBuffers
meshLayout
meshPipelines
renderPass renderPass
inFlightFence
imageAvailableSemaphore
renderFinishedSemaphore
maskMesh
maskMesh
allocator allocator
depthImageView depthImageView
depthAllocatedImage depthAllocatedImage
Vk.FORMAT_D32_SFLOAT Vk.FORMAT_D32_SFLOAT
frames
descriptorSetLayout1
descriptorSetLayout2
textureDescriptorSetLayout
descriptorPool
(GPUSceneData
(V4 0 0 0 0)
(V4 0 0 0 0)
(V4 0 0 0 0)
(V4 0 0 0 0)
(V4 0 0 0 0)
)
uploadContext
initScene
:: (MonadReader ReadState m, MonadIO m, MonadResource m)
=> Vk.Device
-> Vk.DescriptorPool
-> Vk.DescriptorSetLayout
-> m ()
initScene device descriptorPool texturedSetLayout = do
let catMask = RenderObject
{ objectMesh = "mask"
, objectMaterial = "texturedMesh"
, objectMatrix = identity
}
triangles = V.concatMap
(\y ->
V.map
(\x ->
let
translationMatrix = mkTransformation (Quaternion 1 (V3 0 0 0)) (V3 x 0 y)
scaleMatrix = scaled (V4 0.2 0.2 0.2 1)
in
RenderObject
{ objectMesh = "triangle"
, objectMaterial = "defaultMesh"
, objectMatrix = translationMatrix !*! scaleMatrix
}
)
(V.fromList [-20 .. 20])
)
(V.fromList [-20 .. 20])
renderableContainer <- asks renderables
renderableVector <- liftIO $ STM.atomically $ STM.readTMVar renderableContainer
void $ liftIO $ STM.atomically $ STM.swapTMVar renderableContainer $
(renderableVector `V.snoc` catMask) V.++ triangles
let samplerInfo = samplerCreateInfo Vk.FILTER_NEAREST Vk.SAMPLER_ADDRESS_MODE_REPEAT
blockySampler <- snd <$> Vk.withSampler device samplerInfo Nothing allocate
matLibraryContainer <- asks materialLibrary
texturedMaterial <- do
matLibrary <- liftIO $ STM.atomically $ STM.readTMVar matLibraryContainer
return (fromMaybe (error "not texturedMesh") $ matLibrary M.!? "texturedMesh")
let allocInfo = Vk.zero
{ Vk.descriptorPool = descriptorPool
, Vk.setLayouts = V.singleton texturedSetLayout
}
texturedDescriptorSet <- V.head . snd <$> Vk.withDescriptorSets device allocInfo allocate
liftIO $ do
matLibrary <- STM.atomically $ STM.readTMVar matLibraryContainer
let material = texturedMaterial
{ materialTextureSet = texturedDescriptorSet
}
newMatLibrary = M.insert "texturedMesh" material matLibrary
void $ STM.atomically $ STM.swapTMVar matLibraryContainer newMatLibrary
textureContainer <- asks textureLibrary
texture <- do
texLibrary <- liftIO $ STM.atomically $ STM.readTMVar textureContainer
return $ fromMaybe (error "no marksman texture found") $ texLibrary M.!? "marksman"
let imageBufferInfo = Vk.zero
{ Vk.sampler = blockySampler
, Vk.imageView = textureImageView texture
, Vk.imageLayout = Vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
}
texture1 = V.singleton $ Vk.SomeStruct $ writeDescriptorImage
Vk.DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
texturedDescriptorSet
imageBufferInfo
0
Vk.updateDescriptorSets device texture1 V.empty
initDescriptors
:: (MonadResource m)
=> Vk.Device
-> m (Vk.DescriptorSetLayout, Vk.DescriptorSetLayout, Vk.DescriptorSetLayout, Vk.DescriptorPool)
initDescriptors device = do
let cameraBind = descriptorsetLayoutBinding
Vk.DESCRIPTOR_TYPE_UNIFORM_BUFFER
Vk.SHADER_STAGE_VERTEX_BIT
0
sceneBind = descriptorsetLayoutBinding
Vk.DESCRIPTOR_TYPE_UNIFORM_BUFFER -- _DYNAMIC
Vk.SHADER_STAGE_FRAGMENT_BIT
1
objectBind = descriptorsetLayoutBinding
Vk.DESCRIPTOR_TYPE_STORAGE_BUFFER
Vk.SHADER_STAGE_VERTEX_BIT
0
textureBind = descriptorsetLayoutBinding
Vk.DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
Vk.SHADER_STAGE_FRAGMENT_BIT
0
setInfo = Vk.zero
{ Vk.flags = Vk.DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT
, Vk.bindings = V.fromList
[ cameraBind
, sceneBind
]
}
setInfo2 = Vk.zero
{ Vk.flags = Vk.DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT
, Vk.bindings = V.singleton objectBind
}
setInfo3 = Vk.zero
{ Vk.flags = Vk.DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT
, Vk.bindings = V.singleton textureBind
}
poolInfo = Vk.zero
{ Vk.flags =
Vk.DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT .|.
Vk.DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT
, Vk.maxSets = 10
, Vk.poolSizes = V.fromList
[ Vk.zero
{ Vk.type' = Vk.DESCRIPTOR_TYPE_UNIFORM_BUFFER
, Vk.descriptorCount = 10
}
, Vk.zero
{ Vk.type' = Vk.DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
, Vk.descriptorCount = 10
}
, Vk.zero
{ Vk.type' = Vk.DESCRIPTOR_TYPE_STORAGE_BUFFER
, Vk.descriptorCount = 10
}
, Vk.zero
{ Vk.type' = Vk.DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER
, Vk.descriptorCount = 10
}
]
}
(_, descriptorSetLayout1) <-
Vk.withDescriptorSetLayout device setInfo Nothing allocate
(_, descriptorSetLayout2) <-
Vk.withDescriptorSetLayout device setInfo2 Nothing allocate
(_, textureDescriptorSetLayout) <-
Vk.withDescriptorSetLayout device setInfo3 Nothing allocate
(_, descriptorPool) <-
Vk.withDescriptorPool device poolInfo Nothing allocate
return (descriptorSetLayout1, descriptorSetLayout2, textureDescriptorSetLayout, descriptorPool)

View file

@ -14,10 +14,12 @@ import qualified Vulkan.Zero as Vk
createInstance :: MonadResource m => SDL.Window -> m Vk.Instance createInstance :: MonadResource m => SDL.Window -> m Vk.Instance
createInstance window = do createInstance window = do
windowExtensions <- liftIO $ fmap (Vk.EXT_DEBUG_UTILS_EXTENSION_NAME :) $ windowExtensions <- liftIO $ fmap (
[ Vk.EXT_DEBUG_UTILS_EXTENSION_NAME
] ++) $
traverse BS.packCString =<< SDL.vkGetInstanceExtensions window traverse BS.packCString =<< SDL.vkGetInstanceExtensions window
liftIO $ do liftIO $ do
putStrLn "activating followinfg extensions:" putStrLn "activating following extensions:"
print windowExtensions print windowExtensions
let createInfo = Vk.zero let createInfo = Vk.zero
{ Vk.applicationInfo = Just Vk.zero { Vk.applicationInfo = Just Vk.zero
@ -30,11 +32,6 @@ createInstance window = do
, Vk.enabledLayerNames = V.singleton "VK_LAYER_KHRONOS_validation" , Vk.enabledLayerNames = V.singleton "VK_LAYER_KHRONOS_validation"
} }
(_, inst) <- allocate (_, inst) <- Vk.withInstance createInfo Nothing allocate
(Vk.createInstance createInfo Nothing)
(\inst -> do
putStrLn "destroying instance"
Vk.destroyInstance inst Nothing
)
return inst return inst

View file

@ -1,16 +1,23 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedRecordDot #-}
module Main where module Main where
import qualified Control.Concurrent.STM as STM
import Control.Monad import Control.Monad
import Control.Monad.Loops import Control.Monad.Loops
import Control.Monad.IO.Class import Control.Monad.IO.Class
import Control.Monad.Trans.Resource import Control.Monad.Trans.Resource
import Data.Bits import Data.Bits
import Data.Word import Data.ByteString.Char8
import qualified Data.Map.Strict as M
import Data.Time.Clock
import qualified Data.Vector as V
import qualified SDL hiding (V2) import qualified SDL hiding (V2)
import qualified Control.Concurrent.STM as STM import System.IO (stdout)
import System.Mem
import qualified Vulkan as Vk import qualified Vulkan as Vk
import qualified Vulkan.Zero as Vk import qualified Vulkan.Zero as Vk
@ -21,25 +28,53 @@ import Draw (drawFrame)
import Types import Types
main :: IO () main :: IO ()
main = runResourceT $ do main = do
renderablesContainer <- STM.newTMVarIO (V.empty)
meshMap <- STM.newTMVarIO (M.empty)
materialMap <- STM.newTMVarIO (M.empty)
textureMap <- STM.newTMVarIO (M.empty)
frameNumber <- STM.newTMVarIO 0
let initState = ReadState
{ renderables = renderablesContainer
, meshLibrary = meshMap
, materialLibrary = materialMap
, textureLibrary = textureMap
}
runRender initState $ do
engineData <- initEngine engineData <- initEngine
SDL.showWindow $ engineWindow engineData SDL.showWindow $ engineWindow engineData
-- create abort condition for upcoming lop -- create abort condition for upcoming lop
quit <- liftIO $ STM.newTMVarIO True quit <- liftIO $ STM.newTMVarIO True
showPipeline <- liftIO $ STM.newTMVarIO Green time <- liftIO $ STM.newTMVarIO =<< getCurrentTime
frameContainer <- liftIO $ STM.newTMVarIO 0 average <- liftIO $ STM.newTMVarIO 0
-- main loop -- main loop
whileM_ whileM_
(liftIO $ STM.atomically $ STM.readTMVar quit (liftIO $ STM.atomically $ STM.readTMVar quit
) )
( do ( do
frameNumber <- liftIO $ STM.atomically $ STM.takeTMVar frameContainer liftIO performMajorGC
liftIO $ STM.atomically $ STM.putTMVar frameContainer (succ frameNumber) now <- liftIO getCurrentTime
before <- liftIO $ STM.atomically $ STM.swapTMVar time now
let timeDiff = diffUTCTime now before
fps = recip $ nominalDiffTimeToSeconds timeDiff
liftIO $ hPutStr stdout $ pack $ "Current FPS: " <> show (floor fps) <> " \r"
number <- liftIO $ STM.atomically $ do
number <- STM.readTMVar frameNumber
if number == 0 then
void $ STM.swapTMVar average fps
else do
oldAverage <- STM.readTMVar average
let newAverage = (oldAverage * fromIntegral number + fps) / frameCount
frameCount = fromIntegral number + 1
void $ STM.swapTMVar average newAverage
void $ STM.swapTMVar frameNumber (succ number)
return number
-- draw -- draw
drawFrame engineData showPipeline frameNumber -- liftIO $ putStr $ show (recip $ nominalDiffTimeToSeconds timeDiff) <> "\r"
drawFrame engineData number
-- poll events -- poll events
evs <- liftIO SDL.pollEvents evs <- liftIO SDL.pollEvents
-- flip abort condition on window close -- flip abort condition on window close
@ -52,6 +87,12 @@ main = runResourceT $ do
evs evs
) )
fps <- liftIO $ STM.atomically $ STM.readTMVar average
liftIO $ hPutStr stdout $
"\n" <>
"Average FPS: " <> pack (show fps) <> "\n"
Vk.deviceWaitIdle $ engineLogicalDevice engineData Vk.deviceWaitIdle $ engineLogicalDevice engineData
createBufferView createBufferView
@ -66,12 +107,8 @@ createBufferView logicalDevice = do
Vk.BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT Vk.BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT
, Vk.sharingMode = Vk.SHARING_MODE_EXCLUSIVE , Vk.sharingMode = Vk.SHARING_MODE_EXCLUSIVE
} }
buffer <- snd <$> allocate buffer <- snd <$>
(Vk.createBuffer logicalDevice bufferCreateInfo Nothing) (Vk.withBuffer logicalDevice bufferCreateInfo Nothing allocate)
(\buffer -> do
putStrLn "destroying buffer"
Vk.destroyBuffer logicalDevice buffer Nothing
)
let bufferViewCreateInfo = Vk.zero let bufferViewCreateInfo = Vk.zero
{ Vk.buffer = buffer { Vk.buffer = buffer
@ -80,12 +117,7 @@ createBufferView logicalDevice = do
, Vk.range = Vk.WHOLE_SIZE , Vk.range = Vk.WHOLE_SIZE
} }
allocate Vk.withBufferView logicalDevice bufferViewCreateInfo Nothing allocate
(Vk.createBufferView logicalDevice bufferViewCreateInfo Nothing)
(\bufferView -> do
putStrLn "destroying bufferView"
Vk.destroyBufferView logicalDevice bufferView Nothing
)
allocateMemory allocateMemory
:: (MonadResource m) :: (MonadResource m)
@ -96,13 +128,8 @@ allocateMemory logicalDevice buffer = do
memoryRequirements <- Vk.getBufferMemoryRequirements logicalDevice buffer memoryRequirements <- Vk.getBufferMemoryRequirements logicalDevice buffer
let memoryAllocateInfo = Vk.zero let memoryAllocateInfo = Vk.zero
{ Vk.allocationSize = (Vk.size :: Vk.MemoryRequirements -> Word64) memoryRequirements { Vk.allocationSize = memoryRequirements.size
, Vk.memoryTypeIndex = error ("createBuffer: TODO: populate memoryTypeIndex!" :: String) , Vk.memoryTypeIndex = error ("createBuffer: TODO: populate memoryTypeIndex!" :: String)
} }
allocate Vk.withMemory logicalDevice memoryAllocateInfo Nothing allocate
(Vk.allocateMemory logicalDevice memoryAllocateInfo Nothing)
(\memory -> do
putStrLn "Freeing memory"
Vk.freeMemory logicalDevice memory Nothing
)

View file

@ -1,19 +1,25 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedRecordDot #-}
module Memory where module Memory where
import Control.Monad.IO.Class (liftIO) import Control.Monad.IO.Class
import Control.Monad.Trans.Resource import Control.Monad.Trans.Resource
import Data.Bits ((.|.))
import Foreign.Ptr import Foreign.Ptr
import Foreign.Storable
import qualified VulkanMemoryAllocator as VMA import qualified VulkanMemoryAllocator as VMA
import qualified Vulkan.Core10 as Vk import qualified Vulkan.Core10 as Vk
import qualified Vulkan.Dynamic as Vk import qualified Vulkan.Dynamic as Vk
import qualified Vulkan.Zero as Vk import qualified Vulkan.Zero as Vk
-- internal imports
import Types
initAllocator initAllocator
:: (MonadResource m, MonadFail m) :: (MonadResource m, MonadFail m)
=> Vk.PhysicalDevice => Vk.PhysicalDevice
@ -22,9 +28,9 @@ initAllocator
-> m VMA.Allocator -> m VMA.Allocator
initAllocator physicalDevice device instance' = do initAllocator physicalDevice device instance' = do
let instanceProcAddr = castFunPtr $ let instanceProcAddr = castFunPtr $
Vk.pVkGetInstanceProcAddr ((Vk.instanceCmds :: Vk.Instance -> Vk.InstanceCmds) instance') Vk.pVkGetInstanceProcAddr (instance'.instanceCmds)
deviceProcAddr = castFunPtr $ deviceProcAddr = castFunPtr $
Vk.pVkGetDeviceProcAddr ((Vk.deviceCmds :: Vk.Device -> Vk.DeviceCmds) device) Vk.pVkGetDeviceProcAddr (device.deviceCmds)
let allocatorInfo = Vk.zero let allocatorInfo = Vk.zero
{ VMA.physicalDevice = Vk.physicalDeviceHandle physicalDevice { VMA.physicalDevice = Vk.physicalDeviceHandle physicalDevice
, VMA.device = Vk.deviceHandle device , VMA.device = Vk.deviceHandle device
@ -35,11 +41,30 @@ initAllocator physicalDevice device instance' = do
} :: VMA.VulkanFunctions) } :: VMA.VulkanFunctions)
} :: VMA.AllocatorCreateInfo } :: VMA.AllocatorCreateInfo
(_, allocator) <- allocate (_, allocator) <- VMA.withAllocator allocatorInfo allocate
(VMA.createAllocator allocatorInfo)
(\allocator -> do
putStrLn "destroying allocator"
VMA.destroyAllocator allocator
)
return allocator return allocator
createAllocatedBuffer
:: (MonadResource m, MonadIO m)
=> VMA.Allocator
-> Int
-> Vk.BufferUsageFlags
-> VMA.MemoryUsage
-> m AllocatedBuffer
createAllocatedBuffer allocator allocationSize usage memoryUsage = do
let bufferCreateInfo = Vk.zero
{ Vk.size = fromIntegral allocationSize
, Vk.usage = usage
}
vmaAllocationInfo = Vk.zero
{ VMA.usage = memoryUsage
, VMA.flags = VMA.ALLOCATION_CREATE_MAPPED_BIT
, VMA.requiredFlags = Vk.MEMORY_PROPERTY_HOST_VISIBLE_BIT .|. Vk.MEMORY_PROPERTY_HOST_COHERENT_BIT
, VMA.preferredFlags = Vk.MEMORY_PROPERTY_DEVICE_LOCAL_BIT
} :: VMA.AllocationCreateInfo
(_, (buffer, newAllocation, info)) <-
VMA.withBuffer allocator bufferCreateInfo vmaAllocationInfo allocate
return $ AllocatedBuffer buffer newAllocation info

View file

@ -1,18 +1,24 @@
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE DataKinds #-} {-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
module Mesh where module Mesh where
import Codec.Wavefront import Codec.Wavefront
import qualified Control.Concurrent.STM as STM
import qualified Control.Monad as CM (void)
import Control.Monad.IO.Class import Control.Monad.IO.Class
import Control.Monad.Reader
import Control.Monad.Trans.Resource import Control.Monad.Trans.Resource
import qualified Data.Map.Strict as M
import Data.Maybe (fromMaybe)
import qualified Data.Vector as V import qualified Data.Vector as V
import Foreign import Foreign
import Linear (V3(..), V4(..)) import Linear (V2(..), V3(..), V4(..))
import qualified VulkanMemoryAllocator as VMA import qualified VulkanMemoryAllocator as VMA
import qualified Vulkan.Core10 as Vk import qualified Vulkan.Core10 as Vk
@ -21,50 +27,112 @@ import qualified Vulkan.Zero as Vk
-- internal imports -- internal imports
import Types import Types
import Sync
loadMeshes loadMeshes
:: V.Vector Vertex :: (MonadReader ReadState m, MonadResource m, MonadFail m)
loadMeshes = => VMA.Allocator
V.fromList -> UploadContext
[ Vertex (V3 0.5 0.5 0) (V3 0 0 0) (V4 0 1 0 1) -> Vk.Queue
, Vertex (V3 (-0.5) 0.5 0) (V3 0 0 0) (V4 0 1 0 1) -> Vk.Device
, Vertex (V3 0 (-0.5) 0) (V3 0 0 0) (V4 0 1 0 1) -> m ()
loadMeshes allocator uploadContext queue device = do
let triangle = V.fromList
[ Vertex (V3 0.5 0 0) (V3 0 0 0) (V4 0 1 0 1) (V2 0 0)
, Vertex (V3 (-0.5) 0 0) (V3 0 0 0) (V4 0 1 0 1) (V2 0 0)
, Vertex (V3 0 1 0) (V3 0 0 0) (V4 0 1 0 1) (V2 0 0)
] ]
triMesh <- uploadMesh triangle allocator uploadContext queue device
maskMesh <- loadFromObj
"./assets/models/jaeger.obj"
allocator
uploadContext
queue
device
meshLib <- asks meshLibrary
liftIO $ STM.atomically $ do
meshMap <- STM.readTMVar meshLib
let newMap =
M.insert "triangle" triMesh $
M.insert "mask" maskMesh meshMap
CM.void $ STM.swapTMVar meshLib newMap
uploadMesh uploadMesh
:: (MonadResource m, MonadFail m) :: (MonadResource m, MonadFail m)
=> V.Vector Vertex => V.Vector Vertex
-> VMA.Allocator -> VMA.Allocator
-> UploadContext
-> Vk.Queue
-> Vk.Device
-> m Mesh -> m Mesh
uploadMesh vertices allocator = do uploadMesh vertices allocator uploadContext queue device = do
let bufferCreateInfo = Vk.zero
{ Vk.size = fromIntegral $ let bufferSize = fromIntegral $ V.length vertices * sizeOf (undefined :: Vertex)
V.length vertices * sizeOf (Vertex (V3 0 0 0) (V3 0 0 0 ) (V4 0 0 0 0)) stagingBufferCrateInfo = Vk.zero
, Vk.usage = Vk.BUFFER_USAGE_VERTEX_BUFFER_BIT { Vk.size = bufferSize
, Vk.usage = Vk.BUFFER_USAGE_TRANSFER_SRC_BIT
}
stagingAllocationCreateInfo = Vk.zero
{ VMA.usage = VMA.MEMORY_USAGE_CPU_ONLY
} :: VMA.AllocationCreateInfo
(stagingReleaseKey, stagingBuffer) <- do
(releaseKey, (buffer, bAllocation, bInfo)) <-
VMA.withBuffer allocator stagingBufferCrateInfo stagingAllocationCreateInfo allocate
return (releaseKey, AllocatedBuffer
{ bufferInfo = bInfo
, bufferAllocation = bAllocation
, allocatedBuffer = buffer
})
(dataReleaseKey, dataPtr) <-
VMA.withMappedMemory allocator (bufferAllocation stagingBuffer) allocate
liftIO $ V.mapM_ (\(idx, vertex) ->
poke
(castPtr dataPtr `plusPtr` (idx * sizeOf vertex))
vertex
)
(V.zip
(V.fromList [0..])
vertices
)
liftIO $ release dataReleaseKey
let vertexBufferCreateInfo = Vk.zero
{ Vk.size = bufferSize
, Vk.usage = Vk.BUFFER_USAGE_VERTEX_BUFFER_BIT .|. Vk.BUFFER_USAGE_TRANSFER_DST_BIT
} }
allocationCreateInfo = Vk.zero allocationCreateInfo = Vk.zero
{ VMA.usage = VMA.MEMORY_USAGE_CPU_TO_GPU { VMA.usage = VMA.MEMORY_USAGE_GPU_ONLY
} :: VMA.AllocationCreateInfo } :: VMA.AllocationCreateInfo
(_, mesh) <- allocate
(do
(buffer, allocation, _) <- VMA.createBuffer allocator bufferCreateInfo allocationCreateInfo
return (Mesh vertices (AllocatedBuffer buffer allocation))
)
(\(Mesh _ (AllocatedBuffer buffer allocation)) -> do
putStrLn "destroying mesh"
VMA.destroyBuffer allocator buffer allocation
)
(dataReleaseKey, dataPtr) <- allocate mesh <- do
(VMA.mapMemory allocator (bufferAllocation $ meshBuffer mesh)) (buffer, bAllocation, bInfo) <- snd <$>
(\_ -> VMA.unmapMemory allocator (bufferAllocation $ meshBuffer mesh)) VMA.withBuffer allocator vertexBufferCreateInfo allocationCreateInfo allocate
return $ Mesh
{ meshVertices = vertices
, meshBuffer = AllocatedBuffer
{ bufferInfo = bInfo
, bufferAllocation = bAllocation
, allocatedBuffer = buffer
}
}
liftIO $ mapM_ (\(idx, vertex) -> immediateSubmit uploadContext queue device$ \cmd -> do
poke (castPtr (dataPtr `plusPtr` (idx * sizeOf vertex))) vertex let copy = V.singleton $ Vk.BufferCopy
) { size = bufferSize
(zip [0..] (V.toList vertices)) , dstOffset = 0
, srcOffset = 0
}
release dataReleaseKey Vk.cmdCopyBuffer cmd (allocatedBuffer stagingBuffer) (allocatedBuffer $ meshBuffer mesh) copy
release stagingReleaseKey
return mesh return mesh
@ -72,18 +140,22 @@ loadFromObj
:: (MonadResource m, MonadFail m) :: (MonadResource m, MonadFail m)
=> FilePath => FilePath
-> VMA.Allocator -> VMA.Allocator
-> UploadContext
-> Vk.Queue
-> Vk.Device
-> m Mesh -> m Mesh
loadFromObj filepath vma = do loadFromObj filepath vma uploadContext queue device = do
eitherObj <- liftIO $ fromFile filepath eitherObj <- liftIO $ fromFile filepath
case eitherObj of case eitherObj of
Left err -> Left err ->
error ("loadFromObj: loading mesh data: " <> err) error ("loadFromObj: loading mesh data: " <> err)
Right obj -> do Right obj -> do
let vertices = digestFaces (objFaces obj) (objLocations obj) let vertices = digestFaces (objFaces obj) (objLocations obj) (objTexCoords obj)
uploadMesh vertices vma uploadMesh vertices vma uploadContext queue device
where where
digestFaces :: V.Vector (Element Face) -> V.Vector Location -> V.Vector Vertex digestFaces
digestFaces faces locations = :: V.Vector (Element Face) -> V.Vector Location -> V.Vector TexCoord -> V.Vector Vertex
digestFaces faces locations texCoords =
V.foldl V.foldl
(\acc (Element _ _ _ _ (Face fa fb fc others)) -> (\acc (Element _ _ _ _ (Face fa fb fc others)) ->
if not (null others) if not (null others)
@ -95,8 +167,9 @@ loadFromObj filepath vma = do
let position = faceIndexToPosition faceIndex locations let position = faceIndexToPosition faceIndex locations
normal = V3 0 0 0 normal = V3 0 0 0
color = V4 1 1 0 1 color = V4 1 1 0 1
uv = faceIndexToUV faceIndex texCoords
in in
Vertex position normal color Vertex position normal color uv
) )
(V.fromList [ fa, fb, fc ]) (V.fromList [ fa, fb, fc ])
in in
@ -109,3 +182,45 @@ loadFromObj filepath vma = do
let Location x y z _ = locations V.! (faceLocIndex index - 1) let Location x y z _ = locations V.! (faceLocIndex index - 1)
in in
V3 x y z V3 x y z
faceIndexToUV :: FaceIndex -> V.Vector TexCoord -> V2 Float
faceIndexToUV index texCoords =
let TexCoord r s _ =
texCoords V.!
(fromMaybe (error "no UV coordinates present") (faceTexCoordIndex index) - 1)
in
-- flip V coordinate of UVs to fix display
V2 r (-s)
compactDraws
:: M.Map String Mesh
-> M.Map String Material
-> V.Vector RenderObject
-> V.Vector IndirectBatch
compactDraws meshLib materialLib objects =
V.foldl
(\acc (index, RenderObject mesh material _) ->
let newDraw = IndirectBatch
{ batchMesh = meshLib M.! mesh
, batchMaterial = materialLib M.! material
, batchFirst = index
, batchCount = 1
}
sameMesh = not (V.null acc) && (meshLib M.! mesh == batchMesh (V.last acc))
sameMaterial = not (V.null acc) && (materialLib M.! material == batchMaterial (V.last acc))
in
if sameMesh && sameMaterial
then
V.init acc `V.snoc` ((V.last acc)
{ batchCount = batchCount (V.last acc) + 1
})
else
acc `V.snoc` newDraw
)
V.empty
(V.zip
(V.fromList [0..])
objects
)

75
src/Sync.hs Normal file
View file

@ -0,0 +1,75 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
module Sync where
import Control.Monad.Trans.Resource
import Data.Bits (bit)
import qualified Data.Vector as V
import Data.Word
import qualified Vulkan as Vk
import qualified Vulkan.Zero as Vk
-- internal imports
import Control.Monad.Reader
import Types
import Util
initSyncStructures
:: (MonadResource m)
=> Vk.Device
-> Word32
-> m UploadContext
initSyncStructures logicalDevice queueFamilyIndex = do
let fenceCreateInfo = Vk.zero
syncFence <- snd <$>
Vk.withFence logicalDevice fenceCreateInfo Nothing allocate
let uploadCommandPoolCreateInfo = Vk.zero
{ Vk.queueFamilyIndex = queueFamilyIndex
} :: Vk.CommandPoolCreateInfo
syncCommandPool <- snd <$>
Vk.withCommandPool logicalDevice uploadCommandPoolCreateInfo Nothing allocate
let cmdBufAllocInfo = Vk.zero
{ Vk.commandPool = syncCommandPool
, Vk.commandBufferCount = 1
}
syncCommandBuffer <- V.head . snd <$> Vk.withCommandBuffers logicalDevice cmdBufAllocInfo allocate
return $ UploadContext
{ uploadFence = syncFence
, uploadCommandBuffer = syncCommandBuffer
, uploadCommandPool = syncCommandPool
}
immediateSubmit
:: (MonadIO m)
=> UploadContext
-> Vk.Queue
-> Vk.Device
-> (Vk.CommandBuffer -> m ())
-> m ()
immediateSubmit uploadContext queue device func = do
let cmdBegin = beginCommandBuffer Vk.COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
Vk.useCommandBuffer
(uploadCommandBuffer uploadContext)
cmdBegin
(func (uploadCommandBuffer uploadContext))
let submitInfo = createSubmitInfo (uploadCommandBuffer uploadContext)
Vk.queueSubmit queue submitInfo (uploadFence uploadContext)
void $ Vk.waitForFences device (V.singleton $ uploadFence uploadContext) True maxBound
Vk.resetFences device (V.singleton $ uploadFence uploadContext)
Vk.resetCommandPool device (uploadCommandPool uploadContext) (bit 0)

173
src/Textures.hs Normal file
View file

@ -0,0 +1,173 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Textures where
import qualified Codec.Picture as P
import Control.Monad.IO.Class
import Control.Monad.Reader
import Control.Monad.Trans.Resource
import qualified Data.Map.Strict as M
import qualified Data.Vector as V
import qualified Data.Vector.Storable as VS
import Foreign hiding (void)
import qualified Control.Concurrent.STM as STM
import qualified Vulkan as Vk
import qualified Vulkan.CStruct.Extends as Vk
import qualified Vulkan.Zero as Vk
import qualified VulkanMemoryAllocator as VMA
-- internal imports
import Types
import Memory
import Sync
import Image
loadImageFromFile
:: (MonadResource m, MonadIO m)
=> VMA.Allocator
-> UploadContext
-> Vk.Queue
-> Vk.Device
-> FilePath
-> m AllocatedImage
loadImageFromFile allocator uploadContext queue device file = do
pixels <- liftIO $ either error P.convertRGBA8 <$> P.readImage file
let texWidth = P.imageWidth pixels
texHeight = P.imageHeight pixels
imageSize = 4 * texWidth * texHeight
imageFormat = Vk.FORMAT_R8G8B8A8_SRGB
stagingBuffer <-
createAllocatedBuffer
allocator
imageSize
Vk.BUFFER_USAGE_TRANSFER_SRC_BIT
VMA.MEMORY_USAGE_CPU_ONLY
let dataPtr = VMA.mappedData (bufferInfo stagingBuffer)
liftIO $ pokeArray (castPtr dataPtr) (VS.toList $ P.imageData pixels)
let imageExtent = Vk.Extent3D
{ width = fromIntegral texWidth
, height = fromIntegral texHeight
, depth = 1
}
dImgAllocCreateInfo = Vk.zero
{ VMA.usage = VMA.MEMORY_USAGE_GPU_ONLY
} :: VMA.AllocationCreateInfo
dImgCreateInfo = imageCreate
imageFormat
imageExtent
(Vk.IMAGE_USAGE_SAMPLED_BIT .|. Vk.IMAGE_USAGE_TRANSFER_DST_BIT)
(newImage, newAllocation, _) <- snd <$>
VMA.withImage allocator dImgCreateInfo dImgAllocCreateInfo allocate
immediateSubmit
uploadContext
queue
device
(\cmd -> do
let range = Vk.zero
{ Vk.aspectMask = Vk.IMAGE_ASPECT_COLOR_BIT
, Vk.baseMipLevel = 0
, Vk.levelCount = 1
, Vk.baseArrayLayer = 0
, Vk.layerCount = 1
}
imageBarrierToTransfer = V.singleton $ Vk.SomeStruct (Vk.zero
{ Vk.oldLayout = Vk.IMAGE_LAYOUT_UNDEFINED
, Vk.newLayout = Vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
, Vk.image = newImage
, Vk.subresourceRange = range
-- , Vk.srcAccessMask = bit 0
, Vk.dstAccessMask = Vk.ACCESS_TRANSFER_WRITE_BIT
} :: Vk.ImageMemoryBarrier '[])
Vk.cmdPipelineBarrier
cmd
Vk.PIPELINE_STAGE_TOP_OF_PIPE_BIT
Vk.PIPELINE_STAGE_TRANSFER_BIT
(bit 0)
V.empty
V.empty
imageBarrierToTransfer
let copyRegion = V.singleton (Vk.zero
{ Vk.bufferOffset = 0
, Vk.bufferRowLength = 0
, Vk.bufferImageHeight = 0
, Vk.imageExtent = imageExtent
, Vk.imageSubresource = Vk.zero
{ Vk.aspectMask = Vk.IMAGE_ASPECT_COLOR_BIT
, Vk.mipLevel = 0
, Vk.baseArrayLayer = 0
, Vk.layerCount = 1
} :: Vk.ImageSubresourceLayers
} :: Vk.BufferImageCopy)
Vk.cmdCopyBufferToImage
cmd
(allocatedBuffer stagingBuffer)
newImage
Vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
copyRegion
let imageBarrierToReadable = V.singleton $ Vk.SomeStruct (Vk.zero
{ Vk.oldLayout = Vk.IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL
, Vk.newLayout = Vk.IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
, Vk.image = newImage
, Vk.subresourceRange = range
, Vk.srcAccessMask = Vk.ACCESS_TRANSFER_WRITE_BIT
, Vk.dstAccessMask = Vk.ACCESS_SHADER_READ_BIT
} :: Vk.ImageMemoryBarrier '[])
Vk.cmdPipelineBarrier
cmd
Vk.PIPELINE_STAGE_TRANSFER_BIT
Vk.PIPELINE_STAGE_FRAGMENT_SHADER_BIT
(bit 0)
V.empty
V.empty
imageBarrierToReadable
)
return $ AllocatedImage
{ image = newImage
, allocation = newAllocation
}
loadImages
:: (MonadResource m, MonadReader ReadState m)
=> VMA.Allocator
-> UploadContext
-> Vk.Queue
-> Vk.Device
-> m ()
loadImages allocator uploadContext queue device = do
loadedImage <- loadImageFromFile allocator uploadContext queue device "assets/textures/jaeger.texture.tga"
let imageInfo = imageviewCreate (image loadedImage) Vk.FORMAT_R8G8B8A8_SRGB Vk.IMAGE_ASPECT_COLOR_BIT
imageView <- snd <$> Vk.withImageView device imageInfo Nothing allocate
let texture = Texture
{ textureImage = loadedImage
, textureImageView = imageView
}
textureLib <- asks textureLibrary
void $ liftIO $ STM.atomically $ do
texMap <- STM.readTMVar textureLib
let newMap = M.insert "marksman" texture texMap
STM.swapTMVar textureLib newMap

View file

@ -1,6 +1,13 @@
{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE DeriveFunctor #-}
module Types where module Types where
import Control.Concurrent.STM.TMVar as STM
import Control.Monad.IO.Class
import Control.Monad.Reader
import Control.Monad.Trans.Resource
import qualified Data.Map.Strict as M
import qualified Data.Vector as V import qualified Data.Vector as V
import Foreign import Foreign
import Foreign.C.Types (CInt) import Foreign.C.Types (CInt)
@ -10,12 +17,6 @@ import qualified Vulkan as Vk
import qualified Vulkan.Zero as Vk import qualified Vulkan.Zero as Vk
import qualified VulkanMemoryAllocator as VMA import qualified VulkanMemoryAllocator as VMA
data Pipelines
= Rainbow
| Red
| Green
deriving (Eq, Ord, Enum, Bounded)
data ShaderContainer = ShaderContainer data ShaderContainer = ShaderContainer
{ containedVertexShader :: Maybe Vk.ShaderModule { containedVertexShader :: Maybe Vk.ShaderModule
, containedFragmentShader :: Maybe Vk.ShaderModule , containedFragmentShader :: Maybe Vk.ShaderModule
@ -30,48 +31,63 @@ data EngineData = EngineData
, engineInstance :: Vk.Instance , engineInstance :: Vk.Instance
, engineSwapchain :: Vk.SwapchainKHR , engineSwapchain :: Vk.SwapchainKHR
, engineQueue :: Vk.Queue , engineQueue :: Vk.Queue
, engineCommandBuffers :: V.Vector Vk.CommandBuffer
, engineFramebuffers :: V.Vector Vk.Framebuffer , engineFramebuffers :: V.Vector Vk.Framebuffer
, meshPipelineLayout :: Vk.PipelineLayout
, meshPipeline :: V.Vector Vk.Pipeline
, engineRenderPass :: Vk.RenderPass , engineRenderPass :: Vk.RenderPass
, engineInFlightFence :: Vk.Fence
, engineImageAvailableSemaphore :: Vk.Semaphore
, engineRenderFinishedSemaphore :: Vk.Semaphore
, engineMesh :: Mesh
, engineExternalMesh :: Mesh
, engineAllocator :: VMA.Allocator , engineAllocator :: VMA.Allocator
, engineDepthImageView :: Vk.ImageView , engineDepthImageView :: Vk.ImageView
, engineDepthImage :: AllocatedImage , engineDepthImage :: AllocatedImage
, engineDepthFormat :: Vk.Format , engineDepthFormat :: Vk.Format
, engineFrames :: V.Vector FrameData
, engineGlobalSetLayout :: Vk.DescriptorSetLayout
, engineObjectSetLayout :: Vk.DescriptorSetLayout
, engineSingleTextureSetLayout :: Vk.DescriptorSetLayout
, engineDescriptorPool :: Vk.DescriptorPool
, engineSceneParameters :: GPUSceneData
, engineUploadContext :: UploadContext
} }
deriving (Show)
data AllocatedBuffer = AllocatedBuffer data AllocatedBuffer = AllocatedBuffer
{ allocatedBuffer :: Vk.Buffer { allocatedBuffer :: Vk.Buffer
, bufferAllocation :: VMA.Allocation , bufferAllocation :: VMA.Allocation
, bufferInfo :: VMA.AllocationInfo
} }
deriving (Show) deriving (Show)
instance Eq AllocatedBuffer where
(AllocatedBuffer buf1 _ _) == (AllocatedBuffer buf2 _ _) = buf1 == buf2
data Vertex = Vertex data Vertex = Vertex
{ vertexPosition :: V3 Float { vertexPosition :: V3 Float
, vertexNormal :: V3 Float , vertexNormal :: V3 Float
, vertexColor :: V4 Float , vertexColor :: V4 Float
, vertexUV :: V2 Float
} }
deriving (Show) deriving (Show, Eq)
instance Storable Vertex where instance Storable Vertex where
sizeOf (Vertex position normal color) = sizeOf position + sizeOf normal + sizeOf color sizeOf _ =
sizeOf (undefined :: V3 Float) * 2 +
sizeOf (undefined :: V4 Float) +
sizeOf (undefined :: V2 Float)
peek _ = undefined peek ptr = do
pos <- peek (castPtr ptr)
nor <- peek (castPtr ptr `plusPtr` sizeOf pos)
col <- peek (castPtr ptr `plusPtr` sizeOf pos `plusPtr` sizeOf nor)
uv <- peek (castPtr ptr `plusPtr` sizeOf pos `plusPtr` sizeOf nor `plusPtr` sizeOf col)
return $ Vertex pos nor col uv
poke ptr (Vertex position normal color) = do poke ptr (Vertex position normal color uv) = do
let castedV3Ptr = castPtr ptr let castedV3Ptr = castPtr ptr
pokeElemOff castedV3Ptr 0 position pokeElemOff castedV3Ptr 0 position
pokeElemOff castedV3Ptr 1 normal pokeElemOff castedV3Ptr 1 normal
poke (castPtr (ptr `plusPtr` sizeOf position `plusPtr` sizeOf normal)) color poke (castPtr (ptr `plusPtr` sizeOf position `plusPtr` sizeOf normal)) color
poke (castPtr (ptr `plusPtr` sizeOf position `plusPtr` sizeOf normal `plusPtr` sizeOf color)) uv
alignment _ = undefined alignment _ = 0
class VertexInputDescribable v where class VertexInputDescribable v where
@ -103,6 +119,13 @@ instance VertexInputDescribable Vertex where
, Vk.format = Vk.FORMAT_R32G32B32A32_SFLOAT , Vk.format = Vk.FORMAT_R32G32B32A32_SFLOAT
, Vk.offset = fromIntegral (sizeOf (vertexPosition v) + sizeOf (vertexNormal v)) , Vk.offset = fromIntegral (sizeOf (vertexPosition v) + sizeOf (vertexNormal v))
} :: Vk.VertexInputAttributeDescription } :: Vk.VertexInputAttributeDescription
uvAttribute = Vk.zero
{ Vk.binding = 0
, Vk.location = 3
, Vk.format = Vk.FORMAT_R32G32_SFLOAT
, Vk.offset = fromIntegral $
sizeOf (vertexPosition v) + sizeOf (vertexNormal v) + sizeOf (vertexColor v)
} :: Vk.VertexInputAttributeDescription
in in
VertexInputDescription VertexInputDescription
{ vidBindings = V.fromList [ mainBinding ] { vidBindings = V.fromList [ mainBinding ]
@ -110,6 +133,7 @@ instance VertexInputDescribable Vertex where
[ positionAttribute [ positionAttribute
, normalAttribute , normalAttribute
, colorAttribute , colorAttribute
, uvAttribute
] ]
} }
@ -123,7 +147,7 @@ data Mesh = Mesh
{ meshVertices :: V.Vector Vertex { meshVertices :: V.Vector Vertex
, meshBuffer :: AllocatedBuffer , meshBuffer :: AllocatedBuffer
} }
deriving (Show) deriving (Show, Eq)
data MeshPushConstants = MeshPushConstants data MeshPushConstants = MeshPushConstants
{ meshPushData :: V4 Float { meshPushData :: V4 Float
@ -131,9 +155,12 @@ data MeshPushConstants = MeshPushConstants
} }
deriving (Show) deriving (Show)
undefinedMeshPushConstants :: MeshPushConstants
undefinedMeshPushConstants = MeshPushConstants (V4 0 0 0 0) identity
instance Storable MeshPushConstants where instance Storable MeshPushConstants where
sizeOf _ = sizeOf (undefined :: V4 Float) + sizeOf (undefined :: M44 Float) sizeOf _ = sizeOf (V4 0 0 0 0 :: V4 Float) + sizeOf (identity :: M44 Float)
peek ptr = do peek ptr = do
dat <- peek (castPtr ptr) dat <- peek (castPtr ptr)
@ -151,3 +178,154 @@ data AllocatedImage = AllocatedImage
, allocation :: VMA.Allocation , allocation :: VMA.Allocation
} }
deriving (Show) deriving (Show)
data Material = Material
{ materialTextureSet :: Vk.DescriptorSet
, materialPipeline :: Vk.Pipeline
, materialPipelineLayout :: Vk.PipelineLayout
}
deriving (Show, Eq)
data RenderObject = RenderObject
{ objectMesh :: String
, objectMaterial :: String
, objectMatrix :: M44 Float
}
deriving (Show)
data Texture = Texture
{ textureImage :: AllocatedImage
, textureImageView :: Vk.ImageView
}
data ReadState = ReadState
{ renderables :: STM.TMVar (V.Vector RenderObject)
, meshLibrary :: STM.TMVar (M.Map String Mesh)
, materialLibrary :: STM.TMVar (M.Map String Material)
, textureLibrary :: STM.TMVar (M.Map String Texture)
}
newtype RenderReader rd m a = RenderReader
{ runRenderInner :: RenderInner rd m a
}
deriving
(Functor, Applicative, Monad, MonadIO, MonadReader rd, MonadResource, MonadFail)
type RenderInner rd m = ReaderT rd m
type Render a = RenderReader ReadState ResIO a
runRender :: ReadState -> Render a -> IO a
runRender rd actions = runResourceT $ flip runReaderT rd $ runRenderInner actions
data FrameData = FrameData
{ framePresentSemaphore :: Vk.Semaphore
, frameRenderSemaphore :: Vk.Semaphore
, frameRenderFence :: Vk.Fence
, frameCommandPool :: Vk.CommandPool
, frameMainCommandBuffer :: Vk.CommandBuffer
, frameCameraBuffer :: AllocatedBuffer
, frameSceneBuffer :: AllocatedBuffer
, frameObjectBuffer :: AllocatedBuffer
, frameIndirectBuffer :: AllocatedBuffer
, frameGlobalDescriptor :: Vk.DescriptorSet
, frameObjectDescriptor :: Vk.DescriptorSet
}
deriving (Show)
newtype GPUObjectData = GPUObjectData
{ objectModelMatrix :: M44 Float
}
undefinedGPUObjectData :: GPUObjectData
undefinedGPUObjectData = GPUObjectData identity
instance Storable GPUObjectData where
sizeOf (GPUObjectData _) = sizeOf (identity :: M44 Float)
alignment _ = 0
peek ptr = do
GPUObjectData <$> peek (castPtr ptr)
poke ptr (GPUObjectData modelMatrix) =
poke (castPtr ptr) $ transpose modelMatrix
data GPUCameraData = GPUCameraData
{ view :: M44 Float
, projection :: M44 Float
, viewProjection :: M44 Float
}
instance Storable GPUCameraData where
sizeOf _ =
3 * sizeOf (undefined :: M44 Float)
alignment _ = 0
peek ptr = do
v <- peek (castPtr ptr)
p <- peek (castPtr ptr `plusPtr` sizeOf v)
vp <- peek (castPtr ptr `plusPtr` sizeOf v `plusPtr` sizeOf p)
return $ GPUCameraData v p vp
poke ptr (GPUCameraData pview pprojection pviewProjection) = do
poke (castPtr ptr) $ transpose pview
poke (castPtr ptr `plusPtr` sizeOf pview) $ transpose pprojection
poke (castPtr ptr `plusPtr` sizeOf pview `plusPtr` sizeOf pprojection) $
transpose pviewProjection
data GPUSceneData = GPUSceneData
{ fogColor :: V4 Float
, fogDistance :: V4 Float
, ambientColor :: V4 Float
, sunDirection :: V4 Float
, sunColor :: V4 Float
}
deriving (Show)
undefinedGPUSceneData :: GPUSceneData
undefinedGPUSceneData = GPUSceneData
(V4 0 0 0 0)
(V4 0 0 0 0)
(V4 0 0 0 0)
(V4 0 0 0 0)
(V4 0 0 0 0)
instance Storable GPUSceneData where
sizeOf _ =
5 * sizeOf (V4 0 0 0 0 :: V4 Float)
alignment _ = 0
peek ptr = do
fogCol <- peek (castPtr ptr)
fogDst <- peek (castPtr $ ptr `plusPtr` sizeOf fogCol)
ambCol <- peek (castPtr $ ptr `plusPtr` (sizeOf fogCol + sizeOf fogDst))
sunDir <- peek (castPtr $ ptr `plusPtr` (sizeOf fogCol + sizeOf fogDst + sizeOf ambCol))
sunCol <- peek (castPtr $ ptr `plusPtr` (sizeOf fogCol + sizeOf fogDst + sizeOf ambCol + sizeOf sunDir))
return $ GPUSceneData fogCol fogDst ambCol sunDir sunCol
poke ptr (GPUSceneData fogCol fogDist ambCol sunDir sunCol) = do
poke (castPtr ptr) fogCol
poke (castPtr $ ptr `plusPtr` sizeOf fogCol) fogDist
poke (castPtr $ ptr `plusPtr` (sizeOf fogCol + sizeOf fogDist)) ambCol
poke (castPtr $ ptr `plusPtr` (sizeOf fogCol + sizeOf fogDist + sizeOf ambCol)) sunDir
poke (castPtr $ ptr `plusPtr` (sizeOf fogCol + sizeOf fogDist + sizeOf ambCol + sizeOf sunDir)) sunCol
data UploadContext = UploadContext
{ uploadFence :: Vk.Fence
, uploadCommandPool :: Vk.CommandPool
, uploadCommandBuffer :: Vk.CommandBuffer
}
deriving (Show)
data IndirectBatch = IndirectBatch
{ batchMesh :: Mesh
, batchMaterial :: Material
, batchFirst :: Word32
, batchCount :: Word32
}

110
src/Util.hs Normal file
View file

@ -0,0 +1,110 @@
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE DataKinds #-}
module Util where
import Data.Bits
import qualified Data.Vector as V
import Data.Word (Word32)
import qualified Vulkan as Vk
import qualified Vulkan.CStruct.Extends as Vk
import qualified Vulkan.Zero as Vk
-- internal imports
import Types
getFrame :: EngineData -> Int -> FrameData
getFrame engineData frame =
let frameVector = engineFrames engineData
in
frameVector V.! (frame `mod` V.length frameVector)
padUniformBufferSize
:: Vk.DeviceSize
-> Vk.PhysicalDeviceProperties
-> Vk.DeviceSize
padUniformBufferSize origSize gpuProperties =
let minUBOAlignment = Vk.minUniformBufferOffsetAlignment $ Vk.limits gpuProperties
result =
if minUBOAlignment > 0
then (origSize + minUBOAlignment - 1) .&. complement (minUBOAlignment - 1)
else origSize
in
-- trace ("padding result: " <> show result) result
result
descriptorsetLayoutBinding
:: Vk.DescriptorType
-> Vk.ShaderStageFlags
-> Word32
-> Vk.DescriptorSetLayoutBinding
descriptorsetLayoutBinding type' stageFlags binding =
Vk.zero
{ Vk.binding = binding
, Vk.descriptorCount = 1
, Vk.descriptorType = type'
, Vk.immutableSamplers = V.empty
, Vk.stageFlags = stageFlags
}
writeDescriptorBuffer
:: Vk.DescriptorType
-> Vk.DescriptorSet
-> Vk.DescriptorBufferInfo
-> Word32
-> Vk.SomeStruct Vk.WriteDescriptorSet
writeDescriptorBuffer type' dstSet dBufferInfo binding = Vk.SomeStruct $
Vk.zero
{ Vk.dstBinding = binding
, Vk.dstSet = dstSet
, Vk.descriptorCount = 1
, Vk.descriptorType = type'
, Vk.bufferInfo = V.singleton dBufferInfo
}
beginCommandBuffer
:: Vk.CommandBufferUsageFlags
-> Vk.CommandBufferBeginInfo '[]
beginCommandBuffer flags =
Vk.zero
{ Vk.flags = flags
}
createSubmitInfo
:: Vk.CommandBuffer
-> V.Vector (Vk.SomeStruct Vk.SubmitInfo)
createSubmitInfo cmd = V.singleton $ Vk.SomeStruct $
Vk.zero
{ Vk.commandBuffers = V.singleton (Vk.commandBufferHandle cmd)
}
samplerCreateInfo
:: Vk.Filter
-> Vk.SamplerAddressMode
-> Vk.SamplerCreateInfo '[]
samplerCreateInfo filters addressMode =
Vk.zero
{ Vk.magFilter = filters
, Vk.minFilter = filters
, Vk.addressModeU = addressMode
, Vk.addressModeV = addressMode
, Vk.addressModeW = addressMode
}
writeDescriptorImage
:: Vk.DescriptorType
-> Vk.DescriptorSet
-> Vk.DescriptorImageInfo
-> Word32
-> Vk.WriteDescriptorSet '[]
writeDescriptorImage type' set imageInfo binding =
Vk.zero
{ Vk.dstBinding = binding
, Vk.dstSet = set
, Vk.descriptorCount = 1
, Vk.descriptorType = type'
, Vk.imageInfo = V.singleton imageInfo
}

46
test.nix Normal file
View file

@ -0,0 +1,46 @@
{pkgs, vulkan-tutorial, ...}:
pkgs.nixosTest ({
name = "test";
nodes = {
machine = { config, pkgs, ...}: {
environment = {
systemPackages = with pkgs; [
vulkan-tutorial
git
shaderc
vulkan-loader
vulkan-validation-layers
];
sessionVariables = {
VK_LAYER_PATH="${pkgs.vulkan-validation-layers}/share/vulkan/explicit_layer.d";
SDL_VULKAN_LIBRARY="${pkgs.vulkan-loader}/lib/libvulkan.so";
};
};
hardware.opengl = {
enable = true;
driSupport = true;
driSupport32Bit = true;
};
services.xserver = {
enable = true;
displayManager.sddm.enable = true;
desktopManager.cinnamon.enable = true;
};
users.users.test = {
password = "test";
extraGroups = [
"wheel"
];
isNormalUser = true;
};
};
};
skipLint = true;
testScript = ''
start_all()
machine.shell_interact()
'';
})

View file

@ -34,25 +34,30 @@ executable vulkan-tutorial
Memory Memory
Mesh Mesh
Image Image
Sync
Textures
Types Types
Util
-- LANGUAGE extensions used by modules in this package. -- LANGUAGE extensions used by modules in this package.
-- other-extensions: -- other-extensions:
build-depends: base >=4.14.3.0 build-depends: base
, sdl2 , sdl2
, vulkan , vulkan
, vulkan-api
, vulkan-utils , vulkan-utils
, VulkanMemoryAllocator , VulkanMemoryAllocator
, wavefront , wavefront
, linear , linear
, monad-loops , monad-loops
, stm , stm
, time
, bytestring , bytestring
, text , text
, vector , vector
, resourcet , resourcet
, transformers , mtl
, containers
, JuicyPixels
hs-source-dirs: src hs-source-dirs: src
default-language: Haskell2010 default-language: Haskell2010
ghc-options: -Wall ghc-options: -Wall