В OpenCL я хочу сохранить вектор (3D), используя представление «Общий экспонент» для компактного хранилища. Как правило, если вы храните 3D-вектор с плавающей запятой, вы просто сохраняете 3 отдельных значения поплавка (или 4 при правильном выравнивании). Для этого требуется 12 (16) байтов для одной точности, и если вы не требуете такой точности, вы можете использовать "half" precision float и сжать ее до 6 (8) байтов.Представление «Общий экспонент» вектора с плавающей запятой в OpenCL C
При использовании половины точности и 3 отдельных значений, память не выглядит следующим образом (без выравнивания считается):
- координаты х: 1 битого знаком, 5 битых экспонентами, 10 битых мантиссой
- координата: 1 знаковый бит, 5 бит экспоненты, 10 бит мантиссы
- г координат: 1 знаковый бит, 5 бит экспоненты, 10 бит мантиссы
Я хотел бы, чтобы уменьшить это до 4 байтов, используя поделился экспонентом, так как OpenGL использует это в одном из своих внутренних форматов текстуры («RGB9_E5»). Это означает, что самый большой компонент решает, что представляет собой показатель целого числа. Этот показатель затем используется для каждого компонента неявно. Трюки, такие как «нормализованное» хранилище с неявным «1.», перед мантиссой не работают в этом случае. Такое представление как это работает (мы могли бы настроить параметры acutal, так это пример):
- координаты х: 1 бит знака, 8 бит мантиссы
- координату: 1 знаковый бит, 8 бит мантиссы
- г координат: 1 бит знака, 8 бит мантиссы
- 5 битов совместно экспоненту
Я хотел бы сохранить это в OpenCL uint
типа (32 бит) или что-то эквивалентное (например uchar4
). Вопрос сейчас:
Как я могу конвертировать из и в это представление до и от float3
как можно быстрее?
Моя идея заключается в том, как это, но я уверен, что есть некоторые «немного взлома» трюк, который использует битовое представление IEEE поплавки для обхода с плавающей точкой ALU:
- Использование
uchar4
в качестве представителя тип. Храните x, y, z mantisssa в x, y, z компонентах этогоuchar4
. Компонент w разбивается на 5 менее значимых битов(w & 0x1F)
для общего экспонента, а еще три значащих бита(w >> 5) & 1
,(w >> 6) & 1
и(w >> 7) & 1
являются знаками для x, y и z соответственно. - Обратите внимание, что показатель «смещен» на 16, то есть сохраненное значение 16 означает, что представленные числа соответствуют (не включая) 1.0, сохраненное значение 19 означает значения до (не включая) 8,0 и так далее на.
"Распаковка" это представление в
float3
можно сделать с помощью этого кода:float3 unpackCompactVector(uchar4 packed) { float exp = (float)(packed.w & 0x1F) - 16.0; float factor = exp2(exp)/256.0; float x = (float)(packed.x) * factor * (packed.w & 0x20 ? -1.0 : 1.0); float y = (float)(packed.y) * factor * (packed.w & 0x40 ? -1.0 : 1.0); float z = (float)(packed.z) * factor * (packed.w & 0x80 ? -1.0 : 1.0); float3 result = { x, y, z }; return result; }
"Упаковка" а
float3
в это представление может быть сделано с помощью этого кода:uchar4 packCompactVector(float3 vec) { float xAbs = abs(vec.x); uchar xSign = vec.x < 0.0 ? 0x20 : 0; float yAbs = abs(vec.y); uchar ySign = vec.y < 0.0 ? 0x40 : 0; float zAbs = abs(vec.z); uchar zSign = vec.z < 0.0 ? 0x80 : 0; float maxAbs = max(max(xAbs, yAbs), zAbs); int exp = floor(log2(maxAbs)) + 1; float factor = exp2(exp); uchar xMant = floor(xAbs/factor * 256); uchar yMant = floor(yAbs/factor * 256); uchar zMant = floor(zAbs/factor * 256); uchar w = ((exp + 16) & 0x1F) + xSign + ySign + zSign; uchar4 result = { xMant, yMant, zMant, w }; return result; }
Я поставил эквивалентную реализацию в C++ online on ideone. В тестовых примерах показан переход от exp = 3
к exp 4
(с смещением 16 это кодируется как 19 и 20 соответственно) путем кодирования чисел около 8.0
.
Эта реализация, похоже, работает на первый взгляд. Но:
- Есть некоторые угловые шкафы, которые я не покрывал, в частности, чрезмерное и нижнее (экспоненты).
- Я не хочу использовать математические функции с плавающей запятой, такие как
log2
, потому что они медленные.
Можете ли вы предложить лучший способ достичь моей цели?
Обратите внимание, что мне нужно только в OpenCL «устройства код» для этого, мне не нужно конвертировать между представлениями в программе хозяина. Но я добавил тэг C
, так как решение, скорее всего, не зависит от функций OpenCL (OpenCL почти C, а также использует поплавки IEEE 754, работа с битами работает одинаково и т. Д.).
Я только что нашел [эту документацию] (http://developer.download.nvidia.com/opengl/specs/GL_EXT_texture_shared_exponent.txt), которая имеет код C (начиная с середины документа), который кажется многообещающим .. . – leemes
'floor (log2())' может быть заменен бит бифуркационной и целочисленной арифметикой для извлечения и изменения размера/повторного смещения экспонента «maxAbs» без необходимости вычисления дробной части логарифма , Он не выглядит применимым здесь, но когда у вас есть целое число, вы также можете использовать 'clz' (подсчитывать начальные нули), который часто будет одной машинной инструкцией. – user57368