1 //========================================================================
2 // Multi-threading test
3 // Copyright (c) Camilla Löwy <elmindreda@glfw.org>
4 //
5 // This software is provided 'as-is', without any express or implied
6 // warranty. In no event will the authors be held liable for any damages
7 // arising from the use of this software.
8 //
9 // Permission is granted to anyone to use this software for any purpose,
10 // including commercial applications, and to alter it and redistribute it
11 // freely, subject to the following restrictions:
12 //
13 // 1. The origin of this software must not be misrepresented; you must not
14 //    claim that you wrote the original software. If you use this software
15 //    in a product, an acknowledgment in the product documentation would
16 //    be appreciated but is not required.
17 //
18 // 2. Altered source versions must be plainly marked as such, and must not
19 //    be misrepresented as being the original software.
20 //
21 // 3. This notice may not be removed or altered from any source
22 //    distribution.
23 //
24 //========================================================================
25 //
26 // This test is intended to verify whether the OpenGL context part of
27 // the GLFW API is able to be used from multiple threads
28 //
29 //========================================================================
30 
31 #include "tinycthread.h"
32 
33 #define GLAD_GL_IMPLEMENTATION
34 #include <glad/gl.h>
35 #define GLFW_INCLUDE_NONE
36 #include <GLFW/glfw3.h>
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <math.h>
41 
42 typedef struct
43 {
44     GLFWwindow* window;
45     const char* title;
46     float r, g, b;
47     thrd_t id;
48 } Thread;
49 
50 static volatile int running = GLFW_TRUE;
51 
error_callback(int error, const char* description)52 static void error_callback(int error, const char* description)
53 {
54     fprintf(stderr, "Error: %s\n", description);
55 }
56 
key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)57 static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
58 {
59     if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
60         glfwSetWindowShouldClose(window, GLFW_TRUE);
61 }
62 
thread_main(void* data)63 static int thread_main(void* data)
64 {
65     const Thread* thread = data;
66 
67     glfwMakeContextCurrent(thread->window);
68     glfwSwapInterval(1);
69 
70     while (running)
71     {
72         const float v = (float) fabs(sin(glfwGetTime() * 2.f));
73         glClearColor(thread->r * v, thread->g * v, thread->b * v, 0.f);
74 
75         glClear(GL_COLOR_BUFFER_BIT);
76         glfwSwapBuffers(thread->window);
77     }
78 
79     glfwMakeContextCurrent(NULL);
80     return 0;
81 }
82 
main(void)83 int main(void)
84 {
85     int i, result;
86     Thread threads[] =
87     {
88         { NULL, "Red", 1.f, 0.f, 0.f, 0 },
89         { NULL, "Green", 0.f, 1.f, 0.f, 0 },
90         { NULL, "Blue", 0.f, 0.f, 1.f, 0 }
91     };
92     const int count = sizeof(threads) / sizeof(Thread);
93 
94     glfwSetErrorCallback(error_callback);
95 
96     if (!glfwInit())
97         exit(EXIT_FAILURE);
98 
99     for (i = 0;  i < count;  i++)
100     {
101         glfwWindowHint(GLFW_POSITION_X, 200 + 250 * i);
102         glfwWindowHint(GLFW_POSITION_Y, 200);
103 
104         threads[i].window = glfwCreateWindow(200, 200,
105                                              threads[i].title,
106                                              NULL, NULL);
107         if (!threads[i].window)
108         {
109             glfwTerminate();
110             exit(EXIT_FAILURE);
111         }
112 
113         glfwSetKeyCallback(threads[i].window, key_callback);
114     }
115 
116     glfwMakeContextCurrent(threads[0].window);
117     gladLoadGL(glfwGetProcAddress);
118     glfwMakeContextCurrent(NULL);
119 
120     for (i = 0;  i < count;  i++)
121     {
122         if (thrd_create(&threads[i].id, thread_main, threads + i) !=
123             thrd_success)
124         {
125             fprintf(stderr, "Failed to create secondary thread\n");
126 
127             glfwTerminate();
128             exit(EXIT_FAILURE);
129         }
130     }
131 
132     while (running)
133     {
134         glfwWaitEvents();
135 
136         for (i = 0;  i < count;  i++)
137         {
138             if (glfwWindowShouldClose(threads[i].window))
139                 running = GLFW_FALSE;
140         }
141     }
142 
143     for (i = 0;  i < count;  i++)
144         glfwHideWindow(threads[i].window);
145 
146     for (i = 0;  i < count;  i++)
147         thrd_join(threads[i].id, &result);
148 
149     exit(EXIT_SUCCESS);
150 }
151 
152