GLSL的预处理器都有哪些规定?
下面的内容,英文版取自GLSLangSpec.4.60.pdf,中文版是我的翻译,只求意译准确易懂,不求直译严格匹配。
3.3. Preprocessor
There is a preprocessor that processes the source strings as part of the compilation process. Except as noted below, it behaves as the C++ standard preprocessor (see "Normative References").
有一个预处理器preprocessor,它参与GLSL代码的编译过程。除下文所述外,此preprocessor与C++预处理器的规定相同。
The complete list of preprocessor directives is as follows.
preprocessor指令的完整列表如下:
glsl
#
#define
#undef
#if
#ifdef
#ifndef
#else
#elif
#endif
#error
#pragma
#extension
#version
#line
Each number sign (#) can be preceded in its line only by spaces or horizontal tabs. It may also be followed by spaces and horizontal tabs, preceding the directive. Each directive is terminated by a new-line. Preprocessing does not change the number or relative location of new-lines in a source string. Preprocessing takes places after new-lines have been removed by the line-continuation character.
在一行里,指令前面允许出现的字符只有若干' '
或'\t'
,不能有其他字符。指令后面也可以跟着若干' '
或'\t'
字符。每个指令都以换行符(\r\n
或\n
)标志其结束(也就是说,一个指令只能出现在同一行里)。预处理不改变源代码中各个行的相对位置(也就是说,预处理不会增删源代码中的换行符(\r\n
或\n
))。如果有行继续符(line-continuation character)(例如'\'
),那么预处理器会先去掉换行符(即先将'\'
前后2行合并为1行),再进行预处理。GLSL并没有规定行继续符(line-continuation character)必须是哪个字符,这由宿主环境决定。
The number sign (#) on a line by itself is ignored. Any directive not listed above will cause a compile-time error.
如果1行里只有1个'#'
字符,它就会被忽略。如果GLSL源代码中出现了上述列表之外的指令,编译器就会报错。
#define and #undef functionality are defined as is standard for C++ preprocessors for macro definitions both with and without macro parameters.
#define
和#undef
的功能与C++预处理器完全相同。
The following operators are also available:
除了指令,还有下述操作符/运算符:
glsl
defined
##
The following predefined macros are available:
除了指令,还有下述由编译器预先定义的宏(macro):
glsl
__LINE__
__FILE__
__VERSION__
LINE will substitute a decimal integer constant that is one more than the number of preceding new-lines in the current source string.
__LINE__
表示其所在源代码的第几行(从1开始)。
FILE will substitute a decimal integer constant that says which source string number is currently being processed.
__FILE__
表示其所在源代码的序号。(我以为它是glShaderSource(GLuint shader, GLsizei count, string[] codes, GLint[] length)
里所在codes
中的序号,但试验证明不是,所有的__FILE__
都是0)
VERSION will substitute a decimal integer reflecting the version number of the OpenGL Shading Language. The version of the shading language described in this document will have VERSION substitute the decimal integer 460.
__VERSION__
表示GLSL版本号。本文档(GLSLangSpec.4.60.pdf)描述的GLSL的版本号为十进制整数460
。
By convention, all macro names containing two consecutive underscores (__) are reserved for use by underlying software layers. Defining or undefining such a name in a shader does not itself result in an error, but may result in unintended behaviors that stem from having multiple definitions of the same name. All macro names prefixed with "GL_" ("GL" followed by a single underscore) are also reserved, and defining or undefining such a name results in a compile-time error.
按惯例,所有包含__
的宏(macro)名称,都是被保留给底层软件用的。在GLSL中定义或取消定义这样的宏(macro)名称,本身不会导致error,但重复定义相同的宏(macro)名称可能导致意料之外的情况。所有以GL_
开头的宏(macro)名称也是被保留的,如果在GLSL中定义或取消定义这样的宏(macro)名称,就会导致error。
Implementations must support macro-name lengths of up to 1024 characters. Implementations are allowed to generate an error for a macro name of length greater than 1024 characters, but are also allowed to support lengths greater than 1024.
实现(GLSL编译器)必须支持至少1024个字符的宏(macro)名称。实现(GLSL编译器)可以对超过1024个字符的宏(macro)名称生成error,也可以支持超过1024个字符的宏(macro)名称。
#if, #ifdef, #ifndef, #else, #elif, and #endif are defined to operate as is standard for C++ preprocessors except for the following:
除下述内容外,#if
、#ifdef
、#ifndef
、#else
、#elif
、#endif
与C++预处理器完全相同:
-
Expressions following #if and #elif are further restricted to expressions operating on literal integer constants, plus identifiers consumed by the defined operator.
-
#if
、#elif
后跟的表达式范围更小:只支持字面整数常量和用于defined
操作符的标识符(identifier)。 -
Character constants are not supported.
-
不支持字符常量。
The operators available are as follows.
可用的操作符如下表所示:
Precedence 优先级 | Operator class 类型 | Operators 操作符 | Associativity 关联性 |
---|---|---|---|
1 (highest) | parenthetical grouping | ( ) | NA |
2 | unary | defined + - ~ ! | Right to Left |
3 | multiplicative | * / % | Left to Right |
4 | additive | + - | Left to Right |
5 | bit-wise shift | << >> | Left to Right |
6 | relational | < > <= >= | Left to Right |
7 | equality | == != | Left to Right |
8 | bit-wise and | & | Left to Right |
9 | bit-wise exclusive or | ^ | Left to Right |
10 | bit-wise inclusive or | | | Left to Right |
11 | logical and | && | Left to Right |
12 (lowest) | logical inclusive or | || | Left to Right |
The defined
operator can be used in either of the following ways:
操作符defined
可按下述两种方式使用:
glsl
defined identifier
defined ( identifier )
Two tokens in a macro can be concatenated into one token using the token pasting (##) operator, as is standard for C++ preprocessors. The result must be a valid single token, which will then be subject to macro expansion. That is, macro expansion happens only after token pasting. There are no other number sign based operators (e.g. no # or #@), nor is there a sizeof operator.
如果使用token粘接操作符##
,一个宏(macro)里的两个token可以被连接成一个token,这与C++预处理器相同。粘接结果必须是一个有效的token,这就由宏(macro)的展开情况决定了。也就是说,宏(macro)的展开发生于token粘接之后。不存在其他基于#
的操作符(例如,没有#
,没有#@
),也不存在sizeof
操作符。
The semantics of applying operators to integer literals in the preprocessor match those standard in the C++ preprocessor, not those in the OpenGL Shading Language.
预处理器中对字面整数的计算结果,与C++预处理器相同,而未必与GLSL相同。
Preprocessor expressions will be evaluated according to the behavior of the host processor, not the processor targeted by the shader.
预处理器中的表达式由CPU计算,而不是由GPU计算。
#error will cause the implementation to put a compile-time diagnostic message into the shader object's information log (see section 7.12 "Shader, Program and Program Pipeline Queries" of the OpenGL Specification for how to access a shader object's information log). The message will be the tokens following the #error directive, up to the first new-line. The implementation must treat the presence of a #error directive as a compile-time error.
#error
会向shader对象写入一条log(我猜就是用glGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei[] length, string infoLog);
得到的infoLog
)。此log的内容就是#error "xxxxxxx"
指令中的xxxxxxx
。实现(GLSL编译器)必须将遇到#error
指令视为发生了编译时error。
#pragma allows implementation-dependent compiler control. Tokens following #pragma are not subject to preprocessor macro expansion. If an implementation does not recognize the tokens following #pragma, then it will ignore that pragma.
各个实现(GLSL编译器)可以用#pragma
搞各自的控制。#pragma
后跟的token不参与宏(macro)展开。如果某个实现(GLSL编译器)未能识别#pragma
后跟的token,那么这个实现就会无视此#pragma
。
The following pragmas are defined as part of the language.
GLSL内置了下列#pragma
:
glsl
#pragma STDGL
The STDGL pragma is used to reserve pragmas for use by future revisions of this language. No implementation may use a pragma whose first token is STDGL.
这个#pragma STDGL
被保留下来,是为了用于GLSL的未来版本。任何实现(GLSL编译器)均不得使用第一个token为STDGL
的#pragma
。
glsl
#pragma optimize(on)
#pragma optimize(off)
can be used to turn off optimizations as an aid in developing and debugging shaders. It can only be used outside function definitions. By default, optimization is turned on for all shaders.
这个optimize用于开关shader的优化选项。关闭优化,有助于开发和调试shader。它只能写到函数定义的外面。optimize的默认状态是对所有shader都开启(on)的。
glsl
#pragma debug(on)
#pragma debug(off)
The debug pragma can be used to enable compiling and annotating a shader with debug information, so that it can be used with a debugger. It can only be used outside function definitions. By default, debug is turned off.
这个debug用于开启调试信息。它只能写到函数定义的外面。debug的默认状态是关闭(off)的。
Shaders should declare the version of the language they are written to. The language version a shader is written to is specified by
shader应当声明它们使用的GLSL版本号,方式如下:
glsl
#version number profile_opt
where number must be a version of the language, following the same convention as VERSION above. The directive "#version 460" is required in any shader that uses version 4.60 of the language. Any number representing a version of the language a compiler does not support will cause a compile-time error to be generated. Version 1.10 of the language does not require shaders to include this directive, and shaders that do not include a #version directive will be treated as targeting version 1.10. Shaders that specify #version 100 will be treated as targeting version 1.00 of the OpenGL ES Shading Language. Shaders that specify #version 300 will be treated as targeting version 3.00 of the OpenGL ES Shading Language. Shaders that specify #version 310 will be treated as targeting version 3.10 of the OpenGL ES Shading Language.
其中,number
必须是GLSL的一个版本号,与__VERSION__
遵循同样的规则。使用GLSL 4.60版的shader必须写上#version 460
指令。如果编译器发现了不支持的GLSL版本号,就会报error。GLSL 1.10版不要求shader写此指令;没有写此指令的shader会被视为面向GLSL 1.10版。写了#version 100
的shader会被视为面向openGL ES着色语言的1.00版。写了#version 300
的shader会被视为面向openGL ES着色语言的3.00版。写了#version 310
的shader会被视为面向openGL ES着色语言的3.10版。
If the optional profile argument is provided, it must be the name of an OpenGL profile. Currently, there are three choices:
如果要写可选的模式(profile)参数,那只能是一个openGL模式名称。目前,有3个模式可选:
glsl
core
compatibility
es
A profile argument can only be used with version 150 or greater. If no profile argument is provided and the version is 150 or greater, the default is core. If version 300 or 310 is specified, the profile argument is not optional and must be es, or a compile-time error results. The Language Specification for the es profile is specified in The OpenGL ES Shading Language specification.
模式(profile)参数只能用在150
或更高版本。如果没有写模式(profile)参数且版本是150
或更高,那就是默认使用core
模式。如果指定版本为300
或310
,那么模式(profile)参数就不能省略,且必须是es
,否则就会报编译时error。es
模式的语言说明书(Specification)详见OpenGL ES Shading Language specification。
Shaders for the core or compatibility profiles that declare different versions can be linked together. However, es profile shaders cannot be linked with non-es profile shaders or with es profile shaders of a different version, or a link-time error will result. When linking shaders of versions allowed by these rules, remaining link-time errors will be given as per the linking rules in the GLSL version corresponding to the version of the context the shaders are linked under. Shader compile-time errors must still be given strictly based on the version declared (or defaulted to) within each shader.
声明不同版本的core
或compatibility
模式的shader,可以被链接(link)起来。但是,es
模式的shader不能与非es
模式的shader链接(link),也不能与不同版本的es
模式的shader链接(link),否则就会报链接时error。当根据上述规则链接(link)符合版本要求的shader时,其他的链接时error将依据(shader所处上下文版本对应的GLSL版本的链接规则)进行处理。而shader的编译时error仍须严格按照每个shader内部声明的(或默认的)版本进行判定。
Unless otherwise specified, this specification is documenting the core profile, and everything specified for the core profile is also available in the compatibility profile. Features specified as belonging specifically to the compatibility profile are not available in the core profile. Compatibility-profile features are not available when generating SPIR-V.
若无其他声明,那么本文就是在介绍core
模式下的情况,core
模式下的情况,在compatibility
模式下也适用。但只适用于在compatibility
模式下的情况,在core
模式下就不适用了。如果要生成SPIR-V
,那么compatibility
模式的特性就不可用了。
There is a built-in macro definition for each profile the implementation supports. All implementations provide the following macro:
每个模式都有一个内置的宏(macro)定义。所有的实现(GLSL编译器)都提供下述宏(macro):
glsl
#define GL_core_profile 1
Implementations providing the compatibility profile provide the following macro:
支持compatibility
模式的实现(GLSL编译器),提供下述宏(macro):
glsl
#define GL_compatibility_profile 1
Implementations providing the es profile provide the following macro:
支持es
模式的实现(GLSL编译器),提供下述宏(macro):
glsl
#define GL_es_profile 1
The #version directive must occur in a shader before anything else, except for comments and white space.
在一个shader里,在#version
指令之前不能出现任何内容,除了注释和空白符(' '
、'\t'
)。
By default, compilers of this language must issue compile-time syntactic, semantic, and grammatical errors for shaders that do not conform to this specification. Any extended behavior must first be enabled. Directives to control the behavior of the compiler with respect to extensions are declared with the #extension directive
默认情况下,GLSL编译器必须对shader中不符合本说明书的情况报编译时的语法/语义/文法error。任何扩展行为必须先被启用(enable)才能用。#extension
指令的作用就是,控制编译器在扩展(extension)方面的行为。
glsl
#extension extension_name : behavior
#extension all : behavior
where extension_name is the name of an extension. Extension names are not documented in this specification. The token all means the behavior applies to all extensions supported by the compiler.
其中,extension_name
是扩展的名字,本说明书不介绍扩展的名字的详情。all
这个token的意思是,指定的行为(behavior)要作用于编译器支持的全部扩展。
The behavior can be one of the following:
可选的行为(behavior)如下表所示:
Behavior 行为 | Effect 效果 |
---|---|
require | Behave as specified by the extension extension_name. Give a compile-time error on the #extension if the extension extension_name is not supported, or if all is specified. 按照extension_name 的规定执行。 如果extension_name 不被支持 ,或指定了all ,则对#extension 报error。 |
enable | Behave as specified by the extension extension_name. Warn on the #extension if the extension extension_name is not supported. Give a compile-time error on the #extension if all is specified. 按照extension_name 的规定执行。 如果extension_name 不被支持,则对#extension 报warn 。 如果指定了all ,则对#extension 报error。 |
warn | Behave as specified by the extension extension_name, except issue warnings on any detectable use of that extension, unless such use is supported by other enabled or required extensions. If all is specified, then warn on all detectable uses of any extension used. Warn on the #extension if the extension extension_name is not supported. 按照extension_name 的规定执行,但对该扩展的使用报warn ------除非这个使用被其他enable /require 的扩展支持。 如果指定了all ,则对所有扩展的使用报warn 。 如果extension_name 不被支持,则对#extension 本身报warn。 |
disable | Behave (including issuing errors and warnings) as if the extension extension_name is not part of the language definition. If all is specified, then behavior must revert back to that of the non-extended core version of the language being compiled to. Warn on the #extension if the extension extension_name is not supported. 视同extension_name 不存在。 如果指定了all ,则视同使用不存在任何扩展的core 版本。 如果extension_name 不被支持,则对#extension 本身报warn。 |
The extension directive is a simple, low-level mechanism to set the behavior for each extension. It does not define policies such as which combinations are appropriate, those must be defined elsewhere. Order of directives matters in setting the behavior for each extension: Directives that occur later override those seen earlier. The all variant sets the behavior for all extensions, overriding all previously issued extension directives, but only for the behaviors warn and disable.
#extension
指令用于设置每个extension的行为,是一个简单且底层的机制。它不定义"哪个组合是恰当的"等策略(这得在其他地方定义)。各个#extension
指令的先后顺序是有意义的:后出现的#extension
会覆盖先出现的#extension
。#extension all : warn
和#extension all : disable
会为所有扩展设置行为,覆盖所有先前的#extension
指令。
The initial state of the compiler is as if the directive
编译器的初始状态就如同写了如下指令:
glsl
#extension all : disable
was issued, telling the compiler that all error and warning reporting must be done according to this specification, ignoring any extensions.
即,告诉编译器,要求所有错误和警告的报告必须遵循此说明书,并忽略所有扩展功能。
Each extension can define its allowed granularity of scope. If nothing is said, the granularity is a shader (that is, a single compilation unit), and the extension directives must occur before any nonpreprocessor tokens. If necessary, the linker can enforce granularities larger than a single compilation unit, in which case each involved shader will have to contain the necessary extension directive.
每个extension可以定义其允许的作用域粒度。如果什么都没说,粒度就是单个shader(即单个编译单元),且extension指令必须出现在所有非预处理器tokens之前。必要时,链接器可强制采用比单编译单元更大的粒度,此时每个相关的shader均须包含必要的extension指令。
Macro expansion is not done on lines containing #extension and #version directives.
在包含#extension
或#version
指令的代码行上,不会执行宏(macro)展开。
#line must have, after macro substitution, one of the following forms:
#line
指令,在宏(macro)替换后,必须符合下列形式之一:
glsl
#line line
#line line source-string-number
where line and source-string-number are constant integer expressions. If these constant expressions are not integer literals then behavior is undefined. After processing this directive (including its new-line), the implementation will behave as if it is compiling at line number line and source string number source-string-number. Subsequent source strings will be numbered sequentially, until another #line directive overrides that numbering.
其中,line和source-string-number是整型常量表达式,否则其行为就是未定义的,后果是不可测的。处理了这一指令(及换行符\r\n
)后,实现(GLSL编译器)将视自己在第line
行和第source-string-number
个源代码字符串上。后序源代码字符串将被依次编号,直到被下一个#line
指令覆盖之。
Note
注意
Some implementations have allowed constant expressions in #line directives and some have not. Even where expressions are supported the grammar is ambiguous and so results are implementation dependent. For example, + #line +2 +2 // Line number set to 4, or file to 2 and line to 2
某些实现(GLSL编译器)允许在
#line
指令中使用常量表达式,而另一些则不允许。即使支持表达式,由于语法规则存在歧义,最终结果仍可能因实现而异。例如:#line +2 +2 // 可能被解析为行号设为4,或文件编号为2且行号为2
When shaders are compiled for OpenGL SPIR-V, the following predefined macro is available:
当shader为 OpenGL SPIR-V 编译时,以下预定义的宏(macro)可用:
glsl
#define GL_SPIRV 100
When targeting Vulkan, the following predefined macro is available:
当目标平台为 Vulkan 时,以下预定义的宏(macro)可用:
glsl
#define VULKAN 100