From cdee10fe86ddeffbfc643715586c12cec0d571e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 26 Oct 2021 09:56:14 +0200 Subject: [PATCH] Vulkan: Basic geoshader code generation. --- CMakeLists.txt | 2 + Common/GPU/Vulkan/VulkanContext.cpp | 2 - GPU/Common/GeometryShaderGenerator.cpp | 79 ++++++++++++++++++++++ GPU/Common/GeometryShaderGenerator.h | 5 ++ GPU/GPU.vcxproj | 2 + GPU/GPU.vcxproj.filters | 6 ++ GPU/Vulkan/GPU_Vulkan.cpp | 8 +++ GPU/Vulkan/PipelineManagerVulkan.cpp | 2 +- GPU/Vulkan/ShaderManagerVulkan.cpp | 19 +++++- UWP/GPU_UWP/GPU_UWP.vcxproj | 2 + UWP/GPU_UWP/GPU_UWP.vcxproj.filters | 2 + android/jni/Android.mk | 1 + libretro/Makefile.common | 1 + unittest/TestShaderGenerators.cpp | 91 ++++++++++++++++++++++++++ 14 files changed, 217 insertions(+), 5 deletions(-) create mode 100644 GPU/Common/GeometryShaderGenerator.cpp create mode 100644 GPU/Common/GeometryShaderGenerator.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8cec0f281d..52fb7e40c9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1562,6 +1562,8 @@ set(GPU_SOURCES GPU/Common/FragmentShaderGenerator.h GPU/Common/VertexShaderGenerator.cpp GPU/Common/VertexShaderGenerator.h + GPU/Common/GeometryShaderGenerator.cpp + GPU/Common/GeometryShaderGenerator.h GPU/Common/FramebufferManagerCommon.cpp GPU/Common/FramebufferManagerCommon.h GPU/Common/GPUDebugInterface.cpp diff --git a/Common/GPU/Vulkan/VulkanContext.cpp b/Common/GPU/Vulkan/VulkanContext.cpp index 1379cb9f21..c2140c5663 100644 --- a/Common/GPU/Vulkan/VulkanContext.cpp +++ b/Common/GPU/Vulkan/VulkanContext.cpp @@ -600,8 +600,6 @@ void VulkanContext::ChooseDevice(int physical_device) { deviceFeatures_.enabled.samplerAnisotropy = deviceFeatures_.available.samplerAnisotropy; deviceFeatures_.enabled.shaderClipDistance = deviceFeatures_.available.shaderClipDistance; deviceFeatures_.enabled.shaderCullDistance = deviceFeatures_.available.shaderCullDistance; - // For easy wireframe mode, someday. - deviceFeatures_.enabled.fillModeNonSolid = deviceFeatures_.available.fillModeNonSolid; deviceFeatures_.enabled.geometryShader = deviceFeatures_.available.geometryShader; GetDeviceLayerExtensionList(nullptr, device_extension_properties_); diff --git a/GPU/Common/GeometryShaderGenerator.cpp b/GPU/Common/GeometryShaderGenerator.cpp new file mode 100644 index 0000000000..fdd64c8077 --- /dev/null +++ b/GPU/Common/GeometryShaderGenerator.cpp @@ -0,0 +1,79 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include +#include +#include + +#include "Common/StringUtils.h" +#include "Common/GPU/OpenGL/GLFeatures.h" +#include "Common/GPU/ShaderWriter.h" +#include "Common/GPU/thin3d.h" +#include "Core/Config.h" +#include "GPU/ge_constants.h" +#include "GPU/GPUState.h" +#include "GPU/Common/ShaderId.h" +#include "GPU/Common/ShaderUniforms.h" +#include "GPU/Common/GeometryShaderGenerator.h" + +#undef WRITE + +#define WRITE(p, ...) p.F(__VA_ARGS__) + +bool GenerateGeometryShader(const GShaderID &id, char *buffer, const ShaderLanguageDesc &compat, const Draw::Bugs bugs, std::string *errorString) { + std::vector gl_exts; + if (ShaderLanguageIsOpenGL(compat.shaderLanguage)) { + if (gl_extensions.EXT_gpu_shader4) { + gl_exts.push_back("#extension GL_EXT_gpu_shader4 : enable"); + } + } + + ShaderWriter p(buffer, compat, ShaderStage::Geometry, gl_exts.data(), gl_exts.size()); + p.C("layout(triangles) in;\n"); + p.C("layout(triangle_strip, max_vertices = 3) out;\n"); + + std::vector varyings, outVaryings; + + if (id.Bit(GS_BIT_DO_TEXTURE)) { + varyings.push_back(VaryingDef{ "vec3", "v_texcoord", Draw::SEM_TEXCOORD0, 0, "highp" }); + outVaryings.push_back(VaryingDef{ "vec3", "v_texcoordOut", Draw::SEM_TEXCOORD0, 0, "highp" }); + } + varyings.push_back(VaryingDef{ "vec4", "v_color0", Draw::SEM_COLOR0, 1, "lowp" }); + outVaryings.push_back(VaryingDef{ "vec4", "v_color0Out", Draw::SEM_COLOR0, 1, "lowp" }); + if (id.Bit(GS_BIT_LMODE)) { + varyings.push_back(VaryingDef{ "vec3", "v_color1", Draw::SEM_COLOR1, 2, "lowp" }); + outVaryings.push_back(VaryingDef{ "vec3", "v_color1Out", Draw::SEM_COLOR1, 2, "lowp" }); + } + varyings.push_back(VaryingDef{ "float", "v_fogdepth", Draw::SEM_TEXCOORD1, 3, "highp" }); + outVaryings.push_back(VaryingDef{ "float", "v_fogdepthOut", Draw::SEM_TEXCOORD1, 3, "highp" }); + + p.BeginGSMain(varyings, outVaryings); + + p.C(" for (int i = 0; i < gl_in.length(); i++) {\n"); + p.C(" gl_Position = gl_in[i].gl_Position;\n"); // copy attributes + for (size_t i = 0; i < varyings.size(); i++) { + VaryingDef &in = varyings[i]; + VaryingDef &out = outVaryings[i]; + p.F(" %s = %s[i];\n", outVaryings[i].name, varyings[i].name); + } + p.C(" EmitVertex();\n"); + p.C(" }\n"); + + p.EndGSMain(); + + return true; +} diff --git a/GPU/Common/GeometryShaderGenerator.h b/GPU/Common/GeometryShaderGenerator.h new file mode 100644 index 0000000000..9848ffde18 --- /dev/null +++ b/GPU/Common/GeometryShaderGenerator.h @@ -0,0 +1,5 @@ +#pragma once + +#include "GPU/Common/ShaderId.h" + +bool GenerateGeometryShader(const GShaderID &id, char *buffer, const ShaderLanguageDesc &compat, const Draw::Bugs bugs, std::string *errorString); diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index 0480b490d7..808ce53635 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -340,6 +340,7 @@ + @@ -455,6 +456,7 @@ + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index bd28a45e97..e110c20b8b 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -261,6 +261,9 @@ Debugger + + Common + @@ -512,6 +515,9 @@ Debugger + + Common + diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index aa31c93be4..79a2db00d6 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -232,6 +232,14 @@ u32 GPU_Vulkan::CheckGPUFeatures() const { features |= GPU_SUPPORTS_DEPTH_CLAMP; } + if (!draw_->GetBugs().Has(Draw::Bugs::BROKEN_NAN_IN_CONDITIONAL)) { + const bool disabled = PSP_CoreParameter().compat.flags().DisableRangeCulling; + // Fall back to geometry shader culling. + if (enabledFeatures.geometryShader && !disabled && (features & GPU_SUPPORTS_VS_RANGE_CULLING) == 0) { + features |= GPU_SUPPORTS_GS_CULLING; + } + } + // These are VULKAN_4444_FORMAT and friends. uint32_t fmt4444 = draw_->GetDataFormatSupport(Draw::DataFormat::B4G4R4A4_UNORM_PACK16); uint32_t fmt1555 = draw_->GetDataFormatSupport(Draw::DataFormat::A1R5G5B5_UNORM_PACK16); diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index 4b9ff1e4b3..3d7745b452 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -716,7 +716,7 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha VulkanVertexShader *vs = shaderManager->GetVertexShaderFromID(key.vShaderID); VulkanFragmentShader *fs = shaderManager->GetFragmentShaderFromID(key.fShaderID); VulkanGeometryShader *gs = shaderManager->GetGeometryShaderFromID(key.gShaderID); - if (!vs || !fs) { + if (!vs || !fs || (!gs && key.gShaderID.Bit(GS_BIT_ENABLED))) { failed = true; ERROR_LOG(G3D, "Failed to find vs or fs in of pipeline %d in cache", (int)i); continue; diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index 07d9603322..00649a6be1 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -38,6 +38,7 @@ #include "GPU/ge_constants.h" #include "GPU/Common/FragmentShaderGenerator.h" #include "GPU/Common/VertexShaderGenerator.h" +#include "GPU/Common/GeometryShaderGenerator.h" #include "GPU/Vulkan/ShaderManagerVulkan.h" #include "GPU/Vulkan/DrawEngineVulkan.h" #include "GPU/Vulkan/FramebufferManagerVulkan.h" @@ -305,6 +306,11 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader _dbg_assert_(FSID.Bit(FS_BIT_DO_TEXTURE) == VSID.Bit(VS_BIT_DO_TEXTURE)); _dbg_assert_(FSID.Bit(FS_BIT_FLATSHADE) == VSID.Bit(VS_BIT_FLATSHADE)); + if (GSID.Bit(GS_BIT_ENABLED)) { + _dbg_assert_(GSID.Bit(GS_BIT_LMODE) == VSID.Bit(VS_BIT_LMODE)); + _dbg_assert_(GSID.Bit(GS_BIT_DO_TEXTURE) == VSID.Bit(VS_BIT_DO_TEXTURE)); + } + // Just update uniforms if this is the same shader as last time. if (lastVShader_ != nullptr && lastFShader_ != nullptr && VSID == lastVSID_ && FSID == lastFSID_ && GSID == lastGSID_) { *vshader = lastVShader_; @@ -345,7 +351,11 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader gs = gsCache_.Get(GSID); if (!gs) { // Geometry shader not in cache. Let's compile it. - // TODO + std::string genErrorString; + bool success = GenerateGeometryShader(GSID, codeBuffer_, compat_, draw_->GetBugs(), &genErrorString); + _assert_msg_(success, "GS gen error: %s", genErrorString.c_str()); + gs = new VulkanGeometryShader(vulkan, GSID, codeBuffer_); + gsCache_.Insert(GSID, gs); } } else { gs = nullptr; @@ -529,7 +539,12 @@ bool ShaderManagerVulkan::LoadCache(FILE *f) { ERROR_LOG(G3D, "Vulkan shader cache truncated"); break; } - // TODO: Actually generate geometry shaders. + std::string genErrorString; + if (!GenerateGeometryShader(id, codeBuffer_, compat_, draw_->GetBugs(), &genErrorString)) { + return false; + } + VulkanGeometryShader *gs = new VulkanGeometryShader(vulkan, id, codeBuffer_); + gsCache_.Insert(id, gs); } NOTICE_LOG(G3D, "Loaded %d vertex and %d fragment shaders", header.numVertexShaders, header.numFragmentShaders); diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj b/UWP/GPU_UWP/GPU_UWP.vcxproj index 87aac4d38b..05b7839736 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj @@ -385,6 +385,7 @@ + @@ -446,6 +447,7 @@ + diff --git a/UWP/GPU_UWP/GPU_UWP.vcxproj.filters b/UWP/GPU_UWP/GPU_UWP.vcxproj.filters index dbce632981..2296b16a16 100644 --- a/UWP/GPU_UWP/GPU_UWP.vcxproj.filters +++ b/UWP/GPU_UWP/GPU_UWP.vcxproj.filters @@ -55,6 +55,7 @@ + @@ -116,6 +117,7 @@ + diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 2f86711855..42cae5cb28 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -350,6 +350,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/GPU/Common/PostShader.cpp \ $(SRC)/GPU/Common/ShaderUniforms.cpp \ $(SRC)/GPU/Common/VertexShaderGenerator.cpp \ + $(SRC)/GPU/Common/GeometryShaderGenerator.cpp \ $(SRC)/GPU/Debugger/Breakpoints.cpp \ $(SRC)/GPU/Debugger/Debugger.cpp \ $(SRC)/GPU/Debugger/GECommandTable.cpp \ diff --git a/libretro/Makefile.common b/libretro/Makefile.common index e7dd40be35..8eed277932 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -342,6 +342,7 @@ SOURCES_CXX += \ $(GPUDIR)/Debugger/Stepping.cpp \ $(GPUDIR)/Common/FragmentShaderGenerator.cpp \ $(GPUDIR)/Common/VertexShaderGenerator.cpp \ + $(GPUDIR)/Common/GeometryShaderGenerator.cpp \ $(GPUDIR)/Common/TextureCacheCommon.cpp \ $(GPUDIR)/Common/TextureScalerCommon.cpp \ $(GPUDIR)/Common/SoftwareTransformCommon.cpp \ diff --git a/unittest/TestShaderGenerators.cpp b/unittest/TestShaderGenerators.cpp index 810de85b9e..346fadf643 100644 --- a/unittest/TestShaderGenerators.cpp +++ b/unittest/TestShaderGenerators.cpp @@ -12,6 +12,7 @@ #include "GPU/Common/FragmentShaderGenerator.h" #include "GPU/Common/VertexShaderGenerator.h" +#include "GPU/Common/GeometryShaderGenerator.h" #include "GPU/Common/ReinterpretFramebuffer.h" #include "GPU/Common/StencilCommon.h" #include "GPU/Common/DepalettizeShaderCommon.h" @@ -91,6 +92,32 @@ bool GenerateVShader(VShaderID id, char *buffer, ShaderLanguage lang, Draw::Bugs } } +bool GenerateGShader(GShaderID id, char *buffer, ShaderLanguage lang, Draw::Bugs bugs, std::string *errorString) { + errorString->clear(); + + switch (lang) { + case ShaderLanguage::GLSL_VULKAN: + { + ShaderLanguageDesc compat(ShaderLanguage::GLSL_VULKAN); + return GenerateGeometryShader(id, buffer, compat, bugs, errorString); + } + /* + case ShaderLanguage::GLSL_3xx: + { + ShaderLanguageDesc compat(ShaderLanguage::GLSL_3xx); + return GenerateGeometryShader(id, buffer, compat, bugs, errorString); + } + case ShaderLanguage::HLSL_D3D11: + { + ShaderLanguageDesc compat(ShaderLanguage::HLSL_D3D11); + return GenerateGeometryShader(id, buffer, compat, bugs, errorString); + } + */ + default: + return false; + } +} + static VkShaderStageFlagBits StageToVulkan(ShaderStage stage) { switch (stage) { case ShaderStage::Vertex: return VK_SHADER_STAGE_VERTEX_BIT; @@ -369,6 +396,9 @@ bool TestVertexShaders() { if (!id.Bit(VS_BIT_USE_HW_TRANSFORM)) { id.SetBit(VS_BIT_ENABLE_BONES, 0); } + if (id.Bit(VS_BIT_VERTEX_RANGE_CULLING)) { + continue; + } bool generateSuccess[numLanguages]{}; std::string genErrorString[numLanguages]; @@ -463,6 +493,63 @@ bool TestFragmentShaders() { return true; } + +bool TestGeometryShaders() { + char *buffer[numLanguages]; + + for (int i = 0; i < numLanguages; i++) { + buffer[i] = new char[65536]; + } + GMRng rng; + int successes = 0; + int count = 30; + + Draw::Bugs bugs; + + // Generate a bunch of random fragment shader IDs, try to generate shader source. + // Then compile it and check that it's ok. + for (int i = 0; i < count; i++) { + uint32_t bottom = i << 1; + GShaderID id; + id.d[0] = bottom; + id.d[1] = 0; + + id.SetBit(GS_BIT_ENABLED, true); + + bool generateSuccess[numLanguages]{}; + std::string genErrorString[numLanguages]; + + for (int j = 0; j < numLanguages; j++) { + generateSuccess[j] = GenerateGShader(id, buffer[j], languages[j], bugs, &genErrorString[j]); + if (!genErrorString[j].empty()) { + printf("%s\n", genErrorString[j].c_str()); + } + // We ignore the contents of the error string here, not even gonna try to compile if it errors. + } + + // Now that we have the strings ready for easy comparison (buffer,4 in the watch window), + // let's try to compile them. + for (int j = 0; j < numLanguages; j++) { + if (generateSuccess[j]) { + std::string errorMessage; + if (!TestCompileShader(buffer[j], languages[j], ShaderStage::Geometry, &errorMessage)) { + printf("Error compiling geometry shader:\n\n%s\n\n%s\n", LineNumberString(buffer[j]).c_str(), errorMessage.c_str()); + return false; + } + successes++; + } + } + } + + printf("%d/%d geometry shaders generated (it's normal that it's not all, there are invalid bit combos)\n", successes, count * numLanguages); + + for (int i = 0; i < numLanguages; i++) { + delete[] buffer[i]; + } + return true; +} + + bool TestShaderGenerators() { #if PPSSPP_PLATFORM(WINDOWS) LoadD3D11(); @@ -476,6 +563,10 @@ bool TestShaderGenerators() { return false; } + if (!TestGeometryShaders()) { + return false; + } + if (!TestReinterpretShaders()) { return false; }