diff --git a/Source/Core/VideoBackends/Vulkan/ShaderCompiler.cpp b/Source/Core/VideoBackends/Vulkan/ShaderCompiler.cpp index 75bf99a3ba..c97c56f75d 100644 --- a/Source/Core/VideoBackends/Vulkan/ShaderCompiler.cpp +++ b/Source/Core/VideoBackends/Vulkan/ShaderCompiler.cpp @@ -3,9 +3,11 @@ // Refer to the license.txt file included. #include "VideoBackends/Vulkan/ShaderCompiler.h" +#include "VideoBackends/Vulkan/VulkanContext.h" #include #include +#include #include #include #include @@ -37,6 +39,11 @@ static bool CompileShaderToSPV(SPIRVCodeVector* out_code, EShLanguage stage, const char* stage_filename, const char* source_code, size_t source_code_length, const char* header, size_t header_length); +// Copy GLSL source code to a SPIRVCodeVector, for use with VK_NV_glsl_shader. +static void CopyGLSLToSPVVector(SPIRVCodeVector* out_code, const char* stage_filename, + const char* source_code, size_t source_code_length, + const char* header, size_t header_length); + // 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 @@ -219,6 +226,42 @@ bool CompileShaderToSPV(SPIRVCodeVector* out_code, EShLanguage stage, const char return true; } +void CopyGLSLToSPVVector(SPIRVCodeVector* out_code, const char* stage_filename, + const char* source_code, size_t source_code_length, const char* header, + size_t header_length) +{ + std::string full_source_code; + if (header_length > 0) + { + full_source_code.reserve(header_length + source_code_length); + full_source_code.append(header, header_length); + full_source_code.append(source_code, source_code_length); + } + else + { + full_source_code.append(source_code, source_code_length); + } + + 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 << full_source_code << std::endl; + } + + size_t padding = full_source_code.size() % 4; + if (padding != 0) + full_source_code.append(4 - padding, '\n'); + + out_code->resize(full_source_code.size() / 4); + std::memcpy(out_code->data(), full_source_code.c_str(), full_source_code.size()); +} + bool InitializeGlslang() { static bool glslang_initialized = false; @@ -338,29 +381,57 @@ const TBuiltInResource* GetCompilerResourceLimits() } bool CompileVertexShader(SPIRVCodeVector* out_code, const char* source_code, - size_t source_code_length, bool prepend_header) + size_t source_code_length) { + if (g_vulkan_context->SupportsNVGLSLExtension()) + { + CopyGLSLToSPVVector(out_code, "vs", source_code, source_code_length, SHADER_HEADER, + sizeof(SHADER_HEADER) - 1); + return true; + } + return CompileShaderToSPV(out_code, EShLangVertex, "vs", source_code, source_code_length, SHADER_HEADER, sizeof(SHADER_HEADER) - 1); } bool CompileGeometryShader(SPIRVCodeVector* out_code, const char* source_code, - size_t source_code_length, bool prepend_header) + size_t source_code_length) { + if (g_vulkan_context->SupportsNVGLSLExtension()) + { + CopyGLSLToSPVVector(out_code, "gs", source_code, source_code_length, SHADER_HEADER, + sizeof(SHADER_HEADER) - 1); + return true; + } + return CompileShaderToSPV(out_code, EShLangGeometry, "gs", source_code, source_code_length, SHADER_HEADER, sizeof(SHADER_HEADER) - 1); } bool CompileFragmentShader(SPIRVCodeVector* out_code, const char* source_code, - size_t source_code_length, bool prepend_header) + size_t source_code_length) { + if (g_vulkan_context->SupportsNVGLSLExtension()) + { + CopyGLSLToSPVVector(out_code, "ps", source_code, source_code_length, SHADER_HEADER, + sizeof(SHADER_HEADER) - 1); + return true; + } + return CompileShaderToSPV(out_code, EShLangFragment, "ps", source_code, source_code_length, SHADER_HEADER, sizeof(SHADER_HEADER) - 1); } bool CompileComputeShader(SPIRVCodeVector* out_code, const char* source_code, - size_t source_code_length, bool prepend_header) + size_t source_code_length) { + if (g_vulkan_context->SupportsNVGLSLExtension()) + { + CopyGLSLToSPVVector(out_code, "cs", source_code, source_code_length, COMPUTE_SHADER_HEADER, + sizeof(COMPUTE_SHADER_HEADER) - 1); + return true; + } + return CompileShaderToSPV(out_code, EShLangCompute, "cs", source_code, source_code_length, COMPUTE_SHADER_HEADER, sizeof(COMPUTE_SHADER_HEADER) - 1); } diff --git a/Source/Core/VideoBackends/Vulkan/ShaderCompiler.h b/Source/Core/VideoBackends/Vulkan/ShaderCompiler.h index 197dc1787c..84434b1ad6 100644 --- a/Source/Core/VideoBackends/Vulkan/ShaderCompiler.h +++ b/Source/Core/VideoBackends/Vulkan/ShaderCompiler.h @@ -19,19 +19,19 @@ using SPIRVCodeVector = std::vector; // Compile a vertex shader to SPIR-V. bool CompileVertexShader(SPIRVCodeVector* out_code, const char* source_code, - size_t source_code_length, bool prepend_header = true); + size_t source_code_length); // Compile a geometry shader to SPIR-V. bool CompileGeometryShader(SPIRVCodeVector* out_code, const char* source_code, - size_t source_code_length, bool prepend_header = true); + size_t source_code_length); // Compile a fragment shader to SPIR-V. bool CompileFragmentShader(SPIRVCodeVector* out_code, const char* source_code, - size_t source_code_length, bool prepend_header = true); + size_t source_code_length); // Compile a compute shader to SPIR-V. bool CompileComputeShader(SPIRVCodeVector* out_code, const char* source_code, - size_t source_code_length, bool prepend_header = true); + size_t source_code_length); } // namespace ShaderCompiler } // namespace Vulkan diff --git a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp index e0376c3cae..2ff6f34c33 100644 --- a/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp +++ b/Source/Core/VideoBackends/Vulkan/TextureConverter.cpp @@ -407,7 +407,7 @@ bool TextureConverter::SupportsTextureDecoding(TextureFormat format, TLUTFormat std::string shader_source = TextureConversionShader::GenerateDecodingShader(format, palette_format, APIType::Vulkan); - pipeline.compute_shader = Util::CompileAndCreateComputeShader(shader_source, true); + pipeline.compute_shader = Util::CompileAndCreateComputeShader(shader_source); if (pipeline.compute_shader == VK_NULL_HANDLE) { m_decoding_pipelines.emplace(key, pipeline); diff --git a/Source/Core/VideoBackends/Vulkan/Util.cpp b/Source/Core/VideoBackends/Vulkan/Util.cpp index 8a94dd42ad..221fa97cda 100644 --- a/Source/Core/VideoBackends/Vulkan/Util.cpp +++ b/Source/Core/VideoBackends/Vulkan/Util.cpp @@ -249,50 +249,38 @@ VkShaderModule CreateShaderModule(const u32* spv, size_t spv_word_count) return module; } -VkShaderModule CompileAndCreateVertexShader(const std::string& source_code, bool prepend_header) +VkShaderModule CompileAndCreateVertexShader(const std::string& source_code) { ShaderCompiler::SPIRVCodeVector code; - if (!ShaderCompiler::CompileVertexShader(&code, source_code.c_str(), source_code.length(), - prepend_header)) - { + if (!ShaderCompiler::CompileVertexShader(&code, source_code.c_str(), source_code.length())) return VK_NULL_HANDLE; - } return CreateShaderModule(code.data(), code.size()); } -VkShaderModule CompileAndCreateGeometryShader(const std::string& source_code, bool prepend_header) +VkShaderModule CompileAndCreateGeometryShader(const std::string& source_code) { ShaderCompiler::SPIRVCodeVector code; - if (!ShaderCompiler::CompileGeometryShader(&code, source_code.c_str(), source_code.length(), - prepend_header)) - { + if (!ShaderCompiler::CompileGeometryShader(&code, source_code.c_str(), source_code.length())) return VK_NULL_HANDLE; - } return CreateShaderModule(code.data(), code.size()); } -VkShaderModule CompileAndCreateFragmentShader(const std::string& source_code, bool prepend_header) +VkShaderModule CompileAndCreateFragmentShader(const std::string& source_code) { ShaderCompiler::SPIRVCodeVector code; - if (!ShaderCompiler::CompileFragmentShader(&code, source_code.c_str(), source_code.length(), - prepend_header)) - { + if (!ShaderCompiler::CompileFragmentShader(&code, source_code.c_str(), source_code.length())) return VK_NULL_HANDLE; - } return CreateShaderModule(code.data(), code.size()); } -VkShaderModule CompileAndCreateComputeShader(const std::string& source_code, bool prepend_header) +VkShaderModule CompileAndCreateComputeShader(const std::string& source_code) { ShaderCompiler::SPIRVCodeVector code; - if (!ShaderCompiler::CompileComputeShader(&code, source_code.c_str(), source_code.length(), - prepend_header)) - { + if (!ShaderCompiler::CompileComputeShader(&code, source_code.c_str(), source_code.length())) return VK_NULL_HANDLE; - } return CreateShaderModule(code.data(), code.size()); } diff --git a/Source/Core/VideoBackends/Vulkan/Util.h b/Source/Core/VideoBackends/Vulkan/Util.h index 413e3b96c5..56d8d58ff0 100644 --- a/Source/Core/VideoBackends/Vulkan/Util.h +++ b/Source/Core/VideoBackends/Vulkan/Util.h @@ -57,20 +57,16 @@ void ExecuteCurrentCommandsAndRestoreState(bool execute_off_thread, VkShaderModule CreateShaderModule(const u32* spv, size_t spv_word_count); // Compile a vertex shader and create a shader module, discarding the intermediate SPIR-V. -VkShaderModule CompileAndCreateVertexShader(const std::string& source_code, - bool prepend_header = true); +VkShaderModule CompileAndCreateVertexShader(const std::string& source_code); // Compile a geometry shader and create a shader module, discarding the intermediate SPIR-V. -VkShaderModule CompileAndCreateGeometryShader(const std::string& source_code, - bool prepend_header = true); +VkShaderModule CompileAndCreateGeometryShader(const std::string& source_code); // Compile a fragment shader and create a shader module, discarding the intermediate SPIR-V. -VkShaderModule CompileAndCreateFragmentShader(const std::string& source_code, - bool prepend_header = true); +VkShaderModule CompileAndCreateFragmentShader(const std::string& source_code); // Compile a compute shader and create a shader module, discarding the intermediate SPIR-V. -VkShaderModule CompileAndCreateComputeShader(const std::string& source_code, - bool prepend_header = true); +VkShaderModule CompileAndCreateComputeShader(const std::string& source_code); } // Utility shader vertex format diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp index d4524f5c27..765a6f33f3 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.cpp @@ -403,7 +403,8 @@ bool VulkanContext::SelectDeviceExtensions(ExtensionList* extension_list, bool e for (const auto& extension_properties : available_extension_list) INFO_LOG(VIDEO, "Available extension: %s", extension_properties.extensionName); - auto CheckForExtension = [&](const char* name, bool required) -> bool { + auto CheckForExtension = [&](const char* name, bool required, + bool* has_extension = nullptr) -> bool { if (std::find_if(available_extension_list.begin(), available_extension_list.end(), [&](const VkExtensionProperties& properties) { return !strcmp(name, properties.extensionName); @@ -411,9 +412,14 @@ bool VulkanContext::SelectDeviceExtensions(ExtensionList* extension_list, bool e { INFO_LOG(VIDEO, "Enabling extension: %s", name); extension_list->push_back(name); + if (has_extension) + *has_extension = true; return true; } + if (has_extension) + *has_extension = false; + if (required) { ERROR_LOG(VIDEO, "Vulkan: Missing required extension %s.", name); @@ -426,6 +432,7 @@ bool VulkanContext::SelectDeviceExtensions(ExtensionList* extension_list, bool e if (enable_surface && !CheckForExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true)) return false; + CheckForExtension(VK_NV_GLSL_SHADER_EXTENSION_NAME, false, &m_supports_nv_glsl_extension); return true; } diff --git a/Source/Core/VideoBackends/Vulkan/VulkanContext.h b/Source/Core/VideoBackends/Vulkan/VulkanContext.h index 3ac496339d..fd504560b8 100644 --- a/Source/Core/VideoBackends/Vulkan/VulkanContext.h +++ b/Source/Core/VideoBackends/Vulkan/VulkanContext.h @@ -83,6 +83,7 @@ public: { return m_device_features.occlusionQueryPrecise == VK_TRUE; } + bool SupportsNVGLSLExtension() const { return m_supports_nv_glsl_extension; } // Helpers for getting constants VkDeviceSize GetUniformBufferAlignment() const { @@ -129,6 +130,8 @@ private: VkPhysicalDeviceFeatures m_device_features = {}; VkPhysicalDeviceProperties m_device_properties = {}; VkPhysicalDeviceMemoryProperties m_device_memory_properties = {}; + + bool m_supports_nv_glsl_extension = false; }; extern std::unique_ptr g_vulkan_context;