<commit_before>#include "ParamsSM30.h"
#include <ShaderMeta/SM30ShaderMeta.h>
#include <Utils.h>
#include <Logging.h>
#include <iostream>

//???!!!TODO:
//???skip loading shader metadata when creating effect in DEM? all relevant metadata is already copied to the effect.
//!!!struct member duplicates const meta, make membermeta = offset + constmeta?

struct CSM30EffectMeta
{
	// Param ID -> shader type mask + metadata
	//std::map<CStrID, std::pair<uint8_t, CSM30BufferMeta>> Buffers;
	//std::map<CStrID, std::pair<uint8_t, CSM30StructMeta>> Structs;
	std::vector<CSM30BufferMeta> Buffers;
	std::vector<CSM30StructMeta> Structs;
	std::map<CStrID, std::pair<uint8_t, CSM30ConstMeta>> Consts;
	std::map<CStrID, std::pair<uint8_t, CSM30RsrcMeta>> Resources;
	std::map<CStrID, std::pair<uint8_t, CSM30SamplerMeta>> Samplers;

	// Cache for faster search
	std::set<uint32_t> UsedFloat4;
	std::set<uint32_t> UsedInt4;
	std::set<uint32_t> UsedBool;
	std::set<uint32_t> UsedResources;
	std::set<uint32_t> UsedSamplers;

	// For logging
	std::string PrintableName;
};

static bool CheckConstRegisterOverlapping(const CSM30ConstMeta& Param, const CSM30EffectMeta& Other)
{
	const auto& OtherRegs =
		(Param.RegisterSet == RS_Float4) ? Other.UsedFloat4 :
		(Param.RegisterSet == RS_Int4) ? Other.UsedInt4 :
		Other.UsedBool;

	// Fail if overlapping detected. Overlapping data can't be correctly set from effects.
	const auto RegisterEnd = Param.RegisterStart + Param.ElementRegisterCount * Param.ElementCount;
	for (uint32_t r = Param.RegisterStart; r < RegisterEnd; ++r)
		if (OtherRegs.find(r) != OtherRegs.cend())
			return false;

	return true;
}
//---------------------------------------------------------------------

static bool CheckSamplerRegisterOverlapping(const CSM30SamplerMeta& Param, const CSM30EffectMeta& Other)
{
	// Fail if overlapping detected. Overlapping data can't be correctly set from effects.
	for (uint32_t r = Param.RegisterStart; r < Param.RegisterStart + Param.RegisterCount; ++r)
		if (Other.UsedSamplers.find(r) != Other.UsedSamplers.cend())
			return false;

	return true;
}
//---------------------------------------------------------------------

static bool ProcessConstant(uint8_t ShaderType, CSM30ConstMeta& Param, const CSM30ShaderMeta& SrcMeta, CSM30EffectMeta& TargetMeta, const CSM30EffectMeta& OtherMeta1, const CSM30EffectMeta& OtherMeta2, const CContext& Ctx)
{
	CStrID ID(Param.Name.c_str());

	// Check if this param was already added from another shader
	auto ItPrev = TargetMeta.Consts.find(ID);
	if (ItPrev == TargetMeta.Consts.cend())
	{
		// Check register overlapping, because it prevents correct param setup from effects

		if (!CheckConstRegisterOverlapping(Param, OtherMeta1))
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " constant '" << ID.CStr() << "' uses a register used by " << OtherMeta1.PrintableName << " params" << Ctx.LineEnd;
			return false;
		}

		if (!CheckConstRegisterOverlapping(Param, OtherMeta2))
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " constant '" << ID.CStr() << "' uses a register used by " << OtherMeta2.PrintableName << " params" << Ctx.LineEnd;
			return false;
		}

		// Remember used registers for future overlap checks

		auto& Regs =
			(Param.RegisterSet == RS_Float4) ? TargetMeta.UsedFloat4 :
			(Param.RegisterSet == RS_Int4) ? TargetMeta.UsedInt4 :
			TargetMeta.UsedBool;

		const auto RegisterEnd = Param.RegisterStart + Param.ElementRegisterCount * Param.ElementCount;
		for (uint32_t r = Param.RegisterStart; r < RegisterEnd; ++r)
			Regs.insert(r);

		// Copy necessary metadata

		auto It = TargetMeta.Consts.emplace(ID, std::make_pair(ShaderType, std::move(Param))).first;
		CSM30ConstMeta& NewConst = It->second.second;

		const auto& Buffer = SrcMeta.Buffers[Param.BufferIndex];
		auto ItBuffer = std::find(TargetMeta.Buffers.cbegin(), TargetMeta.Buffers.cend(), Buffer);
		if (ItBuffer != TargetMeta.Buffers.cend())
		{
			// The same buffer found, reference it
			NewConst.BufferIndex = std::distance(TargetMeta.Buffers.cbegin(), ItBuffer);
		}
		else
		{
			// Copy new buffer to metadata
			TargetMeta.Buffers.push_back(Buffer);
			NewConst.BufferIndex = static_cast<uint32_t>(TargetMeta.Buffers.size());
		}

		if (NewConst.StructIndex != static_cast<uint32_t>(-1))
		{
			// find exactly the same struct or copy struct meta
			//!!!when the same shader, can check struct index!
			// fix struct index
		}
	}
	else
	{
		// The same param found, check compatibility
		const auto& ExistingMeta = ItPrev->second.second;
		if (ExistingMeta != Param)
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " constant '" << ID.CStr() << "' is not compatible across all tech shaders" << Ctx.LineEnd;
			return false;
		}

		// Compare containing constant buffers
		// NB: we perform this check despite D3D9 has no real constant buffers, because even with
		// the same set of registers we still can have different constant buffer layouts. Using
		// the constant with foreign constant buffer can be incompatible with the shader. D3D9 is
		// legacy and this situation will probably never happen, so I don't spend much time on solution.
		if (TargetMeta.Buffers[ExistingMeta.BufferIndex] != SrcMeta.Buffers[Param.BufferIndex])
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " param '" << ID.CStr() << "' containing constant buffer is not compatible across all tech shaders" << Ctx.LineEnd;
			return false;
		}

		// Extend shader mask
		ItPrev->second.first |= ShaderType;
	}

	return true;
}
//---------------------------------------------------------------------

static bool ProcessResource(uint8_t ShaderType, CSM30RsrcMeta& Param, CSM30EffectMeta& TargetMeta, const CSM30EffectMeta& OtherMeta1, const CSM30EffectMeta& OtherMeta2, const CContext& Ctx)
{
	CStrID ID(Param.Name.c_str());

	// Check if this param was already added from another shader
	auto ItPrev = TargetMeta.Resources.find(ID);
	if (ItPrev == TargetMeta.Resources.cend())
	{
		if (OtherMeta1.UsedResources.find(Param.Register) != OtherMeta1.UsedResources.cend())
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " resource '" << ID.CStr() << "' uses a register used by " << OtherMeta1.PrintableName << " params" << Ctx.LineEnd;
			return false;
		}

		if (OtherMeta2.UsedResources.find(Param.Register) != OtherMeta2.UsedResources.cend())
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " resource '" << ID.CStr() << "' uses a register used by " << OtherMeta2.PrintableName << " params" << Ctx.LineEnd;
			return false;
		}

		TargetMeta.UsedResources.insert(Param.Register);

		TargetMeta.Resources.emplace(ID, std::make_pair(ShaderType, std::move(Param)));
	}
	else
	{
		// The same param found, check compatibility
		const auto& ExistingMeta = ItPrev->second.second;
		if (ExistingMeta != Param)
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " resource '" << ID.CStr() << "' is not compatible across all tech shaders" << Ctx.LineEnd;
			return false;
		}

		// Extend shader mask
		ItPrev->second.first |= ShaderType;
	}

	return true;
}
//---------------------------------------------------------------------

static bool ProcessSampler(uint8_t ShaderType, CSM30SamplerMeta& Param, CSM30EffectMeta& TargetMeta, const CSM30EffectMeta& OtherMeta1, const CSM30EffectMeta& OtherMeta2, const CContext& Ctx)
{
	CStrID ID(Param.Name.c_str());

	// Check if this param was already added from another shader
	auto ItPrev = TargetMeta.Samplers.find(ID);
	if (ItPrev == TargetMeta.Samplers.cend())
	{
		if (!CheckSamplerRegisterOverlapping(Param, OtherMeta1))
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " sampler '" << ID.CStr() << "' uses a register used by " << OtherMeta1.PrintableName << " params" << Ctx.LineEnd;
			return false;
		}

		if (!CheckSamplerRegisterOverlapping(Param, OtherMeta2))
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " sampler '" << ID.CStr() << "' uses a register used by " << OtherMeta2.PrintableName << " params" << Ctx.LineEnd;
			return false;
		}

		for (uint32_t r = Param.RegisterStart; r < Param.RegisterStart + Param.RegisterCount; ++r)
			TargetMeta.UsedSamplers.insert(r);

		TargetMeta.Samplers.emplace(ID, std::make_pair(ShaderType, std::move(Param)));
	}
	else
	{
		// The same param found, check compatibility
		const auto& ExistingMeta = ItPrev->second.second;
		if (ExistingMeta != Param)
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " sampler '" << ID.CStr() << "' is not compatible across all tech shaders" << Ctx.LineEnd;
			return false;
		}

		// Extend shader mask
		ItPrev->second.first |= ShaderType;
	}

	return true;
}
//---------------------------------------------------------------------

bool WriteParameterTablesForDX9C(std::ostream& Stream, const std::vector<CTechnique>& Techs, const CContext& Ctx)
{
	CSM30EffectMeta GlobalMeta, MaterialMeta;
	GlobalMeta.PrintableName = "Global";
	MaterialMeta.PrintableName = "Material";

	for (const auto& Tech : Techs)
	{
		CSM30EffectMeta TechMeta;
		TechMeta.PrintableName = "Tech";

		for (CStrID PassID : Tech.Passes)
		{
			const CRenderState& RS = Ctx.RSCache.at(PassID);

			CStrID ShaderIDs[] = { RS.VertexShader, RS.PixelShader, RS.GeometryShader, RS.HullShader, RS.DomainShader };

			for (uint8_t ShaderType = ShaderType_Vertex; ShaderType < ShaderType_COUNT; ++ShaderType)
			{
				const CStrID ShaderID = ShaderIDs[ShaderType];
				if (!ShaderID) continue;

				CSM30ShaderMeta ShaderMeta;
				{
					const CShaderData& ShaderData = Ctx.ShaderCache.at(ShaderID);
					membuf MetaBuffer(ShaderData.MetaBytes.get(), ShaderData.MetaBytes.get() + ShaderData.MetaByteCount);
					std::istream MetaStream(&MetaBuffer);
					MetaStream >> ShaderMeta;
				}

				for (auto& Const : ShaderMeta.Consts)
				{
					if (Ctx.LogVerbosity >= EVerbosity::Debug)
						std::cout << "Shader '" << ShaderID.CStr() << "' constant " << Const.Name << Ctx.LineEnd;

					CStrID ID(Const.Name.c_str());
					if (Ctx.GlobalParams.find(ID) != Ctx.GlobalParams.cend())
					{
						ProcessConstant(ShaderType, Const, ShaderMeta, GlobalMeta, MaterialMeta, TechMeta, Ctx);
					}
					else
					{
						auto ItMtl = Ctx.MaterialParams.find(ID);
						if (ItMtl != Ctx.MaterialParams.cend())
						{
							ProcessConstant(ShaderType, Const, ShaderMeta, MaterialMeta, GlobalMeta, TechMeta, Ctx);

							// process defaults - null or depends on const type
							//???!!!support defaults for structs? CParams with fields!
						}
						else
						{
							ProcessConstant(ShaderType, Const, ShaderMeta, TechMeta, GlobalMeta, MaterialMeta, Ctx);
						}
					}
				}

				for (auto& Rsrc : ShaderMeta.Resources)
				{
					if (Ctx.LogVerbosity >= EVerbosity::Debug)
						std::cout << "Shader '" << ShaderID.CStr() << "' resource " << Rsrc.Name << Ctx.LineEnd;

					CStrID ID(Rsrc.Name.c_str());
					if (Ctx.GlobalParams.find(ID) != Ctx.GlobalParams.cend())
					{
						ProcessResource(ShaderType, Rsrc, GlobalMeta, MaterialMeta, TechMeta, Ctx);
					}
					else
					{
						auto ItMtl = Ctx.MaterialParams.find(ID);
						if (ItMtl != Ctx.MaterialParams.cend())
						{
							ProcessResource(ShaderType, Rsrc, MaterialMeta, GlobalMeta, TechMeta, Ctx);

							// process defaults - null, string, CStrID
						}
						else
						{
							ProcessResource(ShaderType, Rsrc, TechMeta, GlobalMeta, MaterialMeta, Ctx);
						}
					}
				}

				for (auto& Sampler : ShaderMeta.Samplers)
				{
					if (Ctx.LogVerbosity >= EVerbosity::Debug)
						std::cout << "Shader '" << ShaderID.CStr() << "' sampler " << Sampler.Name << Ctx.LineEnd;

					CStrID ID(Sampler.Name.c_str());
					if (Ctx.GlobalParams.find(ID) != Ctx.GlobalParams.cend())
					{
						ProcessSampler(ShaderType, Sampler, GlobalMeta, MaterialMeta, TechMeta, Ctx);
					}
					else
					{
						auto ItMtl = Ctx.MaterialParams.find(ID);
						if (ItMtl != Ctx.MaterialParams.cend())
						{
							ProcessSampler(ShaderType, Sampler, MaterialMeta, GlobalMeta, TechMeta, Ctx);

							// process defaults - null or CParams
						}
						else
						{
							ProcessSampler(ShaderType, Sampler, TechMeta, GlobalMeta, MaterialMeta, Ctx);
						}
					}
				}
			}
		}
	}

	return false;
}
//---------------------------------------------------------------------
<commit_msg>Fixed new buffer index<commit_after>#include "ParamsSM30.h"
#include <ShaderMeta/SM30ShaderMeta.h>
#include <Utils.h>
#include <Logging.h>
#include <iostream>

