DirectX 8.1 в MASM32: Урок 2 — Архив WASM.RU

Все статьи

DirectX 8.1 в MASM32: Урок 2 — Архив WASM.RU

Урок 2. Запуск приложения в полноэкранном режиме.

Привет ! Сегодня мы научимся запускать приложение в полноэкранном режиме. Но сначала о некоторых упущениях с моей стороны :(. В самом первом уроке, где представлен каркас приложения, присутствует ошибка, проявляющаяся в Windows 98 . При запуске окно не появляется на экране и программа висит в памяти ( у меня так было, когда решил проверить программу под Win 98 ). В Windows XP такой проблемы нет. Все из-за того, что я неосторожно обошелся с параметром hCursor в структуре WNDCLASSEX, поместив ID обычной стрелки сразу, а не получая его от Windows в процессе работы приложения. Чтобы ликвидировать это недоразумение, необходимо перед RegisterClassEx вставить следующие строчки:

Invoke	LoadCursor, NULL, IDC_ARROW
Mov	hCursor, eax

После этого у меня все заработало :). Можно их и не писать, а занести в hCursor ноль, но тогда в окошке будет курсор в виде часов. Также можно курсор вообще отрубить. Не сомневаюсь, что, если у вас возникла эта проблема, вы сразу разобрались. Однако лучше обьявить об ошибке поздно, чем никогда. Чтобы избежать досадных багов в будущем, я буду проверять работу приложений и в 98 и в XP ( к NT и 2000 у меня доступа нет ).

Теперь комментарии к уроку номер 1.

Как вы могли заметить, я не проверяю коды возврата при вызове функций Direct3D, кроме тех случаев где это действительно необходимо. Конечно это плохо, если возникнет ошибка, то придется долго искать какая функция не сработала. Решение принято для того, чтобы меньше засорять исходник ( вы и сами можете добавить обработку ошибок ). Результатом такого шага является или идеальная работа приложения, или его полная неработоспособность при запуске. Аппаратура также бывает разная, и то, что работает на моем ускорителе, может не работать у вас. Особое внимание прошу обратить на CreateDevice и передаваемые ей значения, например, D3DDEVTYPE_HAL, D3DCREATE_SOFTWARE_VERTEXPROCESSING и т.д. Тоже самое относиться и к другим важным функциям. Если видеокарта не поддерживает какие-то параметры, то запуск приложения закончится крахом. В общем, в случае неудачи пробуйте и экспериментируйте, а не ругайте дядю, который написал хрен знает что ( сам иногда ругаюсь, если не получается ;) ). Cпециально для таких случаев я и привожу подробную справку.

Хотелось бы заметить еще об одной важной детали: включаемые файлы INC. Начиная с этого урока, я буду включать в исходник только те INC файлы, которые необходимы именно для него. Советую собрать их все в одной папке ( у меня это \MASM32\DirectX81\ ) и в исходнике менять мои пути на ваши ( это относиться и к LIB файлам ). Внутри каждого INC файла есть дата его последней правки. Так что смотрите и своевременно заменяйте их на более свежие ( они будут вкладываться в исходник в обязательном порядке ).

Ну вот вроде бы ничего не забыл. Переходим к уроку...

Запуск приложения в полноэкранном режиме почти не отличается от запуска в окошке. Все что нам нужно, это заполнить несколько новых параметров в структуре D3DPRESENT_PARAMETERS. Соответственно берем первый урок и после небольших изменений все готово.

В оригинальном хелпе рекомендуется создавать окошко таким размером, какое разрешение мы хотим включить. Т.е. включаем 640х480 - значит создаем окошко 640х480 и т.п. Также его рекомендуется помещать на экран в координаты 0,0.

Для большей наглядности я прописал содержимое структуры D3DPRESENT_PARAMETERS в секции DATA. Описание ее было в предыдущем уроке.

Для нас важно занести в нее следующее:

