2016-02-19 6 views
1

Я часть команды, пишущей приложение Android с использованием OpenGL. У нас есть хороший бит шейдерного кода, эмулирующий математику с двойной точностью, используя поплавки. (В частности, мы реализовали алгоритмы в Andrew Thall's Extended-Precision Floating-Point Numbers for GPU Computation.) Он хорошо работает в версии приложения DirectX, но я обнаружил, что на Android компилятор GLSL оптимизирует некоторый код таким образом, чтобы алгебраически поведение должен сохраняться, но на самом деле он меняет поведение, потому что оптимизация выбрасывает ошибку с плавающей запятой. Например, в следующем:Оптимизация компилятора GLSL приводит к неправильному поведению с операциями с плавающей запятой

vec2 add(float a, float b) { 
    float sum = a + b; 
    float err = b - (sum - a); 
    return vec2(sum, err); 
} 

ошибка значение е получает упрощен до 0 компилятора, так как это верно алгебраически, но, конечно, это не всегда так, когда ошибка с плавающей точкой учитываются.

Я попробовал «#pragma optimize (off)», но это не стандарт и не имеет никакого эффекта. Единственный хак, который я нашел, работает, чтобы создать «нулевой» равномерный поплавок, который остается установленным на 0, и добавить его к оскорбительным значениям в стратегических местах, поэтому рабочая версия вышеуказанной функции будет:

vec2 add(float a, float b) { 
    float sum = a + b; 
    sum += zero; 
    float err = b - (sum - a); 
    return vec2(sum, err); 
} 

Это, очевидно, не идеально. 1) Это PITA, чтобы отследить, где это необходимо, и 2) он зависит от компилятора. Другой компилятор может и не понадобиться, а другой можно, по-видимому, оптимизировать значение e до ноль. Есть ли «правильный» способ решить эту проблему и убедиться, что компилятор GLSL не оптимизирует фактическое поведение?

Edit:

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

float plus_frc(float a, float b) { 
    return mix(a, a + b, b != 0); 
} 

float minus_frc(float a, float b) { 
    return mix(0, a - b, a != b); 
} 

(«FRC» обозначает как «сила» и «фарс», потому что вы заставляете операцию, но необходимость идиотская .) Они реплицируют функциональные возможности (a + b) и (a-b), соответственно, но таким образом, что компилятор не может оптимизировать работу, не использует ветвление и использует fast builtin для выполнения работы , Таким образом, функция выше ошибки сохраняющего «добавить» становится:

vec2 add(float a, float b) { 
    float sum = plus_frc(a, b); 
    float err = b - (sum - a); 
    return vec2(sum, err); 
} 

Обратите внимание, что мы не всегда необходимости использовать нашу «FRC» функцию (например, уравнение найти ERR), но только в тех местах, где компилятор мог бы прервать оптимизацию.

ответ

0

№ Нет. Для управления оптимизацией в GLSL нет обязательного способа. Если компилятор считает, что разумно предположить, что ваш коэффициент ошибок равен нулю, тогда он будет равен нулю.

+0

Это ... немного разочаровывает. :/Но thx. – BLee

 Смежные вопросы

  • Нет связанных вопросов^_^