WWW.DISSERS.RU

БЕСПЛАТНАЯ ЭЛЕКТРОННАЯ БИБЛИОТЕКА

   Добро пожаловать!

Pages:     | 1 |   ...   | 4 | 5 ||

«Том Миллер Managed DirectX*9 Программирование графики и игр **омпэ*> Предисловие Боба Гейнса Менеджера проекта DirectX SDK корпорации Microsoft SAMS [Pi] KICK START Managed DirectX 9 ...»

-- [ Страница 6 ] --

Как и раньше, вначале мы должны создать серверную часть. Необхо­ димо найти и переписать секцию кода, где мы создавали хост для Р2Рсети. Затем в методе инициализации InitializeDirectPlay следует добавить следующий код после вызова EnableSendDataButton:

// Create our voice server first voiceServer = new Voice.Server(connection);

// Create a session description Voice.SessionDescription session = new Voice.SessionDescriptionf);

session.SessionType = Voice.SessionType.Peer;

session.BufferQuality = Voice.BufferQuality.Default;

session.GuidCompressionType = Voice.CompressionGuid.Default;

session.BufferAggressiveness = Voice.BufferAggressiveness.Default;

// Finally start the session voiceServer.StartSession(session);

Итак, теперь мы должны связать создаваемый объект Voice с объек­ том DirectPlay, который будет выполнять функцию передачи голоса. Это позволит передавать голос наряду с другими данными сети. Поскольку мы используем Р2Р-сеть, сообщения будут передаваться другим игрокам напрямую, однако существуют и другие сценарии голосового общения. • Смешанный — в этом режиме все голосовые сообщения отсыла­ ются серверу. Затем сервер объединяет их и переправляет игрокам. Такой подход перегружает трафик, но является очень удобным. • Перенаправляющий — и в этом режиме все голосовые сообще­ ния проходят через хост. Такой вариант снижает трафик для каж­ дого клиента, но значительно увеличивает его для главного ком­ пьютера. Если пропускная способность главного компьютера мала, этот сценарий будет бесполезным. Эхо возвращает звуковое сообщение назад. Еще одной опцией, которую мы можем установить в описании соеди­ нения, является кодек. В нашем приложении кодек установлен по умол­ чанию, а ниже приводится фрагмент кода, позволяющий распечатать в окне вывода кодеки, которые можно использовать дополнительно:

Часть VI. Добавление сетевых возможностей foreach(Voice.Compressionlnformation ci in voiceServer.CompressionTypes) ( Console.WriteLine(ci.Description);

} Данная информация позволит выбрать наиболее подходящий кодек. Вы можете также использовать любой из кодов сжатия, перечисленных в классе CompressionGuid. Для оставшихся параметров мы устанавливаем значения по умолча­ нию. И как обычно, мы добавляем процедуру выхода из соединения и осво­ бождения объекта: if (voiceServer != null) { voiceServer.StopSession();

voiceServer.Dispose();

voiceServer = null;

} Теперь мы имеем половину программы, позволяющей осуществить голосовое соединение. Добавьте к вашему приложению код, приведен­ ный в листинге 20.2.

Листинг 20.2. Присоединение к сеансу голосовой связи. private void ConnectVoice() { // Now create a client to connect voiceClient = new Voice.Client(connection) ;

// Fill in description object for device configuration Voice.SoundDeviceConfig soundConfig = new Voice.SoundDeviceConfig(), soundConfig.Flags = Voice.SoundConfigFlags.AutoSelect;

soundConfig.GuidPlaybackDevice = DSoundHelper.DefaultPlaybackDevice;

soundConfig.GuidCaptureDevice = DSoundHelper.DefaultCaptureDevice;

soundConfig.Window = this;

// Fill in description object for client configuration Voice.ClientConfig clientConfig = new Voice.ClientConfig();

clientConfig.Flags = Voice.ClientConfigFlags.AutoVoiceActivated | Voice.ClientConfigFlags.AutoRecordVolume;

clientConfig.RecordVolume = (int) Voice.RecordVolume.Last;

clientConfig.PlaybackVolume = (int) Voice.PlaybackVolume.Default;

clientConfig.Threshold = Voice.Threshold.Unused;

clientConfig.BufferQuality = Voice.BufferQuality.Default;

ClientConfig.BufferAggressiveness = Voice.BufferAggressiveness.Default;

Глава 20. Особенности более совершенного использования сетей } // Connect to the voice session voiceClient.Connect(soundConfig, clientConfig, Voice.VoiceFlags.Sync);

