Алгоритм тестирования производительности сети 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Алгоритм тестирования производительности сети



 

Задача тестирования сводится к тому, чтобы переслать между сервером и клиентом данные известной длины и измерить время которое на это потребуется. Так как имеется две программы, возникает задача синхронизации. Как программы узнают, что тестирование начато или закончено?

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

 

Ход работы

 


Лабораторная работа № 4.
Определение производительности графического процессора

 

Цель работы: определение производительности графического процессора при помощи операций визуализации текстурированого трёхмерного объекта.

 

Необходимые знания:

— навыки матричных преобразований;

— навыки работы с библиотекой DirectX (уметь инициализировать Direct3D);

навыки программирования в среде MS Visual C++.

 

 

Определение производительности графического процессора.

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

В связи с этим необходимо установить минимальные требования к системе. Для этого создадим программу, которая выполняет простейшие операции по визуализации 3х мерного объекта и подсчитывает количество расчётов в секунду, для определения производительности. Количество кадров (расчетов) или FPS на прямую зависит от производительности графического процессора, таким образом, мы определим его возможности. Для отображения графической информации в реальном времени без потерь, необходимо чтобы количество FPS не уменьшалось ниже 30. При тестировании графического ядра зададимся этим критерием.

 

За "трехмерный объект" возьмем правильную четырехугольную пирамиду, а за текстуру - подредактированную фотографию каменной стены (первоначальный вариант текстуры взят с сайта http://www.3dcafe.com/, на котором собрано огромное количество различных бесплатных для скачивания изображений).

 

 

Создадим следующие функции:

Функция WinMain() – главная функция программы, с неё начинается выполнение

Функция Initialization() –проверка инициализации всех функций

Функция InitD3D() – инициализация графического режима

Функция InitTexture() – инициализация текстуры

Функция InitScene – инициализация сцены

Функция DoMatrices() – построение матриц преобразований

Функция RenderScene() – визуализация сцены

Функция Deinitialization() – корректное завершения работы приложения

Функция MessageProc() – обработка сообщений

 

Функция WinMain()

Главная функция нашего приложения. Содержит цикл обработки сообщений, также функции инициализация и деинициализация приложения.

 

if (Initialization(hWnd))

{

ShowWindow(hWnd, nCmdShow);

UpdateWindow(hWnd);

MSG msg;

ZeroMemory(&msg, sizeof(msg));

While (msg.message!= WM_QUIT)

{

if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

else

RenderScene();

}

}

Deinitialization();

 

 

Если инициализация проходит успешно, окно приложения отображается на экране и начинается цикл обработки сообщений, иначе деинициализация и выход из программы. Функция PeekMessage() проверяет очередь сообщений для текущей нити процесса и, если таковые имеются, помещает одно из них в структуру MSG. Флаг PM_REMOVE говорит о том, что сообщение из очереди удаляется, т.е. на нас возлагается ответственность его корректной обработки в любом случае. Это мы и делаем в следующих двух строчках. Если же очередь сообщений пуста, вызывается функция RenderScene(), которая, согласно ее названию, рендерит очередной кадр сцены. Как только поступает сообщение WM_QUIT, происходит деинициализация приложения (функция Deinitialization()).

 

Функция Initialization()

Осуществляет грамотную (т.е. с учетом ошибок) инициализацию D3D, текстур и сцены. Замечу, что все функции нашего приложения, в которых может возникнуть ошибка, имеют тип BOOL.

Рассмотрим программный код:

 

CurrentFilter = D3DTEXF_NONE;

if (!InitD3D(hWnd)) return FALSE;

if (!InitTexture())

{

MessageBox(NULL, "Не найден файл текстуры!","Ошибка", MB_ICONERROR);

return FALSE;

}

if (!InitScene())return FALSE;

return TRUE;

 

Что такое CurrentFilter? Забегая вперед, скажем, что наша программа будет использовать фильтрацию текстур, причем можно будет переключаться между различными фильтрами. Первая строка кода говорит, что по умолчанию фильтром является D3DTEXF_NONE, т.е. фильтрация не используется. Затем вызываются три функции дополнительной инициализации, причем, если хотя бы в одной из них возникнет ошибка, функция Initialization() вернет значение FALSE, что в свою очередь остановит выполнение программы и приведет к деинициализации. Если функция InitTexture() возвращает значение FALSE, значит не удалось найти файл текстуры и перед аварийным завершением программы необходимо вывести предупредительное сообщение:

 

MessageBox(NULL, "Не найден файл текстуры!", "Ошибка", MB_ICONERROR);

 

Функция InitD3D()

Согласно названию, эта функция инициализирует D3D. Перед созданием устройства рендеринга, необходимо настроить параметры D3DPRESENT_PARAMETERS. К параметрам, которые мы использовали в предыдущем приложении добавилось лишь две строки:

 

p_p.EnableAutoDepthStencil = TRUE;

p_p.AutoDepthStencilFormat = D3DFMT_D16;

 

Первая говорит о том, что при рендеринге будет использоваться Depth Buffer ("буфер глубины"), причем D3D будет управлять им автоматически.

Далее в программе создается устройство рендеринга:

 

if(FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, 3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &p_p, &g_pD3DDevice)))

