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); 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) if (address == MemoryManager.PteUnmapped)
{ {
return null; return null;

View File

@@ -336,24 +336,23 @@ namespace Ryujinx.Graphics.Gpu.Image
if (_loadNeeded[baseHandle + i]) if (_loadNeeded[baseHandle + i])
{ {
var info = GetHandleInformation(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. // 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 layer = 0; layer < info.Layers; layer++)
{ {
for (int level = 0; level < info.Levels; level++) for (int level = 0; level < info.Levels; level++)
{ {
int offsetIndex = GetOffsetIndex(info.BaseLayer + layer, info.BaseLevel + level);
int offset = _allOffsets[offsetIndex]; int offset = _allOffsets[offsetIndex];
int endOffset = Math.Min(offset + _sliceSizes[info.BaseLevel + level], (int)Storage.Size); int endOffset = Math.Min(offset + _sliceSizes[info.BaseLevel + level], (int)Storage.Size);
int size = endOffset - offset; int size = endOffset - offset;
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size)); 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); Storage.SetData(result, info.BaseLayer + layer, info.BaseLevel + level);
offsetIndex++;
} }
} }
} }

View File

@@ -583,6 +583,38 @@ namespace Ryujinx.Graphics.Gpu.Memory
return UnpackPaFromPte(pte) + (va & PageMask); 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> /// <summary>
/// Gets the kind of a given memory page. /// 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. /// This might indicate the type of resource that can be allocated on the page, and also texture tiling.

View File

@@ -397,6 +397,31 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch) private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch)
{ {
bool iaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing); bool iaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.IaIndexing);
if (iaIndexing && !perPatch)
{
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
if (context.Config.Stage == ShaderStage.Geometry)
{
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)context.InputVertices));
}
var spvType = context.TypePointer(StorageClass.Input, attrType);
var spvVar = context.Variable(spvType, StorageClass.Input);
if (context.Config.PassthroughAttributes != 0 && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
{
context.Decorate(spvVar, Decoration.PassthroughNV);
}
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0);
context.AddGlobalVariable(spvVar);
context.InputsArray = spvVar;
}
var inputs = perPatch ? info.InputsPerPatch : info.Inputs; var inputs = perPatch ? info.InputsPerPatch : info.Inputs;
foreach (int attr in inputs) foreach (int attr in inputs)
@@ -410,60 +435,56 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (iaIndexing && isUserAttr && !perPatch) if (iaIndexing && isUserAttr && !perPatch)
{ {
if (context.InputsArray == null) continue;
{
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
if (context.Config.Stage == ShaderStage.Geometry)
{
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)context.InputVertices));
}
var spvType = context.TypePointer(StorageClass.Input, attrType);
var spvVar = context.Variable(spvType, StorageClass.Input);
if (context.Config.PassthroughAttributes != 0 && context.Config.GpuAccessor.QueryHostSupportsGeometryShaderPassthrough())
{
context.Decorate(spvVar, Decoration.PassthroughNV);
}
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0);
context.AddGlobalVariable(spvVar);
context.InputsArray = spvVar;
}
} }
else
PixelImap iq = PixelImap.Unused;
if (context.Config.Stage == ShaderStage.Fragment)
{ {
PixelImap iq = PixelImap.Unused; if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd)
if (context.Config.Stage == ShaderStage.Fragment)
{ {
if (attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd) iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType();
{ }
iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType(); else
} {
else AttributeInfo attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr: false);
{ AggregateType elemType = attrInfo.Type & AggregateType.ElementTypeMask;
AttributeInfo attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr: false);
AggregateType elemType = attrInfo.Type & AggregateType.ElementTypeMask;
if (attrInfo.IsBuiltin && (elemType == AggregateType.S32 || elemType == AggregateType.U32)) if (attrInfo.IsBuiltin && (elemType == AggregateType.S32 || elemType == AggregateType.U32))
{ {
iq = PixelImap.Constant; iq = PixelImap.Constant;
}
} }
} }
DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq);
} }
DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq);
} }
} }
private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch) private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info, bool perPatch)
{ {
bool oaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing); bool oaIndexing = context.Config.UsedFeatures.HasFlag(FeatureFlags.OaIndexing);
if (oaIndexing && !perPatch)
{
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
if (context.Config.Stage == ShaderStage.TessellationControl)
{
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
}
var spvType = context.TypePointer(StorageClass.Output, attrType);
var spvVar = context.Variable(spvType, StorageClass.Output);
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0);
context.AddGlobalVariable(spvVar);
context.OutputsArray = spvVar;
}
var outputs = perPatch ? info.OutputsPerPatch : info.Outputs; var outputs = perPatch ? info.OutputsPerPatch : info.Outputs;
foreach (int attr in outputs) foreach (int attr in outputs)
@@ -477,29 +498,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (oaIndexing && isUserAttr && !perPatch) if (oaIndexing && isUserAttr && !perPatch)
{ {
if (context.OutputsArray == null) continue;
{
var attrType = context.TypeVector(context.TypeFP32(), (LiteralInteger)4);
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), (LiteralInteger)MaxAttributes));
if (context.Config.Stage == ShaderStage.TessellationControl)
{
attrType = context.TypeArray(attrType, context.Constant(context.TypeU32(), context.Config.ThreadsPerInputPrimitive));
}
var spvType = context.TypePointer(StorageClass.Output, attrType);
var spvVar = context.Variable(spvType, StorageClass.Output);
context.Decorate(spvVar, Decoration.Location, (LiteralInteger)0);
context.AddGlobalVariable(spvVar);
context.OutputsArray = spvVar;
}
}
else
{
DeclareOutputAttribute(context, attr, perPatch);
} }
DeclareOutputAttribute(context, attr, perPatch);
} }
if (context.Config.Stage == ShaderStage.Vertex) if (context.Config.Stage == ShaderStage.Vertex)

