Автор: Евгений Коростелев «kore3d»

Опубликовано: 21.04.2010

Изменено: 21.04.2010

Постоянная ссылка

Комментарии [13]

Порядко-независимая прозрачность на GPU с использованием динамических списков





Страницы: 1 2

3. Реализация метода

Прежде чем приступать к изучению данного раздела, рекомендуется скачать исходный код к статье [23]. Это позволит ускорить последующий процесс изучения представленной технологии.

В архиве с реализацией содержится несколько директорий. Основной директорией является prj. В ней содержится весь исходный код (в поддиректориях inc и src) и медиа-контент (поддиректория media), в том числе шейдеры. Реализация для удобства разнесена по следующим файлам:

  • k3d_oit11_api.h – набор вспомогательных функций
  • k3d_oit11_wnd.h – оконная подсистема на основе библиотеки SDL
  • k3d_oit11_d3d.h – основные функции работы с D3D11-контекстом и данными сцены
  • k3d_oit11_oit.h – надстройка над базовыми классами, реализующая внутренние механизмы технологии (инициализация, работа с памятью, пост-обработка)

Ключевыми элементами реализации являются файлы k3d_oit11_d3d.h и k3d_oit11_oit.h. Рассмотрение следует начинать с k3d_oit11_d3d.h. Этот файл содержит реализацию класса CWindowD3D, являющегося потомком CWindowSDL. CWindowSDL, в свою очередь, реализует функции по созданию стандартного оконного приложения и управление им. Класс CWindowD3D содержит следующие методы:

Основные методы

  • Create – инициализация всех компонентов
  • CreateContext – создание элементов контекста: устройства, буферов и других объектов для визуализации
  • CreateSolidContent, CreateTranslucentContent – создание компонентов сцены
  • RenderBegin – начало кадра
  • Render – команды визуализации
  • RenderEnd – окончание построения кадра и отображение его

Переопределяемые методы для взаимодействия с базовым классом из k3d_oit11_wnd.h

  • _OnInit – инициализация
  • _OnIdle – действия во время отсутствия оконных сообщений
  • _OnEvent – реакция на события

Переопределяемые методы для взаимодействия с классом-потомком из k3d_oit11_oit.h

  • _InitTranslucentTech – инициализация порядко-независимой прозрачности
  • _EnableTranslucentTech – включение порядко-независимой прозрачности
  • _DisableTranslucentTech – выключение (парная функция к _EnableTranslucentTech)
  • _GetTranslucentTechMacro – запрос макро-подстановок для компиляции шейдеров полупрозрачных объектов

Вспомогательные методы

  • CompileShader – компиляция шейдеров с диагностическими сообщениями
  • SaveLastFrameToFile – сохранение текущего кадра в директорию приложения (вызывается при нажатии комбинации клавиш Ctrl+P)
  • Synchronize – синхронизация центрального процессора с графическим
  • EnableCacheShaders – включение кэширования результата компиляции шейдеров (игнорируется при отсутствии поддиректории cache в папке с шейдерами)
  • EnableHardware11 – включение аппаратной поддержки HW11 (по-умолчанию, включена)
  • ReadFile – загрузка файла в системную память
  • ResetTimer – сброс таймера
  • GetTimeSec – запрос текущего значения таймера (для измерения времени выполнения фрагментов алгоритма используется синхронизация CPU-GPU)

Загрузка сцены в память видеокарты и визуализация

  • CreateRobot, RenderRobot – робот из демонстрационного приложения «AMD Demo – Mecha»
  • CreateTeapot, RenderTeapot – модель чайника
  • CreateParticles, RenderParticles – 1024 частицы дыма (без автоматического поворота по направлению к наблюдателю)
  • CreateScreenQuad, RenderLegend – прямоугольник для визуализации поверх буфера кадра

Все указанные методы используют стандартные вызовы DirectX11 API. Также, для того, чтобы упростить некоторые рутинные действия, несколько вспомогательных функций вынесено в k3d_oit11_api.h. Помимо этого, используется несколько структур для описания блоков данных класса, и все указатели являются «умными» – используется класс CComPtr (объявленный в atlbase.h). Класс CWindowD3D при этом работает с нижеприведенными структурами.

