Serenity Operating System
1/*
2 * Copyright (c) 2022, Stephan Unverwerth <s.unverwerth@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <LibGL/GL/gl.h>
8#include <LibGL/Shaders/Program.h>
9#include <LibGLSL/Linker.h>
10
11namespace GL {
12
13NonnullRefPtr<Program> Program::create()
14{
15 return adopt_ref(*new Program());
16}
17
18bool Program::is_shader_attached(Shader const& shader) const
19{
20 switch (shader.type()) {
21 case GL_VERTEX_SHADER:
22 return m_vertex_shaders.contains_slow(shader);
23 case GL_FRAGMENT_SHADER:
24 return m_fragment_shaders.contains_slow(shader);
25 default:
26 VERIFY_NOT_REACHED();
27 }
28}
29
30ErrorOr<void> Program::attach_shader(Shader& shader)
31{
32 if (is_shader_attached(shader))
33 return Error::from_string_literal("Trying to attach a shader that is already attached");
34
35 switch (shader.type()) {
36 case GL_VERTEX_SHADER:
37 TRY(m_vertex_shaders.try_append(shader));
38 break;
39
40 case GL_FRAGMENT_SHADER:
41 TRY(m_fragment_shaders.try_append(shader));
42 break;
43
44 default:
45 VERIFY_NOT_REACHED();
46 }
47
48 return {};
49}
50
51ErrorOr<void> Program::link(GPU::Device& device)
52{
53 m_info_log = String {};
54
55 GLSL::Linker linker;
56
57 // Link vertex shader objects
58
59 Vector<GLSL::ObjectFile const*> vertex_shader_object_files;
60 for (auto const& vertex_shader : m_vertex_shaders)
61 vertex_shader_object_files.append(vertex_shader->object_file());
62
63 auto linked_vertex_shader_or_error = linker.link(vertex_shader_object_files);
64
65 if (linked_vertex_shader_or_error.is_error()) {
66 m_link_status = false;
67 m_info_log = linker.messages();
68 return linked_vertex_shader_or_error.release_error();
69 }
70
71 m_linked_vertex_shader = linked_vertex_shader_or_error.release_value();
72
73 // Link fragment shader objects
74
75 Vector<GLSL::ObjectFile const*> fragment_shader_object_files;
76 for (auto fragment_shader : m_fragment_shaders)
77 fragment_shader_object_files.append(fragment_shader->object_file());
78
79 auto linked_fragment_shader_or_error = linker.link(fragment_shader_object_files);
80
81 if (linked_fragment_shader_or_error.is_error()) {
82 m_link_status = false;
83 m_info_log = linker.messages();
84 return linked_fragment_shader_or_error.release_error();
85 }
86
87 m_linked_fragment_shader = linked_fragment_shader_or_error.release_value();
88
89 m_gpu_vertex_shader = TRY(device.create_shader(m_linked_vertex_shader->intermediate_shader_representation()));
90 m_gpu_fragment_shader = TRY(device.create_shader(m_linked_fragment_shader->intermediate_shader_representation()));
91
92 m_link_status = true;
93 return {};
94}
95
96size_t Program::info_log_length() const
97{
98 if (!m_info_log.has_value())
99 return 0;
100
101 // Per the spec we return the size including the null terminator
102 return m_info_log.value().bytes().size() + 1;
103}
104
105}