BackBufferWidth	dd	APP_X				; Ширина ( разрешение по горизонтали )
BackBufferHeight	dd	APP_Y			; Высота ( разрешение по вертикали )
BackBufferFormat	dd	D3DFMT_X8R8G8B8	; Формат используемой поверхности 
BackBufferCount	dd	3				; число Back буферов. 
...						
SwapEffect		dd	D3DSWAPEFFECT_FLIP 	; Эффект обмена BackBuffer
...
Windowed				dd	FALSE		; 0 - полноэкранный
FullScreen_RefreshRateInHz		dd	100		; Частота обновления экрана в Hz
FullScreen_PresentationInterval	dd	1		; Показывать не превышая частоту

Формат поверхности я взял D3DFMT_X8R8G8B8. Можете использовать любой другой, который нравится. Число BackBuffer'ов равно 3, годится и 2. SwapEffect тоже выбирайте на вкус. В поле Windowed обязательно надо занести 0. В FullScreen_RefreshRateInHz ставите число герц, поддерживаемое данным режимом ( главное не переборщить ). Ну и PresentationInterval не забудьте.

Далее все как обычно: создаем устройство и работаем.

Вроде бы, на первый взгляд, все отлично. Но стоит нам нажать ALT+TAB, переключаясь на другую задачу, и затем снова перейти к нашему приложению, то возникнет небольшая проблема. Называется она "проблемой потерянного устройства" или LostDevice и решается легко. Всего то надо добавить пару лишних строчек кода.

Пишем в функции Render_Scene:

    d3dev8   TestCooperativeLevel, pd3dDevice		; Проверяем кооперацию
    cmp	   eax, D3DERR_DEVICELOST			; Выход если устройство потеряно
    jne   noreset    					
    ret
								
    noreset:    							
    cmp	   eax, D3DERR_DEVICENOTRESET		; Устройство сброшено или нет
    jne   noreset2

    d3dev8   Reset, pd3dDevice, ADDR BackBufferWidth	; Сбрасываем
    noreset2:

Справка

 TestCooperativeLevel параметров не передается.

 Метод возвращает D3D_OK если все прошло успешно. 
 Если нет, то D3DERR_DEVICELOST - устройство потеряно и не может быть 
 восстановлено. Рендеринг невозможен.
 И D3DERR_DEVICENOTRESET - устройство не может быть сброшено

 Reset передается указатель на структуру D3DPRESENT_PARAMETERS ( параметр не может быть нулем )

 Метод возвращает D3D_OK если все прошло успешно. 
 Если нет, то D3DERR_INVALIDCALL, D3DERR_OUTOFVIDEOMEMORY или D3DERR_OUTOFMEMORY

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

Добавив эти строчки, мы можем смело переключаться с задачи на задачу, и при возвращении в наше приложение все будет восстанавливаться и работать дальше !

Напоследок предлагаю вам небольшую задачу. Данный кусок кода выполняется в функции Render_Scene всегда. Т.е. если у нас приложение выдает, например, 1000 FPS, то проверка потери устройства происходит 1000 раз. Такой расклад никуда не годится, потому что реально устройство оказывается сброшеным только тогда, когда мы нажимаем ALT+TAB и переключаемся на другую задачу. Отсюда вывод: надо сделать так, чтобы проверка осуществлялась вне цикла отрисовки сцены, а конкретно в сообщении, когда приложение получает, например, фокус и только тогда сбрасывать устройство.

Я пробовал такой вариант и он почти работоспособен. Вам его не предлагаю, так как он имеет маленькое неудобство. Если кто-то сможет решить задачу, то я с удовольствием бы взглянул на результат.

Вот и все. В следующем уроке мы создадим диалог для определения всех доступных режимов экрана, и тогда приложение будет по настоящему правильным.

P.S. Все еще только начинается ...

Мужики, вы чего такие стеснительные. Не верю, что всем все нравиться. Высказывайте свое мнение. Учту его при дальнейшей работе.

Успехов всем в труде и обороне :)

Все вопросы мыльте сюда mybox@aib.ru

2002-2013 (c) wasm.ru