Skip to content

Commit d6c0f3e

Browse files
authored
Merge pull request #1051 from Devsh-Graphics-Programming/optimizer-improvement
Shader compiler options to save preprocessed and spirv, optimizer upgrade
2 parents 90734e7 + c99a3ef commit d6c0f3e

6 files changed

Lines changed: 121 additions & 5 deletions

File tree

include/nbl/asset/utils/IShaderCompiler.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,9 @@ class NBL_API2 IShaderCompiler : public core::IReferenceCounted
245245
@includeFinder Optional parameter; if not nullptr, it will resolve the includes in the code
246246
@maxSelfInclusionCount used only when includeFinder is not nullptr
247247
@extraDefines adds extra defines to the shader before compilation
248+
@optimizerIsExtraPasses Instead of entirely replacing the default optimization passes, run the provided passes after the default ones
249+
@preprocessedOutputPath If not empty, the preprocessed shader will be saved to this path
250+
@spvOutputPath If not empty, the compiled SPIR-V binary will be saved to this path
248251
*/
249252
struct SCompilerOptions
250253
{
@@ -261,6 +264,9 @@ class NBL_API2 IShaderCompiler : public core::IReferenceCounted
261264
SPreprocessorOptions preprocessorOptions = {};
262265
CCache* readCache = nullptr;
263266
CCache* writeCache = nullptr;
267+
bool optimizerIsExtraPasses = false; // Instead of disabling the default opt passes, run the provided optimization passes at the end
268+
std::string preprocessedOutputPath = "";
269+
std::string spvOutputPath = "";
264270
};
265271

266272
class CCache final : public IReferenceCounted
@@ -269,7 +275,7 @@ class NBL_API2 IShaderCompiler : public core::IReferenceCounted
269275

270276
public:
271277
// Used to check compatibility of Caches before reading
272-
constexpr static inline std::string_view VERSION = "1.1.0";
278+
constexpr static inline std::string_view VERSION = "1.1.1";
273279

274280
static auto const SHADER_BUFFER_SIZE_BYTES = sizeof(uint64_t) / sizeof(uint8_t); // It's obviously 8
275281

