1 /*
2 * Copyright © Microsoft Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include <stdio.h>
25 #include <stdint.h>
26 #include <stdexcept>
27
28 #include <directx/d3d12.h>
29 #include <dxgi1_4.h>
30 #include <gtest/gtest.h>
31 #include <wrl.h>
32
33 #include "clc_compiler.h"
34
35 using std::runtime_error;
36 using Microsoft::WRL::ComPtr;
37
38 inline D3D12_CPU_DESCRIPTOR_HANDLE
offset_cpu_handle(D3D12_CPU_DESCRIPTOR_HANDLE handle,UINT offset)39 offset_cpu_handle(D3D12_CPU_DESCRIPTOR_HANDLE handle, UINT offset)
40 {
41 handle.ptr += offset;
42 return handle;
43 }
44
45 inline size_t
align(size_t value,unsigned alignment)46 align(size_t value, unsigned alignment)
47 {
48 assert(alignment > 0);
49 return ((value + (alignment - 1)) / alignment) * alignment;
50 }
51
52 class ComputeTest : public ::testing::Test {
53 protected:
54 struct Shader {
55 std::shared_ptr<struct clc_binary> obj;
56 std::shared_ptr<struct clc_parsed_spirv> metadata;
57 std::shared_ptr<struct clc_dxil_object> dxil;
58 };
59
60 static void
61 enable_d3d12_debug_layer();
62
63 static IDXGIFactory4 *
64 get_dxgi_factory();
65
66 static IDXGIAdapter1 *
67 choose_adapter(IDXGIFactory4 *factory);
68
69 static ID3D12Device *
70 create_device(IDXGIAdapter1 *adapter);
71
72 struct Resources {
addResources73 void add(ComPtr<ID3D12Resource> res,
74 D3D12_DESCRIPTOR_RANGE_TYPE type,
75 unsigned spaceid,
76 unsigned resid)
77 {
78 descs.push_back(res);
79
80 if(!ranges.empty() &&
81 ranges.back().RangeType == type &&
82 ranges.back().RegisterSpace == spaceid &&
83 ranges.back().BaseShaderRegister + ranges.back().NumDescriptors == resid) {
84 ranges.back().NumDescriptors++;
85 return;
86 }
87
88 D3D12_DESCRIPTOR_RANGE1 range;
89
90 range.RangeType = type;
91 range.NumDescriptors = 1;
92 range.BaseShaderRegister = resid;
93 range.RegisterSpace = spaceid;
94 range.OffsetInDescriptorsFromTableStart = descs.size() - 1;
95 range.Flags = D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS;
96 ranges.push_back(range);
97 }
98
99 std::vector<D3D12_DESCRIPTOR_RANGE1> ranges;
100 std::vector<ComPtr<ID3D12Resource>> descs;
101 };
102
103 ComPtr<ID3D12RootSignature>
104 create_root_signature(const Resources &resources);
105
106 ComPtr<ID3D12PipelineState>
107 create_pipeline_state(ComPtr<ID3D12RootSignature> &root_sig,
108 const struct clc_dxil_object &dxil);
109
110 ComPtr<ID3D12Resource>
111 create_buffer(int size, D3D12_HEAP_TYPE heap_type);
112
113 ComPtr<ID3D12Resource>
114 create_upload_buffer_with_data(const void *data, size_t size);
115
116 ComPtr<ID3D12Resource>
117 create_sized_buffer_with_data(size_t buffer_size, const void *data,
118 size_t data_size);
119
120 ComPtr<ID3D12Resource>
create_buffer_with_data(const void * data,size_t size)121 create_buffer_with_data(const void *data, size_t size)
122 {
123 return create_sized_buffer_with_data(size, data, size);
124 }
125
126 void
127 get_buffer_data(ComPtr<ID3D12Resource> res,
128 void *buf, size_t size);
129
130 void
131 resource_barrier(ComPtr<ID3D12Resource> &res,
132 D3D12_RESOURCE_STATES state_before,
133 D3D12_RESOURCE_STATES state_after);
134
135 void
136 execute_cmdlist();
137
138 void
139 create_uav_buffer(ComPtr<ID3D12Resource> res,
140 size_t width, size_t byte_stride,
141 D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle);
142
143 void create_cbv(ComPtr<ID3D12Resource> res, size_t size,
144 D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle);
145
146 ComPtr<ID3D12Resource>
147 add_uav_resource(Resources &resources, unsigned spaceid, unsigned resid,
148 const void *data = NULL, size_t num_elems = 0,
149 size_t elem_size = 0);
150
151 ComPtr<ID3D12Resource>
152 add_cbv_resource(Resources &resources, unsigned spaceid, unsigned resid,
153 const void *data, size_t size);
154
155 void
156 SetUp() override;
157
158 void
159 TearDown() override;
160
161 Shader
162 compile(const std::vector<const char *> &sources,
163 const std::vector<const char *> &compile_args = {},
164 bool create_library = false);
165
166 Shader
167 link(const std::vector<Shader> &sources,
168 bool create_library = false);
169
170 Shader
171 assemble(const char *source);
172
173 void
174 configure(Shader &shader,
175 const struct clc_runtime_kernel_conf *conf);
176
177 void
178 validate(Shader &shader);
179
180 template <typename T>
181 Shader
specialize(Shader & shader,uint32_t id,T const & val)182 specialize(Shader &shader, uint32_t id, T const& val)
183 {
184 Shader new_shader;
185 new_shader.obj = std::shared_ptr<clc_binary>(new clc_binary{}, [](clc_binary *spirv)
186 {
187 clc_free_spirv(spirv);
188 delete spirv;
189 });
190 if (!shader.metadata)
191 configure(shader, NULL);
192
193 clc_spirv_specialization spec;
194 spec.id = id;
195 memcpy(&spec.value, &val, sizeof(val));
196 clc_spirv_specialization_consts consts;
197 consts.specializations = &spec;
198 consts.num_specializations = 1;
199 if (!clc_specialize_spirv(shader.obj.get(), shader.metadata.get(), &consts, new_shader.obj.get()))
200 throw runtime_error("failed to specialize");
201
202 configure(new_shader, NULL);
203
204 return new_shader;
205 }
206
207 enum ShaderArgDirection {
208 SHADER_ARG_INPUT = 1,
209 SHADER_ARG_OUTPUT = 2,
210 SHADER_ARG_INOUT = SHADER_ARG_INPUT | SHADER_ARG_OUTPUT,
211 };
212
213 class RawShaderArg {
214 public:
RawShaderArg(enum ShaderArgDirection dir)215 RawShaderArg(enum ShaderArgDirection dir) : dir(dir) { }
216 virtual size_t get_elem_size() const = 0;
217 virtual size_t get_num_elems() const = 0;
218 virtual const void *get_data() const = 0;
219 virtual void *get_data() = 0;
get_direction()220 enum ShaderArgDirection get_direction() { return dir; }
221 private:
222 enum ShaderArgDirection dir;
223 };
224
225 class NullShaderArg : public RawShaderArg {
226 public:
NullShaderArg()227 NullShaderArg() : RawShaderArg(SHADER_ARG_INPUT) { }
get_elem_size()228 size_t get_elem_size() const override { return 0; }
get_num_elems()229 size_t get_num_elems() const override { return 0; }
get_data()230 const void *get_data() const override { return NULL; }
get_data()231 void *get_data() override { return NULL; }
232 };
233
234 template <typename T>
235 class ShaderArg : public std::vector<T>, public RawShaderArg
236 {
237 public:
238 ShaderArg(const T &v, enum ShaderArgDirection dir = SHADER_ARG_INOUT) :
239 std::vector<T>({ v }), RawShaderArg(dir) { }
240 ShaderArg(const std::vector<T> &v, enum ShaderArgDirection dir = SHADER_ARG_INOUT) :
241 std::vector<T>(v), RawShaderArg(dir) { }
242 ShaderArg(const std::initializer_list<T> v, enum ShaderArgDirection dir = SHADER_ARG_INOUT) :
243 std::vector<T>(v), RawShaderArg(dir) { }
244
245 ShaderArg<T>& operator =(const T &v)
246 {
247 this->clear();
248 this->push_back(v);
249 return *this;
250 }
251
252 operator T&() { return this->at(0); }
253 operator const T&() const { return this->at(0); }
254
255 ShaderArg<T>& operator =(const std::vector<T> &v)
256 {
257 *this = v;
258 return *this;
259 }
260
261 ShaderArg<T>& operator =(std::initializer_list<T> v)
262 {
263 *this = v;
264 return *this;
265 }
266
get_elem_size()267 size_t get_elem_size() const override { return sizeof(T); }
get_num_elems()268 size_t get_num_elems() const override { return this->size(); }
get_data()269 const void *get_data() const override { return this->data(); }
get_data()270 void *get_data() override { return this->data(); }
271 };
272
273 struct CompileArgs
274 {
275 unsigned x, y, z;
276 std::vector<const char *> compiler_command_line;
277 clc_work_properties_data work_props;
278 };
279
280 private:
gather_args(std::vector<RawShaderArg * > & args)281 void gather_args(std::vector<RawShaderArg *> &args) { }
282
283 template <typename T, typename... Rest>
gather_args(std::vector<RawShaderArg * > & args,T & arg,Rest &...rest)284 void gather_args(std::vector<RawShaderArg *> &args, T &arg, Rest&... rest)
285 {
286 args.push_back(&arg);
287 gather_args(args, rest...);
288 }
289
290 void run_shader_with_raw_args(Shader shader,
291 const CompileArgs &compile_args,
292 const std::vector<RawShaderArg *> &args);
293
294 protected:
295 template <typename... Args>
run_shader(Shader shader,const CompileArgs & compile_args,Args &...args)296 void run_shader(Shader shader,
297 const CompileArgs &compile_args,
298 Args&... args)
299 {
300 std::vector<RawShaderArg *> raw_args;
301 gather_args(raw_args, args...);
302 run_shader_with_raw_args(shader, compile_args, raw_args);
303 }
304
305 template <typename... Args>
run_shader(const std::vector<const char * > & sources,unsigned x,unsigned y,unsigned z,Args &...args)306 void run_shader(const std::vector<const char *> &sources,
307 unsigned x, unsigned y, unsigned z,
308 Args&... args)
309 {
310 std::vector<RawShaderArg *> raw_args;
311 gather_args(raw_args, args...);
312 CompileArgs compile_args = { x, y, z };
313 run_shader_with_raw_args(compile(sources), compile_args, raw_args);
314 }
315
316 template <typename... Args>
run_shader(const std::vector<const char * > & sources,const CompileArgs & compile_args,Args &...args)317 void run_shader(const std::vector<const char *> &sources,
318 const CompileArgs &compile_args,
319 Args&... args)
320 {
321 std::vector<RawShaderArg *> raw_args;
322 gather_args(raw_args, args...);
323 run_shader_with_raw_args(
324 compile(sources, compile_args.compiler_command_line),
325 compile_args, raw_args);
326 }
327
328 template <typename... Args>
run_shader(const char * source,unsigned x,unsigned y,unsigned z,Args &...args)329 void run_shader(const char *source,
330 unsigned x, unsigned y, unsigned z,
331 Args&... args)
332 {
333 std::vector<RawShaderArg *> raw_args;
334 gather_args(raw_args, args...);
335 CompileArgs compile_args = { x, y, z };
336 run_shader_with_raw_args(compile({ source }), compile_args, raw_args);
337 }
338
339 IDXGIFactory4 *factory;
340 IDXGIAdapter1 *adapter;
341 ID3D12Device *dev;
342 ID3D12Fence *cmdqueue_fence;
343 ID3D12CommandQueue *cmdqueue;
344 ID3D12CommandAllocator *cmdalloc;
345 ID3D12GraphicsCommandList *cmdlist;
346 ID3D12DescriptorHeap *uav_heap;
347
348 struct clc_libclc *compiler_ctx;
349
350 UINT uav_heap_incr;
351 int fence_value;
352
353 HANDLE event;
354 static PFN_D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE D3D12SerializeVersionedRootSignature;
355 };
356