Compare commits

...

6 Commits

Author SHA1 Message Date
Mary
fe9c49949a vulkan: Enforce Vulkan 1.2+ at instance API level and 1.1+ at device level (#4408)
* vulkan: Enforce Vulkan 1.2+ at instance API level and 1.1+ at device level

This ensure we don't end up trying to initialize with anything currently incompatible.

* Address riperiperi's comment
2023-02-13 22:04:55 +00:00
Mary
052b23c83c vulkan: Do not call vkCmdSetViewport when viewportCount is 0 (#4406)
This fix validation error "VUID-vkCmdSetViewport-viewportCount-arraylength".
2023-02-13 20:32:20 +00:00
riperiperi
e4f68592c3 Fix partial updates for textures. (#4401)
I was forcing some types of texture to partially update when investigating performance with games that stream in data, and noticed that partially loading texture data was really broken on both backends.

Fixes Vulkan texture set by getting the correct expected size for the texture. Fixes partial upload on both backends for both Texture 2D Array and Cubemap using the wrong offset and uploading to the first layer/level for a handle. 3D might also be affected.

This might fix textures randomly having incorrect data in games that render to it - jumbled in the case of OpenGL, and outdated/black in the case of Vulkan. This case typically happens in UE4 games.
2023-02-12 10:30:26 +01:00
Logan Stromberg
1dcd44b94f Treat NpadIdType < 0 as invalid. Filter invalid SupportedPlayers inside IHidServer.SetSupportedNpadIdType(). (#4377)
Co-authored-by: Logan Stromberg <lostromb@microsoft.com>
2023-02-10 12:37:20 -03:00
gdkchan
61b1ce252f Allow partially mapped textures with unmapped start (#4394) 2023-02-10 11:47:59 -03:00
gdkchan
5f38086f94 Fix SPIR-V when all inputs/outputs are indexed (#4389) 2023-02-09 04:48:25 +01:00
10 changed files with 161 additions and 83 deletions

View File

@@ -474,6 +474,13 @@ namespace Ryujinx.Graphics.Gpu.Image
{
address = memoryManager.Translate(info.GpuAddress);
// If the start address is unmapped, let's try to find a page of memory that is mapped.
if (address == MemoryManager.PteUnmapped)
{
address = memoryManager.TranslateFirstMapped(info.GpuAddress, (ulong)info.CalculateSizeInfo(layerSize).TotalSize);
}
// If address is still invalid, the texture is fully unmapped, so it has no data, just return null.
if (address == MemoryManager.PteUnmapped)
{
return null;

View File

@@ -336,24 +336,23 @@ namespace Ryujinx.Graphics.Gpu.Image
if (_loadNeeded[baseHandle + i])
{
var info = GetHandleInformation(baseHandle + i);
int offsetIndex = info.Index;
// Only one of these will be greater than 1, as partial sync is only called when there are sub-image views.
for (int layer = 0; layer < info.Layers; layer++)
{
for (int level = 0; level < info.Levels; level++)
{
int offsetIndex = GetOffsetIndex(info.BaseLayer + layer, info.BaseLevel + level);
int offset = _allOffsets[offsetIndex];
int endOffset = Math.Min(offset + _sliceSizes[info.BaseLevel + level], (int)Storage.Size);
int size = endOffset - offset;
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size));
SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel, true);
SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true);
Storage.SetData(result, info.BaseLayer, info.BaseLevel);
offsetIndex++;
Storage.SetData(result, info.BaseLayer + layer, info.BaseLevel + level);
}
}
}

View File

@@ -583,6 +583,38 @@ namespace Ryujinx.Graphics.Gpu.Memory
return UnpackPaFromPte(pte) + (va & PageMask);
}
/// <summary>
/// Translates a GPU virtual address to a CPU virtual address on the first mapped page of memory
/// on the specified region.
/// If no page is mapped on the specified region, <see cref="PteUnmapped"/> is returned.
/// </summary>
/// <param name="va">GPU virtual address to be translated</param>
/// <param name="size">Size of the range to be translated</param>
/// <returns>CPU virtual address, or <see cref="PteUnmapped"/> if unmapped</returns>
public ulong TranslateFirstMapped(ulong va, ulong size)
{
if (!ValidateAddress(va))
{
return PteUnmapped;
}
ulong endVa = va + size;
ulong pte = GetPte(va);
for (; va < endVa && pte == PteUnmapped; va += PageSize - (va & PageMask))
{
pte = GetPte(va);
}
if (pte == PteUnmapped)
{
return PteUnmapped;
}
return UnpackPaFromPte(pte) + (va & PageMask);
}
/// <summary>
/// Gets the kind of a given memory page.
/// This might indicate the type of resource that can be allocated on the page, and also texture tiling.

View File

@@ -397,20 +397,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch)
{
bool iaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing);
var inputs = perPatch ? info.InputsPerPatch : info.Inputs;
foreach (int attr in inputs)
{
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false, perPatch))
{
continue;
}
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
if (iaIndexing && isUserAttr && !perPatch)
{
if (context.InputsArray == null)
if (iaIndexing && !perPatch)
{
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
@@ -433,9 +421,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.AddGlobalVariable(spvVar);
context.InputsArray = spvVar;
}
}
else
var inputs = perPatch ? info.InputsPerPatch : info.Inputs;
foreach (int attr in inputs)
{
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false, perPatch))
{
continue;
}
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
if (iaIndexing && isUserAttr && !perPatch)
{
continue;
}
PixelImap iq = PixelImap.Unused;
if (context.Config.Stage == ShaderStage.Fragment)
@@ -459,25 +461,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq);
}
}
}
private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch)
{
bool oaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing);
var outputs = perPatch ? info.OutputsPerPatch : info.Outputs;
foreach (int attr in outputs)
{
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true, perPatch))
{
continue;
}
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
if (oaIndexing && isUserAttr && !perPatch)
{
if (context.OutputsArray == null)
if (oaIndexing && !perPatch)
{
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
@@ -495,11 +484,24 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.AddGlobalVariable(spvVar);
context.OutputsArray = spvVar;
}
}
else
var outputs = perPatch ? info.OutputsPerPatch : info.Outputs;
foreach (int attr in outputs)
{
DeclareOutputAttribute(context, attr, perPatch);
if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true, perPatch))
{
continue;
}
bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
if (oaIndexing && isUserAttr && !perPatch)
{
continue;
}
DeclareOutputAttribute(context, attr, perPatch);
}
if (context.Config.Stage == ShaderStage.Vertex)

