From 9297744a04eddd2c889d40729ccae61b22cf2264 Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Sun, 19 Feb 2023 18:25:16 +0800 Subject: [PATCH] Add support for #pragma once directives in shader include files --- src/resources/shader-loader.cpp | 96 ++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 24 deletions(-) diff --git a/src/resources/shader-loader.cpp b/src/resources/shader-loader.cpp index f3f0729..9e07e6c 100644 --- a/src/resources/shader-loader.cpp +++ b/src/resources/shader-loader.cpp @@ -24,17 +24,43 @@ #include "gl/shader-object.hpp" #include "gl/shader-program.hpp" #include +#include + +/** + * Scans a text file for the presence of a `#pragma once` directive. + * + * @param source Text file to scan. + * + * @return `true` if the file contains a `#pragma once` directive, `false` otherwise. + */ +static bool has_pragma_once(const text_file& source) +{ + for (const auto& line: source) + { + std::istringstream line_stream(line); + std::string token; + + // If line contains a `#pragma once` directive + if (line_stream >> token && token == "#pragma" && + line_stream >> token && token == "once") + { + return true; + } + } + + return false; +} /** * Handles `#pragma include` directives by loading the specified text files and inserting them in place. */ -static void handle_includes(text_file* source, resource_manager* resource_manager) +static void handle_includes(text_file& source, std::unordered_set& include_once, resource_manager* resource_manager) { // For each line in the source - for (std::size_t i = 0; i < source->size(); ++i) + for (std::size_t i = 0; i < source.size(); ++i) { std::string token; - std::istringstream line_stream((*source)[i]); + std::istringstream line_stream(source[i]); // If line contains a `#pragma include` directive if (line_stream >> token && token == "#pragma" && @@ -49,27 +75,39 @@ static void handle_includes(text_file* source, resource_manager* resource_manage std::string path = token.substr(1, token.length() - 2); // Load include file - if (!resource_manager->load(path)) + text_file* include_file = resource_manager->load(path); + if (!include_file) { - (*source)[i] = "#error file not found (" + path + ")"; + source[i] = "#error file not found (" + path + ")"; } else { - // Create copy of include file - text_file include_file = *(resource_manager->load(path)); - - // Handle `#pragma include` directives inside include file - handle_includes(&include_file, resource_manager); - - // Replace #pragma include directive with include file contents - source->erase(source->begin() + i); - source->insert(source->begin() + i, include_file.begin(), include_file.end()); - i += include_file.size() - 1; + // If file has not been included or has no `#pragma once` directive + if (!include_once.contains(include_file)) + { + // If file has `#pragma once` directive + if (has_pragma_once(*include_file)) + { + // Add file to set of files to include once + include_once.insert(include_file); + } + + // Create a copy of the include file + text_file include_file_copy = *include_file; + + // Handle `#pragma include` directives inside include file + handle_includes(include_file_copy, include_once, resource_manager); + + // Replace #pragma include directive with include file contents + source.erase(source.begin() + i); + source.insert(source.begin() + i, include_file_copy.begin(), include_file_copy.end()); + i += include_file_copy.size() - 1; + } } } else { - (*source)[i] = "#error malformed include directive (" + (*source)[i] + ")"; + source[i] = "#error malformed include directive (" + source[i] + ")"; } } } @@ -78,15 +116,20 @@ static void handle_includes(text_file* source, resource_manager* resource_manage template <> gl::shader_program* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) { - // Load shader template source - text_file source_lines = *resource_loader::load(resource_manager, file, path); + // Load shader source file + text_file* source_file = resource_loader::load(resource_manager, file, path); + + // Make a copy of the shader source file + text_file source_file_copy = *source_file; // Handle `#pragma include` directives - handle_includes(&source_lines, resource_manager); + std::unordered_set include_once; + include_once.insert(source_file); + handle_includes(source_file_copy, include_once, resource_manager); // Join vector of source lines into single string std::ostringstream stream; - std::copy(source_lines.begin(), source_lines.end(), std::ostream_iterator(stream, "\n")); + std::copy(source_file_copy.begin(), source_file_copy.end(), std::ostream_iterator(stream, "\n")); // Create shader template render::shader_template* shader = new render::shader_template(stream.str()); @@ -109,15 +152,20 @@ gl::shader_program* resource_loader::load(resource_manager* template <> render::shader_template* resource_loader::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) { - // Load shader template source - text_file source_lines = *resource_loader::load(resource_manager, file, path); + // Load shader template source file + text_file* source_file = resource_loader::load(resource_manager, file, path); + + // Make a copy of the shader template source file + text_file source_file_copy = *source_file; // Handle `#pragma include` directives - handle_includes(&source_lines, resource_manager); + std::unordered_set include_once; + include_once.insert(source_file); + handle_includes(source_file_copy, include_once, resource_manager); // Join vector of source lines into single string std::ostringstream stream; - std::copy(source_lines.begin(), source_lines.end(), std::ostream_iterator(stream, "\n")); + std::copy(source_file_copy.begin(), source_file_copy.end(), std::ostream_iterator(stream, "\n")); // Create shader template render::shader_template* shader = new render::shader_template(stream.str());