1 /*
2 * Copyright 2022 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "src/sksl/ir/SkSLLayout.h"
9
10 #include "include/private/base/SkAssert.h"
11 #include "src/base/SkMathPriv.h"
12 #include "src/sksl/SkSLContext.h"
13 #include "src/sksl/SkSLErrorReporter.h"
14 #include "src/sksl/SkSLPosition.h"
15 #include "src/sksl/SkSLString.h"
16
17 namespace SkSL {
18
paddedDescription() const19 std::string Layout::paddedDescription() const {
20 std::string result;
21 auto separator = SkSL::String::Separator();
22 if (fFlags & LayoutFlag::kVulkan) {
23 result += separator() + "vulkan";
24 }
25 if (fFlags & LayoutFlag::kMetal) {
26 result += separator() + "metal";
27 }
28 if (fFlags & LayoutFlag::kWebGPU) {
29 result += separator() + "webgpu";
30 }
31 if (fFlags & LayoutFlag::kDirect3D) {
32 result += separator() + "direct3d";
33 }
34 if (fFlags & LayoutFlag::kRGBA8) {
35 result += separator() + "rgba8";
36 }
37 if (fFlags & LayoutFlag::kRGBA32F) {
38 result += separator() + "rgba32f";
39 }
40 if (fFlags & LayoutFlag::kR32F) {
41 result += separator() + "r32f";
42 }
43 if (fLocation >= 0) {
44 result += separator() + "location = " + std::to_string(fLocation);
45 }
46 if (fOffset >= 0) {
47 result += separator() + "offset = " + std::to_string(fOffset);
48 }
49 if (fBinding >= 0) {
50 result += separator() + "binding = " + std::to_string(fBinding);
51 }
52 if (fTexture >= 0) {
53 result += separator() + "texture = " + std::to_string(fTexture);
54 }
55 if (fSampler >= 0) {
56 result += separator() + "sampler = " + std::to_string(fSampler);
57 }
58 if (fIndex >= 0) {
59 result += separator() + "index = " + std::to_string(fIndex);
60 }
61 if (fSet >= 0) {
62 result += separator() + "set = " + std::to_string(fSet);
63 }
64 if (fBuiltin >= 0) {
65 result += separator() + "builtin = " + std::to_string(fBuiltin);
66 }
67 if (fInputAttachmentIndex >= 0) {
68 result += separator() + "input_attachment_index = " + std::to_string(fInputAttachmentIndex);
69 }
70 if (fFlags & LayoutFlag::kOriginUpperLeft) {
71 result += separator() + "origin_upper_left";
72 }
73 if (fFlags & LayoutFlag::kBlendSupportAllEquations) {
74 result += separator() + "blend_support_all_equations";
75 }
76 if (fFlags & LayoutFlag::kPushConstant) {
77 result += separator() + "push_constant";
78 }
79 if (fFlags & LayoutFlag::kColor) {
80 result += separator() + "color";
81 }
82 #ifdef SKSL_EXT
83 if (fFlags & LayoutFlag::kConstantId) {
84 result += separator() + "constant_id";
85 }
86 #endif
87 if (fLocalSizeX >= 0) {
88 result += separator() + "local_size_x = " + std::to_string(fLocalSizeX);
89 }
90 if (fLocalSizeY >= 0) {
91 result += separator() + "local_size_y = " + std::to_string(fLocalSizeY);
92 }
93 if (fLocalSizeZ >= 0) {
94 result += separator() + "local_size_z = " + std::to_string(fLocalSizeZ);
95 }
96 if (result.size() > 0) {
97 result = "layout (" + result + ") ";
98 }
99 return result;
100 }
101
description() const102 std::string Layout::description() const {
103 std::string s = this->paddedDescription();
104 if (!s.empty()) {
105 s.pop_back();
106 }
107 return s;
108 }
109
checkPermittedLayout(const Context & context,Position pos,LayoutFlags permittedLayoutFlags) const110 bool Layout::checkPermittedLayout(const Context& context,
111 Position pos,
112 LayoutFlags permittedLayoutFlags) const {
113 static constexpr struct { LayoutFlag flag; const char* name; } kLayoutFlags[] = {
114 { LayoutFlag::kOriginUpperLeft, "origin_upper_left"},
115 { LayoutFlag::kPushConstant, "push_constant"},
116 { LayoutFlag::kBlendSupportAllEquations, "blend_support_all_equations"},
117 { LayoutFlag::kColor, "color"},
118 { LayoutFlag::kLocation, "location"},
119 { LayoutFlag::kOffset, "offset"},
120 { LayoutFlag::kBinding, "binding"},
121 { LayoutFlag::kTexture, "texture"},
122 { LayoutFlag::kSampler, "sampler"},
123 { LayoutFlag::kIndex, "index"},
124 { LayoutFlag::kSet, "set"},
125 { LayoutFlag::kBuiltin, "builtin"},
126 { LayoutFlag::kInputAttachmentIndex, "input_attachment_index"},
127 { LayoutFlag::kVulkan, "vulkan"},
128 { LayoutFlag::kMetal, "metal"},
129 { LayoutFlag::kWebGPU, "webgpu"},
130 { LayoutFlag::kDirect3D, "direct3d"},
131 { LayoutFlag::kRGBA8, "rgba8"},
132 { LayoutFlag::kRGBA32F, "rgba32f"},
133 { LayoutFlag::kR32F, "r32f"},
134 { LayoutFlag::kLocalSizeX, "local_size_x"},
135 { LayoutFlag::kLocalSizeY, "local_size_y"},
136 { LayoutFlag::kLocalSizeZ, "local_size_z"},
137 #ifdef SKSL_EXT
138 { LayoutFlag::kConstantId, "constant_id"},
139 #endif
140 };
141
142 bool success = true;
143 LayoutFlags layoutFlags = fFlags;
144
145 LayoutFlags backendFlags = layoutFlags & LayoutFlag::kAllBackends;
146 if (SkPopCount(backendFlags.value()) > 1) {
147 context.fErrors->error(pos, "only one backend qualifier can be used");
148 success = false;
149 }
150
151 LayoutFlags pixelFormatFlags = layoutFlags & LayoutFlag::kAllPixelFormats;
152 if (SkPopCount(pixelFormatFlags.value()) > 1) {
153 context.fErrors->error(pos, "only one pixel format qualifier can be used");
154 success = false;
155 }
156
157 if ((layoutFlags & (LayoutFlag::kTexture | LayoutFlag::kSampler)) &&
158 layoutFlags & LayoutFlag::kBinding) {
159 context.fErrors->error(pos, "'binding' modifier cannot coexist with 'texture'/'sampler'");
160 success = false;
161 }
162 // The `texture` and `sampler` flags are only allowed when targeting Metal, WebGPU or Direct3D.
163 if (!(layoutFlags & (LayoutFlag::kMetal | LayoutFlag::kWebGPU | LayoutFlag::kDirect3D))) {
164 permittedLayoutFlags &= ~LayoutFlag::kTexture;
165 permittedLayoutFlags &= ~LayoutFlag::kSampler;
166 }
167 #ifndef SKSL_EXT
168 // The `push_constant` flag is only allowed when targeting Vulkan or WebGPU.
169 if (!(layoutFlags & (LayoutFlag::kVulkan | LayoutFlag::kWebGPU))) {
170 permittedLayoutFlags &= ~LayoutFlag::kPushConstant;
171 }
172 #endif
173 // The `set` flag is not allowed when explicitly targeting Metal.
174 if (layoutFlags & LayoutFlag::kMetal) {
175 permittedLayoutFlags &= ~LayoutFlag::kSet;
176 }
177
178 for (const auto& lf : kLayoutFlags) {
179 if (layoutFlags & lf.flag) {
180 if (!(permittedLayoutFlags & lf.flag)) {
181 context.fErrors->error(pos, "layout qualifier '" + std::string(lf.name) +
182 "' is not permitted here");
183 success = false;
184 }
185 layoutFlags &= ~lf.flag;
186 }
187 }
188 SkASSERT(layoutFlags == LayoutFlag::kNone);
189 return success;
190 }
191
operator ==(const Layout & other) const192 bool Layout::operator==(const Layout& other) const {
193 return fFlags == other.fFlags &&
194 fLocation == other.fLocation &&
195 fOffset == other.fOffset &&
196 fBinding == other.fBinding &&
197 fTexture == other.fTexture &&
198 fSampler == other.fSampler &&
199 fIndex == other.fIndex &&
200 fSet == other.fSet &&
201 fBuiltin == other.fBuiltin &&
202 #ifdef SKSL_EXT
203 fConstantId == other.fConstantId &&
204 #endif
205 fInputAttachmentIndex == other.fInputAttachmentIndex &&
206 fLocalSizeX == other.fLocalSizeX &&
207 fLocalSizeY == other.fLocalSizeY &&
208 fLocalSizeZ == other.fLocalSizeZ;
209 }
210
211 } // namespace SkSL
212