Опубликован: 28.04.2009 | Доступ: свободный | Студентов: 1840 / 107 | Оценка: 4.36 / 4.40 | Длительность: 16:40:00
Специальности: Программист
Лекция 1:

Введение в XNA Framework

Лекция 1: 123456789 || Лекция 2 >
Точность вычислений с плавающей точкой процессоров архитектуры x86

Блок вычислений с плавающей точкой ( FPU ) процессоров x869До появления i486DX этот блок располагался в отдельной микросхеме, называемой математическим сопроцессором (8087, 80287, 80387) содержит восемь 80-ти битных регистров общего назначения, используемых для хранения операндов и результатов вычислений. Иными словами, независимо от используемых типов данных сопроцессор всегда оперирует с 80-ти битным форматом с плавающей точкой, известным как extended double. Если операнды имеют меньшую разрядность (например, используется тип float ), то они автоматически конвертируются в 80-ти битный формат, а результат перед копированием в память переводится обратно в 32-х битный формат float. Так как подобная точность вычислений оказывается излишней, в одном из управляющих регистров процессора ( CW ) имеются два бита ( PC ), управляющие точностью вычислений. В зависимости от значения битов, задающихся при запуске приложения, вычисления выдадутся с 7-ю, 16-ю или 19-ю значащими цифрами. Ещe раз хочу подчеркнуть важную деталь. Формат чисел в регистрах всегда остаeтся 80-ти битным, просто с понижением точности младшие разряды числа с плавающей точкой могут содержать недостоверную информацию.

Два бита PC регистра CW устанавливаются при запуске потока ( thread ) и, как правило, не меняются в процессе выполнения приложения. В то же время, параметр CreateOptions.SinglePrecision приказывает конструктору класса GraphicsDevice изменить служебный регистр CW таким образом, чтобы установить внутри сопроцессора точность вычислений 7 знаков. В результате, после вызова данного метода вычисления, включая использующие типы double, будут выполняться с 7-ю значащими знаками. Правда существуют и исключения: так, к примеру, биты PC регистра CW оказывают влияние на точность сложения, вычитания, умножения, деления и вычисления квадратного корня, но совершенно не затрагивают точность команд вычисления синуса и косинуса.

В общем, здесь очень много различных тонкостей, но разбираться в них, в общем-то, не нужно. Достаточно запомнить, что флаг CreateOptions.SinglePrecision таит много подводных камней, поэтому применять его стоит только при реальной необходимости поднять производительно математической подсистемы любой ценой. В остальных случаях вы лишь повысите вероятность возникновения различных трудно обнаруживаемых ошибок. Ведь никто не сможет гарантировать, что одна из используемых вами библиотек не начнeт вести себя как-то странно, столкнувшись с аномально низкой точностью вычислений, использующих типы double. Например, пониженная точность может очень негативно сказаться на качестве генератора псевдослучайных чисел.

Итак, для создания графического устройства мы должны объявить в тексте класса формы поле класса

GraphicsDevice:
GraphicsDevice device=null;

Затем в обработчик события Load формы необходимо вставить код создания нового экземпляра класса GraphicsDevice c заданными параметрами листинг 1.1).

private void MainForm_Load(object sender, EventArgs e)
{
// Инициализируем все поля структуры presentParams значениями по умолчанию
PresentationParameters presentParams = new PresentationParameters(); 
// Мы будем осуществлять вывод на поверхность формы, то есть в оконном режиме
presentParams.IsFullScreen = false; 
// Включаем двойную буферизацию
presentParams.BackBufferCount = 1; 
// Переключение буферов должно осуществляться с максимальной эффективностью
presentParams.SwapEffect = SwapEffect.Discard; 
// Задаем ширину и высоту клиентской области окна. Если присвоить этим полям значение 0 (что 
// и происходит по умолчанию), то конструктор класса GraphivsDevice автоматически рассчитает 
// значение этих полей и занесeт их в структуру presentParams. Поэтому эти две строки, в 
// принципе, можно и опустить.
presentParams.BackBufferWidth = ClientSize.Width;
presentParams.BackBufferHeight = ClientSize.Height;
// Создаем новое устройство, обладающее следующими характеристиками:
// - Устройство будет использовать видеоадаптер по умолчанию
// - Устройство будет аппаратным
// - Вывод будет осуществляться на поверхность текущей формы
// – Обработка вершин будет осуществляться средствами GPU
// - Представление данных на экране задаeтся структурой 
presentParams (см. выше)
// – Доступ к устройству возможен только из одного потока приложения
device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, 
DeviceType.Hardware, Handle, CreateOptions.SoftwareVertexProcessing 
| CreateOptions.SingleThreaded, presentParams); 
}
Листинг 1.1.

