// // Copyright 2020 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // DriverUniform.cpp: Add code to support driver uniforms // #include "compiler/translator/tree_util/DriverUniform.h" #include "compiler/translator/Compiler.h" #include "compiler/translator/IntermNode.h" #include "compiler/translator/StaticType.h" #include "compiler/translator/SymbolTable.h" #include "compiler/translator/tree_util/FindMain.h" #include "compiler/translator/tree_util/IntermNode_util.h" #include "compiler/translator/tree_util/IntermTraverse.h" #include "compiler/translator/util.h" namespace sh { namespace { constexpr ImmutableString kEmulatedDepthRangeParams = ImmutableString("ANGLEDepthRangeParams"); constexpr const char kAcbBufferOffsets[] = "acbBufferOffsets"; constexpr const char kDepthRange[] = "depthRange"; constexpr const char kRenderArea[] = "renderArea"; constexpr const char kFlipXY[] = "flipXY"; constexpr const char kDither[] = "dither"; constexpr const char kMisc[] = "misc"; // Extended uniforms constexpr const char kXfbBufferOffsets[] = "xfbBufferOffsets"; constexpr const char kXfbVerticesPerInstance[] = "xfbVerticesPerInstance"; constexpr const char kUnused[] = "unused"; constexpr const char kUnused2[] = "unused2"; } // anonymous namespace // Class DriverUniform bool DriverUniform::addComputeDriverUniformsToShader(TIntermBlock *root, TSymbolTable *symbolTable) { constexpr size_t kNumComputeDriverUniforms = 1; constexpr std::array kComputeDriverUniformNames = { {kAcbBufferOffsets}}; ASSERT(!mDriverUniforms); // This field list mirrors the structure of ComputeDriverUniforms in ContextVk.cpp. TFieldList *driverFieldList = new TFieldList; const std::array kDriverUniformTypes = {{ new TType(EbtUInt, EbpHigh, EvqGlobal, 4), }}; for (size_t uniformIndex = 0; uniformIndex < kNumComputeDriverUniforms; ++uniformIndex) { TField *driverUniformField = new TField(kDriverUniformTypes[uniformIndex], ImmutableString(kComputeDriverUniformNames[uniformIndex]), TSourceLoc(), SymbolType::AngleInternal); driverFieldList->push_back(driverUniformField); } // Define a driver uniform block "ANGLEUniformBlock" with instance name "ANGLEUniforms". TLayoutQualifier layoutQualifier = TLayoutQualifier::Create(); layoutQualifier.blockStorage = EbsStd140; layoutQualifier.pushConstant = true; mDriverUniforms = DeclareInterfaceBlock(root, symbolTable, driverFieldList, EvqUniform, layoutQualifier, TMemoryQualifier::Create(), 0, ImmutableString(vk::kDriverUniformsBlockName), ImmutableString(vk::kDriverUniformsVarName)); return mDriverUniforms != nullptr; } TFieldList *DriverUniform::createUniformFields(TSymbolTable *symbolTable) { constexpr size_t kNumGraphicsDriverUniforms = 6; constexpr std::array kGraphicsDriverUniformNames = {{ kAcbBufferOffsets, kDepthRange, kRenderArea, kFlipXY, kDither, kMisc, }}; // This field list mirrors the structure of GraphicsDriverUniforms in ContextVk.cpp. TFieldList *driverFieldList = new TFieldList; const std::array kDriverUniformTypes = {{ // acbBufferOffsets: Packed ubyte8 new TType(EbtUInt, EbpHigh, EvqGlobal, 2), // depthRange: Near and far depth new TType(EbtFloat, EbpHigh, EvqGlobal, 2), // renderArea: Packed ushort2 new TType(EbtUInt, EbpHigh, EvqGlobal), // flipXY: Packed snorm4 new TType(EbtUInt, EbpHigh, EvqGlobal), // dither: ushort new TType(EbtUInt, EbpHigh, EvqGlobal), // misc: Various bits of state new TType(EbtUInt, EbpHigh, EvqGlobal), }}; for (size_t uniformIndex = 0; uniformIndex < kNumGraphicsDriverUniforms; ++uniformIndex) { TField *driverUniformField = new TField(kDriverUniformTypes[uniformIndex], ImmutableString(kGraphicsDriverUniformNames[uniformIndex]), TSourceLoc(), SymbolType::AngleInternal); driverFieldList->push_back(driverUniformField); } return driverFieldList; } const TType *DriverUniform::createEmulatedDepthRangeType(TSymbolTable *symbolTable) { // If already defined, return it immediately. if (mEmulatedDepthRangeType != nullptr) { return mEmulatedDepthRangeType; } // Create the depth range type. TFieldList *depthRangeParamsFields = new TFieldList(); TType *floatType = new TType(EbtFloat, EbpHigh, EvqGlobal, 1, 1); depthRangeParamsFields->push_back( new TField(floatType, ImmutableString("near"), TSourceLoc(), SymbolType::AngleInternal)); depthRangeParamsFields->push_back( new TField(floatType, ImmutableString("far"), TSourceLoc(), SymbolType::AngleInternal)); depthRangeParamsFields->push_back( new TField(floatType, ImmutableString("diff"), TSourceLoc(), SymbolType::AngleInternal)); TStructure *emulatedDepthRangeParams = new TStructure( symbolTable, kEmulatedDepthRangeParams, depthRangeParamsFields, SymbolType::AngleInternal); mEmulatedDepthRangeType = new TType(emulatedDepthRangeParams, false); return mEmulatedDepthRangeType; } // The Add*DriverUniformsToShader operation adds an internal uniform block to a shader. The driver // block is used to implement Vulkan-specific features and workarounds. Returns the driver uniforms // variable. // // There are Graphics and Compute variations as they require different uniforms. bool DriverUniform::addGraphicsDriverUniformsToShader(TIntermBlock *root, TSymbolTable *symbolTable) { ASSERT(!mDriverUniforms); // Declare the depth range struct type. const TType *emulatedDepthRangeType = createEmulatedDepthRangeType(symbolTable); const TType *emulatedDepthRangeDeclType = new TType(emulatedDepthRangeType->getStruct(), true); const TVariable *depthRangeVar = new TVariable(symbolTable->nextUniqueId(), kEmptyImmutableString, SymbolType::Empty, TExtension::UNDEFINED, emulatedDepthRangeDeclType); DeclareGlobalVariable(root, depthRangeVar); TFieldList *driverFieldList = createUniformFields(symbolTable); if (mMode == DriverUniformMode::InterfaceBlock) { // Define a driver uniform block "ANGLEUniformBlock" with instance name "ANGLEUniforms". TLayoutQualifier layoutQualifier = TLayoutQualifier::Create(); layoutQualifier.blockStorage = EbsStd140; layoutQualifier.pushConstant = true; mDriverUniforms = DeclareInterfaceBlock(root, symbolTable, driverFieldList, EvqUniform, layoutQualifier, TMemoryQualifier::Create(), 0, ImmutableString(vk::kDriverUniformsBlockName), ImmutableString(vk::kDriverUniformsVarName)); } else { // Declare a structure "ANGLEUniformBlock" with instance name "ANGLE_angleUniforms". // This code path is taken only by the direct-to-Metal backend, and the assumptions // about the naming conventions of ANGLE-internal variables run too deeply to rename // this one. auto varName = ImmutableString("ANGLE_angleUniforms"); auto result = DeclareStructure(root, symbolTable, driverFieldList, EvqUniform, TMemoryQualifier::Create(), 0, ImmutableString(vk::kDriverUniformsBlockName), &varName); mDriverUniforms = result.second; } return mDriverUniforms != nullptr; } TIntermTyped *DriverUniform::createDriverUniformRef(const char *fieldName) const { size_t fieldIndex = 0; if (mMode == DriverUniformMode::InterfaceBlock) { fieldIndex = FindFieldIndex(mDriverUniforms->getType().getInterfaceBlock()->fields(), fieldName); } else { fieldIndex = FindFieldIndex(mDriverUniforms->getType().getStruct()->fields(), fieldName); } TIntermSymbol *angleUniformsRef = new TIntermSymbol(mDriverUniforms); TConstantUnion *uniformIndex = new TConstantUnion; uniformIndex->setIConst(static_cast(fieldIndex)); TIntermConstantUnion *indexRef = new TIntermConstantUnion(uniformIndex, *StaticType::GetBasic()); if (mMode == DriverUniformMode::InterfaceBlock) { return new TIntermBinary(EOpIndexDirectInterfaceBlock, angleUniformsRef, indexRef); } return new TIntermBinary(EOpIndexDirectStruct, angleUniformsRef, indexRef); } TIntermTyped *DriverUniform::getAcbBufferOffsets() const { return createDriverUniformRef(kAcbBufferOffsets); } TIntermTyped *DriverUniform::getDepthRange() const { ASSERT(mEmulatedDepthRangeType != nullptr); TIntermTyped *depthRangeRef = createDriverUniformRef(kDepthRange); TIntermTyped *nearRef = new TIntermSwizzle(depthRangeRef, {0}); TIntermTyped *farRef = new TIntermSwizzle(depthRangeRef->deepCopy(), {1}); TIntermTyped *diff = new TIntermBinary(EOpSub, farRef, nearRef); TIntermSequence args = { nearRef->deepCopy(), farRef->deepCopy(), diff, }; return TIntermAggregate::CreateConstructor(*mEmulatedDepthRangeType, &args); } TIntermTyped *DriverUniform::getViewportZScale() const { ASSERT(mEmulatedDepthRangeType != nullptr); TIntermTyped *depthRangeRef = createDriverUniformRef(kDepthRange); TIntermTyped *nearRef = new TIntermSwizzle(depthRangeRef, {0}); TIntermTyped *farRef = new TIntermSwizzle(depthRangeRef->deepCopy(), {1}); TIntermTyped *isNegative = new TIntermBinary(EOpLessThan, farRef, nearRef); return new TIntermTernary(isNegative, CreateFloatNode(-1, EbpMedium), CreateFloatNode(1, EbpMedium)); } TIntermTyped *DriverUniform::getHalfRenderArea() const { TIntermTyped *renderAreaRef = createDriverUniformRef(kRenderArea); TIntermTyped *width = new TIntermBinary(EOpBitwiseAnd, renderAreaRef, CreateUIntNode(0xFFFF)); TIntermTyped *height = new TIntermBinary(EOpBitShiftRight, renderAreaRef->deepCopy(), CreateUIntNode(16)); TIntermSequence widthArgs = { width, }; TIntermTyped *widthAsFloat = TIntermAggregate::CreateConstructor(*StaticType::GetBasic(), &widthArgs); TIntermSequence heightArgs = { height, }; TIntermTyped *heightAsFloat = TIntermAggregate::CreateConstructor( *StaticType::GetBasic(), &heightArgs); TIntermSequence args = { widthAsFloat, heightAsFloat, }; TIntermTyped *renderArea = TIntermAggregate::CreateConstructor(*StaticType::GetBasic(), &args); return new TIntermBinary(EOpVectorTimesScalar, renderArea, CreateFloatNode(0.5, EbpMedium)); } TIntermTyped *DriverUniform::getFlipXY(TSymbolTable *symbolTable, DriverUniformFlip stage) const { TIntermTyped *flipXY = createDriverUniformRef(kFlipXY); TIntermTyped *values = CreateBuiltInUnaryFunctionCallNode( "unpackSnorm4x8", flipXY, *symbolTable, GetESSLOrGLSLVersion(symbolTable->getShaderSpec(), 310, 400)); if (stage == DriverUniformFlip::Fragment) { return new TIntermSwizzle(values, {0, 1}); } return new TIntermSwizzle(values, {2, 3}); } TIntermTyped *DriverUniform::getNegFlipXY(TSymbolTable *symbolTable, DriverUniformFlip stage) const { TIntermTyped *flipXY = getFlipXY(symbolTable, stage); constexpr std::array kMultiplier = {1, -1}; return new TIntermBinary(EOpMul, flipXY, CreateVecNode(kMultiplier.data(), 2, EbpLow)); } TIntermTyped *DriverUniform::getDither() const { return createDriverUniformRef(kDither); } TIntermTyped *DriverUniform::getSwapXY() const { TIntermTyped *miscRef = createDriverUniformRef(kMisc); TIntermTyped *swapXY = new TIntermBinary(EOpBitwiseAnd, miscRef, CreateUIntNode(vk::kDriverUniformsMiscSwapXYMask)); TIntermSequence args = { swapXY, }; return TIntermAggregate::CreateConstructor(*StaticType::GetBasic(), &args); } TIntermTyped *DriverUniform::getAdvancedBlendEquation() const { TIntermTyped *miscRef = createDriverUniformRef(kMisc); TIntermTyped *equation = new TIntermBinary(EOpBitShiftRight, miscRef, CreateUIntNode(vk::kDriverUniformsMiscAdvancedBlendEquationOffset)); equation = new TIntermBinary(EOpBitwiseAnd, equation, CreateUIntNode(vk::kDriverUniformsMiscAdvancedBlendEquationMask)); return equation; } TIntermTyped *DriverUniform::getNumSamples() const { TIntermTyped *miscRef = createDriverUniformRef(kMisc); TIntermTyped *sampleCount = new TIntermBinary( EOpBitShiftRight, miscRef, CreateUIntNode(vk::kDriverUniformsMiscSampleCountOffset)); sampleCount = new TIntermBinary(EOpBitwiseAnd, sampleCount, CreateUIntNode(vk::kDriverUniformsMiscSampleCountMask)); return sampleCount; } TIntermTyped *DriverUniform::getClipDistancesEnabled() const { TIntermTyped *miscRef = createDriverUniformRef(kMisc); TIntermTyped *enabledMask = new TIntermBinary( EOpBitShiftRight, miscRef, CreateUIntNode(vk::kDriverUniformsMiscEnabledClipPlanesOffset)); enabledMask = new TIntermBinary(EOpBitwiseAnd, enabledMask, CreateUIntNode(vk::kDriverUniformsMiscEnabledClipPlanesMask)); return enabledMask; } TIntermTyped *DriverUniform::getTransformDepth() const { TIntermTyped *miscRef = createDriverUniformRef(kMisc); TIntermTyped *transformDepth = new TIntermBinary( EOpBitShiftRight, miscRef, CreateUIntNode(vk::kDriverUniformsMiscTransformDepthOffset)); transformDepth = new TIntermBinary(EOpBitwiseAnd, transformDepth, CreateUIntNode(vk::kDriverUniformsMiscTransformDepthMask)); TIntermSequence args = { transformDepth, }; return TIntermAggregate::CreateConstructor(*StaticType::GetBasic(), &args); } // // Class DriverUniformExtended // TFieldList *DriverUniformExtended::createUniformFields(TSymbolTable *symbolTable) { TFieldList *driverFieldList = DriverUniform::createUniformFields(symbolTable); constexpr size_t kNumGraphicsDriverUniformsExt = 4; constexpr std::array kGraphicsDriverUniformNamesExt = { {kXfbBufferOffsets, kXfbVerticesPerInstance, kUnused, kUnused2}}; const std::array kDriverUniformTypesExt = {{ // xfbBufferOffsets: uvec4 new TType(EbtInt, EbpHigh, EvqGlobal, 4), // xfbVerticesPerInstance: uint new TType(EbtInt, EbpHigh, EvqGlobal), // unused: uvec3 new TType(EbtUInt, EbpHigh, EvqGlobal), new TType(EbtUInt, EbpHigh, EvqGlobal, 2), }}; for (size_t uniformIndex = 0; uniformIndex < kNumGraphicsDriverUniformsExt; ++uniformIndex) { TField *driverUniformField = new TField(kDriverUniformTypesExt[uniformIndex], ImmutableString(kGraphicsDriverUniformNamesExt[uniformIndex]), TSourceLoc(), SymbolType::AngleInternal); driverFieldList->push_back(driverUniformField); } return driverFieldList; } TIntermTyped *DriverUniformExtended::getXfbBufferOffsets() const { return createDriverUniformRef(kXfbBufferOffsets); } TIntermTyped *DriverUniformExtended::getXfbVerticesPerInstance() const { return createDriverUniformRef(kXfbVerticesPerInstance); } TIntermTyped *MakeSwapXMultiplier(TIntermTyped *swapped) { // float(!swapped) TIntermSequence args = { new TIntermUnary(EOpLogicalNot, swapped, nullptr), }; return TIntermAggregate::CreateConstructor(*StaticType::GetBasic(), &args); } TIntermTyped *MakeSwapYMultiplier(TIntermTyped *swapped) { // float(swapped) TIntermSequence args = { swapped, }; return TIntermAggregate::CreateConstructor(*StaticType::GetBasic(), &args); } } // namespace sh