Compare commits
2 Commits
1a05d3a6d9
...
92a595b2f1
| Author | SHA1 | Date | |
|---|---|---|---|
| 92a595b2f1 | |||
| 8abdea6b77 |
Vendored
-7
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
// Используйте IntelliSense, чтобы узнать о возможных атрибутах.
|
|
||||||
// Наведите указатель мыши, чтобы просмотреть описания существующих атрибутов.
|
|
||||||
// Для получения дополнительной информации посетите: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": []
|
|
||||||
}
|
|
||||||
Vendored
+7
-7
@@ -8,13 +8,13 @@
|
|||||||
"-fdiagnostics-color=always",
|
"-fdiagnostics-color=always",
|
||||||
"-g",
|
"-g",
|
||||||
"-fopenmp",
|
"-fopenmp",
|
||||||
"${fileDirname}/main.cpp",
|
"${workspaceFolder}/main.cpp",
|
||||||
"${fileDirname}/Xenith/core.cpp",
|
"${workspaceFolder}/Xenith/core.cpp",
|
||||||
"${fileDirname}/Xenith/token/token.cpp",
|
"${workspaceFolder}/Xenith/token/token.cpp",
|
||||||
"-o",
|
"-I", "${workspaceFolder}/Xenith",
|
||||||
"${fileDirname}/main",
|
"-I", "${workspaceFolder}/Xenith/token",
|
||||||
"-I", "${fileDirname}/Xenith",
|
"-o", "${workspaceFolder}/main",
|
||||||
"-I", "${fileDirname}/Xenith/token"
|
"-lvulkan"
|
||||||
],
|
],
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${fileDirname}"
|
"cwd": "${fileDirname}"
|
||||||
|
|||||||
+173
-3
@@ -2,10 +2,56 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <omp.h>
|
#include <omp.h>
|
||||||
|
#include <vulkan/vulkan.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <chrono>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
#define MAX_CORES 16
|
|
||||||
|
|
||||||
NeuralNetwork::NeuralNetwork(LayerStructure_t layers[], int count) : numLayers(count) {
|
|
||||||
|
NeuralNetwork::NeuralNetwork(LayerStructure_t layers[], int count, bool useVulkan) : numLayers(count) {
|
||||||
|
|
||||||
|
if (useVulkan) {
|
||||||
|
vk::ApplicationInfo appInfo{"Xenith", 1, nullptr, 0, VK_API_VERSION_1_1};
|
||||||
|
instance = vk::createInstance({{}, &appInfo});
|
||||||
|
|
||||||
|
// 2. Выбор видеокарты
|
||||||
|
auto physicalDevices = instance.enumeratePhysicalDevices();
|
||||||
|
if (physicalDevices.empty()) throw std::runtime_error("GPU с поддержкой Vulkan не найдены!");
|
||||||
|
physDev = physicalDevices[0];
|
||||||
|
|
||||||
|
std::cout << "Используем GPU: " << physDev.getProperties().deviceName << std::endl;
|
||||||
|
|
||||||
|
// 3. Поиск очереди для вычислений (Compute)
|
||||||
|
auto queueProps = physDev.getQueueFamilyProperties();
|
||||||
|
int computeFamily = -1;
|
||||||
|
for (int i = 0; i < queueProps.size(); i++) {
|
||||||
|
if (queueProps[i].queueFlags & vk::QueueFlagBits::eCompute) {
|
||||||
|
computeFamily = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (computeFamily == -1) throw std::runtime_error("GPU не поддерживает вычисления (Compute)");
|
||||||
|
|
||||||
|
// ВАЖНО: Сохраняем индекс в переменную класса, чтобы использовать её везде
|
||||||
|
this->computeQueueFamilyIndex = (uint32_t)computeFamily;
|
||||||
|
|
||||||
|
// 4. Создание логического устройства
|
||||||
|
float priority = 1.0f;
|
||||||
|
vk::DeviceQueueCreateInfo queueInfo({}, computeQueueFamilyIndex, 1, &priority);
|
||||||
|
vk::DeviceCreateInfo deviceCreateInfo({}, 1, &queueInfo);
|
||||||
|
device = physDev.createDevice(deviceCreateInfo);
|
||||||
|
|
||||||
|
// 5. Получаем саму очередь
|
||||||
|
queue = device.getQueue(computeQueueFamilyIndex, 0);
|
||||||
|
|
||||||
|
// 6. Создаем пул команд (теперь используем правильный индекс)
|
||||||
|
vk::CommandPoolCreateInfo poolInfo({}, computeQueueFamilyIndex);
|
||||||
|
cmdPool = device.createCommandPool(poolInfo);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) sizes.push_back(layers[i].size);
|
for (int i = 0; i < count; i++) sizes.push_back(layers[i].size);
|
||||||
for (int i = 0; i < count - 1; i++) {
|
for (int i = 0; i < count - 1; i++) {
|
||||||
std::vector<std::vector<double>> layerW;
|
std::vector<std::vector<double>> layerW;
|
||||||
@@ -41,8 +87,9 @@ std::vector<double> NeuralNetwork::feedForward(const std::vector<double>& input)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
double NeuralNetwork::train(const std::vector<double>& input, const std::vector<double>& target, double lr) {
|
double NeuralNetwork::train(const std::vector<double>& input, const std::vector<double>& target, double lr) {
|
||||||
omp_set_num_threads(MAX_CORES);
|
omp_set_num_threads(cpu_count);
|
||||||
|
|
||||||
std::vector<double> pred = feedForward(input);
|
std::vector<double> pred = feedForward(input);
|
||||||
std::vector<std::vector<double>> errors(numLayers);
|
std::vector<std::vector<double>> errors(numLayers);
|
||||||
@@ -83,3 +130,126 @@ double NeuralNetwork::train(const std::vector<double>& input, const std::vector<
|
|||||||
|
|
||||||
return totalErr;
|
return totalErr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t NeuralNetwork::findMemoryType(uint32_t typeFilter, vk::MemoryPropertyFlags properties) {
|
||||||
|
vk::PhysicalDeviceMemoryProperties memProperties = physDev.getMemoryProperties();
|
||||||
|
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
|
||||||
|
if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Не удалось найти подходящий тип памяти!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Внутри класса NeuralNetwork в секции private:
|
||||||
|
std::vector<char> NeuralNetwork::readFile(const std::string& filename) {
|
||||||
|
std::ifstream file(filename, std::ios::ate | std::ios::binary);
|
||||||
|
|
||||||
|
if (!file.is_open()) {
|
||||||
|
throw std::runtime_error("Не удалось открыть файл шейдера: " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t fileSize = (size_t)file.tellg();
|
||||||
|
std::vector<char> buffer(fileSize);
|
||||||
|
|
||||||
|
file.seekg(0);
|
||||||
|
file.read(buffer.data(), fileSize);
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double NeuralNetwork::trainVulkan() {
|
||||||
|
// 1. Создание буферов
|
||||||
|
vk::Buffer inputBuffer = device.createBuffer({{}, sizeof(float) * 2, vk::BufferUsageFlagBits::eStorageBuffer});
|
||||||
|
vk::Buffer outputBuffer = device.createBuffer({{}, sizeof(float), vk::BufferUsageFlagBits::eStorageBuffer});
|
||||||
|
|
||||||
|
// 2. Выделение и привязка памяти для ВХОДА
|
||||||
|
vk::MemoryRequirements inReq = device.getBufferMemoryRequirements(inputBuffer);
|
||||||
|
vk::DeviceMemory inputMemory = device.allocateMemory({
|
||||||
|
inReq.size,
|
||||||
|
findMemoryType(inReq.memoryTypeBits, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent)
|
||||||
|
});
|
||||||
|
device.bindBufferMemory(inputBuffer, inputMemory, 0); // КРИТИЧНО: привязываем память к буферу
|
||||||
|
|
||||||
|
// 3. Копирование данных во входной буфер
|
||||||
|
float inputData[2] = {2.51f, 2.32f};
|
||||||
|
void* pIn = device.mapMemory(inputMemory, 0, sizeof(float) * 2);
|
||||||
|
memcpy(pIn, inputData, sizeof(float) * 2);
|
||||||
|
device.unmapMemory(inputMemory);
|
||||||
|
|
||||||
|
// 4. Выделение и привязка памяти для ВЫХОДА
|
||||||
|
vk::MemoryRequirements outReq = device.getBufferMemoryRequirements(outputBuffer);
|
||||||
|
vk::DeviceMemory outputMemory = device.allocateMemory({
|
||||||
|
outReq.size,
|
||||||
|
findMemoryType(outReq.memoryTypeBits, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent)
|
||||||
|
});
|
||||||
|
device.bindBufferMemory(outputBuffer, outputMemory, 0);
|
||||||
|
|
||||||
|
// 5. ДЕСКРИПТОРЫ (Связь C++ -> Шейдер)
|
||||||
|
// Описываем, что у нас есть 2 слота (binding 0 и 1)
|
||||||
|
std::vector<vk::DescriptorSetLayoutBinding> bindings = {
|
||||||
|
{0, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eCompute},
|
||||||
|
{1, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eCompute}
|
||||||
|
};
|
||||||
|
vk::DescriptorSetLayout dsLayout = device.createDescriptorSetLayout({{}, (uint32_t)bindings.size(), bindings.data()});
|
||||||
|
|
||||||
|
// Создаем пул и выделяем сет дескрипторов
|
||||||
|
vk::DescriptorPoolSize poolSize{vk::DescriptorType::eStorageBuffer, 2};
|
||||||
|
vk::DescriptorPool pool = device.createDescriptorPool({{}, 1, 1, &poolSize});
|
||||||
|
vk::DescriptorSet ds = device.allocateDescriptorSets({pool, 1, &dsLayout})[0];
|
||||||
|
|
||||||
|
// Указываем, какие именно буферы в какие слоты вставить
|
||||||
|
vk::DescriptorBufferInfo bInInfo{inputBuffer, 0, VK_WHOLE_SIZE};
|
||||||
|
vk::DescriptorBufferInfo bOutInfo{outputBuffer, 0, VK_WHOLE_SIZE};
|
||||||
|
device.updateDescriptorSets({
|
||||||
|
{ds, 0, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &bInInfo},
|
||||||
|
{ds, 1, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &bOutInfo}
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
// 6. ПАЙПЛАЙН (Загрузка шейдера)
|
||||||
|
auto shaderCode = readFile("shader.comp.spv"); // Твоя функция чтения файла
|
||||||
|
vk::ShaderModule shaderModule = device.createShaderModule({{}, shaderCode.size(), (uint32_t*)shaderCode.data()});
|
||||||
|
vk::PipelineLayout pipeLayout = device.createPipelineLayout({{}, 1, &dsLayout});
|
||||||
|
|
||||||
|
vk::ComputePipelineCreateInfo pipeInfo{{}, {{}, vk::ShaderStageFlagBits::eCompute, shaderModule, "main"}, pipeLayout};
|
||||||
|
vk::Pipeline pipeline = device.createComputePipeline(nullptr, pipeInfo).value;
|
||||||
|
|
||||||
|
// 7. КОМАНДЫ И ЗАПУСК (Command Buffer)
|
||||||
|
// (Предполагаем, что cmdPool и queue уже созданы в классе)
|
||||||
|
vk::CommandBufferAllocateInfo cmdAllocInfo(cmdPool, vk::CommandBufferLevel::ePrimary, 1);
|
||||||
|
vk::CommandBuffer cmd = device.allocateCommandBuffers(cmdAllocInfo)[0];
|
||||||
|
|
||||||
|
cmd.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
|
||||||
|
cmd.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline);
|
||||||
|
cmd.bindDescriptorSets(vk::PipelineBindPoint::eCompute, pipeLayout, 0, {ds}, {});
|
||||||
|
cmd.dispatch(1, 1, 1); // Запускаем 1 поток
|
||||||
|
cmd.end();
|
||||||
|
|
||||||
|
queue.submit(vk::SubmitInfo(0, nullptr, nullptr, 1, &cmd), nullptr);
|
||||||
|
queue.waitIdle();
|
||||||
|
|
||||||
|
// 8. ЗАБИРАЕМ РЕЗУЛЬТАТ
|
||||||
|
float result = 0;
|
||||||
|
void* pOut = device.mapMemory(outputMemory, 0, sizeof(float));
|
||||||
|
memcpy(&result, pOut, sizeof(float));
|
||||||
|
device.unmapMemory(outputMemory);
|
||||||
|
|
||||||
|
// Очистка (в реальном коде лучше делать в деструкторе)
|
||||||
|
device.destroyPipeline(pipeline);
|
||||||
|
device.destroyPipelineLayout(pipeLayout);
|
||||||
|
device.destroyShaderModule(shaderModule);
|
||||||
|
device.destroyDescriptorPool(pool);
|
||||||
|
device.destroyDescriptorSetLayout(dsLayout);
|
||||||
|
device.destroyBuffer(inputBuffer); device.freeMemory(inputMemory);
|
||||||
|
device.destroyBuffer(outputBuffer); device.freeMemory(outputMemory);
|
||||||
|
|
||||||
|
return (double)result;
|
||||||
|
}
|
||||||
|
|
||||||
|
NeuralNetwork::~NeuralNetwork() {
|
||||||
|
// Здесь позже мы добавим удаление vkInstance, vkDevice и прочего,
|
||||||
|
// чтобы не было утечек памяти на видеокарте.
|
||||||
|
}
|
||||||
|
|||||||
+32
-1
@@ -4,22 +4,53 @@
|
|||||||
#include "typedef.hpp"
|
#include "typedef.hpp"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <omp.h>
|
||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
class NeuralNetwork {
|
class NeuralNetwork {
|
||||||
private:
|
private:
|
||||||
|
// Параметры нейросети
|
||||||
int numLayers;
|
int numLayers;
|
||||||
std::vector<int> sizes;
|
std::vector<int> sizes;
|
||||||
std::vector<std::vector<std::vector<double>>> weights;
|
std::vector<std::vector<std::vector<double>>> weights;
|
||||||
std::vector<std::vector<double>> biases;
|
std::vector<std::vector<double>> biases;
|
||||||
std::vector<std::vector<double>> outputs;
|
std::vector<std::vector<double>> outputs;
|
||||||
|
|
||||||
|
// Объекты Vulkan
|
||||||
|
bool useVulkan; // Сохраняем выбор пользователя
|
||||||
|
vk::Instance instance;
|
||||||
|
vk::PhysicalDevice physDev;
|
||||||
|
vk::Device device;
|
||||||
|
vk::Queue queue;
|
||||||
|
vk::CommandPool cmdPool;
|
||||||
|
uint32_t computeQueueFamilyIndex; // Индекс очереди для команд
|
||||||
|
|
||||||
|
// Вспомогательные методы Vulkan
|
||||||
|
uint32_t findMemoryType(uint32_t typeFilter, vk::MemoryPropertyFlags properties);
|
||||||
|
std::vector<char> readFile(const std::string& filename);
|
||||||
|
|
||||||
|
// Математика CPU
|
||||||
double sigmoid(double x) { return 1.0 / (1.0 + exp(-x)); }
|
double sigmoid(double x) { return 1.0 / (1.0 + exp(-x)); }
|
||||||
double sigmoidDeriv(double x) { return x * (1.0 - x); }
|
double sigmoidDeriv(double x) { return x * (1.0 - x); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NeuralNetwork(LayerStructure_t layers[], int count);
|
int cpu_count = 1;
|
||||||
|
|
||||||
|
// Конструктор
|
||||||
|
NeuralNetwork(LayerStructure_t layers[], int count, bool useVulkan = false);
|
||||||
|
|
||||||
|
// Деструктор (ВАЖНО для очистки Vulkan)
|
||||||
|
~NeuralNetwork();
|
||||||
|
|
||||||
|
// Методы работы
|
||||||
std::vector<double> feedForward(const std::vector<double>& input);
|
std::vector<double> feedForward(const std::vector<double>& input);
|
||||||
double train(const std::vector<double>& input, const std::vector<double>& target, double lr);
|
double train(const std::vector<double>& input, const std::vector<double>& target, double lr);
|
||||||
|
|
||||||
|
// Наш тест Vulkan
|
||||||
|
double trainVulkan();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,2 +1,16 @@
|
|||||||
#version 450
|
#version 450
|
||||||
|
|
||||||
|
layout(local_size_x = 1) in; // Запускаем 1 поток
|
||||||
|
|
||||||
|
layout(std430, binding = 0) buffer InputBuffer {
|
||||||
|
float a;
|
||||||
|
float b;
|
||||||
|
} inputs;
|
||||||
|
|
||||||
|
layout(std430, binding = 1) buffer OutputBuffer {
|
||||||
|
float result;
|
||||||
|
} outputs;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
outputs.result = inputs.a * inputs.b;
|
||||||
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -10,6 +10,8 @@
|
|||||||
#include "Xenith/token/token.hpp"
|
#include "Xenith/token/token.hpp"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::string currentSystemPrompt = "";
|
std::string currentSystemPrompt = "";
|
||||||
|
|
||||||
LayerStructure_t layers[] = {
|
LayerStructure_t layers[] = {
|
||||||
@@ -113,7 +115,7 @@ int main() {
|
|||||||
|
|
||||||
int numLayers = sizeof(layers) / sizeof(layers[0]);
|
int numLayers = sizeof(layers) / sizeof(layers[0]);
|
||||||
|
|
||||||
NeuralNetwork nn(layers, numLayers);
|
NeuralNetwork nn(layers, numLayers, true);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
std::cout << "xentith~$ ";
|
std::cout << "xentith~$ ";
|
||||||
@@ -187,8 +189,10 @@ int main() {
|
|||||||
std::getline(std::cin, currentSystemPrompt);
|
std::getline(std::cin, currentSystemPrompt);
|
||||||
std::cout << "System Prompt updated!" << std::endl;
|
std::cout << "System Prompt updated!" << std::endl;
|
||||||
|
|
||||||
}
|
} else if (cmdIn == "/trainVulkan") {
|
||||||
else if (cmdIn == "/help") {
|
std::cout << nn.trainVulkan();
|
||||||
|
|
||||||
|
} else if (cmdIn == "/help") {
|
||||||
std::cout << "\n--- MENU ---" << std::endl;
|
std::cout << "\n--- MENU ---" << std::endl;
|
||||||
std::cout << "/train\n/trainFile\n/sysPrompt\n/help\n/exit\n";
|
std::cout << "/train\n/trainFile\n/sysPrompt\n/help\n/exit\n";
|
||||||
|
|
||||||
|
|||||||
+136
@@ -0,0 +1,136 @@
|
|||||||
|
#include <vulkan/vulkan.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
// --- ШАГ 0: ШЕЙДЕР ---
|
||||||
|
// Это микро-программа, которая будет работать прямо внутри видеокарты.
|
||||||
|
// В обучении нейронки один шейдер будет делать FeedForward, другой - расчет ошибок.
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
try {
|
||||||
|
const uint32_t N = 1024;
|
||||||
|
const size_t bufferSize = N * N * sizeof(float);
|
||||||
|
|
||||||
|
// --- ШАГ 1: INSTANCE (ЭКЗЕМПЛЯР) ---
|
||||||
|
// Это связь твоего приложения с драйвером Vulkan.
|
||||||
|
// Здесь мы говорим: "Привет, я хочу использовать Vulkan версии 1.1".
|
||||||
|
vk::ApplicationInfo appInfo{"NN_GPU", 1, nullptr, 0, VK_API_VERSION_1_1};
|
||||||
|
vk::Instance instance = vk::createInstance({{}, &appInfo});
|
||||||
|
|
||||||
|
// --- ШАГ 2: PHYSICAL DEVICE (ЖЕЛЕЗО) ---
|
||||||
|
// Мы ищем физическую видеокарту. В системе их может быть несколько.
|
||||||
|
auto physicalDevices = instance.enumeratePhysicalDevices();
|
||||||
|
vk::PhysicalDevice physDev = physicalDevices[0];
|
||||||
|
|
||||||
|
// --- ШАГ 3: QUEUE FAMILY (ОЧЕРЕДЬ КОМАНД) ---
|
||||||
|
// Видеокарта — это отдельный процессор. Мы не вызываем функции GPU напрямую,
|
||||||
|
// мы отправляем "список дел" (командный буфер) в очередь (Queue).
|
||||||
|
// Нам нужна очередь, умеющая в Compute (вычисления).
|
||||||
|
auto queueProps = physDev.getQueueFamilyProperties();
|
||||||
|
uint32_t computeFamily = 0; // Для упрощения берем первую, но в идеале нужно искать флаг eCompute
|
||||||
|
|
||||||
|
// --- ШАГ 4: LOGICAL DEVICE (ЛОГИЧЕСКИЙ ИНТЕРФЕЙС) ---
|
||||||
|
// Это наш "пульт управления" конкретной картой.
|
||||||
|
float priority = 1.0f;
|
||||||
|
vk::DeviceQueueCreateInfo qInfo({}, computeFamily, 1, &priority);
|
||||||
|
vk::Device device = physDev.createDevice({{}, 1, &qInfo});
|
||||||
|
vk::Queue queue = device.getQueue(computeFamily, 0);
|
||||||
|
|
||||||
|
// --- ШАГ 5: МЕНЕДЖМЕНТ ПАМЯТИ (БУФЕРЫ) ---
|
||||||
|
// Видеокарта не видит твой std::vector напрямую.
|
||||||
|
// Нужно: 1. Создать буфер в Vulkan. 2. Выделить под него видеопамять.
|
||||||
|
auto createBuffer = [&](vk::BufferUsageFlags usage) {
|
||||||
|
// Создаем описание буфера
|
||||||
|
vk::Buffer buffer = device.createBuffer({{}, bufferSize, usage});
|
||||||
|
|
||||||
|
// Узнаем требования буфера к памяти (сколько байт и какой тип)
|
||||||
|
vk::MemoryRequirements memReq = device.getBufferMemoryRequirements(buffer);
|
||||||
|
vk::PhysicalDeviceMemoryProperties memProp = physDev.getMemoryProperties();
|
||||||
|
|
||||||
|
// Ищем тип памяти, который доступен и для GPU, и для CPU (HostVisible),
|
||||||
|
// чтобы мы могли скопировать туда веса нейронки.
|
||||||
|
uint32_t memType = 0;
|
||||||
|
for (uint32_t i = 0; i < memProp.memoryTypeCount; i++) {
|
||||||
|
if ((memReq.memoryTypeBits & (1 << i)) &&
|
||||||
|
(memProp.memoryTypes[i].propertyFlags & (vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent))) {
|
||||||
|
memType = i; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::DeviceMemory memory = device.allocateMemory({memReq.size, memType});
|
||||||
|
device.bindBufferMemory(buffer, memory, 0);
|
||||||
|
return std::make_pair(buffer, memory);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Создаем 3 буфера: Веса, Входы, Результат
|
||||||
|
auto [bufA, memA] = createBuffer(vk::BufferUsageFlagBits::eStorageBuffer);
|
||||||
|
auto [bufB, memB] = createBuffer(vk::BufferUsageFlagBits::eStorageBuffer);
|
||||||
|
auto [bufC, memC] = createBuffer(vk::BufferUsageFlagBits::eStorageBuffer);
|
||||||
|
|
||||||
|
// --- ШАГ 6: DESCRIPTORS (СВЯЗКА ШЕЙДЕРА И ПАМЯТИ) ---
|
||||||
|
// Шейдеру нужно знать, что в "binding 0" лежит Матрица А, а в "binding 1" - Матрица B.
|
||||||
|
// Это настраивается через Descriptor Sets.
|
||||||
|
std::vector<vk::DescriptorSetLayoutBinding> bindings = {
|
||||||
|
{0, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eCompute},
|
||||||
|
{1, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eCompute},
|
||||||
|
{2, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eCompute}
|
||||||
|
};
|
||||||
|
vk::DescriptorSetLayout dsLayout = device.createDescriptorSetLayout({{}, (uint32_t)bindings.size(), bindings.data()});
|
||||||
|
|
||||||
|
// Создаем пул дескрипторов и сам набор
|
||||||
|
vk::DescriptorPoolSize poolSize{vk::DescriptorType::eStorageBuffer, 3};
|
||||||
|
vk::DescriptorPool pool = device.createDescriptorPool({{}, 1, 1, &poolSize});
|
||||||
|
vk::DescriptorSet ds = device.allocateDescriptorSets({pool, 1, &dsLayout})[0];
|
||||||
|
|
||||||
|
// Привязываем наши буферы к дескрипторам
|
||||||
|
vk::DescriptorBufferInfo bInfoA{bufA, 0, VK_WHOLE_SIZE}, bInfoB{bufB, 0, VK_WHOLE_SIZE}, bInfoC{bufC, 0, VK_WHOLE_SIZE};
|
||||||
|
device.updateDescriptorSets({
|
||||||
|
{ds, 0, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &bInfoA},
|
||||||
|
{ds, 1, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &bInfoB},
|
||||||
|
{ds, 2, 0, 1, vk::DescriptorType::eStorageBuffer, nullptr, &bInfoC}
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
// --- ШАГ 7: PIPELINE (КОНВЕЙЕР) ---
|
||||||
|
// Это замороженное состояние видеокарты: какой шейдер используем, какие связи.
|
||||||
|
// Настройка Pipeline дорогая, поэтому её делают один раз при старте.
|
||||||
|
vk::PushConstantRange pushConstant{vk::ShaderStageFlagBits::eCompute, 0, sizeof(uint32_t)};
|
||||||
|
vk::PipelineLayout layout = device.createPipelineLayout({{}, 1, &dsLayout, 1, &pushConstant});
|
||||||
|
|
||||||
|
// Загружаем бинарный SPIR-V шейдер
|
||||||
|
// ( readFile - самописная функция загрузки файла )
|
||||||
|
auto shaderCode = readFile("shader.comp.spv");
|
||||||
|
vk::ShaderModule shaderModule = device.createShaderModule({{}, shaderCode.size(), (uint32_t*)shaderCode.data()});
|
||||||
|
vk::PipelineShaderStageCreateInfo stageInfo{{}, vk::ShaderStageFlagBits::eCompute, shaderModule, "main"};
|
||||||
|
|
||||||
|
vk::Pipeline pipeline = device.createComputePipeline(nullptr, {{}, stageInfo, layout}).value;
|
||||||
|
|
||||||
|
// --- ШАГ 8: COMMAND BUFFER (ЗАПИСЬ КОМАНД) ---
|
||||||
|
// Vulkan работает так: ты записываешь все команды заранее в "плеер",
|
||||||
|
// а потом говоришь "Играй!". Это очень быстро.
|
||||||
|
vk::CommandPool cmdPool = device.createCommandPool({{}, computeFamily});
|
||||||
|
vk::CommandBuffer cmd = device.allocateCommandBuffers({cmdPool, vk::CommandBufferLevel::ePrimary, 1})[0];
|
||||||
|
|
||||||
|
cmd.begin({vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
|
||||||
|
cmd.bindPipeline(vk::PipelineBindPoint::eCompute, pipeline);
|
||||||
|
cmd.bindDescriptorSets(vk::PipelineBindPoint::eCompute, layout, 0, {ds}, {});
|
||||||
|
cmd.pushConstants(layout, vk::ShaderStageFlagBits::eCompute, 0, sizeof(uint32_t), &N);
|
||||||
|
// Dispatch - это и есть запуск вычислений. N/16 групп потоков.
|
||||||
|
cmd.dispatch(N / 16, N / 16, 1);
|
||||||
|
cmd.end();
|
||||||
|
|
||||||
|
// --- ШАГ 9: SUBMIT (ОТПРАВКА НА GPU) ---
|
||||||
|
// Только в этот момент видеокарта начинает реально работать.
|
||||||
|
queue.submit(vk::SubmitInfo(0, nullptr, nullptr, 1, &cmd), nullptr);
|
||||||
|
queue.waitIdle(); // Ждем, пока GPU доделает работу
|
||||||
|
|
||||||
|
// Теперь результат в bufC можно забрать через device.mapMemory()
|
||||||
|
|
||||||
|
std::cout << "GPU завершил обучение/расчет!" << std::endl;
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
std::cerr << "Ошибка: " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user