Листинг 1. Структура батча: геометрия сцены, константы, видимость объекта и его свойства

  struct SBatch {
      std::vector<CComPtr<ID3D11Buffer> > pCBs;
      CComPtr<ID3D11VertexShader> pVS;
      CComPtr<ID3D11PixelShader> pPS, pPS_Ex;
      CComPtr<ID3D11Buffer> pVB, pIB;
      CComPtr<ID3D11InputLayout> pIL;
      CD3D11InputElementDesc ieDesc;
      UINT ieStride, ieVerts, ieInds;
      bool bVisible;
      //
      struct SConstantsVS {
        float matrWVP[16];
      };
      struct SConstantsPS {
        float color[4];
      };
  };

Листинг 2. Структура контекста: устройство, цепочка буферов, буфер глубины, состояний конвейера и статистика визуализации

  struct SContext {
      CComPtr<ID3D11Device> pD;
      CComPtr<ID3D11DeviceContext> pC;
      CComPtr<IDXGISwapChain> pSC;
      CComPtr<ID3D11Query> pQ;
      CComPtr<ID3D11Texture2D> pDS;
      CComPtr<ID3D11DepthStencilView> pDSV;
      CComPtr<ID3D11RenderTargetView> pRTV;
      CComPtr<ID3D11UnorderedAccessView> pUAV;
      bool bDebug, bHW11, bOIT;
      size_t screenWidth, screenHeight;
      // statistics
      double timeStart, timePresentSec;
      size_t cntTranslucentRendered, cntTranslucentReserved;
      CComPtr<ID3D11ShaderResourceView> pSRV_Legend;
      bool bLegend;
      SBatch screenQuad;
  };

Листинг 3. Объект, описывающий 3D-сцену: прозрачные и непрозрачные объекты, настройки состояний конвейера для их корректной визуализации

  struct SScene {
    struct SSolidContent {
      CComPtr<ID3D11DepthStencilState> pDSS;	
      CComPtr<ID3D11BlendState> pBS;
      CComPtr<ID3D11RasterizerState> pRS;
      SBatch teapot;
      double timeRenderSec;
      }
    } solid;
    struct STranslucentContent {
      CComPtr<ID3D11DepthStencilState> pDSS;	
      CComPtr<ID3D11BlendState> pBS;
      CComPtr<ID3D11RasterizerState> pRS;
      CComPtr<ID3D11SamplerState> pSS;
      CComPtr<ID3D11ShaderResourceView> pSRV_Particle;
      SBatch robot, particles;
      double timeBeginSec, timeRenderSec, timeEndSec;
      } translucent;
  } m_Scene;

Применение класса CWindowD3D без надстройки позволяет визуализировать полупрозрачные объекты с альфа-смешиванием по стандартной. Так как в этом случае порядок смешивания произвольный, результат визуализации в большинстве случаев будет некорректным (рис. 1).

Основная часть реализации технологии порядко-независимой прозрачности с использованием динамических списков находится в файле k3d_oit11_oit.h. В этом файле располагается класс CWindowOIT, отвечающего за реализацию технологии и являющегося потомком класса CWindowD3D. Класс CWindowOIT имеет следующие методы:

Основные методы

  • CreateBuffers — выделение памяти для использования метода
  • CreatePassMain — подготовка настроек для прохода визуализации полупрозрачных объектов
  • CreatePassPost — создание прохода пост-обработки: сортировка слоев прозрачности и альфа-смешивание

Переопределяемые методы

  • _InitTranslucentTech, _GetTranslucentTechMacro, _EnableTranslucentTech, _DisableTranslucentTech — для взаимодействия с классом CWindowD3D

Используемые константы

  • BUFFER_DIM_X, BUFFER_DIM_Y — задают коэффициенты объема памяти, отводимой под хранение слоев
  • OIT_PIXEL_LAYERS — максимальное число обрабатываемых слоев прозрачности

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

