2013-06-16 2 views
5

В 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, работа с битами работает одинаково и т. Д.).

+1

Я только что нашел [эту документацию] (http://developer.download.nvidia.com/opengl/specs/GL_EXT_texture_shared_exponent.txt), которая имеет код C (начиная с середины документа), который кажется многообещающим .. . – leemes

+0

'floor (log2())' может быть заменен бит бифуркационной и целочисленной арифметикой для извлечения и изменения размера/повторного смещения экспонента «maxAbs» без необходимости вычисления дробной части логарифма , Он не выглядит применимым здесь, но когда у вас есть целое число, вы также можете использовать 'clz' (подсчитывать начальные нули), который часто будет одной машинной инструкцией. – user57368

ответ

0

Если вы использовали CL/GL interop и сохранили ваши данные в текстуре OpenGL в формате RGB9_E5, и если бы вы могли создать изображение OpenCL из этой текстуры, вы могли бы использовать аппаратную структуру текстуры для преобразования в float4 при чтении из изображения. Возможно, стоит попробовать.

+0

Нет, это не тот случай. Это часть более крупной структуры, которую я помещал в буфер. Но я думаю, что я отказываюсь от этой идеи и использую три половинных поплавка. Спасибо, в любом случае ;) – leemes