//???!!!TODO:
//???skip loading shader metadata when creating effect in DEM? all relevant metadata is already copied to the effect.
//!!!struct member duplicates const meta, make membermeta = offset + constmeta?

struct CSM30EffectMeta
{
	// Param ID -> shader type mask + metadata
	//std::map<CStrID, std::pair<uint8_t, CSM30BufferMeta>> Buffers;
	//std::map<CStrID, std::pair<uint8_t, CSM30StructMeta>> Structs;
	std::vector<CSM30BufferMeta> Buffers;
	std::vector<CSM30StructMeta> Structs;
	std::map<CStrID, std::pair<uint8_t, CSM30ConstMeta>> Consts;
	std::map<CStrID, std::pair<uint8_t, CSM30RsrcMeta>> Resources;
	std::map<CStrID, std::pair<uint8_t, CSM30SamplerMeta>> Samplers;

	// Cache for faster search
	std::set<uint32_t> UsedFloat4;
	std::set<uint32_t> UsedInt4;
	std::set<uint32_t> UsedBool;
	std::set<uint32_t> UsedResources;
	std::set<uint32_t> UsedSamplers;

	// For logging
	std::string PrintableName;
};

static bool CheckConstRegisterOverlapping(const CSM30ConstMeta& Param, const CSM30EffectMeta& Other)
{
	const auto& OtherRegs =
		(Param.RegisterSet == RS_Float4) ? Other.UsedFloat4 :
		(Param.RegisterSet == RS_Int4) ? Other.UsedInt4 :
		Other.UsedBool;

	// Fail if overlapping detected. Overlapping data can't be correctly set from effects.
	const auto RegisterEnd = Param.RegisterStart + Param.ElementRegisterCount * Param.ElementCount;
	for (uint32_t r = Param.RegisterStart; r < RegisterEnd; ++r)
		if (OtherRegs.find(r) != OtherRegs.cend())
			return false;

	return true;
}
//---------------------------------------------------------------------

static bool CheckSamplerRegisterOverlapping(const CSM30SamplerMeta& Param, const CSM30EffectMeta& Other)
{
	// Fail if overlapping detected. Overlapping data can't be correctly set from effects.
	for (uint32_t r = Param.RegisterStart; r < Param.RegisterStart + Param.RegisterCount; ++r)
		if (Other.UsedSamplers.find(r) != Other.UsedSamplers.cend())
			return false;

	return true;
}
//---------------------------------------------------------------------

static bool ProcessConstant(uint8_t ShaderType, CSM30ConstMeta& Param, const CSM30ShaderMeta& SrcMeta, CSM30EffectMeta& TargetMeta, const CSM30EffectMeta& OtherMeta1, const CSM30EffectMeta& OtherMeta2, const CContext& Ctx)
{
	CStrID ID(Param.Name.c_str());

	// Check if this param was already added from another shader
	auto ItPrev = TargetMeta.Consts.find(ID);
	if (ItPrev == TargetMeta.Consts.cend())
	{
		// Check register overlapping, because it prevents correct param setup from effects

		if (!CheckConstRegisterOverlapping(Param, OtherMeta1))
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " constant '" << ID.CStr() << "' uses a register used by " << OtherMeta1.PrintableName << " params" << Ctx.LineEnd;
			return false;
		}

		if (!CheckConstRegisterOverlapping(Param, OtherMeta2))
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " constant '" << ID.CStr() << "' uses a register used by " << OtherMeta2.PrintableName << " params" << Ctx.LineEnd;
			return false;
		}

		// Remember used registers for future overlap checks

		auto& Regs =
			(Param.RegisterSet == RS_Float4) ? TargetMeta.UsedFloat4 :
			(Param.RegisterSet == RS_Int4) ? TargetMeta.UsedInt4 :
			TargetMeta.UsedBool;

		const auto RegisterEnd = Param.RegisterStart + Param.ElementRegisterCount * Param.ElementCount;
		for (uint32_t r = Param.RegisterStart; r < RegisterEnd; ++r)
			Regs.insert(r);

		// Copy necessary metadata

		auto It = TargetMeta.Consts.emplace(ID, std::make_pair(ShaderType, std::move(Param))).first;
		CSM30ConstMeta& NewConst = It->second.second;

		const auto& Buffer = SrcMeta.Buffers[Param.BufferIndex];
		auto ItBuffer = std::find(TargetMeta.Buffers.cbegin(), TargetMeta.Buffers.cend(), Buffer);
		if (ItBuffer != TargetMeta.Buffers.cend())
		{
			// The same buffer found, reference it
			NewConst.BufferIndex = std::distance(TargetMeta.Buffers.cbegin(), ItBuffer);
		}
		else
		{
			// Copy new buffer to metadata
			TargetMeta.Buffers.push_back(Buffer);
			NewConst.BufferIndex = static_cast<uint32_t>(TargetMeta.Buffers.size() - 1);
		}

		if (NewConst.StructIndex != static_cast<uint32_t>(-1))
		{
			// find exactly the same struct or copy struct meta
			//!!!when the same shader, can check struct index!
			// fix struct index
		}
	}
	else
	{
		// The same param found, check compatibility
		const auto& ExistingMeta = ItPrev->second.second;
		if (ExistingMeta != Param)
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " constant '" << ID.CStr() << "' is not compatible across all tech shaders" << Ctx.LineEnd;
			return false;
		}

		// Compare containing constant buffers
		// NB: we perform this check despite D3D9 has no real constant buffers, because even with
		// the same set of registers we still can have different constant buffer layouts. Using
		// the constant with foreign constant buffer can be incompatible with the shader. D3D9 is
		// legacy and this situation will probably never happen, so I don't spend much time on solution.
		if (TargetMeta.Buffers[ExistingMeta.BufferIndex] != SrcMeta.Buffers[Param.BufferIndex])
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " param '" << ID.CStr() << "' containing constant buffer is not compatible across all tech shaders" << Ctx.LineEnd;
			return false;
		}

		// Extend shader mask
		ItPrev->second.first |= ShaderType;
	}

	return true;
}
//---------------------------------------------------------------------

static bool ProcessResource(uint8_t ShaderType, CSM30RsrcMeta& Param, CSM30EffectMeta& TargetMeta, const CSM30EffectMeta& OtherMeta1, const CSM30EffectMeta& OtherMeta2, const CContext& Ctx)
{
	CStrID ID(Param.Name.c_str());

	// Check if this param was already added from another shader
	auto ItPrev = TargetMeta.Resources.find(ID);
	if (ItPrev == TargetMeta.Resources.cend())
	{
		if (OtherMeta1.UsedResources.find(Param.Register) != OtherMeta1.UsedResources.cend())
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " resource '" << ID.CStr() << "' uses a register used by " << OtherMeta1.PrintableName << " params" << Ctx.LineEnd;
			return false;
		}

		if (OtherMeta2.UsedResources.find(Param.Register) != OtherMeta2.UsedResources.cend())
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " resource '" << ID.CStr() << "' uses a register used by " << OtherMeta2.PrintableName << " params" << Ctx.LineEnd;
			return false;
		}

		TargetMeta.UsedResources.insert(Param.Register);

		TargetMeta.Resources.emplace(ID, std::make_pair(ShaderType, std::move(Param)));
	}
	else
	{
		// The same param found, check compatibility
		const auto& ExistingMeta = ItPrev->second.second;
		if (ExistingMeta != Param)
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " resource '" << ID.CStr() << "' is not compatible across all tech shaders" << Ctx.LineEnd;
			return false;
		}

		// Extend shader mask
		ItPrev->second.first |= ShaderType;
	}

	return true;
}
//---------------------------------------------------------------------

static bool ProcessSampler(uint8_t ShaderType, CSM30SamplerMeta& Param, CSM30EffectMeta& TargetMeta, const CSM30EffectMeta& OtherMeta1, const CSM30EffectMeta& OtherMeta2, const CContext& Ctx)
{
	CStrID ID(Param.Name.c_str());

	// Check if this param was already added from another shader
	auto ItPrev = TargetMeta.Samplers.find(ID);
	if (ItPrev == TargetMeta.Samplers.cend())
	{
		if (!CheckSamplerRegisterOverlapping(Param, OtherMeta1))
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " sampler '" << ID.CStr() << "' uses a register used by " << OtherMeta1.PrintableName << " params" << Ctx.LineEnd;
			return false;
		}

		if (!CheckSamplerRegisterOverlapping(Param, OtherMeta2))
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " sampler '" << ID.CStr() << "' uses a register used by " << OtherMeta2.PrintableName << " params" << Ctx.LineEnd;
			return false;
		}

		for (uint32_t r = Param.RegisterStart; r < Param.RegisterStart + Param.RegisterCount; ++r)
			TargetMeta.UsedSamplers.insert(r);

		TargetMeta.Samplers.emplace(ID, std::make_pair(ShaderType, std::move(Param)));
	}
	else
	{
		// The same param found, check compatibility
		const auto& ExistingMeta = ItPrev->second.second;
		if (ExistingMeta != Param)
		{
			if (Ctx.LogVerbosity >= EVerbosity::Errors)
				std::cout << TargetMeta.PrintableName << " sampler '" << ID.CStr() << "' is not compatible across all tech shaders" << Ctx.LineEnd;
			return false;
		}

		// Extend shader mask
		ItPrev->second.first |= ShaderType;
	}

	return true;
}
//---------------------------------------------------------------------

