Compare commits

...

4 Commits

Author SHA1 Message Date
Mary
17078ad929 vulkan: Respect VK_KHR_portability_subset vertex stride alignment (#4419)
* vulkan: Respect VK_KHR_portability_subset vertex stride alignment

We were hardcoding alignment to 4, but by specs it can be any values that
is a power of 2.

This also enable VK_KHR_portability_subset if present as per specs
requirements.

* address gdkchan's comment

* Make NeedsVertexBufferAlignment internal
2023-02-15 08:41:48 +00:00
Mary
32450d45de vulkan: Clean up MemoryAllocator (#4418)
This started as an attempt to remove vkGetPhysicalDeviceMemoryProperties
in FindSuitableMemoryTypeIndex (As this could have some overhead and
shouldn't change at runtime) and turned in a little bigger cleanup.
2023-02-15 07:50:26 +01:00
Ac_K
ed7a0474c6 Infra: Issues template cleanup (#4421)
* Infra: Issues template cleanup

* applied
2023-02-14 15:58:57 +01:00
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
12 changed files with 91 additions and 69 deletions

View File

@@ -1,26 +1,27 @@
name: Bug Report name: Bug Report
description: File a bug report description: File a bug report
title: "[Bug] <title>" title: "[Bug]"
labels: bug labels: bug
body: body:
- type: textarea - type: textarea
id: issue id: issue
attributes: attributes:
label: Description of Issue label: Description of the issue
description: What's the issue you encountered? description: What's the issue you encountered?
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: repro id: repro
attributes: attributes:
label: Reproduction Steps label: Reproduction steps
description: How can the issue be reproduced? description: How can the issue be reproduced?
placeholder: Describe each step as precisely as possible
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: log id: log
attributes: attributes:
label: Log File label: Log file
description: A log file will help our developers to better diagnose and fix the issue. description: A log file will help our developers to better diagnose and fix the issue.
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area
validations: validations:
@@ -29,55 +30,44 @@ body:
id: os id: os
attributes: attributes:
label: OS label: OS
placeholder: "Example: Windows 10" placeholder: "e.g. Windows 10"
validations: validations:
required: true required: true
- type: input - type: input
id: ryujinx-version id: ryujinx-version
attributes: attributes:
label: Ryujinx version label: Ryujinx version
placeholder: | placeholder: "e.g. 1.0.470"
- *(e.g. 1.0.470)*
validations: validations:
required: true required: true
- type: input - type: input
id: game-version id: game-version
attributes: attributes:
label: Game version label: Game version
placeholder: | placeholder: "e.g. 1.1.1"
- *(e.g. 1.1.1)*
validations: validations:
required: false required: false
- type: input - type: input
id: cpu id: cpu
attributes: attributes:
label: CPU label: CPU
placeholder: | placeholder: "e.g. i7-6700"
- *(e.g. i7-6700)*
validations: validations:
required: false required: false
- type: input - type: input
id: gpu id: gpu
attributes: attributes:
label: GPU label: GPU
placeholder: | placeholder: "e.g. NVIDIA RTX 2070"
- *(e.g. NVIDIA RTX 2070)*
validations: validations:
required: false required: false
- type: input - type: input
id: ram id: ram
attributes: attributes:
label: RAM label: RAM
placeholder: | placeholder: "e.g. 16GB"
- *(e.g. 16GB)*
validations: validations:
required: false required: false
- type: checkboxes
attributes:
label: Applied Mods?
options:
- label: "Yes"
required: false
- type: textarea - type: textarea
id: mods id: mods
attributes: attributes:

View File

@@ -1,6 +1,6 @@
name: Feature Request name: Feature Request
description: Suggest a new feature for Ryujinx. description: Suggest a new feature for Ryujinx.
title: "[Feature Request] <title>" title: "[Feature Request]"
body: body:
- type: textarea - type: textarea
id: overview id: overview
@@ -12,14 +12,14 @@ body:
- type: textarea - type: textarea
id: details id: details
attributes: attributes:
label: Smaller Details label: Smaller details
description: These may include specific methods of implementation etc. description: These may include specific methods of implementation etc.
validations: validations:
required: true required: true
- type: textarea - type: textarea
id: request id: request
attributes: attributes:
label: Nature of Request label: Nature of request
validations: validations:
required: true required: true
- type: textarea - type: textarea

View File

@@ -1,6 +1,6 @@
name: Missing CPU Instruction name: Missing CPU Instruction
description: CPU Instruction is missing in Ryujinx. description: CPU Instruction is missing in Ryujinx.
title: "[CPU] <title>" title: "[CPU]"
labels: [cpu, not-implemented] labels: [cpu, not-implemented]
body: body:
- type: textarea - type: textarea

View File

@@ -5,7 +5,7 @@ body:
- type: textarea - type: textarea
id: instruction id: instruction
attributes: attributes:
label: Service Call label: Service call
description: What service call is missing? description: What service call is missing?
validations: validations:
required: true required: true

View File

@@ -39,7 +39,6 @@ namespace Ryujinx.Graphics.Vulkan
BufferUsageFlags.VertexBufferBit | BufferUsageFlags.VertexBufferBit |
BufferUsageFlags.TransformFeedbackBufferBitExt; BufferUsageFlags.TransformFeedbackBufferBitExt;
private readonly PhysicalDevice _physicalDevice;
private readonly Device _device; private readonly Device _device;
private readonly IdList<BufferHolder> _buffers; private readonly IdList<BufferHolder> _buffers;
@@ -48,9 +47,8 @@ namespace Ryujinx.Graphics.Vulkan
public StagingBuffer StagingBuffer { get; } public StagingBuffer StagingBuffer { get; }
public BufferManager(VulkanRenderer gd, PhysicalDevice physicalDevice, Device device) public BufferManager(VulkanRenderer gd, Device device)
{ {
_physicalDevice = physicalDevice;
_device = device; _device = device;
_buffers = new IdList<BufferHolder>(); _buffers = new IdList<BufferHolder>();
StagingBuffer = new StagingBuffer(gd, this); StagingBuffer = new StagingBuffer(gd, this);
@@ -114,7 +112,7 @@ namespace Ryujinx.Graphics.Vulkan
allocateFlagsAlt = DefaultBufferMemoryAltFlags; allocateFlagsAlt = DefaultBufferMemoryAltFlags;
} }
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, allocateFlags, allocateFlagsAlt); var allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, allocateFlags, allocateFlagsAlt);
if (allocation.Memory.Handle == 0UL) if (allocation.Memory.Handle == 0UL)
{ {

View File

@@ -8,11 +8,10 @@ namespace Ryujinx.Graphics.Vulkan
{ {
None = 0, None = 0,
VertexBufferAlignment4B = 1, NoTriangleFans = 1,
NoTriangleFans = 1 << 1, NoPointMode = 1 << 1,
NoPointMode = 1 << 2, No3DImageView = 1 << 2,
No3DImageView = 1 << 3, NoLodBias = 1 << 3
NoLodBias = 1 << 4
} }
readonly struct HardwareCapabilities readonly struct HardwareCapabilities
@@ -40,6 +39,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly ShaderStageFlags RequiredSubgroupSizeStages; public readonly ShaderStageFlags RequiredSubgroupSizeStages;
public readonly SampleCountFlags SupportedSampleCounts; public readonly SampleCountFlags SupportedSampleCounts;
public readonly PortabilitySubsetFlags PortabilitySubset; public readonly PortabilitySubsetFlags PortabilitySubset;
public readonly uint VertexBufferAlignment;
public HardwareCapabilities( public HardwareCapabilities(
bool supportsIndexTypeUint8, bool supportsIndexTypeUint8,
@@ -64,7 +64,8 @@ namespace Ryujinx.Graphics.Vulkan
uint maxSubgroupSize, uint maxSubgroupSize,
ShaderStageFlags requiredSubgroupSizeStages, ShaderStageFlags requiredSubgroupSizeStages,
SampleCountFlags supportedSampleCounts, SampleCountFlags supportedSampleCounts,
PortabilitySubsetFlags portabilitySubset) PortabilitySubsetFlags portabilitySubset,
uint vertexBufferAlignment)
{ {
SupportsIndexTypeUint8 = supportsIndexTypeUint8; SupportsIndexTypeUint8 = supportsIndexTypeUint8;
SupportsCustomBorderColor = supportsCustomBorderColor; SupportsCustomBorderColor = supportsCustomBorderColor;
@@ -89,6 +90,7 @@ namespace Ryujinx.Graphics.Vulkan
RequiredSubgroupSizeStages = requiredSubgroupSizeStages; RequiredSubgroupSizeStages = requiredSubgroupSizeStages;
SupportedSampleCounts = supportedSampleCounts; SupportedSampleCounts = supportedSampleCounts;
PortabilitySubset = portabilitySubset; PortabilitySubset = portabilitySubset;
VertexBufferAlignment = vertexBufferAlignment;
} }
} }
} }

