forked from dolphin-emu/dolphin
		
	
		
			
				
	
	
		
			340 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			340 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
// Copyright 2016 Dolphin Emulator Project
 | 
						|
// Licensed under GPLv2+
 | 
						|
// Refer to the license.txt file included.
 | 
						|
 | 
						|
#include "VideoBackends/Vulkan/ShaderCompiler.h"
 | 
						|
 | 
						|
#include <cstddef>
 | 
						|
#include <cstdlib>
 | 
						|
#include <fstream>
 | 
						|
#include <memory>
 | 
						|
#include <string>
 | 
						|
 | 
						|
// glslang includes
 | 
						|
#include "GlslangToSpv.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 "VideoCommon/VideoConfig.h"
 | 
						|
 | 
						|
namespace Vulkan
 | 
						|
{
 | 
						|
namespace ShaderCompiler
 | 
						|
{
 | 
						|
// Registers itself for cleanup via atexit
 | 
						|
bool InitializeGlslang();
 | 
						|
 | 
						|
// Resource limits used when compiling shaders
 | 
						|
static const TBuiltInResource* GetCompilerResourceLimits();
 | 
						|
 | 
						|
// Compile a shader to SPIR-V via glslang
 | 
						|
static bool CompileShaderToSPV(SPIRVCodeVector* out_code, EShLanguage stage,
 | 
						|
                               const char* stage_filename, const char* source_code,
 | 
						|
                               size_t source_code_length, bool prepend_header);
 | 
						|
 | 
						|
// Regarding the UBO bind points, we subtract one from the binding index because
 | 
						|
// the OpenGL backend requires UBO #0 for non-block uniforms (at least on NV).
 | 
						|
// This allows us to share the same shaders but use bind point #0 in the Vulkan
 | 
						|
// backend. None of the Vulkan-specific shaders use UBOs, instead they use push
 | 
						|
// constants, so when/if the GL backend moves to uniform blocks completely this
 | 
						|
// subtraction can be removed.
 | 
						|
static const char SHADER_HEADER[] = R"(
 | 
						|
  // Target GLSL 4.5.
 | 
						|
  #version 450 core
 | 
						|
  #define ATTRIBUTE_LOCATION(x) layout(location = x)
 | 
						|
  #define FRAGMENT_OUTPUT_LOCATION(x) layout(location = x)
 | 
						|
  #define FRAGMENT_OUTPUT_LOCATION_INDEXED(x, y) layout(location = x, index = y)
 | 
						|
  #define UBO_BINDING(packing, x) layout(packing, set = 0, binding = (x - 1))
 | 
						|
  #define SAMPLER_BINDING(x) layout(set = 1, binding = x)
 | 
						|
  #define SSBO_BINDING(x) layout(set = 2, binding = x)
 | 
						|
  #define TEXEL_BUFFER_BINDING(x) layout(set = 2, binding = x)
 | 
						|
  #define VARYING_LOCATION(x) layout(location = x)
 | 
						|
  #define FORCE_EARLY_Z layout(early_fragment_tests) in
 | 
						|
 | 
						|
  // hlsl to glsl function translation
 | 
						|
  #define float2 vec2
 | 
						|
  #define float3 vec3
 | 
						|
  #define float4 vec4
 | 
						|
  #define uint2 uvec2
 | 
						|
  #define uint3 uvec3
 | 
						|
  #define uint4 uvec4
 | 
						|
  #define int2 ivec2
 | 
						|
  #define int3 ivec3
 | 
						|
  #define int4 ivec4
 | 
						|
  #define frac fract
 | 
						|
  #define lerp mix
 | 
						|
 | 
						|
  // These were changed in Vulkan
 | 
						|
  #define gl_VertexID gl_VertexIndex
 | 
						|
  #define gl_InstanceID gl_InstanceIndex
 | 
						|
)";
 | 
						|
 | 
						|
bool CompileShaderToSPV(SPIRVCodeVector* out_code, EShLanguage stage, const char* stage_filename,
 | 
						|
                        const char* source_code, size_t source_code_length, bool prepend_header)
 | 
						|
{
 | 
						|
  if (!InitializeGlslang())
 | 
						|
    return false;
 | 
						|
 | 
						|
  std::unique_ptr<glslang::TShader> shader = std::make_unique<glslang::TShader>(stage);
 | 
						|
  std::unique_ptr<glslang::TProgram> program;
 | 
						|
  glslang::TShader::ForbidInclude includer;
 | 
						|
  EProfile profile = ECoreProfile;
 | 
						|
  EShMessages messages =
 | 
						|
      static_cast<EShMessages>(EShMsgDefault | EShMsgSpvRules | EShMsgVulkanRules);
 | 
						|
  int default_version = 450;
 | 
						|
 | 
						|
  std::string full_source_code;
 | 
						|
  const char* pass_source_code = source_code;
 | 
						|
  int pass_source_code_length = static_cast<int>(source_code_length);
 | 
						|
  if (prepend_header)
 | 
						|
  {
 | 
						|
    full_source_code.reserve(sizeof(SHADER_HEADER) + source_code_length);
 | 
						|
    full_source_code.append(SHADER_HEADER, sizeof(SHADER_HEADER) - 1);
 | 
						|
    full_source_code.append(source_code, source_code_length);
 | 
						|
    pass_source_code = full_source_code.c_str();
 | 
						|
    pass_source_code_length = static_cast<int>(full_source_code.length());
 | 
						|
  }
 | 
						|
 | 
						|
  shader->setStringsWithLengths(&pass_source_code, &pass_source_code_length, 1);
 | 
						|
 | 
						|
  auto DumpBadShader = [&](const char* msg) {
 | 
						|
    static int counter = 0;
 | 
						|
    std::string filename = StringFromFormat(
 | 
						|
        "%sbad_%s_%04i.txt", File::GetUserPath(D_DUMP_IDX).c_str(), stage_filename, counter++);
 | 
						|
 | 
						|
    std::ofstream stream;
 | 
						|
    OpenFStream(stream, filename, std::ios_base::out);
 | 
						|
    if (stream.good())
 | 
						|
    {
 | 
						|
      stream << full_source_code << 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;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    PanicAlert("%s (written to %s)", msg, filename.c_str());
 | 
						|
  };
 | 
						|
 | 
						|
  if (!shader->parse(GetCompilerResourceLimits(), default_version, profile, false, true, messages,
 | 
						|
                     includer))
 | 
						|
  {
 | 
						|
    DumpBadShader("Failed to parse shader");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  // 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 false;
 | 
						|
  }
 | 
						|
 | 
						|
  glslang::TIntermediate* intermediate = program->getIntermediate(stage);
 | 
						|
  if (!intermediate)
 | 
						|
  {
 | 
						|
    DumpBadShader("Failed to generate SPIR-V");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  spv::SpvBuildLogger logger;
 | 
						|
  glslang::GlslangToSpv(*intermediate, *out_code, &logger);
 | 
						|
 | 
						|
  // 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(VIDEO, "Shader info log: %s", shader->getInfoLog());
 | 
						|
  if (strlen(shader->getInfoDebugLog()) > 0)
 | 
						|
    WARN_LOG(VIDEO, "Shader debug info log: %s", shader->getInfoDebugLog());
 | 
						|
  if (strlen(program->getInfoLog()) > 25)
 | 
						|
    WARN_LOG(VIDEO, "Program info log: %s", program->getInfoLog());
 | 
						|
  if (strlen(program->getInfoDebugLog()) > 0)
 | 
						|
    WARN_LOG(VIDEO, "Program debug info log: %s", program->getInfoDebugLog());
 | 
						|
  std::string spv_messages = logger.getAllMessages();
 | 
						|
  if (!spv_messages.empty())
 | 
						|
    WARN_LOG(VIDEO, "SPIR-V conversion messages: %s", spv_messages.c_str());
 | 
						|
 | 
						|
  // 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;
 | 
						|
    OpenFStream(stream, filename, std::ios_base::out);
 | 
						|
    if (stream.good())
 | 
						|
    {
 | 
						|
      stream << full_source_code << 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 true;
 | 
						|
}
 | 
						|
 | 
						|
bool InitializeGlslang()
 | 
						|
{
 | 
						|
  static bool glslang_initialized = false;
 | 
						|
  if (glslang_initialized)
 | 
						|
    return true;
 | 
						|
 | 
						|
  if (!glslang::InitializeProcess())
 | 
						|
  {
 | 
						|
    PanicAlert("Failed to initialize glslang shader compiler");
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  std::atexit([]() { glslang::FinalizeProcess(); });
 | 
						|
 | 
						|
  glslang_initialized = true;
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
const TBuiltInResource* GetCompilerResourceLimits()
 | 
						|
{
 | 
						|
  static const TBuiltInResource limits = {/* .MaxLights = */ 32,
 | 
						|
                                          /* .MaxClipPlanes = */ 6,
 | 
						|
                                          /* .MaxTextureUnits = */ 32,
 | 
						|
                                          /* .MaxTextureCoords = */ 32,
 | 
						|
                                          /* .MaxVertexAttribs = */ 64,
 | 
						|
                                          /* .MaxVertexUniformComponents = */ 4096,
 | 
						|
                                          /* .MaxVaryingFloats = */ 64,
 | 
						|
                                          /* .MaxVertexTextureImageUnits = */ 32,
 | 
						|
                                          /* .MaxCombinedTextureImageUnits = */ 80,
 | 
						|
                                          /* .MaxTextureImageUnits = */ 32,
 | 
						|
                                          /* .MaxFragmentUniformComponents = */ 4096,
 | 
						|
                                          /* .MaxDrawBuffers = */ 32,
 | 
						|
                                          /* .MaxVertexUniformVectors = */ 128,
 | 
						|
                                          /* .MaxVaryingVectors = */ 8,
 | 
						|
                                          /* .MaxFragmentUniformVectors = */ 16,
 | 
						|
                                          /* .MaxVertexOutputVectors = */ 16,
 | 
						|
                                          /* .MaxFragmentInputVectors = */ 15,
 | 
						|
                                          /* .MinProgramTexelOffset = */ -8,
 | 
						|
                                          /* .MaxProgramTexelOffset = */ 7,
 | 
						|
                                          /* .MaxClipDistances = */ 8,
 | 
						|
                                          /* .MaxComputeWorkGroupCountX = */ 65535,
 | 
						|
                                          /* .MaxComputeWorkGroupCountY = */ 65535,
 | 
						|
                                          /* .MaxComputeWorkGroupCountZ = */ 65535,
 | 
						|
                                          /* .MaxComputeWorkGroupSizeX = */ 1024,
 | 
						|
                                          /* .MaxComputeWorkGroupSizeY = */ 1024,
 | 
						|
                                          /* .MaxComputeWorkGroupSizeZ = */ 64,
 | 
						|
                                          /* .MaxComputeUniformComponents = */ 1024,
 | 
						|
                                          /* .MaxComputeTextureImageUnits = */ 16,
 | 
						|
                                          /* .MaxComputeImageUniforms = */ 8,
 | 
						|
                                          /* .MaxComputeAtomicCounters = */ 8,
 | 
						|
                                          /* .MaxComputeAtomicCounterBuffers = */ 1,
 | 
						|
                                          /* .MaxVaryingComponents = */ 60,
 | 
						|
                                          /* .MaxVertexOutputComponents = */ 64,
 | 
						|
                                          /* .MaxGeometryInputComponents = */ 64,
 | 
						|
                                          /* .MaxGeometryOutputComponents = */ 128,
 | 
						|
                                          /* .MaxFragmentInputComponents = */ 128,
 | 
						|
                                          /* .MaxImageUnits = */ 8,
 | 
						|
                                          /* .MaxCombinedImageUnitsAndFragmentOutputs = */ 8,
 | 
						|
                                          /* .MaxCombinedShaderOutputResources = */ 8,
 | 
						|
                                          /* .MaxImageSamples = */ 0,
 | 
						|
                                          /* .MaxVertexImageUniforms = */ 0,
 | 
						|
                                          /* .MaxTessControlImageUniforms = */ 0,
 | 
						|
                                          /* .MaxTessEvaluationImageUniforms = */ 0,
 | 
						|
                                          /* .MaxGeometryImageUniforms = */ 0,
 | 
						|
                                          /* .MaxFragmentImageUniforms = */ 8,
 | 
						|
                                          /* .MaxCombinedImageUniforms = */ 8,
 | 
						|
                                          /* .MaxGeometryTextureImageUnits = */ 16,
 | 
						|
                                          /* .MaxGeometryOutputVertices = */ 256,
 | 
						|
                                          /* .MaxGeometryTotalOutputComponents = */ 1024,
 | 
						|
                                          /* .MaxGeometryUniformComponents = */ 1024,
 | 
						|
                                          /* .MaxGeometryVaryingComponents = */ 64,
 | 
						|
                                          /* .MaxTessControlInputComponents = */ 128,
 | 
						|
                                          /* .MaxTessControlOutputComponents = */ 128,
 | 
						|
                                          /* .MaxTessControlTextureImageUnits = */ 16,
 | 
						|
                                          /* .MaxTessControlUniformComponents = */ 1024,
 | 
						|
                                          /* .MaxTessControlTotalOutputComponents = */ 4096,
 | 
						|
                                          /* .MaxTessEvaluationInputComponents = */ 128,
 | 
						|
                                          /* .MaxTessEvaluationOutputComponents = */ 128,
 | 
						|
                                          /* .MaxTessEvaluationTextureImageUnits = */ 16,
 | 
						|
                                          /* .MaxTessEvaluationUniformComponents = */ 1024,
 | 
						|
                                          /* .MaxTessPatchComponents = */ 120,
 | 
						|
                                          /* .MaxPatchVertices = */ 32,
 | 
						|
                                          /* .MaxTessGenLevel = */ 64,
 | 
						|
                                          /* .MaxViewports = */ 16,
 | 
						|
                                          /* .MaxVertexAtomicCounters = */ 0,
 | 
						|
                                          /* .MaxTessControlAtomicCounters = */ 0,
 | 
						|
                                          /* .MaxTessEvaluationAtomicCounters = */ 0,
 | 
						|
                                          /* .MaxGeometryAtomicCounters = */ 0,
 | 
						|
                                          /* .MaxFragmentAtomicCounters = */ 8,
 | 
						|
                                          /* .MaxCombinedAtomicCounters = */ 8,
 | 
						|
                                          /* .MaxAtomicCounterBindings = */ 1,
 | 
						|
                                          /* .MaxVertexAtomicCounterBuffers = */ 0,
 | 
						|
                                          /* .MaxTessControlAtomicCounterBuffers = */ 0,
 | 
						|
                                          /* .MaxTessEvaluationAtomicCounterBuffers = */ 0,
 | 
						|
                                          /* .MaxGeometryAtomicCounterBuffers = */ 0,
 | 
						|
                                          /* .MaxFragmentAtomicCounterBuffers = */ 1,
 | 
						|
                                          /* .MaxCombinedAtomicCounterBuffers = */ 1,
 | 
						|
                                          /* .MaxAtomicCounterBufferSize = */ 16384,
 | 
						|
                                          /* .MaxTransformFeedbackBuffers = */ 4,
 | 
						|
                                          /* .MaxTransformFeedbackInterleavedComponents = */ 64,
 | 
						|
                                          /* .MaxCullDistances = */ 8,
 | 
						|
                                          /* .MaxCombinedClipAndCullDistances = */ 8,
 | 
						|
                                          /* .MaxSamples = */ 4,
 | 
						|
                                          /* .limits = */ {
 | 
						|
                                              /* .nonInductiveForLoops = */ 1,
 | 
						|
                                              /* .whileLoops = */ 1,
 | 
						|
                                              /* .doWhileLoops = */ 1,
 | 
						|
                                              /* .generalUniformIndexing = */ 1,
 | 
						|
                                              /* .generalAttributeMatrixVectorIndexing = */ 1,
 | 
						|
                                              /* .generalVaryingIndexing = */ 1,
 | 
						|
                                              /* .generalSamplerIndexing = */ 1,
 | 
						|
                                              /* .generalVariableIndexing = */ 1,
 | 
						|
                                              /* .generalConstantMatrixVectorIndexing = */ 1,
 | 
						|
                                          }};
 | 
						|
 | 
						|
  return &limits;
 | 
						|
}
 | 
						|
 | 
						|
bool CompileVertexShader(SPIRVCodeVector* out_code, const char* source_code,
 | 
						|
                         size_t source_code_length, bool prepend_header)
 | 
						|
{
 | 
						|
  return CompileShaderToSPV(out_code, EShLangVertex, "vs", source_code, source_code_length,
 | 
						|
                            prepend_header);
 | 
						|
}
 | 
						|
 | 
						|
bool CompileGeometryShader(SPIRVCodeVector* out_code, const char* source_code,
 | 
						|
                           size_t source_code_length, bool prepend_header)
 | 
						|
{
 | 
						|
  return CompileShaderToSPV(out_code, EShLangGeometry, "gs", source_code, source_code_length,
 | 
						|
                            prepend_header);
 | 
						|
}
 | 
						|
 | 
						|
bool CompileFragmentShader(SPIRVCodeVector* out_code, const char* source_code,
 | 
						|
                           size_t source_code_length, bool prepend_header)
 | 
						|
{
 | 
						|
  return CompileShaderToSPV(out_code, EShLangFragment, "ps", source_code, source_code_length,
 | 
						|
                            prepend_header);
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace ShaderCompiler
 | 
						|
}  // namespace Vulkan
 |