<commit_before>/*
 * SimulationClient.cpp
 *
 *  Created on: 08.04.2016
 *      Author: Marc Hartung
 */

#include "../include/SimulationClient.hpp"

#include "../include/messages/messages.hpp"
#include "../include/network_impl/SimNetworkFunctions.hpp"
#include <stdexcept>

namespace NetOff
{

    SimulationClient::SimulationClient()
            : _hostAddress(""),
              _port(-1),
              _pathToId(),
              _currentState(CurrentState::NONE),
              _netClient(),
              _possibleInputVarNames(),
              _possibleOutputVarNames(),
              _selectedInputVarNames(),
              _selectedOutputVarNames(),
              _isInitialized(),
              _inputMessages(),
              _outputMessages()
    {
    }

    SimulationClient::SimulationClient(const std::string & hostAddress, const int & port)
            : _hostAddress(hostAddress),
              _port(port),
              _pathToId(),
              _currentState(CurrentState::NONE),
              _netClient(),
              _possibleInputVarNames(),
              _possibleOutputVarNames(),
              _selectedInputVarNames(),
              _selectedOutputVarNames(),
              _isInitialized(),
              _inputMessages(),
              _outputMessages()
    {
    }

    SimulationClient::~SimulationClient()
    {
        _netClient.deinitialize();
    }

    bool SimulationClient::initializeConnection()
    {
        if (_port < 1 || _hostAddress.size() < 1)
        {
            std::cout << "SimulationClient: Port or host address wasn't set.\n";
            return false;
        }
        else if (_currentState > CurrentState::NONE)
            return true;

        if (_netClient.initialize(_hostAddress, _port))
        {
            _currentState = CurrentState::INITED;
            return true;
        }
        else
            return false;
    }

    void SimulationClient::deinitialize()
    {
        _currentState = CurrentState::NONE;

        _inputMessages[0].setSpecifyer(ClientMessageSpecifyer::CLIENT_ABORT);
        send(0);
        _netClient.deinitialize();
    }

    int SimulationClient::addSimulation(const std::string & serverPathToSim)
    {
        if (_currentState == CurrentState::NONE)
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to add simulation before calling initialize(host,port).");
        if (_pathToId.find(serverPathToSim) != _pathToId.end())
            return _pathToId[serverPathToSim];

        const int & simId = _pathToId.size();

        _pathToId[serverPathToSim] = simId;
        _possibleInputVarNames.resize(simId + 1);
        _possibleOutputVarNames.resize(simId + 1);

        _selectedInputVarNames.resize(simId + 1);
        _selectedOutputVarNames.resize(simId + 1);

        _inputMessages.resize(simId + 1);
        _outputMessages.resize(simId + 1);
        _isInitialized.resize(simId + 1, false);

        AddSimRequestMessage req(simId, serverPathToSim);
        sendInitialRequest(req);
        AddSimSuccessMessage varMessage(
                recvInitialServerSuccess<AddSimSuccessMessage>(InitialServerMessageSpecifyer::SUCCESS_ADD_SIM,
                                                               "SimulationClient: Couldn't initialize simulation."));
        _possibleInputVarNames[simId] = varMessage.getInputVariableList();
        _possibleOutputVarNames[simId] = varMessage.getOutputVariableList();
        return simId;
    }

    VariableList SimulationClient::getPossibleInputVariableNames(const int & simId)
    {
        if (_currentState < CurrentState::INITED || simId >= (long long int)_possibleInputVarNames.size())
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to get variables names before calling initialize(host,port).");
        return _possibleInputVarNames[simId];
    }

    VariableList SimulationClient::getPossibleOuputVariableNames(const int & simId)
    {
        if (_currentState < CurrentState::INITED || simId >= (long long int)_possibleOutputVarNames.size())
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to get variables names before calling initialize(host,port).");
        return _possibleOutputVarNames[simId];
    }

    ValueContainer & SimulationClient::initializeSimulation(const int & simId, const VariableList & inputs, const VariableList & outputs, const double * inputsReal,
                                                            const int * inputsInt, const char * inputsBool)
    {
        _selectedInputVarNames[simId] = inputs;
        if (!_selectedInputVarNames[simId].isSubsetOf(_possibleInputVarNames[simId]))
            throw std::runtime_error("SimulationClient: Input variable names passed to initializeSimulation, which aren't supported by server.");
        _selectedOutputVarNames[simId] = outputs;
        if (!_selectedOutputVarNames[simId].isSubsetOf(_possibleOutputVarNames[simId]))
            throw std::runtime_error("SimulationClient: Output variable names passed to initializeSimulation, which aren't supported by server.");

        _inputMessages[simId] = ValueContainerMessage<ClientMessageSpecifyer>(simId, inputs, ClientMessageSpecifyer::INPUTS);
        _outputMessages[simId] = ValueContainerMessage<ServerMessageSpecifyer>(simId, outputs, ServerMessageSpecifyer::OUTPUTS);

        InitSimulationMessage initMessage(simId, inputs, outputs);
        this->sendInitialRequest(initMessage);
        if (inputsReal != nullptr)
            _inputMessages[simId].getContainer().setRealValues(inputsReal);
        if (inputsInt != nullptr)
            _inputMessages[simId].getContainer().setIntValues(inputsInt);
        if (inputsBool != nullptr)
            _inputMessages[simId].getContainer().setBoolValues(inputsBool);
        _inputMessages[simId].setSpecifyer(ClientMessageSpecifyer::INPUTS);
        send(simId);
        _isInitialized[simId] = recv(simId, 0.0, ServerMessageSpecifyer::SUCCESS_SIM_INIT);
        _isInitialized[simId] = true;
        return _outputMessages[simId].getContainer();
    }

    bool SimulationClient::getSimulationFile(const int & simId, const std::string & sourcePath, const std::string & targetPath)
    {
        try {
            GetFileMessage message(simId, sourcePath);
            sendInitialRequest(message);
            std::shared_ptr<size_t> numBytes(new size_t[1]);
            // Receive file content.
            std::shared_ptr<char> data = _netClient.variableRecv(numBytes.get());
            // And store it in given target path.
            GetFileSuccessMessage sucMessage(*numBytes, data, targetPath);
        }
        catch (std::runtime_error& ex)
        {
            std::cout << "Something went wrong in " << __FILE__ << " line " << __LINE__ << std::endl;
            return false;
        }
        return true;
    }

    bool SimulationClient::start()
    {
        bool abortSim = false;

        for (const bool & i : _isInitialized)
            if (!i)
            {
                abortSim = true;
                break;
            }
        if (abortSim || _isInitialized.empty())
        {
            AbortRequestMessage abortM;
            sendInitialRequest(abortM);
            throw std::runtime_error("ERROR: SimulationClient: No simulation added or at least one simulation is not initialized.");
        }
        StartRequestMessage startRequest;
        sendInitialRequest(startRequest);
        recvInitialServerSuccess<StartSuccessMessage>(InitialServerMessageSpecifyer::SUCCESS_START, "SimulationClient: Can't start server.");

        _currentState = CurrentState::STARTED;

        return true;
    }

    bool SimulationClient::pause()
    {
        if (_currentState < CurrentState::STARTED)
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to pause before calling start().");

        _inputMessages[0].setSpecifyer(ClientMessageSpecifyer::PAUSE);
        send(0);

        bool res = recv(0, _inputMessages[0].getTime(), ServerMessageSpecifyer::SUCCESS_PAUSE);
        if (res)
            _currentState = CurrentState::PAUSED;
        return res;
    }

    bool SimulationClient::unpause()
    {
        if (_currentState < CurrentState::STARTED)
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to unpause before calling start().");

        _inputMessages[0].setSpecifyer(ClientMessageSpecifyer::UNPAUSE);
        send(0);

        bool res = recv(0, _inputMessages[0].getTime(), ServerMessageSpecifyer::SUCCESS_UNPAUSE);
        if (res)
            _currentState = CurrentState::STARTED;
        return res;
    }

    bool SimulationClient::reset()
    {
        if (_currentState < CurrentState::STARTED)
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to reset before calling start().");

        _inputMessages[0].setSpecifyer(ClientMessageSpecifyer::RESET);
        send(0);

        bool res = recv(0, _inputMessages[0].getTime(), ServerMessageSpecifyer::SUCCESS_RESET);
        if (res)
            _currentState = CurrentState::INITED;
        return res;
    }

    bool SimulationClient::send(const int simId)
    {
        _netClient.send(reinterpret_cast<const char *>(&_inputMessages[simId].getId()), sizeof(int));  //send id
        return _netClient.send(_inputMessages[simId].data(), _inputMessages[simId].dataSize());
    }

    const std::string & SimulationClient::getHostAddress() const
    {
        return _hostAddress;
    }

    void SimulationClient::setHostAddress(const std::string & hostAddress)
    {
        _hostAddress = hostAddress;
    }

    int SimulationClient::getPort() const
    {
        return _port;
    }

    void SimulationClient::setPort(int port)
    {
        _port = port;
    }

    bool SimulationClient::recv(const int simId, const double & expectedTime, const ServerMessageSpecifyer & spec)
    {
        _netClient.recv(_outputMessages[simId].data(), _outputMessages[simId].dataSize());
        return _outputMessages[simId].getTime() == expectedTime && _outputMessages[simId].getSpecifyer() == spec;
    }

    ValueContainer & SimulationClient::recvOutputValues(const int & simId, const double & time)
    {
        if (_currentState < CurrentState::STARTED)
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to receive outputs before calling start().");

        recv(simId, time, ServerMessageSpecifyer::OUTPUTS);
        return _outputMessages[simId].getContainer();
    }

    bool SimulationClient::sendInputValues(const int & simId, const double & time, const ValueContainer & vals)
    {
        if (_currentState < CurrentState::STARTED)
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to send inputs before calling start().");
        if (vals.getSimId() != simId || vals.data() != getInputValueContainer(simId).data())
        {
            throw std::runtime_error("SimulationClient: The ValueContainer is collected via getValueContainerInput()");
        }
        _inputMessages[simId].setSpecifyer(ClientMessageSpecifyer::INPUTS);
        _inputMessages[simId].setTime(time);
        return send(simId);
    }

    ValueContainer & SimulationClient::getInputValueContainer(const int & simId)
    {
        if (!_isInitialized[simId] || _currentState < CurrentState::INITED)
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to get a input container before calling initializeSimulation.");
        return _inputMessages[simId].getContainer();
    }

    ValueContainer & SimulationClient::getOutputValueContainer(const int & simId)
    {
        if (!_isInitialized[simId] || _currentState < CurrentState::INITED)
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to get a out container before calling initializeSimulation().");
        return _outputMessages[simId].getContainer();
    }

    bool SimulationClient::isStarted() const
    {
        return _currentState == CurrentState::STARTED;
    }
}
<commit_msg>Use constructor delegation and delete empty lines<commit_after>/*
 * SimulationClient.cpp
 *
 *  Created on: 08.04.2016
 *      Author: Marc Hartung
 */

#include "../include/SimulationClient.hpp"

#include "../include/messages/messages.hpp"
#include "../include/network_impl/SimNetworkFunctions.hpp"
#include <stdexcept>

namespace NetOff
{

    SimulationClient::SimulationClient()
            : SimulationClient("", -1)
    {
    }

