Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimise shaders #1381

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ else()
set(FIND_DEPENDENCY_glslang "")
endif()

if (VSG_SUPPORTS_ShaderCompiler)
option(VSG_SUPPORTS_ShaderOptimizer "Optional shader optimizer support" ON)
if (VSG_SUPPORTS_ShaderOptimizer)
find_package(SPIRV-Tools-opt CONFIG)
if (NOT SPIRV-Tools-opt_FOUND)
set(VSG_SUPPORTS_ShaderOptimizer OFF)
endif()
endif()
endif()

set(VSG_SUPPORTS_Windowing 1 CACHE STRING "Optional native windowing support providing a default implementation of vsg::Window::create(), 0 for off, 1 for enabled." )
if (VSG_SUPPORTS_Windowing)
if (ANDROID)
Expand Down
1 change: 1 addition & 0 deletions include/vsg/state/ShaderModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ namespace vsg
SpirvTarget target = SPIRV_1_0;
bool forwardCompatible = false;
bool generateDebugInfo = false; // maps to SpvOptions::generateDebugInfo
bool optimize = true;

std::set<std::string> defines;

Expand Down
10 changes: 10 additions & 0 deletions include/vsg/utils/ShaderCompiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
#include <vsg/io/Options.h>
#include <vsg/state/ShaderStage.h>

#if VSG_SUPPORTS_ShaderOptimizer
namespace spvtools
{
class Optimizer;
}
#endif

namespace vsg
{

Expand All @@ -24,6 +31,9 @@ namespace vsg

// default ShaderCompileSettings
ref_ptr<ShaderCompileSettings> defaults;
#if VSG_SUPPORTS_ShaderOptimizer
std::unique_ptr<spvtools::Optimizer> optimizer;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reason to make the optimizer a public member of ShaderCompiler? Are you expecting users to want to control the spvtools::Optimizer settings or invoke it themselves?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Someone might want to do that, but it probably wouldn't work as things stand as the optimiser instance clears its own state as it uses it, so anything someone did with it would only apply to the next shader, and the settings from ShaderCompileSettings would override anything else.

It's really just public because the VSG approach is to make most things public, and I didn't see any potential disasters from making this public, too.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current ShaderCompiler completely encapsulates and hides the glslang implementation details, this approach breaks that so feels awkward.

Is there any reason not create the spvtools::Optimizer object one demand like the program objects are? Or make it a static object local to the ShaderCompiler.cpp?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's reused to avoid having to reallocate it and originally set it up for each shader, although it turned out that it needed to be set up again for each shader anyway, so that saving isn't likely to be significant. It should be fine to create it again each time it's used in ShaderCompiler::compile. It's probably a bad idea to make it a static object as it's certainly not thread-safe and compiling different shaders on different threads seems like something someone might want to do.

#endif

bool compile(ShaderStages& shaders, const std::vector<std::string>& defines = {}, ref_ptr<const Options> options = {});
bool compile(ref_ptr<ShaderStage> shaderStage, const std::vector<std::string>& defines = {}, ref_ptr<const Options> options = {});
Expand Down
4 changes: 4 additions & 0 deletions src/vsg/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ if (${VSG_SUPPORTS_ShaderCompiler})
list(INSERT LIBRARIES 0 PRIVATE glslang::glslang glslang::glslang-default-resource-limits glslang::SPIRV)
endif()

if (VSG_SUPPORTS_ShaderOptimizer)
list(INSERT LIBRARIES 0 PRIVATE SPIRV-Tools-opt)
endif()

# Check for std::atomic
if(NOT MSVC AND NOT ANDROID AND NOT APPLE)
include(CheckCXXSourceCompiles)
Expand Down
3 changes: 3 additions & 0 deletions src/vsg/core/Version.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ extern "C"
/// vsg::ShaderCompiler support enabled when 1, disabled when 0
#define VSG_SUPPORTS_ShaderCompiler @VSG_SUPPORTS_ShaderCompiler@

/// vsg::ShaderCompiler optimizer support enabled when 1, disabled when 0
#cmakedefine01 VSG_SUPPORTS_ShaderOptimizer

/// Native Windowing support provided with vsg::Window::create(windowTraits) enabled when 1, disabled when 0
#define VSG_SUPPORTS_Windowing @VSG_SUPPORTS_Windowing@

Expand Down
11 changes: 11 additions & 0 deletions src/vsg/state/ShaderModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ int ShaderCompileSettings::compare(const Object& rhs_object) const
if ((result = compare_value(target, rhs.target))) return result;
if ((result = compare_value(forwardCompatible, rhs.forwardCompatible))) return result;
if ((result = compare_value(generateDebugInfo, rhs.generateDebugInfo))) return result;
if ((result = compare_value(optimize, rhs.optimize))) return result;
return compare_container(defines, rhs.defines);
}

Expand All @@ -53,6 +54,11 @@ void ShaderCompileSettings::read(Input& input)
input.read("generateDebugInfo", generateDebugInfo);
}

if (input.version_greater_equal(1, 1, 10))
{
input.read("optimize", optimize);
}

input.readValues("defines", defines);
}

Expand All @@ -70,6 +76,11 @@ void ShaderCompileSettings::write(Output& output) const
output.write("generateDebugInfo", generateDebugInfo);
}

