Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

Пример программы




Функции центрального процессора

Взаимодействие центрального и графического процессоров

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

Непосредственно с видеопамятью и графическим процессором работают драйверы видеокарты – программы низкого уровня, учитывающие особенности конкретной видеокарты конкретного производителя. Писать для них программы напрямую сложно, поскольку они не предоставляют интегрированных процедур высокого уровня, которые бы исполняли стандартную, но трудоёмкую часть работы, например, приведение данных к необходимому видеокарте формату, выделение/освобождение памяти и т.п.

В связи с этим, для программирования графических процессоров используются специализированные библиотеки высокоуровневых процедур, в частности – DirectX либо OpenGL. Для дальнейшего упрощения программирования, мы используем дополнительную библиотеку Microsoft XNA Framework, которая использует библиотеку DirectX, предоставляя дополнительные интегрированные процедуры.

Ниже мы подробно рассмотрим программу, запускающую сложение матриц на графическом процессоре. Текст программы написан на языке C#. Мы не будем исчерпывающе комментировать синтаксис этого языка, но детально прокомментируем операции, исполняемые над данными для видеокарты.

· Подключение необходимых библиотек. Используется стандартная библиотека System, а кроме того – библиотека Microsoft.Xna.Framework, упрощающая подготовку данных для графического процессора. Раздел этой библиотеки Microsoft.Xna.Framework.Graphics подключаем отдель­но, чтобы сократить в тексте программы описание доступа к ресурсам этого раздела.

using System;

using Microsoft.Xna.Framework;

using Microsoft.Xna.Framework.Graphics;

 

public class GPGPU: Microsoft.Xna.Framework.Game

