VideoCommon: add common spirv helper functions

Co-authored-by: tellowkrinkle <tellowkrinkle@gmail.com>
This commit is contained in:
iwubcode 2022-05-04 00:40:59 -05:00
parent 1c90d836a5
commit 3790c99a7d
4 changed files with 248 additions and 0 deletions

View File

@ -650,6 +650,7 @@
<ClInclude Include="VideoCommon\RenderState.h" />
<ClInclude Include="VideoCommon\ShaderCache.h" />
<ClInclude Include="VideoCommon\ShaderGenCommon.h" />
<ClInclude Include="VideoCommon\Spirv.h" />
<ClInclude Include="VideoCommon\Statistics.h" />
<ClInclude Include="VideoCommon\TextureCacheBase.h" />
<ClInclude Include="VideoCommon\TextureConfig.h" />
@ -1224,6 +1225,7 @@
<ClCompile Include="VideoCommon\RenderState.cpp" />
<ClCompile Include="VideoCommon\ShaderCache.cpp" />
<ClCompile Include="VideoCommon\ShaderGenCommon.cpp" />
<ClCompile Include="VideoCommon\Spirv.cpp" />
<ClCompile Include="VideoCommon\Statistics.cpp" />
<ClCompile Include="VideoCommon\TextureCacheBase.cpp" />
<ClCompile Include="VideoCommon\TextureConfig.cpp" />

View File

@ -74,6 +74,8 @@ add_library(videocommon
ShaderCache.h
ShaderGenCommon.cpp
ShaderGenCommon.h
Spirv.cpp
Spirv.h
Statistics.cpp
Statistics.h
TextureCacheBase.cpp
@ -139,6 +141,7 @@ PRIVATE
png
xxhash
imgui
glslang
)
if(_M_X86)
@ -181,6 +184,16 @@ if(FFmpeg_FOUND)
endif()
endif()
# Silence warnings on glslang by flagging it as a system include
target_include_directories(videocommon
SYSTEM PUBLIC
${CMAKE_SOURCE_DIR}/Externals/glslang/glslang/Public
SYSTEM PRIVATE
${CMAKE_SOURCE_DIR}/Externals/glslang/StandAlone
${CMAKE_SOURCE_DIR}/Externals/glslang/SPIRV
${CMAKE_SOURCE_DIR}/Externals/glslang
)
if(MSVC)
# Add precompiled header
target_link_libraries(videocommon PRIVATE use_pch)

View File

