From c6f51bbc1e0a4ebb77a0fc6e2e735444ae4f4b50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Mon, 19 Oct 2020 20:38:43 +0200 Subject: [PATCH] Add brute force shader generator tester. Tests D3D11 and Vulkan shaders. Found a potential error with tesselation without normals already. --- GPU/D3D11/D3D11Util.cpp | 12 +- GPU/D3D11/D3D11Util.h | 6 +- GPU/D3D11/FragmentShaderGeneratorD3D11.cpp | 4 +- GPU/D3D11/FragmentShaderGeneratorD3D11.h | 2 +- GPU/D3D11/VertexShaderGeneratorD3D11.cpp | 4 +- GPU/D3D11/VertexShaderGeneratorD3D11.h | 2 +- GPU/Directx9/ShaderManagerDX9.cpp | 4 +- GPU/Directx9/VertexShaderGeneratorDX9.h | 2 +- unittest/TestShaderGenerators.cpp | 174 +++++++++++++++++++++ unittest/UnitTest.cpp | 3 +- unittest/UnitTests.vcxproj | 3 +- unittest/UnitTests.vcxproj.filters | 1 + 12 files changed, 200 insertions(+), 17 deletions(-) create mode 100644 unittest/TestShaderGenerators.cpp diff --git a/GPU/D3D11/D3D11Util.cpp b/GPU/D3D11/D3D11Util.cpp index ee6ad435d3..acbdfc8e35 100644 --- a/GPU/D3D11/D3D11Util.cpp +++ b/GPU/D3D11/D3D11Util.cpp @@ -19,7 +19,7 @@ #include "D3D11Util.h" -static std::vector CompileShaderToBytecode(const char *code, size_t codeSize, const char *target, UINT flags) { +std::vector CompileShaderToBytecodeD3D11(const char *code, size_t codeSize, const char *target, UINT flags) { ID3DBlob *compiledCode = nullptr; ID3DBlob *errorMsgs = nullptr; HRESULT result = ptr_D3DCompile(code, codeSize, nullptr, nullptr, nullptr, "main", target, flags, 0, &compiledCode, &errorMsgs); @@ -31,8 +31,10 @@ static std::vector CompileShaderToBytecode(const char *code, size_t cod errorMsgs->Release(); } if (compiledCode) { + // Success! const uint8_t *buf = (const uint8_t *)compiledCode->GetBufferPointer(); std::vector compiled = std::vector(buf, buf + compiledCode->GetBufferSize()); + _assert_(compiled.size() != 0); compiledCode->Release(); return compiled; } @@ -41,7 +43,7 @@ static std::vector CompileShaderToBytecode(const char *code, size_t cod ID3D11VertexShader *CreateVertexShaderD3D11(ID3D11Device *device, const char *code, size_t codeSize, std::vector *byteCodeOut, D3D_FEATURE_LEVEL featureLevel, UINT flags) { const char *profile = featureLevel <= D3D_FEATURE_LEVEL_9_3 ? "vs_4_0_level_9_1" : "vs_4_0"; - std::vector byteCode = CompileShaderToBytecode(code, codeSize, profile, flags); + std::vector byteCode = CompileShaderToBytecodeD3D11(code, codeSize, profile, flags); if (byteCode.empty()) return nullptr; @@ -54,7 +56,7 @@ ID3D11VertexShader *CreateVertexShaderD3D11(ID3D11Device *device, const char *co ID3D11PixelShader *CreatePixelShaderD3D11(ID3D11Device *device, const char *code, size_t codeSize, D3D_FEATURE_LEVEL featureLevel, UINT flags) { const char *profile = featureLevel <= D3D_FEATURE_LEVEL_9_3 ? "ps_4_0_level_9_1" : "ps_4_0"; - std::vector byteCode = CompileShaderToBytecode(code, codeSize, profile, flags); + std::vector byteCode = CompileShaderToBytecodeD3D11(code, codeSize, profile, flags); if (byteCode.empty()) return nullptr; @@ -66,7 +68,7 @@ ID3D11PixelShader *CreatePixelShaderD3D11(ID3D11Device *device, const char *code ID3D11ComputeShader *CreateComputeShaderD3D11(ID3D11Device *device, const char *code, size_t codeSize, D3D_FEATURE_LEVEL featureLevel, UINT flags) { if (featureLevel <= D3D_FEATURE_LEVEL_9_3) return nullptr; - std::vector byteCode = CompileShaderToBytecode(code, codeSize, "cs_4_0", flags); + std::vector byteCode = CompileShaderToBytecodeD3D11(code, codeSize, "cs_4_0", flags); if (byteCode.empty()) return nullptr; @@ -78,7 +80,7 @@ ID3D11ComputeShader *CreateComputeShaderD3D11(ID3D11Device *device, const char * ID3D11GeometryShader *CreateGeometryShaderD3D11(ID3D11Device *device, const char *code, size_t codeSize, D3D_FEATURE_LEVEL featureLevel, UINT flags) { if (featureLevel <= D3D_FEATURE_LEVEL_9_3) return nullptr; - std::vector byteCode = CompileShaderToBytecode(code, codeSize, "gs_5_0", flags); + std::vector byteCode = CompileShaderToBytecodeD3D11(code, codeSize, "gs_5_0", flags); if (byteCode.empty()) return nullptr; diff --git a/GPU/D3D11/D3D11Util.h b/GPU/D3D11/D3D11Util.h index 11dba6ee4c..e9ae76ad42 100644 --- a/GPU/D3D11/D3D11Util.h +++ b/GPU/D3D11/D3D11Util.h @@ -18,6 +18,8 @@ #pragma once #include +#include + #include class PushBufferD3D11 { @@ -71,6 +73,8 @@ private: bool nextMapDiscard_ = false; }; +std::vector CompileShaderToBytecodeD3D11(const char *code, size_t codeSize, const char *target, UINT flags); + ID3D11VertexShader *CreateVertexShaderD3D11(ID3D11Device *device, const char *code, size_t codeSize, std::vector *byteCodeOut, D3D_FEATURE_LEVEL featureLevel, UINT flags = 0); ID3D11PixelShader *CreatePixelShaderD3D11(ID3D11Device *device, const char *code, size_t codeSize, D3D_FEATURE_LEVEL featureLevel, UINT flags = 0); ID3D11ComputeShader *CreateComputeShaderD3D11(ID3D11Device *device, const char *code, size_t codeSize, D3D_FEATURE_LEVEL featureLevel, UINT flags = 0); @@ -95,4 +99,4 @@ public: if (!SUCCEEDED((x))) \ Crash(); -extern StockObjectsD3D11 stockD3D11; \ No newline at end of file +extern StockObjectsD3D11 stockD3D11; diff --git a/GPU/D3D11/FragmentShaderGeneratorD3D11.cpp b/GPU/D3D11/FragmentShaderGeneratorD3D11.cpp index 07d37c8d5c..d63a0c4b41 100644 --- a/GPU/D3D11/FragmentShaderGeneratorD3D11.cpp +++ b/GPU/D3D11/FragmentShaderGeneratorD3D11.cpp @@ -19,6 +19,6 @@ #include "GPU/D3D11/FragmentShaderGeneratorD3D11.h" #include "GPU/Directx9/PixelShaderGeneratorDX9.h" -void GenerateFragmentShaderD3D11(const FShaderID &id, char *buffer, ShaderLanguage lang) { - DX9::GenerateFragmentShaderHLSL(id, buffer, lang); +bool GenerateFragmentShaderD3D11(const FShaderID &id, char *buffer, ShaderLanguage lang) { + return DX9::GenerateFragmentShaderHLSL(id, buffer, lang); } diff --git a/GPU/D3D11/FragmentShaderGeneratorD3D11.h b/GPU/D3D11/FragmentShaderGeneratorD3D11.h index cf94a63200..5e99cae958 100644 --- a/GPU/D3D11/FragmentShaderGeneratorD3D11.h +++ b/GPU/D3D11/FragmentShaderGeneratorD3D11.h @@ -19,4 +19,4 @@ #include "GPU/Common/ShaderId.h" -void GenerateFragmentShaderD3D11(const FShaderID &id, char *buffer, ShaderLanguage lang); +bool GenerateFragmentShaderD3D11(const FShaderID &id, char *buffer, ShaderLanguage lang); diff --git a/GPU/D3D11/VertexShaderGeneratorD3D11.cpp b/GPU/D3D11/VertexShaderGeneratorD3D11.cpp index 75affc997d..87c24e33f5 100644 --- a/GPU/D3D11/VertexShaderGeneratorD3D11.cpp +++ b/GPU/D3D11/VertexShaderGeneratorD3D11.cpp @@ -19,6 +19,6 @@ #include "GPU/D3D11/VertexShaderGeneratorD3D11.h" #include "GPU/Directx9/VertexShaderGeneratorDX9.h" -void GenerateVertexShaderD3D11(const VShaderID &id, char *buffer, ShaderLanguage lang) { - DX9::GenerateVertexShaderHLSL(id, buffer, lang); +bool GenerateVertexShaderD3D11(const VShaderID &id, char *buffer, ShaderLanguage lang) { + return DX9::GenerateVertexShaderHLSL(id, buffer, lang); } diff --git a/GPU/D3D11/VertexShaderGeneratorD3D11.h b/GPU/D3D11/VertexShaderGeneratorD3D11.h index fcf6f7f749..d595a9e146 100644 --- a/GPU/D3D11/VertexShaderGeneratorD3D11.h +++ b/GPU/D3D11/VertexShaderGeneratorD3D11.h @@ -19,4 +19,4 @@ #include "GPU/Common/ShaderId.h" -void GenerateVertexShaderD3D11(const VShaderID &id, char *buffer, ShaderLanguage lang); +bool GenerateVertexShaderD3D11(const VShaderID &id, char *buffer, ShaderLanguage lang); diff --git a/GPU/Directx9/ShaderManagerDX9.cpp b/GPU/Directx9/ShaderManagerDX9.cpp index f7c331907e..13d998c3d5 100644 --- a/GPU/Directx9/ShaderManagerDX9.cpp +++ b/GPU/Directx9/ShaderManagerDX9.cpp @@ -587,7 +587,7 @@ VSShader *ShaderManagerDX9::ApplyShader(bool useHWTransform, bool useHWTessellat VSShader *vs; if (vsIter == vsCache_.end()) { // Vertex shader not in cache. Let's compile it. - GenerateVertexShaderHLSL(VSID, codeBuffer_); + GenerateVertexShaderHLSL(VSID, codeBuffer_, HLSL_DX9); vs = new VSShader(device_, VSID, codeBuffer_, useHWTransform); if (vs->Failed()) { @@ -605,7 +605,7 @@ VSShader *ShaderManagerDX9::ApplyShader(bool useHWTransform, bool useHWTessellat // next time and we'll do this over and over... // Can still work with software transform. - GenerateVertexShaderHLSL(VSID, codeBuffer_); + GenerateVertexShaderHLSL(VSID, codeBuffer_, HLSL_DX9); vs = new VSShader(device_, VSID, codeBuffer_, false); } diff --git a/GPU/Directx9/VertexShaderGeneratorDX9.h b/GPU/Directx9/VertexShaderGeneratorDX9.h index aecc113ea3..b27f4dedeb 100644 --- a/GPU/Directx9/VertexShaderGeneratorDX9.h +++ b/GPU/Directx9/VertexShaderGeneratorDX9.h @@ -21,7 +21,7 @@ namespace DX9 { - void GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage lang = HLSL_DX9); + bool GenerateVertexShaderHLSL(const VShaderID &id, char *buffer, ShaderLanguage lang); enum { CONST_VS_PROJ = 0, diff --git a/unittest/TestShaderGenerators.cpp b/unittest/TestShaderGenerators.cpp new file mode 100644 index 0000000000..a045bd1359 --- /dev/null +++ b/unittest/TestShaderGenerators.cpp @@ -0,0 +1,174 @@ +#include "Common/StringUtils.h" + +#include "GPU/Common/ShaderId.h" +#include "GPU/Common/ShaderCommon.h" +#include "Common/Data/Random/Rng.h" + +#include "GPU/Vulkan/VulkanContext.h" + +#include "GPU/Vulkan/FragmentShaderGeneratorVulkan.h" +#include "GPU/D3D11/FragmentShaderGeneratorD3D11.h" +#include "GPU/Directx9/PixelShaderGeneratorDX9.h" +#include "GPU/GLES/FragmentShaderGeneratorGLES.h" + +#include "GPU/Vulkan/VertexShaderGeneratorVulkan.h" +#include "GPU/D3D11/VertexShaderGeneratorD3D11.h" +#include "GPU/Directx9/VertexShaderGeneratorDX9.h" +#include "GPU/GLES/VertexShaderGeneratorGLES.h" + +#include "GPU/D3D11/D3D11Util.h" +#include "GPU/D3D11/D3D11Loader.h" + +#include "GPU/D3D9/D3DCompilerLoader.h" +#include "GPU/D3D9/D3D9ShaderCompiler.h" + +bool GenerateFShader(FShaderID id, char *buffer, ShaderLanguage lang) { + switch (lang) { + case ShaderLanguage::HLSL_D3D11: + return GenerateFragmentShaderD3D11(id, buffer, ShaderLanguage::HLSL_D3D11); + case ShaderLanguage::HLSL_DX9: + DX9::GenerateFragmentShaderHLSL(id, buffer, ShaderLanguage::HLSL_DX9); + // TODO: Need a device :( Returning false here so it doesn't get tried. + return false; + case ShaderLanguage::GLSL_VULKAN: + return GenerateVulkanGLSLFragmentShader(id, buffer, 0); + case ShaderLanguage::GLSL_140: + case ShaderLanguage::GLSL_300: + // TODO: Need a device - except that maybe glslang could be used to verify these .... + return false; + default: + return false; + } +} + +bool GenerateVShader(VShaderID id, char *buffer, ShaderLanguage lang) { + switch (lang) { + case ShaderLanguage::HLSL_D3D11: + return GenerateVertexShaderD3D11(id, buffer, ShaderLanguage::HLSL_D3D11); + case ShaderLanguage::HLSL_DX9: + DX9::GenerateVertexShaderHLSL(id, buffer, ShaderLanguage::HLSL_DX9); + // TODO: Need a device :( Returning false here so it doesn't get tried. + return false; + // return DX9::GenerateFragmentShaderHLSL(id, buffer, ShaderLanguage::HLSL_DX9); + case ShaderLanguage::GLSL_VULKAN: + return GenerateVulkanGLSLVertexShader(id, buffer); + default: + return false; + } +} + +bool TestCompileShader(const char *buffer, ShaderLanguage lang, bool vertex) { + switch (lang) { + case ShaderLanguage::HLSL_D3D11: + { + auto output = CompileShaderToBytecodeD3D11(buffer, strlen(buffer), vertex ? "vs_4_0" : "ps_4_0", 0); + return !output.empty(); + } + case ShaderLanguage::HLSL_DX9: + return false; + case ShaderLanguage::GLSL_VULKAN: + { + std::vector spirv; + std::string errorMessage; + bool result = GLSLtoSPV(vertex ? VK_SHADER_STAGE_VERTEX_BIT : VK_SHADER_STAGE_FRAGMENT_BIT, buffer, spirv, &errorMessage); + if (!result) { + printf("GLSLtoSPV ERROR:\n%s\n\n", errorMessage.c_str()); + } + return result; + } + case ShaderLanguage::GLSL_140: + + return false; + case ShaderLanguage::GLSL_300: + + return false; + default: + return false; + } +} + +bool TestShaderGenerators() { + LoadD3D11(); + init_glslang(); + LoadD3DCompilerDynamic(); + + ShaderLanguage languages[] = { + ShaderLanguage::HLSL_D3D11, + ShaderLanguage::GLSL_VULKAN, + ShaderLanguage::GLSL_140, + ShaderLanguage::GLSL_300, + ShaderLanguage::HLSL_DX9, + }; + const int numLanguages = ARRAY_SIZE(languages); + + char *buffer[numLanguages]; + + for (int i = 0; i < numLanguages; i++) { + buffer[i] = new char[65536]; + } + // Generate a bunch of random fragment shader IDs, try to generate shader source. + // Then compile it and check that it's ok. + GMRng rng; + int successes = 0; + int count = 200; + for (int i = 0; i < count; i++) { + uint32_t bottom = rng.R32(); + uint32_t top = rng.R32(); + FShaderID id; + id.d[0] = bottom; + id.d[1] = top; + + bool generateSuccess[numLanguages]{}; + + for (int j = 0; j < numLanguages; j++) { + generateSuccess[j] = GenerateFShader(id, buffer[j], languages[j]); + } + + // 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]) { + if (!TestCompileShader(buffer[j], languages[j], false)) { + printf("Error compiling fragment shader:\n\n%s\n\n", LineNumberString(buffer[j]).c_str()); + return false; + } + successes++; + } + } + } + + printf("%d/%d fragment shaders generated (it's normal that it's not all, there are invalid bit combos)\n", successes, count * numLanguages); + + // Now try vertex shaders. + for (int i = 0; i < count; i++) { + uint32_t bottom = rng.R32(); + uint32_t top = rng.R32(); + VShaderID id; + id.d[0] = bottom; + id.d[1] = top; + + bool generateSuccess[numLanguages]{}; + + for (int j = 0; j < numLanguages; j++) { + generateSuccess[j] = GenerateVShader(id, buffer[j], languages[j]); + } + + // 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]) { + if (!TestCompileShader(buffer[j], languages[j], true)) { + printf("Error compiling vertex shader:\n\n%s\n\n", LineNumberString(buffer[j]).c_str()); + return false; + } + successes++; + } + } + } + + for (int i = 0; i < numLanguages; i++) { + delete[] buffer[i]; + } + + return true; +} diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index a310999360..b5beb3cefa 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -571,6 +571,7 @@ struct TestItem { bool TestArmEmitter(); bool TestArm64Emitter(); bool TestX64Emitter(); +bool TestShaderGenerators(); TestItem availableTests[] = { #if PPSSPP_ARCH(ARM64) || PPSSPP_ARCH(AMD64) || PPSSPP_ARCH(X86) @@ -593,7 +594,7 @@ TestItem availableTests[] = { TEST_ITEM(ParseLBN), TEST_ITEM(QuickTexHash), TEST_ITEM(CLZ), - TEST_ITEM(MemMap), + TEST_ITEM(ShaderGenerators), }; int main(int argc, const char *argv[]) { diff --git a/unittest/UnitTests.vcxproj b/unittest/UnitTests.vcxproj index a401a081b7..f585ee5feb 100644 --- a/unittest/UnitTests.vcxproj +++ b/unittest/UnitTests.vcxproj @@ -382,6 +382,7 @@ true true + @@ -426,4 +427,4 @@ - + \ No newline at end of file diff --git a/unittest/UnitTests.vcxproj.filters b/unittest/UnitTests.vcxproj.filters index fd685e86b1..511e700f67 100644 --- a/unittest/UnitTests.vcxproj.filters +++ b/unittest/UnitTests.vcxproj.filters @@ -11,6 +11,7 @@ Windows +