{

if(FAILED(g_pD3D->CreateDevice(D3DADAPTER_DEFAULT, 3DDEVTYPE_REF, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &p_p, &g_pD3DDevice)))

return FALSE;

}

 

Здесь программа пытается выбрать наиболее оптимальный режим для работы. Сначала она пробует создать аппаратное устройство рендеринга, т.е. когда все обсчеты графики ведет исключительно видеокарта (D3DDEVTYPE_HAL), но если это не удается (причин может быть много, но чаще всего она одна - старая видеокарта) D3D работает в режиме программной эмуляции, что прискорбно отражается на скорости и качестве графики. Едем дальше: Задается режим "выбраковки" тыльных сторон полигонов (подробней мы рассмотрим это дальше - в функции InitScene()):

 

g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);

 

Включаем равномерное освещение всей сцены:

 

g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);

 

Включаем поддержку Z-Buffer'а

 

g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE);

 

Может возникнуть вопрос, для чего может понадобиться отключение Z-Buffer'а. Одно из применений - вывод на экран текста, который должен перекрывать все остальное.

Хотелось бы также подробней рассказать про функцию SetRenderState() интерфейса IDirect3DDevice8. Эта функция вообще незаменима, т.к. с ее помощью можно задать огромное количество различных настроек, влияющих на процесс рендеринга. Вот описание этой функции:

 

HRESULT SetRenderState(D3DRENDERSTATETYPE State, DWORD Value);

 

State - тип изменяемого параметра

Value - новое его значение

 

Функция InitTexture()

Эта функции выполняет только одно действие - загружает текстуру из файла в память для дальнейшего использования. Используем функцию D3DXCreateTextureFromFile(), которую программисты Microsoft написали за нас. Префикс "D3DX-" этой функции, говорит о том, что она взята из библиотеки D3DX. Эта вспомогательная библиотека включает в себя очень много полезных функций, как для математических операций (в основном, работы с матрицами), так и для загрузки изображений, формирования стандартных геометрических объектов (сфера, куб и т.п.) и многого другого.

Вот описание функции D3DXCreateTextureFromFile():

 

HRESULT D3DXCreateTextureFromFile(LPDIRECT3DDEVICE8 pDevice,

LPCSTR pSrcFile,

LPDIRECT3DTEXTURE8* ppTexture);

pDevice - указатель на устройство рендеринга

pSrcFile - текстовая строка, содержащая путь к файлу-текстуре

ppTexture - адрес переменной, которая будет содержать указатель на текстуру

 

Функция InitScene()

Здесь происходит инициализация единственного объекта в сцене - четырехугольной пирамиды.

Как известно, в основании правильной четырехугольной пирамиды (SABCD) лежит квадрат (ABCD). Если сторона квадрата «a», то высота пирамиды h=a/sqrt(2). Теперь, зная «a», мы можем задать в трехмерном пространстве координаты всех пяти вершин пирамиды. В DirectX используется левосторонняя (left-handed) система координат (СК). Направление оси Z в левосторонней и правосторонней СК можно определить, пользуясь правилом соответственно левой и правой руки. Вот, как с помощью этого правила найти куда направлена ось Z в левосторонней СК: вытяни левую руку ладонью вверх и собери четыре пальца (все, кроме большого) вместе в плоскости ладони. Большой палец расположи перпендикулярно остальным четырем (тоже в плоскости ладони). Теперь, если направить ось X вдоль четырех пальцев, а ось Y - вверх, то большой палец укажет направление оси Z.