View File

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

View File

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

View File

@@ -14,6 +14,9 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe static class VulkanInitialization public unsafe static class VulkanInitialization
{ {
private const uint InvalidIndex = uint.MaxValue; 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 string AppName = "Ryujinx.Graphics.Vulkan";
private const int QueuesCount = 2; private const int QueuesCount = 2;
@@ -99,7 +102,7 @@ namespace Ryujinx.Graphics.Vulkan
ApplicationVersion = 1, ApplicationVersion = 1,
PEngineName = (byte*)appName, PEngineName = (byte*)appName,
EngineVersion = 1, EngineVersion = 1,
ApiVersion = Vk.Version12.Value ApiVersion = MaximumVulkanVersion
}; };
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
@@ -224,7 +227,7 @@ namespace Ryujinx.Graphics.Vulkan
ApplicationVersion = 1, ApplicationVersion = 1,
PEngineName = (byte*)appName, PEngineName = (byte*)appName,
EngineVersion = 1, EngineVersion = 1,
ApiVersion = Vk.Version12.Value ApiVersion = MaximumVulkanVersion
}; };
var instanceCreateInfo = new InstanceCreateInfo var instanceCreateInfo = new InstanceCreateInfo
@@ -239,6 +242,27 @@ namespace Ryujinx.Graphics.Vulkan
api.CreateInstance(in instanceCreateInfo, null, out var instance).ThrowOnError(); 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); Marshal.FreeHGlobal(appName);
uint physicalDeviceCount; uint physicalDeviceCount;
@@ -259,6 +283,11 @@ namespace Ryujinx.Graphics.Vulkan
var physicalDevice = physicalDevices[i]; var physicalDevice = physicalDevices[i];
api.GetPhysicalDeviceProperties(physicalDevice, out var properties); api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
if (properties.ApiVersion < MinimalVulkanVersion)
{
continue;
}
devices[i] = new DeviceInfo( devices[i] = new DeviceInfo(
StringFromIdPair(properties.VendorID, properties.DeviceID), StringFromIdPair(properties.VendorID, properties.DeviceID),
VendorUtils.GetNameFromId(properties.VendorID), VendorUtils.GetNameFromId(properties.VendorID),

View File

@@ -38,7 +38,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer
public static bool IsValidNpadIdType(NpadIdType npadIdType) 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) 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])); 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 (deviceType < NpadStyleIndex.System || deviceType >= NpadStyleIndex.FullKey)
{ {
if (npadIdType >= (NpadIdType.Player8 + 1) && npadIdType != NpadIdType.Handheld && npadIdType != NpadIdType.Unknown) if (!HidUtils.IsValidNpadIdType(npadIdType))
{ {
return ResultCode.InvalidNpadIdType; return ResultCode.InvalidNpadIdType;
} }