    SimulationClient::SimulationClient(const std::string & hostAddress, const int & port)
            : _hostAddress(hostAddress),
              _port(port),
              _pathToId(),
              _currentState(CurrentState::NONE),
              _netClient(),
              _possibleInputVarNames(),
              _possibleOutputVarNames(),
              _selectedInputVarNames(),
              _selectedOutputVarNames(),
              _isInitialized(),
              _inputMessages(),
              _outputMessages()
    {
    }

    SimulationClient::~SimulationClient()
    {
        _netClient.deinitialize();
    }

    bool SimulationClient::initializeConnection()
    {
        if (_port < 1 || _hostAddress.size() < 1)
        {
            std::cout << "SimulationClient: Port or host address wasn't set.\n";
            return false;
        }
        else if (_currentState > CurrentState::NONE)
            return true;

        if (_netClient.initialize(_hostAddress, _port))
        {
            _currentState = CurrentState::INITED;
            return true;
        }
        else
            return false;
    }

    void SimulationClient::deinitialize()
    {
        _currentState = CurrentState::NONE;

        _inputMessages[0].setSpecifyer(ClientMessageSpecifyer::CLIENT_ABORT);
        send(0);
        _netClient.deinitialize();
    }

    int SimulationClient::addSimulation(const std::string & serverPathToSim)
    {
        // SimulationClient is not already initialized.
        if (_currentState == CurrentState::NONE)
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to add simulation before calling initialize(host,port).");
        // The very same simulation was already added.
        if (_pathToId.find(serverPathToSim) != _pathToId.end())
            return _pathToId[serverPathToSim];

        const int & simId = _pathToId.size();

        _pathToId[serverPathToSim] = simId;
        _possibleInputVarNames.resize(simId + 1);
        _possibleOutputVarNames.resize(simId + 1);

        _selectedInputVarNames.resize(simId + 1);
        _selectedOutputVarNames.resize(simId + 1);

        _inputMessages.resize(simId + 1);
        _outputMessages.resize(simId + 1);
        _isInitialized.resize(simId + 1, false);

        AddSimRequestMessage req(simId, serverPathToSim);
        sendInitialRequest(req);
        AddSimSuccessMessage varMessage(
                recvInitialServerSuccess<AddSimSuccessMessage>(InitialServerMessageSpecifyer::SUCCESS_ADD_SIM,
                                                               "SimulationClient: Couldn't initialize simulation."));
        _possibleInputVarNames[simId] = varMessage.getInputVariableList();
        _possibleOutputVarNames[simId] = varMessage.getOutputVariableList();
        return simId;
    }

    VariableList SimulationClient::getPossibleInputVariableNames(const int & simId)
    {
        if (_currentState < CurrentState::INITED || simId >= (long long int)_possibleInputVarNames.size())
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to get variables names before calling initialize(host,port).");
        return _possibleInputVarNames[simId];
    }

    VariableList SimulationClient::getPossibleOuputVariableNames(const int & simId)
    {
        if (_currentState < CurrentState::INITED || simId >= (long long int)_possibleOutputVarNames.size())
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to get variables names before calling initialize(host,port).");
        return _possibleOutputVarNames[simId];
    }

    ValueContainer & SimulationClient::initializeSimulation(const int & simId, const VariableList & inputs, const VariableList & outputs,
                                                            const double * inputsReal, const int * inputsInt, const char * inputsBool)
    {
        _selectedInputVarNames[simId] = inputs;
        if (!_selectedInputVarNames[simId].isSubsetOf(_possibleInputVarNames[simId]))
            throw std::runtime_error("SimulationClient: Input variable names passed to initializeSimulation, which aren't supported by server.");
        _selectedOutputVarNames[simId] = outputs;
        if (!_selectedOutputVarNames[simId].isSubsetOf(_possibleOutputVarNames[simId]))
            throw std::runtime_error("SimulationClient: Output variable names passed to initializeSimulation, which aren't supported by server.");

        _inputMessages[simId] = ValueContainerMessage<ClientMessageSpecifyer>(simId, inputs, ClientMessageSpecifyer::INPUTS);
        _outputMessages[simId] = ValueContainerMessage<ServerMessageSpecifyer>(simId, outputs, ServerMessageSpecifyer::OUTPUTS);

        InitSimulationMessage initMessage(simId, inputs, outputs);
        this->sendInitialRequest(initMessage);
        if (inputsReal != nullptr)
            _inputMessages[simId].getContainer().setRealValues(inputsReal);
        if (inputsInt != nullptr)
            _inputMessages[simId].getContainer().setIntValues(inputsInt);
        if (inputsBool != nullptr)
            _inputMessages[simId].getContainer().setBoolValues(inputsBool);
        _inputMessages[simId].setSpecifyer(ClientMessageSpecifyer::INPUTS);
        send(simId);
        _isInitialized[simId] = recv(simId, 0.0, ServerMessageSpecifyer::SUCCESS_SIM_INIT);
        _isInitialized[simId] = true;
        return _outputMessages[simId].getContainer();
    }

    bool SimulationClient::getSimulationFile(const int & simId, const std::string & sourcePath, const std::string & targetPath)
    {
        try {
            GetFileMessage message(simId, sourcePath);
            sendInitialRequest(message);
            std::shared_ptr<size_t> numBytes(new size_t[1]);
            // Receive file content.
            std::shared_ptr<char> data = _netClient.variableRecv(numBytes.get());
            // And store it in given target path.
            GetFileSuccessMessage sucMessage(*numBytes, data, targetPath);
        }
        catch (std::runtime_error& ex)
        {
            std::cout << "Something went wrong in " << __FILE__ << " line " << __LINE__ << std::endl;
            return false;
        }
        return true;
    }

    /////////////////////////////////////////
    ////////////RUNNING CONNECTION///////////
    /////////////////////////////////////////

    bool SimulationClient::start()
    {
        bool abortSim = false;

        for (const bool & i : _isInitialized)
        {
            if (!i)
            {
                abortSim = true;
                break;
            }
        }
        if (abortSim || _isInitialized.empty())
        {
            AbortRequestMessage abortM;
            sendInitialRequest(abortM);
            throw std::runtime_error("ERROR: SimulationClient: No simulation added or at least one simulation is not initialized.");
        }

        StartRequestMessage startRequest;
        sendInitialRequest(startRequest);
        recvInitialServerSuccess<StartSuccessMessage>(InitialServerMessageSpecifyer::SUCCESS_START, "SimulationClient: Can't start server.");

        _currentState = CurrentState::STARTED;

        return true;
    }

    bool SimulationClient::pause()
    {
        if (_currentState < CurrentState::STARTED)
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to pause before calling start().");

        _inputMessages[0].setSpecifyer(ClientMessageSpecifyer::PAUSE);
        send(0);

        bool res = recv(0, _inputMessages[0].getTime(), ServerMessageSpecifyer::SUCCESS_PAUSE);
        if (res)
            _currentState = CurrentState::PAUSED;
        return res;
    }

    bool SimulationClient::unpause()
    {
        if (_currentState < CurrentState::STARTED)
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to unpause before calling start().");

        _inputMessages[0].setSpecifyer(ClientMessageSpecifyer::UNPAUSE);
        send(0);

        bool res = recv(0, _inputMessages[0].getTime(), ServerMessageSpecifyer::SUCCESS_UNPAUSE);
        if (res)
            _currentState = CurrentState::STARTED;
        return res;
    }

    bool SimulationClient::reset()
    {
        if (_currentState < CurrentState::STARTED)
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to reset before calling start().");

        _inputMessages[0].setSpecifyer(ClientMessageSpecifyer::RESET);
        send(0);

        bool res = recv(0, _inputMessages[0].getTime(), ServerMessageSpecifyer::SUCCESS_RESET);
        if (res)
            _currentState = CurrentState::INITED;
        return res;
    }

    bool SimulationClient::isStarted() const
    {
        return _currentState == CurrentState::STARTED;
    }

    bool SimulationClient::send(const int simId)
    {
        _netClient.send(reinterpret_cast<const char *>(&_inputMessages[simId].getId()), sizeof(int));  //send id
        return _netClient.send(_inputMessages[simId].data(), _inputMessages[simId].dataSize());
    }

    bool SimulationClient::recv(const int simId, const double & expectedTime, const ServerMessageSpecifyer & spec)
    {
        _netClient.recv(_outputMessages[simId].data(), _outputMessages[simId].dataSize());
        return _outputMessages[simId].getTime() == expectedTime && _outputMessages[simId].getSpecifyer() == spec;
    }

    const std::string & SimulationClient::getHostAddress() const
    {
        return _hostAddress;
    }

    void SimulationClient::setHostAddress(const std::string & hostAddress)
    {
        _hostAddress = hostAddress;
    }

    int SimulationClient::getPort() const
    {
        return _port;
    }

    void SimulationClient::setPort(int port)
    {
        _port = port;
    }

    ValueContainer & SimulationClient::recvOutputValues(const int & simId, const double & time)
    {
        if (_currentState < CurrentState::STARTED)
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to receive outputs before calling start().");

        recv(simId, time, ServerMessageSpecifyer::OUTPUTS);
        return _outputMessages[simId].getContainer();
    }

    bool SimulationClient::sendInputValues(const int & simId, const double & time, const ValueContainer & vals)
    {
        if (_currentState < CurrentState::STARTED)
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to send inputs before calling start().");

        if (vals.getSimId() != simId || vals.data() != getInputValueContainer(simId).data())
            throw std::runtime_error("SimulationClient: The ValueContainer is collected via getValueContainerInput()");

        _inputMessages[simId].setSpecifyer(ClientMessageSpecifyer::INPUTS);
        _inputMessages[simId].setTime(time);
        return send(simId);
    }

    ValueContainer & SimulationClient::getInputValueContainer(const int & simId)
    {
        if (!_isInitialized[simId] || _currentState < CurrentState::INITED)
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to get a input container before calling initializeSimulation.");
        return _inputMessages[simId].getContainer();
    }

    ValueContainer & SimulationClient::getOutputValueContainer(const int & simId)
    {
        if (!_isInitialized[simId] || _currentState < CurrentState::INITED)
            throw std::runtime_error("ERROR: SimulationClient: It's not possible to get a out container before calling initializeSimulation().");
        return _outputMessages[simId].getContainer();
    }

} // End namespace NetOff
<|endoftext|>


<commit_before>#include "material_exporter.h"
#include "logger.h"
#include <assimp/../../code/BoostWorkaround/boost/lexical_cast.hpp>

static const std::map<aiTextureType, std::string> SupportedTextureTypes = {
	{ aiTextureType_DIFFUSE, "diffuseTexture" },
	{ aiTextureType_SPECULAR, "specularTexture" },
	{ aiTextureType_EMISSIVE, "emissiveTexture" }
};

XML3DMaterialExporter::XML3DMaterialExporter(XML3DExporter* ex, aiMaterial* mat) :
	xml3d(ex),
	aMat(mat)
{

}

XML3DMaterialExporter::~XML3DMaterialExporter() {

}