voiceClient.TransmitTargets = new int[] ( (int)Voice.PlayerId.AllPlayers 1;

Сначала мы создаем объект voice client и указываем объект DirectPlay, который будет использоваться для передачи голосовых данных. Затем, прежде чем присоединиться к сеансу, мы должны установить конфигу­ рацию для обеих звуковых карт. Для этого используется структура SoundDeviceConfig, сообщающая DirectPlay об устройствах, которые могут использоваться для голосовой связи. В нашем случае мы автома­ тически выбираем микрофон и заданные по умолчанию устройства зах­ вата и воспроизведения звука (в главе DirectSound мы использовали со­ вместный доступ к этим устройствам, и здесь мы оставим это без изме­ нения). Далее с помощью структуры ClientConfig мы определяем параметры клиента. Флажки используются таким образом, чтобы максимальное ка­ чество звука устанавливалось автоматически и передача начиналась сра­ зу, как только будет получен звук от микрофона. При использовании флаж­ ка AutoVoiceActivated необходимо установить пороговое значение в по­ ложение «unused». Если же осуществляется «ручной» запуск передачи звука, пороговое значение должно быть установлено на минимальный уровень. Теперь мы устанавливаем уровень звука по умолчанию, затем соеди­ няемся с сетью (точнее с хостом), для простоты используя при этом фла­ жок синхронности (чтобы не записывать дополнительный обработчик). Присоединившись к сеансу, мы определяем объекты для передачи голо­ совых сообщений. И наконец, нам осталось сделать два добавления. Во-первых, необхо­ димо добавить процедуру Dispose, чтобы освободить объект и выйти из соединения (опять используя синхронный флажок): if (voiceClient != null) { voiceClient.Disconnect(Voice.VoiceFlags.Sync);

voiceClient.Dispose();

voiceClient = null;

} Во-вторых, мы должны предусмотреть вызов метода ConnectVoice и для сервера, и для клиента: в конце метода StartSession на сервере, чтобы позволить главному компьютеру создание клиента voice client, и в проце­ дуре OnConnectComplete после успешной установки соединения:

Часть VI. Добавление сетевых возможностей if (e.Message.ResultCode == ResultCode.Success) { this.Beginlnvoke(new AddTextCallback(AddText), new object[] { "Connect Success."});

connected = true;

this.Beginlnvoke(new EnableCallback(EnableSendDataButton), new object[] { true } );

ConnectVoice();

} ПРОВЕРКА УСТАНОВКИ ЗВУКА Если вы не использовали мастер установки, вызов Connect может выдать ошибку RunSetupException. В этом случае можно «захватить» это исключение и произвести установку звука, используя следую­ щий код: Voice.Test t = new Voice.Test();

t.CheckAudioSetup();

После установки можно попробовать подключиться еще раз. Дан­ ная установка выполняется один раз.

Краткие выводы В этой главе мы рассмотрели различные особенности DirectPlay, вклю­ чая следующие разделы. • Модели событий и обработчики. • Пропускная способность, трафик. • Очередность отправки данных. • Лобби-приложения. • Использование голосовой связи в приложении. В нашей заключительной главе мы обобщим накопленную к этому моменту информацию.

Глава 21. Методы достижения максимального быстродействия Глава 2 1. Методы достижения максимального быстродействия В данной главе будут охвачены следующие темы. • Операции преобразования типов: boxing и unboxing. • Модель события и ее недостатки. • Повышение эффективности.

Преобразование типов в объекты При разработке различных программ вопрос быстродействия остает­ ся одним из самых основных. Ниже мы рассмотрим реальные примеры, позволяющие увеличить быстродействие приложений. Пример Billboard (Доска Объявлений), который поставляется с DirectX SDK, имеет различные версии: неуправляемую версию, написанную на C++, а также две управляемые версии, одну, написанная на С#, другую, разработанную в среде VB.NET Поскольку каждый пример из DirectX SDK учитывает частоту смены кадров, можно легко определить, какое из приложений выполняется быстрее. Сравнивая пример Billboard, написанный на C++, с образцом, написан­ ном на С#, нужно обратить внимание на то, что скорость выполнения С# приложения составляет примерно 60% от скорости образца на C++. При­ нимая во внимание использование управляемого и неуправляемого кода в различных версиях следует выяснить, с чем связано это «замедление». Термин «boxing» для.NET Runtime подразумевает преобразование типов в некий объект. Соответственно, процесс обратного преобразова­ ния называется «unboxing». Для выполнения этих операций среде.NET Runtime необходимо выделить часть динамической памяти, достаточную для размещения данных, и затем скопировать данные из стека (где хра­ нится значение) в созданную область динамической памяти. Теперь, используя управляемую версию примера Billboard, можно обратить внимание на то, что каждая отображаемая ветвь имеет соответ­ ствующую структуру, содержащую необходимую для рисования инфор­ мацию. Поскольку структура смешана, но должна отображаться в соот­ ветствующем порядке, выполним следующую операцию: trees.Sort (new TreeSortClass());

Данный класс имеет встроенную процедуру сравнения IComparer, ко­ торая принимает в качестве входных параметров два объекта. Прежде чем она будет передана в метод сравнения, структура используемых объек­ тов должна быть преобразована с помощью метода boxing, а после вы­ полнения процедуры сравнения — преобразована обратно (unboxing).

Часть VI. Добавление сетевых возможностей Метод Sort будет вызываться приблизительно 4300 раз за кадр. При каждом вызове будут выполняться две операции преобразования boxing и, соответственно, две обратных операции. Сама структура определена следующим образом:

public struct Tree { public CustomVertex.PositionColoredTextured vO, vl, v2, v3;

public Vector3 position;

public int treeTexturelndex;

public int offsetlndex;

};

Можно рассчитать объем памяти, который потребуется для размеще­ ния данных в процессе выполнения операции сортировки (исходя из раз­ мера структуры равного 116-ти байтам). Для этого необходимо умножить величину объекта (116 байт) на число объектов (2) и на количество вызо­ вов для одного кадра (4300), получается огромное значение — 997,600 байтов на каждый кадр. И это касается только операции размещения. После того как данные скопированы, и операция сравнения проведе­ на, они должны пройти процедуру unboxing. Это подразумевает те же действия по выделению памяти и копированию данных, только на этот раз для стека. В итоге, данные для каждого кадра (1,995,200 байт) рас­ пределяются между стеком и динамической памятью с поочередным ко­ пированием, причем размер распределенных областей небольшой (116 байт), а их количество составляет немалое значение. Теперь, по крайней мере, понятна причина замедления выполнения программ на C++. Ис­ пользование среды.NET Runtime дает огромное преимущество в быст­ родействии и гибкости, что должно послужить причиной для перехода к программированию в этой среде.

Побочные эффекты моделей событий Допустим, имеется знакомый сценарий, характерный для Управляе­ мого Direct3D. Мы уже достаточно знакомы с работой обработчиков со­ бытий в приложениях Управляемого DirectX, и можем предположить, что многие операции, которые выполняют обработчики, расходуют немало ресурсов, если использовать их не должным образом. Во-первых, это касается распределения памяти. Например, каждый раз при создании вершинного буфера, помимо обычного резервирования памяти, выделяется дополнительная память для делегатов, необходимых для обработки событий. Обработчики событий непосредственно связа­ ны с этими объектами во время работы приложения. Рассмотрим типич­ ный пример:

Глава 21. Методы достижения максимального быстродействия public void WasteVideoMemory() { VertexBuffer waste = n w VertexBuffer(device, 5000, e 0, VertexFormats.None, Pool.Default);

} Этот код выглядит «мертвым». Конечно, вершинный буфер создается, но он никогда не используется, тем не менее, программа «сборщика му­ сора» все равно обработает данный буфер. После создания вершинного буфера отслеживаются события DeviceLost, DeviceReset и Disposing. Обработчики этих событий связывают устрой­ ство и вершинный буфер, и, если буфер нигде не используется, «сборщик мусора» не заметит его. Если бы подобный метод вызывался многократно, мы бы постепенно исчерпали весь запас свободной видеопамяти (поскольку объект был со­ здан в заданном по умолчанию пуле памяти). Другой побочный эффект подобного поведения — время перезагруз­ ки «shutdown time». Нам написало несколько человек, которые сообщили об ошибках в Managed Runtime, связанных с блокировкой приложения при выходе из системы или перезагрузке. Можно предположить, что сценарий был сле­ дующий:

device.SetRenderTarget(0, mySwapChain.GetBackBuffer(0, BackBufferType.Mono));

// Render some stuff Этот код выполняет примерно те же самые операции, которые мы опи­ сывали в предыдущем примере. Создается новый объект (в данном слу­ чае поверхность), который никогда не используется. Таким образом, в результате многократного выполнения этой строки создаются тысячи «потерянных» поверхностей. При закрытии приложения устройство пы­ тается освободить все эти поверхности (функция dispose) и естественно «зависает». Есть два пути для решения этой проблемы. Первый, самый простой, — включение директивы using для объектов, которые будут использоваться в ближайшее время. Соответствующий код может иметь вид: using(Surface backBuffer = mySwapChain.GetBackBuffer(0, BackBufferType.Mono)) { device.SetRenderTarget(0, backBuffer);

// Render some stuff.

I Часть VI. Добавление сетевых возможностей ИСПОЛЬЗОВАНИЕ ДИРЕКТИВЫ USING Эта директива автоматически размещает создаваемый объект. Ком­ пилятор С# разобьет директиву на код, подобный этому: Surface try { backBuffer;

backBuffer = mySwapChain.GetBackBuffer(0, BackBufferType.Mono);

device.SetRenderTarget(0, backBuffer);

// Render some stuff } finally { if (backBuffer != null) backBuffer. Dispose();

} Использование директивы using дает существенный выигрыш в быст­ родействии, особенно для случаев многократного распределения неболь­ ших объемов данных в памяти (см. выше). Однако, это не решает про­ блему выделения дополнительной памяти для процедур обработки со­ бытий. Как правило, события и обработчики событий, содержащиеся в Управляемом DirectX, позволяют вам, как разработчику, не заботиться о размерах и времени существования объекта. Тем не менее, в зависимос­ ти от требований и задач приложения, иногда весьма полезно управлять свойствами объектов самостоятельно. В версии SDK Update Управляемого DirectX 9 мы имеем возможность отключать обработчик событий, если мы знаем заранее, что он нам не понадобится:

