1 // Copyright 2020 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 parseried.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <sstream>
16
17 #include "gtest/gtest.h"
18 #include "src/amberscript/parser.h"
19 #include "src/shader_data.h"
20
21 namespace amber {
22 namespace amberscript {
23
24 namespace {
25 class ThreadEventRecorder : public debug::Thread {
26 std::stringstream& events;
27 std::string indent = " ";
28
29 public:
ThreadEventRecorder(std::stringstream & ev)30 explicit ThreadEventRecorder(std::stringstream& ev) : events(ev) {}
31
StepOver()32 void StepOver() override { events << indent << "STEP_OVER" << std::endl; }
StepIn()33 void StepIn() override { events << indent << "STEP_IN" << std::endl; }
StepOut()34 void StepOut() override { events << indent << "STEP_OUT" << std::endl; }
Continue()35 void Continue() override { events << indent << "CONTINUE" << std::endl; }
ExpectLocation(const debug::Location & location,const std::string & line)36 void ExpectLocation(const debug::Location& location,
37 const std::string& line) override {
38 events << indent << "EXPECT LOCATION \"" << location.file << "\" "
39 << location.line;
40 if (!line.empty()) {
41 events << " \"" << line << "\"";
42 }
43 events << std::endl;
44 }
ExpectCallstack(const std::vector<debug::StackFrame> & callstack)45 void ExpectCallstack(
46 const std::vector<debug::StackFrame>& callstack) override {
47 events << indent << "EXPECT CALLSTACK";
48 for (auto& frame : callstack) {
49 events << indent << " " << frame.name << " " << frame.location.file
50 << ":" << frame.location.line << " " << std::endl;
51 }
52 events << std::endl;
53 }
ExpectLocal(const std::string & name,int64_t value)54 void ExpectLocal(const std::string& name, int64_t value) override {
55 events << indent << "EXPECT LOCAL \"" << name << "\" EQ " << value
56 << std::endl;
57 }
ExpectLocal(const std::string & name,double value)58 void ExpectLocal(const std::string& name, double value) override {
59 events << indent << "EXPECT LOCAL \"" << name << "\" EQ " << value
60 << std::endl;
61 }
ExpectLocal(const std::string & name,const std::string & value)62 void ExpectLocal(const std::string& name, const std::string& value) override {
63 events << indent << "EXPECT LOCAL \"" << name << "\" EQ \"" << value << "\""
64 << std::endl;
65 }
66 };
67
68 class EventRecorder : public debug::Events {
69 public:
70 std::stringstream events;
71
record(const std::shared_ptr<const debug::ThreadScript> & script)72 void record(const std::shared_ptr<const debug::ThreadScript>& script) {
73 ThreadEventRecorder thread{events};
74 script->Run(&thread);
75 }
BreakOnComputeGlobalInvocation(uint32_t x,uint32_t y,uint32_t z,const std::shared_ptr<const debug::ThreadScript> & script)76 void BreakOnComputeGlobalInvocation(
77 uint32_t x,
78 uint32_t y,
79 uint32_t z,
80 const std::shared_ptr<const debug::ThreadScript>& script) override {
81 events << "THREAD GLOBAL_INVOCATION_ID " << x << " " << y << " " << z
82 << std::endl;
83 record(script);
84 events << "END" << std::endl;
85 }
BreakOnVertexIndex(uint32_t index,const std::shared_ptr<const debug::ThreadScript> & script)86 void BreakOnVertexIndex(
87 uint32_t index,
88 const std::shared_ptr<const debug::ThreadScript>& script) override {
89 events << "THREAD VERTEX_INDEX " << index << std::endl;
90 record(script);
91 events << "END" << std::endl;
92 }
BreakOnFragmentWindowSpacePosition(uint32_t x,uint32_t y,const std::shared_ptr<const debug::ThreadScript> & script)93 void BreakOnFragmentWindowSpacePosition(
94 uint32_t x,
95 uint32_t y,
96 const std::shared_ptr<const debug::ThreadScript>& script) override {
97 events << "THREAD FRAGMENT_WINDOW_SPACE_POSITION " << x << " " << y
98 << std::endl;
99 record(script);
100 events << "END" << std::endl;
101 }
102 };
103 } // namespace
104
105 using AmberScriptParserTest = testing::Test;
106
TEST_F(AmberScriptParserTest,DebugEventsScript)107 TEST_F(AmberScriptParserTest, DebugEventsScript) {
108 std::string dbg = R"(THREAD GLOBAL_INVOCATION_ID 1 2 3
109 EXPECT LOCATION "compute.hlsl" 2
110 STEP_IN
111 EXPECT LOCAL "one" EQ 1
112 STEP_OUT
113 EXPECT LOCAL "pi" EQ 3.14
114 STEP_OVER
115 EXPECT LOCAL "cat" EQ "meow"
116 CONTINUE
117 END
118 THREAD VERTEX_INDEX 2
119 EXPECT LOCATION "vertex.hlsl" 2 " dog:woof cat:meow duck:quack"
120 END
121 THREAD FRAGMENT_WINDOW_SPACE_POSITION 4 5
122 EXPECT LOCATION "fragment.hlsl" 42
123 CONTINUE
124 END
125 )";
126
127 std::string in = R"(
128 SHADER compute dbg_compute GLSL
129 void main() {}
130 END
131
132 PIPELINE compute my_pipeline
133 ATTACH dbg_compute
134 END
135
136 DEBUG my_pipeline 2 4 5
137 )" + dbg + "END";
138
139 Parser parser;
140 Result r = parser.Parse(in);
141 ASSERT_TRUE(r.IsSuccess()) << r.Error();
142
143 auto script = parser.GetScript();
144 const auto& commands = script->GetCommands();
145 ASSERT_EQ(1U, commands.size());
146
147 auto* cmd = commands[0].get();
148 ASSERT_TRUE(cmd->IsCompute());
149 auto* compute = cmd->AsCompute();
150 EXPECT_EQ(2U, compute->GetX());
151 EXPECT_EQ(4U, compute->GetY());
152 EXPECT_EQ(5U, compute->GetZ());
153
154 EventRecorder event_recorder;
155 compute->GetDebugScript()->Run(&event_recorder);
156 EXPECT_EQ(dbg, event_recorder.events.str());
157
158 auto& shaders = compute->GetPipeline()->GetShaders();
159 ASSERT_EQ(1U, shaders.size());
160
161 EXPECT_EQ(true, shaders[0].GetEmitDebugInfo());
162 }
163
TEST_F(AmberScriptParserTest,DebugEmitDebugInfoVertex)164 TEST_F(AmberScriptParserTest, DebugEmitDebugInfoVertex) {
165 std::string dbg = R"()";
166
167 std::string in = R"(
168 SHADER vertex dbg_vertex GLSL
169 void main() {}
170 END
171
172 SHADER fragment dbg_fragment GLSL
173 void main() {}
174 END
175
176 BUFFER position_buf DATA_TYPE R8G8_SNORM DATA
177 1 1 2 2 3 3
178 END
179
180 PIPELINE graphics my_pipeline
181 ATTACH dbg_vertex
182 ATTACH dbg_fragment
183 VERTEX_DATA position_buf LOCATION 0
184 END
185
186 DEBUG my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 1
187 THREAD VERTEX_INDEX 100
188 END
189 END)";
190
191 Parser parser;
192 Result r = parser.Parse(in);
193 ASSERT_TRUE(r.IsSuccess()) << r.Error();
194
195 auto script = parser.GetScript();
196 const auto& commands = script->GetCommands();
197 ASSERT_EQ(1U, commands.size());
198 auto* cmd = commands[0].get();
199 ASSERT_TRUE(cmd->IsDrawArrays());
200 auto* draw = cmd->AsDrawArrays();
201
202 for (auto& shader : draw->GetPipeline()->GetShaders()) {
203 bool expect_debug_info = shader.GetShaderType() == kShaderTypeVertex;
204 EXPECT_EQ(expect_debug_info, shader.GetEmitDebugInfo())
205 << "Emit debug info for shader type " << shader.GetShaderType();
206 }
207 }
208
TEST_F(AmberScriptParserTest,DebugEmitDebugInfoFragment)209 TEST_F(AmberScriptParserTest, DebugEmitDebugInfoFragment) {
210 std::string dbg = R"()";
211
212 std::string in = R"(
213 SHADER vertex dbg_vertex GLSL
214 void main() {}
215 END
216
217 SHADER fragment dbg_fragment GLSL
218 void main() {}
219 END
220
221 BUFFER position_buf DATA_TYPE R8G8_SNORM DATA
222 1 1 2 2 3 3
223 END
224
225 PIPELINE graphics my_pipeline
226 ATTACH dbg_vertex
227 ATTACH dbg_fragment
228 VERTEX_DATA position_buf LOCATION 0
229 END
230
231 DEBUG my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 1
232 THREAD FRAGMENT_WINDOW_SPACE_POSITION 1 2
233 END
234 END)";
235
236 Parser parser;
237 Result r = parser.Parse(in);
238 ASSERT_TRUE(r.IsSuccess()) << r.Error();
239
240 auto script = parser.GetScript();
241 const auto& commands = script->GetCommands();
242 ASSERT_EQ(1U, commands.size());
243 auto* cmd = commands[0].get();
244 ASSERT_TRUE(cmd->IsDrawArrays());
245 auto* draw = cmd->AsDrawArrays();
246
247 for (auto& shader : draw->GetPipeline()->GetShaders()) {
248 bool expect_debug_info = shader.GetShaderType() == kShaderTypeFragment;
249 EXPECT_EQ(expect_debug_info, shader.GetEmitDebugInfo())
250 << "Emit debug info for shader type " << shader.GetShaderType();
251 }
252 }
253
TEST_F(AmberScriptParserTest,DebugEmitNoDebugInfo)254 TEST_F(AmberScriptParserTest, DebugEmitNoDebugInfo) {
255 std::string dbg = R"()";
256
257 std::string in = R"(
258 SHADER vertex dbg_vertex GLSL
259 void main() {}
260 END
261
262 SHADER fragment dbg_fragment GLSL
263 void main() {}
264 END
265
266 BUFFER position_buf DATA_TYPE R8G8_SNORM DATA
267 1 1 2 2 3 3
268 END
269
270 PIPELINE graphics my_pipeline
271 ATTACH dbg_vertex
272 ATTACH dbg_fragment
273 VERTEX_DATA position_buf LOCATION 0
274 END
275
276 RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 1
277 )";
278
279 Parser parser;
280 Result r = parser.Parse(in);
281 ASSERT_TRUE(r.IsSuccess()) << r.Error();
282
283 auto script = parser.GetScript();
284 const auto& commands = script->GetCommands();
285 ASSERT_EQ(1U, commands.size());
286 auto* cmd = commands[0].get();
287 ASSERT_TRUE(cmd->IsDrawArrays());
288 auto* draw = cmd->AsDrawArrays();
289
290 for (auto& shader : draw->GetPipeline()->GetShaders()) {
291 EXPECT_EQ(false, shader.GetEmitDebugInfo());
292 }
293 }
294
295 } // namespace amberscript
296 } // namespace amber
297