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 if (fLocalSizeX >= 0) {
83 result += separator() + "local_size_x = " + std::to_string(fLocalSizeX);
84 }
85 if (fLocalSizeY >= 0) {
86 result += separator() + "local_size_y = " + std::to_string(fLocalSizeY);
87 }
88 if (fLocalSizeZ >= 0) {
89 result += separator() + "local_size_z = " + std::to_string(fLocalSizeZ);
90 }
91 if (result.size() > 0) {
92 result = "layout (" + result + ") ";
93 }
94 return result;
95 }
96
description() const97 std::string Layout::description() const {
98 std::string s = this->paddedDescription();
99 if (!s.empty()) {
100 s.pop_back();
101 }
102 return s;
103 }
104
checkPermittedLayout(const Context & context,Position pos,LayoutFlags permittedLayoutFlags) const105 bool Layout::checkPermittedLayout(const Context& context,
106 Position pos,
107 LayoutFlags permittedLayoutFlags) const {
108 static constexpr struct { LayoutFlag flag; const char* name; } kLayoutFlags[] = {
109 { LayoutFlag::kOriginUpperLeft, "origin_upper_left"},
110 { LayoutFlag::kPushConstant, "push_constant"},
111 { LayoutFlag::kBlendSupportAllEquations, "blend_support_all_equations"},
112 { LayoutFlag::kColor, "color"},
113 { LayoutFlag::kLocation, "location"},
114 { LayoutFlag::kOffset, "offset"},
115 { LayoutFlag::kBinding, "binding"},
116 { LayoutFlag::kTexture, "texture"},
117 { LayoutFlag::kSampler, "sampler"},
118 { LayoutFlag::kIndex, "index"},
119 { LayoutFlag::kSet, "set"},
120 { LayoutFlag::kBuiltin, "builtin"},
121 { LayoutFlag::kInputAttachmentIndex, "input_attachment_index"},
122 { LayoutFlag::kVulkan, "vulkan"},
123 { LayoutFlag::kMetal, "metal"},
124 { LayoutFlag::kWebGPU, "webgpu"},
125 { LayoutFlag::kDirect3D, "direct3d"},
126 { LayoutFlag::kRGBA8, "rgba8"},
127 { LayoutFlag::kRGBA32F, "rgba32f"},
128 { LayoutFlag::kR32F, "r32f"},
129 { LayoutFlag::kLocalSizeX, "local_size_x"},
130 { LayoutFlag::kLocalSizeY, "local_size_y"},
131 { LayoutFlag::kLocalSizeZ, "local_size_z"},
132 };
133
134 bool success = true;
135 LayoutFlags layoutFlags = fFlags;
136
137 LayoutFlags backendFlags = layoutFlags & LayoutFlag::kAllBackends;
138 if (SkPopCount(backendFlags.value()) > 1) {
139 context.fErrors->error(pos, "only one backend qualifier can be used");
140 success = false;
141 }
142
143 LayoutFlags pixelFormatFlags = layoutFlags & LayoutFlag::kAllPixelFormats;
144 if (SkPopCount(pixelFormatFlags.value()) > 1) {
145 context.fErrors->error(pos, "only one pixel format qualifier can be used");
146 success = false;
147 }
148
149 if ((layoutFlags & (LayoutFlag::kTexture | LayoutFlag::kSampler)) &&
150 layoutFlags & LayoutFlag::kBinding) {
151 context.fErrors->error(pos, "'binding' modifier cannot coexist with 'texture'/'sampler'");
152 success = false;
153 }
154 // The `texture` and `sampler` flags are only allowed when targeting Metal, WebGPU or Direct3D.
155 if (!(layoutFlags & (LayoutFlag::kMetal | LayoutFlag::kWebGPU | LayoutFlag::kDirect3D))) {
156 permittedLayoutFlags &= ~LayoutFlag::kTexture;
157 permittedLayoutFlags &= ~LayoutFlag::kSampler;
158 }
159 // The `push_constant` flag is only allowed when targeting Vulkan or WebGPU.
160 if (!(layoutFlags & (LayoutFlag::kVulkan | LayoutFlag::kWebGPU))) {
161 permittedLayoutFlags &= ~LayoutFlag::kPushConstant;
162 }
163 // The `set` flag is not allowed when explicitly targeting Metal.
164 if (layoutFlags & LayoutFlag::kMetal) {
165 permittedLayoutFlags &= ~LayoutFlag::kSet;
166 }
167
168 for (const auto& lf : kLayoutFlags) {
169 if (layoutFlags & lf.flag) {
170 if (!(permittedLayoutFlags & lf.flag)) {
171 context.fErrors->error(pos, "layout qualifier '" + std::string(lf.name) +
172 "' is not permitted here");
173 success = false;
174 }
175 layoutFlags &= ~lf.flag;
176 }
177 }
178 SkASSERT(layoutFlags == LayoutFlag::kNone);
179 return success;
180 }
181
operator ==(const Layout & other) const182 bool Layout::operator==(const Layout& other) const {
183 return fFlags == other.fFlags &&
184 fLocation == other.fLocation &&
185 fOffset == other.fOffset &&
186 fBinding == other.fBinding &&
187 fTexture == other.fTexture &&
188 fSampler == other.fSampler &&
189 fIndex == other.fIndex &&
190 fSet == other.fSet &&
191 fBuiltin == other.fBuiltin &&
192 fInputAttachmentIndex == other.fInputAttachmentIndex &&
193 fLocalSizeX == other.fLocalSizeX &&
194 fLocalSizeY == other.fLocalSizeY &&
195 fLocalSizeZ == other.fLocalSizeZ;
196 }
197
198 } // namespace SkSL
199