tinyxml2::XMLElement* XML3DMaterialExporter::getMaterial() {
	tinyxml2::XMLElement* material = xml3d->doc.NewElement("shader");
	aiString name;
	aMat->Get(AI_MATKEY_NAME, name);
	xml3d->stringToHTMLId(name);
	material->SetAttribute("id", name.C_Str());
	material->SetAttribute("script", "urn:xml3d:shader:phong"); //TODO: Choose right shading model

	// For now we only handle properties that the default XML3D material shaders can work with
	tinyxml2::XMLElement* diffuseColor = xml3d->doc.NewElement("float3");
	diffuseColor->SetAttribute("name", "diffuseColor");
	aiColor4D dColor;
	aMat->Get(AI_MATKEY_COLOR_DIFFUSE, dColor);
	diffuseColor->SetText(xml3d->toXml3dString(&dColor, 1, true).c_str());
	material->LinkEndChild(diffuseColor);

	tinyxml2::XMLElement* specularColor = xml3d->doc.NewElement("float3");
	specularColor->SetAttribute("name", "specularColor");
	aiColor4D sColor;
	aMat->Get(AI_MATKEY_COLOR_SPECULAR, sColor);
	specularColor->SetText(xml3d->toXml3dString(&sColor, 1, true).c_str());
	material->LinkEndChild(specularColor);

	tinyxml2::XMLElement* emissiveColor = xml3d->doc.NewElement("float3");
	emissiveColor->SetAttribute("name", "emissiveColor");
	aiColor4D eColor;
	aMat->Get(AI_MATKEY_COLOR_EMISSIVE, eColor);
	emissiveColor->SetText(xml3d->toXml3dString(&eColor, 1, true).c_str());
	material->LinkEndChild(emissiveColor);

	tinyxml2::XMLElement* shininess = xml3d->doc.NewElement("float");
	shininess->SetAttribute("name", "shininess");
	float s;
	aMat->Get(AI_MATKEY_SHININESS, s);
	shininess->SetText(boost::lexical_cast<std::string>(s).c_str());
	material->LinkEndChild(shininess);

	tinyxml2::XMLElement* opacity = xml3d->doc.NewElement("float");
	opacity->SetAttribute("name", "transparency");
	float o;
	aMat->Get(AI_MATKEY_OPACITY, o);
	opacity->SetText(boost::lexical_cast<std::string>(1 - o).c_str());
	material->LinkEndChild(opacity);

	addTexturesToMaterial(material);

	return material;
}

void XML3DMaterialExporter::addTexturesToMaterial(tinyxml2::XMLElement* matElement) {
	for (auto it = SupportedTextureTypes.begin(); it != SupportedTextureTypes.end(); ++it) {
		if (aMat->GetTextureCount(it->first) > 0) {
			tinyxml2::XMLElement* texElement = processTexture(it->first);
			if (texElement) {
				texElement->SetAttribute("name", it->second.c_str());
				matElement->LinkEndChild(texElement);
			}
			if (aMat->GetTextureCount(it->first) > 1) {
				// XML3D only supports 1 texture per channel by default, so for now we limit the exporter to that
				Logger::Warn("Found multiple textures for channel " + std::string(it->second.c_str()) + ", only the first texture will be retained.");
			}
		}
	}
}

tinyxml2::XMLElement* XML3DMaterialExporter::processTexture(aiTextureType texType) {
	int wrapS;
	aMat->Get(AI_MATKEY_MAPPINGMODE_U(texType, 0), wrapS);
	int wrapT;
	aMat->Get(AI_MATKEY_MAPPINGMODE_V(texType, 0), wrapT);

	aiString texPath;
	aMat->Get(AI_MATKEY_TEXTURE(texType, 0), texPath);
	if (texPath.length <= 0) {
		Logger::Debug("Could not find texture path for texture channel " + boost::lexical_cast<std::string, int>(texType));
		aiString matName;
		aMat->Get(AI_MATKEY_NAME, matName);
		Logger::Warn("Could not process a texture for material " + std::string(matName.C_Str()));
		return NULL;
	}

	tinyxml2::XMLElement* texElement = xml3d->doc.NewElement("texture");
	texElement->SetAttribute("wraps", mapModeToString(wrapS).c_str());
	texElement->SetAttribute("wrapt", mapModeToString(wrapT).c_str());
	tinyxml2::XMLElement* imgElement = xml3d->doc.NewElement("img");
	imgElement->SetAttribute("src", texPath.C_Str());
	texElement->LinkEndChild(imgElement);
	return texElement;
}

std::string XML3DMaterialExporter::mapModeToString(int mode) {
	switch (mode) {
	case aiTextureMapMode_Clamp:
		return "clamp";
	case aiTextureMapMode_Wrap:
		return "repeat";
	default:
		Logger::Warn("Unsupported texture wrapping mode encountered. Valid modes are 'clamp' or 'wrap/repeat'. Defaulting to clamp.");
		return "clamp";
	}
}
<commit_msg>Add a warning when encountering a fully transparent material. This is often a problem with the export format confusing opacity with transparency.<commit_after>#include "material_exporter.h"
#include "logger.h"
#include <assimp/../../code/BoostWorkaround/boost/lexical_cast.hpp>

static const std::map<aiTextureType, std::string> SupportedTextureTypes = {
	{ aiTextureType_DIFFUSE, "diffuseTexture" },
	{ aiTextureType_SPECULAR, "specularTexture" },
	{ aiTextureType_EMISSIVE, "emissiveTexture" }
};

XML3DMaterialExporter::XML3DMaterialExporter(XML3DExporter* ex, aiMaterial* mat) :
	xml3d(ex),
	aMat(mat)
{

}

XML3DMaterialExporter::~XML3DMaterialExporter() {

}

tinyxml2::XMLElement* XML3DMaterialExporter::getMaterial() {
	tinyxml2::XMLElement* material = xml3d->doc.NewElement("shader");
	aiString name;
	aMat->Get(AI_MATKEY_NAME, name);
	xml3d->stringToHTMLId(name);
	material->SetAttribute("id", name.C_Str());
	material->SetAttribute("script", "urn:xml3d:shader:phong"); //TODO: Choose right shading model

	// For now we only handle properties that the default XML3D material shaders can work with
	tinyxml2::XMLElement* diffuseColor = xml3d->doc.NewElement("float3");
	diffuseColor->SetAttribute("name", "diffuseColor");
	aiColor4D dColor;
	aMat->Get(AI_MATKEY_COLOR_DIFFUSE, dColor);
	diffuseColor->SetText(xml3d->toXml3dString(&dColor, 1, true).c_str());
	material->LinkEndChild(diffuseColor);

	tinyxml2::XMLElement* specularColor = xml3d->doc.NewElement("float3");
	specularColor->SetAttribute("name", "specularColor");
	aiColor4D sColor;
	aMat->Get(AI_MATKEY_COLOR_SPECULAR, sColor);
	specularColor->SetText(xml3d->toXml3dString(&sColor, 1, true).c_str());
	material->LinkEndChild(specularColor);

	tinyxml2::XMLElement* emissiveColor = xml3d->doc.NewElement("float3");
	emissiveColor->SetAttribute("name", "emissiveColor");
	aiColor4D eColor;
	aMat->Get(AI_MATKEY_COLOR_EMISSIVE, eColor);
	emissiveColor->SetText(xml3d->toXml3dString(&eColor, 1, true).c_str());
	material->LinkEndChild(emissiveColor);

	tinyxml2::XMLElement* shininess = xml3d->doc.NewElement("float");
	shininess->SetAttribute("name", "shininess");
	float s;
	aMat->Get(AI_MATKEY_SHININESS, s);
	shininess->SetText(boost::lexical_cast<std::string>(s).c_str());
	material->LinkEndChild(shininess);

	tinyxml2::XMLElement* opacity = xml3d->doc.NewElement("float");
	opacity->SetAttribute("name", "transparency");
	float o;
	aMat->Get(AI_MATKEY_OPACITY, o);
	if (o <= 0.0001f) {
		Logger::Warn("Material with name '"+std::string(name.C_Str())+"' has an opacity value of 0. Meshes rendered with this material will be fully transparent!");
	}
	opacity->SetText(boost::lexical_cast<std::string>(1 - o).c_str());
	material->LinkEndChild(opacity);

	addTexturesToMaterial(material);

	return material;
}

void XML3DMaterialExporter::addTexturesToMaterial(tinyxml2::XMLElement* matElement) {
	for (auto it = SupportedTextureTypes.begin(); it != SupportedTextureTypes.end(); ++it) {
		if (aMat->GetTextureCount(it->first) > 0) {
			tinyxml2::XMLElement* texElement = processTexture(it->first);
			if (texElement) {
				texElement->SetAttribute("name", it->second.c_str());
				matElement->LinkEndChild(texElement);
			}
			if (aMat->GetTextureCount(it->first) > 1) {
				// XML3D only supports 1 texture per channel by default, so for now we limit the exporter to that
				Logger::Warn("Found multiple textures for channel " + std::string(it->second.c_str()) + ", only the first texture will be retained.");
			}
		}
	}
}

tinyxml2::XMLElement* XML3DMaterialExporter::processTexture(aiTextureType texType) {
	int wrapS;
	aMat->Get(AI_MATKEY_MAPPINGMODE_U(texType, 0), wrapS);
	int wrapT;
	aMat->Get(AI_MATKEY_MAPPINGMODE_V(texType, 0), wrapT);

	aiString texPath;
	aMat->Get(AI_MATKEY_TEXTURE(texType, 0), texPath);
	if (texPath.length <= 0) {
		Logger::Debug("Could not find texture path for texture channel " + boost::lexical_cast<std::string, int>(texType));
		aiString matName;
		aMat->Get(AI_MATKEY_NAME, matName);
		Logger::Warn("Could not process a texture for material " + std::string(matName.C_Str()));
		return NULL;
	}

	tinyxml2::XMLElement* texElement = xml3d->doc.NewElement("texture");
	texElement->SetAttribute("wraps", mapModeToString(wrapS).c_str());
	texElement->SetAttribute("wrapt", mapModeToString(wrapT).c_str());
	tinyxml2::XMLElement* imgElement = xml3d->doc.NewElement("img");
	imgElement->SetAttribute("src", texPath.C_Str());
	texElement->LinkEndChild(imgElement);
	return texElement;
}

std::string XML3DMaterialExporter::mapModeToString(int mode) {
	switch (mode) {
	case aiTextureMapMode_Clamp:
		return "clamp";
	case aiTextureMapMode_Wrap:
		return "repeat";
	default:
		Logger::Warn("Unsupported texture wrapping mode encountered. Valid modes are 'clamp' or 'wrap/repeat'. Defaulting to clamp.");
		return "clamp";
	}
}
<|endoftext|>


<commit_before>#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <pthread.h>
#include <queue>
#include <deque>
#include <algorithm>
#include "assert.h"
#include "parlib.h"

/*
The Weld parallel runtime. When the comments refer to a "computation",
this means a single complete execution of a Weld program.
*/
using namespace std;

// allows each thread to retrieve its index
pthread_key_t id;

typedef deque<work_t *> work_queue;
typedef pthread_spinlock_t work_queue_lock;

int32_t W = 1; // number of workers, default 1
int64_t run_id = 1; // the current run ID.
pthread_t *workers = NULL;
work_queue *all_work_queues = NULL; // queue per worker
work_queue_lock *all_work_queue_locks = NULL; // lock per queue

volatile bool done = false; // if a computation is currently running, have we finished it?
volatile void *result = NULL; // stores the final result of the computation to be passed back
// to the caller

