// 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 "Debug.hpp" #include <string.h> namespace sw { PixelShader::PixelShader(const PixelShader *ps) : Shader() { version = 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(semantic, ps->semantic, sizeof(semantic)); 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 semantic[0][component].active(); } bool PixelShader::usesSpecular(int component) const { return semantic[1][component].active(); } bool PixelShader::usesTexture(int coordinate, int component) const { return semantic[2 + coordinate][component].active(); } void PixelShader::analyze() { analyzeZOverride(); analyzeKill(); analyzeInterpolants(); analyzeDirtyConstants(); analyzeDynamicBranching(); analyzeSamplers(); analyzeCallSites(); analyzeDynamicIndexing(); } void PixelShader::analyzeZOverride() { zOverride = false; for(unsigned int i = 0; i < instruction.size(); i++) { if(instruction[i]->opcode == Shader::OPCODE_TEXM3X2DEPTH || instruction[i]->opcode == Shader::OPCODE_TEXDEPTH || instruction[i]->dst.type == Shader::PARAMETER_DEPTHOUT) { zOverride = true; break; } } } void PixelShader::analyzeKill() { kill = false; for(unsigned int i = 0; i < instruction.size(); i++) { if(instruction[i]->opcode == Shader::OPCODE_TEXKILL || instruction[i]->opcode == Shader::OPCODE_DISCARD) { kill = true; break; } } } void PixelShader::analyzeInterpolants() { if(version < 0x0300) { // Set default mapping; disable unused interpolants below semantic[0][0] = Semantic(Shader::USAGE_COLOR, 0); semantic[0][1] = Semantic(Shader::USAGE_COLOR, 0); semantic[0][2] = Semantic(Shader::USAGE_COLOR, 0); semantic[0][3] = Semantic(Shader::USAGE_COLOR, 0); semantic[1][0] = Semantic(Shader::USAGE_COLOR, 1); semantic[1][1] = Semantic(Shader::USAGE_COLOR, 1); semantic[1][2] = Semantic(Shader::USAGE_COLOR, 1); semantic[1][3] = Semantic(Shader::USAGE_COLOR, 1); for(int i = 0; i < 8; i++) { semantic[2 + i][0] = Semantic(Shader::USAGE_TEXCOORD, i); semantic[2 + i][1] = Semantic(Shader::USAGE_TEXCOORD, i); semantic[2 + i][2] = Semantic(Shader::USAGE_TEXCOORD, i); semantic[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(unsigned int i = 0; i < instruction.size(); i++) { if(instruction[i]->dst.type == Shader::PARAMETER_SAMPLER) { int sampler = instruction[i]->dst.index; samplerType[sampler] = instruction[i]->samplerType; } } bool interpolant[MAX_FRAGMENT_INPUTS][4] = {{false}}; // Interpolants in use for(unsigned int i = 0; i < instruction.size(); i++) { if(instruction[i]->dst.type == Shader::PARAMETER_TEXTURE) { int index = instruction[i]->dst.index + 2; int mask = instruction[i]->dst.mask; switch(instruction[i]->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(version >= 0x0104) { ASSERT(false); } } } for(int argument = 0; argument < 4; argument++) { if(instruction[i]->src[argument].type == Shader::PARAMETER_INPUT || instruction[i]->src[argument].type == Shader::PARAMETER_TEXTURE) { int index = instruction[i]->src[argument].index; int swizzle = instruction[i]->src[argument].swizzle; int mask = instruction[i]->dst.mask; if(instruction[i]->src[argument].type == Shader::PARAMETER_TEXTURE) { index += 2; } switch(instruction[i]->opcode) { case Shader::OPCODE_TEX: case Shader::OPCODE_TEXLDD: case Shader::OPCODE_TEXLDL: case Shader::OPCODE_TEXOFFSET: case Shader::OPCODE_TEXLDLOFFSET: case Shader::OPCODE_TEXELFETCH: case Shader::OPCODE_TEXELFETCHOFFSET: case Shader::OPCODE_TEXGRAD: case Shader::OPCODE_TEXGRADOFFSET: { int sampler = instruction[i]->src[1].index; switch(samplerType[sampler]) { case Shader::SAMPLER_UNKNOWN: if(version == 0x0104) { if((instruction[i]->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(instruction[i]->bias) { interpolant[index][3] = true; } if(instruction[i]->project) { interpolant[index][3] = true; } if(version == 0x0104 && instruction[i]->opcode == Shader::OPCODE_TEX) { if(instruction[i]->src[0].modifier == Shader::MODIFIER_DZ) { interpolant[index][2] = true; } if(instruction[i]->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]) { semantic[index][component] = Semantic(); } } } } else // Shader Model 3.0 input declaration; v# indexable { for(unsigned int i = 0; i < instruction.size(); i++) { if(instruction[i]->opcode == Shader::OPCODE_DCL) { if(instruction[i]->dst.type == Shader::PARAMETER_INPUT) { unsigned char usage = instruction[i]->usage; unsigned char index = instruction[i]->usageIndex; unsigned char mask = instruction[i]->dst.mask; unsigned char reg = instruction[i]->dst.index; if(mask & 0x01) semantic[reg][0] = Semantic(usage, index); if(mask & 0x02) semantic[reg][1] = Semantic(usage, index); if(mask & 0x04) semantic[reg][2] = Semantic(usage, index); if(mask & 0x08) semantic[reg][3] = Semantic(usage, index); } else if(instruction[i]->dst.type == Shader::PARAMETER_MISCTYPE) { unsigned char index = instruction[i]->dst.index; if(index == 0) { vPosDeclared = true; } else if(index == 1) { vFaceDeclared = true; } else ASSERT(false); } } } } if(version >= 0x0200) { for(unsigned int i = 0; i < instruction.size(); i++) { if(instruction[i]->opcode == Shader::OPCODE_DCL) { bool centroid = instruction[i]->dst.centroid; unsigned char reg = instruction[i]->dst.index; switch(instruction[i]->dst.type) { case Shader::PARAMETER_INPUT: semantic[reg][0].centroid = centroid; break; case Shader::PARAMETER_TEXTURE: semantic[2 + reg][0].centroid = centroid; break; default: break; } this->centroid = this->centroid || centroid; } } } } }