C++AMP исключение в простом примере обработки изображений
Я пытаюсь научить себя C++AMP и хотел бы начать с очень простой задачи из моей области - обработки изображений. Я хотел бы преобразовать 24-битное RGB-изображение (растровое изображение) в 8-битное оттенки серого. Данные изображения доступны в unsigned char
массивы (полученные из Bitmap::LockBits(...)
так далее.)
Я знаю, что C++AMP по какой-то причине не может справиться с char
или же unsigned char
данные через array
или же array_view
Я попытался использовать texture
в соответствии с этим блогом. Здесь объясняется, как записываются текстуры 8bpp, хотя VisualStudio 2013 говорит мне writeonly_texture_view
был объявлен устаревшим
Мой код вызывает исключение во время выполнения, говоря: "Не удалось отправить ядро". Полный текст исключения является длинным:
ID3D11DeviceContext:: Dispatch: Представление неупорядоченного доступа (UAV) в слоте 0 блока Compute Shader имеет формат (R8_UINT). Этот формат не поддерживает чтение из шейдера, как БПЛА. Это несоответствие недопустимо, если шейдер фактически использует представление (например, оно не пропускается из-за ветвления кода шейдера). К сожалению, было невозможно, чтобы все аппаратные реализации поддерживали чтение этого формата как БПЛА, несмотря на то, что формат может быть записан как БПЛА. Если шейдеру нужно только выполнять чтение, но не запись в этот ресурс, рассмотрите возможность использования представления ресурсов шейдера вместо БПЛА.
Код, который я использую до сих пор:
namespace gpu = concurrency;
gpu::extent<3> inputExtent(height, width, 3);
gpu::graphics::texture<unsigned int, 3> inputTexture(inputExtent, eight);
gpu::graphics::copy((void*)inputData24bpp, dataLength, inputTexture);
gpu::graphics::texture_view<unsigned int, 3> inputTexView(inputTexture);
gpu::graphics::texture<unsigned int, 2> outputTexture(width, height, eight);
gpu::graphics::writeonly_texture_view<unsigned int, 2> outputTexView(outputTexture);
gpu::parallel_for_each(outputTexture.extent,
[inputTexView, outputTexView](gpu::index<2> pix) restrict(amp) {
gpu::index<3> indR(pix[0], pix[1], 0);
gpu::index<3> indG(pix[0], pix[1], 1);
gpu::index<3> indB(pix[0], pix[1], 2);
unsigned int sum = inputTexView[indR] + inputTexView[indG] + inputTexView[indB];
outputTexView.set(pix, sum / 3);
});
gpu::graphics::copy(outputTexture, outputData8bpp);
В чем причина этого исключения, и что я могу сделать для обхода?
1 ответ
Я также изучал C++Amp самостоятельно и столкнулся с очень похожей проблемой, чем ваша, но в моем случае мне нужно было иметь дело с 16-битным изображением.
Вероятно, проблему можно решить с помощью текстур, хотя я не могу вам в этом помочь из-за недостатка опыта.
Итак, то, что я сделал, в основном основано на битовой маскировке.
Во-первых, обманывайте компилятор, чтобы позволить вам скомпилировать:
unsigned int* sourceData = reinterpret_cast<unsigned int*>(source);
unsigned int* destData = reinterpret_cast<unsigned int*>(dest);
Затем ваш просмотрщик массивов должен увидеть все ваши данные. Имейте в виду, что viwer действительно использует ваши данные размером 32 бита. Таким образом, вы должны выполнить преобразование (делится на 2, потому что 16 бит, используйте 4 для 8 бит).
concurrency::array_view<const unsigned int> source( (size+ 7)/2, sourceData) );
concurrency::array_view<unsigned int> dest( (size+ 7)/2, sourceData) );
Теперь вы можете написать типичный блок for_each.
typedef concurrency::array_view<const unsigned int> OriginalImage;
typedef concurrency::array_view<unsigned int> ResultImage;
bool Filters::Filter_Invert()
{
const int size = k_width*k_height;
const int maxVal = GetMaxSize();
OriginalImage& im_original = GetOriginal();
ResultImage& im_result = GetResult();
im_result.discard_data();
parallel_for_each(
concurrency::extent<2>(k_width, k_height),
[=](concurrency::index<2> idx) restrict(amp)
{
const int pos = GetPos(idx);
const int val = read_int16(im_original, pos);
write_int16(im_result, pos, maxVal - val);
});
return true;
}
int Filters::GetPos( const concurrency::index<2>& idx ) restrict(amp, cpu)
{
return idx[0] * Filters::k_height + idx[1];
}
И тут приходит волшебство:
template <typename T>
unsigned int read_int16(T& arr, int idx) restrict(amp, cpu)
{
return (arr[idx >> 1] & (0xFFFF << ((idx & 0x7) << 4))) >> ((idx & 0x7) << 4);
}
template<typename T>
void write_int16(T& arr, int idx, unsigned int val) restrict(amp, cpu)
{
atomic_fetch_xor(&arr[idx >> 1], arr[idx >> 1] & (0xFFFF << ((idx & 0x7) << 4)));
atomic_fetch_xor(&arr[idx >> 1], (val & 0xFFFF) << ((idx & 0x7) << 4));
}
Обратите внимание, что эти методы для 16 битов для 8 битов не будут работать, но не должно быть слишком сложно адаптировать его к 8 битам. На самом деле, это было основано на 8-битной версии, к сожалению, я не смог найти ссылку.
Надеюсь, поможет.
Дэвид