blob: 107c845b8c8f0b1e2329ac0f6dc6141503bcbc55 [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 Etuaho8b5e8fd2017-12-15 14:59:15 +020023
Olli Etuaho77ba4082016-12-16 12:01:18 +000024void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagnostics)
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +030025{
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +020026 diagnostics->error(symbol.getLine(), reason, symbol.getName().c_str());
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +030027}
28
Olli Etuaho12b0b392017-05-30 13:22:31 +030029class ValidateOutputsTraverser : public TIntermTraverser
30{
31 public:
32 ValidateOutputsTraverser(const TExtensionBehavior &extBehavior, int maxDrawBuffers);
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +030033
Olli Etuaho12b0b392017-05-30 13:22:31 +030034 void validate(TDiagnostics *diagnostics) const;
35
36 void visitSymbol(TIntermSymbol *) override;
37
38 private:
39 int mMaxDrawBuffers;
40 bool mAllowUnspecifiedOutputLocationResolution;
41 bool mUsesFragDepth;
42
43 typedef std::vector<TIntermSymbol *> OutputVector;
44 OutputVector mOutputs;
45 OutputVector mUnspecifiedLocationOutputs;
46 OutputVector mYuvOutputs;
Olli Etuahoaaa55bf2017-12-28 11:20:30 +020047 std::set<int> mVisitedSymbols; // Visited symbol ids.
Olli Etuaho12b0b392017-05-30 13:22:31 +030048};
49
50ValidateOutputsTraverser::ValidateOutputsTraverser(const TExtensionBehavior &extBehavior,
51 int maxDrawBuffers)
Olli Etuaho3d0d9a42015-06-01 12:16:36 +030052 : TIntermTraverser(true, false, false),
Jamie Madill05a80ce2013-06-20 11:55:49 -040053 mMaxDrawBuffers(maxDrawBuffers),
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +030054 mAllowUnspecifiedOutputLocationResolution(
Olli Etuaho2a1e8f92017-07-14 11:49:36 +030055 IsExtensionEnabled(extBehavior, TExtension::EXT_blend_func_extended)),
Andrei Volykhina5527072017-03-22 16:46:30 +030056 mUsesFragDepth(false)
Jamie Madill05a80ce2013-06-20 11:55:49 -040057{
58}
59
Olli Etuaho12b0b392017-05-30 13:22:31 +030060void ValidateOutputsTraverser::visitSymbol(TIntermSymbol *symbol)
Jamie Madill05a80ce2013-06-20 11:55:49 -040061{
Olli Etuahoaaa55bf2017-12-28 11:20:30 +020062 if (symbol->variable().symbolType() == SymbolType::Empty)
Jamie Madill05a80ce2013-06-20 11:55:49 -040063 return;
64
Olli Etuahoaaa55bf2017-12-28 11:20:30 +020065 if (mVisitedSymbols.count(symbol->uniqueId().get()) == 1)
66 return;
Jamie Madill05a80ce2013-06-20 11:55:49 -040067
Olli Etuahoaaa55bf2017-12-28 11:20:30 +020068 mVisitedSymbols.insert(symbol->uniqueId().get());
69
70 TQualifier qualifier = symbol->getQualifier();
Jamie Madill19571812013-08-12 15:26:34 -070071 if (qualifier == EvqFragmentOut)
Jamie Madill05a80ce2013-06-20 11:55:49 -040072 {
Andrei Volykhina5527072017-03-22 16:46:30 +030073 if (symbol->getType().getLayoutQualifier().location != -1)
Jamie Madill05a80ce2013-06-20 11:55:49 -040074 {
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +030075 mOutputs.push_back(symbol);
Jamie Madill05a80ce2013-06-20 11:55:49 -040076 }
Andrei Volykhina5527072017-03-22 16:46:30 +030077 else if (symbol->getType().getLayoutQualifier().yuv == true)
78 {
79 mYuvOutputs.push_back(symbol);
80 }
81 else
82 {
83 mUnspecifiedLocationOutputs.push_back(symbol);
84 }
85 }
86 else if (qualifier == EvqFragDepth || qualifier == EvqFragDepthEXT)
87 {
88 mUsesFragDepth = true;
Jamie Madill05a80ce2013-06-20 11:55:49 -040089 }
90}
91
Olli Etuaho12b0b392017-05-30 13:22:31 +030092void ValidateOutputsTraverser::validate(TDiagnostics *diagnostics) const
Jamie Madill05a80ce2013-06-20 11:55:49 -040093{
Olli Etuaho77ba4082016-12-16 12:01:18 +000094 ASSERT(diagnostics);
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +030095 OutputVector validOutputs(mMaxDrawBuffers);
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +030096
97 for (const auto &symbol : mOutputs)
98 {
99 const TType &type = symbol->getType();
Olli Etuaho96f6adf2017-08-16 11:18:54 +0300100 ASSERT(!type.isArrayOfArrays()); // Disallowed in GLSL ES 3.10 section 4.3.6.
101 const size_t elementCount =
102 static_cast<size_t>(type.isArray() ? type.getOutermostArraySize() : 1u);
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +0300103 const size_t location = static_cast<size_t>(type.getLayoutQualifier().location);
104
105 ASSERT(type.getLayoutQualifier().location != -1);
106
107 if (location + elementCount <= validOutputs.size())
108 {
109 for (size_t elementIndex = 0; elementIndex < elementCount; elementIndex++)
110 {
111 const size_t offsetLocation = location + elementIndex;
112 if (validOutputs[offsetLocation])
113 {
114 std::stringstream strstr;
115 strstr << "conflicting output locations with previously defined output '"
Olli Etuaho8b5e8fd2017-12-15 14:59:15 +0200116 << validOutputs[offsetLocation]->getName() << "'";
Olli Etuaho77ba4082016-12-16 12:01:18 +0000117 error(*symbol, strstr.str().c_str(), diagnostics);
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +0300118 }
119 else
120 {
121 validOutputs[offsetLocation] = symbol;
122 }
123 }
124 }
125 else
126 {
127 if (elementCount > 0)
128 {
Olli Etuaho77ba4082016-12-16 12:01:18 +0000129 error(*symbol,
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +0300130 elementCount > 1 ? "output array locations would exceed MAX_DRAW_BUFFERS"
Olli Etuaho77ba4082016-12-16 12:01:18 +0000131 : "output location must be < MAX_DRAW_BUFFERS",
132 diagnostics);
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +0300133 }
134 }
135 }
136
137 if (!mAllowUnspecifiedOutputLocationResolution &&
138 ((!mOutputs.empty() && !mUnspecifiedLocationOutputs.empty()) ||
139 mUnspecifiedLocationOutputs.size() > 1))
140 {
141 for (const auto &symbol : mUnspecifiedLocationOutputs)
142 {
Olli Etuaho77ba4082016-12-16 12:01:18 +0000143 error(*symbol,
144 "must explicitly specify all locations when using multiple fragment outputs",
145 diagnostics);
Kimmo Kinnunenb18609b2015-07-16 14:13:11 +0300146 }
147 }
Andrei Volykhina5527072017-03-22 16:46:30 +0300148
149 if (!mYuvOutputs.empty() && (mYuvOutputs.size() > 1 || mUsesFragDepth || !mOutputs.empty() ||
150 !mUnspecifiedLocationOutputs.empty()))
151 {
152 for (const auto &symbol : mYuvOutputs)
153 {
154 error(*symbol,
155 "not allowed to specify yuv qualifier when using depth or multiple color "
156 "fragment outputs",
157 diagnostics);
158 }
159 }
Jamie Madill05a80ce2013-06-20 11:55:49 -0400160}
Jamie Madill45bcc782016-11-07 13:58:48 -0500161
Olli Etuaho12b0b392017-05-30 13:22:31 +0300162} // anonymous namespace
163
164bool ValidateOutputs(TIntermBlock *root,
165 const TExtensionBehavior &extBehavior,
166 int maxDrawBuffers,
167 TDiagnostics *diagnostics)
168{
169 ValidateOutputsTraverser validateOutputs(extBehavior, maxDrawBuffers);
170 root->traverse(&validateOutputs);
171 int numErrorsBefore = diagnostics->numErrors();
172 validateOutputs.validate(diagnostics);
173 return (diagnostics->numErrors() == numErrorsBefore);
174}
175
Jamie Madill45bcc782016-11-07 13:58:48 -0500176} // namespace sh