{

· Описание переменных, используемых программой

/* Задаётся размер матрицы, количество элементов в строке/столбце */

int size = 3;

/* Описываем переменную graphics класса GraphicsDeviceManager и затем инициализируем эту переменную операцией graphics = new GraphicsDeviceManager(this). Эта переменная будет обеспечивать доступ к процедурам для управления графическим процессором из библиотеки Microsoft.Xna.Framework */

GraphicsDeviceManager graphics;

· Стандартные процедуры, реализующие исполнение программы на C#

/* Ниже описаны процедуры, которые будут последовательно испол­нены сразу после запуска программы. Процедуры Main и Initialize являются стандартными для проектов на C#. Внутри процедуры Initialize находится вызов процедуры MatrixSum(), которая и осуществляет суммирование матриц на графическом процессоре. */

static void Main(string[] args) { using (GPGPU game = new GPGPU()) game.Run(); }

public GPGPU() { graphics = new GraphicsDeviceManager(this); }

protected override void Initialize()

{

base.Initialize();

MatrixSum();

Exit();

}

 

· Процедура, суммирующая матрицы с использованием GPU

private void MatrixSum()

{

/* Выше была введена переменная graphics класса GraphicsDevice­Manager. В этом классе есть нужный нам подкласс GraphicsDevice. Создаём переменную gpu этого подкласса */

GraphicsDevice gpu = graphics.GraphicsDevice;

· Выделение памяти для рендер-цели

/* Созданная в начале программы переменная size = 3 задаёт размеры матриц (3´3), которые мы будем складывать. Теперь создается рендер-цель – массив 3´3, в который будет выво­диться результат расчетов. Соответствующая переменная называется tex_target. Она является не простым массивом 3´3, а принадлежит к классу RenderTarget2D из библиотеки Microsoft.­Xna.­Framework. Кроме самого массива, в объектах этого класса хранятся некоторые дополнительные параметры, в частности – указатель на переменную gpu, который задаёт конкретную видеокарту, для которой создаётся рендер-цель. */

RenderTarget2D tex_target = new RenderTarget2D(gpu, size, size, 1, SurfaceFormat.Single);

· Выделение памяти для текстур с исходными данными

/* Создаются текстуры, – массивы размера size*size, - в которых будут храниться исходные матрицы. Как и рендер-цель, соответст­вующие переменные являются не простыми массивами, а принад­лежат к более сложному классу Texture2D, в объектах которого, кроме самих массивов, хранятся дополнительные параметры. Как и выше, значения этих параметров устанавливаются при создании новой переменной класса, строкой new Texture2D(gpu, size, size, 1, ResourceUsage.­Dynamic, SurfaceFormat.Single, ResourceManage­ment­Mode.Manual), значениями внутри скобок. */

Texture2D tex_matrix1 = new Texture2D(gpu, size, size, 1, ResourceUsage.Dynamic, SurfaceFormat.Single, ResourceManagement­Mode.Manual);

Texture2D tex_matrix2 = new Texture2D(gpu, size, size, 1, Resource­Usage.Dynamic, SurfaceFormat.Single, ResourceManagement­Mode.­Manual);

· Заполнение массивов исходных данных конкретными значениями

/* Теперь, задаём конкретные значения элементов исходных матриц. Сначала эти значения записываются в одномерные массивы matrix1 и matrix2 размеров size * size. Элементы a ij и b ij после преобразования матриц к одномерным массивам будут иметь номера j * size + i. Затем, мы копируем созданные массивы в текстуры класса Texture2D tex_matrix1 и tex_matrix2 операциями tex_matrix1.­SetData<float>(matrix1) и tex_matrix2.SetData<float>­(matrix2). */

float[] matrix1 = new float[size * size], matrix2 = new float[size * size];

for (int j = 0; j < size; j++)

for (int i = 0; i < size; i++)

{

matrix1[j * size + i] = j * size + i;

matrix2[j * size + i] = i * size + j;

}

tex_matrix1.SetData<float>(matrix1);

tex_matrix2.SetData<float>(matrix2);

· Установление соответствия между целочисленными индексами элементов матриц и текстурными координатами этих элементов

/*Следующая задача – установление соответствия между целочисленными индексами элементов матриц 3´3 и текстурными координатами этих же элементов внутри квадрата (-1;-1)-(1;1), или quad’а, которыми будет оперировать графический процессор, а также внутри квадрата (0;0)-(1;1), в который отображается массив результатов (рендер-цель). Для этого достаточно задать соответствие между индексами «крайних» элементов матриц и координатами вершин квадрата. Соответствие это показано в таблице 5.1. При необходимости рассмотрения матриц других размеров, знаменатель 6 нужно заменить на значение 2*size.

Таблица 5.1

Соответствие между координатами вершин quad’а (-1;-1)-(1;1) и координатами элементов складываемых матриц

Вершины quad’а Координаты элементов рендер-цели
(-1; 1) (1/6; 1/6)
(-1; -1) (1/6; 1+1/6)
(1; 1) (1+1/6; 1/6)
(1; -1) (1+1/6; 1+1/6)

 

Метод определения координат элементов проиллюстрирован также на рис. 5.2. Видно, что приведённые формулы действительно обеспечивают попадание эле­ментов массива 3´3 в необходимые области. Учтён также тот факт, что ось Y на экране (и в рендер цели) направлена сверху вниз.

Третья пространстваенная координата во входных данных у нас не используется, поэтому в конструкторах вида Vector3(-1, 1, 0) третья координата равна нулю, тогда как первые два значения задают координаты вершин quad’а */

float dx = 0.5f / size, dy = 0.5f / size; // смещения для адресации центров текселей

VertexPositionTexture[] v = new VertexPositionTexture[] {

new VertexPositionTexture(new Vector3(-1, 1, 0), new Vector2(0 + dx, 0 + dy)),

new VertexPositionTexture(new Vector3(-1, -1, 0), new Vector2(0 + dx, 1 + dy)),

new VertexPositionTexture(new Vector3(1, 1, 0), new Vector2(1 + dx, 0 + dy)),

new VertexPositionTexture(new Vector3(1, -1, 0), new Vector2(1 + dx, 1 + dy))

};

/* Следующей, стандартной, строкой выделяется область памяти для хранения координат вешин quad’а. Этих вершин 4, чем и обусловлен размер 4 * VertexPositionTexture.SizeInBytes */

VertexBuffer quad = new VertexBuffer(gpu, 4 * VertexPosition­Texture.SizeInBytes, ResourceUsage.None, ResourceManagementMode.­Automatic);

quad.SetData<VertexPositionTexture>(v);

VertexDeclaration quadVertexDeclaration = new VertexDeclaration­(gpu, VertexPositionTexture.VertexElements);

· Компилирование эффектов, содержащих шейдеры

/* В библиотеку Microsoft.Xna.Framework встроен компилятор текстовых файлов, содержащих шейдеры для графического процессора, таких как sum.fx (начало примера и Приложение 1). Следующая строка компилирует файл sum.fx и запи­сывает полученную программу (в машинных кодах) в область оперативной памяти, на которую указывает переменная e типа CompiledEffect. */

CompiledEffect e = Effect.CompileEffectFromFile("sum.fx", null, null, CompilerOptions.None, TargetPlatform.Windows);

/* Проверка успешности компиляции */

if (!e.Success) throw new Exception(e.ErrorsAndWarnings);

· Выбор шейдера, который будет исполняться

/* Создаётся новая переменная, описывающая эффект, который будет исполняться на графическом процессоре. В качестве кода эффекта выбирается откомпилированная программа, на которую указывает переменная e. */

Effect fx = new Effect(gpu, e.GetEffectCode(), CompilerOptions.None, null);

/* В качестве запускаемой на видеокарте процедуры выбирается техника sum из файла sum.fx (описана выше) */

fx.CurrentTechnique = fx.Techniques["sum"];

· Копирование исходных данных в видеопамять, доступную графическому процессору

/* В качестве исходных данных передаём эффекту fx текстуры с исходными данными tex_matrix1 и tex_matrix2, которые мы уже создали в начале программы: */

fx.Parameters["tex_matrix1"].SetValue(tex_matrix1);

fx.Parameters["tex_matrix2"].SetValue(tex_matrix2);

/* Задаем выходные параметры: quad и рендер-цель: */

gpu.Vertices[0].SetSource(quad, 0, VertexPositionTexture.SizeInBytes);

gpu.VertexDeclaration = quadVertexDeclaration;

gpu.SetRenderTarget(0, tex_target);

· Исполнение шейдера на графическом процессоре

fx.Begin();

fx.CurrentTechnique.Passes["sum"].Begin();

gpu.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);

fx.CurrentTechnique.Passes["sum"].End();

fx.End();

· Получение результата из рендер-цели и сохранение его в текстовом файле

/* Копирование массива результатов из видеопамяти в оперативную память, доступную центральному процессору */

gpu.ResolveRenderTarget(0);

/* Создаём текстовый файл для записи результата */

using (System.IO.StreamWriter w = new System.IO.StreamWriter­("results.txt"))

{

/* Создаём одномерный массив, в который будут скопированы данные из рендер-цели, для дальнейшей обработки центральным процессором */

float[] matrix3 = new float[size * size];

/* Копирование данных из рендер цели в массив matrix3. Аргумент <float> означает, что данные будут скопированы в виде скалярных чисел одинарной точности */

tex_target.GetTexture().GetData<float>(matrix3);

/* Запись данных из массива matrix3 в текстовый файл */

for (int j = 0; j < size; j++)

{

for (int i = 0; i < size; i++) w.Write("\t{0}", matrix3[j * size + i]);

w.WriteLine();

}

}}}

 




Поделиться с друзьями:


Дата добавления: 2014-12-07; Просмотров: 372; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.047 сек.