// TODO remove this, just for testing
pthread_mutex_t global_lock;

// gives the current thread's index
static inline int32_t my_id() {
  return (int32_t)reinterpret_cast<intptr_t>(pthread_getspecific(id));
}

extern "C" int32_t my_id_public() {
  return (int32_t)reinterpret_cast<intptr_t>(pthread_getspecific(id));
}

extern "C" void take_global_lock() {
  pthread_mutex_lock(&global_lock);
}

extern "C" void release_global_lock() {
  pthread_mutex_unlock(&global_lock);
}

// retrieve the result of the computation
extern "C" void *get_result() {
  if (weld_rt_get_errno(get_runid()) != 0) {
    return NULL;
  }
  return (void *)result;
}

// set the result of the computation, called from generated LLVM
extern "C" void set_result(void *res) {
  result = res;
}

extern "C" int32_t get_nworkers() {
  return W;
}

extern "C" void set_nworkers(int32_t n) {
  W = n;
}

extern "C" int64_t get_runid() {
  return run_id;
}

extern "C" void set_runid(int64_t id) {
  run_id = id;
}

extern "C" void weld_abort_thread() {
    pthread_exit(NULL);
}

static inline void set_nest(work_t *task) {
  assert(task->full_task);
  vector<int64_t> idxs;
  vector<int64_t> task_ids;
  idxs.push_back(task->cur_idx);
  task_ids.push_back(task->task_id);
  work_t *cur = task->cont;
  int32_t nest_len = 1;
  while (cur != NULL) {
    idxs.push_back(cur->cur_idx);
    // subtract 1 because the conts give us the continuations and we want the
    // task_id's of the loop bodies before the continuations
    task_ids.push_back(cur->task_id - 1);
    cur = cur->cont;
    nest_len++;
  }
  task->nest_idxs = (int64_t *)weld_rt_malloc(get_runid(), sizeof(int64_t) * nest_len);
  task->nest_task_ids = (int64_t *)weld_rt_malloc(get_runid(), sizeof(int64_t) * nest_len);
  task->nest_len = nest_len;
  // we want the outermost idxs to be the "high-order bits" when we do a comparison of task nests
  reverse_copy(idxs.begin(), idxs.end(), task->nest_idxs);
  reverse_copy(task_ids.begin(), task_ids.end(), task->nest_task_ids);
}

static inline void set_full_task(work_t *task) {
  // possible for task to already be full, e.g. if the head task from a start_loop call is queued
  // but stolen before it can be executed (the thief tries to set_full_task a second time)
  if (task->full_task) {
    return;
  }
  task->full_task = true;
  set_nest(task);
}

// attempt to steal from back of the queue of a random victim
// should be called when own work queue empty
static inline bool try_steal() {
  int32_t victim = rand() % W;
  if (!pthread_spin_trylock(all_work_queue_locks + victim)) {
    work_queue *its_work_queue = all_work_queues + victim;
    if (its_work_queue->empty()) {
      pthread_spin_unlock(all_work_queue_locks + victim);
      return false;
    } else {
      work_t *popped = its_work_queue->back();
      its_work_queue->pop_back();
      pthread_spin_unlock(all_work_queue_locks + victim);
      set_full_task(popped);
      pthread_spin_lock((all_work_queue_locks + my_id()));
      (all_work_queues + my_id())->push_front(popped);
      pthread_spin_unlock((all_work_queue_locks + my_id()));
      return true;
    }
  } else {
    return false;
  }
}

// called once task function returns
// decrease the dependency count of the continuation, run the continuation
// if necessary, or signal the end of the computation if we are done
static inline void finish_task(work_t *task) {
  // Exit the thread if there's an error.
  // We don't need to worry about freeing here; the runtime will
  // free all allocated memory as long as it is allocated with
  // `weld_rt_malloc` or `weld_rt_realloc`.
  if (weld_rt_get_errno(get_runid()) != 0) {
    weld_abort_thread();
  }

  if (task->cont == NULL) {
    if (!task->continued) {
      // if this task has no continuation and there was no for loop to end it,
      // the computation is over
      done = true;
    }
    weld_rt_free(get_runid(), task->data);
  } else {
    int32_t previous = __sync_fetch_and_sub(&task->cont->deps, 1);
    if (previous == 1) {
      // run the continuation since we are the last dependency
      set_full_task(task->cont);
      pthread_spin_lock((all_work_queue_locks + my_id()));
      (all_work_queues + my_id())->push_front(task->cont);
      pthread_spin_unlock((all_work_queue_locks + my_id()));
      // we are the last sibling with this data, so we can free it
      weld_rt_free(get_runid(), task->data);
    }
    if (task->full_task) {
      weld_rt_free(get_runid(), task->nest_idxs);
      weld_rt_free(get_runid(), task->nest_task_ids);
    }
  }
  weld_rt_free(get_runid(), task);
}

// set the continuation of w to cont and increment cont
// dependency count
static inline void set_cont(work_t *w, work_t *cont) {
  w->cont = cont;
  __sync_fetch_and_add(&cont->deps, 1);
}

// called from generated code to schedule a for loop with the given body and continuation
// the data pointers store the closures for the body and continuation
// lower and upper give the iteration range for the loop
// w is the currently executing task
extern "C" void pl_start_loop(work_t *w, void *body_data, void *cont_data, void (*body)(work_t*),
  void (*cont)(work_t*), int64_t lower, int64_t upper, int32_t grain_size) {
  work_t *body_task = (work_t *)weld_rt_malloc(get_runid(), sizeof(work_t));
  memset(body_task, 0, sizeof(work_t));
  body_task->data = body_data;
  body_task->fp = body;
  body_task->lower = lower;
  body_task->upper = upper;
  body_task->cur_idx = lower;
  body_task->task_id = w->task_id + 1;
  body_task->grain_size = grain_size;
  work_t *cont_task = (work_t *)weld_rt_malloc(get_runid(), sizeof(work_t));
  memset(cont_task, 0, sizeof(work_t));
  cont_task->data = cont_data;
  cont_task->fp = cont;
  cont_task->cur_idx = w->cur_idx;
  // ensures continuation and all descendants have greater task ID than body
  cont_task->task_id = w->task_id + 2;
  set_cont(body_task, cont_task);
  if (w != NULL) {
    if (w->cont != NULL) {
      // inherit the current task's continuation
      set_cont(cont_task, w->cont);
    } else {
      // this task has no continuation, but it has been effectively
      // continued by this loop so we don't want to end the computation
      // when this task completes
      w->continued = true;
    }
  }
  set_full_task(body_task);

  pthread_spin_lock((all_work_queue_locks + my_id()));
  (all_work_queues + my_id())->push_front(body_task);
  pthread_spin_unlock((all_work_queue_locks + my_id()));
}

static inline work_t *clone_task(work_t *task) {
  work_t *clone = (work_t *)weld_rt_malloc(get_runid(), sizeof(work_t));
  memcpy(clone, task, sizeof(work_t));
  clone->full_task = false;
  return clone;
}

// repeatedly break off the second half of the task into a new task
// until the task's size in iterations drops below a certain threshold
// call with my_id() queue lock held
static inline void split_task(work_t *task) {
  while (task->upper - task->lower > task->grain_size) {
    work_t *last_half = clone_task(task);
    int64_t mid = (task->lower + task->upper) / 2;
    task->upper = mid;
    last_half->lower = mid;
    last_half->cur_idx = mid;
    // task must have non-NULL cont if it has non-zero number of iterations and therefore
    // is a loop body
    set_cont(last_half, task->cont);
    (all_work_queues + my_id())->push_front(last_half);
  }
}

// keeps executing items from the work queue until it is empty
static inline void work_loop() {
  while (true) {
    pthread_spin_lock((all_work_queue_locks + my_id()));
    if ((all_work_queues + my_id())->empty()) {
      pthread_spin_unlock((all_work_queue_locks + my_id()));
      return;
    } else {
      work_t *popped = (all_work_queues + my_id())->front();
      (all_work_queues + my_id())->pop_front();
      split_task(popped);
      pthread_spin_unlock((all_work_queue_locks + my_id()));
      popped->fp(popped);
      finish_task(popped);
    }
  }
}

static void *thread_func(void *data) {
  intptr_t tid = reinterpret_cast<intptr_t>(data);
  pthread_setspecific(id, (void *)tid);

#ifndef __APPLE__
  // pin thread to CPU
  cpu_set_t set;
  CPU_ZERO(&set);
  CPU_SET(tid, &set);
  if (sched_setaffinity(0, sizeof(set), &set) == -1) {
    printf("unable to set affinity for thread %d\n", tid);
  }
#endif

  int iters = 0;

  // this work_loop call is needed to complete any work items that are initially on the queue
  work_loop();
  while (!done) {
    if (try_steal()) {
      iters = 0;
      work_loop();
    } else {
      // If this thread is stalling, periodically check for errors.
      iters++;
      if (iters > 1000000) {
        if (weld_rt_get_errno(get_runid()) != 0) {
          return NULL;
        }
        iters = 0;
      }
    }
  }
  return NULL;
}

// cleanup old computation state and reset to initial values so that
// another computation can be run
static inline void cleanup_computation_state() {
  pthread_key_delete(id);
  pthread_mutex_destroy(&global_lock);

  for (int32_t i = 0; i < W; i++) {
    pthread_spin_destroy(all_work_queue_locks + i);
    all_work_queues[i].clear();
  }

  delete [] all_work_queue_locks;
  delete [] all_work_queues;
  delete [] workers;

  done = false;
}

// start up parallel runtime and kick off threads running thread_func
// block until the computation is complete
// once execute() returns all state has been reset and it is safe to run
// another computation
extern "C" void execute(void (*run)(work_t*), void *data) {
  workers = new pthread_t[W];
  all_work_queue_locks = new work_queue_lock[W];
  all_work_queues = new work_queue[W];

  work_t *run_task = (work_t *)weld_rt_malloc(get_runid(), sizeof(work_t));
  memset(run_task, 0, sizeof(work_t));
  run_task->data = data;
  run_task->fp = run;
  // this initial task can be thought of as a continuation
  set_full_task(run_task);
  all_work_queues[0].push_front(run_task);

  for (int32_t i = 0; i < W; i++) {
    pthread_spin_init(all_work_queue_locks + i, 0);
  }

  pthread_key_create(&id, NULL);
  pthread_mutex_init(&global_lock, NULL);

  for (int32_t i = 0; i < W; i++) {
    pthread_create(workers + i, NULL, &thread_func, reinterpret_cast<void *>(i));
  }

  for (int32_t i = 0; i < W; i++) {
    pthread_join(workers[i], NULL);
  }

  cleanup_computation_state();
}
<commit_msg>reduced lock period<commit_after>#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <pthread.h>
#include <queue>
#include <deque>
#include <algorithm>
#include "assert.h"
#include "parlib.h"

/*
The Weld parallel runtime. When the comments refer to a "computation",
this means a single complete execution of a Weld program.
*/
using namespace std;

// allows each thread to retrieve its index
pthread_key_t id;

typedef deque<work_t *> work_queue;
typedef pthread_spinlock_t work_queue_lock;

