Gráficos (C++ AMP)
C++ AMP contiene varias API en el espacio de nombres Concurrency::graphics que se puede utilizar para tener acceso a la compatibilidad de textura en GPUs.Algunos escenarios comunes son los siguientes:
Se puede utilizar la clase textura como un contenedor de datos para el cálculo y aprovechar la localidad espacial de la caché de textura y de los diseños de hardware de la GPU.la localidad espacial es la propiedad de los datos que están físicamente cerca unos de otros.
El runtime proporciona interoperabilidad eficiente con los sombreadores que no computan.El píxel, el vértice, la teselación, y los sombreadores de casco con frecuencia consumen o generan texturas que puede utilizar en los cálculos de C++ AMP.
Los gráficos API en el C++ AMP proporcionan maneras alternativas de acceder a los búferes empaquetados de sub-palabras.Las texturas que tienen formatos que representan (elementos de textura) teselas se componen de escalares de 8 bits o de 16 bits, permiten el acceso a dicho almacén de datos empaquetado.
[!NOTA]
Las API de C++ AMP no proporcionan muestreo de textura ni funcionalidad de filtrado.Se deben utilizar las características de interoperabilidad de C++ AMP y luego escribir el código en DirectCompute y HLSL.
Los tipos norm y unorm
Los tipos norm y unorm son tipos escalares que limitan el rango de valores float ; esto se conoce como clamping.Estos tipos se pueden construir explícitamente a partir de otros tipos escalares.En las conversiones, el valor es la primera conversión a float y después se fija a la región correspondiente que el norm [- 1,0… 1,0] o el unorm [0,0… 1,0] permite.La conversión de +/- infinito devuelve +/-1.La conversión de NaN es indefinido.Un norm puede ser construido implícitamente a partir de un unorm y sin pérdida de datos.La conversión implícita del operador a float se define en los siguientes tipos.Los operadores binarios están definidos entre estos tipos y otros tipos escalares integrados como float e int: +, -, *,/, ==, !=, >, <, >=, <=.También admiten los operadores de asignación compuestos: +=, -=, *=,/=.El operador unario de negación (-) está definido para los tipos norm.
Biblioteca de vector corto.
La biblioteca de vector corto proporciona algunas de las funcionalidades de Tipo de vector que se define en HLSL y se utiliza normalmente para definir los téxeles.Un vector corto es una estructura de datos que contiene de uno a cuatro valores del mismo tipo.Los tipos admitidos son double, float, int, norm, uint y unorm.Los nombres de los tipos se muestran en la siguiente tabla.Para cada tipo, también hay un typedef correspondiente que no lleva un subrayado en el nombre.Los tipos que llevan subrayado están en Concurrency::graphics (Espacio de nombres).Los tipos que no llevan subrayado están en Concurrency::graphics::direct3d (Espacio de nombres) y están claramente separados de los tipos fundamentales denominados de forma similar,tales como __int8 y __int16.
Longitud 2 |
Longitud 3 |
Longitud 4 |
|
---|---|---|---|
double |
double_2 double2 |
double_3 double3 |
double_4 double4 |
float |
float_2 float2 |
float_3 float3 |
float_4 float4 |
Valor int. |
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 |
Operadores
Si se define un operador entre dos vectores cortos, entonces se define también entre un vector corto y uno escalar.Además, uno de ellos debe ser true:
El tipo escalar tiene que ser igual que el tipo de elemento del vector corto.
El tipo escalar se puede convertir implícitamente en el tipo de elemento del vector utilizando únicamente una conversión definida por el usuario.
La operación se lleva a cabo entre cada componente del vector corto y el escalar.Los operadores válidos son:
Tipo operador |
Tipos válidos |
---|---|
Operadores binarios |
Válido en todos los tipos: +, -, *, /, Válido en tipos enteros: %, ^, |, &, <<, >> Los dos vectores deben tener el mismo tamaño y el resultado es un vector del mismo tamaño. |
Operadores relacionales |
Válido en todos los tipos: == y != |
Operador de asignación compuesto |
Válido en todos los tipos: +=, -=, *=, /= Válido en tipos enteros: %=, ^=, |=, &=, <<=, >>= |
Operadores de incremento y decremento |
Válido en todos los tipos: ++, -- Tanto el prefijo como el sufijo son válidos. |
Operador NOT bit a bit (~) |
Válido en tipos enteros. |
Operador unario - |
Válido en todos los tipos excepto unorm y uint. |
Expresiones swizzling
La biblioteca de vector corto admite el concepto del descriptor de acceso vector_type.identifier para acceder a los componentes de un vector corto.El identifier, conocido como expresión swizzling, especifica los componentes del vector.La expresión puede ser un valor l o un valor r.Los caracteres individuales del identificador pueden ser: x, y, z, y w; o r, g, b y a.«x» y «r» significan el componente zeroésimo, «y» y «g» significan el primer componente y así sucesivamente.(Observe que «x» y «r» no se pueden utilizar en el mismo identificador.) Por consiguiente, «rgba» y «xyzw» devuelven el mismo resultado.Los descriptores de acceso de un único componente como «x» e «y» son tipos de valores escalares.Los descriptores de varios componentes son tipos de vector corto.Por ejemplo, si crea un vector int_4 denominado fourInts y tiene los valores 2, 4, 6 y 8, entonces fourInts.y devuelve el entero 4 y fourInts.rg devuelve un objeto de int_2 con los valores 2 y 4.
Clases de Texturas
Muchas GPU tienen hardware y cachés que se optimizan para capturar los pixels y las téxeles y ofrecer imágenes y texturas.La clase textura<T,N> , que es una clase contenedora para los objetos de téxeles, expone la funcionalidad de textura de los GPU.Una téxel puede ser:
Un int, uint, float, double, norm o un escalar unorm.
Un vector corto que tiene dos o cuatro componentes.La única excepción es double_4, que no se permite.
El objeto texture puede tener un rango de 1, 2, 3.El objeto texture se puede capturar solamente por referencia en la expresión lambda de una llamada a parallel_for_each.Los téxeles se almacenan en la GPU como objetos de textura Direct3D.Para obtener más información sobre texturas y téxeles en Direct3d, consulte introducción a las texturas en Direct3D 11.
El tipo de téxel que se usa puede ser uno de los muchos formatos de textura que se utilizan en la programación de gráficos.Por ejemplo, un formato RGBA podría utilizar 32 bits, con 8 bits para cada uno de los elementos escalares R, G, B y A.El hardware de textura de una tarjeta gráfica puede acceder a los elementos individuales basados en el formato.Por ejemplo, si se utiliza el formato RGBA, el hardware de textura puede convertir cada elemento de 8 bits en un formulario de 32 bits.En C++ AMP, se pueden establecer los bits por elemento escalar del téxel y así para poder automáticamente tener acceso a los elementos escalares individuales en el código sin utilizar el desplazamiento de bit.
Creación de instancias de objetos de textura
Se puede declarar un objeto de textura sin inicialización.El siguiente ejemplo de código declara varios objetos de textura.
#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));
}
También se puede utilizar un constructor para declarar e inicializar un objeto texture.El siguiente ejemplo de código crea una instancia de un objeto texture desde un vector de objetos float_4.Los bits por elemento escalar se establecen en el valor predeterminado.No se puede utilizar este constructor con norm, unorm o los vectores cortos de norm y de unorm, porque no tienen bits predeterminados para el elemento escalar.
#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());
}
También se puede declarar e inicializar un objeto texture mediante una sobrecarga del constructor que tome un puntero a los datos de origen, el tamaño de los datos de origen en bytes y los bits por elemento escalar.
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);
}
Las texturas en estos ejemplos se crean en la vista predeterminada del acelerador predeterminado.Se pueden utilizar otras sobrecargas del constructor si desea especificar un objeto accelerator_view.No se puede crear un objeto de textura en un acelerador de CPU.
Hay límites respecto al tamaño de cada dimensión del objeto texture, como se muestra en la siguiente tabla.Se genera un error en tiempo de ejecución si se superan los límites.
Textura |
Limitación de tamaño |
---|---|
textura<T,1> |
16384 |
textura<T,2> |
16384 |
textura<T,2> |
2048 |
Lectura desde objetos de textura
Se puede leer un objeto texture mediante texture::operator[] (Operador), texture::operator() (Operador) o texture::get (Método).texture::operator[] (Operador) y texture::operator() (Operador) devuelven un valor, no una referencia.Por consiguiente, no se puede escribir a un objeto texture mediante texture::operator[] (Operador).
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();
}
El siguiente ejemplo de código muestra cómo almacenar los canales de textura en un vector corto y después tener acceso a los elementos escalares individuales como propiedades del vector corto.
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;
});
}
En la siguiente tabla se enumeran los bits válidos por canal para cada tipo de vector.
Tipo de datos de textura |
Bits válidos por elemento escalar |
---|---|
int, int_2, int_4 uint, uint_2, uint_4 |
8, 16, 32 |
float, float_2, float_4 |
16, 32 |
double, double_2 |
64 |
norm, norm_2, norm_4 unorm, unorm_2, unorm, 4 |
8, 16 |
Escritura en los objetos de textura
Utilice el método texture::set para escribir en los objetos texture.Un objeto de textura puede ser de sólo lectura o de lectura y escritura.Para que un objeto de textura sea legible y grabable, las siguientes condiciones deben ser verdaderas:
T tiene sólo un componente escalar.(Los vectores cortos no se permiten.)
T no es double, norm o unorm.
La propiedad texture::bits_per_scalar_element es 32.
Si los tres no son true, el objeto texture es de sólo lectura.Las dos primeras condiciones se comprueban durante la compilación.Se genera un error de compilación si se tiene código que intenta escribir en objeto textura a readonly.La condición para texture::bits_per_scalar_element se detecta en tiempo de ejecución y el runtime genera una excepción unsupported_feature si se intenta escribir en un objeto de solo lectura en texture.
El siguiente ejemplo de código escribe valores en un objeto textura.
void writeTexture() {
texture<int, 1> tex1(16);
parallel_for_each(tex1.extent, [&tex1] (index<1> idx) restrict(amp) {
tex1.set(idx, 0);
});
}
Utilización de un objeto writeonly_texture_view
La clase writeonly_texture_view proporciona una vista de solo escritura de un objeto de textura.El objeto writeonly_texture_view debe ser capturado por el valor de la expresión lambda.El siguiente ejemplo de código utiliza un objeto writeonly_texture_view para escribir en el objeto texture con dos componentes (int_2).
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));
});
}
Copiado de objetos de textura
Se puede copiar entre los objetos de textura mediante la función copy o la función copy_async, como se muestra en el siguiente ejemplo de código.
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));
}
También se puede copiar desde una textura a otra mediante el método texture::copy_to.Las dos texturas pueden estar en diferentes accelerator_views.Cuando se copia en un objeto writeonly_texture_view, los datos se copian en el objeto subyacente texture.Los bits por elemento escalar y la extensión deben ser iguales en los objetos de texture de origen y de destino.Si los requisitos no se cumplen, el runtime produce una excepción.
Interoperabilidad
El runtime de C++ AMP admite la interoperabilidad entre texture<T,1> y la interfaz de ID3D11Texture1D, entre texture<T,2> y la interfaz de ID3D11Texture2D y entre texture<T,3> y la interfaz de ID3D11Texture3D.El método get_texture toma un objeto texture y devuelve una interfaz IUnknown.El método make_texture toma una interfaz IUnknown y un objeto accelerator_view y devuelve un objeto texture.