Compare commits

..

10 Commits

Author SHA1 Message Date
fc26189fe1 Eliminate redundant multiplications by gl_FragCoord.w on the shader (#4578)
* Eliminate redundant multiplications by gl_FragCoord.w on the shader

* Shader cache version bump
2023-05-19 11:52:31 -03:00
a40c90e7dd nuget: bump DynamicData from 7.13.5 to 7.13.8 (#5001)
Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 7.13.5 to 7.13.8.
- [Release notes](https://github.com/reactiveui/DynamicData/releases)
- [Changelog](https://github.com/reactivemarbles/DynamicData/blob/main/ReleaseNotes.md)
- [Commits](https://github.com/reactiveui/DynamicData/compare/7.13.5...7.13.8)

---
updated-dependencies:
- dependency-name: DynamicData
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-19 06:52:44 +02:00
f864a49014 Fix Vulkan blit-like operations swizzle (#5003) 2023-05-18 18:16:03 -03:00
ecbf303266 GPU: Avoid using garbage size for non-cb0 storage buffers (#4999)
* GPU: Avoid using garbage size for non-cb0 storage buffers

In the depths area, Tears of the Kingdom uses a global memory access with address on constant buffer slot 6. This isn't standard and thus doesn't actually have a size 8 bytes after it, so we were reading back a garbage size that ended up very large (at least in version 1.1.0), and would synchronize a lot of data per frame.

This PR makes storage buffers created from addresses outside constant buffer slot 0 get their size as the number of bytes remaining in the GPU mapping starting at the given virtual address. This should bound the buffer to a reasonable size, and ideally stop it crossing into other memory.

* Limit max size

* Add TODO

* Feedback
2023-05-18 08:56:34 +02:00
b3bf05356b ava: Fix crash when extracting sections from NCA with no data section (#5002)
Tested against Omega Strickers.
2023-05-17 19:27:49 +00:00
cb4b58052f Start GPU performance counter at 0 instead of host GPU value (#4992)
* Start performance counter at 0 instead of host perf counter value

* whitespace

* init _firstTimestamp in constructer per feedback

* change comment

* punctuation

* address feedback

* revise comment
2023-05-17 15:38:59 -03:00
f8cdd5f484 macos: Fix relaunch with updater when no arguments were provided to the emulator (#4987)
The updater still seems to be erroring when replacing installed folder under normal operations.
2023-05-17 19:02:15 +02:00
22202be394 [GUI] Fix always hide cursor mode not hiding the cursor until it was moved (#4927)
* gtk: Add missing isMouseInClient check for hide-cursor

* ava: Add missing events and default isCursorInRenderer to true

This is necessary because we don't receive a initial PointerEnter event for some reason.
2023-05-14 16:34:31 +02:00
17ba217940 Vulkan: Device map buffers written more than flushed (#4911) 2023-05-13 15:15:05 +02:00
aae4595bdb Add timeout of 35 minutes to workflow jobs (#4928) 2023-05-13 13:24:43 +02:00
19 changed files with 258 additions and 42 deletions

View File

@ -27,6 +27,7 @@ jobs:
build:
name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }})
runs-on: ${{ matrix.os }}
timeout-minutes: 35
strategy:
matrix:
os: [ubuntu-latest, macOS-latest, windows-latest]
@ -109,6 +110,7 @@ jobs:
build_macos:
name: macOS Universal (${{ matrix.configuration }})
runs-on: ubuntu-latest
timeout-minutes: 35
strategy:
matrix:
configuration: [ Debug, Release ]
@ -150,4 +152,4 @@ jobs:
with:
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
path: "publish_ava/*.tar.gz"
if: github.event_name == 'pull_request'
if: github.event_name == 'pull_request'

View File

@ -12,6 +12,7 @@ concurrency: flatpak-release
jobs:
release:
timeout-minutes: 35
runs-on: ubuntu-latest
env:

View File

@ -7,6 +7,7 @@ jobs:
pr_comment:
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
runs-on: ubuntu-latest
timeout-minutes: 35
steps:
- uses: actions/github-script@v6
with:
@ -65,4 +66,4 @@ jobs:
} else {
core.info(`Creating a comment`);
await github.rest.issues.createComment({repo, owner, issue_number, body});
}
}

View File

@ -46,6 +46,7 @@ jobs:
release:
name: Release ${{ matrix.OS_NAME }}
runs-on: ${{ matrix.os }}
timeout-minutes: 35
strategy:
matrix:
os: [ ubuntu-latest, windows-latest ]
@ -143,13 +144,14 @@ jobs:
macos_release:
name: Release MacOS universal
runs-on: ubuntu-latest
timeout-minutes: 35
steps:
- uses: actions/checkout@v3
- uses: actions/setup-dotnet@v3
with:
global-json-file: global.json
- name: Setup LLVM 14
run: |
wget https://apt.llvm.org/llvm.sh
@ -205,4 +207,4 @@ jobs:
needs: release
with:
ryujinx_version: "1.1.${{ github.run_number }}"
secrets: inherit
secrets: inherit

View File

@ -13,7 +13,7 @@
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="Concentus" Version="1.1.7" />
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
<PackageVersion Include="DynamicData" Version="7.13.5" />
<PackageVersion Include="DynamicData" Version="7.13.8" />
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />

View File

@ -36,4 +36,9 @@ sleep 1
# Now replace and reopen.
rm -rf "$INSTALL_DIRECTORY"
mv "$NEW_APP_DIRECTORY" "$INSTALL_DIRECTORY"
open -a "$INSTALL_DIRECTORY" --args "$APP_ARGUMENTS"
if [ "$#" -le 3 ]; then
open -a "$INSTALL_DIRECTORY"
else
open -a "$INSTALL_DIRECTORY" --args "$APP_ARGUMENTS"
fi

View File

@ -86,7 +86,7 @@ namespace Ryujinx.Ava
private KeyboardHotkeyState _prevHotkeyState;
private long _lastCursorMoveTime;
private bool _isCursorInRenderer;
private bool _isCursorInRenderer = true;
private bool _isStopped;
private bool _isActive;
@ -160,7 +160,9 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.HideCursor.Event += HideCursorState_Changed;
_topLevel.PointerMoved += TopLevel_PointerMoved;
_topLevel.PointerMoved += TopLevel_PointerEnterOrMoved;
_topLevel.PointerEnter += TopLevel_PointerEnterOrMoved;
_topLevel.PointerLeave += TopLevel_PointerLeave;
if (OperatingSystem.IsWindows())
{
@ -183,7 +185,7 @@ namespace Ryujinx.Ava
_gpuCancellationTokenSource = new CancellationTokenSource();
}
private void TopLevel_PointerMoved(object sender, PointerEventArgs e)
private void TopLevel_PointerEnterOrMoved(object sender, PointerEventArgs e)
{
if (sender is MainWindow window)
{
@ -201,6 +203,12 @@ namespace Ryujinx.Ava
}
}
}
private void TopLevel_PointerLeave(object sender, PointerEventArgs e)
{
_isCursorInRenderer = false;
}
private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs<int> e)
{
_renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
@ -446,7 +454,9 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel;
ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing;
_topLevel.PointerMoved -= TopLevel_PointerMoved;
_topLevel.PointerMoved -= TopLevel_PointerEnterOrMoved;
_topLevel.PointerEnter -= TopLevel_PointerEnterOrMoved;
_topLevel.PointerLeave -= TopLevel_PointerLeave;
_gpuCancellationTokenSource.Cancel();
_gpuCancellationTokenSource.Dispose();

View File

@ -193,7 +193,7 @@ namespace Ryujinx.Ava.Common
if (nca.Header.ContentType == NcaContentType.Program)
{
int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
if (nca.Header.GetFsHeader(dataIndex).IsPatchSection())
if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection())
{
patchNca = nca;
}

View File

@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
public const int PrimitiveRestartStateIndex = 12;
public const int RenderTargetStateIndex = 27;
private const ulong MaxUnknownStorageSize = 0x100000;
private readonly GpuContext _context;
private readonly GpuChannel _channel;
private readonly DeviceStateWithShadow<ThreedClassState> _state;
@ -356,7 +358,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
SbDescriptor sbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(sbDescAddress);
_channel.BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags);
uint size;
if (sb.SbCbSlot == 0)
{
// Only trust the SbDescriptor size if it comes from slot 0.
size = (uint)sbDescriptor.Size;
}
else
{
// TODO: Use full mapped size and somehow speed up buffer sync.
size = (uint)_channel.MemoryManager.GetMappedSize(sbDescriptor.PackAddress(), MaxUnknownStorageSize);
}
_channel.BufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), size, sb.Flags);
}
}
}

View File

@ -99,6 +99,7 @@ namespace Ryujinx.Graphics.Gpu
private bool _pendingSync;
private long _modifiedSequence;
private ulong _firstTimestamp;
/// <summary>
/// Creates a new instance of the GPU emulation context.
@ -123,6 +124,8 @@ namespace Ryujinx.Graphics.Gpu
DeferredActions = new Queue<Action>();
PhysicalMemoryRegistry = new ConcurrentDictionary<ulong, PhysicalMemory>();
_firstTimestamp = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds);
}
/// <summary>
@ -217,7 +220,8 @@ namespace Ryujinx.Graphics.Gpu
/// <returns>The current GPU timestamp</returns>
public ulong GetTimestamp()
{
ulong ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds);
// Guest timestamp will start at 0, instead of host value.
ulong ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds) - _firstTimestamp;
if (GraphicsConfig.FastGpuTime)
{

View File

@ -637,6 +637,33 @@ namespace Ryujinx.Graphics.Gpu.Memory
return UnpackPaFromPte(pte) + (va & PageMask);
}
/// <summary>
/// Translates a GPU virtual address and returns the number of bytes that are mapped after it.
/// </summary>
/// <param name="va">GPU virtual address to be translated</param>
/// <param name="maxSize">Maximum size in bytes to scan</param>
/// <returns>Number of bytes, 0 if unmapped</returns>
public ulong GetMappedSize(ulong va, ulong maxSize)
{
if (!ValidateAddress(va))
{
return 0;
}
ulong startVa = va;
ulong endVa = va + maxSize;
ulong pte = GetPte(va);
while (pte != PteUnmapped && va < endVa)
{
va += PageSize - (va & PageMask);
pte = GetPte(va);
}
return Math.Min(maxSize, va - startVa);
}
/// <summary>
/// Gets the kind of a given memory page.
/// This might indicate the type of resource that can be allocated on the page, and also texture tiling.