Листинг 4. Структура, описывающая настройки прохода визуализации полупрозрачных объектов

  struct SPathMain {
      // buffers
      CComPtr<ID3D11Texture2D> pT2D_Heads, pT2D_ZLinks, pT2D_Colors;
      CComPtr<ID3D11Texture2D> pT2D_Lens;
      CComPtr<ID3D11Buffer> pBUF_Offset, pBUF_Dump;
      CComPtr<ID3D11UnorderedAccessView> pUAV_Heads, pUAV_ZLinks, pUAV_Colors;
      CComPtr<ID3D11UnorderedAccessView> pUAV_Lens, pUAV_Offset;
      CComPtr<ID3D11ShaderResourceView> pSRV_Heads, pSRV_ZLinks, pSRV_Colors;
      CComPtr<ID3D11ShaderResourceView> pSRV_Lens;
      // states
      CComPtr<ID3D11DepthStencilState> pDSS;	
      CComPtr<ID3D11BlendState> pBS;
  };

Листинг 5. Структура, описывающая настройки прохода пост-обработки

  struct SPathPostprocess {
      // states
      CComPtr<ID3D11BlendState> pBS;
      CComPtr<ID3D11DepthStencilState> pDSS;
      // quad
      CComPtr<ID3D11PixelShader> pPS;
      CComPtr<ID3D11PixelShader> pPS_Debug;
      CComPtr<ID3D11ComputeShader> pCS;
};

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

  • oit11_buffers.h – код, отвечающий за заполнение и обработку буферов
  • oit_sort.h – код, отвечающий за сортировку данных
  • scr_quad.* – код, исполняемый при визуализации прямоугольника поверх буфера кадра
  • draw.h – общие параметры для визуализации контента
  • draw_*.* – код материалов, используемых при визуализации объектов 3D-сцены

Для внедрения описываемой технологии в любой существующий пиксельный шейдер полупрозрачного материала достаточно добавить несколько маркеров. Их определения находятся в файле oit11_buffers.h и используются как показано в листинге 6.

Листинг 6. Пример использования технологии в качестве расширения пиксельного шейдера

#include <draw.h>
//CODE
#include <oit11_buffers.h>
//
PS_OIT_HEADER
void PS_ENTRY_DrawTeapot
(
      in  float4 iPosition     : SV_Position,
      out float4 oTarget0      : SV_Target0
) {
      oTarget0 = cColor;
      PS_OIT_END(iPosition, oTarget0);
}

Далее приводится код сортировки и альфа-смешивания, исполняемый на этапе пост-обработки (листинг 7).

Листинг 7. Пиксельный шейдер этапа пост-обработки

#include <oit11_buffers.h>
//CODE
void PS_ENTRY_MergeStage11
(
    in float4 iPosition : SV_Position, 
    out float4 oColor : SV_Target0 
) {
    oColor = ProcessLayers(iPosition.xy);
}

Реализация вспомогательных функций содержится в файле oit11_buffers.h. В следующем листинге предлагается реализация функции ProcessLayers.

Листинг 8. Функция обработки слоев прозрачности

  float4 ProcessLayers
  (
    in const uint2 iPosition
  ) {
    uint2 kpixel[OIT_PIXEL_LAYERS];
    uint offset = srvHeads[iPosition.xy];
    const int N = srvLens[iPosition.xy];
    //
    [unroll]
    for(int i = 0; i < OIT_PIXEL_LAYERS; i++) {   
        [flatten]
        if(i < N) {
            const uint2 pos = 
uint2(offset % MEMORY_DIM_X, offset / MEMORY_DIM_X);
            const float4 color = srvColors[pos];
            const uint2 dl = srvZLink[pos];
            kpixel[i] = uint2(dl.x, packColorUint(color));
            offset = dl.y;
        } else {
            kpixel[i] = uint2(0xffffffff, 0);      
        } 
    }
    //
  #if (0 == OIT_OVERDRAW_DEBUG) 
    return MergeLayers(kpixel, N);
  #else
    return float4(DebugColor(N), 0);
  #endif
  }

В этом же файле находится реализация функции смешивания слоев MergeLayers (листинг 9).

