Skip to content

Fix texture wrapping not updating on consecutive WhitePixel use #6350

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions osu.Framework.Tests/Graphics/RendererTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. 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));
}
}
}
101 changes: 55 additions & 46 deletions osu.Framework.Tests/Graphics/ShaderStorageBufferObjectStackTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,41 @@
// 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
{
public class ShaderStorageBufferObjectStackTest
{
private const int size = 10;

private ShaderStorageBufferObjectStack<int> stack = null!;
private ShaderStorageBufferObjectStack<TestUniformData> stack = null!;

[SetUp]
public void Setup()
{
stack = new ShaderStorageBufferObjectStack<int>(new DummyRenderer(), 2, size);
stack = new ShaderStorageBufferObjectStack<TestUniformData>(new DummyRenderer(), 2, size);
}

[Test]
public void TestBufferMustBeAtLeast2Elements()
{
Assert.Throws<ArgumentOutOfRangeException>(() => _ = new ShaderStorageBufferObjectStack<int>(new DummyRenderer(), 1, 100));
Assert.Throws<ArgumentOutOfRangeException>(() => _ = new ShaderStorageBufferObjectStack<int>(new DummyRenderer(), 100, 1));
Assert.DoesNotThrow(() => _ = new ShaderStorageBufferObjectStack<int>(new DummyRenderer(), 2, 100));
Assert.DoesNotThrow(() => _ = new ShaderStorageBufferObjectStack<int>(new DummyRenderer(), 100, 2));
Assert.Throws<ArgumentOutOfRangeException>(() => _ = new ShaderStorageBufferObjectStack<TestUniformData>(new DummyRenderer(), 1, 100));
Assert.Throws<ArgumentOutOfRangeException>(() => _ = new ShaderStorageBufferObjectStack<TestUniformData>(new DummyRenderer(), 100, 1));
Assert.DoesNotThrow(() => _ = new ShaderStorageBufferObjectStack<TestUniformData>(new DummyRenderer(), 2, 100));
Assert.DoesNotThrow(() => _ = new ShaderStorageBufferObjectStack<TestUniformData>(new DummyRenderer(), 100, 2));
}

[Test]
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]
Expand All @@ -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]
Expand All @@ -63,26 +65,26 @@ 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));
}
}

[Test]
public void TestPopEntireBuffer()
{
for (int i = 0; i < size; i++)
stack.Push(i);
stack.Push(new TestUniformData { Int = i });

var firstBuffer = stack.CurrentBuffer;

for (int i = size - 1; i >= 0; i--)
{
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();
}
}
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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();
Expand All @@ -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]
Expand All @@ -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;
}
}
}
16 changes: 6 additions & 10 deletions osu.Framework.Tests/Visual/Graphics/TestSceneVertexBatching.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -18,8 +16,10 @@ namespace osu.Framework.Tests.Visual.Graphics
{
public partial class TestSceneVertexBatching : FrameworkTestScene
{
[Resolved]
private GameHost host { get; set; } = null!;
/// <summary>
/// Max number of quads per batch in the default quad batch (Renderer.defaultQuadBatch).
/// </summary>
private const int max_boxes_per_batch = 100;

[Test]
public void TestBatchUntilOverflow()
Expand All @@ -28,16 +28,14 @@ public void TestBatchUntilOverflow()
{
Clear();

int boxesPerBatch = host.Renderer.DefaultQuadBatch.Size;

Add(new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
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,
Expand All @@ -55,16 +53,14 @@ public void TestBatchWithFlushes()
{
Clear();

int boxesPerBatch = host.Renderer.DefaultQuadBatch.Size;

Add(new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -20,6 +22,8 @@ internal class GLShaderStorageBufferObject<TData> : IShaderStorageBufferObject<T

public GLShaderStorageBufferObject(GLRenderer renderer, int uboSize, int ssboSize)
{
Trace.Assert(ThreadSafety.IsDrawThread);

Id = GL.GenBuffer();
Size = renderer.UseStructuredBuffers ? ssboSize : uboSize;
data = new TData[Size];
Expand Down
Loading
Loading