@ -0,0 +1,203 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "VideoCommon/Spirv.h"
// glslang includes
#include "GlslangToSpv.h"
#include "ResourceLimits.h"
#include "ShaderLang.h"
#include "disassemble.h"
#include "Common/FileUtil.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/StringUtil.h"
#include "Common/Version.h"
#include "VideoCommon/VideoBackendBase.h"
#include "VideoCommon/VideoConfig.h"
namespace
{
bool InitializeGlslang()
{
static bool glslang_initialized = false;
if (glslang_initialized)
return true;
if (!glslang::InitializeProcess())
{
PanicAlertFmt("Failed to initialize glslang shader compiler");
return false;
}
std::atexit([]() { glslang::FinalizeProcess(); });
glslang_initialized = true;
return true;
}
const TBuiltInResource* GetCompilerResourceLimits()
{
return &glslang::DefaultTBuiltInResource;
}
std::optional<SPIRV::CodeVector>
CompileShaderToSPV(EShLanguage stage,
std::optional<glslang::EShTargetLanguageVersion> language_version,
const char* stage_filename, std::string_view source)
{
if (!InitializeGlslang())
return std::nullopt;
std::unique_ptr<glslang::TShader> shader = std::make_unique<glslang::TShader>(stage);
std::unique_ptr<glslang::TProgram> program;
glslang::TShader::ForbidIncluder includer;
EProfile profile = ECoreProfile;
EShMessages messages = static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules);
int default_version = 450;
const char* pass_source_code = source.data();
int pass_source_code_length = static_cast<int>(source.size());
if (language_version)
shader->setEnvTarget(glslang::EShTargetSpv, *language_version);
shader->setStringsWithLengths(&pass_source_code, &pass_source_code_length, 1);
auto DumpBadShader = [&](const char* msg) {
static int counter = 0;
std::string filename = VideoBackendBase::BadShaderFilename(stage_filename, counter++);
std::ofstream stream;
File::OpenFStream(stream, filename, std::ios_base::out);
if (stream.good())
{
stream << source << std::endl;
stream << msg << std::endl;
stream << "Shader Info Log:" << std::endl;
stream << shader->getInfoLog() << std::endl;
stream << shader->getInfoDebugLog() << std::endl;
if (program)
{
stream << "Program Info Log:" << std::endl;
stream << program->getInfoLog() << std::endl;
stream << program->getInfoDebugLog() << std::endl;
}
}
stream << "\n";
stream << "Dolphin Version: " + Common::GetScmRevStr() + "\n";
stream << "Video Backend: " + g_video_backend->GetDisplayName();
stream.close();
PanicAlertFmt("{} (written to {})\nDebug info:\n{}", msg, filename, shader->getInfoLog());
};
if (!shader->parse(GetCompilerResourceLimits(), default_version, profile, false, true, messages,
includer))
{
DumpBadShader("Failed to parse shader");
return std::nullopt;
}
// Even though there's only a single shader, we still need to link it to generate SPV
program = std::make_unique<glslang::TProgram>();
program->addShader(shader.get());
if (!program->link(messages))
{
DumpBadShader("Failed to link program");
return std::nullopt;
}
glslang::TIntermediate* intermediate = program->getIntermediate(stage);
if (!intermediate)
{
DumpBadShader("Failed to generate SPIR-V");
return std::nullopt;
}
SPIRV::CodeVector out_code;
spv::SpvBuildLogger logger;
glslang::SpvOptions options;
if (g_ActiveConfig.bEnableValidationLayer)
{
// Attach the source code to the SPIR-V for tools like RenderDoc.
intermediate->addSourceText(pass_source_code, pass_source_code_length);
options.generateDebugInfo = true;
options.disableOptimizer = true;
options.optimizeSize = false;
options.disassemble = false;
options.validate = true;
}
glslang::GlslangToSpv(*intermediate, out_code, &logger, &options);
// Write out messages
// Temporary: skip if it contains "Warning, version 450 is not yet complete; most version-specific
// features are present, but some are missing."
if (strlen(shader->getInfoLog()) > 108)
WARN_LOG_FMT(VIDEO, "Shader info log: {}", shader->getInfoLog());
if (strlen(shader->getInfoDebugLog()) > 0)
WARN_LOG_FMT(VIDEO, "Shader debug info log: {}", shader->getInfoDebugLog());
if (strlen(program->getInfoLog()) > 25)
WARN_LOG_FMT(VIDEO, "Program info log: {}", program->getInfoLog());
if (strlen(program->getInfoDebugLog()) > 0)
WARN_LOG_FMT(VIDEO, "Program debug info log: {}", program->getInfoDebugLog());
const std::string spv_messages = logger.getAllMessages();
if (!spv_messages.empty())
WARN_LOG_FMT(VIDEO, "SPIR-V conversion messages: {}", spv_messages);
// Dump source code of shaders out to file if enabled.
if (g_ActiveConfig.iLog & CONF_SAVESHADERS)
{
static int counter = 0;
std::string filename = StringFromFormat("%s%s_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(),
stage_filename, counter++);
std::ofstream stream;
File::OpenFStream(stream, filename, std::ios_base::out);
if (stream.good())
{
stream << source << std::endl;
stream << "Shader Info Log:" << std::endl;
stream << shader->getInfoLog() << std::endl;
stream << shader->getInfoDebugLog() << std::endl;
stream << "Program Info Log:" << std::endl;
stream << program->getInfoLog() << std::endl;
stream << program->getInfoDebugLog() << std::endl;
stream << "SPIR-V conversion messages: " << std::endl;
stream << spv_messages;
stream << "SPIR-V:" << std::endl;
spv::Disassemble(stream, out_code);
}
}
return out_code;
}
} // namespace
namespace SPIRV
{
std::optional<CodeVector> CompileVertexShader(std::string_view source_code)
{
return CompileShaderToSPV(EShLangVertex, std::nullopt, "vs", source_code);
}
std::optional<CodeVector> CompileGeometryShader(std::string_view source_code)
{
return CompileShaderToSPV(EShLangGeometry, std::nullopt, "gs", source_code);
}
std::optional<CodeVector> CompileFragmentShader(std::string_view source_code)
{
return CompileShaderToSPV(EShLangFragment, std::nullopt, "ps", source_code);
}
std::optional<CodeVector> CompileComputeShader(std::string_view source_code)
{
return CompileShaderToSPV(EShLangCompute, std::nullopt, "cs", source_code);
}
} // namespace SPIRV

View File

@ -0,0 +1,30 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <cstddef>
#include <optional>
#include <string_view>
#include <vector>
#include "Common/CommonTypes.h"
namespace SPIRV
{
// SPIR-V compiled code type
using CodeType = u32;
using CodeVector = std::vector<CodeType>;
// Compile a vertex shader to SPIR-V.
std::optional<CodeVector> CompileVertexShader(std::string_view source_code);
// Compile a geometry shader to SPIR-V.
std::optional<CodeVector> CompileGeometryShader(std::string_view source_code);
// Compile a fragment shader to SPIR-V.
std::optional<CodeVector> CompileFragmentShader(std::string_view source_code);
// Compile a compute shader to SPIR-V.
std::optional<CodeVector> CompileComputeShader(std::string_view source_code);
} // namespace SPIRV