PipelineConverter.cs
1 using Ryujinx.Common; 2 using Ryujinx.Graphics.GAL; 3 using Silk.NET.Vulkan; 4 using System; 5 using Format = Silk.NET.Vulkan.Format; 6 using PolygonMode = Silk.NET.Vulkan.PolygonMode; 7 8 namespace Ryujinx.Graphics.Vulkan 9 { 10 static class PipelineConverter 11 { 12 public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device) 13 { 14 const int MaxAttachments = Constants.MaxRenderTargets + 1; 15 16 AttachmentDescription[] attachmentDescs = null; 17 18 var subpass = new SubpassDescription 19 { 20 PipelineBindPoint = PipelineBindPoint.Graphics, 21 }; 22 23 AttachmentReference* attachmentReferences = stackalloc AttachmentReference[MaxAttachments]; 24 25 Span<int> attachmentIndices = stackalloc int[MaxAttachments]; 26 Span<Format> attachmentFormats = stackalloc Format[MaxAttachments]; 27 28 int attachmentCount = 0; 29 int colorCount = 0; 30 int maxColorAttachmentIndex = -1; 31 32 for (int i = 0; i < state.AttachmentEnable.Length; i++) 33 { 34 if (state.AttachmentEnable[i]) 35 { 36 attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]); 37 38 attachmentIndices[attachmentCount++] = i; 39 colorCount++; 40 maxColorAttachmentIndex = i; 41 } 42 } 43 44 if (state.DepthStencilEnable) 45 { 46 attachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat); 47 } 48 49 if (attachmentCount != 0) 50 { 51 attachmentDescs = new AttachmentDescription[attachmentCount]; 52 53 for (int i = 0; i < attachmentCount; i++) 54 { 55 int bindIndex = attachmentIndices[i]; 56 57 attachmentDescs[i] = new AttachmentDescription( 58 0, 59 attachmentFormats[i], 60 TextureStorage.ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)state.SamplesCount), 61 AttachmentLoadOp.Load, 62 AttachmentStoreOp.Store, 63 AttachmentLoadOp.Load, 64 AttachmentStoreOp.Store, 65 ImageLayout.General, 66 ImageLayout.General); 67 } 68 69 int colorAttachmentsCount = colorCount; 70 71 if (colorAttachmentsCount > MaxAttachments - 1) 72 { 73 colorAttachmentsCount = MaxAttachments - 1; 74 } 75 76 if (colorAttachmentsCount != 0) 77 { 78 subpass.ColorAttachmentCount = (uint)maxColorAttachmentIndex + 1; 79 subpass.PColorAttachments = &attachmentReferences[0]; 80 81 // Fill with VK_ATTACHMENT_UNUSED to cover any gaps. 82 for (int i = 0; i <= maxColorAttachmentIndex; i++) 83 { 84 subpass.PColorAttachments[i] = new AttachmentReference(Vk.AttachmentUnused, ImageLayout.Undefined); 85 } 86 87 for (int i = 0; i < colorAttachmentsCount; i++) 88 { 89 int bindIndex = attachmentIndices[i]; 90 91 subpass.PColorAttachments[bindIndex] = new AttachmentReference((uint)i, ImageLayout.General); 92 } 93 } 94 95 if (state.DepthStencilEnable) 96 { 97 uint dsIndex = (uint)attachmentCount - 1; 98 99 subpass.PDepthStencilAttachment = &attachmentReferences[MaxAttachments - 1]; 100 *subpass.PDepthStencilAttachment = new AttachmentReference(dsIndex, ImageLayout.General); 101 } 102 } 103 104 var subpassDependency = CreateSubpassDependency(gd); 105 106 fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs) 107 { 108 var renderPassCreateInfo = new RenderPassCreateInfo 109 { 110 SType = StructureType.RenderPassCreateInfo, 111 PAttachments = pAttachmentDescs, 112 AttachmentCount = attachmentDescs != null ? (uint)attachmentDescs.Length : 0, 113 PSubpasses = &subpass, 114 SubpassCount = 1, 115 PDependencies = &subpassDependency, 116 DependencyCount = 1, 117 }; 118 119 gd.Api.CreateRenderPass(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError(); 120 121 return new DisposableRenderPass(gd.Api, device, renderPass); 122 } 123 } 124 125 public static SubpassDependency CreateSubpassDependency(VulkanRenderer gd) 126 { 127 var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd); 128 129 return new SubpassDependency( 130 0, 131 0, 132 stages, 133 stages, 134 access, 135 access, 136 0); 137 } 138 139 public unsafe static SubpassDependency2 CreateSubpassDependency2(VulkanRenderer gd) 140 { 141 var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd); 142 143 return new SubpassDependency2( 144 StructureType.SubpassDependency2, 145 null, 146 0, 147 0, 148 stages, 149 stages, 150 access, 151 access, 152 0); 153 } 154 155 public static PipelineState ToVulkanPipelineState(this ProgramPipelineState state, VulkanRenderer gd) 156 { 157 PipelineState pipeline = new(); 158 pipeline.Initialize(); 159 160 // It is assumed that Dynamic State is enabled when this conversion is used. 161 162 pipeline.CullMode = state.CullEnable ? state.CullMode.Convert() : CullModeFlags.None; 163 164 pipeline.DepthBoundsTestEnable = false; // Not implemented. 165 166 pipeline.DepthClampEnable = state.DepthClampEnable; 167 168 pipeline.DepthTestEnable = state.DepthTest.TestEnable; 169 pipeline.DepthWriteEnable = state.DepthTest.WriteEnable; 170 pipeline.DepthCompareOp = state.DepthTest.Func.Convert(); 171 pipeline.DepthMode = state.DepthMode == DepthMode.MinusOneToOne; 172 173 pipeline.FrontFace = state.FrontFace.Convert(); 174 175 pipeline.HasDepthStencil = state.DepthStencilEnable; 176 pipeline.LineWidth = state.LineWidth; 177 pipeline.LogicOpEnable = state.LogicOpEnable; 178 pipeline.LogicOp = state.LogicOp.Convert(); 179 180 pipeline.PatchControlPoints = state.PatchControlPoints; 181 pipeline.PolygonMode = PolygonMode.Fill; // Not implemented. 182 pipeline.PrimitiveRestartEnable = state.PrimitiveRestartEnable; 183 pipeline.RasterizerDiscardEnable = state.RasterizerDiscard; 184 pipeline.SamplesCount = (uint)state.SamplesCount; 185 186 if (gd.Capabilities.SupportsMultiView) 187 { 188 pipeline.ScissorsCount = Constants.MaxViewports; 189 pipeline.ViewportsCount = Constants.MaxViewports; 190 } 191 else 192 { 193 pipeline.ScissorsCount = 1; 194 pipeline.ViewportsCount = 1; 195 } 196 197 pipeline.DepthBiasEnable = state.BiasEnable != 0; 198 199 // Stencil masks and ref are dynamic, so are 0 in the Vulkan pipeline. 200 201 pipeline.StencilFrontFailOp = state.StencilTest.FrontSFail.Convert(); 202 pipeline.StencilFrontPassOp = state.StencilTest.FrontDpPass.Convert(); 203 pipeline.StencilFrontDepthFailOp = state.StencilTest.FrontDpFail.Convert(); 204 pipeline.StencilFrontCompareOp = state.StencilTest.FrontFunc.Convert(); 205 206 pipeline.StencilBackFailOp = state.StencilTest.BackSFail.Convert(); 207 pipeline.StencilBackPassOp = state.StencilTest.BackDpPass.Convert(); 208 pipeline.StencilBackDepthFailOp = state.StencilTest.BackDpFail.Convert(); 209 pipeline.StencilBackCompareOp = state.StencilTest.BackFunc.Convert(); 210 211 pipeline.StencilTestEnable = state.StencilTest.TestEnable; 212 213 pipeline.Topology = gd.TopologyRemap(state.Topology).Convert(); 214 215 int vaCount = Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount); 216 int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount); 217 218 Span<int> vbScalarSizes = stackalloc int[vbCount]; 219 220 for (int i = 0; i < vaCount; i++) 221 { 222 var attribute = state.VertexAttribs[i]; 223 var bufferIndex = attribute.IsZero ? 0 : attribute.BufferIndex + 1; 224 225 pipeline.Internal.VertexAttributeDescriptions[i] = new VertexInputAttributeDescription( 226 (uint)i, 227 (uint)bufferIndex, 228 gd.FormatCapabilities.ConvertToVertexVkFormat(attribute.Format), 229 (uint)attribute.Offset); 230 231 if (!attribute.IsZero && bufferIndex < vbCount) 232 { 233 vbScalarSizes[bufferIndex - 1] = Math.Max(attribute.Format.GetScalarSize(), vbScalarSizes[bufferIndex - 1]); 234 } 235 } 236 237 int descriptorIndex = 1; 238 pipeline.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, 0, VertexInputRate.Vertex); 239 240 for (int i = 0; i < vbCount; i++) 241 { 242 var vertexBuffer = state.VertexBuffers[i]; 243 244 if (vertexBuffer.Enable) 245 { 246 var inputRate = vertexBuffer.Divisor != 0 ? VertexInputRate.Instance : VertexInputRate.Vertex; 247 248 int alignedStride = vertexBuffer.Stride; 249 250 if (gd.NeedsVertexBufferAlignment(vbScalarSizes[i], out int alignment)) 251 { 252 alignedStride = BitUtils.AlignUp(vertexBuffer.Stride, alignment); 253 } 254 255 // TODO: Support divisor > 1 256 pipeline.Internal.VertexBindingDescriptions[descriptorIndex++] = new VertexInputBindingDescription( 257 (uint)i + 1, 258 (uint)alignedStride, 259 inputRate); 260 } 261 } 262 263 pipeline.VertexBindingDescriptionsCount = (uint)descriptorIndex; 264 265 // NOTE: Viewports, Scissors are dynamic. 266 267 for (int i = 0; i < Constants.MaxRenderTargets; i++) 268 { 269 var blend = state.BlendDescriptors[i]; 270 271 if (blend.Enable && state.ColorWriteMask[i] != 0) 272 { 273 pipeline.Internal.ColorBlendAttachmentState[i] = new PipelineColorBlendAttachmentState( 274 blend.Enable, 275 blend.ColorSrcFactor.Convert(), 276 blend.ColorDstFactor.Convert(), 277 blend.ColorOp.Convert(), 278 blend.AlphaSrcFactor.Convert(), 279 blend.AlphaDstFactor.Convert(), 280 blend.AlphaOp.Convert(), 281 (ColorComponentFlags)state.ColorWriteMask[i]); 282 } 283 else 284 { 285 pipeline.Internal.ColorBlendAttachmentState[i] = new PipelineColorBlendAttachmentState( 286 colorWriteMask: (ColorComponentFlags)state.ColorWriteMask[i]); 287 } 288 } 289 290 int attachmentCount = 0; 291 int maxColorAttachmentIndex = -1; 292 uint attachmentIntegerFormatMask = 0; 293 bool allFormatsFloatOrSrgb = true; 294 295 for (int i = 0; i < Constants.MaxRenderTargets; i++) 296 { 297 if (state.AttachmentEnable[i]) 298 { 299 pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]); 300 maxColorAttachmentIndex = i; 301 302 if (state.AttachmentFormats[i].IsInteger()) 303 { 304 attachmentIntegerFormatMask |= 1u << i; 305 } 306 307 allFormatsFloatOrSrgb &= state.AttachmentFormats[i].IsFloatOrSrgb(); 308 } 309 } 310 311 if (state.DepthStencilEnable) 312 { 313 pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat); 314 } 315 316 pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1); 317 pipeline.VertexAttributeDescriptionsCount = (uint)Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount); 318 pipeline.Internal.AttachmentIntegerFormatMask = attachmentIntegerFormatMask; 319 pipeline.Internal.LogicOpsAllowed = attachmentCount == 0 || !allFormatsFloatOrSrgb; 320 321 return pipeline; 322 } 323 } 324 }