Для упаковки шейдеров с использованием Vulkan API, необходимо выполнить следующие шаги:
1. Создать структуру для хранения информации о шейдере. В структуре должны быть поля для указания типа шейдера (например, vertex, fragment или geometry), его имени и других параметров.
2. Создать буфер для хранения шейдеров. Буфер должен быть создан с помощью функции vkCreateBuffer.
3. Создать массив указателей на шейдеры. Массив должен содержать указатели на созданные шейдеры, которые вы хотите упаковать.
4. Создать команду загрузки шейдеров в буфер. Команда должна содержать информацию о буфере, массиве указателей и количестве шейдеров, которые нужно загрузить.
5. Выполнить команду загрузки шейдеров с помощью функции vkCmdBindPipeline и vkCmdBindVertexBuffers.
6. Завершить работу с шейдерами.
#include <vector>
#include <vulkan/vulkan.h>
struct ShaderInfo {
VkShaderType shaderType;
const char* name;
};
void packShaders(const std::vector<ShaderInfo>& shaders, const VkDevicesevice){
// Создаем буфер для хранения шейдеров
VkBuffer buffer;
vkCreateBuffer(device->device(), nullptr, &buffer, VK_BUFFER_CREATE_INFO());
//Cоздаем массив указателей
std::vector<VkShaderModule*> shaderModules;
for (const auto & shader: shaders){
VkShaderModule module;
vkCreateShaderModule(device->device(), &Shader.name, nullptr, &module);
shaderModules.push_back(&module);
}
//Загружаем шейдеры в буфер
VkCommandBuffer commandBuffer;
vkAllocateCommandBuffers(device->device(), VK_COMMAND_BUFFER_LEVEL_PRIMARY, 1, &commandBuffer);
VkPipelineStageFlags stages[3] = {VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT};
VkSubmitInfo submitInfo = {} ;
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer ;
vkBeginCommandBuffer(commandBuffer, VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexBuffer, &stride);
uint32_t index = 0 ;
for (auto& shaderModule: shaderModules) {
vkCmdBindShaderModule(commandBuffer, index++, shaderModule);
}
vkEndCommandBuffer(commandBuffer);
submitInfo.signalSemaphoreCount = 0 ;
submitInfo.waitSemaphoreCount = 0 ;
vkQueueSubmit(device->queue(), 1, &submitInfo, VK_NULL_HANDLE);
//Удаляем буфер и модуль
vkDestroyBuffer(buffer, nullptr);
for (auto& module: shaderModules) {
vkDestroyShaderModule (module, nullptr);
}
}
Характеристика производительности аналитических рабочих нагрузок.
В типичном приложении для медиааналитики происходит декодирование, анализирование аналитических метаданных.
Понимание отличия между этапами обработки может помочь разработать правильную архитектуру для приложений медиааналитики:
1) Если не используется кодирование мультимедиа, аналитика рабочих нагрузок, как правило, более вычислительна и интенсивнее, чем декодирование и постобработка мультимедиа.
Это означает, что любые усилия по оптимизации должны сначала быть направлены на оптимизацию рабочих нагрузок аналитики, а затем на декодирование и постобработку.
2) Аналитические рабочие нагрузки обычно ограничены памятью в то время как декодирование мультимедиа обычно ограничено процессором
3) Аналитические рабочие нагрузки работают с цветом RGB, в то время как кодирование и декодирование мультимедиа с пространством YUV.
Таким образом, между декодированием мультимедиа и аналитикой этап постобработки преобразует цветовое пространство и изменяет размер изображения. Если есть более 1 модели аналитики, может быть одно медиа декодирование с последующим этапом постобработки и аналитики в конвейере, который приводит к важности включения потоков и данных в параллелизм.
4) Многопоточность имеет решающее значение для архивирования всего конвейера и производительности, поскольку рабочие нагрузки аналитики по своей природе параллельны, где анализ может быть выполнен на нескольких потоках, несколько кадров изображения или в пределах нескольких областей изображения параллельно.
5) Рабочие нагрузки аналитики требуют тензорных вычислений. (структуры данных, используемые в моделях нейронных сетей) как ввод и вывод.
Поскольку любая медиа-инфраструктура обычно поддерживает: изображения и звуковые кадры. Правильная абстракция имеет решающее значение для того, чтобы не запутать разработчиков и увеличить медиа-фреймворк кривой обучения.
Дифференцирование рендеринга на основе трассировки лучей моделирует модели переноса света с обработкой видимости.
Следующая программа является примером положения вершин и оптимизация карты нормалей на рендере и физически обосновывает затенение.
В коде сначала объявляется экземпляр рендера и загружаются файлы сцены. Затем, строится прямой вычислительный граф для рендеринга и расcчитываются потери. Потом рассчитываются, позиции вершин и карта нормалей и указываются как цели оптимизации флагом require-gradient. После этих настроек выполняется основной процесс оптимизации с помощью execStep(). Наконец, оптимизированные результаты сохраняются.
class BasicRenderer
{
public:
BasicRenderer();
void loadScene(std::string gltf_filename, std::string target_img_filename)
{
// Загрузим исходную сцену из файла и проанализируйте структуры CpuImage.
std::tie(m_img_map["vtx_pos"], m_img_map["vtx_uv"], m_img_map["faces"],
m_img_map["world_mat"], m_img_map["view_mat"],
m_img_map["prj_mat"], m_img_map["env_img"],
m_img_map["albedo"], m_img_map["metallic"],
m_img_map["roughness"], m_img_map["normal"],
m_img_map["background"]) = LoadGltfAsCpuImages(gltf_filename);
// Load target an image file and parse to a CpuImage structure
m_img_map["target"] = LoadTargetImageAsCpuImage(target_img_filename);
// Create top variables of a computational graph
m_var_map["vtx_pos"] = {VEC3, m_img_map["vtx_pos"].getImgSize()};
m_var_map["vtx_uv"] = {VEC2, m_img_map["vtx_uv"].getImgSize()};
m_var_map["faces"] = {IVEC3, m_img_map["faces"].getImgSize()};
m_var_map["model_mat"] = {MAT4, {1, 1}};
m_var_map["view_mat"] = {MAT4, {1, 1}};
m_var_map["prj_mat"] = {MAT4, {1, 1}};
m_var_map["env_img"] = {VEC3, m_img_map["env_img"].getImgSize()};
m_var_map["albedo"] = {VEC3, m_img_map["albedo"].getImgSize()};
m_var_map["metallic"] = {FLOAT, m_img_map["metallic"].getImgSize()};
m_var_map["roughness"] = {FLOAT, m_img_map["roughness"].getImgSize()};
m_var_map["normal"] = {VEC3, m_img_map["normal"].getImgSize()};
m_var_map["background"] = {VEC3, m_img_map["background"].getImgSize()};
m_var_map["target"] = {VEC4, m_img_map["target"].getImgSize()};
// Установим флаг для отправки изображений ЦП на ГП
m_is_sent = false;
}
void buildGraph(uint32_t K, float r, float sigma, float delta, float lr)
{
// Построим вычислительный граф рендеринга
Variable rendered_img = BuildBasicRenderGraph(
m_var_map["vtx_pos"], m_var_map["vtx_uv"], m_var_map["faces"],
m_var_map["model_mat"], m_var_map["view_mat"],
m_var_map["prj_mat"], m_var_map["env_img"],
m_var_map["albedo"], m_var_map["metallic"],
m_var_map["roughness"], m_var_map["normal"],
m_var_map["background"], K, r, sigma, delta);
// Учтем потери
Variable loss = F::Mean(F::Abs(m_var_map["target"] - rendered_img));
// Установим потери и оптимизатор
m_optimizer.setLossVar(loss);
m_optimizer.setOptimizer([=](Variables xs, Variables gxs)
{
Variables updated_xs;
for (size_t i = 0; i < xs.size(); i++) {
updated_xs.push_back(xs[i] - gxs[i] * lr);
}
return updated_xs;
});
}
void setRequiresGrad(std::string name) {
// Установим флаг требуемого градиента
m_var_map[name].setRequiresGradRecursively();
}
void execStep()
{
// При необходимости отправляем изображения ЦП на ГП.
if (!m_is_sent)
{
for (auto [name, var]: m_var_map)
{
m_optimizer.sendImg(var, m_img_map[name]);
}
m_is_sent = false;
}
// Выполниv одну итерацию оптимизации
m_optimizer.execStep();
}
void saveScene(std::string gltf_filename)
{
// Получиv все изображения графического процессора
for (auto [name, var]: m_var_map)
{
m_img_map[name] = m_optimizer.recv(var);
}
// Сохраниv оптимизированную сцену в файл GLTF.
SaveGltfFromCpuImages(m_img_map);
}
private:
Optimizer m_optimizer;
// Карта данных сцены для CpuImage
std::map<std::string, CpuImage> m_img_map;
// Карта данных сцены для переменной, соответствующей CpuImage
std::map<std::string, Variable> m_var_map;
// Внутренние флаги
bool m_is_sent = false;
};
void OptimizeVertexPostionAndNormalTexture() ;
int main(int argc, char *argv[]) {
OptimizeVertexPostionAndNormalTexture();
return 0;
}
void OptimizeVertexPostionAndNormalTexture()
{
BasicRenderer renderer;
// Загружаем данные сцены на процессор
renderer.loadScene("initial_scene.gltf", "target_img.png");
// Построим вычислительный граф
uint32_t K = 2; // Количество пилинга
float r = 0.01f; // Параметр радиуса
float sigma = r / 7.f; // Параметр смешивания
float delta = r; // Ширина края силуэта
float lr = 0.01f; // Скорость обучения для оптимизатора
renderer.buildGraph(K, r, sigma, delta, lr);
// Отметим позиции вершин и текстуру нормалей как цели оптимизации
renderer.setRequiresGrad("vtx_pos");
renderer.setRequiresGrad("normal");
// Итерации оптимизации
for (int iter = 0; iter < 1000; iter++)
{
renderer.execStep();
}
// Сохраним оптимизированные данные в файл
renderer.saveScene("optimized_scene.gltf");
}
Сшивка изображений предназначена для сшивания изображений, взятых из различных точек зрения в изображение с более широким полем зрения.
Разработан алгоритм сшивки и сделана визуализация процесса для панорамной камеры
Предложена структура глубокого сшивания изображений состоящая из двух модулей:
1) Модуль деформации
2) Модуль непосредственно сшивки использует модель оценки оптического потока для получения попиксельной деформации изображений, далее перемещает пиксели целевого изображения полученные warp модулем. Модуль сшивки смешивает деформированные целевые изображения и эталонные изображения, устраняя при этом нежелательные артефакты, такие как несовпадения, швы и отверстия, которые наносят ущерб правдоподобию сшитого результата.