diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
index 0f3ae2548..5ab22d755 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/CodeGenContext.cs
@@ -31,6 +31,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
         public Dictionary<TextureMeta, (Instruction, Instruction)> Images { get; } = new Dictionary<TextureMeta, (Instruction, Instruction)>();
         public Dictionary<int, Instruction> Inputs { get; } = new Dictionary<int, Instruction>();
         public Dictionary<int, Instruction> Outputs { get; } = new Dictionary<int, Instruction>();
+        public Dictionary<int, Instruction> InputsPerPatch { get; } = new Dictionary<int, Instruction>();
+        public Dictionary<int, Instruction> OutputsPerPatch { get; } = new Dictionary<int, Instruction>();
 
         private readonly Dictionary<AstOperand, Instruction> _locals = new Dictionary<AstOperand, Instruction>();
         private readonly Dictionary<int, Instruction[]> _localForArgs = new Dictionary<int, Instruction[]>();
@@ -148,7 +150,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
 
         public Instruction[] GetMainInterface()
         {
-            var mainInterface = Inputs.Values.Concat(Outputs.Values);
+            var mainInterface = Inputs.Values
+                .Concat(Outputs.Values)
+                .Concat(InputsPerPatch.Values)
+                .Concat(OutputsPerPatch.Values);
 
             if (InputsArray != null)
             {
@@ -216,6 +221,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
                 {
                     IrOperandType.Argument => GetArgument(type, operand),
                     IrOperandType.Attribute => GetAttribute(type, operand.Value & AttributeConsts.Mask, (operand.Value & AttributeConsts.LoadOutputMask) != 0),
+                    IrOperandType.AttributePerPatch => GetAttributePerPatch(type, operand.Value & AttributeConsts.Mask, (operand.Value & AttributeConsts.LoadOutputMask) != 0),
                     IrOperandType.Constant => GetConstant(type, operand),
                     IrOperandType.ConstantBuffer => GetConstantBuffer(type, operand),
                     IrOperandType.LocalVariable => GetLocal(type, operand),
@@ -317,6 +323,40 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
             return BitcastIfNeeded(type, elemType, Load(GetType(elemType), elemPointer));
         }
 
+        public Instruction GetAttributePerPatchElemPointer(int attr, bool isOutAttr, out AggregateType elemType)
+        {
+            var storageClass = isOutAttr ? StorageClass.Output : StorageClass.Input;
+            var attrInfo = AttributeInfo.From(Config, attr, isOutAttr);
+
+            int attrOffset = attrInfo.BaseValue;
+            Instruction ioVariable;
+
+            bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
+
+            elemType = attrInfo.Type & AggregateType.ElementTypeMask;
+
+            ioVariable = isOutAttr ? OutputsPerPatch[attrOffset] : InputsPerPatch[attrOffset];
+
+            if ((attrInfo.Type & (AggregateType.Array | AggregateType.Vector)) == 0)
+            {
+                return ioVariable;
+            }
+
+            var elemIndex = Constant(TypeU32(), attrInfo.GetInnermostIndex());
+            return AccessChain(TypePointer(storageClass, GetType(elemType)), ioVariable, elemIndex);
+        }
+
+        public Instruction GetAttributePerPatch(AggregateType type, int attr, bool isOutAttr)
+        {
+            if (!AttributeInfo.Validate(Config, attr, isOutAttr: false))
+            {
+                return GetConstant(type, new AstOperand(IrOperandType.Constant, 0));
+            }
+
+            var elemPointer = GetAttributePerPatchElemPointer(attr, isOutAttr, out var elemType);
+            return BitcastIfNeeded(type, elemType, Load(GetType(elemType), elemPointer));
+        }
+
         public Instruction GetAttribute(AggregateType type, Instruction attr, bool isOutAttr, Instruction index = null)
         {
             var elemPointer = GetAttributeElemPointer(attr, isOutAttr, index, out var elemType);
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
index 9baf292e8..349a376f4 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/Declarations.cs
@@ -98,8 +98,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
             DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
             DeclareSamplers(context, context.Config.GetTextureDescriptors());
             DeclareImages(context, context.Config.GetImageDescriptors());
-            DeclareInputAttributes(context, info);
-            DeclareOutputAttributes(context, info);
+            DeclareInputAttributes(context, info, perPatch: false);
+            DeclareOutputAttributes(context, info, perPatch: false);
+            DeclareInputAttributes(context, info, perPatch: true);
+            DeclareOutputAttributes(context, info, perPatch: true);
         }
 
         private static void DeclareLocalMemory(CodeGenContext context, int size)
@@ -354,11 +356,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
             };
         }
 
