OpenGL

Описание: Разработка и отладка приложений. Упор на 3D-графику.

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 5 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#1 dyvniy » Пт, 15 мая 2015, 11:59:25

Толковый учебник по OpenGL 1.1,
который поддерживается всеми на свете десктопами, входит в любую винду и работает по RDP.
http://www.talisman.org/opengl-1.1/Reference/glCopyTexSubImage2D.html

Данная функция позволяет копировать в текстуру уже отрендеренное на экран.

Код: Выделить всё


    glBindTexture
(GL_TEXTURE_2D, texture[3]);
    glCopyTexSubImage2D(GL_TEXTURE_2D, 
        0
,      // mipmap level
        0, 0,   // offset
        0, 0,   // x, y
        800, 600// width, height
        );  // from window to texture
    glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB,  GL_UNSIGNED_BYTE, TextureImage);            // from texture to array
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 5 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#2 dyvniy » Ср, 3 июня 2015, 14:18:11

попиксельная прозрачность в фрагментном шейдере
http://apps4all.ru/post/04-10-15-effektivnaya-por ... zovaniem-fragmentnogo-shejdera
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 5 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#3 dyvniy » Сб, 12 сентября 2015, 10:59:18

Точность переменных в OpenGL ES шейдерах

Код: Выделить всё

int range[2], precision;
glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, range, &precision);

http://stackoverflow.com/questions/4414041/what-i ... 2-0-for-iphone-ipod-touch-ipad
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 5 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#4 dyvniy » Чт, 29 сентября 2016, 16:00:17

Сравнение конвейера шейдеров OpenGL ES 2.0 с Direct3D
(наверно в пользу мелко-мягких)
https://msdn.microsoft.com/ru-ru/windows/uwp/gaming/change-your-shader-loading-code
Спойлер
Стадия сборщика входных данных
Стадия вершинного шейдера
Стадия построителя текстуры
Концептуально конвейер шейдеров Direct3D11 очень похож на такой же в OpenGL ES2.0. Однако в терминах проектного решения API главными компонентами для создания и управления стадиями шейдеров являются части двух основных интерфейсов, ID3D11Device1 и ID3D11DeviceContext1. В этом разделе делается попытка сопоставить общие шаблоны API конвейера шейдеров OpenGL ES2.0 с их эквивалентами Direct3D11 в этих интерфейсах.

Обзор конвейера шейдеров Direct3D11

Объекты шейдера создаются с помощью методов в интерфейсе шейдера ID3D11Device1, таких как ID3D11Device1::CreateVertexShader и ID3D11Device1::CreatePixelShader.

Конвейер графики Direct3D11, который управляется экземплярами интерфейса ID3D11DeviceContext1, имеет следующие стадии.

Стадия сборщика входных данных. На стадии сборщика входных данных конвейеру поставляются данные (треугольники, линии и точки). Методы ID3D11DeviceContext1, поддерживающие эту стадию, имеют префикс "IA".
Стадия вершинного шейдера. На стадии вершинного шейдера обрабатываются вершины, обычно с выполнением таких операций, как преобразования, скиннинг и освещение. Вершинный шейдер всегда принимает одну входную вершину и создает одну выходную вершину. Методы ID3D11DeviceContext1, поддерживающие эту стадию, имеют префикс "VS".
Стадия потокового вывода. На стадии потокового вывода поток данных примитивов из конвейера направляется в память на своем пути к средству прорисовки. Поток данных может быть выходным или передаваться в средство программной прорисовки. Поток данных, выведенный в память, можно снова вернуть в конвейер как входные данные или обратно считать из ЦП. Методы ID3D11DeviceContext1, поддерживающие эту стадию, имеют префикс "SO".
Стадия средства программной прорисовки. Средство программной прорисовки обрезает примитивы, готовит их для построителя текстуры и определяет, как вызывать построители текстуры. Вы можете отключить растеризацию, указав конвейеру, что построитель текстуры отсутствует (задайте для построителя текстуры значение NULL с помощью ID3D11DeviceContext::PSSetShader), и отключив проверку глубины и набора элементов (задайте для DepthEnable и StencilEnable значение FALSE в D3D11_DEPTH_STENCIL_DESC). После отключения не будут обновляться связанные с растеризацией счетчики конвейера.
Стадия построителя текстуры. На стадии построителя текстуры принимаются интерполированные данные для примитива и генерируются данные для пикселей, такие как цвет. Методы ID3D11DeviceContext1, поддерживающие эту стадию, имеют префикс "PS".
Стадия слияния вывода. На стадии слияния вывода выходные данные различных типов (значения построителя текстуры, информация о глубине и наборе элементов) объединяются с содержимым целевого объекта отрисовки и буферов глубины и набора элементов для создания окончательного результата конвейера. Методы ID3D11DeviceContext1, поддерживающие эту стадию, имеют префикс "OM".
(Существуют также стадии для шейдеров геометрии, шейдеров поверхности, тесселяторов и шейдеров областей, но так как у них нет аналогов в OpenGL ES 2.0, поэтому здесь мы их обсуждать не будем.) Полный перечень методов для этих стадий см. на справочных страницах ID3D11DeviceContext и ID3D11DeviceContext1. ID3D11DeviceContext1 является расширением ID3D11DeviceContext для Direct3D 11.

Создание шейдера

В Direct3D ресурсы шейдера не создаются до компиляции и их загрузки. Ресурсы создаются, когда загружается HLSL. Таким образом, нет функции, являющейся прямым аналогом glCreateShader, которая создает инициализированный ресурс шейдера конкретного типа (например, GL_VERTEX_SHADER или GL_FRAGMENT_SHADER). Вместо этого шейдеры создаются после загрузки HLSL с конкретными функциями, такими как ID3D11Device1::CreateVertexShader и ID3D11Device1::CreatePixelShader, которые принимают тип и компилированный HLSL в качестве параметров.

OpenGL ES 2.0 Direct3D11
glCreateShader Вызовите ID3D11Device1::CreateVertexShader и ID3D11Device1::CreatePixelShader после успешной загрузки компилированного объекта шейдера (CSO), передавая их в этот объект CSO как буфер.
Компиляция шейдера