Листинг 9. Функция сортировки и смешивания слоев прозрачности

  float4 MergeLayers
  (
    in uint2 kpixel[OIT_PIXEL_LAYERS],
    in const uint N
  ) {
    float4 dest = float4(0, 0, 0, 1);
    //
    switch(N) {
        case 0: {
            return dest;
        } break;
        case 1: {
            float4 src = unpackColorUint(kpixel[0].y);
            return lerp(dest, float4(src.rgb, 0.0), src.a); 
        } break;
        case 2: {
            [flatten]
            if(kpixel[0].x > kpixel[1].x) {
                float4 src = unpackColorUint(kpixel[0].y);
                dest = lerp(dest, float4(src.rgb, 0.0), src.a); 
                src = unpackColorUint(kpixel[1].y);
                dest = lerp(dest, float4(src.rgb, 0.0), src.a); 
            } else {
                float4 src = unpackColorUint(kpixel[1].y);
                dest = lerp(dest, float4(src.rgb, 0.0), src.a); 
                src = unpackColorUint(kpixel[0].y);
                dest = lerp(dest, float4(src.rgb, 0.0), src.a); 
            }
            return dest;
        } break;
    }
    //
    OddEvenMergeSort4(kpixel);
    if(N > 4) {
        OddEvenMergeSort8ex4(kpixel);        
        if(N > 8) {
    #if (OIT_PIXEL_LAYERS > 8)
            OddEvenMergeSort16ex8(kpixel);
        #if (OIT_PIXEL_LAYERS > 16)
            if(N > 32) {
            #if (OIT_PIXEL_LAYERS > 32)
                OddEvenMergeSort64ex16(kpixel); 
            #endif
            } else {
                OddEvenMergeSort32ex16(kpixel); 
            }
        #endif
    #endif
        }
    }
    //
    for(int i = N - 1; i >=0; --i) {
        const float4 src = unpackColorUint(kpixel[i].y);
        dest = lerp(dest, float4(src.rgb, 0.0), src.a); 
    }  
    return dest;
  }

Для упаковки и распаковки значений цветов используется вспомогательный код листинга 10.

Листинг 10. Код упаковки и распаковки значений цветов

  inline uint packColorUint
  (
    in const float4 iColor
  ) {
    uint4 color = saturate(iColor) * 255.0 + 0.5;
    return (color.r << 24) + (color.g << 16) + (color.b << 8) + color.a;
  }
  //
  inline float4 unpackColorUint
  (
    in const uint iPacked
  ) {
    uint4 color = uint4(iPacked >> 24, iPacked >> 16, iPacked >> 8, iPacked);
    color &= 0xff;
    return (color.rgba / 255.0);
  }

Буфера для записи-чтения и чтения задаются в заголовочном файле шейдеров (листинг 11).

Листинг 11. Назначение буферов в шейдере

RWTexture2D<float4>                         uavRT     : register (u0);
globallycoherent RWStructuredBuffer<uint>   uavOffset : register (u1);
globallycoherent RWTexture2D<uint>          uavHeads  : register (u2);
globallycoherent RWTexture2D<uint>          uavLens   : register (u3);
RWTexture2D<float4>                         uavColors : register (u4);
RWTexture2D<uint2>                          uavZLink  : register (u5);
//
Texture2D<uint>   srvHeads      : register (t0);
Texture2D<uint>   srvLens       : register (t1);
Texture2D<float4> srvColors     : register (t2);
Texture2D<uint2>  srvZLink      : register (t3);

На этапе сортировки, как сказано ранее, используется развернутый код сортирующей сети, построенной методом «чет-нечет» Батчера. В листинге 12 приводится вариант реализации для случая четырех сортируемых значений.

Листинг 12. Сортировка методом «чет-нечёт» Батчера четырех значений

  inline void SwapBigEndian(inout uint2 kpixel[SORT_ARRAY_LENGTH], int i, int j)
  {
    [flatten]
    if(kpixel[i].x > kpixel[j].x) {
        uint2 t = kpixel[i];
        kpixel[i] = kpixel[j];
        kpixel[j] = t;
    }
  }
  inline void OddEvenMergeSort4(inout uint2 kpixel[SORT_ARRAY_LENGTH]) {
    SwapBigEndian(kpixel, 0, 1);
    SwapBigEndian(kpixel, 2, 3);
    SwapBigEndian(kpixel, 0, 2);
    SwapBigEndian(kpixel, 1, 3);
    SwapBigEndian(kpixel, 1, 2);
  }

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