-        private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info)
+        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 info.Inputs)
+            foreach (int attr in inputs)
             {
                 if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: false))
                 {
@@ -367,7 +370,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
 
                 bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
 
-                if (iaIndexing && isUserAttr)
+                if (iaIndexing && isUserAttr && !perPatch)
                 {
                     if (context.InputsArray == null)
                     {
@@ -399,16 +402,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
                         iq = context.Config.ImapTypes[(attr - AttributeConsts.UserAttributeBase) / 16].GetFirstUsedType();
                     }
 
-                    DeclareInputOrOutput(context, attr, false, iq);
+                    DeclareInputOrOutput(context, attr, perPatch, isOutAttr: false, iq);
                 }
             }
         }
 
-        private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info)
+        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 info.Outputs)
+            foreach (int attr in outputs)
             {
                 if (!AttributeInfo.Validate(context.Config, attr, isOutAttr: true))
                 {
@@ -417,7 +421,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
 
                 bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
 
-                if (oaIndexing && isUserAttr)
+                if (oaIndexing && isUserAttr && !perPatch)
                 {
                     if (context.OutputsArray == null)
                     {
@@ -435,30 +439,30 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
                 }
                 else
                 {
-                    DeclareOutputAttribute(context, attr);
+                    DeclareOutputAttribute(context, attr, perPatch);
                 }
             }
 
             if (context.Config.Stage == ShaderStage.Vertex)
             {
-                DeclareOutputAttribute(context, AttributeConsts.PositionX);
+                DeclareOutputAttribute(context, AttributeConsts.PositionX, perPatch: false);
             }
         }
 
-        private static void DeclareOutputAttribute(CodeGenContext context, int attr)
+        private static void DeclareOutputAttribute(CodeGenContext context, int attr, bool perPatch)
         {
-            DeclareInputOrOutput(context, attr, true);
+            DeclareInputOrOutput(context, attr, perPatch, isOutAttr: true);
         }
 
         public static void DeclareInvocationId(CodeGenContext context)
         {
-            DeclareInputOrOutput(context, AttributeConsts.LaneId, false);
+            DeclareInputOrOutput(context, AttributeConsts.LaneId, perPatch: false, isOutAttr: false);
         }
 
-        private static void DeclareInputOrOutput(CodeGenContext context, int attr, bool isOutAttr, PixelImap iq = PixelImap.Unused)
+        private static void DeclareInputOrOutput(CodeGenContext context, int attr, bool perPatch, bool isOutAttr, PixelImap iq = PixelImap.Unused)
         {
             bool isUserAttr = attr >= AttributeConsts.UserAttributeBase && attr < AttributeConsts.UserAttributeEnd;
-            if (isUserAttr && context.Config.TransformFeedbackEnabled &&
+            if (isUserAttr && context.Config.TransformFeedbackEnabled && !perPatch &&
                 ((isOutAttr && context.Config.Stage != ShaderStage.Fragment) ||
                 (!isOutAttr && context.Config.Stage != ShaderStage.Vertex)))
             {
@@ -466,7 +470,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
                 return;
             }
 
-            var dict = isOutAttr ? context.Outputs : context.Inputs;
+            var dict = perPatch
+                ? (isOutAttr ? context.OutputsPerPatch : context.InputsPerPatch)
+                : (isOutAttr ? context.Outputs : context.Inputs);
+
             var attrInfo = AttributeInfo.From(context.Config, attr, isOutAttr);
 
             if (dict.ContainsKey(attrInfo.BaseValue))
@@ -485,6 +492,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
             var spvType = context.TypePointer(storageClass, attrType);
             var spvVar = context.Variable(spvType, storageClass);
 
+            if (perPatch)
+            {
+                context.Decorate(spvVar, Decoration.Patch);
+            }
+
             if (attrInfo.IsBuiltin)
             {
                 context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue));
@@ -503,6 +515,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
             else if (isUserAttr)
             {
                 int location = (attr - AttributeConsts.UserAttributeBase) / 16;
+
+                if (perPatch)
+                {
+                    location += 32;
+                }
+
                 context.Decorate(spvVar, Decoration.Location, (LiteralInteger)location);
 
                 if (!isOutAttr)
@@ -586,6 +604,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
         {
             return attr switch
             {
+                AttributeConsts.TessLevelOuter0 => BuiltIn.TessLevelOuter,
+                AttributeConsts.TessLevelInner0 => BuiltIn.TessLevelInner,
                 AttributeConsts.Layer => BuiltIn.Layer,
                 AttributeConsts.ViewportIndex => BuiltIn.ViewportIndex,
                 AttributeConsts.PointSize => BuiltIn.PointSize,
diff --git a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs
index 7dd4a12c0..352e1aa65 100644
--- a/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs
+++ b/Ryujinx.Graphics.Shader/CodeGen/Spirv/SpirvGenerator.cs
@@ -66,11 +66,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
                 context.AddCapability(Capability.FragmentShaderPixelInterlockEXT);
                 context.AddExtension("SPV_EXT_fragment_shader_interlock");
             }
-
-            if (config.Stage == ShaderStage.Geometry)
+            else if (config.Stage == ShaderStage.Geometry)
             {
                 context.AddCapability(Capability.Geometry);
             }
+            else if (config.Stage == ShaderStage.TessellationControl || config.Stage == ShaderStage.TessellationEvaluation)
+            {
+                context.AddCapability(Capability.Tessellation);
+            }
 
             context.AddExtension("SPV_KHR_shader_ballot");
             context.AddExtension("SPV_KHR_subgroup_vote");
@@ -149,11 +152,50 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
             {
                 context.AddEntryPoint(context.Config.Stage.Convert(), spvFunc, "main", context.GetMainInterface());
 
-                if (context.Config.Stage == ShaderStage.Geometry)
+                if (context.Config.Stage == ShaderStage.TessellationControl)
                 {
-                    InputTopology inPrimitive = context.Config.GpuAccessor.QueryPrimitiveTopology();
+                    context.AddExecutionMode(spvFunc, ExecutionMode.OutputVertices, (SpvLiteralInteger)context.Config.ThreadsPerInputPrimitive);
+                }
+                else if (context.Config.Stage == ShaderStage.TessellationEvaluation)
+                {
+                    switch (context.Config.GpuAccessor.QueryTessPatchType())
+                    {
+                        case TessPatchType.Isolines:
+                            context.AddExecutionMode(spvFunc, ExecutionMode.Isolines);
+                            break;
+                        case TessPatchType.Triangles:
+                            context.AddExecutionMode(spvFunc, ExecutionMode.Triangles);
+                            break;
+                        case TessPatchType.Quads:
+                            context.AddExecutionMode(spvFunc, ExecutionMode.Quads);
+                            break;
+                    }
 
-                    switch (inPrimitive)
+                    switch (context.Config.GpuAccessor.QueryTessSpacing())
+                    {
+                        case TessSpacing.EqualSpacing:
+                            context.AddExecutionMode(spvFunc, ExecutionMode.SpacingEqual);
+                            break;
+                        case TessSpacing.FractionalEventSpacing:
+                            context.AddExecutionMode(spvFunc, ExecutionMode.SpacingFractionalEven);
+                            break;
+                        case TessSpacing.FractionalOddSpacing:
+                            context.AddExecutionMode(spvFunc, ExecutionMode.SpacingFractionalOdd);
+                            break;
+                    }
+
+                    if (context.Config.GpuAccessor.QueryTessCw())
+                    {
+                        context.AddExecutionMode(spvFunc, ExecutionMode.VertexOrderCw);
+                    }
+                    else
+                    {
+                        context.AddExecutionMode(spvFunc, ExecutionMode.VertexOrderCcw);
+                    }
+                }
+                else if (context.Config.Stage == ShaderStage.Geometry)
+                {
+                    switch (context.Config.GpuAccessor.QueryPrimitiveTopology())
                     {
                         case InputTopology.Points:
                             context.AddExecutionMode(spvFunc, ExecutionMode.InputPoints);
@@ -329,11 +371,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
                         var source = context.Get(dest.VarType.Convert(), assignment.Source);
                         context.Store(context.GetLocalPointer(dest), source);
                     }
-                    else if (dest.Type == OperandType.Attribute)
+                    else if (dest.Type == OperandType.Attribute || dest.Type == OperandType.AttributePerPatch)
                     {
                         if (AttributeInfo.Validate(context.Config, dest.Value, isOutAttr: true))
                         {
-                            var elemPointer = context.GetAttributeElemPointer(dest.Value, true, null, out var elemType);
+                            bool perPatch = dest.Type == OperandType.AttributePerPatch;
+                            AggregateType elemType;
+
+                            var elemPointer = perPatch
+                                ? context.GetAttributePerPatchElemPointer(dest.Value, true, out elemType)
+                                : context.GetAttributeElemPointer(dest.Value, true, null, out elemType);
+
                             context.Store(elemPointer, context.Get(elemType, assignment.Source));
                         }
                     }
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs
index 2c87721ee..19c4ad461 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs
@@ -281,6 +281,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
             {
                 Info.Outputs.Add(operand.Value & AttributeConsts.Mask);
             }
+            else if (operand.Type == OperandType.AttributePerPatch)
+            {
+                Info.OutputsPerPatch.Add(operand.Value & AttributeConsts.Mask);
+            }
 
             return GetOperand(operand);
         }
@@ -297,6 +301,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
             {
                 Info.Inputs.Add(operand.Value);
             }
+            else if (operand.Type == OperandType.AttributePerPatch)
+            {
+                Info.InputsPerPatch.Add(operand.Value);
+            }
 
             return GetOperand(operand);
         }
diff --git a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs
index 650349b6f..43bdfaba5 100644
--- a/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs
+++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs
@@ -24,6 +24,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
 
         public HashSet<int> Inputs { get; }
         public HashSet<int> Outputs { get; }
+        public HashSet<int> InputsPerPatch { get; }
+        public HashSet<int> OutputsPerPatch { get; }
 
         public HelperFunctionsMask HelperFunctionsMask { get; set; }
 
@@ -35,6 +37,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
 
             Inputs = new HashSet<int>();
             Outputs = new HashSet<int>();
+            InputsPerPatch = new HashSet<int>();
+            OutputsPerPatch = new HashSet<int>();
 
             TransformFeedbackOutputs = new TransformFeedbackOutput[0xc0];
         }
diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
index f69548507..8bf98963d 100644
--- a/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
+++ b/Ryujinx.Graphics.Shader/Translation/AttributeInfo.cs
@@ -6,28 +6,34 @@ namespace Ryujinx.Graphics.Shader.Translation
     {
         private static readonly Dictionary<int, AttributeInfo> BuiltInAttributes = new Dictionary<int, AttributeInfo>()
         {
-            { AttributeConsts.Layer,         new AttributeInfo(AttributeConsts.Layer,         0, 1, AggregateType.S32) },
-            { AttributeConsts.ViewportIndex, new AttributeInfo(AttributeConsts.ViewportIndex, 0, 1, AggregateType.S32) },
-            { AttributeConsts.PointSize,     new AttributeInfo(AttributeConsts.PointSize,     0, 1, AggregateType.FP32) },
-            { AttributeConsts.PositionX,     new AttributeInfo(AttributeConsts.PositionX,     0, 4, AggregateType.Vector | AggregateType.FP32) },
-            { AttributeConsts.PositionY,     new AttributeInfo(AttributeConsts.PositionX,     1, 4, AggregateType.Vector | AggregateType.FP32) },
-            { AttributeConsts.PositionZ,     new AttributeInfo(AttributeConsts.PositionX,     2, 4, AggregateType.Vector | AggregateType.FP32) },
-            { AttributeConsts.PositionW,     new AttributeInfo(AttributeConsts.PositionX,     3, 4, AggregateType.Vector | AggregateType.FP32) },
-            { AttributeConsts.ClipDistance0, new AttributeInfo(AttributeConsts.ClipDistance0, 0, 8, AggregateType.Array  | AggregateType.FP32) },
-            { AttributeConsts.ClipDistance1, new AttributeInfo(AttributeConsts.ClipDistance0, 1, 8, AggregateType.Array  | AggregateType.FP32) },
-            { AttributeConsts.ClipDistance2, new AttributeInfo(AttributeConsts.ClipDistance0, 2, 8, AggregateType.Array  | AggregateType.FP32) },
-            { AttributeConsts.ClipDistance3, new AttributeInfo(AttributeConsts.ClipDistance0, 3, 8, AggregateType.Array  | AggregateType.FP32) },
-            { AttributeConsts.ClipDistance4, new AttributeInfo(AttributeConsts.ClipDistance0, 4, 8, AggregateType.Array  | AggregateType.FP32) },
-            { AttributeConsts.ClipDistance5, new AttributeInfo(AttributeConsts.ClipDistance0, 5, 8, AggregateType.Array  | AggregateType.FP32) },
-            { AttributeConsts.ClipDistance6, new AttributeInfo(AttributeConsts.ClipDistance0, 6, 8, AggregateType.Array  | AggregateType.FP32) },
-            { AttributeConsts.ClipDistance7, new AttributeInfo(AttributeConsts.ClipDistance0, 7, 8, AggregateType.Array  | AggregateType.FP32) },
-            { AttributeConsts.PointCoordX,   new AttributeInfo(AttributeConsts.PointCoordX,   0, 2, AggregateType.Vector | AggregateType.FP32) },
-            { AttributeConsts.PointCoordY,   new AttributeInfo(AttributeConsts.PointCoordX,   1, 2, AggregateType.Vector | AggregateType.FP32) },
-            { AttributeConsts.TessCoordX,    new AttributeInfo(AttributeConsts.TessCoordX,    0, 2, AggregateType.Vector | AggregateType.FP32) },
-            { AttributeConsts.TessCoordY,    new AttributeInfo(AttributeConsts.TessCoordX,    1, 2, AggregateType.Vector | AggregateType.FP32) },
-            { AttributeConsts.InstanceId,    new AttributeInfo(AttributeConsts.InstanceId,    0, 1, AggregateType.S32) },
-            { AttributeConsts.VertexId,      new AttributeInfo(AttributeConsts.VertexId,      0, 1, AggregateType.S32) },
-            { AttributeConsts.FrontFacing,   new AttributeInfo(AttributeConsts.FrontFacing,   0, 1, AggregateType.Bool) },
+            { AttributeConsts.TessLevelOuter0, new AttributeInfo(AttributeConsts.TessLevelOuter0, 0, 4, AggregateType.Array  | AggregateType.FP32) },
+            { AttributeConsts.TessLevelOuter1, new AttributeInfo(AttributeConsts.TessLevelOuter0, 1, 4, AggregateType.Array  | AggregateType.FP32) },
+            { AttributeConsts.TessLevelOuter2, new AttributeInfo(AttributeConsts.TessLevelOuter0, 2, 4, AggregateType.Array  | AggregateType.FP32) },
+            { AttributeConsts.TessLevelOuter3, new AttributeInfo(AttributeConsts.TessLevelOuter0, 3, 4, AggregateType.Array  | AggregateType.FP32) },
+            { AttributeConsts.TessLevelInner0, new AttributeInfo(AttributeConsts.TessLevelInner0, 0, 2, AggregateType.Array  | AggregateType.FP32) },
+            { AttributeConsts.TessLevelInner1, new AttributeInfo(AttributeConsts.TessLevelInner0, 1, 2, AggregateType.Array  | AggregateType.FP32) },
+            { AttributeConsts.Layer,           new AttributeInfo(AttributeConsts.Layer,           0, 1, AggregateType.S32) },
+            { AttributeConsts.ViewportIndex,   new AttributeInfo(AttributeConsts.ViewportIndex,   0, 1, AggregateType.S32) },
+            { AttributeConsts.PointSize,       new AttributeInfo(AttributeConsts.PointSize,       0, 1, AggregateType.FP32) },
+            { AttributeConsts.PositionX,       new AttributeInfo(AttributeConsts.PositionX,       0, 4, AggregateType.Vector | AggregateType.FP32) },
+            { AttributeConsts.PositionY,       new AttributeInfo(AttributeConsts.PositionX,       1, 4, AggregateType.Vector | AggregateType.FP32) },
+            { AttributeConsts.PositionZ,       new AttributeInfo(AttributeConsts.PositionX,       2, 4, AggregateType.Vector | AggregateType.FP32) },
+            { AttributeConsts.PositionW,       new AttributeInfo(AttributeConsts.PositionX,       3, 4, AggregateType.Vector | AggregateType.FP32) },
+            { AttributeConsts.ClipDistance0,   new AttributeInfo(AttributeConsts.ClipDistance0,   0, 8, AggregateType.Array  | AggregateType.FP32) },
+            { AttributeConsts.ClipDistance1,   new AttributeInfo(AttributeConsts.ClipDistance0,   1, 8, AggregateType.Array  | AggregateType.FP32) },
+            { AttributeConsts.ClipDistance2,   new AttributeInfo(AttributeConsts.ClipDistance0,   2, 8, AggregateType.Array  | AggregateType.FP32) },
+            { AttributeConsts.ClipDistance3,   new AttributeInfo(AttributeConsts.ClipDistance0,   3, 8, AggregateType.Array  | AggregateType.FP32) },
+            { AttributeConsts.ClipDistance4,   new AttributeInfo(AttributeConsts.ClipDistance0,   4, 8, AggregateType.Array  | AggregateType.FP32) },
+            { AttributeConsts.ClipDistance5,   new AttributeInfo(AttributeConsts.ClipDistance0,   5, 8, AggregateType.Array  | AggregateType.FP32) },
+            { AttributeConsts.ClipDistance6,   new AttributeInfo(AttributeConsts.ClipDistance0,   6, 8, AggregateType.Array  | AggregateType.FP32) },
+            { AttributeConsts.ClipDistance7,   new AttributeInfo(AttributeConsts.ClipDistance0,   7, 8, AggregateType.Array  | AggregateType.FP32) },
+            { AttributeConsts.PointCoordX,     new AttributeInfo(AttributeConsts.PointCoordX,     0, 2, AggregateType.Vector | AggregateType.FP32) },
+            { AttributeConsts.PointCoordY,     new AttributeInfo(AttributeConsts.PointCoordX,     1, 2, AggregateType.Vector | AggregateType.FP32) },
+            { AttributeConsts.TessCoordX,      new AttributeInfo(AttributeConsts.TessCoordX,      0, 2, AggregateType.Vector | AggregateType.FP32) },
+            { AttributeConsts.TessCoordY,      new AttributeInfo(AttributeConsts.TessCoordX,      1, 2, AggregateType.Vector | AggregateType.FP32) },
+            { AttributeConsts.InstanceId,      new AttributeInfo(AttributeConsts.InstanceId,      0, 1, AggregateType.S32) },
+            { AttributeConsts.VertexId,        new AttributeInfo(AttributeConsts.VertexId,        0, 1, AggregateType.S32) },
+            { AttributeConsts.FrontFacing,     new AttributeInfo(AttributeConsts.FrontFacing,     0, 1, AggregateType.Bool) },
 
             // Special.
             { AttributeConsts.FragmentOutputDepth, new AttributeInfo(AttributeConsts.FragmentOutputDepth, 0, 1, AggregateType.FP32) },