Path: blob/master/servers/rendering/rendering_device_binds.cpp
20831 views
/**************************************************************************/1/* rendering_device_binds.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "rendering_device_binds.h"3132#include "modules/modules_enabled.gen.h" // For glslang.33#ifdef MODULE_GLSLANG_ENABLED34#include "modules/glslang/shader_compile.h"35#endif3637#include "shader_include_db.h"3839Error RDShaderFile::parse_versions_from_text(const String &p_text, const String p_defines, OpenIncludeFunction p_include_func, void *p_include_func_userdata) {40Vector<String> lines = p_text.split("\n");4142bool reading_versions = false;43bool stage_found[RD::SHADER_STAGE_MAX] = {};44RD::ShaderStage stage = RD::SHADER_STAGE_MAX;45static const char *stage_str[RD::SHADER_STAGE_MAX] = {46"vertex",47"fragment",48"tesselation_control",49"tesselation_evaluation",50"compute",51"raygen",52"any_hit",53"closest_hit",54"miss",55"intersection",56};57String stage_code[RD::SHADER_STAGE_MAX];58int stages_found = 0;59HashMap<StringName, String> version_texts;6061versions.clear();62base_error = "";6364for (int lidx = 0; lidx < lines.size(); lidx++) {65String line = lines[lidx];6667{68String ls = line.strip_edges();69if (ls.begins_with("#[") && ls.ends_with("]")) {70String section = ls.substr(2, ls.length() - 3).strip_edges();71if (section == "versions") {72if (stages_found) {73base_error = "Invalid shader file, #[versions] must be the first section found.";74break;75}76reading_versions = true;77} else {78for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) {79if (section == stage_str[i]) {80if (stage_found[i]) {81base_error = "Invalid shader file, stage appears twice: " + section;82break;83}8485stage_found[i] = true;86stages_found++;8788stage = RD::ShaderStage(i);89reading_versions = false;90break;91}92}9394if (!base_error.is_empty()) {95break;96}97}9899continue;100}101}102103if (stage == RD::SHADER_STAGE_MAX && !line.strip_edges().is_empty()) {104line = line.strip_edges();105if (line.begins_with("//") || line.begins_with("/*")) {106continue; //assuming comment (single line)107}108}109110if (reading_versions) {111String l = line.strip_edges();112if (!l.is_empty()) {113if (!l.contains_char('=')) {114base_error = "Missing `=` in '" + l + "'. Version syntax is `version = \"<defines with C escaping>\";`.";115break;116}117if (!l.contains_char(';')) {118// We don't require a semicolon per se, but it's needed for clang-format to handle things properly.119base_error = "Missing `;` in '" + l + "'. Version syntax is `version = \"<defines with C escaping>\";`.";120break;121}122Vector<String> slices = l.get_slicec(';', 0).split("=");123String version = slices[0].strip_edges();124if (!version.is_valid_ascii_identifier()) {125base_error = "Version names must be valid identifiers, found '" + version + "' instead.";126break;127}128String define = slices[1].strip_edges();129if (!define.begins_with("\"") || !define.ends_with("\"")) {130base_error = "Version text must be quoted using \"\", instead found '" + define + "'.";131break;132}133define = "\n" + define.substr(1, define.length() - 2).c_unescape() + "\n"; // Add newline before and after just in case.134135version_texts[version] = define + "\n" + p_defines;136}137} else {138if (stage == RD::SHADER_STAGE_MAX && !line.strip_edges().is_empty()) {139base_error = "Text was found that does not belong to a valid section: " + line;140break;141}142143if (stage != RD::SHADER_STAGE_MAX) {144if (line.strip_edges().begins_with("#include")) {145if (p_include_func) {146//process include147String include = line.replace("#include", "").strip_edges();148if (!include.begins_with("\"") || !include.ends_with("\"")) {149base_error = "Malformed #include syntax, expected #include \"<path>\", found instead: " + include;150break;151}152include = include.substr(1, include.length() - 2).strip_edges();153154String include_code = ShaderIncludeDB::get_built_in_include_file(include);155if (!include_code.is_empty()) {156stage_code[stage] += "\n" + include_code + "\n";157} else {158String include_text = p_include_func(include, p_include_func_userdata);159if (!include_text.is_empty()) {160stage_code[stage] += "\n" + include_text + "\n";161} else {162base_error = "#include failed for file '" + include + "'.";163}164}165} else {166base_error = "#include used, but no include function provided.";167}168} else {169stage_code[stage] += line + "\n";170}171}172}173}174175if (base_error.is_empty()) {176if (stage_found[RD::SHADER_STAGE_COMPUTE] && stages_found > 1) {177ERR_FAIL_V_MSG(ERR_PARSE_ERROR, "When writing compute shaders, [compute] mustbe the only stage present.");178}179180if (version_texts.is_empty()) {181version_texts[""] = ""; //make sure a default version exists182}183184bool errors_found = false;185186/* STEP 2, Compile the versions, add to shader file */187188for (const KeyValue<StringName, String> &E : version_texts) {189Ref<RDShaderSPIRV> bytecode;190bytecode.instantiate();191192for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) {193String code = stage_code[i];194if (code.is_empty()) {195continue;196}197code = code.replace("VERSION_DEFINES", E.value);198String error;199#ifdef MODULE_GLSLANG_ENABLED200Vector<uint8_t> spirv = compile_glslang_shader(RD::ShaderStage(i), ShaderIncludeDB::parse_include_files(code), RD::SHADER_LANGUAGE_VULKAN_VERSION_1_1, RD::SHADER_SPIRV_VERSION_1_4, &error);201bytecode->set_stage_bytecode(RD::ShaderStage(i), spirv);202#else203error = "Shader compilation is not supported because glslang was not enabled.";204#endif205if (!error.is_empty()) {206error += String() + "\n\nStage '" + stage_str[i] + "' source code: \n\n";207Vector<String> sclines = code.split("\n");208for (int j = 0; j < sclines.size(); j++) {209error += itos(j + 1) + "\t\t" + sclines[j] + "\n";210}211errors_found = true;212}213bytecode->set_stage_compile_error(RD::ShaderStage(i), error);214}215216set_bytecode(bytecode, E.key);217}218219return errors_found ? ERR_PARSE_ERROR : OK;220} else {221return ERR_PARSE_ERROR;222}223}224225226