View File

@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 4892;
private const uint CodeGenVersion = 4578;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";

View File

@ -20,6 +20,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
GlobalToStorage.RunPass(blocks[blkIndex], config, ref sbUseMask, ref ubeUseMask);
BindlessToIndexed.RunPass(blocks[blkIndex], config);
BindlessElimination.RunPass(blocks[blkIndex], config);
// FragmentCoord only exists on fragment shaders, so we don't need to check other stages.
if (config.Stage == ShaderStage.Fragment)
{
EliminateMultiplyByFragmentCoordW(blocks[blkIndex]);
}
}
config.SetAccessibleBufferMasks(sbUseMask, ubeUseMask);
@ -281,6 +287,75 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
return modified;
}
private static void EliminateMultiplyByFragmentCoordW(BasicBlock block)
{
foreach (INode node in block.Operations)
{
if (node is Operation operation)
{
EliminateMultiplyByFragmentCoordW(operation);
}
}
}
private static void EliminateMultiplyByFragmentCoordW(Operation operation)
{
// We're looking for the pattern:
// y = x * gl_FragCoord.w
// v = y * (1.0 / gl_FragCoord.w)
// Then we transform it into:
// v = x
// This pattern is common on fragment shaders due to the way how perspective correction is done.
// We are expecting a multiplication by the reciprocal of gl_FragCoord.w.
if (operation.Inst != (Instruction.FP32 | Instruction.Multiply))
{
return;
}
Operand lhs = operation.GetSource(0);
Operand rhs = operation.GetSource(1);
// Check LHS of the the main multiplication operation. We expect an input being multiplied by gl_FragCoord.w.
if (!(lhs.AsgOp is Operation attrMulOp) || attrMulOp.Inst != (Instruction.FP32 | Instruction.Multiply))
{
return;
}
Operand attrMulLhs = attrMulOp.GetSource(0);
Operand attrMulRhs = attrMulOp.GetSource(1);
// LHS should be any input, RHS should be exactly gl_FragCoord.w.
if (!Utils.IsInputLoad(attrMulLhs.AsgOp) || !Utils.IsInputLoad(attrMulRhs.AsgOp, IoVariable.FragmentCoord, 3))
{
return;
}
// RHS of the main multiplication should be a reciprocal operation (1.0 / x).
if (!(rhs.AsgOp is Operation reciprocalOp) || reciprocalOp.Inst != (Instruction.FP32 | Instruction.Divide))
{
return;
}
Operand reciprocalLhs = reciprocalOp.GetSource(0);
Operand reciprocalRhs = reciprocalOp.GetSource(1);
// Check if the divisor is a constant equal to 1.0.
if (reciprocalLhs.Type != OperandType.Constant || reciprocalLhs.AsFloat() != 1.0f)
{
return;
}
// Check if the dividend is gl_FragCoord.w.
if (!Utils.IsInputLoad(reciprocalRhs.AsgOp, IoVariable.FragmentCoord, 3))
{
return;
}
// If everything matches, we can replace the operation with the input load result.
operation.TurnIntoCopy(attrMulLhs);
}
private static void RemoveNode(BasicBlock block, LinkedListNode<INode> llNode)
{
// Remove a node from the nodes list, and also remove itself

View File

@ -4,6 +4,35 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{
static class Utils
{
public static bool IsInputLoad(INode node)
{
return (node is Operation operation) &&
operation.Inst == Instruction.Load &&
operation.StorageKind == StorageKind.Input;
}
public static bool IsInputLoad(INode node, IoVariable ioVariable, int elemIndex)
{
if (!(node is Operation operation) ||
operation.Inst != Instruction.Load ||
operation.StorageKind != StorageKind.Input ||
operation.SourcesCount != 2)
{
return false;
}
Operand ioVariableSrc = operation.GetSource(0);
if (ioVariableSrc.Type != OperandType.Constant || (IoVariable)ioVariableSrc.Value != ioVariable)
{
return false;
}
Operand elemIndexSrc = operation.GetSource(1);
return elemIndexSrc.Type == OperandType.Constant && elemIndexSrc.Value == elemIndex;
}
private static Operation FindBranchSource(BasicBlock block)
{
foreach (BasicBlock sourceBlock in block.Predecessors)

View File

@ -56,6 +56,7 @@ namespace Ryujinx.Graphics.Vulkan
private int _writeCount;
private int _flushCount;
private int _flushTemp;
private int _lastFlushWrite = -1;
private ReaderWriterLock _flushLock;
private FenceHolder _flushFence;
@ -200,14 +201,21 @@ namespace Ryujinx.Graphics.Vulkan
{
if (_baseType == BufferAllocationType.Auto)
{
if (_writeCount >= WriteCountThreshold || _setCount >= SetCountThreshold || _flushCount >= FlushCountThreshold)
// When flushed, wait for a bit more info to make a decision.
bool wasFlushed = _flushTemp > 0;
int multiplier = wasFlushed ? 2 : 0;
if (_writeCount >= (WriteCountThreshold << multiplier) || _setCount >= (SetCountThreshold << multiplier) || _flushCount >= (FlushCountThreshold << multiplier))
{
if (_flushCount > 0 || _flushTemp-- > 0)
{
// Buffers that flush should ideally be mapped in host address space for easy copies.
// If the buffer is large it will do better on GPU memory, as there will be more writes than data flushes (typically individual pages).
// If it is small, then it's likely most of the buffer will be flushed so we want it on host memory, as access is cached.
DesiredType = Size > DeviceLocalSizeThreshold ? BufferAllocationType.DeviceLocalMapped : BufferAllocationType.HostMapped;
bool hostMappingSensitive = _gd.Vendor == Vendor.Nvidia;
bool deviceLocalMapped = Size > DeviceLocalSizeThreshold || (wasFlushed && _writeCount > _flushCount * 10 && hostMappingSensitive) || _currentType == BufferAllocationType.DeviceLocalMapped;
DesiredType = deviceLocalMapped ? BufferAllocationType.DeviceLocalMapped : BufferAllocationType.HostMapped;
// It's harder for a buffer that is flushed to revert to another type of mapping.
if (_flushCount > 0)
@ -215,17 +223,18 @@ namespace Ryujinx.Graphics.Vulkan
_flushTemp = 1000;
}
}
else if (_writeCount >= WriteCountThreshold)
else if (_writeCount >= (WriteCountThreshold << multiplier))
{
// Buffers that are written often should ideally be in the device local heap. (Storage buffers)
DesiredType = BufferAllocationType.DeviceLocal;
}
else if (_setCount > SetCountThreshold)
else if (_setCount > (SetCountThreshold << multiplier))
{
// Buffers that have their data set often should ideally be host mapped. (Constant buffers)
DesiredType = BufferAllocationType.HostMapped;
}
_lastFlushWrite = -1;
_flushCount = 0;
_writeCount = 0;
_setCount = 0;
@ -418,7 +427,12 @@ namespace Ryujinx.Graphics.Vulkan
WaitForFlushFence();
_flushCount++;
if (_lastFlushWrite != _writeCount)
{
// If it's on the same page as the last flush, ignore it.
_lastFlushWrite = _writeCount;
_flushCount++;
}
Span<byte> result;

View File

@ -228,7 +228,12 @@ namespace Ryujinx.Graphics.Vulkan
SignalDirty(DirtyFlags.Storage);
}
public void SetTextureAndSampler(CommandBufferScoped cbs, ShaderStage stage, int binding, ITexture texture, ISampler sampler)
public void SetTextureAndSampler(
CommandBufferScoped cbs,
ShaderStage stage,
int binding,
ITexture texture,
ISampler sampler)
{
if (texture is TextureBuffer textureBuffer)
{
@ -251,6 +256,28 @@ namespace Ryujinx.Graphics.Vulkan
SignalDirty(DirtyFlags.Texture);
}
public void SetTextureAndSamplerIdentitySwizzle(
CommandBufferScoped cbs,
ShaderStage stage,
int binding,
ITexture texture,
ISampler sampler)
{
if (texture is TextureView view)
{
view.Storage.InsertWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
_textureRefs[binding] = view.GetIdentityImageView();
_samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler();
SignalDirty(DirtyFlags.Texture);
}
else
{
SetTextureAndSampler(cbs, stage, binding, texture, sampler);
}
}
public void SetUniformBuffers(CommandBuffer commandBuffer, ReadOnlySpan<BufferAssignment> buffers)
{
for (int i = 0; i < buffers.Length; i++)

View File

@ -415,7 +415,7 @@ namespace Ryujinx.Graphics.Vulkan
var sampler = linearFilter ? _samplerLinear : _samplerNearest;
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, sampler);
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, src, sampler);
Span<float> region = stackalloc float[RegionBufferSize / sizeof(float)];
@ -625,7 +625,7 @@ namespace Ryujinx.Graphics.Vulkan
private void BlitDepthStencilDraw(TextureView src, bool isDepth)
{
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, _samplerNearest);
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, src, _samplerNearest);
if (isDepth)
{
@ -1037,7 +1037,7 @@ namespace Ryujinx.Graphics.Vulkan
var srcView = Create2DLayerView(src, srcLayer + z, srcLevel + l, srcFormat);
var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null);
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
_pipeline.SetImage(0, dstView, dstFormat);
int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32;
@ -1177,7 +1177,7 @@ namespace Ryujinx.Graphics.Vulkan
var srcView = Create2DLayerView(src, srcLayer + z, 0, format);
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 0, srcView, null);
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
_pipeline.SetImage(0, dstView, format);
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
@ -1313,7 +1313,7 @@ namespace Ryujinx.Graphics.Vulkan
var srcView = Create2DLayerView(src, srcLayer + z, 0, format);
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, srcView, null);
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, srcView, null);
_pipeline.SetRenderTarget(
((TextureView)dstView).GetView(format).GetImageViewForAttachment(),
(uint)dst.Width,
@ -1384,7 +1384,7 @@ namespace Ryujinx.Graphics.Vulkan
private void CopyMSAspectDraw(TextureView src, bool fromMS, bool isDepth)
{
_pipeline.SetTextureAndSampler(ShaderStage.Fragment, 0, src, _samplerNearest);
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Fragment, 0, src, _samplerNearest);
if (isDepth)
{

View File

@ -1098,6 +1098,11 @@ namespace Ryujinx.Graphics.Vulkan
_descriptorSetUpdater.SetTextureAndSampler(Cbs, stage, binding, texture, sampler);
}
public void SetTextureAndSamplerIdentitySwizzle(ShaderStage stage, int binding, ITexture texture, ISampler sampler)
{
_descriptorSetUpdater.SetTextureAndSamplerIdentitySwizzle(Cbs, stage, binding, texture, sampler);
}
public void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers)
{
PauseTransformFeedbackInternal();

View File

@ -321,27 +321,27 @@ namespace Ryujinx.Ui
_toggleDockedMode = toggleDockedMode;
if (ConfigurationState.Instance.Hid.EnableMouse.Value)
if (_isMouseInClient)
{
if (_isMouseInClient)
if (ConfigurationState.Instance.Hid.EnableMouse.Value)
{
Window.Cursor = _invisibleCursor;
}
}
else
{
switch (_hideCursorMode)
else
{
case HideCursorMode.OnIdle:
long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime;
Window.Cursor = (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency) ? _invisibleCursor : null;
break;
case HideCursorMode.Always:
Window.Cursor = _invisibleCursor;
break;
case HideCursorMode.Never:
Window.Cursor = null;
break;
switch (_hideCursorMode)
{
case HideCursorMode.OnIdle:
long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime;
Window.Cursor = (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency) ? _invisibleCursor : null;
break;
case HideCursorMode.Always:
Window.Cursor = _invisibleCursor;
break;
case HideCursorMode.Never:
Window.Cursor = null;
break;
}
}
}
}