Merge pull request #6350 from smoogipoo/fix-whitepixel-texture-wrap

Fix texture wrapping not updating on consecutive WhitePixel use
This commit is contained in:
Dean Herbert
2024-08-08 15:42:12 +09:00
committed by GitHub
13 changed files with 240 additions and 273 deletions

View File

@@ -0,0 +1,30 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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));
}
}
}

View File

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

View File

@@ -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!;
/// <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()
@@ -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,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,245 +1,142 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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
{
/// <summary>
/// An <see cref="IRenderer"/> that does nothing. May be used for tests that don't have a visual output.
/// </summary>
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<Rgba32> TakeScreenshot()
=> new Image<Rgba32>(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<T>(Action<T> disposalAction, T target) => disposalAction(target);
Image<Rgba32> IRenderer.TakeScreenshot() => new Image<Rgba32>(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<TVertex> CreateLinearBatch<TVertex>(int size, int maxBuffers, PrimitiveTopology topology) where TVertex : unmanaged, IEquatable<TVertex>, IVertex
protected override IVertexBatch<TVertex> CreateLinearBatch<TVertex>(int size, int maxBuffers, PrimitiveTopology topology)
=> new DummyVertexBatch<TVertex>();
public IVertexBatch<TVertex> CreateQuadBatch<TVertex>(int size, int maxBuffers) where TVertex : unmanaged, IEquatable<TVertex>, IVertex
protected override IVertexBatch<TVertex> CreateQuadBatch<TVertex>(int size, int maxBuffers)
=> new DummyVertexBatch<TVertex>();
public IUniformBuffer<TData> CreateUniformBuffer<TData>() where TData : unmanaged, IEquatable<TData>
protected override IUniformBuffer<TData> CreateUniformBuffer<TData>()
=> new DummyUniformBuffer<TData>();
public IShaderStorageBufferObject<TData> CreateShaderStorageBufferObject<TData>(int uboSize, int ssboSize) where TData : unmanaged, IEquatable<TData>
protected override IShaderStorageBufferObject<TData> CreateShaderStorageBufferObject<TData>(int uboSize, int ssboSize)
=> new DummyShaderStorageBufferObject<TData>(ssboSize);
void IRenderer.SetUniform<T>(IUniformWithValue<T> 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<TexturedVertex2D> IRenderer.DefaultQuadBatch => new DummyVertexBatch<TexturedVertex2D>();
void IRenderer.PushQuadBatch(IVertexBatch<TexturedVertex2D> quadBatch)
protected internal override void SwapBuffers()
{
}
void IRenderer.PopQuadBatch()
protected internal override void WaitUntilIdle()
{
}
event Action<Texture>? IRenderer.TextureCreated
protected internal override void WaitUntilNextFrameReady()
{
add
{
}
remove
{
}
}
Texture[] IRenderer.GetAllTextures() => Array.Empty<Texture>();
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<T>(IUniformWithValue<T> uniform)
{
}
protected override void SetUniformBufferImplementation(string blockName, IUniformBuffer buffer)
{
}
public override IFrameBuffer CreateFrameBuffer(RenderBufferFormat[]? renderBufferFormats = null, TextureFilteringMode filteringMode = TextureFilteringMode.Linear)
=> new DummyFrameBuffer(this);
}
}

View File

@@ -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;
}
}
/// <summary>
/// Unbinds any bound texture.
/// </summary>
@@ -1252,8 +1263,6 @@ namespace osu.Framework.Graphics.Rendering
private void validateUniformLayout<TData>()
{
Trace.Assert(ThreadSafety.IsDrawThread);
if (validUboTypes.Contains(typeof(TData)))
return;

View File

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

View File

@@ -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<TData>(this.renderer));
}