Compare commits

...

2 Commits

Author SHA1 Message Date
gdkchan
550fd4a733 Simplify resolution scale updates (#5541) 2023-08-14 13:57:39 -03:00
riperiperi
33f544fd92 GPU: Track basic buffer copies that modify texture memory (#5554)
This branch changes the buffer copy fast path to notify memory tracking for all resources that aren't buffers. This fixes cases where games would copy buffer data directly into texture memory, which before would only work if the texture did not already exist. I imagine this happens when the guest driver is moving data between allocations or uploading it.

Since this only affects the fast path, cases where the source data has been modified from GPU (fast path copy destination doesn't count) will still fail to notify the texture, though I don't imagine games will do this. This should be resolved in future.

This should fix some texture issues with guest OpenGL games on switch, such as Dragon Quest Builders.

This may also be useful in future for games that move shader data around memory, if we end up using memory tracking for those.
2023-08-14 08:41:11 +02:00
6 changed files with 34 additions and 45 deletions

View File

@@ -61,8 +61,6 @@ namespace Ryujinx.Graphics.Gpu.Image
private int _textureBufferIndex;
private readonly float[] _scales;
private bool _scaleChanged;
private int _lastFragmentTotal;
/// <summary>
@@ -72,14 +70,12 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="channel">The GPU channel that the texture bindings manager belongs to</param>
/// <param name="texturePoolCache">Texture pools cache used to get texture pools from</param>
/// <param name="samplerPoolCache">Sampler pools cache used to get sampler pools from</param>
/// <param name="scales">Array where the scales for the currently bound textures are stored</param>
/// <param name="isCompute">True if the bindings manager is used for the compute engine</param>
public TextureBindingsManager(
GpuContext context,
GpuChannel channel,
TexturePoolCache texturePoolCache,
SamplerPoolCache samplerPoolCache,
float[] scales,
bool isCompute)
{
_context = context;
@@ -87,7 +83,6 @@ namespace Ryujinx.Graphics.Gpu.Image
_texturePoolCache = texturePoolCache;
_samplerPoolCache = samplerPoolCache;
_scales = scales;
_isCompute = isCompute;
int stages = isCompute ? 1 : Constants.ShaderStages;
@@ -239,12 +234,7 @@ namespace Ryujinx.Graphics.Gpu.Image
}
}
if (result != _scales[index])
{
_scaleChanged = true;
_scales[index] = result;
}
_context.SupportBufferUpdater.UpdateRenderScale(index, result);
return changed;
}
@@ -290,11 +280,6 @@ namespace Ryujinx.Graphics.Gpu.Image
// - Vertex stage has bindings that require scale.
// - Fragment stage binding count has been updated since last render scale update.
_scaleChanged = true;
}
if (_scaleChanged)
{
if (!_isCompute)
{
total += fragmentTotal; // Add the fragment bindings to the total.
@@ -302,9 +287,7 @@ namespace Ryujinx.Graphics.Gpu.Image
_lastFragmentTotal = fragmentTotal;
_context.SupportBufferUpdater.UpdateRenderScale(_scales, total, fragmentTotal);
_scaleChanged = false;
_context.SupportBufferUpdater.UpdateRenderScaleFragmentCount(total, fragmentTotal);
}
}

View File

@@ -44,11 +44,8 @@ namespace Ryujinx.Graphics.Gpu.Image
TexturePoolCache texturePoolCache = new(context);
SamplerPoolCache samplerPoolCache = new(context);
float[] scales = new float[64];
new Span<float>(scales).Fill(1f);
_cpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, scales, isCompute: true);
_gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, scales, isCompute: false);
_cpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, isCompute: true);
_gpBindingsManager = new TextureBindingsManager(context, channel, texturePoolCache, samplerPoolCache, isCompute: false);
_texturePoolCache = texturePoolCache;
_samplerPoolCache = samplerPoolCache;

View File

@@ -344,7 +344,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
// Optimization: If the data being copied is already in memory, then copy it directly instead of flushing from GPU.
dstBuffer.ClearModified(dstAddress, size);
memoryManager.Physical.WriteUntracked(dstAddress, memoryManager.Physical.GetSpan(srcAddress, (int)size));
memoryManager.Physical.WriteTrackedResource(dstAddress, memoryManager.Physical.GetSpan(srcAddress, (int)size), ResourceKind.Buffer);
}
}

View File

@@ -236,6 +236,18 @@ namespace Ryujinx.Graphics.Gpu.Memory
_cpuMemory.WriteUntracked(address, data);
}
/// <summary>
/// Writes data to the application process, triggering a precise memory tracking event.
/// </summary>
/// <param name="address">Address to write into</param>
/// <param name="data">Data to be written</param>
/// <param name="kind">Kind of the resource being written, which will not be signalled as CPU modified</param>
public void WriteTrackedResource(ulong address, ReadOnlySpan<byte> data, ResourceKind kind)
{
_cpuMemory.SignalMemoryTracking(address, (ulong)data.Length, true, precise: true, exemptId: (int)kind);
_cpuMemory.WriteUntracked(address, data);
}
/// <summary>
/// Writes data to the application process.
/// </summary>

View File

@@ -136,33 +136,30 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <summary>
/// Updates the render scales for shader input textures or images.
/// </summary>
/// <param name="scales">Scale values</param>
/// <param name="index">Index of the scale</param>
/// <param name="scale">Scale value</param>
public void UpdateRenderScale(int index, float scale)
{
if (_data.RenderScale[1 + index].X != scale)
{
_data.RenderScale[1 + index].X = scale;
DirtyRenderScale(1 + index, 1);
}
}
/// <summary>
/// Updates the render scales for shader input textures or images.
/// </summary>
/// <param name="totalCount">Total number of scales across all stages</param>
/// <param name="fragmentCount">Total number of scales on the fragment shader stage</param>
public void UpdateRenderScale(ReadOnlySpan<float> scales, int totalCount, int fragmentCount)
public void UpdateRenderScaleFragmentCount(int totalCount, int fragmentCount)
{
bool changed = false;
for (int index = 0; index < totalCount; index++)
{
if (_data.RenderScale[1 + index].X != scales[index])
{
_data.RenderScale[1 + index].X = scales[index];
changed = true;
}
}
// Only update fragment count if there are scales after it for the vertex stage.
if (fragmentCount != totalCount && fragmentCount != _data.FragmentRenderScaleCount.X)
{
_data.FragmentRenderScaleCount.X = fragmentCount;
DirtyFragmentRenderScaleCount();
}
if (changed)
{
DirtyRenderScale(0, 1 + totalCount);
}
}
/// <summary>
@@ -172,7 +169,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="isBgra">True if the format is BGRA< false otherwise</param>
public void SetRenderTargetIsBgra(int index, bool isBgra)
{
bool isBgraChanged = (_data.FragmentIsBgra[index].X != 0) != isBgra;
bool isBgraChanged = _data.FragmentIsBgra[index].X != 0 != isBgra;
if (isBgraChanged)
{

View File

@@ -376,7 +376,7 @@ namespace Ryujinx.Graphics.Vulkan
var program = _program;
var bindingSegments = program.BindingSegments[setIndex];
if (bindingSegments.Length == 0 && setIndex != PipelineBase.UniformSetIndex)
if (bindingSegments.Length == 0)
{
return;
}