ifdef for URP?

Is there a shader define for whether URP is enabled, as opposed to standard or HDRP?

Hi!
No, there is no such define. Shader compiler has no idea (and shouldn’t, really), what kind of render pipeline the shader belongs to.
You can have custom defines in shaders themselves, if you want.

1 Like

There are a several definitions declared throughout the shader library such UNIVERSAL_PIPELINE_CORE_INCLUDED and UNIVERSAL_LIGHTING_INCLUDED

The core SRP library has a few as well, such as the installed version.

Right, but these might change, as they are just include file guards, and also depend on order of includes.
So I wouldn’t depend on those.

This is where you would want to use a SubShader for each supported pipeline. You could then define your own.

This does make supporting multiple render pipelines a bit of a nightmare. I’d like to say to unity ‘don’t compile this thing and generate a bunch of errors’ when the environment is using one or another render pipeline.
In my work, I have to support versions of HDRP, URP and BIRP shaders but Unity really doesn’t seem to want me to.

@TLuthDidimo there’s a PackageRequirements block you can use: Unity - Manual: ShaderLab: specifying package requirements

You say they shouldn’t, but here’s an example.

I have a plugin on the asset store which uses ShaderGraph, in order to easily support BiRP, URP, and HDRP. For some reason, a custom shader node works with URP but not with HDRP. How should an asset store developer be able to write a custom function node which handles this? The custom node needs some macro to depend on to know if its compiling for URP or HDRP, to use whatever code is appropriate for whatever pipeline the developer using the plugin is on.

1 Like

This should be done by SG, it already knows which pipeline is the target.

“Which render pipeline does this shader belong to” is definitely not a concept for the shader compiler. All it should care about is “here’s HLSL code with some macros, let’s preprocess and compile it”. There are macros that the compiler should know about, but those are based on things like target platform or graphics API which also affect code generation by the compiler itself.

Well, it’s not. And I wouldnt expect ShaderGraph to handle my custom nodes. Here’s an example of where it fails.

I want a ShaderGraph shader to handle procedural instancing (DrawMeshInstancedIndirect). ShaderGraph doesnt support this out of the box, so I needed to inject the necessary pragmas via a custom node.

This means I need a custom node for the function “foliageSetup()”

This simple function works fine on BiRP and URP, but fails to compile on HDRP. If my asset store plugin wants to seamlessly support all three, what am I supposed to do here? There’s no define for me to check against to do something different for HDRP.

All I’m saying is “SG should define it”. I can forward this to the SG team :slight_smile:

1 Like

Yeah, I can agree with that. Thanks!

I think unity just doesn’t understand what an asset store developer needs and cares even less to address these issues. Let’s debate this for 5 more years, or 10 to get in sync with unity’s timeline. If unity is so afraid that those defines could change and break stuff or whatever the reason for not adding them, why are my shaders breaking from 12.1.6 to 12.1.7. Clearly avoiding breaking things is not one of them.

If you need real-world examples, here is one of my assets, where I need to switch by pipeline and by urp version. Luckily, Amplify provides a Switch by Pipeline node, Switch by SRP Version node, and also defines the SRP version, to get shit done.

In my shaders, I always have a few defines, just in case some 3rd party developer or amplify user wants to filter some things out or even filter the materials in code if needed.

So my question is, why is unity refusing to add those defines when people are asking for them since the beginning?

8610531--1155699--upload_2022-11-24_9-45-0.png

2 Likes

@funkyCoty I forwarded your request :slight_smile:
@BOXOPHOBIC the only system provided by Unity right now that allows you to target different RPs simultaneously is ShaderGraph. This is the only place where such a define would make sense.
I haven’t seen a request to add this define to ShaderGraph (also, I’m not saying there wasn’t one - I just don’t know).
We’ll definitely have a way to add RP-specific things in Block Shaders .

1 Like