View File

@@ -650,9 +650,7 @@ namespace Ryujinx.Graphics.Vulkan
_newState.DepthWriteEnable = oldDepthWriteEnable;
_newState.Topology = oldTopology;
DynamicState.Viewports = oldViewports;
DynamicState.ViewportsCount = (int)oldViewportsCount;
DynamicState.SetViewportsDirty();
DynamicState.SetViewports(ref oldViewports, oldViewportsCount);
_newState.ViewportsCount = oldViewportsCount;
SignalStateChange();
@@ -1183,6 +1181,8 @@ namespace Ryujinx.Graphics.Vulkan
return Math.Clamp(value, 0f, 1f);
}
DynamicState.ViewportsCount = (uint)count;
for (int i = 0; i < count; i++)
{
var viewport = viewports[i];
@@ -1196,8 +1196,6 @@ namespace Ryujinx.Graphics.Vulkan
Clamp(viewport.DepthFar)));
}
DynamicState.ViewportsCount = count;
float disableTransformF = disableTransform ? 1.0f : 0.0f;
if (SupportBufferUpdater.Data.ViewportInverse.W != disableTransformF || disableTransform)
{

View File

@@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Vulkan
private Array4<float> _blendConstants;
public int ViewportsCount;
public uint ViewportsCount;
public Array16<Viewport> Viewports;
private enum DirtyFlags
@@ -88,10 +88,16 @@ namespace Ryujinx.Graphics.Vulkan
_dirty |= DirtyFlags.Viewport;
}
public void SetViewportsDirty()
public void SetViewports(ref Array16<Viewport> viewports, uint viewportsCount)
{
Viewports = viewports;
ViewportsCount = viewportsCount;
if (ViewportsCount != 0)
{
_dirty |= DirtyFlags.Viewport;
}
}
public void ForceAllDirty()
{
@@ -155,7 +161,10 @@ namespace Ryujinx.Graphics.Vulkan
private void RecordViewport(Vk api, CommandBuffer commandBuffer)
{
api.CmdSetViewport(commandBuffer, 0, (uint)ViewportsCount, Viewports.AsSpan());
if (ViewportsCount != 0)
{
api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan());
}
}
}
}

