Files
SoilSimulator/compile_test.cpp
2025-09-08 13:20:35 +02:00

183 lines
6.4 KiB
C++

// opencl_cache.cpp
#include <CL/opencl.hpp> // OpenCL C++ bindings
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <filesystem> // C++17
#include <stdexcept>
// -------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------
// Read a text file into a string
std::string read_file(const std::string &path)
{
std::ifstream f(path, std::ios::in | std::ios::binary);
if (!f) throw std::runtime_error("Cannot open file: " + path);
std::ostringstream ss;
ss << f.rdbuf();
return ss.str();
}
// Read a binary file into a std::vector<uint8_t>
std::vector<uint8_t> read_binary(const std::string &path)
{
std::ifstream f(path, std::ios::binary);
if (!f) throw std::runtime_error("Cannot open binary file: " + path);
return std::vector<uint8_t>(std::istreambuf_iterator<char>(f),
std::istreambuf_iterator<char>());
}
// Write a binary buffer to file
void write_binary(const std::string &path, const std::vector<uint8_t> &data)
{
std::ofstream f(path, std::ios::binary);
if (!f) throw std::runtime_error("Cannot write binary file: " + path);
f.write(reinterpret_cast<const char*>(data.data()), data.size());
}
// Create a unique key for the device (vendor + name + global mem size)
std::string device_key(const cl::Device &dev)
{
std::ostringstream ss;
ss << dev.getInfo<CL_DEVICE_VENDOR>()
<< "-" << dev.getInfo<CL_DEVICE_NAME>()
<< "-" << dev.getInfo<CL_DEVICE_GLOBAL_MEM_SIZE>();
return ss.str();
}
// -------------------------------------------------------------------
// Main
// -------------------------------------------------------------------
int main()
{
try
{
// 1) Pick the first GPU device on the first platform
std::vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
if (platforms.empty())
throw std::runtime_error("No OpenCL platforms found");
cl::Platform platform = platforms[0];
std::vector<cl::Device> devices;
platform.getDevices(CL_DEVICE_TYPE_GPU, &devices);
if (devices.empty())
throw std::runtime_error("No GPU devices on the platform");
cl::Device device = devices[0];
std::cout << "Using platform: "
<< platform.getInfo<CL_PLATFORM_NAME>() << '\n';
std::cout << "Using device : "
<< device.getInfo<CL_DEVICE_NAME>() << '\n';
// 2) Context & cache file name
cl::Context ctx(device);
std::string bin_file = device_key(device) + ".bin";
std::filesystem::path bin_path = std::filesystem::current_path() / bin_file;
cl::Program program;
// 3) If we already have a binary, load it
if (std::filesystem::exists(bin_path))
{
std::cout << "Loading binary from " << bin_path << '\n';
std::vector<uint8_t> bin = read_binary(bin_path.string());
// OpenCL expects an array of *unsigned char* pointers, one per
// device. We are using only one device here.
std::vector<const unsigned char*> binaries(1, bin.data());
std::vector<size_t> lengths(1, bin.size());
std::vector<cl::Program> progs(1);
cl_int err = ctx.createProgramWithBinary(devices,
lengths,
binaries.data(),
nullptr,
&progs[0]);
if (err != CL_SUCCESS)
throw std::runtime_error("clCreateProgramWithBinary failed");
program = progs[0];
}
else
{
// 4) Compile from source, extract the binary, save it
std::cout << "Compiling source (first run)...\n";
std::string src = read_file("example.cl");
cl::Program::Sources srcs(1, { src.c_str(), src.length() + 1 });
program = cl::Program(ctx, srcs);
cl_int err = program.build(devices);
if (err != CL_SUCCESS)
{
std::string log = program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device);
throw std::runtime_error("Program build failed: " + log);
}
// Retrieve the binary
std::vector<unsigned char> bin;
std::vector<size_t> lengths;
program.getInfo(CL_PROGRAM_BINARY_SIZES, &lengths);
program.getInfo(CL_PROGRAM_BINARIES, &bin);
// Save it for next run
write_binary(bin_path.string(), std::vector<uint8_t>(bin.begin(), bin.end()));
std::cout << "Saved binary to " << bin_path << '\n';
}
// -------------------------------------------------------------------
// 5) Demo: add two float vectors
// -------------------------------------------------------------------
size_t N = 1024;
std::vector<float> a(N, 1.0f), b(N, 2.0f), c(N, 0.0f);
// Allocate device buffers
cl::Buffer bufA(ctx, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
sizeof(float) * N, a.data());
cl::Buffer bufB(ctx, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
sizeof(float) * N, b.data());
cl::Buffer bufC(ctx, CL_MEM_WRITE_ONLY,
sizeof(float) * N);
// Create kernel
cl::Kernel kernel(program, "add_vectors");
// Set arguments
kernel.setArg(0, bufA);
kernel.setArg(1, bufB);
kernel.setArg(2, bufC);
// Launch
cl::CommandQueue queue(ctx, device);
queue.enqueueNDRangeKernel(kernel, cl::NullRange,
cl::NDRange(N), cl::NullRange);
queue.finish();
// Read back
queue.enqueueReadBuffer(bufC, CL_TRUE, 0,
sizeof(float) * N, c.data());
// Verify a few elements
bool ok = true;
for (size_t i = 0; i < N; ++i)
{
if (c[i] != a[i] + b[i]) { ok = false; break; }
}
std::cout << "Demo " << (ok ? "succeeded" : "failed") << '\n';
}
catch (const std::exception &e)
{
std::cerr << "ERROR: " << e.what() << '\n';
return 1;
}
return 0;
}