Skip to content
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

Fix texture uploads getting corrupted on Metal with non-Apple GPUs #5924

Merged
merged 2 commits into from
Jul 12, 2023
Merged
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
36 changes: 35 additions & 1 deletion osu.Framework/Graphics/Veldrid/VeldridRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ protected internal override bool AllowTearing
public CommandList Commands { get; private set; } = null!;
public CommandList BufferUpdateCommands { get; private set; } = null!;

public CommandList TextureUpdateCommands { get; private set; } = null!;

private bool beganTextureUpdateCommands;

/// <summary>
/// A list of fences which tracks in-flight frames for the purpose of knowing the last completed frame.
/// This is tracked for the purpose of exposing <see cref="LatestCompletedFrameIndex"/>.
Expand Down Expand Up @@ -213,6 +217,7 @@ protected override void Initialise(IGraphicsSurface graphicsSurface)

Commands = Factory.CreateCommandList();
BufferUpdateCommands = Factory.CreateCommandList();
TextureUpdateCommands = Factory.CreateCommandList();

pipeline.Outputs = Device.SwapchainFramebuffer.OutputDescription;
}
Expand Down Expand Up @@ -353,9 +358,11 @@ protected override bool SetTextureImplementation(INativeTexture? texture, int un
public void UpdateTexture<T>(global::Veldrid.Texture texture, int x, int y, int width, int height, int level, ReadOnlySpan<T> data)
where T : unmanaged
{
ensureTextureUploadCommandsBegan();

var staging = stagingTexturePool.Get(width, height, texture.Format);
Device.UpdateTexture(staging, data, 0, 0, 0, (uint)width, (uint)height, 1, (uint)level, 0);
BufferUpdateCommands.CopyTexture(staging, 0, 0, 0, 0, 0, texture, (uint)x, (uint)y, 0, (uint)level, 0, (uint)width, (uint)height, 1, 1);
TextureUpdateCommands.CopyTexture(staging, 0, 0, 0, 0, 0, texture, (uint)x, (uint)y, 0, (uint)level, 0, (uint)width, (uint)height, 1, 1);
}

/// <summary>
Expand Down Expand Up @@ -488,6 +495,13 @@ public void BindUniformBuffer(string blockName, IVeldridUniformBuffer veldridBuf

public void DrawVertices(PrimitiveTopology type, int indexStart, int indicesCount)
{
// normally we would flush/submit all texture upload commands at the end of the frame, since no actual rendering by the GPU will happen until then,
// but turns out on macOS with non-apple GPU, this results in rendering corruption.
// flushing the texture upload commands here before a draw call fixes the corruption, and there's no explanation as to why that's the case,
// but there is nothing to be lost in flushing here except for a frame that contains many sprites with Texture.BypassTextureUploadQueue = true.
// until that appears to be problem, let's just flush here.
flushTextureUploadCommands();

var veldridShader = (VeldridShader)Shader!;

pipeline.PrimitiveTopology = type;
Expand Down Expand Up @@ -539,6 +553,26 @@ public void DrawVertices(PrimitiveTopology type, int indexStart, int indicesCoun
Commands.DrawIndexed((uint)indicesCount, 1, (uint)indexStart, 0, 0);
}

private void ensureTextureUploadCommandsBegan()
{
if (beganTextureUpdateCommands)
return;

TextureUpdateCommands.Begin();
beganTextureUpdateCommands = true;
}

private void flushTextureUploadCommands()
{
if (!beganTextureUpdateCommands)
return;

TextureUpdateCommands.End();
Device.SubmitCommands(TextureUpdateCommands);

beganTextureUpdateCommands = false;
}

/// <summary>
/// Checks whether the given frame buffer is currently bound.
/// </summary>
Expand Down