From c0235090c0abd2b011500a75da52db22565b14b3 Mon Sep 17 00:00:00 2001 From: Aqua Date: Sun, 24 Aug 2025 12:14:54 +0100 Subject: [PATCH 01/11] start opengl impl --- src/Graphite/Graphite.csproj | 1 + src/Graphite/Instance.cs | 7 +++-- src/Graphite/InstanceInfo.cs | 10 ++++++- src/Graphite/OpenGL/GLInstance.cs | 37 ++++++++++++++++++++++++ src/Graphite/Vulkan/VulkanCommandList.cs | 2 +- tests/Graphite.Tests/Program.cs | 8 ++--- 6 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 src/Graphite/OpenGL/GLInstance.cs diff --git a/src/Graphite/Graphite.csproj b/src/Graphite/Graphite.csproj index bef9993..d2e454c 100644 --- a/src/Graphite/Graphite.csproj +++ b/src/Graphite/Graphite.csproj @@ -5,6 +5,7 @@ + diff --git a/src/Graphite/Instance.cs b/src/Graphite/Instance.cs index 3329aaa..e6692be 100644 --- a/src/Graphite/Instance.cs +++ b/src/Graphite/Instance.cs @@ -1,4 +1,5 @@ using Graphite.D3D11; +using Graphite.OpenGL; using Graphite.Vulkan; namespace Graphite; @@ -50,7 +51,9 @@ public static Instance Create(in InstanceInfo info) { if (OperatingSystem.IsWindows() || (Environment.GetEnvironmentVariable("GRAPHITE_USE_DXVK") ?? "0") == "1") return new D3D11Instance(in info); - - return new VulkanInstance(in info); + + return new GLInstance(in info); + + //return new VulkanInstance(in info); } } \ No newline at end of file diff --git a/src/Graphite/InstanceInfo.cs b/src/Graphite/InstanceInfo.cs index 09a58b2..65960bc 100644 --- a/src/Graphite/InstanceInfo.cs +++ b/src/Graphite/InstanceInfo.cs @@ -15,14 +15,22 @@ public struct InstanceInfo /// public bool Debug; + /// + /// The GetProcAddress function for the OpenGL backend. This does not need to be provided if the OpenGL backend + /// will not be used. + /// + public Func? GLGetProcAddressFunc; + /// /// Create a new . /// /// The name of the application. This is used in some backends. /// Enable graphics debugging. - public InstanceInfo(string appName, bool debug = false) + /// The GetProcAddress function for the OpenGL backend. + public InstanceInfo(string appName, bool debug = false, Func? glGetProcAddressFunc = null) { AppName = appName; Debug = debug; + GLGetProcAddressFunc = glGetProcAddressFunc; } } \ No newline at end of file diff --git a/src/Graphite/OpenGL/GLInstance.cs b/src/Graphite/OpenGL/GLInstance.cs new file mode 100644 index 0000000..60647ce --- /dev/null +++ b/src/Graphite/OpenGL/GLInstance.cs @@ -0,0 +1,37 @@ +using System.Diagnostics; +using Silk.NET.OpenGL; + +namespace Graphite.OpenGL; + +internal sealed class GLInstance : Instance +{ + private readonly GL _gl; + + public override Backend Backend => Backend.OpenGL; + + public GLInstance(ref readonly InstanceInfo info) + { + Debug.Assert(info.GLGetProcAddressFunc != null); + _gl = GL.GetApi(info.GLGetProcAddressFunc); + } + + public override Adapter[] EnumerateAdapters() + { + throw new NotImplementedException(); + } + + public override Surface CreateSurface(in SurfaceInfo info) + { + throw new NotImplementedException(); + } + + public override Device CreateDevice(Surface surface, Adapter? adapter = null) + { + throw new NotImplementedException(); + } + + public override void Dispose() + { + _gl.Dispose(); + } +} \ No newline at end of file diff --git a/src/Graphite/Vulkan/VulkanCommandList.cs b/src/Graphite/Vulkan/VulkanCommandList.cs index b5ea0b9..143cfd8 100644 --- a/src/Graphite/Vulkan/VulkanCommandList.cs +++ b/src/Graphite/Vulkan/VulkanCommandList.cs @@ -114,7 +114,7 @@ public override void GenerateMipmaps(Texture texture) for (uint i = 1; i < vkTexture.MipLevels; i++) { - ImageBlit blit = new ImageBlit + ImageBlit blit = new() { SrcSubresource = new ImageSubresourceLayers { diff --git a/tests/Graphite.Tests/Program.cs b/tests/Graphite.Tests/Program.cs index 1a0cab3..fa9dc66 100644 --- a/tests/Graphite.Tests/Program.cs +++ b/tests/Graphite.Tests/Program.cs @@ -19,17 +19,17 @@ if (!SDL.Init(SDL.InitFlags.Video | SDL.InitFlags.Events)) throw new Exception($"Failed to initialize SDL: {SDL.GetError()}"); -Instance instance = Instance.Create(new InstanceInfo("Graphite.Tests", true)); -Console.WriteLine($"Adapters: {string.Join(", ", instance.EnumerateAdapters())}"); - const int width = 1280; const int height = 720; // Note: The Vulkan flag is here for DXVK support. This flag does not ordinarily need to be passed to CreateWindow. -IntPtr window = SDL.CreateWindow($"Graphite.Tests - {instance.Backend}", width, height, SDL.WindowFlags.Resizable | SDL.WindowFlags.Vulkan); +IntPtr window = SDL.CreateWindow($"Graphite.Tests", width, height, SDL.WindowFlags.Resizable | SDL.WindowFlags.Vulkan); if (window == IntPtr.Zero) throw new Exception($"Failed to create window: {SDL.GetError()}"); +Instance instance = Instance.Create(new InstanceInfo("Graphite.Tests", true, s => SDL.GLGetProcAddress(s).)); +Console.WriteLine($"Adapters: {string.Join(", ", instance.EnumerateAdapters())}"); + uint properties = SDL.GetWindowProperties(window); SurfaceInfo surfaceInfo; From deef5723a904abdc3ebfcfa5d995695cd1ee3613 Mon Sep 17 00:00:00 2001 From: Aqua Date: Sun, 24 Aug 2025 16:21:28 +0100 Subject: [PATCH 02/11] GLContext, GLSurface --- src/Graphite/InstanceInfo.cs | 13 +++++------ src/Graphite/OpenGL/GLContext.cs | 14 ++++++++++++ src/Graphite/OpenGL/GLInstance.cs | 12 +++++++---- src/Graphite/OpenGL/GLSurface.cs | 6 ++++++ tests/Graphite.Tests/Graphite.Tests.csproj | 3 +-- tests/Graphite.Tests/Program.cs | 25 ++++++++++++++++------ 6 files changed, 55 insertions(+), 18 deletions(-) create mode 100644 src/Graphite/OpenGL/GLContext.cs create mode 100644 src/Graphite/OpenGL/GLSurface.cs diff --git a/src/Graphite/InstanceInfo.cs b/src/Graphite/InstanceInfo.cs index 65960bc..27fee90 100644 --- a/src/Graphite/InstanceInfo.cs +++ b/src/Graphite/InstanceInfo.cs @@ -1,3 +1,5 @@ +using Graphite.OpenGL; + namespace Graphite; /// @@ -16,21 +18,20 @@ public struct InstanceInfo public bool Debug; /// - /// The GetProcAddress function for the OpenGL backend. This does not need to be provided if the OpenGL backend - /// will not be used. + /// The OpenGL context info, if any. You do not need to provide this if you will not be using the OpenGL backend. /// - public Func? GLGetProcAddressFunc; + public GLContext? GLContext; /// /// Create a new . /// /// The name of the application. This is used in some backends. /// Enable graphics debugging. - /// The GetProcAddress function for the OpenGL backend. - public InstanceInfo(string appName, bool debug = false, Func? glGetProcAddressFunc = null) + /// The OpenGL context info, if any. + public InstanceInfo(string appName, bool debug = false, GLContext? context = null) { AppName = appName; Debug = debug; - GLGetProcAddressFunc = glGetProcAddressFunc; + GLContext = context; } } \ No newline at end of file diff --git a/src/Graphite/OpenGL/GLContext.cs b/src/Graphite/OpenGL/GLContext.cs new file mode 100644 index 0000000..6396204 --- /dev/null +++ b/src/Graphite/OpenGL/GLContext.cs @@ -0,0 +1,14 @@ +namespace Graphite.OpenGL; + +public class GLContext +{ + public Func GetProcAddressFunc; + + public Action PresentFunc; + + public GLContext(Func getProcAddressFunc, Action presentFunc) + { + GetProcAddressFunc = getProcAddressFunc; + PresentFunc = presentFunc; + } +} \ No newline at end of file diff --git a/src/Graphite/OpenGL/GLInstance.cs b/src/Graphite/OpenGL/GLInstance.cs index 60647ce..c2a36df 100644 --- a/src/Graphite/OpenGL/GLInstance.cs +++ b/src/Graphite/OpenGL/GLInstance.cs @@ -5,24 +5,28 @@ namespace Graphite.OpenGL; internal sealed class GLInstance : Instance { + private readonly GLContext _context; private readonly GL _gl; public override Backend Backend => Backend.OpenGL; public GLInstance(ref readonly InstanceInfo info) { - Debug.Assert(info.GLGetProcAddressFunc != null); - _gl = GL.GetApi(info.GLGetProcAddressFunc); + Debug.Assert(info.GLContext != null); + _context = info.GLContext; + + _gl = GL.GetApi(_context.GetProcAddressFunc); } public override Adapter[] EnumerateAdapters() { - throw new NotImplementedException(); + string name = _gl.GetStringS(StringName.Renderer); + return [new Adapter(0, 0, name)]; } public override Surface CreateSurface(in SurfaceInfo info) { - throw new NotImplementedException(); + return new GLSurface(); } public override Device CreateDevice(Surface surface, Adapter? adapter = null) diff --git a/src/Graphite/OpenGL/GLSurface.cs b/src/Graphite/OpenGL/GLSurface.cs new file mode 100644 index 0000000..766d9d4 --- /dev/null +++ b/src/Graphite/OpenGL/GLSurface.cs @@ -0,0 +1,6 @@ +namespace Graphite.OpenGL; + +internal sealed class GLSurface : Surface +{ + public override void Dispose() { } +} \ No newline at end of file diff --git a/tests/Graphite.Tests/Graphite.Tests.csproj b/tests/Graphite.Tests/Graphite.Tests.csproj index c018b12..e639feb 100644 --- a/tests/Graphite.Tests/Graphite.Tests.csproj +++ b/tests/Graphite.Tests/Graphite.Tests.csproj @@ -14,8 +14,7 @@ - - + diff --git a/tests/Graphite.Tests/Program.cs b/tests/Graphite.Tests/Program.cs index fa9dc66..4f91a42 100644 --- a/tests/Graphite.Tests/Program.cs +++ b/tests/Graphite.Tests/Program.cs @@ -1,8 +1,10 @@ using System.Drawing; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Graphite; using Graphite.Core; +using Graphite.OpenGL; using Graphite.ShaderTools; using SDL3; using StbImageSharp; @@ -22,12 +24,23 @@ const int width = 1280; const int height = 720; -// Note: The Vulkan flag is here for DXVK support. This flag does not ordinarily need to be passed to CreateWindow. -IntPtr window = SDL.CreateWindow($"Graphite.Tests", width, height, SDL.WindowFlags.Resizable | SDL.WindowFlags.Vulkan); +SDL.GLSetAttribute(SDL.GLAttr.ContextMajorVersion, 4); +SDL.GLSetAttribute(SDL.GLAttr.ContextMinorVersion, 3); +SDL.GLSetAttribute(SDL.GLAttr.ContextProfileMask, (int) SDL.GLProfile.Core); + +IntPtr window = SDL.CreateWindow("Graphite.Tests", width, height, SDL.WindowFlags.Resizable | SDL.WindowFlags.OpenGL); if (window == IntPtr.Zero) throw new Exception($"Failed to create window: {SDL.GetError()}"); -Instance instance = Instance.Create(new InstanceInfo("Graphite.Tests", true, s => SDL.GLGetProcAddress(s).)); +IntPtr context = SDL.GLCreateContext(window); +SDL.GLMakeCurrent(window, context); + +Instance instance = Instance.Create(new InstanceInfo("Graphite.Tests", true, new GLContext(s => Marshal.GetFunctionPointerForDelegate(SDL.GLGetProcAddress(s)), + i => + { + SDL.GLSetSwapInterval(i); + SDL.GLSwapWindow(window); + }))); Console.WriteLine($"Adapters: {string.Join(", ", instance.EnumerateAdapters())}"); uint properties = SDL.GetWindowProperties(window); @@ -62,7 +75,7 @@ throw new PlatformNotSupportedException(); Surface surface = instance.CreateSurface(in surfaceInfo); -Device device = instance.CreateDevice(surface); +/*Device device = instance.CreateDevice(surface); CommandList cl = device.CreateCommandList(); Swapchain swapchain = device.CreateSwapchain(new SwapchainInfo(surface, Format.B8G8R8A8_UNorm, new Size2D(width, height), @@ -86,7 +99,7 @@ /*Buffer vertexBuffer = device.CreateBuffer(BufferUsage.VertexBuffer, vertices); Buffer indexBuffer = device.CreateBuffer(BufferUsage.IndexBuffer, indices); -Buffer constantBuffer = device.CreateBuffer(BufferUsage.ConstantBuffer | BufferUsage.MapWrite, Matrix4x4.CreateRotationZ(1));*/ +Buffer constantBuffer = device.CreateBuffer(BufferUsage.ConstantBuffer | BufferUsage.MapWrite, Matrix4x4.CreateRotationZ(1)); uint vertexSize = (uint) vertices.Length * sizeof(float); uint indexSize = (uint) indices.Length * sizeof(ushort); @@ -207,7 +220,7 @@ vertexBuffer.Dispose(); swapchain.Dispose(); cl.Dispose(); -device.Dispose(); +device.Dispose();*/ surface.Dispose(); instance.Dispose(); SDL.DestroyWindow(window); From 09d4a350cacb659e50636b94e5cf9791c7c5cbbd Mon Sep 17 00:00:00 2001 From: Aqua Date: Mon, 25 Aug 2025 14:45:53 +0100 Subject: [PATCH 03/11] start gl devices and swapchains --- src/Graphite/OpenGL/GLDevice.cs | 77 +++++++++++++++++++++++++++++ src/Graphite/OpenGL/GLInstance.cs | 2 +- src/Graphite/OpenGL/GLSwapchain.cs | 43 ++++++++++++++++ src/Graphite/OpenGL/GLTexture.cs | 13 +++++ src/Graphite/OpenGL/GLUtils.cs | 79 ++++++++++++++++++++++++++++++ 5 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 src/Graphite/OpenGL/GLDevice.cs create mode 100644 src/Graphite/OpenGL/GLSwapchain.cs create mode 100644 src/Graphite/OpenGL/GLTexture.cs create mode 100644 src/Graphite/OpenGL/GLUtils.cs diff --git a/src/Graphite/OpenGL/GLDevice.cs b/src/Graphite/OpenGL/GLDevice.cs new file mode 100644 index 0000000..8debfa3 --- /dev/null +++ b/src/Graphite/OpenGL/GLDevice.cs @@ -0,0 +1,77 @@ +using Silk.NET.OpenGL; + +namespace Graphite.OpenGL; + +internal sealed class GLDevice : Device +{ + private readonly GL _gl; + private readonly GLContext _context; + + public override Backend Backend => Backend.OpenGL; + + public GLDevice(GL gl, GLContext context) + { + _gl = gl; + _context = context; + } + + public override Swapchain CreateSwapchain(in SwapchainInfo info) + { + throw new NotImplementedException(); + } + + public override CommandList CreateCommandList() + { + throw new NotImplementedException(); + } + + public override ShaderModule CreateShaderModule(byte[] code, string entryPoint, ShaderMappingInfo mapping = default) + { + throw new NotImplementedException(); + } + + public override Pipeline CreateGraphicsPipeline(in GraphicsPipelineInfo info) + { + throw new NotImplementedException(); + } + + public override unsafe Buffer CreateBuffer(in BufferInfo info, void* data) + { + throw new NotImplementedException(); + } + + public override unsafe Texture CreateTexture(in TextureInfo info) + { + throw new NotImplementedException(); + } + + public override DescriptorLayout CreateDescriptorLayout(params ReadOnlySpan bindings) + { + throw new NotImplementedException(); + } + + public override DescriptorSet CreateDescriptorSet(DescriptorLayout layout, params ReadOnlySpan descriptors) + { + throw new NotImplementedException(); + } + + public override void ExecuteCommandList(CommandList cl) + { + throw new NotImplementedException(); + } + + public override IntPtr MapBuffer(Buffer buffer) + { + throw new NotImplementedException(); + } + + public override void UnmapBuffer(Buffer buffer) + { + throw new NotImplementedException(); + } + + public override void Dispose() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/Graphite/OpenGL/GLInstance.cs b/src/Graphite/OpenGL/GLInstance.cs index c2a36df..cf3a3cc 100644 --- a/src/Graphite/OpenGL/GLInstance.cs +++ b/src/Graphite/OpenGL/GLInstance.cs @@ -31,7 +31,7 @@ public override Surface CreateSurface(in SurfaceInfo info) public override Device CreateDevice(Surface surface, Adapter? adapter = null) { - throw new NotImplementedException(); + return new GLDevice(_gl, _context); } public override void Dispose() diff --git a/src/Graphite/OpenGL/GLSwapchain.cs b/src/Graphite/OpenGL/GLSwapchain.cs new file mode 100644 index 0000000..e44dd23 --- /dev/null +++ b/src/Graphite/OpenGL/GLSwapchain.cs @@ -0,0 +1,43 @@ +using Graphite.Core; +using Silk.NET.OpenGL; + +namespace Graphite.OpenGL; + +internal sealed class GLSwapchain : Swapchain +{ + private readonly GL _gl; + private readonly GLContext _context; + + private uint _texture; + + public override Size2D Size { get; } + + public override Format Format { get; } + + public GLSwapchain(GL gl, GLContext context, ref readonly SwapchainInfo info) + { + _gl = gl; + _context = context; + + Size = info.Size; + Format = info.Format; + + (_, SizedInternalFormat iFormat, _) = info.Format.ToGL(); + + _texture = _gl.GenTexture(); + _gl.BindTexture(TextureTarget.Texture2D, _texture); + _gl.TexStorage2D(TextureTarget.Texture2D, 1, iFormat, info.Size.Width, info.Size.Height); + } + + public override Texture GetNextTexture() + { + throw new NotImplementedException(); + } + + public override void Present() + { + _context.PresentFunc(1); + } + + public override void Dispose() { } +} \ No newline at end of file diff --git a/src/Graphite/OpenGL/GLTexture.cs b/src/Graphite/OpenGL/GLTexture.cs new file mode 100644 index 0000000..5ade0f4 --- /dev/null +++ b/src/Graphite/OpenGL/GLTexture.cs @@ -0,0 +1,13 @@ +namespace Graphite.OpenGL; + +internal sealed class GLTexture : Texture +{ + public static + + public GLTexture(TextureInfo info) : base(info) { } + + public override void Dispose() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/Graphite/OpenGL/GLUtils.cs b/src/Graphite/OpenGL/GLUtils.cs new file mode 100644 index 0000000..14eff17 --- /dev/null +++ b/src/Graphite/OpenGL/GLUtils.cs @@ -0,0 +1,79 @@ +using Silk.NET.OpenGL; + +namespace Graphite.OpenGL; + +internal static class GLUtils +{ + public static (PixelFormat format, SizedInternalFormat iFormat, PixelType type) ToGL(this Format format) + { + return format switch + { + Format.Unknown => (0, 0, 0), + Format.B5G6R5_UNorm => (PixelFormat.Bgr, SizedInternalFormat.Rgb5, PixelType.UnsignedByte), + Format.B5G5R5A1_UNorm => (PixelFormat.Bgra, SizedInternalFormat.Rgb5A1, PixelType.UnsignedByte), + Format.A8_UNorm => (PixelFormat.Alpha, SizedInternalFormat.R8, PixelType.UnsignedByte), + Format.R8_UNorm => (PixelFormat.Red, SizedInternalFormat.R8, PixelType.UnsignedByte), + Format.R8_UInt => (PixelFormat.RedInteger, SizedInternalFormat.R8ui, PixelType.UnsignedInt), + Format.R8_SNorm => (PixelFormat.Red, SizedInternalFormat.R8SNorm, PixelType.Byte), + Format.R8_SInt => (PixelFormat.RedInteger, SizedInternalFormat.R8i, PixelType.Int), + Format.R8G8_UNorm => (PixelFormat.RG, SizedInternalFormat.RG8, PixelType.UnsignedByte), + Format.R8G8_UInt => (PixelFormat.RGInteger, SizedInternalFormat.RG8ui, PixelType.UnsignedInt), + Format.R8G8_SNorm => (PixelFormat.RG, SizedInternalFormat.RG8SNorm, PixelType.Byte), + Format.R8G8_SInt => (PixelFormat.RGInteger, SizedInternalFormat.RG8i, PixelType.Int), + Format.R8G8B8A8_UNorm => (PixelFormat.Rgba, SizedInternalFormat.Rgba8, PixelType.UnsignedByte), + Format.R8G8B8A8_UNorm_SRGB => (PixelFormat.Rgba, SizedInternalFormat.Srgb8Alpha8, PixelType.UnsignedByte), + Format.R8G8B8A8_UInt => (PixelFormat.RgbaInteger, SizedInternalFormat.Rgba8ui, PixelType.UnsignedInt), + Format.R8G8B8A8_SNorm => (PixelFormat.Rgba, SizedInternalFormat.Rgba8SNorm, PixelType.Byte), + Format.R8G8B8A8_SInt => (PixelFormat.RgbaInteger, SizedInternalFormat.Rgba8i, PixelType.Int), + Format.B8G8R8A8_UNorm => (PixelFormat.Bgra, SizedInternalFormat.Rgba8, PixelType.UnsignedByte), + Format.B8G8R8A8_UNorm_SRGB => (PixelFormat.Bgra, SizedInternalFormat.Srgb8Alpha8, PixelType.UnsignedByte), + Format.R10G10B10A2_UNorm => (PixelFormat.Rgba, SizedInternalFormat.Rgb10A2, PixelType.UnsignedByte), + Format.R10G10B10A2_UInt => (PixelFormat.RgbaInteger, SizedInternalFormat.Rgb10A2ui, PixelType.UnsignedInt), + Format.R16_Float => (PixelFormat.Red, SizedInternalFormat.R16f, PixelType.Float), + Format.R16_UNorm => (PixelFormat.Red, SizedInternalFormat.R16, PixelType.UnsignedByte), + Format.R16_UInt => (PixelFormat.Red, SizedInternalFormat.R16ui, PixelType.UnsignedInt), + Format.R16_SNorm => (PixelFormat.Red, SizedInternalFormat.R16SNorm, PixelType.Byte), + Format.R16_SInt => (PixelFormat.Red, SizedInternalFormat.R16i, PixelType.Int), + Format.R16G16_Float => (PixelFormat.RG, SizedInternalFormat.RG16f, PixelType.Float), + Format.R16G16_UNorm => (PixelFormat.RG, SizedInternalFormat.RG16, PixelType.UnsignedByte), + Format.R16G16_UInt => (PixelFormat.RGInteger, SizedInternalFormat.RG16ui, PixelType.UnsignedInt), + Format.R16G16_SNorm => (PixelFormat.RG, SizedInternalFormat.RG16SNorm, PixelType.Byte), + Format.R16G16_SInt => (PixelFormat.RG, SizedInternalFormat.RG16i, PixelType.Int), + Format.R16G16B16A16_Float => (PixelFormat.Rgba, SizedInternalFormat.Rgba16f, PixelType.Float), + Format.R16G16B16A16_UNorm => (PixelFormat.Rgba, SizedInternalFormat.Rgba16, PixelType.UnsignedByte), + Format.R16G16B16A16_UInt => (PixelFormat.RgbaInteger, SizedInternalFormat.Rgba16ui, PixelType.UnsignedInt), + Format.R16G16B16A16_SNorm => (PixelFormat.Rgba, SizedInternalFormat.Rgba16SNorm, PixelType.Byte), + Format.R16G16B16A16_SInt => (PixelFormat.Rgba, SizedInternalFormat.Rgba16i, PixelType.Int), + Format.R32_Float => (PixelFormat.Red, SizedInternalFormat.R32f, PixelType.Float), + Format.R32_UInt => (PixelFormat.RedInteger, SizedInternalFormat.R32ui, PixelType.UnsignedInt), + Format.R32_SInt => (PixelFormat.RedInteger, SizedInternalFormat.R32i, PixelType.Int), + Format.R32G32_Float => (PixelFormat.RG, SizedInternalFormat.RG32f, PixelType.Float), + Format.R32G32_UInt => (PixelFormat.RGInteger, SizedInternalFormat.RG32ui, PixelType.UnsignedInt), + Format.R32G32_SInt => (PixelFormat.RGInteger, SizedInternalFormat.RG32i, PixelType.Int), + Format.R32G32B32_Float => (PixelFormat.Rgb, SizedInternalFormat.Rgb32f, PixelType.Float), + Format.R32G32B32_UInt => (PixelFormat.RgbInteger, SizedInternalFormat.Rgb32ui, PixelType.UnsignedInt), + Format.R32G32B32_SInt => (PixelFormat.RgbInteger, SizedInternalFormat.Rgb32i, PixelType.Int), + Format.R32G32B32A32_Float => (PixelFormat.Rgba, SizedInternalFormat.Rgba32f, PixelType.Float), + Format.R32G32B32A32_UInt => (PixelFormat.RgbaInteger, SizedInternalFormat.Rgba32ui, PixelType.UnsignedInt), + Format.R32G32B32A32_SInt => (PixelFormat.RgbaInteger, SizedInternalFormat.Rgba32i, PixelType.Int), + Format.D16_UNorm => (PixelFormat.DepthComponent, SizedInternalFormat.DepthComponent16, PixelType.UnsignedInt), + Format.D24_UNorm_S8_UInt => (PixelFormat.DepthStencil, SizedInternalFormat.Depth24Stencil8, PixelType.UnsignedByte), + Format.D32_Float => (PixelFormat.DepthComponent, SizedInternalFormat.DepthComponent32f, PixelType.Float), + Format.BC1_UNorm => throw new NotImplementedException(), + Format.BC1_UNorm_SRGB => throw new NotImplementedException(), + Format.BC2_UNorm => throw new NotImplementedException(), + Format.BC2_UNorm_SRGB => throw new NotImplementedException(), + Format.BC3_UNorm => throw new NotImplementedException(), + Format.BC3_UNorm_SRGB => throw new NotImplementedException(), + Format.BC4_UNorm => throw new NotImplementedException(), + Format.BC4_SNorm => throw new NotImplementedException(), + Format.BC5_UNorm => throw new NotImplementedException(), + Format.BC5_SNorm => throw new NotImplementedException(), + Format.BC6H_UF16 => throw new NotImplementedException(), + Format.BC6H_SF16 => throw new NotImplementedException(), + Format.BC7_UNorm => throw new NotImplementedException(), + Format.BC7_UNorm_SRGB => throw new NotImplementedException(), + _ => throw new ArgumentOutOfRangeException(nameof(format), format, null) + }; + } +} \ No newline at end of file From bd7823840a0c65eeea96b502869d996b2bb6f8a4 Mon Sep 17 00:00:00 2001 From: Aqua Date: Fri, 5 Sep 2025 17:28:08 +0100 Subject: [PATCH 04/11] merge main branch --- Graphite.sln | 6 +++++ .../OpenGL => Graphite.OpenGL}/GLContext.cs | 0 .../OpenGL => Graphite.OpenGL}/GLDevice.cs | 24 ++++++++++++++--- .../OpenGL => Graphite.OpenGL}/GLInstance.cs | 8 +++--- .../OpenGL => Graphite.OpenGL}/GLSurface.cs | 0 .../OpenGL => Graphite.OpenGL}/GLSwapchain.cs | 2 +- .../OpenGL => Graphite.OpenGL}/GLTexture.cs | 4 +-- .../OpenGL => Graphite.OpenGL}/GLUtils.cs | 0 src/Graphite.OpenGL/Graphite.OpenGL.csproj | 18 +++++++++++++ src/Graphite.OpenGL/OpenGLBackend.cs | 17 ++++++++++++ src/Graphite/InstanceInfo.cs | 11 +------- .../Graphite.SimpleTest.csproj | 1 + tests/Graphite.SimpleTest/Program.cs | 27 +++++++++++++++---- 13 files changed, 93 insertions(+), 25 deletions(-) rename src/{Graphite/OpenGL => Graphite.OpenGL}/GLContext.cs (100%) rename src/{Graphite/OpenGL => Graphite.OpenGL}/GLDevice.cs (74%) rename src/{Graphite/OpenGL => Graphite.OpenGL}/GLInstance.cs (73%) rename src/{Graphite/OpenGL => Graphite.OpenGL}/GLSurface.cs (100%) rename src/{Graphite/OpenGL => Graphite.OpenGL}/GLSwapchain.cs (92%) rename src/{Graphite/OpenGL => Graphite.OpenGL}/GLTexture.cs (81%) rename src/{Graphite/OpenGL => Graphite.OpenGL}/GLUtils.cs (100%) create mode 100644 src/Graphite.OpenGL/Graphite.OpenGL.csproj create mode 100644 src/Graphite.OpenGL/OpenGLBackend.cs diff --git a/Graphite.sln b/Graphite.sln index e592b95..a4d4dd3 100644 --- a/Graphite.sln +++ b/Graphite.sln @@ -16,6 +16,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Graphite.Vulkan", "src\Grap EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Graphite.D3D11", "src\Graphite.D3D11\Graphite.D3D11.csproj", "{4DEE157C-DDCE-4133-A562-4C869765A07F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Graphite.OpenGL", "src\Graphite.OpenGL\Graphite.OpenGL.csproj", "{DAE5F38B-568D-45D9-AAEF-A71E49B564FD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -50,6 +52,10 @@ Global {4DEE157C-DDCE-4133-A562-4C869765A07F}.Debug|Any CPU.Build.0 = Debug|Any CPU {4DEE157C-DDCE-4133-A562-4C869765A07F}.Release|Any CPU.ActiveCfg = Release|Any CPU {4DEE157C-DDCE-4133-A562-4C869765A07F}.Release|Any CPU.Build.0 = Release|Any CPU + {DAE5F38B-568D-45D9-AAEF-A71E49B564FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DAE5F38B-568D-45D9-AAEF-A71E49B564FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DAE5F38B-568D-45D9-AAEF-A71E49B564FD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DAE5F38B-568D-45D9-AAEF-A71E49B564FD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {7E5E2B62-B479-4BD2-A8E9-3974FE6A3C76} = {0802471C-F455-4B4B-A50C-93EB36406339} diff --git a/src/Graphite/OpenGL/GLContext.cs b/src/Graphite.OpenGL/GLContext.cs similarity index 100% rename from src/Graphite/OpenGL/GLContext.cs rename to src/Graphite.OpenGL/GLContext.cs diff --git a/src/Graphite/OpenGL/GLDevice.cs b/src/Graphite.OpenGL/GLDevice.cs similarity index 74% rename from src/Graphite/OpenGL/GLDevice.cs rename to src/Graphite.OpenGL/GLDevice.cs index 8debfa3..7e97db2 100644 --- a/src/Graphite/OpenGL/GLDevice.cs +++ b/src/Graphite.OpenGL/GLDevice.cs @@ -1,3 +1,4 @@ +using Graphite.Core; using Silk.NET.OpenGL; namespace Graphite.OpenGL; @@ -6,9 +7,9 @@ internal sealed class GLDevice : Device { private readonly GL _gl; private readonly GLContext _context; - - public override Backend Backend => Backend.OpenGL; + public override Backend Backend => OpenGLBackend.Backend; + public GLDevice(GL gl, GLContext context) { _gl = gl; @@ -40,12 +41,12 @@ public override unsafe Buffer CreateBuffer(in BufferInfo info, void* data) throw new NotImplementedException(); } - public override unsafe Texture CreateTexture(in TextureInfo info) + public override unsafe Texture CreateTexture(in TextureInfo info, void* pData) { throw new NotImplementedException(); } - public override DescriptorLayout CreateDescriptorLayout(params ReadOnlySpan bindings) + public override DescriptorLayout CreateDescriptorLayout(in DescriptorLayoutInfo info) { throw new NotImplementedException(); } @@ -55,11 +56,26 @@ public override DescriptorSet CreateDescriptorSet(DescriptorLayout layout, param throw new NotImplementedException(); } + public override Sampler CreateSampler(in SamplerInfo info) + { + throw new NotImplementedException(); + } + public override void ExecuteCommandList(CommandList cl) { throw new NotImplementedException(); } + public override unsafe void UpdateBuffer(Buffer buffer, uint offset, uint size, void* pData) + { + throw new NotImplementedException(); + } + + public override unsafe void UpdateTexture(Texture texture, in Region3D region, void* pData) + { + throw new NotImplementedException(); + } + public override IntPtr MapBuffer(Buffer buffer) { throw new NotImplementedException(); diff --git a/src/Graphite/OpenGL/GLInstance.cs b/src/Graphite.OpenGL/GLInstance.cs similarity index 73% rename from src/Graphite/OpenGL/GLInstance.cs rename to src/Graphite.OpenGL/GLInstance.cs index cf3a3cc..b7bdfde 100644 --- a/src/Graphite/OpenGL/GLInstance.cs +++ b/src/Graphite.OpenGL/GLInstance.cs @@ -7,13 +7,15 @@ internal sealed class GLInstance : Instance { private readonly GLContext _context; private readonly GL _gl; + + public override string BackendName => OpenGLBackend.Name; - public override Backend Backend => Backend.OpenGL; + public override Backend Backend => OpenGLBackend.Backend; public GLInstance(ref readonly InstanceInfo info) { - Debug.Assert(info.GLContext != null); - _context = info.GLContext; + Debug.Assert(OpenGLBackend.Context != null, "OpenGLBackend.Context was null. This value must be set to use the OpenGL backend."); + _context = OpenGLBackend.Context; _gl = GL.GetApi(_context.GetProcAddressFunc); } diff --git a/src/Graphite/OpenGL/GLSurface.cs b/src/Graphite.OpenGL/GLSurface.cs similarity index 100% rename from src/Graphite/OpenGL/GLSurface.cs rename to src/Graphite.OpenGL/GLSurface.cs diff --git a/src/Graphite/OpenGL/GLSwapchain.cs b/src/Graphite.OpenGL/GLSwapchain.cs similarity index 92% rename from src/Graphite/OpenGL/GLSwapchain.cs rename to src/Graphite.OpenGL/GLSwapchain.cs index e44dd23..04da5f2 100644 --- a/src/Graphite/OpenGL/GLSwapchain.cs +++ b/src/Graphite.OpenGL/GLSwapchain.cs @@ -22,7 +22,7 @@ public GLSwapchain(GL gl, GLContext context, ref readonly SwapchainInfo info) Size = info.Size; Format = info.Format; - (_, SizedInternalFormat iFormat, _) = info.Format.ToGL(); + (_, SizedInternalFormat iFormat, _) = GLUtils.ToGL(info.Format); _texture = _gl.GenTexture(); _gl.BindTexture(TextureTarget.Texture2D, _texture); diff --git a/src/Graphite/OpenGL/GLTexture.cs b/src/Graphite.OpenGL/GLTexture.cs similarity index 81% rename from src/Graphite/OpenGL/GLTexture.cs rename to src/Graphite.OpenGL/GLTexture.cs index 5ade0f4..591806b 100644 --- a/src/Graphite/OpenGL/GLTexture.cs +++ b/src/Graphite.OpenGL/GLTexture.cs @@ -1,6 +1,6 @@ namespace Graphite.OpenGL; -internal sealed class GLTexture : Texture +/*internal sealed class GLTexture : Texture { public static @@ -10,4 +10,4 @@ public override void Dispose() { throw new NotImplementedException(); } -} \ No newline at end of file +}*/ \ No newline at end of file diff --git a/src/Graphite/OpenGL/GLUtils.cs b/src/Graphite.OpenGL/GLUtils.cs similarity index 100% rename from src/Graphite/OpenGL/GLUtils.cs rename to src/Graphite.OpenGL/GLUtils.cs diff --git a/src/Graphite.OpenGL/Graphite.OpenGL.csproj b/src/Graphite.OpenGL/Graphite.OpenGL.csproj new file mode 100644 index 0000000..744fe18 --- /dev/null +++ b/src/Graphite.OpenGL/Graphite.OpenGL.csproj @@ -0,0 +1,18 @@ + + + + net9.0 + enable + enable + true + + + + + + + + + + + diff --git a/src/Graphite.OpenGL/OpenGLBackend.cs b/src/Graphite.OpenGL/OpenGLBackend.cs new file mode 100644 index 0000000..69ccb95 --- /dev/null +++ b/src/Graphite.OpenGL/OpenGLBackend.cs @@ -0,0 +1,17 @@ +namespace Graphite.OpenGL; + +public class OpenGLBackend : IBackend +{ + public const int MajorVersion = 4; + + public const int MinorVersion = 3; + + public static string Name => "OpenGL"; + + public static Backend Backend => Backend.OpenGL; + + public static GLContext? Context; + + public Instance CreateInstance(ref readonly InstanceInfo info) + => new GLInstance(in info); +} \ No newline at end of file diff --git a/src/Graphite/InstanceInfo.cs b/src/Graphite/InstanceInfo.cs index 27fee90..09a58b2 100644 --- a/src/Graphite/InstanceInfo.cs +++ b/src/Graphite/InstanceInfo.cs @@ -1,5 +1,3 @@ -using Graphite.OpenGL; - namespace Graphite; /// @@ -17,21 +15,14 @@ public struct InstanceInfo /// public bool Debug; - /// - /// The OpenGL context info, if any. You do not need to provide this if you will not be using the OpenGL backend. - /// - public GLContext? GLContext; - /// /// Create a new . /// /// The name of the application. This is used in some backends. /// Enable graphics debugging. - /// The OpenGL context info, if any. - public InstanceInfo(string appName, bool debug = false, GLContext? context = null) + public InstanceInfo(string appName, bool debug = false) { AppName = appName; Debug = debug; - GLContext = context; } } \ No newline at end of file diff --git a/tests/Graphite.SimpleTest/Graphite.SimpleTest.csproj b/tests/Graphite.SimpleTest/Graphite.SimpleTest.csproj index 9b2d4a3..45f997e 100644 --- a/tests/Graphite.SimpleTest/Graphite.SimpleTest.csproj +++ b/tests/Graphite.SimpleTest/Graphite.SimpleTest.csproj @@ -12,6 +12,7 @@ + diff --git a/tests/Graphite.SimpleTest/Program.cs b/tests/Graphite.SimpleTest/Program.cs index a4bf2c3..4cba852 100644 --- a/tests/Graphite.SimpleTest/Program.cs +++ b/tests/Graphite.SimpleTest/Program.cs @@ -1,9 +1,11 @@ using System.Drawing; using System.Numerics; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using Graphite; using Graphite.Core; using Graphite.D3D11; +using Graphite.OpenGL; using Graphite.ShaderTools; using Graphite.Vulkan; using SDL3; @@ -18,20 +20,35 @@ if (!SDL.Init(SDL.InitFlags.Video | SDL.InitFlags.Events)) throw new Exception($"Failed to initialize SDL: {SDL.GetError()}"); +Instance.RegisterBackend(); Instance.RegisterBackend(); Instance.RegisterBackend(); -Instance instance = Instance.Create(new InstanceInfo("Graphite.SimpleTest", true)); -Console.WriteLine($"Adapters: {string.Join(", ", instance.EnumerateAdapters())}"); - const int width = 800; const int height = 600; -// Note: The Vulkan flag is here for DXVK support. This flag does not ordinarily need to be passed to CreateWindow. -IntPtr window = SDL.CreateWindow($"Graphite.SimpleTest - {instance.BackendName}", width, height, SDL.WindowFlags.Resizable | SDL.WindowFlags.Vulkan); +SDL.GLSetAttribute(SDL.GLAttr.ContextMajorVersion, OpenGLBackend.MajorVersion); +SDL.GLSetAttribute(SDL.GLAttr.ContextMinorVersion, OpenGLBackend.MinorVersion); +SDL.GLSetAttribute(SDL.GLAttr.ContextProfileMask, (int) SDL.GLProfile.Core); + +IntPtr window = SDL.CreateWindow($"Graphite.SimpleTest", width, height, SDL.WindowFlags.Resizable | SDL.WindowFlags.OpenGL); if (window == IntPtr.Zero) throw new Exception($"Failed to create window: {SDL.GetError()}"); +IntPtr context = SDL.GLCreateContext(window); +SDL.GLMakeCurrent(window, context); + +OpenGLBackend.Context = new GLContext(s => Marshal.GetFunctionPointerForDelegate(SDL.GLGetProcAddress(s)), i => +{ + SDL.GLSetSwapInterval(i); + SDL.GLSwapWindow(window); +}); + +Instance instance = Instance.Create(new InstanceInfo("Graphite.SimpleTest", true)); +SDL.SetWindowTitle(window, SDL.GetWindowTitle(window) + $" - {instance.BackendName}"); + +Console.WriteLine($"Adapters: {string.Join(", ", instance.EnumerateAdapters())}"); + uint properties = SDL.GetWindowProperties(window); SurfaceInfo surfaceInfo; From 0e379d401023978c98aab830e57db9951fe8d54c Mon Sep 17 00:00:00 2001 From: Aqua Date: Fri, 5 Sep 2025 21:37:29 +0100 Subject: [PATCH 05/11] GLSwapchain --- src/Graphite.OpenGL/GLDevice.cs | 39 +++++++++- src/Graphite.OpenGL/GLSwapchain.cs | 103 +++++++++++++++++++++++++-- src/Graphite.OpenGL/GLTexture.cs | 18 +++-- tests/Graphite.SimpleTest/Program.cs | 20 +++--- 4 files changed, 155 insertions(+), 25 deletions(-) diff --git a/src/Graphite.OpenGL/GLDevice.cs b/src/Graphite.OpenGL/GLDevice.cs index 7e97db2..7c1a752 100644 --- a/src/Graphite.OpenGL/GLDevice.cs +++ b/src/Graphite.OpenGL/GLDevice.cs @@ -8,17 +8,21 @@ internal sealed class GLDevice : Device private readonly GL _gl; private readonly GLContext _context; + private readonly Dictionary _framebufferCache; + public override Backend Backend => OpenGLBackend.Backend; public GLDevice(GL gl, GLContext context) { _gl = gl; _context = context; + + _framebufferCache = []; } public override Swapchain CreateSwapchain(in SwapchainInfo info) { - throw new NotImplementedException(); + return new GLSwapchain(_gl, _context, in info); } public override CommandList CreateCommandList() @@ -86,8 +90,37 @@ public override void UnmapBuffer(Buffer buffer) throw new NotImplementedException(); } - public override void Dispose() + public override void Dispose() { } + + public uint GetFramebuffer(ReadOnlySpan colorAttachments, GLTexture? depthAttachment = null) { - throw new NotImplementedException(); + HashCode code = new HashCode(); + + foreach (GLTexture texture in colorAttachments) + code.Add(texture); + + if (depthAttachment != null) + code.Add(depthAttachment); + + int hashCode = code.ToHashCode(); + + if (!_framebufferCache.TryGetValue(hashCode, out uint framebuffer)) + { + GraphiteLog.Log($"Creating framebuffer {hashCode}."); + framebuffer = _gl.CreateFramebuffer(); + + foreach (GLTexture texture in colorAttachments) + { + _gl.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, + TextureTarget.Texture2D, texture.Texture, 0); + } + + if (depthAttachment != null) + throw new NotImplementedException(); + + _framebufferCache.Add(hashCode, framebuffer); + } + + return framebuffer; } } \ No newline at end of file diff --git a/src/Graphite.OpenGL/GLSwapchain.cs b/src/Graphite.OpenGL/GLSwapchain.cs index 04da5f2..06de1e3 100644 --- a/src/Graphite.OpenGL/GLSwapchain.cs +++ b/src/Graphite.OpenGL/GLSwapchain.cs @@ -7,8 +7,10 @@ internal sealed class GLSwapchain : Swapchain { private readonly GL _gl; private readonly GLContext _context; + private readonly uint _vao; + private readonly uint _program; - private uint _texture; + private GLTexture _texture; public override Size2D Size { get; } @@ -22,22 +24,109 @@ public GLSwapchain(GL gl, GLContext context, ref readonly SwapchainInfo info) Size = info.Size; Format = info.Format; - (_, SizedInternalFormat iFormat, _) = GLUtils.ToGL(info.Format); + (_, SizedInternalFormat iFormat, _) = info.Format.ToGL(); - _texture = _gl.GenTexture(); - _gl.BindTexture(TextureTarget.Texture2D, _texture); + uint texture = _gl.GenTexture(); + _gl.BindTexture(TextureTarget.Texture2D, texture); _gl.TexStorage2D(TextureTarget.Texture2D, 1, iFormat, info.Size.Width, info.Size.Height); + + _texture = new GLTexture(_gl, texture, TextureInfo.Texture2D(info.Format, info.Size, 1, TextureUsage.None)); + + _vao = _gl.GenVertexArray(); + _gl.BindVertexArray(_vao); + + uint vertexShader = _gl.CreateShader(ShaderType.VertexShader); + _gl.ShaderSource(vertexShader, VertexShader); + _gl.CompileShader(vertexShader); + _gl.GetShader(vertexShader, ShaderParameterName.CompileStatus, out int status); + if (status != (int) GLEnum.True) + throw new Exception($"Failed to compile vertex shader: {_gl.GetShaderInfoLog(vertexShader)}"); + + uint fragmentShader = _gl.CreateShader(GLEnum.FragmentShader); + _gl.ShaderSource(fragmentShader, FragmentShader); + _gl.CompileShader(fragmentShader); + _gl.GetShader(fragmentShader, ShaderParameterName.CompileStatus, out status); + if (status != (int) GLEnum.True) + throw new Exception($"Failed to compile fragment shader: {_gl.GetShaderInfoLog(fragmentShader)}"); + + _program = _gl.CreateProgram(); + _gl.AttachShader(_program, vertexShader); + _gl.AttachShader(_program, fragmentShader); + + _gl.LinkProgram(_program); + _gl.GetProgram(_program, ProgramPropertyARB.LinkStatus, out status); + if (status != (int) GLEnum.True) + throw new Exception($"Failed to link program: {_gl.GetProgramInfoLog(_program)}"); + + _gl.DetachShader(_program, vertexShader); + _gl.DetachShader(_program, fragmentShader); + _gl.DeleteShader(vertexShader); + _gl.DeleteShader(fragmentShader); } public override Texture GetNextTexture() { - throw new NotImplementedException(); + return _texture; } public override void Present() { + _gl.BindVertexArray(_vao); + + _gl.UseProgram(_program); + _gl.ActiveTexture(TextureUnit.Texture0); + _gl.BindTexture(TextureTarget.Texture2D, _texture.Texture); + _gl.DrawArrays(PrimitiveType.Triangles, 0, 6); + _context.PresentFunc(1); } - - public override void Dispose() { } + + public override void Dispose() + { + _texture.Dispose(); + } + + private const string VertexShader = """ + #version 330 core + + out vec2 frag_TexCoord; + + // XY = Position, ZW = TexCoord + const vec4 vertices[4] = vec4[] + ( + vec4(-1.0, -1.0, 0.0, 0.0), + vec4(-1.0, 1.0, 0.0, 1.0), + vec4( 1.0, 1.0, 1.0, 1.0), + vec4( 1.0, -1.0, 1.0, 0.0) + ); + + const int indices[6] = int[] + ( + 0, 1, 3, + 1, 2, 3 + ); + + void main() + { + vec4 vertex = vertices[indices[gl_VertexID]]; + gl_Position = vec4(vertex.xy, 0.0, 1.0); + frag_TexCoord = vertex.zw; + } + """; + + private const string FragmentShader = """ + #version 330 core + + in vec2 frag_TexCoord; + + out vec4 out_Color; + + uniform sampler2D uTexture; + + void main() + { + out_Color = texture(uTexture, frag_TexCoord); + //out_Color = vec4(frag_TexCoord, 0.0, 1.0); + } + """; } \ No newline at end of file diff --git a/src/Graphite.OpenGL/GLTexture.cs b/src/Graphite.OpenGL/GLTexture.cs index 591806b..4bbd933 100644 --- a/src/Graphite.OpenGL/GLTexture.cs +++ b/src/Graphite.OpenGL/GLTexture.cs @@ -1,13 +1,21 @@ +using Silk.NET.OpenGL; + namespace Graphite.OpenGL; -/*internal sealed class GLTexture : Texture +internal sealed class GLTexture : Texture { - public static + private readonly GL _gl; - public GLTexture(TextureInfo info) : base(info) { } + public readonly uint Texture; + + public GLTexture(GL gl, uint texture, TextureInfo info) : base(info) + { + _gl = gl; + Texture = texture; + } public override void Dispose() { - throw new NotImplementedException(); + _gl.DeleteTexture(Texture); } -}*/ \ No newline at end of file +} \ No newline at end of file diff --git a/tests/Graphite.SimpleTest/Program.cs b/tests/Graphite.SimpleTest/Program.cs index 4cba852..ebb9595 100644 --- a/tests/Graphite.SimpleTest/Program.cs +++ b/tests/Graphite.SimpleTest/Program.cs @@ -82,12 +82,12 @@ Surface surface = instance.CreateSurface(in surfaceInfo); Device device = instance.CreateDevice(surface); -CommandList cl = device.CreateCommandList(); +//CommandList cl = device.CreateCommandList(); Swapchain swapchain = device.CreateSwapchain(new SwapchainInfo(surface, Format.B8G8R8A8_UNorm, new Size2D(width, height), PresentMode.Fifo, 2)); -ReadOnlySpan vertices = +/*ReadOnlySpan vertices = [ -0.5f, -0.5f, 0.0f, 1.0f, -0.5f, +0.5f, 0.0f, 0.0f, @@ -127,7 +127,7 @@ cl.GenerateMipmaps(texture0); cl.GenerateMipmaps(texture1); cl.End(); -device.ExecuteCommandList(cl); +device.ExecuteCommandList(cl);*/ /*uint vertexSize = (uint) vertices.Length * sizeof(float); uint indexSize = (uint) indices.Length * sizeof(ushort); @@ -170,7 +170,7 @@ transferBuffer.Dispose();*/ -string shader = File.ReadAllText("Shader.hlsl"); +/*string shader = File.ReadAllText("Shader.hlsl"); ShaderModule vertexShader = device.CreateShaderModuleFromHLSL(ShaderStage.Vertex, shader, "VSMain"); ShaderModule pixelShader = device.CreateShaderModuleFromHLSL(ShaderStage.Pixel, shader, "PSMain"); @@ -207,7 +207,7 @@ }); pixelShader.Dispose(); -vertexShader.Dispose(); +vertexShader.Dispose();*/ float value = 0; @@ -226,7 +226,7 @@ Texture swapchainTexture = swapchain.GetNextTexture(); - nint map = device.MapBuffer(constantBuffer); + /*nint map = device.MapBuffer(constantBuffer); Matrix4x4 matrix = Matrix4x4.CreateRotationZ(value); unsafe { Unsafe.CopyBlock((void*) map, Unsafe.AsPointer(ref matrix), 64); } device.UnmapBuffer(constantBuffer); @@ -251,11 +251,11 @@ cl.EndRenderPass(); cl.End(); - device.ExecuteCommandList(cl); + device.ExecuteCommandList(cl);*/ swapchain.Present(); } -pipeline.Dispose(); +/*pipeline.Dispose(); transformLayout.Dispose(); textureSet.Dispose(); textureLayout.Dispose(); @@ -264,9 +264,9 @@ texture0.Dispose(); constantBuffer.Dispose(); indexBuffer.Dispose(); -vertexBuffer.Dispose(); +vertexBuffer.Dispose();*/ swapchain.Dispose(); -cl.Dispose(); +//cl.Dispose(); device.Dispose(); surface.Dispose(); instance.Dispose(); From e9d88f9af98379d2d0c47d0d1b09ba6087451718 Mon Sep 17 00:00:00 2001 From: Aqua Date: Fri, 5 Sep 2025 22:01:14 +0100 Subject: [PATCH 06/11] basic GL command list and clear color --- src/Graphite.OpenGL/GLCommandList.cs | 84 +++++++++++++++++++ src/Graphite.OpenGL/GLDevice.cs | 29 ++++++- src/Graphite.OpenGL/GLSwapchain.cs | 8 ++ .../BeginRenderPassInstruction.cs | 16 ++++ .../Instructions/IInstruction.cs | 6 ++ .../Instructions/InstructionType.cs | 6 ++ tests/Graphite.SimpleTest/Program.cs | 12 +-- 7 files changed, 153 insertions(+), 8 deletions(-) create mode 100644 src/Graphite.OpenGL/GLCommandList.cs create mode 100644 src/Graphite.OpenGL/Instructions/BeginRenderPassInstruction.cs create mode 100644 src/Graphite.OpenGL/Instructions/IInstruction.cs create mode 100644 src/Graphite.OpenGL/Instructions/InstructionType.cs diff --git a/src/Graphite.OpenGL/GLCommandList.cs b/src/Graphite.OpenGL/GLCommandList.cs new file mode 100644 index 0000000..ca5f78a --- /dev/null +++ b/src/Graphite.OpenGL/GLCommandList.cs @@ -0,0 +1,84 @@ +using Graphite.Core; +using Graphite.OpenGL.Instructions; + +namespace Graphite.OpenGL; + +internal sealed class GLCommandList : CommandList +{ + public readonly List Instructions; + + public GLCommandList() + { + Instructions = []; + } + + public override void Begin() + { + Instructions.Clear(); + } + + public override void End() { } + + public override void CopyBufferToBuffer(Buffer src, uint srcOffset, Buffer dest, uint destOffset, uint copySize = 0) + { + throw new NotImplementedException(); + } + + public override void CopyBufferToTexture(Buffer src, uint srcOffset, Texture dest, Region3D? region = null) + { + throw new NotImplementedException(); + } + + public override void GenerateMipmaps(Texture texture) + { + throw new NotImplementedException(); + } + + public override void BeginRenderPass(in ReadOnlySpan colorAttachments) + { + Instructions.Add(new BeginRenderPassInstruction + { + ColorAttachments = Array.ConvertAll(colorAttachments.ToArray(), input => (GLTexture) input.Texture), + ClearColor = colorAttachments[0].ClearColor + }); + } + + public override void EndRenderPass() { } + + public override void SetGraphicsPipeline(Pipeline pipeline) + { + throw new NotImplementedException(); + } + + public override void SetDescriptorSet(uint slot, Pipeline pipeline, DescriptorSet set) + { + throw new NotImplementedException(); + } + + public override void SetVertexBuffer(uint slot, Buffer buffer, uint stride, uint offset = 0) + { + throw new NotImplementedException(); + } + + public override void SetIndexBuffer(Buffer buffer, Format format, uint offset = 0) + { + throw new NotImplementedException(); + } + + public override void PushDescriptors(uint slot, Pipeline pipeline, params ReadOnlySpan descriptors) + { + throw new NotImplementedException(); + } + + public override void Draw(uint numVertices, uint firstVertex = 0) + { + throw new NotImplementedException(); + } + + public override void DrawIndexed(uint numIndices, uint firstIndex = 0, int baseVertex = 0) + { + throw new NotImplementedException(); + } + + public override void Dispose() { } +} \ No newline at end of file diff --git a/src/Graphite.OpenGL/GLDevice.cs b/src/Graphite.OpenGL/GLDevice.cs index 7c1a752..28496c9 100644 --- a/src/Graphite.OpenGL/GLDevice.cs +++ b/src/Graphite.OpenGL/GLDevice.cs @@ -1,4 +1,5 @@ using Graphite.Core; +using Graphite.OpenGL.Instructions; using Silk.NET.OpenGL; namespace Graphite.OpenGL; @@ -27,7 +28,7 @@ public override Swapchain CreateSwapchain(in SwapchainInfo info) public override CommandList CreateCommandList() { - throw new NotImplementedException(); + return new GLCommandList(); } public override ShaderModule CreateShaderModule(byte[] code, string entryPoint, ShaderMappingInfo mapping = default) @@ -67,7 +68,27 @@ public override Sampler CreateSampler(in SamplerInfo info) public override void ExecuteCommandList(CommandList cl) { - throw new NotImplementedException(); + GLCommandList glList = (GLCommandList) cl; + + foreach (IInstruction instruction in glList.Instructions) + { + switch (instruction) + { + case BeginRenderPassInstruction renderPass: + { + uint framebuffer = GetFramebuffer(renderPass.ColorAttachments); + _gl.BindFramebuffer(FramebufferTarget.Framebuffer, framebuffer); + + _gl.ClearColor(renderPass.ClearColor.R, renderPass.ClearColor.G, renderPass.ClearColor.B, + renderPass.ClearColor.A); + _gl.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); + + break; + } + default: + throw new ArgumentOutOfRangeException(); + } + } } public override unsafe void UpdateBuffer(Buffer buffer, uint offset, uint size, void* pData) @@ -108,6 +129,7 @@ public uint GetFramebuffer(ReadOnlySpan colorAttachments, GLTexture? { GraphiteLog.Log($"Creating framebuffer {hashCode}."); framebuffer = _gl.CreateFramebuffer(); + _gl.BindFramebuffer(FramebufferTarget.Framebuffer, framebuffer); foreach (GLTexture texture in colorAttachments) { @@ -117,6 +139,9 @@ public uint GetFramebuffer(ReadOnlySpan colorAttachments, GLTexture? if (depthAttachment != null) throw new NotImplementedException(); + + if (_gl.CheckFramebufferStatus(FramebufferTarget.Framebuffer) != GLEnum.FramebufferComplete) + throw new Exception("Framebuffer is not complete!"); _framebufferCache.Add(hashCode, framebuffer); } diff --git a/src/Graphite.OpenGL/GLSwapchain.cs b/src/Graphite.OpenGL/GLSwapchain.cs index 06de1e3..6905c69 100644 --- a/src/Graphite.OpenGL/GLSwapchain.cs +++ b/src/Graphite.OpenGL/GLSwapchain.cs @@ -71,6 +71,14 @@ public override Texture GetNextTexture() public override void Present() { + _gl.Disable(EnableCap.Blend); + _gl.Disable(EnableCap.DepthTest); + + _gl.Enable(EnableCap.CullFace); + _gl.CullFace(TriangleFace.Back); + _gl.FrontFace(FrontFaceDirection.CW); + + _gl.BindFramebuffer(FramebufferTarget.Framebuffer, 0); _gl.BindVertexArray(_vao); _gl.UseProgram(_program); diff --git a/src/Graphite.OpenGL/Instructions/BeginRenderPassInstruction.cs b/src/Graphite.OpenGL/Instructions/BeginRenderPassInstruction.cs new file mode 100644 index 0000000..920f69e --- /dev/null +++ b/src/Graphite.OpenGL/Instructions/BeginRenderPassInstruction.cs @@ -0,0 +1,16 @@ +using Graphite.Core; + +namespace Graphite.OpenGL.Instructions; + +internal struct BeginRenderPassInstruction : IInstruction +{ + public GLTexture[] ColorAttachments; + + public ColorF ClearColor; + + public BeginRenderPassInstruction(GLTexture[] colorAttachments, ColorF clearColor) + { + ColorAttachments = colorAttachments; + ClearColor = clearColor; + } +} \ No newline at end of file diff --git a/src/Graphite.OpenGL/Instructions/IInstruction.cs b/src/Graphite.OpenGL/Instructions/IInstruction.cs new file mode 100644 index 0000000..913186f --- /dev/null +++ b/src/Graphite.OpenGL/Instructions/IInstruction.cs @@ -0,0 +1,6 @@ +namespace Graphite.OpenGL.Instructions; + +internal interface IInstruction +{ + +} \ No newline at end of file diff --git a/src/Graphite.OpenGL/Instructions/InstructionType.cs b/src/Graphite.OpenGL/Instructions/InstructionType.cs new file mode 100644 index 0000000..1ab5ae0 --- /dev/null +++ b/src/Graphite.OpenGL/Instructions/InstructionType.cs @@ -0,0 +1,6 @@ +namespace Graphite.OpenGL.Instructions; + +internal enum InstructionType +{ + BeginRenderPass +} \ No newline at end of file diff --git a/tests/Graphite.SimpleTest/Program.cs b/tests/Graphite.SimpleTest/Program.cs index ebb9595..66e91b5 100644 --- a/tests/Graphite.SimpleTest/Program.cs +++ b/tests/Graphite.SimpleTest/Program.cs @@ -82,7 +82,7 @@ Surface surface = instance.CreateSurface(in surfaceInfo); Device device = instance.CreateDevice(surface); -//CommandList cl = device.CreateCommandList(); +CommandList cl = device.CreateCommandList(); Swapchain swapchain = device.CreateSwapchain(new SwapchainInfo(surface, Format.B8G8R8A8_UNorm, new Size2D(width, height), PresentMode.Fifo, 2)); @@ -232,12 +232,12 @@ device.UnmapBuffer(constantBuffer); value += 0.01f; if (value >= float.Pi * 2) - value -= float.Pi * 2; + value -= float.Pi * 2;*/ cl.Begin(); cl.BeginRenderPass([new ColorAttachmentInfo(swapchainTexture, new ColorF(Color.CornflowerBlue))]); - cl.SetGraphicsPipeline(pipeline); + /*cl.SetGraphicsPipeline(pipeline); cl.SetDescriptorSet(0, pipeline, textureSet); cl.PushDescriptors(1, pipeline, new Descriptor(0, DescriptorType.ConstantBuffer, constantBuffer)); @@ -245,13 +245,13 @@ cl.SetVertexBuffer(0, vertexBuffer, 4 * sizeof(float)); cl.SetIndexBuffer(indexBuffer, Format.R16_UInt); - cl.DrawIndexed(6); + cl.DrawIndexed(6);*/ //cl.Draw(6); cl.EndRenderPass(); cl.End(); - device.ExecuteCommandList(cl);*/ + device.ExecuteCommandList(cl); swapchain.Present(); } @@ -266,7 +266,7 @@ indexBuffer.Dispose(); vertexBuffer.Dispose();*/ swapchain.Dispose(); -//cl.Dispose(); +cl.Dispose(); device.Dispose(); surface.Dispose(); instance.Dispose(); From 65f6e11153364911bddac94fc273b59aa35894d8 Mon Sep 17 00:00:00 2001 From: Aqua Date: Sat, 6 Sep 2025 14:27:53 +0100 Subject: [PATCH 07/11] GL shader modules --- src/Graphite.D3D11/D3D11Device.cs | 3 +- src/Graphite.OpenGL/GLDevice.cs | 5 +-- src/Graphite.OpenGL/GLShaderModule.cs | 36 ++++++++++++++++++++ src/Graphite.ShaderTools/Compiler.cs | 29 +++++++++++++++- src/Graphite.ShaderTools/DeviceExtensions.cs | 4 +-- src/Graphite.Vulkan/VulkanDevice.cs | 3 +- src/Graphite/Device.cs | 4 ++- tests/Graphite.SimpleTest/Program.cs | 8 ++--- 8 files changed, 80 insertions(+), 12 deletions(-) create mode 100644 src/Graphite.OpenGL/GLShaderModule.cs diff --git a/src/Graphite.D3D11/D3D11Device.cs b/src/Graphite.D3D11/D3D11Device.cs index bbedb2f..2adf758 100644 --- a/src/Graphite.D3D11/D3D11Device.cs +++ b/src/Graphite.D3D11/D3D11Device.cs @@ -49,7 +49,8 @@ public override CommandList CreateCommandList() return new D3D11CommandList(_device); } - public override ShaderModule CreateShaderModule(byte[] code, string entryPoint, ShaderMappingInfo mapping = default) + public override ShaderModule CreateShaderModule(ShaderStage stage, byte[] code, string entryPoint, + ShaderMappingInfo mapping = default) { return new D3D11ShaderModule(code, in mapping); } diff --git a/src/Graphite.OpenGL/GLDevice.cs b/src/Graphite.OpenGL/GLDevice.cs index 28496c9..677792b 100644 --- a/src/Graphite.OpenGL/GLDevice.cs +++ b/src/Graphite.OpenGL/GLDevice.cs @@ -31,9 +31,10 @@ public override CommandList CreateCommandList() return new GLCommandList(); } - public override ShaderModule CreateShaderModule(byte[] code, string entryPoint, ShaderMappingInfo mapping = default) + public override ShaderModule CreateShaderModule(ShaderStage stage, byte[] code, string entryPoint, + ShaderMappingInfo mapping = default) { - throw new NotImplementedException(); + return new GLShaderModule(_gl, stage, code); } public override Pipeline CreateGraphicsPipeline(in GraphicsPipelineInfo info) diff --git a/src/Graphite.OpenGL/GLShaderModule.cs b/src/Graphite.OpenGL/GLShaderModule.cs new file mode 100644 index 0000000..95a6ff6 --- /dev/null +++ b/src/Graphite.OpenGL/GLShaderModule.cs @@ -0,0 +1,36 @@ +using System.Text; +using Silk.NET.OpenGL; + +namespace Graphite.OpenGL; + +internal sealed class GLShaderModule : ShaderModule +{ + private readonly GL _gl; + + public readonly uint Shader; + + public GLShaderModule(GL gl, ShaderStage stage, byte[] glsl) + { + _gl = gl; + string sglsl = Encoding.UTF8.GetString(glsl); + + ShaderType type = stage switch + { + ShaderStage.Vertex => ShaderType.VertexShader, + ShaderStage.Pixel => ShaderType.FragmentShader, + _ => throw new ArgumentOutOfRangeException(nameof(stage), stage, null) + }; + + Shader = _gl.CreateShader(type); + _gl.ShaderSource(Shader, sglsl); + _gl.CompileShader(Shader); + + if (_gl.GetShader(Shader, ShaderParameterName.CompileStatus) != (int) GLEnum.True) + throw new Exception($"Failed to compile {stage} shader: {_gl.GetShaderInfoLog(Shader)}"); + } + + public override void Dispose() + { + _gl.DeleteShader(Shader); + } +} \ No newline at end of file diff --git a/src/Graphite.ShaderTools/Compiler.cs b/src/Graphite.ShaderTools/Compiler.cs index 2866d00..0fcd751 100644 --- a/src/Graphite.ShaderTools/Compiler.cs +++ b/src/Graphite.ShaderTools/Compiler.cs @@ -218,7 +218,24 @@ public static byte[] TranspileSpirv(GrBackend backend, byte[] spirv, ShaderStage CompilerOptions* options; CheckResult(_spirv.CompilerCreateCompilerOptions(compiler, &options), "Create compiler options"); - CheckResult(_spirv.CompilerOptionsSetUint(options, CompilerOption.HlslShaderModel, 50), "Set shader model"); + + switch (spvBackend) + { + case SpvBackend.Hlsl: + { + CheckResult(_spirv.CompilerOptionsSetUint(options, CompilerOption.HlslShaderModel, 50), "Set shader model"); + break; + } + case SpvBackend.Glsl: + { + CheckResult(_spirv.CompilerOptionsSetUint(options, CompilerOption.GlslVersion, 430), "Set GLSL version"); + break; + } + + default: + throw new ArgumentOutOfRangeException(); + } + CheckResult(_spirv.CompilerInstallCompilerOptions(compiler, options), "Install compiler options"); ExecutionModel model = stage switch @@ -278,6 +295,16 @@ public static byte[] TranspileSpirv(GrBackend backend, byte[] spirv, ShaderStage mapping.VertexInput = vertexInput; return CompileDXBC(compiled, "main", stage); } + + if (backend == GrBackend.OpenGL) + { + nuint length = strlen(compiled); + byte[] glslBytes = new byte[length]; + fixed (byte* pGlsl = glslBytes) + Unsafe.CopyBlock(pGlsl, compiled, (uint) length); + + return glslBytes; + } } finally { diff --git a/src/Graphite.ShaderTools/DeviceExtensions.cs b/src/Graphite.ShaderTools/DeviceExtensions.cs index 44c3445..38ed871 100644 --- a/src/Graphite.ShaderTools/DeviceExtensions.cs +++ b/src/Graphite.ShaderTools/DeviceExtensions.cs @@ -18,7 +18,7 @@ public static ShaderModule CreateShaderModuleFromHLSL(this Device device, Shader byte[] compiled = Compiler.CompileHLSL(device.Backend, stage, hlsl, entryPoint, out ShaderMappingInfo mapping, includeDir, debug); - return device.CreateShaderModule(compiled, entryPoint, mapping); + return device.CreateShaderModule(stage, compiled, entryPoint, mapping); } /// @@ -35,6 +35,6 @@ public static ShaderModule CreateShaderModuleFromSpirv(this Device device, Shade byte[] compiled = Compiler.TranspileSpirv(device.Backend, spirv, stage, entryPoint, out ShaderMappingInfo mapping); - return device.CreateShaderModule(compiled, entryPoint, mapping); + return device.CreateShaderModule(stage, compiled, entryPoint, mapping); } } \ No newline at end of file diff --git a/src/Graphite.Vulkan/VulkanDevice.cs b/src/Graphite.Vulkan/VulkanDevice.cs index 5c0b372..06cfa6b 100644 --- a/src/Graphite.Vulkan/VulkanDevice.cs +++ b/src/Graphite.Vulkan/VulkanDevice.cs @@ -204,7 +204,8 @@ public override CommandList CreateCommandList() return new VulkanCommandList(_vk, Instance, Device, _pool); } - public override ShaderModule CreateShaderModule(byte[] code, string entryPoint, ShaderMappingInfo mapping = default) + public override ShaderModule CreateShaderModule(ShaderStage stage, byte[] code, string entryPoint, + ShaderMappingInfo mapping = default) { // Vulkan natively supports descriptor sets and does not need any vertex remapping, so we ignore any mapping values. return new VulkanShaderModule(_vk, Device, code, entryPoint); diff --git a/src/Graphite/Device.cs b/src/Graphite/Device.cs index 30a3ea4..582beb4 100644 --- a/src/Graphite/Device.cs +++ b/src/Graphite/Device.cs @@ -50,11 +50,13 @@ public abstract class Device : IDisposable /// In order to translate vertex input and descriptors for backends that may not support them, you must provide the /// parameter. /// + /// /// The shader bytecode/string. /// The entry point of the shader. /// The shader mapping to other backends, if appropriate. /// The created . - public abstract ShaderModule CreateShaderModule(byte[] code, string entryPoint, ShaderMappingInfo mapping = default); + public abstract ShaderModule CreateShaderModule(ShaderStage stage, byte[] code, string entryPoint, + ShaderMappingInfo mapping = default); /// /// Create a graphics . diff --git a/tests/Graphite.SimpleTest/Program.cs b/tests/Graphite.SimpleTest/Program.cs index 66e91b5..ce7b382 100644 --- a/tests/Graphite.SimpleTest/Program.cs +++ b/tests/Graphite.SimpleTest/Program.cs @@ -170,12 +170,12 @@ transferBuffer.Dispose();*/ -/*string shader = File.ReadAllText("Shader.hlsl"); +string shader = File.ReadAllText("SimpleShader.hlsl"); ShaderModule vertexShader = device.CreateShaderModuleFromHLSL(ShaderStage.Vertex, shader, "VSMain"); ShaderModule pixelShader = device.CreateShaderModuleFromHLSL(ShaderStage.Pixel, shader, "PSMain"); -DescriptorLayout textureLayout = device.CreateDescriptorLayout(new DescriptorLayoutInfo +/*DescriptorLayout textureLayout = device.CreateDescriptorLayout(new DescriptorLayoutInfo { Bindings = [ @@ -204,10 +204,10 @@ new InputElementDescription(Format.R32G32_Float, 8, 1, 0) ], Descriptors = [textureLayout, transformLayout] -}); +});*/ pixelShader.Dispose(); -vertexShader.Dispose();*/ +vertexShader.Dispose(); float value = 0; From 2bd4c8478285c8f561a0db3133f2961dd94a4181 Mon Sep 17 00:00:00 2001 From: Aqua Date: Sat, 6 Sep 2025 17:02:28 +0100 Subject: [PATCH 08/11] basic GL pipeline --- src/Graphite.OpenGL/GLDevice.cs | 2 +- src/Graphite.OpenGL/GLPipeline.cs | 70 ++++++++++++++++++++++++++++ tests/Graphite.SimpleTest/Program.cs | 12 ++--- 3 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 src/Graphite.OpenGL/GLPipeline.cs diff --git a/src/Graphite.OpenGL/GLDevice.cs b/src/Graphite.OpenGL/GLDevice.cs index 677792b..d320654 100644 --- a/src/Graphite.OpenGL/GLDevice.cs +++ b/src/Graphite.OpenGL/GLDevice.cs @@ -39,7 +39,7 @@ public override ShaderModule CreateShaderModule(ShaderStage stage, byte[] code, public override Pipeline CreateGraphicsPipeline(in GraphicsPipelineInfo info) { - throw new NotImplementedException(); + return new GLPipeline(_gl, in info); } public override unsafe Buffer CreateBuffer(in BufferInfo info, void* data) diff --git a/src/Graphite.OpenGL/GLPipeline.cs b/src/Graphite.OpenGL/GLPipeline.cs new file mode 100644 index 0000000..fa290e7 --- /dev/null +++ b/src/Graphite.OpenGL/GLPipeline.cs @@ -0,0 +1,70 @@ +using Silk.NET.OpenGL; + +namespace Graphite.OpenGL; + +internal sealed class GLPipeline : Pipeline +{ + private readonly GL _gl; + + public readonly uint VertexArray; + + public readonly uint ShaderProgram; + + public GLPipeline(GL gl, ref readonly GraphicsPipelineInfo info) + { + _gl = gl; + + VertexArray = _gl.GenVertexArray(); + _gl.BindVertexArray(VertexArray); + + for (int i = 0; i < info.InputLayout.Length; i++) + { + ref readonly InputElementDescription element = ref info.InputLayout[i]; + + uint location = element.Location; + uint offset = element.Offset; + + _gl.EnableVertexAttribArray(location); + _gl.VertexAttribBinding(location, element.Slot); + + switch (element.Format) + { + case Format.R32_Float: + _gl.VertexAttribFormat(location, 1, VertexAttribType.Float, false, offset); + break; + case Format.R32G32_Float: + _gl.VertexAttribFormat(location, 2, VertexAttribType.Float, false, offset); + break; + case Format.R32G32B32_Float: + _gl.VertexAttribFormat(location, 3, VertexAttribType.Float, false, offset); + break; + case Format.R32G32B32A32_Float: + _gl.VertexAttribFormat(location, 4, VertexAttribType.Float, false, offset); + break; + default: + throw new NotImplementedException(); + } + } + + GLShaderModule vertexShader = (GLShaderModule) info.VertexShader; + GLShaderModule pixelShader = (GLShaderModule) info.PixelShader; + + ShaderProgram = _gl.CreateProgram(); + _gl.AttachShader(ShaderProgram, vertexShader.Shader); + _gl.AttachShader(ShaderProgram, pixelShader.Shader); + + _gl.LinkProgram(ShaderProgram); + + if (_gl.GetProgram(ShaderProgram, ProgramPropertyARB.LinkStatus) != (int) GLEnum.True) + throw new Exception($"Failed to link program: {_gl.GetProgramInfoLog(ShaderProgram)}"); + + _gl.DetachShader(ShaderProgram, pixelShader.Shader); + _gl.DetachShader(ShaderProgram, vertexShader.Shader); + } + + public override void Dispose() + { + _gl.DeleteProgram(ShaderProgram); + _gl.DeleteVertexArray(VertexArray); + } +} \ No newline at end of file diff --git a/tests/Graphite.SimpleTest/Program.cs b/tests/Graphite.SimpleTest/Program.cs index ce7b382..273dba6 100644 --- a/tests/Graphite.SimpleTest/Program.cs +++ b/tests/Graphite.SimpleTest/Program.cs @@ -191,20 +191,20 @@ { Bindings = [new DescriptorBinding(0, DescriptorType.ConstantBuffer, ShaderStage.Vertex)], PushDescriptor = true -}); +});*/ Pipeline pipeline = device.CreateGraphicsPipeline(new GraphicsPipelineInfo { VertexShader = vertexShader, PixelShader = pixelShader, ColorTargets = [new ColorTargetInfo(Format.B8G8R8A8_UNorm)], - InputLayout = + /*InputLayout = [ new InputElementDescription(Format.R32G32_Float, 0, 0, 0), new InputElementDescription(Format.R32G32_Float, 8, 1, 0) ], - Descriptors = [textureLayout, transformLayout] -});*/ + Descriptors = [textureLayout, transformLayout]*/ +}); pixelShader.Dispose(); vertexShader.Dispose(); @@ -255,8 +255,8 @@ swapchain.Present(); } -/*pipeline.Dispose(); -transformLayout.Dispose(); +pipeline.Dispose(); +/*transformLayout.Dispose(); textureSet.Dispose(); textureLayout.Dispose(); sampler.Dispose(); From a879924fe970a0c8f78724a1048ff86861d0991f Mon Sep 17 00:00:00 2001 From: Aqua Date: Sat, 6 Sep 2025 17:07:39 +0100 Subject: [PATCH 09/11] basic drawing --- src/Graphite.OpenGL/GLCommandList.cs | 11 +++++++++-- src/Graphite.OpenGL/GLDevice.cs | 15 +++++++++++++++ .../Instructions/DrawInstruction.cs | 8 ++++++++ .../Instructions/SetPipelineInstruction.cs | 6 ++++++ tests/Graphite.SimpleTest/Program.cs | 6 +++--- 5 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 src/Graphite.OpenGL/Instructions/DrawInstruction.cs create mode 100644 src/Graphite.OpenGL/Instructions/SetPipelineInstruction.cs diff --git a/src/Graphite.OpenGL/GLCommandList.cs b/src/Graphite.OpenGL/GLCommandList.cs index ca5f78a..2e31644 100644 --- a/src/Graphite.OpenGL/GLCommandList.cs +++ b/src/Graphite.OpenGL/GLCommandList.cs @@ -47,7 +47,10 @@ public override void EndRenderPass() { } public override void SetGraphicsPipeline(Pipeline pipeline) { - throw new NotImplementedException(); + Instructions.Add(new SetPipelineInstruction() + { + Pipeline = (GLPipeline) pipeline + }); } public override void SetDescriptorSet(uint slot, Pipeline pipeline, DescriptorSet set) @@ -72,7 +75,11 @@ public override void PushDescriptors(uint slot, Pipeline pipeline, params ReadOn public override void Draw(uint numVertices, uint firstVertex = 0) { - throw new NotImplementedException(); + Instructions.Add(new DrawInstruction() + { + NumVertices = numVertices, + FirstVertex = firstVertex + }); } public override void DrawIndexed(uint numIndices, uint firstIndex = 0, int baseVertex = 0) diff --git a/src/Graphite.OpenGL/GLDevice.cs b/src/Graphite.OpenGL/GLDevice.cs index d320654..325a215 100644 --- a/src/Graphite.OpenGL/GLDevice.cs +++ b/src/Graphite.OpenGL/GLDevice.cs @@ -86,6 +86,21 @@ public override void ExecuteCommandList(CommandList cl) break; } + + case SetPipelineInstruction setPipeline: + { + GLPipeline pipeline = setPipeline.Pipeline; + _gl.BindVertexArray(pipeline.VertexArray); + _gl.UseProgram(pipeline.ShaderProgram); + break; + } + + case DrawInstruction draw: + { + _gl.DrawArrays(PrimitiveType.Triangles, (int) draw.FirstVertex, draw.NumVertices); + break; + } + default: throw new ArgumentOutOfRangeException(); } diff --git a/src/Graphite.OpenGL/Instructions/DrawInstruction.cs b/src/Graphite.OpenGL/Instructions/DrawInstruction.cs new file mode 100644 index 0000000..e6b8a12 --- /dev/null +++ b/src/Graphite.OpenGL/Instructions/DrawInstruction.cs @@ -0,0 +1,8 @@ +namespace Graphite.OpenGL.Instructions; + +public struct DrawInstruction : IInstruction +{ + public uint NumVertices; + + public uint FirstVertex; +} \ No newline at end of file diff --git a/src/Graphite.OpenGL/Instructions/SetPipelineInstruction.cs b/src/Graphite.OpenGL/Instructions/SetPipelineInstruction.cs new file mode 100644 index 0000000..b207f09 --- /dev/null +++ b/src/Graphite.OpenGL/Instructions/SetPipelineInstruction.cs @@ -0,0 +1,6 @@ +namespace Graphite.OpenGL.Instructions; + +internal struct SetPipelineInstruction : IInstruction +{ + public GLPipeline Pipeline; +} \ No newline at end of file diff --git a/tests/Graphite.SimpleTest/Program.cs b/tests/Graphite.SimpleTest/Program.cs index 273dba6..39e3fd7 100644 --- a/tests/Graphite.SimpleTest/Program.cs +++ b/tests/Graphite.SimpleTest/Program.cs @@ -237,16 +237,16 @@ cl.Begin(); cl.BeginRenderPass([new ColorAttachmentInfo(swapchainTexture, new ColorF(Color.CornflowerBlue))]); - /*cl.SetGraphicsPipeline(pipeline); + cl.SetGraphicsPipeline(pipeline); - cl.SetDescriptorSet(0, pipeline, textureSet); + /*cl.SetDescriptorSet(0, pipeline, textureSet); cl.PushDescriptors(1, pipeline, new Descriptor(0, DescriptorType.ConstantBuffer, constantBuffer)); cl.SetVertexBuffer(0, vertexBuffer, 4 * sizeof(float)); cl.SetIndexBuffer(indexBuffer, Format.R16_UInt); cl.DrawIndexed(6);*/ - //cl.Draw(6); + cl.Draw(6); cl.EndRenderPass(); cl.End(); From adf459ce29d564419fa5be309709c4c91f0707b9 Mon Sep 17 00:00:00 2001 From: Aqua Date: Sat, 6 Sep 2025 17:40:44 +0100 Subject: [PATCH 10/11] GL buffers --- src/Graphite.OpenGL/GLBuffer.cs | 24 ++++++++++++++++++++++++ src/Graphite.OpenGL/GLDevice.cs | 2 +- tests/Graphite.SimpleTest/Program.cs | 22 +++++++++++----------- tests/Graphite.SimpleTest/Shader.hlsl | 8 ++++---- 4 files changed, 40 insertions(+), 16 deletions(-) create mode 100644 src/Graphite.OpenGL/GLBuffer.cs diff --git a/src/Graphite.OpenGL/GLBuffer.cs b/src/Graphite.OpenGL/GLBuffer.cs new file mode 100644 index 0000000..e9caa35 --- /dev/null +++ b/src/Graphite.OpenGL/GLBuffer.cs @@ -0,0 +1,24 @@ +using Silk.NET.OpenGL; + +namespace Graphite.OpenGL; + +internal sealed unsafe class GLBuffer : Buffer +{ + private readonly GL _gl; + + public readonly uint Buffer; + + public GLBuffer(GL gl, ref readonly BufferInfo info, void* pData) : base(info) + { + _gl = gl; + + Buffer = _gl.GenBuffer(); + _gl.BindBuffer(BufferTargetARB.ArrayBuffer, Buffer); + _gl.BufferData(BufferTargetARB.ArrayBuffer, info.SizeInBytes, pData, BufferUsageARB.StaticDraw); + } + + public override void Dispose() + { + _gl.DeleteBuffer(Buffer); + } +} \ No newline at end of file diff --git a/src/Graphite.OpenGL/GLDevice.cs b/src/Graphite.OpenGL/GLDevice.cs index 325a215..9693970 100644 --- a/src/Graphite.OpenGL/GLDevice.cs +++ b/src/Graphite.OpenGL/GLDevice.cs @@ -44,7 +44,7 @@ public override Pipeline CreateGraphicsPipeline(in GraphicsPipelineInfo info) public override unsafe Buffer CreateBuffer(in BufferInfo info, void* data) { - throw new NotImplementedException(); + return new GLBuffer(_gl, in info, data); } public override unsafe Texture CreateTexture(in TextureInfo info, void* pData) diff --git a/tests/Graphite.SimpleTest/Program.cs b/tests/Graphite.SimpleTest/Program.cs index 39e3fd7..4eba2e5 100644 --- a/tests/Graphite.SimpleTest/Program.cs +++ b/tests/Graphite.SimpleTest/Program.cs @@ -21,7 +21,7 @@ throw new Exception($"Failed to initialize SDL: {SDL.GetError()}"); Instance.RegisterBackend(); -Instance.RegisterBackend(); +//Instance.RegisterBackend(); Instance.RegisterBackend(); const int width = 800; @@ -87,7 +87,7 @@ device.CreateSwapchain(new SwapchainInfo(surface, Format.B8G8R8A8_UNorm, new Size2D(width, height), PresentMode.Fifo, 2)); -/*ReadOnlySpan vertices = +ReadOnlySpan vertices = [ -0.5f, -0.5f, 0.0f, 1.0f, -0.5f, +0.5f, 0.0f, 0.0f, @@ -101,7 +101,7 @@ 1, 2, 3 ]; -ImageResult result0 = ImageResult.FromMemory(File.ReadAllBytes("DEBUG.png"), ColorComponents.RedGreenBlueAlpha); +/*ImageResult result0 = ImageResult.FromMemory(File.ReadAllBytes("DEBUG.png"), ColorComponents.RedGreenBlueAlpha); ImageResult result1 = ImageResult.FromMemory(File.ReadAllBytes("Bagel.png"), ColorComponents.RedGreenBlueAlpha); TextureInfo textureInfo = new() @@ -114,13 +114,13 @@ }; Texture texture0 = device.CreateTexture(in textureInfo, result0.Data); -Sampler sampler = device.CreateSampler(SamplerInfo.LinearClamp); +Sampler sampler = device.CreateSampler(SamplerInfo.LinearClamp);*/ Buffer vertexBuffer = device.CreateBuffer(BufferUsage.VertexBuffer, vertices); Buffer indexBuffer = device.CreateBuffer(BufferUsage.IndexBuffer, indices); Buffer constantBuffer = device.CreateBuffer(BufferUsage.ConstantBuffer | BufferUsage.MapWrite, Matrix4x4.CreateRotationZ(1)); -textureInfo.Size = new Size3D((uint) result1.Width, (uint) result1.Height); +/*textureInfo.Size = new Size3D((uint) result1.Width, (uint) result1.Height); Texture texture1 = device.CreateTexture(in textureInfo, result1.Data); cl.Begin(); @@ -170,7 +170,7 @@ transferBuffer.Dispose();*/ -string shader = File.ReadAllText("SimpleShader.hlsl"); +string shader = File.ReadAllText("Shader.hlsl"); ShaderModule vertexShader = device.CreateShaderModuleFromHLSL(ShaderStage.Vertex, shader, "VSMain"); ShaderModule pixelShader = device.CreateShaderModuleFromHLSL(ShaderStage.Pixel, shader, "PSMain"); @@ -198,12 +198,12 @@ VertexShader = vertexShader, PixelShader = pixelShader, ColorTargets = [new ColorTargetInfo(Format.B8G8R8A8_UNorm)], - /*InputLayout = + InputLayout = [ new InputElementDescription(Format.R32G32_Float, 0, 0, 0), new InputElementDescription(Format.R32G32_Float, 8, 1, 0) ], - Descriptors = [textureLayout, transformLayout]*/ + //Descriptors = [textureLayout, transformLayout] }); pixelShader.Dispose(); @@ -240,13 +240,13 @@ cl.SetGraphicsPipeline(pipeline); /*cl.SetDescriptorSet(0, pipeline, textureSet); - cl.PushDescriptors(1, pipeline, new Descriptor(0, DescriptorType.ConstantBuffer, constantBuffer)); + cl.PushDescriptors(1, pipeline, new Descriptor(0, DescriptorType.ConstantBuffer, constantBuffer));*/ cl.SetVertexBuffer(0, vertexBuffer, 4 * sizeof(float)); cl.SetIndexBuffer(indexBuffer, Format.R16_UInt); - cl.DrawIndexed(6);*/ - cl.Draw(6); + cl.DrawIndexed(6); + //cl.Draw(6); cl.EndRenderPass(); cl.End(); diff --git a/tests/Graphite.SimpleTest/Shader.hlsl b/tests/Graphite.SimpleTest/Shader.hlsl index 96132cb..b05c66e 100644 --- a/tests/Graphite.SimpleTest/Shader.hlsl +++ b/tests/Graphite.SimpleTest/Shader.hlsl @@ -24,8 +24,8 @@ VSOutput VSMain(const in VSInput input) { VSOutput output; - output.Position = mul(Transform, float4(input.Position, 0.0, 1.0)); - //output.Position = float4(input.Position, 0.0, 1.0); + //output.Position = mul(Transform, float4(input.Position, 0.0, 1.0)); + output.Position = float4(input.Position, 0.0, 1.0); output.TexCoord = input.TexCoord; return output; @@ -33,6 +33,6 @@ VSOutput VSMain(const in VSInput input) float4 PSMain(const in VSOutput input): SV_Target0 { - return lerp(Texture0.Sample(Sampler0, input.TexCoord), Texture1.Sample(Sampler1, input.TexCoord), 0.5); - //return float4(input.TexCoord, 0.0, 1.0); + //return lerp(Texture0.Sample(Sampler0, input.TexCoord), Texture1.Sample(Sampler1, input.TexCoord), 0.5); + return float4(input.TexCoord, 0.0, 1.0); } \ No newline at end of file From 2c887516f0e03e64bc3036891233a1e628a26207 Mon Sep 17 00:00:00 2001 From: Aqua Date: Sat, 6 Sep 2025 18:11:02 +0100 Subject: [PATCH 11/11] GL draw with buffers --- src/Graphite.OpenGL/GLCommandList.cs | 6 ++-- src/Graphite.OpenGL/GLDevice.cs | 35 +++++++++++++++++-- .../Instructions/DrawIndexedInstruction.cs | 17 +++++++++ .../Instructions/SetIndexBufferInstruction.cs | 17 +++++++++ .../SetVertexBufferInstruction.cs | 20 +++++++++++ 5 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 src/Graphite.OpenGL/Instructions/DrawIndexedInstruction.cs create mode 100644 src/Graphite.OpenGL/Instructions/SetIndexBufferInstruction.cs create mode 100644 src/Graphite.OpenGL/Instructions/SetVertexBufferInstruction.cs diff --git a/src/Graphite.OpenGL/GLCommandList.cs b/src/Graphite.OpenGL/GLCommandList.cs index 2e31644..52a7a47 100644 --- a/src/Graphite.OpenGL/GLCommandList.cs +++ b/src/Graphite.OpenGL/GLCommandList.cs @@ -60,12 +60,12 @@ public override void SetDescriptorSet(uint slot, Pipeline pipeline, DescriptorSe public override void SetVertexBuffer(uint slot, Buffer buffer, uint stride, uint offset = 0) { - throw new NotImplementedException(); + Instructions.Add(new SetVertexBufferInstruction(slot, (GLBuffer) buffer, stride, offset)); } public override void SetIndexBuffer(Buffer buffer, Format format, uint offset = 0) { - throw new NotImplementedException(); + Instructions.Add(new SetIndexBufferInstruction((GLBuffer) buffer, format, offset)); } public override void PushDescriptors(uint slot, Pipeline pipeline, params ReadOnlySpan descriptors) @@ -84,7 +84,7 @@ public override void Draw(uint numVertices, uint firstVertex = 0) public override void DrawIndexed(uint numIndices, uint firstIndex = 0, int baseVertex = 0) { - throw new NotImplementedException(); + Instructions.Add(new DrawIndexedInstruction(numIndices, firstIndex, baseVertex)); } public override void Dispose() { } diff --git a/src/Graphite.OpenGL/GLDevice.cs b/src/Graphite.OpenGL/GLDevice.cs index 9693970..910d1ea 100644 --- a/src/Graphite.OpenGL/GLDevice.cs +++ b/src/Graphite.OpenGL/GLDevice.cs @@ -4,7 +4,7 @@ namespace Graphite.OpenGL; -internal sealed class GLDevice : Device +internal sealed unsafe class GLDevice : Device { private readonly GL _gl; private readonly GLContext _context; @@ -71,6 +71,8 @@ public override void ExecuteCommandList(CommandList cl) { GLCommandList glList = (GLCommandList) cl; + DrawElementsType elementsType = 0; + foreach (IInstruction instruction in glList.Instructions) { switch (instruction) @@ -95,11 +97,38 @@ public override void ExecuteCommandList(CommandList cl) break; } + case SetVertexBufferInstruction setVertexBuffer: + { + _gl.BindVertexBuffer(setVertexBuffer.Slot, setVertexBuffer.Buffer.Buffer, + (nint) setVertexBuffer.Offset, setVertexBuffer.Stride); + break; + } + + case SetIndexBufferInstruction setIndexBuffer: + { + _gl.BindBuffer(BufferTargetARB.ElementArrayBuffer, setIndexBuffer.Buffer.Buffer); + elementsType = setIndexBuffer.Format switch + { + Format.R8_UInt => DrawElementsType.UnsignedByte, + Format.R16_UInt => DrawElementsType.UnsignedShort, + Format.R32_UInt => DrawElementsType.UnsignedInt, + _ => throw new NotSupportedException() + }; + break; + } + case DrawInstruction draw: { _gl.DrawArrays(PrimitiveType.Triangles, (int) draw.FirstVertex, draw.NumVertices); break; } + + case DrawIndexedInstruction drawIndexed: + { + _gl.DrawElementsBaseVertex(PrimitiveType.Triangles, drawIndexed.NumIndices, elementsType, + (void*) drawIndexed.FirstIndex, drawIndexed.BaseVertex); + break; + } default: throw new ArgumentOutOfRangeException(); @@ -107,12 +136,12 @@ public override void ExecuteCommandList(CommandList cl) } } - public override unsafe void UpdateBuffer(Buffer buffer, uint offset, uint size, void* pData) + public override void UpdateBuffer(Buffer buffer, uint offset, uint size, void* pData) { throw new NotImplementedException(); } - public override unsafe void UpdateTexture(Texture texture, in Region3D region, void* pData) + public override void UpdateTexture(Texture texture, in Region3D region, void* pData) { throw new NotImplementedException(); } diff --git a/src/Graphite.OpenGL/Instructions/DrawIndexedInstruction.cs b/src/Graphite.OpenGL/Instructions/DrawIndexedInstruction.cs new file mode 100644 index 0000000..78c8628 --- /dev/null +++ b/src/Graphite.OpenGL/Instructions/DrawIndexedInstruction.cs @@ -0,0 +1,17 @@ +namespace Graphite.OpenGL.Instructions; + +internal struct DrawIndexedInstruction : IInstruction +{ + public uint NumIndices; + + public uint FirstIndex; + + public int BaseVertex; + + public DrawIndexedInstruction(uint numIndices, uint firstIndex, int baseVertex) + { + NumIndices = numIndices; + FirstIndex = firstIndex; + BaseVertex = baseVertex; + } +} \ No newline at end of file diff --git a/src/Graphite.OpenGL/Instructions/SetIndexBufferInstruction.cs b/src/Graphite.OpenGL/Instructions/SetIndexBufferInstruction.cs new file mode 100644 index 0000000..874ef2a --- /dev/null +++ b/src/Graphite.OpenGL/Instructions/SetIndexBufferInstruction.cs @@ -0,0 +1,17 @@ +namespace Graphite.OpenGL.Instructions; + +internal struct SetIndexBufferInstruction : IInstruction +{ + public GLBuffer Buffer; + + public Format Format; + + public uint Offset; + + public SetIndexBufferInstruction(GLBuffer buffer, Format format, uint offset) + { + Buffer = buffer; + Format = format; + Offset = offset; + } +} \ No newline at end of file diff --git a/src/Graphite.OpenGL/Instructions/SetVertexBufferInstruction.cs b/src/Graphite.OpenGL/Instructions/SetVertexBufferInstruction.cs new file mode 100644 index 0000000..2392fa0 --- /dev/null +++ b/src/Graphite.OpenGL/Instructions/SetVertexBufferInstruction.cs @@ -0,0 +1,20 @@ +namespace Graphite.OpenGL.Instructions; + +internal struct SetVertexBufferInstruction : IInstruction +{ + public uint Slot; + + public GLBuffer Buffer; + + public uint Stride; + + public uint Offset; + + public SetVertexBufferInstruction(uint slot, GLBuffer buffer, uint stride, uint offset) + { + Slot = slot; + Buffer = buffer; + Stride = stride; + Offset = offset; + } +} \ No newline at end of file