Зачем подключать CUDA к Qt
Типичные сценарии:
- компьютерное зрение
- фильтрация сигналов
- рендеринг
- ML-инференс
- физические симуляции
- обработка больших матриц
Qt отвечает за:
- GUI
- события
- визуализацию
- настройки
CUDA — за:
- тяжёлые вычисления
- массовый параллелизм
- работу с GPU-памятью
Ключевой принцип архитектуры:
UI не знает о CUDA напрямую.
GPU-код изолирован в backend-библиотеке.
Архитектура проекта
qt-cuda-app/ ├── CMakeLists.txt ├── app/ # Qt GUI │ ├── main.cpp │ ├── mainwindow.* │ └── CMakeLists.txt ├── gpu_backend/ # CPU-wrapper │ ├── gpu_api.h │ ├── gpu_api.cpp │ └── CMakeLists.txt ├── cuda_kernels/ # CUDA │ ├── matmul.cu │ ├── matmul.h │ └── CMakeLists.txt
Разделение ролей
| Модуль | Ответственность |
|---|---|
| app | Qt GUI |
| gpu_backend | C++ API, вызываемый из Qt |
| cuda_kernels | kernels + device-код |
GUI вызывает обычный C++ интерфейс — без __global__.
Backend API
gpu_backend/gpu_api.h
#pragma once
#include <vector>
namespace gpu {
std::vector<float> multiplyMatrices(
const std::vector<float>& A,
const std::vector<float>& B,
int M, int K, int N);
}
gpu_backend/gpu_api.cpp
#include "gpu_api.h"
#include "matmul.h"
namespace gpu {
std::vector<float> multiplyMatrices(
const std::vector<float>& A,
const std::vector<float>& B,
int M, int K, int N)
{
std::vector<float> C(M * N);
cudaMatMul(A.data(), B.data(),
C.data(), M, K, N);
return C;
}
}
CUDA kernel
cuda_kernels/matmul.h
#pragma once
void cudaMatMul(const float* A,
const float* B,
float* C,
int M, int K, int N);
cuda_kernels/matmul.cu
#include "matmul.h"
#include <cuda_runtime.h>
#include <stdexcept>
#define CUDA_CHECK(x) \
do { \
cudaError_t err = x; \
if (err != cudaSuccess) \
throw std::runtime_error(cudaGetErrorString(err)); \
} while (0)
constexpr int TILE = 16;
__global__ void matMulKernel(const float* A,
const float* B,
float* C,
int M, int K, int N)
{
__shared__ float sA[TILE][TILE];
__shared__ float sB[TILE][TILE];
int row = blockIdx.y * TILE + threadIdx.y;
int col = blockIdx.x * TILE + threadIdx.x;
float acc = 0.f;
for (int t = 0; t < (K + TILE - 1) / TILE; ++t) {
int aCol = t * TILE + threadIdx.x;
int bRow = t * TILE + threadIdx.y;
sA[threadIdx.y][threadIdx.x] =
(row < M && aCol < K)
? A[row * K + aCol]
: 0.f;
sB[threadIdx.y][threadIdx.x] =
(bRow < K && col < N)
? B[bRow * N + col]
: 0.f;
__syncthreads();
for (int k = 0; k < TILE; ++k)
acc += sA[threadIdx.y][k] *
sB[k][threadIdx.x];
__syncthreads();
}
if (row < M && col < N)
C[row * N + col] = acc;
}
void cudaMatMul(const float* A,
const float* B,
float* C,
int M, int K, int N)
{
size_t sizeA = M * K * sizeof(float);
size_t sizeB = K * N * sizeof(float);
size_t sizeC = M * N * sizeof(float);
float *dA, *dB, *dC;
CUDA_CHECK(cudaMalloc(&dA, sizeA));
CUDA_CHECK(cudaMalloc(&dB, sizeB));
CUDA_CHECK(cudaMalloc(&dC, sizeC));
CUDA_CHECK(cudaMemcpy(dA, A, sizeA,
cudaMemcpyHostToDevice));
CUDA_CHECK(cudaMemcpy(dB, B, sizeB,
cudaMemcpyHostToDevice));
dim3 threads(TILE, TILE);
dim3 blocks((N + TILE - 1) / TILE,
(M + TILE - 1) / TILE);
matMulKernel<<<blocks, threads>>>(dA, dB, dC,
M, K, N);
CUDA_CHECK(cudaDeviceSynchronize());
CUDA_CHECK(cudaMemcpy(C, dC, sizeC,
cudaMemcpyDeviceToHost));
cudaFree(dA);
cudaFree(dB);
cudaFree(dC);
}
Qt GUI вызывает GPU
app/mainwindow.cpp
#include "mainwindow.h"
#include "gpu_api.h"
void MainWindow::runGpuDemo()
{
constexpr int M = 256;
constexpr int K = 256;
constexpr int N = 256;
std::vector<float> A(M * K, 1.f);
std::vector<float> B(K * N, 2.f);
auto C = gpu::multiplyMatrices(A, B, M, K, N);
ui->resultLabel->setText(
QString("C[0] = %1").arg(C[0]));
}
GPU-код запускается из кнопки Qt — прозрачно для UI.
CMake-интеграция
Root CMakeLists.txt
cmake_minimum_required(VERSION 3.24) project(QtCuda LANGUAGES CXX CUDA) add_subdirectory(cuda_kernels) add_subdirectory(gpu_backend) add_subdirectory(app)
cuda_kernels/CMakeLists.txt
add_library(cuda_kernels STATIC matmul.cu)
set_target_properties(cuda_kernels PROPERTIES
CUDA_SEPARABLE_COMPILATION ON
)
set_property(TARGET cuda_kernels PROPERTY
CUDA_ARCHITECTURES native)
target_compile_features(cuda_kernels PUBLIC cxx_std_17)
gpu_backend/CMakeLists.txt
add_library(gpu_backend STATIC gpu_api.cpp)
target_include_directories(gpu_backend PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/cuda_kernels)
target_link_libraries(gpu_backend PUBLIC cuda_kernels)
app/CMakeLists.txt
find_package(Qt6 REQUIRED COMPONENTS Widgets)
qt_add_executable(app
main.cpp
mainwindow.cpp
)
target_link_libraries(app PRIVATE
Qt6::Widgets
gpu_backend)
Порядок выполнения:
1️⃣ Qt GUI → gpu_backend
2️⃣ gpu_backend → CUDA wrapper
3️⃣ kernel запускается
4️⃣ GPU считает
5️⃣ результат возвращается
6️⃣ GUI обновляет интерфейс
