Path: blob/master/servers/rendering/rendering_device_binds.cpp
10277 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] = { false, false, false, false, false };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};52String stage_code[RD::SHADER_STAGE_MAX];53int stages_found = 0;54HashMap<StringName, String> version_texts;5556versions.clear();57base_error = "";5859for (int lidx = 0; lidx < lines.size(); lidx++) {60String line = lines[lidx];6162{63String ls = line.strip_edges();64if (ls.begins_with("#[") && ls.ends_with("]")) {65String section = ls.substr(2, ls.length() - 3).strip_edges();66if (section == "versions") {67if (stages_found) {68base_error = "Invalid shader file, #[versions] must be the first section found.";69break;70}71reading_versions = true;72} else {73for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) {74if (section == stage_str[i]) {75if (stage_found[i]) {76base_error = "Invalid shader file, stage appears twice: " + section;77break;78}7980stage_found[i] = true;81stages_found++;8283stage = RD::ShaderStage(i);84reading_versions = false;85break;86}87}8889if (!base_error.is_empty()) {90break;91}92}9394continue;95}96}9798if (stage == RD::SHADER_STAGE_MAX && !line.strip_edges().is_empty()) {99line = line.strip_edges();100if (line.begins_with("//") || line.begins_with("/*")) {101continue; //assuming comment (single line)102}103}104105if (reading_versions) {106String l = line.strip_edges();107if (!l.is_empty()) {108if (!l.contains_char('=')) {109base_error = "Missing `=` in '" + l + "'. Version syntax is `version = \"<defines with C escaping>\";`.";110break;111}112if (!l.contains_char(';')) {113// We don't require a semicolon per se, but it's needed for clang-format to handle things properly.114base_error = "Missing `;` in '" + l + "'. Version syntax is `version = \"<defines with C escaping>\";`.";115break;116}117Vector<String> slices = l.get_slicec(';', 0).split("=");118String version = slices[0].strip_edges();119if (!version.is_valid_ascii_identifier()) {120base_error = "Version names must be valid identifiers, found '" + version + "' instead.";121break;122}123String define = slices[1].strip_edges();124if (!define.begins_with("\"") || !define.ends_with("\"")) {125base_error = "Version text must be quoted using \"\", instead found '" + define + "'.";126break;127}128define = "\n" + define.substr(1, define.length() - 2).c_unescape() + "\n"; // Add newline before and after just in case.129130version_texts[version] = define + "\n" + p_defines;131}132} else {133if (stage == RD::SHADER_STAGE_MAX && !line.strip_edges().is_empty()) {134base_error = "Text was found that does not belong to a valid section: " + line;135break;136}137138if (stage != RD::SHADER_STAGE_MAX) {139if (line.strip_edges().begins_with("#include")) {140if (p_include_func) {141//process include142String include = line.replace("#include", "").strip_edges();143if (!include.begins_with("\"") || !include.ends_with("\"")) {144base_error = "Malformed #include syntax, expected #include \"<path>\", found instead: " + include;145break;146}147include = include.substr(1, include.length() - 2).strip_edges();148149String include_code = ShaderIncludeDB::get_built_in_include_file(include);150if (!include_code.is_empty()) {151stage_code[stage] += "\n" + include_code + "\n";152} else {153String include_text = p_include_func(include, p_include_func_userdata);154if (!include_text.is_empty()) {155stage_code[stage] += "\n" + include_text + "\n";156} else {157base_error = "#include failed for file '" + include + "'.";158}159}160} else {161base_error = "#include used, but no include function provided.";162}163} else {164stage_code[stage] += line + "\n";165}166}167}168}169170if (base_error.is_empty()) {171if (stage_found[RD::SHADER_STAGE_COMPUTE] && stages_found > 1) {172ERR_FAIL_V_MSG(ERR_PARSE_ERROR, "When writing compute shaders, [compute] mustbe the only stage present.");173}174175if (version_texts.is_empty()) {176version_texts[""] = ""; //make sure a default version exists177}178179bool errors_found = false;180181/* STEP 2, Compile the versions, add to shader file */182183for (const KeyValue<StringName, String> &E : version_texts) {184Ref<RDShaderSPIRV> bytecode;185bytecode.instantiate();186187for (int i = 0; i < RD::SHADER_STAGE_MAX; i++) {188String code = stage_code[i];189if (code.is_empty()) {190continue;191}192code = code.replace("VERSION_DEFINES", E.value);193String error;194#ifdef MODULE_GLSLANG_ENABLED195Vector<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_3, &error);196bytecode->set_stage_bytecode(RD::ShaderStage(i), spirv);197#else198error = "Shader compilation is not supported because glslang was not enabled.";199#endif200if (!error.is_empty()) {201error += String() + "\n\nStage '" + stage_str[i] + "' source code: \n\n";202Vector<String> sclines = code.split("\n");203for (int j = 0; j < sclines.size(); j++) {204error += itos(j + 1) + "\t\t" + sclines[j] + "\n";205}206errors_found = true;207}208bytecode->set_stage_compile_error(RD::ShaderStage(i), error);209}210211set_bytecode(bytecode, E.key);212}213214return errors_found ? ERR_PARSE_ERROR : OK;215} else {216return ERR_PARSE_ERROR;217}218}219220221