View File

@@ -712,7 +712,7 @@ namespace Ryujinx.Graphics.Vulkan
for (int level = 0; level < levels; level++)
{
int mipSize = GetBufferDataLength(Info.GetMipSize(dstLevel + level));
int mipSize = GetBufferDataLength(Info.GetMipSize2D(dstLevel + level) * dstLayers);
int endOffset = offset + mipSize;

View File

@@ -14,6 +14,9 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe static class VulkanInitialization
{
private const uint InvalidIndex = uint.MaxValue;
private static uint MinimalVulkanVersion = Vk.Version11.Value;
private static uint MinimalInstanceVulkanVersion = Vk.Version12.Value;
private static uint MaximumVulkanVersion = Vk.Version12.Value;
private const string AppName = "Ryujinx.Graphics.Vulkan";
private const int QueuesCount = 2;
@@ -99,7 +102,7 @@ namespace Ryujinx.Graphics.Vulkan
ApplicationVersion = 1,
PEngineName = (byte*)appName,
EngineVersion = 1,
ApiVersion = Vk.Version12.Value
ApiVersion = MaximumVulkanVersion
};
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
@@ -224,7 +227,7 @@ namespace Ryujinx.Graphics.Vulkan
ApplicationVersion = 1,
PEngineName = (byte*)appName,
EngineVersion = 1,
ApiVersion = Vk.Version12.Value
ApiVersion = MaximumVulkanVersion
};
var instanceCreateInfo = new InstanceCreateInfo
@@ -239,6 +242,27 @@ namespace Ryujinx.Graphics.Vulkan
api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError();
// We ensure that vkEnumerateInstanceVersion is present (added in 1.1).
// If the instance doesn't support it, no device is going to be 1.1 compatible.
if (api.GetInstanceProcAddr(instance, "vkEnumerateInstanceVersion") == IntPtr.Zero)
{
api.DestroyInstance(instance, null);
return Array.Empty<DeviceInfo>();
}
// We currently assume that the instance is compatible with Vulkan 1.2
// TODO: Remove this once we relax our initialization codepaths.
uint instanceApiVerison = 0;
api.EnumerateInstanceVersion(ref instanceApiVerison).ThrowOnError();
if (instanceApiVerison < MinimalInstanceVulkanVersion)
{
api.DestroyInstance(instance, null);
return Array.Empty<DeviceInfo>();
}
Marshal.FreeHGlobal(appName);
uint physicalDeviceCount;
@@ -259,6 +283,11 @@ namespace Ryujinx.Graphics.Vulkan
var physicalDevice = physicalDevices[i];
api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
if (properties.ApiVersion < MinimalVulkanVersion)
{
continue;
}
devices[i] = new DeviceInfo(
StringFromIdPair(properties.VendorID, properties.DeviceID),
VendorUtils.GetNameFromId(properties.VendorID),

View File

@@ -38,7 +38,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer
public static bool IsValidNpadIdType(NpadIdType npadIdType)
{
return npadIdType <= NpadIdType.Player8 || npadIdType == NpadIdType.Handheld || npadIdType == NpadIdType.Unknown;
return (npadIdType >= NpadIdType.Player1 && npadIdType <= NpadIdType.Player8) ||
npadIdType == NpadIdType.Handheld ||
npadIdType == NpadIdType.Unknown;
}
}
}

View File

@@ -722,7 +722,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
for (int i = 0; i < supportedPlayerIds.Length; ++i)
{
if (supportedPlayerIds[i] >= 0)
if (HidUtils.IsValidNpadIdType(supportedPlayerIds[i]))
{
context.Device.Hid.Npads.SetSupportedPlayer(HidUtils.GetIndexFromNpadIdType(supportedPlayerIds[i]));
}
@@ -1101,7 +1101,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
if (deviceType < NpadStyleIndex.System || deviceType >= NpadStyleIndex.FullKey)
{
if (npadIdType >= (NpadIdType.Player8 + 1) && npadIdType != NpadIdType.Handheld && npadIdType != NpadIdType.Unknown)
if (!HidUtils.IsValidNpadIdType(npadIdType))
{
return ResultCode.InvalidNpadIdType;
}