// Copyright 2016 The SwiftShader Authors. All Rights Reserved.
//
// 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 "PixelShader.hpp"

#include "Vulkan/VkDebug.hpp"

#include <string.h>

namespace sw
{
	PixelShader::PixelShader(const PixelShader *ps) : Shader()
	{
		shaderModel = 0x0300;
		vPosDeclared = false;
		vFaceDeclared = false;
		centroid = false;

		if(ps)   // Make a copy
		{
			for(size_t i = 0; i < ps->getLength(); i++)
			{
				append(new sw::Shader::Instruction(*ps->getInstruction(i)));
			}

			memcpy(input, ps->input, sizeof(input));
			vPosDeclared = ps->vPosDeclared;
			vFaceDeclared = ps->vFaceDeclared;
			usedSamplers = ps->usedSamplers;

			optimize();
			analyze();
		}
	}

	PixelShader::PixelShader(const unsigned long *token) : Shader()
	{
		parse(token);

		vPosDeclared = false;
		vFaceDeclared = false;
		centroid = false;

		optimize();
		analyze();
	}

	PixelShader::~PixelShader()
	{
	}

	int PixelShader::validate(const unsigned long *const token)
	{
		if(!token)
		{
			return 0;
		}

		unsigned short version = (unsigned short)(token[0] & 0x0000FFFF);
		// unsigned char minorVersion = (unsigned char)(token[0] & 0x000000FF);
		unsigned char majorVersion = (unsigned char)((token[0] & 0x0000FF00) >> 8);
		ShaderType shaderType = (ShaderType)((token[0] & 0xFFFF0000) >> 16);

		if(shaderType != SHADER_PIXEL || majorVersion > 3)
		{
			return 0;
		}

		int instructionCount = 1;

		for(int i = 0; token[i] != 0x0000FFFF; i++)
		{
			if((token[i] & 0x0000FFFF) == 0x0000FFFE)   // Comment token
			{
				int length = (token[i] & 0x7FFF0000) >> 16;

				i += length;
			}
			else
			{
				Shader::Opcode opcode = (Shader::Opcode)(token[i] & 0x0000FFFF);

				switch(opcode)
				{
				case Shader::OPCODE_RESERVED0:
				case Shader::OPCODE_MOVA:
					return 0;   // Unsupported operation
				default:
					instructionCount++;
					break;
				}

				i += size(token[i], version);
			}
		}

		return instructionCount;
	}

	bool PixelShader::depthOverride() const
	{
		return zOverride;
	}

	bool PixelShader::containsKill() const
	{
		return kill;
	}

	bool PixelShader::containsCentroid() const
	{
		return centroid;
	}

	bool PixelShader::usesDiffuse(int component) const
	{
		return input[0][component].active();
	}

	bool PixelShader::usesSpecular(int component) const
	{
		return input[1][component].active();
	}

	bool PixelShader::usesTexture(int coordinate, int component) const
	{
		return input[2 + coordinate][component].active();
	}

	void PixelShader::setInput(int inputIdx, int nbComponents, const sw::Shader::Semantic& semantic)
	{
		for(int i = 0; i < nbComponents; ++i)
		{
			input[inputIdx][i] = semantic;
		}
	}

	const sw::Shader::Semantic& PixelShader::getInput(int inputIdx, int component) const
	{
		return input[inputIdx][component];
	}

	void PixelShader::analyze()
	{
		analyzeZOverride();
		analyzeKill();
		analyzeInterpolants();
		analyzeDirtyConstants();
		analyzeDynamicBranching();
		analyzeSamplers();
		analyzeCallSites();
		analyzeIndirectAddressing();
	}

	void PixelShader::analyzeZOverride()
	{
		zOverride = false;

		for(const auto &inst : instruction)
		{
			if(inst->opcode == Shader::OPCODE_TEXM3X2DEPTH ||
			   inst->opcode == Shader::OPCODE_TEXDEPTH ||
			   inst->dst.type == Shader::PARAMETER_DEPTHOUT)
			{
				zOverride = true;

				break;
			}
		}
	}

	void PixelShader::analyzeKill()
	{
		kill = false;

		for(const auto &inst : instruction)
		{
			if(inst->opcode == Shader::OPCODE_TEXKILL ||
			   inst->opcode == Shader::OPCODE_DISCARD)
			{
				kill = true;

				break;
			}
		}
	}

