Мое происхождение этого подхода происходит из программирования шейдеров OpenGL, но проблема более абстрактная. Я напишу некоторый псевдо-код, чтобы уточнить, что я имею в виду.Использование макроса Rust для генерации и компиляции шейдера
В OpenGL рендеринг выполняется в так называемых «шейдерах». Шейдер - это ядро вычисления, которое применяется к каждому элементу набора данных, но с тем преимуществом, что вычисление выполняется на графическом процессоре и поэтому использует преимущество одновременного характера графического процессора для вычисления максимально возможного количества одновременно ,
Проблема заключается в том, что шейдеры представляются в виде текста во время компиляции, а шейдер должен быть скомпилирован во время выполнения драйвером графического процессора. Это означает, что в начале каждой программы функция init
должна скомпилировать каждый исходный файл шейдера в программу до того, как будет вызываться шейдер. Вот пример того, имейте в виду, что это упрощено псевдокод:
let shader_src_A = r#"
attribute float a;
attribute float b;
out float b;
void main() {
b = a * b;
}
"#;
let shader_src_B = r#"
attribute float a;
attribute float b;
out float b;
void main() {
b = a + b;
}
"#;
let mut program_A : ShaderProgram;
let mut program_B : ShaderProgram;
fn init() {
initGL();
program_A = compile_and_link(shader_src_A);
program_B = compile_and_link(shader_src_B);
}
fn render() {
let data1 = vec![1,2,3,4];
let data2 = vec![5,6,7,8];
// move data to the gpu
let gpu_data_1 = move_to_gpu(data1);
let gpu_data_2 = move_to_gpu(data2);
let gpu_data_3 : GpuData<float>;
let gpu_data_4 : GpuData<float>;
program_A(
(gpu_data_1, gpu_data_2) // input
(gpu_data_3,) // output
);
program_B(
(gpu_data_1, gpu_data_2) // input
(gpu_data_4,) // output
);
let data_3 = move_to_cpu(gpu_data_3);
let data_4 = move_to_cpu(gpu_data_4);
println!("data_3 {:?} data_4 {:?}", data_3, data_4);
// data_3 [5, 12, 21, 32] data_4 [6, 8, 10, 12]
}
Цель для меня, чтобы быть в состоянии написать что-то вроде этого:
fn init() {
initGL();
mystery_macro!();
}
fn render() {
let data1 = vec![1,2,3,4];
let data2 = vec![5,6,7,8];
// move data to the gpu
let gpu_data_1 = move_to_gpu(data1);
let gpu_data_2 = move_to_gpu(data2);
let gpu_data_3 : GpuData<float>;
let gpu_data_4 : GpuData<float>;
shade!(
(gpu_data_1, gpu_data_2), // input tuple
(gpu_data_3,), // output tuple
"gpu_data_3 = gpu_data_1 * gpu_data_2;" // this is the shader source, the rest should be generated by the macro.
);
shade!(
(gpu_data_1, gpu_data_2), // input tuple
(gpu_data_3,), // output tuple
"gpu_data_4 = gpu_data_1 + gpu_data_2;" // this is the shader source, the rest should be generated by the macro.
);
let data_3 = move_to_cpu(gpu_data_3);
let data_4 = move_to_cpu(gpu_data_4);
println!("data_3 {:?} data_4 {:?}", data_3, data_4);
}
Основное различие заключается в том, что у меня нет общее место, где написаны все мои шейдеры. Я пишу свои шейдеры, где я их называю, и я не пишу часть шейдера, которая может быть выведена другими аргументами. Генерация части отсутствующего шейдера должна быть прямой, проблема заключается в компиляции шейдера. Средство визуализации, которое вызывает компиляцию каждого шейдера для каждого вызова, слишком медленное, чтобы быть полезным вообще. Идея заключается в том, что макрос должен генерировать это общее место со всеми источниками и программами шейдеров, так что функция init
может компилировать и связывать все программы при запуске программы.
Несмотря на название, я также согласен с решением, которое решает мою проблему по-другому, но я бы предпочел решение, в котором все программы могут быть скомпилированы в функции init
.
EDIT:
Я мог бы также представить себе, что тень не макрос, а заполнитель не оп функции, макрос не будет работать на функцию тени, и путем обхода AST, он может найти все звонки в тень и создание всего, что нужно сделать в функции init.
Итак, вы пытаетесь сгенерировать код в функции init, заданной источником шейдера, из совершенно другой области кода? Я не уверен, что это возможно. – LinearZoetrope
Например, вы должны иметь возможность легко написать макрос, который будет вставлять строку в шаблон источника шейдера, но поиск этих строк из загадки «shade!» Вызывает то, что в коде совершенно несовместимо, не произойдет (по крайней мере не без серьезной темной магии, с которой я не знаком). Вы МОЖЕТЕ сделать это с расширением компилятора. – LinearZoetrope
@ Jsor На данный момент я думаю, что если вы можете скомпилировать программы, ленивые при первом вызове. Таким образом, при первом вызове генерируется источник, программа скомпилирована и выполнена. Второй вызов цикла рендеринга, программа должна быть повторно использована. – Arne