27255c22af6d874a439789908e2e841dcc5bb18f
[platform/core/uifw/dali-demo.git] / examples / direct-rendering / native-renderer.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include "native-renderer.h"
19 #include <iostream>
20
21 /**
22  * Set of math helper functions
23  */
24 namespace
25 {
26 [[maybe_unused]] std::vector<std::string> split(const std::string& s, char seperator)
27 {
28   std::vector<std::string> output;
29   std::string::size_type   prev_pos = 0, pos = 0;
30   while((pos = s.find(seperator, pos)) != std::string::npos)
31   {
32     std::string substring(s.substr(prev_pos, pos - prev_pos));
33     output.push_back(substring);
34     prev_pos = ++pos;
35   }
36   output.push_back(s.substr(prev_pos, pos - prev_pos)); // Last word
37   return output;
38 }
39
40 int GetEnvInt(const char* name, int def)
41 {
42   auto v = getenv(name);
43   return v ? atoi(v) : def;
44 }
45
46 [[maybe_unused]] std::string GetEnvString(const char* name, std::string def = "")
47 {
48   auto v = getenv(name);
49   return v ? std::string(v) : def;
50 }
51
52 const uint32_t MAX_CUBES = GetEnvInt("MAX_CUBES", 200);
53
54 #define GL(x)                                                   \
55   {                                                             \
56     glGetError();                                               \
57     {                                                           \
58       x;                                                        \
59     };                                                          \
60     auto err = glGetError();                                    \
61     if(err)                                                     \
62     {                                                           \
63       printf("%p:%d: ERROR: 0x%X\n", this, __LINE__, int(err)); \
64     }                                                           \
65   }
66
67 constexpr GLfloat CUBE_VERTICES[] = {-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f};
68
69 constexpr GLfloat CUBE_COLOURS[] = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
70
71 constexpr GLushort CUBE_INDICES[] = {0, 2, 3, 0, 1, 3, 4, 6, 7, 4, 5, 7, 8, 9, 10, 11, 8, 10, 12, 13, 14, 15, 12, 14, 16, 17, 18, 16, 19, 18, 20, 21, 22, 20, 23, 22};
72
73 constexpr float QUAD_VERTS[] = {
74   // positions          // colors           // texture coords
75   1.0f,
76   1.0f,
77   1.0f,
78   -1.0f,
79   -1.0f,
80   -1.0f,
81   -1.0f,
82   1.0f,
83 };
84
85 constexpr unsigned short QUAD_INDICES[] = {
86   0, 1, 2, 3, 0, 2};
87
88 constexpr float QUAD_UV[] = {
89   // positions          // colors           // texture coords
90   1.0f,
91   1.0f, // top right
92   1.0f,
93   0.0f, // bottom right
94   0.0f,
95   0.0f, // bottom left
96   0.0f,
97   1.0f // top left
98 };
99
100 float matrixDegreesToRadians(float degrees)
101 {
102   return M_PI * degrees / 180.0f;
103 }
104
105 [[maybe_unused]] void matrixIdentityFunction(float* matrix)
106 {
107   if(matrix == NULL)
108   {
109     return;
110   }
111   matrix[0]  = 1.0f;
112   matrix[1]  = 0.0f;
113   matrix[2]  = 0.0f;
114   matrix[3]  = 0.0f;
115   matrix[4]  = 0.0f;
116   matrix[5]  = 1.0f;
117   matrix[6]  = 0.0f;
118   matrix[7]  = 0.0f;
119   matrix[8]  = 0.0f;
120   matrix[9]  = 0.0f;
121   matrix[10] = 1.0f;
122   matrix[11] = 0.0f;
123   matrix[12] = 0.0f;
124   matrix[13] = 0.0f;
125   matrix[14] = 0.0f;
126   matrix[15] = 1.0f;
127 }
128
129 [[maybe_unused]] void matrixMultiply(float* destination, float* operand1, float* operand2)
130 {
131   float theResult[16];
132   int   i, j = 0;
133   for(i = 0; i < 4; i++)
134   {
135     for(j = 0; j < 4; j++)
136     {
137       theResult[4 * i + j] = operand1[j] * operand2[4 * i] + operand1[4 + j] * operand2[4 * i + 1] +
138                              operand1[8 + j] * operand2[4 * i + 2] + operand1[12 + j] * operand2[4 * i + 3];
139     }
140   }
141   for(i = 0; i < 16; i++)
142   {
143     destination[i] = theResult[i];
144   }
145 }
146
147 [[maybe_unused]] void matrixTranslate(float* matrix, float x, float y, float z)
148 {
149   float temporaryMatrix[16];
150   matrixIdentityFunction(temporaryMatrix);
151   temporaryMatrix[12] = x;
152   temporaryMatrix[13] = y;
153   temporaryMatrix[14] = z;
154   matrixMultiply(matrix, temporaryMatrix, matrix);
155 }
156
157 [[maybe_unused]] void matrixScale(float* matrix, float x, float y, float z)
158 {
159   float tempMatrix[16];
160   matrixIdentityFunction(tempMatrix);
161   tempMatrix[0]  = x;
162   tempMatrix[5]  = y;
163   tempMatrix[10] = z;
164   matrixMultiply(matrix, tempMatrix, matrix);
165 }
166
167 [[maybe_unused]] void matrixRotateX(float* matrix, float angle)
168 {
169   float tempMatrix[16];
170   matrixIdentityFunction(tempMatrix);
171   tempMatrix[5]  = cos(matrixDegreesToRadians(angle));
172   tempMatrix[9]  = -sin(matrixDegreesToRadians(angle));
173   tempMatrix[6]  = sin(matrixDegreesToRadians(angle));
174   tempMatrix[10] = cos(matrixDegreesToRadians(angle));
175   matrixMultiply(matrix, tempMatrix, matrix);
176 }
177 [[maybe_unused]] void matrixRotateY(float* matrix, float angle)
178 {
179   float tempMatrix[16];
180   matrixIdentityFunction(tempMatrix);
181   tempMatrix[0]  = cos(matrixDegreesToRadians(angle));
182   tempMatrix[8]  = sin(matrixDegreesToRadians(angle));
183   tempMatrix[2]  = -sin(matrixDegreesToRadians(angle));
184   tempMatrix[10] = cos(matrixDegreesToRadians(angle));
185   matrixMultiply(matrix, tempMatrix, matrix);
186 }
187 [[maybe_unused]] void matrixRotateZ(float* matrix, float angle)
188 {
189   float tempMatrix[16];
190   matrixIdentityFunction(tempMatrix);
191   tempMatrix[0] = cos(matrixDegreesToRadians(angle));
192   tempMatrix[4] = -sin(matrixDegreesToRadians(angle));
193   tempMatrix[1] = sin(matrixDegreesToRadians(angle));
194   tempMatrix[5] = cos(matrixDegreesToRadians(angle));
195   matrixMultiply(matrix, tempMatrix, matrix);
196 }
197
198 void matrixFrustum(float* matrix, float left, float right, float bottom, float top, float zNear, float zFar)
199 {
200   float temp, xDistance, yDistance, zDistance;
201   temp      = 2.0 * zNear;
202   xDistance = right - left;
203   yDistance = top - bottom;
204   zDistance = zFar - zNear;
205   matrixIdentityFunction(matrix);
206   matrix[0]  = temp / xDistance;
207   matrix[5]  = temp / yDistance;
208   matrix[8]  = (right + left) / xDistance;
209   matrix[9]  = (top + bottom) / yDistance;
210   matrix[10] = (-zFar - zNear) / zDistance;
211   matrix[11] = -1.0f;
212   matrix[14] = (-temp * zFar) / zDistance;
213   matrix[15] = 0.0f;
214 }
215
216 [[maybe_unused]] void matrixPerspective(float* matrix, float fieldOfView, float aspectRatio, float zNear, float zFar)
217 {
218   float ymax, xmax;
219   ymax = zNear * tanf(fieldOfView * M_PI / 360.0);
220   xmax = ymax * aspectRatio;
221   matrixFrustum(matrix, -xmax, xmax, -ymax, ymax, zNear, zFar);
222 }
223
224 } // namespace
225
226 NativeRenderer::~NativeRenderer() = default;
227
228 NativeRenderer::NativeRenderer(const CreateInfo& info)
229 : mWidth(info.width),
230   mHeight(info.height),
231   mCreateInfo(info)
232 {
233 }
234
235 void NativeRenderer::PrepareShader()
236 {
237   static const char glVertexShader[] =
238     "attribute vec4 vertexPosition;\n"
239     "attribute vec3 vertexColour;\n"
240     "varying vec3 fragColour;\n"
241     "uniform mat4 projection;\n"
242     "uniform mat4 modelView;\n"
243     "void main()\n"
244     "{\n"
245     "    gl_Position = projection * modelView * vertexPosition;\n"
246     "    fragColour = vertexColour;\n"
247     "}\n";
248
249   static const char glFragmentShader[] =
250     "precision mediump float;\n"
251     "varying vec3 fragColour;\n"
252     "void main()\n"
253     "{\n"
254     "    gl_FragColor = vec4(fragColour, 1.0);\n"
255     "}\n";
256
257   mProgramId = CreateProgram(glVertexShader, glFragmentShader);
258 }
259
260 void NativeRenderer::Setup(int width, int height)
261 {
262   PrepareShader();
263
264   mVertexLocation       = glGetAttribLocation(mProgramId, "vertexPosition");
265   mVertexColourLocation = glGetAttribLocation(mProgramId, "vertexColour");
266   mProjectionLocation   = glGetUniformLocation(mProgramId, "projection");
267   mModelViewLocation    = glGetUniformLocation(mProgramId, "modelView");
268
269   GL(glEnable(GL_DEPTH_TEST));
270 }
271
272 GLuint NativeRenderer::CreateProgram(const char* vertexSource, const char* fragmentSource)
273 {
274   GLuint vertexShader = LoadShader(GL_VERTEX_SHADER, vertexSource);
275   if(!vertexShader)
276   {
277     return 0;
278   }
279   GLuint fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fragmentSource);
280   if(!fragmentShader)
281   {
282     return 0;
283   }
284   GLuint program = glCreateProgram();
285   if(program)
286   {
287     GL(glAttachShader(program, vertexShader));
288     GL(glAttachShader(program, fragmentShader));
289     GL(glLinkProgram(program));
290     GLint linkStatus = GL_FALSE;
291     GL(glGetProgramiv(program, GL_LINK_STATUS, &linkStatus));
292     if(linkStatus != GL_TRUE)
293     {
294       GLint bufLength = 0;
295       glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
296       if(bufLength)
297       {
298         char* buf = (char*)malloc(bufLength);
299         if(buf)
300         {
301           glGetProgramInfoLog(program, bufLength, NULL, buf);
302           free(buf);
303         }
304       }
305       glDeleteProgram(program);
306       program = 0;
307     }
308   }
309   return program;
310 }
311
312 GLuint NativeRenderer::LoadShader(GLenum shaderType, const char* shaderSource)
313 {
314   GLuint shader = glCreateShader(shaderType);
315   if(shader != 0)
316   {
317     GL(glShaderSource(shader, 1, &shaderSource, NULL));
318     GL(glCompileShader(shader));
319     GLint compiled = 0;
320     glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
321     if(compiled != GL_TRUE)
322     {
323       GLint infoLen = 0;
324       glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
325
326       if(infoLen > 0)
327       {
328         char* logBuffer = (char*)malloc(infoLen);
329
330         if(logBuffer != NULL)
331         {
332           glGetShaderInfoLog(shader, infoLen, NULL, logBuffer);
333           free(logBuffer);
334           logBuffer = NULL;
335         }
336
337         glDeleteShader(shader);
338         shader = 0;
339       }
340     }
341   }
342
343   return shader;
344 }
345
346 void NativeRenderer::RenderCube(const Dali::RenderCallbackInput& input)
347 {
348   uint32_t drawCount = 0;
349
350   float& angle = mAngle;
351
352   [[maybe_unused]] auto x = mCreateInfo.viewportX; // float(mWidth - input.size.width) * 0.5f;
353   [[maybe_unused]] auto y = mCreateInfo.viewportY; // float(mHeight - input.size.height) * 0.5f;
354   auto                  w = mCreateInfo.width;
355   auto                  h = mCreateInfo.height;
356
357   matrixPerspective(mProjectionMatrix, 45, (float)w / (float)h, 0.1f, 100);
358
359   GL(glViewport(x, y, w, h));
360   GL(glEnable(GL_DEPTH_TEST));
361   if(!mCreateInfo.offscreen)
362   {
363     GL(glEnable(GL_SCISSOR_TEST));
364     GL(glScissor(x, y, w, h));
365   }
366   else
367   {
368     GL(glDisable(GL_SCISSOR_TEST));
369   }
370   GL(glClearColor(mCreateInfo.clearColor[0],
371                   mCreateInfo.clearColor[1],
372                   mCreateInfo.clearColor[2],
373                   mCreateInfo.clearColor[3]));
374   {
375     GL(glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT));
376   }
377   GL(glUseProgram(mProgramId));
378   GL(glVertexAttribPointer(mVertexLocation, 3, GL_FLOAT, GL_FALSE, 0, CUBE_VERTICES));
379   GL(glEnableVertexAttribArray(mVertexLocation));
380   GL(glVertexAttribPointer(mVertexColourLocation, 3, GL_FLOAT, GL_FALSE, 0, CUBE_COLOURS));
381   GL(glEnableVertexAttribArray(mVertexColourLocation));
382
383   srand(10);
384
385   const auto maxCubes = int(MAX_CUBES);
386
387   for(int i = 0; i < int(maxCubes); ++i)
388   {
389     GL(matrixIdentityFunction(mModelViewMatrix));
390     matrixScale(mModelViewMatrix, 0.2f, 0.2f, 0.2f);
391     matrixRotateX(mModelViewMatrix, angle);
392     matrixRotateY(mModelViewMatrix, angle);
393     auto max = 7000;
394     if(mPosX.size() == uint32_t(i))
395     {
396       auto xPos = float((rand() % max) - (max / 2)) / 1000.0f;
397       auto yPos = float((rand() % max) - (max / 2)) / 1000.0f;
398       mPosX.emplace_back(xPos);
399       mPosY.emplace_back(yPos);
400     }
401     matrixTranslate(mModelViewMatrix, mPosX[i], mPosY[i], -5.0f);
402     GL(glUniformMatrix4fv(mProjectionLocation, 1, GL_FALSE, mProjectionMatrix));
403     GL(glUniformMatrix4fv(mModelViewLocation, 1, GL_FALSE, mModelViewMatrix));
404     GL(glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, CUBE_INDICES));
405     drawCount++;
406   }
407   angle += 1;
408   if(angle > 360)
409   {
410     angle -= 360;
411   }
412 }
413
414 void NativeRenderer::GlViewInitCallback(const Dali::RenderCallbackInput& input)
415 {
416   Setup(mWidth, mHeight);
417   mState = State::RENDER;
418 }
419
420 int NativeRenderer::GlViewRenderCallback(const Dali::RenderCallbackInput& input)
421 {
422   RenderCube(input);
423   return true;
424 }
425
426 void NativeRenderer::GlViewTerminateCallback(const Dali::RenderCallbackInput& input)
427 {
428   // Nothing to do here
429 }