diff --git a/osu.Framework.Tests/Graphics/ShaderStorageBufferObjectStackTest.cs b/osu.Framework.Tests/Graphics/ShaderStorageBufferObjectStackTest.cs index a8561e3cc7..545dc7c8c2 100644 --- a/osu.Framework.Tests/Graphics/ShaderStorageBufferObjectStackTest.cs +++ b/osu.Framework.Tests/Graphics/ShaderStorageBufferObjectStackTest.cs @@ -2,9 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Runtime.InteropServices; using NUnit.Framework; using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Rendering.Dummy; +using osu.Framework.Graphics.Shaders.Types; namespace osu.Framework.Tests.Graphics { @@ -12,21 +14,21 @@ public class ShaderStorageBufferObjectStackTest { private const int size = 10; - private ShaderStorageBufferObjectStack stack = null!; + private ShaderStorageBufferObjectStack stack = null!; [SetUp] public void Setup() { - stack = new ShaderStorageBufferObjectStack(new DummyRenderer(), 2, size); + stack = new ShaderStorageBufferObjectStack(new DummyRenderer(), 2, size); } [Test] public void TestBufferMustBeAtLeast2Elements() { - Assert.Throws(() => _ = new ShaderStorageBufferObjectStack(new DummyRenderer(), 1, 100)); - Assert.Throws(() => _ = new ShaderStorageBufferObjectStack(new DummyRenderer(), 100, 1)); - Assert.DoesNotThrow(() => _ = new ShaderStorageBufferObjectStack(new DummyRenderer(), 2, 100)); - Assert.DoesNotThrow(() => _ = new ShaderStorageBufferObjectStack(new DummyRenderer(), 100, 2)); + Assert.Throws(() => _ = new ShaderStorageBufferObjectStack(new DummyRenderer(), 1, 100)); + Assert.Throws(() => _ = new ShaderStorageBufferObjectStack(new DummyRenderer(), 100, 1)); + Assert.DoesNotThrow(() => _ = new ShaderStorageBufferObjectStack(new DummyRenderer(), 2, 100)); + Assert.DoesNotThrow(() => _ = new ShaderStorageBufferObjectStack(new DummyRenderer(), 100, 2)); } [Test] @@ -34,7 +36,7 @@ public void TestInitialState() { Assert.That(stack.CurrentOffset, Is.Zero); Assert.That(stack.CurrentBuffer, Is.Not.Null); - Assert.That(stack.CurrentBuffer[stack.CurrentOffset], Is.EqualTo(0)); + Assert.That(stack.CurrentBuffer[stack.CurrentOffset].Int.Value, Is.EqualTo(0)); } [Test] @@ -48,11 +50,11 @@ public void TestAddInitialItem() { var firstBuffer = stack.CurrentBuffer; - stack.Push(1); + stack.Push(new TestUniformData { Int = 1 }); Assert.That(stack.CurrentOffset, Is.Zero); Assert.That(stack.CurrentBuffer, Is.EqualTo(firstBuffer)); - Assert.That(stack.CurrentBuffer[stack.CurrentOffset], Is.EqualTo(1)); + Assert.That(stack.CurrentBuffer[stack.CurrentOffset].Int.Value, Is.EqualTo(1)); } [Test] @@ -63,10 +65,10 @@ public void TestPushToFillOneBuffer() for (int i = 0; i < size; i++) { - stack.Push(i); + stack.Push(new TestUniformData { Int = i }); Assert.That(stack.CurrentOffset, Is.EqualTo(expectedIndex++)); Assert.That(stack.CurrentBuffer, Is.EqualTo(firstBuffer)); - Assert.That(stack.CurrentBuffer[stack.CurrentOffset], Is.EqualTo(i)); + Assert.That(stack.CurrentBuffer[stack.CurrentOffset].Int.Value, Is.EqualTo(i)); } } @@ -74,7 +76,7 @@ public void TestPushToFillOneBuffer() public void TestPopEntireBuffer() { for (int i = 0; i < size; i++) - stack.Push(i); + stack.Push(new TestUniformData { Int = i }); var firstBuffer = stack.CurrentBuffer; @@ -82,7 +84,7 @@ public void TestPopEntireBuffer() { Assert.That(stack.CurrentOffset, Is.EqualTo(i)); Assert.That(stack.CurrentBuffer, Is.EqualTo(firstBuffer)); - Assert.That(stack.CurrentBuffer[stack.CurrentOffset], Is.EqualTo(i)); + Assert.That(stack.CurrentBuffer[stack.CurrentOffset].Int.Value, Is.EqualTo(i)); stack.Pop(); } } @@ -91,47 +93,47 @@ public void TestPopEntireBuffer() public void TestTransitionToBufferOnPush() { for (int i = 0; i < size; i++) - stack.Push(i); + stack.Push(new TestUniformData { Int = i }); var firstBuffer = stack.CurrentBuffer; - int copiedItem = stack.CurrentBuffer[stack.CurrentOffset]; + int copiedItem = stack.CurrentBuffer[stack.CurrentOffset].Int.Value; // Transition to a new buffer... - stack.Push(size); + stack.Push(new TestUniformData { Int = size }); Assert.That(stack.CurrentBuffer, Is.Not.EqualTo(firstBuffer)); // ... where the "hack" employed by the queue means that after a transition, the new item is added at index 1... Assert.That(stack.CurrentOffset, Is.EqualTo(1)); - Assert.That(stack.CurrentBuffer[1], Is.EqualTo(size)); + Assert.That(stack.CurrentBuffer[1].Int.Value, Is.EqualTo(size)); // ... and the first item in the new buffer is a copy of the last referenced item before the push. - Assert.That(stack.CurrentBuffer[0], Is.EqualTo(copiedItem)); + Assert.That(stack.CurrentBuffer[0].Int.Value, Is.EqualTo(copiedItem)); } [Test] public void TestTransitionToBufferOnPop() { for (int i = 0; i < size; i++) - stack.Push(i); + stack.Push(new TestUniformData { Int = i }); var firstBuffer = stack.CurrentBuffer; - int copiedItem = stack.CurrentBuffer[stack.CurrentOffset]; + int copiedItem = stack.CurrentBuffer[stack.CurrentOffset].Int.Value; // Transition to the new buffer. - stack.Push(size); + stack.Push(new TestUniformData { Int = size }); // The "hack" employed means that on the first pop, the index moves to the 0th index in the new buffer. stack.Pop(); Assert.That(stack.CurrentBuffer, Is.Not.EqualTo(firstBuffer)); Assert.That(stack.CurrentOffset, Is.Zero); - Assert.That(stack.CurrentBuffer[stack.CurrentOffset], Is.EqualTo(copiedItem)); + Assert.That(stack.CurrentBuffer[stack.CurrentOffset].Int.Value, Is.EqualTo(copiedItem)); // After a subsequent pop, we transition to the previous buffer and move to the index prior to the copied item. // We've already seen the copied item in the new buffer with the above pop, so we should not see it again here. stack.Pop(); Assert.That(stack.CurrentBuffer, Is.EqualTo(firstBuffer)); Assert.That(stack.CurrentOffset, Is.EqualTo(copiedItem - 1)); - Assert.That(stack.CurrentBuffer[stack.CurrentOffset], Is.EqualTo(copiedItem - 1)); + Assert.That(stack.CurrentBuffer[stack.CurrentOffset].Int.Value, Is.EqualTo(copiedItem - 1)); // Popping once again should move the index further backwards. stack.Pop(); @@ -143,7 +145,7 @@ public void TestTransitionToBufferOnPop() public void TestTransitionToAndFromNewBufferFromMiddle() { for (int i = 0; i < size; i++) - stack.Push(i); + stack.Push(new TestUniformData { Int = i }); // Move to the middle of the current buffer (it can not take up any new items at this point). stack.Pop(); @@ -153,13 +155,13 @@ public void TestTransitionToAndFromNewBufferFromMiddle() int copiedItem = stack.CurrentOffset; // Transition to the new buffer... - stack.Push(size); + stack.Push(new TestUniformData { Int = size }); // ... and as above, we arrive at index 1 in the new buffer. Assert.That(stack.CurrentBuffer, Is.Not.EqualTo(firstBuffer)); Assert.That(stack.CurrentOffset, Is.EqualTo(1)); - Assert.That(stack.CurrentBuffer[1], Is.EqualTo(size)); - Assert.That(stack.CurrentBuffer[0], Is.EqualTo(copiedItem)); + Assert.That(stack.CurrentBuffer[1].Int.Value, Is.EqualTo(size)); + Assert.That(stack.CurrentBuffer[0].Int.Value, Is.EqualTo(copiedItem)); // Transition to the previous buffer... stack.Pop(); @@ -168,7 +170,7 @@ public void TestTransitionToAndFromNewBufferFromMiddle() // ... noting that this is the same as the above "normal" pop case, except that item arrived at is in the middle of the previous buffer. Assert.That(stack.CurrentBuffer, Is.EqualTo(firstBuffer)); Assert.That(stack.CurrentOffset, Is.EqualTo(copiedItem - 1)); - Assert.That(stack.CurrentBuffer[stack.CurrentOffset], Is.EqualTo(copiedItem - 1)); + Assert.That(stack.CurrentBuffer[stack.CurrentOffset].Int.Value, Is.EqualTo(copiedItem - 1)); // Popping once again from this state should move further backwards. stack.Pop(); @@ -180,19 +182,19 @@ public void TestTransitionToAndFromNewBufferFromMiddle() public void TestMoveToAndFromMiddleOfNewBuffer() { for (int i = 0; i < size; i++) - stack.Push(i); + stack.Push(new TestUniformData { Int = i }); var lastBuffer = stack.CurrentBuffer; - int copiedItem1 = stack.CurrentBuffer[stack.CurrentOffset]; + int copiedItem1 = stack.CurrentBuffer[stack.CurrentOffset].Int.Value; // Transition to the middle of the new buffer. - stack.Push(size); - stack.Push(size + 1); + stack.Push(new TestUniformData { Int = size }); + stack.Push(new TestUniformData { Int = size + 1 }); Assert.That(stack.CurrentBuffer, Is.Not.EqualTo(lastBuffer)); Assert.That(stack.CurrentOffset, Is.EqualTo(2)); - Assert.That(stack.CurrentBuffer[2], Is.EqualTo(size + 1)); - Assert.That(stack.CurrentBuffer[1], Is.EqualTo(size)); - Assert.That(stack.CurrentBuffer[0], Is.EqualTo(copiedItem1)); + Assert.That(stack.CurrentBuffer[2].Int.Value, Is.EqualTo(size + 1)); + Assert.That(stack.CurrentBuffer[1].Int.Value, Is.EqualTo(size)); + Assert.That(stack.CurrentBuffer[0].Int.Value, Is.EqualTo(copiedItem1)); // Transition to the previous buffer. stack.Pop(); @@ -201,23 +203,23 @@ public void TestMoveToAndFromMiddleOfNewBuffer() Assert.That(stack.CurrentBuffer, Is.EqualTo(lastBuffer)); // The item that will be copied into the new buffer. - int copiedItem2 = stack.CurrentBuffer[stack.CurrentOffset]; + int copiedItem2 = stack.CurrentBuffer[stack.CurrentOffset].Int.Value; // Transition to the new buffer... - stack.Push(size + 2); + stack.Push(new TestUniformData { Int = size + 2 }); Assert.That(stack.CurrentBuffer, Is.Not.EqualTo(lastBuffer)); // ... noting that this is the same as the normal case of transitioning to a new buffer, except arriving in the middle of it... Assert.That(stack.CurrentOffset, Is.EqualTo(4)); - Assert.That(stack.CurrentBuffer[4], Is.EqualTo(size + 2)); + Assert.That(stack.CurrentBuffer[4].Int.Value, Is.EqualTo(size + 2)); // ... where this is the copied item as a result of the immediate push... - Assert.That(stack.CurrentBuffer[3], Is.EqualTo(copiedItem2)); + Assert.That(stack.CurrentBuffer[3].Int.Value, Is.EqualTo(copiedItem2)); // ... and these are the same items from the first pushes above. - Assert.That(stack.CurrentBuffer[2], Is.EqualTo(size + 1)); - Assert.That(stack.CurrentBuffer[1], Is.EqualTo(size)); - Assert.That(stack.CurrentBuffer[0], Is.EqualTo(copiedItem1)); + Assert.That(stack.CurrentBuffer[2].Int.Value, Is.EqualTo(size + 1)); + Assert.That(stack.CurrentBuffer[1].Int.Value, Is.EqualTo(size)); + Assert.That(stack.CurrentBuffer[0].Int.Value, Is.EqualTo(copiedItem1)); // Transition to the previous buffer... stack.Pop(); @@ -230,7 +232,7 @@ public void TestMoveToAndFromMiddleOfNewBuffer() // 3. From index N-2 -> transition to new buffer. // 4. Transition to old buffer, arrive at index N-3 (N-2 was copied into the new buffer). Assert.That(stack.CurrentOffset, Is.EqualTo(size - 3)); - Assert.That(stack.CurrentBuffer[stack.CurrentOffset], Is.EqualTo(size - 3)); + Assert.That(stack.CurrentBuffer[stack.CurrentOffset].Int.Value, Is.EqualTo(size - 3)); } [Test] @@ -241,18 +243,25 @@ public void TestTransitionFromEmptyStack() var lastBuffer = stack.CurrentBuffer; // Push one item. - stack.Push(i); + stack.Push(new TestUniformData { Int = i }); // On a buffer transition, test that the item at the 0-th index of the first buffer was correct copied to the new buffer. if (stack.CurrentBuffer != lastBuffer) - Assert.That(stack.CurrentBuffer[stack.CurrentOffset - 1], Is.EqualTo(0)); + Assert.That(stack.CurrentBuffer[stack.CurrentOffset - 1].Int.Value, Is.EqualTo(0)); // Test that the item was correctly placed in the new buffer - Assert.That(stack.CurrentBuffer[stack.CurrentOffset], Is.EqualTo(i)); + Assert.That(stack.CurrentBuffer[stack.CurrentOffset].Int.Value, Is.EqualTo(i)); // Return to an empty stack. stack.Pop(); } } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private record struct TestUniformData + { + public UniformInt Int; + private UniformPadding12 pad; + } } } diff --git a/osu.Framework/Graphics/OpenGL/Buffers/GLShaderStorageBufferObject.cs b/osu.Framework/Graphics/OpenGL/Buffers/GLShaderStorageBufferObject.cs index 4dff9b5314..ee4e3d88c9 100644 --- a/osu.Framework/Graphics/OpenGL/Buffers/GLShaderStorageBufferObject.cs +++ b/osu.Framework/Graphics/OpenGL/Buffers/GLShaderStorageBufferObject.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using System.Runtime.InteropServices; +using osu.Framework.Development; using osu.Framework.Graphics.Rendering; using osuTK.Graphics.ES30; @@ -20,6 +22,8 @@ internal class GLShaderStorageBufferObject : IShaderStorageBufferObject : IUniformBuffer, IGLUniformBuffer public GLUniformBuffer(GLRenderer renderer) { + Trace.Assert(ThreadSafety.IsDrawThread); + this.renderer = renderer; size = Marshal.SizeOf(default(TData)); diff --git a/osu.Framework/Graphics/Rendering/Deferred/DeferredShaderStorageBufferObject.cs b/osu.Framework/Graphics/Rendering/Deferred/DeferredShaderStorageBufferObject.cs index 929f232656..c0628ae22d 100644 --- a/osu.Framework/Graphics/Rendering/Deferred/DeferredShaderStorageBufferObject.cs +++ b/osu.Framework/Graphics/Rendering/Deferred/DeferredShaderStorageBufferObject.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using System.Runtime.CompilerServices; +using osu.Framework.Development; using osu.Framework.Graphics.Rendering.Deferred.Allocation; using osu.Framework.Graphics.Rendering.Deferred.Events; using osu.Framework.Graphics.Veldrid.Buffers; @@ -22,6 +24,8 @@ internal class DeferredShaderStorageBufferObject : IShaderStorageBufferOb public DeferredShaderStorageBufferObject(DeferredRenderer renderer, int ssboSize) { + Trace.Assert(ThreadSafety.IsDrawThread); + this.renderer = renderer; elementSize = Unsafe.SizeOf(); diff --git a/osu.Framework/Graphics/Rendering/Deferred/DeferredUniformBuffer.cs b/osu.Framework/Graphics/Rendering/Deferred/DeferredUniformBuffer.cs index a8373115b4..194c688616 100644 --- a/osu.Framework/Graphics/Rendering/Deferred/DeferredUniformBuffer.cs +++ b/osu.Framework/Graphics/Rendering/Deferred/DeferredUniformBuffer.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using osu.Framework.Development; using osu.Framework.Graphics.Rendering.Deferred.Allocation; using osu.Framework.Graphics.Rendering.Deferred.Events; using osu.Framework.Graphics.Veldrid.Buffers; @@ -24,6 +26,8 @@ internal class DeferredUniformBuffer : IUniformBuffer, IDeferredUn public DeferredUniformBuffer(DeferredRenderer renderer) { + Trace.Assert(ThreadSafety.IsDrawThread); + this.renderer = renderer; } diff --git a/osu.Framework/Graphics/Rendering/Renderer.cs b/osu.Framework/Graphics/Rendering/Renderer.cs index aa9ae1994a..8066d379ed 100644 --- a/osu.Framework/Graphics/Rendering/Renderer.cs +++ b/osu.Framework/Graphics/Rendering/Renderer.cs @@ -1263,8 +1263,6 @@ IShaderStorageBufferObject IRenderer.CreateShaderStorageBufferObject() { - Trace.Assert(ThreadSafety.IsDrawThread); - if (validUboTypes.Contains(typeof(TData))) return; diff --git a/osu.Framework/Graphics/Veldrid/Buffers/VeldridShaderStorageBufferObject.cs b/osu.Framework/Graphics/Veldrid/Buffers/VeldridShaderStorageBufferObject.cs index 8f5949ce11..25caaa4474 100644 --- a/osu.Framework/Graphics/Veldrid/Buffers/VeldridShaderStorageBufferObject.cs +++ b/osu.Framework/Graphics/Veldrid/Buffers/VeldridShaderStorageBufferObject.cs @@ -2,7 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using System.Runtime.InteropServices; +using osu.Framework.Development; using osu.Framework.Graphics.Rendering; using Veldrid; @@ -20,6 +22,8 @@ internal class VeldridShaderStorageBufferObject : IShaderStorageBufferObj public VeldridShaderStorageBufferObject(VeldridRenderer renderer, int uboSize, int ssboSize) { + Trace.Assert(ThreadSafety.IsDrawThread); + this.renderer = renderer; elementSize = (uint)Marshal.SizeOf(default(TData)); diff --git a/osu.Framework/Graphics/Veldrid/Buffers/VeldridUniformBuffer.cs b/osu.Framework/Graphics/Veldrid/Buffers/VeldridUniformBuffer.cs index 2bd4cccf7f..2d72dd26b5 100644 --- a/osu.Framework/Graphics/Veldrid/Buffers/VeldridUniformBuffer.cs +++ b/osu.Framework/Graphics/Veldrid/Buffers/VeldridUniformBuffer.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using osu.Framework.Development; using osu.Framework.Graphics.Rendering; using osu.Framework.Statistics; using Veldrid; @@ -37,6 +39,8 @@ internal class VeldridUniformBuffer : IUniformBuffer, IVeldridUnif public VeldridUniformBuffer(VeldridRenderer renderer) { + Trace.Assert(ThreadSafety.IsDrawThread); + this.renderer = renderer; storages.Add(new VeldridUniformBufferStorage(this.renderer)); }