2016-11-18 16 views
0

Я хочу отправить целую цепочку в HLSL в виде 3D-массива, используя единство. Я пытался сделать это уже пару дней, но без какой-либо выгоды. Я пытался упаковать буферы друг в друга (StructuredBuffer<StructuredBuffer<StructuredBuffer<int>>>), но это просто не сработает. И мне нужно сделать эту вещь изменчивой, поэтому я не могу использовать массивы в struct. Что мне делать?3D-буферы в HLSL?

EDIT: Чтобы уточнить немного больше, что я пытаюсь сделать здесь, это медицинская программа. Когда вы делаете сканирование своего тела, некоторые файлы генерируются. Эти файлы называются DICOM-файлами (.dcm). Эти файлы выдаются доктору. Врач должен открыть программу, выбрать все файлы DICOM и загрузить их. Каждый файл DICOM содержит изображение. Однако эти изображения не являются обычными изображениями, которые используются в нашей повседневной жизни. Эти изображения имеют оттенки серого, и каждый пиксель имеет значение от -1000 до нескольких тысяч, поэтому каждый пиксель сохраняется как 2 байта (или Int16). Мне нужно создать 3D-модель тела, которое было отсканировано, поэтому я использую алгоритм Marching Cubes для его создания (посмотрите на Polygonising a Scalar Field). Проблема в том, что я использовал цикл для каждого пикселя примерно на 360 512 * 512 размерах изображений, что занимало слишком много времени. Раньше я читал пиксельные данные из каждого файла, когда мне это нужно, когда я использовал CPU. Теперь я пытаюсь сделать этот процесс во время выполнения. Мне нужно отправить все пиксельные данные на графический процессор перед его обработкой. Это моя проблема. Мне нужен графический процессор для чтения данных с диска. Поскольку это невозможно, мне нужно отправить 360 * 512 * 512 * 4 байта данных на GPU в виде 3D-массива Ints. Я также планирую хранить данные там, чтобы избежать повторной передачи этого огромного объема памяти. Что мне делать? Refer to this link to know more about what I'm doing

+1

Вы можете сгладить свой массив? как FlatArr [x + WIDTH * (y + DEPTH * z)] = 3DArr [x, y, z] с размерами 3DArr [HEIGHT, WIDTH, DEPTH] – TheDjentleman

+0

Максимальный размер буфера в HLSL равен 2048 байтам, что тоже маленький для моей цели @xyLe_ – None

ответ

1

Из того, что я понял, я бы предложил попробовать следующее:

  1. Свести ваши данные (вложенные буферы не то, что вы хотите на вашем ГПА)

  2. Разделить данные по несколько раз ComputeBuffers, если это необходимо (когда я играл с ними на Nvidia Titan XI, можно было хранить примерно 1 ГБ данных на один буфер. Я представлял 3D-облако точек с 1,5 ГБ данных или что-то еще, 360 Мбайт данных, которые вы упомянули, не должны быть проблема тогда)

  3. Если вам нужно несколько буферов: пусть они перекрываются, сколько необходимо для марширующих кубов алгоритма

  4. ли все ваши расчеты в ComputeShader (я думаю, что требует DX11, если у вас есть несколько буфер, запустить его несколько раз и накапливать ваши результаты), а затем использовать полученные результаты в стандартном шейдера, который ваш звонок из OnPostRender функции (использование Graphics.DrawProcedural внутри просто рисовать точки или построить сетку на ГПА)

Редактировать (Может быть интересными для вас)

Если вы хотите добавить данные в буфер gpu (потому что вы не знаете точный размер или вы не можете записать его в gpu сразу), вы можете использовать AppendBuffers и ComputeShader.

C# Script Фрагменты:

struct DataStruct 
{ 
    ... 
} 

DataStruct[] yourData; 
yourData = loadStuff();  

ComputeBuffer tmpBuffer = new ComputeBuffer(512, Marshal.SizeOf(typeof(DataStruct))); 
ComputeBuffer gpuData = new ComputeBuffer(MAX_SIZE, Marshal.SizeOf(typeof(DataStruct)), ComputeBufferType.Append); 

for (int i = 0; i < yourData.Length/512; i++) { 

    // write data subset to temporary buffer on gpu 
    tmpBuffer.SetData(DataStruct.Skip(i*512).Take((i+1)*512).ToArray()); // Use fancy Linq stuff to select data subset 

    // set up and run compute shader for appending data to "gpuData" buffer 
    AppendComputeShader.SetBuffer(0, "inBuffer", tmpBuffer); 
    AppendComputeShader.SetBuffer(0, "appendBuffer", gpuData); 
    AppendComputeShader.Dispatch(0, 512/8, 1, 1); // 8 = gpu work group size -> use 512/8 work groups 
} 

ComputeShader:

struct DataStruct // replicate struct in shader 
{ 
    ... 
} 

#pragma kernel append 
StructuredBuffer<DataStruct> inBuffer; 
AppendStructuredBuffer<DataStruct> appendBuffer; 

[numthreads(8,1,1)] 
void append(int id: SV_DispatchThreadID) { 
    appendBuffer.Append(inBuffer[id]); 
} 

Примечание:

  • AppendComputeShader должен быть назначен через инспектор
  • 512 произвольный размер партии, существует верхний предел того, сколько данных вы можете добавить в буфер gpu сразу, но я думаю, что это зависит от аппаратного обеспечения (для меня это было 65536 * 4 байта)
  • вы должны предоставить максимальный размер для буферов gpu (на Titan X кажется до ~ 1 ГБ)
+0

Кстати, сколько времени потребовалось, чтобы отправить 1 ГБ данных на GPU после того, как вы выполнили свою систему рендеринга @xyLe_? – None

+1

@ Только в конце около 15 минут. это происходило довольно быстро, когда я читал данные с использованием нескольких потоков, а затем писал их партиями ~ 65 тыс. «частиц» на gpu – TheDjentleman

1

В Unity в настоящее время мы имеем MaterialPropertyBlock, что позволяет SetMatrixArray и SetVectorArray, и сделать это еще слаще, мы можем установить глобально с использованием шейдерных статических помощников SetGlobalVectorArray и SetGlobalMatrixArray. Я считаю, что это поможет вам.

Если вы предпочитаете старый способ, просмотрите this quite nice article, где показано, как передавать массивы векторов.

+0

Размеры моего 3D-массива могут сильно различаться, состоящие не только из пары значений в размерах y/z. – None