diff --git a/xmake.lua b/xmake.lua index 9e9f44ea0..d3b70f830 100644 --- a/xmake.lua +++ b/xmake.lua @@ -167,6 +167,7 @@ for name, module in pairs(modules) do end end +includes("xmake/actions/*.lua") includes("tools/xmake.lua") includes("plugins/*/xmake.lua") includes("examples/*/xmake.lua") diff --git a/xmake/actions/generateheaders.lua b/xmake/actions/generateheaders.lua new file mode 100644 index 000000000..b23e88479 --- /dev/null +++ b/xmake/actions/generateheaders.lua @@ -0,0 +1,112 @@ + +task("update-globalheaders") + +set_menu({ + -- Settings menu usage + usage = "xmake update-globalheaders [options]", + description = "Regenerate global headers for each module" +}) + +on_run(function () + local paths = {} + + local excludedFiles = { + ["ConfigCheck.hpp"] = true, + ["Debug.hpp"] = true, + ["DebugOff.hpp"] = true, + ["ThreadSafety.hpp"] = true, + ["ThreadSafetyOff.hpp"] = true + } + + local modules = os.dirs("include/Nazara/*") + for _, modulePath in pairs(modules) do + local moduleName = modulePath:match(".*[\\/](.*)") + + local config, err = io.open(modulePath .. "/Config.hpp", "r") + local head = "" + if (not config) then + error("Failed to read config file: " .. err) + end + + for line in config:lines() do + if (line == "#pragma once") then -- Stop before including the #pragma once as it's already written automatically + break + end + head = head .. line .. "\n" + end + + config:close() + + table.insert(paths, { + Excludes = excludedFiles, + Header = head, + HeaderGuard = "NAZARA_GLOBAL_" .. moduleName:upper() .. "_HPP", + Name = "Nazara" .. moduleName, + SearchDir = modulePath, + Target = modulePath .. ".hpp" + }) + end + + table.insert(paths, { + Excludes = { + ["DeviceFunctions.hpp"] = true, + ["GlobalFunctions.hpp"] = true, + ["InstanceFunctions.hpp"] = true, + }, + HeaderGuard = "NAZARA_GLOBAL_OPENGLRENDERER_WRAPPER_HPP", + Name = "OpenGL wrapper", + SearchDir = "include/Nazara/OpenGLRenderer/Wrapper", + Target = "include/Nazara/OpenGLRenderer/Wrapper.hpp" + }) + + table.insert(paths, { + Excludes = { + ["DeviceFunctions.hpp"] = true, + ["GlobalFunctions.hpp"] = true, + ["InstanceFunctions.hpp"] = true, + }, + HeaderGuard = "NAZARA_GLOBAL_VULKANRENDERER_WRAPPER_HPP", + Name = "Vulkan wrapper", + SearchDir = "include/Nazara/VulkanRenderer/Wrapper", + Target = "include/Nazara/VulkanRenderer/Wrapper.hpp" + }) + + for k,v in ipairs(paths) do + print(v.Name) + local header, err = io.open(v.Target, "w+") + if (not header) then + error("Failed to create header file (" .. v.Target .. "): " .. err) + end + + header:write("// This file was automatically generated\n\n") + if (v.Header) then + header:write(v.Header) + end + + header:write("#pragma once\n\n") + header:write("#ifndef " .. v.HeaderGuard .. "\n") + header:write("#define " .. v.HeaderGuard .. "\n\n") + + local files = os.files(v.SearchDir .. "/*.hpp") + local count = 0 + for _, filePath in pairs(files) do + local pathParts = path.split(filePath) + if (pathParts[1] == "include") then + table.remove(pathParts, 1) + end + + local include = table.concat(pathParts, "/") + print(include) + local fileName = path.filename(filePath) + if (not v.Excludes[fileName]) then + header:write("#include <" .. include .. ">\n") + count = count + 1 + end + end + + header:write("\n#endif // " .. v.HeaderGuard .. "\n") + header:close() + + print(string.format("-#include count: %d", count)) + end +end) diff --git a/xmake/actions/spirv.lua b/xmake/actions/spirv.lua new file mode 100644 index 000000000..8e49d4bc8 --- /dev/null +++ b/xmake/actions/spirv.lua @@ -0,0 +1,282 @@ +local spirvGrammarURI = "https://raw.githubusercontent.com/KhronosGroup/SPIRV-Headers/master/include/spirv/unified1/spirv.core.grammar.json" + +task("update-spirv") + +set_menu({ + -- Settings menu usage + usage = "xmake update-spirv [options]", + description = "Download and parse the SpirV grammar and updates the shader module files with it" +}) + +on_run(function() + import("core.base.json") + import("net.http") + + io.write("Downloading Spir-V grammar... ") + + local tempGrammar = os.tmpfile() .. ".spirv.core.grammar.json" + + http.download(spirvGrammarURI, tempGrammar) + + print("Done") + + io.write("Parsing... ") + + local content = io.readfile(tempGrammar) + + local result, err = json.decode(content) + assert(result, err) + + local instructions = {} + local instructionById = {} + for _, instruction in pairs(result.instructions) do + local duplicateId = instructionById[instruction.opcode] + if (duplicateId == nil) then + table.insert(instructions, instruction) + instructionById[instruction.opcode] = #instructions + else + instructions[duplicateId] = instruction + end + end + + local operands = {} + local operandByInstruction = {} + for _, instruction in pairs(instructions) do + if (instruction.operands) then + local resultId + local firstId = #operands + local operandCount = #instruction.operands + for i, operand in pairs(instruction.operands) do + table.insert(operands, operand) + + if (operand.kind == "IdResult") then + assert(not resultId, "unexpected operand with two IdResult") + resultId = i - 1 + end + end + + operandByInstruction[instruction.opcode] = { firstId = firstId, count = operandCount, resultId = resultId } + end + end + + print("Done") + + io.write("Generating... ") + + local headerFile = io.open("include/Nazara/Shader/SpirvData.hpp", "w+") + assert(headerFile, "failed to open Spir-V header") + + headerFile:write([[ +// Copyright (C) ]] .. os.date("%Y") .. [[ Jérôme Leclercq +// This file is part of the "Nazara Engine - Shader generator" +// For conditions of distribution and use, see copyright notice in Config.hpp" + +// This file was generated automatically, please do not edit + +#pragma once + +#ifndef NAZARA_SPIRVDATA_HPP +#define NAZARA_SPIRVDATA_HPP + +#include +#include +#include + +namespace Nz +{ +]]) + + headerFile:write([[ + constexpr UInt32 SpirvMagicNumber = ]] .. result.magic_number .. [[; + constexpr UInt32 SpirvMajorVersion = ]] .. result.major_version .. [[; + constexpr UInt32 SpirvMinorVersion = ]] .. result.minor_version .. [[; + constexpr UInt32 SpirvRevision = ]] .. result.revision .. [[; + constexpr UInt32 SpirvVersion = (SpirvMajorVersion << 16) | (SpirvMinorVersion << 8); + +]]) + + -- SpirV operations + headerFile:write([[ + enum class SpirvOp + { +]]) + + for _, instruction in pairs(result.instructions) do + headerFile:write("\t\t" .. instruction.opname .. " = " .. instruction.opcode .. ",\n") + end + +headerFile:write([[ + }; + +]]) + + -- SpirV operands + headerFile:write([[ + enum class SpirvOperandKind + { +]]) + + for _, operand in pairs(result.operand_kinds) do + headerFile:write("\t\t" .. operand.kind .. ",\n") + end + + headerFile:write([[ + }; + +]]) + + -- SpirV enums + for _, operand in pairs(result.operand_kinds) do + if (operand.category == "ValueEnum" or operand.category == "BitEnum") then + local enumName = "Spirv" .. operand.kind + headerFile:write([[ + enum class ]] .. enumName .. [[ + + { +]]) + + local maxName + local maxValue + for _, enumerant in pairs(operand.enumerants) do + local value = enumerant.value + + local eName = enumerant.enumerant:match("^%d") and operand.kind .. enumerant.enumerant or enumerant.enumerant + headerFile:write([[ + ]] .. eName .. [[ = ]] .. value .. [[, +]]) + + if (not maxValue or value > maxValue) then + maxName = eName + end + end + + headerFile:write([[ + }; + +]]) + if (operand.category == "BitEnum") then + headerFile:write([[ + template<> + struct EnumAsFlags<]] .. enumName .. [[> + { + static constexpr ]] .. enumName .. [[ max = ]] .. enumName .. "::" .. maxName .. [[; + + static constexpr bool AutoFlag = false; + }; + + +]]) + end + end + end + + -- Struct + headerFile:write([[ + struct SpirvInstruction + { + struct Operand + { + SpirvOperandKind kind; + const char* name; + }; + + SpirvOp op; + const char* name; + const Operand* operands; + const Operand* resultOperand; + std::size_t minOperandCount; + }; + +]]) + + -- Functions signatures + headerFile:write([[ + NAZARA_SHADER_API const SpirvInstruction* GetInstructionData(UInt16 op); +]]) + +headerFile:write([[ +} + +#endif +]]) + + headerFile:close() + + local sourceFile = io.open("src/Nazara/Shader/SpirvData.cpp", "w+") + assert(sourceFile, "failed to open Spir-V source") + + sourceFile:write([[ +// Copyright (C) ]] .. os.date("%Y") .. [[ Jérôme Leclercq +// This file is part of the "Nazara Engine - Shader generator" +// For conditions of distribution and use, see copyright notice in Config.hpp" + +// This file was generated automatically, please do not edit + +#include +#include +#include +#include + +namespace Nz +{ + static constexpr std::array s_operands = { + { +]]) + for _, operand in pairs(operands) do + sourceFile:write([[ + { + SpirvOperandKind::]] .. operand.kind .. [[, + R"(]] .. (operand.name or operand.kind) .. [[)" + }, +]]) + end + + sourceFile:write([[ + } + }; + + static std::array s_instructions = { + { +]]) + + for _, instruction in pairs(instructions) do + local opByInstruction = operandByInstruction[instruction.opcode] + local resultId = opByInstruction and opByInstruction.resultId or nil + + sourceFile:write([[ + { + SpirvOp::]] .. instruction.opname .. [[, + R"(]] .. instruction.opname .. [[)", + ]] .. (opByInstruction and "&s_operands[" .. opByInstruction.firstId .. "]" or "nullptr") .. [[, + ]] .. (resultId and "&s_operands[" .. opByInstruction.firstId + resultId .. "]" or "nullptr") .. [[, + ]] .. (opByInstruction and opByInstruction.count or "0") .. [[, + }, +]]) + end + + sourceFile:write([[ + } + }; + +]]) + + -- Operand to string + sourceFile:write([[ + const SpirvInstruction* GetInstructionData(UInt16 op) + { + auto it = std::lower_bound(std::begin(s_instructions), std::end(s_instructions), op, [](const SpirvInstruction& inst, UInt16 op) { return UInt16(inst.op) < op; }); + if (it != std::end(s_instructions) && UInt16(it->op) == op) + return &*it; + else + return nullptr; + } +]]) + + sourceFile:write([[ +} +]]) + + sourceFile:close() + + print("Done") +end) diff --git a/xmake/actions/unicode.lua b/xmake/actions/unicode.lua new file mode 100644 index 000000000..d231f466c --- /dev/null +++ b/xmake/actions/unicode.lua @@ -0,0 +1,230 @@ +local unicodeDataURL = "https://www.unicode.org/Public/UCD/latest/ucd/UnicodeData.txt" + +task("update-unicode") + +set_menu({ + -- Settings menu usage + usage = "xmake update-unicode [options]", + description = "Download and parse the Unicode Character Data and updates the core module files with it" +}) + +on_run(function() + import("net.http") + +local CategoryToString = {} + CategoryToString["C"] = "Category_Other" + CategoryToString["Cc"] = "Category_Other_Control" + CategoryToString["Cf"] = "Category_Other_Format" + CategoryToString["Cn"] = "Category_Other_NotAssigned" + CategoryToString["Co"] = "Category_Other_PrivateUse" + CategoryToString["Cs"] = "Category_Other_Surrogate" + CategoryToString["L"] = "Category_Letter" + CategoryToString["Ll"] = "Category_Letter_Lowercase" + CategoryToString["Lm"] = "Category_Letter_Modifier" + CategoryToString["Lo"] = "Category_Letter_Other" + CategoryToString["Lt"] = "Category_Letter_Titlecase" + CategoryToString["Lu"] = "Category_Letter_Uppercase" + CategoryToString["M"] = "Category_Mark" + CategoryToString["Me"] = "Category_Mark_Enclosing" + CategoryToString["Mn"] = "Category_Mark_NonSpacing" + CategoryToString["Mc"] = "Category_Mark_SpacingCombining" + CategoryToString["N"] = "Category_Number" + CategoryToString["Nd"] = "Category_Number_DecimalDigit" + CategoryToString["Nl"] = "Category_Number_Letter" + CategoryToString["No"] = "Category_Number_Other" + CategoryToString["P"] = "Category_Punctuation" + CategoryToString["Pe"] = "Category_Punctuation_Close" + CategoryToString["Pc"] = "Category_Punctuation_Connector" + CategoryToString["Pd"] = "Category_Punctuation_Dash" + CategoryToString["Pf"] = "Category_Punctuation_FinalQuote" + CategoryToString["Pi"] = "Category_Punctuation_InitialQuote" + CategoryToString["Ps"] = "Category_Punctuation_Open" + CategoryToString["Po"] = "Category_Punctuation_Other" + CategoryToString["S"] = "Category_Symbol" + CategoryToString["Sc"] = "Category_Symbol_Currency" + CategoryToString["Sm"] = "Category_Symbol_Math" + CategoryToString["Sk"] = "Category_Symbol_Modifier" + CategoryToString["So"] = "Category_Symbol_Other" + CategoryToString["Z"] = "Category_Separator" + CategoryToString["Zl"] = "Category_Separator_Line" + CategoryToString["Zp"] = "Category_Separator_Paragraph" + CategoryToString["Zs"] = "Category_Separator_Space" + +local DirectionToString = {} + DirectionToString["AL"] = "Direction_Arabic_Letter" + DirectionToString["AN"] = "Direction_Arabic_Number" + DirectionToString["BN"] = "Direction_Boundary_Neutral" + DirectionToString["CS"] = "Direction_Common_Separator" + DirectionToString["EN"] = "Direction_European_Number" + DirectionToString["ES"] = "Direction_European_Separator" + DirectionToString["ET"] = "Direction_European_Terminator" + DirectionToString["FSI"] = "Direction_First_Strong_Isolate" + DirectionToString["L"] = "Direction_Left_To_Right" + DirectionToString["LRE"] = "Direction_Left_To_Right_Embedding" + DirectionToString["LRI"] = "Direction_Left_To_Right_Isolate" + DirectionToString["LRO"] = "Direction_Left_To_Right_Override" + DirectionToString["NSM"] = "Direction_Nonspacing_Mark" + DirectionToString["ON"] = "Direction_Other_Neutral" + DirectionToString["B"] = "Direction_Paragraph_Separator" + DirectionToString["PDF"] = "Direction_Pop_Directional_Formatting" + DirectionToString["PDI"] = "Direction_Pop_Directional_Isolate" + DirectionToString["R"] = "Direction_Right_To_Left" + DirectionToString["RLE"] = "Direction_Right_To_Left_Embedding" + DirectionToString["RLI"] = "Direction_Right_To_Left_Isolate" + DirectionToString["RLO"] = "Direction_Right_To_Left_Override" + DirectionToString["S"] = "Direction_Segment_Separator" + DirectionToString["WS"] = "Direction_White_Space" + + local unicodeSet = {} + + io.write("Downloading UnicodeData grammar... ") + + local tempUnicodeFile = os.tmpfile() .. ".UnicodeData.txt" + + http.download(unicodeDataURL, tempUnicodeFile) + + print("Done") + + io.write("Parsing... ") + + local file = io.open(tempUnicodeFile, "r") + + local characters = {} + local characterSets = {} + local lowercaseCharacters = {} + local titlecaseCharacters = {} + local uppercaseCharacters = {} + local currentBlock + local currentBlockStartCodepoint + local lineIndex = 1 + + for line in file:lines() do + local parts = line:split(";", {strict = true}) + + local codepoint = tonumber(parts[1], 16) + local characterName = parts[2] + local category = parts[3] + local direction = parts[5] + local uppercaseMapping = tonumber(parts[13], 16) + local lowercaseMapping = tonumber(parts[14], 16) + local titlecaseMapping = tonumber(parts[15], 16) + + local blockName, blockId = string.match(characterName, "<(.+), (%w+)>") + if (currentBlock) then + if (blockId ~= "Last") then + error("Parsing error: expected last block at line " .. lineIndex) + end + + print("Detected set " .. blockName .. " from codepoint " .. currentBlockStartCodepoint .. " to " .. codepoint) + + table.insert(characterSets, { + startCodepoint = currentBlockStartCodepoint, + endCodepoint = codepoint, + name = "<" .. blockName .. ">", + category = category, + direction = direction + }) + + currentBlock = nil + else + if (blockName) then + if (blockId ~= "First") then + error("Parsing error: expected first block at line " .. lineIndex) + end + + currentBlock = blockName + currentBlockStartCodepoint = codepoint + else + table.insert(characters, { + codepoint = codepoint, + name = characterName, + category = category, + direction = direction, + upper = uppercaseMapping, + lower = lowercaseMapping, + title = titlecaseMapping + }) + + if (lowercaseMapping) then + table.insert(lowercaseCharacters, {codepoint = codepoint, lower = lowercaseMapping}) + end + + if (titlecaseMapping) then + table.insert(titlecaseCharacters, {codepoint = codepoint, title = titlecaseMapping}) + end + + if (uppercaseMapping) then + table.insert(uppercaseCharacters, {codepoint = codepoint, upper = uppercaseMapping}) + end + end + end + + lineIndex = lineIndex + 1 + end + + print("Parsed " .. #characters .. " characters") + + print("Writting Unicode Data to header...") + + file = io.open("src/Nazara/Core/UnicodeData.hpp", "w+") + if (not file) then + error("Failed to open Unicode Data header") + return + end + + file:write(string.format("UnicodeCharacter unicodeCharacters[%d] = {\n", #characters)) + + for _, data in pairs(characters) do + local category = CategoryToString[data.category] + if (not category) then + error("Unknown category " .. data.category .. " for character " .. data.codepoint) + end + + local direction = DirectionToString[data.direction] + if (not direction) then + error("Unknown direction " .. data.direction .. " for character " .. data.codepoint) + end + + file:write(string.format("\t{%d, Unicode::%s, Unicode::%s},\n", data.codepoint, category, direction)) + end + file:write("};\n\n") + + file:write(string.format("UnicodeSet unicodeSets[%d] = {\n", #characterSets)) + + for _, data in pairs(characterSets) do + local category = CategoryToString[data.category] + if (not category) then + error("Unknown category " .. data.category .. " for character " .. data.codepoint) + end + + local direction = DirectionToString[data.direction] + if (not direction) then + error("Unknown direction " .. data.direction .. " for character " .. data.codepoint) + end + + file:write(string.format("\t{%d, %d, {%d, Unicode::%s, Unicode::%s}},\n", data.startCodepoint, data.endCodepoint, data.startCodepoint, category, direction)) + end + file:write("};\n\n") + + file:write(string.format("UnicodeCharacterSimpleMapping unicodeLower[%d] = {\n", #lowercaseCharacters)) + for _, data in pairs(lowercaseCharacters) do + file:write(string.format("\t{%d, %d},\n", data.codepoint, data.lower)) + end + file:write("};\n\n") + + file:write(string.format("UnicodeCharacterSimpleMapping unicodeTitle[%d] = {\n", #titlecaseCharacters)) + for _, data in pairs(titlecaseCharacters) do + file:write(string.format("\t{%d, %d},\n", data.codepoint, data.title)) + end + file:write("};\n\n") + + file:write(string.format("UnicodeCharacterSimpleMapping unicodeUpper[%d] = {\n", #uppercaseCharacters)) + for _, data in pairs(uppercaseCharacters) do + file:write(string.format("\t{%d, %d},\n", data.codepoint, data.upper)) + end + file:write("};\n\n") + + file:close() + + print("Succeeded!") +end)