Координаты вершин пирамиды в пространстве можно записать следующим образом:

 

float a=6.0;

#define vertA {-a/2, a/2, 0.0f, 0xffffffff, 0.0f, 1.0f,}

#define vertB {-a/2, -a/2, 0.0f, 0xffffffff, 0.0f, 0.0f,}

#define vertC {a/2, -a/2, 0.0f, 0xffffffff, 1.0f, 0.0f,}

#define vertD {a/2, a/2, 0.0f, 0xffffffff, 1.0f, 1.0f,}

#define vertS {0.0f, 0.0f, (float)(a/sqrt(2)), 0xffffffff, 0.5f, 0.5f,}

 

В D3D трехмерную модель можно задать различными способами. В нашем случае будем использовать TRIANGLELIST для которого фигура задается последовательностью треугольников. Когда задается треугольник, Direct3D сам определяет его лицевую и тыльную стороны по порядку следования вершин в массиве. При рендеринге, D3D автоматически "выбраковывает" тыльные стороны треугольников. Это заметно повышает скорость работы приложения. Но необходимо указать D3D в каком именно порядке задаются вершины лицевой стороны треугольников. Это делается с помощью той самой функции SetRenderState().

Итак, чтобы описать culling (выбраковку) тыльных сторон треугольников, вершины которых расположены в массиве по часовой стрелке (clockwise), необходимо написать следующее:

 

g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);

 

Для хранения вершин в D3D используются Vertex Buffer'ы (в дальнейшем, VB. Не путать с Visual Basic'ом. В зависимости от конкретной программы, VB'ы могут быть разных форматов. Например, если требуется написать программу, которая рисует на экране набор одноцветных точек, то для задания любой из точек требуется три числа, содержащих ее координаты в пространстве. Если точки должны отличаться по цвету, вводим четвертый параметр - цвет точки. Единственная сложность - мы как-то должны "сообщить" D3D в каком именно формате хранятся вершины в массиве, чтобы в процессе рендеринга не возникло путаницы. Впервые это нужно сделать в момент создания VB, затем перед рендерингом. Формат задается в виде комбинации флагов D3DFVF_*, полный список которых приведен в документации к D3D8. Нам же понадобятся лишь 3 флага:

 

#define D3DFVF_MYVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)

 

D3DFVF_XYZ - вершина задается тремя координатами в пространстве (а может задаваться и четырьмя - при D3DFVF_XYZRHW)

D3DFVF_DIFFUSE - вершина содержит цвет, который влияет на рассеяние света

D3DFVF_TEX1 - вершина содержит две текстурные координаты

Т.к. запись D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1 интерпретируется компилятором в точности, как и D3DFVF_XYZ|D3DFVF_TEX1|D3DFVF_DIFFUSE, значит порядок расположения данных в памяти в этом месте программы не задается. На программиста накладываются обязательства следовать схеме расположения данных, приведенной в руководстве D3D8 (раздел "About Vertex Formats").

Теперь нужно занести вершины всех полигонов пирамиды в память. Для этого создаем массив из вершин и заполняем его данными:

 

MYVERTEX Vertices[] ={ vertS, vertA, vertD,

vertS, vertB, vertA,

vertS, vertC, vertB,

vertS, vertD, vertC,};

 

Следующий шаг - нужно создать буфер вершин (VB) требуемого размера и формата. Пирамида будет отображена на экране так, что ее нижнего основания не будет видно, значит можно обойтись лишь 4-мя полигонами вместо 6-ти. Создание VB производится функцией CreateVertexBuffer:

 

HRESULT CreateVertexBuffer(UINT Length, DWORD Usage, DWORD FVF,

D3DPOOL Pool, IDirect3DVertexBuffer8** ppVertexBuffer);

Length - длина VB в байтах

Usage - дополнительная информация о VB, которую D3D использует для создания оптимального VB

FVF - формат вершин, которые будут храниться в VB

Pool - в какой памяти создавать VB (можно создать его как в видеопамяти, так и в RAM)

ppVertexBuffer - адрес переменной, которая будет содержать указатель на созданный VB

 

Всего для хранения полигонов пирамиды используется 4*3*sizeof(MYVERTEX) байт (4 полигона, по 3 вершины в каждом).

 