Создав объект устройства, можно приступать к реализации закраски формы синим цветом. Для этого воспользуемся методом Clear класса GraphicsDevice, который очищает форму путeм закраски еe заданным цветом:

public void Clear(ClearOptions options, Color color, float depth, 
int stencil);

где

  • options - набор битовых флагов, указывающих какие буферы необходимо очистить. Для очистки экранного буфера используется флаг ClearOptions.Target. Остальные флаги ClearOptions.DepthBuffer и ClearOptions.Stencil, используемые для очистки соответственно буфера глубины и буфера шаблона, которые будут рассмотрены в следующих лекциях.
  • color - цвет, которым будет закрашен экран. Задаeтся с использованием структуры Microsoft.Xna.Framework.Graphics.Color, являющейся функциональным аналогом структуры System.Drawing.Color. Появление такого брата-близнеца обусловлено необходимостью сделать XNA Framework независимым от функциональности платформы Windows.
  • depth - значение, которым будет "закрашен" буфер глубины.
  • stencil - значение, которым будет заполнен буфер шаблона.

Вызов метода Clear необходимо вставить в обработчик события Paint, вызываемый каждый раз при необходимости перерисовки содержимого формы:

private void MainForm Paint(object sender, PaintEventArgs e) 
{
device.Clear(ClearOptions.Target, Microsoft.Xna.Framework.
Graphics.Color.CornflowerBlue, 
0.0f, 0) ; 
}

Обратите внимание на использование полного имени структуры Microsoft.Xna.Framework.Graphics.Color с указанием пространства имен. Если это не сделать, возникнет конфликт с одноименной структурой из пространства имен System.Drawing.

Класс GraphicsDevice имеет и более простую перегрузку ( override ) метода, предназначенную для очистки исключительно экранного буфера:

public void Clear(Color color);

где

color - цвет, которым заполняется весь экран

Использование данного варианта перегрузки метода позволяет несколько упростить код приложения:

private void MainFormPaint(object sender, PaintEventArgs e)
{
device.Clear(Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue); 
}

По окончанию работы приложение должно удалить графическое устройство при помощи метода Dispose. Для этой цели идеально подходит обработчик события FormClosed (листинг 1.2).

private void MainForm_FormClosed(object sender, FormClosedEventArgs e)
{
//  Если устройство существует
if (device != null)
{ //  Удаляем (освобождаем) устройство
device.Dispose(); 
// На всякий случай присваиваем ссылке на устройство значение 
null device = null;
}
}
Листинг 1.2.

Если вы забудете удалить объект устройства, .NET самостоятельно попытается вызвать метод Dispose экземпляра класса GraphicsDevice в процессе сборки мусора (если быть более точным, сборщик мусора вызывает метод Finalize, который довольно часто реализует вызов метода Dispose ). Но здесь имеется один нюанс. Как известно, сборщик мусора для вызова методов Finalize удаляемых объектов создаeт отдельный поток, в то время как на платформе Windows устройство Direct3D имеет право удалить только поток, создавший это устройство. Соответственно, деструктор объекта GraphicsDevice, вызываемый из параллельного потока, не сможет корректно удалить устройство Direct3D.

Примечание

Даже если вы не укажете при создании устройства флаг CreateOptions.SingleThreaded, сборщик мусора всe равно не сможет корректно удалить объект.

Вроде бы всe. Давайте попробуем запустить полученное приложение на выполнение (клавиша F5 ). Не смотря на то, что метод Clear вызывается при каждой перерисовке окна (в этом легко убедится, установив точку останова на строку с вызовом этого метода при помощи клавиши F9 ) , на внешнем виде формы это никак не отражается. Интересно, с чем это может связано?

Всe дело в том, что мы используем двойную буферизацию, то есть наше приложение выполняет все графические построения в невидимом вспомогательном буфере. После окончания визуализации необходимо скопировать информацию из этого вспомогательного буфера на форму. Эту операцию выполняет метод Present класса GraphicsDevice:

void Present()
Лекция 1: 123456789 || Лекция 2 >
Андрей Леонов
Андрей Леонов

Reference = add reference, в висуал студия 2010 не могу найти в вкладке Solution Explorer, Microsoft.Xna.Framework. Его нету.