• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Tint 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/val/val.h"
16 
17 #include "src/utils/io/command.h"
18 #include "src/utils/io/tmpfile.h"
19 
20 #ifdef _WIN32
21 #include <Windows.h>
22 #include <d3dcommon.h>
23 #include <d3dcompiler.h>
24 
25 #include <wrl.h>
26 using Microsoft::WRL::ComPtr;
27 #endif  // _WIN32
28 
29 namespace tint {
30 namespace val {
31 
HlslUsingDXC(const std::string & dxc_path,const std::string & source,const EntryPointList & entry_points)32 Result HlslUsingDXC(const std::string& dxc_path,
33                     const std::string& source,
34                     const EntryPointList& entry_points) {
35   Result result;
36 
37   auto dxc = utils::Command(dxc_path);
38   if (!dxc.Found()) {
39     result.output = "DXC not found at '" + std::string(dxc_path) + "'";
40     result.failed = true;
41     return result;
42   }
43 
44   utils::TmpFile file;
45   file << source;
46 
47   for (auto ep : entry_points) {
48     const char* profile = "";
49 
50     switch (ep.second) {
51       case ast::PipelineStage::kNone:
52         result.output = "Invalid PipelineStage";
53         result.failed = true;
54         return result;
55       case ast::PipelineStage::kVertex:
56         profile = "-T vs_6_0";
57         break;
58       case ast::PipelineStage::kFragment:
59         profile = "-T ps_6_0";
60         break;
61       case ast::PipelineStage::kCompute:
62         profile = "-T cs_6_0";
63         break;
64     }
65 
66     // Match Dawn's compile flags
67     // See dawn\src\dawn_native\d3d12\RenderPipelineD3D12.cpp
68     // and dawn_native\d3d12\ShaderModuleD3D12.cpp (GetDXCArguments)
69     const char* compileFlags =
70         "/Zpr "  // D3DCOMPILE_PACK_MATRIX_ROW_MAJOR
71         "/Gis";  // D3DCOMPILE_IEEE_STRICTNESS
72 
73     auto res = dxc(profile, "-E " + ep.first, compileFlags, file.Path());
74     if (!res.out.empty()) {
75       if (!result.output.empty()) {
76         result.output += "\n";
77       }
78       result.output += res.out;
79     }
80     if (!res.err.empty()) {
81       if (!result.output.empty()) {
82         result.output += "\n";
83       }
84       result.output += res.err;
85     }
86     result.failed = (res.error_code != 0);
87   }
88 
89   if (entry_points.empty()) {
90     result.output = "No entrypoint found";
91     result.failed = true;
92     return result;
93   }
94 
95   return result;
96 }
97 
98 #ifdef _WIN32
HlslUsingFXC(const std::string & source,const EntryPointList & entry_points)99 Result HlslUsingFXC(const std::string& source,
100                     const EntryPointList& entry_points) {
101   Result result;
102 
103   // This library leaks if an error happens in this function, but it is ok
104   // because it is loaded at most once, and the executables using HlslUsingFXC
105   // are short-lived.
106   HMODULE fxcLib = LoadLibraryA("d3dcompiler_47.dll");
107   if (fxcLib == nullptr) {
108     result.output = "Couldn't load FXC";
109     result.failed = true;
110     return result;
111   }
112 
113   pD3DCompile d3dCompile =
114       reinterpret_cast<pD3DCompile>(GetProcAddress(fxcLib, "D3DCompile"));
115   if (d3dCompile == nullptr) {
116     result.output = "Couldn't load D3DCompile from FXC";
117     result.failed = true;
118     return result;
119   }
120 
121   for (auto ep : entry_points) {
122     const char* profile = "";
123     switch (ep.second) {
124       case ast::PipelineStage::kNone:
125         result.output = "Invalid PipelineStage";
126         result.failed = true;
127         return result;
128       case ast::PipelineStage::kVertex:
129         profile = "vs_5_1";
130         break;
131       case ast::PipelineStage::kFragment:
132         profile = "ps_5_1";
133         break;
134       case ast::PipelineStage::kCompute:
135         profile = "cs_5_1";
136         break;
137     }
138 
139     // Match Dawn's compile flags
140     // See dawn\src\dawn_native\d3d12\RenderPipelineD3D12.cpp
141     UINT compileFlags = D3DCOMPILE_OPTIMIZATION_LEVEL0 |
142                         D3DCOMPILE_PACK_MATRIX_ROW_MAJOR |
143                         D3DCOMPILE_IEEE_STRICTNESS;
144 
145     ComPtr<ID3DBlob> compiledShader;
146     ComPtr<ID3DBlob> errors;
147     HRESULT cr = d3dCompile(source.c_str(),    // pSrcData
148                             source.length(),   // SrcDataSize
149                             nullptr,           // pSourceName
150                             nullptr,           // pDefines
151                             nullptr,           // pInclude
152                             ep.first.c_str(),  // pEntrypoint
153                             profile,           // pTarget
154                             compileFlags,      // Flags1
155                             0,                 // Flags2
156                             &compiledShader,   // ppCode
157                             &errors);          // ppErrorMsgs
158     if (FAILED(cr)) {
159       result.output = static_cast<char*>(errors->GetBufferPointer());
160       result.failed = true;
161       return result;
162     }
163   }
164 
165   FreeLibrary(fxcLib);
166 
167   if (entry_points.empty()) {
168     result.output = "No entrypoint found";
169     result.failed = true;
170     return result;
171   }
172 
173   return result;
174 }
175 #endif  // _WIN32
176 
177 }  // namespace val
178 }  // namespace tint
179