diff --git a/Graphics/GraphicsEngine/include/DeviceObjectArchive.hpp b/Graphics/GraphicsEngine/include/DeviceObjectArchive.hpp index 73f145964d..2bc8773c7e 100644 --- a/Graphics/GraphicsEngine/include/DeviceObjectArchive.hpp +++ b/Graphics/GraphicsEngine/include/DeviceObjectArchive.hpp @@ -128,7 +128,7 @@ class DeviceObjectArchive }; static constexpr Uint32 HeaderMagicNumber = 0xDE00000A; - static constexpr Uint32 ArchiveVersion = 9; + static constexpr Uint32 ArchiveVersion = 10; struct ArchiveHeader { diff --git a/Graphics/GraphicsEngine/interface/APIInfo.h b/Graphics/GraphicsEngine/interface/APIInfo.h index 4731f7d9af..dd74c7ea7b 100644 --- a/Graphics/GraphicsEngine/interface/APIInfo.h +++ b/Graphics/GraphicsEngine/interface/APIInfo.h @@ -30,7 +30,7 @@ /// \file /// Diligent API information -#define DILIGENT_API_VERSION 256017 +#define DILIGENT_API_VERSION 256018 #include "../../../Primitives/interface/BasicTypes.h" diff --git a/Graphics/GraphicsEngine/interface/Shader.h b/Graphics/GraphicsEngine/interface/Shader.h index 7909272f6f..f035c0d2a9 100644 --- a/Graphics/GraphicsEngine/interface/Shader.h +++ b/Graphics/GraphicsEngine/interface/Shader.h @@ -1,5 +1,5 @@ /* - * Copyright 2019-2025 Diligent Graphics LLC + * Copyright 2019-2026 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -419,6 +419,35 @@ DILIGENT_TYPED_ENUM(SHADER_COMPILE_FLAGS, Uint32) }; DEFINE_FLAG_ENUM_OPERATORS(SHADER_COMPILE_FLAGS); + +/// Describes the shader optimization level. +DILIGENT_TYPED_ENUM(SHADER_OPTIMIZATION_LEVEL, Uint8) +{ + /// Default optimization level. + + /// Each backend uses its historical default optimization behavior. + SHADER_OPTIMIZATION_LEVEL_DEFAULT = 0, + + /// Optimization is explicitly disabled regardless of build configuration + /// (DXC `-Od`, FXC `D3DCOMPILE_SKIP_OPTIMIZATION`, no SPIR-V performance pass). + SHADER_OPTIMIZATION_LEVEL_DISABLED, + + /// Optimization level 0 (DXC `-O0`, FXC optimization level 0, SPIR-V performance pass). + SHADER_OPTIMIZATION_LEVEL_0, + + /// Optimization level 1 (DXC `-O1`, FXC optimization level 1, SPIR-V performance pass). + SHADER_OPTIMIZATION_LEVEL_1, + + /// Optimization level 2 (DXC `-O2`, FXC optimization level 2, SPIR-V performance pass). + SHADER_OPTIMIZATION_LEVEL_2, + + /// Optimization level 3 (DXC `-O3`, FXC optimization level 3, SPIR-V performance pass). + SHADER_OPTIMIZATION_LEVEL_3, + + SHADER_OPTIMIZATION_LEVEL_COUNT +}; + + // clang-format on @@ -525,6 +554,9 @@ struct ShaderCreateInfo /// Shader compile flags (see Diligent::SHADER_COMPILE_FLAGS). SHADER_COMPILE_FLAGS CompileFlags DEFAULT_INITIALIZER(SHADER_COMPILE_FLAG_NONE); + /// Shader optimization level. See Diligent::SHADER_OPTIMIZATION_LEVEL. + SHADER_OPTIMIZATION_LEVEL ShaderOptimizationLevel DEFAULT_INITIALIZER(SHADER_OPTIMIZATION_LEVEL_DEFAULT); + /// Whether to load constant buffer reflection information. /// The reflection information can be queried through @@ -675,6 +707,9 @@ struct ShaderCreateInfo if (CI1.CompileFlags != CI2.CompileFlags) return false; + if (CI1.ShaderOptimizationLevel != CI2.ShaderOptimizationLevel) + return false; + if (CI1.LoadConstantBufferReflection != CI2.LoadConstantBufferReflection) return false; diff --git a/Graphics/GraphicsEngine/src/PSOSerializer.cpp b/Graphics/GraphicsEngine/src/PSOSerializer.cpp index 66e3206a8c..1118070354 100644 --- a/Graphics/GraphicsEngine/src/PSOSerializer.cpp +++ b/Graphics/GraphicsEngine/src/PSOSerializer.cpp @@ -520,6 +520,7 @@ bool ShaderSerializer::SerializeCI(Serializer& Ser, CI.GLESSLVersion, CI.MSLVersion, CI.CompileFlags, + CI.ShaderOptimizationLevel, CI.LoadConstantBufferReflection, CI.GLSLExtensions, CI.WebGPUEmulatedArrayIndexSuffix)) diff --git a/Graphics/GraphicsEngineD3DBase/src/ShaderD3DBase.cpp b/Graphics/GraphicsEngineD3DBase/src/ShaderD3DBase.cpp index 873b62e59e..c6348d9c4e 100644 --- a/Graphics/GraphicsEngineD3DBase/src/ShaderD3DBase.cpp +++ b/Graphics/GraphicsEngineD3DBase/src/ShaderD3DBase.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2025 Diligent Graphics LLC + * Copyright 2019-2026 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -122,6 +122,19 @@ HRESULT CompileShader(const char* Source, if (ShaderCI.CompileFlags & SHADER_COMPILE_FLAG_PACK_MATRIX_ROW_MAJOR) dwShaderFlags |= D3DCOMPILE_PACK_MATRIX_ROW_MAJOR; + switch (ShaderCI.ShaderOptimizationLevel) + { + case SHADER_OPTIMIZATION_LEVEL_DISABLED: dwShaderFlags |= D3DCOMPILE_SKIP_OPTIMIZATION; break; + case SHADER_OPTIMIZATION_LEVEL_0: dwShaderFlags |= D3DCOMPILE_OPTIMIZATION_LEVEL0; break; + case SHADER_OPTIMIZATION_LEVEL_1: dwShaderFlags |= D3DCOMPILE_OPTIMIZATION_LEVEL1; break; + case SHADER_OPTIMIZATION_LEVEL_2: dwShaderFlags |= D3DCOMPILE_OPTIMIZATION_LEVEL2; break; + case SHADER_OPTIMIZATION_LEVEL_3: dwShaderFlags |= D3DCOMPILE_OPTIMIZATION_LEVEL3; break; + case SHADER_OPTIMIZATION_LEVEL_DEFAULT: + default: + // DEFAULT: no explicit optimization flag (FXC uses its own default level), preserving historical behavior. + break; + } + D3D_SHADER_MACRO Macros[] = {{"D3DCOMPILER", ""}, {}}; D3DIncludeImpl IncludeImpl{ShaderCI.pShaderSourceStreamFactory}; diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp index 3ba50c7874..6973a8df73 100644 --- a/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/ShaderVkImpl.cpp @@ -146,6 +146,7 @@ std::vector CompileShaderGLSLang(const ShaderCreateInfo& Shade Attribs.UseRowMajorMatrices = (ShaderCI.CompileFlags & SHADER_COMPILE_FLAG_PACK_MATRIX_ROW_MAJOR) != 0; Attribs.pShaderSourceStreamFactory = ShaderCI.pShaderSourceStreamFactory; Attribs.ppCompilerOutput = VkShaderCI.ppCompilerOutput; + Attribs.OptimizationLevel = ShaderCI.ShaderOptimizationLevel; if (VkShaderCI.VkVersion >= VK_API_VERSION_1_2) Attribs.Version = GLSLangUtils::SpirvVersion::Vk120; diff --git a/Graphics/GraphicsTools/src/XXH128Hasher.cpp b/Graphics/GraphicsTools/src/XXH128Hasher.cpp index 8c9f87ff18..f02ab29dc4 100644 --- a/Graphics/GraphicsTools/src/XXH128Hasher.cpp +++ b/Graphics/GraphicsTools/src/XXH128Hasher.cpp @@ -75,6 +75,7 @@ XXH128State& XXH128State::Update(const ShaderCreateInfo& ShaderCI) noexcept ShaderCI.GLESSLVersion, ShaderCI.MSLVersion, ShaderCI.CompileFlags, + ShaderCI.ShaderOptimizationLevel, ShaderCI.LoadConstantBufferReflection); if (ShaderCI.Source != nullptr || ShaderCI.FilePath != nullptr) diff --git a/Graphics/ShaderTools/include/GLSLangUtils.hpp b/Graphics/ShaderTools/include/GLSLangUtils.hpp index b832bb6cff..4f5afc00be 100644 --- a/Graphics/ShaderTools/include/GLSLangUtils.hpp +++ b/Graphics/ShaderTools/include/GLSLangUtils.hpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 Diligent Graphics LLC + * Copyright 2019-2026 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -64,6 +64,7 @@ struct GLSLtoSPIRVAttribs IDataBlob** ppCompilerOutput = nullptr; bool AssignBindings = true; bool UseRowMajorMatrices = false; + SHADER_OPTIMIZATION_LEVEL OptimizationLevel = SHADER_OPTIMIZATION_LEVEL_DEFAULT; }; std::vector GLSLtoSPIRV(const GLSLtoSPIRVAttribs& Attribs); diff --git a/Graphics/ShaderTools/src/DXCompiler.cpp b/Graphics/ShaderTools/src/DXCompiler.cpp index e8768e2641..6a3bf8f7fd 100644 --- a/Graphics/ShaderTools/src/DXCompiler.cpp +++ b/Graphics/ShaderTools/src/DXCompiler.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2025 Diligent Graphics LLC + * Copyright 2019-2026 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -691,6 +691,37 @@ void DXCompilerImpl::GetD3D12ShaderReflection(IDxcBlob* pShaderBy } +static const wchar_t* GetDxcOptimizationArg(SHADER_OPTIMIZATION_LEVEL OptimizationLevel, + DXCompilerTarget Target, + const Version& DxcVersion) +{ + switch (OptimizationLevel) + { + case SHADER_OPTIMIZATION_LEVEL_DISABLED: + return DXC_ARG_SKIP_OPTIMIZATIONS; + case SHADER_OPTIMIZATION_LEVEL_0: + return DXC_ARG_OPTIMIZATION_LEVEL0; + case SHADER_OPTIMIZATION_LEVEL_1: + return DXC_ARG_OPTIMIZATION_LEVEL1; + case SHADER_OPTIMIZATION_LEVEL_2: + return DXC_ARG_OPTIMIZATION_LEVEL2; + case SHADER_OPTIMIZATION_LEVEL_3: + return DXC_ARG_OPTIMIZATION_LEVEL3; + case SHADER_OPTIMIZATION_LEVEL_DEFAULT: + default: + // DEFAULT reproduces the historical behavior. +#ifdef DILIGENT_DEBUG + return DXC_ARG_SKIP_OPTIMIZATIONS; +#else + // For the Direct3D12 target, optimization was historically only enabled for DXC 1.5+. + if (Target == DXCompilerTarget::Direct3D12 && DxcVersion < Version{1, 5}) + return DXC_ARG_SKIP_OPTIMIZATIONS; + return DXC_ARG_OPTIMIZATION_LEVEL3; +#endif + } +} + + void DXCompilerImpl::Compile(const ShaderCreateInfo& ShaderCI, ShaderVersion ShaderModel, const char* Preamble, @@ -733,20 +764,15 @@ void DXCompilerImpl::Compile(const ShaderCreateInfo& ShaderCI, { //DxilArgs.push_back(L"-WX"); // Warnings as errors #ifdef DILIGENT_DEBUG - DxilArgs.push_back(DXC_ARG_DEBUG); // Debug info - DxilArgs.push_back(DXC_ARG_SKIP_OPTIMIZATIONS); // Disable optimization + DxilArgs.push_back(DXC_ARG_DEBUG); // Debug info if (m_Library.GetVersion() >= Version{1, 5}) { // Silence the following warning: // no output provided for debug - embedding PDB in shader container. Use -Qembed_debug to silence this warning. DxilArgs.push_back(L"-Qembed_debug"); } -#else - if (m_Library.GetVersion() >= Version{1, 5}) - DxilArgs.push_back(DXC_ARG_OPTIMIZATION_LEVEL3); // Optimization level 3 - else - DxilArgs.push_back(DXC_ARG_SKIP_OPTIMIZATIONS); // TODO: something goes wrong if optimization is enabled #endif + DxilArgs.push_back(GetDxcOptimizationArg(ShaderCI.ShaderOptimizationLevel, m_Library.GetTarget(), m_Library.GetVersion())); } else if (m_Library.GetTarget() == DXCompilerTarget::Vulkan) { @@ -754,12 +780,8 @@ void DXCompilerImpl::Compile(const ShaderCreateInfo& ShaderCI, { L"-spirv", L"-fspv-reflect", -#ifdef DILIGENT_DEBUG - DXC_ARG_SKIP_OPTIMIZATIONS, -#else - DXC_ARG_OPTIMIZATION_LEVEL3 -#endif }); + DxilArgs.push_back(GetDxcOptimizationArg(ShaderCI.ShaderOptimizationLevel, m_Library.GetTarget(), m_Library.GetVersion())); if (m_APIVersion >= VK_API_VERSION_1_2 && ShaderModel >= ShaderVersion{6, 3}) { diff --git a/Graphics/ShaderTools/src/GLSLangUtils.cpp b/Graphics/ShaderTools/src/GLSLangUtils.cpp index 80257e970f..f52082ffd0 100644 --- a/Graphics/ShaderTools/src/GLSLangUtils.cpp +++ b/Graphics/ShaderTools/src/GLSLangUtils.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2025 Diligent Graphics LLC + * Copyright 2019-2026 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -427,6 +427,17 @@ spv_target_env SpirvVersionToSpvTargetEnv(SpirvVersion Version) } // namespace +#ifdef USE_SPIRV_TOOLS +static SPIRV_OPTIMIZATION_FLAGS GetSpirvPerformanceFlag(SHADER_OPTIMIZATION_LEVEL OptimizationLevel) +{ + // SPIRV-Tools has no O0-O3 granularity. DEFAULT and explicit levels 0..3 enable the + // performance pass; only DISABLED skips it. (Legalization, when required, is applied separately.) + return OptimizationLevel == SHADER_OPTIMIZATION_LEVEL_DISABLED ? + SPIRV_OPTIMIZATION_FLAG_NONE : + SPIRV_OPTIMIZATION_FLAG_PERFORMANCE; +} +#endif + std::vector HLSLtoSPIRV(const ShaderCreateInfo& ShaderCI, SpirvVersion Version, const char* ExtraDefinitions, @@ -487,8 +498,10 @@ std::vector HLSLtoSPIRV(const ShaderCreateInfo& ShaderCI, #ifdef USE_SPIRV_TOOLS // SPIR-V bytecode generated from HLSL must be legalized to - // turn it into a valid vulkan SPIR-V shader. - std::vector LegalizedSPIRV = OptimizeSPIRV(SPIRV, SpirvVersionToSpvTargetEnv(Version), SPIRV_OPTIMIZATION_FLAG_LEGALIZATION | SPIRV_OPTIMIZATION_FLAG_PERFORMANCE); + // turn it into a valid vulkan SPIR-V shader. Legalization is always applied for correctness; + // the performance pass is gated by the shader optimization level. + const SPIRV_OPTIMIZATION_FLAGS OptimizationFlags = SPIRV_OPTIMIZATION_FLAG_LEGALIZATION | GetSpirvPerformanceFlag(ShaderCI.ShaderOptimizationLevel); + std::vector LegalizedSPIRV = OptimizeSPIRV(SPIRV, SpirvVersionToSpvTargetEnv(Version), OptimizationFlags); if (!LegalizedSPIRV.empty()) { return LegalizedSPIRV; @@ -535,14 +548,18 @@ std::vector GLSLtoSPIRV(const GLSLtoSPIRVAttribs& Attribs) return SPIRV; #ifdef USE_SPIRV_TOOLS - std::vector OptimizedSPIRV = OptimizeSPIRV(SPIRV, SpirvVersionToSpvTargetEnv(Attribs.Version), SPIRV_OPTIMIZATION_FLAG_PERFORMANCE); - if (!OptimizedSPIRV.empty()) - { - return OptimizedSPIRV; - } - else + const SPIRV_OPTIMIZATION_FLAGS OptimizationFlags = GetSpirvPerformanceFlag(Attribs.OptimizationLevel); + if (OptimizationFlags != SPIRV_OPTIMIZATION_FLAG_NONE) { - LOG_ERROR("Failed to optimize SPIR-V."); + std::vector OptimizedSPIRV = OptimizeSPIRV(SPIRV, SpirvVersionToSpvTargetEnv(Attribs.Version), OptimizationFlags); + if (!OptimizedSPIRV.empty()) + { + return OptimizedSPIRV; + } + else + { + LOG_ERROR("Failed to optimize SPIR-V."); + } } #endif return SPIRV; diff --git a/ReleaseHistory.md b/ReleaseHistory.md index f307ca23ab..e82435cff6 100644 --- a/ReleaseHistory.md +++ b/ReleaseHistory.md @@ -2,6 +2,7 @@ ## Current progress +* Added `SHADER_OPTIMIZATION_LEVEL` enum and `ShaderCreateInfo::ShaderOptimizationLevel` member (API256018) * Added `DrawMeshIndirectAttribs::pMtlAttribs` member (API256017) * `DrawMeshIndirectAttribs::pMtlAttribs` allows specifying Metal object and mesh thread group sizes for `IDeviceContext::DrawMeshIndirect()` * Added `DrawMeshAttribsMtl` struct and `DrawMeshAttribs::pMtlAttribs` member (API256016) diff --git a/Tests/DiligentCoreTest/src/Common/HashUtilsTest.cpp b/Tests/DiligentCoreTest/src/Common/HashUtilsTest.cpp index 3477ee15c0..4f8a335463 100644 --- a/Tests/DiligentCoreTest/src/Common/HashUtilsTest.cpp +++ b/Tests/DiligentCoreTest/src/Common/HashUtilsTest.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2019-2024 Diligent Graphics LLC + * Copyright 2019-2026 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -1185,6 +1185,7 @@ TEST(XXH128HasherTest, ShaderCreateInfo) TEST_RANGE(MSLVersion.Major, 1u, 10u); TEST_FLAGS(CompileFlags, static_cast(1), SHADER_COMPILE_FLAG_LAST); + TEST_RANGE(ShaderOptimizationLevel, static_cast(1), SHADER_OPTIMIZATION_LEVEL_COUNT); TEST_BOOL(LoadConstantBufferReflection); TEST_STRINGS(GLSLExtensions, "Extension 1", "Extension 2", "Extension 3"); diff --git a/Tests/DiligentCoreTest/src/GraphicsEngine/PSOSerializerTest.cpp b/Tests/DiligentCoreTest/src/GraphicsEngine/PSOSerializerTest.cpp index 5953777820..e9e10930ea 100644 --- a/Tests/DiligentCoreTest/src/GraphicsEngine/PSOSerializerTest.cpp +++ b/Tests/DiligentCoreTest/src/GraphicsEngine/PSOSerializerTest.cpp @@ -789,6 +789,7 @@ void SerializeShaderCreateInfo(bool UseBytecode) RefCI.GLESSLVersion = {5, 6}; RefCI.MSLVersion = {7, 8}; RefCI.CompileFlags = SHADER_COMPILE_FLAG_SKIP_REFLECTION; + RefCI.ShaderOptimizationLevel = SHADER_OPTIMIZATION_LEVEL_3; RefCI.LoadConstantBufferReflection = true; RefCI.GLSLExtensions = "My extension"; RefCI.WebGPUEmulatedArrayIndexSuffix = "My suffix"; @@ -839,6 +840,7 @@ void SerializeShaderCreateInfo(bool UseBytecode) EXPECT_EQ (CI.GLESSLVersion, RefCI.GLESSLVersion); EXPECT_EQ (CI.MSLVersion, RefCI.MSLVersion); EXPECT_EQ (CI.CompileFlags, RefCI.CompileFlags); + EXPECT_EQ (CI.ShaderOptimizationLevel, RefCI.ShaderOptimizationLevel); EXPECT_EQ (CI.LoadConstantBufferReflection, RefCI.LoadConstantBufferReflection); EXPECT_STREQ(CI.GLSLExtensions, RefCI.GLSLExtensions); EXPECT_STREQ(CI.WebGPUEmulatedArrayIndexSuffix, RefCI.WebGPUEmulatedArrayIndexSuffix);