| // Copyright 2021 The Tint Authors. | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | #include "src/val/val.h" | 
 |  | 
 | #include "src/utils/io/command.h" | 
 | #include "src/utils/io/tmpfile.h" | 
 |  | 
 | #ifdef _WIN32 | 
 | #include <Windows.h> | 
 | #include <d3dcommon.h> | 
 | #include <d3dcompiler.h> | 
 |  | 
 | #include <wrl.h> | 
 | using Microsoft::WRL::ComPtr; | 
 | #endif  // _WIN32 | 
 |  | 
 | namespace tint { | 
 | namespace val { | 
 |  | 
 | Result HlslUsingDXC(const std::string& dxc_path, | 
 |                     const std::string& source, | 
 |                     const EntryPointList& entry_points) { | 
 |   Result result; | 
 |  | 
 |   auto dxc = utils::Command(dxc_path); | 
 |   if (!dxc.Found()) { | 
 |     result.output = "DXC not found at '" + std::string(dxc_path) + "'"; | 
 |     result.failed = true; | 
 |     return result; | 
 |   } | 
 |  | 
 |   utils::TmpFile file; | 
 |   file << source; | 
 |  | 
 |   for (auto ep : entry_points) { | 
 |     const char* profile = ""; | 
 |  | 
 |     switch (ep.second) { | 
 |       case ast::PipelineStage::kNone: | 
 |         result.output = "Invalid PipelineStage"; | 
 |         result.failed = true; | 
 |         return result; | 
 |       case ast::PipelineStage::kVertex: | 
 |         profile = "-T vs_6_0"; | 
 |         break; | 
 |       case ast::PipelineStage::kFragment: | 
 |         profile = "-T ps_6_0"; | 
 |         break; | 
 |       case ast::PipelineStage::kCompute: | 
 |         profile = "-T cs_6_0"; | 
 |         break; | 
 |     } | 
 |  | 
 |     auto res = dxc(profile, "-E " + ep.first, file.Path()); | 
 |     if (!res.out.empty()) { | 
 |       if (!result.output.empty()) { | 
 |         result.output += "\n"; | 
 |       } | 
 |       result.output += res.out; | 
 |     } | 
 |     if (!res.err.empty()) { | 
 |       if (!result.output.empty()) { | 
 |         result.output += "\n"; | 
 |       } | 
 |       result.output += res.err; | 
 |     } | 
 |     result.failed = (res.error_code != 0); | 
 |   } | 
 |  | 
 |   if (entry_points.empty()) { | 
 |     result.output = "No entrypoint found"; | 
 |     result.failed = true; | 
 |     return result; | 
 |   } | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | #ifdef _WIN32 | 
 | Result HlslUsingFXC(const std::string& source, | 
 |                     const EntryPointList& entry_points) { | 
 |   Result result; | 
 |  | 
 |   // This library leaks if an error happens in this function, but it is ok | 
 |   // because it is loaded at most once, and the executables using HlslUsingFXC | 
 |   // are short-lived. | 
 |   HMODULE fxcLib = LoadLibraryA("d3dcompiler_47.dll"); | 
 |   if (fxcLib == nullptr) { | 
 |     result.output = "Couldn't load FXC"; | 
 |     result.failed = true; | 
 |     return result; | 
 |   } | 
 |  | 
 |   pD3DCompile d3dCompile = | 
 |       reinterpret_cast<pD3DCompile>(GetProcAddress(fxcLib, "D3DCompile")); | 
 |   if (d3dCompile == nullptr) { | 
 |     result.output = "Couldn't load D3DCompile from FXC"; | 
 |     result.failed = true; | 
 |     return result; | 
 |   } | 
 |  | 
 |   for (auto ep : entry_points) { | 
 |     const char* profile = ""; | 
 |     switch (ep.second) { | 
 |       case ast::PipelineStage::kNone: | 
 |         result.output = "Invalid PipelineStage"; | 
 |         result.failed = true; | 
 |         return result; | 
 |       case ast::PipelineStage::kVertex: | 
 |         profile = "vs_5_1"; | 
 |         break; | 
 |       case ast::PipelineStage::kFragment: | 
 |         profile = "ps_5_1"; | 
 |         break; | 
 |       case ast::PipelineStage::kCompute: | 
 |         profile = "cs_5_1"; | 
 |         break; | 
 |     } | 
 |  | 
 |     ComPtr<ID3DBlob> compiledShader; | 
 |     ComPtr<ID3DBlob> errors; | 
 |     if (FAILED(d3dCompile(source.c_str(), source.length(), nullptr, nullptr, | 
 |                           nullptr, ep.first.c_str(), profile, 0, 0, | 
 |                           &compiledShader, &errors))) { | 
 |       result.output = static_cast<char*>(errors->GetBufferPointer()); | 
 |       result.failed = true; | 
 |       return result; | 
 |     } | 
 |   } | 
 |  | 
 |   FreeLibrary(fxcLib); | 
 |  | 
 |   if (entry_points.empty()) { | 
 |     result.output = "No entrypoint found"; | 
 |     result.failed = true; | 
 |     return result; | 
 |   } | 
 |  | 
 |   return result; | 
 | } | 
 | #endif  // _WIN32 | 
 |  | 
 | }  // namespace val | 
 | }  // namespace tint |