diff --git a/osu.Framework.Benchmarks/BenchmarkEventList.cs b/osu.Framework.Benchmarks/BenchmarkEventList.cs index 83eef7a74..e7a5e1a44 100644 --- a/osu.Framework.Benchmarks/BenchmarkEventList.cs +++ b/osu.Framework.Benchmarks/BenchmarkEventList.cs @@ -36,16 +36,16 @@ namespace osu.Framework.Benchmarks [Benchmark] public int Read() { - var reader = filledEventList.CreateReader(); + var enumerator = filledEventList.CreateEnumerator(); int totalVertices = 0; - while (reader.Next()) + while (enumerator.Next()) { - switch (reader.CurrentType()) + switch (enumerator.CurrentType()) { case RenderEventType.Flush: - ref FlushEvent e = ref reader.Current(); + ref FlushEvent e = ref enumerator.Current(); totalVertices += e.VertexCount; break; } diff --git a/osu.Framework/Graphics/Rendering/Deferred/EventList.cs b/osu.Framework/Graphics/Rendering/Deferred/EventList.cs index 2df0943e4..1616d3dde 100644 --- a/osu.Framework/Graphics/Rendering/Deferred/EventList.cs +++ b/osu.Framework/Graphics/Rendering/Deferred/EventList.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using osu.Framework.Graphics.Rendering.Deferred.Allocation; using osu.Framework.Graphics.Rendering.Deferred.Events; @@ -34,16 +35,6 @@ namespace osu.Framework.Graphics.Rendering.Deferred where T : unmanaged, IRenderEvent => events.Add(createEvent(renderEvent)); - /// - /// Replaces the current event referenced by the with a new one. - /// - /// The . - /// The new render event. - /// The new event type. - public void ReplaceCurrent(EventListReader reader, in T newEvent) - where T : unmanaged, IRenderEvent - => events[reader.CurrentIndex()] = createEvent(newEvent); - private MemoryReference createEvent(in T renderEvent) where T : unmanaged, IRenderEvent { @@ -61,8 +52,83 @@ namespace osu.Framework.Graphics.Rendering.Deferred /// /// Creates a reader of this . /// - /// The . - public EventListReader CreateReader() - => new EventListReader(allocator, events); + /// The . + public Enumerator CreateEnumerator() + => new Enumerator(this); + + /// + /// Reads an . Semantically, this is very similar to . + /// + internal ref struct Enumerator + { + private readonly EventList list; + + private int eventIndex; + private Span eventData = Span.Empty; + + public Enumerator(EventList list) + { + this.list = list; + eventIndex = 0; + } + + /// + /// Advances to the next (or first) event in the list. + /// + /// Whether an event can be read. + public bool Next() + { + if (eventIndex < list.events.Count) + { + eventData = list.allocator.GetRegion(list.events[eventIndex]); + eventIndex++; + return true; + } + + eventData = Span.Empty; + return false; + } + + /// + /// Reads the current event type. + /// + /// + /// Not valid for use if returns false. + /// + public readonly ref RenderEventType CurrentType() + => ref MemoryMarshal.AsRef(eventData); + + /// + /// Reads the current event. + /// + /// The expected event type. + /// + /// Not valid for use if returns false. + /// + public readonly ref T Current() + where T : unmanaged, IRenderEvent + => ref MemoryMarshal.AsRef(eventData[1..]); + + /// + /// Replaces the current event with a new one. + /// + /// The new render event. + /// The new event type. + public void Replace(T newEvent) + where T : unmanaged, IRenderEvent + { + if (Unsafe.SizeOf() <= eventData.Length) + { + // Fast path where we can maintain contiguous data references. + eventData[0] = (byte)newEvent.Type; + Unsafe.WriteUnaligned(ref eventData[1], newEvent); + } + else + { + // Slow path. + eventData = list.allocator.GetRegion(list.events[eventIndex] = list.createEvent(newEvent)); + } + } + } } } diff --git a/osu.Framework/Graphics/Rendering/Deferred/EventListReader.cs b/osu.Framework/Graphics/Rendering/Deferred/EventListReader.cs deleted file mode 100644 index 60707a91c..000000000 --- a/osu.Framework/Graphics/Rendering/Deferred/EventListReader.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using osu.Framework.Graphics.Rendering.Deferred.Allocation; -using osu.Framework.Graphics.Rendering.Deferred.Events; - -namespace osu.Framework.Graphics.Rendering.Deferred -{ - /// - /// Reads an . Semantically, this is very similar to . - /// - internal ref struct EventListReader - { - private readonly ResourceAllocator allocator; - private readonly List events = new List(); - - private int eventIndex; - private Span eventData = Span.Empty; - - public EventListReader(ResourceAllocator allocator, List events) - { - this.allocator = allocator; - this.events = events; - eventIndex = 0; - } - - /// - /// Advances to the next (or first) event in the list. - /// - /// Whether an event can be read. - public bool Next() - { - if (eventIndex < events.Count) - { - eventData = allocator.GetRegion(events[eventIndex]); - eventIndex++; - return true; - } - - eventData = Span.Empty; - return false; - } - - /// - /// Reads the current event type. - /// - /// - /// Not valid for use if returns false. - /// - public readonly ref RenderEventType CurrentType() - => ref MemoryMarshal.AsRef(eventData); - - /// - /// Reads the current event. - /// - /// The expected event type. - /// - /// Not valid for use if returns false. - /// - public readonly ref T Current() - where T : unmanaged, IRenderEvent - => ref MemoryMarshal.AsRef(eventData[1..]); - - /// - /// The index of the current event in the list. - /// - public int CurrentIndex() - => eventIndex - 1; - } -} diff --git a/osu.Framework/Graphics/Rendering/Deferred/EventProcessor.cs b/osu.Framework/Graphics/Rendering/Deferred/EventProcessor.cs index eeda97658..04b87bb5f 100644 --- a/osu.Framework/Graphics/Rendering/Deferred/EventProcessor.cs +++ b/osu.Framework/Graphics/Rendering/Deferred/EventProcessor.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; using System.IO; using System.Text; using osu.Framework.Graphics.Rendering.Deferred.Allocation; @@ -39,21 +38,21 @@ namespace osu.Framework.Graphics.Rendering.Deferred if (string.IsNullOrEmpty(FrameworkEnvironment.DeferredRendererEventsOutputPath)) return; - EventListReader reader = context.RenderEvents.CreateReader(); + EventList.Enumerator enumerator = context.RenderEvents.CreateEnumerator(); StringBuilder builder = new StringBuilder(); int indent = 0; - while (reader.Next()) + while (enumerator.Next()) { string info; int indentChange = 0; - switch (reader.CurrentType()) + switch (enumerator.CurrentType()) { case RenderEventType.DrawNodeAction: { - ref DrawNodeActionEvent e = ref reader.Current(); + ref DrawNodeActionEvent e = ref enumerator.Current(); info = $"DrawNode.{e.Action} ({context.Dereference(e.DrawNode)})"; @@ -73,7 +72,7 @@ namespace osu.Framework.Graphics.Rendering.Deferred default: { - info = $"{reader.CurrentType().ToString()}"; + info = $"{enumerator.CurrentType().ToString()}"; break; } } @@ -88,15 +87,15 @@ namespace osu.Framework.Graphics.Rendering.Deferred private void processUploads() { - EventListReader reader = context.RenderEvents.CreateReader(); + EventList.Enumerator enumerator = context.RenderEvents.CreateEnumerator(); - while (reader.Next()) + while (enumerator.Next()) { - switch (reader.CurrentType()) + switch (enumerator.CurrentType()) { case RenderEventType.AddPrimitiveToBatch: { - ref AddPrimitiveToBatchEvent e = ref reader.Current(); + ref AddPrimitiveToBatchEvent e = ref enumerator.Current(); IDeferredVertexBatch batch = context.Dereference(e.VertexBatch); batch.Write(e.Memory); break; @@ -104,16 +103,16 @@ namespace osu.Framework.Graphics.Rendering.Deferred case RenderEventType.SetUniformBufferData: { - ref SetUniformBufferDataEvent e = ref reader.Current(); + ref SetUniformBufferDataEvent e = ref enumerator.Current(); IDeferredUniformBuffer buffer = context.Dereference(e.Buffer); - UniformBufferReference range = buffer.Write(e.Memory); - context.RenderEvents.ReplaceCurrent(reader, new SetUniformBufferRangeEvent(e.Buffer, range)); + UniformBufferReference range = buffer.Write(e.Data.Memory); + enumerator.Replace(e with { Data = new UniformBufferData(range) }); break; } case RenderEventType.SetShaderStorageBufferObjectData: { - ref SetShaderStorageBufferObjectDataEvent e = ref reader.Current(); + ref SetShaderStorageBufferObjectDataEvent e = ref enumerator.Current(); IDeferredShaderStorageBufferObject buffer = context.Dereference(e.Buffer); buffer.Write(e.Index, e.Memory); break; @@ -127,74 +126,70 @@ namespace osu.Framework.Graphics.Rendering.Deferred private void processEvents() { - EventListReader reader = context.RenderEvents.CreateReader(); + EventList.Enumerator enumerator = context.RenderEvents.CreateEnumerator(); - while (reader.Next()) + while (enumerator.Next()) { - switch (reader.CurrentType()) + switch (enumerator.CurrentType()) { case RenderEventType.SetFrameBuffer: - processEvent(reader.Current()); + processEvent(enumerator.Current()); break; case RenderEventType.ResizeFrameBuffer: - processEvent(reader.Current()); + processEvent(enumerator.Current()); break; case RenderEventType.SetShader: - processEvent(reader.Current()); + processEvent(enumerator.Current()); break; case RenderEventType.SetTexture: - processEvent(reader.Current()); + processEvent(enumerator.Current()); break; case RenderEventType.SetUniformBuffer: - processEvent(reader.Current()); + processEvent(enumerator.Current()); break; case RenderEventType.Clear: - processEvent(reader.Current()); + processEvent(enumerator.Current()); break; case RenderEventType.SetDepthInfo: - processEvent(reader.Current()); + processEvent(enumerator.Current()); break; case RenderEventType.SetScissor: - processEvent(reader.Current()); + processEvent(enumerator.Current()); break; case RenderEventType.SetScissorState: - processEvent(reader.Current()); + processEvent(enumerator.Current()); break; case RenderEventType.SetStencilInfo: - processEvent(reader.Current()); + processEvent(enumerator.Current()); break; case RenderEventType.SetViewport: - processEvent(reader.Current()); + processEvent(enumerator.Current()); break; case RenderEventType.SetBlend: - processEvent(reader.Current()); + processEvent(enumerator.Current()); break; case RenderEventType.SetBlendMask: - processEvent(reader.Current()); + processEvent(enumerator.Current()); break; case RenderEventType.Flush: - processEvent(reader.Current()); - break; - - case RenderEventType.SetUniformBufferRange: - processEvent(reader.Current()); + processEvent(enumerator.Current()); break; case RenderEventType.SetUniformBufferData: - Debug.Fail($"Should be replaced by {nameof(SetUniformBufferRangeEvent)} during upload."); + processEvent(enumerator.Current()); break; } } @@ -242,12 +237,12 @@ namespace osu.Framework.Graphics.Rendering.Deferred private void processEvent(in FlushEvent e) => context.Dereference(e.VertexBatch).Draw(e.VertexCount); - private void processEvent(in SetUniformBufferRangeEvent e) + private void processEvent(in SetUniformBufferDataEvent e) { - IDeferredUniformBuffer buffer = context.Dereference(e.UniformBuffer); + IDeferredUniformBuffer buffer = context.Dereference(e.Buffer); - buffer.Activate(e.Range.Chunk); - graphics.SetUniformBufferOffset(buffer, (uint)e.Range.OffsetInChunk); + buffer.Activate(e.Data.Range.Chunk); + graphics.SetUniformBufferOffset(buffer, (uint)e.Data.Range.OffsetInChunk); } } } diff --git a/osu.Framework/Graphics/Rendering/Deferred/Events/RenderEventType.cs b/osu.Framework/Graphics/Rendering/Deferred/Events/RenderEventType.cs index ceb12d63b..378237cf3 100644 --- a/osu.Framework/Graphics/Rendering/Deferred/Events/RenderEventType.cs +++ b/osu.Framework/Graphics/Rendering/Deferred/Events/RenderEventType.cs @@ -24,6 +24,5 @@ namespace osu.Framework.Graphics.Rendering.Deferred.Events Flush, DrawNodeAction, - SetUniformBufferRange } } diff --git a/osu.Framework/Graphics/Rendering/Deferred/Events/SetUniformBufferDataEvent.cs b/osu.Framework/Graphics/Rendering/Deferred/Events/SetUniformBufferDataEvent.cs index 916a5798a..27a8b6e68 100644 --- a/osu.Framework/Graphics/Rendering/Deferred/Events/SetUniformBufferDataEvent.cs +++ b/osu.Framework/Graphics/Rendering/Deferred/Events/SetUniformBufferDataEvent.cs @@ -2,18 +2,55 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Runtime.InteropServices; using osu.Framework.Graphics.Rendering.Deferred.Allocation; namespace osu.Framework.Graphics.Rendering.Deferred.Events { - internal readonly record struct SetUniformBufferDataEvent(ResourceReference Buffer, MemoryReference Memory) : IRenderEvent + internal readonly record struct SetUniformBufferDataEvent(ResourceReference Buffer, UniformBufferData Data) : IRenderEvent { public RenderEventType Type => RenderEventType.SetUniformBufferData; public static SetUniformBufferDataEvent Create(DeferredRenderer renderer, IDeferredUniformBuffer uniformBuffer, T data) where T : unmanaged, IEquatable { - return new SetUniformBufferDataEvent(renderer.Context.Reference(uniformBuffer), renderer.Context.AllocateObject(data)); + return new SetUniformBufferDataEvent(renderer.Context.Reference(uniformBuffer), new UniformBufferData(renderer.Context.AllocateObject(data))); + } + } + + [StructLayout(LayoutKind.Explicit)] + internal readonly struct UniformBufferData : IEquatable + { + [FieldOffset(0)] + public readonly MemoryReference Memory; + + [FieldOffset(0)] + public readonly UniformBufferReference Range; + + public UniformBufferData(MemoryReference memory) + { + Memory = memory; + } + + public UniformBufferData(UniformBufferReference range) + { + Range = range; + } + + public bool Equals(UniformBufferData other) + { + return Memory.Equals(other.Memory) + && Range.Equals(other.Range); + } + + public override bool Equals(object? obj) + { + return obj is UniformBufferData other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(Memory, Range); } } } diff --git a/osu.Framework/Graphics/Rendering/Deferred/Events/SetUniformBufferRangeEvent.cs b/osu.Framework/Graphics/Rendering/Deferred/Events/SetUniformBufferRangeEvent.cs deleted file mode 100644 index 55f7dd483..000000000 --- a/osu.Framework/Graphics/Rendering/Deferred/Events/SetUniformBufferRangeEvent.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics.Rendering.Deferred.Allocation; - -namespace osu.Framework.Graphics.Rendering.Deferred.Events -{ - internal readonly record struct SetUniformBufferRangeEvent(ResourceReference UniformBuffer, UniformBufferReference Range) : IRenderEvent - { - public RenderEventType Type => RenderEventType.SetUniformBufferRange; - } -}