//============================================================================== static ANKI_USE_RESULT Error xmlVec3( const XmlElement& el_, const CString& str, Vec3& out) { Error err = ErrorCode::NONE; XmlElement el; err = el_.getChildElementOptional(str, el); if(err || !el) { return err; } err = el.getVec3(out); return err; }
//============================================================================== static ANKI_USE_RESULT Error xmlU32( const XmlElement& el_, const CString& str, U32& out) { Error err = ErrorCode::NONE; XmlElement el; err = el_.getChildElementOptional(str, el); if(err || !el) { return err; } I64 tmp; err = el.getI64(tmp); if(!err) { out = static_cast<U32>(tmp); } return err; }
//============================================================================== Error Material::parseMaterialTag(const XmlElement& materialEl, ResourceInitializer& rinit) { Error err = ErrorCode::NONE; XmlElement el; // levelsOfDetail // XmlElement lodEl; ANKI_CHECK(materialEl.getChildElementOptional("levelsOfDetail", lodEl)); if(lodEl) { I64 tmp; ANKI_CHECK(lodEl.getI64(tmp)); m_lodsCount = (tmp < 1) ? 1 : tmp; } else { m_lodsCount = 1; } // shadow // XmlElement shadowEl; ANKI_CHECK(materialEl.getChildElementOptional("shadow", shadowEl)); if(shadowEl) { I64 tmp; ANKI_CHECK(shadowEl.getI64(tmp)); m_shadow = tmp; } // blendFunctions // XmlElement blendFunctionsEl; ANKI_CHECK( materialEl.getChildElementOptional("blendFunctions", blendFunctionsEl)); if(blendFunctionsEl) { CString cstr; // sFactor ANKI_CHECK(blendFunctionsEl.getChildElement("sFactor", el)); ANKI_CHECK(el.getText(cstr)); m_blendingSfactor = blendToEnum(cstr); if(m_blendingSfactor == 0) { return ErrorCode::USER_DATA; } // dFactor ANKI_CHECK(blendFunctionsEl.getChildElement("dFactor", el)); ANKI_CHECK(el.getText(cstr)); m_blendingDfactor = blendToEnum(cstr); if(m_blendingDfactor == 0) { return ErrorCode::USER_DATA; } } else { m_passesCount = 2; } // depthTesting // XmlElement depthTestingEl; ANKI_CHECK( materialEl.getChildElementOptional("depthTesting", depthTestingEl)); if(depthTestingEl) { I64 tmp; ANKI_CHECK(depthTestingEl.getI64(tmp)); m_depthTesting = tmp; } // wireframe // XmlElement wireframeEl; ANKI_CHECK(materialEl.getChildElementOptional("wireframe", wireframeEl)); if(wireframeEl) { I64 tmp; ANKI_CHECK(wireframeEl.getI64(tmp)); m_wireframe = tmp; } // shaderProgram // ANKI_CHECK(materialEl.getChildElement("programs", el)); MaterialProgramCreator loader(rinit.m_tempAlloc); ANKI_CHECK(loader.parseProgramsTag(el)); m_tessellation = loader.hasTessellation(); U tessCount = m_tessellation ? 2 : 1; // Alloc program vector ANKI_CHECK(m_progs.create(rinit.m_alloc, countShaders(ShaderType::VERTEX) + countShaders(ShaderType::TESSELLATION_CONTROL) + countShaders(ShaderType::TESSELLATION_EVALUATION) + countShaders(ShaderType::GEOMETRY) + countShaders(ShaderType::FRAGMENT))); // Aloc progam descriptors m_pplines.resize(m_passesCount * m_lodsCount * tessCount); m_hash = 0; for(ShaderType shader = ShaderType::VERTEX; shader <= ShaderType::FRAGMENT; ++shader) { Bool isTessellationShader = shader == ShaderType::TESSELLATION_CONTROL || shader == ShaderType::TESSELLATION_EVALUATION; if(!m_tessellation && isTessellationShader) { // Skip tessellation if not enabled continue; } if(shader == ShaderType::GEOMETRY) { // Skip geometry for now continue; } for(U level = 0; level < m_lodsCount; ++level) { if(level > 0 && isTessellationShader) { continue; } for(U pid = 0; pid < m_passesCount; ++pid) { for(U tess = 0; tess < tessCount; ++tess) { if(tess == 0 && isTessellationShader) { continue; } if(tess > 0 && shader == ShaderType::FRAGMENT) { continue; } TempResourceString src; TempResourceString::ScopeDestroyer srcd( &src, rinit.m_tempAlloc); ANKI_CHECK(src.sprintf( rinit.m_tempAlloc, "%s\n" "#define LOD %u\n" "#define PASS %u\n" "#define TESSELLATION %u\n" "%s\n", &rinit.m_resources._getShadersPrependedSource()[0], level, pid, tess, &loader.getProgramSource(shader)[0])); TempResourceString filename; TempResourceString::ScopeDestroyer filenamed( &filename, rinit.m_tempAlloc); ANKI_CHECK(createProgramSourceToCache(src, filename)); RenderingKey key((Pass)pid, level, tess); ProgramResourcePointer& progr = getProgram(key, shader); ANKI_CHECK( progr.load(filename.toCString(), &rinit.m_resources)); // Update the hash m_hash ^= computeHash(&src[0], src.getLength()); } } } } ANKI_CHECK(populateVariables(loader)); // Get uniform block size ANKI_ASSERT(m_progs.getSize() > 0); m_shaderBlockSize = loader.getUniformBlockSize(); return err; }
//============================================================================== void MaterialProgramCreator::parseOperationTag( const XmlElement& operationTag, GLenum glshader, GLbitfield glshaderbit, MPString& out) { static const char OUT[] = {"out"}; // <id></id> I id = operationTag.getChildElement("id").getInt(); // <returnType></returnType> XmlElement retTypeEl = operationTag.getChildElement("returnType"); MPString retType(retTypeEl.getText(), m_alloc); MPString operationOut(m_alloc); if(retType != "void") { MPString tmp(MPString::toString(id, m_alloc)); operationOut = ANKI_STRL(OUT) + tmp; } // <function>functionName</function> MPString funcName( operationTag.getChildElement("function").getText(), m_alloc); // <arguments></arguments> XmlElement argsEl = operationTag.getChildElementOptional("arguments"); MPStringList argsList(m_alloc); if(argsEl) { // Get all arguments XmlElement argEl = argsEl.getChildElement("argument"); do { MPString arg(argEl.getText(), m_alloc); // Search for all the inputs and mark the appropriate Input* input = nullptr; for(Input& in : m_inputs) { // Check that the first part of the string is equal to the // variable and the following char is '[' if(in.m_name == arg) { input = ∈ in.m_shaderReferencedMask = glshaderbit; break; } } // The argument should be an input variable or an outXX if(!(input != nullptr || std::strncmp(&arg[0], OUT, sizeof(OUT) - 1) == 0)) { throw ANKI_EXCEPTION("Incorrect argument: %s", &arg[0]); } // Add to a list and do something special if instanced if(input && input->m_instanced) { if(glshader == GL_VERTEX_SHADER) { argsList.push_back(ANKI_STRL(argEl.getText()) + "[gl_InstanceID]"); m_instanceIdMask |= glshaderbit; } else if(glshader == GL_TESS_CONTROL_SHADER) { argsList.push_back(ANKI_STRL(argEl.getText()) + "[vInstanceId[0]]"); m_instanceIdMask |= glshaderbit; } else if(glshader == GL_TESS_EVALUATION_SHADER) { argsList.push_back(ANKI_STRL(argEl.getText()) + "[commonPatch.instanceId]"); m_instanceIdMask |= glshaderbit; } else if(glshader == GL_FRAGMENT_SHADER) { argsList.push_back(ANKI_STRL(argEl.getText()) + "[vInstanceId]"); m_instanceIdMask |= glshaderbit; } else { throw ANKI_EXCEPTION( "Cannot access the instance ID in all shaders"); } } else { argsList.push_back(MPString(argEl.getText(), m_alloc)); } // Advance argEl = argEl.getNextSiblingElement("argument"); } while(argEl); } // Now write everything MPString lines(m_alloc); lines.reserve(256); lines += "#if defined(" + funcName + "_DEFINED)"; // Write the defines for the operationOuts for(const MPString& arg : argsList) { if(arg.find(OUT) == 0) { lines += " && defined(" + arg + "_DEFINED)"; } } lines += "\n"; if(retType != "void") { lines += "#\tdefine " + operationOut + "_DEFINED\n\t" + retTypeEl.getText() + " " + operationOut + " = "; } else { lines += "\t"; } // write the blah = func(args...) lines += funcName + "("; lines += argsList.join(", "); lines += ");\n"; lines += "#endif"; // Done out = std::move(lines); }
//============================================================================== void MaterialProgramCreator::parseInputsTag(const XmlElement& programEl) { XmlElement inputsEl = programEl.getChildElementOptional("inputs"); if(!inputsEl) { return; } // Get shader type GLbitfield glshaderbit; GLenum glshader; U shaderidx; getShaderInfo( programEl.getChildElement("type").getText(), glshader, glshaderbit, shaderidx); XmlElement inputEl = inputsEl.getChildElement("input"); do { Input inpvar(m_alloc); // <name> inpvar.m_name = inputEl.getChildElement("name").getText(); // <type> inpvar.m_type = inputEl.getChildElement("type").getText(); // <value> XmlElement valueEl = inputEl.getChildElement("value"); if(valueEl.getText()) { inpvar.m_value = MPStringList::splitString( valueEl.getText(), ' ', m_alloc); } // <const> XmlElement constEl = inputEl.getChildElementOptional("const"); inpvar.m_constant = (constEl) ? constEl.getInt() : false; // <arraySize> XmlElement arrSizeEl = inputEl.getChildElementOptional("arraySize"); inpvar.m_arraySize = (arrSizeEl) ? arrSizeEl.getInt() : 0; // <instanced> if(inpvar.m_arraySize == 0) { XmlElement instancedEl = inputEl.getChildElementOptional("instanced"); inpvar.m_instanced = (instancedEl) ? instancedEl.getInt() : 0; // If one input var is instanced notify the whole program that // it's instanced if(inpvar.m_instanced) { m_instanced = true; } } // Now you have the info to check if duplicate Input* duplicateInp = nullptr; for(Input& in : m_inputs) { if(in.m_name == inpvar.m_name) { duplicateInp = ∈ break; } } if(duplicateInp != nullptr) { // Duplicate. Make sure it's the same as the other shader Bool same = duplicateInp->m_type == inpvar.m_type || duplicateInp->m_value == inpvar.m_value || duplicateInp->m_constant == inpvar.m_constant || duplicateInp->m_arraySize == inpvar.m_arraySize || duplicateInp->m_instanced == inpvar.m_instanced; if(!same) { throw ANKI_EXCEPTION("Variable defined differently between " "shaders: %s", &inpvar.m_name[0]); } duplicateInp->m_shaderDefinedMask |= glshaderbit; goto advance; } if(inpvar.m_constant == false) { // Handle NON-consts inpvar.m_line = inpvar.m_type + " " + inpvar.m_name; if(inpvar.m_arraySize > 1) { MPString tmp(MPString::toString(inpvar.m_arraySize, m_alloc)); inpvar.m_line += "[" + tmp + "U]"; } if(inpvar.m_instanced) { MPString tmp( MPString::toString(ANKI_GL_MAX_INSTANCES, m_alloc)); inpvar.m_line += "[" + tmp + "U]"; } inpvar.m_line += ";"; // Can put it block if(inpvar.m_type == "sampler2D" || inpvar.m_type == "samplerCube") { MPString tmp( MPString::toString(m_texBinding++, m_alloc)); inpvar.m_line = ANKI_STRL("layout(binding = ") + tmp + ") uniform " + inpvar.m_line; inpvar.m_inBlock = false; } else { inpvar.m_inBlock = true; m_uniformBlock.push_back(inpvar.m_line); m_uniformBlockReferencedMask |= glshaderbit; } } else { // Handle consts if(inpvar.m_value.size() == 0) { throw ANKI_EXCEPTION("Empty value and const is illogical"); } if(inpvar.m_arraySize > 0) { throw ANKI_EXCEPTION("Const arrays currently cannot " "be handled"); } inpvar.m_inBlock = false; inpvar.m_line = ANKI_STRL("const ") + inpvar.m_type + " " + inpvar.m_name + " = " + inpvar.m_type + "(" + inpvar.m_value.join(", ") + ");"; } inpvar.m_shaderDefinedMask = glshaderbit; m_inputs.push_back(inpvar); advance: // Advance inputEl = inputEl.getNextSiblingElement("input"); } while(inputEl); }
//============================================================================== Error Animation::load(const ResourceFilename& filename) { XmlElement el; I64 tmp; F64 ftmp; m_startTime = MAX_F32; F32 maxTime = MIN_F32; // Document XmlDocument doc; ANKI_CHECK(openFileParseXml(filename, doc)); XmlElement rootel; ANKI_CHECK(doc.getChildElement("animation", rootel)); // Count the number of identity keys. If all of the keys are identities // drop a vector U identPosCount = 0; U identRotCount = 0; U identScaleCount = 0; // <repeat> XmlElement repel; ANKI_CHECK(rootel.getChildElementOptional("repeat", repel)); if(repel) { ANKI_CHECK(repel.getI64(tmp)); m_repeat = tmp; } else { m_repeat = false; } // <channels> XmlElement channelsEl; ANKI_CHECK(rootel.getChildElement("channels", channelsEl)); XmlElement chEl; ANKI_CHECK(channelsEl.getChildElement("channel", chEl)); U32 channelCount = 0; ANKI_CHECK(chEl.getSiblingElementsCount(channelCount)); if(channelCount == 0) { ANKI_LOGE("Didn't found any channels"); return ErrorCode::USER_DATA; } m_channels.create(getAllocator(), channelCount); // For all channels channelCount = 0; do { AnimationChannel& ch = m_channels[channelCount]; // <name> ANKI_CHECK(chEl.getChildElement("name", el)); CString strtmp; ANKI_CHECK(el.getText(strtmp)); ch.m_name.create(getAllocator(), strtmp); XmlElement keysEl, keyEl; // <positionKeys> ANKI_CHECK(chEl.getChildElementOptional("positionKeys", keysEl)); if(keysEl) { ANKI_CHECK(keysEl.getChildElement("key", keyEl)); U32 count = 0; ANKI_CHECK(keyEl.getSiblingElementsCount(count)); ch.m_positions.create(getAllocator(), count); count = 0; do { Key<Vec3>& key = ch.m_positions[count++]; // <time> ANKI_CHECK(keyEl.getChildElement("time", el)); ANKI_CHECK(el.getF64(ftmp)); key.m_time = ftmp; m_startTime = std::min(m_startTime, key.m_time); maxTime = std::max(maxTime, key.m_time); // <value> ANKI_CHECK(keyEl.getChildElement("value", el)); ANKI_CHECK(el.getVec3(key.m_value)); // Check ident if(key.m_value == Vec3(0.0)) { ++identPosCount; } // Move to next ANKI_CHECK(keyEl.getNextSiblingElement("key", keyEl)); } while(keyEl); } // <rotationKeys> ANKI_CHECK(chEl.getChildElement("rotationKeys", keysEl)); if(keysEl) { ANKI_CHECK(keysEl.getChildElement("key", keyEl)); U32 count = 0; ANKI_CHECK(keysEl.getSiblingElementsCount(count)); ch.m_rotations.create(getAllocator(), count); count = 0; do { Key<Quat>& key = ch.m_rotations[count++]; // <time> ANKI_CHECK(keyEl.getChildElement("time", el)); ANKI_CHECK(el.getF64(ftmp)); key.m_time = ftmp; m_startTime = std::min(m_startTime, key.m_time); maxTime = std::max(maxTime, key.m_time); // <value> Vec4 tmp2; ANKI_CHECK(keyEl.getChildElement("value", el)); ANKI_CHECK(el.getVec4(tmp2)); key.m_value = Quat(tmp2); // Check ident if(key.m_value == Quat::getIdentity()) { ++identRotCount; } // Move to next ANKI_CHECK(keyEl.getNextSiblingElement("key", keyEl)); } while(keyEl); } // <scalingKeys> ANKI_CHECK(chEl.getChildElementOptional("scalingKeys", keysEl)); if(keysEl) { ANKI_CHECK(keysEl.getChildElement("key", keyEl)); U32 count = 0; ANKI_CHECK(keyEl.getSiblingElementsCount(count)); ch.m_scales.create(getAllocator(), count); count = 0; do { Key<F32>& key = ch.m_scales[count++]; // <time> ANKI_CHECK(keyEl.getChildElement("time", el)); ANKI_CHECK(el.getF64(ftmp)); key.m_time = ftmp; m_startTime = std::min(m_startTime, key.m_time); maxTime = std::max(maxTime, key.m_time); // <value> ANKI_CHECK(keyEl.getChildElement("value", el)); ANKI_CHECK(el.getF64(ftmp)); key.m_value = ftmp; // Check ident if(isZero(key.m_value - 1.0)) { ++identScaleCount; } // Move to next ANKI_CHECK(keyEl.getNextSiblingElement("key", keyEl)); } while(keyEl); } // Remove identity vectors if(identPosCount == ch.m_positions.getSize()) { ch.m_positions.destroy(getAllocator()); } if(identRotCount == ch.m_rotations.getSize()) { ch.m_rotations.destroy(getAllocator()); } if(identScaleCount == ch.m_scales.getSize()) { ch.m_scales.destroy(getAllocator()); } // Move to next channel ++channelCount; ANKI_CHECK(chEl.getNextSiblingElement("channel", chEl)); } while(chEl); m_duration = maxTime - m_startTime; return ErrorCode::NONE; }
//============================================================================== void Material::parseMaterialTag(const XmlElement& materialEl, ResourceInitializer& rinit) { // levelsOfDetail // XmlElement lodEl = materialEl.getChildElementOptional("levelsOfDetail"); if(lodEl) { I tmp = lodEl.getInt(); m_lodsCount = (tmp < 1) ? 1 : tmp; } else { m_lodsCount = 1; } // shadow // XmlElement shadowEl = materialEl.getChildElementOptional("shadow"); if(shadowEl) { m_shadow = shadowEl.getInt(); } // blendFunctions // XmlElement blendFunctionsEl = materialEl.getChildElementOptional("blendFunctions"); if(blendFunctionsEl) { // sFactor m_blendingSfactor = blendToEnum( blendFunctionsEl.getChildElement("sFactor").getText()); // dFactor m_blendingDfactor = blendToEnum( blendFunctionsEl.getChildElement("dFactor").getText()); } else { m_passesCount = 2; } // depthTesting // XmlElement depthTestingEl = materialEl.getChildElementOptional("depthTesting"); if(depthTestingEl) { m_depthTesting = depthTestingEl.getInt(); } // wireframe // XmlElement wireframeEl = materialEl.getChildElementOptional("wireframe"); if(wireframeEl) { m_wireframe = wireframeEl.getInt(); } // shaderProgram // MaterialProgramCreator mspc( materialEl.getChildElement("programs"), rinit.m_tempAlloc); m_tessellation = mspc.hasTessellation(); U tessCount = m_tessellation ? 2 : 1; // Alloc program vector U progCount = 0; progCount += m_passesCount * m_lodsCount * tessCount; if(m_tessellation) { progCount += m_passesCount * m_lodsCount * 2; } progCount += m_passesCount * m_lodsCount; m_progs.resize(progCount); // Aloc progam descriptors m_pplines.resize(m_passesCount * m_lodsCount * tessCount); m_hash = 0; for(U shader = 0; shader < 5; shader++) { if(!m_tessellation && (shader == 1 || shader == 2)) { continue; } if(shader == 3) { continue; } for(U level = 0; level < m_lodsCount; ++level) { for(U pid = 0; pid < m_passesCount; ++pid) { for(U tess = 0; tess < tessCount; ++tess) { TempResourceString src(rinit.m_tempAlloc); src.sprintf("#define LOD %u\n" "#define PASS %u\n" "#define TESSELLATION %u\n", level, pid, tess); TempResourceString filename = createProgramSourceToChache(src); RenderingKey key((Pass)pid, level, tess); ProgramResourcePointer& progr = getProgram(key, shader); progr.load(filename.toCString(), &rinit.m_resources); // Update the hash m_hash ^= computeHash(&src[0], src.getLength()); } } } } populateVariables(mspc); // Get uniform block size ANKI_ASSERT(m_progs.size() > 0); m_shaderBlockSize = m_progs[0]->getGlProgram().findBlock("bDefaultBlock").getSize(); }