В приложениях для универсальной платформы Windows (UWP) шейдеры Direct3D должны предварительно компилироваться как файлы компилированного объекта шейдера (CSO-файлы) и загружаться с помощью одного из API файла среды выполнения Windows. (Классические приложения могут компилировать шейдеры из текстовых файлов или строк во время выполнения.) CSO-файлы создаются из любых HLSL-файлов, являющихся частью проекта Microsoft Visual Studio, и сохраняют те же имена только с расширением CSO-файла. Обеспечивайте их включение в ваш пакет при отправке!

OpenGL ES2.0 Direct3D11
glCompileShader Н/д. Компилируйте шейдеры в CSO-файлы в Visual Studio и включайте их в пакет.
Использование glGetShaderiv для получения состояния компиляции Н/д. Проверяйте по выходным результатам компилятора Visual StudioFX (FXC), имели ли место ошибки при компиляции. Если компиляция прошла успешна, создается соответствующий CSO-файл.
Загрузка шейдера

Как показано в разделе о создании шейдера, Direct3D11 создает шейдер, когда соответствующий CSO-файл загружается в буфер и передается в один из методов из следующей таблицы.

OpenGL ES 2.0 Direct3D11
ShaderSource Вызовите ID3D11Device1::CreateVertexShader и ID3D11Device1::CreatePixelShader после успешной загрузки компилированного объекта шейдера.
Настройка конвейера

В OpenGL ES2.0 есть объект "программа-шейдер", который содержит несколько шейдеров для выполнения. Отдельные шейдеры подключены к объекту программы-шейдера. Однако в Direct3D11 вы работаете непосредственно с контекстом отрисовки (ID3D11DeviceContext1) и создаете шейдеры в нем.

OpenGL ES 2.0 Direct3D11
glCreateProgram Н/д. Direct3D11 не использует абстракцию объекта программы-шейдера.
glLinkProgram Н/д. Direct3D11 не использует абстракцию объекта программы-шейдера.
glUseProgram Н/д. Direct3D11 не использует абстракцию объекта программы-шейдера.
glGetProgramiv Используйте созданную вами ссылку на ID3D11DeviceContext1.
Создайте экземпляр ID3D11DeviceContext1 и ID3D11Device1 с помощью статического метода D3D11CreateDevice.

Microsoft::WRL::ComPtr<ID3D11Device1> m_d3dDevice;
Microsoft::WRL::ComPtr<ID3D11DeviceContext1> m_d3dContext;

// ...

Код: Выделить всё

D3D11CreateDevice(
  
nullptr// Specify nullptr to use the default adapter.
  
D3D_DRIVER_TYPE_HARDWARE,
  
nullptr,
  
creationFlags// Set set debug and Direct2D compatibility flags.
  
featureLevels// List of feature levels this app can support.
  
ARRAYSIZE(featureLevels),
  
D3D11_SDK_VERSION// Always set this to D3D11_SDK_VERSION for Windows Store apps.
  
&device// Returns the Direct3D device created.
  
&m_featureLevel// Returns feature level of device created.
  
&m_d3dContext // Returns the device's immediate context.
); 

Установка окон просмотра

Установка окна просмотра Direct3D11 очень похожа на установку окна просмотра в OpenGL ES2.0. В Direct3D 11 вызовите ID3D11DeviceContext::RSSetViewports с настроенным CD3D11_VIEWPORT.

Direct3D11. Установка окна просмотра.

Код: Выделить всё

CD3D11_VIEWPORT viewport(
        
0.0f,
        
0.0f,
        
m_d3dRenderTargetSize.Width,
        
m_d3dRenderTargetSize.Height
        
);
m_d3dContext->RSSetViewports(1, &viewport); 