	void PixelShader::analyzeInterpolants()
	{
		if(shaderModel < 0x0300)
		{
			// Set default mapping; disable unused interpolants below
			input[0][0] = Semantic(Shader::USAGE_COLOR, 0);
			input[0][1] = Semantic(Shader::USAGE_COLOR, 0);
			input[0][2] = Semantic(Shader::USAGE_COLOR, 0);
			input[0][3] = Semantic(Shader::USAGE_COLOR, 0);

			input[1][0] = Semantic(Shader::USAGE_COLOR, 1);
			input[1][1] = Semantic(Shader::USAGE_COLOR, 1);
			input[1][2] = Semantic(Shader::USAGE_COLOR, 1);
			input[1][3] = Semantic(Shader::USAGE_COLOR, 1);

			for(int i = 0; i < 8; i++)
			{
				input[2 + i][0] = Semantic(Shader::USAGE_TEXCOORD, i);
				input[2 + i][1] = Semantic(Shader::USAGE_TEXCOORD, i);
				input[2 + i][2] = Semantic(Shader::USAGE_TEXCOORD, i);
				input[2 + i][3] = Semantic(Shader::USAGE_TEXCOORD, i);
			}

			Shader::SamplerType samplerType[16];

			for(int i = 0; i < 16; i++)
			{
				samplerType[i] = Shader::SAMPLER_UNKNOWN;
			}

			for(const auto &inst : instruction)
			{
				if(inst->dst.type == Shader::PARAMETER_SAMPLER)
				{
					int sampler = inst->dst.index;

					samplerType[sampler] = inst->samplerType;
				}
			}

			bool interpolant[MAX_FRAGMENT_INPUTS][4] = {{false}};   // Interpolants in use

			for(const auto &inst : instruction)
			{
				if(inst->dst.type == Shader::PARAMETER_TEXTURE)
				{
					int index = inst->dst.index + 2;

					switch(inst->opcode)
					{
					case Shader::OPCODE_TEX:
					case Shader::OPCODE_TEXBEM:
					case Shader::OPCODE_TEXBEML:
					case Shader::OPCODE_TEXCOORD:
					case Shader::OPCODE_TEXDP3:
					case Shader::OPCODE_TEXDP3TEX:
					case Shader::OPCODE_TEXM3X2DEPTH:
					case Shader::OPCODE_TEXM3X2PAD:
					case Shader::OPCODE_TEXM3X2TEX:
					case Shader::OPCODE_TEXM3X3:
					case Shader::OPCODE_TEXM3X3PAD:
					case Shader::OPCODE_TEXM3X3TEX:
						interpolant[index][0] = true;
						interpolant[index][1] = true;
						interpolant[index][2] = true;
						break;
					case Shader::OPCODE_TEXKILL:
						if(majorVersion < 2)
						{
							interpolant[index][0] = true;
							interpolant[index][1] = true;
							interpolant[index][2] = true;
						}
						else
						{
							interpolant[index][0] = true;
							interpolant[index][1] = true;
							interpolant[index][2] = true;
							interpolant[index][3] = true;
						}
						break;
					case Shader::OPCODE_TEXM3X3VSPEC:
						interpolant[index][0] = true;
						interpolant[index][1] = true;
						interpolant[index][2] = true;
						interpolant[index - 2][3] = true;
						interpolant[index - 1][3] = true;
						interpolant[index - 0][3] = true;
						break;
					case Shader::OPCODE_DCL:
						break;   // Ignore
					default:   // Arithmetic instruction
						if(shaderModel >= 0x0104)
						{
							ASSERT(false);
						}
					}
				}

				for(int argument = 0; argument < 4; argument++)
				{
					if(inst->src[argument].type == Shader::PARAMETER_INPUT ||
					   inst->src[argument].type == Shader::PARAMETER_TEXTURE)
					{
						int index = inst->src[argument].index;
						int swizzle = inst->src[argument].swizzle;
						int mask = inst->dst.mask;

						if(inst->src[argument].type == Shader::PARAMETER_TEXTURE)
						{
							index += 2;
						}

						switch(inst->opcode)
						{
						case Shader::OPCODE_TEX:
						case Shader::OPCODE_TEXLDD:
						case Shader::OPCODE_TEXLDL:
						case Shader::OPCODE_TEXLOD:
						case Shader::OPCODE_TEXBIAS:
						case Shader::OPCODE_TEXOFFSET:
						case Shader::OPCODE_TEXOFFSETBIAS:
						case Shader::OPCODE_TEXLODOFFSET:
						case Shader::OPCODE_TEXELFETCH:
						case Shader::OPCODE_TEXELFETCHOFFSET:
						case Shader::OPCODE_TEXGRAD:
						case Shader::OPCODE_TEXGRADOFFSET:
							{
								int sampler = inst->src[1].index;

								switch(samplerType[sampler])
								{
								case Shader::SAMPLER_UNKNOWN:
									if(shaderModel == 0x0104)
									{
										if((inst->src[0].swizzle & 0x30) == 0x20)   // .xyz
										{
											interpolant[index][0] = true;
											interpolant[index][1] = true;
											interpolant[index][2] = true;
										}
										else   // .xyw
										{
											interpolant[index][0] = true;
											interpolant[index][1] = true;
											interpolant[index][3] = true;
										}
									}
									else
									{
										ASSERT(false);
									}
									break;
								case Shader::SAMPLER_1D:
									interpolant[index][0] = true;
									break;
								case Shader::SAMPLER_2D:
									interpolant[index][0] = true;
									interpolant[index][1] = true;
									break;
								case Shader::SAMPLER_CUBE:
									interpolant[index][0] = true;
									interpolant[index][1] = true;
									interpolant[index][2] = true;
									break;
								case Shader::SAMPLER_VOLUME:
									interpolant[index][0] = true;
									interpolant[index][1] = true;
									interpolant[index][2] = true;
									break;
								default:
									ASSERT(false);
								}

								if(inst->bias)
								{
									interpolant[index][3] = true;
								}

								if(inst->project)
								{
									interpolant[index][3] = true;
								}

								if(shaderModel == 0x0104 && inst->opcode == Shader::OPCODE_TEX)
								{
									if(inst->src[0].modifier == Shader::MODIFIER_DZ)
									{
										interpolant[index][2] = true;
									}

									if(inst->src[0].modifier == Shader::MODIFIER_DW)
									{
										interpolant[index][3] = true;
									}
								}
							}
							break;
						case Shader::OPCODE_M3X2:
							if(mask & 0x1)
							{
								interpolant[index][0] |= swizzleContainsComponentMasked(swizzle, 0, 0x7);
								interpolant[index][1] |= swizzleContainsComponentMasked(swizzle, 1, 0x7);
								interpolant[index][2] |= swizzleContainsComponentMasked(swizzle, 2, 0x7);
								interpolant[index][3] |= swizzleContainsComponentMasked(swizzle, 3, 0x7);
							}

							if(argument == 1)
							{
								if(mask & 0x2)
								{
									interpolant[index + 1][0] |= swizzleContainsComponentMasked(swizzle, 0, 0x7);
									interpolant[index + 1][1] |= swizzleContainsComponentMasked(swizzle, 1, 0x7);
									interpolant[index + 1][2] |= swizzleContainsComponentMasked(swizzle, 2, 0x7);
									interpolant[index + 1][3] |= swizzleContainsComponentMasked(swizzle, 3, 0x7);
								}
							}
							break;
						case Shader::OPCODE_M3X3:
							if(mask & 0x1)
							{
								interpolant[index][0] |= swizzleContainsComponentMasked(swizzle, 0, 0x7);
								interpolant[index][1] |= swizzleContainsComponentMasked(swizzle, 1, 0x7);
								interpolant[index][2] |= swizzleContainsComponentMasked(swizzle, 2, 0x7);
								interpolant[index][3] |= swizzleContainsComponentMasked(swizzle, 3, 0x7);
							}

							if(argument == 1)
							{
								if(mask & 0x2)
								{
									interpolant[index + 1][0] |= swizzleContainsComponentMasked(swizzle, 0, 0x7);
									interpolant[index + 1][1] |= swizzleContainsComponentMasked(swizzle, 1, 0x7);
									interpolant[index + 1][2] |= swizzleContainsComponentMasked(swizzle, 2, 0x7);
									interpolant[index + 1][3] |= swizzleContainsComponentMasked(swizzle, 3, 0x7);
								}

								if(mask & 0x4)
								{
									interpolant[index + 2][0] |= swizzleContainsComponentMasked(swizzle, 0, 0x7);
									interpolant[index + 2][1] |= swizzleContainsComponentMasked(swizzle, 1, 0x7);
									interpolant[index + 2][2] |= swizzleContainsComponentMasked(swizzle, 2, 0x7);
									interpolant[index + 2][3] |= swizzleContainsComponentMasked(swizzle, 3, 0x7);
								}
							}
							break;
						case Shader::OPCODE_M3X4:
							if(mask & 0x1)
							{
								interpolant[index][0] |= swizzleContainsComponentMasked(swizzle, 0, 0x7);
								interpolant[index][1] |= swizzleContainsComponentMasked(swizzle, 1, 0x7);
								interpolant[index][2] |= swizzleContainsComponentMasked(swizzle, 2, 0x7);
								interpolant[index][3] |= swizzleContainsComponentMasked(swizzle, 3, 0x7);
							}

							if(argument == 1)
							{
								if(mask & 0x2)
								{
									interpolant[index + 1][0] |= swizzleContainsComponentMasked(swizzle, 0, 0x7);
									interpolant[index + 1][1] |= swizzleContainsComponentMasked(swizzle, 1, 0x7);
									interpolant[index + 1][2] |= swizzleContainsComponentMasked(swizzle, 2, 0x7);
									interpolant[index + 1][3] |= swizzleContainsComponentMasked(swizzle, 3, 0x7);
								}

								if(mask & 0x4)
								{
									interpolant[index + 2][0] |= swizzleContainsComponentMasked(swizzle, 0, 0x7);
									interpolant[index + 2][1] |= swizzleContainsComponentMasked(swizzle, 1, 0x7);
									interpolant[index + 2][2] |= swizzleContainsComponentMasked(swizzle, 2, 0x7);
									interpolant[index + 2][3] |= swizzleContainsComponentMasked(swizzle, 3, 0x7);
								}

								if(mask & 0x8)
								{
									interpolant[index + 3][0] |= swizzleContainsComponentMasked(swizzle, 0, 0x7);
									interpolant[index + 3][1] |= swizzleContainsComponentMasked(swizzle, 1, 0x7);
									interpolant[index + 3][2] |= swizzleContainsComponentMasked(swizzle, 2, 0x7);
									interpolant[index + 3][3] |= swizzleContainsComponentMasked(swizzle, 3, 0x7);
								}
							}
							break;
						case Shader::OPCODE_M4X3:
							if(mask & 0x1)
							{
								interpolant[index][0] |= swizzleContainsComponent(swizzle, 0);
								interpolant[index][1] |= swizzleContainsComponent(swizzle, 1);
								interpolant[index][2] |= swizzleContainsComponent(swizzle, 2);
								interpolant[index][3] |= swizzleContainsComponent(swizzle, 3);
							}

							if(argument == 1)
							{
								if(mask & 0x2)
								{
									interpolant[index + 1][0] |= swizzleContainsComponent(swizzle, 0);
									interpolant[index + 1][1] |= swizzleContainsComponent(swizzle, 1);
									interpolant[index + 1][2] |= swizzleContainsComponent(swizzle, 2);
									interpolant[index + 1][3] |= swizzleContainsComponent(swizzle, 3);
								}

								if(mask & 0x4)
								{
									interpolant[index + 2][0] |= swizzleContainsComponent(swizzle, 0);
									interpolant[index + 2][1] |= swizzleContainsComponent(swizzle, 1);
									interpolant[index + 2][2] |= swizzleContainsComponent(swizzle, 2);
									interpolant[index + 2][3] |= swizzleContainsComponent(swizzle, 3);
								}
							}
							break;
						case Shader::OPCODE_M4X4:
							if(mask & 0x1)
							{
								interpolant[index][0] |= swizzleContainsComponent(swizzle, 0);
								interpolant[index][1] |= swizzleContainsComponent(swizzle, 1);
								interpolant[index][2] |= swizzleContainsComponent(swizzle, 2);
								interpolant[index][3] |= swizzleContainsComponent(swizzle, 3);
							}

							if(argument == 1)
							{
								if(mask & 0x2)
								{
									interpolant[index + 1][0] |= swizzleContainsComponent(swizzle, 0);
									interpolant[index + 1][1] |= swizzleContainsComponent(swizzle, 1);
									interpolant[index + 1][2] |= swizzleContainsComponent(swizzle, 2);
									interpolant[index + 1][3] |= swizzleContainsComponent(swizzle, 3);
								}

								if(mask & 0x4)
								{
									interpolant[index + 2][0] |= swizzleContainsComponent(swizzle, 0);
									interpolant[index + 2][1] |= swizzleContainsComponent(swizzle, 1);
									interpolant[index + 2][2] |= swizzleContainsComponent(swizzle, 2);
									interpolant[index + 2][3] |= swizzleContainsComponent(swizzle, 3);
								}

								if(mask & 0x8)
								{
									interpolant[index + 3][0] |= swizzleContainsComponent(swizzle, 0);
									interpolant[index + 3][1] |= swizzleContainsComponent(swizzle, 1);
									interpolant[index + 3][2] |= swizzleContainsComponent(swizzle, 2);
									interpolant[index + 3][3] |= swizzleContainsComponent(swizzle, 3);
								}
							}
							break;
						case Shader::OPCODE_CRS:
							if(mask & 0x1)
							{
								interpolant[index][0] |= swizzleContainsComponentMasked(swizzle, 0, 0x6);
								interpolant[index][1] |= swizzleContainsComponentMasked(swizzle, 1, 0x6);
								interpolant[index][2] |= swizzleContainsComponentMasked(swizzle, 2, 0x6);
								interpolant[index][3] |= swizzleContainsComponentMasked(swizzle, 3, 0x6);
							}

							if(mask & 0x2)
							{
								interpolant[index][0] |= swizzleContainsComponentMasked(swizzle, 0, 0x5);
								interpolant[index][1] |= swizzleContainsComponentMasked(swizzle, 1, 0x5);
								interpolant[index][2] |= swizzleContainsComponentMasked(swizzle, 2, 0x5);
								interpolant[index][3] |= swizzleContainsComponentMasked(swizzle, 3, 0x5);
							}

							if(mask & 0x4)
							{
								interpolant[index][0] |= swizzleContainsComponentMasked(swizzle, 0, 0x3);
								interpolant[index][1] |= swizzleContainsComponentMasked(swizzle, 1, 0x3);
								interpolant[index][2] |= swizzleContainsComponentMasked(swizzle, 2, 0x3);
								interpolant[index][3] |= swizzleContainsComponentMasked(swizzle, 3, 0x3);
							}
							break;
						case Shader::OPCODE_DP2ADD:
							if(argument == 0 || argument == 1)
							{
								interpolant[index][0] |= swizzleContainsComponentMasked(swizzle, 0, 0x3);
								interpolant[index][1] |= swizzleContainsComponentMasked(swizzle, 1, 0x3);
								interpolant[index][2] |= swizzleContainsComponentMasked(swizzle, 2, 0x3);
								interpolant[index][3] |= swizzleContainsComponentMasked(swizzle, 3, 0x3);
							}
							else   // argument == 2
							{
								interpolant[index][0] |= swizzleContainsComponent(swizzle, 0);
								interpolant[index][1] |= swizzleContainsComponent(swizzle, 1);
								interpolant[index][2] |= swizzleContainsComponent(swizzle, 2);
								interpolant[index][3] |= swizzleContainsComponent(swizzle, 3);
							}
							break;
						case Shader::OPCODE_DP3:
							interpolant[index][0] |= swizzleContainsComponentMasked(swizzle, 0, 0x7);
							interpolant[index][1] |= swizzleContainsComponentMasked(swizzle, 1, 0x7);
							interpolant[index][2] |= swizzleContainsComponentMasked(swizzle, 2, 0x7);
							interpolant[index][3] |= swizzleContainsComponentMasked(swizzle, 3, 0x7);
							break;
						case Shader::OPCODE_DP4:
							interpolant[index][0] |= swizzleContainsComponent(swizzle, 0);
							interpolant[index][1] |= swizzleContainsComponent(swizzle, 1);
							interpolant[index][2] |= swizzleContainsComponent(swizzle, 2);
							interpolant[index][3] |= swizzleContainsComponent(swizzle, 3);
							break;
						case Shader::OPCODE_SINCOS:
						case Shader::OPCODE_EXP2X:
						case Shader::OPCODE_LOG2X:
						case Shader::OPCODE_POWX:
						case Shader::OPCODE_RCPX:
						case Shader::OPCODE_RSQX:
							interpolant[index][0] |= swizzleContainsComponent(swizzle, 0);
							interpolant[index][1] |= swizzleContainsComponent(swizzle, 1);
							interpolant[index][2] |= swizzleContainsComponent(swizzle, 2);
							interpolant[index][3] |= swizzleContainsComponent(swizzle, 3);
							break;
						case Shader::OPCODE_NRM3:
							interpolant[index][0] |= swizzleContainsComponentMasked(swizzle, 0, 0x7 | mask);
							interpolant[index][1] |= swizzleContainsComponentMasked(swizzle, 1, 0x7 | mask);
							interpolant[index][2] |= swizzleContainsComponentMasked(swizzle, 2, 0x7 | mask);
							interpolant[index][3] |= swizzleContainsComponentMasked(swizzle, 3, 0x7 | mask);
							break;
						case Shader::OPCODE_MOV:
						case Shader::OPCODE_ADD:
						case Shader::OPCODE_SUB:
						case Shader::OPCODE_MUL:
						case Shader::OPCODE_MAD:
						case Shader::OPCODE_ABS:
						case Shader::OPCODE_CMP0:
						case Shader::OPCODE_CND:
						case Shader::OPCODE_FRC:
						case Shader::OPCODE_LRP:
						case Shader::OPCODE_MAX:
						case Shader::OPCODE_MIN:
						case Shader::OPCODE_CMP:
						case Shader::OPCODE_BREAKC:
						case Shader::OPCODE_DFDX:
						case Shader::OPCODE_DFDY:
							interpolant[index][0] |= swizzleContainsComponentMasked(swizzle, 0, mask);
							interpolant[index][1] |= swizzleContainsComponentMasked(swizzle, 1, mask);
							interpolant[index][2] |= swizzleContainsComponentMasked(swizzle, 2, mask);
							interpolant[index][3] |= swizzleContainsComponentMasked(swizzle, 3, mask);
							break;
						case Shader::OPCODE_TEXCOORD:
							interpolant[index][0] = true;
							interpolant[index][1] = true;
							interpolant[index][2] = true;
							interpolant[index][3] = true;
							break;
						case Shader::OPCODE_TEXDP3:
						case Shader::OPCODE_TEXDP3TEX:
						case Shader::OPCODE_TEXM3X2PAD:
						case Shader::OPCODE_TEXM3X3PAD:
						case Shader::OPCODE_TEXM3X2TEX:
						case Shader::OPCODE_TEXM3X3SPEC:
						case Shader::OPCODE_TEXM3X3VSPEC:
						case Shader::OPCODE_TEXBEM:
						case Shader::OPCODE_TEXBEML:
						case Shader::OPCODE_TEXM3X2DEPTH:
						case Shader::OPCODE_TEXM3X3:
						case Shader::OPCODE_TEXM3X3TEX:
							interpolant[index][0] = true;
							interpolant[index][1] = true;
							interpolant[index][2] = true;
							break;
						case Shader::OPCODE_TEXREG2AR:
						case Shader::OPCODE_TEXREG2GB:
						case Shader::OPCODE_TEXREG2RGB:
							break;
						default:
						//	ASSERT(false);   // Refine component usage
							interpolant[index][0] = true;
							interpolant[index][1] = true;
							interpolant[index][2] = true;
							interpolant[index][3] = true;
						}
					}
				}
			}

			for(int index = 0; index < MAX_FRAGMENT_INPUTS; index++)
			{
				for(int component = 0; component < 4; component++)
				{
					if(!interpolant[index][component])
					{
						input[index][component] = Semantic();
					}
				}
			}
		}
		else   // Shader Model 3.0 input declaration; v# indexable
		{
			for(const auto &inst : instruction)
			{
				if(inst->opcode == Shader::OPCODE_DCL)
				{
					if(inst->dst.type == Shader::PARAMETER_INPUT)
					{
						unsigned char usage = inst->usage;
						unsigned char index = inst->usageIndex;
						unsigned char mask = inst->dst.mask;
						unsigned char reg = inst->dst.index;

						if(mask & 0x01) input[reg][0] = Semantic(usage, index);
						if(mask & 0x02) input[reg][1] = Semantic(usage, index);
						if(mask & 0x04) input[reg][2] = Semantic(usage, index);
						if(mask & 0x08) input[reg][3] = Semantic(usage, index);
					}
					else if(inst->dst.type == Shader::PARAMETER_MISCTYPE)
					{
						unsigned char index = inst->dst.index;

						if(index == Shader::VPosIndex)
						{
							vPosDeclared = true;
						}
						else if(index == Shader::VFaceIndex)
						{
							vFaceDeclared = true;
						}
						else ASSERT(false);
					}
				}
			}
		}

		if(shaderModel >= 0x0200)
		{
			for(const auto &inst : instruction)
			{
				if(inst->opcode == Shader::OPCODE_DCL)
				{
					bool centroid = inst->dst.centroid;
					unsigned char reg = inst->dst.index;

					switch(inst->dst.type)
					{
					case Shader::PARAMETER_INPUT:
						input[reg][0].centroid = centroid;
						break;
					case Shader::PARAMETER_TEXTURE:
						input[2 + reg][0].centroid = centroid;
						break;
					default:
						break;
					}

					this->centroid = this->centroid || centroid;
				}
			}
		}
	}
}