Exactly there. We are talking about asset store developers who target multiple pipelines. Imagine a fancy calculation for HDRP, a cheaper one for URP in the same shader. Or some math on HDRP taking camera relative rendering pos into account, but not in URP. We already have a define for this, but if URP adds it as well, maybe I want to make things different for each pipeline. Or whatever the user wants to make different for each pipeline in one shader. You get the idea.

I saw forum posts about shaders defines and scripting defines on this topic. I think made a request for a SwitchByPipeline node for SG on the roadmap, but for some reason, I can’t see my requests. Or at least I think this one was of the requests I made.

@BOXOPHOBIC I fully understand the value of “tell me which pipeline I’m working with” :slight_smile:
More than that, I think it would be simple to add it to SG.

1 Like

@funkyCoty @BOXOPHOBIC looks like there’s an existing workaround

1 Like

The above workaround only works if the Core.hlsl is imported like from SG. From a custom shader I cannot use that to conditionally include Core.hlsl file.

I naively thought that there could be an easy way to detect RP in code/shader/editor/build.
Instead I ended up spending weeks to figure this out just to support Built-in and URP…

Anyway, here is how I finally managed to make it work and if anyone has a better way I will much appreciate it because then I can delete all of this and ship less boilerplate.

1. Making it work from code
This script will automatically detect the pipeline and create a Scripting Define Symbol.

It also HAS to go into Assets/Editor folder, or it will not get compiled before the rest of the code that depends on that define and errors will be thrown when you try to import your code all at once like with a unitypackage.

You can see it was added in URP under Project Settings > Player > Scripting Define Symbols and you can use it from code with

#if UNITY_PIPELINE_URP
#endif

2. Making it work from a shader
This should be added to the shader with the rest of the #pragma code.

#pragma shader_feature UNITY_PIPELINE_URP

It is important to use shader_feature because the pipelines are mutually exclusive.
If you use multi_compile Unity will create all variants and trying to import files from non installed URP package will break the build.

3. Enabling the keyword from code in editor
From a ScriptableRendererFeature script in Create method add

// NOTE: this is not sufficient for build, ShaderPreprocessor script is also needed
var unityPipelineURP = GlobalKeyword.Create("UNITY_PIPELINE_URP");
Shader.EnableKeyword(unityPipelineURP);

That will not work in build but only in the editor because all variants exists in editor and you can switch them but once you build they get stripped.

4. Enabling the keyword from build
This code is only executed on build so it can’t be a single place from where editor materials are also updated.

#if UNITY_EDITOR
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEditor.Rendering;
using UnityEditor.Build;

class ShaderPreprocessor : IPreprocessShaders
{
    public int callbackOrder { get { return 0; } }

    public void OnProcessShader(Shader shader, ShaderSnippetData snippet, IList<ShaderCompilerData> data)
    {
        if (shader.name.Contains("YourShaderName"))
        {
            for (int i = 0; i < data.Count; ++i)
            {
                #if UNITY_PIPELINE_URP
                var keyword = new ShaderKeyword("UNITY_PIPELINE_URP");
                if (!data[i].shaderKeywordSet.IsEnabled(keyword))
                    data[i].shaderKeywordSet.Enable(keyword);
                #endif
            }
        }
    }
}
#endif

Why is setting global keywords not persisted somewhere like with Scriptable Define Symbols or local ones serialized with the material and then skipped from stripping if enabled?

I need to sleep.

3 Likes

This is quite a mess. I don’t get Unity’s argumentation of “you should not know”. What I hear them say is “We don’t care and we have no clue why someone would ever need such”. I am writing an editor mode tooling to import models and I need to do things differently, e.g. calling the converter, depending on the pipeline. The converter is only available in URP packages so if these are not installed code does not compile. I don’t want to set arbitrary compiler symbols in my users project just to have some custom workaround. There should be a stable Unity solution for this that fully supports a C# workflow and not just UIs Unity provides ala SG.

2 Likes

I’ve improved on the detection by using assembly definition and based on the presence of urp library a define symbol can be added conditionally, but that is only a replacement for the Editor folder gist part of my previous post, the rest is still needed.

I’m wondering, how are you calling the converter through code?

1 Like