OpenGL ES 2.0 Direct3D11
glViewport
[
**CD3D11\_VIEWPORT**
](https://msdn.microsoft.com/library/windows/desktop/jj151722), [**ID3D11DeviceContext::RSSetViewports**](https://msdn.microsoft.com/library/windows/desktop/ff476480) |
Настройка вершинных шейдеров

Настройка вершинного шейдера в Direct3D11 выполняется, когда шейдер загружен. Универсальные объекты передаются как буферы констант с помощью ID3D11DeviceContext1::VSSetConstantBuffers1.

OpenGL ES 2.0 Direct3D11
glAttachShader ID3D11Device1::CreateVertexShader
glGetShaderiv, glGetShaderSource ID3D11DeviceContext1::VSGetShader
glGetUniformfv, glGetUniformiv
[
**ID3D11DeviceContext1::VSGetConstantBuffers1**
](https://msdn.microsoft.com/library/windows/desktop/hh446793). |
Настройка построителей текстуры

Настройка построителя текстуры в Direct3D11 выполняется, когда шейдер загружен. Универсальные объекты передаются как буферы констант с помощью ID3D11DeviceContext1::PSSetConstantBuffers1..

OpenGL ES 2.0 Direct3D11
glAttachShader ID3D11Device1::CreatePixelShader
glGetShaderiv, glGetShaderSource ID3D11DeviceContext1::PSGetShader
glGetUniformfv, glGetUniformiv
[
**ID3D11DeviceContext1::PSGetConstantBuffers1**
](https://msdn.microsoft.com/library/windows/desktop/hh404645). |
Генерация окончательных результатов

Когда конвейер завершает работу, результаты стадий шейдеров извлекаются в задний буфер. В Direct3D11, так же как и в Open GL ES2.0, это включает вызов команды рисования для вывода результатов в виде цветовой карты в задний буфер с последующей отправкой содержимого заднего буфера на экран.

OpenGL ES 2.0 Direct3D 11
glDrawElements
[
**ID3D11DeviceContext1::Draw**
](https://msdn.microsoft.com/library/windows/desktop/ff476407), [**ID3D11DeviceContext1::DrawIndexed**](https://msdn.microsoft.com/library/windows/desktop/ff476409) (или другие методы Draw\* в [**ID3D11DeviceContext1**](https://msdn.microsoft.com/library/windows/desktop/ff476385)). |
| eglSwapBuffers | IDXGISwapChain1::Present1 |

Перенос GLSL в HLSL

Языки GLSL и HLSL не имеют существенных отличий, за исключением поддержки сложных типов и некоторых отличий в общем синтаксисе. Многие разработчики считают простейшим способом переноса альтернативное именование общих инструкций и определений OpenGL ES2.0 для совмещения имен с их эквивалентами в HLSL. Отметим, что Direct3D использует версию модели шейдера для выражения набора функций HLSL, поддерживаемых графическим интерфейсом, тогда как OpenGL имеет другую спецификацию версии для HLSL. Следующая таблица должна дать вам примерное представление о наборе функций языка шейдера, определенных для Direct3D11 и OpenGL ES2.0, в терминах версии другого языка.

Язык шейдера Версия функций GLSL Модель шейдера Direct3D
Direct3D11 HLSL ~4.30. SM5.0
GLSL ES для OpenGL ES2.0 1.40. Другие (более старые) реализации GLSL ES для OpenGL ES2.0 могут использовать версии от 1.10 до1.30. Чтобы выяснить это, проверьте ваш исходный код с помощью glGetString(GL_SHADING_LANGUAGE_VERSION) или glGetString(SHADING_LANGUAGE_VERSION). ~SM2.0
Подробно о различиях двух языков шейдера, а также об общем сопоставлении синтаксиса см. в справочнике по сопоставлению GLSL с HLSL.

Перенос встроенных элементов OpenGL в семантику HLSL

Семантика Direct3D11 HLSL представляет строки, которые аналогично универсальным именам и именам атрибутов используются для идентификации значения, передаваемого между приложением и программой-шейдером. Хотя можно использовать любые из возможных строк, рекомендуется использовать строки, указывающие на использование, такие как POSITION или COLOR. Вы назначаете такую семантику при построении буфера констант или макета входного буфера. Можно также добавлять к семантике цифру от0 до7, чтобы использовать отдельные регистры для сходных значений. Например: COLOR0, COLOR1, COLOR2...

Семантика с префиксами "SV_" представляет системные значения, которые записываются вашей программой-шейдером. Ваше приложение (выполняющееся на ЦП) не может изменять их самостоятельно. Обычно они содержат значения, которые являются входными или выходными для другой стадии шейдера в конвейере графики или полностью генерируются ЦП.

Кроме того, выражения SV_ имеют разное поведение, когда они используются для указания входных или выходных результатов для стадии шейдера. Например, SV_POSITION (выход) содержит данные вершин, преобразованные на стадии вершинного шейдера, а SV_POSITION (вход) содержит значения позиций пикселей, интерполированные при растеризации.

Перечислим некоторые сопоставления для общих встроенных элементов шейдера OpenGL ES2.0:

Системное значение OpenGL Используйте семантику HLSL
gl_Position POSITION(n) для данных буфера вершин. SV_POSITION предоставляет позицию пикселя построителю текстуры и не может записываться вашим приложением.
gl_Normal NORMAL(n) для данных нормали, предоставленных буфером вершин.
gl_TexCoord[n] TEXCOORD(n) для данных UV-координат (ST в некоторой документации OpenGL) текстуры, которые предоставляются шейдером.
gl_FragColor COLOR(n) для данных цветов RGBA, которые предоставляются шейдеру. Отметим, что они обрабатываются аналогично данным координат. Семантика просто помогает понять, что они являются данными цветов.
gl_FragData[n] SV_Target[n] для записи из построителя текстуры в целевую текстуру или в другой буфер пикселей.
Метод, с помощью которого вы кодируете семантику, не совпадает с методом, используемым компонентами OpenGL ES2.0. В OpenGL вы можете получить доступ ко многим внутренним элементам без настройки или объявления. В Direct3D для использования конкретной семантики необходимо объявить поле в определенном буфере констант. Можно также объявить возвращаемое значение метода main() шейдера.

Приведем пример семантики, используемой в определении буфера констант:

Код: Выделить всё

struct VertexShaderInput
{
  
float3 pos POSITION;
  
float3 color COLOR0;
}; 


Код: Выделить всё

// The position is interpolated to the pixel value by the system. The per-vertex color data is also interpolated and passed through the pixel shader. 
struct PixelShaderInput
{
  
float4 pos SV_POSITION;
  
float3 color COLOR0;
}; 

Этот код определяет пару простых буферов констант

Вот пример семантики, используемой для определения значения, возвращаемого шейдером фрагментов:

Код: Выделить всё

// A pass-through for the (interpolated) color data.
float4 main(PixelShaderInput input) : SV_TARGET
{
  return 
float4(input.color,1.0f);

В этом случае SV_TARGET представляет расположение целевого объекта отрисовки, в который записывается цвет пикселей (определенный как вектор из четырех значений с плавающей точкой), когда заканчивается выполнение шейдера.

Подробнее об использовании семантики с Direct3D: Семантики HLSL.
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 5 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#5 dyvniy » Пн, 20 марта 2017, 18:36:31

Про глубинное устройство видеокарты, как с ней работать из биоса
https://habrahabr.ru/company/neobit/blog/176707/

Код: Выделить всё

Как запустить программу без операционной системы: часть 3: Графика
Блог компании НеоБИТ, Системное программирование*


В этой части попробуем сделать “невозможное”: научимся использовать графический дисплей без операционной системы. На самом деле это задача не из легких, особенно в случае работы в 32-х битном защищенном режиме, и особенно если хочется использовать приличное разрешение экрана а не 320x200x8. Но все по порядку: раз хотим графику – значит нужно работать с видеокартой.

Современные графические карты – это практически полноценные компьютеры по мощности не уступающие основному: тут и декодирование MPEG2 в качестве 1080p, поддержка 3D графики и шейдеров, технологии вроде CUDA, и многое другое. Это все выглядит весьма сложно. С другой стороны видеокарты – это всего лишь очередной PCI девайс, такой же, как и остальные. Это устройство мы даже “нашли” в предыдущей статье с номером класса устройства 0x03 (class_name=graphics adapter). Как и с любым, устройством с видеокартой можно работать при помощи портов ввода-вывода или MMIO областей памяти, а сама видеокарта может использовать DMA и прерывания для взаимодействия с основным процессором. Если посмотреть на диапазон портов ввода-вывода, доступных у видео карт, то мы увидим, что всего ей выделяется менее 50-ти байт – не так уж и много с учетом огромной функциональности, которой обладают современные видеокарты.



По стандарту VGA для работы с видеокартой будут использоваться именно два диапазона портов ввода вывода: 0x03B0-0x03BB и 0x3C0-0x3DF. Помимо этих диапазонов есть еще диапазон видеопамяти (0xA0000-0xBFFFF), отображаемой в основную память.


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

К сожалению, есть одно но: VGA — стандарт старый и рассчитан на использование простых графических дисплеев с малым, по современным меркам, разрешением экрана. В стандарте определено пару десятков различных графических и текстовых режимов (посмотреть все можно тут), среди которых самыми “крутыми” являются: 320x200 на 256 цветов и 640x480 на 16 цветов. При этом в стандарт входят только функции переключения режимов и функции для работы с палитрой. А где же нормальные разрешения экранов: 1920x1080 24 бита, ну или хотя бы 800x600 24 бита? Где поддержка MPEG2 графики? Где 3D графика? В стандарте VGA этого нет. Про графические режимы еще можно сказать, что они входят в стандарт SVGA работа с которым, осуществляется через расширения BIOS VBE, про которые речь пойдет дальше. А 3D, MPEG2, CUDA и другие модные фишки поддерживаются каждым производителем видеокарт по-своему. Предельное разрешение экрана в 640x480 в стандарте VGA, помимо рассчета на старые дисплеи, обусловлено размером видеопамяти: всего 128 килобайт (для работы с большим разрешением требуется гораздо больше места: 1920x1080 24бит – это более 6Mb). Значит нужно, чтобы была еще область видеопамяти большого размера, и у современных видео карт такая область есть:


Один из приведенных выше диапазонов используется как Linear Frame Buffer (LFB) – тоже видеопамять. Второй диапазон адресов используется для отображения сотен регистров и битовых полей, которые используются для настройки видеокарты. Причем набор регистров и способы работы с ними уникальны не только у каждого производителя видео карт — они могут значительно отличаться как между сериями, так и даже моделями одного производителя, и все это нужно, чтобы обеспечить работу CUDA, MPEG2, 3D,… Исходники всех графических драйверов в ОС Linux занимают более 8 мегабайт:


А вот пример драйвера видеокарты для одного из производителей:



Остальные содержат еще больше кода И это еще без дополнительных библиотек в Linux, которые используются в драйверах.

Значит, чтобы обеспечить поддержку 3D или потокового видео, нужно очень много поработать. Как говориться: если у вас есть еще одна жизнь, чтобы потратить ее, то …

Впрочем, в этой статье мы сосредоточимся на том, чтобы просто включить приличный графический режим и нарисовать в нем что-нибудь красивое: например, фрактал. Посмотрим насколько это просто.

Начнем с того, что приличные графические режимы относятся к стандарту SVGA и доступны для включения через расширение BIOS VBE (VESA BIOS Extension). VBE – это расширение BIOS, код которого располагается на самой видео карте и позволяет работать с ней. Стандарт расширений VBE включает в себя несколько функций, которыми можно пользоваться:
1. Получение информации о графическом устройстве. Поддерживается ли VESA, объем видео памяти на видео карте итд.
2. Получение по номеру режима информации о нем: разрешение экрана, указатель на LFB и битность.
3. Включение видео режима по его номеру.
4. Включение банка для видео режима по номеру банка, о них ниже.
5. Другие функции в том числе для управления палитрой, что нужно для 8-ми битных режимов.

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

Все режимы, поддерживаемые видео картой нумеруются по порядку от 0x000 до 0x1FF. Не все номера заняты и не все номера после 0x100 имеют в стандарте точное значение разрешения экрана: таким образом, по требуемому разрешению экрана номер еще нужно будет найти. Номера режимов до 0x100 определены стандартом VGA и полностью совпадают с ним. LFB – Linear Frame Buffer, это область видеопамяти, рассчитанная на большие разрешения экрана (обычно она располагается за пределами оперативной памяти, но до 4GB).

Без LFB для работы со всеми графическими режимами используется стандартная область видеопамяти с 0xA0000 по 0xC0000. В этом случае будет использоваться режим “банков”. Весь экран разбивается на нумерованные части (банки) и в каждый момент времени область видеопамяти указывает на одну из таких частей. То есть, прежде чем рисовать пиксель на экране, нужно будет установить номер банка, затем нарисовать пиксель, обращаясь к видеопамяти. Таким образом одна и та же область памяти может использоваться повторно для работы с различными областями на дисплее.



Использовать LFB проще и быстрее, поскольку ничего переключать не нужно, и весь дисплей отображается в LFB целиком. Видеопамять в LFB организована линейным образом: счет пикселей начинается построчно с верхнего левого угла экрана. Каждый пиксель представлен одним, двумя, тремя или четырьмя байтами в зависимости от битности текущего графического режима. Байты расположены подряд и в них закодирован цвет пикселя. Самые простые и приличные режимы это 32-х и 24-х битные (трех и четырех байтные). В этих режимах каждый канал цвета (Red, Green, Blue) представлены 1-м байтом. В 32-х битном режиме еще один байт зарезервирован и не используется (можно сказать, что он используется для выравнивания). Еще одна особенность с LFB: по стандарту, чтобы включить LFB в номере режима нужно установить еще один бит: mode_number | 0x4000.



Таким образом, используя функцию номер 2 (в приведенном выше списке), можно будет найти номер режима с LFB и включить его при помощи функции номер 3. Затем можно будет рисовать на экране просто записывая байтики со значениями RGB по нужному смещению в буфере LFB.
Пока все выглядит многообещающе, но, VBE это расширение BIOS и оно представляет собой 16-ти битный код для Real-Mode обрабатывающий определенную функцию BIOS (в нашем случае 10h). Получается, что надо использовать VBE (16 бит Real Mode) из привычного 32- бита Protected Mode, который мы получили в предыдущей статье. Существует три способа, как это сделать:
1. Переключиться в Real Mode, выполнить нужные действия, вернуться обратно в Protected Mode. Нужно писать функции перехода между режимами, сохранять состояние процессора, да и в общем случае нужно еще прерывания корректно обрабатывать.
2. Использовать расширение VBE через 16-ти битный интерфейс из Protected Mode. Для этого нужно настраивать таблицы дескрипторов, создавать call Gate, компилировать 16-ти битный код и настраивать еще одну дополнительную структуру по стандарту VBE. Тоже не очень удобно, к тому же не все видео карты такое расширение поддерживают.
3. Использовать эмулятор 16-бит Real Mode, который работает в Protected Mode. Единственное ограничение этого эмулятора в том, что трудно будет писать обработчики прерываний для самой видео карты, но нам это и не нужно, ибо все функции VBE прерывания видео карты не используют.

Третий способ выглядит самым простым, поскольку можно взять готовый эмулятор x86emu (простой и хорошо переносимый) и использовать его для вызова функций VBE.
Поработать, конечно, придётся, но мы сможем за относительно небольшое количество действий собрать программу, которая на любой современной видеокарте сможет, без операционной системы, включить графический режим и нарисовать фрактал. Пока без шейдеров и 3D но, зато графика.

! ВАЖНО!: Все дальнейшие действия могут успешно осуществляться только после успешного прохождения всех 6-ти шагов из первой части статьи “Как запустить программу без операционной системы”

Наш план:

1. Добавить несколько функций в common, которые нам понадобятся для x86emu и рисования фракталов.
2. Портировать x86emu.
3. Написать несколько функций для работы с VBE.
4. Написать функцию рисования фрактала.
5. Все объединить и запустить.

Приступим.

Шаг 1. Дополняем common стандартными функциями.


Сперва нужно добавить несколько функций работы с 64-х битными числами. Они понадобятся gcc для компиляции x86emu.

1. Добавляем следующие файлы в папку common: udivdi3.c, umoddi3.c, moddi3.c, qdivrem.c, divdi3.c. Их можно взять здесь.

2. Далее нужно добавить еще файл quad.h в include. Его так же можно взять из www.openbsd.org/cgi-bin/cvsweb/src/lib/libc/quad. В нем нужно заменить строки:

#include <sys/types.h>
#if !defined(_KERNEL) && !defined(_STANDALONE)
#include <limits.h>
#else
#include <sys/limits.h>
#endif


на строки:
#include “types.h”


3. Теперь, качаем библиотеку newlib.Она понадобится еще для одной функции. Из исходников библиотеки нужно скопировать файл newlib-2.0.0\newlib\libm\math\s_floor.c в папку common. В нем заменяем строку:
#include "fdlibm.h"

на строку:
#include "types.h"

4. Еще нужно добавить функции setjmp/longjmp. Эмулятор x86emu использует эти функции для обработки ошибок. Реализация этих функция приводится ниже, он была создана на основе реализации из newlib, но немного упрощена. Функция позволяет сохранить состояние процессора, а затем его восстановить. По сути похоже на ручную реализацию C++ исключений. Для того, чтобы эти функции появились в нашем коде, надо создать файл setjmp.s в common со следующим содержанием (немного простого ассемблера):
   .globl   setjmp
setjmp:
   movl   4(%esp),%ecx
   movl   4(%ebp), %edx
   movl   %edx, 0(%ecx)
   movl   %ebx, 4(%ecx)

       addl   $4,%ebp
       movl   %ebp, 8(%ecx)
       subl   $4,%ebp

       movl   (%ebp),%edx
       movl   %edx,12(%ecx)
   movl   %esi,16(%ecx)
   movl   %edi,20(%ecx)
   movl   %eax,24(%ecx)
   xorl   %eax,%eax
   ret

   .globl   longjmp
longjmp:

   movl   4(%esp),%edx
   movl   8(%esp),%eax
   movl   0(%edx),%ecx
   movl   4(%edx),%ebx
   movl   8(%edx),%esp
   movl   12(%edx),%ebp
   movl   16(%edx),%esi
   movl   20(%edx),%edi
   testl   %eax,%eax
   jnz   1f
   incl   %eax
1:   movl   %ecx,0(%esp)
   ret


Ассемблерный код просто сохраняет регистры в определнном порядке, а затем их восстанавливает.
5. Теперь функции нужно объявить для языка C. Для этого создаем файл setjmp.h в include со следующим содержанием:
#ifndef _SETJMP_H_
#define _SETJMP_H_

#define   _JBLEN   10      

typedef long jmp_buf[_JBLEN];

#ifdef __cplusplus
extern "C" {
#endif

int setjmp (jmp_buf);
void longjmp (jmp_buf, int);

#ifdef __cplusplus
}
#endif

#endif


6. Еще один важный набор функций, который потребуется, это функции работы со строками и памятью (memset, strlen итд.). Эти функции готовыми можно взять из bitvisor, поэтому нужно скачать исходники этого гипервизора. Из этих исходников скопируем файл core\string.s в common. В нем заменим строку:
.include "longmode.h"

на строку:
l
ongmode = 0


7. Далее нужно скопировать из bitvisor еще файл include\core\string.h в include. В нем заменить строку:
#include <core/types.h>

на строку:
#include "types.h"

8. Последнее, что понадобиться – это функции работы с портами ввода-вывода. Для этого добавляем файл io.h в каталог include, который без изменений можно взять из того же проекта bitvisor (в исходниках проекта он расположен в include\io.h).
9. Чтобы все скопилировалось, заменяем содержимое include\types.h на следующие:
#ifndef _TYPES_H
#define _TYPES_H

#define NULL 0
typedef unsigned int size_t;

typedef unsigned long ulong;
typedef unsigned long u32;
typedef unsigned short u16;
typedef unsigned char u8;

typedef unsigned long long uint64_t;
typedef unsigned long long u_int64_t;
typedef unsigned long uint32_t;
typedef unsigned long u_int32_t;
typedef unsigned short uint16_t;
typedef unsigned short u_int16_t;
typedef unsigned char uint8_t;
typedef unsigned char u_int8_t;

typedef  long __int32_t;
typedef unsigned long __uint32_t;

typedef unsigned long long u_quad_t;    /* quads */
typedef long long quad_t;
typedef quad_t * qaddr_t;
typedef unsigned long u_int;
typedef unsigned long uint;

typedef long long int64_t;
typedef long int32_t;
typedef short int16_t;
typedef char int8_t;


#define _QUAD_HIGHWORD 1
#define _QUAD_LOWWORD 0

#define __BEGIN_DECLS
#define __END_DECLS
#define __dead
#define __far

#define __HI(x) *(1+(int*)&x)
#define __LO(x) *(int*)&x

#define __P(a) a
#define CHAR_BIT        8          /* max # of bits in a "char" */

#define EXTRACT_WORDS(i0, i1, x) \
   i0 =  __HI(x); \
   i1 =  __LO(x);

#define INSERT_WORDS(x, i0, i1) \
   __HI(x) = i0; \
   __LO(x) = i1;

#define _BEGIN_STD_C
#define _END_STD_C

#define _EXFUN(a,b) a b

#endif

Эти изменения необходимы для определения ряда типов и макросов, которые нужны в используемых сторонних исходниках. Как видно по коду, все определения тривиальны.

Шаг 2. Портирование x86emu


X86emu входит в состав FreeBSD, поэтому ее нужно оттуда взять и доработать немного. Для этого создаем директорию x86emu в корне и из http://svnweb.freebsd.org/base/vendor-sys/x86emu/dist/ в эту папку копируем следующие файлы: x86emu.c, x86emu.h, x86emu_regs.h, x86emu_util.c.

Теперь, нужно внести несколько изменений в эти исходники:
1. В файле x86emu\x86emu.c заменить строки:
#include <dev/x86emu/x86emu.h>
#include <dev/x86emu/x86emu_regs.h>

На строки:
#include "x86emu.h"
#include "x86emu_regs.h"

2. В файле x86emu\x86emu.h заменить строки:
#include <sys/types.h>
#include <sys/endian.h>

#ifdef _KERNEL
#include <sys/systm.h>
#else
#include <setjmp.h>
#endif

На строки
#include "types.h"
#include "setjmp.h"


3. В файле x86emu\x86emu_util.c заменить строки:
#include <sys/param.h>
#include <sys/endian.h>
#include <dev/x86emu/x86emu.h>
#include <dev/x86emu/x86emu_regs.h>

На строки:
#include "x86emu.h"
#include "x86emu_regs.h"
#include "Io.h"
#define htole16(x)      ((uint16_t)(x))
#define htole32(x)      ((uint32_t)(x))
#define letoh16(x)      ((uint16_t)(x))
#define letoh32(x)      ((uint32_t)(x))


4. Далее необходимо в файл x86emu\x86emu_util.c добавить несколько функций перед функцией x86emu_init_default:
static uint8_t x86emu_inb(struct x86emu *emu, uint16_t port) {
    uint8_t val = 0;
   in8(port, &val);
    return val;
}

static void x86emu_outb(struct x86emu *emu, uint16_t port, uint8_t data) {
   out8(port, data);
}

static uint16_t x86emu_inw(struct x86emu *emu, uint16_t port) {
    uint16_t val = 0;
   in16(port, &val);
    return val;
}

static void x86emu_outw(struct x86emu *emu, uint16_t port, uint16_t data) {
   out16(port, data);
}

static uint32_t x86emu_inl(struct x86emu *emu, uint16_t port) {
    uint32_t val = 0;
   in32(port, &val);
    return val;
}

static void x86emu_outl(struct x86emu *emu, uint16_t port, uint32_t data) {
   out32(port, data);
}

Эти функции представляют собой обертки над функциями обращения к портамм ввода-вывода.

5. В самой же функции x86emu_init_default добавить следующие определения:
emu->emu_inb = x86emu_inb;
   emu->emu_inw = x86emu_inw;
   emu->emu_inl = x86emu_inl;
   emu->emu_outb = x86emu_outb;
   emu->emu_outw = x86emu_outw;
   emu->emu_outl = x86emu_outl;


Поскольку видеокарта – это устройство и VBE будет работать с ней через порты ввода-вывода при помощи ранее определенных функций, то эмулятору нужно сообщить о их наличии.

Шаг 3. Добавление функций для работы с BIOS.


Теперь можно использовать функции BIOS VBE через x86emu. Осталось сделать несколько функций которые непосредственно выполняют запрос к BIOS. Для этого создаем файл bios.c в папке common со следующим содержанием:

#include "types.h"
#include "bios.h"
#include "x86emu.h"

struct x86emu emulator;

void VBE_BiosInit(void)
{
    memset(&emulator, 0, sizeof(emulator));
    x86emu_init_default(&emulator);
    emulator.mem_base = (char *)0;
    emulator.mem_size = BIOS_SIZE;
}

void VBE_BiosInterrupt( BIOS_REGS *p_regs, u8 num )
{
    memcpy(&(emulator.x86), p_regs, sizeof(BIOS_REGS));
    x86emu_exec_intr(&emulator, num);
    memcpy(p_regs, &(emulator.x86), sizeof(BIOS_REGS));
}


И в папке include файл bios.h со следующим содержанием:
#ifndef _BIOS_H
#define _BIOS_H

#define BIOS_SIZE 0x100000
#define BIOS_HIGH_BASE 0xC0000
#define BIOS_HIGH_SIZE (0x100000 - 0xC0000)
#define BIOS_BDA_BASE 0x9fc00
#define BIOS_BDA_SIZE 0x400
#define VBE_BIOS_INFO_OFFSET      0x70000
#define VBE_BIOS_MODE_INFO_OFFSET 0x80000

typedef struct _BIOS_REGS
{
    u16 CS;
    u16 DS;
    u16 ES;
    u16 FS;
    u16 GS;
    u16 SS;
    u32 EFLAGS;
    u32 EAX;
    u32 EBX;
    u32 ECX;
    u32 EDX;
    u32 ESP;
    u32 EBP;
    u32 ESI;
    u32 EDI;
    u32 EIP;
} BIOS_REGS;

void VBE_BiosInit(void);
void VBE_BiosInterrupt( BIOS_REGS *p_regs, u8 num );

#endif


Таким образом мы определили функцию для инициализации работы с bios (VBE_BiosInit), которую нужно будет вызвать в начале работы и функцию вызова функции bios (VBE_BiosInterrupt). Название последней следует из того, что именно через инструкцию int (interrupt) происходит вызов функций BIOS в Real-Mode. При помощи этой функции можно вызывать функции VBE в соответствие со стандартом. Для вызова прерывания необходимо заполнить структуру с состоянием процессора и вызвать эмулятор. Эмлуятор начнет декодировать и эмулировать код из таблицы IVT и собственно кода BIOS. Инструкция за инструкцией эмулятор выполнит весь необходимый код обработчика int 10h. В процессе работы эмулятор будет вызывать функции работы с портами ввода-вывода, которые мы указали ранее, на шаге 2.

Шаг 4. Добавление функций для работы с VBE.


Теперь все готово, чтобы написать несколько функций для работы с VBE. Сперва добавим файл vbe.h, который будет содержать определения необходимых структур. Его можно взять из кода VirtualBox (http://www.virtualbox.org/svn/vbox/trunk/src/VBox/Devices/Graphics/BIOS/vbe.h). Заменим в нем строки:
#include "vgabios.h"
#include <VBox/Hardware/VBoxVideoVBE.h>


На строки:
#include "types.h"


Теперь создадим файл vbe.c в папке common со следующим содержанием:
#include "types.h"
#include "printf.h"
#include "string.h"
#include "bios.h"
#include "vbe.h"

ulong vbe_lfb_addr = 0;
ulong vbe_selected_mode = 0;
ulong vbe_bytes = 0;

VbeInfoBlock *VBE_GetGeneralInfo()
{
    BIOS_REGS regs;
    memset(®s, 0, sizeof(BIOS_REGS));
    regs.ECX = 0;
    regs.EAX = 0x4f00;
    regs.ES = VBE_BIOS_INFO_OFFSET >> 4;
    regs.EDI = 0x0;
    VBE_BiosInterrupt(®s, 0x10);
    if (regs.EAX != 0x4f)
        return NULL;
    return (VbeInfoBlock *)(VBE_BIOS_INFO_OFFSET);
}

ModeInfoBlock *VBE_GetModeInfo( ulong mode )
{
    BIOS_REGS regs;
    memset(®s, 0, sizeof(BIOS_REGS));
    regs.ECX = mode;
    regs.EAX = 0x4f01;
    regs.ES = VBE_BIOS_MODE_INFO_OFFSET >> 4;
    regs.EDI = 0x0;
    VBE_BiosInterrupt(®s, 0x10);
    if (regs.EAX != 0x4f)
        return NULL;
    return (ModeInfoBlock *)(VBE_BIOS_MODE_INFO_OFFSET);
}

int VBE_SetMode( ulong mode )
{
    BIOS_REGS regs;
    memset(®s, 0, sizeof(BIOS_REGS));
    if (mode >= 0x100)
    {
        regs.EBX = mode;
        regs.EAX = 0x4f02;
    }
    else
    {
        regs.EAX = mode;
    }
    VBE_BiosInterrupt(®s, 0x10);
    return (regs.EAX == 0x4f);
}

int VBE_Setup(int w, int h)
{
    uint32_t m = 0;
   
    printf("\nVBE: test started");
    VBE_BiosInit();
    memset((char *)VBE_BIOS_INFO_OFFSET, 0, sizeof(VbeInfoBlock));
    memset((char *)VBE_BIOS_MODE_INFO_OFFSET, 0, sizeof(ModeInfoBlock));

    VbeInfoBlock *p_info = VBE_GetGeneralInfo();
    int vbe_support = (p_info != NULL);
    if (vbe_support == 0)
    {
        printf("\nVBE: not supported");
        return 0;
    }
   
    vbe_support = (p_info->VbeVersion >= 0x200);
    vbe_support = vbe_support && (p_info->VbeSignature.SigChr[0] == 'V');
    vbe_support = vbe_support && (p_info->VbeSignature.SigChr[1] == 'E');
    vbe_support = vbe_support && (p_info->VbeSignature.SigChr[2] == 'S');
    vbe_support = vbe_support && (p_info->VbeSignature.SigChr[3] == 'A');
    if (vbe_support == 0)
    {
        printf("\nVBE: not supported");
        return 0;
    }

    //Try to find  mode
int found = 0;
    for (m = 0x0; m < 0x200; m++)
    {
        ModeInfoBlock *p_m_info = VBE_GetModeInfo(m);
        if (p_m_info != NULL)
   {
            printf("\nVBE: %x %dx%dx%d at %x", m,
      p_m_info->XResolution,
      p_m_info->YResolution,
      p_m_info->BitsPerPixel,
      p_m_info->PhysBasePtr);
           
       if (p_m_info->PhysBasePtr != 0
      && p_m_info->XResolution == w
      && p_m_info->YResolution == h
      && (p_m_info->BitsPerPixel == 24 || p_m_info->BitsPerPixel == 32))
            {
found = 1;
                vbe_selected_mode = m;
                vbe_lfb_addr = p_m_info->PhysBasePtr;
      vbe_bytes = p_m_info->BitsPerPixel / 8;
                printf("\nVBE: FOUND GOOD %dx%dx%d -> %x at %x", w, h, vbe_bytes, vbe_selected_mode, vbe_lfb_addr);
            }
        }
    }
return found;
}


Рассмотрим подробнее функции, объявленые в этом файле:
• VBE_GetGeneralInfo. Эта функция проверяет наличие VBE у видео карты. Она использует функцию BIOS и проверяет возвращаемые значения в соответствие со спецификацией.
• VBE_GetModeInfo. Эта функция спрашивает у видео карты информацию о режиме по номеру. Возвращает информацию об этом режиме в виде структуры. Параметры передаваемые VBE_BiosInterrupt обусловлены спецификацией VBE.
• VBE_SetMode. Эта функция просто включает нужный режим по номеру. Параметры передаваемые VBE_BiosInterrupt обусловлены спецификацией VBE.
• VBE_Setup. Самая важная функция: она перебирает все режимы и ищет тот, который удовлетворяет указанному в параметрах разрешению экрана. Так же функция ищет режим только 24-х и 32-х битные и с поддержкой LFB. В результате поисков она заполняет три глобальных переменные:
o vbe_lfb_addr – адрес LFB. В него можно срезу писать данные для рисования на экране.
o vbe_selected_mode – номер выбранного режима, чтобы его можно было включить.
o vbe_bytes – количество байтов на пиксель (3 или 4).

Все готово для рисования.

Шаг 5. Добавление функции рисования фрактала.

Приступаем к самому интересному: рисуем фрактал. Будем рисовать фрактал на множестве Жюлиа. За основу для рисования фрактала был взят код из этой замечательной статьи. Для рисования фрактала, можно просто создать файл fractal.c в корневой директории с исходниками, со следующим содержанием:

#include "types.h"
#include "printf.h"
#include "string.h"

int VBE_SetMode( ulong mode );
int VBE_Setup(int w, int h);
double floor(double x);

extern ulong vbe_lfb_addr;
extern ulong vbe_selected_mode;
extern ulong vbe_bytes;

int HSVtoRGB(int _h, int _s, int _v)
{
double h = (double)_h / 255.0, s = (double)_s / 255.0, v = (double)_v / 255.0;
double r = 0;
double g = 0;
double b = 0;

if (s == 0)
{
    r = v;
    g = v;
    b = v;
}
else
{
    double varH = h * 6;
    double varI = floor(varH);
    double var1 = v * (1 - s);
    double var2 = v * (1 - (s * (varH - varI)));
    double var3 = v * (1 - (s * (1 - (varH - varI))));

    if (varI == 0)
    {
        r = v;
        g = var3;
        b = var1;
    }
    else if (varI == 1)
    {
        r = var2;
        g = v;
        b = var1;
    }
    else if (varI == 2)
    {
        r = var1;
        g = v;
        b = var3;
    }
    else if (varI == 3)
    {
        r = var1;
        g = var2;
        b = v;
    }
    else if (varI == 4)
    {
        r = var3;
        g = var1;
        b = v;
    }
    else
    {
        r = v;
        g = var1;
        b = var2;
    }
  }
  return ((int)(r * 255) << 16) | ((int)(g * 255) << 8) | (int)(b * 255);
}
 

void DrawFractal(void)
{
    int x = 0, y = 0, w= 800, h = 600;

    if (!VBE_Setup(w, h))
   return;
    if (!VBE_SetMode(vbe_selected_mode | 0x4000))
return;


    double cRe, cIm;
    double newRe, newIm, oldRe, oldIm;
    double zoom = 1, moveX = 0, moveY = 0;
    int color;
    int maxIterations = 300;

    cRe = -0.7;
    cIm = 0.27015;

    for(x = 0; x < w; x++)
    for(y = 0; y < h; y++)
    {
        newRe = 1.5 * (x - w / 2) / (0.5 * zoom * w) + moveX;
        newIm = (y - h / 2) / (0.5 * zoom * h) + moveY;

        int i;
        for(i = 0; i < maxIterations; i++)
        {
            oldRe = newRe;
            oldIm = newIm;
            newRe = oldRe * oldRe - oldIm * oldIm + cRe;
            newIm = 2 * oldRe * oldIm + cIm;
            if((newRe * newRe + newIm * newIm) > 4) break;
        }
        color = HSVtoRGB(i % 256, 255, 255 * (i < maxIterations));

   // Draw pixel
        *(int *)((char *)vbe_lfb_addr + y * w * vbe_bytes + x * vbe_bytes + 0) = color & 0xFFFFFF;
    }
}

Разберем этот код чуть подробнее. Во-первых, этот код содержит необходимые определения:

int VBE_SetMode( ulong mode );
int VBE_Setup(int w, int h);
double floor(double x);

extern ulong vbe_lfb_addr;
extern ulong vbe_selected_mode;
extern ulong vbe_bytes;


Во-вторых, определена функция преобразования цвета HSVtoRGB. Она нужна, чтобы все выглядело красиво. Ее реализация была взята отсюда

Наконец, самая главная функция рисования фрактала DrawFractal. В ней нужно отметить несколько моментов:

1. Сперва в ней определены параметры экрана которые будут использоваться для режима и рисования:
int x = 0, y = 0, w= 800, h = 600;
Вы можете менять эти функции на свой вкус.

2. Затем настраивается VBE:
    if (!VBE_Setup(w, h))
return;


3. Потом включается найденный графический режим:
   if (!VBE_SetMode(vbe_selected_mode | 0x4000))
return;


4. Далее рисуется фрактал. Для установки точки на экране используется простая запись числа в память, при этом вычислив правильное смещение:
*(int *)((char *)vbe_lfb_addr + y * w * vbe_bytes + x * vbe_bytes + 0) = color & 0xFFFFFF;


Теперь, когда все готово, надо вызвать эту функцию из main, иначе мы не увидим результаты своих трудов. Вносим изменения в kernel.c:

#include "printf.h"
#include "screen.h"
#include "types.h"

void DrawFractal(void);

void main()
{   
    clear_screen();
    printf("\n>>> Hello World!\n");
    DrawFractal();
}


Шаг 6. Доработка makefile и запуск


Остается доработать только makefile, чтобы все скомпилировалось. Для этого внесем следующие изменения:
1. Обновим OBJFILES:

OBJFILES = \
   loader.o  \
   common/printf.o  \
   common/screen.o  \
   common/bios.o  \
   common/vbe.o  \
   common/qdivrem.o  \
   common/udivdi3.o  \
   common/umoddi3.o  \
   common/divdi3.o  \
   common/moddi3.o  \
   common/setjmp.o  \
   common/string.o  \
   common/s_floor.o  \
   x86emu/x86emu.o  \
   x86emu/x86emu_util.o  \
   fractal.o \
   kernel.o


2. Добавим еще одну директорию include, для этого внесем изменение в строку:
$(CC) -Ix86emu -Iinclude $(CFLAGS) -o $@ -c $<


3. Добавим цель для компиляции ассемблера:
.s.o:
   as -o $@ $<


4. Теперь можно пересобрать проект:
make rebuild
sudo make image


5. Запускаем проект, чтобы убедиться, что все работает:
sudo qemu-system-i386 -hda hdd.img


Если все сделано правильно, то мы должны увидеть вот такую красоту:


Как и в предыдущих частях статьи, при помощи команды dd можно скопировать образ hdd.img на флешку и проверить работу программы на реальном компьютере.

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

Ссылки на следующие статьи цикла:
"Как запустить программу без операционной системы: часть 4. Параллельные вычисления"
"Как запустить программу без операционной системы: часть 5. Обращение к BIOS из ОС"
"Как запустить программу без операционной системы: часть 6. Поддержка работы с дисками с файловой системой FAT"
Изображение

dyvniy M
Автор темы, Администратор
Администратор
Аватара
dyvniy M
Автор темы, Администратор
Администратор
Возраст: 41
Репутация: 1
Лояльность: 1
Сообщения: 3579
Зарегистрирован: Ср, 10 октября 2012
С нами: 11 лет 5 месяцев
Профессия: Программист
Откуда: Россия, Москва
ICQ Сайт Skype ВКонтакте

#6 dyvniy » Чт, 31 августа 2017, 16:44:04

sudo apt-get install mesa-utils
glxinfo | grep "OpenGL version"
glxgears

sudo apt-get install glmark2
glmark2

https://benchmark.unigine.com/heaven
Изображение


Название раздела: Программирование (под Desktop и Android)
Описание: Разработка и отладка приложений. Упор на 3D-графику.

Быстрый ответ


Введите код в точности так, как вы его видите. Регистр символов не имеет значения.
Код подтверждения
:) ;) :hihi: :P :hah: :haha: :angel: :( :st: :_( :cool: 8-| :beee: :ham: :rrr: :grr: :* :secret: :stupid: :music: Ещё смайлики…
   

Вернуться в «Программирование (под Desktop и Android)»

Кто сейчас на форуме (по активности за 15 минут)

Сейчас этот раздел просматривают: 7 гостей