Serenity Operating System
1/*
2 * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Array.h>
8#include <AK/DeprecatedString.h>
9#include <AK/JsonObject.h>
10#include <AK/NumericLimits.h>
11#include <AK/Optional.h>
12#include <AK/SourceGenerator.h>
13#include <AK/StringBuilder.h>
14#include <AK/StringView.h>
15#include <AK/Vector.h>
16#include <LibCore/ArgsParser.h>
17#include <LibCore/File.h>
18#include <LibMain/Main.h>
19
20struct ArgumentDefinition {
21 Optional<DeprecatedString> name;
22 Optional<DeprecatedString> cpp_type;
23 DeprecatedString expression;
24 Optional<DeprecatedString> cast_to;
25};
26
27struct FunctionDefinition {
28 DeprecatedString name;
29 DeprecatedString return_type;
30 Vector<ArgumentDefinition> arguments;
31 DeprecatedString implementation;
32 bool unimplemented;
33 DeprecatedString variant_gl_type;
34};
35
36struct VariantType {
37 DeprecatedString encoded_type;
38 Optional<DeprecatedString> implementation;
39 bool unimplemented;
40};
41
42struct Variants {
43 Vector<DeprecatedString> api_suffixes { "" };
44 Vector<u32> argument_counts { NumericLimits<u32>::max() };
45 Vector<DeprecatedString> argument_defaults { "" };
46 bool convert_range { false };
47 Vector<VariantType> types {
48 {
49 .encoded_type = "",
50 .implementation = Optional<DeprecatedString> {},
51 .unimplemented = false,
52 }
53 };
54 DeprecatedString pointer_argument { "" };
55};
56
57struct EncodedTypeEntry {
58 StringView encoded_type;
59 StringView cpp_type;
60 StringView gl_type;
61};
62
63// clang-format off
64constexpr static Array<EncodedTypeEntry, 9> type_definitions = {
65 EncodedTypeEntry { "b"sv, "GLbyte"sv, "GL_BYTE"sv },
66 EncodedTypeEntry { "d"sv, "GLdouble"sv, "GL_DOUBLE"sv },
67 EncodedTypeEntry { "f"sv, "GLfloat"sv, "GL_FLOAT"sv },
68 EncodedTypeEntry { "i"sv, "GLint"sv, "GL_INT"sv },
69 EncodedTypeEntry { "s"sv, "GLshort"sv, "GL_SHORT"sv },
70 EncodedTypeEntry { "ub"sv, "GLubyte"sv, "GL_UNSIGNED_BYTE"sv },
71 EncodedTypeEntry { "ui"sv, "GLuint"sv, "GL_UNSIGNED_INT"sv },
72 EncodedTypeEntry { "us"sv, "GLushort"sv, "GL_UNSIGNED_SHORT"sv },
73 EncodedTypeEntry { "x"sv, "GLfixed"sv, "GL_INT"sv },
74};
75// clang-format on
76
77struct EncodedType {
78 EncodedTypeEntry type_entry;
79 DeprecatedString cpp_type;
80 DeprecatedString function_name_suffix;
81 bool is_pointer;
82 bool is_const_pointer;
83};
84
85Vector<DeprecatedString> get_name_list(Optional<JsonValue const&> name_definition)
86{
87 if (!name_definition.has_value() || name_definition->is_null())
88 return {};
89
90 Vector<DeprecatedString, 1> names;
91 if (name_definition->is_string()) {
92 names.append(name_definition->as_string());
93 } else if (name_definition->is_array()) {
94 name_definition->as_array().for_each([&names](auto& value) {
95 VERIFY(value.is_string());
96 names.append(value.as_string());
97 });
98 } else {
99 VERIFY_NOT_REACHED();
100 }
101 return names;
102}
103
104Optional<EncodedType> get_encoded_type(DeprecatedString encoded_type)
105{
106 bool is_const_pointer = !encoded_type.ends_with('!');
107 if (!is_const_pointer)
108 encoded_type = encoded_type.substring_view(0, encoded_type.length() - 1);
109 DeprecatedString function_name_suffix = encoded_type;
110
111 bool is_pointer = encoded_type.ends_with('v');
112 if (is_pointer)
113 encoded_type = encoded_type.substring_view(0, encoded_type.length() - 1);
114
115 VERIFY(is_const_pointer || is_pointer);
116
117 Optional<EncodedTypeEntry> type_definition;
118 for (size_t i = 0; i < type_definitions.size(); ++i) {
119 if (type_definitions[i].encoded_type == encoded_type) {
120 type_definition = type_definitions[i];
121 break;
122 }
123 }
124
125 if (!type_definition.has_value())
126 return {};
127
128 return EncodedType {
129 .type_entry = type_definition.value(),
130 .cpp_type = DeprecatedString::formatted(
131 "{}{}{}",
132 type_definition->cpp_type,
133 is_pointer && is_const_pointer ? " const" : "",
134 is_pointer ? "*" : ""),
135 .function_name_suffix = function_name_suffix,
136 .is_pointer = is_pointer,
137 .is_const_pointer = is_const_pointer,
138 };
139}
140
141DeprecatedString wrap_expression_in_range_conversion(DeprecatedString source_type, DeprecatedString target_type, DeprecatedString expression)
142{
143 VERIFY(target_type == "GLfloat" || target_type == "GLdouble");
144
145 // No range conversion required
146 if (source_type == target_type || source_type == "GLdouble")
147 return expression;
148
149 if (source_type == "GLbyte")
150 return DeprecatedString::formatted("({} + 128.) / 127.5 - 1.", expression);
151 else if (source_type == "GLfloat")
152 return DeprecatedString::formatted("static_cast<GLdouble>({})", expression);
153 else if (source_type == "GLint")
154 return DeprecatedString::formatted("({} + 2147483648.) / 2147483647.5 - 1.", expression);
155 else if (source_type == "GLshort")
156 return DeprecatedString::formatted("({} + 32768.) / 32767.5 - 1.", expression);
157 else if (source_type == "GLubyte")
158 return DeprecatedString::formatted("{} / 255.", expression);
159 else if (source_type == "GLuint")
160 return DeprecatedString::formatted("{} / 4294967296.", expression);
161 else if (source_type == "GLushort")
162 return DeprecatedString::formatted("{} / 65536.", expression);
163 VERIFY_NOT_REACHED();
164}
165
166Variants read_variants_settings(JsonObject const& variants_obj)
167{
168 Variants variants;
169
170 if (variants_obj.has_array("argument_counts"sv)) {
171 variants.argument_counts.clear_with_capacity();
172 variants_obj.get_array("argument_counts"sv)->for_each([&](auto const& argument_count_value) {
173 variants.argument_counts.append(argument_count_value.to_u32());
174 });
175 }
176 if (variants_obj.has_array("argument_defaults"sv)) {
177 variants.argument_defaults.clear_with_capacity();
178 variants_obj.get_array("argument_defaults"sv)->for_each([&](auto const& argument_default_value) {
179 variants.argument_defaults.append(argument_default_value.as_string());
180 });
181 }
182 if (variants_obj.has_bool("convert_range"sv)) {
183 variants.convert_range = variants_obj.get_bool("convert_range"sv).value();
184 }
185 if (variants_obj.has_array("api_suffixes"sv)) {
186 variants.api_suffixes.clear_with_capacity();
187 variants_obj.get_array("api_suffixes"sv)->for_each([&](auto const& suffix_value) {
188 variants.api_suffixes.append(suffix_value.as_string());
189 });
190 }
191 if (variants_obj.has_string("pointer_argument"sv)) {
192 variants.pointer_argument = variants_obj.get_deprecated_string("pointer_argument"sv).value();
193 }
194 if (variants_obj.has_object("types"sv)) {
195 variants.types.clear_with_capacity();
196 variants_obj.get_object("types"sv)->for_each_member([&](auto const& key, auto const& type_value) {
197 auto const& type = type_value.as_object();
198 variants.types.append(VariantType {
199 .encoded_type = key,
200 .implementation = type.get_deprecated_string("implementation"sv),
201 .unimplemented = type.get_bool("unimplemented"sv).value_or(false),
202 });
203 });
204 }
205
206 return variants;
207}
208
209Vector<ArgumentDefinition> copy_arguments_for_variant(Vector<ArgumentDefinition> arguments, Variants variants,
210 u32 argument_count, EncodedType encoded_type)
211{
212 Vector<ArgumentDefinition> variant_arguments = arguments;
213 auto base_cpp_type = encoded_type.type_entry.cpp_type;
214
215 size_t variadic_index = 0;
216 for (size_t i = 0; i < variant_arguments.size(); ++i) {
217 // Skip arguments with a fixed type
218 if (variant_arguments[i].cpp_type.has_value())
219 continue;
220
221 variant_arguments[i].cpp_type = encoded_type.cpp_type;
222 auto cast_to = variant_arguments[i].cast_to;
223
224 // Pointer argument
225 if (encoded_type.is_pointer) {
226 variant_arguments[i].name = (variadic_index == 0) ? variants.pointer_argument : Optional<DeprecatedString> {};
227
228 if (variadic_index >= argument_count) {
229 // If this variable argument is past the argument count, fall back to the defaults
230 variant_arguments[i].expression = variants.argument_defaults[variadic_index];
231 variant_arguments[i].cast_to = Optional<DeprecatedString> {};
232
233 } else if (argument_count == 1 && variants.argument_counts.size() == 1) {
234 // Otherwise, if the pointer is the only variadic argument, pass it through unchanged
235 variant_arguments[i].cast_to = Optional<DeprecatedString> {};
236
237 } else {
238 // Otherwise, index into the pointer argument
239 auto indexed_expression = DeprecatedString::formatted("{}[{}]", variants.pointer_argument, variadic_index);
240 if (variants.convert_range && cast_to.has_value())
241 indexed_expression = wrap_expression_in_range_conversion(base_cpp_type, cast_to.value(), indexed_expression);
242 variant_arguments[i].expression = indexed_expression;
243 }
244
245 } else {
246 // Regular argument
247 if (variadic_index >= argument_count) {
248 // If the variable argument is past the argument count, fall back to the defaults
249 variant_arguments[i].name = Optional<DeprecatedString> {};
250 variant_arguments[i].expression = variants.argument_defaults[variadic_index];
251 variant_arguments[i].cast_to = Optional<DeprecatedString> {};
252
253 } else if (variants.convert_range && cast_to.has_value()) {
254 // Otherwise, if we need to convert the input values, wrap the expression in a range conversion
255 variant_arguments[i].expression = wrap_expression_in_range_conversion(
256 base_cpp_type,
257 cast_to.value(),
258 variant_arguments[i].expression);
259 }
260 }
261
262 // Determine if we can skip casting to the target type
263 if (cast_to == base_cpp_type || (variants.convert_range && cast_to == "GLdouble"))
264 variant_arguments[i].cast_to = Optional<DeprecatedString> {};
265
266 variadic_index++;
267 }
268
269 return variant_arguments;
270}
271
272Vector<FunctionDefinition> create_function_definitions(DeprecatedString function_name, JsonObject const& function_definition)
273{
274 // A single function definition can expand to multiple generated functions by way of:
275 // - differing API suffices (ARB, EXT, etc.);
276 // - differing argument counts;
277 // - differing argument types.
278 // These can all be combined.
279
280 // Parse base argument definitions first; these may later be modified by variants
281 Vector<ArgumentDefinition> argument_definitions;
282 JsonArray const& arguments = function_definition.get_array("arguments"sv).value_or(JsonArray {});
283 arguments.for_each([&argument_definitions](auto const& argument_value) {
284 VERIFY(argument_value.is_object());
285 auto const& argument = argument_value.as_object();
286
287 auto type = argument.get_deprecated_string("type"sv);
288 auto argument_names = get_name_list(argument.get("name"sv));
289 auto expression = argument.get_deprecated_string("expression"sv).value_or("@argument_name@");
290 auto cast_to = argument.get_deprecated_string("cast_to"sv);
291
292 // Add an empty dummy name when all we have is an expression
293 if (argument_names.is_empty() && !expression.is_empty())
294 argument_names.append("");
295
296 for (auto const& argument_name : argument_names) {
297 argument_definitions.append({ .name = argument_name.is_empty() ? Optional<DeprecatedString> {} : argument_name,
298 .cpp_type = type,
299 .expression = expression,
300 .cast_to = cast_to });
301 }
302 });
303
304 // Create functions for each name and/or variant
305 Vector<FunctionDefinition> functions;
306
307 auto return_type = function_definition.get_deprecated_string("return_type"sv).value_or("void");
308 auto function_implementation = function_definition.get_deprecated_string("implementation"sv).value_or(function_name.to_snakecase());
309 auto function_unimplemented = function_definition.get_bool("unimplemented"sv).value_or(false);
310
311 if (!function_definition.has("variants"sv)) {
312 functions.append({
313 .name = function_name,
314 .return_type = return_type,
315 .arguments = argument_definitions,
316 .implementation = function_implementation,
317 .unimplemented = function_unimplemented,
318 .variant_gl_type = "",
319 });
320 return functions;
321 }
322
323 // Read variants settings for this function
324 auto variants_obj = function_definition.get_object("variants"sv).value();
325 auto variants = read_variants_settings(variants_obj);
326
327 for (auto argument_count : variants.argument_counts) {
328 for (auto const& variant_type : variants.types) {
329 auto encoded_type = get_encoded_type(variant_type.encoded_type);
330 auto variant_arguments = encoded_type.has_value()
331 ? copy_arguments_for_variant(argument_definitions, variants, argument_count, encoded_type.value())
332 : argument_definitions;
333 auto variant_type_implementation = variant_type.implementation.has_value()
334 ? variant_type.implementation.value()
335 : function_implementation;
336
337 for (auto const& api_suffix : variants.api_suffixes) {
338 functions.append({
339 .name = DeprecatedString::formatted(
340 "{}{}{}{}",
341 function_name,
342 variants.argument_counts.size() > 1 ? DeprecatedString::formatted("{}", argument_count) : "",
343 encoded_type.has_value() && variants.types.size() > 1 ? encoded_type->function_name_suffix : "",
344 api_suffix),
345 .return_type = return_type,
346 .arguments = variant_arguments,
347 .implementation = variant_type_implementation,
348 .unimplemented = variant_type.unimplemented || function_unimplemented,
349 .variant_gl_type = encoded_type.has_value() ? encoded_type->type_entry.gl_type : ""sv,
350 });
351 }
352 }
353 }
354
355 return functions;
356}
357
358ErrorOr<void> generate_header_file(JsonObject& api_data, Core::File& file)
359{
360 StringBuilder builder;
361 SourceGenerator generator { builder };
362
363 generator.appendln("#pragma once");
364 generator.append("\n");
365 generator.appendln("#include <LibGL/GL/glplatform.h>");
366 generator.append("\n");
367 generator.appendln("#ifdef __cplusplus");
368 generator.appendln("extern \"C\" {");
369 generator.appendln("#endif");
370 generator.append("\n");
371
372 api_data.for_each_member([&](auto& function_name, auto& value) {
373 VERIFY(value.is_object());
374 auto const& function = value.as_object();
375 auto function_definitions = create_function_definitions(function_name, function);
376
377 for (auto const& function_definition : function_definitions) {
378 auto function_generator = generator.fork();
379
380 function_generator.set("name", function_definition.name);
381 function_generator.set("return_type", function_definition.return_type);
382
383 function_generator.append("GLAPI @return_type@ gl@name@(");
384
385 bool first = true;
386 for (auto const& argument_definition : function_definition.arguments) {
387 if (!argument_definition.name.has_value() || !argument_definition.cpp_type.has_value())
388 continue;
389
390 auto argument_generator = function_generator.fork();
391 argument_generator.set("argument_type", argument_definition.cpp_type.value());
392 argument_generator.set("argument_name", argument_definition.name.value());
393
394 if (!first)
395 argument_generator.append(", ");
396 first = false;
397 argument_generator.append("@argument_type@ @argument_name@");
398 }
399
400 function_generator.appendln(");");
401 }
402 });
403
404 generator.appendln("#ifdef __cplusplus");
405 generator.appendln("}");
406 generator.appendln("#endif");
407
408 TRY(file.write_until_depleted(generator.as_string_view().bytes()));
409 return {};
410}
411
412ErrorOr<void> generate_implementation_file(JsonObject& api_data, Core::File& file)
413{
414 StringBuilder builder;
415 SourceGenerator generator { builder };
416
417 generator.appendln("#include <LibGL/GL/glapi.h>");
418 generator.appendln("#include <LibGL/GLContext.h>");
419 generator.append("\n");
420 generator.appendln("extern GL::GLContext* g_gl_context;");
421 generator.append("\n");
422
423 api_data.for_each_member([&](auto& function_name, auto& value) {
424 VERIFY(value.is_object());
425 JsonObject const& function = value.as_object();
426 auto function_definitions = create_function_definitions(function_name, function);
427
428 for (auto const& function_definition : function_definitions) {
429 auto function_generator = generator.fork();
430 auto return_type = function_definition.return_type;
431
432 function_generator.set("name"sv, function_definition.name);
433 function_generator.set("return_type"sv, return_type);
434 function_generator.set("implementation"sv, function_definition.implementation);
435 function_generator.set("variant_gl_type"sv, function_definition.variant_gl_type);
436
437 function_generator.append("@return_type@ gl@name@(");
438
439 bool first = true;
440 for (auto const& argument_definition : function_definition.arguments) {
441 if (!argument_definition.name.has_value() || !argument_definition.cpp_type.has_value())
442 continue;
443
444 auto argument_generator = function_generator.fork();
445 argument_generator.set("argument_type", argument_definition.cpp_type.value());
446 argument_generator.set("argument_name", argument_definition.name.value());
447
448 if (!first)
449 argument_generator.append(", ");
450 first = false;
451 argument_generator.append("@argument_type@ @argument_name@");
452 }
453 function_generator.appendln(")");
454 function_generator.appendln("{");
455
456 if (function_definition.unimplemented) {
457 function_generator.append(" dbgln(\"gl@name@(");
458
459 first = true;
460 for (auto const& argument_definition : function_definition.arguments) {
461 if (!argument_definition.name.has_value())
462 continue;
463 if (!first)
464 function_generator.append(", ");
465 first = false;
466 if (argument_definition.cpp_type.value().ends_with('*'))
467 function_generator.append("{:p}");
468 else if (argument_definition.cpp_type.value() == "GLenum")
469 function_generator.append("{:#x}");
470 else
471 function_generator.append("{}");
472 }
473
474 function_generator.append("): unimplemented\"");
475
476 for (auto const& argument_definition : function_definition.arguments) {
477 if (!argument_definition.name.has_value())
478 continue;
479
480 function_generator.append(", ");
481 function_generator.append(argument_definition.name.value());
482 }
483
484 function_generator.appendln(");");
485 function_generator.appendln(" TODO();");
486 } else {
487 function_generator.appendln(" if (!g_gl_context)");
488 if (return_type.ends_with('*'))
489 function_generator.appendln(" return nullptr;");
490 else if (return_type == "GLboolean"sv)
491 function_generator.appendln(" return GL_FALSE;");
492 else if (return_type == "GLenum"sv)
493 function_generator.appendln(" return GL_INVALID_OPERATION;");
494 else if (return_type == "GLuint"sv)
495 function_generator.appendln(" return 0;");
496 else if (return_type == "void"sv)
497 function_generator.appendln(" return;");
498 else
499 VERIFY_NOT_REACHED();
500 function_generator.append(" ");
501 if (return_type != "void"sv)
502 function_generator.append("return ");
503 function_generator.append("g_gl_context->gl_@implementation@(");
504
505 first = true;
506 for (auto const& argument_definition : function_definition.arguments) {
507 auto argument_generator = function_generator.fork();
508
509 auto cast_to = argument_definition.cast_to;
510 argument_generator.set("argument_name", argument_definition.name.value_or(""));
511 argument_generator.set("cast_to", cast_to.value_or(""));
512
513 if (!first)
514 argument_generator.append(", ");
515 first = false;
516
517 if (cast_to.has_value())
518 argument_generator.append("static_cast<@cast_to@>(");
519 argument_generator.append(argument_definition.expression);
520 if (cast_to.has_value())
521 argument_generator.append(")");
522 }
523
524 function_generator.appendln(");");
525 }
526
527 function_generator.appendln("}");
528 function_generator.append("\n");
529 }
530 });
531
532 TRY(file.write_until_depleted(generator.as_string_view().bytes()));
533 return {};
534}
535
536ErrorOr<JsonValue> read_entire_file_as_json(StringView filename)
537{
538 auto file = TRY(Core::File::open(filename, Core::File::OpenMode::Read));
539 auto json_size = TRY(file->size());
540 auto json_data = TRY(ByteBuffer::create_uninitialized(json_size));
541 TRY(file->read_until_filled(json_data.bytes()));
542 return JsonValue::from_string(json_data);
543}
544
545ErrorOr<int> serenity_main(Main::Arguments arguments)
546{
547 StringView generated_header_path;
548 StringView generated_implementation_path;
549 StringView api_json_path;
550
551 Core::ArgsParser args_parser;
552 args_parser.add_option(generated_header_path, "Path to the OpenGL API header file to generate", "generated-header-path", 'h', "generated-header-path");
553 args_parser.add_option(generated_implementation_path, "Path to the OpenGL API implementation file to generate", "generated-implementation-path", 'c', "generated-implementation-path");
554 args_parser.add_option(api_json_path, "Path to the JSON file to read from", "json-path", 'j', "json-path");
555 args_parser.parse(arguments);
556
557 auto json = TRY(read_entire_file_as_json(api_json_path));
558 VERIFY(json.is_object());
559 auto api_data = json.as_object();
560
561 auto generated_header_file = TRY(Core::File::open(generated_header_path, Core::File::OpenMode::Write));
562 auto generated_implementation_file = TRY(Core::File::open(generated_implementation_path, Core::File::OpenMode::Write));
563
564 TRY(generate_header_file(api_data, *generated_header_file));
565 TRY(generate_implementation_file(api_data, *generated_implementation_file));
566
567 return 0;
568}