"type": "coreclr",
"request": "launch",
"preLaunchTask": "build VulkanContext",
- "program": "${workspaceFolder}/build/Debug/netcoreapp3.1/VulkanContext",
+ "program": "${workspaceFolder}/build/Debug/net6/VulkanContext",
"args": [],
- "cwd": "${workspaceFolder}/build/Debug/netcoreapp3.1/",
+ "cwd": "${workspaceFolder}/build/Debug/net6/",
"stopAtEntry": false,
"console": "internalConsole",
"env": {
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build ClearScreen",
- "program": "${workspaceFolder}/build/Debug/netcoreapp3.1/ClearScreen",
+ "program": "${workspaceFolder}/build/Debug/net6/ClearScreen",
"args": [],
- "cwd": "${workspaceFolder}/build/Debug/netcoreapp3.1/",
+ "cwd": "${workspaceFolder}/build/Debug/net6/",
"stopAtEntry": false,
"console": "internalConsole",
"env": {
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build Triangle",
- "program": "${workspaceFolder}/build/Debug/netcoreapp3.1/Triangle",
+ "program": "${workspaceFolder}/build/Debug/net6/Triangle",
"args": [],
- "cwd": "${workspaceFolder}/build/Debug/netcoreapp3.1/",
+ "cwd": "${workspaceFolder}/build/Debug/net6/",
+ "stopAtEntry": false,
+ "console": "internalConsole"
+ },
+ {
+ "name": ".NET Core Launch (ShaderObject)",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build ShaderObject",
+ "program": "${workspaceFolder}/build/Debug/net6/ShaderObject",
+ "args": [],
+ "cwd": "${workspaceFolder}/build/Debug/net6/",
"stopAtEntry": false,
"console": "internalConsole"
},
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build FillTests",
- "program": "${workspaceFolder}/build/Debug/netcoreapp3.1/FillTests",
+ "program": "${workspaceFolder}/build/Debug/net6/FillTests",
"args": [],
- "cwd": "${workspaceFolder}/build/Debug/netcoreapp3.1/",
+ "cwd": "${workspaceFolder}/build/Debug/net6/",
"stopAtEntry": false,
"console": "internalConsole"
},
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build DynamicRendering",
- "program": "${workspaceFolder}/build/Debug/netcoreapp3.1/DynamicRendering",
+ "program": "${workspaceFolder}/build/Debug/net6/DynamicRendering",
"args": [],
- "cwd": "${workspaceFolder}/build/Debug/netcoreapp3.1/",
+ "cwd": "${workspaceFolder}/build/Debug/net6/",
"stopAtEntry": false,
"console": "internalConsole"
},
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build MeshShader",
- "program": "${workspaceFolder}/build/Debug/netcoreapp3.1/MeshShader",
+ "program": "${workspaceFolder}/build/Debug/net6/MeshShader",
"args": [],
- "cwd": "${workspaceFolder}/build/Debug/netcoreapp3.1/",
+ "cwd": "${workspaceFolder}/build/Debug/net6/",
"stopAtEntry": false,
"console": "internalConsole",
"env": {
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build Compute",
- "program": "${workspaceFolder}/build/Debug/netcoreapp3.1/compute",
+ "program": "${workspaceFolder}/build/Debug/net6/compute",
"args": [],
- "cwd": "${workspaceFolder}/build/Debug/netcoreapp3.1/",
+ "cwd": "${workspaceFolder}/build/Debug/net6/",
"stopAtEntry": false,
"console": "internalConsole",
"env": {
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build ExternalMemmories",
- "program": "${workspaceFolder}/build/Debug/netcoreapp3.1/ExternalMemmories",
+ "program": "${workspaceFolder}/build/Debug/net6/ExternalMemmories",
"args": [],
- "cwd": "${workspaceFolder}/build/Debug/netcoreapp3.1/",
+ "cwd": "${workspaceFolder}/build/Debug/net6/",
"stopAtEntry": false,
"console": "internalConsole"
},
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build Textured",
- "program": "${workspaceFolder}/build/Debug/netcoreapp3.1/Textured",
+ "program": "${workspaceFolder}/build/Debug/net6/Textured",
"args": [],
- "cwd": "${workspaceFolder}/build/Debug/netcoreapp3.1/",
+ "cwd": "${workspaceFolder}/build/Debug/net6/",
"stopAtEntry": false,
"console": "internalConsole"
},
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build pbr",
- "program": "${workspaceFolder}/build/Debug/netcoreapp3.1/pbr",
+ "program": "${workspaceFolder}/build/Debug/net6/pbr",
"args": [],
- "cwd": "${workspaceFolder}/build/Debug/netcoreapp3.1/",
+ "cwd": "${workspaceFolder}/build/Debug/net6/",
"stopAtEntry": false,
"console": "internalConsole"
},
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build deferred",
- "program": "${workspaceFolder}/build/Debug/netcoreapp3.1/deferred",
+ "program": "${workspaceFolder}/build/Debug/net6/deferred",
"args": [],
- "cwd": "${workspaceFolder}/build/Debug/netcoreapp3.1/",
+ "cwd": "${workspaceFolder}/build/Debug/net6/",
"stopAtEntry": false,
"console": "integratedTerminal"
},
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build DistanceFieldFontTest",
- "program": "${workspaceFolder}/build/Debug/netcoreapp3.1/DistanceFieldFontTest",
+ "program": "${workspaceFolder}/build/Debug/net6/DistanceFieldFontTest",
"args": [],
- "cwd": "${workspaceFolder}/build/Debug/netcoreapp3.1/",
+ "cwd": "${workspaceFolder}/build/Debug/net6/",
"console": "internalConsole",
"stopAtEntry": false
},
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build immutableSampler",
- "program": "${workspaceFolder}/build/Debug/netcoreapp3.1/ImmutableSampler",
+ "program": "${workspaceFolder}/build/Debug/net6/ImmutableSampler",
"args": [],
- "cwd": "${workspaceFolder}/build/Debug/netcoreapp3.1/",
+ "cwd": "${workspaceFolder}/build/Debug/net6/",
"console": "internalConsole",
"stopAtEntry": false
},
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build Multithreading",
- "program": "${workspaceFolder}/build/Debug/netcoreapp3.1/Multithreading",
+ "program": "${workspaceFolder}/build/Debug/net6/Multithreading",
"args": [],
- "cwd": "${workspaceFolder}/build/Debug/netcoreapp3.1/",
+ "cwd": "${workspaceFolder}/build/Debug/net6/",
"console": "internalConsole",
"stopAtEntry": false
},
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build Multithreading2",
- "program": "${workspaceFolder}/build/Debug/netcoreapp3.1/Multithreading2",
+ "program": "${workspaceFolder}/build/Debug/net6/Multithreading2",
"args": [],
- "cwd": "${workspaceFolder}/build/Debug/netcoreapp3.1/",
+ "cwd": "${workspaceFolder}/build/Debug/net6/",
"console": "internalConsole",
"stopAtEntry": false
},
],
"problemMatcher": "$msCompile"
},
+ {
+ "label": "build ShaderObject",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/samples/ShaderObject/ShaderObject.csproj",
+ "/property:GenerateFullPaths=true",
+ "/property:SolutionDir=${workspaceFolder}/",
+ "/property:Configuration=Debug",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
{
"label": "build FillTests",
"command": "dotnet",
get;
set;
}
+ /// <summary>
+ /// Optional, Specify the target spirv version. Default depends on TargetEnv
+ /// </summary>
+ public ITaskItem TargetSpirV {
+ get;
+ set;
+ }
+ /// <summary>
+ /// Optional, Specify the target client environment. (default=Vulkan1.0)
+ /// </summary>
+ public ITaskItem TargetEnv {
+ get;
+ set;
+ }
volatile bool success;
//due to includes mechanic, file inclusion position has to be recorded
}else
optimisationStr = "-O";
+ string targetSpirV = "";
+ if (!string.IsNullOrEmpty(TargetSpirV?.ItemSpec))
+ targetSpirV = $"--target-spv={TargetSpirV.ItemSpec}";
+ string targetEnv = "";
+ if (!string.IsNullOrEmpty(TargetEnv?.ItemSpec))
+ targetEnv = $"--target-env={TargetEnv.ItemSpec}";
+
Process glslc = new Process();
//glslc.StartInfo.StandardOutputEncoding = System.Text.Encoding.ASCII;
//glslc.StartInfo.StandardErrorEncoding = System.Text.Encoding.ASCII;
glslc.StartInfo.RedirectStandardOutput = true;
glslc.StartInfo.RedirectStandardError = true;
glslc.StartInfo.FileName = glslcPath;
- glslc.StartInfo.Arguments = $"\"{tempFile}\" -o \"{DestinationFile.ItemSpec}\" {macros.ToString()} {optimisationStr}";
+ glslc.StartInfo.Arguments = $"\"{tempFile}\" -o \"{DestinationFile.ItemSpec}\" {macros.ToString()} {optimisationStr} {targetEnv} {targetSpirV}";
glslc.StartInfo.CreateNoWindow = true;
glslc.EnableRaisingEvents = true;
<PackageVersion>$(SpirVTasksPackageVersion)</PackageVersion>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageRequireLicenseAcceptance>False</PackageRequireLicenseAcceptance>
- <PackageProjectUrl>https://github.com/jpbruyere/vk.net/blob/master/SpirVTasks/README.md</PackageProjectUrl>
+ <PackageProjectUrl>https://github.com/jpbruyere/vke.net/blob/master/SpirVTasks/README.md</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>icon.png</PackageIcon>
<License>MIT</License>
<AssemblyName>SpirVTasks_$(TargetFramework)</AssemblyName>
<RestoreIgnoreFailedSource>true</RestoreIgnoreFailedSource>
+
+ <PackageReleaseNotes>
+
+ </PackageReleaseNotes>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.0.461" PrivateAssets="all" />
<RootDirectory>$(MSBuildThisFileDirectory)../</RootDirectory>
<Deterministic>true</Deterministic>
- <TargetFrameworks>netcoreapp3.1</TargetFrameworks>
+ <TargetFrameworks>net6</TargetFrameworks>
<RepositoryUrl>https://github.com/jpbruyere/vke.net</RepositoryUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RootDirectory>$(MSBuildThisFileDirectory)../</RootDirectory>
<Deterministic>true</Deterministic>
- <TargetFrameworks>netcoreapp3.1</TargetFrameworks>
+ <TargetFrameworks>net6</TargetFrameworks>
<RepositoryUrl>https://github.com/jpbruyere/vke.net</RepositoryUrl>
<License>MIT</License>
namespace MeshShader {
class Program : SampleBase {
- public override string[] EnabledInstanceExtensions => new string[] {
+ public override string[] EnabledLayers => new string[] {
+ "VK_LAYER_KHRONOS_validation"
+ };
+ public override string[] EnabledInstanceExtensions => new string[] {
Ext.I.VK_KHR_get_physical_device_properties2,
- //Ext.I.VK_EXT_debug_utils,
+ Ext.I.VK_EXT_debug_utils,
};
public override string[] EnabledDeviceExtensions => new string[] {
Ext.D.VK_KHR_swapchain,
-#version 450
+#version 460
-#extension GL_NV_mesh_shader : require
+#extension GL_EXT_mesh_shader : require
layout(local_size_x = 1) in;
layout(triangles, max_vertices = 3, max_primitives = 1) out;
void main()
{
- gl_MeshVerticesNV[0].gl_Position = mvp * vertices[0];
- gl_MeshVerticesNV[1].gl_Position = mvp * vertices[1];
- gl_MeshVerticesNV[2].gl_Position = mvp * vertices[2];
+ gl_MeshVerticesEXT[0].gl_Position = mvp * vertices[0];
+ gl_MeshVerticesEXT[1].gl_Position = mvp * vertices[1];
+ gl_MeshVerticesEXT[2].gl_Position = mvp * vertices[2];
v_out[0].color = vec4(colors[0], 1.0);
v_out[1].color = vec4(colors[1], 1.0);
v_out[2].color = vec4(colors[2], 1.0);
- gl_PrimitiveIndicesNV[0] = 0;
- gl_PrimitiveIndicesNV[1] = 1;
- gl_PrimitiveIndicesNV[2] = 2;
-
- gl_PrimitiveCountNV = 1;
+ gl_PrimitiveTriangleIndicesEXT[0] = uvec3(0,1,2);
}
\ No newline at end of file
--- /dev/null
+# Shaders
+For this tutorials we'll need a `vertex` and a `fragment` shaders. Vulkan need them to be compiled into [SPIR-V](https://www.khronos.org/spir/). Install the [Vulkan Sdk](https://www.lunarg.com/vulkan-sdk/) and after building it, ensure the `VULKAN_SDK` environment variable points to its binary subdir.
+```bash
+export VULKAN_SDK=/VulkanSDK/1.2.176.1/x86_64
+```
+To enable automatic shader compilation during build, add the [SpirVTasks package](https://www.nuget.org/packages/SpirVTasks/) and a generic **GLSLShader** item globing a full directory.
+```xml
+<ItemGroup>
+ <PackageReference Include="SpirVTasks" />
+</ItemGroup>
+<ItemGroup>
+ <GLSLShader Include="shaders\*.*" />
+</ItemGroup>
+```
+See [SpirVTasks documentation](https://github.com/jpbruyere/vke.net/tree/master/SpirVTasks) for more informations.
+
+# Creating buffers
+
+Vke has two classes to handle buffers. Mappable [`HostBuffer`](../../../../wiki/vke.HostBuffer) and device only [`GPUBuffer`](../../../../wiki/vke.GPUBuffer).
+For this first simple example, we will only use host mappable buffers. Those classes can handle a Generic argument of a blittable type to handle arrays. Resources like buffers or images are activated in constructor, and they need to be explicitly disposed on cleanup. Create them in the `initVulkan` override.
+
+```csharp
+//the vertex buffer
+vbo = new HostBuffer<Vertex> (dev, VkBufferUsageFlags.VertexBuffer, vertices);
+//the index buffer
+ibo = new HostBuffer<ushort> (dev, VkBufferUsageFlags.IndexBuffer, indices);
+//a permanantly mapped buffer for the mvp matrice
+uboMats = new HostBuffer (dev, VkBufferUsageFlags.UniformBuffer, mvp, true);
+```
+
+To be able to access the mvp matrix in a shader, we need a descriptor. This implies to create a descriptor pool to allocate it from and configure the triangle pipeline layout with a corresponding descriptor layout for our matrix.
+```csharp
+descriptorPool = new DescriptorPool (dev, 1, new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer));
+```
+# Configuring pipelines
+
+Graphic pipeline configuration are predefined by the [`GraphicPipelineConfig`](../../../../wiki/vke.GraphicPipelineConfig) class, which ease sharing configs for several pipelines having lots in common. The pipeline layout will be automatically activated on pipeline creation, so that sharing layout among different pipelines will benefit from the reference counting to automatically dispose unused layout on pipeline clean up. It's the same for [`DescriptorSetLayout`](../../wiki/api/DescriptorSetLayout).
+```csharp
+using (GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (
+ VkPrimitiveTopology.TriangleList, VkSampleCountFlags.SampleCount1, false)) {
+
+ cfg.Layout = new PipelineLayout (dev,
+ new DescriptorSetLayout (dev,
+ new VkDescriptorSetLayoutBinding (
+ 0, VkShaderStageFlags.Vertex, VkDescriptorType.UniformBuffer)));
+```
+Next we configure a default [`RenderPass`](../../../../wiki/vke.RenderPass) with just a color attachment for the swap chain image, a default sub-pass is automatically created and the render pass activation will follow the pipeline life cycle and will be automatically disposed when no longer in use.
+```csharp
+ cfg.RenderPass = new RenderPass (dev, swapChain.ColorFormat, cfg.Samples);
+```
+Configuration of vertex bindings and attributes
+```csharp
+cfg.AddVertexBinding<Vertex> (0);
+cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat, //position
+ VkFormat.R32g32b32Sfloat);//color
+```
+# Adding the shaders
+Add both vertex and fragment shaders to the globbed directory of your `.csproj`
+
+##### triangle.vert
+```glsl
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec3 inPos;
+layout (location = 1) in vec3 inColor;
+
+layout (binding = 0) uniform UBO
+{
+ mat4 mvp;
+};
+
+layout (location = 0) out vec3 outColor;
+
+out gl_PerVertex
+{
+ vec4 gl_Position;
+};
+
+void main()
+{
+ outColor = inColor;
+ gl_Position = mvp * vec4(inPos.xyz, 1.0);
+}
+```
+##### triangle.frag
+```glsl
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec3 inColor;
+layout (location = 0) out vec4 outFragColor;
+
+void main()
+{
+ outFragColor = vec4(inColor, 1.0);
+}
+```
+
+Shaders will be compiled into spir-v automatically during build by the `SpirVTasks`. The resulting shaders will be embedded in the assembly. To specifiy that the shader path is a resource name, put the '**`#`**' prefix. Else the path will be search on disk.
+```csharp
+cfg.AddShader (dev, VkShaderStageFlags.Vertex, "#shaders.triangle.vert.spv");
+cfg.AddShader (dev, VkShaderStageFlags.Fragment, "#shaders.triangle.frag.spv");
+```
+Because native ShaderModule used during pipeline creation may be distroyed once the pipeline is created, The PipelineConfig class implement the
+'IDisposable' interface to release those pointers automaticaly.
+
+# Creating the pipeline
+Once the pipeline configuration is complete, we use it to effectively create and activate a new graphic pipeline. Activables used by the pipeline (like the RenderPass, or the PipelineLayout) are referenced in the newly created managed pipeline. So the Configuration object doesn't need cleanup.
+```csharp
+ pipeline = new GraphicPipeline (cfg);
+```
+# Descriptor allocation
+Because descriptor layouts used for a pipeline are only activated on pipeline activation, descriptor sets must not be allocated before, except if the layout has been manually activated, but in this case, layouts will also need to be explicitly disposed.
+```csharp
+ descriptorSet = descriptorPool.Allocate (pipeline.Layout.DescriptorSetLayouts[0]);
+```
+# Descriptor update
+
+The descriptor update is a two step operation. First we create a [`DescriptorSetWrites`](../../../../wiki/vke.DescriptorSetWrites) object defining the layout(s), than we write the descriptor(s).
+The `Descriptor` property of the mvp HostBuffer will return a default descriptor with no offset of the full size of the buffer.
+
+```csharp
+DescriptorSetWrites uboUpdate =
+ new DescriptorSetWrites (descriptorSet, pipeline.Layout.DescriptorSetLayouts[0]);
+
+uboUpdate.Write (dev, uboMats.Descriptor);
+```
+
+# Updating the view
+
+Override the `UpdateView` method of the `VkWindow` class to update view related stuff like matrices.
+
+```csharp
+public override void UpdateView () {
+ mvp = Matrix4x4.Create ...
+ uboMats.Update (mvp, (uint)Marshal.SizeOf<Matrix4x4> ());
+ base.UpdateView ();
+}
+```
+This method is called at least once before the rendering loop just after 'OnResize'.
+Then, it is triggered in the render loop each time the `updateViewRequested` field of `VkWindow` is set to 'true',
+don't forget to reset `updateViewRequested` to 'false' or call the `base.UpdateView()` which will reset this boolean.
+
+In a typical application, the mouse movements will set `updateViewRequested` to true.
+```csharp
+protected override void onMouseMove (double xPos, double yPos) {
+ updateViewRequested = true;
+```
--- /dev/null
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <IncludeDefaultNoneItems>false</IncludeDefaultNoneItems>
+ </PropertyGroup>
+</Project>
--- /dev/null
+// Copyright (c) 2023 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using vke;
+using Vulkan;
+using Glfw;
+using System.Collections.Generic;
+
+//the traditional triangle sample with shader object
+namespace ShaderObjectSample {
+ class Program : SampleBase {
+ public override string[] EnabledDeviceExtensions => new string[] {
+ Ext.D.VK_KHR_swapchain,
+ Ext.D.VK_EXT_shader_object
+ };
+ static void Main (string[] args) {
+#if DEBUG
+ Instance.VALIDATION = true;
+#endif
+ using (Program vke = new Program ()) {
+ vke.Run ();
+ }
+ }
+
+ const float rotSpeed = 0.01f, zoomSpeed = 0.01f;
+ float rotX, rotY, zoom = 1f;
+
+ //vertex structure
+ [StructLayout(LayoutKind.Sequential)]
+ struct Vertex {
+ Vector3 position;
+ Vector3 color;
+
+ public Vertex (float x, float y, float z, float r, float g, float b) {
+ position = new Vector3 (x, y, z);
+ color = new Vector3 (r, g, b);
+ }
+ }
+
+ HostBuffer ibo; //a host mappable buffer to hold the indices.
+ HostBuffer vbo; //a host mappable buffer to hold vertices.
+ HostBuffer<Matrix4x4> uboMVPmatrix; //a host mappable buffer for mvp matrice.
+
+ DescriptorPool descriptorPool;
+ DescriptorSet descriptorSet;//descriptor set for the mvp matrice.
+
+ FrameBuffers frameBuffers; //the frame buffer collection coupled to the swapchain images
+ GraphicPipeline pipeline; //the triangle rendering pipeline
+
+ //triangle vertices (position + color per vertex) and indices.
+ Vertex[] vertices = {
+ new Vertex (-1.0f, -1.0f, 0.0f , 1.0f, 0.0f, 0.0f),
+ new Vertex ( 1.0f, -1.0f, 0.0f , 0.0f, 1.0f, 0.0f),
+ new Vertex ( 0.0f, 1.0f, 0.0f , 0.0f, 0.0f, 1.0f),
+ };
+ ushort[] indices = new ushort[] { 0, 1, 2 };
+
+ protected override void initVulkan () {
+ base.initVulkan ();
+
+ DescriptorSetLayout dsLayout = new DescriptorSetLayout (dev, new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex, VkDescriptorType.UniformBuffer));
+ ShaderObject vertexShader = new ShaderObject(dev, VkShaderStageFlags.Vertex, VkShaderStageFlags.Fragment, dsLayout);
+ vertexShader.FileName = "#shaders.main.vert.spv";
+
+ vertexShader.Activate();
+
+
+ vbo = new HostBuffer<Vertex> (dev, VkBufferUsageFlags.VertexBuffer, vertices);
+ ibo = new HostBuffer<ushort> (dev, VkBufferUsageFlags.IndexBuffer, indices);
+
+ uboMVPmatrix = new HostBuffer<Matrix4x4> (dev, VkBufferUsageFlags.UniformBuffer, 1, true);
+
+ descriptorPool = new DescriptorPool (dev, 1, new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer));
+
+ /*
+ cfg.AddVertexBinding<Vertex> (0);
+ cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat, VkFormat.R32g32b32Sfloat);//position + color
+ cfg.AddShaders (
+ new ShaderInfo (dev, VkShaderStageFlags.Vertex, "#shaders.main.vert.spv"),
+ new ShaderInfo (dev, VkShaderStageFlags.Fragment, "#shaders.main.frag.spv")
+ );
+ */
+
+ descriptorSet = descriptorPool.Allocate (pipeline.Layout.DescriptorSetLayouts[0]);
+ DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descriptorSet, pipeline.Layout.DescriptorSetLayouts[0]);
+ uboUpdate.Write (dev, uboMVPmatrix.Descriptor);
+ cmds = cmdPool.AllocateCommandBuffer (swapChain.ImageCount);
+ }
+
+ //view update override, see base method for more informations.
+ public override void UpdateView () {
+ uboMVPmatrix.AsSpan()[0] =
+ Matrix4x4.CreateFromAxisAngle (Vector3.UnitY, rotY) *
+ Matrix4x4.CreateFromAxisAngle (Vector3.UnitX, rotX) *
+ Matrix4x4.CreateTranslation (0, 0, -3f * zoom) *
+ Helpers.CreatePerspectiveFieldOfView (Helpers.DegreesToRadians (45f), (float)swapChain.Width / (float)swapChain.Height, 0.1f, 256.0f);
+
+ base.UpdateView ();
+ }
+ protected override void onMouseMove (double xPos, double yPos) {
+ double diffX = lastMouseX - xPos;
+ double diffY = lastMouseY - yPos;
+ if (GetButton (MouseButton.Left) == InputAction.Press) {
+ rotY -= rotSpeed * (float)diffX;
+ rotX += rotSpeed * (float)diffY;
+ updateViewRequested = true;
+ } else if (GetButton (MouseButton.Right) == InputAction.Press) {
+ zoom += zoomSpeed * (float)diffY;
+ updateViewRequested = true;
+ }
+ }
+
+ void buildCommandBuffers() {
+ cmdPool.Reset (VkCommandPoolResetFlags.ReleaseResources);
+
+ for (int i = 0; i < swapChain.ImageCount; ++i) {
+ FrameBuffer fb = frameBuffers[i];
+ cmds[i].Start ();
+
+ pipeline.RenderPass.Begin (cmds[i], fb);
+
+ cmds[i].SetViewport (swapChain.Width, swapChain.Height);
+ cmds[i].SetScissor (swapChain.Width, swapChain.Height);
+
+ cmds[i].BindDescriptorSet (pipeline.Layout, descriptorSet);
+
+ cmds[i].BindPipeline (pipeline);
+
+ cmds[i].BindVertexBuffer (vbo);
+ cmds[i].BindIndexBuffer (ibo, VkIndexType.Uint16);
+ cmds[i].DrawIndexed ((uint)indices.Length);
+
+ pipeline.RenderPass.End (cmds[i]);
+
+ cmds[i].End ();
+ }
+ }
+
+ protected override void OnResize () {
+ base.OnResize ();
+
+ updateViewRequested = true;
+
+ frameBuffers?.Dispose();
+ frameBuffers = pipeline.RenderPass.CreateFrameBuffers(swapChain);
+
+ buildCommandBuffers ();
+ }
+ //clean up
+ protected override void Dispose (bool disposing) {
+ dev.WaitIdle ();
+ if (disposing) {
+ if (!isDisposed) {
+ //pipeline clean up will dispose PipelineLayout, DescriptorSet layouts and render pass automatically. If their reference count is zero, their handles will be destroyed.
+ pipeline.Dispose ();
+ //frame buffers are automatically activated on creation as for resources, so it requests an explicit call to dispose.
+ frameBuffers?.Dispose();
+ //the descriptor pool
+ descriptorPool.Dispose ();
+ //resources have to be explicityly disposed.
+ vbo.Dispose ();
+ ibo.Dispose ();
+ uboMVPmatrix.Dispose ();
+ }
+ }
+
+ base.Dispose (disposing);
+ }
+ }
+}
--- /dev/null
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec3 inColor;
+layout (location = 0) out vec4 outFragColor;
+
+void main()
+{
+ outFragColor = vec4(inColor, 1.0);
+}
\ No newline at end of file
--- /dev/null
+#version 450
+
+#extension GL_ARB_separate_shader_objects : enable
+#extension GL_ARB_shading_language_420pack : enable
+
+layout (location = 0) in vec3 inPos;
+layout (location = 1) in vec3 inColor;
+
+layout (binding = 0) uniform UBO
+{
+ mat4 mvp;
+};
+
+layout (location = 0) out vec3 outColor;
+
+out gl_PerVertex
+{
+ vec4 gl_Position;
+};
+
+
+void main()
+{
+ outColor = inColor;
+ gl_Position = mvp * vec4(inPos.xyz, 1.0);
+}
class Program : SampleBase {
static void Main (string[] args) {
#if DEBUG
- //Instance.VALIDATION = true;
+ Instance.VALIDATION = true;
#endif
using (Program vke = new Program ()) {
vke.Run ();
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MeshShader", "samples\MeshShader\MeshShader.csproj", "{5787D082-7E4E-4B18-9C69-529CBC4F105E}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DynamicRendering", "samples\DynamicRendering\DynamicRendering.csproj", "{3A0E5765-911E-4451-9715-ED55095D6C39}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicRendering", "samples\DynamicRendering\DynamicRendering.csproj", "{3A0E5765-911E-4451-9715-ED55095D6C39}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FillTests", "samples\FillTests\FillTests.csproj", "{273C88AE-D095-4CF9-8D6F-D7ABE3E9C8D3}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FillTests", "samples\FillTests\FillTests.csproj", "{273C88AE-D095-4CF9-8D6F-D7ABE3E9C8D3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShaderObject", "samples\ShaderObject\ShaderObject.csproj", "{1B63D92E-7C54-41DA-9BCA-AC0C53F3D371}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
ReleaseSpirVTasks|Any CPU = ReleaseSpirVTasks|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {7B05B5A7-49E2-4D05-BEF8-734F70CDBF17}.BuildPackages|Any CPU.ActiveCfg = Release|Any CPU
- {7B05B5A7-49E2-4D05-BEF8-734F70CDBF17}.BuildPackages|Any CPU.Build.0 = Release|Any CPU
- {7B05B5A7-49E2-4D05-BEF8-734F70CDBF17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7B05B5A7-49E2-4D05-BEF8-734F70CDBF17}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7B05B5A7-49E2-4D05-BEF8-734F70CDBF17}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7B05B5A7-49E2-4D05-BEF8-734F70CDBF17}.Release|Any CPU.Build.0 = Release|Any CPU
{7B05B5A7-49E2-4D05-BEF8-734F70CDBF17}.ReleaseSpirVTasks|Any CPU.ActiveCfg = Release|Any CPU
{7B05B5A7-49E2-4D05-BEF8-734F70CDBF17}.ReleaseSpirVTasks|Any CPU.Build.0 = Release|Any CPU
{642726F4-0592-4846-8EAF-A5D1964C85A7}.BuildPackages|Any CPU.ActiveCfg = Release|Any CPU
{5787D082-7E4E-4B18-9C69-529CBC4F105E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5787D082-7E4E-4B18-9C69-529CBC4F105E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5787D082-7E4E-4B18-9C69-529CBC4F105E}.Release|Any CPU.Build.0 = Release|Any CPU
- {3A0E5765-911E-4451-9715-ED55095D6C39}.BuildPackages|Any CPU.ActiveCfg = Debug|Any CPU
- {3A0E5765-911E-4451-9715-ED55095D6C39}.BuildPackages|Any CPU.Build.0 = Debug|Any CPU
{3A0E5765-911E-4451-9715-ED55095D6C39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3A0E5765-911E-4451-9715-ED55095D6C39}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3A0E5765-911E-4451-9715-ED55095D6C39}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3A0E5765-911E-4451-9715-ED55095D6C39}.Release|Any CPU.Build.0 = Release|Any CPU
- {3A0E5765-911E-4451-9715-ED55095D6C39}.ReleaseSpirVTasks|Any CPU.ActiveCfg = Debug|Any CPU
- {3A0E5765-911E-4451-9715-ED55095D6C39}.ReleaseSpirVTasks|Any CPU.Build.0 = Debug|Any CPU
- {273C88AE-D095-4CF9-8D6F-D7ABE3E9C8D3}.BuildPackages|Any CPU.ActiveCfg = Debug|Any CPU
- {273C88AE-D095-4CF9-8D6F-D7ABE3E9C8D3}.BuildPackages|Any CPU.Build.0 = Debug|Any CPU
{273C88AE-D095-4CF9-8D6F-D7ABE3E9C8D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{273C88AE-D095-4CF9-8D6F-D7ABE3E9C8D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{273C88AE-D095-4CF9-8D6F-D7ABE3E9C8D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{273C88AE-D095-4CF9-8D6F-D7ABE3E9C8D3}.Release|Any CPU.Build.0 = Release|Any CPU
- {273C88AE-D095-4CF9-8D6F-D7ABE3E9C8D3}.ReleaseSpirVTasks|Any CPU.ActiveCfg = Debug|Any CPU
- {273C88AE-D095-4CF9-8D6F-D7ABE3E9C8D3}.ReleaseSpirVTasks|Any CPU.Build.0 = Debug|Any CPU
+ {1B63D92E-7C54-41DA-9BCA-AC0C53F3D371}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1B63D92E-7C54-41DA-9BCA-AC0C53F3D371}.Debug|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
{5787D082-7E4E-4B18-9C69-529CBC4F105E} = {16439374-B8DB-4643-8116-EB3358B49A12}
{3A0E5765-911E-4451-9715-ED55095D6C39} = {16439374-B8DB-4643-8116-EB3358B49A12}
{273C88AE-D095-4CF9-8D6F-D7ABE3E9C8D3} = {16439374-B8DB-4643-8116-EB3358B49A12}
+ {1B63D92E-7C54-41DA-9BCA-AC0C53F3D371} = {16439374-B8DB-4643-8116-EB3358B49A12}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1360F94D-CF3C-4121-A8D7-E227F41668F1}
using Vulkan;
namespace vke {
- public static partial class Helpers {
- /// <summary>Throw an erro if VkResult != Success.</summary>
+ public static partial class Helpers {
static void xmlMakeTypeFieldsAsAttributes (Type t, ref XmlAttributeOverrides overrides)
{
foreach (FieldInfo fi in t.GetFields (BindingFlags.Public | BindingFlags.Instance))
drawFence.Wait ();
drawFence.Reset ();
- presentQueue.Submit (cmds[idx], swapChain.presentComplete, drawComplete[idx], drawFence);
+ presentQueue.Submit (cmds[idx], swapChain.nextImgAvail, drawComplete[idx], drawFence);
presentQueue.Present (swapChain, drawComplete[idx]);
+ swapChain.semaphoresQueue.Enqueue(swapChain.nextImgAvail);
//presentQueue.WaitIdle ();
}
--- /dev/null
+// Copyright (c) 2023 Jean-Philippe Bruyère <jp_bruyere@hotmail.com>
+//
+// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using Vulkan;
+using static Vulkan.Vk;
+using static Vulkan.Utils;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace vke {
+ /// <summary>
+ /// Collection of FrameBuffers, useful to handle multiple framebuffers for a swapchain.
+ /// </summary>
+ public class ShaderObjects : Collection<ShaderObject>, IDisposable
+ {
+ //public Framebuffer this[int index] => Items[index];
+
+ public void Dispose()
+ {
+ foreach (ShaderObject fb in Items)
+ fb.Dispose();
+ ClearItems();
+ }
+ }
+ public sealed class ShaderObject : Activable {
+ internal VkShaderEXT handle;
+ public VkShaderEXT Handle => handle;
+ public string FileName;
+ public VkShaderStageFlags Stage;
+ public VkShaderStageFlags NextStage;
+ public VkShaderCreateFlagsEXT Flags = VkShaderCreateFlagsEXT.LinkStageEXT;
+
+ public List<DescriptorSetLayout> DescriptorSetLayouts = new List<DescriptorSetLayout> ();
+ public List<VkPushConstantRange> PushConstantRanges = new List<VkPushConstantRange> ();
+
+ protected override VkDebugUtilsObjectNameInfoEXT DebugUtilsInfo
+ => new VkDebugUtilsObjectNameInfoEXT (VkObjectType.ShaderEXT, handle.Handle);
+
+ #region CTORS
+ public ShaderObject (Device device, VkShaderStageFlags stage, VkShaderStageFlags nextStage) : base (device) {
+ Stage = stage;
+ NextStage = nextStage;
+ }
+ public ShaderObject (Device device, VkShaderStageFlags stage, VkShaderStageFlags nextStage, VkPushConstantRange pushConstantRange, params DescriptorSetLayout[] descriptorSetLayouts)
+ : this (device, stage, nextStage, descriptorSetLayouts) {
+ PushConstantRanges.Add (pushConstantRange);
+ }
+ public ShaderObject (Device device, VkShaderStageFlags stage, VkShaderStageFlags nextStage, VkPushConstantRange[] pushConstantRanges, params DescriptorSetLayout[] descriptorSetLayouts)
+ : this (device, stage, nextStage, descriptorSetLayouts) {
+ foreach (VkPushConstantRange pcr in pushConstantRanges)
+ PushConstantRanges.Add (pcr);
+ }
+ public ShaderObject (Device device, VkShaderStageFlags stage, VkShaderStageFlags nextStage, params DescriptorSetLayout[] descriptorSetLayouts)
+ :this (device, stage, nextStage) {
+
+ if (descriptorSetLayouts.Length > 0)
+ DescriptorSetLayouts.AddRange (descriptorSetLayouts);
+ }
+ #endregion
+
+ public void AddPushConstants (params VkPushConstantRange[] pushConstantRanges) {
+ foreach (VkPushConstantRange pcr in pushConstantRanges)
+ PushConstantRanges.Add (pcr);
+ }
+
+ uint[] getCode(out UIntPtr shaderSize) {
+ using (Stream stream = Helpers.GetStreamFromPath (FileName)) {
+ using (BinaryReader br = new BinaryReader (stream)) {
+ byte[] shaderCode = br.ReadBytes ((int)stream.Length);
+ shaderSize = (UIntPtr)shaderCode.Length;
+ ReadOnlySpan<uint> tmp = MemoryMarshal.Cast<byte, uint> (shaderCode);
+ return tmp.ToArray();
+ }
+ }
+ }
+
+ public override void Activate () {
+ if (state != ActivableState.Activated) {
+ foreach (DescriptorSetLayout dsl in DescriptorSetLayouts)
+ dsl.Activate ();
+
+
+ VkShaderCreateInfoEXT info = default;
+ info.flags = VkShaderCreateFlagsEXT.LinkStageEXT;
+ info.stage = VkShaderStageFlags.Vertex;
+ info.stage = VkShaderStageFlags.Fragment;
+ info.codeType = VkShaderCodeTypeEXT.SpirvEXT;
+
+ uint[] code = getCode(out UIntPtr codeSize);
+ info.codeSize = codeSize;
+ info.pCode = code.PinPointer();
+
+ VkDescriptorSetLayout[] dsls = DescriptorSetLayouts.Select (dsl => dsl.handle).ToArray ();
+
+ if (dsls.Length > 0) {
+ info.pSetLayouts = dsls;
+ }
+ if (PushConstantRanges.Count > 0) {
+ info.pPushConstantRanges = PushConstantRanges;
+ }
+ CheckResult (vkCreateShadersEXT (Dev.Handle, 1, ref info, IntPtr.Zero, out handle));
+
+ code.Unpin();
+ info.Dispose();
+
+ }
+ base.Activate ();
+ }
+
+ public override string ToString () {
+ return string.Format ($"{base.ToString ()}[0x{handle.Handle.ToString("x")}]");
+ }
+
+ #region IDisposable Support
+ protected override void Dispose (bool disposing) {
+ if (state == ActivableState.Activated) {
+ if (disposing) {
+ foreach (DescriptorSetLayout dsl in DescriptorSetLayouts)
+ dsl.Dispose ();
+ } else
+ System.Diagnostics.Debug.WriteLine ("VKE Activable ShaderObject disposed by finalizer");
+
+ vkDestroyShaderEXT (Dev.Handle, handle, IntPtr.Zero);
+ }else if (disposing)
+ System.Diagnostics.Debug.WriteLine ("Calling dispose on unactive ShaderObject");
+
+ base.Dispose (disposing);
+ }
+ #endregion
+ }
+}
using Vulkan;
using static Vulkan.Vk;
using static Vulkan.Utils;
+using System.Collections.Generic;
namespace vke {
/// <summary>
VkSwapchainCreateInfoKHR createInfos;
PresentQueue presentQueue;
- public VkSemaphore presentComplete;
+ public Queue<VkSemaphore> semaphoresQueue = new Queue<VkSemaphore>();
+ public VkSemaphore nextImgAvail;
public Image[] images;
protected override VkDebugUtilsObjectNameInfoEXT DebugUtilsInfo
if (Handle.Handle != 0)
_destroy ();
- presentComplete = Dev.CreateSemaphore ();
- presentComplete.SetDebugMarkerName (Dev, "Semaphore PresentComplete");
Handle = newSwapChain;
CheckResult (vkGetSwapchainImagesKHR (Dev.Handle, Handle, out uint imageCount, IntPtr.Zero));
- if (imageCount == 0)
+ if (imageCount == 0)
throw new Exception ("Swapchain image count is 0.");
VkImage[] imgs = new VkImage[imageCount];
CheckResult (vkGetSwapchainImagesKHR (Dev.Handle, Handle, out imageCount, imgs.Pin ()));
images[i].CreateView ();
images[i].SetName ("SwapChain Img" + i);
images[i].Descriptor.imageView.SetDebugMarkerName (Dev, "SwapChain Img" + i + " view");
+
+
+ nextImgAvail = Dev.CreateSemaphore ();
+ nextImgAvail.SetDebugMarkerName (Dev, $"Semaphore " + i + " Img Acquired");
+
+ semaphoresQueue.Enqueue(nextImgAvail);
}
}
/// <summary>
/// <returns>Swapchain image index or -1 if failed</returns>
/// <param name="fence">an optional fence to signal.</param>
public int GetNextImage (Fence fence = null) {
- VkResult res = vkAcquireNextImageKHR (Dev.Handle, Handle, UInt64.MaxValue, presentComplete, fence, out currentImageIndex);
+ nextImgAvail = semaphoresQueue.Dequeue();
+ VkResult res = vkAcquireNextImageKHR (Dev.Handle, Handle, UInt64.MaxValue, nextImgAvail, fence, out currentImageIndex);
if (res == VkResult.ErrorOutOfDateKHR || res == VkResult.SuboptimalKHR) {
Create ();
return -1;
}
void _destroy () {
- for (int i = 0; i < ImageCount; i++)
+ for (int i = 0; i < ImageCount; i++) {
images[i].Dispose ();
+ }
+ if (semaphoresQueue.Count < ImageCount)
+ Dev.DestroySemaphore (nextImgAvail);
+ while(semaphoresQueue.Count > 0)
+ Dev.DestroySemaphore (semaphoresQueue.Dequeue());
vkDestroySwapchainKHR (Dev.Handle, Handle, IntPtr.Zero);
- Dev.DestroySemaphore (presentComplete);
}
public override string ToString () {
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
- <TargetFramework>netcoreapp3.1</TargetFramework>
+ <TargetFramework>netstandard2.1</TargetFramework>
<ReleaseVersion>$(VkeReleaseVersion)</ReleaseVersion>
<ItemGroup>
<PackageReference Include="SpirVTasks" Version="$(SpirVTasksPackageVersion)" />
- <PackageReference Include="Vulkan" Version="0.5.1" />
+ <PackageReference Include="Vulkan" Version="0.5.4" />
<PackageReference Include="shaderc.net" Version="0.1.0" />
- <PackageReference Include="glfw-sharp" Version="0.2.14" />
+ <PackageReference Include="glfw-sharp" Version="0.2.15" />
</ItemGroup>
<ItemGroup>