Препроцессор

Имеется препроцессор, который обрабатывает исходные строки как часть процесса компиляции. За исключением случаев, указанных ниже, они ведут себя как стандартный препроцессор С++ (раздел 10 «Нормативные ссылки»).

Полный список директив препроцессора выглядит следующим образом.

#
#define
#undef
#if
#ifdef
#ifndef
#else
#elif
#endif
#error
#pragma
#extension
#version
#line

Так же доступны следующие операторы

defined
##

Каждому знаку (#) могут предшествовать только пробелы или горизонтальные табуляции, имеется в виду в пределах линии. Так же после него и перед директивой могут быть пробелы и горизонтальные табуляции. Директива заканчивается с началом новой линии. Предварительная обработка не изменяет количество или взаимное расположение линий в исходной строке. Предварительная обработка занимает места после того, как новые линии были удалены по символу продолжения строки.

Знак (#) на отдельной линии игнорируется. Любая директива, отсутствующая в  списке выше, будет вызывать диагностическое сообщение и приводит к плохой или не верной трактовке шейдера.

#define и #undef обладают аналогичным функционалом, что и стандартный препроцессор С++, макроопределение с и без макро параметров.

Доступны следующие предопределенные макросы:

__LINE__
__FILE__
__VERSION__

__LINE__ заменит десятеричную целочисленную константу, которая на единицу больше, чем предыдущих новых линий в текущей строке источнике.

__FILE__ заменит десятеричную целочисленную константу, которая говорит, какой номер строки источника обрабатывается в данный момент.

__VERSION__ заменит десятеричное целочисленное, отражающее номер версии OpenGL shading language. Версия языка шейдеров в данном документе будет равна 450.

По соглашению, все имена макросов, содержащих два последовательных символа подчеркивания (__) зарезервированы для использования системой. Определение такого имени в шейдере не приведет к ошибке, но может привести к неопределенному поведению, вытекающему из нескольких определений одного имени. Все имена с префиксом «GL_» («GL» с последующим однократным подчеркиванием) так же зарезервированы и определение такого имени приведет к ошибке времени компиляции.

#if, #ifdef, #ifndef, #else, #elif, и #endif определяются как стандартные для препроцессора С++. Выражения после #if и #elif так же ограничены работой с целочисленными константами, плюс идентификаторами, определяемыми оператором defined. Доступны следующие операторы.

Приоритет Класс оператора Операторы Ассоциативность
1 (высший) Скобочная группировка  ( ) NA
2 Унарные  defined
+ — ~ !
Right to Left
3 Мультипликативные  * / % Left to Right
4 Добавление  + — Left to Right
5 Побитовое смещение  << >> Left to Right
6 Реляционные  < > <= >= Left to Right
7 Равенство  == != Left to Right
8 Побитовое И  & Left to Right
9 Побитовое исключающее ИЛИ  ^ Left to Right
10 Побитовое включающее ИЛИ  | Left to Right
11 Логическое И  && Left to Right
12 (нисший) Логичесткое включающее ИЛИ  || Left to Right

Оператор defined может быть использован одним из следующим способов:

defined identifier
defined ( identifier )

Два элемента в макросе могут быть объединены в один используя оператор склеивания (##), как и в препроцессоре С++. Результат должен быть допустимым единичным элементом, который будет в дальнейшем подвергаться макроопределению. То есть макроопределение происходит только после склеивания. Там нет других операторов основанных на знаках чисел (например нет # или #@), а так же оператора SizeOf.

Семантика применения операторов к целочисленным в препроцессоре повторяет таковое стандарта в препроцессоре С++.

Препроцессорные выражения будут оцениваться в соответствии с поведением основного процессора, а не процессора, на который нацелен шейдер.

#error поместит диагностическое сообщение в журнал информации объекта шейдера во время компиляции (как получить информацию из журнала смотрите 7.12 «Shader and Program Queries” в OpenGL Graphics System Specification). Сообщение будет содержать информацию, следующую за #error и вплоть до новой линии. В дальнейшем шейдер будет рассмотрен как плохо сформированный.

#pragma позволяет реализовать зависящее от компилятора управление. Выражения, следующие за #pragma не учитываются препроцессорными макрорасширениями. Если реализация не распознает следующее за #pragma, то он будет это просто игнорировать. Следующие примера определяются как часть языка:

#pragma STDGL

STDGL зарезервированна для использования в последующих версиях языка.

#pragma optimize(on)
#pragma optimize(off)

может быть использован для отключения оптимизации в качестве вспомогательного средства разработки и отладки шейдеров.  Может быть использовано только вне определений функций. По умолчанию оптимизация включена для всех шейдеров.

#pragma debug(on)
#pragma debug(off)

может быть использован для включения отладочной информации во время компиляции и аннотирования, так, что он может быть использован отладчиком. Может быть использовано только вне определений функций. По умолчанию, отладка выключена.

Шейдеры должны объявлять версию языка, на которой они написаны.

#version number profile

где number должен быть версией языка следуя тем же правилам, что и __VERSION__ ранее. “#version 450” требуется в любом шейдере, который использует версию языка 4.50 . Любое число, представляющее собой версию языка и не поддерживаемое компилятором, приведет к ошибке времени компиляции. Версия языка 1.10 не требует включения этой директивы в шейдеры, и соответственно шейдеры, не имеющие директивы  #version будут рассмотрены как шейдеры версии 1.10 . Шейдеры, которые определяются как  #version 100 будут соответственно рассматриваться как нацеленные на версию 1.00 OpenGL ES Shading Language. Шейдеры, которые определяются как  #version 300 будут соответственно рассматриваться как нацеленные на версию 3.00 OpenGL ES Shading Language. Шейдеры, которые определяются как  #version 310 будут соответственно рассматриваться как нацеленные на версию 3.10 OpenGL ES Shading Language.

Если предусмотрен опциональный аргумент профиля, то он должен быть указан в profile. В настоящее время существует три варианта:

core
compatibility
es

Аргумент профиля может быть использован только с версии 150 и выше. Если ни один аргумент не предусмотрен, а версия 150 или выше, то по умолчанию будет core. Если версия 300 или 310, аргумент профиля не опционален и должен быть es, и будет ошибка времени компиляции. Спецификация для профиля es указана в OpenGL ES Shading Language specification.

Шейдеры для профилей core или compatibility, которые декларированы разными версиями, могут быть связаны друг с другом. Однако, es профили шейдеров не может быть связан с не-es профилями шейдеров другой версии, это приведет к ошибке времени компоновки. При компоновке шейдеров версий, разрешенных данными правилами, оставшиеся ошибки времени компоновки будут даны в соответствии с правилами компоновки в версии GLSL, соответствующей версии вонтекста шейдеров. Ошибки времени компиляции шейдеров будут даны строго в соответствии с версией указанной (либо по умолчанию) в пределах каждого шейдера.

Если не указанно иного, эта спецификация является задокументированным профилем core, и все указанное для профиля core так же доступно в профиле compatibility. Характеристики, указанные как принадлежащие конкретно к профилю compatibility, не доступны в профиле core.

Поддерживается реализация встроенных макро определений для каждого профиля. Все реализации предусматривают следующий макрос:

#define GL_core_profile 1

Реализации профиля compatibility предусматривают следующий макрос:

#define GL_compatibility_profile 1

Реализации профиля es предусматривают следующий макрос:

#define GL_es_profile 1

Директива #version должна быть написана в шейдере в начале, до чего-либо другого, исключениями являются пробелы и комментарии.

По умолчанию, компиляторы данного языка во врмя компиляции должны выдавать лексические и грамматические ошибки в случае, если шейдеры не соответствуют данной спецификации. Любое расширенное поведение (либо более по русски «дополнительные возможности», но оставил дословный перевод extended behavior, столкнемся с ним еще не раз) должно быть сначала включено. Директивы по контролю за поведением компилятора при помощи расширений объявляются директивой #extension.

#extension extension_name : behavior
#extension all : behavior

где extension_name – это имя расширения. Имена расширений н описаны в данной спецификации. all означает, что поведение отностится ко всем расширениям, поддерживаемым компилятором. Список поведений:

Поведение Результат
require (требовать) Поведение определено расшиением extension_name.
Выдает ошибку времени компиляции на #extension, если расширение extension_name не поддерживается, или если указано all.
enable (включить) Поведение определено расшиением extension_name.
Выдает предупреждение на #extension, если расширение extension_name не поддерживается.
Выдает ошибку времени компиляции, если указано all.
warn (предупреждать) Поведение определено расшиением extension_name, за исключением предупрежденией, полученных во время его использования, если только такое использование не поддерживается другими включенными или запрошенными расширениями.
Если все указанно all, то выдает предупреждение на все используемые расширения.
Предупреждение на #extension, если раширение extension_name не поддерживается.
disable (запретить/отключить) Ведет себя (в том числе предупреждения и ошибки), как если расширение extension_name не является описанной частью языка.
Если указано all, то поведение должно вернуться к не расширенной core версии языка для компиляции.
Выдает предупреждение на #extension, если расширние не extension_name поддерживается.

Директива extension является простым низкоуровневым механизмом установки поведения для каждого расширения. Она не определяет общую логику, например какие комбинации являются подходящими для определения в другом месте. Порядок происходящего в окружении для каждого расширения: Директивы выполняемые позже переопределяют рассмотренное ранее. Вариант all определяет поведение для всех расширений, переопределяет все предыдущие директивы extension, но только для поведений warn (предупреждать) и disable (запретить/отключить).

Начальное состояние компилятора аналогично директиве

#extension all : disable

говорящей оператору, что все ошибки и предупреждения должны быть обработаны в соответствии с данной спецификацией, игнорируя любые расширения.

Каждое расширение может определить собственный допустимый уровень детализации в своей области действия. Если ничего не сказано, то детализация это шейдер (т.е. один модуль компиляции), и директивы расширений должны быть объявлены до любой не препроцессорной операции. При необходимости линковщик может обеспечить детализацию большую, чем один элемент компиляции, в этом случае каждый участвующий шейдер будет содержать необходимую директиву расширения.

Макро пространство не заканчивается на линиях, содержащих директивы #extension and #version.

После макро подстановки, #line должен иметь одну из следующих форм:

#line line
#line line source-string-number

где line и source-string-number целочисленное константное выражение. После обработки этой директивы (включая её новую линию), реализация будет вести себя так, как будто она компилирует в линию номер line и строку источника номер source-string-number. Последующие строки источники будут пронумерованы последовательно, пока другая директива не переопределит эту нумерацию.

Main Admin

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *