Что происходит, когда вы делите на 0 в шейдере? - PullRequest
9 голосов
/ 27 апреля 2011

Известно, что ветвление особенно затратно в вычислительном отношении в шейдере OpenGL ES.В таком шейдере я проверяю значение null перед делением на него, например:

if(value == 0.0)
    other_value = 0.0;
else
    other_value = 1.0 / value;

Чтобы ускорить процесс, я бы хотел избежать этого if, выполнив непосредственно:

other_value = 1.0 / value;

Интересно, что случится, если value окажется равным 0, что довольно редко встречается в моем лечении, поэтому проверить его не так просто.Вылетает ли шейдер?Вылетает ли приложение?

Ответы [ 6 ]

14 голосов
/ 05 апреля 2013

Не определено действительно.

В симуляторе iPad при делении красного компонента на 0,0 выдается 0 (все красные исчезли из этого рисунка).

enter image description here

На оборудовании iPad, делениеКомпонент r на 0.0 насыщает его до полного красного цвета (т. е. + бесконечность ограничена).

enter image description here

Так что нет, вы не можете вообще полагаться на неопределенное поведение для получения последовательного результата, но ничего плохого не происходит, если разделить на 0 само по себе.

В моем шейдере я делю значения rgb по альфе.Так уж получилось, что если значение альфа равно 0, пиксель все равно не отображается, поэтому у меня нет проблем с делением в любом случае (давая 0 или + inf).

4 голосов
/ 17 июля 2012

Такие филиалы, как правило, не дорогие.Они обычно реализуются с использованием предикации.Таким образом, графический процессор вычисляет обе ветви, но только фактически сохраняет результаты инструкций, где условие предикации является истинным.Следовательно, инструкция перехода не используется.Вот как может выглядеть код ассемблера:

cmp_eq p0, r0, 0.0   // predicate = (value == 0.0)
(p0) mov r1, 0.0     // other_value = 0.0, if predicate true
(!p0) rcp r1, r0     // other_value = 1.0 / value, if predicate false

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

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

2 голосов
/ 28 июня 2016

Вместо

float invert_value(in float value)
{
if(value == 0.0)
    return 0.0;
else
    return 1.0 / value;
}

вы можете написать

float invert_value_ifless(in float value)
{
float sign_value = sign(value);
float sign_value_squared = sign_value*sign_value;
return sign_value_squared / ( value + sign_value_squared - 1.0);
}

Это возвращает именно то, что вы хотите, и имеет

  1. нет ifs
  2. без делений на 0

Я не уверен, что это действительно быстрее на недавнем оборудовании с предикацией.

2 голосов
/ 29 апреля 2011

Я никогда не видел падения шейдера от чего-то подобного. Это, скорее всего, приведет к значению мусора (например, nan) и испортит любые другие вычисления, которые вы выполняете с результатом. Кроме того, я бы не беспокоился об этом (и определенно не добавил бы код ветвления, чтобы предотвратить это).

2 голосов
/ 27 апреля 2011

Если вы хотите ограничиться iPhone, почему бы вам не попробовать и посмотреть, что происходит?Однако нет никаких гарантий относительно того, что произойдет с будущим оборудованием, на котором будут работать приложения для iPhone.Это может привести к сбою.Это ничего не могло сделать.Это может отображать странные пиксели.Это может позвонить твоей свекрови.(Все, так как это неопределенное поведение.)

1 голос
/ 25 июля 2012

на старом графическом оборудовании деление на ноль приведет к значению бесконечности с плавающей точкой, что на самом деле является правильным ответом. однако вряд ли это то, что вам нужно, потому что вы, вероятно, просто сохраните бесконечность (или что-то из нее полученное) в буфере кадров, который, вероятно, является форматом, подобным RGBA8, который не способен отличить бесконечность от 0 или 1.

на более новом настольном оборудовании, возможно иметь буфер кадров с плавающей точкой, и в этом случае бесконечность - это действительная вещь для хранения там - но все равно вряд ли будет то, что вы хотите, если только кунг-фу с плавающей точкой не достаточно продвинутый.

...