Compare commits

...

4 commits

Author SHA1 Message Date
sunshineinabox
b8a8e8d96a
Merge 48f9ada59b into 1b9656e960 2024-12-13 11:59:29 +01:00
Daenorth
1b9656e960
Norwegian Translation (#338)
Some checks failed
Canary release job / Create tag (push) Has been cancelled
Canary release job / Release for linux-arm64 (push) Has been cancelled
Canary release job / Release for linux-x64 (push) Has been cancelled
Canary release job / Release for win-x64 (push) Has been cancelled
Canary release job / Release MacOS universal (push) Has been cancelled
2024-12-13 03:56:20 -06:00
Hack茶ん
8994e7476c
Korean Former Maintainers & About description translations (#371) 2024-12-13 03:54:07 -06:00
sunshineinabox
48f9ada59b Support VK_EXT_extended_dynamic_state and VK_EXT_extended_dynamic_state2 2024-11-21 17:53:52 -08:00
28 changed files with 2298 additions and 666 deletions

View file

@ -50,6 +50,10 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsViewportSwizzle;
public readonly bool SupportsIndirectParameters;
public readonly bool SupportsDepthClipControl;
public readonly bool SupportsExtendedDynamicState;
public readonly bool SupportsExtendedDynamicState2;
public readonly bool SupportsLogicOpDynamicState;
public readonly bool SupportsPatchControlPointsDynamicState;
public readonly int UniformBufferSetIndex;
public readonly int StorageBufferSetIndex;
@ -118,6 +122,10 @@ namespace Ryujinx.Graphics.GAL
bool supportsViewportSwizzle,
bool supportsIndirectParameters,
bool supportsDepthClipControl,
bool supportsExtendedDynamicState,
bool supportsExtendedDynamicState2,
bool supportsLogicOpDynamicState,
bool supportsPatchControlPointsDynamicState,
int uniformBufferSetIndex,
int storageBufferSetIndex,
int textureSetIndex,
@ -180,6 +188,10 @@ namespace Ryujinx.Graphics.GAL
SupportsViewportSwizzle = supportsViewportSwizzle;
SupportsIndirectParameters = supportsIndirectParameters;
SupportsDepthClipControl = supportsDepthClipControl;
SupportsExtendedDynamicState = supportsExtendedDynamicState;
SupportsExtendedDynamicState2 = supportsExtendedDynamicState2;
SupportsLogicOpDynamicState = supportsLogicOpDynamicState;
SupportsPatchControlPointsDynamicState = supportsPatchControlPointsDynamicState;
UniformBufferSetIndex = uniformBufferSetIndex;
StorageBufferSetIndex = storageBufferSetIndex;
TextureSetIndex = textureSetIndex;

View file

@ -2,6 +2,7 @@ namespace Ryujinx.Graphics.GAL
{
public enum Face
{
None = 0,
Front = 0x404,
Back = 0x405,
FrontAndBack = 0x408,

View file

@ -50,9 +50,9 @@ namespace Ryujinx.Graphics.GAL
void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp);
void SetDepthClamp(bool clamp);
void SetDepthMode(DepthMode mode);
void SetDepthTest(DepthTestDescriptor depthTest);
void SetDepthTest(DepthTestDescriptor depthTest, bool signalChange = true);
void SetFaceCulling(bool enable, Face face);
void SetFaceCulling(Face face);
void SetFrontFace(FrontFace frontFace);
@ -75,16 +75,16 @@ namespace Ryujinx.Graphics.GAL
void SetPrimitiveRestart(bool enable, int index);
void SetPrimitiveTopology(PrimitiveTopology topology);
void SetPrimitiveTopology(PrimitiveTopology topology, bool signalChange = true);
void SetProgram(IProgram program);
void SetProgram(IProgram program, bool signalChange = true);
void SetRasterizerDiscard(bool discard);
void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask);
void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask, bool signalChange = true);
void SetRenderTargets(ITexture[] colors, ITexture depthStencil);
void SetScissors(ReadOnlySpan<Rectangle<int>> regions);
void SetScissors(ReadOnlySpan<Rectangle<int>> regions, bool signalChange = true);
void SetStencilTest(StencilTestDescriptor stencilTest);
@ -102,7 +102,7 @@ namespace Ryujinx.Graphics.GAL
void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs);
void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers);
void SetViewports(ReadOnlySpan<Viewport> viewports);
void SetViewports(ReadOnlySpan<Viewport> viewports, bool signalChange = true);
void TextureBarrier();
void TextureBarrierTiled();

View file

@ -3,18 +3,16 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
struct SetFaceCullingCommand : IGALCommand, IGALCommand<SetFaceCullingCommand>
{
public readonly CommandType CommandType => CommandType.SetFaceCulling;
private bool _enable;
private Face _face;
public void Set(bool enable, Face face)
public void Set(Face face)
{
_enable = enable;
_face = face;
}
public static void Run(ref SetFaceCullingCommand command, ThreadedRenderer threaded, IRenderer renderer)
{
renderer.Pipeline.SetFaceCulling(command._enable, command._face);
renderer.Pipeline.SetFaceCulling(command._face);
}
}
}

View file

@ -159,15 +159,15 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand();
}
public void SetDepthTest(DepthTestDescriptor depthTest)
public void SetDepthTest(DepthTestDescriptor depthTest, bool signalChange = true)
{
_renderer.New<SetDepthTestCommand>().Set(depthTest);
_renderer.QueueCommand();
}
public void SetFaceCulling(bool enable, Face face)
public void SetFaceCulling(Face face)
{
_renderer.New<SetFaceCullingCommand>().Set(enable, face);
_renderer.New<SetFaceCullingCommand>().Set(face);
_renderer.QueueCommand();
}
@ -243,13 +243,13 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand();
}
public void SetPrimitiveTopology(PrimitiveTopology topology)
public void SetPrimitiveTopology(PrimitiveTopology topology, bool signalChange = true)
{
_renderer.New<SetPrimitiveTopologyCommand>().Set(topology);
_renderer.QueueCommand();
}
public void SetProgram(IProgram program)
public void SetProgram(IProgram program, bool signalChange = true)
{
_renderer.New<SetProgramCommand>().Set(Ref(program));
_renderer.QueueCommand();
@ -261,7 +261,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand();
}
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask)
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask, bool signalChange = true)
{
_renderer.New<SetRenderTargetColorMasksCommand>().Set(_renderer.CopySpan(componentMask));
_renderer.QueueCommand();
@ -273,7 +273,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand();
}
public void SetScissors(ReadOnlySpan<Rectangle<int>> scissors)
public void SetScissors(ReadOnlySpan<Rectangle<int>> scissors, bool signalChange = true)
{
_renderer.New<SetScissorsCommand>().Set(_renderer.CopySpan(scissors));
_renderer.QueueCommand();
@ -339,7 +339,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand();
}
public void SetViewports(ReadOnlySpan<Viewport> viewports)
public void SetViewports(ReadOnlySpan<Viewport> viewports, bool signalChange = true)
{
_renderer.New<SetViewportsCommand>().Set(_renderer.CopySpan(viewports));
_renderer.QueueCommand();

View file

@ -51,11 +51,12 @@ namespace Ryujinx.Graphics.GAL
public StencilTestDescriptor StencilTest;
public FrontFace FrontFace;
public Face CullMode;
public bool CullEnable;
public PolygonModeMask BiasEnable;
public float LineWidth;
public bool AlphaToCoverageEnable;
public bool AlphaToOneEnable;
// TODO: Polygon mode.
public bool DepthClampEnable;
public bool RasterizerDiscard;
@ -63,6 +64,9 @@ namespace Ryujinx.Graphics.GAL
public bool PrimitiveRestartEnable;
public uint PatchControlPoints;
public float DepthBiasUnits;
public float DepthBiasFactor;
public DepthMode DepthMode;
public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)

View file

@ -854,6 +854,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
enables |= (depthBias.FillEnable ? PolygonModeMask.Fill : 0);
_pipeline.BiasEnable = enables;
_pipeline.DepthBiasUnits = units / 2f;
_pipeline.DepthBiasFactor = factor;
_context.Renderer.Pipeline.SetDepthBias(enables, factor, units / 2f, clamp);
}
@ -1026,7 +1029,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
float width = _state.State.LineWidthSmooth;
bool smooth = _state.State.LineSmoothEnable;
_pipeline.LineWidth = width;
_context.Renderer.Pipeline.SetLineParameters(width, smooth);
}
@ -1196,9 +1198,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
var yControl = _state.State.YControl;
var face = _state.State.FaceState;
_pipeline.CullEnable = face.CullEnable;
_pipeline.CullMode = face.CullFace;
_context.Renderer.Pipeline.SetFaceCulling(face.CullEnable, face.CullFace);
if (face.CullEnable)
{
_pipeline.CullMode = face.CullFace;
_context.Renderer.Pipeline.SetFaceCulling(face.CullFace);
}
else
{
_pipeline.CullMode = Face.None;
_context.Renderer.Pipeline.SetFaceCulling(Face.None);
}
UpdateFrontFace(yControl, face.FrontFace);
}
@ -1388,6 +1397,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
bool alphaToCoverageEnable = (_state.State.MultisampleControl & 1) != 0;
bool alphaToOneEnable = (_state.State.MultisampleControl & 0x10) != 0;
_pipeline.AlphaToCoverageEnable = alphaToCoverageEnable;
_pipeline.AlphaToOneEnable = alphaToOneEnable;
_context.Renderer.Pipeline.SetMultisampleState(new MultisampleDescriptor(
alphaToCoverageEnable,
_state.State.AlphaToCoverageDitherEnable,

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 = 7353;
private const uint CodeGenVersion = 6877;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";

View file

@ -22,6 +22,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private readonly CancellationToken _cancellationToken;
private readonly Action<ShaderCacheState, int, int> _stateChangeCallback;
private readonly HashSet<ProgramPipelineState> _pipelineStateSet = new();
/// <summary>
/// Indicates if the cache should be loaded.
/// </summary>
@ -232,10 +234,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
for (int index = 0; index < ThreadCount; index++)
{
workThreads[index] = new Thread(ProcessAsyncQueue)
{
Name = $"GPU.AsyncTranslationThread.{index}",
};
workThreads[index] = new Thread(ProcessAsyncQueue) { Name = $"GPU.AsyncTranslationThread.{index}", };
}
int programCount = _hostStorage.GetProgramCount();
@ -305,6 +304,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
using var streams = _hostStorage.GetOutputStreams(_context);
int packagedShaders = 0;
ProgramPipelineState currentPipelineState = default;
foreach (var kv in _programList)
{
if (!Active)
@ -314,12 +315,53 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
(CachedShaderProgram program, byte[] binaryCode) = kv.Value;
_hostStorage.AddShader(_context, program, binaryCode, streams);
if (program.SpecializationState.PipelineState.HasValue && _context.Capabilities.SupportsExtendedDynamicState)
{
currentPipelineState = program.SpecializationState.PipelineState.Value;
_stateChangeCallback(ShaderCacheState.Packaging, ++packagedShaders, _programList.Count);
if (_context.Capabilities.SupportsExtendedDynamicState)
{
currentPipelineState.StencilTest = default;
currentPipelineState.CullMode = 0;
currentPipelineState.FrontFace = 0;
currentPipelineState.DepthTest = default;
currentPipelineState.Topology = ConvertToClass(currentPipelineState.Topology);
}
if (_context.Capabilities.SupportsExtendedDynamicState2)
{
currentPipelineState.PrimitiveRestartEnable = false;
currentPipelineState.BiasEnable = 0;
currentPipelineState.RasterizerDiscard = false;
}
if (_context.Capabilities.SupportsLogicOpDynamicState)
{
currentPipelineState.LogicOp = 0;
}
if (_context.Capabilities.SupportsPatchControlPointsDynamicState)
{
currentPipelineState.PatchControlPoints = 0;
}
}
if (!_pipelineStateSet.Contains(currentPipelineState) ||
!_context.Capabilities.SupportsExtendedDynamicState ||
!program.SpecializationState.PipelineState.HasValue)
{
_hostStorage.AddShader(_context, program, binaryCode, streams);
_stateChangeCallback(ShaderCacheState.Packaging, ++packagedShaders, _programList.Count);
if (_context.Capabilities.SupportsExtendedDynamicState)
{
_pipelineStateSet.Add(currentPipelineState);
}
}
}
Logger.Info?.Print(LogClass.Gpu, $"Rebuilt {_programList.Count} shaders successfully.");
Logger.Info?.Print(LogClass.Gpu, $"Rebuilt {packagedShaders} shaders successfully.");
}
else
{
@ -343,6 +385,29 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
_stateChangeCallback(ShaderCacheState.Loaded, programCount, programCount);
}
private PrimitiveTopology ConvertToClass(PrimitiveTopology topology)
{
return topology switch
{
PrimitiveTopology.Points => PrimitiveTopology.Points,
PrimitiveTopology.Lines or
PrimitiveTopology.LineStrip or
PrimitiveTopology.LinesAdjacency or
PrimitiveTopology.LineStripAdjacency => PrimitiveTopology.Lines,
PrimitiveTopology.Triangles or
PrimitiveTopology.TriangleStrip or
PrimitiveTopology.TriangleFan or
PrimitiveTopology.TrianglesAdjacency or
PrimitiveTopology.TriangleStripAdjacency or
PrimitiveTopology.Polygon => PrimitiveTopology.TriangleStrip,
PrimitiveTopology.Patches => PrimitiveTopology.Patches,
PrimitiveTopology.Quads => PrimitiveTopology.Quads,
PrimitiveTopology.QuadStrip => PrimitiveTopology.QuadStrip,
PrimitiveTopology.LineLoop => PrimitiveTopology.LineLoop,
_ => PrimitiveTopology.TriangleStrip,
};
}
/// <summary>
/// Enqueues a host program for compilation.
/// </summary>

View file

@ -191,6 +191,10 @@ namespace Ryujinx.Graphics.OpenGL
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
supportsDepthClipControl: true,
supportsExtendedDynamicState: false,
supportsExtendedDynamicState2: false,
supportsLogicOpDynamicState: false,
supportsPatchControlPointsDynamicState: false,
uniformBufferSetIndex: 0,
storageBufferSetIndex: 1,
textureSetIndex: 2,

View file

@ -898,7 +898,7 @@ namespace Ryujinx.Graphics.OpenGL
}
}
public void SetDepthTest(DepthTestDescriptor depthTest)
public void SetDepthTest(DepthTestDescriptor depthTest, bool signalChange = true)
{
if (depthTest.TestEnable)
{
@ -915,11 +915,11 @@ namespace Ryujinx.Graphics.OpenGL
_depthTestEnable = depthTest.TestEnable;
}
public void SetFaceCulling(bool enable, Face face)
public void SetFaceCulling(Face face)
{
_cullEnable = enable;
_cullEnable = face != Face.None;
if (!enable)
if (!_cullEnable)
{
GL.Disable(EnableCap.CullFace);
return;
@ -1107,12 +1107,12 @@ namespace Ryujinx.Graphics.OpenGL
GL.Enable(EnableCap.PrimitiveRestart);
}
public void SetPrimitiveTopology(PrimitiveTopology topology)
public void SetPrimitiveTopology(PrimitiveTopology topology, bool signalChange = true)
{
_primitiveType = topology.Convert();
}
public void SetProgram(IProgram program)
public void SetProgram(IProgram program, bool signalChange = true)
{
Program prg = (Program)program;
@ -1154,7 +1154,7 @@ namespace Ryujinx.Graphics.OpenGL
_rasterizerDiscard = discard;
}
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMasks)
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMasks, bool signalChange = true)
{
_componentMasks = 0;
@ -1195,7 +1195,7 @@ namespace Ryujinx.Graphics.OpenGL
_framebuffer.SetDrawBuffers(colors.Length);
}
public void SetScissors(ReadOnlySpan<Rectangle<int>> regions)
public void SetScissors(ReadOnlySpan<Rectangle<int>> regions, bool signalChange = true)
{
int count = Math.Min(regions.Length, Constants.MaxViewports);
@ -1388,7 +1388,7 @@ namespace Ryujinx.Graphics.OpenGL
_vertexArray.SetVertexBuffers(vertexBuffers);
}
public void SetViewports(ReadOnlySpan<Viewport> viewports)
public void SetViewports(ReadOnlySpan<Viewport> viewports, bool signalChange = true)
{
Array.Resize(ref _viewportArray, viewports.Length * 4);
Array.Resize(ref _depthRangeArray, viewports.Length * 2);

View file

@ -257,8 +257,8 @@ namespace Ryujinx.Graphics.Vulkan.Effects
scissors[0] = new Rectangle<int>(0, 0, texture.Width, texture.Height);
_pipeline.SetRenderTarget(texture, (uint)texture.Width, (uint)texture.Height);
_pipeline.SetRenderTargetColorMasks(colorMasks);
_pipeline.SetRenderTarget(texture, (uint)texture.Width, (uint)texture.Height, false);
_pipeline.SetRenderTargetColorMasks(colorMasks, false);
_pipeline.SetScissors(scissors);
_pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f));
}

View file

