#include #include "internal_includes/HLSLccToolkit.h" #include "internal_includes/HLSLCrossCompilerContext.h" #include "hlslcc.h" #include "internal_includes/debug.h" #include "internal_includes/Shader.h" #include "internal_includes/toMetal.h" #include #include #include #include using namespace HLSLcc; #ifdef _MSC_VER #if _MSC_VER < 1900 #define snprintf _snprintf #endif #endif #ifndef fpcheck #ifdef _MSC_VER #define fpcheck(x) (_isnan(x) || !_finite(x)) #else #define fpcheck(x) (std::isnan(x) || std::isinf(x)) #endif #endif // #ifndef fpcheck // Returns nonzero if types are just different precisions of the same underlying type static bool AreTypesCompatibleMetal(SHADER_VARIABLE_TYPE a, uint32_t ui32TOFlag) { SHADER_VARIABLE_TYPE b = TypeFlagsToSVTType(ui32TOFlag); if (a == b) return true; // Special case for array indices: both uint and int are fine if ((ui32TOFlag & TO_FLAG_INTEGER) && (ui32TOFlag & TO_FLAG_UNSIGNED_INTEGER) && (a == SVT_INT || a == SVT_INT16 || a == SVT_UINT || a == SVT_UINT16)) return true; return false; } std::string ToMetal::TranslateOperandSwizzle(const Operand* psOperand, uint32_t ui32ComponentMask, int iRebase, bool includeDot /*= true*/) { std::ostringstream oss; uint32_t accessMask = ui32ComponentMask & psOperand->GetAccessMask(); if (psOperand->eType == OPERAND_TYPE_INPUT) { int regSpace = psOperand->GetRegisterSpace(psContext); // Skip swizzle for scalar inputs, but only if we haven't redirected them if (regSpace == 0) { if ((psContext->psShader->asPhases[psContext->currentPhase].acInputNeedsRedirect[psOperand->ui32RegisterNumber] == 0) && (psContext->psShader->abScalarInput[regSpace][psOperand->ui32RegisterNumber] & accessMask)) { return ""; } } else { if ((psContext->psShader->asPhases[psContext->currentPhase].acPatchConstantsNeedsRedirect[psOperand->ui32RegisterNumber] == 0) && (psContext->psShader->abScalarInput[regSpace][psOperand->ui32RegisterNumber] & accessMask)) { return ""; } } } if (psOperand->eType == OPERAND_TYPE_OUTPUT) { int regSpace = psOperand->GetRegisterSpace(psContext); // Skip swizzle for scalar outputs, but only if we haven't redirected them if (regSpace == 0) { if ((psContext->psShader->asPhases[psContext->currentPhase].acOutputNeedsRedirect[psOperand->ui32RegisterNumber] == 0) && (psContext->psShader->abScalarOutput[regSpace][psOperand->ui32RegisterNumber] & accessMask)) { return ""; } } else { if ((psContext->psShader->asPhases[psContext->currentPhase].acPatchConstantsNeedsRedirect[psOperand->ui32RegisterNumber] == 0) && (psContext->psShader->abScalarOutput[regSpace][psOperand->ui32RegisterNumber] & accessMask)) { return ""; } } } if (psOperand->iWriteMaskEnabled && psOperand->iNumComponents != 1) { //Component Mask if (psOperand->eSelMode == OPERAND_4_COMPONENT_MASK_MODE) { uint32_t mask; if (psOperand->ui32CompMask != 0) mask = psOperand->ui32CompMask & ui32ComponentMask; else mask = ui32ComponentMask; if (mask != 0 && mask != OPERAND_4_COMPONENT_MASK_ALL) { if (includeDot) oss << "."; if (mask & OPERAND_4_COMPONENT_MASK_X) { ASSERT(iRebase == 0); oss << "x"; } if (mask & OPERAND_4_COMPONENT_MASK_Y) { ASSERT(iRebase <= 1); oss << "xy"[1 - iRebase]; } if (mask & OPERAND_4_COMPONENT_MASK_Z) { ASSERT(iRebase <= 2); oss << "xyz"[2 - iRebase]; } if (mask & OPERAND_4_COMPONENT_MASK_W) { ASSERT(iRebase <= 3); oss << "xyzw"[3 - iRebase]; } } } else //Component Swizzle if (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) { if (ui32ComponentMask != OPERAND_4_COMPONENT_MASK_ALL || !(psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X && psOperand->aui32Swizzle[1] == OPERAND_4_COMPONENT_Y && psOperand->aui32Swizzle[2] == OPERAND_4_COMPONENT_Z && psOperand->aui32Swizzle[3] == OPERAND_4_COMPONENT_W ) ) { uint32_t i; if (includeDot) oss << "."; for (i = 0; i < 4; ++i) { if (!(ui32ComponentMask & (OPERAND_4_COMPONENT_MASK_X << i))) continue; if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_X) { ASSERT(iRebase == 0); oss << "x"; } else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Y) { ASSERT(iRebase <= 1); oss << "xy"[1 - iRebase]; } else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_Z) { ASSERT(iRebase <= 2); oss << "xyz"[2 - iRebase]; } else if (psOperand->aui32Swizzle[i] == OPERAND_4_COMPONENT_W) { ASSERT(iRebase <= 3); oss << "xyzw"[3 - iRebase]; } } } } else if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) // ui32ComponentMask is ignored in this case { if (includeDot) oss << "."; if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_X) { ASSERT(iRebase == 0); oss << "x"; } else if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Y) { ASSERT(iRebase <= 1); oss << "xy"[1 - iRebase]; } else if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_Z) { ASSERT(iRebase <= 2); oss << "xyz"[2 - iRebase]; } else if (psOperand->aui32Swizzle[0] == OPERAND_4_COMPONENT_W) { ASSERT(iRebase <= 3); oss << "xyzw"[3 - iRebase]; } } } return oss.str(); } std::string ToMetal::TranslateOperandIndex(const Operand* psOperand, int index) { int i = index; std::ostringstream oss; ASSERT(index < psOperand->iIndexDims); switch (psOperand->eIndexRep[i]) { case OPERAND_INDEX_IMMEDIATE32: { oss << "[" << psOperand->aui32ArraySizes[i] << "]"; return oss.str(); } case OPERAND_INDEX_RELATIVE: { oss << "[" << TranslateOperand(psOperand->m_SubOperands[i].get(), TO_FLAG_INTEGER) << "]"; return oss.str(); } case OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE: { oss << "[" << TranslateOperand(psOperand->m_SubOperands[i].get(), TO_FLAG_INTEGER) << " + " << psOperand->aui32ArraySizes[i] << "]"; return oss.str(); } default: { ASSERT(0); return ""; break; } } } /*static std::string GetBitcastOp(HLSLCrossCompilerContext *psContext, SHADER_VARIABLE_TYPE from, SHADER_VARIABLE_TYPE to, uint32_t numComponents) { if (psContext->psShader->eTargetLanguage == LANG_METAL) { std::ostringstream oss; oss << "as_type<"; oss << GetConstructorForTypeMetal(to, numComponents); oss << ">"; return oss.str(); } else { if ((to == SVT_FLOAT || to == SVT_FLOAT16 || to == SVT_FLOAT10) && from == SVT_INT) return "intBitsToFloat"; else if ((to == SVT_FLOAT || to == SVT_FLOAT16 || to == SVT_FLOAT10) && from == SVT_UINT) return "uintBitsToFloat"; else if (to == SVT_INT && from == SVT_FLOAT) return "floatBitsToInt"; else if (to == SVT_UINT && from == SVT_FLOAT) return "floatBitsToUint"; } ASSERT(0); return "ERROR missing components in GetBitcastOp()"; }*/ // Helper function to print floats with full precision static std::string printFloat(float f) { char temp[30]; snprintf(temp, 30, "%.9g", f); char * ePos = strchr(temp, 'e'); char * pointPos = strchr(temp, '.'); if (ePos == NULL && pointPos == NULL && !fpcheck(f)) return std::string(temp) + ".0"; else return std::string(temp); } // Helper function to print out a single 32-bit immediate value in desired format static std::string printImmediate32(uint32_t value, SHADER_VARIABLE_TYPE eType) { std::ostringstream oss; int needsParenthesis = 0; // Print floats as bit patterns. if ((eType == SVT_FLOAT || eType == SVT_FLOAT16 || eType == SVT_FLOAT10) && fpcheck(*((float *)(&value)))) { oss << "as_type("; eType = SVT_INT; needsParenthesis = 1; } switch (eType) { default: ASSERT(0); case SVT_INT: case SVT_INT16: case SVT_INT12: // Need special handling for anything >= uint 0x3fffffff if (value > 0x3ffffffe) oss << "int(0x" << std::hex << value << "u)"; else oss << "0x" << std::hex << value << ""; break; case SVT_UINT: case SVT_UINT16: oss << "0x" << std::hex << value << "u"; break; case SVT_FLOAT: case SVT_FLOAT10: case SVT_FLOAT16: oss << printFloat(*((float *)(&value))); break; case SVT_BOOL: if (value == 0) oss << "false"; else oss << "true"; } if (needsParenthesis) oss << ")"; return oss.str(); } static std::string MakeCBVarName(const std::string &cbName, const std::string &fullName, bool isUnityInstancingBuffer) { // For Unity instancing buffer: "CBufferName.StructTypeName[] -> CBufferName[]". See ToMetal::DeclareConstantBuffer. if (isUnityInstancingBuffer && !cbName.empty() && cbName[cbName.size() - 1] == '.' && fullName.find_first_of('[') != std::string::npos) { return cbName.substr(0, cbName.size() - 1) + fullName.substr(fullName.find_first_of('[')); } return cbName + fullName; } std::string ToMetal::TranslateVariableName(const Operand* psOperand, uint32_t ui32TOFlag, uint32_t* pui32IgnoreSwizzle, uint32_t ui32CompMask, int *piRebase) { std::ostringstream oss; int numParenthesis = 0; int hasCtor = 0; int needsBoolUpscale = 0; // If nonzero, bools need * 0xffffffff in them SHADER_VARIABLE_TYPE requestedType = TypeFlagsToSVTType(ui32TOFlag); SHADER_VARIABLE_TYPE eType = psOperand->GetDataType(psContext, requestedType); int numComponents = psOperand->GetNumSwizzleElements(ui32CompMask); int requestedComponents = 0; int scalarWithSwizzle = 0; *pui32IgnoreSwizzle = 0; if (psOperand->eType == OPERAND_TYPE_TEMP) { // Check for scalar if (psContext->psShader->GetTempComponentCount(eType, psOperand->ui32RegisterNumber) == 1 && psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE) { scalarWithSwizzle = 1; // Going to need a constructor } } if (psOperand->eType == OPERAND_TYPE_INPUT) { // Check for scalar // You would think checking would be easy but there is a caveat: // checking abScalarInput might report as scalar, while in reality that was redirected and now is vector so swizzle must be preserved // as an example consider we have input: // float2 x; float y; // and later on we do // tex2D(xxx, fixed2(x.x, y)); // in that case we will generate redirect but which ui32RegisterNumber will be used for it is not strictly "specified" // so we may end up with treating it as scalar (even though it is vector now) const int redirectInput = psContext->psShader->asPhases[psContext->currentPhase].acInputNeedsRedirect[psOperand->ui32RegisterNumber]; const bool wasRedirected = redirectInput == 0xFF || redirectInput == 0xFE; const int scalarInput = psContext->psShader->abScalarInput[psOperand->GetRegisterSpace(psContext)][psOperand->ui32RegisterNumber]; if (!wasRedirected && (scalarInput & psOperand->GetAccessMask()) && (psOperand->eSelMode == OPERAND_4_COMPONENT_SWIZZLE_MODE)) { scalarWithSwizzle = 1; *pui32IgnoreSwizzle = 1; } } if (piRebase) *piRebase = 0; if (ui32TOFlag & TO_AUTO_EXPAND_TO_VEC2) requestedComponents = 2; else if (ui32TOFlag & TO_AUTO_EXPAND_TO_VEC3) requestedComponents = 3; else if (ui32TOFlag & TO_AUTO_EXPAND_TO_VEC4) requestedComponents = 4; requestedComponents = std::max(requestedComponents, numComponents); if (!(ui32TOFlag & (TO_FLAG_DESTINATION | TO_FLAG_NAME_ONLY | TO_FLAG_DECLARATION_NAME))) { if (psOperand->eType == OPERAND_TYPE_IMMEDIATE32 || psOperand->eType == OPERAND_TYPE_IMMEDIATE64) { // Mark the operand type to match whatever we're asking for in the flags. ((Operand *)psOperand)->aeDataType[0] = requestedType; ((Operand *)psOperand)->aeDataType[1] = requestedType; ((Operand *)psOperand)->aeDataType[2] = requestedType; ((Operand *)psOperand)->aeDataType[3] = requestedType; } bool bitcast = false; if (AreTypesCompatibleMetal(eType, ui32TOFlag) == 0) { if (CanDoDirectCast(psContext, eType, requestedType)) { hasCtor = 1; if (eType == SVT_BOOL) { needsBoolUpscale = 1; // make sure to wrap the whole thing in parens so the upscale // multiply only applies to the bool oss << "("; numParenthesis++; } oss << GetConstructorForType(psContext, requestedType, requestedComponents, false) << "("; numParenthesis++; } else { // Direct cast not possible, need to do bitcast. oss << "as_type<" << GetConstructorForTypeMetal(requestedType, requestedComponents) << ">("; hasCtor = 1; bitcast = true; numParenthesis++; } } // Add ctor if needed (upscaling). Type conversion is already handled above, so here we must // use the original type to not make type conflicts in bitcasts bool needsUpscaling = ((numComponents < requestedComponents) || (scalarWithSwizzle != 0)) && (hasCtor == 0 || bitcast); // Add constuctor if half precision is forced to avoid template ambiguity error from compiler bool needsForcedCtor = (ui32TOFlag & TO_FLAG_FORCE_HALF) && (psOperand->eType == OPERAND_TYPE_IMMEDIATE32 || psOperand->eType == OPERAND_TYPE_IMMEDIATE64); if (needsForcedCtor) requestedComponents = std::max(requestedComponents, 1); if (needsUpscaling || needsForcedCtor) { oss << GetConstructorForType(psContext, eType, requestedComponents, false) << "("; numParenthesis++; hasCtor = 1; } } switch (psOperand->eType) { case OPERAND_TYPE_IMMEDIATE32: { if (psOperand->iNumComponents == 1) { oss << printImmediate32(*((unsigned int*)(&psOperand->afImmediates[0])), requestedType); } else { int i; int firstItemAdded = 0; if (hasCtor == 0) { oss << GetConstructorForTypeMetal(requestedType, requestedComponents) << "("; numParenthesis++; hasCtor = 1; } for (i = 0; i < 4; i++) { uint32_t uval; if (!(ui32CompMask & (1 << i))) continue; if (firstItemAdded) oss << ", "; uval = *((uint32_t*)(&psOperand->afImmediates[i >= psOperand->iNumComponents ? psOperand->iNumComponents - 1 : i])); oss << printImmediate32(uval, requestedType); firstItemAdded = 1; } oss << ")"; *pui32IgnoreSwizzle = 1; numParenthesis--; } break; } case OPERAND_TYPE_IMMEDIATE64: { ASSERT(0); // doubles not supported on Metal break; } case OPERAND_TYPE_INPUT: { int regSpace = psOperand->GetRegisterSpace(psContext); switch (psOperand->iIndexDims) { case INDEX_2D: { const ShaderInfo::InOutSignature *psSig = NULL; psContext->psShader->sInfo.GetInputSignatureFromRegister(psOperand->ui32RegisterNumber, psOperand->ui32CompMask, &psSig); if (psContext->psShader->eShaderType == HULL_SHADER || psContext->psShader->eShaderType == DOMAIN_SHADER) { oss << "input.cp"; oss << TranslateOperandIndex(psOperand, 0);//Vertex index oss << "." << psContext->GetDeclaredInputName(psOperand, piRebase, 1, pui32IgnoreSwizzle); } else { // Not sure if this codepath is active outside hull/domain oss << psContext->GetDeclaredInputName(psOperand, piRebase, 0, pui32IgnoreSwizzle); oss << TranslateOperandIndex(psOperand, 0);//Vertex index } break; } default: { if (psOperand->eIndexRep[0] == OPERAND_INDEX_IMMEDIATE32_PLUS_RELATIVE) { ASSERT(psContext->psShader->aIndexedInput[regSpace][psOperand->ui32RegisterNumber] != 0); oss << "phase" << psContext->currentPhase << "_Input" << regSpace << "_" << psOperand->ui32RegisterNumber << "["; oss << TranslateOperand(psOperand->m_SubOperands[0].get(), TO_FLAG_INTEGER); oss << "]"; } else { if (psContext->psShader->aIndexedInput[regSpace][psOperand->ui32RegisterNumber] != 0) { const uint32_t parentIndex = psContext->psShader->aIndexedInputParents[regSpace][psOperand->ui32RegisterNumber]; oss << "phase" << psContext->currentPhase << "_Input" << regSpace << "_" << parentIndex << "[" << (psOperand->ui32RegisterNumber - parentIndex) << "]"; } else { oss << psContext->GetDeclaredInputName(psOperand, piRebase, 0, pui32IgnoreSwizzle); } } break; } } break; } case OPERAND_TYPE_OUTPUT: case OPERAND_TYPE_OUTPUT_DEPTH: case OPERAND_TYPE_OUTPUT_DEPTH_GREATER_EQUAL: case OPERAND_TYPE_OUTPUT_DEPTH_LESS_EQUAL: { int stream = 0; oss << psContext->GetDeclaredOutputName(psOperand, &stream, pui32IgnoreSwizzle, piRebase, 0); if (psOperand->m_SubOperands[0].get()) { oss << "["; oss << TranslateOperand(psOperand->m_SubOperands[0].get(), TO_AUTO_BITCAST_TO_INT); oss << "]"; } break; } case OPERAND_TYPE_TEMP: { SHADER_VARIABLE_TYPE eTempType = psOperand->GetDataType(psContext); if (psOperand->eSpecialName == NAME_UNDEFINED && psOperand->specialName.length()) { oss << psOperand->specialName; break; } oss << HLSLCC_TEMP_PREFIX; ASSERT(psOperand->ui32RegisterNumber < 0x10000); // Sanity check after temp splitting. switch (eTempType) { case SVT_FLOAT: ASSERT(psContext->psShader->psFloatTempSizes[psOperand->ui32RegisterNumber] != 0); if (psContext->psShader->psFloatTempSizes[psOperand->ui32RegisterNumber] == 1) *pui32IgnoreSwizzle = 1; break; case SVT_FLOAT16: ASSERT(psContext->psShader->psFloat16TempSizes[psOperand->ui32RegisterNumber] != 0); oss << ("16_"); if (psContext->psShader->psFloat16TempSizes[psOperand->ui32RegisterNumber] == 1) *pui32IgnoreSwizzle = 1; break; case SVT_FLOAT10: ASSERT(psContext->psShader->psFloat10TempSizes[psOperand->ui32RegisterNumber] != 0); oss << ("10_"); if (psContext->psShader->psFloat10TempSizes[psOperand->ui32RegisterNumber] == 1) *pui32IgnoreSwizzle = 1; break; case SVT_INT: ASSERT(psContext->psShader->psIntTempSizes[psOperand->ui32RegisterNumber] != 0); oss << ("i"); if (psContext->psShader->psIntTempSizes[psOperand->ui32RegisterNumber] == 1) *pui32IgnoreSwizzle = 1; break; case SVT_INT16: ASSERT(psContext->psShader->psInt16TempSizes[psOperand->ui32RegisterNumber] != 0); oss << ("i16_"); if (psContext->psShader->psInt16TempSizes[psOperand->ui32RegisterNumber] == 1) *pui32IgnoreSwizzle = 1; break; case SVT_INT12: ASSERT(psContext->psShader->psInt12TempSizes[psOperand->ui32RegisterNumber] != 0); oss << ("i12_"); if (psContext->psShader->psInt12TempSizes[psOperand->ui32RegisterNumber] == 1) *pui32IgnoreSwizzle = 1; break; case SVT_UINT: ASSERT(psContext->psShader->psUIntTempSizes[psOperand->ui32RegisterNumber] != 0); oss << ("u"); if (psContext->psShader->psUIntTempSizes[psOperand->ui32RegisterNumber] == 1) *pui32IgnoreSwizzle = 1; break; case SVT_UINT16: ASSERT(psContext->psShader->psUInt16TempSizes[psOperand->ui32RegisterNumber] != 0); oss << ("u16_"); if (psContext->psShader->psUInt16TempSizes[psOperand->ui32RegisterNumber] == 1) *pui32IgnoreSwizzle = 1; break; case SVT_DOUBLE: ASSERT(psContext->psShader->psDoubleTempSizes[psOperand->ui32RegisterNumber] != 0); oss << ("d"); if (psContext->psShader->psDoubleTempSizes[psOperand->ui32RegisterNumber] == 1) *pui32IgnoreSwizzle = 1; break; case SVT_BOOL: ASSERT(psContext->psShader->psBoolTempSizes[psOperand->ui32RegisterNumber] != 0); oss << ("b"); if (psContext->psShader->psBoolTempSizes[psOperand->ui32RegisterNumber] == 1) *pui32IgnoreSwizzle = 1; break; default: ASSERT(0 && "Should never get here!"); } oss << psOperand->ui32RegisterNumber; break; } case OPERAND_TYPE_SPECIAL_IMMCONSTINT: case OPERAND_TYPE_SPECIAL_IMMCONST: case OPERAND_TYPE_SPECIAL_OUTBASECOLOUR: case OPERAND_TYPE_SPECIAL_OUTOFFSETCOLOUR: case OPERAND_TYPE_SPECIAL_FOG: case OPERAND_TYPE_SPECIAL_ADDRESS: case OPERAND_TYPE_SPECIAL_LOOPCOUNTER: case OPERAND_TYPE_SPECIAL_TEXCOORD: { ASSERT(0 && "DX9 shaders no longer supported!"); break; } case OPERAND_TYPE_SPECIAL_POSITION: { ASSERT(0 && "TODO normal shader support"); // bcatcstr(glsl, "gl_Position"); break; } case OPERAND_TYPE_SPECIAL_POINTSIZE: { ASSERT(0 && "TODO normal shader support"); // bcatcstr(glsl, "gl_PointSize"); break; } case OPERAND_TYPE_CONSTANT_BUFFER: { const ConstantBuffer* psCBuf = NULL; const ShaderVarType* psVarType = NULL; int32_t index = -1; std::vector arrayIndices; bool isArray = false; bool isFBInput = false; psContext->psShader->sInfo.GetConstantBufferFromBindingPoint(RGROUP_CBUFFER, psOperand->aui32ArraySizes[0], &psCBuf); ASSERT(psCBuf != NULL); if (ui32TOFlag & TO_FLAG_DECLARATION_NAME) { pui32IgnoreSwizzle[0] = 1; } std::string cbName = ""; if (psCBuf) { //$Globals. cbName = GetCBName(psCBuf->name); cbName += "."; // Drop the constant buffer name from subpass inputs if (cbName.substr(0, 19) == "hlslcc_SubpassInput") cbName = ""; } if ((ui32TOFlag & TO_FLAG_DECLARATION_NAME) != TO_FLAG_DECLARATION_NAME) { //Work out the variable name. Don't apply swizzle to that variable yet. int32_t rebase = 0; ASSERT(psCBuf != NULL); uint32_t componentsNeeded = 1; if (psOperand->eSelMode != OPERAND_4_COMPONENT_SELECT_1_MODE) { uint32_t minSwiz = 3; uint32_t maxSwiz = 0; int i; for (i = 0; i < 4; i++) { if ((ui32CompMask & (1 << i)) == 0) continue; minSwiz = std::min(minSwiz, psOperand->aui32Swizzle[i]); maxSwiz = std::max(maxSwiz, psOperand->aui32Swizzle[i]); } componentsNeeded = maxSwiz - minSwiz + 1; } // When we have a component mask that doesn't have .x set (this basically only happens when we manually open operands into components) // We have to pull down the swizzle array to match the first bit that's actually set uint32_t tmpSwizzle[4] = { 0 }; int firstBitSet = 0; if (ui32CompMask == 0) ui32CompMask = 0xf; while ((ui32CompMask & (1 << firstBitSet)) == 0) firstBitSet++; std::copy(&psOperand->aui32Swizzle[firstBitSet], &psOperand->aui32Swizzle[4], &tmpSwizzle[0]); ShaderInfo::GetShaderVarFromOffset(psOperand->aui32ArraySizes[1], tmpSwizzle, psCBuf, &psVarType, &isArray, &arrayIndices, &rebase, psContext->flags); // Get a possible dynamic array index std::string dynamicIndexStr; bool needsIndexCalcRevert = false; bool isAoS = ((!isArray && arrayIndices.size() > 0) || (isArray && arrayIndices.size() > 1)); bool isUnityInstancingBuffer = isAoS && IsUnityFlexibleInstancingBuffer(psCBuf); Operand *psDynIndexOp = psOperand->GetDynamicIndexOperand(psContext, psVarType, isAoS, &needsIndexCalcRevert); if (psDynIndexOp != NULL) { SHADER_VARIABLE_TYPE eType = psDynIndexOp->GetDataType(psContext); uint32_t opFlags = TO_FLAG_INTEGER; if (eType != SVT_INT && eType != SVT_UINT) opFlags = TO_AUTO_BITCAST_TO_INT; dynamicIndexStr = TranslateOperand(psDynIndexOp, opFlags, 0x1); // Just take the first component for the index } if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE || (componentsNeeded <= psVarType->Columns)) { // Simple case: just access one component std::string fullName = ShaderInfo::GetShaderVarIndexedFullName(psVarType, arrayIndices, dynamicIndexStr, needsIndexCalcRevert, psContext->flags & HLSLCC_FLAG_TRANSLATE_MATRICES); // Special hack for MSAA subpass inputs: in Metal we can only read the "current" sample, so ignore the index if (strncmp(fullName.c_str(), "hlslcc_fbinput", 14) == 0) isFBInput = true; if (((psContext->flags & HLSLCC_FLAG_TRANSLATE_MATRICES) != 0) && ((psVarType->Class == SVC_MATRIX_ROWS) || (psVarType->Class == SVC_MATRIX_COLUMNS))) { // We'll need to add the prefix only to the last section of the name size_t commaPos = fullName.find_last_of('.'); char prefix[256]; sprintf(prefix, HLSLCC_TRANSLATE_MATRIX_FORMAT_STRING, psVarType->Rows, psVarType->Columns); if (commaPos == std::string::npos) fullName.insert(0, prefix); else fullName.insert(commaPos + 1, prefix); } oss << MakeCBVarName(cbName, fullName, isUnityInstancingBuffer); } else { // Non-simple case: build vec4 and apply mask uint32_t i; int32_t tmpRebase; std::vector tmpArrayIndices; bool tmpIsArray; int firstItemAdded = 0; oss << GetConstructorForTypeMetal(psVarType->Type, GetNumberBitsSet(ui32CompMask)) << "("; for (i = 0; i < 4; i++) { const ShaderVarType *tmpVarType = NULL; if ((ui32CompMask & (1 << i)) == 0) continue; tmpRebase = 0; if (firstItemAdded != 0) oss << ", "; else firstItemAdded = 1; uint32_t tmpSwizzle[4] = { 0 }; std::copy(&psOperand->aui32Swizzle[i], &psOperand->aui32Swizzle[4], &tmpSwizzle[0]); ShaderInfo::GetShaderVarFromOffset(psOperand->aui32ArraySizes[1], tmpSwizzle, psCBuf, &tmpVarType, &tmpIsArray, &tmpArrayIndices, &tmpRebase, psContext->flags); std::string fullName = ShaderInfo::GetShaderVarIndexedFullName(tmpVarType, tmpArrayIndices, dynamicIndexStr, needsIndexCalcRevert, psContext->flags & HLSLCC_FLAG_TRANSLATE_MATRICES); oss << MakeCBVarName(cbName, fullName, isUnityInstancingBuffer); if (tmpVarType->Class != SVC_SCALAR) { uint32_t swizzle; tmpRebase /= 4; // 0 => 0, 4 => 1, 8 => 2, 12 /= 3 swizzle = psOperand->aui32Swizzle[i] - tmpRebase; oss << "." << ("xyzw"[swizzle]); } } oss << ")"; // Clear rebase, we've already done it. rebase = 0; // Also swizzle. *pui32IgnoreSwizzle = 1; } if (isArray) { index = arrayIndices.back(); // Dynamic index is atm supported only at the root array level. Add here only if there is no such parent. bool hasDynamicIndex = !dynamicIndexStr.empty() && (arrayIndices.size() <= 1); bool hasImmediateIndex = (index != -1) && !(hasDynamicIndex && index == 0); // Ignore index altogether on fb inputs if (isFBInput) { // Nothing to do here } else if (hasDynamicIndex || hasImmediateIndex) { std::ostringstream fullIndexOss; if (hasDynamicIndex && hasImmediateIndex) fullIndexOss << "(" << dynamicIndexStr << " + " << index << ")"; else if (hasDynamicIndex) fullIndexOss << dynamicIndexStr; else // hasImmediateStr fullIndexOss << index; if (((psVarType->Class == SVC_MATRIX_COLUMNS) || (psVarType->Class == SVC_MATRIX_ROWS)) && (psVarType->Elements > 1) && ((psContext->flags & HLSLCC_FLAG_TRANSLATE_MATRICES) == 0)) { // Special handling for old matrix arrays oss << "[" << fullIndexOss.str() << " / 4]"; oss << "[" << fullIndexOss.str() << " %% 4]"; } else // This path is atm the default { oss << "[" << fullIndexOss.str() << "]"; } } } if (psVarType->Class == SVC_VECTOR && !*pui32IgnoreSwizzle) { switch (rebase) { case 4: { if (psVarType->Columns == 2) { //.x(GLSL) is .y(HLSL). .y(GLSL) is .z(HLSL) oss << ".xxyx"; } else if (psVarType->Columns == 3) { //.x(GLSL) is .y(HLSL). .y(GLSL) is .z(HLSL) .z(GLSL) is .w(HLSL) oss << ".xxyz"; } break; } case 8: { if (psVarType->Columns == 2) { //.x(GLSL) is .z(HLSL). .y(GLSL) is .w(HLSL) oss << ".xxxy"; } break; } case 0: default: { //No rebase, but extend to vec4. if (psVarType->Columns == 2) { oss << ".xyxx"; } else if (psVarType->Columns == 3) { oss << ".xyzx"; } break; } } } if (psVarType->Class == SVC_SCALAR) { *pui32IgnoreSwizzle = 1; // CB arrays are all declared as 4-component vectors to match DX11 data layout. // Therefore add swizzle here to access the element corresponding to the scalar var. if ((psVarType->Elements > 0) && (psContext->psShader->eShaderType == COMPUTE_SHADER)) { oss << ".x"; } } } break; } case OPERAND_TYPE_RESOURCE: { oss << ResourceName(RGROUP_TEXTURE, psOperand->ui32RegisterNumber); *pui32IgnoreSwizzle = 1; break; } case OPERAND_TYPE_SAMPLER: { oss << ResourceName(RGROUP_SAMPLER, psOperand->ui32RegisterNumber); *pui32IgnoreSwizzle = 1; break; } case OPERAND_TYPE_FUNCTION_BODY: { ASSERT(0); break; } case OPERAND_TYPE_INPUT_FORK_INSTANCE_ID: case OPERAND_TYPE_INPUT_JOIN_INSTANCE_ID: { oss << "phaseInstanceID"; // Not a real builtin, but passed as a function parameter. *pui32IgnoreSwizzle = 1; break; } case OPERAND_TYPE_IMMEDIATE_CONSTANT_BUFFER: { oss << "ImmCB_" << psContext->currentPhase; oss << TranslateOperandIndex(psOperand, 0); break; } case OPERAND_TYPE_INPUT_DOMAIN_POINT: { oss << "mtl_TessCoord"; break; } case OPERAND_TYPE_INPUT_CONTROL_POINT: { int ignoreRedirect = 1; int regSpace = psOperand->GetRegisterSpace(psContext); if ((regSpace == 0 && psContext->psShader->asPhases[psContext->currentPhase].acInputNeedsRedirect[psOperand->ui32RegisterNumber] == 0xfe) || (regSpace == 1 && psContext->psShader->asPhases[psContext->currentPhase].acPatchConstantsNeedsRedirect[psOperand->ui32RegisterNumber] == 0xfe)) { ignoreRedirect = 0; } if (ignoreRedirect) { oss << "input.cp"; oss << TranslateOperandIndex(psOperand, 0);//Vertex index oss << "." << psContext->GetDeclaredInputName(psOperand, piRebase, ignoreRedirect, pui32IgnoreSwizzle); } else { oss << psContext->GetDeclaredInputName(psOperand, piRebase, ignoreRedirect, pui32IgnoreSwizzle); oss << TranslateOperandIndex(psOperand, 0);//Vertex index } // Check for scalar if ((psContext->psShader->abScalarInput[psOperand->GetRegisterSpace(psContext)][psOperand->ui32RegisterNumber] & psOperand->GetAccessMask()) != 0) *pui32IgnoreSwizzle = 1; break; } case OPERAND_TYPE_NULL: { // Null register, used to discard results of operations oss << "//null"; break; } case OPERAND_TYPE_OUTPUT_CONTROL_POINT_ID: { oss << "controlPointID"; *pui32IgnoreSwizzle = 1; break; } case OPERAND_TYPE_OUTPUT_COVERAGE_MASK: { oss << "mtl_CoverageMask"; *pui32IgnoreSwizzle = 1; break; } case OPERAND_TYPE_INPUT_COVERAGE_MASK: { oss << "mtl_CoverageMask"; //Skip swizzle on scalar types. *pui32IgnoreSwizzle = 1; break; } case OPERAND_TYPE_INPUT_THREAD_ID://SV_DispatchThreadID { oss << "mtl_ThreadID"; break; } case OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP://SV_GroupThreadID { oss << "mtl_ThreadIDInGroup"; break; } case OPERAND_TYPE_INPUT_THREAD_GROUP_ID://SV_GroupID { oss << "mtl_ThreadGroupID"; break; } case OPERAND_TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED://SV_GroupIndex { if (requestedComponents > 1 && !hasCtor) { oss << GetConstructorForType(psContext, eType, requestedComponents, false) << "("; numParenthesis++; hasCtor = 1; } for (uint32_t i = 0; i < requestedComponents; i++) { oss << "mtl_ThreadIndexInThreadGroup"; if (i < requestedComponents - 1) oss << ", "; } *pui32IgnoreSwizzle = 1; // No swizzle meaningful for scalar. break; } case OPERAND_TYPE_UNORDERED_ACCESS_VIEW: { oss << ResourceName(RGROUP_UAV, psOperand->ui32RegisterNumber); *pui32IgnoreSwizzle = 1; break; } case OPERAND_TYPE_THREAD_GROUP_SHARED_MEMORY: { oss << "TGSM" << psOperand->ui32RegisterNumber; *pui32IgnoreSwizzle = 1; break; } case OPERAND_TYPE_INPUT_PRIMITIVEID: { // Not supported on Metal ASSERT(0); break; } case OPERAND_TYPE_INDEXABLE_TEMP: { oss << "TempArray" << psOperand->aui32ArraySizes[0] << "["; if (psOperand->aui32ArraySizes[1] != 0 || !psOperand->m_SubOperands[1].get()) oss << psOperand->aui32ArraySizes[1]; if (psOperand->m_SubOperands[1].get()) { if (psOperand->aui32ArraySizes[1] != 0) oss << "+"; oss << TranslateOperand(psOperand->m_SubOperands[1].get(), TO_FLAG_INTEGER); } oss << "]"; break; } case OPERAND_TYPE_STREAM: { // Not supported on Metal ASSERT(0); break; } case OPERAND_TYPE_INPUT_GS_INSTANCE_ID: { // Not supported on Metal ASSERT(0); break; } case OPERAND_TYPE_THIS_POINTER: { ASSERT(0); // Nope. break; } case OPERAND_TYPE_INPUT_PATCH_CONSTANT: { const ShaderInfo::InOutSignature* psIn; psContext->psShader->sInfo.GetPatchConstantSignatureFromRegister(psOperand->ui32RegisterNumber, psOperand->GetAccessMask(), &psIn); *piRebase = psIn->iRebase; switch (psIn->eSystemValueType) { case NAME_POSITION: oss << "mtl_Position"; break; case NAME_RENDER_TARGET_ARRAY_INDEX: oss << "mtl_Layer"; *pui32IgnoreSwizzle = 1; break; case NAME_CLIP_DISTANCE: // this is temp variable, declaration and redirecting to actual output is handled in DeclareClipPlanes char tmpName[128]; sprintf(tmpName, "phase%d_ClipDistance%d", psContext->currentPhase, psIn->ui32SemanticIndex); oss << tmpName; *pui32IgnoreSwizzle = 1; break; case NAME_VIEWPORT_ARRAY_INDEX: oss << "mtl_ViewPortIndex"; *pui32IgnoreSwizzle = 1; break; case NAME_VERTEX_ID: oss << "mtl_VertexID"; *pui32IgnoreSwizzle = 1; break; case NAME_INSTANCE_ID: oss << "mtl_InstanceID"; *pui32IgnoreSwizzle = 1; break; case NAME_IS_FRONT_FACE: oss << "(mtl_FrontFace ? 0xffffffffu : uint(0))"; *pui32IgnoreSwizzle = 1; break; case NAME_PRIMITIVE_ID: // Not on Metal ASSERT(0); break; // as far as i understand tesselation factors are always coming from tessFactor variable (it is always declared in ToMetal::Translate) case NAME_FINAL_QUAD_U_EQ_0_EDGE_TESSFACTOR: case NAME_FINAL_TRI_U_EQ_0_EDGE_TESSFACTOR: case NAME_FINAL_LINE_DENSITY_TESSFACTOR: if (psContext->psShader->aIndexedOutput[1][psOperand->ui32RegisterNumber]) oss << "tessFactor.edgeTessellationFactor"; else oss << "tessFactor.edgeTessellationFactor[0]"; *pui32IgnoreSwizzle = 1; break; case NAME_FINAL_QUAD_V_EQ_0_EDGE_TESSFACTOR: case NAME_FINAL_TRI_V_EQ_0_EDGE_TESSFACTOR: case NAME_FINAL_LINE_DETAIL_TESSFACTOR: oss << "tessFactor.edgeTessellationFactor[1]"; *pui32IgnoreSwizzle = 1; break; case NAME_FINAL_QUAD_U_EQ_1_EDGE_TESSFACTOR: case NAME_FINAL_TRI_W_EQ_0_EDGE_TESSFACTOR: oss << "tessFactor.edgeTessellationFactor[2]"; *pui32IgnoreSwizzle = 1; break; case NAME_FINAL_QUAD_V_EQ_1_EDGE_TESSFACTOR: oss << "tessFactor.edgeTessellationFactor[3]"; *pui32IgnoreSwizzle = 1; break; case NAME_FINAL_TRI_INSIDE_TESSFACTOR: case NAME_FINAL_QUAD_U_INSIDE_TESSFACTOR: if (psContext->psShader->aIndexedOutput[1][psOperand->ui32RegisterNumber]) oss << "tessFactor.insideTessellationFactor"; else oss << "tessFactor.insideTessellationFactor[0]"; *pui32IgnoreSwizzle = 1; break; case NAME_FINAL_QUAD_V_INSIDE_TESSFACTOR: oss << "tessFactor.insideTessellationFactor[1]"; *pui32IgnoreSwizzle = 1; break; default: const std::string patchPrefix = "patch."; if (psContext->psShader->eShaderType == DOMAIN_SHADER) oss << psContext->inputPrefix << patchPrefix << psIn->semanticName << psIn->ui32SemanticIndex; else oss << patchPrefix << psIn->semanticName << psIn->ui32SemanticIndex; // Disable swizzles if this is a scalar if (psContext->psShader->eShaderType == HULL_SHADER) { if ((psContext->psShader->abScalarOutput[1][psOperand->ui32RegisterNumber] & psOperand->GetAccessMask()) != 0) *pui32IgnoreSwizzle = 1; } else { if ((psContext->psShader->abScalarInput[1][psOperand->ui32RegisterNumber] & psOperand->GetAccessMask()) != 0) *pui32IgnoreSwizzle = 1; } break; } break; } default: { ASSERT(0); break; } } if (hasCtor && (*pui32IgnoreSwizzle == 0)) { oss << TranslateOperandSwizzle(psOperand, ui32CompMask, piRebase ? *piRebase : 0); *pui32IgnoreSwizzle = 1; } if (needsBoolUpscale) { if (requestedType == SVT_UINT || requestedType == SVT_UINT16 || requestedType == SVT_UINT8) oss << ") * 0xffffffffu"; else oss << ") * int(0xffffffffu)"; numParenthesis--; oss << ")"; numParenthesis--; } while (numParenthesis != 0) { oss << ")"; numParenthesis--; } return oss.str(); } std::string ToMetal::TranslateOperand(const Operand* psOperand, uint32_t ui32TOFlag, uint32_t ui32ComponentMask) { std::ostringstream oss; uint32_t ui32IgnoreSwizzle = 0; int iRebase = 0; // in single-component mode there is no need to use mask if (psOperand->eSelMode == OPERAND_4_COMPONENT_SELECT_1_MODE) ui32ComponentMask = OPERAND_4_COMPONENT_MASK_ALL; if (ui32TOFlag & TO_FLAG_NAME_ONLY) { return TranslateVariableName(psOperand, ui32TOFlag, &ui32IgnoreSwizzle, OPERAND_4_COMPONENT_MASK_ALL, &iRebase); } switch (psOperand->eModifier) { case OPERAND_MODIFIER_NONE: { break; } case OPERAND_MODIFIER_NEG: { oss << ("(-"); break; } case OPERAND_MODIFIER_ABS: { oss << ("abs("); break; } case OPERAND_MODIFIER_ABSNEG: { oss << ("-abs("); break; } } oss << TranslateVariableName(psOperand, ui32TOFlag, &ui32IgnoreSwizzle, ui32ComponentMask, &iRebase); if (!ui32IgnoreSwizzle) { oss << TranslateOperandSwizzle(psOperand, ui32ComponentMask, iRebase); } switch (psOperand->eModifier) { case OPERAND_MODIFIER_NONE: { break; } case OPERAND_MODIFIER_NEG: { oss << (")"); break; } case OPERAND_MODIFIER_ABS: { oss << (")"); break; } case OPERAND_MODIFIER_ABSNEG: { oss << (")"); break; } } return oss.str(); }