bool WriteParameterTablesForDX9C(std::ostream& Stream, const std::vector<CTechnique>& Techs, const CContext& Ctx)
{
	CSM30EffectMeta GlobalMeta, MaterialMeta;
	GlobalMeta.PrintableName = "Global";
	MaterialMeta.PrintableName = "Material";

	for (const auto& Tech : Techs)
	{
		CSM30EffectMeta TechMeta;
		TechMeta.PrintableName = "Tech";

		for (CStrID PassID : Tech.Passes)
		{
			const CRenderState& RS = Ctx.RSCache.at(PassID);

			CStrID ShaderIDs[] = { RS.VertexShader, RS.PixelShader, RS.GeometryShader, RS.HullShader, RS.DomainShader };

			for (uint8_t ShaderType = ShaderType_Vertex; ShaderType < ShaderType_COUNT; ++ShaderType)
			{
				const CStrID ShaderID = ShaderIDs[ShaderType];
				if (!ShaderID) continue;

				CSM30ShaderMeta ShaderMeta;
				{
					const CShaderData& ShaderData = Ctx.ShaderCache.at(ShaderID);
					membuf MetaBuffer(ShaderData.MetaBytes.get(), ShaderData.MetaBytes.get() + ShaderData.MetaByteCount);
					std::istream MetaStream(&MetaBuffer);
					MetaStream >> ShaderMeta;
				}

				for (auto& Const : ShaderMeta.Consts)
				{
					if (Ctx.LogVerbosity >= EVerbosity::Debug)
						std::cout << "Shader '" << ShaderID.CStr() << "' constant " << Const.Name << Ctx.LineEnd;

					CStrID ID(Const.Name.c_str());
					if (Ctx.GlobalParams.find(ID) != Ctx.GlobalParams.cend())
					{
						ProcessConstant(ShaderType, Const, ShaderMeta, GlobalMeta, MaterialMeta, TechMeta, Ctx);
					}
					else
					{
						auto ItMtl = Ctx.MaterialParams.find(ID);
						if (ItMtl != Ctx.MaterialParams.cend())
						{
							ProcessConstant(ShaderType, Const, ShaderMeta, MaterialMeta, GlobalMeta, TechMeta, Ctx);

							// process defaults - null or depends on const type
							//???!!!support defaults for structs? CParams with fields!
						}
						else
						{
							ProcessConstant(ShaderType, Const, ShaderMeta, TechMeta, GlobalMeta, MaterialMeta, Ctx);
						}
					}
				}

				for (auto& Rsrc : ShaderMeta.Resources)
				{
					if (Ctx.LogVerbosity >= EVerbosity::Debug)
						std::cout << "Shader '" << ShaderID.CStr() << "' resource " << Rsrc.Name << Ctx.LineEnd;

					CStrID ID(Rsrc.Name.c_str());
					if (Ctx.GlobalParams.find(ID) != Ctx.GlobalParams.cend())
					{
						ProcessResource(ShaderType, Rsrc, GlobalMeta, MaterialMeta, TechMeta, Ctx);
					}
					else
					{
						auto ItMtl = Ctx.MaterialParams.find(ID);
						if (ItMtl != Ctx.MaterialParams.cend())
						{
							ProcessResource(ShaderType, Rsrc, MaterialMeta, GlobalMeta, TechMeta, Ctx);

							// process defaults - null, string, CStrID
						}
						else
						{
							ProcessResource(ShaderType, Rsrc, TechMeta, GlobalMeta, MaterialMeta, Ctx);
						}
					}
				}

				for (auto& Sampler : ShaderMeta.Samplers)
				{
					if (Ctx.LogVerbosity >= EVerbosity::Debug)
						std::cout << "Shader '" << ShaderID.CStr() << "' sampler " << Sampler.Name << Ctx.LineEnd;

					CStrID ID(Sampler.Name.c_str());
					if (Ctx.GlobalParams.find(ID) != Ctx.GlobalParams.cend())
					{
						ProcessSampler(ShaderType, Sampler, GlobalMeta, MaterialMeta, TechMeta, Ctx);
					}
					else
					{
						auto ItMtl = Ctx.MaterialParams.find(ID);
						if (ItMtl != Ctx.MaterialParams.cend())
						{
							ProcessSampler(ShaderType, Sampler, MaterialMeta, GlobalMeta, TechMeta, Ctx);

							// process defaults - null or CParams
						}
						else
						{
							ProcessSampler(ShaderType, Sampler, TechMeta, GlobalMeta, MaterialMeta, Ctx);
						}
					}
				}
			}
		}
	}

	return false;
}
//---------------------------------------------------------------------
<|endoftext|>


<commit_before>// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you 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.

#include "GetElementTagNameCommandHandler.h"
#include "errorcodes.h"
#include "../Browser.h"
#include "../Element.h"
#include "../IECommandExecutor.h"

namespace webdriver {

GetElementTagNameCommandHandler::GetElementTagNameCommandHandler(void) {
}

GetElementTagNameCommandHandler::~GetElementTagNameCommandHandler(void) {
}

void GetElementTagNameCommandHandler::ExecuteInternal(
    const IECommandExecutor& executor,
    const ParametersMap& command_parameters,
    Response* response) {
  ParametersMap::const_iterator id_parameter_iterator = command_parameters.find("id");
  if (id_parameter_iterator == command_parameters.end()) {
    response->SetErrorResponse(ERROR_INVALID_ARGUMENT, "Missing parameter in URL: id");
    return;
  } else {
    std::string element_id = id_parameter_iterator->second.asString();
    ElementHandle element_wrapper;
    int status_code = this->GetElement(executor, element_id, &element_wrapper);
    if (status_code == WD_SUCCESS) {
      std::string return_value = element_wrapper->GetTagName();
      response->SetSuccessResponse(return_value);
      return;
    } else {
      response->SetErrorResponse(ERROR_STALE_ELEMENT_REFERENCE, "Element is no longer valid");
      return;
    }
  }
}

} // namespace webdriver
<commit_msg>Updating IE driver element tag name command handler to be spec compliant<commit_after>// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you 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.

#include "GetElementTagNameCommandHandler.h"
#include "errorcodes.h"
#include "../Browser.h"
#include "../Element.h"
#include "../IECommandExecutor.h"

namespace webdriver {

GetElementTagNameCommandHandler::GetElementTagNameCommandHandler(void) {
}

GetElementTagNameCommandHandler::~GetElementTagNameCommandHandler(void) {
}

void GetElementTagNameCommandHandler::ExecuteInternal(
    const IECommandExecutor& executor,
    const ParametersMap& command_parameters,
    Response* response) {
  ParametersMap::const_iterator id_parameter_iterator = command_parameters.find("id");
  if (id_parameter_iterator == command_parameters.end()) {
    response->SetErrorResponse(ERROR_INVALID_ARGUMENT, "Missing parameter in URL: id");
    return;
  } else {
    std::string element_id = id_parameter_iterator->second.asString();

    BrowserHandle browser_wrapper;
    int status_code = executor.GetCurrentBrowser(&browser_wrapper);
    if (status_code != WD_SUCCESS) {
      response->SetErrorResponse(ERROR_NO_SUCH_WINDOW, "Unable to get browser");
      return;
    }

    ElementHandle element_wrapper;
    status_code = this->GetElement(executor, element_id, &element_wrapper);
    if (status_code == WD_SUCCESS) {
      std::string return_value = element_wrapper->GetTagName();
      response->SetSuccessResponse(return_value);
      return;
    } else {
      response->SetErrorResponse(ERROR_STALE_ELEMENT_REFERENCE, "Element is no longer valid");
      return;
    }
  }
}

} // namespace webdriver
<|endoftext|>


<commit_before>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

#include <Arduino.h>
#include <TheThingsNetwork.h>

#define debugPrintLn(...) { if (debugStream) debugStream->println(__VA_ARGS__); }
#define debugPrint(...) { if (debugStream) debugStream->print(__VA_ARGS__); }

#define TTN_HEX_CHAR_TO_NIBBLE(c) ((c >= 'A') ? (c - 'A' + 0x0A) : (c - '0'))
#define TTN_HEX_PAIR_TO_BYTE(h, l) ((TTN_HEX_CHAR_TO_NIBBLE(h) << 4) + TTN_HEX_CHAR_TO_NIBBLE(l))

String TheThingsNetwork::readLine() {
  while (true) {
    String line = modemStream->readStringUntil('\n');
    if (line.length() > 0) {
      return line.substring(0, line.length() - 1);
    }
  }
}

String TheThingsNetwork::readValue(String cmd) {
  while(modemStream->available()) {
    modemStream->read();
  }

  modemStream->println(cmd);
  return readLine();
}

bool TheThingsNetwork::sendCommand(String cmd) {
  debugPrint(F("Sending: "));
  debugPrintLn(cmd);

  String line = readValue(cmd);

  if (line != "ok") {
    debugPrint(F("Response is not OK: "));
    debugPrintLn(line);
    return false;
  }

  return true;
}

bool TheThingsNetwork::sendCommand(String cmd, String value) {
  size_t l = value.length();
  byte buf[l];
  value.getBytes(buf, l);

  return sendCommand(cmd, buf, l);
}

char btohexa_high(unsigned char b) {
  b >>= 4;
  return (b > 0x9u) ? b + 'A' - 10 : b + '0';
}

char btohexa_low(unsigned char b) {
  b &= 0x0F;
  return (b > 0x9u) ? b + 'A' - 10 : b + '0';
}

bool TheThingsNetwork::sendCommand(String cmd, const byte *buf, size_t length) {
  String str = cmd + " ";

  for (int32_t i = 0; i < length; i++) {
    str += btohexa_high(buf[i]);
    str += btohexa_low(buf[i]);
  }

  return sendCommand(str);
}

void TheThingsNetwork::reset(bool adr) {
  String version = readValue(F("sys reset"));
  model = version.substring(0, version.indexOf(' '));
  debugPrint(F("Version is "));
  debugPrint(version);
  debugPrint(F(", model is "));
  debugPrintLn(model);

  String devEui = readValue(F("sys get hweui"));
  String str = "";
  str.concat(F("mac set deveui "));
  str.concat(devEui);
  sendCommand(str);

  str = "";
  str.concat(F("mac set adr "));
  if(adr){
    str.concat(F("on"));
  } else {
    str.concat(F("off"));
  }
  sendCommand(str);
}

void TheThingsNetwork::onMessage(void (*cb)(const byte* payload, size_t length, int8_t port)) {
  this->messageCallback = cb;
}

bool TheThingsNetwork::personalize(const byte devAddr[4], const byte nwkSKey[16], const byte appSKey[16]) {
  reset();
  sendCommand(F("mac set devaddr"), devAddr, 4);
  sendCommand(F("mac set nwkskey"), nwkSKey, 16);
  sendCommand(F("mac set appskey"), appSKey, 16);
  return personalize();
}

bool TheThingsNetwork::personalize() {
  configureChannels(this->sf, this->fsb);
  sendCommand(F("mac join abp"));
  String response = readLine();
  if (response != F("accepted")) {
    debugPrint(F("Personalize not accepted: "));
    debugPrintLn(response);
    return false;
  }

  fillAirtimeInfo();
  debugPrint(F("Personalize accepted. Status: "));
  debugPrintLn(readValue(F("mac get status")));
  return true;
}

bool TheThingsNetwork::provision(const byte appEui[8], const byte appKey[16]) {
  sendCommand(F("mac set appeui"), appEui, 8);
  sendCommand(F("mac set appkey"), appKey, 16);
  return sendCommand(F("mac save"));
}

bool TheThingsNetwork::join(int8_t retries, int32_t retryDelay) {
  configureChannels(this->sf, this->fsb);
  String devEui = readValue(F("sys get hweui"));
  String str = "";
  str.concat(F("mac set deveui "));
  str.concat(devEui);
  sendCommand(str);
  while (retries != 0) {
    if (retries > 0) {
      retries--;
    }
    if (!sendCommand(F("mac join otaa"))) {
      debugPrintLn(F("Send join command failed"));
      delay(retryDelay);
      continue;
    }
    String response = readLine();
    if (response != F("accepted")) {
      debugPrint(F("Join not accepted: "));
      debugPrintLn(response);
      delay(retryDelay);
      continue;
    }
    debugPrint(F("Join accepted. Status: "));
    debugPrintLn(readValue(F("mac get status")));
    debugPrint(F("DevAddr: "));
    debugPrintLn(readValue(F("mac get devaddr")));
    fillAirtimeInfo();
    return true;
  }
  return false;
}

bool TheThingsNetwork::join(const byte appEui[8], const byte appKey[16], int8_t retries, int32_t retryDelay) {
  reset();
  provision(appEui, appKey);
  return join(retries, retryDelay);
}

int TheThingsNetwork::sendBytes(const byte* payload, size_t length, int8_t port, bool confirm) {
  String str = "";
  str.concat(F("mac tx "));
  str.concat(confirm ? F("cnf ") : F("uncnf "));
  str.concat(port);
  if (!sendCommand(str, payload, length)) {
    debugPrintLn(F("Send command failed"));
    return -1;
  }

  String response = readLine();
  float i = this->airtime;
  trackAirtime(length);
  debugPrint(F("Airtime added: "));
  debugPrint(this->airtime - i);
  debugPrintLn(F(" s"));
  debugPrint(F("Total airtime: "));
  debugPrint(this->airtime);
  debugPrintLn(F(" s"));
  if (response == F("mac_tx_ok")) {
    debugPrintLn(F("Successful transmission"));
    return 1;
  }
  if (response.startsWith(F("mac_rx"))) {
    int8_t portEnds = response.indexOf(" ", 7);
    int8_t downlinkPort = response.substring(7, portEnds).toInt();
    String data = response.substring(portEnds + 1);
    size_t downlinkLength = data.length() / 2;
    byte downlink[64];
    for (int32_t i = 0, d = 0; i < downlinkLength; i++, d += 2) {
      downlink[i] = TTN_HEX_PAIR_TO_BYTE(data[d], data[d+1]);
    }
    debugPrint(F("Successful transmission. Received "));
    debugPrint(downlinkLength);
    debugPrintLn(F(" bytes"));
    if (this->messageCallback)
      this->messageCallback(downlink, downlinkLength, downlinkPort);
    return 2;
  }

  debugPrint(F("Unexpected response: "));
  debugPrintLn(response);
  return -10;
}