@ -238,6 +238,7 @@ namespace Ryujinx.Graphics.Vulkan
Face.Back => CullModeFlags.BackBit,
Face.Front => CullModeFlags.FrontBit,
Face.FrontAndBack => CullModeFlags.FrontAndBack,
Face.None => CullModeFlags.None,
_ => LogInvalidAndReturn(face, nameof(Face), CullModeFlags.BackBit),
};
}
@ -310,6 +311,25 @@ namespace Ryujinx.Graphics.Vulkan
};
}
public static PrimitiveTopology ConvertToClass(this PrimitiveTopology topology)
{
return topology switch
{
PrimitiveTopology.PointList => PrimitiveTopology.PointList,
PrimitiveTopology.LineList or
PrimitiveTopology.LineStrip or
PrimitiveTopology.LineListWithAdjacency or
PrimitiveTopology.LineStripWithAdjacency => PrimitiveTopology.LineList,
PrimitiveTopology.TriangleList or
PrimitiveTopology.TriangleStrip or
PrimitiveTopology.TriangleFan or
PrimitiveTopology.TriangleListWithAdjacency or
PrimitiveTopology.TriangleStripWithAdjacency => PrimitiveTopology.TriangleStrip,
PrimitiveTopology.PatchList => PrimitiveTopology.PatchList,
_ => LogInvalidAndReturn(topology, nameof(PrimitiveTopology), PrimitiveTopology.TriangleStrip),
};
}
public static StencilOp Convert(this GAL.StencilOp op)
{
return op switch

View file

@ -31,6 +31,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsShaderStorageImageMultisample;
public readonly bool SupportsConditionalRendering;
public readonly bool SupportsExtendedDynamicState;
public readonly PhysicalDeviceExtendedDynamicState2FeaturesEXT SupportsExtendedDynamicState2;
public readonly bool SupportsMultiView;
public readonly bool SupportsNullDescriptors;
public readonly bool SupportsPushDescriptors;
@ -46,6 +47,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsViewportArray2;
public readonly bool SupportsHostImportedMemory;
public readonly bool SupportsDepthClipControl;
public readonly bool SupportsWideLines;
public readonly bool SupportsAttachmentFeedbackLoop;
public readonly bool SupportsDynamicAttachmentFeedbackLoop;
public readonly uint SubgroupSize;
@ -54,6 +56,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly uint VertexBufferAlignment;
public readonly uint SubTexelPrecisionBits;
public readonly ulong MinResourceAlignment;
public readonly uint MaxTessellationPatchSize;
public HardwareCapabilities(
bool supportsIndexTypeUint8,
@ -71,6 +74,8 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsShaderStorageImageMultisample,
bool supportsConditionalRendering,
bool supportsExtendedDynamicState,
PhysicalDeviceExtendedDynamicState2FeaturesEXT supportsExtendedDynamicState2,
uint maxTessellationPatchSize,
bool supportsMultiView,
bool supportsNullDescriptors,
bool supportsPushDescriptors,
@ -86,6 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsViewportArray2,
bool supportsHostImportedMemory,
bool supportsDepthClipControl,
bool supportsWideLines,
bool supportsAttachmentFeedbackLoop,
bool supportsDynamicAttachmentFeedbackLoop,
uint subgroupSize,
@ -110,6 +116,8 @@ namespace Ryujinx.Graphics.Vulkan
SupportsShaderStorageImageMultisample = supportsShaderStorageImageMultisample;
SupportsConditionalRendering = supportsConditionalRendering;
SupportsExtendedDynamicState = supportsExtendedDynamicState;
SupportsExtendedDynamicState2 = supportsExtendedDynamicState2;
MaxTessellationPatchSize = maxTessellationPatchSize;
SupportsMultiView = supportsMultiView;
SupportsNullDescriptors = supportsNullDescriptors;
SupportsPushDescriptors = supportsPushDescriptors;
@ -125,6 +133,7 @@ namespace Ryujinx.Graphics.Vulkan
SupportsViewportArray2 = supportsViewportArray2;
SupportsHostImportedMemory = supportsHostImportedMemory;
SupportsDepthClipControl = supportsDepthClipControl;
SupportsWideLines = supportsWideLines;
SupportsAttachmentFeedbackLoop = supportsAttachmentFeedbackLoop;
SupportsDynamicAttachmentFeedbackLoop = supportsDynamicAttachmentFeedbackLoop;
SubgroupSize = subgroupSize;

View file

@ -429,35 +429,35 @@ namespace Ryujinx.Graphics.Vulkan
if (dstIsDepthOrStencil)
{
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit);
_pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always));
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit, false);
_pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always), false);
}
else if (src.Info.Target.IsMultisample())
{
_pipeline.SetProgram(_programColorBlitMs);
_pipeline.SetProgram(_programColorBlitMs, false);
}
else if (clearAlpha)
{
_pipeline.SetProgram(_programColorBlitClearAlpha);
_pipeline.SetProgram(_programColorBlitClearAlpha, false);
}
else
{
_pipeline.SetProgram(_programColorBlit);
_pipeline.SetProgram(_programColorBlit, false);
}
int dstWidth = dst.Width;
int dstHeight = dst.Height;
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
_pipeline.SetRenderTargetColorMasks(new uint[] { 0xf });
_pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dstWidth, dstHeight) });
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false);
_pipeline.SetRenderTargetColorMasks(new uint[] { 0xf }, false);
_pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dstWidth, dstHeight) }, false);
if (clearAlpha)
{
_pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f));
}
_pipeline.SetViewports(viewports);
_pipeline.SetViewports(viewports, false);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
_pipeline.Draw(4, 1, 0, 0);
@ -524,10 +524,10 @@ namespace Ryujinx.Graphics.Vulkan
int dstWidth = dst.Width;
int dstHeight = dst.Height;
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
_pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dstWidth, dstHeight) });
_pipeline.SetViewports(viewports);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false);
_pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dstWidth, dstHeight) }, false);
_pipeline.SetViewports(viewports, false);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip, false);
var aspectFlags = src.Info.Format.ConvertAspectFlags();
@ -589,12 +589,12 @@ namespace Ryujinx.Graphics.Vulkan
if (isDepth)
{
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit);
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programDepthBlitMs : _programDepthBlit, false);
_pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always));
}
else
{
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programStencilBlitMs : _programStencilBlit);
_pipeline.SetProgram(src.Info.Target.IsMultisample() ? _programStencilBlitMs : _programStencilBlit, false);
_pipeline.SetStencilTest(CreateStencilTestDescriptor(true));
}
@ -684,11 +684,11 @@ namespace Ryujinx.Graphics.Vulkan
program = _programColorClearF;
}
_pipeline.SetProgram(program);
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
_pipeline.SetRenderTargetColorMasks(new[] { componentMask });
_pipeline.SetViewports(viewports);
_pipeline.SetScissors(stackalloc Rectangle<int>[] { scissor });
_pipeline.SetProgram(program, false);
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false);
_pipeline.SetRenderTargetColorMasks(new[] { componentMask }, false);
_pipeline.SetViewports(viewports, false);
_pipeline.SetScissors(stackalloc Rectangle<int>[] { scissor }, false);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
_pipeline.Draw(4, 1, 0, 0);
_pipeline.Finish();
@ -731,12 +731,12 @@ namespace Ryujinx.Graphics.Vulkan
0f,
1f);
_pipeline.SetProgram(_programDepthStencilClear);
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
_pipeline.SetViewports(viewports);
_pipeline.SetScissors(stackalloc Rectangle<int>[] { scissor });
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
_pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always));
_pipeline.SetProgram(_programDepthStencilClear, false);
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false);
_pipeline.SetViewports(viewports, false);
_pipeline.SetScissors(stackalloc Rectangle<int>[] { scissor }, false);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip, false);
_pipeline.SetDepthTest(new DepthTestDescriptor(true, depthMask, CompareOp.Always), false);
_pipeline.SetStencilTest(CreateStencilTestDescriptor(stencilMask != 0, stencilValue, 0xff, stencilMask));
_pipeline.Draw(4, 1, 0, 0);
_pipeline.Finish();
@ -794,8 +794,8 @@ namespace Ryujinx.Graphics.Vulkan
0f,
1f);
pipeline.SetProgram(_programColorBlit);
pipeline.SetViewports(viewports);
pipeline.SetProgram(_programColorBlit, false);
pipeline.SetViewports(viewports, false);
pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
pipeline.Draw(4, 1, 0, 0);
@ -1129,16 +1129,16 @@ namespace Ryujinx.Graphics.Vulkan
0f,
1f);
_pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dst.Width, dst.Height) });
_pipeline.SetViewports(viewports);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
_pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dst.Width, dst.Height) }, false);
_pipeline.SetViewports(viewports, false);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip, false);
for (int z = 0; z < depth; z++)
{
var srcView = Create2DLayerView(src, srcLayer + z, 0);
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
_pipeline.SetRenderTarget(dstView, (uint)dst.Width, (uint)dst.Height);
_pipeline.SetRenderTarget(dstView, (uint)dst.Width, (uint)dst.Height, false);
CopyMSDraw(srcView, aspectFlags, fromMS: true);
@ -1251,9 +1251,9 @@ namespace Ryujinx.Graphics.Vulkan
1f);
_pipeline.SetRenderTargetColorMasks(new uint[] { 0xf });
_pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dst.Width, dst.Height) });
_pipeline.SetViewports(viewports);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
_pipeline.SetScissors(stackalloc Rectangle<int>[] { new Rectangle<int>(0, 0, dst.Width, dst.Height) }, false);
_pipeline.SetViewports(viewports, false);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip, false);
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, buffer.Range) });
@ -1264,7 +1264,7 @@ namespace Ryujinx.Graphics.Vulkan
var srcView = Create2DLayerView(src, srcLayer + z, 0);
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
_pipeline.SetRenderTarget(dstView, (uint)dst.Width, (uint)dst.Height);
_pipeline.SetRenderTarget(dstView, (uint)dst.Width, (uint)dst.Height, false);
CopyMSDraw(srcView, aspectFlags, fromMS: false);
@ -1281,7 +1281,7 @@ namespace Ryujinx.Graphics.Vulkan
}
else
{
_pipeline.SetProgram(_programColorDrawToMs);
_pipeline.SetProgram(_programColorDrawToMs, false);
var format = GetFormat(src.Info.BytesPerPixel);
var vkFormat = FormatTable.GetFormat(format);
@ -1358,12 +1358,12 @@ namespace Ryujinx.Graphics.Vulkan
if (isDepth)
{
_pipeline.SetProgram(fromMS ? _programDepthDrawToNonMs : _programDepthDrawToMs);
_pipeline.SetProgram(fromMS ? _programDepthDrawToNonMs : _programDepthDrawToMs, false);
_pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always));
}
else
{
_pipeline.SetProgram(fromMS ? _programStencilDrawToNonMs : _programStencilDrawToMs);
_pipeline.SetProgram(fromMS ? _programStencilDrawToNonMs : _programStencilDrawToMs, false);
_pipeline.SetStencilTest(CreateStencilTestDescriptor(true));
}

View file

