blob: 9ac9c68d59e8f3bfe60517165c72f317bb93998b [file] [log] [blame]
Jamie Madill05a80ce2013-06-20 11:55:49 -04001//
2// Copyright (c) 2013 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
Olli Etuaho12b0b392017-05-30 13:22:31 +03006// ValidateOutputs validates fragment shader outputs. It checks for conflicting locations,
7// out-of-range locations, that locations are specified when using multiple outputs, and YUV output
8// validity.
Jamie Madill05a80ce2013-06-20 11:55:49 -04009
Geoff Lang17732822013-08-29 13:46:49 -040010#include "compiler/translator/ValidateOutputs.h"
Olli Etuaho12b0b392017-05-30 13:22:31 +030011
12#include <set>
13
Geoff Lang17732822013-08-29 13:46:49 -040014#include "compiler/translator/InfoSink.h"
Olli Etuahocccf2b02017-07-05 14:50:54 +030015#include "compiler/translator/IntermTraverse.h"
Jamie Madill6b9cb252013-10-17 10:45:47 -040016#include "compiler/translator/ParseContext.h"
Jamie Madill05a80ce2013-06-20 11:55:49 -040017
Jamie Madill45bcc782016-11-07 13:58:48 -050018namespace sh
19{
20
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +030021namespace
22{
Olli Etuaho77ba4082016-12-16 12:01:18 +000023void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagnostics)
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +030024{
Olli Etuaho77ba4082016-12-16 12:01:18 +000025 diagnostics->error(symbol.getLine(), reason, symbol.getSymbol().c_str());
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +030026}
27
Olli Etuaho12b0b392017-05-30 13:22:31 +030028class ValidateOutputsTraverser : public TIntermTraverser
29{
30 public:
31 ValidateOutputsTraverser(const TExtensionBehavior &extBehavior, int maxDrawBuffers);
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +030032
Olli Etuaho12b0b392017-05-30 13:22:31 +030033 void validate(TDiagnostics *diagnostics) const;
34
35 void visitSymbol(TIntermSymbol *) override;
36
37 private:
38 int mMaxDrawBuffers;
39 bool mAllowUnspecifiedOutputLocationResolution;
40 bool mUsesFragDepth;
41
42 typedef std::vector<TIntermSymbol *> OutputVector;
43 OutputVector mOutputs;
44 OutputVector mUnspecifiedLocationOutputs;
45 OutputVector mYuvOutputs;
46 std::set<std::string> mVisitedSymbols;
47};
48
49ValidateOutputsTraverser::ValidateOutputsTraverser(const TExtensionBehavior &extBehavior,
50 int maxDrawBuffers)
Olli Etuaho3d0d9a42015-06-01 12:16:36 +030051 : TIntermTraverser(true, false, false),
Jamie Madill05a80ce2013-06-20 11:55:49 -040052 mMaxDrawBuffers(maxDrawBuffers),
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +030053 mAllowUnspecifiedOutputLocationResolution(
Andrei Volykhina5527072017-03-22 16:46:30 +030054 IsExtensionEnabled(extBehavior, "GL_EXT_blend_func_extended")),
55 mUsesFragDepth(false)
Jamie Madill05a80ce2013-06-20 11:55:49 -040056{
57}
58
Olli Etuaho12b0b392017-05-30 13:22:31 +030059void ValidateOutputsTraverser::visitSymbol(TIntermSymbol *symbol)
Jamie Madill05a80ce2013-06-20 11:55:49 -040060{
Jamie Madilld7b1ab52016-12-12 14:42:19 -050061 TString name = symbol->getSymbol();
Jamie Madill05a80ce2013-06-20 11:55:49 -040062 TQualifier qualifier = symbol->getQualifier();
63
Jamie Madill5ed23982016-04-22 15:08:57 -040064 if (mVisitedSymbols.count(name.c_str()) == 1)
Jamie Madill05a80ce2013-06-20 11:55:49 -040065 return;
66
Jamie Madill5ed23982016-04-22 15:08:57 -040067 mVisitedSymbols.insert(name.c_str());
Jamie Madill05a80ce2013-06-20 11:55:49 -040068
Jamie Madill19571812013-08-12 15:26:34 -070069 if (qualifier == EvqFragmentOut)
Jamie Madill05a80ce2013-06-20 11:55:49 -040070 {
Andrei Volykhina5527072017-03-22 16:46:30 +030071 if (symbol->getType().getLayoutQualifier().location != -1)
Jamie Madill05a80ce2013-06-20 11:55:49 -040072 {
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +030073 mOutputs.push_back(symbol);
Jamie Madill05a80ce2013-06-20 11:55:49 -040074 }
Andrei Volykhina5527072017-03-22 16:46:30 +030075 else if (symbol->getType().getLayoutQualifier().yuv == true)
76 {
77 mYuvOutputs.push_back(symbol);
78 }
79 else
80 {
81 mUnspecifiedLocationOutputs.push_back(symbol);
82 }
83 }
84 else if (qualifier == EvqFragDepth || qualifier == EvqFragDepthEXT)
85 {
86 mUsesFragDepth = true;
Jamie Madill05a80ce2013-06-20 11:55:49 -040087 }
88}
89
Olli Etuaho12b0b392017-05-30 13:22:31 +030090void ValidateOutputsTraverser::validate(TDiagnostics *diagnostics) const
Jamie Madill05a80ce2013-06-20 11:55:49 -040091{
Olli Etuaho77ba4082016-12-16 12:01:18 +000092 ASSERT(diagnostics);
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +030093 OutputVector validOutputs(mMaxDrawBuffers);
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +030094
95 for (const auto &symbol : mOutputs)
96 {
97 const TType &type = symbol->getType();
Olli Etuaho856c4972016-08-08 11:38:39 +030098 const size_t elementCount = static_cast<size_t>(type.isArray() ? type.getArraySize() : 1u);
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +030099 const size_t location = static_cast<size_t>(type.getLayoutQualifier().location);
100
101 ASSERT(type.getLayoutQualifier().location != -1);
102
103 if (location + elementCount <= validOutputs.size())
104 {
105 for (size_t elementIndex = 0; elementIndex < elementCount; elementIndex++)
106 {
107 const size_t offsetLocation = location + elementIndex;
108 if (validOutputs[offsetLocation])
109 {
110 std::stringstream strstr;
111 strstr << "conflicting output locations with previously defined output '"
112 << validOutputs[offsetLocation]->getSymbol() << "'";
Olli Etuaho77ba4082016-12-16 12:01:18 +0000113 error(*symbol, strstr.str().c_str(), diagnostics);
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +0300114 }
115 else
116 {
117 validOutputs[offsetLocation] = symbol;
118 }
119 }
120 }
121 else
122 {
123 if (elementCount > 0)
124 {
Olli Etuaho77ba4082016-12-16 12:01:18 +0000125 error(*symbol,
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +0300126 elementCount > 1 ? "output array locations would exceed MAX_DRAW_BUFFERS"
Olli Etuaho77ba4082016-12-16 12:01:18 +0000127 : "output location must be < MAX_DRAW_BUFFERS",
128 diagnostics);
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +0300129 }
130 }
131 }
132
133 if (!mAllowUnspecifiedOutputLocationResolution &&
134 ((!mOutputs.empty() && !mUnspecifiedLocationOutputs.empty()) ||
135 mUnspecifiedLocationOutputs.size() > 1))
136 {
137 for (const auto &symbol : mUnspecifiedLocationOutputs)
138 {
Olli Etuaho77ba4082016-12-16 12:01:18 +0000139 error(*symbol,
140 "must explicitly specify all locations when using multiple fragment outputs",
141 diagnostics);
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +0300142 }
143 }
Andrei Volykhina5527072017-03-22 16:46:30 +0300144
145 if (!mYuvOutputs.empty() && (mYuvOutputs.size() > 1 || mUsesFragDepth || !mOutputs.empty() ||
146 !mUnspecifiedLocationOutputs.empty()))
147 {
148 for (const auto &symbol : mYuvOutputs)
149 {
150 error(*symbol,
151 "not allowed to specify yuv qualifier when using depth or multiple color "
152 "fragment outputs",
153 diagnostics);
154 }
155 }
Jamie Madill05a80ce2013-06-20 11:55:49 -0400156}
Jamie Madill45bcc782016-11-07 13:58:48 -0500157
Olli Etuaho12b0b392017-05-30 13:22:31 +0300158} // anonymous namespace
159
160bool ValidateOutputs(TIntermBlock *root,
161 const TExtensionBehavior &extBehavior,
162 int maxDrawBuffers,
163 TDiagnostics *diagnostics)
164{
165 ValidateOutputsTraverser validateOutputs(extBehavior, maxDrawBuffers);
166 root->traverse(&validateOutputs);
167 int numErrorsBefore = diagnostics->numErrors();
168 validateOutputs.validate(diagnostics);
169 return (diagnostics->numErrors() == numErrorsBefore);
170}
171
Jamie Madill45bcc782016-11-07 13:58:48 -0500172} // namespace sh