michael@0: // michael@0: // Copyright (c) 2012 The ANGLE Project Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: // michael@0: michael@0: #include "compiler/InfoSink.h" michael@0: #include "compiler/ParseHelper.h" michael@0: #include "compiler/depgraph/DependencyGraphOutput.h" michael@0: #include "compiler/timing/RestrictFragmentShaderTiming.h" michael@0: michael@0: RestrictFragmentShaderTiming::RestrictFragmentShaderTiming(TInfoSinkBase& sink) michael@0: : mSink(sink) michael@0: , mNumErrors(0) michael@0: { michael@0: // Sampling ops found only in fragment shaders. michael@0: mSamplingOps.insert("texture2D(s21;vf2;f1;"); michael@0: mSamplingOps.insert("texture2DProj(s21;vf3;f1;"); michael@0: mSamplingOps.insert("texture2DProj(s21;vf4;f1;"); michael@0: mSamplingOps.insert("textureCube(sC1;vf3;f1;"); michael@0: // Sampling ops found in both vertex and fragment shaders. michael@0: mSamplingOps.insert("texture2D(s21;vf2;"); michael@0: mSamplingOps.insert("texture2DProj(s21;vf3;"); michael@0: mSamplingOps.insert("texture2DProj(s21;vf4;"); michael@0: mSamplingOps.insert("textureCube(sC1;vf3;"); michael@0: // Sampling ops provided by OES_EGL_image_external. michael@0: mSamplingOps.insert("texture2D(1;vf2;"); michael@0: mSamplingOps.insert("texture2DProj(1;vf3;"); michael@0: mSamplingOps.insert("texture2DProj(1;vf4;"); michael@0: // Sampling ops provided by ARB_texture_rectangle. michael@0: mSamplingOps.insert("texture2DRect(1;vf2;"); michael@0: mSamplingOps.insert("texture2DRectProj(1;vf3;"); michael@0: mSamplingOps.insert("texture2DRectProj(1;vf4;"); michael@0: } michael@0: michael@0: // FIXME(mvujovic): We do not know if the execution time of built-in operations like sin, pow, etc. michael@0: // can vary based on the value of the input arguments. If so, we should restrict those as well. michael@0: void RestrictFragmentShaderTiming::enforceRestrictions(const TDependencyGraph& graph) michael@0: { michael@0: mNumErrors = 0; michael@0: michael@0: // FIXME(mvujovic): The dependency graph does not support user defined function calls right now, michael@0: // so we generate errors for them. michael@0: validateUserDefinedFunctionCallUsage(graph); michael@0: michael@0: // Starting from each sampler, traverse the dependency graph and generate an error each time we michael@0: // hit a node where sampler dependent values are not allowed. michael@0: for (TGraphSymbolVector::const_iterator iter = graph.beginSamplerSymbols(); michael@0: iter != graph.endSamplerSymbols(); michael@0: ++iter) michael@0: { michael@0: TGraphSymbol* samplerSymbol = *iter; michael@0: clearVisited(); michael@0: samplerSymbol->traverse(this); michael@0: } michael@0: } michael@0: michael@0: void RestrictFragmentShaderTiming::validateUserDefinedFunctionCallUsage(const TDependencyGraph& graph) michael@0: { michael@0: for (TFunctionCallVector::const_iterator iter = graph.beginUserDefinedFunctionCalls(); michael@0: iter != graph.endUserDefinedFunctionCalls(); michael@0: ++iter) michael@0: { michael@0: TGraphFunctionCall* functionCall = *iter; michael@0: beginError(functionCall->getIntermFunctionCall()); michael@0: mSink << "A call to a user defined function is not permitted.\n"; michael@0: } michael@0: } michael@0: michael@0: void RestrictFragmentShaderTiming::beginError(const TIntermNode* node) michael@0: { michael@0: ++mNumErrors; michael@0: mSink.prefix(EPrefixError); michael@0: mSink.location(node->getLine()); michael@0: } michael@0: michael@0: bool RestrictFragmentShaderTiming::isSamplingOp(const TIntermAggregate* intermFunctionCall) const michael@0: { michael@0: return !intermFunctionCall->isUserDefined() && michael@0: mSamplingOps.find(intermFunctionCall->getName()) != mSamplingOps.end(); michael@0: } michael@0: michael@0: void RestrictFragmentShaderTiming::visitArgument(TGraphArgument* parameter) michael@0: { michael@0: // Texture cache access time might leak sensitive information. michael@0: // Thus, we restrict sampler dependent values from affecting the coordinate or LOD bias of a michael@0: // sampling operation. michael@0: if (isSamplingOp(parameter->getIntermFunctionCall())) { michael@0: switch (parameter->getArgumentNumber()) { michael@0: case 1: michael@0: // Second argument (coord) michael@0: beginError(parameter->getIntermFunctionCall()); michael@0: mSink << "An expression dependent on a sampler is not permitted to be the" michael@0: << " coordinate argument of a sampling operation.\n"; michael@0: break; michael@0: case 2: michael@0: // Third argument (bias) michael@0: beginError(parameter->getIntermFunctionCall()); michael@0: mSink << "An expression dependent on a sampler is not permitted to be the" michael@0: << " bias argument of a sampling operation.\n"; michael@0: break; michael@0: default: michael@0: // First argument (sampler) michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void RestrictFragmentShaderTiming::visitSelection(TGraphSelection* selection) michael@0: { michael@0: beginError(selection->getIntermSelection()); michael@0: mSink << "An expression dependent on a sampler is not permitted in a conditional statement.\n"; michael@0: } michael@0: michael@0: void RestrictFragmentShaderTiming::visitLoop(TGraphLoop* loop) michael@0: { michael@0: beginError(loop->getIntermLoop()); michael@0: mSink << "An expression dependent on a sampler is not permitted in a loop condition.\n"; michael@0: } michael@0: michael@0: void RestrictFragmentShaderTiming::visitLogicalOp(TGraphLogicalOp* logicalOp) michael@0: { michael@0: beginError(logicalOp->getIntermLogicalOp()); michael@0: mSink << "An expression dependent on a sampler is not permitted on the left hand side of a logical " michael@0: << logicalOp->getOpString() michael@0: << " operator.\n"; michael@0: }