int TheThingsNetwork::poll(int8_t port, bool confirm) {
  byte payload[] = { 0x00 };
  return sendBytes(payload, 1, port, confirm);
}

void TheThingsNetwork::fillAirtimeInfo() {
  this->info.sf = 0;
  this->info.ps = 0;
  this->info.band = 0;
  this->info.header = 0;
  this->info.cr = 0;
  this->info.de = 0;

  int16_t i;
  String message = readValue(F("radio get sf"));
  for (i = 2; message[i] && i <= 3; i++) {
    this->info.sf = (this->info.sf + message[i] - 48) * 10;
  }
  this->info.sf = this->info.sf / 10;

  message = readValue(F("radio get bw"));
  for (i = 0; message[i] && i <= 2; i++) {
    this->info.band = (this->info.band + message[i] - 48) * 10;
  }
  this->info.band = this->info.band / 10;

  message = readValue(F("radio get prlen"));
  this->info.ps = this->info.ps + message[0] - 48;

  message = readValue(F("radio get crc"));
  this->info.header = message == F("on") ? 1 : 0;

  message = readValue(F("radio get cr"));
  this->info.cr = (this->info.cr + message[2] - 48);

  this->info.de = this->info.sf >= 11 ? 1 : 0;
}

void TheThingsNetwork::trackAirtime(size_t payloadSize) {
  payloadSize = 13 + payloadSize;

  float Tsym = pow(2, this->info.sf) / this->info.band;
  float Tpreamble = (this->info.ps + 4.25) * Tsym;
  int16_t payLoadSymbNb = 8 + (max(ceil((8 * payloadSize - 4 * this->info.sf + 28 + 16 - 20 * this->info.header) / (4 * (this->info.sf - 2 * this->info.de))) * (this->info.cr + 4), 0));
  float Tpayload = payLoadSymbNb * Tsym;
  float Tpacket = Tpreamble + Tpayload;
  this->airtime = this->airtime + (Tpacket / 1000);
}

void TheThingsNetwork::showStatus() {
  debugPrint(F("EUI: "));
  debugPrintLn(readValue(F("sys get hweui")));
  debugPrint(F("Battery: "));
  debugPrintLn(readValue(F("sys get vdd")));
  debugPrint(F("AppEUI: "));
  debugPrintLn(readValue(F("mac get appeui")));
  debugPrint(F("DevEUI: "));
  debugPrintLn(readValue(F("mac get deveui")));

  if (this->model == F("RN2483")) {
    debugPrint(F("Band: "));
    debugPrintLn(readValue(F("mac get band")));
  }
  debugPrint(F("Data Rate: "));
  debugPrintLn(readValue(F("mac get dr")));
  debugPrint(F("RX Delay 1: "));
  debugPrintLn(readValue(F("mac get rxdelay1")));
  debugPrint(F("RX Delay 2: "));
  debugPrintLn(readValue(F("mac get rxdelay2")));
  debugPrint(F("Total airtime: "));
  debugPrint(this->airtime);
  debugPrintLn(F(" s"));
}

void TheThingsNetwork::configureEU868(int8_t sf) {
  int8_t ch;
  int8_t dr = -1;
  int32_t freq = 867100000;
  String str = "";

  str.concat(F("mac set rx2 3 869525000"));
  sendCommand(str);
  str = "";
  for (ch = 0; ch <= 7; ch++) {
    if (ch >= 3) {
      str.concat(F("mac set ch freq "));
      str.concat(ch);
      str.concat(F(" "));
      str.concat(freq);
      sendCommand(str);
      str = "";
      str.concat(F("mac set ch drrange "));
      str.concat(ch);
      str.concat(F(" 0 5"));
      sendCommand(str);
      str = "";
      str.concat(F("mac set ch status "));
      str.concat(ch);
      str.concat(F(" on"));
      sendCommand(str);
      str = "";
      freq = freq + 200000;
    }
    str.concat(F("mac set ch dcycle "));
    str.concat(ch);
    str.concat(F(" 799"));
    sendCommand(str);
    str = "";
  }
  str.concat(F("mac set ch drrange 1 0 6"));
  sendCommand(str);
  str = "";
  str.concat(F("mac set pwridx "));
  str.concat(TTN_PWRIDX_868);
  sendCommand(str);
  switch (sf) {
    case 7:
      dr = 5;
      break;
    case 8:
      dr = 4;
      break;
    case 9:
      dr = 3;
      break;
    case 10:
      dr = 2;
      break;
    case 11:
      dr = 1;
      break;
    case 12:
      dr = 0;
      break;
    default:
      debugPrintLn(F("Invalid SF"))
      break;
  }
  if (dr > -1){
    str = "";
    str.concat(F("mac set dr "));
    str.concat(dr);
    sendCommand(str);
  }
}

void TheThingsNetwork::configureUS915(int8_t sf, int8_t fsb) {
  int8_t ch;
  int8_t dr = -1;
  String str = "";
  int8_t chLow = fsb > 0 ? (fsb - 1) * 8 : 0;
  int8_t chHigh = fsb > 0 ? chLow + 7 : 71;
  int8_t ch500 = fsb + 63;

  sendCommand(F("radio set freq 904200000"));
  str = "";
  str.concat(F("mac set pwridx "));
  str.concat(TTN_PWRIDX_915);
  sendCommand(str);
  for (ch = 0; ch < 72; ch++) {
    str = "";
    str.concat(F("mac set ch status "));
    str.concat(ch);
    if (ch == ch500 || ch <= chHigh && ch >= chLow) {
      str.concat(F(" on"));
      sendCommand(str);
      if (ch < 63) {
        str = "";
        str.concat(F("mac set ch drrange "));
        str.concat(ch);
        str.concat(F(" 0 3"));
        sendCommand(str);
        str = "";
      }
    }
    else {
      str.concat(F(" off"));
      sendCommand(str);
    }
  }
  switch (sf) {
    case 7:
      dr = 3;
      break;
    case 8:
     dr = 2;
     break;
    case 9:
      dr = 1;
      break;
    case 10:
      dr = 0;
      break;
    default:
      debugPrintLn(F("Invalid SF"))
      break;
  }
  if (dr > -1){
    str = "";
    str.concat(F("mac set dr "));
    str.concat(dr);
    sendCommand(str);
  }
}

void TheThingsNetwork::configureChannels(int8_t sf, int8_t fsb) {
  switch (this->fp) {
    case TTN_FP_EU868:
      configureEU868(sf);
      break;
    case TTN_FP_US915:
      configureUS915(sf, fsb);
      break;
    default:
      debugPrintLn("Invalid frequency plan");
      break;
  }
  String retries = "";
  retries.concat(F("mac set retx "));
  retries.concat(TTN_RETX);
  sendCommand(retries);
}

TheThingsNetwork::TheThingsNetwork(Stream& modemStream, Stream& debugStream, ttn_fp_t fp, int8_t sf, int8_t fsb) {
  this->debugStream = &debugStream;
  this->modemStream = &modemStream;
  this->fp = fp;
  this->sf = sf;
  this->fsb = fsb;
}
<commit_msg>changed types<commit_after>// Copyright © 2016 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

#include <Arduino.h>
#include <TheThingsNetwork.h>

#define debugPrintLn(...) { if (debugStream) debugStream->println(__VA_ARGS__); }
#define debugPrint(...) { if (debugStream) debugStream->print(__VA_ARGS__); }

#define TTN_HEX_CHAR_TO_NIBBLE(c) ((c >= 'A') ? (c - 'A' + 0x0A) : (c - '0'))
#define TTN_HEX_PAIR_TO_BYTE(h, l) ((TTN_HEX_CHAR_TO_NIBBLE(h) << 4) + TTN_HEX_CHAR_TO_NIBBLE(l))

String TheThingsNetwork::readLine() {
  while (true) {
    String line = modemStream->readStringUntil('\n');
    if (line.length() > 0) {
      return line.substring(0, line.length() - 1);
    }
  }
}

String TheThingsNetwork::readValue(String cmd) {
  while(modemStream->available()) {
    modemStream->read();
  }

  modemStream->println(cmd);
  return readLine();
}

bool TheThingsNetwork::sendCommand(String cmd) {
  debugPrint(F("Sending: "));
  debugPrintLn(cmd);

  String line = readValue(cmd);

  if (line != "ok") {
    debugPrint(F("Response is not OK: "));
    debugPrintLn(line);
    return false;
  }

  return true;
}

bool TheThingsNetwork::sendCommand(String cmd, String value) {
  size_t l = value.length();
  byte buf[l];
  value.getBytes(buf, l);

  return sendCommand(cmd, buf, l);
}

char btohexa_high(unsigned char b) {
  b >>= 4;
  return (b > 0x9u) ? b + 'A' - 10 : b + '0';
}

char btohexa_low(unsigned char b) {
  b &= 0x0F;
  return (b > 0x9u) ? b + 'A' - 10 : b + '0';
}

bool TheThingsNetwork::sendCommand(String cmd, const byte *buf, size_t length) {
  String str = cmd + " ";

  for (size_t i = 0; i < length; i++) {
    str += btohexa_high(buf[i]);
    str += btohexa_low(buf[i]);
  }

  return sendCommand(str);
}

void TheThingsNetwork::reset(bool adr) {
  String version = readValue(F("sys reset"));
  model = version.substring(0, version.indexOf(' '));
  debugPrint(F("Version is "));
  debugPrint(version);
  debugPrint(F(", model is "));
  debugPrintLn(model);

  String devEui = readValue(F("sys get hweui"));
  String str = "";
  str.concat(F("mac set deveui "));
  str.concat(devEui);
  sendCommand(str);

  str = "";
  str.concat(F("mac set adr "));
  if(adr){
    str.concat(F("on"));
  } else {
    str.concat(F("off"));
  }
  sendCommand(str);
}

void TheThingsNetwork::onMessage(void (*cb)(const byte* payload, size_t length, int8_t port)) {
  this->messageCallback = cb;
}

bool TheThingsNetwork::personalize(const byte devAddr[4], const byte nwkSKey[16], const byte appSKey[16]) {
  reset();
  sendCommand(F("mac set devaddr"), devAddr, 4);
  sendCommand(F("mac set nwkskey"), nwkSKey, 16);
  sendCommand(F("mac set appskey"), appSKey, 16);
  return personalize();
}

bool TheThingsNetwork::personalize() {
  configureChannels(this->sf, this->fsb);
  sendCommand(F("mac join abp"));
  String response = readLine();
  if (response != F("accepted")) {
    debugPrint(F("Personalize not accepted: "));
    debugPrintLn(response);
    return false;
  }

  fillAirtimeInfo();
  debugPrint(F("Personalize accepted. Status: "));
  debugPrintLn(readValue(F("mac get status")));
  return true;
}

bool TheThingsNetwork::provision(const byte appEui[8], const byte appKey[16]) {
  sendCommand(F("mac set appeui"), appEui, 8);
  sendCommand(F("mac set appkey"), appKey, 16);
  return sendCommand(F("mac save"));
}

bool TheThingsNetwork::join(int8_t retries, int32_t retryDelay) {
  configureChannels(this->sf, this->fsb);
  String devEui = readValue(F("sys get hweui"));
  String str = "";
  str.concat(F("mac set deveui "));
  str.concat(devEui);
  sendCommand(str);
  while (retries != 0) {
    if (retries > 0) {
      retries--;
    }
    if (!sendCommand(F("mac join otaa"))) {
      debugPrintLn(F("Send join command failed"));
      delay(retryDelay);
      continue;
    }
    String response = readLine();
    if (response != F("accepted")) {
      debugPrint(F("Join not accepted: "));
      debugPrintLn(response);
      delay(retryDelay);
      continue;
    }
    debugPrint(F("Join accepted. Status: "));
    debugPrintLn(readValue(F("mac get status")));
    debugPrint(F("DevAddr: "));
    debugPrintLn(readValue(F("mac get devaddr")));
    fillAirtimeInfo();
    return true;
  }
  return false;
}