@ -1,3 +1,5 @@
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan;
@ -74,6 +76,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly BufferState[] _transformFeedbackBuffers;
private readonly VertexBufferState[] _vertexBuffers;
private ulong _vertexBuffersDirty;
private bool _bindingsSet;
protected Rectangle<int> ClearScissor;
private readonly VertexBufferUpdater _vertexBufferUpdater;
@ -87,6 +90,9 @@ namespace Ryujinx.Graphics.Vulkan
private bool _tfEnabled;
private bool _tfActive;
private readonly bool _supportExtDynamic;
private readonly bool _supportExtDynamic2;
private FeedbackLoopAspects _feedbackLoop;
private bool _passWritesDepthStencil;
@ -94,6 +100,8 @@ namespace Ryujinx.Graphics.Vulkan
public ulong DrawCount { get; private set; }
public bool RenderPassActive { get; private set; }
private readonly int[] _vertexBufferBindings;
public unsafe PipelineBase(VulkanRenderer gd, Device device)
{
Gd = gd;
@ -126,7 +134,19 @@ namespace Ryujinx.Graphics.Vulkan
_storedBlend = new PipelineColorBlendAttachmentState[Constants.MaxRenderTargets];
_newState.Initialize();
_supportExtDynamic = gd.Capabilities.SupportsExtendedDynamicState;
_supportExtDynamic2 = gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2;
_bindingsSet = false;
_vertexBufferBindings = new int[Constants.MaxVertexBuffers];
for (int i = 0; i < Constants.MaxVertexBuffers; i++)
{
_vertexBufferBindings[i] = i + 1;
}
_newState.Initialize(gd.Capabilities);
}
public void Initialize()
@ -632,19 +652,46 @@ namespace Ryujinx.Graphics.Vulkan
{
if (texture is TextureView srcTexture)
{
var oldCullMode = _newState.CullMode;
var oldStencilTestEnable = _newState.StencilTestEnable;
var oldDepthTestEnable = _newState.DepthTestEnable;
var oldDepthWriteEnable = _newState.DepthWriteEnable;
var oldViewports = DynamicState.Viewports;
var oldViewportsCount = _newState.ViewportsCount;
var oldTopology = _topology;
CullModeFlags oldCullMode;
bool oldStencilTestEnable;
bool oldDepthTestEnable;
bool oldDepthWriteEnable;
PrimitiveTopology oldTopology;
Array16<Silk.NET.Vulkan.Viewport> oldViewports = DynamicState.Viewports;
uint oldViewportsCount;
_newState.CullMode = CullModeFlags.None;
_newState.StencilTestEnable = false;
_newState.DepthTestEnable = false;
_newState.DepthWriteEnable = false;
SignalStateChange();
if (_supportExtDynamic)
{
oldCullMode = DynamicState.CullMode;
oldStencilTestEnable = DynamicState.StencilTestEnable;
oldDepthTestEnable = DynamicState.DepthTestEnable;
oldDepthWriteEnable = DynamicState.DepthWriteEnable;
oldTopology = _topology;
oldViewportsCount = DynamicState.ViewportsCount;
}
else
{
oldCullMode = _newState.CullMode;
oldStencilTestEnable = _newState.StencilTestEnable;
oldDepthTestEnable = _newState.DepthTestEnable;
oldDepthWriteEnable = _newState.DepthWriteEnable;
oldTopology = _topology;
oldViewportsCount = _newState.ViewportsCount;
}
if (_supportExtDynamic)
{
DynamicState.SetCullMode(CullModeFlags.None);
DynamicState.SetDepthTestBool(false, false);
DynamicState.SetStencilTest(false);
}
else
{
_newState.CullMode = CullModeFlags.None;
_newState.StencilTestEnable = false;
_newState.DepthTestEnable = false;
_newState.DepthWriteEnable = false;
}
Gd.HelperShader.DrawTexture(
Gd,
@ -654,16 +701,24 @@ namespace Ryujinx.Graphics.Vulkan
srcRegion,
dstRegion);
_newState.CullMode = oldCullMode;
_newState.StencilTestEnable = oldStencilTestEnable;
_newState.DepthTestEnable = oldDepthTestEnable;
_newState.DepthWriteEnable = oldDepthWriteEnable;
if (_supportExtDynamic)
{
DynamicState.SetCullMode(oldCullMode);
DynamicState.SetStencilTest(oldStencilTestEnable);
DynamicState.SetDepthTestBool(oldDepthTestEnable, oldDepthWriteEnable);
}
else
{
_newState.CullMode = oldCullMode;
_newState.StencilTestEnable = oldStencilTestEnable;
_newState.DepthTestEnable = oldDepthTestEnable;
_newState.DepthWriteEnable = oldDepthWriteEnable;
_newState.ViewportsCount = oldViewportsCount;
}
SetPrimitiveTopology(oldTopology);
DynamicState.SetViewports(ref oldViewports, oldViewportsCount);
_newState.ViewportsCount = oldViewportsCount;
SignalStateChange();
}
}
@ -792,48 +847,102 @@ namespace Ryujinx.Graphics.Vulkan
public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp)
{
DynamicState.SetDepthBias(factor, units, clamp);
bool depthBiasEnable = (enables != 0) && (factor != 0 && units != 0);
bool changed = false;
_newState.DepthBiasEnable = enables != 0;
SignalStateChange();
}
if (_supportExtDynamic2)
{
DynamicState.SetDepthBiasEnable(depthBiasEnable);
}
else if (_newState.DepthBiasEnable != depthBiasEnable)
{
_newState.DepthBiasEnable = depthBiasEnable;
changed = true;
}
public void SetDepthClamp(bool clamp)
{
_newState.DepthClampEnable = clamp;
SignalStateChange();
}
if (depthBiasEnable)
{
DynamicState.SetDepthBias(factor, units, clamp);
}
public void SetDepthMode(DepthMode mode)
{
bool oldMode = _newState.DepthMode;
_newState.DepthMode = mode == DepthMode.MinusOneToOne;
if (_newState.DepthMode != oldMode)
if (changed)
{
SignalStateChange();
}
}
public void SetDepthTest(DepthTestDescriptor depthTest)
public void SetDepthClamp(bool clamp)
{
_newState.DepthTestEnable = depthTest.TestEnable;
_newState.DepthWriteEnable = depthTest.WriteEnable;
_newState.DepthCompareOp = depthTest.Func.Convert();
_newState.DepthClampEnable = clamp;
UpdatePassDepthStencil();
SignalStateChange();
}
public void SetFaceCulling(bool enable, Face face)
public void SetDepthMode(DepthMode mode)
{
_newState.CullMode = enable ? face.Convert() : CullModeFlags.None;
bool newMode = mode == DepthMode.MinusOneToOne;
if (_newState.DepthMode == newMode)
{
return;
}
_newState.DepthMode = newMode;
SignalStateChange();
}
public void SetDepthTest(DepthTestDescriptor depthTest, bool signalChange = true)
{
if (_supportExtDynamic)
{
DynamicState.SetDepthTestBool(depthTest.TestEnable, depthTest.WriteEnable);
if (depthTest.TestEnable)
{
DynamicState.SetDepthTestCompareOp(depthTest.Func.Convert());
}
}
else
{
_newState.DepthTestEnable = depthTest.TestEnable;
_newState.DepthWriteEnable = depthTest.WriteEnable;
_newState.DepthCompareOp = depthTest.Func.Convert();
if (signalChange)
{
SignalStateChange();
}
}
UpdatePassDepthStencil();
}
public void SetFaceCulling(Face face)
{
if (_supportExtDynamic)
{
DynamicState.SetCullMode(face.Convert());
}
else
{
_newState.CullMode = face.Convert();
SignalStateChange();
}
}
public void SetFrontFace(FrontFace frontFace)
{
_newState.FrontFace = frontFace.Convert();
SignalStateChange();
if (_supportExtDynamic)
{
DynamicState.SetFrontFace(frontFace.Convert());
}
else
{
_newState.FrontFace = frontFace.Convert();
SignalStateChange();
}
}
public void SetImage(ShaderStage stage, int binding, ITexture image)
@ -872,28 +981,56 @@ namespace Ryujinx.Graphics.Vulkan
public void SetLineParameters(float width, bool smooth)
{
_newState.LineWidth = width;
SignalStateChange();
if (!Gd.IsMoltenVk)
{
DynamicState.SetLineWidth(Gd.Capabilities.SupportsWideLines ? width : 1.0f);
}
}
public void SetLogicOpState(bool enable, LogicalOp op)
{
// Vendors other than NVIDIA have a bug where it enables logical operations even for float formats,
// so we need to force disable them here.
bool logicOpEnable = enable && (Gd.Vendor == Vendor.Nvidia || _newState.Internal.LogicOpsAllowed);
_newState.LogicOpEnable = enable;
_newState.LogicOp = op.Convert();
if (Gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2LogicOp)
{
if (logicOpEnable)
{
DynamicState.SetLogicOp(op.Convert());
}
}
else
{
_newState.LogicOp = op.Convert();
}
SignalStateChange();
}
public void SetMultisampleState(MultisampleDescriptor multisample)
{
_newState.AlphaToCoverageEnable = multisample.AlphaToCoverageEnable;
_newState.AlphaToOneEnable = multisample.AlphaToOneEnable;
SignalStateChange();
}
public void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel)
{
_newState.PatchControlPoints = (uint)vertices;
SignalStateChange();
if (Gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints)
{
DynamicState.SetPatchControlPoints((uint)vertices);
}
else
{
_newState.PatchControlPoints = (uint)vertices;
SignalStateChange();
}
// TODO: Default levels (likely needs emulation on shaders?)
}
@ -910,12 +1047,21 @@ namespace Ryujinx.Graphics.Vulkan
public void SetPrimitiveRestart(bool enable, int index)
{
_newState.PrimitiveRestartEnable = enable;
if (_supportExtDynamic2)
{
DynamicState.SetPrimitiveRestartEnable(enable);
}
else
{
_newState.PrimitiveRestartEnable = enable;
SignalStateChange();
}
// TODO: What to do about the index?
SignalStateChange();
}
public void SetPrimitiveTopology(PrimitiveTopology topology)
public void SetPrimitiveTopology(PrimitiveTopology topology, bool signalChange = true)
{
_topology = topology;
@ -923,10 +1069,18 @@ namespace Ryujinx.Graphics.Vulkan
_newState.Topology = vkTopology;
SignalStateChange();
if (_supportExtDynamic)
{
DynamicState.SetPrimitiveTopology(vkTopology);
}
if (signalChange)
{
SignalStateChange();
}
}
public void SetProgram(IProgram program)
public void SetProgram(IProgram program, bool signalChange = true)
{
var internalProgram = (ShaderCollection)program;
var stages = internalProgram.GetInfos();
@ -942,7 +1096,10 @@ namespace Ryujinx.Graphics.Vulkan
stages.CopyTo(_newState.Stages.AsSpan()[..stages.Length]);
SignalStateChange();
if (signalChange)
{
SignalStateChange();
}
if (internalProgram.IsCompute)
{
@ -968,18 +1125,26 @@ namespace Ryujinx.Graphics.Vulkan
public void SetRasterizerDiscard(bool discard)
{
_newState.RasterizerDiscardEnable = discard;
SignalStateChange();
if (_supportExtDynamic2)
{
DynamicState.SetRasterizerDiscard(discard);
}
else
{
_newState.RasterizerDiscardEnable = discard;
SignalStateChange();
}
if (!discard && Gd.IsQualcommProprietary)
{
// On Adreno, enabling rasterizer discard somehow corrupts the viewport state.
// Force it to be updated on next use to work around this bug.
DynamicState.ForceAllDirty();
DynamicState.ForceAllDirty(Gd);
}
}
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask)
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask, bool signalChange = true)
{
int count = Math.Min(Constants.MaxRenderTargets, componentMask.Length);
int writtenAttachments = 0;
@ -1019,7 +1184,10 @@ namespace Ryujinx.Graphics.Vulkan
}
else
{
SignalStateChange();
if (signalChange)
{
SignalStateChange();
}
if (writtenAttachments != _writtenAttachmentCount)
{
@ -1043,7 +1211,7 @@ namespace Ryujinx.Graphics.Vulkan
SetRenderTargetsInternal(colors, depthStencil, Gd.IsTBDR);
}
public void SetScissors(ReadOnlySpan<Rectangle<int>> regions)
public void SetScissors(ReadOnlySpan<Rectangle<int>> regions, bool signalChange = true)
{
int maxScissors = Gd.Capabilities.SupportsMultiView ? Constants.MaxViewports : 1;
int count = Math.Min(maxScissors, regions.Length);
@ -1052,6 +1220,8 @@ namespace Ryujinx.Graphics.Vulkan
ClearScissor = regions[0];
}
DynamicState.ScissorsCount = count;
for (int i = 0; i < count; i++)
{
var region = regions[i];
@ -1061,34 +1231,56 @@ namespace Ryujinx.Graphics.Vulkan
DynamicState.SetScissor(i, new Rect2D(offset, extent));
}
DynamicState.ScissorsCount = count;
if (!_supportExtDynamic)
{
_newState.ScissorsCount = (uint)count;
_newState.ScissorsCount = (uint)count;
SignalStateChange();
if (signalChange)
{
SignalStateChange();
}
}
}
public void SetStencilTest(StencilTestDescriptor stencilTest)
{
DynamicState.SetStencilMasks(
(uint)stencilTest.BackFuncMask,
if (_supportExtDynamic)
{
DynamicState.SetStencilTestandOp(
stencilTest.BackSFail.Convert(),
stencilTest.BackDpPass.Convert(),
stencilTest.BackDpFail.Convert(),
stencilTest.BackFunc.Convert(),
stencilTest.FrontSFail.Convert(),
stencilTest.FrontDpPass.Convert(),
stencilTest.FrontDpFail.Convert(),
stencilTest.FrontFunc.Convert(),
stencilTest.TestEnable);
UpdatePassDepthStencil();
}
else
{
_newState.StencilBackFailOp = stencilTest.BackSFail.Convert();
_newState.StencilBackPassOp = stencilTest.BackDpPass.Convert();
_newState.StencilBackDepthFailOp = stencilTest.BackDpFail.Convert();
_newState.StencilBackCompareOp = stencilTest.BackFunc.Convert();
_newState.StencilFrontFailOp = stencilTest.FrontSFail.Convert();
_newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert();
_newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert();
_newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert();
_newState.StencilTestEnable = stencilTest.TestEnable;
UpdatePassDepthStencil();
SignalStateChange();
}
DynamicState.SetStencilMask((uint)stencilTest.BackFuncMask,
(uint)stencilTest.BackMask,
(uint)stencilTest.BackFuncRef,
(uint)stencilTest.FrontFuncMask,
(uint)stencilTest.FrontMask,
(uint)stencilTest.FrontFuncRef);
_newState.StencilTestEnable = stencilTest.TestEnable;
_newState.StencilBackFailOp = stencilTest.BackSFail.Convert();
_newState.StencilBackPassOp = stencilTest.BackDpPass.Convert();
_newState.StencilBackDepthFailOp = stencilTest.BackDpFail.Convert();
_newState.StencilBackCompareOp = stencilTest.BackFunc.Convert();
_newState.StencilFrontFailOp = stencilTest.FrontSFail.Convert();
_newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert();
_newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert();
_newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert();
UpdatePassDepthStencil();
SignalStateChange();
}
public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
@ -1207,12 +1399,24 @@ namespace Ryujinx.Graphics.Vulkan
{
int count = Math.Min(Constants.MaxVertexBuffers, vertexBuffers.Length);
_newState.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, 0, VertexInputRate.Vertex);
int validCount = 1;
if (!_bindingsSet)
{
_newState.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, _supportExtDynamic && (!Gd.IsMoltenVk || Gd.SupportsMTL31) ? null : 0, VertexInputRate.Vertex);
for (int i = 1; i < count; i++)
{
_newState.Internal.VertexBindingDescriptions[i] = new VertexInputBindingDescription((uint)i);
}
_bindingsSet = true;
}
BufferHandle lastHandle = default;
Auto<DisposableBuffer> lastBuffer = default;
bool vertexBindingDescriptionChanged = false;
bool vertexDescriptionCountChanged = false;
for (int i = 0; i < count; i++)
{
@ -1231,13 +1435,32 @@ namespace Ryujinx.Graphics.Vulkan
if (vb != null)
{
int binding = i + 1;
int descriptorIndex = validCount++;
_newState.Internal.VertexBindingDescriptions[descriptorIndex] = new VertexInputBindingDescription(
(uint)binding,
(uint)vertexBuffer.Stride,
inputRate);
if (_supportExtDynamic && (!Gd.IsMoltenVk || Gd.SupportsMTL31))
{
if (_newState.Internal.VertexBindingDescriptions[descriptorIndex].InputRate != inputRate ||
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Binding != _vertexBufferBindings[i])
{
_newState.Internal.VertexBindingDescriptions[descriptorIndex].InputRate = inputRate;
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Binding = (uint)_vertexBufferBindings[i];
vertexBindingDescriptionChanged = true;
}
}
else
{
if (_newState.Internal.VertexBindingDescriptions[descriptorIndex].InputRate != inputRate ||
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Stride != vertexBuffer.Stride ||
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Binding != _vertexBufferBindings[i])
{
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Binding = (uint)_vertexBufferBindings[i];
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Stride = (uint)vertexBuffer.Stride;
_newState.Internal.VertexBindingDescriptions[descriptorIndex].InputRate = inputRate;
vertexBindingDescriptionChanged = true;
}
}
int vbSize = vertexBuffer.Buffer.Size;
@ -1253,7 +1476,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
ref var buffer = ref _vertexBuffers[binding];
ref var buffer = ref _vertexBuffers[_vertexBufferBindings[i]];
int oldScalarAlign = buffer.AttributeScalarAlignment;
if (Gd.Capabilities.VertexBufferAlignment < 2 &&
@ -1270,7 +1493,7 @@ namespace Ryujinx.Graphics.Vulkan
vbSize,
vertexBuffer.Stride);
buffer.BindVertexBuffer(Gd, Cbs, (uint)binding, ref _newState, _vertexBufferUpdater);
buffer.BindVertexBuffer(Gd, Cbs, (uint)_vertexBufferBindings[i], ref _newState, _vertexBufferUpdater);
}
}
else
@ -1286,7 +1509,7 @@ namespace Ryujinx.Graphics.Vulkan
vbSize,
vertexBuffer.Stride);
_vertexBuffersDirty |= 1UL << binding;
_vertexBuffersDirty |= 1UL << _vertexBufferBindings[i];
}
buffer.AttributeScalarAlignment = oldScalarAlign;
@ -1296,11 +1519,19 @@ namespace Ryujinx.Graphics.Vulkan
_vertexBufferUpdater.Commit(Cbs);
_newState.VertexBindingDescriptionsCount = (uint)validCount;
SignalStateChange();
if (_newState.VertexBindingDescriptionsCount != validCount)
{
_newState.VertexBindingDescriptionsCount = (uint)validCount;
vertexDescriptionCountChanged = true;
}
if (vertexDescriptionCountChanged || vertexBindingDescriptionChanged)
{
SignalStateChange();
}
}
public void SetViewports(ReadOnlySpan<Viewport> viewports)
public void SetViewports(ReadOnlySpan<Viewport> viewports, bool signalChange = true)
{
int maxViewports = Gd.Capabilities.SupportsMultiView ? Constants.MaxViewports : 1;
int count = Math.Min(maxViewports, viewports.Length);
@ -1325,8 +1556,15 @@ namespace Ryujinx.Graphics.Vulkan
Clamp(viewport.DepthFar)));
}
_newState.ViewportsCount = (uint)count;
SignalStateChange();
if (!_supportExtDynamic)
{
_newState.ViewportsCount = (uint)count;
if (signalChange)
{
SignalStateChange();
}
}
}
public void SwapBuffer(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
@ -1375,7 +1613,7 @@ namespace Ryujinx.Graphics.Vulkan
_vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length);
_descriptorSetUpdater.SignalCommandBufferChange();
DynamicState.ForceAllDirty();
DynamicState.ForceAllDirty(Gd);
_currentPipelineHandle = 0;
}
@ -1529,9 +1767,10 @@ namespace Ryujinx.Graphics.Vulkan
{
if (Gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
{
_newState.FeedbackLoopDynamicState = true;
DynamicState.SetFeedbackLoop(aspects);
}
else
else if (Gd.Capabilities.SupportsAttachmentFeedbackLoop)
{
_newState.FeedbackLoopAspects = aspects;
}
@ -1591,7 +1830,14 @@ namespace Ryujinx.Graphics.Vulkan
}
// Stencil test being enabled doesn't necessarily mean a write, but it's not critical to check.
_passWritesDepthStencil |= (_newState.DepthTestEnable && _newState.DepthWriteEnable) || _newState.StencilTestEnable;
if (_supportExtDynamic)
{
_passWritesDepthStencil |= (DynamicState.DepthTestEnable && DynamicState.DepthWriteEnable) || DynamicState.StencilTestEnable;
}
else
{
_passWritesDepthStencil |= (_newState.DepthTestEnable && _newState.DepthWriteEnable) || _newState.StencilTestEnable;
}
}
private bool RecreateGraphicsPipelineIfNeeded()

View file

@ -4,6 +4,7 @@ using Silk.NET.Vulkan;
using System;
using Format = Silk.NET.Vulkan.Format;
using PolygonMode = Silk.NET.Vulkan.PolygonMode;
using PrimitiveTopology = Ryujinx.Graphics.GAL.PrimitiveTopology;
namespace Ryujinx.Graphics.Vulkan
{
@ -154,64 +155,84 @@ namespace Ryujinx.Graphics.Vulkan
public static PipelineState ToVulkanPipelineState(this ProgramPipelineState state, VulkanRenderer gd)
{
var extendedDynamicState2 = gd.Capabilities.SupportsExtendedDynamicState2;
var extendedDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
PipelineState pipeline = new();
pipeline.Initialize();
pipeline.Initialize(gd.Capabilities);
// It is assumed that Dynamic State is enabled when this conversion is used.
pipeline.CullMode = state.CullEnable ? state.CullMode.Convert() : CullModeFlags.None;
pipeline.DepthBoundsTestEnable = false; // Not implemented.
pipeline.DepthClampEnable = state.DepthClampEnable;
pipeline.DepthTestEnable = state.DepthTest.TestEnable;
pipeline.DepthWriteEnable = state.DepthTest.WriteEnable;
pipeline.DepthCompareOp = state.DepthTest.Func.Convert();
pipeline.AlphaToCoverageEnable = state.AlphaToCoverageEnable;
pipeline.AlphaToOneEnable = state.AlphaToOneEnable;
pipeline.DepthMode = state.DepthMode == DepthMode.MinusOneToOne;
pipeline.FrontFace = state.FrontFace.Convert();
pipeline.HasDepthStencil = state.DepthStencilEnable;
pipeline.LineWidth = state.LineWidth;
pipeline.LogicOpEnable = state.LogicOpEnable;
pipeline.LogicOp = state.LogicOp.Convert();
pipeline.PatchControlPoints = state.PatchControlPoints;
pipeline.PolygonMode = PolygonMode.Fill; // Not implemented.
pipeline.PrimitiveRestartEnable = state.PrimitiveRestartEnable;
pipeline.RasterizerDiscardEnable = state.RasterizerDiscard;
pipeline.SamplesCount = (uint)state.SamplesCount;
if (gd.Capabilities.SupportsMultiView)
{
pipeline.ScissorsCount = Constants.MaxViewports;
pipeline.ViewportsCount = Constants.MaxViewports;
}
else
{
pipeline.ScissorsCount = 1;
pipeline.ViewportsCount = 1;
}
pipeline.DepthBiasEnable = state.BiasEnable != 0;
// Stencil masks and ref are dynamic, so are 0 in the Vulkan pipeline.
pipeline.StencilFrontFailOp = state.StencilTest.FrontSFail.Convert();
pipeline.StencilFrontPassOp = state.StencilTest.FrontDpPass.Convert();
pipeline.StencilFrontDepthFailOp = state.StencilTest.FrontDpFail.Convert();
pipeline.StencilFrontCompareOp = state.StencilTest.FrontFunc.Convert();
pipeline.StencilBackFailOp = state.StencilTest.BackSFail.Convert();
pipeline.StencilBackPassOp = state.StencilTest.BackDpPass.Convert();
pipeline.StencilBackDepthFailOp = state.StencilTest.BackDpFail.Convert();
pipeline.StencilBackCompareOp = state.StencilTest.BackFunc.Convert();
pipeline.StencilTestEnable = state.StencilTest.TestEnable;
pipeline.Topology = gd.TopologyRemap(state.Topology).Convert();
if (!extendedDynamicState)
{
pipeline.DepthCompareOp = state.DepthTest.Func.Convert();
pipeline.CullMode = state.CullMode.Convert();
pipeline.DepthTestEnable = state.DepthTest.TestEnable;
pipeline.DepthWriteEnable = state.DepthTest.WriteEnable;
pipeline.FrontFace = state.FrontFace.Convert();
if (gd.Capabilities.SupportsMultiView)
{
pipeline.ScissorsCount = Constants.MaxViewports;
pipeline.ViewportsCount = Constants.MaxViewports;
}
else
{
pipeline.ScissorsCount = 1;
pipeline.ViewportsCount = 1;
}
pipeline.StencilTestEnable = state.StencilTest.TestEnable;
pipeline.StencilFrontFailOp = state.StencilTest.FrontSFail.Convert();
pipeline.StencilFrontPassOp = state.StencilTest.FrontDpPass.Convert();
pipeline.StencilFrontDepthFailOp = state.StencilTest.FrontDpFail.Convert();
pipeline.StencilFrontCompareOp = state.StencilTest.FrontFunc.Convert();
pipeline.StencilBackFailOp = state.StencilTest.BackSFail.Convert();
pipeline.StencilBackPassOp = state.StencilTest.BackDpPass.Convert();
pipeline.StencilBackDepthFailOp = state.StencilTest.BackDpFail.Convert();
pipeline.StencilBackCompareOp = state.StencilTest.BackFunc.Convert();
}
if (!extendedDynamicState2.ExtendedDynamicState2)
{
pipeline.PrimitiveRestartEnable = state.PrimitiveRestartEnable;
pipeline.RasterizerDiscardEnable = state.RasterizerDiscard;
pipeline.DepthBiasEnable = (state.BiasEnable != 0) &&
(state.DepthBiasFactor != 0 && state.DepthBiasUnits != 0);
}
if (!extendedDynamicState2.ExtendedDynamicState2LogicOp)
{
pipeline.LogicOp = state.LogicOp.Convert();
}
if (!extendedDynamicState2.ExtendedDynamicState2PatchControlPoints)
{
pipeline.PatchControlPoints = state.PatchControlPoints;
}
pipeline.SamplesCount = (uint)state.SamplesCount;
pipeline.LogicOpEnable = state.LogicOpEnable;
int vaCount = Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount);
@ -235,7 +256,7 @@ namespace Ryujinx.Graphics.Vulkan
}
int descriptorIndex = 1;
pipeline.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, 0, VertexInputRate.Vertex);
pipeline.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, extendedDynamicState && (!gd.IsMoltenVk || gd.SupportsMTL31) ? null : 0, VertexInputRate.Vertex);
for (int i = 0; i < vbCount; i++)
{
@ -255,7 +276,7 @@ namespace Ryujinx.Graphics.Vulkan
// TODO: Support divisor > 1
pipeline.Internal.VertexBindingDescriptions[descriptorIndex++] = new VertexInputBindingDescription(
(uint)i + 1,
(uint)alignedStride,
extendedDynamicState && (!gd.IsMoltenVk || gd.SupportsMTL31) ? null : (uint)alignedStride,
inputRate);
}
}

View file