Device.IsUsingEventHandlers = false;

device = new Deviсе (...);

Значение «true» установлено по умолчанию, и мы можем изменить его в любое время. Устанавив «false» еще до создания устройства, мы полностью отключаем обработку событий в приложении. ОТКЛЮЧЕНИЕ ОБРАБОТКИ СОБЫТИЙ Использование этой особенности выключит автоматическое отсле­ живание событий. Использование внешнего управления обработ­ чиками потребует некоторого опыта и сноровки, поэтому при напи­ сании программ тщательно проверяйте ваши действия.

Глава 21. Методы достижения максимального быстродействия Итак, теперь в большинстве случаев мы можем использовать обра­ ботчики в нашем приложении, при необходимости отключая некоторые из них. Рассмотрим следующий код: // Device is currently using event handlers Device.IsUsingEventHandlers = false;

using(Surface backBuffer = mySwapChain.GetBackBuffer(0, BackBufferType.Mono)) { device.SetRenderTarget(0, backBuffer);

// Render some stuff. } // Allow device events to be hooked again Device.IsUsingEventHandlers = true;

Данная процедура отключает обработчики событий на то время, пока создается временная поверхность, и после этого включает их опять. В зависимости от наших требований, мы можем выключить обработку со­ бытий полностью или частично, добиваясь таким образом максималь­ ной эффективности. ИСПОЛЬЗОВАНИЕ УСТРОЙСТВА «THREADING DEVICE» Так как существует возможность отключения обработки событий, мы можем использовать в устройстве мультипоточный режим, вклю­ чив соответствующий флажок в используемой для создания устрой­ ства структуре параметров. Если мы собираемся управлять обра­ боткой событий «вручную», это можно делать в общем потоке: presentParams.ForceNoMultiThreadedFlag = true Эффективность методов Знание эффективности каждого из используемых методов в отдельно­ сти может существенно помочь при написании быстрого и эффективно­ го приложения. В Управляемом DirectX имеются случаи, когда, казалось бы, «безо­ бидный» код может вызвать значительные проблемы. Например, любой метод, принимающий в качестве параметра строку, должен разместить в памяти неуправляемый строковый тип и скопировать туда данный пара­ метр. Возьмем случай, где мы используем язык шейдеров HLSL для отобра­ жения нашей сцены, и будем переключать используемые для каждого кадра техники:

Часть VI. Добавление сетевых возможностей myEffeet.Technique = "TransformWorldSpace";

//Render some stuff myEffeet.Technique = "TransformAndAddGlow";

// Render some other stuff Каждый раз, когда мы переключаем методику, происходит перерасп­ ределение памяти и копирование названия методики. Решить данную проблему можно с помощью кэширования возвращаемых указателей, например: // On device creations, cache technique handles EffectHandle handlel = myEffect.GetTechnique("TransformWorldSpace");

EffectHandle handle2 = myEffeet.GetTechnique("TransformAndAddGlow");

// Later on, for every render myEffeet.Technique = handlel;

//Render some stuff myEffeet.Technique = handle2;

//Render some stuff Краткие выводы В этой главе мы рассмотрели следующие вопросы. • Операции преобразования типов boxing и unboxing. • Использование модели события и ее недостатки. • Эффективность используемых методов.

ЧАСТЬ VII ПРИЛОЖЕНИЯ Часть VII. Приложения Приложение А. Использование сборок диагностики Предположим, что вы только что завершили написание игры и гото­ вите ее к выходу в свет. Несмотря на положительные тестовые результа­ ты вашей испытательной группы, у вас все равно не было возможности проверить это приложение на всех системах без исключения. Допустим, что приблизительно 5 % всех пользователей, купивших игру, не смогут запустить ее по каким-либо причинам. У вас есть предположе­ ния, что это связано с типом используемой видеокарты. Попробуем про­ верить это, используя пространство имен Diagnostics. В этом приложении мы рассмотрим следующие вопросы. Перечисление всех диагностических опций и вывод сообщений о них. Проверка отдельных пунктов.

Перечисление всех опций в системе Пространство имен Diagnostics включает в себя те же опции, что и инструмент DxDiag. Используя его, можно узнать практически все о ва­ шей системе Данные диагностики размещаются иерархически. Можно использо­ вать рекурсивную функцию для перечисления всех возможных опций и объектов, находящихся в указанном корневом контейнере. Данная функ­ ция приведена в листинге АЛ и включена в образец DirectX SDK DxDiagOutput.

Листинг А.1. Выводимые данные диагностики. static void OutputDiagData(string parent, Container root) { try { foreach (PropertyData pd in root.Properties) { // Just display the data Console.WriteLine("{0}.{l} = {2}", parent, pd.Name, pd.Data);

} } catch try { Приложение А. Использование сборок диагностики foreach (ContainerData cd in root.Containers) { // Recurse all the internal nodes if (parent == null) OutputDiagData(cd.Name, cd.Container);

else OutputDiagData(parent + V + cd.Name, cd.Container);

} catch { } } // We are done with this container, we can dispose it. root.Dispose() ;

} Здесь выполняется поиск и отображение всех имеющихся свойств и опций, а также соответствующих им значений. В зависимости от свой­ ства значения будут либо строчными, либо булевыми, либо целочислен­ ными. При необходимости дополнительную информацию можно полу­ чить, используя метод pd.Data.GetType(). После того как мы получили список свойств данного контейнера, не­ обходимо проверить, содержит ли он дочерние контейнеры. Если да, про­ сматриваем каждый из них. Для начала необходимо создать хотя бы первый контейнер и вызвать указанный метод. Объект контейнера содержит только один конструк­ тор, имеющий в качестве параметра одну логическую переменную. Это значение используется, чтобы определить, должна ли диагностика вклю­ чать в себя информацию об аппаратных средствах WHQL (Windows Hardware Quality Labs). Получение этой информации может занять продолжительное время, лоэтому если в данной операции нет необходимости, ее лучше пропус­ тить. Образец DirectX SDK использует точку входа в программу, приве­ денную в листинге А.2. Листинг А.2. Запуск приложения. static void Main(string[] args) { try { // Just start our recursive loop with our root container. Don't worry // about checking Whql OutputDiagData(null, new Container(false));

} catch { // Something bad happened } } Часть VII. Приложения Таким образом, сформировав опции и свойства диагностики в вашем приложении, можно обнаружить искомый сбой в работе программы.

Проверка отдельных пунктов Как известно, полное перечисление всех опций и свойств (как в DxDiag, так и в DirectX SDK) может занимать немало времени. Допустим, мы хотели бы получить информацию относительно небольшого числа пунк-. тов. Ниже приведен пример кода, позволяющего получить определенную информацию о версии DirectX, которая использовалась в вашей системе:

Container parent = new Container(false);

Container child = parent.GetContainer("DxDiag_SystemInfo").Container;

int dxVersionMajor = (int)child.GetProperty("dwDirectXVersionMajor").Data;

int dxVersionMinor = (int)child.GetPropertyCdwDirectXVersionMinor").Data;

string dxVersionLetter = (string)child.GetProperty("szDirectXVersionLetter").Data;

Console. WriteLinePDX Version:(01.(1)(21",dxVersionMaj or,dxVersionMinor,dxVersionLetter);

Мы создаем корневой (без информации WHQL) контейнер. Затем, после образования дочернего контейнера DxDiag Systemlnfo, получаем три различных свойства версии DirectX: главный и вспомогательный номера версии, а также символ, связанный с этой версией (известна, на­ пример, версия DX8.1B). Префиксы в именах свойств могут определять заданный для объекта по умолчанию тип данных. Эти имена соответствуют Венгерскому при­ мечанию для разработчиков программ на С. Пункты с префиксом «sz» означают строки, пункты с префиксом «b» — логические значения. Все другие префиксы относятся к целочисленным значениям. Использование пространства имен Diagnostics может оказать суще­ ственную помощь при наладке и проверке вашего приложения. А воз­ можность обратной связи с пользователями позволяет отследить работу приложения на самых разнообразных системах и видеокартах. Как говорится, — «Лучше планировать проблему до, чем ждать и со­ жалеть после».

Приложение В. Воспроизведение музыки и видео Приложение В. Воспроизведение музыки и видео Как мы уже писали в начале книги, в окончательной версии Управляе­ мого DirectX SDK API мы исключили компоненты DirectMusic и DirectShow. Вместо этого мы включили пространство имен AudioVideoPlayback, по­ зволяющее запускать видеоролики и музыкальные файлы (например, фай­ лы mp3s или wma). В этом приложении мы обсудим следующие вопросы. • Простое воспроизведение звукового файла. • Простое проигрывание видео файла. Проигрывание видео файла в отдельном окне. Использование видео файла в качестве текстуры в 3D приложении.

Воспроизведение звукового файла Прежде чем начать работу со звуковыми и видео файлами, необходи­ мо добавить в приложение соответствующие ссылки на пространство имен AudioVideoPlayback, а также директиву using. При создании пространства имен AudioVideoPlayback нашей главной задачей ставилась простота в использовании. В итоге, мы можем запус­ тить воспроизведение музыкального файла с помощью одной короткой строки, например, код для запуска звукового файла «piano.mp3» выгля­ дит следующим образом:

Audio someAudio = new Audio("piano.mp3", true);

Имеются два основных класса, которые включены в пространство имен AudioVideoPlayback: класс Video и класс Audio. Эти названия довольно очевидны. Конструктор для каждого класса имеет два варианта. Первый параметр для каждого из вариантов представляет собой имя файла, кото­ рый мы хотим запустить. Другой параметр содержит булево значение, которое определяет автоматическое воспроизведение этого файла, в на­ шем случае «true» (по умолчанию «false», то есть файл не будет проиг­ рываться без соответствующей команды). Таким образом, мы создали звуковой объект, который будет запускаться автоматически. Конструкторы — не единственный способ создавать (или загружать) эти файлы. Наиболее близким является статический метод FromFile, ко­ торый принимает те же самые параметры и возвращает новый экземпляр класса. Имеется также статический метод FromUri, который ведет себя подобно методу FromFile, за исключением того, что он загружает данные с web-узла (или любого корректного URL). При этом воспроизведение при использовании URL может начаться еще до того, как файл загрузит­ ся полностью.

Часть VII. Приложения Существуют также методы Open и OpenUrl с теми же входными пара­ метрами. Они заменяют данные в уже созданном объекте на данные, по­ лученные из нового файла или web-узла.

Воспроизведение видео файла в отдельном окне В предыдущем примере для показа простоты работы мы использова­ ли звуковой файл. Допустим, мы хотим сделать то же самое с видео фай­ лом (например, вместо звукового файла запустить видео ролик «butterfly.mpg»). Для этого достаточно использовать следующий код: Video someVideo = new Video("butterfly.mpg", true);

Обратите внимание, что в этом случае воспроизведение будет осуще­ ствляться в новом окне. Немного изменив код, мы можем воспроизводить ролик в уже имею­ щемся окне. Для этого необходимо отключить автоматическое воспроиз­ ведение (убрав значение «true» в первой строке) и добавить еще две до­ полнительные строки: Video someVideo = new Video("butterfly.mpg");

someVideo.Owner = this;

someVideo.Play() ;

После отмены режима автоматического воспроизведения мы устанав­ ливаем «владельца» файла, который может быть любым объектом Windows-форм. Этим «владельцем» может быть непосредственно наша основная или ее дочерняя форма (например, picture box). После всех этих действий мы, наконец, запускаем видео. Обратите внимание, что теперь ролик прокручивается внутри окна, которое мы можем определить в свой­ ствах «владельца». Мы можем управлять звуком воспроизводимого видео файла (если он содержит звуковые данные) с помощью свойства Audio, отвечающего за соответствующий нашему видео звуковой объект, с которым мы можем оперировать, как с обычным звуковым объектом.

Использование видео файла в качестве текстуры В этом разделе мы покажем, как можно использовать видео файл в качестве ЗО-текстуры в нашем ЗО-приложении. Многие разработчики Приложение В. Воспроизведение музыки и видео используют в своих играх различные ролики из кинофильмов в качестве фона или деталей игры, и пространство имен AudioVideoPlayback доста­ точно хорошо справляется с этой задачей. В качестве наиболее простого случая возьмем фрагмент кинофильма и запустим его в полноэкранном режиме. Для начала просто загрузите видео файл в объект Video, установите опцию Fullscreen в значение «true» и запустите ролик. Пока все достаточно просто. Рассмотрим более сложный сценарий с использованием объектов текстурирования. Начнем по порядку. У нас имеется событие TextureReadyToRender, которое мы хотели бы отслеживать. Данное со­ бытие будет зафиксировано каждый раз, когда в видео файле будет появляться новый кадр, который мы хотим отобразить в качестве тек­ стуры. Параметр TextureRenderEventArgs процедуры обработчика события содержит вновь созданную текстуру, которая готова для отображения. Поскольку это событие может быть отслежено из различных потоков, необходимо убедиться в том, что мы не используем множественные по­ токи, одновременно обращающиеся к переменным (никто не знает, чем это может закончиться). Теперь все, что необходимо для запуска игры, — это вызвать для ви­ део объекта метод RenderToTexture. Пример, который поставляется с DirectX SDK, отобразит полную сце­ ну игры с той же самой скоростью смены кадров, как и в кинофильме. Во многих случаях это вполне приемлемо для разработчиков, поскольку видео-текстура явление довольно редкое. Но что необходимо сделать, если мы хотим использовать текстуру в нашем приложении, работающем в режиме реального времени. Вначале мы должны выяснить, где создана эта видео текстура: в сис­ темном пуле памяти или в заданном по умолчанию пуле. Это можно сде­ лать, проверив описание первого уровня текстуры. Если текстура была создана в заданном по умолчанию пуле памяти (наиболее общий случай), для создания текстуры в устройстве DirectSD необходимо использовать метод GetRenderTargetData и поверхность, со­ зданную нами в системном пуле памяти. Поскольку мы не можем ото­ бражать текстуры, созданные в системном пуле памяти, необходимо со­ здать используемую для рендеринга текстуру в заданном по умолчанию пуле памяти, например:

SurfaceDescription ds = е.Texture.GetLeyelDescription(O);

if (ds.Pool == Pool.Default) { systemSurface = device.CreateOffscreenPlainSurfacefds.Width, ds.Height, ds.Format, Pool.SystemMemory);

} 392 texture = new Texture(device, ds.Width, ds.Height, 1, Usage.Dynamic, ds.Format, Pool.Default);

Часть VII. Приложения Отсюда видно, что если видео текстура была создана в заданном по умолчанию пуле памяти, приложение создаст за кадром поверхность для отображения на нее нашей текстуры, а также текстуру в заданном по умолчанию пуле памяти, которую мы можем использовать для рендерин­ га нашей сцены. После того как это будет сделано, необходимо приме­ нить два метода, позволяющие устройству передать данные от одной тек­ стуры к другой:

using(Surface videoSurface = e.Texture.GetSurfaceLevel(O)) { using(Surface textureSurface = texture.GetSurfaceLevel(O)) { device.GetRenderTargetData(videoSurface, systemSurface);

device.UpdateSurface(systemSurface, textureSurface);

} } Как вы можете видеть, мы получаем данные рендеринга от нашей ви­ део текстуры и перемещаем их на нашу временную поверхность, создан­ ную в системной памяти. Оттуда мы перемещаем эту поверхность на нашу реальную текстуру. Только затем мы можем использовать текстуру, со­ зданную в заданном по умолчанию пуле памяти, в качестве текстуры для рендеринга нашей сцены. Поскольку в нашем случае текстура доступна для нескольких пото­ ков, мы должны гарантировать (используя семантику блокировки), что только один поток одновременно имеет доступ к этой текстуре. В завершение следует отметить, что класс AudioVideoPlayback разра­ ботан не как подобие DirectShow. Этот класс не предназначен для неко­ торых опций, таких как, например, захват видеоизображений. Описан­ ные объекты разработаны для наиболее общих и простых случаев заг­ рузки и воспроизведения звуковых и видео данных. Описанные классы эффективны и удобны, но при этом они не могут рассматриваться как полноценная альтернатива DirectShow API. На данный момент мы не имеем каких-либо запланированных обнов­ лений для этого пространства имен Управляемого DirectX.

Pages:     | 1 |   ...   | 4 | 5 ||



© 2011 www.dissers.ru - «Бесплатная электронная библиотека»

Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.