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