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:
PJB3005
2025-10-07 18:18:48 +02:00
parent 617d3e81b2
commit b7a3526131
64 changed files with 1302 additions and 1205 deletions

View File

@@ -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>

View File

@@ -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" />

View File

@@ -0,0 +1,4 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Robust.Client")]
[module: SkipLocalsInit]

View 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*;

View File

@@ -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);

View File

@@ -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>.

View File

@@ -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()

View 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>

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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()

View File

@@ -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

View File

@@ -1,6 +1,4 @@
using System.Threading;
namespace Robust.Client.Graphics.Clyde.Rhi;
namespace Robust.Client.Graphics.Rhi.WebGpu;
internal sealed partial class RhiWebGpu
{

View 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;
}
}

View File

@@ -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;
}
}

View 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;
}
}

View File

@@ -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;
}
}

View 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);
}
}

View 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) ?? "");
}
}

View File

@@ -1,3 +1,5 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Robust.Client")]
[assembly: InternalsVisibleTo("Robust.Client.Graphics.Rhi")]
[module: SkipLocalsInit]

View 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,
};
}

View 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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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),

View File

@@ -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()

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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
{

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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));
}
}

View File

@@ -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
{

View File

@@ -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" />

View File

@@ -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");

View File

@@ -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})");

View 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")]

View File

@@ -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,

View File

@@ -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)

View File

@@ -1,5 +1,4 @@
using System;
using System.Runtime.Serialization;
using Robust.Shared.Analyzers;
namespace Robust.Shared.IoC.Exceptions
{

View File

@@ -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
{

View File

@@ -1,5 +1,4 @@
using System;
using System.Runtime.Serialization;
using Robust.Shared.Analyzers;
namespace Robust.Shared.IoC.Exceptions
{

View File

@@ -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]

View File

@@ -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>

View File

@@ -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
{

View 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>

View File

@@ -1,7 +1,4 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using JetBrains.Annotations;
namespace Robust.Shared.Utility;

View 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 };
}

View File

@@ -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;
}
}
}

View File

@@ -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">

View 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;
}
}