int32_t W = 1; // number of workers, default 1
int64_t run_id = 1; // the current run ID.
pthread_t *workers = NULL;
work_queue *all_work_queues = NULL; // queue per worker
work_queue_lock *all_work_queue_locks = NULL; // lock per queue

volatile bool done = false; // if a computation is currently running, have we finished it?
volatile void *result = NULL; // stores the final result of the computation to be passed back
// to the caller

// TODO remove this, just for testing
pthread_mutex_t global_lock;

// gives the current thread's index
static inline int32_t my_id() {
  return (int32_t)reinterpret_cast<intptr_t>(pthread_getspecific(id));
}

extern "C" int32_t my_id_public() {
  return (int32_t)reinterpret_cast<intptr_t>(pthread_getspecific(id));
}

extern "C" void take_global_lock() {
  pthread_mutex_lock(&global_lock);
}

extern "C" void release_global_lock() {
  pthread_mutex_unlock(&global_lock);
}

// retrieve the result of the computation
extern "C" void *get_result() {
  if (weld_rt_get_errno(get_runid()) != 0) {
    return NULL;
  }
  return (void *)result;
}

// set the result of the computation, called from generated LLVM
extern "C" void set_result(void *res) {
  result = res;
}

extern "C" int32_t get_nworkers() {
  return W;
}

extern "C" void set_nworkers(int32_t n) {
  W = n;
}

extern "C" int64_t get_runid() {
  return run_id;
}

extern "C" void set_runid(int64_t id) {
  run_id = id;
}

extern "C" void weld_abort_thread() {
    pthread_exit(NULL);
}

static inline void set_nest(work_t *task) {
  assert(task->full_task);
  vector<int64_t> idxs;
  vector<int64_t> task_ids;
  idxs.push_back(task->cur_idx);
  task_ids.push_back(task->task_id);
  work_t *cur = task->cont;
  int32_t nest_len = 1;
  while (cur != NULL) {
    idxs.push_back(cur->cur_idx);
    // subtract 1 because the conts give us the continuations and we want the
    // task_id's of the loop bodies before the continuations
    task_ids.push_back(cur->task_id - 1);
    cur = cur->cont;
    nest_len++;
  }
  task->nest_idxs = (int64_t *)weld_rt_malloc(get_runid(), sizeof(int64_t) * nest_len);
  task->nest_task_ids = (int64_t *)weld_rt_malloc(get_runid(), sizeof(int64_t) * nest_len);
  task->nest_len = nest_len;
  // we want the outermost idxs to be the "high-order bits" when we do a comparison of task nests
  reverse_copy(idxs.begin(), idxs.end(), task->nest_idxs);
  reverse_copy(task_ids.begin(), task_ids.end(), task->nest_task_ids);
}

static inline void set_full_task(work_t *task) {
  // possible for task to already be full, e.g. if the head task from a start_loop call is queued
  // but stolen before it can be executed (the thief tries to set_full_task a second time)
  if (task->full_task) {
    return;
  }
  task->full_task = true;
  set_nest(task);
}

// attempt to steal from back of the queue of a random victim
// should be called when own work queue empty
static inline bool try_steal() {
  int32_t victim = rand() % W;
  if (!pthread_spin_trylock(all_work_queue_locks + victim)) {
    work_queue *its_work_queue = all_work_queues + victim;
    if (its_work_queue->empty()) {
      pthread_spin_unlock(all_work_queue_locks + victim);
      return false;
    } else {
      work_t *popped = its_work_queue->back();
      its_work_queue->pop_back();
      pthread_spin_unlock(all_work_queue_locks + victim);
      set_full_task(popped);
      pthread_spin_lock((all_work_queue_locks + my_id()));
      (all_work_queues + my_id())->push_front(popped);
      pthread_spin_unlock((all_work_queue_locks + my_id()));
      return true;
    }
  } else {
    return false;
  }
}

// called once task function returns
// decrease the dependency count of the continuation, run the continuation
// if necessary, or signal the end of the computation if we are done
static inline void finish_task(work_t *task) {
  if (task->cont == NULL) {
    if (!task->continued) {
      // if this task has no continuation and there was no for loop to end it,
      // the computation is over
      done = true;
    }
    weld_rt_free(get_runid(), task->data);
  } else {
    int32_t previous = __sync_fetch_and_sub(&task->cont->deps, 1);
    if (previous == 1) {
      // run the continuation since we are the last dependency
      set_full_task(task->cont);
      pthread_spin_lock((all_work_queue_locks + my_id()));
      (all_work_queues + my_id())->push_front(task->cont);
      pthread_spin_unlock((all_work_queue_locks + my_id()));
      // we are the last sibling with this data, so we can free it
      weld_rt_free(get_runid(), task->data);
    }
    if (task->full_task) {
      weld_rt_free(get_runid(), task->nest_idxs);
      weld_rt_free(get_runid(), task->nest_task_ids);
    }
  }
  weld_rt_free(get_runid(), task);
}

// set the continuation of w to cont and increment cont
// dependency count
static inline void set_cont(work_t *w, work_t *cont) {
  w->cont = cont;
  __sync_fetch_and_add(&cont->deps, 1);
}

// called from generated code to schedule a for loop with the given body and continuation
// the data pointers store the closures for the body and continuation
// lower and upper give the iteration range for the loop
// w is the currently executing task
extern "C" void pl_start_loop(work_t *w, void *body_data, void *cont_data, void (*body)(work_t*),
  void (*cont)(work_t*), int64_t lower, int64_t upper, int32_t grain_size) {
  work_t *body_task = (work_t *)weld_rt_malloc(get_runid(), sizeof(work_t));
  memset(body_task, 0, sizeof(work_t));
  body_task->data = body_data;
  body_task->fp = body;
  body_task->lower = lower;
  body_task->upper = upper;
  body_task->cur_idx = lower;
  body_task->task_id = w->task_id + 1;
  body_task->grain_size = grain_size;
  work_t *cont_task = (work_t *)weld_rt_malloc(get_runid(), sizeof(work_t));
  memset(cont_task, 0, sizeof(work_t));
  cont_task->data = cont_data;
  cont_task->fp = cont;
  cont_task->cur_idx = w->cur_idx;
  // ensures continuation and all descendants have greater task ID than body
  cont_task->task_id = w->task_id + 2;
  set_cont(body_task, cont_task);
  if (w != NULL) {
    if (w->cont != NULL) {
      // inherit the current task's continuation
      set_cont(cont_task, w->cont);
    } else {
      // this task has no continuation, but it has been effectively
      // continued by this loop so we don't want to end the computation
      // when this task completes
      w->continued = true;
    }
  }
  set_full_task(body_task);

  pthread_spin_lock((all_work_queue_locks + my_id()));
  (all_work_queues + my_id())->push_front(body_task);
  pthread_spin_unlock((all_work_queue_locks + my_id()));
}

static inline work_t *clone_task(work_t *task) {
  work_t *clone = (work_t *)weld_rt_malloc(get_runid(), sizeof(work_t));
  memcpy(clone, task, sizeof(work_t));
  clone->full_task = false;
  return clone;
}

// repeatedly break off the second half of the task into a new task
// until the task's size in iterations drops below a certain threshold
static inline void split_task(work_t *task) {
  while (task->upper - task->lower > task->grain_size) {
    work_t *last_half = clone_task(task);
    int64_t mid = (task->lower + task->upper) / 2;
    task->upper = mid;
    last_half->lower = mid;
    last_half->cur_idx = mid;
    // task must have non-NULL cont if it has non-zero number of iterations and therefore
    // is a loop body
    set_cont(last_half, task->cont);
    pthread_spin_lock((all_work_queue_locks + my_id()));
    (all_work_queues + my_id())->push_front(last_half);
    pthread_spin_unlock((all_work_queue_locks + my_id()));
  }
}

// keeps executing items from the work queue until it is empty
static inline void work_loop() {
  while (true) {
    pthread_spin_lock((all_work_queue_locks + my_id()));
    if ((all_work_queues + my_id())->empty()) {
      pthread_spin_unlock((all_work_queue_locks + my_id()));
      return;
    } else {
      work_t *popped = (all_work_queues + my_id())->front();
      (all_work_queues + my_id())->pop_front();
      pthread_spin_unlock((all_work_queue_locks + my_id()));
      split_task(popped);
      // Exit the thread if there's an error.
      // We don't need to worry about freeing here; the runtime will
      // free all allocated memory as long as it is allocated with
      // `weld_rt_malloc` or `weld_rt_realloc`.
      if (weld_rt_get_errno(get_runid()) != 0) {
        weld_abort_thread();
      }
      popped->fp(popped);
      finish_task(popped);
    }
  }
}

static void *thread_func(void *data) {
  intptr_t tid = reinterpret_cast<intptr_t>(data);
  pthread_setspecific(id, (void *)tid);

#ifndef __APPLE__
  // pin thread to CPU
  cpu_set_t set;
  CPU_ZERO(&set);
  CPU_SET(tid, &set);
  if (sched_setaffinity(0, sizeof(set), &set) == -1) {
    printf("unable to set affinity for thread %d\n", tid);
  }
#endif

  int iters = 0;

  // this work_loop call is needed to complete any work items that are initially on the queue
  work_loop();
  while (!done) {
    if (try_steal()) {
      iters = 0;
      work_loop();
    } else {
      // If this thread is stalling, periodically check for errors.
      iters++;
      if (iters > 1000000) {
        if (weld_rt_get_errno(get_runid()) != 0) {
          return NULL;
        }
        iters = 0;
      }
    }
  }
  return NULL;
}

// cleanup old computation state and reset to initial values so that
// another computation can be run
static inline void cleanup_computation_state() {
  pthread_key_delete(id);
  pthread_mutex_destroy(&global_lock);

  for (int32_t i = 0; i < W; i++) {
    pthread_spin_destroy(all_work_queue_locks + i);
    all_work_queues[i].clear();
  }

  delete [] all_work_queue_locks;
  delete [] all_work_queues;
  delete [] workers;

  done = false;
}

// start up parallel runtime and kick off threads running thread_func
// block until the computation is complete
// once execute() returns all state has been reset and it is safe to run
// another computation
extern "C" void execute(void (*run)(work_t*), void *data) {
  workers = new pthread_t[W];
  all_work_queue_locks = new work_queue_lock[W];
  all_work_queues = new work_queue[W];

  work_t *run_task = (work_t *)weld_rt_malloc(get_runid(), sizeof(work_t));
  memset(run_task, 0, sizeof(work_t));
  run_task->data = data;
  run_task->fp = run;
  // this initial task can be thought of as a continuation
  set_full_task(run_task);
  all_work_queues[0].push_front(run_task);

  for (int32_t i = 0; i < W; i++) {
    pthread_spin_init(all_work_queue_locks + i, 0);
  }

  pthread_key_create(&id, NULL);
  pthread_mutex_init(&global_lock, NULL);

  for (int32_t i = 0; i < W; i++) {
    pthread_create(workers + i, NULL, &thread_func, reinterpret_cast<void *>(i));
  }

  for (int32_t i = 0; i < W; i++) {
    pthread_join(workers[i], NULL);
  }

  cleanup_computation_state();
}
<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/memory/p9_mss_draminit_training.C $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file p9_mss_draminit_training.C
/// @brief Train dram
///
// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com>
// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 2
// *HWP Consumed by: FSP:HB

