389 lines
11 KiB
C
389 lines
11 KiB
C
#include <stdio.h>
|
|
#include <gtk/gtk.h>
|
|
#include <math.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <CL/cl.h>
|
|
#include <epoxy/gl.h>
|
|
#include <epoxy/glx.h>
|
|
#include <time.h>
|
|
#include <cglm/call.h>
|
|
|
|
static const char * const vertex_shader = "#version 330 core\n \
|
|
layout (location = 0) in vec3 aPos;\n \
|
|
layout (location = 1) in vec2 aTexCoord;\n\
|
|
out vec2 texCoord;\n\
|
|
out vec3 vertPos;\n \
|
|
uniform float zoom;\n\
|
|
void main()\n \
|
|
{\n \
|
|
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n \
|
|
texCoord = aTexCoord / zoom;\n\
|
|
vertPos = aPos;\n\
|
|
}\n";
|
|
|
|
static const char * const orange_framgment_shader = " \
|
|
#version 330 core\n \
|
|
out vec4 FragColor;\n \
|
|
in vec3 vertPos;\n \
|
|
in vec2 texCoord;\n \
|
|
uniform sampler2D ourTexture;\n \
|
|
\n \
|
|
void main()\n \
|
|
{\n \
|
|
FragColor = texture(ourTexture, texCoord);\n\
|
|
}";
|
|
|
|
struct canvas_buffer {
|
|
unsigned char *mandelbrot_buffer;
|
|
unsigned int iterations;
|
|
unsigned int height;
|
|
unsigned int width;
|
|
double center_x;
|
|
double center_y;
|
|
double x_span;
|
|
double y_span;
|
|
};
|
|
|
|
static gboolean on_main_window_close(GtkWidget *window, gpointer data)
|
|
{
|
|
gtk_main_quit();
|
|
return FALSE;
|
|
}
|
|
|
|
void check_error(cl_int error)
|
|
{
|
|
if (error != CL_SUCCESS) {
|
|
printf("OpenCL call failed with error %d\n", (int)error);
|
|
exit(error);
|
|
}
|
|
}
|
|
|
|
void load_kernel_from_file(char *file, char **src)
|
|
{
|
|
long fsize;
|
|
FILE *f = fopen(file, "rb");
|
|
|
|
fseek(f, 0, SEEK_END);
|
|
fsize = ftell(f);
|
|
fseek(f, 0, SEEK_SET); //same as rewind(f);
|
|
*src = (char *)malloc(fsize + 1);
|
|
fread(*src, fsize, 1, f);
|
|
fclose(f);
|
|
(*src)[fsize] = 0;
|
|
}
|
|
|
|
cl_program create_program(char *src, cl_context context)
|
|
{
|
|
cl_program program;
|
|
size_t lengths[1];
|
|
cl_int error = 0;
|
|
|
|
lengths[0] = strlen(src);
|
|
printf("strlen: %u\n", (unsigned int)lengths[0]);
|
|
program = clCreateProgramWithSource(context, 1, (const char **)&src,
|
|
lengths, &error);
|
|
check_error(error);
|
|
|
|
return program;
|
|
}
|
|
|
|
static int calculate_mandelbrot(struct canvas_buffer *buff)
|
|
{
|
|
|
|
int i;
|
|
cl_int error;
|
|
cl_int ret;
|
|
cl_platform_id *platform_ids = NULL;
|
|
cl_device_id *device_ids = NULL;
|
|
cl_uint platform_id_count = 0;
|
|
cl_uint device_id_count = 0;
|
|
cl_context context;
|
|
cl_program program;
|
|
cl_kernel kernel;
|
|
cl_command_queue queue;
|
|
cl_mem dat_buff;
|
|
char *source_code = NULL;
|
|
char temp_buff[100];
|
|
size_t data_size;
|
|
size_t work_size = 64;
|
|
|
|
|
|
data_size = buff->width*buff->height;
|
|
|
|
if (!buff->mandelbrot_buffer)
|
|
buff->mandelbrot_buffer = (unsigned char *)malloc(data_size*3);
|
|
|
|
if (!buff->mandelbrot_buffer)
|
|
return -ENOMEM;
|
|
|
|
|
|
clGetPlatformIDs(0, NULL, &platform_id_count);
|
|
if (platform_id_count == 0)
|
|
return -ENOMEM;
|
|
|
|
platform_ids = (cl_platform_id *)malloc(platform_id_count *
|
|
sizeof(cl_platform_id));
|
|
if (platform_ids == NULL)
|
|
return -ENOMEM;
|
|
|
|
clGetPlatformIDs(platform_id_count, platform_ids, NULL);
|
|
|
|
printf("Platforms available: %u\n", (unsigned int)platform_id_count);
|
|
|
|
for (i = 0; i < platform_id_count; i++) {
|
|
temp_buff[0] = '\0';
|
|
clGetPlatformInfo(platform_ids[i], CL_PLATFORM_NAME, sizeof(temp_buff), temp_buff, NULL);
|
|
printf("Platform ID %d: %s\n", i, temp_buff);
|
|
}
|
|
|
|
clGetDeviceIDs(platform_ids[0], CL_DEVICE_TYPE_ALL, 0, NULL,
|
|
&device_id_count);
|
|
printf("Device in Platform 0 count: %u\n",
|
|
(unsigned int)device_id_count);
|
|
|
|
if (device_id_count == 0)
|
|
return -1;
|
|
|
|
device_ids = (cl_device_id *) malloc(sizeof(cl_device_id)
|
|
*device_id_count);
|
|
|
|
clGetDeviceIDs(platform_ids[0], CL_DEVICE_TYPE_ALL, device_id_count,
|
|
device_ids, NULL);
|
|
|
|
for (i = 0; i < device_id_count; i++) {
|
|
temp_buff[0] = '\0';
|
|
clGetDeviceInfo(device_ids[i], CL_DEVICE_NAME, sizeof(temp_buff), temp_buff, NULL);
|
|
printf("Device ID %d: %s\n", i, temp_buff);
|
|
}
|
|
|
|
|
|
context = clCreateContext(NULL, 1, &device_ids[0], NULL, NULL, &error);
|
|
check_error(error);
|
|
|
|
queue = clCreateCommandQueue(context, device_ids[0], 0, &error);
|
|
|
|
dat_buff = clCreateBuffer(context, CL_MEM_WRITE_ONLY |
|
|
CL_MEM_COPY_HOST_PTR,
|
|
3*data_size, buff->mandelbrot_buffer, &error);
|
|
check_error(error);
|
|
|
|
load_kernel_from_file("cl_kernels/mandelbrot.cl", &source_code);
|
|
program = create_program(source_code, context);
|
|
|
|
/* Compile the source code */
|
|
check_error(clBuildProgram(program, device_id_count,
|
|
device_ids, NULL, NULL, NULL));
|
|
kernel = clCreateKernel(program, "mandelbrot", &error);
|
|
|
|
|
|
/* Setting the arguments */
|
|
clSetKernelArg(kernel, 0, sizeof(cl_mem), (void *)&dat_buff);
|
|
clSetKernelArg(kernel, 1, sizeof(unsigned int), (void *)&buff->iterations);
|
|
clSetKernelArg(kernel, 2, sizeof(double), (void *)&buff->center_x);
|
|
clSetKernelArg(kernel, 3, sizeof(double), (void *)&buff->center_y);
|
|
clSetKernelArg(kernel, 4, sizeof(double), (void *)&buff->x_span);
|
|
clSetKernelArg(kernel, 5, sizeof(double), (void *)&buff->y_span);
|
|
clSetKernelArg(kernel, 6, sizeof(unsigned int), (void *)&buff->width);
|
|
clSetKernelArg(kernel, 7, sizeof(unsigned int), (void *)&buff->height);
|
|
|
|
ret = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, &data_size, &work_size, 0, NULL, NULL);
|
|
check_error(ret);
|
|
|
|
clEnqueueReadBuffer(queue, dat_buff, CL_TRUE, 0, data_size*3, buff->mandelbrot_buffer, 0, NULL, NULL);
|
|
|
|
ret = clFlush(queue);
|
|
ret = clFinish(queue);
|
|
ret = clReleaseKernel(kernel);
|
|
ret = clReleaseProgram(program);
|
|
ret = clReleaseMemObject(dat_buff);
|
|
ret = clReleaseCommandQueue(queue);
|
|
ret = clReleaseContext(context);
|
|
free(platform_ids);
|
|
free(device_ids);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
float vertices[] = {
|
|
// Vertex Texture
|
|
-0.95f, -0.95f, 0.0f, 0.0f, 0.0f, // bottom left
|
|
-0.95f, 0.95f, 0.0f, 0.0f, 1.0f, // top left
|
|
0.95f, -0.95f, 0.0f, 1.0f, 0.0f, // bottom right
|
|
0.95f, 0.95f, 0.0f, 1.0f, 1.0f, // top right
|
|
};
|
|
|
|
unsigned int indices[] = {
|
|
0, 1, 2,
|
|
2, 1, 3
|
|
};
|
|
|
|
static unsigned int vertex_buffer_object;
|
|
static unsigned int element_buffer_object;
|
|
static unsigned int vertex_array_object;
|
|
static unsigned int shader_program_id;
|
|
static unsigned int texture_id;
|
|
|
|
gboolean render(GtkGLArea *gl_area, GdkGLContext *context, gpointer user_data)
|
|
{
|
|
int zoom_loc;
|
|
static float zoom = 1.0f;
|
|
|
|
gtk_gl_area_make_current(gl_area);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
glUseProgram(shader_program_id);
|
|
zoom_loc = glGetUniformLocation(shader_program_id, "zoom");
|
|
glUniform1f(zoom_loc, zoom);
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
glBindTexture(GL_TEXTURE_2D, texture_id);
|
|
glBindVertexArray(vertex_array_object);
|
|
//glDrawArrays(GL_TRIANGLES, 0, 3);
|
|
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
|
|
|
glFlush();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void realize(GtkGLArea *gl_area, gpointer user_data)
|
|
{
|
|
|
|
unsigned int vertex_shader_id;
|
|
unsigned int fragment_shader_id;
|
|
struct canvas_buffer *mandelbrot_buff = (struct canvas_buffer *)user_data;
|
|
|
|
int success;
|
|
char info_log[512];
|
|
|
|
gtk_gl_area_make_current(gl_area);
|
|
glClearColor(0.2, 0.2, 0.2, 1);
|
|
|
|
glGenVertexArrays(1, &vertex_array_object);
|
|
glBindVertexArray(vertex_array_object);
|
|
|
|
glGenBuffers(1, &vertex_buffer_object);
|
|
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object);
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
|
|
|
|
glGenBuffers(1, &element_buffer_object);
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer_object);
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
|
|
|
|
vertex_shader_id = glCreateShader(GL_VERTEX_SHADER);
|
|
glShaderSource(vertex_shader_id, 1, &vertex_shader, NULL);
|
|
glCompileShader(vertex_shader_id);
|
|
glGetShaderiv(vertex_shader_id, GL_COMPILE_STATUS, &success);
|
|
|
|
if (!success) {
|
|
glGetShaderInfoLog(vertex_shader_id, 512, NULL, info_log);
|
|
printf("%s\n", info_log);
|
|
} else {
|
|
printf("Vertex shader compilation successful\n");
|
|
}
|
|
|
|
fragment_shader_id = glCreateShader(GL_FRAGMENT_SHADER);
|
|
glShaderSource(fragment_shader_id, 1, &orange_framgment_shader, NULL);
|
|
glCompileShader(fragment_shader_id);
|
|
glGetShaderiv(fragment_shader_id, GL_COMPILE_STATUS, &success);
|
|
|
|
if (!success) {
|
|
glGetShaderInfoLog(fragment_shader_id, 512, NULL, info_log);
|
|
printf("%s\n", info_log);
|
|
} else {
|
|
printf("Fragment shader compilation successful\n");
|
|
}
|
|
|
|
shader_program_id = glCreateProgram();
|
|
glAttachShader(shader_program_id, vertex_shader_id);
|
|
glAttachShader(shader_program_id, fragment_shader_id);
|
|
glLinkProgram(shader_program_id);
|
|
|
|
glGetShaderiv(shader_program_id, GL_LINK_STATUS, &success);
|
|
if (!success) {
|
|
glGetShaderInfoLog(shader_program_id, 512, NULL, info_log);
|
|
printf("%s\n", info_log);
|
|
} else {
|
|
printf("Shader Core linked successfully\n");
|
|
}
|
|
|
|
glUseProgram(shader_program_id);
|
|
glDeleteShader(vertex_shader_id);
|
|
glDeleteShader(fragment_shader_id);
|
|
|
|
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
|
|
glEnableVertexAttribArray(0);
|
|
|
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
|
|
glEnableVertexAttribArray(1);
|
|
|
|
glGenTextures(1, &texture_id);
|
|
glBindTexture(GL_TEXTURE_2D, texture_id);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, mandelbrot_buff->width, mandelbrot_buff->height,
|
|
0, GL_RGB, GL_UNSIGNED_BYTE, mandelbrot_buff->mandelbrot_buffer);
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
|
|
|
|
|
|
|
//glClear(GL_COLOR_BUFFER_BIT);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
GtkGLArea *gl_area;
|
|
GtkWindow *window;
|
|
GtkBuilder *builder;
|
|
static struct canvas_buffer mandelbrot_buff;
|
|
clock_t start, end;
|
|
|
|
gtk_init(&argc, &argv);
|
|
|
|
builder = gtk_builder_new_from_resource("/main.glade");
|
|
|
|
window = GTK_WINDOW(gtk_builder_get_object(builder, "main-window"));
|
|
g_signal_connect(window, "delete-event", G_CALLBACK(on_main_window_close), NULL);
|
|
|
|
|
|
mandelbrot_buff.x_span = 0.1;//2.8;
|
|
mandelbrot_buff.y_span = 0.1;//2.25;
|
|
mandelbrot_buff.center_x = -0.6;
|
|
mandelbrot_buff.center_y = 0.44;
|
|
mandelbrot_buff.width = 4000;
|
|
mandelbrot_buff.height = 4000;
|
|
mandelbrot_buff.iterations = 1000;
|
|
mandelbrot_buff.mandelbrot_buffer = NULL;
|
|
|
|
printf("Compile and run Mandelbrot on OpenCL HW\n");
|
|
start = clock();
|
|
calculate_mandelbrot(&mandelbrot_buff);
|
|
end = clock();
|
|
printf("Calculation finished. Time needed: %lf ms\n", ((double)(end - start)) / CLOCKS_PER_SEC * 1000.0);
|
|
|
|
|
|
gl_area = GTK_GL_AREA(gtk_gl_area_new());
|
|
gtk_gl_area_set_auto_render(gl_area, TRUE);
|
|
gtk_widget_set_vexpand(GTK_WIDGET(gl_area), TRUE);
|
|
gtk_widget_set_hexpand(GTK_WIDGET(gl_area), TRUE);
|
|
g_signal_connect(gl_area, "realize", G_CALLBACK(realize), &mandelbrot_buff);
|
|
g_signal_connect(gl_area, "render", G_CALLBACK(render), NULL);
|
|
|
|
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(gl_area));
|
|
gtk_widget_show(GTK_WIDGET(gl_area));
|
|
|
|
g_object_unref(builder);
|
|
|
|
gtk_gl_area_make_current(gl_area);
|
|
|
|
printf("Gui will be displayed\n");
|
|
gtk_main();
|
|
|
|
return 0;
|
|
}
|