Програмиране за графични ускорители

Борислав Станимиров

Hello, World

  • Борислав aka Боби aka iboB
  • Предимно С++ програмист
  • Предимно програмист на игри
  • Занимавам се с open source

История

Графични ускорители

Графичен ускорител

  • Също така наричан GPU
  • По идея е устройство за растеризация на триъгълници
  • По дизайн е ray caster (а не ray tracer)
  • По имплементация е много процесори

CPU

GPU

Програмиране за GPU

  • API/SDK:
  • OpenGL - върви просто навсякъде (kinda)
  • Direct3D - подмножество на DirectX. Windows only.
  • Vulkan и D3D12 - тънък слой
  • Други...
  • Пишат се предимно от производителите на хардуер

Проста графика

colorCube
rotateColorCube
rotateWireCube
rotateColorCube
rotateTextureCube
rotateLambertCube
rotateLambertNormSphere
rotateLambertNormWireSphere
rotateLambertNormSphere

Прости термини

  • Vertex - връх
    • Атрибути: позиция, текстурни координати, нормали...
    • Вертексни буфери
  • Примитиви - триъгълници, отсечки, точки
  • Индексни буфери
  • Текстури - изображения

Прости хитрости

Проста графика

rotateLambertNormSphere
rotateLambertSphere
rotateLambertWireSphere
rotateLambertSphere

Предишното с псевдокод

vertices = {
    0, 0, 0, // позиция
    1, 1, // текстурни координати
    0, 1, 0, // нормала
    ...
};
SetVertexBuffer(vertices, vertices.size() * 8 * sizeof(float));
SetVertexFormat(POSITION | TEXCOORD | NORMAL);
SetIndexBuffer({0,1,2,0,2,3...});
SetLightPos({2,4,7});
SetEyePos({sin(t),0,cos(t)}); // върти камера
SetTexture(someTexture);
Draw(TRIANGLES);
                

Fixed pipeline: функция за всичко :(

Flexible Pipeline

  • ...или програмируем
  • Код за видеокартата
    • Асемблер (всичко започва от там)
    • OpenGL и Vulkan: GLSL
    • Direct3D: HLSL
    • Cg, Fx, Много други...
  • Шейдъри
    • Вертексен
    • Фрагментен / Пикселен

Flexible pipeline псевдокод

vertices = {0, 0, 0, 1, 1, 0, 1, 0, ...};
auto stride = 8*sizeof(float);
SetVertexBuffer(vertices, vertices.size() * stride);
SetAttribPointer("aPosition", 0, FLOAT3, stride);
SetAttribPointer("aTC", 3*sizeof(float), FLOAT2, stride);
SetAttribPointer("aNormal", 5*sizeof(float), FLOAT3, stride);
UseVertexShader(vsCode);
UseFragmentShader(fsCode);
SetUniform("uView", viewMatrix);
SetUnirorm("uTexture", someTexture);
SetUniform("uLightPos", {2,4,7});
SetIndexBuffer({0,1,2,0,2,3...});
Draw(TRIANGLES);
                

Simple Vertex Shader


attribute vec4 aPos;

uniform mat4 uCam;

void main() {
    gl_Position = uCam * aPos;
}
                

Color Fragment Shader


void main() {
    gl_FragColor = vec4(0, 0.9, 0.3, 1);
}
                

Uniform Color Fragment Shader


uniform vec4 uColor;

void main() {
    gl_FragColor = uColor;
}
                

Lambert Vertex Shader

attribute vec4 aPos;
attribute vec2 aTexCoord;
attribute vec3 aNormal;

uniform mat4 uCam;
uniform vec3 uLightPos;

varying vec2 vTexCoord;
varying vec3 vNormal;
varying vec3 vLightDir;

void main() {
    vTexCoord = aTexCoord;
    vNormal = aNormal;
    vLightDir = uLightPos - aPos.xyz;
    gl_Position = uCam * aPos;
}
                

Lambert Fragment Shader

attribute vec4 aPos;
varying vec2 vTexCoord;
varying vec3 vNormal;
varying vec3 vLightDir;

uniform sampler2D uDiffuse;

void main() {
    float shade = dot(normalize(vNormal), normalize(vLightDir));
    shade = saturate(shade);

    vec4 color = texture2D(uDiffuse, vTexCoord);
    gl_FragColor = vec4(color.xyz * shade, 1);
}
                

Хм... цветове и вектори са едно и също?

Текстура с цвят

Текстура с нормали

Normal Mapping Vertex

//...
attribute vec3 aNormal;
attribute vec3 aTangent;
attribute vec3 aBitangent; // optional

uniform vec3 uLightPos;
varying vec3 vSSLightDir; // SS = surface space

void main() {
    mat3 tbn = mat3(
        aTangent,
        aBitangent /* cross(aNormal, aTangent) */,
        aNormal);
    vec3 lightDir = uLightPos - aPos.xyz;
    vSSLightDir = lightDir * tbn;
    //...
}
                

Normal Mapping Pixel


// ...
vec3 ssNormal = normalize(2.0 * texture2D(uNormalMap, vTexCoord).xyz - 1.0);
float shade = dot(ssNormal, normalize(vSSLightDir));
// и всичко е както преди
// ...
                

Хитра графика

rotateLambertGraniteCube
angleLambertGraniteCube
rotateLightGraniteCube
rotateLightNormalGraniteCube
rotateLightNormalSpecularGraniteCube
rotateLightNormalSpecularBrickCube

Още гаври

  • Пикселни
    • Пречупване на светлината
    • Отражения и environment mapping
    • Ambient occlusion
    • Сенки
  • Вертексни
    • Morphing
    • Скелетни анимации
  • Други шейдъри
    • Hull shader
    • Domain shader
    • Geometry shader

GPGPU

Не само графика

  • Видеокартата е много мощно животно
    • 1920x1080
    • = 2 073 600
    • @ 60fps
  • General Purpose GPU
  • Текстура към текстура

Ерата на GPGPU

  • Compute shaders
  • OpenCL
  • CUDA
  • CYCL и хетерогенно програмиране
  • Близкото бъдеще - WebCL

OpenCL Blur

__kernel void avg_blur(__global float* result, __global float* input)
{
    int x = get_global_id(0), y = get_global_id(1);
    int n = y*get_global_size(0) + x;

    int local_size = get_local_size(1) * get_local_size(0);
    int ln = get_local_id(1)*get_local_size(0) + get_local_id(0)

    __local accum[local_size];
    accum[ln] = input[n];
    barrier(CLK_LOCAL_MEM_FENCE); // --------------------

    result[i] = 0;
    for(int i=0; i<local_size; ++i)
        result[i] += accum[i];
    result[i] /= local_size;
}
                

GPGPU не е панацея

  • Трансферът на данни не е безплатен
  • Някои алгоритми не са паралелизируеми


Но ако имаме еднакви операции върху голямо количество данни,
най-вероятно ще спечелим!

Благодаря


Борислав Станимиров / ibob.github.io / @stanimirovb


Тази презентация е тук: http://ibob.github.io/slides/code4gpus-bg/
Презентацията е лицензирана с Creative Commons Признание 3.0
Лицензи на допълнителните материали Виж тук