#include <fapi2.H>
#include <mss.H>

#include <p9_mss_draminit_training.H>

using fapi2::TARGET_TYPE_MCBIST;
using fapi2::TARGET_TYPE_MCA;

extern "C"
{
    ///
    /// @brief Train dram
    /// @param[in] i_target, the McBIST of the ports of the dram you're training
    /// @param[in] i_special_training, optional CAL_STEP_ENABLE override. Used in sim, debug
    /// @return FAPI2_RC_SUCCESS iff ok
    ///
    fapi2::ReturnCode p9_mss_draminit_training( const fapi2::Target<TARGET_TYPE_MCBIST>& i_target,
            const uint16_t i_special_training )
    {
        fapi2::buffer<uint16_t> l_cal_steps_enabled = i_special_training;

        FAPI_INF("Start draminit training");

        uint8_t l_reset_disable = 0;
        FAPI_TRY( mss::draminit_reset_disable(l_reset_disable) );

        // Configure the CCS engine.
        {
            fapi2::buffer<uint64_t> l_ccs_config;

            FAPI_TRY( mss::ccs::read_mode(i_target, l_ccs_config) );

            // It's unclear if we want to run with this true or false. Right now (10/15) this
            // has to be false. Shelton was unclear if this should be on or off in general BRS
            mss::ccs::stop_on_err(i_target, l_ccs_config, false);
            mss::ccs::ue_disable(i_target, l_ccs_config, false);
            mss::ccs::copy_cke_to_spare_cke(i_target, l_ccs_config, true);

            // Hm. Centaur sets this up for the longest duration possible. Can we do better?
            mss::ccs::cal_count(i_target, l_ccs_config, ~0, ~0);

#ifndef JIM_SAYS_TURN_OFF_ECC
            mss::ccs::disable_ecc(i_target, l_ccs_config);
#endif
            FAPI_TRY( mss::ccs::write_mode(i_target, l_ccs_config) );
        }

        // Clean out any previous calibration results, set bad-bits and configure the ranks.
        FAPI_DBG("MCA's on this McBIST: %d", i_target.getChildren<TARGET_TYPE_MCA>().size());

        for( auto p : i_target.getChildren<TARGET_TYPE_MCA>())
        {
            mss::ccs::program<TARGET_TYPE_MCBIST, TARGET_TYPE_MCA> l_program;

            // Setup a series of register probes which we'll see during the polling loop
            l_program.iv_probes =
            {
                // One block for each DP16
                {p, "wr_cntr_status0 (dp16 0)", MCA_DDRPHY_DP16_WR_CNTR_STATUS0_P0_0},
                {p, "wr_cntr_status1 (dp16 0)", MCA_DDRPHY_DP16_WR_CNTR_STATUS1_P0_0},
                {p, "wr_cntr_status2 (dp16 0)", MCA_DDRPHY_DP16_WR_CNTR_STATUS2_P0_0},
                {p, "wr_lvl_status (dp16 0)",   MCA_DDRPHY_DP16_WR_LVL_STATUS0_P0_0},

                {p, "wr_cntr_status0 (dp16 1)", MCA_DDRPHY_DP16_WR_CNTR_STATUS0_P0_1},
                {p, "wr_cntr_status1 (dp16 1)", MCA_DDRPHY_DP16_WR_CNTR_STATUS1_P0_1},
                {p, "wr_cntr_status2 (dp16 1)", MCA_DDRPHY_DP16_WR_CNTR_STATUS2_P0_1},
                {p, "wr_lvl_status (dp16 1)",   MCA_DDRPHY_DP16_WR_LVL_STATUS0_P0_1},

                {p, "wr_cntr_status0 (dp16 2)", MCA_DDRPHY_DP16_WR_CNTR_STATUS0_P0_2},
                {p, "wr_cntr_status1 (dp16 2)", MCA_DDRPHY_DP16_WR_CNTR_STATUS1_P0_2},
                {p, "wr_cntr_status2 (dp16 2)", MCA_DDRPHY_DP16_WR_CNTR_STATUS2_P0_2},
                {p, "wr_lvl_status (dp16 2)",   MCA_DDRPHY_DP16_WR_LVL_STATUS0_P0_2},

                {p, "wr_cntr_status0 (dp16 3)", MCA_DDRPHY_DP16_WR_CNTR_STATUS0_P0_3},
                {p, "wr_cntr_status1 (dp16 3)", MCA_DDRPHY_DP16_WR_CNTR_STATUS1_P0_3},
                {p, "wr_cntr_status2 (dp16 3)", MCA_DDRPHY_DP16_WR_CNTR_STATUS2_P0_3},
                {p, "wr_lvl_status (dp16 3)",   MCA_DDRPHY_DP16_WR_LVL_STATUS0_P0_3},

                {p, "wr_cntr_status0 (dp16 4)", MCA_DDRPHY_DP16_WR_CNTR_STATUS0_P0_4},
                {p, "wr_cntr_status1 (dp16 4)", MCA_DDRPHY_DP16_WR_CNTR_STATUS1_P0_4},
                {p, "wr_cntr_status2 (dp16 4)", MCA_DDRPHY_DP16_WR_CNTR_STATUS2_P0_4},
                {p, "wr_lvl_status (dp16 4)",   MCA_DDRPHY_DP16_WR_LVL_STATUS0_P0_4},
            };

            // Delays in the CCS instruction ARR1 for training are supposed to be 0xFFFF,
            // and we're supposed to poll for the done or timeout bit. But we don't want
            // to wait 0xFFFF cycles before we start polling - that's too long. So we put
            // in a best-guess of how long to wait. This, in a perfect world, would be the
            // time it takes one rank to train one training algorithm times the number of
            // ranks we're going to train. We fail-safe as worst-case we simply poll the
            // register too much - so we can tune this as we learn more.
            l_program.iv_poll.iv_initial_sim_delay = mss::DELAY_100US;
            l_program.iv_poll.iv_initial_sim_delay = 200;
            l_program.iv_poll.iv_poll_count = 0xFFFF;

            // Returned from set_rank_pairs, it tells us how many rank pairs
            // we configured on this port.
            std::vector<uint64_t> l_pairs;

#ifdef CAL_STATUS_DOESNT_REPORT_COMPLETE
            // This isn't correct - shouldn't be setting
            static const uint64_t CLEAR_CAL_COMPLETE = 0x000000000000F000;
            FAPI_TRY( mss::putScom(p, MCA_DDRPHY_PC_INIT_CAL_STATUS_P0, CLEAR_CAL_COMPLETE) );
#endif
            FAPI_TRY( mss::putScom(p, MCA_DDRPHY_PC_INIT_CAL_ERROR_P0, 0) );
            FAPI_TRY( mss::putScom(p, MCA_DDRPHY_PC_INIT_CAL_CONFIG0_P0, 0) );

            // Disable port fails as it doesn't appear the MC handles initial cal timeouts
            // correctly (cal_length.) BRS, see conversation with Brad Michael
            FAPI_TRY( mss::change_port_fail_disable(p, mss::ON ) );

            // The following registers must be configured to the correct operating environment:

            // Unclear, can probably be 0's for sim BRS
            // • Section 5.2.5.10 SEQ ODT Write Configuration {0-3} on page 422

            FAPI_TRY( mss::reset_seq_config0(p) );
            FAPI_TRY( mss::reset_seq_rd_wr_data(p) );

            FAPI_TRY( mss::reset_odt_config(p) );

            // These are reset in phy_scominit
            // • Section 5.2.6.1 WC Configuration 0 Register on page 434
            // • Section 5.2.6.2 WC Configuration 1 Register on page 436
            // • Section 5.2.6.3 WC Configuration 2 Register on page 438

            // Get our rank pairs.
            FAPI_TRY( mss::get_rank_pairs(p, l_pairs) );

            // Setup the config register
            //
            // Grab the attribute which contains the information on what cal steps we should run
            // if the i_specal_training bits have not been specified.
            if (i_special_training == 0)
            {
                FAPI_TRY( mss::cal_step_enable(p, l_cal_steps_enabled) );
            }

            FAPI_DBG("cal steps enabled: 0x%x special training: 0x%x", l_cal_steps_enabled, i_special_training);

            // Check to see if we're supposed to reset the delay values before starting training
            // don't reset if we're running special training - assumes there's a checkpoint which has valid state.
            if ((l_reset_disable == fapi2::ENUM_ATTR_MSS_DRAMINIT_RESET_DISABLE_ENABLE) && (i_special_training == 0))
            {
                FAPI_TRY( mss::dp16::reset_delay_values(p, l_pairs) );
            }

            FAPI_DBG("generating calibration CCS instructions: %d rank-pairs", l_pairs.size());

            // For each rank pair we need to calibrate, pop a ccs instruction in an array and execute it.
            // NOTE: IF YOU CALIBRATE MORE THAN ONE RANK PAIR PER CCS PROGRAM, MAKE SURE TO CHANGE
            // THE PROCESSING OF THE ERRORS. (it's hard to figure out which DIMM failed, too) BRS.
            for (auto rp : l_pairs)
            {
                auto l_inst = mss::ccs::initial_cal_command<TARGET_TYPE_MCBIST>(rp);

                FAPI_DBG("exeecuting training CCS instruction: 0x%llx, 0x%llx", l_inst.arr0, l_inst.arr1);
                l_program.iv_instructions.push_back(l_inst);

                // We need to figure out how long to wait before we start polling. Each cal step has an expected
                // duration, so for each cal step which was enabled, we update the CCS program.
                FAPI_TRY( mss::cal_timer_setup(p, l_program.iv_poll, l_cal_steps_enabled) );
                FAPI_TRY( mss::setup_cal_config(p, rp, l_cal_steps_enabled) );

                // In the event of an init cal hang, CCS_STATQ(2) will assert and CCS_STATQ(3:5) = “001” to indicate a
                // timeout. Otherwise, if calibration completes, FW should inspect DDRPHY_FIR_REG bits (50) and (58)
                // for signs of a calibration error. If either bit is on, then the DDRPHY_PC_INIT_CAL_ERROR register
                // should be polled to determine which calibration step failed.

                // If we got a cal timeout, or another CCS error just leave now. If we got success, check the error
                // bits for a cal failure. We'll return the proper ReturnCode so all we need to do is FAPI_TRY.
                FAPI_TRY( mss::ccs::execute(i_target, l_program, p) );
                FAPI_TRY( mss::process_initial_cal_errors(p) );
            }
        }

    fapi_try_exit:
        FAPI_INF("End draminit training");
        return fapi2::current_err;
    }
}
<commit_msg>Add phy control error checking, clean up dp16, apb<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/memory/p9_mss_draminit_training.C $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file p9_mss_draminit_training.C
/// @brief Train dram
///
// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com>
// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 2
// *HWP Consumed by: FSP:HB

#include <fapi2.H>
#include <mss.H>

#include <p9_mss_draminit_training.H>

using fapi2::TARGET_TYPE_MCBIST;
using fapi2::TARGET_TYPE_MCA;