bool TheThingsNetwork::join(const byte appEui[8], const byte appKey[16], int8_t retries, int32_t retryDelay) {
  reset();
  provision(appEui, appKey);
  return join(retries, retryDelay);
}

int TheThingsNetwork::sendBytes(const byte* payload, size_t length, int8_t port, bool confirm) {
  String str = "";
  str.concat(F("mac tx "));
  str.concat(confirm ? F("cnf ") : F("uncnf "));
  str.concat(port);
  if (!sendCommand(str, payload, length)) {
    debugPrintLn(F("Send command failed"));
    return -1;
  }

  String response = readLine();
  float i = this->airtime;
  trackAirtime(length);
  debugPrint(F("Airtime added: "));
  debugPrint(this->airtime - i);
  debugPrintLn(F(" s"));
  debugPrint(F("Total airtime: "));
  debugPrint(this->airtime);
  debugPrintLn(F(" s"));
  if (response == F("mac_tx_ok")) {
    debugPrintLn(F("Successful transmission"));
    return 1;
  }
  if (response.startsWith(F("mac_rx"))) {
    int8_t portEnds = response.indexOf(" ", 7);
    int8_t downlinkPort = response.substring(7, portEnds).toInt();
    String data = response.substring(portEnds + 1);
    size_t downlinkLength = data.length() / 2;
    byte downlink[64];
    for (size_t i = 0, d = 0; i < downlinkLength; i++, d += 2) {
      downlink[i] = TTN_HEX_PAIR_TO_BYTE(data[d], data[d+1]);
    }
    debugPrint(F("Successful transmission. Received "));
    debugPrint(downlinkLength);
    debugPrintLn(F(" bytes"));
    if (this->messageCallback)
      this->messageCallback(downlink, downlinkLength, downlinkPort);
    return 2;
  }

  debugPrint(F("Unexpected response: "));
  debugPrintLn(response);
  return -10;
}

int TheThingsNetwork::poll(int8_t port, bool confirm) {
  byte payload[] = { 0x00 };
  return sendBytes(payload, 1, port, confirm);
}

void TheThingsNetwork::fillAirtimeInfo() {
  this->info.sf = 0;
  this->info.ps = 0;
  this->info.band = 0;
  this->info.header = 0;
  this->info.cr = 0;
  this->info.de = 0;

  int16_t i;
  String message = readValue(F("radio get sf"));
  for (i = 2; message[i] && i <= 3; i++) {
    this->info.sf = (this->info.sf + message[i] - 48) * 10;
  }
  this->info.sf = this->info.sf / 10;

  message = readValue(F("radio get bw"));
  for (i = 0; message[i] && i <= 2; i++) {
    this->info.band = (this->info.band + message[i] - 48) * 10;
  }
  this->info.band = this->info.band / 10;

  message = readValue(F("radio get prlen"));
  this->info.ps = this->info.ps + message[0] - 48;

  message = readValue(F("radio get crc"));
  this->info.header = message == F("on") ? 1 : 0;

  message = readValue(F("radio get cr"));
  this->info.cr = (this->info.cr + message[2] - 48);

  this->info.de = this->info.sf >= 11 ? 1 : 0;
}

void TheThingsNetwork::trackAirtime(size_t payloadSize) {
  payloadSize = 13 + payloadSize;

  float Tsym = pow(2, this->info.sf) / this->info.band;
  float Tpreamble = (this->info.ps + 4.25) * Tsym;
  int16_t payLoadSymbNb = 8 + (max(ceil((8 * payloadSize - 4 * this->info.sf + 28 + 16 - 20 * this->info.header) / (4 * (this->info.sf - 2 * this->info.de))) * (this->info.cr + 4), 0));
  float Tpayload = payLoadSymbNb * Tsym;
  float Tpacket = Tpreamble + Tpayload;
  this->airtime = this->airtime + (Tpacket / 1000);
}

void TheThingsNetwork::showStatus() {
  debugPrint(F("EUI: "));
  debugPrintLn(readValue(F("sys get hweui")));
  debugPrint(F("Battery: "));
  debugPrintLn(readValue(F("sys get vdd")));
  debugPrint(F("AppEUI: "));
  debugPrintLn(readValue(F("mac get appeui")));
  debugPrint(F("DevEUI: "));
  debugPrintLn(readValue(F("mac get deveui")));

  if (this->model == F("RN2483")) {
    debugPrint(F("Band: "));
    debugPrintLn(readValue(F("mac get band")));
  }
  debugPrint(F("Data Rate: "));
  debugPrintLn(readValue(F("mac get dr")));
  debugPrint(F("RX Delay 1: "));
  debugPrintLn(readValue(F("mac get rxdelay1")));
  debugPrint(F("RX Delay 2: "));
  debugPrintLn(readValue(F("mac get rxdelay2")));
  debugPrint(F("Total airtime: "));
  debugPrint(this->airtime);
  debugPrintLn(F(" s"));
}

void TheThingsNetwork::configureEU868(int8_t sf) {
  int8_t ch;
  int8_t dr = -1;
  int32_t freq = 867100000;
  String str = "";

  str.concat(F("mac set rx2 3 869525000"));
  sendCommand(str);
  str = "";
  for (ch = 0; ch <= 7; ch++) {
    if (ch >= 3) {
      str.concat(F("mac set ch freq "));
      str.concat(ch);
      str.concat(F(" "));
      str.concat(freq);
      sendCommand(str);
      str = "";
      str.concat(F("mac set ch drrange "));
      str.concat(ch);
      str.concat(F(" 0 5"));
      sendCommand(str);
      str = "";
      str.concat(F("mac set ch status "));
      str.concat(ch);
      str.concat(F(" on"));
      sendCommand(str);
      str = "";
      freq = freq + 200000;
    }
    str.concat(F("mac set ch dcycle "));
    str.concat(ch);
    str.concat(F(" 799"));
    sendCommand(str);
    str = "";
  }
  str.concat(F("mac set ch drrange 1 0 6"));
  sendCommand(str);
  str = "";
  str.concat(F("mac set pwridx "));
  str.concat(TTN_PWRIDX_868);
  sendCommand(str);
  switch (sf) {
    case 7:
      dr = 5;
      break;
    case 8:
      dr = 4;
      break;
    case 9:
      dr = 3;
      break;
    case 10:
      dr = 2;
      break;
    case 11:
      dr = 1;
      break;
    case 12:
      dr = 0;
      break;
    default:
      debugPrintLn(F("Invalid SF"))
      break;
  }
  if (dr > -1){
    str = "";
    str.concat(F("mac set dr "));
    str.concat(dr);
    sendCommand(str);
  }
}

void TheThingsNetwork::configureUS915(int8_t sf, int8_t fsb) {
  int8_t ch;
  int8_t dr = -1;
  String str = "";
  int8_t chLow = fsb > 0 ? (fsb - 1) * 8 : 0;
  int8_t chHigh = fsb > 0 ? chLow + 7 : 71;
  int8_t ch500 = fsb + 63;

  sendCommand(F("radio set freq 904200000"));
  str = "";
  str.concat(F("mac set pwridx "));
  str.concat(TTN_PWRIDX_915);
  sendCommand(str);
  for (ch = 0; ch < 72; ch++) {
    str = "";
    str.concat(F("mac set ch status "));
    str.concat(ch);
    if (ch == ch500 || ch <= chHigh && ch >= chLow) {
      str.concat(F(" on"));
      sendCommand(str);
      if (ch < 63) {
        str = "";
        str.concat(F("mac set ch drrange "));
        str.concat(ch);
        str.concat(F(" 0 3"));
        sendCommand(str);
        str = "";
      }
    }
    else {
      str.concat(F(" off"));
      sendCommand(str);
    }
  }
  switch (sf) {
    case 7:
      dr = 3;
      break;
    case 8:
     dr = 2;
     break;
    case 9:
      dr = 1;
      break;
    case 10:
      dr = 0;
      break;
    default:
      debugPrintLn(F("Invalid SF"))
      break;
  }
  if (dr > -1){
    str = "";
    str.concat(F("mac set dr "));
    str.concat(dr);
    sendCommand(str);
  }
}

void TheThingsNetwork::configureChannels(int8_t sf, int8_t fsb) {
  switch (this->fp) {
    case TTN_FP_EU868:
      configureEU868(sf);
      break;
    case TTN_FP_US915:
      configureUS915(sf, fsb);
      break;
    default:
      debugPrintLn("Invalid frequency plan");
      break;
  }
  String retries = "";
  retries.concat(F("mac set retx "));
  retries.concat(TTN_RETX);
  sendCommand(retries);
}

TheThingsNetwork::TheThingsNetwork(Stream& modemStream, Stream& debugStream, ttn_fp_t fp, int8_t sf, int8_t fsb) {
  this->debugStream = &debugStream;
  this->modemStream = &modemStream;
  this->fp = fp;
  this->sf = sf;
  this->fsb = fsb;
}
<|endoftext|>


<commit_before>/*=========================================================================
 *
 *  Copyright RTK Consortium
 *
 *  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.txt
 *
 *  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.
 *
 *=========================================================================*/

// std include
#include <stdio.h>

#include "rtkHndImageIO.h"
#include <itkMetaDataObject.h>

//--------------------------------------------------------------------
// Read Image Information
void rtk::HndImageIO::ReadImageInformation()
{
  Hnd_header hnd;
  FILE *     fp;

  fp = fopen (m_FileName.c_str(), "rb");
  if (fp == NULL)
    itkGenericExceptionMacro(<< "Could not open file (for reading): " << m_FileName);

  size_t nelements = 0;
  nelements += fread ( (void *) hnd.sFileType, sizeof(char), 32, fp);
  nelements += fread ( (void *) &hnd.FileLength, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) hnd.sChecksumSpec, sizeof(char), 4, fp);
  nelements += fread ( (void *) &hnd.nCheckSum, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) hnd.sCreationDate, sizeof(char), 8, fp);
  nelements += fread ( (void *) hnd.sCreationTime, sizeof(char), 8, fp);
  nelements += fread ( (void *) hnd.sPatientID, sizeof(char), 16, fp);
  nelements += fread ( (void *) &hnd.nPatientSer, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) hnd.sSeriesID, sizeof(char), 16, fp);
  nelements += fread ( (void *) &hnd.nSeriesSer, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) hnd.sSliceID, sizeof(char), 16, fp);
  nelements += fread ( (void *) &hnd.nSliceSer, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) &hnd.SizeX, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) &hnd.SizeY, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) &hnd.dSliceZPos, sizeof(double), 1, fp);
  nelements += fread ( (void *) hnd.sModality, sizeof(char), 16, fp);
  nelements += fread ( (void *) &hnd.nWindow, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) &hnd.nLevel, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) &hnd.nPixelOffset, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) hnd.sImageType, sizeof(char), 4, fp);
  nelements += fread ( (void *) &hnd.dGantryRtn, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dSAD, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dSFD, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCollX1, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCollX2, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCollY1, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCollY2, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCollRtn, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dFieldX, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dFieldY, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dBladeX1, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dBladeX2, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dBladeY1, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dBladeY2, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dIDUPosLng, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dIDUPosLat, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dIDUPosVrt, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dIDUPosRtn, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dPatientSupportAngle, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dTableTopEccentricAngle, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCouchVrt, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCouchLng, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCouchLat, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dIDUResolutionX, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dIDUResolutionY, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dImageResolutionX, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dImageResolutionY, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dEnergy, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dDoseRate, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dXRayKV, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dXRayMA, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dMetersetExposure, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dAcqAdjustment, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCTProjectionAngle, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCTNormChamber, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dGatingTimeTag, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dGating4DInfoX, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dGating4DInfoY, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dGating4DInfoZ, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dGating4DInfoTime, sizeof(double), 1, fp);

  if(nelements != /*char*/120 + /*itk::uint32_t*/10 +  /*double*/41)
    itkGenericExceptionMacro(<< "Could not read header data in " << m_FileName);

  if(fclose (fp) != 0)
    itkGenericExceptionMacro(<< "Could not close file: " << m_FileName);

  /* Convert hnd to ITK image information */
  SetNumberOfDimensions(2);
  SetDimensions(0, hnd.SizeX);
  SetDimensions(1, hnd.SizeY);
  SetSpacing(0, hnd.dIDUResolutionX);
  SetSpacing(1, hnd.dIDUResolutionY);
  SetOrigin(0, -0.5*(hnd.SizeX-1)*hnd.dIDUResolutionX); //SR: assumed centered
  SetOrigin(1, -0.5*(hnd.SizeY-1)*hnd.dIDUResolutionY); //SR: assumed centered
  SetComponentType(itk::ImageIOBase::UINT);

  /* Store important meta information in the meta data dictionary */
  itk::EncapsulateMetaData<double>(this->GetMetaDataDictionary(), "dCTProjectionAngle", hnd.dCTProjectionAngle);
}

