Графика (C++ AMP)
C++ AMP содержит несколько интерфейсов API в пространстве имен Concurrency::graphics, которые можно использовать для доступа к поддержке текстур на GPU. Ниже описываются наиболее типичные сценарии применения:
класс texture можно использовать в качестве контейнера данных для вычисления и использования пространственной локальности кэша и размещения текстур в GPU. Пространственная локальность — это свойство элементов данных, заключающееся в их близком физическом расположении друг к другу;
среда выполнения обеспечивает эффективное взаимодействие с невычислительными шейдерами. Шейдеры пикселей, вершин, тесселяции и поверхностей часто используют или создают текстуры, которые можно использовать в вычислениях C++ AMP;
графические API в C++ AMP предоставляет альтернативные способы доступа к буферам, упакованным в подслова. Текстуры, имеющие форматы, которые представляют тексели (элементы текстуры), состоящие из 8 или 16-разрядных скалярных величин, позволяют обращаться к данным, упакованным таким образом.
Типы norm и unorm
Типы norm и unorm — скалярные типы, которые ограничивают диапазон значений float; это называется сжатием. Эти типы могут быть созданы из других скалярных типов явным образом. При приведении типов значение сначала приводится к float, а затем сжимается до соответствующих границ, разрешенных типом norm [- 1.0…1.0] или unorm [0.0…1.0]. Приведение из +/- бесконечности возвращает +/-1. Приведение из NaN не определено. Тип norm может быть неявно создан из unorm без потери данных. В этих типах определен неявный оператор приведения к float. Определены бинарные операторы между этими типами и другими встроенными скалярными типами, такими как float и int: +, -, *, /, ==, !=, >, <, >=, <=. Также поддерживаются составные операторы присваивания: +=,-=, * =, / =. Для типов norm определен унарный оператор отрицания (-).
Библиотека коротких векторов
Библиотека коротких векторов предоставляет определенные функции типа Vector, определенного в HLSL и, как правило, используемого для определения текселей. Короткий вектор — это структура данных, которая содержит от одного до четырех значений одного типа. Поддерживаемые типы данных: double, float, int, norm, uint и unorm. Имена типов показаны в следующей таблице. Для каждого типа также существует определение typedef без символа подчеркивания в имени. Типы, которые содержат подчеркивания, находятся в Пространство имен Concurrency::graphics. Типы, которые не содержат подчеркивания, находятся в Пространство имен Concurrency::graphics::direct3d, так что они четко отделены от фундаментальных типов с похожими названиями, таких как __int8 и __int16.
Длина 2 |
Длина 3 |
Длина 4 |
|
---|---|---|---|
double |
double_2 double2 |
double_3 double3 |
double_4 double4 |
float |
float_2 float2 |
float_3 float3 |
float_4 float4 |
целое число |
int_2 int2 |
int_3 int3 |
int_4 int4 |
norm |
norm_2 norm2 |
norm_3 norm3 |
norm_4 norm4 |
uint |
uint_2 uint2 |
uint_3 uint3 |
uint_4 uint4 |
unorm |
unorm_2 unorm2 |
unorm_3 unorm3 |
unorm_4 unorm4 |
Операторы
Если оператор определен между двумя короткими векторами, то он также определен между коротким вектором и скаляром. Кроме того, должно выполняться одно из следующих утверждений:
тип скаляра должен быть тем же, что и тип элемента короткого вектора;
тип скаляра может быть неявно преобразован в тип элемента вектора с помощью только одного определенного пользователем преобразования.
Операция выполняется покомпонентно между каждым компонентом короткого вектора и скаляром. Ниже приведены допустимые операторы.
Тип оператора |
Допустимые типы |
---|---|
Бинарные операторы |
Допустимы для всех типов: +, -, *, /, Допустимы для целочисленных типов: %, ^, |, &, <<, >> Два вектора должны иметь одинаковый размер, а результат будет вектором такого же размера. |
Операторы отношения |
Допустимы для всех типов: == и != |
Составной оператор присваивания |
Допустимы для всех типов: +=, -=, *=, /= Допустимы для целочисленных типов: %=, ^=, |=, &=, <<=, >>= |
Операторы инкремента и декремента |
Допустимы для всех типов: ++, -- Как префиксная, так и постфиксная формы являются допустимыми. |
Оператор побитового НЕ (~) |
Допустим для целочисленных типов. |
Унарный оператор - |
Допустим для всех типов, за исключением unorm и uint. |
Группирующие выражения
Библиотека коротких векторов поддерживает конструкции метода доступа вида vector_type.identifier для обращения к компонентам короткого вектора. Идентификатор identifier, известный как группирующее выражение, указывает компоненты вектора. Выражение может быть l-значением или r-значением. Отдельными символами идентификатора могут быть: x, y, z и w; или r, g, b и a. "x" и "r" означают нулевой компонент, "y" и "g" — первый компонент и т. д. (Обратите внимание, что "x" и "r" не могут использоваться в одном и том же идентификаторе). Поэтому "rgba" и "xyzw" возвращают одинаковый результат. Методы доступа из одного компонента, такие как "x" и "y", являются скалярными типами значений. Многокомпонентные методы доступа — типы короткого вектора. Например, если есть вектор int_4 с именем fourInts и значениями 2, 4, 6 и 8, то fourInts.y возвращает целое число 4, а fourInts.rg — объект int_2 со значениями 2 и 4.
Классы текстуры
Многие графические процессоры имеют аппаратные компоненты и кэши, оптимизированные для получения пикселов и текселей и визуализации изображений и текстур. Класс texture<T,N>, являющийся контейнером для объектов текселей, предоставляет функциональные возможности для работы с текстурами на таких графических процессорах. Тексель может быть:
скаляром типа int, uint, float, double, norm или unorm;
коротким вектором, который содержит 2 или 4 компонента. Единственное исключение — тип double_4, который не разрешен.
Объект texture может иметь ранг 1, 2 или 3. Объект texture может быть перехвачен только по ссылке в лямбда-выражении в вызове метода parallel_for_each. Текстура хранится в GPU в виде объектов текстуры Direct3D. Дополнительные сведения о текстурах и текселях в Direct3D см. в разделе Введение в текстуры в Direct3D 11.
В качестве типа текселя можно использовать одним из множества различных форматов текстур, которые используются при программировании графики. Например, формат RGBA может использовать 32 бита, по 8 бит на каждый из скалярных элементов R, G, B и A. Текстурные компоненты видеокарты могут обращаться к отдельным элементам текстуры в зависимости от формата. Например, если используется формат RGBA, то текстурные компоненты могут извлекать каждый 8-битный элемент в 32-битное представление. В C++ AMP можно задать количество бит на один скалярный элемент текселя, что позволяет автоматически обращаться к отдельным скалярным элементам в коде без использования битовых сдвигов.
Создание объектов текстуры
Объект текстуры можно объявить без инициализации. В следующем примере кода объявляется несколько объектов текстуры.
#include <amp.h>
#include <amp_graphics.h>
using namespace concurrency;
using namespace concurrency::graphics;
void declareTextures() {
// Create a 16-texel texture of int.
texture<int, 1> intTexture1(16);
texture<int, 1> intTexture2(extent<1>(16));
// Create a 16 x 32 texture of float_2.
texture<float_2, 2> floatTexture1(16, 32);
texture<float_2, 2> floatTexture2(extent<2>(16, 32));
// Create a 2 x 4 x 8 texture of uint_4.
texture<uint_4, 3> uintTexture1(2, 4, 8);
texture<uint_4, 3> uintTexture2(extent<3>(2, 4, 8));
}
Кроме того, для объявления и инициализации объектов texture можно использовать конструкторы. В следующем примере кода создается объект texture из вектора объектов float_4. Количество битов на один скалярный элемент установлено по умолчанию. Нельзя использовать этот конструктор с norm, unorm или с короткими векторами norm и unorm, поскольку они не имеют количества бит по умолчанию на один скалярный элемент.
#include <amp.h>
#include <amp_graphics.h>
#include <vector>
using namespace concurrency;
using namespace concurrency::graphics;
void initializeTexture() {
std::vector<int_4> texels;
for (int i = 0; i < 768 * 1024; i++) {
int_4 i4(i, i, i, i);
texels.push_back(i4);
}
texture<int_4, 2> aTexture(768, 1024, texels.begin(), texels.end());
}
Можно также объявить и инициализировать объект texture с использованием перегрузки конструктора, которая принимает указатель на исходные данные, размер исходных данных в байтах и количество бит на один скалярный элемент.
void createTextureWithBPC() {
// Create the source data.
float source[1024 * 2];
for (int i = 0; i < 1024 * 2; i++) {
source[i] = (float)i;
}
// Initialize the texture by using the size of source in bytes
// and bits per scalar element.
texture<float_2, 1> floatTexture(1024, source, (unsigned int)sizeof(source), 32U);
}
Текстуры в этих примерах созданы в представлении по умолчанию ускорителя по умолчанию. Можно использовать другие перегрузки конструктора, если нужно определить объект accelerator_view. Объект текстуры нельзя создать в ускорителе ЦП.
Существуют ограничения на размер каждого измерения объекта texture, показанные в следующей таблице. Если пределы превышены, то возникает ошибка времени выполнения.
Текстура |
Ограничение размера |
---|---|
texture<T,1> |
16384 |
texture<T,2> |
16384 |
texture<T,2> |
2048 |
Чтение из объектов текстуры
Читать из объекта texture можно с помощью Оператор texture::operator[], Оператор texture::operator() или Метод texture::get. Оператор texture::operator[] и Оператор texture::operator() возвращают значение, а не ссылки. Поэтому нельзя осуществлять запись в объект texture с помощью Оператор texture::operator[].
void readTexture() {
std::vector<int_2> src;
for (int i = 0; i < 16 *32; i++) {
int_2 i2(i, i);
src.push_back(i2);
}
std::vector<int_2> dst(16 * 32);
array_view<int_2, 2> arr(16, 32, dst);
arr.discard_data();
const texture<int_2, 2> tex9(16, 32, src.begin(), src.end());
parallel_for_each(tex9.extent, [=, &tex9] (index<2> idx) restrict(amp) {
// Use the subscript operator.
arr[idx].x += tex9[idx].x;
// Use the function () operator.
arr[idx].x += tex9(idx).x;
// Use the get method.
arr[idx].y += tex9.get(idx).y;
// Use the function () operator.
arr[idx].y += tex9(idx[0], idx[1]).y;
});
arr.synchronize();
}
В следующем примере кода показано, как сохранить каналы текстуры в коротком векторе, а затем обращаться к отдельным скалярным элементам как к свойствам короткого вектора.
void UseBitsPerScalarElement() {
// Create the image data.
// Each unsigned int (32-bit) represents four 8-bit scalar elements(r,g,b,a values).
const int image_height = 16;
const int image_width = 16;
std::vector<unsigned int> image(image_height * image_width);
extent<2> image_extent(image_height, image_width);
// By using uint_4 and 8 bits per channel, each 8-bit channel in the data source is
// stored in one 32-bit component of a uint_4.
texture<uint_4, 2> image_texture(image_extent, image.data(), image_extent.size() * 4U, 8U);
// Use can access the RGBA values of the source data by using swizzling expressions of the uint_4.
parallel_for_each(image_extent,
[&image_texture](index<2> idx) restrict(amp)
{
// 4 bytes are automatically extracted when reading.
uint_4 color = image_texture[idx];
unsigned int r = color.r;
unsigned int g = color.g;
unsigned int b = color.b;
unsigned int a = color.a;
});
}
В следующей таблице перечислено допустимое количество бит на канал для каждой разновидности типа вектора.
Тип данных текстуры |
Допустимое количество бит на скалярный элемент |
---|---|
int, int_2, int_4 uint, uint_2, uint_4 |
8, 16, 32 |
int_3, uint_3 |
32 |
float, float_2, float_4 |
16, 32 |
float_3 |
32 |
double, double_2 |
64 |
norm, norm_2, norm_4 unorm, unorm_2, unorm_4 |
8, 16 |
Запись в объекты текстуры
Метод texture::set используется для записи в объекты texture. Объект текстуры может быть доступен только для чтения или для чтения и записи. Для объекта текстуры, который должен быть доступен для чтения и записи, должны выполняться следующие условия:
T имеет только один скалярный компонент. (Нельзя использовать короткие векторы);
T не является double, norm или unorm;
значение свойства texture::bits_per_scalar_element — 32.
Если все три условия не выполняются, то объект texture будет доступен только для чтения. Первые два условия проверяются во время компиляции. Ошибка компиляции генерируется, если имеется код, который пытается писать в readonly-объект текстуры. Условие для texture::bits_per_scalar_element обнаруживается во время выполнения, и среда выполнения создает исключение unsupported_feature при попытке записи в объект texture, доступный только для чтения.
В следующем примере кода выполняется запись значений в объект текстуры.
void writeTexture() {
texture<int, 1> tex1(16);
parallel_for_each(tex1.extent, [&tex1] (index<1> idx) restrict(amp) {
tex1.set(idx, 0);
});
}
Копирование объектов текстуры
Объекты текстуры можно копировать с помощью функции copy или функции copy_async, как показано в следующем примере кода.
void copyHostArrayToTexture() {
// Copy from source array to texture object by using the copy function.
float floatSource[1024 * 2];
for (int i = 0; i < 1024 * 2; i++) {
floatSource[i] = (float)i;
}
texture<float_2, 1> floatTexture(1024);
copy(floatSource, (unsigned int)sizeof(floatSource), floatTexture);
// Copy from source array to texture object by using the copy function.
char charSource[16 * 16];
for (int i = 0; i < 16 * 16; i++) {
charSource[i] = (char)i;
}
texture<int, 2> charTexture(16, 16, 8U);
copy(charSource, (unsigned int)sizeof(charSource), charTexture);
// Copy from texture object to source array by using the copy function.
copy(charTexture, charSource, (unsigned int)sizeof(charSource));
}
Также можно скопировать одну текстуру в другую с помощью метода texture::copy_to. Две текстуры могут находиться на разных объектах accelerator_view. При копировании в объект writeonly_texture_view данные копируются в нижележащий объект texture. Количество бит на один скалярный элемент и размер должны быть одинаковыми на объектах texture источника и назначения. Если эти требования не выполнены, то генерируется исключение.
Классы представления текстуры
Класс texture_view появился в C++ AMP начиная с версии Visual Studio 2013. Представления текстуры поддерживают те же типы текселей и ранги, что и Класс texture, но в отличие от текстур, они предоставляет доступ к дополнительным функциям оборудования, таким как дискретизация текстур и MIP-карты. Представления текстуры поддерживают доступ только для чтения, только для записи и для чтения и записи к соответствующим данным текстуры.
Доступ только для чтения предоставляется специализацией шаблона texture_view<const T, N>, которая поддерживает элементы с 1, 2 или 4 компонентами, дискретизацию текстур и динамический доступ к диапазону уровней MIP-карты, определяемых при создании экземпляра представления.
Доступ только для записи предоставляется неспециализированным классом шаблона texture_view<T, N>, который поддерживает элементы с 2 или 4 компонентами и позволяет обращаться к одному уровню MIP-карты, определяемому при создании экземпляра представления. Оно не поддерживает дискретизацию.
Доступ для чтения и записи предоставляется неспециализированным классом шаблона texture_view<T, N>, который, как и текстуры, поддерживает элементы только с одним компонентом; представление может обращаться к одному уровню MIP-карты, определяемому при создании экземпляра этого представления. Оно не поддерживает дискретизацию.
Представления текстуры аналогичны к представлениям массивов, но не обеспечивают автоматическую функциональность для управления данными и перемещения, которую Класс array_view предоставляет через Класс array. К texture_view можно обращаться только в представлении ускорителя, в котором находятся соответствующие данные текстуры.
Использовать класс writeonly_texture_view не рекомендуется
В Visual Studio 2013 в C++ AMP появилась лучшая поддержка аппаратных функций для работы с текстурами, таких как дискретизация и MIP-карты, которые не могли поддерживаться в Класс writeonly_texture_view. Новый класс texture_view поддерживает надмножество функций в writeonly_texture_view; поэтому использовать writeonly_texture_view больше не рекомендуется.
Рекомендуется (по крайней мере в новом коде) использовать texture_view для доступа к функциям, которые раньше реализовывались с помощью writeonly_texture_view. Сравните следующие два примера кода, в которых осуществляется запись в объект текстуры, состоящий из двух компонентов (int_2). Обратите внимание, что в обоих случаях представление wo_tv4 должно захватываться по значению в лямбда-выражении. Ниже приведен пример, в котором используется новый класс texture_view:
void write2ComponentTexture() {
texture<int_2, 1> tex4(16);
texture_view<int_2, 1> wo_tv4(tex4);
parallel_for_each(extent<1>(16), [=] (index<1> idx) restrict(amp) {
wo_tv4.set(idx, int_2(1, 1));
});
}
А здесь показан нерекомендуемый класс writeonly_texture_view:
void write2ComponentTexture() {
texture<int_2, 1> tex4(16);
writeonly_texture_view<int_2, 1> wo_tv4(tex4);
parallel_for_each(extent<1>(16), [=] (index<1> idx) restrict(amp) {
wo_tv4.set(idx, int_2(1, 1));
});
}
Можно видеть, что оба примера кода практически идентичны, когда требуется выполнить запись в базовый уровень MIP-карты. Если в вашем коде используется класс writeonly_texture_view и вы не планируете улучшать его, код можно не менять. Если же вы думаете о расширении кода, рекомендуется переписать его с использованием класса texture_view, поскольку усовершенствования в нем поддерживают новые аппаратные функции для работы с текстурами. Ниже эти новые возможности описаны более подробно.
Дополнительные сведения о переводе класса writeonly_texture_view в категорию нерекомендуемых см. в статье Обзор Overview of the Texture View Design in C++ AMP в блоге по параллельному программированию в машинном коде.
Создание экземпляров объектов представлений текстуры
Объявление объекта texture_view похоже на объявление объекта array_view, связанного с array. В следующем примере кода объявляются несколько объектов texture и связанных с ними объектов texture_view.
#include <amp.h>
#include <amp_graphics.h>
using namespace concurrency;
using namespace concurrency::graphics;
void declareTextureViews()
{
// Create a 16-texel texture of int, with associated texture_views.
texture<int, 1> intTexture(16);
texture_view<const int, 1> intTextureViewRO(intTexture); // read-only
texture_view<int, 1> intTextureViewRW(intTexture); // read-write
// Create a 16 x 32 texture of float_2, with associated texture_views.
texture<float_2, 2> floatTexture(16, 32);
texture_view<const float_2, 2> floatTextureViewRO(floatTexture); // read-only
texture_view<float_2, 2> floatTextureViewRO(floatTexture); // write-only
// Create a 2 x 4 x 8 texture of uint_4, with associated texture_views.
texture<uint_4, 3> uintTexture(2, 4, 8);
texture_view<const uint_4, 3> uintTextureViewRO(uintTexture); // read-only
texture_view<uint_4, 3> uintTextureViewWO(uintTexture); // write-only
}
Обратите внимание, что представление текстуры, тип элемента которой не является константным и содержащее один компонент, доступно для чтения и записи, а представление текстуры, тип элемента которой не является константным, но содержащее более одного компонента, доступно только для записи. Представления текстуры с константными типами элемента всегда доступны только для чтения, но если тип элемента не является константным, число компонентов в этом элементе определяют режим доступа к текстуре — чтение и запись (один компонент) или только запись (несколько компонентов).
Тип элемента texture_view (его "константность" и количество компонентов в нем) также определяют, поддерживает ли представление дискретизацию текстуры и способ доступа к уровням MIP-карт.
Тип |
Компоненты |
Read |
Запись |
Дискретизация |
Доступ к MIP-карте |
---|---|---|---|---|---|
texture_view<const T, N> |
1, 2, 4 |
Да |
Нет (1) |
Да |
Да, индексируемый. Диапазон определяется при создании экземпляра. |
Texture_view<T, N> |
1 2, 4 |
Да Нет (2) |
Да Да |
Нет (1) Нет (1) |
Да, один уровень. Уровень определяется при создании экземпляра. Да, один уровень. Уровень определяется при создании экземпляра. |
Из этой таблицы можно увидеть, что представления текстур, доступные только для чтения, полностью поддерживают новые возможности в обмен на невозможность записи в в представление. Доступные для записи представления текстуры ограничены тем, что они поддерживают доступ только к одному уровню MIP-карт. Представления текстуры для чтения и записи являются более специализированным по сравнению с поддерживающими только запись, поскольку они содержат дополнительное требование о том, что тип элемента в таком представлении текстуры должен содержать только один компонент. Обратите внимание, что дискретизация не поддерживается для записываемых представлений текстур, поскольку эта операция предполагает чтение.
Чтение из объектов представления текстуры
Чтение недискретизированных данных текстуры через представление текстуры похоже на чтение из самой текстуры за тем исключением, что текстуры передаются по ссылке, а представления текстуры — по значению. В следующих двух примерах кода демонстрируется это. В первом — с использованием только texture:
void write2ComponentTexture() {
texture<int_2, 1> text_data(16);
parallel_for_each(extent<1>(16), [&] (index<1> idx) restrict(amp) {
tex_data.set(idx, int_2(1, 1));
});
}
А ниже приведен тот же пример, но с использованием класса texture_view:
void write2ComponentTexture() {
texture<int_2, 1> tex_data(16);
texture_view<int_2, 1> tex_view(tex_data);
parallel_for_each(extent<1>(16), [=] (index<1> idx) restrict(amp) {
tex_view.set(idx, int_2(1, 1));
});
}
Представления текстур, элементы которых основаны на типах с плавающей запятой, например float, float_2 или float_4, также могут считываться с помощью дискретизации текстур, чтобы можно было использовать аппаратную поддержку различных режимов фильтрации и адресации. C++ AMP поддерживает два режима фильтрации, которые чаще всего используются в вычислениях — точечная фильтрация (ближайший сосед) и линейная фильтрации (средневзвешенный показатель), а также четыре режима адресации — с переносом, зеркальный, ограниченный и граничный. Дополнительные сведения о режимах фильтрации см. в разделе Перечисление filter_mode; дополнительные сведения о режимах адресации см. в разделе Перечисление address_mode.
Помимо режимов, поддерживаемых в C++ AMP напрямую, можно использовать другие режимы фильтрации и адресации базовой платформы через API взаимодействия, чтобы применять дискретизатор текстур, созданный непосредственно API. Например, Direct3D поддерживает другие режимы фильтрации, такие как анизотропная фильтрация, и может применять к каждому измерению текстуры свой режим адресации. С помощью API Direct3D можно создать дискретизатор текстур, координаты которого будут переноситься по вертикали, зеркально отражаться по горизонтали и дискретизироваться с помощью анизотропного фильтра, после чего использовать этот дискретизатор в коде C++ AMP с помощью API взаимодействия make_sampler. Дополнительные сведения см. в записи Texture Sampling in C++ AMP в блоге по параллельному программированию в машинном коде.
Представления текстуры также поддерживают чтение MIP-карт. Доступные только для чтения представления текстуры (имеющие константный тип элемента) обеспечивают наибольшую гибкость, поскольку диапазон MIP-уровней, определяемых при создании экземпляра, может дискретизироваться динамически и потому что поддерживаются элементы с 1, 2 или 4 компонентами. Представления текстуры для чтения и записи, содержащие элементы с одним компонентом, также поддерживают MIP-карты, но только один уровень определяется при создании экземпляра. Дополнительные сведения см. в записи Texture with Mipmaps в блоге по параллельному программированию в машинном коде.
Запись в объекты представлений текстуры
Метод texture_view::get служит для записи в соответствующую texture через объект texture_view. Представление текстуры могут быть доступным только для чтения, для чтения и записи или только для записи. Чтобы представление текстуры было доступно для записи, оно должно иметь неконстантный тип элемента; чтобы представление текстуры было доступным для чтения и записи, его тип элемента должен также иметь только один компонент. В противном случае представление текстуры доступно только для чтения. Через представление текстуры одновременно можно обращаться только к одному уровню MIP-карты текстуры, и этот уровень задается при создании экземпляра представления.
В этом примере показано, как осуществлять запись во второй по детализации уровень текстуры, имеющей 4 уровня MIP-карт. Самый подробный уровень MIP-карт — уровень 0.
// Create a texture that has 4 mipmap levels : 16x16, 8x8, 4x4, 2x2
texture<int, 2> tex(extent<2>(16, 16), 16U, 4);
// Create a writable texture view to the second mipmap level :4x4
texture_view<int, 2> w_view(tex, 1);
parallel_for_each(w_view.extent, [=](index<2> idx) restrict(amp)
{
w_view.set(idx, 123);
});
Взаимодействие
Среда выполнения C++ AMP поддерживает взаимодействие между texture<T,1> и интерфейсом ID3D11Texture1D, между texture<T,2> и интерфейсом ID3D11Texture2D и между texture<T,3> и интерфейсом ID3D11Texture3D. Метод get_texture принимает объект texture и возвращает интерфейс IUnknown. Метод make_texture принимает интерфейс IUnknown и объект accelerator_view, а возвращает объект texture.