extern "C"
{
    ///
    /// @brief Train dram
    /// @param[in] i_target, the McBIST of the ports of the dram you're training
    /// @param[in] i_special_training, optional CAL_STEP_ENABLE override. Used in sim, debug
    /// @return FAPI2_RC_SUCCESS iff ok
    ///
    fapi2::ReturnCode p9_mss_draminit_training( const fapi2::Target<TARGET_TYPE_MCBIST>& i_target,
            const uint16_t i_special_training )
    {
        fapi2::buffer<uint16_t> l_cal_steps_enabled = i_special_training;

        FAPI_INF("Start draminit training");

        uint8_t l_reset_disable = 0;
        FAPI_TRY( mss::draminit_reset_disable(l_reset_disable) );

        // Configure the CCS engine.
        {
            fapi2::buffer<uint64_t> l_ccs_config;

            FAPI_TRY( mss::ccs::read_mode(i_target, l_ccs_config) );

            // It's unclear if we want to run with this true or false. Right now (10/15) this
            // has to be false. Shelton was unclear if this should be on or off in general BRS
            mss::ccs::stop_on_err(i_target, l_ccs_config, false);
            mss::ccs::ue_disable(i_target, l_ccs_config, false);
            mss::ccs::copy_cke_to_spare_cke(i_target, l_ccs_config, true);

            // Hm. Centaur sets this up for the longest duration possible. Can we do better?
            mss::ccs::cal_count(i_target, l_ccs_config, ~0, ~0);

#ifndef JIM_SAYS_TURN_OFF_ECC
            mss::ccs::disable_ecc(i_target, l_ccs_config);
#endif
            FAPI_TRY( mss::ccs::write_mode(i_target, l_ccs_config) );
        }

        // Clean out any previous calibration results, set bad-bits and configure the ranks.
        FAPI_DBG("MCA's on this McBIST: %d", i_target.getChildren<TARGET_TYPE_MCA>().size());

        for( auto p : i_target.getChildren<TARGET_TYPE_MCA>())
        {
            mss::ccs::program<TARGET_TYPE_MCBIST, TARGET_TYPE_MCA> l_program;

            // Setup a series of register probes which we'll see during the polling loop
            l_program.iv_probes =
            {
                // One block for each DP16
                {p, "wr_cntr_status0 (dp16 0)", MCA_DDRPHY_DP16_WR_CNTR_STATUS0_P0_0},
                {p, "wr_cntr_status1 (dp16 0)", MCA_DDRPHY_DP16_WR_CNTR_STATUS1_P0_0},
                {p, "wr_cntr_status2 (dp16 0)", MCA_DDRPHY_DP16_WR_CNTR_STATUS2_P0_0},
                {p, "wr_lvl_status (dp16 0)",   MCA_DDRPHY_DP16_WR_LVL_STATUS0_P0_0},

                {p, "wr_cntr_status0 (dp16 1)", MCA_DDRPHY_DP16_WR_CNTR_STATUS0_P0_1},
                {p, "wr_cntr_status1 (dp16 1)", MCA_DDRPHY_DP16_WR_CNTR_STATUS1_P0_1},
                {p, "wr_cntr_status2 (dp16 1)", MCA_DDRPHY_DP16_WR_CNTR_STATUS2_P0_1},
                {p, "wr_lvl_status (dp16 1)",   MCA_DDRPHY_DP16_WR_LVL_STATUS0_P0_1},

                {p, "wr_cntr_status0 (dp16 2)", MCA_DDRPHY_DP16_WR_CNTR_STATUS0_P0_2},
                {p, "wr_cntr_status1 (dp16 2)", MCA_DDRPHY_DP16_WR_CNTR_STATUS1_P0_2},
                {p, "wr_cntr_status2 (dp16 2)", MCA_DDRPHY_DP16_WR_CNTR_STATUS2_P0_2},
                {p, "wr_lvl_status (dp16 2)",   MCA_DDRPHY_DP16_WR_LVL_STATUS0_P0_2},

                {p, "wr_cntr_status0 (dp16 3)", MCA_DDRPHY_DP16_WR_CNTR_STATUS0_P0_3},
                {p, "wr_cntr_status1 (dp16 3)", MCA_DDRPHY_DP16_WR_CNTR_STATUS1_P0_3},
                {p, "wr_cntr_status2 (dp16 3)", MCA_DDRPHY_DP16_WR_CNTR_STATUS2_P0_3},
                {p, "wr_lvl_status (dp16 3)",   MCA_DDRPHY_DP16_WR_LVL_STATUS0_P0_3},

                {p, "wr_cntr_status0 (dp16 4)", MCA_DDRPHY_DP16_WR_CNTR_STATUS0_P0_4},
                {p, "wr_cntr_status1 (dp16 4)", MCA_DDRPHY_DP16_WR_CNTR_STATUS1_P0_4},
                {p, "wr_cntr_status2 (dp16 4)", MCA_DDRPHY_DP16_WR_CNTR_STATUS2_P0_4},
                {p, "wr_lvl_status (dp16 4)",   MCA_DDRPHY_DP16_WR_LVL_STATUS0_P0_4},
            };

            // Delays in the CCS instruction ARR1 for training are supposed to be 0xFFFF,
            // and we're supposed to poll for the done or timeout bit. But we don't want
            // to wait 0xFFFF cycles before we start polling - that's too long. So we put
            // in a best-guess of how long to wait. This, in a perfect world, would be the
            // time it takes one rank to train one training algorithm times the number of
            // ranks we're going to train. We fail-safe as worst-case we simply poll the
            // register too much - so we can tune this as we learn more.
            l_program.iv_poll.iv_initial_sim_delay = mss::DELAY_100US;
            l_program.iv_poll.iv_initial_sim_delay = 200;
            l_program.iv_poll.iv_poll_count = 0xFFFF;

            // Returned from set_rank_pairs, it tells us how many rank pairs
            // we configured on this port.
            std::vector<uint64_t> l_pairs;

#ifdef CAL_STATUS_DOESNT_REPORT_COMPLETE
            // This isn't correct - shouldn't be setting
            static const uint64_t CLEAR_CAL_COMPLETE = 0x000000000000F000;
            FAPI_TRY( mss::putScom(p, MCA_DDRPHY_PC_INIT_CAL_STATUS_P0, CLEAR_CAL_COMPLETE) );
#endif
            FAPI_TRY( mss::putScom(p, MCA_DDRPHY_PC_INIT_CAL_ERROR_P0, 0) );
            FAPI_TRY( mss::putScom(p, MCA_DDRPHY_PC_INIT_CAL_CONFIG0_P0, 0) );

            // Disable port fails as it doesn't appear the MC handles initial cal timeouts
            // correctly (cal_length.) BRS, see conversation with Brad Michael
            FAPI_TRY( mss::change_port_fail_disable(p, mss::ON ) );

            // The following registers must be configured to the correct operating environment:

            // Unclear, can probably be 0's for sim BRS
            // • Section 5.2.5.10 SEQ ODT Write Configuration {0-3} on page 422

            FAPI_TRY( mss::reset_seq_config0(p) );
            FAPI_TRY( mss::reset_seq_rd_wr_data(p) );

            FAPI_TRY( mss::reset_odt_config(p) );

            // These are reset in phy_scominit
            // • Section 5.2.6.1 WC Configuration 0 Register on page 434
            // • Section 5.2.6.2 WC Configuration 1 Register on page 436
            // • Section 5.2.6.3 WC Configuration 2 Register on page 438

            // Get our rank pairs.
            FAPI_TRY( mss::get_rank_pairs(p, l_pairs) );

            // Setup the config register
            //
            // Grab the attribute which contains the information on what cal steps we should run
            // if the i_specal_training bits have not been specified.
            if (i_special_training == 0)
            {
                FAPI_TRY( mss::cal_step_enable(p, l_cal_steps_enabled) );
            }

            FAPI_DBG("cal steps enabled: 0x%x special training: 0x%x", l_cal_steps_enabled, i_special_training);

            // Check to see if we're supposed to reset the delay values before starting training
            // don't reset if we're running special training - assumes there's a checkpoint which has valid state.
            if ((l_reset_disable == fapi2::ENUM_ATTR_MSS_DRAMINIT_RESET_DISABLE_ENABLE) && (i_special_training == 0))
            {
                FAPI_TRY( mss::dp16<TARGET_TYPE_MCA>().reset_delay_values(p, l_pairs) );
            }

            FAPI_DBG("generating calibration CCS instructions: %d rank-pairs", l_pairs.size());

            // For each rank pair we need to calibrate, pop a ccs instruction in an array and execute it.
            // NOTE: IF YOU CALIBRATE MORE THAN ONE RANK PAIR PER CCS PROGRAM, MAKE SURE TO CHANGE
            // THE PROCESSING OF THE ERRORS. (it's hard to figure out which DIMM failed, too) BRS.
            for (auto rp : l_pairs)
            {
                auto l_inst = mss::ccs::initial_cal_command<TARGET_TYPE_MCBIST>(rp);

                FAPI_DBG("exeecuting training CCS instruction: 0x%llx, 0x%llx", l_inst.arr0, l_inst.arr1);
                l_program.iv_instructions.push_back(l_inst);

                // We need to figure out how long to wait before we start polling. Each cal step has an expected
                // duration, so for each cal step which was enabled, we update the CCS program.
                FAPI_TRY( mss::cal_timer_setup(p, l_program.iv_poll, l_cal_steps_enabled) );
                FAPI_TRY( mss::setup_cal_config(p, rp, l_cal_steps_enabled) );

                // In the event of an init cal hang, CCS_STATQ(2) will assert and CCS_STATQ(3:5) = “001” to indicate a
                // timeout. Otherwise, if calibration completes, FW should inspect DDRPHY_FIR_REG bits (50) and (58)
                // for signs of a calibration error. If either bit is on, then the DDRPHY_PC_INIT_CAL_ERROR register
                // should be polled to determine which calibration step failed.

                // If we got a cal timeout, or another CCS error just leave now. If we got success, check the error
                // bits for a cal failure. We'll return the proper ReturnCode so all we need to do is FAPI_TRY.
                FAPI_TRY( mss::ccs::execute(i_target, l_program, p) );
                FAPI_TRY( mss::process_initial_cal_errors(p) );
            }
        }

    fapi_try_exit:
        FAPI_INF("End draminit training");
        return fapi2::current_err;
    }
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2008-2013 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#include "Precompiled.h"
#include "Context.h"

#include "DebugNew.h"

namespace Urho3D
{

void RemoveNamedAttribute(HashMap<ShortStringHash, Vector<AttributeInfo> >& attributes, ShortStringHash objectType, const char* name)
{
    HashMap<ShortStringHash, Vector<AttributeInfo> >::Iterator i = attributes.Find(objectType);
    if (i == attributes.End())
        return;

    Vector<AttributeInfo>& infos = i->second_;

    for (Vector<AttributeInfo>::Iterator j = infos.Begin(); j != infos.End(); ++j)
    {
        if (!j->name_.Compare(name, true))
        {
            infos.Erase(j);
            break;
        }
    }

    // If the vector became empty, erase the object type from the map
    if (infos.Empty())
        attributes.Erase(i);
}

Context::Context() :
    eventHandler_(0)
{
    #ifdef ANDROID
    // Always reset the random seed on Android, as the Urho3D library might not be unloaded between runs
    SetRandomSeed(1);
    #endif
}

Context::~Context()
{
    subsystems_.Clear();
    factories_.Clear();
}

SharedPtr<Object> Context::CreateObject(ShortStringHash objectType)
{
    HashMap<ShortStringHash, SharedPtr<ObjectFactory> >::ConstIterator i = factories_.Find(objectType);
    if (i != factories_.End())
        return i->second_->CreateObject();
    else
        return SharedPtr<Object>();
}

void Context::RegisterFactory(ObjectFactory* factory)
{
    if (!factory)
        return;

    factories_[factory->GetType()] = factory;
}

void Context::RegisterComponentFactory(ObjectFactory* factory, const char* category)
{
    RegisterFactory(factory);
    if (String::CStringLength(category))
        componentCategories_[category].Push(factory->GetType());
}

void Context::RegisterSubsystem(Object* object)
{
    if (!object)
        return;

    subsystems_[object->GetType()] = object;
}

void Context::RemoveSubsystem(ShortStringHash objectType)
{
    HashMap<ShortStringHash, SharedPtr<Object> >::Iterator i = subsystems_.Find(objectType);
    if (i != subsystems_.End())
        subsystems_.Erase(i);
}

void Context::RegisterAttribute(ShortStringHash objectType, const AttributeInfo& attr)
{
    // None or Pointer types can not be supported
    if (attr.type_ == VAR_NONE || attr.type_ == VAR_PTR)
        return;

    attributes_[objectType].Push(attr);

    if (attr.mode_ & AM_NET)
        networkAttributes_[objectType].Push(attr);
}

void Context::RemoveAttribute(ShortStringHash objectType, const char* name)
{
    RemoveNamedAttribute(attributes_, objectType, name);
    RemoveNamedAttribute(networkAttributes_, objectType, name);
}

void Context::UpdateAttributeDefaultValue(ShortStringHash objectType, const char* name, const Variant& defaultValue)
{
    AttributeInfo* info = GetAttribute(objectType, name);
    if (info)
        info->defaultValue_ = defaultValue;
}

void Context::CopyBaseAttributes(ShortStringHash baseType, ShortStringHash derivedType)
{
    const Vector<AttributeInfo>* baseAttributes = GetAttributes(baseType);
    if (baseAttributes)
    {
        for (unsigned i = 0; i < baseAttributes->Size(); ++i)
        {
            const AttributeInfo& attr = baseAttributes->At(i);
            attributes_[derivedType].Push(attr);
            if (attr.mode_ & AM_NET)
                networkAttributes_[derivedType].Push(attr);
        }
    }
}

Object* Context::GetSubsystem(ShortStringHash type) const
{
    HashMap<ShortStringHash, SharedPtr<Object> >::ConstIterator i = subsystems_.Find(type);
    if (i != subsystems_.End())
        return i->second_;
    else
        return 0;
}

Object* Context::GetEventSender() const
{
    if (!eventSenders_.Empty())
        return eventSenders_.Back();
    else
        return 0;
}

const String& Context::GetTypeName(ShortStringHash type) const
{
    // Search factories to find the hash-to-name mapping
    HashMap<ShortStringHash, SharedPtr<ObjectFactory> >::ConstIterator i = factories_.Find(type);
    return i != factories_.End() ? i->second_->GetTypeName() : String::EMPTY;
}

AttributeInfo* Context::GetAttribute(ShortStringHash objectType, const char* name)
{
    HashMap<ShortStringHash, Vector<AttributeInfo> >::Iterator i = attributes_.Find(objectType);
    if (i == attributes_.End())
        return 0;

    Vector<AttributeInfo>& infos = i->second_;

    for (Vector<AttributeInfo>::Iterator j = infos.Begin(); j != infos.End(); ++j)
    {
        if (!j->name_.Compare(name, true))
            return &(*j);
    }

    return 0;
}

void Context::AddEventReceiver(Object* receiver, StringHash eventType)
{
    eventReceivers_[eventType].Insert(receiver);
}

void Context::AddEventReceiver(Object* receiver, Object* sender, StringHash eventType)
{
    specificEventReceivers_[sender][eventType].Insert(receiver);
}

void Context::RemoveEventSender(Object* sender)
{
    HashMap<Object*, HashMap<StringHash, HashSet<Object*> > >::Iterator i = specificEventReceivers_.Find(sender);
    if (i != specificEventReceivers_.End())
    {
        for (HashMap<StringHash, HashSet<Object*> >::Iterator j = i->second_.Begin(); j != i->second_.End(); ++j)
        {
            for (HashSet<Object*>::Iterator k = j->second_.Begin(); k != j->second_.End(); ++k)
                (*k)->RemoveEventSender(sender);
        }
        specificEventReceivers_.Erase(i);
    }
}

void Context::RemoveEventReceiver(Object* receiver, StringHash eventType)
{
    HashSet<Object*>* group = GetEventReceivers(eventType);
    if (group)
        group->Erase(receiver);
}

void Context::RemoveEventReceiver(Object* receiver, Object* sender, StringHash eventType)
{
    HashSet<Object*>* group = GetEventReceivers(sender, eventType);
    if (group)
        group->Erase(receiver);
}

void Context::EndSendEvent()
{
    eventSenders_.Pop();
}

}
<commit_msg>Added nullcheck.<commit_after>//
// Copyright (c) 2008-2013 the Urho3D project.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#include "Precompiled.h"
#include "Context.h"

#include "DebugNew.h"

namespace Urho3D
{

void RemoveNamedAttribute(HashMap<ShortStringHash, Vector<AttributeInfo> >& attributes, ShortStringHash objectType, const char* name)
{
    HashMap<ShortStringHash, Vector<AttributeInfo> >::Iterator i = attributes.Find(objectType);
    if (i == attributes.End())
        return;

    Vector<AttributeInfo>& infos = i->second_;

    for (Vector<AttributeInfo>::Iterator j = infos.Begin(); j != infos.End(); ++j)
    {
        if (!j->name_.Compare(name, true))
        {
            infos.Erase(j);
            break;
        }
    }

    // If the vector became empty, erase the object type from the map
    if (infos.Empty())
        attributes.Erase(i);
}

Context::Context() :
    eventHandler_(0)
{
    #ifdef ANDROID
    // Always reset the random seed on Android, as the Urho3D library might not be unloaded between runs
    SetRandomSeed(1);
    #endif
}

Context::~Context()
{
    subsystems_.Clear();
    factories_.Clear();
}

SharedPtr<Object> Context::CreateObject(ShortStringHash objectType)
{
    HashMap<ShortStringHash, SharedPtr<ObjectFactory> >::ConstIterator i = factories_.Find(objectType);
    if (i != factories_.End())
        return i->second_->CreateObject();
    else
        return SharedPtr<Object>();
}

void Context::RegisterFactory(ObjectFactory* factory)
{
    if (!factory)
        return;

    factories_[factory->GetType()] = factory;
}

void Context::RegisterComponentFactory(ObjectFactory* factory, const char* category)
{
    if (!factory)
        return;

    RegisterFactory(factory);
    if (String::CStringLength(category))
        componentCategories_[category].Push(factory->GetType());
}

void Context::RegisterSubsystem(Object* object)
{
    if (!object)
        return;

    subsystems_[object->GetType()] = object;
}

void Context::RemoveSubsystem(ShortStringHash objectType)
{
    HashMap<ShortStringHash, SharedPtr<Object> >::Iterator i = subsystems_.Find(objectType);
    if (i != subsystems_.End())
        subsystems_.Erase(i);
}

void Context::RegisterAttribute(ShortStringHash objectType, const AttributeInfo& attr)
{
    // None or Pointer types can not be supported
    if (attr.type_ == VAR_NONE || attr.type_ == VAR_PTR)
        return;

    attributes_[objectType].Push(attr);

    if (attr.mode_ & AM_NET)
        networkAttributes_[objectType].Push(attr);
}

void Context::RemoveAttribute(ShortStringHash objectType, const char* name)
{
    RemoveNamedAttribute(attributes_, objectType, name);
    RemoveNamedAttribute(networkAttributes_, objectType, name);
}

void Context::UpdateAttributeDefaultValue(ShortStringHash objectType, const char* name, const Variant& defaultValue)
{
    AttributeInfo* info = GetAttribute(objectType, name);
    if (info)
        info->defaultValue_ = defaultValue;
}

void Context::CopyBaseAttributes(ShortStringHash baseType, ShortStringHash derivedType)
{
    const Vector<AttributeInfo>* baseAttributes = GetAttributes(baseType);
    if (baseAttributes)
    {
        for (unsigned i = 0; i < baseAttributes->Size(); ++i)
        {
            const AttributeInfo& attr = baseAttributes->At(i);
            attributes_[derivedType].Push(attr);
            if (attr.mode_ & AM_NET)
                networkAttributes_[derivedType].Push(attr);
        }
    }
}

Object* Context::GetSubsystem(ShortStringHash type) const
{
    HashMap<ShortStringHash, SharedPtr<Object> >::ConstIterator i = subsystems_.Find(type);
    if (i != subsystems_.End())
        return i->second_;
    else
        return 0;
}

Object* Context::GetEventSender() const
{
    if (!eventSenders_.Empty())
        return eventSenders_.Back();
    else
        return 0;
}

const String& Context::GetTypeName(ShortStringHash type) const
{
    // Search factories to find the hash-to-name mapping
    HashMap<ShortStringHash, SharedPtr<ObjectFactory> >::ConstIterator i = factories_.Find(type);
    return i != factories_.End() ? i->second_->GetTypeName() : String::EMPTY;
}

AttributeInfo* Context::GetAttribute(ShortStringHash objectType, const char* name)
{
    HashMap<ShortStringHash, Vector<AttributeInfo> >::Iterator i = attributes_.Find(objectType);
    if (i == attributes_.End())
        return 0;

    Vector<AttributeInfo>& infos = i->second_;

    for (Vector<AttributeInfo>::Iterator j = infos.Begin(); j != infos.End(); ++j)
    {
        if (!j->name_.Compare(name, true))
            return &(*j);
    }

    return 0;
}

void Context::AddEventReceiver(Object* receiver, StringHash eventType)
{
    eventReceivers_[eventType].Insert(receiver);
}

void Context::AddEventReceiver(Object* receiver, Object* sender, StringHash eventType)
{
    specificEventReceivers_[sender][eventType].Insert(receiver);
}

void Context::RemoveEventSender(Object* sender)
{
    HashMap<Object*, HashMap<StringHash, HashSet<Object*> > >::Iterator i = specificEventReceivers_.Find(sender);
    if (i != specificEventReceivers_.End())
    {
        for (HashMap<StringHash, HashSet<Object*> >::Iterator j = i->second_.Begin(); j != i->second_.End(); ++j)
        {
            for (HashSet<Object*>::Iterator k = j->second_.Begin(); k != j->second_.End(); ++k)
                (*k)->RemoveEventSender(sender);
        }
        specificEventReceivers_.Erase(i);
    }
}

void Context::RemoveEventReceiver(Object* receiver, StringHash eventType)
{
    HashSet<Object*>* group = GetEventReceivers(eventType);
    if (group)
        group->Erase(receiver);
}

void Context::RemoveEventReceiver(Object* receiver, Object* sender, StringHash eventType)
{
    HashSet<Object*>* group = GetEventReceivers(sender, eventType);
    if (group)
        group->Erase(receiver);
}

void Context::EndSendEvent()
{
    eventSenders_.Pop();
}

}
<|endoftext|>