//--------------------------------------------------------------------
bool rtk::HndImageIO::CanReadFile(const char* FileNameToRead)
{
  std::string                  filename(FileNameToRead);
  const std::string::size_type it = filename.find_last_of( "." );
  std::string                  fileExt( filename, it+1, filename.length() );

  if (fileExt != std::string("hnd") )
    return false;
  return true;
}

//--------------------------------------------------------------------
// Read Image Content
void rtk::HndImageIO::Read(void * buffer)
{
  FILE *fp;

  itk::uint32_t*      buf = (itk::uint32_t*)buffer;
  unsigned char *pt_lut;
  itk::uint32_t       a;
  unsigned char  v;
  int            lut_idx, lut_off;
  size_t         num_read;
  char           dc;
  short          ds;
  long           dl, diff=0;
  itk::uint32_t       i;

  fp = fopen (m_FileName.c_str(), "rb");
  if (fp == NULL)
    itkGenericExceptionMacro(<< "Could not open file (for reading): " << m_FileName);

  pt_lut = (unsigned char*) malloc (sizeof (unsigned char) * GetDimensions(0) * GetDimensions(1) );

  /* Read LUT */
  if(fseek (fp, 1024, SEEK_SET) != 0)
    itkGenericExceptionMacro(<< "Could not seek to image data in: " << m_FileName);

  size_t nbytes = (GetDimensions(1)-1)*GetDimensions(0) / 4;
  if(nbytes != fread (pt_lut, sizeof(unsigned char), nbytes, fp))
    itkGenericExceptionMacro(<< "Could not read image LUT in: " << m_FileName);

  /* Read first row */
  for (i = 0; i < GetDimensions(0); i++) {
    if(1 != fread (&a, sizeof(itk::uint32_t), 1, fp))
      itkGenericExceptionMacro(<< "Could not read first row in: " << m_FileName);
    buf[i] = a;
    }

  /* Read first pixel of second row */
  if(1 != fread (&a, sizeof(itk::uint32_t), 1, fp))
    itkGenericExceptionMacro(<< "Could not read first pixel of second row");
  buf[i++] = a;

  /* Decompress the rest */
  lut_idx = 0;
  lut_off = 0;
  while (i < GetDimensions(0) * GetDimensions(1) ) {
    itk::uint32_t r11, r12, r21;

    r11 = buf[i-GetDimensions(0)-1];
    r12 = buf[i-GetDimensions(0)];
    r21 = buf[i-1];
    v = pt_lut[lut_idx];
    switch (lut_off) {
      case 0:
        v = v & 0x03;
        lut_off++;
        break;
      case 1:
        v = (v & 0x0C) >> 2;
        lut_off++;
        break;
      case 2:
        v = (v & 0x30) >> 4;
        lut_off++;
        break;
      case 3:
        v = (v & 0xC0) >> 6;
        lut_off = 0;
        lut_idx++;
        break;
      }
    switch (v) {
      case 0:
        num_read = fread (&dc, sizeof(unsigned char), 1, fp);
        if (num_read != 1) goto read_error;
        diff = dc;
        break;
      case 1:
        num_read = fread (&ds, sizeof(unsigned short), 1, fp);
        if (num_read != 1) goto read_error;
        diff = ds;
        break;
      case 2:
        num_read = fread (&dl, sizeof(itk::uint32_t), 1, fp);
        if (num_read != 1) goto read_error;
        diff = dl;
        break;
      }

    buf[i] = r21 + r12 + diff - r11;
    i++;
    }

  /* Clean up */
  free (pt_lut);
  if(fclose (fp) != 0)
    itkGenericExceptionMacro(<< "Could not close file: " << m_FileName);
  return;

read_error:

  itkGenericExceptionMacro(<< "Error reading hnd file");
  free (pt_lut);
  if(fclose (fp) != 0)
    itkGenericExceptionMacro(<< "Could not close file: " << m_FileName);
  return;
}

//--------------------------------------------------------------------
bool rtk::HndImageIO::CanWriteFile(const char* itkNotUsed(FileNameToWrite))
{
  return false;
}

//--------------------------------------------------------------------
// Write Image
void rtk::HndImageIO::Write(const void* itkNotUsed(buffer))
{
  //TODO?
}
<commit_msg>Style<commit_after>/*=========================================================================
 *
 *  Copyright RTK Consortium
 *
 *  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.txt
 *
 *  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.
 *
 *=========================================================================*/

// std include
#include <stdio.h>

#include "rtkHndImageIO.h"
#include <itkMetaDataObject.h>

//--------------------------------------------------------------------
// Read Image Information
void rtk::HndImageIO::ReadImageInformation()
{
  Hnd_header hnd;
  FILE *     fp;

  fp = fopen (m_FileName.c_str(), "rb");
  if (fp == NULL)
    itkGenericExceptionMacro(<< "Could not open file (for reading): " << m_FileName);

  size_t nelements = 0;
  nelements += fread ( (void *) hnd.sFileType, sizeof(char), 32, fp);
  nelements += fread ( (void *) &hnd.FileLength, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) hnd.sChecksumSpec, sizeof(char), 4, fp);
  nelements += fread ( (void *) &hnd.nCheckSum, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) hnd.sCreationDate, sizeof(char), 8, fp);
  nelements += fread ( (void *) hnd.sCreationTime, sizeof(char), 8, fp);
  nelements += fread ( (void *) hnd.sPatientID, sizeof(char), 16, fp);
  nelements += fread ( (void *) &hnd.nPatientSer, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) hnd.sSeriesID, sizeof(char), 16, fp);
  nelements += fread ( (void *) &hnd.nSeriesSer, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) hnd.sSliceID, sizeof(char), 16, fp);
  nelements += fread ( (void *) &hnd.nSliceSer, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) &hnd.SizeX, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) &hnd.SizeY, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) &hnd.dSliceZPos, sizeof(double), 1, fp);
  nelements += fread ( (void *) hnd.sModality, sizeof(char), 16, fp);
  nelements += fread ( (void *) &hnd.nWindow, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) &hnd.nLevel, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) &hnd.nPixelOffset, sizeof(itk::uint32_t), 1, fp);
  nelements += fread ( (void *) hnd.sImageType, sizeof(char), 4, fp);
  nelements += fread ( (void *) &hnd.dGantryRtn, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dSAD, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dSFD, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCollX1, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCollX2, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCollY1, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCollY2, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCollRtn, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dFieldX, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dFieldY, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dBladeX1, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dBladeX2, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dBladeY1, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dBladeY2, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dIDUPosLng, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dIDUPosLat, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dIDUPosVrt, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dIDUPosRtn, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dPatientSupportAngle, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dTableTopEccentricAngle, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCouchVrt, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCouchLng, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCouchLat, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dIDUResolutionX, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dIDUResolutionY, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dImageResolutionX, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dImageResolutionY, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dEnergy, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dDoseRate, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dXRayKV, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dXRayMA, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dMetersetExposure, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dAcqAdjustment, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCTProjectionAngle, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dCTNormChamber, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dGatingTimeTag, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dGating4DInfoX, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dGating4DInfoY, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dGating4DInfoZ, sizeof(double), 1, fp);
  nelements += fread ( (void *) &hnd.dGating4DInfoTime, sizeof(double), 1, fp);

  if(nelements != /*char*/120 + /*itk::uint32_t*/10 +  /*double*/41)
    itkGenericExceptionMacro(<< "Could not read header data in " << m_FileName);

  if(fclose (fp) != 0)
    itkGenericExceptionMacro(<< "Could not close file: " << m_FileName);

  /* Convert hnd to ITK image information */
  SetNumberOfDimensions(2);
  SetDimensions(0, hnd.SizeX);
  SetDimensions(1, hnd.SizeY);
  SetSpacing(0, hnd.dIDUResolutionX);
  SetSpacing(1, hnd.dIDUResolutionY);
  SetOrigin(0, -0.5*(hnd.SizeX-1)*hnd.dIDUResolutionX); //SR: assumed centered
  SetOrigin(1, -0.5*(hnd.SizeY-1)*hnd.dIDUResolutionY); //SR: assumed centered
  SetComponentType(itk::ImageIOBase::UINT);

  /* Store important meta information in the meta data dictionary */
  itk::EncapsulateMetaData<double>(this->GetMetaDataDictionary(), "dCTProjectionAngle", hnd.dCTProjectionAngle);
}

//--------------------------------------------------------------------
bool rtk::HndImageIO::CanReadFile(const char* FileNameToRead)
{
  std::string                  filename(FileNameToRead);
  const std::string::size_type it = filename.find_last_of( "." );
  std::string                  fileExt( filename, it+1, filename.length() );

  if (fileExt != std::string("hnd") )
    return false;
  return true;
}

//--------------------------------------------------------------------
// Read Image Content
void rtk::HndImageIO::Read(void * buffer)
{
  FILE *fp;

  itk::uint32_t *buf = (itk::uint32_t*)buffer;
  unsigned char *pt_lut;
  itk::uint32_t  a;
  unsigned char  v;
  int            lut_idx, lut_off;
  size_t         num_read;
  char           dc;
  short          ds;
  long           dl, diff=0;
  itk::uint32_t  i;

  fp = fopen (m_FileName.c_str(), "rb");
  if (fp == NULL)
    itkGenericExceptionMacro(<< "Could not open file (for reading): " << m_FileName);

  pt_lut = (unsigned char*) malloc (sizeof (unsigned char) * GetDimensions(0) * GetDimensions(1) );

  /* Read LUT */
  if(fseek (fp, 1024, SEEK_SET) != 0)
    itkGenericExceptionMacro(<< "Could not seek to image data in: " << m_FileName);

  size_t nbytes = (GetDimensions(1)-1)*GetDimensions(0) / 4;
  if(nbytes != fread (pt_lut, sizeof(unsigned char), nbytes, fp))
    itkGenericExceptionMacro(<< "Could not read image LUT in: " << m_FileName);

  /* Read first row */
  for (i = 0; i < GetDimensions(0); i++) {
    if(1 != fread (&a, sizeof(itk::uint32_t), 1, fp))
      itkGenericExceptionMacro(<< "Could not read first row in: " << m_FileName);
    buf[i] = a;
    }

  /* Read first pixel of second row */
  if(1 != fread (&a, sizeof(itk::uint32_t), 1, fp))
    itkGenericExceptionMacro(<< "Could not read first pixel of second row");
  buf[i++] = a;

  /* Decompress the rest */
  lut_idx = 0;
  lut_off = 0;
  while (i < GetDimensions(0) * GetDimensions(1) ) {
    itk::uint32_t r11, r12, r21;

    r11 = buf[i-GetDimensions(0)-1];
    r12 = buf[i-GetDimensions(0)];
    r21 = buf[i-1];
    v = pt_lut[lut_idx];
    switch (lut_off) {
      case 0:
        v = v & 0x03;
        lut_off++;
        break;
      case 1:
        v = (v & 0x0C) >> 2;
        lut_off++;
        break;
      case 2:
        v = (v & 0x30) >> 4;
        lut_off++;
        break;
      case 3:
        v = (v & 0xC0) >> 6;
        lut_off = 0;
        lut_idx++;
        break;
      }
    switch (v) {
      case 0:
        num_read = fread (&dc, sizeof(unsigned char), 1, fp);
        if (num_read != 1) goto read_error;
        diff = dc;
        break;
      case 1:
        num_read = fread (&ds, sizeof(unsigned short), 1, fp);
        if (num_read != 1) goto read_error;
        diff = ds;
        break;
      case 2:
        num_read = fread (&dl, sizeof(itk::uint32_t), 1, fp);
        if (num_read != 1) goto read_error;
        diff = dl;
        break;
      }

    buf[i] = r21 + r12 + diff - r11;
    i++;
    }

  /* Clean up */
  free (pt_lut);
  if(fclose (fp) != 0)
    itkGenericExceptionMacro(<< "Could not close file: " << m_FileName);
  return;

read_error:

  itkGenericExceptionMacro(<< "Error reading hnd file");
  free (pt_lut);
  if(fclose (fp) != 0)
    itkGenericExceptionMacro(<< "Could not close file: " << m_FileName);
  return;
}

