Используем вычислительную мощь ATI StreamComputing CAL с простотой C# (из .NET)
Ожидает приглашения
В посте описывается мой вариант процесса программирования видео карт и моя библиотека для использования ATI StreamComputing из .NET языков (в примере C#). По сути это оболочка над ранее написанной библиотекой useGPU. Логика работы с видео картой и процесса разработки приложения осталась прежней. Объясню на примере.
Вот шейдер написанный на HLSL. Каждый поток читает все элементы одномерной текстуры и суммирует их. Соглашусь шейдер не практичен — результаты всех потоков идентичны. Для того что бы разные потоки читали свои массивы из разных участков двухмерной текстуры нужно подправлять код в ассемблере.
IMHO: HLSL может оказатся удобнее чем brook+, в нём неофициально есть классы и обьекты, по крайней мере документации я об этом не видел. Классы не поддерживают конструкторов/деструкторов, статических переменных/функций и виртуальных функций, но это не очень страшно по моему. Сама возможность инкапсуляции уже гуд. В 11-ом DirectX это всё появится официально плюс много других плюшек. Но думаю резон использовать ATI StreamComputing всё равно останетя — меньше задержки при обращении к устройству.
Пример валидного кода с классом:
Недостатоков всего два: нужно править шейдер на асме (для доступа к номеру потока или local memory), и нужно переключатся между средами — что совсем не сложно. С другой стороны — AMD-шный компилятор HLSL содержит различны расширения — так что можно обращатся к ассемблеру редко. После выхода DirectX 11 первый недостаток отпадёт — можно будет писать computing shaders.
Код на C#, использующий вышеуказанный шейдер скомпилированный в CAL IL (унифицированный ассемблер видеокарт ATI, который если честно очень слабо отличается от D3D assembly).
Шаг 1. Написание шейдера.
Вот шейдер написанный на HLSL. Каждый поток читает все элементы одномерной текстуры и суммирует их. Соглашусь шейдер не практичен — результаты всех потоков идентичны. Для того что бы разные потоки читали свои массивы из разных участков двухмерной текстуры нужно подправлять код в ассемблере.
//описание семплера нужно как формальность, далее мы о нём совсем забудем.
SamplerState S
{
};
//текстура из которой будем читать данные
Texture1D Input;
/*описываем структуры входящих значений для шейдера (не константы и не данные текстуры )*/
struct VS_OUTPUT
{
float2 pos : POSITION;
/*семантика нужна компилятору, он думает что у нашего шейдера есть некая графическая специфика. Семантику POSITION*, где * - номер элемента текстуры можно прописать к любому типу поля - даже вашему собственному*/
};
struct PS_OUTPUT
{
int bla: SV_Target1;
/*ситуация аналогична предудущей*/
};
float get(Texture1D t, float coord)
{
return t.Sample(S,coord);
//для того что бы забыть о семплерах совсем
}
PS_OUTPUT main (VS_OUTPUT In)
{
PS_OUTPUT Output;
Output.bla = 0;//инициализация - иначе компилятор ругается
for(int i = 0; i < color="#cc0000"> return Output;
}
IMHO: HLSL может оказатся удобнее чем brook+, в нём неофициально есть классы и обьекты, по крайней мере документации я об этом не видел. Классы не поддерживают конструкторов/деструкторов, статических переменных/функций и виртуальных функций, но это не очень страшно по моему. Сама возможность инкапсуляции уже гуд. В 11-ом DirectX это всё появится официально плюс много других плюшек. Но думаю резон использовать ATI StreamComputing всё равно останетя — меньше задержки при обращении к устройству.
Пример валидного кода с классом:
class my
{
int4 a;
//это не конструкто
void my()
{
a = 2;
}
void bla();
};
void my::bla()
{
this.a += 1;
}
struct PS_OUTPUT
{
my bla: SV_Target1;
//подобное обьявление позволяет забыть о семантике и менять только свой класс
};
Недостатоков всего два: нужно править шейдер на асме (для доступа к номеру потока или local memory), и нужно переключатся между средами — что совсем не сложно. С другой стороны — AMD-шный компилятор HLSL содержит различны расширения — так что можно обращатся к ассемблеру редко. После выхода DirectX 11 первый недостаток отпадёт — можно будет писать computing shaders.
Шаг 2. С помощью AMD GPUShaderAnalyzer 1.47 (или ниже))) — из более поздних такую возможность убрали ) получаем для нашего шейдера Intermediate Language.
Шаг 3. Написание хост кода использующего шейдер.
Код на C#, использующий вышеуказанный шейдер скомпилированный в CAL IL (унифицированный ассемблер видеокарт ATI, который если честно очень слабо отличается от D3D assembly).
string s = @"il_ps_2_0
dcl_cb cb0[1]
dcl_output_generic o0.x___
dcl_resource_id(0)_type(1d,unnorm)_fmtx(float)_fmty(float)_fmtz(float)_fmtw(float)
; l0 = (0.000000f 0.000000f 0.000000f 0.000000f)
; l0 = (0.000000f 0.000000f 0.000000f 0.000000f)
dcl_literal l0, 0x00000000, 0x00000000, 0x00000000, 0x00000000
mov r0.xyz_, l0
whileloop
ige r0.___w, r0.z, cb0[0].x
break_logicalnz r0.w
itof r0.___w, r0.z
sample_resource(0)_sampler(0) r1, r0.w
iadd r0.x___, r0.x, r1.x
; l2 = (0.000000f 0.000000f 0.000000f 0.000000f)
dcl_literal l2, 0x00000001, 0x00000001, 0x00000001, 0x00000001
iadd r0.__z_, r0.z, l2
endloop
mov o0.x___, r0.x
ret_dyn
end";
/*Инициализируем связь с CAL runtime, узнаём что у нас за устройство в системе и какими характеристиками оно обладает */
dotGPU.GPU g = new dotGPU.GPU();
/*компилируем нашу программу*/
g.CreateImage(s, 0);
//создаём текстурку - знаю было бы быстрее с использованием массивов.
var data = new List();
data.Add(123);
data.Add(456);
//Копируем текстуру на видеокарту и получаем номер соответствующего ей ресурса
int n1 = (int)g.Allocate(0, false, data, 2, 1, 20);
var data1 = new List();
data1.Add(2);
//аналогичный процесс для константы
int n2 = (int)g.Allocate(0, false, data1, 1, 1, 20);
int o = (int)g.Allocate(0, true, null, 64, 1, 20);
/*Запускаем на выполнение шейдер 0 на устройстве 0, размер домена выполнения(количество тредов) равен размеру аргумента 2, связываем хендлеры ресурсов с регистрами шейдера и получаем в результате хендлер задачи*/
int e = (int)g.Execute(0, 0, 2,
new List() { "i0", "cb0", "o0" },
new List() { n1,n2,o});
//ждём пока не закончится выполнение шейдера
while (!g.Wait(0, e));
//забираем результат
data1 = g.GetResult(o);
//завершаем работу с видеокартой - это нужно делать явно!
g.Close();
Шаг 4. Release)
Шаг 5. Debug))))))