4. Результаты

В следующей таблице приводится серия изображений, полученных на видеокарте ATI Radeon HD 5670.

Без прозрачности
800 кадров/сек
Неправильная прозрачность
615 кадров/сек
Правильная прозрачность
40 кадров/сек
Число слоев
Таблица 2. Сравнение результатов применения технологии

В первой колонке приводится пример визуализации непрозрачного робота. Во второй - результат альфа-смешивания без учета порядка растеризации. В третьей - результат использования технологии порядко-независимой прозрачности с использованием динамических списков. Четвертое изображение - диагностический режим, показывающий информацию о числе слоев прозрачности для различных пикселей буфера кадра. Максимальное число на тестовой сцене достигает 60 слоев на пиксель. Таким образом, в худшем случае приходится сортировать 64 слоя. Этот случай довольно ресурсоемкий для расчёта на используемом тестовом аппаратном обеспечении. Из собранной статистики получено, что чтение памяти и сортировка с последующим альфа-смешиванием, то есть весь проход пост-обработки, при текущем ракурсе и разрешении изображения 800 на 600 пикселей занимает 22 мс. Кроме этого, в среднем на один пиксель приходится один слой прозрачности. В этом случае динамические списки позволяют существенно экономить видеопамять, выделяемую под хранение слоев прозрачности.

В таблице 3 представлена еще одна серия изображений. На них, кроме робота, представлены непрозрачный чайник и 1024 разноцветные частицы. Этот пример демонстрирует взаимодействие непрозрачные и прозрачных объектов сцены между собой. На изображении видно, что модель непрозрачного чайника корректно взаимодействует с полупрозрачными объектами, перекрывая их – в области перекрытия число слоев существенно меньше, чем в других частях полученного изображения.

Неправильная прозрачность
377 кадров/сек
Правильная прозрачность
22 кадра/сек
Число слоев
Таблица 3. Применение технологии для системы частиц

Стадия пост-обработки для случая, представленного в таблице 3, занимает 33 мс и среднее число слоев равно 6. Данные по-прежнему указаны для разрешения 800 на 600 пикселей.

Полученные результаты свидетельствуют о том, что наиболее ресурсоемким является чтение большого числа пикселей из памяти (ограниченность texture fillrate). А также производительность существенно падает при сортировке 64 слоев. В следующей таблице (таблица 4) приводится более детальное сравнение зависимости производительности от числа обрабатываемых слоев прозрачности. Разрешение по-прежнему остается 800 на 600 пикселей.

Число слоев 8 16 32 64
Время пост-обработки 3 мс 5 мс 12 мс 33 мс
Время кадра 13 мс 16 мс 23 мс 44 мс
Число компараторов 19 63 191 543
Число всех прозрачных пикселей 20979516 2474133 2544049 2544972
Расположение переполняющихся пикселей
Число переполнившихся пикселей 88135 15692 233 0
Число пикселей, использующих дополнительную обработку 391865 72443 15459 233
Таблица 4. Сравнение производительности технологии
при разном количестве слоев прозрачности

Дополнительная обработка подразумевает использование более длинной сортировки и цикла смешивания с большим числом итераций.

5. Заключение

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

В будущем, судя по результатам тестирования на видеокарте бюджетного уровня, эта технология будет широко применяться большинством разработчиков игр. Этот день наступит когда большинство пользователей систем виртуальной реальности перейдет на аппаратные решения уровня DirectX версии 11. На сегодняшний же день этот API еще не настолько распространен в силу того, что требуемые аппаратные решения не представлены широко.

Так, на момент написания статьи на рынке предлагались только решения компании AMD и, к моменту публикации, на рынке только-только начинали появляться первые решения от компании NVIDIA на базе архитектуры Fermi [24]. Их доступность массовому потребителю является первым шагом к качественно новым фотореалистичным спецэффектам, одним из которых является порядко-независимая прозрачность.

