mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Move RHI to its own project
Move some of Robust.Shared to a new project so it can be depended upon without adding longer dependency chains.
This commit is contained in:
@@ -28,5 +28,5 @@
|
||||
<Import Project="Robust.Analyzers.targets" Condition="'$(SkipRobustAnalyzer)' != 'true'" />
|
||||
|
||||
<!-- serialization generator -->
|
||||
<Import Project="Robust.Serialization.Generator.targets" Condition="'$(SkipRobustAnalyzer)' != 'true'" />
|
||||
<Import Project="Robust.Serialization.Generator.targets" Condition="'$(SkipRobustAnalyzer)' != 'true' And '$(SkipRobustSerializationGenerator)' != 'true'" />
|
||||
</Project>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\PreferNonGenericVariantForAttribute.cs" LogicalName="Robust.Shared.Analyzers.PreferNonGenericVariantForAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\PreferOtherTypeAttribute.cs" LogicalName="Robust.Shared.Analyzers.PreferOtherTypeAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\ForbidLiteralAttribute.cs" LogicalName="Robust.Shared.Analyzers.ForbidLiteralAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Analyzers\ObsoleteInheritanceAttribute.cs" LogicalName="Robust.Shared.Analyzers.ObsoleteInheritanceAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared.Utility\Analyzers\ObsoleteInheritanceAttribute.cs" LogicalName="Robust.Shared.Analyzers.ObsoleteInheritanceAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\IoC\DependencyAttribute.cs" LogicalName="Robust.Shared.IoC.DependencyAttribute.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\GameObjects\EventBusAttributes.cs" LogicalName="Robust.Shared.GameObjects.EventBusAttributes.cs" LinkBase="Implementations" />
|
||||
<EmbeddedResource Include="..\Robust.Shared\Serialization\NetSerializableAttribute.cs" LogicalName="Robust.Shared.Serialization.NetSerializableAttribute.cs" LinkBase="Implementations" />
|
||||
|
||||
4
Robust.Client.Graphics.Rhi/AssemblyInfo.cs
Normal file
4
Robust.Client.Graphics.Rhi/AssemblyInfo.cs
Normal file
@@ -0,0 +1,4 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Robust.Client")]
|
||||
[module: SkipLocalsInit]
|
||||
21
Robust.Client.Graphics.Rhi/GlobalUsings.cs
Normal file
21
Robust.Client.Graphics.Rhi/GlobalUsings.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
global using Robust.Client.Interop.RobustNative.Webgpu;
|
||||
|
||||
global using static Robust.Client.Interop.RobustNative.Webgpu.Wgpu;
|
||||
|
||||
global using unsafe WGPUTexture = Robust.Client.Interop.RobustNative.Webgpu.WGPUTextureImpl*;
|
||||
global using unsafe WGPUDevice = Robust.Client.Interop.RobustNative.Webgpu.WGPUDeviceImpl*;
|
||||
global using unsafe WGPUQueue = Robust.Client.Interop.RobustNative.Webgpu.WGPUQueueImpl*;
|
||||
global using unsafe WGPUAdapter = Robust.Client.Interop.RobustNative.Webgpu.WGPUAdapterImpl*;
|
||||
global using unsafe WGPUInstance = Robust.Client.Interop.RobustNative.Webgpu.WGPUInstanceImpl*;
|
||||
global using unsafe WGPUTextureView = Robust.Client.Interop.RobustNative.Webgpu.WGPUTextureViewImpl*;
|
||||
global using unsafe WGPUBindGroup = Robust.Client.Interop.RobustNative.Webgpu.WGPUBindGroupImpl*;
|
||||
global using unsafe WGPUBindGroupLayout = Robust.Client.Interop.RobustNative.Webgpu.WGPUBindGroupLayoutImpl*;
|
||||
global using unsafe WGPUBuffer = Robust.Client.Interop.RobustNative.Webgpu.WGPUBufferImpl*;
|
||||
global using unsafe WGPUSampler = Robust.Client.Interop.RobustNative.Webgpu.WGPUSamplerImpl*;
|
||||
global using unsafe WGPUCommandBuffer = Robust.Client.Interop.RobustNative.Webgpu.WGPUCommandBufferImpl*;
|
||||
global using unsafe WGPUCommandEncoder = Robust.Client.Interop.RobustNative.Webgpu.WGPUCommandEncoderImpl*;
|
||||
global using unsafe WGPURenderPassEncoder = Robust.Client.Interop.RobustNative.Webgpu.WGPURenderPassEncoderImpl*;
|
||||
global using unsafe WGPURenderPipeline = Robust.Client.Interop.RobustNative.Webgpu.WGPURenderPipelineImpl*;
|
||||
global using unsafe WGPUPipelineLayout = Robust.Client.Interop.RobustNative.Webgpu.WGPUPipelineLayoutImpl*;
|
||||
global using unsafe WGPUShaderModule = Robust.Client.Interop.RobustNative.Webgpu.WGPUShaderModuleImpl*;
|
||||
global using unsafe WGPUSurface = Robust.Client.Interop.RobustNative.Webgpu.WGPUSurfaceImpl*;
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Client.Graphics.Rhi.WebGpu;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde.Rhi;
|
||||
namespace Robust.Client.Graphics.Rhi;
|
||||
|
||||
public abstract partial class RhiBase
|
||||
{
|
||||
@@ -9,7 +9,7 @@ public abstract partial class RhiBase
|
||||
// Clyde <-> RHI API.
|
||||
//
|
||||
|
||||
internal abstract void Init();
|
||||
internal abstract void Init(in RhiInitParams initParams, out RhiWebGpu.WindowData windowData);
|
||||
internal abstract void Shutdown();
|
||||
|
||||
/// <summary>
|
||||
@@ -18,20 +18,20 @@ public abstract partial class RhiBase
|
||||
/// <remarks>
|
||||
/// Does not get called for the main window.
|
||||
/// </remarks>
|
||||
internal abstract void WindowCreated(Clyde.WindowReg reg);
|
||||
internal abstract RhiWebGpu.WindowData WindowCreated(in RhiWindowSurfaceParams surfaceParams, Vector2i size);
|
||||
|
||||
/// <summary>
|
||||
/// A window is about to be destroyed by Clyde. Clean up resources for it.
|
||||
/// </summary>
|
||||
internal abstract void WindowDestroy(Clyde.WindowReg reg);
|
||||
internal abstract void WindowDestroy(RhiWebGpu.WindowData reg);
|
||||
|
||||
/// <summary>
|
||||
/// Recreate the native swap chain, in case it has become suboptimal (e.g. due to window resizing).
|
||||
/// </summary>
|
||||
internal abstract void WindowRecreateSwapchain(Clyde.WindowReg reg);
|
||||
internal abstract void WindowRecreateSwapchain(RhiWebGpu.WindowData reg, Vector2i size);
|
||||
|
||||
internal abstract RhiTextureView CreateTextureViewForWindow(Clyde.WindowReg reg);
|
||||
internal abstract void WindowPresent(Clyde.WindowReg reg);
|
||||
internal abstract RhiTexture CreateTextureViewForWindow(RhiWebGpu.WindowData reg);
|
||||
internal abstract void WindowPresent(RhiWebGpu.WindowData reg);
|
||||
|
||||
//
|
||||
// RHI-internal API to de-OOP the public RHI API.
|
||||
@@ -42,7 +42,8 @@ public abstract partial class RhiBase
|
||||
in RhiRenderPassDescriptor descriptor
|
||||
);
|
||||
|
||||
internal abstract RhiCommandBuffer CommandEncoderFinish(in RhiCommandEncoder encoder,
|
||||
internal abstract RhiCommandBuffer CommandEncoderFinish(
|
||||
in RhiCommandEncoder encoder,
|
||||
in RhiCommandBufferDescriptor descriptor);
|
||||
|
||||
internal abstract void RenderPassEncoderSetPipeline(
|
||||
@@ -101,6 +102,31 @@ public abstract partial class RhiBase
|
||||
internal abstract RhiMappedBufferRange BufferGetMappedRange(RhiBuffer buffer, nuint offset, nuint size);
|
||||
internal abstract void BufferUnmap(RhiBuffer buffer);
|
||||
internal abstract void BufferDrop(RhiBuffer buffer);
|
||||
|
||||
internal struct RhiInitParams
|
||||
{
|
||||
public required string Backends;
|
||||
public required RhiPowerPreference PowerPreference;
|
||||
public required Vector2i MainWindowSize;
|
||||
public required RhiWindowSurfaceParams MainWindowSurfaceParams;
|
||||
}
|
||||
|
||||
internal unsafe struct RhiWindowSurfaceParams
|
||||
{
|
||||
#if WINDOWS
|
||||
public void* HInstance;
|
||||
public void* HWnd;
|
||||
#elif OSX
|
||||
public void* MetalLayer;
|
||||
#elif LINUX
|
||||
public bool Wayland; // False = X11
|
||||
public void* X11Display;
|
||||
public void* X11Window;
|
||||
|
||||
public void* WaylandDisplay;
|
||||
public void* WaylandSurface;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
internal record struct RhiHandle(long Value);
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Numerics;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde.Rhi;
|
||||
namespace Robust.Client.Graphics.Rhi;
|
||||
|
||||
/// <summary>
|
||||
/// Equivalent to a WGSL <c>mat3x2f</c>.
|
||||
@@ -1,16 +1,12 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Buffers;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
// ReSharper disable IdentifierTypo
|
||||
// ReSharper disable UnusedMember.Global
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde.Rhi;
|
||||
namespace Robust.Client.Graphics.Rhi;
|
||||
|
||||
//
|
||||
// The Render Hardware Interface (RHI) is an abstraction for the underlying native graphics API.
|
||||
@@ -34,7 +30,7 @@ public abstract partial class RhiBase
|
||||
public abstract RhiQueue Queue { get; }
|
||||
|
||||
public abstract RhiLimits DeviceLimits { get; }
|
||||
public abstract RhiAdapterProperties AdapterProperties { get; }
|
||||
public abstract RhiAdapterInfo AdapterInfo { get; }
|
||||
public abstract string Description { get; }
|
||||
|
||||
public abstract RhiTexture CreateTexture(in RhiTextureDescriptor descriptor);
|
||||
@@ -57,7 +53,7 @@ public abstract partial class RhiBase
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Values for <see cref="CVars.DisplayGpuPowerPreference"/>.
|
||||
/// Values for <c>display.gpu_power_preference</c>.
|
||||
/// </summary>
|
||||
public enum RhiPowerPreference : byte
|
||||
{
|
||||
@@ -97,7 +93,6 @@ public sealed record RhiLimits(
|
||||
ulong MaxBufferSize,
|
||||
uint MaxVertexAttributes,
|
||||
uint MaxVertexBufferArrayStride,
|
||||
uint MaxInterStageShaderComponents,
|
||||
uint MaxInterStageShaderVariables,
|
||||
uint MaxColorAttachments,
|
||||
uint MaxColorAttachmentBytesPerSample,
|
||||
@@ -110,12 +105,13 @@ public sealed record RhiLimits(
|
||||
);
|
||||
|
||||
// TODO: Should have internal constructor to avoid breaking changes if we add fields.
|
||||
public sealed record RhiAdapterProperties(
|
||||
public sealed record RhiAdapterInfo(
|
||||
uint VendorID,
|
||||
string VendorName,
|
||||
uint DeviceID,
|
||||
string Vendor,
|
||||
string Architecture,
|
||||
string Name,
|
||||
string Driver,
|
||||
string Device,
|
||||
string Description,
|
||||
RhiAdapterType AdapterType,
|
||||
RhiBackendType BackendType
|
||||
);
|
||||
@@ -140,8 +136,6 @@ public enum RhiBackendType : byte
|
||||
OpenGles = 7,
|
||||
}
|
||||
|
||||
public sealed record RhiAdapterInfo();
|
||||
|
||||
public sealed class RhiBuffer : RhiObject
|
||||
{
|
||||
internal ActiveMapping? Mapping;
|
||||
@@ -150,26 +144,26 @@ public sealed class RhiBuffer : RhiObject
|
||||
|
||||
public async ValueTask MapAsync(RhiMapModeFlags mode, nuint offset, nuint size)
|
||||
{
|
||||
await Rhi.BufferMapAsync(this, mode, offset, size);
|
||||
await Impl.BufferMapAsync(this, mode, offset, size);
|
||||
}
|
||||
|
||||
public RhiMappedBufferRange GetMappedRange(nuint offset, nuint size)
|
||||
{
|
||||
return Rhi.BufferGetMappedRange(this, offset, size);
|
||||
return Impl.BufferGetMappedRange(this, offset, size);
|
||||
}
|
||||
|
||||
public void Unmap()
|
||||
{
|
||||
Rhi.BufferUnmap(this);
|
||||
Impl.BufferUnmap(this);
|
||||
}
|
||||
|
||||
internal RhiBuffer(RhiBase rhi, RhiHandle handle) : base(rhi, handle)
|
||||
internal RhiBuffer(RhiBase impl, RhiHandle handle) : base(impl, handle)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void ReleaseUnmanagedResources()
|
||||
{
|
||||
Rhi.BufferDrop(this);
|
||||
Impl.BufferDrop(this);
|
||||
}
|
||||
|
||||
internal sealed class ActiveMapping
|
||||
@@ -301,13 +295,13 @@ public enum RhiBufferUsageFlags : ushort
|
||||
|
||||
public sealed class RhiBindGroup : RhiObject
|
||||
{
|
||||
internal RhiBindGroup(RhiBase rhi, RhiHandle handle) : base(rhi, handle)
|
||||
internal RhiBindGroup(RhiBase impl, RhiHandle handle) : base(impl, handle)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void ReleaseUnmanagedResources()
|
||||
{
|
||||
Rhi.BindGroupDrop(this);
|
||||
Impl.BindGroupDrop(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,7 +381,7 @@ public enum RhiSamplerBindingType : byte
|
||||
|
||||
public sealed class RhiBindGroupLayout : RhiObject
|
||||
{
|
||||
internal RhiBindGroupLayout(RhiBase rhi, RhiHandle handle) : base(rhi, handle)
|
||||
internal RhiBindGroupLayout(RhiBase impl, RhiHandle handle) : base(impl, handle)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -403,7 +397,7 @@ public record struct RhiRenderPassDescriptor(
|
||||
|
||||
public sealed class RhiQuerySet : RhiObject
|
||||
{
|
||||
internal RhiQuerySet(RhiBase rhi, RhiHandle handle) : base(rhi, handle)
|
||||
internal RhiQuerySet(RhiBase impl, RhiHandle handle) : base(impl, handle)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -453,35 +447,35 @@ public sealed class RhiRenderPassEncoder : RhiObject
|
||||
{
|
||||
public void SetPipeline(RhiRenderPipeline pipeline)
|
||||
{
|
||||
Rhi.RenderPassEncoderSetPipeline(this, pipeline);
|
||||
Impl.RenderPassEncoderSetPipeline(this, pipeline);
|
||||
}
|
||||
|
||||
public void Draw(uint vertexCount, uint instanceCount, uint firstVertex, uint firstInstance)
|
||||
{
|
||||
Rhi.RenderPassEncoderDraw(this, vertexCount, instanceCount, firstVertex, firstInstance);
|
||||
Impl.RenderPassEncoderDraw(this, vertexCount, instanceCount, firstVertex, firstInstance);
|
||||
}
|
||||
|
||||
public void SetBindGroup(uint index, RhiBindGroup? bindGroup)
|
||||
{
|
||||
Rhi.RenderPassEncoderSetBindGroup(this, index, bindGroup);
|
||||
Impl.RenderPassEncoderSetBindGroup(this, index, bindGroup);
|
||||
}
|
||||
|
||||
public void SetVertexBuffer(uint slot, RhiBuffer? buffer, ulong offset = 0, ulong? size = null)
|
||||
{
|
||||
Rhi.RenderPassEncoderSetVertexBuffer(this, slot, buffer, offset, size);
|
||||
Impl.RenderPassEncoderSetVertexBuffer(this, slot, buffer, offset, size);
|
||||
}
|
||||
|
||||
public void SetScissorRect(uint x, uint y, uint w, uint h)
|
||||
{
|
||||
Rhi.RenderPassEncoderSetScissorRect(this, x, y, w, h);
|
||||
Impl.RenderPassEncoderSetScissorRect(this, x, y, w, h);
|
||||
}
|
||||
|
||||
public void End()
|
||||
{
|
||||
Rhi.RenderPassEncoderEnd(this);
|
||||
Impl.RenderPassEncoderEnd(this);
|
||||
}
|
||||
|
||||
internal RhiRenderPassEncoder(RhiBase rhi, RhiHandle handle) : base(rhi, handle)
|
||||
internal RhiRenderPassEncoder(RhiBase impl, RhiHandle handle) : base(impl, handle)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -492,13 +486,13 @@ public record struct RhiCommandBufferDescriptor(
|
||||
|
||||
public sealed class RhiCommandBuffer : RhiObject
|
||||
{
|
||||
internal RhiCommandBuffer(RhiBase rhi, RhiHandle handle) : base(rhi, handle)
|
||||
internal RhiCommandBuffer(RhiBase impl, RhiHandle handle) : base(impl, handle)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void ReleaseUnmanagedResources()
|
||||
{
|
||||
Rhi.CommandBufferDrop(this);
|
||||
Impl.CommandBufferDrop(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -512,12 +506,12 @@ public sealed class RhiCommandEncoder : RhiObject
|
||||
{
|
||||
public RhiRenderPassEncoder BeginRenderPass(in RhiRenderPassDescriptor descriptor)
|
||||
{
|
||||
return Rhi.CommandEncoderBeginRenderPass(this, descriptor);
|
||||
return Impl.CommandEncoderBeginRenderPass(this, descriptor);
|
||||
}
|
||||
|
||||
public RhiCommandBuffer Finish(in RhiCommandBufferDescriptor descriptor)
|
||||
{
|
||||
return Rhi.CommandEncoderFinish(this, descriptor);
|
||||
return Impl.CommandEncoderFinish(this, descriptor);
|
||||
}
|
||||
|
||||
public RhiCommandBuffer Finish()
|
||||
@@ -525,7 +519,7 @@ public sealed class RhiCommandEncoder : RhiObject
|
||||
return Finish(new RhiCommandBufferDescriptor());
|
||||
}
|
||||
|
||||
internal RhiCommandEncoder(RhiBase rhi, RhiHandle handle) : base(rhi, handle)
|
||||
internal RhiCommandEncoder(RhiBase impl, RhiHandle handle) : base(impl, handle)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -536,7 +530,7 @@ public interface IRhiPipelineLayoutBase
|
||||
|
||||
public sealed class RhiPipelineLayout : RhiObject, IRhiPipelineLayoutBase
|
||||
{
|
||||
internal RhiPipelineLayout(RhiBase rhi, RhiHandle handle) : base(rhi, handle)
|
||||
internal RhiPipelineLayout(RhiBase impl, RhiHandle handle) : base(impl, handle)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -677,7 +671,7 @@ public enum RhiIndexFormat : byte
|
||||
|
||||
public record struct RhiDepthStencilState(
|
||||
RhiTextureFormat Format,
|
||||
bool DepthWriteEnabled,
|
||||
bool? DepthWriteEnabled,
|
||||
RhiCompareFunction DepthCompare,
|
||||
// TODO: Fix struct defs to make these not nullable please god
|
||||
RhiStencilFaceState? StencilFront,
|
||||
@@ -889,12 +883,12 @@ public enum RhiCompareFunction : byte
|
||||
|
||||
public abstract class RhiObject : IDisposable
|
||||
{
|
||||
internal readonly RhiBase Rhi;
|
||||
internal readonly RhiBase Impl;
|
||||
internal readonly RhiHandle Handle;
|
||||
|
||||
private protected RhiObject(RhiBase rhi, RhiHandle handle)
|
||||
private protected RhiObject(RhiBase impl, RhiHandle handle)
|
||||
{
|
||||
Rhi = rhi;
|
||||
Impl = impl;
|
||||
Handle = handle;
|
||||
}
|
||||
|
||||
@@ -917,13 +911,13 @@ public abstract class RhiObject : IDisposable
|
||||
// TODO: Shouldn't be a RhiObject since it can't be disposed directly?
|
||||
public sealed class RhiQueue : RhiObject
|
||||
{
|
||||
internal RhiQueue(RhiBase rhi, RhiHandle handle) : base(rhi, handle)
|
||||
internal RhiQueue(RhiBase impl, RhiHandle handle) : base(impl, handle)
|
||||
{
|
||||
}
|
||||
|
||||
public void Submit(params RhiCommandBuffer[] commandBuffers)
|
||||
{
|
||||
Rhi.QueueSubmit(this, commandBuffers);
|
||||
Impl.QueueSubmit(this, commandBuffers);
|
||||
}
|
||||
|
||||
public void WriteTexture(
|
||||
@@ -932,7 +926,7 @@ public sealed class RhiQueue : RhiObject
|
||||
in RhiImageDataLayout dataLayout,
|
||||
RhiExtent3D size)
|
||||
{
|
||||
Rhi.QueueWriteTexture(this, destination, data, dataLayout, size);
|
||||
Impl.QueueWriteTexture(this, destination, data, dataLayout, size);
|
||||
}
|
||||
|
||||
public void WriteTexture<T>(
|
||||
@@ -955,7 +949,7 @@ public sealed class RhiQueue : RhiObject
|
||||
ulong bufferOffset,
|
||||
ReadOnlySpan<byte> data)
|
||||
{
|
||||
Rhi.QueueWriteBuffer(buffer, bufferOffset, data);
|
||||
Impl.QueueWriteBuffer(buffer, bufferOffset, data);
|
||||
}
|
||||
|
||||
public void WriteBuffer<T>(
|
||||
@@ -974,45 +968,45 @@ public sealed class RhiQueue : RhiObject
|
||||
|
||||
public sealed class RhiTexture : RhiObject
|
||||
{
|
||||
internal RhiTexture(RhiBase rhi, RhiHandle handle) : base(rhi, handle)
|
||||
internal RhiTexture(RhiBase impl, RhiHandle handle) : base(impl, handle)
|
||||
{
|
||||
}
|
||||
|
||||
public RhiTextureView CreateView(in RhiTextureViewDescriptor descriptor)
|
||||
{
|
||||
return Rhi.TextureCreateView(this, in descriptor);
|
||||
return Impl.TextureCreateView(this, in descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RhiTextureView : RhiObject, IRhiBindingResource
|
||||
{
|
||||
internal RhiTextureView(RhiBase rhi, RhiHandle handle) : base(rhi, handle)
|
||||
internal RhiTextureView(RhiBase impl, RhiHandle handle) : base(impl, handle)
|
||||
{
|
||||
}
|
||||
|
||||
internal override void ReleaseUnmanagedResources()
|
||||
{
|
||||
Rhi.TextureViewDrop(this);
|
||||
Impl.TextureViewDrop(this);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RhiSampler : RhiObject, IRhiBindingResource
|
||||
{
|
||||
internal RhiSampler(RhiBase rhi, RhiHandle handle) : base(rhi, handle)
|
||||
internal RhiSampler(RhiBase impl, RhiHandle handle) : base(impl, handle)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RhiShaderModule : RhiObject
|
||||
{
|
||||
internal RhiShaderModule(RhiBase rhi, RhiHandle handle) : base(rhi, handle)
|
||||
internal RhiShaderModule(RhiBase impl, RhiHandle handle) : base(impl, handle)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RhiRenderPipeline : RhiObject
|
||||
{
|
||||
internal RhiRenderPipeline(RhiBase rhi, RhiHandle handle) : base(rhi, handle)
|
||||
internal RhiRenderPipeline(RhiBase impl, RhiHandle handle) : base(impl, handle)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1169,7 +1163,6 @@ public enum RhiTextureAspect : byte
|
||||
Final
|
||||
}
|
||||
|
||||
[Virtual]
|
||||
public class RhiException : Exception
|
||||
{
|
||||
public RhiException()
|
||||
18
Robust.Client.Graphics.Rhi/Robust.Client.Graphics.Rhi.csproj
Normal file
18
Robust.Client.Graphics.Rhi/Robust.Client.Graphics.Rhi.csproj
Normal file
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\MSBuild\Robust.Engine.props"/>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<SkipRobustSerializationGenerator>true</SkipRobustSerializationGenerator>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Robust.Client.Interop.RobustNative\Robust.Client.Interop.RobustNative.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared.Utility\Robust.Shared.Utility.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets"/>
|
||||
</Project>
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Silk.NET.WebGPU;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde.Rhi;
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
@@ -12,7 +8,7 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
|
||||
internal override void BindGroupDrop(RhiBindGroup rhiBindGroup)
|
||||
{
|
||||
_wgpu.BindGroupDrop(_bindGroupRegistry[rhiBindGroup.Handle].Native);
|
||||
wgpuBindGroupRelease(_bindGroupRegistry[rhiBindGroup.Handle].Native);
|
||||
_bindGroupRegistry.Remove(rhiBindGroup.Handle);
|
||||
}
|
||||
|
||||
@@ -20,41 +16,41 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[512];
|
||||
|
||||
var pDescriptor = BumpAllocate<BindGroupLayoutDescriptor>(ref buffer);
|
||||
pDescriptor->Label = BumpAllocateUtf8(ref buffer, descriptor.Label);
|
||||
var pDescriptor = BumpAllocate<WGPUBindGroupLayoutDescriptor>(ref buffer);
|
||||
pDescriptor->label = BumpAllocateStringView(ref buffer, descriptor.Label);
|
||||
var entries = descriptor.Entries;
|
||||
pDescriptor->EntryCount = (uint)entries.Length;
|
||||
pDescriptor->Entries = BumpAllocate<BindGroupLayoutEntry>(ref buffer, entries.Length);
|
||||
pDescriptor->entryCount = (uint)entries.Length;
|
||||
pDescriptor->entries = BumpAllocate<WGPUBindGroupLayoutEntry>(ref buffer, entries.Length);
|
||||
for (var i = 0; i < entries.Length; i++)
|
||||
{
|
||||
ref var entry = ref entries[i];
|
||||
var pEntry = &pDescriptor->Entries[i];
|
||||
var pEntry = &pDescriptor->entries[i];
|
||||
|
||||
pEntry->Binding = entry.Binding;
|
||||
pEntry->Visibility = (ShaderStage)entry.Visibility;
|
||||
pEntry->binding = entry.Binding;
|
||||
pEntry->visibility = (ulong)entry.Visibility;
|
||||
|
||||
switch (entry.Layout)
|
||||
{
|
||||
case RhiSamplerBindingLayout sampler:
|
||||
pEntry->Sampler.Type = (SamplerBindingType)sampler.Type;
|
||||
pEntry->sampler.type = (WGPUSamplerBindingType)sampler.Type;
|
||||
break;
|
||||
case RhiTextureBindingLayout texture:
|
||||
pEntry->Texture.Multisampled = texture.Multisampled;
|
||||
pEntry->Texture.SampleType = (TextureSampleType)texture.SampleType;
|
||||
pEntry->Texture.ViewDimension =
|
||||
(TextureViewDimension)ValidateTextureViewDimension(texture.ViewDimension);
|
||||
pEntry->texture.multisampled = texture.Multisampled ? 1u : 0u;
|
||||
pEntry->texture.sampleType = (WGPUTextureSampleType)texture.SampleType;
|
||||
pEntry->texture.viewDimension =
|
||||
(WGPUTextureViewDimension)ValidateTextureViewDimension(texture.ViewDimension);
|
||||
break;
|
||||
case RhiBufferBindingLayout layoutBuffer:
|
||||
pEntry->Buffer.Type = (BufferBindingType) layoutBuffer.Type;
|
||||
pEntry->Buffer.HasDynamicOffset = layoutBuffer.HasDynamicOffset;
|
||||
pEntry->Buffer.MinBindingSize = layoutBuffer.MinBindingSize;
|
||||
pEntry->buffer.type = (WGPUBufferBindingType) layoutBuffer.Type;
|
||||
pEntry->buffer.hasDynamicOffset = layoutBuffer.HasDynamicOffset ? 1u : 0u;
|
||||
pEntry->buffer.minBindingSize = layoutBuffer.MinBindingSize;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
var native = _webGpu.DeviceCreateBindGroupLayout(_wgpuDevice, pDescriptor);
|
||||
var native = wgpuDeviceCreateBindGroupLayout(_wgpuDevice, pDescriptor);
|
||||
|
||||
// TODO: Thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
@@ -67,38 +63,38 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
// TODO: SAFETY
|
||||
Span<byte> buffer = stackalloc byte[1024];
|
||||
|
||||
var pDescriptor = BumpAllocate<BindGroupDescriptor>(ref buffer);
|
||||
pDescriptor->Label = BumpAllocateUtf8(ref buffer, descriptor.Label);
|
||||
pDescriptor->Layout = _bindGroupLayoutRegistry[descriptor.Layout.Handle].Native;
|
||||
var pDescriptor = BumpAllocate<WGPUBindGroupDescriptor>(ref buffer);
|
||||
pDescriptor->label = BumpAllocateStringView(ref buffer, descriptor.Label);
|
||||
pDescriptor->layout = _bindGroupLayoutRegistry[descriptor.Layout.Handle].Native;
|
||||
|
||||
var entries = descriptor.Entries;
|
||||
pDescriptor->EntryCount = (uint) entries.Length;
|
||||
pDescriptor->Entries = BumpAllocate<BindGroupEntry>(ref buffer, entries.Length);
|
||||
pDescriptor->entryCount = (uint) entries.Length;
|
||||
pDescriptor->entries = BumpAllocate<WGPUBindGroupEntry>(ref buffer, entries.Length);
|
||||
for (var i = 0; i < entries.Length; i++)
|
||||
{
|
||||
ref var entry = ref descriptor.Entries[i];
|
||||
var pEntry = &pDescriptor->Entries[i];
|
||||
var pEntry = &pDescriptor->entries[i];
|
||||
|
||||
pEntry->Binding = entry.Binding;
|
||||
pEntry->binding = entry.Binding;
|
||||
switch (entry.Resource)
|
||||
{
|
||||
case RhiSampler rhiSampler:
|
||||
pEntry->Sampler = _samplerRegistry[rhiSampler.Handle].Native;
|
||||
pEntry->sampler = _samplerRegistry[rhiSampler.Handle].Native;
|
||||
break;
|
||||
case RhiTextureView rhiTextureView:
|
||||
pEntry->TextureView = _textureViewRegistry[rhiTextureView.Handle].Native;
|
||||
pEntry->textureView = _textureViewRegistry[rhiTextureView.Handle].Native;
|
||||
break;
|
||||
case RhiBufferBinding bufferBinding:
|
||||
pEntry->Buffer = _bufferRegistry[bufferBinding.Buffer.Handle].Native;
|
||||
pEntry->Offset = bufferBinding.Offset;
|
||||
pEntry->Size = bufferBinding.Size ?? WebGPU.WholeSize;
|
||||
pEntry->buffer = _bufferRegistry[bufferBinding.Buffer.Handle].Native;
|
||||
pEntry->offset = bufferBinding.Offset;
|
||||
pEntry->size = bufferBinding.Size ?? WGPU_WHOLE_SIZE;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
var bindGroup = _webGpu.DeviceCreateBindGroup(_wgpuDevice, pDescriptor);
|
||||
var bindGroup = wgpuDeviceCreateBindGroup(_wgpuDevice, pDescriptor);
|
||||
|
||||
var handle = AllocRhiHandle();
|
||||
_bindGroupRegistry.Add(handle, new BindGroupReg { Native = bindGroup });
|
||||
@@ -107,11 +103,11 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
|
||||
private sealed class BindGroupLayoutReg
|
||||
{
|
||||
public BindGroupLayout* Native;
|
||||
public WGPUBindGroupLayout Native;
|
||||
}
|
||||
|
||||
private sealed class BindGroupReg
|
||||
{
|
||||
public BindGroup* Native;
|
||||
public WGPUBindGroup Native;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
using Internal.TypeSystem;
|
||||
using Silk.NET.WebGPU;
|
||||
using Buffer = Silk.NET.WebGPU.Buffer;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde.Rhi;
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed partial class RhiWebGpu
|
||||
{
|
||||
@@ -16,19 +10,19 @@ internal sealed partial class RhiWebGpu
|
||||
public override unsafe RhiBuffer CreateBuffer(in RhiBufferDescriptor descriptor)
|
||||
{
|
||||
Span<byte> buffer = stackalloc byte[512];
|
||||
var pDescriptor = BumpAllocate<BufferDescriptor>(ref buffer);
|
||||
pDescriptor->Label = BumpAllocateUtf8(ref buffer, descriptor.Label);
|
||||
pDescriptor->MappedAtCreation = descriptor.MappedAtCreation;
|
||||
pDescriptor->Size = descriptor.Size;
|
||||
pDescriptor->Usage = (BufferUsage) descriptor.Usage;
|
||||
var pDescriptor = BumpAllocate<WGPUBufferDescriptor>(ref buffer);
|
||||
pDescriptor->label = BumpAllocateStringView(ref buffer, descriptor.Label);
|
||||
pDescriptor->mappedAtCreation = descriptor.MappedAtCreation ? 1u : 0u;
|
||||
pDescriptor->size = descriptor.Size;
|
||||
pDescriptor->usage = (ulong) descriptor.Usage;
|
||||
|
||||
var native = _webGpu.DeviceCreateBuffer(_wgpuDevice, pDescriptor);
|
||||
var native = wgpuDeviceCreateBuffer(_wgpuDevice, pDescriptor);
|
||||
|
||||
var handle = AllocRhiHandle();
|
||||
_bufferRegistry.Add(handle, new BufferReg { Native = native });
|
||||
var rhiBuffer= new RhiBuffer(this, handle);
|
||||
|
||||
if (pDescriptor->MappedAtCreation)
|
||||
if (pDescriptor->mappedAtCreation == 1)
|
||||
{
|
||||
rhiBuffer.Mapping = new RhiBuffer.ActiveMapping(rhiBuffer) { Valid = true };
|
||||
}
|
||||
@@ -39,7 +33,7 @@ internal sealed partial class RhiWebGpu
|
||||
internal override unsafe RhiBufferMapState BufferGetMapState(RhiBuffer buffer)
|
||||
{
|
||||
var nativeBuffer = _bufferRegistry[buffer.Handle].Native;
|
||||
return (RhiBufferMapState) _webGpu.BufferGetMapState(nativeBuffer);
|
||||
return (RhiBufferMapState) wgpuBufferGetMapState(nativeBuffer);
|
||||
}
|
||||
|
||||
internal override async ValueTask BufferMapAsync(RhiBuffer buffer, RhiMapModeFlags mode, nuint offset, nuint size)
|
||||
@@ -56,13 +50,16 @@ internal sealed partial class RhiWebGpu
|
||||
{
|
||||
var nativeBuffer = _bufferRegistry[buffer.Handle].Native;
|
||||
|
||||
_webGpu.BufferMapAsync(
|
||||
wgpuBufferMapAsync(
|
||||
nativeBuffer,
|
||||
(MapMode) mode,
|
||||
(ulong) mode,
|
||||
offset,
|
||||
size,
|
||||
new PfnBufferMapCallback(&WgpuMapBufferAsyncCallback),
|
||||
promise.UserData
|
||||
new WGPUBufferMapCallbackInfo
|
||||
{
|
||||
callback = &WgpuMapBufferAsyncCallback,
|
||||
userdata1 = promise.UserData,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -72,7 +69,7 @@ internal sealed partial class RhiWebGpu
|
||||
buffer.Mapping = new RhiBuffer.ActiveMapping(buffer) { Valid = true };
|
||||
}
|
||||
|
||||
if (result.Status != BufferMapAsyncStatus.Success)
|
||||
if (result.Status != WGPUMapAsyncStatus.WGPUMapAsyncStatus_Success)
|
||||
throw new RhiException(result.Status.ToString());
|
||||
|
||||
buffer.MapState = RhiBufferMapState.Mapped;
|
||||
@@ -95,7 +92,7 @@ internal sealed partial class RhiWebGpu
|
||||
}
|
||||
|
||||
var nativeBuffer = _bufferRegistry[buffer.Handle].Native;
|
||||
var mapped = _webGpu.BufferGetMappedRange(nativeBuffer, offset, size);
|
||||
var mapped = wgpuBufferGetMappedRange(nativeBuffer, offset, size);
|
||||
|
||||
return new RhiMappedBufferRange(buffer.Mapping, mapped, (int) size);
|
||||
}
|
||||
@@ -118,7 +115,7 @@ internal sealed partial class RhiWebGpu
|
||||
throw new InvalidOperationException("Current thread has buffer accessible as span, cannot unmap!");
|
||||
|
||||
var nativeBuffer = _bufferRegistry[buffer.Handle].Native;
|
||||
_webGpu.BufferUnmap(nativeBuffer);
|
||||
wgpuBufferUnmap(nativeBuffer);
|
||||
|
||||
buffer.Mapping.Valid = false;
|
||||
buffer.Mapping = null;
|
||||
@@ -128,20 +125,24 @@ internal sealed partial class RhiWebGpu
|
||||
|
||||
internal override unsafe void BufferDrop(RhiBuffer buffer)
|
||||
{
|
||||
_wgpu.BufferDrop(_bufferRegistry[buffer.Handle].Native);
|
||||
wgpuBufferRelease(_bufferRegistry[buffer.Handle].Native);
|
||||
_bufferRegistry.Remove(buffer.Handle);
|
||||
}
|
||||
|
||||
private sealed unsafe class BufferReg
|
||||
{
|
||||
public Buffer* Native;
|
||||
public WGPUBuffer Native;
|
||||
}
|
||||
|
||||
private record struct WgpuMapBufferAsyncResult(BufferMapAsyncStatus Status);
|
||||
private record struct WgpuMapBufferAsyncResult(WGPUMapAsyncStatus Status);
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static unsafe void WgpuMapBufferAsyncCallback(BufferMapAsyncStatus status, void* userdata)
|
||||
private static unsafe void WgpuMapBufferAsyncCallback(
|
||||
WGPUMapAsyncStatus status,
|
||||
WGPUStringView stringView,
|
||||
void* userdata1,
|
||||
void* userdata2)
|
||||
{
|
||||
WgpuPromise<WgpuMapBufferAsyncResult>.SetResult(userdata, new WgpuMapBufferAsyncResult(status));
|
||||
WgpuPromise<WgpuMapBufferAsyncResult>.SetResult(userdata1, new WgpuMapBufferAsyncResult(status));
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde.Rhi;
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
@@ -18,7 +15,7 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
|
||||
internal override void CommandBufferDrop(RhiCommandBuffer commandBuffer)
|
||||
{
|
||||
_wgpu.CommandBufferDrop(_commandBufferRegistry[commandBuffer.Handle].Native);
|
||||
wgpuCommandBufferRelease(_commandBufferRegistry[commandBuffer.Handle].Native);
|
||||
CommandBufferDropped(commandBuffer);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Silk.NET.WebGPU;
|
||||
using Buffer = Silk.NET.WebGPU.Buffer;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde.Rhi;
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
@@ -12,15 +7,15 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
|
||||
public override RhiCommandEncoder CreateCommandEncoder(in RhiCommandEncoderDescriptor descriptor)
|
||||
{
|
||||
CommandEncoder* nativeEncoder;
|
||||
WGPUCommandEncoder nativeEncoder;
|
||||
fixed (byte* pLabel = MakeLabel(descriptor.Label))
|
||||
{
|
||||
var nativeDescriptor = new CommandEncoderDescriptor
|
||||
var nativeDescriptor = new WGPUCommandEncoderDescriptor
|
||||
{
|
||||
Label = pLabel
|
||||
label = new WGPUStringView {data = (sbyte*)pLabel, length = WGPU_STRLEN}
|
||||
};
|
||||
|
||||
nativeEncoder = _webGpu.DeviceCreateCommandEncoder(_wgpuDevice, &nativeDescriptor);
|
||||
nativeEncoder = wgpuDeviceCreateCommandEncoder(_wgpuDevice, &nativeDescriptor);
|
||||
}
|
||||
|
||||
// TODO: thread safety
|
||||
@@ -38,49 +33,49 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
|
||||
Span<byte> buffer = stackalloc byte[512];
|
||||
|
||||
var pDescriptor = BumpAllocate<RenderPassDescriptor>(ref buffer);
|
||||
pDescriptor->Label = BumpAllocateUtf8(ref buffer, descriptor.Label);
|
||||
var pDescriptor = BumpAllocate<WGPURenderPassDescriptor>(ref buffer);
|
||||
pDescriptor->label = BumpAllocateStringView(ref buffer, descriptor.Label);
|
||||
|
||||
var colorAttachments = descriptor.ColorAttachments;
|
||||
pDescriptor->ColorAttachmentCount = (uint)colorAttachments.Length;
|
||||
pDescriptor->ColorAttachments = BumpAllocate<RenderPassColorAttachment>(ref buffer, colorAttachments.Length);
|
||||
pDescriptor->colorAttachmentCount = (uint)colorAttachments.Length;
|
||||
pDescriptor->colorAttachments = BumpAllocate<WGPURenderPassColorAttachment>(ref buffer, colorAttachments.Length);
|
||||
for (var i = 0; i < colorAttachments.Length; i++)
|
||||
{
|
||||
ref var attachment = ref colorAttachments[i];
|
||||
var pAttachment = &pDescriptor->ColorAttachments[i];
|
||||
pAttachment->View = _textureViewRegistry[attachment.View.Handle].Native;
|
||||
var pAttachment = &pDescriptor->colorAttachments[i];
|
||||
pAttachment->view = _textureViewRegistry[attachment.View.Handle].Native;
|
||||
if (attachment.ResolveTarget is { } resolveTarget)
|
||||
pAttachment->ResolveTarget = _textureViewRegistry[resolveTarget.Handle].Native;
|
||||
pAttachment->ClearValue = WgpuColor(attachment.ClearValue);
|
||||
pAttachment->LoadOp = (LoadOp)attachment.LoadOp;
|
||||
pAttachment->StoreOp = (StoreOp)attachment.StoreOp;
|
||||
pAttachment->resolveTarget = _textureViewRegistry[resolveTarget.Handle].Native;
|
||||
pAttachment->clearValue = WgpuColor(attachment.ClearValue);
|
||||
pAttachment->loadOp = (WGPULoadOp)attachment.LoadOp;
|
||||
pAttachment->storeOp = (WGPUStoreOp)attachment.StoreOp;
|
||||
}
|
||||
|
||||
if (descriptor.DepthStencilAttachment is { } depthStencilAttachment)
|
||||
{
|
||||
var pDepthStencilAttachment = BumpAllocate<RenderPassDepthStencilAttachment>(ref buffer);
|
||||
pDescriptor->DepthStencilAttachment = pDepthStencilAttachment;
|
||||
var pDepthStencilAttachment = BumpAllocate<WGPURenderPassDepthStencilAttachment>(ref buffer);
|
||||
pDescriptor->depthStencilAttachment = pDepthStencilAttachment;
|
||||
|
||||
pDepthStencilAttachment->View = _textureViewRegistry[depthStencilAttachment.View.Handle].Native;
|
||||
pDepthStencilAttachment->DepthLoadOp = (LoadOp)depthStencilAttachment.DepthLoadOp;
|
||||
pDepthStencilAttachment->DepthStoreOp = (StoreOp)depthStencilAttachment.DepthStoreOp;
|
||||
pDepthStencilAttachment->DepthClearValue = depthStencilAttachment.DepthClearValue;
|
||||
pDepthStencilAttachment->DepthReadOnly = depthStencilAttachment.DepthReadOnly;
|
||||
pDepthStencilAttachment->StencilLoadOp = (LoadOp)depthStencilAttachment.StencilLoadOp;
|
||||
pDepthStencilAttachment->StencilStoreOp = (StoreOp)depthStencilAttachment.StencilStoreOp;
|
||||
pDepthStencilAttachment->StencilClearValue = depthStencilAttachment.StencilClearValue;
|
||||
pDepthStencilAttachment->StencilReadOnly = depthStencilAttachment.StencilReadOnly;
|
||||
pDepthStencilAttachment->view = _textureViewRegistry[depthStencilAttachment.View.Handle].Native;
|
||||
pDepthStencilAttachment->depthLoadOp = (WGPULoadOp)depthStencilAttachment.DepthLoadOp;
|
||||
pDepthStencilAttachment->depthStoreOp = (WGPUStoreOp)depthStencilAttachment.DepthStoreOp;
|
||||
pDepthStencilAttachment->depthClearValue = depthStencilAttachment.DepthClearValue;
|
||||
pDepthStencilAttachment->depthReadOnly = depthStencilAttachment.DepthReadOnly ? 1u : 0u;
|
||||
pDepthStencilAttachment->stencilLoadOp = (WGPULoadOp)depthStencilAttachment.StencilLoadOp;
|
||||
pDepthStencilAttachment->stencilStoreOp = (WGPUStoreOp)depthStencilAttachment.StencilStoreOp;
|
||||
pDepthStencilAttachment->stencilClearValue = depthStencilAttachment.StencilClearValue;
|
||||
pDepthStencilAttachment->stencilReadOnly = depthStencilAttachment.StencilReadOnly ? 1u : 0u;
|
||||
}
|
||||
|
||||
if (descriptor.OcclusionQuerySet != null)
|
||||
throw new NotImplementedException();
|
||||
|
||||
var pDescriptorMaxDrawCount = BumpAllocate<RenderPassDescriptorMaxDrawCount>(ref buffer);
|
||||
pDescriptor->NextInChain = (ChainedStruct*)pDescriptorMaxDrawCount;
|
||||
pDescriptorMaxDrawCount->Chain.SType = SType.RenderPassDescriptorMaxDrawCount;
|
||||
pDescriptorMaxDrawCount->MaxDrawCount = descriptor.MaxDrawCount;
|
||||
var pDescriptorMaxDrawCount = BumpAllocate<WGPURenderPassMaxDrawCount>(ref buffer);
|
||||
pDescriptor->nextInChain = (WGPUChainedStruct*)pDescriptorMaxDrawCount;
|
||||
pDescriptorMaxDrawCount->chain.sType = WGPUSType.WGPUSType_RenderPassMaxDrawCount;
|
||||
pDescriptorMaxDrawCount->maxDrawCount = descriptor.MaxDrawCount;
|
||||
|
||||
var nativeEncoder = _webGpu.CommandEncoderBeginRenderPass(
|
||||
var nativeEncoder = wgpuCommandEncoderBeginRenderPass(
|
||||
_commandEncoderRegistry[encoder.Handle].Native,
|
||||
pDescriptor
|
||||
);
|
||||
@@ -96,7 +91,7 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
RhiRenderPipeline pipeline)
|
||||
{
|
||||
// TODO: safety
|
||||
_webGpu.RenderPassEncoderSetPipeline(
|
||||
wgpuRenderPassEncoderSetPipeline(
|
||||
_renderPassEncoderRegistry[encoder.Handle].Native,
|
||||
_renderPipelineRegistry[pipeline.Handle].Native
|
||||
);
|
||||
@@ -110,7 +105,7 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
uint firstInstance)
|
||||
{
|
||||
// TODO: safety
|
||||
_webGpu.RenderPassEncoderDraw(
|
||||
wgpuRenderPassEncoderDraw(
|
||||
_renderPassEncoderRegistry[encoder.Handle].Native,
|
||||
vertexCount,
|
||||
instanceCount,
|
||||
@@ -124,7 +119,7 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
// TODO: safety
|
||||
var handle = encoder.Handle;
|
||||
|
||||
_webGpu.RenderPassEncoderEnd(_renderPassEncoderRegistry[handle].Native);
|
||||
wgpuRenderPassEncoderEnd(_renderPassEncoderRegistry[handle].Native);
|
||||
RenderPassEncoderDropped(handle);
|
||||
}
|
||||
|
||||
@@ -133,7 +128,7 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
uint index,
|
||||
RhiBindGroup? bindGroup)
|
||||
{
|
||||
_webGpu.RenderPassEncoderSetBindGroup(
|
||||
wgpuRenderPassEncoderSetBindGroup(
|
||||
_renderPassEncoderRegistry[encoder.Handle].Native,
|
||||
index,
|
||||
_bindGroupRegistry[bindGroup!.Handle].Native,
|
||||
@@ -148,16 +143,16 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
ulong offset,
|
||||
ulong? size)
|
||||
{
|
||||
Buffer* nativeBuffer = null;
|
||||
WGPUBuffer nativeBuffer = null;
|
||||
if (buffer != null)
|
||||
nativeBuffer = _bufferRegistry[buffer.Handle].Native;
|
||||
|
||||
_webGpu.RenderPassEncoderSetVertexBuffer(
|
||||
wgpuRenderPassEncoderSetVertexBuffer(
|
||||
_renderPassEncoderRegistry[encoder.Handle].Native,
|
||||
slot,
|
||||
nativeBuffer,
|
||||
offset,
|
||||
size ?? WebGPU.WholeSize
|
||||
size ?? WGPU_WHOLE_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
@@ -166,7 +161,7 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
uint x, uint y, uint w, uint h)
|
||||
{
|
||||
// TODO: safety
|
||||
_webGpu.RenderPassEncoderSetScissorRect(
|
||||
wgpuRenderPassEncoderSetScissorRect(
|
||||
_renderPassEncoderRegistry[encoder.Handle].Native,
|
||||
x,
|
||||
y,
|
||||
@@ -183,10 +178,10 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
var handle = encoder.Handle;
|
||||
|
||||
Span<byte> buffer = stackalloc byte[512];
|
||||
var pDescriptor = BumpAllocate<CommandBufferDescriptor>(ref buffer);
|
||||
pDescriptor->Label = BumpAllocateUtf8(ref buffer, descriptor.Label);
|
||||
var pDescriptor = BumpAllocate<WGPUCommandBufferDescriptor>(ref buffer);
|
||||
pDescriptor->label = BumpAllocateStringView(ref buffer, descriptor.Label);
|
||||
|
||||
var nativeBuffer = _webGpu.CommandEncoderFinish(
|
||||
var nativeBuffer = wgpuCommandEncoderFinish(
|
||||
_commandEncoderRegistry[handle].Native,
|
||||
pDescriptor
|
||||
);
|
||||
@@ -210,16 +205,16 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
|
||||
private sealed class CommandEncoderReg
|
||||
{
|
||||
public CommandEncoder* Native;
|
||||
public WGPUCommandEncoder Native;
|
||||
}
|
||||
|
||||
private sealed class RenderPassEncoderReg
|
||||
{
|
||||
public RenderPassEncoder* Native;
|
||||
public WGPURenderPassEncoder Native;
|
||||
}
|
||||
|
||||
private sealed class CommandBufferReg
|
||||
{
|
||||
public CommandBuffer* Native;
|
||||
public WGPUCommandBuffer Native;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,10 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Shared.Maths;
|
||||
using Silk.NET.WebGPU;
|
||||
using Color = Silk.NET.WebGPU.Color;
|
||||
using RColor = Robust.Shared.Maths.Color;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde.Rhi;
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
@@ -23,34 +18,65 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
return $"{a}.{b}.{c}.{d}";
|
||||
}
|
||||
|
||||
private static Color WgpuColor(RhiColor color) => new()
|
||||
private static WGPUOptionalBool WgpuOptionalBool(bool? value)
|
||||
{
|
||||
R = color.R,
|
||||
G = color.G,
|
||||
B = color.B,
|
||||
A = color.A
|
||||
};
|
||||
|
||||
private static Extent3D WgpuExtent3D(RhiExtent3D extent)
|
||||
{
|
||||
return new Extent3D
|
||||
return value switch
|
||||
{
|
||||
Width = extent.Width,
|
||||
Height = extent.Height,
|
||||
DepthOrArrayLayers = extent.Depth
|
||||
null => WGPUOptionalBool.WGPUOptionalBool_Undefined,
|
||||
false => WGPUOptionalBool.WGPUOptionalBool_False,
|
||||
true => WGPUOptionalBool.WGPUOptionalBool_True,
|
||||
};
|
||||
}
|
||||
|
||||
private static Origin3D WgpuOrigin3D(RhiOrigin3D origin)
|
||||
private static WGPUColor WgpuColor(RhiColor color) => new()
|
||||
{
|
||||
return new Origin3D
|
||||
r = color.R,
|
||||
g = color.G,
|
||||
b = color.B,
|
||||
a = color.A
|
||||
};
|
||||
|
||||
private static WGPUExtent3D WgpuExtent3D(RhiExtent3D extent)
|
||||
{
|
||||
return new WGPUExtent3D
|
||||
{
|
||||
X = origin.X,
|
||||
Y = origin.Y,
|
||||
Z = origin.Z
|
||||
width = extent.Width,
|
||||
height = extent.Height,
|
||||
depthOrArrayLayers = extent.Depth
|
||||
};
|
||||
}
|
||||
|
||||
private static WGPUOrigin3D WgpuOrigin3D(RhiOrigin3D origin)
|
||||
{
|
||||
return new WGPUOrigin3D
|
||||
{
|
||||
x = origin.X,
|
||||
y = origin.Y,
|
||||
z = origin.Z
|
||||
};
|
||||
}
|
||||
|
||||
private static string? GetString(WGPUStringView stringView)
|
||||
{
|
||||
if (stringView.data == null)
|
||||
{
|
||||
if (stringView.length == WGPU_STRLEN)
|
||||
return null;
|
||||
if (stringView.length == 0)
|
||||
return "";
|
||||
throw new RhiException("Null address to WGPUStringView");
|
||||
}
|
||||
|
||||
if (stringView.length == WGPU_STRLEN)
|
||||
return Marshal.PtrToStringUTF8((IntPtr)stringView.data);
|
||||
|
||||
if (stringView.length > int.MaxValue)
|
||||
throw new RhiException("WGPUStringView too long!");
|
||||
|
||||
var span = new ReadOnlySpan<byte>(stringView.data, (int)stringView.length);
|
||||
return Encoding.UTF8.GetString(span);
|
||||
}
|
||||
|
||||
private static RhiTextureFormat ValidateTextureFormat(RhiTextureFormat format)
|
||||
{
|
||||
if (format is 0 or >= RhiTextureFormat.Final)
|
||||
@@ -123,12 +149,12 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
return compareFunction;
|
||||
}
|
||||
|
||||
private static PowerPreference ValidatePowerPreference(RhiPowerPreference powerPreference)
|
||||
private static WGPUPowerPreference ValidatePowerPreference(RhiPowerPreference powerPreference)
|
||||
{
|
||||
if (powerPreference >= RhiPowerPreference.Final)
|
||||
throw new ArgumentException($"Invalid {nameof(RhiPowerPreference)}");
|
||||
|
||||
return (PowerPreference) powerPreference;
|
||||
return (WGPUPowerPreference) powerPreference;
|
||||
}
|
||||
|
||||
private static string MarshalFromString(byte* str)
|
||||
@@ -205,6 +231,23 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
return (byte*) ptr;
|
||||
}
|
||||
|
||||
private static WGPUStringView BumpAllocateStringView(ref Span<byte> buf, string? str)
|
||||
{
|
||||
if (str == null)
|
||||
return WGPUStringView.Null;
|
||||
|
||||
var byteCount = Encoding.UTF8.GetByteCount(str) ;
|
||||
var ptr = BumpAllocate(ref buf, byteCount);
|
||||
var dstSpan = new Span<byte>(ptr, byteCount);
|
||||
Encoding.UTF8.GetBytes(str, dstSpan);
|
||||
|
||||
return new WGPUStringView
|
||||
{
|
||||
data = (sbyte*)ptr,
|
||||
length = (nuint)byteCount
|
||||
};
|
||||
}
|
||||
|
||||
[DoesNotReturn]
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void ThrowBumpAllocOutOfSpace()
|
||||
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using Silk.NET.WebGPU;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde.Rhi;
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
@@ -19,27 +16,27 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
// TODO: Thread safety
|
||||
var nativeTexture = _textureRegistry[destination.Texture.Handle].Native;
|
||||
|
||||
var nativeDestination = new ImageCopyTexture
|
||||
var nativeDestination = new WGPUTexelCopyTextureInfo
|
||||
{
|
||||
Aspect = (TextureAspect)ValidateTextureAspect(destination.Aspect),
|
||||
Texture = nativeTexture,
|
||||
Origin = WgpuOrigin3D(destination.Origin),
|
||||
MipLevel = destination.MipLevel
|
||||
aspect = (WGPUTextureAspect)ValidateTextureAspect(destination.Aspect),
|
||||
texture = nativeTexture,
|
||||
origin = WgpuOrigin3D(destination.Origin),
|
||||
mipLevel = destination.MipLevel
|
||||
};
|
||||
|
||||
var nativeDataLayout = new TextureDataLayout
|
||||
var nativeDataLayout = new WGPUTexelCopyBufferLayout
|
||||
{
|
||||
// TODO: Validation
|
||||
Offset = dataLayout.Offset,
|
||||
BytesPerRow = dataLayout.BytesPerRow,
|
||||
RowsPerImage = dataLayout.RowsPerImage
|
||||
offset = dataLayout.Offset,
|
||||
bytesPerRow = dataLayout.BytesPerRow,
|
||||
rowsPerImage = dataLayout.RowsPerImage
|
||||
};
|
||||
|
||||
var nativeSize = WgpuExtent3D(size);
|
||||
|
||||
fixed (byte* pData = data)
|
||||
{
|
||||
_webGpu.QueueWriteTexture(
|
||||
wgpuQueueWriteTexture(
|
||||
_wgpuQueue,
|
||||
&nativeDestination,
|
||||
pData, (nuint) data.Length,
|
||||
@@ -55,7 +52,7 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
|
||||
fixed (byte* pData = data)
|
||||
{
|
||||
_webGpu.QueueWriteBuffer(
|
||||
wgpuQueueWriteBuffer(
|
||||
_wgpuQueue,
|
||||
nativeBuffer,
|
||||
bufferOffset,
|
||||
@@ -68,13 +65,13 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
// TODO: Safety
|
||||
|
||||
var pBuffers = stackalloc CommandBuffer*[commandBuffers.Length];
|
||||
var pBuffers = stackalloc WGPUCommandBuffer[commandBuffers.Length];
|
||||
for (var i = 0; i < commandBuffers.Length; i++)
|
||||
{
|
||||
pBuffers[i] = _commandBufferRegistry[commandBuffers[i].Handle].Native;
|
||||
}
|
||||
|
||||
_webGpu.QueueSubmit(
|
||||
wgpuQueueSubmit(
|
||||
_wgpuQueue,
|
||||
(uint) commandBuffers.Length,
|
||||
pBuffers
|
||||
@@ -1,6 +1,4 @@
|
||||
using System.Threading;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde.Rhi;
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed partial class RhiWebGpu
|
||||
{
|
||||
224
Robust.Client.Graphics.Rhi/WebGpu/RhiWebGpu.RenderPipeline.cs
Normal file
224
Robust.Client.Graphics.Rhi/WebGpu/RhiWebGpu.RenderPipeline.cs
Normal file
@@ -0,0 +1,224 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
private readonly Dictionary<RhiHandle, RenderPipelineReg> _renderPipelineRegistry = new();
|
||||
private readonly Dictionary<RhiHandle, PipelineLayoutReg> _pipelineLayoutRegistry = new();
|
||||
|
||||
public override RhiPipelineLayout CreatePipelineLayout(in RhiPipelineLayoutDescriptor descriptor)
|
||||
{
|
||||
// TODO: SAFETY
|
||||
|
||||
Span<byte> buffer = stackalloc byte[128];
|
||||
var pDescriptor = BumpAllocate<WGPUPipelineLayoutDescriptor>(ref buffer);
|
||||
pDescriptor->label = BumpAllocateStringView(ref buffer, descriptor.Label);
|
||||
|
||||
var layouts = descriptor.BindGroupLayouts;
|
||||
pDescriptor->bindGroupLayoutCount = (uint) layouts.Length;
|
||||
pDescriptor->bindGroupLayouts = BumpAllocatePtr<WGPUBindGroupLayoutImpl>(ref buffer, layouts.Length);
|
||||
for (var i = 0; i < layouts.Length; i++)
|
||||
{
|
||||
pDescriptor->bindGroupLayouts[i] = _bindGroupLayoutRegistry[layouts[i].Handle].Native;
|
||||
}
|
||||
|
||||
var native = wgpuDeviceCreatePipelineLayout(_wgpuDevice, pDescriptor);
|
||||
|
||||
// TODO: Thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
_pipelineLayoutRegistry.Add(handle, new PipelineLayoutReg { Native = native });
|
||||
return new RhiPipelineLayout(this, handle);
|
||||
}
|
||||
|
||||
public override RhiRenderPipeline CreateRenderPipeline(in RhiRenderPipelineDescriptor descriptor)
|
||||
{
|
||||
// TODO: THREAD SAFETY
|
||||
// TODO: INPUT VALIDATION
|
||||
|
||||
var vertexShader = _shaderModuleRegistry[descriptor.Vertex.ProgrammableStage.ShaderModule.Handle].Native;
|
||||
|
||||
const int bufferSize = 8192;
|
||||
var bufferPtr = NativeMemory.AlignedAlloc(bufferSize, 8);
|
||||
|
||||
WGPURenderPipeline nativePipeline;
|
||||
try
|
||||
{
|
||||
var buffer = new Span<byte>(bufferPtr, bufferSize);
|
||||
|
||||
WGPURenderPipelineDescriptor pipelineDesc = default;
|
||||
pipelineDesc.label = BumpAllocateStringView(ref buffer, descriptor.Label);
|
||||
|
||||
// Pipeline layout
|
||||
switch (descriptor.Layout)
|
||||
{
|
||||
case RhiPipelineLayout pipelineLayout:
|
||||
pipelineDesc.layout = _pipelineLayoutRegistry[pipelineLayout.Handle].Native;
|
||||
break;
|
||||
|
||||
case RhiAutoLayoutMode:
|
||||
throw new NotSupportedException("wgpu does not support auto layout yet");
|
||||
// Default case: no layout given, do nothing
|
||||
}
|
||||
|
||||
// Vertex state
|
||||
pipelineDesc.vertex.module = vertexShader;
|
||||
pipelineDesc.vertex.entryPoint = BumpAllocateStringView(
|
||||
ref buffer,
|
||||
descriptor.Vertex.ProgrammableStage.EntryPoint);
|
||||
|
||||
WgpuProgrammableConstants(
|
||||
ref buffer,
|
||||
descriptor.Vertex.ProgrammableStage.Constants,
|
||||
out pipelineDesc.vertex.constantCount,
|
||||
out pipelineDesc.vertex.constants);
|
||||
|
||||
var buffers = descriptor.Vertex.Buffers;
|
||||
pipelineDesc.vertex.bufferCount = (uint)buffers.Length;
|
||||
pipelineDesc.vertex.buffers = BumpAllocate<WGPUVertexBufferLayout>(ref buffer, buffers.Length);
|
||||
for (var i = 0; i < buffers.Length; i++)
|
||||
{
|
||||
ref var bufferLayout = ref pipelineDesc.vertex.buffers[i];
|
||||
bufferLayout.arrayStride = buffers[i].ArrayStride;
|
||||
bufferLayout.stepMode = (WGPUVertexStepMode)buffers[i].StepMode;
|
||||
|
||||
var attributes = buffers[i].Attributes;
|
||||
bufferLayout.attributeCount = (uint)attributes.Length;
|
||||
bufferLayout.attributes = BumpAllocate<WGPUVertexAttribute>(ref buffer, attributes.Length);
|
||||
for (var j = 0; j < attributes.Length; j++)
|
||||
{
|
||||
ref var attribute = ref bufferLayout.attributes[j];
|
||||
attribute.format = (WGPUVertexFormat)attributes[j].Format;
|
||||
attribute.offset = attributes[j].Offset;
|
||||
attribute.shaderLocation = attributes[j].ShaderLocation;
|
||||
}
|
||||
}
|
||||
|
||||
// Primitive state
|
||||
pipelineDesc.primitive.topology = (WGPUPrimitiveTopology)descriptor.Primitive.Topology;
|
||||
pipelineDesc.primitive.stripIndexFormat = (WGPUIndexFormat)descriptor.Primitive.StripIndexformat;
|
||||
pipelineDesc.primitive.frontFace = (WGPUFrontFace)descriptor.Primitive.FrontFace;
|
||||
pipelineDesc.primitive.cullMode = (WGPUCullMode)descriptor.Primitive.CullMode;
|
||||
pipelineDesc.primitive.unclippedDepth = descriptor.Primitive.UnclippedDepth ? 1u : 0u;
|
||||
|
||||
// Depth stencil state
|
||||
if (descriptor.DepthStencil is { } depthStencil)
|
||||
{
|
||||
var pDepthStencil = BumpAllocate<WGPUDepthStencilState>(ref buffer);
|
||||
pipelineDesc.depthStencil = pDepthStencil;
|
||||
|
||||
pDepthStencil->format = (WGPUTextureFormat)depthStencil.Format;
|
||||
pDepthStencil->depthWriteEnabled = WgpuOptionalBool(depthStencil.DepthWriteEnabled);
|
||||
pDepthStencil->depthCompare = (WGPUCompareFunction)depthStencil.DepthCompare;
|
||||
pDepthStencil->stencilFront = WgpuStencilFaceState(depthStencil.StencilFront ?? new RhiStencilFaceState());
|
||||
pDepthStencil->stencilBack = WgpuStencilFaceState(depthStencil.StencilBack ?? new RhiStencilFaceState());
|
||||
pDepthStencil->stencilReadMask = depthStencil.StencilReadMask;
|
||||
pDepthStencil->stencilWriteMask = depthStencil.StencilWriteMask;
|
||||
pDepthStencil->depthBias = depthStencil.DepthBias;
|
||||
pDepthStencil->depthBiasSlopeScale = depthStencil.DepthBiasSlopeScale;
|
||||
pDepthStencil->depthBiasClamp = depthStencil.DepthBiasClamp;
|
||||
}
|
||||
|
||||
// Multisample state
|
||||
pipelineDesc.multisample.count = descriptor.Multisample.Count;
|
||||
pipelineDesc.multisample.mask = descriptor.Multisample.Mask;
|
||||
pipelineDesc.multisample.alphaToCoverageEnabled = descriptor.Multisample.AlphaToCoverageEnabled ? 1u : 0u;
|
||||
|
||||
// Fragment state
|
||||
if (descriptor.Fragment is { } fragment)
|
||||
{
|
||||
var fragmentShader = _shaderModuleRegistry[fragment.ProgrammableStage.ShaderModule.Handle].Native;
|
||||
|
||||
var pFragment = BumpAllocate<WGPUFragmentState>(ref buffer);
|
||||
pipelineDesc.fragment = pFragment;
|
||||
|
||||
pFragment->module = fragmentShader;
|
||||
pFragment->entryPoint = BumpAllocateStringView(ref buffer, fragment.ProgrammableStage.EntryPoint);
|
||||
|
||||
WgpuProgrammableConstants(
|
||||
ref buffer,
|
||||
fragment.ProgrammableStage.Constants,
|
||||
out pFragment->constantCount,
|
||||
out pFragment->constants);
|
||||
|
||||
var targets = fragment.Targets;
|
||||
pFragment->targetCount = (uint)targets.Length;
|
||||
pFragment->targets = BumpAllocate<WGPUColorTargetState>(ref buffer, targets.Length);
|
||||
for (var i = 0; i < targets.Length; i++)
|
||||
{
|
||||
ref var target = ref pFragment->targets[i];
|
||||
target.format = (WGPUTextureFormat)targets[i].Format;
|
||||
|
||||
if (targets[i].Blend is { } blend)
|
||||
{
|
||||
var pBlend = BumpAllocate<WGPUBlendState>(ref buffer);
|
||||
target.blend = pBlend;
|
||||
|
||||
pBlend->alpha = WgpuBlendComponent(blend.Alpha);
|
||||
pBlend->color = WgpuBlendComponent(blend.Color);
|
||||
}
|
||||
|
||||
target.writeMask = (ulong)targets[i].WriteMask;
|
||||
}
|
||||
}
|
||||
|
||||
nativePipeline = wgpuDeviceCreateRenderPipeline(_wgpuDevice, &pipelineDesc);
|
||||
}
|
||||
finally
|
||||
{
|
||||
NativeMemory.AlignedFree(bufferPtr);
|
||||
}
|
||||
|
||||
// TODO: Thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
_renderPipelineRegistry.Add(handle, new RenderPipelineReg { Native = nativePipeline });
|
||||
return new RhiRenderPipeline(this, handle);
|
||||
}
|
||||
|
||||
private static WGPUStencilFaceState WgpuStencilFaceState(in RhiStencilFaceState state)
|
||||
{
|
||||
return new WGPUStencilFaceState
|
||||
{
|
||||
compare = (WGPUCompareFunction)state.Compare,
|
||||
failOp = (WGPUStencilOperation)state.FailOp,
|
||||
depthFailOp = (WGPUStencilOperation)state.DepthFailOp,
|
||||
passOp = (WGPUStencilOperation)state.PassOp
|
||||
};
|
||||
}
|
||||
|
||||
private static void WgpuProgrammableConstants(
|
||||
ref Span<byte> buffer,
|
||||
RhiConstantEntry[] constants,
|
||||
out nuint constantCount,
|
||||
out WGPUConstantEntry* pConstants)
|
||||
{
|
||||
constantCount = (uint)constants.Length;
|
||||
pConstants = BumpAllocate<WGPUConstantEntry>(ref buffer, constants.Length);
|
||||
for (var i = 0; i < constants.Length; i++)
|
||||
{
|
||||
ref var constant = ref pConstants[i];
|
||||
constant.key = BumpAllocateStringView(ref buffer, constants[i].Key);
|
||||
constant.value = constants[i].Value;
|
||||
}
|
||||
}
|
||||
|
||||
private static WGPUBlendComponent WgpuBlendComponent(in RhiBlendComponent component)
|
||||
{
|
||||
return new WGPUBlendComponent
|
||||
{
|
||||
operation = (WGPUBlendOperation)component.Operation,
|
||||
dstFactor = (WGPUBlendFactor)component.DstFactor,
|
||||
srcFactor = (WGPUBlendFactor)component.SrcFactor,
|
||||
};
|
||||
}
|
||||
|
||||
private sealed class RenderPipelineReg
|
||||
{
|
||||
public WGPURenderPipeline Native;
|
||||
}
|
||||
|
||||
private sealed class PipelineLayoutReg
|
||||
{
|
||||
public WGPUPipelineLayout Native;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using Silk.NET.WebGPU;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde.Rhi;
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
@@ -17,25 +14,29 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
var mipmapFilter = ValidateMipmapFilterMode(descriptor.MipmapFilter);
|
||||
var compare = ValidateCompareFunction(descriptor.Compare);
|
||||
|
||||
Sampler* sampler;
|
||||
WGPUSampler sampler;
|
||||
fixed (byte* label = MakeLabel(descriptor.Label))
|
||||
{
|
||||
var samplerDesc = new SamplerDescriptor
|
||||
var samplerDesc = new WGPUSamplerDescriptor
|
||||
{
|
||||
AddressModeU = (AddressMode) addressModeU,
|
||||
AddressModeV = (AddressMode) addressModeV,
|
||||
AddressModeW = (AddressMode) addressModeW,
|
||||
MagFilter = (FilterMode) magFilter,
|
||||
MinFilter = (FilterMode) minFilter,
|
||||
MipmapFilter = (MipmapFilterMode) mipmapFilter,
|
||||
LodMinClamp = descriptor.LodMinClamp,
|
||||
LodMaxClamp = descriptor.LodMaxClamp,
|
||||
Compare = (CompareFunction) compare,
|
||||
MaxAnisotropy = descriptor.MaxAnisotropy,
|
||||
Label = label
|
||||
addressModeU = (WGPUAddressMode) addressModeU,
|
||||
addressModeV = (WGPUAddressMode) addressModeV,
|
||||
addressModeW = (WGPUAddressMode) addressModeW,
|
||||
magFilter = (WGPUFilterMode) magFilter,
|
||||
minFilter = (WGPUFilterMode) minFilter,
|
||||
mipmapFilter = (WGPUMipmapFilterMode) mipmapFilter,
|
||||
lodMinClamp = descriptor.LodMinClamp,
|
||||
lodMaxClamp = descriptor.LodMaxClamp,
|
||||
compare = (WGPUCompareFunction) compare,
|
||||
maxAnisotropy = descriptor.MaxAnisotropy,
|
||||
label = new WGPUStringView
|
||||
{
|
||||
data = (sbyte*)label,
|
||||
length = WGPU_STRLEN
|
||||
}
|
||||
};
|
||||
|
||||
sampler = _webGpu.DeviceCreateSampler(_wgpuDevice, &samplerDesc);
|
||||
sampler = wgpuDeviceCreateSampler(_wgpuDevice, &samplerDesc);
|
||||
}
|
||||
|
||||
// TODO: Thread safety
|
||||
@@ -46,6 +47,6 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
|
||||
private sealed class SamplerReg
|
||||
{
|
||||
public Sampler* Native;
|
||||
public WGPUSampler Native;
|
||||
}
|
||||
}
|
||||
46
Robust.Client.Graphics.Rhi/WebGpu/RhiWebGpu.ShaderModule.cs
Normal file
46
Robust.Client.Graphics.Rhi/WebGpu/RhiWebGpu.ShaderModule.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
private readonly Dictionary<RhiHandle, ShaderModuleReg> _shaderModuleRegistry = new();
|
||||
|
||||
public override RhiShaderModule CreateShaderModule(in RhiShaderModuleDescriptor descriptor)
|
||||
{
|
||||
var codeBytes = Encoding.UTF8.GetBytes(descriptor.Code);
|
||||
|
||||
WGPUShaderModule shaderModule;
|
||||
fixed (byte* pCode = codeBytes)
|
||||
fixed (byte* pLabel = MakeLabel(descriptor.Label))
|
||||
{
|
||||
var descWgsl = new WGPUShaderSourceWGSL();
|
||||
descWgsl.code = new WGPUStringView
|
||||
{
|
||||
data = (sbyte*)pCode,
|
||||
length = WGPU_STRLEN
|
||||
};
|
||||
descWgsl.chain.sType = WGPUSType.WGPUSType_ShaderSourceWGSL;
|
||||
|
||||
var desc = new WGPUShaderModuleDescriptor();
|
||||
desc.label = new WGPUStringView
|
||||
{
|
||||
data = (sbyte*)pLabel,
|
||||
length = WGPU_STRLEN
|
||||
};
|
||||
desc.nextInChain = (WGPUChainedStruct*) (&descWgsl);
|
||||
|
||||
shaderModule = wgpuDeviceCreateShaderModule(_wgpuDevice, &desc);
|
||||
}
|
||||
|
||||
// TODO: Thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
_shaderModuleRegistry.Add(handle, new ShaderModuleReg { Native = shaderModule });
|
||||
return new RhiShaderModule(this, handle);
|
||||
}
|
||||
|
||||
private sealed class ShaderModuleReg
|
||||
{
|
||||
public WGPUShaderModule Native;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Utility;
|
||||
using Silk.NET.WebGPU;
|
||||
using WGPUTexture = Silk.NET.WebGPU.Texture;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde.Rhi;
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
@@ -22,39 +17,45 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
ValidateTextureUsage(usage);
|
||||
|
||||
// TODO: Copy to stackalloc instead.
|
||||
var viewFormats = descriptor.ViewFormats?.ToArray() ?? Array.Empty<RhiTextureFormat>();
|
||||
var viewFormats = descriptor.ViewFormats?.ToArray() ?? [];
|
||||
foreach (var vf in viewFormats)
|
||||
{
|
||||
ValidateTextureFormat(vf);
|
||||
}
|
||||
|
||||
DebugTools.Assert(
|
||||
sizeof(RhiTextureFormat) == sizeof(TextureFormat),
|
||||
Debug.Assert(
|
||||
sizeof(RhiTextureFormat) == sizeof(WGPUTextureFormat),
|
||||
"Pointer to view formats array is cast directly to pass to native, sizes must match");
|
||||
|
||||
WGPUTexture* texturePtr;
|
||||
fixed (byte* label = MakeLabel(descriptor.Label))
|
||||
WGPUTexture texturePtr;
|
||||
var label = MakeLabel(descriptor.Label);
|
||||
fixed (byte* pLabel = label)
|
||||
fixed (RhiTextureFormat* pViewFormats = viewFormats)
|
||||
{
|
||||
var webGpuDesc = new TextureDescriptor
|
||||
var webGpuDesc = new WGPUTextureDescriptor
|
||||
{
|
||||
SampleCount = descriptor.SampleCount,
|
||||
MipLevelCount = descriptor.MipLevelCount,
|
||||
Dimension = (TextureDimension) dimension,
|
||||
Format = (TextureFormat) format,
|
||||
Label = label,
|
||||
Size = WgpuExtent3D(descriptor.Size),
|
||||
Usage = (TextureUsage) usage,
|
||||
ViewFormats = (TextureFormat*) pViewFormats,
|
||||
ViewFormatCount = checked((uint) viewFormats.Length),
|
||||
sampleCount = descriptor.SampleCount,
|
||||
mipLevelCount = descriptor.MipLevelCount,
|
||||
dimension = (WGPUTextureDimension) dimension,
|
||||
format = (WGPUTextureFormat) format,
|
||||
label = new WGPUStringView
|
||||
{
|
||||
data = (sbyte*)pLabel,
|
||||
length = (UIntPtr)(label?.Length ?? 0),
|
||||
},
|
||||
size = WgpuExtent3D(descriptor.Size),
|
||||
usage = (ulong) usage,
|
||||
viewFormats = (WGPUTextureFormat*) pViewFormats,
|
||||
viewFormatCount = checked((uint) viewFormats.Length),
|
||||
};
|
||||
|
||||
texturePtr = _webGpu.DeviceCreateTexture(_wgpuDevice, &webGpuDesc);
|
||||
texturePtr = wgpuDeviceCreateTexture(_wgpuDevice, &webGpuDesc);
|
||||
}
|
||||
|
||||
if (texturePtr == null)
|
||||
throw new RhiException("Texture creation failed");
|
||||
|
||||
return AllocRhiTexture(texturePtr);
|
||||
// TODO: Thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
_textureRegistry.Add(handle, new TextureReg { Native = texturePtr });
|
||||
@@ -79,22 +80,26 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
if (arrayLayerCount == 0)
|
||||
throw new ArgumentException($"Invalid {nameof(descriptor.ArrayLayerCount)}");
|
||||
|
||||
TextureView* textureView;
|
||||
WGPUTextureView textureView;
|
||||
fixed (byte* label = MakeLabel(descriptor.Label))
|
||||
{
|
||||
var webGpuDesc = new TextureViewDescriptor
|
||||
var webGpuDesc = new WGPUTextureViewDescriptor
|
||||
{
|
||||
Format = (TextureFormat) format,
|
||||
Dimension = (TextureViewDimension) dimension,
|
||||
Aspect = (TextureAspect) aspect,
|
||||
Label = label,
|
||||
BaseMipLevel = descriptor.BaseMipLevel,
|
||||
MipLevelCount = mipLevelCount,
|
||||
BaseArrayLayer = descriptor.BaseArrayLayer,
|
||||
ArrayLayerCount = descriptor.ArrayLayerCount
|
||||
format = (WGPUTextureFormat) format,
|
||||
dimension = (WGPUTextureViewDimension) dimension,
|
||||
aspect = (WGPUTextureAspect) aspect,
|
||||
label = new WGPUStringView
|
||||
{
|
||||
data = (sbyte*)label,
|
||||
length = WGPU_STRLEN
|
||||
},
|
||||
baseMipLevel = descriptor.BaseMipLevel,
|
||||
mipLevelCount = mipLevelCount,
|
||||
baseArrayLayer = descriptor.BaseArrayLayer,
|
||||
arrayLayerCount = descriptor.ArrayLayerCount
|
||||
};
|
||||
|
||||
textureView = _webGpu.TextureCreateView(nativeTexture, &webGpuDesc);
|
||||
textureView = wgpuTextureCreateView(nativeTexture, &webGpuDesc);
|
||||
}
|
||||
|
||||
return AllocRhiTextureView(textureView);
|
||||
@@ -102,24 +107,33 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
|
||||
internal override void TextureViewDrop(RhiTextureView textureView)
|
||||
{
|
||||
_wgpu.TextureViewDrop(_textureViewRegistry[textureView.Handle].Native);
|
||||
wgpuTextureViewRelease(_textureViewRegistry[textureView.Handle].Native);
|
||||
|
||||
_textureViewRegistry.Remove(textureView.Handle);
|
||||
}
|
||||
|
||||
internal override RhiTextureView CreateTextureViewForWindow(Clyde.WindowReg reg)
|
||||
internal override RhiTexture CreateTextureViewForWindow(WindowData reg)
|
||||
{
|
||||
// TODO: Thread safety
|
||||
|
||||
var swapChain = reg.RhiWebGpuData!.SwapChain;
|
||||
var surface = reg.Surface;
|
||||
|
||||
// This creates a new texture view handle.
|
||||
var textureView = _webGpu.SwapChainGetCurrentTextureView(swapChain);
|
||||
WGPUSurfaceTexture textureRet;
|
||||
wgpuSurfaceGetCurrentTexture(surface, &textureRet);
|
||||
|
||||
return AllocRhiTextureView(textureView);
|
||||
return AllocRhiTexture(textureRet.texture);
|
||||
}
|
||||
|
||||
private RhiTextureView AllocRhiTextureView(TextureView* native)
|
||||
private RhiTexture AllocRhiTexture(WGPUTexture native)
|
||||
{
|
||||
// TODO: Thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
_textureRegistry.Add(handle, new TextureReg { Native = native });
|
||||
return new RhiTexture(this, handle);
|
||||
}
|
||||
|
||||
private RhiTextureView AllocRhiTextureView(WGPUTextureView native)
|
||||
{
|
||||
// TODO: Thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
@@ -129,11 +143,11 @@ internal sealed unsafe partial class RhiWebGpu
|
||||
|
||||
private sealed class TextureReg
|
||||
{
|
||||
public WGPUTexture* Native;
|
||||
public WGPUTexture Native;
|
||||
}
|
||||
|
||||
private sealed class TextureViewReg
|
||||
{
|
||||
public TextureView* Native;
|
||||
public WGPUTextureView Native;
|
||||
}
|
||||
}
|
||||
126
Robust.Client.Graphics.Rhi/WebGpu/RhiWebGpu.Window.cs
Normal file
126
Robust.Client.Graphics.Rhi/WebGpu/RhiWebGpu.Window.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
public sealed class WindowData
|
||||
{
|
||||
public WGPUSurface Surface;
|
||||
}
|
||||
|
||||
private WindowData CreateSurfaceForWindow(in RhiWindowSurfaceParams surfaceParams)
|
||||
{
|
||||
WGPUSurfaceDescriptor surfaceDesc = default;
|
||||
|
||||
#if WINDOWS
|
||||
var surfaceDescHwnd = new WGPUSurfaceSourceWindowsHWND
|
||||
{
|
||||
chain =
|
||||
{
|
||||
sType = WGPUSType.WGPUSType_SurfaceSourceWindowsHWND
|
||||
},
|
||||
hinstance = surfaceParams.HInstance,
|
||||
hwnd = surfaceParams.HWnd,
|
||||
};
|
||||
|
||||
surfaceDesc.nextInChain = (WGPUChainedStruct*)(&surfaceDescHwnd);
|
||||
|
||||
#elif OSX
|
||||
// TODO: macOS surface creation
|
||||
WGPUSurfaceSourceMetalLayer surfaceDescMetal;
|
||||
/*
|
||||
var metalLayer = _clyde._windowing.WindowGetMetalLayer(window);
|
||||
|
||||
if (metalLayer != null)
|
||||
{
|
||||
surfaceDescMetal = new WGPUSurfaceSourceMetalLayer
|
||||
{
|
||||
chain =
|
||||
{
|
||||
sType = WGPUSType.WGPUSType_SurfaceSourceMetalLayer
|
||||
},
|
||||
layer = ((IntPtr)metalLayer.Value).ToPointer()
|
||||
};
|
||||
|
||||
surfaceDesc.nextInChain = (WGPUChainedStruct*)(&surfaceDescMetal);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
*/
|
||||
#elif LINUX
|
||||
// TODO: Linux surface creation
|
||||
/*
|
||||
WGPUSurfaceSourceXlibWindow surfaceDescX11;
|
||||
var xDisplay = _clyde._windowing.WindowGetX11Display(window);
|
||||
var xWindow = _clyde._windowing.WindowGetX11Id(window);
|
||||
|
||||
if (xDisplay != null && xWindow != null)
|
||||
{
|
||||
surfaceDescX11 = new WGPUSurfaceSourceXlibWindow
|
||||
{
|
||||
chain =
|
||||
{
|
||||
sType = WGPUSType.WGPUSType_SurfaceSourceXlibWindow
|
||||
},
|
||||
display = ((IntPtr)xDisplay.Value).ToPointer(),
|
||||
window = xWindow.Value
|
||||
};
|
||||
|
||||
surfaceDesc.nextInChain = (WGPUChainedStruct*)(&surfaceDescX11);
|
||||
*/
|
||||
#endif
|
||||
|
||||
var surface = wgpuInstanceCreateSurface(_wgpuInstance, &surfaceDesc);
|
||||
return new WindowData
|
||||
{
|
||||
Surface = surface
|
||||
};
|
||||
}
|
||||
|
||||
private void ConfigureSurface(WindowData window, Vector2i size)
|
||||
{
|
||||
// TODO: Safety
|
||||
var format = WGPUTextureFormat.WGPUTextureFormat_BGRA8UnormSrgb;
|
||||
_sawmill.Debug($"Preferred surface format is {format}");
|
||||
|
||||
var swapChainDesc = new WGPUSurfaceConfiguration
|
||||
{
|
||||
format = format,
|
||||
height = (uint)size.X,
|
||||
width = (uint)size.Y,
|
||||
usage = WGPUTextureUsage_RenderAttachment,
|
||||
presentMode = WGPUPresentMode.WGPUPresentMode_Fifo
|
||||
};
|
||||
|
||||
wgpuSurfaceConfigure(window.Surface, &swapChainDesc);
|
||||
|
||||
_sawmill.Debug("WebGPU Surface created!");
|
||||
}
|
||||
|
||||
internal override WindowData WindowCreated(in RhiWindowSurfaceParams surfaceParams, Vector2i size)
|
||||
{
|
||||
var windowData = CreateSurfaceForWindow(in surfaceParams);
|
||||
ConfigureSurface(windowData, size);
|
||||
return windowData;
|
||||
}
|
||||
|
||||
internal override void WindowDestroy(WindowData reg)
|
||||
{
|
||||
wgpuSurfaceUnconfigure(reg.Surface);
|
||||
wgpuSurfaceRelease(reg.Surface);
|
||||
}
|
||||
|
||||
internal override void WindowRecreateSwapchain(WindowData reg, Vector2i size)
|
||||
{
|
||||
ConfigureSurface(reg, size);
|
||||
}
|
||||
|
||||
internal override void WindowPresent(WindowData reg)
|
||||
{
|
||||
// TODO: Safety
|
||||
wgpuSurfacePresent(reg.Surface);
|
||||
}
|
||||
}
|
||||
333
Robust.Client.Graphics.Rhi/WebGpu/RhiWebGpu.cs
Normal file
333
Robust.Client.Graphics.Rhi/WebGpu/RhiWebGpu.cs
Normal file
@@ -0,0 +1,333 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Utility;
|
||||
using RLogLevel = Robust.Shared.Log.LogLevel;
|
||||
|
||||
#pragma warning disable CS8500
|
||||
|
||||
namespace Robust.Client.Graphics.Rhi.WebGpu;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu : RhiBase
|
||||
{
|
||||
private readonly ISawmill _sawmill;
|
||||
private readonly ISawmill _apiLogSawmill;
|
||||
|
||||
private WGPUInstance _wgpuInstance;
|
||||
private WGPUAdapter _wgpuAdapter;
|
||||
private WGPUDevice _wgpuDevice;
|
||||
private WGPUQueue _wgpuQueue;
|
||||
|
||||
private RhiLimits? _deviceLimits;
|
||||
private RhiAdapterInfo? _adapterProperties;
|
||||
private string _description = "not initialized";
|
||||
|
||||
public override RhiLimits DeviceLimits =>
|
||||
_deviceLimits ?? throw new InvalidOperationException("Not initialized yet");
|
||||
|
||||
public override RhiAdapterInfo AdapterInfo =>
|
||||
_adapterProperties ?? throw new InvalidOperationException("Not initialized yet");
|
||||
|
||||
public override string Description => _description;
|
||||
|
||||
public RhiWebGpu(IDependencyCollection dependencies)
|
||||
{
|
||||
var logMgr = dependencies.Resolve<ILogManager>();
|
||||
|
||||
_sawmill = logMgr.GetSawmill("clyde.rhi.webGpu");
|
||||
_apiLogSawmill = logMgr.GetSawmill("clyde.rhi.webGpu.apiLog");
|
||||
|
||||
Queue = new RhiQueue(this, default);
|
||||
}
|
||||
|
||||
internal override void Init(in RhiInitParams initParams, out WindowData windowData)
|
||||
{
|
||||
_sawmill.Info("Initializing WebGPU RHI!");
|
||||
|
||||
InitInstance(in initParams);
|
||||
|
||||
windowData = CreateSurfaceForWindow(in initParams.MainWindowSurfaceParams);
|
||||
|
||||
_sawmill.Debug("WebGPU main surface created!");
|
||||
|
||||
InitAdapterAndDevice(in initParams, windowData.Surface);
|
||||
|
||||
ConfigureSurface(windowData, initParams.MainWindowSize);
|
||||
}
|
||||
|
||||
private void InitInstance(in RhiInitParams initParams)
|
||||
{
|
||||
var wgpuVersion = WgpuVersionToString(wgpuGetVersion());
|
||||
_sawmill.Debug($"wgpu-native loaded, version: {wgpuVersion}");
|
||||
|
||||
_description = $"WebGPU (wgpu-native {wgpuVersion})";
|
||||
|
||||
InitLogging();
|
||||
|
||||
Span<byte> buffer = stackalloc byte[128];
|
||||
var pInstanceDescriptor = BumpAllocate<WGPUInstanceDescriptor>(ref buffer);
|
||||
|
||||
// Specify instance extras for wgpu-native.
|
||||
var pInstanceExtras = BumpAllocate<WGPUInstanceExtras>(ref buffer);
|
||||
pInstanceDescriptor->nextInChain = (WGPUChainedStruct*)pInstanceExtras;
|
||||
pInstanceExtras->chain.sType = (WGPUSType)WGPUNativeSType.WGPUSType_InstanceExtras;
|
||||
pInstanceExtras->backends = (uint)GetInstanceBackendCfg(initParams.Backends);
|
||||
|
||||
_wgpuInstance = wgpuCreateInstance(pInstanceDescriptor);
|
||||
|
||||
_sawmill.Debug("WebGPU instance created!");
|
||||
}
|
||||
|
||||
private ulong GetInstanceBackendCfg(string backendCvar)
|
||||
{
|
||||
if (backendCvar == "all")
|
||||
return WGPUInstanceBackend_Primary | WGPUInstanceBackend_Secondary;
|
||||
|
||||
var backends = 0ul;
|
||||
foreach (var opt in backendCvar.Split(","))
|
||||
{
|
||||
backends |= opt switch
|
||||
{
|
||||
"vulkan" => WGPUInstanceBackend_Vulkan,
|
||||
"gl" => WGPUInstanceBackend_GL,
|
||||
"metal" => WGPUInstanceBackend_Metal,
|
||||
"dx12" => WGPUInstanceBackend_DX12,
|
||||
"dx11" => WGPUInstanceBackend_DX11,
|
||||
"browser" => WGPUInstanceBackend_BrowserWebGPU,
|
||||
_ => throw new ArgumentException($"Unknown wgpu backend: '{opt}'")
|
||||
};
|
||||
}
|
||||
|
||||
return backends;
|
||||
}
|
||||
|
||||
private void InitAdapterAndDevice(in RhiInitParams initParams, WGPUSurface forSurface)
|
||||
{
|
||||
var powerPreference = ValidatePowerPreference(initParams.PowerPreference);
|
||||
|
||||
var requestAdapterOptions = new WGPURequestAdapterOptions
|
||||
{
|
||||
compatibleSurface = forSurface,
|
||||
powerPreference = powerPreference
|
||||
};
|
||||
|
||||
WgpuRequestAdapterResult result;
|
||||
wgpuInstanceRequestAdapter(
|
||||
_wgpuInstance,
|
||||
&requestAdapterOptions,
|
||||
new WGPURequestAdapterCallbackInfo
|
||||
{
|
||||
callback = &WgpuRequestAdapterCallback,
|
||||
userdata1 = &result,
|
||||
});
|
||||
|
||||
if (result.Status != WGPURequestAdapterStatus.WGPURequestAdapterStatus_Success)
|
||||
throw new RhiException($"Adapter request failed: {result.Message}");
|
||||
|
||||
_sawmill.Debug("WebGPU adapter created!");
|
||||
|
||||
_wgpuAdapter = result.Adapter.P;
|
||||
|
||||
WGPUAdapterInfo adapterProps = default;
|
||||
wgpuAdapterGetInfo(_wgpuAdapter, &adapterProps);
|
||||
|
||||
WGPULimits adapterLimits = default;
|
||||
wgpuAdapterGetLimits(_wgpuAdapter, &adapterLimits);
|
||||
|
||||
_sawmill.Debug($"adapter device: {GetString(adapterProps.device)}");
|
||||
_sawmill.Debug($"adapter vendor: {GetString(adapterProps.vendor)} ({adapterProps.vendorID})");
|
||||
_sawmill.Debug($"adapter description: {GetString(adapterProps.description)}");
|
||||
_sawmill.Debug($"adapter architecture: {GetString(adapterProps.architecture)}");
|
||||
_sawmill.Debug($"adapter backend: {adapterProps.backendType}");
|
||||
_sawmill.Debug($"adapter type: {adapterProps.adapterType}");
|
||||
_sawmill.Debug($"adapter UBO alignment: {adapterLimits.minUniformBufferOffsetAlignment}");
|
||||
|
||||
_adapterProperties = new RhiAdapterInfo(
|
||||
adapterProps.vendorID,
|
||||
adapterProps.deviceID,
|
||||
GetString(adapterProps.vendor) ?? "",
|
||||
GetString(adapterProps.architecture) ?? "",
|
||||
GetString(adapterProps.device) ?? "",
|
||||
GetString(adapterProps.description) ?? "",
|
||||
(RhiAdapterType) adapterProps.adapterType,
|
||||
(RhiBackendType) adapterProps.backendType
|
||||
);
|
||||
|
||||
// Default limits, from WebGPU spec.
|
||||
var requiredLimits = new WGPULimits();
|
||||
if (false)
|
||||
{
|
||||
// GLES3.0
|
||||
requiredLimits.maxComputeWorkgroupStorageSize = 16384;
|
||||
requiredLimits.maxComputeInvocationsPerWorkgroup = 256;
|
||||
requiredLimits.maxComputeWorkgroupSizeX = 256;
|
||||
requiredLimits.maxComputeWorkgroupSizeY = 256;
|
||||
requiredLimits.maxComputeWorkgroupSizeZ = 256;
|
||||
requiredLimits.maxComputeWorkgroupsPerDimension = 65536;
|
||||
requiredLimits.maxDynamicStorageBuffersPerPipelineLayout = 0;
|
||||
requiredLimits.maxStorageBuffersPerShaderStage = 4;
|
||||
requiredLimits.maxStorageBufferBindingSize = 134217728;
|
||||
}
|
||||
|
||||
// Required minimums
|
||||
requiredLimits.minStorageBufferOffsetAlignment = 256;
|
||||
requiredLimits.minUniformBufferOffsetAlignment = 256;
|
||||
|
||||
requiredLimits.maxTextureDimension1D = 8192;
|
||||
requiredLimits.maxTextureDimension2D = 8192;
|
||||
requiredLimits.maxTextureDimension3D = 2048;
|
||||
requiredLimits.maxTextureArrayLayers = 256;
|
||||
requiredLimits.maxBindGroups = 4;
|
||||
requiredLimits.maxBindingsPerBindGroup = 1000;
|
||||
requiredLimits.maxDynamicUniformBuffersPerPipelineLayout = 8;
|
||||
requiredLimits.maxSampledTexturesPerShaderStage = 16;
|
||||
requiredLimits.maxSamplersPerShaderStage = 16;
|
||||
requiredLimits.maxUniformBuffersPerShaderStage = 12;
|
||||
requiredLimits.maxUniformBufferBindingSize = 65536;
|
||||
requiredLimits.maxVertexBuffers = 8;
|
||||
requiredLimits.maxVertexAttributes = 16;
|
||||
requiredLimits.maxVertexBufferArrayStride = 2048;
|
||||
requiredLimits.maxInterStageShaderVariables = 16;
|
||||
requiredLimits.maxColorAttachments = 8;
|
||||
requiredLimits.maxColorAttachmentBytesPerSample = 32;
|
||||
requiredLimits.maxBufferSize = 268435456;
|
||||
|
||||
// Custom limits
|
||||
// Take as low UBO alignment as we can get.
|
||||
requiredLimits.minUniformBufferOffsetAlignment = adapterLimits.minUniformBufferOffsetAlignment;
|
||||
|
||||
// TODO: clear this.
|
||||
var errorGCHandle = GCHandle.Alloc(this);
|
||||
|
||||
var deviceDesc = new WGPUDeviceDescriptor();
|
||||
deviceDesc.requiredLimits = &requiredLimits;
|
||||
deviceDesc.uncapturedErrorCallbackInfo = new WGPUUncapturedErrorCallbackInfo
|
||||
{
|
||||
callback = &UncapturedErrorCallback,
|
||||
userdata1 = (void*)GCHandle.ToIntPtr(errorGCHandle),
|
||||
};
|
||||
WgpuRequestDeviceResult deviceResult;
|
||||
wgpuAdapterRequestDevice(
|
||||
_wgpuAdapter,
|
||||
&deviceDesc,
|
||||
new WGPURequestDeviceCallbackInfo
|
||||
{
|
||||
callback = &WgpuRequestDeviceCallback,
|
||||
userdata1 = &deviceResult
|
||||
});
|
||||
|
||||
if (deviceResult.Status != WGPURequestDeviceStatus.WGPURequestDeviceStatus_Success)
|
||||
throw new Exception($"Device request failed: {deviceResult.Message}");
|
||||
|
||||
_sawmill.Debug("WebGPU device created!");
|
||||
|
||||
_wgpuDevice = deviceResult.Device;
|
||||
_wgpuQueue = wgpuDeviceGetQueue(_wgpuDevice);
|
||||
|
||||
_deviceLimits = new RhiLimits(
|
||||
requiredLimits.maxTextureDimension1D,
|
||||
requiredLimits.maxTextureDimension2D,
|
||||
requiredLimits.maxTextureDimension3D,
|
||||
requiredLimits.maxTextureArrayLayers,
|
||||
requiredLimits.maxBindGroups,
|
||||
requiredLimits.maxBindingsPerBindGroup,
|
||||
requiredLimits.maxDynamicUniformBuffersPerPipelineLayout,
|
||||
requiredLimits.maxDynamicStorageBuffersPerPipelineLayout,
|
||||
requiredLimits.maxSampledTexturesPerShaderStage,
|
||||
requiredLimits.maxSamplersPerShaderStage,
|
||||
requiredLimits.maxStorageBuffersPerShaderStage,
|
||||
requiredLimits.maxStorageTexturesPerShaderStage,
|
||||
requiredLimits.maxUniformBuffersPerShaderStage,
|
||||
requiredLimits.maxUniformBufferBindingSize,
|
||||
requiredLimits.maxStorageBufferBindingSize,
|
||||
requiredLimits.minUniformBufferOffsetAlignment,
|
||||
requiredLimits.minStorageBufferOffsetAlignment,
|
||||
requiredLimits.maxVertexBuffers,
|
||||
requiredLimits.maxBufferSize,
|
||||
requiredLimits.maxVertexAttributes,
|
||||
requiredLimits.maxVertexBufferArrayStride,
|
||||
requiredLimits.maxInterStageShaderVariables,
|
||||
requiredLimits.maxColorAttachments,
|
||||
requiredLimits.maxColorAttachmentBytesPerSample,
|
||||
requiredLimits.maxComputeWorkgroupStorageSize,
|
||||
requiredLimits.maxComputeInvocationsPerWorkgroup,
|
||||
requiredLimits.maxComputeWorkgroupSizeX,
|
||||
requiredLimits.maxComputeWorkgroupSizeY,
|
||||
requiredLimits.maxComputeWorkgroupSizeZ,
|
||||
requiredLimits.maxComputeWorkgroupsPerDimension
|
||||
);
|
||||
}
|
||||
|
||||
private void InitLogging()
|
||||
{
|
||||
// TODO: clear this.
|
||||
var gcHandle = GCHandle.Alloc(this);
|
||||
|
||||
wgpuSetLogCallback(&LogCallback, (void*)GCHandle.ToIntPtr(gcHandle));
|
||||
wgpuSetLogLevel(WGPULogLevel.WGPULogLevel_Warn);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static void LogCallback(WGPULogLevel level, WGPUStringView message, void* userdata)
|
||||
{
|
||||
var self = (RhiWebGpu)GCHandle.FromIntPtr((nint)userdata).Target!;
|
||||
var messageString = GetString(message)!;
|
||||
|
||||
var robustLevel = level switch
|
||||
{
|
||||
WGPULogLevel.WGPULogLevel_Error => RLogLevel.Error,
|
||||
WGPULogLevel.WGPULogLevel_Warn => RLogLevel.Warning,
|
||||
WGPULogLevel.WGPULogLevel_Info => RLogLevel.Info,
|
||||
WGPULogLevel.WGPULogLevel_Debug => RLogLevel.Debug,
|
||||
WGPULogLevel.WGPULogLevel_Trace or _ => RLogLevel.Verbose,
|
||||
};
|
||||
|
||||
self._apiLogSawmill.Log(robustLevel, messageString);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static void UncapturedErrorCallback(WGPUDevice* device, WGPUErrorType level, WGPUStringView message, void* userdata1, void* userdata2)
|
||||
{
|
||||
var self = (RhiWebGpu)GCHandle.FromIntPtr((nint)userdata1).Target!;
|
||||
var messageString = GetString(message);
|
||||
|
||||
self._apiLogSawmill.Error(messageString ?? "Unknown error");
|
||||
}
|
||||
|
||||
internal override void Shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
private record struct WgpuRequestAdapterResult(WGPURequestAdapterStatus Status, Ptr<WGPUAdapterImpl> Adapter, string Message);
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static void WgpuRequestAdapterCallback(
|
||||
WGPURequestAdapterStatus status,
|
||||
WGPUAdapter adapter,
|
||||
WGPUStringView message,
|
||||
void* userdata1,
|
||||
void* userdata2)
|
||||
{
|
||||
*(WgpuRequestAdapterResult*)userdata1 = new WgpuRequestAdapterResult(
|
||||
status,
|
||||
adapter,
|
||||
GetString(message) ?? "");
|
||||
}
|
||||
|
||||
private record struct WgpuRequestDeviceResult(WGPURequestDeviceStatus Status, Ptr<WGPUDeviceImpl> Device, string Message);
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static void WgpuRequestDeviceCallback(
|
||||
WGPURequestDeviceStatus status,
|
||||
WGPUDevice device,
|
||||
WGPUStringView message,
|
||||
void* userdata1,
|
||||
void* userdata2)
|
||||
{
|
||||
*(WgpuRequestDeviceResult*)userdata1 = new WgpuRequestDeviceResult(
|
||||
status,
|
||||
device,
|
||||
GetString(message) ?? "");
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Robust.Client")]
|
||||
[assembly: InternalsVisibleTo("Robust.Client.Graphics.Rhi")]
|
||||
[module: SkipLocalsInit]
|
||||
|
||||
10
Robust.Client.Interop.RobustNative/Webgpu/WGPUStringView.cs
Normal file
10
Robust.Client.Interop.RobustNative/Webgpu/WGPUStringView.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Robust.Client.Interop.RobustNative.Webgpu;
|
||||
|
||||
internal partial struct WGPUStringView
|
||||
{
|
||||
public static readonly WGPUStringView Null = new WGPUStringView
|
||||
{
|
||||
data = null,
|
||||
length = Wgpu.WGPU_STRLEN,
|
||||
};
|
||||
}
|
||||
7
Robust.Client.Interop.RobustNative/Webgpu/Wgpu.cs
Normal file
7
Robust.Client.Interop.RobustNative/Webgpu/Wgpu.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Robust.Client.Interop.RobustNative.Webgpu;
|
||||
|
||||
internal static partial class Wgpu
|
||||
{
|
||||
public static readonly nuint WGPU_STRLEN = nuint.MaxValue;
|
||||
public const ulong WGPU_WHOLE_SIZE = ulong.MaxValue;
|
||||
}
|
||||
@@ -4,7 +4,6 @@ using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics.Clyde.Rhi;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared;
|
||||
|
||||
@@ -2,7 +2,7 @@ using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics.Clyde.Rhi;
|
||||
using Robust.Client.Graphics.Rhi;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Vector2 = Robust.Shared.Maths.Vector2;
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Client.Graphics.Clyde.Rhi;
|
||||
using Robust.Client.Graphics.Rhi;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
@@ -108,7 +108,7 @@ namespace Robust.Client.Graphics.Clyde
|
||||
TextureHandle = textureObject.TextureId,
|
||||
ColorFormat = format.ColorFormat,
|
||||
DepthStencilTexture = depthStencilTexture,
|
||||
DepthSencilTextureView = depthStencilTextureView
|
||||
DepthSencilTextureView = depthStencilTextureView,
|
||||
MemoryPressure = pressure,
|
||||
SampleParameters = sampleParameters,
|
||||
Instance = new WeakReference<RenderTargetBase>(renderTarget),
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using Robust.Client.Graphics.Clyde.Rhi;
|
||||
using Robust.Client.Graphics.Rhi;
|
||||
using Robust.Client.Graphics.Rhi.WebGpu;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Utility;
|
||||
using RhiBase = Robust.Client.Graphics.Rhi.RhiBase;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde;
|
||||
|
||||
@@ -19,11 +21,15 @@ internal sealed partial class Clyde
|
||||
|
||||
Rhi = graphicsApi switch
|
||||
{
|
||||
"webGpu" => new RhiWebGpu(this, _deps),
|
||||
"webGpu" => new RhiWebGpu(_deps),
|
||||
_ => throw new Exception($"Unknown RHI: {graphicsApi}")
|
||||
};
|
||||
|
||||
Rhi.Init();
|
||||
Rhi.Init(new RhiBase.RhiInitParams
|
||||
{
|
||||
Backends = _cfg.GetCVar(CVars.DisplayWgpuBackends),
|
||||
PowerPreference = (RhiPowerPreference)_cfg.GetCVar(CVars.DisplayGpuPowerPreference)
|
||||
});
|
||||
}
|
||||
|
||||
private void AcquireSwapchainTextures()
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Robust.Client.Graphics.Clyde.Rhi;
|
||||
using Robust.Client.Graphics.Rhi;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using RhiBase = Robust.Client.Graphics.Rhi.RhiBase;
|
||||
using RVector2 = Robust.Shared.Maths.Vector2;
|
||||
using RVector4 = Robust.Shared.Maths.Vector4;
|
||||
using SVector2 = System.Numerics.Vector2;
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Data;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using Robust.Client.Graphics.Clyde.Rhi;
|
||||
using Robust.Client.Graphics.Rhi;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
@@ -5,7 +5,8 @@ using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Client.Graphics.Clyde.Rhi;
|
||||
using Robust.Client.Graphics.Rhi;
|
||||
using Robust.Client.Graphics.Rhi.WebGpu;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared;
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.Graphics.Clyde.Rhi;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
@@ -18,6 +17,7 @@ using Robust.Shared.Timing;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using Color = Robust.Shared.Maths.Color;
|
||||
using RhiBase = Robust.Client.Graphics.Rhi.RhiBase;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde
|
||||
{
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Client.Graphics.Clyde.Rhi;
|
||||
using Robust.Client.Graphics.Rhi;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Exceptions;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Utility;
|
||||
using RhiBase = Robust.Client.Graphics.Rhi.RhiBase;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde;
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
using Silk.NET.WebGPU;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde.Rhi;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
// wgpu-native and Dawn can't agree on how resources should be dropped
|
||||
// Dawn wants reference counting, wgpu doesn't.
|
||||
// This will (in the future) abstract the two.
|
||||
|
||||
private void WgpuDropTextureView(TextureView* tv) => _wgpu.TextureViewDrop(tv);
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using Silk.NET.WebGPU;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde.Rhi;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
private readonly Dictionary<RhiHandle, RenderPipelineReg> _renderPipelineRegistry = new();
|
||||
private readonly Dictionary<RhiHandle, PipelineLayoutReg> _pipelineLayoutRegistry = new();
|
||||
|
||||
public override RhiPipelineLayout CreatePipelineLayout(in RhiPipelineLayoutDescriptor descriptor)
|
||||
{
|
||||
// TODO: SAFETY
|
||||
|
||||
Span<byte> buffer = stackalloc byte[128];
|
||||
var pDescriptor = BumpAllocate<PipelineLayoutDescriptor>(ref buffer);
|
||||
pDescriptor->Label = BumpAllocateUtf8(ref buffer, descriptor.Label);
|
||||
|
||||
var layouts = descriptor.BindGroupLayouts;
|
||||
pDescriptor->BindGroupLayoutCount = (uint) layouts.Length;
|
||||
pDescriptor->BindGroupLayouts = BumpAllocatePtr<BindGroupLayout>(ref buffer, layouts.Length);
|
||||
for (var i = 0; i < layouts.Length; i++)
|
||||
{
|
||||
pDescriptor->BindGroupLayouts[i] = _bindGroupLayoutRegistry[layouts[i].Handle].Native;
|
||||
}
|
||||
|
||||
var native = _webGpu.DeviceCreatePipelineLayout(_wgpuDevice, pDescriptor);
|
||||
|
||||
// TODO: Thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
_pipelineLayoutRegistry.Add(handle, new PipelineLayoutReg { Native = native });
|
||||
return new RhiPipelineLayout(this, handle);
|
||||
}
|
||||
|
||||
public override RhiRenderPipeline CreateRenderPipeline(in RhiRenderPipelineDescriptor descriptor)
|
||||
{
|
||||
// TODO: THREAD SAFETY
|
||||
// TODO: INPUT VALIDATION
|
||||
|
||||
var vertexShader = _shaderModuleRegistry[descriptor.Vertex.ProgrammableStage.ShaderModule.Handle].Native;
|
||||
|
||||
const int bufferSize = 8192;
|
||||
var bufferPtr = NativeMemory.AlignedAlloc(bufferSize, 8);
|
||||
|
||||
RenderPipeline* nativePipeline;
|
||||
try
|
||||
{
|
||||
var buffer = new Span<byte>(bufferPtr, bufferSize);
|
||||
|
||||
RenderPipelineDescriptor pipelineDesc = default;
|
||||
pipelineDesc.Label = BumpAllocateUtf8(ref buffer, descriptor.Label);
|
||||
|
||||
// Pipeline layout
|
||||
switch (descriptor.Layout)
|
||||
{
|
||||
case RhiPipelineLayout pipelineLayout:
|
||||
pipelineDesc.Layout = _pipelineLayoutRegistry[pipelineLayout.Handle].Native;
|
||||
break;
|
||||
|
||||
case RhiAutoLayoutMode:
|
||||
throw new NotSupportedException("wgpu does not support auto layout yet");
|
||||
// Default case: no layout given, do nothing
|
||||
}
|
||||
|
||||
// Vertex state
|
||||
pipelineDesc.Vertex.Module = vertexShader;
|
||||
pipelineDesc.Vertex.EntryPoint = BumpAllocateUtf8(
|
||||
ref buffer,
|
||||
descriptor.Vertex.ProgrammableStage.EntryPoint);
|
||||
|
||||
WgpuProgrammableConstants(
|
||||
ref buffer,
|
||||
descriptor.Vertex.ProgrammableStage.Constants,
|
||||
out pipelineDesc.Vertex.ConstantCount,
|
||||
out pipelineDesc.Vertex.Constants);
|
||||
|
||||
var buffers = descriptor.Vertex.Buffers;
|
||||
pipelineDesc.Vertex.BufferCount = (uint)buffers.Length;
|
||||
pipelineDesc.Vertex.Buffers = BumpAllocate<VertexBufferLayout>(ref buffer, buffers.Length);
|
||||
for (var i = 0; i < buffers.Length; i++)
|
||||
{
|
||||
ref var bufferLayout = ref pipelineDesc.Vertex.Buffers[i];
|
||||
bufferLayout.ArrayStride = buffers[i].ArrayStride;
|
||||
bufferLayout.StepMode = (VertexStepMode)buffers[i].StepMode;
|
||||
|
||||
var attributes = buffers[i].Attributes;
|
||||
bufferLayout.AttributeCount = (uint)attributes.Length;
|
||||
bufferLayout.Attributes = BumpAllocate<VertexAttribute>(ref buffer, attributes.Length);
|
||||
for (var j = 0; j < attributes.Length; j++)
|
||||
{
|
||||
ref var attribute = ref bufferLayout.Attributes[j];
|
||||
attribute.Format = (VertexFormat)attributes[j].Format;
|
||||
attribute.Offset = attributes[j].Offset;
|
||||
attribute.ShaderLocation = attributes[j].ShaderLocation;
|
||||
}
|
||||
}
|
||||
|
||||
// Primitive state
|
||||
pipelineDesc.Primitive.Topology = (PrimitiveTopology)descriptor.Primitive.Topology;
|
||||
pipelineDesc.Primitive.StripIndexFormat = (IndexFormat)descriptor.Primitive.StripIndexformat;
|
||||
pipelineDesc.Primitive.FrontFace = (FrontFace)descriptor.Primitive.FrontFace;
|
||||
pipelineDesc.Primitive.CullMode = (CullMode)descriptor.Primitive.CullMode;
|
||||
|
||||
var pPrimitiveDepthClipControl = BumpAllocate<PrimitiveDepthClipControl>(ref buffer);
|
||||
pPrimitiveDepthClipControl->Chain.SType = SType.PrimitiveDepthClipControl;
|
||||
pipelineDesc.Primitive.NextInChain = &pPrimitiveDepthClipControl->Chain;
|
||||
|
||||
pPrimitiveDepthClipControl->UnclippedDepth = descriptor.Primitive.UnclippedDepth;
|
||||
|
||||
// Depth stencil state
|
||||
if (descriptor.DepthStencil is { } depthStencil)
|
||||
{
|
||||
var pDepthStencil = BumpAllocate<DepthStencilState>(ref buffer);
|
||||
pipelineDesc.DepthStencil = pDepthStencil;
|
||||
|
||||
pDepthStencil->Format = (TextureFormat)depthStencil.Format;
|
||||
pDepthStencil->DepthWriteEnabled = depthStencil.DepthWriteEnabled;
|
||||
pDepthStencil->DepthCompare = (CompareFunction)depthStencil.DepthCompare;
|
||||
pDepthStencil->StencilFront = WgpuStencilFaceState(depthStencil.StencilFront ?? new RhiStencilFaceState());
|
||||
pDepthStencil->StencilBack = WgpuStencilFaceState(depthStencil.StencilBack ?? new RhiStencilFaceState());
|
||||
pDepthStencil->StencilReadMask = depthStencil.StencilReadMask;
|
||||
pDepthStencil->StencilWriteMask = depthStencil.StencilWriteMask;
|
||||
pDepthStencil->DepthBias = depthStencil.DepthBias;
|
||||
pDepthStencil->DepthBiasSlopeScale = depthStencil.DepthBiasSlopeScale;
|
||||
pDepthStencil->DepthBiasClamp = depthStencil.DepthBiasClamp;
|
||||
}
|
||||
|
||||
// Multisample state
|
||||
pipelineDesc.Multisample.Count = descriptor.Multisample.Count;
|
||||
pipelineDesc.Multisample.Mask = descriptor.Multisample.Mask;
|
||||
pipelineDesc.Multisample.AlphaToCoverageEnabled = descriptor.Multisample.AlphaToCoverageEnabled;
|
||||
|
||||
// Fragment state
|
||||
if (descriptor.Fragment is { } fragment)
|
||||
{
|
||||
var fragmentShader = _shaderModuleRegistry[fragment.ProgrammableStage.ShaderModule.Handle].Native;
|
||||
|
||||
var pFragment = BumpAllocate<FragmentState>(ref buffer);
|
||||
pipelineDesc.Fragment = pFragment;
|
||||
|
||||
pFragment->Module = fragmentShader;
|
||||
pFragment->EntryPoint = BumpAllocateUtf8(ref buffer, fragment.ProgrammableStage.EntryPoint);
|
||||
|
||||
WgpuProgrammableConstants(
|
||||
ref buffer,
|
||||
fragment.ProgrammableStage.Constants,
|
||||
out pFragment->ConstantCount,
|
||||
out pFragment->Constants);
|
||||
|
||||
var targets = fragment.Targets;
|
||||
pFragment->TargetCount = (uint)targets.Length;
|
||||
pFragment->Targets = BumpAllocate<ColorTargetState>(ref buffer, targets.Length);
|
||||
for (var i = 0; i < targets.Length; i++)
|
||||
{
|
||||
ref var target = ref pFragment->Targets[i];
|
||||
target.Format = (TextureFormat)targets[i].Format;
|
||||
|
||||
if (targets[i].Blend is { } blend)
|
||||
{
|
||||
var pBlend = BumpAllocate<BlendState>(ref buffer);
|
||||
target.Blend = pBlend;
|
||||
|
||||
pBlend->Alpha = WgpuBlendComponent(blend.Alpha);
|
||||
pBlend->Color = WgpuBlendComponent(blend.Color);
|
||||
}
|
||||
|
||||
target.WriteMask = (ColorWriteMask)targets[i].WriteMask;
|
||||
}
|
||||
}
|
||||
|
||||
nativePipeline = _webGpu.DeviceCreateRenderPipeline(_wgpuDevice, &pipelineDesc);
|
||||
}
|
||||
finally
|
||||
{
|
||||
NativeMemory.AlignedFree(bufferPtr);
|
||||
}
|
||||
|
||||
// TODO: Thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
_renderPipelineRegistry.Add(handle, new RenderPipelineReg { Native = nativePipeline });
|
||||
return new RhiRenderPipeline(this, handle);
|
||||
}
|
||||
|
||||
private static StencilFaceState WgpuStencilFaceState(in RhiStencilFaceState state)
|
||||
{
|
||||
return new StencilFaceState
|
||||
{
|
||||
Compare = (CompareFunction)state.Compare,
|
||||
FailOp = (StencilOperation)state.FailOp,
|
||||
DepthFailOp = (StencilOperation)state.DepthFailOp,
|
||||
PassOp = (StencilOperation)state.PassOp
|
||||
};
|
||||
}
|
||||
|
||||
private static void WgpuProgrammableConstants(
|
||||
ref Span<byte> buffer,
|
||||
RhiConstantEntry[] constants,
|
||||
out uint constantCount,
|
||||
out ConstantEntry* pConstants)
|
||||
{
|
||||
constantCount = (uint)constants.Length;
|
||||
pConstants = BumpAllocate<ConstantEntry>(ref buffer, constants.Length);
|
||||
for (var i = 0; i < constants.Length; i++)
|
||||
{
|
||||
ref var constant = ref pConstants[i];
|
||||
constant.Key = BumpAllocateUtf8(ref buffer, constants[i].Key);
|
||||
constant.Value = constants[i].Value;
|
||||
}
|
||||
}
|
||||
|
||||
private static BlendComponent WgpuBlendComponent(in RhiBlendComponent component)
|
||||
{
|
||||
return new BlendComponent
|
||||
{
|
||||
Operation = (BlendOperation)component.Operation,
|
||||
DstFactor = (BlendFactor)component.DstFactor,
|
||||
SrcFactor = (BlendFactor)component.SrcFactor,
|
||||
};
|
||||
}
|
||||
|
||||
private sealed class RenderPipelineReg
|
||||
{
|
||||
public RenderPipeline* Native;
|
||||
}
|
||||
|
||||
private sealed class PipelineLayoutReg
|
||||
{
|
||||
public PipelineLayout* Native;
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Silk.NET.WebGPU;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde.Rhi;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
private readonly Dictionary<RhiHandle, ShaderModuleReg> _shaderModuleRegistry = new();
|
||||
|
||||
public override RhiShaderModule CreateShaderModule(in RhiShaderModuleDescriptor descriptor)
|
||||
{
|
||||
var codeBytes = Encoding.UTF8.GetBytes(descriptor.Code);
|
||||
|
||||
ShaderModule* shaderModule;
|
||||
fixed (byte* pCode = codeBytes)
|
||||
fixed (byte* pLabel = MakeLabel(descriptor.Label))
|
||||
{
|
||||
var descWgsl = new ShaderModuleWGSLDescriptor();
|
||||
descWgsl.Code = pCode;
|
||||
descWgsl.Chain.SType = SType.ShaderModuleWgsldescriptor;
|
||||
|
||||
var desc = new ShaderModuleDescriptor();
|
||||
desc.Label = pLabel;
|
||||
desc.NextInChain = (ChainedStruct*) (&descWgsl);
|
||||
|
||||
shaderModule = _webGpu.DeviceCreateShaderModule(_wgpuDevice, &desc);
|
||||
}
|
||||
|
||||
// TODO: Thread safety
|
||||
var handle = AllocRhiHandle();
|
||||
_shaderModuleRegistry.Add(handle, new ShaderModuleReg { Native = shaderModule });
|
||||
return new RhiShaderModule(this, handle);
|
||||
}
|
||||
|
||||
private sealed class ShaderModuleReg
|
||||
{
|
||||
public ShaderModule* Native;
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
using System;
|
||||
using Robust.Shared.Utility;
|
||||
using Silk.NET.WebGPU;
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde.Rhi;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu
|
||||
{
|
||||
public sealed class WindowData
|
||||
{
|
||||
public Surface* Surface;
|
||||
public SwapChain* SwapChain;
|
||||
}
|
||||
|
||||
private void CreateSurfaceForWindow(Clyde.WindowReg window)
|
||||
{
|
||||
DebugTools.Assert(_clyde._windowing != null);
|
||||
|
||||
SurfaceDescriptor surfaceDesc = default;
|
||||
SurfaceDescriptorFromWindowsHWND surfaceDescHwnd;
|
||||
SurfaceDescriptorFromXlibWindow surfaceDescX11;
|
||||
SurfaceDescriptorFromMetalLayer surfaceDescMetal;
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
var hInstance = _clyde._windowing.WindowGetWin32Instance(window);
|
||||
var hWnd = _clyde._windowing.WindowGetWin32Window(window);
|
||||
|
||||
surfaceDescHwnd = new SurfaceDescriptorFromWindowsHWND
|
||||
{
|
||||
Chain =
|
||||
{
|
||||
SType = SType.SurfaceDescriptorFromWindowsHwnd
|
||||
},
|
||||
Hinstance = hInstance,
|
||||
Hwnd = hWnd
|
||||
};
|
||||
|
||||
surfaceDesc.NextInChain = (ChainedStruct*) (&surfaceDescHwnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
var xDisplay = _clyde._windowing.WindowGetX11Display(window);
|
||||
var xWindow = _clyde._windowing.WindowGetX11Id(window);
|
||||
|
||||
if (xDisplay != null && xWindow != null)
|
||||
{
|
||||
surfaceDescX11 = new SurfaceDescriptorFromXlibWindow
|
||||
{
|
||||
Chain =
|
||||
{
|
||||
SType = SType.SurfaceDescriptorFromXlibWindow
|
||||
},
|
||||
Display = ((IntPtr) xDisplay.Value).ToPointer(),
|
||||
Window = xWindow.Value
|
||||
};
|
||||
|
||||
surfaceDesc.NextInChain = (ChainedStruct*) (&surfaceDescX11);
|
||||
}
|
||||
else
|
||||
{
|
||||
var metalLayer = _clyde._windowing.WindowGetMetalLayer(window);
|
||||
|
||||
if (metalLayer != null)
|
||||
{
|
||||
surfaceDescMetal = new SurfaceDescriptorFromMetalLayer
|
||||
{
|
||||
Chain =
|
||||
{
|
||||
SType = SType.SurfaceDescriptorFromMetalLayer
|
||||
},
|
||||
Layer = ((IntPtr) metalLayer.Value).ToPointer()
|
||||
};
|
||||
|
||||
surfaceDesc.NextInChain = (ChainedStruct*) (&surfaceDescMetal);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var surface = _webGpu.InstanceCreateSurface(_wgpuInstance, &surfaceDesc);
|
||||
window.RhiWebGpuData = new WindowData
|
||||
{
|
||||
Surface = surface
|
||||
};
|
||||
}
|
||||
|
||||
private void CreateSwapChainForWindow(Clyde.WindowReg window)
|
||||
{
|
||||
// TODO: Safety
|
||||
var rhiData = window.RhiWebGpuData!;
|
||||
|
||||
var format = TextureFormat.Bgra8UnormSrgb;
|
||||
_sawmill.Debug($"Preferred surface format is {format}");
|
||||
|
||||
var swapChainDesc = new SwapChainDescriptor
|
||||
{
|
||||
Format = format,
|
||||
Height = (uint)window.FramebufferSize.Y,
|
||||
Width = (uint)window.FramebufferSize.X,
|
||||
Usage = TextureUsage.RenderAttachment,
|
||||
PresentMode = PresentMode.Fifo
|
||||
};
|
||||
|
||||
var swapChain = _webGpu.DeviceCreateSwapChain(_wgpuDevice, rhiData.Surface, &swapChainDesc);
|
||||
rhiData.SwapChain = swapChain;
|
||||
|
||||
_sawmill.Debug("WebGPU Surface created!");
|
||||
}
|
||||
|
||||
internal override void WindowCreated(Clyde.WindowReg reg)
|
||||
{
|
||||
CreateSurfaceForWindow(reg);
|
||||
CreateSwapChainForWindow(reg);
|
||||
}
|
||||
|
||||
internal override void WindowDestroy(Clyde.WindowReg reg)
|
||||
{
|
||||
var rhiData = reg.RhiWebGpuData!;
|
||||
|
||||
_wgpu.SwapChainDrop(rhiData.SwapChain);
|
||||
_wgpu.SurfaceDrop(rhiData.Surface);
|
||||
|
||||
reg.RhiWebGpuData = null;
|
||||
}
|
||||
|
||||
internal override void WindowRecreateSwapchain(Clyde.WindowReg reg)
|
||||
{
|
||||
var rhiData = reg.RhiWebGpuData!;
|
||||
|
||||
_wgpu.SwapChainDrop(rhiData.SwapChain);
|
||||
|
||||
CreateSwapChainForWindow(reg);
|
||||
}
|
||||
|
||||
internal override void WindowPresent(Clyde.WindowReg reg)
|
||||
{
|
||||
// TODO: Safety
|
||||
var rhiData = reg.RhiWebGpuData!;
|
||||
|
||||
_webGpu.SwapChainPresent(rhiData.SwapChain);
|
||||
}
|
||||
}
|
||||
@@ -1,356 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using Silk.NET.WebGPU;
|
||||
using Silk.NET.WebGPU.Extensions.WGPU;
|
||||
using LogLevel = Silk.NET.WebGPU.Extensions.WGPU.LogLevel;
|
||||
using RLogLevel = Robust.Shared.Log.LogLevel;
|
||||
using RColor = Robust.Shared.Maths.Color;
|
||||
|
||||
#pragma warning disable CS8500
|
||||
|
||||
namespace Robust.Client.Graphics.Clyde.Rhi;
|
||||
|
||||
internal sealed unsafe partial class RhiWebGpu : RhiBase
|
||||
{
|
||||
private readonly Clyde _clyde;
|
||||
private readonly IConfigurationManager _cfg;
|
||||
|
||||
private readonly ISawmill _sawmill;
|
||||
private readonly ISawmill _apiLogSawmill;
|
||||
|
||||
private WebGPU _webGpu = default!;
|
||||
private Wgpu _wgpu = default!;
|
||||
private Instance* _wgpuInstance;
|
||||
private Adapter* _wgpuAdapter;
|
||||
private Device* _wgpuDevice;
|
||||
private Queue* _wgpuQueue;
|
||||
|
||||
private RhiLimits? _deviceLimits;
|
||||
private RhiAdapterProperties? _adapterProperties;
|
||||
private string _description = "not initialized";
|
||||
|
||||
public override RhiLimits DeviceLimits =>
|
||||
_deviceLimits ?? throw new InvalidOperationException("Not initialized yet");
|
||||
|
||||
public override RhiAdapterProperties AdapterProperties =>
|
||||
_adapterProperties ?? throw new InvalidOperationException("Not initialized yet");
|
||||
|
||||
public override string Description => _description;
|
||||
|
||||
public RhiWebGpu(Clyde clyde, IDependencyCollection dependencies)
|
||||
{
|
||||
var logMgr = dependencies.Resolve<ILogManager>();
|
||||
_cfg = dependencies.Resolve<IConfigurationManager>();
|
||||
|
||||
_clyde = clyde;
|
||||
_sawmill = logMgr.GetSawmill("clyde.rhi.webGpu");
|
||||
_apiLogSawmill = logMgr.GetSawmill("clyde.rhi.webGpu.apiLog");
|
||||
|
||||
Queue = new RhiQueue(this, default);
|
||||
}
|
||||
|
||||
internal override void Init()
|
||||
{
|
||||
_sawmill.Info("Initializing WebGPU RHI!");
|
||||
|
||||
InitInstance();
|
||||
|
||||
CreateSurfaceForWindow(_clyde._mainWindow!);
|
||||
|
||||
_sawmill.Debug("WebGPU main surface created!");
|
||||
|
||||
InitAdapterAndDevice(_clyde._mainWindow!.RhiWebGpuData!.Surface);
|
||||
|
||||
CreateSwapChainForWindow(_clyde._mainWindow!);
|
||||
}
|
||||
|
||||
private void InitInstance()
|
||||
{
|
||||
var context = WebGPU.CreateDefaultContext(new[]
|
||||
{ "wgpu_native.dll", "libwgpu_native.so", "libwgpu_native.dylib" });
|
||||
_webGpu = new WebGPU(context);
|
||||
_wgpu = new Wgpu(context);
|
||||
|
||||
var wgpuVersion = WgpuVersionToString(_wgpu.GetVersion());
|
||||
_sawmill.Debug($"wgpu-native loaded, version: {wgpuVersion}");
|
||||
|
||||
_description = $"WebGPU (wgpu-native {wgpuVersion})";
|
||||
|
||||
InitLogging();
|
||||
|
||||
Span<byte> buffer = stackalloc byte[128];
|
||||
var pInstanceDescriptor = BumpAllocate<InstanceDescriptor>(ref buffer);
|
||||
|
||||
// Specify instance extras for wgpu-native.
|
||||
var pInstanceExtras = BumpAllocate<InstanceExtras>(ref buffer);
|
||||
pInstanceDescriptor->NextInChain = (ChainedStruct*)pInstanceExtras;
|
||||
pInstanceExtras->Chain.SType = (SType)NativeSType.STypeInstanceExtras;
|
||||
pInstanceExtras->Backends = (uint)GetInstanceBackendCfg();
|
||||
|
||||
_wgpuInstance = _webGpu.CreateInstance(pInstanceDescriptor);
|
||||
|
||||
_sawmill.Debug("WebGPU instance created!");
|
||||
}
|
||||
|
||||
private InstanceBackend GetInstanceBackendCfg()
|
||||
{
|
||||
var configured = _cfg.GetCVar(CVars.DisplayWgpuBackends);
|
||||
if (configured == "all")
|
||||
return InstanceBackend.Primary | InstanceBackend.Secondary;
|
||||
|
||||
var backends = InstanceBackend.None;
|
||||
foreach (var opt in configured.Split(","))
|
||||
{
|
||||
backends |= opt switch
|
||||
{
|
||||
"vulkan" => InstanceBackend.Vulkan,
|
||||
"gl" => InstanceBackend.GL,
|
||||
"metal" => InstanceBackend.Metal,
|
||||
"dx12" => InstanceBackend.DX12,
|
||||
"dx11" => InstanceBackend.DX11,
|
||||
"browser" => InstanceBackend.BrowserWebGpu,
|
||||
_ => throw new ArgumentException($"Unknown wgpu backend: '{opt}'")
|
||||
};
|
||||
}
|
||||
|
||||
return backends;
|
||||
}
|
||||
|
||||
private void InitAdapterAndDevice(Surface* forSurface)
|
||||
{
|
||||
var powerPreference = ValidatePowerPreference(
|
||||
(RhiPowerPreference)_cfg.GetCVar(CVars.DisplayGpuPowerPreference)
|
||||
);
|
||||
|
||||
var requestAdapterOptions = new RequestAdapterOptions
|
||||
{
|
||||
CompatibleSurface = forSurface,
|
||||
PowerPreference = powerPreference
|
||||
};
|
||||
|
||||
WgpuRequestAdapterResult result;
|
||||
_webGpu.InstanceRequestAdapter(
|
||||
_wgpuInstance,
|
||||
&requestAdapterOptions,
|
||||
new PfnRequestAdapterCallback(&WgpuRequestAdapterCallback),
|
||||
&result);
|
||||
|
||||
if (result.Status != RequestAdapterStatus.Success)
|
||||
throw new RhiException($"Adapter request failed: {result.Message}");
|
||||
|
||||
_sawmill.Debug("WebGPU adapter created!");
|
||||
|
||||
_wgpuAdapter = result.Adapter.P;
|
||||
|
||||
AdapterProperties adapterProps = default;
|
||||
_webGpu.AdapterGetProperties(_wgpuAdapter, &adapterProps);
|
||||
|
||||
SupportedLimits adapterLimits = default;
|
||||
_webGpu.AdapterGetLimits(_wgpuAdapter, &adapterLimits);
|
||||
|
||||
_sawmill.Debug($"adapter name: {MarshalFromString(adapterProps.Name)}");
|
||||
_sawmill.Debug($"adapter vendor: {MarshalFromString(adapterProps.VendorName)} ({adapterProps.VendorID})");
|
||||
_sawmill.Debug($"adapter driver: {MarshalFromString(adapterProps.DriverDescription)}");
|
||||
_sawmill.Debug($"adapter architecture: {MarshalFromString(adapterProps.Architecture)}");
|
||||
_sawmill.Debug($"adapter backend: {adapterProps.BackendType}");
|
||||
_sawmill.Debug($"adapter type: {adapterProps.AdapterType}");
|
||||
_sawmill.Debug($"adapter UBO alignment: {adapterLimits.Limits.MinUniformBufferOffsetAlignment}");
|
||||
|
||||
_adapterProperties = new RhiAdapterProperties(
|
||||
adapterProps.VendorID,
|
||||
MarshalFromString(adapterProps.VendorName),
|
||||
MarshalFromString(adapterProps.Architecture),
|
||||
MarshalFromString(adapterProps.Name),
|
||||
MarshalFromString(adapterProps.DriverDescription),
|
||||
(RhiAdapterType) adapterProps.AdapterType,
|
||||
(RhiBackendType) adapterProps.BackendType
|
||||
);
|
||||
|
||||
// Default limits, from WebGPU spec.
|
||||
var requiredLimits = new RequiredLimits();
|
||||
if (false)
|
||||
{
|
||||
// GLES3.0
|
||||
requiredLimits.Limits.MaxComputeWorkgroupStorageSize = 16384;
|
||||
requiredLimits.Limits.MaxComputeInvocationsPerWorkgroup = 256;
|
||||
requiredLimits.Limits.MaxComputeWorkgroupSizeX = 256;
|
||||
requiredLimits.Limits.MaxComputeWorkgroupSizeY = 256;
|
||||
requiredLimits.Limits.MaxComputeWorkgroupSizeZ = 256;
|
||||
requiredLimits.Limits.MaxComputeWorkgroupsPerDimension = 65536;
|
||||
requiredLimits.Limits.MaxDynamicStorageBuffersPerPipelineLayout = 0;
|
||||
requiredLimits.Limits.MaxStorageBuffersPerShaderStage = 4;
|
||||
requiredLimits.Limits.MaxStorageBufferBindingSize = 134217728;
|
||||
}
|
||||
|
||||
// Required minimums
|
||||
requiredLimits.Limits.MinStorageBufferOffsetAlignment = 256;
|
||||
requiredLimits.Limits.MinUniformBufferOffsetAlignment = 256;
|
||||
|
||||
requiredLimits.Limits.MaxTextureDimension1D = 8192;
|
||||
requiredLimits.Limits.MaxTextureDimension2D = 8192;
|
||||
requiredLimits.Limits.MaxTextureDimension3D = 2048;
|
||||
requiredLimits.Limits.MaxTextureArrayLayers = 256;
|
||||
requiredLimits.Limits.MaxBindGroups = 4;
|
||||
requiredLimits.Limits.MaxBindingsPerBindGroup = 1000;
|
||||
requiredLimits.Limits.MaxDynamicUniformBuffersPerPipelineLayout = 8;
|
||||
requiredLimits.Limits.MaxSampledTexturesPerShaderStage = 16;
|
||||
requiredLimits.Limits.MaxSamplersPerShaderStage = 16;
|
||||
requiredLimits.Limits.MaxUniformBuffersPerShaderStage = 12;
|
||||
requiredLimits.Limits.MaxUniformBufferBindingSize = 65536;
|
||||
requiredLimits.Limits.MaxVertexBuffers = 8;
|
||||
requiredLimits.Limits.MaxVertexAttributes = 16;
|
||||
requiredLimits.Limits.MaxVertexBufferArrayStride = 2048;
|
||||
requiredLimits.Limits.MaxInterStageShaderComponents = 60;
|
||||
requiredLimits.Limits.MaxInterStageShaderVariables = 16;
|
||||
requiredLimits.Limits.MaxColorAttachments = 8;
|
||||
requiredLimits.Limits.MaxColorAttachmentBytesPerSample = 32;
|
||||
requiredLimits.Limits.MaxBufferSize = 268435456;
|
||||
|
||||
// Custom limits
|
||||
// Take as low UBO alignment as we can get.
|
||||
requiredLimits.Limits.MinUniformBufferOffsetAlignment = adapterLimits.Limits.MinUniformBufferOffsetAlignment;
|
||||
|
||||
var deviceDesc = new DeviceDescriptor();
|
||||
deviceDesc.RequiredLimits = &requiredLimits;
|
||||
WgpuRequestDeviceResult deviceResult;
|
||||
_webGpu.AdapterRequestDevice(
|
||||
_wgpuAdapter,
|
||||
&deviceDesc,
|
||||
new PfnRequestDeviceCallback(&WgpuRequestDeviceCallback),
|
||||
&deviceResult);
|
||||
|
||||
if (deviceResult.Status != RequestDeviceStatus.Success)
|
||||
throw new Exception($"Device request failed: {deviceResult.Message}");
|
||||
|
||||
_sawmill.Debug("WebGPU device created!");
|
||||
|
||||
_wgpuDevice = deviceResult.Device;
|
||||
_wgpuQueue = _webGpu.DeviceGetQueue(_wgpuDevice);
|
||||
|
||||
ref var limits = ref requiredLimits.Limits;
|
||||
|
||||
_deviceLimits = new RhiLimits(
|
||||
limits.MaxTextureDimension1D,
|
||||
limits.MaxTextureDimension2D,
|
||||
limits.MaxTextureDimension3D,
|
||||
limits.MaxTextureArrayLayers,
|
||||
limits.MaxBindGroups,
|
||||
limits.MaxBindingsPerBindGroup,
|
||||
limits.MaxDynamicUniformBuffersPerPipelineLayout,
|
||||
limits.MaxDynamicStorageBuffersPerPipelineLayout,
|
||||
limits.MaxSampledTexturesPerShaderStage,
|
||||
limits.MaxSamplersPerShaderStage,
|
||||
limits.MaxStorageBuffersPerShaderStage,
|
||||
limits.MaxStorageTexturesPerShaderStage,
|
||||
limits.MaxUniformBuffersPerShaderStage,
|
||||
limits.MaxUniformBufferBindingSize,
|
||||
limits.MaxStorageBufferBindingSize,
|
||||
limits.MinUniformBufferOffsetAlignment,
|
||||
limits.MinStorageBufferOffsetAlignment,
|
||||
limits.MaxVertexBuffers,
|
||||
limits.MaxBufferSize,
|
||||
limits.MaxVertexAttributes,
|
||||
limits.MaxVertexBufferArrayStride,
|
||||
limits.MaxInterStageShaderComponents,
|
||||
limits.MaxInterStageShaderVariables,
|
||||
limits.MaxColorAttachments,
|
||||
limits.MaxColorAttachmentBytesPerSample,
|
||||
limits.MaxComputeWorkgroupStorageSize,
|
||||
limits.MaxComputeInvocationsPerWorkgroup,
|
||||
limits.MaxComputeWorkgroupSizeX,
|
||||
limits.MaxComputeWorkgroupSizeY,
|
||||
limits.MaxComputeWorkgroupSizeZ,
|
||||
limits.MaxComputeWorkgroupsPerDimension
|
||||
);
|
||||
|
||||
InitErrorCallback();
|
||||
}
|
||||
|
||||
private void InitLogging()
|
||||
{
|
||||
// TODO: clear this.
|
||||
var gcHandle = GCHandle.Alloc(this);
|
||||
|
||||
_wgpu.SetLogCallback(new PfnLogCallback(&LogCallback), (void*)GCHandle.ToIntPtr(gcHandle));
|
||||
_wgpu.SetLogLevel(LogLevel.Warn);
|
||||
}
|
||||
|
||||
private void InitErrorCallback()
|
||||
{
|
||||
// TODO: clear this.
|
||||
var gcHandle = GCHandle.Alloc(this);
|
||||
|
||||
_webGpu.DeviceSetUncapturedErrorCallback(
|
||||
_wgpuDevice,
|
||||
new PfnErrorCallback(&UncapturedErrorCallback),
|
||||
(void*)GCHandle.ToIntPtr(gcHandle));
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static void LogCallback(LogLevel level, byte* message, void* userdata)
|
||||
{
|
||||
var self = (RhiWebGpu)GCHandle.FromIntPtr((nint)userdata).Target!;
|
||||
var messageString = Marshal.PtrToStringUTF8((nint)message)!;
|
||||
|
||||
var robustLevel = level switch
|
||||
{
|
||||
LogLevel.Error => RLogLevel.Error,
|
||||
LogLevel.Warn => RLogLevel.Warning,
|
||||
LogLevel.Info => RLogLevel.Info,
|
||||
LogLevel.Debug => RLogLevel.Debug,
|
||||
LogLevel.Trace or _ => RLogLevel.Verbose,
|
||||
};
|
||||
|
||||
self._apiLogSawmill.Log(robustLevel, messageString);
|
||||
}
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static void UncapturedErrorCallback(ErrorType level, byte* message, void* userdata)
|
||||
{
|
||||
var self = (RhiWebGpu)GCHandle.FromIntPtr((nint)userdata).Target!;
|
||||
var messageString = Marshal.PtrToStringUTF8((nint)message)!;
|
||||
|
||||
self._apiLogSawmill.Error(messageString);
|
||||
}
|
||||
|
||||
internal override void Shutdown()
|
||||
{
|
||||
}
|
||||
|
||||
private record struct WgpuRequestAdapterResult(RequestAdapterStatus Status, Ptr<Adapter> Adapter, string Message);
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static void WgpuRequestAdapterCallback(
|
||||
RequestAdapterStatus status,
|
||||
Adapter* adapter,
|
||||
byte* message,
|
||||
void* userdata)
|
||||
{
|
||||
*(WgpuRequestAdapterResult*)userdata = new WgpuRequestAdapterResult(
|
||||
status,
|
||||
adapter,
|
||||
MarshalFromString(message));
|
||||
}
|
||||
|
||||
private record struct WgpuRequestDeviceResult(RequestDeviceStatus Status, Ptr<Device> Device, string Message);
|
||||
|
||||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
|
||||
private static void WgpuRequestDeviceCallback(
|
||||
RequestDeviceStatus status,
|
||||
Device* device,
|
||||
byte* message,
|
||||
void* userdata)
|
||||
{
|
||||
*(WgpuRequestDeviceResult*)userdata = new WgpuRequestDeviceResult(
|
||||
status,
|
||||
device,
|
||||
MarshalFromString(message));
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,11 @@ using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Client.Graphics.Clyde.Rhi;
|
||||
using Robust.Shared.Maths;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using Color = Robust.Shared.Maths.Color;
|
||||
using RhiBase = Robust.Client.Graphics.Rhi.RhiBase;
|
||||
|
||||
namespace Robust.Client.Graphics
|
||||
{
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
<PackageReference Include="DiscordRichPresence" PrivateAssets="compile" />
|
||||
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" PrivateAssets="compile" />
|
||||
<PackageReference Include="Silk.NET.WebGPU" PrivateAssets="compile" />
|
||||
<PackageReference Include="Silk.NET.WebGPU.Extensions.WGPU" PrivateAssets="compile" />
|
||||
<PackageReference Include="SQLitePCLRaw.provider.sqlite3" Condition="'$(UseSystemSqlite)' == 'True'" PrivateAssets="compile" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Condition="'$(UseSystemSqlite)' != 'True'" PrivateAssets="compile" />
|
||||
<PackageReference Include="SpaceWizards.NFluidsynth" PrivateAssets="compile" />
|
||||
@@ -44,6 +42,7 @@
|
||||
<ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
|
||||
<ProjectReference Include="..\Lidgren.Network\Lidgren.Network.csproj" />
|
||||
<ProjectReference Include="..\Robust.Client.Interop.RobustNative\Robust.Client.Interop.RobustNative.csproj" />
|
||||
<ProjectReference Include="..\Robust.Client.Graphics.Rhi\Robust.Client.Graphics.Rhi.csproj" />
|
||||
<ProjectReference Include="..\Robust.LoaderApi\Robust.LoaderApi\Robust.LoaderApi.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj" />
|
||||
|
||||
@@ -45,9 +45,9 @@ namespace Robust.Client.UserInterface.CustomControls.DebugMonitorControls
|
||||
var stats = _clydeInternal.DebugStats;
|
||||
|
||||
_textBuilder.AppendLine($@"RHI: {rhi.Description}
|
||||
Adapter: {rhi.AdapterProperties.Name}
|
||||
Vendor: {rhi.AdapterProperties.VendorName} ({rhi.AdapterProperties.VendorID})
|
||||
Driver: {rhi.AdapterProperties.Driver}");
|
||||
Adapter: {rhi.AdapterInfo.Name}
|
||||
Vendor: {rhi.AdapterInfo.VendorName} ({rhi.AdapterInfo.VendorID})
|
||||
Driver: {rhi.AdapterInfo.Driver}");
|
||||
|
||||
_textBuilder.Append($"Windowing: {info.WindowingApi}\n");
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Graphics.Clyde.Rhi;
|
||||
using Robust.Client.Graphics.Rhi;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using RhiBase = Robust.Client.Graphics.Rhi.RhiBase;
|
||||
|
||||
namespace Robust.Client.UserInterface;
|
||||
|
||||
@@ -29,7 +30,7 @@ public sealed partial class DevWindowTabRenderer : Control
|
||||
{
|
||||
Add("RHI description", rhi.Description);
|
||||
|
||||
var adapter = rhi.AdapterProperties;
|
||||
var adapter = rhi.AdapterInfo;
|
||||
|
||||
Add("Adapter name", adapter.Name);
|
||||
Add("Adapter vendor", $"{adapter.VendorName} ({adapter.VendorID})");
|
||||
|
||||
6
Robust.Shared.Utility/AssemblyInfo.cs
Normal file
6
Robust.Shared.Utility/AssemblyInfo.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Robust.Client")]
|
||||
[assembly: InternalsVisibleTo("Robust.Client.Graphics.Rhi")]
|
||||
[assembly: InternalsVisibleTo("Robust.Server")]
|
||||
[assembly: InternalsVisibleTo("Robust.Shared")]
|
||||
@@ -1,15 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace Robust.Shared.IoC
|
||||
namespace Robust.Shared.IoC
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies that the field this is applied to is a dependency,
|
||||
/// which will be resolved by <see cref="IoCManager" /> when the containing class is instantiated.
|
||||
/// which will be resolved by <see cref="IDependencyCollection" /> when the containing class is instantiated.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The dependency is resolved as if <see cref="IoCManager.Resolve{T}()" /> were to be called,
|
||||
/// but it avoids circular references and init order issues due to internal code in the <see cref="IoCManager" />.
|
||||
/// The dependency is resolved as if <see cref="IDependencyCollection.Resolve{T}()" /> were to be called,
|
||||
/// but it avoids circular references and init order issues due to internal code in the <see cref="IDependencyCollection" />.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The dependency can be injected into read only fields without issues,
|
||||
@@ -1,16 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Frozen;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.IoC.Exceptions;
|
||||
using Robust.Shared.Utility;
|
||||
using NotNull = System.Diagnostics.CodeAnalysis.NotNullAttribute;
|
||||
|
||||
namespace Robust.Shared.IoC
|
||||
{
|
||||
@@ -267,7 +261,7 @@ namespace Robust.Shared.IoC
|
||||
_pendingResolves.Enqueue(interfaceType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void CheckRegisterInterface(Type interfaceType, Type implementationType, bool overwrite)
|
||||
{
|
||||
lock (_serviceBuildLock)
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Robust.Shared.Analyzers;
|
||||
|
||||
namespace Robust.Shared.IoC.Exceptions
|
||||
{
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Robust.Shared.Reflection;
|
||||
using Robust.Shared.Analyzers;
|
||||
|
||||
namespace Robust.Shared.IoC.Exceptions
|
||||
{
|
||||
@@ -7,8 +6,7 @@ namespace Robust.Shared.IoC.Exceptions
|
||||
/// An exception for when a type doesn't correctly implement an interface, but is still IoC or reflection accessible..
|
||||
/// Such as missing an attribute.
|
||||
/// </summary>
|
||||
/// <seealso cref="IoCManager" />
|
||||
/// <seealso cref="IReflectionManager" />
|
||||
/// <seealso cref="IDependencyCollection" />
|
||||
[Virtual]
|
||||
public class InvalidImplementationException : Exception
|
||||
{
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Robust.Shared.Analyzers;
|
||||
|
||||
namespace Robust.Shared.IoC.Exceptions
|
||||
{
|
||||
@@ -1,10 +1,9 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using Robust.Shared.Analyzers;
|
||||
|
||||
namespace Robust.Shared.IoC.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Thrown by <see cref="IoCManager.Resolve{T}()"/> if one attempts to resolve an interface that isn't registered.
|
||||
/// Thrown by <see cref="IDependencyCollection.Resolve{T}()"/> if one attempts to resolve an interface that isn't registered.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[Virtual]
|
||||
@@ -1,9 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.IoC.Exceptions;
|
||||
using Robust.Shared.Reflection;
|
||||
using NotNull = System.Diagnostics.CodeAnalysis.NotNullAttribute;
|
||||
|
||||
namespace Robust.Shared.IoC
|
||||
@@ -23,14 +20,13 @@ namespace Robust.Shared.IoC
|
||||
/// Secondly, it's very useful for unit tests as we can replace components to test things.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// To use the IoCManager, it first needs some types registered through <see cref="Register{TInterface, TImplementation}"/>.
|
||||
/// These implementations can then be fetched with <see cref="Resolve{T}"/>, or through field injection with <see cref="DependencyAttribute" />.
|
||||
/// To use the IoCManager, it first needs some types registered through <see cref="Register{TInterface, TImplementation}(bool)"/>.
|
||||
/// These implementations can then be fetched with <see cref="Resolve{T}()"/>, or through field injection with <see cref="DependencyAttribute" />.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This type is thread safe: registration and <see cref="BuildGraph"/> can run concurrently with resolves.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <seealso cref="IReflectionManager"/>
|
||||
public interface IDependencyCollection
|
||||
{
|
||||
IDependencyCollection FromParent(IDependencyCollection parentCollection);
|
||||
@@ -41,7 +37,7 @@ namespace Robust.Shared.IoC
|
||||
IEnumerable<Type> GetRegisteredTypes();
|
||||
|
||||
/// <summary>
|
||||
/// Registers an interface to an implementation, to make it accessible to <see cref="DependencyCollection.Resolve{T}"/>
|
||||
/// Registers an interface to an implementation, to make it accessible to <see cref="Resolve{T}()"/>
|
||||
/// <see cref="IDependencyCollection.BuildGraph"/> MUST be called after this method to make the new interface available.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInterface">The type that will be resolvable.</typeparam>
|
||||
@@ -59,7 +55,7 @@ namespace Robust.Shared.IoC
|
||||
where TInterface : class;
|
||||
|
||||
/// <summary>
|
||||
/// Registers an interface to an implementation, to make it accessible to <see cref="DependencyCollection.Resolve{T}"/>
|
||||
/// Registers an interface to an implementation, to make it accessible to <see cref="Resolve{T}()"/>
|
||||
/// <see cref="IDependencyCollection.BuildGraph"/> MUST be called after this method to make the new interface available.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInterface">The type that will be resolvable.</typeparam>
|
||||
@@ -104,9 +100,9 @@ namespace Robust.Shared.IoC
|
||||
|
||||
/// <summary>
|
||||
/// Registers an interface to an existing instance of an implementation,
|
||||
/// making it accessible to <see cref="IDependencyCollection.Resolve{T}"/>.
|
||||
/// Unlike <see cref="IDependencyCollection.Register{TInterface, TImplementation}"/>,
|
||||
/// <see cref="IDependencyCollection.BuildGraph"/> does not need to be called after registering an instance
|
||||
/// making it accessible to <see cref="Resolve{T}()"/>.
|
||||
/// Unlike <see cref="Register{TInterface, TImplementation}(bool)"/>,
|
||||
/// <see cref="BuildGraph"/> does not need to be called after registering an instance
|
||||
/// if deferredInject is false.
|
||||
/// </summary>
|
||||
/// <typeparam name="TInterface">The type that will be resolvable.</typeparam>
|
||||
@@ -119,9 +115,9 @@ namespace Robust.Shared.IoC
|
||||
|
||||
/// <summary>
|
||||
/// Registers an interface to an existing instance of an implementation,
|
||||
/// making it accessible to <see cref="IDependencyCollection.Resolve{T}"/>.
|
||||
/// Unlike <see cref="IDependencyCollection.Register{TInterface, TImplementation}"/>,
|
||||
/// <see cref="IDependencyCollection.BuildGraph"/> does not need to be called after registering an instance.
|
||||
/// making it accessible to <see cref="Resolve{T}()"/>.
|
||||
/// Unlike <see cref="Register{TInterface, TImplementation}(bool)"/>,
|
||||
/// <see cref="BuildGraph"/> does not need to be called after registering an instance.
|
||||
/// </summary>
|
||||
/// <param name="type">The type that will be resolvable.</param>
|
||||
/// <param name="implementation">The existing instance to use as the implementation.</param>
|
||||
@@ -4,9 +4,9 @@ namespace Robust.Shared.IoC
|
||||
/// If implemented on a type instantiated by IoC,
|
||||
/// <see cref="IPostInjectInit.PostInject" /> will be called after all dependencies have been injected.
|
||||
/// Do not assume any order in the initialization of other managers,
|
||||
/// Or the availability of things through <see cref="IoCManager.Resolve{T}()" />
|
||||
/// Or the availability of things through <see cref="IDependencyCollection.Resolve{T}()" />
|
||||
/// </summary>
|
||||
/// <seealso cref="IoCManager" />
|
||||
/// <seealso cref="IDependencyCollection" />
|
||||
/// <seealso cref="DependencyAttribute" />
|
||||
public interface IPostInjectInit
|
||||
{
|
||||
17
Robust.Shared.Utility/Robust.Shared.Utility.csproj
Normal file
17
Robust.Shared.Utility/Robust.Shared.Utility.csproj
Normal file
@@ -0,0 +1,17 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\MSBuild\Robust.Engine.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>Robust.Shared</RootNamespace>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<SkipRobustSerializationGenerator>true</SkipRobustSerializationGenerator>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All" />
|
||||
<PackageReference Include="Serilog" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\MSBuild\Robust.Properties.targets" />
|
||||
</Project>
|
||||
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Robust.Shared.Utility;
|
||||
|
||||
13
Robust.Shared.Utility/Utility/Ptr.cs
Normal file
13
Robust.Shared.Utility/Utility/Ptr.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace Robust.Shared.Utility;
|
||||
|
||||
/// <summary>
|
||||
/// Pointer-wrapper struct so pointers can be sanely stored in generics and records.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The actual type pointed to</typeparam>
|
||||
internal unsafe struct Ptr<T> where T : unmanaged
|
||||
{
|
||||
public T* P;
|
||||
|
||||
public static implicit operator T*(Ptr<T> t) => t.P;
|
||||
public static implicit operator Ptr<T>(T* ptr) => new() { P = ptr };
|
||||
}
|
||||
@@ -1,9 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Robust.Shared.Utility
|
||||
{
|
||||
@@ -119,19 +115,6 @@ namespace Robust.Shared.Utility
|
||||
}
|
||||
}
|
||||
|
||||
internal static IEnumerable<AbstractFieldInfo> GetAllPropertiesAndFields(this Type type)
|
||||
{
|
||||
foreach (var field in type.GetAllFields())
|
||||
{
|
||||
yield return new SpecificFieldInfo(field);
|
||||
}
|
||||
|
||||
foreach (var property in type.GetAllProperties())
|
||||
{
|
||||
yield return new SpecificPropertyInfo(property);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TrySelectCommonType(Type type1, Type type2, [NotNullWhen(true)] out Type? commonType)
|
||||
{
|
||||
commonType = null;
|
||||
@@ -147,40 +130,6 @@ namespace Robust.Shared.Utility
|
||||
return commonType != null;
|
||||
}
|
||||
|
||||
internal static SpecificFieldInfo? GetBackingField(this Type type, string propertyName)
|
||||
{
|
||||
foreach (var parent in type.GetClassHierarchy())
|
||||
{
|
||||
var field = parent.GetField($"<{propertyName}>k__BackingField",
|
||||
BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
if (field != null)
|
||||
{
|
||||
return new SpecificFieldInfo(field);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool HasBackingField(this Type type, string propertyName)
|
||||
{
|
||||
return type.GetBackingField(propertyName) != null;
|
||||
}
|
||||
|
||||
internal static bool TryGetBackingField(this Type type, string propertyName,
|
||||
[NotNullWhen(true)] out SpecificFieldInfo? field)
|
||||
{
|
||||
return (field = type.GetBackingField(propertyName)) != null;
|
||||
}
|
||||
|
||||
public static bool IsBackingField(this MemberInfo memberInfo)
|
||||
{
|
||||
return memberInfo.HasCustomAttribute<CompilerGeneratedAttribute>() &&
|
||||
memberInfo.Name.StartsWith("<") &&
|
||||
memberInfo.Name.EndsWith(">k__BackingField");
|
||||
}
|
||||
|
||||
public static bool HasCustomAttribute<T>(this MemberInfo memberInfo) where T : Attribute
|
||||
{
|
||||
return memberInfo.GetCustomAttribute<T>() != null;
|
||||
@@ -208,16 +157,5 @@ namespace Robust.Shared.Utility
|
||||
.GetConstructors(flags)
|
||||
.Any(m => m.GetParameters().Length == 0);
|
||||
}
|
||||
|
||||
internal static bool IsAutogeneratedRecordMember(this AbstractFieldInfo info)
|
||||
{
|
||||
if (info.DeclaringType == null)
|
||||
return false;
|
||||
|
||||
if (info.Name == "EqualityContract" && info.FieldType == typeof(Type))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@
|
||||
<ProjectReference Include="..\Lidgren.Network\Lidgren.Network.csproj" />
|
||||
<ProjectReference Include="..\NetSerializer\NetSerializer\NetSerializer.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\Robust.Shared.Utility\Robust.Shared.Utility.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Utility\TypeAbbreviations.yaml">
|
||||
|
||||
68
Robust.Shared/Utility/SerializationTypeHelpers.cs
Normal file
68
Robust.Shared/Utility/SerializationTypeHelpers.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Robust.Shared.Utility;
|
||||
|
||||
internal static class SerializationTypeHelpers
|
||||
{
|
||||
internal static IEnumerable<AbstractFieldInfo> GetAllPropertiesAndFields(this Type type)
|
||||
{
|
||||
foreach (var field in type.GetAllFields())
|
||||
{
|
||||
yield return new SpecificFieldInfo(field);
|
||||
}
|
||||
|
||||
foreach (var property in type.GetAllProperties())
|
||||
{
|
||||
yield return new SpecificPropertyInfo(property);
|
||||
}
|
||||
}
|
||||
|
||||
internal static SpecificFieldInfo? GetBackingField(this Type type, string propertyName)
|
||||
{
|
||||
foreach (var parent in type.GetClassHierarchy())
|
||||
{
|
||||
var field = parent.GetField($"<{propertyName}>k__BackingField",
|
||||
BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
|
||||
if (field != null)
|
||||
{
|
||||
return new SpecificFieldInfo(field);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool HasBackingField(this Type type, string propertyName)
|
||||
{
|
||||
return type.GetBackingField(propertyName) != null;
|
||||
}
|
||||
|
||||
internal static bool TryGetBackingField(this Type type, string propertyName,
|
||||
[NotNullWhen(true)] out SpecificFieldInfo? field)
|
||||
{
|
||||
return (field = type.GetBackingField(propertyName)) != null;
|
||||
}
|
||||
|
||||
public static bool IsBackingField(this MemberInfo memberInfo)
|
||||
{
|
||||
return memberInfo.HasCustomAttribute<CompilerGeneratedAttribute>() &&
|
||||
memberInfo.Name.StartsWith("<") &&
|
||||
memberInfo.Name.EndsWith(">k__BackingField");
|
||||
}
|
||||
|
||||
internal static bool IsAutogeneratedRecordMember(this AbstractFieldInfo info)
|
||||
{
|
||||
if (info.DeclaringType == null)
|
||||
return false;
|
||||
|
||||
if (info.Name == "EqualityContract" && info.FieldType == typeof(Type))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user