if(FAILED(g_pD3DDevice->CreateVertexBuffer(4*3*sizeof(MYVERTEX),

0, D3DFVF_MYVERTEX, D3DPOOL_DEFAULT, &g_pVB)))

{

return FALSE;

}

 

Остается заполнить буфер вершинами. Для операций заполнения в DX (не только в D3D) используется пара команд Lock() и Unlock(). Команда Lock() возвращает адрес памяти, по которому расположен первый байт буфера. При этом вся память, отведенная под буфер как бы "запирается", и становится недоступной для других приложений. Операция "отпирания" памяти производится командой Unlock(). После запирания памяти, скопируем данные с помощью функции языка Си memcpy().

 

VOID* pVertices;

if(FAILED(g_pVB->Lock(0,sizeof(Vertices), (BYTE**)&pVertices, 0)))

return FALSE;

memcpy(pVertices, Vertices, sizeof(Vertices));

g_pVB->Unlock();

 

Функция DoMatrices()

У нас есть трехмерное пространство сцены, которое содержит вершины всех объектов, есть камера - глаз, с помощью которого мы видим это пространство, а также плоскость экрана монитора, на которую осуществляется проектирование. Все это ("мир", камера, операция проектирования) может быть выражено тремя матрицами: World Matrix (мировая матрица), View Matrix (видовая матрица) и Projection Matrix (проекционная матрица).

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

Функция D3DXMatrixIdentity() строит единичную матрицу.

Функция D3DXMatrixRotationZ() строит матрицу вращения относительно оси Z на заданный угол.

Функция D3DXMatrixLookAtLH() строит видовую матрицу. Параметры этой функции задают точку, в которую будет смотреть камера. Постфикс -(LH) говорит о том, что матрица будет действительна для левосторонней системы координат (аналогично -(RH) для правосторонней)

Функция D3DXMatrixPerspectiveFovLH() строит проекционную матрицу.

Для того, чтобы "заставить" устройство рендеринга использовать только что созданные нами матрицы, существует функция SetTransform():

 

HRESULT SetTransform(D3DTRANSFORMSTATETYPE State,

CONST D3DMATRIX* pMatrix);

State - тип матрицы, которую нужно изменить (мировая, видовая, и т.д.)

pMatrix - указатель на "матрицу-заменитель":)

 

Вот что нам требуется от каждой из матриц:

a) Мировая

Сделаем так, чтобы пирамида равномерно вращалась вокруг оси Z:

 

D3DXMATRIX matWorld;

D3DXMatrixIdentity(&matWorld);

D3DXMatrixRotationZ(&matWorld, GetTickCount()/1024.0f);

g_pD3DDevice->SetTransform(D3DTS_WORLD, &matWorld);

 

b) Видовая

Камера должна смотреть на пирамиду сбоку, причем не должно быть видно нижнего основания пирамиды:

 

D3DXMATRIX matView;

D3DXMatrixLookAtLH(&matView, &D3DXVECTOR3(5.0f, 5.0f, 6.5f),

&D3DXVECTOR3(0.0f, 0.0f, 1.0f),

&D3DXVECTOR3(0.0f, 0.0f, 1.0f));

g_pD3DDevice->SetTransform(D3DTS_VIEW, &matView);

 

c) Проекционная

 

D3DXMATRIX matProj;

D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI/3, 1.0f, 1.0f, 100.0f);

g_pD3DDevice->SetTransform(D3DTS_PROJECTION, &matProj);

Здесь D3DX_PI/3 - это поле зрения (field of view) камеры.

 

 

Функция RenderScene()

Собственно, здесь и происходит рендеринг сцены. Как всегда, он начинается с очистки окна и Z-Buffer'а:

 

g_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,

D3DCOLOR_XRGB(100, 100, 100), 1.0f, 0);

 

Затем, подготавливаем D3D к началу сцены.

 

g_pD3DDevice->BeginScene();

 

Пересчитываем матрицы:

 

DoMatrices();

 

Теперь настроим текстуру. Еще раз вернемся к месту, где задаются вершины пирамиды:

 

float a=6.0;

#define vertA {-a/2, a/2, 0.0f, 0xffffffff, 0.0f, 1.0f,}

#define vertB {-a/2, -a/2, 0.0f, 0xffffffff, 0.0f, 0.0f,}

#define vertC {a/2, -a/2, 0.0f, 0xffffffff, 1.0f, 0.0f,}

