CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Common/GPU/ShaderTranslation.cpp
Views: 1401
// Copyright (c) 2017- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include "ppsspp_config.h"1819#include <memory>20#include <vector>21#include <sstream>2223// DbgNew is not compatible with Glslang24#ifdef DBG_NEW25#undef new26#undef free27#undef malloc28#undef realloc29#endif3031// Weird issue32#if PPSSPP_PLATFORM(WINDOWS) && PPSSPP_ARCH(ARM)33#undef free34#endif3536#include "Common/Log.h"37#include "Common/StringUtils.h"38#include "Common/GPU/Shader.h"3940#include "Common/GPU/ShaderTranslation.h"41#include "ext/glslang/SPIRV/GlslangToSpv.h"42#include "Common/GPU/thin3d.h"43#include "Common/GPU/Shader.h"44#include "Common/GPU/OpenGL/GLFeatures.h"4546#include "ext/SPIRV-Cross/spirv.hpp"47#include "ext/SPIRV-Cross/spirv_common.hpp"48#include "ext/SPIRV-Cross/spirv_cross.hpp"49#include "ext/SPIRV-Cross/spirv_glsl.hpp"50#ifdef _WIN3251#include "ext/SPIRV-Cross/spirv_hlsl.hpp"52#endif5354static EShLanguage GetShLanguageFromStage(const ShaderStage stage) {55switch (stage) {56case ShaderStage::Vertex: return EShLangVertex;57case ShaderStage::Geometry: return EShLangGeometry;58case ShaderStage::Fragment: return EShLangFragment;59case ShaderStage::Compute: return EShLangCompute;60default: return EShLangVertex;61}62}6364void ShaderTranslationInit() {65glslang::InitializeProcess();66}67void ShaderTranslationShutdown() {68glslang::FinalizeProcess();69}7071struct Builtin {72const char *needle;73const char *replacement;74};7576static const char * const cbufferDecl = R"(77cbuffer data : register(b0) {78float2 u_texelDelta;79float2 u_pixelDelta;80float4 u_time;81float4 u_timeDelta;82float4 u_setting;83float u_video;84};85)";8687static const char * const vulkanPrologue =88R"(#version 45089#extension GL_ARB_separate_shader_objects : enable90#extension GL_ARB_shading_language_420pack : enable91)";9293static const char * const vulkanUboDecl = R"(94layout (std140, set = 0, binding = 0) uniform Data {95vec2 u_texelDelta;96vec2 u_pixelDelta;97vec4 u_time;98vec4 u_timeDelta;99vec4 u_setting;100float u_video;101};102)";103104static const char * const d3d9RegisterDecl = R"(105float4 gl_HalfPixel : register(c0);106float2 u_texelDelta : register(c1);107float2 u_pixelDelta : register(c2);108float4 u_time : register(c3);109float4 u_timeDelta : register(c4);110float4 u_setting : register(c5);111float u_video : register(c6);112)";113114// SPIRV-Cross' HLSL output has some deficiencies we need to work around.115// Also we need to rip out single uniforms and replace them with blocks.116// Should probably do it in the source shader instead and then back translate to old style GLSL, but117// SPIRV-Cross currently won't compile with the Android NDK so I can't be bothered.118std::string Postprocess(std::string code, ShaderLanguage lang, ShaderStage stage) {119if (lang != HLSL_D3D11 && lang != HLSL_D3D9)120return code;121122std::stringstream out;123124// Output the uniform buffer.125if (lang == HLSL_D3D11)126out << cbufferDecl;127else if (lang == HLSL_D3D9)128out << d3d9RegisterDecl;129130// Alright, now let's go through it line by line and zap the single uniforms.131std::string line;132std::stringstream instream(code);133while (std::getline(instream, line)) {134int num;135if (lang == HLSL_D3D9 && sscanf(line.c_str(), "uniform sampler2D sampler%d;", &num) == 1) {136out << "sampler2D sampler" << num << " : register(s" << num << ");\n";137continue;138}139if (line.find("uniform float") != std::string::npos) {140continue;141}142out << line << "\n";143}144std::string output = out.str();145return output;146}147148static_assert(Draw::SEM_TEXCOORD0 == 3, "Semantic shader hardcoded in glsl below.");149150bool ConvertToVulkanGLSL(std::string *dest, TranslatedShaderMetadata *destMetadata, std::string src, ShaderStage stage, std::string *errorMessage) {151std::stringstream out;152153static const struct {154ShaderStage stage;155const char *needle;156const char *replacement;157} replacements[] = {158{ ShaderStage::Vertex, "attribute vec4 a_position;", "layout(location = 0) in vec4 a_position;" },159{ ShaderStage::Vertex, "attribute vec2 a_texcoord0;", "layout(location = 3) in vec2 a_texcoord0;"},160{ ShaderStage::Vertex, "varying vec2 v_position;", "layout(location = 0) out vec2 v_position;" },161{ ShaderStage::Fragment, "varying vec2 v_position;", "layout(location = 0) in vec2 v_position;" },162{ ShaderStage::Fragment, "texture2D(", "texture(" },163{ ShaderStage::Fragment, "gl_FragColor", "fragColor0" },164};165166out << vulkanPrologue;167if (stage == ShaderStage::Fragment) {168out << "layout (location = 0) out vec4 fragColor0;\n";169}170// Output the uniform buffer.171out << vulkanUboDecl;172173// Alright, now let's go through it line by line and zap the single uniforms174// and perform replacements.175std::string line;176std::stringstream instream(src);177while (std::getline(instream, line)) {178int vecSize, num;179if (line.find("uniform bool") != std::string::npos) {180continue;181} else if (line.find("uniform sampler2D") == 0) {182if (sscanf(line.c_str(), "uniform sampler2D sampler%d", &num) == 1)183line = StringFromFormat("layout(set = 0, binding = %d) ", num + 1) + line;184else if (line.find("sampler0") != line.npos)185line = "layout(set = 0, binding = 1) " + line;186else187line = "layout(set = 0, binding = 2) " + line;188} else if (line.find("uniform ") != std::string::npos) {189continue;190} else if (2 == sscanf(line.c_str(), "varying vec%d v_texcoord%d;", &vecSize, &num)) {191if (stage == ShaderStage::Fragment) {192line = StringFromFormat("layout(location = %d) in vec%d v_texcoord%d;", num, vecSize, num);193} else {194line = StringFromFormat("layout(location = %d) out vec%d v_texcoord%d;", num, vecSize, num);195}196}197for (int i = 0; i < ARRAY_SIZE(replacements); i++) {198if (replacements[i].stage == stage)199line = ReplaceAll(line, replacements[i].needle, replacements[i].replacement);200}201out << line << "\n";202}203204// DUMPLOG(src.c_str());205// INFO_LOG(Log::System, "---->");206// DUMPLOG(LineNumberString(out.str()).c_str());207208*dest = out.str();209return true;210}211212bool TranslateShader(std::string *dest, ShaderLanguage destLang, const ShaderLanguageDesc &desc, TranslatedShaderMetadata *destMetadata, std::string src, ShaderLanguage srcLang, ShaderStage stage, std::string *errorMessage) {213_assert_(errorMessage != nullptr);214215if (srcLang != GLSL_3xx && srcLang != GLSL_1xx) {216*errorMessage = StringFromFormat("Bad src shader language: %s", ShaderLanguageAsString(srcLang));217return false;218}219220if ((srcLang == GLSL_1xx || srcLang == GLSL_3xx) && destLang == GLSL_VULKAN) {221// Let's just mess about at the string level, no need to recompile.222bool result = ConvertToVulkanGLSL(dest, destMetadata, src, stage, errorMessage);223return result;224}225226errorMessage->clear();227228glslang::TProgram program;229const char *shaderStrings[1]{};230231TBuiltInResource Resources{};232InitShaderResources(Resources);233234// Don't enable SPIR-V and Vulkan rules when parsing GLSL. Our postshaders are written in oldschool GLES 2.0.235EShMessages messages = EShMessages::EShMsgDefault;236237EShLanguage shaderStage = GetShLanguageFromStage(stage);238239glslang::TShader shader(shaderStage);240241shaderStrings[0] = src.c_str();242shader.setStrings(shaderStrings, 1);243244// TODO: Should set settings here based on srcLang.245if (!shader.parse(&Resources, 100, EProfile::ECompatibilityProfile, false, false, messages)) {246*errorMessage = StringFromFormat("%s parser failure: %s\n%s", ShaderStageAsString(stage), shader.getInfoLog(), shader.getInfoDebugLog());247return false; // something didn't work248}249250// Note that program does not take ownership of &shader, so this is fine.251program.addShader(&shader);252253if (!program.link(messages)) {254*errorMessage = StringFromFormat("%s linker failure: %s\n%s", ShaderStageAsString(stage), shader.getInfoLog(), shader.getInfoDebugLog());255return false;256}257258std::vector<unsigned int> spirv;259// Can't fail, parsing worked, "linking" worked.260glslang::SpvOptions options;261options.disableOptimizer = false;262options.optimizeSize = false;263options.generateDebugInfo = false;264glslang::GlslangToSpv(*program.getIntermediate(shaderStage), spirv, &options);265266// For whatever reason, with our config, the above outputs an invalid SPIR-V version, 0.267// Patch it up so spirv-cross accepts it.268spirv[1] = glslang::EShTargetSpv_1_0;269270// Alright, step 1 done. Now let's take this SPIR-V shader and output in our desired format.271272switch (destLang) {273#ifdef _WIN32274case HLSL_D3D9:275{276spirv_cross::CompilerHLSL hlsl(spirv);277spirv_cross::CompilerHLSL::Options options{};278options.shader_model = 30;279spirv_cross::CompilerGLSL::Options options_common{};280options_common.vertex.fixup_clipspace = true;281hlsl.set_hlsl_options(options);282hlsl.set_common_options(options_common);283std::string raw = hlsl.compile();284*dest = Postprocess(raw, destLang, stage);285return true;286}287case HLSL_D3D11:288{289spirv_cross::CompilerHLSL hlsl(spirv);290spirv_cross::ShaderResources resources = hlsl.get_shader_resources();291292int i = 0;293for (auto &resource : resources.sampled_images) {294const std::string &name = hlsl.get_name(resource.id);295int num;296if (sscanf(name.c_str(), "sampler%d", &num) != 1)297num = i;298hlsl.set_decoration(resource.id, spv::DecorationBinding, num);299i++;300}301spirv_cross::CompilerHLSL::Options options{};302options.shader_model = 50;303spirv_cross::CompilerGLSL::Options options_common{};304options_common.vertex.fixup_clipspace = true;305hlsl.set_hlsl_options(options);306hlsl.set_common_options(options_common);307std::string raw = hlsl.compile();308*dest = Postprocess(raw, destLang, stage);309return true;310}311#endif312case GLSL_1xx:313{314spirv_cross::CompilerGLSL glsl(std::move(spirv));315// The SPIR-V is now parsed, and we can perform reflection on it.316spirv_cross::ShaderResources resources = glsl.get_shader_resources();317// Get all sampled images in the shader.318for (auto &resource : resources.sampled_images) {319unsigned set = glsl.get_decoration(resource.id, spv::DecorationDescriptorSet);320unsigned binding = glsl.get_decoration(resource.id, spv::DecorationBinding);321printf("Image %s at set = %u, binding = %u\n", resource.name.c_str(), set, binding);322// Modify the decoration to prepare it for GLSL.323glsl.unset_decoration(resource.id, spv::DecorationDescriptorSet);324// Some arbitrary remapping if we want.325glsl.set_decoration(resource.id, spv::DecorationBinding, set * 16 + binding);326}327// Set some options.328spirv_cross::CompilerGLSL::Options options;329options.version = 140;330options.es = true;331glsl.set_common_options(options);332333// Compile to GLSL, ready to give to GL driver.334*dest = glsl.compile();335return true;336}337case GLSL_3xx:338{339spirv_cross::CompilerGLSL glsl(std::move(spirv));340// The SPIR-V is now parsed, and we can perform reflection on it.341spirv_cross::ShaderResources resources = glsl.get_shader_resources();342// Set some options.343spirv_cross::CompilerGLSL::Options options;344options.es = desc.gles;345options.version = gl_extensions.GLSLVersion();346// macOS OpenGL 4.1 implementation does not support GL_ARB_shading_language_420pack.347// Prevent explicit binding location emission enabled in SPIRV-Cross by default.348options.enable_420pack_extension = gl_extensions.ARB_shading_language_420pack;349glsl.set_common_options(options);350// Compile to GLSL, ready to give to GL driver.351*dest = glsl.compile();352return true;353}354default:355*errorMessage = StringFromFormat("Unsupported destination language: %s", ShaderLanguageAsString(destLang));356return false;357}358}359360361