1 // Copyright 2019 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/dxc_helper.h"
16
17 #include <algorithm>
18 #include <sstream>
19
20 #include "src/platform.h"
21
22 #if AMBER_PLATFORM_WINDOWS
23 #pragma warning(push)
24 #pragma warning(disable : 4267)
25 #pragma warning(disable : 4003)
26 #endif // AMBER_PLATFORM_WINDOWS
27
28 // clang-format off
29 // The order here matters, so don't reformat.
30 #include "dxc/Support/WinAdapter.h"
31 #include "dxc/Support/WinIncludes.h"
32 #include "dxc/Support/Global.h"
33 #include "dxc/Support/HLSLOptions.h"
34 #include "dxc/Support/dxcapi.use.h"
35 #include "dxc/dxcapi.h"
36 // clang-format on
37
38 #if AMBER_PLATFORM_WINDOWS
39 #pragma warning(pop)
40 #endif // AMBER_PLATFORM_WINDOWS
41
42 namespace amber {
43 namespace dxchelper {
44 namespace {
45
46 const wchar_t* kDxcFlags[] = {
47 L"-spirv", // SPIR-V compilation
48 L"-fcgl", // No SPIR-V Optimization
49 L"-enable-16bit-types", // Enabling 16bit types
50 };
51 const size_t kDxcFlagsCount = sizeof(kDxcFlags) / sizeof(const wchar_t*);
52
53 // Converts an IDxcBlob into a vector of 32-bit unsigned integers which
54 // is returned via the 'binaryWords' argument.
ConvertIDxcBlobToUint32(IDxcBlob * blob,std::vector<uint32_t> * binaryWords)55 void ConvertIDxcBlobToUint32(IDxcBlob* blob,
56 std::vector<uint32_t>* binaryWords) {
57 size_t num32BitWords = (blob->GetBufferSize() + 3) / 4;
58 std::string binaryStr(static_cast<char*>(blob->GetBufferPointer()),
59 blob->GetBufferSize());
60 binaryStr.resize(num32BitWords * 4, 0);
61 binaryWords->resize(num32BitWords, 0);
62 memcpy(binaryWords->data(), binaryStr.data(), binaryStr.size());
63 }
64
65 } // namespace
66
Compile(const std::string & src,const std::string & entry,const std::string & profile,const std::string & spv_env,std::vector<uint32_t> * generated_binary)67 Result Compile(const std::string& src,
68 const std::string& entry,
69 const std::string& profile,
70 const std::string& spv_env,
71 std::vector<uint32_t>* generated_binary) {
72 DxcInitThreadMalloc();
73
74 if (hlsl::options::initHlslOptTable()) {
75 DxcCleanupThreadMalloc();
76 return Result("DXC compile failure: initHlslOptTable");
77 }
78
79 IDxcLibrary* dxc_lib;
80 if (DxcCreateInstance(CLSID_DxcLibrary, __uuidof(IDxcLibrary),
81 reinterpret_cast<void**>(&dxc_lib)) < 0) {
82 DxcCleanupThreadMalloc();
83 return Result("DXCCreateInstance for DXCLibrary failed");
84 }
85
86 IDxcBlobEncoding* source;
87 if (dxc_lib->CreateBlobWithEncodingOnHeapCopy(
88 src.data(), static_cast<uint32_t>(src.size()), CP_UTF8, &source) <
89 0) {
90 DxcCleanupThreadMalloc();
91 return Result("DXC compile failure: CreateBlobFromFile");
92 }
93
94 IDxcIncludeHandler* include_handler;
95 if (dxc_lib->CreateIncludeHandler(&include_handler) < 0) {
96 DxcCleanupThreadMalloc();
97 return Result("DXC compile failure: CreateIncludeHandler");
98 }
99
100 IDxcCompiler* compiler;
101 if (DxcCreateInstance(CLSID_DxcCompiler, __uuidof(IDxcCompiler),
102 reinterpret_cast<void**>(&compiler)) < 0) {
103 DxcCleanupThreadMalloc();
104 return Result("DXCCreateInstance for DXCCompiler failed");
105 }
106
107 IDxcOperationResult* result;
108 std::wstring src_filename =
109 L"amber." + std::wstring(profile.begin(), profile.end());
110
111 std::vector<const wchar_t*> dxc_flags(kDxcFlags, &kDxcFlags[kDxcFlagsCount]);
112 const wchar_t* target_env = nullptr;
113 if (!spv_env.compare("spv1.3") || !spv_env.compare("vulkan1.1")) {
114 target_env = L"-fspv-target-env=vulkan1.1";
115 } else if (!spv_env.compare("spv1.0") || !spv_env.compare("vulkan1.0")) {
116 target_env = L"-fspv-target-env=vulkan1.0";
117 } else if (!spv_env.empty()) {
118 return Result(
119 "Invalid target environment. Choose spv1.3 or vulkan1.1 for vulkan1.1 "
120 "and spv1.0 or vulkan1.0 for vulkan1.0.");
121 }
122 if (target_env)
123 dxc_flags.push_back(target_env);
124
125 if (compiler->Compile(source, /* source text */
126 src_filename.c_str(), /* original file source */
127 std::wstring(entry.begin(), entry.end())
128 .c_str(), /* entry point name */
129 std::wstring(profile.begin(), profile.end())
130 .c_str(), /* shader profile to compile */
131 dxc_flags.data(), /* arguments */
132 dxc_flags.size(), /* argument count */
133 nullptr, /* defines */
134 0, /* define count */
135 include_handler, /* handler for #include */
136 &result /* output status */) < 0) {
137 DxcCleanupThreadMalloc();
138 return Result("DXC compile failure: Compile");
139 }
140
141 // Compilation is done. We can clean up the HlslOptTable.
142 hlsl::options::cleanupHlslOptTable();
143
144 // Get compilation results.
145 HRESULT result_status;
146 if (result->GetStatus(&result_status) < 0) {
147 DxcCleanupThreadMalloc();
148 return Result("DXC compile failure: GetStatus");
149 }
150
151 // Get diagnostics string.
152 IDxcBlobEncoding* error_buffer;
153 if (result->GetErrorBuffer(&error_buffer)) {
154 DxcCleanupThreadMalloc();
155 return Result("DXC compile failure: GetErrorBuffer");
156 }
157
158 const std::string diagnostics(
159 static_cast<char*>(error_buffer->GetBufferPointer()),
160 error_buffer->GetBufferSize());
161
162 bool success = true;
163 if (SUCCEEDED(result_status)) {
164 IDxcBlob* compiled_blob;
165 if (result->GetResult(&compiled_blob) < 0) {
166 DxcCleanupThreadMalloc();
167 return Result("DXC compile failure: GetResult");
168 }
169 ConvertIDxcBlobToUint32(compiled_blob, generated_binary);
170 } else {
171 success = false;
172 }
173
174 DxcCleanupThreadMalloc();
175 return success ? Result() : Result("DXC compile failure: " + diagnostics);
176 }
177
178 } // namespace dxchelper
179 } // namespace amber
180