#define vertD {a/2, a/2, 0.0f, 0xffffffff, 1.0f, 1.0f,}

#define vertS {0.0f, 0.0f, (float)(a/sqrt(2)), 0xffffffff, 0.5f, 0.5f,}

 

0xffffffff (белый цвет) - цветовой "вес" вершины. Белый цвет означает, что текстура в данной вершине будет того же цвета и яркости, что и в оригинале. Если заменить цвет на 0x00000000, то вершина будет черной и при рендеринге вокруг нее образуется черное пятно. Последние два числа являются текстурными координатами.

У нас будет одноуровневая текстура, первым и единственным уровнем которой будет загруженная ранее g_pTexture:

 

g_pD3DDevice->SetTexture(0, g_pTexture);

 

При рендеринге на текстуры могут накладываться фильтры, что сглаживает многие недостатки. Изюминкой программы является то, что тип фильтра можно менять прямо во время исполнения (клавишами F1, F2, F3, F4).

 

g_pD3DDevice->SetTextureStageState(0, D3DTSS_MAGFILTER, CurrentFilter);

g_pD3DDevice->SetTextureStageState(0, D3DTSS_MINFILTER, CurrentFilter);

g_pD3DDevice->SetTextureStageState(0, D3DTSS_MIPFILTER, CurrentFilter);

 

Перед рендерингом из VB, необходимо задать сам буфер и формат вершин, что делается следующими двумя строками:

 

g_pD3DDevice->SetStreamSource(0, g_pVB, sizeof(MYVERTEX));

g_pD3DDevice->SetVertexShader(D3DFVF_MYVERTEX);

 

Все готово для рендеринга! Делается это всего одной функцией DrawPrimitive(). Первый параметр говорит о том, в каком виде хранятся в VB вершины. В нашем случае, объект задается последовательностью треугольников (D3DPT_TRIANGLELIST). Второй параметр говорит о том, с какой по номеру вершины из VB начинать отрисовку. Третий параметр - количество примитивов (в нашем случае треугольников), которые требуется отрендерить.

 

g_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 4);

 

Завершаем сцену:

 

g_pD3DDevice->EndScene();

 

Отображаем бэк-буфер на экране:

 

g_pD3DDevice->Present(NULL, NULL, NULL, NULL);

 

Функция Deinitialization()

Для освобождения большинства (если не всех) видов ресурсов в D3D используется функция Release():

 

if(g_pTexture!= NULL) g_pTexture->Release();

if(g_pVB!= NULL) g_pVB->Release();

if(g_pD3DDevice!= NULL) g_pD3DDevice->Release();

if(g_pD3D!= NULL) g_pD3D->Release();

 

Также, перед завершением работы нужно освободить ранее зарегистрированный класс окна:

 

UnregisterClass("PyramidClass", wclass.hInstance);

 

Функция MessageProc()

Программа будет обрабатывать только два типа сообщений: WM_KEYDOWN (нажата клавиша) и WM_DESTROY (уничтожено окно приложения).

При поступлении сообщения WM_KEYDOWN получаем виртуальный код нажатой клавиши:

 

case WM_KEYDOWN:

int VK = (int)wParam;

 

Из клавиш, обрабатываем только Esc, F1, F2, F3, F4. При нажатии на Esc программа должна завершиться. Если нажата клавиша F1-F4, должен смениться тип фильтра (переменная CurrentFilter) и заголовок окна:

 

switch(VK)

{

case VK_ESCAPE: PostQuitMessage(0);

return 0;

case VK_F2: CurrentFilter = D3DTEXF_POINT;

SetWindowText(hWnd, "D3D Pyramid (Filter=D3DTEXF_POINT)");

break;

}

 

При поступлении сообщения WM_DESTROY, выполняются действия, аналогичные производящимся при обработке клавиши Esc:

 

case WM_DESTROY: PostQuitMessage(0);

return 0;

 

Далее проект необходимо откомпилировать.

 

Ход работы

 

1. Реализовать программный код обеспечивающий:

— вывод на экран трёхмерного объекта

— наложение текстур на объект

— использование линейной, точечной и анизотропной фильтрации

— расчет количества кадров в секунду (FPS)

2. Оформить протокол.

 



Поделиться:


Последнее изменение этой страницы: 2016-08-15; просмотров: 216; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.149.242.253 (0.182 с.)