6. Демонстрационное приложение

Бинарная версия демонстрационного приложения: kore_oit11_dlists_x86_win7.zip

Исходный код: kore_oit11_dlists_src.zip

Для сборки демонстрационного приложения потребуются следующие версии продуктов:

  • Microsoft Visual Studio 2008
  • Microsoft DirectX SDK (February 2010)
  • Библиотека Simple DirectMedia Layer версии 1.2

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

  • ATI Radeon HD 5x00
  • NVIDIA GeForce GTX 4xx

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

Поскольку компиляция сложных шейдеров в ассемблерные инструкции графического процессора занимает длительный промежуток времени, рекомендуется создавать каталог cache в папке shaders (prj\media\shaders\). Кэширование будет полезно, если планируется неоднократно запускать приложение с одинаковыми значениями макро-подстановок, используемых в шейдере. В этом случае при последующем запуске стадия компиляции будет происходить почти мгновенно.

7. Литература

  1. C. Everitt, Interactive order-independent transparency, Technical report, NVIDIA Corp., 2001
  2. L. Bavoil, K. Myers, Order independent transparency with dual depth peeling. Technical report, NVIDIA Corp., 2008
  3. N. Thibieroz, Robust Order-Independent Transparency via Reverse Depth Peeling, ShaderX6, pp. 211–226
  4. D. Pangerl, Deferred Rendering Transparency, Article 2.7, ShaderX7
  5. B.-Q. Liu, L.-Y. Wei, Y.-Q Xu, Multi-Layer Depth Peeling via Fragment Sort, Tech report, Microsoft Research Asia, 2006
  6. L. Bavoil, S.P. Callahan, A. Lefohn, J.L.D. Comba, C.T. Silva, Multi-Fragment Effects on the GPU using the k-Buffer, Proceedings of the 2007 symposium on Interactive 3D graphics and games, 2007, pp.97-104
  7. D. Pangerl, ZT-Buffer Algorithm, Article 2.8, ShaderX5: Advanced Rendering Techniques, Wolfgang Engel, Ed., Charles River Media, 2007, p.151–157
  8. K. Myers, L. Bavoil, Stencil routed A-Buffer, ACM SIGGRAPH Technical Sketch, 2007
  9. K. Myers, L. Bavoil, Deferred Rendering using a Stencil Routed K-Buffer, ShaderX6, 2008, pp. 189–198
  10. Kipfer, R. Westermann, Improved GPU Sorting, Chapter 46, GPU Gems II
  11. Meng-Cheng Huang Fang Liu, Xue-Hui Liu and En-Hua Wu, Multi-Fragment Effects on the GPU using Bucket Sort, Article 8.1 of GPU PRO, AK Peters, 2010
  12. Meng-Cheng Huang Fang Liu, Xue-Hui Liu and En-Hua Wu, Efficient depth peeling via bucket sort, Proceedings of the Conference on High Performance Graphics 2009, pp.51-57
  13. L. Carpenter, The A-buffer, an antialiased hidden surface method, Proceeding of the 11th annual conf. On Computer graphics and interactive techniques, 1984, pp.103-108
  14. C. Pepper, Prefix sum pass to linearize A-buffer storage, Patent, Microsoft Corp., 2006
  15. MSDN, OIT11 Sample
  16. V. Coda, OIT/A-buffer demo: improvements
  17. AMD Demo – Mecha
  18. MSDN, Shader Model 5
  19. D. Knuth, The Art of Computer Programming, Volume 3: Sorting and Searching, Addison-Wesley, 1997, pp.219–247
  20. K. Batcher, Sorting Networks and their Applications, Proceedings of AFIPS Spring Joint Comput. Conf., 1968, pp.307-314
  21. H. Lang, Odd-even mergesort
  22. J. Hugues, The Evolving Non-Determinism (END) algorithm has found more efficient networks
  23. E. Korostelev, Sources of OIT With Dynamic Lists
  24. NVIDIA, NVIDIA’s Next Generation CUDA Compute Architecture: Fermi, 2010

 


 

Статья участвовала в конкурсе статей по программированию #4 (2009).

 

Страницы: 1 2