Line data Source code
1 : // Copyright (c) 2025 Milton McDonald
2 : // This source code is licensed under the MIT License. See LICENSE file in the
3 : // project root for details.
4 :
5 : #include "shader.h"
6 :
7 : #include <algorithm>
8 : #include <cassert>
9 : #include <format>
10 : #include <iostream>
11 : #include <ranges>
12 : #include <unordered_map>
13 : #include <utility>
14 :
15 : #include "error.h"
16 : #include "glad/glad.h"
17 : #include "graphics-engine/i-shader.h"
18 :
19 : using enum graphics_engine::gl_types::GLShaderObjectParameter;
20 : using enum graphics_engine::gl_types::GLShaderType;
21 : using enum graphics_engine::types::ErrorCode;
22 :
23 : using graphics_engine::error::CheckGLError;
24 : using graphics_engine::error::MakeErrorCode;
25 : using graphics_engine::gl_types::GLShaderType;
26 : using graphics_engine::gl_wrappers::AttachShader;
27 : using graphics_engine::gl_wrappers::CompileShader;
28 : using graphics_engine::gl_wrappers::CreateProgram;
29 : using graphics_engine::gl_wrappers::CreateShader;
30 : using graphics_engine::gl_wrappers::GetShaderInfoLog;
31 : using graphics_engine::gl_wrappers::GetShaderiv;
32 : using graphics_engine::gl_wrappers::LinkProgram;
33 : using graphics_engine::gl_wrappers::ShaderSource;
34 : using graphics_engine::shader::IShaderPtr;
35 : using graphics_engine::types::Expected;
36 : using graphics_engine::types::ShaderSourceMap;
37 :
38 : using std::cerr;
39 : using std::exception;
40 : using std::format;
41 : using std::is_same_v;
42 : using std::runtime_error;
43 : using std::string;
44 : using std::to_underlying;
45 : using std::unexpected;
46 : using std::unordered_map;
47 : using std::vector;
48 : using std::ranges::contains;
49 : using std::ranges::for_each;
50 : using std::views::keys;
51 :
52 : static_assert(is_same_v<GLchar, char>,
53 : "GLchar and char are not the same type!");
54 : static_assert(is_same_v<GLint, int>, "GLint and int are not the same type!");
55 : static_assert(is_same_v<GLsizei, int>,
56 : "GLsizei and int are not the same type!");
57 : static_assert(is_same_v<GLuint, unsigned int>,
58 : "GLuint and unsigned int are not the same type!");
59 :
60 : namespace graphics_engine::shader {
61 :
62 0 : auto DeleteShader(unsigned int shader_id)
63 : -> ::graphics_engine::types::Expected<void> {
64 0 : glDeleteShader(shader_id);
65 0 : if (GLenum error = glGetError(); error != GL_NO_ERROR) {
66 0 : cerr << "glDeleteShader failed with error code " << error << '\n';
67 0 : switch (error) {
68 0 : default:
69 0 : assert(false); // If we get here, add a new case to the switch
70 0 : [[fallthrough]];
71 0 : case GL_INVALID_VALUE:
72 0 : return unexpected(MakeErrorCode(kGLErrorInvalidValue));
73 : }
74 : }
75 :
76 0 : return {};
77 : }
78 :
79 1 : auto CreateIShader(const ShaderSourceMap& sources) -> IShaderPtr {
80 1 : Shader shader;
81 1 : Expected<void> result = shader.Initialize(sources);
82 1 : if (!result.has_value()) {
83 0 : cerr << "Shader initialization failed with error code "
84 0 : << result.error().value() << ": " << result.error().message() << '\n';
85 0 : return nullptr;
86 : }
87 :
88 1 : return std::make_unique<Shader>(shader);
89 1 : }
90 :
91 1 : auto Shader::GetProgramId() const -> unsigned int { return program_id_; }
92 :
93 1 : auto Shader::Initialize(const types::ShaderSourceMap& sources)
94 : -> types::Expected<void> {
95 1 : std::vector<GLuint> shader_ids;
96 :
97 : // Compile each of the shaders in the shader source map.
98 5 : for (const auto& [shader_type, source_code] : sources) {
99 2 : Expected<GLuint> shader_id = CreateShader(shader_type);
100 2 : if (!shader_id) {
101 0 : cerr << "CreateShader failed for shader type "
102 0 : << to_underlying(shader_type) << " with error code "
103 0 : << shader_id.error().value() << ": " << shader_id.error().message()
104 0 : << '\n';
105 0 : return unexpected(shader_id.error());
106 : }
107 :
108 2 : shader_ids.push_back(*shader_id);
109 :
110 2 : const GLchar* source_code_cstr = source_code.c_str();
111 2 : Expected<void> result =
112 2 : ShaderSource(*shader_id, 1, &source_code_cstr, nullptr);
113 2 : if (!result.has_value()) {
114 0 : cerr << "ShaderSource failed with error code " << result.error().value()
115 0 : << ": " << result.error().message() << '\n';
116 0 : return unexpected(result.error());
117 : }
118 :
119 2 : result = CompileShader(*shader_id);
120 2 : if (!result.has_value()) {
121 0 : cerr << "CompileShader failed with error code " << result.error().value()
122 0 : << ": " << result.error().message() << '\n';
123 0 : return unexpected(result.error());
124 : }
125 :
126 2 : int params{};
127 2 : result = GetShaderiv(*shader_id, kCompileStatus, ¶ms);
128 2 : if (!result.has_value()) {
129 0 : cerr << "GetShaderiv failed with error code " << result.error().value()
130 0 : << ": " << result.error().message() << '\n';
131 0 : return unexpected(result.error());
132 : }
133 :
134 : // If compilation failed...
135 2 : if (params == GL_FALSE) {
136 0 : result = GetShaderiv(*shader_id, kInfoLogLength, ¶ms);
137 0 : if (!result.has_value()) {
138 0 : cerr << "GetShaderiv failed with error code " << result.error().value()
139 0 : << ": " << result.error().message() << '\n';
140 0 : return unexpected(result.error());
141 : }
142 :
143 0 : string info_log(params, '\0');
144 0 : result = GetShaderInfoLog(*shader_id, params, nullptr, info_log.data());
145 0 : if (!result.has_value()) {
146 0 : cerr << "GetShaderInfoLog failed with error code "
147 0 : << result.error().value() << ": " << result.error().message()
148 0 : << '\n';
149 0 : return unexpected(result.error());
150 : }
151 0 : }
152 : }
153 :
154 : // Compilation succeeded. Time to link.
155 1 : Expected<GLuint> program_id = CreateProgram();
156 1 : if (!program_id) {
157 0 : cerr << "CreateProgram failed with error code "
158 0 : << program_id.error().value() << ": " << program_id.error().message()
159 0 : << '\n';
160 0 : return unexpected(program_id.error());
161 : }
162 :
163 3 : for (const auto& shader_id : shader_ids) {
164 2 : Expected<void> result = AttachShader(*program_id, shader_id);
165 2 : if (!result) {
166 0 : cerr << "AttachShader failed with error code: " << result.error().value()
167 0 : << ": " << result.error().message() << '\n';
168 0 : return unexpected(result.error());
169 : }
170 : }
171 :
172 1 : Expected<void> result = LinkProgram(*program_id);
173 1 : if (!result) {
174 0 : cerr << "LinkProgram failed with error code: " << result.error().value()
175 0 : << ": " << result.error().message() << '\n';
176 0 : return unexpected(result.error());
177 : }
178 :
179 1 : program_id_ = *program_id;
180 :
181 1 : return {};
182 1 : }
183 :
184 : } // namespace graphics_engine::shader
|