//--------------------------------------------------------------------
bool rtk::HndImageIO::CanWriteFile(const char* itkNotUsed(FileNameToWrite))
{
  return false;
}

//--------------------------------------------------------------------
// Write Image
void rtk::HndImageIO::Write(const void* itkNotUsed(buffer))
{
  //TODO?
}
<|endoftext|>


<commit_before>// ======================================================================== //
// Copyright 2009-2016 Intel Corporation                                    //
//                                                                          //
// 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.                                           //
// ======================================================================== //

#include "tessellation_cache.h"

namespace embree
{
  SharedLazyTessellationCache SharedLazyTessellationCache::sharedLazyTessellationCache;

  __thread ThreadWorkState* SharedLazyTessellationCache::init_t_state = nullptr;
  ThreadWorkState* SharedLazyTessellationCache::current_t_state = nullptr;

  void resizeTessellationCache(size_t new_size)
  {
    if (new_size >= SharedLazyTessellationCache::MAX_TESSELLATION_CACHE_SIZE)
      new_size = SharedLazyTessellationCache::MAX_TESSELLATION_CACHE_SIZE;
    if (SharedLazyTessellationCache::sharedLazyTessellationCache.getSize() != new_size)
      SharedLazyTessellationCache::sharedLazyTessellationCache.realloc(new_size);
  }

  void resetTessellationCache()
  {
    //SharedLazyTessellationCache::sharedLazyTessellationCache.addCurrentIndex(SharedLazyTessellationCache::NUM_CACHE_SEGMENTS);
    SharedLazyTessellationCache::sharedLazyTessellationCache.reset();
  }

  SharedLazyTessellationCache::SharedLazyTessellationCache()
  {
    size = 0;
    data = nullptr;
    maxBlocks              = size/BLOCK_SIZE;
    localTime              = NUM_CACHE_SEGMENTS;
    next_block             = 0;
    numRenderThreads       = 0;
#if FORCE_SIMPLE_FLUSH == 1
    switch_block_threshold = maxBlocks;
#else
    switch_block_threshold = maxBlocks/NUM_CACHE_SEGMENTS;
#endif
    threadWorkState     = new ThreadWorkState[NUM_PREALLOC_THREAD_WORK_STATES];

    //reset_state.reset();
    //linkedlist_mtx.reset();
  }

  SharedLazyTessellationCache::~SharedLazyTessellationCache()
  {
    for (ThreadWorkState* t=current_t_state; t!=nullptr; )
    {
      ThreadWorkState* next = t->next;
      if (t->allocated) delete t;
      t = next;
    }

    delete[] threadWorkState;
  }

  void SharedLazyTessellationCache::getNextRenderThreadWorkState()
  {
    const size_t id = numRenderThreads.fetch_add(1);
    if (id >= NUM_PREALLOC_THREAD_WORK_STATES) init_t_state = new ThreadWorkState(true);
    else                                       init_t_state = &threadWorkState[id];

    /* critical section for updating link list with new thread state */
    linkedlist_mtx.lock();
    init_t_state->next = current_t_state;
    current_t_state = init_t_state;
    linkedlist_mtx.unlock();
  }

  void SharedLazyTessellationCache::waitForUsersLessEqual(ThreadWorkState *const t_state,
							  const unsigned int users)
   {
     while( !(t_state->counter <= users) )
     {
       _mm_pause();
       _mm_pause();
       _mm_pause();
       _mm_pause();
     }
   }

  void SharedLazyTessellationCache::allocNextSegment()
  {
    if (reset_state.try_lock())
      {
	if (next_block >= switch_block_threshold)
	  {
            /* lock the linked list of thread states */

            linkedlist_mtx.lock();

            /* block all threads */
	    for (ThreadWorkState *t=current_t_state;t!=nullptr;t=t->next)
	      if (lockThread(t,THREAD_BLOCK_ATOMIC_ADD) != 0)
		waitForUsersLessEqual(t,THREAD_BLOCK_ATOMIC_ADD);

            /* switch to the next segment */
	    addCurrentIndex();
	    CACHE_STATS(PRINT("RESET TESS CACHE"));

#if FORCE_SIMPLE_FLUSH == 1
	    next_block = 0;
	    switch_block_threshold = maxBlocks;
#else
	    const size_t region = localTime % NUM_CACHE_SEGMENTS;
	    next_block = region * (maxBlocks/NUM_CACHE_SEGMENTS);
	    switch_block_threshold = next_block + (maxBlocks/NUM_CACHE_SEGMENTS);
	    assert( switch_block_threshold <= maxBlocks );
#endif

	    CACHE_STATS(SharedTessellationCacheStats::cache_flushes++);

            /* release all blocked threads */

	    for (ThreadWorkState *t=current_t_state;t!=nullptr;t=t->next)
	      unlockThread(t,-THREAD_BLOCK_ATOMIC_ADD);

            /* unlock the linked list of thread states */

            linkedlist_mtx.unlock();

	  }
	reset_state.unlock();
      }
    else
      reset_state.wait_until_unlocked();
  }

  void SharedLazyTessellationCache::reset()
  {
    /* lock the reset_state */
    reset_state.lock();

    /* lock the linked list of thread states */
    linkedlist_mtx.lock();

    /* block all threads */
    for (ThreadWorkState *t=current_t_state;t!=nullptr;t=t->next)
      if (lockThread(t,THREAD_BLOCK_ATOMIC_ADD) != 0)
        waitForUsersLessEqual(t,THREAD_BLOCK_ATOMIC_ADD);

    /* reset to the first segment */
    next_block = 0;
#if FORCE_SIMPLE_FLUSH == 1
    switch_block_threshold = maxBlocks;
#else
    switch_block_threshold = maxBlocks/NUM_CACHE_SEGMENTS;
#endif

    /* reset local time */
    localTime = NUM_CACHE_SEGMENTS;

    /* release all blocked threads */
    for (ThreadWorkState *t=current_t_state;t!=nullptr;t=t->next)
      unlockThread(t,-THREAD_BLOCK_ATOMIC_ADD);

    /* unlock the linked list of thread states */
    linkedlist_mtx.unlock();

    /* unlock the reset_state */
    reset_state.unlock();
  }

  void SharedLazyTessellationCache::realloc(const size_t new_size)
  {
    /* lock the reset_state */
    reset_state.lock();

    /* lock the linked list of thread states */
    linkedlist_mtx.lock();

    /* block all threads */
    for (ThreadWorkState *t=current_t_state;t!=nullptr;t=t->next)
      if (lockThread(t,THREAD_BLOCK_ATOMIC_ADD) != 0)
        waitForUsersLessEqual(t,THREAD_BLOCK_ATOMIC_ADD);

    /* reallocate data */
    if (data) os_free(data,size);
    size      = new_size;
    data      = nullptr;
    if (size) data = (float*)os_malloc(size); // FIXME: do os_reserve under linux
    maxBlocks = size/BLOCK_SIZE;

    /* invalidate entire cache */
    localTime += NUM_CACHE_SEGMENTS;

    /* reset to the first segment */
#if FORCE_SIMPLE_FLUSH == 1
    next_block = 0;
    switch_block_threshold = maxBlocks;
#else
    const size_t region = localTime % NUM_CACHE_SEGMENTS;
    next_block = region * (maxBlocks/NUM_CACHE_SEGMENTS);
    switch_block_threshold = next_block + (maxBlocks/NUM_CACHE_SEGMENTS);
    assert( switch_block_threshold <= maxBlocks );
#endif

    /* release all blocked threads */
    for (ThreadWorkState *t=current_t_state;t!=nullptr;t=t->next)
      unlockThread(t,-THREAD_BLOCK_ATOMIC_ADD);

    /* unlock the linked list of thread states */
    linkedlist_mtx.unlock();

    /* unlock the reset_state */
    reset_state.unlock();
  }

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////

  std::atomic<size_t> SharedTessellationCacheStats::cache_accesses(0);
  std::atomic<size_t> SharedTessellationCacheStats::cache_hits(0);
  std::atomic<size_t> SharedTessellationCacheStats::cache_misses(0);
  std::atomic<size_t> SharedTessellationCacheStats::cache_flushes(0);
  SpinLock   SharedTessellationCacheStats::mtx;
  std::atomic<size_t> *SharedTessellationCacheStats::cache_patch_builds(nullptr);
  size_t SharedTessellationCacheStats::cache_num_patches(0);

  void SharedTessellationCacheStats::printStats()
  {
    PRINT(cache_accesses);
    PRINT(cache_misses);
    PRINT(cache_hits);
    PRINT(cache_flushes);
    PRINT(100.0f * cache_hits / cache_accesses);
    assert(cache_hits + cache_misses == cache_accesses);
    PRINT(cache_num_patches);
    size_t patches = 0;
    size_t builds  = 0;
    for (size_t i=0;i<cache_num_patches;i++)
      if (cache_patch_builds[i])
	{
	  patches++;
	  builds += cache_patch_builds[i];
	}
    PRINT(patches);
    PRINT(builds);
    PRINT((double)builds/patches);
  }

  void SharedTessellationCacheStats::clearStats()
  {
    SharedTessellationCacheStats::cache_accesses  = 0;
    SharedTessellationCacheStats::cache_hits      = 0;
    SharedTessellationCacheStats::cache_misses    = 0;
    SharedTessellationCacheStats::cache_flushes   = 0;
    for (size_t i=0;i<cache_num_patches;i++)
      cache_patch_builds[i] = 0;
  }

  void SharedTessellationCacheStats::incPatchBuild(const size_t ID, const size_t numPatches)
  {
    if (!cache_patch_builds)
      {
	mtx.lock();
	if (!cache_patch_builds)
	  {
	    PRINT(numPatches);
	    cache_num_patches = numPatches;
	    cache_patch_builds = (std::atomic<size_t>*)os_malloc(numPatches*sizeof(std::atomic<size_t>));
	    memset(cache_patch_builds,0,numPatches*sizeof(std::atomic<size_t>));
	  }
	mtx.unlock();
      }
    assert(ID < cache_num_patches);
    cache_patch_builds[ID]++;
  }

  struct cache_regression_test : public RegressionTest
  {
    BarrierSys barrier;
    std::atomic<size_t> numFailed;
    std::atomic<int> threadIDCounter;
    static const size_t numEntries = 4*1024;
    SharedLazyTessellationCache::CacheEntry entry[numEntries];

    cache_regression_test()
      : RegressionTest("cache_regression_test"), numFailed(0), threadIDCounter(0)
    {
      registerRegressionTest(this);
    }

    static void thread_alloc(cache_regression_test* This)
    {
      int threadID = This->threadIDCounter++;
      size_t maxN = SharedLazyTessellationCache::sharedLazyTessellationCache.maxAllocSize()/4;
      This->barrier.wait();

      for (size_t j=0; j<100000; j++)
      {
        size_t elt = (threadID+j)%numEntries;
        size_t N = min(1+10*(elt%1000),maxN);

        volatile int* data = (volatile int*) SharedLazyTessellationCache::lookup(This->entry[elt],0,[&] () {
            int* data = (int*) SharedLazyTessellationCache::sharedLazyTessellationCache.malloc(4*N);
            for (size_t k=0; k<N; k++) data[k] = elt;
            return data;
          });

        if (data == nullptr) {
          SharedLazyTessellationCache::sharedLazyTessellationCache.unlock();
          This->numFailed++;
          continue;
        }

        /* check memory block */
        for (size_t k=0; k<N; k++) {
          if (data[k] != elt) {
            This->numFailed++;
            break;
          }
        }

        SharedLazyTessellationCache::sharedLazyTessellationCache.unlock();
      }
      This->barrier.wait();
    }

    bool run ()
    {
      numFailed.store(0);

      size_t numThreads = getNumberOfLogicalThreads();
      barrier.init(numThreads+1);

      /* create threads */
      std::vector<thread_t> threads;
      for (size_t i=0; i<numThreads; i++)
        threads.push_back(createThread((thread_func)thread_alloc,this));

      /* run test */
      barrier.wait();
      barrier.wait();

      /* destroy threads */
      for (size_t i=0; i<numThreads; i++)
        join(threads[i]);

      return numFailed == 0;
    }
  };

  cache_regression_test cache_regression;
};

