Поделиться через


Программирование одного или нескольких потоков (Direct3D 9)

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

Использование потоков

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

  • Вершина состоит из n потоков.
  • Поток состоит из m элементов.
  • Элемент имеет значение [положение, цвет, нормали, координата текстуры].

Метод IDirect3DDevice9::SetStreamSource привязывает буфер вершин к потоку данных устройства, создавая связь между данными вершин и одним из нескольких портов потока данных, которые передают примитивные функции обработки. Фактические ссылки на потоковые данные не возникают до вызова метода рисования, например IDirect3DDevice9::D rawPrimitive.

Сопоставление входных элементов вершин с входными регистрами вершин для программируемых шейдеров вершин определяется в объявлении шейдера, но элементы входных вершин не имеют определенной семантики об их использовании. Интерпретация входных элементов вершин запрограммирована с помощью инструкций шейдера. Функция шейдера вершин определяется массивом инструкций, применяемых к каждой вершине. Регистры выходных данных вершин явно записываются в , используя инструкции в функции шейдера.

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

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

struct CUSTOMVERTEX_TEX1
{
    FLOAT x, y, z;      // The untransformed position for the vertex
    DWORD diffColor;    // The vertex diffuse color    
    DWORD specColor;    // The vertex specular color
    float tu_1, tv_1;   // Texture coordinates for a single texture
};
 
struct CUSTOMVERTEX_TEX2
{
    FLOAT x, y, z;      // The untransformed position for the vertex
    DWORD diffColor;    // The vertex diffuse color    
    DWORD specColor;    // The vertex specular color
    float tu_2, tv_2;   // Texture coordinates for multitexturing
};

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

struct CUSTOMVERTEX_TEX2
{
    FLOAT x, y, z;      // The untransformed position for the vertex
    DWORD diffColor;    // The vertex diffuse color    
    DWORD specColor;    // The vertex specular color
    float tu_1, tv_1;   // Texture coordinates for a single texture
    float tu_2, tv_2;   // Texture coordinates for multitexturing
};

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

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

// Multistream vertex
// Stream 0, pos, diffuse, specular
struct POSCOLORVERTEX
{
    FLOAT x, y, z;
    DWORD diffColor, specColor;
};
#define D3DFVF_POSCOLORVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_SPECULAR)
// Stream 1, tex coord 0
struct TEXC0VERTEX
{
    FLOAT tu1, tv1;
};
#define D3DFVF_TEXC0VERTEX (D3DFVF_TEX1)

// Stream 2, tex coord 1
struct TEXC1VERTEX
{
    FLOAT tu2, tv2;
};
#define D3DFVF_TEXC1VERTEX (D3DFVF_TEX0)

Объявление вершины будет следующим:

// Multitexture - multistream

D3DVERTEXELEMENT9 dwDecl3[] = 
{
    {0, 0,  D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_POSITION, 0},
    {0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_COLOR, 0},
    {0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_COLOR, 1},
    {1,  0, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_TEXCOORD, 0},
    {2,  0, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_TEXCOORD, 1},
    D3DDECL_END()
};

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

LPDIRECT3DVERTEXDECLARATION9 m_pVertexDeclaration;
g_d3dDevice->CreateVertexDeclaration(dwDecl3, &m_pVertexDeclaration);

m_pd3dDevice->SetVertexDeclaration(m_pVertexDeclaration);

Примеры сочетаний

Однопотоковый диффузный цвет

Параметры объявления вершин и потока для отрисовки диффузного цвета будут выглядеть следующим образом:

D3DVERTEXELEMENT9 dwDecl3[] = 
{
    {0,  0,  D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_POSITION, 0},
    {0, 12,  D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_COLOR, 0 ,
    {0, 16,  D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_COLOR, 1},
    D3DDECL_END()
};
   m_pd3dDevice->SetStreamSource(0, m_pVBVertexShader0, 0,
                                      sizeof(CUSTOMVERTEX));
   m_pd3dDevice->SetStreamSource(1, NULL, 0, 0);
   m_pd3dDevice->SetStreamSource(2, NULL, 0, 0);

Два потока с цветом и текстурой

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

D3DVERTEXELEMENT9 dwDecl3[] = 
{
    {0, 0,  D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_POSITION, 0},
    {0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_COLOR, 0},
    {0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_COLOR, 1},

    {1,  0, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_TEXCOORD, 0},

    D3DDECL_END()
};
   m_pd3dDevice->SetStreamSource(0, m_pVBPosColor, 0,
                                           sizeof(POSCOLORVERTEX));
   m_pd3dDevice->SetStreamSource(1, m_pVBTexC0, 0,
                                           sizeof(TEXC0VERTEX));
   m_pd3dDevice->SetStreamSource(2, NULL, 0, 0);

Два потока с цветом и двумя текстурами

Параметры объявления вершин и потока для отрисовки двух текстур с несколькими текстурами будут выглядеть следующим образом:

D3DVERTEXELEMENT9 dwDecl3[] = 
{
    {0, 0,  D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_POSITION, 0},
    {0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_COLOR, 0},
    {0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_COLOR, 1},

    {1,  0, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_TEXCOORD, 0},

    {2,  0, D3DDECLTYPE_FLOAT2,   D3DDECLMETHOD_DEFAULT, 
                                      D3DDECLUSAGE_TEXCOORD, 1},
    
    D3DDECL_END()
};
 
m_pd3dDevice->SetStreamSource(0, m_pVBPosColor, 0, 
                                          sizeof(POSCOLORVERTEX));
m_pd3dDevice->SetStreamSource(1, m_pVBTexC0, 0, 
                                          sizeof(TEXC0VERTEX));
m_pd3dDevice->SetStreamSource(2, m_pVBTexC1, 0, 
                                          sizeof(TEXC1VERTEX));

В каждом случае достаточно следующего вызова IDirect3DDevice9::D rawPrimitive .

       m_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, NUM_TRIS);

Это показывает гибкость потоков при решении проблемы дублирования данных и избыточной передачи данных через шину (то есть излишние пропускной способности).

Объявление вершин