1 // Copyright 2018 The Amber Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/vkscript/section_parser.h"
16 
17 #include <cassert>
18 #include <iostream>
19 #include <sstream>
20 #include <string>
21 
22 #include "src/make_unique.h"
23 #include "src/shader_data.h"
24 
25 namespace amber {
26 namespace vkscript {
27 
28 // static
HasShader(const NodeType type)29 bool SectionParser::HasShader(const NodeType type) {
30   return type == NodeType::kShader;
31 }
32 
33 SectionParser::SectionParser() = default;
34 
35 SectionParser::~SectionParser() = default;
36 
Parse(const std::string & data)37 Result SectionParser::Parse(const std::string& data) {
38   Result result = SplitSections(data);
39   if (!result.IsSuccess())
40     return result;
41   return {};
42 }
43 
NameToNodeType(const std::string & data,NodeType * section_type,ShaderType * shader_type,ShaderFormat * fmt) const44 Result SectionParser::NameToNodeType(const std::string& data,
45                                      NodeType* section_type,
46                                      ShaderType* shader_type,
47                                      ShaderFormat* fmt) const {
48   assert(section_type);
49   assert(shader_type);
50   assert(fmt);
51 
52   *fmt = kShaderFormatText;
53 
54   std::string name;
55   size_t pos = data.rfind(" spirv hex");
56   if (pos != std::string::npos) {
57     *fmt = kShaderFormatSpirvHex;
58     name = data.substr(0, pos);
59   } else {
60     pos = data.rfind(" spirv");
61     if (pos != std::string::npos) {
62       *fmt = kShaderFormatSpirvAsm;
63       name = data.substr(0, pos);
64     } else {
65       name = data;
66     }
67   }
68 
69   pos = data.rfind(" passthrough");
70   if (pos != std::string::npos) {
71     *fmt = kShaderFormatDefault;
72     name = data.substr(0, pos);
73   }
74 
75   if (name == "comment") {
76     *section_type = NodeType::kComment;
77   } else if (name == "indices") {
78     *section_type = NodeType::kIndices;
79   } else if (name == "require") {
80     *section_type = NodeType::kRequire;
81   } else if (name == "test") {
82     *section_type = NodeType::kTest;
83   } else if (name == "vertex data") {
84     *section_type = NodeType::kVertexData;
85   } else if (name == "compute shader") {
86     *section_type = NodeType::kShader;
87     *shader_type = kShaderTypeCompute;
88     if (*fmt == kShaderFormatText)
89       *fmt = kShaderFormatGlsl;
90   } else if (name == "fragment shader") {
91     *section_type = NodeType::kShader;
92     *shader_type = kShaderTypeFragment;
93     if (*fmt == kShaderFormatText)
94       *fmt = kShaderFormatGlsl;
95   } else if (name == "geometry shader") {
96     *section_type = NodeType::kShader;
97     *shader_type = kShaderTypeGeometry;
98     if (*fmt == kShaderFormatText)
99       *fmt = kShaderFormatGlsl;
100   } else if (name == "tessellation control shader") {
101     *section_type = NodeType::kShader;
102     *shader_type = kShaderTypeTessellationControl;
103     if (*fmt == kShaderFormatText)
104       *fmt = kShaderFormatGlsl;
105   } else if (name == "tessellation evaluation shader") {
106     *section_type = NodeType::kShader;
107     *shader_type = kShaderTypeTessellationEvaluation;
108     if (*fmt == kShaderFormatText)
109       *fmt = kShaderFormatGlsl;
110   } else if (name == "vertex shader") {
111     *section_type = NodeType::kShader;
112     *shader_type = kShaderTypeVertex;
113     if (*fmt == kShaderFormatText)
114       *fmt = kShaderFormatGlsl;
115   } else {
116     return Result("Invalid name: " + data);
117   }
118 
119   if (!SectionParser::HasShader(*section_type) &&
120       (*fmt == kShaderFormatGlsl || *fmt == kShaderFormatSpirvAsm ||
121        *fmt == kShaderFormatSpirvHex)) {
122     return Result("Invalid source format: " + data);
123   }
124 
125   return {};
126 }
127 
AddSection(NodeType section_type,ShaderType shader_type,ShaderFormat fmt,size_t line_count,const std::string & contents)128 void SectionParser::AddSection(NodeType section_type,
129                                ShaderType shader_type,
130                                ShaderFormat fmt,
131                                size_t line_count,
132                                const std::string& contents) {
133   if (section_type == NodeType::kComment)
134     return;
135 
136   if (fmt == kShaderFormatDefault) {
137     sections_.push_back({section_type, shader_type, kShaderFormatSpirvAsm,
138                          line_count, kPassThroughShader});
139     return;
140   }
141 
142   size_t size = contents.size();
143   while (size > 0) {
144     if (contents[size - 1] == '\n' || contents[size - 1] == '\r') {
145       --size;
146       continue;
147     }
148     break;
149   }
150 
151   sections_.push_back(
152       {section_type, shader_type, fmt, line_count, contents.substr(0, size)});
153 }
154 
SplitSections(const std::string & data)155 Result SectionParser::SplitSections(const std::string& data) {
156   std::stringstream ss(data);
157   size_t line_count = 0;
158   size_t section_start = 0;
159   bool in_section = false;
160 
161   NodeType current_type = NodeType::kComment;
162   ShaderType current_shader = kShaderTypeVertex;
163   ShaderFormat current_fmt = kShaderFormatText;
164   std::string section_contents;
165 
166   for (std::string line; std::getline(ss, line);) {
167     ++line_count;
168 
169     if (!in_section) {
170       if (line.empty() || line[0] == '#' || line == "\r")
171         continue;
172 
173       if (line[0] != '[')
174         return Result(std::to_string(line_count) + ": Invalid character");
175 
176       section_start = line_count;
177       in_section = true;
178     }
179 
180     if (line.empty()) {
181       section_contents += "\n";
182       continue;
183     }
184 
185     if (line[0] == '[') {
186       AddSection(current_type, current_shader, current_fmt, section_start,
187                  section_contents);
188       section_start = line_count;
189       section_contents = "";
190 
191       size_t name_end = line.rfind("]");
192       if (name_end == std::string::npos)
193         return Result(std::to_string(line_count) + ": Missing section close");
194 
195       std::string name = line.substr(1, name_end - 1);
196 
197       Result r =
198           NameToNodeType(name, &current_type, &current_shader, &current_fmt);
199       if (!r.IsSuccess())
200         return Result(std::to_string(line_count) + ": " + r.Error());
201     } else {
202       section_contents += line + "\n";
203     }
204   }
205   AddSection(current_type, current_shader, current_fmt, section_start,
206              section_contents);
207 
208   return {};
209 }
210 
211 }  // namespace vkscript
212 }  // namespace amber
213