View File

@@ -9,34 +9,36 @@ namespace Ryujinx.Graphics.Vulkan
private ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024; private ulong MaxDeviceMemoryUsageEstimate = 16UL * 1024 * 1024 * 1024;
private readonly Vk _api; private readonly Vk _api;
private readonly PhysicalDevice _physicalDevice;
private readonly Device _device; private readonly Device _device;
private readonly List<MemoryAllocatorBlockList> _blockLists; private readonly List<MemoryAllocatorBlockList> _blockLists;
private readonly int _blockAlignment;
private readonly PhysicalDeviceMemoryProperties _physicalDeviceMemoryProperties;
private int _blockAlignment; public MemoryAllocator(Vk api, PhysicalDevice physicalDevice, Device device, uint maxMemoryAllocationCount)
public MemoryAllocator(Vk api, Device device, uint maxMemoryAllocationCount)
{ {
_api = api; _api = api;
_physicalDevice = physicalDevice;
_device = device; _device = device;
_blockLists = new List<MemoryAllocatorBlockList>(); _blockLists = new List<MemoryAllocatorBlockList>();
_blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)maxMemoryAllocationCount); _blockAlignment = (int)Math.Min(int.MaxValue, MaxDeviceMemoryUsageEstimate / (ulong)maxMemoryAllocationCount);
_api.GetPhysicalDeviceMemoryProperties(_physicalDevice, out _physicalDeviceMemoryProperties);
} }
public MemoryAllocation AllocateDeviceMemory( public MemoryAllocation AllocateDeviceMemory(
PhysicalDevice physicalDevice,
MemoryRequirements requirements, MemoryRequirements requirements,
MemoryPropertyFlags flags = 0) MemoryPropertyFlags flags = 0)
{ {
return AllocateDeviceMemory(physicalDevice, requirements, flags, flags); return AllocateDeviceMemory(requirements, flags, flags);
} }
public MemoryAllocation AllocateDeviceMemory( public MemoryAllocation AllocateDeviceMemory(
PhysicalDevice physicalDevice,
MemoryRequirements requirements, MemoryRequirements requirements,
MemoryPropertyFlags flags, MemoryPropertyFlags flags,
MemoryPropertyFlags alternativeFlags) MemoryPropertyFlags alternativeFlags)
{ {
int memoryTypeIndex = FindSuitableMemoryTypeIndex(_api, physicalDevice, requirements.MemoryTypeBits, flags, alternativeFlags); int memoryTypeIndex = FindSuitableMemoryTypeIndex(requirements.MemoryTypeBits, flags, alternativeFlags);
if (memoryTypeIndex < 0) if (memoryTypeIndex < 0)
{ {
return default; return default;
@@ -65,20 +67,16 @@ namespace Ryujinx.Graphics.Vulkan
return newBl.Allocate(size, alignment, map); return newBl.Allocate(size, alignment, map);
} }
private static int FindSuitableMemoryTypeIndex( private int FindSuitableMemoryTypeIndex(
Vk api,
PhysicalDevice physicalDevice,
uint memoryTypeBits, uint memoryTypeBits,
MemoryPropertyFlags flags, MemoryPropertyFlags flags,
MemoryPropertyFlags alternativeFlags) MemoryPropertyFlags alternativeFlags)
{ {
int bestCandidateIndex = -1; int bestCandidateIndex = -1;
api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties); for (int i = 0; i < _physicalDeviceMemoryProperties.MemoryTypeCount; i++)
for (int i = 0; i < properties.MemoryTypeCount; i++)
{ {
var type = properties.MemoryTypes[i]; var type = _physicalDeviceMemoryProperties.MemoryTypes[i];
if ((memoryTypeBits & (1 << i)) != 0) if ((memoryTypeBits & (1 << i)) != 0)
{ {

View File

@@ -1,4 +1,5 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
@@ -1136,7 +1137,7 @@ namespace Ryujinx.Graphics.Vulkan
buffer.Dispose(); buffer.Dispose();
if (!Gd.Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.VertexBufferAlignment4B) && if (Gd.Capabilities.VertexBufferAlignment < 2 &&
(vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0) (vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0)
{ {
buffer = new VertexBufferState( buffer = new VertexBufferState(

View File

@@ -1,4 +1,5 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Common;
using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
@@ -253,7 +254,7 @@ namespace Ryujinx.Graphics.Vulkan
if (gd.NeedsVertexBufferAlignment(vbScalarSizes[i], out int alignment)) if (gd.NeedsVertexBufferAlignment(vbScalarSizes[i], out int alignment))
{ {
alignedStride = (vertexBuffer.Stride + (alignment - 1)) & -alignment; alignedStride = BitUtils.AlignUp(vertexBuffer.Stride, alignment);
} }
// TODO: Support divisor > 1 // TODO: Support divisor > 1

View File

@@ -55,7 +55,6 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe TextureStorage( public unsafe TextureStorage(
VulkanRenderer gd, VulkanRenderer gd,
PhysicalDevice physicalDevice,
Device device, Device device,
TextureCreateInfo info, TextureCreateInfo info,
float scaleFactor, float scaleFactor,
@@ -118,7 +117,7 @@ namespace Ryujinx.Graphics.Vulkan
if (foreignAllocation == null) if (foreignAllocation == null)
{ {
gd.Api.GetImageMemoryRequirements(device, _image, out var requirements); gd.Api.GetImageMemoryRequirements(device, _image, out var requirements);
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(physicalDevice, requirements, DefaultImageMemoryFlags); var allocation = gd.MemoryAllocator.AllocateDeviceMemory(requirements, DefaultImageMemoryFlags);
if (allocation.Memory.Handle == 0UL) if (allocation.Memory.Handle == 0UL)
{ {
@@ -173,7 +172,7 @@ namespace Ryujinx.Graphics.Vulkan
var info = NewCreateInfoWith(ref _info, format, _info.BytesPerPixel); var info = NewCreateInfoWith(ref _info, format, _info.BytesPerPixel);
storage = new TextureStorage(_gd, default, _device, info, ScaleFactor, _allocationAuto); storage = new TextureStorage(_gd, _device, info, ScaleFactor, _allocationAuto);
_aliasedStorages.Add(format, storage); _aliasedStorages.Add(format, storage);
} }

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;
@@ -33,7 +36,8 @@ namespace Ryujinx.Graphics.Vulkan
"VK_KHR_shader_float16_int8", "VK_KHR_shader_float16_int8",
"VK_EXT_shader_subgroup_ballot", "VK_EXT_shader_subgroup_ballot",
"VK_EXT_subgroup_size_control", "VK_EXT_subgroup_size_control",
"VK_NV_geometry_shader_passthrough" "VK_NV_geometry_shader_passthrough",
"VK_KHR_portability_subset", // By spec, we should enable this if present.
}; };
public static string[] RequiredExtensions { get; } = new string[] public static string[] RequiredExtensions { get; } = new string[]
@@ -99,7 +103,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 +228,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 +243,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 +284,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

@@ -234,10 +234,12 @@ namespace Ryujinx.Graphics.Vulkan
Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2); Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2);
var portabilityFlags = PortabilitySubsetFlags.None; var portabilityFlags = PortabilitySubsetFlags.None;
uint vertexBufferAlignment = 1;
if (usePortability) if (usePortability)
{ {
portabilityFlags |= propertiesPortabilitySubset.MinVertexInputBindingStrideAlignment > 1 ? PortabilitySubsetFlags.VertexBufferAlignment4B : 0; vertexBufferAlignment = propertiesPortabilitySubset.MinVertexInputBindingStrideAlignment;
portabilityFlags |= featuresPortabilitySubset.TriangleFans ? 0 : PortabilitySubsetFlags.NoTriangleFans; portabilityFlags |= featuresPortabilitySubset.TriangleFans ? 0 : PortabilitySubsetFlags.NoTriangleFans;
portabilityFlags |= featuresPortabilitySubset.PointPolygons ? 0 : PortabilitySubsetFlags.NoPointMode; portabilityFlags |= featuresPortabilitySubset.PointPolygons ? 0 : PortabilitySubsetFlags.NoPointMode;
portabilityFlags |= featuresPortabilitySubset.ImageView2DOn3DImage ? 0 : PortabilitySubsetFlags.No3DImageView; portabilityFlags |= featuresPortabilitySubset.ImageView2DOn3DImage ? 0 : PortabilitySubsetFlags.No3DImageView;
@@ -278,9 +280,10 @@ namespace Ryujinx.Graphics.Vulkan
propertiesSubgroupSizeControl.MaxSubgroupSize, propertiesSubgroupSizeControl.MaxSubgroupSize,
propertiesSubgroupSizeControl.RequiredSubgroupSizeStages, propertiesSubgroupSizeControl.RequiredSubgroupSizeStages,
supportedSampleCounts, supportedSampleCounts,
portabilityFlags); portabilityFlags,
vertexBufferAlignment);
MemoryAllocator = new MemoryAllocator(Api, _device, properties.Limits.MaxMemoryAllocationCount); MemoryAllocator = new MemoryAllocator(Api, _physicalDevice, _device, properties.Limits.MaxMemoryAllocationCount);
CommandBufferPool = VulkanInitialization.CreateCommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex); CommandBufferPool = VulkanInitialization.CreateCommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
@@ -290,7 +293,7 @@ namespace Ryujinx.Graphics.Vulkan
BackgroundResources = new BackgroundResources(this, _device); BackgroundResources = new BackgroundResources(this, _device);
BufferManager = new BufferManager(this, _physicalDevice, _device); BufferManager = new BufferManager(this, _device);
_syncManager = new SyncManager(this, _device); _syncManager = new SyncManager(this, _device);
_pipeline = new PipelineFull(this, _device); _pipeline = new PipelineFull(this, _device);
@@ -388,7 +391,7 @@ namespace Ryujinx.Graphics.Vulkan
internal TextureStorage CreateTextureStorage(TextureCreateInfo info, float scale) internal TextureStorage CreateTextureStorage(TextureCreateInfo info, float scale)
{ {
return new TextureStorage(this, _physicalDevice, _device, info, scale); return new TextureStorage(this, _device, info, scale);
} }
public void DeleteBuffer(BufferHandle buffer) public void DeleteBuffer(BufferHandle buffer)
@@ -636,11 +639,11 @@ namespace Ryujinx.Graphics.Vulkan
PrintGpuInformation(); PrintGpuInformation();
} }
public bool NeedsVertexBufferAlignment(int attrScalarAlignment, out int alignment) internal bool NeedsVertexBufferAlignment(int attrScalarAlignment, out int alignment)
{ {
if (Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.VertexBufferAlignment4B)) if (Capabilities.VertexBufferAlignment > 1)
{ {
alignment = 4; alignment = (int)Capabilities.VertexBufferAlignment;
return true; return true;
} }