From: Jean-Philippe Bruyère Date: Sat, 9 May 2020 10:48:37 +0000 (+0200) Subject: glfw win ptr public in vkCrowWin, save sample tests, use cmd base intead of primary... X-Git-Tag: v0.1.21~6 X-Git-Url: https://git.osiis.dedyn.io/?a=commitdiff_plain;h=65d7f337865e1a42e78770d5d64996a6bff1da22;p=jp%2Fvke.net.git glfw win ptr public in vkCrowWin, save sample tests, use cmd base intead of primary where possible --- diff --git a/Directory.Build.props b/Directory.Build.props index 5654285..e4fbc00 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,7 +4,7 @@ $(SolutionDir)build\$(Configuration)\ 0.1.44 $(SpirVTasksReleaseVersion)-beta - 0.1.16 + 0.1.18 $(VkeReleaseVersion)-beta 7.2 diff --git a/samples/crowWin/FSQPipeline.cs b/samples/crowWin/FSQPipeline.cs new file mode 100644 index 0000000..99c5654 --- /dev/null +++ b/samples/crowWin/FSQPipeline.cs @@ -0,0 +1,41 @@ +// Copyright (c) 2019 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using System.Numerics; +using vke; +using Vulkan; + +namespace vke { + + public class FSQPipeline : GraphicPipeline { + public FSQPipeline (RenderPass renderPass, PipelineLayout pipelineLayout, int attachment = 0, PipelineCache pipelineCache = null) + : base (renderPass, pipelineCache, "FSQ pipeline") { + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, this.RenderPass.Samples, false); + cfg.RenderPass = RenderPass; + cfg.Layout = pipelineLayout; + cfg.AddShader (Dev, VkShaderStageFlags.Vertex, "#vke.FullScreenQuad.vert.spv"); + cfg.AddShader (Dev, VkShaderStageFlags.Fragment, "#shaders.simpletexture.frag.spv"); + cfg.multisampleState.rasterizationSamples = Samples; + + cfg.blendAttachments[attachment] = new VkPipelineColorBlendAttachmentState (true); + + layout = cfg.Layout; + + init (cfg); + + cfg.DisposeShaders (); + } + + public virtual void RecordDraw (CommandBuffer cmd) { + Bind (cmd); + cmd.Draw (3, 1, 0, 0); + } + + protected override void Dispose (bool disposing) { + base.Dispose (disposing); + } + } + +} diff --git a/samples/crowWin/ObservablePipelineConfig.cs b/samples/crowWin/ObservablePipelineConfig.cs new file mode 100644 index 0000000..0a2c557 --- /dev/null +++ b/samples/crowWin/ObservablePipelineConfig.cs @@ -0,0 +1,105 @@ +// Copyright (c) 2019 Bruyère Jean-Philippe jp_bruyere@hotmail.com +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using Crow; +using vke; +using Vulkan; + +namespace vkeEditor { + public class ObservablePipelineConfig : GraphicPipelineConfig, IValueChange { + #region IValueChange implementation + public event EventHandler ValueChanged; + public virtual void NotifyValueChanged (string MemberName, object _value) + { + ValueChanged?.Invoke (this, new Crow.ValueChangeEventArgs (MemberName, _value)); + } + #endregion + + + public ObservablePipelineConfig () + { + Viewports = new ObservableList (); + Scissors = new ObservableList (); + blendAttachments = new ObservableList (); + dynamicStates = new ObservableList (); + vertexBindings = new ObservableList (); + vertexAttributes = new ObservableList (); + Shaders = new ObservableList (); + } + + bool isExpanded; + public bool IsExpanded { + get => isExpanded; + set { + if (isExpanded == value) + return; + isExpanded = value; + NotifyValueChanged ("IsExpanded", isExpanded); + } + } + + public string FrontGroupBoxCaption => + FrontAndBackDifferent ? "Front Stencil State" : "Font and Back Stencil States"; + + bool frontAndBackDifferent; + //front and back stencil state idem + public bool FrontAndBackDifferent { + get => frontAndBackDifferent; + set { + if (frontAndBackDifferent == value) + return; + frontAndBackDifferent = value; + NotifyValueChanged ("FrontAndBackDifferent", frontAndBackDifferent); + NotifyValueChanged ("FrontGroupBoxCaption", FrontGroupBoxCaption); + } + } + public VkStencilOpState FrontStencil { + get => depthStencilState.front; + set { + depthStencilState.front = value; + if (!frontAndBackDifferent) + BackStencil = value; + NotifyValueChanged ("FrontStencil", depthStencilState.front); + } + } + public VkStencilOpState BackStencil { + get => depthStencilState.back; + set { + depthStencilState.back = value; + NotifyValueChanged ("BackStencil", depthStencilState.back); + } + } + + #region BlendAttachments + public ObservableList ObsViewports + =>Viewports as ObservableList; + public ObservableList ObsScissors + => Scissors as ObservableList; + public ObservableList ObsBlendAttachments + => blendAttachments as ObservableList; + public ObservableList ObsDynamicStates + => dynamicStates as ObservableList; + public ObservableList ObsVertexBindings + => vertexBindings as ObservableList; + public ObservableList ObsVertexAttributes + => vertexAttributes as ObservableList; + public ObservableList ObsShaders + => Shaders as ObservableList; + + public void onEditBlendAttachmentClick (object sender, MouseEventArgs e) + { + /*Widget w = VkWindow.CurWin.loadWindow ("ui/editBlendAttachment.crow", this); + w.FindByName ("OkButton").MouseClick += (s, ev) => ObsBlendAttachments.RaiseEdit (); */ + } + public void onEditVertexAttributesClick (object sender, MouseEventArgs e) + { + /*Widget w = VkCrowWindow.CurWin.loadWindow ("ui/editVertexAttributes.crow", this); + w.FindByName ("OkButton").MouseClick += (s, ev) => ObsVertexAttributes.RaiseEdit ();*/ + } + + #endregion + + + } +} diff --git a/samples/crowWin/Program.cs b/samples/crowWin/Program.cs new file mode 100644 index 0000000..dd493fd --- /dev/null +++ b/samples/crowWin/Program.cs @@ -0,0 +1,212 @@ +// Copyright (c) 2019 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using System.Threading; +using Glfw; +using vke; +using Vulkan; + +namespace vkeEditor { + public class Program : CrowWindow { + static void Main (string [] args) + { +#if DEBUG + Instance.VALIDATION = true; + //Instance.RENDER_DOC_CAPTURE = true; +#endif + + using (Program vke = new Program ()) { + vke.Run (); + } + } + + float rotSpeed = 0.01f, zoomSpeed = 0.01f; + float rotX, rotY, rotZ = 0f, zoom = 1f; + + struct Matrices { + public Matrix4x4 projection; + public Matrix4x4 view; + public Matrix4x4 model; + } + 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); + } + } + + + Matrices matrices; + + HostBuffer ibo; + HostBuffer vbo; + HostBuffer uboMats; + + public DescriptorPool descriptorPool; + DescriptorSetLayout dsLayout; + DescriptorSet descriptorSet; + + FrameBuffers frameBuffers; + GraphicPipeline pipeline; + + 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 }; + + + string source; + + public string Source { + get => Source; + set { + if (source == value) + return; + source = value; + NotifyValueChanged ("Source", source); + } + } + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, VkSampleCountFlags.SampleCount1, true); + + Program () : base () {} + + protected override void initVulkan () { + base.initVulkan (); + + cmds = cmdPool.AllocateCommandBuffer (swapChain.ImageCount); + + vbo = new HostBuffer (dev, VkBufferUsageFlags.VertexBuffer, vertices); + ibo = new HostBuffer (dev, VkBufferUsageFlags.IndexBuffer, indices); + uboMats = new HostBuffer (dev, VkBufferUsageFlags.UniformBuffer, matrices); + + descriptorPool = new DescriptorPool (dev, 1, + new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer)); + + dsLayout = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex, VkDescriptorType.UniformBuffer) + ); + + cfg.Layout = new PipelineLayout (dev, dsLayout); + cfg.RenderPass = renderPass; + cfg.AddVertexBinding (0); + cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat, VkFormat.R32g32b32Sfloat); + + using (shaderc.Compiler comp = new shaderc.Compiler ()) { + cfg.AddShaders (comp.CreateShaderInfo (dev, "shaders/main.vert", shaderc.ShaderKind.VertexShader)); + cfg.AddShaders (comp.CreateShaderInfo (dev, "shaders/main.frag", shaderc.ShaderKind.FragmentShader)); + } + + pipeline = new GraphicPipeline (cfg); + + cfg.DisposeShaders (); + //note that descriptor set is allocated after the pipeline creation that use this layout, layout is activated + //automaticaly on pipeline creation, and will be disposed automatically when no longuer in use. + descriptorSet = descriptorPool.Allocate (dsLayout); + + DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descriptorSet, dsLayout.Bindings[0]); + uboUpdate.Write (dev, uboMats.Descriptor); + + uboMats.Map (); + + UpdateFrequency = 20; + + loadWindow ("#ui.testImage.crow", this); + + } + protected override void OnResize () { + base.OnResize (); + + frameBuffers?.Dispose (); + frameBuffers = pipeline.RenderPass.CreateFrameBuffers (swapChain); + + buildCommandBuffers (); + } + + + void buildCommandBuffers () { + cmdPool.Reset (); + 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); + + RecordDraw (cmds[i]); + + pipeline.RenderPass.End (cmds[i]); + + cmds[i].End (); + } + } + + public override void UpdateView () + { + matrices.projection = Matrix4x4.CreatePerspectiveFieldOfView (Utils.DegreesToRadians (45f), + (float)swapChain.Width / (float)swapChain.Height, 0.1f, 256.0f) * Camera.VKProjectionCorrection; + matrices.view = + Matrix4x4.CreateFromAxisAngle (Vector3.UnitZ, rotZ) * + Matrix4x4.CreateFromAxisAngle (Vector3.UnitY, rotY) * + Matrix4x4.CreateFromAxisAngle (Vector3.UnitX, rotX) * + Matrix4x4.CreateTranslation (0, 0, -3f * zoom); + matrices.model = Matrix4x4.Identity; + uboMats.Update (matrices, (uint)Marshal.SizeOf ()); + updateViewRequested = false; + } + + protected override void onMouseMove (double xPos, double yPos) + { + base.onMouseMove (xPos, yPos); + if (MouseIsInInterface) + return; + + double diffX = lastMouseX - xPos; + double diffY = lastMouseY - yPos; + if (MouseButton [0]) { + rotY -= rotSpeed * (float)diffX; + rotX += rotSpeed * (float)diffY; + } else if (MouseButton [1]) { + zoom += zoomSpeed * (float)diffY; + } else + return; + updateViewRequested = true; + } + + + protected override void Dispose (bool disposing) + { + dev.WaitIdle (); + if (disposing) { + if (!isDisposed) { + pipeline.Dispose (); + + frameBuffers?.Dispose (); + descriptorPool.Dispose (); + vbo.Dispose (); + ibo.Dispose (); + uboMats.Dispose (); + } + } + base.Dispose (disposing); + } + } +} diff --git a/samples/crowWin/VkCrowWindow.cs b/samples/crowWin/VkCrowWindow.cs new file mode 100644 index 0000000..7f71778 --- /dev/null +++ b/samples/crowWin/VkCrowWindow.cs @@ -0,0 +1,248 @@ +// Copyright (c) 2019 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using Glfw; +using vke; +using Vulkan; +using Crow; +using System.Threading; + +namespace vke { + /// + /// Vulkan context with Crow enabled window. + /// Crow vector drawing is handled with Cairo Image on an Host mapped vulkan image. + /// This is an easy way to have GUI in my samples with low GPU cost. Most of the ui + /// is cached on cpu memory images. + /// + public class CrowWindow : VkWindow, IValueChange { + #region IValueChange implementation + public event EventHandler ValueChanged; + public virtual void NotifyValueChanged (string MemberName, object _value) + { + ValueChanged?.Invoke (this, new ValueChangeEventArgs (MemberName, _value)); + } + #endregion + + public bool MouseIsInInterface => + iFace.HoverWidget != null; + + FSQPipeline fsqPl; + DescriptorPool dsPool; + DescriptorSet descSet; + CommandPool cmdPoolCrow; + PrimaryCommandBuffer cmdUpdateCrow; + Image crowImage; + HostBuffer crowBuffer; + Interface iFace; + protected RenderPass renderPass; + + volatile bool running; + + + VkDescriptorSetLayoutBinding dslBinding = new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler); + + protected override void initVulkan () { + base.initVulkan (); + + iFace = new Interface ((int)Width, (int)Height, WindowHandle); + iFace.Init (); + + CreateRenderPass (); + + fsqPl = new FSQPipeline (renderPass, + new PipelineLayout (dev, new DescriptorSetLayout (dev, dslBinding))); + + cmdPoolCrow = new CommandPool (presentQueue, VkCommandPoolCreateFlags.ResetCommandBuffer); + cmdUpdateCrow = cmdPoolCrow.AllocateCommandBuffer (); + + dsPool = new DescriptorPool (dev, 1, new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler)); + descSet = dsPool.Allocate (fsqPl.Layout.DescriptorSetLayouts[0]); + + Thread ui = new Thread (crowThread); + ui.IsBackground = true; + ui.Start (); + } + protected virtual void CreateRenderPass () { + renderPass = new RenderPass (dev, swapChain.ColorFormat, dev.GetSuitableDepthFormat (), VkSampleCountFlags.SampleCount1); + } + + + protected override void onMouseMove (double xPos, double yPos) + { + if (iFace.OnMouseMove ((int)xPos, (int)yPos)) + return; + base.onMouseMove (xPos, yPos); + } + protected override void onMouseButtonDown (MouseButton button) { + if (iFace.OnMouseButtonDown (button)) + return; + base.onMouseButtonDown (button); + } + protected override void onMouseButtonUp (MouseButton button) + { + if (iFace.OnMouseButtonUp (button)) + return; + base.onMouseButtonUp (button); + } + protected override void onChar (CodePoint cp) { + if (iFace.OnKeyPress (cp.ToChar())) + return; + base.onChar (cp); + } + protected override void onKeyUp (Key key, int scanCode, Modifier modifiers) { + if (iFace.OnKeyUp (key)) + return; + base.onKeyUp (key, scanCode, modifiers); + } + protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) { + if (iFace.OnKeyDown (key)) + return; + base.onKeyDown (key, scanCode, modifiers); + } + + protected override void render () { + + int idx = swapChain.GetNextImage (); + if (idx < 0) { + OnResize (); + return; + } + + if (cmds[idx] == null) + return; + + drawFence.Wait (); + drawFence.Reset (); + + if (Monitor.IsEntered (iFace.UpdateMutex)) + Monitor.Exit (iFace.UpdateMutex); + + presentQueue.Submit (cmds[idx], swapChain.presentComplete, drawComplete[idx], drawFence); + presentQueue.Present (swapChain, drawComplete[idx]); + } + + public override void Update () { + if (iFace.IsDirty) { + drawFence.Wait (); + drawFence.Reset (); + Monitor.Enter (iFace.UpdateMutex); + presentQueue.Submit (cmdUpdateCrow, default, default, drawFence); + iFace.IsDirty = false; + } + + NotifyValueChanged ("fps", fps); + } + + protected override void OnResize () + { + base.OnResize (); + dev.WaitIdle (); + initCrowSurface (); + iFace.ProcessResize (new Crow.Rectangle (0, 0, (int)Width, (int)Height)); + } + + public void RecordDraw (CommandBuffer cmd) { + //fsqPl.Bind (cmd); + //cmd.BindDescriptorSet() + fsqPl.BindDescriptorSet (cmd, descSet, 0); + fsqPl.RecordDraw (cmd); + } + + void crowThread () { + while (iFace.surf == null) { + Thread.Sleep (10); + } + running = true; + while (running) { + iFace.Update (); + Thread.Sleep (10); + } + } + void initCrowSurface () { + lock (iFace.UpdateMutex) { + iFace.surf?.Dispose (); + crowImage?.Dispose (); + crowBuffer?.Dispose (); + + crowBuffer = new HostBuffer (dev, VkBufferUsageFlags.TransferSrc | VkBufferUsageFlags.TransferDst, Width * Height * 4, true); + + crowImage = new Image (dev, VkFormat.B8g8r8a8Unorm, VkImageUsageFlags.Sampled | VkImageUsageFlags.TransferDst, + VkMemoryPropertyFlags.DeviceLocal, Width, Height, VkImageType.Image2D, VkSampleCountFlags.SampleCount1, VkImageTiling.Linear); + crowImage.CreateView (VkImageViewType.ImageView2D, VkImageAspectFlags.Color); + crowImage.CreateSampler (VkFilter.Nearest, VkFilter.Nearest, VkSamplerMipmapMode.Nearest, VkSamplerAddressMode.ClampToBorder); + crowImage.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + + DescriptorSetWrites dsw = new DescriptorSetWrites (descSet, dslBinding); + dsw.Write (dev, crowImage.Descriptor); + + iFace.surf = new Crow.Cairo.ImageSurface (crowBuffer.MappedData, Crow.Cairo.Format.ARGB32, + (int)Width, (int)Height, (int)Width * 4); + + PrimaryCommandBuffer cmd = cmdPoolCrow.AllocateAndStart (VkCommandBufferUsageFlags.OneTimeSubmit); + crowImage.SetLayout (cmd, VkImageAspectFlags.Color, VkImageLayout.Preinitialized, VkImageLayout.ShaderReadOnlyOptimal); + presentQueue.EndSubmitAndWait (cmd, true); + + recordUpdateCrowCmd (); + } + } + + /// + /// command buffer must have been reseted + /// + void recordUpdateCrowCmd () { + cmdPoolCrow.Reset (); + cmdUpdateCrow.Start (); + crowImage.SetLayout (cmdUpdateCrow, VkImageAspectFlags.Color, + VkImageLayout.ShaderReadOnlyOptimal, VkImageLayout.TransferDstOptimal, + VkPipelineStageFlags.FragmentShader, VkPipelineStageFlags.Transfer); + + crowBuffer.CopyTo (cmdUpdateCrow, crowImage, VkImageLayout.ShaderReadOnlyOptimal); + + crowImage.SetLayout (cmdUpdateCrow, VkImageAspectFlags.Color, + VkImageLayout.TransferDstOptimal, VkImageLayout.ShaderReadOnlyOptimal, + VkPipelineStageFlags.Transfer, VkPipelineStageFlags.FragmentShader); + cmdUpdateCrow.End (); + } + + + protected void loadWindow (string path, object dataSource = null) { + try { + Widget w = iFace.FindByName (path); + if (w != null) { + iFace.PutOnTop (w); + return; + } + w = iFace.Load (path); + w.Name = path; + w.DataSource = dataSource; + + } catch (Exception ex) { + System.Diagnostics.Debug.WriteLine (ex.ToString ()); + } + } + protected void closeWindow (string path) { + Widget g = iFace.FindByName (path); + if (g != null) + iFace.DeleteWidget (g); + } + + + protected override void Dispose (bool disposing) { + dev.WaitIdle (); + + running = false; + fsqPl.Dispose (); + dsPool.Dispose (); + cmdPoolCrow.Dispose (); + crowImage?.Dispose (); + crowBuffer?.Dispose (); + iFace.Dispose (); + + base.Dispose (disposing); + } + + } +} diff --git a/samples/crowWin/VkFormatWidget.cs b/samples/crowWin/VkFormatWidget.cs new file mode 100644 index 0000000..2351155 --- /dev/null +++ b/samples/crowWin/VkFormatWidget.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2020 Jean-Philippe Bruyère +// +// This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) +using System; +using Crow; +using Crow.Cairo; +using Vulkan; + +namespace vkeEditor { + public class VkFormatWidget : Widget { + public VkFormatWidget () { + } + + VkFormat format; + public VkFormat Format { + get => format; + set { + if (format == value) + return; + format = value; + NotifyValueChanged ("Format", format); + RegisterForGraphicUpdate (); + } + } + + protected override void onDraw (Context gr) { + base.onDraw (gr); + + Rectangle r = ClientRectangle; + + + } + } +} diff --git a/samples/crowWin/crowWin.csproj b/samples/crowWin/crowWin.csproj new file mode 100644 index 0000000..04611c0 --- /dev/null +++ b/samples/crowWin/crowWin.csproj @@ -0,0 +1,14 @@ + + + false + + + + + + + + PreserveNewest + + + \ No newline at end of file diff --git a/samples/crowWin/shaders/main.frag b/samples/crowWin/shaders/main.frag new file mode 100644 index 0000000..4e79d2e --- /dev/null +++ b/samples/crowWin/shaders/main.frag @@ -0,0 +1,9 @@ +#version 450 + +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 diff --git a/samples/crowWin/shaders/main.vert b/samples/crowWin/shaders/main.vert new file mode 100644 index 0000000..fd9e54e --- /dev/null +++ b/samples/crowWin/shaders/main.vert @@ -0,0 +1,31 @@ +#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 projectionMatrix; + mat4 viewMatrix; + mat4 modelMatrix; +} ubo; + +layout (location = 0) out vec3 outColor; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + + +void main() +{ + outColor = inColor; + gl_Position = ubo.projectionMatrix * ubo.viewMatrix * ubo.modelMatrix * vec4(inPos.xyz, 1.0); + //gl_Position.y = -gl_Position.y; + //gl_Position.z = (gl_Position.z + gl_Position.w) / 2.0; + +} \ No newline at end of file diff --git a/samples/crowWin/shaders/simpletexture.frag b/samples/crowWin/shaders/simpletexture.frag new file mode 100644 index 0000000..792e208 --- /dev/null +++ b/samples/crowWin/shaders/simpletexture.frag @@ -0,0 +1,15 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +layout (set = 0, binding = 0) uniform sampler2D samplerColor; + +layout (location = 0) in vec2 inUV; + +layout (location = 0) out vec4 outFragColor; + +void main() +{ + outFragColor = texture(samplerColor, inUV); +} diff --git a/samples/crowWin/ui/main.crow b/samples/crowWin/ui/main.crow new file mode 100755 index 0000000..00b39d6 --- /dev/null +++ b/samples/crowWin/ui/main.crow @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/crowWin/ui/testImage.crow b/samples/crowWin/ui/testImage.crow new file mode 100644 index 0000000..79ad12f --- /dev/null +++ b/samples/crowWin/ui/testImage.crow @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/samples/shadow/DebuDrawPipeline.cs b/samples/shadow/DebuDrawPipeline.cs new file mode 100644 index 0000000..fef2751 --- /dev/null +++ b/samples/shadow/DebuDrawPipeline.cs @@ -0,0 +1,78 @@ +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using VK; + +namespace CVKL { + public class DebugDrawPipeline : GraphicPipeline { + public HostBuffer Vertices; + uint vertexCount; + uint vboLength = 100 * 6 * sizeof (float); + + public DebugDrawPipeline (Device dev, DescriptorSetLayout dsLayout, VkFormat colorFormat, VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1) : + base (new RenderPass (dev, colorFormat), "Debug draw pipeline") { + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.LineList, samples); + cfg.rasterizationState.lineWidth = 1.0f; + cfg.RenderPass = RenderPass; + cfg.Layout = new PipelineLayout(dev, dsLayout); + cfg.Layout.AddPushConstants ( + new VkPushConstantRange (VkShaderStageFlags.Vertex, (uint)Marshal.SizeOf () * 2) + ); + cfg.AddVertexBinding (0, 6 * sizeof(float)); + cfg.SetVertexAttributes (0, VkFormat.R32g32b32Sfloat, VkFormat.R32g32b32Sfloat); + + cfg.blendAttachments[0] = new VkPipelineColorBlendAttachmentState (true); + + cfg.AddShader (VkShaderStageFlags.Vertex, "shaders/debug.vert.spv"); + cfg.AddShader (VkShaderStageFlags.Fragment, "shaders/debug.frag.spv"); + + layout = cfg.Layout; + + init (cfg); + + Vertices = new HostBuffer (dev, VkBufferUsageFlags.VertexBuffer, vboLength); + Vertices.Map (); + } + + public void AddLine (Vector3 start, Vector3 end, float r, float g, float b) { + float[] data = { + start.X, start.Y, start.Z, + r, g, b, + end.X, end.Y, end.Z, + r, g, b + }; + Vertices.Update (data, 12 * sizeof (float), vertexCount * 6 * sizeof (float)); + vertexCount+=2; + } + + public void RecordDraw (CommandBuffer cmd, Framebuffer fb, Camera camera) { + RenderPass.Begin (cmd, fb); + const int ratio = 8; + cmd.SetViewport (fb.Width/ratio, fb.Height/ratio, (ratio-1) * (int)fb.Width / ratio, (ratio-1) * (int)fb.Height / ratio); + //cmd.SetViewport (200, 200,100,100,-10,10);//, 4 * (int)fb.Width / 5, 4 * (int)fb.Height / 5); + cmd.SetScissor (fb.Width / ratio, fb.Height / ratio, (ratio-1) * (int)fb.Width / ratio, (ratio-1) * (int)fb.Height / ratio); + //cmd.SetScissor (200, 200,100,100); + + Matrix4x4 ortho = Matrix4x4.CreateOrthographic (4, 4.0f / camera.AspectRatio,-1,1); + + cmd.PushConstant (layout, VkShaderStageFlags.Vertex, ortho); + + Bind (cmd); + + cmd.BindVertexBuffer (Vertices); + cmd.Draw (vertexCount); + RenderPass.End (cmd); + } + + protected override void Dispose (bool disposing) { + if (disposing) { + Vertices.Unmap (); + Vertices.Dispose (); + } + + base.Dispose (disposing); + } + } + +} diff --git a/samples/shadow/DeferredPbrRenderer.cs b/samples/shadow/DeferredPbrRenderer.cs new file mode 100644 index 0000000..0668628 --- /dev/null +++ b/samples/shadow/DeferredPbrRenderer.cs @@ -0,0 +1,511 @@ +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using vke; +using vke.Environment; +using vke.glTF; +using Vulkan; + +namespace deferred { + public class DeferredPbrRenderer : IDisposable { + Device dev; + Queue gQueue; + public static int MAX_MATERIAL_COUNT = 4; + public static VkSampleCountFlags NUM_SAMPLES = VkSampleCountFlags.SampleCount1; + public static VkFormat HDR_FORMAT = VkFormat.R16g16b16a16Sfloat; + public static VkFormat MRT_FORMAT = VkFormat.R32g32b32a32Sfloat; + public static bool TEXTURE_ARRAY; + + public enum DebugView { + none, + color, + normal, + pos, + occlusion, + emissive, + metallic, + roughness, + depth, + prefill, + irradiance, + shadowMap + } + public DebugView currentDebugView = DebugView.none; + public int lightNumDebug = 0; + public int debugMip = 0; + public int debugFace = 0; + + const float lightMoveSpeed = 0.1f; + public float exposure = 2.0f; + public float gamma = 1.2f; + + public struct Matrices { + public Matrix4x4 projection; + public Matrix4x4 model; + public Matrix4x4 view; + public Vector4 camPos; + public float prefilteredCubeMipLevels; + public float scaleIBLAmbient; + } + public struct Light { + public Vector4 position; + public Vector4 color; + public Matrix4x4 mvp; + } + + public Matrices matrices = new Matrices { + scaleIBLAmbient = 1.0f, + }; + public Light[] lights = { + new Light { + position = new Vector4(1.5f,2.5f,1.5f,0f), + color = new Vector4(1,1.0f,1.0f,1) + }, + new Light { + position = new Vector4(-1.5f,2.5f,1.5f,0f), + color = new Vector4(0.8f,0.8f,1,1) + } + }; + + FrameBuffer frameBuffer; + public Image gbColorRough, gbEmitMetal, gbN_AO, gbPos, hdrImgResolved; + + DescriptorPool descriptorPool; + DescriptorSetLayout descLayoutMain, descLayoutTextures, descLayoutGBuff; + DescriptorSet dsMain, dsGBuff; + + public PipelineCache pipelineCache; + Pipeline gBuffPipeline, composePipeline, debugPipeline; + + public HostBuffer uboMatrices { get; private set; } + public HostBuffer uboLights { get; private set; } + + RenderPass renderPass; + + public PbrModel model { get; private set; } + public EnvironmentCube envCube; + public ShadowMapRenderer shadowMapRenderer; + + public BoundingBox modelAABB; + + public VkSemaphore DrawComplete; + + const int SP_SKYBOX = 0; + const int SP_MODELS = 1; + const int SP_COMPOSE = 2; + //const int SP_TONE_MAPPING = 3; + + string cubemapPath; + + uint width, height; + public uint Width => width; + public uint Height => height; + + public DeferredPbrRenderer (Queue gQueue, string cubemapPath, uint width, uint height, float nearPlane, float farPlane) { + this.gQueue = gQueue; + this.dev = gQueue.Dev; + this.cubemapPath = cubemapPath; + this.width = width; + this.height = height; + + DrawComplete = dev.CreateSemaphore(); + + pipelineCache = new PipelineCache (dev); + + descriptorPool = new DescriptorPool (dev, 5, + new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer, 3), + new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 6), + new VkDescriptorPoolSize (VkDescriptorType.InputAttachment, 5), + new VkDescriptorPoolSize (VkDescriptorType.StorageImage, 4) + ); + + uboMatrices = new HostBuffer (dev, VkBufferUsageFlags.UniformBuffer, matrices, true); + uboLights = new HostBuffer (dev, VkBufferUsageFlags.UniformBuffer, lights, true); + +#if WITH_SHADOWS + shadowMapRenderer = new ShadowMapRenderer (gQueue, this); +#endif + + init (nearPlane, farPlane); + } + + void init_renderpass () { + renderPass = new RenderPass (dev, NUM_SAMPLES); + + renderPass.AddAttachment (HDR_FORMAT, VkImageLayout.ShaderReadOnlyOptimal, NUM_SAMPLES);//final outpout + renderPass.AddAttachment (dev.GetSuitableDepthFormat (), VkImageLayout.DepthStencilAttachmentOptimal, NUM_SAMPLES); + renderPass.AddAttachment (VkFormat.R8g8b8a8Unorm, VkImageLayout.ColorAttachmentOptimal, NUM_SAMPLES, VkAttachmentLoadOp.Clear, VkAttachmentStoreOp.DontCare);//GBuff0 (color + roughness) and final color before resolve + renderPass.AddAttachment (VkFormat.R8g8b8a8Unorm, VkImageLayout.ColorAttachmentOptimal, NUM_SAMPLES, VkAttachmentLoadOp.Clear, VkAttachmentStoreOp.DontCare);//GBuff1 (emit + metal) + renderPass.AddAttachment (MRT_FORMAT, VkImageLayout.ColorAttachmentOptimal, NUM_SAMPLES, VkAttachmentLoadOp.Clear, VkAttachmentStoreOp.DontCare);//GBuff2 (normals + AO) + renderPass.AddAttachment (MRT_FORMAT, VkImageLayout.ColorAttachmentOptimal, NUM_SAMPLES, VkAttachmentLoadOp.Clear, VkAttachmentStoreOp.DontCare);//GBuff3 (Pos + depth) + + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { depthStencil = new VkClearDepthStencilValue (1.0f, 0) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + + SubPass[] subpass = { new SubPass (), new SubPass (), new SubPass ()}; + //skybox + subpass[SP_SKYBOX].AddColorReference (0, VkImageLayout.ColorAttachmentOptimal); + //models + subpass[SP_MODELS].AddColorReference (new VkAttachmentReference (2, VkImageLayout.ColorAttachmentOptimal), + new VkAttachmentReference (3, VkImageLayout.ColorAttachmentOptimal), + new VkAttachmentReference (4, VkImageLayout.ColorAttachmentOptimal), + new VkAttachmentReference (5, VkImageLayout.ColorAttachmentOptimal)); + subpass[SP_MODELS].SetDepthReference (1, VkImageLayout.DepthStencilAttachmentOptimal); + subpass[SP_MODELS].AddPreservedReference (0); + + //compose + subpass[SP_COMPOSE].AddColorReference (0, VkImageLayout.ColorAttachmentOptimal); + subpass[SP_COMPOSE].AddInputReference (new VkAttachmentReference (2, VkImageLayout.ShaderReadOnlyOptimal), + new VkAttachmentReference (3, VkImageLayout.ShaderReadOnlyOptimal), + new VkAttachmentReference (4, VkImageLayout.ShaderReadOnlyOptimal), + new VkAttachmentReference (5, VkImageLayout.ShaderReadOnlyOptimal)); + //tone mapping + //subpass[SP_TONE_MAPPING].AddColorReference ((NUM_SAMPLES == VkSampleCountFlags.SampleCount1) ? 0u : 2u, VkImageLayout.ColorAttachmentOptimal); + //subpass[SP_TONE_MAPPING].AddInputReference (new VkAttachmentReference (6, VkImageLayout.ShaderReadOnlyOptimal)); + //if (NUM_SAMPLES != VkSampleCountFlags.SampleCount1) + //subpass[SP_TONE_MAPPING].AddResolveReference (0, VkImageLayout.ColorAttachmentOptimal); + + renderPass.AddSubpass (subpass); + + renderPass.AddDependency (Vk.SubpassExternal, SP_SKYBOX, + VkPipelineStageFlags.BottomOfPipe, VkPipelineStageFlags.ColorAttachmentOutput, + VkAccessFlags.MemoryRead, VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite); + renderPass.AddDependency (SP_SKYBOX, SP_MODELS, + VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.FragmentShader, + VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.ShaderRead); + renderPass.AddDependency (SP_MODELS, SP_COMPOSE, + VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.FragmentShader, + VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.ShaderRead); + //renderPass.AddDependency (SP_COMPOSE, Vk.SubpassExternal, + //VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.Transfer, + //VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.TransferRead); + //renderPass.AddDependency (SP_COMPOSE, SP_COMPOSE, + //VkPipelineStageFlags.Transfer, VkPipelineStageFlags.ComputeShader, + //VkAccessFlags.TransferWrite, VkAccessFlags.ShaderRead); + //renderPass.AddDependency (Vk.SubpassExternal, SP_TONE_MAPPING, + // VkPipelineStageFlags.ComputeShader, VkPipelineStageFlags.FragmentShader, + // VkAccessFlags.ShaderWrite, VkAccessFlags.ShaderRead); + //renderPass.AddDependency (SP_SKYBOX, SP_TONE_MAPPING, + //VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.FragmentShader, + //VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.ShaderRead); + renderPass.AddDependency (SP_COMPOSE, Vk.SubpassExternal, + VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.Transfer, + VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.TransferRead); + //renderPass.AddDependency (SP_TONE_MAPPING, Vk.SubpassExternal, + //VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.BottomOfPipe, + //VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.MemoryRead); + } + + void init (float nearPlane, float farPlane) { + init_renderpass (); + + descLayoutMain = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer),//matrices and params + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (4, VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer),//lights + new VkDescriptorSetLayoutBinding (5, VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer));//materials +#if WITH_SHADOWS + descLayoutMain.Bindings.Add (new VkDescriptorSetLayoutBinding (6, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler)); +#endif + + if (TEXTURE_ARRAY) { + descLayoutMain.Bindings.Add (new VkDescriptorSetLayoutBinding (7, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler));//texture array + //descLayoutMain.Bindings.Add (new VkDescriptorSetLayoutBinding (8, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler));//down sampled hdr + } else { + descLayoutTextures = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (4, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler) + ); + } + + descLayoutGBuff = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment),//color + roughness + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment),//emit + metal + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment),//normals + AO + new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment));//Pos + depth + + + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, NUM_SAMPLES); + cfg.rasterizationState.cullMode = VkCullModeFlags.Back; + if (NUM_SAMPLES != VkSampleCountFlags.SampleCount1) { + cfg.multisampleState.sampleShadingEnable = true; + cfg.multisampleState.minSampleShading = 0.5f; + } + cfg.Cache = pipelineCache; + if (TEXTURE_ARRAY) + cfg.Layout = new PipelineLayout (dev, descLayoutMain, descLayoutGBuff); + else + cfg.Layout = new PipelineLayout (dev, descLayoutMain, descLayoutGBuff, descLayoutTextures); + + cfg.Layout.AddPushConstants ( + new VkPushConstantRange (VkShaderStageFlags.Vertex, (uint)Marshal.SizeOf ()), + new VkPushConstantRange (VkShaderStageFlags.Fragment, sizeof (int), 64) + ); + cfg.RenderPass = renderPass; + cfg.SubpassIndex = SP_MODELS; + cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false)); + cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false)); + cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false)); + //cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false)); + + cfg.AddVertex (); + + using (SpecializationInfo constants = new SpecializationInfo ( + new SpecializationConstant (0, nearPlane), + new SpecializationConstant (1, farPlane), + new SpecializationConstant (2, MAX_MATERIAL_COUNT))) { + + cfg.AddShaders (new ShaderInfo (dev, VkShaderStageFlags.Vertex, "#shaders.GBuffPbr.vert.spv")); + if (TEXTURE_ARRAY) + cfg.AddShaders (new ShaderInfo (dev, VkShaderStageFlags.Fragment, "#shaders.GBuffPbrTexArray.frag.spv", constants)); + else + cfg.AddShaders (new ShaderInfo (dev, VkShaderStageFlags.Fragment, "#shaders.GBuffPbr.frag.spv", constants)); + + gBuffPipeline = new GraphicPipeline (cfg); + } + cfg.rasterizationState.cullMode = VkCullModeFlags.Front; + //COMPOSE PIPELINE + cfg.blendAttachments.Clear (); + cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false)); + cfg.ResetShadersAndVerticesInfos (); + cfg.SubpassIndex = SP_COMPOSE; + cfg.Layout = gBuffPipeline.Layout; + cfg.depthStencilState.depthTestEnable = false; + cfg.depthStencilState.depthWriteEnable = false; + using (SpecializationInfo constants = new SpecializationInfo ( + new SpecializationConstant (0, (uint)lights.Length))) { + cfg.AddShaders (new ShaderInfo (dev, VkShaderStageFlags.Vertex, "#vke.FullScreenQuad.vert.spv")); +#if WITH_SHADOWS + cfg.AddShaders (new ShaderInfo (dev, VkShaderStageFlags.Fragment, "#shaders.compose_with_shadows.frag.spv", constants)); +#else + cfg.AddShader (VkShaderStageFlags.Fragment, "#shaders.compose.frag.spv", constants); +#endif + composePipeline = new GraphicPipeline (cfg); + } + cfg.Shaders[1].Dispose (); + //DEBUG DRAW use subpass of compose + cfg.Shaders[1] = new ShaderInfo (dev, VkShaderStageFlags.Fragment, "#shaders.show_gbuff.frag.spv"); + cfg.SubpassIndex = SP_COMPOSE; + debugPipeline = new GraphicPipeline (cfg); + cfg.DisposeShaders (); + ////TONE MAPPING + //cfg.shaders[1] = new ShaderInfo (VkShaderStageFlags.Fragment, "#shaders.tone_mapping.frag.spv"); + //cfg.SubpassIndex = SP_TONE_MAPPING; + //toneMappingPipeline = new GraphicPipeline (cfg); + + dsMain = descriptorPool.Allocate (descLayoutMain); + dsGBuff = descriptorPool.Allocate (descLayoutGBuff); + + envCube = new EnvironmentCube (cubemapPath, gBuffPipeline.Layout, gQueue, renderPass); + + matrices.prefilteredCubeMipLevels = envCube.prefilterCube.CreateInfo.mipLevels; + + DescriptorSetWrites dsMainWrite = new DescriptorSetWrites (dsMain, descLayoutMain.Bindings.GetRange (0, 5).ToArray ()); + dsMainWrite.Write (dev, + uboMatrices.Descriptor, + envCube.irradianceCube.Descriptor, + envCube.prefilterCube.Descriptor, + envCube.lutBrdf.Descriptor, + uboLights.Descriptor); + +#if WITH_SHADOWS + dsMainWrite = new DescriptorSetWrites (dsMain, descLayoutMain.Bindings[6]); + dsMainWrite.Write (dev, shadowMapRenderer.shadowMap.Descriptor); +#endif + } + + + public void LoadModel (Queue transferQ, string path) { + dev.WaitIdle (); + model?.Dispose (); + + if (TEXTURE_ARRAY) { + PbrModelTexArray mod = new PbrModelTexArray (transferQ, path); + if (mod.texArray != null) { + DescriptorSetWrites uboUpdate = new DescriptorSetWrites (dsMain, descLayoutMain.Bindings[5], descLayoutMain.Bindings[7]); + uboUpdate.Write (dev, mod.materialUBO.Descriptor, mod.texArray.Descriptor); + } + model = mod; + } else { + model = new PbrModelSeparatedTextures (transferQ, path, + descLayoutTextures, + AttachmentType.Color, + AttachmentType.PhysicalProps, + AttachmentType.Normal, + AttachmentType.AmbientOcclusion, + AttachmentType.Emissive); + + DescriptorSetWrites uboUpdate = new DescriptorSetWrites (dsMain, descLayoutMain.Bindings[5]); + uboUpdate.Write (dev, model.materialUBO.Descriptor); + } + + + modelAABB = model.DefaultScene.AABB; + } + public void buildCommandBuffers (PrimaryCommandBuffer cmd) { + + + renderPass.Begin (cmd, frameBuffer); + + cmd.SetViewport (frameBuffer.Width, frameBuffer.Height); + cmd.SetScissor (frameBuffer.Width, frameBuffer.Height); + + cmd.BindDescriptorSet (gBuffPipeline.Layout, dsMain); + + envCube.RecordDraw (cmd); + + renderPass.BeginSubPass (cmd); + + if (model != null) { + gBuffPipeline.Bind (cmd); + model.Bind (cmd); + model.DrawAll (cmd, gBuffPipeline.Layout); + } + + renderPass.BeginSubPass (cmd); + + //cmd.BindDescriptorSet (composePipeline.Layout, dsMain); + cmd.BindDescriptorSet (composePipeline.Layout, dsGBuff, 1); + + if (currentDebugView == DebugView.none) + composePipeline.Bind (cmd); + else { + debugPipeline.Bind (cmd); + uint debugValue = (uint)currentDebugView - 1; + if (currentDebugView == DebugView.shadowMap) + debugValue += (uint)((lightNumDebug << 8)); + else + debugValue += (uint)((debugFace << 8) + (debugMip << 16)); + cmd.PushConstant (debugPipeline.Layout, VkShaderStageFlags.Fragment, debugValue, (uint)Marshal.SizeOf ()); + } + + cmd.Draw (3, 1, 0, 0); + + //renderPass.BeginSubPass (cmd); + //toneMappingPipeline.Bind (cmd); + //cmd.Draw (3, 1, 0, 0); + + renderPass.End (cmd); + } + + public void MoveLight (Vector4 dir) { + lights[lightNumDebug].position += dir * lightMoveSpeed; +#if WITH_SHADOWS + shadowMapRenderer.updateShadowMap = true; +#endif + } + + #region update + public void UpdateView (Camera camera) { + camera.AspectRatio = (float)width / height; + + matrices.projection = camera.Projection; + //matrices.projection = Utils.CreatePerspectiveFieldOfView (Utils.DegreesToRadians (60), 1, 0.1f, 16f); + matrices.view = camera.View; + //matrices.view = Matrix4x4.CreateLookAt (lights[0].position.ToVector3 (), Vector3.Zero, Vector3.UnitY); + matrices.model = camera.Model; + + Matrix4x4.Invert (camera.View, out Matrix4x4 inv); + matrices.camPos = new Vector4 (inv.M41, inv.M42, inv.M43, 0); + + uboMatrices.Update (matrices, (uint)Marshal.SizeOf ()); + } + + #endregion + + + void createGBuff () { + gbColorRough?.Dispose (); + gbEmitMetal?.Dispose (); + gbN_AO?.Dispose (); + gbPos?.Dispose (); + hdrImgResolved?.Dispose (); + + + hdrImgResolved = new Image (dev, HDR_FORMAT, VkImageUsageFlags.Sampled | VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransferSrc, VkMemoryPropertyFlags.DeviceLocal, width, height, VkImageType.Image2D, NUM_SAMPLES); + gbColorRough = new Image (dev, VkFormat.R8g8b8a8Unorm, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransientAttachment, VkMemoryPropertyFlags.DeviceLocal, width, height, VkImageType.Image2D, NUM_SAMPLES); + gbEmitMetal = new Image (dev, VkFormat.R8g8b8a8Unorm, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransientAttachment, VkMemoryPropertyFlags.DeviceLocal, width, height, VkImageType.Image2D, NUM_SAMPLES); + gbN_AO = new Image (dev, MRT_FORMAT, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransientAttachment, VkMemoryPropertyFlags.DeviceLocal, width, height, VkImageType.Image2D, NUM_SAMPLES); + gbPos = new Image (dev, MRT_FORMAT, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.TransientAttachment, VkMemoryPropertyFlags.DeviceLocal, width, height, VkImageType.Image2D, NUM_SAMPLES); + + + gbColorRough.CreateView (); gbColorRough.CreateSampler (); + gbColorRough.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + gbEmitMetal.CreateView (); gbEmitMetal.CreateSampler (); + gbEmitMetal.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + gbN_AO.CreateView (); gbN_AO.CreateSampler (); + gbN_AO.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + gbPos.CreateView (); gbPos.CreateSampler (); + gbPos.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + hdrImgResolved.CreateView (); hdrImgResolved.CreateSampler (); + hdrImgResolved.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + + DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descLayoutGBuff); + uboUpdate.Write (dev, dsGBuff, gbColorRough.Descriptor, + gbEmitMetal.Descriptor, + gbN_AO.Descriptor, + gbPos.Descriptor); + + gbColorRough.SetName ("GBuffColorRough"); + gbEmitMetal.SetName ("GBuffEmitMetal"); + gbN_AO.SetName ("GBuffN"); + gbPos.SetName ("GBuffPos"); + hdrImgResolved.SetName ("HDRimg resolved"); + } + + public void Resize (uint width, uint height) { + this.width = width; + this.height = height; + + frameBuffer?.Dispose (); + createGBuff (); + + frameBuffer = new FrameBuffer (renderPass, width, height, new Image[] { + hdrImgResolved, null, gbColorRough, gbEmitMetal, gbN_AO, gbPos}); + } + + public void Dispose () { + dev.WaitIdle (); + + frameBuffer?.Dispose (); + + gbColorRough.Dispose (); + gbEmitMetal.Dispose (); + gbN_AO.Dispose (); + gbPos.Dispose (); + hdrImgResolved.Dispose (); + + gBuffPipeline.Dispose (); + composePipeline.Dispose (); + //toneMappingPipeline.Dispose (); + debugPipeline?.Dispose (); + + descLayoutMain.Dispose (); + descLayoutTextures?.Dispose (); + descLayoutGBuff.Dispose (); + + uboMatrices.Dispose (); + uboLights.Dispose (); + model.Dispose (); + envCube.Dispose (); + +#if WITH_SHADOWS + shadowMapRenderer.Dispose (); +#endif + + descriptorPool.Dispose (); + + dev.DestroySemaphore (DrawComplete); + } + } +} diff --git a/samples/shadow/EnvironmentPipeline.cs b/samples/shadow/EnvironmentPipeline.cs new file mode 100644 index 0000000..32f8e6a --- /dev/null +++ b/samples/shadow/EnvironmentPipeline.cs @@ -0,0 +1,317 @@ +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using Vulkan; + +namespace vke { + public class EnvironmentCube : GraphicPipeline { + + GPUBuffer vboSkybox; + + public Image cubemap { get; private set; } + public Image lutBrdf { get; private set; } + public Image irradianceCube { get; private set; } + public Image prefilterCube { get; set; } + + public EnvironmentCube (string cubemapPath, DescriptorSet dsSkybox, PipelineLayout plLayout, Queue staggingQ, RenderPass renderPass, PipelineCache cache = null) + : base (renderPass, cache, "EnvCube pipeline") { + + using (CommandPool cmdPool = new CommandPool (staggingQ.Dev, staggingQ.index)) { + + vboSkybox = new GPUBuffer (staggingQ, cmdPool, VkBufferUsageFlags.VertexBuffer, box_vertices); + + cubemap = KTX.KTX.Load (staggingQ, cmdPool, cubemapPath, + VkImageUsageFlags.Sampled, VkMemoryPropertyFlags.DeviceLocal, true); + cubemap.CreateView (VkImageViewType.Cube, VkImageAspectFlags.Color); + cubemap.CreateSampler (VkSamplerAddressMode.ClampToEdge); + cubemap.SetName ("skybox Texture"); + cubemap.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, renderPass.Samples, false); + cfg.RenderPass = renderPass; + cfg.Layout = plLayout; + cfg.AddVertexBinding (0, 3 * sizeof (float)); + cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat); + cfg.AddShader (VkShaderStageFlags.Vertex, "#deferred.skybox.vert.spv"); + cfg.AddShader (VkShaderStageFlags.Fragment, "#deferred.skybox.frag.spv"); + cfg.multisampleState.rasterizationSamples = Samples; + + layout = cfg.Layout; + + init (cfg); + + generateBRDFLUT (staggingQ, cmdPool); + generateCubemaps (staggingQ, cmdPool); + } + + } + + public void RecordDraw (CommandBuffer cmd) { + Bind (cmd); + cmd.BindVertexBuffer (vboSkybox); + cmd.Draw (36); + } + + #region skybox + + static float[] box_vertices = { + 1.0f, 1.0f,-1.0f, // +X side + 1.0f, 1.0f, 1.0f, + 1.0f,-1.0f, 1.0f, + 1.0f,-1.0f, 1.0f, + 1.0f,-1.0f,-1.0f, + 1.0f, 1.0f,-1.0f, + + -1.0f,-1.0f,-1.0f, // +X side + -1.0f,-1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f, 1.0f, 1.0f, + -1.0f, 1.0f,-1.0f, + -1.0f,-1.0f,-1.0f, + + -1.0f, 1.0f,-1.0f, // +Y side + -1.0f, 1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + -1.0f, 1.0f,-1.0f, + 1.0f, 1.0f, 1.0f, + 1.0f, 1.0f,-1.0f, + + -1.0f,-1.0f,-1.0f, // -Y side + 1.0f,-1.0f,-1.0f, + 1.0f,-1.0f, 1.0f, + -1.0f,-1.0f,-1.0f, + 1.0f,-1.0f, 1.0f, + -1.0f,-1.0f, 1.0f, + + -1.0f, 1.0f, 1.0f, // +Z side + -1.0f,-1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + -1.0f,-1.0f, 1.0f, + 1.0f,-1.0f, 1.0f, + 1.0f, 1.0f, 1.0f, + + -1.0f,-1.0f,-1.0f, // -Z side + 1.0f, 1.0f,-1.0f, + 1.0f,-1.0f,-1.0f, + -1.0f,-1.0f,-1.0f, + -1.0f, 1.0f,-1.0f, + 1.0f, 1.0f,-1.0f, + + }; + #endregion + + void generateBRDFLUT (Queue staggingQ, CommandPool cmdPool) { + const VkFormat format = VkFormat.R16g16Sfloat; + const int dim = 512; + + lutBrdf = new Image (Dev, format, VkImageUsageFlags.ColorAttachment | VkImageUsageFlags.Sampled, + VkMemoryPropertyFlags.DeviceLocal, dim, dim); + lutBrdf.SetName ("lutBrdf"); + + lutBrdf.CreateView (); + lutBrdf.CreateSampler (VkSamplerAddressMode.ClampToEdge); + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, VkSampleCountFlags.SampleCount1, false); + + cfg.Layout = new PipelineLayout (Dev, new DescriptorSetLayout (Dev)); + cfg.RenderPass = new RenderPass (Dev); + cfg.RenderPass.AddAttachment (format, VkImageLayout.ShaderReadOnlyOptimal); + cfg.RenderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0, 0, 0) }); + cfg.RenderPass.AddSubpass (new SubPass (VkImageLayout.ColorAttachmentOptimal)); + cfg.AddShader (VkShaderStageFlags.Vertex, "#deferred.genbrdflut.vert.spv"); + cfg.AddShader (VkShaderStageFlags.Fragment, "#deferred.genbrdflut.frag.spv"); + + using (GraphicPipeline pl = new GraphicPipeline (cfg)) { + using (FrameBuffer fb = new FrameBuffer (cfg.RenderPass, dim, dim, lutBrdf)) { + CommandBuffer cmd = cmdPool.AllocateCommandBuffer (); + cmd.Start (VkCommandBufferUsageFlags.OneTimeSubmit); + pl.RenderPass.Begin (cmd, fb); + cmd.SetViewport (dim, dim); + cmd.SetScissor (dim, dim); + pl.Bind (cmd); + cmd.Draw (3, 1, 0, 0); + pl.RenderPass.End (cmd); + cmd.End (); + + staggingQ.Submit (cmd); + staggingQ.WaitIdle (); + + cmd.Free (); + } + } + lutBrdf.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + } + + public enum CBTarget { IRRADIANCE = 0, PREFILTEREDENV = 1 }; + + public Image generateCubeMap (Queue staggingQ, CommandPool cmdPool, CBTarget target) { + const float deltaPhi = (2.0f * (float)Math.PI) / 180.0f; + const float deltaTheta = (0.5f * (float)Math.PI) / 64.0f; + + VkFormat format = VkFormat.R32g32b32a32Sfloat; + uint dim = 64; + + if (target == CBTarget.PREFILTEREDENV) { + format = VkFormat.R16g16b16a16Sfloat; + dim = 512; + } + + uint numMips = (uint)Math.Floor (Math.Log (dim, 2)) + 1; + + Image imgFbOffscreen = new Image (Dev, format, VkImageUsageFlags.TransferSrc | VkImageUsageFlags.ColorAttachment, + VkMemoryPropertyFlags.DeviceLocal, dim, dim); + imgFbOffscreen.SetName ("offscreenfb"); + imgFbOffscreen.CreateView (); + + Image cmap = new Image (Dev, format, VkImageUsageFlags.TransferDst | VkImageUsageFlags.Sampled, + VkMemoryPropertyFlags.DeviceLocal, dim, dim, VkImageType.Image2D, VkSampleCountFlags.SampleCount1, VkImageTiling.Optimal, + numMips, 6, 1, VkImageCreateFlags.CubeCompatible); + if (target == CBTarget.PREFILTEREDENV) + cmap.SetName ("prefilterenvmap"); + else + cmap.SetName ("irradianceCube"); + cmap.CreateView (VkImageViewType.Cube, VkImageAspectFlags.Color, 6, 0); + cmap.CreateSampler (VkSamplerAddressMode.ClampToEdge); + + DescriptorPool dsPool = new DescriptorPool (Dev, 2, new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler)); + + DescriptorSetLayout dsLayout = new DescriptorSetLayout (Dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler)); + + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, VkSampleCountFlags.SampleCount1, false); + cfg.Layout = new PipelineLayout (Dev, dsLayout); + cfg.Layout.AddPushConstants ( + new VkPushConstantRange (VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, (uint)Marshal.SizeOf () + 8)); + + cfg.RenderPass = new RenderPass (Dev); + cfg.RenderPass.AddAttachment (format, VkImageLayout.ColorAttachmentOptimal); + cfg.RenderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0, 0, 0) }); + cfg.RenderPass.AddSubpass (new SubPass (VkImageLayout.ColorAttachmentOptimal)); + + cfg.AddVertexBinding (0, 3 * sizeof (float)); + cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat); + + cfg.AddShader (VkShaderStageFlags.Vertex, "#deferred.filtercube.vert.spv"); + if (target == CBTarget.PREFILTEREDENV) + cfg.AddShader (VkShaderStageFlags.Fragment, "#deferred.prefilterenvmap.frag.spv"); + else + cfg.AddShader (VkShaderStageFlags.Fragment, "#deferred.irradiancecube.frag.spv"); + + Matrix4x4[] matrices = { + // POSITIVE_X + Matrix4x4.CreateRotationX(Utils.DegreesToRadians(180)) * Matrix4x4.CreateRotationY(Utils.DegreesToRadians(90)), + // NEGATIVE_X + Matrix4x4.CreateRotationX(Utils.DegreesToRadians(180)) * Matrix4x4.CreateRotationY(Utils.DegreesToRadians(-90)), + // POSITIVE_Y + Matrix4x4.CreateRotationX(Utils.DegreesToRadians(-90)), + // NEGATIVE_Y + Matrix4x4.CreateRotationX(Utils.DegreesToRadians(90)), + // POSITIVE_Z + Matrix4x4.CreateRotationX(Utils.DegreesToRadians(180)), + // NEGATIVE_Z + Matrix4x4.CreateRotationZ(Utils.DegreesToRadians(180)) + }; + + VkImageSubresourceRange subRes = new VkImageSubresourceRange (VkImageAspectFlags.Color, 0, numMips, 0, 6); + + using (GraphicPipeline pl = new GraphicPipeline (cfg)) { + + DescriptorSet dset = dsPool.Allocate (dsLayout); + DescriptorSetWrites dsUpdate = new DescriptorSetWrites (dsLayout); + dsUpdate.Write (Dev, dset, cubemap.Descriptor); + Dev.WaitIdle (); + + using (FrameBuffer fb = new FrameBuffer (pl.RenderPass, dim, dim, imgFbOffscreen)) { + CommandBuffer cmd = cmdPool.AllocateCommandBuffer (); + cmd.Start (VkCommandBufferUsageFlags.OneTimeSubmit); + + cmap.SetLayout (cmd, VkImageLayout.Undefined, VkImageLayout.TransferDstOptimal, subRes); + + float roughness = 0; + + cmd.SetScissor (dim, dim); + cmd.SetViewport ((float)(dim), (float)dim); + + for (int m = 0; m < numMips; m++) { + roughness = (float)m / ((float)numMips - 1f); + + for (int f = 0; f < 6; f++) { + pl.RenderPass.Begin (cmd, fb); + + pl.Bind (cmd); + + float viewPortSize = (float)Math.Pow (0.5, m) * dim; + cmd.SetViewport (viewPortSize, viewPortSize); + cmd.PushConstant (pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, + matrices[f] * Matrix4x4.CreatePerspectiveFieldOfView (Utils.DegreesToRadians (90), 1f, 0.1f, 512f)); + if (target == CBTarget.IRRADIANCE) { + cmd.PushConstant (pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, deltaPhi, (uint)Marshal.SizeOf ()); + cmd.PushConstant (pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, deltaTheta, (uint)Marshal.SizeOf () + 4); + } else { + cmd.PushConstant (pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, roughness, (uint)Marshal.SizeOf ()); + cmd.PushConstant (pl.Layout, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, 64u, (uint)Marshal.SizeOf () + 4); + } + + cmd.BindDescriptorSet (pl.Layout, dset); + cmd.BindVertexBuffer (vboSkybox); + cmd.Draw (36); + + pl.RenderPass.End (cmd); + + imgFbOffscreen.SetLayout (cmd, VkImageAspectFlags.Color, + VkImageLayout.ColorAttachmentOptimal, VkImageLayout.TransferSrcOptimal); + + VkImageCopy region = new VkImageCopy (); + region.srcSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, 1); + region.dstSubresource = new VkImageSubresourceLayers (VkImageAspectFlags.Color, 1, (uint)m, (uint)f); + region.extent = new VkExtent3D { width = (uint)viewPortSize, height = (uint)viewPortSize, depth = 1 }; + + Vk.vkCmdCopyImage (cmd.Handle, + imgFbOffscreen.Handle, VkImageLayout.TransferSrcOptimal, + cmap.Handle, VkImageLayout.TransferDstOptimal, + 1, region.Pin ()); + region.Unpin (); + + imgFbOffscreen.SetLayout (cmd, VkImageAspectFlags.Color, + VkImageLayout.TransferSrcOptimal, VkImageLayout.ColorAttachmentOptimal); + + } + } + + cmap.SetLayout (cmd, VkImageLayout.TransferDstOptimal, VkImageLayout.ShaderReadOnlyOptimal, subRes); + + cmd.End (); + + staggingQ.Submit (cmd); + staggingQ.WaitIdle (); + + cmd.Free (); + } + } + cmap.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + + dsLayout.Dispose (); + imgFbOffscreen.Dispose (); + dsPool.Dispose (); + + return cmap; + } + + void generateCubemaps (Queue staggingQ, CommandPool cmdPool) { + irradianceCube = generateCubeMap (staggingQ, cmdPool, CBTarget.IRRADIANCE); + prefilterCube = generateCubeMap (staggingQ, cmdPool, CBTarget.PREFILTEREDENV); + } + + protected override void Dispose (bool disposing) { + vboSkybox.Dispose (); + cubemap.Dispose (); + lutBrdf.Dispose (); + irradianceCube.Dispose (); + prefilterCube.Dispose (); + + base.Dispose (disposing); + } + } + +} diff --git a/samples/shadow/main-crow.cs b/samples/shadow/main-crow.cs new file mode 100644 index 0000000..f90efcb --- /dev/null +++ b/samples/shadow/main-crow.cs @@ -0,0 +1,405 @@ +using System; +using System.Numerics; +using Glfw; +using Vulkan; +using vke; +using System.Collections.Generic; +using System.Linq; + +namespace deferred { + class Program : Crow.CrowWin { + static void Main (string[] args) { +#if DEBUG + Instance.VALIDATION = true; + Instance.DEBUG_UTILS = true; + Instance.RENDER_DOC_CAPTURE = false; +#endif + DeferredPbrRenderer.TEXTURE_ARRAY = true; + DeferredPbrRenderer.NUM_SAMPLES = VkSampleCountFlags.SampleCount1; + DeferredPbrRenderer.HDR_FORMAT = VkFormat.R32g32b32a32Sfloat; + DeferredPbrRenderer.MRT_FORMAT = VkFormat.R16g16b16a16Sfloat; + + PbrModelTexArray.TEXTURE_DIM = 1024; + + using (Program vke = new Program ()) { + vke.Run (); + } + } + + #region crow ui + public Crow.Command CMDViewScenes, CMDViewEditor, CMDViewDebug, CMDViewMaterials; + void init_crow_commands () { + CMDViewScenes = new Crow.Command (new Action (() => loadWindow ("#deferred.main.crow", this))) { Caption = "Lighting", Icon = new Crow.SvgPicture ("#deferred.crow.svg"), CanExecute = true }; + CMDViewEditor = new Crow.Command (new Action (() => loadWindow ("#deferred.scenes.crow", this))) { Caption = "Scenes", Icon = new Crow.SvgPicture ("#deferred.crow.svg"), CanExecute = true }; + CMDViewDebug = new Crow.Command (new Action (() => loadWindow ("#deferred.debug.crow", this))) { Caption = "Debug", Icon = new Crow.SvgPicture ("#deferred.crow.svg"), CanExecute = true }; + CMDViewMaterials = new Crow.Command (new Action (() => loadWindow ("#deferred.materials.crow", this))) { Caption = "Materials", Icon = new Crow.SvgPicture ("#deferred.crow.svg"), CanExecute = true }; + } + + public DeferredPbrRenderer.DebugView CurrentDebugView { + get { return renderer.currentDebugView; } + set { + if (value == renderer.currentDebugView) + return; + lock(crow.UpdateMutex) + renderer.currentDebugView = value; + rebuildBuffers = true; + NotifyValueChanged ("CurrentDebugView", renderer.currentDebugView); + } + } + + public float Gamma { + get { return renderer.matrices.gamma; } + set { + if (value == renderer.matrices.gamma) + return; + renderer.matrices.gamma = value; + NotifyValueChanged ("Gamma", value); + updateViewRequested = true; + } + } + public float Exposure { + get { return renderer.matrices.exposure; } + set { + if (value == renderer.matrices.exposure) + return; + renderer.matrices.exposure = value; + NotifyValueChanged ("Exposure", value); + updateViewRequested = true; + } + } + public float LightStrength { + get { return renderer.lights[renderer.lightNumDebug].color.X; } + set { + if (value == renderer.lights[renderer.lightNumDebug].color.X) + return; + renderer.lights[renderer.lightNumDebug].color = new Vector4(value); + NotifyValueChanged ("LightStrength", value); + renderer.uboLights.Update (renderer.lights); + } + } + public List Lights => renderer.lights.ToList (); + public List Scenes => renderer.model.Scenes; + #endregion + + public override string[] EnabledDeviceExtensions => new string[] { + Ext.D.VK_KHR_swapchain, + Ext.D.VK_EXT_debug_marker + }; + + protected override void configureEnabledFeatures (VkPhysicalDeviceFeatures available_features, ref VkPhysicalDeviceFeatures features) { + base.configureEnabledFeatures (available_features, ref features); + + features.samplerAnisotropy = available_features.samplerAnisotropy; + features.sampleRateShading = available_features.sampleRateShading; + features.geometryShader = available_features.geometryShader; + features.pipelineStatisticsQuery = true; + + if (available_features.textureCompressionETC2) { + features.textureCompressionETC2 = true; + Image.DefaultTextureFormat = VkFormat.Etc2R8g8b8a8UnormBlock; + }else if (available_features.textureCompressionBC) { + //features.textureCompressionBC = true; + //Image.DefaultTextureFormat = VkFormat.Bc3UnormBlock; + } + + + } + + protected override void createQueues () { + base.createQueues (); + transferQ = new Queue (dev, VkQueueFlags.Transfer); + } + + string[] cubemapPathes = { + Utils.DataDirectory + "textures/papermill.ktx", + Utils.DataDirectory + "textures/cubemap_yokohama_bc3_unorm.ktx", + Utils.DataDirectory + "textures/gcanyon_cube.ktx", + Utils.DataDirectory + "textures/pisa_cube.ktx", + Utils.DataDirectory + "textures/uffizi_cube.ktx", + }; + string[] modelPathes = { + //"/mnt/devel/gts/vkChess.net/data/models/chess.glb", + //"/home/jp/gltf/jaguar/scene.gltf", + Utils.DataDirectory + "models/DamagedHelmet/glTF/DamagedHelmet.gltf", + Utils.DataDirectory + "models/shadow.glb", + Utils.DataDirectory + "models/Hubble.glb", + Utils.DataDirectory + "models/MER_static.glb", + Utils.DataDirectory + "models/ISS_stationary.glb", + }; + + int curModelIndex = 0; + bool reloadModel; + + Queue transferQ; + DeferredPbrRenderer renderer; + PipelineStatisticsQueryPool statPool; + TimestampQueryPool timestampQPool; + ulong[] results; + + DebugReport dbgRepport; + + protected override void initVulkan () { + base.initVulkan (); + +#if DEBUG + dbgRepport = new DebugReport (instance, + VkDebugReportFlagsEXT.ErrorEXT + | VkDebugReportFlagsEXT.DebugEXT + | VkDebugReportFlagsEXT.WarningEXT + | VkDebugReportFlagsEXT.PerformanceWarningEXT + ); +#endif + + camera = new Camera (Utils.DegreesToRadians (45f), 1f, 0.1f, 16f); + camera.SetPosition (0, 0, 2); + + renderer = new DeferredPbrRenderer (dev, swapChain, presentQueue, cubemapPathes[2], camera.NearPlane, camera.FarPlane); + renderer.LoadModel (transferQ, modelPathes[curModelIndex]); + camera.Model = Matrix4x4.CreateScale (1f / Math.Max (Math.Max (renderer.modelAABB.Width, renderer.modelAABB.Height), renderer.modelAABB.Depth)); + + statPool = new PipelineStatisticsQueryPool (dev, + VkQueryPipelineStatisticFlags.InputAssemblyVertices | + VkQueryPipelineStatisticFlags.InputAssemblyPrimitives | + VkQueryPipelineStatisticFlags.ClippingInvocations | + VkQueryPipelineStatisticFlags.ClippingPrimitives | + VkQueryPipelineStatisticFlags.FragmentShaderInvocations); + + timestampQPool = new TimestampQueryPool (dev); + + init_crow_commands (); + + crow.Load ("#deferred.menu.crow").DataSource = this; + + } + + protected override void recordDraw (CommandBuffer cmd, int imageIndex) { + statPool.Begin (cmd); + renderer.buildCommandBuffers (cmd, imageIndex); + statPool.End (cmd); + } + + public override void UpdateView () { + renderer.UpdateView (camera); + updateViewRequested = false; +#if WITH_SHADOWS + if (renderer.shadowMapRenderer.updateShadowMap) + renderer.shadowMapRenderer.update_shadow_map (cmdPool); +#endif + } + + int frameCount = 0; + public override void Update () { + if (reloadModel) { + renderer.LoadModel (transferQ, modelPathes[curModelIndex]); + reloadModel = false; + camera.Model = Matrix4x4.CreateScale (1f / Math.Max (Math.Max (renderer.modelAABB.Width, renderer.modelAABB.Height), renderer.modelAABB.Depth)); + updateViewRequested = true; + rebuildBuffers = true; +#if WITH_SHADOWS + renderer.shadowMapRenderer.updateShadowMap = true; +#endif + } + + base.Update (); + + if (++frameCount > 20) { + NotifyValueChanged ("fps", fps); + frameCount = 0; + } + + results = statPool.GetResults (); + } + protected override void OnResize () { + renderer.Resize (); + base.OnResize (); + } + + #region Mouse and keyboard + protected override void onMouseMove (double xPos, double yPos) { + if (crow.ProcessMouseMove ((int)xPos, (int)yPos)) + return; + + double diffX = lastMouseX - xPos; + double diffY = lastMouseY - yPos; + if (MouseButton[0]) { + camera.Rotate ((float)-diffX, (float)-diffY); + } else if (MouseButton[1]) { + camera.SetZoom ((float)diffY); + } else + return; + + updateViewRequested = true; + } + protected override void onMouseButtonDown (Glfw.MouseButton button) { + if (crow.ProcessMouseButtonDown ((Crow.MouseButton)button)) + return; + base.onMouseButtonDown (button); + } + protected override void onMouseButtonUp (Glfw.MouseButton button) { + if (crow.ProcessMouseButtonUp ((Crow.MouseButton)button)) + return; + base.onMouseButtonUp (button); + } + protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) { + if (crow.ProcessKeyDown ((Crow.Key)key)) + return; + switch (key) { + case Key.F: + if (modifiers.HasFlag (Modifier.Shift)) { + renderer.debugFace--; + if (renderer.debugFace < 0) + renderer.debugFace = 5; + } else { + renderer.debugFace++; + if (renderer.debugFace >= 5) + renderer.debugFace = 0; + } + rebuildBuffers = true; + break; + case Key.M: + if (modifiers.HasFlag (Modifier.Shift)) { + renderer.debugMip--; + if (renderer.debugMip < 0) + renderer.debugMip = (int)renderer.envCube.prefilterCube.CreateInfo.mipLevels - 1; + } else { + renderer.debugMip++; + if (renderer.debugMip >= renderer.envCube.prefilterCube.CreateInfo.mipLevels) + renderer.debugMip = 0; + } + rebuildBuffers = true; + break; + case Key.L: + if (modifiers.HasFlag (Modifier.Shift)) { + renderer.lightNumDebug--; + if (renderer.lightNumDebug < 0) + renderer.lightNumDebug = (int)renderer.lights.Length - 1; + } else { + renderer.lightNumDebug++; + if (renderer.lightNumDebug >= renderer.lights.Length) + renderer.lightNumDebug = 0; + } + rebuildBuffers = true; + break; + case Key.Keypad0: + case Key.Keypad1: + case Key.Keypad2: + case Key.Keypad3: + case Key.Keypad4: + case Key.Keypad5: + case Key.Keypad6: + case Key.Keypad7: + case Key.Keypad8: + case Key.Keypad9: + renderer.currentDebugView = (DeferredPbrRenderer.DebugView)(int)key-320; + rebuildBuffers = true; + break; + case Key.KeypadDivide: + renderer.currentDebugView = DeferredPbrRenderer.DebugView.irradiance; + rebuildBuffers = true; + break; + case Key.S: + if (modifiers.HasFlag (Modifier.Control)) { + renderer.pipelineCache.Save (); + Console.WriteLine ($"Pipeline Cache saved."); + } else { + renderer.currentDebugView = DeferredPbrRenderer.DebugView.shadowMap; + rebuildBuffers = true; + } + break; + case Key.Up: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight(-Vector4.UnitZ); + else + camera.Move (0, 0, 1); + break; + case Key.Down: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (Vector4.UnitZ); + else + camera.Move (0, 0, -1); + break; + case Key.Left: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (-Vector4.UnitX); + else + camera.Move (1, 0, 0); + break; + case Key.Right: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (Vector4.UnitX); + else + camera.Move (-1, 0, 0); + break; + case Key.PageUp: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (Vector4.UnitY); + else + camera.Move (0, 1, 0); + break; + case Key.PageDown: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (-Vector4.UnitY); + else + camera.Move (0, -1, 0); + break; + case Key.F2: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.matrices.exposure -= 0.3f; + else + renderer.matrices.exposure += 0.3f; + break; + case Key.F3: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.matrices.gamma -= 0.1f; + else + renderer.matrices.gamma += 0.1f; + break; + case Key.F4: + if (camera.Type == Camera.CamType.FirstPerson) + camera.Type = Camera.CamType.LookAt; + else + camera.Type = Camera.CamType.FirstPerson; + Console.WriteLine ($"camera type = {camera.Type}"); + break; + case Key.KeypadAdd: + curModelIndex++; + if (curModelIndex >= modelPathes.Length) + curModelIndex = 0; + reloadModel = true; + break; + case Key.KeypadSubtract: + curModelIndex--; + if (curModelIndex < 0) + curModelIndex = modelPathes.Length -1; + reloadModel = true; + break; + default: + base.onKeyDown (key, scanCode, modifiers); + return; + } + updateViewRequested = true; + } + protected override void onKeyUp (Key key, int scanCode, Modifier modifiers) { + if (crow.ProcessKeyUp ((Crow.Key)key)) + return; + } + protected override void onChar (CodePoint cp) { + if (crow.ProcessKeyPress (cp.ToChar ())) + return; + } + #endregion + + protected override void Dispose (bool disposing) { + if (disposing) { + if (!isDisposed) { + renderer.Dispose (); + statPool.Dispose (); + timestampQPool.Dispose (); + dbgRepport?.Dispose (); + } + } + + base.Dispose (disposing); + } + } +} diff --git a/samples/shadow/main.cs b/samples/shadow/main.cs new file mode 100644 index 0000000..56ba57f --- /dev/null +++ b/samples/shadow/main.cs @@ -0,0 +1,385 @@ +// Copyright (c) 2020 Jean-Philippe Bruyère +// +// 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 vke.glTF; +using Glfw; +using Vulkan; +using System.Diagnostics; + +namespace deferred { + /// + /// Deferred PBR rendering. + /// + class Deferred : VkWindow { + + static void Main (string[] args) { +#if DEBUG + Instance.VALIDATION = true; + //Instance.RENDER_DOC_CAPTURE = true; +#endif + SwapChain.PREFERED_FORMAT = VkFormat.B8g8r8a8Srgb; + DeferredPbrRenderer.TEXTURE_ARRAY = true; + DeferredPbrRenderer.NUM_SAMPLES = VkSampleCountFlags.SampleCount8; + DeferredPbrRenderer.HDR_FORMAT = VkFormat.R32g32b32a32Sfloat; + DeferredPbrRenderer.MRT_FORMAT = VkFormat.R32g32b32a32Sfloat; + + PbrModelTexArray.TEXTURE_DIM = 1024; + + using (Deferred vke = new Deferred ()) { + vke.Run (); + } + } + + public override string[] EnabledInstanceExtensions => new string[] { + Ext.I.VK_EXT_debug_utils, + }; + + public override string[] EnabledDeviceExtensions => new string[] { + Ext.D.VK_KHR_swapchain, + }; + + protected override void configureEnabledFeatures (VkPhysicalDeviceFeatures available_features, ref VkPhysicalDeviceFeatures enabled_features) { + base.configureEnabledFeatures (available_features, ref enabled_features); + + enabled_features.samplerAnisotropy = available_features.samplerAnisotropy; + enabled_features.sampleRateShading = available_features.sampleRateShading; + enabled_features.geometryShader = available_features.geometryShader; + + enabled_features.textureCompressionBC = available_features.textureCompressionBC; + } + + protected override void createQueues () { + base.createQueues (); + transferQ = new Queue (dev, VkQueueFlags.Transfer); + computeQ = new Queue (dev, VkQueueFlags.Compute); + } + string[] cubemapPathes = { + Utils.DataDirectory + "textures/papermill.ktx", + Utils.DataDirectory + "textures/cubemap_yokohama_bc3_unorm.ktx", + Utils.DataDirectory + "textures/gcanyon_cube.ktx", + Utils.DataDirectory + "textures/pisa_cube.ktx", + Utils.DataDirectory + "textures/uffizi_cube.ktx", + }; + string[] modelPathes = { + Utils.DataDirectory + "models/cubeOnPlane.glb", + //"/mnt/devel/vkPinball/data/models/pinball.gltf", + Utils.DataDirectory + "models/DamagedHelmet/glTF/DamagedHelmet.gltf", + Utils.DataDirectory + "models/Hubble.glb", + Utils.DataDirectory + "models/MER_static.glb", + Utils.DataDirectory + "models/ISS_stationary.glb", + }; + + int curModelIndex = 0; + bool reloadModel; + bool rebuildBuffers; + + Queue transferQ, computeQ; + DeferredPbrRenderer renderer; + + + GraphicPipeline plToneMap; + FrameBuffers frameBuffers; + DescriptorPool descriptorPool; + DescriptorSet descriptorSet; + + vke.DebugUtils.Messenger dbgmsg; + + protected override void initVulkan () { + base.initVulkan (); + +#if DEBUG + dbgmsg = new vke.DebugUtils.Messenger (instance, VkDebugUtilsMessageTypeFlagsEXT.PerformanceEXT | VkDebugUtilsMessageTypeFlagsEXT.ValidationEXT | VkDebugUtilsMessageTypeFlagsEXT.GeneralEXT, + VkDebugUtilsMessageSeverityFlagsEXT.InfoEXT | + VkDebugUtilsMessageSeverityFlagsEXT.WarningEXT | + VkDebugUtilsMessageSeverityFlagsEXT.ErrorEXT | + VkDebugUtilsMessageSeverityFlagsEXT.VerboseEXT); +#endif + + camera = new Camera (Utils.DegreesToRadians (45f), 1f, 0.1f, 16f); + camera.SetPosition (0, 0, -2); + + //renderer = new DeferredPbrRenderer (presentQueue, cubemapPathes[2], swapChain.Width, swapChain.Height, camera.NearPlane, camera.FarPlane); + renderer = new DeferredPbrRenderer (presentQueue, cubemapPathes[2], swapChain.Width, swapChain.Height, camera.NearPlane, camera.FarPlane); + renderer.LoadModel (transferQ, modelPathes[curModelIndex]); + foreach (Model.Mesh mesh in renderer.model.Meshes) { + Console.WriteLine (mesh.Name); + } + camera.Model = Matrix4x4.CreateScale (1f / Math.Max (Math.Max (renderer.modelAABB.Width, renderer.modelAABB.Height), renderer.modelAABB.Depth)); + + init_final_pl (); + } + + void init_final_pl() { + descriptorPool = new DescriptorPool (dev, 3, + new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 2), + new VkDescriptorPoolSize (VkDescriptorType.StorageImage, 4) + ); + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, DeferredPbrRenderer.NUM_SAMPLES); + if (DeferredPbrRenderer.NUM_SAMPLES != VkSampleCountFlags.SampleCount1) { + cfg.multisampleState.sampleShadingEnable = true; + cfg.multisampleState.minSampleShading = 0.5f; + } + cfg.Layout = new PipelineLayout (dev, + new VkPushConstantRange (VkShaderStageFlags.Fragment, 2 * sizeof (float)), + new DescriptorSetLayout (dev, 0, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler) + )); + + cfg.RenderPass = new RenderPass (dev, swapChain.ColorFormat, DeferredPbrRenderer.NUM_SAMPLES); + + cfg.AddShaders (new ShaderInfo (dev, VkShaderStageFlags.Vertex, "#vke.FullScreenQuad.vert.spv")); + cfg.AddShaders (new ShaderInfo (dev, VkShaderStageFlags.Fragment, "#shaders.tone_mapping.frag.spv")); + + plToneMap = new GraphicPipeline (cfg); + + cfg.DisposeShaders (); + + descriptorSet = descriptorPool.Allocate (cfg.Layout.DescriptorSetLayouts[0]); + + } + + void buildCommandBuffers () { + for (int i = 0; i < swapChain.ImageCount; ++i) { + cmds[i]?.Free (); + cmds[i] = cmdPool.AllocateAndStart (); + + renderer.buildCommandBuffers (cmds[i]); + + plToneMap.RenderPass.Begin (cmds[i], frameBuffers[i]); + + cmds[i].SetViewport (frameBuffers[i].Width, frameBuffers[i].Height); + cmds[i].SetScissor (frameBuffers[i].Width, frameBuffers[i].Height); + + plToneMap.Bind (cmds[i]); + plToneMap.BindDescriptorSet (cmds[i], descriptorSet); + + cmds[i].PushConstant (plToneMap.Layout, VkShaderStageFlags.Fragment, 8, new float[] { renderer.exposure, renderer.gamma }, 0); + + cmds[i].Draw (3, 1, 0, 0); + + plToneMap.RenderPass.End (cmds[i]); + + cmds[i].End (); + } + } + + public override void UpdateView () { + dev.WaitIdle (); + + renderer.UpdateView (camera); + updateViewRequested = false; +#if WITH_SHADOWS + if (renderer.shadowMapRenderer.updateShadowMap) + renderer.shadowMapRenderer.update_shadow_map (cmdPool); +#endif + } + + public override void Update () { + if (reloadModel) { + renderer.LoadModel (transferQ, modelPathes[curModelIndex]); + reloadModel = false; + camera.Model = Matrix4x4.CreateScale (1f / Math.Max (Math.Max (renderer.modelAABB.Width, renderer.modelAABB.Height), renderer.modelAABB.Depth)); + updateViewRequested = true; + rebuildBuffers = true; +#if WITH_SHADOWS + renderer.shadowMapRenderer.updateShadowMap = true; +#endif + } + + if (rebuildBuffers) { + buildCommandBuffers (); + rebuildBuffers = false; + } + + } + + protected override void OnResize () { + base.OnResize (); + + dev.WaitIdle (); + + renderer.Resize (Width, Height); + + UpdateView (); + + frameBuffers?.Dispose(); + frameBuffers = plToneMap.RenderPass.CreateFrameBuffers (swapChain); + + DescriptorSetWrites dsUpdate = new DescriptorSetWrites (plToneMap.Layout.DescriptorSetLayouts[0].Bindings[0]); + dsUpdate.Write (dev, descriptorSet, renderer.hdrImgResolved.Descriptor); + + buildCommandBuffers (); + + dev.WaitIdle (); + } + + #region Mouse and keyboard + protected override void onScroll (double xOffset, double yOffset) { + } + protected override void onMouseMove (double xPos, double yPos) { + double diffX = lastMouseX - xPos; + double diffY = lastMouseY - yPos; + if (MouseButton[0]) { + camera.Rotate ((float)-diffY, (float)-diffX, 0); + } else if (MouseButton[1]) { + camera.SetZoom ((float)diffY); + } else + return; + + updateViewRequested = true; + } + protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) { + switch (key) { + case Key.F: + if (modifiers.HasFlag (Modifier.Shift)) { + renderer.debugFace--; + if (renderer.debugFace < 0) + renderer.debugFace = 5; + } else { + renderer.debugFace++; + if (renderer.debugFace >= 5) + renderer.debugFace = 0; + } + rebuildBuffers = true; + break; + case Key.M: + if (modifiers.HasFlag (Modifier.Shift)) { + renderer.debugMip--; + if (renderer.debugMip < 0) + renderer.debugMip = (int)renderer.envCube.prefilterCube.CreateInfo.mipLevels - 1; + } else { + renderer.debugMip++; + if (renderer.debugMip >= renderer.envCube.prefilterCube.CreateInfo.mipLevels) + renderer.debugMip = 0; + } + rebuildBuffers = true; + break; + case Key.L: + if (modifiers.HasFlag (Modifier.Shift)) { + renderer.lightNumDebug--; + if (renderer.lightNumDebug < 0) + renderer.lightNumDebug = (int)renderer.lights.Length - 1; + } else { + renderer.lightNumDebug++; + if (renderer.lightNumDebug >= renderer.lights.Length) + renderer.lightNumDebug = 0; + } + rebuildBuffers = true; + break; + case Key.Keypad0: + case Key.Keypad1: + case Key.Keypad2: + case Key.Keypad3: + case Key.Keypad4: + case Key.Keypad5: + case Key.Keypad6: + case Key.Keypad7: + case Key.Keypad8: + case Key.Keypad9: + renderer.currentDebugView = (DeferredPbrRenderer.DebugView)(int)key-320; + rebuildBuffers = true; + break; + case Key.KeypadDivide: + renderer.currentDebugView = DeferredPbrRenderer.DebugView.irradiance; + rebuildBuffers = true; + break; + case Key.S: + if (modifiers.HasFlag (Modifier.Control)) { + renderer.pipelineCache.Save (); + Console.WriteLine ($"Pipeline Cache saved."); + } else { + renderer.currentDebugView = DeferredPbrRenderer.DebugView.shadowMap; + rebuildBuffers = true; + } + break; + case Key.Up: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight(-Vector4.UnitZ); + else + camera.Move (0, 0, 1); + break; + case Key.Down: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (Vector4.UnitZ); + else + camera.Move (0, 0, -1); + break; + case Key.Left: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (-Vector4.UnitX); + else + camera.Move (1, 0, 0); + break; + case Key.Right: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (Vector4.UnitX); + else + camera.Move (-1, 0, 0); + break; + case Key.PageUp: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (Vector4.UnitY); + else + camera.Move (0, 1, 0); + break; + case Key.PageDown: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.MoveLight (-Vector4.UnitY); + else + camera.Move (0, -1, 0); + break; + case Key.F2: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.exposure -= 0.3f; + else + renderer.exposure += 0.3f; + rebuildBuffers = true; + break; + case Key.F3: + if (modifiers.HasFlag (Modifier.Shift)) + renderer.gamma -= 0.1f; + else + renderer.gamma += 0.1f; + rebuildBuffers = true; + break; + case Key.KeypadAdd: + curModelIndex++; + if (curModelIndex >= modelPathes.Length) + curModelIndex = 0; + reloadModel = true; + break; + case Key.KeypadSubtract: + curModelIndex--; + if (curModelIndex < 0) + curModelIndex = modelPathes.Length -1; + reloadModel = true; + break; + default: + base.onKeyDown (key, scanCode, modifiers); + return; + } + updateViewRequested = true; + } + #endregion + + protected override void Dispose (bool disposing) { + dev.WaitIdle (); + if (disposing) { + if (!isDisposed) { + frameBuffers?.Dispose(); + renderer?.Dispose (); + plToneMap?.Dispose (); + descriptorPool?.Dispose (); + dbgmsg?.Dispose (); + } + } + base.Dispose (disposing); + } + } +} diff --git a/samples/shadow/mainShadow.cs b/samples/shadow/mainShadow.cs new file mode 100644 index 0000000..0bc3103 --- /dev/null +++ b/samples/shadow/mainShadow.cs @@ -0,0 +1,415 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.InteropServices; +using Glfw; +using VK; +using CVKL; + +namespace deferredShadow { + class Program : VkWindow{ + static void Main (string[] args) { + using (Program vke = new Program ()) { + vke.Run (); + } + } + + VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1; + + public struct Matrices { + public Matrix4x4 projection; + public Matrix4x4 view; + public Matrix4x4 model; + public Vector4 lightPos; + public float gamma; + public float exposure; + } + + public Matrices matrices = new Matrices { + lightPos = new Vector4 (1.0f, 0.0f, 0.0f, 1.0f), + gamma = 1.0f, + exposure = 2.0f, + }; + + const uint SHADOW_MAP_SIZE = 4096; + +#if DEBUG + PipelineStatisticsQueryPool statPool; + TimestampQueryPool timestampQPool; + ulong[] results; +#endif + + protected override void configureEnabledFeatures (ref VkPhysicalDeviceFeatures features) { + base.configureEnabledFeatures (ref features); +#if DEBUG + features.pipelineStatisticsQuery = true; +#endif + } + + Program () : base(true) { + //camera.Model = Matrix4x4.CreateRotationX (Utils.DegreesToRadians (-90)) * Matrix4x4.CreateRotationY (Utils.DegreesToRadians (180)); + //camera.SetRotation (-0.1f,-0.4f); + camera.SetPosition (0, 0, -3); + + init (); + +#if DEBUG + statPool = new PipelineStatisticsQueryPool (dev, + VkQueryPipelineStatisticFlags.InputAssemblyVertices | + VkQueryPipelineStatisticFlags.InputAssemblyPrimitives | + VkQueryPipelineStatisticFlags.ClippingInvocations | + VkQueryPipelineStatisticFlags.ClippingPrimitives | + VkQueryPipelineStatisticFlags.FragmentShaderInvocations); + + timestampQPool = new TimestampQueryPool (dev); +#endif + } + + Framebuffer[] frameBuffers; + Image gbColorRough, gbEmitMetal, gbN, gbPos; + + DescriptorPool descriptorPool; + DescriptorSetLayout descLayoutMain, descLayoutModelTextures, descLayoutGBuff; + DescriptorSet dsMain, dsGBuff; + + Pipeline gBuffPipeline, composePipeline; + + HostBuffer uboMats; + + RenderPass renderPass; + + Model model; + EnvironmentCube envCube; + + DebugDrawPipeline debugDraw; + Framebuffer[] debugFB; + + void init () { + VkFormat depthFormat = dev.GetSuitableDepthFormat (); + renderPass = new RenderPass (dev); + + renderPass.AddAttachment (swapChain.ColorFormat, VkImageLayout.ColorAttachmentOptimal, VkSampleCountFlags.SampleCount1); + renderPass.AddAttachment (depthFormat, VkImageLayout.DepthStencilAttachmentOptimal, samples); + renderPass.AddAttachment (VkFormat.R8g8b8a8Unorm, VkImageLayout.ColorAttachmentOptimal); + renderPass.AddAttachment (VkFormat.R8g8b8a8Unorm, VkImageLayout.ColorAttachmentOptimal); + renderPass.AddAttachment (VkFormat.R16g16b16a16Sfloat, VkImageLayout.ColorAttachmentOptimal); + renderPass.AddAttachment (VkFormat.R16g16b16a16Sfloat, VkImageLayout.ColorAttachmentOptimal); + //renderPass.AddAttachment (VkFormat.R16g16b16a16Sfloat, VkImageLayout.DepthStencilReadOnlyOptimal); + + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { depthStencil = new VkClearDepthStencilValue (1.0f, 0) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + + SubPass[] subpass = { new SubPass (), new SubPass (), new SubPass() }; + subpass[0].AddColorReference ( new VkAttachmentReference (2, VkImageLayout.ColorAttachmentOptimal), + new VkAttachmentReference (3, VkImageLayout.ColorAttachmentOptimal), + new VkAttachmentReference (4, VkImageLayout.ColorAttachmentOptimal), + new VkAttachmentReference (5, VkImageLayout.ColorAttachmentOptimal)); + subpass[0].SetDepthReference (1, VkImageLayout.DepthStencilAttachmentOptimal); + + subpass[1].AddColorReference (0, VkImageLayout.ColorAttachmentOptimal); + subpass[1].AddInputReference ( new VkAttachmentReference (2, VkImageLayout.ShaderReadOnlyOptimal), + new VkAttachmentReference (3, VkImageLayout.ShaderReadOnlyOptimal), + new VkAttachmentReference (4, VkImageLayout.ShaderReadOnlyOptimal), + new VkAttachmentReference (5, VkImageLayout.ShaderReadOnlyOptimal)); + renderPass.AddSubpass (subpass); + + renderPass.AddDependency (Vk.SubpassExternal, 0, + VkPipelineStageFlags.BottomOfPipe, VkPipelineStageFlags.ColorAttachmentOutput, + VkAccessFlags.MemoryRead, VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite); + renderPass.AddDependency (0, 1, + VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.FragmentShader, + VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.ShaderRead); + renderPass.AddDependency (1, Vk.SubpassExternal, + VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.BottomOfPipe, + VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.MemoryRead); + + + descriptorPool = new DescriptorPool (dev, 3, + new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer, 2), + new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 3), + new VkDescriptorPoolSize (VkDescriptorType.InputAttachment, 4) + ); + + descLayoutMain = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler)); + + descLayoutModelTextures = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (4, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler) + ); + + descLayoutGBuff = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment), + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment), + new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment)); + + dsMain = descriptorPool.Allocate (descLayoutMain); + dsGBuff = descriptorPool.Allocate (descLayoutGBuff); + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, samples); + cfg.Layout = new PipelineLayout (dev, descLayoutMain, descLayoutModelTextures, descLayoutGBuff); + cfg.Layout.AddPushConstants ( + new VkPushConstantRange (VkShaderStageFlags.Vertex, (uint)Marshal.SizeOf ()), + new VkPushConstantRange (VkShaderStageFlags.Fragment, (uint)Marshal.SizeOf (), 64) + ); + cfg.RenderPass = renderPass; + cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false)); + cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false)); + cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false)); + + cfg.AddVertexBinding (0); + cfg.SetVertexAttributes (0, VkFormat.R32g32b32Sfloat, VkFormat.R32g32b32Sfloat, VkFormat.R32g32Sfloat); + cfg.AddShader (VkShaderStageFlags.Vertex, "shaders/pbrtest.vert.spv"); + cfg.AddShader (VkShaderStageFlags.Fragment, "shaders/GBuffPbr.frag.spv"); + + gBuffPipeline = new GraphicPipeline (cfg); + cfg.blendAttachments.Clear (); + cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false)); + cfg.ResetShadersAndVerticesInfos (); + cfg.SubpassIndex = 1; + cfg.Layout = gBuffPipeline.Layout; + cfg.depthStencilState.depthTestEnable = false; + cfg.depthStencilState.depthWriteEnable = false; + cfg.AddShader (VkShaderStageFlags.Vertex, "shaders/FullScreenQuad.vert.spv"); + cfg.AddShader (VkShaderStageFlags.Fragment, "shaders/pbrtest.frag.spv"); + composePipeline = new GraphicPipeline (cfg); + + envCube = new EnvironmentCube (presentQueue, renderPass); + + uboMats = new HostBuffer (dev, VkBufferUsageFlags.UniformBuffer, (ulong)Marshal.SizeOf () * 2); + uboMats.Map ();//permanent map + + DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descLayoutMain); + uboUpdate.Write (dev, dsMain, uboMats.Descriptor, + envCube.lutBrdf.Descriptor, + envCube.irradianceCube.Descriptor, + envCube.prefilterCube.Descriptor); + uboMats.Descriptor.offset = (ulong)Marshal.SizeOf (); + envCube.WriteDesc (uboMats.Descriptor); +#if DEBUG + debugDraw = new DebugDrawPipeline (dev, descLayoutMain, swapChain.ColorFormat); + debugDraw.AddLine (Vector3.Zero, new Vector3(matrices.lightPos.X,matrices.lightPos.Y,matrices.lightPos.Z)*3, 1, 1, 1); + debugDraw.AddLine (Vector3.Zero, Vector3.UnitX, 1, 0, 0); + debugDraw.AddLine (Vector3.Zero, Vector3.UnitY, 0, 1, 0); + debugDraw.AddLine (Vector3.Zero, Vector3.UnitZ, 0, 0, 1); +#endif + + + model = new Model (presentQueue, "../data/models/DamagedHelmet/glTF/DamagedHelmet.gltf"); + //model = new Model (presentQueue, "../data/models/chess.gltf"); + //model = new Model (presentQueue, "../data/models/Sponza/glTF/Sponza.gltf"); + //model = new Model (dev, presentQueue, "../data/models/icosphere.gltf"); + //model = new Model (dev, presentQueue, cmdPool, "../data/models/cube.gltf"); + model.WriteMaterialsDescriptorSets (descLayoutModelTextures, + VK.AttachmentType.Color, + VK.AttachmentType.Normal, + VK.AttachmentType.AmbientOcclusion, + VK.AttachmentType.PhysicalProps, + VK.AttachmentType.Emissive); + } + + void buildCommandBuffers () { + for (int i = 0; i < swapChain.ImageCount; ++i) { + cmds[i]?.Free (); + cmds[i] = cmdPool.AllocateCommandBuffer (); + cmds[i].Start (); + +#if DEBUG + statPool.Begin (cmds[i]); + recordDraw (cmds[i], frameBuffers[i]); + statPool.End (cmds[i]); + + debugDraw.RecordDraw (cmds[i], debugFB[i], camera); +#else + recordDraw (cmds[i], frameBuffers[i]); +#endif + + cmds[i].End (); + } + } + void recordDraw (CommandBuffer cmd, Framebuffer fb) { + renderPass.Begin (cmd, fb); + + cmd.SetViewport (fb.Width, fb.Height); + cmd.SetScissor (fb.Width, fb.Height); + + cmd.BindDescriptorSet (gBuffPipeline.Layout, dsMain); + gBuffPipeline.Bind (cmd); + model.Bind (cmd); + model.DrawAll (cmd, gBuffPipeline.Layout); + + renderPass.BeginSubPass (cmd); + + cmd.BindDescriptorSet (composePipeline.Layout, dsGBuff, 2); + composePipeline.Bind (cmd); + + cmd.Draw (3, 1, 0, 0); + + renderPass.End (cmd); + } + +#region update + void updateMatrices () { + camera.AspectRatio = (float)swapChain.Width / swapChain.Height; + + matrices.projection = camera.Projection; + matrices.view = camera.View; + matrices.model = camera.Model; + uboMats.Update (matrices, (uint)Marshal.SizeOf ()); + matrices.view *= Matrix4x4.CreateTranslation (-matrices.view.Translation); + matrices.model = Matrix4x4.Identity; + uboMats.Update (matrices, (uint)Marshal.SizeOf (), (uint)Marshal.SizeOf ()); + } + + public override void UpdateView () { + updateMatrices (); + updateViewRequested = false; + } + public override void Update () { +#if DEBUG + results = statPool.GetResults (); +#endif + } +#endregion + + + + void createGBuff () { + gbColorRough?.Dispose (); + gbEmitMetal?.Dispose (); + gbN?.Dispose (); + gbPos?.Dispose (); + + gbColorRough = new Image (dev, VkFormat.R8g8b8a8Unorm, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height); + gbEmitMetal = new Image (dev, VkFormat.R8g8b8a8Unorm, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height); + gbN = new Image (dev, VkFormat.R16g16b16a16Sfloat, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height); + gbPos = new Image (dev, VkFormat.R16g16b16a16Sfloat, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height); + + gbColorRough.CreateView (); + gbColorRough.CreateSampler (); + gbEmitMetal.CreateView (); + gbEmitMetal.CreateSampler (); + gbN.CreateView (); + gbN.CreateSampler (); + gbPos.CreateView (); + gbPos.CreateSampler (); + + DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descLayoutGBuff); + uboUpdate.Write (dev, dsGBuff, gbColorRough.Descriptor, + gbEmitMetal.Descriptor, + gbN.Descriptor, + gbPos.Descriptor); + gbColorRough.SetName ("GBuffColorRough"); + gbEmitMetal.SetName ("GBuffEmitMetal"); + gbN.SetName ("GBuffN"); + gbPos.SetName ("GBuffPos"); + } + + protected override void OnResize () { + updateMatrices (); + + if (frameBuffers != null) + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); +#if DEBUG + if (debugFB != null) + for (int i = 0; i < swapChain.ImageCount; ++i) + debugFB[i]?.Dispose (); +#endif + + createGBuff (); + + frameBuffers = new Framebuffer[swapChain.ImageCount]; + debugFB = new Framebuffer[swapChain.ImageCount]; + + for (int i = 0; i < swapChain.ImageCount; ++i) { + frameBuffers[i] = new Framebuffer (renderPass, swapChain.Width, swapChain.Height, new Image[] { + swapChain.images[i], null, gbColorRough, gbEmitMetal, gbN, gbPos}); +#if DEBUG + debugFB[i] = new Framebuffer (debugDraw.RenderPass, swapChain.Width, swapChain.Height,(debugDraw.Samples == VkSampleCountFlags.SampleCount1) + ? new Image[] { swapChain.images[i] } : new Image[] { null, swapChain.images[i] }); + debugFB[i].SetName ("main FB " + i); +#endif + } + + buildCommandBuffers (); + } + + + #region Mouse and keyboard + protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) { + switch (key) { + case Key.F1: + if (modifiers.HasFlag (Modifier.Shift)) + matrices.exposure -= 0.3f; + else + matrices.exposure += 0.3f; + break; + case Key.F2: + if (modifiers.HasFlag (Modifier.Shift)) + matrices.gamma -= 0.1f; + else + matrices.gamma += 0.1f; + break; + case Key.F3: + if (camera.Type == Camera.CamType.FirstPerson) + camera.Type = Camera.CamType.LookAt; + else + camera.Type = Camera.CamType.FirstPerson; + Console.WriteLine ($"camera type = {camera.Type}"); + break; + default: + base.onKeyDown (key, scanCode, modifiers); + return; + } + updateViewRequested = true; + } +#endregion + + protected override void Dispose (bool disposing) { + if (disposing) { + if (!isDisposed) { + dev.WaitIdle (); + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); + + gbColorRough.Dispose (); + gbEmitMetal.Dispose (); + gbN.Dispose (); + gbPos.Dispose (); + + gBuffPipeline.Dispose (); + composePipeline.Dispose (); + + descLayoutMain.Dispose (); + descLayoutModelTextures.Dispose (); + descLayoutGBuff.Dispose (); + + uboMats.Dispose (); + model.Dispose (); + envCube.Dispose (); + + descriptorPool.Dispose (); +#if DEBUG + debugDraw.Dispose (); + timestampQPool?.Dispose (); + statPool?.Dispose (); +#endif + } + } + + base.Dispose (disposing); + } + } +} diff --git a/samples/shadow/mainWithDebugDrawer.cs b/samples/shadow/mainWithDebugDrawer.cs new file mode 100644 index 0000000..f95ed0a --- /dev/null +++ b/samples/shadow/mainWithDebugDrawer.cs @@ -0,0 +1,412 @@ +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using Glfw; +using VK; +using CVKL; + +namespace deferredDebug { + class Program : VkWindow{ + static void Main (string[] args) { + using (Program vke = new Program ()) { + vke.Run (); + } + } + + VkSampleCountFlags samples = VkSampleCountFlags.SampleCount1; + + public struct Matrices { + public Matrix4x4 projection; + public Matrix4x4 view; + public Matrix4x4 model; + public Vector4 lightPos; + public float gamma; + public float exposure; + } + + public Matrices matrices = new Matrices { + lightPos = new Vector4 (1.0f, 0, 0, 1.0f), + gamma = 1.0f, + exposure = 2.0f, + }; + +#if DEBUG + PipelineStatisticsQueryPool statPool; + TimestampQueryPool timestampQPool; + ulong[] results; +#endif + + protected override void configureEnabledFeatures (ref VkPhysicalDeviceFeatures features) { + base.configureEnabledFeatures (ref features); +#if DEBUG + features.pipelineStatisticsQuery = true; +#endif + } + + Program () : base(true) { + camera.Model = Matrix4x4.CreateRotationX (Utils.DegreesToRadians (-90)) * Matrix4x4.CreateRotationY (Utils.DegreesToRadians (180)); + camera.SetRotation (-0.1f,-0.4f); + camera.SetPosition (0, 0, -3); + + init (); + +#if DEBUG + statPool = new PipelineStatisticsQueryPool (dev, + VkQueryPipelineStatisticFlags.InputAssemblyVertices | + VkQueryPipelineStatisticFlags.InputAssemblyPrimitives | + VkQueryPipelineStatisticFlags.ClippingInvocations | + VkQueryPipelineStatisticFlags.ClippingPrimitives | + VkQueryPipelineStatisticFlags.FragmentShaderInvocations); + + timestampQPool = new TimestampQueryPool (dev); +#endif + } + + Framebuffer[] frameBuffers; + Image gbColorRough, gbEmitMetal, gbN, gbPos; + + DescriptorPool descriptorPool; + DescriptorSetLayout descLayoutMain, descLayoutModelTextures, descLayoutGBuff; + DescriptorSet dsMain, dsGBuff; + + Pipeline gBuffPipeline, composePipeline; + + HostBuffer uboMats; + + RenderPass renderPass; + + Model model; + EnvironmentCube envCube; + + DebugDrawPipeline debugDraw; + Framebuffer[] debugFB; + + void init () { + renderPass = new RenderPass (dev); + renderPass.AddAttachment (swapChain.ColorFormat, VkImageLayout.ColorAttachmentOptimal, VkSampleCountFlags.SampleCount1); + renderPass.AddAttachment (dev.GetSuitableDepthFormat(), VkImageLayout.DepthStencilAttachmentOptimal, samples); + renderPass.AddAttachment (VkFormat.R8g8b8a8Unorm, VkImageLayout.ColorAttachmentOptimal); + renderPass.AddAttachment (VkFormat.R8g8b8a8Unorm, VkImageLayout.ColorAttachmentOptimal); + renderPass.AddAttachment (VkFormat.R16g16b16a16Sfloat, VkImageLayout.ColorAttachmentOptimal); + renderPass.AddAttachment (VkFormat.R16g16b16a16Sfloat, VkImageLayout.ColorAttachmentOptimal); + + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { depthStencil = new VkClearDepthStencilValue (1.0f, 0) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + renderPass.ClearValues.Add (new VkClearValue { color = new VkClearColorValue (0.0f, 0.0f, 0.0f) }); + + SubPass[] subpass = { new SubPass (), new SubPass () }; + subpass[0].AddColorReference ( new VkAttachmentReference (2, VkImageLayout.ColorAttachmentOptimal), + new VkAttachmentReference (3, VkImageLayout.ColorAttachmentOptimal), + new VkAttachmentReference (4, VkImageLayout.ColorAttachmentOptimal), + new VkAttachmentReference (5, VkImageLayout.ColorAttachmentOptimal)); + subpass[0].SetDepthReference (1, VkImageLayout.DepthStencilAttachmentOptimal); + + subpass[1].AddColorReference (0, VkImageLayout.ColorAttachmentOptimal); + subpass[1].AddInputReference ( new VkAttachmentReference (2, VkImageLayout.ShaderReadOnlyOptimal), + new VkAttachmentReference (3, VkImageLayout.ShaderReadOnlyOptimal), + new VkAttachmentReference (4, VkImageLayout.ShaderReadOnlyOptimal), + new VkAttachmentReference (5, VkImageLayout.ShaderReadOnlyOptimal)); + renderPass.AddSubpass (subpass); + + renderPass.AddDependency (Vk.SubpassExternal, 0, + VkPipelineStageFlags.BottomOfPipe, VkPipelineStageFlags.ColorAttachmentOutput, + VkAccessFlags.MemoryRead, VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite); + renderPass.AddDependency (0, 1, + VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.FragmentShader, + VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.ShaderRead); + renderPass.AddDependency (1, Vk.SubpassExternal, + VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.BottomOfPipe, + VkAccessFlags.ColorAttachmentRead | VkAccessFlags.ColorAttachmentWrite, VkAccessFlags.MemoryRead); + + + descriptorPool = new DescriptorPool (dev, 3, + new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer, 2), + new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler, 3), + new VkDescriptorPoolSize (VkDescriptorType.InputAttachment, 4) + ); + + descLayoutMain = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex | VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler)); + + descLayoutModelTextures = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (4, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler) + ); + + descLayoutGBuff = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment), + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment), + new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.InputAttachment)); + + dsMain = descriptorPool.Allocate (descLayoutMain); + dsGBuff = descriptorPool.Allocate (descLayoutGBuff); + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, samples); + cfg.Layout = new PipelineLayout (dev, descLayoutMain, descLayoutModelTextures, descLayoutGBuff); + cfg.Layout.AddPushConstants ( + new VkPushConstantRange (VkShaderStageFlags.Vertex, (uint)Marshal.SizeOf ()), + new VkPushConstantRange (VkShaderStageFlags.Fragment, sizeof(int), 64) + ); + cfg.RenderPass = renderPass; + cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false)); + cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false)); + cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false)); + + cfg.AddVertexBinding (0); + cfg.SetVertexAttributes (0, VkFormat.R32g32b32Sfloat, VkFormat.R32g32b32Sfloat, VkFormat.R32g32Sfloat); + cfg.AddShader (VkShaderStageFlags.Vertex, "shaders/pbrtest.vert.spv"); + cfg.AddShader (VkShaderStageFlags.Fragment, "shaders/GBuffPbr.frag.spv"); + + gBuffPipeline = new GraphicPipeline (cfg); + cfg.blendAttachments.Clear (); + cfg.blendAttachments.Add (new VkPipelineColorBlendAttachmentState (false)); + cfg.ResetShadersAndVerticesInfos (); + cfg.SubpassIndex = 1; + cfg.Layout = gBuffPipeline.Layout; + cfg.depthStencilState.depthTestEnable = false; + cfg.depthStencilState.depthWriteEnable = false; + cfg.AddShader (VkShaderStageFlags.Vertex, "shaders/FullScreenQuad.vert.spv"); + cfg.AddShader (VkShaderStageFlags.Fragment, "shaders/pbrtest.frag.spv"); + composePipeline = new GraphicPipeline (cfg); + + envCube = new EnvironmentCube (presentQueue, renderPass); + + uboMats = new HostBuffer (dev, VkBufferUsageFlags.UniformBuffer, (ulong)Marshal.SizeOf () * 2); + uboMats.Map ();//permanent map + + DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descLayoutMain); + uboUpdate.Write (dev, dsMain, uboMats.Descriptor, + envCube.lutBrdf.Descriptor, + envCube.irradianceCube.Descriptor, + envCube.prefilterCube.Descriptor); + uboMats.Descriptor.offset = (ulong)Marshal.SizeOf (); + envCube.WriteDesc (uboMats.Descriptor); +#if DEBUG + debugDraw = new DebugDrawPipeline (dev, descLayoutMain, swapChain.ColorFormat); + debugDraw.AddLine (Vector3.Zero, new Vector3(matrices.lightPos.X,matrices.lightPos.Y,matrices.lightPos.Z)*3, 1, 1, 1); + debugDraw.AddLine (Vector3.Zero, Vector3.UnitX, 1, 0, 0); + debugDraw.AddLine (Vector3.Zero, Vector3.UnitY, 0, 1, 0); + debugDraw.AddLine (Vector3.Zero, Vector3.UnitZ, 0, 0, 1); +#endif + + + model = new Model (presentQueue, "../data/models/DamagedHelmet/glTF/DamagedHelmet.gltf"); + //model = new Model (presentQueue, "../data/models/chess.gltf"); + //model = new Model (presentQueue, "../data/models/Sponza/glTF/Sponza.gltf"); + //model = new Model (dev, presentQueue, "../data/models/icosphere.gltf"); + //model = new Model (dev, presentQueue, cmdPool, "../data/models/cube.gltf"); + model.WriteMaterialsDescriptorSets (descLayoutModelTextures, + VK.AttachmentType.Color, + VK.AttachmentType.Normal, + VK.AttachmentType.AmbientOcclusion, + VK.AttachmentType.PhysicalProps, + VK.AttachmentType.Emissive); + } + + void buildCommandBuffers () { + for (int i = 0; i < swapChain.ImageCount; ++i) { + cmds[i]?.Free (); + cmds[i] = cmdPool.AllocateCommandBuffer (); + cmds[i].Start (); + +#if DEBUG + statPool.Begin (cmds[i]); + recordDraw (cmds[i], frameBuffers[i]); + statPool.End (cmds[i]); + + debugDraw.RecordDraw (cmds[i], debugFB[i], camera); +#else + recordDraw (cmds[i], frameBuffers[i]); +#endif + + cmds[i].End (); + } + } + void recordDraw (CommandBuffer cmd, Framebuffer fb) { + renderPass.Begin (cmd, fb); + + cmd.SetViewport (fb.Width, fb.Height); + cmd.SetScissor (fb.Width, fb.Height); + + cmd.BindDescriptorSet (gBuffPipeline.Layout, dsMain); + gBuffPipeline.Bind (cmd); + model.Bind (cmd); + model.DrawAll (cmd, gBuffPipeline.Layout); + + renderPass.BeginSubPass (cmd); + + cmd.BindDescriptorSet (composePipeline.Layout, dsGBuff, 2); + composePipeline.Bind (cmd); + + cmd.Draw (3, 1, 0, 0); + + renderPass.End (cmd); + } + +#region update + void updateMatrices () { + camera.AspectRatio = (float)swapChain.Width / swapChain.Height; + + matrices.projection = camera.Projection; + matrices.view = camera.View; + matrices.model = camera.Model; + uboMats.Update (matrices, (uint)Marshal.SizeOf ()); + matrices.view *= Matrix4x4.CreateTranslation (-matrices.view.Translation); + matrices.model = Matrix4x4.Identity; + uboMats.Update (matrices, (uint)Marshal.SizeOf (), (uint)Marshal.SizeOf ()); + } + + public override void UpdateView () { + updateMatrices (); + updateViewRequested = false; + } + public override void Update () { +#if DEBUG + results = statPool.GetResults (); +#endif + } +#endregion + + + + void createGBuff () { + gbColorRough?.Dispose (); + gbEmitMetal?.Dispose (); + gbN?.Dispose (); + gbPos?.Dispose (); + + gbColorRough = new Image (dev, VkFormat.R8g8b8a8Unorm, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height); + gbEmitMetal = new Image (dev, VkFormat.R8g8b8a8Unorm, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height); + gbN = new Image (dev, VkFormat.R16g16b16a16Sfloat, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height); + gbPos = new Image (dev, VkFormat.R16g16b16a16Sfloat, VkImageUsageFlags.InputAttachment | VkImageUsageFlags.ColorAttachment, VkMemoryPropertyFlags.DeviceLocal, swapChain.Width, swapChain.Height); + + gbColorRough.CreateView (); + gbColorRough.CreateSampler (); + gbEmitMetal.CreateView (); + gbEmitMetal.CreateSampler (); + gbN.CreateView (); + gbN.CreateSampler (); + gbPos.CreateView (); + gbPos.CreateSampler (); + + DescriptorSetWrites uboUpdate = new DescriptorSetWrites (descLayoutGBuff); + uboUpdate.Write (dev, dsGBuff, gbColorRough.Descriptor, + gbEmitMetal.Descriptor, + gbN.Descriptor, + gbPos.Descriptor); + gbColorRough.SetName ("GBuffColorRough"); + gbEmitMetal.SetName ("GBuffEmitMetal"); + gbN.SetName ("GBuffN"); + gbPos.SetName ("GBuffPos"); + } + + protected override void OnResize () { + updateMatrices (); + + if (frameBuffers != null) + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); +#if DEBUG + if (debugFB != null) + for (int i = 0; i < swapChain.ImageCount; ++i) + debugFB[i]?.Dispose (); +#endif + + createGBuff (); + + frameBuffers = new Framebuffer[swapChain.ImageCount]; + debugFB = new Framebuffer[swapChain.ImageCount]; + + for (int i = 0; i < swapChain.ImageCount; ++i) { + frameBuffers[i] = new Framebuffer (renderPass, swapChain.Width, swapChain.Height, new Image[] { + swapChain.images[i], null, gbColorRough, gbEmitMetal, gbN, gbPos}); +#if DEBUG + debugFB[i] = new Framebuffer (debugDraw.RenderPass, swapChain.Width, swapChain.Height,(debugDraw.Samples == VkSampleCountFlags.SampleCount1) + ? new Image[] { swapChain.images[i] } : new Image[] { null, swapChain.images[i] }); + debugFB[i].SetName ("main FB " + i); +#endif + } + + buildCommandBuffers (); + } + + + #region Mouse and keyboard + protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) { + switch (key) { + case Key.F1: + if (modifiers.HasFlag (Modifier.Shift)) + matrices.exposure -= 0.3f; + else + matrices.exposure += 0.3f; + break; + case Key.F2: + if (modifiers.HasFlag (Modifier.Shift)) + matrices.gamma -= 0.1f; + else + matrices.gamma += 0.1f; + break; + case Key.F3: + if (camera.Type == Camera.CamType.FirstPerson) + camera.Type = Camera.CamType.LookAt; + else + camera.Type = Camera.CamType.FirstPerson; + Console.WriteLine ($"camera type = {camera.Type}"); + break; + default: + base.onKeyDown (key, scanCode, modifiers); + return; + } + updateViewRequested = true; + } +#endregion + + protected override void Dispose (bool disposing) { + if (disposing) { + if (!isDisposed) { + dev.WaitIdle (); + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); + + gbColorRough.Dispose (); + gbEmitMetal.Dispose (); + gbN.Dispose (); + gbPos.Dispose (); + + gBuffPipeline.Dispose (); + composePipeline.Dispose (); + + descLayoutMain.Dispose (); + descLayoutModelTextures.Dispose (); + descLayoutGBuff.Dispose (); + + uboMats.Dispose (); + model.Dispose (); + envCube.Dispose (); + + descriptorPool.Dispose (); +#if DEBUG + foreach (Framebuffer fb in debugFB) + fb.Dispose (); + + debugDraw.Dispose (); + timestampQPool?.Dispose (); + statPool?.Dispose (); +#endif + } + } + + base.Dispose (disposing); + } + } +} diff --git a/samples/shadow/modelWithVkvgStats.cs b/samples/shadow/modelWithVkvgStats.cs new file mode 100644 index 0000000..f4ea123 --- /dev/null +++ b/samples/shadow/modelWithVkvgStats.cs @@ -0,0 +1,359 @@ +using System; +using System.Numerics; +using System.Runtime.InteropServices; +using Glfw; +using CVKL; +using VK; +using static CVKL.Camera; + +namespace modelWithVkvgStats { + class Program : VkWindow { + + PipelineStatisticsQueryPool statPool; + TimestampQueryPool timestampQPool; + + ulong[] results; + + protected override void configureEnabledFeatures (ref VkPhysicalDeviceFeatures features) { + base.configureEnabledFeatures (ref features); + features.pipelineStatisticsQuery = true; + } + public struct Matrices { + public Matrix4x4 projection; + public Matrix4x4 view; + public Matrix4x4 model; + public Vector4 lightPos; + public float gamma; + public float exposure; + } + + public Matrices matrices = new Matrices { + lightPos = new Vector4 (0.0f, 0.0f, -2.0f, 1.0f), + gamma = 1.0f, + exposure = 2.0f, + }; + + HostBuffer uboMats; + + DescriptorPool descriptorPool; + DescriptorSetLayout descLayoutMatrix; + DescriptorSetLayout descLayoutTextures; + DescriptorSet dsMats; + + GraphicPipeline pipeline; + GraphicPipeline uiPipeline; + Framebuffer[] frameBuffers; + + Model model; + + vkvg.Device vkvgDev; + vkvg.Surface vkvgSurf; + Image vkvgImage; + + void vkvgDraw () { + + using (vkvg.Context ctx = new vkvg.Context (vkvgSurf)) { + ctx.Operator = vkvg.Operator.Clear; + ctx.Paint (); + ctx.Operator = vkvg.Operator.Over; + + ctx.LineWidth = 1; + ctx.SetSource (0.1, 0.1, 0.1, 0.3); + ctx.Rectangle (5.5, 5.5, 400, 250); + ctx.FillPreserve (); + ctx.Flush (); + ctx.SetSource (0.8, 0.8, 0.8); + ctx.Stroke (); + + ctx.FontFace = "mono"; + ctx.FontSize = 10; + int x = 16; + int y = 40, dy = 16; + ctx.MoveTo (x, y); + ctx.ShowText (string.Format ($"fps: {fps,5} ")); + ctx.MoveTo (x + 200, y - 0.5); + ctx.Rectangle (x + 200, y - 8.5, 0.1 * fps, 10); + ctx.SetSource (0.1, 0.9, 0.1); + ctx.Fill (); + ctx.SetSource (0.8, 0.8, 0.8); + y += dy; + ctx.MoveTo (x, y); + ctx.ShowText (string.Format ($"Exposure:{matrices.exposure,5} ")); + y += dy; + ctx.MoveTo (x, y); + ctx.ShowText (string.Format ($"Gamma: {matrices.gamma,5} ")); + if (results == null) + return; + + y += dy*2; + ctx.MoveTo (x, y); + ctx.ShowText ("Pipeline Statistics"); + ctx.MoveTo (x-2, 2.5+y); + ctx.LineTo (x+160, 2.5+y); + ctx.Stroke (); + y += 4; + x += 20; + + for (int i = 0; i < statPool.RequestedStats.Length; i++) { + y += dy; + ctx.MoveTo (x, y); + ctx.ShowText (string.Format ($"{statPool.RequestedStats[i].ToString(),-30} :{results[i],12:0,0} ")); + } + + y += dy; + ctx.MoveTo (x, y); + ctx.ShowText (string.Format ($"{"Elapsed microsecond",-20} :{timestampQPool.ElapsedMiliseconds:0.0000} ")); + } + } + + Program () : base () { + vkvgDev = new vkvg.Device (instance.Handle, phy.Handle, dev.VkDev.Handle, presentQueue.qFamIndex, + vkvg.SampleCount.Sample_4, presentQueue.index); + + camera.Type = CamType.FirstPerson; + camera.Model = Matrix4x4.CreateScale (0.05f) * Matrix4x4.CreateRotationX((float)Math.PI); + //Camera.SetRotation (-0.1f,-0.4f); + camera.SetPosition (0, 2, -3); + + init (); + + model = new Model (presentQueue, "../data/models/Sponza/glTF/Sponza.gltf"); + model.WriteMaterialsDescriptorSets (descLayoutTextures, + VK.AttachmentType.Color, + VK.AttachmentType.Normal, + VK.AttachmentType.AmbientOcclusion, + VK.AttachmentType.PhysicalProps, + VK.AttachmentType.Emissive); + } + + void init (VkSampleCountFlags samples = VkSampleCountFlags.SampleCount4) { + descriptorPool = new DescriptorPool (dev, 2, + new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer), + new VkDescriptorPoolSize (VkDescriptorType.CombinedImageSampler) + ); + + descLayoutMatrix = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Vertex|VkShaderStageFlags.Fragment, VkDescriptorType.UniformBuffer), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler) + ); + + descLayoutTextures = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (2, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (3, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler), + new VkDescriptorSetLayoutBinding (4, VkShaderStageFlags.Fragment, VkDescriptorType.CombinedImageSampler) + ); + + dsMats = descriptorPool.Allocate (descLayoutMatrix); + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList, samples); + + cfg.Layout = new PipelineLayout (dev, descLayoutMatrix, descLayoutTextures); + cfg.Layout.AddPushConstants ( + new VkPushConstantRange (VkShaderStageFlags.Vertex, (uint)Marshal.SizeOf ()), + new VkPushConstantRange (VkShaderStageFlags.Fragment, (uint)Marshal.SizeOf (), 64) + ); + cfg.RenderPass = new RenderPass (dev, swapChain.ColorFormat, dev.GetSuitableDepthFormat (), samples); + + cfg.AddVertexBinding (0); + cfg.SetVertexAttributes (0, VkFormat.R32g32b32Sfloat, VkFormat.R32g32b32Sfloat, VkFormat.R32g32Sfloat); + + cfg.AddShader (VkShaderStageFlags.Vertex, "shaders/pbrtest.vert.spv"); + cfg.AddShader (VkShaderStageFlags.Fragment, "shaders/pbrtest.frag.spv"); + + pipeline = new GraphicPipeline (cfg); + + cfg.ResetShadersAndVerticesInfos (); + cfg.AddShader (VkShaderStageFlags.Vertex, "shaders/FullScreenQuad.vert.spv"); + cfg.AddShader (VkShaderStageFlags.Fragment, "shaders/simpletexture.frag.spv"); + + cfg.blendAttachments[0] = new VkPipelineColorBlendAttachmentState (true); + + uiPipeline = new GraphicPipeline (cfg); + + uboMats = new HostBuffer (dev, VkBufferUsageFlags.UniformBuffer, (ulong)Marshal.SizeOf()); + uboMats.Map ();//permanent map + + DescriptorSetWrites uboUpdate = new DescriptorSetWrites (dsMats, descLayoutMatrix.Bindings[0]); + uboUpdate.Write (dev, uboMats.Descriptor); + + cfg.Layout.SetName ("Main Pipeline layout"); + uboMats.SetName ("uboMats"); + descriptorPool.SetName ("main pool"); + descLayoutTextures.SetName ("descLayoutTextures"); + + statPool = new PipelineStatisticsQueryPool (dev, + VkQueryPipelineStatisticFlags.InputAssemblyVertices | + VkQueryPipelineStatisticFlags.InputAssemblyPrimitives | + VkQueryPipelineStatisticFlags.ClippingInvocations | + VkQueryPipelineStatisticFlags.ClippingPrimitives | + VkQueryPipelineStatisticFlags.FragmentShaderInvocations); + + timestampQPool = new TimestampQueryPool (dev); + + } + + void buildCommandBuffers () { + for (int i = 0; i < swapChain.ImageCount; ++i) { + cmds[i]?.Free (); + + cmds[i] = cmdPool.AllocateCommandBuffer (); + cmds[i].Start (); + + statPool.Begin (cmds[i]); + cmds[i].BeginRegion ("draw" + i, 0.5f, 1f, 0f); + cmds[i].Handle.SetDebugMarkerName (dev, "cmd Draw" + i); + recordDraw (cmds[i], frameBuffers[i]); + cmds[i].EndRegion (); + statPool.End (cmds[i]); + cmds[i].End (); + } + } + void recordDraw (CommandBuffer cmd, Framebuffer fb) { + cmd.BeginRegion ("models", 0.5f, 1f, 0f); + pipeline.RenderPass.Begin (cmd, fb); + + cmd.SetViewport (fb.Width, fb.Height); + cmd.SetScissor (fb.Width, fb.Height); + + cmd.BindDescriptorSet (pipeline.Layout, dsMats); + pipeline.Bind (cmd); + model.Bind (cmd); + model.DrawAll (cmd, pipeline.Layout); + + cmd.EndRegion (); + cmd.BeginRegion ("vkvg", 0.5f, 1f, 0f); + uiPipeline.Bind (cmd); + + timestampQPool.Start (cmd); + + vkvgImage.SetLayout (cmd, VkImageAspectFlags.Color, VkImageLayout.ColorAttachmentOptimal, VkImageLayout.ShaderReadOnlyOptimal, + VkPipelineStageFlags.ColorAttachmentOutput, VkPipelineStageFlags.FragmentShader); + + cmd.Draw (3, 1, 0, 0); + + vkvgImage.SetLayout (cmd, VkImageAspectFlags.Color, VkImageLayout.ShaderReadOnlyOptimal, VkImageLayout.ColorAttachmentOptimal, + VkPipelineStageFlags.FragmentShader, VkPipelineStageFlags.BottomOfPipe); + + timestampQPool.End (cmd); + + pipeline.RenderPass.End (cmd); + cmd.EndRegion (); + } + + #region update + void updateMatrices () { + + camera.AspectRatio = (float)swapChain.Width / swapChain.Height; + + matrices.projection = camera.Projection; + matrices.view = camera.View; + matrices.model = camera.Model; + uboMats.Update (matrices, (uint)Marshal.SizeOf ()); + } + + public override void UpdateView () { + updateMatrices (); + updateViewRequested = false; + } + public override void Update () { + results = statPool.GetResults (); + vkvgDraw (); + } + #endregion + + #region mouse and keyboard + protected override void onKeyDown (Key key, int scanCode, Modifier modifiers) { + switch (key) { + case Key.F1: + if (modifiers.HasFlag (Modifier.Shift)) + matrices.exposure -= 0.3f; + else + matrices.exposure += 0.3f; + break; + case Key.F2: + if (modifiers.HasFlag (Modifier.Shift)) + matrices.gamma -= 0.1f; + else + matrices.gamma += 0.1f; + break; + case Key.F3: + if (camera.Type == CamType.FirstPerson) + camera.Type = CamType.LookAt; + else + camera.Type = CamType.FirstPerson; + break; + default: + base.onKeyDown (key, scanCode, modifiers); + return; + } + updateViewRequested = true; + } + #endregion + + protected override void OnResize () { + + vkvgImage?.Dispose (); + vkvgSurf?.Dispose (); + vkvgSurf = new vkvg.Surface (vkvgDev, (int)swapChain.Width, (int)swapChain.Height); + vkvgImage = new Image (dev, new VkImage ((ulong)vkvgSurf.VkImage.ToInt64 ()), VkFormat.B8g8r8a8Unorm, + VkImageUsageFlags.ColorAttachment, (uint)vkvgSurf.Width, (uint)vkvgSurf.Height); + vkvgImage.CreateView (VkImageViewType.ImageView2D, VkImageAspectFlags.Color); + vkvgImage.CreateSampler (VkFilter.Nearest,VkFilter.Nearest, VkSamplerMipmapMode.Nearest, VkSamplerAddressMode.ClampToBorder); + + vkvgImage.Descriptor.imageLayout = VkImageLayout.ShaderReadOnlyOptimal; + DescriptorSetWrites uboUpdate = new DescriptorSetWrites (dsMats, descLayoutMatrix.Bindings[1]); + uboUpdate.Write (dev, vkvgImage.Descriptor); + + updateMatrices (); + + if (frameBuffers!=null) + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); + + frameBuffers = new Framebuffer[swapChain.ImageCount]; + + for (int i = 0; i < swapChain.ImageCount; ++i) { + frameBuffers[i] = new Framebuffer (pipeline.RenderPass, swapChain.Width, swapChain.Height, + (pipeline.Samples == VkSampleCountFlags.SampleCount1) ? new Image[] { + swapChain.images[i], + null + } : new Image[] { + null, + null, + swapChain.images[i] + }); + frameBuffers[i].SetName ("main FB " + i); + + } + + buildCommandBuffers (); + } + + protected override void Dispose (bool disposing) { + if (disposing) { + if (!isDisposed) { + dev.WaitIdle (); + for (int i = 0; i < swapChain.ImageCount; ++i) + frameBuffers[i]?.Dispose (); + model.Dispose (); + pipeline.Dispose (); + descLayoutMatrix.Dispose (); + descLayoutTextures.Dispose (); + descriptorPool.Dispose (); + + uboMats.Dispose (); + + vkvgSurf?.Dispose (); + vkvgDev.Dispose (); + + timestampQPool.Dispose (); + statPool.Dispose (); + } + } + + base.Dispose (disposing); + } + } +} diff --git a/samples/shadow/shaders/GBuffPbr.frag b/samples/shadow/shaders/GBuffPbr.frag new file mode 100644 index 0000000..9e30cab --- /dev/null +++ b/samples/shadow/shaders/GBuffPbr.frag @@ -0,0 +1,140 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +#define MANUAL_SRGB 0 + +struct Material { + vec4 baseColorFactor; + vec4 emissiveFactor; + vec4 diffuseFactor; + vec4 specularFactor; + float workflow; + uint tex0; + uint tex1; + float metallicFactor; + float roughnessFactor; + float alphaMask; + float alphaMaskCutoff; + int pad0; +}; + +#include "GBuffPbrCommon.inc" +#include "tonemap.inc" + +// Material bindings +layout (set = 2, binding = 0) uniform sampler2D colorMap; +layout (set = 2, binding = 1) uniform sampler2D physicalDescriptorMap; +layout (set = 2, binding = 2) uniform sampler2D normalMap; +layout (set = 2, binding = 3) uniform sampler2D aoMap; +layout (set = 2, binding = 4) uniform sampler2D emissiveMap; + +// Find the normal for this fragment, pulling either from a predefined normal map +// or from the interpolated mesh normal and tangent attributes. +vec3 getNormal() +{ + vec3 tangentNormal; + // Perturb normal, see http://www.thetenthplanet.de/archives/1180 + if ((materials[materialIdx].tex0 & MAP_NORMAL) == MAP_NORMAL) + tangentNormal = texture(normalMap, inUV0).xyz * 2.0 - 1.0; + else if ((materials[materialIdx].tex1 & MAP_NORMAL) == MAP_NORMAL) + tangentNormal = texture(normalMap, inUV1).xyz * 2.0 - 1.0; + else + return normalize(inNormal); + + vec3 q1 = dFdx(inWorldPos); + vec3 q2 = dFdy(inWorldPos); + vec2 st1 = dFdx(inUV0); + vec2 st2 = dFdy(inUV0); + + vec3 N = normalize(inNormal); + vec3 T = normalize(q1 * st2.t - q2 * st1.t); + vec3 B = -normalize(cross(N, T)); + mat3 TBN = mat3(T, B, N); + + return normalize(TBN * tangentNormal); +} + +void main() +{ + float perceptualRoughness; + float metallic; + vec4 baseColor; + vec3 emissive = vec3(0); + + baseColor = materials[materialIdx].baseColorFactor; + + if (materials[materialIdx].workflow == PBR_WORKFLOW_METALLIC_ROUGHNESS) { + perceptualRoughness = materials[materialIdx].roughnessFactor; + metallic = materials[materialIdx].metallicFactor; + // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel. + // This layout intentionally reserves the 'r' channel for (optional) occlusion map data + if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS){ + perceptualRoughness *= texture(physicalDescriptorMap, inUV0).g; + metallic *= texture(physicalDescriptorMap, inUV0).b; + }else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS){ + perceptualRoughness *= texture(physicalDescriptorMap, inUV1).g; + metallic *= texture(physicalDescriptorMap, inUV1).b; + } + perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0); + metallic = clamp(metallic, 0.0, 1.0); + + // The albedo may be defined from a base texture or a flat color + if ((materials[materialIdx].tex0 & MAP_COLOR) == MAP_COLOR) + baseColor *= SRGBtoLINEAR(texture(colorMap, inUV0)); + else if ((materials[materialIdx].tex1 & MAP_COLOR) == MAP_COLOR) + baseColor *= SRGBtoLINEAR(texture(colorMap, inUV1)); + } + + if (materials[materialIdx].alphaMask == 1.0f) { + if (baseColor.a < materials[materialIdx].alphaMaskCutoff) + discard; + } + + if (materials[materialIdx].workflow == PBR_WORKFLOW_SPECULAR_GLOSINESS) { + // Values from specular glossiness workflow are converted to metallic roughness + if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS) + perceptualRoughness = 1.0 - texture(physicalDescriptorMap, inUV0).a; + else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS) + perceptualRoughness = 1.0 - texture(physicalDescriptorMap, inUV1).a; + else + perceptualRoughness = 0.0; + + const float epsilon = 1e-6; + + vec4 diffuse = SRGBtoLINEAR(texture(colorMap, inUV0)); + vec3 specular = SRGBtoLINEAR(texture(physicalDescriptorMap, inUV0)).rgb; + + float maxSpecular = max(max(specular.r, specular.g), specular.b); + + // Convert metallic value from specular glossiness inputs + metallic = convertMetallic(diffuse.rgb, specular, maxSpecular); + + vec3 baseColorDiffusePart = diffuse.rgb * ((1.0 - maxSpecular) / (1 - c_MinRoughness) / max(1 - metallic, epsilon)) * materials[materialIdx].diffuseFactor.rgb; + vec3 baseColorSpecularPart = specular - (vec3(c_MinRoughness) * (1 - metallic) * (1 / max(metallic, epsilon))) * materials[materialIdx].specularFactor.rgb; + baseColor = vec4(mix(baseColorDiffusePart, baseColorSpecularPart, metallic * metallic), diffuse.a); + + } + + const float u_OcclusionStrength = 1.0f; + const float u_EmissiveFactor = 1.0f; + float ao = 1.0f; + + if ((materials[materialIdx].tex0 & MAP_EMISSIVE) == MAP_EMISSIVE) + emissive = SRGBtoLINEAR(texture(emissiveMap, inUV0)).rgb * u_EmissiveFactor; + else if ((materials[materialIdx].tex1 & MAP_EMISSIVE) == MAP_EMISSIVE) + emissive = SRGBtoLINEAR(texture(emissiveMap, inUV1)).rgb * u_EmissiveFactor; + + if ((materials[materialIdx].tex0 & MAP_AO) == MAP_AO) + ao = texture(aoMap, inUV0).r; + else if ((materials[materialIdx].tex1 & MAP_AO) == MAP_AO) + ao = texture(aoMap, inUV1).r; + + vec3 n = getNormal(); + vec3 p = inWorldPos; + + outColorRough = vec4 (baseColor.rgb, perceptualRoughness); + outEmitMetal = vec4 (emissive, metallic); + outN_AO = vec4 (n, ao); + outPos = vec4 (p, linearDepth(gl_FragCoord.z)); +} \ No newline at end of file diff --git a/samples/shadow/shaders/GBuffPbr.vert b/samples/shadow/shaders/GBuffPbr.vert new file mode 100644 index 0000000..7cafe49 --- /dev/null +++ b/samples/shadow/shaders/GBuffPbr.vert @@ -0,0 +1,37 @@ +#version 450 + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec3 inNormal; +layout (location = 2) in vec2 inUV0; +layout (location = 3) in vec2 inUV1; + +layout (set = 0, binding = 0) uniform UBO { + mat4 projection; + mat4 model; + mat4 view; +} ubo; + +layout(push_constant) uniform PushConsts { + mat4 model; +} pc; + +layout (location = 0) out vec3 outWorldPos; +layout (location = 1) out vec3 outNormal; +layout (location = 2) out vec2 outUV0; +layout (location = 3) out vec2 outUV1; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + vec4 locPos = ubo.model * pc.model * vec4(inPos, 1.0); + outNormal = normalize(transpose(inverse(mat3(ubo.model * pc.model))) * inNormal); + + outWorldPos = locPos.xyz; + outUV0 = inUV0; + outUV1 = inUV1; + gl_Position = ubo.projection * ubo.view * vec4(outWorldPos, 1.0); +} diff --git a/samples/shadow/shaders/GBuffPbrCommon.inc b/samples/shadow/shaders/GBuffPbrCommon.inc new file mode 100644 index 0000000..4146fa5 --- /dev/null +++ b/samples/shadow/shaders/GBuffPbrCommon.inc @@ -0,0 +1,56 @@ +layout (constant_id = 0) const float NEAR_PLANE = 0.1f; +layout (constant_id = 1) const float FAR_PLANE = 256.0f; +layout (constant_id = 2) const int MAT_COUNT = 1; + +const float M_PI = 3.141592653589793; +const float c_MinRoughness = 0.04; + +const float PBR_WORKFLOW_METALLIC_ROUGHNESS = 1.0; +const float PBR_WORKFLOW_SPECULAR_GLOSINESS = 2.0f; + +const uint MAP_COLOR = 0x1; +const uint MAP_NORMAL = 0x2; +const uint MAP_AO = 0x4; +const uint MAP_METAL = 0x8; +const uint MAP_ROUGHNESS = 0x10; +const uint MAP_METALROUGHNESS = 0x20; +const uint MAP_EMISSIVE = 0x40; + +layout (location = 0) in vec3 inWorldPos; +layout (location = 1) in vec3 inNormal; +layout (location = 2) in vec2 inUV0; +layout (location = 3) in vec2 inUV1; + +layout (set = 0, binding = 5) uniform UBOMaterials { + Material materials[MAT_COUNT]; +}; + +layout (push_constant) uniform PushCsts { + layout(offset = 64) + int materialIdx; +}; + +layout (location = 0) out vec4 outColorRough; +layout (location = 1) out vec4 outEmitMetal; +layout (location = 2) out vec4 outN_AO; +layout (location = 3) out vec4 outPos; + +// Gets metallic factor from specular glossiness workflow inputs +float convertMetallic(vec3 diffuse, vec3 specular, float maxSpecular) { + float perceivedDiffuse = sqrt(0.299 * diffuse.r * diffuse.r + 0.587 * diffuse.g * diffuse.g + 0.114 * diffuse.b * diffuse.b); + float perceivedSpecular = sqrt(0.299 * specular.r * specular.r + 0.587 * specular.g * specular.g + 0.114 * specular.b * specular.b); + if (perceivedSpecular < c_MinRoughness) { + return 0.0; + } + float a = c_MinRoughness; + float b = perceivedDiffuse * (1.0 - maxSpecular) / (1.0 - c_MinRoughness) + perceivedSpecular - 2.0 * c_MinRoughness; + float c = c_MinRoughness - perceivedSpecular; + float D = max(b * b - 4.0 * a * c, 0.0); + return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0); +} + +float linearDepth(float depth) +{ + float z = depth * 2.0f - 1.0f; + return (2.0f * NEAR_PLANE * FAR_PLANE) / (FAR_PLANE + NEAR_PLANE - z * (FAR_PLANE - NEAR_PLANE)); +} \ No newline at end of file diff --git a/samples/shadow/shaders/GBuffPbrTexArray.frag b/samples/shadow/shaders/GBuffPbrTexArray.frag new file mode 100644 index 0000000..cddf49f --- /dev/null +++ b/samples/shadow/shaders/GBuffPbrTexArray.frag @@ -0,0 +1,142 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_ARB_shading_language_420pack : enable + +#define MANUAL_SRGB 0 + +struct Material { + vec4 baseColorFactor; + vec4 emissiveFactor; + vec4 diffuseFactor; + vec4 specularFactor; + + float workflow; + uint tex0; + uint tex1; + int baseColorTex; + + int physicalDescTex; + int normalTex; + int occlusionTex; + int emissiveTex; + + float metallicFactor; + float roughnessFactor; + float alphaMask; + float alphaMaskCutoff; +}; + +#include "GBuffPbrCommon.inc" +#include "tonemap.inc" + +layout (set = 0, binding = 7) uniform sampler2DArray texArray; + +// Find the normal for this fragment, pulling either from a predefined normal map +// or from the interpolated mesh normal and tangent attributes. +vec3 getNormal() +{ + vec3 tangentNormal; + // Perturb normal, see http://www.thetenthplanet.de/archives/1180 + if ((materials[materialIdx].tex0 & MAP_NORMAL) == MAP_NORMAL) + tangentNormal = texture(texArray, vec3(inUV0, materials[materialIdx].normalTex)).xyz * 2.0 - 1.0; + else if ((materials[materialIdx].tex1 & MAP_NORMAL) == MAP_NORMAL) + tangentNormal = texture(texArray, vec3(inUV1, materials[materialIdx].normalTex)).xyz * 2.0 - 1.0; + else + return normalize(inNormal); + + vec3 q1 = dFdx(inWorldPos); + vec3 q2 = dFdy(inWorldPos); + vec2 st1 = dFdx(inUV0); + vec2 st2 = dFdy(inUV0); + + vec3 N = normalize(inNormal); + vec3 T = normalize(q1 * st2.t - q2 * st1.t); + vec3 B = -normalize(cross(N, T)); + mat3 TBN = mat3(T, B, N); + + return normalize(TBN * tangentNormal); +} + +void main() +{ + float perceptualRoughness; + float metallic; + vec4 baseColor; + vec3 emissive = vec3(0); + + baseColor = materials[materialIdx].baseColorFactor; + + if (materials[materialIdx].workflow == PBR_WORKFLOW_METALLIC_ROUGHNESS) { + perceptualRoughness = materials[materialIdx].roughnessFactor; + metallic = materials[materialIdx].metallicFactor; + // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel. + // This layout intentionally reserves the 'r' channel for (optional) occlusion map data + if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS){ + perceptualRoughness *= texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex)).g; + metallic *= texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex)).b; + }else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS){ + perceptualRoughness *= texture(texArray, vec3(inUV1, materials[materialIdx].physicalDescTex)).g; + metallic *= texture(texArray, vec3(inUV1, materials[materialIdx].physicalDescTex)).b; + } + perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0); + metallic = clamp(metallic, 0.0, 1.0); + + // The albedo may be defined from a base texture or a flat color + if ((materials[materialIdx].tex0 & MAP_COLOR) == MAP_COLOR) + baseColor *= SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].baseColorTex))); + else if ((materials[materialIdx].tex1 & MAP_COLOR) == MAP_COLOR) + baseColor *= SRGBtoLINEAR(texture(texArray, vec3(inUV1, materials[materialIdx].baseColorTex))); + } + + if (materials[materialIdx].alphaMask == 1.0f) { + if (baseColor.a < materials[materialIdx].alphaMaskCutoff) + discard; + } + + if (materials[materialIdx].workflow == PBR_WORKFLOW_SPECULAR_GLOSINESS) { + // Values from specular glossiness workflow are converted to metallic roughness + if ((materials[materialIdx].tex0 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS) + perceptualRoughness = 1.0 - texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex)).a; + else if ((materials[materialIdx].tex1 & MAP_METALROUGHNESS) == MAP_METALROUGHNESS) + perceptualRoughness = 1.0 - texture(texArray, vec3(inUV1, materials[materialIdx].physicalDescTex)).a; + else + perceptualRoughness = 0.0; + + const float epsilon = 1e-6; + + vec4 diffuse = SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].baseColorTex))); + vec3 specular = SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].physicalDescTex))).rgb; + + float maxSpecular = max(max(specular.r, specular.g), specular.b); + + // Convert metallic value from specular glossiness inputs + metallic = convertMetallic(diffuse.rgb, specular, maxSpecular); + + vec3 baseColorDiffusePart = diffuse.rgb * ((1.0 - maxSpecular) / (1 - c_MinRoughness) / max(1 - metallic, epsilon)) * materials[materialIdx].diffuseFactor.rgb; + vec3 baseColorSpecularPart = specular - (vec3(c_MinRoughness) * (1 - metallic) * (1 / max(metallic, epsilon))) * materials[materialIdx].specularFactor.rgb; + baseColor = vec4(mix(baseColorDiffusePart, baseColorSpecularPart, metallic * metallic), diffuse.a); + + } + + const float u_OcclusionStrength = 1.0f; + const float u_EmissiveFactor = 1.0f; + float ao = 1.0f; + + if ((materials[materialIdx].tex0 & MAP_EMISSIVE) == MAP_EMISSIVE) + emissive = SRGBtoLINEAR(texture(texArray, vec3(inUV0, materials[materialIdx].emissiveTex))).rgb * u_EmissiveFactor; + else if ((materials[materialIdx].tex1 & MAP_EMISSIVE) == MAP_EMISSIVE) + emissive = SRGBtoLINEAR(texture(texArray, vec3(inUV1, materials[materialIdx].emissiveTex))).rgb * u_EmissiveFactor; + + if ((materials[materialIdx].tex0 & MAP_AO) == MAP_AO) + ao = texture(texArray, vec3(inUV0, materials[materialIdx].occlusionTex)).r; + else if ((materials[materialIdx].tex1 & MAP_AO) == MAP_AO) + ao = texture(texArray, vec3(inUV1, materials[materialIdx].occlusionTex)).r; + + vec3 n = getNormal(); + vec3 p = inWorldPos; + + outColorRough = vec4 (baseColor.rgb, perceptualRoughness); + outEmitMetal = vec4 (emissive, metallic); + outN_AO = vec4 (n, ao); + outPos = vec4 (p, linearDepth(gl_FragCoord.z)); +} \ No newline at end of file diff --git a/samples/shadow/shaders/bloom.comp b/samples/shadow/shaders/bloom.comp new file mode 100644 index 0000000..e2b1318 --- /dev/null +++ b/samples/shadow/shaders/bloom.comp @@ -0,0 +1,60 @@ +#version 450 + +layout (local_size_x = 16, local_size_y = 16) in; +layout (binding = 0, rgba16f) uniform readonly image2D inputImage; +layout (binding = 1, rgba16f) uniform image2D resultImage; + +layout(push_constant) uniform PushConsts { + vec2 texSize; + int dir; + float scale; + float strength; +} pc; + +void main(void) +{ + // From the OpenGL Super bible + const float weights[] = float[](0.0024499299678342, + 0.0043538453346397, + 0.0073599963704157, + 0.0118349786570722, + 0.0181026699707781, + 0.0263392293891488, + 0.0364543006660986, + 0.0479932050577658, + 0.0601029809166942, + 0.0715974486241365, + 0.0811305381519717, + 0.0874493212267511, + 0.0896631113333857, + 0.0874493212267511, + 0.0811305381519717, + 0.0715974486241365, + 0.0601029809166942, + 0.0479932050577658, + 0.0364543006660986, + 0.0263392293891488, + 0.0181026699707781, + 0.0118349786570722, + 0.0073599963704157, + 0.0043538453346397, + 0.0024499299678342); + + + float ar = 1.0; + vec4 color = vec4(0.0); + + // Aspect ratio for vertical blur pass + if (pc.dir == 1) + ar = pc.texSize.y / pc.texSize.x; + + vec2 P = gl_GlobalInvocationID.yx - vec2(0, (weights.length() >> 1) * ar * pc.scale); + + for (int i = 0; i < weights.length(); i++) + { + vec2 dv = vec2(0.0, i * pc.scale) * ar; + color += imageLoad (inputImage, ivec2(P + dv)) * weights[i] * pc.strength; + } + + imageStore(resultImage, ivec2(gl_GlobalInvocationID.xy), color); +} \ No newline at end of file diff --git a/samples/shadow/shaders/bloom.frag b/samples/shadow/shaders/bloom.frag new file mode 100644 index 0000000..c3bf402 --- /dev/null +++ b/samples/shadow/shaders/bloom.frag @@ -0,0 +1,63 @@ +#version 450 + +layout (binding = 0) uniform sampler2D samplerColor0; +layout (binding = 1) uniform sampler2D samplerColor1; + +layout (location = 0) in vec2 inUV; + +layout (location = 0) out vec4 outColor; + +layout (constant_id = 0) const int dir = 0; + +void main(void) +{ + // From the OpenGL Super bible + const float weights[] = float[](0.0024499299678342, + 0.0043538453346397, + 0.0073599963704157, + 0.0118349786570722, + 0.0181026699707781, + 0.0263392293891488, + 0.0364543006660986, + 0.0479932050577658, + 0.0601029809166942, + 0.0715974486241365, + 0.0811305381519717, + 0.0874493212267511, + 0.0896631113333857, + 0.0874493212267511, + 0.0811305381519717, + 0.0715974486241365, + 0.0601029809166942, + 0.0479932050577658, + 0.0364543006660986, + 0.0263392293891488, + 0.0181026699707781, + 0.0118349786570722, + 0.0073599963704157, + 0.0043538453346397, + 0.0024499299678342); + + + const float blurScale = 0.003; + const float blurStrength = 1.0; + + float ar = 1.0; + // Aspect ratio for vertical blur pass + if (dir == 1) + { + vec2 ts = textureSize(samplerColor1, 0); + ar = ts.y / ts.x; + } + + vec2 P = inUV.yx - vec2(0, (weights.length() >> 1) * ar * blurScale); + + vec4 color = vec4(0.0); + for (int i = 0; i < weights.length(); i++) + { + vec2 dv = vec2(0.0, i * blurScale) * ar; + color += texture(samplerColor1, P + dv) * weights[i] * blurStrength; + } + + outColor = color; +} \ No newline at end of file diff --git a/samples/shadow/shaders/compose.frag b/samples/shadow/shaders/compose.frag new file mode 100644 index 0000000..e6ab256 --- /dev/null +++ b/samples/shadow/shaders/compose.frag @@ -0,0 +1,157 @@ +#include "preamble.inc" + +layout (set = 0, binding = 0) uniform UBO { + mat4 projection; + mat4 model; + mat4 view; + vec4 camPos; + float exposure; + float gamma; + float prefilteredCubeMipLevels; + float scaleIBLAmbient; +} ubo; + +layout (constant_id = 0) const uint NUM_LIGHTS = 1; + +struct Light { + vec4 position; + vec4 color; + mat4 mvp; +}; + +layout (set = 0, binding = 4) uniform UBOLights { + Light lights[NUM_LIGHTS]; +}; + +const float M_PI = 3.141592653589793; +const float c_MinRoughness = 0.04; + +layout (input_attachment_index = 0, set = 1, binding = 0) uniform subpassInputMS samplerColorRough; +layout (input_attachment_index = 1, set = 1, binding = 1) uniform subpassInputMS samplerEmitMetal; +layout (input_attachment_index = 2, set = 1, binding = 2) uniform subpassInputMS samplerN_AO; +layout (input_attachment_index = 3, set = 1, binding = 3) uniform subpassInputMS samplerPos; + +layout (set = 0, binding = 1) uniform samplerCube samplerIrradiance; +layout (set = 0, binding = 2) uniform samplerCube prefilteredMap; +layout (set = 0, binding = 3) uniform sampler2D samplerBRDFLUT; + +layout (location = 0) in vec2 inUV; + +layout (location = 0) out vec4 outColor; + +#include "pbr.inc" + +// Calculation of the lighting contribution from an optional Image Based Light source. +// Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1]. +// See our README.md on Environment Maps [3] for additional discussion. +vec3 getIBLContribution(PBRInfo pbrInputs, vec3 n, vec3 reflection) +{ + float lod = (pbrInputs.perceptualRoughness * ubo.prefilteredCubeMipLevels); + // retrieve a scale and bias to F0. See [1], Figure 3 + vec3 brdf = (texture(samplerBRDFLUT, vec2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness))).rgb; + vec3 diffuseLight = texture(samplerIrradiance, reflection).rgb; + + vec3 specularLight = textureLod(prefilteredMap, reflection, lod).rgb; + + vec3 diffuse = diffuseLight * pbrInputs.diffuseColor; + vec3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y); + + // For presentation, this allows us to disable IBL terms + // For presentation, this allows us to disable IBL terms + diffuse *= ubo.scaleIBLAmbient; + specular *= ubo.scaleIBLAmbient; + + return diffuse + specular; +} + +void main() +{ + if (subpassLoad(samplerPos, gl_SampleID).a == 1.0f) + discard; + + float perceptualRoughness = subpassLoad(samplerColorRough, gl_SampleID).a; + float metallic = subpassLoad(samplerEmitMetal, gl_SampleID).a; + vec3 diffuseColor; + vec4 baseColor = vec4(subpassLoad(samplerColorRough, gl_SampleID).rgb, 1); + vec3 emissive = subpassLoad (samplerEmitMetal, gl_SampleID).rgb; + + vec3 f0 = vec3(0.04); + + diffuseColor = baseColor.rgb * (vec3(1.0) - f0); + diffuseColor *= 1.0 - metallic; + + float alphaRoughness = perceptualRoughness * perceptualRoughness; + + vec3 specularColor = mix(f0, baseColor.rgb, metallic); + + // Compute reflectance. + float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b); + + // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect. + // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%. + float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0); + vec3 specularEnvironmentR0 = specularColor.rgb; + vec3 specularEnvironmentR90 = vec3(1.0) * reflectance90; + + vec3 n = subpassLoad(samplerN_AO, gl_SampleID).rgb; + vec3 pos = subpassLoad(samplerPos, gl_SampleID).rgb; + vec3 v = normalize(ubo.camPos.xyz - pos); // Vector from surface point to camera + + vec3 colors = vec3(0); + vec3 lightTarget = vec3(0); + + for (int i=0; i -1.0 && shadowCoord.z < 1.0) + { + float dist = texture(samplerShadowMap, vec3(shadowCoord.st + offset, layer)).r; + if (shadowCoord.w > 0.0 && dist < shadowCoord.z) + shadow = SHADOW_FACTOR; + }else + shadow = 0.05f;//for debug view out of light proj + + return shadow; +} + +float filterPCF(vec4 sc, float layer) +{ + ivec2 texDim = textureSize(samplerShadowMap, 0).xy; + float scale = 1.5; + float dx = scale * 1.0 / float(texDim.x); + float dy = scale * 1.0 / float(texDim.y); + + float shadowFactor = 0.0; + int count = 0; + int range = 1; + + for (int x = -range; x <= range; x++) + { + for (int y = -range; y <= range; y++) + { + shadowFactor += textureProj(sc, layer, vec2(dx*x, dy*y)); + count++; + } + + } + return shadowFactor / count; +} + +void main() +{ + if (subpassLoad(samplerPos, gl_SampleID).a == 1.0f) + discard; + + float perceptualRoughness = subpassLoad(samplerColorRough, gl_SampleID).a; + float metallic = subpassLoad(samplerEmitMetal, gl_SampleID).a; + vec3 diffuseColor; + vec4 baseColor = vec4(subpassLoad(samplerColorRough, gl_SampleID).rgb, 1); + vec3 emissive = subpassLoad (samplerEmitMetal, gl_SampleID).rgb; + + vec3 f0 = vec3(0.04); + + diffuseColor = baseColor.rgb * (vec3(1.0) - f0); + diffuseColor *= 1.0 - metallic; + + float alphaRoughness = perceptualRoughness * perceptualRoughness; + + vec3 specularColor = mix(f0, baseColor.rgb, metallic); + + // Compute reflectance. + float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b); + + // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect. + // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%. + float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0); + vec3 specularEnvironmentR0 = specularColor.rgb; + vec3 specularEnvironmentR90 = vec3(1.0) * reflectance90; + + vec3 n = subpassLoad(samplerN_AO, gl_SampleID).rgb; + vec3 pos = subpassLoad(samplerPos, gl_SampleID).rgb; + vec3 v = normalize(ubo.camPos.xyz - pos); // Vector from surface point to camera + + vec3 colors = vec3(0); + vec3 lightTarget = vec3(0); + + for (int i=0; i1.0) + // hdrColor.rgb += c.rgb * 0.05; + //outColor = SRGBtoLINEAR(tonemap(hdrColor, exposure, gamma)); + outColor = tonemap(hdrColor, exposure, gamma); + + + /* + outColor = vec4(SRGBtoLINEAR(tonemap(hdrColor.rgb)), hdrColor.a);;*/ + +/* vec3 mapped = vec3(1.0) - exp(-hdrColor.rgb * pc.exposure); + mapped = pow(mapped, vec3(1.0 / pc.gamma)); + outColor = vec4(mapped, hdrColor.a);*/ +} \ No newline at end of file diff --git a/samples/shadow/shadow.csproj b/samples/shadow/shadow.csproj new file mode 100644 index 0000000..734c30d --- /dev/null +++ b/samples/shadow/shadow.csproj @@ -0,0 +1,21 @@ + + + + false + + + + TRACE;NETSTANDARD;NETSTANDARD2_0;MEMORY_POOLS;WITH_SHADOWS;_WITH_VKVG;DEBUG;NETFRAMEWORK;NET472 + + + TRACE;DEBUG;NETSTANDARD;NETSTANDARD2_0;MEMORY_POOLS;WITH_SHADOWS;_WITH_VKVG + + + + + + + + + + diff --git a/samples/shadow/shadowMapRenderer.cs b/samples/shadow/shadowMapRenderer.cs new file mode 100644 index 0000000..875e11c --- /dev/null +++ b/samples/shadow/shadowMapRenderer.cs @@ -0,0 +1,159 @@ +/*shadow mapping greatly inspired from: +* Vulkan Example - Deferred shading with shadows from multiple light sources using geometry shader instancing +* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de +* 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 vke.glTF; +using Vulkan; +using static deferred.DeferredPbrRenderer; + +namespace deferred { + public class ShadowMapRenderer : IDisposable { + Device dev; + Queue gQueue; + + public static uint SHADOWMAP_SIZE = 4096; + public static VkFormat SHADOWMAP_FORMAT = VkFormat.D32SfloatS8Uint; + public static VkSampleCountFlags SHADOWMAP_NUM_SAMPLES = VkSampleCountFlags.SampleCount1; + public bool updateShadowMap = true; + + public float depthBiasConstant = 1.5f; + public float depthBiasSlope = 1.75f; + float lightFOV = Utils.DegreesToRadians (60); + float lightFarPlane; + + + + RenderPass shadowPass; + FrameBuffer fbShadowMap; + public Image shadowMap { get; private set; } + Pipeline shadowPipeline; + DescriptorPool descriptorPool; + DescriptorSetLayout descLayoutShadow; + DescriptorSet dsShadow; + DeferredPbrRenderer renderer; + + public ShadowMapRenderer (Queue gQueue, DeferredPbrRenderer renderer, float farPlane = 16f) { + this.lightFarPlane = farPlane; + this.gQueue = gQueue; + this.dev = gQueue.Dev; + this.renderer = renderer; + + descriptorPool = new DescriptorPool (dev, 1, + new VkDescriptorPoolSize (VkDescriptorType.UniformBuffer, 2) + ); + + init (); + } + + void init () { + + //Shadow map renderpass + shadowPass = new RenderPass (dev, VkSampleCountFlags.SampleCount1); + shadowPass.AddAttachment (SHADOWMAP_FORMAT, VkImageLayout.DepthStencilReadOnlyOptimal, SHADOWMAP_NUM_SAMPLES); + shadowPass.ClearValues.Add (new VkClearValue { depthStencil = new VkClearDepthStencilValue (1.0f, 0) }); + + SubPass subpass0 = new SubPass (); + subpass0.SetDepthReference (0); + shadowPass.AddSubpass (subpass0); + + shadowPass.AddDependency (Vk.SubpassExternal, 0, + VkPipelineStageFlags.FragmentShader, VkPipelineStageFlags.EarlyFragmentTests, + VkAccessFlags.ShaderRead, VkAccessFlags.DepthStencilAttachmentWrite); + shadowPass.AddDependency (0, Vk.SubpassExternal, + VkPipelineStageFlags.LateFragmentTests, VkPipelineStageFlags.FragmentShader, + VkAccessFlags.DepthStencilAttachmentWrite, VkAccessFlags.ShaderRead); + + descLayoutShadow = new DescriptorSetLayout (dev, + new VkDescriptorSetLayoutBinding (0, VkShaderStageFlags.Geometry, VkDescriptorType.UniformBuffer),//matrices + new VkDescriptorSetLayoutBinding (1, VkShaderStageFlags.Geometry, VkDescriptorType.UniformBuffer));//lights + + GraphicPipelineConfig cfg = GraphicPipelineConfig.CreateDefault (VkPrimitiveTopology.TriangleList); + cfg.rasterizationState.cullMode = VkCullModeFlags.Back; + cfg.rasterizationState.depthBiasEnable = true; + cfg.dynamicStates.Add (VkDynamicState.DepthBias); + + cfg.RenderPass = shadowPass; + + cfg.Layout = new PipelineLayout (dev, descLayoutShadow); + cfg.Layout.AddPushConstants ( + new VkPushConstantRange (VkShaderStageFlags.Vertex, (uint)Marshal.SizeOf ()) + ); + + cfg.AddVertexBinding (0); + cfg.AddVertexAttributes (0, VkFormat.R32g32b32Sfloat); + + cfg.AddShaders (new ShaderInfo (dev, VkShaderStageFlags.Vertex, "#shaders.shadow.vert.spv")); + cfg.AddShaders (new ShaderInfo (dev, VkShaderStageFlags.Geometry, "#shaders.shadow.geom.spv")); + + shadowPipeline = new GraphicPipeline (cfg); + + cfg.DisposeShaders (); + + //shadow map image + shadowMap = new Image (dev, SHADOWMAP_FORMAT, VkImageUsageFlags.DepthStencilAttachment | VkImageUsageFlags.Sampled, VkMemoryPropertyFlags.DeviceLocal, SHADOWMAP_SIZE, SHADOWMAP_SIZE, + VkImageType.Image2D, SHADOWMAP_NUM_SAMPLES, VkImageTiling.Optimal, 1, (uint)renderer.lights.Length); + shadowMap.CreateView (VkImageViewType.ImageView2DArray, VkImageAspectFlags.Depth, shadowMap.CreateInfo.arrayLayers); + shadowMap.CreateSampler (VkSamplerAddressMode.ClampToBorder); + shadowMap.Descriptor.imageLayout = VkImageLayout.DepthStencilReadOnlyOptimal; + + fbShadowMap = new FrameBuffer (shadowPass, SHADOWMAP_SIZE, SHADOWMAP_SIZE, (uint)renderer.lights.Length); + fbShadowMap.attachments.Add (shadowMap); + fbShadowMap.Activate (); + + dsShadow = descriptorPool.Allocate (descLayoutShadow); + + DescriptorSetWrites dsWrite = new DescriptorSetWrites (dsShadow, descLayoutShadow); + dsWrite.Write (dev, renderer.uboMatrices.Descriptor, renderer.uboLights.Descriptor); + } + + public void update_light_matrices () { + Matrix4x4 proj = Utils.CreatePerspectiveFieldOfView (lightFOV, 1, 0.1f, lightFarPlane); + for (int i = 0; i < renderer.lights.Length; i++) { + Matrix4x4 view = Matrix4x4.CreateLookAt (renderer.lights[i].position.ToVector3 (), Vector3.Zero, Vector3.UnitY); + renderer.lights[i].mvp = view * proj; + } + renderer.uboLights.Update (renderer.lights); + dev.WaitIdle (); + } + + public void update_shadow_map (CommandPool cmdPool) { + update_light_matrices (); + + PrimaryCommandBuffer cmd = cmdPool.AllocateAndStart (); + + shadowPass.Begin (cmd, fbShadowMap); + + cmd.SetViewport (SHADOWMAP_SIZE, SHADOWMAP_SIZE); + cmd.SetScissor (SHADOWMAP_SIZE, SHADOWMAP_SIZE); + + cmd.BindDescriptorSet (shadowPipeline.Layout, dsShadow); + + Vk.vkCmdSetDepthBias (cmd.Handle, depthBiasConstant, 0.0f, depthBiasSlope); + + shadowPipeline.Bind (cmd); + + if (renderer.model != null) { + renderer.model.Bind (cmd); + renderer.model.DrawAll (cmd, shadowPipeline.Layout, true); + } + + shadowPass.End (cmd); + + gQueue.EndSubmitAndWait (cmd); + updateShadowMap = false; + } + + + public void Dispose () { + shadowPipeline?.Dispose (); + fbShadowMap?.Dispose (); + shadowMap?.Dispose (); + descriptorPool?.Dispose (); + } + } +} diff --git a/samples/shadow/ui/debug.crow b/samples/shadow/ui/debug.crow new file mode 100644 index 0000000..4d78bf1 --- /dev/null +++ b/samples/shadow/ui/debug.crow @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/samples/shadow/ui/deferred.style b/samples/shadow/ui/deferred.style new file mode 100644 index 0000000..c21ffb6 --- /dev/null +++ b/samples/shadow/ui/deferred.style @@ -0,0 +1,61 @@ +icon { + Width="14"; + Height="14"; +} +MemberViewLabel { + Margin="1"; + Height="Fit"; + Width="50%"; + Background="White"; +} +MemberViewHStack { + Focusable="true"; + Height="Fit"; + Spacing="1"; + MouseEnter="{Background=SteelBlue}"; + MouseLeave="{Background=Transparent}"; +} + +IcoBut { + Template = "#Crow.Coding.ui.IcoBut.template"; + MinimumSize = "10,10"; + Width = "8"; + Height = "14"; + Background = "White"; +} + +WinSchema { + Template="#Crow.Coding.ui.DockWindows.WinSchemaItem.template"; + Focusable = "true"; + MinimumSize="150,50"; + Width = "Fit"; + Height = "Fit"; +} +TabItem { + AllowDrag="false"; + Background="Jet"; +} +TreeItemBorder { + CornerRadius="2"; + Margin="0"; + Focusable="true"; + Height="Fit"; + Width="Stretched"; + Foreground="Transparent"; + MouseEnter="{Foreground=DimGrey}"; + MouseLeave="{Foreground=Transparent}"; +} +TreeIcon { + Margin="0"; + Width="14"; + Height="14"; +} +NormalizedFloat { + Minimum="0.0"; + Maximum="1.0"; + SmallIncrement="0.01"; + LargeIncrement="0.1"; + Decimals="2"; + Width="60"; + Foreground="Black"; +} diff --git a/samples/shadow/ui/main.crow b/samples/shadow/ui/main.crow new file mode 100644 index 0000000..14e6082 --- /dev/null +++ b/samples/shadow/ui/main.crow @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/shadow/ui/materials.crow b/samples/shadow/ui/materials.crow new file mode 100644 index 0000000..b4c5432 --- /dev/null +++ b/samples/shadow/ui/materials.crow @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + +