diff --git a/osu.Framework.Tests/Graphics/RendererTest.cs b/osu.Framework.Tests/Graphics/RendererTest.cs new file mode 100644 index 000000000..1f77e8e1c --- /dev/null +++ b/osu.Framework.Tests/Graphics/RendererTest.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics.Rendering.Dummy; +using osu.Framework.Graphics.Textures; + +namespace osu.Framework.Tests.Graphics +{ + public class RendererTest + { + [Test] + public void TestWhitePixelReuseUpdatesTextureWrapping() + { + DummyRenderer renderer = new DummyRenderer(); + + renderer.BindTexture(renderer.WhitePixel, 0, WrapMode.None, WrapMode.None); + Assert.That(renderer.CurrentWrapModeS, Is.EqualTo(WrapMode.None)); + Assert.That(renderer.CurrentWrapModeS, Is.EqualTo(WrapMode.None)); + + renderer.BindTexture(renderer.WhitePixel, 0, WrapMode.ClampToEdge, WrapMode.ClampToEdge); + Assert.That(renderer.CurrentWrapModeS, Is.EqualTo(WrapMode.ClampToEdge)); + Assert.That(renderer.CurrentWrapModeS, Is.EqualTo(WrapMode.ClampToEdge)); + + renderer.BindTexture(renderer.WhitePixel, 0, WrapMode.None, WrapMode.None); + Assert.That(renderer.CurrentWrapModeS, Is.EqualTo(WrapMode.None)); + Assert.That(renderer.CurrentWrapModeS, Is.EqualTo(WrapMode.None)); + } + } +} diff --git a/osu.Framework.Tests/Graphics/ShaderStorageBufferObjectStackTest.cs b/osu.Framework.Tests/Graphics/ShaderStorageBufferObjectStackTest.cs index a8561e3cc..545dc7c8c 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 @@ namespace osu.Framework.Tests.Graphics { 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 @@ namespace osu.Framework.Tests.Graphics { 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 @@ namespace osu.Framework.Tests.Graphics { 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 @@ namespace osu.Framework.Tests.Graphics 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 @@ namespace osu.Framework.Tests.Graphics 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 @@ namespace osu.Framework.Tests.Graphics { 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 @@ namespace osu.Framework.Tests.Graphics 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 @@ namespace osu.Framework.Tests.Graphics 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 @@ namespace osu.Framework.Tests.Graphics 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 @@ namespace osu.Framework.Tests.Graphics // ... 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 @@ namespace osu.Framework.Tests.Graphics 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 @@ namespace osu.Framework.Tests.Graphics 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 @@ namespace osu.Framework.Tests.Graphics // 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 @@ namespace osu.Framework.Tests.Graphics 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.Tests/Visual/Graphics/TestSceneVertexBatching.cs b/osu.Framework.Tests/Visual/Graphics/TestSceneVertexBatching.cs index 9e1b03b30..13767014d 100644 --- a/osu.Framework.Tests/Visual/Graphics/TestSceneVertexBatching.cs +++ b/osu.Framework.Tests/Visual/Graphics/TestSceneVertexBatching.cs @@ -3,13 +3,11 @@ using System.Linq; using NUnit.Framework; -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Platform; using osu.Framework.Utils; using osuTK; using osuTK.Graphics; @@ -18,8 +16,10 @@ namespace osu.Framework.Tests.Visual.Graphics { public partial class TestSceneVertexBatching : FrameworkTestScene { - [Resolved] - private GameHost host { get; set; } = null!; + /// + /// Max number of quads per batch in the default quad batch (Renderer.defaultQuadBatch). + /// + private const int max_boxes_per_batch = 100; [Test] public void TestBatchUntilOverflow() @@ -28,8 +28,6 @@ namespace osu.Framework.Tests.Visual.Graphics { Clear(); - int boxesPerBatch = host.Renderer.DefaultQuadBatch.Size; - Add(new FillFlowContainer { Anchor = Anchor.Centre, @@ -37,7 +35,7 @@ namespace osu.Framework.Tests.Visual.Graphics RelativeSizeAxes = Axes.Both, Margin = new MarginPadding(25f), Spacing = new Vector2(10f), - ChildrenEnumerable = Enumerable.Range(0, boxesPerBatch * 2).Select(i => new Box + ChildrenEnumerable = Enumerable.Range(0, max_boxes_per_batch * 2).Select(i => new Box { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -55,8 +53,6 @@ namespace osu.Framework.Tests.Visual.Graphics { Clear(); - int boxesPerBatch = host.Renderer.DefaultQuadBatch.Size; - Add(new FillFlowContainer { Anchor = Anchor.Centre, @@ -64,7 +60,7 @@ namespace osu.Framework.Tests.Visual.Graphics RelativeSizeAxes = Axes.Both, Margin = new MarginPadding(25f), Spacing = new Vector2(10f), - ChildrenEnumerable = Enumerable.Range(0, boxesPerBatch * 2).Select(i => new BoxWithFlush + ChildrenEnumerable = Enumerable.Range(0, max_boxes_per_batch * 2).Select(i => new BoxWithFlush { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Framework/Graphics/OpenGL/Buffers/GLShaderStorageBufferObject.cs b/osu.Framework/Graphics/OpenGL/Buffers/GLShaderStorageBufferObject.cs index 4dff9b531..ee4e3d88c 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 @@ namespace osu.Framework.Graphics.OpenGL.Buffers public GLShaderStorageBufferObject(GLRenderer renderer, int uboSize, int ssboSize) { + Trace.Assert(ThreadSafety.IsDrawThread); + Id = GL.GenBuffer(); Size = renderer.UseStructuredBuffers ? ssboSize : uboSize; data = new TData[Size]; diff --git a/osu.Framework/Graphics/OpenGL/Buffers/GLUniformBuffer.cs b/osu.Framework/Graphics/OpenGL/Buffers/GLUniformBuffer.cs index 5dea0c8b6..d8a6c707b 100644 --- a/osu.Framework/Graphics/OpenGL/Buffers/GLUniformBuffer.cs +++ b/osu.Framework/Graphics/OpenGL/Buffers/GLUniformBuffer.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 osu.Framework.Statistics; using osuTK.Graphics.ES30; @@ -20,6 +22,8 @@ namespace osu.Framework.Graphics.OpenGL.Buffers 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 929f23265..c0628ae22 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 @@ namespace osu.Framework.Graphics.Rendering.Deferred 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 a8373115b..194c68861 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 @@ namespace osu.Framework.Graphics.Rendering.Deferred public DeferredUniformBuffer(DeferredRenderer renderer) { + Trace.Assert(ThreadSafety.IsDrawThread); + this.renderer = renderer; } diff --git a/osu.Framework/Graphics/Rendering/Dummy/DummyFrameBuffer.cs b/osu.Framework/Graphics/Rendering/Dummy/DummyFrameBuffer.cs index 519893bf8..f4a1d18a1 100644 --- a/osu.Framework/Graphics/Rendering/Dummy/DummyFrameBuffer.cs +++ b/osu.Framework/Graphics/Rendering/Dummy/DummyFrameBuffer.cs @@ -26,7 +26,7 @@ namespace osu.Framework.Graphics.Rendering.Dummy public DummyFrameBuffer(IRenderer renderer) { - Texture = new Texture(new DummyNativeTexture(renderer), WrapMode.None, WrapMode.None); + Texture = new Texture(new DummyNativeTexture(renderer, 1, 1), WrapMode.None, WrapMode.None); } public void Bind() diff --git a/osu.Framework/Graphics/Rendering/Dummy/DummyNativeTexture.cs b/osu.Framework/Graphics/Rendering/Dummy/DummyNativeTexture.cs index 36f07a96e..c014cc44b 100644 --- a/osu.Framework/Graphics/Rendering/Dummy/DummyNativeTexture.cs +++ b/osu.Framework/Graphics/Rendering/Dummy/DummyNativeTexture.cs @@ -14,8 +14,8 @@ namespace osu.Framework.Graphics.Rendering.Dummy public string Identifier => string.Empty; public int MaxSize => 4096; // Sane default for testing purposes. - public int Width { get; set; } = 1; - public int Height { get; set; } = 1; + public int Width { get; set; } + public int Height { get; set; } public int? MipLevel { get; set; } public bool Available => true; public bool BypassTextureUploadQueueing { get; set; } @@ -23,9 +23,11 @@ namespace osu.Framework.Graphics.Rendering.Dummy public bool IsQueuedForUpload { get; set; } ulong INativeTexture.TotalBindCount { get; set; } - public DummyNativeTexture(IRenderer renderer) + public DummyNativeTexture(IRenderer renderer, int width, int height) { Renderer = renderer; + Width = width; + Height = height; } public void FlushUploads() diff --git a/osu.Framework/Graphics/Rendering/Dummy/DummyRenderer.cs b/osu.Framework/Graphics/Rendering/Dummy/DummyRenderer.cs index 04783126c..0dff42beb 100644 --- a/osu.Framework/Graphics/Rendering/Dummy/DummyRenderer.cs +++ b/osu.Framework/Graphics/Rendering/Dummy/DummyRenderer.cs @@ -1,245 +1,142 @@ // 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 osu.Framework.Graphics.Primitives; -using osu.Framework.Graphics.Rendering.Vertices; using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Textures; using osu.Framework.Platform; -using osu.Framework.Threading; -using osuTK; using osuTK.Graphics; using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; -using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; namespace osu.Framework.Graphics.Rendering.Dummy { /// /// An that does nothing. May be used for tests that don't have a visual output. /// - public sealed class DummyRenderer : IRenderer + public sealed class DummyRenderer : Renderer { - public int MaxTextureSize => int.MaxValue; - public int MaxTexturesUploadedPerFrame { get; set; } = int.MaxValue; - public int MaxPixelsUploadedPerFrame { get; set; } = int.MaxValue; + protected internal override bool VerticalSync { get; set; } = true; + protected internal override bool AllowTearing { get; set; } + public override bool IsDepthRangeZeroToOne => true; + public override bool IsUvOriginTopLeft => true; + public override bool IsClipSpaceYInverted => true; - public bool IsDepthRangeZeroToOne => true; - public bool IsUvOriginTopLeft => true; - public bool IsClipSpaceYInverted => true; - public ref readonly MaskingInfo CurrentMaskingInfo => ref maskingInfo; - private readonly MaskingInfo maskingInfo; + protected internal override Image TakeScreenshot() + => new Image(1, 1); - public RectangleI Viewport => RectangleI.Empty; - public RectangleF Ortho => RectangleF.Empty; - public RectangleI Scissor => RectangleI.Empty; - public Vector2I ScissorOffset => Vector2I.Zero; - public Matrix4 ProjectionMatrix => Matrix4.Identity; - public DepthInfo CurrentDepthInfo => DepthInfo.Default; - public StencilInfo CurrentStencilInfo => StencilInfo.Default; - public WrapMode CurrentWrapModeS => WrapMode.None; - public WrapMode CurrentWrapModeT => WrapMode.None; - public bool IsMaskingActive => false; - public bool UsingBackbuffer => false; - public Texture WhitePixel { get; } - DepthValue IRenderer.BackbufferDepth { get; } = new DepthValue(); - - public bool IsInitialised { get; private set; } - - public DummyRenderer() - { - maskingInfo = default; - WhitePixel = new TextureWhitePixel(new Texture(new DummyNativeTexture(this), WrapMode.None, WrapMode.None)); - } - - public ulong FrameIndex { get; private set; } - - bool IRenderer.VerticalSync { get; set; } = true; - - bool IRenderer.AllowTearing { get; set; } - - Storage? IRenderer.CacheStorage { set { } } - - void IRenderer.Initialise(IGraphicsSurface graphicsSurface) - { - IsInitialised = true; - } - - void IRenderer.BeginFrame(Vector2 windowSize) - { - FrameIndex++; - } - - void IRenderer.FinishFrame() - { - } - - void IRenderer.FlushCurrentBatch(FlushBatchSource? source) - { - } - - void IRenderer.SwapBuffers() - { - } - - void IRenderer.WaitUntilIdle() - { - } - - void IRenderer.WaitUntilNextFrameReady() - { - } - - void IRenderer.MakeCurrent() - { - } - - void IRenderer.ClearCurrent() - { - } - - public bool BindTexture(Texture texture, int unit = 0, WrapMode? wrapModeS = null, WrapMode? wrapModeT = null) - => true; - - public void UseProgram(IShader? shader) - { - } - - public void Clear(ClearInfo clearInfo) - { - } - - public void PushScissorState(bool enabled) - { - } - - public void PopScissorState() - { - } - - public void SetBlend(BlendingParameters blendingParameters) - { - } - - public void SetBlendMask(BlendingMask blendingMask) - { - } - - public void PushViewport(RectangleI viewport) - { - } - - public void PopViewport() - { - } - - public void PushScissor(RectangleI scissor) - { - } - - public void PopScissor() - { - } - - public void PushScissorOffset(Vector2I offset) - { - } - - public void PopScissorOffset() - { - } - - public void PushProjectionMatrix(Matrix4 matrix) - { - } - - public void PopProjectionMatrix() - { - } - - public void PushMaskingInfo(in MaskingInfo maskingInfo, bool overwritePreviousScissor = false) - { - } - - public void PopMaskingInfo() - { - } - - public void PushDepthInfo(DepthInfo depthInfo) - { - } - - public void PopDepthInfo() - { - } - - public void PushStencilInfo(StencilInfo stencilInfo) - { - } - - public void PopStencilInfo() - { - } - - public void ScheduleExpensiveOperation(ScheduledDelegate operation) => operation.RunTask(); - - public void ScheduleDisposal(Action disposalAction, T target) => disposalAction(target); - - Image IRenderer.TakeScreenshot() => new Image(1366, 768); - - IShaderPart IRenderer.CreateShaderPart(IShaderStore manager, string name, byte[]? rawData, ShaderPartType partType) + protected override IShaderPart CreateShaderPart(IShaderStore store, string name, byte[]? rawData, ShaderPartType partType) => new DummyShaderPart(); - IShader IRenderer.CreateShader(string name, IShaderPart[] parts) + protected override IShader CreateShader(string name, IShaderPart[] parts, ShaderCompilationStore compilationStore) => new DummyShader(this); - public IFrameBuffer CreateFrameBuffer(RenderBufferFormat[]? renderBufferFormats = null, TextureFilteringMode filteringMode = TextureFilteringMode.Linear) - => new DummyFrameBuffer(this); - - public Texture CreateTexture(int width, int height, bool manualMipmaps = false, TextureFilteringMode filteringMode = TextureFilteringMode.Linear, WrapMode wrapModeS = WrapMode.None, - WrapMode wrapModeT = WrapMode.None, Color4? initialisationColour = null) - => new Texture(new DummyNativeTexture(this) { Width = width, Height = height }, wrapModeS, wrapModeT); - - public Texture CreateVideoTexture(int width, int height) - => CreateTexture(width, height); - - public IVertexBatch CreateLinearBatch(int size, int maxBuffers, PrimitiveTopology topology) where TVertex : unmanaged, IEquatable, IVertex + protected override IVertexBatch CreateLinearBatch(int size, int maxBuffers, PrimitiveTopology topology) => new DummyVertexBatch(); - public IVertexBatch CreateQuadBatch(int size, int maxBuffers) where TVertex : unmanaged, IEquatable, IVertex + protected override IVertexBatch CreateQuadBatch(int size, int maxBuffers) => new DummyVertexBatch(); - public IUniformBuffer CreateUniformBuffer() where TData : unmanaged, IEquatable + protected override IUniformBuffer CreateUniformBuffer() => new DummyUniformBuffer(); - public IShaderStorageBufferObject CreateShaderStorageBufferObject(int uboSize, int ssboSize) where TData : unmanaged, IEquatable + protected override IShaderStorageBufferObject CreateShaderStorageBufferObject(int uboSize, int ssboSize) => new DummyShaderStorageBufferObject(ssboSize); - void IRenderer.SetUniform(IUniformWithValue uniform) + public Texture CreateTexture(int width, int height, bool manualMipmaps = false, TextureFilteringMode filteringMode = TextureFilteringMode.Linear, WrapMode wrapModeS = WrapMode.None) + => base.CreateTexture(width, height, manualMipmaps, filteringMode, wrapModeS, wrapModeS, null); + + protected override INativeTexture CreateNativeTexture(int width, int height, bool manualMipmaps = false, TextureFilteringMode filteringMode = TextureFilteringMode.Linear, + Color4? initialisationColour = null) + => new DummyNativeTexture(this, width, height); + + protected override INativeTexture CreateNativeVideoTexture(int width, int height) + => new DummyNativeTexture(this, width, height); + + protected override void Initialise(IGraphicsSurface graphicsSurface) { } - IVertexBatch IRenderer.DefaultQuadBatch => new DummyVertexBatch(); - - void IRenderer.PushQuadBatch(IVertexBatch quadBatch) + protected internal override void SwapBuffers() { } - void IRenderer.PopQuadBatch() + protected internal override void WaitUntilIdle() { } - event Action? IRenderer.TextureCreated + protected internal override void WaitUntilNextFrameReady() { - add - { - } - remove - { - } } - Texture[] IRenderer.GetAllTextures() => Array.Empty(); + protected internal override void MakeCurrent() + { + } + + protected internal override void ClearCurrent() + { + } + + protected override void ClearImplementation(ClearInfo clearInfo) + { + } + + protected override void SetBlendImplementation(BlendingParameters blendingParameters) + { + } + + protected override void SetBlendMaskImplementation(BlendingMask blendingMask) + { + } + + protected override void SetViewportImplementation(RectangleI viewport) + { + } + + protected override void SetScissorImplementation(RectangleI scissor) + { + } + + protected override void SetScissorStateImplementation(bool enabled) + { + } + + protected override void SetDepthInfoImplementation(DepthInfo depthInfo) + { + } + + protected override void SetStencilInfoImplementation(StencilInfo stencilInfo) + { + } + + protected override bool SetTextureImplementation(INativeTexture? texture, int unit) + => true; + + protected override void SetFrameBufferImplementation(IFrameBuffer? frameBuffer) + { + } + + protected override void DeleteFrameBufferImplementation(IFrameBuffer frameBuffer) + { + } + + public override void DrawVerticesImplementation(PrimitiveTopology topology, int vertexStart, int verticesCount) + { + } + + protected override void SetShaderImplementation(IShader shader) + { + } + + protected override void SetUniformImplementation(IUniformWithValue uniform) + { + } + + protected override void SetUniformBufferImplementation(string blockName, IUniformBuffer buffer) + { + } + + public override IFrameBuffer CreateFrameBuffer(RenderBufferFormat[]? renderBufferFormats = null, TextureFilteringMode filteringMode = TextureFilteringMode.Linear) + => new DummyFrameBuffer(this); } } diff --git a/osu.Framework/Graphics/Rendering/Renderer.cs b/osu.Framework/Graphics/Rendering/Renderer.cs index cf8a4d43e..8066d379e 100644 --- a/osu.Framework/Graphics/Rendering/Renderer.cs +++ b/osu.Framework/Graphics/Rendering/Renderer.cs @@ -793,6 +793,8 @@ namespace osu.Framework.Graphics.Rendering if (texture is TextureWhitePixel && lastBoundTextureIsAtlas[unit]) { + setWrapMode(wrapModeS ?? texture.WrapModeS, wrapModeT ?? texture.WrapModeT); + // We can use the special white space from any atlas texture. return true; } @@ -823,17 +825,7 @@ namespace osu.Framework.Graphics.Rendering if (!SetTextureImplementation(texture, unit)) return false; - if (wrapModeS != CurrentWrapModeS) - { - CurrentWrapModeS = wrapModeS; - globalUniformsChanged = true; - } - - if (wrapModeT != CurrentWrapModeT) - { - CurrentWrapModeT = wrapModeT; - globalUniformsChanged = true; - } + setWrapMode(wrapModeS, wrapModeT); lastBoundTexture[unit] = texture; lastBoundTextureIsAtlas[unit] = false; @@ -845,6 +837,25 @@ namespace osu.Framework.Graphics.Rendering return true; } + private void setWrapMode(WrapMode wrapModeS, WrapMode wrapModeT) + { + if (wrapModeS != CurrentWrapModeS) + { + FlushCurrentBatch(FlushBatchSource.BindTexture); + + CurrentWrapModeS = wrapModeS; + globalUniformsChanged = true; + } + + if (wrapModeT != CurrentWrapModeT) + { + FlushCurrentBatch(FlushBatchSource.BindTexture); + + CurrentWrapModeT = wrapModeT; + globalUniformsChanged = true; + } + } + /// /// Unbinds any bound texture. /// @@ -1252,8 +1263,6 @@ namespace osu.Framework.Graphics.Rendering private void validateUniformLayout() { - 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 8f5949ce1..25caaa447 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 @@ namespace osu.Framework.Graphics.Veldrid.Buffers 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 2bd4cccf7..2d72dd26b 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 @@ namespace osu.Framework.Graphics.Veldrid.Buffers public VeldridUniformBuffer(VeldridRenderer renderer) { + Trace.Assert(ThreadSafety.IsDrawThread); + this.renderer = renderer; storages.Add(new VeldridUniformBufferStorage(this.renderer)); }