extern "C" void printTessCacheStats()
{
  PRINT("SHARED TESSELLATION CACHE");
  embree::SharedTessellationCacheStats::printStats();
  embree::SharedTessellationCacheStats::clearStats();
}
<commit_msg>affinitized threads in the tessellation cache regression test, seems to solve hang issue<commit_after>// ======================================================================== //
// Copyright 2009-2016 Intel Corporation                                    //
//                                                                          //
// 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.                                           //
// ======================================================================== //

#include "tessellation_cache.h"

namespace embree
{
  SharedLazyTessellationCache SharedLazyTessellationCache::sharedLazyTessellationCache;

  __thread ThreadWorkState* SharedLazyTessellationCache::init_t_state = nullptr;
  ThreadWorkState* SharedLazyTessellationCache::current_t_state = nullptr;

  void resizeTessellationCache(size_t new_size)
  {
    if (new_size >= SharedLazyTessellationCache::MAX_TESSELLATION_CACHE_SIZE)
      new_size = SharedLazyTessellationCache::MAX_TESSELLATION_CACHE_SIZE;
    if (SharedLazyTessellationCache::sharedLazyTessellationCache.getSize() != new_size)
      SharedLazyTessellationCache::sharedLazyTessellationCache.realloc(new_size);
  }

  void resetTessellationCache()
  {
    //SharedLazyTessellationCache::sharedLazyTessellationCache.addCurrentIndex(SharedLazyTessellationCache::NUM_CACHE_SEGMENTS);
    SharedLazyTessellationCache::sharedLazyTessellationCache.reset();
  }

  SharedLazyTessellationCache::SharedLazyTessellationCache()
  {
    size = 0;
    data = nullptr;
    maxBlocks              = size/BLOCK_SIZE;
    localTime              = NUM_CACHE_SEGMENTS;
    next_block             = 0;
    numRenderThreads       = 0;
#if FORCE_SIMPLE_FLUSH == 1
    switch_block_threshold = maxBlocks;
#else
    switch_block_threshold = maxBlocks/NUM_CACHE_SEGMENTS;
#endif
    threadWorkState     = new ThreadWorkState[NUM_PREALLOC_THREAD_WORK_STATES];

    //reset_state.reset();
    //linkedlist_mtx.reset();
  }

  SharedLazyTessellationCache::~SharedLazyTessellationCache()
  {
    for (ThreadWorkState* t=current_t_state; t!=nullptr; )
    {
      ThreadWorkState* next = t->next;
      if (t->allocated) delete t;
      t = next;
    }

    delete[] threadWorkState;
  }

  void SharedLazyTessellationCache::getNextRenderThreadWorkState()
  {
    const size_t id = numRenderThreads.fetch_add(1);
    if (id >= NUM_PREALLOC_THREAD_WORK_STATES) init_t_state = new ThreadWorkState(true);
    else                                       init_t_state = &threadWorkState[id];

    /* critical section for updating link list with new thread state */
    linkedlist_mtx.lock();
    init_t_state->next = current_t_state;
    current_t_state = init_t_state;
    linkedlist_mtx.unlock();
  }

  void SharedLazyTessellationCache::waitForUsersLessEqual(ThreadWorkState *const t_state,
							  const unsigned int users)
   {
     while( !(t_state->counter <= users) )
     {
       _mm_pause();
       _mm_pause();
       _mm_pause();
       _mm_pause();
     }
   }

  void SharedLazyTessellationCache::allocNextSegment()
  {
    if (reset_state.try_lock())
      {
	if (next_block >= switch_block_threshold)
	  {
            /* lock the linked list of thread states */

            linkedlist_mtx.lock();

            /* block all threads */
	    for (ThreadWorkState *t=current_t_state;t!=nullptr;t=t->next)
	      if (lockThread(t,THREAD_BLOCK_ATOMIC_ADD) != 0)
		waitForUsersLessEqual(t,THREAD_BLOCK_ATOMIC_ADD);

            /* switch to the next segment */
	    addCurrentIndex();
	    CACHE_STATS(PRINT("RESET TESS CACHE"));

#if FORCE_SIMPLE_FLUSH == 1
	    next_block = 0;
	    switch_block_threshold = maxBlocks;
#else
	    const size_t region = localTime % NUM_CACHE_SEGMENTS;
	    next_block = region * (maxBlocks/NUM_CACHE_SEGMENTS);
	    switch_block_threshold = next_block + (maxBlocks/NUM_CACHE_SEGMENTS);
	    assert( switch_block_threshold <= maxBlocks );
#endif

	    CACHE_STATS(SharedTessellationCacheStats::cache_flushes++);

            /* release all blocked threads */

	    for (ThreadWorkState *t=current_t_state;t!=nullptr;t=t->next)
	      unlockThread(t,-THREAD_BLOCK_ATOMIC_ADD);

            /* unlock the linked list of thread states */

            linkedlist_mtx.unlock();

	  }
	reset_state.unlock();
      }
    else
      reset_state.wait_until_unlocked();
  }

  void SharedLazyTessellationCache::reset()
  {
    /* lock the reset_state */
    reset_state.lock();

    /* lock the linked list of thread states */
    linkedlist_mtx.lock();

    /* block all threads */
    for (ThreadWorkState *t=current_t_state;t!=nullptr;t=t->next)
      if (lockThread(t,THREAD_BLOCK_ATOMIC_ADD) != 0)
        waitForUsersLessEqual(t,THREAD_BLOCK_ATOMIC_ADD);

    /* reset to the first segment */
    next_block = 0;
#if FORCE_SIMPLE_FLUSH == 1
    switch_block_threshold = maxBlocks;
#else
    switch_block_threshold = maxBlocks/NUM_CACHE_SEGMENTS;
#endif

    /* reset local time */
    localTime = NUM_CACHE_SEGMENTS;

    /* release all blocked threads */
    for (ThreadWorkState *t=current_t_state;t!=nullptr;t=t->next)
      unlockThread(t,-THREAD_BLOCK_ATOMIC_ADD);

    /* unlock the linked list of thread states */
    linkedlist_mtx.unlock();

    /* unlock the reset_state */
    reset_state.unlock();
  }

  void SharedLazyTessellationCache::realloc(const size_t new_size)
  {
    /* lock the reset_state */
    reset_state.lock();

    /* lock the linked list of thread states */
    linkedlist_mtx.lock();

    /* block all threads */
    for (ThreadWorkState *t=current_t_state;t!=nullptr;t=t->next)
      if (lockThread(t,THREAD_BLOCK_ATOMIC_ADD) != 0)
        waitForUsersLessEqual(t,THREAD_BLOCK_ATOMIC_ADD);

    /* reallocate data */
    if (data) os_free(data,size);
    size      = new_size;
    data      = nullptr;
    if (size) data = (float*)os_malloc(size); // FIXME: do os_reserve under linux
    maxBlocks = size/BLOCK_SIZE;

    /* invalidate entire cache */
    localTime += NUM_CACHE_SEGMENTS;

    /* reset to the first segment */
#if FORCE_SIMPLE_FLUSH == 1
    next_block = 0;
    switch_block_threshold = maxBlocks;
#else
    const size_t region = localTime % NUM_CACHE_SEGMENTS;
    next_block = region * (maxBlocks/NUM_CACHE_SEGMENTS);
    switch_block_threshold = next_block + (maxBlocks/NUM_CACHE_SEGMENTS);
    assert( switch_block_threshold <= maxBlocks );
#endif

    /* release all blocked threads */
    for (ThreadWorkState *t=current_t_state;t!=nullptr;t=t->next)
      unlockThread(t,-THREAD_BLOCK_ATOMIC_ADD);

    /* unlock the linked list of thread states */
    linkedlist_mtx.unlock();

    /* unlock the reset_state */
    reset_state.unlock();
  }

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////

  std::atomic<size_t> SharedTessellationCacheStats::cache_accesses(0);
  std::atomic<size_t> SharedTessellationCacheStats::cache_hits(0);
  std::atomic<size_t> SharedTessellationCacheStats::cache_misses(0);
  std::atomic<size_t> SharedTessellationCacheStats::cache_flushes(0);
  SpinLock   SharedTessellationCacheStats::mtx;
  std::atomic<size_t> *SharedTessellationCacheStats::cache_patch_builds(nullptr);
  size_t SharedTessellationCacheStats::cache_num_patches(0);

  void SharedTessellationCacheStats::printStats()
  {
    PRINT(cache_accesses);
    PRINT(cache_misses);
    PRINT(cache_hits);
    PRINT(cache_flushes);
    PRINT(100.0f * cache_hits / cache_accesses);
    assert(cache_hits + cache_misses == cache_accesses);
    PRINT(cache_num_patches);
    size_t patches = 0;
    size_t builds  = 0;
    for (size_t i=0;i<cache_num_patches;i++)
      if (cache_patch_builds[i])
	{
	  patches++;
	  builds += cache_patch_builds[i];
	}
    PRINT(patches);
    PRINT(builds);
    PRINT((double)builds/patches);
  }

  void SharedTessellationCacheStats::clearStats()
  {
    SharedTessellationCacheStats::cache_accesses  = 0;
    SharedTessellationCacheStats::cache_hits      = 0;
    SharedTessellationCacheStats::cache_misses    = 0;
    SharedTessellationCacheStats::cache_flushes   = 0;
    for (size_t i=0;i<cache_num_patches;i++)
      cache_patch_builds[i] = 0;
  }

  void SharedTessellationCacheStats::incPatchBuild(const size_t ID, const size_t numPatches)
  {
    if (!cache_patch_builds)
      {
	mtx.lock();
	if (!cache_patch_builds)
	  {
	    PRINT(numPatches);
	    cache_num_patches = numPatches;
	    cache_patch_builds = (std::atomic<size_t>*)os_malloc(numPatches*sizeof(std::atomic<size_t>));
	    memset(cache_patch_builds,0,numPatches*sizeof(std::atomic<size_t>));
	  }
	mtx.unlock();
      }
    assert(ID < cache_num_patches);
    cache_patch_builds[ID]++;
  }

  struct cache_regression_test : public RegressionTest
  {
    BarrierSys barrier;
    std::atomic<size_t> numFailed;
    std::atomic<int> threadIDCounter;
    static const size_t numEntries = 4*1024;
    SharedLazyTessellationCache::CacheEntry entry[numEntries];

    cache_regression_test()
      : RegressionTest("cache_regression_test"), numFailed(0), threadIDCounter(0)
    {
      registerRegressionTest(this);
    }

    static void thread_alloc(cache_regression_test* This)
    {
      int threadID = This->threadIDCounter++;
      size_t maxN = SharedLazyTessellationCache::sharedLazyTessellationCache.maxAllocSize()/4;
      This->barrier.wait();

      for (size_t j=0; j<100000; j++)
      {
        size_t elt = (threadID+j)%numEntries;
        size_t N = min(1+10*(elt%1000),maxN);

        volatile int* data = (volatile int*) SharedLazyTessellationCache::lookup(This->entry[elt],0,[&] () {
            int* data = (int*) SharedLazyTessellationCache::sharedLazyTessellationCache.malloc(4*N);
            for (size_t k=0; k<N; k++) data[k] = elt;
            return data;
          });

        if (data == nullptr) {
          SharedLazyTessellationCache::sharedLazyTessellationCache.unlock();
          This->numFailed++;
          continue;
        }

        /* check memory block */
        for (size_t k=0; k<N; k++) {
          if (data[k] != elt) {
            This->numFailed++;
            break;
          }
        }

        SharedLazyTessellationCache::sharedLazyTessellationCache.unlock();
      }
      This->barrier.wait();
    }

    bool run ()
    {
      numFailed.store(0);

      size_t numThreads = getNumberOfLogicalThreads();
      barrier.init(numThreads+1);

      /* create threads */
      std::vector<thread_t> threads;
      for (size_t i=0; i<numThreads; i++)
        threads.push_back(createThread((thread_func)thread_alloc,this,0,i));

      /* run test */
      barrier.wait();
      barrier.wait();

      /* destroy threads */
      for (size_t i=0; i<numThreads; i++)
        join(threads[i]);

      return numFailed == 0;
    }
  };

  cache_regression_test cache_regression;
};

extern "C" void printTessCacheStats()
{
  PRINT("SHARED TESSELLATION CACHE");
  embree::SharedTessellationCacheStats::printStats();
  embree::SharedTessellationCacheStats::clearStats();
}
<|endoftext|>