@@ -361,7 +367,7 @@ class NBL_API2 IShaderCompiler : public core::IReferenceCounted
361367
public:
362368
inline bool operator==(const SCompilerArgs& other) const {
363369
bool retVal = true;
364-
if (stage != other.stage || targetSpirvVersion != other.targetSpirvVersion || debugInfoFlags != other.debugInfoFlags || preprocessorArgs != other.preprocessorArgs) retVal = false;
370+
if (stage != other.stage || targetSpirvVersion != other.targetSpirvVersion || debugInfoFlags != other.debugInfoFlags || preprocessorArgs != other.preprocessorArgs || optimizerIsExtraPasses != other.optimizerIsExtraPasses) retVal = false;
365371
if (optimizerPasses.size() != other.optimizerPasses.size()) retVal = false;
366372
for (auto passesIt = optimizerPasses.begin(), otherPassesIt = other.optimizerPasses.begin(); passesIt != optimizerPasses.end(); passesIt++, otherPassesIt++) {
367373
if (*passesIt != *otherPassesIt) {
@@ -389,6 +395,7 @@ class NBL_API2 IShaderCompiler : public core::IReferenceCounted
389395
// Only SEntry should instantiate this struct
390396
SCompilerArgs(const SCompilerOptions& options)
391397
: stage(options.stage), targetSpirvVersion(options.preprocessorOptions.targetSpirvVersion), debugInfoFlags(options.debugInfoFlags), preprocessorArgs(options.preprocessorOptions)
398+
, optimizerIsExtraPasses(options.optimizerIsExtraPasses)
392399
{
393400
if (options.spirvOptimizer) {
394401
for (auto pass : options.spirvOptimizer->getPasses())
@@ -399,6 +406,7 @@ class NBL_API2 IShaderCompiler : public core::IReferenceCounted
399406
IShader::E_SHADER_STAGE stage;
400407
E_SPIRV_VERSION targetSpirvVersion;
401408
std::vector<ISPIRVOptimizer::E_OPTIMIZER_PASS> optimizerPasses;
409+
bool optimizerIsExtraPasses;
402410
core::bitflag<E_DEBUG_INFO_FLAGS> debugInfoFlags;
403411
SPreprocessorArgs preprocessorArgs;
404412
};
@@ -458,6 +466,9 @@ class NBL_API2 IShaderCompiler : public core::IReferenceCounted
458466

459467
bool setContent(const asset::ICPUBuffer* uncompressedSpirvBuffer);
460468

469+
IShader::E_SHADER_STAGE getShaderStage() const { return compilerArgs.stage; }
470+
SPreprocessorArgs getPreprocessorArgs() const { return compilerArgs.preprocessorArgs; }
471+
461472
core::smart_refctd_ptr<IShader> decompressShader() const;
462473

463474
// TODO: make some of these private

include/nbl/video/ILogicalDevice.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,9 @@ class NBL_API2 ILogicalDevice : public core::IReferenceCounted, public IDeviceMe
833833
std::span<const asset::IShaderCompiler::SMacroDefinition> extraDefines = {};
834834
hlsl::ShaderStage stage = hlsl::ShaderStage::ESS_ALL_OR_LIBRARY;
835835
core::bitflag<asset::IShaderCompiler::E_DEBUG_INFO_FLAGS> debugInfoFlags = asset::IShaderCompiler::E_DEBUG_INFO_FLAGS::EDIF_NONE;
836+
bool optimizerIsExtraPasses = false; // Instead of disabling the default opt passes, run the provided optimization passes at the end
837+
std::string preprocessedOutputPath = "";
838+
std::string spvOutputPath = "";
836839
};
837840
core::smart_refctd_ptr<asset::IShader> compileShader(const SShaderCreationParameters& creationParams);
838841

src/nbl/asset/utils/CHLSLCompiler.cpp

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,46 @@ core::smart_refctd_ptr<IShader> CHLSLCompiler::compileToSPIRV_impl(const std::st
546546
auto newCode = preprocessShader(std::string(code), stage, hlslOptions.preprocessorOptions, dxc_compile_flags, dependencies);
547547
if (newCode.empty()) return nullptr;
548548

549+
auto saveToFile = [&](const std::string& filePath, const void* buffer, size_t size, const char* loggerName)
550+
{
551+
core::smart_refctd_ptr<system::IFile> saveFile;
552+
553+
system::ISystem::future_t<core::smart_refctd_ptr<system::IFile>> future;
554+
// Ensure it doesn't exist
555+
m_system->deleteFile(filePath + "_temp");
556+
m_system->createFile(future, filePath + "_temp", system::IFile::ECF_WRITE);
557+
if (future.wait())
558+
{
559+
future.acquire().move_into(saveFile);
560+
if (saveFile)
561+
{
562+
system::IFile::success_t succ;
563+
saveFile->write(succ, buffer, 0, size);
564+
if (!succ)
565+
{
566+
logger.log(std::string("Failed Writing To Temp ") + loggerName + " File.", nbl::system::ILogger::ELL_ERROR);
567+
return;
568+
}
569+
saveFile = nullptr; // drop handle first
570+
m_system->deleteFile(filePath); // safe to delete + rename
571+
auto renameResult = m_system->moveFileOrDirectory(filePath + "_temp", filePath);
572+
if (renameResult)
573+
{
574+
logger.log(std::string("Failed Saving ") + loggerName + " File. Check it's not open.", nbl::system::ILogger::ELL_ERROR);
575+
// Clean up
576+
m_system->deleteFile(filePath + "_temp");
577+
}
578+
}
579+
else
580+
logger.log(std::string("Failed Creating ") + loggerName + " File.", nbl::system::ILogger::ELL_ERROR);
581+
}
582+
else
583+
logger.log(std::string("Failed Creating ") + loggerName + " File.", nbl::system::ILogger::ELL_ERROR);
584+
};
585+
586+
if (!options.preprocessedOutputPath.empty())
587+
saveToFile(options.preprocessedOutputPath, newCode.data(), newCode.size(), "Preprocessed Output");
588+
549589
// Suffix is the shader model version
550590
std::wstring targetProfile(SHADER_MODEL_PROFILE);
551591

@@ -584,13 +624,13 @@ core::smart_refctd_ptr<IShader> CHLSLCompiler::compileToSPIRV_impl(const std::st
584624
// TODO: add entry point to `CHLSLCompiler::SOptions` and handle it properly in `dxc_compile_flags.empty()`
585625
arguments.push_back(L"main");
586626
}
587-
// If a custom SPIR-V optimizer is specified, use that instead of DXC's spirv-opt.
627+
// If a custom SPIR-V optimizer is specified and set to replace default optimization passes, use that instead of DXC's spirv-opt.
588628
// This is how we can get more optimizer options.
589629
//
590630
// Optimization is also delegated to SPIRV-Tools. Right now there are no difference between
591631
// optimization levels greater than zero; they will all invoke the same optimization recipe.
592632
// https://github.com/Microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#optimization
593-
if (hlslOptions.spirvOptimizer)
633+
if (hlslOptions.spirvOptimizer && !hlslOptions.optimizerIsExtraPasses)
594634
arguments.push_back(L"-O0");
595635
}
596636
//
@@ -651,6 +691,9 @@ core::smart_refctd_ptr<IShader> CHLSLCompiler::compileToSPIRV_impl(const std::st
651691
if (hlslOptions.spirvOptimizer)
652692
outSpirv = hlslOptions.spirvOptimizer->optimize(outSpirv.get(), logger);
653693

694+
if (outSpirv && !options.spvOutputPath.empty())
695+
saveToFile(options.spvOutputPath, outSpirv->getPointer(), outSpirv->getSize(), "SPIR-V Output");
696+
654697
return core::make_smart_refctd_ptr<asset::IShader>(std::move(outSpirv), IShader::E_CONTENT_TYPE::ECT_SPIRV, hlslOptions.preprocessorOptions.sourceIdentifier.data());
655698
}
656699

src/nbl/asset/utils/IShaderCompiler.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ namespace nbl::system::json {
111111
{ "shaderStage", shaderStage },
112112
{ "spirvVersion", spirvVersion },
113113
{ "optimizerPasses", p.optimizerPasses },
114+
{ "optimizerIsExtraPasses", p.optimizerIsExtraPasses },
114115
{ "debugFlags", debugFlags },
115116
{ "preprocessorArgs", p.preprocessorArgs },
116117
};
@@ -122,6 +123,7 @@ namespace nbl::system::json {
122123
j.at("shaderStage").get_to(shaderStage);
123124
j.at("spirvVersion").get_to(spirvVersion);
124125
j.at("optimizerPasses").get_to(p.optimizerPasses);
126+
j.at("optimizerIsExtraPasses").get_to(p.optimizerIsExtraPasses);
125127
j.at("debugFlags").get_to(debugFlags);
126128
j.at("preprocessorArgs").get_to(p.preprocessorArgs);
127129
p.stage = static_cast<IShader::E_SHADER_STAGE>(shaderStage);
@@ -502,6 +504,43 @@ core::smart_refctd_ptr<IShader> nbl::asset::IShaderCompiler::compileToSPIRV(cons
502504
return IShaderCompiler::writeDepfile(params, dependencies, options.preprocessorOptions.includeFinder, options.preprocessorOptions.logger);
503505
};
504506

507+
auto saveToFile = [&](const std::string& filePath, const void* buffer, size_t size, const char* loggerName)
508+
{
509+
core::smart_refctd_ptr<system::IFile> saveFile;
510+
511+
system::ISystem::future_t<core::smart_refctd_ptr<system::IFile>> future;
512+
// Ensure it doesn't exist
513+
m_system->deleteFile(filePath + "_temp");
514+
m_system->createFile(future, filePath + "_temp", system::IFile::ECF_WRITE);
515+
if (future.wait())
516+
{
517+
future.acquire().move_into(saveFile);
518+
if (saveFile)
519+
{
520+
system::IFile::success_t succ;
521+
saveFile->write(succ, buffer, 0, size);
522+
if (!succ)
523+
{
524+
options.preprocessorOptions.logger.log(std::string("Failed Writing To Temp ") + loggerName + " File.", nbl::system::ILogger::ELL_ERROR);
525+
return;
526+
}
527+
saveFile = nullptr; // drop handle first
528+
m_system->deleteFile(filePath);
529+
auto renameResult = m_system->moveFileOrDirectory(filePath + "_temp", filePath);
530+
if (renameResult)
531+
{
532+
options.preprocessorOptions.logger.log(std::string("Failed Saving ") + loggerName + " File. Check it's not open.", nbl::system::ILogger::ELL_ERROR);
533+
// Clean up
534+
m_system->deleteFile(filePath + "_temp");
535+
}
536+
}
537+
else
538+
options.preprocessorOptions.logger.log(std::string("Failed Creating ") + loggerName + " File.", nbl::system::ILogger::ELL_ERROR);
539+
}
540+
else
541+
options.preprocessorOptions.logger.log(std::string("Failed Creating ") + loggerName + " File.", nbl::system::ILogger::ELL_ERROR);
542+
};
543+
505544
CCache::SEntry entry;
506545
if (options.readCache || options.writeCache)
507546
entry = CCache::SEntry(code, options);
@@ -519,6 +558,22 @@ core::smart_refctd_ptr<IShader> nbl::asset::IShaderCompiler::compileToSPIRV(cons
519558
auto shader = found->decompressShader();
520559
if (depfileEnabled && !writeDepfileFromDependencies(found->dependencies))
521560
return nullptr;
561+
if (!options.spvOutputPath.empty())
562+
saveToFile(options.spvOutputPath, shader->getContent()->getPointer(), shader->getContent()->getSize(), "SPIR-V");
563+
// Entry doesn't store preprocessed shader, so preprocess it
564+
if (!options.preprocessedOutputPath.empty())
565+
{
566+
// copy shader contents
567+
std::string shaderContents = found->mainFileContents;
568+
auto stage = options.stage;
569+
// Create a copy, cache hit means dependencies haven't changed but there is no function that takes a const pointer. This is meant for debug anyway.
570+
auto deps = found->dependencies;
571+
auto preprocessedShader = preprocessShader(std::move(shaderContents), stage, options.preprocessorOptions, &deps);
572+
if (preprocessedShader.empty())
573+
options.preprocessorOptions.logger.log("Failed to preprocess shader for output.", nbl::system::ILogger::ELL_ERROR);
574+
else
575+
saveToFile(options.preprocessedOutputPath, preprocessedShader.data(), preprocessedShader.size(), "Preprocessed Shader");
576+
}
522577
return shader;
523578
}
524579
}

src/nbl/system/ISystem.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ bool ISystem::deleteDirectory(const system::path& p)
125125
bool nbl::system::ISystem::deleteFile(const system::path& p)
126126
{
127127
if (std::filesystem::exists(p) && !std::filesystem::is_directory(p))
128-
return std::filesystem::remove(p);
128+
return std::filesystem::remove(p);
129129
else
130130
return false;
131131
}

src/nbl/video/ILogicalDevice.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,11 +362,15 @@ core::smart_refctd_ptr<asset::IShader> ILogicalDevice::compileShader(const SShad
362362
commonCompileOptions.stage = creationParams.stage;
363363
commonCompileOptions.debugInfoFlags = creationParams.debugInfoFlags;
364364
commonCompileOptions.spirvOptimizer = creationParams.optimizer;
365+
commonCompileOptions.optimizerIsExtraPasses = creationParams.optimizerIsExtraPasses;
365366
commonCompileOptions.preprocessorOptions.targetSpirvVersion = m_physicalDevice->getLimits().spirvVersion;
366367

367368
commonCompileOptions.readCache = creationParams.readCache;
368369
commonCompileOptions.writeCache = creationParams.writeCache;
369370

371+
commonCompileOptions.preprocessedOutputPath = creationParams.preprocessedOutputPath;
372+
commonCompileOptions.spvOutputPath = creationParams.spvOutputPath;
373+
370374
if (sourceContent==asset::IShader::E_CONTENT_TYPE::ECT_HLSL)
371375
{
372376
// TODO: add specific HLSLCompiler::SOption params

0 commit comments

Comments
 (0)