Skip to content
Snippets Groups Projects
Compiler.cpp 3.19 KiB

#include "vkcv/shader/Compiler.hpp"

#include <vkcv/Container.hpp>
#include <vkcv/File.hpp>
#include <vkcv/Logger.hpp>

namespace vkcv::shader {
	
	bool Compiler::compileSourceWithHeaders(ShaderStage shaderStage,
											const std::string &shaderSource,
											const Dictionary<std::filesystem::path, std::string> &shaderHeaders,
											const ShaderCompiledFunction &compiled) {
		const std::filesystem::path directory = generateTemporaryDirectoryPath();
		
		if (!std::filesystem::create_directory(directory)) {
			vkcv_log(LogLevel::ERROR, "The directory could not be created (%s)", directory.c_str());
			return false;
		}
		
		for (const auto& header : shaderHeaders) {
			if (header.first.has_parent_path()) {
				std::filesystem::create_directories(directory / header.first.parent_path());
			}
			
			if (!writeTextToFile(directory / header.first, header.second)) {
				return false;
			}
		}
		
		return compileSource(
				shaderStage,
				shaderSource,
				[&directory, &compiled] (vkcv::ShaderStage shaderStage,
										 const std::filesystem::path& path) {
					if (compiled) {
						compiled(shaderStage, path);
					}
					
					std::filesystem::remove_all(directory);
				}, directory
		);
	}

	void Compiler::compile(ShaderStage shaderStage,
												 const std::filesystem::path &shaderPath,
												 const ShaderCompiledFunction &compiled,
												 const std::filesystem::path &includePath,
												 bool update) {
		std::string shaderCode;
		bool result = readTextFromFile(shaderPath, shaderCode);
		
		if (!result) {
			vkcv_log(LogLevel::ERROR, "Loading shader failed: (%s)", shaderPath.string().c_str());
		}
		
		if (!includePath.empty()) {
			result = compileSource(shaderStage, shaderCode, compiled, includePath);
		} else {
			result = compileSource(shaderStage, shaderCode, compiled, shaderPath.parent_path());
		}
		
		if (!result) {
			vkcv_log(LogLevel::ERROR, "Shader compilation failed: (%s)", shaderPath.string().c_str());
		}
		
		if (update) {
			// TODO: Shader hot compilation during runtime
		}
	}
	
	void Compiler::compileProgram(ShaderProgram& program,
								  const Dictionary<ShaderStage, const std::filesystem::path>& stages,
								  const ShaderProgramCompiledFunction& compiled,
								  const std::filesystem::path& includePath, bool update) {
		std::vector<std::pair<ShaderStage, const std::filesystem::path>> stageList;
		size_t i;
		
		stageList.reserve(stages.size());
		for (const auto& stage : stages) {
			stageList.push_back(stage);
		}
		
		/* Compile a shader programs stages in parallel to improve performance */
		#pragma omp parallel for shared(stageList, includePath, update) private(i)
		for (i = 0; i < stageList.size(); i++) {
			const auto& stage = stageList[i];
			
			compile(
				stage.first,
				stage.second,
				[&program](ShaderStage shaderStage, const std::filesystem::path& path) {
					#pragma omp critical
					program.addShader(shaderStage, path);
				},
				includePath,
				update
			);
		}
		
		if (compiled) {
			compiled(program);
		}
	}
	
	std::string Compiler::getDefine(const std::string &name) const {
		return m_defines.at(name);
	}
	
	void Compiler::setDefine(const std::string &name, const std::string &value) {
		m_defines[name] = value;
	}
	
}