blob: d323b4d07d0ad03189572db3f1d3f1381e44ffec [file] [log] [blame]
// Copyright 2019 The Amber 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/dxc_helper.h"
#include <algorithm>
#include <sstream>
#include "src/platform.h"
#if AMBER_PLATFORM_WINDOWS
#pragma warning(push)
#pragma warning(disable : 4267)
#pragma warning(disable : 4003)
#endif // AMBER_PLATFORM_WINDOWS
// clang-format off
// The order here matters, so don't reformat.
#include "dxc/Support/WinAdapter.h"
#include "dxc/Support/WinIncludes.h"
#include "dxc/Support/Global.h"
#include "dxc/Support/HLSLOptions.h"
#include "dxc/Support/dxcapi.use.h"
#include "dxc/dxcapi.h"
// clang-format on
#if AMBER_PLATFORM_WINDOWS
#pragma warning(pop)
#endif // AMBER_PLATFORM_WINDOWS
namespace amber {
namespace dxchelper {
namespace {
const wchar_t* kDxcFlags[] = {
L"-spirv", // SPIR-V compilation
L"-fcgl", // No SPIR-V Optimization
L"-enable-16bit-types", // Enabling 16bit types
};
const size_t kDxcFlagsCount = sizeof(kDxcFlags) / sizeof(const wchar_t*);
// Converts an IDxcBlob into a vector of 32-bit unsigned integers which
// is returned via the 'binaryWords' argument.
void ConvertIDxcBlobToUint32(IDxcBlob* blob,
std::vector<uint32_t>* binaryWords) {
size_t num32BitWords = (blob->GetBufferSize() + 3) / 4;
std::string binaryStr(static_cast<char*>(blob->GetBufferPointer()),
blob->GetBufferSize());
binaryStr.resize(num32BitWords * 4, 0);
binaryWords->resize(num32BitWords, 0);
memcpy(binaryWords->data(), binaryStr.data(), binaryStr.size());
}
} // namespace
Result Compile(const std::string& src,
const std::string& entry,
const std::string& profile,
const std::string& spv_env,
std::vector<uint32_t>* generated_binary) {
DxcInitThreadMalloc();
if (hlsl::options::initHlslOptTable()) {
DxcCleanupThreadMalloc();
return Result("DXC compile failure: initHlslOptTable");
}
IDxcLibrary* dxc_lib;
if (DxcCreateInstance(CLSID_DxcLibrary, __uuidof(IDxcLibrary),
reinterpret_cast<void**>(&dxc_lib)) < 0) {
DxcCleanupThreadMalloc();
return Result("DXCCreateInstance for DXCLibrary failed");
}
IDxcBlobEncoding* source;
if (dxc_lib->CreateBlobWithEncodingOnHeapCopy(
src.data(), static_cast<uint32_t>(src.size()), CP_UTF8, &source) <
0) {
DxcCleanupThreadMalloc();
return Result("DXC compile failure: CreateBlobFromFile");
}
IDxcIncludeHandler* include_handler;
if (dxc_lib->CreateIncludeHandler(&include_handler) < 0) {
DxcCleanupThreadMalloc();
return Result("DXC compile failure: CreateIncludeHandler");
}
IDxcCompiler* compiler;
if (DxcCreateInstance(CLSID_DxcCompiler, __uuidof(IDxcCompiler),
reinterpret_cast<void**>(&compiler)) < 0) {
DxcCleanupThreadMalloc();
return Result("DXCCreateInstance for DXCCompiler failed");
}
IDxcOperationResult* result;
std::wstring src_filename =
L"amber." + std::wstring(profile.begin(), profile.end());
std::vector<const wchar_t*> dxc_flags(kDxcFlags, &kDxcFlags[kDxcFlagsCount]);
const wchar_t* target_env = nullptr;
if (!spv_env.compare("spv1.3") || !spv_env.compare("vulkan1.1")) {
target_env = L"-fspv-target-env=vulkan1.1";
} else if (!spv_env.compare("spv1.0") || !spv_env.compare("vulkan1.0")) {
target_env = L"-fspv-target-env=vulkan1.0";
} else if (!spv_env.empty()) {
return Result(
"Invalid target environment. Choose spv1.3 or vulkan1.1 for vulkan1.1 "
"and spv1.0 or vulkan1.0 for vulkan1.0.");
}
if (target_env)
dxc_flags.push_back(target_env);
if (compiler->Compile(source, /* source text */
src_filename.c_str(), /* original file source */
std::wstring(entry.begin(), entry.end())
.c_str(), /* entry point name */
std::wstring(profile.begin(), profile.end())
.c_str(), /* shader profile to compile */
dxc_flags.data(), /* arguments */
dxc_flags.size(), /* argument count */
nullptr, /* defines */
0, /* define count */
include_handler, /* handler for #include */
&result /* output status */) < 0) {
DxcCleanupThreadMalloc();
return Result("DXC compile failure: Compile");
}
// Compilation is done. We can clean up the HlslOptTable.
hlsl::options::cleanupHlslOptTable();
// Get compilation results.
HRESULT result_status;
if (result->GetStatus(&result_status) < 0) {
DxcCleanupThreadMalloc();
return Result("DXC compile failure: GetStatus");
}
// Get diagnostics string.
IDxcBlobEncoding* error_buffer;
if (result->GetErrorBuffer(&error_buffer)) {
DxcCleanupThreadMalloc();
return Result("DXC compile failure: GetErrorBuffer");
}
const std::string diagnostics(
static_cast<char*>(error_buffer->GetBufferPointer()),
error_buffer->GetBufferSize());
bool success = true;
if (SUCCEEDED(result_status)) {
IDxcBlob* compiled_blob;
if (result->GetResult(&compiled_blob) < 0) {
DxcCleanupThreadMalloc();
return Result("DXC compile failure: GetResult");
}
ConvertIDxcBlobToUint32(compiled_blob, generated_binary);
} else {
success = false;
}
DxcCleanupThreadMalloc();
return success ? Result() : Result("DXC compile failure: " + diagnostics);
}
} // namespace dxchelper
} // namespace amber