Declarations.cs
1 using Ryujinx.Common; 2 using Ryujinx.Graphics.Shader.IntermediateRepresentation; 3 using Ryujinx.Graphics.Shader.StructuredIr; 4 using Ryujinx.Graphics.Shader.Translation; 5 using System; 6 using System.Collections.Generic; 7 using System.Globalization; 8 using System.Linq; 9 10 namespace Ryujinx.Graphics.Shader.CodeGen.Glsl 11 { 12 static class Declarations 13 { 14 public static void Declare(CodeGenContext context, StructuredProgramInfo info) 15 { 16 context.AppendLine(context.TargetApi == TargetApi.Vulkan ? "#version 460 core" : "#version 450 core"); 17 context.AppendLine("#extension GL_ARB_gpu_shader_int64 : enable"); 18 19 if (context.HostCapabilities.SupportsShaderBallot) 20 { 21 context.AppendLine("#extension GL_ARB_shader_ballot : enable"); 22 } 23 else 24 { 25 context.AppendLine("#extension GL_KHR_shader_subgroup_basic : enable"); 26 context.AppendLine("#extension GL_KHR_shader_subgroup_ballot : enable"); 27 context.AppendLine("#extension GL_KHR_shader_subgroup_shuffle : enable"); 28 } 29 30 context.AppendLine("#extension GL_ARB_shader_group_vote : enable"); 31 context.AppendLine("#extension GL_EXT_shader_image_load_formatted : enable"); 32 context.AppendLine("#extension GL_EXT_texture_shadow_lod : enable"); 33 34 if (context.Definitions.Stage == ShaderStage.Compute) 35 { 36 context.AppendLine("#extension GL_ARB_compute_shader : enable"); 37 } 38 else if (context.Definitions.Stage == ShaderStage.Fragment) 39 { 40 if (context.HostCapabilities.SupportsFragmentShaderInterlock) 41 { 42 context.AppendLine("#extension GL_ARB_fragment_shader_interlock : enable"); 43 } 44 else if (context.HostCapabilities.SupportsFragmentShaderOrderingIntel) 45 { 46 context.AppendLine("#extension GL_INTEL_fragment_shader_ordering : enable"); 47 } 48 } 49 else 50 { 51 if (context.Definitions.Stage == ShaderStage.Vertex) 52 { 53 context.AppendLine("#extension GL_ARB_shader_draw_parameters : enable"); 54 } 55 56 context.AppendLine("#extension GL_ARB_shader_viewport_layer_array : enable"); 57 } 58 59 if (context.Definitions.GpPassthrough && context.HostCapabilities.SupportsGeometryShaderPassthrough) 60 { 61 context.AppendLine("#extension GL_NV_geometry_shader_passthrough : enable"); 62 } 63 64 if (context.HostCapabilities.SupportsViewportMask) 65 { 66 context.AppendLine("#extension GL_NV_viewport_array2 : enable"); 67 } 68 69 context.AppendLine("#pragma optionNV(fastmath off)"); 70 context.AppendLine(); 71 72 context.AppendLine($"const int {DefaultNames.UndefinedName} = 0;"); 73 context.AppendLine(); 74 75 DeclareConstantBuffers(context, context.Properties.ConstantBuffers.Values); 76 DeclareStorageBuffers(context, context.Properties.StorageBuffers.Values); 77 DeclareMemories(context, context.Properties.LocalMemories.Values, isShared: false); 78 DeclareMemories(context, context.Properties.SharedMemories.Values, isShared: true); 79 DeclareSamplers(context, context.Properties.Textures.Values); 80 DeclareImages(context, context.Properties.Images.Values); 81 82 if (context.Definitions.Stage != ShaderStage.Compute) 83 { 84 if (context.Definitions.Stage == ShaderStage.Geometry) 85 { 86 string inPrimitive = context.Definitions.InputTopology.ToGlslString(); 87 88 context.AppendLine($"layout (invocations = {context.Definitions.ThreadsPerInputPrimitive}, {inPrimitive}) in;"); 89 90 if (context.Definitions.GpPassthrough && context.HostCapabilities.SupportsGeometryShaderPassthrough) 91 { 92 context.AppendLine($"layout (passthrough) in gl_PerVertex"); 93 context.EnterScope(); 94 context.AppendLine("vec4 gl_Position;"); 95 context.AppendLine("float gl_PointSize;"); 96 context.AppendLine("float gl_ClipDistance[];"); 97 context.LeaveScope(";"); 98 } 99 else 100 { 101 string outPrimitive = context.Definitions.OutputTopology.ToGlslString(); 102 int maxOutputVertices = context.Definitions.MaxOutputVertices; 103 104 context.AppendLine($"layout ({outPrimitive}, max_vertices = {maxOutputVertices}) out;"); 105 } 106 107 context.AppendLine(); 108 } 109 else if (context.Definitions.Stage == ShaderStage.TessellationControl) 110 { 111 int threadsPerInputPrimitive = context.Definitions.ThreadsPerInputPrimitive; 112 113 context.AppendLine($"layout (vertices = {threadsPerInputPrimitive}) out;"); 114 context.AppendLine(); 115 } 116 else if (context.Definitions.Stage == ShaderStage.TessellationEvaluation) 117 { 118 bool tessCw = context.Definitions.TessCw; 119 120 if (context.TargetApi == TargetApi.Vulkan) 121 { 122 // We invert the front face on Vulkan backend, so we need to do that here aswell. 123 tessCw = !tessCw; 124 } 125 126 string patchType = context.Definitions.TessPatchType.ToGlsl(); 127 string spacing = context.Definitions.TessSpacing.ToGlsl(); 128 string windingOrder = tessCw ? "cw" : "ccw"; 129 130 context.AppendLine($"layout ({patchType}, {spacing}, {windingOrder}) in;"); 131 context.AppendLine(); 132 } 133 134 static bool IsUserDefined(IoDefinition ioDefinition, StorageKind storageKind) 135 { 136 return ioDefinition.StorageKind == storageKind && ioDefinition.IoVariable == IoVariable.UserDefined; 137 } 138 139 static bool IsUserDefinedOutput(ShaderStage stage, IoDefinition ioDefinition) 140 { 141 IoVariable ioVariable = stage == ShaderStage.Fragment ? IoVariable.FragmentOutputColor : IoVariable.UserDefined; 142 return ioDefinition.StorageKind == StorageKind.Output && ioDefinition.IoVariable == ioVariable; 143 } 144 145 DeclareInputAttributes(context, info.IoDefinitions.Where(x => IsUserDefined(x, StorageKind.Input))); 146 DeclareOutputAttributes(context, info.IoDefinitions.Where(x => IsUserDefinedOutput(context.Definitions.Stage, x))); 147 DeclareInputAttributesPerPatch(context, info.IoDefinitions.Where(x => IsUserDefined(x, StorageKind.InputPerPatch))); 148 DeclareOutputAttributesPerPatch(context, info.IoDefinitions.Where(x => IsUserDefined(x, StorageKind.OutputPerPatch))); 149 150 if (context.Definitions.TransformFeedbackEnabled && context.Definitions.LastInVertexPipeline) 151 { 152 var tfOutput = context.Definitions.GetTransformFeedbackOutput(AttributeConsts.PositionX); 153 if (tfOutput.Valid) 154 { 155 context.AppendLine($"layout (xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}) out gl_PerVertex"); 156 context.EnterScope(); 157 context.AppendLine("vec4 gl_Position;"); 158 context.LeaveScope(context.Definitions.Stage == ShaderStage.TessellationControl ? " gl_out[];" : ";"); 159 } 160 } 161 } 162 else 163 { 164 string localSizeX = NumberFormatter.FormatInt(context.Definitions.ComputeLocalSizeX); 165 string localSizeY = NumberFormatter.FormatInt(context.Definitions.ComputeLocalSizeY); 166 string localSizeZ = NumberFormatter.FormatInt(context.Definitions.ComputeLocalSizeZ); 167 168 context.AppendLine( 169 "layout (" + 170 $"local_size_x = {localSizeX}, " + 171 $"local_size_y = {localSizeY}, " + 172 $"local_size_z = {localSizeZ}) in;"); 173 context.AppendLine(); 174 } 175 176 if (context.Definitions.Stage == ShaderStage.Fragment) 177 { 178 if (context.Definitions.EarlyZForce) 179 { 180 context.AppendLine("layout (early_fragment_tests) in;"); 181 context.AppendLine(); 182 } 183 184 if (context.Definitions.OriginUpperLeft) 185 { 186 context.AppendLine("layout (origin_upper_left) in vec4 gl_FragCoord;"); 187 context.AppendLine(); 188 } 189 } 190 191 if ((info.HelperFunctionsMask & HelperFunctionsMask.MultiplyHighS32) != 0) 192 { 193 AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl"); 194 } 195 196 if ((info.HelperFunctionsMask & HelperFunctionsMask.MultiplyHighU32) != 0) 197 { 198 AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighU32.glsl"); 199 } 200 201 if ((info.HelperFunctionsMask & HelperFunctionsMask.SwizzleAdd) != 0) 202 { 203 AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/SwizzleAdd.glsl"); 204 } 205 } 206 207 public static void DeclareLocals(CodeGenContext context, StructuredFunction function) 208 { 209 foreach (AstOperand decl in function.Locals) 210 { 211 string name = context.OperandManager.DeclareLocal(decl); 212 213 context.AppendLine(GetVarTypeName(context, decl.VarType) + " " + name + ";"); 214 } 215 } 216 217 public static string GetVarTypeName(CodeGenContext context, AggregateType type, bool precise = true) 218 { 219 if (context.HostCapabilities.ReducedPrecision) 220 { 221 precise = false; 222 } 223 224 return type switch 225 { 226 AggregateType.Void => "void", 227 AggregateType.Bool => "bool", 228 AggregateType.FP32 => precise ? "precise float" : "float", 229 AggregateType.FP64 => "double", 230 AggregateType.S32 => "int", 231 AggregateType.U32 => "uint", 232 AggregateType.Vector2 | AggregateType.Bool => "bvec2", 233 AggregateType.Vector2 | AggregateType.FP32 => precise ? "precise vec2" : "vec2", 234 AggregateType.Vector2 | AggregateType.FP64 => "dvec2", 235 AggregateType.Vector2 | AggregateType.S32 => "ivec2", 236 AggregateType.Vector2 | AggregateType.U32 => "uvec2", 237 AggregateType.Vector3 | AggregateType.Bool => "bvec3", 238 AggregateType.Vector3 | AggregateType.FP32 => precise ? "precise vec3" : "vec3", 239 AggregateType.Vector3 | AggregateType.FP64 => "dvec3", 240 AggregateType.Vector3 | AggregateType.S32 => "ivec3", 241 AggregateType.Vector3 | AggregateType.U32 => "uvec3", 242 AggregateType.Vector4 | AggregateType.Bool => "bvec4", 243 AggregateType.Vector4 | AggregateType.FP32 => precise ? "precise vec4" : "vec4", 244 AggregateType.Vector4 | AggregateType.FP64 => "dvec4", 245 AggregateType.Vector4 | AggregateType.S32 => "ivec4", 246 AggregateType.Vector4 | AggregateType.U32 => "uvec4", 247 _ => throw new ArgumentException($"Invalid variable type \"{type}\"."), 248 }; 249 } 250 251 private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers) 252 { 253 DeclareBuffers(context, buffers, "uniform"); 254 } 255 256 private static void DeclareStorageBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers) 257 { 258 DeclareBuffers(context, buffers, "buffer"); 259 } 260 261 private static void DeclareBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers, string declType) 262 { 263 foreach (BufferDefinition buffer in buffers) 264 { 265 string layout = buffer.Layout switch 266 { 267 BufferLayout.Std140 => "std140", 268 _ => "std430", 269 }; 270 271 string set = string.Empty; 272 273 if (context.TargetApi == TargetApi.Vulkan) 274 { 275 set = $"set = {buffer.Set}, "; 276 } 277 278 context.AppendLine($"layout ({set}binding = {buffer.Binding}, {layout}) {declType} _{buffer.Name}"); 279 context.EnterScope(); 280 281 foreach (StructureField field in buffer.Type.Fields) 282 { 283 if (field.Type.HasFlag(AggregateType.Array)) 284 { 285 string typeName = GetVarTypeName(context, field.Type & ~AggregateType.Array); 286 287 if (field.ArrayLength > 0) 288 { 289 string arraySize = field.ArrayLength.ToString(CultureInfo.InvariantCulture); 290 291 context.AppendLine($"{typeName} {field.Name}[{arraySize}];"); 292 } 293 else 294 { 295 context.AppendLine($"{typeName} {field.Name}[];"); 296 } 297 } 298 else 299 { 300 string typeName = GetVarTypeName(context, field.Type); 301 302 context.AppendLine($"{typeName} {field.Name};"); 303 } 304 } 305 306 context.LeaveScope($" {buffer.Name};"); 307 context.AppendLine(); 308 } 309 } 310 311 private static void DeclareMemories(CodeGenContext context, IEnumerable<MemoryDefinition> memories, bool isShared) 312 { 313 string prefix = isShared ? "shared " : string.Empty; 314 315 foreach (MemoryDefinition memory in memories) 316 { 317 string typeName = GetVarTypeName(context, memory.Type & ~AggregateType.Array); 318 319 if (memory.Type.HasFlag(AggregateType.Array)) 320 { 321 if (memory.ArrayLength > 0) 322 { 323 string arraySize = memory.ArrayLength.ToString(CultureInfo.InvariantCulture); 324 325 context.AppendLine($"{prefix}{typeName} {memory.Name}[{arraySize}];"); 326 } 327 else 328 { 329 context.AppendLine($"{prefix}{typeName} {memory.Name}[];"); 330 } 331 } 332 else 333 { 334 context.AppendLine($"{prefix}{typeName} {memory.Name};"); 335 } 336 } 337 } 338 339 private static void DeclareSamplers(CodeGenContext context, IEnumerable<TextureDefinition> definitions) 340 { 341 foreach (var definition in definitions) 342 { 343 string arrayDecl = string.Empty; 344 345 if (definition.ArrayLength > 1) 346 { 347 arrayDecl = $"[{NumberFormatter.FormatInt(definition.ArrayLength)}]"; 348 } 349 else if (definition.ArrayLength == 0) 350 { 351 arrayDecl = "[]"; 352 } 353 354 string samplerTypeName = definition.Separate ? definition.Type.ToGlslTextureType() : definition.Type.ToGlslSamplerType(); 355 356 string layout = string.Empty; 357 358 if (context.TargetApi == TargetApi.Vulkan) 359 { 360 layout = $", set = {definition.Set}"; 361 } 362 363 context.AppendLine($"layout (binding = {definition.Binding}{layout}) uniform {samplerTypeName} {definition.Name}{arrayDecl};"); 364 } 365 } 366 367 private static void DeclareImages(CodeGenContext context, IEnumerable<TextureDefinition> definitions) 368 { 369 foreach (var definition in definitions) 370 { 371 string arrayDecl = string.Empty; 372 373 if (definition.ArrayLength > 1) 374 { 375 arrayDecl = $"[{NumberFormatter.FormatInt(definition.ArrayLength)}]"; 376 } 377 else if (definition.ArrayLength == 0) 378 { 379 arrayDecl = "[]"; 380 } 381 382 string imageTypeName = definition.Type.ToGlslImageType(definition.Format.GetComponentType()); 383 384 if (definition.Flags.HasFlag(TextureUsageFlags.ImageCoherent)) 385 { 386 imageTypeName = "coherent " + imageTypeName; 387 } 388 389 string layout = definition.Format.ToGlslFormat(); 390 391 if (!string.IsNullOrEmpty(layout)) 392 { 393 layout = ", " + layout; 394 } 395 396 if (context.TargetApi == TargetApi.Vulkan) 397 { 398 layout = $", set = {definition.Set}{layout}"; 399 } 400 401 context.AppendLine($"layout (binding = {definition.Binding}{layout}) uniform {imageTypeName} {definition.Name}{arrayDecl};"); 402 } 403 } 404 405 private static void DeclareInputAttributes(CodeGenContext context, IEnumerable<IoDefinition> inputs) 406 { 407 if (context.Definitions.IaIndexing) 408 { 409 string suffix = context.Definitions.Stage == ShaderStage.Geometry ? "[]" : string.Empty; 410 411 context.AppendLine($"layout (location = 0) in vec4 {DefaultNames.IAttributePrefix}{suffix}[{Constants.MaxAttributes}];"); 412 context.AppendLine(); 413 } 414 else 415 { 416 foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) 417 { 418 DeclareInputAttribute(context, ioDefinition.Location, ioDefinition.Component); 419 } 420 421 if (inputs.Any()) 422 { 423 context.AppendLine(); 424 } 425 } 426 } 427 428 private static void DeclareInputAttributesPerPatch(CodeGenContext context, IEnumerable<IoDefinition> inputs) 429 { 430 foreach (var ioDefinition in inputs.OrderBy(x => x.Location)) 431 { 432 DeclareInputAttributePerPatch(context, ioDefinition.Location); 433 } 434 435 if (inputs.Any()) 436 { 437 context.AppendLine(); 438 } 439 } 440 441 private static void DeclareInputAttribute(CodeGenContext context, int location, int component) 442 { 443 string suffix = IsArrayAttributeGlsl(context.Definitions.Stage, isOutAttr: false) ? "[]" : string.Empty; 444 string iq = string.Empty; 445 446 if (context.Definitions.Stage == ShaderStage.Fragment) 447 { 448 iq = context.Definitions.ImapTypes[location].GetFirstUsedType() switch 449 { 450 PixelImap.Constant => "flat ", 451 PixelImap.ScreenLinear => "noperspective ", 452 _ => string.Empty, 453 }; 454 } 455 456 string name = $"{DefaultNames.IAttributePrefix}{location}"; 457 458 if (context.Definitions.TransformFeedbackEnabled && context.Definitions.Stage == ShaderStage.Fragment) 459 { 460 bool hasComponent = context.Definitions.HasPerLocationInputOrOutputComponent(IoVariable.UserDefined, location, component, isOutput: false); 461 462 if (hasComponent) 463 { 464 char swzMask = "xyzw"[component]; 465 466 context.AppendLine($"layout (location = {location}, component = {component}) {iq}in float {name}_{swzMask}{suffix};"); 467 } 468 else 469 { 470 int components = context.Definitions.GetTransformFeedbackOutputComponents(location, 0); 471 472 string type = components switch 473 { 474 2 => "vec2", 475 3 => "vec3", 476 4 => "vec4", 477 _ => "float", 478 }; 479 480 context.AppendLine($"layout (location = {location}) in {type} {name};"); 481 } 482 } 483 else 484 { 485 bool passthrough = (context.AttributeUsage.PassthroughAttributes & (1 << location)) != 0; 486 string pass = passthrough && context.HostCapabilities.SupportsGeometryShaderPassthrough ? "passthrough, " : string.Empty; 487 string type = GetVarTypeName(context, context.Definitions.GetUserDefinedType(location, isOutput: false), false); 488 489 context.AppendLine($"layout ({pass}location = {location}) {iq}in {type} {name}{suffix};"); 490 } 491 } 492 493 private static void DeclareInputAttributePerPatch(CodeGenContext context, int patchLocation) 494 { 495 int location = context.AttributeUsage.GetPerPatchAttributeLocation(patchLocation); 496 string name = $"{DefaultNames.PerPatchAttributePrefix}{patchLocation}"; 497 498 context.AppendLine($"layout (location = {location}) patch in vec4 {name};"); 499 } 500 501 private static void DeclareOutputAttributes(CodeGenContext context, IEnumerable<IoDefinition> outputs) 502 { 503 if (context.Definitions.OaIndexing) 504 { 505 context.AppendLine($"layout (location = 0) out vec4 {DefaultNames.OAttributePrefix}[{Constants.MaxAttributes}];"); 506 context.AppendLine(); 507 } 508 else 509 { 510 outputs = outputs.OrderBy(x => x.Location); 511 512 if (context.Definitions.Stage == ShaderStage.Fragment && context.Definitions.DualSourceBlend) 513 { 514 IoDefinition firstOutput = outputs.ElementAtOrDefault(0); 515 IoDefinition secondOutput = outputs.ElementAtOrDefault(1); 516 517 if (firstOutput.Location + 1 == secondOutput.Location) 518 { 519 DeclareOutputDualSourceBlendAttribute(context, firstOutput.Location); 520 outputs = outputs.Skip(2); 521 } 522 } 523 524 foreach (var ioDefinition in outputs) 525 { 526 DeclareOutputAttribute(context, ioDefinition.Location, ioDefinition.Component); 527 } 528 529 if (outputs.Any()) 530 { 531 context.AppendLine(); 532 } 533 } 534 } 535 536 private static void DeclareOutputAttribute(CodeGenContext context, int location, int component) 537 { 538 string suffix = IsArrayAttributeGlsl(context.Definitions.Stage, isOutAttr: true) ? "[]" : string.Empty; 539 string name = $"{DefaultNames.OAttributePrefix}{location}{suffix}"; 540 541 if (context.Definitions.TransformFeedbackEnabled && context.Definitions.LastInVertexPipeline) 542 { 543 bool hasComponent = context.Definitions.HasPerLocationInputOrOutputComponent(IoVariable.UserDefined, location, component, isOutput: true); 544 545 if (hasComponent) 546 { 547 char swzMask = "xyzw"[component]; 548 549 string xfb = string.Empty; 550 551 var tfOutput = context.Definitions.GetTransformFeedbackOutput(location, component); 552 if (tfOutput.Valid) 553 { 554 xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}"; 555 } 556 557 context.AppendLine($"layout (location = {location}, component = {component}{xfb}) out float {name}_{swzMask};"); 558 } 559 else 560 { 561 int components = context.Definitions.GetTransformFeedbackOutputComponents(location, 0); 562 563 string type = components switch 564 { 565 2 => "vec2", 566 3 => "vec3", 567 4 => "vec4", 568 _ => "float", 569 }; 570 571 string xfb = string.Empty; 572 573 var tfOutput = context.Definitions.GetTransformFeedbackOutput(location, 0); 574 if (tfOutput.Valid) 575 { 576 xfb = $", xfb_buffer = {tfOutput.Buffer}, xfb_offset = {tfOutput.Offset}, xfb_stride = {tfOutput.Stride}"; 577 } 578 579 context.AppendLine($"layout (location = {location}{xfb}) out {type} {name};"); 580 } 581 } 582 else 583 { 584 string type = context.Definitions.Stage != ShaderStage.Fragment ? "vec4" : 585 GetVarTypeName(context, context.Definitions.GetFragmentOutputColorType(location), false); 586 587 if (context.HostCapabilities.ReducedPrecision && context.Definitions.Stage == ShaderStage.Vertex && location == 0) 588 { 589 context.AppendLine($"layout (location = {location}) invariant out {type} {name};"); 590 } 591 else 592 { 593 context.AppendLine($"layout (location = {location}) out {type} {name};"); 594 } 595 } 596 } 597 598 private static void DeclareOutputDualSourceBlendAttribute(CodeGenContext context, int location) 599 { 600 string name = $"{DefaultNames.OAttributePrefix}{location}"; 601 string name2 = $"{DefaultNames.OAttributePrefix}{(location + 1)}"; 602 603 context.AppendLine($"layout (location = {location}, index = 0) out vec4 {name};"); 604 context.AppendLine($"layout (location = {location}, index = 1) out vec4 {name2};"); 605 } 606 607 private static void DeclareOutputAttributesPerPatch(CodeGenContext context, IEnumerable<IoDefinition> outputs) 608 { 609 foreach (var ioDefinition in outputs) 610 { 611 DeclareOutputAttributePerPatch(context, ioDefinition.Location); 612 } 613 614 if (outputs.Any()) 615 { 616 context.AppendLine(); 617 } 618 } 619 620 private static void DeclareOutputAttributePerPatch(CodeGenContext context, int patchLocation) 621 { 622 int location = context.AttributeUsage.GetPerPatchAttributeLocation(patchLocation); 623 string name = $"{DefaultNames.PerPatchAttributePrefix}{patchLocation}"; 624 625 context.AppendLine($"layout (location = {location}) patch out vec4 {name};"); 626 } 627 628 private static bool IsArrayAttributeGlsl(ShaderStage stage, bool isOutAttr) 629 { 630 if (isOutAttr) 631 { 632 return stage == ShaderStage.TessellationControl; 633 } 634 else 635 { 636 return stage == ShaderStage.TessellationControl || 637 stage == ShaderStage.TessellationEvaluation || 638 stage == ShaderStage.Geometry; 639 } 640 } 641 642 private static void AppendHelperFunction(CodeGenContext context, string filename) 643 { 644 string code = EmbeddedResources.ReadAllText(filename); 645 646 code = code.Replace("\t", CodeGenContext.Tab); 647 648 if (context.HostCapabilities.SupportsShaderBallot) 649 { 650 code = code.Replace("$SUBGROUP_INVOCATION$", "gl_SubGroupInvocationARB"); 651 code = code.Replace("$SUBGROUP_BROADCAST$", "readInvocationARB"); 652 } 653 else 654 { 655 code = code.Replace("$SUBGROUP_INVOCATION$", "gl_SubgroupInvocationID"); 656 code = code.Replace("$SUBGROUP_BROADCAST$", "subgroupBroadcast"); 657 } 658 659 context.AppendLine(code); 660 context.AppendLine(); 661 } 662 } 663 }