💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

339 lines
9.4 KiB

  1. /*
  2. * Copyright (C) 2021 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper source code.
  5. *
  6. * Antkeeper source code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper source code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "gl/shader-program.hpp"
  20. #include "gl/shader-object.hpp"
  21. #include "gl/shader-variable-type.hpp"
  22. #include "gl/shader-input.hpp"
  23. #include <glad/glad.h>
  24. #include <stdexcept>
  25. namespace gl {
  26. shader_program::shader_program():
  27. gl_program_id(0),
  28. linked(false)
  29. {
  30. // Create an OpenGL shader program
  31. gl_program_id = glCreateProgram();
  32. // Handle OpenGL errors
  33. if (!gl_program_id)
  34. {
  35. throw std::runtime_error("An error occurred while creating an OpenGL shader program.");
  36. }
  37. }
  38. shader_program::~shader_program()
  39. {
  40. // Delete shader inputs
  41. free_inputs();
  42. // Detach all shader objects
  43. detach_all();
  44. // Delete the OpenGL shader program
  45. glDeleteProgram(gl_program_id);
  46. }
  47. void shader_program::attach(const shader_object* object)
  48. {
  49. if (attached_objects.find(object) != attached_objects.end())
  50. {
  51. throw std::runtime_error("Shader object is already attached to the shader program.");
  52. }
  53. else
  54. {
  55. // Check that both the OpenGL shader program and OpenGL shader object are valid
  56. if (glIsProgram(gl_program_id) != GL_TRUE)
  57. throw std::runtime_error("OpenGL shader program is not a valid program object.");
  58. if (glIsShader(object->gl_shader_id) != GL_TRUE)
  59. throw std::runtime_error("OpenGL shader object is not a valid shader object.");
  60. // Attach the OpenGL shader object to the OpenGL shader program
  61. glAttachShader(gl_program_id, object->gl_shader_id);
  62. // Handle OpenGL errors
  63. switch (glGetError())
  64. {
  65. case GL_INVALID_OPERATION:
  66. throw std::runtime_error("OpenGL shader object is already attached to the shader program.");
  67. break;
  68. }
  69. // Add shader object to set of attached objects
  70. attached_objects.insert(object);
  71. }
  72. }
  73. void shader_program::detach(const shader_object* object)
  74. {
  75. if (attached_objects.find(object) == attached_objects.end())
  76. {
  77. throw std::runtime_error("Shader object is not attached to the shader program.");
  78. }
  79. else
  80. {
  81. // Check that both the OpenGL shader program and OpenGL shader object are valid
  82. if (glIsProgram(gl_program_id) != GL_TRUE)
  83. throw std::runtime_error("OpenGL shader program is not a valid program object.");
  84. if (glIsShader(object->gl_shader_id) != GL_TRUE)
  85. throw std::runtime_error("OpenGL shader object is not a valid shader object.");
  86. // Detach the OpenGL shader object from the OpenGL shader program
  87. glDetachShader(gl_program_id, object->gl_shader_id);
  88. // Handle OpenGL errors
  89. switch (glGetError())
  90. {
  91. case GL_INVALID_OPERATION:
  92. throw std::runtime_error("OpenGL shader object is not attached to the shader program.");
  93. break;
  94. }
  95. // Remove shader object from set of attached objects
  96. attached_objects.erase(object);
  97. }
  98. }
  99. void shader_program::detach_all()
  100. {
  101. while (!attached_objects.empty())
  102. detach(*attached_objects.begin());
  103. }
  104. bool shader_program::link()
  105. {
  106. // Check that the OpenGL shader program is valid
  107. if (glIsProgram(gl_program_id) != GL_TRUE)
  108. throw std::runtime_error("OpenGL shader program is not a valid program object.");
  109. // Link OpenGL shader program
  110. glLinkProgram(gl_program_id);
  111. // Handle OpenGL errors
  112. switch (glGetError())
  113. {
  114. case GL_INVALID_OPERATION:
  115. throw std::runtime_error("OpenGL shader program is the currently active program object and transform feedback mode is active.");
  116. break;
  117. }
  118. // Get OpenGL shader program linking status
  119. GLint gl_link_status;
  120. glGetProgramiv(gl_program_id, GL_LINK_STATUS, &gl_link_status);
  121. linked = (gl_link_status == GL_TRUE);
  122. // Get OpenGL shader program info log length
  123. GLint gl_info_log_length;
  124. glGetProgramiv(gl_program_id, GL_INFO_LOG_LENGTH, &gl_info_log_length);
  125. if (gl_info_log_length > 0)
  126. {
  127. // Resize string to accommodate OpenGL shader program info log
  128. info_log.resize(gl_info_log_length);
  129. // Read OpenGL shader program info log into string
  130. glGetProgramInfoLog(gl_program_id, gl_info_log_length, &gl_info_log_length, info_log.data());
  131. // Remove redundant null terminator from string
  132. info_log.pop_back();
  133. }
  134. else
  135. {
  136. // Empty info log
  137. info_log.clear();
  138. }
  139. // Find shader inputs
  140. find_inputs();
  141. return linked;
  142. }
  143. void shader_program::find_inputs()
  144. {
  145. // Get maximum uniform name length
  146. GLint max_uniform_name_length = 0;
  147. glGetProgramiv(gl_program_id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_name_length);
  148. // Allocate uniform name buffer
  149. GLchar* uniform_name = new GLchar[max_uniform_name_length];
  150. // Get number of active uniforms in the shader
  151. GLint active_uniform_count = 0;
  152. glGetProgramiv(gl_program_id, GL_ACTIVE_UNIFORMS, &active_uniform_count);
  153. // Set first available texture unit
  154. int available_texture_unit = 0;
  155. // For each active uniform
  156. for (GLuint uniform_index = 0; uniform_index < static_cast<GLuint>(active_uniform_count); ++uniform_index)
  157. {
  158. // Get information about uniform
  159. GLsizei uniform_name_length;
  160. GLint uniform_size;
  161. GLenum uniform_type;
  162. glGetActiveUniform(gl_program_id, uniform_index, static_cast<GLsizei>(max_uniform_name_length), &uniform_name_length, &uniform_size, &uniform_type, &uniform_name[0]);
  163. // Get name without array symbols
  164. std::string input_name = uniform_name;
  165. std::size_t bracketPos = input_name.find_first_of("[");
  166. if (bracketPos != std::string::npos)
  167. {
  168. input_name = input_name.substr(0, bracketPos);
  169. }
  170. // Determine corresponding shader variable data type
  171. shader_variable_type variable_type;
  172. int texture_unit = -1;
  173. bool unsupported = false;
  174. switch (uniform_type)
  175. {
  176. case GL_BOOL:
  177. variable_type = shader_variable_type::bool1;
  178. break;
  179. case GL_BOOL_VEC2:
  180. variable_type = shader_variable_type::bool2;
  181. break;
  182. case GL_BOOL_VEC3:
  183. variable_type = shader_variable_type::bool3;
  184. break;
  185. case GL_BOOL_VEC4:
  186. variable_type = shader_variable_type::bool4;
  187. break;
  188. case GL_INT:
  189. variable_type = shader_variable_type::int1;
  190. break;
  191. case GL_INT_VEC2:
  192. variable_type = shader_variable_type::int2;
  193. break;
  194. case GL_INT_VEC3:
  195. variable_type = shader_variable_type::int3;
  196. break;
  197. case GL_INT_VEC4:
  198. variable_type = shader_variable_type::int4;
  199. break;
  200. case GL_UNSIGNED_INT:
  201. variable_type = shader_variable_type::uint1;
  202. break;
  203. case GL_UNSIGNED_INT_VEC2:
  204. variable_type = shader_variable_type::uint2;
  205. break;
  206. case GL_UNSIGNED_INT_VEC3:
  207. variable_type = shader_variable_type::uint3;
  208. break;
  209. case GL_UNSIGNED_INT_VEC4:
  210. variable_type = shader_variable_type::uint4;
  211. break;
  212. case GL_FLOAT:
  213. variable_type = shader_variable_type::float1;
  214. break;
  215. case GL_FLOAT_VEC2:
  216. variable_type = shader_variable_type::float2;
  217. break;
  218. case GL_FLOAT_VEC3:
  219. variable_type = shader_variable_type::float3;
  220. break;
  221. case GL_FLOAT_VEC4:
  222. variable_type = shader_variable_type::float4;
  223. break;
  224. case GL_FLOAT_MAT2:
  225. variable_type = shader_variable_type::float2x2;
  226. break;
  227. case GL_FLOAT_MAT3:
  228. variable_type = shader_variable_type::float3x3;
  229. break;
  230. case GL_FLOAT_MAT4:
  231. variable_type = shader_variable_type::float4x4;
  232. break;
  233. case GL_SAMPLER_1D:
  234. case GL_SAMPLER_1D_SHADOW:
  235. variable_type = shader_variable_type::texture_1d;
  236. texture_unit = available_texture_unit;
  237. available_texture_unit += uniform_size;
  238. break;
  239. case GL_SAMPLER_2D:
  240. case GL_SAMPLER_2D_SHADOW:
  241. variable_type = shader_variable_type::texture_2d;
  242. texture_unit = available_texture_unit;
  243. available_texture_unit += uniform_size;
  244. break;
  245. case GL_SAMPLER_3D:
  246. variable_type = shader_variable_type::texture_3d;
  247. texture_unit = available_texture_unit;
  248. available_texture_unit += uniform_size;
  249. break;
  250. case GL_SAMPLER_CUBE:
  251. variable_type = shader_variable_type::texture_cube;
  252. texture_unit = available_texture_unit;
  253. available_texture_unit += uniform_size;
  254. break;
  255. default:
  256. unsupported = true;
  257. break;
  258. }
  259. // Check if data type is supported
  260. if (unsupported)
  261. {
  262. std::string message = std::string("Shader uniform \"") + std::string(uniform_name) + std::string("\" has unsupported data type.");
  263. throw std::runtime_error(message.c_str());
  264. }
  265. // Get uniform location
  266. GLint uniform_location = glGetUniformLocation(gl_program_id, uniform_name);
  267. if (uniform_location == -1)
  268. {
  269. //std::string message = std::string("Unable to get location for uniform \"") + std::string(uniform_name) + std::string("\"");
  270. //throw std::runtime_error(message.c_str());
  271. }
  272. else
  273. {
  274. // Create new shader input
  275. shader_input* input = new shader_input(this, inputs.size(), uniform_location, input_name, variable_type, uniform_size, texture_unit);
  276. input_map[input_name] = input;
  277. inputs.push_back(input);
  278. }
  279. }
  280. // Free uniform name buffer
  281. delete[] uniform_name;
  282. }
  283. void shader_program::free_inputs()
  284. {
  285. for (shader_input* input: inputs)
  286. {
  287. delete input;
  288. }
  289. inputs.clear();
  290. }
  291. } // namespace gl