@ -1,6 +1,8 @@
using Ryujinx.Common.Memory;
using Silk.NET.Vulkan;
using Silk.NET.Vulkan.Extensions.EXT;
using System;
using System.Numerics;
namespace Ryujinx.Graphics.Vulkan
{
@ -9,6 +11,7 @@ namespace Ryujinx.Graphics.Vulkan
private float _depthBiasSlopeFactor;
private float _depthBiasConstantFactor;
private float _depthBiasClamp;
private bool _depthBiasEnable;
public int ScissorsCount;
private Array16<Rect2D> _scissors;
@ -20,6 +23,23 @@ namespace Ryujinx.Graphics.Vulkan
private uint _frontWriteMask;
private uint _frontReference;
private StencilOp _backFailOp;
private StencilOp _backPassOp;
private StencilOp _backDepthFailOp;
private CompareOp _backCompareOp;
private StencilOp _frontFailOp;
private StencilOp _frontPassOp;
private StencilOp _frontDepthFailOp;
private CompareOp _frontCompareOp;
private float _lineWidth;
public bool StencilTestEnable;
public bool DepthTestEnable;
public bool DepthWriteEnable;
private CompareOp _depthCompareOp;
private Array4<float> _blendConstants;
private FeedbackLoopAspects _feedbackLoopAspects;
@ -27,6 +47,20 @@ namespace Ryujinx.Graphics.Vulkan
public uint ViewportsCount;
public Array16<Viewport> Viewports;
public CullModeFlags CullMode;
private FrontFace _frontFace;
private bool _discard;
private LogicOp _logicOp;
private uint _patchControlPoints;
public PrimitiveTopology Topology;
private bool _primitiveRestartEnable;
[Flags]
private enum DirtyFlags
{
None = 0,
@ -36,7 +70,21 @@ namespace Ryujinx.Graphics.Vulkan
Stencil = 1 << 3,
Viewport = 1 << 4,
FeedbackLoop = 1 << 5,
All = Blend | DepthBias | Scissor | Stencil | Viewport | FeedbackLoop,
CullMode = 1 << 6,
FrontFace = 1 << 7,
DepthTestBool = 1 << 8,
DepthTestCompareOp = 1 << 9,
StencilTestEnableAndStencilOp = 1 << 10,
LineWidth = 1 << 11,
RasterDiscard = 1 << 12,
LogicOp = 1 << 13,
PatchControlPoints = 1 << 14,
PrimitiveRestart = 1 << 15,
PrimitiveTopology = 1 << 16,
DepthBiasEnable = 1 << 17,
Standard = Blend | DepthBias | Scissor | Stencil | Viewport,
Extended = CullMode | FrontFace | DepthTestBool | DepthTestCompareOp | StencilTestEnableAndStencilOp | PrimitiveTopology,
Extended2 = RasterDiscard | PrimitiveRestart | DepthBiasEnable,
}
private DirtyFlags _dirty;
@ -47,7 +95,6 @@ namespace Ryujinx.Graphics.Vulkan
_blendConstants[1] = g;
_blendConstants[2] = b;
_blendConstants[3] = a;
_dirty |= DirtyFlags.Blend;
}
@ -60,15 +107,64 @@ namespace Ryujinx.Graphics.Vulkan
_dirty |= DirtyFlags.DepthBias;
}
public void SetDepthBiasEnable(bool enable)
{
_depthBiasEnable = enable;
_dirty |= DirtyFlags.DepthBiasEnable;
}
public void SetScissor(int index, Rect2D scissor)
{
_scissors[index] = scissor;
_dirty |= DirtyFlags.Scissor;
}
public void SetStencilMasks(
uint backCompareMask,
public void SetDepthTestBool(bool testEnable, bool writeEnable)
{
DepthTestEnable = testEnable;
DepthWriteEnable = writeEnable;
_dirty |= DirtyFlags.DepthTestBool;
}
public void SetDepthTestCompareOp(CompareOp depthTestOp)
{
_depthCompareOp = depthTestOp;
_dirty |= DirtyFlags.DepthTestCompareOp;
}
public void SetStencilTestandOp(
StencilOp backFailOp,
StencilOp backPassOp,
StencilOp backDepthFailOp,
CompareOp backCompareOp,
StencilOp frontFailOp,
StencilOp frontPassOp,
StencilOp frontDepthFailOp,
CompareOp frontCompareOp,
bool stencilTestEnable)
{
_backFailOp = backFailOp;
_backPassOp = backPassOp;
_backDepthFailOp = backDepthFailOp;
_backCompareOp = backCompareOp;
_frontFailOp = frontFailOp;
_frontPassOp = frontPassOp;
_frontDepthFailOp = frontDepthFailOp;
_frontCompareOp = frontCompareOp;
StencilTestEnable = stencilTestEnable;
_dirty |= DirtyFlags.StencilTestEnableAndStencilOp;
}
public void SetStencilTest(bool stencilTestEnable)
{
StencilTestEnable = stencilTestEnable;
_dirty |= DirtyFlags.StencilTestEnableAndStencilOp;
}
public void SetStencilMask(uint backCompareMask,
uint backWriteMask,
uint backReference,
uint frontCompareMask,
@ -81,28 +177,46 @@ namespace Ryujinx.Graphics.Vulkan
_frontCompareMask = frontCompareMask;
_frontWriteMask = frontWriteMask;
_frontReference = frontReference;
_dirty |= DirtyFlags.Stencil;
}
public void SetViewport(int index, Viewport viewport)
{
Viewports[index] = viewport;
_dirty |= DirtyFlags.Viewport;
}
public void SetViewports(ref Array16<Viewport> viewports, uint viewportsCount)
{
Viewports = viewports;
ViewportsCount = viewportsCount;
if (ViewportsCount != 0)
if (!Viewports.Equals(viewports) || ViewportsCount != viewportsCount)
{
_dirty |= DirtyFlags.Viewport;
Viewports = viewports;
ViewportsCount = viewportsCount;
if (ViewportsCount != 0)
{
_dirty |= DirtyFlags.Viewport;
}
}
}
public void SetCullMode(CullModeFlags cullMode)
{
CullMode = cullMode;
_dirty |= DirtyFlags.CullMode;
}
public void SetFrontFace(FrontFace frontFace)
{
_frontFace = frontFace;
_dirty |= DirtyFlags.FrontFace;
}
public void SetLineWidth(float width)
{
_lineWidth = width;
_dirty |= DirtyFlags.LineWidth;
}
public void SetFeedbackLoop(FeedbackLoopAspects aspects)
{
_feedbackLoopAspects = aspects;
@ -110,43 +224,149 @@ namespace Ryujinx.Graphics.Vulkan
_dirty |= DirtyFlags.FeedbackLoop;
}
public void ForceAllDirty()
public void SetRasterizerDiscard(bool discard)
{
_dirty = DirtyFlags.All;
_discard = discard;
_dirty |= DirtyFlags.RasterDiscard;
}
public void SetPrimitiveRestartEnable(bool primitiveRestart)
{
_primitiveRestartEnable = primitiveRestart;
_dirty |= DirtyFlags.PrimitiveRestart;
}
public void SetPrimitiveTopology(PrimitiveTopology primitiveTopology)
{
Topology = primitiveTopology;
_dirty |= DirtyFlags.PrimitiveTopology;
}
public void SetLogicOp(LogicOp op)
{
_logicOp = op;
_dirty |= DirtyFlags.LogicOp;
}
public void SetPatchControlPoints(uint points)
{
_patchControlPoints = points;
_dirty |= DirtyFlags.PatchControlPoints;
}
public void ForceAllDirty(VulkanRenderer gd)
{
_dirty = DirtyFlags.Standard;
if (gd.Capabilities.SupportsExtendedDynamicState)
{
_dirty |= DirtyFlags.Extended;
}
if (gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2)
{
_dirty |= DirtyFlags.Extended2;
if (gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2LogicOp)
{
_dirty |= DirtyFlags.LogicOp;
}
if (gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints)
{
_dirty |= DirtyFlags.PatchControlPoints;
}
}
if (!gd.IsMoltenVk)
{
_dirty |= DirtyFlags.LineWidth;
}
if (gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
{
_dirty |= DirtyFlags.FeedbackLoop;
}
}
public void ReplayIfDirty(VulkanRenderer gd, CommandBuffer commandBuffer)
{
Vk api = gd.Api;
if (_dirty.HasFlag(DirtyFlags.Blend))
if (_dirty == DirtyFlags.None)
{
RecordBlend(api, commandBuffer);
return;
}
if (_dirty.HasFlag(DirtyFlags.DepthBias))
{
RecordDepthBias(api, commandBuffer);
}
var api = gd.Api;
var extendedStateApi = gd.ExtendedDynamicStateApi;
var extendedState2Api = gd.ExtendedDynamicState2Api;
var dynamicFeedbackLoopApi = gd.DynamicFeedbackLoopApi;
if (_dirty.HasFlag(DirtyFlags.Scissor))
{
RecordScissor(api, commandBuffer);
}
DirtyFlags dirtyFlags = _dirty;
if (_dirty.HasFlag(DirtyFlags.Stencil))
while (dirtyFlags != DirtyFlags.None)
{
RecordStencilMasks(api, commandBuffer);
}
int bitIndex = BitOperations.TrailingZeroCount((uint)dirtyFlags);
DirtyFlags currentFlag = (DirtyFlags)(1 << bitIndex);
if (_dirty.HasFlag(DirtyFlags.Viewport))
{
RecordViewport(api, commandBuffer);
}
switch (currentFlag)
{
case DirtyFlags.Blend:
RecordBlend(api, commandBuffer);
break;
case DirtyFlags.DepthBias:
RecordDepthBias(api, commandBuffer);
break;
case DirtyFlags.Scissor:
RecordScissor(gd, commandBuffer);
break;
case DirtyFlags.Stencil:
RecordStencil(api, commandBuffer);
break;
case DirtyFlags.Viewport:
RecordViewport(gd, commandBuffer);
break;
case DirtyFlags.FeedbackLoop:
RecordFeedbackLoop(dynamicFeedbackLoopApi, commandBuffer);
break;
case DirtyFlags.CullMode:
RecordCullMode(extendedStateApi, commandBuffer);
break;
case DirtyFlags.FrontFace:
RecordFrontFace(extendedStateApi, commandBuffer);
break;
case DirtyFlags.DepthTestBool:
RecordDepthTestBool(extendedStateApi, commandBuffer);
break;
case DirtyFlags.DepthTestCompareOp:
RecordDepthTestCompareOp(extendedStateApi, commandBuffer);
break;
case DirtyFlags.StencilTestEnableAndStencilOp:
RecordStencilTestAndOp(extendedStateApi, commandBuffer);
break;
case DirtyFlags.LineWidth:
RecordLineWidth(api, commandBuffer);
break;
case DirtyFlags.RasterDiscard:
RecordRasterizationDiscard(extendedState2Api, commandBuffer);
break;
case DirtyFlags.LogicOp:
RecordLogicOp(extendedState2Api, commandBuffer);
break;
case DirtyFlags.PatchControlPoints:
RecordPatchControlPoints(extendedState2Api, commandBuffer);
break;
case DirtyFlags.PrimitiveRestart:
RecordPrimitiveRestartEnable(gd, commandBuffer);
break;
case DirtyFlags.PrimitiveTopology:
RecordPrimitiveTopology(extendedStateApi, commandBuffer);
break;
case DirtyFlags.DepthBiasEnable:
RecordDepthBiasEnable(extendedState2Api, commandBuffer);
break;
}
if (_dirty.HasFlag(DirtyFlags.FeedbackLoop) && gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
{
RecordFeedbackLoop(gd.DynamicFeedbackLoopApi, commandBuffer);
dirtyFlags &= ~currentFlag;
}
_dirty = DirtyFlags.None;
@ -162,15 +382,27 @@ namespace Ryujinx.Graphics.Vulkan
api.CmdSetDepthBias(commandBuffer, _depthBiasConstantFactor, _depthBiasClamp, _depthBiasSlopeFactor);
}
private void RecordScissor(Vk api, CommandBuffer commandBuffer)
private readonly void RecordDepthBiasEnable(ExtExtendedDynamicState2 gd, CommandBuffer commandBuffer)
{
gd.CmdSetDepthBiasEnable(commandBuffer, _depthBiasEnable);
}
private void RecordScissor(VulkanRenderer gd, CommandBuffer commandBuffer)
{
if (ScissorsCount != 0)
{
api.CmdSetScissor(commandBuffer, 0, (uint)ScissorsCount, _scissors.AsSpan());
if (gd.Capabilities.SupportsExtendedDynamicState)
{
gd.ExtendedDynamicStateApi.CmdSetScissorWithCount(commandBuffer, (uint)ScissorsCount, _scissors.AsSpan());
}
else
{
gd.Api.CmdSetScissor(commandBuffer, 0, (uint)ScissorsCount, _scissors.AsSpan());
}
}
}
private readonly void RecordStencilMasks(Vk api, CommandBuffer commandBuffer)
private readonly void RecordStencil(Vk api, CommandBuffer commandBuffer)
{
api.CmdSetStencilCompareMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backCompareMask);
api.CmdSetStencilWriteMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backWriteMask);
@ -180,12 +412,107 @@ namespace Ryujinx.Graphics.Vulkan
api.CmdSetStencilReference(commandBuffer, StencilFaceFlags.FaceFrontBit, _frontReference);
}
private void RecordViewport(Vk api, CommandBuffer commandBuffer)
private readonly void RecordStencilTestAndOp(ExtExtendedDynamicState api, CommandBuffer commandBuffer)
{
if (ViewportsCount != 0)
api.CmdSetStencilTestEnable(commandBuffer, StencilTestEnable);
api.CmdSetStencilOp(commandBuffer, StencilFaceFlags.FaceBackBit, _backFailOp, _backPassOp, _backDepthFailOp, _backCompareOp);
api.CmdSetStencilOp(commandBuffer, StencilFaceFlags.FaceFrontBit, _frontFailOp, _frontPassOp, _frontDepthFailOp, _frontCompareOp);
}
private void RecordViewport(VulkanRenderer gd, CommandBuffer commandBuffer)
{
if (ViewportsCount == 0)
{
api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan());
return;
}
if (gd.Capabilities.SupportsExtendedDynamicState)
{
gd.ExtendedDynamicStateApi.CmdSetViewportWithCount(commandBuffer, ViewportsCount, Viewports.AsSpan());
}
else
{
gd.Api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan());
}
}
private readonly void RecordCullMode(ExtExtendedDynamicState api, CommandBuffer commandBuffer)
{
api.CmdSetCullMode(commandBuffer, CullMode);
}
private readonly void RecordFrontFace(ExtExtendedDynamicState api, CommandBuffer commandBuffer)
{
api.CmdSetFrontFace(commandBuffer, _frontFace);
}
private readonly void RecordDepthTestBool(ExtExtendedDynamicState api, CommandBuffer commandBuffer)
{
api.CmdSetDepthTestEnable(commandBuffer, DepthTestEnable);
api.CmdSetDepthWriteEnable(commandBuffer, DepthWriteEnable);
}
private readonly void RecordDepthTestCompareOp(ExtExtendedDynamicState api, CommandBuffer commandBuffer)
{
api.CmdSetDepthCompareOp(commandBuffer, _depthCompareOp);
}
private readonly void RecordRasterizationDiscard(ExtExtendedDynamicState2 extendedDynamicState2Api, CommandBuffer commandBuffer)
{
extendedDynamicState2Api.CmdSetRasterizerDiscardEnable(commandBuffer, _discard);
}
private readonly void RecordPrimitiveRestartEnable(VulkanRenderer gd, CommandBuffer commandBuffer)
{
bool primitiveRestartEnable = _primitiveRestartEnable;
bool topologySupportsRestart;
if (gd.Capabilities.SupportsPrimitiveTopologyListRestart)
{
topologySupportsRestart = gd.Capabilities.SupportsPrimitiveTopologyPatchListRestart ||
Topology != PrimitiveTopology.PatchList;
}
else
{
topologySupportsRestart = Topology == PrimitiveTopology.LineStrip ||
Topology == PrimitiveTopology.TriangleStrip ||
Topology == PrimitiveTopology.TriangleFan ||
Topology == PrimitiveTopology.LineStripWithAdjacency ||
Topology == PrimitiveTopology.TriangleStripWithAdjacency;
}
primitiveRestartEnable &= topologySupportsRestart;
// Cannot disable primitiveRestartEnable for these Topologies on MacOS.
if (gd.IsMoltenVk)
{
primitiveRestartEnable = true;
}
gd.ExtendedDynamicState2Api.CmdSetPrimitiveRestartEnable(commandBuffer, primitiveRestartEnable);
}
private readonly void RecordPrimitiveTopology(ExtExtendedDynamicState extendedDynamicStateApi, CommandBuffer commandBuffer)
{
extendedDynamicStateApi.CmdSetPrimitiveTopology(commandBuffer, Topology);
}
private readonly void RecordLogicOp(ExtExtendedDynamicState2 extendedDynamicState2Api, CommandBuffer commandBuffer)
{
extendedDynamicState2Api.CmdSetLogicOp(commandBuffer, _logicOp);
}
private readonly void RecordPatchControlPoints(ExtExtendedDynamicState2 extendedDynamicState2Api, CommandBuffer commandBuffer)
{
extendedDynamicState2Api.CmdSetPatchControlPoints(commandBuffer, _patchControlPoints);
}
private readonly void RecordLineWidth(Vk api, CommandBuffer commandBuffer)
{
api.CmdSetLineWidth(commandBuffer, _lineWidth);
}
private readonly void RecordFeedbackLoop(ExtAttachmentFeedbackLoopDynamicState api, CommandBuffer commandBuffer)

View file

@ -9,11 +9,15 @@ namespace Ryujinx.Graphics.Vulkan
{
}
public void SetRenderTarget(TextureView view, uint width, uint height)
public void SetRenderTarget(TextureView view, uint width, uint height, bool signalChange = true)
{
CreateFramebuffer(view, width, height);
CreateRenderPass();
SignalStateChange();
if (signalChange)
{
SignalStateChange();
}
}
private void CreateFramebuffer(TextureView view, uint width, uint height)

File diff suppressed because it is too large Load diff

View file