if (output.version_greater_equal(1, 1, 10))
{
output.write("optimize", optimize);
}

output.writeValues("defines", defines);
}

Expand Down
1,990 changes: 907 additions & 1,083 deletions src/vsg/text/shaders/text_ShaderSet.cpp

Large diffs are not rendered by default.

108 changes: 97 additions & 11 deletions src/vsg/utils/ShaderCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
# include <glslang/SPIRV/GlslangToSpv.h>
#endif

#if VSG_SUPPORTS_ShaderOptimizer
# include <spirv-tools/optimizer.hpp>
#endif

#include <algorithm>
#include <iomanip>

Expand All @@ -35,27 +39,62 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI

using namespace vsg;

namespace
{
#if VSG_SUPPORTS_ShaderCompiler
static std::atomic_uint s_initialized = 0;
static std::atomic_uint s_initialized = 0;

static void s_initializeProcess()
{
if (s_initialized.fetch_add(1) == 0)
static void s_initializeProcess()
{
glslang::InitializeProcess();
if (s_initialized.fetch_add(1) == 0)
{
glslang::InitializeProcess();
}
}
}

static void s_finalizeProcess()
{
if (s_initialized.fetch_sub(1) == 1)
static void s_finalizeProcess()
{
glslang::FinalizeProcess();
if (s_initialized.fetch_sub(1) == 1)
{
glslang::FinalizeProcess();
}
}
}

#endif

#if VSG_SUPPORTS_ShaderOptimizer
constexpr static spv_target_env selectTargetEnv(const ShaderCompileSettings& settings)
{
switch (settings.vulkanVersion)
{
case VK_MAKE_API_VERSION(0, 1, 0, 0):
return SPV_ENV_VULKAN_1_0;
case VK_MAKE_API_VERSION(0, 1, 1, 0):
switch (settings.target)
{
case ShaderCompileSettings::SPIRV_1_0:
case ShaderCompileSettings::SPIRV_1_1:
case ShaderCompileSettings::SPIRV_1_2:
case ShaderCompileSettings::SPIRV_1_3:
return SPV_ENV_VULKAN_1_1;
case ShaderCompileSettings::SPIRV_1_4:
return SPV_ENV_VULKAN_1_1_SPIRV_1_4;
default:
return SPV_ENV_VULKAN_1_1;
}
case VK_MAKE_API_VERSION(0, 1, 2, 0):
return SPV_ENV_VULKAN_1_2;
case VK_MAKE_API_VERSION(0, 1, 3, 0):
return SPV_ENV_VULKAN_1_3;
//case VK_MAKE_API_VERSION(0, 1, 4, 0):
// return SPV_ENV_VULKAN_1_4;
default:
return SPV_ENV_UNIVERSAL_1_0;
}
}
#endif
}

std::string debugFormatShaderSource(const std::string& source)
{
std::istringstream iss(source);
Expand All @@ -74,7 +113,39 @@ std::string debugFormatShaderSource(const std::string& source)
ShaderCompiler::ShaderCompiler() :
Inherit(),
defaults(ShaderCompileSettings::create())
#if VSG_SUPPORTS_ShaderOptimizer
, optimizer(std::make_unique<spvtools::Optimizer>(selectTargetEnv(*defaults)))
#endif
{
#if VSG_SUPPORTS_ShaderOptimizer
optimizer->SetMessageConsumer([](spv_message_level_t level, const char* source,
const spv_position_t& position, const char* message){
Logger::Level vsgLevel;
switch (level)
{
case SPV_MSG_FATAL:
case SPV_MSG_INTERNAL_ERROR:
vsgLevel = Logger::LOGGER_FATAL;
break;
case SPV_MSG_ERROR:
vsgLevel = Logger::LOGGER_ERROR;
break;
case SPV_MSG_WARNING:
vsgLevel = Logger::LOGGER_WARN;
break;
case SPV_MSG_INFO:
vsgLevel = Logger::LOGGER_INFO;
break;
case SPV_MSG_DEBUG:
vsgLevel = Logger::LOGGER_DEBUG;
break;
default:
vsgLevel = Logger::LOGGER_INFO;
break;
}
vsg::log(vsgLevel, "spvtools::Optimizer: ", source ? source : "", ", ", position.line, ", ", position.column, ", ", position.index, ", ", message ? message : "No message");
});
#endif
}

ShaderCompiler::~ShaderCompiler()
Expand Down Expand Up @@ -284,6 +355,21 @@ bool ShaderCompiler::compile(ShaderStages& shaders, const std::vector<std::strin

vsg_shader->module->code.clear();
glslang::GlslangToSpv(*(program->getIntermediate((EShLanguage)eshl_stage)), vsg_shader->module->code, &logger, &spvOptions);

#if VSG_SUPPORTS_ShaderOptimizer
if (settings && optimizer && settings->optimize)
{
optimizer->SetTargetEnv(selectTargetEnv(*settings));
optimizer->RegisterPerformancePasses(true);
vsg::ShaderModule::SPIRV unoptimized(std::move(vsg_shader->module->code));
bool success = optimizer->Run(unoptimized.data(), unoptimized.size(), &vsg_shader->module->code);
if (!success)
{
warn("Shader optimisation failed, reverting to unoptimised.");
vsg_shader->module->code = std::move(unoptimized);
}
}
#endif
}
}

Expand Down
Loading
Loading