ppsspp/unittest/TestShaderGenerators.cpp
Henrik Rydgård 4da2ca0935 Implement shader blending for D3D9
This was easy, dunno why I never got around to it before..
I guess I didn't know about VPOS.

This does raise our minimum shader model requirement to ps_3_0.
2022-08-30 12:19:56 +02:00

475 lines
13 KiB
C++

#include "ppsspp_config.h"
#include <algorithm>
#include "Common/StringUtils.h"
#include "GPU/Common/ShaderId.h"
#include "GPU/Common/ShaderCommon.h"
#include "GPU/Common/GPUStateUtils.h"
#include "Common/Data/Random/Rng.h"
#include "GPU/Vulkan/VulkanContext.h"
#include "GPU/Common/FragmentShaderGenerator.h"
#include "GPU/Common/VertexShaderGenerator.h"
#include "GPU/Common/ReinterpretFramebuffer.h"
#include "GPU/Common/StencilCommon.h"
#include "GPU/Common/DepalettizeShaderCommon.h"
#if PPSSPP_PLATFORM(WINDOWS)
#include "GPU/D3D11/D3D11Util.h"
#include "GPU/D3D11/D3D11Loader.h"
#include "GPU/D3D9/D3DCompilerLoader.h"
#include "GPU/D3D9/D3D9ShaderCompiler.h"
#endif
bool GenerateFShader(FShaderID id, char *buffer, ShaderLanguage lang, Draw::Bugs bugs, std::string *errorString) {
uint64_t uniformMask;
switch (lang) {
case ShaderLanguage::GLSL_VULKAN:
{
ShaderLanguageDesc compat(ShaderLanguage::GLSL_VULKAN);
return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, errorString);
}
case ShaderLanguage::GLSL_1xx:
{
ShaderLanguageDesc compat(ShaderLanguage::GLSL_1xx);
return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, errorString);
}
case ShaderLanguage::GLSL_3xx:
{
ShaderLanguageDesc compat(ShaderLanguage::GLSL_1xx);
return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, errorString);
}
case ShaderLanguage::HLSL_D3D9:
{
ShaderLanguageDesc compat(ShaderLanguage::HLSL_D3D9);
return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, errorString);
}
case ShaderLanguage::HLSL_D3D11:
{
ShaderLanguageDesc compat(ShaderLanguage::HLSL_D3D11);
return GenerateFragmentShader(id, buffer, compat, bugs, &uniformMask, errorString);
}
default:
return false;
}
}
bool GenerateVShader(VShaderID id, char *buffer, ShaderLanguage lang, Draw::Bugs bugs, std::string *errorString) {
uint32_t attrMask;
uint64_t uniformMask;
switch (lang) {
case ShaderLanguage::GLSL_VULKAN:
{
ShaderLanguageDesc compat(ShaderLanguage::GLSL_VULKAN);
return GenerateVertexShader(id, buffer, compat, bugs, &attrMask, &uniformMask, errorString);
}
case ShaderLanguage::GLSL_1xx:
{
ShaderLanguageDesc compat(ShaderLanguage::GLSL_1xx);
return GenerateVertexShader(id, buffer, compat, bugs, &attrMask, &uniformMask, errorString);
}
case ShaderLanguage::GLSL_3xx:
{
ShaderLanguageDesc compat(ShaderLanguage::GLSL_1xx);
return GenerateVertexShader(id, buffer, compat, bugs, &attrMask, &uniformMask, errorString);
}
case ShaderLanguage::HLSL_D3D9:
{
ShaderLanguageDesc compat(ShaderLanguage::HLSL_D3D9);
return GenerateVertexShader(id, buffer, compat, bugs, &attrMask, &uniformMask, errorString);
}
case ShaderLanguage::HLSL_D3D11:
{
ShaderLanguageDesc compat(ShaderLanguage::HLSL_D3D11);
return GenerateVertexShader(id, buffer, compat, bugs, &attrMask, &uniformMask, errorString);
}
default:
return false;
}
}
bool TestCompileShader(const char *buffer, ShaderLanguage lang, ShaderStage stage, std::string *errorMessage) {
std::vector<uint32_t> spirv;
switch (lang) {
#if PPSSPP_PLATFORM(WINDOWS)
case ShaderLanguage::HLSL_D3D11:
{
auto output = CompileShaderToBytecodeD3D11(buffer, strlen(buffer), stage == ShaderStage::Vertex ? "vs_4_0" : "ps_4_0", 0);
return !output.empty();
}
case ShaderLanguage::HLSL_D3D9:
{
LPD3DBLOB blob = CompileShaderToByteCodeD3D9(buffer, stage == ShaderStage::Vertex ? "vs_3_0" : "ps_3_0", errorMessage);
if (blob) {
blob->Release();
return true;
} else {
return false;
}
}
#endif
case ShaderLanguage::GLSL_VULKAN:
return GLSLtoSPV(stage == ShaderStage::Vertex ? VK_SHADER_STAGE_VERTEX_BIT : VK_SHADER_STAGE_FRAGMENT_BIT, buffer, GLSLVariant::VULKAN, spirv, errorMessage);
case ShaderLanguage::GLSL_1xx:
return GLSLtoSPV(stage == ShaderStage::Vertex ? VK_SHADER_STAGE_VERTEX_BIT : VK_SHADER_STAGE_FRAGMENT_BIT, buffer, GLSLVariant::GL140, spirv, errorMessage);
case ShaderLanguage::GLSL_3xx:
return GLSLtoSPV(stage == ShaderStage::Vertex ? VK_SHADER_STAGE_VERTEX_BIT : VK_SHADER_STAGE_FRAGMENT_BIT, buffer, GLSLVariant::GLES300, spirv, errorMessage);
default:
return false;
}
}
void PrintDiff(const char *a, const char *b) {
// Stupidest diff ever: Just print both lines, and a few around it, when we find a mismatch.
std::vector<std::string> a_lines;
std::vector<std::string> b_lines;
SplitString(a, '\n', a_lines);
SplitString(b, '\n', b_lines);
for (size_t i = 0; i < a_lines.size() && i < b_lines.size(); i++) {
if (a_lines[i] != b_lines[i]) {
// Print some context
for (size_t j = std::max((int)i - 4, 0); j < i; j++) {
printf("%s\n", a_lines[j].c_str());
}
printf("DIFF found at line %d:\n", (int)i);
printf("a: %s\n", a_lines[i].c_str());
printf("b: %s\n", b_lines[i].c_str());
printf("...continues...\n");
for (size_t j = i + 1; j < i + 5 && j < a_lines.size() && j < b_lines.size(); j++) {
printf("a: %s\n", a_lines[j].c_str());
printf("b: %s\n", b_lines[j].c_str());
}
printf("==================\n");
return;
}
}
}
const char *ShaderLanguageToString(ShaderLanguage lang) {
switch (lang) {
case HLSL_D3D11: return "HLSL_D3D11";
case HLSL_D3D9: return "HLSL_D3D9";
case GLSL_VULKAN: return "GLSL_VULKAN";
case GLSL_1xx: return "GLSL_1xx";
case GLSL_3xx: return "GLSL_3xx";
default: return "N/A";
}
}
bool TestReinterpretShaders() {
Draw::Bugs bugs;
ShaderLanguage languages[] = {
#if PPSSPP_PLATFORM(WINDOWS)
ShaderLanguage::HLSL_D3D11,
#endif
ShaderLanguage::GLSL_VULKAN,
ShaderLanguage::GLSL_3xx,
};
GEBufferFormat fmts[3] = {
GE_FORMAT_565,
GE_FORMAT_5551,
GE_FORMAT_4444,
};
char *buffer = new char[65536];
// Generate all despite failures - it's only 6.
bool failed = false;
for (int k = 0; k < ARRAY_SIZE(languages); k++) {
printf("=== %s ===\n\n", ShaderLanguageToString(languages[k]));
ShaderLanguageDesc desc(languages[k]);
std::string errorMessage;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == j)
continue; // useless shader!
ShaderWriter writer(buffer, desc, ShaderStage::Fragment, nullptr, 0);
GenerateReinterpretFragmentShader(writer, fmts[i], fmts[j]);
if (!TestCompileShader(buffer, languages[k], ShaderStage::Fragment, &errorMessage)) {
printf("Error compiling fragment shader %d:\n\n%s\n\n%s\n", (int)j, LineNumberString(buffer).c_str(), errorMessage.c_str());
failed = true;
return false;
} else {
printf("===\n%s\n===\n", buffer);
}
}
}
}
delete[] buffer;
return !failed;
}
bool TestStencilShaders() {
Draw::Bugs bugs;
ShaderLanguage languages[] = {
#if PPSSPP_PLATFORM(WINDOWS)
ShaderLanguage::HLSL_D3D9,
ShaderLanguage::HLSL_D3D11,
#endif
ShaderLanguage::GLSL_VULKAN,
ShaderLanguage::GLSL_3xx,
};
char *buffer = new char[65536];
bool failed = false;
for (int k = 0; k < ARRAY_SIZE(languages); k++) {
printf("=== %s ===\n\n", ShaderLanguageToString(languages[k]));
ShaderLanguageDesc desc(languages[k]);
std::string errorMessage;
// Generate all despite failures - it's only 6.
GenerateStencilFs(buffer, desc, bugs);
if (!TestCompileShader(buffer, languages[k], ShaderStage::Fragment, &errorMessage)) {
printf("Error compiling stencil shader:\n\n%s\n\n%s\n", LineNumberString(buffer).c_str(), errorMessage.c_str());
failed = true;
return false;
} else {
printf("===\n%s\n===\n", buffer);
}
GenerateStencilVs(buffer, desc);
if (!TestCompileShader(buffer, languages[k], ShaderStage::Vertex, &errorMessage)) {
printf("Error compiling stencil shader:\n\n%s\n\n%s\n", LineNumberString(buffer).c_str(), errorMessage.c_str());
failed = true;
return false;
} else {
printf("===\n%s\n===\n", buffer);
}
}
delete[] buffer;
return !failed;
}
bool TestDepalShaders() {
Draw::Bugs bugs;
ShaderLanguage languages[] = {
#if PPSSPP_PLATFORM(WINDOWS)
ShaderLanguage::HLSL_D3D9,
ShaderLanguage::HLSL_D3D11,
#endif
ShaderLanguage::GLSL_VULKAN,
ShaderLanguage::GLSL_3xx,
ShaderLanguage::GLSL_1xx,
};
char *buffer = new char[65536];
bool failed = false;
for (int k = 0; k < ARRAY_SIZE(languages); k++) {
printf("=== %s ===\n\n", ShaderLanguageToString(languages[k]));
ShaderLanguageDesc desc(languages[k]);
std::string errorMessage;
// TODO: Try some different configurations of the fragment shader.
// But first just try one.
DepalConfig config{};
config.clutFormat = GE_CMODE_16BIT_ABGR4444;
config.shift = 8;
config.startPos = 64;
config.mask = 0xFF;
config.bufferFormat = GE_FORMAT_8888;
config.textureFormat = GE_TFMT_CLUT32;
config.depthUpperBits = 0;
ShaderWriter writer(buffer, desc, ShaderStage::Fragment);
GenerateDepalFs(writer, config);
if (!TestCompileShader(buffer, languages[k], ShaderStage::Fragment, &errorMessage)) {
printf("Error compiling depal shader:\n\n%s\n\n%s\n", LineNumberString(buffer).c_str(), errorMessage.c_str());
failed = true;
return false;
} else {
printf("===\n%s\n===\n", buffer);
}
}
delete[] buffer;
return !failed;
}
const ShaderLanguage languages[] = {
#if PPSSPP_PLATFORM(WINDOWS)
ShaderLanguage::HLSL_D3D9,
ShaderLanguage::HLSL_D3D11,
#endif
ShaderLanguage::GLSL_VULKAN,
ShaderLanguage::GLSL_1xx,
ShaderLanguage::GLSL_3xx,
};
const int numLanguages = ARRAY_SIZE(languages);
bool TestVertexShaders() {
char *buffer[numLanguages];
for (int i = 0; i < numLanguages; i++) {
buffer[i] = new char[65536];
}
GMRng rng;
int successes = 0;
int count = 700;
Draw::Bugs bugs;
// Generate a bunch of random vertex 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 = rng.R32();
uint32_t top = rng.R32();
VShaderID id;
id.d[0] = bottom;
id.d[1] = top;
// The generated bits need some adjustment:
// We don't use these bits in the HLSL shader generator.
id.SetBits(VS_BIT_WEIGHT_FMTSCALE, 2, 0);
// If mode is through, we won't do hardware transform.
if (id.Bit(VS_BIT_IS_THROUGH)) {
id.SetBit(VS_BIT_USE_HW_TRANSFORM, 0);
}
if (!id.Bit(VS_BIT_USE_HW_TRANSFORM)) {
id.SetBit(VS_BIT_ENABLE_BONES, 0);
}
bool generateSuccess[numLanguages]{};
std::string genErrorString[numLanguages];
for (int j = 0; j < numLanguages; j++) {
generateSuccess[j] = GenerateVShader(id, buffer[j], languages[j], bugs, &genErrorString[j]);
if (!genErrorString[j].empty()) {
printf("%s\n", genErrorString[j].c_str());
}
}
// 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::Vertex, &errorMessage)) {
printf("Error compiling vertex shader %d:\n\n%s\n\n%s\n", (int)j, LineNumberString(buffer[j]).c_str(), errorMessage.c_str());
return false;
}
successes++;
}
}
}
printf("%d/%d vertex 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 TestFragmentShaders() {
char *buffer[numLanguages];
for (int i = 0; i < numLanguages; i++) {
buffer[i] = new char[65536];
}
GMRng rng;
int successes = 0;
int count = 300;
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 = rng.R32();
uint32_t top = rng.R32();
FShaderID id;
id.d[0] = bottom;
id.d[1] = top;
// bits we don't need to test because they are irrelevant on d3d11
id.SetBit(FS_BIT_NO_DEPTH_CANNOT_DISCARD_STENCIL, false);
id.SetBit(FS_BIT_SHADER_DEPAL, false);
// DX9 disabling:
if (static_cast<ReplaceAlphaType>(id.Bits(FS_BIT_STENCIL_TO_ALPHA, 2)) == ReplaceAlphaType::REPLACE_ALPHA_DUALSOURCE)
continue;
bool generateSuccess[numLanguages]{};
std::string genErrorString[numLanguages];
for (int j = 0; j < numLanguages; j++) {
generateSuccess[j] = GenerateFShader(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::Fragment, &errorMessage)) {
printf("Error compiling fragment shader:\n\n%s\n\n%s\n", LineNumberString(buffer[j]).c_str(), errorMessage.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);
for (int i = 0; i < numLanguages; i++) {
delete[] buffer[i];
}
return true;
}
bool TestShaderGenerators() {
#if PPSSPP_PLATFORM(WINDOWS)
LoadD3D11();
init_glslang();
LoadD3DCompilerDynamic();
#else
init_glslang();
#endif
if (!TestStencilShaders()) {
return false;
}
if (!TestReinterpretShaders()) {
return false;
}
if (!TestDepalShaders()) {
return false;
}
if (!TestFragmentShaders()) {
return false;
}
if (!TestVertexShaders()) {
return false;
}
return true;
}