@ -11,23 +11,17 @@ namespace Ryujinx.Graphics.Vulkan
{
public ulong Id0;
public ulong Id1;
public ulong Id2;
public ulong Id3;
public ulong Id4;
public ulong Id5;
public ulong Id6;
public ulong Id7;
public ulong Id8;
private readonly uint VertexAttributeDescriptionsCount => (byte)((Id5 >> 38) & 0xFF);
private readonly uint VertexBindingDescriptionsCount => (byte)((Id5 >> 46) & 0xFF);
private readonly uint ColorBlendAttachmentStateCount => (byte)((Id6 >> 8) & 0xFF);
private readonly bool HasDepthStencil => ((Id6 >> 63) & 0x1) != 0UL;
private readonly uint VertexAttributeDescriptionsCount => (byte)((Id0 >> 38) & 0xFF);
private readonly uint VertexBindingDescriptionsCount => (byte)((Id0 >> 46) & 0xFF);
private readonly uint ColorBlendAttachmentStateCount => (byte)((Id1 >> 8) & 0xFF);
private readonly bool HasDepthStencil => ((Id1 >> 63) & 0x1) != 0UL;
public Array32<VertexInputAttributeDescription> VertexAttributeDescriptions;
public Array33<VertexInputBindingDescription> VertexBindingDescriptions;
public Array32<VertexInputBindingDescription> VertexBindingDescriptions;
public Array8<PipelineColorBlendAttachmentState> ColorBlendAttachmentState;
public Array9<Format> AttachmentFormats;
public uint AttachmentIntegerFormatMask;
@ -40,9 +34,7 @@ namespace Ryujinx.Graphics.Vulkan
public bool Equals(ref PipelineUid other)
{
if (!Unsafe.As<ulong, Vector256<byte>>(ref Id0).Equals(Unsafe.As<ulong, Vector256<byte>>(ref other.Id0)) ||
!Unsafe.As<ulong, Vector256<byte>>(ref Id4).Equals(Unsafe.As<ulong, Vector256<byte>>(ref other.Id4)) ||
!Unsafe.As<ulong, Vector128<byte>>(ref Id7).Equals(Unsafe.As<ulong, Vector128<byte>>(ref other.Id7)))
if (!Unsafe.As<ulong, Vector256<byte>>(ref Id0).Equals(Unsafe.As<ulong, Vector256<byte>>(ref other.Id0)))
{
return false;
}
@ -80,12 +72,7 @@ namespace Ryujinx.Graphics.Vulkan
ulong hash64 = Id0 * 23 ^
Id1 * 23 ^
Id2 * 23 ^
Id3 * 23 ^
Id4 * 23 ^
Id5 * 23 ^
Id6 * 23 ^
Id7 * 23 ^
Id8 * 23;
Id3 * 23;
for (int i = 0; i < (int)VertexAttributeDescriptionsCount; i++)
{

View file

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using PrimitiveTopology = Silk.NET.Vulkan.PrimitiveTopology;
namespace Ryujinx.Graphics.Vulkan
{
@ -538,7 +539,7 @@ namespace Ryujinx.Graphics.Vulkan
public void CreateBackgroundComputePipeline()
{
PipelineState pipeline = new();
pipeline.Initialize();
pipeline.Initialize(_gd.Capabilities);
pipeline.Stages[0] = _shaders[0].GetInfo();
pipeline.StagesCount = 1;

View file

@ -73,7 +73,10 @@ namespace Ryujinx.Graphics.Vulkan
_buffer = autoBuffer;
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)stride;
if (!gd.Capabilities.SupportsExtendedDynamicState)
{
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)stride;
}
}
return;
@ -81,8 +84,11 @@ namespace Ryujinx.Graphics.Vulkan
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int size);
// The original stride must be reapplied in case it was rewritten.
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;
if (!gd.Capabilities.SupportsExtendedDynamicState)
{
// The original stride must be reapplied in case it was rewritten.
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;
}
if (_offset >= size)
{

View file

@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.Vulkan
{
if (_count != 0)
{
if (_gd.Capabilities.SupportsExtendedDynamicState)
if (_gd.Capabilities.SupportsExtendedDynamicState && (_gd.SupportsMTL31 || !_gd.IsMoltenVk))
{
_gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
cbs.CommandBuffer,

View file

@ -23,6 +23,7 @@ namespace Ryujinx.Graphics.Vulkan
private static readonly string[] _desirableExtensions = {
ExtConditionalRendering.ExtensionName,
ExtExtendedDynamicState.ExtensionName,
ExtExtendedDynamicState2.ExtensionName,
ExtTransformFeedback.ExtensionName,
KhrDrawIndirectCount.ExtensionName,
KhrPushDescriptor.ExtensionName,
@ -314,6 +315,17 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &supportedFeaturesCustomBorderColor;
}
PhysicalDeviceExtendedDynamicState2FeaturesEXT supportedFeaturesExtExtendedDynamicState2 = new()
{
SType = StructureType.PhysicalDeviceExtendedDynamicState2FeaturesExt,
PNext = features2.PNext,
};
if (physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState2.ExtensionName))
{
features2.PNext = &supportedFeaturesExtExtendedDynamicState2;
}
PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT supportedFeaturesPrimitiveTopologyListRestart = new()
{
SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt,
@ -416,6 +428,7 @@ namespace Ryujinx.Graphics.Vulkan
TessellationShader = supportedFeatures.TessellationShader,
VertexPipelineStoresAndAtomics = supportedFeatures.VertexPipelineStoresAndAtomics,
RobustBufferAccess = useRobustBufferAccess,
WideLines = supportedFeatures.WideLines,
SampleRateShading = supportedFeatures.SampleRateShading,
};
@ -473,6 +486,20 @@ namespace Ryujinx.Graphics.Vulkan
pExtendedFeatures = &featuresExtendedDynamicState;
if (physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState2.ExtensionName))
{
var featuresExtendedDynamicState2 = new PhysicalDeviceExtendedDynamicState2FeaturesEXT()
{
SType = StructureType.PhysicalDeviceExtendedDynamicState2FeaturesExt,
PNext = pExtendedFeatures,
ExtendedDynamicState2 = supportedFeaturesExtExtendedDynamicState2.ExtendedDynamicState2,
ExtendedDynamicState2LogicOp = supportedFeaturesExtExtendedDynamicState2.ExtendedDynamicState2LogicOp,
ExtendedDynamicState2PatchControlPoints = supportedFeaturesExtExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints,
};
pExtendedFeatures = &featuresExtendedDynamicState2;
}
var featuresVk11 = new PhysicalDeviceVulkan11Features
{
SType = StructureType.PhysicalDeviceVulkan11Features,

View file

@ -37,6 +37,7 @@ namespace Ryujinx.Graphics.Vulkan
internal KhrSwapchain SwapchainApi { get; private set; }
internal ExtConditionalRendering ConditionalRenderingApi { get; private set; }
internal ExtExtendedDynamicState ExtendedDynamicStateApi { get; private set; }
internal ExtExtendedDynamicState2 ExtendedDynamicState2Api { get; private set; }
internal KhrPushDescriptor PushDescriptorApi { get; private set; }
internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
@ -92,6 +93,7 @@ namespace Ryujinx.Graphics.Vulkan
internal bool IsIntelArc { get; private set; }
internal bool IsQualcommProprietary { get; private set; }
internal bool IsMoltenVk { get; private set; }
internal bool SupportsMTL31 { get; private set; }
internal bool IsTBDR { get; private set; }
internal bool IsSharedMemory { get; private set; }
@ -117,6 +119,8 @@ namespace Ryujinx.Graphics.Vulkan
// Any device running on MacOS is using MoltenVK, even Intel and AMD vendors.
if (IsMoltenVk = OperatingSystem.IsMacOS())
MVKInitialization.Initialize();
SupportsMTL31 = OperatingSystem.IsMacOSVersionAtLeast(14);
}
public static VulkanRenderer Create(
@ -139,6 +143,11 @@ namespace Ryujinx.Graphics.Vulkan
ExtendedDynamicStateApi = extendedDynamicStateApi;
}
if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExtendedDynamicState2 extendedDynamicState2Api))
{
ExtendedDynamicState2Api = extendedDynamicState2Api;
}
if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrPushDescriptor pushDescriptorApi))
{
PushDescriptorApi = pushDescriptorApi;
@ -233,6 +242,11 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt,
};
PhysicalDeviceExtendedDynamicState2FeaturesEXT featuresExtendedDynamicState2 = new()
{
SType = StructureType.PhysicalDeviceExtendedDynamicState2FeaturesExt,
};
PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2 = new()
{
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt,
@ -273,6 +287,12 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &featuresPrimitiveTopologyListRestart;
}
if (_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState2.ExtensionName))
{
featuresExtendedDynamicState2.PNext = features2.PNext;
features2.PNext = &featuresExtendedDynamicState2;
}
if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2"))
{
featuresRobustness2.PNext = features2.PNext;
@ -406,6 +426,10 @@ namespace Ryujinx.Graphics.Vulkan
properties.Limits.FramebufferDepthSampleCounts &
properties.Limits.FramebufferStencilSampleCounts;
// Temporarily disable this, can be added back at a later date, make it easy to re-enable.
// Disabled because currently causing Device Lost error on NVIDIA.
featuresExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints = false;
Capabilities = new HardwareCapabilities(
_physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"),
supportsCustomBorderColor,
@ -422,6 +446,8 @@ namespace Ryujinx.Graphics.Vulkan
features2.Features.ShaderStorageImageMultisample,
_physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
featuresExtendedDynamicState2,
_physicalDevice.PhysicalDeviceProperties.Limits.MaxTessellationPatchSize,
features2.Features.MultiViewport && !(IsMoltenVk && Vendor == Vendor.Amd), // Workaround for AMD on MoltenVK issue
featuresRobustness2.NullDescriptor || IsMoltenVk,
supportsPushDescriptors && !IsMoltenVk,
@ -437,6 +463,7 @@ namespace Ryujinx.Graphics.Vulkan
_physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"),
_physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName),
supportsDepthClipControl && featuresDepthClipControl.DepthClipControl,
_physicalDevice.PhysicalDeviceFeatures.WideLines,
supportsAttachmentFeedbackLoop && featuresAttachmentFeedbackLoop.AttachmentFeedbackLoopLayout,
supportsDynamicAttachmentFeedbackLoop && featuresDynamicAttachmentFeedbackLoop.AttachmentFeedbackLoopDynamicState,
propertiesSubgroup.SubgroupSize,
@ -772,6 +799,10 @@ namespace Ryujinx.Graphics.Vulkan
supportsViewportSwizzle: false,
supportsIndirectParameters: true,
supportsDepthClipControl: Capabilities.SupportsDepthClipControl,
supportsExtendedDynamicState: Capabilities.SupportsExtendedDynamicState,
supportsExtendedDynamicState2: Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2,
supportsLogicOpDynamicState: Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2LogicOp,
supportsPatchControlPointsDynamicState: Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints,
uniformBufferSetIndex: PipelineBase.UniformSetIndex,
storageBufferSetIndex: PipelineBase.StorageSetIndex,
textureSetIndex: PipelineBase.TextureSetIndex,

View file

@ -555,9 +555,9 @@
"AboutGithubUrlTooltipMessage": "클릭하면 기본 브라우저에서 Ryujinx GitHub 페이지가 열립니다.",
"AboutDiscordUrlTooltipMessage": "클릭하면 기본 브라우저에서 Ryujinx 디스코드 서버 초대장이 열립니다.",
"AboutRyujinxAboutTitle": "정보 :",
"AboutRyujinxAboutContent": "Ryujinx is an emulator for the Nintendo Switch™.\nGet all the latest news in our Discord.\nDevelopers interested in contributing can find out more on our GitHub or Discord.",
"AboutRyujinxAboutContent": "Ryujinx는 Nintendo Switch™용 에뮬레이터입니다.\n모든 최신 소식을 Discord에서 확인하세요.\n기여에 관심이 있는 개발자는 GitHub 또는 Discord에서 자세한 내용을 확인할 수 있습니다.",
"AboutRyujinxMaintainersTitle": "유지 관리 :",
"AboutRyujinxFormerMaintainersTitle": "Formerly Maintained By:",
"AboutRyujinxFormerMaintainersTitle": "이전 관리자 :",
"AboutRyujinxMaintainersContentTooltipMessage": "클릭하면 기본 브라우저에서 기여자 페이지가 열립니다.",
"AmiiboSeriesLabel": "Amiibo 시리즈",
"AmiiboCharacterLabel": "캐릭터",

View file

@ -0,0 +1,785 @@
{
"Language": "Norsk",
"MenuBarFileOpenApplet": "Åpne Program",
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Åpne Mii Redigerings program i eget vindu",
"SettingsTabInputDirectMouseAccess": "Direkte tilgang med Mus",
"SettingsTabSystemMemoryManagerMode": "Memory Manager-modus",
"SettingsTabSystemMemoryManagerModeSoftware": "Programvare",
"SettingsTabSystemMemoryManagerModeHost": "Vert (rask)",
"SettingsTabSystemMemoryManagerModeHostUnchecked": "Vert Ukontrollert (raskets, utrygt)",
"SettingsTabSystemUseHypervisor": "Bruk Hypervisor",
"MenuBarFile": "_Fil",
"MenuBarFileOpenFromFile": "_Last inn program fra fil",
"MenuBarFileOpenFromFileError": "Ingen apper ble funnet i valgt fil.",
"MenuBarFileOpenUnpacked": "Last inn _Upakket spill",
"MenuBarFileOpenEmuFolder": "Åpne Ryujinx mappe",
"MenuBarFileOpenLogsFolder": "Åpne Logg mappen",
"MenuBarFileExit": "_Avslutt",
"MenuBarOptions": "_Alternativer",
"MenuBarOptionsToggleFullscreen": "Fullskjermsvisning av/på",
"MenuBarOptionsStartGamesInFullscreen": "Start spill i Fullskjermmodus",
"MenuBarOptionsStopEmulation": "Stopp Emulering",
"MenuBarOptionsSettings": "_Innstillinger",
"MenuBarOptionsManageUserProfiles": "_Administrere Brukerprofiler",
"MenuBarActions": "_Handlinger",
"MenuBarOptionsSimulateWakeUpMessage": "Simuler oppvåknings-melding",
"MenuBarActionsScanAmiibo": "Skann en Amiibo",
"MenuBarTools": "_Verktøy",
"MenuBarToolsInstallFirmware": "Installer fastvare",
"MenuBarFileToolsInstallFirmwareFromFile": "Installer en fastvare fra XCI eller ZIP",
"MenuBarFileToolsInstallFirmwareFromDirectory": "Installer en fastvare fra en mappe",
"MenuBarToolsManageFileTypes": "Behandle filtyper",
"MenuBarToolsInstallFileTypes": "Installer filtyper",
"MenuBarToolsUninstallFileTypes": "Avinstaller filtyper",
"MenuBarView": "_Vis",
"MenuBarViewWindow": "Vindu størrelse",
"MenuBarViewWindow720": "720p",
"MenuBarViewWindow1080": "1080p",
"MenuBarHelp": "_Hjelp",
"MenuBarHelpCheckForUpdates": "Se etter oppdateringer",
"MenuBarHelpAbout": "Om",
"MenuSearch": "Søk ...",
"GameListHeaderFavorite": "Fav",
"GameListHeaderIcon": "Ikon",
"GameListHeaderApplication": "Navn",
"GameListHeaderDeveloper": "Utvikler",
"GameListHeaderVersion": "Versjon",
"GameListHeaderTimePlayed": "Spilletid",
"GameListHeaderLastPlayed": "Sist Spilt",
"GameListHeaderFileExtension": "Fil Eks.",
"GameListHeaderFileSize": "Fil Størrelse",
"GameListHeaderPath": "Bane",
"GameListContextMenuOpenUserSaveDirectory": "Åpne bruker lagrings mappe",
"GameListContextMenuOpenUserSaveDirectoryToolTip": "Åpner mappen som inneholder programmets bruker lagring",
"GameListContextMenuOpenDeviceSaveDirectory": "Åpne lagringsmappe for enheten",
"GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Åpner mappen som inneholder programmets lagringsenhet",
"GameListContextMenuOpenBcatSaveDirectory": "Åpne BCAT lagringsmappe",
"GameListContextMenuOpenBcatSaveDirectoryToolTip": "Åpner mappen som inneholder programmets BCAT-lagring",
"GameListContextMenuManageTitleUpdates": "Administrer titteloppdateringer",
"GameListContextMenuManageTitleUpdatesToolTip": "Åpner vinduet Tittel - Oppdateringskontroll",
"GameListContextMenuManageDlc": "Administrer DLC",
"GameListContextMenuManageDlcToolTip": "Åpner DLC håndteringsvinduet",
"GameListContextMenuCacheManagement": "Cache administrasjon",
"GameListContextMenuCacheManagementPurgePptc": "Start PPTC gjenoppbygging",
"GameListContextMenuCacheManagementPurgePptcToolTip": "Utløs PPTC for å gjenoppbygge ved oppstart av neste spill-start",
"GameListContextMenuCacheManagementPurgeShaderCache": "Tøm shader cache",
"GameListContextMenuCacheManagementPurgeShaderCacheToolTip": "Sletter applikasjonens shader cache",
"GameListContextMenuCacheManagementOpenPptcDirectory": "Åpne PPTC mappe",
"GameListContextMenuCacheManagementOpenPptcDirectoryToolTip": "Åpner mappen som inneholder programmets PPTC cache",
"GameListContextMenuCacheManagementOpenShaderCacheDirectory": "Åpne Shader Cache-mappen",
"GameListContextMenuCacheManagementOpenShaderCacheDirectoryToolTip": "Åpner mappen som inneholder Programmets shader cache",
"GameListContextMenuExtractData": "Hent ut data",
"GameListContextMenuExtractDataExeFS": "ExeFS",
"GameListContextMenuExtractDataExeFSToolTip": "Pakk ut ExeFS seksjonen fra Programmets gjeldende konfigurasjon (inkludert oppdateringer)",
"GameListContextMenuExtractDataRomFS": "RomFS",
"GameListContextMenuExtractDataRomFSToolTip": "Pakk ut RomFS seksjonen fra applikasjonens gjeldende konfigurasjon (inkludert oppdateringer)",
"GameListContextMenuExtractDataLogo": "Logo",
"GameListContextMenuExtractDataLogoToolTip": "Pakk ut Logo-seksjonen fra applikasjonens gjeldende konfigurasjon (inkludert oppdateringer)",
"GameListContextMenuCreateShortcut": "Lag programsnarvei",
"GameListContextMenuCreateShortcutToolTip": "Lag en snarvei på skrivebordet som starter den valgte Applikasjonen",
"GameListContextMenuCreateShortcutToolTipMacOS": "Lag snarvei i macOSs Program-mappen som starter det valgte programmet",
"GameListContextMenuOpenModsDirectory": "Åpne Modifikasjonsmappen",
"GameListContextMenuOpenModsDirectoryToolTip": "Åpner mappen som inneholder programmets modifikasjoner",
"GameListContextMenuOpenSdModsDirectory": "Åpne Atmosfære modifikasjons mappen",
"GameListContextMenuOpenSdModsDirectoryToolTip": "Åpner den alternative SD-kortets Atmosfære-mappe som inneholder programmoduser. Nyttig for modifikasjoner som er pakket for ekte maskinvare.",
"StatusBarGamesLoaded": "{0}/{1} Spill Lastet",
"StatusBarSystemVersion": "System versjon: {0}",
"LinuxVmMaxMapCountDialogTitle": "Lav grense for minnetildelinger oppdaget",
"LinuxVmMaxMapCountDialogTextPrimary": "Ønsker du å øke verdien av vm.max_map_count til {0}",
"LinuxVmMaxMapCountDialogTextSecondary": "Noen spill kan prøve å lage flere minnekartlegging enn det som er tillatt. Ryujinx vil krasjes så snart denne grensen overskrides.",
"LinuxVmMaxMapCountDialogButtonUntilRestart": "Ja, frem til neste omstart",
"LinuxVmMaxMapCountDialogButtonPersistent": "Ja, permanent",
"LinuxVmMaxMapCountWarningTextPrimary": "Den maksimale mengden Minnetilordninger er lavere enn anbefalt.",
"LinuxVmMaxMapCountWarningTextSecondary": "Gjeldende verdi av vm.max_map_count ({0}) er lavere enn {1}. Noen spill kan prøve å lage flere minnedelinger enn det som er tillatt. Ryujinx vil kræsje så snart denne grensen overskrides.\n\nDet kan hende du ønsker å enten øke grensen eller installere pkexec manuelt, slik at Ryujinx kan hjelpe til med det.",
"Settings": "Innstillinger",
"SettingsTabGeneral": "Brukergrensesnitt",
"SettingsTabGeneralGeneral": "Generelt",
"SettingsTabGeneralEnableDiscordRichPresence": "Aktiver Discord Rik Tilstedeværelse",
"SettingsTabGeneralCheckUpdatesOnLaunch": "Se etter oppdateringer ved oppstart",
"SettingsTabGeneralShowConfirmExitDialog": "Vis \"Bekreft Avslutt\" vinduet",
"SettingsTabGeneralRememberWindowState": "Husk vinduets størrelse/posisjon",
"SettingsTabGeneralHideCursor": "Skjul musepeker:",
"SettingsTabGeneralHideCursorNever": "Aldri",
"SettingsTabGeneralHideCursorOnIdle": "Når inaktiv",
"SettingsTabGeneralHideCursorAlways": "Alltid",
"SettingsTabGeneralGameDirectories": "Spillmapper",
"SettingsTabGeneralAdd": "Legg til",
"SettingsTabGeneralRemove": "Fjern",
"SettingsTabSystem": "System",
"SettingsTabSystemCore": "Kjerne",
"SettingsTabSystemSystemRegion": "System region:",
"SettingsTabSystemSystemRegionJapan": "Japan",
"SettingsTabSystemSystemRegionUSA": "USA",
"SettingsTabSystemSystemRegionEurope": "Europa",
"SettingsTabSystemSystemRegionAustralia": "Australia",
"SettingsTabSystemSystemRegionChina": "Kina",
"SettingsTabSystemSystemRegionKorea": "Korea",
"SettingsTabSystemSystemRegionTaiwan": "Taiwan",
"SettingsTabSystemSystemLanguage": "Systemspråk",
"SettingsTabSystemSystemLanguageJapanese": "Japansk",
"SettingsTabSystemSystemLanguageAmericanEnglish": "Amerikansk engelsk",
"SettingsTabSystemSystemLanguageFrench": "Fransk",
"SettingsTabSystemSystemLanguageGerman": "Tysk",
"SettingsTabSystemSystemLanguageItalian": "Italiensk",
"SettingsTabSystemSystemLanguageSpanish": "Spansk",
"SettingsTabSystemSystemLanguageChinese": "Kinesisk",
"SettingsTabSystemSystemLanguageKorean": "Koreansk",
"SettingsTabSystemSystemLanguageDutch": "Nederlandsk",
"SettingsTabSystemSystemLanguagePortuguese": "Portugisisk",
"SettingsTabSystemSystemLanguageRussian": "Russisk",
"SettingsTabSystemSystemLanguageTaiwanese": "Taiwansk",
"SettingsTabSystemSystemLanguageBritishEnglish": "Britisk engelsk",
"SettingsTabSystemSystemLanguageCanadianFrench": "Canadisk Fransk",
"SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Latinamerikansk spansk",
"SettingsTabSystemSystemLanguageSimplifiedChinese": "Forenklet kinesisk",
"SettingsTabSystemSystemLanguageTraditionalChinese": "Tradisjonell Kinesisk",
"SettingsTabSystemSystemTimeZone": "System Tidssone:",
"SettingsTabSystemSystemTime": "System tid:",
"SettingsTabSystemEnableVsync": "VSynk",
"SettingsTabSystemEnablePptc": "PPTC (Profilert Vedvarende Oversettelseshurtigbuffer)",
"SettingsTabSystemEnableFsIntegrityChecks": "FS Integritetssjekk",
"SettingsTabSystemAudioBackend": "Lyd Backend:",
"SettingsTabSystemAudioBackendDummy": "Dummy",
"SettingsTabSystemAudioBackendOpenAL": "OpenAL",
"SettingsTabSystemAudioBackendSoundIO": "Lyd Inn/Ut",
"SettingsTabSystemAudioBackendSDL2": "SDL2",
"SettingsTabSystemHacks": "Hacks",
"SettingsTabSystemHacksNote": "Kan forårsake ustabilitet",
"SettingsTabSystemExpandDramSize": "Utvid DRAM til 8GiB",
"SettingsTabSystemIgnoreMissingServices": "Ignorer manglende tjenester",
"SettingsTabGraphics": "Grafikk",
"SettingsTabGraphicsAPI": "Grafikk API",
"SettingsTabGraphicsEnableShaderCache": "Aktiver Shader Cachen",
"SettingsTabGraphicsAnisotropicFiltering": "Anisotropisk filtrering:",
"SettingsTabGraphicsAnisotropicFilteringAuto": "Automatisk",
"SettingsTabGraphicsAnisotropicFiltering2x": "2x",
"SettingsTabGraphicsAnisotropicFiltering4x": "4x",
"SettingsTabGraphicsAnisotropicFiltering8x": "8x",
"SettingsTabGraphicsAnisotropicFiltering16x": "16x",
"SettingsTabGraphicsResolutionScale": "Oppløsnings skala:",
"SettingsTabGraphicsResolutionScaleCustom": "Egendefinert (anbefales ikke)",
"SettingsTabGraphicsResolutionScaleNative": "Naturlig (720p/1080p)",
"SettingsTabGraphicsResolutionScale2x": "2 x (1440p/2160p)",
"SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)",
"SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (anbefales ikke)",
"SettingsTabGraphicsAspectRatio": "Bildeformat",
"SettingsTabGraphicsAspectRatio4x3": "4:3",
"SettingsTabGraphicsAspectRatio16x9": "16:9",
"SettingsTabGraphicsAspectRatio16x10": "16:10",
"SettingsTabGraphicsAspectRatio21x9": "21:9",
"SettingsTabGraphicsAspectRatio32x9": "32:9",
"SettingsTabGraphicsAspectRatioStretch": "Strekk for og Tilpasse vindu",
"SettingsTabGraphicsDeveloperOptions": "Utvikleralternativer",
"SettingsTabGraphicsShaderDumpPath": "Grafikk Shader Dump bane:",
"SettingsTabLogging": "Logging",
"SettingsTabLoggingLogging": "Logging",
"SettingsTabLoggingEnableLoggingToFile": "Aktiver logging til fil",
"SettingsTabLoggingEnableStubLogs": "Aktiver Stub-logger",
"SettingsTabLoggingEnableInfoLogs": "Aktiver informasjonslogger",
"SettingsTabLoggingEnableWarningLogs": "Aktiver varsellogger",
"SettingsTabLoggingEnableErrorLogs": "Aktiver feillogger",
"SettingsTabLoggingEnableTraceLogs": "Aktiver spor logger",
"SettingsTabLoggingEnableGuestLogs": "Aktiver gjestelogger",
"SettingsTabLoggingEnableFsAccessLogs": "Aktiver Fs tilgangslogger",
"SettingsTabLoggingFsGlobalAccessLogMode": "Fs Global Access-logg-modus:",
"SettingsTabLoggingDeveloperOptions": "Utvikleralternativer",
"SettingsTabLoggingDeveloperOptionsNote": "Advarsel: Vil redusere ytelsen",
"SettingsTabLoggingGraphicsBackendLogLevel": "Grafikk Backend-loggnivå:",
"SettingsTabLoggingGraphicsBackendLogLevelNone": "Ingen",
"SettingsTabLoggingGraphicsBackendLogLevelError": "Feil",
"SettingsTabLoggingGraphicsBackendLogLevelPerformance": "Tregere",
"SettingsTabLoggingGraphicsBackendLogLevelAll": "Alle",
"SettingsTabLoggingEnableDebugLogs": "Aktiver feilsøkingslogger",
"SettingsTabInput": "Inndata",
"SettingsTabInputEnableDockedMode": "Forankret modus",
"SettingsTabInputDirectKeyboardAccess": "Direkte tastaturtilgang",
"SettingsButtonSave": "Lagre",
"SettingsButtonClose": "Lukk",
"SettingsButtonOk": "OK",
"SettingsButtonCancel": "Avbryt",
"SettingsButtonApply": "Bruk",
"ControllerSettingsPlayer": "Spiller",
"ControllerSettingsPlayer1": "Spiller 1",
"ControllerSettingsPlayer2": "Spiller 2",
"ControllerSettingsPlayer3": "Spiller 3",
"ControllerSettingsPlayer4": "Spiller 4",
"ControllerSettingsPlayer5": "Spiller 5",
"ControllerSettingsPlayer6": "Spiller 6",
"ControllerSettingsPlayer7": "Spiller 7",
"ControllerSettingsPlayer8": "Spiller 8",
"ControllerSettingsHandheld": "Håndholdt",
"ControllerSettingsInputDevice": "Inndataenhet",
"ControllerSettingsRefresh": "Oppdater",
"ControllerSettingsDeviceDisabled": "Deaktivert",
"ControllerSettingsControllerType": "Kontrollertype",
"ControllerSettingsControllerTypeHandheld": "Håndholdt",
"ControllerSettingsControllerTypeProController": "Pro Controller",
"ControllerSettingsControllerTypeJoyConPair": "JoyCon Par",
"ControllerSettingsControllerTypeJoyConLeft": "JoyCon venstre",
"ControllerSettingsControllerTypeJoyConRight": "JoyCon høyre",
"ControllerSettingsProfile": "Profil",
"ControllerSettingsProfileDefault": "Standard",
"ControllerSettingsLoad": "Last",
"ControllerSettingsAdd": "Legg til",
"ControllerSettingsRemove": "Fjern",
"ControllerSettingsButtons": "Knapper",
"ControllerSettingsButtonA": "A",
"ControllerSettingsButtonB": "B",
"ControllerSettingsButtonX": "X",
"ControllerSettingsButtonY": "Y",
"ControllerSettingsButtonPlus": "+",
"ControllerSettingsButtonMinus": "-",
"ControllerSettingsDPad": "Retningsfelt",
"ControllerSettingsDPadUp": "Opp",
"ControllerSettingsDPadDown": "Ned",
"ControllerSettingsDPadLeft": "Venstre",
"ControllerSettingsDPadRight": "Høyre",
"ControllerSettingsStickButton": "Knapp",
"ControllerSettingsStickUp": "Opp",
"ControllerSettingsStickDown": "Ned",
"ControllerSettingsStickLeft": "Venstre",
"ControllerSettingsStickRight": "Høyre",
"ControllerSettingsStickStick": "Styrespak",
"ControllerSettingsStickInvertXAxis": "Inverter Styrespak X",
"ControllerSettingsStickInvertYAxis": "Inverter Styrespak Y",
"ControllerSettingsStickDeadzone": "Død sone:",
"ControllerSettingsLStick": "Venstre styrespak",
"ControllerSettingsRStick": "Høyre styrespak",
"ControllerSettingsTriggersLeft": "Utløsere venstre",
"ControllerSettingsTriggersRight": "Utløsere høyre",
"ControllerSettingsTriggersButtonsLeft": "Utløserknapper Venstre",
"ControllerSettingsTriggersButtonsRight": "Utløserknapper høyre",
"ControllerSettingsTriggers": "Utløsere",
"ControllerSettingsTriggerL": "L",
"ControllerSettingsTriggerR": "R",
"ControllerSettingsTriggerZL": "ZL",
"ControllerSettingsTriggerZR": "ZR",
"ControllerSettingsLeftSL": "SL",
"ControllerSettingsLeftSR": "SR",
"ControllerSettingsRightSL": "SL",
"ControllerSettingsRightSR": "SR",
"ControllerSettingsExtraButtonsLeft": "Knapper til venstre",
"ControllerSettingsExtraButtonsRight": "Knapper til høyre",
"ControllerSettingsMisc": "Diverse",
"ControllerSettingsTriggerThreshold": "Utløser terskel:",
"ControllerSettingsMotion": "Bevegelse",
"ControllerSettingsMotionUseCemuhookCompatibleMotion": "Bruk CemuHook kompatibel bevegelse",
"ControllerSettingsMotionControllerSlot": "Kontrollertype:",
"ControllerSettingsMotionMirrorInput": "Speilvend",
"ControllerSettingsMotionRightJoyConSlot": "Høyre JoyCon set:",
"ControllerSettingsMotionServerHost": "Server Vert:",
"ControllerSettingsMotionGyroSensitivity": "Gyro følsomhet:",
"ControllerSettingsMotionGyroDeadzone": "Gyro Dødsone:",
"ControllerSettingsSave": "Lagre",
"ControllerSettingsClose": "Lukk",
"KeyUnknown": "Ukjent",
"KeyShiftLeft": "Skift venstre",
"KeyShiftRight": "Skift høyre",
"KeyControlLeft": "Ctrl venstre",
"KeyMacControlLeft": "⌃ Venstre",
"KeyControlRight": "Ctrl høyre",
"KeyMacControlRight": "⌃ Høyre",
"KeyAltLeft": "Alt Venstre",
"KeyMacAltLeft": "⌥ Venstre",
"KeyAltRight": "Alt høyre",
"KeyMacAltRight": "⌥ Høyre",
"KeyWinLeft": "⊞ Venstre",
"KeyMacWinLeft": "⌘ Venstre",
"KeyWinRight": "⊞ Høyre",
"KeyMacWinRight": "⌘ Høyre",
"KeyMenu": "Meny",
"KeyUp": "Opp",
"KeyDown": "Ned",
"KeyLeft": "Venstre",
"KeyRight": "Høyre",
"KeyEnter": "Enter",
"KeyEscape": "Esc-tast",
"KeySpace": "Mellomrom",
"KeyTab": "Tab",
"KeyBackSpace": "Tilbaketast",
"KeyInsert": "Sett inn",
"KeyDelete": "Slett",
"KeyPageUp": "Side opp",
"KeyPageDown": "Side ned",
"KeyHome": "Hjem",
"KeyEnd": "Avslutt",
"KeyCapsLock": "Skiftelås",
"KeyScrollLock": "Rullelås",
"KeyPrintScreen": "Skjermbilde",
"KeyPause": "Stans midlertidig",
"KeyNumLock": "Numerisk Lås",
"KeyClear": "Tøm",
"KeyKeypad0": "Numerisk 0",
"KeyKeypad1": "Numerisk 1",
"KeyKeypad2": "Numerisk 2",
"KeyKeypad3": "Numerisk 3",
"KeyKeypad4": "Numerisk 4",
"KeyKeypad5": "Numerisk 5",
"KeyKeypad6": "Numerisk 6",
"KeyKeypad7": "Numerisk 7",
"KeyKeypad8": "Numerisk 8",
"KeyKeypad9": "Numerisk 9",
"KeyKeypadDivide": "Numerisk Dele",
"KeyKeypadMultiply": "Numerisk Multiplisere",
"KeyKeypadSubtract": "Numerisk Subtrakt",
"KeyKeypadAdd": "Numerisk Legg til",
"KeyKeypadDecimal": "Numerisk Desimal",
"KeyKeypadEnter": "Numerisk Enter",
"KeyNumber0": "0",
"KeyNumber1": "1",
"KeyNumber2": "2",
"KeyNumber3": "3",
"KeyNumber4": "4",
"KeyNumber5": "5",
"KeyNumber6": "6",
"KeyNumber7": "7",
"KeyNumber8": "8",
"KeyNumber9": "9",
"KeyTilde": "~",
"KeyGrave": "`",
"KeyMinus": "-",
"KeyPlus": "+",
"KeyBracketLeft": "[",
"KeyBracketRight": "]",
"KeySemicolon": ";",
"KeyQuote": "\"",
"KeyComma": ",",
"KeyPeriod": ".",
"KeySlash": "/",
"KeyBackSlash": "\\",
"KeyUnbound": "Ikke bundet",
"GamepadLeftStick": "Venstre Styrespak Trykk",
"GamepadRightStick": "R Styrespak Trykk",
"GamepadLeftShoulder": "Venstre Skulder",
"GamepadRightShoulder": "Høyre Skulder",
"GamepadLeftTrigger": "Venstre utløser",
"GamepadRightTrigger": "Høyre utløser",
"GamepadDpadUp": "Opp",
"GamepadDpadDown": "Ned",
"GamepadDpadLeft": "Venstre",
"GamepadDpadRight": "Høyre",
"GamepadMinus": "-",
"GamepadPlus": "+",
"GamepadGuide": "Veiledning",
"GamepadMisc1": "Diverse",
"GamepadPaddle1": "Paddle 1",
"GamepadPaddle2": "Paddle 2",
"GamepadPaddle3": "Paddle 3",
"GamepadPaddle4": "Paddle 4",
"GamepadTouchpad": "Berøringsplate",
"GamepadSingleLeftTrigger0": "Venstre utløser 0",
"GamepadSingleRightTrigger0": "Høyre utløser 0",
"GamepadSingleLeftTrigger1": "Venstre utløser 1",
"GamepadSingleRightTrigger1": "Høyre utløser 1",
"StickLeft": "Venstre styrespak",
"StickRight": "Høyre styrespak",
"UserProfilesSelectedUserProfile": "Valgt brukerprofil:",
"UserProfilesSaveProfileName": "Lagre profilnavnet",
"UserProfilesChangeProfileImage": "Endre profilbilde",
"UserProfilesAvailableUserProfiles": "Tilgjengelige brukerprofiler:",
"UserProfilesAddNewProfile": "Opprett Profil",
"UserProfilesDelete": "Slett",
"UserProfilesClose": "Lukk",
"ProfileNameSelectionWatermark": "Velg ett kallenavn",
"ProfileImageSelectionTitle": "Valg av profilbilde",
"ProfileImageSelectionHeader": "Velg et profilbilde",
"ProfileImageSelectionNote": "Du kan importere et tilpasset profilbilde, eller velge en avatar fra system fastvare",
"ProfileImageSelectionImportImage": "Importer bildefil",
"ProfileImageSelectionSelectAvatar": "Velg fastvare profilbilde",
"InputDialogTitle": "Input Dialog",
"InputDialogOk": "OK",
"InputDialogCancel": "Avbryt",
"InputDialogAddNewProfileTitle": "Velg profilnavnet",
"InputDialogAddNewProfileHeader": "Vennligst skriv inn et profilnavn",
"InputDialogAddNewProfileSubtext": "(Maks lengde: {0})",
"AvatarChoose": "Velg profilbilde",
"AvatarSetBackgroundColor": "Angi bakgrunnsfarge",
"AvatarClose": "Lukk",
"ControllerSettingsLoadProfileToolTip": "Last inn profil",
"ControllerSettingsAddProfileToolTip": "Legg til profil",
"ControllerSettingsRemoveProfileToolTip": "Fjern profil",
"ControllerSettingsSaveProfileToolTip": "Lagre Profil",
"MenuBarFileToolsTakeScreenshot": "Ta skjermbilde",
"MenuBarFileToolsHideUi": "Skjul brukergrensesnitt",
"GameListContextMenuRunApplication": "Kjør programmet",
"GameListContextMenuToggleFavorite": "Vis/Skjul favoritter",
"GameListContextMenuToggleFavoriteToolTip": "Vis/Skjul favorittstatus for spillet",
"SettingsTabGeneralTheme": "Tema:",
"SettingsTabGeneralThemeAuto": "Automatisk",
"SettingsTabGeneralThemeDark": "Mørk",
"SettingsTabGeneralThemeLight": "Lys",
"ControllerSettingsConfigureGeneral": "Konfigurer",
"ControllerSettingsRumble": "Vibrasjon",
"ControllerSettingsRumbleStrongMultiplier": "Sterk Vibrasjon multiplikator",
"ControllerSettingsRumbleWeakMultiplier": "Svak Vibrasjon multiplikator",
"DialogMessageSaveNotAvailableMessage": "Det er ingen lagrede data for {0} [{1:x16}]",
"DialogMessageSaveNotAvailableCreateSaveMessage": "Vil du lage lagrede data for dette spillet",
"DialogConfirmationTitle": "Ryujinx - Bekreftelse",
"DialogUpdaterTitle": "Ryujinx Oppdaterer",
"DialogErrorTitle": "Ryujinx - Feil",
"DialogWarningTitle": "Ryujinx - Advarsel",
"DialogExitTitle": "Ryujinx - Avslutt",
"DialogErrorMessage": "Ryujinx har støtt på ett problem",
"DialogExitMessage": "Er du sikker på at du ønsker å lukke Ryujinx?",
"DialogExitSubMessage": "Alle ulagrede data vil gå tapt!",
"DialogMessageCreateSaveErrorMessage": "Det oppstod en feil under oppretting av den angitte lagredata: {0}",
"DialogMessageFindSaveErrorMessage": "Det oppstod en feil under oppretting av den angitte lagredata: {0}",
"FolderDialogExtractTitle": "Velg mappen å pakke ut i",
"DialogNcaExtractionMessage": "Trekker ut {0} seksjonen fra {1}...",
"DialogNcaExtractionTitle": "Ryujinx - NCA Seksjon Ekstraktor",
"DialogNcaExtractionMainNcaNotFoundErrorMessage": "Uttrekksfeil. Hoveddelen av NCA var ikke tilstede i valgt fil.",
"DialogNcaExtractionCheckLogErrorMessage": "Uttrekkingsfeil. Les loggfilen for mer informasjon.",
"DialogNcaExtractionSuccessMessage": "Utvinningen er fullført.",
"DialogUpdaterConvertFailedMessage": "Kunne ikke konvertere gjeldende Ryujinx-versjon.",
"DialogUpdaterCancelUpdateMessage": "Avbryter oppdatering!",
"DialogUpdaterAlreadyOnLatestVersionMessage": "Du bruker allerede den nyeste versjonen av Ryujinx!",
"DialogUpdaterFailedToGetVersionMessage": "En feil oppstod ved forsøk på å få utgivelsesinformasjon fra GitHub Utgivelse. Dette kan forårsakes hvis en ny utgave blir samlet av GitHub Handlinger. Prøv igjen om noen minutter.",
"DialogUpdaterConvertFailedGithubMessage": "Kan ikke konvertere mottatt Ryujinx-versjon fra Github Utgivelse.",
"DialogUpdaterDownloadingMessage": "Laster ned oppdatering...",
"DialogUpdaterExtractionMessage": "Pakker ut oppdatering...",
"DialogUpdaterRenamingMessage": "Endrer navn på oppdatering...",
"DialogUpdaterAddingFilesMessage": "Legger til ny oppdatering...",
"DialogUpdaterCompleteMessage": "Oppdateringen er fullført!",
"DialogUpdaterRestartMessage": "Vil du starte Ryujinx på nytt nå?",
"DialogUpdaterNoInternetMessage": "Du er ikke tilkoblet internett",
"DialogUpdaterNoInternetSubMessage": "Kontroller at du har en fungerende Internett-tilkobling!",
"DialogUpdaterDirtyBuildMessage": "Du kan ikke oppdatere en skitten versjon av Ryujinx!",
"DialogUpdaterDirtyBuildSubMessage": "Vennligst last ned Ryujinx på https://ryujinx.org/ hvis du ser etter en støttet versjon.",
"DialogRestartRequiredMessage": "Omstart Kreves",
"DialogThemeRestartMessage": "Temaet har blitt lagret. En omstart kreves for å bruke temaet.",
"DialogThemeRestartSubMessage": "Vil du starte på nytt",
"DialogFirmwareInstallEmbeddedMessage": "Ønsker du å installere fastvaren innebygd i dette spillet? (Firmware {0})",
"DialogFirmwareInstallEmbeddedSuccessMessage": "Det ble ikke funnet noen installert fastvare, men Ryujinx kunne installere fastvare {0} fra det oppgitte spillet.\nemulatoren vil nå starte.",
"DialogFirmwareNoFirmwareInstalledMessage": "Ingen fastvare installert",
"DialogFirmwareInstalledMessage": "fastvare {0} ble installert",
"DialogInstallFileTypesSuccessMessage": "Filtyper ble installert!",
"DialogInstallFileTypesErrorMessage": "Kunne ikke installere filtyper.",
"DialogUninstallFileTypesSuccessMessage": "Filtyper ble avinstallert!",
"DialogUninstallFileTypesErrorMessage": "Kunne ikke avinstallere filtyper.",
"DialogOpenSettingsWindowLabel": "Åpne innstillinger-vinduet",
"DialogControllerAppletTitle": "Kontroller Applet",
"DialogMessageDialogErrorExceptionMessage": "Feil ved visning av meldings-dialog: {0}",
"DialogSoftwareKeyboardErrorExceptionMessage": "Feil ved visning av programvaretastatur: {0}",
"DialogErrorAppletErrorExceptionMessage": "Feil ved visning av Feilmeldingsdialog: {0}",
"DialogUserErrorDialogMessage": "{0}: {1}",
"DialogUserErrorDialogInfoMessage": "\nFor mer informasjon om hvordan du fikser denne feilen, følg vår oppsettsveiledning.",
"DialogUserErrorDialogTitle": "Ryujinx Feilmelding ({0})",
"DialogAmiiboApiTitle": "Amiibo API",
"DialogAmiiboApiFailFetchMessage": "En feil oppstod under henting av informasjon fra API.",
"DialogAmiiboApiConnectErrorMessage": "Kan ikke koble til Amiibo API server. Tjenesten kan være nede, eller du må kanskje verifisere at din internettforbindelse er tilkoblet.",
"DialogProfileInvalidProfileErrorMessage": "Profil {0} er ikke kompatibel med den gjeldende inndata konfigurasjonen",
"DialogProfileDefaultProfileOverwriteErrorMessage": "Standard profil kan ikke overskrives",
"DialogProfileDeleteProfileTitle": "Sletter profil",
"DialogProfileDeleteProfileMessage": "Denne handlingen er irreversibel, er du sikker på at du vil fortsette?",
"DialogWarning": "Advarsel",
"DialogPPTCDeletionMessage": "Du er i ferd med å bygge en PPTC i køen ved neste oppstart av:\n\n{0}\n\nEr du sikker på at du vil fortsette?",
"DialogPPTCDeletionErrorMessage": "Feil under rensing av PPTC cache ved {0}: {1}",
"DialogShaderDeletionMessage": "Du er i ferd med å slette Shader cachen for :\n\n{0}\n\nEr du sikker på at du vil fortsette?",
"DialogShaderDeletionErrorMessage": "Feil under tømming av Shader cache ved {0}: {1}",
"DialogRyujinxErrorMessage": "Ryujinx har støtt på ett problem",
"DialogInvalidTitleIdErrorMessage": "UI-feil: Det valgte spillet har ikke en gyldig tittel-ID",
"DialogFirmwareInstallerFirmwareNotFoundErrorMessage": "En gyldig systemfastvare ble ikke funnet i {0}.",
"DialogFirmwareInstallerFirmwareInstallTitle": "Installer fastvare {0}",
"DialogFirmwareInstallerFirmwareInstallMessage": "Systemversjon {0} vil bli installert.",
"DialogFirmwareInstallerFirmwareInstallSubMessage": "\n\nDette erstatter den gjeldende systemversjonen {0}.",
"DialogFirmwareInstallerFirmwareInstallConfirmMessage": "Vil du fortsette?",
"DialogFirmwareInstallerFirmwareInstallWaitMessage": "Installerer fastvare...",
"DialogFirmwareInstallerFirmwareInstallSuccessMessage": "Systemversjon {0} ble installert.",
"DialogUserProfileDeletionWarningMessage": "Det vil ikke være noen profiler å åpnes hvis valgt profil blir slettet",
"DialogUserProfileDeletionConfirmMessage": "Vil du slette den valgte profilen",
"DialogUserProfileUnsavedChangesTitle": "Advarsel - Ulagrede endringer",
"DialogUserProfileUnsavedChangesMessage": "Du har gjort endringer i denne brukerprofilen som ikke er lagret.",
"DialogUserProfileUnsavedChangesSubMessage": "Vil du forkaste endringene dine?",
"DialogControllerSettingsModifiedConfirmMessage": "Innstillinger for gjeldende kontroller har blitt oppdatert.",
"DialogControllerSettingsModifiedConfirmSubMessage": "Vil du lagre?",
"DialogLoadFileErrorMessage": "{0}. Feilet fil: {1}",
"DialogModAlreadyExistsMessage": "Modifikasjon eksisterer allerede",
"DialogModInvalidMessage": "Den angitte mappen inneholder ikke en modifikasjon!",
"DialogModDeleteNoParentMessage": "Kunne ikke slette: Fant ikke overordnet mappe for mod \"{0}\"!",
"DialogDlcNoDlcErrorMessage": "Den angitte filen inneholder ikke en DLC for den valgte tittelen!",
"DialogPerformanceCheckLoggingEnabledMessage": "Du har sporing aktivert, noe som er designet til å bli brukt av utviklere.",
"DialogPerformanceCheckLoggingEnabledConfirmMessage": "For optimal ytelse er det anbefalt å deaktivere spor-logging. Ønsker du å deaktivere spor-logging nå?",
"DialogPerformanceCheckShaderDumpEnabledMessage": "Du har aktiv dumping av shader, som bare er laget for å brukes av utviklere.",
"DialogPerformanceCheckShaderDumpEnabledConfirmMessage": "For optimal ytelse er det anbefalt å deaktivere dumping. Ønsker du å deaktivere shader dumping nå?",
"DialogLoadAppGameAlreadyLoadedMessage": "Et spill er allerede lastet inn",
"DialogLoadAppGameAlreadyLoadedSubMessage": "Slutt å emulere eller lukk emulatoren før du starter et annet spill.",
"DialogUpdateAddUpdateErrorMessage": "Den angitte filen inneholder ikke en oppdatering for den valgte tittelen!",
"DialogSettingsBackendThreadingWarningTitle": "Advarsel - Backend Tråd",
"DialogSettingsBackendThreadingWarningMessage": "Ryujinx må startes på nytt etter at dette alternativet er endret slik at det tas i bruk helt. Avhengig av plattformen din, må du kanskje manuelt skru av driveren's egen flertråder når du bruker Ryujinx's.",
"DialogModManagerDeletionWarningMessage": "Du er i ferd med å slette modifikasjonen: {0}\n\ner du sikker på at du vil fortsette?",
"DialogModManagerDeletionAllWarningMessage": "Du er i ferd med å slette alle modifikasjonene for denne tittelen: {0}\n\ner du sikker på at du vil fortsette?",
"SettingsTabGraphicsFeaturesOptions": "Funksjoner",
"SettingsTabGraphicsBackendMultithreading": "Grafikk Backend Fleroppgavekjøring",
"CommonAuto": "Automatisk",
"CommonOff": "Av",
"CommonOn": "På",
"InputDialogYes": "Ja",
"InputDialogNo": "Nei",
"DialogProfileInvalidProfileNameErrorMessage": "Filnavnet inneholder ugyldige tegn. Prøv på nytt.",
"MenuBarOptionsPauseEmulation": "Stans midlertidig",
"MenuBarOptionsResumeEmulation": "Gjenoppta",
"AboutUrlTooltipMessage": "Klikk for å åpne Ryujinx nettsiden i din standardnettleser.",
"AboutDisclaimerMessage": "Ryujinx er ikke knyttet til NintendoTM,\neller noen av samarbeidspartnerne sine, på noen som helst måte.",
"AboutAmiiboDisclaimerMessage": "AmiiboAPI (www.amiiboapi.com) brukes sammen med\ni vår Amiibo-emulsjon.",
"AboutPatreonUrlTooltipMessage": "Klikk for å åpne Ryujinx Patreon-siden i din standardnettleser.",
"AboutGithubUrlTooltipMessage": "Klikk for å åpne Ryujinx GitHub siden i din standardnettleser.",
"AboutDiscordUrlTooltipMessage": "Klikk for å åpne en invitasjon til Ryujinx Discord-serveren i nettleseren din.",
"AboutTwitterUrlTooltipMessage": "Klikk for å åpne Ryujinx Twitter-siden i din standardnettleser.",
"AboutRyujinxAboutTitle": "Om:",
"AboutRyujinxAboutContent": "Ryujinx er en emulator for Nintendo SwitchTM.\nVennligst støtt oss på Patreon.\nFå alle de siste nyhetene på vår Twitter eller Discord.\nUtviklere som er interessert i å bidra kan finne ut mer på GitHub eller Discord.",
"AboutRyujinxMaintainersTitle": "Vedlikeholdt av:",
"AboutRyujinxMaintainersContentTooltipMessage": "Klikk for å åpne Bidragsyter-siden i standardleseren din.",
"AboutRyujinxSupprtersTitle": "Støttet på Patreon av:",
"AmiiboSeriesLabel": "Amibo Serie",
"AmiiboCharacterLabel": "Karakter",
"AmiiboScanButtonLabel": "Skanne det",
"AmiiboOptionsShowAllLabel": "Vis alle Amiibo",
"AmiiboOptionsUsRandomTagLabel": "Hack: Bruk tilfeldig tag-Uuid",
"DlcManagerTableHeadingEnabledLabel": "Aktivert",
"DlcManagerTableHeadingTitleIdLabel": "Tittel ID",
"DlcManagerTableHeadingContainerPathLabel": "Beholder bane",
"DlcManagerTableHeadingFullPathLabel": "Fullstendig bane",
"DlcManagerRemoveAllButton": "Fjern alle",
"DlcManagerEnableAllButton": "Aktiver alle",
"DlcManagerDisableAllButton": "Deaktiver alle",
"ModManagerDeleteAllButton": "Slett alt",
"MenuBarOptionsChangeLanguage": "Endre språk",
"MenuBarShowFileTypes": "Vis Filtyper",
"CommonSort": "Sorter",
"CommonShowNames": "Vis navn",
"CommonFavorite": "Favoritt",
"OrderAscending": "Stigende",
"OrderDescending": "Synkende",
"SettingsTabGraphicsFeatures": "Funksjoner & forbedringer",
"ErrorWindowTitle": "Feilvindu",
"ToggleDiscordTooltip": "Velg om Ryujinx skal vises på din \"spillende\" Discord aktivitet eller ikke",
"AddGameDirBoxTooltip": "Angi en spillmappe for å legge til i listen",
"AddGameDirTooltip": "Legg til en spillmappe i listen",
"RemoveGameDirTooltip": "Fjern valgt spillmappe",
"CustomThemeCheckTooltip": "Bruk et egendefinert Avalonia tema for GUI for å endre utseende til emulatormenyene",
"CustomThemePathTooltip": "Bane til egendefinert GUI-tema",
"CustomThemeBrowseTooltip": "Søk etter et egendefinert GUI-tema",
"DockModeToggleTooltip": "Forankret modus gjør at systemet oppføre seg som en forankret Nintendo Switch. Dette forbedrer grafikkkvaliteten i de fleste spill. Motsatt vil deaktivering av dette gjøre at systemet oppføre seg som en håndholdt Nintendo Switch, noe som reduserer grafikkkvaliteten.\n\nKonfigurer spiller 1 kontroller hvis du planlegger å bruke forankret modus; konfigurer håndholdte kontroller hvis du planlegger å bruke håndholdte modus.\n\nLa PÅ hvis du er usikker.",
"DirectKeyboardTooltip": "Direkte tastaturtilgang (HID) støtte. Gir deg spill-tilgang til tastaturet som en tekstinnlegg-enhet.\n\nfungerer kun med spill som lokalt støtter tastaturbruk på Ninteno SwitchTM maskinvare.\n\nLa være AV hvis du er usikker.",
"DirectMouseTooltip": "Direkte musepeker (HID) støtte. Gir deg spill-tilgang til musepeker.\n\nfungerer kun med spill som lokalt støtter musepekere på Ninteno SwitchTM maskinvare.\n\nNår aktivert, kan det hende touch funksjoner ikke fungerer\n\nLa være AV hvis du er usikker.",
"RegionTooltip": "Endre systemregion",
"LanguageTooltip": "Endre systemspråk",
"TimezoneTooltip": "Endre systemtidssone",
"TimeTooltip": "Endre systemtid",
"VSyncToggleTooltip": "Emuler konsollens loddrett synkronisering. på ett vis en bildefrekvens begrensning for de fleste spill; deaktivering kan få spill til å kjøre med høyere hastighet, eller til å laste skjermene tar lengre tid eller sitter fast.\n\nkan byttes inn i spillet med en hurtigtast for preferansen (F1 som standard). Vi anbefaler å gjøre dette hvis du planlegger å deaktivere dette.\n\nLa være PÅ hvis du er usikker.",
"PptcToggleTooltip": "Lagrer oversatte JIT funksjoner så de ikke trenger og bli oversatt hver gang spillet laster.\n\nKan redusere hakkete spilling og gjør at spillet starter opp raskere ved første oppstart.\n\nLa være PÅ om usikker.",
"FsIntegrityToggleTooltip": "Sjekker for korrupte filer ved oppstart av et spill, og dersom korrupte filer oppdages, viser en hashfeil i loggen.\n\nhar ingen innvirkning på ytelsen og er ment å hjelpe med feilsøking.\n\nLa være PÅ hvis usikker.",
"AudioBackendTooltip": "Endrer backend brukt til å gjengi lyd.\n\nSDL2 er foretrukket, mens OpenAL og SoundIO brukes som reserveløsning. Dummy kommer ikke til å ha lyd.\n\nSett til SDL2 hvis usikker.",
"MemoryManagerTooltip": "Endre hvordan gjesteminne tilordnes og åpnes. Påvirker emulator CPU-ytelsen veldig mye.\n\nSett til HOST UNCHECKED hvis usikker.",
"MemoryManagerSoftwareTooltip": "Bruk en programvareside tabell for adresseoversettelse. Høyeste nøyaktighet, men tregeste ytelse.",
"MemoryManagerHostTooltip": "Direkte kartminne i vertens adresseområde. Mye raskere JIT kompilering og utførelse.",
"MemoryManagerUnsafeTooltip": "Direkte kartminne, men ikke masker adressen i gjesteadressen før du har tilgang. raskere, men på bekostning av sikkerhet. gjeste-programmet kan få tilgang til minne fra hvor som helst i Ryujinx, så bare kjøre programmer du stoler på med denne modusen.",
"UseHypervisorTooltip": "Bruk Hypervisor i stedet for JIT. Det øker ytelsen mye hvis det er tilgjengelig, men det kan være ustabilt i den nåværende tilstanden.",
"DRamTooltip": "Bruker en alternativ minnemodus med 8GiB i DRAM for og etterligne Switch utvikler modeller.\n\nDette er bare nyttig for teksturpakker eller 4k oppløsningsmoduler. Forbedrer IKKE ytelsen.\n\nLa AV hvis usikker.",
"IgnoreMissingServicesTooltip": "Ignorerer ikke implementerte Horisont OS-tjenester. Dette kan hjelpe med å omgå krasj ved oppstart av enkelte spill.\n\nLa AV hvis du er usikker.",
"GraphicsBackendThreadingTooltip": "Utfører grafikkbackend kommandoer på en annen tråd.\n\nØker hastigheten for shaderkomprimering, reduserer hakking og forbedrer ytelsen til GPU-drivere uten å spre støtten fra sine egne. Litt bedre ytelse på drivere med flertråd.\n\nSett for å AUTO hvis usikker.",
"GalThreadingTooltip": "Utfører grafikkbackend kommandoer på en annen tråd.\n\nØker hastigheten for shaderkomprimering, reduserer hakking og forbedrer ytelsen til GPU-drivere uten flertråd støtte. Litt bedre ytelse på drivere med flertråd.\n\nSett for å AUTO hvis usikker.",
"ShaderCacheToggleTooltip": "Lagrer en disk shader cache som reduserer hakking jo flere ganger du spiller.\n\nLa være PÅ om usikker.",
"ResolutionScaleTooltip": "Dobler spillets gjengivelse.\n\nNoen få spill fungerer kanskje ikke med dette aktivert og kan se veldig pikselert ut selv når gjengivelsen er økt; for de spillene, så kan det hende du må bruke modifikasjoner som fjerner anti-aliasing eller som øker den interne gjengivelsen. For og bruke sistnenvte, så vil du helst bruke \"Native\".\n\nHa til tanke at 4x er unødig for virituelt alle maskiner.",
"ResolutionScaleEntryTooltip": "Det er mer sannsynlig at flytende punktoppløsning skalaer som 1.5. Ikke-integrerte skalaer forårsaker problemer eller krasj.",
"AnisotropyTooltip": "Nivå av Anisotropisk filtrering. Sett til Auto for å bruke verdien som kreves av spillet.",
"AspectRatioTooltip": "Sideforhold angitt til gjengitt vindu.\n\nBare bytt dette om du bruker en modifikasjon som forandrer Sideforholdet på spillet ditt, ellers vil grafikken bli strukket.\n\nLa være på 16:9 om usikker.",
"ShaderDumpPathTooltip": "Grafikk Shader Dump bane",
"FileLogTooltip": "Lagrer konsoll-logging til en loggfil på harddisken. Påvirker ikke ytelsen.",
"StubLogTooltip": "Skriver ut log meldinger i konsollen. Påvirker ikke ytelsen.",
"InfoLogTooltip": "Skriver ut info loggmeldinger i konsollen. Påvirker ikke ytelse.",
"WarnLogTooltip": "Skriver ut varselloggmeldinger i konsollen. påvirker ikke ytelsen.",
"ErrorLogTooltip": "Skriver ut feilloggmeldinger i konsollen. Påvirker ikke ytelse.",
"TraceLogTooltip": "Skriver ut sporbare loggmeldinger i konsollen. påvirker ikke ytelsen.",
"GuestLogTooltip": "Skriver ut gjesteloggmeldinger i konsollen. påvirker ikke ytelsen.",
"FileAccessLogTooltip": "Skriver ut filtilgang til loggmeldinger i konsollen.",
"FSAccessLogModeTooltip": "Aktiverer FS tilgang loggutgang til konsollen. Mulige moduser er 0-3",
"DeveloperOptionTooltip": "Bruk med forsiktighet",
"OpenGlLogLevel": "Krever riktige loggnivåer aktivert",
"DebugLogTooltip": "Skriver ut loggmeldinger i konsollen.\n\nBruk bare dette hvis et medlem har gitt spesifikke instruksjoner, siden det vil gjøre loggene vanskelig å lese og forverre emulatorytelse.",
"LoadApplicationFileTooltip": "Åpne filutforsker for å velge en Switch kompatibel fil å laste",
"LoadApplicationFolderTooltip": "Åpne en filutforsker for å velge en Switch kompatibel, upakket applikasjon for å laste",
"OpenRyujinxFolderTooltip": "Åpne Ryujinx filsystem-mappen",
"OpenRyujinxLogsTooltip": "Åpner mappen hvor logger er lagret",
"ExitTooltip": "Avslutt Ryujinx",
"OpenSettingsTooltip": "Åpne innstillinger-vinduet",
"OpenProfileManagerTooltip": "Åpne vindu for brukerprofiler",
"StopEmulationTooltip": "Stopp emuleringen av dette spillet og gå tilbake til spill valg",
"CheckUpdatesTooltip": "Se etter oppdateringer til Ryujinx",
"OpenAboutTooltip": "Åpne Om Vindu",
"GridSize": "Rutenett størrelse",
"GridSizeTooltip": "Endre størrelsen på rutenettet elementer",
"SettingsTabSystemSystemLanguageBrazilianPortuguese": "Brasiliansk portugisisk",
"AboutRyujinxContributorsButtonHeader": "Se alle bidragsytere",
"SettingsTabSystemAudioVolume": "Lydnivå: ",
"AudioVolumeTooltip": "Endre lydenivå",
"SettingsTabSystemEnableInternetAccess": "Internett-tilgang for gjeste/LAN-modus",
"EnableInternetAccessTooltip": "Tillater emulert applikasjon å koble til Internett.\n\nSpill med en LAN-modus kan koble til hverandre når dette er aktivert og systemene er koblet til det samme tilgangspunktet. Dette inkluderer ekte konsoller også.\n\ntillater IKKE tilkobling til Nintendo servere. Kan forårsake krasjing i enkelte spill som prøver å koble til Internett.\n\nLa stå AV hvis du er usikker.",
"GameListContextMenuManageCheatToolTip": "Administrer juksemoduser",
"GameListContextMenuManageCheat": "Administrer juksemoduser",
"GameListContextMenuManageModToolTip": "Administrer modifikasjoner",
"GameListContextMenuManageMod": "Administrer modifikasjoner",
"ControllerSettingsStickRange": "Omfang:",
"DialogStopEmulationTitle": "Ryujinx - Stopp emulasjon",
"DialogStopEmulationMessage": "Er du sikker på at du vil stoppe emulasjonen?",
"SettingsTabCpu": "Prosessor",
"SettingsTabAudio": "Lyd",
"SettingsTabNetwork": "Nettverk",
"SettingsTabNetworkConnection": "Nettverk tilkobling",
"SettingsTabCpuCache": "CPU-buffer",
"SettingsTabCpuMemory": "Prosessor modus",
"DialogUpdaterFlatpakNotSupportedMessage": "Vennligst oppdater Ryujinx via FlatHub.",
"UpdaterDisabledWarningTitle": "Oppdatering Deaktivert!",
"ControllerSettingsRotate90": "Roter 90° med klokken",
"IconSize": "Ikon størrelse",
"IconSizeTooltip": "Endre størrelsen på spillikonene",
"MenuBarOptionsShowConsole": "Vis konsoll",
"ShaderCachePurgeError": "Feil under tømming av shader cache ved {0}: {1}",
"UserErrorNoKeys": "Finner ikke nøkler",
"UserErrorNoFirmware": "Fastvare ikke funnet",
"UserErrorFirmwareParsingFailed": "Fastvare analysefeil",
"UserErrorApplicationNotFound": "Applikasjon ikke funnet",
"UserErrorUnknown": "Ukjent feil",
"UserErrorUndefined": "Udefinert feil",
"UserErrorNoKeysDescription": "Ryujinx kunne ikke finne 'prod.keys' filen din",
"UserErrorNoFirmwareDescription": "Ryujinx kunne ikke finne noen fastvare installert",
"UserErrorFirmwareParsingFailedDescription": "Ryujinx klarte ikke å analysere levert fastvare. Dette er vanligvis forårsaket av utdaterte nøkler.",
"UserErrorApplicationNotFoundDescription": "Ryujinx kunne ikke finne en gyldig applikasjon på den gitte banen.",
"UserErrorUnknownDescription": "En ukjent feil oppstod!",
"UserErrorUndefinedDescription": "En udefinert feil oppstod! Dette burde ikke skje, vennligst kontakt en utvikler!",
"OpenSetupGuideMessage": "Åpne oppsettsveiledningen",
"NoUpdate": "Ingen oppdatering",
"TitleUpdateVersionLabel": "Versjon {0}",
"TitleBundledUpdateVersionLabel": "Pakket: Versjon {0}",
"TitleBundledDlcLabel": "Pakket:",
"RyujinxInfo": "Ryujinx - Informasjon",
"RyujinxConfirm": "Ryujinx - Bekreftelse",
"FileDialogAllTypes": "Alle typer",
"Never": "Aldri",
"SwkbdMinCharacters": "Må være minimum {0} tegn lang",
"SwkbdMinRangeCharacters": "Må være {0}-{1} tegn",
"SoftwareKeyboard": "Programvare Tastatur",
"SoftwareKeyboardModeNumeric": "Må kun være 0-9 eller '.'",
"SoftwareKeyboardModeAlphabet": "Må kun være uten CJK-tegn",
"SoftwareKeyboardModeASCII": "Må være kun ASCII-tekst",
"ControllerAppletControllers": "Støttede kontrollere:",
"ControllerAppletPlayers": "Spillere:",
"ControllerAppletDescription": "Din nåværende konfigurasjon er ugyldig. Åpne innstillinger og konfigurer inndata.",
"ControllerAppletDocked": "Docked modus. Håndholdt kontroll skal være deaktivert.",
"UpdaterRenaming": "Omdøper gamle filer...",
"UpdaterRenameFailed": "Oppdateringen kunne ikke gi filen nytt navn: {0}",
"UpdaterAddingFiles": "Legger til nye filer...",
"UpdaterExtracting": "Pakker ut oppdatering...",
"UpdaterDownloading": "Laster ned oppdatering...",
"Game": "Spill",
"Docked": "Forankret",
"Handheld": "Håndholdt",
"ConnectionError": "Tilkoblingsfeil",
"AboutPageDeveloperListMore": "{0} og mer...",
"ApiError": "API feil.",
"LoadingHeading": "Laster {0}",
"CompilingPPTC": "Sammensetter PTC",
"CompilingShaders": "Samler Shaders",
"AllKeyboards": "Alle tastaturer",
"OpenFileDialogTitle": "Velg en støttet fil for å åpne",
"OpenFolderDialogTitle": "Velg en mappe med et pakket ut spill",
"AllSupportedFormats": "Alle støttede formater",
"RyujinxUpdater": "Ryujinx Oppgradering",
"SettingsTabHotkeys": "Hurtigtaster for tastatur",
"SettingsTabHotkeysHotkeys": "Hurtigtaster for tastatur",
"SettingsTabHotkeysToggleVsyncHotkey": "Aktiver/deaktiver VSync:",
"SettingsTabHotkeysScreenshotHotkey": "Skjermbilde",
"SettingsTabHotkeysShowUiHotkey": "Vis UI:",
"SettingsTabHotkeysPauseHotkey": "Stans midlertidig:",
"SettingsTabHotkeysToggleMuteHotkey": "Demp:",
"ControllerMotionTitle": "Innstillinger for bevegelses kontroll",
"ControllerRumbleTitle": "Innstillinger for Vibrasjon",
"SettingsSelectThemeFileDialogTitle": "Velg tema fil",
"SettingsXamlThemeFile": "Xaml tema-fil",
"AvatarWindowTitle": "Administrer kontoer - Profilbilde",
"Amiibo": "Amiibo",
"Unknown": "Ukjent",
"Usage": "Forbruk",
"Writable": "Skrivbart",
"SelectDlcDialogTitle": "Velg DLC-filer",
"SelectUpdateDialogTitle": "Velg oppdateringsfiler",
"SelectModDialogTitle": "Velg modifikasjons mappe",
"UserProfileWindowTitle": "Bruker Profiler Behandler",
"CheatWindowTitle": "Juksing behandler",
"DlcWindowTitle": "Behandle nedlastbart innhold for {0} ({1})",
"ModWindowTitle": "Administrere Modifikasjoner for {0} ({1})",
"UpdateWindowTitle": "Tittel oppdatering behandler",
"CheatWindowHeading": "Juks tilgjengelig for {0} [{1}]",
"BuildId": "VersjonsId:",
"DlcWindowHeading": "{0} Nedlastbare innhold(er)",
"ModWindowHeading": "{0} Modifikasjoner(s)",
"UserProfilesEditProfile": "Rediger Valgte",
"Cancel": "Avbryt",
"Save": "Lagre",
"Discard": "Forkast",
"Paused": "Satt på pause",
"UserProfilesSetProfileImage": "Angi profilbilde",
"UserProfileEmptyNameError": "Navn er påkrevd",
"UserProfileNoImageError": "Profilbilde må være angitt",
"GameUpdateWindowHeading": "Administrer oppdateringer for {0} ({1})",
"SettingsTabHotkeysResScaleUpHotkey": "Øke oppløsning:",
"SettingsTabHotkeysResScaleDownHotkey": "Reduser oppløsning:",
"UserProfilesName": "Navn:",
"UserProfilesUserId": "Bruker ID:",
"SettingsTabGraphicsBackend": "Grafikk Backend",
"SettingsTabGraphicsBackendTooltip": "Velg grafikkbackend som skal brukes i emulatoren.\n\nVulkan er generelt bedre for alle moderne grafikkort, så lenge driverne er oppdatert. Vulkan har også en raskere sammenstilling av Shader (mindre hakkete) på alle GPU-leverandører.\n\nOpenGL kan oppnå bedre resultater for eldre Nvidia GPU-er, på eldre AMD GPU-er på Linux, eller på GPU-er med lavere VRAM, selv om skyggekompileringsutløser vil være større.\n\nSett til Vulkan hvis du er usikker. Sett til OpenGL hvis ikke GPU-en støtter Vulkan selv med de nyeste grafikkdriverne.",
"SettingsEnableTextureRecompression": "Aktiver teksturkomprimering",
"SettingsEnableTextureRecompressionTooltip": "Kompresser ASTC-teksturer for å redusere VRAM-bruk.\n\nSpill som bruker dette teksturformatet, inkluderer Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGrafikkkort med 4GiB VRAM eller mindre, vil sannsynligvis krasje på et tidspunkt når spillene kjører.\n\nAktiver bare hvis du går tom for VRAM på nevnte spill. La AV om du er usikker.",
"SettingsTabGraphicsPreferredGpu": "Foretrukket GPU",
"SettingsTabGraphicsPreferredGpuTooltip": "Velg grafikkkortet som skal brukes sammen med Vulkan grafikkbackenden\n\nPåvirker ikke GPU-er som OpenGL skal bruke.\n\nSett til GPU-merket som \"dGPU\" hvis usikker. Hvis det ikke det er en, la være uberørt.",
"SettingsAppRequiredRestartMessage": "Ryujinx Omstart nødvendig",
"SettingsGpuBackendRestartMessage": "Grafikk Backend eller GPU-innstillinger er endret. Dette krever en omstart for å aktiveres",
"SettingsGpuBackendRestartSubMessage": "Vil du starte på nytt nå?",
"RyujinxUpdaterMessage": "Ønsker du å oppdatere Ryujinx til den nyeste versjonen?",
"SettingsTabHotkeysVolumeUpHotkey": "Øk Volum:",
"SettingsTabHotkeysVolumeDownHotkey": "Reduser Volum:",
"SettingsEnableMacroHLE": "Aktiver Makro HLE",
"SettingsEnableMacroHLETooltip": "High-level emulering av GPU makrokode.\n\nForbedrer ytelse, men kan forårsake grafiske glitches i noen spill.\n\nForlat PÅ hvis usikker.",
"SettingsEnableColorSpacePassthrough": "Fargeromsgjennomgang",
"SettingsEnableColorSpacePassthroughTooltip": "Dirigerer Vulkan backenden til å gå gjennom farge informasjonen uten og spesifisere en fargeromsgjennomgang. For brukere med en bred spillvisning kan dette resultere i mer vibrerende farger og få riktig farge.",
"VolumeShort": "Vol",
"UserProfilesManageSaves": "Administrer lagring",
"DeleteUserSave": "Vil du slette bruker data for dette spillet?",
"IrreversibleActionNote": "Denne handlingen er ikke reverserbar.",
"SaveManagerHeading": "Administrer lagring for {0} ({1})",
"SaveManagerTitle": "Lagre behandler",
"Name": "Navn",
"Size": "Størrelse",
"Search": "Søk",
"UserProfilesRecoverLostAccounts": "Gjenopprett tapte kontoer",
"Recover": "Gjenopprett",
"UserProfilesRecoverHeading": "Lagring ble funnet for følgende kontoer",
"UserProfilesRecoverEmptyList": "Ingen profiler å gjenopprette",
"GraphicsAATooltip": "Aktiverer anti-aliasing til spill render.\n\nFXAA vil gjøre det meste av bildet, mens SMAA vil forsøke å finne berørte kanter og glatte dem ut.\n\nAnbefales ikke til bruk i forbindelse med FSR-skaleringsfilteret.\n\nDette valget kan endres mens et spill kjører ved å klikke \"Apply\" nedenfor; du kan bare flytte innstillingsvinduet til du finner det foretrukne utseendet til et spill.\n\nForlat på NONE hvis usikker.",
"GraphicsAALabel": "Kantutjevning:",
"GraphicsScalingFilterLabel": "Skaleringsfilter:",
"GraphicsScalingFilterTooltip": "Velg det skaleringsfilteret som skal brukes når du bruker oppløsningsskalaen.\n\nBilinear fungerer godt for 3D-spill og er et trygt standardalternativ.\n\nNærmeste anbefales for pixel kunst-spill.\n\nFSR 1.0 er bare et skarpere filter, ikke anbefalt for bruk med FXAA eller SMAA.\n\nOmrådeskalering anbefales når nedskalering er større enn utgangsvinduet. Den kan brukes til å oppnå en superprøvetaket anti-aliasingseffekt når en nedskalerer med mer enn 2x.\n\nDette valget kan endres mens et spill kjører ved å klikke \"Apply\" nedenfor; du kan bare flytte innstillingsvinduet til du finner det foretrukne utseendet til et spill.\n\nLa være på BILINEAR hvis usikker.",
"GraphicsScalingFilterBilinear": "Bilinear",
"GraphicsScalingFilterNearest": "Nærmeste",
"GraphicsScalingFilterFsr": "FSR",
"GraphicsScalingFilterArea": "Område",
"GraphicsScalingFilterLevelLabel": "Nivå",
"GraphicsScalingFilterLevelTooltip": "Definer FSR 1,0 skarpere nivå. Høyere er skarpere.",
"SmaaLow": "SMAA lav",
"SmaaMedium": "SMAA Middels",
"SmaaHigh": "SMAA høy",
"SmaaUltra": "SMAA Ultra",
"UserEditorTitle": "Rediger bruker",
"UserEditorTitleCreate": "Opprett bruker",
"SettingsTabNetworkInterface": "Nettverksgrensesnitt",
"NetworkInterfaceTooltip": "Nettverksgrensesnittets grensesnitt brukt for LAN/LDN funksjoner.\n\ni konjuksjon med en VPN eller XLink Kai og ett spill med LAN støtte, kan bli brukt til og spoofe ett \"samme-nettverk\" tilkobling over nettet.\n\nLa være på DEFAULT om usikker.",
"NetworkInterfaceDefault": "Standard",
"PackagingShaders": "Pakker Shaders",
"AboutChangelogButton": "Vis endringslogg på GitHub",
"AboutChangelogButtonTooltipMessage": "Klikk for å åpne endringsloggen for denne versjonen i din nettleser.",
"SettingsTabNetworkMultiplayer": "Flerspiller",
"MultiplayerMode": "Modus:",
"MultiplayerModeTooltip": "Endre LDN flerspillermodus.\n\nLdnMitm vil endre lokal trådløst/lokal spillfunksjonalitet i spill som skal fungere som om den var LAN, noe som tillater lokal, samme nettverk forbindelser med andre Ryujinx instanser og hacket Nintendo Switch konsoller som har installert ldn_mitm-modulen.\n\nFlerspiller krever at alle spillerne er på samme versjon (dvs. Super Smash Bros. Ultimat v13.0.1 kan ikke koble til v13.0.0).\n\nForlat DEAKTIVERT hvis usikker.",
"MultiplayerModeDisabled": "Deaktivert",
"MultiplayerModeLdnMitm": "ldn_mitm"
}